diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 7cec0d649..a025e66ac 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -37,6 +37,6 @@ Run `zcashd --version` to find out This includes the relevant contents of `~/.zcash/debug.log`. You can paste raw text, attach the file directly in the issue or link to the text via a pastebin type site. Please also include any non-standard things you did during compilation (extra flags, dependency version changes etc.) if applicable. -### Do you have a back up of `~/.zcash` directory and/or take a VM snapshot? +### Do you have a backup of `~/.zcash` directory and/or take a VM snapshot? - Backing up / making a copy of the `~/.zcash` directory might help make the problem reproducible. Please redact appropriately. - Taking a VM snapshot is really helpful for interactively testing fixes diff --git a/.gitignore b/.gitignore index 3e934cb9f..bebcef932 100644 --- a/.gitignore +++ b/.gitignore @@ -114,3 +114,9 @@ libzcashconsensus.pc src/fiat/-usd contrib/debian/files contrib/debian/substvars + +src/rpcmisc~.cpp +src/komodo-cli +src/komodod +src/komodo-tx +src/komodo-test diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..e69de29bb diff --git a/.travis.yml b/.travis.yml index 250756396..ac331dcf9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +<<<<<<< HEAD language: cpp compiler: @@ -21,3 +22,84 @@ notifications: - "Alt Message : %{repository_slug} - (%{commit} - %{author}): %{message}, Build Time: %{duration}" - "Change view : %{compare_url}" - "Build details : %{build_url}" +======= +# errata: +# - A travis bug causes caches to trample eachother when using the same +# compiler key (which we don't use anyway). This is worked around for now by +# replacing the "compilers" with a build name prefixed by the no-op ":" +# command. See: https://github.com/travis-ci/travis-ci/issues/4393 +# - sudo/dist/group are set so as to get Blue Box VMs, necessary for [loopback] +# IPv6 support + +sudo: required +dist: precise +group: legacy + +os: linux +language: cpp +compiler: gcc +env: + global: + - MAKEJOBS=-j3 + - RUN_TESTS=false + - BOOST_TEST_RANDOM=1$TRAVIS_BUILD_ID + - CCACHE_SIZE=100M + - CCACHE_TEMPDIR=/tmp/.ccache-temp + - CCACHE_COMPRESS=1 + - BASE_OUTDIR=$TRAVIS_BUILD_DIR/out + - SDK_URL=https://bitcoincore.org/depends-sources/sdks + - PYTHON_DEBUG=1 + - WINEDEBUG=fixme-all +cache: + apt: true + directories: + - depends/built + - depends/sdk-sources + - $HOME/.ccache +matrix: + fast_finish: true + include: + - compiler: ": ARM" + env: HOST=arm-linux-gnueabihf PACKAGES="g++-arm-linux-gnueabihf" DEP_OPTS="" GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports" + - compiler: ": Win32" + env: HOST=i686-w64-mingw32 PACKAGES="nsis gcc-mingw-w64-i686 g++-mingw-w64-i686 binutils-mingw-w64-i686 mingw-w64-dev wine bc" RUN_TESTS=true GOAL="deploy" BITCOIN_CONFIG="--enable-reduce-exports" MAKEJOBS="-j2" + - compiler: ": 32-bit + dash" + env: HOST=i686-pc-linux-gnu PACKAGES="g++-multilib bc python-zmq" PPA="ppa:chris-lea/zeromq" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++" USE_SHELL="/bin/dash" + - compiler: ": Win64" + env: HOST=x86_64-w64-mingw32 PACKAGES="nsis gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 binutils-mingw-w64-x86-64 mingw-w64-dev wine bc" RUN_TESTS=true GOAL="deploy" BITCOIN_CONFIG="--enable-reduce-exports" MAKEJOBS="-j2" + - compiler: ": bitcoind" + env: HOST=x86_64-unknown-linux-gnu PACKAGES="bc python-zmq" PPA="ppa:chris-lea/zeromq" DEP_OPTS="DEBUG=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports CPPFLAGS=-DDEBUG_LOCKORDER" + - compiler: ": No wallet" + env: HOST=x86_64-unknown-linux-gnu DEP_OPTS="NO_WALLET=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports" + - compiler: ": Cross-Mac" + env: HOST=x86_64-apple-darwin11 PACKAGES="cmake libcap-dev libz-dev libbz2-dev" BITCOIN_CONFIG="--enable-reduce-exports" OSX_SDK=10.9 GOAL="deploy" + exclude: + - compiler: gcc +install: + - if [ -n "$PACKAGES" ]; then sudo rm -f /etc/apt/sources.list.d/travis_ci_zeromq3-source.list; fi + - if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get update; fi + - if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get install --no-install-recommends --no-upgrade -qq $PACKAGES; fi +before_script: + - unset CC; unset CXX + - mkdir -p depends/SDKs depends/sdk-sources + - if [ -n "$OSX_SDK" -a ! -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then curl --location --fail $SDK_URL/MacOSX${OSX_SDK}.sdk.tar.gz -o depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi + - if [ -n "$OSX_SDK" -a -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then tar -C depends/SDKs -xf depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi + - make $MAKEJOBS -C depends HOST=$HOST $DEP_OPTS +script: + - if [ -n "$USE_SHELL" ]; then export CONFIG_SHELL="$USE_SHELL"; fi + - OUTDIR=$BASE_OUTDIR/$TRAVIS_PULL_REQUEST/$TRAVIS_JOB_NUMBER-$HOST + - BITCOIN_CONFIG_ALL="--disable-dependency-tracking --prefix=$TRAVIS_BUILD_DIR/depends/$HOST --bindir=$OUTDIR/bin --libdir=$OUTDIR/lib" + - depends/$HOST/native/bin/ccache --max-size=$CCACHE_SIZE + - if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then export CCACHE_READONLY=1; fi + - test -n "$USE_SHELL" && eval '"$USE_SHELL" -c "./autogen.sh"' || ./autogen.sh + - ./configure --cache-file=config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false) + - make distdir PACKAGE=bitcoin VERSION=$HOST + - cd bitcoin-$HOST + - ./configure --cache-file=../config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false) + - make $MAKEJOBS $GOAL || ( echo "Build failure. Verbose build follows." && make $GOAL V=1 ; false ) + - export LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/depends/$HOST/lib + - if [ "$RUN_TESTS" = "true" ]; then make check; fi + - if [ "$RUN_TESTS" = "true" ]; then qa/pull-tester/rpc-tests.sh; fi +after_script: + - if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then (echo "Upload goes here. Something like: scp -r $BASE_OUTDIR server" || echo "upload failed"); fi +>>>>>>> zcash/master diff --git a/COPYING b/COPYING index 67a092a04..c84bfb7aa 100644 --- a/COPYING +++ b/COPYING @@ -28,8 +28,7 @@ open-source licenses. For further details see 'contrib/debian/copyright'. This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (https://www.openssl.org/). This product includes cryptographic -software written by Eric Young (eay@cryptsoft.com), -and UPnP software written by Thomas Bernard. +software written by Eric Young (eay@cryptsoft.com). Although almost all of the Zcash code is licensed under "permissive" open source diff --git a/Dockerfile b/Dockerfile index 71136b3af..7a4308f7d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,13 @@ -FROM kolobus/ubuntu:komodo -MAINTAINER Mihail Fedorov +FROM ubuntu:16.04 +MAINTAINER Mihail Fedorov + +RUN apt-get -y update && \ + apt-get -y upgrade && \ + apt-get -y install build-essential pkg-config libc6-dev m4 g++-multilib autoconf libtool ncurses-dev \ + unzip python zlib1g-dev wget bsdmainutils automake libssl-dev libprotobuf-dev \ + protobuf-compiler libqrencode-dev libdb++-dev software-properties-common libcurl4-openssl-dev curl && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* ADD ./ /komodo ENV HOME /komodo diff --git a/Makefile.am b/Makefile.am index 3445faa2a..143b2cc68 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,6 +12,7 @@ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libzcashconsensus.pc endif + BITCOIND_BIN=$(top_builddir)/src/zcashd$(EXEEXT) BITCOIN_CLI_BIN=$(top_builddir)/src/zcash-cli$(EXEEXT) BITCOIN_WIN_INSTALLER=$(PACKAGE)-$(PACKAGE_VERSION)-win$(WINDOWS_BITS)-setup$(EXEEXT) @@ -33,6 +34,7 @@ DIST_DOCS = $(wildcard doc/*.md) $(wildcard doc/release-notes/*.md) BIN_CHECKS=$(top_srcdir)/contrib/devtools/symbol-check.py \ $(top_srcdir)/contrib/devtools/security-check.py + WINDOWS_PACKAGING = $(top_srcdir)/share/pixmaps/bitcoin.ico \ $(top_srcdir)/share/pixmaps/nsis-header.bmp \ $(top_srcdir)/share/pixmaps/nsis-wizard.bmp @@ -54,7 +56,7 @@ COVERAGE_INFO = baseline_filtered_combined.info baseline.info block_test.info \ else COVERAGE_INFO = baseline_filtered_combined.info baseline.info block_test.info \ leveldb_baseline.info test_bitcoin_filtered.info total_coverage.info \ - baseline_filtered.info block_test_filtered.info \ + baseline_filtered.info \ leveldb_baseline_filtered.info test_bitcoin_coverage.info test_bitcoin.info \ #zcash-gtest.info zcash-gtest_filtered.info zcash-gtest_coverage.info endif @@ -72,14 +74,6 @@ distcheck-hook: distcleancheck: @: -$(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_CLI_BIN) $(top_builddir)/release - @test -f $(MAKENSIS) && $(MAKENSIS) -V2 $(top_builddir)/share/setup.nsi || \ - echo error: could not build $@ - @echo built $@ - $(if $(findstring src/,$(MAKECMDGOALS)),$(MAKECMDGOALS), none): FORCE $(MAKE) -C src $(patsubst src/%,%,$@) @@ -256,6 +250,7 @@ test_bitcoin_filtered.info: test_bitcoin.info -o $@ endif + block_test.info: test_bitcoin_filtered.info $(MKDIR_P) qa/tmp -@TIMEOUT=15 qa/pull-tester/run-bitcoind-for-test.sh $(JAVA) -jar $(JAVA_COMPARISON_TOOL) qa/tmp/compTool 0 @@ -289,6 +284,7 @@ block_test_filtered.info: block_test.info -o $@ endif + test_bitcoin_coverage.info: baseline_filtered_combined.info test_bitcoin_filtered.info $(LCOV) -a baseline_filtered.info -a leveldb_baseline_filtered.info -a test_bitcoin_filtered.info -o $@ @@ -329,12 +325,6 @@ endif endif -if USE_COMPARISON_TOOL -check-local: - $(MKDIR_P) qa/tmp - @qa/pull-tester/run-bitcoind-for-test.sh $(JAVA) -jar $(JAVA_COMPARISON_TOOL) qa/tmp/compTool $(COMPARISON_TOOL_REORG_TESTS) 2>&1 -endif - dist_bin_SCRIPTS = zcutil/fetch-params.sh dist_noinst_SCRIPTS = autogen.sh zcutil/build-debian-package.sh zcutil/build.sh @@ -343,11 +333,9 @@ EXTRA_DIST = $(top_srcdir)/share/genbuild.sh qa/pull-tester/rpc-tests.sh qa/pull install-exec-hook: mv $(DESTDIR)$(bindir)/fetch-params.sh $(DESTDIR)$(bindir)/zcash-fetch-params -CLEANFILES = $(OSX_DMG) $(BITCOIN_WIN_INSTALLER) - .INTERMEDIATE: $(COVERAGE_INFO) DISTCHECK_CONFIGURE_FLAGS = --enable-man clean-local: - rm -rf test_bitcoin.coverage/ zcash-gtest.coverage/ total.coverage/ $(OSX_APP) + rm -rf test_bitcoin.coverage/ zcash-gtest.coverage/ total.coverage/ diff --git a/README.md b/README.md index 65f34d3d4..74425bd3c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +Komodo 1.0.15 ## Komodod This software is Komodo client, generally you will use this if you want to mine KMD or setup a full node. @@ -33,7 +34,7 @@ Dependencies ``` #The following packages are needed: -sudo apt-get install build-essential pkg-config libc6-dev m4 g++-multilib autoconf libtool ncurses-dev unzip git python python-zmq zlib1g-dev wget libcurl3-gnutls-dev bsdmainutils automake +sudo apt-get install build-essential pkg-config libc6-dev m4 g++-multilib autoconf libtool ncurses-dev unzip git python python-zmq zlib1g-dev wget libcurl4-openssl-dev bsdmainutils automake curl ``` Komodo @@ -63,6 +64,18 @@ cd komodo #This can take some time. ``` + +**komodo is experimental and a work-in-progress.** Use at your own risk. + +Deprecation Policy +------------------ + +This release is considered deprecated one year after the release day. There +is an automatic deprecation shutdown feature which will halt the node some +time after this one year period. The automatic feature is based on block +height and can be explicitly disabled. + + # to update an existing version, git checkout dPoW if not on that branch already git pull ./zcutil/fetch-params.sh diff --git a/build-aux/m4/ax_boost_base.m4 b/build-aux/m4/ax_boost_base.m4 index 3f24d5ddc..45d948933 100644 --- a/build-aux/m4/ax_boost_base.m4 +++ b/build-aux/m4/ax_boost_base.m4 @@ -33,7 +33,7 @@ # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 23 +#serial 26 AC_DEFUN([AX_BOOST_BASE], [ @@ -95,8 +95,8 @@ if test "x$want_boost" = "xyes"; then x86_64) libsubdirs="lib64 libx32 lib lib64" ;; - ppc64|s390x|sparc64|aarch64) - libsubdirs="lib64 lib lib64" + ppc64|s390x|sparc64|aarch64|ppc64le) + libsubdirs="lib64 lib lib64 ppc64le" ;; esac @@ -170,7 +170,7 @@ if test "x$want_boost" = "xyes"; then AC_MSG_RESULT(yes) succeeded=yes found_system=yes - ],[: + ],[ ]) AC_LANG_POP([C++]) @@ -179,6 +179,10 @@ 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 + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + BOOST_CPPFLAGS= + BOOST_LDFLAGS= _version=0 if test "$ac_boost_path" != ""; then if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then @@ -191,6 +195,12 @@ if test "x$want_boost" = "xyes"; then VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` BOOST_CPPFLAGS="-I$ac_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" + fi + fi fi else if test "$cross_compiling" != yes; then @@ -253,7 +263,7 @@ if test "x$want_boost" = "xyes"; then AC_MSG_RESULT(yes) succeeded=yes found_system=yes - ],[: + ],[ ]) AC_LANG_POP([C++]) fi diff --git a/build-aux/m4/ax_boost_program_options.m4 b/build-aux/m4/ax_boost_program_options.m4 index f59144185..2bdb59371 100644 --- a/build-aux/m4/ax_boost_program_options.m4 +++ b/build-aux/m4/ax_boost_program_options.m4 @@ -29,7 +29,7 @@ # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 22 +#serial 24 AC_DEFUN([AX_BOOST_PROGRAM_OPTIONS], [ @@ -63,9 +63,9 @@ AC_DEFUN([AX_BOOST_PROGRAM_OPTIONS], AC_CACHE_CHECK([whether the Boost::Program_Options library is available], ax_cv_boost_program_options, [AC_LANG_PUSH(C++) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], - [[boost::program_options::options_description generic("Generic options"); + [[boost::program_options::error err("Error message"); return 0;]])], ax_cv_boost_program_options=yes, ax_cv_boost_program_options=no) AC_LANG_POP([C++]) @@ -74,7 +74,6 @@ AC_DEFUN([AX_BOOST_PROGRAM_OPTIONS], AC_DEFINE(HAVE_BOOST_PROGRAM_OPTIONS,,[define if the Boost::PROGRAM_OPTIONS library is available]) BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` if test "x$ax_boost_user_program_options_lib" = "x"; then - ax_lib= for libextension in `ls $BOOSTLIBDIR/libboost_program_options*.so* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.so.*$;\1;'` `ls $BOOSTLIBDIR/libboost_program_options*.dylib* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.dylib.*$;\1;'` `ls $BOOSTLIBDIR/libboost_program_options*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.a.*$;\1;'` ; do ax_lib=${libextension} AC_CHECK_LIB($ax_lib, exit, diff --git a/build-aux/m4/ax_boost_system.m4 b/build-aux/m4/ax_boost_system.m4 index 9c78280fc..1c05450cb 100644 --- a/build-aux/m4/ax_boost_system.m4 +++ b/build-aux/m4/ax_boost_system.m4 @@ -31,7 +31,7 @@ # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 17 +#serial 18 AC_DEFUN([AX_BOOST_SYSTEM], [ @@ -68,9 +68,10 @@ AC_DEFUN([AX_BOOST_SYSTEM], ax_cv_boost_system, [AC_LANG_PUSH([C++]) CXXFLAGS_SAVE=$CXXFLAGS + CXXFLAGS= AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], - [[boost::system::system_category]])], + [[boost::system::error_category *a = 0;]])], ax_cv_boost_system=yes, ax_cv_boost_system=no) CXXFLAGS=$CXXFLAGS_SAVE AC_LANG_POP([C++]) diff --git a/build-aux/m4/ax_check_compile_flag.m4 b/build-aux/m4/ax_check_compile_flag.m4 index c3a8d695a..ca3639715 100644 --- a/build-aux/m4/ax_check_compile_flag.m4 +++ b/build-aux/m4/ax_check_compile_flag.m4 @@ -4,7 +4,7 @@ # # SYNOPSIS # -# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS]) +# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) # # DESCRIPTION # @@ -19,6 +19,8 @@ # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to # force the compiler to issue an error when a bad flag is given. # +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this # macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. # @@ -53,19 +55,19 @@ # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. -#serial 2 +#serial 4 AC_DEFUN([AX_CHECK_COMPILE_FLAG], -[AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" - AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], + AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) -AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes], +AS_VAR_IF(CACHEVAR,yes, [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl diff --git a/build-aux/m4/ax_check_link_flag.m4 b/build-aux/m4/ax_check_link_flag.m4 index e2d0d363e..eb01a6ce1 100644 --- a/build-aux/m4/ax_check_link_flag.m4 +++ b/build-aux/m4/ax_check_link_flag.m4 @@ -4,7 +4,7 @@ # # SYNOPSIS # -# AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS]) +# AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) # # DESCRIPTION # @@ -19,6 +19,8 @@ # EXTRA-FLAGS FLAG". This can for example be used to force the linker to # issue an error when a bad flag is given. # +# INPUT gives an alternative input source to AC_LINK_IFELSE. +# # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this # macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG. # @@ -53,18 +55,19 @@ # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. -#serial 2 +#serial 4 AC_DEFUN([AX_CHECK_LINK_FLAG], -[AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [ ax_check_save_flags=$LDFLAGS LDFLAGS="$LDFLAGS $4 $1" - AC_LINK_IFELSE([AC_LANG_PROGRAM()], + AC_LINK_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) LDFLAGS=$ax_check_save_flags]) -AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes], +AS_VAR_IF(CACHEVAR,yes, [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl diff --git a/build-aux/m4/ax_check_preproc_flag.m4 b/build-aux/m4/ax_check_preproc_flag.m4 index b1cfef6b8..ca1d5ee2b 100644 --- a/build-aux/m4/ax_check_preproc_flag.m4 +++ b/build-aux/m4/ax_check_preproc_flag.m4 @@ -4,7 +4,7 @@ # # SYNOPSIS # -# AX_CHECK_PREPROC_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS]) +# AX_CHECK_PREPROC_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) # # DESCRIPTION # @@ -19,6 +19,8 @@ # "CPPFLAGS EXTRA-FLAGS FLAG". This can for example be used to force the # preprocessor to issue an error when a bad flag is given. # +# INPUT gives an alternative input source to AC_PREPROC_IFELSE. +# # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this # macro in sync with AX_CHECK_{COMPILE,LINK}_FLAG. # @@ -53,19 +55,19 @@ # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. -#serial 2 +#serial 4 AC_DEFUN([AX_CHECK_PREPROC_FLAG], -[AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]cppflags_$4_$1])dnl AC_CACHE_CHECK([whether _AC_LANG preprocessor accepts $1], CACHEVAR, [ ax_check_save_flags=$CPPFLAGS CPPFLAGS="$CPPFLAGS $4 $1" - AC_PREPROC_IFELSE([AC_LANG_PROGRAM()], + AC_PREPROC_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) CPPFLAGS=$ax_check_save_flags]) -AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes], +AS_VAR_IF(CACHEVAR,yes, [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl diff --git a/build-aux/m4/ax_cxx_compile_stdcxx.m4 b/build-aux/m4/ax_cxx_compile_stdcxx.m4 new file mode 100644 index 000000000..f147cee3b --- /dev/null +++ b/build-aux/m4/ax_cxx_compile_stdcxx.m4 @@ -0,0 +1,568 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# 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). +# +# 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. +# +# The third argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline support for the specified C++ standard is +# required and that the macro should error out if no mode with that +# support is found. If specified 'optional', then configuration proceeds +# regardless, after defining HAVE_CXX${VERSION} if and only if a +# supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov +# Copyright (c) 2015 Paul Norman +# Copyright (c) 2015 Moritz Klammler +# +# 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 + +dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro +dnl (serial version number 13). + +AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl + m4_if([$1], [11], [], + [$1], [14], [], + [$1], [17], [m4_fatal([support for C++17 not yet implemented in AX_CXX_COMPILE_STDCXX])], + [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$2], [], [], + [$2], [ext], [], + [$2], [noext], [], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], + [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], + [$3], [optional], [ax_cxx_compile_cxx$1_required=false], + [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) + m4_if([$4], [], [ax_cxx_compile_cxx$1_try_default=true], + [$4], [default], [ax_cxx_compile_cxx$1_try_default=true], + [$4], [nodefault], [ax_cxx_compile_cxx$1_try_default=false], + [m4_fatal([invalid fourth argument `$4' to AX_CXX_COMPILE_STDCXX])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + + m4_if([$4], [nodefault], [], [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 switch in -std=gnu++$1 -std=gnu++0x; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + fi]) + + m4_if([$2], [ext], [], [dnl + if test x$ac_success = xno; then + dnl HP's aCC needs +std=c++11 according to: + dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf + dnl Cray's crayCC needs "-h std=c++11" + for switch in -std=c++$1 -std=c++0x +std=c++$1 "-h std=c++$1"; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx$1_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) + fi + fi + if test x$ac_success = xno; then + HAVE_CXX$1=0 + AC_MSG_NOTICE([No compiler with C++$1 support was found]) + else + HAVE_CXX$1=1 + AC_DEFINE(HAVE_CXX$1,1, + [define if the compiler supports basic C++$1 syntax]) + fi + AC_SUBST(HAVE_CXX$1) +]) + + +dnl Test body for checking C++11 support + +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], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 +) + + +dnl Tests for new features in C++11 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ + +// If the compiler admits that it is not ready for C++11, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201103L + +#error "This is not a C++11 compiler" + +#else + +namespace cxx11 +{ + + namespace test_static_assert + { + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + } + + namespace test_final_override + { + + struct Base + { + virtual void f() {} + }; + + struct Derived : public Base + { + virtual void f() override {} + }; + + } + + namespace test_double_right_angle_brackets + { + + template < typename T > + struct check {}; + + typedef check single_type; + typedef check> double_type; + typedef check>> triple_type; + typedef check>>> quadruple_type; + + } + + namespace test_decltype + { + + int + f() + { + int a = 1; + decltype(a) b = 2; + return a + b; + } + + } + + namespace test_type_deduction + { + + template < typename T1, typename T2 > + struct is_same + { + static const bool value = false; + }; + + template < typename T > + struct is_same + { + static const bool value = true; + }; + + template < typename T1, typename T2 > + auto + add(T1 a1, T2 a2) -> decltype(a1 + a2) + { + return a1 + a2; + } + + int + test(const int c, volatile int v) + { + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == false, ""); + auto ac = c; + auto av = v; + auto sumi = ac + av + 'x'; + auto sumf = ac + av + 1.0; + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == true, ""); + return (sumf > 0.0) ? sumi : add(c, v); + } + + } + + namespace test_noexcept + { + + int f() { return 0; } + int g() noexcept { return 0; } + + static_assert(noexcept(f()) == false, ""); + static_assert(noexcept(g()) == true, ""); + + } + + namespace test_constexpr + { + + template < typename CharT > + unsigned long constexpr + strlen_c_r(const CharT *const s, const unsigned long acc) noexcept + { + return *s ? strlen_c_r(s + 1, acc + 1) : acc; + } + + template < typename CharT > + unsigned long constexpr + strlen_c(const CharT *const s) noexcept + { + return strlen_c_r(s, 0UL); + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("1") == 1UL, ""); + static_assert(strlen_c("example") == 7UL, ""); + static_assert(strlen_c("another\0example") == 7UL, ""); + + } + + namespace test_rvalue_references + { + + template < int N > + struct answer + { + static constexpr int value = N; + }; + + answer<1> f(int&) { return answer<1>(); } + answer<2> f(const int&) { return answer<2>(); } + answer<3> f(int&&) { return answer<3>(); } + + void + test() + { + int i = 0; + const int c = 0; + static_assert(decltype(f(i))::value == 1, ""); + static_assert(decltype(f(c))::value == 2, ""); + static_assert(decltype(f(0))::value == 3, ""); + } + + } + + namespace test_uniform_initialization + { + + struct test + { + static const int zero {}; + static const int one {1}; + }; + + static_assert(test::zero == 0, ""); + static_assert(test::one == 1, ""); + + } + + namespace test_lambdas + { + + void + test1() + { + auto lambda1 = [](){}; + auto lambda2 = lambda1; + lambda1(); + lambda2(); + } + + int + test2() + { + auto a = [](int i, int j){ return i + j; }(1, 2); + auto b = []() -> int { return '0'; }(); + auto c = [=](){ return a + b; }(); + auto d = [&](){ return c; }(); + auto e = [a, &b](int x) mutable { + const auto identity = [](int y){ return y; }; + for (auto i = 0; i < a; ++i) + a += b--; + return x + identity(a + b); + }(0); + return a + b + c + d + e; + } + + int + test3() + { + const auto nullary = [](){ return 0; }; + const auto unary = [](int x){ return x; }; + using nullary_t = decltype(nullary); + using unary_t = decltype(unary); + const auto higher1st = [](nullary_t f){ return f(); }; + const auto higher2nd = [unary](nullary_t f1){ + return [unary, f1](unary_t f2){ return f2(unary(f1())); }; + }; + return higher1st(nullary) + higher2nd(nullary)(unary); + } + + } + + namespace test_variadic_templates + { + + template + struct sum; + + template + struct sum + { + static constexpr auto value = N0 + sum::value; + }; + + template <> + struct sum<> + { + static constexpr auto value = 0; + }; + + static_assert(sum<>::value == 0, ""); + static_assert(sum<1>::value == 1, ""); + static_assert(sum<23>::value == 23, ""); + static_assert(sum<1, 2>::value == 3, ""); + static_assert(sum<5, 5, 11>::value == 21, ""); + static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); + + } + + // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function + // because of this. + namespace test_template_alias_sfinae + { + + struct foo {}; + + template + using member = typename T::member_type; + + template + void func(...) {} + + template + void func(member*) {} + + void test(); + + void test() { func(0); } + + } + +} // namespace cxx11 + +#endif // __cplusplus >= 201103L + +]]) + + +dnl Tests for new features in C++14 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ + +// If the compiler admits that it is not ready for C++14, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201402L + +#error "This is not a C++14 compiler" + +#else + +namespace cxx14 +{ + + namespace test_polymorphic_lambdas + { + + int + test() + { + const auto lambda = [](auto&&... args){ + const auto istiny = [](auto x){ + return (sizeof(x) == 1UL) ? 1 : 0; + }; + const int aretiny[] = { istiny(args)... }; + return aretiny[0]; + }; + return lambda(1, 1L, 1.0f, '1'); + } + + } + + namespace test_binary_literals + { + + constexpr auto ivii = 0b0000000000101010; + static_assert(ivii == 42, "wrong value"); + + } + + namespace test_generalized_constexpr + { + + template < typename CharT > + constexpr unsigned long + strlen_c(const CharT *const s) noexcept + { + auto length = 0UL; + for (auto p = s; *p; ++p) + ++length; + return length; + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("x") == 1UL, ""); + static_assert(strlen_c("test") == 4UL, ""); + static_assert(strlen_c("another\0test") == 7UL, ""); + + } + + namespace test_lambda_init_capture + { + + int + test() + { + auto x = 0; + const auto lambda1 = [a = x](int b){ return a + b; }; + const auto lambda2 = [a = lambda1(x)](){ return a; }; + return lambda2(); + } + + } + + namespace test_digit_seperators + { + + constexpr auto ten_million = 100'000'000; + static_assert(ten_million == 100000000, ""); + + } + + namespace test_return_type_deduction + { + + auto f(int& x) { return x; } + decltype(auto) g(int& x) { return x; } + + template < typename T1, typename T2 > + struct is_same + { + static constexpr auto value = false; + }; + + template < typename T > + struct is_same + { + static constexpr auto value = true; + }; + + int + test() + { + auto x = 0; + static_assert(is_same::value, ""); + static_assert(is_same::value, ""); + return x; + } + + } + +} // namespace cxx14 + +#endif // __cplusplus >= 201402L + +]]) diff --git a/build-aux/m4/ax_gcc_func_attribute.m4 b/build-aux/m4/ax_gcc_func_attribute.m4 index 275ca63a2..c788ca9bd 100644 --- a/build-aux/m4/ax_gcc_func_attribute.m4 +++ b/build-aux/m4/ax_gcc_func_attribute.m4 @@ -31,6 +31,7 @@ # cold # const # constructor +# constructor_priority for constructor attribute with priority # deprecated # destructor # dllexport @@ -73,7 +74,7 @@ # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 2 +#serial 3 AC_DEFUN([AX_GCC_FUNC_ATTRIBUTE], [ AS_VAR_PUSHDEF([ac_var], [ax_cv_have_func_attribute_$1]) @@ -103,6 +104,9 @@ AC_DEFUN([AX_GCC_FUNC_ATTRIBUTE], [ [const], [ int foo( void ) __attribute__(($1)); ], + [constructor_priority], [ + int foo( void ) __attribute__((__constructor__(65535/2))); + ], [constructor], [ int foo( void ) __attribute__(($1)); ], @@ -180,6 +184,8 @@ AC_DEFUN([AX_GCC_FUNC_ATTRIBUTE], [ [visibility], [ int foo_def( void ) __attribute__(($1("default"))); int foo_hid( void ) __attribute__(($1("hidden"))); + int foo_int( void ) __attribute__(($1("internal"))); + int foo_pro( void ) __attribute__(($1("protected"))); ], [warning], [ int foo( void ) __attribute__(($1(""))); diff --git a/build-aux/m4/ax_openmp.m4 b/build-aux/m4/ax_openmp.m4 new file mode 100644 index 000000000..866e1d664 --- /dev/null +++ b/build-aux/m4/ax_openmp.m4 @@ -0,0 +1,123 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_openmp.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_OPENMP([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +# +# DESCRIPTION +# +# This macro tries to find out how to compile programs that use OpenMP a +# standard API and set of compiler directives for parallel programming +# (see http://www-unix.mcs/) +# +# On success, it sets the OPENMP_CFLAGS/OPENMP_CXXFLAGS/OPENMP_F77FLAGS +# output variable to the flag (e.g. -omp) used both to compile *and* link +# OpenMP programs in the current language. +# +# NOTE: You are assumed to not only compile your program with these flags, +# but also link it with them as well. +# +# If you want to compile everything with OpenMP, you should set: +# +# CFLAGS="$CFLAGS $OPENMP_CFLAGS" +# #OR# CXXFLAGS="$CXXFLAGS $OPENMP_CXXFLAGS" +# #OR# FFLAGS="$FFLAGS $OPENMP_FFLAGS" +# +# (depending on the selected language). +# +# The user can override the default choice by setting the corresponding +# environment variable (e.g. OPENMP_CFLAGS). +# +# ACTION-IF-FOUND is a list of shell commands to run if an OpenMP flag is +# found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it is +# not found. If ACTION-IF-FOUND is not specified, the default action will +# define HAVE_OPENMP. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2015 John W. Peterson +# Copyright (c) 2016 Nick R. Papior +# +# 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. + +#serial 13 + +AC_DEFUN([AX_OPENMP], [ +AC_PREREQ([2.69]) dnl for _AC_LANG_PREFIX + +AC_CACHE_CHECK([for OpenMP flag of _AC_LANG compiler], ax_cv_[]_AC_LANG_ABBREV[]_openmp, [save[]_AC_LANG_PREFIX[]FLAGS=$[]_AC_LANG_PREFIX[]FLAGS +ax_cv_[]_AC_LANG_ABBREV[]_openmp=unknown +# Flags to try: -fopenmp (gcc), -mp (SGI & PGI), +# -qopenmp (icc>=15), -openmp (icc), +# -xopenmp (Sun), -omp (Tru64), +# -qsmp=omp (AIX), +# none +ax_openmp_flags="-fopenmp -openmp -qopenmp -mp -xopenmp -omp -qsmp=omp none" +if test "x$OPENMP_[]_AC_LANG_PREFIX[]FLAGS" != x; then + ax_openmp_flags="$OPENMP_[]_AC_LANG_PREFIX[]FLAGS $ax_openmp_flags" +fi +for ax_openmp_flag in $ax_openmp_flags; do + case $ax_openmp_flag in + none) []_AC_LANG_PREFIX[]FLAGS=$save[]_AC_LANG_PREFIX[] ;; + *) []_AC_LANG_PREFIX[]FLAGS="$save[]_AC_LANG_PREFIX[]FLAGS $ax_openmp_flag" ;; + esac + AC_LINK_IFELSE([AC_LANG_SOURCE([[ +@%:@include + +static void +parallel_fill(int * data, int n) +{ + int i; +@%:@pragma omp parallel for + for (i = 0; i < n; ++i) + data[i] = i; +} + +int +main() +{ + int arr[100000]; + omp_set_num_threads(2); + parallel_fill(arr, 100000); + return 0; +} +]])],[ax_cv_[]_AC_LANG_ABBREV[]_openmp=$ax_openmp_flag; break],[]) +done +[]_AC_LANG_PREFIX[]FLAGS=$save[]_AC_LANG_PREFIX[]FLAGS +]) +if test "x$ax_cv_[]_AC_LANG_ABBREV[]_openmp" = "xunknown"; then + m4_default([$2],:) +else + if test "x$ax_cv_[]_AC_LANG_ABBREV[]_openmp" != "xnone"; then + OPENMP_[]_AC_LANG_PREFIX[]FLAGS=$ax_cv_[]_AC_LANG_ABBREV[]_openmp + fi + m4_default([$1], [AC_DEFINE(HAVE_OPENMP,1,[Define if OpenMP is enabled])]) +fi +])dnl AX_OPENMP diff --git a/build-aux/m4/ax_pthread.m4 b/build-aux/m4/ax_pthread.m4 index d383ad5c6..d218d1af7 100644 --- a/build-aux/m4/ax_pthread.m4 +++ b/build-aux/m4/ax_pthread.m4 @@ -19,10 +19,10 @@ # is necessary on AIX to use the special cc_r compiler alias.) # # NOTE: You are assumed to not only compile your program with these flags, -# but also link it with them as well. e.g. you should link with +# but also to link with them as well. For example, you might link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # -# If you are only building threads programs, you may wish to use these +# 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" @@ -30,8 +30,8 @@ # CC="$PTHREAD_CC" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant -# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name -# (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to +# that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). # # Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the # PTHREAD_PRIO_INHERIT symbol is defined when compiling with @@ -82,35 +82,40 @@ # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. -#serial 21 +#serial 22 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) +AC_REQUIRE([AC_PROG_CC]) +AC_REQUIRE([AC_PROG_SED]) AC_LANG_PUSH([C]) ax_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h -# requires special compiler flags (e.g. on True64 or Sequent). +# requires special compiler flags (e.g. on Tru64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: -if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then - save_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - save_LIBS="$LIBS" - LIBS="$PTHREAD_LIBS $LIBS" - AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) - AC_TRY_LINK_FUNC([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 - LIBS="$save_LIBS" - CFLAGS="$save_CFLAGS" +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" fi # We must check for the threads library under a number of different @@ -123,7 +128,7 @@ fi # 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 -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" +ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: @@ -132,186 +137,334 @@ ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mt # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) -# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) -# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) -# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) -# -pthreads: Solaris/gcc -# -mthreads: Mingw32/gcc, Lynx/gcc +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 +# (Note: HP C rejects this with "bad form for `-t' option") +# -pthreads: Solaris/gcc (Note: HP C also rejects) # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it -# doesn't hurt to check since this sometimes defines pthreads too; -# also defines -D_REENTRANT) -# ... -mt is also the pthreads flag for HP/aCC +# doesn't hurt to check since this sometimes defines pthreads and +# -D_REENTRANT too), HP C (must be checked before -lpthread, which +# is present but should not be used directly; and before -mthreads, +# because the compiler interprets this as "-mt" + "-hreads") +# -mthreads: Mingw32/gcc, Lynx/gcc # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) -case ${host_os} in - solaris*) +case $host_os in + + freebsd*) + + # -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" + ;; + + hpux*) + + # 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" + ;; + + openedition*) - # On Solaris (at least, for some versions), libc contains stubbed - # (non-functional) versions of the pthreads routines, so link-based - # tests will erroneously succeed. (We need to link with -pthreads/-mt/ - # -lpthread.) (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 just look for -pthreads and -lpthread first: + # 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.) - ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" - ;; + 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.])]) + ;; - darwin*) - ax_pthread_flags="-pthread $ax_pthread_flags" - ;; + 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). + + ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags" + ;; esac -# Clang doesn't consider unrecognized options an error unless we specify -# -Werror. We throw in some extra Clang-specific options to ensure that -# this doesn't happen for GCC, which also accepts -Werror. - -AC_MSG_CHECKING([if compiler needs -Werror to reject unknown flags]) -save_CFLAGS="$CFLAGS" -ax_pthread_extra_flags="-Werror" -CFLAGS="$CFLAGS $ax_pthread_extra_flags -Wunknown-warning-option -Wsizeof-array-argument" -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([int foo(void);],[foo()])], - [AC_MSG_RESULT([yes])], - [ax_pthread_extra_flags= - AC_MSG_RESULT([no])]) -CFLAGS="$save_CFLAGS" - -if test x"$ax_pthread_ok" = xno; then -for flag in $ax_pthread_flags; do - - case $flag in - none) - AC_MSG_CHECKING([whether pthreads work without any flags]) - ;; - - -*) - AC_MSG_CHECKING([whether pthreads work with $flag]) - PTHREAD_CFLAGS="$flag" - ;; - - pthread-config) - AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) - if test x"$ax_pthread_config" = xno; then continue; fi - PTHREAD_CFLAGS="`pthread-config --cflags`" - PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" - ;; - - *) - AC_MSG_CHECKING([for the pthreads library -l$flag]) - PTHREAD_LIBS="-l$flag" - ;; - esac - - save_LIBS="$LIBS" - save_CFLAGS="$CFLAGS" - LIBS="$PTHREAD_LIBS $LIBS" - CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ax_pthread_extra_flags" - - # 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 - 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], - []) - - LIBS="$save_LIBS" - CFLAGS="$save_CFLAGS" - - AC_MSG_RESULT([$ax_pthread_ok]) - if test "x$ax_pthread_ok" = xyes; then - break; - fi - - PTHREAD_LIBS="" - PTHREAD_CFLAGS="" +# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) + +AS_IF([test "x$GCC" = "xyes"], + [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"]) + +# 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" + ;; + + aix* | freebsd*) + ax_pthread_check_macro="_THREAD_SAFE" + ;; + + *) + 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" + +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 + +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 - save_LIBS="$LIBS" - LIBS="$PTHREAD_LIBS $LIBS" - save_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - - # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. - AC_MSG_CHECKING([for joinable pthread attribute]) - attr_name=unknown - for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do - AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], - [int attr = $attr; return attr /* ; */])], - [attr_name=$attr; break], - []) - done - AC_MSG_RESULT([$attr_name]) - if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then - AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$attr_name], - [Define to necessary symbol if this constant - uses a non-standard name on your system.]) - fi - - AC_MSG_CHECKING([if more special flags are required for pthreads]) - flag=no - case ${host_os} in - aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";; - osf* | hpux*) flag="-D_REENTRANT";; - solaris*) - if test "$GCC" = "yes"; then - flag="-D_REENTRANT" - else - # TODO: What about Clang on Solaris? - flag="-mt -D_REENTRANT" - fi - ;; - esac - AC_MSG_RESULT([$flag]) - if test "x$flag" != xno; then - PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" - fi - - 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"], - [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])]) - - LIBS="$save_LIBS" - CFLAGS="$save_CFLAGS" - - # 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 +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 fi test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" @@ -321,12 +474,12 @@ AC_SUBST([PTHREAD_CFLAGS]) AC_SUBST([PTHREAD_CC]) # 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]) - : +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]) + : else - ax_pthread_ok=no - $2 + ax_pthread_ok=no + $2 fi AC_LANG_POP ])dnl AX_PTHREAD diff --git a/build-aux/m4/l_atomic.m4 b/build-aux/m4/l_atomic.m4 new file mode 100644 index 000000000..906724b64 --- /dev/null +++ b/build-aux/m4/l_atomic.m4 @@ -0,0 +1,40 @@ +# Some versions of gcc/libstdc++ require linking with -latomic if +# using the C++ atomic library. +# +# Sourced from http://bugs.debian.org/797228 + +m4_define([_CHECK_ATOMIC_testbody], [[ + #include + #include + + int main() { + std::atomic a{}; + + int64_t v = 5; + int64_t r = a.fetch_add(v); + return static_cast(r); + } +]]) + +AC_DEFUN([CHECK_ATOMIC], [ + + AC_LANG_PUSH(C++) + + AC_MSG_CHECKING([whether std::atomic can be used without link library]) + + AC_LINK_IFELSE([AC_LANG_SOURCE([_CHECK_ATOMIC_testbody])],[ + AC_MSG_RESULT([yes]) + ],[ + AC_MSG_RESULT([no]) + LIBS="$LIBS -latomic" + AC_MSG_CHECKING([whether std::atomic needs -latomic]) + AC_LINK_IFELSE([AC_LANG_SOURCE([_CHECK_ATOMIC_testbody])],[ + AC_MSG_RESULT([yes]) + ],[ + AC_MSG_RESULT([no]) + AC_MSG_FAILURE([cannot figure our how to use std::atomic]) + ]) + ]) + + AC_LANG_POP +]) diff --git a/code_of_conduct.md b/code_of_conduct.md index 959fbe4d5..d8f622351 100644 --- a/code_of_conduct.md +++ b/code_of_conduct.md @@ -41,19 +41,19 @@ is deemed necessary and appropriate to the circumstances. Maintainers are obligated to maintain confidentiality with regard to the reporter of an incident. -You may send reports to [our Conduct email](mailto:conduct@z.cash). +You may send reports to [our Conduct email](mailto:developer@komodoplatform.com). If you wish to contact specific maintainers directly, the following have made themselves available for conduct issues: -- Daira Hopwood (daira at z.cash) -- Sean Bowe (sean at z.cash) - +- Benny Fairbank (benny at komodoplatform.com) +- Support Team (support at komodoplatform.com) +- ca333 (ca333 at komodoplatform.com) This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.3.0, available at -[http://contributor-covenant.org/version/1/3/0/][version] +[https://www.contributor-covenant.org/version/1/3/0/][version] -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/3/0/ +[homepage]: https://www.contributor-covenant.org +[version]: https://www.contributor-covenant.org/version/1/3/0/ diff --git a/configure.ac b/configure.ac index c471bab89..decf2b2e6 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 1) define(_CLIENT_VERSION_MINOR, 0) -define(_CLIENT_VERSION_REVISION, 8) +define(_CLIENT_VERSION_REVISION, 15) define(_CLIENT_VERSION_BUILD, 50) 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))) @@ -14,6 +14,16 @@ AC_CONFIG_HEADERS([src/config/bitcoin-config.h]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([build-aux/m4]) +BITCOIN_DAEMON_NAME=zcashd +BITCOIN_CLI_NAME=zcash-cli +BITCOIN_TX_NAME=zcash-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 + ARFLAGS="cr" +fi + AC_CANONICAL_HOST AH_TOP([#ifndef BITCOIN_CONFIG_H]) @@ -24,7 +34,7 @@ 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]) +AM_INIT_AUTOMAKE([no-define subdir-objects foreign tar-pax]) dnl faketime messes with timestamps and causes configure to be re-run. dnl --disable-maintainer-mode can be used to bypass this. @@ -40,9 +50,6 @@ else CXXFLAGS_overridden=no fi -# Zcash requries C++11 compatibility; set it early: -CXXFLAGS="-std=c++11 $CXXFLAGS" - AC_PROG_CXX m4_ifdef([AC_PROG_OBJCXX],[AC_PROG_OBJCXX]) @@ -54,6 +61,11 @@ case $host in lt_cv_deplibs_check_method="pass_all" ;; esac +dnl Require C++11 compiler (no GNU extensions) +AX_CXX_COMPILE_STDCXX([11], [noext], [mandatory], [nodefault]) +dnl Check if -latomic is required for +CHECK_ATOMIC + dnl Libtool init checks. LT_INIT([pic-only]) @@ -63,7 +75,6 @@ AC_PATH_TOOL(RANLIB, ranlib) AC_PATH_TOOL(STRIP, strip) AC_PATH_TOOL(GCOV, gcov) AC_PATH_PROG(LCOV, lcov) -AC_PATH_PROG(JAVA, java) AC_PATH_PROG(GENHTML, genhtml) AC_PATH_PROG([GIT], [git]) AC_PATH_PROG(CCACHE,ccache) @@ -72,9 +83,6 @@ AC_PATH_PROG(HEXDUMP,hexdump) AC_PATH_TOOL(READELF,readelf) AC_PATH_TOOL(CPPFILT,c++filt) -dnl pkg-config check. -PKG_PROG_PKG_CONFIG - # Enable wallet AC_ARG_ENABLE([wallet], [AS_HELP_STRING([--enable-wallet], @@ -94,32 +102,28 @@ AC_ARG_ENABLE([rust], [enable_rust=$enableval], [enable_rust=yes]) -AC_ARG_WITH([miniupnpc], - [AS_HELP_STRING([--with-miniupnpc], - [enable UPNP (default is yes if libminiupnpc is found)])], - [use_upnp=$withval], - [use_upnp=auto]) - -AC_ARG_ENABLE([upnp-default], - [AS_HELP_STRING([--enable-upnp-default], - [if UPNP is enabled, turn it on at startup (default is no)])], - [use_upnp_default=$enableval], - [use_upnp_default=no]) +AC_ARG_ENABLE([proton], + [AS_HELP_STRING([--disable-proton], + [disable Proton (AMQP messaging)])], + [use_proton=$enableval], + [use_proton=yes]) AC_ARG_ENABLE(tests, AS_HELP_STRING([--enable-tests],[compile tests (default is yes)]), [use_tests=$enableval], [use_tests=yes]) -AC_ARG_WITH([comparison-tool], - AS_HELP_STRING([--with-comparison-tool],[path to java comparison tool (requires --enable-tests)]), - [use_comparison_tool=$withval], - [use_comparison_tool=no]) +AC_ARG_ENABLE([asan], + [AS_HELP_STRING([--enable-asan], + [instrument the executables with asan (default is no)])], + [use_asan=$enableval], + [use_asan=no]) -AC_ARG_ENABLE([comparison-tool-reorg-tests], - AS_HELP_STRING([--enable-comparison-tool-reorg-tests],[enable expensive reorg tests in the comparison tool (default no)]), - [use_comparison_tool_reorg_tests=$enableval], - [use_comparison_tool_reorg_tests=no]) +AC_ARG_ENABLE([tsan], + [AS_HELP_STRING([--enable-tsan], + [instrument the executables with tsan (default is no)])], + [use_tsan=$enableval], + [use_tsan=no]) if test x$TARGET_OS = xdarwin; then AC_ARG_ENABLE([hardening], @@ -180,6 +184,16 @@ AC_ARG_ENABLE([debug], [enable_debug=$enableval], [enable_debug=no]) +# Turn warnings into errors +AC_ARG_ENABLE([werror], + [AS_HELP_STRING([--enable-werror], + [Treat all 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 CPPFLAGS="$CPPFLAGS -DDEBUG -DDEBUG_LOCKORDER" if test "x$GCC" = xyes; then @@ -191,11 +205,28 @@ if test "x$enable_debug" = xyes; then fi fi -## TODO: Remove these hard-coded paths and flags. They are here for the sake of -## compatibility with the legacy buildsystem. -## +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") + fi + ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror" +fi + if test "x$CXXFLAGS_overridden" = "xno"; then - CXXFLAGS="$CXXFLAGS -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter -Wno-self-assign" + 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" @@ -217,8 +248,6 @@ AC_ARG_WITH([daemon], [build_bitcoind=$withval], [build_bitcoind=yes]) -AC_LANG_PUSH([C++]) - use_pkgconfig=yes case $host in *mingw*) @@ -343,6 +372,7 @@ dnl fi AX_CHECK_LINK_FLAG([[-Wl,-headerpad_max_install_names]], [LDFLAGS="$LDFLAGS -Wl,-headerpad_max_install_names"]) CPPFLAGS="$CPPFLAGS -DMAC_OSX" + OBJCXXFLAGS="$CXXFLAGS" ;; *linux*) TARGET_OS=linux @@ -351,20 +381,14 @@ dnl fi ;; esac -if test x$use_comparison_tool != xno; then - if test x$JAVA = x; then - AC_MSG_ERROR("comparison tool set but java not found") - fi - AC_SUBST(JAVA_COMPARISON_TOOL, $use_comparison_tool) -fi - -if test x$use_comparison_tool_reorg_tests != xno; then - if test x$use_comparison_tool = x; then - AC_MSG_ERROR("comparison tool reorg tests but comparison tool was not specified") +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.) fi - AC_SUBST(COMPARISON_TOOL_REORG_TESTS, 1) -else - AC_SUBST(COMPARISON_TOOL_REORG_TESTS, 0) + ]) fi if test x$use_lcov = xyes; then @@ -374,15 +398,9 @@ if test x$use_lcov = xyes; then if test x$GCOV = x; then AC_MSG_ERROR("lcov testing requested but gcov not found") fi - if test x$JAVA = x; then - AC_MSG_ERROR("lcov testing requested but java not found") - fi if test x$GENHTML = x; then AC_MSG_ERROR("lcov testing requested but genhtml not found") fi - if test x$use_comparison_tool = x; then - AC_MSG_ERROR("lcov testing requested but comparison tool was not specified") - fi LCOV="$LCOV --gcov-tool=$GCOV --rc lcov_branch_coverage=1" GENHTML="$GENHTML --branch-coverage" AX_CHECK_COMPILE_FLAG([--coverage],[CXXFLAGS="$CXXFLAGS --coverage"], @@ -443,6 +461,34 @@ else AC_SEARCH_LIBS([clock_gettime],[rt]) 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"]) +fi + +#asan and tsan cannot be used together +if test x$use_asan$use_tsan == xyesyes; then + AC_MSG_ERROR(asan and tsan cannot be simultaneously enabled) +fi + +# using asan flag to enable address sanitizer and undefined behavior sanitizer +if test x$use_asan == xyes; then + AX_CHECK_LINK_FLAG([-static-libstdc++],[SAN_LDFLAGS="$SAN_LDFLAGS -static-libstdc++"],[AC_MSG_ERROR(Cannot statically link -static-libstdc++)]) + AX_CHECK_LINK_FLAG([-static-libasan],[SAN_LDFLAGS="$SAN_LDFLAGS -static-libasan"],[AC_MSG_ERROR(Cannot statically link -static-libasan)]) + AX_CHECK_COMPILE_FLAG([-fsanitize=address],[SAN_CXXFLAGS="$SAN_CXXFLAGS -fsanitize=address"],[AC_MSG_ERROR(Cannot enable -fsanitize=address)]) + AX_CHECK_COMPILE_FLAG([-fsanitize=undefined],[SAN_CXXFLAGS="$SAN_CXXFLAGS -fsanitize=undefined"],[AC_MSG_ERROR(Cannot enable -fsanitize=undefined)]) + AX_CHECK_COMPILE_FLAG([-fno-omit-frame-pointer],[SAN_CXXFLAGS="$SAN_CXXFLAGS -fno-omit-frame-pointer"],[AC_MSG_ERROR(Cannot enable -fno-omit-frame-pointer)]) +fi + +# using tsan flag to enable address thread sanitizer +# TSAN is supported on Linux x84_64 and tested on Ubuntu 12.04 +# Non-position-independent executables are not supported. Use with -fPIE and -pie flags +# libc/libstdc++ static linking is not supported +if test x$use_tsan == xyes; then + AX_CHECK_COMPILE_FLAG([-fsanitize=thread],[SAN_CXXFLAGS="$SAN_CXXFLAGS -fsanitize=thread"],[AC_MSG_ERROR(Cannot enable -fsanitize=thread)]) + AX_CHECK_COMPILE_FLAG([-fno-omit-frame-pointer],[SAN_CXXFLAGS="$SAN_CXXFLAGS -fno-omit-frame-pointer"],[AC_MSG_ERROR(Cannot enable -fno-omit-frame-pointer)]) +fi + if test x$use_hardening != xno; then 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]) @@ -469,6 +515,7 @@ if test x$use_hardening != xno; then # 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 case $host in @@ -476,11 +523,6 @@ if test x$use_hardening != xno; then AC_CHECK_LIB([ssp], [main],, AC_MSG_ERROR(lib missing)) ;; esac - - CXXFLAGS="$CXXFLAGS $HARDENED_CXXFLAGS" - CPPFLAGS="$CPPFLAGS $HARDENED_CPPFLAGS" - LDFLAGS="$LDFLAGS $HARDENED_LDFLAGS" - OBJCXXFLAGS="$CXXFLAGS" fi dnl this flag screws up non-darwin gcc even when the check fails. special-case it. @@ -549,13 +591,21 @@ if test x$enable_wallet != xno; then BITCOIN_FIND_BDB62 fi -dnl Check for libminiupnpc (optional) -if test x$use_upnp != xno; then - AC_CHECK_HEADERS( - [miniupnpc/miniwget.h miniupnpc/miniupnpc.h miniupnpc/upnpcommands.h miniupnpc/upnperrors.h], - [AC_CHECK_LIB([miniupnpc], [main],[MINIUPNPC_LIBS=-lminiupnpc], [have_miniupnpc=no])], - [have_miniupnpc=no] - ) +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], [main], + [PROTON_LIBS="-lqpid-proton-cpp -lqpid-proton"], + [AC_MSG_WARN([Proton libraries 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 @@ -567,33 +617,14 @@ fi if test x$use_boost = xyes; then dnl Check for boost libs -AX_BOOST_BASE +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 - -if test x$use_reduce_exports = xyes; then - AC_MSG_CHECKING([for working boost reduced exports]) - TEMP_CPPFLAGS="$CPPFLAGS" - CPPFLAGS="$BOOST_CPPFLAGS $CPPFLAGS" - AC_PREPROC_IFELSE([AC_LANG_PROGRAM([[ - @%:@include - ]], [[ - #if BOOST_VERSION >= 104900 - // Everything is okay - #else - # error Boost version is too old - #endif - ]])],[ - AC_MSG_RESULT(yes) - ],[ - AC_MSG_ERROR([boost versions < 1.49 are known to be broken with reduced exports. Use --disable-reduce-exports.]) - ]) - CPPFLAGS="$TEMP_CPPFLAGS" -fi fi if test x$use_reduce_exports = xyes; then @@ -634,69 +665,11 @@ if test x$use_tests = xyes; then 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" - -dnl Boost >= 1.50 uses sleep_for rather than the now-deprecated sleep, however -dnl it was broken from 1.50 to 1.52 when backed by nanosleep. Use sleep_for if -dnl a working version is available, else fall back to sleep. sleep was removed -dnl after 1.56. -dnl If neither is available, abort. -TEMP_LIBS="$LIBS" -LIBS="$BOOST_LIBS $LIBS" -TEMP_CPPFLAGS="$CPPFLAGS" -CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" -AC_LINK_IFELSE([AC_LANG_PROGRAM([[ - #include - #include - ]],[[ - #if BOOST_VERSION >= 105000 && (!defined(BOOST_HAS_NANOSLEEP) || BOOST_VERSION >= 105200) - boost::this_thread::sleep_for(boost::chrono::milliseconds(0)); - #else - choke me - #endif - ]])], - [boost_sleep=yes; - AC_DEFINE(HAVE_WORKING_BOOST_SLEEP_FOR, 1, [Define this symbol if boost sleep_for works])], - [boost_sleep=no]) -LIBS="$TEMP_LIBS" -CPPFLAGS="$TEMP_CPPFLAGS" - -if test x$boost_sleep != xyes; then -TEMP_LIBS="$LIBS" -LIBS="$BOOST_LIBS $LIBS" -TEMP_CPPFLAGS="$CPPFLAGS" -CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" -AC_LINK_IFELSE([AC_LANG_PROGRAM([[ - #include - #include - #include - ]],[[ - #if BOOST_VERSION <= 105600 - boost::this_thread::sleep(boost::posix_time::milliseconds(0)); - #else - choke me - #endif - ]])], - [boost_sleep=yes; AC_DEFINE(HAVE_WORKING_BOOST_SLEEP, 1, [Define this symbol if boost sleep works])], - [boost_sleep=no]) -LIBS="$TEMP_LIBS" -CPPFLAGS="$TEMP_CPPFLAGS" -fi - -if test x$boost_sleep != xyes; then - AC_MSG_ERROR(No working boost sleep implementation found.) -fi - fi if test x$use_pkgconfig = xyes; then - - if test x"$PKG_CONFIG" = "x"; then - AC_MSG_ERROR(pkg-config not found.) - fi - - : #NOP + : dnl m4_ifdef( [PKG_CHECK_MODULES], [ @@ -785,7 +758,8 @@ echo 'Hunting for libsnark include directory...' if test -d "$LIBSNARK_INCDIR"; then echo "Found libsnark include directory: $LIBSNARK_INCDIR" else - AC_MSG_ERROR(libsnark include directory not found) +#AC_MSG_ERROR(libsnark include directory not found) +echo "libsnark include directory not found ($LIBSNARK_INCDIR)" fi CPPFLAGS="-I$LIBSNARK_INCDIR $CPPFLAGS" @@ -803,15 +777,29 @@ if test x$enable_rust != xno; then RUST_LIBS="-lrustzcash" fi -LIBZCASH_LIBS="-lsnark -lgmp -lgmpxx -lboost_system-mt -lcrypto -lsodium -fopenmp $RUST_LIBS" +dnl Check for OpenMP support +AX_OPENMP( + [AC_DEFINE(HAVE_OPENMP, 1, [Define if OpenMP is enabled]) + AM_CONDITIONAL([HAVE_OPENMP], [true]) + CXXFLAGS="$CXXFLAGS $OPENMP_CXXFLAGS"], + [AC_MSG_WARN([OpenMP not supported, disabling multithreading]) + AC_DEFINE(HAVE_OPENMP, 0, [Define if OpenMP is enabled]) + AM_CONDITIONAL([HAVE_OPENMP], [false])]) -CXXFLAGS_TEMP="$CXXFLAGS" -LIBS_TEMP="$LIBS" -CXXFLAGS="$CXXFLAGS $SSL_CFLAGS $CRYPTO_CFLAGS" -LIBS="$LIBS $SSL_LIBS $CRYPTO_LIBS $GMP_LIBS $GMPXX_LIBS" -AC_CHECK_HEADER([openssl/ec.h],, AC_MSG_ERROR(OpenSSL ec header missing),) -CXXFLAGS="$CXXFLAGS_TEMP" -LIBS="$LIBS_TEMP" +# Gitian uses a config.site that sets depends_prefix, and then sets --prefix=/ +# build.sh just uses --prefix +if test x$depends_prefix != x; then + LIBSNARK_DEPINST="$depends_prefix" +else + LIBSNARK_DEPINST="$prefix" +fi + +# Additional Zcash flags +AX_CHECK_COMPILE_FLAG([-fwrapv],[CXXFLAGS="$CXXFLAGS -fwrapv"]) +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="-lgmp -lgmpxx -lboost_system -lcrypto -lsodium $RUST_LIBS" AC_MSG_CHECKING([whether to build bitcoind]) AM_CONDITIONAL([BUILD_BITCOIND], [test x$build_bitcoind = xyes]) @@ -881,35 +869,10 @@ else AC_MSG_RESULT(no) fi -dnl enable upnp support -AC_MSG_CHECKING([whether to build with support for UPnP]) -if test x$have_miniupnpc = xno; then - if test x$use_upnp = xyes; then - AC_MSG_ERROR("UPnP requested but cannot be built. use --without-miniupnpc") - fi - AC_MSG_RESULT(no) -else - if test x$use_upnp != xno; then - AC_MSG_RESULT(yes) - AC_MSG_CHECKING([whether to build with UPnP enabled by default]) - use_upnp=yes - upnp_setting=0 - if test x$use_upnp_default != xno; then - use_upnp_default=yes - upnp_setting=1 - fi - AC_MSG_RESULT($use_upnp_default) - AC_DEFINE_UNQUOTED([USE_UPNP],[$upnp_setting],[UPnP support not compiled if undefined, otherwise value (0 or 1) determines default state]) - if test x$TARGET_OS = xwindows; then - MINIUPNPC_CPPFLAGS="-DSTATICLIB -DMINIUPNP_STATICLIB" - fi - else - AC_MSG_RESULT(no) - fi -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_bitcoin]) if test x$use_tests = xyes; then AC_MSG_RESULT([yes]) @@ -938,10 +901,10 @@ AM_CONDITIONAL([ENABLE_MINING],[test x$enable_mining = xyes]) AM_CONDITIONAL([ENABLE_RUST],[test x$enable_rust = xyes]) AM_CONDITIONAL([ENABLE_TESTS],[test x$BUILD_TEST = xyes]) AM_CONDITIONAL([USE_LCOV],[test x$use_lcov = xyes]) -AM_CONDITIONAL([USE_COMPARISON_TOOL],[test x$use_comparison_tool != xno]) -AM_CONDITIONAL([USE_COMPARISON_TOOL_REORG_TESTS],[test x$use_comparison_tool_reorg_test != xno]) AM_CONDITIONAL([GLIBC_BACK_COMPAT],[test x$use_glibc_compat = xyes]) AM_CONDITIONAL([HARDEN],[test x$use_hardening = xyes]) +AM_CONDITIONAL([ASAN],[test x$use_asan = xyes]) +AM_CONDITIONAL([TSAN],[test x$use_tsan = xyes]) AC_DEFINE(CLIENT_VERSION_MAJOR, _CLIENT_VERSION_MAJOR, [Major version]) AC_DEFINE(CLIENT_VERSION_MINOR, _CLIENT_VERSION_MINOR, [Minor version]) @@ -955,20 +918,34 @@ AC_SUBST(CLIENT_VERSION_REVISION, _CLIENT_VERSION_REVISION) 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_CLI_NAME) +AC_SUBST(BITCOIN_TX_NAME) AC_SUBST(RELDFLAGS) +AC_SUBST(ERROR_CXXFLAGS) +AC_SUBST(SAN_CXXFLAGS) +AC_SUBST(SAN_LDFLAGS) +AC_SUBST(HARDENED_CXXFLAGS) +AC_SUBST(HARDENED_CPPFLAGS) +AC_SUBST(HARDENED_LDFLAGS) +AC_SUBST(PIC_FLAGS) +AC_SUBST(PIE_FLAGS) AC_SUBST(LIBTOOL_APP_LDFLAGS) -AC_SUBST(USE_UPNP) AC_SUBST(BOOST_LIBS) AC_SUBST(TESTDEFS) AC_SUBST(LEVELDB_TARGET_FLAGS) -AC_SUBST(MINIUPNPC_CPPFLAGS) -AC_SUBST(MINIUPNPC_LIBS) +AC_SUBST(CRYPTO_LIBS) +AC_SUBST(SSL_LIBS) +AC_SUBST(EVENT_LIBS) +AC_SUBST(EVENT_PTHREADS_LIBS) +AC_SUBST(ZMQ_LIBS) AC_SUBST(GMP_LIBS) AC_SUBST(GMPXX_LIBS) -AC_SUBST(LIBSNARK_LIBS) +AC_SUBST(LIBSNARK_DEPINST) AC_SUBST(LIBZCASH_LIBS) -AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi src/test/buildenv.py]) +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]) @@ -995,8 +972,8 @@ 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" -AC_CONFIG_SUBDIRS([src/secp256k1 src/univalue]) +ac_configure_args="${ac_configure_args} --disable-shared --with-pic --with-bignum=no --enable-module-recovery" +AC_CONFIG_SUBDIRS([src/secp256k1 src/snark src/univalue src/cryptoconditions]) AC_OUTPUT @@ -1010,3 +987,25 @@ case $host in chmod 755 libtool ;; esac + +echo +echo "Options used to compile and link:" +echo " with wallet = $enable_wallet" +echo " with rust = $enable_rust" +echo " with proton = $use_proton" +echo " with zmq = $use_zmq" +echo " with test = $use_tests" +echo " debug enabled = $enable_debug" +echo " werror = $enable_werror" +echo +echo " target os = $TARGET_OS" +echo " build os = $BUILD_OS" +echo +echo " CC = $CC" +echo " CFLAGS = $CFLAGS" +echo " CPPFLAGS = $CPPFLAGS" +echo " CXX = $CXX" +echo " CXXFLAGS = $CXXFLAGS" +echo " LDFLAGS = $LDFLAGS" +echo " ARFLAGS = $ARFLAGS" +echo diff --git a/contrib/amqp/amqp_sub.py b/contrib/amqp/amqp_sub.py new file mode 100644 index 000000000..bc51e8428 --- /dev/null +++ b/contrib/amqp/amqp_sub.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python2 +# Copyright (c) 2017 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://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/ci-workers/README.md b/contrib/ci-workers/README.md new file mode 100644 index 000000000..37f7ad833 --- /dev/null +++ b/contrib/ci-workers/README.md @@ -0,0 +1,62 @@ +# Zcash CI workers + +This folder contains the Ansible playbooks for configuring a fresh OS +installation for use as a Buildbot worker in Zcash's CI. + +# Criteria for Adding Workers + +a. Don't add workers until users complain about a problem on a platform + that doesn't yet have workers or if we anticipate many users will use + a platform, we may pre-emptively add an unsupported worker for it. + +b. Prioritize the platforms that seem to have the most users. + +c. When adding workers start by adding workers for the "most common" + variant of any distro, then if users later encounter problems with a + sub-variant, we can consider adding new workers at that point. + Example: add Ubuntu Desktop before Xubuntu, on the assumption the + former has a larger population base. + +# Setting up a latent worker on Amazon EC2 + +- Add a regular (non-latent) worker to the master.cfg for dev-ci.z.cash, and + deploy the changes. + - This enables the Ansible playbook to run to completion, ending in the worker + connecting to the master. + +- Start a basic EC2 instance using the template AMI for the target OS. + - Choose the smallest instance size, it won't be used for building Zcash. + +- Figure out which user to log into the instance with. + - E.g. for the Ubuntu template, use "ubuntu" instead of "root" + - If you get an Ansible error later with a message like "Failed to connect to + the host via ssh: Received message too long 1349281121\r\n", that means the + instance is sending a text string in response to the SSH connection, and the + Ansible protocol is balking. Try manually logging in with the same + credentials to diagnose. + +- Create `inventory/hosts` containing the following: + + [zcash-ci-worker-unix] + some-name ansible_host= ansible_ssh_user= + +- Run `ansible-playbook -e buildbot_worker_host_template=templates/host.ec2.j2 -i inventory/hosts unix.yml`, + passing in the worker's Buildbot name and password. + - After a successful run, the worker should be connected to dev-ci.z.cash and + visible in its worker list. + +- Create an AMI from the instance. This is the worker AMI to put into the + master.cfg for dev-ci.z.cash. + - 16 GB of storage should be sufficient. + +- SSH into the instance, and edit the worker config to connect to ci.z.cash. + +- Create an AMI from the instance. This is the worker AMI to put into the + master.cfg for ci.z.cash. + - 16 GB of storage should be sufficient. + +- Delete the instance (it is no longer needed). + +- Edit the master.cfg to turn the new worker into a latent (using the new AMI + IDs), add it to the appropriate worker groups, set up new builders etc. + - Deploy this via the normal PR review process. diff --git a/contrib/ci-workers/ansible.cfg b/contrib/ci-workers/ansible.cfg new file mode 100644 index 000000000..c58fea3c0 --- /dev/null +++ b/contrib/ci-workers/ansible.cfg @@ -0,0 +1,2 @@ +[ssh_connection] +pipelining = True diff --git a/contrib/ci-workers/grind.yml b/contrib/ci-workers/grind.yml new file mode 100644 index 000000000..ef7e5758e --- /dev/null +++ b/contrib/ci-workers/grind.yml @@ -0,0 +1,27 @@ +--- +# Configure a Buildbot worker +- include: unix.yml + +- name: Install grind-specific worker dependencies + hosts: zcash-ci-worker-unix + become: true + + vars_files: + - vars/default.yml + + tasks: + - name: Get dependencies for distribution + include_vars: "{{ item }}" + with_first_found: + - files: + - "vars/{{ ansible_distribution }}-{{ ansible_distribution_version }}.yml" + - "vars/{{ ansible_distribution }}-{{ ansible_distribution_major_version | int }}.yml" + - "vars/{{ ansible_distribution }}.yml" + - "vars/{{ ansible_os_family }}.yml" + skip: true + + - name: Install required packages + package: + name: "{{ item }}" + state: present + with_items: "{{ grind_deps }}" diff --git a/contrib/ci-workers/tasks/install-pip.yml b/contrib/ci-workers/tasks/install-pip.yml new file mode 100644 index 000000000..8beff50ef --- /dev/null +++ b/contrib/ci-workers/tasks/install-pip.yml @@ -0,0 +1,8 @@ +--- +- name: Fetch pip installer + get_url: + url: https://bootstrap.pypa.io/get-pip.py + dest: /tmp/get-pip.py + +- name: Install pip + command: "{{ ansible_python.executable }} /tmp/get-pip.py" diff --git a/contrib/ci-workers/templates/buildbot-worker.service.j2 b/contrib/ci-workers/templates/buildbot-worker.service.j2 new file mode 100644 index 000000000..ffe497bcf --- /dev/null +++ b/contrib/ci-workers/templates/buildbot-worker.service.j2 @@ -0,0 +1,17 @@ +[Unit] +Description=Buildbot worker +Wants=network.target +After=network.target + +[Service] +Type=forking +PIDFile=/home/{{ buildbot_worker_user }}/{{ buildbot_worker_name }}/twistd.pid +WorkingDirectory=/home/{{ buildbot_worker_user }} +ExecStart={{ pip_bin_dir }}/buildbot-worker start {{ buildbot_worker_name }} +ExecReload={{ pip_bin_dir }}/buildbot-worker restart {{ buildbot_worker_name }} +ExecStop={{ pip_bin_dir }}/buildbot-worker stop {{ buildbot_worker_name }} +Restart=always +User={{ buildbot_worker_user }} + +[Install] +WantedBy=multi-user.target diff --git a/contrib/ci-workers/templates/host.ec2.j2 b/contrib/ci-workers/templates/host.ec2.j2 new file mode 100644 index 000000000..dee692e02 --- /dev/null +++ b/contrib/ci-workers/templates/host.ec2.j2 @@ -0,0 +1 @@ +OS: {{ ansible_distribution }} {{ ansible_distribution_version }} diff --git a/contrib/ci-workers/templates/host.j2 b/contrib/ci-workers/templates/host.j2 new file mode 100644 index 000000000..3a5abb0c2 --- /dev/null +++ b/contrib/ci-workers/templates/host.j2 @@ -0,0 +1,3 @@ +OS: {{ ansible_distribution }} {{ ansible_distribution_version }} +Memory: {{ ansible_memtotal_mb }} MB +CPU: {{ ansible_processor[1] }} diff --git a/contrib/ci-workers/unix.yml b/contrib/ci-workers/unix.yml new file mode 100644 index 000000000..6e6cc49c4 --- /dev/null +++ b/contrib/ci-workers/unix.yml @@ -0,0 +1,152 @@ +--- +- name: Configure a Buildbot worker for Zcash CI + hosts: zcash-ci-worker-unix + become: true + gather_facts: False + + vars_files: + - vars/default.yml + - vars/buildbot.yml + + vars_prompt: + - name: "buildbot_worker_admin" + prompt: "Admin details" + default: "Zcash " + - name: "buildbot_worker_name" + prompt: "Buildbot worker name (provided by ZECC)" + private: no + - name: "buildbot_worker_password" + prompt: "Buildbot worker password (provided by ZECC)" + + pre_tasks: + - name: Install Python 2.7 for Ansible and Buildbot + raw: test -e /usr/bin/python || test -e /usr/bin/python2 || test -e /usr/bin/python2.7 || test -e /usr/local/bin/python2.7 || (test -e /usr/bin/apt && apt -qqy update && apt install -qqy python) || (test -e /usr/bin/dnf && dnf install -qqy python2) || (test -e /usr/sbin/pkg && pkg install -qqy python2) + register: output + changed_when: + - output.stdout != "" + - output.stdout != "\r\n" + + - name: Check if Python is in the configured location + raw: test -e {{ ansible_python_interpreter }} + ignore_errors: true + register: python_check + when: ansible_python_interpreter is defined + + - name: Fail if configured Python is unavailable + fail: + msg: "Python is not accessible at {{ ansible_python_interpreter }} on this host! Please set the inventory variable 'ansible_python_interpreter' to the location of the Python 2.7 binary." + when: ansible_python_interpreter is defined and python_check.rc == 1 + + - name: Check if Python is in the default location + raw: test -e /usr/bin/python + ignore_errors: true + register: python_check + when: ansible_python_interpreter is undefined + + - name: Fail if default Python is unavailable + fail: + msg: Python is not accessible at /usr/bin/python on this host! Please set the inventory variable 'ansible_python_interpreter' to the location of the Python 2.7 binary. + when: ansible_python_interpreter is undefined and python_check.rc == 1 + + - name: Gathering Facts + setup: + + - name: Fail if Python is the wrong version + fail: + msg: "The Python binary at {{ ansible_python.executable }} is version {{ ansible_python_version }}! Please set the inventory variable 'ansible_python_interpreter' to the location of the Python 2.7 binary." + when: ansible_python.version.major != 2 or ansible_python.version.minor != 7 + + tasks: + - name: Get dependencies for distribution + include_vars: "{{ item }}" + with_first_found: + - files: + - "vars/{{ ansible_distribution }}-{{ ansible_distribution_version }}.yml" + - "vars/{{ ansible_distribution }}-{{ ansible_distribution_major_version | int }}.yml" + - "vars/{{ ansible_distribution }}.yml" + - "vars/{{ ansible_os_family }}.yml" + skip: true + + - name: Collate dependencies + set_fact: + package_deps: "{{ buildbot_deps + fetch_deps + conf_deps + build_deps + link_deps + dist_deps }}" + python_modules: "{{ buildbot_modules + rpc_test_modules }}" + + - name: Update rolling release [Arch Linux] + pacman: + update_cache: yes + upgrade: yes + when: ansible_distribution == 'Archlinux' + + - name: Install required packages + package: + name: "{{ item }}" + state: present + with_items: "{{ package_deps }}" + + - name: Install pip [CentOS] + include: tasks/install-pip.yml + when: ansible_distribution == 'CentOS' + + - name: Install required Python modules + pip: + name: "{{ item }}" + state: latest + with_items: "{{ python_modules }}" + notify: restart buildbot-worker + + - name: Set up the Buildbot worker user + user: + name: "{{ buildbot_worker_user }}" + comment: Buildbot worker + shell: /bin/bash + state: present + + - name: Create Buildbot worker + command: > + buildbot-worker create-worker ~/{{ buildbot_worker_name }} + {{ buildbot_master_host }}:{{ buildbot_master_port }} + {{ buildbot_worker_name|quote }} {{ buildbot_worker_password|quote }} + args: + creates: "~/{{ buildbot_worker_name }}/buildbot.tac" + become_user: "{{ buildbot_worker_user }}" + + - name: Set admin details for Buildbot worker + copy: + content: "{{ buildbot_worker_admin }}" + dest: "~{{ buildbot_worker_user }}/{{ buildbot_worker_name }}/info/admin" + owner: "{{ buildbot_worker_user }}" + group: "{{ buildbot_worker_user }}" + mode: "0644" + + - name: Set host details for Buildbot worker + template: + src: "{{ buildbot_worker_host_template }}" + dest: "~{{ buildbot_worker_user }}/{{ buildbot_worker_name }}/info/host" + owner: "{{ buildbot_worker_user }}" + group: "{{ buildbot_worker_user }}" + mode: "0644" + + - name: Copy Buildbot worker systemd service unit + template: + src: templates/buildbot-worker.service.j2 + dest: "/etc/systemd/system/buildbot-worker.service" + owner: root + group: root + mode: "0644" + notify: reload systemd + + - name: Start Buildbot worker. + service: + name: buildbot-worker + state: started + enabled: yes + + handlers: + - name: restart buildbot-worker + service: + name: buildbot-worker + state: restarted + + - name: reload systemd + command: /bin/systemctl daemon-reload diff --git a/contrib/ci-workers/vars/Archlinux.yml b/contrib/ci-workers/vars/Archlinux.yml new file mode 100644 index 000000000..ac4a44e5b --- /dev/null +++ b/contrib/ci-workers/vars/Archlinux.yml @@ -0,0 +1,7 @@ +--- +buildbot_deps: + - python2-pip +build_deps: + - multilib/gcc + - make +pip_bin_dir: /usr/bin diff --git a/contrib/ci-workers/vars/CentOS.yml b/contrib/ci-workers/vars/CentOS.yml new file mode 100644 index 000000000..7e09b0717 --- /dev/null +++ b/contrib/ci-workers/vars/CentOS.yml @@ -0,0 +1,13 @@ +--- +buildbot_deps: [] # Empty to remove python-pip +build_deps: + - bzip2 + - gcc + - gcc-c++ + - make + - patch +dist_deps: + - pkgconfig # Required until b556beda264308e040f8d88aca4f2f386a0183d9 is pulled in + - python-devel + - redhat-rpm-config +pip_bin_dir: /usr/bin diff --git a/contrib/ci-workers/vars/Debian.yml b/contrib/ci-workers/vars/Debian.yml new file mode 100644 index 000000000..992224721 --- /dev/null +++ b/contrib/ci-workers/vars/Debian.yml @@ -0,0 +1,6 @@ +--- +build_deps: + - build-essential # Depends on g++, libc6-dev, make +dist_deps: + - pkg-config # Required until b556beda264308e040f8d88aca4f2f386a0183d9 is pulled in + - python-dev diff --git a/contrib/ci-workers/vars/Fedora.yml b/contrib/ci-workers/vars/Fedora.yml new file mode 100644 index 000000000..1c6b0e0f3 --- /dev/null +++ b/contrib/ci-workers/vars/Fedora.yml @@ -0,0 +1,10 @@ +--- +build_deps: + - gcc + - gcc-c++ + - make + - patch +dist_deps: + - pkgconfig # Required until b556beda264308e040f8d88aca4f2f386a0183d9 is pulled in + - python-devel + - redhat-rpm-config diff --git a/contrib/ci-workers/vars/FreeBSD.yml b/contrib/ci-workers/vars/FreeBSD.yml new file mode 100644 index 000000000..65909d71d --- /dev/null +++ b/contrib/ci-workers/vars/FreeBSD.yml @@ -0,0 +1,9 @@ +--- +buildbot_deps: + - py27-pip +build_deps: + - gcc + - gmake +dist_deps: + - bash + - pkgconf # Required until b556beda264308e040f8d88aca4f2f386a0183d9 is pulled in diff --git a/contrib/ci-workers/vars/Ubuntu.yml b/contrib/ci-workers/vars/Ubuntu.yml new file mode 100644 index 000000000..4acca499b --- /dev/null +++ b/contrib/ci-workers/vars/Ubuntu.yml @@ -0,0 +1,5 @@ +--- +build_deps: + - build-essential # Depends on g++, libc6-dev, make +dist_deps: + - pkg-config # Required until b556beda264308e040f8d88aca4f2f386a0183d9 is pulled in diff --git a/contrib/ci-workers/vars/buildbot.yml b/contrib/ci-workers/vars/buildbot.yml new file mode 100644 index 000000000..38e3fd25a --- /dev/null +++ b/contrib/ci-workers/vars/buildbot.yml @@ -0,0 +1,5 @@ +--- +buildbot_worker_user: zcbbworker +buildbot_master_host: dev-ci.z.cash +buildbot_master_port: 9899 +buildbot_worker_host_template: templates/host.j2 diff --git a/contrib/ci-workers/vars/default.yml b/contrib/ci-workers/vars/default.yml new file mode 100644 index 000000000..38c5afc8e --- /dev/null +++ b/contrib/ci-workers/vars/default.yml @@ -0,0 +1,49 @@ +--- +# These variables can be overridden in distribution files. + +# Dependencies required to install Buildbot +buildbot_deps: + - python-pip # So we can install Python modules + +# Dependencies required to download files +fetch_deps: + - git + - wget # For zcutil/fetch-params.sh + +# Dependencies required to configure Zcash +conf_deps: + - autoconf + - automake + - m4 + +# Dependencies required to compile Zcash +build_deps: + - g++ + - gcc + - make + +# Dependencies required to link Zcash +link_deps: + - libtool + +# Additional distribution-specific dependencies +dist_deps: [] + +# Additional grind-specific dependencies +grind_deps: + - lcov + - valgrind + +# Python modules required for a Zcash Buildbot worker +buildbot_modules: + - pip # Needs to be updated first so Buildbot installs + - buildbot-worker + - pyflakes + +# Python modules required to run the Zcash RPC test suite +rpc_test_modules: + - pyblake2 + - pyzmq + +# Environment variables +pip_bin_dir: /usr/local/bin diff --git a/contrib/debian/changelog b/contrib/debian/changelog index c400dca73..c0b1d157f 100644 --- a/contrib/debian/changelog +++ b/contrib/debian/changelog @@ -1,3 +1,93 @@ +zcash (1.0.15) stable; urgency=medium + + * 1.0.15 release. + + -- Zcash Company Wed, 28 Feb 2018 16:58:19 +0000 + +zcash (1.0.15~rc1) stable; urgency=medium + + * 1.0.15-rc1 release. + + -- Zcash Company Sat, 24 Feb 2018 04:20:05 +0000 + +zcash (1.0.14) stable; urgency=medium + + * 1.0.14 release. + + -- Zcash Company Wed, 03 Jan 2018 23:54:16 +0100 + +zcash (1.0.14~rc1) stable; urgency=medium + + * 1.0.14-rc1 release. + + -- Zcash Company Fri, 22 Dec 2017 10:12:41 +0000 + +zcash (1.0.13) stable; urgency=medium + + * 1.0.13 release. + + -- Zcash Company Mon, 20 Nov 2017 12:31:53 +0000 + +zcash (1.0.13~rc2) stable; urgency=medium + + * 1.0.13-rc2 release. + + -- Zcash Company Fri, 17 Nov 2017 18:01:08 +0000 + +zcash (1.0.13~rc1) stable; urgency=medium + + * 1.0.13-rc1 release. + + -- Zcash Company Wed, 15 Nov 2017 00:02:21 +0000 + +zcash (1.0.12) stable; urgency=medium + + * 1.0.12 release. + + -- Zcash Company Thu, 28 Sep 2017 01:26:44 +0100 + +zcash (1.0.12~rc1) stable; urgency=medium + + * 1.0.12-rc1 release. + + -- Zcash Company Sat, 23 Sep 2017 10:51:36 +0100 + +zcash (1.0.11) stable; urgency=medium + + * 1.0.11 release. + + -- Zcash Company Tue, 15 Aug 2017 10:06:25 +0100 + +zcash (1.0.11~rc1) stable; urgency=medium + + * 1.0.11-rc1 release. + + -- Zcash Company Tue, 01 Aug 2017 17:12:52 +0200 + +zcash (1.0.10+1) stable; urgency=medium + + * 1.0.10-1 release. + + -- Zcash Company Fri, 23 Jun 2017 19:50:41 -0700 + +zcash (1.0.10) stable; urgency=medium + + * 1.0.10 release. + + -- Zcash Company Thu, 22 Jun 2017 15:13:04 +1200 + +zcash (1.0.9) stable; urgency=medium + + * 1.0.9 release. + + -- Zcash Company Wed, 24 May 2017 12:51:06 -0700 + +zcash (1.0.8+1) jessie; urgency=high + + * 1.0.8-1 release. + + -- Zcash Company Thu, 13 Apr 2017 20:20:37 -0700 + zcash (1.0.8) jessie; urgency=medium * 1.0.8 release. diff --git a/contrib/debian/copyright b/contrib/debian/copyright index aea954e07..3fbaa848b 100644 --- a/contrib/debian/copyright +++ b/contrib/debian/copyright @@ -38,10 +38,6 @@ Files: depends/sources/openssl-*.tar.gz Copyright: 1998-2016 The OpenSSL Project and 1995-1998 Eric Young License: OpenSSL+SSLeay -Files: depends/sources/miniupnpc-*.tar.gz -Copyright: 2005-2016 Thomas BERNARD -License: BSD-3clause - Files: depends/sources/zeromq-*.tar.gz Copyright: 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. @@ -59,6 +55,18 @@ 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: src/secp256k1/build-aux/m4/ax_jni_include_dir.m4 +Copyright: 2008 Don Anderson +License: GNU-All-permissive-License + +Files: src/secp256k1/build-aux/m4/ax_prog_cc_for_build.m4 +Copyright: 2008 Paolo Bonzini +License: GNU-All-permissive-License + License: Boost-Software-License-1.0 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by @@ -1091,3 +1099,226 @@ License: LGPL-with-ZeroMQ-exception 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 + and this notice are preserved. This file is offered as-is, without any + warranty. + diff --git a/contrib/devtools/gen-manpages.sh b/contrib/devtools/gen-manpages.sh index 245714814..b604a4c28 100755 --- a/contrib/devtools/gen-manpages.sh +++ b/contrib/devtools/gen-manpages.sh @@ -4,25 +4,25 @@ TOPDIR=${TOPDIR:-$(git rev-parse --show-toplevel)} SRCDIR=${SRCDIR:-$TOPDIR/src} MANDIR=${MANDIR:-$TOPDIR/doc/man} -ZCASHD=${ZCASHD:-$SRCDIR/zcashd} -ZCASHCLI=${ZCASHCLI:-$SRCDIR/zcash-cli} -ZCASHTX=${ZCASHTX:-$SRCDIR/zcash-tx} +KOMODOD=${KOMODOD:-$SRCDIR/komodod} +KOMODOCLI=${KOMODOCLI:-$SRCDIR/komodo-cli} +KOMODOTX=${KOMODOTX:-$SRCDIR/komodo-tx} -[ ! -x $ZCASHD ] && echo "$ZCASHD not found or not executable." && exit 1 +[ ! -x $KOMODOD ] && echo "$KOMODOD not found or not executable." && exit 1 # The autodetected version git tag can screw up manpage output a little bit -ZECVER=($($ZCASHCLI --version | head -n1 | awk -F'[ -]' '{ print $5, $6 }')) +KMDVER=($($KOMODOCLI --version | head -n1 | awk -F'[ -]' '{ print $5, $6 }')) # Create a footer file with copyright content. -# This gets autodetected fine for zcashd if --version-string is not set, -# but has different outcomes for zcash-cli. +# This gets autodetected fine for komodod if --version-string is not set, +# but has different outcomes for komodo-cli. echo "[COPYRIGHT]" > footer.h2m -$ZCASHD --version | sed -n '1!p' >> footer.h2m +$KOMODOD --version | sed -n '1!p' >> footer.h2m -for cmd in $ZCASHD $ZCASHCLI $ZCASHTX; do +for cmd in $KOMODOD $KOMODOCLI $KOMODOTX; do cmdname="${cmd##*/}" - help2man -N --version-string=${ZECVER[0]} --include=footer.h2m -o ${MANDIR}/${cmdname}.1 ${cmd} - sed -i "s/\\\-${ZECVER[1]}//g" ${MANDIR}/${cmdname}.1 + help2man -N --version-string=${KMDVER[0]} --include=footer.h2m -o ${MANDIR}/${cmdname}.1 ${cmd} + sed -i "s/\\\-${KMDVER[1]}//g" ${MANDIR}/${cmdname}.1 done rm -f footer.h2m diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py index 301fea85c..bee8f3cc1 100755 --- a/contrib/devtools/security-check.py +++ b/contrib/devtools/security-check.py @@ -12,6 +12,7 @@ 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): ''' @@ -94,7 +95,7 @@ def check_ELF_RELRO(executable): 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]): + 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:]): have_bindnow = True return have_gnu_relro and have_bindnow @@ -114,26 +115,50 @@ def check_ELF_Canary(executable): def get_PE_dll_characteristics(executable): ''' - Get PE DllCharacteristics bits + 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': - return int(tokens[1],16) - return 0 + 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_PIE(executable): +def check_PE_DYNAMIC_BASE(executable): '''PIE: DllCharacteristics bit 0x40 signifies dynamicbase (ASLR)''' - return bool(get_PE_dll_characteristics(executable) & 0x40) + (arch,bits) = get_PE_dll_characteristics(executable) + reqbits = IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE + return (bits & reqbits) == reqbits + +# 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): + '''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)''' - return bool(get_PE_dll_characteristics(executable) & 0x100) + (arch,bits) = get_PE_dll_characteristics(executable) + return (bits & IMAGE_DLL_CHARACTERISTICS_NX_COMPAT) == IMAGE_DLL_CHARACTERISTICS_NX_COMPAT CHECKS = { 'ELF': [ @@ -143,7 +168,8 @@ CHECKS = { ('Canary', check_ELF_Canary) ], 'PE': [ - ('PIE', check_PE_PIE), + ('DYNAMIC_BASE', check_PE_DYNAMIC_BASE), + ('HIGH_ENTROPY_VA', check_PE_HIGH_ENTROPY_VA), ('NX', check_PE_NX) ] } @@ -168,12 +194,18 @@ if __name__ == '__main__': continue failed = [] + warning = [] for (name, func) in CHECKS[etype]: if not func(filename): - failed.append(name) + 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 diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index 90c21be45..de9e405e1 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -1,5 +1,5 @@ --- -name: "zcash-1.0.8" +name: "zcash-1.0.15" enable_cache: true distro: "debian" suites: @@ -85,7 +85,7 @@ script: | BASEPREFIX=`pwd`/depends # Build dependencies for each host for i in $HOSTS; do - make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}" + NO_PROTON="x" make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}" done # Faketime for binaries diff --git a/contrib/komodo-cli.bash-completion b/contrib/komodo-cli.bash-completion new file mode 100644 index 000000000..1efc05d46 --- /dev/null +++ b/contrib/komodo-cli.bash-completion @@ -0,0 +1,160 @@ +# bash programmable completion for komodo-cli(1) +# 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. + +# call $komodo-cli for RPC +_komodo_rpc() { + # determine already specified args necessary for RPC + local rpcargs=() + for i in ${COMP_LINE}; do + case "$i" in + -conf=*|-datadir=*|-regtest|-rpc*|-testnet) + rpcargs=( "${rpcargs[@]}" "$i" ) + ;; + esac + done + $komodo_cli "${rpcargs[@]}" "$@" +} + +# Add wallet accounts to COMPREPLY +_komodo_accounts() { + local accounts + # Accounts are deprecated in komodo + #accounts=$(_komodo_rpc listaccounts | awk -F '"' '{ print $2 }') + accounts="\\\"\\\"" + COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W "$accounts" -- "$cur" ) ) +} + +_komodo_cli() { + local cur prev words=() cword + local komodo_cli + + # save and use original argument to invoke komodo-cli for -help, help and RPC + # as komodo-cli might not be in $PATH + komodo_cli="$1" + + COMPREPLY=() + _get_comp_words_by_ref -n = cur prev words cword + + if ((cword > 5)); then + case ${words[cword-5]} in + sendtoaddress) + COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) + return 0 + ;; + esac + fi + + if ((cword > 4)); then + case ${words[cword-4]} in + importaddress|listtransactions|setban) + COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) + return 0 + ;; + signrawtransaction) + COMPREPLY=( $( compgen -W "ALL NONE SINGLE ALL|ANYONECANPAY NONE|ANYONECANPAY SINGLE|ANYONECANPAY" -- "$cur" ) ) + return 0 + ;; + esac + fi + + if ((cword > 3)); then + case ${words[cword-3]} in + addmultisigaddress) + _komodo_accounts + return 0 + ;; + getbalance|gettxout|importaddress|importpubkey|importprivkey|listreceivedbyaccount|listreceivedbyaddress|listsinceblock) + COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) + return 0 + ;; + esac + fi + + if ((cword > 2)); then + case ${words[cword-2]} in + addnode) + COMPREPLY=( $( compgen -W "add remove onetry" -- "$cur" ) ) + return 0 + ;; + setban) + COMPREPLY=( $( compgen -W "add remove" -- "$cur" ) ) + return 0 + ;; + fundrawtransaction|getblock|getblockheader|getmempoolancestors|getmempooldescendants|getrawtransaction|gettransaction|listaccounts|listreceivedbyaccount|listreceivedbyaddress|sendrawtransaction) + COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) + return 0 + ;; + z_importkey|z_importviewingkey) + COMPREPLY=( $( compgen -W "yes no whenkeyisnew" -- "$cur" ) ) + return 0 + ;; + move|setaccount) + _komodo_accounts + return 0 + ;; + esac + fi + + case "$prev" in + backupwallet|dumpwallet|importwallet|z_exportwallet|z_importwallet) + _filedir + return 0 + ;; + getaddednodeinfo|getrawmempool|lockunspent|setgenerate) + COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) + return 0 + ;; + getaccountaddress|getaddressesbyaccount|getbalance|getnewaddress|getreceivedbyaccount|listtransactions|move|sendfrom|sendmany) + _komodo_accounts + return 0 + ;; + esac + + case "$cur" in + -conf=*) + cur="${cur#*=}" + _filedir + return 0 + ;; + -datadir=*) + cur="${cur#*=}" + _filedir -d + return 0 + ;; + -*=*) # prevent nonsense completions + return 0 + ;; + *) + local helpopts commands + + # only parse -help if senseful + if [[ -z "$cur" || "$cur" =~ ^- ]]; then + helpopts=$($komodo_cli -help 2>&1 | awk '$1 ~ /^-/ { sub(/=.*/, "="); print $1 }' ) + fi + + # only parse help if senseful + if [[ -z "$cur" || "$cur" =~ ^[a-z] ]]; then + commands=$(_komodo_rpc help 2>/dev/null | awk '$1 ~ /^[a-z]/ { print $1; }') + fi + + COMPREPLY=( $( compgen -W "$helpopts $commands" -- "$cur" ) ) + + # Prevent space if an argument is desired + if [[ $COMPREPLY == *= ]]; then + compopt -o nospace + fi + return 0 + ;; + esac +} && +complete -F _komodo_cli komodo-cli + +# Local variables: +# mode: shell-script +# sh-basic-offset: 4 +# sh-indent-comment: t +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et filetype=sh diff --git a/contrib/bitcoin-tx.bash-completion b/contrib/komodo-tx.bash-completion similarity index 74% rename from contrib/bitcoin-tx.bash-completion rename to contrib/komodo-tx.bash-completion index 0206eba74..69e259381 100644 --- a/contrib/bitcoin-tx.bash-completion +++ b/contrib/komodo-tx.bash-completion @@ -1,15 +1,15 @@ -# bash programmable completion for bitcoin-tx(1) +# bash programmable completion for komodo-tx(1) # Copyright (c) 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. -_bitcoin_tx() { +_komodo_tx() { local cur prev words=() cword - local bitcoin_tx + local komodo_tx - # save and use original argument to invoke bitcoin-tx for -help + # save and use original argument to invoke komodo-tx for -help # it might not be in $PATH - bitcoin_tx="$1" + komodo_tx="$1" COMPREPLY=() _get_comp_words_by_ref -n =: cur prev words cword @@ -27,15 +27,15 @@ _bitcoin_tx() { if [[ "$cword" == 1 || ( "$prev" != "-create" && "$prev" == -* ) ]]; then # only options (or an uncompletable hex-string) allowed - # parse bitcoin-tx -help for options + # parse komodo-tx -help for options local helpopts - helpopts=$($bitcoin_tx -help | sed -e '/^ -/ p' -e d ) + helpopts=$($komodo_tx -help | sed -e '/^ -/ p' -e d ) COMPREPLY=( $( compgen -W "$helpopts" -- "$cur" ) ) else # only commands are allowed # parse -help for commands local helpcmds - helpcmds=$($bitcoin_tx -help | sed -e '1,/Commands:/d' -e 's/=.*/=/' -e '/^ [a-z]/ p' -e d ) + helpcmds=$($komodo_tx -help | sed -e '1,/Commands:/d' -e 's/=.*/=/' -e '/^ [a-z]/ p' -e d ) COMPREPLY=( $( compgen -W "$helpcmds" -- "$cur" ) ) fi @@ -46,7 +46,7 @@ _bitcoin_tx() { return 0 } && -complete -F _bitcoin_tx zcash-tx +complete -F _komodo_tx komodo-tx # Local variables: # mode: shell-script diff --git a/contrib/komodod.bash-completion b/contrib/komodod.bash-completion new file mode 100644 index 000000000..4c1ec516f --- /dev/null +++ b/contrib/komodod.bash-completion @@ -0,0 +1,57 @@ +# bash programmable completion for komodod(1) +# Copyright (c) 2012-2017 The Bitcoin Core developers +# Copyright (c) 2016-2017 The komodo developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +_komodod() { + local cur prev words=() cword + local komodod + + # save and use original argument to invoke komodod for -help + # it might not be in $PATH + komodod="$1" + + COMPREPLY=() + _get_comp_words_by_ref -n = cur prev words cword + + case "$cur" in + -conf=*|-pid=*|-loadblock=*|-rpccookiefile=*|-wallet=*) + cur="${cur#*=}" + _filedir + return 0 + ;; + -datadir=*|-exportdir=*) + cur="${cur#*=}" + _filedir -d + return 0 + ;; + -*=*) # prevent nonsense completions + return 0 + ;; + *) + + # only parse -help if senseful + if [[ -z "$cur" || "$cur" =~ ^- ]]; then + local helpopts + helpopts=$($komodod -help 2>&1 | awk '$1 ~ /^-/ { sub(/=.*/, "="); print $1 }' ) + COMPREPLY=( $( compgen -W "$helpopts" -- "$cur" ) ) + fi + + # Prevent space if an argument is desired + if [[ $COMPREPLY == *= ]]; then + compopt -o nospace + fi + return 0 + ;; + esac +} && +complete -F _komodod komodod + +# Local variables: +# mode: shell-script +# sh-basic-offset: 4 +# sh-indent-comment: t +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et filetype=sh diff --git a/contrib/bitcoin-cli.bash-completion b/contrib/zcash-cli.bash-completion similarity index 88% rename from contrib/bitcoin-cli.bash-completion rename to contrib/zcash-cli.bash-completion index f2a44d232..37fa1d116 100644 --- a/contrib/bitcoin-cli.bash-completion +++ b/contrib/zcash-cli.bash-completion @@ -1,9 +1,9 @@ -# bash programmable completion for bitcoin-cli(1) +# bash programmable completion for zcash-cli(1) # 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. -# call $bitcoin-cli for RPC +# call $zcash-cli for RPC _zcash_rpc() { # determine already specified args necessary for RPC local rpcargs=() @@ -14,7 +14,7 @@ _zcash_rpc() { ;; esac done - $bitcoin_cli "${rpcargs[@]}" "$@" + $zcash_cli "${rpcargs[@]}" "$@" } # Add wallet accounts to COMPREPLY @@ -28,11 +28,11 @@ _zcash_accounts() { _zcash_cli() { local cur prev words=() cword - local bitcoin_cli + local zcash_cli - # save and use original argument to invoke bitcoin-cli for -help, help and RPC - # as bitcoin-cli might not be in $PATH - bitcoin_cli="$1" + # save and use original argument to invoke zcash-cli for -help, help and RPC + # as zcash-cli might not be in $PATH + zcash_cli="$1" COMPREPLY=() _get_comp_words_by_ref -n = cur prev words cword @@ -82,10 +82,14 @@ _zcash_cli() { COMPREPLY=( $( compgen -W "add remove" -- "$cur" ) ) return 0 ;; - fundrawtransaction|getblock|getblockheader|getmempoolancestors|getmempooldescendants|getrawtransaction|gettransaction|listaccounts|listreceivedbyaccount|listreceivedbyaddress|sendrawtransaction|z_importkey) + fundrawtransaction|getblock|getblockheader|getmempoolancestors|getmempooldescendants|getrawtransaction|gettransaction|listaccounts|listreceivedbyaccount|listreceivedbyaddress|sendrawtransaction) COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) return 0 ;; + z_importkey|z_importviewingkey) + COMPREPLY=( $( compgen -W "yes no whenkeyisnew" -- "$cur" ) ) + return 0 + ;; move|setaccount) _zcash_accounts return 0 @@ -127,7 +131,7 @@ _zcash_cli() { # only parse -help if senseful if [[ -z "$cur" || "$cur" =~ ^- ]]; then - helpopts=$($bitcoin_cli -help 2>&1 | awk '$1 ~ /^-/ { sub(/=.*/, "="); print $1 }' ) + helpopts=$($zcash_cli -help 2>&1 | awk '$1 ~ /^-/ { sub(/=.*/, "="); print $1 }' ) fi # only parse help if senseful diff --git a/contrib/zcash-tx.bash-completion b/contrib/zcash-tx.bash-completion new file mode 100644 index 000000000..e808f93cb --- /dev/null +++ b/contrib/zcash-tx.bash-completion @@ -0,0 +1,57 @@ +# bash programmable completion for zcash-tx(1) +# Copyright (c) 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. + +_zcash_tx() { + local cur prev words=() cword + local zcash_tx + + # save and use original argument to invoke zcash-tx for -help + # it might not be in $PATH + zcash_tx="$1" + + COMPREPLY=() + _get_comp_words_by_ref -n =: cur prev words cword + + case "$cur" in + load=*:*) + cur="${cur#load=*:}" + _filedir + return 0 + ;; + *=*) # prevent attempts to complete other arguments + return 0 + ;; + esac + + if [[ "$cword" == 1 || ( "$prev" != "-create" && "$prev" == -* ) ]]; then + # only options (or an uncompletable hex-string) allowed + # parse zcash-tx -help for options + local helpopts + helpopts=$($zcash_tx -help | sed -e '/^ -/ p' -e d ) + COMPREPLY=( $( compgen -W "$helpopts" -- "$cur" ) ) + else + # only commands are allowed + # parse -help for commands + local helpcmds + helpcmds=$($zcash_tx -help | sed -e '1,/Commands:/d' -e 's/=.*/=/' -e '/^ [a-z]/ p' -e d ) + COMPREPLY=( $( compgen -W "$helpcmds" -- "$cur" ) ) + fi + + # Prevent space if an argument is desired + if [[ $COMPREPLY == *= ]]; then + compopt -o nospace + fi + + return 0 +} && +complete -F _zcash_tx zcash-tx + +# Local variables: +# mode: shell-script +# sh-basic-offset: 4 +# sh-indent-comment: t +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et filetype=sh diff --git a/contrib/bitcoind.bash-completion b/contrib/zcashd.bash-completion similarity index 89% rename from contrib/bitcoind.bash-completion rename to contrib/zcashd.bash-completion index 104365024..65f07cd80 100644 --- a/contrib/bitcoind.bash-completion +++ b/contrib/zcashd.bash-completion @@ -6,11 +6,11 @@ _zcashd() { local cur prev words=() cword - local bitcoind + local zcashd # save and use original argument to invoke zcashd for -help # it might not be in $PATH - bitcoind="$1" + zcashd="$1" COMPREPLY=() _get_comp_words_by_ref -n = cur prev words cword @@ -21,7 +21,7 @@ _zcashd() { _filedir return 0 ;; - -datadir=*) + -datadir=*|-exportdir=*) cur="${cur#*=}" _filedir -d return 0 @@ -34,7 +34,7 @@ _zcashd() { # only parse -help if senseful if [[ -z "$cur" || "$cur" =~ ^- ]]; then local helpopts - helpopts=$($bitcoind -help 2>&1 | awk '$1 ~ /^-/ { sub(/=.*/, "="); print $1 }' ) + helpopts=$($zcashd -help 2>&1 | awk '$1 ~ /^-/ { sub(/=.*/, "="); print $1 }' ) COMPREPLY=( $( compgen -W "$helpopts" -- "$cur" ) ) fi diff --git a/depends/Makefile b/depends/Makefile index b6beea3d4..472d87885 100644 --- a/depends/Makefile +++ b/depends/Makefile @@ -74,9 +74,9 @@ include packages/packages.mk rust_packages_$(NO_RUST) = $(rust_packages) wallet_packages_$(NO_WALLET) = $(wallet_packages) -upnp_packages_$(NO_UPNP) = $(upnp_packages) +proton_packages_$(NO_PROTON) = $(proton_packages) -packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(rust_packages_) $(wallet_packages_) $(upnp_packages_) +packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(rust_packages_) $(proton_packages_) $(wallet_packages_) native_packages += $($(host_arch)_$(host_os)_native_packages) $($(host_os)_native_packages) all_packages = $(packages) $(native_packages) @@ -113,7 +113,6 @@ $(host_prefix)/share/config.site : config.site.in $(host_prefix)/.stamp_$(final_ -e 's|@CPPFLAGS@|$(strip $(host_CPPFLAGS) $(host_$(release_type)_CPPFLAGS))|' \ -e 's|@LDFLAGS@|$(strip $(host_LDFLAGS) $(host_$(release_type)_LDFLAGS))|' \ -e 's|@no_wallet@|$(NO_WALLET)|' \ - -e 's|@no_upnp@|$(NO_UPNP)|' \ -e 's|@debug@|$(DEBUG)|' \ $< > $@ $(AT)touch $@ diff --git a/depends/README.md b/depends/README.md index 663d49584..41898de1a 100644 --- a/depends/README.md +++ b/depends/README.md @@ -33,9 +33,8 @@ 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 sdk's can be found (used by OSX) - FALLBACK_DOWNLOAD_PATH: If a source file can't be fetched, try here before giving up + 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 - NO_UPNP: Don't download/build/cache packages needed for enabling upnp DEBUG: disable some optimizations and enable more runtime checking If some packages are not built, for example `make NO_WALLET=1`, the appropriate diff --git a/depends/builders/darwin.mk b/depends/builders/darwin.mk index 0028d3f6f..b986aecec 100644 --- a/depends/builders/darwin.mk +++ b/depends/builders/darwin.mk @@ -7,11 +7,19 @@ 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 --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -L -f -o +#<<<<<<< HEAD +##build_darwin_DOWNLOAD = curl --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -L -f -o #darwin host on darwin builder. overrides darwin host preferences. -darwin_CC= gcc-5 -darwin_CXX= g++-5 +#darwin_CC= gcc-5 +#darwin_CXX= g++-5 +#======= +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) +darwin_CXX:=$(shell xcrun -f clang++) -mmacosx-version-min=$(OSX_MIN_VERSION) -stdlib=libc++ +#>>>>>>> zcash/master darwin_AR:=$(shell xcrun -f ar) darwin_RANLIB:=$(shell xcrun -f ranlib) darwin_STRIP:=$(shell xcrun -f strip) diff --git a/depends/builders/linux.mk b/depends/builders/linux.mk index 98d0e9de3..b03f42401 100644 --- a/depends/builders/linux.mk +++ b/depends/builders/linux.mk @@ -1,2 +1,2 @@ build_linux_SHA256SUM = sha256sum -build_linux_DOWNLOAD = wget --timeout=$(DOWNLOAD_CONNECT_TIMEOUT) --tries=$(DOWNLOAD_RETRIES) -nv -O +build_linux_DOWNLOAD = curl --location --fail --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -o diff --git a/depends/config.site.in b/depends/config.site.in index 153330360..8cdbcd2e4 100644 --- a/depends/config.site.in +++ b/depends/config.site.in @@ -7,20 +7,12 @@ ac_tool_prefix=${host_alias}- if test -z $with_boost; then with_boost=$depends_prefix fi -# Disable comparison utility (#592) -#if test -z $with_comparison_tool; then -# with_comparison_tool=$depends_prefix/native/share/BitcoindComparisonTool_jar/BitcoindComparisonTool.jar -#fi if test -z $enable_wallet && test -n "@no_wallet@"; then enable_wallet=no fi -if test -z $with_miniupnpc && test -n "@no_upnp@"; then - with_miniupnpc=no -fi - if test x@host_os@ = xdarwin; then BREW=no PORT=no diff --git a/depends/funcs.mk b/depends/funcs.mk index addd1a510..df305a74a 100644 --- a/depends/funcs.mk +++ b/depends/funcs.mk @@ -22,7 +22,8 @@ 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" "$(2)/$(3)" ) && \ + ( $(build_DOWNLOAD) "$$($(1)_download_dir)/$(4).temp" "$(PRIORITY_DOWNLOAD_PATH)/$(4)" || \ + $(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) && \ @@ -42,6 +43,10 @@ $(eval $(1)_build_id_long:=$(1)-$($(1)_version)-$($(1)_recipe_hash)-$(release_ty $(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)_file_name_$(host_os)),$($(1)_file_name_$(host_os)),$($(1)_file_name))) +$(eval $(1)_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) @@ -124,9 +129,9 @@ $(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+=PATH=$(build_prefix)/bin:$(PATH) -$(1)_build_env+=PATH=$(build_prefix)/bin:$(PATH) -$(1)_stage_env+=PATH=$(build_prefix)/bin:$(PATH) +$(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)" ifneq ($($(1)_nm),) diff --git a/depends/hosts/darwin.mk b/depends/hosts/darwin.mk index 52c0003e4..4f1748657 100644 --- a/depends/hosts/darwin.mk +++ b/depends/hosts/darwin.mk @@ -1,9 +1,15 @@ -OSX_MIN_VERSION=10.7 -OSX_SDK_VERSION=10.9 +OSX_MIN_VERSION=10.8 +OSX_SDK_VERSION=10.11 OSX_SDK=$(SDK_PATH)/MacOSX$(OSX_SDK_VERSION).sdk -LD64_VERSION=241.9 -darwin_CC=gcc-5 -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION) -darwin_CXX=g++-5 -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION) +#<<<<<<< HEAD +#LD64_VERSION=241.9 +#darwin_CC=gcc-5 -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION) +#darwin_CXX=g++-5 -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION) +#======= +LD64_VERSION=253.9 +darwin_CC=clang -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION) +darwin_CXX=clang++ -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION) -stdlib=libc++ +#>>>>>>> zcash/master darwin_CFLAGS=-pipe darwin_CXXFLAGS=$(darwin_CFLAGS) diff --git a/depends/packages/bdb.mk b/depends/packages/bdb.mk index 7babd175a..3ff5a7bd9 100644 --- a/depends/packages/bdb.mk +++ b/depends/packages/bdb.mk @@ -9,6 +9,12 @@ define $(package)_set_vars $(package)_config_opts=--disable-shared --enable-cxx --disable-replication $(package)_config_opts_mingw32=--enable-mingw $(package)_config_opts_linux=--with-pic +$(package)_cxxflags=-std=c++11 +endef + +define $(package)_preprocess_cmds + 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 endef define $(package)_config_cmds diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk index 679faacde..099993a62 100644 --- a/depends/packages/boost.mk +++ b/depends/packages/boost.mk @@ -9,7 +9,7 @@ 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+=threading=multi link=static -sNO_BZIP2=1 -sNO_ZLIB=1 +$(package)_config_opts+=link=static -sNO_BZIP2=1 -sNO_ZLIB=1 $(package)_config_opts_linux=threadapi=pthread runtime-link=shared $(package)_config_opts_darwin=--toolset=gcc threadapi=pthread runtime-link=shared $(package)_config_opts_mingw32=binary-format=pe target-os=windows threadapi=win32 runtime-link=static @@ -21,7 +21,7 @@ $(package)_archiver_$(host_os)=$($(package)_ar) $(package)_toolset_darwin=gcc $(package)_archiver_darwin=$($(package)_ar) $(package)_config_libraries=chrono,filesystem,program_options,system,thread,test -$(package)_cxxflags=-fvisibility=hidden +$(package)_cxxflags=-std=c++11 -fvisibility=hidden $(package)_cxxflags_linux=-fPIC endef diff --git a/depends/packages/googlemock.mk b/depends/packages/googlemock.mk index 67246ae75..2809fdeb7 100644 --- a/depends/packages/googlemock.mk +++ b/depends/packages/googlemock.mk @@ -4,7 +4,7 @@ package=googlemock $(package)_version=1.7.0 $(package)_dependencies=googletest -$(package)_download_path=https://github.com/google/$(package)/archive/ +$(package)_download_path=https://github.com/google/$(package)/archive $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_download_file=release-$($(package)_version).tar.gz $(package)_sha256_hash=3f20b6acb37e5a98e8c4518165711e3e35d47deb6cdb5a4dd4566563b5efd232 diff --git a/depends/packages/googletest.mk b/depends/packages/googletest.mk index 652e97aaa..b9fa3e7e8 100644 --- a/depends/packages/googletest.mk +++ b/depends/packages/googletest.mk @@ -1,9 +1,9 @@ package=googletest -$(package)_version=1.7.0 +$(package)_version=1.8.0 $(package)_download_path=https://github.com/google/$(package)/archive/ $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_download_file=release-$($(package)_version).tar.gz -$(package)_sha256_hash=f73a6546fdf9fce9ff93a5015e0333a8af3062a152a9ad6bcb772c96687016cc +$(package)_sha256_hash=58a6f4277ca2bc8565222b3bbd58a177609e9c488e8a72649359ba51450db7d8 ifeq ($(build_os),darwin) define $(package)_set_vars @@ -19,11 +19,15 @@ endef else $(package)_install=install define $(package)_build_cmds - $(MAKE) -C make CXXFLAGS=-fPIC gtest.a + $(MAKE) -C googlemock/make CXXFLAGS=-fPIC gmock.a && \ + $(MAKE) -C googletest/make CXXFLAGS=-fPIC gtest.a endef endif define $(package)_stage_cmds - $($(package)_install) -D ./make/gtest.a $($(package)_staging_dir)$(host_prefix)/lib/libgtest.a && \ - cp -a ./include $($(package)_staging_dir)$(host_prefix)/include + mkdir -p $($(package)_staging_dir)$(host_prefix)/lib && \ + install ./googlemock/make/gmock.a $($(package)_staging_dir)$(host_prefix)/lib/libgmock.a && \ + install ./googletest/make/gtest.a $($(package)_staging_dir)$(host_prefix)/lib/libgtest.a && \ + cp -a ./googlemock/include $($(package)_staging_dir)$(host_prefix)/ && \ + cp -a ./googletest/include $($(package)_staging_dir)$(host_prefix)/ endef diff --git a/depends/packages/libsnark.mk b/depends/packages/libsnark.mk index 00c897d73..cb4bc04ca 100644 --- a/depends/packages/libsnark.mk +++ b/depends/packages/libsnark.mk @@ -1,6 +1,6 @@ package=libsnark $(package)_version=0.1 -$(package)_download_path=https://supernetorg.bintray.com/misc/ +$(package)_download_path=https://supernetorg.bintray.com/misc $(package)_file_name=$(package)-$($(package)_git_commit).tar.gz $(package)_download_file=$(package)-$($(package)_git_commit).tar.gz $(package)_sha256_hash=47478adc2ae88c448dc736d59dfe007de6478e41e88d2d4d2ff4135a17ee6f90 diff --git a/depends/packages/libsodium.mk b/depends/packages/libsodium.mk index 371225014..91e6f27b7 100644 --- a/depends/packages/libsodium.mk +++ b/depends/packages/libsodium.mk @@ -1,8 +1,15 @@ package=libsodium -$(package)_version=1.0.11 -$(package)_download_path=https://supernetorg.bintray.com/misc -$(package)_file_name=libsodium-1.0.11.tar.gz -$(package)_sha256_hash=a14549db3c49f6ae2170cbbf4664bd48ace50681045e8dbea7c8d9fb96f9c765 +#<<<<<<< HEAD +#$(package)_version=1.0.11 +#$(package)_download_path=https://supernetorg.bintray.com/misc +#$(package)_file_name=libsodium-1.0.11.tar.gz +#$(package)_sha256_hash=a14549db3c49f6ae2170cbbf4664bd48ace50681045e8dbea7c8d9fb96f9c765 +#======= +$(package)_version=1.0.15 +$(package)_download_path=https://download.libsodium.org/libsodium/releases/ +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=fb6a9e879a2f674592e4328c5d9f79f082405ee4bb05cb6e679b90afe9e178f4 +#>>>>>>> zcash/master $(package)_dependencies= $(package)_config_opts= diff --git a/depends/packages/miniupnpc.mk b/depends/packages/miniupnpc.mk deleted file mode 100644 index 0d8efbc3f..000000000 --- a/depends/packages/miniupnpc.mk +++ /dev/null @@ -1,32 +0,0 @@ -package=miniupnpc -$(package)_version=2.0 -$(package)_download_path=http://miniupnp.free.fr/files -$(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=d434ceb8986efbe199c5ca53f90ed53eab290b1e6d0530b717eb6fa49d61f93b -$(package)_patches=fix-solaris-compilation.patch strlen-before-memcmp.patch patch-strlen-patch.patch - -define $(package)_set_vars -$(package)_build_opts=CC="$($(package)_cc)" -$(package)_build_opts_darwin=OS=Darwin -$(package)_build_opts_mingw32=-f Makefile.mingw -$(package)_build_env+=CFLAGS="$($(package)_cflags) $($(package)_cppflags)" AR="$($(package)_ar)" -endef - -define $(package)_preprocess_cmds - mkdir dll && \ - sed -e 's|MINIUPNPC_VERSION_STRING \"version\"|MINIUPNPC_VERSION_STRING \"$($(package)_version)\"|' -e 's|OS/version|$(host)|' miniupnpcstrings.h.in > miniupnpcstrings.h && \ - sed -i.old "s|miniupnpcstrings.h: miniupnpcstrings.h.in wingenminiupnpcstrings|miniupnpcstrings.h: miniupnpcstrings.h.in|" Makefile.mingw && \ - patch -p2 < $($(package)_patch_dir)/fix-solaris-compilation.patch && \ - patch -p2 < $($(package)_patch_dir)/strlen-before-memcmp.patch && \ - patch -p2 < $($(package)_patch_dir)/patch-strlen-patch.patch -endef - -define $(package)_build_cmds - $(MAKE) libminiupnpc.a $($(package)_build_opts) -endef - -define $(package)_stage_cmds - mkdir -p $($(package)_staging_prefix_dir)/include/miniupnpc $($(package)_staging_prefix_dir)/lib &&\ - install *.h $($(package)_staging_prefix_dir)/include/miniupnpc &&\ - install libminiupnpc.a $($(package)_staging_prefix_dir)/lib -endef diff --git a/depends/packages/openssl.mk b/depends/packages/openssl.mk index fe19c6734..f80cd6d25 100644 --- a/depends/packages/openssl.mk +++ b/depends/packages/openssl.mk @@ -85,6 +85,7 @@ $(package)_config_opts_arm_linux=linux-generic32 $(package)_config_opts_aarch64_linux=linux-generic64 $(package)_config_opts_mipsel_linux=linux-generic32 $(package)_config_opts_mips_linux=linux-generic32 +$(package)_config_opts_powerpc_linux=linux-generic32 $(package)_config_opts_x86_64_darwin=darwin64-x86_64-cc $(package)_config_opts_x86_64_mingw32=mingw64 $(package)_config_opts_i686_mingw32=mingw diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index b1951431a..3c78e29c3 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -1,15 +1,13 @@ rust_packages := rust librustzcash -zcash_packages := libsnark libgmp libsodium +proton_packages := proton +zcash_packages := libgmp libsodium ifeq ($(host_os),linux) - packages := boost openssl libevent zeromq $(zcash_packages) googletest googlemock + packages := boost openssl libevent zeromq $(zcash_packages) googletest #googlemock else - packages := boost openssl libevent zeromq $(zcash_packages) googletest googlemock libcurl + packages := boost openssl libevent zeromq $(zcash_packages) libcurl googletest #googlemock endif - native_packages := native_ccache wallet_packages=bdb - -upnp_packages=miniupnpc diff --git a/depends/packages/proton.mk b/depends/packages/proton.mk new file mode 100644 index 000000000..aa49f380f --- /dev/null +++ b/depends/packages/proton.mk @@ -0,0 +1,24 @@ +package=proton +$(package)_version=0.17.0 +$(package)_download_path=http://apache.cs.utah.edu/qpid/proton/$($(package)_version) +$(package)_file_name=qpid-proton-$($(package)_version).tar.gz +$(package)_sha256_hash=6ffd26d3d0e495bfdb5d9fefc5349954e6105ea18cc4bb191161d27742c5a01a +$(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_PHP=OFF -DBUILD_JAVA=OFF -DBUILD_PERL=OFF -DBUILD_RUBY=OFF -DBUILD_JAVASCRIPT=OFF -DBUILD_GO=OFF +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/packages/rust.mk b/depends/packages/rust.mk index 2e3f0b204..eb28c76d1 100644 --- a/depends/packages/rust.mk +++ b/depends/packages/rust.mk @@ -1,16 +1,23 @@ package=rust $(package)_version=1.16.0 $(package)_download_path=https://static.rust-lang.org/dist -ifeq ($(build_os),darwin) -$(package)_file_name=rust-$($(package)_version)-x86_64-apple-darwin.tar.gz -$(package)_sha256_hash=2d08259ee038d3a2c77a93f1a31fc59e7a1d6d1bbfcba3dba3c8213b2e5d1926 -else ifeq ($(host_os),mingw32) -$(package)_file_name=rust-$($(package)_version)-i686-unknown-linux-gnu.tar.gz -$(package)_sha256_hash=b5859161ebb182d3b75fa14a5741e5de87b088146fb0ef4a30f3b2439c6179c5 -else -$(package)_file_name=rust-$($(package)_version)-x86_64-unknown-linux-gnu.tar.gz -$(package)_sha256_hash=48621912c242753ba37cad5145df375eeba41c81079df46f93ffb4896542e8fd -endif +#<<<<<<< HEAD +#ifeq ($(build_os),darwin) +#$(package)_file_name=rust-$($(package)_version)-x86_64-apple-darwin.tar.gz +#$(package)_sha256_hash=2d08259ee038d3a2c77a93f1a31fc59e7a1d6d1bbfcba3dba3c8213b2e5d1926 +#else ifeq ($(host_os),mingw32) +#$(package)_file_name=rust-$($(package)_version)-i686-unknown-linux-gnu.tar.gz +#$(package)_sha256_hash=b5859161ebb182d3b75fa14a5741e5de87b088146fb0ef4a30f3b2439c6179c5 +#else +#$(package)_file_name=rust-$($(package)_version)-x86_64-unknown-linux-gnu.tar.gz +#$(package)_sha256_hash=48621912c242753ba37cad5145df375eeba41c81079df46f93ffb4896542e8fd +#endif +#======= +$(package)_file_name_linux=rust-$($(package)_version)-x86_64-unknown-linux-gnu.tar.gz +$(package)_sha256_hash_linux=48621912c242753ba37cad5145df375eeba41c81079df46f93ffb4896542e8fd +$(package)_file_name_darwin=rust-$($(package)_version)-x86_64-apple-darwin.tar.gz +$(package)_sha256_hash_darwin=2d08259ee038d3a2c77a93f1a31fc59e7a1d6d1bbfcba3dba3c8213b2e5d1926 +#>>>>>>> zcash/master define $(package)_stage_cmds ./install.sh --destdir=$($(package)_staging_dir) --prefix=$(host_prefix)/native --disable-ldconfig diff --git a/depends/packages/zeromq.mk b/depends/packages/zeromq.mk index 8d324a3f4..850cce778 100644 --- a/depends/packages/zeromq.mk +++ b/depends/packages/zeromq.mk @@ -22,6 +22,7 @@ $(package)_sha256_hash=27d1e82a099228ee85a7ddb2260f40830212402c605a4a10b5e5498a7 define $(package)_set_vars $(package)_config_opts=--without-documentation --disable-shared --disable-curve $(package)_config_opts_linux=--with-pic + $(package)_cxxflags=-std=c++11 endef endif diff --git a/depends/patches/miniupnpc/fix-solaris-compilation.patch b/depends/patches/miniupnpc/fix-solaris-compilation.patch deleted file mode 100644 index 30eb3b106..000000000 --- a/depends/patches/miniupnpc/fix-solaris-compilation.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 71ce1d6dfa5424f8fe8633e23494c7638ea2c79e Mon Sep 17 00:00:00 2001 -From: Thomas Bernard -Date: Thu, 10 Nov 2016 21:55:33 +0100 -Subject: [PATCH] fix for Solaris 11 compilation - -see #216 ---- - miniupnpc/Makefile | 2 ++ - miniupnpc/minissdpc.c | 3 +++ - 2 files changed, 5 insertions(+) - -diff --git a/miniupnpc/Makefile b/miniupnpc/Makefile -index 5c23000..72cdc0f 100644 ---- a/miniupnpc/Makefile -+++ b/miniupnpc/Makefile -@@ -43,10 +43,12 @@ CFLAGS += -D_NETBSD_SOURCE - endif - ifneq ($(OS), FreeBSD) - ifneq ($(OS), Darwin) -+ifneq ($(OS), SunOS) - #CFLAGS += -D_POSIX_C_SOURCE=200112L - CFLAGS += -D_XOPEN_SOURCE=600 - endif - endif -+endif - #CFLAGS += -ansi - # -DNO_GETADDRINFO - INSTALL = install -diff --git a/miniupnpc/minissdpc.c b/miniupnpc/minissdpc.c -index f200f07..263160e 100644 ---- a/miniupnpc/minissdpc.c -+++ b/miniupnpc/minissdpc.c -@@ -73,6 +73,9 @@ struct sockaddr_un { - - #if !defined(HAS_IP_MREQN) && !defined(_WIN32) - #include -+#if defined(__sun) -+#include -+#endif - #endif - - #if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN) diff --git a/depends/patches/miniupnpc/patch-strlen-patch.patch b/depends/patches/miniupnpc/patch-strlen-patch.patch deleted file mode 100644 index df7140234..000000000 --- a/depends/patches/miniupnpc/patch-strlen-patch.patch +++ /dev/null @@ -1,22 +0,0 @@ -From 0aa7c46227acd8ddb135c577674ad454bf2fba86 Mon Sep 17 00:00:00 2001 -From: Thomas Bernard -Date: Fri, 11 Nov 2016 17:53:21 +0100 -Subject: [PATCH] remove unsigned/signed comparison - ---- - miniupnpc/portlistingparse.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/miniupnpc/portlistingparse.c b/miniupnpc/portlistingparse.c -index 1bed763..07f3f87 100644 ---- a/miniupnpc/portlistingparse.c -+++ b/miniupnpc/portlistingparse.c -@@ -55,7 +55,7 @@ startelt(void * d, const char * name, int l) - pdata->curelt = PortMappingEltNone; - for(i = 0; elements[i].str; i++) - { -- if(strlen(elements[i].str) == l && memcmp(name, elements[i].str, l) == 0) -+ if(strlen(elements[i].str) == (size_t)l && memcmp(name, elements[i].str, l) == 0) - { - pdata->curelt = elements[i].code; - break; diff --git a/depends/patches/miniupnpc/strlen-before-memcmp.patch b/depends/patches/miniupnpc/strlen-before-memcmp.patch deleted file mode 100644 index 8e1f2005e..000000000 --- a/depends/patches/miniupnpc/strlen-before-memcmp.patch +++ /dev/null @@ -1,23 +0,0 @@ -From ec1c49bb0cd5e448e6f0adee7de3a831c4869bdd Mon Sep 17 00:00:00 2001 -From: Thomas Bernard -Date: Fri, 11 Nov 2016 17:24:39 +0100 -Subject: [PATCH] check strlen before memcmp - -1st try to fix #220 ---- - miniupnpc/portlistingparse.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/miniupnpc/portlistingparse.c b/miniupnpc/portlistingparse.c -index 0e09278..1bed763 100644 ---- a/miniupnpc/portlistingparse.c -+++ b/miniupnpc/portlistingparse.c -@@ -55,7 +55,7 @@ startelt(void * d, const char * name, int l) - pdata->curelt = PortMappingEltNone; - for(i = 0; elements[i].str; i++) - { -- if(memcmp(name, elements[i].str, l) == 0) -+ if(strlen(elements[i].str) == l && memcmp(name, elements[i].str, l) == 0) - { - pdata->curelt = elements[i].code; - break; diff --git a/depends/patches/proton/minimal-build.patch b/depends/patches/proton/minimal-build.patch new file mode 100644 index 000000000..90588929f --- /dev/null +++ b/depends/patches/proton/minimal-build.patch @@ -0,0 +1,288 @@ +From 03f5fc0826115edbfca468261b70c0daf627f488 Mon Sep 17 00:00:00 2001 +From: Simon +Date: Thu, 27 Apr 2017 17:15:59 -0700 +Subject: [PATCH] Enable C++11, build static library and cpp bindings with minimal dependencies. + +--- + CMakeLists.txt | 13 +++++++------ + examples/cpp/CMakeLists.txt | 1 + + proton-c/CMakeLists.txt | 32 +++++++++++++++---------------- + proton-c/bindings/CMakeLists.txt | 6 +++--- + proton-c/bindings/cpp/CMakeLists.txt | 24 +++++++++++------------ + proton-c/bindings/cpp/docs/CMakeLists.txt | 2 +- + proton-c/docs/api/CMakeLists.txt | 2 +- + 7 files changed, 41 insertions(+), 39 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index b538ffd..4a5e787 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -18,14 +18,15 @@ + # + cmake_minimum_required (VERSION 2.8.7) + ++set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + project (Proton C) + + # Enable C++ now for examples and bindings subdirectories, but make it optional. + enable_language(CXX OPTIONAL) + + # Enable testing +-enable_testing() +-include (CTest) ++#enable_testing() ++#include (CTest) + + # Pull in local cmake modules + set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/tools/cmake/Modules/") +@@ -141,7 +142,7 @@ set (BINDINGS_DIR ${LIB_INSTALL_DIR}/proton/bindings) + + 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 PERL PHP PYTHON RUBY) ++#set (BINDING_LANGS PERL PHP PYTHON RUBY) + + foreach (LANG ${BINDING_LANGS}) + set (SYSINSTALL_${LANG} OFF CACHE BOOL "Install ${LANG} bindings into interpreter specified location.") +@@ -156,10 +157,10 @@ set (PROTON_SHARE ${SHARE_INSTALL_DIR}/proton-${PN_VERSION}) + # End of variables used during install + + # Check for valgrind here so tests under proton-c/ and examples/ can use it. +-find_program(VALGRIND_EXE valgrind DOC "Location of the valgrind program") ++#find_program(VALGRIND_EXE valgrind DOC "Location of the valgrind program") + mark_as_advanced (VALGRIND_EXE) + +-option(ENABLE_VALGRIND "Use valgrind to detect run-time problems" ON) ++#option(ENABLE_VALGRIND "Use valgrind to detect run-time problems" ON) + if (ENABLE_VALGRIND) + if (NOT VALGRIND_EXE) + message(STATUS "Can't locate the valgrind command; no run-time error detection") +@@ -171,7 +172,7 @@ if (ENABLE_VALGRIND) + endif (ENABLE_VALGRIND) + + add_subdirectory(proton-c) +-add_subdirectory(examples) ++#add_subdirectory(examples) + + install (FILES LICENSE README.md + DESTINATION ${PROTON_SHARE}) +diff --git a/examples/cpp/CMakeLists.txt b/examples/cpp/CMakeLists.txt +index 304d899..f4877b4 100644 +--- a/examples/cpp/CMakeLists.txt ++++ b/examples/cpp/CMakeLists.txt +@@ -17,6 +17,7 @@ + # under the License. + # + ++set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + find_package(ProtonCpp REQUIRED) + + include_directories(${ProtonCpp_INCLUDE_DIRS}) +diff --git a/proton-c/CMakeLists.txt b/proton-c/CMakeLists.txt +index 8edb661..dc7b99c 100644 +--- a/proton-c/CMakeLists.txt ++++ b/proton-c/CMakeLists.txt +@@ -22,24 +22,24 @@ include(CheckSymbolExists) + + include(soversion.cmake) + +-add_custom_target(docs) +-add_custom_target(doc DEPENDS docs) ++#add_custom_target(docs) ++#add_custom_target(doc DEPENDS docs) + + # Set the default SSL/TLS implementation +-find_package(OpenSSL) ++#find_package(OpenSSL) + find_package(PythonInterp REQUIRED) +-find_package(SWIG) ++#find_package(SWIG) + # FindSwig.cmake "forgets" make its outputs advanced like a good citizen + mark_as_advanced(SWIG_DIR SWIG_EXECUTABLE SWIG_VERSION) + + # See if Cyrus SASL is available +-find_library(CYRUS_SASL_LIBRARY sasl2) +-find_path(CYRUS_SASL_INCLUDE_DIR sasl/sasl.h PATH_SUFFIXES include) +-find_package_handle_standard_args(CyrusSASL DEFAULT_MSG CYRUS_SASL_LIBRARY CYRUS_SASL_INCLUDE_DIR) ++#find_library(CYRUS_SASL_LIBRARY sasl2) ++#find_path(CYRUS_SASL_INCLUDE_DIR sasl/sasl.h PATH_SUFFIXES include) ++#find_package_handle_standard_args(CyrusSASL DEFAULT_MSG CYRUS_SASL_LIBRARY CYRUS_SASL_INCLUDE_DIR) + mark_as_advanced(CYRUS_SASL_LIBRARY CYRUS_SASL_INCLUDE_DIR) + + # Find saslpasswd2 executable to generate test config +-find_program(SASLPASSWD_EXE saslpasswd2 DOC "Program used to make SASL user db for testing") ++#find_program(SASLPASSWD_EXE saslpasswd2 DOC "Program used to make SASL user db for testing") + mark_as_advanced(SASLPASSWD_EXE) + + if(WIN32 AND NOT CYGWIN) +@@ -315,8 +315,8 @@ pn_absolute_install_dir(EXEC_PREFIX "." ${CMAKE_INSTALL_PREFIX}) + pn_absolute_install_dir(LIBDIR ${LIB_INSTALL_DIR} ${CMAKE_INSTALL_PREFIX}) + pn_absolute_install_dir(INCLUDEDIR ${INCLUDE_INSTALL_DIR} ${CMAKE_INSTALL_PREFIX}) + +-add_subdirectory(docs/api) +-add_subdirectory(../tests/tools/apps/c ../tests/tools/apps/c) ++#add_subdirectory(docs/api) ++#add_subdirectory(../tests/tools/apps/c ../tests/tools/apps/c) + + # for full source distribution: + set (qpid-proton-platform-all +@@ -507,7 +507,7 @@ if (BUILD_WITH_CXX) + endif (BUILD_WITH_CXX) + + add_library ( +- qpid-proton-core SHARED ++ qpid-proton-core STATIC + ${qpid-proton-core} + ${qpid-proton-layers} + ${qpid-proton-platform} +@@ -527,7 +527,7 @@ set_target_properties ( + ) + + add_library( +- qpid-proton SHARED ++ qpid-proton STATIC + # Proton Core + ${qpid-proton-core} + ${qpid-proton-layers} +@@ -629,7 +629,7 @@ install (FILES + + # c tests: + +-add_subdirectory(src/tests) ++#add_subdirectory(src/tests) + + if (CMAKE_SYSTEM_NAME STREQUAL Windows) + # No change needed for windows already use correct separator +@@ -712,7 +712,7 @@ if (BUILD_PYTHON) + + endif (BUILD_PYTHON) + +-find_program(RUBY_EXE "ruby") ++#find_program(RUBY_EXE "ruby") + if (RUBY_EXE AND BUILD_RUBY) + set (rb_root "${pn_test_root}/ruby") + set (rb_src "${CMAKE_CURRENT_SOURCE_DIR}/bindings/ruby") +@@ -751,8 +751,8 @@ if (RUBY_EXE AND BUILD_RUBY) + else (DEFAULT_RUBY_TESTING) + message(STATUS "Skipping Ruby tests: missing dependencies") + endif (DEFAULT_RUBY_TESTING) +-else (RUBY_EXE) +- message (STATUS "Cannot find ruby, skipping ruby tests") ++#else (RUBY_EXE) ++# message (STATUS "Cannot find ruby, skipping ruby tests") + endif() + + mark_as_advanced (RUBY_EXE RSPEC_EXE) +diff --git a/proton-c/bindings/CMakeLists.txt b/proton-c/bindings/CMakeLists.txt +index 6b88384..d1a50a5 100644 +--- a/proton-c/bindings/CMakeLists.txt ++++ b/proton-c/bindings/CMakeLists.txt +@@ -19,14 +19,14 @@ + + # Add bindings that do not require swig here - the directory name must be the same as the binding name + # See below for swig bindings +-set(BINDINGS javascript cpp go) ++set(BINDINGS cpp) + + # Prerequisites for javascript. + # + # It uses a C/C++ to JavaScript cross-compiler called emscripten (https://github.com/kripken/emscripten). Emscripten takes C/C++ + # and compiles it into a highly optimisable subset of JavaScript called asm.js (http://asmjs.org/) that can be + # aggressively optimised and run at near-native speed (usually between 1.5 to 10 times slower than native C/C++). +-find_package(Emscripten) ++#find_package(Emscripten) + if (EMSCRIPTEN_FOUND) + set (DEFAULT_JAVASCRIPT ON) + endif (EMSCRIPTEN_FOUND) +@@ -37,7 +37,7 @@ if (CMAKE_CXX_COMPILER) + endif (CMAKE_CXX_COMPILER) + + # Prerequisites for Go +-find_program(GO_EXE go) ++#find_program(GO_EXE go) + mark_as_advanced(GO_EXE) + if (GO_EXE) + if(WIN32) +diff --git a/proton-c/bindings/cpp/CMakeLists.txt b/proton-c/bindings/cpp/CMakeLists.txt +index 0cc4024..796fe29 100644 +--- a/proton-c/bindings/cpp/CMakeLists.txt ++++ b/proton-c/bindings/cpp/CMakeLists.txt +@@ -16,7 +16,7 @@ + # specific language governing permissions and limitations + # under the License. + # +- ++set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + include(cpp.cmake) # Compiler checks + + include_directories( +@@ -89,7 +89,7 @@ set_source_files_properties ( + COMPILE_FLAGS "${LTO}" + ) + +-add_library(qpid-proton-cpp SHARED ${qpid-proton-cpp-source}) ++add_library(qpid-proton-cpp STATIC ${qpid-proton-cpp-source}) + + target_link_libraries (qpid-proton-cpp ${PLATFORM_LIBS} qpid-proton) + +@@ -120,8 +120,8 @@ endif (MSVC) + + install (DIRECTORY "include/proton" DESTINATION ${INCLUDE_INSTALL_DIR} FILES_MATCHING PATTERN "*.hpp") + +-add_subdirectory(docs) +-add_subdirectory(${CMAKE_SOURCE_DIR}/tests/tools/apps/cpp ${CMAKE_BINARY_DIR}/tests/tools/apps/cpp) ++#add_subdirectory(docs) ++#add_subdirectory(${CMAKE_SOURCE_DIR}/tests/tools/apps/cpp ${CMAKE_BINARY_DIR}/tests/tools/apps/cpp) + + # Pkg config file + configure_file( +@@ -171,12 +171,12 @@ macro(add_cpp_test test) + endif () + endmacro(add_cpp_test) + +-add_cpp_test(codec_test) ++#add_cpp_test(codec_test) + #add_cpp_test(engine_test) +-add_cpp_test(thread_safe_test) +-add_cpp_test(interop_test ${CMAKE_SOURCE_DIR}/tests) +-add_cpp_test(message_test) +-add_cpp_test(scalar_test) +-add_cpp_test(value_test) +-add_cpp_test(container_test) +-add_cpp_test(url_test) ++#add_cpp_test(thread_safe_test) ++#add_cpp_test(interop_test ${CMAKE_SOURCE_DIR}/tests) ++#add_cpp_test(message_test) ++#add_cpp_test(scalar_test) ++#add_cpp_test(value_test) ++#add_cpp_test(container_test) ++#add_cpp_test(url_test) +diff --git a/proton-c/bindings/cpp/docs/CMakeLists.txt b/proton-c/bindings/cpp/docs/CMakeLists.txt +index d512d15..8576867 100644 +--- a/proton-c/bindings/cpp/docs/CMakeLists.txt ++++ b/proton-c/bindings/cpp/docs/CMakeLists.txt +@@ -17,7 +17,7 @@ + # under the License. + # + +-find_package(Doxygen) ++#find_package(Doxygen) + + if (DOXYGEN_FOUND) + configure_file ( +diff --git a/proton-c/docs/api/CMakeLists.txt b/proton-c/docs/api/CMakeLists.txt +index 7756e48..71ebb93 100644 +--- a/proton-c/docs/api/CMakeLists.txt ++++ b/proton-c/docs/api/CMakeLists.txt +@@ -17,7 +17,7 @@ + # under the License. + # + +-find_package(Doxygen) ++#find_package(Doxygen) + if (DOXYGEN_FOUND) + configure_file (${CMAKE_CURRENT_SOURCE_DIR}/user.doxygen.in + ${CMAKE_CURRENT_BINARY_DIR}/user.doxygen) +-- +2.7.4 + diff --git a/doc/amqp.md b/doc/amqp.md new file mode 100644 index 000000000..f733fa514 --- /dev/null +++ b/doc/amqp.md @@ -0,0 +1,123 @@ +# 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 Zcash daemon can be configured to act as a trusted "border +router", implementing the Zcash 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 Zcash 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. + +## 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 zcashd: + + $ ./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 zcashd like this: + + $ zcashd -amqppubhashtx=amqp://127.0.0.1:5672 + +Or this: + + $ zcashd -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 zcash.conf. + +Please see `contrib/amqp/amqp_sub.py` for a working example of an +AMQP server listening for messages. + +## Remarks + +From the perspective of zcashd, 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 zcashd 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 Zcash +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, zcashd 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, zcashd appends an up-counting sequence number to each notification +which allows listeners to detect lost notifications. + diff --git a/doc/authors.md b/doc/authors.md index f9fc63367..8f249b1c0 100644 --- a/doc/authors.md +++ b/doc/authors.md @@ -1,68 +1,93 @@ Zcash Contributors ================== -Jack Grigg (392) -Simon Liu (254) -Sean Bowe (185) -Daira Hopwood (80) +Jack Grigg (601) +Simon Liu (297) +Sean Bowe (193) +Daira Hopwood (102) +Wladimir J. van der Laan (71) Taylor Hornby (65) -Wladimir J. van der Laan (58) -Jonas Schnelli (48) -Jay Graber (47) +Jay Graber (61) +Nathan Wilcox (56) +Jonas Schnelli (49) Kevin Gallagher (38) -Cory Fields (15) -Pieter Wuille (14) -Nathan Wilcox (10) +Cory Fields (30) +Pieter Wuille (24) +syd (15) nomnombtc (9) -fanquake (5) -Paige Peterson (5) -MarcoFalke (5) +Paige Peterson (9) +Matt Corallo (9) +fanquake (8) +MarcoFalke (7) +Luke Dashjr (6) Johnathan Corgan (5) Gregory Maxwell (5) +Ariel Gabizon (5) +kozyilmaz (4) Philip Kaufmann (4) Peter Todd (4) Patrick Strateman (4) -Matt Corallo (4) Karl-Johan Alm (4) +Jorge Timón (4) Jeff Garzik (4) David Mercer (4) Daniel Cousens (4) lpescher (3) +Suhas Daftuar (3) Pavel Janík (3) +João Barbosa (3) +Ariel (3) Alfie John (3) +str4d (2) paveljanik (2) +kpcyrd (2) aniemerg (2) Scott (2) Robert C. Seacord (2) -Luke Dashjr (2) -João Barbosa (2) +Per Grön (2) +Pavel Vasin (2) Joe Turgeon (2) +Jason Davies (2) Jack Gavigan (2) ITH4Coinomia (2) Gavin Andresen (2) +Daniel Kraft (2) +Bjorn Hjortsberg (2) +Amgad Abdelhafez (2) zathras-crypto (1) unsystemizer (1) +practicalswift (1) mruddy (1) mrbandrews (1) kazcw (1) +jc (1) isle2983 (1) instagibbs (1) +emilrus (1) dexX7 (1) +daniel (1) calebogden (1) ayleph (1) Tom Ritter (1) Stephen (1) S. Matthew English (1) -Pavel Vasin (1) +Ross Nicoll (1) +René Nyffenegger (1) Paul Georgiou (1) Paragon Initiative Enterprises, LLC (1) +Nicolas DORIER (1) Nathaniel Mahieu (1) Murilo Santana (1) +Maxwell Gubler (1) Matt Quinn (1) +Mark Friedenbach (1) +Marius Kjærstad (1) Louis Nyffenegger (1) Leo Arias (1) Lars-Magnus Skog (1) -Jorge Timón (1) +Kevin Pan (1) +Jonathan "Duke" Leto (1) +Jonas Nick (1) Jeffrey Walton (1) Ian Kelling (1) Gaurav Rana (1) @@ -70,17 +95,23 @@ Forrest Voight (1) Florian Schmaus (1) Ethan Heilman (1) Eran Tromer (1) -Daniel Kraft (1) +Duke Leto (1) Christian von Roques (1) Chirag Davé (1) Casey Rodarmor (1) Cameron Boehmer (1) Bryan Stitt (1) +Bruno Arueira (1) +Boris Hajduk (1) Bob McElrath (1) Bitcoin Error Log (1) +Ashley Holman (1) +Anthony Towns (1) Allan Niemerg (1) Alex van der Peet (1) +Alex Morcos (1) Alex (1) Adam Weiss (1) Adam Brown (1) 4ZEC (1) +21E14 (1) diff --git a/doc/files.md b/doc/files.md index 2d3787912..6a60a61ee 100644 --- a/doc/files.md +++ b/doc/files.md @@ -1,12 +1,12 @@ -* zcash.conf: contains configuration settings for zcashd -* zcashd.pid: stores the process id of zcashd while running +* komodo.conf: contains configuration settings for komodod +* komodod.pid: stores the process id of komodod while running * blocks/blk000??.dat: block data (custom, 128 MiB per file) * blocks/rev000??.dat; block undo data (custom) * blocks/index/*; block index (LevelDB) * chainstate/*; block chain state database (LevelDB) * database/*: BDB database environment * db.log: wallet database log file -* debug.log: contains debug information and general logging generated by zcashd +* debug.log: contains debug information and general logging generated by komodod * fee_estimates.dat: stores statistics used to estimate minimum transaction fees and priorities required for confirmation * peers.dat: peer IP address database (custom format) * wallet.dat: personal wallet (BDB) with keys and transactions diff --git a/doc/hotfix-process.md b/doc/hotfix-process.md new file mode 100644 index 000000000..e6ce8a7a2 --- /dev/null +++ b/doc/hotfix-process.md @@ -0,0 +1,74 @@ +Hotfix Release Process +====================== + +Hotfix releases are versioned by incrementing the build number of the latest +release. For example: + + First hotfix: v1.0.11 -> v1.0.11-1 + Second hotfix: v1.0.11-1 -> v1.0.11-2 + +In the commands below, and are prefixed with a v, ie. +v1.0.11 (not 1.0.11). + +## Create a hotfix branch + +Create a hotfix branch from the previous release tag, and push it to the main +repository: + + $ git branch hotfix- + $ git push 'git@github.com:zcash/zcash' hotfix- + +## Implement hotfix changes + +Hotfix changes are implemented the same way as regular changes (developers work +in separate branches per change, and push the branches to their own repositories), +except that the branches are based on the hotfix branch instead of master: + + $ git checkout hotfix- + $ git checkout -b + +## Merge hotfix PRs + +Hotfix PRs are created like regular PRs, except using the hotfix branch as the +base instead of master. Each PR should be reviewed as normal, and then the +following process should be used to merge: + +- A CI merge build is manually run by logging into the CI server, going to the + pr-merge builder, clicking the "force" button, and entering the following + values: + + - Repository: https://github.com//zcash + - must be in the set of "safe" users as-specified in the CI + config. + - Branch: name of the hotfix PR branch (not the hotfix release branch). + +- A link to the build and its result is manually added to the PR as a comment. + +- If the build was successful, the PR is merged via the GitHub button. + +## Release process + +The majority of this process is identical to the standard release process. +However, there are a few notable differences: + +- When running the release script, use the `--hotfix` flag: + + $ ./zcutil/make-release.py --hotfix + +- To review the automated changes in git: + + $ git log hotfix-..HEAD + +- After the standard review process, use the hotfix merge process outlined above + instead of the regular merge process. + +- When making the tag, check out the hotfix branch instead of master. + +## Post-release + +Once the hotfix release has been created, a new PR should be opened for merging +the hotfix release branch into master. This may require fixing merge conflicts +(e.g. changing the version number in the hotfix branch to match master, if +master is ahead). Such conflicts **MUST** be addressed with additional commits +to the hotfix branch; specifically, the branch **MUST NOT** be rebased on +master. diff --git a/doc/man/komodo-cli.1 b/doc/man/komodo-cli.1 new file mode 100644 index 000000000..b7fdd19d6 --- /dev/null +++ b/doc/man/komodo-cli.1 @@ -0,0 +1,84 @@ +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.3. +.TH KOMODO-CLI "1" "March 2018" "komodo-cli v1.0.8" "User Commands" +.SH NAME +komodo-cli \- manual page for komodo-cli v1.0.8 +.SH DESCRIPTION +Komodo RPC client version v1.0.8 +.PP +In order to ensure you are adequately protecting your privacy when using Zcash, +please see . +.SS "Usage:" +.TP +komodo\-cli [options] [params] +Send command to Komodo +.TP +komodo\-cli [options] help +List commands +.TP +komodo\-cli [options] help +Get help for a command +.SH OPTIONS +.HP +\-? +.IP +This help message +.HP +\fB\-conf=\fR +.IP +Specify configuration file (default: komodo.conf) +.HP +\fB\-datadir=\fR +.IP +Specify data directory +.HP +\fB\-testnet\fR +.IP +Use the test network +.HP +\fB\-regtest\fR +.IP +Enter regression test mode, which uses a special chain in which blocks +can be solved instantly. This is intended for regression testing tools +and app development. +.HP +\fB\-rpcconnect=\fR +.IP +Send commands to node running on (default: 127.0.0.1) +.HP +\fB\-rpcport=\fR +.IP +Connect to JSON\-RPC on (default: 8232 or testnet: 18232) +.HP +\fB\-rpcwait\fR +.IP +Wait for RPC server to start +.HP +\fB\-rpcuser=\fR +.IP +Username for JSON\-RPC connections +.HP +\fB\-rpcpassword=\fR +.IP +Password for JSON\-RPC connections +.HP +\fB\-rpcclienttimeout=\fR +.IP +Timeout in seconds during HTTP requests, or 0 for no timeout. (default: +900) +.SH COPYRIGHT + +In order to ensure you are adequately protecting your privacy when using Zcash, +please see . + +Copyright (C) 2009-2017 The Bitcoin Core Developers +Copyright (C) 2015-2017 The Zcash Developers +Copyright (C) 2015-2017 jl777 and SuperNET developers + +This is experimental software. + +Distributed under the MIT software license, see the accompanying file COPYING +or . + +This product includes software developed by the OpenSSL Project for use in the +OpenSSL Toolkit and cryptographic software written +by Eric Young and UPnP software written by Thomas Bernard. diff --git a/doc/man/komodo-tx.1 b/doc/man/komodo-tx.1 new file mode 100644 index 000000000..16f4e9a8d --- /dev/null +++ b/doc/man/komodo-tx.1 @@ -0,0 +1,102 @@ +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.3. +.TH KOMODO-TX "1" "March 2018" "komodo-tx v1.0.8" "User Commands" +.SH NAME +komodo-tx \- manual page for komodo-tx v1.0.8 +.SH DESCRIPTION +Zcash zcash\-tx utility version v1.0.8 +.SS "Usage:" +.TP +zcash\-tx [options] [commands] +Update hex\-encoded zcash transaction +.TP +zcash\-tx [options] \fB\-create\fR [commands] +Create hex\-encoded zcash transaction +.SH OPTIONS +.HP +\-? +.IP +This help message +.HP +\fB\-create\fR +.IP +Create new, empty TX. +.HP +\fB\-json\fR +.IP +Select JSON output +.HP +\fB\-txid\fR +.IP +Output only the hex\-encoded transaction id of the resultant transaction. +.HP +\fB\-regtest\fR +.IP +Enter regression test mode, which uses a special chain in which blocks +can be solved instantly. +.HP +\fB\-testnet\fR +.IP +Use the test network +.PP +Commands: +.IP +delin=N +.IP +Delete input N from TX +.IP +delout=N +.IP +Delete output N from TX +.IP +in=TXID:VOUT +.IP +Add input to TX +.IP +locktime=N +.IP +Set TX lock time to N +.IP +nversion=N +.IP +Set TX version to N +.IP +outaddr=VALUE:ADDRESS +.IP +Add address\-based output to TX +.IP +outscript=VALUE:SCRIPT +.IP +Add raw script output to TX +.IP +sign=SIGHASH\-FLAGS +.IP +Add zero or more signatures to transaction. This command requires JSON +registers:prevtxs=JSON object, privatekeys=JSON object. See +signrawtransaction docs for format of sighash flags, JSON objects. +.PP +Register Commands: +.IP +load=NAME:FILENAME +.IP +Load JSON file FILENAME into register NAME +.IP +set=NAME:JSON\-STRING +.IP +Set register NAME to given JSON\-STRING +.SH COPYRIGHT + +In order to ensure you are adequately protecting your privacy when using Zcash, +please see . + +Copyright (C) 2009-2017 The Bitcoin Core Developers +Copyright (C) 2015-2017 The Zcash Developers +Copyright (C) 2015-2017 jl777 and SuperNET developers + +This is experimental software. + +Distributed under the MIT software license, see the accompanying file COPYING +or . + +This product includes software developed by the OpenSSL Project for use in the +OpenSSL Toolkit and cryptographic software written +by Eric Young and UPnP software written by Thomas Bernard. diff --git a/doc/man/komodod.1 b/doc/man/komodod.1 new file mode 100644 index 000000000..4c728657d --- /dev/null +++ b/doc/man/komodod.1 @@ -0,0 +1,472 @@ +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.3. +.TH KOMODOD "1" "March 2018" "komodod v1.0.8" "User Commands" +.SH NAME +komodod \- manual page for komodod v1.0.8 +.SH DESCRIPTION +Komodo Daemon version v1.0.8 +.PP +In order to ensure you are adequately protecting your privacy when using Zcash, +please see . +.SS "Usage:" +.TP +komodod [options] +Start Komodo Daemon +.SH OPTIONS +.HP +\-? +.IP +This help message +.HP +\fB\-alerts\fR +.IP +Receive and display P2P network alerts (default: 1) +.HP +\fB\-alertnotify=\fR +.IP +Execute command when a relevant alert is received or we see a really +long fork (%s in cmd is replaced by message) +.HP +\fB\-blocknotify=\fR +.IP +Execute command when the best block changes (%s in cmd is replaced by +block hash) +.HP +\fB\-checkblocks=\fR +.IP +How many blocks to check at startup (default: 288, 0 = all) +.HP +\fB\-checklevel=\fR +.IP +How thorough the block verification of \fB\-checkblocks\fR is (0\-4, default: 3) +.HP +\fB\-conf=\fR +.IP +Specify configuration file (default: komodo.conf) +.HP +\fB\-daemon\fR +.IP +Run in the background as a daemon and accept commands +.HP +\fB\-datadir=\fR +.IP +Specify data directory +.HP +\fB\-exportdir=\fR +.IP +Specify directory to be used when exporting data +.HP +\fB\-dbcache=\fR +.IP +Set database cache size in megabytes (4 to 16384, default: 100) +.HP +\fB\-loadblock=\fR +.IP +Imports blocks from external blk000??.dat file on startup +.HP +\fB\-maxorphantx=\fR +.IP +Keep at most unconnectable transactions in memory (default: 100) +.HP +\fB\-par=\fR +.IP +Set the number of script verification threads (\fB\-2\fR to 16, 0 = auto, <0 = +leave that many cores free, default: 0) +.HP +\fB\-pid=\fR +.IP +Specify pid file (default: komodod.pid) +.HP +\fB\-prune=\fR +.IP +Reduce storage requirements by pruning (deleting) old blocks. This mode +disables wallet support and is incompatible with \fB\-txindex\fR. Warning: +Reverting this setting requires re\-downloading the entire blockchain. +(default: 0 = disable pruning blocks, >550 = target size in MiB to use +for block files) +.HP +\fB\-reindex\fR +.IP +Rebuild block chain index from current blk000??.dat files on startup +.HP +\fB\-sysperms\fR +.IP +Create new files with system default permissions, instead of umask 077 +(only effective with disabled wallet functionality) +.HP +\fB\-txindex\fR +.IP +Maintain a full transaction index, used by the getrawtransaction rpc +call (default: 0) +.PP +Connection options: +.HP +\fB\-addnode=\fR +.IP +Add a node to connect to and attempt to keep the connection open +.HP +\fB\-banscore=\fR +.IP +Threshold for disconnecting misbehaving peers (default: 100) +.HP +\fB\-bantime=\fR +.IP +Number of seconds to keep misbehaving peers from reconnecting (default: +86400) +.HP +\fB\-bind=\fR +.IP +Bind to given address and always listen on it. Use [host]:port notation +for IPv6 +.HP +\fB\-connect=\fR +.IP +Connect only to the specified node(s) +.HP +\fB\-discover\fR +.IP +Discover own IP addresses (default: 1 when listening and no \fB\-externalip\fR +or \fB\-proxy\fR) +.HP +\fB\-dns\fR +.IP +Allow DNS lookups for \fB\-addnode\fR, \fB\-seednode\fR and \fB\-connect\fR (default: 1) +.HP +\fB\-dnsseed\fR +.IP +Query for peer addresses via DNS lookup, if low on addresses (default: 1 +unless \fB\-connect\fR) +.HP +\fB\-externalip=\fR +.IP +Specify your own public address +.HP +\fB\-forcednsseed\fR +.IP +Always query for peer addresses via DNS lookup (default: 0) +.HP +\fB\-listen\fR +.IP +Accept connections from outside (default: 1 if no \fB\-proxy\fR or \fB\-connect\fR) +.HP +\fB\-listenonion\fR +.IP +Automatically create Tor hidden service (default: 1) +.HP +\fB\-maxconnections=\fR +.IP +Maintain at most connections to peers (default: 125) +.HP +\fB\-maxreceivebuffer=\fR +.IP +Maximum per\-connection receive buffer, *1000 bytes (default: 5000) +.HP +\fB\-maxsendbuffer=\fR +.IP +Maximum per\-connection send buffer, *1000 bytes (default: 1000) +.HP +\fB\-onion=\fR +.IP +Use separate SOCKS5 proxy to reach peers via Tor hidden services +(default: \fB\-proxy\fR) +.HP +\fB\-onlynet=\fR +.IP +Only connect to nodes in network (ipv4, ipv6 or onion) +.HP +\fB\-permitbaremultisig\fR +.IP +Relay non\-P2SH multisig (default: 1) +.HP +\fB\-port=\fR +.IP +Listen for connections on (default: 7770 or testnet: 17770) +.HP +\fB\-proxy=\fR +.IP +Connect through SOCKS5 proxy +.HP +\fB\-proxyrandomize\fR +.IP +Randomize credentials for every proxy connection. This enables Tor +stream isolation (default: 1) +.HP +\fB\-seednode=\fR +.IP +Connect to a node to retrieve peer addresses, and disconnect +.HP +\fB\-timeout=\fR +.IP +Specify connection timeout in milliseconds (minimum: 1, default: 5000) +.HP +\fB\-torcontrol=\fR: +.IP +Tor control port to use if onion listening enabled (default: +127.0.0.1:9051) +.HP +\fB\-torpassword=\fR +.IP +Tor control port password (default: empty) +.HP +\fB\-upnp\fR +.IP +Use UPnP to map the listening port (default: 0) +.HP +\fB\-whitebind=\fR +.IP +Bind to given address and whitelist peers connecting to it. Use +[host]:port notation for IPv6 +.HP +\fB\-whitelist=\fR +.IP +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 +.PP +Wallet options: +.HP +\fB\-disablewallet\fR +.IP +Do not load the wallet and disable wallet RPC calls +.HP +\fB\-keypool=\fR +.IP +Set key pool size to (default: 100) +.HP +\fB\-paytxfee=\fR +.IP +Fee (in BTC/kB) to add to transactions you send (default: 0.00) +.HP +\fB\-rescan\fR +.IP +Rescan the blockchain for missing wallet transactions on startup +.HP +\fB\-salvagewallet\fR +.IP +Attempt to recover private keys from a corrupt wallet.dat on startup +.HP +\fB\-sendfreetransactions\fR +.IP +Send transactions as zero\-fee transactions if possible (default: 0) +.HP +\fB\-spendzeroconfchange\fR +.IP +Spend unconfirmed change when sending transactions (default: 1) +.HP +\fB\-txconfirmtarget=\fR +.IP +If paytxfee is not set, include enough fee so transactions begin +confirmation on average within n blocks (default: 2) +.HP +\fB\-maxtxfee=\fR +.IP +Maximum total fees to use in a single wallet transaction; setting this +too low may abort large transactions (default: 0.10) +.HP +\fB\-upgradewallet\fR +.IP +Upgrade wallet to latest format on startup +.HP +\fB\-wallet=\fR +.IP +Specify wallet file (within data directory) (default: wallet.dat) +.HP +\fB\-walletbroadcast\fR +.IP +Make the wallet broadcast transactions (default: 1) +.HP +\fB\-walletnotify=\fR +.IP +Execute command when a wallet transaction changes (%s in cmd is replaced +by TxID) +.HP +\fB\-zapwallettxes=\fR +.IP +Delete all wallet transactions and only recover those parts of the +blockchain through \fB\-rescan\fR on startup (1 = keep tx meta data e.g. +account owner and payment request information, 2 = drop tx meta data) +.PP +ZeroMQ notification options: +.HP +\fB\-zmqpubhashblock=\fR
+.IP +Enable publish hash block in
+.HP +\fB\-zmqpubhashtx=\fR
+.IP +Enable publish hash transaction in
+.HP +\fB\-zmqpubrawblock=\fR
+.IP +Enable publish raw block in
+.HP +\fB\-zmqpubrawtx=\fR
+.IP +Enable publish raw transaction in
+.PP +Debugging/Testing options: +.HP +\fB\-debug=\fR +.IP +Output debugging information (default: 0, supplying is +optional). If is not supplied or if = 1, output +all debugging information. can be: addrman, alert, bench, +coindb, db, estimatefee, http, libevent, lock, mempool, net, +partitioncheck, pow, proxy, prune, rand, reindex, rpc, selectcoins, tor, +zmq, zrpc, zrpcunsafe (implies zrpc). +.HP +\fB\-experimentalfeatures\fR +.IP +Enable use of experimental features +.HP +\fB\-help\-debug\fR +.IP +Show all debugging options (usage: \fB\-\-help\fR \fB\-help\-debug\fR) +.HP +\fB\-logips\fR +.IP +Include IP addresses in debug output (default: 0) +.HP +\fB\-logtimestamps\fR +.IP +Prepend debug output with timestamp (default: 1) +.HP +\fB\-minrelaytxfee=\fR +.IP +Fees (in BTC/Kb) smaller than this are considered zero fee for relaying +(default: 0.000001) +.HP +\fB\-printtoconsole\fR +.IP +Send trace/debug info to console instead of debug.log file +.HP +\fB\-shrinkdebugfile\fR +.IP +Shrink debug.log file on client startup (default: 1 when no \fB\-debug\fR) +.HP +\fB\-testnet\fR +.IP +Use the test network +.PP +Node relay options: +.HP +\fB\-datacarrier\fR +.IP +Relay and mine data carrier transactions (default: 1) +.HP +\fB\-datacarriersize\fR +.IP +Maximum size of data in data carrier transactions we relay and mine +(default: 8192) +.PP +Block creation options: +.HP +\fB\-blockminsize=\fR +.IP +Set minimum block size in bytes (default: 0) +.HP +\fB\-blockmaxsize=\fR +.IP +Set maximum block size in bytes (default: 2000000) +.HP +\fB\-blockprioritysize=\fR +.IP +Set maximum size of high\-priority/low\-fee transactions in bytes +(default: 1000000) +.PP +Mining options: +.HP +\fB\-gen\fR +.IP +Generate coins (default: 0) +.HP +\fB\-genproclimit=\fR +.IP +Set the number of threads for coin generation if enabled (\fB\-1\fR = all +cores, default: 1) +.HP +\fB\-equihashsolver=\fR +.IP +Specify the Equihash solver to be used if enabled (default: "default") +.HP +\fB\-mineraddress=\fR +.IP +Send mined coins to a specific single address +.HP +\fB\-minetolocalwallet\fR +.IP +Require that mined blocks use a coinbase address in the local wallet +(default: 1) +.PP +RPC server options: +.HP +\fB\-server\fR +.IP +Accept command line and JSON\-RPC commands +.HP +\fB\-rest\fR +.IP +Accept public REST requests (default: 0) +.HP +\fB\-rpcbind=\fR +.IP +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) +.HP +\fB\-rpcuser=\fR +.IP +Username for JSON\-RPC connections +.HP +\fB\-rpcpassword=\fR +.IP +Password for JSON\-RPC connections +.HP +\fB\-rpcport=\fR +.IP +Listen for JSON\-RPC connections on (default: 7771 or testnet: +17771) +.HP +\fB\-rpcallowip=\fR +.IP +Allow JSON\-RPC connections from specified source. Valid for 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). This option can be specified +multiple times +.HP +\fB\-rpcthreads=\fR +.IP +Set the number of threads to service RPC calls (default: 4) +.PP +Metrics Options (only if \fB\-daemon\fR and \fB\-printtoconsole\fR are not set): +.HP +\fB\-showmetrics\fR +.IP +Show metrics on stdout (default: 1 if running in a console, 0 otherwise) +.HP +\fB\-metricsui\fR +.IP +Set to 1 for a persistent metrics screen, 0 for sequential metrics +output (default: 1 if running in a console, 0 otherwise) +.HP +\fB\-metricsrefreshtime\fR +.IP +Number of seconds between metrics refreshes (default: 1 if running in a +console, 600 otherwise) +.SH COPYRIGHT + +In order to ensure you are adequately protecting your privacy when using Zcash, +please see . + +Copyright (C) 2009-2017 The Bitcoin Core Developers +Copyright (C) 2015-2017 The Zcash Developers +Copyright (C) 2015-2017 jl777 and SuperNET developers + +This is experimental software. + +Distributed under the MIT software license, see the accompanying file COPYING +or . + +This product includes software developed by the OpenSSL Project for use in the +OpenSSL Toolkit and cryptographic software written +by Eric Young and UPnP software written by Thomas Bernard. diff --git a/doc/man/zcash-cli.1 b/doc/man/zcash-cli.1 index 9dbefb9e8..a30af6fe6 100644 --- a/doc/man/zcash-cli.1 +++ b/doc/man/zcash-cli.1 @@ -1,12 +1,12 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH ZCASH-CLI "1" "March 2017" "zcash-cli v1.0.8" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.3. +.TH ZCASH-CLI "1" "February 2018" "zcash-cli v1.0.15" "User Commands" .SH NAME -zcash-cli \- manual page for zcash-cli v1.0.8 +zcash-cli \- manual page for zcash-cli v1.0.15 .SH DESCRIPTION -Zcash RPC client version v1.0.8 +Zcash RPC client version v1.0.15 .PP In order to ensure you are adequately protecting your privacy when using Zcash, -please see . +please see . .SS "Usage:" .TP zcash\-cli [options] [params] @@ -66,6 +66,10 @@ Password for JSON\-RPC connections Timeout in seconds during HTTP requests, or 0 for no timeout. (default: 900) .SH COPYRIGHT + +In order to ensure you are adequately protecting your privacy when using Zcash, +please see . + Copyright (C) 2009-2017 The Bitcoin Core Developers Copyright (C) 2015-2017 The Zcash Developers @@ -76,4 +80,4 @@ or . This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit and cryptographic software written -by Eric Young and UPnP software written by Thomas Bernard. +by Eric Young. diff --git a/doc/man/zcash-tx.1 b/doc/man/zcash-tx.1 index 4bd1b6b1d..f5830e6de 100644 --- a/doc/man/zcash-tx.1 +++ b/doc/man/zcash-tx.1 @@ -1,9 +1,9 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH ZCASH-TX "1" "March 2017" "zcash-tx v1.0.8" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.3. +.TH ZCASH-TX "1" "February 2018" "zcash-tx v1.0.15" "User Commands" .SH NAME -zcash-tx \- manual page for zcash-tx v1.0.8 +zcash-tx \- manual page for zcash-tx v1.0.15 .SH DESCRIPTION -Zcash zcash\-tx utility version v1.0.8 +Zcash zcash\-tx utility version v1.0.15 .SS "Usage:" .TP zcash\-tx [options] [commands] @@ -68,7 +68,7 @@ outscript=VALUE:SCRIPT .IP Add raw script output to TX .IP -sign=SIGHASH\-FLAGS +sign=HEIGHT:SIGHASH\-FLAGS .IP Add zero or more signatures to transaction. This command requires JSON registers:prevtxs=JSON object, privatekeys=JSON object. See @@ -84,6 +84,10 @@ set=NAME:JSON\-STRING .IP Set register NAME to given JSON\-STRING .SH COPYRIGHT + +In order to ensure you are adequately protecting your privacy when using Zcash, +please see . + Copyright (C) 2009-2017 The Bitcoin Core Developers Copyright (C) 2015-2017 The Zcash Developers @@ -94,4 +98,4 @@ or . This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit and cryptographic software written -by Eric Young and UPnP software written by Thomas Bernard. +by Eric Young. diff --git a/doc/man/zcashd.1 b/doc/man/zcashd.1 index 3fbfe32e6..eaef634c0 100644 --- a/doc/man/zcashd.1 +++ b/doc/man/zcashd.1 @@ -1,12 +1,12 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH ZCASHD "1" "March 2017" "zcashd v1.0.8" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.3. +.TH ZCASHD "1" "February 2018" "zcashd v1.0.15" "User Commands" .SH NAME -zcashd \- manual page for zcashd v1.0.8 +zcashd \- manual page for zcashd v1.0.15 .SH DESCRIPTION -Zcash Daemon version v1.0.8 +Zcash Daemon version v1.0.15 .PP In order to ensure you are adequately protecting your privacy when using Zcash, -please see . +please see . .SS "Usage:" .TP zcashd [options] @@ -51,13 +51,18 @@ Run in the background as a daemon and accept commands .IP Specify data directory .HP +\fB\-disabledeprecation=\fR +.IP +Disable block\-height node deprecation and automatic shutdown (example: +\fB\-disabledeprecation\fR=\fI\,1\/\fR.0.15) +.HP \fB\-exportdir=\fR .IP Specify directory to be used when exporting data .HP \fB\-dbcache=\fR .IP -Set database cache size in megabytes (4 to 16384, default: 100) +Set database cache size in megabytes (4 to 16384, default: 450) .HP \fB\-loadblock=\fR .IP @@ -67,9 +72,14 @@ Imports blocks from external blk000??.dat file on startup .IP Keep at most unconnectable transactions in memory (default: 100) .HP +\fB\-mempooltxinputlimit=\fR +.IP +Set the maximum number of transparent inputs in a transaction that the +mempool will accept (default: 0 = no limit applied) +.HP \fB\-par=\fR .IP -Set the number of script verification threads (\fB\-4\fR to 16, 0 = auto, <0 = +Set the number of script verification threads (\fB\-2\fR to 16, 0 = auto, <0 = leave that many cores free, default: 0) .HP \fB\-pid=\fR @@ -207,10 +217,6 @@ Tor control port to use if onion listening enabled (default: .IP Tor control port password (default: empty) .HP -\fB\-upnp\fR -.IP -Use UPnP to map the listening port (default: 0) -.HP \fB\-whitebind=\fR .IP Bind to given address and whitelist peers connecting to it. Use @@ -235,11 +241,11 @@ Set key pool size to (default: 100) .HP \fB\-paytxfee=\fR .IP -Fee (in BTC/kB) to add to transactions you send (default: 0.00) +Fee (in ZEC/kB) to add to transactions you send (default: 0.00) .HP \fB\-rescan\fR .IP -Rescan the blockchain for missing wallet transactions on startup +Rescan the block chain for missing wallet transactions on startup .HP \fB\-salvagewallet\fR .IP @@ -258,10 +264,15 @@ Spend unconfirmed change when sending transactions (default: 1) If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: 2) .HP +\fB\-txexpirydelta\fR +.IP +Set the number of blocks after which a transaction that has not been +mined will become invalid (default: 20) +.HP \fB\-maxtxfee=\fR .IP -Maximum total fees to use in a single wallet transaction; setting this -too low may abort large transactions (default: 0.10) +Maximum total fees (in ZEC) to use in a single wallet transaction; +setting this too low may abort large transactions (default: 0.10) .HP \fB\-upgradewallet\fR .IP @@ -333,7 +344,7 @@ Prepend debug output with timestamp (default: 1) .HP \fB\-minrelaytxfee=\fR .IP -Fees (in BTC/Kb) smaller than this are considered zero fee for relaying +Fees (in ZEC/kB) smaller than this are considered zero fee for relaying (default: 0.000001) .HP \fB\-printtoconsole\fR @@ -454,6 +465,10 @@ output (default: 1 if running in a console, 0 otherwise) Number of seconds between metrics refreshes (default: 1 if running in a console, 600 otherwise) .SH COPYRIGHT + +In order to ensure you are adequately protecting your privacy when using Zcash, +please see . + Copyright (C) 2009-2017 The Bitcoin Core Developers Copyright (C) 2015-2017 The Zcash Developers @@ -464,4 +479,4 @@ or . This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit and cryptographic software written -by Eric Young and UPnP software written by Thomas Bernard. +by Eric Young. diff --git a/doc/payment-api.md b/doc/payment-api.md index c4127b223..14732adba 100644 --- a/doc/payment-api.md +++ b/doc/payment-api.md @@ -32,7 +32,7 @@ RPC calls by category: * Addresses : z_getnewaddress, z_listaddresses, z_validateaddress * Keys : z_exportkey, z_importkey, z_exportwallet, z_importwallet * Operation: z_getoperationresult, z_getoperationstatus, z_listoperationids -* Payment : z_listreceivedbyaddress, z_sendmany +* Payment : z_listreceivedbyaddress, z_sendmany, z_shieldcoinbase RPC parameter conventions: @@ -46,7 +46,7 @@ RPC parameter conventions: Command | Parameters | Description --- | --- | --- -z_getbalance
| address [minconf=1] | Returns the balance of a taddr or zaddr belonging to the node’s wallet.

Optionally set the minimum number of confirmations a private or transaction transaction must have in order to be included in the balance. Use 0 to count unconfirmed transactions. +z_getbalance
| address [minconf=1] | Returns the balance of a taddr or zaddr belonging to the node’s wallet.

Optionally set the minimum number of confirmations a private or transparent transaction must have in order to be included in the balance. Use 0 to count unconfirmed transactions. z_gettotalbalance
| [minconf=1] | Return the total value of funds stored in the node’s wallet.

Optionally set the minimum number of confirmations a private or transparent transaction must have in order to be included in the balance. Use 0 to count unconfirmed transactions.

Output:
{
"transparent" : 1.23,
"private" : 4.56,
"total" : 5.79} ### Addresses @@ -55,7 +55,7 @@ Command | Parameters | Description --- | --- | --- z_getnewaddress | | Return a new zaddr for sending and receiving payments. The spending key for this zaddr will be added to the node’s wallet.

Output:
zN68D8hSs3... z_listaddresses | | Returns a list of all the zaddrs in this node’s wallet for which you have a spending key.

Output:
{ [“z123…”, “z456...”, “z789...”] } -z_validateaddress | | Return information about a given zaddr.

Output:
{"isvalid" : true,
"address" : "zcWsmq...",
"payingkey" : "f5bb3c...",
"transmissionkey" : "7a58c7...",
"ismine" : true} +z_validateaddress | zaddr | Return information about a given zaddr.

Output:
{"isvalid" : true,
"address" : "zcWsmq...",
"payingkey" : "f5bb3c...",
"transmissionkey" : "7a58c7...",
"ismine" : true} ### Key Management @@ -64,14 +64,15 @@ Command | Parameters | Description z_exportkey | zaddr | _Requires an unlocked wallet or an unencrypted wallet._

Return a zkey for a given zaddr belonging to the node’s wallet.

The key will be returned as a string formatted using Base58Check as described in the Zcash protocol spec.

Output:AKWUAkypwQjhZ6LLNaMuuuLcmZ6gt5UFyo8m3jGutvALmwZKLdR5 z_importkey | zkey [rescan=true] | _Wallet must be unlocked._

Add a zkey as returned by z_exportkey to a node's wallet.

The key should be formatted using Base58Check as described in the Zcash protocol spec.

Set rescan to true (the default) to rescan the entire local block database for transactions affecting any address or pubkey script in the wallet (including transactions affecting the newly-added address for this spending key). z_exportwallet | filename | _Requires an unlocked wallet or an unencrypted wallet._

Creates or overwrites a file with taddr private keys and zaddr private keys in a human-readable format.

Filename is the file in which the wallet dump will be placed. May be prefaced by an absolute file path. An existing file with that name will be overwritten.

No value is returned but a JSON-RPC error will be reported if a failure occurred. -z_importwallet | filename | _Requires an unlocked wallet or an unencrypted wallet._

Imports private keys from a file in wallet export file format (see z_exportwallet). These keys will be added to the keys currently in the wallet. This call may need to rescan all or parts of the block chain for transactions affecting the newly-added keys, which may take several minutes.

Filename is the file to import. The path is relative to zcashd’s working directory.

No value is returned but a JSON-RPC error will be reported if a failure occurred. +z_importwallet | filename | _Requires an unlocked wallet or an unencrypted wallet._

Imports private keys from a file in wallet export file format (see z_exportwallet). These keys will be added to the keys currently in the wallet. This call may need to rescan all or parts of the block chain for transactions affecting the newly-added keys, which may take several minutes.

Filename is the file to import. The path is relative to komodod’s working directory.

No value is returned but a JSON-RPC error will be reported if a failure occurred. ### Payment Command | Parameters | Description --- | --- | --- z_listreceivedbyaddress
| zaddr [minconf=1] | Return a list of amounts received by a zaddr belonging to the node’s wallet.

Optionally set the minimum number of confirmations which a received amount must have in order to be included in the result. Use 0 to count unconfirmed transactions.

Output:
[{
“txid”: “4a0f…”,
“amount”: 0.54,
“memo”:”F0FF…”,}, {...}, {...}
] -z_sendmany
| fromaddress amounts [minconf=1] [fee=0.0001] | _This is an Asynchronous RPC call_

Send funds from an address to multiple outputs. The address can be either a taddr or a zaddr.

Amounts is a list containing key/value pairs corresponding to the addresses and amount to pay. Each output address can be in taddr or zaddr format.

When sending to a zaddr, you also have the option of attaching a memo in hexadecimal format.

**NOTE:**When sending coinbase funds to a zaddr, the node's wallet does not allow any change. Put another way, spending a partial amount of a coinbase utxo is not allowed. This is not a consensus rule but a local wallet rule due to the current implementation of z_sendmany. In future, this rule may be removed.

Example of Outputs parameter:
[{“address”:”t123…”, “amount”:0.005},
,{“address”:”z010…”,”amount”:0.03, “memo”:”f508af…”}]

Optionally set the minimum number of confirmations which a private or transparent transaction must have in order to be used as an input.

Optionally set a transaction fee, which by default is 0.0001 ZEC.

Any transparent change will be sent to a new transparent address. Any private change will be sent back to the zaddr being used as the source of funds.

Returns an operationid. You use the operationid value with z_getoperationstatus and z_getoperationresult to obtain the result of sending funds, which if successful, will be a txid. +z_sendmany
| fromaddress amounts [minconf=1] [fee=0.0001] | _This is an Asynchronous RPC call_

Send funds from an address to multiple outputs. The address can be either a taddr or a zaddr.

Amounts is a list containing key/value pairs corresponding to the addresses and amount to pay. Each output address can be in taddr or zaddr format.

When sending to a zaddr, you also have the option of attaching a memo in hexadecimal format.

**NOTE:**When sending coinbase funds to a zaddr, the node's wallet does not allow any change. Put another way, spending a partial amount of a coinbase utxo is not allowed. This is not a consensus rule but a local wallet rule due to the current implementation of z_sendmany. In future, this rule may be removed.

Example of Outputs parameter:
[{“address”:”t123…”, “amount”:0.005},
,{“address”:”z010…”,”amount”:0.03, “memo”:”f508af…”}]

Optionally set the minimum number of confirmations which a private or transparent transaction must have in order to be used as an input. When sending from a zaddr, minconf must be greater than zero.

Optionally set a transaction fee, which by default is 0.0001 ZEC.

Any transparent change will be sent to a new transparent address. Any private change will be sent back to the zaddr being used as the source of funds.

Returns an operationid. You use the operationid value with z_getoperationstatus and z_getoperationresult to obtain the result of sending funds, which if successful, will be a txid. +z_shieldcoinbase
| fromaddress toaddress [fee=0.0001] [limit=50] | _This is an Asynchronous RPC call_

Shield transparent coinbase funds by sending to a shielded z address. Utxos selected for shielding will be locked. If there is an error, they are unlocked. The RPC call `listlockunspent` can be used to return a list of locked utxos.

The number of coinbase utxos selected for shielding can be set with the limit parameter, which has a default value of 50. If the parameter is set to 0, the number of utxos selected is limited by the `-mempooltxinputlimit` option. Any limit is constrained by a consensus rule defining a maximum transaction size of 100000 bytes.

The from address is a taddr or "*" for all taddrs belonging to the wallet. The to address is a zaddr. The default fee is 0.0001.

Returns an object containing an operationid which can be used with z_getoperationstatus and z_getoperationresult, along with key-value pairs regarding how many utxos are being shielded in this transaction and what remains to be shielded. ### Operations @@ -100,7 +101,7 @@ It is currently not possible to cancel operations. Command | Parameters | Description --- | --- | --- -z_getoperationresult
| [operationids] | Return OperationStatus JSON objects for all completed operations the node is currently aware of, and then remove the operation from memory.

Operationids is an optional array to filter which operations you want to receive status objects for.

Output is a list of operation status objects, where the status is either "failed", "cancelled" or "success".
[
{“operationid”: “opid-11ee…”,
“status”: “cancelled”},
{“operationid”: “opid-9876”, “status”: ”failed”},
{“operationid”: “opid-0e0e”,
“status”:”success”,
“execution_time”:”25”,
“result”: {“txid”:”af3887654…”,...}
},
] +z_getoperationresult
| [operationids] | Return OperationStatus JSON objects for all completed operations the node is currently aware of, and then remove the operation from memory.

Operationids is an optional array to filter which operations you want to receive status objects for.

Output is a list of operation status objects, where the status is either "failed", "cancelled" or "success".
[
{“operationid”: “opid-11ee…”,
“status”: “cancelled”},
{“operationid”: “opid-9876”, “status”: ”failed”},
{“operationid”: “opid-0e0e”,
“status”:”success”,
“execution_time”:”25”,
“result”: {“txid”:”af3887654…”,...}
},
]

Examples:
zcash-cli z_getoperationresult '["opid-8120fa20-5ee7-4587-957b-f2579c2d882b"]'
zcash-cli z_getoperationresult z_getoperationstatus
| [operationids] | Return OperationStatus JSON objects for all operations the node is currently aware of.

Operationids is an optional array to filter which operations you want to receive status objects for.

Output is a list of operation status objects.
[
{“operationid”: “opid-12ee…”,
“status”: “queued”},
{“operationid”: “opd-098a…”, “status”: ”executing”},
{“operationid”: “opid-9876”, “status”: ”failed”}
]

When the operation succeeds, the status object will also include the result.

{“operationid”: “opid-0e0e”,
“status”:”success”,
“execution_time”:”25”,
“result”: {“txid”:”af3887654…”,...}
} z_listoperationids
| [state] | Return a list of operationids for all operations which the node is currently aware of.

State is an optional string parameter to filter the operations you want listed by their state. Acceptable parameter values are ‘queued’, ‘executing’, ‘success’, ‘failed’, ‘cancelled’.

[“opid-0e0e…”, “opid-1af4…”, … ] @@ -112,6 +113,7 @@ Zcash error codes are defined in https://github.com/zcash/zcash/blob/master/src/ RPC_INVALID_PARAMETER (-8) | _Invalid, missing or duplicate parameter_ ---------------------------| ------------------------------------------------- +"Minconf cannot be zero when sending from zaddr" | Cannot accept minimum confirmation value of zero when sending from zaddr. "Minconf cannot be negative" | Cannot accept negative minimum confirmation number. "Minimum number of confirmations cannot be less than 0" | Cannot accept negative minimum confirmation number. "From address parameter missing" | Missing an address to send funds from. @@ -157,7 +159,7 @@ RPC_WALLET_ERROR (-4) | _Unspecified problem with wallet_ "Could not find previous JoinSplit anchor" | Try restarting node with `-reindex`. "Error decrypting output note of previous JoinSplit: __" | "Could not find witness for note commitment" | Try restarting node with `-rescan`. -"Witness for note commitment is null" | Missing witness for note commitement. +"Witness for note commitment is null" | Missing witness for note commitment. "Witness for spendable note does not have same anchor as change input" | Invalid anchor for spendable note witness. "Not enough funds to pay miners fee" | Retry with sufficient funds. "Missing hex data for raw transaction" | Raw transaction data is null. diff --git a/doc/payment-disclosure.md b/doc/payment-disclosure.md new file mode 100644 index 000000000..d0aa68a96 --- /dev/null +++ b/doc/payment-disclosure.md @@ -0,0 +1,107 @@ +# Payment Disclosure (Experimental Feature) + +**Summary** + +Use RPC calls `z_getpaymentdisclosure` and `z_validatepaymentdisclosure` to reveal details of a shielded payment. + +**Who should read this document** + +Frequent users of shielded transactions, payment processors, exchanges, block explorer + +### Experimental Feature + +This is an experimental feature. Enable it by launching `zcashd` with flags: + + zcashd -experimentalfeatures -paymentdisclosure -debug=paymentdisclosure -txindex=1 + +These flags can also be set as options in `zcash.conf`. + +All nodes that generate or validate payment disclosures must run with `txindex=1` enabled. + +### Background + +Payment Disclosure is an implementation of the work-in-progress Payment Disclosure ZIP [1]. + +The ZIP describes a method of proving that a payment was sent to a shielded address. In the typical case, this means enabling a sender to present a proof that they transferred funds to a recipient's shielded address. + +[1] https://github.com/zcash/zips/pull/119 + +### Example Use Case + +Alice the customer sends 10 ZEC to Bob the merchant at the shielded address shown on their website. However, Bob is not sure if he received the funds. + +Alice's node is running with payment disclosure enabled, so Alice generates a payment disclosure and provides it to Bob, who verifies the payment was made. + +If Bob is a bad merchant, Alice can present the payment disclosure to a third party to validate that payment was indeed made. + +### Solution + +A payment disclosure can be generated for any output of a JoinSplit using the RPC call: + + z_getpaymentdisclosure txid js_index output_index (message) + +An optional message can be supplied. This could be used for a refund address or some other reference, as currently it is not common practice to (ahead of time) include a refund address in the memo field when making a payment. + +To validate a payment disclosure, the following RPC call can be used: + + z_validatepaymentdisclosure hexdata + +### Example + +Generate a payment disclosure for the first joinsplit, second output (index starts from zero): + + zcash-cli z_getpaymentdisclosure 79189528d611e811a1c7bb0358dd31343033d14b4c1e998d7c4799c40f8b652b 0 1 "Hello" + +This returns a payment disclosure in the form of a hex string: + + 706462ff000a3722aafa8190cdf9710bfad6da2af6d3a74262c1fc96ad47df814b0cd5641c2b658b0fc499477c8d991e4c4bd133303431dd5803bbc7a111e811d6289518790000000000000000017e861adb829d8cb1cbcf6330b8c2e25fb0d08041a67a857815a136f0227f8a5342bce5b3c0d894e2983000eb594702d3c1580817d0374e15078528e56bb6f80c0548656c6c6f59a7085395c9e706d82afe3157c54ad4ae5bf144fcc774a8d9c921c58471402019c156ec5641e2173c4fb6467df5f28530dc4636fa71f4d0e48fc5c560fac500 + +To validate the payment disclosure: + + zcash-cli z_validatepaymentdisclosure HEXDATA + +This returns data related to the payment and the payment disclosure: + + { + "txid": "79189528d611e811a1c7bb0358dd31343033d14b4c1e998d7c4799c40f8b652b", + "jsIndex": 0, + "outputIndex": 1, + "version": 0, + "onetimePrivKey": "1c64d50c4b81df47ad96fcc16242a7d3f62adad6fa0b71f9cd9081faaa22370a", + "message": "Hello", + "joinSplitPubKey": "d1c465d16166b602992479acfac18e87dc18065f6cefde6a002e70bc371b9faf", + "signatureVerified": true, + "paymentAddress": "ztaZJXy8iX8nrk2ytXKDBoTWqPkhQcj6E2ifARnD3wfkFwsxXs5SoX7NGmrjkzSiSKn8VtLHTJae48vX5NakvmDhtGNY5eb", + "memo": "f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "value": 12.49900000, + "commitmentMatch": true, + "valid": true + } + +The `signatureVerified` field confirms that the payment disclosure was generated and signed with the joinSplitPrivKey, which should only be known by the node generating and sending the transaction 7918...652b in question. + +### Where is the data stored? + +For all nodes, payment disclosure does not touch `wallet.dat` in any way. + +For nodes that only validate payment disclosures, no data is stored locally. + +For nodes that generate payment disclosures, a LevelDB database is created in the node's datadir. For most users, this would be in the folder: + + $HOME/.zcash/paymentdisclosure + +If you decide you don't want to use payment disclosure, it is safe to shut down your node and delete the database folder. + +### Security Properties + +Please consult the work-in-progress ZIP for details about the protocol, security properties and caveats. + +### Reminder + +Feedback is most welcome! + +This is an experimental feature so there are no guarantees that the protocol, database format, RPC interface etc. will remain the same in the future. + +### Notes + +Currently there is no user friendly way to help senders identify which joinsplit output index maps to a given payment they made. It is possible to construct this from `debug.log`. Ideas and feedback are most welcome on how to improve the user experience. diff --git a/doc/reducing-memory-usage.md b/doc/reducing-memory-usage.md new file mode 100644 index 000000000..333ea0086 --- /dev/null +++ b/doc/reducing-memory-usage.md @@ -0,0 +1,8 @@ +In-memory caches +---------------- + +The size of some in-memory caches can be reduced. As caches trade off memory usage for performance, usually reducing these have a negative effect on performance. + +- `-dbcache=` - the UTXO database cache size, this defaults to `450` (`100` before 1.0.15). The unit is MiB (where 1 GiB = 1024 MiB). + - The minimum value for `-dbcache` is 4. + - A lower dbcache make initial sync time much longer. After the initial sync, the effect is less pronounced for most use-cases, unless fast validation of blocks is important such as for mining. diff --git a/doc/release-notes.md b/doc/release-notes.md new file mode 100644 index 000000000..a29094b51 --- /dev/null +++ b/doc/release-notes.md @@ -0,0 +1,6 @@ +(note: this is a temporary file, to be added-to by anybody, and moved to +release-notes at release time) + +Notable changes +=============== + diff --git a/doc/release-notes/release-notes-1.0.10-1.md b/doc/release-notes/release-notes-1.0.10-1.md new file mode 100644 index 000000000..fea2f5156 --- /dev/null +++ b/doc/release-notes/release-notes-1.0.10-1.md @@ -0,0 +1,11 @@ +Jack Grigg (1): + Disable building Proton in Gitian + +Sean Bowe (2): + Revert "Remove an unneeded version workaround as per @str4d's review comment." + Revert "Delete old protocol version constants and simplify code that used them." + +Simon Liu (2): + make-release.py: Versioning changes for 1.0.10-1. + make-release.py: Updated manpages for 1.0.10-1. + diff --git a/doc/release-notes/release-notes-1.0.10.md b/doc/release-notes/release-notes-1.0.10.md new file mode 100644 index 000000000..16d2a62ba --- /dev/null +++ b/doc/release-notes/release-notes-1.0.10.md @@ -0,0 +1,79 @@ +Notable changes +=============== + +Signature validation using libsecp256k1 +--------------------------------------- + +ECDSA signatures inside Zcash transactions now use validation using +[https://github.com/bitcoin/secp256k1](libsecp256k1) instead of OpenSSL. + +Depending on the platform, this means a significant speedup for raw signature +validation speed. The advantage is largest on x86_64, where validation is over +five times faster. In practice, this translates to a raw reindexing and new +block validation times that are less than half of what it was before. + +Libsecp256k1 has undergone very extensive testing and validation upstream. + +A side effect of this change is that libconsensus no longer depends on OpenSSL. + +Changelog +========= + +Boris Hajduk (1): + documentatin z_validateaddress was missing param + +Daira Hopwood (8): + Delete old protocol version constants and simplify code that used them. fixes #2244 + Remove an unneeded version workaround as per @str4d's review comment. + Remove unneeded lax ECDSA signature verification. + Strict DER signatures are always enforced; remove the flag and code that used it. + Repair tests for strict DER signatures. While we're at it, repair a similar test for CLTV, and make the repaired RPC tests run by default. + Make transaction test failures print the comments preceding the test JSON. + Fix a comment that was made stale before launch by #1016 (commit 542da61). + Delete test that is redundant and inapplicable to Zcash. + +Jack Grigg (20): + Fix incorrect locking in CCryptoKeyStore + Use AtomicTimer for metrics screen thread count + Revert "Fix secp256k1 test compilation" + Squashed 'src/secp256k1/' changes from 22f60a6..84973d3 + Fix potential overflows in ECDSA DER parsers + Rename FALLBACK_DOWNLOAD_PATH to PRIORITY_DOWNLOAD_PATH + Add test for incorrect consensus logic + Correct consensus logic in ContextualCheckInputs + Add comments + Update Debian copyright list + Specify ECDSA constant sizes as constants + Remove redundant `= 0` initialisations + Ensure that ECDSA constant sizes are correctly-sized + Add test for -mempooltxinputlimit + Hold an ECCVerifyHandle in zcash-gtest + Additional testing of -mempooltxinputlimit + Fix comment + Use sendfrom for both t-addr calls + make-release.py: Versioning changes for 1.0.10. + make-release.py: Updated manpages for 1.0.10. + +Kevin Pan (1): + "getblocktemplate" could work without wallet + +Pieter Wuille (2): + Update key.cpp to new secp256k1 API + Switch to libsecp256k1-based validation for ECDSA + +Simon Liu (5): + Fix intermediate vpub_new leakage in multi joinsplit tx (#1360) + Add option 'mempooltxinputlimit' so the mempool can reject a transaction based on the number of transparent inputs. + Check mempooltxinputlimit when creating a transaction to avoid local mempool rejection. + Partial revert & fix for commit 9e84b5a ; code block in wrong location. + Fix #b1eb4f2 so test checks sendfrom as originally intended. + +Wladimir J. van der Laan (2): + Use real number of cores for default -par, ignore virtual cores + Remove ChainParams::DefaultMinerThreads + +kozyilmaz (3): + [macOS] system linker does not support “--version” option but only “-v” + option to disable building libraries (zcutil/build.sh) + support per platform filename and hash setting for dependencies + diff --git a/doc/release-notes/release-notes-1.0.11-rc1.md b/doc/release-notes/release-notes-1.0.11-rc1.md new file mode 100644 index 000000000..4542a448c --- /dev/null +++ b/doc/release-notes/release-notes-1.0.11-rc1.md @@ -0,0 +1,44 @@ +Changelog +========= + +Ariel Gabizon (2): + make-release.py: Versioning changes for 1.0.11-rc1. + make-release.py: Updated manpages for 1.0.11-rc1. + +Daira Hopwood (7): + Clean up imports to be pyflakes-checkable. fixes #2450 + For unused variables reported by pyflakes, either remove the variable, suppress the warning, or fix a bug (if the wrong variable was used). refs #2450 + Cosmetics (trailing whitespace, comment conventions, etc.) + Alert 1004 (version 1.0.10 only) + Remove UPnP support. fixes #2500 + Change wording in Security Warnings section of README.md. + Document our criteria for adding CI workers. closes #2499 + +Jack Grigg (15): + Pull in temporary release notes during the release process + Ansible playbook for installing Zcash dependencies and Buildbot worker + Variable overrides for Debian, Ubuntu and Fedora + Variable overrides for FreeBSD + Simplify Python installation, inform user if they need to manually configure + Add test for issue #2444 + Add Buildbot worker setup to Ansible playbook + Add steps for setting up a latent worker on Amazon EC2 + Add pyblake2 to required Python modules + Remove Buildbot version from host file + Add a separate Buildbot host info template for EC2 + Add pyflakes to required Python modules + Add block download progress to metrics UI + Correct and extend EstimateNetHeightInner tests + Improve network height estimation + +Simon Liu (3): + Closes #2446 by adding generated field to listunspent. + Fixes #2519. When sending from a zaddr, minconf cannot be zero. + Fixes #2480. Null entry in map was dereferenced leading to a segfault. + +Wladimir J. van der Laan (1): + rpc: Add WWW-Authenticate header to 401 response + +practicalswift (1): + Net: Fix resource leak in ReadBinaryFile(...) + diff --git a/doc/release-notes/release-notes-1.0.11.md b/doc/release-notes/release-notes-1.0.11.md new file mode 100644 index 000000000..d5a12a222 --- /dev/null +++ b/doc/release-notes/release-notes-1.0.11.md @@ -0,0 +1,47 @@ +Changelog +========= + +Ariel Gabizon (3): + make-release.py: Versioning changes for 1.0.11-rc1. + make-release.py: Updated manpages for 1.0.11-rc1. + make-release.py: Updated release notes and changelog for 1.0.11-rc1. + +Daira Hopwood (7): + Clean up imports to be pyflakes-checkable. fixes #2450 + For unused variables reported by pyflakes, either remove the variable, suppress the warning, or fix a bug (if the wrong variable was used). refs #2450 + Cosmetics (trailing whitespace, comment conventions, etc.) + Alert 1004 (version 1.0.10 only) + Remove UPnP support. fixes #2500 + Change wording in Security Warnings section of README.md. + Document our criteria for adding CI workers. closes #2499 + +Jack Grigg (17): + Pull in temporary release notes during the release process + Ansible playbook for installing Zcash dependencies and Buildbot worker + Variable overrides for Debian, Ubuntu and Fedora + Variable overrides for FreeBSD + Simplify Python installation, inform user if they need to manually configure + Add test for issue #2444 + Add Buildbot worker setup to Ansible playbook + Add steps for setting up a latent worker on Amazon EC2 + Add pyblake2 to required Python modules + Remove Buildbot version from host file + Add a separate Buildbot host info template for EC2 + Add pyflakes to required Python modules + Add block download progress to metrics UI + Correct and extend EstimateNetHeightInner tests + Improve network height estimation + make-release.py: Versioning changes for 1.0.11. + make-release.py: Updated manpages for 1.0.11. + +Simon Liu (3): + Closes #2446 by adding generated field to listunspent. + Fixes #2519. When sending from a zaddr, minconf cannot be zero. + Fixes #2480. Null entry in map was dereferenced leading to a segfault. + +Wladimir J. van der Laan (1): + rpc: Add WWW-Authenticate header to 401 response + +practicalswift (1): + Net: Fix resource leak in ReadBinaryFile(...) + diff --git a/doc/release-notes/release-notes-1.0.12-rc1.md b/doc/release-notes/release-notes-1.0.12-rc1.md new file mode 100644 index 000000000..27d36b7e7 --- /dev/null +++ b/doc/release-notes/release-notes-1.0.12-rc1.md @@ -0,0 +1,57 @@ +Changelog +========= + +Ariel (1): + add examples to z_getoperationresult + +Ariel Gabizon (1): + add load-wallet benchmark + +Bjorn Hjortsberg (2): + Do not warn on built in declaration mismatch + Remove deprecated exception specification + +Jack Grigg (20): + ci-workers: Enable pipelining, and use root to set admin and host details + Variable overrides for Arch Linux + Rationalize currency unit to "ZEC" + ci-workers: Fail if Python is not version 2.7 + ci-workers: Variable overrides and process tweaks for CentOS 7 + Add build progress to the release script if progressbar module is available + Add hotfix support to release script + Document the hotfix release process + Enforce sequential hotfix versioning + Benchmark time to call sendtoaddress with many UTXOs + Fix bug in benchmark data generation script + Adjust instructions for UTXO dataset creation + Add GitHub release notes to release process + Clarify branching and force-building operations in hotfix process + Update user guide translations as part of release process + make-release.py: Send stderr to stdout + List dependencies for release script in release process doc + Additional test cases for importprivkey RPC test + make-release.py: Versioning changes for 1.0.12-rc1. + make-release.py: Updated manpages for 1.0.12-rc1. + +Jason Davies (1): + Fix deprecation policy comment. + +Nathan Wilcox (5): + key_import_export rpc-test: verify that UTXO view co-evolves for nodes sharing a key. + Add a new rpc-test-specified requirement: `importprivkey` outputs the associated address. (Test fails.) + [tests pass] Output address on new key import. + Add a new requirement that `importprivkey` API is idempotent. + [tests pass] Ensure `importprivkey` outputs the address in case key is already imported. + +Ross Nicoll (1): + Rationalize currency unit to "BTC" + +Simon Liu (3): + Closes #2583. Exclude watch-only utxos from z_sendmany coin selection. + Set up a clean chain. Delete redundant method wait_until_miner_sees() via use of sync_all(). + Implement RPC shield_coinbase #2448. + +kpcyrd (2): + Fetch params from ipfs if possible + Prefer wget over ipfs + diff --git a/doc/release-notes/release-notes-1.0.12.md b/doc/release-notes/release-notes-1.0.12.md new file mode 100644 index 000000000..6aa6cd651 --- /dev/null +++ b/doc/release-notes/release-notes-1.0.12.md @@ -0,0 +1,65 @@ +Changelog +========= + +Ariel (1): + add examples to z_getoperationresult + +Ariel Gabizon (1): + add load-wallet benchmark + +Bjorn Hjortsberg (2): + Do not warn on built in declaration mismatch + Remove deprecated exception specification + +Jack Grigg (26): + ci-workers: Enable pipelining, and use root to set admin and host details + Variable overrides for Arch Linux + Rationalize currency unit to "ZEC" + ci-workers: Fail if Python is not version 2.7 + ci-workers: Variable overrides and process tweaks for CentOS 7 + Add build progress to the release script if progressbar module is available + Add hotfix support to release script + Document the hotfix release process + Enforce sequential hotfix versioning + Benchmark time to call sendtoaddress with many UTXOs + Fix bug in benchmark data generation script + Adjust instructions for UTXO dataset creation + Add GitHub release notes to release process + Clarify branching and force-building operations in hotfix process + Update user guide translations as part of release process + make-release.py: Send stderr to stdout + List dependencies for release script in release process doc + Additional test cases for importprivkey RPC test + make-release.py: Versioning changes for 1.0.12-rc1. + make-release.py: Updated manpages for 1.0.12-rc1. + make-release.py: Updated release notes and changelog for 1.0.12-rc1. + Fix pyflakes warnings in RPC tests + Individualise performance-measurements.sh errors for debugging + Fix incorrect failure in memory benchmark + make-release.py: Versioning changes for 1.0.12. + make-release.py: Updated manpages for 1.0.12. + +Jason Davies (1): + Fix deprecation policy comment. + +Nathan Wilcox (5): + key_import_export rpc-test: verify that UTXO view co-evolves for nodes sharing a key. + Add a new rpc-test-specified requirement: `importprivkey` outputs the associated address. (Test fails.) + [tests pass] Output address on new key import. + Add a new requirement that `importprivkey` API is idempotent. + [tests pass] Ensure `importprivkey` outputs the address in case key is already imported. + +Ross Nicoll (1): + Rationalize currency unit to "BTC" + +Simon Liu (5): + Closes #2583. Exclude watch-only utxos from z_sendmany coin selection. + Set up a clean chain. Delete redundant method wait_until_miner_sees() via use of sync_all(). + Implement RPC shield_coinbase #2448. + Update which lock to synchronize on when calling GetBestAnchor(). + Closes #2637. Make z_shieldcoinbase an experimental feature where it can be enabled with: zcashd -experimentalfeatures -zshieldcoinbase. + +kpcyrd (2): + Fetch params from ipfs if possible + Prefer wget over ipfs + diff --git a/doc/release-notes/release-notes-1.0.13-rc1.md b/doc/release-notes/release-notes-1.0.13-rc1.md new file mode 100644 index 000000000..4d8a37a7c --- /dev/null +++ b/doc/release-notes/release-notes-1.0.13-rc1.md @@ -0,0 +1,88 @@ +Changelog +========= + +Ariel Gabizon (1): + boost::format -> tinyformat + +Bruno Arueira (1): + Removes out bitcoin mention in favor for zcash + +Cory Fields (1): + httpserver: explicitly detach worker threads + +Duke Leto (1): + Update performance-measurements.sh + +Jack Grigg (37): + Squashed 'src/snark/' content from commit 9ada3f8 + Add libsnark compile flag to not copy DEPINST to PREFIX + Add Ansible playbook for grind workers + Add connections in BIP65 and BIP66 tests to the test manager + Add benchmark for listunspent + [Test] MiniNode: Implement JSDescription parsing + [Test] MiniNode: Implement v2 CTransaction parsing + [Test] MiniNode: Implement Zcash block parsing + [Test] MiniNode: Update protocol version and network magics + [Test] MiniNode: Use Zcash PoW + [Test] MiniNode: Fix coinbase creation + [Test] MiniNode: Coerce OP_PUSHDATA bytearrays to bytes + [Test] MiniNode: Implement Zcash coinbase + Fix BIP65 and BIP66 tests + Un-indent RPC test output in test runner + Replace full-test-suite.sh with a new test suite driver script + Move ensure-no-dot-so-in-depends.py into full_test_suite.py + Move check-security-hardening.sh into full_test_suite.py + Add memory benchmark for validatelargetx + Migrate libsnark test code to Google Test + Remove test code corresponding to removed code + Add alt_bn128 to QAP and Merkle tree gadget tests + Update libsnark LDLIBS + Add "make check" to libsnark that runs the Google Tests + Add "make libsnark-tests" that runs libsnark's "make check" + Changes to get test_r1cs_ppzksnark passing + Add bitcoin-util-test.py to full_test_suite.py + Add stdout notice if any stage fails + Add libsnark to "make clean" + Ensure that libsnark is built first, so its headers are available + Remove OpenSSL libraries from libsnark LDLIBS + Add libsnark tests to full_test_suite.py + Add --list-stages argument to full_test_suite.py + Fix NPE in rpc_wallet_tests + make-release.py: Versioning changes for 1.0.13-rc1. + make-release.py: Updated manpages for 1.0.13-rc1. + Change auto-senescence cycle to 16 weeks + +Jason Davies (1): + Replace "bitcoin" with "Zcash". + +Jay Graber (1): + s/zcash/Zcash + +Jonathan "Duke" Leto (1): + Fix bug where performance-measurements.sh fails hards when given no args + +João Barbosa (1): + Improve shutdown process + +Sean Bowe (5): + Remove libsnark from depends system and integrate it into build system. + Remove crusty old "loadVerifyingKey"/"loadProvingKey" APIs and associated invariants. + Refactor proof generation function. + Add streaming prover. + Integrate low memory prover. + +Simon Liu (7): + Replace 'bitcoin address' with 'zcash address'. + Closes #2639. z_shieldcoinbase is now supported, no longer experimental. + Closes #2263 fixing broken pipe error. + Closes #2576. Update link to security info on z.cash website. + Closes #2639. Adds optional limit parameter with a default value of 50. + Fix an issue where qa test wallet_shieldcoinbase could hang. + Add payment disclosure as experimental feature. + +Wladimir J. van der Laan (4): + Make HTTP server shutdown more graceful + http: Wait for worker threads to exit + http: Force-exit event loop after predefined time + http: speed up shutdown + diff --git a/doc/release-notes/release-notes-1.0.13-rc2.md b/doc/release-notes/release-notes-1.0.13-rc2.md new file mode 100644 index 000000000..6ade7d922 --- /dev/null +++ b/doc/release-notes/release-notes-1.0.13-rc2.md @@ -0,0 +1,95 @@ +Changelog +========= + +Ariel Gabizon (1): + boost::format -> tinyformat + +Bruno Arueira (1): + Removes out bitcoin mention in favor for zcash + +Cory Fields (1): + httpserver: explicitly detach worker threads + +Duke Leto (1): + Update performance-measurements.sh + +Jack Grigg (44): + Squashed 'src/snark/' content from commit 9ada3f8 + Add libsnark compile flag to not copy DEPINST to PREFIX + Add Ansible playbook for grind workers + Add connections in BIP65 and BIP66 tests to the test manager + Add benchmark for listunspent + [Test] MiniNode: Implement JSDescription parsing + [Test] MiniNode: Implement v2 CTransaction parsing + [Test] MiniNode: Implement Zcash block parsing + [Test] MiniNode: Update protocol version and network magics + [Test] MiniNode: Use Zcash PoW + [Test] MiniNode: Fix coinbase creation + [Test] MiniNode: Coerce OP_PUSHDATA bytearrays to bytes + [Test] MiniNode: Implement Zcash coinbase + Fix BIP65 and BIP66 tests + Un-indent RPC test output in test runner + Replace full-test-suite.sh with a new test suite driver script + Move ensure-no-dot-so-in-depends.py into full_test_suite.py + Move check-security-hardening.sh into full_test_suite.py + Add memory benchmark for validatelargetx + Migrate libsnark test code to Google Test + Remove test code corresponding to removed code + Add alt_bn128 to QAP and Merkle tree gadget tests + Update libsnark LDLIBS + Add "make check" to libsnark that runs the Google Tests + Add "make libsnark-tests" that runs libsnark's "make check" + Changes to get test_r1cs_ppzksnark passing + Add bitcoin-util-test.py to full_test_suite.py + Add stdout notice if any stage fails + Add libsnark to "make clean" + Ensure that libsnark is built first, so its headers are available + Remove OpenSSL libraries from libsnark LDLIBS + Add libsnark tests to full_test_suite.py + Add --list-stages argument to full_test_suite.py + Fix NPE in rpc_wallet_tests + make-release.py: Versioning changes for 1.0.13-rc1. + make-release.py: Updated manpages for 1.0.13-rc1. + make-release.py: Updated release notes and changelog for 1.0.13-rc1. + Change auto-senescence cycle to 16 weeks + Move libsnark from DIST_SUBDIRS into EXTRA_DIST + Pass correct dependencies path to libsnark from both Gitian and build.sh + Mark libsnark includes as library includes + Add the tar-pax option to automake + make-release.py: Versioning changes for 1.0.13-rc2. + make-release.py: Updated manpages for 1.0.13-rc2. + +Jason Davies (1): + Replace "bitcoin" with "Zcash". + +Jay Graber (1): + s/zcash/Zcash + +Jonathan "Duke" Leto (1): + Fix bug where performance-measurements.sh fails hards when given no args + +João Barbosa (1): + Improve shutdown process + +Sean Bowe (5): + Remove libsnark from depends system and integrate it into build system. + Remove crusty old "loadVerifyingKey"/"loadProvingKey" APIs and associated invariants. + Refactor proof generation function. + Add streaming prover. + Integrate low memory prover. + +Simon Liu (7): + Replace 'bitcoin address' with 'zcash address'. + Closes #2639. z_shieldcoinbase is now supported, no longer experimental. + Closes #2263 fixing broken pipe error. + Closes #2576. Update link to security info on z.cash website. + Closes #2639. Adds optional limit parameter with a default value of 50. + Fix an issue where qa test wallet_shieldcoinbase could hang. + Add payment disclosure as experimental feature. + +Wladimir J. van der Laan (4): + Make HTTP server shutdown more graceful + http: Wait for worker threads to exit + http: Force-exit event loop after predefined time + http: speed up shutdown + diff --git a/doc/release-notes/release-notes-1.0.13.md b/doc/release-notes/release-notes-1.0.13.md new file mode 100644 index 000000000..3a1a950a9 --- /dev/null +++ b/doc/release-notes/release-notes-1.0.13.md @@ -0,0 +1,98 @@ +Changelog +========= + +Ariel Gabizon (1): + boost::format -> tinyformat + +Bruno Arueira (1): + Removes out bitcoin mention in favor for zcash + +Cory Fields (1): + httpserver: explicitly detach worker threads + +Duke Leto (1): + Update performance-measurements.sh + +Jack Grigg (47): + Squashed 'src/snark/' content from commit 9ada3f8 + Add libsnark compile flag to not copy DEPINST to PREFIX + Add Ansible playbook for grind workers + Add connections in BIP65 and BIP66 tests to the test manager + Add benchmark for listunspent + [Test] MiniNode: Implement JSDescription parsing + [Test] MiniNode: Implement v2 CTransaction parsing + [Test] MiniNode: Implement Zcash block parsing + [Test] MiniNode: Update protocol version and network magics + [Test] MiniNode: Use Zcash PoW + [Test] MiniNode: Fix coinbase creation + [Test] MiniNode: Coerce OP_PUSHDATA bytearrays to bytes + [Test] MiniNode: Implement Zcash coinbase + Fix BIP65 and BIP66 tests + Un-indent RPC test output in test runner + Replace full-test-suite.sh with a new test suite driver script + Move ensure-no-dot-so-in-depends.py into full_test_suite.py + Move check-security-hardening.sh into full_test_suite.py + Add memory benchmark for validatelargetx + Migrate libsnark test code to Google Test + Remove test code corresponding to removed code + Add alt_bn128 to QAP and Merkle tree gadget tests + Update libsnark LDLIBS + Add "make check" to libsnark that runs the Google Tests + Add "make libsnark-tests" that runs libsnark's "make check" + Changes to get test_r1cs_ppzksnark passing + Add bitcoin-util-test.py to full_test_suite.py + Add stdout notice if any stage fails + Add libsnark to "make clean" + Ensure that libsnark is built first, so its headers are available + Remove OpenSSL libraries from libsnark LDLIBS + Add libsnark tests to full_test_suite.py + Add --list-stages argument to full_test_suite.py + Fix NPE in rpc_wallet_tests + make-release.py: Versioning changes for 1.0.13-rc1. + make-release.py: Updated manpages for 1.0.13-rc1. + make-release.py: Updated release notes and changelog for 1.0.13-rc1. + Change auto-senescence cycle to 16 weeks + Move libsnark from DIST_SUBDIRS into EXTRA_DIST + Pass correct dependencies path to libsnark from both Gitian and build.sh + Mark libsnark includes as library includes + Add the tar-pax option to automake + make-release.py: Versioning changes for 1.0.13-rc2. + make-release.py: Updated manpages for 1.0.13-rc2. + make-release.py: Updated release notes and changelog for 1.0.13-rc2. + make-release.py: Versioning changes for 1.0.13. + make-release.py: Updated manpages for 1.0.13. + +Jason Davies (1): + Replace "bitcoin" with "Zcash". + +Jay Graber (1): + s/zcash/Zcash + +Jonathan "Duke" Leto (1): + Fix bug where performance-measurements.sh fails hards when given no args + +João Barbosa (1): + Improve shutdown process + +Sean Bowe (5): + Remove libsnark from depends system and integrate it into build system. + Remove crusty old "loadVerifyingKey"/"loadProvingKey" APIs and associated invariants. + Refactor proof generation function. + Add streaming prover. + Integrate low memory prover. + +Simon Liu (7): + Replace 'bitcoin address' with 'zcash address'. + Closes #2639. z_shieldcoinbase is now supported, no longer experimental. + Closes #2263 fixing broken pipe error. + Closes #2576. Update link to security info on z.cash website. + Closes #2639. Adds optional limit parameter with a default value of 50. + Fix an issue where qa test wallet_shieldcoinbase could hang. + Add payment disclosure as experimental feature. + +Wladimir J. van der Laan (4): + Make HTTP server shutdown more graceful + http: Wait for worker threads to exit + http: Force-exit event loop after predefined time + http: speed up shutdown + diff --git a/doc/release-notes/release-notes-1.0.14-rc1.md b/doc/release-notes/release-notes-1.0.14-rc1.md new file mode 100644 index 000000000..1654bf483 --- /dev/null +++ b/doc/release-notes/release-notes-1.0.14-rc1.md @@ -0,0 +1,156 @@ +Notable changes +=============== + +Incoming viewing keys +--------------------- + +Support for incoming viewing keys, as described in +[the Zcash protocol spec](https://github.com/zcash/zips/blob/master/protocol/protocol.pdf), +has been added to the wallet. + +Use the `z_exportviewingkey` RPC method to obtain the incoming viewing key for a +z-address in a node's wallet. For Sprout z-addresses, these always begin with +"ZiVK" (or "ZiVt" for testnet z-addresses). Use `z_importviewingkey` to import +these into another node. + +A node that possesses an incoming viewing key for a z-address can view all past +transactions received by that address, as well as all future transactions sent +to it, by using `z_listreceivedbyaddress`. They cannot spend any funds from the +address. This is similar to the behaviour of "watch-only" t-addresses. + +`z_gettotalbalance` now has an additional boolean parameter for including the +balance of "watch-only" addresses (both transparent and shielded), which is set +to `false` by default. `z_getbalance` has also been updated to work with +watch-only addresses. + +- **Caution:** for z-addresses, these balances will **not** be accurate if any + funds have been sent from the address. This is because incoming viewing keys + cannot detect spends, and so the "balance" is just the sum of all received + notes, including ones that have been spent. Some future use-cases for incoming + viewing keys will include synchronization data to keep their balances accurate + (e.g. [#2542](https://github.com/zcash/zcash/issues/2542)). + +Changelog +========= + +Anthony Towns (1): + Add configure check for -latomic + +Cory Fields (12): + c++11: don't throw from the reverselock destructor + c++11: CAccountingEntry must be defined before use in a list + c++11: fix libbdb build against libc++ in c++11 mode + depends: use c++11 + depends: bump OSX toolchain + build: Split hardening/fPIE options out + build: define base filenames for use elsewhere in the buildsystem + build: quiet annoying warnings without adding new ones + build: fix Windows builds without pkg-config + build: force a c++ standard to be specified + build: warn about variable length arrays + build: add --enable-werror option + +Jack Grigg (36): + Squashed 'src/secp256k1/' changes from 84973d3..6ad5cdb + Use g-prefixed coreutils commands if they are available + Replace hard-coded defaults for HOST and BUILD with config.guess + Remove manual -std=c++11 flag + Replace "install -D" with "mkdir -p && install" + Check if OpenMP is available before using it + [libsnark] Use POSIX-compliant ar arguments + Include endian-ness compatibility layer in Equihash implementation + build: Split hardening/fPIE options out in Zcash-specific binaries + Change --enable-werror to apply to all warnings, use it in build.sh + Move Zcash flags into configure.ac + ViewingKey -> ReceivingKey per zcash/zips#117 + Implement viewing key storage in the keystore + Factor out common logic from CZCPaymentAddress and CZCSpendingKey + Track net value entering and exiting the Sprout circuit + Add Sprout value pool to getblock and getblockchaininfo + Apply -fstack-protector-all to libsnark + Add Rust and Proton to configure options printout + Clarify operator precedence in serialization of nSproutValue + Remove nSproutValue TODO from CDiskBlockIndex + Add Base58 encoding of viewing keys + Implement viewing key storage in the wallet + Add RPC methods for exporting/importing viewing keys + Update wallet logic to account for viewing keys + Add watch-only support to Zcash RPC methods + Modify zcrawkeygen RPC method to set "zcviewingkey" to the viewing key + Cleanup: Add braces for clarity + Add cautions to z_getbalance and z_gettotalbalance help text about viewing keys + Add release notes for incoming viewing keys + Create release notes starting from the previous non-beta non-RC release + release-notes.py: Remove unnecessary parameter + Regenerate previous release notes to conform to new format + Exclude beta and RC release notes from author tallies + Fix pyflakes warnings in zkey_import_export RPC test + make-release.py: Versioning changes for 1.0.14-rc1. + make-release.py: Updated manpages for 1.0.14-rc1. + +Jay Graber (3): + Add cli and rpc examples for z_sendmany + Fix cli help result for z_shieldcoinbase + Add rpc test that exercises z_importkey + +Jonas Schnelli (1): + Add compile and link options echo to configure + +Luke Dashjr (4): + depends: Use curl for fetching on Linux + Travis: Use curl rather than wget for Mac SDK + Bugfix: depends/Travis: Use --location (follow redirects) and --fail [on HTTP error response] with curl + Travis: Use Blue Box VMs for IPv6 loopback support + +MarcoFalke (2): + Fix url in .travis.yml + [depends] builders: No need to set -L and --location for curl + +Per Grön (2): + Deduplicate test utility method wait_and_assert_operationid_status + Print result of RPC call in test only when PYTHON_DEBUG is set + +René Nyffenegger (1): + Use AC_ARG_VAR to set ARFLAGS. + +Simon Liu (5): + RPC dumpwallet and z_exportwallet updated to no longer allow overwriting an existing file. + Add documentation for shielding coinbase utxos. + Add documentation for payment disclosure. + Closes #2759. Fixes broken pipe error with QA test wallet.py. + Closes #2746. Payment disclosure blobs now use 'zpd:' prefix. + +Wladimir J. van der Laan (6): + build: Enable C++11 build, require C++11 compiler + build: update ax_cxx_compile_stdcxx to serial 4 + test: Remove java comparison tool + build: Remove check for `openssl/ec.h` + devtools: Check for high-entropy ASLR in 64-bit PE executables + build: supply `-Wl,--high-entropy-va` + +daniel (1): + add powerpc build support for openssl lib + +fanquake (3): + [build-aux] Update Boost & check macros to latest serials + [depends] Add -stdlib=libc++ to darwin CXX flags + [depends] Set OSX_MIN_VERSION to 10.8 + +kozyilmaz (1): + empty spaces in PATH variable cause build failure + +syd (13): + Upgrade googletest to 1.8.0 + Get the sec-hard tests to run correctly. + Update libsodium from 1.0.11 to 1.0.15 + Remove Boost conditional compilation. + Update to address @daira comments wrt fixing configure.ac + Get rid of consensus.fPowAllowMinDifficultyBlocks. + Don't compile libgtest.a when building libsnark. + Add gtests to .gitignore + Get rid of fp3 from libsnark, it is not used. + InitGoogleMock instead of InitGoogleTest per CR + Get rid of underscore prefixes for include guards. + Rename bash completion files so that they refer to zcash and not bitcoin. + Fix libsnark test failure. + diff --git a/doc/release-notes/release-notes-1.0.14.md b/doc/release-notes/release-notes-1.0.14.md new file mode 100644 index 000000000..4b9cd0810 --- /dev/null +++ b/doc/release-notes/release-notes-1.0.14.md @@ -0,0 +1,160 @@ +Notable changes +=============== + +Incoming viewing keys +--------------------- + +Support for incoming viewing keys, as described in +[the Zcash protocol spec](https://github.com/zcash/zips/blob/master/protocol/protocol.pdf), +has been added to the wallet. + +Use the `z_exportviewingkey` RPC method to obtain the incoming viewing key for a +z-address in a node's wallet. For Sprout z-addresses, these always begin with +"ZiVK" (or "ZiVt" for testnet z-addresses). Use `z_importviewingkey` to import +these into another node. + +A node that possesses an incoming viewing key for a z-address can view all past +transactions received by that address, as well as all future transactions sent +to it, by using `z_listreceivedbyaddress`. They cannot spend any funds from the +address. This is similar to the behaviour of "watch-only" t-addresses. + +`z_gettotalbalance` now has an additional boolean parameter for including the +balance of "watch-only" addresses (both transparent and shielded), which is set +to `false` by default. `z_getbalance` has also been updated to work with +watch-only addresses. + +- **Caution:** for z-addresses, these balances will **not** be accurate if any + funds have been sent from the address. This is because incoming viewing keys + cannot detect spends, and so the "balance" is just the sum of all received + notes, including ones that have been spent. Some future use-cases for incoming + viewing keys will include synchronization data to keep their balances accurate + (e.g. [#2542](https://github.com/zcash/zcash/issues/2542)). + +Changelog +========= + +Anthony Towns (1): + Add configure check for -latomic + +Cory Fields (12): + c++11: don't throw from the reverselock destructor + c++11: CAccountingEntry must be defined before use in a list + c++11: fix libbdb build against libc++ in c++11 mode + depends: use c++11 + depends: bump OSX toolchain + build: Split hardening/fPIE options out + build: define base filenames for use elsewhere in the buildsystem + build: quiet annoying warnings without adding new ones + build: fix Windows builds without pkg-config + build: force a c++ standard to be specified + build: warn about variable length arrays + build: add --enable-werror option + +Jack Grigg (40): + Squashed 'src/secp256k1/' changes from 84973d3..6ad5cdb + Use g-prefixed coreutils commands if they are available + Replace hard-coded defaults for HOST and BUILD with config.guess + Remove manual -std=c++11 flag + Replace "install -D" with "mkdir -p && install" + Check if OpenMP is available before using it + [libsnark] Use POSIX-compliant ar arguments + Include endian-ness compatibility layer in Equihash implementation + build: Split hardening/fPIE options out in Zcash-specific binaries + Change --enable-werror to apply to all warnings, use it in build.sh + Move Zcash flags into configure.ac + ViewingKey -> ReceivingKey per zcash/zips#117 + Implement viewing key storage in the keystore + Factor out common logic from CZCPaymentAddress and CZCSpendingKey + Track net value entering and exiting the Sprout circuit + Add Sprout value pool to getblock and getblockchaininfo + Apply -fstack-protector-all to libsnark + Add Rust and Proton to configure options printout + Clarify operator precedence in serialization of nSproutValue + Remove nSproutValue TODO from CDiskBlockIndex + Add Base58 encoding of viewing keys + Implement viewing key storage in the wallet + Add RPC methods for exporting/importing viewing keys + Update wallet logic to account for viewing keys + Add watch-only support to Zcash RPC methods + Modify zcrawkeygen RPC method to set "zcviewingkey" to the viewing key + Cleanup: Add braces for clarity + Add cautions to z_getbalance and z_gettotalbalance help text about viewing keys + Add release notes for incoming viewing keys + Create release notes starting from the previous non-beta non-RC release + release-notes.py: Remove unnecessary parameter + Regenerate previous release notes to conform to new format + Exclude beta and RC release notes from author tallies + Fix pyflakes warnings in zkey_import_export RPC test + make-release.py: Versioning changes for 1.0.14-rc1. + make-release.py: Updated manpages for 1.0.14-rc1. + make-release.py: Updated release notes and changelog for 1.0.14-rc1. + Update release process + make-release.py: Versioning changes for 1.0.14. + make-release.py: Updated manpages for 1.0.14. + +Jay Graber (3): + Add cli and rpc examples for z_sendmany + Fix cli help result for z_shieldcoinbase + Add rpc test that exercises z_importkey + +Jonas Schnelli (1): + Add compile and link options echo to configure + +Luke Dashjr (4): + depends: Use curl for fetching on Linux + Travis: Use curl rather than wget for Mac SDK + Bugfix: depends/Travis: Use --location (follow redirects) and --fail [on HTTP error response] with curl + Travis: Use Blue Box VMs for IPv6 loopback support + +MarcoFalke (2): + Fix url in .travis.yml + [depends] builders: No need to set -L and --location for curl + +Per Grön (2): + Deduplicate test utility method wait_and_assert_operationid_status + Print result of RPC call in test only when PYTHON_DEBUG is set + +René Nyffenegger (1): + Use AC_ARG_VAR to set ARFLAGS. + +Simon Liu (5): + RPC dumpwallet and z_exportwallet updated to no longer allow overwriting an existing file. + Add documentation for shielding coinbase utxos. + Add documentation for payment disclosure. + Closes #2759. Fixes broken pipe error with QA test wallet.py. + Closes #2746. Payment disclosure blobs now use 'zpd:' prefix. + +Wladimir J. van der Laan (6): + build: Enable C++11 build, require C++11 compiler + build: update ax_cxx_compile_stdcxx to serial 4 + test: Remove java comparison tool + build: Remove check for `openssl/ec.h` + devtools: Check for high-entropy ASLR in 64-bit PE executables + build: supply `-Wl,--high-entropy-va` + +daniel (1): + add powerpc build support for openssl lib + +fanquake (3): + [build-aux] Update Boost & check macros to latest serials + [depends] Add -stdlib=libc++ to darwin CXX flags + [depends] Set OSX_MIN_VERSION to 10.8 + +kozyilmaz (1): + empty spaces in PATH variable cause build failure + +syd (13): + Upgrade googletest to 1.8.0 + Get the sec-hard tests to run correctly. + Update libsodium from 1.0.11 to 1.0.15 + Remove Boost conditional compilation. + Update to address @daira comments wrt fixing configure.ac + Get rid of consensus.fPowAllowMinDifficultyBlocks. + Don't compile libgtest.a when building libsnark. + Add gtests to .gitignore + Get rid of fp3 from libsnark, it is not used. + InitGoogleMock instead of InitGoogleTest per CR + Get rid of underscore prefixes for include guards. + Rename bash completion files so that they refer to zcash and not bitcoin. + Fix libsnark test failure. + diff --git a/doc/release-notes/release-notes-1.0.15-rc1.md b/doc/release-notes/release-notes-1.0.15-rc1.md new file mode 100644 index 000000000..b4b0b2f0a --- /dev/null +++ b/doc/release-notes/release-notes-1.0.15-rc1.md @@ -0,0 +1,165 @@ +Notable changes +=============== + +UTXO and note merging +--------------------- + +In order to simplify the process of combining many small UTXOs and notes into a +few larger ones, a new RPC method `z_mergetoaddress` has been added. It merges +funds from t-addresses, z-addresses, or both, and sends them to a single +t-address or z-address. + +Unlike most other RPC methods, `z_mergetoaddress` operates over a particular +quantity of UTXOs and notes, instead of a particular amount of ZEC. By default, +it will merge 50 UTXOs and 10 notes at a time; these limits can be adjusted with +the parameters `transparent_limit` and `shielded_limit`. + +`z_mergetoaddress` also returns the number of UTXOs and notes remaining in the +given addresses, which can be used to automate the merging process (for example, +merging until the number of UTXOs falls below some value). + +UTXO memory accounting +---------------------- + +The default -dbcache has been changed in this release to 450MiB. Users can set -dbcache to a higher value (e.g. to keep the UTXO set more fully cached in memory). Users on low-memory systems (such as systems with 1GB or less) should consider specifying a lower value for this parameter. + +Additional information relating to running on low-memory systems can be found here: [reducing-memory-usage.md](https://github.com/zcash/zcash/blob/master/doc/reducing-memory-usage.md). + +Changelog +========= + +21E14 (1): + Remove obsolete reference to CValidationState from UpdateCoins. + +Alex Morcos (1): + Implement helper class for CTxMemPoolEntry constructor + +Ariel (2): + add blake2b writer + update SignatureHash according to Overwinter spec + +Ashley Holman (1): + TxMemPool: Change mapTx to a boost::multi_index_container + +Cory Fields (2): + chainparams: move CCheckpointData into chainparams.h + chainparams: don't use std namespace + +Daniel Kraft (1): + Clean up chainparams some more. + +Jack Grigg (38): + Scope the ECDSA constant sizes to CPubKey / CKey classes + Enable Bash completion for -exportdir + Check chainValueZat when checking value pool monitoring + Add missing namespace for boost::get + Add viewing key prefix to regtest parameters + zkey_import_export: Synchronize mempools before mining + Use JoinSplitTestingSetup for Boost sighash tests + Network upgrade activation mechanism + Allow changing network upgrade parameters on regtest + Test network upgrade logic + Adjust rewind logic to use the network upgrade mechanism + Add Overwinter to upgrade list + Add method for fetching the next activation height after a given block height + Use a boost::optional for nCachedBranchId + Change UI/log status message for block rewinding + Update quote from ZIP 200 + Update SignatureHash tests for transaction format changes + Implement roll-back limit for reorganisation + Add rollback limit to block index rewinding + Remove mempool transactions which commit to an unmineable branch ID + Remove P2WPKH and P2WSH from signing logic + Add consensus branch ID parameter to SignatureHash, remove SigVersion parameter + Cleanup: Wrap function arguments + Regenerate SignatureHash tests + Make number of inputs configurable in validatelargetx test + Use v3 transactions with caching for validatelargetx benchmark + Extend CWallet::GetFilteredNotes to enable filtering on a set of addresses + Add branch IDs for current and next block to getblockchaininfo + Check Equihash solution when loading block index + Implement z_mergetoaddress for combining UTXOs and notes + Gate z_mergetoaddress as an experimental feature + Add z_mergetoaddress to release notes + Check upgrade status in wallet_overwintertx RPC test + Document that consensus.chaintip != consensus.nextblock just before an upgrade + Regenerate sighash tests + wallet_mergetoaddress: Add additional syncs to prevent race conditions + make-release.py: Versioning changes for 1.0.15-rc1. + make-release.py: Updated manpages for 1.0.15-rc1. + +Jay Graber (8): + Add getdeprecationinfo rpc call to return current version and deprecation block height. + Make applicable only on mainnet + Add upgrades field to RPC call getblockchaininfo + Implement transaction expiry for Overwinter + Add -txexpirydelta cli option + Add mempool_tx_expiry.py test + Add expiry to z_mergetoaddress + Change rpc_tests to 21 + +Jonas Nick (1): + Reduce unnecessary hashing in signrawtransaction + +Jorge Timón (3): + Chainparams: Introduce CreateGenesisBlock() static function + Chainparams: CTestNetParams and CRegTestParams extend directly from CChainParams + Mempool: Use Consensus::CheckTxInputs direclty over main::CheckInputs + +Marius Kjærstad (1): + Changed http:// to https:// on some links + +Mark Friedenbach (1): + Explicitly set tx.nVersion for the genesis block and mining tests + +Matt Corallo (5): + Add failing test checking timelocked-txn removal during reorg + Fix removal of time-locked transactions during reorg + Fix comment in removeForReorg + Make indentation in ActivateBestChainStep readable + removeForReorg calls once-per-disconnect-> once-per-reorg + +Maxwell Gubler (1): + Fix syntax examples for z_importwallet and export + +Nicolas DORIER (1): + Unit test for sighash caching + +Pavel Vasin (1): + remove unused NOBLKS_VERSION_{START,END} constants + +Pieter Wuille (8): + Add rewind logic to deal with post-fork software updates + Support -checkmempool=N, which runs checks on average once every N transactions + Report non-mandatory script failures correctly + Refactor script validation to observe amounts + BIP143: Verification logic + BIP143: Signing logic + Precompute sighashes + Rename to PrecomputedTransactionData + +Simon Liu (11): + Fixes #2793. Backport commit f33afd3 to increase dbcache default. + Add documentation about dbcache. + Add note about dbcache to 1.0.15 release notes. + Remove redundant service flag NODE_GETUTXO meant for Bitcoin XT. + Implementation of Overwinter transaction format ZIP 202. + Add test to check malformed v1 transaction against Overwinter tx parser + Closes #2964. z_sendmany once again makes v1 tx for taddr to taddr. + Closes #2954 and #2959. Fixes Overwinter issues in sighash_tests. + Add field nProtocolVersion to struct NetworkUpgrade. + Overwinter peer management and network handshaking. + Add python qa test overwinter_peer_management. + +Suhas Daftuar (3): + Track coinbase spends in CTxMemPoolEntry + Don't call removeForReorg if DisconnectTip fails + Fix removeForReorg to use MedianTimePast + +jc (1): + read hashReserved from disk block index + +syd (2): + Fix libsnark dependency build. + Remove OSX and Windows files from Makefile + share directory. + diff --git a/doc/release-notes/release-notes-1.0.15.md b/doc/release-notes/release-notes-1.0.15.md new file mode 100644 index 000000000..3195c955a --- /dev/null +++ b/doc/release-notes/release-notes-1.0.15.md @@ -0,0 +1,209 @@ +Notable changes +=============== + +Overwinter network upgrade +-------------------------- + +The code preparations for the Overwinter network upgrade, as described in [ZIP +200](https://github.com/zcash/zips/blob/master/zip-0200.rst), [ZIP +201](https://github.com/zcash/zips/blob/master/zip-0201.rst), [ZIP +202](https://github.com/zcash/zips/blob/master/zip-0202.rst), [ZIP +203](https://github.com/zcash/zips/blob/master/zip-0203.rst), and [ZIP +143](https://github.com/zcash/zips/blob/master/zip-0143.rst) are +finished and included in this release. Overwinter will activate on testnet at +height 207500, and can also be activated at a specific height in regtest mode +by setting the config option `-nuparams=5ba81b19:HEIGHT`. + +However, because the Overwinter activation height is not yet specified for +mainnet, version 1.0.15 will behave similarly as other pre-Overwinter releases +even after a future activation of Overwinter on the network. Upgrading from +1.0.15 will be required in order to follow the Overwinter network upgrade on +mainnet. + +Overwinter transaction format +----------------------------- + +Once Overwinter has activated, transactions must use the new v3 format +(including coinbase transactions). All RPC methods that create new transactions +(such as `createrawtransaction` and `getblocktemplate`) will create v3 +transactions once the Overwinter activation height has been reached. + +Overwinter transaction expiry +----------------------------- + +Overwinter transactions created by `zcashd` will also have a default expiry +height set (the block height after which the transaction becomes invalid) of 20 +blocks after the height of the next block. This can be configured with the +config option `-txexpirydelta`. + +UTXO and note merging +--------------------- + +In order to simplify the process of combining many small UTXOs and notes into a +few larger ones, a new RPC method `z_mergetoaddress` has been added. It merges +funds from t-addresses, z-addresses, or both, and sends them to a single +t-address or z-address. + +Unlike most other RPC methods, `z_mergetoaddress` operates over a particular +quantity of UTXOs and notes, instead of a particular amount of ZEC. By default, +it will merge 50 UTXOs and 10 notes at a time; these limits can be adjusted with +the parameters `transparent_limit` and `shielded_limit`. + +`z_mergetoaddress` also returns the number of UTXOs and notes remaining in the +given addresses, which can be used to automate the merging process (for example, +merging until the number of UTXOs falls below some value). + +UTXO memory accounting +---------------------- + +The default `-dbcache` has been changed in this release to 450MiB. Users can set +`-dbcache` to a higher value (e.g. to keep the UTXO set more fully cached in +memory). Users on low-memory systems (such as systems with 1GB or less) should +consider specifying a lower value for this parameter. + +Additional information relating to running on low-memory systems can be found +here: [reducing-memory-usage.md](https://github.com/zcash/zcash/blob/master/doc/reducing-memory-usage.md). + +Changelog +========= + +21E14 (1): + Remove obsolete reference to CValidationState from UpdateCoins. + +Alex Morcos (1): + Implement helper class for CTxMemPoolEntry constructor + +Ariel (2): + add blake2b writer + update SignatureHash according to Overwinter spec + +Ashley Holman (1): + TxMemPool: Change mapTx to a boost::multi_index_container + +Cory Fields (2): + chainparams: move CCheckpointData into chainparams.h + chainparams: don't use std namespace + +Daniel Kraft (1): + Clean up chainparams some more. + +Jack Grigg (43): + Scope the ECDSA constant sizes to CPubKey / CKey classes + Enable Bash completion for -exportdir + Check chainValueZat when checking value pool monitoring + Add missing namespace for boost::get + Add viewing key prefix to regtest parameters + zkey_import_export: Synchronize mempools before mining + Use JoinSplitTestingSetup for Boost sighash tests + Network upgrade activation mechanism + Allow changing network upgrade parameters on regtest + Test network upgrade logic + Adjust rewind logic to use the network upgrade mechanism + Add Overwinter to upgrade list + Add method for fetching the next activation height after a given block height + Use a boost::optional for nCachedBranchId + Change UI/log status message for block rewinding + Update quote from ZIP 200 + Update SignatureHash tests for transaction format changes + Implement roll-back limit for reorganisation + Add rollback limit to block index rewinding + Remove mempool transactions which commit to an unmineable branch ID + Remove P2WPKH and P2WSH from signing logic + Add consensus branch ID parameter to SignatureHash, remove SigVersion parameter + Cleanup: Wrap function arguments + Regenerate SignatureHash tests + Make number of inputs configurable in validatelargetx test + Use v3 transactions with caching for validatelargetx benchmark + Extend CWallet::GetFilteredNotes to enable filtering on a set of addresses + Add branch IDs for current and next block to getblockchaininfo + Check Equihash solution when loading block index + Implement z_mergetoaddress for combining UTXOs and notes + Gate z_mergetoaddress as an experimental feature + Add z_mergetoaddress to release notes + Check upgrade status in wallet_overwintertx RPC test + Document that consensus.chaintip != consensus.nextblock just before an upgrade + Regenerate sighash tests + wallet_mergetoaddress: Add additional syncs to prevent race conditions + make-release.py: Versioning changes for 1.0.15-rc1. + make-release.py: Updated manpages for 1.0.15-rc1. + make-release.py: Updated release notes and changelog for 1.0.15-rc1. + Use block hash comparison for consistency check when loading block index + Overwinter release notes and testnet activation height + make-release.py: Versioning changes for 1.0.15. + make-release.py: Updated manpages for 1.0.15. + +Jay Graber (8): + Add getdeprecationinfo rpc call to return current version and deprecation block height. + Make applicable only on mainnet + Add upgrades field to RPC call getblockchaininfo + Implement transaction expiry for Overwinter + Add -txexpirydelta cli option + Add mempool_tx_expiry.py test + Add expiry to z_mergetoaddress + Change rpc_tests to 21 + +Jonas Nick (1): + Reduce unnecessary hashing in signrawtransaction + +Jorge Timón (3): + Chainparams: Introduce CreateGenesisBlock() static function + Chainparams: CTestNetParams and CRegTestParams extend directly from CChainParams + Mempool: Use Consensus::CheckTxInputs direclty over main::CheckInputs + +Marius Kjærstad (1): + Changed http:// to https:// on some links + +Mark Friedenbach (1): + Explicitly set tx.nVersion for the genesis block and mining tests + +Matt Corallo (5): + Add failing test checking timelocked-txn removal during reorg + Fix removal of time-locked transactions during reorg + Fix comment in removeForReorg + Make indentation in ActivateBestChainStep readable + removeForReorg calls once-per-disconnect-> once-per-reorg + +Maxwell Gubler (1): + Fix syntax examples for z_importwallet and export + +Nicolas DORIER (1): + Unit test for sighash caching + +Pavel Vasin (1): + remove unused NOBLKS_VERSION_{START,END} constants + +Pieter Wuille (8): + Add rewind logic to deal with post-fork software updates + Support -checkmempool=N, which runs checks on average once every N transactions + Report non-mandatory script failures correctly + Refactor script validation to observe amounts + BIP143: Verification logic + BIP143: Signing logic + Precompute sighashes + Rename to PrecomputedTransactionData + +Simon Liu (11): + Fixes #2793. Backport commit f33afd3 to increase dbcache default. + Add documentation about dbcache. + Add note about dbcache to 1.0.15 release notes. + Remove redundant service flag NODE_GETUTXO meant for Bitcoin XT. + Implementation of Overwinter transaction format ZIP 202. + Add test to check malformed v1 transaction against Overwinter tx parser + Closes #2964. z_sendmany once again makes v1 tx for taddr to taddr. + Closes #2954 and #2959. Fixes Overwinter issues in sighash_tests. + Add field nProtocolVersion to struct NetworkUpgrade. + Overwinter peer management and network handshaking. + Add python qa test overwinter_peer_management. + +Suhas Daftuar (3): + Track coinbase spends in CTxMemPoolEntry + Don't call removeForReorg if DisconnectTip fails + Fix removeForReorg to use MedianTimePast + +jc (1): + read hashReserved from disk block index + +syd (2): + Fix libsnark dependency build. + Remove OSX and Windows files from Makefile + share directory. + diff --git a/doc/release-notes/release-notes-1.0.8-1.md b/doc/release-notes/release-notes-1.0.8-1.md new file mode 100644 index 000000000..4650f552c --- /dev/null +++ b/doc/release-notes/release-notes-1.0.8-1.md @@ -0,0 +1,16 @@ +Daira Hopwood (3): + Don't rely on a finite upper bound on fee rate or priority. + Simplify JoinSplit priority calculation. refs 1896 + Add check for JoinSplit priority as calculated by CCoinsViewCache::GetPriority. + +Jack Grigg (1): + Use a larger -rpcclienttimeout for slow performance measurements + +Nathan Wilcox (2): + Bump version numbers for v1.0.8-1. + Commit the changes from gen-manpages.sh, except manually tweak the version strings. + +str4d (2): + Update tests to check actual infinity as well as INF_FEERATE + Add unit test for security issue 2017-04-11.a + diff --git a/doc/release-notes/release-notes-1.0.9.md b/doc/release-notes/release-notes-1.0.9.md new file mode 100644 index 000000000..a1335ec97 --- /dev/null +++ b/doc/release-notes/release-notes-1.0.9.md @@ -0,0 +1,90 @@ +Amgad Abdelhafez (2): + Update timedata.cpp + Update timedata.cpp + +Daira Hopwood (4): + Fix an error reporting bug due to BrokenPipeError and ConnectionResetError not existing in Python 2. refs #2263 + Alert 1002 (versions 1.0.0-1.0.2 inclusive). + Alert 1003 (versions 1.0.3-1.0.8 inclusive). + Disable building Proton by default. + +Jack Grigg (14): + Fix prioritisetransaction RPC test + torcontrol: Handle escapes in Tor QuotedStrings + torcontrol: Add missing copyright header + Convert Zcash versions to Debian format + [manpage] Handle build numbers in versions + Address Daira's comments + Address Daira's further comments + Correctly handle three-digit octals with leading digit 4-7 + Check that >3-digit octals are truncated. + Implement automatic shutdown of deprecated Zcash versions + Wrap messages nicely on metrics screen + Regenerate miner tests + Add a benchmark for calling ConnectBlock on a block with many inputs + Remove additional sources of determinism from benchmark archive + +Jay Graber (2): + Change help text examples to use Zcash addresses + Poll on getblocktemplate result rather than use bare sleep to avoid race condition. + +Nathan Wilcox (39): + [Direct master commit] Fix a release snafu in debian version string. + Show toolchain versions in build.sh. + Start on a make-release.py script; currently just arg parsing and unittests [unittests fail]. + Update version spec by altering test; also update regex to pass single 0 digits in major/minor/patch. + Add another case from debian-style versions. + Add all of the zcash release tags in my current repo as positive test vector. + Add support for beta/rc release versions. + Add version sorting, assert that RELEASE_PREV is the most recent release. + Make SystemExit errors less redundant in output; verify clean git status on master. + Always run unittests prior to actual runs. + Make --help output clean by not running self-test. + Add an option to run against a different repo directory. + Make sure to pull the latest master. + Exit instead of raising an unexpected exception, since it's already logged. + Implement `PathPatcher` abstraction, `clientversion.h` rewrite, and build numbering w/ unittests. + Implement the IS_RELEASE rule for betas. + Generalize buildnum patching for both `clientversion.h` and `configure.ac`. + Modify the `APPROX_RELEASE_HEIGHT`. + Remove portions of `./doc/release-process.md` now implemented in `make-release.py`. + Switch from `sh_out_logged` to `sh_log`. + Shorten the arg log line. + Commit the version changes and build. + Generate manpages; commit that; improve error output in sh_log. + Polish logging a bit more. + Tidy up / systematize logging output a bit more. + First full-release-branch version of script; rewrite large swatch of release-process.md. [Manually tested.] + Enable set -u mode. + Fix a variable name typo. + Reuse zcash_rpc. + Do not use `-rpcwait` on all `zcash_rpc` invocations, only block when starting zcashd. + Fix `release-process.md` doc usage for `make-release.py` to have correct arguments and order. + Include release version in commit comments. + Examine all future versions which are assumed to follow the same Version parser schema. + Consider both beta and rc versions to be `IS_RELEASE == false`. + Add a few more version strings to positive parser test. + Define the deprecation policy for 1.0.9. + Clarify that the feature is automated *shutdown*. + make-release.py: Versioning changes for 1.0.9. + make-release.py: Updated manpages for 1.0.9. + +Paige Peterson (4): + wallet backup instructions + typo and rewording edits + str4d and Ariel's suggestions + specify exportdir being within homedirectory + +Sean Bowe (1): + Check that pairings work properly when the G1 point is at infinity. + +Simon Liu (5): + Add AMQP 1.0 support via Apache Qpid Proton C++ API 0.17.0 + Add --disable-proton flag to build.sh. Proton has build/linker issues with gcc 4.9.2 and requires gcc 5.x. + Fix proton build issue with debian jessie, as used on CI servers. + Change regtest port to 18344. Closes #2269. + Patch to build Proton with minimal dependencies. + +emilrus (1): + Replace bitcoind with zcashd + diff --git a/doc/release-process.md b/doc/release-process.md index b4e2a3565..93a8e8362 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -2,125 +2,132 @@ Release Process ==================== Meta: There should always be a single release engineer to disambiguate responsibility. -## Pre-release - -The following should have been checked well in advance of the release: - -- All dependencies have been updated as appropriate: - - BDB - - Boost - - ccache - - libgmp - - libsnark (upstream of our fork) - - libsodium - - miniupnpc - - OpenSSL +If this is a hotfix release, please see `./hotfix-process.md` before proceeding. +## Pre-release -## Release process +### Github Milestone -## A. Define the release version as: +Ensure all goals for the github milestone are met. If not, remove tickets +or PRs with a comment as to why it is not included. (Running out of time +is a common reason.) - $ ZCASH_RELEASE=MAJOR.MINOR.REVISION(-BUILD_STRING) +### Pre-release checklist: -Example: +Check that dependencies are properly hosted by looking at the `check-depends` builder: - $ ZCASH_RELEASE=1.0.0-beta2 + https://ci.z.cash/#/builders/1 -Also, the following commands use the `ZCASH_RELEASE_PREV` bash variable for the -previous release: +Check that there are no surprising performance regressions: - $ ZCASH_RELEASE_PREV=1.0.0-beta1 + https://speed.z.cash -## B. Create a new release branch / github PR +Ensure that new performance metrics appear on that site. -### B1. Check that you are up-to-date with current master, then create a release branch. +### Protocol Safety Checks: -### B2. Update (commit) version in sources. +If this release changes the behavior of the protocol or fixes a serious +bug, verify that a pre-release PR merge updated `PROTOCOL_VERSION` in +`version.h` correctly. - README.md - src/clientversion.h - configure.ac - contrib/gitian-descriptors/gitian-linux.yml +If this release breaks backwards compatibility or needs to prevent +interaction with software forked projects, change the network magic +numbers. Set the four `pchMessageStart` in `CTestNetParams` in +`chainparams.cpp` to random values. -In `configure.ac` and `clientversion.h`: +Both of these should be done in standard PRs ahead of the release +process. If these were not anticipated correctly, this could block the +release, so if you suspect this is necessary, double check with the +whole engineering team. -- Increment `CLIENT_VERSION_BUILD` according to the following schema: +## Release dependencies - - 0-24: `1.0.0-beta1`-`1.0.0-beta25` - - 25-49: `1.0.0-rc1`-`1.0.0-rc25` - - 50: `1.0.0` - - 51-99: `1.0.0-1`-`1.0.0-49` - - (`CLIENT_VERSION_REVISION` rolls over) - - 0-24: `1.0.1-beta1`-`1.0.1-beta25` +The release script has the following dependencies: -- Change `CLIENT_VERSION_IS_RELEASE` to false while Zcash is in beta-test phase. +- `help2man` +- `debchange` (part of the devscripts Debian package) -If this release changes the behavior of the protocol or fixes a serious bug, we may -also wish to change the `PROTOCOL_VERSION` in `version.h`. +You can optionally install the `progressbar2` Python module with pip to have a +progress bar displayed during the build process. -Commit these changes. (Be sure to do this before building, or else the built binary will include the flag `-dirty`) +## Release process -Build by running `./zcutil/build.sh`. +In the commands below, and are prefixed with a v, ie. +v1.0.9 (not 1.0.9). -Then perform the following command: +### Create the release branch - $ bash contrib/devtools/gen-manpages.sh +Run the release script, which will verify you are on the latest clean +checkout of master, create a branch, then commit standard automated +changes to that branch locally: -Commit the changes. + $ ./zcutil/make-release.py -### B3. Generate release notes +Examples: -Run the release-notes.py script to generate release notes and update authors.md file. For example: + $ ./zcutil/make-release.py v1.0.9 v1.0.8-1 v1.0.8-1 120000 + $ ./zcutil/make-release.py v1.0.13 v1.0.13-rc1 v1.0.12 222900 - $ python zcutil/release-notes.py --version $ZCASH_RELEASE +### Create, Review, and Merge the release branch pull request -Add the newly created release notes to the Git repository: +Review the automated changes in git: - $ git add doc/release-notes/release-notes-$ZCASH_RELEASE.md + $ git log master..HEAD -Update the Debian package changelog: +Push the resulting branch to github: - export DEBVERSION="${ZCASH_RELEASE}" - export DEBEMAIL="${DEBEMAIL:-team@z.cash}" - export DEBFULLNAME="${DEBFULLNAME:-Zcash Company}" + $ git push 'git@github.com:$YOUR_GITHUB_NAME/zcash' $(git rev-parse --abbrev-ref HEAD) - dch -v $DEBVERSION -D jessie -c contrib/debian/changelog +Then create the PR on github. Complete the standard review process, +then merge, then wait for CI to complete. -(`dch` comes from the devscripts package.) +## Make tag for the newly merged result -### B4. Change the network magics +Checkout master and pull the latest version to ensure master is up to date with the release PR which was merged in before. -If this release breaks backwards compatibility, change the network magic -numbers. Set the four `pchMessageStart` in `CTestNetParams` in `chainparams.cpp` -to random values. + $ git checkout master + $ git pull --ff-only -### B5. Merge the previous changes +Check the last commit on the local and remote versions of master to make sure they are the same: -Do the normal pull-request, review, testing process for this release PR. + $ git log -1 -## C. Verify code artifact hosting +The output should include something like, which is created by Homu: -### C1. Ensure depends tree is working + Auto merge of #4242 - nathan-at-least:release-v1.0.9, r=nathan-at-least -https://ci.z.cash/builders/depends-sources +Then create the git tag. The `-s` means the release tag will be +signed. **CAUTION:** Remember the `v` at the beginning here: -### C2. Ensure public parameters work + $ git tag -s v1.0.9 + $ git push origin v1.0.9 -Run `./fetch-params.sh`. +## Make and deploy deterministic builds -## D. Make tag for the newly merged result +- Run the [Gitian deterministic build environment](https://github.com/zcash/zcash-gitian) +- Compare the uploaded [build manifests on gitian.sigs](https://github.com/zcash/gitian.sigs) +- If all is well, the DevOps engineer will build the Debian packages and update the + [apt.z.cash package repository](https://apt.z.cash). -Checkout master and pull the latest version to ensure master is up to date with the release PR which was merged in before. +## Add release notes to GitHub -Check the last commit on the local and remote versions of master to make sure they are the same. +- Go to the [GitHub tags page](https://github.com/zcash/zcash/tags). +- Click "Add release notes" beside the tag for this release. +- Copy the release blog post into the release description, and edit to suit + publication on GitHub. See previous release notes for examples. +- Click "Publish release" if publishing the release blog post now, or + "Save draft" to store the notes internally (and then return later to publish + once the blog post is up). -Then create the git tag: +Note that some GitHub releases are marked as "Verified", and others as +"Unverified". This is related to the GPG signature on the release tag - in +particular, GitHub needs the corresponding public key to be uploaded to a +corresponding GitHub account. If this release is marked as "Unverified", click +the marking to see what GitHub wants to be done. - $ git tag -s v${ZCASH_RELEASE} - $ git push origin v${ZCASH_RELEASE} +## Post Release Task List -## E. Deploy testnet +### Deploy testnet Notify the Zcash DevOps engineer/sysadmin that the release has been tagged. They update some variables in the company's automation code and then run an Ansible playbook, which: @@ -131,26 +138,12 @@ Notify the Zcash DevOps engineer/sysadmin that the release has been tagged. They Then, verify that nodes can connect to the testnet server, and update the guide on the wiki to ensure the correct hostname is listed in the recommended zcash.conf. -## F. Update the 1.0 User Guide - -## G. Publish the release announcement (blog, zcash-dev, slack) - -### G1. Check in with users who opened issues that were resolved in the release - -Contact all users who opened `user support` issues that were resolved in the release, and ask them if the release fixes or improves their issue. - -## H. Make and deploy deterministic builds - -- Run the [Gitian deterministic build environment](https://github.com/zcash/zcash-gitian) -- Compare the uploaded [build manifests on gitian.sigs](https://github.com/zcash/gitian.sigs) -- If all is well, the DevOps engineer will build the Debian packages and update the - [apt.z.cash package repository](https://apt.z.cash). - -## I. Celebrate +### Update the 1.0 User Guide -## missing steps -Zcash still needs: +This also means updating [the translations](https://github.com/zcash/zcash-docs). +Coordinate with the translation team for now. Suggestions for improving this +part of the process should be added to #2596. -* thorough pre-release testing (presumably more thorough than standard PR tests) +### Publish the release announcement (blog, github, zcash-dev, slack) -* automated release deployment (e.g.: updating build-depends mirror, deploying testnet, etc...) +## Celebrate diff --git a/doc/security-warnings.md b/doc/security-warnings.md index ab14b9b44..556a55c4a 100644 --- a/doc/security-warnings.md +++ b/doc/security-warnings.md @@ -42,7 +42,7 @@ Wallet encryption is disabled, for several reasons: You should use full-disk encryption (or encryption of your home directory) to protect your wallet at rest, and should assume (even unprivileged) users who are -runnng on your OS can read your wallet.dat file. +running on your OS can read your wallet.dat file. Side-Channel Attacks -------------------- diff --git a/doc/shield-coinbase.md b/doc/shield-coinbase.md new file mode 100644 index 000000000..d3986fec7 --- /dev/null +++ b/doc/shield-coinbase.md @@ -0,0 +1,101 @@ +# Shielding Coinbase UTXOs + +**Summary** + +Use `z_shieldcoinbase` RPC call to shield coinbase UTXOs. + +**Who should read this document** + +Miners, Mining pools, Online wallets + +## Background + +The current Zcash protocol includes a consensus rule that coinbase rewards must be sent to a shielded address. + +## User Experience Challenges + +A user can use the z_sendmany RPC call to shield coinbase funds, but the call was not designed for sweeping up many UTXOs, and offered a suboptimal user experience. + +If customers send mining pool payouts to their online wallet, the service provider must sort through UTXOs to correctly determine the non-coinbase UTXO funds that can be withdrawn or transferred by customers to another transparent address. + +## Solution + +The z_shieldcoinbase call makes it easy to sweep up coinbase rewards from multiple coinbase UTXOs across multiple coinbase reward addresses. + + z_shieldcoinbase fromaddress toaddress (fee) (limit) + +The default fee is 0.0010000 ZEC and the default limit on the maximum number of UTXOs to shield is 50. + +## Examples + +Sweep up coinbase UTXOs from a transparent address you use for mining: + + zcash-cli z_shieldcoinbase tMyMiningAddress zMyPrivateAddress + +Sweep up coinbase UTXOs from multiple transparent addresses to a shielded address: + + zcash-cli z_shieldcoinbase "*" zMyPrivateAddress + +Sweep up with a fee of 1.23 ZEC: + + zcash-cli z_shieldcoinbase tMyMiningAddress zMyPrivateAddress 1.23 + +Sweep up with a fee of 0.1 ZEC and set limit on the maximum number of UTXOs to shield at 25: + + zcash-cli z_shieldcoinbase "*" zMyPrivateAddress 0.1 25 + +### Asynchronous Call + +The `z_shieldcoinbase` RPC call is an asynchronous call, so you can queue up multiple operations. + +When you invoke + + zcash-cli z_shieldcoinbase tMyMiningAddress zMyPrivateAddress + +JSON will be returned immediately, with the following data fields populated: + +- operationid: a temporary id to use with `z_getoperationstatus` and `z_getoperationresult` to get the status and result of the operation. +- shieldedUTXOs: number of coinbase UTXOs being shielded +- shieldedValue: value of coinbase UTXOs being shielded. +- remainingUTXOs: number of coinbase UTXOs still available for shielding. +- remainingValue: value of coinbase UTXOs still available for shielding + +### Locking UTXOs + +The `z_shieldcoinbase` call will lock any selected UTXOs. This prevents the selected UTXOs which are already queued up from being selected for any other send operation. If the `z_shieldcoinbase` call fails, any locked UTXOs are unlocked. + +You can use the RPC call `lockunspent` to see which UTXOs have been locked. You can also use this call to unlock any UTXOs in the event of an unexpected system failure which leaves UTXOs in a locked state. + +### Limits, Performance and Transaction Confirmation + +The number of coinbase UTXOs selected for shielding can be adjusted by setting the limit parameter. The default value is 50. + +If the limit parameter is set to zero, the zcashd `mempooltxinputlimit` option will be used instead, where the default value for `mempooltxinputlimit` is zero, which means no limit. + +Any limit is constrained by a hard limit due to the consensus rule defining a maximum transaction size of 100,000 bytes. + +In general, the more UTXOs that are selected, the longer it takes for the transaction to be verified. Due to the quadratic hashing problem, some miners use the `mempooltxinputlimit` option to reject transactions with a large number of UTXO inputs. + +Currently, as of November 2017, there is no commonly agreed upon limit, but as a rule of thumb (a form of emergent consensus) if a transaction has less than 100 UTXO inputs, the transaction will be mined promptly by the majority of mining pools, but if it has many more UTXO inputs, such as 500, it might take several days to be mined by a miner who has higher or no limits. + +### Anatomy of a z_shieldcoinbase transaction + +The transaction created is a shielded transaction. It consists of a single joinsplit, which consumes coinbase UTXOs as input, and deposits value at a shielded address, minus any fee. + +The number of coinbase UTXOs is determined by a user configured limit. + +If no limit is set (in the case when limit parameter and `mempooltxinputlimit` options are set to zero) the behaviour of z_shieldcoinbase is to consume as many UTXOs as possible, with `z_shieldcoinbase` constructing a transaction up to the size limit of 100,000 bytes. + +As a result, the maximum number of inputs that can be selected is: + +- P2PKH coinbase UTXOs ~ 662 +- 2-of-3 multisig P2SH coinbase UTXOs ~ 244. + +Here is an example of using `z_shieldcoinbase` on testnet to shield multi-sig coinbase UTXOs. + +- Block 141042 is almost ~2 MB in size (the maximum size for a block) and contains 1 coinbase reward transaction and 20 transactions, each indivually created by a call to z_shieldcoinbase. + - https://explorer.testnet.z.cash/block/0050552a78e97c89f666713c8448d49ad1d7263274422272696187dedf6c0d03 +- Drilling down into a transaction, you can see there is one joinsplit, with 244 inputs (vin) and 0 outputs (vout). + - https://explorer.testnet.z.cash/tx/cf4f3da2e434f68b6e361303403344e22a9ff9a8fda9abc180d9520d0ca6527d + + diff --git a/doc/tor.md b/doc/tor.md index ce717515a..e02c2bb00 100644 --- a/doc/tor.md +++ b/doc/tor.md @@ -1,18 +1,18 @@ -*** Warning: Do not assume Tor support does the correct thing in Zcash; better Tor support is a future feature goal. *** +*** Warning: Do not assume Tor support does the correct thing in Komodo; better Tor support is a future feature goal. *** TOR SUPPORT IN ZCASH ==================== -It is possible to run Zcash as a Tor hidden service, and connect to such services. +It is possible to run Komodo as a Tor hidden service, and connect to such services. The following directions assume you have a Tor proxy running on port 9050. Many distributions default to having a SOCKS proxy listening on port 9050, but others may not. In particular, the Tor Browser Bundle defaults to listening on port 9150. See [Tor Project FAQ:TBBSocksPort](https://www.torproject.org/docs/faq.html.en#TBBSocksPort) for how to properly configure Tor. -1. Run Zcash behind a Tor proxy +1. Run Komodo behind a Tor proxy ------------------------------- -The first step is running Zcash behind a Tor proxy. This will already make all +The first step is running Komodo behind a Tor proxy. This will already make all outgoing connections be anonymized, but more is possible. -proxy=ip:port Set the proxy server. If SOCKS5 is selected (default), this proxy @@ -33,10 +33,10 @@ outgoing connections be anonymized, but more is possible. In a typical situation, this suffices to run behind a Tor proxy: - ./zcashd -proxy=127.0.0.1:9050 + ./komodod -proxy=127.0.0.1:9050 -2. Run a Zcash hidden server +2. Run a Komodo hidden server ---------------------------- If you configure your Tor system accordingly, it is possible to make your node also @@ -44,13 +44,13 @@ reachable from the Tor network. Add these lines to your /etc/tor/torrc (or equiv config file): HiddenServiceDir /var/lib/tor/zcash-service/ - HiddenServicePort 8233 127.0.0.1:8233 - HiddenServicePort 18233 127.0.0.1:18233 + HiddenServicePort 7771 127.0.0.1:7771 + HiddenServicePort 17771 127.0.0.1:17771 The directory can be different of course, but (both) port numbers should be equal to -your zcashd's P2P listen port (8233 by default). +your komodod's P2P listen port (7771 by default). - -externalip=X You can tell Zcash about its publicly reachable address using + -externalip=X You can tell Komodo about its publicly reachable address using this option, and this can be a .onion address. Given the above configuration, you can find your onion address in /var/lib/tor/zcash-service/hostname. Onion addresses are given @@ -70,25 +70,25 @@ your zcashd's P2P listen port (8233 by default). In a typical situation, where you're only reachable via Tor, this should suffice: - ./zcashd -proxy=127.0.0.1:9050 -externalip=zctestseie6wxgio.onion -listen + ./komodod -proxy=127.0.0.1:9050 -externalip=zctestseie6wxgio.onion -listen (obviously, replace the Onion address with your own). It should be noted that you still listen on all devices and another node could establish a clearnet connection, when knowing your address. To mitigate this, additionally bind the address of your Tor proxy: - ./bitcoind ... -bind=127.0.0.1 + ./zcashd ... -bind=127.0.0.1 If you don't care too much about hiding your node, and want to be reachable on IPv4 as well, use `discover` instead: - ./zcashd ... -discover + ./komodod ... -discover -and open port 8233 on your firewall (or use -upnp). +and open port 7771 on your firewall (or use -upnp). If you only want to use Tor to reach onion addresses, but not use it as a proxy for normal IPv4/IPv6 communication, use: - ./zcashd -onion=127.0.0.1:9050 -externalip=zctestseie6wxgio.onion -discover + ./komodod -onion=127.0.0.1:9050 -externalip=zctestseie6wxgio.onion -discover 3. Automatically listen on Tor @@ -96,47 +96,47 @@ for normal IPv4/IPv6 communication, use: Starting with Tor version 0.2.7.1 it is possible, through Tor's control socket API, to create and destroy 'ephemeral' hidden services programmatically. -Zcash has been updated to make use of this. +Komodo has been updated to make use of this. This means that if Tor is running (and proper authentication has been configured), -Zcash automatically creates a hidden service to listen on. Zcash will also use Tor +Komodo automatically creates a hidden service to listen on. Komodo will also use Tor automatically to connect to other .onion nodes if the control socket can be successfully opened. This will positively affect the number of available .onion nodes and their usage. -This new feature is enabled by default if Zcash is listening (`-listen`), and +This new feature is enabled by default if Komodo is listening (`-listen`), and requires a Tor connection to work. It can be explicitly disabled with `-listenonion=0` and, if not disabled, configured using the `-torcontrol` and `-torpassword` settings. To show verbose debugging information, pass `-debug=tor`. Connecting to Tor's control socket API requires one of two authentication methods to be -configured. For cookie authentication the user running zcashd must have write access +configured. For cookie authentication the user running komodod must have write access to the `CookieAuthFile` specified in Tor configuration. In some cases this is preconfigured and the creation of a hidden service is automatic. If permission problems are seen with `-debug=tor` they can be resolved by adding both the user running tor and -the user running zcashd to the same group and setting permissions appropriately. On -Debian-based systems the user running zcashd can be added to the debian-tor group, +the user running komodod to the same group and setting permissions appropriately. On +Debian-based systems the user running komodod can be added to the debian-tor group, which has the appropriate permissions. An alternative authentication method is the use of the `-torpassword` flag and a `hash-password` which can be enabled and specified in Tor configuration. -4. Connect to a Zcash hidden server +4. Connect to a Komodo hidden server ----------------------------------- To test your set-up, you might want to try connecting via Tor on a different computer to just a -a single Zcash hidden server. Launch zcashd as follows: +a single Komodo hidden server. Launch komodod as follows: - ./zcashd -onion=127.0.0.1:9050 -connect=zctestseie6wxgio.onion + ./komodod -onion=127.0.0.1:9050 -connect=zctestseie6wxgio.onion -Now use zcash-cli to verify there is only a single peer connection. +Now use komodo-cli to verify there is only a single peer connection. - zcash-cli getpeerinfo + komodo-cli getpeerinfo [ { "id" : 1, - "addr" : "zctestseie6wxgio.onion:18233", + "addr" : "zctestseie6wxgio.onion:17770", ... "version" : 170002, "subver" : "/MagicBean:1.0.0/", @@ -146,4 +146,4 @@ Now use zcash-cli to verify there is only a single peer connection. To connect to multiple Tor nodes, use: - ./zcashd -onion=127.0.0.1:9050 -addnode=zctestseie6wxgio.onion -dnsseed=0 -onlynet=onion + ./komodod -onion=127.0.0.1:9050 -addnode=zctestseie6wxgio.onion -dnsseed=0 -onlynet=onion diff --git a/doc/wallet-backup.md b/doc/wallet-backup.md new file mode 100644 index 000000000..02454db72 --- /dev/null +++ b/doc/wallet-backup.md @@ -0,0 +1,91 @@ +# Wallet Backup Instructions + +## Overview + +Backing up your Zcash private keys is the best way to be proactive about preventing loss of access to your ZEC. + +Problems resulting from bugs in the code, user error, device failure, etc. may lead to losing access to your wallet (and as a result, the private keys of addresses which are required to spend from them). + +No matter what the cause of a corrupted or lost wallet could be, we highly recommend all users backup on a regular basis. Anytime a new address in the wallet is generated, we recommending making a new backup so all private keys for addresses in your wallet are safe. + +Note that a backup is a duplicate of data needed to spend ZEC so where you keep your backup(s) is another important consideration. You should not store backups where they would be equally or increasingly susceptible to loss or theft. + +## Instructions for backing up your wallet and/or private keys + +These instructions are specific for the officially supported Zcash Linux client. For backing up with third-party wallets, please consult with user guides or support channels provided for those services. + +There are multiple ways to make sure you have at least one other copy of the private keys needed to spend your ZEC and view your shielded ZEC. + +For all methods, you will need to include an export directory setting in your config file (`zcash.conf` located in the data directory which is `~/.zcash/` unless it's been overridden with `datadir=` setting): + +`exportdir=path/to/chosen/export/directory` + +You may chose any directory within the home directory as the location for export & backup files. If the directory doesn't exist, it will be created. + +Note that zcashd will need to be stopped and restarted for edits in the config file to take effect. + +### Using `backupwallet` + +To create a backup of your wallet, use: + +`zcash-cli backupwallet `. + +The backup will be an exact copy of the current state of your wallet.dat file stored in the export directory you specified in the config file. The file path will also be returned. + +If you generate a new Zcash address, it will not be reflected in the backup file. + +If your original `wallet.dat` file becomes inaccessible for whatever reason, you can use your backup by copying it into your data directory and renaming the copy to `wallet.dat`. + +### Using `z_exportwallet` & `z_importwallet` + +If you prefer to have an export of your private keys in human readable format, you can use: + +`zcash-cli z_exportwallet ` + +This will generate a file in the export directory listing all transparent and shielded private keys with their associated public addresses. The file path will be returned in the command line. + +To import keys into a wallet which were previously exported to a file, use: + +`zcash-cli z_importwallet ` + +### Using `z_exportkey`, `z_importkey`, `dumpprivkey` & `importprivkey` + +If you prefer to export a single private key for a shielded address, you can use: + +`zcash-cli z_exportkey ` + +This will return the private key and will not create a new file. + +For exporting a single private key for a transparent address, you can use the command inherited from Bitcoin: + +`zcash-cli dumpprivkey ` + +This will return the private key and will not create a new file. + +To import a private key for a shielded address, use: + +`zcash-cli z_importkey ` + +This will add the key to your wallet and rescan the wallet for associated transactions if it is not already part of the wallet. + +The rescanning process can take a few minutes for a new private key. To skip it, instead use: + +`zcash-cli z_importkey no` + +For other instructions on fine-tuning the wallet rescan, see the command's help documentation: + +`zcash-cli help z_importkey` + +To import a private key for a transparent address, use: + +`zcash-cli importprivkey ` + +This has the same functionality as `z_importkey` but works with transparent addresses. + +See the command's help documentation for instructions on fine-tuning the wallet rescan: + +`zcash-cli help importprivkey` + +### Using `dumpwallet` + +This command inherited from Bitcoin is deprecated. It will export private keys in a similar fashion as `z_exportwallet` but only for transparent addresses. \ No newline at end of file diff --git a/doc/zmq.md b/doc/zmq.md index e23c0937c..c13a31d2c 100644 --- a/doc/zmq.md +++ b/doc/zmq.md @@ -2,11 +2,11 @@ [ZeroMQ](http://zeromq.org/) is a lightweight wrapper around TCP connections, inter-process communication, and shared-memory, -providing various message-oriented semantics such as publish/subcribe, +providing various message-oriented semantics such as publish/subscribe, request/reply, and push/pull. The Zcash daemon can be configured to act as a trusted "border -router", implementing the zcash wire protocol and relay, making +router", implementing the Zcash wire 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 @@ -46,7 +46,7 @@ operation. By default, the ZeroMQ feature is automatically compiled in if the necessary prerequisites are found. To disable, use --disable-zmq -during the *configure* step of building zcashd: +during the *configure* step of building komodod: $ ./configure --disable-zmq (other options) @@ -67,8 +67,8 @@ address. The same address can be used in more than one notification. For instance: - $ zcashd -zmqpubhashtx=tcp://127.0.0.1:28332 \ - -zmqpubrawtx=ipc:///tmp/zcashd.tx.raw + $ komodod -zmqpubhashtx=tcp://127.0.0.1:28332 \ + -zmqpubrawtx=ipc:///tmp/komodod.tx.raw Each PUB notification has a topic and body, where the header corresponds to the notification type. For instance, for the @@ -88,9 +88,9 @@ arriving. Please see `contrib/zmq/zmq_sub.py` for a working example. ## Remarks -From the perspective of zcashd, the ZeroMQ socket is write-only; PUB +From the perspective of komodod, the ZeroMQ socket is write-only; PUB sockets don't even have a read function. Thus, there is no state -introduced into zcashd directly. Furthermore, no information is +introduced into komodod directly. Furthermore, no information is broadcast that wasn't already received from the public P2P network. No authentication or authorization is done on connecting clients; it @@ -102,6 +102,6 @@ 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. There are several possibilities that ZMQ notification can get lost -during transmission depending on the communication type your are +during transmission depending on the communication type you are using. Zcashd appends an up-counting sequence number to each notification which allows listeners to detect lost notifications. diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index 08ff3fe7a..0ed693951 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -11,10 +11,15 @@ export BITCOIND=${REAL_BITCOIND} #Run the tests testScripts=( + 'paymentdisclosure.py' 'prioritisetransaction.py' 'wallet_treestate.py' + 'wallet_anchorfork.py' 'wallet_protectcoinbase.py' + 'wallet_shieldcoinbase.py' + 'wallet_mergetoaddress.py' 'wallet.py' + 'wallet_overwintertx.py' 'wallet_nullifiers.py' 'wallet_1941.py' 'listtransactions.py' @@ -25,7 +30,10 @@ testScripts=( 'rawtransactions.py' 'rest.py' 'mempool_spendcoinbase.py' - 'mempool_coinbase_spends.py' + 'mempool_reorg.py' + 'mempool_tx_input_limit.py' + 'mempool_nu_activation.py' + 'mempool_tx_expiry.py' 'httpbasics.py' 'zapwallettxes.py' 'proxy_test.py' @@ -33,17 +41,21 @@ testScripts=( 'fundrawtransaction.py' 'signrawtransactions.py' 'walletbackup.py' + 'key_import_export.py' 'nodehandling.py' 'reindex.py' 'decodescript.py' 'disablewallet.py' 'zcjoinsplit.py' 'zcjoinsplitdoublespend.py' + 'zkey_import_export.py' + 'reorg_limit.py' 'getblocktemplate.py' + 'bip65-cltv-p2p.py' + 'bipdersig-p2p.py' + 'overwinter_peer_management.py' ); testScriptsExt=( - 'bipdersig-p2p.py' - 'bipdersig.py' 'getblocktemplate_longpoll.py' 'getblocktemplate_proposals.py' 'pruning.py' @@ -65,6 +77,10 @@ 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} @@ -78,7 +94,7 @@ function runTestScript echo -e "=== Running testscript ${testName} ===" - if eval "$@" | sed 's/^/ /' + if eval "$@" then successCount=$(expr $successCount + 1) echo "--- Success: ${testName} ---" diff --git a/qa/pull-tester/tests-config.sh.in b/qa/pull-tester/tests-config.sh.in index 1cb9ee06b..cc76e8ad8 100755 --- a/qa/pull-tester/tests-config.sh.in +++ b/qa/pull-tester/tests-config.sh.in @@ -11,6 +11,7 @@ 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/zcashd${EXEEXT}" REAL_BITCOINCLI="$BUILDDIR/src/zcash-cli${EXEEXT}" diff --git a/qa/rpc-tests/README.md b/qa/rpc-tests/README.md index cfda8fe91..a0229b56d 100644 --- a/qa/rpc-tests/README.md +++ b/qa/rpc-tests/README.md @@ -1,13 +1,8 @@ Regression tests of RPC interface ================================= -### [python-bitcoinrpc](https://github.com/jgarzik/python-bitcoinrpc) -Git subtree of [https://github.com/jgarzik/python-bitcoinrpc](https://github.com/jgarzik/python-bitcoinrpc). -Changes to python-bitcoinrpc should be made upstream, and then -pulled here using git subtree. - ### [test_framework/test_framework.py](test_framework/test_framework.py) -Base class for new regression tests. +Base class for RPC regression tests. ### [test_framework/util.py](test_framework/util.py) Generally useful functions. @@ -35,8 +30,8 @@ If you set the environment variable `PYTHON_DEBUG=1` you will get some debug out A 200-block -regtest blockchain and wallets for four nodes is created the first time a regression test is run and -is stored in the cache/ directory. Each node has 25 mature -blocks (25*50=1250 BTC) in its wallet. +is stored in the cache/ directory. Each node has the miner +subsidy from 25 mature blocks (25*10=250 ZEC) in its wallet. After the first run, the cache/ blockchain and wallets are copied into a temporary directory and used as the initial @@ -47,5 +42,5 @@ to recover with: ```bash rm -rf cache -killall bitcoind +killall zcashd ``` diff --git a/qa/rpc-tests/bip65-cltv-p2p.py b/qa/rpc-tests/bip65-cltv-p2p.py index 1f8548c21..cfd2df01e 100755 --- a/qa/rpc-tests/bip65-cltv-p2p.py +++ b/qa/rpc-tests/bip65-cltv-p2p.py @@ -5,35 +5,23 @@ # from test_framework.test_framework import ComparisonTestFramework -from test_framework.util import * +from test_framework.util import start_nodes from test_framework.mininode import CTransaction, NetworkThread from test_framework.blocktools import create_coinbase, create_block from test_framework.comptool import TestInstance, TestManager from test_framework.script import CScript, OP_1NEGATE, OP_NOP2, OP_DROP -from binascii import hexlify, unhexlify +from binascii import unhexlify import cStringIO -import time -def cltv_invalidate(tx): - '''Modify the signature in vin 0 of the tx to fail CLTV - - Prepends -1 CLTV DROP in the scriptSig itself. - ''' - tx.vin[0].scriptSig = CScript([OP_1NEGATE, OP_NOP2, OP_DROP] + - list(CScript(tx.vin[0].scriptSig))) ''' -This test is meant to exercise BIP65 (CHECKLOCKTIMEVERIFY) +This test is meant to exercise BIP65 (CHECKLOCKTIMEVERIFY). Connect to a single node. -Mine 2 (version 3) blocks (save the coinbases for later). -Generate 98 more version 3 blocks, verify the node accepts. -Mine 749 version 4 blocks, verify the node accepts. -Check that the new CLTV rules are not enforced on the 750th version 4 block. -Check that the new CLTV rules are enforced on the 751st version 4 block. -Mine 199 new version blocks. -Mine 1 old-version block. -Mine 1 new version block. -Mine 1 old version block, see that the node rejects. +Mine a coinbase block, and then ... +Mine 1 version 4 block. +Check that the CLTV rules are enforced. + +TODO: factor out common code from {bipdersig-p2p,bip65-cltv-p2p}.py. ''' class BIP65Test(ComparisonTestFramework): @@ -42,10 +30,10 @@ class BIP65Test(ComparisonTestFramework): self.num_nodes = 1 def setup_network(self): - # Must set the blockversion for this test self.nodes = start_nodes(1, self.options.tmpdir, - extra_args=[['-debug', '-whitelist=127.0.0.1', '-blockversion=3']], + extra_args=[['-debug', '-whitelist=127.0.0.1']], binary=[self.options.testbinary]) + self.is_network_split = False def run_test(self): test = TestManager(self, self.options.tmpdir) @@ -64,112 +52,45 @@ class BIP65Test(ComparisonTestFramework): tx.deserialize(f) return tx - def get_tests(self): + def invalidate_transaction(self, tx): + ''' + Modify the signature in vin 0 of the tx to fail CLTV + + Prepends -1 CLTV DROP in the scriptSig itself. + ''' + tx.vin[0].scriptSig = CScript([OP_1NEGATE, OP_NOP2, OP_DROP] + + list(CScript(tx.vin[0].scriptSig))) - self.coinbase_blocks = self.nodes[0].generate(2) + def get_tests(self): + self.coinbase_blocks = self.nodes[0].generate(1) + self.nodes[0].generate(100) self.tip = int ("0x" + self.nodes[0].getbestblockhash() + "L", 0) self.nodeaddress = self.nodes[0].getnewaddress() - self.last_block_time = time.time() - - ''' 98 more version 3 blocks ''' - test_blocks = [] - for i in xrange(98): - block = create_block(self.tip, create_coinbase(2), self.last_block_time + 1) - block.nVersion = 3 - block.rehash() - block.solve() - test_blocks.append([block, True]) - self.last_block_time += 1 - self.tip = block.sha256 - yield TestInstance(test_blocks, sync_every_block=False) - ''' Mine 749 version 4 blocks ''' - test_blocks = [] - for i in xrange(749): - block = create_block(self.tip, create_coinbase(2), self.last_block_time + 1) + '''Check that the rules are enforced.''' + for valid in (True, False): + spendtx = self.create_transaction(self.nodes[0], + self.coinbase_blocks[0], + self.nodeaddress, 1.0) + if not valid: + self.invalidate_transaction(spendtx) + spendtx.rehash() + + gbt = self.nodes[0].getblocktemplate() + self.block_time = gbt["mintime"] + 1 + self.block_bits = int("0x" + gbt["bits"], 0) + + block = create_block(self.tip, create_coinbase(101), + self.block_time, self.block_bits) block.nVersion = 4 + block.vtx.append(spendtx) + block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() - test_blocks.append([block, True]) - self.last_block_time += 1 + self.block_time += 1 self.tip = block.sha256 - yield TestInstance(test_blocks, sync_every_block=False) + yield TestInstance([[block, valid]]) - ''' - Check that the new CLTV rules are not enforced in the 750th - version 3 block. - ''' - spendtx = self.create_transaction(self.nodes[0], - self.coinbase_blocks[0], self.nodeaddress, 1.0) - cltv_invalidate(spendtx) - spendtx.rehash() - - block = create_block(self.tip, create_coinbase(2), self.last_block_time + 1) - block.nVersion = 4 - block.vtx.append(spendtx) - block.hashMerkleRoot = block.calc_merkle_root() - block.rehash() - block.solve() - - self.last_block_time += 1 - self.tip = block.sha256 - yield TestInstance([[block, True]]) - - ''' - Check that the new CLTV rules are enforced in the 751st version 4 - block. - ''' - spendtx = self.create_transaction(self.nodes[0], - self.coinbase_blocks[1], self.nodeaddress, 1.0) - cltv_invalidate(spendtx) - spendtx.rehash() - - block = create_block(self.tip, create_coinbase(1), self.last_block_time + 1) - block.nVersion = 4 - block.vtx.append(spendtx) - block.hashMerkleRoot = block.calc_merkle_root() - block.rehash() - block.solve() - self.last_block_time += 1 - yield TestInstance([[block, False]]) - - ''' Mine 199 new version blocks on last valid tip ''' - test_blocks = [] - for i in xrange(199): - block = create_block(self.tip, create_coinbase(1), self.last_block_time + 1) - block.nVersion = 4 - block.rehash() - block.solve() - test_blocks.append([block, True]) - self.last_block_time += 1 - self.tip = block.sha256 - yield TestInstance(test_blocks, sync_every_block=False) - - ''' Mine 1 old version block ''' - block = create_block(self.tip, create_coinbase(1), self.last_block_time + 1) - block.nVersion = 3 - block.rehash() - block.solve() - self.last_block_time += 1 - self.tip = block.sha256 - yield TestInstance([[block, True]]) - - ''' Mine 1 new version block ''' - block = create_block(self.tip, create_coinbase(1), self.last_block_time + 1) - block.nVersion = 4 - block.rehash() - block.solve() - self.last_block_time += 1 - self.tip = block.sha256 - yield TestInstance([[block, True]]) - - ''' Mine 1 old version block, should be invalid ''' - block = create_block(self.tip, create_coinbase(1), self.last_block_time + 1) - block.nVersion = 3 - block.rehash() - block.solve() - self.last_block_time += 1 - yield TestInstance([[block, False]]) if __name__ == '__main__': BIP65Test().main() diff --git a/qa/rpc-tests/bip65-cltv.py b/qa/rpc-tests/bip65-cltv.py deleted file mode 100755 index e60395dce..000000000 --- a/qa/rpc-tests/bip65-cltv.py +++ /dev/null @@ -1,89 +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 http://www.opensource.org/licenses/mit-license.php. - -# -# Test the CHECKLOCKTIMEVERIFY (BIP65) soft-fork logic -# - -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -import os -import shutil - -class BIP65Test(BitcoinTestFramework): - - def setup_network(self): - self.nodes = [] - self.nodes.append(start_node(0, self.options.tmpdir, [])) - self.nodes.append(start_node(1, self.options.tmpdir, ["-blockversion=3"])) - self.nodes.append(start_node(2, self.options.tmpdir, ["-blockversion=4"])) - connect_nodes(self.nodes[1], 0) - connect_nodes(self.nodes[2], 0) - self.is_network_split = False - self.sync_all() - - def run_test(self): - cnt = self.nodes[0].getblockcount() - - # Mine some old-version blocks - self.nodes[1].generate(100) - self.sync_all() - if (self.nodes[0].getblockcount() != cnt + 100): - raise AssertionError("Failed to mine 100 version=3 blocks") - - # Mine 750 new-version blocks - for i in xrange(15): - self.nodes[2].generate(50) - self.sync_all() - if (self.nodes[0].getblockcount() != cnt + 850): - raise AssertionError("Failed to mine 750 version=4 blocks") - - # TODO: check that new CHECKLOCKTIMEVERIFY rules are not enforced - - # Mine 1 new-version block - self.nodes[2].generate(1) - self.sync_all() - if (self.nodes[0].getblockcount() != cnt + 851): - raise AssertionFailure("Failed to mine a version=4 blocks") - - # TODO: check that new CHECKLOCKTIMEVERIFY rules are enforced - - # Mine 198 new-version blocks - for i in xrange(2): - self.nodes[2].generate(99) - self.sync_all() - if (self.nodes[0].getblockcount() != cnt + 1049): - raise AssertionError("Failed to mine 198 version=4 blocks") - - # Mine 1 old-version block - self.nodes[1].generate(1) - self.sync_all() - if (self.nodes[0].getblockcount() != cnt + 1050): - raise AssertionError("Failed to mine a version=3 block after 949 version=4 blocks") - - # Mine 1 new-version blocks - self.nodes[2].generate(1) - self.sync_all() - if (self.nodes[0].getblockcount() != cnt + 1051): - raise AssertionError("Failed to mine a version=3 block") - - # Mine 1 old-version blocks - try: - self.nodes[1].generate(1) - raise AssertionError("Succeeded to mine a version=3 block after 950 version=4 blocks") - except JSONRPCException: - pass - self.sync_all() - if (self.nodes[0].getblockcount() != cnt + 1051): - raise AssertionError("Accepted a version=3 block after 950 version=4 blocks") - - # Mine 1 new-version blocks - self.nodes[2].generate(1) - self.sync_all() - if (self.nodes[0].getblockcount() != cnt + 1052): - raise AssertionError("Failed to mine a version=4 block") - -if __name__ == '__main__': - BIP65Test().main() diff --git a/qa/rpc-tests/bipdersig-p2p.py b/qa/rpc-tests/bipdersig-p2p.py index 41717377b..f254843f1 100755 --- a/qa/rpc-tests/bipdersig-p2p.py +++ b/qa/rpc-tests/bipdersig-p2p.py @@ -5,55 +5,34 @@ # from test_framework.test_framework import ComparisonTestFramework -from test_framework.util import * +from test_framework.util import start_nodes from test_framework.mininode import CTransaction, NetworkThread from test_framework.blocktools import create_coinbase, create_block from test_framework.comptool import TestInstance, TestManager from test_framework.script import CScript -from binascii import hexlify, unhexlify +from binascii import unhexlify import cStringIO -import time - -# A canonical signature consists of: -# <30> <02> <02> -def unDERify(tx): - ''' - Make the signature in vin 0 of a tx non-DER-compliant, - by adding padding after the S-value. - ''' - scriptSig = CScript(tx.vin[0].scriptSig) - newscript = [] - for i in scriptSig: - if (len(newscript) == 0): - newscript.append(i[0:-1] + '\0' + i[-1]) - else: - newscript.append(i) - tx.vin[0].scriptSig = CScript(newscript) - + + ''' This test is meant to exercise BIP66 (DER SIG). Connect to a single node. -Mine 2 (version 2) blocks (save the coinbases for later). -Generate 98 more version 2 blocks, verify the node accepts. -Mine 749 version 3 blocks, verify the node accepts. -Check that the new DERSIG rules are not enforced on the 750th version 3 block. -Check that the new DERSIG rules are enforced on the 751st version 3 block. -Mine 199 new version blocks. -Mine 1 old-version block. -Mine 1 new version block. -Mine 1 old version block, see that the node rejects. +Mine a coinbase block, and then ... +Mine 1 version 4 block. +Check that the DERSIG rules are enforced. + +TODO: factor out common code from {bipdersig-p2p,bip65-cltv-p2p}.py. ''' - class BIP66Test(ComparisonTestFramework): def __init__(self): self.num_nodes = 1 def setup_network(self): - # Must set the blockversion for this test - self.nodes = start_nodes(1, self.options.tmpdir, - extra_args=[['-debug', '-whitelist=127.0.0.1', '-blockversion=2']], + self.nodes = start_nodes(1, self.options.tmpdir, + extra_args=[['-debug', '-whitelist=127.0.0.1']], binary=[self.options.testbinary]) + self.is_network_split = False def run_test(self): test = TestManager(self, self.options.tmpdir) @@ -72,112 +51,52 @@ class BIP66Test(ComparisonTestFramework): tx.deserialize(f) return tx - def get_tests(self): + def invalidate_transaction(self, tx): + ''' + Make the signature in vin 0 of a tx non-DER-compliant, + by adding padding after the S-value. - self.coinbase_blocks = self.nodes[0].generate(2) + A canonical signature consists of: + <30> <02> <02> + ''' + scriptSig = CScript(tx.vin[0].scriptSig) + newscript = [] + for i in scriptSig: + if (len(newscript) == 0): + newscript.append(i[0:-1] + '\0' + i[-1]) + else: + newscript.append(i) + tx.vin[0].scriptSig = CScript(newscript) + + def get_tests(self): + self.coinbase_blocks = self.nodes[0].generate(1) + self.nodes[0].generate(100) self.tip = int ("0x" + self.nodes[0].getbestblockhash() + "L", 0) self.nodeaddress = self.nodes[0].getnewaddress() - self.last_block_time = time.time() - ''' 98 more version 2 blocks ''' - test_blocks = [] - for i in xrange(98): - block = create_block(self.tip, create_coinbase(2), self.last_block_time + 1) - block.nVersion = 2 + '''Check that the rules are enforced.''' + for valid in (True, False): + spendtx = self.create_transaction(self.nodes[0], + self.coinbase_blocks[0], + self.nodeaddress, 1.0) + if not valid: + self.invalidate_transaction(spendtx) + spendtx.rehash() + + gbt = self.nodes[0].getblocktemplate() + self.block_time = gbt["mintime"] + 1 + self.block_bits = int("0x" + gbt["bits"], 0) + + block = create_block(self.tip, create_coinbase(101), + self.block_time, self.block_bits) + block.nVersion = 4 + block.vtx.append(spendtx) + block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() - test_blocks.append([block, True]) - self.last_block_time += 1 self.tip = block.sha256 - yield TestInstance(test_blocks, sync_every_block=False) + yield TestInstance([[block, valid]]) - ''' Mine 749 version 3 blocks ''' - test_blocks = [] - for i in xrange(749): - block = create_block(self.tip, create_coinbase(2), self.last_block_time + 1) - block.nVersion = 3 - block.rehash() - block.solve() - test_blocks.append([block, True]) - self.last_block_time += 1 - self.tip = block.sha256 - yield TestInstance(test_blocks, sync_every_block=False) - - ''' - Check that the new DERSIG rules are not enforced in the 750th - version 3 block. - ''' - spendtx = self.create_transaction(self.nodes[0], - self.coinbase_blocks[0], self.nodeaddress, 1.0) - unDERify(spendtx) - spendtx.rehash() - - block = create_block(self.tip, create_coinbase(2), self.last_block_time + 1) - block.nVersion = 3 - block.vtx.append(spendtx) - block.hashMerkleRoot = block.calc_merkle_root() - block.rehash() - block.solve() - - self.last_block_time += 1 - self.tip = block.sha256 - yield TestInstance([[block, True]]) - - ''' - Check that the new DERSIG rules are enforced in the 751st version 3 - block. - ''' - spendtx = self.create_transaction(self.nodes[0], - self.coinbase_blocks[1], self.nodeaddress, 1.0) - unDERify(spendtx) - spendtx.rehash() - - block = create_block(self.tip, create_coinbase(1), self.last_block_time + 1) - block.nVersion = 3 - block.vtx.append(spendtx) - block.hashMerkleRoot = block.calc_merkle_root() - block.rehash() - block.solve() - self.last_block_time += 1 - yield TestInstance([[block, False]]) - - ''' Mine 199 new version blocks on last valid tip ''' - test_blocks = [] - for i in xrange(199): - block = create_block(self.tip, create_coinbase(1), self.last_block_time + 1) - block.nVersion = 3 - block.rehash() - block.solve() - test_blocks.append([block, True]) - self.last_block_time += 1 - self.tip = block.sha256 - yield TestInstance(test_blocks, sync_every_block=False) - - ''' Mine 1 old version block ''' - block = create_block(self.tip, create_coinbase(1), self.last_block_time + 1) - block.nVersion = 2 - block.rehash() - block.solve() - self.last_block_time += 1 - self.tip = block.sha256 - yield TestInstance([[block, True]]) - - ''' Mine 1 new version block ''' - block = create_block(self.tip, create_coinbase(1), self.last_block_time + 1) - block.nVersion = 3 - block.rehash() - block.solve() - self.last_block_time += 1 - self.tip = block.sha256 - yield TestInstance([[block, True]]) - - ''' Mine 1 old version block, should be invalid ''' - block = create_block(self.tip, create_coinbase(1), self.last_block_time + 1) - block.nVersion = 2 - block.rehash() - block.solve() - self.last_block_time += 1 - yield TestInstance([[block, False]]) if __name__ == '__main__': BIP66Test().main() diff --git a/qa/rpc-tests/bipdersig.py b/qa/rpc-tests/bipdersig.py deleted file mode 100755 index 243f816f6..000000000 --- a/qa/rpc-tests/bipdersig.py +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env python2 -# Copyright (c) 2014 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 the BIP66 changeover logic -# - -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -import os -import shutil - -class BIP66Test(BitcoinTestFramework): - - def setup_network(self): - self.nodes = [] - self.nodes.append(start_node(0, self.options.tmpdir, [])) - self.nodes.append(start_node(1, self.options.tmpdir, ["-blockversion=2"])) - self.nodes.append(start_node(2, self.options.tmpdir, ["-blockversion=3"])) - connect_nodes(self.nodes[1], 0) - connect_nodes(self.nodes[2], 0) - self.is_network_split = False - self.sync_all() - - def run_test(self): - cnt = self.nodes[0].getblockcount() - - # Mine some old-version blocks - self.nodes[1].generate(100) - self.sync_all() - if (self.nodes[0].getblockcount() != cnt + 100): - raise AssertionError("Failed to mine 100 version=2 blocks") - - # Mine 750 new-version blocks - for i in xrange(15): - self.nodes[2].generate(50) - self.sync_all() - if (self.nodes[0].getblockcount() != cnt + 850): - raise AssertionError("Failed to mine 750 version=3 blocks") - - # TODO: check that new DERSIG rules are not enforced - - # Mine 1 new-version block - self.nodes[2].generate(1) - self.sync_all() - if (self.nodes[0].getblockcount() != cnt + 851): - raise AssertionFailure("Failed to mine a version=3 blocks") - - # TODO: check that new DERSIG rules are enforced - - # Mine 198 new-version blocks - for i in xrange(2): - self.nodes[2].generate(99) - self.sync_all() - if (self.nodes[0].getblockcount() != cnt + 1049): - raise AssertionError("Failed to mine 198 version=3 blocks") - - # Mine 1 old-version block - self.nodes[1].generate(1) - self.sync_all() - if (self.nodes[0].getblockcount() != cnt + 1050): - raise AssertionError("Failed to mine a version=2 block after 949 version=3 blocks") - - # Mine 1 new-version blocks - self.nodes[2].generate(1) - self.sync_all() - if (self.nodes[0].getblockcount() != cnt + 1051): - raise AssertionError("Failed to mine a version=3 block") - - # Mine 1 old-version blocks - try: - self.nodes[1].generate(1) - raise AssertionError("Succeeded to mine a version=2 block after 950 version=3 blocks") - except JSONRPCException: - pass - self.sync_all() - if (self.nodes[0].getblockcount() != cnt + 1051): - raise AssertionError("Accepted a version=2 block after 950 version=3 blocks") - - # Mine 1 new-version blocks - self.nodes[2].generate(1) - self.sync_all() - if (self.nodes[0].getblockcount() != cnt + 1052): - raise AssertionError("Failed to mine a version=3 block") - -if __name__ == '__main__': - BIP66Test().main() diff --git a/qa/rpc-tests/decodescript.py b/qa/rpc-tests/decodescript.py index ce3bc94ef..89364a840 100755 --- a/qa/rpc-tests/decodescript.py +++ b/qa/rpc-tests/decodescript.py @@ -4,7 +4,9 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.util import assert_equal, initialize_chain_clean, \ + start_nodes + class DecodeScriptTest(BitcoinTestFramework): """Tests decoding scripts via RPC command "decodescript".""" diff --git a/qa/rpc-tests/disablewallet.py b/qa/rpc-tests/disablewallet.py index 67acdcea1..339c6a8f6 100755 --- a/qa/rpc-tests/disablewallet.py +++ b/qa/rpc-tests/disablewallet.py @@ -8,7 +8,8 @@ # from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.util import initialize_chain_clean, start_nodes + class DisableWalletTest (BitcoinTestFramework): diff --git a/qa/rpc-tests/forknotify.py b/qa/rpc-tests/forknotify.py index 0acef8e30..1be750a64 100755 --- a/qa/rpc-tests/forknotify.py +++ b/qa/rpc-tests/forknotify.py @@ -8,9 +8,9 @@ # from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.util import start_node, connect_nodes + import os -import shutil class ForkNotifyTest(BitcoinTestFramework): @@ -19,7 +19,7 @@ class ForkNotifyTest(BitcoinTestFramework): def setup_network(self): self.nodes = [] self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt") - with open(self.alert_filename, 'w') as f: + with open(self.alert_filename, 'w'): pass # Just open then close to create zero-length file self.nodes.append(start_node(0, self.options.tmpdir, ["-blockversion=2", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""])) diff --git a/qa/rpc-tests/fundrawtransaction.py b/qa/rpc-tests/fundrawtransaction.py index 1d15fd132..42896be4f 100755 --- a/qa/rpc-tests/fundrawtransaction.py +++ b/qa/rpc-tests/fundrawtransaction.py @@ -4,9 +4,12 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -from pprint import pprint -from time import sleep +from test_framework.authproxy import JSONRPCException +from test_framework.util import assert_equal, assert_greater_than, \ + initialize_chain_clean, start_nodes, connect_nodes_bi, stop_nodes, \ + wait_bitcoinds + +from decimal import Decimal # Create one-input, one-output, no-fee transaction: class RawTransactionsTest(BitcoinTestFramework): diff --git a/qa/rpc-tests/getblocktemplate.py b/qa/rpc-tests/getblocktemplate.py index c9777c0c7..82082afa9 100755 --- a/qa/rpc-tests/getblocktemplate.py +++ b/qa/rpc-tests/getblocktemplate.py @@ -4,7 +4,8 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.util import initialize_chain_clean, start_nodes, \ + connect_nodes_bi class GetBlockTemplateTest(BitcoinTestFramework): diff --git a/qa/rpc-tests/getblocktemplate_longpoll.py b/qa/rpc-tests/getblocktemplate_longpoll.py index aab456242..37a40384b 100755 --- a/qa/rpc-tests/getblocktemplate_longpoll.py +++ b/qa/rpc-tests/getblocktemplate_longpoll.py @@ -4,8 +4,10 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.authproxy import AuthServiceProxy +from test_framework.util import random_transaction +from decimal import Decimal def check_array_result(object_array, to_match, expected): """ diff --git a/qa/rpc-tests/getblocktemplate_proposals.py b/qa/rpc-tests/getblocktemplate_proposals.py index 0c9e7e4cc..16b2e9b94 100755 --- a/qa/rpc-tests/getblocktemplate_proposals.py +++ b/qa/rpc-tests/getblocktemplate_proposals.py @@ -4,7 +4,7 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.authproxy import JSONRPCException from binascii import a2b_hex, b2a_hex from hashlib import sha256 diff --git a/qa/rpc-tests/hardforkdetection.py b/qa/rpc-tests/hardforkdetection.py index d399dc964..dbfb5cd34 100755 --- a/qa/rpc-tests/hardforkdetection.py +++ b/qa/rpc-tests/hardforkdetection.py @@ -5,9 +5,10 @@ # from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.authproxy import JSONRPCException +from test_framework.util import assert_equal, start_node + import os -import shutil class HardForkDetectionTest(BitcoinTestFramework): @@ -16,7 +17,7 @@ class HardForkDetectionTest(BitcoinTestFramework): def setup_network(self): self.nodes = [] self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt") - with open(self.alert_filename, 'w') as f: + with open(self.alert_filename, 'w'): pass # Just open then close to create zero-length file self.nodes.append(start_node(0, self.options.tmpdir, ["-blockversion=2", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""])) diff --git a/qa/rpc-tests/httpbasics.py b/qa/rpc-tests/httpbasics.py index b66533543..b1a4623bd 100755 --- a/qa/rpc-tests/httpbasics.py +++ b/qa/rpc-tests/httpbasics.py @@ -8,7 +8,8 @@ # from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.util import assert_equal, start_nodes + import base64 try: @@ -36,45 +37,45 @@ class HTTPBasicsTest (BitcoinTestFramework): conn = httplib.HTTPConnection(url.hostname, url.port) conn.connect() conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) - out1 = conn.getresponse().read(); + out1 = conn.getresponse().read() assert_equal('"error":null' in out1, True) - assert_equal(conn.sock!=None, True) #according to http/1.1 connection must still be open! + assert_equal(conn.sock!=None, True) # according to http/1.1 connection must still be open! - #send 2nd request without closing connection + # send 2nd request without closing connection conn.request('POST', '/', '{"method": "getchaintips"}', headers) - out2 = conn.getresponse().read(); - assert_equal('"error":null' in out1, True) #must also response with a correct json-rpc message - assert_equal(conn.sock!=None, True) #according to http/1.1 connection must still be open! + out2 = conn.getresponse().read() + assert_equal('"error":null' in out2, True) # must also response with a correct json-rpc message + assert_equal(conn.sock!=None, True) # according to http/1.1 connection must still be open! conn.close() - #same should be if we add keep-alive because this should be the std. behaviour + # same should be if we add keep-alive because this should be the std. behaviour headers = {"Authorization": "Basic " + base64.b64encode(authpair), "Connection": "keep-alive"} conn = httplib.HTTPConnection(url.hostname, url.port) conn.connect() conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) - out1 = conn.getresponse().read(); + out1 = conn.getresponse().read() assert_equal('"error":null' in out1, True) - assert_equal(conn.sock!=None, True) #according to http/1.1 connection must still be open! + assert_equal(conn.sock!=None, True) # according to http/1.1 connection must still be open! - #send 2nd request without closing connection + # send 2nd request without closing connection conn.request('POST', '/', '{"method": "getchaintips"}', headers) - out2 = conn.getresponse().read(); - assert_equal('"error":null' in out1, True) #must also response with a correct json-rpc message - assert_equal(conn.sock!=None, True) #according to http/1.1 connection must still be open! + out2 = conn.getresponse().read() + assert_equal('"error":null' in out2, True) # must also response with a correct json-rpc message + assert_equal(conn.sock!=None, True) # according to http/1.1 connection must still be open! conn.close() - #now do the same with "Connection: close" + # now do the same with "Connection: close" headers = {"Authorization": "Basic " + base64.b64encode(authpair), "Connection":"close"} conn = httplib.HTTPConnection(url.hostname, url.port) conn.connect() conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) - out1 = conn.getresponse().read(); + out1 = conn.getresponse().read() assert_equal('"error":null' in out1, True) - assert_equal(conn.sock!=None, False) #now the connection must be closed after the response + assert_equal(conn.sock!=None, False) # now the connection must be closed after the response - #node1 (2nd node) is running with disabled keep-alive option + # node1 (2nd node) is running with disabled keep-alive option urlNode1 = urlparse.urlparse(self.nodes[1].url) authpair = urlNode1.username + ':' + urlNode1.password headers = {"Authorization": "Basic " + base64.b64encode(authpair)} @@ -82,10 +83,10 @@ class HTTPBasicsTest (BitcoinTestFramework): conn = httplib.HTTPConnection(urlNode1.hostname, urlNode1.port) conn.connect() conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) - out1 = conn.getresponse().read(); + out1 = conn.getresponse().read() assert_equal('"error":null' in out1, True) - #node2 (third node) is running with standard keep-alive parameters which means keep-alive is on + # node2 (third node) is running with standard keep-alive parameters which means keep-alive is on urlNode2 = urlparse.urlparse(self.nodes[2].url) authpair = urlNode2.username + ':' + urlNode2.password headers = {"Authorization": "Basic " + base64.b64encode(authpair)} @@ -93,9 +94,9 @@ class HTTPBasicsTest (BitcoinTestFramework): conn = httplib.HTTPConnection(urlNode2.hostname, urlNode2.port) conn.connect() conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) - out1 = conn.getresponse().read(); + out1 = conn.getresponse().read() assert_equal('"error":null' in out1, True) - assert_equal(conn.sock!=None, True) #connection must be closed because bitcoind should use keep-alive by default + assert_equal(conn.sock!=None, True) # connection must be closed because bitcoind should use keep-alive by default if __name__ == '__main__': - HTTPBasicsTest ().main () + HTTPBasicsTest().main() diff --git a/qa/rpc-tests/invalidateblock.py b/qa/rpc-tests/invalidateblock.py index 2b9c8154e..5cbd1ea98 100755 --- a/qa/rpc-tests/invalidateblock.py +++ b/qa/rpc-tests/invalidateblock.py @@ -8,22 +8,23 @@ # from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.util import initialize_chain_clean, start_node, \ + connect_nodes_bi, sync_blocks + +import time class InvalidateTest(BitcoinTestFramework): - - def setup_chain(self): print("Initializing test directory "+self.options.tmpdir) initialize_chain_clean(self.options.tmpdir, 3) - + def setup_network(self): self.nodes = [] - self.is_network_split = False + self.is_network_split = False self.nodes.append(start_node(0, self.options.tmpdir, ["-debug"])) self.nodes.append(start_node(1, self.options.tmpdir, ["-debug"])) self.nodes.append(start_node(2, self.options.tmpdir, ["-debug"])) - + def run_test(self): print "Make sure we repopulate setBlockIndexCandidates after InvalidateBlock:" print "Mine 4 blocks on Node 0" diff --git a/qa/rpc-tests/invalidblockrequest.py b/qa/rpc-tests/invalidblockrequest.py index 272cc08f9..05b33d772 100755 --- a/qa/rpc-tests/invalidblockrequest.py +++ b/qa/rpc-tests/invalidblockrequest.py @@ -5,11 +5,11 @@ # from test_framework.test_framework import ComparisonTestFramework -from test_framework.util import * +from test_framework.util import assert_equal from test_framework.comptool import TestManager, TestInstance -from test_framework.mininode import * -from test_framework.blocktools import * -import logging +from test_framework.mininode import NetworkThread +from test_framework.blocktools import create_block, create_coinbase, create_transaction + import copy import time @@ -25,7 +25,7 @@ re-requested. # Use the ComparisonTestFramework with 1 node: only use --testbinary. class InvalidBlockRequestTest(ComparisonTestFramework): - ''' Can either run this test as 1 node with expected answers, or two and compare them. + ''' Can either run this test as 1 node with expected answers, or two and compare them. Change the "outcome" variable from each TestInstance object to only do the comparison. ''' def __init__(self): self.num_nodes = 1 diff --git a/qa/rpc-tests/key_import_export.py b/qa/rpc-tests/key_import_export.py new file mode 100755 index 000000000..87b2daa2b --- /dev/null +++ b/qa/rpc-tests/key_import_export.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python2 +# Copyright (c) 2017 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +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 + +import logging + +logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO) + + +class KeyImportExportTest (BitcoinTestFramework): + + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 4) + + def setup_network(self, split=False): + self.nodes = start_nodes(4, self.options.tmpdir ) + connect_nodes_bi(self.nodes,0,1) + connect_nodes_bi(self.nodes,1,2) + connect_nodes_bi(self.nodes,0,2) + connect_nodes_bi(self.nodes,0,3) + self.is_network_split=False + self.sync_all() + + def run_test(self): + [alice, bob, charlie, miner] = self.nodes + + def alice_to_bob(amount): + alice.sendtoaddress(addr, Decimal(amount)) + self.sync_all() + miner.generate(1) + self.sync_all() + + def verify_utxos(node, amounts): + utxos = node.listunspent(1, 10**9, [addr]) + + def cmp_confirmations_high_to_low(a, b): + return cmp(b["confirmations"], a["confirmations"]) + + utxos.sort(cmp_confirmations_high_to_low) + + try: + assert_equal(amounts, [utxo["amount"] for utxo in utxos]) + except AssertionError: + logging.error( + 'Expected amounts: %r; utxos: %r', + amounts, utxos) + raise + + # Seed Alice with some funds + alice.generate(10) + self.sync_all() + miner.generate(100) + self.sync_all() + + # Now get a pristine address for receiving transfers: + addr = bob.getnewaddress() + verify_utxos(bob, []) + verify_utxos(charlie, []) + + # the amounts of each txn embodied which generates a single UTXO: + amounts = map(Decimal, ['2.3', '3.7', '0.1', '0.5', '1.0', '0.19']) + + # Internal test consistency assertion: + assert_greater_than( + alice.getbalance(), + reduce(Decimal.__add__, amounts)) + + logging.info("Sending pre-export txns...") + for amount in amounts[0:2]: + alice_to_bob(amount) + + logging.info("Exporting privkey from bob...") + privkey = bob.dumpprivkey(addr) + + logging.info("Sending post-export txns...") + for amount in amounts[2:4]: + alice_to_bob(amount) + + verify_utxos(bob, amounts[:4]) + verify_utxos(charlie, []) + + logging.info("Importing privkey into charlie...") + ipkaddr = charlie.importprivkey(privkey, '', True) + assert_equal(addr, ipkaddr) + + # importprivkey should have rescanned, so this should pass: + verify_utxos(charlie, amounts[:4]) + + # Verify idempotent behavior: + ipkaddr2 = charlie.importprivkey(privkey, '', True) + assert_equal(addr, ipkaddr2) + + # amounts should be unchanged + verify_utxos(charlie, amounts[:4]) + + logging.info("Sending post-import txns...") + for amount in amounts[4:]: + alice_to_bob(amount) + + verify_utxos(bob, amounts) + verify_utxos(charlie, amounts) + + +if __name__ == '__main__': + KeyImportExportTest().main() diff --git a/qa/rpc-tests/keypool.py b/qa/rpc-tests/keypool.py index 38d6874dc..a7b32e13f 100755 --- a/qa/rpc-tests/keypool.py +++ b/qa/rpc-tests/keypool.py @@ -6,18 +6,17 @@ # Exercise the wallet keypool, and interaction with wallet encryption/locking # Add python-bitcoinrpc to module search path: + +from test_framework.authproxy import JSONRPCException +from test_framework.util import check_json_precision, initialize_chain, \ + start_nodes, start_node, stop_nodes, wait_bitcoinds, bitcoind_processes + import os import sys - -import json import shutil -import subprocess import tempfile import traceback -from test_framework.util import * - - def check_array_result(object_array, to_match, expected): """ Pass in array of JSON objects, a dictionary with key/value pairs diff --git a/qa/rpc-tests/listtransactions.py b/qa/rpc-tests/listtransactions.py index 4df8d795d..a735f41ab 100755 --- a/qa/rpc-tests/listtransactions.py +++ b/qa/rpc-tests/listtransactions.py @@ -6,8 +6,8 @@ # Exercise the listtransactions API from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from decimal import Decimal def check_array_result(object_array, to_match, expected): """ diff --git a/qa/rpc-tests/maxblocksinflight.py b/qa/rpc-tests/maxblocksinflight.py index a601147ce..beef3d2ea 100755 --- a/qa/rpc-tests/maxblocksinflight.py +++ b/qa/rpc-tests/maxblocksinflight.py @@ -4,9 +4,15 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. # -from test_framework.mininode import * +from test_framework.mininode import NodeConn, NodeConnCB, NetworkThread, \ + EarlyDisconnectError, CInv, msg_inv, mininode_lock from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.util import initialize_chain_clean, start_nodes, \ + p2p_port + +import os +import time +import random import logging ''' @@ -43,7 +49,6 @@ class TestManager(NodeConnCB): def run(self): try: - fail = False self.connection.rpc.generate(1) # Leave IBD numBlocksToGenerate = [ 8, 16, 128, 1024 ] @@ -56,7 +61,7 @@ class TestManager(NodeConnCB): current_invs = [] if len(current_invs) > 0: self.connection.send_message(msg_inv(current_invs)) - + # Wait and see how many blocks were requested time.sleep(2) @@ -75,7 +80,7 @@ class TestManager(NodeConnCB): self.disconnectOkay = True self.connection.disconnect_node() - + class MaxBlocksInFlightTest(BitcoinTestFramework): def add_options(self, parser): parser.add_option("--testbinary", dest="testbinary", diff --git a/qa/rpc-tests/mempool_nu_activation.py b/qa/rpc-tests/mempool_nu_activation.py new file mode 100755 index 000000000..f54095660 --- /dev/null +++ b/qa/rpc-tests/mempool_nu_activation.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python2 +# Copyright (c) 2018 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal, initialize_chain_clean, \ + start_node, connect_nodes, wait_and_assert_operationid_status + +from decimal import Decimal + +# Test mempool behaviour around network upgrade activation +class MempoolUpgradeActivationTest(BitcoinTestFramework): + + alert_filename = None # Set by setup_network + + def setup_network(self): + args = ["-checkmempool", "-debug=mempool", "-blockmaxsize=4000", "-nuparams=5ba81b19:200"] + self.nodes = [] + 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 + + def setup_chain(self): + print "Initializing test directory "+self.options.tmpdir + initialize_chain_clean(self.options.tmpdir, 2) + + def run_test(self): + self.nodes[1].generate(100) + self.sync_all() + + # Mine 97 blocks. After this, nodes[1] blocks + # 1 to 97 are spend-able. + self.nodes[0].generate(97) + self.sync_all() + + # Shield some ZEC + node1_taddr = self.nodes[1].getnewaddress() + node0_zaddr = self.nodes[0].z_getnewaddress() + recipients = [{'address': node0_zaddr, 'amount': Decimal('10')}] + myopid = self.nodes[1].z_sendmany(node1_taddr, recipients, 1, Decimal('0')) + print wait_and_assert_operationid_status(self.nodes[1], myopid) + self.sync_all() + + # Mine block 198. After this, the mempool expects + # block 199, which is the last Sprout block. + self.nodes[0].generate(1) + self.sync_all() + + # Mempool should be empty. + assert_equal(set(self.nodes[0].getrawmempool()), set()) + + # Check node 0 shielded balance + assert_equal(self.nodes[0].z_getbalance(node0_zaddr), Decimal('10')) + + # Fill the mempool with twice as many transactions as can fit into blocks + node0_taddr = self.nodes[0].getnewaddress() + sprout_txids = [] + while self.nodes[1].getmempoolinfo()['bytes'] < 2 * 4000: + sprout_txids.append(self.nodes[1].sendtoaddress(node0_taddr, Decimal('0.001'))) + self.sync_all() + + # Spends should be in the mempool + sprout_mempool = set(self.nodes[0].getrawmempool()) + assert_equal(sprout_mempool, set(sprout_txids)) + + # Mine block 199. After this, the mempool expects + # block 200, which is the first Overwinter block. + self.nodes[0].generate(1) + self.sync_all() + + # mempool should be empty. + assert_equal(set(self.nodes[0].getrawmempool()), set()) + + # Block 199 should contain a subset of the original mempool + # (with all other transactions having been dropped) + block_txids = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['tx'] + assert(len(block_txids) < len(sprout_txids)) + for txid in block_txids[1:]: # Exclude coinbase + assert(txid in sprout_txids) + + # Create some transparent Overwinter transactions + overwinter_txids = [self.nodes[1].sendtoaddress(node0_taddr, Decimal('0.001')) for i in range(10)] + self.sync_all() + + # Create a shielded Overwinter transaction + recipients = [{'address': node0_taddr, 'amount': Decimal('10')}] + myopid = self.nodes[0].z_sendmany(node0_zaddr, recipients, 1, Decimal('0')) + shielded = wait_and_assert_operationid_status(self.nodes[0], myopid) + assert(shielded != None) + overwinter_txids.append(shielded) + self.sync_all() + + # Spends should be in the mempool + assert_equal(set(self.nodes[0].getrawmempool()), set(overwinter_txids)) + + # Node 0 note should be unspendable + assert_equal(self.nodes[0].z_getbalance(node0_zaddr), Decimal('0')) + + # Invalidate block 199. + self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) + + # BUG: Ideally, the mempool should now only contain the transactions + # that were in block 199, the Overwinter transactions having been dropped. + # However, because chainActive is not updated until after the transactions + # in the disconnected block have been re-added to the mempool, the height + # seen by AcceptToMemoryPool is one greater than it should be. This causes + # the block 199 transactions to be validated against the Overwinter rules, + # and rejected because they (obviously) fail. + #assert_equal(set(self.nodes[0].getrawmempool()), set(block_txids[1:])) + assert_equal(set(self.nodes[0].getrawmempool()), set()) + + # Node 0 note should be spendable again + assert_equal(self.nodes[0].z_getbalance(node0_zaddr), Decimal('10')) + +if __name__ == '__main__': + MempoolUpgradeActivationTest().main() diff --git a/qa/rpc-tests/mempool_coinbase_spends.py b/qa/rpc-tests/mempool_reorg.py similarity index 70% rename from qa/rpc-tests/mempool_coinbase_spends.py rename to qa/rpc-tests/mempool_reorg.py index 578ddb1fb..ad12dadf2 100755 --- a/qa/rpc-tests/mempool_coinbase_spends.py +++ b/qa/rpc-tests/mempool_reorg.py @@ -9,9 +9,9 @@ # from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -import os -import shutil +from test_framework.authproxy import JSONRPCException +from test_framework.util import assert_equal, assert_raises, start_node, connect_nodes + # Create one-input, one-output, no-fee transaction: class MempoolCoinbaseTest(BitcoinTestFramework): @@ -36,8 +36,6 @@ class MempoolCoinbaseTest(BitcoinTestFramework): return signresult["hex"] def run_test(self): - start_count = self.nodes[0].getblockcount() - # Mine three blocks. After this, nodes[0] blocks # 101, 102, and 103 are spend-able. new_blocks = self.nodes[1].generate(4) @@ -52,16 +50,25 @@ class MempoolCoinbaseTest(BitcoinTestFramework): # 3. Indirect (coinbase and child both in chain) : spend_103 and spend_103_1 # Use invalidatblock to make all of the above coinbase spends invalid (immature coinbase), # and make sure the mempool code behaves correctly. - b = [ self.nodes[0].getblockhash(n) for n in range(102, 105) ] + b = [ self.nodes[0].getblockhash(n) for n in range(101, 105) ] coinbase_txids = [ self.nodes[0].getblock(h)['tx'][0] for h in b ] - spend_101_raw = self.create_tx(coinbase_txids[0], node1_address, 10) - spend_102_raw = self.create_tx(coinbase_txids[1], node0_address, 10) - spend_103_raw = self.create_tx(coinbase_txids[2], node0_address, 10) + spend_101_raw = self.create_tx(coinbase_txids[1], node1_address, 10) + spend_102_raw = self.create_tx(coinbase_txids[2], node0_address, 10) + spend_103_raw = self.create_tx(coinbase_txids[3], node0_address, 10) + + # Create a block-height-locked transaction which will be invalid after reorg + timelock_tx = self.nodes[0].createrawtransaction([{"txid": coinbase_txids[0], "vout": 0}], {node0_address: 10}) + # Set the time lock + timelock_tx = timelock_tx.replace("ffffffff", "11111111", 1) + timelock_tx = timelock_tx[:-8] + hex(self.nodes[0].getblockcount() + 2)[2:] + "000000" + timelock_tx = self.nodes[0].signrawtransaction(timelock_tx)["hex"] + assert_raises(JSONRPCException, self.nodes[0].sendrawtransaction, timelock_tx) # Broadcast and mine spend_102 and 103: spend_102_id = self.nodes[0].sendrawtransaction(spend_102_raw) spend_103_id = self.nodes[0].sendrawtransaction(spend_103_raw) self.nodes[0].generate(1) + assert_raises(JSONRPCException, self.nodes[0].sendrawtransaction, timelock_tx) # Create 102_1 and 103_1: spend_102_1_raw = self.create_tx(spend_102_id, node1_address, 10) @@ -69,7 +76,8 @@ class MempoolCoinbaseTest(BitcoinTestFramework): # Broadcast and mine 103_1: spend_103_1_id = self.nodes[0].sendrawtransaction(spend_103_1_raw) - self.nodes[0].generate(1) + last_block = self.nodes[0].generate(1) + timelock_tx_id = self.nodes[0].sendrawtransaction(timelock_tx) # ... now put spend_101 and spend_102_1 in memory pools: spend_101_id = self.nodes[0].sendrawtransaction(spend_101_raw) @@ -77,7 +85,11 @@ class MempoolCoinbaseTest(BitcoinTestFramework): self.sync_all() - assert_equal(set(self.nodes[0].getrawmempool()), set([ spend_101_id, spend_102_1_id ])) + assert_equal(set(self.nodes[0].getrawmempool()), set([ spend_101_id, spend_102_1_id, timelock_tx_id ])) + + for node in self.nodes: + node.invalidateblock(last_block[0]) + assert_equal(set(self.nodes[0].getrawmempool()), set([ spend_101_id, spend_102_1_id, spend_103_1_id ])) # Use invalidateblock to re-org back and make all those coinbase spends # immature/invalid: diff --git a/qa/rpc-tests/mempool_resurrect_test.py b/qa/rpc-tests/mempool_resurrect_test.py index dd3f0486f..1cbbd2cd5 100755 --- a/qa/rpc-tests/mempool_resurrect_test.py +++ b/qa/rpc-tests/mempool_resurrect_test.py @@ -9,9 +9,8 @@ # from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -import os -import shutil +from test_framework.util import assert_equal, start_node + # Create one-input, one-output, no-fee transaction: class MempoolCoinbaseTest(BitcoinTestFramework): diff --git a/qa/rpc-tests/mempool_spendcoinbase.py b/qa/rpc-tests/mempool_spendcoinbase.py index b2ec6937b..5366fb955 100755 --- a/qa/rpc-tests/mempool_spendcoinbase.py +++ b/qa/rpc-tests/mempool_spendcoinbase.py @@ -14,9 +14,10 @@ # from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -import os -import shutil +from test_framework.authproxy import JSONRPCException +from test_framework.util import assert_equal, assert_greater_than, assert_raises, \ + start_node + # Create one-input, one-output, no-fee transaction: class MempoolSpendCoinbaseTest(BitcoinTestFramework): diff --git a/qa/rpc-tests/mempool_tx_expiry.py b/qa/rpc-tests/mempool_tx_expiry.py new file mode 100755 index 000000000..9edf156d2 --- /dev/null +++ b/qa/rpc-tests/mempool_tx_expiry.py @@ -0,0 +1,184 @@ +#!/usr/bin/env python2 +# Copyright (c) 2018 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# +# Test proper expiry for transactions >= version 3 +# + +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 + +from decimal import Decimal + +class MempoolTxExpiryTest(BitcoinTestFramework): + + def setup_nodes(self): + return start_nodes(4, self.options.tmpdir, [["-nuparams=5ba81b19:205", "-txexpirydelta=4", "-debug=mempool"]] * 4) + + # Test before, at, and after expiry block + # TODO: Test case of dependent txs in reorgs + # chain is at block height 199 when run_test executes + def run_test(self): + z_alice = self.nodes[0].z_getnewaddress() + bob = self.nodes[2].getnewaddress() + z_bob = self.nodes[2].z_getnewaddress() + + # When Overwinter not yet activated, no expiryheight in tx + sapling_tx = self.nodes[0].sendtoaddress(bob, 0.01) + rawtx = self.nodes[0].getrawtransaction(sapling_tx, 1) + assert_equal(rawtx["overwintered"], False) + assert("expiryheight" not in rawtx) + + self.nodes[0].generate(6) + self.sync_all() + + ## Shield one of Alice's coinbase funds to her zaddr + res = self.nodes[0].z_shieldcoinbase("*", z_alice, 0.0001, 1) + wait_and_assert_operationid_status(self.nodes[0], res['opid']) + self.nodes[0].generate(1) + self.sync_all() + + # 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")) + + print "Splitting network..." + self.split_network() + + # Create transactions + zsendamount = Decimal('1.0') - Decimal('0.0001') + recipients = [] + recipients.append({"address": z_bob, "amount": zsendamount}) + myopid = self.nodes[0].z_sendmany(z_alice, recipients) + persist_shielded = wait_and_assert_operationid_status(self.nodes[0], myopid) + persist_transparent = self.nodes[0].sendtoaddress(bob, 0.01) + # Verify transparent transaction is version 3 intended for Overwinter branch + rawtx = self.nodes[0].getrawtransaction(persist_transparent, 1) + assert_equal(rawtx["version"], 3) + assert_equal(rawtx["overwintered"], True) + assert_equal(rawtx["expiryheight"], 212) + print "Blockheight at persist_transparent & persist_shielded creation:", self.nodes[0].getblockchaininfo()['blocks'] + print "Expiryheight of persist_transparent:", rawtx['expiryheight'] + # Verify shielded transaction is version 3 intended for Overwinter branch + rawtx = self.nodes[0].getrawtransaction(persist_shielded, 1) + print "Expiryheight of persist_shielded", rawtx['expiryheight'] + assert_equal(rawtx["version"], 3) + assert_equal(rawtx["overwintered"], True) + assert_equal(rawtx["expiryheight"], 212) + + print "\n Blockheight advances to less than expiry block height. After reorg, txs should persist in mempool" + assert(persist_transparent in self.nodes[0].getrawmempool()) + assert(persist_shielded in self.nodes[0].getrawmempool()) + assert_equal(set(self.nodes[2].getrawmempool()), set()) + print "mempool node 0:", self.nodes[0].getrawmempool() + print "mempool node 2:", self.nodes[2].getrawmempool() + bal = self.nodes[0].z_gettotalbalance() + print "Printing balance before persist_shielded & persist_transparent are initially mined from mempool", bal + # Txs are mined on node 0; will later be rolled back + self.nodes[0].generate(1) + print "Node 0 generated 1 block" + print "Node 0 height:", self.nodes[0].getblockchaininfo()['blocks'] + print "Node 2 height:", self.nodes[2].getblockchaininfo()['blocks'] + bal = self.nodes[0].z_gettotalbalance() + print "Printing balance after persist_shielded & persist_transparent are mined:", bal + assert_equal(set(self.nodes[0].getrawmempool()), set()) + + print "Mine 2 competing blocks on Node 2..." + blocks = self.nodes[2].generate(2) + for block in blocks: + blk = self.nodes[2].getblock(block) + print "Height: {0}, Mined block txs: {1}".format(blk["height"], blk["tx"]) + print "Connect nodes to force a reorg" + connect_nodes_bi(self.nodes,0,2) + self.is_network_split = False + + print "Syncing blocks" + sync_blocks(self.nodes) + + print "Ensure that txs are back in mempool of node 0" + print "Blockheight node 0:", self.nodes[0].getblockchaininfo()['blocks'] + print "Blockheight node 2:", self.nodes[2].getblockchaininfo()['blocks'] + print "mempool node 0: ", self.nodes[0].getrawmempool() + print "mempool node 2: ", self.nodes[2].getrawmempool() + assert(persist_transparent in self.nodes[0].getrawmempool()) + assert(persist_shielded in self.nodes[0].getrawmempool()) + bal = self.nodes[0].z_gettotalbalance() + # Mine txs to get them out of the way of mempool sync in split_network() + print "Generating another block on node 0 to clear txs from mempool" + self.nodes[0].generate(1) + assert_equal(set(self.nodes[0].getrawmempool()), set()) + sync_blocks(self.nodes) + + print "Splitting network..." + self.split_network() + + print "\n Blockheight advances to equal expiry block height. After reorg, txs should persist in mempool" + myopid = self.nodes[0].z_sendmany(z_alice, recipients) + persist_shielded_2 = wait_and_assert_operationid_status(self.nodes[0], myopid) + persist_transparent_2 = self.nodes[0].sendtoaddress(bob, 0.01) + rawtx_trans = self.nodes[0].getrawtransaction(persist_transparent_2, 1) + rawtx_shield = self.nodes[0].getrawtransaction(persist_shielded_2, 1) + print "Blockheight node 0 at persist_transparent_2 creation:", self.nodes[0].getblockchaininfo()['blocks'] + print "Blockheight node 2 at persist_transparent_2 creation:", self.nodes[2].getblockchaininfo()['blocks'] + print "Expiryheight of persist_transparent_2:", rawtx_trans['expiryheight'] + print "Expiryheight of persist_shielded_2:", rawtx_shield['expiryheight'] + blocks = self.nodes[2].generate(4) + for block in blocks: + blk = self.nodes[2].getblock(block) + print "Height: {0}, Mined block txs: {1}".format(blk["height"], blk["tx"]) + print "Connect nodes to force a reorg" + connect_nodes_bi(self.nodes, 0, 2) + self.is_network_split = False + sync_blocks(self.nodes) + print "Ensure that persist_transparent_2 & persist_shielded_2 are in mempool at expiry block height" + print "Blockheight node 0:", self.nodes[0].getblockchaininfo()['blocks'] + print "Blockheight node 2:", self.nodes[2].getblockchaininfo()['blocks'] + print "mempool node 0: ", self.nodes[0].getrawmempool() + print "mempool node 2: ", self.nodes[2].getrawmempool() + assert(persist_transparent_2 in self.nodes[0].getrawmempool()) + assert(persist_shielded_2 in self.nodes[0].getrawmempool()) + # Mine persist txs to get them out of the way of mempool sync in split_network() + self.nodes[0].generate(1) + assert_equal(set(self.nodes[0].getrawmempool()), set()) + sync_blocks(self.nodes) + print "Balance after persist_shielded_2 is mined to remove from mempool: ", self.nodes[0].z_gettotalbalance() + + print "Splitting network..." + self.split_network() + + print "\n Blockheight advances to greater than expiry block height. After reorg, txs should expire from mempool" + print "Balance before expire_shielded is sent: ", self.nodes[0].z_gettotalbalance() + myopid = self.nodes[0].z_sendmany(z_alice, recipients) + expire_shielded = wait_and_assert_operationid_status(self.nodes[0], myopid) + expire_transparent = self.nodes[0].sendtoaddress(bob, 0.01) + print "Blockheight node 0 at expire_transparent creation:", self.nodes[0].getblockchaininfo()['blocks'] + print "Blockheight node 2 at expire_shielded creation:", self.nodes[2].getblockchaininfo()['blocks'] + print "Expiryheight of expire_transparent:", self.nodes[0].getrawtransaction(expire_transparent, 1)['expiryheight'] + print "Expiryheight of expire_shielded:", self.nodes[0].getrawtransaction(expire_shielded, 1)['expiryheight'] + assert(expire_transparent in self.nodes[0].getrawmempool()) + assert(expire_shielded in self.nodes[0].getrawmempool()) + blocks = self.nodes[2].generate(6) + for block in blocks: + blk = self.nodes[2].getblock(block) + print "Height: {0}, Mined block txs: {1}".format(blk["height"], blk["tx"]) + print "Connect nodes to force a reorg" + connect_nodes_bi(self.nodes, 0, 2) + self.is_network_split = False + sync_blocks(self.nodes) + print "Ensure that expire_transparent & expire_shielded are in mempool at expiry block height" + print "mempool node 0: ", self.nodes[0].getrawmempool() + print "mempool node 2: ", self.nodes[2].getrawmempool() + assert_equal(set(self.nodes[0].getrawmempool()), set()) + 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")) + + +if __name__ == '__main__': + MempoolTxExpiryTest().main() diff --git a/qa/rpc-tests/mempool_tx_input_limit.py b/qa/rpc-tests/mempool_tx_input_limit.py new file mode 100755 index 000000000..c48d73be0 --- /dev/null +++ b/qa/rpc-tests/mempool_tx_input_limit.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python2 +# Copyright (c) 2017 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.authproxy import JSONRPCException +from test_framework.util import assert_equal, initialize_chain_clean, \ + start_node, connect_nodes, wait_and_assert_operationid_status + +import time +from decimal import Decimal + +# Test -mempooltxinputlimit +class MempoolTxInputLimitTest(BitcoinTestFramework): + + alert_filename = None # Set by setup_network + + def setup_network(self): + args = ["-checkmempool", "-debug=mempool", "-mempooltxinputlimit=2"] + self.nodes = [] + 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 + + def setup_chain(self): + print "Initializing test directory "+self.options.tmpdir + initialize_chain_clean(self.options.tmpdir, 2) + + def call_z_sendmany(self, from_addr, to_addr, amount): + recipients = [] + recipients.append({"address": to_addr, "amount": amount}) + myopid = self.nodes[0].z_sendmany(from_addr, recipients) + return wait_and_assert_operationid_status(self.nodes[0], myopid) + + def run_test(self): + self.nodes[0].generate(100) + self.sync_all() + # Mine three blocks. After this, nodes[0] blocks + # 1, 2, and 3 are spend-able. + self.nodes[1].generate(3) + self.sync_all() + + # Check 1: z_sendmany is limited by -mempooltxinputlimit + + # Add zaddr to node 0 + node0_zaddr = self.nodes[0].z_getnewaddress() + + # Send three inputs from node 0 taddr to zaddr to get out of coinbase + node0_taddr = self.nodes[0].getnewaddress(); + recipients = [] + recipients.append({"address":node0_zaddr, "amount":Decimal('30.0')-Decimal('0.0001')}) # utxo amount less fee + myopid = self.nodes[0].z_sendmany(node0_taddr, recipients) + + opids = [] + opids.append(myopid) + + # Spend should fail due to -mempooltxinputlimit + timeout = 120 + status = None + for x in xrange(1, timeout): + results = self.nodes[0].z_getoperationresult(opids) + if len(results)==0: + time.sleep(1) + else: + status = results[0]["status"] + msg = results[0]["error"]["message"] + assert_equal("failed", status) + assert_equal("Too many transparent inputs 3 > limit 2", msg) + break + + # Mempool should be empty. + assert_equal(set(self.nodes[0].getrawmempool()), set()) + + # Reduce amount to only use two inputs + spend_zaddr_amount = Decimal('20.0') - Decimal('0.0001') + spend_zaddr_id = self.call_z_sendmany(node0_taddr, node0_zaddr, spend_zaddr_amount) # utxo amount less fee + self.sync_all() + + # Spend should be in the mempool + assert_equal(set(self.nodes[0].getrawmempool()), set([ spend_zaddr_id ])) + + self.nodes[0].generate(1) + self.sync_all() + + # mempool should be empty. + assert_equal(set(self.nodes[0].getrawmempool()), set()) + + # Check 2: sendfrom is limited by -mempooltxinputlimit + recipients = [] + spend_taddr_amount = spend_zaddr_amount - Decimal('0.0001') + spend_taddr_output = Decimal('8') + + # Create three outputs + recipients.append({"address":self.nodes[1].getnewaddress(), "amount": spend_taddr_output}) + recipients.append({"address":self.nodes[1].getnewaddress(), "amount": spend_taddr_output}) + recipients.append({"address":self.nodes[1].getnewaddress(), "amount": spend_taddr_amount - spend_taddr_output - spend_taddr_output}) + + myopid = self.nodes[0].z_sendmany(node0_zaddr, recipients) + wait_and_assert_operationid_status(self.nodes[0], myopid) + self.nodes[1].generate(1) + self.sync_all() + + # Should use three UTXOs and fail + try: + self.nodes[1].sendfrom("", node0_taddr, spend_taddr_amount - Decimal('1')) + assert(False) + except JSONRPCException,e: + msg = e.error['message'] + assert_equal("Too many transparent inputs 3 > limit 2", msg) + + # mempool should be empty. + assert_equal(set(self.nodes[1].getrawmempool()), set()) + + # Should use two UTXOs and succeed + spend_taddr_id2 = self.nodes[1].sendfrom("", node0_taddr, spend_taddr_output + spend_taddr_output - Decimal('1')) + + # Spend should be in the mempool + assert_equal(set(self.nodes[1].getrawmempool()), set([ spend_taddr_id2 ])) + +if __name__ == '__main__': + MempoolTxInputLimitTest().main() diff --git a/qa/rpc-tests/merkle_blocks.py b/qa/rpc-tests/merkle_blocks.py index 5c333124f..2c0fcd203 100755 --- a/qa/rpc-tests/merkle_blocks.py +++ b/qa/rpc-tests/merkle_blocks.py @@ -8,9 +8,10 @@ # from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -import os -import shutil +from test_framework.authproxy import JSONRPCException +from test_framework.util import assert_equal, assert_raises, \ + initialize_chain_clean, start_node, connect_nodes + class MerkleBlockTest(BitcoinTestFramework): diff --git a/qa/rpc-tests/nodehandling.py b/qa/rpc-tests/nodehandling.py index 9a77bd97e..391a935d0 100755 --- a/qa/rpc-tests/nodehandling.py +++ b/qa/rpc-tests/nodehandling.py @@ -8,13 +8,10 @@ # from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -import base64 +from test_framework.util import assert_equal, connect_nodes_bi, p2p_port + +import time -try: - import http.client as httplib -except ImportError: - import httplib try: import urllib.parse as urlparse except ImportError: diff --git a/qa/rpc-tests/overwinter_peer_management.py b/qa/rpc-tests/overwinter_peer_management.py new file mode 100755 index 000000000..6c59e47ba --- /dev/null +++ b/qa/rpc-tests/overwinter_peer_management.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python2 +# Copyright (c) 2018 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.mininode import NodeConn, NodeConnCB, NetworkThread, \ + msg_ping, MY_VERSION, OVERWINTER_PROTO_VERSION +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import initialize_chain_clean, start_nodes, \ + p2p_port, assert_equal + +import time + +# +# In this test we connect Sprout and Overwinter mininodes to a Zcashd node +# which will activate Overwinter at block 10. +# +# We test: +# 1. the mininodes stay connected to Zcash with Sprout consensus rules +# 2. when Overwinter activates, the Sprout mininodes are dropped +# 3. new Overwinter nodes can connect to Zcash +# 4. new Sprout nodes cannot connect to Zcash +# +# This test *does not* verify that prior to Overwinter activation, the Zcashd +# node will prefer connections with Overwinter nodes, with an eviction process +# that prioritizes Sprout connections. +# + + +class TestManager(NodeConnCB): + def __init__(self): + NodeConnCB.__init__(self) + self.create_callback_map() + + def on_close(self, conn): + pass + + def on_reject(self, conn, message): + conn.rejectMessage = message + + +class OverwinterPeerManagementTest(BitcoinTestFramework): + + def setup_chain(self): + print "Initializing test directory "+self.options.tmpdir + initialize_chain_clean(self.options.tmpdir, 1) + + def setup_network(self): + self.nodes = start_nodes(1, self.options.tmpdir, + extra_args=[['-nuparams=5ba81b19:10', '-debug', '-whitelist=127.0.0.1']]) + + def run_test(self): + test = TestManager() + + # Launch 10 Sprout and 10 Overwinter mininodes + nodes = [] + for x in xrange(10): + nodes.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test, "regtest", False)) + nodes.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test, "regtest", True)) + + # Start up network handling in another thread + NetworkThread().start() + + # Sprout consensus rules apply at block height 9 + self.nodes[0].generate(9) + assert_equal(9, self.nodes[0].getblockcount()) + + # Verify mininodes are still connected to zcashd node + peerinfo = self.nodes[0].getpeerinfo() + versions = [x["version"] for x in peerinfo] + assert_equal(10, versions.count(MY_VERSION)) + assert_equal(10, versions.count(OVERWINTER_PROTO_VERSION)) + + # Overwinter consensus rules activate at block height 10 + self.nodes[0].generate(1) + assert_equal(10, self.nodes[0].getblockcount()) + + # Mininodes send ping message to zcashd node. + pingCounter = 1 + for node in nodes: + node.send_message(msg_ping(pingCounter)) + pingCounter = pingCounter + 1 + + time.sleep(3) + + # Verify Sprout mininodes have been dropped and Overwinter mininodes are still connected. + peerinfo = self.nodes[0].getpeerinfo() + versions = [x["version"] for x in peerinfo] + assert_equal(0, versions.count(MY_VERSION)) + assert_equal(10, versions.count(OVERWINTER_PROTO_VERSION)) + + # Extend the Overwinter chain with another block. + self.nodes[0].generate(1) + + # Connect a new Overwinter mininode to the zcashd node, which is accepted. + nodes.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test, "regtest", True)) + time.sleep(3) + assert_equal(11, len(self.nodes[0].getpeerinfo())) + + # Try to connect a new Sprout mininode to the zcashd node, which is rejected. + sprout = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test, "regtest", False) + nodes.append(sprout) + time.sleep(3) + assert("Version must be 170003 or greater" in str(sprout.rejectMessage)) + + # Verify that only Overwinter mininodes are connected. + peerinfo = self.nodes[0].getpeerinfo() + versions = [x["version"] for x in peerinfo] + assert_equal(0, versions.count(MY_VERSION)) + assert_equal(11, versions.count(OVERWINTER_PROTO_VERSION)) + + for node in nodes: + node.disconnect_node() + +if __name__ == '__main__': + OverwinterPeerManagementTest().main() diff --git a/qa/rpc-tests/p2p-acceptblock.py b/qa/rpc-tests/p2p-acceptblock.py index 83c03eeb7..25221fbed 100755 --- a/qa/rpc-tests/p2p-acceptblock.py +++ b/qa/rpc-tests/p2p-acceptblock.py @@ -4,12 +4,17 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. # -from test_framework.mininode import * +from test_framework.mininode import CBlockHeader, CInv, NodeConn, NodeConnCB, \ + NetworkThread, msg_block, msg_headers, msg_inv, msg_ping, msg_pong, \ + mininode_lock from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -import time +from test_framework.util import assert_equal, initialize_chain_clean, \ + start_node, p2p_port from test_framework.blocktools import create_block, create_coinbase +import os +import time + ''' AcceptBlockTest -- test processing of unrequested blocks. diff --git a/qa/rpc-tests/paymentdisclosure.py b/qa/rpc-tests/paymentdisclosure.py new file mode 100755 index 000000000..48d4712a9 --- /dev/null +++ b/qa/rpc-tests/paymentdisclosure.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python2 +# Copyright (c) 2017 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.test_framework import BitcoinTestFramework +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 + +from decimal import Decimal + +class PaymentDisclosureTest (BitcoinTestFramework): + + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 4) + + def setup_network(self, split=False): + args = ['-debug=zrpcunsafe,paymentdisclosure', '-experimentalfeatures', '-paymentdisclosure', '-txindex=1'] + self.nodes = [] + self.nodes.append(start_node(0, self.options.tmpdir, args)) + self.nodes.append(start_node(1, self.options.tmpdir, args)) + # node 2 does not enable payment disclosure + args2 = ['-debug=zrpcunsafe', '-experimentalfeatures', '-txindex=1'] + self.nodes.append(start_node(2, self.options.tmpdir, args2)) + connect_nodes_bi(self.nodes,0,1) + connect_nodes_bi(self.nodes,1,2) + connect_nodes_bi(self.nodes,0,2) + self.is_network_split=False + self.sync_all() + + def run_test (self): + print "Mining blocks..." + + self.nodes[0].generate(4) + walletinfo = self.nodes[0].getwalletinfo() + assert_equal(walletinfo['immature_balance'], 40) + assert_equal(walletinfo['balance'], 0) + self.sync_all() + self.nodes[2].generate(3) + self.sync_all() + self.nodes[1].generate(101) + self.sync_all() + assert_equal(self.nodes[0].getbalance(), 40) + assert_equal(self.nodes[1].getbalance(), 10) + assert_equal(self.nodes[2].getbalance(), 30) + + mytaddr = self.nodes[0].getnewaddress() + myzaddr = self.nodes[0].z_getnewaddress() + + # Check that Node 2 has payment disclosure disabled. + try: + self.nodes[2].z_getpaymentdisclosure("invalidtxid", 0, 0) + assert(False) + except JSONRPCException as e: + errorString = e.error['message'] + assert("payment disclosure is disabled" in errorString) + + # Check that Node 0 returns an error for an unknown txid + try: + self.nodes[0].z_getpaymentdisclosure("invalidtxid", 0, 0) + assert(False) + except JSONRPCException as e: + errorString = e.error['message'] + assert("No information available about transaction" in errorString) + + # Shield coinbase utxos from node 0 of value 40, standard fee of 0.00010000 + recipients = [{"address":myzaddr, "amount":Decimal('40.0')-Decimal('0.0001')}] + myopid = self.nodes[0].z_sendmany(mytaddr, recipients) + txid = wait_and_assert_operationid_status(self.nodes[0], myopid) + + # Check the tx has joinsplits + assert( len(self.nodes[0].getrawtransaction("" + txid, 1)["vjoinsplit"]) > 0 ) + + # Sync mempools + self.sync_all() + + # Confirm that you can't create a payment disclosure for an unconfirmed tx + try: + self.nodes[0].z_getpaymentdisclosure(txid, 0, 0) + assert(False) + except JSONRPCException as e: + errorString = e.error['message'] + assert("Transaction has not been confirmed yet" in errorString) + + try: + self.nodes[1].z_getpaymentdisclosure(txid, 0, 0) + assert(False) + except JSONRPCException as e: + errorString = e.error['message'] + assert("Transaction has not been confirmed yet" in errorString) + + # Mine tx + self.nodes[0].generate(1) + self.sync_all() + + # Confirm that Node 1 cannot create a payment disclosure for a transaction which does not impact its wallet + try: + self.nodes[1].z_getpaymentdisclosure(txid, 0, 0) + assert(False) + except JSONRPCException as e: + errorString = e.error['message'] + assert("Transaction does not belong to the wallet" in errorString) + + # Check that an invalid joinsplit index is rejected + try: + self.nodes[0].z_getpaymentdisclosure(txid, 1, 0) + assert(False) + except JSONRPCException as e: + errorString = e.error['message'] + assert("Invalid js_index" in errorString) + + try: + self.nodes[0].z_getpaymentdisclosure(txid, -1, 0) + assert(False) + except JSONRPCException as e: + errorString = e.error['message'] + assert("Invalid js_index" in errorString) + + # Check that an invalid output index is rejected + try: + self.nodes[0].z_getpaymentdisclosure(txid, 0, 2) + assert(False) + except JSONRPCException as e: + errorString = e.error['message'] + assert("Invalid output_index" in errorString) + + try: + self.nodes[0].z_getpaymentdisclosure(txid, 0, -1) + assert(False) + except JSONRPCException as e: + errorString = e.error['message'] + assert("Invalid output_index" in errorString) + + # Ask Node 0 to create and validate a payment disclosure for output 0 + message = "Here is proof of my payment!" + pd = self.nodes[0].z_getpaymentdisclosure(txid, 0, 0, message) + result = self.nodes[0].z_validatepaymentdisclosure(pd) + assert(result["valid"]) + output_value_sum = Decimal(result["value"]) + + # Ask Node 1 to confirm the payment disclosure is valid + result = self.nodes[1].z_validatepaymentdisclosure(pd) + assert(result["valid"]) + assert_equal(result["message"], message) + assert_equal(result["value"], output_value_sum) + + # Confirm that payment disclosure begins with prefix zpd: + assert(pd.startswith("zpd:")) + + # Confirm that payment disclosure without prefix zpd: fails validation + try: + self.nodes[1].z_validatepaymentdisclosure(pd[4:]) + assert(False) + except JSONRPCException as e: + errorString = e.error['message'] + assert("payment disclosure prefix not found" in errorString) + + # Check that total value of output index 0 and index 1 should equal shielding amount of 40 less standard fee. + 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('39.99990000')) + + # Create a z->z transaction, sending shielded funds from node 0 to node 1 + node1zaddr = self.nodes[1].z_getnewaddress() + recipients = [{"address":node1zaddr, "amount":Decimal('1')}] + myopid = self.nodes[0].z_sendmany(myzaddr, recipients) + txid = wait_and_assert_operationid_status(self.nodes[0], myopid) + self.sync_all() + self.nodes[0].generate(1) + self.sync_all() + + # Confirm that Node 0 can create a valid payment disclosure + pd = self.nodes[0].z_getpaymentdisclosure(txid, 0, 0, "a message of your choice") + result = self.nodes[0].z_validatepaymentdisclosure(pd) + assert(result["valid"]) + + # Confirm that Node 1, even as recipient of shielded funds, cannot create a payment disclosure + # as the transaction was created by Node 0 and Node 1's payment disclosure database does not + # contain the necessary data to do so, where the data would only have been available on Node 0 + # when executing z_shieldcoinbase. + try: + self.nodes[1].z_getpaymentdisclosure(txid, 0, 0) + assert(False) + except JSONRPCException as e: + errorString = e.error['message'] + assert("Could not find payment disclosure info for the given joinsplit output" in errorString) + + # Payment disclosures cannot be created for transparent transactions. + txid = self.nodes[2].sendtoaddress(mytaddr, 1.0) + self.sync_all() + + # No matter the type of transaction, if it has not been confirmed, it is ignored. + try: + self.nodes[0].z_getpaymentdisclosure(txid, 0, 0) + assert(False) + except JSONRPCException as e: + errorString = e.error['message'] + assert("Transaction has not been confirmed yet" in errorString) + + self.nodes[0].generate(1) + self.sync_all() + + # Confirm that a payment disclosure can only be generated for a shielded transaction. + try: + self.nodes[0].z_getpaymentdisclosure(txid, 0, 0) + assert(False) + except JSONRPCException as e: + errorString = e.error['message'] + assert("Transaction is not a shielded transaction" in errorString) + +if __name__ == '__main__': + PaymentDisclosureTest().main() diff --git a/qa/rpc-tests/prioritisetransaction.py b/qa/rpc-tests/prioritisetransaction.py index 2e8f11656..134b9b160 100755 --- a/qa/rpc-tests/prioritisetransaction.py +++ b/qa/rpc-tests/prioritisetransaction.py @@ -4,10 +4,13 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -from time import * +from test_framework.util import assert_equal, initialize_chain_clean, \ + start_node, connect_nodes from test_framework.mininode import COIN +import time + + class PrioritiseTransactionTest (BitcoinTestFramework): def setup_chain(self): @@ -23,34 +26,6 @@ class PrioritiseTransactionTest (BitcoinTestFramework): self.is_network_split=False self.sync_all() - # Returns txid if operation was a success or None - def wait_and_assert_operationid_status(self, myopid, in_status='success', in_errormsg=None): - print('waiting for async operation {}'.format(myopid)) - opids = [] - opids.append(myopid) - timeout = 300 - status = None - errormsg = None - txid = None - for x in xrange(1, timeout): - results = self.nodes[0].z_getoperationresult(opids) - if len(results)==0: - sleep(1) - else: - status = results[0]["status"] - if status == "failed": - errormsg = results[0]['error']['message'] - elif status == "success": - txid = results[0]['result']['txid'] - break - print('...returned status: {}'.format(status)) - assert_equal(in_status, status) - if errormsg is not None: - assert(in_errormsg is not None) - assert_equal(in_errormsg in errormsg, True) - print('...returned error: {}'.format(errormsg)) - return txid - def run_test (self): # tx priority is calculated: priority = sum(input_value_in_base_units * input_age)/size_in_bytes @@ -67,7 +42,7 @@ class PrioritiseTransactionTest (BitcoinTestFramework): self.sync_all() # Create tx of lower value to be prioritized on node 0 - # Older transactions get mined first, so this lower value, newer tx is unlikely to be mined without prioritization + # Older transactions get mined first, so this lower value, newer tx is unlikely to be mined without prioritisation priority_tx_0 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1) # Check that priority_tx_0 is not in block_template() prior to prioritisation @@ -79,19 +54,50 @@ class PrioritiseTransactionTest (BitcoinTestFramework): break assert_equal(in_block_template, False) - priority_result = self.nodes[0].prioritisetransaction(priority_tx_0, 1000, int(3 * base_fee * COIN)) + priority_success = self.nodes[0].prioritisetransaction(priority_tx_0, 1000, int(3 * base_fee * COIN)) + assert(priority_success) - # Check that prioritized transaction is in getblocktemplate() + # Check that prioritized transaction is not in getblocktemplate() + # (not updated because no new txns) + in_block_template = False + block_template = self.nodes[0].getblocktemplate() + for tx in block_template['transactions']: + if tx['hash'] == priority_tx_0: + in_block_template = True + break + assert_equal(in_block_template, False) + + # Sending a new transaction will make getblocktemplate refresh within 10s + self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1) + + # Check that prioritized transaction is not in getblocktemplate() + # (too soon) in_block_template = False block_template = self.nodes[0].getblocktemplate() for tx in block_template['transactions']: if tx['hash'] == priority_tx_0: in_block_template = True break - # NOTE: getblocktemplate() should return prioritized transaction, but is not - # Noted by user in issue #1884 assert_equal(in_block_template, False) + # Check that prioritized transaction is in getblocktemplate() + # getblocktemplate() will refresh after 1 min, or after 10 sec if new transaction is added to mempool + # Mempool is probed every 10 seconds. We'll give getblocktemplate() a maximum of 30 seconds to refresh + block_template = self.nodes[0].getblocktemplate() + start = time.time(); + in_block_template = False + while in_block_template == False: + for tx in block_template['transactions']: + if tx['hash'] == priority_tx_0: + in_block_template = True + break + if time.time() - start > 30: + raise AssertionError("Test timed out because prioritised transaction was not returned by getblocktemplate within 30 seconds.") + time.sleep(1) + block_template = self.nodes[0].getblocktemplate() + + assert(in_block_template) + # Node 1 doesn't get the next block, so this *shouldn't* be mined despite being prioritized on node 1 priority_tx_1 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 0.1) self.nodes[1].prioritisetransaction(priority_tx_1, 1000, int(3 * base_fee * COIN)) diff --git a/qa/rpc-tests/proton_test.py b/qa/rpc-tests/proton_test.py new file mode 100755 index 000000000..d9fb27bd3 --- /dev/null +++ b/qa/rpc-tests/proton_test.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python2 +# Copyright (c) 2017 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://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 +# + +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/qa/rpc-tests/proxy_test.py b/qa/rpc-tests/proxy_test.py index 3623c1616..e4fb48820 100755 --- a/qa/rpc-tests/proxy_test.py +++ b/qa/rpc-tests/proxy_test.py @@ -2,14 +2,14 @@ # Copyright (c) 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. -import socket -import traceback, sys -from binascii import hexlify -import time, os from test_framework.socks5 import Socks5Configuration, Socks5Command, Socks5Server, AddressType from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.util import assert_equal, start_nodes + +import socket +import os + ''' Test plan: - Start bitcoind's with different proxy configurations diff --git a/qa/rpc-tests/pruning.py b/qa/rpc-tests/pruning.py index 21f8d6938..2639060fa 100755 --- a/qa/rpc-tests/pruning.py +++ b/qa/rpc-tests/pruning.py @@ -12,8 +12,12 @@ # ******** from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.authproxy import JSONRPCException +from test_framework.util import initialize_chain_clean, start_node, \ + connect_nodes, stop_node, sync_blocks + import os.path +import time def calc_usage(blockdir): return sum(os.path.getsize(blockdir+f) for f in os.listdir(blockdir) if os.path.isfile(blockdir+f))/(1024*1024) @@ -190,7 +194,7 @@ class PruneTest(BitcoinTestFramework): try: self.nodes[2].getblock(self.forkhash) raise AssertionError("Old block wasn't pruned so can't test redownload") - except JSONRPCException as e: + except JSONRPCException: print "Will need to redownload block",self.forkheight # Verify that we have enough history to reorg back to the fork point @@ -253,7 +257,7 @@ class PruneTest(BitcoinTestFramework): newtx = newtx + rawtx[94:] # Appears to be ever so slightly faster to sign with SIGHASH_NONE signresult = node.signrawtransaction(newtx,None,None,"NONE") - txid = node.sendrawtransaction(signresult["hex"], True) + node.sendrawtransaction(signresult["hex"], True) # Mine a full sized block which will be these transactions we just created node.generate(1) diff --git a/qa/rpc-tests/rawtransactions.py b/qa/rpc-tests/rawtransactions.py index 45517d3fd..dc919f028 100755 --- a/qa/rpc-tests/rawtransactions.py +++ b/qa/rpc-tests/rawtransactions.py @@ -9,9 +9,11 @@ # from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -from pprint import pprint -from time import sleep +from test_framework.authproxy import JSONRPCException +from test_framework.util import assert_equal, initialize_chain_clean, \ + start_nodes, connect_nodes_bi + +from decimal import Decimal # Create one-input, one-output, no-fee transaction: class RawTransactionsTest(BitcoinTestFramework): @@ -104,18 +106,20 @@ class RawTransactionsTest(BitcoinTestFramework): mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey'], addr3Obj['pubkey']]) mSigObjValid = self.nodes[2].validateaddress(mSigObj) + assert_equal(mSigObjValid['isvalid'], True) txId = self.nodes[0].sendtoaddress(mSigObj, 2.2); decTx = self.nodes[0].gettransaction(txId) rawTx = self.nodes[0].decoderawtransaction(decTx['hex']) sPK = rawTx['vout'][0]['scriptPubKey']['hex'] + [sPK] # hush pyflakes self.sync_all() self.nodes[0].generate(1) self.sync_all() - #THIS IS A INCOMPLETE FEATURE - #NODE2 HAS TWO OF THREE KEY AND THE FUNDS SHOULD BE SPENDABLE AND COUNT AT BALANCE CALCULATION - assert_equal(self.nodes[2].getbalance(), bal) #for now, assume the funds of a 2of3 multisig tx are not marked as spendable + # THIS IS A INCOMPLETE FEATURE + # NODE2 HAS TWO OF THREE KEY AND THE FUNDS SHOULD BE SPENDABLE AND COUNT AT BALANCE CALCULATION + assert_equal(self.nodes[2].getbalance(), bal) # for now, assume the funds of a 2of3 multisig tx are not marked as spendable txDetails = self.nodes[0].gettransaction(txId, True) rawTx = self.nodes[0].decoderawtransaction(txDetails['hex']) @@ -130,10 +134,10 @@ class RawTransactionsTest(BitcoinTestFramework): outputs = { self.nodes[0].getnewaddress() : 2.199 } rawTx = self.nodes[2].createrawtransaction(inputs, outputs) rawTxPartialSigned = self.nodes[1].signrawtransaction(rawTx, inputs) - assert_equal(rawTxPartialSigned['complete'], False) #node1 only has one key, can't comp. sign the tx - + assert_equal(rawTxPartialSigned['complete'], False) # node1 only has one key, can't comp. sign the tx + rawTxSigned = self.nodes[2].signrawtransaction(rawTx, inputs) - assert_equal(rawTxSigned['complete'], True) #node2 can sign the tx compl., own two of three keys + assert_equal(rawTxSigned['complete'], True) # node2 can sign the tx compl., own two of three keys self.nodes[2].sendrawtransaction(rawTxSigned['hex']) rawTx = self.nodes[0].decoderawtransaction(rawTxSigned['hex']) self.sync_all() diff --git a/qa/rpc-tests/receivedby.py b/qa/rpc-tests/receivedby.py index 16d6bd4cf..59f7bf258 100755 --- a/qa/rpc-tests/receivedby.py +++ b/qa/rpc-tests/receivedby.py @@ -6,15 +6,14 @@ # Exercise the listreceivedbyaddress API from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from decimal import Decimal def get_sub_array_from_array(object_array, to_match): ''' - Finds and returns a sub array from an array of arrays. - to_match should be a unique idetifier of a sub array + Finds and returns a sub array from an array of arrays. + to_match should be a unique idetifier of a sub array ''' - num_matched = 0 for item in object_array: all_match = True for key,value in to_match.items(): @@ -26,12 +25,12 @@ def get_sub_array_from_array(object_array, to_match): return [] def check_array_result(object_array, to_match, expected, should_not_find = False): - """ - Pass in array of JSON objects, a dictionary with key/value pairs - to match against, and another dictionary with expected key/value - pairs. - If the should_not_find flag is true, to_match should not be found in object_array - """ + ''' + Pass in array of JSON objects, a dictionary with key/value pairs + to match against, and another dictionary with expected key/value + pairs. + If the should_not_find flag is true, to_match should not be found in object_array + ''' if should_not_find == True: expected = { } num_matched = 0 @@ -62,49 +61,49 @@ class ReceivedByTest(BitcoinTestFramework): txid = self.nodes[0].sendtoaddress(addr, 0.1) self.sync_all() - #Check not listed in listreceivedbyaddress because has 0 confirmations + # Check not listed in listreceivedbyaddress because has 0 confirmations check_array_result(self.nodes[1].listreceivedbyaddress(), {"address":addr}, { }, True) - #Bury Tx under 10 block so it will be returned by listreceivedbyaddress + # Bury Tx under 10 block so it will be returned by listreceivedbyaddress self.nodes[1].generate(10) self.sync_all() check_array_result(self.nodes[1].listreceivedbyaddress(), {"address":addr}, {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]}) - #With min confidence < 10 + # With min confidence < 10 check_array_result(self.nodes[1].listreceivedbyaddress(5), {"address":addr}, {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]}) - #With min confidence > 10, should not find Tx + # With min confidence > 10, should not find Tx check_array_result(self.nodes[1].listreceivedbyaddress(11),{"address":addr},{ },True) - #Empty Tx + # Empty Tx addr = self.nodes[1].getnewaddress() check_array_result(self.nodes[1].listreceivedbyaddress(0,True), {"address":addr}, {"address":addr, "account":"", "amount":0, "confirmations":0, "txids":[]}) ''' - getreceivedbyaddress Test + getreceivedbyaddress Test ''' # Send from node 0 to 1 addr = self.nodes[1].getnewaddress() txid = self.nodes[0].sendtoaddress(addr, 0.1) self.sync_all() - #Check balance is 0 because of 0 confirmations + # Check balance is 0 because of 0 confirmations balance = self.nodes[1].getreceivedbyaddress(addr) if balance != Decimal("0.0"): raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance)) - #Check balance is 0.1 + # Check balance is 0.1 balance = self.nodes[1].getreceivedbyaddress(addr,0) if balance != Decimal("0.1"): raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance)) - #Bury Tx under 10 block so it will be returned by the default getreceivedbyaddress + # Bury Tx under 10 block so it will be returned by the default getreceivedbyaddress self.nodes[1].generate(10) self.sync_all() balance = self.nodes[1].getreceivedbyaddress(addr) @@ -112,15 +111,15 @@ class ReceivedByTest(BitcoinTestFramework): raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance)) ''' - listreceivedbyaccount + getreceivedbyaccount Test + listreceivedbyaccount + getreceivedbyaccount Test ''' - #set pre-state + # set pre-state addrArr = self.nodes[1].getnewaddress() account = self.nodes[1].getaccount(addrArr) received_by_account_json = get_sub_array_from_array(self.nodes[1].listreceivedbyaccount(),{"account":account}) if len(received_by_account_json) == 0: raise AssertionError("No accounts found in node") - balance_by_account = rec_by_accountArr = self.nodes[1].getreceivedbyaccount(account) + balance_by_account = self.nodes[1].getreceivedbyaccount(account) txid = self.nodes[0].sendtoaddress(addr, 0.1) self.sync_all() @@ -147,7 +146,7 @@ class ReceivedByTest(BitcoinTestFramework): if balance != balance_by_account + Decimal("0.1"): raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance)) - #Create a new account named "mynewaccount" that has a 0 balance + # Create a new account named "mynewaccount" that has a 0 balance self.nodes[1].getaccountaddress("mynewaccount") received_by_account_json = get_sub_array_from_array(self.nodes[1].listreceivedbyaccount(0,True),{"account":"mynewaccount"}) if len(received_by_account_json) == 0: diff --git a/qa/rpc-tests/reindex.py b/qa/rpc-tests/reindex.py index f2e3f248e..ae7563192 100755 --- a/qa/rpc-tests/reindex.py +++ b/qa/rpc-tests/reindex.py @@ -6,9 +6,11 @@ # # Test -reindex with CheckBlockIndex # + from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -import os.path +from test_framework.util import assert_equal, initialize_chain_clean, \ + start_node, stop_node, wait_bitcoinds + class ReindexTest(BitcoinTestFramework): diff --git a/qa/rpc-tests/reorg_limit.py b/qa/rpc-tests/reorg_limit.py new file mode 100755 index 000000000..12cd146fc --- /dev/null +++ b/qa/rpc-tests/reorg_limit.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python2 +# Copyright (c) 2017 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# +# Test reorg limit +# + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + check_node, + connect_nodes_bi, + sync_blocks, +) +from time import sleep + +def check_stopped(i, timeout=10): + stopped = False + for x in xrange(1, timeout): + ret = check_node(i) + if ret is None: + sleep(1) + else: + stopped = True + break + return stopped + +class ReorgLimitTest(BitcoinTestFramework): + + def run_test(self): + assert(self.nodes[0].getblockcount() == 200) + assert(self.nodes[2].getblockcount() == 200) + + self.split_network() + + print "Test the maximum-allowed reorg:" + print "Mine 99 blocks on Node 0" + self.nodes[0].generate(99) + assert(self.nodes[0].getblockcount() == 299) + assert(self.nodes[2].getblockcount() == 200) + + print "Mine competing 100 blocks on Node 2" + self.nodes[2].generate(100) + assert(self.nodes[0].getblockcount() == 299) + assert(self.nodes[2].getblockcount() == 300) + + print "Connect nodes to force a reorg" + connect_nodes_bi(self.nodes, 0, 2) + self.is_network_split = False + sync_blocks(self.nodes) + + print "Check Node 0 is still running and on the correct chain" + assert(self.nodes[0].getblockcount() == 300) + + self.split_network() + + print "Test the minimum-rejected reorg:" + print "Mine 100 blocks on Node 0" + self.nodes[0].generate(100) + assert(self.nodes[0].getblockcount() == 400) + assert(self.nodes[2].getblockcount() == 300) + + print "Mine competing 101 blocks on Node 2" + self.nodes[2].generate(101) + assert(self.nodes[0].getblockcount() == 400) + assert(self.nodes[2].getblockcount() == 401) + + print "Sync nodes to force a reorg" + connect_nodes_bi(self.nodes, 0, 2) + self.is_network_split = False + # sync_blocks uses RPC calls to wait for nodes to be synced, so don't + # call it here, because it will have a non-specific connection error + # when Node 0 stops. Instead, we explicitly check for the process itself + # to stop. + + print "Check Node 0 is no longer running" + assert(check_stopped(0)) + + # Dummy stop to enable the test to tear down + self.nodes[0].stop = lambda: True + +if __name__ == '__main__': + ReorgLimitTest().main() diff --git a/qa/rpc-tests/rest.py b/qa/rpc-tests/rest.py index d315c66d3..b14ec765d 100755 --- a/qa/rpc-tests/rest.py +++ b/qa/rpc-tests/rest.py @@ -7,14 +7,15 @@ # Test REST interface # - from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -from struct import * +from test_framework.util import assert_equal, assert_greater_than, \ + initialize_chain_clean, start_nodes, connect_nodes_bi + +import struct import binascii import json import StringIO -import decimal +from decimal import Decimal try: import http.client as httplib @@ -28,11 +29,11 @@ except ImportError: def deser_uint256(f): r = 0 for i in range(8): - t = unpack(b">= halvings coinbaseoutput.scriptPubKey = "" coinbase.vout = [ coinbaseoutput ] + if halvings == 0: # regtest + froutput = CTxOut() + froutput.nValue = coinbaseoutput.nValue / 5 + # regtest + fraddr = bytearray([0x67, 0x08, 0xe6, 0x67, 0x0d, 0xb0, 0xb9, 0x50, + 0xda, 0xc6, 0x80, 0x31, 0x02, 0x5c, 0xc5, 0xb6, + 0x32, 0x13, 0xa4, 0x91]) + froutput.scriptPubKey = CScript([OP_HASH160, fraddr, OP_EQUAL]) + coinbaseoutput.nValue -= froutput.nValue + coinbase.vout = [ coinbaseoutput, froutput ] coinbase.calc_sha256() return coinbase diff --git a/qa/rpc-tests/test_framework/comptool.py b/qa/rpc-tests/test_framework/comptool.py index b945f1bf2..7f9a97d68 100755 --- a/qa/rpc-tests/test_framework/comptool.py +++ b/qa/rpc-tests/test_framework/comptool.py @@ -4,10 +4,13 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. # -from mininode import * +from mininode import CBlock, CTransaction, CInv, NodeConn, NodeConnCB, \ + msg_inv, msg_getheaders, msg_ping, msg_mempool, mininode_lock, MAX_INV_SZ from blockstore import BlockStore, TxStore from util import p2p_port +import time + ''' This is a tool for comparing two or more bitcoinds to each other using a script provided. @@ -25,8 +28,6 @@ generator that returns TestInstance objects. See below for definition. # on_getheaders: provide headers via BlockStore # on_getdata: provide blocks via BlockStore -global mininode_lock - def wait_until(predicate, attempts=float('inf'), timeout=float('inf')): attempt = 0 elapsed = 0 diff --git a/qa/rpc-tests/test_framework/equihash.py b/qa/rpc-tests/test_framework/equihash.py new file mode 100755 index 000000000..e40451978 --- /dev/null +++ b/qa/rpc-tests/test_framework/equihash.py @@ -0,0 +1,293 @@ +from operator import itemgetter +import struct + +DEBUG = False +VERBOSE = False + + +word_size = 32 +word_mask = (1<= 8 and word_size >= 7+bit_len + bit_len_mask = (1<= bit_len: + acc_bits -= bit_len + for x in xrange(byte_pad, out_width): + out[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 + ) + j += out_width + + return out + +def compress_array(inp, out_len, bit_len, byte_pad=0): + assert bit_len >= 8 and word_size >= 7+bit_len + + in_width = (bit_len+7)/8 + byte_pad + assert out_len == bit_len*len(inp)/(8*in_width) + out = bytearray(out_len) + + bit_len_mask = (1 << bit_len) - 1 + + # The acc_bits least-significant bits of acc_value represent a bit sequence + # in big-endian order. + acc_bits = 0; + acc_value = 0; + + j = 0 + for i in xrange(out_len): + # When we have fewer than 8 bits left in the accumulator, read the next + # input element. + if acc_bits < 8: + acc_value = ((acc_value << bit_len) & word_mask) | inp[j] + for x in xrange(byte_pad, in_width): + acc_value = acc_value | ( + ( + # Apply bit_len_mask across byte boundaries + inp[j+x] & ((bit_len_mask >> (8*(in_width-x-1))) & 0xFF) + ) << (8*(in_width-x-1))); # Big-endian + j += in_width + acc_bits += bit_len + + acc_bits -= 8 + out[i] = (acc_value >> acc_bits) & 0xFF + + return out + +def get_indices_from_minimal(minimal, bit_len): + eh_index_size = 4 + assert (bit_len+7)/8 <= eh_index_size + len_indices = 8*eh_index_size*len(minimal)/bit_len + byte_pad = eh_index_size - (bit_len+7)/8 + expanded = expand_array(minimal, len_indices, bit_len, byte_pad) + return [struct.unpack('>I', expanded[i:i+4])[0] for i in range(0, len_indices, eh_index_size)] + +def get_minimal_from_indices(indices, bit_len): + eh_index_size = 4 + assert (bit_len+7)/8 <= eh_index_size + len_indices = len(indices)*eh_index_size + min_len = bit_len*len_indices/(8*eh_index_size) + byte_pad = eh_index_size - (bit_len+7)/8 + byte_indices = bytearray(''.join([struct.pack('>I', i) for i in indices])) + return compress_array(byte_indices, min_len, bit_len, byte_pad) + + +def hash_nonce(digest, nonce): + for i in range(8): + digest.update(struct.pack('> (32*i))) + +def hash_xi(digest, xi): + digest.update(struct.pack(' 0: + # 2b) Find next set of unordered pairs with collisions on first n/(k+1) bits + j = 1 + while j < len(X): + if not has_collision(X[-1][0], X[-1-j][0], i, collision_length): + break + j += 1 + + # 2c) Store tuples (X_i ^ X_j, (i, j)) on the table + for l in range(0, j-1): + for m in range(l+1, j): + # Check that there are no duplicate indices in tuples i and j + if distinct_indices(X[-1-l][1], X[-1-m][1]): + if X[-1-l][1][0] < X[-1-m][1][0]: + concat = X[-1-l][1] + X[-1-m][1] + else: + concat = X[-1-m][1] + X[-1-l][1] + Xc.append((xor(X[-1-l][0], X[-1-m][0]), concat)) + + # 2d) Drop this set + while j > 0: + X.pop(-1) + j -= 1 + # 2e) Replace previous list with new list + X = Xc + + # k+1) Find a collision on last 2n(k+1) bits + if DEBUG: + print 'Final round:' + print '- Sorting list' + X.sort(key=itemgetter(0)) + if DEBUG and VERBOSE: + for Xi in X[-32:]: + print '%s %s' % (print_hash(Xi[0]), Xi[1]) + if DEBUG: print '- Finding collisions' + solns = [] + while len(X) > 0: + j = 1 + while j < len(X): + if not (has_collision(X[-1][0], X[-1-j][0], k, collision_length) and + has_collision(X[-1][0], X[-1-j][0], k+1, collision_length)): + break + j += 1 + + for l in range(0, j-1): + for m in range(l+1, j): + res = xor(X[-1-l][0], X[-1-m][0]) + if count_zeroes(res) == 8*hash_length and distinct_indices(X[-1-l][1], X[-1-m][1]): + if DEBUG and VERBOSE: + print 'Found solution:' + print '- %s %s' % (print_hash(X[-1-l][0]), X[-1-l][1]) + print '- %s %s' % (print_hash(X[-1-m][0]), X[-1-m][1]) + if X[-1-l][1][0] < X[-1-m][1][0]: + solns.append(list(X[-1-l][1] + X[-1-m][1])) + else: + solns.append(list(X[-1-m][1] + X[-1-l][1])) + + # 2d) Drop this set + while j > 0: + X.pop(-1) + j -= 1 + return [get_minimal_from_indices(soln, collision_length+1) for soln in solns] + +def gbp_validate(digest, minimal, n, k): + validate_params(n, k) + collision_length = n/(k+1) + hash_length = (k+1)*((collision_length+7)//8) + indices_per_hash_output = 512/n + solution_width = (1 << k)*(collision_length+1)//8 + + if len(minimal) != solution_width: + print 'Invalid solution length: %d (expected %d)' % \ + (len(minimal), solution_width) + return False + + X = [] + for i in get_indices_from_minimal(minimal, collision_length+1): + r = i % indices_per_hash_output + # X_i = H(I||V||x_i) + curr_digest = digest.copy() + hash_xi(curr_digest, i/indices_per_hash_output) + tmp_hash = curr_digest.digest() + X.append(( + expand_array(bytearray(tmp_hash[r*n/8:(r+1)*n/8]), + hash_length, collision_length), + (i,) + )) + + for r in range(1, k+1): + Xc = [] + for i in range(0, len(X), 2): + if not has_collision(X[i][0], X[i+1][0], r, collision_length): + print 'Invalid solution: invalid collision length between StepRows' + return False + if X[i+1][1][0] < X[i][1][0]: + print 'Invalid solution: Index tree incorrectly ordered' + return False + if not distinct_indices(X[i][1], X[i+1][1]): + print 'Invalid solution: duplicate indices' + return False + Xc.append((xor(X[i][0], X[i+1][0]), X[i][1] + X[i+1][1])) + X = Xc + + if len(X) != 1: + print 'Invalid solution: incorrect length after end of rounds: %d' % len(X) + return False + + if count_zeroes(X[0][0]) != 8*hash_length: + print 'Invalid solution: incorrect number of zeroes: %d' % count_zeroes(X[0][0]) + return False + + return True + +def zcash_person(n, k): + return b'ZcashPoW' + struct.pack('= n): + raise ValueError('n must be larger than k') + if (((n/(k+1))+1) >= 32): + raise ValueError('Parameters must satisfy n/(k+1)+1 < 32') diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index c39637073..3c9821259 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -30,9 +30,18 @@ from threading import RLock from threading import Thread import logging import copy +from pyblake2 import blake2b +from .equihash import ( + gbp_basic, + gbp_validate, + hash_nonce, + zcash_person, +) + +OVERWINTER_PROTO_VERSION = 170003 BIP0031_VERSION = 60000 -MY_VERSION = 60001 # past bip-31 for ping/pong +MY_VERSION = 170002 # past bip-31 for ping/pong MY_SUBVERSION = "/python-mininode-tester:0.0.1/" MAX_INV_SZ = 50000 @@ -234,6 +243,36 @@ def ser_int_vector(l): return r +def deser_char_vector(f): + nit = struct.unpack("= 2: + self.vjoinsplit = deser_vector(f, JSDescription) + if len(self.vjoinsplit) > 0: + self.joinSplitPubKey = deser_uint256(f) + self.joinSplitSig = f.read(64) self.sha256 = None self.hash = None @@ -406,6 +604,11 @@ class CTransaction(object): r += ser_vector(self.vin) r += ser_vector(self.vout) r += struct.pack("= 2: + r += ser_vector(self.vjoinsplit) + if len(self.vjoinsplit) > 0: + r += ser_uint256(self.joinSplitPubKey) + r += self.joinSplitSig return r def rehash(self): @@ -425,8 +628,15 @@ class CTransaction(object): return True def __repr__(self): - return "CTransaction(nVersion=%i vin=%s vout=%s nLockTime=%i)" \ + r = "CTransaction(nVersion=%i vin=%s vout=%s nLockTime=%i" \ % (self.nVersion, repr(self.vin), repr(self.vout), self.nLockTime) + if self.nVersion >= 2: + r += " vjoinsplit=%s" % repr(self.vjoinsplit) + if len(self.vjoinsplit) > 0: + r += " joinSplitPubKey=%064x joinSplitSig=%064x" \ + (self.joinSplitPubKey, self.joinSplitSig) + r += ")" + return r class CBlockHeader(object): @@ -437,20 +647,24 @@ class CBlockHeader(object): self.nVersion = header.nVersion self.hashPrevBlock = header.hashPrevBlock self.hashMerkleRoot = header.hashMerkleRoot + self.hashReserved = header.hashReserved self.nTime = header.nTime self.nBits = header.nBits self.nNonce = header.nNonce + self.nSolution = header.nSolution self.sha256 = header.sha256 self.hash = header.hash self.calc_sha256() def set_null(self): - self.nVersion = 1 + self.nVersion = 4 self.hashPrevBlock = 0 self.hashMerkleRoot = 0 + self.hashReserved = 0 self.nTime = 0 self.nBits = 0 self.nNonce = 0 + self.nSolution = [] self.sha256 = None self.hash = None @@ -458,9 +672,11 @@ class CBlockHeader(object): self.nVersion = struct.unpack(" target: @@ -537,17 +763,31 @@ class CBlock(CBlockHeader): return False return True - def solve(self): - self.calc_sha256() + def solve(self, n=48, k=5): target = uint256_from_compact(self.nBits) - while self.sha256 > target: + # H(I||... + digest = blake2b(digest_size=(512/n)*n/8, person=zcash_person(n, k)) + digest.update(super(CBlock, self).serialize()[:108]) + self.nNonce = 0 + while True: + # H(I||V||... + curr_digest = digest.copy() + hash_nonce(curr_digest, self.nNonce) + # (x_1, x_2, ...) = A(I, V, n, k) + solns = gbp_basic(curr_digest, n, k) + for soln in solns: + assert(gbp_validate(curr_digest, soln, n, k)) + self.nSolution = soln + self.rehash() + if self.sha256 <= target: + return self.nNonce += 1 - self.rehash() def __repr__(self): - return "CBlock(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x nTime=%s nBits=%08x nNonce=%08x vtx=%s)" \ + return "CBlock(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x hashReserved=%064x nTime=%s nBits=%08x nNonce=%064x nSolution=%s vtx=%s)" \ % (self.nVersion, self.hashPrevBlock, self.hashMerkleRoot, - time.ctime(self.nTime), self.nBits, self.nNonce, repr(self.vtx)) + self.hashReserved, time.ctime(self.nTime), self.nBits, + self.nNonce, repr(self.nSolution), repr(self.vtx)) class CUnsignedAlert(object): @@ -629,8 +869,12 @@ class CAlert(object): class msg_version(object): command = "version" - def __init__(self): - self.nVersion = MY_VERSION + def __init__(self, overwintered=False): + if overwintered: + self.nVersion = OVERWINTER_PROTO_VERSION + else: + self.nVersion = MY_VERSION + self.nServices = 1 self.nTime = time.time() self.addrTo = CAddress() @@ -1082,12 +1326,12 @@ class NodeConn(asyncore.dispatcher): "mempool": msg_mempool } MAGIC_BYTES = { - "mainnet": "\xf9\xbe\xb4\xd9", # mainnet - "testnet3": "\x0b\x11\x09\x07", # testnet3 - "regtest": "\xfa\xbf\xb5\xda" # regtest + "mainnet": "\x24\xe9\x27\x64", # mainnet + "testnet3": "\xfa\x1a\xf9\xbf", # testnet3 + "regtest": "\xaa\xe8\x3f\x5f" # regtest } - def __init__(self, dstaddr, dstport, rpc, callback, net="regtest"): + def __init__(self, dstaddr, dstport, rpc, callback, net="regtest", overwintered=False): asyncore.dispatcher.__init__(self, map=mininode_socket_map) self.log = logging.getLogger("NodeConn(%s:%d)" % (dstaddr, dstport)) self.dstaddr = dstaddr @@ -1104,7 +1348,7 @@ class NodeConn(asyncore.dispatcher): self.disconnect = False # stuff version msg into sendbuf - vt = msg_version() + vt = msg_version(overwintered) vt.addrTo.ip = self.dstaddr vt.addrTo.port = self.dstport vt.addrFrom.ip = "0.0.0.0" diff --git a/qa/rpc-tests/test_framework/script.py b/qa/rpc-tests/test_framework/script.py index e37ab5d45..55a7f8e51 100644 --- a/qa/rpc-tests/test_framework/script.py +++ b/qa/rpc-tests/test_framework/script.py @@ -24,10 +24,10 @@ if sys.version > '3': bchr = lambda x: bytes([x]) bord = lambda x: x -import copy import struct +import binascii -import test_framework.bignum +from test_framework import bignum MAX_SCRIPT_SIZE = 10000 MAX_SCRIPT_ELEMENT_SIZE = 520 @@ -666,7 +666,7 @@ class CScript(bytes): else: other = CScriptOp.encode_op_pushdata(bignum.bn2vch(other)) elif isinstance(other, (bytes, bytearray)): - other = CScriptOp.encode_op_pushdata(other) + other = bytes(CScriptOp.encode_op_pushdata(other)) return other def __add__(self, other): diff --git a/qa/rpc-tests/test_framework/test_framework.py b/qa/rpc-tests/test_framework/test_framework.py index f44a10a40..56151bb3e 100755 --- a/qa/rpc-tests/test_framework/test_framework.py +++ b/qa/rpc-tests/test_framework/test_framework.py @@ -13,8 +13,11 @@ import shutil import tempfile import traceback -from authproxy import AuthServiceProxy, JSONRPCException -from util import * +from authproxy import JSONRPCException +from util import assert_equal, check_json_precision, \ + initialize_chain, initialize_chain_clean, \ + start_nodes, connect_nodes_bi, stop_nodes, \ + sync_blocks, sync_mempools, wait_bitcoinds class BitcoinTestFramework(object): diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index f5ef9dec6..f3ea481ee 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -21,8 +21,7 @@ import subprocess import time import re -from authproxy import AuthServiceProxy, JSONRPCException -from util import * +from authproxy import AuthServiceProxy def p2p_port(n): return 11000 + n + os.getpid()%999 @@ -153,7 +152,7 @@ def initialize_chain_clean(test_dir, num_nodes): Useful if a test case wants complete control over initialization. """ for i in range(num_nodes): - datadir=initialize_datadir(test_dir, i) + initialize_datadir(test_dir, i) def _rpchost_to_args(rpchost): @@ -214,6 +213,10 @@ def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, binary=None): def log_filename(dirname, n_node, logname): return os.path.join(dirname, "node"+str(n_node), "regtest", logname) +def check_node(i): + bitcoind_processes[i].poll() + return bitcoind_processes[i].returncode + def stop_node(node, i): node.stop() bitcoind_processes[i].wait() @@ -369,3 +372,33 @@ def assert_raises(exc, fun, *args, **kwds): raise AssertionError("Unexpected exception raised: "+type(e).__name__) else: raise AssertionError("No exception raised") + +# Returns txid if operation was a success or None +def wait_and_assert_operationid_status(node, myopid, in_status='success', in_errormsg=None): + print('waiting for async operation {}'.format(myopid)) + opids = [] + opids.append(myopid) + timeout = 300 + status = None + errormsg = None + txid = None + for x in xrange(1, timeout): + results = node.z_getoperationresult(opids) + if len(results)==0: + time.sleep(1) + else: + status = results[0]["status"] + if status == "failed": + errormsg = results[0]['error']['message'] + elif status == "success": + txid = results[0]['result']['txid'] + break + assert_equal(in_status, status) + if errormsg is not None: + assert(in_errormsg is not None) + assert_equal(in_errormsg in errormsg, True) + if os.getenv("PYTHON_DEBUG", ""): + print('...returned status: {}'.format(status)) + if errormsg is not None: + print('...returned error: {}'.format(errormsg)) + return txid diff --git a/qa/rpc-tests/txn_doublespend.py b/qa/rpc-tests/txn_doublespend.py index 32699ad9d..bbe383d16 100755 --- a/qa/rpc-tests/txn_doublespend.py +++ b/qa/rpc-tests/txn_doublespend.py @@ -8,10 +8,9 @@ # from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -from decimal import Decimal -import os -import shutil +from test_framework.util import assert_equal, connect_nodes, \ + sync_blocks, gather_inputs + class TxnMallTest(BitcoinTestFramework): @@ -77,7 +76,7 @@ class TxnMallTest(BitcoinTestFramework): assert_equal(tx2["confirmations"], 0) # Now give doublespend to miner: - mutated_txid = self.nodes[2].sendrawtransaction(doublespend["hex"]) + self.nodes[2].sendrawtransaction(doublespend["hex"]) # ... mine a block... self.nodes[2].generate(1) diff --git a/qa/rpc-tests/wallet.py b/qa/rpc-tests/wallet.py index ff827892a..12dfac0e4 100755 --- a/qa/rpc-tests/wallet.py +++ b/qa/rpc-tests/wallet.py @@ -5,8 +5,13 @@ from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -from time import * +from test_framework.authproxy import JSONRPCException +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_bitcoinds + +import time +from decimal import Decimal class WalletTest (BitcoinTestFramework): @@ -72,6 +77,18 @@ class WalletTest (BitcoinTestFramework): node0utxos = self.nodes[0].listunspent(1) assert_equal(len(node0utxos), 3) + # Check 'generated' field of listunspent + # Node 0: has one coinbase utxo and two regular utxos + assert_equal(sum(int(uxto["generated"] is True) for uxto in node0utxos), 1) + # Node 1: has 101 coinbase utxos and no regular utxos + node1utxos = self.nodes[1].listunspent(1) + assert_equal(len(node1utxos), 101) + assert_equal(sum(int(uxto["generated"] is True) for uxto in node1utxos), 101) + # Node 2: has no coinbase utxos and two regular utxos + node2utxos = self.nodes[2].listunspent(1) + assert_equal(len(node2utxos), 2) + assert_equal(sum(int(uxto["generated"] is True) for uxto in node2utxos), 0) + # create both transactions txns_to_send = [] for utxo in node0utxos: @@ -100,7 +117,7 @@ class WalletTest (BitcoinTestFramework): # Send 10 BTC normal address = self.nodes[0].getnewaddress("") self.nodes[2].settxfee(Decimal('0.001')) - txid = self.nodes[2].sendtoaddress(address, 10, "", "", False) + self.nodes[2].sendtoaddress(address, 10, "", "", False) self.sync_all() self.nodes[2].generate(1) self.sync_all() @@ -110,7 +127,7 @@ class WalletTest (BitcoinTestFramework): assert_equal(self.nodes[0].getbalance("*"), Decimal('10.00000000')) # Send 10 BTC with subtract fee from amount - txid = self.nodes[2].sendtoaddress(address, 10, "", "", True) + self.nodes[2].sendtoaddress(address, 10, "", "", True) self.sync_all() self.nodes[2].generate(1) self.sync_all() @@ -120,7 +137,7 @@ class WalletTest (BitcoinTestFramework): assert_equal(self.nodes[0].getbalance("*"), Decimal('19.99900000')) # Sendmany 10 BTC - txid = self.nodes[2].sendmany("", {address: 10}, 0, "", []) + self.nodes[2].sendmany("", {address: 10}, 0, "", []) self.sync_all() self.nodes[2].generate(1) self.sync_all() @@ -130,7 +147,7 @@ class WalletTest (BitcoinTestFramework): assert_equal(self.nodes[0].getbalance("*"), Decimal('29.99900000')) # Sendmany 10 BTC with subtract fee from amount - txid = self.nodes[2].sendmany("", {address: 10}, 0, "", [address]) + self.nodes[2].sendmany("", {address: 10}, 0, "", [address]) self.sync_all() self.nodes[2].generate(1) self.sync_all() @@ -171,7 +188,7 @@ class WalletTest (BitcoinTestFramework): signedRawTx = self.nodes[1].signrawtransaction(rawTx) decRawTx = self.nodes[1].decoderawtransaction(signedRawTx['hex']) zeroValueTxid= decRawTx['txid'] - sendResp = self.nodes[1].sendrawtransaction(signedRawTx['hex']) + self.nodes[1].sendrawtransaction(signedRawTx['hex']) self.sync_all() self.nodes[1].generate(1) #mine a block @@ -238,7 +255,7 @@ class WalletTest (BitcoinTestFramework): self.sync_all() mybalance = self.nodes[2].z_getbalance(mytaddr) - assert_equal(self.nodes[2].z_getbalance(mytaddr), Decimal('10.0')); + assert_equal(mybalance, Decimal('10.0')); mytxdetails = self.nodes[2].gettransaction(mytxid) myvjoinsplits = mytxdetails["vjoinsplit"] @@ -254,6 +271,16 @@ class WalletTest (BitcoinTestFramework): for i in xrange(0,num_t_recipients): newtaddr = self.nodes[2].getnewaddress() recipients.append({"address":newtaddr, "amount":amount_per_recipient}) + + # Issue #2759 Workaround START + # HTTP connection to node 0 may fall into a state, during the few minutes it takes to process + # loop above to create new addresses, that when z_sendmany is called with a large amount of + # rpc data in recipients, the connection fails with a 'broken pipe' error. Making a RPC call + # to node 0 before calling z_sendmany appears to fix this issue, perhaps putting the HTTP + # connection into a good state to handle a large amount of data in recipients. + self.nodes[0].getinfo() + # Issue #2759 Workaround END + try: self.nodes[0].z_sendmany(myzaddr, recipients) except JSONRPCException,e: @@ -271,6 +298,11 @@ class WalletTest (BitcoinTestFramework): for i in xrange(0,num_z_recipients): newzaddr = self.nodes[2].z_getnewaddress() recipients.append({"address":newzaddr, "amount":amount_per_recipient}) + + # Issue #2759 Workaround START + self.nodes[0].getinfo() + # Issue #2759 Workaround END + try: self.nodes[0].z_sendmany(myzaddr, recipients) except JSONRPCException,e: @@ -306,7 +338,7 @@ class WalletTest (BitcoinTestFramework): for x in xrange(1, timeout): results = self.nodes[2].z_getoperationresult(opids) if len(results)==0: - sleep(1) + time.sleep(1) else: status = results[0]["status"] mytxid = results[0]["result"]["txid"] @@ -363,7 +395,7 @@ class WalletTest (BitcoinTestFramework): for x in xrange(1, timeout): results = self.nodes[2].z_getoperationresult(opids) if len(results)==0: - sleep(1) + time.sleep(1) else: status = results[0]["status"] break diff --git a/qa/rpc-tests/wallet_1941.py b/qa/rpc-tests/wallet_1941.py index 5f2fe3f72..d70b514fc 100755 --- a/qa/rpc-tests/wallet_1941.py +++ b/qa/rpc-tests/wallet_1941.py @@ -6,10 +6,11 @@ # This is a regression test for #1941. from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -from time import * +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 -import sys +from decimal import Decimal starttime = 1388534400 @@ -39,30 +40,6 @@ class Wallet1941RegressionTest (BitcoinTestFramework): connect_nodes_bi(self.nodes, 0, 1) self.sync_all() - def wait_and_assert_operationid_status(self, myopid, in_status='success', in_errormsg=None): - print('waiting for async operation {}'.format(myopid)) - opids = [] - opids.append(myopid) - timeout = 300 - status = None - errormsg = None - for x in xrange(1, timeout): - results = self.nodes[0].z_getoperationresult(opids) - if len(results)==0: - sleep(1) - else: - status = results[0]["status"] - if status == "failed": - errormsg = results[0]['error']['message'] - break - print('...returned status: {}'.format(status)) - print('...error msg: {}'.format(errormsg)) - assert_equal(in_status, status) - if errormsg is not None: - assert(in_errormsg is not None) - assert_equal(in_errormsg in errormsg, True) - print('...returned error: {}'.format(errormsg)) - def run_test (self): print "Mining blocks..." @@ -76,7 +53,7 @@ class Wallet1941RegressionTest (BitcoinTestFramework): recipients = [] recipients.append({"address":myzaddr, "amount":Decimal('10.0') - Decimal('0.0001')}) myopid = self.nodes[0].z_sendmany(mytaddr, recipients) - self.wait_and_assert_operationid_status(myopid) + wait_and_assert_operationid_status(self.nodes[0], myopid) self.nodes[0].generate(1) # Ensure the block times of the latest blocks exceed the variability diff --git a/qa/rpc-tests/wallet_anchorfork.py b/qa/rpc-tests/wallet_anchorfork.py new file mode 100755 index 000000000..a4df66daf --- /dev/null +++ b/qa/rpc-tests/wallet_anchorfork.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python2 +# Copyright (c) 2018 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +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, stop_nodes, connect_nodes_bi, \ + wait_and_assert_operationid_status, wait_bitcoinds +from decimal import Decimal + +class WalletAnchorForkTest (BitcoinTestFramework): + + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 4) + + # Start nodes with -regtestprotectcoinbase to set fCoinbaseMustBeProtected to true. + def setup_network(self, split=False): + self.nodes = start_nodes(3, self.options.tmpdir, extra_args=[['-regtestprotectcoinbase', '-debug=zrpc']] * 3 ) + connect_nodes_bi(self.nodes,0,1) + connect_nodes_bi(self.nodes,1,2) + connect_nodes_bi(self.nodes,0,2) + self.is_network_split=False + self.sync_all() + + def run_test (self): + print "Mining blocks..." + self.nodes[0].generate(4) + + walletinfo = self.nodes[0].getwalletinfo() + assert_equal(walletinfo['immature_balance'], 40) + assert_equal(walletinfo['balance'], 0) + + self.sync_all() + self.nodes[1].generate(102) + self.sync_all() + + assert_equal(self.nodes[0].getbalance(), 40) + assert_equal(self.nodes[1].getbalance(), 20) + assert_equal(self.nodes[2].getbalance(), 0) + + # At this point in time, commitment tree is the empty root + + # Node 0 creates a joinsplit transaction + mytaddr0 = self.nodes[0].getnewaddress() + myzaddr0 = self.nodes[0].z_getnewaddress() + recipients = [] + recipients.append({"address":myzaddr0, "amount": Decimal('10.0') - Decimal('0.0001')}) + myopid = self.nodes[0].z_sendmany(mytaddr0, recipients) + wait_and_assert_operationid_status(self.nodes[0], myopid) + + # Sync up mempools and mine the transaction. All nodes have the same anchor. + self.sync_all() + self.nodes[0].generate(1) + self.sync_all() + + # Stop nodes. + stop_nodes(self.nodes) + wait_bitcoinds() + + # Relaunch nodes and partition network into two: + # A: node 0 + # B: node 1, 2 + self.nodes = start_nodes(3, self.options.tmpdir, extra_args=[['-regtestprotectcoinbase', '-debug=zrpc']] * 3 ) + connect_nodes_bi(self.nodes,1,2) + + # Partition B, node 1 mines an empty block + self.nodes[1].generate(1) + + # Partition A, node 0 creates a joinsplit transaction + recipients = [] + recipients.append({"address":myzaddr0, "amount": Decimal('10.0') - Decimal('0.0001')}) + myopid = self.nodes[0].z_sendmany(mytaddr0, recipients) + txid = wait_and_assert_operationid_status(self.nodes[0], myopid) + rawhex = self.nodes[0].getrawtransaction(txid) + + # Partition A, node 0 mines a block with the transaction + self.nodes[0].generate(1) + + # Partition B, node 1 mines the same joinsplit transaction + txid2 = self.nodes[1].sendrawtransaction(rawhex) + assert_equal(txid, txid2) + self.nodes[1].generate(1) + + # Check that Partition B is one block ahead and that they have different tips + assert_equal(self.nodes[0].getblockcount() + 1, self.nodes[1].getblockcount()) + assert( self.nodes[0].getbestblockhash() != self.nodes[1].getbestblockhash()) + + # Shut down all nodes so any in-memory state is saved to disk + stop_nodes(self.nodes) + wait_bitcoinds() + + # Relaunch nodes and reconnect the entire network + self.nodes = start_nodes(3, self.options.tmpdir, extra_args=[['-regtestprotectcoinbase', '-debug=zrpc']] * 3 ) + connect_nodes_bi(self.nodes,0, 1) + connect_nodes_bi(self.nodes,1, 2) + connect_nodes_bi(self.nodes,0, 2) + + # Mine a new block and let it propagate + self.nodes[1].generate(1) + + # Due to a bug in v1.0.0-1.0.3, node 0 will die with a tree root assertion, so sync_all() will throw an exception. + self.sync_all() + + # v1.0.4 will reach here safely + assert_equal( self.nodes[0].getbestblockhash(), self.nodes[1].getbestblockhash()) + assert_equal( self.nodes[1].getbestblockhash(), self.nodes[2].getbestblockhash()) + +if __name__ == '__main__': + WalletAnchorForkTest().main() diff --git a/qa/rpc-tests/wallet_mergetoaddress.py b/qa/rpc-tests/wallet_mergetoaddress.py new file mode 100755 index 000000000..e5d5089a4 --- /dev/null +++ b/qa/rpc-tests/wallet_mergetoaddress.py @@ -0,0 +1,366 @@ +#!/usr/bin/env python2 +# Copyright (c) 2017 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.test_framework import BitcoinTestFramework +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 + +from decimal import Decimal + +class WalletMergeToAddressTest (BitcoinTestFramework): + + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 4) + + def setup_network(self, split=False): + args = ['-debug=zrpcunsafe', '-experimentalfeatures', '-zmergetoaddress'] + self.nodes = [] + self.nodes.append(start_node(0, self.options.tmpdir, args)) + self.nodes.append(start_node(1, self.options.tmpdir, args)) + args2 = ['-debug=zrpcunsafe', '-experimentalfeatures', '-zmergetoaddress', '-mempooltxinputlimit=7'] + self.nodes.append(start_node(2, self.options.tmpdir, args2)) + connect_nodes_bi(self.nodes,0,1) + connect_nodes_bi(self.nodes,1,2) + connect_nodes_bi(self.nodes,0,2) + self.is_network_split=False + self.sync_all() + + def run_test (self): + print "Mining blocks..." + + self.nodes[0].generate(1) + do_not_shield_taddr = self.nodes[0].getnewaddress() + + self.nodes[0].generate(4) + walletinfo = self.nodes[0].getwalletinfo() + assert_equal(walletinfo['immature_balance'], 50) + assert_equal(walletinfo['balance'], 0) + self.sync_all() + self.nodes[2].generate(1) + self.nodes[2].getnewaddress() + self.nodes[2].generate(1) + self.nodes[2].getnewaddress() + self.nodes[2].generate(1) + self.sync_all() + self.nodes[1].generate(101) + self.sync_all() + assert_equal(self.nodes[0].getbalance(), 50) + assert_equal(self.nodes[1].getbalance(), 10) + assert_equal(self.nodes[2].getbalance(), 30) + + # Shield the coinbase + myzaddr = self.nodes[0].z_getnewaddress() + result = self.nodes[0].z_shieldcoinbase("*", myzaddr, 0) + wait_and_assert_operationid_status(self.nodes[0], result['opid']) + self.sync_all() + self.nodes[1].generate(1) + self.sync_all() + + # Prepare some UTXOs and notes for merging + mytaddr = self.nodes[0].getnewaddress() + mytaddr2 = self.nodes[0].getnewaddress() + mytaddr3 = self.nodes[0].getnewaddress() + result = self.nodes[0].z_sendmany(myzaddr, [ + {'address': do_not_shield_taddr, 'amount': 10}, + {'address': mytaddr, 'amount': 10}, + {'address': mytaddr2, 'amount': 10}, + {'address': mytaddr3, 'amount': 10}, + ], 1, 0) + wait_and_assert_operationid_status(self.nodes[0], result) + self.sync_all() + self.nodes[1].generate(1) + self.sync_all() + + # Merging will fail because from arguments need to be in an array + try: + self.nodes[0].z_mergetoaddress("*", myzaddr) + assert(False) + except JSONRPCException,e: + errorString = e.error['message'] + assert_equal("JSON value is not an array as expected" in errorString, True) + + # Merging will fail when trying to spend from watch-only address + self.nodes[2].importaddress(mytaddr) + try: + self.nodes[2].z_mergetoaddress([mytaddr], myzaddr) + assert(False) + except JSONRPCException,e: + errorString = e.error['message'] + assert_equal("Could not find any funds to merge" in errorString, True) + + # Merging will fail because fee is negative + try: + self.nodes[0].z_mergetoaddress(["*"], myzaddr, -1) + assert(False) + except JSONRPCException,e: + errorString = e.error['message'] + assert_equal("Amount out of range" in errorString, True) + + # Merging will fail because fee is larger than MAX_MONEY + try: + self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('21000000.00000001')) + assert(False) + except JSONRPCException,e: + errorString = e.error['message'] + assert_equal("Amount out of range" in errorString, True) + + # Merging will fail because fee is larger than sum of UTXOs + try: + self.nodes[0].z_mergetoaddress(["*"], myzaddr, 999) + assert(False) + except JSONRPCException,e: + errorString = e.error['message'] + assert_equal("Insufficient funds" in errorString, True) + + # Merging will fail because transparent limit parameter must be at least 0 + try: + self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), -1) + assert(False) + except JSONRPCException,e: + errorString = e.error['message'] + assert_equal("Limit on maximum number of UTXOs cannot be negative" in errorString, True) + + # Merging will fail because transparent limit parameter is absurdly large + try: + self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), 99999999999999) + assert(False) + except JSONRPCException,e: + errorString = e.error['message'] + assert_equal("JSON integer out of range" in errorString, True) + + # Merging will fail because shielded limit parameter must be at least 0 + try: + self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), 50, -1) + assert(False) + except JSONRPCException,e: + errorString = e.error['message'] + assert_equal("Limit on maximum number of notes cannot be negative" in errorString, True) + + # Merging will fail because shielded limit parameter is absurdly large + try: + self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), 50, 99999999999999) + assert(False) + except JSONRPCException,e: + errorString = e.error['message'] + assert_equal("JSON integer out of range" in errorString, True) + + # Merging will fail for this specific case where it would spend a fee and do nothing + try: + self.nodes[0].z_mergetoaddress([mytaddr], mytaddr) + assert(False) + except JSONRPCException,e: + errorString = e.error['message'] + assert_equal("Destination address is also the only source address, and all its funds are already merged" in errorString, True) + + # Merge UTXOs from node 0 of value 30, standard fee of 0.00010000 + result = self.nodes[0].z_mergetoaddress([mytaddr, mytaddr2, mytaddr3], myzaddr) + wait_and_assert_operationid_status(self.nodes[0], result['opid']) + self.sync_all() + self.nodes[1].generate(1) + self.sync_all() + + # Confirm balances and that do_not_shield_taddr containing funds of 10 was left alone + assert_equal(self.nodes[0].getbalance(), 10) + assert_equal(self.nodes[0].z_getbalance(do_not_shield_taddr), Decimal('10.0')) + assert_equal(self.nodes[0].z_getbalance(myzaddr), Decimal('39.99990000')) + assert_equal(self.nodes[1].getbalance(), 40) + assert_equal(self.nodes[2].getbalance(), 30) + + # Shield all notes to another z-addr + myzaddr2 = self.nodes[0].z_getnewaddress() + result = self.nodes[0].z_mergetoaddress(["ANY_ZADDR"], myzaddr2, 0) + assert_equal(result["mergingUTXOs"], Decimal('0')) + assert_equal(result["remainingUTXOs"], Decimal('0')) + assert_equal(result["mergingNotes"], Decimal('2')) + assert_equal(result["remainingNotes"], Decimal('0')) + wait_and_assert_operationid_status(self.nodes[0], result['opid']) + self.sync_all() + blockhash = self.nodes[1].generate(1) + self.sync_all() + + assert_equal(len(self.nodes[0].getblock(blockhash[0])['tx']), 2) + assert_equal(self.nodes[0].z_getbalance(myzaddr), 0) + assert_equal(self.nodes[0].z_getbalance(myzaddr2), Decimal('39.99990000')) + + # Shield coinbase UTXOs from any node 2 taddr, and set fee to 0 + result = self.nodes[2].z_shieldcoinbase("*", myzaddr, 0) + wait_and_assert_operationid_status(self.nodes[2], result['opid']) + self.sync_all() + self.nodes[1].generate(1) + self.sync_all() + + assert_equal(self.nodes[0].getbalance(), 10) + assert_equal(self.nodes[0].z_getbalance(myzaddr), Decimal('30')) + assert_equal(self.nodes[0].z_getbalance(myzaddr2), Decimal('39.99990000')) + assert_equal(self.nodes[1].getbalance(), 60) + assert_equal(self.nodes[2].getbalance(), 0) + + # Merge all notes from node 0 into a node 0 taddr, and set fee to 0 + result = self.nodes[0].z_mergetoaddress(["ANY_ZADDR"], mytaddr, 0) + wait_and_assert_operationid_status(self.nodes[0], result['opid']) + self.sync_all() + self.nodes[1].generate(1) + self.sync_all() + + assert_equal(self.nodes[0].getbalance(), Decimal('79.99990000')) + assert_equal(self.nodes[0].z_getbalance(do_not_shield_taddr), Decimal('10.0')) + assert_equal(self.nodes[0].z_getbalance(mytaddr), Decimal('69.99990000')) + assert_equal(self.nodes[0].z_getbalance(myzaddr), 0) + assert_equal(self.nodes[0].z_getbalance(myzaddr2), 0) + assert_equal(self.nodes[1].getbalance(), 70) + assert_equal(self.nodes[2].getbalance(), 0) + + # Merge all node 0 UTXOs together into a node 1 taddr, and set fee to 0 + self.nodes[1].getnewaddress() # Ensure we have an empty address + n1taddr = self.nodes[1].getnewaddress() + result = self.nodes[0].z_mergetoaddress(["ANY_TADDR"], n1taddr, 0) + wait_and_assert_operationid_status(self.nodes[0], result['opid']) + self.sync_all() + self.nodes[1].generate(1) + self.sync_all() + + assert_equal(self.nodes[0].getbalance(), 0) + assert_equal(self.nodes[0].z_getbalance(do_not_shield_taddr), 0) + assert_equal(self.nodes[0].z_getbalance(mytaddr), 0) + assert_equal(self.nodes[0].z_getbalance(myzaddr), 0) + assert_equal(self.nodes[1].getbalance(), Decimal('159.99990000')) + assert_equal(self.nodes[1].z_getbalance(n1taddr), Decimal('79.99990000')) + assert_equal(self.nodes[2].getbalance(), 0) + + # Generate 800 regular UTXOs on node 0, and 20 regular UTXOs on node 2 + mytaddr = self.nodes[0].getnewaddress() + n2taddr = self.nodes[2].getnewaddress() + self.nodes[1].generate(1000) + self.sync_all() + for i in range(800): + self.nodes[1].sendtoaddress(mytaddr, 1) + for i in range(20): + self.nodes[1].sendtoaddress(n2taddr, 1) + self.nodes[1].generate(1) + self.sync_all() + + # Merging the 800 UTXOs will occur over two transactions, since max tx size is 100,000 bytes. + # We don't verify mergingTransparentValue as UTXOs are not selected in any specific order, so value can change on each test run. + # We set an unrealistically high limit parameter of 99999, to verify that max tx size will constrain the number of UTXOs. + result = self.nodes[0].z_mergetoaddress([mytaddr], myzaddr, 0, 99999) + assert_equal(result["mergingUTXOs"], Decimal('662')) + assert_equal(result["remainingUTXOs"], Decimal('138')) + assert_equal(result["mergingNotes"], Decimal('0')) + assert_equal(result["mergingShieldedValue"], Decimal('0')) + assert_equal(result["remainingNotes"], Decimal('0')) + assert_equal(result["remainingShieldedValue"], Decimal('0')) + remainingTransparentValue = result["remainingTransparentValue"] + opid1 = result['opid'] + + # Verify that UTXOs are locked (not available for selection) by queuing up another merging operation + result = self.nodes[0].z_mergetoaddress([mytaddr], myzaddr, 0, 0) + assert_equal(result["mergingUTXOs"], Decimal('138')) + assert_equal(result["mergingTransparentValue"], Decimal(remainingTransparentValue)) + assert_equal(result["remainingUTXOs"], Decimal('0')) + assert_equal(result["remainingTransparentValue"], Decimal('0')) + assert_equal(result["mergingNotes"], Decimal('0')) + assert_equal(result["mergingShieldedValue"], Decimal('0')) + assert_equal(result["remainingNotes"], Decimal('0')) + assert_equal(result["remainingShieldedValue"], Decimal('0')) + opid2 = result['opid'] + + # wait for both aysnc operations to complete + wait_and_assert_operationid_status(self.nodes[0], opid1) + wait_and_assert_operationid_status(self.nodes[0], opid2) + + # sync_all() invokes sync_mempool() but node 2's mempool limit will cause tx1 and tx2 to be rejected. + # So instead, we sync on blocks and mempool for node 0 and node 1, and after a new block is generated + # which mines tx1 and tx2, all nodes will have an empty mempool which can then be synced. + sync_blocks(self.nodes[:2]) + sync_mempools(self.nodes[:2]) + # Generate enough blocks to ensure all transactions are mined + while self.nodes[1].getmempoolinfo()['size'] > 0: + self.nodes[1].generate(1) + self.sync_all() + + # Verify maximum number of UTXOs which node 2 can shield is limited by option -mempooltxinputlimit + # This option is used when the limit parameter is set to 0. + result = self.nodes[2].z_mergetoaddress([n2taddr], myzaddr, Decimal('0.0001'), 0) + assert_equal(result["mergingUTXOs"], Decimal('7')) + assert_equal(result["remainingUTXOs"], Decimal('13')) + assert_equal(result["mergingNotes"], Decimal('0')) + assert_equal(result["remainingNotes"], Decimal('0')) + wait_and_assert_operationid_status(self.nodes[2], result['opid']) + self.sync_all() + self.nodes[1].generate(1) + self.sync_all() + + # Verify maximum number of UTXOs which node 0 can shield is set by default limit parameter of 50 + mytaddr = self.nodes[0].getnewaddress() + for i in range(100): + self.nodes[1].sendtoaddress(mytaddr, 1) + self.nodes[1].generate(1) + self.sync_all() + result = self.nodes[0].z_mergetoaddress([mytaddr], myzaddr, Decimal('0.0001')) + assert_equal(result["mergingUTXOs"], Decimal('50')) + assert_equal(result["remainingUTXOs"], Decimal('50')) + assert_equal(result["mergingNotes"], Decimal('0')) + # Remaining notes are only counted if we are trying to merge any notes + assert_equal(result["remainingNotes"], Decimal('0')) + 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_mergetoaddress([mytaddr], myzaddr, Decimal('0.0001'), 33) + assert_equal(result["mergingUTXOs"], Decimal('33')) + assert_equal(result["remainingUTXOs"], Decimal('17')) + assert_equal(result["mergingNotes"], Decimal('0')) + # Remaining notes are only counted if we are trying to merge any notes + assert_equal(result["remainingNotes"], Decimal('0')) + wait_and_assert_operationid_status(self.nodes[0], result['opid']) + # Don't sync node 2 which rejects the tx due to its mempooltxinputlimit + sync_blocks(self.nodes[:2]) + sync_mempools(self.nodes[:2]) + self.nodes[1].generate(1) + self.sync_all() + + # Verify maximum number of notes which node 0 can shield can be set by the limit parameter + # Also check that we can set off a second merge before the first one is complete + + # myzaddr has 5 notes at this point + result1 = self.nodes[0].z_mergetoaddress([myzaddr], myzaddr, 0.0001, 50, 2) + result2 = self.nodes[0].z_mergetoaddress([myzaddr], myzaddr, 0.0001, 50, 2) + + # First merge should select from all notes + assert_equal(result1["mergingUTXOs"], Decimal('0')) + # Remaining UTXOs are only counted if we are trying to merge any UTXOs + assert_equal(result1["remainingUTXOs"], Decimal('0')) + assert_equal(result1["mergingNotes"], Decimal('2')) + assert_equal(result1["remainingNotes"], Decimal('3')) + + # Second merge should ignore locked notes + assert_equal(result2["mergingUTXOs"], Decimal('0')) + assert_equal(result2["remainingUTXOs"], Decimal('0')) + assert_equal(result2["mergingNotes"], Decimal('2')) + assert_equal(result2["remainingNotes"], Decimal('1')) + wait_and_assert_operationid_status(self.nodes[0], result1['opid']) + wait_and_assert_operationid_status(self.nodes[0], result2['opid']) + + self.sync_all() + self.nodes[1].generate(1) + self.sync_all() + + # Shield both UTXOs and notes to a z-addr + result = self.nodes[0].z_mergetoaddress(["*"], myzaddr, 0, 10, 2) + assert_equal(result["mergingUTXOs"], Decimal('10')) + assert_equal(result["remainingUTXOs"], Decimal('7')) + assert_equal(result["mergingNotes"], Decimal('2')) + assert_equal(result["remainingNotes"], Decimal('1')) + wait_and_assert_operationid_status(self.nodes[0], result['opid']) + # Don't sync node 2 which rejects the tx due to its mempooltxinputlimit + sync_blocks(self.nodes[:2]) + sync_mempools(self.nodes[:2]) + self.nodes[1].generate(1) + self.sync_all() + +if __name__ == '__main__': + WalletMergeToAddressTest().main() diff --git a/qa/rpc-tests/wallet_nullifiers.py b/qa/rpc-tests/wallet_nullifiers.py index 93f5a499d..207631efb 100755 --- a/qa/rpc-tests/wallet_nullifiers.py +++ b/qa/rpc-tests/wallet_nullifiers.py @@ -5,8 +5,11 @@ from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -from time import * +from test_framework.util import assert_equal, start_node, \ + start_nodes, connect_nodes_bi, bitcoind_processes + +import time +from decimal import Decimal class WalletNullifiersTest (BitcoinTestFramework): @@ -32,7 +35,7 @@ class WalletNullifiersTest (BitcoinTestFramework): for x in xrange(1, timeout): results = self.nodes[0].z_getoperationresult(opids) if len(results)==0: - sleep(1) + time.sleep(1) else: status = results[0]["status"] assert_equal("success", status) @@ -73,7 +76,7 @@ class WalletNullifiersTest (BitcoinTestFramework): for x in xrange(1, timeout): results = self.nodes[0].z_getoperationresult(opids) if len(results)==0: - sleep(1) + time.sleep(1) else: status = results[0]["status"] assert_equal("success", status) @@ -105,7 +108,7 @@ class WalletNullifiersTest (BitcoinTestFramework): for x in xrange(1, timeout): results = self.nodes[2].z_getoperationresult(opids) if len(results)==0: - sleep(1) + time.sleep(1) else: status = results[0]["status"] assert_equal("success", status) @@ -146,11 +149,12 @@ class WalletNullifiersTest (BitcoinTestFramework): for x in xrange(1, timeout): results = self.nodes[1].z_getoperationresult(opids) if len(results)==0: - sleep(1) + time.sleep(1) else: status = results[0]["status"] assert_equal("success", status) mytxid = results[0]["result"]["txid"] + [mytxid] # hush pyflakes break self.sync_all() @@ -166,5 +170,50 @@ class WalletNullifiersTest (BitcoinTestFramework): assert_equal(self.nodes[1].z_getbalance(myzaddr), zaddrremaining2) assert_equal(self.nodes[2].z_getbalance(myzaddr), zaddrremaining2) + # Test viewing keys + + node3mined = Decimal('250.0') + assert_equal({k: Decimal(v) for k, v in self.nodes[3].z_gettotalbalance().items()}, { + 'transparent': node3mined, + 'private': zsendmany2notevalue, + 'total': node3mined + zsendmany2notevalue, + }) + + # add node 1 address and node 2 viewing key to node 3 + myzvkey = self.nodes[2].z_exportviewingkey(myzaddr) + self.nodes[3].importaddress(mytaddr1) + self.nodes[3].z_importviewingkey(myzvkey, 'whenkeyisnew', 1) + + # Check the address has been imported + assert_equal(myzaddr in self.nodes[3].z_listaddresses(), False) + assert_equal(myzaddr in self.nodes[3].z_listaddresses(True), True) + + # Node 3 should see the same received notes as node 2 + assert_equal( + self.nodes[2].z_listreceivedbyaddress(myzaddr), + self.nodes[3].z_listreceivedbyaddress(myzaddr)) + + # Node 3's balances should be unchanged without explicitly requesting + # to include watch-only balances + assert_equal({k: Decimal(v) for k, v in self.nodes[3].z_gettotalbalance().items()}, { + 'transparent': node3mined, + 'private': zsendmany2notevalue, + 'total': node3mined + zsendmany2notevalue, + }) + + # Wallet can't cache nullifiers for notes received by addresses it only has a + # viewing key for, and therefore can't detect spends. So it sees a balance + # corresponding to the sum of all notes the address received. + # TODO: Fix this during the Sapling upgrade (via #2277) + assert_equal({k: Decimal(v) for k, v in self.nodes[3].z_gettotalbalance(1, True).items()}, { + 'transparent': node3mined + Decimal('1.0'), + 'private': zsendmany2notevalue + zsendmanynotevalue + zaddrremaining + zaddrremaining2, + 'total': node3mined + Decimal('1.0') + zsendmany2notevalue + zsendmanynotevalue + zaddrremaining + zaddrremaining2, + }) + + # Check individual balances reflect the above + assert_equal(self.nodes[3].z_getbalance(mytaddr1), Decimal('1.0')) + assert_equal(self.nodes[3].z_getbalance(myzaddr), zsendmanynotevalue + zaddrremaining + zaddrremaining2) + if __name__ == '__main__': WalletNullifiersTest().main () diff --git a/qa/rpc-tests/wallet_overwintertx.py b/qa/rpc-tests/wallet_overwintertx.py new file mode 100755 index 000000000..61932fece --- /dev/null +++ b/qa/rpc-tests/wallet_overwintertx.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python2 +# Copyright (c) 2018 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +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 + +from decimal import Decimal + +class WalletOverwinterTxTest (BitcoinTestFramework): + + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 4) + + def setup_network(self, split=False): + self.nodes = start_nodes(4, self.options.tmpdir, extra_args=[["-nuparams=5ba81b19:200", "-debug=zrpcunsafe", "-txindex"]] * 4 ) + connect_nodes_bi(self.nodes,0,1) + connect_nodes_bi(self.nodes,1,2) + connect_nodes_bi(self.nodes,0,2) + connect_nodes_bi(self.nodes,0,3) + self.is_network_split=False + self.sync_all() + + def run_test (self): + self.nodes[0].generate(100) + self.sync_all() + self.nodes[1].generate(98) + self.sync_all() + # Node 0 has reward from blocks 1 to 98 which are spendable. + + taddr0 = self.nodes[0].getnewaddress() + taddr1 = self.nodes[1].getnewaddress() + taddr2 = self.nodes[2].getnewaddress() + zaddr2 = self.nodes[2].z_getnewaddress() + taddr3 = self.nodes[3].getnewaddress() + zaddr3 = self.nodes[3].z_getnewaddress() + + # + # Currently at block 198. The next block to be mined 199 is a Sprout block + # + bci = self.nodes[0].getblockchaininfo() + assert_equal(bci['consensus']['chaintip'], '00000000') + assert_equal(bci['consensus']['nextblock'], '00000000') + assert_equal(bci['upgrades']['5ba81b19']['status'], 'pending') + + # Node 0 sends transparent funds to Node 2 + tsendamount = Decimal('1.0') + txid_transparent = self.nodes[0].sendtoaddress(taddr2, tsendamount) + self.sync_all() + + # Node 2 sends the zero-confirmation transparent funds to Node 1 using z_sendmany + recipients = [] + recipients.append({"address":taddr1, "amount": Decimal('0.5')}) + 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') + recipients = [] + recipients.append({"address":zaddr2, "amount": zsendamount}) + myopid = self.nodes[0].z_sendmany(taddr0, recipients) + txid_shielded = wait_and_assert_operationid_status(self.nodes[0], myopid) + + self.sync_all() + self.nodes[0].generate(1) + self.sync_all() + + # 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].z_getbalance(zaddr2), zsendamount) + + # Verify transaction versions are 1 or 2 (intended for Sprout) + result = self.nodes[0].getrawtransaction(txid_transparent, 1) + assert_equal(result["version"], 1) + assert_equal(result["overwintered"], False) + result = self.nodes[0].getrawtransaction(txid_zsendmany, 1) + assert_equal(result["version"], 1) + assert_equal(result["overwintered"], False) + result = self.nodes[0].getrawtransaction(txid_shielded, 1) + assert_equal(result["version"], 2) + assert_equal(result["overwintered"], False) + + # + # Currently at block 199. The next block to be mined 200 is an Overwinter block + # + bci = self.nodes[0].getblockchaininfo() + assert_equal(bci['consensus']['chaintip'], '00000000') + assert_equal(bci['consensus']['nextblock'], '5ba81b19') + assert_equal(bci['upgrades']['5ba81b19']['status'], 'pending') + + # Node 0 sends transparent funds to Node 3 + tsendamount = Decimal('1.0') + txid_transparent = self.nodes[0].sendtoaddress(taddr3, tsendamount) + self.sync_all() + + # Node 3 sends the zero-confirmation transparent funds to Node 1 using z_sendmany + recipients = [] + recipients.append({"address":taddr1, "amount": Decimal('0.5')}) + 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') + recipients = [] + recipients.append({"address":zaddr3, "amount": zsendamount}) + myopid = self.nodes[0].z_sendmany(taddr0, recipients) + txid_shielded = wait_and_assert_operationid_status(self.nodes[0], myopid) + + # Mine the first Overwinter block + self.sync_all() + self.nodes[0].generate(1) + self.sync_all() + bci = self.nodes[0].getblockchaininfo() + assert_equal(bci['consensus']['chaintip'], '5ba81b19') + assert_equal(bci['consensus']['nextblock'], '5ba81b19') + assert_equal(bci['upgrades']['5ba81b19']['status'], 'active') + + # 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].z_getbalance(zaddr3), zsendamount) + + # Verify transaction version is 3 (intended for Overwinter) + result = self.nodes[0].getrawtransaction(txid_transparent, 1) + assert_equal(result["version"], 3) + assert_equal(result["overwintered"], True) + assert_equal(result["versiongroupid"], "03c48270") + result = self.nodes[0].getrawtransaction(txid_zsendmany, 1) + assert_equal(result["version"], 3) + assert_equal(result["overwintered"], True) + assert_equal(result["versiongroupid"], "03c48270") + result = self.nodes[0].getrawtransaction(txid_shielded, 1) + assert_equal(result["version"], 3) + assert_equal(result["overwintered"], True) + assert_equal(result["versiongroupid"], "03c48270") + +if __name__ == '__main__': + WalletOverwinterTxTest().main() diff --git a/qa/rpc-tests/wallet_protectcoinbase.py b/qa/rpc-tests/wallet_protectcoinbase.py index 1ed0d5b1f..afe851a16 100755 --- a/qa/rpc-tests/wallet_protectcoinbase.py +++ b/qa/rpc-tests/wallet_protectcoinbase.py @@ -5,8 +5,26 @@ from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -from time import * +from test_framework.authproxy import JSONRPCException +from test_framework.mininode import COIN +from test_framework.util import assert_equal, initialize_chain_clean, \ + start_nodes, connect_nodes_bi, stop_node, wait_and_assert_operationid_status + +import sys +import time +import timeit +from decimal import Decimal + +def check_value_pool(node, name, total): + value_pools = node.getblockchaininfo()['valuePools'] + found = False + for pool in value_pools: + if pool['id'] == name: + found = True + assert_equal(pool['monitored'], True) + assert_equal(pool['chainValue'], total) + assert_equal(pool['chainValueZat'], total * COIN) + assert(found) class WalletProtectCoinbaseTest (BitcoinTestFramework): @@ -16,41 +34,14 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): # Start nodes with -regtestprotectcoinbase to set fCoinbaseMustBeProtected to true. def setup_network(self, split=False): - self.nodes = start_nodes(3, self.options.tmpdir, extra_args=[['-regtestprotectcoinbase', '-debug=zrpcunsafe']] * 3 ) + self.nodes = start_nodes(4, self.options.tmpdir, extra_args=[['-regtestprotectcoinbase', '-debug=zrpcunsafe']] * 4 ) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) + connect_nodes_bi(self.nodes,0,3) self.is_network_split=False self.sync_all() - # Returns txid if operation was a success or None - def wait_and_assert_operationid_status(self, myopid, in_status='success', in_errormsg=None): - print('waiting for async operation {}'.format(myopid)) - opids = [] - opids.append(myopid) - timeout = 300 - status = None - errormsg = None - txid = None - for x in xrange(1, timeout): - results = self.nodes[0].z_getoperationresult(opids) - if len(results)==0: - sleep(1) - else: - status = results[0]["status"] - if status == "failed": - errormsg = results[0]['error']['message'] - elif status == "success": - txid = results[0]['result']['txid'] - break - print('...returned status: {}'.format(status)) - assert_equal(in_status, status) - if errormsg is not None: - assert(in_errormsg is not None) - assert_equal(in_errormsg in errormsg, True) - print('...returned error: {}'.format(errormsg)) - return txid - def run_test (self): print "Mining blocks..." @@ -67,6 +58,12 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): assert_equal(self.nodes[0].getbalance(), 40) assert_equal(self.nodes[1].getbalance(), 10) assert_equal(self.nodes[2].getbalance(), 0) + assert_equal(self.nodes[3].getbalance(), 0) + + check_value_pool(self.nodes[0], 'sprout', 0) + check_value_pool(self.nodes[1], 'sprout', 0) + check_value_pool(self.nodes[2], 'sprout', 0) + check_value_pool(self.nodes[3], 'sprout', 0) # Send will fail because we are enforcing the consensus rule that # coinbase utxos can only be sent to a zaddr. @@ -81,6 +78,27 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): mytaddr = self.nodes[0].getnewaddress() myzaddr = self.nodes[0].z_getnewaddress() + # Node 3 will test that watch only address utxos are not selected + self.nodes[3].importaddress(mytaddr) + recipients= [{"address":myzaddr, "amount": Decimal('1')}] + myopid = self.nodes[3].z_sendmany(mytaddr, recipients) + errorString="" + status = None + opids = [myopid] + timeout = 10 + for x in xrange(1, timeout): + results = self.nodes[3].z_getoperationresult(opids) + if len(results)==0: + time.sleep(1) + else: + status = results[0]["status"] + errorString = results[0]["error"]["message"] + break + assert_equal("failed", status) + assert_equal("no UTXOs found for taddr from address" in errorString, True) + stop_node(self.nodes[3], 3) + self.nodes.pop() + # This send will fail because our wallet does not allow any change when protecting a coinbase utxo, # as it's currently not possible to specify a change address in z_sendmany. recipients = [] @@ -94,7 +112,7 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): for x in xrange(1, timeout): results = self.nodes[0].z_getoperationresult(opids) if len(results)==0: - sleep(1) + time.sleep(1) else: status = results[0]["status"] errorString = results[0]["error"]["message"] @@ -112,10 +130,11 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): assert_equal("wallet does not allow any change" in errorString, True) # This send will succeed. We send two coinbase utxos totalling 20.0 less a fee of 0.00010000, with no change. + shieldvalue = Decimal('20.0') - Decimal('0.0001') recipients = [] - recipients.append({"address":myzaddr, "amount": Decimal('20.0') - Decimal('0.0001')}) + recipients.append({"address":myzaddr, "amount": shieldvalue}) myopid = self.nodes[0].z_sendmany(mytaddr, recipients) - mytxid = self.wait_and_assert_operationid_status(myopid) + mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid) self.sync_all() self.nodes[1].generate(1) self.sync_all() @@ -140,11 +159,15 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): assert_equal(Decimal(resp["private"]), Decimal('19.9999')) assert_equal(Decimal(resp["total"]), Decimal('39.9999')) + # The Sprout value pool should reflect the send + sproutvalue = shieldvalue + check_value_pool(self.nodes[0], 'sprout', sproutvalue) + # 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('19.9999')}) myopid = self.nodes[0].z_sendmany(myzaddr, recipients, 1, Decimal('0.0')) - mytxid = self.wait_and_assert_operationid_status(myopid) + mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid) self.sync_all() self.nodes[1].generate(1) self.sync_all() @@ -153,11 +176,15 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): assert_equal(Decimal(resp["private"]), Decimal('19.9999')) assert_equal(Decimal(resp["total"]), Decimal('39.9999')) + # The Sprout value pool should be unchanged + check_value_pool(self.nodes[0], 'sprout', sproutvalue) + # convert note to transparent funds + unshieldvalue = Decimal('10.0') recipients = [] - recipients.append({"address":mytaddr, "amount":Decimal('10.0')}) + recipients.append({"address":mytaddr, "amount": unshieldvalue}) myopid = self.nodes[0].z_sendmany(myzaddr, recipients) - mytxid = self.wait_and_assert_operationid_status(myopid) + mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid) assert(mytxid is not None) self.sync_all() @@ -169,10 +196,12 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): self.sync_all() # check balances + sproutvalue -= unshieldvalue + Decimal('0.0001') resp = self.nodes[0].z_gettotalbalance() assert_equal(Decimal(resp["transparent"]), Decimal('30.0')) assert_equal(Decimal(resp["private"]), Decimal('9.9998')) assert_equal(Decimal(resp["total"]), Decimal('39.9998')) + 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. @@ -181,7 +210,7 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): amount = Decimal('10.0') - Decimal('0.00010000') - 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) - self.wait_and_assert_operationid_status(myopid, "failed", "Insufficient transparent funds, have 10.00, need 0.00000053 more to avoid creating invalid change output 0.00000001 (dust threshold is 0.00000054)") + wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient transparent funds, have 10.00, need 0.00000053 more to avoid creating invalid change output 0.00000001 (dust threshold is 0.00000054)") # Send will fail because send amount is too big, even when including coinbase utxos errorString = "" @@ -195,9 +224,9 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): recipients = [] recipients.append({"address":self.nodes[1].getnewaddress(), "amount":Decimal('10000.0')}) myopid = self.nodes[0].z_sendmany(mytaddr, recipients) - self.wait_and_assert_operationid_status(myopid, "failed", "Insufficient transparent funds, have 10.00, need 10000.0001") + wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient transparent funds, have 10.00, need 10000.0001") myopid = self.nodes[0].z_sendmany(myzaddr, recipients) - self.wait_and_assert_operationid_status(myopid, "failed", "Insufficient protected funds, have 9.9998, need 10000.0001") + wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient protected funds, have 9.9998, need 10000.0001") # Send will fail because of insufficient funds unless sender uses coinbase utxos try: @@ -216,12 +245,25 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): amount_per_recipient = Decimal('0.00000546') # dust threshold # Note that regtest chainparams does not require standard tx, so setting the amount to be # less than the dust threshold, e.g. 0.00000001 will not result in mempool rejection. + start_time = timeit.default_timer() for i in xrange(0,num_t_recipients): newtaddr = self.nodes[2].getnewaddress() recipients.append({"address":newtaddr, "amount":amount_per_recipient}) + elapsed = timeit.default_timer() - start_time + print("...invoked getnewaddress() {} times in {} seconds".format(num_t_recipients, elapsed)) + + # Issue #2263 Workaround START + # HTTP connection to node 0 may fall into a state, during the few minutes it takes to process + # loop above to create new addresses, that when z_sendmany is called with a large amount of + # rpc data in recipients, the connection fails with a 'broken pipe' error. Making a RPC call + # to node 0 before calling z_sendmany appears to fix this issue, perhaps putting the HTTP + # connection into a good state to handle a large amount of data in recipients. + self.nodes[0].getinfo() + # Issue #2263 Workaround END + myopid = self.nodes[0].z_sendmany(myzaddr, recipients) try: - self.wait_and_assert_operationid_status(myopid) + wait_and_assert_operationid_status(self.nodes[0], myopid) except JSONRPCException as e: print("JSONRPC error: "+e.error['message']) assert(False) @@ -235,7 +277,9 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): # check balance node2balance = amount_per_recipient * num_t_recipients + sproutvalue -= node2balance + Decimal('0.0001') assert_equal(self.nodes[2].getbalance(), node2balance) + check_value_pool(self.nodes[0], 'sprout', sproutvalue) # Send will fail because fee is negative try: @@ -284,7 +328,7 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): newzaddr = self.nodes[2].z_getnewaddress() recipients.append({"address":newzaddr, "amount":amount_per_recipient}) myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, custom_fee) - self.wait_and_assert_operationid_status(myopid) + wait_and_assert_operationid_status(self.nodes[0], myopid) self.sync_all() self.nodes[1].generate(1) self.sync_all() @@ -294,6 +338,8 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): assert_equal(Decimal(resp["private"]), send_amount) resp = self.nodes[0].z_getbalance(myzaddr) assert_equal(Decimal(resp), zbalance - custom_fee - send_amount) + sproutvalue -= custom_fee + check_value_pool(self.nodes[0], 'sprout', sproutvalue) if __name__ == '__main__': WalletProtectCoinbaseTest().main() diff --git a/qa/rpc-tests/wallet_shieldcoinbase.py b/qa/rpc-tests/wallet_shieldcoinbase.py new file mode 100755 index 000000000..b77fedcf0 --- /dev/null +++ b/qa/rpc-tests/wallet_shieldcoinbase.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python2 +# Copyright (c) 2017 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.test_framework import BitcoinTestFramework +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 + +from decimal import Decimal + +class WalletShieldCoinbaseTest (BitcoinTestFramework): + + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 4) + + def setup_network(self, split=False): + args = ['-regtestprotectcoinbase', '-debug=zrpcunsafe'] + self.nodes = [] + self.nodes.append(start_node(0, self.options.tmpdir, args)) + self.nodes.append(start_node(1, self.options.tmpdir, args)) + args2 = ['-regtestprotectcoinbase', '-debug=zrpcunsafe', "-mempooltxinputlimit=7"] + self.nodes.append(start_node(2, self.options.tmpdir, args2)) + connect_nodes_bi(self.nodes,0,1) + connect_nodes_bi(self.nodes,1,2) + connect_nodes_bi(self.nodes,0,2) + self.is_network_split=False + self.sync_all() + + def run_test (self): + print "Mining blocks..." + + self.nodes[0].generate(1) + do_not_shield_taddr = self.nodes[0].getnewaddress() + + self.nodes[0].generate(4) + walletinfo = self.nodes[0].getwalletinfo() + assert_equal(walletinfo['immature_balance'], 50) + assert_equal(walletinfo['balance'], 0) + self.sync_all() + self.nodes[2].generate(1) + self.nodes[2].getnewaddress() + self.nodes[2].generate(1) + self.nodes[2].getnewaddress() + self.nodes[2].generate(1) + self.sync_all() + self.nodes[1].generate(101) + self.sync_all() + assert_equal(self.nodes[0].getbalance(), 50) + assert_equal(self.nodes[1].getbalance(), 10) + assert_equal(self.nodes[2].getbalance(), 30) + + # Prepare to send taddr->zaddr + mytaddr = self.nodes[0].getnewaddress() + myzaddr = self.nodes[0].z_getnewaddress() + + # Shielding will fail when trying to spend from watch-only address + self.nodes[2].importaddress(mytaddr) + try: + self.nodes[2].z_shieldcoinbase(mytaddr, myzaddr) + except JSONRPCException,e: + errorString = e.error['message'] + assert_equal("Could not find any coinbase funds to shield" in errorString, True) + + # Shielding will fail because fee is negative + try: + self.nodes[0].z_shieldcoinbase("*", myzaddr, -1) + except JSONRPCException,e: + errorString = e.error['message'] + assert_equal("Amount out of range" in errorString, True) + + # Shielding will fail because fee is larger than MAX_MONEY + try: + self.nodes[0].z_shieldcoinbase("*", myzaddr, Decimal('21000000.00000001')) + except JSONRPCException,e: + errorString = e.error['message'] + assert_equal("Amount out of range" in errorString, True) + + # Shielding will fail because fee is larger than sum of utxos + try: + self.nodes[0].z_shieldcoinbase("*", myzaddr, 999) + except JSONRPCException,e: + errorString = e.error['message'] + assert_equal("Insufficient coinbase funds" in errorString, True) + + # Shielding will fail because limit parameter must be at least 0 + try: + self.nodes[0].z_shieldcoinbase("*", myzaddr, Decimal('0.001'), -1) + except JSONRPCException,e: + errorString = e.error['message'] + assert_equal("Limit on maximum number of utxos cannot be negative" in errorString, True) + + # Shielding will fail because limit parameter is absurdly large + try: + self.nodes[0].z_shieldcoinbase("*", myzaddr, Decimal('0.001'), 99999999999999) + except JSONRPCException,e: + 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 + result = self.nodes[0].z_shieldcoinbase(mytaddr, myzaddr) + wait_and_assert_operationid_status(self.nodes[0], result['opid']) + self.sync_all() + self.nodes[1].generate(1) + self.sync_all() + + # Confirm balances and that do_not_shield_taddr containing funds of 10 was left alone + assert_equal(self.nodes[0].getbalance(), 10) + assert_equal(self.nodes[0].z_getbalance(do_not_shield_taddr), Decimal('10.0')) + assert_equal(self.nodes[0].z_getbalance(myzaddr), Decimal('39.99990000')) + assert_equal(self.nodes[1].getbalance(), 20) + assert_equal(self.nodes[2].getbalance(), 30) + + # Shield coinbase utxos from any node 2 taddr, and set fee to 0 + result = self.nodes[2].z_shieldcoinbase("*", myzaddr, 0) + wait_and_assert_operationid_status(self.nodes[2], result['opid']) + self.sync_all() + self.nodes[1].generate(1) + self.sync_all() + + assert_equal(self.nodes[0].getbalance(), 10) + assert_equal(self.nodes[0].z_getbalance(myzaddr), Decimal('69.99990000')) + assert_equal(self.nodes[1].getbalance(), 30) + assert_equal(self.nodes[2].getbalance(), 0) + + # Generate 800 coinbase utxos on node 0, and 20 coinbase utxos on node 2 + self.nodes[0].generate(800) + self.sync_all() + self.nodes[2].generate(20) + self.sync_all() + self.nodes[1].generate(100) + self.sync_all() + mytaddr = self.nodes[0].getnewaddress() + + # Shielding the 800 utxos will occur over two transactions, since max tx size is 100,000 bytes. + # We don't verify shieldingValue as utxos are not selected in any specific order, so value can change on each test run. + # We set an unrealistically high limit parameter of 99999, to verify that max tx size will constrain the number of utxos. + result = self.nodes[0].z_shieldcoinbase(mytaddr, myzaddr, 0, 99999) + assert_equal(result["shieldingUTXOs"], Decimal('662')) + assert_equal(result["remainingUTXOs"], Decimal('138')) + remainingValue = result["remainingValue"] + opid1 = result['opid'] + + # Verify that utxos are locked (not available for selection) by queuing up another shielding operation + result = self.nodes[0].z_shieldcoinbase(mytaddr, myzaddr, 0, 0) + assert_equal(result["shieldingValue"], Decimal(remainingValue)) + assert_equal(result["shieldingUTXOs"], Decimal('138')) + assert_equal(result["remainingValue"], Decimal('0')) + assert_equal(result["remainingUTXOs"], Decimal('0')) + opid2 = result['opid'] + + # wait for both aysnc operations to complete + wait_and_assert_operationid_status(self.nodes[0], opid1) + wait_and_assert_operationid_status(self.nodes[0], opid2) + + # sync_all() invokes sync_mempool() but node 2's mempool limit will cause tx1 and tx2 to be rejected. + # So instead, we sync on blocks and mempool for node 0 and node 1, and after a new block is generated + # which mines tx1 and tx2, all nodes will have an empty mempool which can then be synced. + sync_blocks(self.nodes[:2]) + sync_mempools(self.nodes[:2]) + self.nodes[1].generate(1) + self.sync_all() + + # Verify maximum number of utxos which node 2 can shield is limited by option -mempooltxinputlimit + # This option is used when the limit parameter is set to 0. + mytaddr = self.nodes[2].getnewaddress() + result = self.nodes[2].z_shieldcoinbase(mytaddr, myzaddr, Decimal('0.0001'), 0) + assert_equal(result["shieldingUTXOs"], Decimal('7')) + assert_equal(result["remainingUTXOs"], Decimal('13')) + wait_and_assert_operationid_status(self.nodes[2], result['opid']) + self.sync_all() + self.nodes[1].generate(1) + self.sync_all() + + # Verify maximum number of utxos which node 0 can shield is set by default limit parameter of 50 + self.nodes[0].generate(200) + self.sync_all() + mytaddr = self.nodes[0].getnewaddress() + result = self.nodes[0].z_shieldcoinbase(mytaddr, myzaddr, Decimal('0.0001')) + 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) + assert_equal(result["shieldingUTXOs"], Decimal('33')) + assert_equal(result["remainingUTXOs"], Decimal('17')) + wait_and_assert_operationid_status(self.nodes[0], result['opid']) + # Don't sync node 2 which rejects the tx due to its mempooltxinputlimit + sync_blocks(self.nodes[:2]) + sync_mempools(self.nodes[:2]) + self.nodes[1].generate(1) + self.sync_all() + +if __name__ == '__main__': + WalletShieldCoinbaseTest().main() diff --git a/qa/rpc-tests/wallet_treestate.py b/qa/rpc-tests/wallet_treestate.py index ae55368f0..b3edcd7c5 100755 --- a/qa/rpc-tests/wallet_treestate.py +++ b/qa/rpc-tests/wallet_treestate.py @@ -5,10 +5,11 @@ from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -from time import * +from test_framework.util import assert_equal, initialize_chain_clean, \ + start_nodes, connect_nodes_bi, wait_and_assert_operationid_status -import sys +import time +from decimal import Decimal class WalletTreeStateTest (BitcoinTestFramework): @@ -25,30 +26,6 @@ class WalletTreeStateTest (BitcoinTestFramework): self.is_network_split=False self.sync_all() - def wait_and_assert_operationid_status(self, myopid, in_status='success', in_errormsg=None): - print('waiting for async operation {}'.format(myopid)) - opids = [] - opids.append(myopid) - timeout = 300 - status = None - errormsg = None - for x in xrange(1, timeout): - results = self.nodes[0].z_getoperationresult(opids) - if len(results)==0: - sleep(1) - else: - status = results[0]["status"] - if status == "failed": - errormsg = results[0]['error']['message'] - break - print('...returned status: {}'.format(status)) - print('...error msg: {}'.format(errormsg)) - assert_equal(in_status, status) - if errormsg is not None: - assert(in_errormsg is not None) - assert_equal(in_errormsg in errormsg, True) - print('...returned error: {}'.format(errormsg)) - def run_test (self): print "Mining blocks..." @@ -64,17 +41,17 @@ class WalletTreeStateTest (BitcoinTestFramework): recipients = [] recipients.append({"address":myzaddr, "amount":Decimal('10.0') - Decimal('0.0001')}) myopid = self.nodes[0].z_sendmany(mytaddr, recipients) - self.wait_and_assert_operationid_status(myopid) + wait_and_assert_operationid_status(self.nodes[0], myopid) self.sync_all() self.nodes[1].generate(1) self.sync_all() myopid = self.nodes[0].z_sendmany(mytaddr, recipients) - self.wait_and_assert_operationid_status(myopid) + wait_and_assert_operationid_status(self.nodes[0], myopid) self.sync_all() self.nodes[1].generate(1) self.sync_all() myopid = self.nodes[0].z_sendmany(mytaddr, recipients) - self.wait_and_assert_operationid_status(myopid) + wait_and_assert_operationid_status(self.nodes[0], myopid) self.sync_all() self.nodes[1].generate(1) self.sync_all() @@ -91,7 +68,7 @@ class WalletTreeStateTest (BitcoinTestFramework): recipients = [] recipients.append({"address":self.nodes[2].z_getnewaddress(), "amount":Decimal('10.0') - Decimal('0.0001')}) myopid = self.nodes[0].z_sendmany(mytaddr, recipients) - self.wait_and_assert_operationid_status(myopid) + 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. @@ -106,7 +83,7 @@ class WalletTreeStateTest (BitcoinTestFramework): status = results[0]["status"] if status == "executing": break - sleep(1) + time.sleep(1) # Now mine Tx 1 which will change global treestate before Tx 2's second joinsplit begins processing self.sync_all() @@ -114,7 +91,7 @@ class WalletTreeStateTest (BitcoinTestFramework): self.sync_all() # Wait for Tx 2 to be created - self.wait_and_assert_operationid_status(myopid) + wait_and_assert_operationid_status(self.nodes[0], myopid) # Note that a bug existed in v1.0.0-1.0.3 where Tx 2 creation would fail with an error: # "Witness for spendable note does not have same anchor as change input" diff --git a/qa/rpc-tests/walletbackup.py b/qa/rpc-tests/walletbackup.py index 5f3eb6fcc..78128ad49 100755 --- a/qa/rpc-tests/walletbackup.py +++ b/qa/rpc-tests/walletbackup.py @@ -34,9 +34,17 @@ and confirm again balances are correct. """ from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.authproxy import JSONRPCException +from test_framework.util import assert_equal, initialize_chain_clean, \ + start_nodes, start_node, connect_nodes, stop_node, \ + sync_blocks, sync_mempools + +import os +import shutil from random import randint +from decimal import Decimal import logging + logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO) class WalletBackupTest(BitcoinTestFramework): @@ -134,6 +142,14 @@ class WalletBackupTest(BitcoinTestFramework): self.nodes[2].backupwallet("walletbak") self.nodes[2].dumpwallet("walletdump") + # Verify dumpwallet cannot overwrite an existing file + try: + self.nodes[2].dumpwallet("walletdump") + assert(False) + except JSONRPCException as e: + errorString = e.error['message'] + assert("Cannot overwrite existing file" in errorString) + logging.info("More transactions") for i in range(5): self.do_one_round() diff --git a/qa/rpc-tests/zapwallettxes.py b/qa/rpc-tests/zapwallettxes.py index b15138818..5da4ba125 100755 --- a/qa/rpc-tests/zapwallettxes.py +++ b/qa/rpc-tests/zapwallettxes.py @@ -4,7 +4,9 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.authproxy import JSONRPCException +from test_framework.util import assert_equal, initialize_chain_clean, \ + start_nodes, start_node, connect_nodes_bi, bitcoind_processes class ZapWalletTXesTest (BitcoinTestFramework): @@ -27,56 +29,56 @@ class ZapWalletTXesTest (BitcoinTestFramework): self.sync_all() self.nodes[1].generate(101) self.sync_all() - + assert_equal(self.nodes[0].getbalance(), 40) - + txid0 = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11) txid1 = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 10) self.sync_all() self.nodes[0].generate(1) self.sync_all() - + txid2 = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11) txid3 = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 5) - + tx0 = self.nodes[0].gettransaction(txid0) - assert_equal(tx0['txid'], txid0) #tx0 must be available (confirmed) - + assert_equal(tx0['txid'], txid0) # tx0 must be available (confirmed) + tx1 = self.nodes[0].gettransaction(txid1) - assert_equal(tx1['txid'], txid1) #tx1 must be available (confirmed) - + assert_equal(tx1['txid'], txid1) # tx1 must be available (confirmed) + tx2 = self.nodes[0].gettransaction(txid2) - assert_equal(tx2['txid'], txid2) #tx2 must be available (unconfirmed) - + assert_equal(tx2['txid'], txid2) # tx2 must be available (unconfirmed) + tx3 = self.nodes[0].gettransaction(txid3) - assert_equal(tx3['txid'], txid3) #tx3 must be available (unconfirmed) - - #restart bitcoind + assert_equal(tx3['txid'], txid3) # tx3 must be available (unconfirmed) + + # restart zcashd self.nodes[0].stop() bitcoind_processes[0].wait() self.nodes[0] = start_node(0,self.options.tmpdir) - + tx3 = self.nodes[0].gettransaction(txid3) - assert_equal(tx3['txid'], txid3) #tx must be available (unconfirmed) - + assert_equal(tx3['txid'], txid3) # tx must be available (unconfirmed) + self.nodes[0].stop() bitcoind_processes[0].wait() - - #restart bitcoind with zapwallettxes + + # restart zcashd with zapwallettxes self.nodes[0] = start_node(0,self.options.tmpdir, ["-zapwallettxes=1"]) - + aException = False try: tx3 = self.nodes[0].gettransaction(txid3) except JSONRPCException,e: print e aException = True - - assert_equal(aException, True) #there must be a expection because the unconfirmed wallettx0 must be gone by now + + assert_equal(aException, True) # there must be a expection because the unconfirmed wallettx0 must be gone by now tx0 = self.nodes[0].gettransaction(txid0) - assert_equal(tx0['txid'], txid0) #tx0 (confirmed) must still be available because it was confirmed + assert_equal(tx0['txid'], txid0) # tx0 (confirmed) must still be available because it was confirmed if __name__ == '__main__': - ZapWalletTXesTest ().main () + ZapWalletTXesTest().main() diff --git a/qa/rpc-tests/zcjoinsplit.py b/qa/rpc-tests/zcjoinsplit.py index 2b439e831..7e5aba6e3 100755 --- a/qa/rpc-tests/zcjoinsplit.py +++ b/qa/rpc-tests/zcjoinsplit.py @@ -5,11 +5,9 @@ # from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -from decimal import Decimal -import os -import shutil -import sys +from test_framework.util import assert_equal, start_node, \ + gather_inputs + class JoinSplitTest(BitcoinTestFramework): def setup_network(self): diff --git a/qa/rpc-tests/zcjoinsplitdoublespend.py b/qa/rpc-tests/zcjoinsplitdoublespend.py index cb89a9182..98837b57e 100755 --- a/qa/rpc-tests/zcjoinsplitdoublespend.py +++ b/qa/rpc-tests/zcjoinsplitdoublespend.py @@ -5,11 +5,10 @@ # from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -from decimal import Decimal -import os -import shutil -import sys +from test_framework.authproxy import JSONRPCException +from test_framework.util import assert_equal, connect_nodes, \ + gather_inputs, sync_blocks + import time class JoinSplitTest(BitcoinTestFramework): @@ -41,7 +40,7 @@ class JoinSplitTest(BitcoinTestFramework): assert_equal(self.cannot_joinsplit(node, txn), True) def run_test(self): - # All nodes should start with 250 BTC: + # All nodes should start with 250 ZEC: starting_balance = 250 for i in range(4): assert_equal(self.nodes[i].getbalance(), starting_balance) diff --git a/qa/rpc-tests/zkey_import_export.py b/qa/rpc-tests/zkey_import_export.py new file mode 100755 index 000000000..323debe31 --- /dev/null +++ b/qa/rpc-tests/zkey_import_export.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python2 +# Copyright (c) 2017 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +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 + +import logging +import time +import math + +logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO) + + +class ZkeyImportExportTest (BitcoinTestFramework): + + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 5) + + def setup_network(self, split=False): + self.nodes = start_nodes(5, self.options.tmpdir ) + connect_nodes_bi(self.nodes,0,1) + connect_nodes_bi(self.nodes,1,2) + connect_nodes_bi(self.nodes,0,2) + connect_nodes_bi(self.nodes,0,3) + connect_nodes_bi(self.nodes,0,4) + self.is_network_split=False + self.sync_all() + + def run_test(self): + [alice, bob, charlie, david, miner] = self.nodes + + def z_send(from_node, from_addr, to_addr, amount): + opid = from_node.z_sendmany(from_addr, [{"address": to_addr, "amount": Decimal(amount)}]) + wait_and_assert_operationid_status(from_node, opid) + self.sync_all() + miner.generate(1) + self.sync_all() + + def z_getbalance(node, zaddr): + bal = node.z_getbalance(zaddr) + # Ignore fees for sake of comparison + round_balance = math.ceil(bal*100)/100 + return round_balance + + def verify_utxos(node, amts, zaddr): + amts.sort(reverse=True) + txs = node.z_listreceivedbyaddress(zaddr) + + def cmp_confirmations_high_to_low(a, b): + return cmp(b["amount"], a["amount"]) + + txs.sort(cmp_confirmations_high_to_low) + print("Sorted txs", txs) + print("amts", amts) + + try: + assert_equal(amts, [tx["amount"] for tx in txs]) + except AssertionError: + logging.error( + 'Expected amounts: %r; txs: %r', + amts, txs) + raise + + def get_private_balance(node): + balance = node.z_gettotalbalance() + return balance['private'] + + def find_imported_key(node, import_zaddr): + zaddrs = node.z_listaddresses() + assert(import_zaddr in zaddrs) + return import_zaddr + + # Seed Alice with some funds + alice.generate(10) + self.sync_all() + miner.generate(100) + self.sync_all() + # Shield Alice's coinbase funds to her zaddr + alice_zaddr = alice.z_getnewaddress() + res = alice.z_shieldcoinbase("*", alice_zaddr) + wait_and_assert_operationid_status(alice, res['opid']) + self.sync_all() + miner.generate(1) + self.sync_all() + + # Now get a pristine z-address for receiving transfers: + bob_zaddr = bob.z_getnewaddress() + verify_utxos(bob, [], bob_zaddr) + # TODO: Verify that charlie doesn't have funds in addr + # verify_utxos(charlie, []) + + # the amounts of each txn embodied which generates a single UTXO: + amounts = map(Decimal, ['2.3', '3.7', '0.1', '0.5', '1.0', '0.19']) + + # Internal test consistency assertion: + assert_greater_than( + get_private_balance(alice), + reduce(Decimal.__add__, amounts)) + + logging.info("Sending pre-export txns...") + for amount in amounts[0:2]: + z_send(alice, alice_zaddr, bob_zaddr, amount) + + logging.info("Exporting privkey from bob...") + privkey = bob.z_exportkey(bob_zaddr) + + logging.info("Sending post-export txns...") + for amount in amounts[2:4]: + z_send(alice, alice_zaddr, bob_zaddr, amount) + + print("Bob amounts:", amounts[:4]) + verify_utxos(bob, amounts[:4], bob_zaddr) + # verify_utxos(charlie, []) + + logging.info("Importing privkey into charlie...") + # z_importkey rescan defaults to "whenkeyisnew", so should rescan here + charlie.z_importkey(privkey) + ipk_zaddr = find_imported_key(charlie, bob_zaddr) + + # z_importkey should have rescanned for new key, so this should pass: + verify_utxos(charlie, amounts[:4], ipk_zaddr) + + # Verify idempotent behavior: + charlie.z_importkey(privkey) + ipk_zaddr2 = find_imported_key(charlie, bob_zaddr) + + # amounts should be unchanged + verify_utxos(charlie, amounts[:4], ipk_zaddr2) + + logging.info("Sending post-import txns...") + for amount in amounts[4:]: + z_send(alice, alice_zaddr, bob_zaddr, amount) + + verify_utxos(bob, amounts, bob_zaddr) + verify_utxos(charlie, amounts, ipk_zaddr) + verify_utxos(charlie, amounts, ipk_zaddr2) + + # Try to reproduce zombie balance reported in #1936 + # At generated zaddr, receive ZEC, and send ZEC back out. bob -> alice + for amount in amounts[:2]: + print("Sending amount from bob to alice: ", amount) + z_send(bob, bob_zaddr, alice_zaddr, amount) + + balance = float(sum(amounts) - sum(amounts[:2])) + assert_equal(z_getbalance(bob, bob_zaddr), balance) + + # z_import onto new node "david" (blockchain rescan, default or True?) + david.z_importkey(privkey) + d_ipk_zaddr = find_imported_key(david, bob_zaddr) + + # Check if amt bob spent is deducted for charlie and david + assert_equal(z_getbalance(charlie, ipk_zaddr), balance) + assert_equal(z_getbalance(david, d_ipk_zaddr), balance) + +if __name__ == '__main__': + ZkeyImportExportTest().main() diff --git a/qa/rpc-tests/zmq_test.py b/qa/rpc-tests/zmq_test.py index 97850bea3..d70e73114 100755 --- a/qa/rpc-tests/zmq_test.py +++ b/qa/rpc-tests/zmq_test.py @@ -8,21 +8,12 @@ # from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.util import assert_equal, bytes_to_hex_str, start_nodes + import zmq -import binascii import struct -try: - import http.client as httplib -except ImportError: - import httplib -try: - import urllib.parse as urlparse -except ImportError: - import urlparse - -class ZMQTest (BitcoinTestFramework): +class ZMQTest(BitcoinTestFramework): port = 28332 @@ -51,8 +42,9 @@ class ZMQTest (BitcoinTestFramework): assert_equal(topic, b"hashtx") body = msg[1] nseq = msg[2] + [nseq] # hush pyflakes msgSequence = struct.unpack('> 7) - 1 + l += 1 + return bytes(v)[::-1] + +def decode_varint(v): + n = 0 + for ch in range(len(v)): + n = (n << 7) | (ord(v[ch]) & 0x7F) + if (ord(v[ch]) & 0x80): + n += 1 + else: + return n + +def compress_amount(n): + if n == 0: + return 0 + e = 0 + while (((n % 10) == 0) and e < 9): + n /= 10 + e += 1 + if e < 9: + d = (n % 10) + assert(d >= 1 and d <= 9) + n /= 10 + return 1 + (n*9 + d - 1)*10 + e + else: + return 1 + (n - 1)*10 + 9 + +OP_DUP = 0x76 +OP_EQUAL = 0x87 +OP_EQUALVERIFY = 0x88 +OP_HASH160 = 0xa9 +OP_CHECKSIG = 0xac +def to_key_id(script): + if len(script) == 25 and \ + script[0] == OP_DUP and \ + script[1] == OP_HASH160 and \ + script[2] == 20 and \ + script[23] == OP_EQUALVERIFY and \ + script[24] == OP_CHECKSIG: + return script[3:23] + return bytes() + +def to_script_id(script): + if len(script) == 23 and \ + script[0] == OP_HASH160 and \ + script[1] == 20 and \ + script[22] == OP_EQUAL: + return script[2:22] + return bytes() + +def to_pubkey(script): + if len(script) == 35 and \ + script[0] == 33 and \ + script[34] == OP_CHECKSIG and \ + (script[1] == 0x02 or script[1] == 0x03): + return script[1:34] + if len(script) == 67 and \ + script[0] == 65 and \ + script[66] == OP_CHECKSIG and \ + script[1] == 0x04: + return script[1:66] # assuming is fully valid + return bytes() + +def compress_script(script): + result = bytearray() + + key_id = to_key_id(script) + if key_id: + result.append(0x00) + result.extend(key_id) + return bytes(result) + + script_id = to_script_id(script) + if script_id: + result.append(0x01) + result.extend(script_id) + return bytes(result) + + pubkey = to_pubkey(script) + if pubkey: + result.append(0x00) + result.extend(pubkey[1:33]) + if pubkey[0] == 0x02 or pubkey[0] == 0x03: + result[0] = pubkey[0] + return bytes(result) + elif pubkey[0] == 0x04: + result[0] = 0x04 | (pubkey[64] & 0x01) + return bytes(result) + + size = len(script) + 6 + result.append(encode_varint(size)) + result.extend(script) + return bytes(result) + +def deterministic_filter(tarinfo): + tarinfo.uid = tarinfo.gid = 0 + tarinfo.uname = tarinfo.gname = "root" + tarinfo.mtime = calendar.timegm(time.strptime('2017-05-17', '%Y-%m-%d')) + tarinfo.mode |= stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP + tarinfo.mode &= ~stat.S_IWGRP + if tarinfo.isdir(): + tarinfo.mode |= \ + stat.S_IXUSR | \ + stat.S_IXGRP | \ + stat.S_IXOTH + else: + tarinfo.mode &= \ + ~stat.S_IXUSR & \ + ~stat.S_IXGRP & \ + ~stat.S_IXOTH + return tarinfo + +def create_benchmark_archive(blk_hash): + blk = json.loads(subprocess.check_output([ZCASH_CLI, 'getblock', blk_hash])) + print 'Height: %d' % blk['height'] + print 'Transactions: %d' % len(blk['tx']) + + os.mkdir('benchmark') + with open('benchmark/block-%d.dat' % blk['height'], 'wb') as f: + f.write(binascii.unhexlify(subprocess.check_output([ZCASH_CLI, 'getblock', blk_hash, 'false']).strip())) + + txs = [json.loads(subprocess.check_output([ZCASH_CLI, 'getrawtransaction', tx, '1']) + ) for tx in blk['tx']] + + js_txs = len([tx for tx in txs if len(tx['vjoinsplit']) > 0]) + if js_txs: + print 'Block contains %d JoinSplit-containing transactions' % js_txs + return + + inputs = [(x['txid'], x['vout']) for tx in txs for x in tx['vin'] if x.has_key('txid')] + print 'Total inputs: %d' % len(inputs) + + unique_inputs = {} + for i in sorted(inputs): + if unique_inputs.has_key(i[0]): + unique_inputs[i[0]].append(i[1]) + else: + unique_inputs[i[0]] = [i[1]] + print 'Unique input transactions: %d' % len(unique_inputs) + + db_path = 'benchmark/block-%d-inputs' % blk['height'] + db = plyvel.DB(db_path, create_if_missing=True) + wb = db.write_batch() + bar = progressbar.ProgressBar(redirect_stdout=True) + print 'Collecting input coins for block' + for tx in bar(unique_inputs.keys()): + rawtx = json.loads(subprocess.check_output([ZCASH_CLI, 'getrawtransaction', tx, '1'])) + + mask_size = 0 + mask_code = 0 + b = 0 + while 2+b*8 < len(rawtx['vout']): + zero = True + i = 0 + while i < 8 and 2+b*8+i < len(rawtx['vout']): + if 2+b*8+i in unique_inputs[tx]: + zero = False + i += 1 + if not zero: + mask_size = b + 1 + mask_code += 1 + b += 1 + + coinbase = len(rawtx['vin']) == 1 and 'coinbase' in rawtx['vin'][0] + first = len(rawtx['vout']) > 0 and 0 in unique_inputs[tx] + second = len(rawtx['vout']) > 1 and 1 in unique_inputs[tx] + code = 8*(mask_code - (0 if first or second else 1)) + \ + (1 if coinbase else 0) + \ + (2 if first else 0) + \ + (4 if second else 0) + + coins = bytearray() + # Serialized format: + # - VARINT(nVersion) + coins.extend(encode_varint(rawtx['version'])) + # - VARINT(nCode) + coins.extend(encode_varint(code)) + # - unspentness bitvector, for vout[2] and further; least significant byte first + for b in range(mask_size): + avail = 0 + i = 0 + while i < 8 and 2+b*8+i < len(rawtx['vout']): + if 2+b*8+i in unique_inputs[tx]: + avail |= (1 << i) + i += 1 + coins.append(avail) + # - the non-spent CTxOuts (via CTxOutCompressor) + for i in range(len(rawtx['vout'])): + if i in unique_inputs[tx]: + coins.extend(encode_varint(compress_amount(int(rawtx['vout'][i]['valueZat'])))) + coins.extend(compress_script( + binascii.unhexlify(rawtx['vout'][i]['scriptPubKey']['hex']))) + # - VARINT(nHeight) + coins.extend(encode_varint(json.loads( + subprocess.check_output([ZCASH_CLI, 'getblockheader', rawtx['blockhash']]) + )['height'])) + + db_key = b'c' + bytes(binascii.unhexlify(tx)[::-1]) + db_val = bytes(coins) + wb.put(db_key, db_val) + + wb.write() + db.close() + + # Make reproducible archive + os.remove('%s/LOG' % db_path) + files = subprocess.check_output(['find', 'benchmark']).strip().split('\n') + archive_name = 'block-%d.tar' % blk['height'] + tar = tarfile.open(archive_name, 'w') + for name in sorted(files): + tar.add(name, recursive=False, filter=deterministic_filter) + tar.close() + subprocess.check_call(['xz', '-6', archive_name]) + print 'Created archive %s.xz' % archive_name + subprocess.call(['rm', '-r', 'benchmark']) + +if __name__ == '__main__': + check_deps() + create_benchmark_archive('0000000007cdb809e48e51dd0b530e8f5073e0a9e9bd7ae920fe23e874658c74') diff --git a/qa/zcash/create_wallet_200k_utxos.py b/qa/zcash/create_wallet_200k_utxos.py new file mode 100755 index 000000000..d4a1d9d48 --- /dev/null +++ b/qa/zcash/create_wallet_200k_utxos.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python2 +# Copyright (c) 2017 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# +# Create a large wallet +# +# To use: +# - Copy to qa/rpc-tests/wallet_large.py +# - Add wallet_large.py to RPC tests list +# - ./qa/pull-tester/rpc-tests.sh wallet_large --nocleanup +# - Archive the resulting /tmp/test###### directory +# + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, + connect_nodes_bi, + initialize_chain_clean, + start_nodes, +) + +from decimal import Decimal + + +class LargeWalletTest(BitcoinTestFramework): + + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 2) + + def setup_network(self): + self.nodes = start_nodes(2, self.options.tmpdir) + connect_nodes_bi(self.nodes, 0, 1) + self.is_network_split = False + self.sync_all() + + def run_test(self): + self.nodes[1].generate(103) + self.sync_all() + + inputs = [] + for i in range(200000): + taddr = self.nodes[0].getnewaddress() + inputs.append(self.nodes[1].sendtoaddress(taddr, Decimal("0.001"))) + if i % 1000 == 0: + self.nodes[1].generate(1) + self.sync_all() + + self.nodes[1].generate(1) + self.sync_all() + print('Node 0: %d transactions, %d UTXOs' % + (len(self.nodes[0].listtransactions()), len(self.nodes[0].listunspent()))) + print('Node 1: %d transactions, %d UTXOs' % + (len(self.nodes[1].listtransactions()), len(self.nodes[1].listunspent()))) + assert_equal(len(self.nodes[0].listunspent()), len(inputs)) + +if __name__ == '__main__': + LargeWalletTest().main() diff --git a/qa/zcash/ensure-no-dot-so-in-depends.py b/qa/zcash/ensure-no-dot-so-in-depends.py deleted file mode 100755 index beb4b9ec0..000000000 --- a/qa/zcash/ensure-no-dot-so-in-depends.py +++ /dev/null @@ -1,41 +0,0 @@ -#! /usr/bin/env python2 - -import sys -import os - -def main(): - this_script = os.path.abspath(sys.argv[0]) - basedir = os.path.dirname(this_script) - arch_dir = os.path.join( - basedir, - '..', - '..', - 'depends', - 'x86_64-unknown-linux-gnu', - ) - - exit_code = 0 - - if os.path.isdir(arch_dir): - lib_dir = os.path.join(arch_dir, 'lib') - libraries = os.listdir(lib_dir) - - for lib in libraries: - if lib.find(".so") != -1: - print lib - exit_code = 1 - else: - exit_code = 2 - print "arch-specific build dir not present: {}".format(arch_dir) - print "Did you build the ./depends tree?" - print "Are you on a currently unsupported architecture?" - - if exit_code == 0: - print "PASS." - else: - print "FAIL." - - sys.exit(exit_code) - -if __name__ == '__main__': - main() diff --git a/qa/zcash/full-test-suite.sh b/qa/zcash/full-test-suite.sh deleted file mode 100755 index 7860b105a..000000000 --- a/qa/zcash/full-test-suite.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -# -# Execute all of the automated tests related to Zcash. -# - -set -eu - -SUITE_EXIT_STATUS=0 -REPOROOT="$(readlink -f "$(dirname "$0")"/../../)" - -function run_test_phase -{ - echo "===== BEGIN: $*" - set +e - eval "$@" - if [ $? -eq 0 ] - then - echo "===== PASSED: $*" - else - echo "===== FAILED: $*" - SUITE_EXIT_STATUS=1 - fi - set -e -} - -cd "${REPOROOT}" - -# Test phases: -run_test_phase "${REPOROOT}/qa/zcash/check-security-hardening.sh" -run_test_phase "${REPOROOT}/qa/zcash/ensure-no-dot-so-in-depends.py" - -# If make check fails, show test-suite.log as part of our run_test_phase -# output (and fail the phase with false): -run_test_phase make check '||' \ - '{' \ - echo '=== ./src/test-suite.log ===' ';' \ - cat './src/test-suite.log' ';' \ - false ';' \ - '}' - -exit $SUITE_EXIT_STATUS - - - - - - diff --git a/qa/zcash/full_test_suite.py b/qa/zcash/full_test_suite.py new file mode 100755 index 000000000..d8a076420 --- /dev/null +++ b/qa/zcash/full_test_suite.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python2 +# +# Execute all of the automated tests related to Zcash. +# + +import argparse +import os +import re +import subprocess +import sys + +REPOROOT = os.path.dirname( + os.path.dirname( + os.path.dirname( + os.path.abspath(__file__) + ) + ) +) + +def repofile(filename): + return os.path.join(REPOROOT, filename) + + +# +# Custom test runners +# + +RE_RPATH_RUNPATH = re.compile('No RPATH.*No RUNPATH') +RE_FORTIFY_AVAILABLE = re.compile('FORTIFY_SOURCE support available.*Yes') +RE_FORTIFY_USED = re.compile('Binary compiled with FORTIFY_SOURCE support.*Yes') + +def test_rpath_runpath(filename): + output = subprocess.check_output( + [repofile('qa/zcash/checksec.sh'), '--file', repofile(filename)] + ) + if RE_RPATH_RUNPATH.search(output): + print('PASS: %s has no RPATH or RUNPATH.' % filename) + return True + else: + print('FAIL: %s has an RPATH or a RUNPATH.' % filename) + print(output) + return False + +def test_fortify_source(filename): + proc = subprocess.Popen( + [repofile('qa/zcash/checksec.sh'), '--fortify-file', repofile(filename)], + stdout=subprocess.PIPE, + ) + line1 = proc.stdout.readline() + line2 = proc.stdout.readline() + proc.terminate() + if RE_FORTIFY_AVAILABLE.search(line1) and RE_FORTIFY_USED.search(line2): + print('PASS: %s has FORTIFY_SOURCE.' % filename) + return True + else: + print('FAIL: %s is missing FORTIFY_SOURCE.' % filename) + return False + +def check_security_hardening(): + ret = True + + # PIE, RELRO, Canary, and NX are tested by make check-security. + ret &= subprocess.call(['make', '-C', repofile('src'), 'check-security']) == 0 + + ret &= test_rpath_runpath('src/zcashd') + ret &= test_rpath_runpath('src/zcash-cli') + ret &= test_rpath_runpath('src/zcash-gtest') + ret &= test_rpath_runpath('src/zcash-tx') + ret &= test_rpath_runpath('src/test/test_bitcoin') + ret &= test_rpath_runpath('src/zcash/GenerateParams') + + # NOTE: checksec.sh does not reliably determine whether FORTIFY_SOURCE + # is enabled for the entire binary. See issue #915. + ret &= test_fortify_source('src/zcashd') + ret &= test_fortify_source('src/zcash-cli') + ret &= test_fortify_source('src/zcash-gtest') + ret &= test_fortify_source('src/zcash-tx') + ret &= test_fortify_source('src/test/test_bitcoin') + ret &= test_fortify_source('src/zcash/GenerateParams') + + return ret + +def ensure_no_dot_so_in_depends(): + arch_dir = os.path.join( + REPOROOT, + 'depends', + 'x86_64-unknown-linux-gnu', + ) + + exit_code = 0 + + if os.path.isdir(arch_dir): + lib_dir = os.path.join(arch_dir, 'lib') + libraries = os.listdir(lib_dir) + + for lib in libraries: + if lib.find(".so") != -1: + print lib + exit_code = 1 + else: + exit_code = 2 + print "arch-specific build dir not present: {}".format(arch_dir) + print "Did you build the ./depends tree?" + print "Are you on a currently unsupported architecture?" + + if exit_code == 0: + print "PASS." + else: + print "FAIL." + + return exit_code == 0 + +def util_test(): + return subprocess.call( + [repofile('src/test/bitcoin-util-test.py')], + cwd=repofile('src'), + env={'PYTHONPATH': repofile('src/test'), 'srcdir': repofile('src')} + ) == 0 + + +# +# Tests +# + +STAGES = [ + 'btest', + 'gtest', + 'sec-hard', + 'no-dot-so', + 'util-test', + 'secp256k1', + 'libsnark', + 'univalue', + 'rpc', +] + +STAGE_COMMANDS = { + 'btest': [repofile('src/test/test_bitcoin'), '-p'], + 'gtest': [repofile('src/zcash-gtest')], + 'sec-hard': check_security_hardening, + 'no-dot-so': ensure_no_dot_so_in_depends, + 'util-test': util_test, + 'secp256k1': ['make', '-C', repofile('src/secp256k1'), 'check'], + 'libsnark': ['make', '-C', repofile('src'), 'libsnark-tests'], + 'univalue': ['make', '-C', repofile('src/univalue'), 'check'], + 'rpc': [repofile('qa/pull-tester/rpc-tests.sh')], +} + + +# +# Test driver +# + +def run_stage(stage): + print('Running stage %s' % stage) + print('=' * (len(stage) + 14)) + print + + cmd = STAGE_COMMANDS[stage] + if type(cmd) == type([]): + ret = subprocess.call(cmd) == 0 + else: + ret = cmd() + + print + print('-' * (len(stage) + 15)) + print('Finished stage %s' % stage) + print + + return ret + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--list-stages', dest='list', action='store_true') + parser.add_argument('stage', nargs='*', default=STAGES, + help='One of %s'%STAGES) + args = parser.parse_args() + + # Check for list + if args.list: + for s in STAGES: + print(s) + sys.exit(0) + + # Check validity of stages + for s in args.stage: + if s not in STAGES: + print("Invalid stage '%s' (choose from %s)" % (s, STAGES)) + sys.exit(1) + + # Run the stages + passed = True + for s in args.stage: + passed &= run_stage(s) + + if not passed: + print("!!! One or more test stages failed !!!") + sys.exit(1) + +if __name__ == '__main__': + main() diff --git a/qa/zcash/performance-measurements.sh b/qa/zcash/performance-measurements.sh index 2c79e04ef..15f3077b5 100755 --- a/qa/zcash/performance-measurements.sh +++ b/qa/zcash/performance-measurements.sh @@ -1,37 +1,113 @@ #!/bin/bash +set -u -set -e DATADIR=./benchmark-datadir +SHA256CMD="$(command -v sha256sum || echo shasum)" +SHA256ARGS="$(command -v sha256sum >/dev/null || echo '-a 256')" function zcash_rpc { - ./src/zcash-cli -datadir="$DATADIR" -rpcwait -rpcuser=user -rpcpassword=password -rpcport=5983 "$@" + ./src/zcash-cli -datadir="$DATADIR" -rpcuser=user -rpcpassword=password -rpcport=5983 "$@" +} + +function zcash_rpc_slow { + # Timeout of 1 hour + zcash_rpc -rpcclienttimeout=3600 "$@" +} + +function zcash_rpc_veryslow { + # Timeout of 2.5 hours + zcash_rpc -rpcclienttimeout=9000 "$@" +} + +function zcash_rpc_wait_for_start { + zcash_rpc -rpcwait getinfo > /dev/null } function zcashd_generate { zcash_rpc generate 101 > /dev/null } +function extract_benchmark_datadir { + if [ -f "$1.tar.xz" ]; then + # Check the hash of the archive: + "$SHA256CMD" $SHA256ARGS -c < /dev/null - wait $ZCASH_PID + wait $ZCASHD_PID } function zcashd_massif_start { - rm -rf "$DATADIR" - mkdir -p "$DATADIR" - touch "$DATADIR/zcash.conf" + case "$1" in + sendtoaddress|loadwallet|listunspent) + case "$2" in + 200k-recv) + use_200k_benchmark 0 + ;; + 200k-send) + use_200k_benchmark 1 + ;; + *) + echo "Bad arguments to zcashd_massif_start." + exit 1 + esac + ;; + *) + rm -rf "$DATADIR" + mkdir -p "$DATADIR/regtest" + touch "$DATADIR/zcash.conf" + esac rm -f massif.out valgrind --tool=massif --time-unit=ms --massif-out-file=massif.out ./src/zcashd -regtest -datadir="$DATADIR" -rpcuser=user -rpcpassword=password -rpcport=5983 -showmetrics=0 & ZCASHD_PID=$! + zcash_rpc_wait_for_start } function zcashd_massif_stop { @@ -42,11 +118,12 @@ function zcashd_massif_stop { function zcashd_valgrind_start { rm -rf "$DATADIR" - mkdir -p "$DATADIR" + mkdir -p "$DATADIR/regtest" touch "$DATADIR/zcash.conf" rm -f valgrind.out valgrind --leak-check=yes -v --error-limit=no --log-file="valgrind.out" ./src/zcashd -regtest -datadir="$DATADIR" -rpcuser=user -rpcpassword=password -rpcport=5983 -showmetrics=0 & ZCASHD_PID=$! + zcash_rpc_wait_for_start } function zcashd_valgrind_stop { @@ -55,12 +132,41 @@ function zcashd_valgrind_stop { cat valgrind.out } +function extract_benchmark_data { + if [ -f "block-107134.tar.xz" ]; then + # Check the hash of the archive: + "$SHA256CMD" $SHA256ARGS -c < c #8F6125", -", c #956727", -"< c #916B2E", -"1 c #996B2C", -"2 c #B47B23", -"3 c #BD7C20", -"4 c #A17330", -"5 c #AB7D3B", -"6 c #C17F20", -"7 c #B9831F", -"8 c #BB842B", -"9 c #BD8533", -"0 c #B68F3D", -"q c #BE8C3B", -"w c #C4801F", -"e c #FE8C03", -"r c #F38A0F", -"t c #FD8E0A", -"y c #FF910C", -"u c #F78F13", -"i c #F98F10", -"p c #F79016", -"a c #FE9314", -"s c #F6931E", -"d c #FD961B", -"f c #FE991E", -"g c #C58421", -"h c #CD8621", -"j c #C78B21", -"k c #CC8B23", -"l c #C2852B", -"z c #C08B2D", -"x c #D28722", -"c c #D38B25", -"v c #DB8E22", -"b c #D28E2C", -"n c #D49323", -"m c #DC9224", -"M c #DC9B25", -"N c #D4922D", -"B c #DF972A", -"V c #DF982E", -"C c #C18D33", -"Z c #C58E38", -"A c #CB9332", -"S c #C2933C", -"D c #CD9339", -"F c #CC9938", -"G c #D19733", -"H c #DA9230", -"J c #D59935", -"K c #DC9C33", -"L c #DC9E3B", -"P c #E49124", -"I c #EA9426", -"U c #E09D26", -"Y c #EC972B", -"T c #F79625", -"R c #F99524", -"E c #F69A26", -"W c #F89825", -"Q c #F2972B", -"! c #F59A2C", -"~ c #F89B2B", -"^ c #E79D33", -"/ c #EF9D31", -"( c #E19F3A", -") c #EF9D3A", -"_ c #F49C33", -"` c #F99E32", -"' c #F49F39", -"] c #D6A13E", -"[ c #DAA33B", -"{ c #E3A127", -"} c #E7A328", -"| c #EDA32C", -" . c #EDA829", -".. c #FFA325", -"X. c #FFAB25", -"o. c #F3A42B", -"O. c #FFA429", -"+. c #F4A929", -"@. c #FFAC2A", -"#. c #FFB227", -"$. c #FFB32C", -"%. c #FFBA2D", -"&. c #EEA830", -"*. c #F7A334", -"=. c #FAA036", -"-. c #FCAB34", -";. c #F4A13C", -":. c #F9A33B", -">. c #F4A83B", -",. c #FFA83F", -"<. c #FDB432", -"1. c #FFBB33", -"2. c #FFB73A", -"3. c #FDB93E", -"4. c #FFC12F", -"5. c #FFC432", -"6. c #FFC338", -"7. c #D2A043", -"8. c #D8A140", -"9. c #EEA144", -"0. c #E2A840", -"q. c #EDA34B", -"w. c #F4A444", -"e. c #F9A642", -"r. c #FBA945", -"t. c #F3A64B", -"y. c #F4A84E", -"u. c #FBAB4B", -"i. c #EEB041", -"p. c #FABA44", -"a. c #ECA653", -"s. c #EEAC5D", -"d. c #F3AA53", -"f. c #FAAE53", -"g. c #F2AD5A", -"h. c #FBB056", -"j. c #F6B15E", -"k. c #FBB25B", -"l. c #DDAF79", -"z. c #E3A962", -"x. c #EBAE63", -"c. c #E4AC68", -"v. c #EAAF69", -"b. c #EEB065", -"n. c #E7B06C", -"m. c #EEB36B", -"M. c #F5B263", -"N. c #FBB461", -"B. c #E6B274", -"V. c #ECB574", -"C. c #E7B57B", -"Z. c #EAB77C", -"A. c #ECB97C", -"S. c #F2B770", -"D. c #F0BB7A", -"F. c #DBB485", -"G. c #DFB888", -"H. c #E4B984", -"J. c #EDBD82", -"K. c #E5BC8B", -"L. c #EABE8A", -"P. c #F0BE82", -"I. c #E0BF96", -"U. c #EDC089", -"Y. c #F0C28B", -"T. c #E5C194", -"R. c #E9C191", -"E. c #E4C39C", -"W. c #EBC699", -"Q. c #EBC99F", -"!. c #DFC3A0", -"~. c #DDCAAF", -"^. c #CFC7BD", -"/. c #D2CBB6", -"(. c #DBC8B1", -"). c #DBCDBB", -"_. c #E2C6A4", -"`. c #E6C8A5", -"'. c #EACBA5", -"]. c #E1C7A8", -"[. c #E3CBAD", -"{. c #EACCAA", -"}. c #EED1AC", -"|. c #E1CDB3", -" X c #E3CFB8", -".X c #E6D1B6", -"XX c #EBD2B3", -"oX c #E3D1BB", -"OX c #EAD6BB", -"+X c #EBD8BF", -"@X c #D3CDC2", -"#X c #D8CDC2", -"$X c #D0CECA", -"%X c #DDD3C4", -"&X c #D3D2CC", -"*X c #DDD5CB", -"=X c #CCD3D5", -"-X c #C9D7DF", -";X c #D2D4D6", -":X c #DEDAD4", -">X c #DDDCDB", -",X c #E2D4C2", -".N b b b b N >.( C > HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX4 L _ *.@.<.$.X.X...X.X.X.X.X.X...X.@.$.<.@.*./ G , HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX< L -.@.$.X...R R R T T T T W W W W W W T T T T R R W ..X.$.@.*.J HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXD -.%.X.W R T T W W W W W W W W W W W W W W W W W W W W W W T T R W X.%.+.A HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXS -.$.X.R T T 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 T T R X.$.-.C HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXF <.@.f R T 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 T R W #.<.A HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX[ <.X.f T 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 W W W W W T R X.$.K HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX0.$...R T 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 W E W W W W W W W T R ..%.G HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXS 1...R T 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 E ~ ~ E W W W W W W W W W T R X.1.A HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX3.X.d T 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 W ~ ~ ~ ~ ~ E W W W W W W W W W W T R @.2.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX7.5.f T 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 W ~ ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W T W %.z HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX3.X.s T 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 W ~ ~ ~ ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W T R $.<.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX1...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 E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ E E W W W W W W W W W W W W W R ..1.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX0 5.f T 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 ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W T W 5.8 HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX8.$.s 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 E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W T R %.N HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXi.#.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 E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W R $.&.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXp.X.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 E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` ` ` ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W R @.<.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXp.X.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 E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` ` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W R @.<.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXi.X.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 E ~ E ~ W R ~ ~ ~ ~ ~ ~ ` ` ` ` ` ` ` ` ` ` ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W R @.| HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX] #.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 ~ ~ ~ ~ ~ ! s e t d ~ ` ` ` ` ` ` =.=.=.` ` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W R %.N HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXq %.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 E W E ~ ~ ~ ~ y l.=XI.x.) p a =.` ` =.=.=.=.=.=.` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W R %.2 HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX5 5.d 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 E ~ ~ ~ ~ ~ ~ ~ ~ t (.jXVXNXuX@XF.W ` =.:.` W =.:.=.=.` ` ` ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W T R 5.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX1.f T 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 E ~ ~ ~ ~ ~ ~ ~ ~ R Q eXDXSXSXDXgX#Xa ` =.=.;.q.W a a R ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W T W %.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX3...T 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 E ~ ~ ~ ~ ~ ~ ~ ~ ~ ` a a.NXSXGXGXAXNXV.a :.:.f c.tX*XE.n.9.R ~ ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W W T @.@.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXD #.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 E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` t H.VXSXGXGXDXmXy.f :.:.a I.hXBXCXNXiX^.' W ` ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W W R %.g HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX5.d 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 E ~ W ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` i |.CXGXGXGXCX3X~ ` :.:.R %XCXSXGXAXNX>XW ~ ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W W R 5.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX2.W T W W W W W W W W W W W W W W W W W W W W W W W W ~ ~ ~ s t e a W ~ ` ` ` ` ` ` W ! eXFXGXGXSXVX[.d :.:.~ w.uXFXGXGXSXVXW.a ` ` ` ` ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W W T ..@.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHX9 $.R W W W W W W W W W W W W W W W W W W W W W W E W ~ ~ ~ y F./.B.9.T t t a ~ =.` =.a a.hXDXGXGXSXNXA.d :.e.R v.NXSXGXGXSXNXm.a =.` ` ` ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W W R %.= HXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHX6.d W W W W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ W i &XjXNXfX:X].B.q.T t a d e K.VXSXGXGXDXaXd.W e.e.d E.VXSXGXGXDXvXw.W =.` ` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W W W W %.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXK X.T W W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ~ a ) uXDXSXFXFXCXNXfX:X_.B.q.r .XFXGXGXGXCX3X=.=.e.,.~ %XCXGXGXGXCX1XW ` =.` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W W T $.m HXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHX5.R W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ~ ~ t x.NXSXGXGXGXSXSXDXFXCXNXmX8XcXSXGXGXGXCXW.e :.e.=.t.uXFXGXGXSXVXE.d :.=.=.` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W W W R %.HXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHX^ X.T W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ~ ~ ~ ` t T.VXSXGXGXGXGXGXGXGXSXSXFXGXGXGXGXGXGXFX}.9.' W e v.VXSXGXGXSXNXm.d :.=.=.=.` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W W W T @.P HXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHX1.R W W W W W W W W W W W W W W W W E E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ s ;XNXAXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXFXNX>X|.V.XXFXGXGXGXFXbXy.~ :.:.=.=.` ` ` ` ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W W W R %.HXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXH X.T W W W W W W W W W W W W W W E E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` R ' $XsXNXVXFXSXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXFXCXCXFXSXGXGXGXCXOXa :.:.:.=.=.=.` ` ` ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W W T $.c HXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHX1.R W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` ` ` ` ~ t.V.`.5XVXFXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXFXXFXGXGXGXGXGXGXGXSXCX{.e.P.'.2XvXNXBXDXSXGXGXGXGXGXGXGXGXGXSXDXjX~.y W =.` ` ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W W W @.HXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHX: 1.R W W W W E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` ` ` ` =.=.=.=.=.:.:.:.:.:.:.:.:.e.e.e.~ s.fXDXGXGXGXGXGXGXGXSXNXD.f =.=.,.M.L.oXaXVXDXSXGXGXGXGXGXGXGXGXGXAXVX(.t ~ ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W R %. HXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXl #.T W W W E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` ` ` ` =.=.=.=.:.:.:.:.:.:.:.:.:.e.e.e.e.r.W H.NXSXGXGXGXGXGXGXGXDXzXg.r.f.f.f.r.=.=.g.`.fXBXAXGXGXGXGXGXGXGXGXGXAXjXH.t =.` ` ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W T $.6 HXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHX~ ..W W W W E ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` ` ` ` =.=.=.=.=.:.:.:.:.:.:.:.e.e.e.e.e.e.e.r.W |.CXGXGXGXGXGXGXGXGXBX1X,.f.f.f.f.h.h.f.,.~ d.3XVXAXGXGXGXGXGXGXGXGXGXDXsX' f ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W ..~ HXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHX$.R W W W W W E ~ ~ ~ ~ ~ ` ` ` ` ` ` =.=.=.=.=.=.:.:.:.:.:.:.:.e.e.e.e.e.r.r.r.,.w.>XFXGXGXGXGXGXGXGXSXNX`.=.f.h.h.h.h.f.f.f.f.=.~ ,XVXSXGXGXGXGXGXGXGXGXSXVXT.y ` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W R $.HXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXX %.T W W W W W E ~ ~ ~ ~ ~ ` ` ` ` =.=.=.=.=.:.:.:.:.:.:.:.:.e.e.e.e.e.e.r.r.r.u.=.x.fXDXGXGXGXGXGXGXGXSXmXA.,.h.h.h.k.k.h.f.f.f.f.:.~ 5XFXGXGXGXGXGXGXGXGXGXCX:XW ~ ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W T $.. HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHX8 $.T W W W W W W E ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.:.:.:.:.e.e.e.e.e.r.r.r.r.r.u.u.~ K.NXSXGXGXGXGXGXGXGXDXzXj.r.k.k.k.k.k.h.f.f.f.f.f.W V.VXSXGXGXGXGXGXGXGXGXDXuXw.f ` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W T $.3 HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXY ..W W W W W W W E ~ ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.:.e.e.e.e.e.e.r.r.r.r.u.u.u.u.~ |.CXGXGXGXGXGXGXGXGXBX2Xr.f.k.k.k.k.k.k.h.f.f.f.f.,.d.bXFXGXGXGXGXGXGXGXGXDXfXd.d =.` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W O.P HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXO.W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` ` =.=.:.:.:.:.e.e.e.e.r.r.r.r.r.r.u.u.u.u.r.w.>XFXGXGXGXGXGXGXGXSXNX'.,.k.k.k.k.k.k.k.h.h.f.f.f.e.y.kXFXGXGXGXGXGXGXGXGXDXfXg.d =.` ` ` ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W O.HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHX$.R W W W W W W W W E ~ ~ ~ ~ ~ ` ` ` ` =.=.=.:.:.:.:.e.e.r.r.r.r.u.u.u.u.u.u.f.=.b.fXDXGXGXGXGXGXGXGXSXmXJ.r.k.k.k.k.k.k.k.h.h.f.f.f.:.s.mXFXGXGXGXGXGXGXGXGXDXpXy.R =.` ` ` ~ ~ ~ ~ ~ E E W W W W W W W W W W W W W W W W W $.HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHX1.R W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.e.e.e.r.r.u.u.u.u.u.u.u.f.=.K.NXSXGXGXGXGXGXGXGXFXxXM.u.k.k.k.k.k.k.k.k.h.f.f.k.~ K.VXSXGXGXGXGXGXGXGXGXCX5X=.~ =.=.` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W $.HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHX+ $.T W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.e.e.e.r.r.u.u.u.u.f.f.f.=.|.CXGXGXGXGXGXGXGXGXFXXFXGXGXGXGXGXGXGXGXFX9XA.b.u.r.r.u.u.h.h.h.u.r.O.w.:XCXSXGXGXGXGXGXGXGXGXSXhXL.a :.=.=.=.` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W T $.* HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXV X.T W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.e.e.e.r.r.u.u.u.u.f.,.b.fXFXGXGXGXGXGXGXGXGXSXFXVXpX*X[.R.V.M.g.d.d.g.b.T.pXCXSXGXGXGXGXGXGXGXGXGXDXpXe.~ :.:.=.=.` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W T $.; HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHX| O.T W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` ` =.=.:.:.:.:.:.e.e.r.r.u.u.u.u.f.=.K.NXSXGXGXGXGXGXGXGXGXGXGXSXFXFXBXNXmXuX>X3X3XyXmXVXFXSXGXGXGXGXGXGXGXGXGXAXhXE.d :.:.:.=.=.` ` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W T @.h HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXc @.T W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ` ` ` ` =.:.:.:.:.:.e.e.e.r.r.u.u.u.u.=.|.BXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXSXSXFXFXFXFXFXSXSXGXGXGXGXGXGXGXGXGXGXAXNX>X~ =.e.:.:.:.=.` ` ` ` ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W @.h HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXk @.T W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` =.=.:.:.:.:.e.e.e.r.r.r.u.u.r.w.>XFXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXZXNXeXe.~ e.:.:.:.:.=.=.=.` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W @.h HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXc @.T W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.e.e.e.r.r.u.u.=.x.fXFXGXGXGXGXGXGXGXGXGXFXFXSXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXFXCXfXoX:.~ r.e.:.:.:.:.:.=.` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W @.h HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXc @.T W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ` ` ` ` =.=.:.:.:.:.:.e.e.r.r.r.u.~ K.NXSXGXGXGXGXGXGXGXSXZX6XkXmXNXBXDXAXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGX0X'.S.~ =.u.e.e.e.:.:.:.:.=.=.` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W @.h HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXk @.T W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` ` =.=.:.:.:.:.e.e.e.r.r.u.~ |.CXGXGXGXGXGXGXGXGXFX4X,.k.D.Q.,XkXmXNXDXSXSXGXGXGXGXGXGXGXGXGXGXGXXFXGXGXGXGXGXGXGXSXVX{.,.f.u.r.u.N.J.{.5XNXBXAXSXGXGXGXGXGXGXGXGXGXFXMXH.W r.u.r.e.e.e.:.:.:.:.=.=.` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W T @.h HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXo.O.T W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.e.e.e.r.O.s.fXFXGXGXGXGXGXGXGXSXmXJ.r.N.N.N.N.h.r.r.f.J.1XhXBXAXGXGXGXGXGXGXGXGXSXDXjX!.W e.u.r.e.e.e.:.:.:.:.=.=.` ` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W T @.g HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXB X.T W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` =.=.:.:.:.:.:.e.e.r.W H.NXSXGXGXGXGXGXGXGXDXuXM.u.k.k.N.N.N.N.N.h.,.e.D.>XNXSXGXGXGXGXGXGXGXGXSXZXjXE.W r.r.e.e.e.:.:.:.:.=.=.=.` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W T $.- HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXl @.T W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.e.e.r.W |.CXGXGXGXGXGXGXGXGXBX2Xr.h.k.k.k.k.k.k.k.k.k.h.,.,.|.NXZXGXGXGXGXGXGXGXGXGXZXgXV.~ u.e.e.e.:.:.:.:.:.=.=.` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W T $.% HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHX@ $.T W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ` ` ` ` =.=.:.:.:.:.e.:.' >XFXGXGXGXGXGXGXGXSXNX{.,.k.k.k.k.k.k.k.k.k.k.k.k.u.~ `.NXSXGXGXGXGXGXGXGXGXSXCX>X=.e.r.r.e.e.:.:.:.:.:.=.=.` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W T $.. HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHX%.R W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ` ` ` ` =.=.:.:.:.:.e.~ s.fXFXGXGXGXGXGXGXGXSXNXJ.,.k.k.k.k.k.k.k.k.k.k.h.h.k.u.O.2XCXGXGXGXGXGXGXGXGXGXAXhXV.~ u.r.e.e.e.:.:.:.:.=.=.=.` ` ` ~ ~ ~ ~ ~ W W W W W W W W W W $.HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHX$.R W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ` ` ` ` ~ :.:.:.:.e.f Z.VXSXGXGXGXGXGXGXGXDXzXM.r.k.k.k.k.k.k.k.h.h.h.h.f.f.k.=.V.NXSXGXGXGXGXGXGXGXGXSXVX`.W r.e.e.e.e.:.:.:.:.=.=.=.` ` ` ~ ~ ~ ~ ~ ~ E W W W W W W W W $.HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXO.W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` =.~ Q a a W =.=.t XCXGXGXGXGXGXGXGXGXBX2Xr.f.k.k.k.k.k.k.h.h.h.h.f.f.f.f.r.y.kXFXGXGXGXGXGXGXGXGXGXBX,X~ :.e.e.e.:.:.:.:.:.:.=.=.` ` ` ` ~ ~ ~ ~ ~ E W W W W W W W ~ ..HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXI O.W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` a z.-X_.B.q.! u C.NXSXGXGXGXGXGXGXGXSXNX'.=.h.h.k.k.k.h.h.f.f.f.f.f.f.f.f.r.w.5XFXGXGXGXGXGXGXGXGXGXCX2X=.:.e.:.:.:.:.:.:.:.:.=.=.=.` ` ` ` ~ ~ ~ ~ ~ E W W W W W W O.P HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXk @.T W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ~ t ).jXVXNXaX2X1XBXDXSXGXGXGXGXGXGXGXSXmXA.:.h.h.h.h.h.f.f.f.f.f.f.f.f.f.f.,.d.vXFXGXGXGXGXGXGXGXGXGXCX1X` =.:.:.:.:.:.:.=.=.=.=.=.=.` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W T $.; HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXo %.T W W W W W W W W W W W W W W W W W W W W ~ ~ ~ ` y q.fXZXSXSXFXFXFXSXSXGXGXGXGXGXGXGXGXFXxXj.r.f.h.h.h.f.f.f.f.f.f.f.f.u.u.f.W B.NXSXGXGXGXGXGXGXGXGXSXBXoXW :.:.:.:.:.:.=.=.=.=.=.` ` ` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W %. HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHX$.R W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ` e !.CXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXFX+Xd ,.f.h.h.h.f.f.f.f.f.f.u.u.u.f.,.T :XFXGXGXGXGXGXGXGXGXGXSXNXE.f :.:.:.:.:.=.=.=.=.` ` ` ` ` ` ~ ~ ~ ~ ~ ~ ~ ~ ~ W W W W R $.HXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHX~ ..W W W W W W W W W W W W W W W W W W W W E ~ ~ a _ aXFXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXFX7XV.s.:.=.:.,.u.f.f.f.f.u.u.u.r.~ s ~.VXSXGXGXGXGXGXGXGXGXGXAXhXV.d :.:.=.=.=.=.=.` ` ` ` ` ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ W W W W O.E HXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXg $.T W W W W W W W W W W W W W W W W W W W E ~ ~ e G.hXAXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXFXVXpX*X_.Z.x.t.:.` ~ ~ ~ ~ ~ ' x.*XVXSXGXGXGXGXGXGXGXGXGXGXDXuXw.W :.=.=.=.=.` ` ` ` ` ` ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ W W W W W W T $.; HXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHX %.R W W W W W W W W W W W W W W W W W W W W ~ d T qXgXBXFXSXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXFXFXBXNXaX>X,X[._.T.T.E.|.:XNXCXSXGXGXGXGXGXGXGXGXGXGXSXVX Xd =.=.=.=.` ` ` ` ` ` ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ E W W W W W W R %.HXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHX@.W W W W W W W W W W W W W W W W W W W W W ~ R ` s.H.oXkXNXNXCXFXSXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXSXDXFXCXCXBXVXVXBXCXFXSXSXGXGXGXGXGXGXGXGXGXGXGXAXhXm.a :.` =.` ` ` ` ` ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ W W W W W W W W W W @.HXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXx @.T W W W W W W W W W W W W W W W W W W W W ~ ~ y t a _ g.L.oXkXhXVXCXFXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXGXSXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXBX:Xf ~ ` ` ` ` ` ` ` ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ E W W W W W W W W W T $.h HXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHX%.R W W W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ d a t a ' s.R.oXnXDXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXZXhXg.y =.` ` ` ` ` ~ ~ ~ ~ ~ ~ ~ ~ ~ E ~ E W W W W W W W W W W R %.HXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXO.~ W W W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ` ` ~ W a a d ! c #DF943B", -", c #D8913C", -"< c #D8923E", -"1 c #DF953E", -"2 c #E28B23", -"3 c #E38B23", -"4 c #EA9023", -"5 c #EB9023", -"6 c #ED9122", -"7 c #ED9123", -"8 c #EE9123", -"9 c #EE9223", -"0 c #F39421", -"q c #F19423", -"w c #F39523", -"e c #F79521", -"r c #F59422", -"t c #F49623", -"y c #F69622", -"u c #F79623", -"i c #F09324", -"p c #F19424", -"a c #F19525", -"s c #F49624", -"d c #F59625", -"f c #F49725", -"g c #F79624", -"h c #F79724", -"j c #F69725", -"k c #F79725", -"l c #F69726", -"z c #F79726", -"x c #F89621", -"c c #F89722", -"v c #F89723", -"b c #F89724", -"n c #F89824", -"m c #F89825", -"M c #F99825", -"N c #F89925", -"B c #F89926", -"V c #F89927", -"C c #F99927", -"Z c #F0972E", -"A c #F7992A", -"S c #F79A2B", -"D c #F79B2C", -"F c #F69A2D", -"G c #F79D2F", -"H c #F89929", -"J c #F89A28", -"K c #F89A29", -"L c #F99A29", -"P c #F99B29", -"I c #F89A2A", -"U c #F89A2B", -"Y c #F99B2B", -"T c #F89B2C", -"R c #F89C2C", -"E c #F99C2D", -"W c #F99C2E", -"Q c #F89D2E", -"! c #F99D2F", -"~ c #E29335", -"^ c #E49639", -"/ c #E2983F", -"( c #F79F35", -") c #F99E31", -"_ c #F89E32", -"` c #F99E32", -"' c #F9A033", -"] c #F9A035", -"[ c #F9A135", -"{ c #F9A036", -"} c #F9A136", -"| c #F9A137", -" . c #F3A03F", -".. c #F7A43F", -"X. c #F8A139", -"o. c #F9A23A", -"O. c #FAA33B", -"+. c #FAA43E", -"@. c #FAA43F", -"#. c #EF9F41", -"$. c #EEA244", -"%. c #ECA34B", -"&. c #F8A440", -"*. c #F9A541", -"=. c #F9A644", -"-. c #F9A947", -";. c #F0A349", -":. c #F5A648", -">. c #F1A74E", -",. c #F7AA4F", -"<. c #E4A458", -"1. c #E4A55B", -"2. c #E8A95E", -"3. c #F2A950", -"4. c #F4AA52", -"5. c #FBAF55", -"6. c #E4A860", -"7. c #EAAC63", -"8. c #EBAF68", -"9. c #F2AF61", -"0. c #EBB16C", -"q. c #F6B568", -"w. c #E3AF71", -"e. c #EBBE89", -"r. c #E0BC93", -"t. c #E3C199", -"y. c #E6C59D", -"u. c #EAC89E", -"i. c #E7C8A2", -"p. c #EACBA6", -"a. c #EBCFAF", -"s. c #F1CCA0", -"d. c #E7CEB1", -"f. c #ECD1B0", -"g. c #E5D2BB", -"h. c #E8D2B8", -"j. c #DFDFDF", -"k. c #E7D5C1", -"l. c #E7D7C4", -"z. c #E5D7C7", -"x. c #E7DACB", -"c. c #EADAC8", -"v. c #E9DCCC", -"b. c #EDDFCE", -"n. c #E5DDD3", -"m. c #E4DFD9", -"M. c #ECE0D1", -"N. c #E4E1DD", -"B. c #EDE3D8", -"V. c #EAE4DD", -"C. c #ECE5DC", -"Z. c #E2E2E2", -"A. c #E5E2E0", -"S. c #E4E4E4", -"D. c #E7E7E7", -"F. c #EAEAE9", -"G. c gray92", -"H. c #EEEEEE", -"J. c None", -/* pixels */ -"J.J.J.J.J.J.J.1 > J.J.J.J.J.J.J.", -"J.J.J.J.J./ ..| ' ( ~ J.J.J.J.J.", -"J.J.J.< *.{ V $ r U W _ - J.J.J.", -"J.J., o.J 0 # <.w.$.F N H % J.J.", -"J.J.o.T e 1.r.k.x.t.S z B u J.J.", -"J.^ [ Y ! #.z.H.M.n.0.d n m 2 J.", -"J.X.) | =. .h.B.5.f.j.;.v B d J.", -": Q M ` &.>.A.V.p.c.l.4.E n d = ", -"; I b A Z 2.D.s.u.F.a.-.} C w & ", -"J.l g y 6.m.G.q.3.b.Z.,.] D 8 J.", -"J.3 k c %.d.C.v.N.S.y.@.L a * J.", -"J.J.j z x 8.i.g.e.9.+.W t 6 J.J.", -"J.J.+ s h G :.7.O.R B s 7 . J.J.", -"J.J.J.O i f P L K d p 5 J.J.J.", -"J.J.J.J.J.@ 9 q i 4 + J.J.J.J.J.", -"J.J.J.J.J.J.J.X o J.J.J.J.J.J.J." -}; diff --git a/share/pixmaps/bitcoin256.png b/share/pixmaps/bitcoin256.png deleted file mode 100644 index 1d42116ef..000000000 Binary files a/share/pixmaps/bitcoin256.png and /dev/null differ diff --git a/share/pixmaps/bitcoin256.xpm b/share/pixmaps/bitcoin256.xpm deleted file mode 100644 index 87bb35cda..000000000 --- a/share/pixmaps/bitcoin256.xpm +++ /dev/null @@ -1,465 +0,0 @@ -/* XPM */ -static char *bitcoin___[] = { -/* columns rows colors chars-per-pixel */ -"256 256 203 2", -" c #BE741B", -". c #C1761B", -"X c #C6791C", -"o c #CC7C1D", -"O c #D07F1D", -"+ c #C67B21", -"@ c #CC7E21", -"# c #D4821E", -"$ c #D9841F", -"% c #ED8E1D", -"& c #EF911F", -"* c #CF8022", -"= c #D48323", -"- c #DB8621", -"; c #DD8922", -": c #D58729", -"> c #D6882B", -", c #DE8C2A", -"< c #CE8C3C", -"1 c #D28934", -"2 c #D98E32", -"3 c #D28E3C", -"4 c #DF9132", -"5 c #D6903E", -"6 c #DD933B", -"7 c #E58C22", -"8 c #E98F23", -"9 c #E38F2B", -"0 c #E88F28", -"q c #ED9124", -"w c #E6922D", -"e c #EB942B", -"r c #EF982F", -"t c #F59624", -"y c #F89723", -"u c #F79826", -"i c #F89825", -"p c #F1972A", -"a c #F59A2C", -"s c #F89B2B", -"d c #E59534", -"f c #EA9632", -"g c #EE9933", -"h c #E3963B", -"j c #E6993D", -"k c #EC9C3B", -"l c #F49C33", -"z c #F99E32", -"x c #F29E3A", -"c c #F7A037", -"v c #F9A036", -"b c #F5A13C", -"n c #F9A33B", -"m c #CE9147", -"M c #D29245", -"N c #DC9641", -"B c #DD9846", -"V c #D2954B", -"C c #DC9A4B", -"Z c #E59C44", -"A c #EA9E43", -"S c #E39E4B", -"D c #E89F49", -"F c #F09F40", -"G c #EDA145", -"H c #E6A14D", -"J c #EBA34B", -"K c #F4A443", -"L c #F9A642", -"P c #F7A847", -"I c #FAA846", -"U c #F3A64A", -"Y c #F8A748", -"T c #F5A94D", -"R c #FAAA4B", -"E c #E6A454", -"W c #EBA552", -"Q c #EDA856", -"! c #E4A55B", -"~ c #E8A75B", -"^ c #E7A95E", -"/ c #EBA95B", -"( c #F0A751", -") c #F4AB53", -"_ c #FAAE53", -"` c #F4AE5A", -"' c #F8AF59", -"] c #FAB057", -"[ c #F6B15E", -"{ c #FAB25B", -"} c #DFAD6F", -"| c #DCAE77", -" . c #DFB27D", -".. c #E5AA64", -"X. c #E8AB61", -"o. c #E5AE6C", -"O. c #E6B06F", -"+. c #ECB16C", -"@. c #F5B365", -"#. c #FBB562", -"$. c #FBB867", -"%. c #F5B66B", -"&. c #FAB768", -"*. c #F4B86F", -"=. c #FBB96A", -"-. c #E1AE71", -";. c #E5B174", -":. c #EBB573", -">. c #EFB977", -",. c #E5B47A", -"<. c #EEBA7B", -"1. c #F3B770", -"2. c #F3B974", -"3. c #FBBC72", -"4. c #F3BC7B", -"5. c #F8BF7A", -"6. c #FAC079", -"7. c #DCB382", -"8. c #DFBB8F", -"9. c #DABB96", -"0. c #DBBD99", -"q. c #E2B682", -"w. c #E4B985", -"e. c #ECBD84", -"r. c #E3BB8B", -"t. c #EABF8C", -"y. c #F1BE83", -"u. c #E2BE92", -"i. c #D3BDA2", -"p. c #DEC09C", -"a. c #EEC28D", -"s. c #F4C286", -"d. c #F8C282", -"f. c #F3C48B", -"g. c #E7C297", -"h. c #ECC393", -"j. c #E2C29D", -"k. c #EAC69B", -"l. c #ECC89F", -"z. c #F1C694", -"x. c #F2C897", -"c. c #F1CA9B", -"v. c #DBC2A3", -"b. c #D6C2AB", -"n. c #DDC7AD", -"m. c #DEC9AF", -"M. c #D3C4B3", -"N. c #DDCAB3", -"B. c #D2C7B9", -"V. c #D6C9BA", -"C. c #DDCEBB", -"Z. c #DFD0BE", -"A. c #E2C5A2", -"S. c #E8C7A0", -"D. c #E6C9A5", -"F. c #EBCBA4", -"G. c #E2C7A8", -"H. c #E3CAAC", -"J. c #EBCDA9", -"K. c #EFD2AF", -"L. c #F3D1A7", -"P. c #F1D1A9", -"I. c #E4CEB3", -"U. c #E8CFB1", -"Y. c #E1CFBA", -"T. c #E6D0B6", -"R. c #E9D1B4", -"E. c #E4D2BC", -"W. c #EAD4BA", -"Q. c #F4D5B0", -"!. c #F4D9B9", -"~. c #CDCDCD", -"^. c #D5CCC3", -"/. c #D4CFCA", -"(. c #DED2C3", -"). c #D3D1CE", -"_. c #DED6CC", -"`. c #D5D5D5", -"'. c #DBD7D1", -"]. c #DEDAD4", -"[. c #DDDDDC", -"{. c #E3D5C3", -"}. c #E9D7C1", -"|. c #EBD9C4", -" X c #E1D6CA", -".X c #E3D9CD", -"XX c #EADDCD", -"oX c #E1DBD4", -"OX c #E8DFD4", -"+X c #E1DEDB", -"@X c #EDE3D7", -"#X c #E3E1DE", -"$X c #E8E3DC", -"%X c #F6E5D2", -"&X c #F4EBDF", -"*X c #E4E4E4", -"=X c #ECE7E2", -"-X c #EDE9E4", -";X c #ECECEC", -":X c #F0EBE7", -">X c #F4F4F4", -",X c #FEFEFE", -"X>X>X>X;X;X*X[.`.r.n n z v v v v c x l p l x x c c v v v v z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u i u X>X>X>X>X>X;X*X[.`.@.n n v v v v v c g E | S k f r l l l z z z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u i e X>X,X,X,X,X>X>X;X*X_.R n v v v v v v x e 0.`.`.V.p.;.H f e e p l l z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y , X>X,X,X,X,X>X>X;X*XI.L n v v v v n n x g V.`.[.[.[.[.[.(.p.;.S f r l z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u u y X,X,X,X,X,X>X>X;X*Xa.n n v v v n n n l A `.[.*X*X-X-X*X*X*X[.`.V.9.K z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u i u X,X,X,X,X,X>X>X-X[.%.n n n n n n n b p o.[.*X;X;X;X>X;X;X*X*X[.`.~.T z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y 0 X>X,X,X,X,X,X>X;X*XoXR L n n n n n n b g u.*X-X;X>X>X>X>X>X;X*X*X[.N.L n z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y X>X,X,X,X,X>X>X;X*XI.L L n n n n n n b g C.*X;X>X>X,X,X,X>X>X;X*X[.g.L n z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u i u X,X,X,X,X,X>X>X;X*Xh.L L n n n n n n l G [.*X;X>X,X,X,X,X>X>X;X*X[.2.n n z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y w X,X,X,X,X,X>X>X-X[.%.L n n n n n n b l o.*X;X>X>X,X,X,X,X,X>X;X*X]._ n v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i y X>X,X,X,X,X,X>X;X*XoXR L n n n n n n b g j.*X;X>X>X,X,X,X,X,X>X;X*XE.I n v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y t X>X,X,X,X,X>X>X;X*XT.I L n n n n n n b k Z.*X;X>X,X,X,X,X,X>X>X;X*Xl.L n v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y ; X,X,X,X,X,X>X>X;X*Xh.L L n n n n L L x G [.*X;X>X,X,X,X,X,X>X>X;X*X4.n n v v v z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u i u X>X,X,X,X,X,X>X>X-X[.%.L L n n n L L L l ;.*X;X>X>X,X,X,X,X,X>X;X*X[._ L n v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y q X>X>X>X;X;X;X;X*X*X*X*X].N.q.! d e e r p q ,.-X;X>X>X,X,X,X,X,X>X;X*XoX_ I L n L L L L K g j.*X;X>X>X,X,X,X,X,X>X;X*XE.Y L n v v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X>X>X>X>X>X>X>X>X;X;X;X;X*X*X*X*X_.I.r.o.Z w D.;X>X>X,X,X,X,X,X,X>X;X*XW.R I L L L L L L K k Y.*X;X>X,X,X,X,X,X>X>X;X*Xl.L L n v v v v v z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y q 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*Xx.I I L L L L L L x J [.*X;X>X,X,X,X,X,X>X>X;X*X4.L n n v v v v v z z z z z z s s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 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;X&.L L L L L L L L x ;.*X;X>X>X,X,X,X,X,X>X;X*X[.' L n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y t 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@Xb l x x K L L L L k j.*X;X>X>X,X,X,X,X,X>X;X*XE.R L n n n n v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 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:XW.g.;.H k k k b F k {.;X>X>X,X,X,X,X,X>X>X;X*XS.I L n n n n v v v v v z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i y t 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>X;X;X*X+XE.j.,.~ j A =X;X>X>X,X,X,X,X,X>X>X;X*X4.I L n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 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>X;X;X;X*X*X*X*XXX}.;X>X>X,X,X,X,X,X,X>X>X;X#X{ I n n n n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i y t 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>X>X;X;X;X;X;X>X>X>X,X,X,X,X,X,X,X>X>X;X|.R I n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 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>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X>X>X;XF.L L n n n n n n n v v v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i y q 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>X,X,X,X,X,X,X,X,X,X,X,X>X>X;X@.a x b b n n n n n v v v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 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,X,X,X,X,X,X,X,X,X,X,X>X>X;X|.e.G g l c b n n n n v v v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 0 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,X,X,X,X,X,X,X,X,X,X,X>X>X;X-X+XG...k g l b n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i y t 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,X,X,X,X,X,X,X,X,X,X,X>X>X;X;X*X*X(.w.A g l c c v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t = 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,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X-X*X*X'.u.A r l x c v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q 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,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X;X*X*X].u.k r l c v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 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,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X;X*X*X_.q.g p l z v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 7 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,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X;X*X[.C.W p l c v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i y t 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,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X*X*X[.w.r a l z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 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,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[.H.g a z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 0 X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;Xf.3.x.R..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;X*X*X(.k p z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i y t X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X$.{ { { $.3.f.F.{.[.*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_.W p z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t @ X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X-X|.{ ] _ ] { { { { $.3.h.R..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'.k p z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 0 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ._ ] _ _ _ _ ] { { { #.$.$.f.T.oX*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_.l a z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xs._ _ _ _ _ _ _ _ _ ] { { { { { =.l..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*XH.t z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X+X&.] _ _ _ _ _ _ _ _ _ _ _ _ ] { { { #.k.oX*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[.:.t z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t ; X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.{ { _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ] _ { J.*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'.l s z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XF.{ { _ _ _ _ _ _ _ _ ] _ _ _ _ _ _ _ _ _ _ _ y.oX*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[.t.u z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xs.{ ] _ _ _ _ _ ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ ' .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'.z z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X].&.{ ] _ _ _ ] ] ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ R R oX*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.:.u z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t ; X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.#.{ _ _ _ _ ] ] ] ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ I @.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XD.s z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XF.{ { _ ' ] ] ] ] ] { { { ] ] ] _ _ _ _ _ _ _ _ R R _ n k.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X_.n z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xs.{ { ] ] ] ] { { { { { { ] ] ] _ _ _ _ _ _ _ _ R R R I T +X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.T z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.=.{ { ] ] ] { { { { { { { ] ] ] _ _ _ _ _ _ _ _ _ R R R K D.*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.%.z z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t = X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.#.{ ] ] { { { { { { { { { { ] ] ] _ _ _ _ _ _ _ _ R R R K e.*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X-X[.<.v v z z z z z z z s s s s s s s s s s s s u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 7 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.{ { { { { { { { { { { { { { { ] ] ] _ _ _ _ _ _ _ _ R R K +.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X<.n v v z z z z z z s s s s s s s s s s s s u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xs.#.{ { { { { { { { { { { { { { ] ] ] ] _ _ _ _ _ _ _ _ R U / *X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xe.n n v v z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.=.#.{ { { { { { { { { { { { { { ] ] ] ' _ _ _ _ _ _ _ _ R K +.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X<.n n v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u t X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.$.#.{ { { { { { { { { { { { { { { ] ] ] ] _ _ _ _ _ _ _ T K ,.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X-X[.>.n n v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t @ X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.#.#.{ { { { { { { { { { { { { { { ] ] ] ] _ _ _ _ _ _ _ T G j.*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.%.n n v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t = X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xf.#.{ { { { { { { { { { { { { { { { { ] ] ] _ _ _ _ _ _ _ T J X-X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X]._ L n v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t ; X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X#X3.#.{ { { { { { { { { { { { { { { { { { ] ] ] _ _ _ _ _ ) G ..*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X{.R L n v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 7 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X|.=.#.{ { { { { { { { { { { { { { { { { { { ] ] ' _ _ _ _ T k E.*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XH.L L n v v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;Xc.] { { { { { { { { #.{ { { { { { { { { { { ] ] ] _ _ _ ( A w.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X-X[.a.L n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;Xx.( Q ( ) ` [ [ { #.#.#.{ { { { { { { { { { { ] ] _ ) T D o.*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[.[ L n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u q X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;XOXI.u.O./ Q Q ` ` [ [ [ { { { { { { { { { ] ' ) ( J H r.*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*XE.R I n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u q 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_.H.r.;.X./ Q Q ) ) ` ` ` ` ` ) ) ( J H W ,.{.*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[.y.I L n n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u t 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].(.H.u.q.;.^ ^ ~ ~ E E ~ o.r.G. 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_._ Y L n n n n n v v v z z z z z z z s s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i u t @ 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[.]..X X XoX+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[.f.R I n n n n n n v v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t = 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-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;X*X X_ R L n n n n n n v v v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t = 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>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;X*X[.%.R I L n n n n n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i t - 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>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*X[.k.R R L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i t ; 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,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[.l.] _ I L L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i t ; 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,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[.l.{ _ Y L L L n n n n n n n n v v v v v v z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i t ; 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,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].h.{ _ R L L L L L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i t ; 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,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[.T.3.{ ] R I L L L L L n n n n n n n n n v v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i t ; 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,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*XW.s.#.{ _ R I I L L L L L L n n n n n n n n v v v v z z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i t ; X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X-XQ.|.OX*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,X,X,X,X>X&X!.L.d.#.{ ] R R I I I L L L L L L n n n n n n n n n v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i t ; X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;XXX3.3.3.s.c.R..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>X%X{ L R _ _ R R R I I I I L L L L L L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i t ; X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X-XK.&.=.=.&.=.3.3.d.c.R..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 K Y R R Y I I I I L L L L L L n n n n n n n n n v v v v z z z z z z z s s s s s s s s s s s s u u u i i i i i i i i i i i i i i i i i i i i i i i i t ; X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xf.$.#.#.#.#.&.&.=.=.3.3.f.F.}.+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;XOX:.K U R R I I I I I L L L L L L n n n n n n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i t ; X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X-X+X3.$.#.{ { #.#.#.#.$.$.&.=.=.3.6.c.W.+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.K K R R I I I I I L L L L L n n n n n n n n v v v v v v z z z z z z s s s s s s s s s s s s u u u i i i i i i i i i i i i i i i i i i i i i i u t = X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.&.#.{ { { { #.#.#.#.#.#.#.#.$.$.=.=.5.J..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*XH.K K R R I I I I L L L L L L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i u t = X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.#.#.{ { { { #.#.#.#.#.#.#.#.{ #.#.$.$.$.=.z.{.*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*XC.U K R I I I I I L L L L L L n n n n n n n n v v v v v z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i u q * s u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u u u u s s s s s s s s s s s z z z z z z z v v v v v n n n n n n n n L L L L L L I I K A Z.*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xf.#.#.{ { { { { #.#.#.#.{ { { { { { { #.#.#.#.$.z.{.*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*XC.b K Y I I I I L L L L L L n n n n n n n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i u q + X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.3.#.{ { { { { { #.#.#.{ { { { { { { { { { { #.#.#.$.F.+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[.H.b P I I I I I L L L L L n n n n n n n n n v v v v v z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.$.#.{ { { { { { { { { { { { { { { { { { { { { { { #.{ 2.{.*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[.e.b Y I I I I L L L L L L n n n n n n n n v v v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.#.#.{ { { { { { { { { { { { { { { { { { { { { { { { { { { U.*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].T L Y I I I I L L L L L L n n n n n n n n v v v v v z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i t q X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xf.#.{ { { { { { { { { { { { { { { { { { { { { { { { { ] { { _ R.*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*XD.L R I I I I L L L L L L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i t q X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.=.#.{ { { { { { { { { { { { { { { { { { { { { { ] ] ] ] ] { ' R T.*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[.` L I I I I I L L L L L L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i t 8 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.$.#.{ { { { { { { { { { { { { { { { { { { { ] ] ] ] ] ] ] _ ] _ R oX*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X-X[.g.n I Y I I I I L L L L L n n n n n n n n n v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i t 7 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.{ { { { { { { { { { { { { { { { { { { { { ] ] ] ] ] _ _ _ _ _ ] Y <.*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(.I I I I I I L L L L L L L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i u t ; X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xf.{ { { { { { { { { { { { { { { { { { { ] ] ] ] ] ] _ _ _ _ _ _ _ _ T .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[.[ L I I I L L L L L L L L n n n n n n n n n v v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i u q = X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.=.{ { ] { { { { { { { { { { { { { { ] ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ P g.*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X-X[.e.n I L L L L L L L L L L n n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.#.{ ] ] { { { { { { { { { { { ] ] ] ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ Y +.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xg.L I L L L L L L L L L n n n n n n n n n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i t q X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.{ { ] ] ] { { { { { { { { { ] ] ] ] ] ' _ _ _ _ _ _ _ _ _ _ _ _ _ _ T Q #X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XD.I I L L L L L L L n n n n n n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xf.{ { ] ] ] ] { { { { { { { ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ Y W +X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XI.I I L L L L L n n n n n n n n n n n n n n n v v v v v v z z z z z z z s s s s s s s s s s u u u u u i i i i i i i i i i i i t 7 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.=.{ ] ] ] ] ] { { { { { ] ] ] ] ] ' _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R T W +X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XE.I L L L L n n n n n n n n n n n n n n v v v v v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i u q ; X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.#.{ _ _ ] ] ] ] { ] ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R R R K 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*XE.I L n n n n n n n n n n n n n n n n v v v v v v v v v z z z z z z z z s s s s s s s s s s s s u u u i i i i i i i i i i i t q @ X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.{ { _ _ _ ] ] ] ] ] ] ] ] ' _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R R R R x q.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XD.R L n n n n n n n n n n n n n n n v v v v v v v v v z z z z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i t q 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*Xs.{ ] _ _ _ ] ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R R R R R R T k G.*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XS.I L n n n n n n n n n n n n n v v v v v v v v v z z z z z z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i t q 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&.{ _ _ _ _ _ ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R R R R R R R K A oX;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xh.L L n n n n n n n n n n v v v v v v v v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s u u u i i i i i i i i i t 7 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|.{ ] _ _ _ _ _ ] ] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R R R R R R R R U k u.*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[.2.L L n n n n n n n n n v v v v v v v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s u u u u i i i i i i i u q = 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;Xc.R _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R R R R R R R R R T k D +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[.' L n n n n n n n n v v v v v v v z v v z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s u u u u i i i i i i i t q 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;Xf.K G G U ) ) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R R R R R R R R R R U A j {.*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{.R L n n n n n n v v v v v v v v v z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u i i i i i i t q 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-XXXH.w.X.J J J T ) ) ) _ _ _ _ _ _ _ _ R R R R R R R R R R R Y K k D Y.*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.L L n n n n v v v v v v v v z z z z z z z z z z z z z s z s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i u t 7 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>X>X;X-X*X#X(.A.q...H J J U U T T T T R R R R R R R R R Y Y U K k A ;..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[.4.L n n n v v v v v v v v z z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i t q * 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>X>X;X;X;X*X*X*X[.(.H.u.,.^ J D G A J K K U U U U K k k k A E w.Y.*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]._ L n v v v v v v v v v v z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i t q 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>X>X>X>X;X;X;X-X*X*X*X*X[._.N.A.u.;.;...E E E E ..;.q.j.I.+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*XH.I L n v v v v v v v v z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i t 8 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>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,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.1.L n v v v v v v z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i u q ; 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>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,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X XR L n v v v v v z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i t q 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>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,X,X,X,X,X,X,X,X,X,X>X;X-X[.a.L n v v v z z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i t 8 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,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,X,X,X,X,X,X>X>X;X*X]._ L n v z z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i u q ; 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,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,X,X,X>X;X*X[.a.L n v z z z z z z z z z z z z 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 u u u u u i i i i i i i i i i i i i i i i t q 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,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>X>X;X*X_.R L n z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i i i i t 8 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,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;X*X[.2.L n z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u s u u u u u u u u i i i i i i i i i i i i i i i i i i t q = 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,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*X[.D.L L v z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i t q 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,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 XR L n z z z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i i i i i i i i u q 7 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,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'._ I n z z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u i u u i i i i i i i i i i i i i i i i i i i i i i t q o 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,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].%.L L z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i t 8 X>X,X,X,X,X,X>X>X;X=X=.5.c.W.oX*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,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X*X*X_.%.I L z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i t q - X>X,X,X,X,X,X>X>X;X|._ _ _ { #.4.l.}.$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,X,X,X,X,X>X>X>X>X;X;X*X[.E.{ I L v z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u i u i i i i i i i i i i i i i i i i i i i i i i i i i i i t 8 X,X,X,X,X,X>X>X;X*XF.R R R R _ _ { { { 4.-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>X>X>X>X>X;X;X*X*X[.k._ I n z z z 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 u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q 7 X,X,X,X,X,X>X>X;X*X4.R I I I I R R R b U -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>X;X;X;X-X*X*X[.T.*.R L n z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X>X;X*X+X] R I L I I I I P x t.;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;X*X*X*X[.].U.4.R I L v z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u q 7 X>X,X,X,X,X>X>X;X*XE.R Y L L I I I I K k I.-X;X>X,X,X,X,X,X,X>X>X;X|.f.J.W..X[.[.*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X[._.I.h.#.R L L n z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u i u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q . X,X,X,X,X,X>X>X;X*Xl.I I L L L I I P K A oX-X>X>X,X,X,X,X,X>X>X;X;Xs.R _ _ { #.4.y.S.l.T.{.{. XoXoXoXoX].oX{.{.E.k.a.2.{ _ I L n v z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u q 7 X,X,X,X,X,X>X>X;X[.2.I I L L L L I L x ^ *X;X>X>X,X,X,X,X,X>X>X;X*X#.I I I I Y I R I _ R _ ] { { [ { { { { ] _ R R I I L n n v z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q . X>X,X,X,X,X,X>X;X*X]._ Y L L L L L I L k r.*X;X>X>X,X,X,X,X,X>X;X-X.XR L n n n n n n L L L L L L L n L n n n L n n n c v z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u q 7 X>X,X,X,X,X>X>X;X*XT.R I L L L L L L K k H.*X;X>X>X,X,X,X,X>X>X;X*XJ.L L n n n n n v v v v v v v v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 8 X,X,X,X,X,X>X>X;X*Xk.I I n L L L L L b k ].*X;X>X,X,X,X,X,X>X>X;X*Xy.L n n n n v v v v v v v v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q 7 X,X,X,X,X,X>X>X-X[.2.L L n L L L L L l ^ [.-X>X>X,X,X,X,X,X>X;X*X[.[ L n n n v v v v v v v z z v z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 8 X,X,X,X,X,X>X;X*X]._ L L n L L L L K g r.*X;X>X>X,X,X,X,X,X>X;X*X{.R L n v v v v v v v z z z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q - X,X,X,X,X>X>X;X*XE.I L n n n L L L b g H.*X;X>X>X,X,X,X,X>X>X;X*XF.L L v v v v v v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u q 8 X>X>X,X>X>X>X;X*Xk.L L n n n n L L x k _.*X;X>X,X,X,X,X,X>X>X;X*Xy.n n v v v v v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q # X>X>X>X>X;X*X[.2.L L n n n n n b l ~ [.-X>X>X,X,X,X,X,X>X;X*X[.' L n v v v z z z z z z z z z z z z z 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 u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q 7 X>X,X,X,X,X,X>X;X*X{.I n c v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 8 X>X,X,X,X,X>X>X;X*XF.L n v z z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q - X>X,X,X,X,X>X>X;X*X4.n n z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q 7 .L n n n n n n b l E [.*X;X>X>X,X,X,X>X>X;X*X[.' n v z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 8 X>X>X>X>X>X;X*X{.I n z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q - X>X>X;X;X*X[.S.n n z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q 7 c #F79827", -", c #F89825", -"< c #F0962B", -"1 c #F59A2D", -"2 c #F99B2B", -"3 c #EC9732", -"4 c #EC9A37", -"5 c #E2963B", -"6 c #E6983A", -"7 c #EC9C3B", -"8 c #F69D33", -"9 c #F99E32", -"0 c #F49E3A", -"q c #F9A036", -"w c #F6A13C", -"e c #F9A33B", -"r c #D79341", -"t c #DC9641", -"y c #E39A43", -"u c #EA9D42", -"i c #EFA041", -"p c #EDA34B", -"a c #F5A443", -"s c #F9A643", -"d c #FAA846", -"f c #F2A64C", -"g c #F9AA4B", -"h c #E5A251", -"j c #ECA756", -"k c #EBA758", -"l c #FAAF57", -"z c #FBB057", -"x c #FBB25B", -"c c #DFB179", -"v c #E4AA65", -"b c #EBAE64", -"n c #E9AF69", -"m c #FBB665", -"M c #F1B46A", -"N c #F8B96D", -"B c #E5B071", -"V c #EBB777", -"C c #EEB877", -"Z c #E7B478", -"A c #EBB97D", -"S c #F0B671", -"D c #F2B871", -"F c #EFBC80", -"G c #E6BD8D", -"H c #EDBF88", -"J c #E6BF90", -"K c #F1C187", -"L c #F1C288", -"P c #E5C093", -"I c #EEC493", -"U c #E1C19B", -"Y c #E9C69C", -"T c #ECC89D", -"R c #F1C897", -"E c #DFC5A4", -"W c #DBCBB8", -"Q c #E2C7A7", -"! c #EBCBA6", -"~ c #E6CBAB", -"^ c #E9D2B7", -"/ c #E5D1B9", -"( c #EBD6BD", -") c #EFD9BE", -"_ c #DDD0C2", -"` c #DCD7D2", -"' c #DEDEDE", -"] c #ECDAC5", -"[ c #EDDECB", -"{ c #E9E0D5", -"} c #E7E0D9", -"| c #E9E2DB", -" . c #EFE8DF", -".. c #E5E5E5", -"X. c #EBE7E2", -"o. c #EFEAE6", -"O. c #ECECEC", -"+. c #F2ECE6", -"@. c #F1F0EE", -"#. c #F4F4F4", -"$. c #FBFBFB", -"%. c None", -/* pixels */ -"%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.", -"%.%.%.%.%.%.%.%.%.%.%.%.%.%.t 5 5 $ %.%.%.%.%.%.%.%.%.%.%.%.%.%.", -"%.%.%.%.%.%.%.%.%.%.%.r u w q 9 9 9 8 4 # %.%.%.%.%.%.%.%.%.%.%.", -"%.%.%.%.%.%.%.%.%.y s e 9 2 , , , : > 2 9 q 5 %.%.%.%.%.%.%.%.%.", -"%.%.%.%.%.%.%.%.s q 2 , , , , : , > 2 2 > > 2 9 %.%.%.%.%.%.%.%.", -"%.%.%.%.%.%.t e 1 , , , , : : ; > 2 9 9 2 , , > 2 + %.%.%.%.%.%.", -"%.%.%.%.%.$ e 2 , , , , , , ; u u 8 1 1 2 > , , > > + %.%.%.%.%.", -"%.%.%.%.%.e 2 , , : > ; ; > < ` ` 0 c n 1 2 , , , > , %.%.%.%.%.", -"%.%.%.%.e 1 , , , , ; h v - 3 ..! w ' _ 9 2 > , , , > : %.%.%.%.", -"%.%.%.6 q , : , > 2 > W ..| [ #.H V ..D 9 9 2 , , , , , % %.%.%.", -"%.%.%.e 2 , > 2 2 2 9 b ! #.$.$.#.#.#.Y i 1 2 > , , , > ; %.%.%.", -"%.%.@ q > 2 2 2 9 q e q 0 o.$.+.) { #.#.| b 2 2 , , , , : X %.%.", -"%.%.4 9 2 2 9 q e e s w b O.#.( m x I @.$...f > > , , , : & %.%.", -"%.%.8 > 2 2 9 e s d g a P #.#.L x l a [ $.#.A 2 2 , : , , ; %.%.", -"%.+ 1 , , 2 2 q e d g f / $.#.T n k Z o.$.O.M 9 2 > , , , ; X %.", -"%.* 2 , , , 2 9 q e s f X.$.#.O.O.O.#.$.+.Y g e 9 2 , , , ; o %.", -"%.* 2 , , , 2 2 q e w n O.$.[ R ( O.$.$.[ d s e 9 2 2 , , ; o %.", -"%.+ 2 , , , > 2 8 8 1 G #.#.T m m N ] #.#.~ s e e 9 2 > : ; X %.", -"%.%.> , , , , 2 < v B [ $.O.m z z s b #.$...g e e q 9 2 ; = %.%.", -"%.%.= : , , , : 7 ' O.#.$.@.C j p u ~ #.$.} g q 9 9 2 2 ; % %.%.", -"%.%.o , , , , : 0 G ^ .$.#.O.X.{ X.#.$.#.Y e 9 2 2 > , ; %.%.", -"%.%.%., : , , , 2 2 2 M O.) ] #.#.#.#.O./ d 9 2 > , , ; = %.%.%.", -"%.%.%.& ; , , , , 2 ; Q ..g F O.K A H S s 9 2 > , : , ; o %.%.%.", -"%.%.%.%.; ; , , , , 2 E _ d ' ..d q q 9 2 > , : , , ; = %.%.%.%.", -"%.%.%.%.%.; : , , , 2 q d g U J e 2 2 > , , , , , ; = %.%.%.%.%.", -"%.%.%.%.%.o ; : , , , 2 9 q 9 q 9 > , : , , , , ; = . %.%.%.%.%.", -"%.%.%.%.%.%.. ; ; , , > 2 2 2 > , , , , , , , ; = %.%.%.%.%.%.", -"%.%.%.%.%.%.%.%.= ; : > 2 2 , , : , , , , ; ; & %.%.%.%.%.%.%.%.", -"%.%.%.%.%.%.%.%.%.. = ; > : , , , , ; ; = = X %.%.%.%.%.%.%.%.%.", -"%.%.%.%.%.%.%.%.%.%.%. % = ; ; ; ; & O %.%.%.%.%.%.%.%.%.%.%.", -"%.%.%.%.%.%.%.%.%.%.%.%.%.%. X X %.%.%.%.%.%.%.%.%.%.%.%.%.%.", -"%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%." -}; diff --git a/share/pixmaps/bitcoin64.png b/share/pixmaps/bitcoin64.png deleted file mode 100644 index 08c676ae4..000000000 Binary files a/share/pixmaps/bitcoin64.png and /dev/null differ diff --git a/share/pixmaps/bitcoin64.xpm b/share/pixmaps/bitcoin64.xpm deleted file mode 100644 index 851829d41..000000000 --- a/share/pixmaps/bitcoin64.xpm +++ /dev/null @@ -1,242 +0,0 @@ -/* XPM */ -static char *bitcoin__[] = { -/* columns rows colors chars-per-pixel */ -"64 64 172 2", -" c #8F6319", -". c #8F6A1A", -"X c #90651A", -"o c #916C1A", -"O c #AF7C1E", -"+ c #B1781E", -"@ c #9A7026", -"# c #AC801F", -"$ c #B1811F", -"% c #A9812B", -"& c #B08320", -"* c #BB8621", -"= c #BD8E22", -"- c #A58132", -"; c #FC8400", -": c #FD8A03", -"> c #FD8E0C", -", c #FF910E", -"< c #F98F14", -"1 c #F79117", -"2 c #FD9314", -"3 c #FC951B", -"4 c #FE9A1D", -"5 c #CA8E22", -"6 c #CC8E2A", -"7 c #D48D23", -"8 c #C39223", -"9 c #CE9925", -"0 c #D19C25", -"q c #D19329", -"w c #D5992B", -"e c #DD9D33", -"r c #D69F3C", -"t c #E29425", -"y c #E79925", -"u c #EA9926", -"i c #E69A2C", -"p c #F79625", -"a c #F99524", -"s c #F79825", -"d c #F89825", -"f c #F3962A", -"g c #F69B2C", -"h c #F89B2B", -"j c #E19F30", -"k c #EE9B34", -"l c #F49D33", -"z c #F99E32", -"x c #F39F3B", -"c c #DFA731", -"v c #D7A43D", -"b c #DCA63C", -"n c #EEA328", -"m c #FFA225", -"M c #FFAB26", -"N c #F3A529", -"B c #FEA429", -"V c #F4AB2A", -"C c #FFAC2A", -"Z c #FFB325", -"A c #FFB42C", -"S c #FFBB2D", -"D c #E3A335", -"F c #E5A438", -"G c #EDA03D", -"H c #F7A037", -"J c #FAA135", -"K c #F3AB31", -"L c #FEAB31", -"P c #F4A13C", -"I c #F9A33B", -"U c #FDB432", -"Y c #FFBF37", -"T c #FFC12F", -"R c #FFC230", -"E c #FFC03E", -"W c #DFAF41", -"Q c #ECA34D", -"! c #EDA84E", -"~ c #F2A343", -"^ c #FAA642", -"/ c #FAA846", -"( c #F1A74C", -") c #F6A94F", -"_ c #FAAA4A", -"` c #E7A451", -"' c #ECA754", -"] c #EFAA56", -"[ c #ECAC5B", -"{ c #F3AA52", -"} c #FCAE52", -"| c #FBB056", -" . c #FBB25C", -".. c #E7AB61", -"X. c #ECB067", -"o. c #E7B36D", -"O. c #EBB36C", -"+. c #F2B163", -"@. c #FCB460", -"#. c #F0B56B", -"$. c #E3B274", -"%. c #EDB672", -"&. c #EDB877", -"*. c #E2B57C", -"=. c #ECB97B", -"-. c #E4BA83", -";. c #EBBD83", -":. c #E7BF8D", -">. c #EBBD88", -",. c #E9C08C", -"<. c #E7C496", -"1. c #EBC393", -"2. c #EBC997", -"3. c #E7C49A", -"4. c #E9C69A", -"5. c #E3CA9D", -"6. c #E9C89E", -"7. c #DCC9AE", -"8. c #DDCBB2", -"9. c #E3C7A2", -"0. c #E5CAA3", -"q. c #E9CBA3", -"w. c #E5CEAB", -"e. c #E8CEAA", -"r. c #E4D4AC", -"t. c #EBD2AF", -"y. c #E7CFB2", -"u. c #E1D4B4", -"i. c #E8D5B6", -"p. c #E5D7BB", -"a. c #E9D6BB", -"s. c #E5D8B9", -"d. c #EAD8BE", -"f. c #F0D6B4", -"g. c #DFDFC6", -"h. c #E3D6C1", -"j. c #E9D7C0", -"k. c #E6DAC5", -"l. c #EBDCC7", -"z. c #E5DCCA", -"x. c #EADEC9", -"c. c #E8DFD0", -"v. c #D7E2D9", -"b. c #E3E0C9", -"n. c #EEE2CB", -"m. c #E6E1D4", -"M. c #E9E2D3", -"N. c #E4E4DC", -"B. c #E9E5DE", -"V. c #F4EDDE", -"C. c #DFE8E6", -"Z. c #DEEEE8", -"A. c #DFF2F3", -"S. c #DDFFFF", -"D. c #E1E6E0", -"F. c #E8E6E2", -"G. c #E8E9E5", -"H. c #E5EFEC", -"J. c #E8E9EA", -"K. c #EAF3EE", -"L. c #F3F3EB", -"P. c #E7EDF2", -"I. c #E8EEF3", -"U. c #E7F4F7", -"Y. c #E9F0F7", -"T. c #EBF5FD", -"R. c #E4FEFF", -"E. c #ECFCFF", -"W. c #F4F5F4", -"Q. c #F4FFFF", -"!. c #FEFFFF", -"~. c None", -/* pixels */ -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.F L h C C A A A A C C h L e ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.D N C m d d a a p a a p a a d m m C N j ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.- K M m a p s d d d d d d d d d d d d s p d m M V % ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.Y M d a d d d d d d d d d d d d d d d d h h d s a d M U ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.E m 4 a d d d d d d d d d d d d d d d d d d h h h d d d a d M U ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.C 4 a d d d d d d d d d d d d d d d d d h h h h h h d d d d d a m C ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.W S a p d d d d d d d d d d d d d d d d h h h h g g h h h d d d d d p a S c ~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.v M a s d d d d d d d d d d d d d d d h h h h h g z z g h h d d d d d d s a C w ~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.r Z a d d d d d d d d d d d d d d d g 4 : 2 h z z z z z h h h h d d d d d d d a S q ~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.b Z a d d d d d d d d d d d d d d h h 4 x $.l a z H h h H z h h h d d d d d d d d a A w ~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.T a s d d d d d d d d d d d d h h h g : $.R.T.7.a B x f > a H h h d d d d d d d d s a R ~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.U a s d d d d d d d d d d d d h h h h z : e.!.!.p.2 3 8.D.5.' a h h h d d d d d d d d p d A ~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.U M p d d d d d d d d d d h h 1 : : 2 h h p B.!.Q.%., l J.!.R.-.> z h h h d d d d d d d d p C N ~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.S a d d d d d d d d d d h d 3 7.r.O.G p ; k E.!.T.( , [ E.!.T.~ 4 z h h h d d d d d d d d d a S ~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.V d s d d d d d d d d h h h 2 l E.!.Q.T.m.:.q.!.!.l.: : -.Q.!.c.a z z z g h h d d d d d d d d s m A ~.~.~.~.~.~.~.", -"~.~.~.~.~.~.@ S a d d d d d d d h h h h z : *.R.!.!.!.!.Q.!.!.!.V.,.Q d.!.Q.1.2 I z z h h h d d d d d d d d d a S X ~.~.~.~.~.~.", -"~.~.~.~.~.~.U d s d d d d d h h h h h g z a [ 5.M.Q.!.!.!.!.!.!.!.Q.E.!.!.Q.&.; 3 J H z h h h d d d d d d d d s h C ~.~.~.~.~.~.", -"~.~.~.~.~.~.S a d d d d h h h h h h z z z I d > < %.W.!.!.!.!.!.!.!.!.!.!.!.W.s.[ > 4 H g h h d d d d d d d d d a S ~.~.~.~.~.~.", -"~.~.~.~.~.i M p d d d h h h h g z z z z J H I I J > x.!.!.!.!.Q.T.E.Q.!.!.!.!.!.E.u.f 2 H h h h d d d d d d d d p C 7 ~.~.~.~.~.", -"~.~.~.~.~.C a d h h h h h g g z z z J J I I I I J P J.!.!.!.!.d.P =.e.G.E.!.!.!.!.Q.Z.f 2 z h h d d d d d d d d d d A ~.~.~.~.~.", -"~.~.~.~.~.A a h h h h h g z z z J H I I I I ^ / d X.E.!.!.!.Q.1.4 I J I ;.U.!.!.!.!.!.N.1 h g h h d d d d d d d d a S ~.~.~.~.~.", -"~.~.~.~.6 C p d h h h z z J J J I I I I ^ ^ ^ _ a 3.Q.!.!.!.E.#.I . ._ 3 ] K.!.!.!.!.E.O., z h h h d d d d d d d p A + ~.~.~.~.", -"~.~.~.~.i B d d h h h g z J I I I I ^ ^ ^ / / _ h k.!.!.!.!.J.) } . .| .3 6.Q.!.!.!.Q.q.> z g h h d d d d d d d d B t ~.~.~.~.", -"~.~.~.~.B d d d d h h h z z J I I ^ / / / _ _ ^ ( I.!.!.!.Q.d.I . . .| .d 1.Q.!.!.!.Q.q.2 z h h h d d d d d d d d d B ~.~.~.~.", -"~.~.~.~.C a d d d d h h g z J H I ^ ^ / _ _ } J %.E.!.!.!.Q.;.4 _ } | } J f m.!.!.!.!.Q.;.2 J z g h h d d d d d d d a A ~.~.~.~.", -"~.~.~.~.C a d d d d h h h z z J I I ^ ^ / _ } z 6.Q.!.!.!.!.n.<.&.+.{ ) ] h.Q.!.!.!.!.R.~ d H z z h h h d d d d d d a A ~.~.~.~.", -"~.~.~.~.A a d d d d d h h g z z H I I ^ / _ _ z k.!.!.!.!.!.!.Q.E.I.F.F.T.Q.!.!.!.!.E.9.2 I J z z h h h d d d d d d d A ~.~.~.~.", -"~.~.~.~.S a d d d d d h h h z z J I I ^ ^ / I ( P.!.!.!.!.Q.Q.!.!.!.!.!.!.!.!.!.!.E.w.d J I I J z h h h d d d d d d d A ~.~.~.~.", -"~.~.~.~.A a d d d d d d h h h z J J I I ^ / h O.E.!.!.!.Q.f.1.z.Y.E.!.!.!.!.!.!.L.! , ^ / I I H z z h h h d d d d d d A ~.~.~.~.", -"~.~.~.~.S p d d d d d d h h h z z J I I ^ / d <.Q.!.!.!.E.+.d _ +.>.k.E.!.!.!.!.Q.s.P J _ ^ I I J z z h h h d d d d d A ~.~.~.~.", -"~.~.~.~.C a d d d d d d d h h g z z H I I ^ d k.!.!.!.!.J.{ | @.} I I O.H.!.!.!.!.Q.C.l I ^ I I H J z g h h d d d d a A ~.~.~.~.", -"~.~.~.~.B a d d d d d d d h h h h z z J I J x P.!.!.!.Q.j.I . . . . .B { K.!.!.!.!.Q.0.a / ^ I I J z z h h h d d d a A ~.~.~.~.", -"~.~.~.~.B d d d d d d d d d h h h J h f 2 ; [ E.!.!.!.Q.1.I . . .| | .d 4.Q.!.!.!.!.m.z I ^ I I I J z h h h h d d d B ~.~.~.~.", -"~.~.~.~.u B d d d d d d d d h h z , ' v.q.X.M.!.!.!.!.E.#.^ . .| } } } d >.Q.!.!.!.!.F.x J I I I J J z z h h h d d C t ~.~.~.~.", -"~.~.~.~.7 C p d d d d d d d d h h : y.Q.Q.Q.!.!.!.!.!.B.d B / _ } } } J 1 k.!.!.!.!.!.c.s J I H J J z z z h h h h s A + ~.~.~.~.", -"~.~.~.~.~.A a d d d d d d d d h > ` R.!.!.!.!.!.!.!.!.L.q.=.[ ~ z h h l 0.Q.!.!.!.!.Q.q.2 I J J z z h h h h h h h a S ~.~.~.~.~.", -"~.~.~.~.~.C d d d d d d d d d d > ..g.Y.E.Q.!.!.!.!.!.!.Q.E.T.B.k.a.d.P.Q.!.!.!.!.!.E.[ 2 J z z z g h h h h d d d d C ~.~.~.~.~.", -"~.~.~.~.~.y C p d d d d d d d d g 3 > l [ <.x.W.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.Q.z.> z z z h h h h h d d d d p C 7 ~.~.~.~.~.", -"~.~.~.~.~.~.S a d d d d d d d d d h h 3 , > ; =.Q.!.W.T.Q.!.!.!.!.!.!.!.!.!.!.!.Q.A.g 2 z h h h h h h d d d d d a S ~.~.~.~.~.~.", -"~.~.~.~.~.~.C h s d d d d d d d d d h g z H : <.!.!.t.l &.V.!.!.Q.Q.Q.Q.!.Q.Q.E.b.l > H h h h h h d d d d d d s m C ~.~.~.~.~.~.", -"~.~.~.~.~.~.X S a d d d d d d d d d h h h h p N.!.Q.=.: < c.!.Q.2.&.e.a.d.i.6.[ < 2 z h h h h d d d d d d d d a S ~.~.~.~.~.~.", -"~.~.~.~.~.~.~.A h s d d d d d d d d d h g 2 ~ E.!.E.{ 2 [ E.!.T.l : 2 1 3 2 > > h z h h h h d d d d d d d d s m A ~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.S a d d d d d d d d d h h : -.R.!.B.h 2 =.Q.!.M.p z z z h h z g h h h d d d d d d d d d d d a S ~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.N C p d d d d d d d d d h 3 ' 2.N.9.2 3 z.!.!.q.> J z h h h h h h d d d d d d d d d d d d p C n ~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.S h p d d d d d d d d d z 3 : p l J g 8.T.S.O.> z h h h h h d d d d d d d d d d d d d p h S ~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.S a s d d d d d d d d h h z d h I J a P o.P d g h h h d d d d d d d d d d d d d d s a S ~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.* S a s d d d d d d d d h h g z J J h 3 > d z h h h d d d d d d d d d d d d d d s a S * ~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.$ T a s d d d d d d d h h h z z z h g g h h d d d d d d d d d d d d d d d d s a T O ~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.& S a p d d d d d d h h h z g h h h h h d d d d d d d d d d d d d d d d p a S # ~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.8 S d p d d d d d d h h g h h h h d d d d d d d d d d d d d d d d d p h S = ~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.S A a s d d d d h h h h h d d d d d d d d d d d d d d d d d s a A S ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.0 T m p d d d d h h h d d d d d d d d d d d d d d d d d p B S 9 ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.V S m a p d h d d d d d d d d d d d d d d d d p a m S V ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.o V S C d p p d d d d d d d d d d d d p p d C S N . ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.5 C S A B d d a a d d a a a d B A S C 5 ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.O t B A A A A A A A A B t O ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~." -}; diff --git a/share/pixmaps/check.ico b/share/pixmaps/check.ico deleted file mode 100644 index 0c4e6e814..000000000 Binary files a/share/pixmaps/check.ico and /dev/null differ diff --git a/share/pixmaps/favicon.ico b/share/pixmaps/favicon.ico deleted file mode 100644 index 754eebc48..000000000 Binary files a/share/pixmaps/favicon.ico and /dev/null differ diff --git a/share/pixmaps/nsis-header.bmp b/share/pixmaps/nsis-header.bmp deleted file mode 100644 index 9ab0ce259..000000000 Binary files a/share/pixmaps/nsis-header.bmp and /dev/null differ diff --git a/share/pixmaps/nsis-wizard.bmp b/share/pixmaps/nsis-wizard.bmp deleted file mode 100644 index 71255c685..000000000 Binary files a/share/pixmaps/nsis-wizard.bmp and /dev/null differ diff --git a/share/pixmaps/send16.bmp b/share/pixmaps/send16.bmp deleted file mode 100644 index 676b5c4b4..000000000 Binary files a/share/pixmaps/send16.bmp and /dev/null differ diff --git a/share/pixmaps/send16mask.bmp b/share/pixmaps/send16mask.bmp deleted file mode 100644 index 06c747f93..000000000 Binary files a/share/pixmaps/send16mask.bmp and /dev/null differ diff --git a/share/pixmaps/send16masknoshadow.bmp b/share/pixmaps/send16masknoshadow.bmp deleted file mode 100644 index faf24e0d8..000000000 Binary files a/share/pixmaps/send16masknoshadow.bmp and /dev/null differ diff --git a/share/pixmaps/send20.bmp b/share/pixmaps/send20.bmp deleted file mode 100644 index 2b90422b3..000000000 Binary files a/share/pixmaps/send20.bmp and /dev/null differ diff --git a/share/pixmaps/send20mask.bmp b/share/pixmaps/send20mask.bmp deleted file mode 100644 index f124d0da0..000000000 Binary files a/share/pixmaps/send20mask.bmp and /dev/null differ diff --git a/share/setup.nsi.in b/share/setup.nsi.in deleted file mode 100644 index 6c0e895bb..000000000 --- a/share/setup.nsi.in +++ /dev/null @@ -1,179 +0,0 @@ -Name "@PACKAGE_NAME@ (@WINDOWS_BITS@-bit)" - -RequestExecutionLevel highest -SetCompressor /SOLID lzma - -# General Symbol Definitions -!define REGKEY "SOFTWARE\$(^Name)" -!define VERSION @CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@.@CLIENT_VERSION_REVISION@ -!define COMPANY "Bitcoin Core project" -!define URL http://www.bitcoin.org/ - -# MUI Symbol Definitions -!define MUI_ICON "@abs_top_srcdir@/share/pixmaps/bitcoin.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 $INSTDIR\bitcoin-qt.exe -!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 -!if "@WINDOWS_BITS@" == "64" -!include x64.nsh -!endif - -# 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 -OutFile @abs_top_srcdir@/bitcoin-${VERSION}-win@WINDOWS_BITS@-setup.exe -!if "@WINDOWS_BITS@" == "64" -InstallDir $PROGRAMFILES64\Bitcoin -!else -InstallDir $PROGRAMFILES\Bitcoin -!endif -CRCCheck on -XPStyle on -BrandingText " " -ShowInstDetails show -VIProductVersion ${VERSION}.@CLIENT_VERSION_BUILD@ -VIAddVersionKey ProductName "Bitcoin Core" -VIAddVersionKey ProductVersion "${VERSION}" -VIAddVersionKey CompanyName "${COMPANY}" -VIAddVersionKey CompanyWebsite "${URL}" -VIAddVersionKey FileVersion "${VERSION}" -VIAddVersionKey FileDescription "" -VIAddVersionKey LegalCopyright "" -InstallDirRegKey HKCU "${REGKEY}" Path -ShowUninstDetails show - -# Installer sections -Section -Main SEC0000 - SetOutPath $INSTDIR - SetOverwrite on - File @abs_top_srcdir@/release/bitcoin-qt.exe - File /oname=COPYING.txt @abs_top_srcdir@/COPYING - File /oname=readme.txt @abs_top_srcdir@/doc/README_windows.txt - SetOutPath $INSTDIR\daemon - File @abs_top_srcdir@/release/bitcoind.exe - File @abs_top_srcdir@/release/bitcoin-cli.exe - SetOutPath $INSTDIR\doc - File /r @abs_top_srcdir@/doc\*.* - SetOutPath $INSTDIR - WriteRegStr HKCU "${REGKEY}\Components" Main 1 - - # Remove old wxwidgets-based-bitcoin executable and locales: - Delete /REBOOTOK $INSTDIR\bitcoin.exe - RMDir /r /REBOOTOK $INSTDIR\locale -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\bitcoin-qt.exe - 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 "${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\uninstall.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 "bitcoin" "URL Protocol" "" - WriteRegStr HKCR "bitcoin" "" "URL:Bitcoin" - WriteRegStr HKCR "bitcoin\DefaultIcon" "" $INSTDIR\bitcoin-qt.exe - WriteRegStr HKCR "bitcoin\shell\open\command" "" '"$INSTDIR\bitcoin-qt.exe" "%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\bitcoin-qt.exe - Delete /REBOOTOK $INSTDIR\COPYING.txt - Delete /REBOOTOK $INSTDIR\readme.txt - RMDir /r /REBOOTOK $INSTDIR\daemon - RMDir /r /REBOOTOK $INSTDIR\doc - 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 "$SMSTARTUP\Bitcoin.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 "bitcoin" - 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 "@WINDOWS_BITS@" == "64" - ${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} -!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/share/ui.rc b/share/ui.rc deleted file mode 100644 index 063641cba..000000000 --- a/share/ui.rc +++ /dev/null @@ -1,15 +0,0 @@ -bitcoin ICON "pixmaps/bitcoin.ico" - -#include "wx/msw/wx.rc" - -check ICON "pixmaps/check.ico" -send16 BITMAP "pixmaps/send16.bmp" -send16mask BITMAP "pixmaps/send16mask.bmp" -send16masknoshadow BITMAP "pixmaps/send16masknoshadow.bmp" -send20 BITMAP "pixmaps/send20.bmp" -send20mask BITMAP "pixmaps/send20mask.bmp" -addressbook16 BITMAP "pixmaps/addressbook16.bmp" -addressbook16mask BITMAP "pixmaps/addressbook16mask.bmp" -addressbook20 BITMAP "pixmaps/addressbook20.bmp" -addressbook20mask BITMAP "pixmaps/addressbook20mask.bmp" -favicon ICON "pixmaps/favicon.ico" diff --git a/src/Makefile.am b/src/Makefile.am index 72805281a..00acd5e05 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,8 @@ -DIST_SUBDIRS = secp256k1 univalue -AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) +DIST_SUBDIRS = secp256k1 univalue cryptoconditions +AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(SAN_LDFLAGS) $(HARDENED_LDFLAGS) +AM_CXXFLAGS = $(SAN_CXXFLAGS) $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) +AM_CPPFLAGS = $(HARDENED_CPPFLAGS) if EMBEDDED_LEVELDB LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/include @@ -14,13 +16,16 @@ $(LIBLEVELDB): $(LIBMEMENV) $(LIBLEVELDB) $(LIBMEMENV): @echo "Building LevelDB ..." && $(MAKE) -C $(@D) $(@F) CXX="$(CXX)" \ CC="$(CC)" PLATFORM=$(TARGET_OS) AR="$(AR)" $(LEVELDB_TARGET_FLAGS) \ - OPT="$(CXXFLAGS) $(CPPFLAGS) -D__STDC_LIMIT_MACROS" + 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 $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS) BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include +BITCOIN_INCLUDES += -I$(srcdir)/cryptoconditions/include +BITCOIN_INCLUDES += -I$(srcdir)/snark +BITCOIN_INCLUDES += -I$(srcdir)/snark/libsnark BITCOIN_INCLUDES += -I$(srcdir)/univalue/include if TARGET_WINDOWS @@ -38,15 +43,32 @@ LIBBITCOIN_CLI=libbitcoin_cli.a LIBBITCOIN_UTIL=libbitcoin_util.a LIBBITCOIN_CRYPTO=crypto/libbitcoin_crypto.a LIBSECP256K1=secp256k1/libsecp256k1.la +LIBCRYPTOCONDITIONS=cryptoconditions/libcryptoconditions_core.la +LIBSNARK=snark/libsnark.a LIBUNIVALUE=univalue/libunivalue.la LIBZCASH=libzcash.a -lcurl $(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) +LIBSNARK_CXXFLAGS = -fPIC -DBINARY_OUTPUT -DNO_PT_COMPRESSION=1 -fstack-protector-all +LIBSNARK_CONFIG_FLAGS = CURVE=ALT_BN128 NO_PROCPS=1 NO_DOCS=1 STATIC=1 NO_SUPERCOP=1 FEATUREFLAGS=-DMONTGOMERY_OUTPUT NO_COPY_DEPINST=1 NO_COMPILE_LIBGTEST=1 +if HAVE_OPENMP +LIBSNARK_CONFIG_FLAGS += MULTICORE=1 +endif + +$(LIBSNARK): $(wildcard snark/src/*) + $(AM_V_at) CXXFLAGS="$(LIBSNARK_CXXFLAGS)" $(MAKE) $(AM_MAKEFLAGS) -C snark/ DEPINST="$(LIBSNARK_DEPINST)" $(LIBSNARK_CONFIG_FLAGS) OPTFLAGS="-O2 -march=x86-64" + +libsnark-tests: $(wildcard snark/src/*) + $(AM_V_at) CXXFLAGS="$(LIBSNARK_CXXFLAGS)" $(MAKE) $(AM_MAKEFLAGS) -C snark/ check DEPINST="$(LIBSNARK_DEPINST)" $(LIBSNARK_CONFIG_FLAGS) OPTFLAGS="-O2 -march=x86-64" + $(LIBUNIVALUE): $(wildcard univalue/lib/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C univalue/ +$(LIBCRYPTOCONDITIONS): $(wildcard cryptoconditions/src/*) $(wildcard cryptoconditions/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 = \ @@ -63,6 +85,9 @@ endif if ENABLE_ZMQ EXTRA_LIBRARIES += libbitcoin_zmq.a endif +if ENABLE_PROTON +EXTRA_LIBRARIES += libbitcoin_proton.a +endif if BUILD_BITCOIN_LIBS lib_LTLIBRARIES = libzcashconsensus.la @@ -94,17 +119,23 @@ LIBZCASH_H = \ zcash/util.h \ zcash/Zcash.h -.PHONY: FORCE check-symbols check-security +.PHONY: FORCE collate-libsnark check-symbols check-security # bitcoin core # 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 \ base58.h \ bloom.h \ + cc/eval.h \ chain.h \ chainparams.h \ chainparamsbase.h \ @@ -121,11 +152,11 @@ BITCOIN_CORE_H = \ compressor.h \ consensus/consensus.h \ consensus/params.h \ + consensus/upgrades.h \ consensus/validation.h \ core_io.h \ core_memusage.h \ - eccryptoverify.h \ - ecwrapper.h \ + deprecation.h \ hash.h \ httprpc.h \ httpserver.h \ @@ -143,6 +174,8 @@ BITCOIN_CORE_H = \ net.h \ netbase.h \ noui.h \ + paymentdisclosure.h \ + paymentdisclosuredb.h \ policy/fees.h \ pow.h \ primitives/block.h \ @@ -158,7 +191,7 @@ BITCOIN_CORE_H = \ script/interpreter.h \ script/script.h \ script/script_error.h \ - script/sigcache.h \ + script/serverchecker.h \ script/sign.h \ script/standard.h \ serialize.h \ @@ -185,7 +218,9 @@ BITCOIN_CORE_H = \ utiltime.h \ validationinterface.h \ version.h \ + wallet/asyncrpcoperation_mergetoaddress.h \ wallet/asyncrpcoperation_sendmany.h \ + wallet/asyncrpcoperation_shieldcoinbase.h \ wallet/crypter.h \ wallet/db.h \ wallet/wallet.h \ @@ -204,7 +239,8 @@ obj/build.h: FORCE libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h # server: zcashd -libbitcoin_server_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS) +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 \ @@ -213,8 +249,13 @@ libbitcoin_server_a_SOURCES = \ asyncrpcoperation.cpp \ asyncrpcqueue.cpp \ bloom.cpp \ + cc/eval.cpp \ + cc/importpayout.cpp \ + cc/disputepayout.cpp \ + cc/betprotocol.cpp \ chain.cpp \ checkpoints.cpp \ + deprecation.cpp \ httprpc.cpp \ httpserver.cpp \ init.cpp \ @@ -225,6 +266,8 @@ libbitcoin_server_a_SOURCES = \ miner.cpp \ net.cpp \ noui.cpp \ + paymentdisclosure.cpp \ + paymentdisclosuredb.cpp \ policy/fees.cpp \ pow.cpp \ rest.cpp \ @@ -234,7 +277,7 @@ libbitcoin_server_a_SOURCES = \ rpcnet.cpp \ rpcrawtransaction.cpp \ rpcserver.cpp \ - script/sigcache.cpp \ + script/serverchecker.cpp \ timedata.cpp \ torcontrol.cpp \ txdb.cpp \ @@ -247,23 +290,40 @@ if ENABLE_ZMQ LIBBITCOIN_ZMQ=libbitcoin_zmq.a libbitcoin_zmq_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(ZMQ_CFLAGS) +libbitcoin_zmq_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_zmq_a_SOURCES = \ zmq/zmqabstractnotifier.cpp \ zmq/zmqnotificationinterface.cpp \ zmq/zmqpublishnotifier.cpp endif +if ENABLE_PROTON +LIBBITCOIN_PROTON=libbitcoin_proton.a + +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: zcashd, but only linked when wallet enabled -libbitcoin_wallet_a_CPPFLAGS = $(BITCOIN_INCLUDES) +libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_wallet_a_SOURCES = \ utiltest.cpp \ utiltest.h \ zcbenchmarks.cpp \ zcbenchmarks.h \ + wallet/asyncrpcoperation_mergetoaddress.cpp \ wallet/asyncrpcoperation_sendmany.cpp \ + wallet/asyncrpcoperation_shieldcoinbase.cpp \ wallet/crypter.cpp \ wallet/db.cpp \ + paymentdisclosure.cpp \ + paymentdisclosuredb.cpp \ + wallet/rpcdisclosure.cpp \ wallet/rpcdump.cpp \ wallet/rpcwallet.cpp \ wallet/wallet.cpp \ @@ -273,7 +333,8 @@ libbitcoin_wallet_a_SOURCES = \ $(LIBZCASH_H) # crypto primitives library -crypto_libbitcoin_crypto_a_CPPFLAGS = $(BITCOIN_CONFIG_INCLUDES) +crypto_libbitcoin_crypto_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_CONFIG_INCLUDES) +crypto_libbitcoin_crypto_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) crypto_libbitcoin_crypto_a_SOURCES = \ crypto/common.h \ crypto/equihash.cpp \ @@ -305,7 +366,8 @@ crypto_libbitcoin_crypto_a_SOURCES += \ endif # common: shared between zcashd and non-server tools -libbitcoin_common_a_CPPFLAGS = $(BITCOIN_INCLUDES) +libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_common_a_SOURCES = \ amount.cpp \ arith_uint256.cpp \ @@ -313,10 +375,9 @@ libbitcoin_common_a_SOURCES = \ chainparams.cpp \ coins.cpp \ compressor.cpp \ + consensus/upgrades.cpp \ core_read.cpp \ core_write.cpp \ - eccryptoverify.cpp \ - ecwrapper.cpp \ hash.cpp \ key.cpp \ keystore.cpp \ @@ -326,6 +387,7 @@ libbitcoin_common_a_SOURCES = \ protocol.cpp \ pubkey.cpp \ scheduler.cpp \ + script/cc.cpp \ script/interpreter.cpp \ script/script.cpp \ script/script_error.cpp \ @@ -337,7 +399,8 @@ libbitcoin_common_a_SOURCES = \ # util: shared between all executables. # This library *must* be included to make sure that the glibc # backward-compatibility objects and their sanity checks are linked. -libbitcoin_util_a_CPPFLAGS = $(BITCOIN_INCLUDES) +libbitcoin_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +libbitcoin_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_util_a_SOURCES = \ support/pagelocker.cpp \ chainparamsbase.cpp \ @@ -362,7 +425,8 @@ libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp endif # cli: zcash-cli -libbitcoin_cli_a_CPPFLAGS = $(BITCOIN_INCLUDES) +libbitcoin_cli_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +libbitcoin_cli_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_cli_a_SOURCES = \ rpcclient.cpp \ $(BITCOIN_CORE_H) \ @@ -373,7 +437,8 @@ nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h # bitcoind binary # komodod_SOURCES = bitcoind.cpp -komodod_CPPFLAGS = $(BITCOIN_INCLUDES) +komodod_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +komodod_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) komodod_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) if TARGET_WINDOWS @@ -387,9 +452,11 @@ komodod_LDADD = \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CRYPTO) \ $(LIBZCASH) \ + $(LIBSNARK) \ $(LIBLEVELDB) \ $(LIBMEMENV) \ - $(LIBSECP256K1) + $(LIBSECP256K1) \ + $(LIBCRYPTOCONDITIONS) if ENABLE_ZMQ komodod_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) @@ -404,16 +471,19 @@ komodod_LDADD += \ $(BDB_LIBS) \ $(SSL_LIBS) \ $(CRYPTO_LIBS) \ - $(MINIUPNPC_LIBS) \ $(EVENT_PTHREADS_LIBS) \ $(EVENT_LIBS) \ - $(LIBZCASH) \ $(LIBBITCOIN_CRYPTO) \ $(LIBZCASH_LIBS) +if ENABLE_PROTON +komodod_LDADD += $(LIBBITCOIN_PROTON) $(PROTON_LIBS) +endif + # bitcoin-cli binary # komodo_cli_SOURCES = bitcoin-cli.cpp -komodo_cli_CPPFLAGS = $(BITCOIN_INCLUDES) $(EVENT_CFLAGS) +komodo_cli_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CFLAGS) +komodo_cli_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) komodo_cli_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) if TARGET_WINDOWS @@ -429,13 +499,15 @@ komodo_cli_LDADD = \ $(CRYPTO_LIBS) \ $(EVENT_LIBS) \ $(LIBZCASH) \ + $(LIBSNARK) \ $(LIBBITCOIN_CRYPTO) \ $(LIBZCASH_LIBS) # # zcash-tx binary # komodo_tx_SOURCES = komodo-tx.cpp -komodo_tx_CPPFLAGS = $(BITCOIN_INCLUDES) +komodo_tx_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +komodo_tx_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) komodo_tx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) if TARGET_WINDOWS @@ -449,8 +521,10 @@ komodo_tx_LDADD = \ $(LIBBITCOIN_UTIL) \ $(LIBSECP256K1) \ $(LIBZCASH) \ + $(LIBSNARK) \ $(LIBBITCOIN_CRYPTO) \ - $(LIBZCASH_LIBS) + $(LIBZCASH_LIBS) \ + $(LIBCRYPTOCONDITIONS) komodo_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) # @@ -472,12 +546,10 @@ libzcash_a_SOURCES = \ zcash/circuit/prfs.tcc \ zcash/circuit/utils.tcc -libzcash_a_CPPFLAGS = -DMULTICORE -fopenmp -fPIC -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DBOOST_SPIRIT_THREADSAFE -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS $(HARDENED_CPPFLAGS) -pipe -O1 -g -Wstack-protector -fstack-protector-all -fPIE -fvisibility=hidden -DSTATIC $(BITCOIN_INCLUDES) - -libzcash_a_CXXFLAGS = $(HARDENED_CXXFLAGS) -fwrapv -fno-strict-aliasing - -#libzcash_a_LDFLAGS = $(HARDENED_LDFLAGS) +libzcash_a_CPPFLAGS = -DMULTICORE -fopenmp -fPIC -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DBOOST_SPIRIT_THREADSAFE -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS $(HARDENED_CPPFLAGS) $(HARDENED_CXXFLAGS) $(HARDENED_LDFLAGS) -pipe $(SAN_LDFLAGS) -O1 -g -Wstack-protector $(SAN_CXXFLAGS) -fstack-protector-all -fPIE -fvisibility=hidden -DSTATIC $(BITCOIN_INCLUDES) +libzcash_a_CXXFLAGS = $(SAN_CXXFLAGS) $(HARDENED_CXXFLAGS) -fwrapv -fno-strict-aliasing +libzcash_a_LDFLAGS = $(SAN_LDFLAGS) $(HARDENED_LDFLAGS) libzcash_a_CPPFLAGS += -DMONTGOMERY_OUTPUT # zcashconsensus library # @@ -490,8 +562,6 @@ libzcashconsensus_la_SOURCES = \ crypto/sha1.cpp \ crypto/sha256.cpp \ crypto/sha512.cpp \ - eccryptoverify.cpp \ - ecwrapper.cpp \ hash.cpp \ primitives/transaction.cpp \ pubkey.cpp \ @@ -505,9 +575,10 @@ if GLIBC_BACK_COMPAT libzcashconsensus_la_SOURCES += compat/glibc_compat.cpp endif -libzcashconsensus_la_LDFLAGS = -no-undefined $(RELDFLAGS) -libzcashconsensus_la_LIBADD = $(CRYPTO_LIBS) -libzcashconsensus_la_CPPFLAGS = $(CRYPTO_CFLAGS) -I$(builddir)/obj -DBUILD_BITCOIN_INTERNAL +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 # @@ -516,11 +587,12 @@ CLEANFILES = leveldb/libleveldb.a leveldb/libmemenv.a *.gcda *.gcno */*.gcno wal DISTCLEANFILES = obj/build.h -EXTRA_DIST = leveldb +EXTRA_DIST = leveldb snark clean-local: -$(MAKE) -C leveldb clean -$(MAKE) -C secp256k1 clean + -$(MAKE) -C snark clean rm -f leveldb/*/*.gcno leveldb/helpers/memenv/*.gcno -rm -f config.h @@ -530,27 +602,28 @@ clean-local: .mm.o: $(AM_V_CXX) $(OBJCXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ - $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o $@ $< + $(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) + $(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)]..." - $(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) $(top_srcdir)/contrib/devtools/security-check.py < $(bin_PROGRAMS) + $(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) $(top_srcdir)/contrib/devtools/security-check.py $(bin_PROGRAMS) endif %.pb.cc %.pb.h: %.proto @test -f $(PROTOC) $(AM_V_GEN) $(PROTOC) --cpp_out=$(@D) --proto_path=$(abspath $( 0) @@ -27,5 +29,5 @@ CAmount CFeeRate::GetFee(size_t nSize) const std::string CFeeRate::ToString() const { - return strprintf("%d.%08d BTC/kB", nSatoshisPerK / COIN, nSatoshisPerK % COIN); + return strprintf("%d.%08d %s/kB", nSatoshisPerK / COIN, nSatoshisPerK % COIN, CURRENCY_UNIT); } diff --git a/src/amount.h b/src/amount.h index d49ab0723..9b62f7663 100644 --- a/src/amount.h +++ b/src/amount.h @@ -16,6 +16,8 @@ typedef int64_t CAmount; static const CAmount COIN = 100000000; static const CAmount CENT = 1000000; +extern const std::string CURRENCY_UNIT; + /** No amount larger than this (in satoshi) is valid. * * Note that this constant is *not* the total money supply, which in Bitcoin diff --git a/src/amqp/amqpabstractnotifier.cpp b/src/amqp/amqpabstractnotifier.cpp new file mode 100644 index 000000000..57686ef1d --- /dev/null +++ b/src/amqp/amqpabstractnotifier.cpp @@ -0,0 +1,21 @@ +// Copyright (c) 2017 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://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 new file mode 100644 index 000000000..c993a2b3e --- /dev/null +++ b/src/amqp/amqpabstractnotifier.h @@ -0,0 +1,43 @@ +// Copyright (c) 2017 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://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 new file mode 100644 index 000000000..dcc5f7709 --- /dev/null +++ b/src/amqp/amqpconfig.h @@ -0,0 +1,33 @@ +// Copyright (c) 2017 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://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 new file mode 100644 index 000000000..66f5398ca --- /dev/null +++ b/src/amqp/amqpnotificationinterface.cpp @@ -0,0 +1,136 @@ +// Copyright (c) 2017 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://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 new file mode 100644 index 000000000..0c07ce235 --- /dev/null +++ b/src/amqp/amqpnotificationinterface.h @@ -0,0 +1,36 @@ +// Copyright (c) 2017 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://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 new file mode 100644 index 000000000..589eb151f --- /dev/null +++ b/src/amqp/amqppublishnotifier.cpp @@ -0,0 +1,177 @@ +// Copyright (c) 2017 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "amqppublishnotifier.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()); + + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + { + LOCK(cs_main); + CBlock block; + if(!ReadBlockFromDisk(block, pindex)) { + 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 new file mode 100644 index 000000000..08b3aba08 --- /dev/null +++ b/src/amqp/amqppublishnotifier.h @@ -0,0 +1,56 @@ +// Copyright (c) 2017 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://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 new file mode 100644 index 000000000..7fa85d89c --- /dev/null +++ b/src/amqp/amqpsender.h @@ -0,0 +1,115 @@ +// Copyright (c) 2017 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://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/assetchains b/src/assetchains index 1ca93b3c2..517687097 100755 --- a/src/assetchains +++ b/src/assetchains @@ -1,7 +1,7 @@ #!/bin/bash source pubkey.txt args=("$@") -seed_ip=`getent hosts seed.mewhub.com | awk '{ print $1 }'` +seed_ip=`getent hosts zero.kolo.supernet.org | awk '{ print $1 }'` komodo_binary='./komodod' delay=20 @@ -35,11 +35,9 @@ komodo_asset JUMBLR 999999 komodo_asset BET 999999 komodo_asset CRYPTO 999999 komodo_asset HODL 9999999 -#komodo_asset SHARK 1401 komodo_asset MSHARK 1400000 komodo_asset BOTS 999999 komodo_asset MGW 999999 -#komodo_asset MVP 1000000 komodo_asset COQUI 72000000 komodo_asset WLC 210000000 komodo_asset KV 1000000 @@ -49,37 +47,11 @@ komodo_asset MNZ 257142858 komodo_asset AXO 200000000 komodo_asset ETOMIC 100000000 komodo_asset BTCH 20998641 -komodo_asset VOTE 49999999999 - -#komodo_asset USD -#komodo_asset EUR -#komodo_asset JPY -#komodo_asset GBP -#komodo_asset AUD -#komodo_asset CAD -#komodo_asset CHF -#komodo_asset NZD -#komodo_asset CNY -#komodo_asset RUB -#komodo_asset MXN -#komodo_asset BRL -#komodo_asset INR -#komodo_asset HKD -#komodo_asset TRY -#komodo_asset ZAR -#komodo_asset PLN -#komodo_asset NOK -#komodo_asset SEK -#komodo_asset DKK -#komodo_asset CZK -#komodo_asset HUF -#komodo_asset ILS -#komodo_asset KRW -#komodo_asset MYR -#komodo_asset PHP -#komodo_asset RON -#komodo_asset SGD -#komodo_asset THB -#komodo_asset BGN -#komodo_asset IDR -#komodo_asset HRK +komodo_asset VOTE2018 600000000 +komodo_asset PIZZA 100000000 +komodo_asset BEER 100000000 +komodo_asset NINJA 100000000 +komodo_asset OOT 216000000 +komodo_asset BNTN 500000000 +komodo_asset CHAIN 999999 +komodo_asset PRLPAY 500000000 diff --git a/src/assetchains.old b/src/assetchains.old index 20f47d006..c025e0430 100755 --- a/src/assetchains.old +++ b/src/assetchains.old @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash set -x delay=60 source pubkey.txt @@ -24,9 +24,14 @@ echo $pubkey ./komodod -pubkey=$pubkey -ac_name=AXO -ac_supply=200000000 -addnode=78.47.196.146 & ./komodod -pubkey=$pubkey -ac_name=ETOMIC -ac_supply=100000000 -addnode=78.47.196.146 & ./komodod -pubkey=$pubkey -ac_name=BTCH -ac_supply=20998641 -addnode=78.47.196.146 & -./komodod -pubkey=$pubkey -ac_name=VOTE -ac_supply=49999999999 -addnode=78.47.196.146 & -./komodod -pubkey=$pubkey -ac_name=BEER -ac_supply=100000000 -addnode=24.54.206.138 & -./komodod -pubkey=$pubkey -ac_name=PIZZA -ac_supply=100000000 -addnode=24.54.206.138 & +./komodod -pubkey=$pubkey -ac_name=VOTE2018 -ac_supply=600000000 -addnode=78.47.196.146 & +./komodod -pubkey=$pubkey -ac_name=BEER -ac_supply=100000000 -addnode=78.47.196.146 & +./komodod -pubkey=$pubkey -ac_name=PIZZA -ac_supply=100000000 -addnode=78.47.196.146 & +./komodod -pubkey=$pubkey -ac_name=NINJA -ac_supply=100000000 -addnode=78.47.196.146 & +./komodod -pubkey=$pubkey -ac_name=OOT -ac_supply=216000000 -addnode=174.138.107.226 & +./komodod -pubkey=$pubkey -ac_name=BNTN -ac_supply=500000000 -addnode=94.130.169.205 & +./komodod -pubkey=$pubkey -ac_name=CHAIN -ac_supply=999999 -addnode=78.47.146.222 & +./komodod -pubkey=$pubkey -ac_name=PRLPAY -ac_supply=500000000 -addnode=13.250.226.125 & #sleep $delay #./komodod -pubkey=$pubkey -ac_name=USD -addnode=78.47.196.146 $1 & diff --git a/src/asyncrpcoperation.h b/src/asyncrpcoperation.h index 5475102a5..2b821e256 100644 --- a/src/asyncrpcoperation.h +++ b/src/asyncrpcoperation.h @@ -24,7 +24,7 @@ using namespace std; * * To subclass AsyncRPCOperation, implement the main() method. * Update the operation status as work is underway and completes. - * If main() can be interrupted, inmplement the cancel() method. + * If main() can be interrupted, implement the cancel() method. */ typedef std::string AsyncRPCOperationId; diff --git a/src/base58.cpp b/src/base58.cpp index d09544682..c9c64bc58 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -323,67 +323,60 @@ bool CBitcoinSecret::SetString(const std::string& strSecret) return SetString(strSecret.c_str()); } -bool CZCPaymentAddress::Set(const libzcash::PaymentAddress& addr) +template +bool CZCEncoding::Set(const DATA_TYPE& addr) { CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss << addr; std::vector addrSerialized(ss.begin(), ss.end()); - assert(addrSerialized.size() == libzcash::SerializedPaymentAddressSize); - SetData(Params().Base58Prefix(CChainParams::ZCPAYMENT_ADDRRESS), &addrSerialized[0], libzcash::SerializedPaymentAddressSize); + assert(addrSerialized.size() == SER_SIZE); + SetData(Params().Base58Prefix(PREFIX), &addrSerialized[0], SER_SIZE); return true; } -libzcash::PaymentAddress CZCPaymentAddress::Get() const +template +DATA_TYPE CZCEncoding::Get() const { - if (vchData.size() != libzcash::SerializedPaymentAddressSize) { + if (vchData.size() != SER_SIZE) { throw std::runtime_error( - "payment address is invalid" + PrependName(" is invalid") ); } - if (vchVersion != Params().Base58Prefix(CChainParams::ZCPAYMENT_ADDRRESS)) { + if (vchVersion != Params().Base58Prefix(PREFIX)) { throw std::runtime_error( - "payment address is for wrong network type" + PrependName(" is for wrong network type") ); } std::vector serialized(vchData.begin(), vchData.end()); CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION); - libzcash::PaymentAddress ret; - ss >> ret; - return ret; -} - -bool CZCSpendingKey::Set(const libzcash::SpendingKey& addr) -{ - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << addr; - std::vector addrSerialized(ss.begin(), ss.end()); - assert(addrSerialized.size() == libzcash::SerializedSpendingKeySize); - SetData(Params().Base58Prefix(CChainParams::ZCSPENDING_KEY), &addrSerialized[0], libzcash::SerializedSpendingKeySize); - return true; -} - -libzcash::SpendingKey CZCSpendingKey::Get() const -{ - if (vchData.size() != libzcash::SerializedSpendingKeySize) { - throw std::runtime_error( - "spending key is invalid" - ); - } - - if (vchVersion != Params().Base58Prefix(CChainParams::ZCSPENDING_KEY)) { - throw std::runtime_error( - "spending key is for wrong network type" - ); - } - - std::vector serialized(vchData.begin(), vchData.end()); - - CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION); - libzcash::SpendingKey ret; + DATA_TYPE ret; ss >> ret; return ret; } +// Explicit instantiations for libzcash::PaymentAddress +template bool CZCEncoding::Set(const libzcash::PaymentAddress& addr); +template libzcash::PaymentAddress CZCEncoding::Get() const; + +// Explicit instantiations for libzcash::ViewingKey +template bool CZCEncoding::Set(const libzcash::ViewingKey& vk); +template libzcash::ViewingKey CZCEncoding::Get() const; + +// Explicit instantiations for libzcash::SpendingKey +template bool CZCEncoding::Set(const libzcash::SpendingKey& sk); +template libzcash::SpendingKey CZCEncoding::Get() const; diff --git a/src/base58.h b/src/base58.h index 88efadbd6..24e7abfc8 100644 --- a/src/base58.h +++ b/src/base58.h @@ -96,26 +96,48 @@ public: bool operator> (const CBase58Data& b58) const { return CompareTo(b58) > 0; } }; -class CZCPaymentAddress : public CBase58Data { +template +class CZCEncoding : public CBase58Data { +protected: + virtual std::string PrependName(const std::string& s) const = 0; + +public: + bool Set(const DATA_TYPE& addr); + + DATA_TYPE Get() const; +}; + +class CZCPaymentAddress : public CZCEncoding { +protected: + std::string PrependName(const std::string& s) const { return "payment address" + s; } + public: - bool Set(const libzcash::PaymentAddress& addr); CZCPaymentAddress() {} CZCPaymentAddress(const std::string& strAddress) { SetString(strAddress.c_str(), 2); } CZCPaymentAddress(const libzcash::PaymentAddress& addr) { Set(addr); } +}; + +class CZCViewingKey : public CZCEncoding { +protected: + std::string PrependName(const std::string& s) const { return "viewing key" + s; } + +public: + CZCViewingKey() {} - libzcash::PaymentAddress Get() const; + CZCViewingKey(const std::string& strViewingKey) { SetString(strViewingKey.c_str(), 3); } + CZCViewingKey(const libzcash::ViewingKey& vk) { Set(vk); } }; -class CZCSpendingKey : public CBase58Data { +class CZCSpendingKey : public CZCEncoding { +protected: + std::string PrependName(const std::string& s) const { return "spending key" + s; } + public: - bool Set(const libzcash::SpendingKey& addr); CZCSpendingKey() {} CZCSpendingKey(const std::string& strAddress) { SetString(strAddress.c_str(), 2); } CZCSpendingKey(const libzcash::SpendingKey& addr) { Set(addr); } - - libzcash::SpendingKey Get() const; }; /** base58-encoded Bitcoin addresses. @@ -172,7 +194,7 @@ public: K GetKey() { K ret; if (vchData.size() == Size) { - //if base58 encouded data not holds a ext key, return a !IsValid() key + //if base58 encoded data not holds a ext key, return a !IsValid() key ret.Decode(&vchData[0]); } return ret; diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 6e116adf2..285d06c3f 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -65,6 +65,7 @@ public: }; +#define FROM_CLI #include "uint256.h" #include "arith_uint256.h" @@ -75,14 +76,14 @@ public: #include "komodo_cJSON.c" #include "komodo_notary.h" -uint32_t komodo_heightstamp(int32_t height) +void komodo_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotaries,uint8_t notaryid,uint256 txhash,uint64_t voutmask,uint8_t numvouts,uint32_t *pvals,uint8_t numpvals,int32_t KMDheight,uint32_t KMDtimestamp,uint64_t opretvalue,uint8_t *opretbuf,uint16_t opretlen,uint16_t vout) { - return(0); + } -void komodo_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotaries,uint8_t notaryid,uint256 txhash,uint64_t voutmask,uint8_t numvouts,uint32_t *pvals,uint8_t numpvals,int32_t KMDheight,uint32_t KMDtimestamp,uint64_t opretvalue,uint8_t *opretbuf,uint16_t opretlen,uint16_t vout) +uint32_t komodo_heightstamp(int32_t height) { - + return(0); } static bool AppInitRPC(int argc, char* argv[]) diff --git a/src/cJSON.h b/src/cJSON.h index fb8971575..1e388137e 100644 --- a/src/cJSON.h +++ b/src/cJSON.h @@ -20,14 +20,6 @@ THE SOFTWARE. */ -#include -#include -#include -#include -#include -#include -#include - #ifndef cJSON__h #define cJSON__h diff --git a/src/cc/betprotocol.cpp b/src/cc/betprotocol.cpp new file mode 100644 index 000000000..53b79176c --- /dev/null +++ b/src/cc/betprotocol.cpp @@ -0,0 +1,139 @@ +#include + +#include "streams.h" +#include "script/cc.h" +#include "cc/eval.h" +#include "cc/betprotocol.h" +#include "primitives/transaction.h" + + +std::vector BetProtocol::PlayerConditions() +{ + std::vector subs; + for (int i=0; i result(vmResultHash.begin(), vmResultHash.begin()+32); + mtx.vout.push_back(CTxOut(0, CScript() << OP_RETURN << result)); + return mtx; +} + + +CMutableTransaction BetProtocol::MakePostEvidenceTx(uint256 signedSessionTxHash, + int playerIdx, std::vector state) +{ + CMutableTransaction mtx; + + mtx.vin.push_back(CTxIn(signedSessionTxHash, playerIdx+1, CScript())); + mtx.vout.push_back(CTxOut(0, CScript() << OP_RETURN << state)); + + return mtx; +} + + +CC* BetProtocol::MakePayoutCond(uint256 signedSessionTxHash) +{ + // TODO: 2/3 majority + CC* agree = CCNewThreshold(players.size(), PlayerConditions()); + + CC *import; + { + CC *importEval = CCNewEval(E_MARSHAL( + ss << EVAL_IMPORTPAYOUT << signedSessionTxHash; + )); + + CC *oneof = CCNewThreshold(1, PlayerConditions()); + + import = CCNewThreshold(2, {oneof, importEval}); + } + + return CCNewThreshold(1, {agree, import}); +} + + +CMutableTransaction BetProtocol::MakeStakeTx(CAmount totalPayout, uint256 signedSessionTxHash) +{ + CMutableTransaction mtx; + + CC *payoutCond = MakePayoutCond(signedSessionTxHash); + mtx.vout.push_back(CTxOut(totalPayout, CCPubKey(payoutCond))); + cc_free(payoutCond); + + return mtx; +} + + +CMutableTransaction BetProtocol::MakeAgreePayoutTx(std::vector payouts, + uint256 signedStakeTxHash) +{ + CMutableTransaction mtx; + mtx.vin.push_back(CTxIn(signedStakeTxHash, 0, CScript())); + mtx.vout = payouts; + return mtx; +} + + +CMutableTransaction BetProtocol::MakeImportPayoutTx(std::vector payouts, + CTransaction signedDisputeTx, uint256 signedStakeTxHash, MoMProof momProof) +{ + CMutableTransaction mtx; + mtx.vin.push_back(CTxIn(signedStakeTxHash, 0, CScript())); + mtx.vout = payouts; + CScript proofData; + proofData << OP_RETURN << E_MARSHAL(ss << momProof << signedDisputeTx); + mtx.vout.insert(mtx.vout.begin(), CTxOut(0, proofData)); + return mtx; +} + + +bool GetOpReturnHash(CScript script, uint256 &hash) +{ + std::vector vHash; + GetOpReturnData(script, vHash); + if (vHash.size() != 32) return false; + hash = uint256(vHash); + return true; +} diff --git a/src/cc/betprotocol.h b/src/cc/betprotocol.h new file mode 100644 index 000000000..b08783b85 --- /dev/null +++ b/src/cc/betprotocol.h @@ -0,0 +1,68 @@ +#ifndef BETPROTOCOL_H +#define BETPROTOCOL_H + +#include "cc/eval.h" +#include "pubkey.h" +#include "primitives/block.h" +#include "primitives/transaction.h" +#include "cryptoconditions/include/cryptoconditions.h" + + +class MoMProof +{ +public: + int nIndex; + std::vector branch; + uint256 notarisationHash; + + MoMProof() {} + MoMProof(int i, std::vector b, uint256 n) : notarisationHash(n), nIndex(i), branch(b) {} + uint256 Exec(uint256 hash) const { return CBlock::CheckMerkleBranch(hash, branch, nIndex); } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(VARINT(nIndex)); + READWRITE(branch); + READWRITE(notarisationHash); + } +}; + + +class BetProtocol +{ +protected: + std::vector playerConditions(); +public: + EvalCode disputeCode; + std::vector players; + std::vector vmParams; + uint32_t waitBlocks; + + // Utility + BetProtocol(EvalCode dc, std::vector ps, uint32_t wb, std::vector vmp) + : disputeCode(dc), waitBlocks(wb), vmParams(vmp), players(ps) {} + std::vector PlayerConditions(); + + // on PANGEA + CC* MakeDisputeCond(); + CMutableTransaction MakeSessionTx(CAmount spendFee); + CMutableTransaction MakeDisputeTx(uint256 signedSessionTxHash, uint256 vmResultHash); + CMutableTransaction MakePostEvidenceTx(uint256 signedSessionTxHash, + int playerIndex, std::vector state); + + // on KMD + CC* MakePayoutCond(uint256 signedSessionTxHash); + CMutableTransaction MakeStakeTx(CAmount totalPayout, uint256 signedSessionTx); + CMutableTransaction MakeAgreePayoutTx(std::vector payouts, uint256 signedStakeTxHash); + CMutableTransaction MakeImportPayoutTx(std::vector payouts, + CTransaction signedDisputeTx, uint256 signedStakeTxHash, MoMProof momProof); +}; + + + +bool GetOpReturnHash(CScript script, uint256 &hash); + + +#endif /* BETPROTOCOL_H */ diff --git a/src/cc/disputepayout.cpp b/src/cc/disputepayout.cpp new file mode 100644 index 000000000..610342274 --- /dev/null +++ b/src/cc/disputepayout.cpp @@ -0,0 +1,84 @@ +#include + +#include "hash.h" +#include "chain.h" +#include "version.h" +#include "script/cc.h" +#include "cc/eval.h" +#include "cc/betprotocol.h" +#include "primitives/transaction.h" + + +/* + * Crypto-Condition EVAL method that resolves a dispute of a session + * + * IN: vm - AppVM virtual machine to verify states + * IN: params - condition params + * IN: disputeTx - transaction attempting to resolve dispute + * IN: nIn - index of input of dispute tx + * + * disputeTx: attempt to resolve a dispute + * + * in 0: Spends Session TX first output, reveals DisputeHeader + * out 0: OP_RETURN hash of payouts + */ +bool Eval::DisputePayout(AppVM &vm, std::vector params, const CTransaction &disputeTx, unsigned int nIn) +{ + if (disputeTx.vout.size() == 0) return Invalid("no-vouts"); + + // get payouts hash + uint256 payoutHash; + if (!GetOpReturnHash(disputeTx.vout[0].scriptPubKey, payoutHash)) + return Invalid("invalid-payout-hash"); + + // load params + uint16_t waitBlocks; + std::vector vmParams; + if (!E_UNMARSHAL(params, ss >> VARINT(waitBlocks); ss >> vmParams)) + return Invalid("malformed-params"); + + // ensure that enough time has passed + { + CTransaction sessionTx; + CBlockIndex sessionBlock; + + // if unconformed its too soon + if (!GetTxConfirmed(disputeTx.vin[0].prevout.hash, sessionTx, sessionBlock)) + return Error("couldnt-get-parent"); + + if (GetCurrentHeight() < sessionBlock.nHeight + waitBlocks) + return Invalid("dispute-too-soon"); // Not yet + } + + // get spends + std::vector spends; + if (!GetSpendsConfirmed(disputeTx.vin[0].prevout.hash, spends)) + return Error("couldnt-get-spends"); + + // verify result from VM + int maxLength = -1; + uint256 bestPayout; + for (int i=1; i vmState; + if (spends[i].vout.size() == 0) continue; + if (!GetOpReturnData(spends[i].vout[0].scriptPubKey, vmState)) continue; + auto out = vm.evaluate(vmParams, vmState); + uint256 resultHash = SerializeHash(out.second); + if (out.first > maxLength) { + maxLength = out.first; + bestPayout = resultHash; + } + // The below means that if for any reason there is a draw, the first dispute wins + else if (out.first == maxLength) { + if (bestPayout != payoutHash) { + fprintf(stderr, "WARNING: VM has multiple solutions of same length\n"); + bestPayout = resultHash; + } + } + } + + if (maxLength == -1) return Invalid("no-evidence"); + + return bestPayout == payoutHash ? Valid() : Invalid("wrong-payout"); +} diff --git a/src/cc/eval.cpp b/src/cc/eval.cpp new file mode 100644 index 000000000..3c53f9866 --- /dev/null +++ b/src/cc/eval.cpp @@ -0,0 +1,205 @@ +#include +#include + +#include "primitives/transaction.h" +#include "script/cc.h" +#include "cc/eval.h" +#include "main.h" +#include "chain.h" +#include "core_io.h" + + +Eval* EVAL_TEST = 0; + + +bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn) +{ + Eval eval_; + Eval *eval = EVAL_TEST; + if (!eval) eval = &eval_; + + bool out = eval->Dispatch(cond, tx, nIn); + assert(eval->state.IsValid() == out); + + if (eval->state.IsValid()) return true; + + std::string lvl = eval->state.IsInvalid() ? "Invalid" : "Error!"; + fprintf(stderr, "CC Eval %s %s: %s spending tx %s\n", + EvalToStr(cond->code[0]).data(), + lvl.data(), + eval->state.GetRejectReason().data(), + tx.vin[nIn].prevout.hash.GetHex().data()); + if (eval->state.IsError()) fprintf(stderr, "Culprit: %s\n", EncodeHexTx(tx).data()); + return false; +} + + +/* + * Test the validity of an Eval node + */ +bool Eval::Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn) +{ + if (cond->codeLength == 0) + return Invalid("empty-eval"); + + uint8_t ecode = cond->code[0]; + std::vector vparams(cond->code+1, cond->code+cond->codeLength); + + if (ecode == EVAL_IMPORTPAYOUT) { + return ImportPayout(vparams, txTo, nIn); + } + + return Invalid("invalid-code"); +} + + +bool Eval::GetSpendsConfirmed(uint256 hash, std::vector &spends) const +{ + // NOT IMPLEMENTED + return false; +} + + +bool Eval::GetTxUnconfirmed(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock) const +{ + bool fAllowSlow = false; // Don't allow slow + return GetTransaction(hash, txOut, hashBlock, fAllowSlow); +} + + +bool Eval::GetTxConfirmed(const uint256 &hash, CTransaction &txOut, CBlockIndex &block) const +{ + uint256 hashBlock; + if (!GetTxUnconfirmed(hash, txOut, hashBlock)) + return false; + if (hashBlock.IsNull() || !GetBlock(hashBlock, block)) + return false; + return true; +} + + +unsigned int Eval::GetCurrentHeight() const +{ + return chainActive.Height(); +} + + +bool Eval::GetBlock(uint256 hash, CBlockIndex& blockIdx) const +{ + auto r = mapBlockIndex.find(hash); + if (r != mapBlockIndex.end()) { + blockIdx = *r->second; + return true; + } + fprintf(stderr, "CC Eval Error: Can't get block from index\n"); + return false; +} + + +extern int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); + + +int32_t Eval::GetNotaries(uint8_t pubkeys[64][33], int32_t height, uint32_t timestamp) const +{ + return komodo_notaries(pubkeys, height, timestamp); +} + + +bool Eval::CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const +{ + if (tx.vin.size() < 11) return false; + + uint8_t seenNotaries[64] = {0}; + uint8_t notaries[64][33]; + int nNotaries = GetNotaries(notaries, height, timestamp); + + BOOST_FOREACH(const CTxIn &txIn, tx.vin) + { + // Get notary pubkey + CTransaction tx; + uint256 hashBlock; + if (!GetTxUnconfirmed(txIn.prevout.hash, tx, hashBlock)) return false; + if (tx.vout.size() < txIn.prevout.n) return false; + CScript spk = tx.vout[txIn.prevout.n].scriptPubKey; + if (spk.size() != 35) return false; + const unsigned char *pk = spk.data(); + if (pk++[0] != 33) return false; + if (pk[33] != OP_CHECKSIG) return false; + + // Check it's a notary + for (int i=0; i vdata; + if (!GetOpReturnData(scriptPK, vdata)) return false; + + CDataStream ss(vdata, SER_NETWORK, PROTOCOL_VERSION); + + try { + ss >> blockHash; + ss >> height; + if (ASSETCHAINS_SYMBOL[0]) + ss >> txHash; + + char *nullPos = (char*) memchr(&ss[0], 0, ss.size()); + if (!nullPos) return false; + ss.read(symbol, nullPos-&ss[0]+1); + + if (ss.size() < 36) return false; + ss >> MoM; + ss >> MoMDepth; + } catch (...) { + return false; + } + return true; +} + + +/* + * Misc + */ + +std::string EvalToStr(EvalCode c) +{ + FOREACH_EVAL(EVAL_GENERATE_STRING); + char s[10]; + sprintf(s, "0x%x", c); + return std::string(s); +} diff --git a/src/cc/eval.h b/src/cc/eval.h new file mode 100644 index 000000000..f998c9f3d --- /dev/null +++ b/src/cc/eval.h @@ -0,0 +1,143 @@ +#ifndef CC_EVAL_H +#define CC_EVAL_H + +#include + +#include "chain.h" +#include "streams.h" +#include "version.h" +#include "consensus/validation.h" +#include "primitives/transaction.h" + + +/* + * Eval codes + * + * Add to below macro to generate new code. + * + * If at some point a new interpretation model is introduced, + * there should be a code identifying it. For example, + * a possible code is EVAL_BITCOIN_SCRIPT, where the entire binary + * after the code is interpreted as a bitcoin script. + */ +#define FOREACH_EVAL(EVAL) \ + EVAL(EVAL_IMPORTPAYOUT, 0xe1) + +typedef uint8_t EvalCode; + + +class AppVM; +class NotarisationData; + + +class Eval +{ +public: + CValidationState state; + + bool Invalid(std::string s) { return state.Invalid(false, 0, s); } + bool Error(std::string s) { return state.Error(s); } + bool Valid() { return true; } + + /* + * Test validity of a CC_Eval node + */ + virtual bool Dispatch(const CC *cond, const CTransaction &tx, unsigned int nIn); + + /* + * Dispute a payout using a VM + */ + bool DisputePayout(AppVM &vm, std::vector params, const CTransaction &disputeTx, unsigned int nIn); + + /* + * Test an ImportPayout CC Eval condition + */ + bool ImportPayout(std::vector params, const CTransaction &importTx, unsigned int nIn); + + /* + * IO functions + */ + virtual bool GetTxUnconfirmed(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock) const; + virtual bool GetTxConfirmed(const uint256 &hash, CTransaction &txOut, CBlockIndex &block) const; + virtual unsigned int GetCurrentHeight() const; + virtual bool GetSpendsConfirmed(uint256 hash, std::vector &spends) const; + virtual bool GetBlock(uint256 hash, CBlockIndex& blockIdx) const; + virtual int32_t GetNotaries(uint8_t pubkeys[64][33], int32_t height, uint32_t timestamp) const; + virtual bool GetNotarisationData(uint256 notarisationHash, NotarisationData &data) const; + virtual bool CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const; +}; + + +bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn); + + +/* + * Virtual machine to use in the case of on-chain app evaluation + */ +class AppVM +{ +public: + /* + * in: header - paramters agreed upon by all players + * in: body - gamestate + * out: length - length of game (longest wins) + * out: payments - vector of CTxOut, always deterministically sorted. + */ + virtual std::pair> + evaluate(std::vector header, std::vector body) = 0; +}; + + +/* + * Data from notarisation OP_RETURN + */ +class NotarisationData { +public: + uint256 blockHash; + uint32_t height; + uint256 txHash; // Only get this guy in asset chains not in KMD + char symbol[64]; + uint256 MoM; + uint32_t MoMDepth; + + bool Parse(CScript scriptPubKey); +}; + + +/* + * Eval code utilities. + */ +#define EVAL_GENERATE_DEF(L,I) const uint8_t L = I; +#define EVAL_GENERATE_STRING(L,I) if (c == I) return #L; + +FOREACH_EVAL(EVAL_GENERATE_DEF); + +std::string EvalToStr(EvalCode c); + + +/* + * Serialisation boilerplate + */ +#define E_MARSHAL(body) SerializeF([&] (CDataStream &ss) {body;}) +template +std::vector SerializeF(const T f) +{ + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + f(ss); + return std::vector(ss.begin(), ss.end()); +} + +#define E_UNMARSHAL(params, body) DeserializeF(params, [&] (CDataStream &ss) {body;}) +template +bool DeserializeF(const std::vector vIn, T f) +{ + CDataStream ss(vIn, SER_NETWORK, PROTOCOL_VERSION); + try { + f(ss); + if (ss.eof()) return true; + } catch(...) {} + return false; +} + + +#endif /* CC_EVAL_H */ diff --git a/src/cc/importpayout.cpp b/src/cc/importpayout.cpp new file mode 100644 index 000000000..1363eb924 --- /dev/null +++ b/src/cc/importpayout.cpp @@ -0,0 +1,76 @@ +#include + +#include "main.h" +#include "chain.h" +#include "streams.h" +#include "cc/eval.h" +#include "cc/betprotocol.h" +#include "primitives/transaction.h" + + +/* + * Crypto-Condition EVAL method that verifies a payout against a transaction + * notarised on another chain. + * + * IN: params - condition params + * IN: importTx - Payout transaction on value chain (KMD) + * IN: nIn - index of input of stake + * + * importTx: Spends stakeTx with payouts from asset chain + * + * in 0: Spends Stake TX and contains ImportPayout CC + * out 0: OP_RETURN MomProof, disputeTx + * out 1-: arbitrary payouts + * + * disputeTx: Spends sessionTx.0 (opener on asset chain) + * + * in 0: spends sessionTx.0 + * in 1-: anything + * out 0: OP_RETURN hash of payouts + * out 1-: anything + */ +bool Eval::ImportPayout(const std::vector params, const CTransaction &importTx, unsigned int nIn) +{ + if (importTx.vout.size() == 0) return Invalid("no-vouts"); + + // load data from vout[0] + MoMProof proof; + CTransaction disputeTx; + { + std::vector vopret; + GetOpReturnData(importTx.vout[0].scriptPubKey, vopret); + if (!E_UNMARSHAL(vopret, ss >> proof; ss >> disputeTx)) + return Invalid("invalid-payload"); + } + + // Check disputeTx.0 shows correct payouts + { + uint256 givenPayoutsHash; + GetOpReturnHash(disputeTx.vout[0].scriptPubKey, givenPayoutsHash); + std::vector payouts(importTx.vout.begin() + 1, importTx.vout.end()); + if (givenPayoutsHash != SerializeHash(payouts)) + return Invalid("wrong-payouts"); + } + + // Check disputeTx spends sessionTx.0 + // condition ImportPayout params is session ID from other chain + { + uint256 sessionHash; + if (!E_UNMARSHAL(params, ss >> sessionHash)) + return Invalid("malformed-params"); + if (disputeTx.vin[0].prevout != COutPoint(sessionHash, 0)) + return Invalid("wrong-session"); + } + + // Check disputeTx solves momproof from vout[0] + { + NotarisationData data; + if (!GetNotarisationData(proof.notarisationHash, data)) + return Invalid("coudnt-load-mom"); + + if (data.MoM != proof.Exec(disputeTx.GetHash())) + return Invalid("mom-check-fail"); + } + + return Valid(); +} diff --git a/src/chain.cpp b/src/chain.cpp index 9cd13d997..7bb72d5d3 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -81,7 +81,8 @@ CBlockIndex* CBlockIndex::GetAncestor(int height) CBlockIndex* pindexWalk = this; int heightWalk = nHeight; - while (heightWalk > height) { + while ( heightWalk > height && pindexWalk != 0 ) + { int heightSkip = GetSkipHeight(heightWalk); int heightSkipPrev = GetSkipHeight(heightWalk - 1); if (pindexWalk->pskip != NULL && diff --git a/src/chain.h b/src/chain.h index b7e8a9176..a28e4e44a 100644 --- a/src/chain.h +++ b/src/chain.h @@ -16,6 +16,8 @@ #include +static const int SPROUT_VALUE_VERSION = 1001400; + struct CDiskBlockPos { int nFile; @@ -92,8 +94,14 @@ enum BlockStatus: uint32_t { BLOCK_FAILED_VALID = 32, //! stage after last reached validness failed BLOCK_FAILED_CHILD = 64, //! descends from failed block BLOCK_FAILED_MASK = BLOCK_FAILED_VALID | BLOCK_FAILED_CHILD, + + BLOCK_ACTIVATES_UPGRADE = 128, //! block activates a network upgrade }; +//! Short-hand for the highest consensus validity we implement. +//! Blocks with this validity are assumed to satisfy all consensus rules. +static const BlockStatus BLOCK_VALID_CONSENSUS = BLOCK_VALID_SCRIPTS; + /** The block chain is a tree shaped structure starting with the * genesis block at the root, with each block potentially having multiple * candidates to be the next block. A blockindex may have multiple pprev pointing @@ -138,12 +146,26 @@ public: //! Verification status of this block. See enum BlockStatus unsigned int nStatus; + //! Branch ID corresponding to the consensus rules used to validate this block. + //! Only cached if block validity is BLOCK_VALID_CONSENSUS. + //! Persisted at each activation height, memory-only for intervening blocks. + boost::optional nCachedBranchId; + //! The anchor for the tree state up to the start of this block uint256 hashAnchor; //! (memory only) The anchor for the tree state up to the end of this block uint256 hashAnchorEnd; + //! Change in value held by the Sprout circuit over this block. + //! Will be boost::none for older blocks on old nodes until a reindex has taken place. + boost::optional nSproutValue; + + //! (memory only) Total value held by the Sprout circuit up to and including this block. + //! Will be boost::none for on old nodes until a reindex has taken place. + //! Will be boost::none if nChainTx is zero. + boost::optional nChainSproutValue; + //! block header int nVersion; uint256 hashMerkleRoot; @@ -155,7 +177,7 @@ public: //! (memory only) Sequential id assigned to distinguish order in which blocks are received. uint32_t nSequenceId; - + void SetNull() { phashBlock = NULL; @@ -169,9 +191,12 @@ public: nTx = 0; nChainTx = 0; nStatus = 0; + nCachedBranchId = boost::none; hashAnchor = uint256(); hashAnchorEnd = uint256(); nSequenceId = 0; + nSproutValue = boost::none; + nChainSproutValue = boost::none; nVersion = 0; hashMerkleRoot = uint256(); @@ -328,6 +353,18 @@ public: READWRITE(VARINT(nDataPos)); if (nStatus & BLOCK_HAVE_UNDO) READWRITE(VARINT(nUndoPos)); + if (nStatus & BLOCK_ACTIVATES_UPGRADE) { + if (ser_action.ForRead()) { + uint32_t branchId; + READWRITE(branchId); + nCachedBranchId = branchId; + } else { + // nCachedBranchId must always be set if BLOCK_ACTIVATES_UPGRADE is set. + assert(nCachedBranchId); + uint32_t branchId = *nCachedBranchId; + READWRITE(branchId); + } + } READWRITE(hashAnchor); // block header @@ -339,6 +376,12 @@ public: READWRITE(nBits); READWRITE(nNonce); READWRITE(nSolution); + + // Only read/write nSproutValue if the client version used to create + // this index was storing them. + if ((nType & SER_DISK) && (nVersion >= SPROUT_VALUE_VERSION)) { + READWRITE(nSproutValue); + } } uint256 GetBlockHash() const diff --git a/src/chainparams.cpp b/src/chainparams.cpp index d84278b31..49c4a2244 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -15,10 +15,56 @@ #include "base58.h" -using namespace std; - #include "chainparamsseeds.h" +static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, uint32_t nTime, const uint256& nNonce, const std::vector& nSolution, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward) +{ + // To create a genesis block for a new chain which is Overwintered: + // txNew.nVersion = 3 + // txNew.fOverwintered = true + // txNew.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID + // txNew.nExpiryHeight = + CMutableTransaction txNew; + txNew.nVersion = 1; + txNew.vin.resize(1); + txNew.vout.resize(1); + txNew.vin[0].scriptSig = CScript() << 520617983 << CScriptNum(4) << std::vector((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); + txNew.vout[0].nValue = genesisReward; + txNew.vout[0].scriptPubKey = genesisOutputScript; + + CBlock genesis; + genesis.nTime = nTime; + genesis.nBits = nBits; + genesis.nNonce = nNonce; + genesis.nSolution = nSolution; + genesis.nVersion = nVersion; + genesis.vtx.push_back(txNew); + genesis.hashPrevBlock.SetNull(); + genesis.hashMerkleRoot = genesis.BuildMerkleTree(); + return genesis; +} + +/** + * Build the genesis block. Note that the output of its generation + * transaction cannot be spent since it did not originally exist in the + * database (and is in any case of zero value). + * + * >>> from pyblake2 import blake2s + * >>> 'Zcash' + blake2s(b'The Economist 2016-10-29 Known unknown: Another crypto-currency is born. BTC#436254 0000000000000000044f321997f336d2908cf8c8d6893e88dbf067e2d949487d ETH#2521903 483039a6b6bd8bd05f0584f9a078d075e454925eb71c1f13eaff59b405a721bb DJIA close on 27 Oct 2016: 18,169.68').hexdigest() + * + * CBlock(hash=00040fe8, ver=4, hashPrevBlock=00000000000000, hashMerkleRoot=c4eaa5, nTime=1477641360, nBits=1f07ffff, nNonce=4695, vtx=1) + * CTransaction(hash=c4eaa5, ver=1, vin.size=1, vout.size=1, nLockTime=0) + * CTxIn(COutPoint(000000, -1), coinbase 04ffff071f0104455a6361736830623963346565663862376363343137656535303031653335303039383462366665613335363833613763616331343161303433633432303634383335643334) + * CTxOut(nValue=0.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B) + * vMerkleTree: c4eaa5 + */ +static CBlock CreateGenesisBlock(uint32_t nTime, const uint256& nNonce, const std::vector& nSolution, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward) +{ + const char* pszTimestamp = "Zcash0b9c4eef8b7cc417ee5001e3500984b6fea35683a7cac141a043c42064835d34"; + const CScript genesisOutputScript = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG; + return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTime, nNonce, nSolution, nBits, nVersion, genesisReward); +} + /** * Main network */ @@ -59,6 +105,16 @@ public: consensus.nPowMaxAdjustUp = 16; // 16% adjustment up consensus.nPowTargetSpacing = 1 * 60; consensus.fPowAllowMinDifficultyBlocks = true; //false; + consensus.vUpgrades[Consensus::BASE_SPROUT].nProtocolVersion = 170002; + consensus.vUpgrades[Consensus::BASE_SPROUT].nActivationHeight = + Consensus::NetworkUpgrade::ALWAYS_ACTIVE; + consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nProtocolVersion = 170002; + consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nActivationHeight = + Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; + consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion = 170004; + consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight = + Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; + /** * The message start string is designed to be unlikely to occur in normal data. * The characters are rarely used upper ASCII, not valid as UTF-8, and produce @@ -77,11 +133,12 @@ public: BOOST_STATIC_ASSERT(equihash_parameters_acceptable(N, K)); nEquihashN = N; nEquihashK = K; + const char* pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"; CMutableTransaction txNew; txNew.vin.resize(1); txNew.vout.resize(1); - txNew.vin[0].scriptSig = CScript() << 486604799 << CScriptNum(4) << vector((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); + txNew.vin[0].scriptSig = CScript() << 486604799 << CScriptNum(4) << std::vector((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); txNew.vout[0].nValue = 50 * COIN; txNew.vout[0].scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG; genesis.vtx.push_back(txNew); @@ -93,13 +150,21 @@ public: genesis.nNonce = uint256S("0x000000000000000000000000000000000000000000000000000000000000000b"); genesis.nSolution = ParseHex("000d5ba7cda5d473947263bf194285317179d2b0d307119c2e7cc4bd8ac456f0774bd52b0cd9249be9d40718b6397a4c7bbd8f2b3272fed2823cd2af4bd1632200ba4bf796727d6347b225f670f292343274cc35099466f5fb5f0cd1c105121b28213d15db2ed7bdba490b4cedc69742a57b7c25af24485e523aadbb77a0144fc76f79ef73bd8530d42b9f3b9bed1c135ad1fe152923fafe98f95f76f1615e64c4abb1137f4c31b218ba2782bc15534788dda2cc08a0ee2987c8b27ff41bd4e31cd5fb5643dfe862c9a02ca9f90c8c51a6671d681d04ad47e4b53b1518d4befafefe8cadfb912f3d03051b1efbf1dfe37b56e93a741d8dfd80d576ca250bee55fab1311fc7b3255977558cdda6f7d6f875306e43a14413facdaed2f46093e0ef1e8f8a963e1632dcbeebd8e49fd16b57d49b08f9762de89157c65233f60c8e38a1f503a48c555f8ec45dedecd574a37601323c27be597b956343107f8bd80f3a925afaf30811df83c402116bb9c1e5231c70fff899a7c82f73c902ba54da53cc459b7bf1113db65cc8f6914d3618560ea69abd13658fa7b6af92d374d6eca9529f8bd565166e4fcbf2a8dfb3c9b69539d4d2ee2e9321b85b331925df195915f2757637c2805e1d4131e1ad9ef9bc1bb1c732d8dba4738716d351ab30c996c8657bab39567ee3b29c6d054b711495c0d52e1cd5d8e55b4f0f0325b97369280755b46a02afd54be4ddd9f77c22272b8bbb17ff5118fedbae2564524e797bd28b5f74f7079d532ccc059807989f94d267f47e724b3f1ecfe00ec9e6541c961080d8891251b84b4480bc292f6a180bea089fef5bbda56e1e41390d7c0e85ba0ef530f7177413481a226465a36ef6afe1e2bca69d2078712b3912bba1a99b1fbff0d355d6ffe726d2bb6fbc103c4ac5756e5bee6e47e17424ebcbf1b63d8cb90ce2e40198b4f4198689daea254307e52a25562f4c1455340f0ffeb10f9d8e914775e37d0edca019fb1b9c6ef81255ed86bc51c5391e0591480f66e2d88c5f4fd7277697968656a9b113ab97f874fdd5f2465e5559533e01ba13ef4a8f7a21d02c30c8ded68e8c54603ab9c8084ef6d9eb4e92c75b078539e2ae786ebab6dab73a09e0aa9ac575bcefb29e930ae656e58bcb513f7e3c17e079dce4f05b5dbc18c2a872b22509740ebe6a3903e00ad1abc55076441862643f93606e3dc35e8d9f2caef3ee6be14d513b2e062b21d0061de3bd56881713a1a5c17f5ace05e1ec09da53f99442df175a49bd154aa96e4949decd52fed79ccf7ccbce32941419c314e374e4a396ac553e17b5340336a1a25c22f9e42a243ba5404450b650acfc826a6e432971ace776e15719515e1634ceb9a4a35061b668c74998d3dfb5827f6238ec015377e6f9c94f38108768cf6e5c8b132e0303fb5a200368f845ad9d46343035a6ff94031df8d8309415bb3f6cd5ede9c135fdabcc030599858d803c0f85be7661c88984d88faa3d26fb0e9aac0056a53f1b5d0baed713c853c4a2726869a0a124a8a5bbc0fc0ef80c8ae4cb53636aa02503b86a1eb9836fcc259823e2692d921d88e1ffc1e6cb2bde43939ceb3f32a611686f539f8f7c9f0bf00381f743607d40960f06d347d1cd8ac8a51969c25e37150efdf7aa4c2037a2fd0516fb444525ab157a0ed0a7412b2fa69b217fe397263153782c0f64351fbdf2678fa0dc8569912dcd8e3ccad38f34f23bbbce14c6a26ac24911b308b82c7e43062d180baeac4ba7153858365c72c63dcf5f6a5b08070b730adb017aeae925b7d0439979e2679f45ed2f25a7edcfd2fb77a8794630285ccb0a071f5cce410b46dbf9750b0354aae8b65574501cc69efb5b6a43444074fee116641bb29da56c2b4a7f456991fc92b2"); + + /*genesis = CreateGenesisBlock( + 1477641360, + uint256S("0x0000000000000000000000000000000000000000000000000000000000001257"), + ParseHex("000a889f00854b8665cd555f4656f68179d31ccadc1b1f7fb0952726313b16941da348284d67add4686121d4e3d930160c1348d8191c25f12b267a6a9c131b5031cbf8af1f79c9d513076a216ec87ed045fa966e01214ed83ca02dc1797270a454720d3206ac7d931a0a680c5c5e099057592570ca9bdf6058343958b31901fce1a15a4f38fd347750912e14004c73dfe588b903b6c03166582eeaf30529b14072a7b3079e3a684601b9b3024054201f7440b0ee9eb1a7120ff43f713735494aa27b1f8bab60d7f398bca14f6abb2adbf29b04099121438a7974b078a11635b594e9170f1086140b4173822dd697894483e1c6b4e8b8dcd5cb12ca4903bc61e108871d4d915a9093c18ac9b02b6716ce1013ca2c1174e319c1a570215bc9ab5f7564765f7be20524dc3fdf8aa356fd94d445e05ab165ad8bb4a0db096c097618c81098f91443c719416d39837af6de85015dca0de89462b1d8386758b2cf8a99e00953b308032ae44c35e05eb71842922eb69797f68813b59caf266cb6c213569ae3280505421a7e3a0a37fdf8e2ea354fc5422816655394a9454bac542a9298f176e211020d63dee6852c40de02267e2fc9d5e1ff2ad9309506f02a1a71a0501b16d0d36f70cdfd8de78116c0c506ee0b8ddfdeb561acadf31746b5a9dd32c21930884397fb1682164cb565cc14e089d66635a32618f7eb05fe05082b8a3fae620571660a6b89886eac53dec109d7cbb6930ca698a168f301a950be152da1be2b9e07516995e20baceebecb5579d7cdbc16d09f3a50cb3c7dffe33f26686d4ff3f8946ee6475e98cf7b3cf9062b6966e838f865ff3de5fb064a37a21da7bb8dfd2501a29e184f207caaba364f36f2329a77515dcb710e29ffbf73e2bbd773fab1f9a6b005567affff605c132e4e4dd69f36bd201005458cfbd2c658701eb2a700251cefd886b1e674ae816d3f719bac64be649c172ba27a4fd55947d95d53ba4cbc73de97b8af5ed4840b659370c556e7376457f51e5ebb66018849923db82c1c9a819f173cccdb8f3324b239609a300018d0fb094adf5bd7cbb3834c69e6d0b3798065c525b20f040e965e1a161af78ff7561cd874f5f1b75aa0bc77f720589e1b810f831eac5073e6dd46d00a2793f70f7427f0f798f2f53a67e615e65d356e66fe40609a958a05edb4c175bcc383ea0530e67ddbe479a898943c6e3074c6fcc252d6014de3a3d292b03f0d88d312fe221be7be7e3c59d07fa0f2f4029e364f1f355c5d01fa53770d0cd76d82bf7e60f6903bc1beb772e6fde4a70be51d9c7e03c8d6d8dfb361a234ba47c470fe630820bbd920715621b9fbedb49fcee165ead0875e6c2b1af16f50b5d6140cc981122fcbcf7c5a4e3772b3661b628e08380abc545957e59f634705b1bbde2f0b4e055a5ec5676d859be77e20962b645e051a880fddb0180b4555789e1f9344a436a84dc5579e2553f1e5fb0a599c137be36cabbed0319831fea3fddf94ddc7971e4bcf02cdc93294a9aab3e3b13e3b058235b4f4ec06ba4ceaa49d675b4ba80716f3bc6976b1fbf9c8bf1f3e3a4dc1cd83ef9cf816667fb94f1e923ff63fef072e6a19321e4812f96cb0ffa864da50ad74deb76917a336f31dce03ed5f0303aad5e6a83634f9fcc371096f8288b8f02ddded5ff1bb9d49331e4a84dbe1543164438fde9ad71dab024779dcdde0b6602b5ae0a6265c14b94edd83b37403f4b78fcd2ed555b596402c28ee81d87a909c4e8722b30c71ecdd861b05f61f8b1231795c76adba2fdefa451b283a5d527955b9f3de1b9828e7b2e74123dd47062ddcc09b05e7fa13cb2212a6fdbc65d7e852cec463ec6fd929f5b8483cf3052113b13dac91b69f49d1b7d1aec01c4a68e41ce157"), + 0x1f07ffff, 4, 0);*/ + consensus.hashGenesisBlock = genesis.GetHash(); assert(consensus.hashGenesisBlock == uint256S("0x027e3758c3a65b12aa1046462b486d0a63bfa1beae327897f56c5cfb7daaae71")); assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); vFixedSeeds.clear(); vSeeds.clear(); - vSeeds.push_back(CDNSSeedData("komodoplatform.com", "seeds.komodoplatform.com")); // @kolo - vSeeds.push_back(CDNSSeedData("komodo.mewhub.com", "seeds.komodo.mewhub.com")); // @kolo + vSeeds.push_back(CDNSSeedData("komodoplatform.com", "seeds.komodoplatform.com")); // @kolo - old static dns seeds + vSeeds.push_back(CDNSSeedData("kolo.supernet.org", "static.kolo.supernet.org")); // @kolo - new static dns seeds ToDo + vSeeds.push_back(CDNSSeedData("kolo.supernet.org", "dynamic.kolo.supernet.org")); // @kolo - crawler seeds ToDo // TODO: set up bootstrapping for mainnet base58Prefixes[PUBKEY_ADDRESS] = std::vector(1,60); base58Prefixes[SCRIPT_ADDRESS] = std::vector(1,85); @@ -118,21 +183,6 @@ public: fRequireStandard = true; fMineBlocksOnDemand = false; fTestnetToBeDeprecatedFieldRPC = false; -/* - checkpointData = (Checkpoints::CCheckpointData) - { - boost::assign::map_list_of - (0, consensus.hashGenesisBlock), - //(2500, uint256S("0x0e6a3d5a46eba97c4e7618d66a39f115729e1176433c98481124c2bf733aa54e")) - //(15000, uint256S("0x00f0bd236790e903321a2d22f85bd6bf8a505f6ef4eddb20458a65d37e14d142")), - //(100000, uint256S("0x0f02eb1f3a4b89df9909fec81a4bd7d023e32e24e1f5262d9fc2cc36a715be6f")), - 1481120910, // * UNIX timestamp of last checkpoint block - 110415, // * total number of transactions between genesis and last checkpoint - // (the tx=... number in the SetBestChain debug.log lines) - 2777 // * estimated number of transactions per day after checkpoint - // total number of tx / (checkpoint block height / (24 * 24)) - }; -*/ // LogPrintf(">>>>>>>> ac_name = %u\n",GetArg("-ac_name","").c_str()); @@ -151,14 +201,14 @@ public: }; static CMainParams mainParams; -void CChainParams::SetCheckpointData(Checkpoints::CCheckpointData checkpointData) +void CChainParams::SetCheckpointData(CChainParams::CCheckpointData checkpointData) { CChainParams::checkpointData = checkpointData; } void *chainparams_commandline(void *ptr) { - Checkpoints::CCheckpointData checkpointData; + CChainParams::CCheckpointData checkpointData; while ( ASSETCHAINS_PORT == 0 ) { #ifdef _WIN32 @@ -353,25 +403,49 @@ void *chainparams_commandline(void *ptr) /** * Testnet (v3) */ -class CTestNetParams : public CMainParams { +class CTestNetParams : public CChainParams { public: CTestNetParams() { strNetworkID = "test"; strCurrencyUnits = "TAZ"; + consensus.fCoinbaseMustBeProtected = true; + consensus.nSubsidySlowStartInterval = 20000; + consensus.nSubsidyHalvingInterval = 840000; consensus.nMajorityEnforceBlockUpgrade = 51; consensus.nMajorityRejectBlockOutdated = 75; consensus.nMajorityWindow = 400; consensus.powLimit = uint256S("07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + consensus.nPowAveragingWindow = 17; assert(maxUint/UintToArith256(consensus.powLimit) >= consensus.nPowAveragingWindow); + + vAlertPubKey = ParseHex("00"); + nDefaultPort = 17770; + nMinerThreads = 0; + consensus.nPowMaxAdjustDown = 32; // 32% adjustment down + consensus.nPowMaxAdjustUp = 16; // 16% adjustment up + consensus.nPowTargetSpacing = 2.5 * 60; + consensus.vUpgrades[Consensus::BASE_SPROUT].nProtocolVersion = 170002; + consensus.vUpgrades[Consensus::BASE_SPROUT].nActivationHeight = + Consensus::NetworkUpgrade::ALWAYS_ACTIVE; + consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nProtocolVersion = 170002; + consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nActivationHeight = + Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; + consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion = 170003; + consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight = 207500; + consensus.fPowAllowMinDifficultyBlocks = true; pchMessageStart[0] = 0x5A; pchMessageStart[1] = 0x1F; pchMessageStart[2] = 0x7E; pchMessageStart[3] = 0x62; - vAlertPubKey = ParseHex("00"); - nDefaultPort = 17770; - nMinerThreads = 0; + vAlertPubKey = ParseHex("020e46e79a2a8d12b9b5d12c7a91adb4e454edfae43c0a0cb805427d2ac7613fd9"); + nMaxTipAge = 24 * 60 * 60; + nPruneAfterHeight = 1000; + const size_t N = 200, K = 9; + BOOST_STATIC_ASSERT(equihash_parameters_acceptable(N, K)); + nEquihashN = N; + nEquihashK = K; //! Modify the testnet genesis block so the timestamp is valid for a later start. genesis.nTime = 1296688602; @@ -402,7 +476,7 @@ public: fMineBlocksOnDemand = false; fTestnetToBeDeprecatedFieldRPC = true; - checkpointData = (Checkpoints::CCheckpointData) { + checkpointData = (CCheckpointData) { boost::assign::map_list_of (0, consensus.hashGenesisBlock) (38000, uint256S("0x001e9a2d2e2892b88e9998cf7b079b41d59dd085423a921fe8386cecc42287b8")), @@ -418,7 +492,7 @@ static CTestNetParams testNetParams; /** * Regression test */ -class CRegTestParams : public CTestNetParams { +class CRegTestParams : public CChainParams { public: CRegTestParams() { strNetworkID = "regtest"; @@ -430,15 +504,28 @@ public: consensus.nMajorityRejectBlockOutdated = 950; consensus.nMajorityWindow = 1000; consensus.powLimit = uint256S("0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"); + consensus.nPowAveragingWindow = 17; assert(maxUint/UintToArith256(consensus.powLimit) >= consensus.nPowAveragingWindow); consensus.nPowMaxAdjustDown = 0; // Turn off adjustment down consensus.nPowMaxAdjustUp = 0; // Turn off adjustment up + consensus.nPowTargetSpacing = 2.5 * 60; + consensus.vUpgrades[Consensus::BASE_SPROUT].nProtocolVersion = 170002; + consensus.vUpgrades[Consensus::BASE_SPROUT].nActivationHeight = + Consensus::NetworkUpgrade::ALWAYS_ACTIVE; + consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nProtocolVersion = 170002; + consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nActivationHeight = + Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; + consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion = 170003; + consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight = + Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; + pchMessageStart[0] = 0xaa; pchMessageStart[1] = 0x8e; pchMessageStart[2] = 0xf3; pchMessageStart[3] = 0xf5; nMinerThreads = 1; nMaxTipAge = 24 * 60 * 60; + nPruneAfterHeight = 1000; const size_t N = 48, K = 5; BOOST_STATIC_ASSERT(equihash_parameters_acceptable(N, K)); nEquihashN = N; @@ -449,7 +536,6 @@ public: genesis.nSolution = ParseHex("0f2a976db4c4263da10fd5d38eb1790469cf19bdb4bf93450e09a72fdff17a3454326399"); consensus.hashGenesisBlock = genesis.GetHash(); nDefaultPort = 17779; - assert(consensus.hashGenesisBlock == uint256S("0x00a215b4fe36f5d2f829d43e587bf10e89e64f9f48a5b6ce18559089e8fd643d")); nPruneAfterHeight = 1000; vFixedSeeds.clear(); //! Regtest mode doesn't have any fixed seeds. @@ -461,18 +547,34 @@ public: fMineBlocksOnDemand = true; fTestnetToBeDeprecatedFieldRPC = false; - checkpointData = (Checkpoints::CCheckpointData){ + checkpointData = (CCheckpointData){ boost::assign::map_list_of ( 0, uint256S("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")), 0, 0, 0 }; + // These prefixes are the same as the testnet prefixes + base58Prefixes[PUBKEY_ADDRESS] = {0x1D,0x25}; + base58Prefixes[SCRIPT_ADDRESS] = {0x1C,0xBA}; + base58Prefixes[SECRET_KEY] = {0xEF}; + // do not rely on these BIP32 prefixes; they are not specified and may change + base58Prefixes[EXT_PUBLIC_KEY] = {0x04,0x35,0x87,0xCF}; + base58Prefixes[EXT_SECRET_KEY] = {0x04,0x35,0x83,0x94}; + base58Prefixes[ZCPAYMENT_ADDRRESS] = {0x16,0xB6}; + base58Prefixes[ZCVIEWING_KEY] = {0xA8,0xAC,0x0C}; + base58Prefixes[ZCSPENDING_KEY] = {0xAC,0x08}; // Founders reward script expects a vector of 2-of-3 multisig addresses vFoundersRewardAddress = { "t2FwcEhFdNXuFMv1tcYwaBJtYVtMj8b1uTg" }; assert(vFoundersRewardAddress.size() <= consensus.GetLastFoundersRewardBlockHeight()); } + + void UpdateNetworkUpgradeParameters(Consensus::UpgradeIndex idx, int nActivationHeight) + { + assert(idx > Consensus::BASE_SPROUT && idx < Consensus::MAX_NETWORK_UPGRADES); + consensus.vUpgrades[idx].nActivationHeight = nActivationHeight; + } }; static CRegTestParams regTestParams; @@ -537,7 +639,7 @@ CScript CChainParams::GetFoundersRewardScriptAtHeight(int nHeight) const { CBitcoinAddress address(GetFoundersRewardAddressAtHeight(nHeight).c_str()); assert(address.IsValid()); assert(address.IsScript()); - CScriptID scriptID = get(address.Get()); // Get() returns a boost variant + CScriptID scriptID = boost::get(address.Get()); // Get() returns a boost variant CScript script = CScript() << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL; return script; } @@ -546,3 +648,8 @@ std::string CChainParams::GetFoundersRewardAddressAtIndex(int i) const { assert(i >= 0 && i < vFoundersRewardAddress.size()); return vFoundersRewardAddress[i]; } + +void UpdateNetworkUpgradeParameters(Consensus::UpgradeIndex idx, int nActivationHeight) +{ + regTestParams.UpdateNetworkUpgradeParameters(idx, nActivationHeight); +} diff --git a/src/chainparams.h b/src/chainparams.h index 00d2c255f..2041b2209 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -7,7 +7,6 @@ #define BITCOIN_CHAINPARAMS_H #include "chainparamsbase.h" -#include "checkpoints.h" #include "consensus/params.h" #include "primitives/block.h" #include "protocol.h" @@ -26,6 +25,8 @@ struct SeedSpec6 { uint16_t port; }; +typedef std::map MapCheckpoints; + /** * CChainParams defines various tweakable parameters of a given instance of the @@ -46,17 +47,22 @@ public: ZCPAYMENT_ADDRRESS, ZCSPENDING_KEY, + ZCVIEWING_KEY, MAX_BASE58_TYPES }; + struct CCheckpointData { + MapCheckpoints mapCheckpoints; + int64_t nTimeLastCheckpoint; + int64_t nTransactionsLastCheckpoint; + double fTransactionsPerDay; + }; 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; } - /** Used if GenerateBitcoins is called with a negative number of threads */ - int DefaultMinerThreads() const { return nMinerThreads; } const CBlock& GenesisBlock() const { return genesis; } /** Make miner wait to have peers to avoid wasting work */ bool MiningRequiresPeers() const { return fMiningRequiresPeers; } @@ -78,7 +84,7 @@ public: const std::vector& DNSSeeds() const { return vSeeds; } const std::vector& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } const std::vector& FixedSeeds() const { return vFixedSeeds; } - const Checkpoints::CCheckpointData& Checkpoints() const { return checkpointData; } + const CCheckpointData& Checkpoints() const { return checkpointData; } /** Return the founder's reward address and script for a given block height */ std::string GetFoundersRewardAddressAtHeight(int height) const; CScript GetFoundersRewardScriptAtHeight(int height) const; @@ -87,7 +93,7 @@ public: void SetRegTestCoinbaseMustBeProtected() { consensus.fCoinbaseMustBeProtected = true; } void SetDefaultPort(uint16_t port) { nDefaultPort = port; } - void SetCheckpointData(Checkpoints::CCheckpointData checkpointData); + void SetCheckpointData(CCheckpointData checkpointData); //void setnonce(uint32_t nonce) { memcpy(&genesis.nNonce,&nonce,sizeof(nonce)); } //void settimestamp(uint32_t timestamp) { genesis.nTime = timestamp; } @@ -118,7 +124,7 @@ protected: bool fRequireStandard = false; bool fMineBlocksOnDemand = false; bool fTestnetToBeDeprecatedFieldRPC = false; - Checkpoints::CCheckpointData checkpointData; + CCheckpointData checkpointData; std::vector vFoundersRewardAddress; }; @@ -140,4 +146,9 @@ void SelectParams(CBaseChainParams::Network network); */ bool SelectParamsFromCommandLine(); +/** + * Allows modifying the network upgrade regtest parameters. + */ +void UpdateNetworkUpgradeParameters(Consensus::UpgradeIndex idx, int nActivationHeight); + #endif // BITCOIN_CHAINPARAMS_H diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index a66b2da9e..475f200e6 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -25,7 +25,7 @@ static CBaseMainParams mainParams; /** * Testnet (v3) */ -class CBaseTestNetParams : public CBaseMainParams +class CBaseTestNetParams : public CBaseChainParams { public: CBaseTestNetParams() @@ -39,11 +39,12 @@ static CBaseTestNetParams testNetParams; /* * Regression test */ -class CBaseRegTestParams : public CBaseTestNetParams +class CBaseRegTestParams : public CBaseChainParams { public: CBaseRegTestParams() { + nRPCPort = 18232; strDataDir = "regtest"; } }; diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index ec1e2a47f..70fd1e8f2 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -23,7 +23,7 @@ namespace Checkpoints { * fast multicore CPU, it won't be much higher than 1. */ static const double SIGCHECK_VERIFICATION_FACTOR = 5.0; - bool CheckBlock(const CCheckpointData& data, int nHeight, const uint256& hash) + bool CheckBlock(const CChainParams::CCheckpointData& data, int nHeight, const uint256& hash) { const MapCheckpoints& checkpoints = data.mapCheckpoints; @@ -33,7 +33,7 @@ namespace Checkpoints { } //! Guess how far we are in the verification process at the given block index - double GuessVerificationProgress(const CCheckpointData& data, CBlockIndex *pindex, bool fSigchecks) { + double GuessVerificationProgress(const CChainParams::CCheckpointData& data, CBlockIndex *pindex, bool fSigchecks) { if (pindex==NULL) return 0.0; @@ -62,7 +62,7 @@ namespace Checkpoints { return fWorkBefore / (fWorkBefore + fWorkAfter); } - int GetTotalBlocksEstimate(const CCheckpointData& data) + int GetTotalBlocksEstimate(const CChainParams::CCheckpointData& data) { const MapCheckpoints& checkpoints = data.mapCheckpoints; @@ -72,7 +72,7 @@ namespace Checkpoints { return checkpoints.rbegin()->first; } - CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) + CBlockIndex* GetLastCheckpoint(const CChainParams::CCheckpointData& data) { const MapCheckpoints& checkpoints = data.mapCheckpoints; diff --git a/src/checkpoints.h b/src/checkpoints.h index f4c3992ba..b75da9ef2 100644 --- a/src/checkpoints.h +++ b/src/checkpoints.h @@ -6,10 +6,12 @@ #define BITCOIN_CHECKPOINTS_H #include "uint256.h" +#include "chainparams.h" #include class CBlockIndex; +struct CCheckpointData; /** * Block-chain checkpoints are compiled-in sanity checks. @@ -17,7 +19,8 @@ class CBlockIndex; */ namespace Checkpoints { -typedef std::map MapCheckpoints; + + typedef std::map MapCheckpoints; struct CCheckpointData { MapCheckpoints mapCheckpoints; @@ -25,15 +28,16 @@ struct CCheckpointData { int64_t nTransactionsLastCheckpoint; double fTransactionsPerDay; }; - bool CheckBlock(const CCheckpointData& data, int nHeight, const uint256& hash); + bool CheckBlock(const CChainParams::CCheckpointData& data, int nHeight, const uint256& hash); + //! Return conservative estimate of total number of blocks, 0 if unknown -int GetTotalBlocksEstimate(const CCheckpointData& data); + int GetTotalBlocksEstimate(const CChainParams::CCheckpointData& data); //! Returns last CBlockIndex* in mapBlockIndex that is a checkpoint -CBlockIndex* GetLastCheckpoint(const CCheckpointData& data); + CBlockIndex* GetLastCheckpoint(const CChainParams::CCheckpointData& data); -double GuessVerificationProgress(const CCheckpointData& data, CBlockIndex* pindex, bool fSigchecks = true); +double GuessVerificationProgress(const CChainParams::CCheckpointData& data, CBlockIndex* pindex, bool fSigchecks = true); } //namespace Checkpoints diff --git a/src/clientversion.cpp b/src/clientversion.cpp index 1e3eccbea..ae67e678f 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -100,7 +100,7 @@ const std::string CLIENT_NAME("MagicBean"); const std::string CLIENT_BUILD(BUILD_DESC CLIENT_VERSION_SUFFIX); const std::string CLIENT_DATE(BUILD_DATE); -static std::string FormatVersion(int nVersion) +std::string FormatVersion(int nVersion) { if (nVersion % 100 < 25) return strprintf("%d.%d.%d-beta%d", nVersion / 1000000, (nVersion / 10000) % 100, (nVersion / 100) % 100, (nVersion % 100)+1); diff --git a/src/clientversion.h b/src/clientversion.h index 108c89b85..1203760bb 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -17,7 +17,7 @@ //! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 1 #define CLIENT_VERSION_MINOR 0 -#define CLIENT_VERSION_REVISION 8 +#define CLIENT_VERSION_REVISION 15 #define CLIENT_VERSION_BUILD 52 //! Set to true for release, false for prerelease or test build @@ -63,6 +63,7 @@ extern const std::string CLIENT_BUILD; extern const std::string CLIENT_DATE; +std::string FormatVersion(int nVersion); std::string FormatFullVersion(); std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector& comments); diff --git a/src/coins.cpp b/src/coins.cpp index 272beb3d1..bb40af9cc 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -473,8 +473,6 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const { if (tx.IsCoinBase()) return 0.0; - //CAmount nTotalIn = 0; - // Joinsplits 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 whenever a transaction contains any JoinSplits. @@ -493,34 +491,9 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const if (!coins->IsAvailable(txin.prevout.n)) continue; if (coins->nHeight < nHeight) { dResult += coins->vout[txin.prevout.n].nValue * (nHeight-coins->nHeight); - //nTotalIn += coins->vout[txin.prevout.n].nValue; } } - // If a transaction contains a joinsplit, we boost the priority of the transaction. - // Joinsplits 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 pick a - // very large number and multiply it by the transaction's fee per 1000 bytes of data. - // One trillion, 1000000000000, is equivalent to 1 ZEC utxo * 10000 blocks (~17 days). - /*if (tx.vjoinsplit.size() > 0) { - unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); - nTotalIn += tx.GetJoinSplitValueIn(); - CAmount fee = nTotalIn - tx.GetValueOut(); - CFeeRate feeRate(fee, nTxSize); - CAmount feePerK = feeRate.GetFeePerK(); - - if (feePerK == 0) { - feePerK = 1; - } - - dResult += 1000000000000 * double(feePerK); - // We cast feePerK from int64_t to double because if feePerK is a large number, say - // close to MAX_MONEY, the multiplication operation will result in an integer overflow. - // The variable dResult should never overflow since a 64-bit double in C++ is typically - // a double-precision floating-point number as specified by IEE 754, with a maximum - // value DBL_MAX of 1.79769e+308. - }*/ - return tx.ComputePriority(dResult); } diff --git a/src/coins.h b/src/coins.h index e7a8a017a..fcc32caae 100644 --- a/src/coins.h +++ b/src/coins.h @@ -171,7 +171,7 @@ public: nSize += ::GetSerializeSize(VARINT(nCode), nType, nVersion); // spentness bitmask nSize += nMaskSize; - // txouts themself + // txouts for (unsigned int i = 0; i < vout.size(); i++) if (!vout[i].IsNull()) nSize += ::GetSerializeSize(CTxOutCompressor(REF(vout[i])), nType, nVersion); diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index d9f24db11..d2e7dec3f 100644 --- a/src/consensus/consensus.h +++ b/src/consensus/consensus.h @@ -9,7 +9,11 @@ /** The minimum allowed block version (network rule) */ static const int32_t MIN_BLOCK_VERSION = 4; /** The minimum allowed transaction version (network rule) */ -static const int32_t MIN_TX_VERSION = 1; +static const int32_t SPROUT_MIN_TX_VERSION = 1; +/** The minimum allowed transaction version (network rule) */ +static const int32_t OVERWINTER_MIN_TX_VERSION = 3; +/** The maximum allowed transaction version (network rule) */ +static const int32_t OVERWINTER_MAX_TX_VERSION = 3; /** The maximum allowed size for a serialized block, in bytes (network rule) */ static const unsigned int MAX_BLOCK_SIZE = 2000000; /** The maximum allowed number of signature check operations in a block (network rule) */ @@ -18,6 +22,8 @@ static const unsigned int MAX_BLOCK_SIGOPS = 20000; static const unsigned int MAX_TX_SIZE = 100000; /** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */ extern int COINBASE_MATURITY; +/** The minimum value which is invalid for expiry height, used by CTransaction and CMutableTransaction */ +static constexpr uint32_t TX_EXPIRY_HEIGHT_THRESHOLD = 500000000; /** Flags for LockTime() */ enum { diff --git a/src/consensus/params.h b/src/consensus/params.h index d3e6462b8..855729ff0 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -9,6 +9,54 @@ #include "uint256.h" namespace Consensus { + +/** + * Index into Params.vUpgrades and NetworkUpgradeInfo + * + * Being array indices, these MUST be numbered consecutively. + * + * The order of these indices MUST match the order of the upgrades on-chain, as + * several functions depend on the enum being sorted. + */ +enum UpgradeIndex { + // Sprout must be first + BASE_SPROUT, + UPGRADE_TESTDUMMY, + UPGRADE_OVERWINTER, + // NOTE: Also add new upgrades to NetworkUpgradeInfo in upgrades.cpp + MAX_NETWORK_UPGRADES +}; + +struct NetworkUpgrade { + /** + * The first protocol version which will understand the new consensus rules + */ + int nProtocolVersion; + + /** + * Height of the first block for which the new consensus rules will be active + */ + int nActivationHeight; + + /** + * Special value for nActivationHeight indicating that the upgrade is always active. + * This is useful for testing, as it means tests don't need to deal with the activation + * process (namely, faking a chain of somewhat-arbitrary length). + * + * New blockchains that want to enable upgrade rules from the beginning can also use + * this value. However, additional care must be taken to ensure the genesis block + * satisfies the enabled rules. + */ + static constexpr int ALWAYS_ACTIVE = 0; + + /** + * Special value for nActivationHeight indicating that the upgrade will never activate. + * This is useful when adding upgrade code that has a testnet activation height, but + * should remain disabled on mainnet. + */ + static constexpr int NO_ACTIVATION_HEIGHT = -1; +}; + /** * Parameters that influence chain consensus. */ @@ -39,9 +87,10 @@ struct Params { int nMajorityEnforceBlockUpgrade; int nMajorityRejectBlockOutdated; int nMajorityWindow; + int fPowAllowMinDifficultyBlocks; + NetworkUpgrade vUpgrades[MAX_NETWORK_UPGRADES]; /** Proof of work parameters */ uint256 powLimit; - bool fPowAllowMinDifficultyBlocks; int64_t nPowAveragingWindow; int64_t nPowMaxAdjustDown; int64_t nPowMaxAdjustUp; diff --git a/src/consensus/upgrades.cpp b/src/consensus/upgrades.cpp new file mode 100644 index 000000000..c913c7ea0 --- /dev/null +++ b/src/consensus/upgrades.cpp @@ -0,0 +1,126 @@ +// Copyright (c) 2018 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "consensus/upgrades.h" + +/** + * General information about each network upgrade. + * Ordered by Consensus::UpgradeIndex. + */ +const struct NUInfo NetworkUpgradeInfo[Consensus::MAX_NETWORK_UPGRADES] = { + { + /*.nBranchId =*/ 0, + /*.strName =*/ "Sprout", + /*.strInfo =*/ "The Zcash network at launch", + }, + { + /*.nBranchId =*/ 0x74736554, + /*.strName =*/ "Test dummy", + /*.strInfo =*/ "Test dummy info", + }, + { + /*.nBranchId =*/ 0x5ba81b19, + /*.strName =*/ "Overwinter", + /*.strInfo =*/ "See https://z.cash/upgrade/overwinter.html for details.", + } +}; + +const uint32_t SPROUT_BRANCH_ID = NetworkUpgradeInfo[Consensus::BASE_SPROUT].nBranchId; + +UpgradeState NetworkUpgradeState( + int nHeight, + const Consensus::Params& params, + Consensus::UpgradeIndex idx) +{ + assert(nHeight >= 0); + assert(idx >= Consensus::BASE_SPROUT && idx < Consensus::MAX_NETWORK_UPGRADES); + auto nActivationHeight = params.vUpgrades[idx].nActivationHeight; + + if (nActivationHeight == Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT) { + return UPGRADE_DISABLED; + } else if (nHeight >= nActivationHeight) { + // From ZIP 200: + // + // ACTIVATION_HEIGHT + // The non-zero block height at which the network upgrade rules will come + // into effect, and be enforced as part of the blockchain consensus. + // + // For removal of ambiguity, the block at height ACTIVATION_HEIGHT - 1 is + // subject to the pre-upgrade consensus rules, and would be the last common + // block in the event of a persistent pre-upgrade branch. + return UPGRADE_ACTIVE; + } else { + return UPGRADE_PENDING; + } +} + +bool NetworkUpgradeActive( + int nHeight, + const Consensus::Params& params, + Consensus::UpgradeIndex idx) +{ + return NetworkUpgradeState(nHeight, params, idx) == UPGRADE_ACTIVE; +} + +int CurrentEpoch(int nHeight, const Consensus::Params& params) { + for (auto idxInt = Consensus::MAX_NETWORK_UPGRADES - 1; idxInt >= Consensus::BASE_SPROUT; idxInt--) { + if (NetworkUpgradeActive(nHeight, params, Consensus::UpgradeIndex(idxInt))) { + return idxInt; + } + } +} + +uint32_t CurrentEpochBranchId(int nHeight, const Consensus::Params& params) { + return NetworkUpgradeInfo[CurrentEpoch(nHeight, params)].nBranchId; +} + +bool IsActivationHeight( + int nHeight, + const Consensus::Params& params, + Consensus::UpgradeIndex idx) +{ + assert(idx >= Consensus::BASE_SPROUT && idx < Consensus::MAX_NETWORK_UPGRADES); + + // Don't count Sprout as an activation height + if (idx == Consensus::BASE_SPROUT) { + return false; + } + + return nHeight >= 0 && nHeight == params.vUpgrades[idx].nActivationHeight; +} + +bool IsActivationHeightForAnyUpgrade( + int nHeight, + const Consensus::Params& params) +{ + if (nHeight < 0) { + return false; + } + + // Don't count Sprout as an activation height + for (int idx = Consensus::BASE_SPROUT + 1; idx < Consensus::MAX_NETWORK_UPGRADES; idx++) { + if (nHeight == params.vUpgrades[idx].nActivationHeight) + return true; + } + + return false; +} + +boost::optional NextActivationHeight( + int nHeight, + const Consensus::Params& params) +{ + if (nHeight < 0) { + return boost::none; + } + + // Don't count Sprout as an activation height + for (auto idx = Consensus::BASE_SPROUT + 1; idx < Consensus::MAX_NETWORK_UPGRADES; idx++) { + if (NetworkUpgradeState(nHeight, params, Consensus::UpgradeIndex(idx)) == UPGRADE_PENDING) { + return params.vUpgrades[idx].nActivationHeight; + } + } + + return boost::none; +} diff --git a/src/consensus/upgrades.h b/src/consensus/upgrades.h new file mode 100644 index 000000000..620dc94c4 --- /dev/null +++ b/src/consensus/upgrades.h @@ -0,0 +1,90 @@ +// Copyright (c) 2018 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef ZCASH_CONSENSUS_UPGRADES_H +#define ZCASH_CONSENSUS_UPGRADES_H + +#include "consensus/params.h" + +#include + +enum UpgradeState { + UPGRADE_DISABLED, + UPGRADE_PENDING, + UPGRADE_ACTIVE +}; + +struct NUInfo { + /** Branch ID (a random non-zero 32-bit value) */ + uint32_t nBranchId; + /** User-facing name for the upgrade */ + std::string strName; + /** User-facing information string about the upgrade */ + std::string strInfo; +}; + +extern const struct NUInfo NetworkUpgradeInfo[]; + +// Consensus branch id to identify pre-overwinter (Sprout) consensus rules. +extern const uint32_t SPROUT_BRANCH_ID; + +/** + * Checks the state of a given network upgrade based on block height. + * Caller must check that the height is >= 0 (and handle unknown heights). + */ +UpgradeState NetworkUpgradeState( + int nHeight, + const Consensus::Params& params, + Consensus::UpgradeIndex idx); + +/** + * Returns true if the given network upgrade is active as of the given block + * height. Caller must check that the height is >= 0 (and handle unknown + * heights). + */ +bool NetworkUpgradeActive( + int nHeight, + const Consensus::Params& params, + Consensus::UpgradeIndex idx); + +/** + * Returns the index of the most recent upgrade as of the given block height + * (corresponding to the current "epoch"). Consensus::BASE_SPROUT is the + * default value if no upgrades are active. Caller must check that the height + * is >= 0 (and handle unknown heights). + */ +int CurrentEpoch(int nHeight, const Consensus::Params& params); + +/** + * Returns the branch ID of the most recent upgrade as of the given block height + * (corresponding to the current "epoch"), or 0 if no upgrades are active. + * Caller must check that the height is >= 0 (and handle unknown heights). + */ +uint32_t CurrentEpochBranchId(int nHeight, const Consensus::Params& params); + +/** + * Returns true if the given block height is the activation height for the given + * upgrade. + */ +bool IsActivationHeight( + int nHeight, + const Consensus::Params& params, + Consensus::UpgradeIndex upgrade); + +/** + * Returns true if the given block height is the activation height for any upgrade. + */ +bool IsActivationHeightForAnyUpgrade( + int nHeight, + const Consensus::Params& params); + +/** + * Returns the activation height for the next upgrade after the given block height, + * or boost::none if there are no more known upgrades. + */ +boost::optional NextActivationHeight( + int nHeight, + const Consensus::Params& params); + +#endif // ZCASH_CONSENSUS_UPGRADES_H diff --git a/src/consensus/validation.h b/src/consensus/validation.h index c62adcd8f..6c4db4c59 100644 --- a/src/consensus/validation.h +++ b/src/consensus/validation.h @@ -17,6 +17,7 @@ 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; +static const unsigned char REJECT_HAVEBETTER = 0x44; /** Capture information about block/transaction validation */ class CValidationState { diff --git a/src/crypto/common.h b/src/crypto/common.h index 5d5027ada..ad4c6dd5e 100644 --- a/src/crypto/common.h +++ b/src/crypto/common.h @@ -16,7 +16,7 @@ #include "compat/endian.h" #if defined(NDEBUG) -# error "Bitcoin cannot be compiled without assertions." +# error "Zcash cannot be compiled without assertions." #endif uint16_t static inline ReadLE16(const unsigned char* ptr) diff --git a/src/crypto/equihash.cpp b/src/crypto/equihash.cpp index 9e6d18590..04ee5f3b7 100644 --- a/src/crypto/equihash.cpp +++ b/src/crypto/equihash.cpp @@ -16,6 +16,7 @@ #include "config/bitcoin-config.h" #endif +#include "compat/endian.h" #include "crypto/equihash.h" #include "util.h" #ifndef __linux__ diff --git a/src/cryptoconditions/.gitignore b/src/cryptoconditions/.gitignore new file mode 100644 index 000000000..1d84f2618 --- /dev/null +++ b/src/cryptoconditions/.gitignore @@ -0,0 +1,24 @@ +*.pyc +.cache +/Makefile +/Makefile.in +/aclocal.m4 +/autom4te.cache/ +/src/cryptoconditions-config.h +/configure +/depcomp +/install-sh +/libtool +/ltmain.sh +/m4/ +/missing +/stamp-h? +.deps/ +.dirstamp +.libs/ +*.l[ao] +*.[ao] +*~ +converter-sample.c +config.* +.pytest_cache diff --git a/src/cryptoconditions/.travis.yml b/src/cryptoconditions/.travis.yml new file mode 100644 index 000000000..166cc9def --- /dev/null +++ b/src/cryptoconditions/.travis.yml @@ -0,0 +1,10 @@ +language: C +sudo: true +compiler: + - clang + - gcc +before_script: ./autogen.sh +addons: + apt: + packages: + - gdb diff --git a/src/cryptoconditions/LICENSE b/src/cryptoconditions/LICENSE new file mode 100644 index 000000000..bd423f56e --- /dev/null +++ b/src/cryptoconditions/LICENSE @@ -0,0 +1,13 @@ +Copyright 2017 Scott Sadler + +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/cryptoconditions/Makefile.am b/src/cryptoconditions/Makefile.am new file mode 100644 index 000000000..52f12eee0 --- /dev/null +++ b/src/cryptoconditions/Makefile.am @@ -0,0 +1,91 @@ +lib_LTLIBRARIES=libcryptoconditions.la +noinst_LTLIBRARIES=$(CRYPTOCONDITIONS_CORE) +SUBDIRS = src/include/secp256k1 + +include_HEADERS = include/cryptoconditions.h + +# Have a separate build target for cryptoconditions that does not contain secp256k1 + +libcryptoconditions_la_SOURCES = include/cryptoconditions.h +libcryptoconditions_la_LIBADD = $(CRYPTOCONDITIONS_CORE) $(LIBSECP256K1) + +AM_CFLAGS = -I$(top_srcdir)/src/asn -I$(top_srcdir)/include -I$(top_srcdir)/src/include \ + -Wall -Wno-pointer-sign -Wno-discarded-qualifiers + +LIBSECP256K1=src/include/secp256k1/libsecp256k1.la + +$(LIBSECP256K1): $(wildcard src/secp256k1/*) + $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) + +CRYPTOCONDITIONS_CORE=libcryptoconditions_core.la + +libcryptoconditions_core_la_SOURCES = \ + src/cryptoconditions.c \ + src/utils.c \ + src/include/cJSON.c \ + src/include/sha256.c \ + src/include/ed25519/src/keypair.c \ + src/include/ed25519/src/seed.c \ + src/include/ed25519/src/verify.c \ + src/include/ed25519/src/sign.c \ + src/include/ed25519/src/fe.c \ + src/include/ed25519/src/sc.c \ + src/include/ed25519/src/sha512.c \ + src/include/ed25519/src/ge.c \ + src/include/ed25519/src/add_scalar.c \ + src/include/ed25519/src/key_exchange.c \ + src/asn/Condition.c \ + src/asn/SimpleSha256Condition.c \ + src/asn/CompoundSha256Condition.c \ + src/asn/ConditionTypes.c \ + src/asn/Fulfillment.c \ + src/asn/PreimageFulfillment.c \ + src/asn/PrefixFulfillment.c \ + src/asn/ThresholdFulfillment.c \ + src/asn/RsaSha256Fulfillment.c \ + src/asn/Ed25519Sha512Fulfillment.c \ + src/asn/PrefixFingerprintContents.c \ + src/asn/ThresholdFingerprintContents.c \ + src/asn/RsaFingerprintContents.c \ + src/asn/Ed25519FingerprintContents.c \ + src/asn/EvalFulfillment.c \ + src/asn/Secp256k1FingerprintContents.c \ + src/asn/Secp256k1Fulfillment.c \ + src/asn/INTEGER.c \ + src/asn/NativeEnumerated.c \ + src/asn/NativeInteger.c \ + src/asn/asn_SET_OF.c \ + src/asn/constr_CHOICE.c \ + src/asn/constr_SEQUENCE.c \ + src/asn/constr_SET_OF.c \ + src/asn/OCTET_STRING.c \ + src/asn/BIT_STRING.c \ + src/asn/asn_codecs_prim.c \ + src/asn/ber_tlv_length.c \ + src/asn/ber_tlv_tag.c \ + src/asn/ber_decoder.c \ + src/asn/der_encoder.c \ + src/asn/constr_TYPE.c \ + src/asn/constraints.c \ + src/asn/xer_support.c \ + src/asn/xer_decoder.c \ + src/asn/xer_encoder.c \ + src/asn/per_support.c \ + src/asn/per_decoder.c \ + src/asn/per_encoder.c \ + src/asn/per_opentype.c + +test: + bash -c '[ -d .env ] || virtualenv .env -p python3' + .env/bin/pip install pytest + gdb -batch -ex run -ex bt --args .env/bin/python -m pytest -s -x -v 2>&1 | grep -v ^"No stack."$ + +test-debug-interactive: + gdb -ex run --args python3 -m pytest -s -x -v + +asn: + cd src/asn; \ + mv asn_system.h asn_system.bak; \ + rm *.c *.h; \ + asn1c CryptoConditions.asn; \ + mv asn_system.bak asn_system.h diff --git a/src/cryptoconditions/README.md b/src/cryptoconditions/README.md new file mode 100644 index 000000000..9ec2a6a5f --- /dev/null +++ b/src/cryptoconditions/README.md @@ -0,0 +1,165 @@ +# libcryptoconditions [![Build Status](https://travis-ci.org/libscott/libcryptoconditions.svg?branch=komodo)](https://travis-ci.org/libscott/libcryptoconditions) + +Interledger Crypto-Conditions in C, targeting spec [draft-thomas-crypto-conditions-03](https://tools.ietf.org/html/draft-thomas-crypto-conditions-03). + +Features shared object and easy to use JSON api, as well as a command line interface written in Python. + +## Quickstart + +```shell +git clone --recursive https://github.com/libscott/libcryptoconditions +cd libcryptoconditions +./autogen.sh +./configure +make +./cryptoconditions.py --help +``` + +## Status + +JSON interface may not be particularly safe. The rest is pretty good now. + +## Embedding + +For the binary interface, see [cryptoconditions.h](./include/cryptoconditions.h). + +To embed in other languages, the easiest way may be to call the JSON RPC method via FFI. This is how it looks in Python: + +```python +import json +from ctypes import * + +so = cdll.LoadLibrary('.libs/libcryptoconditions.so') +so.jsonRPC.restype = c_char_p + +def call_cryptoconditions_rpc(method, params): + out = so.jsonRPC(json.dumps({ + 'method': method, + 'params': params, + })) + return json.loads(out) +``` + +## JSON methods + +### encodeCondition + +Encode a JSON condition to a base64 binary string + +```shell +cryptoconditions encodeCondition '{ + "type": "ed25519-sha-256", + "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo" +}' +{ + "bin": "pCeAIHmSOauo_E_36r-8TETmnovf7ZkzJOEu1keSq-KJzx1fgQMCAAA", + "uri": "ni:///sha-256;eZI5q6j8T_fqv7xMROaei9_tmTMk4S7WR5Kr4onPHV8?fpt=ed25519-sha-256&cost=131072" +} +``` + +### decodeCondition + +Decode a binary condition + +```shell +cryptoconditions decodeCondition '{ + "bin": "pCeAIHmSOauo_E_36r-8TETmnovf7ZkzJOEu1keSq-KJzx1fgQMCAAA" +}' +{ + "bin": "pCeAIHmSOauo_E_36r-8TETmnovf7ZkzJOEu1keSq-KJzx1fgQMCAAA", + "uri": "ni:///sha-256;eZI5q6j8T_fqv7xMROaei9_tmTMk4S7WR5Kr4onPHV8?fpt=ed25519-sha-256&cost=131072" +} +``` + +### encodeFulfillment + +Encode a JSON condition to a binary fulfillment. The condition must be fulfilled, that is, +it needs to have signatures present. + +```shell +cryptoconditions encodeFulfillment '{ +{ + "type": "ed25519-sha-256", + "publicKey": "E0x0Ws4GhWhO_zBoUyaLbuqCz6hDdq11Ft1Dgbe9y9k", + "signature": "jcuovSRpHwqiC781KzSM1Jd0Qtyfge0cMGttUdLOVdjJlSBFLTtgpinASOaJpd-VGjhSGWkp1hPWuMAAZq6pAg" +}' +{ + "fulfillment": "pGSAIBNMdFrOBoVoTv8waFMmi27qgs-oQ3atdRbdQ4G3vcvZgUCNy6i9JGkfCqILvzUrNIzUl3RC3J-B7Rwwa21R0s5V2MmVIEUtO2CmKcBI5oml35UaOFIZaSnWE9a4wABmrqkC" +} + +``` + +### decodeFulfillment + +Decode a binary fulfillment + +```shell +cryptoconditions decodeFulfillment '{ + "fulfillment": "pGSAINdamAGCsQq31Uv-08lkBzoO4XLz2qYjJa8CGmj3B1EagUDlVkMAw2CscpCG4syAboKKhId_Hrjl2XTYc-BlIkkBVV-4ghWQozusxh45cBz5tGvSW_XwWVu-JGVRQUOOehAL" +}' +{ + "bin": "pCeAIHmSOauo_E_36r-8TETmnovf7ZkzJOEu1keSq-KJzx1fgQMCAAA", + "uri": "ni:///sha-256;eZI5q6j8T_fqv7xMROaei9_tmTMk4S7WR5Kr4onPHV8?fpt=ed25519-sha-256&cost=131072" +} +``` + +### verifyFulfillment + +Verify a fulfillment against a message and a condition URL + +```shell +cryptoconditions verifyFulfillment '{ + "message": "", + "fulfillment": "pGSAINdamAGCsQq31Uv-08lkBzoO4XLz2qYjJa8CGmj3B1EagUDlVkMAw2CscpCG4syAboKKhId_Hrjl2XTYc-BlIkkBVV-4ghWQozusxh45cBz5tGvSW_XwWVu-JGVRQUOOehAL", + "condition": "pCeAIHmSOauo_E_36r-8TETmnovf7ZkzJOEu1keSq-KJzx1fgQMCAAA" +} +{ + "valid": true +} +``` + +### signTreeEd25519 + +Sign all ed25519 nodes in a condition tree + +```shell +cryptoconditions signTreeEd25519 '{ + "condition": { + "type": "ed25519-sha-256", + "publicKey": "E0x0Ws4GhWhO_zBoUyaLbuqCz6hDdq11Ft1Dgbe9y9k", + }, + "privateKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + "message": "", +}' +{ + "num_signed": 1, + "condition": { + "type": "ed25519-sha-256", + "publicKey": "E0x0Ws4GhWhO_zBoUyaLbuqCz6hDdq11Ft1Dgbe9y9k", + "signature": "jcuovSRpHwqiC781KzSM1Jd0Qtyfge0cMGttUdLOVdjJlSBFLTtgpinASOaJpd-VGjhSGWkp1hPWuMAAZq6pAg" + } +} +``` + +### signTreeSecp256k1 + +Sign all secp256k1 nodes in a condition tree + +```shell +cryptoconditions signTreeSecp256k1 '{ + "condition": { + "type": "secp256k1-sha-256", + "publicKey": "AmkauD4tVL5-I7NN9hE_A8SlA0WdCIeJe_1Nac_km1hr", + }, + "privateKey": "Bxwd5hOLZcTvzrR5Cupm3IV7TWHHl8nNLeO4UhYfRs4", + "message": "", +}' +{ + "num_signed": 1, + "condition": { + "type": "secp256k1-sha-256", + "publicKey": "AmkauD4tVL5-I7NN9hE_A8SlA0WdCIeJe_1Nac_km1hr", + "signature": "LSQLzZo4cmt04KoCdoFcbIJX5MZ9CM6324SqkdqV1PppfUwquiWa7HD97hf4jdkdqU3ep8ZS9AU7zEJoUAl_Gg" + } +} +``` diff --git a/src/cryptoconditions/autogen.sh b/src/cryptoconditions/autogen.sh new file mode 100755 index 000000000..65286b935 --- /dev/null +++ b/src/cryptoconditions/autogen.sh @@ -0,0 +1,3 @@ +#!/bin/sh +set -e +autoreconf -if --warnings=all diff --git a/src/cryptoconditions/compile b/src/cryptoconditions/compile new file mode 100755 index 000000000..a85b723c7 --- /dev/null +++ b/src/cryptoconditions/compile @@ -0,0 +1,347 @@ +#! /bin/sh +# Wrapper for compilers which do not understand '-c -o'. + +scriptversion=2012-10-14.11; # UTC + +# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Written by Tom Tromey . +# +# 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 2, 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 to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +nl=' +' + +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent tools from complaining about whitespace usage. +IFS=" "" $nl" + +file_conv= + +# func_file_conv build_file lazy +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. If the determined conversion +# type is listed in (the comma separated) LAZY, no conversion will +# take place. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv/,$2, in + *,$file_conv,*) + ;; + mingw/*) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin/*) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine/*) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_cl_dashL linkdir +# Make cl look for libraries in LINKDIR +func_cl_dashL () +{ + func_file_conv "$1" + if test -z "$lib_path"; then + lib_path=$file + else + lib_path="$lib_path;$file" + fi + linker_opts="$linker_opts -LIBPATH:$file" +} + +# func_cl_dashl library +# Do a library search-path lookup for cl +func_cl_dashl () +{ + lib=$1 + found=no + save_IFS=$IFS + IFS=';' + for dir in $lib_path $LIB + do + IFS=$save_IFS + if $shared && test -f "$dir/$lib.dll.lib"; then + found=yes + lib=$dir/$lib.dll.lib + break + fi + if test -f "$dir/$lib.lib"; then + found=yes + lib=$dir/$lib.lib + break + fi + if test -f "$dir/lib$lib.a"; then + found=yes + lib=$dir/lib$lib.a + break + fi + done + IFS=$save_IFS + + if test "$found" != yes; then + lib=$lib.lib + fi +} + +# func_cl_wrapper cl arg... +# Adjust compile command to suit cl +func_cl_wrapper () +{ + # Assume a capable shell + lib_path= + shared=: + linker_opts= + for arg + do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + eat=1 + case $2 in + *.o | *.[oO][bB][jJ]) + func_file_conv "$2" + set x "$@" -Fo"$file" + shift + ;; + *) + func_file_conv "$2" + set x "$@" -Fe"$file" + shift + ;; + esac + ;; + -I) + eat=1 + func_file_conv "$2" mingw + set x "$@" -I"$file" + shift + ;; + -I*) + func_file_conv "${1#-I}" mingw + set x "$@" -I"$file" + shift + ;; + -l) + eat=1 + func_cl_dashl "$2" + set x "$@" "$lib" + shift + ;; + -l*) + func_cl_dashl "${1#-l}" + set x "$@" "$lib" + shift + ;; + -L) + eat=1 + func_cl_dashL "$2" + ;; + -L*) + func_cl_dashL "${1#-L}" + ;; + -static) + shared=false + ;; + -Wl,*) + arg=${1#-Wl,} + save_ifs="$IFS"; IFS=',' + for flag in $arg; do + IFS="$save_ifs" + linker_opts="$linker_opts $flag" + done + IFS="$save_ifs" + ;; + -Xlinker) + eat=1 + linker_opts="$linker_opts $2" + ;; + -*) + set x "$@" "$1" + shift + ;; + *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) + func_file_conv "$1" + set x "$@" -Tp"$file" + shift + ;; + *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) + func_file_conv "$1" mingw + set x "$@" "$file" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift + done + if test -n "$linker_opts"; then + linker_opts="-link$linker_opts" + fi + exec "$@" $linker_opts + exit 1 +} + +eat= + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand '-c -o'. +Remove '-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file 'INSTALL'. + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "compile $scriptversion" + exit $? + ;; + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) + func_cl_wrapper "$@" # Doesn't return... + ;; +esac + +ofile= +cfile= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + # So we strip '-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no '-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # '.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` + +# Create the lock directory. +# Note: use '[/\\:.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + test "$cofile" = "$ofile" || mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/src/cryptoconditions/configure.ac b/src/cryptoconditions/configure.ac new file mode 100644 index 000000000..d6b45159a --- /dev/null +++ b/src/cryptoconditions/configure.ac @@ -0,0 +1,42 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ([2.69]) +AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS]) +AC_CONFIG_HEADERS([src/cryptoconditions-config.h]) +AC_CONFIG_MACRO_DIRS([m4]) +AC_CONFIG_SUBDIRS([src/include/secp256k1]) + +AM_INIT_AUTOMAKE([foreign subdir-objects]) +LT_INIT + +# Checks for programs. +AC_PROG_CC +AC_PROG_CC_STDC + +# Checks for libraries. + +# Checks for header files. +AC_FUNC_ALLOCA +AC_CHECK_HEADERS([arpa/inet.h float.h inttypes.h limits.h locale.h malloc.h netinet/in.h stddef.h stdint.h stdlib.h string.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_CHECK_HEADER_STDBOOL +AC_TYPE_INT16_T +AC_TYPE_INT32_T +AC_TYPE_INT8_T +AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T +AC_TYPE_UINT16_T +AC_TYPE_UINT32_T +AC_TYPE_UINT64_T +AC_TYPE_UINT8_T +AC_CHECK_TYPES([ptrdiff_t]) + +# Checks for library functions. +AC_FUNC_MALLOC +AC_FUNC_STRTOD +AC_CHECK_FUNCS([localeconv memchr memset]) + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/src/cryptoconditions/cryptoconditions.py b/src/cryptoconditions/cryptoconditions.py new file mode 100755 index 000000000..4ffd86ce3 --- /dev/null +++ b/src/cryptoconditions/cryptoconditions.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python + +import sys +import json +import ctypes +import base64 +import os.path +import argparse +from ctypes import * + + +so = cdll.LoadLibrary('.libs/libcryptoconditions.so') +so.jsonRPC.restype = c_char_p + + +def jsonRPC(method, params, load=True): + out = so.cc_jsonRPC(json.dumps({ + 'method': method, + 'params': params, + })) + return json.loads(out) if load else out + + +def b16_to_b64(b16): + return base64.urlsafe_b64encode(base64.b16decode(b16)).rstrip('=') + + +USAGE = "cryptoconditions [-h] {method} {request_json}" + +def get_help(): + methods = jsonRPC("listMethods", {})['methods'] + + txt = USAGE + "\n\nmethods:\n" + + for method in methods: + txt += ' %s: %s\n' % (method['name'], method['description']) + + txt += """\noptional arguments: + -h, --help show this help message and exit +""" + return txt + + +def get_parser(): + class Parser(argparse.ArgumentParser): + def format_help(self): + return get_help() + + parser = Parser(description='Crypto Conditions JSON interface', usage=USAGE) + + json_loads = lambda r: json.loads(r) + json_loads.__name__ = 'json' + + parser.add_argument("method") + parser.add_argument("request", type=json_loads) + + return parser + + +if __name__ == '__main__': + args = get_parser().parse_args() + print(jsonRPC(args.method, args.request, load=False)) diff --git a/src/cryptoconditions/include/cryptoconditions.h b/src/cryptoconditions/include/cryptoconditions.h new file mode 100644 index 000000000..08f00a2ca --- /dev/null +++ b/src/cryptoconditions/include/cryptoconditions.h @@ -0,0 +1,106 @@ +#include +#include + + +#ifndef CRYPTOCONDITIONS_H +#define CRYPTOCONDITIONS_H + + +#ifdef __cplusplus +extern "C" { +#endif + + +struct CC; +struct CCType; + + +enum CCTypeId { + CC_Anon = -1, + CC_Preimage = 0, + CC_Prefix = 1, + CC_Threshold = 2, + CC_Ed25519 = 4, + CC_Secp256k1 = 5, + CC_Eval = 15 +}; + + +/* + * Evaliliary verification callback + */ +typedef int (*VerifyEval)(struct CC *cond, void *context); + + + +/* + * Crypto Condition + */ +typedef struct CC { + struct CCType *type; + union { + // public key types + struct { uint8_t *publicKey, *signature; }; + // preimage + struct { uint8_t *preimage; size_t preimageLength; }; + // threshold + struct { long threshold; uint8_t size; struct CC **subconditions; }; + // prefix + struct { uint8_t *prefix; size_t prefixLength; struct CC *subcondition; + size_t maxMessageLength; }; + // eval + struct { uint8_t *code; size_t codeLength; }; + // anon + struct { uint8_t fingerprint[32]; uint32_t subtypes; unsigned long cost; + struct CCType *conditionType; }; + }; +} CC; + + + +/* + * Crypto Condition Visitor + */ +typedef struct CCVisitor { + int (*visit)(CC *cond, struct CCVisitor visitor); + const uint8_t *msg; + size_t msgLength; + void *context; +} CCVisitor; + + +/* + * Public methods + */ +int cc_isFulfilled(const CC *cond); +int cc_verify(const struct CC *cond, const uint8_t *msg, size_t msgLength, + int doHashMessage, const uint8_t *condBin, size_t condBinLength, + VerifyEval verifyEval, void *evalContext); +int cc_visit(CC *cond, struct CCVisitor visitor); +int cc_signTreeEd25519(CC *cond, const uint8_t *privateKey, const uint8_t *msg, + const size_t msgLength); +int cc_signTreeSecp256k1Msg32(CC *cond, const uint8_t *privateKey, const uint8_t *msg32); +int cc_secp256k1VerifyTreeMsg32(const CC *cond, const uint8_t *msg32); +size_t cc_conditionBinary(const CC *cond, uint8_t *buf); +size_t cc_fulfillmentBinary(const CC *cond, uint8_t *buf, size_t bufLength); +struct CC* cc_conditionFromJSON(cJSON *params, char *err); +struct CC* cc_conditionFromJSONString(const char *json, char *err); +struct CC* cc_readConditionBinary(const uint8_t *cond_bin, size_t cond_bin_len); +struct CC* cc_readFulfillmentBinary(const uint8_t *ffill_bin, size_t ffill_bin_len); +struct CC* cc_new(int typeId); +struct cJSON* cc_conditionToJSON(const CC *cond); +char* cc_conditionToJSONString(const CC *cond); +char* cc_conditionUri(const CC *cond); +char* cc_jsonRPC(char *request); +char* cc_typeName(const CC *cond); +enum CCTypeId cc_typeId(const CC *cond); +unsigned long cc_getCost(const CC *cond); +uint32_t cc_typeMask(const CC *cond); +int cc_isAnon(const CC *cond); +void cc_free(struct CC *cond); + +#ifdef __cplusplus +} +#endif + +#endif /* CRYPTOCONDITIONS_H */ diff --git a/src/cryptoconditions/src/anon.c b/src/cryptoconditions/src/anon.c new file mode 100644 index 000000000..114517074 --- /dev/null +++ b/src/cryptoconditions/src/anon.c @@ -0,0 +1,73 @@ + +#include "asn/Condition.h" +#include "asn/Fulfillment.h" +#include "asn/PrefixFingerprintContents.h" +#include "asn/OCTET_STRING.h" +#include "include/cJSON.h" +#include "cryptoconditions.h" + + +struct CCType CC_AnonType; + + +CC *mkAnon(const Condition_t *asnCond) { + + CCType *realType = getTypeByAsnEnum(asnCond->present); + if (!realType) { + fprintf(stderr, "Unknown ASN type: %i", asnCond->present); + return 0; + } + CC *cond = cc_new(CC_Anon); + cond->conditionType = realType; + const CompoundSha256Condition_t *deets = &asnCond->choice.thresholdSha256; + memcpy(cond->fingerprint, deets->fingerprint.buf, 32); + cond->cost = deets->cost; + if (realType->getSubtypes) { + cond->subtypes = fromAsnSubtypes(deets->subtypes); + } + return cond; +} + + + +static void anonToJSON(const CC *cond, cJSON *params) { + unsigned char *b64 = base64_encode(cond->fingerprint, 32); + cJSON_AddItemToObject(params, "fingerprint", cJSON_CreateString(b64)); + free(b64); + cJSON_AddItemToObject(params, "cost", cJSON_CreateNumber(cond->cost)); + cJSON_AddItemToObject(params, "subtypes", cJSON_CreateNumber(cond->subtypes)); +} + + +static unsigned char *anonFingerprint(const CC *cond) { + unsigned char *out = calloc(1, 32); + memcpy(out, cond->fingerprint, 32); + return out; +} + + +static unsigned long anonCost(const CC *cond) { + return cond->cost; +} + + +static uint32_t anonSubtypes(const CC *cond) { + return cond->subtypes; +} + + +static Fulfillment_t *anonFulfillment(const CC *cond) { + return NULL; +} + + +static void anonFree(CC *cond) { +} + + +static int anonIsFulfilled(const CC *cond) { + return 0; +} + + +struct CCType CC_AnonType = { -1, "(anon)", Condition_PR_NOTHING, NULL, &anonFingerprint, &anonCost, &anonSubtypes, NULL, &anonToJSON, NULL, &anonFulfillment, &anonIsFulfilled, &anonFree }; diff --git a/src/cryptoconditions/src/asn/BIT_STRING.c b/src/cryptoconditions/src/asn/BIT_STRING.c new file mode 100644 index 000000000..997ff4161 --- /dev/null +++ b/src/cryptoconditions/src/asn/BIT_STRING.c @@ -0,0 +1,189 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +/* + * BIT STRING basic type description. + */ +static const ber_tlv_tag_t asn_DEF_BIT_STRING_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (3 << 2)) +}; +static asn_OCTET_STRING_specifics_t asn_DEF_BIT_STRING_specs = { + sizeof(BIT_STRING_t), + offsetof(BIT_STRING_t, _asn_ctx), + ASN_OSUBV_BIT +}; +asn_TYPE_descriptor_t asn_DEF_BIT_STRING = { + "BIT STRING", + "BIT_STRING", + OCTET_STRING_free, /* Implemented in terms of OCTET STRING */ + BIT_STRING_print, + BIT_STRING_constraint, + OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_encode_der, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_decode_xer_binary, + BIT_STRING_encode_xer, + OCTET_STRING_decode_uper, /* Unaligned PER decoder */ + OCTET_STRING_encode_uper, /* Unaligned PER encoder */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_BIT_STRING_tags, + sizeof(asn_DEF_BIT_STRING_tags) + / sizeof(asn_DEF_BIT_STRING_tags[0]), + asn_DEF_BIT_STRING_tags, /* Same as above */ + sizeof(asn_DEF_BIT_STRING_tags) + / sizeof(asn_DEF_BIT_STRING_tags[0]), + 0, /* No PER visible constraints */ + 0, 0, /* No members */ + &asn_DEF_BIT_STRING_specs +}; + +/* + * BIT STRING generic constraint. + */ +int +BIT_STRING_constraint(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const BIT_STRING_t *st = (const BIT_STRING_t *)sptr; + + if(st && st->buf) { + if((st->size == 0 && st->bits_unused) + || st->bits_unused < 0 || st->bits_unused > 7) { + ASN__CTFAIL(app_key, td, sptr, + "%s: invalid padding byte (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + return 0; +} + +static char *_bit_pattern[16] = { + "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", + "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111" +}; + +asn_enc_rval_t +BIT_STRING_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_enc_rval_t er; + char scratch[128]; + char *p = scratch; + char *scend = scratch + (sizeof(scratch) - 10); + const BIT_STRING_t *st = (const BIT_STRING_t *)sptr; + int xcan = (flags & XER_F_CANONICAL); + uint8_t *buf; + uint8_t *end; + + if(!st || !st->buf) + ASN__ENCODE_FAILED; + + er.encoded = 0; + + buf = st->buf; + end = buf + st->size - 1; /* Last byte is special */ + + /* + * Binary dump + */ + for(; buf < end; buf++) { + int v = *buf; + int nline = xcan?0:(((buf - st->buf) % 8) == 0); + if(p >= scend || nline) { + er.encoded += p - scratch; + ASN__CALLBACK(scratch, p - scratch); + p = scratch; + if(nline) ASN__TEXT_INDENT(1, ilevel); + } + memcpy(p + 0, _bit_pattern[v >> 4], 4); + memcpy(p + 4, _bit_pattern[v & 0x0f], 4); + p += 8; + } + + if(!xcan && ((buf - st->buf) % 8) == 0) + ASN__TEXT_INDENT(1, ilevel); + er.encoded += p - scratch; + ASN__CALLBACK(scratch, p - scratch); + p = scratch; + + if(buf == end) { + int v = *buf; + int ubits = st->bits_unused; + int i; + for(i = 7; i >= ubits; i--) + *p++ = (v & (1 << i)) ? 0x31 : 0x30; + er.encoded += p - scratch; + ASN__CALLBACK(scratch, p - scratch); + } + + if(!xcan) ASN__TEXT_INDENT(1, ilevel - 1); + + ASN__ENCODED_OK(er); +cb_failed: + ASN__ENCODE_FAILED; +} + + +/* + * BIT STRING specific contents printer. + */ +int +BIT_STRING_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + const char * const h2c = "0123456789ABCDEF"; + char scratch[64]; + const BIT_STRING_t *st = (const BIT_STRING_t *)sptr; + uint8_t *buf; + uint8_t *end; + char *p = scratch; + + (void)td; /* Unused argument */ + + if(!st || !st->buf) + return (cb("", 8, app_key) < 0) ? -1 : 0; + + ilevel++; + buf = st->buf; + end = buf + st->size; + + /* + * Hexadecimal dump. + */ + for(; buf < end; buf++) { + if((buf - st->buf) % 16 == 0 && (st->size > 16) + && buf != st->buf) { + _i_INDENT(1); + /* Dump the string */ + if(cb(scratch, p - scratch, app_key) < 0) return -1; + p = scratch; + } + *p++ = h2c[*buf >> 4]; + *p++ = h2c[*buf & 0x0F]; + *p++ = 0x20; + } + + if(p > scratch) { + p--; /* Eat the tailing space */ + + if((st->size > 16)) { + _i_INDENT(1); + } + + /* Dump the incomplete 16-bytes row */ + if(cb(scratch, p - scratch, app_key) < 0) + return -1; + } + + return 0; +} + diff --git a/src/cryptoconditions/src/asn/BIT_STRING.h b/src/cryptoconditions/src/asn/BIT_STRING.h new file mode 100644 index 000000000..732e878bc --- /dev/null +++ b/src/cryptoconditions/src/asn/BIT_STRING.h @@ -0,0 +1,33 @@ +/*- + * Copyright (c) 2003 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _BIT_STRING_H_ +#define _BIT_STRING_H_ + +#include /* Some help from OCTET STRING */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct BIT_STRING_s { + uint8_t *buf; /* BIT STRING body */ + int size; /* Size of the above buffer */ + + int bits_unused;/* Unused trailing bits in the last octet (0..7) */ + + asn_struct_ctx_t _asn_ctx; /* Parsing across buffer boundaries */ +} BIT_STRING_t; + +extern asn_TYPE_descriptor_t asn_DEF_BIT_STRING; + +asn_struct_print_f BIT_STRING_print; /* Human-readable output */ +asn_constr_check_f BIT_STRING_constraint; +xer_type_encoder_f BIT_STRING_encode_xer; + +#ifdef __cplusplus +} +#endif + +#endif /* _BIT_STRING_H_ */ diff --git a/src/cryptoconditions/src/asn/CompoundSha256Condition.c b/src/cryptoconditions/src/asn/CompoundSha256Condition.c new file mode 100644 index 000000000..31c276bf6 --- /dev/null +++ b/src/cryptoconditions/src/asn/CompoundSha256Condition.c @@ -0,0 +1,235 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#include "CompoundSha256Condition.h" + +static int +cost_3_constraint(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + + /* Constraint check succeeded */ + return 0; +} + +/* + * This type is implemented using NativeInteger, + * so here we adjust the DEF accordingly. + */ +static void +cost_3_inherit_TYPE_descriptor(asn_TYPE_descriptor_t *td) { + td->free_struct = asn_DEF_NativeInteger.free_struct; + td->print_struct = asn_DEF_NativeInteger.print_struct; + td->check_constraints = asn_DEF_NativeInteger.check_constraints; + td->ber_decoder = asn_DEF_NativeInteger.ber_decoder; + td->der_encoder = asn_DEF_NativeInteger.der_encoder; + td->xer_decoder = asn_DEF_NativeInteger.xer_decoder; + td->xer_encoder = asn_DEF_NativeInteger.xer_encoder; + td->uper_decoder = asn_DEF_NativeInteger.uper_decoder; + td->uper_encoder = asn_DEF_NativeInteger.uper_encoder; + if(!td->per_constraints) + td->per_constraints = asn_DEF_NativeInteger.per_constraints; + td->elements = asn_DEF_NativeInteger.elements; + td->elements_count = asn_DEF_NativeInteger.elements_count; + /* td->specifics = asn_DEF_NativeInteger.specifics; // Defined explicitly */ +} + +static void +cost_3_free(asn_TYPE_descriptor_t *td, + void *struct_ptr, int contents_only) { + cost_3_inherit_TYPE_descriptor(td); + td->free_struct(td, struct_ptr, contents_only); +} + +static int +cost_3_print(asn_TYPE_descriptor_t *td, const void *struct_ptr, + int ilevel, asn_app_consume_bytes_f *cb, void *app_key) { + cost_3_inherit_TYPE_descriptor(td); + return td->print_struct(td, struct_ptr, ilevel, cb, app_key); +} + +static asn_dec_rval_t +cost_3_decode_ber(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **structure, const void *bufptr, size_t size, int tag_mode) { + cost_3_inherit_TYPE_descriptor(td); + return td->ber_decoder(opt_codec_ctx, td, structure, bufptr, size, tag_mode); +} + +static asn_enc_rval_t +cost_3_encode_der(asn_TYPE_descriptor_t *td, + void *structure, int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + cost_3_inherit_TYPE_descriptor(td); + return td->der_encoder(td, structure, tag_mode, tag, cb, app_key); +} + +static asn_dec_rval_t +cost_3_decode_xer(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **structure, const char *opt_mname, const void *bufptr, size_t size) { + cost_3_inherit_TYPE_descriptor(td); + return td->xer_decoder(opt_codec_ctx, td, structure, opt_mname, bufptr, size); +} + +static asn_enc_rval_t +cost_3_encode_xer(asn_TYPE_descriptor_t *td, void *structure, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + cost_3_inherit_TYPE_descriptor(td); + return td->xer_encoder(td, structure, ilevel, flags, cb, app_key); +} + +static int +memb_fingerprint_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const OCTET_STRING_t *st = (const OCTET_STRING_t *)sptr; + size_t size; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + size = st->size; + + if((size == 32)) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static int +memb_cost_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + + /* Constraint check succeeded */ + return 0; +} + +static const asn_INTEGER_specifics_t asn_SPC_cost_specs_3 = { + 0, 0, 0, 0, 0, + 0, /* Native long size */ + 1 /* Unsigned representation */ +}; +static const ber_tlv_tag_t asn_DEF_cost_tags_3[] = { + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + (ASN_TAG_CLASS_UNIVERSAL | (2 << 2)) +}; +static /* Use -fall-defs-global to expose */ +asn_TYPE_descriptor_t asn_DEF_cost_3 = { + "cost", + "cost", + cost_3_free, + cost_3_print, + cost_3_constraint, + cost_3_decode_ber, + cost_3_encode_der, + cost_3_decode_xer, + cost_3_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_cost_tags_3, + sizeof(asn_DEF_cost_tags_3) + /sizeof(asn_DEF_cost_tags_3[0]) - 1, /* 1 */ + asn_DEF_cost_tags_3, /* Same as above */ + sizeof(asn_DEF_cost_tags_3) + /sizeof(asn_DEF_cost_tags_3[0]), /* 2 */ + 0, /* No PER visible constraints */ + 0, 0, /* No members */ + &asn_SPC_cost_specs_3 /* Additional specs */ +}; + +static asn_TYPE_member_t asn_MBR_CompoundSha256Condition_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct CompoundSha256Condition, fingerprint), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_OCTET_STRING, + memb_fingerprint_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "fingerprint" + }, + { ATF_NOFLAGS, 0, offsetof(struct CompoundSha256Condition, cost), + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_cost_3, + memb_cost_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "cost" + }, + { ATF_NOFLAGS, 0, offsetof(struct CompoundSha256Condition, subtypes), + (ASN_TAG_CLASS_CONTEXT | (2 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_ConditionTypes, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "subtypes" + }, +}; +static const ber_tlv_tag_t asn_DEF_CompoundSha256Condition_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_CompoundSha256Condition_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 }, /* fingerprint */ + { (ASN_TAG_CLASS_CONTEXT | (1 << 2)), 1, 0, 0 }, /* cost */ + { (ASN_TAG_CLASS_CONTEXT | (2 << 2)), 2, 0, 0 } /* subtypes */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_CompoundSha256Condition_specs_1 = { + sizeof(struct CompoundSha256Condition), + offsetof(struct CompoundSha256Condition, _asn_ctx), + asn_MAP_CompoundSha256Condition_tag2el_1, + 3, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_CompoundSha256Condition = { + "CompoundSha256Condition", + "CompoundSha256Condition", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_CompoundSha256Condition_tags_1, + sizeof(asn_DEF_CompoundSha256Condition_tags_1) + /sizeof(asn_DEF_CompoundSha256Condition_tags_1[0]), /* 1 */ + asn_DEF_CompoundSha256Condition_tags_1, /* Same as above */ + sizeof(asn_DEF_CompoundSha256Condition_tags_1) + /sizeof(asn_DEF_CompoundSha256Condition_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_CompoundSha256Condition_1, + 3, /* Elements count */ + &asn_SPC_CompoundSha256Condition_specs_1 /* Additional specs */ +}; + diff --git a/src/cryptoconditions/src/asn/CompoundSha256Condition.h b/src/cryptoconditions/src/asn/CompoundSha256Condition.h new file mode 100644 index 000000000..5d791be4f --- /dev/null +++ b/src/cryptoconditions/src/asn/CompoundSha256Condition.h @@ -0,0 +1,42 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#ifndef _CompoundSha256Condition_H_ +#define _CompoundSha256Condition_H_ + + +#include + +/* Including external dependencies */ +#include +#include +#include "ConditionTypes.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* CompoundSha256Condition */ +typedef struct CompoundSha256Condition { + OCTET_STRING_t fingerprint; + unsigned long cost; + ConditionTypes_t subtypes; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} CompoundSha256Condition_t; + +/* Implementation */ +/* extern asn_TYPE_descriptor_t asn_DEF_cost_3; // (Use -fall-defs-global to expose) */ +extern asn_TYPE_descriptor_t asn_DEF_CompoundSha256Condition; + +#ifdef __cplusplus +} +#endif + +#endif /* _CompoundSha256Condition_H_ */ +#include diff --git a/src/cryptoconditions/src/asn/Condition.c b/src/cryptoconditions/src/asn/Condition.c new file mode 100644 index 000000000..49ec83e3b --- /dev/null +++ b/src/cryptoconditions/src/asn/Condition.c @@ -0,0 +1,114 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#include "Condition.h" + +static asn_TYPE_member_t asn_MBR_Condition_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct Condition, choice.preimageSha256), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_SimpleSha256Condition, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "preimageSha256" + }, + { ATF_NOFLAGS, 0, offsetof(struct Condition, choice.prefixSha256), + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_CompoundSha256Condition, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "prefixSha256" + }, + { ATF_NOFLAGS, 0, offsetof(struct Condition, choice.thresholdSha256), + (ASN_TAG_CLASS_CONTEXT | (2 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_CompoundSha256Condition, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "thresholdSha256" + }, + { ATF_NOFLAGS, 0, offsetof(struct Condition, choice.rsaSha256), + (ASN_TAG_CLASS_CONTEXT | (3 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_SimpleSha256Condition, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "rsaSha256" + }, + { ATF_NOFLAGS, 0, offsetof(struct Condition, choice.ed25519Sha256), + (ASN_TAG_CLASS_CONTEXT | (4 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_SimpleSha256Condition, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "ed25519Sha256" + }, + { ATF_NOFLAGS, 0, offsetof(struct Condition, choice.secp256k1Sha256), + (ASN_TAG_CLASS_CONTEXT | (5 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_SimpleSha256Condition, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "secp256k1Sha256" + }, + { ATF_NOFLAGS, 0, offsetof(struct Condition, choice.evalSha256), + (ASN_TAG_CLASS_CONTEXT | (15 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_SimpleSha256Condition, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "evalSha256" + }, +}; +static const asn_TYPE_tag2member_t asn_MAP_Condition_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 }, /* preimageSha256 */ + { (ASN_TAG_CLASS_CONTEXT | (1 << 2)), 1, 0, 0 }, /* prefixSha256 */ + { (ASN_TAG_CLASS_CONTEXT | (2 << 2)), 2, 0, 0 }, /* thresholdSha256 */ + { (ASN_TAG_CLASS_CONTEXT | (3 << 2)), 3, 0, 0 }, /* rsaSha256 */ + { (ASN_TAG_CLASS_CONTEXT | (4 << 2)), 4, 0, 0 }, /* ed25519Sha256 */ + { (ASN_TAG_CLASS_CONTEXT | (5 << 2)), 5, 0, 0 }, /* secp256k1Sha256 */ + { (ASN_TAG_CLASS_CONTEXT | (15 << 2)), 6, 0, 0 } /* evalSha256 */ +}; +static asn_CHOICE_specifics_t asn_SPC_Condition_specs_1 = { + sizeof(struct Condition), + offsetof(struct Condition, _asn_ctx), + offsetof(struct Condition, present), + sizeof(((struct Condition *)0)->present), + asn_MAP_Condition_tag2el_1, + 7, /* Count of tags in the map */ + 0, + -1 /* Extensions start */ +}; +asn_TYPE_descriptor_t asn_DEF_Condition = { + "Condition", + "Condition", + CHOICE_free, + CHOICE_print, + CHOICE_constraint, + CHOICE_decode_ber, + CHOICE_encode_der, + CHOICE_decode_xer, + CHOICE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + CHOICE_outmost_tag, + 0, /* No effective tags (pointer) */ + 0, /* No effective tags (count) */ + 0, /* No tags (pointer) */ + 0, /* No tags (count) */ + 0, /* No PER visible constraints */ + asn_MBR_Condition_1, + 7, /* Elements count */ + &asn_SPC_Condition_specs_1 /* Additional specs */ +}; + diff --git a/src/cryptoconditions/src/asn/Condition.h b/src/cryptoconditions/src/asn/Condition.h new file mode 100644 index 000000000..ad32b18aa --- /dev/null +++ b/src/cryptoconditions/src/asn/Condition.h @@ -0,0 +1,59 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#ifndef _Condition_H_ +#define _Condition_H_ + + +#include + +/* Including external dependencies */ +#include "SimpleSha256Condition.h" +#include "CompoundSha256Condition.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Dependencies */ +typedef enum Condition_PR { + Condition_PR_NOTHING, /* No components present */ + Condition_PR_preimageSha256, + Condition_PR_prefixSha256, + Condition_PR_thresholdSha256, + Condition_PR_rsaSha256, + Condition_PR_ed25519Sha256, + Condition_PR_secp256k1Sha256, + Condition_PR_evalSha256 +} Condition_PR; + +/* Condition */ +typedef struct Condition { + Condition_PR present; + union Condition_u { + SimpleSha256Condition_t preimageSha256; + CompoundSha256Condition_t prefixSha256; + CompoundSha256Condition_t thresholdSha256; + SimpleSha256Condition_t rsaSha256; + SimpleSha256Condition_t ed25519Sha256; + SimpleSha256Condition_t secp256k1Sha256; + SimpleSha256Condition_t evalSha256; + } choice; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} Condition_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_Condition; + +#ifdef __cplusplus +} +#endif + +#endif /* _Condition_H_ */ +#include diff --git a/src/cryptoconditions/src/asn/ConditionTypes.c b/src/cryptoconditions/src/asn/ConditionTypes.c new file mode 100644 index 000000000..16ca9d19c --- /dev/null +++ b/src/cryptoconditions/src/asn/ConditionTypes.c @@ -0,0 +1,108 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#include "ConditionTypes.h" + +int +ConditionTypes_constraint(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + /* Replace with underlying type checker */ + td->check_constraints = asn_DEF_BIT_STRING.check_constraints; + return td->check_constraints(td, sptr, ctfailcb, app_key); +} + +/* + * This type is implemented using BIT_STRING, + * so here we adjust the DEF accordingly. + */ +static void +ConditionTypes_1_inherit_TYPE_descriptor(asn_TYPE_descriptor_t *td) { + td->free_struct = asn_DEF_BIT_STRING.free_struct; + td->print_struct = asn_DEF_BIT_STRING.print_struct; + td->check_constraints = asn_DEF_BIT_STRING.check_constraints; + td->ber_decoder = asn_DEF_BIT_STRING.ber_decoder; + td->der_encoder = asn_DEF_BIT_STRING.der_encoder; + td->xer_decoder = asn_DEF_BIT_STRING.xer_decoder; + td->xer_encoder = asn_DEF_BIT_STRING.xer_encoder; + td->uper_decoder = asn_DEF_BIT_STRING.uper_decoder; + td->uper_encoder = asn_DEF_BIT_STRING.uper_encoder; + if(!td->per_constraints) + td->per_constraints = asn_DEF_BIT_STRING.per_constraints; + td->elements = asn_DEF_BIT_STRING.elements; + td->elements_count = asn_DEF_BIT_STRING.elements_count; + td->specifics = asn_DEF_BIT_STRING.specifics; +} + +void +ConditionTypes_free(asn_TYPE_descriptor_t *td, + void *struct_ptr, int contents_only) { + ConditionTypes_1_inherit_TYPE_descriptor(td); + td->free_struct(td, struct_ptr, contents_only); +} + +int +ConditionTypes_print(asn_TYPE_descriptor_t *td, const void *struct_ptr, + int ilevel, asn_app_consume_bytes_f *cb, void *app_key) { + ConditionTypes_1_inherit_TYPE_descriptor(td); + return td->print_struct(td, struct_ptr, ilevel, cb, app_key); +} + +asn_dec_rval_t +ConditionTypes_decode_ber(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **structure, const void *bufptr, size_t size, int tag_mode) { + ConditionTypes_1_inherit_TYPE_descriptor(td); + return td->ber_decoder(opt_codec_ctx, td, structure, bufptr, size, tag_mode); +} + +asn_enc_rval_t +ConditionTypes_encode_der(asn_TYPE_descriptor_t *td, + void *structure, int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + ConditionTypes_1_inherit_TYPE_descriptor(td); + return td->der_encoder(td, structure, tag_mode, tag, cb, app_key); +} + +asn_dec_rval_t +ConditionTypes_decode_xer(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **structure, const char *opt_mname, const void *bufptr, size_t size) { + ConditionTypes_1_inherit_TYPE_descriptor(td); + return td->xer_decoder(opt_codec_ctx, td, structure, opt_mname, bufptr, size); +} + +asn_enc_rval_t +ConditionTypes_encode_xer(asn_TYPE_descriptor_t *td, void *structure, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + ConditionTypes_1_inherit_TYPE_descriptor(td); + return td->xer_encoder(td, structure, ilevel, flags, cb, app_key); +} + +static const ber_tlv_tag_t asn_DEF_ConditionTypes_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (3 << 2)) +}; +asn_TYPE_descriptor_t asn_DEF_ConditionTypes = { + "ConditionTypes", + "ConditionTypes", + ConditionTypes_free, + ConditionTypes_print, + ConditionTypes_constraint, + ConditionTypes_decode_ber, + ConditionTypes_encode_der, + ConditionTypes_decode_xer, + ConditionTypes_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_ConditionTypes_tags_1, + sizeof(asn_DEF_ConditionTypes_tags_1) + /sizeof(asn_DEF_ConditionTypes_tags_1[0]), /* 1 */ + asn_DEF_ConditionTypes_tags_1, /* Same as above */ + sizeof(asn_DEF_ConditionTypes_tags_1) + /sizeof(asn_DEF_ConditionTypes_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + 0, 0, /* Defined elsewhere */ + 0 /* No specifics */ +}; + diff --git a/src/cryptoconditions/src/asn/ConditionTypes.h b/src/cryptoconditions/src/asn/ConditionTypes.h new file mode 100644 index 000000000..3d391eb36 --- /dev/null +++ b/src/cryptoconditions/src/asn/ConditionTypes.h @@ -0,0 +1,49 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#ifndef _ConditionTypes_H_ +#define _ConditionTypes_H_ + + +#include + +/* Including external dependencies */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Dependencies */ +typedef enum ConditionTypes { + ConditionTypes_preImageSha256 = 0, + ConditionTypes_prefixSha256 = 1, + ConditionTypes_thresholdSha256 = 2, + ConditionTypes_rsaSha256 = 3, + ConditionTypes_ed25519Sha256 = 4, + ConditionTypes_secp256k1Sha256 = 5, + ConditionTypes_evalSha256 = 15 +} e_ConditionTypes; + +/* ConditionTypes */ +typedef BIT_STRING_t ConditionTypes_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_ConditionTypes; +asn_struct_free_f ConditionTypes_free; +asn_struct_print_f ConditionTypes_print; +asn_constr_check_f ConditionTypes_constraint; +ber_type_decoder_f ConditionTypes_decode_ber; +der_type_encoder_f ConditionTypes_encode_der; +xer_type_decoder_f ConditionTypes_decode_xer; +xer_type_encoder_f ConditionTypes_encode_xer; + +#ifdef __cplusplus +} +#endif + +#endif /* _ConditionTypes_H_ */ +#include diff --git a/src/cryptoconditions/src/asn/CryptoConditions.asn b/src/cryptoconditions/src/asn/CryptoConditions.asn new file mode 100644 index 000000000..42a3c88f1 --- /dev/null +++ b/src/cryptoconditions/src/asn/CryptoConditions.asn @@ -0,0 +1,113 @@ +---- + +Crypto-Conditions DEFINITIONS AUTOMATIC TAGS ::= BEGIN + + -- Conditions + + Condition ::= CHOICE { + preimageSha256 [0] SimpleSha256Condition, + prefixSha256 [1] CompoundSha256Condition, + thresholdSha256 [2] CompoundSha256Condition, + rsaSha256 [3] SimpleSha256Condition, + ed25519Sha256 [4] SimpleSha256Condition, + secp256k1Sha256 [5] SimpleSha256Condition, + evalSha256 [15] SimpleSha256Condition + } + + SimpleSha256Condition ::= SEQUENCE { + fingerprint OCTET STRING (SIZE(32)), + cost INTEGER (0..4294967295) + } + + CompoundSha256Condition ::= SEQUENCE { + fingerprint OCTET STRING (SIZE(32)), + cost INTEGER (0..4294967295), + subtypes ConditionTypes + } + + ConditionTypes ::= BIT STRING { + preImageSha256 (0), + prefixSha256 (1), + thresholdSha256 (2), + rsaSha256 (3), + ed25519Sha256 (4), + secp256k1Sha256 (5), + evalSha256 (15) + } + + -- Fulfillments + + Fulfillment ::= CHOICE { + preimageSha256 [0] PreimageFulfillment , + prefixSha256 [1] PrefixFulfillment, + thresholdSha256 [2] ThresholdFulfillment, + rsaSha256 [3] RsaSha256Fulfillment, + ed25519Sha256 [4] Ed25519Sha512Fulfillment, + secp256k1Sha256 [5] Secp256k1Fulfillment, + evalSha256 [15] EvalFulfillment + } + + PreimageFulfillment ::= SEQUENCE { + preimage OCTET STRING + } + + PrefixFulfillment ::= SEQUENCE { + prefix OCTET STRING, + maxMessageLength INTEGER (0..4294967295), + subfulfillment Fulfillment + } + + ThresholdFulfillment ::= SEQUENCE { + subfulfillments SET OF Fulfillment, + subconditions SET OF Condition + } + + RsaSha256Fulfillment ::= SEQUENCE { + modulus OCTET STRING, + signature OCTET STRING + } + + Ed25519Sha512Fulfillment ::= SEQUENCE { + publicKey OCTET STRING (SIZE(32)), + signature OCTET STRING (SIZE(64)) + } + + Secp256k1Fulfillment ::= SEQUENCE { + publicKey OCTET STRING (SIZE(33)), + signature OCTET STRING (SIZE(64)) + } + + EvalFulfillment ::= SEQUENCE { + code OCTET STRING + } + + -- Fingerprint Content + + -- The PREIMAGE-SHA-256 condition fingerprint content is not DER encoded + -- The fingerprint content is the preimage + -- Same for Eval + + PrefixFingerprintContents ::= SEQUENCE { + prefix OCTET STRING, + maxMessageLength INTEGER (0..4294967295), + subcondition Condition + } + + ThresholdFingerprintContents ::= SEQUENCE { + threshold INTEGER (1..65535), + subconditions2 SET OF Condition + } + + RsaFingerprintContents ::= SEQUENCE { + modulus OCTET STRING + } + + Ed25519FingerprintContents ::= SEQUENCE { + publicKey OCTET STRING (SIZE(32)) + } + + Secp256k1FingerprintContents ::= SEQUENCE { + publicKey OCTET STRING (SIZE(33)) + } + +END diff --git a/src/cryptoconditions/src/asn/Ed25519FingerprintContents.c b/src/cryptoconditions/src/asn/Ed25519FingerprintContents.c new file mode 100644 index 000000000..c47213100 --- /dev/null +++ b/src/cryptoconditions/src/asn/Ed25519FingerprintContents.c @@ -0,0 +1,84 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#include "Ed25519FingerprintContents.h" + +static int +memb_publicKey_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const OCTET_STRING_t *st = (const OCTET_STRING_t *)sptr; + size_t size; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + size = st->size; + + if((size == 32)) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static asn_TYPE_member_t asn_MBR_Ed25519FingerprintContents_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct Ed25519FingerprintContents, publicKey), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_OCTET_STRING, + memb_publicKey_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "publicKey" + }, +}; +static const ber_tlv_tag_t asn_DEF_Ed25519FingerprintContents_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_Ed25519FingerprintContents_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 } /* publicKey */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_Ed25519FingerprintContents_specs_1 = { + sizeof(struct Ed25519FingerprintContents), + offsetof(struct Ed25519FingerprintContents, _asn_ctx), + asn_MAP_Ed25519FingerprintContents_tag2el_1, + 1, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_Ed25519FingerprintContents = { + "Ed25519FingerprintContents", + "Ed25519FingerprintContents", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_Ed25519FingerprintContents_tags_1, + sizeof(asn_DEF_Ed25519FingerprintContents_tags_1) + /sizeof(asn_DEF_Ed25519FingerprintContents_tags_1[0]), /* 1 */ + asn_DEF_Ed25519FingerprintContents_tags_1, /* Same as above */ + sizeof(asn_DEF_Ed25519FingerprintContents_tags_1) + /sizeof(asn_DEF_Ed25519FingerprintContents_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_Ed25519FingerprintContents_1, + 1, /* Elements count */ + &asn_SPC_Ed25519FingerprintContents_specs_1 /* Additional specs */ +}; + diff --git a/src/cryptoconditions/src/asn/Ed25519FingerprintContents.h b/src/cryptoconditions/src/asn/Ed25519FingerprintContents.h new file mode 100644 index 000000000..7ef9e188e --- /dev/null +++ b/src/cryptoconditions/src/asn/Ed25519FingerprintContents.h @@ -0,0 +1,37 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#ifndef _Ed25519FingerprintContents_H_ +#define _Ed25519FingerprintContents_H_ + + +#include + +/* Including external dependencies */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Ed25519FingerprintContents */ +typedef struct Ed25519FingerprintContents { + OCTET_STRING_t publicKey; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} Ed25519FingerprintContents_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_Ed25519FingerprintContents; + +#ifdef __cplusplus +} +#endif + +#endif /* _Ed25519FingerprintContents_H_ */ +#include diff --git a/src/cryptoconditions/src/asn/Ed25519Sha512Fulfillment.c b/src/cryptoconditions/src/asn/Ed25519Sha512Fulfillment.c new file mode 100644 index 000000000..6f756fb56 --- /dev/null +++ b/src/cryptoconditions/src/asn/Ed25519Sha512Fulfillment.c @@ -0,0 +1,120 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#include "Ed25519Sha512Fulfillment.h" + +static int +memb_publicKey_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const OCTET_STRING_t *st = (const OCTET_STRING_t *)sptr; + size_t size; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + size = st->size; + + if((size == 32)) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static int +memb_signature_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const OCTET_STRING_t *st = (const OCTET_STRING_t *)sptr; + size_t size; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + size = st->size; + + if((size == 64)) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static asn_TYPE_member_t asn_MBR_Ed25519Sha512Fulfillment_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct Ed25519Sha512Fulfillment, publicKey), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_OCTET_STRING, + memb_publicKey_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "publicKey" + }, + { ATF_NOFLAGS, 0, offsetof(struct Ed25519Sha512Fulfillment, signature), + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_OCTET_STRING, + memb_signature_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "signature" + }, +}; +static const ber_tlv_tag_t asn_DEF_Ed25519Sha512Fulfillment_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_Ed25519Sha512Fulfillment_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 }, /* publicKey */ + { (ASN_TAG_CLASS_CONTEXT | (1 << 2)), 1, 0, 0 } /* signature */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_Ed25519Sha512Fulfillment_specs_1 = { + sizeof(struct Ed25519Sha512Fulfillment), + offsetof(struct Ed25519Sha512Fulfillment, _asn_ctx), + asn_MAP_Ed25519Sha512Fulfillment_tag2el_1, + 2, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_Ed25519Sha512Fulfillment = { + "Ed25519Sha512Fulfillment", + "Ed25519Sha512Fulfillment", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_Ed25519Sha512Fulfillment_tags_1, + sizeof(asn_DEF_Ed25519Sha512Fulfillment_tags_1) + /sizeof(asn_DEF_Ed25519Sha512Fulfillment_tags_1[0]), /* 1 */ + asn_DEF_Ed25519Sha512Fulfillment_tags_1, /* Same as above */ + sizeof(asn_DEF_Ed25519Sha512Fulfillment_tags_1) + /sizeof(asn_DEF_Ed25519Sha512Fulfillment_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_Ed25519Sha512Fulfillment_1, + 2, /* Elements count */ + &asn_SPC_Ed25519Sha512Fulfillment_specs_1 /* Additional specs */ +}; + diff --git a/src/cryptoconditions/src/asn/Ed25519Sha512Fulfillment.h b/src/cryptoconditions/src/asn/Ed25519Sha512Fulfillment.h new file mode 100644 index 000000000..603839803 --- /dev/null +++ b/src/cryptoconditions/src/asn/Ed25519Sha512Fulfillment.h @@ -0,0 +1,38 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#ifndef _Ed25519Sha512Fulfillment_H_ +#define _Ed25519Sha512Fulfillment_H_ + + +#include + +/* Including external dependencies */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Ed25519Sha512Fulfillment */ +typedef struct Ed25519Sha512Fulfillment { + OCTET_STRING_t publicKey; + OCTET_STRING_t signature; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} Ed25519Sha512Fulfillment_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_Ed25519Sha512Fulfillment; + +#ifdef __cplusplus +} +#endif + +#endif /* _Ed25519Sha512Fulfillment_H_ */ +#include diff --git a/src/cryptoconditions/src/asn/EvalFulfillment.c b/src/cryptoconditions/src/asn/EvalFulfillment.c new file mode 100644 index 000000000..f43b21e1f --- /dev/null +++ b/src/cryptoconditions/src/asn/EvalFulfillment.c @@ -0,0 +1,58 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#include "EvalFulfillment.h" + +static asn_TYPE_member_t asn_MBR_EvalFulfillment_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct EvalFulfillment, code), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_OCTET_STRING, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "code" + }, +}; +static const ber_tlv_tag_t asn_DEF_EvalFulfillment_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_EvalFulfillment_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 } /* code */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_EvalFulfillment_specs_1 = { + sizeof(struct EvalFulfillment), + offsetof(struct EvalFulfillment, _asn_ctx), + asn_MAP_EvalFulfillment_tag2el_1, + 1, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_EvalFulfillment = { + "EvalFulfillment", + "EvalFulfillment", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_EvalFulfillment_tags_1, + sizeof(asn_DEF_EvalFulfillment_tags_1) + /sizeof(asn_DEF_EvalFulfillment_tags_1[0]), /* 1 */ + asn_DEF_EvalFulfillment_tags_1, /* Same as above */ + sizeof(asn_DEF_EvalFulfillment_tags_1) + /sizeof(asn_DEF_EvalFulfillment_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_EvalFulfillment_1, + 1, /* Elements count */ + &asn_SPC_EvalFulfillment_specs_1 /* Additional specs */ +}; + diff --git a/src/cryptoconditions/src/asn/EvalFulfillment.h b/src/cryptoconditions/src/asn/EvalFulfillment.h new file mode 100644 index 000000000..378baa367 --- /dev/null +++ b/src/cryptoconditions/src/asn/EvalFulfillment.h @@ -0,0 +1,37 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#ifndef _EvalFulfillment_H_ +#define _EvalFulfillment_H_ + + +#include + +/* Including external dependencies */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* EvalFulfillment */ +typedef struct EvalFulfillment { + OCTET_STRING_t code; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} EvalFulfillment_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_EvalFulfillment; + +#ifdef __cplusplus +} +#endif + +#endif /* _EvalFulfillment_H_ */ +#include diff --git a/src/cryptoconditions/src/asn/Fulfillment.c b/src/cryptoconditions/src/asn/Fulfillment.c new file mode 100644 index 000000000..faf43b772 --- /dev/null +++ b/src/cryptoconditions/src/asn/Fulfillment.c @@ -0,0 +1,114 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#include "Fulfillment.h" + +static asn_TYPE_member_t asn_MBR_Fulfillment_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct Fulfillment, choice.preimageSha256), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_PreimageFulfillment, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "preimageSha256" + }, + { ATF_POINTER, 0, offsetof(struct Fulfillment, choice.prefixSha256), + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_PrefixFulfillment, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "prefixSha256" + }, + { ATF_POINTER, 0, offsetof(struct Fulfillment, choice.thresholdSha256), + (ASN_TAG_CLASS_CONTEXT | (2 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_ThresholdFulfillment, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "thresholdSha256" + }, + { ATF_NOFLAGS, 0, offsetof(struct Fulfillment, choice.rsaSha256), + (ASN_TAG_CLASS_CONTEXT | (3 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_RsaSha256Fulfillment, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "rsaSha256" + }, + { ATF_NOFLAGS, 0, offsetof(struct Fulfillment, choice.ed25519Sha256), + (ASN_TAG_CLASS_CONTEXT | (4 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_Ed25519Sha512Fulfillment, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "ed25519Sha256" + }, + { ATF_NOFLAGS, 0, offsetof(struct Fulfillment, choice.secp256k1Sha256), + (ASN_TAG_CLASS_CONTEXT | (5 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_Secp256k1Fulfillment, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "secp256k1Sha256" + }, + { ATF_NOFLAGS, 0, offsetof(struct Fulfillment, choice.evalSha256), + (ASN_TAG_CLASS_CONTEXT | (15 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_EvalFulfillment, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "evalSha256" + }, +}; +static const asn_TYPE_tag2member_t asn_MAP_Fulfillment_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 }, /* preimageSha256 */ + { (ASN_TAG_CLASS_CONTEXT | (1 << 2)), 1, 0, 0 }, /* prefixSha256 */ + { (ASN_TAG_CLASS_CONTEXT | (2 << 2)), 2, 0, 0 }, /* thresholdSha256 */ + { (ASN_TAG_CLASS_CONTEXT | (3 << 2)), 3, 0, 0 }, /* rsaSha256 */ + { (ASN_TAG_CLASS_CONTEXT | (4 << 2)), 4, 0, 0 }, /* ed25519Sha256 */ + { (ASN_TAG_CLASS_CONTEXT | (5 << 2)), 5, 0, 0 }, /* secp256k1Sha256 */ + { (ASN_TAG_CLASS_CONTEXT | (15 << 2)), 6, 0, 0 } /* evalSha256 */ +}; +static asn_CHOICE_specifics_t asn_SPC_Fulfillment_specs_1 = { + sizeof(struct Fulfillment), + offsetof(struct Fulfillment, _asn_ctx), + offsetof(struct Fulfillment, present), + sizeof(((struct Fulfillment *)0)->present), + asn_MAP_Fulfillment_tag2el_1, + 7, /* Count of tags in the map */ + 0, + -1 /* Extensions start */ +}; +asn_TYPE_descriptor_t asn_DEF_Fulfillment = { + "Fulfillment", + "Fulfillment", + CHOICE_free, + CHOICE_print, + CHOICE_constraint, + CHOICE_decode_ber, + CHOICE_encode_der, + CHOICE_decode_xer, + CHOICE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + CHOICE_outmost_tag, + 0, /* No effective tags (pointer) */ + 0, /* No effective tags (count) */ + 0, /* No tags (pointer) */ + 0, /* No tags (count) */ + 0, /* No PER visible constraints */ + asn_MBR_Fulfillment_1, + 7, /* Elements count */ + &asn_SPC_Fulfillment_specs_1 /* Additional specs */ +}; + diff --git a/src/cryptoconditions/src/asn/Fulfillment.h b/src/cryptoconditions/src/asn/Fulfillment.h new file mode 100644 index 000000000..01799e949 --- /dev/null +++ b/src/cryptoconditions/src/asn/Fulfillment.h @@ -0,0 +1,70 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#ifndef _Fulfillment_H_ +#define _Fulfillment_H_ + + +#include + +/* Including external dependencies */ +#include "PreimageFulfillment.h" +#include "RsaSha256Fulfillment.h" +#include "Ed25519Sha512Fulfillment.h" +#include "Secp256k1Fulfillment.h" +#include "EvalFulfillment.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Dependencies */ +typedef enum Fulfillment_PR { + Fulfillment_PR_NOTHING, /* No components present */ + Fulfillment_PR_preimageSha256, + Fulfillment_PR_prefixSha256, + Fulfillment_PR_thresholdSha256, + Fulfillment_PR_rsaSha256, + Fulfillment_PR_ed25519Sha256, + Fulfillment_PR_secp256k1Sha256, + Fulfillment_PR_evalSha256 +} Fulfillment_PR; + +/* Forward declarations */ +struct PrefixFulfillment; +struct ThresholdFulfillment; + +/* Fulfillment */ +typedef struct Fulfillment { + Fulfillment_PR present; + union Fulfillment_u { + PreimageFulfillment_t preimageSha256; + struct PrefixFulfillment *prefixSha256; + struct ThresholdFulfillment *thresholdSha256; + RsaSha256Fulfillment_t rsaSha256; + Ed25519Sha512Fulfillment_t ed25519Sha256; + Secp256k1Fulfillment_t secp256k1Sha256; + EvalFulfillment_t evalSha256; + } choice; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} Fulfillment_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_Fulfillment; + +#ifdef __cplusplus +} +#endif + +/* Referred external types */ +#include "PrefixFulfillment.h" +#include "ThresholdFulfillment.h" + +#endif /* _Fulfillment_H_ */ +#include diff --git a/src/cryptoconditions/src/asn/INTEGER.c b/src/cryptoconditions/src/asn/INTEGER.c new file mode 100644 index 000000000..eed82176b --- /dev/null +++ b/src/cryptoconditions/src/asn/INTEGER.c @@ -0,0 +1,1025 @@ +/*- + * Copyright (c) 2003-2014 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include /* Encoder and decoder of a primitive type */ +#include + +/* + * INTEGER basic type description. + */ +static const ber_tlv_tag_t asn_DEF_INTEGER_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (2 << 2)) +}; +asn_TYPE_descriptor_t asn_DEF_INTEGER = { + "INTEGER", + "INTEGER", + ASN__PRIMITIVE_TYPE_free, + INTEGER_print, + asn_generic_no_constraint, + ber_decode_primitive, + INTEGER_encode_der, + INTEGER_decode_xer, + INTEGER_encode_xer, +#ifdef ASN_DISABLE_PER_SUPPORT + 0, + 0, +#else + INTEGER_decode_uper, /* Unaligned PER decoder */ + INTEGER_encode_uper, /* Unaligned PER encoder */ +#endif /* ASN_DISABLE_PER_SUPPORT */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_INTEGER_tags, + sizeof(asn_DEF_INTEGER_tags) / sizeof(asn_DEF_INTEGER_tags[0]), + asn_DEF_INTEGER_tags, /* Same as above */ + sizeof(asn_DEF_INTEGER_tags) / sizeof(asn_DEF_INTEGER_tags[0]), + 0, /* No PER visible constraints */ + 0, 0, /* No members */ + 0 /* No specifics */ +}; + +/* + * Encode INTEGER type using DER. + */ +asn_enc_rval_t +INTEGER_encode_der(asn_TYPE_descriptor_t *td, void *sptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + INTEGER_t *st = (INTEGER_t *)sptr; + + ASN_DEBUG("%s %s as INTEGER (tm=%d)", + cb?"Encoding":"Estimating", td->name, tag_mode); + + /* + * Canonicalize integer in the buffer. + * (Remove too long sign extension, remove some first 0x00 bytes) + */ + if(st->buf) { + uint8_t *buf = st->buf; + uint8_t *end1 = buf + st->size - 1; + int shift; + + /* Compute the number of superfluous leading bytes */ + for(; buf < end1; buf++) { + /* + * If the contents octets of an integer value encoding + * consist of more than one octet, then the bits of the + * first octet and bit 8 of the second octet: + * a) shall not all be ones; and + * b) shall not all be zero. + */ + switch(*buf) { + case 0x00: if((buf[1] & 0x80) == 0) + continue; + break; + case 0xff: if((buf[1] & 0x80)) + continue; + break; + } + break; + } + + /* Remove leading superfluous bytes from the integer */ + shift = buf - st->buf; + if(shift) { + uint8_t *nb = st->buf; + uint8_t *end; + + st->size -= shift; /* New size, minus bad bytes */ + end = nb + st->size; + + for(; nb < end; nb++, buf++) + *nb = *buf; + } + + } /* if(1) */ + + return der_encode_primitive(td, sptr, tag_mode, tag, cb, app_key); +} + +static const asn_INTEGER_enum_map_t *INTEGER_map_enum2value(asn_INTEGER_specifics_t *specs, const char *lstart, const char *lstop); + +/* + * INTEGER specific human-readable output. + */ +static ssize_t +INTEGER__dump(const asn_TYPE_descriptor_t *td, const INTEGER_t *st, asn_app_consume_bytes_f *cb, void *app_key, int plainOrXER) { + asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; + char scratch[32]; /* Enough for 64-bit integer */ + uint8_t *buf = st->buf; + uint8_t *buf_end = st->buf + st->size; + signed long value; + ssize_t wrote = 0; + char *p; + int ret; + + if(specs && specs->field_unsigned) + ret = asn_INTEGER2ulong(st, (unsigned long *)&value); + else + ret = asn_INTEGER2long(st, &value); + + /* Simple case: the integer size is small */ + if(ret == 0) { + const asn_INTEGER_enum_map_t *el; + size_t scrsize; + char *scr; + + el = (value >= 0 || !specs || !specs->field_unsigned) + ? INTEGER_map_value2enum(specs, value) : 0; + if(el) { + scrsize = el->enum_len + 32; + scr = (char *)alloca(scrsize); + if(plainOrXER == 0) + ret = snprintf(scr, scrsize, + "%ld (%s)", value, el->enum_name); + else + ret = snprintf(scr, scrsize, + "<%s/>", el->enum_name); + } else if(plainOrXER && specs && specs->strict_enumeration) { + ASN_DEBUG("ASN.1 forbids dealing with " + "unknown value of ENUMERATED type"); + errno = EPERM; + return -1; + } else { + scrsize = sizeof(scratch); + scr = scratch; + ret = snprintf(scr, scrsize, + (specs && specs->field_unsigned) + ?"%lu":"%ld", value); + } + assert(ret > 0 && (size_t)ret < scrsize); + return (cb(scr, ret, app_key) < 0) ? -1 : ret; + } else if(plainOrXER && specs && specs->strict_enumeration) { + /* + * Here and earlier, we cannot encode the ENUMERATED values + * if there is no corresponding identifier. + */ + ASN_DEBUG("ASN.1 forbids dealing with " + "unknown value of ENUMERATED type"); + errno = EPERM; + return -1; + } + + /* Output in the long xx:yy:zz... format */ + /* TODO: replace with generic algorithm (Knuth TAOCP Vol 2, 4.3.1) */ + for(p = scratch; buf < buf_end; buf++) { + const char * const h2c = "0123456789ABCDEF"; + if((p - scratch) >= (ssize_t)(sizeof(scratch) - 4)) { + /* Flush buffer */ + if(cb(scratch, p - scratch, app_key) < 0) + return -1; + wrote += p - scratch; + p = scratch; + } + *p++ = h2c[*buf >> 4]; + *p++ = h2c[*buf & 0x0F]; + *p++ = 0x3a; /* ":" */ + } + if(p != scratch) + p--; /* Remove the last ":" */ + + wrote += p - scratch; + return (cb(scratch, p - scratch, app_key) < 0) ? -1 : wrote; +} + +/* + * INTEGER specific human-readable output. + */ +int +INTEGER_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + const INTEGER_t *st = (const INTEGER_t *)sptr; + ssize_t ret; + + (void)td; + (void)ilevel; + + if(!st || !st->buf) + ret = cb("", 8, app_key); + else + ret = INTEGER__dump(td, st, cb, app_key, 0); + + return (ret < 0) ? -1 : 0; +} + +struct e2v_key { + const char *start; + const char *stop; + const asn_INTEGER_enum_map_t *vemap; + const unsigned int *evmap; +}; +static int +INTEGER__compar_enum2value(const void *kp, const void *am) { + const struct e2v_key *key = (const struct e2v_key *)kp; + const asn_INTEGER_enum_map_t *el = (const asn_INTEGER_enum_map_t *)am; + const char *ptr, *end, *name; + + /* Remap the element (sort by different criterion) */ + el = key->vemap + key->evmap[el - key->vemap]; + + /* Compare strings */ + for(ptr = key->start, end = key->stop, name = el->enum_name; + ptr < end; ptr++, name++) { + if(*ptr != *name) + return *(const unsigned char *)ptr + - *(const unsigned char *)name; + } + return name[0] ? -1 : 0; +} + +static const asn_INTEGER_enum_map_t * +INTEGER_map_enum2value(asn_INTEGER_specifics_t *specs, const char *lstart, const char *lstop) { + const asn_INTEGER_enum_map_t *el_found; + int count = specs ? specs->map_count : 0; + struct e2v_key key; + const char *lp; + + if(!count) return NULL; + + /* Guaranteed: assert(lstart < lstop); */ + /* Figure out the tag name */ + for(lstart++, lp = lstart; lp < lstop; lp++) { + switch(*lp) { + case 9: case 10: case 11: case 12: case 13: case 32: /* WSP */ + case 0x2f: /* '/' */ case 0x3e: /* '>' */ + break; + default: + continue; + } + break; + } + if(lp == lstop) return NULL; /* No tag found */ + lstop = lp; + + key.start = lstart; + key.stop = lstop; + key.vemap = specs->value2enum; + key.evmap = specs->enum2value; + el_found = (asn_INTEGER_enum_map_t *)bsearch(&key, + specs->value2enum, count, sizeof(specs->value2enum[0]), + INTEGER__compar_enum2value); + if(el_found) { + /* Remap enum2value into value2enum */ + el_found = key.vemap + key.evmap[el_found - key.vemap]; + } + return el_found; +} + +static int +INTEGER__compar_value2enum(const void *kp, const void *am) { + long a = *(const long *)kp; + const asn_INTEGER_enum_map_t *el = (const asn_INTEGER_enum_map_t *)am; + long b = el->nat_value; + if(a < b) return -1; + else if(a == b) return 0; + else return 1; +} + +const asn_INTEGER_enum_map_t * +INTEGER_map_value2enum(asn_INTEGER_specifics_t *specs, long value) { + int count = specs ? specs->map_count : 0; + if(!count) return 0; + return (asn_INTEGER_enum_map_t *)bsearch(&value, specs->value2enum, + count, sizeof(specs->value2enum[0]), + INTEGER__compar_value2enum); +} + +static int +INTEGER_st_prealloc(INTEGER_t *st, int min_size) { + void *p = MALLOC(min_size + 1); + if(p) { + void *b = st->buf; + st->size = 0; + st->buf = p; + FREEMEM(b); + return 0; + } else { + return -1; + } +} + +/* + * Decode the chunk of XML text encoding INTEGER. + */ +static enum xer_pbd_rval +INTEGER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chunk_buf, size_t chunk_size) { + INTEGER_t *st = (INTEGER_t *)sptr; + long dec_value; + long hex_value = 0; + const char *lp; + const char *lstart = (const char *)chunk_buf; + const char *lstop = lstart + chunk_size; + enum { + ST_LEADSPACE, + ST_SKIPSPHEX, + ST_WAITDIGITS, + ST_DIGITS, + ST_DIGITS_TRAILSPACE, + ST_HEXDIGIT1, + ST_HEXDIGIT2, + ST_HEXDIGITS_TRAILSPACE, + ST_HEXCOLON, + ST_END_ENUM, + ST_UNEXPECTED + } state = ST_LEADSPACE; + const char *dec_value_start = 0; /* INVARIANT: always !0 in ST_DIGITS */ + const char *dec_value_end = 0; + + if(chunk_size) + ASN_DEBUG("INTEGER body %ld 0x%2x..0x%2x", + (long)chunk_size, *lstart, lstop[-1]); + + if(INTEGER_st_prealloc(st, (chunk_size/3) + 1)) + return XPBD_SYSTEM_FAILURE; + + /* + * We may have received a tag here. It will be processed inline. + * Use strtoul()-like code and serialize the result. + */ + for(lp = lstart; lp < lstop; lp++) { + int lv = *lp; + switch(lv) { + case 0x09: case 0x0a: case 0x0d: case 0x20: + switch(state) { + case ST_LEADSPACE: + case ST_DIGITS_TRAILSPACE: + case ST_HEXDIGITS_TRAILSPACE: + case ST_SKIPSPHEX: + continue; + case ST_DIGITS: + dec_value_end = lp; + state = ST_DIGITS_TRAILSPACE; + continue; + case ST_HEXCOLON: + state = ST_HEXDIGITS_TRAILSPACE; + continue; + default: + break; + } + break; + case 0x2d: /* '-' */ + if(state == ST_LEADSPACE) { + dec_value = 0; + dec_value_start = lp; + state = ST_WAITDIGITS; + continue; + } + break; + case 0x2b: /* '+' */ + if(state == ST_LEADSPACE) { + dec_value = 0; + dec_value_start = lp; + state = ST_WAITDIGITS; + continue; + } + break; + case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: + case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: + switch(state) { + case ST_DIGITS: continue; + case ST_SKIPSPHEX: /* Fall through */ + case ST_HEXDIGIT1: + hex_value = (lv - 0x30) << 4; + state = ST_HEXDIGIT2; + continue; + case ST_HEXDIGIT2: + hex_value += (lv - 0x30); + state = ST_HEXCOLON; + st->buf[st->size++] = (uint8_t)hex_value; + continue; + case ST_HEXCOLON: + return XPBD_BROKEN_ENCODING; + case ST_LEADSPACE: + dec_value = 0; + dec_value_start = lp; + /* FALL THROUGH */ + case ST_WAITDIGITS: + state = ST_DIGITS; + continue; + default: + break; + } + break; + case 0x3c: /* '<', start of XML encoded enumeration */ + if(state == ST_LEADSPACE) { + const asn_INTEGER_enum_map_t *el; + el = INTEGER_map_enum2value( + (asn_INTEGER_specifics_t *) + td->specifics, lstart, lstop); + if(el) { + ASN_DEBUG("Found \"%s\" => %ld", + el->enum_name, el->nat_value); + dec_value = el->nat_value; + state = ST_END_ENUM; + lp = lstop - 1; + continue; + } + ASN_DEBUG("Unknown identifier for INTEGER"); + } + return XPBD_BROKEN_ENCODING; + case 0x3a: /* ':' */ + if(state == ST_HEXCOLON) { + /* This colon is expected */ + state = ST_HEXDIGIT1; + continue; + } else if(state == ST_DIGITS) { + /* The colon here means that we have + * decoded the first two hexadecimal + * places as a decimal value. + * Switch decoding mode. */ + ASN_DEBUG("INTEGER re-evaluate as hex form"); + state = ST_SKIPSPHEX; + dec_value_start = 0; + lp = lstart - 1; + continue; + } else { + ASN_DEBUG("state %d at %ld", state, (long)(lp - lstart)); + break; + } + /* [A-Fa-f] */ + case 0x41:case 0x42:case 0x43:case 0x44:case 0x45:case 0x46: + case 0x61:case 0x62:case 0x63:case 0x64:case 0x65:case 0x66: + switch(state) { + case ST_SKIPSPHEX: + case ST_LEADSPACE: /* Fall through */ + case ST_HEXDIGIT1: + hex_value = lv - ((lv < 0x61) ? 0x41 : 0x61); + hex_value += 10; + hex_value <<= 4; + state = ST_HEXDIGIT2; + continue; + case ST_HEXDIGIT2: + hex_value += lv - ((lv < 0x61) ? 0x41 : 0x61); + hex_value += 10; + st->buf[st->size++] = (uint8_t)hex_value; + state = ST_HEXCOLON; + continue; + case ST_DIGITS: + ASN_DEBUG("INTEGER re-evaluate as hex form"); + state = ST_SKIPSPHEX; + dec_value_start = 0; + lp = lstart - 1; + continue; + default: + break; + } + break; + } + + /* Found extra non-numeric stuff */ + ASN_DEBUG("INTEGER :: Found non-numeric 0x%2x at %ld", + lv, (long)(lp - lstart)); + state = ST_UNEXPECTED; + break; + } + + switch(state) { + case ST_END_ENUM: + /* Got a complete and valid enumeration encoded as a tag. */ + break; + case ST_DIGITS: + dec_value_end = lstop; + /* FALL THROUGH */ + case ST_DIGITS_TRAILSPACE: + /* The last symbol encountered was a digit. */ + switch(asn_strtol_lim(dec_value_start, &dec_value_end, &dec_value)) { + case ASN_STRTOL_OK: + break; + case ASN_STRTOL_ERROR_RANGE: + return XPBD_DECODER_LIMIT; + case ASN_STRTOL_ERROR_INVAL: + case ASN_STRTOL_EXPECT_MORE: + case ASN_STRTOL_EXTRA_DATA: + return XPBD_BROKEN_ENCODING; + } + break; + case ST_HEXCOLON: + case ST_HEXDIGITS_TRAILSPACE: + st->buf[st->size] = 0; /* Just in case termination */ + return XPBD_BODY_CONSUMED; + case ST_HEXDIGIT1: + case ST_HEXDIGIT2: + case ST_SKIPSPHEX: + return XPBD_BROKEN_ENCODING; + case ST_LEADSPACE: + /* Content not found */ + return XPBD_NOT_BODY_IGNORE; + case ST_WAITDIGITS: + case ST_UNEXPECTED: + ASN_DEBUG("INTEGER: No useful digits (state %d)", state); + return XPBD_BROKEN_ENCODING; /* No digits */ + } + + /* + * Convert the result of parsing of enumeration or a straight + * decimal value into a BER representation. + */ + if(asn_long2INTEGER(st, dec_value)) + return XPBD_SYSTEM_FAILURE; + + return XPBD_BODY_CONSUMED; +} + +asn_dec_rval_t +INTEGER_decode_xer(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname, + const void *buf_ptr, size_t size) { + + return xer_decode_primitive(opt_codec_ctx, td, + sptr, sizeof(INTEGER_t), opt_mname, + buf_ptr, size, INTEGER__xer_body_decode); +} + +asn_enc_rval_t +INTEGER_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + const INTEGER_t *st = (const INTEGER_t *)sptr; + asn_enc_rval_t er; + + (void)ilevel; + (void)flags; + + if(!st || !st->buf) + ASN__ENCODE_FAILED; + + er.encoded = INTEGER__dump(td, st, cb, app_key, 1); + if(er.encoded < 0) ASN__ENCODE_FAILED; + + ASN__ENCODED_OK(er); +} + +#ifndef ASN_DISABLE_PER_SUPPORT + +asn_dec_rval_t +INTEGER_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) { + asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; + asn_dec_rval_t rval = { RC_OK, 0 }; + INTEGER_t *st = (INTEGER_t *)*sptr; + asn_per_constraint_t *ct; + int repeat; + + (void)opt_codec_ctx; + + if(!st) { + st = (INTEGER_t *)(*sptr = CALLOC(1, sizeof(*st))); + if(!st) ASN__DECODE_FAILED; + } + + if(!constraints) constraints = td->per_constraints; + ct = constraints ? &constraints->value : 0; + + if(ct && ct->flags & APC_EXTENSIBLE) { + int inext = per_get_few_bits(pd, 1); + if(inext < 0) ASN__DECODE_STARVED; + if(inext) ct = 0; + } + + FREEMEM(st->buf); + st->buf = 0; + st->size = 0; + if(ct) { + if(ct->flags & APC_SEMI_CONSTRAINED) { + st->buf = (uint8_t *)CALLOC(1, 2); + if(!st->buf) ASN__DECODE_FAILED; + st->size = 1; + } else if(ct->flags & APC_CONSTRAINED && ct->range_bits >= 0) { + size_t size = (ct->range_bits + 7) >> 3; + st->buf = (uint8_t *)MALLOC(1 + size + 1); + if(!st->buf) ASN__DECODE_FAILED; + st->size = size; + } + } + + /* X.691-2008/11, #13.2.2, constrained whole number */ + if(ct && ct->flags != APC_UNCONSTRAINED) { + /* #11.5.6 */ + ASN_DEBUG("Integer with range %d bits", ct->range_bits); + if(ct->range_bits >= 0) { + if((size_t)ct->range_bits > 8 * sizeof(unsigned long)) + ASN__DECODE_FAILED; + + if(specs && specs->field_unsigned) { + unsigned long uvalue; + if(uper_get_constrained_whole_number(pd, + &uvalue, ct->range_bits)) + ASN__DECODE_STARVED; + ASN_DEBUG("Got value %lu + low %ld", + uvalue, ct->lower_bound); + uvalue += ct->lower_bound; + if(asn_ulong2INTEGER(st, uvalue)) + ASN__DECODE_FAILED; + } else { + unsigned long svalue; + if(uper_get_constrained_whole_number(pd, + &svalue, ct->range_bits)) + ASN__DECODE_STARVED; + ASN_DEBUG("Got value %ld + low %ld", + svalue, ct->lower_bound); + svalue += ct->lower_bound; + if(asn_long2INTEGER(st, svalue)) + ASN__DECODE_FAILED; + } + return rval; + } + } else { + ASN_DEBUG("Decoding unconstrained integer %s", td->name); + } + + /* X.691, #12.2.3, #12.2.4 */ + do { + ssize_t len; + void *p; + int ret; + + /* Get the PER length */ + len = uper_get_length(pd, -1, &repeat); + if(len < 0) ASN__DECODE_STARVED; + + p = REALLOC(st->buf, st->size + len + 1); + if(!p) ASN__DECODE_FAILED; + st->buf = (uint8_t *)p; + + ret = per_get_many_bits(pd, &st->buf[st->size], 0, 8 * len); + if(ret < 0) ASN__DECODE_STARVED; + st->size += len; + } while(repeat); + st->buf[st->size] = 0; /* JIC */ + + /* #12.2.3 */ + if(ct && ct->lower_bound) { + /* + * TODO: replace by in-place arithmetics. + */ + long value; + if(asn_INTEGER2long(st, &value)) + ASN__DECODE_FAILED; + if(asn_long2INTEGER(st, value + ct->lower_bound)) + ASN__DECODE_FAILED; + } + + return rval; +} + +asn_enc_rval_t +INTEGER_encode_uper(asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) { + asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; + asn_enc_rval_t er; + INTEGER_t *st = (INTEGER_t *)sptr; + const uint8_t *buf; + const uint8_t *end; + asn_per_constraint_t *ct; + long value = 0; + unsigned long v = 0; + + if(!st || st->size == 0) ASN__ENCODE_FAILED; + + if(!constraints) constraints = td->per_constraints; + ct = constraints ? &constraints->value : 0; + + er.encoded = 0; + + if(ct) { + int inext = 0; + if(specs && specs->field_unsigned) { + unsigned long uval; + if(asn_INTEGER2ulong(st, &uval)) + ASN__ENCODE_FAILED; + /* Check proper range */ + if(ct->flags & APC_SEMI_CONSTRAINED) { + if(uval < (unsigned long)ct->lower_bound) + inext = 1; + } else if(ct->range_bits >= 0) { + if(uval < (unsigned long)ct->lower_bound + || uval > (unsigned long)ct->upper_bound) + inext = 1; + } + ASN_DEBUG("Value %lu (%02x/%d) lb %lu ub %lu %s", + uval, st->buf[0], st->size, + ct->lower_bound, ct->upper_bound, + inext ? "ext" : "fix"); + value = uval; + } else { + if(asn_INTEGER2long(st, &value)) + ASN__ENCODE_FAILED; + /* Check proper range */ + if(ct->flags & APC_SEMI_CONSTRAINED) { + if(value < ct->lower_bound) + inext = 1; + } else if(ct->range_bits >= 0) { + if(value < ct->lower_bound + || value > ct->upper_bound) + inext = 1; + } + ASN_DEBUG("Value %ld (%02x/%d) lb %ld ub %ld %s", + value, st->buf[0], st->size, + ct->lower_bound, ct->upper_bound, + inext ? "ext" : "fix"); + } + if(ct->flags & APC_EXTENSIBLE) { + if(per_put_few_bits(po, inext, 1)) + ASN__ENCODE_FAILED; + if(inext) ct = 0; + } else if(inext) { + ASN__ENCODE_FAILED; + } + } + + + /* X.691-11/2008, #13.2.2, test if constrained whole number */ + if(ct && ct->range_bits >= 0) { + /* #11.5.6 -> #11.3 */ + ASN_DEBUG("Encoding integer %ld (%lu) with range %d bits", + value, value - ct->lower_bound, ct->range_bits); + v = value - ct->lower_bound; + if(uper_put_constrained_whole_number_u(po, v, ct->range_bits)) + ASN__ENCODE_FAILED; + ASN__ENCODED_OK(er); + } + + if(ct && ct->lower_bound) { + ASN_DEBUG("Adjust lower bound to %ld", ct->lower_bound); + /* TODO: adjust lower bound */ + ASN__ENCODE_FAILED; + } + + for(buf = st->buf, end = st->buf + st->size; buf < end;) { + ssize_t mayEncode = uper_put_length(po, end - buf); + if(mayEncode < 0) + ASN__ENCODE_FAILED; + if(per_put_many_bits(po, buf, 8 * mayEncode)) + ASN__ENCODE_FAILED; + buf += mayEncode; + } + + ASN__ENCODED_OK(er); +} + +#endif /* ASN_DISABLE_PER_SUPPORT */ + +int +asn_INTEGER2long(const INTEGER_t *iptr, long *lptr) { + uint8_t *b, *end; + size_t size; + long l; + + /* Sanity checking */ + if(!iptr || !iptr->buf || !lptr) { + errno = EINVAL; + return -1; + } + + /* Cache the begin/end of the buffer */ + b = iptr->buf; /* Start of the INTEGER buffer */ + size = iptr->size; + end = b + size; /* Where to stop */ + + if(size > sizeof(long)) { + uint8_t *end1 = end - 1; + /* + * Slightly more advanced processing, + * able to >sizeof(long) bytes, + * when the actual value is small + * (0x0000000000abcdef would yield a fine 0x00abcdef) + */ + /* Skip out the insignificant leading bytes */ + for(; b < end1; b++) { + switch(*b) { + case 0x00: if((b[1] & 0x80) == 0) continue; break; + case 0xff: if((b[1] & 0x80) != 0) continue; break; + } + break; + } + + size = end - b; + if(size > sizeof(long)) { + /* Still cannot fit the long */ + errno = ERANGE; + return -1; + } + } + + /* Shortcut processing of a corner case */ + if(end == b) { + *lptr = 0; + return 0; + } + + /* Perform the sign initialization */ + /* Actually l = -(*b >> 7); gains nothing, yet unreadable! */ + if((*b >> 7)) l = -1; else l = 0; + + /* Conversion engine */ + for(; b < end; b++) + l = (l << 8) | *b; + + *lptr = l; + return 0; +} + +int +asn_INTEGER2ulong(const INTEGER_t *iptr, unsigned long *lptr) { + uint8_t *b, *end; + unsigned long l; + size_t size; + + if(!iptr || !iptr->buf || !lptr) { + errno = EINVAL; + return -1; + } + + b = iptr->buf; + size = iptr->size; + end = b + size; + + /* If all extra leading bytes are zeroes, ignore them */ + for(; size > sizeof(unsigned long); b++, size--) { + if(*b) { + /* Value won't fit unsigned long */ + errno = ERANGE; + return -1; + } + } + + /* Conversion engine */ + for(l = 0; b < end; b++) + l = (l << 8) | *b; + + *lptr = l; + return 0; +} + +int +asn_ulong2INTEGER(INTEGER_t *st, unsigned long value) { + uint8_t *buf; + uint8_t *end; + uint8_t *b; + int shr; + + if(value <= LONG_MAX) + return asn_long2INTEGER(st, value); + + buf = (uint8_t *)MALLOC(1 + sizeof(value)); + if(!buf) return -1; + + end = buf + (sizeof(value) + 1); + buf[0] = 0; + for(b = buf + 1, shr = (sizeof(long)-1)*8; b < end; shr -= 8, b++) + *b = (uint8_t)(value >> shr); + + if(st->buf) FREEMEM(st->buf); + st->buf = buf; + st->size = 1 + sizeof(value); + + return 0; +} + +int +asn_long2INTEGER(INTEGER_t *st, long value) { + uint8_t *buf, *bp; + uint8_t *p; + uint8_t *pstart; + uint8_t *pend1; + int littleEndian = 1; /* Run-time detection */ + int add; + + if(!st) { + errno = EINVAL; + return -1; + } + + buf = (uint8_t *)MALLOC(sizeof(value)); + if(!buf) return -1; + + if(*(char *)&littleEndian) { + pstart = (uint8_t *)&value + sizeof(value) - 1; + pend1 = (uint8_t *)&value; + add = -1; + } else { + pstart = (uint8_t *)&value; + pend1 = pstart + sizeof(value) - 1; + add = 1; + } + + /* + * If the contents octet consists of more than one octet, + * then bits of the first octet and bit 8 of the second octet: + * a) shall not all be ones; and + * b) shall not all be zero. + */ + for(p = pstart; p != pend1; p += add) { + switch(*p) { + case 0x00: if((*(p+add) & 0x80) == 0) + continue; + break; + case 0xff: if((*(p+add) & 0x80)) + continue; + break; + } + break; + } + /* Copy the integer body */ + for(pstart = p, bp = buf, pend1 += add; p != pend1; p += add) + *bp++ = *p; + + if(st->buf) FREEMEM(st->buf); + st->buf = buf; + st->size = bp - buf; + + return 0; +} + +/* + * This function is going to be DEPRECATED soon. + */ +enum asn_strtol_result_e +asn_strtol(const char *str, const char *end, long *lp) { + const char *endp = end; + + switch(asn_strtol_lim(str, &endp, lp)) { + case ASN_STRTOL_ERROR_RANGE: + return ASN_STRTOL_ERROR_RANGE; + case ASN_STRTOL_ERROR_INVAL: + return ASN_STRTOL_ERROR_INVAL; + case ASN_STRTOL_EXPECT_MORE: + return ASN_STRTOL_ERROR_INVAL; /* Retain old behavior */ + case ASN_STRTOL_OK: + return ASN_STRTOL_OK; + case ASN_STRTOL_EXTRA_DATA: + return ASN_STRTOL_ERROR_INVAL; /* Retain old behavior */ + } + + return ASN_STRTOL_ERROR_INVAL; /* Retain old behavior */ +} + +/* + * Parse the number in the given string until the given *end position, + * returning the position after the last parsed character back using the + * same (*end) pointer. + * WARNING: This behavior is different from the standard strtol(3). + */ +enum asn_strtol_result_e +asn_strtol_lim(const char *str, const char **end, long *lp) { + int sign = 1; + long l; + + const long upper_boundary = LONG_MAX / 10; + long last_digit_max = LONG_MAX % 10; + + if(str >= *end) return ASN_STRTOL_ERROR_INVAL; + + switch(*str) { + case '-': + last_digit_max++; + sign = -1; + /* FALL THROUGH */ + case '+': + str++; + if(str >= *end) { + *end = str; + return ASN_STRTOL_EXPECT_MORE; + } + } + + for(l = 0; str < (*end); str++) { + switch(*str) { + case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: + case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: { + int d = *str - '0'; + if(l < upper_boundary) { + l = l * 10 + d; + } else if(l == upper_boundary) { + if(d <= last_digit_max) { + if(sign > 0) { + l = l * 10 + d; + } else { + sign = 1; + l = -l * 10 - d; + } + } else { + *end = str; + return ASN_STRTOL_ERROR_RANGE; + } + } else { + *end = str; + return ASN_STRTOL_ERROR_RANGE; + } + } + continue; + default: + *end = str; + *lp = sign * l; + return ASN_STRTOL_EXTRA_DATA; + } + } + + *end = str; + *lp = sign * l; + return ASN_STRTOL_OK; +} + diff --git a/src/cryptoconditions/src/asn/INTEGER.h b/src/cryptoconditions/src/asn/INTEGER.h new file mode 100644 index 000000000..9a8809707 --- /dev/null +++ b/src/cryptoconditions/src/asn/INTEGER.h @@ -0,0 +1,82 @@ +/*- + * Copyright (c) 2003, 2005 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _INTEGER_H_ +#define _INTEGER_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef ASN__PRIMITIVE_TYPE_t INTEGER_t; + +extern asn_TYPE_descriptor_t asn_DEF_INTEGER; + +/* Map with to integer value association */ +typedef struct asn_INTEGER_enum_map_s { + long nat_value; /* associated native integer value */ + size_t enum_len; /* strlen("tag") */ + const char *enum_name; /* "tag" */ +} asn_INTEGER_enum_map_t; + +/* This type describes an enumeration for INTEGER and ENUMERATED types */ +typedef const struct asn_INTEGER_specifics_s { + const asn_INTEGER_enum_map_t *value2enum; /* N -> "tag"; sorted by N */ + const unsigned int *enum2value; /* "tag" => N; sorted by tag */ + int map_count; /* Elements in either map */ + int extension; /* This map is extensible */ + int strict_enumeration; /* Enumeration set is fixed */ + int field_width; /* Size of native integer */ + int field_unsigned; /* Signed=0, unsigned=1 */ +} asn_INTEGER_specifics_t; + +asn_struct_print_f INTEGER_print; +ber_type_decoder_f INTEGER_decode_ber; +der_type_encoder_f INTEGER_encode_der; +xer_type_decoder_f INTEGER_decode_xer; +xer_type_encoder_f INTEGER_encode_xer; +per_type_decoder_f INTEGER_decode_uper; +per_type_encoder_f INTEGER_encode_uper; + +/*********************************** + * Some handy conversion routines. * + ***********************************/ + +/* + * Returns 0 if it was possible to convert, -1 otherwise. + * -1/EINVAL: Mandatory argument missing + * -1/ERANGE: Value encoded is out of range for long representation + * -1/ENOMEM: Memory allocation failed (in asn_long2INTEGER()). + */ +int asn_INTEGER2long(const INTEGER_t *i, long *l); +int asn_INTEGER2ulong(const INTEGER_t *i, unsigned long *l); +int asn_long2INTEGER(INTEGER_t *i, long l); +int asn_ulong2INTEGER(INTEGER_t *i, unsigned long l); + +/* A a reified version of strtol(3) with nicer error reporting. */ +enum asn_strtol_result_e { + ASN_STRTOL_ERROR_RANGE = -3, /* Input outside of numeric range for long type */ + ASN_STRTOL_ERROR_INVAL = -2, /* Invalid data encountered (e.g., "+-") */ + ASN_STRTOL_EXPECT_MORE = -1, /* More data expected (e.g. "+") */ + ASN_STRTOL_OK = 0, /* Conversion succeded, number ends at (*end) */ + ASN_STRTOL_EXTRA_DATA = 1 /* Conversion succeded, but the string has extra stuff */ +}; +enum asn_strtol_result_e asn_strtol_lim(const char *str, const char **end, long *l); + +/* The asn_strtol is going to be DEPRECATED soon */ +enum asn_strtol_result_e asn_strtol(const char *str, const char *end, long *l); + +/* + * Convert the integer value into the corresponding enumeration map entry. + */ +const asn_INTEGER_enum_map_t *INTEGER_map_value2enum(asn_INTEGER_specifics_t *specs, long value); + +#ifdef __cplusplus +} +#endif + +#endif /* _INTEGER_H_ */ diff --git a/src/cryptoconditions/src/asn/Makefile.am.sample b/src/cryptoconditions/src/asn/Makefile.am.sample new file mode 100644 index 000000000..9ff904aca --- /dev/null +++ b/src/cryptoconditions/src/asn/Makefile.am.sample @@ -0,0 +1,120 @@ +ASN_MODULE_SOURCES= \ + Condition.c \ + SimpleSha256Condition.c \ + CompoundSha256Condition.c \ + ConditionTypes.c \ + Fulfillment.c \ + PreimageFulfillment.c \ + PrefixFulfillment.c \ + ThresholdFulfillment.c \ + RsaSha256Fulfillment.c \ + Ed25519Sha512Fulfillment.c \ + Secp256k1Fulfillment.c \ + EvalFulfillment.c \ + PrefixFingerprintContents.c \ + ThresholdFingerprintContents.c \ + RsaFingerprintContents.c \ + Ed25519FingerprintContents.c \ + Secp256k1FingerprintContents.c + +ASN_MODULE_HEADERS= \ + Condition.h \ + SimpleSha256Condition.h \ + CompoundSha256Condition.h \ + ConditionTypes.h \ + Fulfillment.h \ + PreimageFulfillment.h \ + PrefixFulfillment.h \ + ThresholdFulfillment.h \ + RsaSha256Fulfillment.h \ + Ed25519Sha512Fulfillment.h \ + Secp256k1Fulfillment.h \ + EvalFulfillment.h \ + PrefixFingerprintContents.h \ + ThresholdFingerprintContents.h \ + RsaFingerprintContents.h \ + Ed25519FingerprintContents.h \ + Secp256k1FingerprintContents.h + +ASN_MODULE_HEADERS+=INTEGER.h +ASN_MODULE_HEADERS+=NativeEnumerated.h +ASN_MODULE_SOURCES+=INTEGER.c +ASN_MODULE_SOURCES+=NativeEnumerated.c +ASN_MODULE_HEADERS+=NativeInteger.h +ASN_MODULE_SOURCES+=NativeInteger.c +ASN_MODULE_HEADERS+=asn_SET_OF.h +ASN_MODULE_SOURCES+=asn_SET_OF.c +ASN_MODULE_HEADERS+=constr_CHOICE.h +ASN_MODULE_SOURCES+=constr_CHOICE.c +ASN_MODULE_HEADERS+=constr_SEQUENCE.h +ASN_MODULE_SOURCES+=constr_SEQUENCE.c +ASN_MODULE_HEADERS+=constr_SET_OF.h +ASN_MODULE_SOURCES+=constr_SET_OF.c +ASN_MODULE_HEADERS+=asn_application.h +ASN_MODULE_HEADERS+=asn_system.h +ASN_MODULE_HEADERS+=asn_codecs.h +ASN_MODULE_HEADERS+=asn_internal.h +ASN_MODULE_HEADERS+=OCTET_STRING.h +ASN_MODULE_SOURCES+=OCTET_STRING.c +ASN_MODULE_HEADERS+=BIT_STRING.h +ASN_MODULE_SOURCES+=BIT_STRING.c +ASN_MODULE_SOURCES+=asn_codecs_prim.c +ASN_MODULE_HEADERS+=asn_codecs_prim.h +ASN_MODULE_HEADERS+=ber_tlv_length.h +ASN_MODULE_SOURCES+=ber_tlv_length.c +ASN_MODULE_HEADERS+=ber_tlv_tag.h +ASN_MODULE_SOURCES+=ber_tlv_tag.c +ASN_MODULE_HEADERS+=ber_decoder.h +ASN_MODULE_SOURCES+=ber_decoder.c +ASN_MODULE_HEADERS+=der_encoder.h +ASN_MODULE_SOURCES+=der_encoder.c +ASN_MODULE_HEADERS+=constr_TYPE.h +ASN_MODULE_SOURCES+=constr_TYPE.c +ASN_MODULE_HEADERS+=constraints.h +ASN_MODULE_SOURCES+=constraints.c +ASN_MODULE_HEADERS+=xer_support.h +ASN_MODULE_SOURCES+=xer_support.c +ASN_MODULE_HEADERS+=xer_decoder.h +ASN_MODULE_SOURCES+=xer_decoder.c +ASN_MODULE_HEADERS+=xer_encoder.h +ASN_MODULE_SOURCES+=xer_encoder.c +ASN_MODULE_HEADERS+=per_support.h +ASN_MODULE_SOURCES+=per_support.c +ASN_MODULE_HEADERS+=per_decoder.h +ASN_MODULE_SOURCES+=per_decoder.c +ASN_MODULE_HEADERS+=per_encoder.h +ASN_MODULE_SOURCES+=per_encoder.c +ASN_MODULE_HEADERS+=per_opentype.h +ASN_MODULE_SOURCES+=per_opentype.c +ASN_CONVERTER_SOURCES+=converter-sample.c + + +lib_LTLIBRARIES=libsomething.la +libsomething_la_SOURCES=$(ASN_MODULE_SOURCES) $(ASN_MODULE_HEADERS) + +# This file may be used as an input for make(3) +# Remove the lines below to convert it into a pure .am file +TARGET = progname +CFLAGS += -I. +OBJS=${ASN_MODULE_SOURCES:.c=.o} ${ASN_CONVERTER_SOURCES:.c=.o} + +all: $(TARGET) + +$(TARGET): ${OBJS} + $(CC) $(CFLAGS) -o $(TARGET) ${OBJS} $(LDFLAGS) $(LIBS) + +.SUFFIXES: +.SUFFIXES: .c .o + +.c.o: + $(CC) $(CFLAGS) -o $@ -c $< + +clean: + rm -f $(TARGET) + rm -f $(OBJS) + +regen: regenerate-from-asn1-source + +regenerate-from-asn1-source: + asn1c CryptoConditions.asn + diff --git a/src/cryptoconditions/src/asn/NativeEnumerated.c b/src/cryptoconditions/src/asn/NativeEnumerated.c new file mode 100644 index 000000000..78366af31 --- /dev/null +++ b/src/cryptoconditions/src/asn/NativeEnumerated.c @@ -0,0 +1,207 @@ +/*- + * Copyright (c) 2004, 2007 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +/* + * Read the NativeInteger.h for the explanation wrt. differences between + * INTEGER and NativeInteger. + * Basically, both are decoders and encoders of ASN.1 INTEGER type, but this + * implementation deals with the standard (machine-specific) representation + * of them instead of using the platform-independent buffer. + */ +#include +#include + +/* + * NativeEnumerated basic type description. + */ +static const ber_tlv_tag_t asn_DEF_NativeEnumerated_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (10 << 2)) +}; +asn_TYPE_descriptor_t asn_DEF_NativeEnumerated = { + "ENUMERATED", /* The ASN.1 type is still ENUMERATED */ + "ENUMERATED", + NativeInteger_free, + NativeInteger_print, + asn_generic_no_constraint, + NativeInteger_decode_ber, + NativeInteger_encode_der, + NativeInteger_decode_xer, + NativeEnumerated_encode_xer, + NativeEnumerated_decode_uper, + NativeEnumerated_encode_uper, + 0, /* Use generic outmost tag fetcher */ + asn_DEF_NativeEnumerated_tags, + sizeof(asn_DEF_NativeEnumerated_tags) / sizeof(asn_DEF_NativeEnumerated_tags[0]), + asn_DEF_NativeEnumerated_tags, /* Same as above */ + sizeof(asn_DEF_NativeEnumerated_tags) / sizeof(asn_DEF_NativeEnumerated_tags[0]), + 0, /* No PER visible constraints */ + 0, 0, /* No members */ + 0 /* No specifics */ +}; + +asn_enc_rval_t +NativeEnumerated_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; + asn_enc_rval_t er; + const long *native = (const long *)sptr; + const asn_INTEGER_enum_map_t *el; + + (void)ilevel; + (void)flags; + + if(!native) ASN__ENCODE_FAILED; + + el = INTEGER_map_value2enum(specs, *native); + if(el) { + size_t srcsize = el->enum_len + 5; + char *src = (char *)alloca(srcsize); + + er.encoded = snprintf(src, srcsize, "<%s/>", el->enum_name); + assert(er.encoded > 0 && (size_t)er.encoded < srcsize); + if(cb(src, er.encoded, app_key) < 0) ASN__ENCODE_FAILED; + ASN__ENCODED_OK(er); + } else { + ASN_DEBUG("ASN.1 forbids dealing with " + "unknown value of ENUMERATED type"); + ASN__ENCODE_FAILED; + } +} + +asn_dec_rval_t +NativeEnumerated_decode_uper(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, + void **sptr, asn_per_data_t *pd) { + asn_INTEGER_specifics_t *specs = (asn_INTEGER_specifics_t *)td->specifics; + asn_dec_rval_t rval = { RC_OK, 0 }; + long *native = (long *)*sptr; + asn_per_constraint_t *ct; + long value; + + (void)opt_codec_ctx; + + if(constraints) ct = &constraints->value; + else if(td->per_constraints) ct = &td->per_constraints->value; + else ASN__DECODE_FAILED; /* Mandatory! */ + if(!specs) ASN__DECODE_FAILED; + + if(!native) { + native = (long *)(*sptr = CALLOC(1, sizeof(*native))); + if(!native) ASN__DECODE_FAILED; + } + + ASN_DEBUG("Decoding %s as NativeEnumerated", td->name); + + if(ct->flags & APC_EXTENSIBLE) { + int inext = per_get_few_bits(pd, 1); + if(inext < 0) ASN__DECODE_STARVED; + if(inext) ct = 0; + } + + if(ct && ct->range_bits >= 0) { + value = per_get_few_bits(pd, ct->range_bits); + if(value < 0) ASN__DECODE_STARVED; + if(value >= (specs->extension + ? specs->extension - 1 : specs->map_count)) + ASN__DECODE_FAILED; + } else { + if(!specs->extension) + ASN__DECODE_FAILED; + /* + * X.691, #10.6: normally small non-negative whole number; + */ + value = uper_get_nsnnwn(pd); + if(value < 0) ASN__DECODE_STARVED; + value += specs->extension - 1; + if(value >= specs->map_count) + ASN__DECODE_FAILED; + } + + *native = specs->value2enum[value].nat_value; + ASN_DEBUG("Decoded %s = %ld", td->name, *native); + + return rval; +} + +static int +NativeEnumerated__compar_value2enum(const void *ap, const void *bp) { + const asn_INTEGER_enum_map_t *a = ap; + const asn_INTEGER_enum_map_t *b = bp; + if(a->nat_value == b->nat_value) + return 0; + if(a->nat_value < b->nat_value) + return -1; + return 1; +} + +asn_enc_rval_t +NativeEnumerated_encode_uper(asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) { + asn_INTEGER_specifics_t *specs = (asn_INTEGER_specifics_t *)td->specifics; + asn_enc_rval_t er; + long native, value; + asn_per_constraint_t *ct; + int inext = 0; + asn_INTEGER_enum_map_t key; + const asn_INTEGER_enum_map_t *kf; + + if(!sptr) ASN__ENCODE_FAILED; + if(!specs) ASN__ENCODE_FAILED; + + if(constraints) ct = &constraints->value; + else if(td->per_constraints) ct = &td->per_constraints->value; + else ASN__ENCODE_FAILED; /* Mandatory! */ + + ASN_DEBUG("Encoding %s as NativeEnumerated", td->name); + + er.encoded = 0; + + native = *(long *)sptr; + if(native < 0) ASN__ENCODE_FAILED; + + key.nat_value = native; + kf = bsearch(&key, specs->value2enum, specs->map_count, + sizeof(key), NativeEnumerated__compar_value2enum); + if(!kf) { + ASN_DEBUG("No element corresponds to %ld", native); + ASN__ENCODE_FAILED; + } + value = kf - specs->value2enum; + + if(ct->range_bits >= 0) { + int cmpWith = specs->extension + ? specs->extension - 1 : specs->map_count; + if(value >= cmpWith) + inext = 1; + } + if(ct->flags & APC_EXTENSIBLE) { + if(per_put_few_bits(po, inext, 1)) + ASN__ENCODE_FAILED; + if(inext) ct = 0; + } else if(inext) { + ASN__ENCODE_FAILED; + } + + if(ct && ct->range_bits >= 0) { + if(per_put_few_bits(po, value, ct->range_bits)) + ASN__ENCODE_FAILED; + ASN__ENCODED_OK(er); + } + + if(!specs->extension) + ASN__ENCODE_FAILED; + + /* + * X.691, #10.6: normally small non-negative whole number; + */ + ASN_DEBUG("value = %ld, ext = %d, inext = %d, res = %ld", + value, specs->extension, inext, + value - (inext ? (specs->extension - 1) : 0)); + if(uper_put_nsnnwn(po, value - (inext ? (specs->extension - 1) : 0))) + ASN__ENCODE_FAILED; + + ASN__ENCODED_OK(er); +} + diff --git a/src/cryptoconditions/src/asn/NativeEnumerated.h b/src/cryptoconditions/src/asn/NativeEnumerated.h new file mode 100644 index 000000000..c59bb1ba9 --- /dev/null +++ b/src/cryptoconditions/src/asn/NativeEnumerated.h @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 2004, 2005, 2006 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +/* + * This type differs from the standard ENUMERATED in that it is modelled using + * the fixed machine type (long, int, short), so it can hold only values of + * limited length. There is no type (i.e., NativeEnumerated_t, any integer type + * will do). + * This type may be used when integer range is limited by subtype constraints. + */ +#ifndef _NativeEnumerated_H_ +#define _NativeEnumerated_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern asn_TYPE_descriptor_t asn_DEF_NativeEnumerated; + +xer_type_encoder_f NativeEnumerated_encode_xer; +per_type_decoder_f NativeEnumerated_decode_uper; +per_type_encoder_f NativeEnumerated_encode_uper; + +#ifdef __cplusplus +} +#endif + +#endif /* _NativeEnumerated_H_ */ diff --git a/src/cryptoconditions/src/asn/NativeInteger.c b/src/cryptoconditions/src/asn/NativeInteger.c new file mode 100644 index 000000000..e8ce6d2c3 --- /dev/null +++ b/src/cryptoconditions/src/asn/NativeInteger.c @@ -0,0 +1,332 @@ +/*- + * Copyright (c) 2004, 2005, 2006 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +/* + * Read the NativeInteger.h for the explanation wrt. differences between + * INTEGER and NativeInteger. + * Basically, both are decoders and encoders of ASN.1 INTEGER type, but this + * implementation deals with the standard (machine-specific) representation + * of them instead of using the platform-independent buffer. + */ +#include +#include + +/* + * NativeInteger basic type description. + */ +static const ber_tlv_tag_t asn_DEF_NativeInteger_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (2 << 2)) +}; +asn_TYPE_descriptor_t asn_DEF_NativeInteger = { + "INTEGER", /* The ASN.1 type is still INTEGER */ + "INTEGER", + NativeInteger_free, + NativeInteger_print, + asn_generic_no_constraint, + NativeInteger_decode_ber, + NativeInteger_encode_der, + NativeInteger_decode_xer, + NativeInteger_encode_xer, + NativeInteger_decode_uper, /* Unaligned PER decoder */ + NativeInteger_encode_uper, /* Unaligned PER encoder */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_NativeInteger_tags, + sizeof(asn_DEF_NativeInteger_tags) / sizeof(asn_DEF_NativeInteger_tags[0]), + asn_DEF_NativeInteger_tags, /* Same as above */ + sizeof(asn_DEF_NativeInteger_tags) / sizeof(asn_DEF_NativeInteger_tags[0]), + 0, /* No PER visible constraints */ + 0, 0, /* No members */ + 0 /* No specifics */ +}; + +/* + * Decode INTEGER type. + */ +asn_dec_rval_t +NativeInteger_decode_ber(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, + void **nint_ptr, const void *buf_ptr, size_t size, int tag_mode) { + asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; + long *native = (long *)*nint_ptr; + asn_dec_rval_t rval; + ber_tlv_len_t length; + + /* + * If the structure is not there, allocate it. + */ + if(native == NULL) { + native = (long *)(*nint_ptr = CALLOC(1, sizeof(*native))); + if(native == NULL) { + rval.code = RC_FAIL; + rval.consumed = 0; + return rval; + } + } + + ASN_DEBUG("Decoding %s as INTEGER (tm=%d)", + td->name, tag_mode); + + /* + * Check tags. + */ + rval = ber_check_tags(opt_codec_ctx, td, 0, buf_ptr, size, + tag_mode, 0, &length, 0); + if(rval.code != RC_OK) + return rval; + + ASN_DEBUG("%s length is %d bytes", td->name, (int)length); + + /* + * Make sure we have this length. + */ + buf_ptr = ((const char *)buf_ptr) + rval.consumed; + size -= rval.consumed; + if(length > (ber_tlv_len_t)size) { + rval.code = RC_WMORE; + rval.consumed = 0; + return rval; + } + + /* + * ASN.1 encoded INTEGER: buf_ptr, length + * Fill the native, at the same time checking for overflow. + * If overflow occured, return with RC_FAIL. + */ + { + INTEGER_t tmp; + union { + const void *constbuf; + void *nonconstbuf; + } unconst_buf; + long l; + + unconst_buf.constbuf = buf_ptr; + tmp.buf = (uint8_t *)unconst_buf.nonconstbuf; + tmp.size = length; + + if((specs&&specs->field_unsigned) + ? asn_INTEGER2ulong(&tmp, (unsigned long *)&l) /* sic */ + : asn_INTEGER2long(&tmp, &l)) { + rval.code = RC_FAIL; + rval.consumed = 0; + return rval; + } + + *native = l; + } + + rval.code = RC_OK; + rval.consumed += length; + + ASN_DEBUG("Took %ld/%ld bytes to encode %s (%ld)", + (long)rval.consumed, (long)length, td->name, (long)*native); + + return rval; +} + +/* + * Encode the NativeInteger using the standard INTEGER type DER encoder. + */ +asn_enc_rval_t +NativeInteger_encode_der(asn_TYPE_descriptor_t *sd, void *ptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + unsigned long native = *(unsigned long *)ptr; /* Disable sign ext. */ + asn_enc_rval_t erval; + INTEGER_t tmp; + +#ifdef WORDS_BIGENDIAN /* Opportunistic optimization */ + + tmp.buf = (uint8_t *)&native; + tmp.size = sizeof(native); + +#else /* Works even if WORDS_BIGENDIAN is not set where should've been */ + uint8_t buf[sizeof(native)]; + uint8_t *p; + + /* Prepare a fake INTEGER */ + for(p = buf + sizeof(buf) - 1; p >= buf; p--, native >>= 8) + *p = (uint8_t)native; + + tmp.buf = buf; + tmp.size = sizeof(buf); +#endif /* WORDS_BIGENDIAN */ + + /* Encode fake INTEGER */ + erval = INTEGER_encode_der(sd, &tmp, tag_mode, tag, cb, app_key); + if(erval.encoded == -1) { + assert(erval.structure_ptr == &tmp); + erval.structure_ptr = ptr; + } + return erval; +} + +/* + * Decode the chunk of XML text encoding INTEGER. + */ +asn_dec_rval_t +NativeInteger_decode_xer(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname, + const void *buf_ptr, size_t size) { + asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; + asn_dec_rval_t rval; + INTEGER_t st; + void *st_ptr = (void *)&st; + long *native = (long *)*sptr; + + if(!native) { + native = (long *)(*sptr = CALLOC(1, sizeof(*native))); + if(!native) ASN__DECODE_FAILED; + } + + memset(&st, 0, sizeof(st)); + rval = INTEGER_decode_xer(opt_codec_ctx, td, &st_ptr, + opt_mname, buf_ptr, size); + if(rval.code == RC_OK) { + long l; + if((specs&&specs->field_unsigned) + ? asn_INTEGER2ulong(&st, (unsigned long *)&l) /* sic */ + : asn_INTEGER2long(&st, &l)) { + rval.code = RC_FAIL; + rval.consumed = 0; + } else { + *native = l; + } + } else { + /* + * Cannot restart from the middle; + * there is no place to save state in the native type. + * Request a continuation from the very beginning. + */ + rval.consumed = 0; + } + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_INTEGER, &st); + return rval; +} + + +asn_enc_rval_t +NativeInteger_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; + char scratch[32]; /* Enough for 64-bit int */ + asn_enc_rval_t er; + const long *native = (const long *)sptr; + + (void)ilevel; + (void)flags; + + if(!native) ASN__ENCODE_FAILED; + + er.encoded = snprintf(scratch, sizeof(scratch), + (specs && specs->field_unsigned) + ? "%lu" : "%ld", *native); + if(er.encoded <= 0 || (size_t)er.encoded >= sizeof(scratch) + || cb(scratch, er.encoded, app_key) < 0) + ASN__ENCODE_FAILED; + + ASN__ENCODED_OK(er); +} + +asn_dec_rval_t +NativeInteger_decode_uper(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) { + + asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; + asn_dec_rval_t rval; + long *native = (long *)*sptr; + INTEGER_t tmpint; + void *tmpintptr = &tmpint; + + (void)opt_codec_ctx; + ASN_DEBUG("Decoding NativeInteger %s (UPER)", td->name); + + if(!native) { + native = (long *)(*sptr = CALLOC(1, sizeof(*native))); + if(!native) ASN__DECODE_FAILED; + } + + memset(&tmpint, 0, sizeof tmpint); + rval = INTEGER_decode_uper(opt_codec_ctx, td, constraints, + &tmpintptr, pd); + if(rval.code == RC_OK) { + if((specs&&specs->field_unsigned) + ? asn_INTEGER2ulong(&tmpint, (unsigned long *)native) + : asn_INTEGER2long(&tmpint, native)) + rval.code = RC_FAIL; + else + ASN_DEBUG("NativeInteger %s got value %ld", + td->name, *native); + } + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_INTEGER, &tmpint); + + return rval; +} + +asn_enc_rval_t +NativeInteger_encode_uper(asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) { + asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; + asn_enc_rval_t er; + long native; + INTEGER_t tmpint; + + if(!sptr) ASN__ENCODE_FAILED; + + native = *(long *)sptr; + + ASN_DEBUG("Encoding NativeInteger %s %ld (UPER)", td->name, native); + + memset(&tmpint, 0, sizeof(tmpint)); + if((specs&&specs->field_unsigned) + ? asn_ulong2INTEGER(&tmpint, native) + : asn_long2INTEGER(&tmpint, native)) + ASN__ENCODE_FAILED; + er = INTEGER_encode_uper(td, constraints, &tmpint, po); + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_INTEGER, &tmpint); + return er; +} + +/* + * INTEGER specific human-readable output. + */ +int +NativeInteger_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; + const long *native = (const long *)sptr; + char scratch[32]; /* Enough for 64-bit int */ + int ret; + + (void)td; /* Unused argument */ + (void)ilevel; /* Unused argument */ + + if(native) { + ret = snprintf(scratch, sizeof(scratch), + (specs && specs->field_unsigned) + ? "%lu" : "%ld", *native); + assert(ret > 0 && (size_t)ret < sizeof(scratch)); + return (cb(scratch, ret, app_key) < 0) ? -1 : 0; + } else { + return (cb("", 8, app_key) < 0) ? -1 : 0; + } +} + +void +NativeInteger_free(asn_TYPE_descriptor_t *td, void *ptr, int contents_only) { + + if(!td || !ptr) + return; + + ASN_DEBUG("Freeing %s as INTEGER (%d, %p, Native)", + td->name, contents_only, ptr); + + if(!contents_only) { + FREEMEM(ptr); + } +} + diff --git a/src/cryptoconditions/src/asn/NativeInteger.h b/src/cryptoconditions/src/asn/NativeInteger.h new file mode 100644 index 000000000..4e63a8355 --- /dev/null +++ b/src/cryptoconditions/src/asn/NativeInteger.h @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +/* + * This type differs from the standard INTEGER in that it is modelled using + * the fixed machine type (long, int, short), so it can hold only values of + * limited length. There is no type (i.e., NativeInteger_t, any integer type + * will do). + * This type may be used when integer range is limited by subtype constraints. + */ +#ifndef _NativeInteger_H_ +#define _NativeInteger_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern asn_TYPE_descriptor_t asn_DEF_NativeInteger; + +asn_struct_free_f NativeInteger_free; +asn_struct_print_f NativeInteger_print; +ber_type_decoder_f NativeInteger_decode_ber; +der_type_encoder_f NativeInteger_encode_der; +xer_type_decoder_f NativeInteger_decode_xer; +xer_type_encoder_f NativeInteger_encode_xer; +per_type_decoder_f NativeInteger_decode_uper; +per_type_encoder_f NativeInteger_encode_uper; + +#ifdef __cplusplus +} +#endif + +#endif /* _NativeInteger_H_ */ diff --git a/src/cryptoconditions/src/asn/OCTET_STRING.c b/src/cryptoconditions/src/asn/OCTET_STRING.c new file mode 100644 index 000000000..5420dedec --- /dev/null +++ b/src/cryptoconditions/src/asn/OCTET_STRING.c @@ -0,0 +1,1807 @@ +/*- + * Copyright (c) 2003, 2004, 2005, 2006 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include /* for .bits_unused member */ +#include + +/* + * OCTET STRING basic type description. + */ +static const ber_tlv_tag_t asn_DEF_OCTET_STRING_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (4 << 2)) +}; +static const asn_OCTET_STRING_specifics_t asn_DEF_OCTET_STRING_specs = { + sizeof(OCTET_STRING_t), + offsetof(OCTET_STRING_t, _asn_ctx), + ASN_OSUBV_STR +}; +static const asn_per_constraints_t asn_DEF_OCTET_STRING_constraints = { + { APC_CONSTRAINED, 8, 8, 0, 255 }, + { APC_SEMI_CONSTRAINED, -1, -1, 0, 0 }, + 0, 0 +}; +asn_TYPE_descriptor_t asn_DEF_OCTET_STRING = { + "OCTET STRING", /* Canonical name */ + "OCTET_STRING", /* XML tag name */ + OCTET_STRING_free, + OCTET_STRING_print, /* non-ascii stuff, generally */ + asn_generic_no_constraint, + OCTET_STRING_decode_ber, + OCTET_STRING_encode_der, + OCTET_STRING_decode_xer_hex, + OCTET_STRING_encode_xer, + OCTET_STRING_decode_uper, /* Unaligned PER decoder */ + OCTET_STRING_encode_uper, /* Unaligned PER encoder */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_OCTET_STRING_tags, + sizeof(asn_DEF_OCTET_STRING_tags) + / sizeof(asn_DEF_OCTET_STRING_tags[0]), + asn_DEF_OCTET_STRING_tags, /* Same as above */ + sizeof(asn_DEF_OCTET_STRING_tags) + / sizeof(asn_DEF_OCTET_STRING_tags[0]), + 0, /* No PER visible constraints */ + 0, 0, /* No members */ + &asn_DEF_OCTET_STRING_specs +}; + +#undef _CH_PHASE +#undef NEXT_PHASE +#undef PREV_PHASE +#define _CH_PHASE(ctx, inc) do { \ + if(ctx->phase == 0) \ + ctx->context = 0; \ + ctx->phase += inc; \ + } while(0) +#define NEXT_PHASE(ctx) _CH_PHASE(ctx, +1) +#define PREV_PHASE(ctx) _CH_PHASE(ctx, -1) + +#undef ADVANCE +#define ADVANCE(num_bytes) do { \ + size_t num = (num_bytes); \ + buf_ptr = ((const char *)buf_ptr) + num; \ + size -= num; \ + consumed_myself += num; \ + } while(0) + +#undef RETURN +#define RETURN(_code) do { \ + asn_dec_rval_t tmprval; \ + tmprval.code = _code; \ + tmprval.consumed = consumed_myself; \ + return tmprval; \ + } while(0) + +#undef APPEND +#define APPEND(bufptr, bufsize) do { \ + size_t _bs = (bufsize); /* Append size */ \ + size_t _ns = ctx->context; /* Allocated now */ \ + size_t _es = st->size + _bs; /* Expected size */ \ + /* int is really a typeof(st->size): */ \ + if((int)_es < 0) RETURN(RC_FAIL); \ + if(_ns <= _es) { \ + void *ptr; \ + /* Be nice and round to the memory allocator */ \ + do { _ns = _ns ? _ns << 1 : 16; } \ + while(_ns <= _es); \ + /* int is really a typeof(st->size): */ \ + if((int)_ns < 0) RETURN(RC_FAIL); \ + ptr = REALLOC(st->buf, _ns); \ + if(ptr) { \ + st->buf = (uint8_t *)ptr; \ + ctx->context = _ns; \ + } else { \ + RETURN(RC_FAIL); \ + } \ + ASN_DEBUG("Reallocating into %ld", (long)_ns); \ + } \ + memcpy(st->buf + st->size, bufptr, _bs); \ + /* Convenient nul-termination */ \ + st->buf[_es] = '\0'; \ + st->size = _es; \ + } while(0) + +/* + * The main reason why ASN.1 is still alive is that too much time and effort + * is necessary for learning it more or less adequately, thus creating a gut + * necessity to demonstrate that aquired skill everywhere afterwards. + * No, I am not going to explain what the following stuff is. + */ +struct _stack_el { + ber_tlv_len_t left; /* What's left to read (or -1) */ + ber_tlv_len_t got; /* What was actually processed */ + int cont_level; /* Depth of subcontainment */ + int want_nulls; /* Want null "end of content" octets? */ + int bits_chopped; /* Flag in BIT STRING mode */ + ber_tlv_tag_t tag; /* For debugging purposes */ + struct _stack_el *prev; + struct _stack_el *next; +}; +struct _stack { + struct _stack_el *tail; + struct _stack_el *cur_ptr; +}; + +static struct _stack_el * +OS__add_stack_el(struct _stack *st) { + struct _stack_el *nel; + + /* + * Reuse the old stack frame or allocate a new one. + */ + if(st->cur_ptr && st->cur_ptr->next) { + nel = st->cur_ptr->next; + nel->bits_chopped = 0; + nel->got = 0; + /* Retain the nel->cont_level, it's correct. */ + } else { + nel = (struct _stack_el *)CALLOC(1, sizeof(struct _stack_el)); + if(nel == NULL) + return NULL; + + if(st->tail) { + /* Increase a subcontainment depth */ + nel->cont_level = st->tail->cont_level + 1; + st->tail->next = nel; + } + nel->prev = st->tail; + st->tail = nel; + } + + st->cur_ptr = nel; + + return nel; +} + +static struct _stack * +_new_stack() { + return (struct _stack *)CALLOC(1, sizeof(struct _stack)); +} + +/* + * Decode OCTET STRING type. + */ +asn_dec_rval_t +OCTET_STRING_decode_ber(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, + void **sptr, const void *buf_ptr, size_t size, int tag_mode) { + asn_OCTET_STRING_specifics_t *specs = td->specifics + ? (asn_OCTET_STRING_specifics_t *)td->specifics + : &asn_DEF_OCTET_STRING_specs; + BIT_STRING_t *st = (BIT_STRING_t *)*sptr; + asn_dec_rval_t rval; + asn_struct_ctx_t *ctx; + ssize_t consumed_myself = 0; + struct _stack *stck; /* Expectations stack structure */ + struct _stack_el *sel = 0; /* Stack element */ + int tlv_constr; + enum asn_OS_Subvariant type_variant = specs->subvariant; + + ASN_DEBUG("Decoding %s as %s (frame %ld)", + td->name, + (type_variant == ASN_OSUBV_STR) ? + "OCTET STRING" : "OS-SpecialCase", + (long)size); + + /* + * Create the string if does not exist. + */ + if(st == NULL) { + st = (BIT_STRING_t *)(*sptr = CALLOC(1, specs->struct_size)); + if(st == NULL) RETURN(RC_FAIL); + } + + /* Restore parsing context */ + ctx = (asn_struct_ctx_t *)((char *)st + specs->ctx_offset); + + switch(ctx->phase) { + case 0: + /* + * Check tags. + */ + rval = ber_check_tags(opt_codec_ctx, td, ctx, + buf_ptr, size, tag_mode, -1, + &ctx->left, &tlv_constr); + if(rval.code != RC_OK) + return rval; + + if(tlv_constr) { + /* + * Complex operation, requires stack of expectations. + */ + ctx->ptr = _new_stack(); + if(ctx->ptr) { + stck = (struct _stack *)ctx->ptr; + } else { + RETURN(RC_FAIL); + } + } else { + /* + * Jump into stackless primitive decoding. + */ + _CH_PHASE(ctx, 3); + if(type_variant == ASN_OSUBV_ANY && tag_mode != 1) + APPEND(buf_ptr, rval.consumed); + ADVANCE(rval.consumed); + goto phase3; + } + + NEXT_PHASE(ctx); + /* Fall through */ + case 1: + phase1: + /* + * Fill the stack with expectations. + */ + stck = (struct _stack *)ctx->ptr; + sel = stck->cur_ptr; + do { + ber_tlv_tag_t tlv_tag; + ber_tlv_len_t tlv_len; + ber_tlv_tag_t expected_tag; + ssize_t tl, ll, tlvl; + /* This one works even if (sel->left == -1) */ + ssize_t Left = ((!sel||(size_t)sel->left >= size) + ?(ssize_t)size:sel->left); + + + ASN_DEBUG("%p, s->l=%ld, s->wn=%ld, s->g=%ld\n", sel, + (long)(sel?sel->left:0), + (long)(sel?sel->want_nulls:0), + (long)(sel?sel->got:0) + ); + if(sel && sel->left <= 0 && sel->want_nulls == 0) { + if(sel->prev) { + struct _stack_el *prev = sel->prev; + if(prev->left != -1) { + if(prev->left < sel->got) + RETURN(RC_FAIL); + prev->left -= sel->got; + } + prev->got += sel->got; + sel = stck->cur_ptr = prev; + if(!sel) break; + tlv_constr = 1; + continue; + } else { + sel = stck->cur_ptr = 0; + break; /* Nothing to wait */ + } + } + + tl = ber_fetch_tag(buf_ptr, Left, &tlv_tag); + ASN_DEBUG("fetch tag(size=%ld,L=%ld), %sstack, left=%ld, wn=%ld, tl=%ld", + (long)size, (long)Left, sel?"":"!", + (long)(sel?sel->left:0), + (long)(sel?sel->want_nulls:0), + (long)tl); + switch(tl) { + case -1: RETURN(RC_FAIL); + case 0: RETURN(RC_WMORE); + } + + tlv_constr = BER_TLV_CONSTRUCTED(buf_ptr); + + ll = ber_fetch_length(tlv_constr, + (const char *)buf_ptr + tl,Left - tl,&tlv_len); + ASN_DEBUG("Got tag=%s, tc=%d, left=%ld, tl=%ld, len=%ld, ll=%ld", + ber_tlv_tag_string(tlv_tag), tlv_constr, + (long)Left, (long)tl, (long)tlv_len, (long)ll); + switch(ll) { + case -1: RETURN(RC_FAIL); + case 0: RETURN(RC_WMORE); + } + + if(sel && sel->want_nulls + && ((const uint8_t *)buf_ptr)[0] == 0 + && ((const uint8_t *)buf_ptr)[1] == 0) + { + + ASN_DEBUG("Eat EOC; wn=%d--", sel->want_nulls); + + if(type_variant == ASN_OSUBV_ANY + && (tag_mode != 1 || sel->cont_level)) + APPEND("\0\0", 2); + + ADVANCE(2); + sel->got += 2; + if(sel->left != -1) { + sel->left -= 2; /* assert(sel->left >= 2) */ + } + + sel->want_nulls--; + if(sel->want_nulls == 0) { + /* Move to the next expectation */ + sel->left = 0; + tlv_constr = 1; + } + + continue; + } + + /* + * Set up expected tags, + * depending on ASN.1 type being decoded. + */ + switch(type_variant) { + case ASN_OSUBV_BIT: + /* X.690: 8.6.4.1, NOTE 2 */ + /* Fall through */ + case ASN_OSUBV_STR: + default: + if(sel) { + int level = sel->cont_level; + if(level < td->all_tags_count) { + expected_tag = td->all_tags[level]; + break; + } else if(td->all_tags_count) { + expected_tag = td->all_tags + [td->all_tags_count - 1]; + break; + } + /* else, Fall through */ + } + /* Fall through */ + case ASN_OSUBV_ANY: + expected_tag = tlv_tag; + break; + } + + + if(tlv_tag != expected_tag) { + char buf[2][32]; + ber_tlv_tag_snprint(tlv_tag, + buf[0], sizeof(buf[0])); + ber_tlv_tag_snprint(td->tags[td->tags_count-1], + buf[1], sizeof(buf[1])); + ASN_DEBUG("Tag does not match expectation: %s != %s", + buf[0], buf[1]); + RETURN(RC_FAIL); + } + + tlvl = tl + ll; /* Combined length of T and L encoding */ + if((tlv_len + tlvl) < 0) { + /* tlv_len value is too big */ + ASN_DEBUG("TLV encoding + length (%ld) is too big", + (long)tlv_len); + RETURN(RC_FAIL); + } + + /* + * Append a new expectation. + */ + sel = OS__add_stack_el(stck); + if(!sel) RETURN(RC_FAIL); + + sel->tag = tlv_tag; + + sel->want_nulls = (tlv_len==-1); + if(sel->prev && sel->prev->left != -1) { + /* Check that the parent frame is big enough */ + if(sel->prev->left < tlvl + (tlv_len==-1?0:tlv_len)) + RETURN(RC_FAIL); + if(tlv_len == -1) + sel->left = sel->prev->left - tlvl; + else + sel->left = tlv_len; + } else { + sel->left = tlv_len; + } + if(type_variant == ASN_OSUBV_ANY + && (tag_mode != 1 || sel->cont_level)) + APPEND(buf_ptr, tlvl); + sel->got += tlvl; + ADVANCE(tlvl); + + ASN_DEBUG("+EXPECT2 got=%ld left=%ld, wn=%d, clvl=%d", + (long)sel->got, (long)sel->left, + sel->want_nulls, sel->cont_level); + + } while(tlv_constr); + if(sel == NULL) { + /* Finished operation, "phase out" */ + ASN_DEBUG("Phase out"); + _CH_PHASE(ctx, +3); + break; + } + + NEXT_PHASE(ctx); + /* Fall through */ + case 2: + stck = (struct _stack *)ctx->ptr; + sel = stck->cur_ptr; + ASN_DEBUG("Phase 2: Need %ld bytes, size=%ld, alrg=%ld, wn=%d", + (long)sel->left, (long)size, (long)sel->got, + sel->want_nulls); + { + ber_tlv_len_t len; + + assert(sel->left >= 0); + + len = ((ber_tlv_len_t)size < sel->left) + ? (ber_tlv_len_t)size : sel->left; + if(len > 0) { + if(type_variant == ASN_OSUBV_BIT + && sel->bits_chopped == 0) { + /* Put the unused-bits-octet away */ + st->bits_unused = *(const uint8_t *)buf_ptr; + APPEND(((const char *)buf_ptr+1), (len - 1)); + sel->bits_chopped = 1; + } else { + APPEND(buf_ptr, len); + } + ADVANCE(len); + sel->left -= len; + sel->got += len; + } + + if(sel->left) { + ASN_DEBUG("OS left %ld, size = %ld, wn=%d\n", + (long)sel->left, (long)size, sel->want_nulls); + RETURN(RC_WMORE); + } + + PREV_PHASE(ctx); + goto phase1; + } + break; + case 3: + phase3: + /* + * Primitive form, no stack required. + */ + assert(ctx->left >= 0); + + if(size < (size_t)ctx->left) { + if(!size) RETURN(RC_WMORE); + if(type_variant == ASN_OSUBV_BIT && !ctx->context) { + st->bits_unused = *(const uint8_t *)buf_ptr; + ctx->left--; + ADVANCE(1); + } + APPEND(buf_ptr, size); + assert(ctx->context > 0); + ctx->left -= size; + ADVANCE(size); + RETURN(RC_WMORE); + } else { + if(type_variant == ASN_OSUBV_BIT + && !ctx->context && ctx->left) { + st->bits_unused = *(const uint8_t *)buf_ptr; + ctx->left--; + ADVANCE(1); + } + APPEND(buf_ptr, ctx->left); + ADVANCE(ctx->left); + ctx->left = 0; + + NEXT_PHASE(ctx); + } + break; + } + + if(sel) { + ASN_DEBUG("3sel p=%p, wn=%d, l=%ld, g=%ld, size=%ld", + sel->prev, sel->want_nulls, + (long)sel->left, (long)sel->got, (long)size); + if(sel->prev || sel->want_nulls > 1 || sel->left > 0) { + RETURN(RC_WMORE); + } + } + + /* + * BIT STRING-specific processing. + */ + if(type_variant == ASN_OSUBV_BIT && st->size) { + /* Finalize BIT STRING: zero out unused bits. */ + st->buf[st->size-1] &= 0xff << st->bits_unused; + } + + ASN_DEBUG("Took %ld bytes to encode %s: [%s]:%ld", + (long)consumed_myself, td->name, + (type_variant == ASN_OSUBV_STR) ? (char *)st->buf : "", + (long)st->size); + + + RETURN(RC_OK); +} + +/* + * Encode OCTET STRING type using DER. + */ +asn_enc_rval_t +OCTET_STRING_encode_der(asn_TYPE_descriptor_t *td, void *sptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_enc_rval_t er; + asn_OCTET_STRING_specifics_t *specs = td->specifics + ? (asn_OCTET_STRING_specifics_t *)td->specifics + : &asn_DEF_OCTET_STRING_specs; + BIT_STRING_t *st = (BIT_STRING_t *)sptr; + enum asn_OS_Subvariant type_variant = specs->subvariant; + int fix_last_byte = 0; + + ASN_DEBUG("%s %s as OCTET STRING", + cb?"Estimating":"Encoding", td->name); + + /* + * Write tags. + */ + if(type_variant != ASN_OSUBV_ANY || tag_mode == 1) { + er.encoded = der_write_tags(td, + (type_variant == ASN_OSUBV_BIT) + st->size, + tag_mode, type_variant == ASN_OSUBV_ANY, tag, + cb, app_key); + if(er.encoded == -1) { + er.failed_type = td; + er.structure_ptr = sptr; + return er; + } + } else { + /* Disallow: [] IMPLICIT ANY */ + assert(type_variant != ASN_OSUBV_ANY || tag_mode != -1); + er.encoded = 0; + } + + if(!cb) { + er.encoded += (type_variant == ASN_OSUBV_BIT) + st->size; + ASN__ENCODED_OK(er); + } + + /* + * Prepare to deal with the last octet of BIT STRING. + */ + if(type_variant == ASN_OSUBV_BIT) { + uint8_t b = st->bits_unused & 0x07; + if(b && st->size) fix_last_byte = 1; + ASN__CALLBACK(&b, 1); + er.encoded++; + } + + /* Invoke callback for the main part of the buffer */ + ASN__CALLBACK(st->buf, st->size - fix_last_byte); + + /* The last octet should be stripped off the unused bits */ + if(fix_last_byte) { + uint8_t b = st->buf[st->size-1] & (0xff << st->bits_unused); + ASN__CALLBACK(&b, 1); + } + + er.encoded += st->size; + ASN__ENCODED_OK(er); +cb_failed: + ASN__ENCODE_FAILED; +} + +asn_enc_rval_t +OCTET_STRING_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + const char * const h2c = "0123456789ABCDEF"; + const OCTET_STRING_t *st = (const OCTET_STRING_t *)sptr; + asn_enc_rval_t er; + char scratch[16 * 3 + 4]; + char *p = scratch; + uint8_t *buf; + uint8_t *end; + size_t i; + + if(!st || (!st->buf && st->size)) + ASN__ENCODE_FAILED; + + er.encoded = 0; + + /* + * Dump the contents of the buffer in hexadecimal. + */ + buf = st->buf; + end = buf + st->size; + if(flags & XER_F_CANONICAL) { + char *scend = scratch + (sizeof(scratch) - 2); + for(; buf < end; buf++) { + if(p >= scend) { + ASN__CALLBACK(scratch, p - scratch); + er.encoded += p - scratch; + p = scratch; + } + *p++ = h2c[(*buf >> 4) & 0x0F]; + *p++ = h2c[*buf & 0x0F]; + } + + ASN__CALLBACK(scratch, p-scratch); /* Dump the rest */ + er.encoded += p - scratch; + } else { + for(i = 0; buf < end; buf++, i++) { + if(!(i % 16) && (i || st->size > 16)) { + ASN__CALLBACK(scratch, p-scratch); + er.encoded += (p-scratch); + p = scratch; + ASN__TEXT_INDENT(1, ilevel); + } + *p++ = h2c[(*buf >> 4) & 0x0F]; + *p++ = h2c[*buf & 0x0F]; + *p++ = 0x20; + } + if(p - scratch) { + p--; /* Remove the tail space */ + ASN__CALLBACK(scratch, p-scratch); /* Dump the rest */ + er.encoded += p - scratch; + if(st->size > 16) + ASN__TEXT_INDENT(1, ilevel-1); + } + } + + ASN__ENCODED_OK(er); +cb_failed: + ASN__ENCODE_FAILED; +} + +static const struct OCTET_STRING__xer_escape_table_s { + const char *string; + int size; +} OCTET_STRING__xer_escape_table[] = { +#define OSXET(s) { s, sizeof(s) - 1 } + OSXET("\074\156\165\154\057\076"), /* */ + OSXET("\074\163\157\150\057\076"), /* */ + OSXET("\074\163\164\170\057\076"), /* */ + OSXET("\074\145\164\170\057\076"), /* */ + OSXET("\074\145\157\164\057\076"), /* */ + OSXET("\074\145\156\161\057\076"), /* */ + OSXET("\074\141\143\153\057\076"), /* */ + OSXET("\074\142\145\154\057\076"), /* */ + OSXET("\074\142\163\057\076"), /* */ + OSXET("\011"), /* \t */ + OSXET("\012"), /* \n */ + OSXET("\074\166\164\057\076"), /* */ + OSXET("\074\146\146\057\076"), /* */ + OSXET("\015"), /* \r */ + OSXET("\074\163\157\057\076"), /* */ + OSXET("\074\163\151\057\076"), /* */ + OSXET("\074\144\154\145\057\076"), /* */ + OSXET("\074\144\143\061\057\076"), /* */ + OSXET("\074\144\143\062\057\076"), /* */ + OSXET("\074\144\143\063\057\076"), /* */ + OSXET("\074\144\143\064\057\076"), /* */ + OSXET("\074\156\141\153\057\076"), /* */ + OSXET("\074\163\171\156\057\076"), /* */ + OSXET("\074\145\164\142\057\076"), /* */ + OSXET("\074\143\141\156\057\076"), /* */ + OSXET("\074\145\155\057\076"), /* */ + OSXET("\074\163\165\142\057\076"), /* */ + OSXET("\074\145\163\143\057\076"), /* */ + OSXET("\074\151\163\064\057\076"), /* */ + OSXET("\074\151\163\063\057\076"), /* */ + OSXET("\074\151\163\062\057\076"), /* */ + OSXET("\074\151\163\061\057\076"), /* */ + { 0, 0 }, /* " " */ + { 0, 0 }, /* ! */ + { 0, 0 }, /* \" */ + { 0, 0 }, /* # */ + { 0, 0 }, /* $ */ + { 0, 0 }, /* % */ + OSXET("\046\141\155\160\073"), /* & */ + { 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}, /* 01234567 */ + {0,0},{0,0},{0,0},{0,0}, /* 89:; */ + OSXET("\046\154\164\073"), /* < */ + { 0, 0 }, /* = */ + OSXET("\046\147\164\073"), /* > */ +}; + +static int +OS__check_escaped_control_char(const void *buf, int size) { + size_t i; + /* + * Inefficient algorithm which translates the escape sequences + * defined above into characters. Returns -1 if not found. + * TODO: replace by a faster algorithm (bsearch(), hash or + * nested table lookups). + */ + for(i = 0; i < 32 /* Don't spend time on the bottom half */; i++) { + const struct OCTET_STRING__xer_escape_table_s *el; + el = &OCTET_STRING__xer_escape_table[i]; + if(el->size == size && memcmp(buf, el->string, size) == 0) + return i; + } + return -1; +} + +static int +OCTET_STRING__handle_control_chars(void *struct_ptr, const void *chunk_buf, size_t chunk_size) { + /* + * This might be one of the escape sequences + * for control characters. Check it out. + * #11.15.5 + */ + int control_char = OS__check_escaped_control_char(chunk_buf,chunk_size); + if(control_char >= 0) { + OCTET_STRING_t *st = (OCTET_STRING_t *)struct_ptr; + void *p = REALLOC(st->buf, st->size + 2); + if(p) { + st->buf = (uint8_t *)p; + st->buf[st->size++] = control_char; + st->buf[st->size] = '\0'; /* nul-termination */ + return 0; + } + } + + return -1; /* No, it's not */ +} + +asn_enc_rval_t +OCTET_STRING_encode_xer_utf8(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + const OCTET_STRING_t *st = (const OCTET_STRING_t *)sptr; + asn_enc_rval_t er; + uint8_t *buf, *end; + uint8_t *ss; /* Sequence start */ + ssize_t encoded_len = 0; + + (void)ilevel; /* Unused argument */ + (void)flags; /* Unused argument */ + + if(!st || (!st->buf && st->size)) + ASN__ENCODE_FAILED; + + buf = st->buf; + end = buf + st->size; + for(ss = buf; buf < end; buf++) { + unsigned int ch = *buf; + int s_len; /* Special encoding sequence length */ + + /* + * Escape certain characters: X.680/11.15 + */ + if(ch < sizeof(OCTET_STRING__xer_escape_table) + /sizeof(OCTET_STRING__xer_escape_table[0]) + && (s_len = OCTET_STRING__xer_escape_table[ch].size)) { + if(((buf - ss) && cb(ss, buf - ss, app_key) < 0) + || cb(OCTET_STRING__xer_escape_table[ch].string, s_len, + app_key) < 0) + ASN__ENCODE_FAILED; + encoded_len += (buf - ss) + s_len; + ss = buf + 1; + } + } + + encoded_len += (buf - ss); + if((buf - ss) && cb(ss, buf - ss, app_key) < 0) + ASN__ENCODE_FAILED; + + er.encoded = encoded_len; + ASN__ENCODED_OK(er); +} + +/* + * Convert from hexadecimal format (cstring): "AB CD EF" + */ +static ssize_t OCTET_STRING__convert_hexadecimal(void *sptr, const void *chunk_buf, size_t chunk_size, int have_more) { + OCTET_STRING_t *st = (OCTET_STRING_t *)sptr; + const char *chunk_stop = (const char *)chunk_buf; + const char *p = chunk_stop; + const char *pend = p + chunk_size; + unsigned int clv = 0; + int half = 0; /* Half bit */ + uint8_t *buf; + + /* Reallocate buffer according to high cap estimation */ + ssize_t _ns = st->size + (chunk_size + 1) / 2; + void *nptr = REALLOC(st->buf, _ns + 1); + if(!nptr) return -1; + st->buf = (uint8_t *)nptr; + buf = st->buf + st->size; + + /* + * If something like " a b c " appears here, the " a b":3 will be + * converted, and the rest skipped. That is, unless buf_size is greater + * than chunk_size, then it'll be equivalent to "ABC0". + */ + for(; p < pend; p++) { + int ch = *(const unsigned char *)p; + switch(ch) { + case 0x09: case 0x0a: case 0x0c: case 0x0d: + case 0x20: + /* Ignore whitespace */ + continue; + case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: /*01234*/ + case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: /*56789*/ + clv = (clv << 4) + (ch - 0x30); + break; + case 0x41: case 0x42: case 0x43: /* ABC */ + case 0x44: case 0x45: case 0x46: /* DEF */ + clv = (clv << 4) + (ch - 0x41 + 10); + break; + case 0x61: case 0x62: case 0x63: /* abc */ + case 0x64: case 0x65: case 0x66: /* def */ + clv = (clv << 4) + (ch - 0x61 + 10); + break; + default: + *buf = 0; /* JIC */ + return -1; + } + if(half++) { + half = 0; + *buf++ = clv; + chunk_stop = p + 1; + } + } + + /* + * Check partial decoding. + */ + if(half) { + if(have_more) { + /* + * Partial specification is fine, + * because no more more PXER_TEXT data is available. + */ + *buf++ = clv << 4; + chunk_stop = p; + } + } else { + chunk_stop = p; + } + + st->size = buf - st->buf; /* Adjust the buffer size */ + assert(st->size <= _ns); + st->buf[st->size] = 0; /* Courtesy termination */ + + return (chunk_stop - (const char *)chunk_buf); /* Converted size */ +} + +/* + * Convert from binary format: "00101011101" + */ +static ssize_t OCTET_STRING__convert_binary(void *sptr, const void *chunk_buf, size_t chunk_size, int have_more) { + BIT_STRING_t *st = (BIT_STRING_t *)sptr; + const char *p = (const char *)chunk_buf; + const char *pend = p + chunk_size; + int bits_unused = st->bits_unused & 0x7; + uint8_t *buf; + + /* Reallocate buffer according to high cap estimation */ + ssize_t _ns = st->size + (chunk_size + 7) / 8; + void *nptr = REALLOC(st->buf, _ns + 1); + if(!nptr) return -1; + st->buf = (uint8_t *)nptr; + buf = st->buf + st->size; + + (void)have_more; + + if(bits_unused == 0) + bits_unused = 8; + else if(st->size) + buf--; + + /* + * Convert series of 0 and 1 into the octet string. + */ + for(; p < pend; p++) { + int ch = *(const unsigned char *)p; + switch(ch) { + case 0x09: case 0x0a: case 0x0c: case 0x0d: + case 0x20: + /* Ignore whitespace */ + break; + case 0x30: + case 0x31: + if(bits_unused-- <= 0) { + *++buf = 0; /* Clean the cell */ + bits_unused = 7; + } + *buf |= (ch&1) << bits_unused; + break; + default: + st->bits_unused = bits_unused; + return -1; + } + } + + if(bits_unused == 8) { + st->size = buf - st->buf; + st->bits_unused = 0; + } else { + st->size = buf - st->buf + 1; + st->bits_unused = bits_unused; + } + + assert(st->size <= _ns); + st->buf[st->size] = 0; /* Courtesy termination */ + + return chunk_size; /* Converted in full */ +} + +/* + * Something like strtod(), but with stricter rules. + */ +static int +OS__strtoent(int base, const char *buf, const char *end, int32_t *ret_value) { + int32_t val = 0; + const char *p; + + for(p = buf; p < end; p++) { + int ch = *p; + + /* Strange huge value */ + if((val * base + base) < 0) + return -1; + + switch(ch) { + case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: /*01234*/ + case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: /*56789*/ + val = val * base + (ch - 0x30); + break; + case 0x41: case 0x42: case 0x43: /* ABC */ + case 0x44: case 0x45: case 0x46: /* DEF */ + val = val * base + (ch - 0x41 + 10); + break; + case 0x61: case 0x62: case 0x63: /* abc */ + case 0x64: case 0x65: case 0x66: /* def */ + val = val * base + (ch - 0x61 + 10); + break; + case 0x3b: /* ';' */ + *ret_value = val; + return (p - buf) + 1; + default: + return -1; /* Character set error */ + } + } + + *ret_value = -1; + return (p - buf); +} + +/* + * Convert from the plain UTF-8 format, expanding entity references: "2 < 3" + */ +static ssize_t OCTET_STRING__convert_entrefs(void *sptr, const void *chunk_buf, size_t chunk_size, int have_more) { + OCTET_STRING_t *st = (OCTET_STRING_t *)sptr; + const char *p = (const char *)chunk_buf; + const char *pend = p + chunk_size; + uint8_t *buf; + + /* Reallocate buffer */ + ssize_t _ns = st->size + chunk_size; + void *nptr = REALLOC(st->buf, _ns + 1); + if(!nptr) return -1; + st->buf = (uint8_t *)nptr; + buf = st->buf + st->size; + + /* + * Convert series of 0 and 1 into the octet string. + */ + for(; p < pend; p++) { + int ch = *(const unsigned char *)p; + int len; /* Length of the rest of the chunk */ + + if(ch != 0x26 /* '&' */) { + *buf++ = ch; + continue; /* That was easy... */ + } + + /* + * Process entity reference. + */ + len = chunk_size - (p - (const char *)chunk_buf); + if(len == 1 /* "&" */) goto want_more; + if(p[1] == 0x23 /* '#' */) { + const char *pval; /* Pointer to start of digits */ + int32_t val = 0; /* Entity reference value */ + int base; + + if(len == 2 /* "&#" */) goto want_more; + if(p[2] == 0x78 /* 'x' */) + pval = p + 3, base = 16; + else + pval = p + 2, base = 10; + len = OS__strtoent(base, pval, p + len, &val); + if(len == -1) { + /* Invalid charset. Just copy verbatim. */ + *buf++ = ch; + continue; + } + if(!len || pval[len-1] != 0x3b) goto want_more; + assert(val > 0); + p += (pval - p) + len - 1; /* Advance past entref */ + + if(val < 0x80) { + *buf++ = (char)val; + } else if(val < 0x800) { + *buf++ = 0xc0 | ((val >> 6)); + *buf++ = 0x80 | ((val & 0x3f)); + } else if(val < 0x10000) { + *buf++ = 0xe0 | ((val >> 12)); + *buf++ = 0x80 | ((val >> 6) & 0x3f); + *buf++ = 0x80 | ((val & 0x3f)); + } else if(val < 0x200000) { + *buf++ = 0xf0 | ((val >> 18)); + *buf++ = 0x80 | ((val >> 12) & 0x3f); + *buf++ = 0x80 | ((val >> 6) & 0x3f); + *buf++ = 0x80 | ((val & 0x3f)); + } else if(val < 0x4000000) { + *buf++ = 0xf8 | ((val >> 24)); + *buf++ = 0x80 | ((val >> 18) & 0x3f); + *buf++ = 0x80 | ((val >> 12) & 0x3f); + *buf++ = 0x80 | ((val >> 6) & 0x3f); + *buf++ = 0x80 | ((val & 0x3f)); + } else { + *buf++ = 0xfc | ((val >> 30) & 0x1); + *buf++ = 0x80 | ((val >> 24) & 0x3f); + *buf++ = 0x80 | ((val >> 18) & 0x3f); + *buf++ = 0x80 | ((val >> 12) & 0x3f); + *buf++ = 0x80 | ((val >> 6) & 0x3f); + *buf++ = 0x80 | ((val & 0x3f)); + } + } else { + /* + * Ugly, limited parsing of & > < + */ + char *sc = (char *)memchr(p, 0x3b, len > 5 ? 5 : len); + if(!sc) goto want_more; + if((sc - p) == 4 + && p[1] == 0x61 /* 'a' */ + && p[2] == 0x6d /* 'm' */ + && p[3] == 0x70 /* 'p' */) { + *buf++ = 0x26; + p = sc; + continue; + } + if((sc - p) == 3) { + if(p[1] == 0x6c) { + *buf = 0x3c; /* '<' */ + } else if(p[1] == 0x67) { + *buf = 0x3e; /* '>' */ + } else { + /* Unsupported entity reference */ + *buf++ = ch; + continue; + } + if(p[2] != 0x74) { + /* Unsupported entity reference */ + *buf++ = ch; + continue; + } + buf++; + p = sc; + continue; + } + /* Unsupported entity reference */ + *buf++ = ch; + } + + continue; + want_more: + if(have_more) { + /* + * We know that no more data (of the same type) + * is coming. Copy the rest verbatim. + */ + *buf++ = ch; + continue; + } + chunk_size = (p - (const char *)chunk_buf); + /* Processing stalled: need more data */ + break; + } + + st->size = buf - st->buf; + assert(st->size <= _ns); + st->buf[st->size] = 0; /* Courtesy termination */ + + return chunk_size; /* Converted in full */ +} + +/* + * Decode OCTET STRING from the XML element's body. + */ +static asn_dec_rval_t +OCTET_STRING__decode_xer(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, void **sptr, + const char *opt_mname, const void *buf_ptr, size_t size, + int (*opt_unexpected_tag_decoder) + (void *struct_ptr, const void *chunk_buf, size_t chunk_size), + ssize_t (*body_receiver) + (void *struct_ptr, const void *chunk_buf, size_t chunk_size, + int have_more) +) { + OCTET_STRING_t *st = (OCTET_STRING_t *)*sptr; + asn_OCTET_STRING_specifics_t *specs = td->specifics + ? (asn_OCTET_STRING_specifics_t *)td->specifics + : &asn_DEF_OCTET_STRING_specs; + const char *xml_tag = opt_mname ? opt_mname : td->xml_tag; + asn_struct_ctx_t *ctx; /* Per-structure parser context */ + asn_dec_rval_t rval; /* Return value from the decoder */ + int st_allocated; + + /* + * Create the string if does not exist. + */ + if(!st) { + st = (OCTET_STRING_t *)CALLOC(1, specs->struct_size); + *sptr = (void *)st; + if(!st) goto sta_failed; + st_allocated = 1; + } else { + st_allocated = 0; + } + if(!st->buf) { + /* This is separate from above section */ + st->buf = (uint8_t *)CALLOC(1, 1); + if(!st->buf) { + if(st_allocated) { + *sptr = 0; + goto stb_failed; + } else { + goto sta_failed; + } + } + } + + /* Restore parsing context */ + ctx = (asn_struct_ctx_t *)(((char *)*sptr) + specs->ctx_offset); + + return xer_decode_general(opt_codec_ctx, ctx, *sptr, xml_tag, + buf_ptr, size, opt_unexpected_tag_decoder, body_receiver); + +stb_failed: + FREEMEM(st); +sta_failed: + rval.code = RC_FAIL; + rval.consumed = 0; + return rval; +} + +/* + * Decode OCTET STRING from the hexadecimal data. + */ +asn_dec_rval_t +OCTET_STRING_decode_xer_hex(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, void **sptr, + const char *opt_mname, const void *buf_ptr, size_t size) { + return OCTET_STRING__decode_xer(opt_codec_ctx, td, sptr, opt_mname, + buf_ptr, size, 0, OCTET_STRING__convert_hexadecimal); +} + +/* + * Decode OCTET STRING from the binary (0/1) data. + */ +asn_dec_rval_t +OCTET_STRING_decode_xer_binary(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, void **sptr, + const char *opt_mname, const void *buf_ptr, size_t size) { + return OCTET_STRING__decode_xer(opt_codec_ctx, td, sptr, opt_mname, + buf_ptr, size, 0, OCTET_STRING__convert_binary); +} + +/* + * Decode OCTET STRING from the string (ASCII/UTF-8) data. + */ +asn_dec_rval_t +OCTET_STRING_decode_xer_utf8(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, void **sptr, + const char *opt_mname, const void *buf_ptr, size_t size) { + return OCTET_STRING__decode_xer(opt_codec_ctx, td, sptr, opt_mname, + buf_ptr, size, + OCTET_STRING__handle_control_chars, + OCTET_STRING__convert_entrefs); +} + +static int +OCTET_STRING_per_get_characters(asn_per_data_t *po, uint8_t *buf, + size_t units, unsigned int bpc, unsigned int unit_bits, + long lb, long ub, asn_per_constraints_t *pc) { + uint8_t *end = buf + units * bpc; + + ASN_DEBUG("Expanding %d characters into (%ld..%ld):%d", + (int)units, lb, ub, unit_bits); + + /* X.691: 27.5.4 */ + if((unsigned long)ub <= ((unsigned long)2 << (unit_bits - 1))) { + /* Decode without translation */ + lb = 0; + } else if(pc && pc->code2value) { + if(unit_bits > 16) + return 1; /* FATAL: can't have constrained + * UniversalString with more than + * 16 million code points */ + for(; buf < end; buf += bpc) { + int value; + int code = per_get_few_bits(po, unit_bits); + if(code < 0) return -1; /* WMORE */ + value = pc->code2value(code); + if(value < 0) { + ASN_DEBUG("Code %d (0x%02x) is" + " not in map (%ld..%ld)", + code, code, lb, ub); + return 1; /* FATAL */ + } + switch(bpc) { + case 1: *buf = value; break; + case 2: buf[0] = value >> 8; buf[1] = value; break; + case 4: buf[0] = value >> 24; buf[1] = value >> 16; + buf[2] = value >> 8; buf[3] = value; break; + } + } + return 0; + } + + /* Shortcut the no-op copying to the aligned structure */ + if(lb == 0 && (unit_bits == 8 * bpc)) { + return per_get_many_bits(po, buf, 0, unit_bits * units); + } + + for(; buf < end; buf += bpc) { + int code = per_get_few_bits(po, unit_bits); + int ch = code + lb; + if(code < 0) return -1; /* WMORE */ + if(ch > ub) { + ASN_DEBUG("Code %d is out of range (%ld..%ld)", + ch, lb, ub); + return 1; /* FATAL */ + } + switch(bpc) { + case 1: *buf = ch; break; + case 2: buf[0] = ch >> 8; buf[1] = ch; break; + case 4: buf[0] = ch >> 24; buf[1] = ch >> 16; + buf[2] = ch >> 8; buf[3] = ch; break; + } + } + + return 0; +} + +static int +OCTET_STRING_per_put_characters(asn_per_outp_t *po, const uint8_t *buf, + size_t units, unsigned int bpc, unsigned int unit_bits, + long lb, long ub, asn_per_constraints_t *pc) { + const uint8_t *end = buf + units * bpc; + + ASN_DEBUG("Squeezing %d characters into (%ld..%ld):%d (%d bpc)", + (int)units, lb, ub, unit_bits, bpc); + + /* X.691: 27.5.4 */ + if((unsigned long)ub <= ((unsigned long)2 << (unit_bits - 1))) { + /* Encode as is */ + lb = 0; + } else if(pc && pc->value2code) { + for(; buf < end; buf += bpc) { + int code; + uint32_t value; + switch(bpc) { + case 1: value = *(const uint8_t *)buf; break; + case 2: value = (buf[0] << 8) | buf[1]; break; + case 4: value = (buf[0] << 24) | (buf[1] << 16) + | (buf[2] << 8) | buf[3]; break; + default: return -1; + } + code = pc->value2code(value); + if(code < 0) { + ASN_DEBUG("Character %d (0x%02x) is" + " not in map (%ld..%ld)", + *buf, *buf, lb, ub); + return -1; + } + if(per_put_few_bits(po, code, unit_bits)) + return -1; + } + } + + /* Shortcut the no-op copying to the aligned structure */ + if(lb == 0 && (unit_bits == 8 * bpc)) { + return per_put_many_bits(po, buf, unit_bits * units); + } + + for(ub -= lb; buf < end; buf += bpc) { + int ch; + uint32_t value; + switch(bpc) { + case 1: value = *(const uint8_t *)buf; break; + case 2: value = (buf[0] << 8) | buf[1]; break; + case 4: value = (buf[0] << 24) | (buf[1] << 16) + | (buf[2] << 8) | buf[3]; break; + default: return -1; + } + ch = value - lb; + if(ch < 0 || ch > ub) { + ASN_DEBUG("Character %d (0x%02x)" + " is out of range (%ld..%ld)", + *buf, *buf, lb, ub + lb); + return -1; + } + if(per_put_few_bits(po, ch, unit_bits)) + return -1; + } + + return 0; +} + +asn_dec_rval_t +OCTET_STRING_decode_uper(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, + void **sptr, asn_per_data_t *pd) { + + asn_OCTET_STRING_specifics_t *specs = td->specifics + ? (asn_OCTET_STRING_specifics_t *)td->specifics + : &asn_DEF_OCTET_STRING_specs; + asn_per_constraints_t *pc = constraints ? constraints + : td->per_constraints; + asn_per_constraint_t *cval; + asn_per_constraint_t *csiz; + asn_dec_rval_t rval = { RC_OK, 0 }; + BIT_STRING_t *st = (BIT_STRING_t *)*sptr; + ssize_t consumed_myself = 0; + int repeat; + enum { + OS__BPC_BIT = 0, + OS__BPC_CHAR = 1, + OS__BPC_U16 = 2, + OS__BPC_U32 = 4 + } bpc; /* Bytes per character */ + unsigned int unit_bits; + unsigned int canonical_unit_bits; + + (void)opt_codec_ctx; + + if(pc) { + cval = &pc->value; + csiz = &pc->size; + } else { + cval = &asn_DEF_OCTET_STRING_constraints.value; + csiz = &asn_DEF_OCTET_STRING_constraints.size; + } + + switch(specs->subvariant) { + default: + case ASN_OSUBV_ANY: + ASN_DEBUG("Unrecognized subvariant %d", specs->subvariant); + RETURN(RC_FAIL); + case ASN_OSUBV_BIT: + canonical_unit_bits = unit_bits = 1; + bpc = OS__BPC_BIT; + break; + case ASN_OSUBV_STR: + canonical_unit_bits = unit_bits = 8; + if(cval->flags & APC_CONSTRAINED) + unit_bits = cval->range_bits; + bpc = OS__BPC_CHAR; + break; + case ASN_OSUBV_U16: + canonical_unit_bits = unit_bits = 16; + if(cval->flags & APC_CONSTRAINED) + unit_bits = cval->range_bits; + bpc = OS__BPC_U16; + break; + case ASN_OSUBV_U32: + canonical_unit_bits = unit_bits = 32; + if(cval->flags & APC_CONSTRAINED) + unit_bits = cval->range_bits; + bpc = OS__BPC_U32; + break; + } + + /* + * Allocate the string. + */ + if(!st) { + st = (BIT_STRING_t *)(*sptr = CALLOC(1, specs->struct_size)); + if(!st) RETURN(RC_FAIL); + } + + ASN_DEBUG("PER Decoding %s size %ld .. %ld bits %d", + csiz->flags & APC_EXTENSIBLE ? "extensible" : "non-extensible", + csiz->lower_bound, csiz->upper_bound, csiz->effective_bits); + + if(csiz->flags & APC_EXTENSIBLE) { + int inext = per_get_few_bits(pd, 1); + if(inext < 0) RETURN(RC_WMORE); + if(inext) { + csiz = &asn_DEF_OCTET_STRING_constraints.size; + cval = &asn_DEF_OCTET_STRING_constraints.value; + unit_bits = canonical_unit_bits; + } + } + + if(csiz->effective_bits >= 0) { + FREEMEM(st->buf); + if(bpc) { + st->size = csiz->upper_bound * bpc; + } else { + st->size = (csiz->upper_bound + 7) >> 3; + } + st->buf = (uint8_t *)MALLOC(st->size + 1); + if(!st->buf) { st->size = 0; RETURN(RC_FAIL); } + } + + /* X.691, #16.5: zero-length encoding */ + /* X.691, #16.6: short fixed length encoding (up to 2 octets) */ + /* X.691, #16.7: long fixed length encoding (up to 64K octets) */ + if(csiz->effective_bits == 0) { + int ret; + if(bpc) { + ASN_DEBUG("Encoding OCTET STRING size %ld", + csiz->upper_bound); + ret = OCTET_STRING_per_get_characters(pd, st->buf, + csiz->upper_bound, bpc, unit_bits, + cval->lower_bound, cval->upper_bound, pc); + if(ret > 0) RETURN(RC_FAIL); + } else { + ASN_DEBUG("Encoding BIT STRING size %ld", + csiz->upper_bound); + ret = per_get_many_bits(pd, st->buf, 0, + unit_bits * csiz->upper_bound); + } + if(ret < 0) RETURN(RC_WMORE); + consumed_myself += unit_bits * csiz->upper_bound; + st->buf[st->size] = 0; + if(bpc == 0) { + int ubs = (csiz->upper_bound & 0x7); + st->bits_unused = ubs ? 8 - ubs : 0; + } + RETURN(RC_OK); + } + + st->size = 0; + do { + ssize_t raw_len; + ssize_t len_bytes; + ssize_t len_bits; + void *p; + int ret; + + /* Get the PER length */ + raw_len = uper_get_length(pd, csiz->effective_bits, &repeat); + if(raw_len < 0) RETURN(RC_WMORE); + raw_len += csiz->lower_bound; + + ASN_DEBUG("Got PER length eb %ld, len %ld, %s (%s)", + (long)csiz->effective_bits, (long)raw_len, + repeat ? "repeat" : "once", td->name); + if(bpc) { + len_bytes = raw_len * bpc; + len_bits = len_bytes * unit_bits; + } else { + len_bits = raw_len; + len_bytes = (len_bits + 7) >> 3; + if(len_bits & 0x7) + st->bits_unused = 8 - (len_bits & 0x7); + /* len_bits be multiple of 16K if repeat is set */ + } + p = REALLOC(st->buf, st->size + len_bytes + 1); + if(!p) RETURN(RC_FAIL); + st->buf = (uint8_t *)p; + + if(bpc) { + ret = OCTET_STRING_per_get_characters(pd, + &st->buf[st->size], raw_len, bpc, unit_bits, + cval->lower_bound, cval->upper_bound, pc); + if(ret > 0) RETURN(RC_FAIL); + } else { + ret = per_get_many_bits(pd, &st->buf[st->size], + 0, len_bits); + } + if(ret < 0) RETURN(RC_WMORE); + st->size += len_bytes; + } while(repeat); + st->buf[st->size] = 0; /* nul-terminate */ + + return rval; +} + +asn_enc_rval_t +OCTET_STRING_encode_uper(asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) { + + asn_OCTET_STRING_specifics_t *specs = td->specifics + ? (asn_OCTET_STRING_specifics_t *)td->specifics + : &asn_DEF_OCTET_STRING_specs; + asn_per_constraints_t *pc = constraints ? constraints + : td->per_constraints; + asn_per_constraint_t *cval; + asn_per_constraint_t *csiz; + const BIT_STRING_t *st = (const BIT_STRING_t *)sptr; + asn_enc_rval_t er = { 0, 0, 0 }; + int inext = 0; /* Lies not within extension root */ + unsigned int unit_bits; + unsigned int canonical_unit_bits; + unsigned int sizeinunits; + const uint8_t *buf; + int ret; + enum { + OS__BPC_BIT = 0, + OS__BPC_CHAR = 1, + OS__BPC_U16 = 2, + OS__BPC_U32 = 4 + } bpc; /* Bytes per character */ + int ct_extensible; + + if(!st || (!st->buf && st->size)) + ASN__ENCODE_FAILED; + + if(pc) { + cval = &pc->value; + csiz = &pc->size; + } else { + cval = &asn_DEF_OCTET_STRING_constraints.value; + csiz = &asn_DEF_OCTET_STRING_constraints.size; + } + ct_extensible = csiz->flags & APC_EXTENSIBLE; + + switch(specs->subvariant) { + default: + case ASN_OSUBV_ANY: + ASN__ENCODE_FAILED; + case ASN_OSUBV_BIT: + canonical_unit_bits = unit_bits = 1; + bpc = OS__BPC_BIT; + sizeinunits = st->size * 8 - (st->bits_unused & 0x07); + ASN_DEBUG("BIT STRING of %d bytes, %d bits unused", + sizeinunits, st->bits_unused); + break; + case ASN_OSUBV_STR: + canonical_unit_bits = unit_bits = 8; + if(cval->flags & APC_CONSTRAINED) + unit_bits = cval->range_bits; + bpc = OS__BPC_CHAR; + sizeinunits = st->size; + break; + case ASN_OSUBV_U16: + canonical_unit_bits = unit_bits = 16; + if(cval->flags & APC_CONSTRAINED) + unit_bits = cval->range_bits; + bpc = OS__BPC_U16; + sizeinunits = st->size / 2; + break; + case ASN_OSUBV_U32: + canonical_unit_bits = unit_bits = 32; + if(cval->flags & APC_CONSTRAINED) + unit_bits = cval->range_bits; + bpc = OS__BPC_U32; + sizeinunits = st->size / 4; + break; + } + + ASN_DEBUG("Encoding %s into %d units of %d bits" + " (%ld..%ld, effective %d)%s", + td->name, sizeinunits, unit_bits, + csiz->lower_bound, csiz->upper_bound, + csiz->effective_bits, ct_extensible ? " EXT" : ""); + + /* Figure out whether size lies within PER visible constraint */ + + if(csiz->effective_bits >= 0) { + if((int)sizeinunits < csiz->lower_bound + || (int)sizeinunits > csiz->upper_bound) { + if(ct_extensible) { + cval = &asn_DEF_OCTET_STRING_constraints.value; + csiz = &asn_DEF_OCTET_STRING_constraints.size; + unit_bits = canonical_unit_bits; + inext = 1; + } else + ASN__ENCODE_FAILED; + } + } else { + inext = 0; + } + + if(ct_extensible) { + /* Declare whether length is [not] within extension root */ + if(per_put_few_bits(po, inext, 1)) + ASN__ENCODE_FAILED; + } + + /* X.691, #16.5: zero-length encoding */ + /* X.691, #16.6: short fixed length encoding (up to 2 octets) */ + /* X.691, #16.7: long fixed length encoding (up to 64K octets) */ + if(csiz->effective_bits >= 0) { + ASN_DEBUG("Encoding %d bytes (%ld), length in %d bits", + st->size, sizeinunits - csiz->lower_bound, + csiz->effective_bits); + ret = per_put_few_bits(po, sizeinunits - csiz->lower_bound, + csiz->effective_bits); + if(ret) ASN__ENCODE_FAILED; + if(bpc) { + ret = OCTET_STRING_per_put_characters(po, st->buf, + sizeinunits, bpc, unit_bits, + cval->lower_bound, cval->upper_bound, pc); + } else { + ret = per_put_many_bits(po, st->buf, + sizeinunits * unit_bits); + } + if(ret) ASN__ENCODE_FAILED; + ASN__ENCODED_OK(er); + } + + ASN_DEBUG("Encoding %d bytes", st->size); + + if(sizeinunits == 0) { + if(uper_put_length(po, 0)) + ASN__ENCODE_FAILED; + ASN__ENCODED_OK(er); + } + + buf = st->buf; + while(sizeinunits) { + ssize_t maySave = uper_put_length(po, sizeinunits); + if(maySave < 0) ASN__ENCODE_FAILED; + + ASN_DEBUG("Encoding %ld of %ld", + (long)maySave, (long)sizeinunits); + + if(bpc) { + ret = OCTET_STRING_per_put_characters(po, buf, + maySave, bpc, unit_bits, + cval->lower_bound, cval->upper_bound, pc); + } else { + ret = per_put_many_bits(po, buf, maySave * unit_bits); + } + if(ret) ASN__ENCODE_FAILED; + + if(bpc) + buf += maySave * bpc; + else + buf += maySave >> 3; + sizeinunits -= maySave; + assert(!(maySave & 0x07) || !sizeinunits); + } + + ASN__ENCODED_OK(er); +} + +int +OCTET_STRING_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + const char * const h2c = "0123456789ABCDEF"; + const OCTET_STRING_t *st = (const OCTET_STRING_t *)sptr; + char scratch[16 * 3 + 4]; + char *p = scratch; + uint8_t *buf; + uint8_t *end; + size_t i; + + (void)td; /* Unused argument */ + + if(!st || (!st->buf && st->size)) + return (cb("", 8, app_key) < 0) ? -1 : 0; + + /* + * Dump the contents of the buffer in hexadecimal. + */ + buf = st->buf; + end = buf + st->size; + for(i = 0; buf < end; buf++, i++) { + if(!(i % 16) && (i || st->size > 16)) { + if(cb(scratch, p - scratch, app_key) < 0) + return -1; + _i_INDENT(1); + p = scratch; + } + *p++ = h2c[(*buf >> 4) & 0x0F]; + *p++ = h2c[*buf & 0x0F]; + *p++ = 0x20; + } + + if(p > scratch) { + p--; /* Remove the tail space */ + if(cb(scratch, p - scratch, app_key) < 0) + return -1; + } + + return 0; +} + +int +OCTET_STRING_print_utf8(asn_TYPE_descriptor_t *td, const void *sptr, + int ilevel, asn_app_consume_bytes_f *cb, void *app_key) { + const OCTET_STRING_t *st = (const OCTET_STRING_t *)sptr; + + (void)td; /* Unused argument */ + (void)ilevel; /* Unused argument */ + + if(st && (st->buf || !st->size)) { + return (cb(st->buf, st->size, app_key) < 0) ? -1 : 0; + } else { + return (cb("", 8, app_key) < 0) ? -1 : 0; + } +} + +void +OCTET_STRING_free(asn_TYPE_descriptor_t *td, void *sptr, int contents_only) { + OCTET_STRING_t *st = (OCTET_STRING_t *)sptr; + asn_OCTET_STRING_specifics_t *specs; + asn_struct_ctx_t *ctx; + struct _stack *stck; + + if(!td || !st) + return; + + specs = td->specifics + ? (asn_OCTET_STRING_specifics_t *)td->specifics + : &asn_DEF_OCTET_STRING_specs; + ctx = (asn_struct_ctx_t *)((char *)st + specs->ctx_offset); + + ASN_DEBUG("Freeing %s as OCTET STRING", td->name); + + if(st->buf) { + FREEMEM(st->buf); + st->buf = 0; + } + + /* + * Remove decode-time stack. + */ + stck = (struct _stack *)ctx->ptr; + if(stck) { + while(stck->tail) { + struct _stack_el *sel = stck->tail; + stck->tail = sel->prev; + FREEMEM(sel); + } + FREEMEM(stck); + } + + if(!contents_only) { + FREEMEM(st); + } +} + +/* + * Conversion routines. + */ +int +OCTET_STRING_fromBuf(OCTET_STRING_t *st, const char *str, int len) { + void *buf; + + if(st == 0 || (str == 0 && len)) { + errno = EINVAL; + return -1; + } + + /* + * Clear the OCTET STRING. + */ + if(str == NULL) { + FREEMEM(st->buf); + st->buf = 0; + st->size = 0; + return 0; + } + + /* Determine the original string size, if not explicitly given */ + if(len < 0) + len = strlen(str); + + /* Allocate and fill the memory */ + buf = MALLOC(len + 1); + if(buf == NULL) + return -1; + + memcpy(buf, str, len); + ((uint8_t *)buf)[len] = '\0'; /* Couldn't use memcpy(len+1)! */ + FREEMEM(st->buf); + st->buf = (uint8_t *)buf; + st->size = len; + + return 0; +} + +OCTET_STRING_t * +OCTET_STRING_new_fromBuf(asn_TYPE_descriptor_t *td, const char *str, int len) { + asn_OCTET_STRING_specifics_t *specs = td->specifics + ? (asn_OCTET_STRING_specifics_t *)td->specifics + : &asn_DEF_OCTET_STRING_specs; + OCTET_STRING_t *st; + + st = (OCTET_STRING_t *)CALLOC(1, specs->struct_size); + if(st && str && OCTET_STRING_fromBuf(st, str, len)) { + FREEMEM(st); + st = NULL; + } + + return st; +} + diff --git a/src/cryptoconditions/src/asn/OCTET_STRING.h b/src/cryptoconditions/src/asn/OCTET_STRING.h new file mode 100644 index 000000000..013c7b13f --- /dev/null +++ b/src/cryptoconditions/src/asn/OCTET_STRING.h @@ -0,0 +1,86 @@ +/*- + * Copyright (c) 2003 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _OCTET_STRING_H_ +#define _OCTET_STRING_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct OCTET_STRING { + uint8_t *buf; /* Buffer with consecutive OCTET_STRING bits */ + int size; /* Size of the buffer */ + + asn_struct_ctx_t _asn_ctx; /* Parsing across buffer boundaries */ +} OCTET_STRING_t; + +extern asn_TYPE_descriptor_t asn_DEF_OCTET_STRING; + +asn_struct_free_f OCTET_STRING_free; +asn_struct_print_f OCTET_STRING_print; +asn_struct_print_f OCTET_STRING_print_utf8; +ber_type_decoder_f OCTET_STRING_decode_ber; +der_type_encoder_f OCTET_STRING_encode_der; +xer_type_decoder_f OCTET_STRING_decode_xer_hex; /* Hexadecimal */ +xer_type_decoder_f OCTET_STRING_decode_xer_binary; /* 01010111010 */ +xer_type_decoder_f OCTET_STRING_decode_xer_utf8; /* ASCII/UTF-8 */ +xer_type_encoder_f OCTET_STRING_encode_xer; +xer_type_encoder_f OCTET_STRING_encode_xer_utf8; +per_type_decoder_f OCTET_STRING_decode_uper; +per_type_encoder_f OCTET_STRING_encode_uper; + +/****************************** + * Handy conversion routines. * + ******************************/ + +/* + * This function clears the previous value of the OCTET STRING (if any) + * and then allocates a new memory with the specified content (str/size). + * If size = -1, the size of the original string will be determined + * using strlen(str). + * If str equals to NULL, the function will silently clear the + * current contents of the OCTET STRING. + * Returns 0 if it was possible to perform operation, -1 otherwise. + */ +int OCTET_STRING_fromBuf(OCTET_STRING_t *s, const char *str, int size); + +/* Handy conversion from the C string into the OCTET STRING. */ +#define OCTET_STRING_fromString(s, str) OCTET_STRING_fromBuf(s, str, -1) + +/* + * Allocate and fill the new OCTET STRING and return a pointer to the newly + * allocated object. NULL is permitted in str: the function will just allocate + * empty OCTET STRING. + */ +OCTET_STRING_t *OCTET_STRING_new_fromBuf(asn_TYPE_descriptor_t *td, + const char *str, int size); + +/**************************** + * Internally useful stuff. * + ****************************/ + +typedef const struct asn_OCTET_STRING_specifics_s { + /* + * Target structure description. + */ + int struct_size; /* Size of the structure */ + int ctx_offset; /* Offset of the asn_struct_ctx_t member */ + + enum asn_OS_Subvariant { + ASN_OSUBV_ANY, /* The open type (ANY) */ + ASN_OSUBV_BIT, /* BIT STRING */ + ASN_OSUBV_STR, /* String types, not {BMP,Universal}String */ + ASN_OSUBV_U16, /* 16-bit character (BMPString) */ + ASN_OSUBV_U32 /* 32-bit character (UniversalString) */ + } subvariant; +} asn_OCTET_STRING_specifics_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _OCTET_STRING_H_ */ diff --git a/src/cryptoconditions/src/asn/PrefixFingerprintContents.c b/src/cryptoconditions/src/asn/PrefixFingerprintContents.c new file mode 100644 index 000000000..13f54aaa1 --- /dev/null +++ b/src/cryptoconditions/src/asn/PrefixFingerprintContents.c @@ -0,0 +1,209 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#include "PrefixFingerprintContents.h" + +static int +maxMessageLength_3_constraint(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + + /* Constraint check succeeded */ + return 0; +} + +/* + * This type is implemented using NativeInteger, + * so here we adjust the DEF accordingly. + */ +static void +maxMessageLength_3_inherit_TYPE_descriptor(asn_TYPE_descriptor_t *td) { + td->free_struct = asn_DEF_NativeInteger.free_struct; + td->print_struct = asn_DEF_NativeInteger.print_struct; + td->check_constraints = asn_DEF_NativeInteger.check_constraints; + td->ber_decoder = asn_DEF_NativeInteger.ber_decoder; + td->der_encoder = asn_DEF_NativeInteger.der_encoder; + td->xer_decoder = asn_DEF_NativeInteger.xer_decoder; + td->xer_encoder = asn_DEF_NativeInteger.xer_encoder; + td->uper_decoder = asn_DEF_NativeInteger.uper_decoder; + td->uper_encoder = asn_DEF_NativeInteger.uper_encoder; + if(!td->per_constraints) + td->per_constraints = asn_DEF_NativeInteger.per_constraints; + td->elements = asn_DEF_NativeInteger.elements; + td->elements_count = asn_DEF_NativeInteger.elements_count; + /* td->specifics = asn_DEF_NativeInteger.specifics; // Defined explicitly */ +} + +static void +maxMessageLength_3_free(asn_TYPE_descriptor_t *td, + void *struct_ptr, int contents_only) { + maxMessageLength_3_inherit_TYPE_descriptor(td); + td->free_struct(td, struct_ptr, contents_only); +} + +static int +maxMessageLength_3_print(asn_TYPE_descriptor_t *td, const void *struct_ptr, + int ilevel, asn_app_consume_bytes_f *cb, void *app_key) { + maxMessageLength_3_inherit_TYPE_descriptor(td); + return td->print_struct(td, struct_ptr, ilevel, cb, app_key); +} + +static asn_dec_rval_t +maxMessageLength_3_decode_ber(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **structure, const void *bufptr, size_t size, int tag_mode) { + maxMessageLength_3_inherit_TYPE_descriptor(td); + return td->ber_decoder(opt_codec_ctx, td, structure, bufptr, size, tag_mode); +} + +static asn_enc_rval_t +maxMessageLength_3_encode_der(asn_TYPE_descriptor_t *td, + void *structure, int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + maxMessageLength_3_inherit_TYPE_descriptor(td); + return td->der_encoder(td, structure, tag_mode, tag, cb, app_key); +} + +static asn_dec_rval_t +maxMessageLength_3_decode_xer(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **structure, const char *opt_mname, const void *bufptr, size_t size) { + maxMessageLength_3_inherit_TYPE_descriptor(td); + return td->xer_decoder(opt_codec_ctx, td, structure, opt_mname, bufptr, size); +} + +static asn_enc_rval_t +maxMessageLength_3_encode_xer(asn_TYPE_descriptor_t *td, void *structure, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + maxMessageLength_3_inherit_TYPE_descriptor(td); + return td->xer_encoder(td, structure, ilevel, flags, cb, app_key); +} + +static int +memb_maxMessageLength_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + + /* Constraint check succeeded */ + return 0; +} + +static const asn_INTEGER_specifics_t asn_SPC_maxMessageLength_specs_3 = { + 0, 0, 0, 0, 0, + 0, /* Native long size */ + 1 /* Unsigned representation */ +}; +static const ber_tlv_tag_t asn_DEF_maxMessageLength_tags_3[] = { + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + (ASN_TAG_CLASS_UNIVERSAL | (2 << 2)) +}; +static /* Use -fall-defs-global to expose */ +asn_TYPE_descriptor_t asn_DEF_maxMessageLength_3 = { + "maxMessageLength", + "maxMessageLength", + maxMessageLength_3_free, + maxMessageLength_3_print, + maxMessageLength_3_constraint, + maxMessageLength_3_decode_ber, + maxMessageLength_3_encode_der, + maxMessageLength_3_decode_xer, + maxMessageLength_3_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_maxMessageLength_tags_3, + sizeof(asn_DEF_maxMessageLength_tags_3) + /sizeof(asn_DEF_maxMessageLength_tags_3[0]) - 1, /* 1 */ + asn_DEF_maxMessageLength_tags_3, /* Same as above */ + sizeof(asn_DEF_maxMessageLength_tags_3) + /sizeof(asn_DEF_maxMessageLength_tags_3[0]), /* 2 */ + 0, /* No PER visible constraints */ + 0, 0, /* No members */ + &asn_SPC_maxMessageLength_specs_3 /* Additional specs */ +}; + +static asn_TYPE_member_t asn_MBR_PrefixFingerprintContents_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct PrefixFingerprintContents, prefix), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_OCTET_STRING, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "prefix" + }, + { ATF_NOFLAGS, 0, offsetof(struct PrefixFingerprintContents, maxMessageLength), + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_maxMessageLength_3, + memb_maxMessageLength_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "maxMessageLength" + }, + { ATF_NOFLAGS, 0, offsetof(struct PrefixFingerprintContents, subcondition), + (ASN_TAG_CLASS_CONTEXT | (2 << 2)), + +1, /* EXPLICIT tag at current level */ + &asn_DEF_Condition, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "subcondition" + }, +}; +static const ber_tlv_tag_t asn_DEF_PrefixFingerprintContents_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_PrefixFingerprintContents_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 }, /* prefix */ + { (ASN_TAG_CLASS_CONTEXT | (1 << 2)), 1, 0, 0 }, /* maxMessageLength */ + { (ASN_TAG_CLASS_CONTEXT | (2 << 2)), 2, 0, 0 } /* subcondition */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_PrefixFingerprintContents_specs_1 = { + sizeof(struct PrefixFingerprintContents), + offsetof(struct PrefixFingerprintContents, _asn_ctx), + asn_MAP_PrefixFingerprintContents_tag2el_1, + 3, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_PrefixFingerprintContents = { + "PrefixFingerprintContents", + "PrefixFingerprintContents", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_PrefixFingerprintContents_tags_1, + sizeof(asn_DEF_PrefixFingerprintContents_tags_1) + /sizeof(asn_DEF_PrefixFingerprintContents_tags_1[0]), /* 1 */ + asn_DEF_PrefixFingerprintContents_tags_1, /* Same as above */ + sizeof(asn_DEF_PrefixFingerprintContents_tags_1) + /sizeof(asn_DEF_PrefixFingerprintContents_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_PrefixFingerprintContents_1, + 3, /* Elements count */ + &asn_SPC_PrefixFingerprintContents_specs_1 /* Additional specs */ +}; + diff --git a/src/cryptoconditions/src/asn/PrefixFingerprintContents.h b/src/cryptoconditions/src/asn/PrefixFingerprintContents.h new file mode 100644 index 000000000..5d1254cd4 --- /dev/null +++ b/src/cryptoconditions/src/asn/PrefixFingerprintContents.h @@ -0,0 +1,42 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#ifndef _PrefixFingerprintContents_H_ +#define _PrefixFingerprintContents_H_ + + +#include + +/* Including external dependencies */ +#include +#include +#include "Condition.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* PrefixFingerprintContents */ +typedef struct PrefixFingerprintContents { + OCTET_STRING_t prefix; + unsigned long maxMessageLength; + Condition_t subcondition; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} PrefixFingerprintContents_t; + +/* Implementation */ +/* extern asn_TYPE_descriptor_t asn_DEF_maxMessageLength_3; // (Use -fall-defs-global to expose) */ +extern asn_TYPE_descriptor_t asn_DEF_PrefixFingerprintContents; + +#ifdef __cplusplus +} +#endif + +#endif /* _PrefixFingerprintContents_H_ */ +#include diff --git a/src/cryptoconditions/src/asn/PrefixFulfillment.c b/src/cryptoconditions/src/asn/PrefixFulfillment.c new file mode 100644 index 000000000..f4c854807 --- /dev/null +++ b/src/cryptoconditions/src/asn/PrefixFulfillment.c @@ -0,0 +1,209 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#include "PrefixFulfillment.h" + +static int +maxMessageLength_3_constraint(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + + /* Constraint check succeeded */ + return 0; +} + +/* + * This type is implemented using NativeInteger, + * so here we adjust the DEF accordingly. + */ +static void +maxMessageLength_3_inherit_TYPE_descriptor(asn_TYPE_descriptor_t *td) { + td->free_struct = asn_DEF_NativeInteger.free_struct; + td->print_struct = asn_DEF_NativeInteger.print_struct; + td->check_constraints = asn_DEF_NativeInteger.check_constraints; + td->ber_decoder = asn_DEF_NativeInteger.ber_decoder; + td->der_encoder = asn_DEF_NativeInteger.der_encoder; + td->xer_decoder = asn_DEF_NativeInteger.xer_decoder; + td->xer_encoder = asn_DEF_NativeInteger.xer_encoder; + td->uper_decoder = asn_DEF_NativeInteger.uper_decoder; + td->uper_encoder = asn_DEF_NativeInteger.uper_encoder; + if(!td->per_constraints) + td->per_constraints = asn_DEF_NativeInteger.per_constraints; + td->elements = asn_DEF_NativeInteger.elements; + td->elements_count = asn_DEF_NativeInteger.elements_count; + /* td->specifics = asn_DEF_NativeInteger.specifics; // Defined explicitly */ +} + +static void +maxMessageLength_3_free(asn_TYPE_descriptor_t *td, + void *struct_ptr, int contents_only) { + maxMessageLength_3_inherit_TYPE_descriptor(td); + td->free_struct(td, struct_ptr, contents_only); +} + +static int +maxMessageLength_3_print(asn_TYPE_descriptor_t *td, const void *struct_ptr, + int ilevel, asn_app_consume_bytes_f *cb, void *app_key) { + maxMessageLength_3_inherit_TYPE_descriptor(td); + return td->print_struct(td, struct_ptr, ilevel, cb, app_key); +} + +static asn_dec_rval_t +maxMessageLength_3_decode_ber(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **structure, const void *bufptr, size_t size, int tag_mode) { + maxMessageLength_3_inherit_TYPE_descriptor(td); + return td->ber_decoder(opt_codec_ctx, td, structure, bufptr, size, tag_mode); +} + +static asn_enc_rval_t +maxMessageLength_3_encode_der(asn_TYPE_descriptor_t *td, + void *structure, int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + maxMessageLength_3_inherit_TYPE_descriptor(td); + return td->der_encoder(td, structure, tag_mode, tag, cb, app_key); +} + +static asn_dec_rval_t +maxMessageLength_3_decode_xer(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **structure, const char *opt_mname, const void *bufptr, size_t size) { + maxMessageLength_3_inherit_TYPE_descriptor(td); + return td->xer_decoder(opt_codec_ctx, td, structure, opt_mname, bufptr, size); +} + +static asn_enc_rval_t +maxMessageLength_3_encode_xer(asn_TYPE_descriptor_t *td, void *structure, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + maxMessageLength_3_inherit_TYPE_descriptor(td); + return td->xer_encoder(td, structure, ilevel, flags, cb, app_key); +} + +static int +memb_maxMessageLength_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + + /* Constraint check succeeded */ + return 0; +} + +static const asn_INTEGER_specifics_t asn_SPC_maxMessageLength_specs_3 = { + 0, 0, 0, 0, 0, + 0, /* Native long size */ + 1 /* Unsigned representation */ +}; +static const ber_tlv_tag_t asn_DEF_maxMessageLength_tags_3[] = { + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + (ASN_TAG_CLASS_UNIVERSAL | (2 << 2)) +}; +static /* Use -fall-defs-global to expose */ +asn_TYPE_descriptor_t asn_DEF_maxMessageLength_3 = { + "maxMessageLength", + "maxMessageLength", + maxMessageLength_3_free, + maxMessageLength_3_print, + maxMessageLength_3_constraint, + maxMessageLength_3_decode_ber, + maxMessageLength_3_encode_der, + maxMessageLength_3_decode_xer, + maxMessageLength_3_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_maxMessageLength_tags_3, + sizeof(asn_DEF_maxMessageLength_tags_3) + /sizeof(asn_DEF_maxMessageLength_tags_3[0]) - 1, /* 1 */ + asn_DEF_maxMessageLength_tags_3, /* Same as above */ + sizeof(asn_DEF_maxMessageLength_tags_3) + /sizeof(asn_DEF_maxMessageLength_tags_3[0]), /* 2 */ + 0, /* No PER visible constraints */ + 0, 0, /* No members */ + &asn_SPC_maxMessageLength_specs_3 /* Additional specs */ +}; + +static asn_TYPE_member_t asn_MBR_PrefixFulfillment_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct PrefixFulfillment, prefix), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_OCTET_STRING, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "prefix" + }, + { ATF_NOFLAGS, 0, offsetof(struct PrefixFulfillment, maxMessageLength), + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_maxMessageLength_3, + memb_maxMessageLength_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "maxMessageLength" + }, + { ATF_POINTER, 0, offsetof(struct PrefixFulfillment, subfulfillment), + (ASN_TAG_CLASS_CONTEXT | (2 << 2)), + +1, /* EXPLICIT tag at current level */ + &asn_DEF_Fulfillment, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "subfulfillment" + }, +}; +static const ber_tlv_tag_t asn_DEF_PrefixFulfillment_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_PrefixFulfillment_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 }, /* prefix */ + { (ASN_TAG_CLASS_CONTEXT | (1 << 2)), 1, 0, 0 }, /* maxMessageLength */ + { (ASN_TAG_CLASS_CONTEXT | (2 << 2)), 2, 0, 0 } /* subfulfillment */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_PrefixFulfillment_specs_1 = { + sizeof(struct PrefixFulfillment), + offsetof(struct PrefixFulfillment, _asn_ctx), + asn_MAP_PrefixFulfillment_tag2el_1, + 3, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_PrefixFulfillment = { + "PrefixFulfillment", + "PrefixFulfillment", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_PrefixFulfillment_tags_1, + sizeof(asn_DEF_PrefixFulfillment_tags_1) + /sizeof(asn_DEF_PrefixFulfillment_tags_1[0]), /* 1 */ + asn_DEF_PrefixFulfillment_tags_1, /* Same as above */ + sizeof(asn_DEF_PrefixFulfillment_tags_1) + /sizeof(asn_DEF_PrefixFulfillment_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_PrefixFulfillment_1, + 3, /* Elements count */ + &asn_SPC_PrefixFulfillment_specs_1 /* Additional specs */ +}; + diff --git a/src/cryptoconditions/src/asn/PrefixFulfillment.h b/src/cryptoconditions/src/asn/PrefixFulfillment.h new file mode 100644 index 000000000..1c06cbc8d --- /dev/null +++ b/src/cryptoconditions/src/asn/PrefixFulfillment.h @@ -0,0 +1,47 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#ifndef _PrefixFulfillment_H_ +#define _PrefixFulfillment_H_ + + +#include + +/* Including external dependencies */ +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward declarations */ +struct Fulfillment; + +/* PrefixFulfillment */ +typedef struct PrefixFulfillment { + OCTET_STRING_t prefix; + unsigned long maxMessageLength; + struct Fulfillment *subfulfillment; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} PrefixFulfillment_t; + +/* Implementation */ +/* extern asn_TYPE_descriptor_t asn_DEF_maxMessageLength_3; // (Use -fall-defs-global to expose) */ +extern asn_TYPE_descriptor_t asn_DEF_PrefixFulfillment; + +#ifdef __cplusplus +} +#endif + +/* Referred external types */ +#include "Fulfillment.h" + +#endif /* _PrefixFulfillment_H_ */ +#include diff --git a/src/cryptoconditions/src/asn/PreimageFulfillment.c b/src/cryptoconditions/src/asn/PreimageFulfillment.c new file mode 100644 index 000000000..e7b918824 --- /dev/null +++ b/src/cryptoconditions/src/asn/PreimageFulfillment.c @@ -0,0 +1,58 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#include "PreimageFulfillment.h" + +static asn_TYPE_member_t asn_MBR_PreimageFulfillment_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct PreimageFulfillment, preimage), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_OCTET_STRING, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "preimage" + }, +}; +static const ber_tlv_tag_t asn_DEF_PreimageFulfillment_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_PreimageFulfillment_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 } /* preimage */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_PreimageFulfillment_specs_1 = { + sizeof(struct PreimageFulfillment), + offsetof(struct PreimageFulfillment, _asn_ctx), + asn_MAP_PreimageFulfillment_tag2el_1, + 1, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_PreimageFulfillment = { + "PreimageFulfillment", + "PreimageFulfillment", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_PreimageFulfillment_tags_1, + sizeof(asn_DEF_PreimageFulfillment_tags_1) + /sizeof(asn_DEF_PreimageFulfillment_tags_1[0]), /* 1 */ + asn_DEF_PreimageFulfillment_tags_1, /* Same as above */ + sizeof(asn_DEF_PreimageFulfillment_tags_1) + /sizeof(asn_DEF_PreimageFulfillment_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_PreimageFulfillment_1, + 1, /* Elements count */ + &asn_SPC_PreimageFulfillment_specs_1 /* Additional specs */ +}; + diff --git a/src/cryptoconditions/src/asn/PreimageFulfillment.h b/src/cryptoconditions/src/asn/PreimageFulfillment.h new file mode 100644 index 000000000..04df5bd3b --- /dev/null +++ b/src/cryptoconditions/src/asn/PreimageFulfillment.h @@ -0,0 +1,37 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#ifndef _PreimageFulfillment_H_ +#define _PreimageFulfillment_H_ + + +#include + +/* Including external dependencies */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* PreimageFulfillment */ +typedef struct PreimageFulfillment { + OCTET_STRING_t preimage; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} PreimageFulfillment_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_PreimageFulfillment; + +#ifdef __cplusplus +} +#endif + +#endif /* _PreimageFulfillment_H_ */ +#include diff --git a/src/cryptoconditions/src/asn/RsaFingerprintContents.c b/src/cryptoconditions/src/asn/RsaFingerprintContents.c new file mode 100644 index 000000000..63d9f5296 --- /dev/null +++ b/src/cryptoconditions/src/asn/RsaFingerprintContents.c @@ -0,0 +1,58 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#include "RsaFingerprintContents.h" + +static asn_TYPE_member_t asn_MBR_RsaFingerprintContents_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct RsaFingerprintContents, modulus), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_OCTET_STRING, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "modulus" + }, +}; +static const ber_tlv_tag_t asn_DEF_RsaFingerprintContents_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_RsaFingerprintContents_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 } /* modulus */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_RsaFingerprintContents_specs_1 = { + sizeof(struct RsaFingerprintContents), + offsetof(struct RsaFingerprintContents, _asn_ctx), + asn_MAP_RsaFingerprintContents_tag2el_1, + 1, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_RsaFingerprintContents = { + "RsaFingerprintContents", + "RsaFingerprintContents", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_RsaFingerprintContents_tags_1, + sizeof(asn_DEF_RsaFingerprintContents_tags_1) + /sizeof(asn_DEF_RsaFingerprintContents_tags_1[0]), /* 1 */ + asn_DEF_RsaFingerprintContents_tags_1, /* Same as above */ + sizeof(asn_DEF_RsaFingerprintContents_tags_1) + /sizeof(asn_DEF_RsaFingerprintContents_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_RsaFingerprintContents_1, + 1, /* Elements count */ + &asn_SPC_RsaFingerprintContents_specs_1 /* Additional specs */ +}; + diff --git a/src/cryptoconditions/src/asn/RsaFingerprintContents.h b/src/cryptoconditions/src/asn/RsaFingerprintContents.h new file mode 100644 index 000000000..8637cd911 --- /dev/null +++ b/src/cryptoconditions/src/asn/RsaFingerprintContents.h @@ -0,0 +1,37 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#ifndef _RsaFingerprintContents_H_ +#define _RsaFingerprintContents_H_ + + +#include + +/* Including external dependencies */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* RsaFingerprintContents */ +typedef struct RsaFingerprintContents { + OCTET_STRING_t modulus; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} RsaFingerprintContents_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_RsaFingerprintContents; + +#ifdef __cplusplus +} +#endif + +#endif /* _RsaFingerprintContents_H_ */ +#include diff --git a/src/cryptoconditions/src/asn/RsaSha256Fulfillment.c b/src/cryptoconditions/src/asn/RsaSha256Fulfillment.c new file mode 100644 index 000000000..3072a6bea --- /dev/null +++ b/src/cryptoconditions/src/asn/RsaSha256Fulfillment.c @@ -0,0 +1,68 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#include "RsaSha256Fulfillment.h" + +static asn_TYPE_member_t asn_MBR_RsaSha256Fulfillment_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct RsaSha256Fulfillment, modulus), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_OCTET_STRING, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "modulus" + }, + { ATF_NOFLAGS, 0, offsetof(struct RsaSha256Fulfillment, signature), + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_OCTET_STRING, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "signature" + }, +}; +static const ber_tlv_tag_t asn_DEF_RsaSha256Fulfillment_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_RsaSha256Fulfillment_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 }, /* modulus */ + { (ASN_TAG_CLASS_CONTEXT | (1 << 2)), 1, 0, 0 } /* signature */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_RsaSha256Fulfillment_specs_1 = { + sizeof(struct RsaSha256Fulfillment), + offsetof(struct RsaSha256Fulfillment, _asn_ctx), + asn_MAP_RsaSha256Fulfillment_tag2el_1, + 2, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_RsaSha256Fulfillment = { + "RsaSha256Fulfillment", + "RsaSha256Fulfillment", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_RsaSha256Fulfillment_tags_1, + sizeof(asn_DEF_RsaSha256Fulfillment_tags_1) + /sizeof(asn_DEF_RsaSha256Fulfillment_tags_1[0]), /* 1 */ + asn_DEF_RsaSha256Fulfillment_tags_1, /* Same as above */ + sizeof(asn_DEF_RsaSha256Fulfillment_tags_1) + /sizeof(asn_DEF_RsaSha256Fulfillment_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_RsaSha256Fulfillment_1, + 2, /* Elements count */ + &asn_SPC_RsaSha256Fulfillment_specs_1 /* Additional specs */ +}; + diff --git a/src/cryptoconditions/src/asn/RsaSha256Fulfillment.h b/src/cryptoconditions/src/asn/RsaSha256Fulfillment.h new file mode 100644 index 000000000..fda524276 --- /dev/null +++ b/src/cryptoconditions/src/asn/RsaSha256Fulfillment.h @@ -0,0 +1,38 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#ifndef _RsaSha256Fulfillment_H_ +#define _RsaSha256Fulfillment_H_ + + +#include + +/* Including external dependencies */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* RsaSha256Fulfillment */ +typedef struct RsaSha256Fulfillment { + OCTET_STRING_t modulus; + OCTET_STRING_t signature; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} RsaSha256Fulfillment_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_RsaSha256Fulfillment; + +#ifdef __cplusplus +} +#endif + +#endif /* _RsaSha256Fulfillment_H_ */ +#include diff --git a/src/cryptoconditions/src/asn/Secp256k1FingerprintContents.c b/src/cryptoconditions/src/asn/Secp256k1FingerprintContents.c new file mode 100644 index 000000000..e31887f75 --- /dev/null +++ b/src/cryptoconditions/src/asn/Secp256k1FingerprintContents.c @@ -0,0 +1,84 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#include "Secp256k1FingerprintContents.h" + +static int +memb_publicKey_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const OCTET_STRING_t *st = (const OCTET_STRING_t *)sptr; + size_t size; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + size = st->size; + + if((size == 33)) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static asn_TYPE_member_t asn_MBR_Secp256k1FingerprintContents_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct Secp256k1FingerprintContents, publicKey), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_OCTET_STRING, + memb_publicKey_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "publicKey" + }, +}; +static const ber_tlv_tag_t asn_DEF_Secp256k1FingerprintContents_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_Secp256k1FingerprintContents_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 } /* publicKey */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_Secp256k1FingerprintContents_specs_1 = { + sizeof(struct Secp256k1FingerprintContents), + offsetof(struct Secp256k1FingerprintContents, _asn_ctx), + asn_MAP_Secp256k1FingerprintContents_tag2el_1, + 1, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_Secp256k1FingerprintContents = { + "Secp256k1FingerprintContents", + "Secp256k1FingerprintContents", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_Secp256k1FingerprintContents_tags_1, + sizeof(asn_DEF_Secp256k1FingerprintContents_tags_1) + /sizeof(asn_DEF_Secp256k1FingerprintContents_tags_1[0]), /* 1 */ + asn_DEF_Secp256k1FingerprintContents_tags_1, /* Same as above */ + sizeof(asn_DEF_Secp256k1FingerprintContents_tags_1) + /sizeof(asn_DEF_Secp256k1FingerprintContents_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_Secp256k1FingerprintContents_1, + 1, /* Elements count */ + &asn_SPC_Secp256k1FingerprintContents_specs_1 /* Additional specs */ +}; + diff --git a/src/cryptoconditions/src/asn/Secp256k1FingerprintContents.h b/src/cryptoconditions/src/asn/Secp256k1FingerprintContents.h new file mode 100644 index 000000000..d0a3b0b39 --- /dev/null +++ b/src/cryptoconditions/src/asn/Secp256k1FingerprintContents.h @@ -0,0 +1,37 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#ifndef _Secp256k1FingerprintContents_H_ +#define _Secp256k1FingerprintContents_H_ + + +#include + +/* Including external dependencies */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Secp256k1FingerprintContents */ +typedef struct Secp256k1FingerprintContents { + OCTET_STRING_t publicKey; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} Secp256k1FingerprintContents_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_Secp256k1FingerprintContents; + +#ifdef __cplusplus +} +#endif + +#endif /* _Secp256k1FingerprintContents_H_ */ +#include diff --git a/src/cryptoconditions/src/asn/Secp256k1Fulfillment.c b/src/cryptoconditions/src/asn/Secp256k1Fulfillment.c new file mode 100644 index 000000000..cf8d65512 --- /dev/null +++ b/src/cryptoconditions/src/asn/Secp256k1Fulfillment.c @@ -0,0 +1,120 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#include "Secp256k1Fulfillment.h" + +static int +memb_publicKey_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const OCTET_STRING_t *st = (const OCTET_STRING_t *)sptr; + size_t size; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + size = st->size; + + if((size == 33)) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static int +memb_signature_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const OCTET_STRING_t *st = (const OCTET_STRING_t *)sptr; + size_t size; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + size = st->size; + + if((size == 64)) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static asn_TYPE_member_t asn_MBR_Secp256k1Fulfillment_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct Secp256k1Fulfillment, publicKey), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_OCTET_STRING, + memb_publicKey_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "publicKey" + }, + { ATF_NOFLAGS, 0, offsetof(struct Secp256k1Fulfillment, signature), + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_OCTET_STRING, + memb_signature_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "signature" + }, +}; +static const ber_tlv_tag_t asn_DEF_Secp256k1Fulfillment_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_Secp256k1Fulfillment_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 }, /* publicKey */ + { (ASN_TAG_CLASS_CONTEXT | (1 << 2)), 1, 0, 0 } /* signature */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_Secp256k1Fulfillment_specs_1 = { + sizeof(struct Secp256k1Fulfillment), + offsetof(struct Secp256k1Fulfillment, _asn_ctx), + asn_MAP_Secp256k1Fulfillment_tag2el_1, + 2, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_Secp256k1Fulfillment = { + "Secp256k1Fulfillment", + "Secp256k1Fulfillment", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_Secp256k1Fulfillment_tags_1, + sizeof(asn_DEF_Secp256k1Fulfillment_tags_1) + /sizeof(asn_DEF_Secp256k1Fulfillment_tags_1[0]), /* 1 */ + asn_DEF_Secp256k1Fulfillment_tags_1, /* Same as above */ + sizeof(asn_DEF_Secp256k1Fulfillment_tags_1) + /sizeof(asn_DEF_Secp256k1Fulfillment_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_Secp256k1Fulfillment_1, + 2, /* Elements count */ + &asn_SPC_Secp256k1Fulfillment_specs_1 /* Additional specs */ +}; + diff --git a/src/cryptoconditions/src/asn/Secp256k1Fulfillment.h b/src/cryptoconditions/src/asn/Secp256k1Fulfillment.h new file mode 100644 index 000000000..79221317f --- /dev/null +++ b/src/cryptoconditions/src/asn/Secp256k1Fulfillment.h @@ -0,0 +1,38 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#ifndef _Secp256k1Fulfillment_H_ +#define _Secp256k1Fulfillment_H_ + + +#include + +/* Including external dependencies */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Secp256k1Fulfillment */ +typedef struct Secp256k1Fulfillment { + OCTET_STRING_t publicKey; + OCTET_STRING_t signature; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} Secp256k1Fulfillment_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_Secp256k1Fulfillment; + +#ifdef __cplusplus +} +#endif + +#endif /* _Secp256k1Fulfillment_H_ */ +#include diff --git a/src/cryptoconditions/src/asn/SimpleSha256Condition.c b/src/cryptoconditions/src/asn/SimpleSha256Condition.c new file mode 100644 index 000000000..17c45a5fb --- /dev/null +++ b/src/cryptoconditions/src/asn/SimpleSha256Condition.c @@ -0,0 +1,225 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#include "SimpleSha256Condition.h" + +static int +cost_3_constraint(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + + /* Constraint check succeeded */ + return 0; +} + +/* + * This type is implemented using NativeInteger, + * so here we adjust the DEF accordingly. + */ +static void +cost_3_inherit_TYPE_descriptor(asn_TYPE_descriptor_t *td) { + td->free_struct = asn_DEF_NativeInteger.free_struct; + td->print_struct = asn_DEF_NativeInteger.print_struct; + td->check_constraints = asn_DEF_NativeInteger.check_constraints; + td->ber_decoder = asn_DEF_NativeInteger.ber_decoder; + td->der_encoder = asn_DEF_NativeInteger.der_encoder; + td->xer_decoder = asn_DEF_NativeInteger.xer_decoder; + td->xer_encoder = asn_DEF_NativeInteger.xer_encoder; + td->uper_decoder = asn_DEF_NativeInteger.uper_decoder; + td->uper_encoder = asn_DEF_NativeInteger.uper_encoder; + if(!td->per_constraints) + td->per_constraints = asn_DEF_NativeInteger.per_constraints; + td->elements = asn_DEF_NativeInteger.elements; + td->elements_count = asn_DEF_NativeInteger.elements_count; + /* td->specifics = asn_DEF_NativeInteger.specifics; // Defined explicitly */ +} + +static void +cost_3_free(asn_TYPE_descriptor_t *td, + void *struct_ptr, int contents_only) { + cost_3_inherit_TYPE_descriptor(td); + td->free_struct(td, struct_ptr, contents_only); +} + +static int +cost_3_print(asn_TYPE_descriptor_t *td, const void *struct_ptr, + int ilevel, asn_app_consume_bytes_f *cb, void *app_key) { + cost_3_inherit_TYPE_descriptor(td); + return td->print_struct(td, struct_ptr, ilevel, cb, app_key); +} + +static asn_dec_rval_t +cost_3_decode_ber(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **structure, const void *bufptr, size_t size, int tag_mode) { + cost_3_inherit_TYPE_descriptor(td); + return td->ber_decoder(opt_codec_ctx, td, structure, bufptr, size, tag_mode); +} + +static asn_enc_rval_t +cost_3_encode_der(asn_TYPE_descriptor_t *td, + void *structure, int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + cost_3_inherit_TYPE_descriptor(td); + return td->der_encoder(td, structure, tag_mode, tag, cb, app_key); +} + +static asn_dec_rval_t +cost_3_decode_xer(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **structure, const char *opt_mname, const void *bufptr, size_t size) { + cost_3_inherit_TYPE_descriptor(td); + return td->xer_decoder(opt_codec_ctx, td, structure, opt_mname, bufptr, size); +} + +static asn_enc_rval_t +cost_3_encode_xer(asn_TYPE_descriptor_t *td, void *structure, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + cost_3_inherit_TYPE_descriptor(td); + return td->xer_encoder(td, structure, ilevel, flags, cb, app_key); +} + +static int +memb_fingerprint_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const OCTET_STRING_t *st = (const OCTET_STRING_t *)sptr; + size_t size; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + size = st->size; + + if((size == 32)) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static int +memb_cost_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + + /* Constraint check succeeded */ + return 0; +} + +static const asn_INTEGER_specifics_t asn_SPC_cost_specs_3 = { + 0, 0, 0, 0, 0, + 0, /* Native long size */ + 1 /* Unsigned representation */ +}; +static const ber_tlv_tag_t asn_DEF_cost_tags_3[] = { + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + (ASN_TAG_CLASS_UNIVERSAL | (2 << 2)) +}; +static /* Use -fall-defs-global to expose */ +asn_TYPE_descriptor_t asn_DEF_cost_3 = { + "cost", + "cost", + cost_3_free, + cost_3_print, + cost_3_constraint, + cost_3_decode_ber, + cost_3_encode_der, + cost_3_decode_xer, + cost_3_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_cost_tags_3, + sizeof(asn_DEF_cost_tags_3) + /sizeof(asn_DEF_cost_tags_3[0]) - 1, /* 1 */ + asn_DEF_cost_tags_3, /* Same as above */ + sizeof(asn_DEF_cost_tags_3) + /sizeof(asn_DEF_cost_tags_3[0]), /* 2 */ + 0, /* No PER visible constraints */ + 0, 0, /* No members */ + &asn_SPC_cost_specs_3 /* Additional specs */ +}; + +static asn_TYPE_member_t asn_MBR_SimpleSha256Condition_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct SimpleSha256Condition, fingerprint), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_OCTET_STRING, + memb_fingerprint_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "fingerprint" + }, + { ATF_NOFLAGS, 0, offsetof(struct SimpleSha256Condition, cost), + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_cost_3, + memb_cost_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "cost" + }, +}; +static const ber_tlv_tag_t asn_DEF_SimpleSha256Condition_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_SimpleSha256Condition_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 }, /* fingerprint */ + { (ASN_TAG_CLASS_CONTEXT | (1 << 2)), 1, 0, 0 } /* cost */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_SimpleSha256Condition_specs_1 = { + sizeof(struct SimpleSha256Condition), + offsetof(struct SimpleSha256Condition, _asn_ctx), + asn_MAP_SimpleSha256Condition_tag2el_1, + 2, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_SimpleSha256Condition = { + "SimpleSha256Condition", + "SimpleSha256Condition", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_SimpleSha256Condition_tags_1, + sizeof(asn_DEF_SimpleSha256Condition_tags_1) + /sizeof(asn_DEF_SimpleSha256Condition_tags_1[0]), /* 1 */ + asn_DEF_SimpleSha256Condition_tags_1, /* Same as above */ + sizeof(asn_DEF_SimpleSha256Condition_tags_1) + /sizeof(asn_DEF_SimpleSha256Condition_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_SimpleSha256Condition_1, + 2, /* Elements count */ + &asn_SPC_SimpleSha256Condition_specs_1 /* Additional specs */ +}; + diff --git a/src/cryptoconditions/src/asn/SimpleSha256Condition.h b/src/cryptoconditions/src/asn/SimpleSha256Condition.h new file mode 100644 index 000000000..4ca08877b --- /dev/null +++ b/src/cryptoconditions/src/asn/SimpleSha256Condition.h @@ -0,0 +1,40 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#ifndef _SimpleSha256Condition_H_ +#define _SimpleSha256Condition_H_ + + +#include + +/* Including external dependencies */ +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* SimpleSha256Condition */ +typedef struct SimpleSha256Condition { + OCTET_STRING_t fingerprint; + unsigned long cost; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} SimpleSha256Condition_t; + +/* Implementation */ +/* extern asn_TYPE_descriptor_t asn_DEF_cost_3; // (Use -fall-defs-global to expose) */ +extern asn_TYPE_descriptor_t asn_DEF_SimpleSha256Condition; + +#ifdef __cplusplus +} +#endif + +#endif /* _SimpleSha256Condition_H_ */ +#include diff --git a/src/cryptoconditions/src/asn/ThresholdFingerprintContents.c b/src/cryptoconditions/src/asn/ThresholdFingerprintContents.c new file mode 100644 index 000000000..b4e05ac67 --- /dev/null +++ b/src/cryptoconditions/src/asn/ThresholdFingerprintContents.c @@ -0,0 +1,138 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#include "ThresholdFingerprintContents.h" + +static int +memb_threshold_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + long value; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + value = *(const long *)sptr; + + if((value >= 1 && value <= 65535)) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static asn_TYPE_member_t asn_MBR_subconditions2_3[] = { + { ATF_POINTER, 0, 0, + -1 /* Ambiguous tag (CHOICE?) */, + 0, + &asn_DEF_Condition, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "" + }, +}; +static const ber_tlv_tag_t asn_DEF_subconditions2_tags_3[] = { + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + (ASN_TAG_CLASS_UNIVERSAL | (17 << 2)) +}; +static asn_SET_OF_specifics_t asn_SPC_subconditions2_specs_3 = { + sizeof(struct subconditions2), + offsetof(struct subconditions2, _asn_ctx), + 2, /* XER encoding is XMLValueList */ +}; +static /* Use -fall-defs-global to expose */ +asn_TYPE_descriptor_t asn_DEF_subconditions2_3 = { + "subconditions2", + "subconditions2", + SET_OF_free, + SET_OF_print, + SET_OF_constraint, + SET_OF_decode_ber, + SET_OF_encode_der, + SET_OF_decode_xer, + SET_OF_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_subconditions2_tags_3, + sizeof(asn_DEF_subconditions2_tags_3) + /sizeof(asn_DEF_subconditions2_tags_3[0]) - 1, /* 1 */ + asn_DEF_subconditions2_tags_3, /* Same as above */ + sizeof(asn_DEF_subconditions2_tags_3) + /sizeof(asn_DEF_subconditions2_tags_3[0]), /* 2 */ + 0, /* No PER visible constraints */ + asn_MBR_subconditions2_3, + 1, /* Single element */ + &asn_SPC_subconditions2_specs_3 /* Additional specs */ +}; + +static asn_TYPE_member_t asn_MBR_ThresholdFingerprintContents_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct ThresholdFingerprintContents, threshold), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_NativeInteger, + memb_threshold_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "threshold" + }, + { ATF_NOFLAGS, 0, offsetof(struct ThresholdFingerprintContents, subconditions2), + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + 0, + &asn_DEF_subconditions2_3, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "subconditions2" + }, +}; +static const ber_tlv_tag_t asn_DEF_ThresholdFingerprintContents_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_ThresholdFingerprintContents_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 }, /* threshold */ + { (ASN_TAG_CLASS_CONTEXT | (1 << 2)), 1, 0, 0 } /* subconditions2 */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_ThresholdFingerprintContents_specs_1 = { + sizeof(struct ThresholdFingerprintContents), + offsetof(struct ThresholdFingerprintContents, _asn_ctx), + asn_MAP_ThresholdFingerprintContents_tag2el_1, + 2, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_ThresholdFingerprintContents = { + "ThresholdFingerprintContents", + "ThresholdFingerprintContents", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_ThresholdFingerprintContents_tags_1, + sizeof(asn_DEF_ThresholdFingerprintContents_tags_1) + /sizeof(asn_DEF_ThresholdFingerprintContents_tags_1[0]), /* 1 */ + asn_DEF_ThresholdFingerprintContents_tags_1, /* Same as above */ + sizeof(asn_DEF_ThresholdFingerprintContents_tags_1) + /sizeof(asn_DEF_ThresholdFingerprintContents_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_ThresholdFingerprintContents_1, + 2, /* Elements count */ + &asn_SPC_ThresholdFingerprintContents_specs_1 /* Additional specs */ +}; + diff --git a/src/cryptoconditions/src/asn/ThresholdFingerprintContents.h b/src/cryptoconditions/src/asn/ThresholdFingerprintContents.h new file mode 100644 index 000000000..4d1c4f491 --- /dev/null +++ b/src/cryptoconditions/src/asn/ThresholdFingerprintContents.h @@ -0,0 +1,51 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#ifndef _ThresholdFingerprintContents_H_ +#define _ThresholdFingerprintContents_H_ + + +#include + +/* Including external dependencies */ +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward declarations */ +struct Condition; + +/* ThresholdFingerprintContents */ +typedef struct ThresholdFingerprintContents { + long threshold; + struct subconditions2 { + A_SET_OF(struct Condition) list; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; + } subconditions2; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} ThresholdFingerprintContents_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_ThresholdFingerprintContents; + +#ifdef __cplusplus +} +#endif + +/* Referred external types */ +#include "Condition.h" + +#endif /* _ThresholdFingerprintContents_H_ */ +#include diff --git a/src/cryptoconditions/src/asn/ThresholdFulfillment.c b/src/cryptoconditions/src/asn/ThresholdFulfillment.c new file mode 100644 index 000000000..28c5e36a8 --- /dev/null +++ b/src/cryptoconditions/src/asn/ThresholdFulfillment.c @@ -0,0 +1,158 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#include "ThresholdFulfillment.h" + +static asn_TYPE_member_t asn_MBR_subfulfillments_2[] = { + { ATF_POINTER, 0, 0, + -1 /* Ambiguous tag (CHOICE?) */, + 0, + &asn_DEF_Fulfillment, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "" + }, +}; +static const ber_tlv_tag_t asn_DEF_subfulfillments_tags_2[] = { + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + (ASN_TAG_CLASS_UNIVERSAL | (17 << 2)) +}; +static asn_SET_OF_specifics_t asn_SPC_subfulfillments_specs_2 = { + sizeof(struct subfulfillments), + offsetof(struct subfulfillments, _asn_ctx), + 2, /* XER encoding is XMLValueList */ +}; +static /* Use -fall-defs-global to expose */ +asn_TYPE_descriptor_t asn_DEF_subfulfillments_2 = { + "subfulfillments", + "subfulfillments", + SET_OF_free, + SET_OF_print, + SET_OF_constraint, + SET_OF_decode_ber, + SET_OF_encode_der, + SET_OF_decode_xer, + SET_OF_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_subfulfillments_tags_2, + sizeof(asn_DEF_subfulfillments_tags_2) + /sizeof(asn_DEF_subfulfillments_tags_2[0]) - 1, /* 1 */ + asn_DEF_subfulfillments_tags_2, /* Same as above */ + sizeof(asn_DEF_subfulfillments_tags_2) + /sizeof(asn_DEF_subfulfillments_tags_2[0]), /* 2 */ + 0, /* No PER visible constraints */ + asn_MBR_subfulfillments_2, + 1, /* Single element */ + &asn_SPC_subfulfillments_specs_2 /* Additional specs */ +}; + +static asn_TYPE_member_t asn_MBR_subconditions_4[] = { + { ATF_POINTER, 0, 0, + -1 /* Ambiguous tag (CHOICE?) */, + 0, + &asn_DEF_Condition, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "" + }, +}; +static const ber_tlv_tag_t asn_DEF_subconditions_tags_4[] = { + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + (ASN_TAG_CLASS_UNIVERSAL | (17 << 2)) +}; +static asn_SET_OF_specifics_t asn_SPC_subconditions_specs_4 = { + sizeof(struct subconditions), + offsetof(struct subconditions, _asn_ctx), + 2, /* XER encoding is XMLValueList */ +}; +static /* Use -fall-defs-global to expose */ +asn_TYPE_descriptor_t asn_DEF_subconditions_4 = { + "subconditions", + "subconditions", + SET_OF_free, + SET_OF_print, + SET_OF_constraint, + SET_OF_decode_ber, + SET_OF_encode_der, + SET_OF_decode_xer, + SET_OF_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_subconditions_tags_4, + sizeof(asn_DEF_subconditions_tags_4) + /sizeof(asn_DEF_subconditions_tags_4[0]) - 1, /* 1 */ + asn_DEF_subconditions_tags_4, /* Same as above */ + sizeof(asn_DEF_subconditions_tags_4) + /sizeof(asn_DEF_subconditions_tags_4[0]), /* 2 */ + 0, /* No PER visible constraints */ + asn_MBR_subconditions_4, + 1, /* Single element */ + &asn_SPC_subconditions_specs_4 /* Additional specs */ +}; + +static asn_TYPE_member_t asn_MBR_ThresholdFulfillment_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct ThresholdFulfillment, subfulfillments), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + 0, + &asn_DEF_subfulfillments_2, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "subfulfillments" + }, + { ATF_NOFLAGS, 0, offsetof(struct ThresholdFulfillment, subconditions), + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + 0, + &asn_DEF_subconditions_4, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "subconditions" + }, +}; +static const ber_tlv_tag_t asn_DEF_ThresholdFulfillment_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_ThresholdFulfillment_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 }, /* subfulfillments */ + { (ASN_TAG_CLASS_CONTEXT | (1 << 2)), 1, 0, 0 } /* subconditions */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_ThresholdFulfillment_specs_1 = { + sizeof(struct ThresholdFulfillment), + offsetof(struct ThresholdFulfillment, _asn_ctx), + asn_MAP_ThresholdFulfillment_tag2el_1, + 2, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_ThresholdFulfillment = { + "ThresholdFulfillment", + "ThresholdFulfillment", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_ThresholdFulfillment_tags_1, + sizeof(asn_DEF_ThresholdFulfillment_tags_1) + /sizeof(asn_DEF_ThresholdFulfillment_tags_1[0]), /* 1 */ + asn_DEF_ThresholdFulfillment_tags_1, /* Same as above */ + sizeof(asn_DEF_ThresholdFulfillment_tags_1) + /sizeof(asn_DEF_ThresholdFulfillment_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_ThresholdFulfillment_1, + 2, /* Elements count */ + &asn_SPC_ThresholdFulfillment_specs_1 /* Additional specs */ +}; + diff --git a/src/cryptoconditions/src/asn/ThresholdFulfillment.h b/src/cryptoconditions/src/asn/ThresholdFulfillment.h new file mode 100644 index 000000000..e00ae5f2b --- /dev/null +++ b/src/cryptoconditions/src/asn/ThresholdFulfillment.h @@ -0,0 +1,57 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "Crypto-Conditions" + * found in "CryptoConditions.asn" + */ + +#ifndef _ThresholdFulfillment_H_ +#define _ThresholdFulfillment_H_ + + +#include + +/* Including external dependencies */ +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward declarations */ +struct Fulfillment; +struct Condition; + +/* ThresholdFulfillment */ +typedef struct ThresholdFulfillment { + struct subfulfillments { + A_SET_OF(struct Fulfillment) list; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; + } subfulfillments; + struct subconditions { + A_SET_OF(struct Condition) list; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; + } subconditions; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} ThresholdFulfillment_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_ThresholdFulfillment; + +#ifdef __cplusplus +} +#endif + +/* Referred external types */ +#include "Fulfillment.h" +#include "Condition.h" + +#endif /* _ThresholdFulfillment_H_ */ +#include diff --git a/src/cryptoconditions/src/asn/asn_SET_OF.c b/src/cryptoconditions/src/asn/asn_SET_OF.c new file mode 100644 index 000000000..944f2cb8a --- /dev/null +++ b/src/cryptoconditions/src/asn/asn_SET_OF.c @@ -0,0 +1,88 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +/* + * Add another element into the set. + */ +int +asn_set_add(void *asn_set_of_x, void *ptr) { + asn_anonymous_set_ *as = _A_SET_FROM_VOID(asn_set_of_x); + + if(as == 0 || ptr == 0) { + errno = EINVAL; /* Invalid arguments */ + return -1; + } + + /* + * Make sure there's enough space to insert an element. + */ + if(as->count == as->size) { + int _newsize = as->size ? (as->size << 1) : 4; + void *_new_arr; + _new_arr = REALLOC(as->array, _newsize * sizeof(as->array[0])); + if(_new_arr) { + as->array = (void **)_new_arr; + as->size = _newsize; + } else { + /* ENOMEM */ + return -1; + } + } + + as->array[as->count++] = ptr; + + return 0; +} + +void +asn_set_del(void *asn_set_of_x, int number, int _do_free) { + asn_anonymous_set_ *as = _A_SET_FROM_VOID(asn_set_of_x); + + if(as) { + void *ptr; + if(number < 0 || number >= as->count) + return; + + if(_do_free && as->free) { + ptr = as->array[number]; + } else { + ptr = 0; + } + + as->array[number] = as->array[--as->count]; + + /* + * Invoke the third-party function only when the state + * of the parent structure is consistent. + */ + if(ptr) as->free(ptr); + } +} + +/* + * Free the contents of the set, do not free the set itself. + */ +void +asn_set_empty(void *asn_set_of_x) { + asn_anonymous_set_ *as = _A_SET_FROM_VOID(asn_set_of_x); + + if(as) { + if(as->array) { + if(as->free) { + while(as->count--) + as->free(as->array[as->count]); + } + FREEMEM(as->array); + as->array = 0; + } + as->count = 0; + as->size = 0; + } + +} + diff --git a/src/cryptoconditions/src/asn/asn_SET_OF.h b/src/cryptoconditions/src/asn/asn_SET_OF.h new file mode 100644 index 000000000..7edf14b51 --- /dev/null +++ b/src/cryptoconditions/src/asn/asn_SET_OF.h @@ -0,0 +1,62 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef ASN_SET_OF_H +#define ASN_SET_OF_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define A_SET_OF(type) \ + struct { \ + type **array; \ + int count; /* Meaningful size */ \ + int size; /* Allocated size */ \ + void (*free)(type *); \ + } + +#define ASN_SET_ADD(headptr, ptr) \ + asn_set_add((headptr), (ptr)) + +/******************************************* + * Implementation of the SET OF structure. + */ + +/* + * Add another structure into the set by its pointer. + * RETURN VALUES: + * 0 for success and -1/errno for failure. + */ +int asn_set_add(void *asn_set_of_x, void *ptr); + +/* + * Delete the element from the set by its number (base 0). + * This is a constant-time operation. The order of elements before the + * deleted ones is guaranteed, the order of elements after the deleted + * one is NOT guaranteed. + * If _do_free is given AND the (*free) is initialized, the element + * will be freed using the custom (*free) function as well. + */ +void asn_set_del(void *asn_set_of_x, int number, int _do_free); + +/* + * Empty the contents of the set. Will free the elements, if (*free) is given. + * Will NOT free the set itself. + */ +void asn_set_empty(void *asn_set_of_x); + +/* + * Cope with different conversions requirements to/from void in C and C++. + * This is mostly useful for support library. + */ +typedef A_SET_OF(void) asn_anonymous_set_; +#define _A_SET_FROM_VOID(ptr) ((asn_anonymous_set_ *)(ptr)) +#define _A_CSET_FROM_VOID(ptr) ((const asn_anonymous_set_ *)(ptr)) + +#ifdef __cplusplus +} +#endif + +#endif /* ASN_SET_OF_H */ diff --git a/src/cryptoconditions/src/asn/asn_application.h b/src/cryptoconditions/src/asn/asn_application.h new file mode 100644 index 000000000..71e9ba61b --- /dev/null +++ b/src/cryptoconditions/src/asn/asn_application.h @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 2004, 2006 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +/* + * Application-level ASN.1 callbacks. + */ +#ifndef ASN_APPLICATION_H +#define ASN_APPLICATION_H + +#include "asn_system.h" /* for platform-dependent types */ +#include "asn_codecs.h" /* for ASN.1 codecs specifics */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Generic type of an application-defined callback to return various + * types of data to the application. + * EXPECTED RETURN VALUES: + * -1: Failed to consume bytes. Abort the mission. + * Non-negative return values indicate success, and ignored. + */ +typedef int (asn_app_consume_bytes_f)(const void *buffer, size_t size, + void *application_specific_key); + +/* + * A callback of this type is called whenever constraint validation fails + * on some ASN.1 type. See "constraints.h" for more details on constraint + * validation. + * This callback specifies a descriptor of the ASN.1 type which failed + * the constraint check, as well as human readable message on what + * particular constraint has failed. + */ +typedef void (asn_app_constraint_failed_f)(void *application_specific_key, + struct asn_TYPE_descriptor_s *type_descriptor_which_failed, + const void *structure_which_failed_ptr, + const char *error_message_format, ...) GCC_PRINTFLIKE(4, 5); + +#ifdef __cplusplus +} +#endif + +#include "constr_TYPE.h" /* for asn_TYPE_descriptor_t */ + +#endif /* ASN_APPLICATION_H */ diff --git a/src/cryptoconditions/src/asn/asn_codecs.h b/src/cryptoconditions/src/asn/asn_codecs.h new file mode 100644 index 000000000..4b2a29429 --- /dev/null +++ b/src/cryptoconditions/src/asn/asn_codecs.h @@ -0,0 +1,109 @@ +/*- + * Copyright (c) 2003, 2004, 2005 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef ASN_CODECS_H +#define ASN_CODECS_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct asn_TYPE_descriptor_s; /* Forward declaration */ + +/* + * This structure defines a set of parameters that may be passed + * to every ASN.1 encoder or decoder function. + * WARNING: if max_stack_size member is set, and you are calling the + * function pointers of the asn_TYPE_descriptor_t directly, + * this structure must be ALLOCATED ON THE STACK! + * If you can't always satisfy this requirement, use ber_decode(), + * xer_decode() and uper_decode() functions instead. + */ +typedef struct asn_codec_ctx_s { + /* + * Limit the decoder routines to use no (much) more stack than a given + * number of bytes. Most of decoders are stack-based, and this + * would protect against stack overflows if the number of nested + * encodings is high. + * The OCTET STRING, BIT STRING and ANY BER decoders are heap-based, + * and are safe from this kind of overflow. + * A value from getrlimit(RLIMIT_STACK) may be used to initialize + * this variable. Be careful in multithreaded environments, as the + * stack size is rather limited. + */ + size_t max_stack_size; /* 0 disables stack bounds checking */ +} asn_codec_ctx_t; + +/* + * Type of the return value of the encoding functions (der_encode, xer_encode). + */ +typedef struct asn_enc_rval_s { + /* + * Number of bytes encoded. + * -1 indicates failure to encode the structure. + * In this case, the members below this one are meaningful. + */ + ssize_t encoded; + + /* + * Members meaningful when (encoded == -1), for post mortem analysis. + */ + + /* Type which cannot be encoded */ + struct asn_TYPE_descriptor_s *failed_type; + + /* Pointer to the structure of that type */ + void *structure_ptr; +} asn_enc_rval_t; +#define ASN__ENCODE_FAILED do { \ + asn_enc_rval_t tmp_error; \ + tmp_error.encoded = -1; \ + tmp_error.failed_type = td; \ + tmp_error.structure_ptr = sptr; \ + ASN_DEBUG("Failed to encode element %s", td ? td->name : ""); \ + return tmp_error; \ +} while(0) +#define ASN__ENCODED_OK(rval) do { \ + rval.structure_ptr = 0; \ + rval.failed_type = 0; \ + return rval; \ +} while(0) + +/* + * Type of the return value of the decoding functions (ber_decode, xer_decode) + * + * Please note that the number of consumed bytes is ALWAYS meaningful, + * even if code==RC_FAIL. This is to indicate the number of successfully + * decoded bytes, hence providing a possibility to fail with more diagnostics + * (i.e., print the offending remainder of the buffer). + */ +enum asn_dec_rval_code_e { + RC_OK, /* Decoded successfully */ + RC_WMORE, /* More data expected, call again */ + RC_FAIL /* Failure to decode data */ +}; +typedef struct asn_dec_rval_s { + enum asn_dec_rval_code_e code; /* Result code */ + size_t consumed; /* Number of bytes consumed */ +} asn_dec_rval_t; +#define ASN__DECODE_FAILED do { \ + asn_dec_rval_t tmp_error; \ + tmp_error.code = RC_FAIL; \ + tmp_error.consumed = 0; \ + ASN_DEBUG("Failed to decode element %s", td ? td->name : ""); \ + return tmp_error; \ +} while(0) +#define ASN__DECODE_STARVED do { \ + asn_dec_rval_t tmp_error; \ + tmp_error.code = RC_WMORE; \ + tmp_error.consumed = 0; \ + return tmp_error; \ +} while(0) + +#ifdef __cplusplus +} +#endif + +#endif /* ASN_CODECS_H */ diff --git a/src/cryptoconditions/src/asn/asn_codecs_prim.c b/src/cryptoconditions/src/asn/asn_codecs_prim.c new file mode 100644 index 000000000..426339c94 --- /dev/null +++ b/src/cryptoconditions/src/asn/asn_codecs_prim.c @@ -0,0 +1,312 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +/* + * Decode an always-primitive type. + */ +asn_dec_rval_t +ber_decode_primitive(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, + void **sptr, const void *buf_ptr, size_t size, int tag_mode) { + ASN__PRIMITIVE_TYPE_t *st = (ASN__PRIMITIVE_TYPE_t *)*sptr; + asn_dec_rval_t rval; + ber_tlv_len_t length = 0; /* =0 to avoid [incorrect] warning. */ + + /* + * If the structure is not there, allocate it. + */ + if(st == NULL) { + st = (ASN__PRIMITIVE_TYPE_t *)CALLOC(1, sizeof(*st)); + if(st == NULL) ASN__DECODE_FAILED; + *sptr = (void *)st; + } + + ASN_DEBUG("Decoding %s as plain primitive (tm=%d)", + td->name, tag_mode); + + /* + * Check tags and extract value length. + */ + rval = ber_check_tags(opt_codec_ctx, td, 0, buf_ptr, size, + tag_mode, 0, &length, 0); + if(rval.code != RC_OK) + return rval; + + ASN_DEBUG("%s length is %d bytes", td->name, (int)length); + + /* + * Make sure we have this length. + */ + buf_ptr = ((const char *)buf_ptr) + rval.consumed; + size -= rval.consumed; + if(length > (ber_tlv_len_t)size) { + rval.code = RC_WMORE; + rval.consumed = 0; + return rval; + } + + st->size = (int)length; + /* The following better be optimized away. */ + if(sizeof(st->size) != sizeof(length) + && (ber_tlv_len_t)st->size != length) { + st->size = 0; + ASN__DECODE_FAILED; + } + + st->buf = (uint8_t *)MALLOC(length + 1); + if(!st->buf) { + st->size = 0; + ASN__DECODE_FAILED; + } + + memcpy(st->buf, buf_ptr, length); + st->buf[length] = '\0'; /* Just in case */ + + rval.code = RC_OK; + rval.consumed += length; + + ASN_DEBUG("Took %ld/%ld bytes to encode %s", + (long)rval.consumed, + (long)length, td->name); + + return rval; +} + +/* + * Encode an always-primitive type using DER. + */ +asn_enc_rval_t +der_encode_primitive(asn_TYPE_descriptor_t *td, void *sptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_enc_rval_t erval; + ASN__PRIMITIVE_TYPE_t *st = (ASN__PRIMITIVE_TYPE_t *)sptr; + + ASN_DEBUG("%s %s as a primitive type (tm=%d)", + cb?"Encoding":"Estimating", td->name, tag_mode); + + erval.encoded = der_write_tags(td, st->size, tag_mode, 0, tag, + cb, app_key); + ASN_DEBUG("%s wrote tags %d", td->name, (int)erval.encoded); + if(erval.encoded == -1) { + erval.failed_type = td; + erval.structure_ptr = sptr; + return erval; + } + + if(cb && st->buf) { + if(cb(st->buf, st->size, app_key) < 0) { + erval.encoded = -1; + erval.failed_type = td; + erval.structure_ptr = sptr; + return erval; + } + } else { + assert(st->buf || st->size == 0); + } + + erval.encoded += st->size; + ASN__ENCODED_OK(erval); +} + +void +ASN__PRIMITIVE_TYPE_free(asn_TYPE_descriptor_t *td, void *sptr, + int contents_only) { + ASN__PRIMITIVE_TYPE_t *st = (ASN__PRIMITIVE_TYPE_t *)sptr; + + if(!td || !sptr) + return; + + ASN_DEBUG("Freeing %s as a primitive type", td->name); + + if(st->buf) + FREEMEM(st->buf); + + if(!contents_only) + FREEMEM(st); +} + + +/* + * Local internal type passed around as an argument. + */ +struct xdp_arg_s { + asn_TYPE_descriptor_t *type_descriptor; + void *struct_key; + xer_primitive_body_decoder_f *prim_body_decoder; + int decoded_something; + int want_more; +}; + +/* + * Since some kinds of primitive values can be encoded using value-specific + * tags (, , etc), the primitive decoder must + * be supplied with such tags to parse them as needed. + */ +static int +xer_decode__unexpected_tag(void *key, const void *chunk_buf, size_t chunk_size) { + struct xdp_arg_s *arg = (struct xdp_arg_s *)key; + enum xer_pbd_rval bret; + + /* + * The chunk_buf is guaranteed to start at '<'. + */ + assert(chunk_size && ((const char *)chunk_buf)[0] == 0x3c); + + /* + * Decoding was performed once already. Prohibit doing it again. + */ + if(arg->decoded_something) + return -1; + + bret = arg->prim_body_decoder(arg->type_descriptor, + arg->struct_key, chunk_buf, chunk_size); + switch(bret) { + case XPBD_SYSTEM_FAILURE: + case XPBD_DECODER_LIMIT: + case XPBD_BROKEN_ENCODING: + break; + case XPBD_BODY_CONSUMED: + /* Tag decoded successfully */ + arg->decoded_something = 1; + /* Fall through */ + case XPBD_NOT_BODY_IGNORE: /* Safe to proceed further */ + return 0; + } + + return -1; +} + +static ssize_t +xer_decode__primitive_body(void *key, const void *chunk_buf, size_t chunk_size, int have_more) { + struct xdp_arg_s *arg = (struct xdp_arg_s *)key; + enum xer_pbd_rval bret; + size_t lead_wsp_size; + + if(arg->decoded_something) { + if(xer_whitespace_span(chunk_buf, chunk_size) == chunk_size) { + /* + * Example: + * "123 " + * ^- chunk_buf position. + */ + return chunk_size; + } + /* + * Decoding was done once already. Prohibit doing it again. + */ + return -1; + } + + if(!have_more) { + /* + * If we've received something like "1", we can't really + * tell whether it is really `1` or `123`, until we know + * that there is no more data coming. + * The have_more argument will be set to 1 once something + * like this is available to the caller of this callback: + * "1want_more = 1; + return -1; + } + + lead_wsp_size = xer_whitespace_span(chunk_buf, chunk_size); + chunk_buf = (const char *)chunk_buf + lead_wsp_size; + chunk_size -= lead_wsp_size; + + bret = arg->prim_body_decoder(arg->type_descriptor, + arg->struct_key, chunk_buf, chunk_size); + switch(bret) { + case XPBD_SYSTEM_FAILURE: + case XPBD_DECODER_LIMIT: + case XPBD_BROKEN_ENCODING: + break; + case XPBD_BODY_CONSUMED: + /* Tag decoded successfully */ + arg->decoded_something = 1; + /* Fall through */ + case XPBD_NOT_BODY_IGNORE: /* Safe to proceed further */ + return lead_wsp_size + chunk_size; + } + + return -1; +} + + +asn_dec_rval_t +xer_decode_primitive(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, + void **sptr, + size_t struct_size, + const char *opt_mname, + const void *buf_ptr, size_t size, + xer_primitive_body_decoder_f *prim_body_decoder +) { + const char *xml_tag = opt_mname ? opt_mname : td->xml_tag; + asn_struct_ctx_t s_ctx; + struct xdp_arg_s s_arg; + asn_dec_rval_t rc; + + /* + * Create the structure if does not exist. + */ + if(!*sptr) { + *sptr = CALLOC(1, struct_size); + if(!*sptr) ASN__DECODE_FAILED; + } + + memset(&s_ctx, 0, sizeof(s_ctx)); + s_arg.type_descriptor = td; + s_arg.struct_key = *sptr; + s_arg.prim_body_decoder = prim_body_decoder; + s_arg.decoded_something = 0; + s_arg.want_more = 0; + + rc = xer_decode_general(opt_codec_ctx, &s_ctx, &s_arg, + xml_tag, buf_ptr, size, + xer_decode__unexpected_tag, xer_decode__primitive_body); + switch(rc.code) { + case RC_OK: + if(!s_arg.decoded_something) { + char ch; + ASN_DEBUG("Primitive body is not recognized, " + "supplying empty one"); + /* + * Decoding opportunity has come and gone. + * Where's the result? + * Try to feed with empty body, see if it eats it. + */ + if(prim_body_decoder(s_arg.type_descriptor, + s_arg.struct_key, &ch, 0) + != XPBD_BODY_CONSUMED) { + /* + * This decoder does not like empty stuff. + */ + ASN__DECODE_FAILED; + } + } + break; + case RC_WMORE: + /* + * Redo the whole thing later. + * We don't have a context to save intermediate parsing state. + */ + rc.consumed = 0; + break; + case RC_FAIL: + rc.consumed = 0; + if(s_arg.want_more) + rc.code = RC_WMORE; + else + ASN__DECODE_FAILED; + break; + } + return rc; +} + diff --git a/src/cryptoconditions/src/asn/asn_codecs_prim.h b/src/cryptoconditions/src/asn/asn_codecs_prim.h new file mode 100644 index 000000000..0f683fdd0 --- /dev/null +++ b/src/cryptoconditions/src/asn/asn_codecs_prim.h @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef ASN_CODECS_PRIM_H +#define ASN_CODECS_PRIM_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ASN__PRIMITIVE_TYPE_s { + uint8_t *buf; /* Buffer with consecutive primitive encoding bytes */ + int size; /* Size of the buffer */ +} ASN__PRIMITIVE_TYPE_t; /* Do not use this type directly! */ + +asn_struct_free_f ASN__PRIMITIVE_TYPE_free; +ber_type_decoder_f ber_decode_primitive; +der_type_encoder_f der_encode_primitive; + +/* + * A callback specification for the xer_decode_primitive() function below. + */ +enum xer_pbd_rval { + XPBD_SYSTEM_FAILURE, /* System failure (memory shortage, etc) */ + XPBD_DECODER_LIMIT, /* Hit some decoder limitation or deficiency */ + XPBD_BROKEN_ENCODING, /* Encoding of a primitive body is broken */ + XPBD_NOT_BODY_IGNORE, /* Not a body format, but safe to ignore */ + XPBD_BODY_CONSUMED /* Body is recognized and consumed */ +}; +typedef enum xer_pbd_rval (xer_primitive_body_decoder_f) + (asn_TYPE_descriptor_t *td, void *struct_ptr, + const void *chunk_buf, size_t chunk_size); + +/* + * Specific function to decode simple primitive types. + * Also see xer_decode_general() in xer_decoder.h + */ +asn_dec_rval_t xer_decode_primitive(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *type_descriptor, + void **struct_ptr, size_t struct_size, + const char *opt_mname, + const void *buf_ptr, size_t size, + xer_primitive_body_decoder_f *prim_body_decoder +); + +#ifdef __cplusplus +} +#endif + +#endif /* ASN_CODECS_PRIM_H */ diff --git a/src/cryptoconditions/src/asn/asn_internal.h b/src/cryptoconditions/src/asn/asn_internal.h new file mode 100644 index 000000000..9c94ca6c3 --- /dev/null +++ b/src/cryptoconditions/src/asn/asn_internal.h @@ -0,0 +1,128 @@ +/*- + * Copyright (c) 2003, 2004, 2005, 2007 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +/* + * Declarations internally useful for the ASN.1 support code. + */ +#ifndef ASN_INTERNAL_H +#define ASN_INTERNAL_H + +#include "asn_application.h" /* Application-visible API */ + +#ifndef __NO_ASSERT_H__ /* Include assert.h only for internal use. */ +#include /* for assert() macro */ +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Environment version might be used to avoid running with the old library */ +#define ASN1C_ENVIRONMENT_VERSION 923 /* Compile-time version */ +int get_asn1c_environment_version(void); /* Run-time version */ + +#define CALLOC(nmemb, size) calloc(nmemb, size) +#define MALLOC(size) malloc(size) +#define REALLOC(oldptr, size) realloc(oldptr, size) +#define FREEMEM(ptr) free(ptr) + +#define asn_debug_indent 0 +#define ASN_DEBUG_INDENT_ADD(i) do{}while(0) + +/* + * A macro for debugging the ASN.1 internals. + * You may enable or override it. + */ +#ifndef ASN_DEBUG /* If debugging code is not defined elsewhere... */ +#if EMIT_ASN_DEBUG == 1 /* And it was asked to emit this code... */ +#ifdef __GNUC__ +#ifdef ASN_THREAD_SAFE +/* Thread safety requires sacrifice in output indentation: + * Retain empty definition of ASN_DEBUG_INDENT_ADD. */ +#else /* !ASN_THREAD_SAFE */ +#undef ASN_DEBUG_INDENT_ADD +#undef asn_debug_indent +int asn_debug_indent; +#define ASN_DEBUG_INDENT_ADD(i) do { asn_debug_indent += i; } while(0) +#endif /* ASN_THREAD_SAFE */ +#define ASN_DEBUG(fmt, args...) do { \ + int adi = asn_debug_indent; \ + while(adi--) fprintf(stderr, " "); \ + fprintf(stderr, fmt, ##args); \ + fprintf(stderr, " (%s:%d)\n", \ + __FILE__, __LINE__); \ + } while(0) +#else /* !__GNUC__ */ +void ASN_DEBUG_f(const char *fmt, ...); +#define ASN_DEBUG ASN_DEBUG_f +#endif /* __GNUC__ */ +#else /* EMIT_ASN_DEBUG != 1 */ +static void ASN_DEBUG(const char *fmt, ...) { (void)fmt; } +#endif /* EMIT_ASN_DEBUG */ +#endif /* ASN_DEBUG */ + +/* + * Invoke the application-supplied callback and fail, if something is wrong. + */ +#define ASN__E_cbc(buf, size) (cb((buf), (size), app_key) < 0) +#define ASN__E_CALLBACK(foo) do { \ + if(foo) goto cb_failed; \ + } while(0) +#define ASN__CALLBACK(buf, size) \ + ASN__E_CALLBACK(ASN__E_cbc(buf, size)) +#define ASN__CALLBACK2(buf1, size1, buf2, size2) \ + ASN__E_CALLBACK(ASN__E_cbc(buf1, size1) || ASN__E_cbc(buf2, size2)) +#define ASN__CALLBACK3(buf1, size1, buf2, size2, buf3, size3) \ + ASN__E_CALLBACK(ASN__E_cbc(buf1, size1) \ + || ASN__E_cbc(buf2, size2) \ + || ASN__E_cbc(buf3, size3)) + +#define ASN__TEXT_INDENT(nl, level) do { \ + int tmp_level = (level); \ + int tmp_nl = ((nl) != 0); \ + int tmp_i; \ + if(tmp_nl) ASN__CALLBACK("\n", 1); \ + if(tmp_level < 0) tmp_level = 0; \ + for(tmp_i = 0; tmp_i < tmp_level; tmp_i++) \ + ASN__CALLBACK(" ", 4); \ + er.encoded += tmp_nl + 4 * tmp_level; \ + } while(0) + +#define _i_INDENT(nl) do { \ + int tmp_i; \ + if((nl) && cb("\n", 1, app_key) < 0) \ + return -1; \ + for(tmp_i = 0; tmp_i < ilevel; tmp_i++) \ + if(cb(" ", 4, app_key) < 0) \ + return -1; \ + } while(0) + +/* + * Check stack against overflow, if limit is set. + */ +#define ASN__DEFAULT_STACK_MAX (30000) +static int __attribute__((unused)) +ASN__STACK_OVERFLOW_CHECK(asn_codec_ctx_t *ctx) { + if(ctx && ctx->max_stack_size) { + + /* ctx MUST be allocated on the stack */ + ptrdiff_t usedstack = ((char *)ctx - (char *)&ctx); + if(usedstack > 0) usedstack = -usedstack; /* grows up! */ + + /* double negative required to avoid int wrap-around */ + if(usedstack < -(ptrdiff_t)ctx->max_stack_size) { + ASN_DEBUG("Stack limit %ld reached", + (long)ctx->max_stack_size); + return -1; + } + } + return 0; +} + +#ifdef __cplusplus +} +#endif + +#endif /* ASN_INTERNAL_H */ diff --git a/src/cryptoconditions/src/asn/asn_system.h b/src/cryptoconditions/src/asn/asn_system.h new file mode 100644 index 000000000..7e64ba109 --- /dev/null +++ b/src/cryptoconditions/src/asn/asn_system.h @@ -0,0 +1,128 @@ +/*- + * Copyright (c) 2003, 2004, 2007 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +/* + * Miscellaneous system-dependent types. + */ +#ifndef ASN_SYSTEM_H +#define ASN_SYSTEM_H + +#ifdef CRYPTOCONDITIONS_HAVE_CONFIG_H +#include "cryptoconditions-config.h" +#endif + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE /* for snprintf() on some linux systems */ +#endif + +#include /* For snprintf(3) */ +#include /* For *alloc(3) */ +#include /* For memcpy(3) */ +#include /* For size_t */ +#include /* For LONG_MAX */ +#include /* For va_start */ +#include /* for offsetof and ptrdiff_t */ + +#ifdef HAVE_ALLOCA_H +#include /* For alloca(3) */ +#endif + +#ifdef _WIN32 + +#include +#define snprintf _snprintf +#define vsnprintf _vsnprintf + +/* To avoid linking with ws2_32.lib, here's the definition of ntohl() */ +#define sys_ntohl(l) ((((l) << 24) & 0xff000000) \ + | (((l) << 8) & 0xff0000) \ + | (((l) >> 8) & 0xff00) \ + | ((l >> 24) & 0xff)) + +#ifdef _MSC_VER /* MSVS.Net */ +#ifndef __cplusplus +#define inline __inline +#endif +#ifndef ASSUMESTDTYPES /* Standard types have been defined elsewhere */ +#define ssize_t SSIZE_T +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#endif /* ASSUMESTDTYPES */ +#define WIN32_LEAN_AND_MEAN +#include +#include +#define isnan _isnan +#define finite _finite +#define copysign _copysign +#define ilogb _logb +#else /* !_MSC_VER */ +#include +#endif /* _MSC_VER */ + +#else /* !_WIN32 */ + +#if defined(__vxworks) +#include +#else /* !defined(__vxworks) */ + +#include /* C99 specifies this file */ +/* + * 1. Earlier FreeBSD version didn't have , + * but was present. + * 2. Sun Solaris requires for alloca(3), + * but does not have . + */ +#if (!defined(__FreeBSD__) || !defined(_SYS_INTTYPES_H_)) +#if defined(sun) +#include /* For alloca(3) */ +#include /* for finite(3) */ +#elif defined(__hpux) +#ifdef __GNUC__ +#include /* For alloca(3) */ +#else /* !__GNUC__ */ +#define inline +#endif /* __GNUC__ */ +#else +#include /* SUSv2+ and C99 specify this file, for uintXX_t */ +#endif /* defined(sun) */ +#endif + +#include /* for ntohl() */ +#define sys_ntohl(foo) ntohl(foo) + +#endif /* defined(__vxworks) */ + +#endif /* _WIN32 */ + +#if __GNUC__ >= 3 +#ifndef GCC_PRINTFLIKE +#define GCC_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var))) +#endif +#ifndef GCC_NOTUSED +#define GCC_NOTUSED __attribute__((unused)) +#endif +#else +#ifndef GCC_PRINTFLIKE +#define GCC_PRINTFLIKE(fmt,var) /* nothing */ +#endif +#ifndef GCC_NOTUSED +#define GCC_NOTUSED +#endif +#endif + +/* Figure out if thread safety is requested */ +#if !defined(ASN_THREAD_SAFE) && (defined(THREAD_SAFE) || defined(_REENTRANT)) +#define ASN_THREAD_SAFE +#endif /* Thread safety */ + +#ifndef offsetof /* If not defined by */ +#define offsetof(s, m) ((ptrdiff_t)&(((s *)0)->m) - (ptrdiff_t)((s *)0)) +#endif /* offsetof */ + +#endif /* ASN_SYSTEM_H */ diff --git a/src/cryptoconditions/src/asn/ber_decoder.c b/src/cryptoconditions/src/asn/ber_decoder.c new file mode 100644 index 000000000..b3a6329e0 --- /dev/null +++ b/src/cryptoconditions/src/asn/ber_decoder.c @@ -0,0 +1,283 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include + +#undef ADVANCE +#define ADVANCE(num_bytes) do { \ + size_t num = num_bytes; \ + ptr = ((const char *)ptr) + num; \ + size -= num; \ + consumed_myself += num; \ + } while(0) +#undef RETURN +#define RETURN(_code) do { \ + asn_dec_rval_t rval; \ + rval.code = _code; \ + if(opt_ctx) opt_ctx->step = step; /* Save context */ \ + if(_code == RC_OK || opt_ctx) \ + rval.consumed = consumed_myself; \ + else \ + rval.consumed = 0; /* Context-free */ \ + return rval; \ + } while(0) + +/* + * The BER decoder of any type. + */ +asn_dec_rval_t +ber_decode(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *type_descriptor, + void **struct_ptr, const void *ptr, size_t size) { + asn_codec_ctx_t s_codec_ctx; + + /* + * Stack checker requires that the codec context + * must be allocated on the stack. + */ + if(opt_codec_ctx) { + if(opt_codec_ctx->max_stack_size) { + s_codec_ctx = *opt_codec_ctx; + opt_codec_ctx = &s_codec_ctx; + } + } else { + /* If context is not given, be security-conscious anyway */ + memset(&s_codec_ctx, 0, sizeof(s_codec_ctx)); + s_codec_ctx.max_stack_size = ASN__DEFAULT_STACK_MAX; + opt_codec_ctx = &s_codec_ctx; + } + + /* + * Invoke type-specific decoder. + */ + return type_descriptor->ber_decoder(opt_codec_ctx, type_descriptor, + struct_ptr, /* Pointer to the destination structure */ + ptr, size, /* Buffer and its size */ + 0 /* Default tag mode is 0 */ + ); +} + +/* + * Check the set of >> tags matches the definition. + */ +asn_dec_rval_t +ber_check_tags(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, asn_struct_ctx_t *opt_ctx, + const void *ptr, size_t size, int tag_mode, int last_tag_form, + ber_tlv_len_t *last_length, int *opt_tlv_form) { + ssize_t consumed_myself = 0; + ssize_t tag_len; + ssize_t len_len; + ber_tlv_tag_t tlv_tag; + ber_tlv_len_t tlv_len; + ber_tlv_len_t limit_len = -1; + int expect_00_terminators = 0; + int tlv_constr = -1; /* If CHOICE, opt_tlv_form is not given */ + int step = opt_ctx ? opt_ctx->step : 0; /* Where we left previously */ + int tagno; + + /* + * Make sure we didn't exceed the maximum stack size. + */ + if(ASN__STACK_OVERFLOW_CHECK(opt_codec_ctx)) + RETURN(RC_FAIL); + + /* + * So what does all this implicit skip stuff mean? + * Imagine two types, + * A ::= [5] IMPLICIT T + * B ::= [2] EXPLICIT T + * Where T is defined as + * T ::= [4] IMPLICIT SEQUENCE { ... } + * + * Let's say, we are starting to decode type A, given the + * following TLV stream: <5> <0>. What does this mean? + * It means that the type A contains type T which is, + * in turn, empty. + * Remember though, that we are still in A. We cannot + * just pass control to the type T decoder. Why? Because + * the type T decoder expects <4> <0>, not <5> <0>. + * So, we must make sure we are going to receive <5> while + * still in A, then pass control to the T decoder, indicating + * that the tag <4> was implicitly skipped. The decoder of T + * hence will be prepared to treat <4> as valid tag, and decode + * it appropriately. + */ + + tagno = step /* Continuing where left previously */ + + (tag_mode==1?-1:0) + ; + ASN_DEBUG("ber_check_tags(%s, size=%ld, tm=%d, step=%d, tagno=%d)", + td->name, (long)size, tag_mode, step, tagno); + /* assert(td->tags_count >= 1) May not be the case for CHOICE or ANY */ + + if(tag_mode == 0 && tagno == td->tags_count) { + /* + * This must be the _untagged_ ANY type, + * which outermost tag isn't known in advance. + * Fetch the tag and length separately. + */ + tag_len = ber_fetch_tag(ptr, size, &tlv_tag); + switch(tag_len) { + case -1: RETURN(RC_FAIL); + case 0: RETURN(RC_WMORE); + } + tlv_constr = BER_TLV_CONSTRUCTED(ptr); + len_len = ber_fetch_length(tlv_constr, + (const char *)ptr + tag_len, size - tag_len, &tlv_len); + switch(len_len) { + case -1: RETURN(RC_FAIL); + case 0: RETURN(RC_WMORE); + } + ASN_DEBUG("Advancing %ld in ANY case", + (long)(tag_len + len_len)); + ADVANCE(tag_len + len_len); + } else { + assert(tagno < td->tags_count); /* At least one loop */ + } + for((void)tagno; tagno < td->tags_count; tagno++, step++) { + + /* + * Fetch and process T from TLV. + */ + tag_len = ber_fetch_tag(ptr, size, &tlv_tag); + ASN_DEBUG("Fetching tag from {%p,%ld}: " + "len %ld, step %d, tagno %d got %s", + ptr, (long)size, + (long)tag_len, step, tagno, + ber_tlv_tag_string(tlv_tag)); + switch(tag_len) { + case -1: RETURN(RC_FAIL); + case 0: RETURN(RC_WMORE); + } + + tlv_constr = BER_TLV_CONSTRUCTED(ptr); + + /* + * If {I}, don't check anything. + * If {I,B,C}, check B and C unless we're at I. + */ + if(tag_mode != 0 && step == 0) { + /* + * We don't expect tag to match here. + * It's just because we don't know how the tag + * is supposed to look like. + */ + } else { + assert(tagno >= 0); /* Guaranteed by the code above */ + if(tlv_tag != td->tags[tagno]) { + /* + * Unexpected tag. Too bad. + */ + ASN_DEBUG("Expected: %s, " + "expectation failed (tn=%d, tm=%d)", + ber_tlv_tag_string(td->tags[tagno]), + tagno, tag_mode + ); + RETURN(RC_FAIL); + } + } + + /* + * Attention: if there are more tags expected, + * ensure that the current tag is presented + * in constructed form (it contains other tags!). + * If this one is the last one, check that the tag form + * matches the one given in descriptor. + */ + if(tagno < (td->tags_count - 1)) { + if(tlv_constr == 0) { + ASN_DEBUG("tlv_constr = %d, expfail", + tlv_constr); + RETURN(RC_FAIL); + } + } else { + if(last_tag_form != tlv_constr + && last_tag_form != -1) { + ASN_DEBUG("last_tag_form %d != %d", + last_tag_form, tlv_constr); + RETURN(RC_FAIL); + } + } + + /* + * Fetch and process L from TLV. + */ + len_len = ber_fetch_length(tlv_constr, + (const char *)ptr + tag_len, size - tag_len, &tlv_len); + ASN_DEBUG("Fetching len = %ld", (long)len_len); + switch(len_len) { + case -1: RETURN(RC_FAIL); + case 0: RETURN(RC_WMORE); + } + + /* + * FIXME + * As of today, the chain of tags + * must either contain several indefinite length TLVs, + * or several definite length ones. + * No mixing is allowed. + */ + if(tlv_len == -1) { + /* + * Indefinite length. + */ + if(limit_len == -1) { + expect_00_terminators++; + } else { + ASN_DEBUG("Unexpected indefinite length " + "in a chain of definite lengths"); + RETURN(RC_FAIL); + } + ADVANCE(tag_len + len_len); + continue; + } else { + if(expect_00_terminators) { + ASN_DEBUG("Unexpected definite length " + "in a chain of indefinite lengths"); + RETURN(RC_FAIL); + } + } + + /* + * Check that multiple TLVs specify ever decreasing length, + * which is consistent. + */ + if(limit_len == -1) { + limit_len = tlv_len + tag_len + len_len; + if(limit_len < 0) { + /* Too great tlv_len value? */ + RETURN(RC_FAIL); + } + } else if(limit_len != tlv_len + tag_len + len_len) { + /* + * Inner TLV specifies length which is inconsistent + * with the outer TLV's length value. + */ + ASN_DEBUG("Outer TLV is %ld and inner is %ld", + (long)limit_len, (long)tlv_len); + RETURN(RC_FAIL); + } + + ADVANCE(tag_len + len_len); + + limit_len -= (tag_len + len_len); + if((ssize_t)size > limit_len) { + /* + * Make sure that we won't consume more bytes + * from the parent frame than the inferred limit. + */ + size = limit_len; + } + } + + if(opt_tlv_form) + *opt_tlv_form = tlv_constr; + if(expect_00_terminators) + *last_length = -expect_00_terminators; + else + *last_length = tlv_len; + + RETURN(RC_OK); +} diff --git a/src/cryptoconditions/src/asn/ber_decoder.h b/src/cryptoconditions/src/asn/ber_decoder.h new file mode 100644 index 000000000..9fe2e895d --- /dev/null +++ b/src/cryptoconditions/src/asn/ber_decoder.h @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _BER_DECODER_H_ +#define _BER_DECODER_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct asn_TYPE_descriptor_s; /* Forward declaration */ +struct asn_codec_ctx_s; /* Forward declaration */ + +/* + * The BER decoder of any type. + * This function may be invoked directly from the application. + * The der_encode() function (der_encoder.h) is an opposite to ber_decode(). + */ +asn_dec_rval_t ber_decode(struct asn_codec_ctx_s *opt_codec_ctx, + struct asn_TYPE_descriptor_s *type_descriptor, + void **struct_ptr, /* Pointer to a target structure's pointer */ + const void *buffer, /* Data to be decoded */ + size_t size /* Size of that buffer */ + ); + +/* + * Type of generic function which decodes the byte stream into the structure. + */ +typedef asn_dec_rval_t (ber_type_decoder_f)( + struct asn_codec_ctx_s *opt_codec_ctx, + struct asn_TYPE_descriptor_s *type_descriptor, + void **struct_ptr, const void *buf_ptr, size_t size, + int tag_mode); + +/******************************* + * INTERNALLY USEFUL FUNCTIONS * + *******************************/ + +/* + * Check that all tags correspond to the type definition (as given in head). + * On return, last_length would contain either a non-negative length of the + * value part of the last TLV, or the negative number of expected + * "end of content" sequences. The number may only be negative if the + * head->last_tag_form is non-zero. + */ +asn_dec_rval_t ber_check_tags( + struct asn_codec_ctx_s *opt_codec_ctx, /* codec options */ + struct asn_TYPE_descriptor_s *type_descriptor, + asn_struct_ctx_t *opt_ctx, /* saved decoding context */ + const void *ptr, size_t size, + int tag_mode, /* {-1,0,1}: IMPLICIT, no, EXPLICIT */ + int last_tag_form, /* {-1,0:1}: any, primitive, constr */ + ber_tlv_len_t *last_length, + int *opt_tlv_form /* optional tag form */ + ); + +#ifdef __cplusplus +} +#endif + +#endif /* _BER_DECODER_H_ */ diff --git a/src/cryptoconditions/src/asn/ber_tlv_length.c b/src/cryptoconditions/src/asn/ber_tlv_length.c new file mode 100644 index 000000000..4c2f1e5fd --- /dev/null +++ b/src/cryptoconditions/src/asn/ber_tlv_length.c @@ -0,0 +1,178 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +ssize_t +ber_fetch_length(int _is_constructed, const void *bufptr, size_t size, + ber_tlv_len_t *len_r) { + const uint8_t *buf = (const uint8_t *)bufptr; + unsigned oct; + + if(size == 0) + return 0; /* Want more */ + + oct = *(const uint8_t *)buf; + if((oct & 0x80) == 0) { + /* + * Short definite length. + */ + *len_r = oct; /* & 0x7F */ + return 1; + } else { + ber_tlv_len_t len; + size_t skipped; + + if(_is_constructed && oct == 0x80) { + *len_r = -1; /* Indefinite length */ + return 1; + } + + if(oct == 0xff) { + /* Reserved in standard for future use. */ + return -1; + } + + oct &= 0x7F; /* Leave only the 7 LS bits */ + for(len = 0, buf++, skipped = 1; + oct && (++skipped <= size); buf++, oct--) { + + len = (len << 8) | *buf; + if(len < 0 + || (len >> ((8 * sizeof(len)) - 8) && oct > 1)) { + /* + * Too large length value. + */ + return -1; + } + } + + if(oct == 0) { + ber_tlv_len_t lenplusepsilon = (size_t)len + 1024; + /* + * Here length may be very close or equal to 2G. + * However, the arithmetics used in some decoders + * may add some (small) quantities to the length, + * to check the resulting value against some limits. + * This may result in integer wrap-around, which + * we try to avoid by checking it earlier here. + */ + if(lenplusepsilon < 0) { + /* Too large length value */ + return -1; + } + + *len_r = len; + return skipped; + } + + return 0; /* Want more */ + } + +} + +ssize_t +ber_skip_length(asn_codec_ctx_t *opt_codec_ctx, + int _is_constructed, const void *ptr, size_t size) { + ber_tlv_len_t vlen; /* Length of V in TLV */ + ssize_t tl; /* Length of L in TLV */ + ssize_t ll; /* Length of L in TLV */ + size_t skip; + + /* + * Make sure we didn't exceed the maximum stack size. + */ + if(ASN__STACK_OVERFLOW_CHECK(opt_codec_ctx)) + return -1; + + /* + * Determine the size of L in TLV. + */ + ll = ber_fetch_length(_is_constructed, ptr, size, &vlen); + if(ll <= 0) return ll; + + /* + * Definite length. + */ + if(vlen >= 0) { + skip = ll + vlen; + if(skip > size) + return 0; /* Want more */ + return skip; + } + + /* + * Indefinite length! + */ + ASN_DEBUG("Skipping indefinite length"); + for(skip = ll, ptr = ((const char *)ptr) + ll, size -= ll;;) { + ber_tlv_tag_t tag; + + /* Fetch the tag */ + tl = ber_fetch_tag(ptr, size, &tag); + if(tl <= 0) return tl; + + ll = ber_skip_length(opt_codec_ctx, + BER_TLV_CONSTRUCTED(ptr), + ((const char *)ptr) + tl, size - tl); + if(ll <= 0) return ll; + + skip += tl + ll; + + /* + * This may be the end of the indefinite length structure, + * two consecutive 0 octets. + * Check if it is true. + */ + if(((const uint8_t *)ptr)[0] == 0 + && ((const uint8_t *)ptr)[1] == 0) + return skip; + + ptr = ((const char *)ptr) + tl + ll; + size -= tl + ll; + } + + /* UNREACHABLE */ +} + +size_t +der_tlv_length_serialize(ber_tlv_len_t len, void *bufp, size_t size) { + size_t required_size; /* Size of len encoding */ + uint8_t *buf = (uint8_t *)bufp; + uint8_t *end; + size_t i; + + if(len <= 127) { + /* Encoded in 1 octet */ + if(size) *buf = (uint8_t)len; + return 1; + } + + /* + * Compute the size of the subsequent bytes. + */ + for(required_size = 1, i = 8; i < 8 * sizeof(len); i += 8) { + if(len >> i) + required_size++; + else + break; + } + + if(size <= required_size) + return required_size + 1; + + *buf++ = (uint8_t)(0x80 | required_size); /* Length of the encoding */ + + /* + * Produce the len encoding, space permitting. + */ + end = buf + required_size; + for(i -= 8; buf < end; i -= 8, buf++) + *buf = (uint8_t)(len >> i); + + return required_size + 1; +} + diff --git a/src/cryptoconditions/src/asn/ber_tlv_length.h b/src/cryptoconditions/src/asn/ber_tlv_length.h new file mode 100644 index 000000000..349680224 --- /dev/null +++ b/src/cryptoconditions/src/asn/ber_tlv_length.h @@ -0,0 +1,50 @@ +/*- + * Copyright (c) 2003 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _BER_TLV_LENGTH_H_ +#define _BER_TLV_LENGTH_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef ssize_t ber_tlv_len_t; + +/* + * This function tries to fetch the length of the BER TLV value and place it + * in *len_r. + * RETURN VALUES: + * 0: More data expected than bufptr contains. + * -1: Fatal error deciphering length. + * >0: Number of bytes used from bufptr. + * On return with >0, len_r is constrained as -1..MAX, where -1 mean + * that the value is of indefinite length. + */ +ssize_t ber_fetch_length(int _is_constructed, const void *bufptr, size_t size, + ber_tlv_len_t *len_r); + +/* + * This function expects bufptr to be positioned over L in TLV. + * It returns number of bytes occupied by L and V together, suitable + * for skipping. The function properly handles indefinite length. + * RETURN VALUES: + * Standard {-1,0,>0} convention. + */ +ssize_t ber_skip_length( + struct asn_codec_ctx_s *opt_codec_ctx, /* optional context */ + int _is_constructed, const void *bufptr, size_t size); + +/* + * This function serializes the length (L from TLV) in DER format. + * It always returns number of bytes necessary to represent the length, + * it is a caller's responsibility to check the return value + * against the supplied buffer's size. + */ +size_t der_tlv_length_serialize(ber_tlv_len_t len, void *bufptr, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* _BER_TLV_LENGTH_H_ */ diff --git a/src/cryptoconditions/src/asn/ber_tlv_tag.c b/src/cryptoconditions/src/asn/ber_tlv_tag.c new file mode 100644 index 000000000..42708760e --- /dev/null +++ b/src/cryptoconditions/src/asn/ber_tlv_tag.c @@ -0,0 +1,144 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +ssize_t +ber_fetch_tag(const void *ptr, size_t size, ber_tlv_tag_t *tag_r) { + ber_tlv_tag_t val; + ber_tlv_tag_t tclass; + size_t skipped; + + if(size == 0) + return 0; + + val = *(const uint8_t *)ptr; + tclass = (val >> 6); + if((val &= 0x1F) != 0x1F) { + /* + * Simple form: everything encoded in a single octet. + * Tag Class is encoded using two least significant bits. + */ + *tag_r = (val << 2) | tclass; + return 1; + } + + /* + * Each octet contains 7 bits of useful information. + * The MSB is 0 if it is the last octet of the tag. + */ + for(val = 0, ptr = ((const char *)ptr) + 1, skipped = 2; + skipped <= size; + ptr = ((const char *)ptr) + 1, skipped++) { + unsigned int oct = *(const uint8_t *)ptr; + if(oct & 0x80) { + val = (val << 7) | (oct & 0x7F); + /* + * Make sure there are at least 9 bits spare + * at the MS side of a value. + */ + if(val >> ((8 * sizeof(val)) - 9)) { + /* + * We would not be able to accomodate + * any more tag bits. + */ + return -1; + } + } else { + val = (val << 7) | oct; + *tag_r = (val << 2) | tclass; + return skipped; + } + } + + return 0; /* Want more */ +} + + +ssize_t +ber_tlv_tag_fwrite(ber_tlv_tag_t tag, FILE *f) { + char buf[sizeof("[APPLICATION ]") + 32]; + ssize_t ret; + + ret = ber_tlv_tag_snprint(tag, buf, sizeof(buf)); + if(ret >= (ssize_t)sizeof(buf) || ret < 2) { + errno = EPERM; + return -1; + } + + return fwrite(buf, 1, ret, f); +} + +ssize_t +ber_tlv_tag_snprint(ber_tlv_tag_t tag, char *buf, size_t size) { + char *type = 0; + int ret; + + switch(tag & 0x3) { + case ASN_TAG_CLASS_UNIVERSAL: type = "UNIVERSAL "; break; + case ASN_TAG_CLASS_APPLICATION: type = "APPLICATION "; break; + case ASN_TAG_CLASS_CONTEXT: type = ""; break; + case ASN_TAG_CLASS_PRIVATE: type = "PRIVATE "; break; + } + + ret = snprintf(buf, size, "[%s%u]", type, ((unsigned)tag) >> 2); + if(ret <= 0 && size) buf[0] = '\0'; /* against broken libc's */ + + return ret; +} + +char * +ber_tlv_tag_string(ber_tlv_tag_t tag) { + static char buf[sizeof("[APPLICATION ]") + 32]; + + (void)ber_tlv_tag_snprint(tag, buf, sizeof(buf)); + + return buf; +} + + +size_t +ber_tlv_tag_serialize(ber_tlv_tag_t tag, void *bufp, size_t size) { + int tclass = BER_TAG_CLASS(tag); + ber_tlv_tag_t tval = BER_TAG_VALUE(tag); + uint8_t *buf = (uint8_t *)bufp; + uint8_t *end; + size_t required_size; + size_t i; + + if(tval <= 30) { + /* Encoded in 1 octet */ + if(size) buf[0] = (tclass << 6) | tval; + return 1; + } else if(size) { + *buf++ = (tclass << 6) | 0x1F; + size--; + } + + /* + * Compute the size of the subsequent bytes. + */ + for(required_size = 1, i = 7; i < 8 * sizeof(tval); i += 7) { + if(tval >> i) + required_size++; + else + break; + } + + if(size < required_size) + return required_size + 1; + + /* + * Fill in the buffer, space permitting. + */ + end = buf + required_size - 1; + for(i -= 7; buf < end; i -= 7, buf++) + *buf = 0x80 | ((tval >> i) & 0x7F); + *buf = (tval & 0x7F); /* Last octet without high bit */ + + return required_size + 1; +} + diff --git a/src/cryptoconditions/src/asn/ber_tlv_tag.h b/src/cryptoconditions/src/asn/ber_tlv_tag.h new file mode 100644 index 000000000..60e866861 --- /dev/null +++ b/src/cryptoconditions/src/asn/ber_tlv_tag.h @@ -0,0 +1,60 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _BER_TLV_TAG_H_ +#define _BER_TLV_TAG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum asn_tag_class { + ASN_TAG_CLASS_UNIVERSAL = 0, /* 0b00 */ + ASN_TAG_CLASS_APPLICATION = 1, /* 0b01 */ + ASN_TAG_CLASS_CONTEXT = 2, /* 0b10 */ + ASN_TAG_CLASS_PRIVATE = 3 /* 0b11 */ +}; +typedef unsigned ber_tlv_tag_t; /* BER TAG from Tag-Length-Value */ + +/* + * Tag class is encoded together with tag value for optimization purposes. + */ +#define BER_TAG_CLASS(tag) ((tag) & 0x3) +#define BER_TAG_VALUE(tag) ((tag) >> 2) +#define BER_TLV_CONSTRUCTED(tagptr) (((*(const uint8_t *)tagptr)&0x20)?1:0) + +#define BER_TAGS_EQUAL(tag1, tag2) ((tag1) == (tag2)) + +/* + * Several functions for printing the TAG in the canonical form + * (i.e. "[PRIVATE 0]"). + * Return values correspond to their libc counterparts (if any). + */ +ssize_t ber_tlv_tag_snprint(ber_tlv_tag_t tag, char *buf, size_t buflen); +ssize_t ber_tlv_tag_fwrite(ber_tlv_tag_t tag, FILE *); +char *ber_tlv_tag_string(ber_tlv_tag_t tag); + + +/* + * This function tries to fetch the tag from the input stream. + * RETURN VALUES: + * 0: More data expected than bufptr contains. + * -1: Fatal error deciphering tag. + * >0: Number of bytes used from bufptr. tag_r will contain the tag. + */ +ssize_t ber_fetch_tag(const void *bufptr, size_t size, ber_tlv_tag_t *tag_r); + +/* + * This function serializes the tag (T from TLV) in BER format. + * It always returns number of bytes necessary to represent the tag, + * it is a caller's responsibility to check the return value + * against the supplied buffer's size. + */ +size_t ber_tlv_tag_serialize(ber_tlv_tag_t tag, void *bufptr, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* _BER_TLV_TAG_H_ */ diff --git a/src/cryptoconditions/src/asn/constr_CHOICE.c b/src/cryptoconditions/src/asn/constr_CHOICE.c new file mode 100644 index 000000000..6116e6a6b --- /dev/null +++ b/src/cryptoconditions/src/asn/constr_CHOICE.c @@ -0,0 +1,1114 @@ +/* + * Copyright (c) 2003, 2004, 2005, 2006, 2007 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +/* + * Number of bytes left for this structure. + * (ctx->left) indicates the number of bytes _transferred_ for the structure. + * (size) contains the number of bytes in the buffer passed. + */ +#define LEFT ((size<(size_t)ctx->left)?size:(size_t)ctx->left) + +/* + * If the subprocessor function returns with an indication that it wants + * more data, it may well be a fatal decoding problem, because the + * size is constrained by the 's L, even if the buffer size allows + * reading more data. + * For example, consider the buffer containing the following TLVs: + * ... + * The TLV length clearly indicates that one byte is expected in V, but + * if the V processor returns with "want more data" even if the buffer + * contains way more data than the V processor have seen. + */ +#define SIZE_VIOLATION (ctx->left >= 0 && (size_t)ctx->left <= size) + +/* + * This macro "eats" the part of the buffer which is definitely "consumed", + * i.e. was correctly converted into local representation or rightfully skipped. + */ +#undef ADVANCE +#define ADVANCE(num_bytes) do { \ + size_t num = num_bytes; \ + ptr = ((const char *)ptr) + num;\ + size -= num; \ + if(ctx->left >= 0) \ + ctx->left -= num; \ + consumed_myself += num; \ + } while(0) + +/* + * Switch to the next phase of parsing. + */ +#undef NEXT_PHASE +#define NEXT_PHASE(ctx) do { \ + ctx->phase++; \ + ctx->step = 0; \ + } while(0) + +/* + * Return a standardized complex structure. + */ +#undef RETURN +#define RETURN(_code) do { \ + rval.code = _code; \ + rval.consumed = consumed_myself;\ + return rval; \ + } while(0) + +/* + * See the definitions. + */ +static int _fetch_present_idx(const void *struct_ptr, int off, int size); +static void _set_present_idx(void *sptr, int offset, int size, int pres); + +/* + * Tags are canonically sorted in the tag to member table. + */ +static int +_search4tag(const void *ap, const void *bp) { + const asn_TYPE_tag2member_t *a = (const asn_TYPE_tag2member_t *)ap; + const asn_TYPE_tag2member_t *b = (const asn_TYPE_tag2member_t *)bp; + + int a_class = BER_TAG_CLASS(a->el_tag); + int b_class = BER_TAG_CLASS(b->el_tag); + + if(a_class == b_class) { + ber_tlv_tag_t a_value = BER_TAG_VALUE(a->el_tag); + ber_tlv_tag_t b_value = BER_TAG_VALUE(b->el_tag); + + if(a_value == b_value) + return 0; + else if(a_value < b_value) + return -1; + else + return 1; + } else if(a_class < b_class) { + return -1; + } else { + return 1; + } +} + +/* + * The decoder of the CHOICE type. + */ +asn_dec_rval_t +CHOICE_decode_ber(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **struct_ptr, const void *ptr, size_t size, int tag_mode) { + /* + * Bring closer parts of structure description. + */ + asn_CHOICE_specifics_t *specs = (asn_CHOICE_specifics_t *)td->specifics; + asn_TYPE_member_t *elements = td->elements; + + /* + * Parts of the structure being constructed. + */ + void *st = *struct_ptr; /* Target structure. */ + asn_struct_ctx_t *ctx; /* Decoder context */ + + ber_tlv_tag_t tlv_tag; /* T from TLV */ + ssize_t tag_len; /* Length of TLV's T */ + asn_dec_rval_t rval; /* Return code from subparsers */ + + ssize_t consumed_myself = 0; /* Consumed bytes from ptr */ + + ASN_DEBUG("Decoding %s as CHOICE", td->name); + + /* + * Create the target structure if it is not present already. + */ + if(st == 0) { + st = *struct_ptr = CALLOC(1, specs->struct_size); + if(st == 0) { + RETURN(RC_FAIL); + } + } + + /* + * Restore parsing context. + */ + ctx = (asn_struct_ctx_t *)((char *)st + specs->ctx_offset); + + /* + * Start to parse where left previously + */ + switch(ctx->phase) { + case 0: + /* + * PHASE 0. + * Check that the set of tags associated with given structure + * perfectly fits our expectations. + */ + + if(tag_mode || td->tags_count) { + rval = ber_check_tags(opt_codec_ctx, td, ctx, ptr, size, + tag_mode, -1, &ctx->left, 0); + if(rval.code != RC_OK) { + ASN_DEBUG("%s tagging check failed: %d", + td->name, rval.code); + return rval; + } + + if(ctx->left >= 0) { + /* ?Substracted below! */ + ctx->left += rval.consumed; + } + ADVANCE(rval.consumed); + } else { + ctx->left = -1; + } + + NEXT_PHASE(ctx); + + ASN_DEBUG("Structure consumes %ld bytes, buffer %ld", + (long)ctx->left, (long)size); + + /* Fall through */ + case 1: + /* + * Fetch the T from TLV. + */ + tag_len = ber_fetch_tag(ptr, LEFT, &tlv_tag); + ASN_DEBUG("In %s CHOICE tag length %d", td->name, (int)tag_len); + switch(tag_len) { + case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE); + /* Fall through */ + case -1: RETURN(RC_FAIL); + } + + do { + const asn_TYPE_tag2member_t *t2m; + asn_TYPE_tag2member_t key; + + key.el_tag = tlv_tag; + t2m = (const asn_TYPE_tag2member_t *)bsearch(&key, + specs->tag2el, specs->tag2el_count, + sizeof(specs->tag2el[0]), _search4tag); + if(t2m) { + /* + * Found the element corresponding to the tag. + */ + NEXT_PHASE(ctx); + ctx->step = t2m->el_no; + break; + } else if(specs->ext_start == -1) { + ASN_DEBUG("Unexpected tag %s " + "in non-extensible CHOICE %s", + ber_tlv_tag_string(tlv_tag), td->name); + RETURN(RC_FAIL); + } else { + /* Skip this tag */ + ssize_t skip; + + ASN_DEBUG("Skipping unknown tag %s", + ber_tlv_tag_string(tlv_tag)); + + skip = ber_skip_length(opt_codec_ctx, + BER_TLV_CONSTRUCTED(ptr), + (const char *)ptr + tag_len, + LEFT - tag_len); + + switch(skip) { + case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE); + /* Fall through */ + case -1: RETURN(RC_FAIL); + } + + ADVANCE(skip + tag_len); + RETURN(RC_OK); + } + } while(0); + + case 2: + /* + * PHASE 2. + * Read in the element. + */ + do { + asn_TYPE_member_t *elm;/* CHOICE's element */ + void *memb_ptr; /* Pointer to the member */ + void **memb_ptr2; /* Pointer to that pointer */ + + elm = &elements[ctx->step]; + + /* + * Compute the position of the member inside a structure, + * and also a type of containment (it may be contained + * as pointer or using inline inclusion). + */ + if(elm->flags & ATF_POINTER) { + /* Member is a pointer to another structure */ + memb_ptr2 = (void **)((char *)st + elm->memb_offset); + } else { + /* + * A pointer to a pointer + * holding the start of the structure + */ + memb_ptr = (char *)st + elm->memb_offset; + memb_ptr2 = &memb_ptr; + } + /* Set presence to be able to free it properly at any time */ + _set_present_idx(st, specs->pres_offset, + specs->pres_size, ctx->step + 1); + /* + * Invoke the member fetch routine according to member's type + */ + rval = elm->type->ber_decoder(opt_codec_ctx, elm->type, + memb_ptr2, ptr, LEFT, elm->tag_mode); + switch(rval.code) { + case RC_OK: + break; + case RC_WMORE: /* More data expected */ + if(!SIZE_VIOLATION) { + ADVANCE(rval.consumed); + RETURN(RC_WMORE); + } + RETURN(RC_FAIL); + case RC_FAIL: /* Fatal error */ + RETURN(rval.code); + } /* switch(rval) */ + + ADVANCE(rval.consumed); + } while(0); + + NEXT_PHASE(ctx); + + /* Fall through */ + case 3: + ASN_DEBUG("CHOICE %s Leftover: %ld, size = %ld, tm=%d, tc=%d", + td->name, (long)ctx->left, (long)size, + tag_mode, td->tags_count); + + if(ctx->left > 0) { + /* + * The type must be fully decoded + * by the CHOICE member-specific decoder. + */ + RETURN(RC_FAIL); + } + + if(ctx->left == -1 + && !(tag_mode || td->tags_count)) { + /* + * This is an untagged CHOICE. + * It doesn't contain nothing + * except for the member itself, including all its tags. + * The decoding is completed. + */ + NEXT_PHASE(ctx); + break; + } + + /* + * Read in the "end of data chunks"'s. + */ + while(ctx->left < 0) { + ssize_t tl; + + tl = ber_fetch_tag(ptr, LEFT, &tlv_tag); + switch(tl) { + case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE); + /* Fall through */ + case -1: RETURN(RC_FAIL); + } + + /* + * Expected <0><0>... + */ + if(((const uint8_t *)ptr)[0] == 0) { + if(LEFT < 2) { + if(SIZE_VIOLATION) + RETURN(RC_FAIL); + else + RETURN(RC_WMORE); + } else if(((const uint8_t *)ptr)[1] == 0) { + /* + * Correctly finished with <0><0>. + */ + ADVANCE(2); + ctx->left++; + continue; + } + } else { + ASN_DEBUG("Unexpected continuation in %s", + td->name); + RETURN(RC_FAIL); + } + + /* UNREACHABLE */ + } + + NEXT_PHASE(ctx); + case 4: + /* No meaningful work here */ + break; + } + + RETURN(RC_OK); +} + +asn_enc_rval_t +CHOICE_encode_der(asn_TYPE_descriptor_t *td, void *sptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_CHOICE_specifics_t *specs = (asn_CHOICE_specifics_t *)td->specifics; + asn_TYPE_member_t *elm; /* CHOICE element */ + asn_enc_rval_t erval; + void *memb_ptr; + size_t computed_size = 0; + int present; + + if(!sptr) ASN__ENCODE_FAILED; + + ASN_DEBUG("%s %s as CHOICE", + cb?"Encoding":"Estimating", td->name); + + present = _fetch_present_idx(sptr, + specs->pres_offset, specs->pres_size); + + /* + * If the structure was not initialized, it cannot be encoded: + * can't deduce what to encode in the choice type. + */ + if(present <= 0 || present > td->elements_count) { + if(present == 0 && td->elements_count == 0) { + /* The CHOICE is empty?! */ + erval.encoded = 0; + ASN__ENCODED_OK(erval); + } + ASN__ENCODE_FAILED; + } + + /* + * Seek over the present member of the structure. + */ + elm = &td->elements[present-1]; + if(elm->flags & ATF_POINTER) { + memb_ptr = *(void **)((char *)sptr + elm->memb_offset); + if(memb_ptr == 0) { + if(elm->optional) { + erval.encoded = 0; + ASN__ENCODED_OK(erval); + } + /* Mandatory element absent */ + ASN__ENCODE_FAILED; + } + } else { + memb_ptr = (void *)((char *)sptr + elm->memb_offset); + } + + /* + * If the CHOICE itself is tagged EXPLICIT: + * T ::= [2] EXPLICIT CHOICE { ... } + * Then emit the appropriate tags. + */ + if(tag_mode == 1 || td->tags_count) { + /* + * For this, we need to pre-compute the member. + */ + ssize_t ret; + + /* Encode member with its tag */ + erval = elm->type->der_encoder(elm->type, memb_ptr, + elm->tag_mode, elm->tag, 0, 0); + if(erval.encoded == -1) + return erval; + + /* Encode CHOICE with parent or my own tag */ + ret = der_write_tags(td, erval.encoded, tag_mode, 1, tag, + cb, app_key); + if(ret == -1) + ASN__ENCODE_FAILED; + computed_size += ret; + } + + /* + * Encode the single underlying member. + */ + erval = elm->type->der_encoder(elm->type, memb_ptr, + elm->tag_mode, elm->tag, cb, app_key); + if(erval.encoded == -1) + return erval; + + ASN_DEBUG("Encoded CHOICE member in %ld bytes (+%ld)", + (long)erval.encoded, (long)computed_size); + + erval.encoded += computed_size; + + return erval; +} + +ber_tlv_tag_t +CHOICE_outmost_tag(const asn_TYPE_descriptor_t *td, const void *ptr, int tag_mode, ber_tlv_tag_t tag) { + asn_CHOICE_specifics_t *specs = (asn_CHOICE_specifics_t *)td->specifics; + int present; + + assert(tag_mode == 0); (void)tag_mode; + assert(tag == 0); (void)tag; + + /* + * Figure out which CHOICE element is encoded. + */ + present = _fetch_present_idx(ptr, specs->pres_offset, specs->pres_size); + + if(present > 0 || present <= td->elements_count) { + const asn_TYPE_member_t *elm = &td->elements[present-1]; + const void *memb_ptr; + + if(elm->flags & ATF_POINTER) { + memb_ptr = *(const void * const *) + ((const char *)ptr + elm->memb_offset); + } else { + memb_ptr = (const void *) + ((const char *)ptr + elm->memb_offset); + } + + return asn_TYPE_outmost_tag(elm->type, memb_ptr, + elm->tag_mode, elm->tag); + } else { + return (ber_tlv_tag_t)-1; + } +} + +int +CHOICE_constraint(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + asn_CHOICE_specifics_t *specs = (asn_CHOICE_specifics_t *)td->specifics; + int present; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + /* + * Figure out which CHOICE element is encoded. + */ + present = _fetch_present_idx(sptr, specs->pres_offset,specs->pres_size); + if(present > 0 && present <= td->elements_count) { + asn_TYPE_member_t *elm = &td->elements[present-1]; + const void *memb_ptr; + + if(elm->flags & ATF_POINTER) { + memb_ptr = *(const void * const *)((const char *)sptr + elm->memb_offset); + if(!memb_ptr) { + if(elm->optional) + return 0; + ASN__CTFAIL(app_key, td, sptr, + "%s: mandatory CHOICE element %s absent (%s:%d)", + td->name, elm->name, __FILE__, __LINE__); + return -1; + } + } else { + memb_ptr = (const void *)((const char *)sptr + elm->memb_offset); + } + + if(elm->memb_constraints) { + return elm->memb_constraints(elm->type, memb_ptr, + ctfailcb, app_key); + } else { + int ret = elm->type->check_constraints(elm->type, + memb_ptr, ctfailcb, app_key); + /* + * Cannot inherit it eralier: + * need to make sure we get the updated version. + */ + elm->memb_constraints = elm->type->check_constraints; + return ret; + } + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: no CHOICE element given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +#undef XER_ADVANCE +#define XER_ADVANCE(num_bytes) do { \ + size_t num = num_bytes; \ + buf_ptr = (const void *)(((const char *)buf_ptr) + num); \ + size -= num; \ + consumed_myself += num; \ + } while(0) + +/* + * Decode the XER (XML) data. + */ +asn_dec_rval_t +CHOICE_decode_xer(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **struct_ptr, const char *opt_mname, + const void *buf_ptr, size_t size) { + /* + * Bring closer parts of structure description. + */ + asn_CHOICE_specifics_t *specs = (asn_CHOICE_specifics_t *)td->specifics; + const char *xml_tag = opt_mname ? opt_mname : td->xml_tag; + + /* + * Parts of the structure being constructed. + */ + void *st = *struct_ptr; /* Target structure. */ + asn_struct_ctx_t *ctx; /* Decoder context */ + + asn_dec_rval_t rval; /* Return value of a decoder */ + ssize_t consumed_myself = 0; /* Consumed bytes from ptr */ + int edx; /* Element index */ + + /* + * Create the target structure if it is not present already. + */ + if(st == 0) { + st = *struct_ptr = CALLOC(1, specs->struct_size); + if(st == 0) RETURN(RC_FAIL); + } + + /* + * Restore parsing context. + */ + ctx = (asn_struct_ctx_t *)((char *)st + specs->ctx_offset); + if(ctx->phase == 0 && !*xml_tag) + ctx->phase = 1; /* Skip the outer tag checking phase */ + + /* + * Phases of XER/XML processing: + * Phase 0: Check that the opening tag matches our expectations. + * Phase 1: Processing body and reacting on closing tag. + * Phase 2: Processing inner type. + * Phase 3: Only waiting for closing tag. + * Phase 4: Skipping unknown extensions. + * Phase 5: PHASED OUT + */ + for(edx = ctx->step; ctx->phase <= 4;) { + pxer_chunk_type_e ch_type; /* XER chunk type */ + ssize_t ch_size; /* Chunk size */ + xer_check_tag_e tcv; /* Tag check value */ + asn_TYPE_member_t *elm; + + /* + * Go inside the member. + */ + if(ctx->phase == 2) { + asn_dec_rval_t tmprval; + void *memb_ptr; /* Pointer to the member */ + void **memb_ptr2; /* Pointer to that pointer */ + + elm = &td->elements[edx]; + + if(elm->flags & ATF_POINTER) { + /* Member is a pointer to another structure */ + memb_ptr2 = (void **)((char *)st + + elm->memb_offset); + } else { + memb_ptr = (char *)st + elm->memb_offset; + memb_ptr2 = &memb_ptr; + } + + /* Start/Continue decoding the inner member */ + tmprval = elm->type->xer_decoder(opt_codec_ctx, + elm->type, memb_ptr2, elm->name, + buf_ptr, size); + XER_ADVANCE(tmprval.consumed); + ASN_DEBUG("XER/CHOICE: itdf: [%s] code=%d", + elm->type->name, tmprval.code); + if(tmprval.code != RC_OK) + RETURN(tmprval.code); + assert(_fetch_present_idx(st, + specs->pres_offset, specs->pres_size) == 0); + /* Record what we've got */ + _set_present_idx(st, + specs->pres_offset, specs->pres_size, edx + 1); + ctx->phase = 3; + /* Fall through */ + } + + /* No need to wait for closing tag; special mode. */ + if(ctx->phase == 3 && !*xml_tag) { + ctx->phase = 5; /* Phase out */ + RETURN(RC_OK); + } + + /* + * Get the next part of the XML stream. + */ + ch_size = xer_next_token(&ctx->context, buf_ptr, size, &ch_type); + if(ch_size == -1) { + RETURN(RC_FAIL); + } else { + switch(ch_type) { + case PXER_WMORE: + RETURN(RC_WMORE); + case PXER_COMMENT: /* Got XML comment */ + case PXER_TEXT: /* Ignore free-standing text */ + XER_ADVANCE(ch_size); /* Skip silently */ + continue; + case PXER_TAG: + break; /* Check the rest down there */ + } + } + + tcv = xer_check_tag(buf_ptr, ch_size, xml_tag); + ASN_DEBUG("XER/CHOICE checked [%c%c%c%c] vs [%s], tcv=%d", + ch_size>0?((const uint8_t *)buf_ptr)[0]:'?', + ch_size>1?((const uint8_t *)buf_ptr)[1]:'?', + ch_size>2?((const uint8_t *)buf_ptr)[2]:'?', + ch_size>3?((const uint8_t *)buf_ptr)[3]:'?', + xml_tag, tcv); + + /* Skip the extensions section */ + if(ctx->phase == 4) { + ASN_DEBUG("skip_unknown(%d, %ld)", + tcv, (long)ctx->left); + switch(xer_skip_unknown(tcv, &ctx->left)) { + case -1: + ctx->phase = 5; + RETURN(RC_FAIL); + continue; + case 1: + ctx->phase = 3; + /* Fall through */ + case 0: + XER_ADVANCE(ch_size); + continue; + case 2: + ctx->phase = 3; + break; + } + } + + switch(tcv) { + case XCT_BOTH: + break; /* No CHOICE? */ + case XCT_CLOSING: + if(ctx->phase != 3) + break; + XER_ADVANCE(ch_size); + ctx->phase = 5; /* Phase out */ + RETURN(RC_OK); + case XCT_OPENING: + if(ctx->phase == 0) { + XER_ADVANCE(ch_size); + ctx->phase = 1; /* Processing body phase */ + continue; + } + /* Fall through */ + case XCT_UNKNOWN_OP: + case XCT_UNKNOWN_BO: + + if(ctx->phase != 1) + break; /* Really unexpected */ + + /* + * Search which inner member corresponds to this tag. + */ + for(edx = 0; edx < td->elements_count; edx++) { + elm = &td->elements[edx]; + tcv = xer_check_tag(buf_ptr,ch_size,elm->name); + switch(tcv) { + case XCT_BOTH: + case XCT_OPENING: + /* + * Process this member. + */ + ctx->step = edx; + ctx->phase = 2; + break; + case XCT_UNKNOWN_OP: + case XCT_UNKNOWN_BO: + continue; + default: + edx = td->elements_count; + break; /* Phase out */ + } + break; + } + if(edx != td->elements_count) + continue; + + /* It is expected extension */ + if(specs->ext_start != -1) { + ASN_DEBUG("Got anticipated extension"); + /* + * Check for (XCT_BOTH or XCT_UNKNOWN_BO) + * By using a mask. Only record a pure + * tags. + */ + if(tcv & XCT_CLOSING) { + /* Found without body */ + ctx->phase = 3; /* Terminating */ + } else { + ctx->left = 1; + ctx->phase = 4; /* Skip ...'s */ + } + XER_ADVANCE(ch_size); + continue; + } + + /* Fall through */ + default: + break; + } + + ASN_DEBUG("Unexpected XML tag [%c%c%c%c] in CHOICE [%s]" + " (ph=%d, tag=%s)", + ch_size>0?((const uint8_t *)buf_ptr)[0]:'?', + ch_size>1?((const uint8_t *)buf_ptr)[1]:'?', + ch_size>2?((const uint8_t *)buf_ptr)[2]:'?', + ch_size>3?((const uint8_t *)buf_ptr)[3]:'?', + td->name, ctx->phase, xml_tag); + break; + } + + ctx->phase = 5; /* Phase out, just in case */ + RETURN(RC_FAIL); +} + + +asn_enc_rval_t +CHOICE_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_CHOICE_specifics_t *specs=(asn_CHOICE_specifics_t *)td->specifics; + asn_enc_rval_t er; + int present; + + if(!sptr) + ASN__ENCODE_FAILED; + + /* + * Figure out which CHOICE element is encoded. + */ + present = _fetch_present_idx(sptr, specs->pres_offset,specs->pres_size); + + if(present <= 0 || present > td->elements_count) { + ASN__ENCODE_FAILED; + } else { + asn_enc_rval_t tmper; + asn_TYPE_member_t *elm = &td->elements[present-1]; + void *memb_ptr; + const char *mname = elm->name; + unsigned int mlen = strlen(mname); + + if(elm->flags & ATF_POINTER) { + memb_ptr = *(void **)((char *)sptr + elm->memb_offset); + if(!memb_ptr) ASN__ENCODE_FAILED; + } else { + memb_ptr = (void *)((char *)sptr + elm->memb_offset); + } + + er.encoded = 0; + + if(!(flags & XER_F_CANONICAL)) ASN__TEXT_INDENT(1, ilevel); + ASN__CALLBACK3("<", 1, mname, mlen, ">", 1); + + tmper = elm->type->xer_encoder(elm->type, memb_ptr, + ilevel + 1, flags, cb, app_key); + if(tmper.encoded == -1) return tmper; + + ASN__CALLBACK3("", 1); + + er.encoded += 5 + (2 * mlen) + tmper.encoded; + } + + if(!(flags & XER_F_CANONICAL)) ASN__TEXT_INDENT(1, ilevel - 1); + + ASN__ENCODED_OK(er); +cb_failed: + ASN__ENCODE_FAILED; +} + +asn_dec_rval_t +CHOICE_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) { + asn_CHOICE_specifics_t *specs = (asn_CHOICE_specifics_t *)td->specifics; + asn_dec_rval_t rv; + asn_per_constraint_t *ct; + asn_TYPE_member_t *elm; /* CHOICE's element */ + void *memb_ptr; + void **memb_ptr2; + void *st = *sptr; + int value; + + if(ASN__STACK_OVERFLOW_CHECK(opt_codec_ctx)) + ASN__DECODE_FAILED; + + /* + * Create the target structure if it is not present already. + */ + if(!st) { + st = *sptr = CALLOC(1, specs->struct_size); + if(!st) ASN__DECODE_FAILED; + } + + if(constraints) ct = &constraints->value; + else if(td->per_constraints) ct = &td->per_constraints->value; + else ct = 0; + + if(ct && ct->flags & APC_EXTENSIBLE) { + value = per_get_few_bits(pd, 1); + if(value < 0) ASN__DECODE_STARVED; + if(value) ct = 0; /* Not restricted */ + } + + if(ct && ct->range_bits >= 0) { + value = per_get_few_bits(pd, ct->range_bits); + if(value < 0) ASN__DECODE_STARVED; + ASN_DEBUG("CHOICE %s got index %d in range %d", + td->name, value, ct->range_bits); + if(value > ct->upper_bound) + ASN__DECODE_FAILED; + } else { + if(specs->ext_start == -1) + ASN__DECODE_FAILED; + value = uper_get_nsnnwn(pd); + if(value < 0) ASN__DECODE_STARVED; + value += specs->ext_start; + if(value >= td->elements_count) + ASN__DECODE_FAILED; + } + + /* Adjust if canonical order is different from natural order */ + if(specs->canonical_order) + value = specs->canonical_order[value]; + + /* Set presence to be able to free it later */ + _set_present_idx(st, specs->pres_offset, specs->pres_size, value + 1); + + elm = &td->elements[value]; + if(elm->flags & ATF_POINTER) { + /* Member is a pointer to another structure */ + memb_ptr2 = (void **)((char *)st + elm->memb_offset); + } else { + memb_ptr = (char *)st + elm->memb_offset; + memb_ptr2 = &memb_ptr; + } + ASN_DEBUG("Discovered CHOICE %s encodes %s", td->name, elm->name); + + if(ct && ct->range_bits >= 0) { + rv = elm->type->uper_decoder(opt_codec_ctx, elm->type, + elm->per_constraints, memb_ptr2, pd); + } else { + rv = uper_open_type_get(opt_codec_ctx, elm->type, + elm->per_constraints, memb_ptr2, pd); + } + + if(rv.code != RC_OK) + ASN_DEBUG("Failed to decode %s in %s (CHOICE) %d", + elm->name, td->name, rv.code); + return rv; +} + +asn_enc_rval_t +CHOICE_encode_uper(asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) { + asn_CHOICE_specifics_t *specs = (asn_CHOICE_specifics_t *)td->specifics; + asn_TYPE_member_t *elm; /* CHOICE's element */ + asn_per_constraint_t *ct; + void *memb_ptr; + int present; + int present_enc; + + if(!sptr) ASN__ENCODE_FAILED; + + ASN_DEBUG("Encoding %s as CHOICE", td->name); + + if(constraints) ct = &constraints->value; + else if(td->per_constraints) ct = &td->per_constraints->value; + else ct = 0; + + present = _fetch_present_idx(sptr, + specs->pres_offset, specs->pres_size); + + /* + * If the structure was not initialized properly, it cannot be encoded: + * can't deduce what to encode in the choice type. + */ + if(present <= 0 || present > td->elements_count) + ASN__ENCODE_FAILED; + else + present--; + + ASN_DEBUG("Encoding %s CHOICE element %d", td->name, present); + + /* Adjust if canonical order is different from natural order */ + if(specs->canonical_order) + present_enc = specs->canonical_order[present]; + else + present_enc = present; + + if(ct && ct->range_bits >= 0) { + if(present_enc < ct->lower_bound + || present_enc > ct->upper_bound) { + if(ct->flags & APC_EXTENSIBLE) { + if(per_put_few_bits(po, 1, 1)) + ASN__ENCODE_FAILED; + } else { + ASN__ENCODE_FAILED; + } + ct = 0; + } + } + if(ct && ct->flags & APC_EXTENSIBLE) + if(per_put_few_bits(po, 0, 1)) + ASN__ENCODE_FAILED; + + elm = &td->elements[present]; + if(elm->flags & ATF_POINTER) { + /* Member is a pointer to another structure */ + memb_ptr = *(void **)((char *)sptr + elm->memb_offset); + if(!memb_ptr) ASN__ENCODE_FAILED; + } else { + memb_ptr = (char *)sptr + elm->memb_offset; + } + + if(ct && ct->range_bits >= 0) { + if(per_put_few_bits(po, present_enc, ct->range_bits)) + ASN__ENCODE_FAILED; + + return elm->type->uper_encoder(elm->type, elm->per_constraints, + memb_ptr, po); + } else { + asn_enc_rval_t rval; + if(specs->ext_start == -1) + ASN__ENCODE_FAILED; + if(uper_put_nsnnwn(po, present_enc - specs->ext_start)) + ASN__ENCODE_FAILED; + if(uper_open_type_put(elm->type, elm->per_constraints, + memb_ptr, po)) + ASN__ENCODE_FAILED; + rval.encoded = 0; + ASN__ENCODED_OK(rval); + } +} + + +int +CHOICE_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_CHOICE_specifics_t *specs = (asn_CHOICE_specifics_t *)td->specifics; + int present; + + if(!sptr) return (cb("", 8, app_key) < 0) ? -1 : 0; + + /* + * Figure out which CHOICE element is encoded. + */ + present = _fetch_present_idx(sptr, specs->pres_offset,specs->pres_size); + + /* + * Print that element. + */ + if(present > 0 && present <= td->elements_count) { + asn_TYPE_member_t *elm = &td->elements[present-1]; + const void *memb_ptr; + + if(elm->flags & ATF_POINTER) { + memb_ptr = *(const void * const *)((const char *)sptr + elm->memb_offset); + if(!memb_ptr) return (cb("", 8, app_key) < 0) ? -1 : 0; + } else { + memb_ptr = (const void *)((const char *)sptr + elm->memb_offset); + } + + /* Print member's name and stuff */ + if(0) { + if(cb(elm->name, strlen(elm->name), app_key) < 0 + || cb(": ", 2, app_key) < 0) + return -1; + } + + return elm->type->print_struct(elm->type, memb_ptr, ilevel, + cb, app_key); + } else { + return (cb("", 8, app_key) < 0) ? -1 : 0; + } +} + +void +CHOICE_free(asn_TYPE_descriptor_t *td, void *ptr, int contents_only) { + asn_CHOICE_specifics_t *specs = (asn_CHOICE_specifics_t *)td->specifics; + int present; + + if(!td || !ptr) + return; + + ASN_DEBUG("Freeing %s as CHOICE", td->name); + + /* + * Figure out which CHOICE element is encoded. + */ + present = _fetch_present_idx(ptr, specs->pres_offset, specs->pres_size); + + /* + * Free that element. + */ + if(present > 0 && present <= td->elements_count) { + asn_TYPE_member_t *elm = &td->elements[present-1]; + void *memb_ptr; + + if(elm->flags & ATF_POINTER) { + memb_ptr = *(void **)((char *)ptr + elm->memb_offset); + if(memb_ptr) + ASN_STRUCT_FREE(*elm->type, memb_ptr); + } else { + memb_ptr = (void *)((char *)ptr + elm->memb_offset); + ASN_STRUCT_FREE_CONTENTS_ONLY(*elm->type, memb_ptr); + } + } + + if(!contents_only) { + FREEMEM(ptr); + } +} + + +/* + * The following functions functions offer protection against -fshort-enums, + * compatible with little- and big-endian machines. + * If assertion is triggered, either disable -fshort-enums, or add an entry + * here with the ->pres_size of your target stracture. + * Unless the target structure is packed, the ".present" member + * is guaranteed to be aligned properly. ASN.1 compiler itself does not + * produce packed code. + */ +static int +_fetch_present_idx(const void *struct_ptr, int pres_offset, int pres_size) { + const void *present_ptr; + int present; + + present_ptr = ((const char *)struct_ptr) + pres_offset; + + switch(pres_size) { + case sizeof(int): present = *(const int *)present_ptr; break; + case sizeof(short): present = *(const short *)present_ptr; break; + case sizeof(char): present = *(const char *)present_ptr; break; + default: + /* ANSI C mandates enum to be equivalent to integer */ + assert(pres_size != sizeof(int)); + return 0; /* If not aborted, pass back safe value */ + } + + return present; +} + +static void +_set_present_idx(void *struct_ptr, int pres_offset, int pres_size, int present) { + void *present_ptr; + present_ptr = ((char *)struct_ptr) + pres_offset; + + switch(pres_size) { + case sizeof(int): *(int *)present_ptr = present; break; + case sizeof(short): *(short *)present_ptr = present; break; + case sizeof(char): *(char *)present_ptr = present; break; + default: + /* ANSI C mandates enum to be equivalent to integer */ + assert(pres_size != sizeof(int)); + } +} diff --git a/src/cryptoconditions/src/asn/constr_CHOICE.h b/src/cryptoconditions/src/asn/constr_CHOICE.h new file mode 100644 index 000000000..e824a2206 --- /dev/null +++ b/src/cryptoconditions/src/asn/constr_CHOICE.h @@ -0,0 +1,57 @@ +/*- + * Copyright (c) 2003, 2004, 2005 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _CONSTR_CHOICE_H_ +#define _CONSTR_CHOICE_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef const struct asn_CHOICE_specifics_s { + /* + * Target structure description. + */ + int struct_size; /* Size of the target structure. */ + int ctx_offset; /* Offset of the asn_codec_ctx_t member */ + int pres_offset; /* Identifier of the present member */ + int pres_size; /* Size of the identifier (enum) */ + + /* + * Tags to members mapping table. + */ + const asn_TYPE_tag2member_t *tag2el; + int tag2el_count; + + /* Canonical ordering of CHOICE elements, for PER */ + int *canonical_order; + + /* + * Extensions-related stuff. + */ + int ext_start; /* First member of extensions, or -1 */ +} asn_CHOICE_specifics_t; + +/* + * A set specialized functions dealing with the CHOICE type. + */ +asn_struct_free_f CHOICE_free; +asn_struct_print_f CHOICE_print; +asn_constr_check_f CHOICE_constraint; +ber_type_decoder_f CHOICE_decode_ber; +der_type_encoder_f CHOICE_encode_der; +xer_type_decoder_f CHOICE_decode_xer; +xer_type_encoder_f CHOICE_encode_xer; +per_type_decoder_f CHOICE_decode_uper; +per_type_encoder_f CHOICE_encode_uper; +asn_outmost_tag_f CHOICE_outmost_tag; + +#ifdef __cplusplus +} +#endif + +#endif /* _CONSTR_CHOICE_H_ */ diff --git a/src/cryptoconditions/src/asn/constr_SEQUENCE.c b/src/cryptoconditions/src/asn/constr_SEQUENCE.c new file mode 100644 index 000000000..5923023de --- /dev/null +++ b/src/cryptoconditions/src/asn/constr_SEQUENCE.c @@ -0,0 +1,1425 @@ +/*- + * Copyright (c) 2003, 2004, 2005, 2006, 2007 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +/* + * Number of bytes left for this structure. + * (ctx->left) indicates the number of bytes _transferred_ for the structure. + * (size) contains the number of bytes in the buffer passed. + */ +#define LEFT ((size<(size_t)ctx->left)?size:(size_t)ctx->left) + +/* + * If the subprocessor function returns with an indication that it wants + * more data, it may well be a fatal decoding problem, because the + * size is constrained by the 's L, even if the buffer size allows + * reading more data. + * For example, consider the buffer containing the following TLVs: + * ... + * The TLV length clearly indicates that one byte is expected in V, but + * if the V processor returns with "want more data" even if the buffer + * contains way more data than the V processor have seen. + */ +#define SIZE_VIOLATION (ctx->left >= 0 && (size_t)ctx->left <= size) + +/* + * This macro "eats" the part of the buffer which is definitely "consumed", + * i.e. was correctly converted into local representation or rightfully skipped. + */ +#undef ADVANCE +#define ADVANCE(num_bytes) do { \ + size_t num = num_bytes; \ + ptr = ((const char *)ptr) + num; \ + size -= num; \ + if(ctx->left >= 0) \ + ctx->left -= num; \ + consumed_myself += num; \ + } while(0) + +/* + * Switch to the next phase of parsing. + */ +#undef NEXT_PHASE +#undef PHASE_OUT +#define NEXT_PHASE(ctx) do { \ + ctx->phase++; \ + ctx->step = 0; \ + } while(0) +#define PHASE_OUT(ctx) do { ctx->phase = 10; } while(0) + +/* + * Return a standardized complex structure. + */ +#undef RETURN +#define RETURN(_code) do { \ + rval.code = _code; \ + rval.consumed = consumed_myself;\ + return rval; \ + } while(0) + +/* + * Check whether we are inside the extensions group. + */ +#define IN_EXTENSION_GROUP(specs, memb_idx) \ + ( ((memb_idx) > (specs)->ext_after) \ + &&((memb_idx) < (specs)->ext_before)) + + +/* + * Tags are canonically sorted in the tag2element map. + */ +static int +_t2e_cmp(const void *ap, const void *bp) { + const asn_TYPE_tag2member_t *a = (const asn_TYPE_tag2member_t *)ap; + const asn_TYPE_tag2member_t *b = (const asn_TYPE_tag2member_t *)bp; + + int a_class = BER_TAG_CLASS(a->el_tag); + int b_class = BER_TAG_CLASS(b->el_tag); + + if(a_class == b_class) { + ber_tlv_tag_t a_value = BER_TAG_VALUE(a->el_tag); + ber_tlv_tag_t b_value = BER_TAG_VALUE(b->el_tag); + + if(a_value == b_value) { + if(a->el_no > b->el_no) + return 1; + /* + * Important: we do not check + * for a->el_no <= b->el_no! + */ + return 0; + } else if(a_value < b_value) + return -1; + else + return 1; + } else if(a_class < b_class) { + return -1; + } else { + return 1; + } +} + + +/* + * The decoder of the SEQUENCE type. + */ +asn_dec_rval_t +SEQUENCE_decode_ber(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **struct_ptr, const void *ptr, size_t size, int tag_mode) { + /* + * Bring closer parts of structure description. + */ + asn_SEQUENCE_specifics_t *specs = (asn_SEQUENCE_specifics_t *)td->specifics; + asn_TYPE_member_t *elements = td->elements; + + /* + * Parts of the structure being constructed. + */ + void *st = *struct_ptr; /* Target structure. */ + asn_struct_ctx_t *ctx; /* Decoder context */ + + ber_tlv_tag_t tlv_tag; /* T from TLV */ + asn_dec_rval_t rval; /* Return code from subparsers */ + + ssize_t consumed_myself = 0; /* Consumed bytes from ptr */ + int edx; /* SEQUENCE element's index */ + + ASN_DEBUG("Decoding %s as SEQUENCE", td->name); + + /* + * Create the target structure if it is not present already. + */ + if(st == 0) { + st = *struct_ptr = CALLOC(1, specs->struct_size); + if(st == 0) { + RETURN(RC_FAIL); + } + } + + /* + * Restore parsing context. + */ + ctx = (asn_struct_ctx_t *)((char *)st + specs->ctx_offset); + + /* + * Start to parse where left previously + */ + switch(ctx->phase) { + case 0: + /* + * PHASE 0. + * Check that the set of tags associated with given structure + * perfectly fits our expectations. + */ + + rval = ber_check_tags(opt_codec_ctx, td, ctx, ptr, size, + tag_mode, 1, &ctx->left, 0); + if(rval.code != RC_OK) { + ASN_DEBUG("%s tagging check failed: %d", + td->name, rval.code); + return rval; + } + + if(ctx->left >= 0) + ctx->left += rval.consumed; /* ?Substracted below! */ + ADVANCE(rval.consumed); + + NEXT_PHASE(ctx); + + ASN_DEBUG("Structure consumes %ld bytes, buffer %ld", + (long)ctx->left, (long)size); + + /* Fall through */ + case 1: + /* + * PHASE 1. + * From the place where we've left it previously, + * try to decode the next member from the list of + * this structure's elements. + * (ctx->step) stores the member being processed + * between invocations and the microphase {0,1} of parsing + * that member: + * step = ( * 2 + ). + */ + for(edx = (ctx->step >> 1); edx < td->elements_count; + edx++, ctx->step = (ctx->step & ~1) + 2) { + void *memb_ptr; /* Pointer to the member */ + void **memb_ptr2; /* Pointer to that pointer */ + ssize_t tag_len; /* Length of TLV's T */ + int opt_edx_end; /* Next non-optional element */ + int use_bsearch; + int n; + + if(ctx->step & 1) + goto microphase2; + + /* + * MICROPHASE 1: Synchronize decoding. + */ + ASN_DEBUG("In %s SEQUENCE left %d, edx=%d flags=%d" + " opt=%d ec=%d", + td->name, (int)ctx->left, edx, + elements[edx].flags, elements[edx].optional, + td->elements_count); + + if(ctx->left == 0 /* No more stuff is expected */ + && ( + /* Explicit OPTIONAL specification reaches the end */ + (edx + elements[edx].optional + == td->elements_count) + || + /* All extensions are optional */ + (IN_EXTENSION_GROUP(specs, edx) + && specs->ext_before > td->elements_count) + ) + ) { + ASN_DEBUG("End of SEQUENCE %s", td->name); + /* + * Found the legitimate end of the structure. + */ + PHASE_OUT(ctx); + RETURN(RC_OK); + } + + /* + * Fetch the T from TLV. + */ + tag_len = ber_fetch_tag(ptr, LEFT, &tlv_tag); + ASN_DEBUG("Current tag in %s SEQUENCE for element %d " + "(%s) is %s encoded in %d bytes, of frame %ld", + td->name, edx, elements[edx].name, + ber_tlv_tag_string(tlv_tag), (int)tag_len, (long)LEFT); + switch(tag_len) { + case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE); + /* Fall through */ + case -1: RETURN(RC_FAIL); + } + + if(ctx->left < 0 && ((const uint8_t *)ptr)[0] == 0) { + if(LEFT < 2) { + if(SIZE_VIOLATION) + RETURN(RC_FAIL); + else + RETURN(RC_WMORE); + } else if(((const uint8_t *)ptr)[1] == 0) { + ASN_DEBUG("edx = %d, opt = %d, ec=%d", + edx, elements[edx].optional, + td->elements_count); + if((edx + elements[edx].optional + == td->elements_count) + || (IN_EXTENSION_GROUP(specs, edx) + && specs->ext_before + > td->elements_count)) { + /* + * Yeah, baby! Found the terminator + * of the indefinite length structure. + */ + /* + * Proceed to the canonical + * finalization function. + * No advancing is necessary. + */ + goto phase3; + } + } + } + + /* + * Find the next available type with this tag. + */ + use_bsearch = 0; + opt_edx_end = edx + elements[edx].optional + 1; + if(opt_edx_end > td->elements_count) + opt_edx_end = td->elements_count; /* Cap */ + else if(opt_edx_end - edx > 8) { + /* Limit the scope of linear search... */ + opt_edx_end = edx + 8; + use_bsearch = 1; + /* ... and resort to bsearch() */ + } + for(n = edx; n < opt_edx_end; n++) { + if(BER_TAGS_EQUAL(tlv_tag, elements[n].tag)) { + /* + * Found element corresponding to the tag + * being looked at. + * Reposition over the right element. + */ + edx = n; + ctx->step = 1 + 2 * edx; /* Remember! */ + goto microphase2; + } else if(elements[n].flags & ATF_OPEN_TYPE) { + /* + * This is the ANY type, which may bear + * any flag whatsoever. + */ + edx = n; + ctx->step = 1 + 2 * edx; /* Remember! */ + goto microphase2; + } else if(elements[n].tag == (ber_tlv_tag_t)-1) { + use_bsearch = 1; + break; + } + } + if(use_bsearch) { + /* + * Resort to a binary search over + * sorted array of tags. + */ + const asn_TYPE_tag2member_t *t2m; + asn_TYPE_tag2member_t key; + key.el_tag = tlv_tag; + key.el_no = edx; + t2m = (const asn_TYPE_tag2member_t *)bsearch(&key, + specs->tag2el, specs->tag2el_count, + sizeof(specs->tag2el[0]), _t2e_cmp); + if(t2m) { + const asn_TYPE_tag2member_t *best = 0; + const asn_TYPE_tag2member_t *t2m_f, *t2m_l; + int edx_max = edx + elements[edx].optional; + /* + * Rewind to the first element with that tag, + * `cause bsearch() does not guarantee order. + */ + t2m_f = t2m + t2m->toff_first; + t2m_l = t2m + t2m->toff_last; + for(t2m = t2m_f; t2m <= t2m_l; t2m++) { + if(t2m->el_no > edx_max) break; + if(t2m->el_no < edx) continue; + best = t2m; + } + if(best) { + edx = best->el_no; + ctx->step = 1 + 2 * edx; + goto microphase2; + } + } + n = opt_edx_end; + } + if(n == opt_edx_end) { + /* + * If tag is unknown, it may be either + * an unknown (thus, incorrect) tag, + * or an extension (...), + * or an end of the indefinite-length structure. + */ + if(!IN_EXTENSION_GROUP(specs, + edx + elements[edx].optional)) { + ASN_DEBUG("Unexpected tag %s (at %d)", + ber_tlv_tag_string(tlv_tag), edx); + ASN_DEBUG("Expected tag %s (%s)%s", + ber_tlv_tag_string(elements[edx].tag), + elements[edx].name, + elements[edx].optional + ?" or alternatives":""); + RETURN(RC_FAIL); + } else { + /* Skip this tag */ + ssize_t skip; + edx += elements[edx].optional; + + ASN_DEBUG("Skipping unexpected %s (at %d)", + ber_tlv_tag_string(tlv_tag), edx); + skip = ber_skip_length(opt_codec_ctx, + BER_TLV_CONSTRUCTED(ptr), + (const char *)ptr + tag_len, + LEFT - tag_len); + ASN_DEBUG("Skip length %d in %s", + (int)skip, td->name); + switch(skip) { + case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE); + /* Fall through */ + case -1: RETURN(RC_FAIL); + } + + ADVANCE(skip + tag_len); + ctx->step -= 2; + edx--; + continue; /* Try again with the next tag */ + } + } + + /* + * MICROPHASE 2: Invoke the member-specific decoder. + */ + ctx->step |= 1; /* Confirm entering next microphase */ + microphase2: + ASN_DEBUG("Inside SEQUENCE %s MF2", td->name); + + /* + * Compute the position of the member inside a structure, + * and also a type of containment (it may be contained + * as pointer or using inline inclusion). + */ + if(elements[edx].flags & ATF_POINTER) { + /* Member is a pointer to another structure */ + memb_ptr2 = (void **)((char *)st + elements[edx].memb_offset); + } else { + /* + * A pointer to a pointer + * holding the start of the structure + */ + memb_ptr = (char *)st + elements[edx].memb_offset; + memb_ptr2 = &memb_ptr; + } + /* + * Invoke the member fetch routine according to member's type + */ + rval = elements[edx].type->ber_decoder(opt_codec_ctx, + elements[edx].type, + memb_ptr2, ptr, LEFT, + elements[edx].tag_mode); + ASN_DEBUG("In %s SEQUENCE decoded %d %s of %d " + "in %d bytes rval.code %d, size=%d", + td->name, edx, elements[edx].type->name, + (int)LEFT, (int)rval.consumed, rval.code, (int)size); + switch(rval.code) { + case RC_OK: + break; + case RC_WMORE: /* More data expected */ + if(!SIZE_VIOLATION) { + ADVANCE(rval.consumed); + RETURN(RC_WMORE); + } + ASN_DEBUG("Size violation (c->l=%ld <= s=%ld)", + (long)ctx->left, (long)size); + /* Fall through */ + case RC_FAIL: /* Fatal error */ + RETURN(RC_FAIL); + } /* switch(rval) */ + + ADVANCE(rval.consumed); + } /* for(all structure members) */ + + phase3: + ctx->phase = 3; + case 3: /* 00 and other tags expected */ + case 4: /* only 00's expected */ + + ASN_DEBUG("SEQUENCE %s Leftover: %ld, size = %ld", + td->name, (long)ctx->left, (long)size); + + /* + * Skip everything until the end of the SEQUENCE. + */ + while(ctx->left) { + ssize_t tl, ll; + + tl = ber_fetch_tag(ptr, LEFT, &tlv_tag); + switch(tl) { + case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE); + /* Fall through */ + case -1: RETURN(RC_FAIL); + } + + /* + * If expected <0><0>... + */ + if(ctx->left < 0 + && ((const uint8_t *)ptr)[0] == 0) { + if(LEFT < 2) { + if(SIZE_VIOLATION) + RETURN(RC_FAIL); + else + RETURN(RC_WMORE); + } else if(((const uint8_t *)ptr)[1] == 0) { + /* + * Correctly finished with <0><0>. + */ + ADVANCE(2); + ctx->left++; + ctx->phase = 4; + continue; + } + } + + if(!IN_EXTENSION_GROUP(specs, td->elements_count) + || ctx->phase == 4) { + ASN_DEBUG("Unexpected continuation " + "of a non-extensible type " + "%s (SEQUENCE): %s", + td->name, + ber_tlv_tag_string(tlv_tag)); + RETURN(RC_FAIL); + } + + ll = ber_skip_length(opt_codec_ctx, + BER_TLV_CONSTRUCTED(ptr), + (const char *)ptr + tl, LEFT - tl); + switch(ll) { + case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE); + /* Fall through */ + case -1: RETURN(RC_FAIL); + } + + ADVANCE(tl + ll); + } + + PHASE_OUT(ctx); + } + + RETURN(RC_OK); +} + + +/* + * The DER encoder of the SEQUENCE type. + */ +asn_enc_rval_t +SEQUENCE_encode_der(asn_TYPE_descriptor_t *td, + void *sptr, int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + size_t computed_size = 0; + asn_enc_rval_t erval; + ssize_t ret; + int edx; + + ASN_DEBUG("%s %s as SEQUENCE", + cb?"Encoding":"Estimating", td->name); + + /* + * Gather the length of the underlying members sequence. + */ + for(edx = 0; edx < td->elements_count; edx++) { + asn_TYPE_member_t *elm = &td->elements[edx]; + void *memb_ptr; + if(elm->flags & ATF_POINTER) { + memb_ptr = *(void **)((char *)sptr + elm->memb_offset); + if(!memb_ptr) { + if(elm->optional) continue; + /* Mandatory element is missing */ + ASN__ENCODE_FAILED; + } + } else { + memb_ptr = (void *)((char *)sptr + elm->memb_offset); + } + erval = elm->type->der_encoder(elm->type, memb_ptr, + elm->tag_mode, elm->tag, + 0, 0); + if(erval.encoded == -1) + return erval; + computed_size += erval.encoded; + ASN_DEBUG("Member %d %s estimated %ld bytes", + edx, elm->name, (long)erval.encoded); + } + + /* + * Encode the TLV for the sequence itself. + */ + ret = der_write_tags(td, computed_size, tag_mode, 1, tag, cb, app_key); + ASN_DEBUG("Wrote tags: %ld (+%ld)", (long)ret, (long)computed_size); + if(ret == -1) + ASN__ENCODE_FAILED; + erval.encoded = computed_size + ret; + + if(!cb) ASN__ENCODED_OK(erval); + + /* + * Encode all members. + */ + for(edx = 0; edx < td->elements_count; edx++) { + asn_TYPE_member_t *elm = &td->elements[edx]; + asn_enc_rval_t tmperval; + void *memb_ptr; + + if(elm->flags & ATF_POINTER) { + memb_ptr = *(void **)((char *)sptr + elm->memb_offset); + if(!memb_ptr) continue; + } else { + memb_ptr = (void *)((char *)sptr + elm->memb_offset); + } + tmperval = elm->type->der_encoder(elm->type, memb_ptr, + elm->tag_mode, elm->tag, + cb, app_key); + if(tmperval.encoded == -1) + return tmperval; + computed_size -= tmperval.encoded; + ASN_DEBUG("Member %d %s of SEQUENCE %s encoded in %ld bytes", + edx, elm->name, td->name, (long)tmperval.encoded); + } + + if(computed_size != 0) + /* + * Encoded size is not equal to the computed size. + */ + ASN__ENCODE_FAILED; + + ASN__ENCODED_OK(erval); +} + + +#undef XER_ADVANCE +#define XER_ADVANCE(num_bytes) do { \ + size_t num = num_bytes; \ + buf_ptr = ((const char *)buf_ptr) + num;\ + size -= num; \ + consumed_myself += num; \ + } while(0) + +/* + * Decode the XER (XML) data. + */ +asn_dec_rval_t +SEQUENCE_decode_xer(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **struct_ptr, const char *opt_mname, + const void *buf_ptr, size_t size) { + /* + * Bring closer parts of structure description. + */ + asn_SEQUENCE_specifics_t *specs + = (asn_SEQUENCE_specifics_t *)td->specifics; + asn_TYPE_member_t *elements = td->elements; + const char *xml_tag = opt_mname ? opt_mname : td->xml_tag; + + /* + * ... and parts of the structure being constructed. + */ + void *st = *struct_ptr; /* Target structure. */ + asn_struct_ctx_t *ctx; /* Decoder context */ + + asn_dec_rval_t rval; /* Return value from a decoder */ + ssize_t consumed_myself = 0; /* Consumed bytes from ptr */ + int edx; /* Element index */ + int edx_end; + + /* + * Create the target structure if it is not present already. + */ + if(st == 0) { + st = *struct_ptr = CALLOC(1, specs->struct_size); + if(st == 0) RETURN(RC_FAIL); + } + + /* + * Restore parsing context. + */ + ctx = (asn_struct_ctx_t *)((char *)st + specs->ctx_offset); + + + /* + * Phases of XER/XML processing: + * Phase 0: Check that the opening tag matches our expectations. + * Phase 1: Processing body and reacting on closing tag. + * Phase 2: Processing inner type. + * Phase 3: Skipping unknown extensions. + * Phase 4: PHASED OUT + */ + for(edx = ctx->step; ctx->phase <= 3;) { + pxer_chunk_type_e ch_type; /* XER chunk type */ + ssize_t ch_size; /* Chunk size */ + xer_check_tag_e tcv; /* Tag check value */ + asn_TYPE_member_t *elm; + int n; + + /* + * Go inside the inner member of a sequence. + */ + if(ctx->phase == 2) { + asn_dec_rval_t tmprval; + void *memb_ptr; /* Pointer to the member */ + void **memb_ptr2; /* Pointer to that pointer */ + + elm = &td->elements[edx]; + + if(elm->flags & ATF_POINTER) { + /* Member is a pointer to another structure */ + memb_ptr2 = (void **)((char *)st + elm->memb_offset); + } else { + memb_ptr = (char *)st + elm->memb_offset; + memb_ptr2 = &memb_ptr; + } + + /* Invoke the inner type decoder, m.b. multiple times */ + tmprval = elm->type->xer_decoder(opt_codec_ctx, + elm->type, memb_ptr2, elm->name, + buf_ptr, size); + XER_ADVANCE(tmprval.consumed); + if(tmprval.code != RC_OK) + RETURN(tmprval.code); + ctx->phase = 1; /* Back to body processing */ + ctx->step = ++edx; + ASN_DEBUG("XER/SEQUENCE phase => %d, step => %d", + ctx->phase, ctx->step); + /* Fall through */ + } + + /* + * Get the next part of the XML stream. + */ + ch_size = xer_next_token(&ctx->context, buf_ptr, size, + &ch_type); + if(ch_size == -1) { + RETURN(RC_FAIL); + } else { + switch(ch_type) { + case PXER_WMORE: + RETURN(RC_WMORE); + case PXER_COMMENT: /* Got XML comment */ + case PXER_TEXT: /* Ignore free-standing text */ + XER_ADVANCE(ch_size); /* Skip silently */ + continue; + case PXER_TAG: + break; /* Check the rest down there */ + } + } + + tcv = xer_check_tag(buf_ptr, ch_size, xml_tag); + ASN_DEBUG("XER/SEQUENCE: tcv = %d, ph=%d [%s]", + tcv, ctx->phase, xml_tag); + + /* Skip the extensions section */ + if(ctx->phase == 3) { + switch(xer_skip_unknown(tcv, &ctx->left)) { + case -1: + ctx->phase = 4; + RETURN(RC_FAIL); + case 0: + XER_ADVANCE(ch_size); + continue; + case 1: + XER_ADVANCE(ch_size); + ctx->phase = 1; + continue; + case 2: + ctx->phase = 1; + break; + } + } + + switch(tcv) { + case XCT_CLOSING: + if(ctx->phase == 0) break; + ctx->phase = 0; + /* Fall through */ + case XCT_BOTH: + if(ctx->phase == 0) { + if(edx >= td->elements_count + || + /* Explicit OPTIONAL specs reaches the end */ + (edx + elements[edx].optional + == td->elements_count) + || + /* All extensions are optional */ + (IN_EXTENSION_GROUP(specs, edx) + && specs->ext_before + > td->elements_count) + ) { + XER_ADVANCE(ch_size); + ctx->phase = 4; /* Phase out */ + RETURN(RC_OK); + } else { + ASN_DEBUG("Premature end of XER SEQUENCE"); + RETURN(RC_FAIL); + } + } + /* Fall through */ + case XCT_OPENING: + if(ctx->phase == 0) { + XER_ADVANCE(ch_size); + ctx->phase = 1; /* Processing body phase */ + continue; + } + /* Fall through */ + case XCT_UNKNOWN_OP: + case XCT_UNKNOWN_BO: + + ASN_DEBUG("XER/SEQUENCE: tcv=%d, ph=%d, edx=%d", + tcv, ctx->phase, edx); + if(ctx->phase != 1) { + break; /* Really unexpected */ + } + + if(edx < td->elements_count) { + /* + * Search which member corresponds to this tag. + */ + edx_end = edx + elements[edx].optional + 1; + if(edx_end > td->elements_count) + edx_end = td->elements_count; + for(n = edx; n < edx_end; n++) { + elm = &td->elements[n]; + tcv = xer_check_tag(buf_ptr, + ch_size, elm->name); + switch(tcv) { + case XCT_BOTH: + case XCT_OPENING: + /* + * Process this member. + */ + ctx->step = edx = n; + ctx->phase = 2; + break; + case XCT_UNKNOWN_OP: + case XCT_UNKNOWN_BO: + continue; + default: + n = edx_end; + break; /* Phase out */ + } + break; + } + if(n != edx_end) + continue; + } else { + ASN_DEBUG("Out of defined members: %d/%d", + edx, td->elements_count); + } + + /* It is expected extension */ + if(IN_EXTENSION_GROUP(specs, + edx + (edx < td->elements_count + ? elements[edx].optional : 0))) { + ASN_DEBUG("Got anticipated extension at %d", + edx); + /* + * Check for (XCT_BOTH or XCT_UNKNOWN_BO) + * By using a mask. Only record a pure + * tags. + */ + if(tcv & XCT_CLOSING) { + /* Found without body */ + } else { + ctx->left = 1; + ctx->phase = 3; /* Skip ...'s */ + } + XER_ADVANCE(ch_size); + continue; + } + + /* Fall through */ + default: + break; + } + + ASN_DEBUG("Unexpected XML tag in SEQUENCE [%c%c%c%c%c%c]", + size>0?((const char *)buf_ptr)[0]:'.', + size>1?((const char *)buf_ptr)[1]:'.', + size>2?((const char *)buf_ptr)[2]:'.', + size>3?((const char *)buf_ptr)[3]:'.', + size>4?((const char *)buf_ptr)[4]:'.', + size>5?((const char *)buf_ptr)[5]:'.'); + break; + } + + ctx->phase = 4; /* "Phase out" on hard failure */ + RETURN(RC_FAIL); +} + +asn_enc_rval_t +SEQUENCE_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_enc_rval_t er; + int xcan = (flags & XER_F_CANONICAL); + int edx; + + if(!sptr) + ASN__ENCODE_FAILED; + + er.encoded = 0; + + for(edx = 0; edx < td->elements_count; edx++) { + asn_enc_rval_t tmper; + asn_TYPE_member_t *elm = &td->elements[edx]; + void *memb_ptr; + const char *mname = elm->name; + unsigned int mlen = strlen(mname); + + if(elm->flags & ATF_POINTER) { + memb_ptr = *(void **)((char *)sptr + elm->memb_offset); + if(!memb_ptr) { + if(elm->optional) + continue; + /* Mandatory element is missing */ + ASN__ENCODE_FAILED; + } + } else { + memb_ptr = (void *)((char *)sptr + elm->memb_offset); + } + + if(!xcan) ASN__TEXT_INDENT(1, ilevel); + ASN__CALLBACK3("<", 1, mname, mlen, ">", 1); + + /* Print the member itself */ + tmper = elm->type->xer_encoder(elm->type, memb_ptr, + ilevel + 1, flags, cb, app_key); + if(tmper.encoded == -1) return tmper; + + ASN__CALLBACK3("", 1); + er.encoded += 5 + (2 * mlen) + tmper.encoded; + } + + if(!xcan) ASN__TEXT_INDENT(1, ilevel - 1); + + ASN__ENCODED_OK(er); +cb_failed: + ASN__ENCODE_FAILED; +} + +int +SEQUENCE_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + int edx; + int ret; + + if(!sptr) return (cb("", 8, app_key) < 0) ? -1 : 0; + + /* Dump preamble */ + if(cb(td->name, strlen(td->name), app_key) < 0 + || cb(" ::= {", 6, app_key) < 0) + return -1; + + for(edx = 0; edx < td->elements_count; edx++) { + asn_TYPE_member_t *elm = &td->elements[edx]; + const void *memb_ptr; + + if(elm->flags & ATF_POINTER) { + memb_ptr = *(const void * const *)((const char *)sptr + elm->memb_offset); + if(!memb_ptr) { + if(elm->optional) continue; + /* Print line */ + /* Fall through */ + } + } else { + memb_ptr = (const void *)((const char *)sptr + elm->memb_offset); + } + + /* Indentation */ + _i_INDENT(1); + + /* Print the member's name and stuff */ + if(cb(elm->name, strlen(elm->name), app_key) < 0 + || cb(": ", 2, app_key) < 0) + return -1; + + /* Print the member itself */ + ret = elm->type->print_struct(elm->type, memb_ptr, ilevel + 1, + cb, app_key); + if(ret) return ret; + } + + ilevel--; + _i_INDENT(1); + + return (cb("}", 1, app_key) < 0) ? -1 : 0; +} + +void +SEQUENCE_free(asn_TYPE_descriptor_t *td, void *sptr, int contents_only) { + int edx; + + if(!td || !sptr) + return; + + ASN_DEBUG("Freeing %s as SEQUENCE", td->name); + + for(edx = 0; edx < td->elements_count; edx++) { + asn_TYPE_member_t *elm = &td->elements[edx]; + void *memb_ptr; + if(elm->flags & ATF_POINTER) { + memb_ptr = *(void **)((char *)sptr + elm->memb_offset); + if(memb_ptr) + ASN_STRUCT_FREE(*elm->type, memb_ptr); + } else { + memb_ptr = (void *)((char *)sptr + elm->memb_offset); + ASN_STRUCT_FREE_CONTENTS_ONLY(*elm->type, memb_ptr); + } + } + + if(!contents_only) { + FREEMEM(sptr); + } +} + +int +SEQUENCE_constraint(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + int edx; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + /* + * Iterate over structure members and check their validity. + */ + for(edx = 0; edx < td->elements_count; edx++) { + asn_TYPE_member_t *elm = &td->elements[edx]; + const void *memb_ptr; + + if(elm->flags & ATF_POINTER) { + memb_ptr = *(const void * const *)((const char *)sptr + elm->memb_offset); + if(!memb_ptr) { + if(elm->optional) + continue; + ASN__CTFAIL(app_key, td, sptr, + "%s: mandatory element %s absent (%s:%d)", + td->name, elm->name, __FILE__, __LINE__); + return -1; + } + } else { + memb_ptr = (const void *)((const char *)sptr + elm->memb_offset); + } + + if(elm->memb_constraints) { + int ret = elm->memb_constraints(elm->type, memb_ptr, + ctfailcb, app_key); + if(ret) return ret; + } else { + int ret = elm->type->check_constraints(elm->type, + memb_ptr, ctfailcb, app_key); + if(ret) return ret; + /* + * Cannot inherit it earlier: + * need to make sure we get the updated version. + */ + elm->memb_constraints = elm->type->check_constraints; + } + } + + return 0; +} + +asn_dec_rval_t +SEQUENCE_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) { + asn_SEQUENCE_specifics_t *specs = (asn_SEQUENCE_specifics_t *)td->specifics; + void *st = *sptr; /* Target structure. */ + int extpresent; /* Extension additions are present */ + uint8_t *opres; /* Presence of optional root members */ + asn_per_data_t opmd; + asn_dec_rval_t rv; + int edx; + + (void)constraints; + + if(ASN__STACK_OVERFLOW_CHECK(opt_codec_ctx)) + ASN__DECODE_FAILED; + + if(!st) { + st = *sptr = CALLOC(1, specs->struct_size); + if(!st) ASN__DECODE_FAILED; + } + + ASN_DEBUG("Decoding %s as SEQUENCE (UPER)", td->name); + + /* Handle extensions */ + if(specs->ext_before >= 0) { + extpresent = per_get_few_bits(pd, 1); + if(extpresent < 0) ASN__DECODE_STARVED; + } else { + extpresent = 0; + } + + /* Prepare a place and read-in the presence bitmap */ + memset(&opmd, 0, sizeof(opmd)); + if(specs->roms_count) { + opres = (uint8_t *)MALLOC(((specs->roms_count + 7) >> 3) + 1); + if(!opres) ASN__DECODE_FAILED; + /* Get the presence map */ + if(per_get_many_bits(pd, opres, 0, specs->roms_count)) { + FREEMEM(opres); + ASN__DECODE_STARVED; + } + opmd.buffer = opres; + opmd.nbits = specs->roms_count; + ASN_DEBUG("Read in presence bitmap for %s of %d bits (%x..)", + td->name, specs->roms_count, *opres); + } else { + opres = 0; + } + + /* + * Get the sequence ROOT elements. + */ + for(edx = 0; edx < td->elements_count; edx++) { + asn_TYPE_member_t *elm = &td->elements[edx]; + void *memb_ptr; /* Pointer to the member */ + void **memb_ptr2; /* Pointer to that pointer */ + + if(IN_EXTENSION_GROUP(specs, edx)) + continue; + + /* Fetch the pointer to this member */ + if(elm->flags & ATF_POINTER) { + memb_ptr2 = (void **)((char *)st + elm->memb_offset); + } else { + memb_ptr = (char *)st + elm->memb_offset; + memb_ptr2 = &memb_ptr; + } + + /* Deal with optionality */ + if(elm->optional) { + int present = per_get_few_bits(&opmd, 1); + ASN_DEBUG("Member %s->%s is optional, p=%d (%d->%d)", + td->name, elm->name, present, + (int)opmd.nboff, (int)opmd.nbits); + if(present == 0) { + /* This element is not present */ + if(elm->default_value) { + /* Fill-in DEFAULT */ + if(elm->default_value(1, memb_ptr2)) { + FREEMEM(opres); + ASN__DECODE_FAILED; + } + ASN_DEBUG("Filled-in default"); + } + /* The member is just not present */ + continue; + } + /* Fall through */ + } + + /* Fetch the member from the stream */ + ASN_DEBUG("Decoding member %s in %s", elm->name, td->name); + rv = elm->type->uper_decoder(opt_codec_ctx, elm->type, + elm->per_constraints, memb_ptr2, pd); + if(rv.code != RC_OK) { + ASN_DEBUG("Failed decode %s in %s", + elm->name, td->name); + FREEMEM(opres); + return rv; + } + } + + /* Optionality map is not needed anymore */ + FREEMEM(opres); + + /* + * Deal with extensions. + */ + if(extpresent) { + ssize_t bmlength; + uint8_t *epres; /* Presence of extension members */ + asn_per_data_t epmd; + + bmlength = uper_get_nslength(pd); + if(bmlength < 0) ASN__DECODE_STARVED; + + ASN_DEBUG("Extensions %ld present in %s", (long)bmlength, td->name); + + epres = (uint8_t *)MALLOC((bmlength + 15) >> 3); + if(!epres) ASN__DECODE_STARVED; + + /* Get the extensions map */ + if(per_get_many_bits(pd, epres, 0, bmlength)) { + FREEMEM(epres); + ASN__DECODE_STARVED; + } + + memset(&epmd, 0, sizeof(epmd)); + epmd.buffer = epres; + epmd.nbits = bmlength; + ASN_DEBUG("Read in extensions bitmap for %s of %ld bits (%x..)", + td->name, (long)bmlength, *epres); + + /* Go over extensions and read them in */ + for(edx = specs->ext_after + 1; edx < td->elements_count; edx++) { + asn_TYPE_member_t *elm = &td->elements[edx]; + void *memb_ptr; /* Pointer to the member */ + void **memb_ptr2; /* Pointer to that pointer */ + int present; + + if(!IN_EXTENSION_GROUP(specs, edx)) { + ASN_DEBUG("%d is not extension", edx); + continue; + } + + /* Fetch the pointer to this member */ + if(elm->flags & ATF_POINTER) { + memb_ptr2 = (void **)((char *)st + elm->memb_offset); + } else { + memb_ptr = (void *)((char *)st + elm->memb_offset); + memb_ptr2 = &memb_ptr; + } + + present = per_get_few_bits(&epmd, 1); + if(present <= 0) { + if(present < 0) break; /* No more extensions */ + continue; + } + + ASN_DEBUG("Decoding member %s in %s %p", elm->name, td->name, *memb_ptr2); + rv = uper_open_type_get(opt_codec_ctx, elm->type, + elm->per_constraints, memb_ptr2, pd); + if(rv.code != RC_OK) { + FREEMEM(epres); + return rv; + } + } + + /* Skip over overflow extensions which aren't present + * in this system's version of the protocol */ + for(;;) { + ASN_DEBUG("Getting overflow extensions"); + switch(per_get_few_bits(&epmd, 1)) { + case -1: break; + case 0: continue; + default: + if(uper_open_type_skip(opt_codec_ctx, pd)) { + FREEMEM(epres); + ASN__DECODE_STARVED; + } + } + break; + } + + FREEMEM(epres); + } + + /* Fill DEFAULT members in extensions */ + for(edx = specs->roms_count; edx < specs->roms_count + + specs->aoms_count; edx++) { + asn_TYPE_member_t *elm = &td->elements[edx]; + void **memb_ptr2; /* Pointer to member pointer */ + + if(!elm->default_value) continue; + + /* Fetch the pointer to this member */ + if(elm->flags & ATF_POINTER) { + memb_ptr2 = (void **)((char *)st + + elm->memb_offset); + if(*memb_ptr2) continue; + } else { + continue; /* Extensions are all optionals */ + } + + /* Set default value */ + if(elm->default_value(1, memb_ptr2)) { + ASN__DECODE_FAILED; + } + } + + rv.consumed = 0; + rv.code = RC_OK; + return rv; +} + +static int +SEQUENCE_handle_extensions(asn_TYPE_descriptor_t *td, void *sptr, + asn_per_outp_t *po1, asn_per_outp_t *po2) { + asn_SEQUENCE_specifics_t *specs + = (asn_SEQUENCE_specifics_t *)td->specifics; + int exts_present = 0; + int exts_count = 0; + int edx; + + if(specs->ext_before < 0) + return 0; + + /* Find out which extensions are present */ + for(edx = specs->ext_after + 1; edx < td->elements_count; edx++) { + asn_TYPE_member_t *elm = &td->elements[edx]; + void *memb_ptr; /* Pointer to the member */ + void **memb_ptr2; /* Pointer to that pointer */ + int present; + + if(!IN_EXTENSION_GROUP(specs, edx)) { + ASN_DEBUG("%s (@%d) is not extension", elm->type->name, edx); + continue; + } + + /* Fetch the pointer to this member */ + if(elm->flags & ATF_POINTER) { + memb_ptr2 = (void **)((char *)sptr + elm->memb_offset); + present = (*memb_ptr2 != 0); + } else { + memb_ptr = (void *)((char *)sptr + elm->memb_offset); + memb_ptr2 = &memb_ptr; + present = 1; + } + + ASN_DEBUG("checking %s (@%d) present => %d", + elm->type->name, edx, present); + exts_count++; + exts_present += present; + + /* Encode as presence marker */ + if(po1 && per_put_few_bits(po1, present, 1)) + return -1; + /* Encode as open type field */ + if(po2 && present && uper_open_type_put(elm->type, + elm->per_constraints, *memb_ptr2, po2)) + return -1; + + } + + return exts_present ? exts_count : 0; +} + +asn_enc_rval_t +SEQUENCE_encode_uper(asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) { + asn_SEQUENCE_specifics_t *specs + = (asn_SEQUENCE_specifics_t *)td->specifics; + asn_enc_rval_t er; + int n_extensions; + int edx; + int i; + + (void)constraints; + + if(!sptr) + ASN__ENCODE_FAILED; + + er.encoded = 0; + + ASN_DEBUG("Encoding %s as SEQUENCE (UPER)", td->name); + + + /* + * X.691#18.1 Whether structure is extensible + * and whether to encode extensions + */ + if(specs->ext_before >= 0) { + n_extensions = SEQUENCE_handle_extensions(td, sptr, 0, 0); + per_put_few_bits(po, n_extensions ? 1 : 0, 1); + } else { + n_extensions = 0; /* There are no extensions to encode */ + } + + /* Encode a presence bitmap */ + for(i = 0; i < specs->roms_count; i++) { + asn_TYPE_member_t *elm; + void *memb_ptr; /* Pointer to the member */ + void **memb_ptr2; /* Pointer to that pointer */ + int present; + + edx = specs->oms[i]; + elm = &td->elements[edx]; + + /* Fetch the pointer to this member */ + if(elm->flags & ATF_POINTER) { + memb_ptr2 = (void **)((char *)sptr + elm->memb_offset); + present = (*memb_ptr2 != 0); + } else { + memb_ptr = (void *)((char *)sptr + elm->memb_offset); + memb_ptr2 = &memb_ptr; + present = 1; + } + + /* Eliminate default values */ + if(present && elm->default_value + && elm->default_value(0, memb_ptr2) == 1) + present = 0; + + ASN_DEBUG("Element %s %s %s->%s is %s", + elm->flags & ATF_POINTER ? "ptr" : "inline", + elm->default_value ? "def" : "wtv", + td->name, elm->name, present ? "present" : "absent"); + if(per_put_few_bits(po, present, 1)) + ASN__ENCODE_FAILED; + } + + /* + * Encode the sequence ROOT elements. + */ + ASN_DEBUG("ext_after = %d, ec = %d, eb = %d", specs->ext_after, td->elements_count, specs->ext_before); + for(edx = 0; edx < ((specs->ext_after < 0) + ? td->elements_count : specs->ext_before - 1); edx++) { + + asn_TYPE_member_t *elm = &td->elements[edx]; + void *memb_ptr; /* Pointer to the member */ + void **memb_ptr2; /* Pointer to that pointer */ + + if(IN_EXTENSION_GROUP(specs, edx)) + continue; + + ASN_DEBUG("About to encode %s", elm->type->name); + + /* Fetch the pointer to this member */ + if(elm->flags & ATF_POINTER) { + memb_ptr2 = (void **)((char *)sptr + elm->memb_offset); + if(!*memb_ptr2) { + ASN_DEBUG("Element %s %d not present", + elm->name, edx); + if(elm->optional) + continue; + /* Mandatory element is missing */ + ASN__ENCODE_FAILED; + } + } else { + memb_ptr = (void *)((char *)sptr + elm->memb_offset); + memb_ptr2 = &memb_ptr; + } + + /* Eliminate default values */ + if(elm->default_value && elm->default_value(0, memb_ptr2) == 1) + continue; + + ASN_DEBUG("Encoding %s->%s", td->name, elm->name); + er = elm->type->uper_encoder(elm->type, elm->per_constraints, + *memb_ptr2, po); + if(er.encoded == -1) + return er; + } + + /* No extensions to encode */ + if(!n_extensions) ASN__ENCODED_OK(er); + + ASN_DEBUG("Length of %d bit-map", n_extensions); + /* #18.8. Write down the presence bit-map length. */ + if(uper_put_nslength(po, n_extensions)) + ASN__ENCODE_FAILED; + + ASN_DEBUG("Bit-map of %d elements", n_extensions); + /* #18.7. Encoding the extensions presence bit-map. */ + /* TODO: act upon NOTE in #18.7 for canonical PER */ + if(SEQUENCE_handle_extensions(td, sptr, po, 0) != n_extensions) + ASN__ENCODE_FAILED; + + ASN_DEBUG("Writing %d extensions", n_extensions); + /* #18.9. Encode extensions as open type fields. */ + if(SEQUENCE_handle_extensions(td, sptr, 0, po) != n_extensions) + ASN__ENCODE_FAILED; + + ASN__ENCODED_OK(er); +} + diff --git a/src/cryptoconditions/src/asn/constr_SEQUENCE.h b/src/cryptoconditions/src/asn/constr_SEQUENCE.h new file mode 100644 index 000000000..c2aeb6676 --- /dev/null +++ b/src/cryptoconditions/src/asn/constr_SEQUENCE.h @@ -0,0 +1,60 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _CONSTR_SEQUENCE_H_ +#define _CONSTR_SEQUENCE_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef const struct asn_SEQUENCE_specifics_s { + /* + * Target structure description. + */ + int struct_size; /* Size of the target structure. */ + int ctx_offset; /* Offset of the asn_struct_ctx_t member */ + + /* + * Tags to members mapping table (sorted). + */ + const asn_TYPE_tag2member_t *tag2el; + int tag2el_count; + + /* + * Optional members of the extensions root (roms) or additions (aoms). + * Meaningful for PER. + */ + const int *oms; /* Optional MemberS */ + int roms_count; /* Root optional members count */ + int aoms_count; /* Additions optional members count */ + + /* + * Description of an extensions group. + */ + int ext_after; /* Extensions start after this member */ + int ext_before; /* Extensions stop before this member */ +} asn_SEQUENCE_specifics_t; + + +/* + * A set specialized functions dealing with the SEQUENCE type. + */ +asn_struct_free_f SEQUENCE_free; +asn_struct_print_f SEQUENCE_print; +asn_constr_check_f SEQUENCE_constraint; +ber_type_decoder_f SEQUENCE_decode_ber; +der_type_encoder_f SEQUENCE_encode_der; +xer_type_decoder_f SEQUENCE_decode_xer; +xer_type_encoder_f SEQUENCE_encode_xer; +per_type_decoder_f SEQUENCE_decode_uper; +per_type_encoder_f SEQUENCE_encode_uper; + +#ifdef __cplusplus +} +#endif + +#endif /* _CONSTR_SEQUENCE_H_ */ diff --git a/src/cryptoconditions/src/asn/constr_SET_OF.c b/src/cryptoconditions/src/asn/constr_SET_OF.c new file mode 100644 index 000000000..2dbc6e518 --- /dev/null +++ b/src/cryptoconditions/src/asn/constr_SET_OF.c @@ -0,0 +1,954 @@ +/*- + * Copyright (c) 2003, 2004, 2005 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +/* + * Number of bytes left for this structure. + * (ctx->left) indicates the number of bytes _transferred_ for the structure. + * (size) contains the number of bytes in the buffer passed. + */ +#define LEFT ((size<(size_t)ctx->left)?size:(size_t)ctx->left) + +/* + * If the subprocessor function returns with an indication that it wants + * more data, it may well be a fatal decoding problem, because the + * size is constrained by the 's L, even if the buffer size allows + * reading more data. + * For example, consider the buffer containing the following TLVs: + * ... + * The TLV length clearly indicates that one byte is expected in V, but + * if the V processor returns with "want more data" even if the buffer + * contains way more data than the V processor have seen. + */ +#define SIZE_VIOLATION (ctx->left >= 0 && (size_t)ctx->left <= size) + +/* + * This macro "eats" the part of the buffer which is definitely "consumed", + * i.e. was correctly converted into local representation or rightfully skipped. + */ +#undef ADVANCE +#define ADVANCE(num_bytes) do { \ + size_t num = num_bytes; \ + ptr = ((const char *)ptr) + num;\ + size -= num; \ + if(ctx->left >= 0) \ + ctx->left -= num; \ + consumed_myself += num; \ + } while(0) + +/* + * Switch to the next phase of parsing. + */ +#undef NEXT_PHASE +#undef PHASE_OUT +#define NEXT_PHASE(ctx) do { \ + ctx->phase++; \ + ctx->step = 0; \ + } while(0) +#define PHASE_OUT(ctx) do { ctx->phase = 10; } while(0) + +/* + * Return a standardized complex structure. + */ +#undef RETURN +#define RETURN(_code) do { \ + rval.code = _code; \ + rval.consumed = consumed_myself;\ + return rval; \ + } while(0) + +/* + * The decoder of the SET OF type. + */ +asn_dec_rval_t +SET_OF_decode_ber(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **struct_ptr, const void *ptr, size_t size, int tag_mode) { + /* + * Bring closer parts of structure description. + */ + asn_SET_OF_specifics_t *specs = (asn_SET_OF_specifics_t *)td->specifics; + asn_TYPE_member_t *elm = td->elements; /* Single one */ + + /* + * Parts of the structure being constructed. + */ + void *st = *struct_ptr; /* Target structure. */ + asn_struct_ctx_t *ctx; /* Decoder context */ + + ber_tlv_tag_t tlv_tag; /* T from TLV */ + asn_dec_rval_t rval; /* Return code from subparsers */ + + ssize_t consumed_myself = 0; /* Consumed bytes from ptr */ + + ASN_DEBUG("Decoding %s as SET OF", td->name); + + /* + * Create the target structure if it is not present already. + */ + if(st == 0) { + st = *struct_ptr = CALLOC(1, specs->struct_size); + if(st == 0) { + RETURN(RC_FAIL); + } + } + + /* + * Restore parsing context. + */ + ctx = (asn_struct_ctx_t *)((char *)st + specs->ctx_offset); + + /* + * Start to parse where left previously + */ + switch(ctx->phase) { + case 0: + /* + * PHASE 0. + * Check that the set of tags associated with given structure + * perfectly fits our expectations. + */ + + rval = ber_check_tags(opt_codec_ctx, td, ctx, ptr, size, + tag_mode, 1, &ctx->left, 0); + if(rval.code != RC_OK) { + ASN_DEBUG("%s tagging check failed: %d", + td->name, rval.code); + return rval; + } + + if(ctx->left >= 0) + ctx->left += rval.consumed; /* ?Substracted below! */ + ADVANCE(rval.consumed); + + ASN_DEBUG("Structure consumes %ld bytes, " + "buffer %ld", (long)ctx->left, (long)size); + + NEXT_PHASE(ctx); + /* Fall through */ + case 1: + /* + * PHASE 1. + * From the place where we've left it previously, + * try to decode the next item. + */ + for(;; ctx->step = 0) { + ssize_t tag_len; /* Length of TLV's T */ + + if(ctx->step & 1) + goto microphase2; + + /* + * MICROPHASE 1: Synchronize decoding. + */ + + if(ctx->left == 0) { + ASN_DEBUG("End of SET OF %s", td->name); + /* + * No more things to decode. + * Exit out of here. + */ + PHASE_OUT(ctx); + RETURN(RC_OK); + } + + /* + * Fetch the T from TLV. + */ + tag_len = ber_fetch_tag(ptr, LEFT, &tlv_tag); + switch(tag_len) { + case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE); + /* Fall through */ + case -1: RETURN(RC_FAIL); + } + + if(ctx->left < 0 && ((const uint8_t *)ptr)[0] == 0) { + if(LEFT < 2) { + if(SIZE_VIOLATION) + RETURN(RC_FAIL); + else + RETURN(RC_WMORE); + } else if(((const uint8_t *)ptr)[1] == 0) { + /* + * Found the terminator of the + * indefinite length structure. + */ + break; + } + } + + /* Outmost tag may be unknown and cannot be fetched/compared */ + if(elm->tag != (ber_tlv_tag_t)-1) { + if(BER_TAGS_EQUAL(tlv_tag, elm->tag)) { + /* + * The new list member of expected type has arrived. + */ + } else { + ASN_DEBUG("Unexpected tag %s fixed SET OF %s", + ber_tlv_tag_string(tlv_tag), td->name); + ASN_DEBUG("%s SET OF has tag %s", + td->name, ber_tlv_tag_string(elm->tag)); + RETURN(RC_FAIL); + } + } + + /* + * MICROPHASE 2: Invoke the member-specific decoder. + */ + ctx->step |= 1; /* Confirm entering next microphase */ + microphase2: + + /* + * Invoke the member fetch routine according to member's type + */ + rval = elm->type->ber_decoder(opt_codec_ctx, + elm->type, &ctx->ptr, ptr, LEFT, 0); + ASN_DEBUG("In %s SET OF %s code %d consumed %d", + td->name, elm->type->name, + rval.code, (int)rval.consumed); + switch(rval.code) { + case RC_OK: + { + asn_anonymous_set_ *list = _A_SET_FROM_VOID(st); + if(ASN_SET_ADD(list, ctx->ptr) != 0) + RETURN(RC_FAIL); + else + ctx->ptr = 0; + } + break; + case RC_WMORE: /* More data expected */ + if(!SIZE_VIOLATION) { + ADVANCE(rval.consumed); + RETURN(RC_WMORE); + } + /* Fall through */ + case RC_FAIL: /* Fatal error */ + ASN_STRUCT_FREE(*elm->type, ctx->ptr); + ctx->ptr = 0; + RETURN(RC_FAIL); + } /* switch(rval) */ + + ADVANCE(rval.consumed); + } /* for(all list members) */ + + NEXT_PHASE(ctx); + case 2: + /* + * Read in all "end of content" TLVs. + */ + while(ctx->left < 0) { + if(LEFT < 2) { + if(LEFT > 0 && ((const char *)ptr)[0] != 0) { + /* Unexpected tag */ + RETURN(RC_FAIL); + } else { + RETURN(RC_WMORE); + } + } + if(((const char *)ptr)[0] == 0 + && ((const char *)ptr)[1] == 0) { + ADVANCE(2); + ctx->left++; + } else { + RETURN(RC_FAIL); + } + } + + PHASE_OUT(ctx); + } + + RETURN(RC_OK); +} + +/* + * Internally visible buffer holding a single encoded element. + */ +struct _el_buffer { + uint8_t *buf; + size_t length; + size_t size; +}; +/* Append bytes to the above structure */ +static int _el_addbytes(const void *buffer, size_t size, void *el_buf_ptr) { + struct _el_buffer *el_buf = (struct _el_buffer *)el_buf_ptr; + + if(el_buf->length + size > el_buf->size) + return -1; + + memcpy(el_buf->buf + el_buf->length, buffer, size); + + el_buf->length += size; + return 0; +} +static int _el_buf_cmp(const void *ap, const void *bp) { + const struct _el_buffer *a = (const struct _el_buffer *)ap; + const struct _el_buffer *b = (const struct _el_buffer *)bp; + int ret; + size_t common_len; + + if(a->length < b->length) + common_len = a->length; + else + common_len = b->length; + + ret = memcmp(a->buf, b->buf, common_len); + if(ret == 0) { + if(a->length < b->length) + ret = -1; + else if(a->length > b->length) + ret = 1; + } + + return ret; +} + +/* + * The DER encoder of the SET OF type. + */ +asn_enc_rval_t +SET_OF_encode_der(asn_TYPE_descriptor_t *td, void *ptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_TYPE_member_t *elm = td->elements; + asn_TYPE_descriptor_t *elm_type = elm->type; + der_type_encoder_f *der_encoder = elm_type->der_encoder; + asn_anonymous_set_ *list = _A_SET_FROM_VOID(ptr); + size_t computed_size = 0; + ssize_t encoding_size = 0; + struct _el_buffer *encoded_els; + ssize_t eels_count = 0; + size_t max_encoded_len = 1; + asn_enc_rval_t erval; + int ret; + int edx; + + ASN_DEBUG("Estimating size for SET OF %s", td->name); + + /* + * Gather the length of the underlying members sequence. + */ + for(edx = 0; edx < list->count; edx++) { + void *memb_ptr = list->array[edx]; + if(!memb_ptr) continue; + erval = der_encoder(elm_type, memb_ptr, 0, elm->tag, 0, 0); + if(erval.encoded == -1) + return erval; + computed_size += erval.encoded; + + /* Compute maximum encoding's size */ + if(max_encoded_len < (size_t)erval.encoded) + max_encoded_len = erval.encoded; + } + + /* + * Encode the TLV for the sequence itself. + */ + encoding_size = der_write_tags(td, computed_size, tag_mode, 1, tag, + cb, app_key); + if(encoding_size == -1) { + erval.encoded = -1; + erval.failed_type = td; + erval.structure_ptr = ptr; + return erval; + } + computed_size += encoding_size; + + if(!cb || list->count == 0) { + erval.encoded = computed_size; + ASN__ENCODED_OK(erval); + } + + /* + * DER mandates dynamic sorting of the SET OF elements + * according to their encodings. Build an array of the + * encoded elements. + */ + encoded_els = (struct _el_buffer *)MALLOC( + list->count * sizeof(encoded_els[0])); + if(encoded_els == NULL) { + erval.encoded = -1; + erval.failed_type = td; + erval.structure_ptr = ptr; + return erval; + } + + ASN_DEBUG("Encoding members of %s SET OF", td->name); + + /* + * Encode all members. + */ + for(edx = 0; edx < list->count; edx++) { + void *memb_ptr = list->array[edx]; + struct _el_buffer *encoded_el = &encoded_els[eels_count]; + + if(!memb_ptr) continue; + + /* + * Prepare space for encoding. + */ + encoded_el->buf = (uint8_t *)MALLOC(max_encoded_len); + if(encoded_el->buf) { + encoded_el->length = 0; + encoded_el->size = max_encoded_len; + } else { + for(edx--; edx >= 0; edx--) + FREEMEM(encoded_els[edx].buf); + FREEMEM(encoded_els); + erval.encoded = -1; + erval.failed_type = td; + erval.structure_ptr = ptr; + return erval; + } + + /* + * Encode the member into the prepared space. + */ + erval = der_encoder(elm_type, memb_ptr, 0, elm->tag, + _el_addbytes, encoded_el); + if(erval.encoded == -1) { + for(; edx >= 0; edx--) + FREEMEM(encoded_els[edx].buf); + FREEMEM(encoded_els); + return erval; + } + encoding_size += erval.encoded; + eels_count++; + } + + /* + * Sort the encoded elements according to their encoding. + */ + qsort(encoded_els, eels_count, sizeof(encoded_els[0]), _el_buf_cmp); + + /* + * Report encoded elements to the application. + * Dispose of temporary sorted members table. + */ + ret = 0; + for(edx = 0; edx < eels_count; edx++) { + struct _el_buffer *encoded_el = &encoded_els[edx]; + /* Report encoded chunks to the application */ + if(ret == 0 + && cb(encoded_el->buf, encoded_el->length, app_key) < 0) + ret = -1; + FREEMEM(encoded_el->buf); + } + FREEMEM(encoded_els); + + if(ret || computed_size != (size_t)encoding_size) { + /* + * Standard callback failed, or + * encoded size is not equal to the computed size. + */ + erval.encoded = -1; + erval.failed_type = td; + erval.structure_ptr = ptr; + } else { + erval.encoded = computed_size; + } + + ASN__ENCODED_OK(erval); +} + +#undef XER_ADVANCE +#define XER_ADVANCE(num_bytes) do { \ + size_t num = num_bytes; \ + buf_ptr = ((const char *)buf_ptr) + num;\ + size -= num; \ + consumed_myself += num; \ + } while(0) + +/* + * Decode the XER (XML) data. + */ +asn_dec_rval_t +SET_OF_decode_xer(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **struct_ptr, const char *opt_mname, + const void *buf_ptr, size_t size) { + /* + * Bring closer parts of structure description. + */ + asn_SET_OF_specifics_t *specs = (asn_SET_OF_specifics_t *)td->specifics; + asn_TYPE_member_t *element = td->elements; + const char *elm_tag; + const char *xml_tag = opt_mname ? opt_mname : td->xml_tag; + + /* + * ... and parts of the structure being constructed. + */ + void *st = *struct_ptr; /* Target structure. */ + asn_struct_ctx_t *ctx; /* Decoder context */ + + asn_dec_rval_t rval; /* Return value from a decoder */ + ssize_t consumed_myself = 0; /* Consumed bytes from ptr */ + + /* + * Create the target structure if it is not present already. + */ + if(st == 0) { + st = *struct_ptr = CALLOC(1, specs->struct_size); + if(st == 0) RETURN(RC_FAIL); + } + + /* Which tag is expected for the downstream */ + if(specs->as_XMLValueList) { + elm_tag = (specs->as_XMLValueList == 1) ? 0 : ""; + } else { + elm_tag = (*element->name) + ? element->name : element->type->xml_tag; + } + + /* + * Restore parsing context. + */ + ctx = (asn_struct_ctx_t *)((char *)st + specs->ctx_offset); + + /* + * Phases of XER/XML processing: + * Phase 0: Check that the opening tag matches our expectations. + * Phase 1: Processing body and reacting on closing tag. + * Phase 2: Processing inner type. + */ + for(; ctx->phase <= 2;) { + pxer_chunk_type_e ch_type; /* XER chunk type */ + ssize_t ch_size; /* Chunk size */ + xer_check_tag_e tcv; /* Tag check value */ + + /* + * Go inside the inner member of a set. + */ + if(ctx->phase == 2) { + asn_dec_rval_t tmprval; + + /* Invoke the inner type decoder, m.b. multiple times */ + ASN_DEBUG("XER/SET OF element [%s]", elm_tag); + tmprval = element->type->xer_decoder(opt_codec_ctx, + element->type, &ctx->ptr, elm_tag, + buf_ptr, size); + if(tmprval.code == RC_OK) { + asn_anonymous_set_ *list = _A_SET_FROM_VOID(st); + if(ASN_SET_ADD(list, ctx->ptr) != 0) + RETURN(RC_FAIL); + ctx->ptr = 0; + XER_ADVANCE(tmprval.consumed); + } else { + XER_ADVANCE(tmprval.consumed); + RETURN(tmprval.code); + } + ctx->phase = 1; /* Back to body processing */ + ASN_DEBUG("XER/SET OF phase => %d", ctx->phase); + /* Fall through */ + } + + /* + * Get the next part of the XML stream. + */ + ch_size = xer_next_token(&ctx->context, + buf_ptr, size, &ch_type); + if(ch_size == -1) { + RETURN(RC_FAIL); + } else { + switch(ch_type) { + case PXER_WMORE: + RETURN(RC_WMORE); + case PXER_COMMENT: /* Got XML comment */ + case PXER_TEXT: /* Ignore free-standing text */ + XER_ADVANCE(ch_size); /* Skip silently */ + continue; + case PXER_TAG: + break; /* Check the rest down there */ + } + } + + tcv = xer_check_tag(buf_ptr, ch_size, xml_tag); + ASN_DEBUG("XER/SET OF: tcv = %d, ph=%d t=%s", + tcv, ctx->phase, xml_tag); + switch(tcv) { + case XCT_CLOSING: + if(ctx->phase == 0) break; + ctx->phase = 0; + /* Fall through */ + case XCT_BOTH: + if(ctx->phase == 0) { + /* No more things to decode */ + XER_ADVANCE(ch_size); + ctx->phase = 3; /* Phase out */ + RETURN(RC_OK); + } + /* Fall through */ + case XCT_OPENING: + if(ctx->phase == 0) { + XER_ADVANCE(ch_size); + ctx->phase = 1; /* Processing body phase */ + continue; + } + /* Fall through */ + case XCT_UNKNOWN_OP: + case XCT_UNKNOWN_BO: + + ASN_DEBUG("XER/SET OF: tcv=%d, ph=%d", tcv, ctx->phase); + if(ctx->phase == 1) { + /* + * Process a single possible member. + */ + ctx->phase = 2; + continue; + } + /* Fall through */ + default: + break; + } + + ASN_DEBUG("Unexpected XML tag in SET OF"); + break; + } + + ctx->phase = 3; /* "Phase out" on hard failure */ + RETURN(RC_FAIL); +} + + + +typedef struct xer_tmp_enc_s { + void *buffer; + size_t offset; + size_t size; +} xer_tmp_enc_t; +static int +SET_OF_encode_xer_callback(const void *buffer, size_t size, void *key) { + xer_tmp_enc_t *t = (xer_tmp_enc_t *)key; + if(t->offset + size >= t->size) { + size_t newsize = (t->size << 2) + size; + void *p = REALLOC(t->buffer, newsize); + if(!p) return -1; + t->buffer = p; + t->size = newsize; + } + memcpy((char *)t->buffer + t->offset, buffer, size); + t->offset += size; + return 0; +} +static int +SET_OF_xer_order(const void *aptr, const void *bptr) { + const xer_tmp_enc_t *a = (const xer_tmp_enc_t *)aptr; + const xer_tmp_enc_t *b = (const xer_tmp_enc_t *)bptr; + size_t minlen = a->offset; + int ret; + if(b->offset < minlen) minlen = b->offset; + /* Well-formed UTF-8 has this nice lexicographical property... */ + ret = memcmp(a->buffer, b->buffer, minlen); + if(ret != 0) return ret; + if(a->offset == b->offset) + return 0; + if(a->offset == minlen) + return -1; + return 1; +} + + +asn_enc_rval_t +SET_OF_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_enc_rval_t er; + asn_SET_OF_specifics_t *specs = (asn_SET_OF_specifics_t *)td->specifics; + asn_TYPE_member_t *elm = td->elements; + asn_anonymous_set_ *list = _A_SET_FROM_VOID(sptr); + const char *mname = specs->as_XMLValueList + ? 0 : ((*elm->name) ? elm->name : elm->type->xml_tag); + size_t mlen = mname ? strlen(mname) : 0; + int xcan = (flags & XER_F_CANONICAL); + xer_tmp_enc_t *encs = 0; + size_t encs_count = 0; + void *original_app_key = app_key; + asn_app_consume_bytes_f *original_cb = cb; + int i; + + if(!sptr) ASN__ENCODE_FAILED; + + if(xcan) { + encs = (xer_tmp_enc_t *)MALLOC(list->count * sizeof(encs[0])); + if(!encs) ASN__ENCODE_FAILED; + cb = SET_OF_encode_xer_callback; + } + + er.encoded = 0; + + for(i = 0; i < list->count; i++) { + asn_enc_rval_t tmper; + + void *memb_ptr = list->array[i]; + if(!memb_ptr) continue; + + if(encs) { + memset(&encs[encs_count], 0, sizeof(encs[0])); + app_key = &encs[encs_count]; + encs_count++; + } + + if(mname) { + if(!xcan) ASN__TEXT_INDENT(1, ilevel); + ASN__CALLBACK3("<", 1, mname, mlen, ">", 1); + } + + if(!xcan && specs->as_XMLValueList == 1) + ASN__TEXT_INDENT(1, ilevel + 1); + tmper = elm->type->xer_encoder(elm->type, memb_ptr, + ilevel + (specs->as_XMLValueList != 2), + flags, cb, app_key); + if(tmper.encoded == -1) { + td = tmper.failed_type; + sptr = tmper.structure_ptr; + goto cb_failed; + } + if(tmper.encoded == 0 && specs->as_XMLValueList) { + const char *name = elm->type->xml_tag; + size_t len = strlen(name); + ASN__CALLBACK3("<", 1, name, len, "/>", 2); + } + + if(mname) { + ASN__CALLBACK3("", 1); + er.encoded += 5; + } + + er.encoded += (2 * mlen) + tmper.encoded; + } + + if(!xcan) ASN__TEXT_INDENT(1, ilevel - 1); + + if(encs) { + xer_tmp_enc_t *enc = encs; + xer_tmp_enc_t *end = encs + encs_count; + ssize_t control_size = 0; + + cb = original_cb; + app_key = original_app_key; + qsort(encs, encs_count, sizeof(encs[0]), SET_OF_xer_order); + + for(; enc < end; enc++) { + ASN__CALLBACK(enc->buffer, enc->offset); + FREEMEM(enc->buffer); + enc->buffer = 0; + control_size += enc->offset; + } + assert(control_size == er.encoded); + } + + goto cleanup; +cb_failed: + er.encoded = -1; + er.failed_type = td; + er.structure_ptr = sptr; +cleanup: + if(encs) { + while(encs_count-- > 0) { + if(encs[encs_count].buffer) + FREEMEM(encs[encs_count].buffer); + } + FREEMEM(encs); + } + ASN__ENCODED_OK(er); +} + +int +SET_OF_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_TYPE_member_t *elm = td->elements; + const asn_anonymous_set_ *list = _A_CSET_FROM_VOID(sptr); + int ret; + int i; + + if(!sptr) return (cb("", 8, app_key) < 0) ? -1 : 0; + + /* Dump preamble */ + if(cb(td->name, strlen(td->name), app_key) < 0 + || cb(" ::= {", 6, app_key) < 0) + return -1; + + for(i = 0; i < list->count; i++) { + const void *memb_ptr = list->array[i]; + if(!memb_ptr) continue; + + _i_INDENT(1); + + ret = elm->type->print_struct(elm->type, memb_ptr, + ilevel + 1, cb, app_key); + if(ret) return ret; + } + + ilevel--; + _i_INDENT(1); + + return (cb("}", 1, app_key) < 0) ? -1 : 0; +} + +void +SET_OF_free(asn_TYPE_descriptor_t *td, void *ptr, int contents_only) { + if(td && ptr) { + asn_SET_OF_specifics_t *specs; + asn_TYPE_member_t *elm = td->elements; + asn_anonymous_set_ *list = _A_SET_FROM_VOID(ptr); + asn_struct_ctx_t *ctx; /* Decoder context */ + int i; + + /* + * Could not use set_of_empty() because of (*free) + * incompatibility. + */ + for(i = 0; i < list->count; i++) { + void *memb_ptr = list->array[i]; + if(memb_ptr) + ASN_STRUCT_FREE(*elm->type, memb_ptr); + } + list->count = 0; /* No meaningful elements left */ + + asn_set_empty(list); /* Remove (list->array) */ + + specs = (asn_SET_OF_specifics_t *)td->specifics; + ctx = (asn_struct_ctx_t *)((char *)ptr + specs->ctx_offset); + if(ctx->ptr) { + ASN_STRUCT_FREE(*elm->type, ctx->ptr); + ctx->ptr = 0; + } + + if(!contents_only) { + FREEMEM(ptr); + } + } +} + +int +SET_OF_constraint(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + asn_TYPE_member_t *elm = td->elements; + asn_constr_check_f *constr; + const asn_anonymous_set_ *list = _A_CSET_FROM_VOID(sptr); + int i; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + constr = elm->memb_constraints; + if(!constr) constr = elm->type->check_constraints; + + /* + * Iterate over the members of an array. + * Validate each in turn, until one fails. + */ + for(i = 0; i < list->count; i++) { + const void *memb_ptr = list->array[i]; + int ret; + + if(!memb_ptr) continue; + + ret = constr(elm->type, memb_ptr, ctfailcb, app_key); + if(ret) return ret; + } + + /* + * Cannot inherit it eralier: + * need to make sure we get the updated version. + */ + if(!elm->memb_constraints) + elm->memb_constraints = elm->type->check_constraints; + + return 0; +} + +asn_dec_rval_t +SET_OF_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) { + asn_dec_rval_t rv; + asn_SET_OF_specifics_t *specs = (asn_SET_OF_specifics_t *)td->specifics; + asn_TYPE_member_t *elm = td->elements; /* Single one */ + void *st = *sptr; + asn_anonymous_set_ *list; + asn_per_constraint_t *ct; + int repeat = 0; + ssize_t nelems; + + if(ASN__STACK_OVERFLOW_CHECK(opt_codec_ctx)) + ASN__DECODE_FAILED; + + /* + * Create the target structure if it is not present already. + */ + if(!st) { + st = *sptr = CALLOC(1, specs->struct_size); + if(!st) ASN__DECODE_FAILED; + } + list = _A_SET_FROM_VOID(st); + + /* Figure out which constraints to use */ + if(constraints) ct = &constraints->size; + else if(td->per_constraints) ct = &td->per_constraints->size; + else ct = 0; + + if(ct && ct->flags & APC_EXTENSIBLE) { + int value = per_get_few_bits(pd, 1); + if(value < 0) ASN__DECODE_STARVED; + if(value) ct = 0; /* Not restricted! */ + } + + if(ct && ct->effective_bits >= 0) { + /* X.691, #19.5: No length determinant */ + nelems = per_get_few_bits(pd, ct->effective_bits); + ASN_DEBUG("Preparing to fetch %ld+%ld elements from %s", + (long)nelems, ct->lower_bound, td->name); + if(nelems < 0) ASN__DECODE_STARVED; + nelems += ct->lower_bound; + } else { + nelems = -1; + } + + do { + int i; + if(nelems < 0) { + nelems = uper_get_length(pd, + ct ? ct->effective_bits : -1, &repeat); + ASN_DEBUG("Got to decode %d elements (eff %d)", + (int)nelems, (int)(ct ? ct->effective_bits : -1)); + if(nelems < 0) ASN__DECODE_STARVED; + } + + for(i = 0; i < nelems; i++) { + void *ptr = 0; + ASN_DEBUG("SET OF %s decoding", elm->type->name); + rv = elm->type->uper_decoder(opt_codec_ctx, elm->type, + elm->per_constraints, &ptr, pd); + ASN_DEBUG("%s SET OF %s decoded %d, %p", + td->name, elm->type->name, rv.code, ptr); + if(rv.code == RC_OK) { + if(ASN_SET_ADD(list, ptr) == 0) + continue; + ASN_DEBUG("Failed to add element into %s", + td->name); + /* Fall through */ + rv.code = RC_FAIL; + } else { + ASN_DEBUG("Failed decoding %s of %s (SET OF)", + elm->type->name, td->name); + } + if(ptr) ASN_STRUCT_FREE(*elm->type, ptr); + return rv; + } + + nelems = -1; /* Allow uper_get_length() */ + } while(repeat); + + ASN_DEBUG("Decoded %s as SET OF", td->name); + + rv.code = RC_OK; + rv.consumed = 0; + return rv; +} + diff --git a/src/cryptoconditions/src/asn/constr_SET_OF.h b/src/cryptoconditions/src/asn/constr_SET_OF.h new file mode 100644 index 000000000..75e18cfa0 --- /dev/null +++ b/src/cryptoconditions/src/asn/constr_SET_OF.h @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 2003 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _CONSTR_SET_OF_H_ +#define _CONSTR_SET_OF_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef const struct asn_SET_OF_specifics_s { + /* + * Target structure description. + */ + int struct_size; /* Size of the target structure. */ + int ctx_offset; /* Offset of the asn_struct_ctx_t member */ + + /* XER-specific stuff */ + int as_XMLValueList; /* The member type must be encoded like this */ +} asn_SET_OF_specifics_t; + +/* + * A set specialized functions dealing with the SET OF type. + */ +asn_struct_free_f SET_OF_free; +asn_struct_print_f SET_OF_print; +asn_constr_check_f SET_OF_constraint; +ber_type_decoder_f SET_OF_decode_ber; +der_type_encoder_f SET_OF_encode_der; +xer_type_decoder_f SET_OF_decode_xer; +xer_type_encoder_f SET_OF_encode_xer; +per_type_decoder_f SET_OF_decode_uper; +per_type_encoder_f SET_OF_encode_uper; + +#ifdef __cplusplus +} +#endif + +#endif /* _CONSTR_SET_OF_H_ */ diff --git a/src/cryptoconditions/src/asn/constr_TYPE.c b/src/cryptoconditions/src/asn/constr_TYPE.c new file mode 100644 index 000000000..322f68c86 --- /dev/null +++ b/src/cryptoconditions/src/asn/constr_TYPE.c @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +/* + * Version of the ASN.1 infrastructure shipped with compiler. + */ +int get_asn1c_environment_version() { return ASN1C_ENVIRONMENT_VERSION; } + +static asn_app_consume_bytes_f _print2fp; + +/* + * Return the outmost tag of the type. + */ +ber_tlv_tag_t +asn_TYPE_outmost_tag(const asn_TYPE_descriptor_t *type_descriptor, + const void *struct_ptr, int tag_mode, ber_tlv_tag_t tag) { + + if(tag_mode) + return tag; + + if(type_descriptor->tags_count) + return type_descriptor->tags[0]; + + return type_descriptor->outmost_tag(type_descriptor, struct_ptr, 0, 0); +} + +/* + * Print the target language's structure in human readable form. + */ +int +asn_fprint(FILE *stream, asn_TYPE_descriptor_t *td, const void *struct_ptr) { + if(!stream) stream = stdout; + if(!td || !struct_ptr) { + errno = EINVAL; + return -1; + } + + /* Invoke type-specific printer */ + if(td->print_struct(td, struct_ptr, 1, _print2fp, stream)) + return -1; + + /* Terminate the output */ + if(_print2fp("\n", 1, stream)) + return -1; + + return fflush(stream); +} + +/* Dump the data into the specified stdio stream */ +static int +_print2fp(const void *buffer, size_t size, void *app_key) { + FILE *stream = (FILE *)app_key; + + if(fwrite(buffer, 1, size, stream) != size) + return -1; + + return 0; +} + + +/* + * Some compilers do not support variable args macros. + * This function is a replacement of ASN_DEBUG() macro. + */ +void ASN_DEBUG_f(const char *fmt, ...); +void ASN_DEBUG_f(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); +} diff --git a/src/cryptoconditions/src/asn/constr_TYPE.h b/src/cryptoconditions/src/asn/constr_TYPE.h new file mode 100644 index 000000000..a9cd86dc3 --- /dev/null +++ b/src/cryptoconditions/src/asn/constr_TYPE.h @@ -0,0 +1,180 @@ +/*- + * Copyright (c) 2003, 2004, 2005, 2006 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +/* + * This file contains the declaration structure called "ASN.1 Type Definition", + * which holds all information necessary for encoding and decoding routines. + * This structure even contains pointer to these encoding and decoding routines + * for each defined ASN.1 type. + */ +#ifndef _CONSTR_TYPE_H_ +#define _CONSTR_TYPE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct asn_TYPE_descriptor_s; /* Forward declaration */ +struct asn_TYPE_member_s; /* Forward declaration */ + +/* + * This type provides the context information for various ASN.1 routines, + * primarily ones doing decoding. A member _asn_ctx of this type must be + * included into certain target language's structures, such as compound types. + */ +typedef struct asn_struct_ctx_s { + short phase; /* Decoding phase */ + short step; /* Elementary step of a phase */ + int context; /* Other context information */ + void *ptr; /* Decoder-specific stuff (stack elements) */ + ber_tlv_len_t left; /* Number of bytes left, -1 for indefinite */ +} asn_struct_ctx_t; + +#include /* Basic Encoding Rules decoder */ +#include /* Distinguished Encoding Rules encoder */ +#include /* Decoder of XER (XML, text) */ +#include /* Encoder into XER (XML, text) */ +#include /* Packet Encoding Rules decoder */ +#include /* Packet Encoding Rules encoder */ +#include /* Subtype constraints support */ + +/* + * Free the structure according to its specification. + * If (free_contents_only) is set, the wrapper structure itself (struct_ptr) + * will not be freed. (It may be useful in case the structure is allocated + * statically or arranged on the stack, yet its elements are allocated + * dynamically.) + */ +typedef void (asn_struct_free_f)( + struct asn_TYPE_descriptor_s *type_descriptor, + void *struct_ptr, int free_contents_only); +#define ASN_STRUCT_FREE(asn_DEF, ptr) (asn_DEF).free_struct(&(asn_DEF),ptr,0) +#define ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF, ptr) \ + (asn_DEF).free_struct(&(asn_DEF),ptr,1) + +/* + * Print the structure according to its specification. + */ +typedef int (asn_struct_print_f)( + struct asn_TYPE_descriptor_s *type_descriptor, + const void *struct_ptr, + int level, /* Indentation level */ + asn_app_consume_bytes_f *callback, void *app_key); + +/* + * Return the outmost tag of the type. + * If the type is untagged CHOICE, the dynamic operation is performed. + * NOTE: This function pointer type is only useful internally. + * Do not use it in your application. + */ +typedef ber_tlv_tag_t (asn_outmost_tag_f)( + const struct asn_TYPE_descriptor_s *type_descriptor, + const void *struct_ptr, int tag_mode, ber_tlv_tag_t tag); +/* The instance of the above function type; used internally. */ +asn_outmost_tag_f asn_TYPE_outmost_tag; + + +/* + * The definitive description of the destination language's structure. + */ +typedef struct asn_TYPE_descriptor_s { + const char *name; /* A name of the ASN.1 type. "" in some cases. */ + const char *xml_tag; /* Name used in XML tag */ + + /* + * Generalized functions for dealing with the specific type. + * May be directly invoked by applications. + */ + asn_struct_free_f *free_struct; /* Free the structure */ + asn_struct_print_f *print_struct; /* Human readable output */ + asn_constr_check_f *check_constraints; /* Constraints validator */ + ber_type_decoder_f *ber_decoder; /* Generic BER decoder */ + der_type_encoder_f *der_encoder; /* Canonical DER encoder */ + xer_type_decoder_f *xer_decoder; /* Generic XER decoder */ + xer_type_encoder_f *xer_encoder; /* [Canonical] XER encoder */ + per_type_decoder_f *uper_decoder; /* Unaligned PER decoder */ + per_type_encoder_f *uper_encoder; /* Unaligned PER encoder */ + + /*********************************************************************** + * Internally useful members. Not to be used by applications directly. * + **********************************************************************/ + + /* + * Tags that are expected to occur. + */ + asn_outmost_tag_f *outmost_tag; /* */ + const ber_tlv_tag_t *tags; /* Effective tags sequence for this type */ + int tags_count; /* Number of tags which are expected */ + const ber_tlv_tag_t *all_tags; /* Every tag for BER/containment */ + int all_tags_count; /* Number of tags */ + + asn_per_constraints_t *per_constraints; /* PER compiled constraints */ + + /* + * An ASN.1 production type members (members of SEQUENCE, SET, CHOICE). + */ + struct asn_TYPE_member_s *elements; + int elements_count; + + /* + * Additional information describing the type, used by appropriate + * functions above. + */ + const void *specifics; +} asn_TYPE_descriptor_t; + +/* + * This type describes an element of the constructed type, + * i.e. SEQUENCE, SET, CHOICE, etc. + */ + enum asn_TYPE_flags_e { + ATF_NOFLAGS, + ATF_POINTER = 0x01, /* Represented by the pointer */ + ATF_OPEN_TYPE = 0x02 /* ANY type, without meaningful tag */ + }; +typedef struct asn_TYPE_member_s { + enum asn_TYPE_flags_e flags; /* Element's presentation flags */ + int optional; /* Following optional members, including current */ + int memb_offset; /* Offset of the element */ + ber_tlv_tag_t tag; /* Outmost (most immediate) tag */ + int tag_mode; /* IMPLICIT/no/EXPLICIT tag at current level */ + asn_TYPE_descriptor_t *type; /* Member type descriptor */ + asn_constr_check_f *memb_constraints; /* Constraints validator */ + asn_per_constraints_t *per_constraints; /* PER compiled constraints */ + int (*default_value)(int setval, void **sptr); /* DEFAULT */ + const char *name; /* ASN.1 identifier of the element */ +} asn_TYPE_member_t; + +/* + * BER tag to element number mapping. + */ +typedef struct asn_TYPE_tag2member_s { + ber_tlv_tag_t el_tag; /* Outmost tag of the member */ + int el_no; /* Index of the associated member, base 0 */ + int toff_first; /* First occurence of the el_tag, relative */ + int toff_last; /* Last occurence of the el_tag, relatvie */ +} asn_TYPE_tag2member_t; + +/* + * This function is a wrapper around (td)->print_struct, which prints out + * the contents of the target language's structure (struct_ptr) into the + * file pointer (stream) in human readable form. + * RETURN VALUES: + * 0: The structure is printed. + * -1: Problem dumping the structure. + * (See also xer_fprint() in xer_encoder.h) + */ +int asn_fprint(FILE *stream, /* Destination stream descriptor */ + asn_TYPE_descriptor_t *td, /* ASN.1 type descriptor */ + const void *struct_ptr); /* Structure to be printed */ + +#ifdef __cplusplus +} +#endif + +#endif /* _CONSTR_TYPE_H_ */ diff --git a/src/cryptoconditions/src/asn/constraints.c b/src/cryptoconditions/src/asn/constraints.c new file mode 100644 index 000000000..1bdda73e5 --- /dev/null +++ b/src/cryptoconditions/src/asn/constraints.c @@ -0,0 +1,93 @@ +#include "asn_internal.h" +#include "constraints.h" + +int +asn_generic_no_constraint(asn_TYPE_descriptor_t *type_descriptor, + const void *struct_ptr, asn_app_constraint_failed_f *cb, void *key) { + + (void)type_descriptor; /* Unused argument */ + (void)struct_ptr; /* Unused argument */ + (void)cb; /* Unused argument */ + (void)key; /* Unused argument */ + + /* Nothing to check */ + return 0; +} + +int +asn_generic_unknown_constraint(asn_TYPE_descriptor_t *type_descriptor, + const void *struct_ptr, asn_app_constraint_failed_f *cb, void *key) { + + (void)type_descriptor; /* Unused argument */ + (void)struct_ptr; /* Unused argument */ + (void)cb; /* Unused argument */ + (void)key; /* Unused argument */ + + /* Unknown how to check */ + return 0; +} + +struct errbufDesc { + asn_TYPE_descriptor_t *failed_type; + const void *failed_struct_ptr; + char *errbuf; + size_t errlen; +}; + +static void +_asn_i_ctfailcb(void *key, asn_TYPE_descriptor_t *td, const void *sptr, const char *fmt, ...) { + struct errbufDesc *arg = key; + va_list ap; + ssize_t vlen; + ssize_t maxlen; + + arg->failed_type = td; + arg->failed_struct_ptr = sptr; + + maxlen = arg->errlen; + if(maxlen <= 0) + return; + + va_start(ap, fmt); + vlen = vsnprintf(arg->errbuf, maxlen, fmt, ap); + va_end(ap); + if(vlen >= maxlen) { + arg->errbuf[maxlen-1] = '\0'; /* Ensuring libc correctness */ + arg->errlen = maxlen - 1; /* Not counting termination */ + return; + } else if(vlen >= 0) { + arg->errbuf[vlen] = '\0'; /* Ensuring libc correctness */ + arg->errlen = vlen; /* Not counting termination */ + } else { + /* + * The libc on this system is broken. + */ + vlen = sizeof("") - 1; + maxlen--; + arg->errlen = vlen < maxlen ? vlen : maxlen; + memcpy(arg->errbuf, "", arg->errlen); + arg->errbuf[arg->errlen] = 0; + } + + return; +} + +int +asn_check_constraints(asn_TYPE_descriptor_t *type_descriptor, + const void *struct_ptr, char *errbuf, size_t *errlen) { + struct errbufDesc arg; + int ret; + + arg.failed_type = 0; + arg.failed_struct_ptr = 0; + arg.errbuf = errbuf; + arg.errlen = errlen ? *errlen : 0; + + ret = type_descriptor->check_constraints(type_descriptor, + struct_ptr, _asn_i_ctfailcb, &arg); + if(ret == -1 && errlen) + *errlen = arg.errlen; + + return ret; +} + diff --git a/src/cryptoconditions/src/asn/constraints.h b/src/cryptoconditions/src/asn/constraints.h new file mode 100644 index 000000000..48d49e246 --- /dev/null +++ b/src/cryptoconditions/src/asn/constraints.h @@ -0,0 +1,63 @@ +/*- + * Copyright (c) 2004, 2006 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef ASN1_CONSTRAINTS_VALIDATOR_H +#define ASN1_CONSTRAINTS_VALIDATOR_H + +#include /* Platform-dependent types */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct asn_TYPE_descriptor_s; /* Forward declaration */ + +/* + * Validate the structure according to the ASN.1 constraints. + * If errbuf and errlen are given, they shall be pointing to the appropriate + * buffer space and its length before calling this function. Alternatively, + * they could be passed as NULL's. If constraints validation fails, + * errlen will contain the actual number of bytes taken from the errbuf + * to encode an error message (properly 0-terminated). + * + * RETURN VALUES: + * This function returns 0 in case all ASN.1 constraints are met + * and -1 if one or more constraints were failed. + */ +int +asn_check_constraints(struct asn_TYPE_descriptor_s *type_descriptor, + const void *struct_ptr, /* Target language's structure */ + char *errbuf, /* Returned error description */ + size_t *errlen /* Length of the error description */ + ); + + +/* + * Generic type for constraint checking callback, + * associated with every type descriptor. + */ +typedef int (asn_constr_check_f)( + struct asn_TYPE_descriptor_s *type_descriptor, + const void *struct_ptr, + asn_app_constraint_failed_f *optional_callback, /* Log the error */ + void *optional_app_key /* Opaque key passed to a callback */ + ); + +/******************************* + * INTERNALLY USEFUL FUNCTIONS * + *******************************/ + +asn_constr_check_f asn_generic_no_constraint; /* No constraint whatsoever */ +asn_constr_check_f asn_generic_unknown_constraint; /* Not fully supported */ + +/* + * Invoke the callback with a complete error message. + */ +#define ASN__CTFAIL if(ctfailcb) ctfailcb + +#ifdef __cplusplus +} +#endif + +#endif /* ASN1_CONSTRAINTS_VALIDATOR_H */ diff --git a/src/cryptoconditions/src/asn/der_encoder.c b/src/cryptoconditions/src/asn/der_encoder.c new file mode 100644 index 000000000..1c014802a --- /dev/null +++ b/src/cryptoconditions/src/asn/der_encoder.c @@ -0,0 +1,201 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include + +static ssize_t der_write_TL(ber_tlv_tag_t tag, ber_tlv_len_t len, + asn_app_consume_bytes_f *cb, void *app_key, int constructed); + +/* + * The DER encoder of any type. + */ +asn_enc_rval_t +der_encode(asn_TYPE_descriptor_t *type_descriptor, void *struct_ptr, + asn_app_consume_bytes_f *consume_bytes, void *app_key) { + + ASN_DEBUG("DER encoder invoked for %s", + type_descriptor->name); + + /* + * Invoke type-specific encoder. + */ + return type_descriptor->der_encoder(type_descriptor, + struct_ptr, /* Pointer to the destination structure */ + 0, 0, + consume_bytes, app_key); +} + +/* + * Argument type and callback necessary for der_encode_to_buffer(). + */ +typedef struct enc_to_buf_arg { + void *buffer; + size_t left; +} enc_to_buf_arg; +static int encode_to_buffer_cb(const void *buffer, size_t size, void *key) { + enc_to_buf_arg *arg = (enc_to_buf_arg *)key; + + if(arg->left < size) + return -1; /* Data exceeds the available buffer size */ + + memcpy(arg->buffer, buffer, size); + arg->buffer = ((char *)arg->buffer) + size; + arg->left -= size; + + return 0; +} + +/* + * A variant of the der_encode() which encodes the data into the provided buffer + */ +asn_enc_rval_t +der_encode_to_buffer(asn_TYPE_descriptor_t *type_descriptor, void *struct_ptr, + void *buffer, size_t buffer_size) { + enc_to_buf_arg arg; + asn_enc_rval_t ec; + + arg.buffer = buffer; + arg.left = buffer_size; + + ec = type_descriptor->der_encoder(type_descriptor, + struct_ptr, /* Pointer to the destination structure */ + 0, 0, encode_to_buffer_cb, &arg); + if(ec.encoded != -1) { + assert(ec.encoded == (ssize_t)(buffer_size - arg.left)); + /* Return the encoded contents size */ + } + return ec; +} + + +/* + * Write out leading TL[v] sequence according to the type definition. + */ +ssize_t +der_write_tags(asn_TYPE_descriptor_t *sd, + size_t struct_length, + int tag_mode, int last_tag_form, + ber_tlv_tag_t tag, /* EXPLICIT or IMPLICIT tag */ + asn_app_consume_bytes_f *cb, + void *app_key) { + const ber_tlv_tag_t *tags; /* Copy of tags stream */ + int tags_count; /* Number of tags */ + size_t overall_length; + ssize_t *lens; + int i; + + ASN_DEBUG("Writing tags (%s, tm=%d, tc=%d, tag=%s, mtc=%d)", + sd->name, tag_mode, sd->tags_count, + ber_tlv_tag_string(tag), + tag_mode + ?(sd->tags_count+1 + -((tag_mode == -1) && sd->tags_count)) + :sd->tags_count + ); + + if(tag_mode) { + /* + * Instead of doing shaman dance like we do in ber_check_tags(), + * allocate a small array on the stack + * and initialize it appropriately. + */ + int stag_offset; + ber_tlv_tag_t *tags_buf; + tags_buf = (ber_tlv_tag_t *)alloca((sd->tags_count + 1) * sizeof(ber_tlv_tag_t)); + if(!tags_buf) { /* Can fail on !x86 */ + errno = ENOMEM; + return -1; + } + tags_count = sd->tags_count + + 1 /* EXPLICIT or IMPLICIT tag is given */ + - ((tag_mode == -1) && sd->tags_count); + /* Copy tags over */ + tags_buf[0] = tag; + stag_offset = -1 + ((tag_mode == -1) && sd->tags_count); + for(i = 1; i < tags_count; i++) + tags_buf[i] = sd->tags[i + stag_offset]; + tags = tags_buf; + } else { + tags = sd->tags; + tags_count = sd->tags_count; + } + + /* No tags to write */ + if(tags_count == 0) + return 0; + + lens = (ssize_t *)alloca(tags_count * sizeof(lens[0])); + if(!lens) { + errno = ENOMEM; + return -1; + } + + /* + * Array of tags is initialized. + * Now, compute the size of the TLV pairs, from right to left. + */ + overall_length = struct_length; + for(i = tags_count - 1; i >= 0; --i) { + lens[i] = der_write_TL(tags[i], overall_length, 0, 0, 0); + if(lens[i] == -1) return -1; + overall_length += lens[i]; + lens[i] = overall_length - lens[i]; + } + + if(!cb) return overall_length - struct_length; + + ASN_DEBUG("%s %s TL sequence (%d elements)", + cb?"Encoding":"Estimating", sd->name, tags_count); + + /* + * Encode the TL sequence for real. + */ + for(i = 0; i < tags_count; i++) { + ssize_t len; + int _constr; + + /* Check if this tag happens to be constructed */ + _constr = (last_tag_form || i < (tags_count - 1)); + + len = der_write_TL(tags[i], lens[i], cb, app_key, _constr); + if(len == -1) return -1; + } + + return overall_length - struct_length; +} + +static ssize_t +der_write_TL(ber_tlv_tag_t tag, ber_tlv_len_t len, + asn_app_consume_bytes_f *cb, void *app_key, + int constructed) { + uint8_t buf[32]; + size_t size = 0; + int buf_size = cb?sizeof(buf):0; + ssize_t tmp; + + /* Serialize tag (T from TLV) into possibly zero-length buffer */ + tmp = ber_tlv_tag_serialize(tag, buf, buf_size); + if(tmp == -1 || tmp > (ssize_t)sizeof(buf)) return -1; + size += tmp; + + /* Serialize length (L from TLV) into possibly zero-length buffer */ + tmp = der_tlv_length_serialize(len, buf+size, buf_size?buf_size-size:0); + if(tmp == -1) return -1; + size += tmp; + + if(size > sizeof(buf)) + return -1; + + /* + * If callback is specified, invoke it, and check its return value. + */ + if(cb) { + if(constructed) *buf |= 0x20; + if(cb(buf, size, app_key) < 0) + return -1; + } + + return size; +} diff --git a/src/cryptoconditions/src/asn/der_encoder.h b/src/cryptoconditions/src/asn/der_encoder.h new file mode 100644 index 000000000..61431c6db --- /dev/null +++ b/src/cryptoconditions/src/asn/der_encoder.h @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _DER_ENCODER_H_ +#define _DER_ENCODER_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct asn_TYPE_descriptor_s; /* Forward declaration */ + +/* + * The DER encoder of any type. May be invoked by the application. + * The ber_decode() function (ber_decoder.h) is an opposite of der_encode(). + */ +asn_enc_rval_t der_encode(struct asn_TYPE_descriptor_s *type_descriptor, + void *struct_ptr, /* Structure to be encoded */ + asn_app_consume_bytes_f *consume_bytes_cb, + void *app_key /* Arbitrary callback argument */ + ); + +/* A variant of der_encode() which encodes data into the pre-allocated buffer */ +asn_enc_rval_t der_encode_to_buffer( + struct asn_TYPE_descriptor_s *type_descriptor, + void *struct_ptr, /* Structure to be encoded */ + void *buffer, /* Pre-allocated buffer */ + size_t buffer_size /* Initial buffer size (maximum) */ + ); + +/* + * Type of the generic DER encoder. + */ +typedef asn_enc_rval_t (der_type_encoder_f)( + struct asn_TYPE_descriptor_s *type_descriptor, + void *struct_ptr, /* Structure to be encoded */ + int tag_mode, /* {-1,0,1}: IMPLICIT, no, EXPLICIT */ + ber_tlv_tag_t tag, + asn_app_consume_bytes_f *consume_bytes_cb, /* Callback */ + void *app_key /* Arbitrary callback argument */ + ); + + +/******************************* + * INTERNALLY USEFUL FUNCTIONS * + *******************************/ + +/* + * Write out leading TL[v] sequence according to the type definition. + */ +ssize_t der_write_tags( + struct asn_TYPE_descriptor_s *type_descriptor, + size_t struct_length, + int tag_mode, /* {-1,0,1}: IMPLICIT, no, EXPLICIT */ + int last_tag_form, /* {0,!0}: prim, constructed */ + ber_tlv_tag_t tag, + asn_app_consume_bytes_f *consume_bytes_cb, + void *app_key + ); + +#ifdef __cplusplus +} +#endif + +#endif /* _DER_ENCODER_H_ */ diff --git a/src/cryptoconditions/src/asn/per_decoder.c b/src/cryptoconditions/src/asn/per_decoder.c new file mode 100644 index 000000000..461b7262f --- /dev/null +++ b/src/cryptoconditions/src/asn/per_decoder.c @@ -0,0 +1,93 @@ +#include +#include +#include + +/* + * Decode a "Production of a complete encoding", X.691#10.1. + * The complete encoding contains at least one byte, and is an integral + * multiple of 8 bytes. + */ +asn_dec_rval_t +uper_decode_complete(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, void **sptr, const void *buffer, size_t size) { + asn_dec_rval_t rval; + + rval = uper_decode(opt_codec_ctx, td, sptr, buffer, size, 0, 0); + if(rval.consumed) { + /* + * We've always given 8-aligned data, + * so convert bits to integral bytes. + */ + rval.consumed += 7; + rval.consumed >>= 3; + } else if(rval.code == RC_OK) { + if(size) { + if(((const uint8_t *)buffer)[0] == 0) { + rval.consumed = 1; /* 1 byte */ + } else { + ASN_DEBUG("Expecting single zeroed byte"); + rval.code = RC_FAIL; + } + } else { + /* Must contain at least 8 bits. */ + rval.code = RC_WMORE; + } + } + + return rval; +} + +asn_dec_rval_t +uper_decode(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, void **sptr, const void *buffer, size_t size, int skip_bits, int unused_bits) { + asn_codec_ctx_t s_codec_ctx; + asn_dec_rval_t rval; + asn_per_data_t pd; + + if(skip_bits < 0 || skip_bits > 7 + || unused_bits < 0 || unused_bits > 7 + || (unused_bits > 0 && !size)) + ASN__DECODE_FAILED; + + /* + * Stack checker requires that the codec context + * must be allocated on the stack. + */ + if(opt_codec_ctx) { + if(opt_codec_ctx->max_stack_size) { + s_codec_ctx = *opt_codec_ctx; + opt_codec_ctx = &s_codec_ctx; + } + } else { + /* If context is not given, be security-conscious anyway */ + memset(&s_codec_ctx, 0, sizeof(s_codec_ctx)); + s_codec_ctx.max_stack_size = ASN__DEFAULT_STACK_MAX; + opt_codec_ctx = &s_codec_ctx; + } + + /* Fill in the position indicator */ + memset(&pd, 0, sizeof(pd)); + pd.buffer = (const uint8_t *)buffer; + pd.nboff = skip_bits; + pd.nbits = 8 * size - unused_bits; /* 8 is CHAR_BIT from */ + if(pd.nboff > pd.nbits) + ASN__DECODE_FAILED; + + /* + * Invoke type-specific decoder. + */ + if(!td->uper_decoder) + ASN__DECODE_FAILED; /* PER is not compiled in */ + rval = td->uper_decoder(opt_codec_ctx, td, 0, sptr, &pd); + if(rval.code == RC_OK) { + /* Return the number of consumed bits */ + rval.consumed = ((pd.buffer - (const uint8_t *)buffer) << 3) + + pd.nboff - skip_bits; + ASN_DEBUG("PER decoding consumed %ld, counted %ld", + (long)rval.consumed, (long)pd.moved); + assert(rval.consumed == pd.moved); + } else { + /* PER codec is not a restartable */ + rval.consumed = 0; + } + return rval; +} + diff --git a/src/cryptoconditions/src/asn/per_decoder.h b/src/cryptoconditions/src/asn/per_decoder.h new file mode 100644 index 000000000..8397a545f --- /dev/null +++ b/src/cryptoconditions/src/asn/per_decoder.h @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 2005, 2007 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _PER_DECODER_H_ +#define _PER_DECODER_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct asn_TYPE_descriptor_s; /* Forward declaration */ + +/* + * Unaligned PER decoder of a "complete encoding" as per X.691#10.1. + * On success, this call always returns (.consumed >= 1), as per X.691#10.1.3. + */ +asn_dec_rval_t uper_decode_complete(struct asn_codec_ctx_s *opt_codec_ctx, + struct asn_TYPE_descriptor_s *type_descriptor, /* Type to decode */ + void **struct_ptr, /* Pointer to a target structure's pointer */ + const void *buffer, /* Data to be decoded */ + size_t size /* Size of data buffer */ + ); + +/* + * Unaligned PER decoder of any ASN.1 type. May be invoked by the application. + * WARNING: This call returns the number of BITS read from the stream. Beware. + */ +asn_dec_rval_t uper_decode(struct asn_codec_ctx_s *opt_codec_ctx, + struct asn_TYPE_descriptor_s *type_descriptor, /* Type to decode */ + void **struct_ptr, /* Pointer to a target structure's pointer */ + const void *buffer, /* Data to be decoded */ + size_t size, /* Size of data buffer */ + int skip_bits, /* Number of unused leading bits, 0..7 */ + int unused_bits /* Number of unused tailing bits, 0..7 */ + ); + + +/* + * Type of the type-specific PER decoder function. + */ +typedef asn_dec_rval_t (per_type_decoder_f)(asn_codec_ctx_t *opt_codec_ctx, + struct asn_TYPE_descriptor_s *type_descriptor, + asn_per_constraints_t *constraints, + void **struct_ptr, + asn_per_data_t *per_data + ); + +#ifdef __cplusplus +} +#endif + +#endif /* _PER_DECODER_H_ */ diff --git a/src/cryptoconditions/src/asn/per_encoder.c b/src/cryptoconditions/src/asn/per_encoder.c new file mode 100644 index 000000000..47f3c916d --- /dev/null +++ b/src/cryptoconditions/src/asn/per_encoder.c @@ -0,0 +1,151 @@ +#include +#include +#include + +static asn_enc_rval_t uper_encode_internal(asn_TYPE_descriptor_t *td, asn_per_constraints_t *, void *sptr, asn_app_consume_bytes_f *cb, void *app_key); + +asn_enc_rval_t +uper_encode(asn_TYPE_descriptor_t *td, void *sptr, asn_app_consume_bytes_f *cb, void *app_key) { + return uper_encode_internal(td, 0, sptr, cb, app_key); +} + +/* + * Argument type and callback necessary for uper_encode_to_buffer(). + */ +typedef struct enc_to_buf_arg { + void *buffer; + size_t left; +} enc_to_buf_arg; +static int encode_to_buffer_cb(const void *buffer, size_t size, void *key) { + enc_to_buf_arg *arg = (enc_to_buf_arg *)key; + + if(arg->left < size) + return -1; /* Data exceeds the available buffer size */ + + memcpy(arg->buffer, buffer, size); + arg->buffer = ((char *)arg->buffer) + size; + arg->left -= size; + + return 0; +} + +asn_enc_rval_t +uper_encode_to_buffer(asn_TYPE_descriptor_t *td, void *sptr, void *buffer, size_t buffer_size) { + enc_to_buf_arg key; + + key.buffer = buffer; + key.left = buffer_size; + + if(td) ASN_DEBUG("Encoding \"%s\" using UNALIGNED PER", td->name); + + return uper_encode_internal(td, 0, sptr, encode_to_buffer_cb, &key); +} + +typedef struct enc_dyn_arg { + void *buffer; + size_t length; + size_t allocated; +} enc_dyn_arg; +static int +encode_dyn_cb(const void *buffer, size_t size, void *key) { + enc_dyn_arg *arg = key; + if(arg->length + size >= arg->allocated) { + void *p; + arg->allocated = arg->allocated ? (arg->allocated << 2) : size; + p = REALLOC(arg->buffer, arg->allocated); + if(!p) { + FREEMEM(arg->buffer); + memset(arg, 0, sizeof(*arg)); + return -1; + } + arg->buffer = p; + } + memcpy(((char *)arg->buffer) + arg->length, buffer, size); + arg->length += size; + return 0; +} +ssize_t +uper_encode_to_new_buffer(asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void *sptr, void **buffer_r) { + asn_enc_rval_t er; + enc_dyn_arg key; + + memset(&key, 0, sizeof(key)); + + er = uper_encode_internal(td, constraints, sptr, encode_dyn_cb, &key); + switch(er.encoded) { + case -1: + FREEMEM(key.buffer); + return -1; + case 0: + FREEMEM(key.buffer); + key.buffer = MALLOC(1); + if(key.buffer) { + *(char *)key.buffer = '\0'; + *buffer_r = key.buffer; + return 1; + } else { + return -1; + } + default: + *buffer_r = key.buffer; + ASN_DEBUG("Complete encoded in %ld bits", (long)er.encoded); + return ((er.encoded + 7) >> 3); + } +} + +/* + * Internally useful functions. + */ + +/* Flush partially filled buffer */ +static int +_uper_encode_flush_outp(asn_per_outp_t *po) { + uint8_t *buf; + + if(po->nboff == 0 && po->buffer == po->tmpspace) + return 0; + + buf = po->buffer + (po->nboff >> 3); + /* Make sure we account for the last, partially filled */ + if(po->nboff & 0x07) { + buf[0] &= 0xff << (8 - (po->nboff & 0x07)); + buf++; + } + + return po->outper(po->tmpspace, buf - po->tmpspace, po->op_key); +} + +static asn_enc_rval_t +uper_encode_internal(asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void *sptr, asn_app_consume_bytes_f *cb, void *app_key) { + asn_per_outp_t po; + asn_enc_rval_t er; + + /* + * Invoke type-specific encoder. + */ + if(!td || !td->uper_encoder) + ASN__ENCODE_FAILED; /* PER is not compiled in */ + + po.buffer = po.tmpspace; + po.nboff = 0; + po.nbits = 8 * sizeof(po.tmpspace); + po.outper = cb; + po.op_key = app_key; + po.flushed_bytes = 0; + + er = td->uper_encoder(td, constraints, sptr, &po); + if(er.encoded != -1) { + size_t bits_to_flush; + + bits_to_flush = ((po.buffer - po.tmpspace) << 3) + po.nboff; + + /* Set number of bits encoded to a firm value */ + er.encoded = (po.flushed_bytes << 3) + bits_to_flush; + + if(_uper_encode_flush_outp(&po)) + ASN__ENCODE_FAILED; + } + + return er; +} + diff --git a/src/cryptoconditions/src/asn/per_encoder.h b/src/cryptoconditions/src/asn/per_encoder.h new file mode 100644 index 000000000..95a6506e4 --- /dev/null +++ b/src/cryptoconditions/src/asn/per_encoder.h @@ -0,0 +1,69 @@ +/*- + * Copyright (c) 2006, 2007 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _PER_ENCODER_H_ +#define _PER_ENCODER_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct asn_TYPE_descriptor_s; /* Forward declaration */ + +/* + * Unaligned PER encoder of any ASN.1 type. May be invoked by the application. + * WARNING: This function returns the number of encoded bits in the .encoded + * field of the return value. Use the following formula to convert to bytes: + * bytes = ((.encoded + 7) / 8) + */ +asn_enc_rval_t uper_encode(struct asn_TYPE_descriptor_s *type_descriptor, + void *struct_ptr, /* Structure to be encoded */ + asn_app_consume_bytes_f *consume_bytes_cb, /* Data collector */ + void *app_key /* Arbitrary callback argument */ +); + +/* + * A variant of uper_encode() which encodes data into the existing buffer + * WARNING: This function returns the number of encoded bits in the .encoded + * field of the return value. + */ +asn_enc_rval_t uper_encode_to_buffer( + struct asn_TYPE_descriptor_s *type_descriptor, + void *struct_ptr, /* Structure to be encoded */ + void *buffer, /* Pre-allocated buffer */ + size_t buffer_size /* Initial buffer size (max) */ +); + +/* + * A variant of uper_encode_to_buffer() which allocates buffer itself. + * Returns the number of bytes in the buffer or -1 in case of failure. + * WARNING: This function produces a "Production of the complete encoding", + * with length of at least one octet. Contrast this to precise bit-packing + * encoding of uper_encode() and uper_encode_to_buffer(). + */ +ssize_t uper_encode_to_new_buffer( + struct asn_TYPE_descriptor_s *type_descriptor, + asn_per_constraints_t *constraints, + void *struct_ptr, /* Structure to be encoded */ + void **buffer_r /* Buffer allocated and returned */ +); + +/* + * Type of the generic PER encoder function. + */ +typedef asn_enc_rval_t (per_type_encoder_f)( + struct asn_TYPE_descriptor_s *type_descriptor, + asn_per_constraints_t *constraints, + void *struct_ptr, + asn_per_outp_t *per_output +); + +#ifdef __cplusplus +} +#endif + +#endif /* _PER_ENCODER_H_ */ diff --git a/src/cryptoconditions/src/asn/per_opentype.c b/src/cryptoconditions/src/asn/per_opentype.c new file mode 100644 index 000000000..404aa7264 --- /dev/null +++ b/src/cryptoconditions/src/asn/per_opentype.c @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2007 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include +#include + +typedef struct uper_ugot_key { + asn_per_data_t oldpd; /* Old per data source */ + size_t unclaimed; + size_t ot_moved; /* Number of bits moved by OT processing */ + int repeat; +} uper_ugot_key; + +static int uper_ugot_refill(asn_per_data_t *pd); +static int per_skip_bits(asn_per_data_t *pd, int skip_nbits); +static asn_dec_rval_t uper_sot_suck(asn_codec_ctx_t *, asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd); + +/* + * Encode an "open type field". + * #10.1, #10.2 + */ +int +uper_open_type_put(asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) { + void *buf; + void *bptr; + ssize_t size; + size_t toGo; + + ASN_DEBUG("Open type put %s ...", td->name); + + size = uper_encode_to_new_buffer(td, constraints, sptr, &buf); + if(size <= 0) return -1; + + for(bptr = buf, toGo = size; toGo;) { + ssize_t maySave = uper_put_length(po, toGo); + ASN_DEBUG("Prepending length %d to %s and allowing to save %d", + (int)size, td->name, (int)maySave); + if(maySave < 0) break; + if(per_put_many_bits(po, bptr, maySave * 8)) break; + bptr = (char *)bptr + maySave; + toGo -= maySave; + } + + FREEMEM(buf); + if(toGo) return -1; + + ASN_DEBUG("Open type put %s of length %ld + overhead (1byte?)", + td->name, (long)size); + + return 0; +} + +static asn_dec_rval_t +uper_open_type_get_simple(asn_codec_ctx_t *ctx, asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) { + asn_dec_rval_t rv; + ssize_t chunk_bytes; + int repeat; + uint8_t *buf = 0; + size_t bufLen = 0; + size_t bufSize = 0; + asn_per_data_t spd; + size_t padding; + + ASN__STACK_OVERFLOW_CHECK(ctx); + + ASN_DEBUG("Getting open type %s...", td->name); + + do { + chunk_bytes = uper_get_length(pd, -1, &repeat); + if(chunk_bytes < 0) { + FREEMEM(buf); + ASN__DECODE_STARVED; + } + if(bufLen + chunk_bytes > bufSize) { + void *ptr; + bufSize = chunk_bytes + (bufSize << 2); + ptr = REALLOC(buf, bufSize); + if(!ptr) { + FREEMEM(buf); + ASN__DECODE_FAILED; + } + buf = ptr; + } + if(per_get_many_bits(pd, buf + bufLen, 0, chunk_bytes << 3)) { + FREEMEM(buf); + ASN__DECODE_STARVED; + } + bufLen += chunk_bytes; + } while(repeat); + + ASN_DEBUG("Getting open type %s encoded in %ld bytes", td->name, + (long)bufLen); + + memset(&spd, 0, sizeof(spd)); + spd.buffer = buf; + spd.nbits = bufLen << 3; + + ASN_DEBUG_INDENT_ADD(+4); + rv = td->uper_decoder(ctx, td, constraints, sptr, &spd); + ASN_DEBUG_INDENT_ADD(-4); + + if(rv.code == RC_OK) { + /* Check padding validity */ + padding = spd.nbits - spd.nboff; + if ((padding < 8 || + /* X.691#10.1.3 */ + (spd.nboff == 0 && spd.nbits == 8 && spd.buffer == buf)) && + per_get_few_bits(&spd, padding) == 0) { + /* Everything is cool */ + FREEMEM(buf); + return rv; + } + FREEMEM(buf); + if(padding >= 8) { + ASN_DEBUG("Too large padding %d in open type", (int)padding); + ASN__DECODE_FAILED; + } else { + ASN_DEBUG("Non-zero padding"); + ASN__DECODE_FAILED; + } + } else { + FREEMEM(buf); + /* rv.code could be RC_WMORE, nonsense in this context */ + rv.code = RC_FAIL; /* Noone would give us more */ + } + + return rv; +} + +static asn_dec_rval_t GCC_NOTUSED +uper_open_type_get_complex(asn_codec_ctx_t *ctx, asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) { + uper_ugot_key arg; + asn_dec_rval_t rv; + ssize_t padding; + + ASN__STACK_OVERFLOW_CHECK(ctx); + + ASN_DEBUG("Getting open type %s from %s", td->name, + per_data_string(pd)); + arg.oldpd = *pd; + arg.unclaimed = 0; + arg.ot_moved = 0; + arg.repeat = 1; + pd->refill = uper_ugot_refill; + pd->refill_key = &arg; + pd->nbits = pd->nboff; /* 0 good bits at this point, will refill */ + pd->moved = 0; /* This now counts the open type size in bits */ + + ASN_DEBUG_INDENT_ADD(+4); + rv = td->uper_decoder(ctx, td, constraints, sptr, pd); + ASN_DEBUG_INDENT_ADD(-4); + +#define UPDRESTOREPD do { \ + /* buffer and nboff are valid, preserve them. */ \ + pd->nbits = arg.oldpd.nbits - (pd->moved - arg.ot_moved); \ + pd->moved = arg.oldpd.moved + (pd->moved - arg.ot_moved); \ + pd->refill = arg.oldpd.refill; \ + pd->refill_key = arg.oldpd.refill_key; \ + } while(0) + + if(rv.code != RC_OK) { + UPDRESTOREPD; + return rv; + } + + ASN_DEBUG("OpenType %s pd%s old%s unclaimed=%d, repeat=%d", td->name, + per_data_string(pd), + per_data_string(&arg.oldpd), + (int)arg.unclaimed, (int)arg.repeat); + + padding = pd->moved % 8; + if(padding) { + int32_t pvalue; + if(padding > 7) { + ASN_DEBUG("Too large padding %d in open type", + (int)padding); + rv.code = RC_FAIL; + UPDRESTOREPD; + return rv; + } + padding = 8 - padding; + ASN_DEBUG("Getting padding of %d bits", (int)padding); + pvalue = per_get_few_bits(pd, padding); + switch(pvalue) { + case -1: + ASN_DEBUG("Padding skip failed"); + UPDRESTOREPD; + ASN__DECODE_STARVED; + case 0: break; + default: + ASN_DEBUG("Non-blank padding (%d bits 0x%02x)", + (int)padding, (int)pvalue); + UPDRESTOREPD; + ASN__DECODE_FAILED; + } + } + if(pd->nboff != pd->nbits) { + ASN_DEBUG("Open type %s overhead pd%s old%s", td->name, + per_data_string(pd), per_data_string(&arg.oldpd)); + if(1) { + UPDRESTOREPD; + ASN__DECODE_FAILED; + } else { + arg.unclaimed += pd->nbits - pd->nboff; + } + } + + /* Adjust pd back so it points to original data */ + UPDRESTOREPD; + + /* Skip data not consumed by the decoder */ + if(arg.unclaimed) { + ASN_DEBUG("Getting unclaimed %d", (int)arg.unclaimed); + switch(per_skip_bits(pd, arg.unclaimed)) { + case -1: + ASN_DEBUG("Claim of %d failed", (int)arg.unclaimed); + ASN__DECODE_STARVED; + case 0: + ASN_DEBUG("Got claim of %d", (int)arg.unclaimed); + break; + default: + /* Padding must be blank */ + ASN_DEBUG("Non-blank unconsumed padding"); + ASN__DECODE_FAILED; + } + arg.unclaimed = 0; + } + + if(arg.repeat) { + ASN_DEBUG("Not consumed the whole thing"); + rv.code = RC_FAIL; + return rv; + } + + return rv; +} + + +asn_dec_rval_t +uper_open_type_get(asn_codec_ctx_t *ctx, asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) { + + return uper_open_type_get_simple(ctx, td, constraints, sptr, pd); +} + +int +uper_open_type_skip(asn_codec_ctx_t *ctx, asn_per_data_t *pd) { + asn_TYPE_descriptor_t s_td; + asn_dec_rval_t rv; + + s_td.name = ""; + s_td.uper_decoder = uper_sot_suck; + + rv = uper_open_type_get(ctx, &s_td, 0, 0, pd); + if(rv.code != RC_OK) + return -1; + else + return 0; +} + +/* + * Internal functions. + */ + +static asn_dec_rval_t +uper_sot_suck(asn_codec_ctx_t *ctx, asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) { + asn_dec_rval_t rv; + + (void)ctx; + (void)td; + (void)constraints; + (void)sptr; + + while(per_get_few_bits(pd, 24) >= 0); + + rv.code = RC_OK; + rv.consumed = pd->moved; + + return rv; +} + +static int +uper_ugot_refill(asn_per_data_t *pd) { + uper_ugot_key *arg = pd->refill_key; + ssize_t next_chunk_bytes, next_chunk_bits; + ssize_t avail; + + asn_per_data_t *oldpd = &arg->oldpd; + + ASN_DEBUG("REFILLING pd->moved=%ld, oldpd->moved=%ld", + (long)pd->moved, (long)oldpd->moved); + + /* Advance our position to where pd is */ + oldpd->buffer = pd->buffer; + oldpd->nboff = pd->nboff; + oldpd->nbits -= pd->moved - arg->ot_moved; + oldpd->moved += pd->moved - arg->ot_moved; + arg->ot_moved = pd->moved; + + if(arg->unclaimed) { + /* Refill the container */ + if(per_get_few_bits(oldpd, 1)) + return -1; + if(oldpd->nboff == 0) { + assert(0); + return -1; + } + pd->buffer = oldpd->buffer; + pd->nboff = oldpd->nboff - 1; + pd->nbits = oldpd->nbits; + ASN_DEBUG("UNCLAIMED <- return from (pd->moved=%ld)", + (long)pd->moved); + return 0; + } + + if(!arg->repeat) { + ASN_DEBUG("Want more but refill doesn't have it"); + return -1; + } + + next_chunk_bytes = uper_get_length(oldpd, -1, &arg->repeat); + ASN_DEBUG("Open type LENGTH %ld bytes at off %ld, repeat %ld", + (long)next_chunk_bytes, (long)oldpd->moved, (long)arg->repeat); + if(next_chunk_bytes < 0) return -1; + if(next_chunk_bytes == 0) { + pd->refill = 0; /* No more refills, naturally */ + assert(!arg->repeat); /* Implementation guarantee */ + } + next_chunk_bits = next_chunk_bytes << 3; + avail = oldpd->nbits - oldpd->nboff; + if(avail >= next_chunk_bits) { + pd->nbits = oldpd->nboff + next_chunk_bits; + arg->unclaimed = 0; + ASN_DEBUG("!+Parent frame %ld bits, alloting %ld [%ld..%ld] (%ld)", + (long)next_chunk_bits, (long)oldpd->moved, + (long)oldpd->nboff, (long)oldpd->nbits, + (long)(oldpd->nbits - oldpd->nboff)); + } else { + pd->nbits = oldpd->nbits; + arg->unclaimed = next_chunk_bits - avail; + ASN_DEBUG("!-Parent frame %ld, require %ld, will claim %ld", + (long)avail, (long)next_chunk_bits, + (long)arg->unclaimed); + } + pd->buffer = oldpd->buffer; + pd->nboff = oldpd->nboff; + ASN_DEBUG("Refilled pd%s old%s", + per_data_string(pd), per_data_string(oldpd)); + return 0; +} + +static int +per_skip_bits(asn_per_data_t *pd, int skip_nbits) { + int hasNonZeroBits = 0; + while(skip_nbits > 0) { + int skip; + + /* per_get_few_bits() is more efficient when nbits <= 24 */ + if(skip_nbits < 24) + skip = skip_nbits; + else + skip = 24; + skip_nbits -= skip; + + switch(per_get_few_bits(pd, skip)) { + case -1: return -1; /* Starving */ + case 0: continue; /* Skipped empty space */ + default: hasNonZeroBits = 1; continue; + } + } + return hasNonZeroBits; +} diff --git a/src/cryptoconditions/src/asn/per_opentype.h b/src/cryptoconditions/src/asn/per_opentype.h new file mode 100644 index 000000000..facfaa637 --- /dev/null +++ b/src/cryptoconditions/src/asn/per_opentype.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2007 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _PER_OPENTYPE_H_ +#define _PER_OPENTYPE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +asn_dec_rval_t uper_open_type_get(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd); + +int uper_open_type_skip(asn_codec_ctx_t *opt_codec_ctx, asn_per_data_t *pd); + +int uper_open_type_put(asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po); + +#ifdef __cplusplus +} +#endif + +#endif /* _PER_OPENTYPE_H_ */ diff --git a/src/cryptoconditions/src/asn/per_support.c b/src/cryptoconditions/src/asn/per_support.c new file mode 100644 index 000000000..14b4c4c76 --- /dev/null +++ b/src/cryptoconditions/src/asn/per_support.c @@ -0,0 +1,483 @@ +/* + * Copyright (c) 2005-2014 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +char * +per_data_string(asn_per_data_t *pd) { + static char buf[2][32]; + static int n; + n = (n+1) % 2; + snprintf(buf[n], sizeof(buf[n]), + "{m=%ld span %+ld[%d..%d] (%d)}", + (long)pd->moved, + (((long)pd->buffer) & 0xf), + (int)pd->nboff, (int)pd->nbits, + (int)(pd->nbits - pd->nboff)); + return buf[n]; +} + +void +per_get_undo(asn_per_data_t *pd, int nbits) { + if((ssize_t)pd->nboff < nbits) { + assert((ssize_t)pd->nboff < nbits); + } else { + pd->nboff -= nbits; + pd->moved -= nbits; + } +} + +/* + * Extract a small number of bits (<= 31) from the specified PER data pointer. + */ +int32_t +per_get_few_bits(asn_per_data_t *pd, int nbits) { + size_t off; /* Next after last bit offset */ + ssize_t nleft; /* Number of bits left in this stream */ + uint32_t accum; + const uint8_t *buf; + + if(nbits < 0) + return -1; + + nleft = pd->nbits - pd->nboff; + if(nbits > nleft) { + int32_t tailv, vhead; + if(!pd->refill || nbits > 31) return -1; + /* Accumulate unused bytes before refill */ + ASN_DEBUG("Obtain the rest %d bits (want %d)", + (int)nleft, (int)nbits); + tailv = per_get_few_bits(pd, nleft); + if(tailv < 0) return -1; + /* Refill (replace pd contents with new data) */ + if(pd->refill(pd)) + return -1; + nbits -= nleft; + vhead = per_get_few_bits(pd, nbits); + /* Combine the rest of previous pd with the head of new one */ + tailv = (tailv << nbits) | vhead; /* Could == -1 */ + return tailv; + } + + /* + * Normalize position indicator. + */ + if(pd->nboff >= 8) { + pd->buffer += (pd->nboff >> 3); + pd->nbits -= (pd->nboff & ~0x07); + pd->nboff &= 0x07; + } + pd->moved += nbits; + pd->nboff += nbits; + off = pd->nboff; + buf = pd->buffer; + + /* + * Extract specified number of bits. + */ + if(off <= 8) + accum = nbits ? (buf[0]) >> (8 - off) : 0; + else if(off <= 16) + accum = ((buf[0] << 8) + buf[1]) >> (16 - off); + else if(off <= 24) + accum = ((buf[0] << 16) + (buf[1] << 8) + buf[2]) >> (24 - off); + else if(off <= 31) + accum = ((buf[0] << 24) + (buf[1] << 16) + + (buf[2] << 8) + (buf[3])) >> (32 - off); + else if(nbits <= 31) { + asn_per_data_t tpd = *pd; + /* Here are we with our 31-bits limit plus 1..7 bits offset. */ + per_get_undo(&tpd, nbits); + /* The number of available bits in the stream allow + * for the following operations to take place without + * invoking the ->refill() function */ + accum = per_get_few_bits(&tpd, nbits - 24) << 24; + accum |= per_get_few_bits(&tpd, 24); + } else { + per_get_undo(pd, nbits); + return -1; + } + + accum &= (((uint32_t)1 << nbits) - 1); + + ASN_DEBUG(" [PER got %2d<=%2d bits => span %d %+ld[%d..%d]:%02x (%d) => 0x%x]", + (int)nbits, (int)nleft, + (int)pd->moved, + (((long)pd->buffer) & 0xf), + (int)pd->nboff, (int)pd->nbits, + ((pd->buffer != NULL)?pd->buffer[0]:0), + (int)(pd->nbits - pd->nboff), + (int)accum); + + return accum; +} + +/* + * Extract a large number of bits from the specified PER data pointer. + */ +int +per_get_many_bits(asn_per_data_t *pd, uint8_t *dst, int alright, int nbits) { + int32_t value; + + if(alright && (nbits & 7)) { + /* Perform right alignment of a first few bits */ + value = per_get_few_bits(pd, nbits & 0x07); + if(value < 0) return -1; + *dst++ = value; /* value is already right-aligned */ + nbits &= ~7; + } + + while(nbits) { + if(nbits >= 24) { + value = per_get_few_bits(pd, 24); + if(value < 0) return -1; + *(dst++) = value >> 16; + *(dst++) = value >> 8; + *(dst++) = value; + nbits -= 24; + } else { + value = per_get_few_bits(pd, nbits); + if(value < 0) return -1; + if(nbits & 7) { /* implies left alignment */ + value <<= 8 - (nbits & 7), + nbits += 8 - (nbits & 7); + if(nbits > 24) + *dst++ = value >> 24; + } + if(nbits > 16) + *dst++ = value >> 16; + if(nbits > 8) + *dst++ = value >> 8; + *dst++ = value; + break; + } + } + + return 0; +} + +/* + * Get the length "n" from the stream. + */ +ssize_t +uper_get_length(asn_per_data_t *pd, int ebits, int *repeat) { + ssize_t value; + + *repeat = 0; + + if(ebits >= 0) return per_get_few_bits(pd, ebits); + + value = per_get_few_bits(pd, 8); + if(value < 0) return -1; + if((value & 128) == 0) /* #10.9.3.6 */ + return (value & 0x7F); + if((value & 64) == 0) { /* #10.9.3.7 */ + value = ((value & 63) << 8) | per_get_few_bits(pd, 8); + if(value < 0) return -1; + return value; + } + value &= 63; /* this is "m" from X.691, #10.9.3.8 */ + if(value < 1 || value > 4) + return -1; + *repeat = 1; + return (16384 * value); +} + +/* + * Get the normally small length "n". + * This procedure used to decode length of extensions bit-maps + * for SET and SEQUENCE types. + */ +ssize_t +uper_get_nslength(asn_per_data_t *pd) { + ssize_t length; + + ASN_DEBUG("Getting normally small length"); + + if(per_get_few_bits(pd, 1) == 0) { + length = per_get_few_bits(pd, 6) + 1; + if(length <= 0) return -1; + ASN_DEBUG("l=%d", (int)length); + return length; + } else { + int repeat; + length = uper_get_length(pd, -1, &repeat); + if(length >= 0 && !repeat) return length; + return -1; /* Error, or do not support >16K extensions */ + } +} + +/* + * Get the normally small non-negative whole number. + * X.691, #10.6 + */ +ssize_t +uper_get_nsnnwn(asn_per_data_t *pd) { + ssize_t value; + + value = per_get_few_bits(pd, 7); + if(value & 64) { /* implicit (value < 0) */ + value &= 63; + value <<= 2; + value |= per_get_few_bits(pd, 2); + if(value & 128) /* implicit (value < 0) */ + return -1; + if(value == 0) + return 0; + if(value >= 3) + return -1; + value = per_get_few_bits(pd, 8 * value); + return value; + } + + return value; +} + +/* + * X.691-11/2008, #11.6 + * Encoding of a normally small non-negative whole number + */ +int +uper_put_nsnnwn(asn_per_outp_t *po, int n) { + int bytes; + + if(n <= 63) { + if(n < 0) return -1; + return per_put_few_bits(po, n, 7); + } + if(n < 256) + bytes = 1; + else if(n < 65536) + bytes = 2; + else if(n < 256 * 65536) + bytes = 3; + else + return -1; /* This is not a "normally small" value */ + if(per_put_few_bits(po, bytes, 8)) + return -1; + + return per_put_few_bits(po, n, 8 * bytes); +} + + +/* X.691-2008/11, #11.5.6 -> #11.3 */ +int uper_get_constrained_whole_number(asn_per_data_t *pd, unsigned long *out_value, int nbits) { + unsigned long lhalf; /* Lower half of the number*/ + long half; + + if(nbits <= 31) { + half = per_get_few_bits(pd, nbits); + if(half < 0) return -1; + *out_value = half; + return 0; + } + + if((size_t)nbits > 8 * sizeof(*out_value)) + return -1; /* RANGE */ + + half = per_get_few_bits(pd, 31); + if(half < 0) return -1; + + if(uper_get_constrained_whole_number(pd, &lhalf, nbits - 31)) + return -1; + + *out_value = ((unsigned long)half << (nbits - 31)) | lhalf; + return 0; +} + + +/* X.691-2008/11, #11.5.6 -> #11.3 */ +int uper_put_constrained_whole_number_s(asn_per_outp_t *po, long v, int nbits) { + /* + * Assume signed number can be safely coerced into + * unsigned of the same range. + * The following testing code will likely be optimized out + * by compiler if it is true. + */ + unsigned long uvalue1 = ULONG_MAX; + long svalue = uvalue1; + unsigned long uvalue2 = svalue; + assert(uvalue1 == uvalue2); + return uper_put_constrained_whole_number_u(po, v, nbits); +} + +int uper_put_constrained_whole_number_u(asn_per_outp_t *po, unsigned long v, int nbits) { + if(nbits <= 31) { + return per_put_few_bits(po, v, nbits); + } else { + /* Put higher portion first, followed by lower 31-bit */ + if(uper_put_constrained_whole_number_u(po, v >> 31, nbits - 31)) + return -1; + return per_put_few_bits(po, v, 31); + } +} + +/* + * Put a small number of bits (<= 31). + */ +int +per_put_few_bits(asn_per_outp_t *po, uint32_t bits, int obits) { + size_t off; /* Next after last bit offset */ + size_t omsk; /* Existing last byte meaningful bits mask */ + uint8_t *buf; + + if(obits <= 0 || obits >= 32) return obits ? -1 : 0; + + ASN_DEBUG("[PER put %d bits %x to %p+%d bits]", + obits, (int)bits, po->buffer, (int)po->nboff); + + /* + * Normalize position indicator. + */ + if(po->nboff >= 8) { + po->buffer += (po->nboff >> 3); + po->nbits -= (po->nboff & ~0x07); + po->nboff &= 0x07; + } + + /* + * Flush whole-bytes output, if necessary. + */ + if(po->nboff + obits > po->nbits) { + int complete_bytes = (po->buffer - po->tmpspace); + ASN_DEBUG("[PER output %ld complete + %ld]", + (long)complete_bytes, (long)po->flushed_bytes); + if(po->outper(po->tmpspace, complete_bytes, po->op_key) < 0) + return -1; + if(po->nboff) + po->tmpspace[0] = po->buffer[0]; + po->buffer = po->tmpspace; + po->nbits = 8 * sizeof(po->tmpspace); + po->flushed_bytes += complete_bytes; + } + + /* + * Now, due to sizeof(tmpspace), we are guaranteed large enough space. + */ + buf = po->buffer; + omsk = ~((1 << (8 - po->nboff)) - 1); + off = (po->nboff + obits); + + /* Clear data of debris before meaningful bits */ + bits &= (((uint32_t)1 << obits) - 1); + + ASN_DEBUG("[PER out %d %u/%x (t=%d,o=%d) %x&%x=%x]", obits, + (int)bits, (int)bits, + (int)po->nboff, (int)off, + buf[0], (int)(omsk&0xff), + (int)(buf[0] & omsk)); + + if(off <= 8) /* Completely within 1 byte */ + po->nboff = off, + bits <<= (8 - off), + buf[0] = (buf[0] & omsk) | bits; + else if(off <= 16) + po->nboff = off, + bits <<= (16 - off), + buf[0] = (buf[0] & omsk) | (bits >> 8), + buf[1] = bits; + else if(off <= 24) + po->nboff = off, + bits <<= (24 - off), + buf[0] = (buf[0] & omsk) | (bits >> 16), + buf[1] = bits >> 8, + buf[2] = bits; + else if(off <= 31) + po->nboff = off, + bits <<= (32 - off), + buf[0] = (buf[0] & omsk) | (bits >> 24), + buf[1] = bits >> 16, + buf[2] = bits >> 8, + buf[3] = bits; + else { + per_put_few_bits(po, bits >> (obits - 24), 24); + per_put_few_bits(po, bits, obits - 24); + } + + ASN_DEBUG("[PER out %u/%x => %02x buf+%ld]", + (int)bits, (int)bits, buf[0], + (long)(po->buffer - po->tmpspace)); + + return 0; +} + + +/* + * Output a large number of bits. + */ +int +per_put_many_bits(asn_per_outp_t *po, const uint8_t *src, int nbits) { + + while(nbits) { + uint32_t value; + + if(nbits >= 24) { + value = (src[0] << 16) | (src[1] << 8) | src[2]; + src += 3; + nbits -= 24; + if(per_put_few_bits(po, value, 24)) + return -1; + } else { + value = src[0]; + if(nbits > 8) + value = (value << 8) | src[1]; + if(nbits > 16) + value = (value << 8) | src[2]; + if(nbits & 0x07) + value >>= (8 - (nbits & 0x07)); + if(per_put_few_bits(po, value, nbits)) + return -1; + break; + } + } + + return 0; +} + +/* + * Put the length "n" (or part of it) into the stream. + */ +ssize_t +uper_put_length(asn_per_outp_t *po, size_t length) { + + if(length <= 127) /* #10.9.3.6 */ + return per_put_few_bits(po, length, 8) + ? -1 : (ssize_t)length; + else if(length < 16384) /* #10.9.3.7 */ + return per_put_few_bits(po, length|0x8000, 16) + ? -1 : (ssize_t)length; + + length >>= 14; + if(length > 4) length = 4; + + return per_put_few_bits(po, 0xC0 | length, 8) + ? -1 : (ssize_t)(length << 14); +} + + +/* + * Put the normally small length "n" into the stream. + * This procedure used to encode length of extensions bit-maps + * for SET and SEQUENCE types. + */ +int +uper_put_nslength(asn_per_outp_t *po, size_t length) { + + if(length <= 64) { + /* #10.9.3.4 */ + if(length == 0) return -1; + return per_put_few_bits(po, length-1, 7) ? -1 : 0; + } else { + if(uper_put_length(po, length) != (ssize_t)length) { + /* This might happen in case of >16K extensions */ + return -1; + } + } + + return 0; +} + diff --git a/src/cryptoconditions/src/asn/per_support.h b/src/cryptoconditions/src/asn/per_support.h new file mode 100644 index 000000000..a75ac94fc --- /dev/null +++ b/src/cryptoconditions/src/asn/per_support.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2005-2014 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _PER_SUPPORT_H_ +#define _PER_SUPPORT_H_ + +#include /* Platform-specific types */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Pre-computed PER constraints. + */ +typedef const struct asn_per_constraint_s { + enum asn_per_constraint_flags { + APC_UNCONSTRAINED = 0x0, /* No PER visible constraints */ + APC_SEMI_CONSTRAINED = 0x1, /* Constrained at "lb" */ + APC_CONSTRAINED = 0x2, /* Fully constrained */ + APC_EXTENSIBLE = 0x4 /* May have extension */ + } flags; + int range_bits; /* Full number of bits in the range */ + int effective_bits; /* Effective bits */ + long lower_bound; /* "lb" value */ + long upper_bound; /* "ub" value */ +} asn_per_constraint_t; +typedef const struct asn_per_constraints_s { + struct asn_per_constraint_s value; + struct asn_per_constraint_s size; + int (*value2code)(unsigned int value); + int (*code2value)(unsigned int code); +} asn_per_constraints_t; + +/* + * This structure describes a position inside an incoming PER bit stream. + */ +typedef struct asn_per_data_s { + const uint8_t *buffer; /* Pointer to the octet stream */ + size_t nboff; /* Bit offset to the meaningful bit */ + size_t nbits; /* Number of bits in the stream */ + size_t moved; /* Number of bits moved through this bit stream */ + int (*refill)(struct asn_per_data_s *); + void *refill_key; +} asn_per_data_t; + +/* + * Extract a small number of bits (<= 31) from the specified PER data pointer. + * This function returns -1 if the specified number of bits could not be + * extracted due to EOD or other conditions. + */ +int32_t per_get_few_bits(asn_per_data_t *per_data, int get_nbits); + +/* Undo the immediately preceeding "get_few_bits" operation */ +void per_get_undo(asn_per_data_t *per_data, int get_nbits); + +/* + * Extract a large number of bits from the specified PER data pointer. + * This function returns -1 if the specified number of bits could not be + * extracted due to EOD or other conditions. + */ +int per_get_many_bits(asn_per_data_t *pd, uint8_t *dst, int right_align, + int get_nbits); + +/* + * Get the length "n" from the Unaligned PER stream. + */ +ssize_t uper_get_length(asn_per_data_t *pd, + int effective_bound_bits, + int *repeat); + +/* + * Get the normally small length "n". + */ +ssize_t uper_get_nslength(asn_per_data_t *pd); + +/* + * Get the normally small non-negative whole number. + */ +ssize_t uper_get_nsnnwn(asn_per_data_t *pd); + +/* X.691-2008/11, #11.5.6 */ +int uper_get_constrained_whole_number(asn_per_data_t *pd, unsigned long *v, int nbits); + +/* Non-thread-safe debugging function, don't use it */ +char *per_data_string(asn_per_data_t *pd); + +/* + * This structure supports forming PER output. + */ +typedef struct asn_per_outp_s { + uint8_t *buffer; /* Pointer into the (tmpspace) */ + size_t nboff; /* Bit offset to the meaningful bit */ + size_t nbits; /* Number of bits left in (tmpspace) */ + uint8_t tmpspace[32]; /* Preliminary storage to hold data */ + int (*outper)(const void *data, size_t size, void *op_key); + void *op_key; /* Key for (outper) data callback */ + size_t flushed_bytes; /* Bytes already flushed through (outper) */ +} asn_per_outp_t; + +/* Output a small number of bits (<= 31) */ +int per_put_few_bits(asn_per_outp_t *per_data, uint32_t bits, int obits); + +/* Output a large number of bits */ +int per_put_many_bits(asn_per_outp_t *po, const uint8_t *src, int put_nbits); + +/* X.691-2008/11, #11.5 */ +int uper_put_constrained_whole_number_s(asn_per_outp_t *po, long v, int nbits); +int uper_put_constrained_whole_number_u(asn_per_outp_t *po, unsigned long v, int nbits); + +/* + * Put the length "n" to the Unaligned PER stream. + * This function returns the number of units which may be flushed + * in the next units saving iteration. + */ +ssize_t uper_put_length(asn_per_outp_t *po, size_t whole_length); + +/* + * Put the normally small length "n" to the Unaligned PER stream. + * Returns 0 or -1. + */ +int uper_put_nslength(asn_per_outp_t *po, size_t length); + +/* + * Put the normally small non-negative whole number. + */ +int uper_put_nsnnwn(asn_per_outp_t *po, int n); + +#ifdef __cplusplus +} +#endif + +#endif /* _PER_SUPPORT_H_ */ diff --git a/src/cryptoconditions/src/asn/xer_decoder.c b/src/cryptoconditions/src/asn/xer_decoder.c new file mode 100644 index 000000000..299a7c1ee --- /dev/null +++ b/src/cryptoconditions/src/asn/xer_decoder.c @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2004, 2005 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include /* XER/XML parsing support */ + + +/* + * Decode the XER encoding of a given type. + */ +asn_dec_rval_t +xer_decode(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **struct_ptr, const void *buffer, size_t size) { + asn_codec_ctx_t s_codec_ctx; + + /* + * Stack checker requires that the codec context + * must be allocated on the stack. + */ + if(opt_codec_ctx) { + if(opt_codec_ctx->max_stack_size) { + s_codec_ctx = *opt_codec_ctx; + opt_codec_ctx = &s_codec_ctx; + } + } else { + /* If context is not given, be security-conscious anyway */ + memset(&s_codec_ctx, 0, sizeof(s_codec_ctx)); + s_codec_ctx.max_stack_size = ASN__DEFAULT_STACK_MAX; + opt_codec_ctx = &s_codec_ctx; + } + + /* + * Invoke type-specific decoder. + */ + return td->xer_decoder(opt_codec_ctx, td, struct_ptr, 0, buffer, size); +} + + + +struct xer__cb_arg { + pxml_chunk_type_e chunk_type; + size_t chunk_size; + const void *chunk_buf; + int callback_not_invoked; +}; + +static int +xer__token_cb(pxml_chunk_type_e type, const void *_chunk_data, size_t _chunk_size, void *key) { + struct xer__cb_arg *arg = (struct xer__cb_arg *)key; + arg->chunk_type = type; + arg->chunk_size = _chunk_size; + arg->chunk_buf = _chunk_data; + arg->callback_not_invoked = 0; + return -1; /* Terminate the XML parsing */ +} + +/* + * Fetch the next token from the XER/XML stream. + */ +ssize_t +xer_next_token(int *stateContext, const void *buffer, size_t size, pxer_chunk_type_e *ch_type) { + struct xer__cb_arg arg; + int new_stateContext = *stateContext; + ssize_t ret; + + arg.callback_not_invoked = 1; + ret = pxml_parse(&new_stateContext, buffer, size, xer__token_cb, &arg); + if(ret < 0) return -1; + if(arg.callback_not_invoked) { + assert(ret == 0); /* No data was consumed */ + *ch_type = PXER_WMORE; + return 0; /* Try again with more data */ + } else { + assert(arg.chunk_size); + assert(arg.chunk_buf == buffer); + } + + /* + * Translate the XML chunk types into more convenient ones. + */ + switch(arg.chunk_type) { + case PXML_TEXT: + *ch_type = PXER_TEXT; + break; + case PXML_TAG: + *ch_type = PXER_WMORE; + return 0; /* Want more */ + case PXML_TAG_END: + *ch_type = PXER_TAG; + break; + case PXML_COMMENT: + case PXML_COMMENT_END: + *ch_type = PXER_COMMENT; + break; + } + + *stateContext = new_stateContext; + return arg.chunk_size; +} + +#define CSLASH 0x2f /* '/' */ +#define LANGLE 0x3c /* '<' */ +#define RANGLE 0x3e /* '>' */ + +xer_check_tag_e +xer_check_tag(const void *buf_ptr, int size, const char *need_tag) { + const char *buf = (const char *)buf_ptr; + const char *end; + xer_check_tag_e ct = XCT_OPENING; + + if(size < 2 || buf[0] != LANGLE || buf[size-1] != RANGLE) { + if(size >= 2) + ASN_DEBUG("Broken XML tag: \"%c...%c\"", + buf[0], buf[size - 1]); + return XCT_BROKEN; + } + + /* + * Determine the tag class. + */ + if(buf[1] == CSLASH) { + buf += 2; /* advance past "" */ + ct = XCT_CLOSING; + if(size > 0 && buf[size-1] == CSLASH) + return XCT_BROKEN; /* */ + } else { + buf++; /* advance past "<" */ + size -= 2; /* strip "<" and ">" */ + if(size > 0 && buf[size-1] == CSLASH) { + ct = XCT_BOTH; + size--; /* One more, for "/" */ + } + } + + /* Sometimes we don't care about the tag */ + if(!need_tag || !*need_tag) + return (xer_check_tag_e)(XCT__UNK__MASK | ct); + + /* + * Determine the tag name. + */ + for(end = buf + size; buf < end; buf++, need_tag++) { + int b = *buf, n = *need_tag; + if(b != n) { + if(n == 0) { + switch(b) { + case 0x09: case 0x0a: case 0x0c: case 0x0d: + case 0x20: + /* "": whitespace is normal */ + return ct; + } + } + return (xer_check_tag_e)(XCT__UNK__MASK | ct); + } + if(b == 0) + return XCT_BROKEN; /* Embedded 0 in buf?! */ + } + if(*need_tag) + return (xer_check_tag_e)(XCT__UNK__MASK | ct); + + return ct; +} + + +#undef ADVANCE +#define ADVANCE(num_bytes) do { \ + size_t num = (num_bytes); \ + buf_ptr = ((const char *)buf_ptr) + num; \ + size -= num; \ + consumed_myself += num; \ + } while(0) + +#undef RETURN +#define RETURN(_code) do { \ + rval.code = _code; \ + rval.consumed = consumed_myself; \ + if(rval.code != RC_OK) \ + ASN_DEBUG("Failed with %d", rval.code); \ + return rval; \ + } while(0) + +#define XER_GOT_BODY(chunk_buf, chunk_size, size) do { \ + ssize_t converted_size = body_receiver \ + (struct_key, chunk_buf, chunk_size, \ + (size_t)chunk_size < size); \ + if(converted_size == -1) RETURN(RC_FAIL); \ + if(converted_size == 0 \ + && size == (size_t)chunk_size) \ + RETURN(RC_WMORE); \ + chunk_size = converted_size; \ + } while(0) +#define XER_GOT_EMPTY() do { \ + if(body_receiver(struct_key, 0, 0, size > 0) == -1) \ + RETURN(RC_FAIL); \ + } while(0) + +/* + * Generalized function for decoding the primitive values. + */ +asn_dec_rval_t +xer_decode_general(asn_codec_ctx_t *opt_codec_ctx, + asn_struct_ctx_t *ctx, /* Type decoder context */ + void *struct_key, + const char *xml_tag, /* Expected XML tag */ + const void *buf_ptr, size_t size, + int (*opt_unexpected_tag_decoder) + (void *struct_key, const void *chunk_buf, size_t chunk_size), + ssize_t (*body_receiver) + (void *struct_key, const void *chunk_buf, size_t chunk_size, + int have_more) + ) { + + asn_dec_rval_t rval; + ssize_t consumed_myself = 0; + + (void)opt_codec_ctx; + + /* + * Phases of XER/XML processing: + * Phase 0: Check that the opening tag matches our expectations. + * Phase 1: Processing body and reacting on closing tag. + */ + if(ctx->phase > 1) RETURN(RC_FAIL); + for(;;) { + pxer_chunk_type_e ch_type; /* XER chunk type */ + ssize_t ch_size; /* Chunk size */ + xer_check_tag_e tcv; /* Tag check value */ + + /* + * Get the next part of the XML stream. + */ + ch_size = xer_next_token(&ctx->context, buf_ptr, size, + &ch_type); + if(ch_size == -1) { + RETURN(RC_FAIL); + } else { + switch(ch_type) { + case PXER_WMORE: + RETURN(RC_WMORE); + case PXER_COMMENT: /* Got XML comment */ + ADVANCE(ch_size); /* Skip silently */ + continue; + case PXER_TEXT: + if(ctx->phase == 0) { + /* + * We have to ignore whitespace here, + * but in order to be forward compatible + * with EXTENDED-XER (EMBED-VALUES, #25) + * any text is just ignored here. + */ + } else { + XER_GOT_BODY(buf_ptr, ch_size, size); + } + ADVANCE(ch_size); + continue; + case PXER_TAG: + break; /* Check the rest down there */ + } + } + + assert(ch_type == PXER_TAG && size); + + tcv = xer_check_tag(buf_ptr, ch_size, xml_tag); + /* + * Phase 0: + * Expecting the opening tag + * for the type being processed. + * Phase 1: + * Waiting for the closing XML tag. + */ + switch(tcv) { + case XCT_BOTH: + if(ctx->phase) break; + /* Finished decoding of an empty element */ + XER_GOT_EMPTY(); + ADVANCE(ch_size); + ctx->phase = 2; /* Phase out */ + RETURN(RC_OK); + case XCT_OPENING: + if(ctx->phase) break; + ADVANCE(ch_size); + ctx->phase = 1; /* Processing body phase */ + continue; + case XCT_CLOSING: + if(!ctx->phase) break; + ADVANCE(ch_size); + ctx->phase = 2; /* Phase out */ + RETURN(RC_OK); + case XCT_UNKNOWN_BO: + /* + * Certain tags in the body may be expected. + */ + if(opt_unexpected_tag_decoder + && opt_unexpected_tag_decoder(struct_key, + buf_ptr, ch_size) >= 0) { + /* Tag's processed fine */ + ADVANCE(ch_size); + if(!ctx->phase) { + /* We are not expecting + * the closing tag anymore. */ + ctx->phase = 2; /* Phase out */ + RETURN(RC_OK); + } + continue; + } + /* Fall through */ + default: + break; /* Unexpected tag */ + } + + ASN_DEBUG("Unexpected XML tag (expected \"%s\")", xml_tag); + break; /* Dark and mysterious things have just happened */ + } + + RETURN(RC_FAIL); +} + + +size_t +xer_whitespace_span(const void *chunk_buf, size_t chunk_size) { + const char *p = (const char *)chunk_buf; + const char *pend = p + chunk_size; + + for(; p < pend; p++) { + switch(*p) { + /* X.693, #8.1.4 + * HORISONTAL TAB (9) + * LINE FEED (10) + * CARRIAGE RETURN (13) + * SPACE (32) + */ + case 0x09: case 0x0a: case 0x0d: case 0x20: + continue; + default: + break; + } + break; + } + return (p - (const char *)chunk_buf); +} + +/* + * This is a vastly simplified, non-validating XML tree skipper. + */ +int +xer_skip_unknown(xer_check_tag_e tcv, ber_tlv_len_t *depth) { + assert(*depth > 0); + switch(tcv) { + case XCT_BOTH: + case XCT_UNKNOWN_BO: + /* These negate each other. */ + return 0; + case XCT_OPENING: + case XCT_UNKNOWN_OP: + ++(*depth); + return 0; + case XCT_CLOSING: + case XCT_UNKNOWN_CL: + if(--(*depth) == 0) + return (tcv == XCT_CLOSING) ? 2 : 1; + return 0; + default: + return -1; + } +} diff --git a/src/cryptoconditions/src/asn/xer_decoder.h b/src/cryptoconditions/src/asn/xer_decoder.h new file mode 100644 index 000000000..301b613cf --- /dev/null +++ b/src/cryptoconditions/src/asn/xer_decoder.h @@ -0,0 +1,106 @@ +/*- + * Copyright (c) 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _XER_DECODER_H_ +#define _XER_DECODER_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct asn_TYPE_descriptor_s; /* Forward declaration */ + +/* + * The XER decoder of any ASN.1 type. May be invoked by the application. + */ +asn_dec_rval_t xer_decode(struct asn_codec_ctx_s *opt_codec_ctx, + struct asn_TYPE_descriptor_s *type_descriptor, + void **struct_ptr, /* Pointer to a target structure's pointer */ + const void *buffer, /* Data to be decoded */ + size_t size /* Size of data buffer */ + ); + +/* + * Type of the type-specific XER decoder function. + */ +typedef asn_dec_rval_t (xer_type_decoder_f)(asn_codec_ctx_t *opt_codec_ctx, + struct asn_TYPE_descriptor_s *type_descriptor, + void **struct_ptr, + const char *opt_mname, /* Member name */ + const void *buf_ptr, size_t size + ); + +/******************************* + * INTERNALLY USEFUL FUNCTIONS * + *******************************/ + +/* + * Generalized function for decoding the primitive values. + * Used by more specialized functions, such as OCTET_STRING_decode_xer_utf8 + * and others. This function should not be used by applications, as its API + * is subject to changes. + */ +asn_dec_rval_t xer_decode_general(asn_codec_ctx_t *opt_codec_ctx, + asn_struct_ctx_t *ctx, /* Type decoder context */ + void *struct_key, /* Treated as opaque pointer */ + const char *xml_tag, /* Expected XML tag name */ + const void *buf_ptr, size_t size, + int (*opt_unexpected_tag_decoder) + (void *struct_key, const void *chunk_buf, size_t chunk_size), + ssize_t (*body_receiver) + (void *struct_key, const void *chunk_buf, size_t chunk_size, + int have_more) + ); + + +/* + * Fetch the next XER (XML) token from the stream. + * The function returns the number of bytes occupied by the chunk type, + * returned in the _ch_type. The _ch_type is only set (and valid) when + * the return value is >= 0. + */ + typedef enum pxer_chunk_type { + PXER_WMORE, /* Chunk type is not clear, more data expected. */ + PXER_TAG, /* Complete XER tag */ + PXER_TEXT, /* Plain text between XER tags */ + PXER_COMMENT /* A comment, may be part of */ + } pxer_chunk_type_e; +ssize_t xer_next_token(int *stateContext, + const void *buffer, size_t size, pxer_chunk_type_e *_ch_type); + +/* + * This function checks the buffer against the tag name is expected to occur. + */ + typedef enum xer_check_tag { + XCT_BROKEN = 0, /* The tag is broken */ + XCT_OPENING = 1, /* This is the tag */ + XCT_CLOSING = 2, /* This is the tag */ + XCT_BOTH = 3, /* This is the tag */ + XCT__UNK__MASK = 4, /* Mask of everything unexpected */ + XCT_UNKNOWN_OP = 5, /* Unexpected tag */ + XCT_UNKNOWN_CL = 6, /* Unexpected tag */ + XCT_UNKNOWN_BO = 7 /* Unexpected tag */ + } xer_check_tag_e; +xer_check_tag_e xer_check_tag(const void *buf_ptr, int size, + const char *need_tag); + +/* + * Get the number of bytes consisting entirely of XER whitespace characters. + * RETURN VALUES: + * >=0: Number of whitespace characters in the string. + */ +size_t xer_whitespace_span(const void *chunk_buf, size_t chunk_size); + +/* + * Skip the series of anticipated extensions. + */ +int xer_skip_unknown(xer_check_tag_e tcv, ber_tlv_len_t *depth); + +#ifdef __cplusplus +} +#endif + +#endif /* _XER_DECODER_H_ */ diff --git a/src/cryptoconditions/src/asn/xer_encoder.c b/src/cryptoconditions/src/asn/xer_encoder.c new file mode 100644 index 000000000..460657580 --- /dev/null +++ b/src/cryptoconditions/src/asn/xer_encoder.c @@ -0,0 +1,67 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +/* + * The XER encoder of any type. May be invoked by the application. + */ +asn_enc_rval_t +xer_encode(asn_TYPE_descriptor_t *td, void *sptr, + enum xer_encoder_flags_e xer_flags, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_enc_rval_t er, tmper; + const char *mname; + size_t mlen; + int xcan = (xer_flags & XER_F_CANONICAL) ? 1 : 2; + + if(!td || !sptr) goto cb_failed; + + mname = td->xml_tag; + mlen = strlen(mname); + + ASN__CALLBACK3("<", 1, mname, mlen, ">", 1); + + tmper = td->xer_encoder(td, sptr, 1, xer_flags, cb, app_key); + if(tmper.encoded == -1) return tmper; + + ASN__CALLBACK3("\n", xcan); + + er.encoded = 4 + xcan + (2 * mlen) + tmper.encoded; + + ASN__ENCODED_OK(er); +cb_failed: + ASN__ENCODE_FAILED; +} + +/* + * This is a helper function for xer_fprint, which directs all incoming data + * into the provided file descriptor. + */ +static int +xer__print2fp(const void *buffer, size_t size, void *app_key) { + FILE *stream = (FILE *)app_key; + + if(fwrite(buffer, 1, size, stream) != size) + return -1; + + return 0; +} + +int +xer_fprint(FILE *stream, asn_TYPE_descriptor_t *td, void *sptr) { + asn_enc_rval_t er; + + if(!stream) stream = stdout; + if(!td || !sptr) + return -1; + + er = xer_encode(td, sptr, XER_F_BASIC, xer__print2fp, stream); + if(er.encoded == -1) + return -1; + + return fflush(stream); +} diff --git a/src/cryptoconditions/src/asn/xer_encoder.h b/src/cryptoconditions/src/asn/xer_encoder.h new file mode 100644 index 000000000..055e73c0c --- /dev/null +++ b/src/cryptoconditions/src/asn/xer_encoder.h @@ -0,0 +1,59 @@ +/*- + * Copyright (c) 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _XER_ENCODER_H_ +#define _XER_ENCODER_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct asn_TYPE_descriptor_s; /* Forward declaration */ + +/* Flags used by the xer_encode() and (*xer_type_encoder_f), defined below */ +enum xer_encoder_flags_e { + /* Mode of encoding */ + XER_F_BASIC = 0x01, /* BASIC-XER (pretty-printing) */ + XER_F_CANONICAL = 0x02 /* Canonical XER (strict rules) */ +}; + +/* + * The XER encoder of any type. May be invoked by the application. + */ +asn_enc_rval_t xer_encode(struct asn_TYPE_descriptor_s *type_descriptor, + void *struct_ptr, /* Structure to be encoded */ + enum xer_encoder_flags_e xer_flags, + asn_app_consume_bytes_f *consume_bytes_cb, + void *app_key /* Arbitrary callback argument */ + ); + +/* + * The variant of the above function which dumps the BASIC-XER (XER_F_BASIC) + * output into the chosen file pointer. + * RETURN VALUES: + * 0: The structure is printed. + * -1: Problem printing the structure. + * WARNING: No sensible errno value is returned. + */ +int xer_fprint(FILE *stream, struct asn_TYPE_descriptor_s *td, void *sptr); + +/* + * Type of the generic XER encoder. + */ +typedef asn_enc_rval_t (xer_type_encoder_f)( + struct asn_TYPE_descriptor_s *type_descriptor, + void *struct_ptr, /* Structure to be encoded */ + int ilevel, /* Level of indentation */ + enum xer_encoder_flags_e xer_flags, + asn_app_consume_bytes_f *consume_bytes_cb, /* Callback */ + void *app_key /* Arbitrary callback argument */ + ); + +#ifdef __cplusplus +} +#endif + +#endif /* _XER_ENCODER_H_ */ diff --git a/src/cryptoconditions/src/asn/xer_support.c b/src/cryptoconditions/src/asn/xer_support.c new file mode 100644 index 000000000..36b4bfbfc --- /dev/null +++ b/src/cryptoconditions/src/asn/xer_support.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2003, 2004 X/IO Labs, xiolabs.com. + * Copyright (c) 2003, 2004, 2005 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include + +/* Parser states */ +typedef enum { + ST_TEXT, + ST_TAG_START, + ST_TAG_BODY, + ST_TAG_QUOTE_WAIT, + ST_TAG_QUOTED_STRING, + ST_TAG_UNQUOTED_STRING, + ST_COMMENT_WAIT_DASH1, /* ""[0] */ + ST_COMMENT_CLO_RT /* "-->"[1] */ +} pstate_e; + +static const int +_charclass[256] = { + 0,0,0,0,0,0,0,0, 0,1,1,0,1,1,0,0, + 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2, 2,2,0,0,0,0,0,0, /* 01234567 89 */ + 0,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3, /* ABCDEFG HIJKLMNO */ + 3,3,3,3,3,3,3,3, 3,3,3,0,0,0,0,0, /* PQRSTUVW XYZ */ + 0,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3, /* abcdefg hijklmno */ + 3,3,3,3,3,3,3,3, 3,3,3,0,0,0,0,0 /* pqrstuvw xyz */ +}; +#define WHITESPACE(c) (_charclass[(unsigned char)(c)] == 1) +#define ALNUM(c) (_charclass[(unsigned char)(c)] >= 2) +#define ALPHA(c) (_charclass[(unsigned char)(c)] == 3) + +/* Aliases for characters, ASCII/UTF-8 */ +#define EXCLAM 0x21 /* '!' */ +#define CQUOTE 0x22 /* '"' */ +#define CDASH 0x2d /* '-' */ +#define CSLASH 0x2f /* '/' */ +#define LANGLE 0x3c /* '<' */ +#define CEQUAL 0x3d /* '=' */ +#define RANGLE 0x3e /* '>' */ +#define CQUEST 0x3f /* '?' */ + +/* Invoke token callback */ +#define TOKEN_CB_CALL(type, _ns, _current_too, _final) do { \ + int _ret; \ + pstate_e ns = _ns; \ + ssize_t _sz = (p - chunk_start) + _current_too; \ + if (!_sz) { \ + /* Shortcut */ \ + state = _ns; \ + break; \ + } \ + _ret = cb(type, chunk_start, _sz, key); \ + if(_ret < _sz) { \ + if(_current_too && _ret == -1) \ + state = ns; \ + goto finish; \ + } \ + chunk_start = p + _current_too; \ + state = ns; \ + } while(0) + +#define TOKEN_CB(_type, _ns, _current_too) \ + TOKEN_CB_CALL(_type, _ns, _current_too, 0) + +#define PXML_TAG_FINAL_CHUNK_TYPE PXML_TAG_END +#define PXML_COMMENT_FINAL_CHUNK_TYPE PXML_COMMENT_END + +#define TOKEN_CB_FINAL(_type, _ns, _current_too) \ + TOKEN_CB_CALL( _type ## _FINAL_CHUNK_TYPE , _ns, _current_too, 1) + +/* + * Parser itself + */ +ssize_t pxml_parse(int *stateContext, const void *xmlbuf, size_t size, pxml_callback_f *cb, void *key) { + pstate_e state = (pstate_e)*stateContext; + const char *chunk_start = (const char *)xmlbuf; + const char *p = chunk_start; + const char *end = p + size; + + for(; p < end; p++) { + int C = *(const unsigned char *)p; + switch(state) { + case ST_TEXT: + /* + * Initial state: we're in the middle of some text, + * or just have started. + */ + if (C == LANGLE) + /* We're now in the tag, probably */ + TOKEN_CB(PXML_TEXT, ST_TAG_START, 0); + break; + case ST_TAG_START: + if (ALPHA(C) || (C == CSLASH)) + state = ST_TAG_BODY; + else if (C == EXCLAM) + state = ST_COMMENT_WAIT_DASH1; + else + /* + * Not characters and not whitespace. + * Must be something like "3 < 4". + */ + TOKEN_CB(PXML_TEXT, ST_TEXT, 1);/* Flush as data */ + break; + case ST_TAG_BODY: + switch(C) { + case RANGLE: + /* End of the tag */ + TOKEN_CB_FINAL(PXML_TAG, ST_TEXT, 1); + break; + case LANGLE: + /* + * The previous tag wasn't completed, but still + * recognized as valid. (Mozilla-compatible) + */ + TOKEN_CB_FINAL(PXML_TAG, ST_TAG_START, 0); + break; + case CEQUAL: + state = ST_TAG_QUOTE_WAIT; + break; + } + break; + case ST_TAG_QUOTE_WAIT: + /* + * State after the equal sign ("=") in the tag. + */ + switch(C) { + case CQUOTE: + state = ST_TAG_QUOTED_STRING; + break; + case RANGLE: + /* End of the tag */ + TOKEN_CB_FINAL(PXML_TAG, ST_TEXT, 1); + break; + default: + if(!WHITESPACE(C)) + /* Unquoted string value */ + state = ST_TAG_UNQUOTED_STRING; + } + break; + case ST_TAG_QUOTED_STRING: + /* + * Tag attribute's string value in quotes. + */ + if(C == CQUOTE) { + /* Return back to the tag state */ + state = ST_TAG_BODY; + } + break; + case ST_TAG_UNQUOTED_STRING: + if(C == RANGLE) { + /* End of the tag */ + TOKEN_CB_FINAL(PXML_TAG, ST_TEXT, 1); + } else if(WHITESPACE(C)) { + /* Return back to the tag state */ + state = ST_TAG_BODY; + } + break; + case ST_COMMENT_WAIT_DASH1: + if(C == CDASH) { + state = ST_COMMENT_WAIT_DASH2; + } else { + /* Some ordinary tag. */ + state = ST_TAG_BODY; + } + break; + case ST_COMMENT_WAIT_DASH2: + if(C == CDASH) { + /* Seen "<--" */ + state = ST_COMMENT; + } else { + /* Some ordinary tag */ + state = ST_TAG_BODY; + } + break; + case ST_COMMENT: + if(C == CDASH) { + state = ST_COMMENT_CLO_DASH2; + } + break; + case ST_COMMENT_CLO_DASH2: + if(C == CDASH) { + state = ST_COMMENT_CLO_RT; + } else { + /* This is not an end of a comment */ + state = ST_COMMENT; + } + break; + case ST_COMMENT_CLO_RT: + if(C == RANGLE) { + TOKEN_CB_FINAL(PXML_COMMENT, ST_TEXT, 1); + } else if(C == CDASH) { + /* Maintain current state, still waiting for '>' */ + } else { + state = ST_COMMENT; + } + break; + } /* switch(*ptr) */ + } /* for() */ + + /* + * Flush the partially processed chunk, state permitting. + */ + if(p - chunk_start) { + switch (state) { + case ST_COMMENT: + TOKEN_CB(PXML_COMMENT, state, 0); + break; + case ST_TEXT: + TOKEN_CB(PXML_TEXT, state, 0); + break; + default: break; /* a no-op */ + } + } + +finish: + *stateContext = (int)state; + return chunk_start - (const char *)xmlbuf; +} + diff --git a/src/cryptoconditions/src/asn/xer_support.h b/src/cryptoconditions/src/asn/xer_support.h new file mode 100644 index 000000000..8b01944ab --- /dev/null +++ b/src/cryptoconditions/src/asn/xer_support.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2003, 2004 X/IO Labs, xiolabs.com. + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _XER_SUPPORT_H_ +#define _XER_SUPPORT_H_ + +#include /* Platform-specific types */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Types of data transferred to the application. + */ +typedef enum { + PXML_TEXT, /* Plain text between XML tags. */ + PXML_TAG, /* A tag, starting with '<'. */ + PXML_COMMENT, /* An XML comment, including "". */ + /* + * The following chunk types are reported if the chunk + * terminates the specified XML element. + */ + PXML_TAG_END, /* Tag ended */ + PXML_COMMENT_END /* Comment ended */ +} pxml_chunk_type_e; + +/* + * Callback function that is called by the parser when parsed data is + * available. The _opaque is the pointer to a field containing opaque user + * data specified in pxml_create() call. The chunk type is _type and the text + * data is the piece of buffer identified by _bufid (as supplied to + * pxml_feed() call) starting at offset _offset and of _size bytes size. + * The chunk is NOT '\0'-terminated. + */ +typedef int (pxml_callback_f)(pxml_chunk_type_e _type, + const void *_chunk_data, size_t _chunk_size, void *_key); + +/* + * Parse the given buffer as it were a chunk of XML data. + * Invoke the specified callback each time the meaninful data is found. + * This function returns number of bytes consumed from the bufer. + * It will always be lesser than or equal to the specified _size. + * The next invocation of this function must account the difference. + */ +ssize_t pxml_parse(int *_stateContext, const void *_buf, size_t _size, + pxml_callback_f *cb, void *_key); + +#ifdef __cplusplus +} +#endif + +#endif /* _XER_SUPPORT_H_ */ diff --git a/src/cryptoconditions/src/cryptoconditions-config.h.in b/src/cryptoconditions/src/cryptoconditions-config.h.in new file mode 100644 index 000000000..72f0d216b --- /dev/null +++ b/src/cryptoconditions/src/cryptoconditions-config.h.in @@ -0,0 +1,176 @@ +/* src/cryptoconditions-config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP + systems. This function is required for `alloca.c' support on those systems. + */ +#undef CRAY_STACKSEG_END + +/* Define to 1 if using `alloca.c'. */ +#undef C_ALLOCA + +/* Define to 1 if you have `alloca', as a function or macro. */ +#undef HAVE_ALLOCA + +/* Define to 1 if you have and it should be used (not on Ultrix). + */ +#undef HAVE_ALLOCA_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_ARPA_INET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_FLOAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIMITS_H + +/* Define to 1 if you have the `localeconv' function. */ +#undef HAVE_LOCALECONV + +/* Define to 1 if you have the header file. */ +#undef HAVE_LOCALE_H + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#undef HAVE_MALLOC + +/* Define to 1 if you have the header file. */ +#undef HAVE_MALLOC_H + +/* Define to 1 if you have the `memchr' function. */ +#undef HAVE_MEMCHR + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `memset' function. */ +#undef HAVE_MEMSET + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if the system has the type `ptrdiff_t'. */ +#undef HAVE_PTRDIFF_T + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDDEF_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if the system has the type `_Bool'. */ +#undef HAVE__BOOL + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at runtime. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ +#undef STACK_DIRECTION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Define for Solaris 2.5.1 so the uint32_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT32_T + +/* Define for Solaris 2.5.1 so the uint64_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT64_T + +/* Define for Solaris 2.5.1 so the uint8_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT8_T + +/* Define to the type of a signed integer type of width exactly 16 bits if + such a type exists and the standard includes do not define it. */ +#undef int16_t + +/* Define to the type of a signed integer type of width exactly 32 bits if + such a type exists and the standard includes do not define it. */ +#undef int32_t + +/* Define to the type of a signed integer type of width exactly 8 bits if such + a type exists and the standard includes do not define it. */ +#undef int8_t + +/* Define to rpl_malloc if the replacement function should be used. */ +#undef malloc + +/* Define to `unsigned int' if does not define. */ +#undef size_t + +/* Define to `int' if does not define. */ +#undef ssize_t + +/* Define to the type of an unsigned integer type of width exactly 16 bits if + such a type exists and the standard includes do not define it. */ +#undef uint16_t + +/* Define to the type of an unsigned integer type of width exactly 32 bits if + such a type exists and the standard includes do not define it. */ +#undef uint32_t + +/* Define to the type of an unsigned integer type of width exactly 64 bits if + such a type exists and the standard includes do not define it. */ +#undef uint64_t + +/* Define to the type of an unsigned integer type of width exactly 8 bits if + such a type exists and the standard includes do not define it. */ +#undef uint8_t diff --git a/src/cryptoconditions/src/cryptoconditions.c b/src/cryptoconditions/src/cryptoconditions.c new file mode 100644 index 000000000..2f136917e --- /dev/null +++ b/src/cryptoconditions/src/cryptoconditions.c @@ -0,0 +1,298 @@ +#include "strings.h" +#include "asn/Condition.h" +#include "asn/Fulfillment.h" +#include "asn/OCTET_STRING.h" +#include "cryptoconditions.h" +#include "src/internal.h" +#include "src/threshold.c" +#include "src/prefix.c" +#include "src/preimage.c" +#include "src/ed25519.c" +#include "src/secp256k1.c" +#include "src/anon.c" +#include "src/eval.c" +#include "src/json_rpc.c" +#include +#include + + +struct CCType *CCTypeRegistry[] = { + &CC_PreimageType, + &CC_PrefixType, + &CC_ThresholdType, + NULL, /* &CC_rsaType */ + &CC_Ed25519Type, + &CC_Secp256k1Type, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 6-14 unused */ + &CC_EvalType +}; + + +int CCTypeRegistryLength = sizeof(CCTypeRegistry) / sizeof(CCTypeRegistry[0]); + + +void appendUriSubtypes(uint32_t mask, unsigned char *buf) { + int append = 0; + for (int i=0; i<32; i++) { + if (mask & 1 << i) { + if (append) { + strcat(buf, ","); + strcat(buf, CCTypeRegistry[i]->name); + } else { + strcat(buf, "&subtypes="); + strcat(buf, CCTypeRegistry[i]->name); + append = 1; + } + } + } +} + + +char *cc_conditionUri(const CC *cond) { + unsigned char *fp = cond->type->fingerprint(cond); + if (!fp) return NULL; + + unsigned char *encoded = base64_encode(fp, 32); + + unsigned char *out = calloc(1, 1000); + sprintf(out, "ni:///sha-256;%s?fpt=%s&cost=%lu", + encoded, cc_typeName(cond), cc_getCost(cond)); + + if (cond->type->getSubtypes) { + appendUriSubtypes(cond->type->getSubtypes(cond), out); + } + + free(fp); + free(encoded); + + return out; +} + + +ConditionTypes_t asnSubtypes(uint32_t mask) { + ConditionTypes_t types; + uint8_t buf[4] = {0,0,0,0}; + int maxId = 0; + + for (int i=0; i<32; i++) { + if (mask & (1<> 3] |= 1 << (7 - i % 8); + } + } + + types.size = 1 + (maxId >> 3); + types.buf = calloc(1, types.size); + memcpy(types.buf, &buf, types.size); + types.bits_unused = 7 - maxId % 8; + return types; +} + + +uint32_t fromAsnSubtypes(const ConditionTypes_t types) { + uint32_t mask = 0; + for (int i=0; i> 3] & (1 << (7 - i % 8))) { + mask |= 1 << i; + } + } + return mask; +} + + +size_t cc_conditionBinary(const CC *cond, unsigned char *buf) { + Condition_t *asn = calloc(1, sizeof(Condition_t)); + asnCondition(cond, asn); + asn_enc_rval_t rc = der_encode_to_buffer(&asn_DEF_Condition, asn, buf, 1000); + if (rc.encoded == -1) { + fprintf(stderr, "CONDITION NOT ENCODED\n"); + return 0; + } + ASN_STRUCT_FREE(asn_DEF_Condition, asn); + return rc.encoded; +} + + +size_t cc_fulfillmentBinary(const CC *cond, unsigned char *buf, size_t length) { + Fulfillment_t *ffill = asnFulfillmentNew(cond); + asn_enc_rval_t rc = der_encode_to_buffer(&asn_DEF_Fulfillment, ffill, buf, length); + if (rc.encoded == -1) { + fprintf(stderr, "FULFILLMENT NOT ENCODED\n"); + return 0; + } + ASN_STRUCT_FREE(asn_DEF_Fulfillment, ffill); + return rc.encoded; +} + + +void asnCondition(const CC *cond, Condition_t *asn) { + asn->present = cc_isAnon(cond) ? cond->conditionType->asnType : cond->type->asnType; + + // This may look a little weird - we dont have a reference here to the correct + // union choice for the condition type, so we just assign everything to the threshold + // type. This works out nicely since the union choices have the same binary interface. + + CompoundSha256Condition_t *choice = &asn->choice.thresholdSha256; + choice->cost = cc_getCost(cond); + choice->fingerprint.buf = cond->type->fingerprint(cond); + choice->fingerprint.size = 32; + choice->subtypes = asnSubtypes(cond->type->getSubtypes(cond)); +} + + +Condition_t *asnConditionNew(const CC *cond) { + Condition_t *asn = calloc(1, sizeof(Condition_t)); + asnCondition(cond, asn); + return asn; +} + + +Fulfillment_t *asnFulfillmentNew(const CC *cond) { + return cond->type->toFulfillment(cond); +} + + +unsigned long cc_getCost(const CC *cond) { + return cond->type->getCost(cond); +} + + +CCType *getTypeByAsnEnum(Condition_PR present) { + for (int i=0; iasnType == present) { + return CCTypeRegistry[i]; + } + } + return NULL; +} + + +CC *fulfillmentToCC(Fulfillment_t *ffill) { + CCType *type = getTypeByAsnEnum(ffill->present); + if (!type) { + fprintf(stderr, "Unknown fulfillment type: %i\n", ffill->present); + return 0; + } + return type->fromFulfillment(ffill); +} + + +CC *cc_readFulfillmentBinary(const unsigned char *ffill_bin, size_t ffill_bin_len) { + CC *cond = 0; + unsigned char *buf = malloc(ffill_bin_len); + Fulfillment_t *ffill = 0; + asn_dec_rval_t rval = ber_decode(0, &asn_DEF_Fulfillment, (void **)&ffill, ffill_bin, ffill_bin_len); + if (rval.code != RC_OK) { + goto end; + } + // Do malleability check + asn_enc_rval_t rc = der_encode_to_buffer(&asn_DEF_Fulfillment, ffill, buf, ffill_bin_len); + if (rc.encoded == -1) { + fprintf(stderr, "FULFILLMENT NOT ENCODED\n"); + goto end; + } + if (rc.encoded != ffill_bin_len || 0 != memcmp(ffill_bin, buf, rc.encoded)) { + goto end; + } + + cond = fulfillmentToCC(ffill); +end: + free(buf); + if (ffill) ASN_STRUCT_FREE(asn_DEF_Fulfillment, ffill); + return cond; +} + + +int cc_visit(CC *cond, CCVisitor visitor) { + int out = visitor.visit(cond, visitor); + if (out && cond->type->visitChildren) { + out = cond->type->visitChildren(cond, visitor); + } + return out; +} + + +int cc_verify(const struct CC *cond, const unsigned char *msg, size_t msgLength, int doHashMsg, + const unsigned char *condBin, size_t condBinLength, + VerifyEval verifyEval, void *evalContext) { + unsigned char targetBinary[1000]; + const size_t binLength = cc_conditionBinary(cond, targetBinary); + if (0 != memcmp(condBin, targetBinary, binLength)) { + return 0; + } + + if (!cc_ed25519VerifyTree(cond, msg, msgLength)) { + return 0; + } + + unsigned char msgHash[32]; + if (doHashMsg) sha256(msg, msgLength, msgHash); + else memcpy(msgHash, msg, 32); + + if (!cc_secp256k1VerifyTreeMsg32(cond, msgHash)) { + return 0; + } + + if (!cc_verifyEval(cond, verifyEval, evalContext)) { + return 0; + } + return 1; +} + + +CC *cc_readConditionBinary(const unsigned char *cond_bin, size_t length) { + Condition_t *asnCond = 0; + asn_dec_rval_t rval; + rval = ber_decode(0, &asn_DEF_Condition, (void **)&asnCond, cond_bin, length); + if (rval.code != RC_OK) { + fprintf(stderr, "Failed reading condition binary\n"); + return NULL; + } + CC *cond = mkAnon(asnCond); + ASN_STRUCT_FREE(asn_DEF_Condition, asnCond); + return cond; +} + + +int cc_isAnon(const CC *cond) { + return cond->type->typeId == CC_Anon; +} + + +enum CCTypeId cc_typeId(const CC *cond) { + return cc_isAnon(cond) ? cond->conditionType->typeId : cond->type->typeId; +} + + +uint32_t cc_typeMask(const CC *cond) { + uint32_t mask = 1 << cc_typeId(cond); + if (cond->type->getSubtypes) + mask |= cond->type->getSubtypes(cond); + return mask; +} + + +int cc_isFulfilled(const CC *cond) { + return cond->type->isFulfilled(cond); +} + + +char *cc_typeName(const CC *cond) { + return cc_isAnon(cond) ? cond->conditionType->name : cond->type->name; +} + + +CC *cc_new(int typeId) { + CC *cond = calloc(1, sizeof(CC)); + cond->type = typeId == CC_Anon ? &CC_AnonType : CCTypeRegistry[typeId]; + return cond; +} + + +void cc_free(CC *cond) { + if (cond) + cond->type->free(cond); + free(cond); +} + + diff --git a/src/cryptoconditions/src/ed25519.c b/src/cryptoconditions/src/ed25519.c new file mode 100644 index 000000000..7ae99b226 --- /dev/null +++ b/src/cryptoconditions/src/ed25519.c @@ -0,0 +1,168 @@ +#include "asn/Condition.h" +#include "asn/Fulfillment.h" +#include "asn/Ed25519FingerprintContents.h" +#include "asn/OCTET_STRING.h" +#include "include/cJSON.h" +#include "include/ed25519/src/ed25519.h" +#include "cryptoconditions.h" + + +struct CCType CC_Ed25519Type; + + +static unsigned char *ed25519Fingerprint(const CC *cond) { + Ed25519FingerprintContents_t *fp = calloc(1, sizeof(Ed25519FingerprintContents_t)); + OCTET_STRING_fromBuf(&fp->publicKey, cond->publicKey, 32); + return hashFingerprintContents(&asn_DEF_Ed25519FingerprintContents, fp); +} + + +int ed25519Verify(CC *cond, CCVisitor visitor) { + if (cond->type->typeId != CC_Ed25519Type.typeId) return 1; + // TODO: test failure mode: empty sig / null pointer + return ed25519_verify(cond->signature, visitor.msg, visitor.msgLength, cond->publicKey); +} + + +static int cc_ed25519VerifyTree(const CC *cond, const unsigned char *msg, size_t msgLength) { + CCVisitor visitor = {&ed25519Verify, msg, msgLength, NULL}; + return cc_visit((CC*) cond, visitor); +} + + +/* + * Signing data + */ +typedef struct CCEd25519SigningData { + unsigned char *pk; + unsigned char *skpk; + int nSigned; +} CCEd25519SigningData; + + +/* + * Visitor that signs an ed25519 condition if it has a matching public key + */ +static int ed25519Sign(CC *cond, CCVisitor visitor) { + if (cond->type->typeId != CC_Ed25519Type.typeId) return 1; + CCEd25519SigningData *signing = (CCEd25519SigningData*) visitor.context; + if (0 != memcmp(cond->publicKey, signing->pk, 32)) return 1; + if (!cond->signature) cond->signature = malloc(64); + ed25519_sign(cond->signature, visitor.msg, visitor.msgLength, + signing->pk, signing->skpk); + signing->nSigned++; + return 1; +} + + +/* + * Sign ed25519 conditions in a tree + */ +int cc_signTreeEd25519(CC *cond, const unsigned char *privateKey, const unsigned char *msg, + const size_t msgLength) { + unsigned char pk[32], skpk[64]; + ed25519_create_keypair(pk, skpk, privateKey); + + CCEd25519SigningData signing = {pk, skpk, 0}; + CCVisitor visitor = {&ed25519Sign, (unsigned char*)msg, msgLength, &signing}; + cc_visit(cond, visitor); + return signing.nSigned; +} + + +static unsigned long ed25519Cost(const CC *cond) { + return 131072; +} + + +static CC *ed25519FromJSON(const cJSON *params, char *err) { + size_t binsz; + + cJSON *pk_item = cJSON_GetObjectItem(params, "publicKey"); + if (!cJSON_IsString(pk_item)) { + strcpy(err, "publicKey must be a string"); + return NULL; + } + unsigned char *pk = base64_decode(pk_item->valuestring, &binsz); + if (32 != binsz) { + strcpy(err, "publicKey has incorrect length"); + free(pk); + return NULL; + } + + cJSON *signature_item = cJSON_GetObjectItem(params, "signature"); + unsigned char *sig = NULL; + if (signature_item && !cJSON_IsNull(signature_item)) { + if (!cJSON_IsString(signature_item)) { + strcpy(err, "signature must be null or a string"); + return NULL; + } + sig = base64_decode(signature_item->valuestring, &binsz); + if (64 != binsz) { + strcpy(err, "signature has incorrect length"); + free(sig); + return NULL; + } + } + + CC *cond = cc_new(CC_Ed25519); + cond->publicKey = pk; + cond->signature = sig; + return cond; +} + + +static void ed25519ToJSON(const CC *cond, cJSON *params) { + unsigned char *b64 = base64_encode(cond->publicKey, 32); + cJSON_AddItemToObject(params, "publicKey", cJSON_CreateString(b64)); + free(b64); + if (cond->signature) { + b64 = base64_encode(cond->signature, 64); + cJSON_AddItemToObject(params, "signature", cJSON_CreateString(b64)); + free(b64); + } +} + + +static CC *ed25519FromFulfillment(const Fulfillment_t *ffill) { + CC *cond = cc_new(CC_Ed25519); + cond->publicKey = malloc(32); + memcpy(cond->publicKey, ffill->choice.ed25519Sha256.publicKey.buf, 32); + cond->signature = malloc(64); + memcpy(cond->signature, ffill->choice.ed25519Sha256.signature.buf, 64); + return cond; +} + + +static Fulfillment_t *ed25519ToFulfillment(const CC *cond) { + if (!cond->signature) { + return NULL; + } + Fulfillment_t *ffill = calloc(1, sizeof(Fulfillment_t)); + ffill->present = Fulfillment_PR_ed25519Sha256; + Ed25519Sha512Fulfillment_t *ed2 = &ffill->choice.ed25519Sha256; + OCTET_STRING_fromBuf(&ed2->publicKey, cond->publicKey, 32); + OCTET_STRING_fromBuf(&ed2->signature, cond->signature, 64); + return ffill; +} + + +int ed25519IsFulfilled(const CC *cond) { + return cond->signature > 0; +} + + +static void ed25519Free(CC *cond) { + free(cond->publicKey); + if (cond->signature) { + free(cond->signature); + } +} + + +static uint32_t ed25519Subtypes(const CC *cond) { + return 0; +} + + +struct CCType CC_Ed25519Type = { 4, "ed25519-sha-256", Condition_PR_ed25519Sha256, 0, &ed25519Fingerprint, &ed25519Cost, &ed25519Subtypes, &ed25519FromJSON, &ed25519ToJSON, &ed25519FromFulfillment, &ed25519ToFulfillment, &ed25519IsFulfilled, &ed25519Free }; diff --git a/src/cryptoconditions/src/eval.c b/src/cryptoconditions/src/eval.c new file mode 100644 index 000000000..4a7d13276 --- /dev/null +++ b/src/cryptoconditions/src/eval.c @@ -0,0 +1,119 @@ +#include "asn/Condition.h" +#include "asn/Fulfillment.h" +#include "asn/EvalFulfillment.h" +#include "asn/OCTET_STRING.h" +#include "cryptoconditions.h" +#include "internal.h" +#include "include/cJSON.h" + + +struct CCType CC_EvalType; + + +static unsigned char *evalFingerprint(const CC *cond) { + unsigned char *hash = calloc(1, 32); + sha256(cond->code, cond->codeLength, hash); + return hash; +} + + +static unsigned long evalCost(const CC *cond) { + return 1048576; // Pretty high +} + + +static CC *evalFromJSON(const cJSON *params, char *err) { + size_t codeLength; + unsigned char *code = 0; + + if (!jsonGetBase64(params, "code", err, &code, &codeLength)) { + return NULL; + } + + CC *cond = cc_new(CC_Eval); + cond->code = code; + cond->codeLength = codeLength; + return cond; +} + + +static void evalToJSON(const CC *cond, cJSON *code) { + + // add code + unsigned char *b64 = base64_encode(cond->code, cond->codeLength); + cJSON_AddItemToObject(code, "code", cJSON_CreateString(b64)); + free(b64); +} + + +static CC *evalFromFulfillment(const Fulfillment_t *ffill) { + CC *cond = cc_new(CC_Eval); + + EvalFulfillment_t *eval = &ffill->choice.evalSha256; + + OCTET_STRING_t octets = eval->code; + cond->codeLength = octets.size; + cond->code = malloc(octets.size); + memcpy(cond->code, octets.buf, octets.size); + + return cond; +} + + +static Fulfillment_t *evalToFulfillment(const CC *cond) { + Fulfillment_t *ffill = calloc(1, sizeof(Fulfillment_t)); + ffill->present = Fulfillment_PR_evalSha256; + EvalFulfillment_t *eval = &ffill->choice.evalSha256; + OCTET_STRING_fromBuf(&eval->code, cond->code, cond->codeLength); + return ffill; +} + + +int evalIsFulfilled(const CC *cond) { + return 1; +} + + +static void evalFree(CC *cond) { + free(cond->code); +} + + +static uint32_t evalSubtypes(const CC *cond) { + return 0; +} + + +/* + * The JSON api doesn't contain custom verifiers, so a stub method is provided suitable for testing + */ +int jsonVerifyEval(CC *cond, void *context) { + if (cond->codeLength == 5 && 0 == memcmp(cond->code, "TEST", 4)) { + return cond->code[5]; + } + fprintf(stderr, "Cannot verify eval; user function unknown\n"); + return 0; +} + + +typedef struct CCEvalVerifyData { + VerifyEval verify; + void *context; +} CCEvalVerifyData; + + +int evalVisit(CC *cond, CCVisitor visitor) { + if (cond->type->typeId != CC_Eval) return 1; + CCEvalVerifyData *evalData = visitor.context; + return evalData->verify(cond, evalData->context); +} + + +int cc_verifyEval(const CC *cond, VerifyEval verify, void *context) { + CCEvalVerifyData evalData = {verify, context}; + CCVisitor visitor = {&evalVisit, "", 0, &evalData}; + return cc_visit(cond, visitor); +} + + +struct CCType CC_EvalType = { 15, "eval-sha-256", Condition_PR_evalSha256, 0, &evalFingerprint, &evalCost, &evalSubtypes, &evalFromJSON, &evalToJSON, &evalFromFulfillment, &evalToFulfillment, &evalIsFulfilled, &evalFree }; diff --git a/src/cryptoconditions/src/include/cJSON.c b/src/cryptoconditions/src/include/cJSON.c new file mode 100644 index 000000000..306bb5b0d --- /dev/null +++ b/src/cryptoconditions/src/include/cJSON.c @@ -0,0 +1,2699 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + 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. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" + +/* define our own boolean type */ +#define true ((cJSON_bool)1) +#define false ((cJSON_bool)0) + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) +{ + return (const char*) (global_error.json + global_error.position); +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 5) || (CJSON_VERSION_PATCH != 9) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) +{ + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks +{ + void *(*allocate)(size_t size); + void (*deallocate)(void *pointer); + void *(*reallocate)(void *pointer, size_t size); +} internal_hooks; + +static internal_hooks global_hooks = { malloc, free, realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + if (!(copy = (unsigned char*)hooks->allocate(length))) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +{ + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; +} + +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +#define cannot_read(buffer, size) (!can_read(buffer, size)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) +{ + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (number <= INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +typedef struct +{ + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) + { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) + { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else + { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + if (newbuffer) + { + memcpy(newbuffer, p->buffer, p->offset + 1); + } + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26]; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if ((d * 0) != 0) + { + length = sprintf((char*)number_buffer, "null"); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d)) + { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char*)number_buffer, "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occured */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) +{ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +{ + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) + { + return false; + } + + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = strlen((const char*)value) + sizeof(""); + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(&buffer))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +#define cjson_min(a, b) ((a < b) ? a : b) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(256); + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->length); + buffer->buffer = NULL; + if (printed == NULL) { + goto fail; + } + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + } + + if (printed != NULL) + { + hooks->deallocate(printed); + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if ((len < 0) || (buf == NULL)) + { + return false; + } + + p.buffer = (unsigned char*)buf; + p.length = (size_t)len; + p.offset = 0; + p.noalloc = true; + p.format = fmt; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) + { + return false; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + if (!output_buffer->noalloc) + { + output_buffer->hooks.deallocate(output_buffer->buffer); + } + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { + return false; + } + update_offset(output_buffer); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* faile to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) + { + if (output_buffer->format) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = (size_t) ((output_buffer->format ? 1 : 0) + (current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) + { + return 0; + } + + child = array->child; + + while(child != NULL) + { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) +{ + cJSON *reference = NULL; + if (item == NULL) + { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) + { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL)) + { + return; + } + + child = array->child; + + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + } + else + { + /* append to the end */ + while (child->next) + { + child = child->next; + } + suffix_object(child, item); + } +} + +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + if (item == NULL) + { + return; + } + + /* call cJSON_AddItemToObjectCS for code reuse */ + cJSON_AddItemToObjectCS(object, (char*)cJSON_strdup((const unsigned char*)string, &global_hooks), item); + /* remove cJSON_StringIsConst flag */ + item->type &= ~cJSON_StringIsConst; +} + +#if defined (__clang__) || ((__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + if ((item == NULL) || (string == NULL)) + { + return; + } + if (!(item->type & cJSON_StringIsConst) && item->string) + { + global_hooks.deallocate(item->string); + } + item->string = (char*)string; + item->type |= cJSON_StringIsConst; + cJSON_AddItemToArray(object, item); +} +#if defined (__clang__) || ((__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) + { + return; + } + + cJSON_AddItemToArray(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { + return; + } + + cJSON_AddItemToObject(object, string, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL)) + { + return NULL; + } + + if (item->prev != NULL) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0) + { + return; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + cJSON_AddItemToArray(array, newitem); + return; + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (replacement == NULL) || (item == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (parent->child == item) + { + parent->child = replacement; + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return; + } + + cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = b ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0;a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + unsigned char *into = (unsigned char*)json; + + if (json == NULL) + { + return; + } + + while (*json) + { + if (*json == ' ') + { + json++; + } + else if (*json == '\t') + { + /* Whitespace characters. */ + json++; + } + else if (*json == '\r') + { + json++; + } + else if (*json=='\n') + { + json++; + } + else if ((*json == '/') && (json[1] == '/')) + { + /* double-slash comments, to end of line. */ + while (*json && (*json != '\n')) + { + json++; + } + } + else if ((*json == '/') && (json[1] == '*')) + { + /* multiline comments. */ + while (*json && !((*json == '*') && (json[1] == '/'))) + { + json++; + } + json += 2; + } + else if (*json == '\"') + { + /* string literals, which are \" sensitive. */ + *into++ = (unsigned char)*json++; + while (*json && (*json != '\"')) + { + if (*json == '\\') + { + *into++ = (unsigned char)*json++; + } + *into++ = (unsigned char)*json++; + } + *into++ = (unsigned char)*json++; + } + else + { + /* All other characters. */ + *into++ = (unsigned char)*json++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (a->valuedouble == b->valuedouble) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); +} diff --git a/src/cryptoconditions/src/include/cJSON.h b/src/cryptoconditions/src/include/cJSON.h new file mode 100644 index 000000000..1e388137e --- /dev/null +++ b/src/cryptoconditions/src/include/cJSON.h @@ -0,0 +1,263 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + 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. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 5 +#define CJSON_VERSION_PATCH 9 + +#include + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; +} cJSON; + +typedef struct cJSON_Hooks +{ + void *(*malloc_fn)(size_t sz); + void (*free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 2 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type __stdcall +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type __stdcall +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type __stdcall +#endif +#else /* !WIN32 */ +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* These utilities create an Array of count items. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will +need to be released. With recurse!=0, it will duplicate any children connected to the item. +The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); + + +CJSON_PUBLIC(void) cJSON_Minify(char *json); + +/* Macros for creating things quickly. */ +#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) +#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) +#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) +#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) +#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) +#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) +#define cJSON_AddRawToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateRaw(s)) + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/cryptoconditions/src/include/ed25519/license.txt b/src/cryptoconditions/src/include/ed25519/license.txt new file mode 100644 index 000000000..c1503f912 --- /dev/null +++ b/src/cryptoconditions/src/include/ed25519/license.txt @@ -0,0 +1,16 @@ +Copyright (c) 2015 Orson Peters + +This software is provided 'as-is', without any express or implied warranty. In no event will the +authors be held liable for any damages arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, including commercial +applications, and to alter it and redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the + original software. If you use this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be misrepresented as + being the original software. + +3. This notice may not be removed or altered from any source distribution. diff --git a/src/cryptoconditions/src/include/ed25519/readme.md b/src/cryptoconditions/src/include/ed25519/readme.md new file mode 100644 index 000000000..89329bbe4 --- /dev/null +++ b/src/cryptoconditions/src/include/ed25519/readme.md @@ -0,0 +1,166 @@ +Ed25519 +======= + +This is a portable implementation of [Ed25519](http://ed25519.cr.yp.to/) based +on the SUPERCOP "ref10" implementation. Additionally there is key exchanging +and scalar addition included to further aid building a PKI using Ed25519. All +code is licensed under the permissive zlib license. + +All code is pure ANSI C without any dependencies, except for the random seed +generation which uses standard OS cryptography APIs (`CryptGenRandom` on +Windows, `/dev/urandom` on nix). If you wish to be entirely portable define +`ED25519_NO_SEED`. This disables the `ed25519_create_seed` function, so if your +application requires key generation you must supply your own seeding function +(which is simply a 256 bit (32 byte) cryptographic random number generator). + + +Performance +----------- + +On a Windows machine with an Intel Pentium B970 @ 2.3GHz I got the following +speeds (running on only one a single core): + + Seed generation: 64us (15625 per second) + Key generation: 88us (11364 per second) + Message signing (short message): 87us (11494 per second) + Message verifying (short message): 228us (4386 per second) + Scalar addition: 100us (10000 per second) + Key exchange: 220us (4545 per second) + +The speeds on other machines may vary. Sign/verify times will be higher with +longer messages. The implementation significantly benefits from 64 bit +architectures, if possible compile as 64 bit. + + +Usage +----- + +Simply add all .c and .h files in the `src/` folder to your project and include +`ed25519.h` in any file you want to use the API. If you prefer to use a shared +library, only copy `ed25519.h` and define `ED25519_DLL` before importing. A +windows DLL is pre-built. + +There are no defined types for seeds, private keys, public keys, shared secrets +or signatures. Instead simple `unsigned char` buffers are used with the +following sizes: + +```c +unsigned char seed[32]; +unsigned char signature[64]; +unsigned char public_key[32]; +unsigned char private_key[64]; +unsigned char scalar[32]; +unsigned char shared_secret[32]; +``` + +API +--- + +```c +int ed25519_create_seed(unsigned char *seed); +``` + +Creates a 32 byte random seed in `seed` for key generation. `seed` must be a +writable 32 byte buffer. Returns 0 on success, and nonzero on failure. + +```c +void ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, + const unsigned char *seed); +``` + +Creates a new key pair from the given seed. `public_key` must be a writable 32 +byte buffer, `private_key` must be a writable 64 byte buffer and `seed` must be +a 32 byte buffer. + +```c +void ed25519_sign(unsigned char *signature, + const unsigned char *message, size_t message_len, + const unsigned char *public_key, const unsigned char *private_key); +``` + +Creates a signature of the given message with the given key pair. `signature` +must be a writable 64 byte buffer. `message` must have at least `message_len` +bytes to be read. + +```c +int ed25519_verify(const unsigned char *signature, + const unsigned char *message, size_t message_len, + const unsigned char *public_key); +``` + +Verifies the signature on the given message using `public_key`. `signature` +must be a readable 64 byte buffer. `message` must have at least `message_len` +bytes to be read. Returns 1 if the signature matches, 0 otherwise. + +```c +void ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, + const unsigned char *scalar); +``` + +Adds `scalar` to the given key pair where scalar is a 32 byte buffer (possibly +generated with `ed25519_create_seed`), generating a new key pair. You can +calculate the public key sum without knowing the private key and vice versa by +passing in `NULL` for the key you don't know. This is useful for enforcing +randomness on a key pair by a third party while only knowing the public key, +among other things. Warning: the last bit of the scalar is ignored - if +comparing scalars make sure to clear it with `scalar[31] &= 127`. + + +```c +void ed25519_key_exchange(unsigned char *shared_secret, + const unsigned char *public_key, const unsigned char *private_key); +``` + +Performs a key exchange on the given public key and private key, producing a +shared secret. It is recommended to hash the shared secret before using it. +`shared_secret` must be a 32 byte writable buffer where the shared secret will +be stored. + +Example +------- + +```c +unsigned char seed[32], public_key[32], private_key[64], signature[64]; +unsigned char other_public_key[32], other_private_key[64], shared_secret[32]; +const unsigned char message[] = "TEST MESSAGE"; + +/* create a random seed, and a key pair out of that seed */ +if (ed25519_create_seed(seed)) { + printf("error while generating seed\n"); + exit(1); +} + +ed25519_create_keypair(public_key, private_key, seed); + +/* create signature on the message with the key pair */ +ed25519_sign(signature, message, strlen(message), public_key, private_key); + +/* verify the signature */ +if (ed25519_verify(signature, message, strlen(message), public_key)) { + printf("valid signature\n"); +} else { + printf("invalid signature\n"); +} + +/* create a dummy keypair to use for a key exchange, normally you'd only have +the public key and receive it through some communication channel */ +if (ed25519_create_seed(seed)) { + printf("error while generating seed\n"); + exit(1); +} + +ed25519_create_keypair(other_public_key, other_private_key, seed); + +/* do a key exchange with other_public_key */ +ed25519_key_exchange(shared_secret, other_public_key, private_key); + +/* + the magic here is that ed25519_key_exchange(shared_secret, public_key, + other_private_key); would result in the same shared_secret +*/ + +``` + +License +------- +All code is released under the zlib license. See license.txt for details. diff --git a/src/cryptoconditions/src/include/ed25519/src/add_scalar.c b/src/cryptoconditions/src/include/ed25519/src/add_scalar.c new file mode 100644 index 000000000..7528a7a4a --- /dev/null +++ b/src/cryptoconditions/src/include/ed25519/src/add_scalar.c @@ -0,0 +1,69 @@ +#include "ed25519.h" +#include "ge.h" +#include "sc.h" +#include "sha512.h" + + +/* see http://crypto.stackexchange.com/a/6215/4697 */ +void ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar) { + const unsigned char SC_1[32] = {1}; /* scalar with value 1 */ + + unsigned char n[32]; + ge_p3 nB; + ge_p1p1 A_p1p1; + ge_p3 A; + ge_p3 public_key_unpacked; + ge_cached T; + + sha512_context hash; + unsigned char hashbuf[64]; + + int i; + + /* copy the scalar and clear highest bit */ + for (i = 0; i < 31; ++i) { + n[i] = scalar[i]; + } + n[31] = scalar[31] & 127; + + /* private key: a = n + t */ + if (private_key) { + sc_muladd(private_key, SC_1, n, private_key); + + // https://github.com/orlp/ed25519/issues/3 + sha512_init(&hash); + sha512_update(&hash, private_key + 32, 32); + sha512_update(&hash, scalar, 32); + sha512_final(&hash, hashbuf); + for (i = 0; i < 32; ++i) { + private_key[32 + i] = hashbuf[i]; + } + } + + /* public key: A = nB + T */ + if (public_key) { + /* if we know the private key we don't need a point addition, which is faster */ + /* using a "timing attack" you could find out wether or not we know the private + key, but this information seems rather useless - if this is important pass + public_key and private_key seperately in 2 function calls */ + if (private_key) { + ge_scalarmult_base(&A, private_key); + } else { + /* unpack public key into T */ + ge_frombytes_negate_vartime(&public_key_unpacked, public_key); + fe_neg(public_key_unpacked.X, public_key_unpacked.X); /* undo negate */ + fe_neg(public_key_unpacked.T, public_key_unpacked.T); /* undo negate */ + ge_p3_to_cached(&T, &public_key_unpacked); + + /* calculate n*B */ + ge_scalarmult_base(&nB, n); + + /* A = n*B + T */ + ge_add(&A_p1p1, &nB, &T); + ge_p1p1_to_p3(&A, &A_p1p1); + } + + /* pack public key */ + ge_p3_tobytes(public_key, &A); + } +} diff --git a/src/cryptoconditions/src/include/ed25519/src/ed25519.h b/src/cryptoconditions/src/include/ed25519/src/ed25519.h new file mode 100644 index 000000000..8924659fa --- /dev/null +++ b/src/cryptoconditions/src/include/ed25519/src/ed25519.h @@ -0,0 +1,38 @@ +#ifndef ED25519_H +#define ED25519_H + +#include + +#if defined(_WIN32) + #if defined(ED25519_BUILD_DLL) + #define ED25519_DECLSPEC __declspec(dllexport) + #elif defined(ED25519_DLL) + #define ED25519_DECLSPEC __declspec(dllimport) + #else + #define ED25519_DECLSPEC + #endif +#else + #define ED25519_DECLSPEC +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ED25519_NO_SEED +int ED25519_DECLSPEC ed25519_create_seed(unsigned char *seed); +#endif + +void ED25519_DECLSPEC ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed); +void ED25519_DECLSPEC ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key); +int ED25519_DECLSPEC ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key); +void ED25519_DECLSPEC ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar); +void ED25519_DECLSPEC ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/cryptoconditions/src/include/ed25519/src/fe.c b/src/cryptoconditions/src/include/ed25519/src/fe.c new file mode 100644 index 000000000..2105eb7b2 --- /dev/null +++ b/src/cryptoconditions/src/include/ed25519/src/fe.c @@ -0,0 +1,1491 @@ +#include "fixedint.h" +#include "fe.h" + + +/* + helper functions +*/ +static uint64_t load_3(const unsigned char *in) { + uint64_t result; + + result = (uint64_t) in[0]; + result |= ((uint64_t) in[1]) << 8; + result |= ((uint64_t) in[2]) << 16; + + return result; +} + +static uint64_t load_4(const unsigned char *in) { + uint64_t result; + + result = (uint64_t) in[0]; + result |= ((uint64_t) in[1]) << 8; + result |= ((uint64_t) in[2]) << 16; + result |= ((uint64_t) in[3]) << 24; + + return result; +} + + + +/* + h = 0 +*/ + +void fe_0(fe h) { + h[0] = 0; + h[1] = 0; + h[2] = 0; + h[3] = 0; + h[4] = 0; + h[5] = 0; + h[6] = 0; + h[7] = 0; + h[8] = 0; + h[9] = 0; +} + + + +/* + h = 1 +*/ + +void fe_1(fe h) { + h[0] = 1; + h[1] = 0; + h[2] = 0; + h[3] = 0; + h[4] = 0; + h[5] = 0; + h[6] = 0; + h[7] = 0; + h[8] = 0; + h[9] = 0; +} + + + +/* + h = f + g + Can overlap h with f or g. + + Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + + Postconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +void fe_add(fe h, const fe f, const fe g) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t h0 = f0 + g0; + int32_t h1 = f1 + g1; + int32_t h2 = f2 + g2; + int32_t h3 = f3 + g3; + int32_t h4 = f4 + g4; + int32_t h5 = f5 + g5; + int32_t h6 = f6 + g6; + int32_t h7 = f7 + g7; + int32_t h8 = f8 + g8; + int32_t h9 = f9 + g9; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} + + + +/* + Replace (f,g) with (g,g) if b == 1; + replace (f,g) with (f,g) if b == 0. + + Preconditions: b in {0,1}. +*/ + +void fe_cmov(fe f, const fe g, unsigned int b) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t x0 = f0 ^ g0; + int32_t x1 = f1 ^ g1; + int32_t x2 = f2 ^ g2; + int32_t x3 = f3 ^ g3; + int32_t x4 = f4 ^ g4; + int32_t x5 = f5 ^ g5; + int32_t x6 = f6 ^ g6; + int32_t x7 = f7 ^ g7; + int32_t x8 = f8 ^ g8; + int32_t x9 = f9 ^ g9; + + b = (unsigned int) (- (int) b); /* silence warning */ + x0 &= b; + x1 &= b; + x2 &= b; + x3 &= b; + x4 &= b; + x5 &= b; + x6 &= b; + x7 &= b; + x8 &= b; + x9 &= b; + + f[0] = f0 ^ x0; + f[1] = f1 ^ x1; + f[2] = f2 ^ x2; + f[3] = f3 ^ x3; + f[4] = f4 ^ x4; + f[5] = f5 ^ x5; + f[6] = f6 ^ x6; + f[7] = f7 ^ x7; + f[8] = f8 ^ x8; + f[9] = f9 ^ x9; +} + +/* + Replace (f,g) with (g,f) if b == 1; + replace (f,g) with (f,g) if b == 0. + + Preconditions: b in {0,1}. +*/ + +void fe_cswap(fe f,fe g,unsigned int b) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t x0 = f0 ^ g0; + int32_t x1 = f1 ^ g1; + int32_t x2 = f2 ^ g2; + int32_t x3 = f3 ^ g3; + int32_t x4 = f4 ^ g4; + int32_t x5 = f5 ^ g5; + int32_t x6 = f6 ^ g6; + int32_t x7 = f7 ^ g7; + int32_t x8 = f8 ^ g8; + int32_t x9 = f9 ^ g9; + b = (unsigned int) (- (int) b); /* silence warning */ + x0 &= b; + x1 &= b; + x2 &= b; + x3 &= b; + x4 &= b; + x5 &= b; + x6 &= b; + x7 &= b; + x8 &= b; + x9 &= b; + f[0] = f0 ^ x0; + f[1] = f1 ^ x1; + f[2] = f2 ^ x2; + f[3] = f3 ^ x3; + f[4] = f4 ^ x4; + f[5] = f5 ^ x5; + f[6] = f6 ^ x6; + f[7] = f7 ^ x7; + f[8] = f8 ^ x8; + f[9] = f9 ^ x9; + g[0] = g0 ^ x0; + g[1] = g1 ^ x1; + g[2] = g2 ^ x2; + g[3] = g3 ^ x3; + g[4] = g4 ^ x4; + g[5] = g5 ^ x5; + g[6] = g6 ^ x6; + g[7] = g7 ^ x7; + g[8] = g8 ^ x8; + g[9] = g9 ^ x9; +} + + + +/* + h = f +*/ + +void fe_copy(fe h, const fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + + h[0] = f0; + h[1] = f1; + h[2] = f2; + h[3] = f3; + h[4] = f4; + h[5] = f5; + h[6] = f6; + h[7] = f7; + h[8] = f8; + h[9] = f9; +} + + + +/* + Ignores top bit of h. +*/ + +void fe_frombytes(fe h, const unsigned char *s) { + int64_t h0 = load_4(s); + int64_t h1 = load_3(s + 4) << 6; + int64_t h2 = load_3(s + 7) << 5; + int64_t h3 = load_3(s + 10) << 3; + int64_t h4 = load_3(s + 13) << 2; + int64_t h5 = load_4(s + 16); + int64_t h6 = load_3(s + 20) << 7; + int64_t h7 = load_3(s + 23) << 5; + int64_t h8 = load_3(s + 26) << 4; + int64_t h9 = (load_3(s + 29) & 8388607) << 2; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + + carry9 = (h9 + (int64_t) (1 << 24)) >> 25; + h0 += carry9 * 19; + h9 -= carry9 << 25; + carry1 = (h1 + (int64_t) (1 << 24)) >> 25; + h2 += carry1; + h1 -= carry1 << 25; + carry3 = (h3 + (int64_t) (1 << 24)) >> 25; + h4 += carry3; + h3 -= carry3 << 25; + carry5 = (h5 + (int64_t) (1 << 24)) >> 25; + h6 += carry5; + h5 -= carry5 << 25; + carry7 = (h7 + (int64_t) (1 << 24)) >> 25; + h8 += carry7; + h7 -= carry7 << 25; + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + carry2 = (h2 + (int64_t) (1 << 25)) >> 26; + h3 += carry2; + h2 -= carry2 << 26; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry6 = (h6 + (int64_t) (1 << 25)) >> 26; + h7 += carry6; + h6 -= carry6 << 26; + carry8 = (h8 + (int64_t) (1 << 25)) >> 26; + h9 += carry8; + h8 -= carry8 << 26; + + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + + + +void fe_invert(fe out, const fe z) { + fe t0; + fe t1; + fe t2; + fe t3; + int i; + + fe_sq(t0, z); + + for (i = 1; i < 1; ++i) { + fe_sq(t0, t0); + } + + fe_sq(t1, t0); + + for (i = 1; i < 2; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t1, z, t1); + fe_mul(t0, t0, t1); + fe_sq(t2, t0); + + for (i = 1; i < 1; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t1, t2); + fe_sq(t2, t1); + + for (i = 1; i < 5; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t2, t1); + fe_sq(t2, t1); + + for (i = 1; i < 10; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t2, t2, t1); + fe_sq(t3, t2); + + for (i = 1; i < 20; ++i) { + fe_sq(t3, t3); + } + + fe_mul(t2, t3, t2); + fe_sq(t2, t2); + + for (i = 1; i < 10; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t2, t1); + fe_sq(t2, t1); + + for (i = 1; i < 50; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t2, t2, t1); + fe_sq(t3, t2); + + for (i = 1; i < 100; ++i) { + fe_sq(t3, t3); + } + + fe_mul(t2, t3, t2); + fe_sq(t2, t2); + + for (i = 1; i < 50; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t2, t1); + fe_sq(t1, t1); + + for (i = 1; i < 5; ++i) { + fe_sq(t1, t1); + } + + fe_mul(out, t1, t0); +} + + + +/* + return 1 if f is in {1,3,5,...,q-2} + return 0 if f is in {0,2,4,...,q-1} + + Preconditions: + |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +int fe_isnegative(const fe f) { + unsigned char s[32]; + + fe_tobytes(s, f); + + return s[0] & 1; +} + + + +/* + return 1 if f == 0 + return 0 if f != 0 + + Preconditions: + |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +int fe_isnonzero(const fe f) { + unsigned char s[32]; + unsigned char r; + + fe_tobytes(s, f); + + r = s[0]; + #define F(i) r |= s[i] + F(1); + F(2); + F(3); + F(4); + F(5); + F(6); + F(7); + F(8); + F(9); + F(10); + F(11); + F(12); + F(13); + F(14); + F(15); + F(16); + F(17); + F(18); + F(19); + F(20); + F(21); + F(22); + F(23); + F(24); + F(25); + F(26); + F(27); + F(28); + F(29); + F(30); + F(31); + #undef F + + return r != 0; +} + + + +/* + h = f * g + Can overlap h with f or g. + + Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + |g| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + + Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. + */ + + /* + Notes on implementation strategy: + + Using schoolbook multiplication. + Karatsuba would save a little in some cost models. + + Most multiplications by 2 and 19 are 32-bit precomputations; + cheaper than 64-bit postcomputations. + + There is one remaining multiplication by 19 in the carry chain; + one *19 precomputation can be merged into this, + but the resulting data flow is considerably less clean. + + There are 12 carries below. + 10 of them are 2-way parallelizable and vectorizable. + Can get away with 11 carries, but then data flow is much deeper. + + With tighter constraints on inputs can squeeze carries into int32. +*/ + +void fe_mul(fe h, const fe f, const fe g) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t g1_19 = 19 * g1; /* 1.959375*2^29 */ + int32_t g2_19 = 19 * g2; /* 1.959375*2^30; still ok */ + int32_t g3_19 = 19 * g3; + int32_t g4_19 = 19 * g4; + int32_t g5_19 = 19 * g5; + int32_t g6_19 = 19 * g6; + int32_t g7_19 = 19 * g7; + int32_t g8_19 = 19 * g8; + int32_t g9_19 = 19 * g9; + int32_t f1_2 = 2 * f1; + int32_t f3_2 = 2 * f3; + int32_t f5_2 = 2 * f5; + int32_t f7_2 = 2 * f7; + int32_t f9_2 = 2 * f9; + int64_t f0g0 = f0 * (int64_t) g0; + int64_t f0g1 = f0 * (int64_t) g1; + int64_t f0g2 = f0 * (int64_t) g2; + int64_t f0g3 = f0 * (int64_t) g3; + int64_t f0g4 = f0 * (int64_t) g4; + int64_t f0g5 = f0 * (int64_t) g5; + int64_t f0g6 = f0 * (int64_t) g6; + int64_t f0g7 = f0 * (int64_t) g7; + int64_t f0g8 = f0 * (int64_t) g8; + int64_t f0g9 = f0 * (int64_t) g9; + int64_t f1g0 = f1 * (int64_t) g0; + int64_t f1g1_2 = f1_2 * (int64_t) g1; + int64_t f1g2 = f1 * (int64_t) g2; + int64_t f1g3_2 = f1_2 * (int64_t) g3; + int64_t f1g4 = f1 * (int64_t) g4; + int64_t f1g5_2 = f1_2 * (int64_t) g5; + int64_t f1g6 = f1 * (int64_t) g6; + int64_t f1g7_2 = f1_2 * (int64_t) g7; + int64_t f1g8 = f1 * (int64_t) g8; + int64_t f1g9_38 = f1_2 * (int64_t) g9_19; + int64_t f2g0 = f2 * (int64_t) g0; + int64_t f2g1 = f2 * (int64_t) g1; + int64_t f2g2 = f2 * (int64_t) g2; + int64_t f2g3 = f2 * (int64_t) g3; + int64_t f2g4 = f2 * (int64_t) g4; + int64_t f2g5 = f2 * (int64_t) g5; + int64_t f2g6 = f2 * (int64_t) g6; + int64_t f2g7 = f2 * (int64_t) g7; + int64_t f2g8_19 = f2 * (int64_t) g8_19; + int64_t f2g9_19 = f2 * (int64_t) g9_19; + int64_t f3g0 = f3 * (int64_t) g0; + int64_t f3g1_2 = f3_2 * (int64_t) g1; + int64_t f3g2 = f3 * (int64_t) g2; + int64_t f3g3_2 = f3_2 * (int64_t) g3; + int64_t f3g4 = f3 * (int64_t) g4; + int64_t f3g5_2 = f3_2 * (int64_t) g5; + int64_t f3g6 = f3 * (int64_t) g6; + int64_t f3g7_38 = f3_2 * (int64_t) g7_19; + int64_t f3g8_19 = f3 * (int64_t) g8_19; + int64_t f3g9_38 = f3_2 * (int64_t) g9_19; + int64_t f4g0 = f4 * (int64_t) g0; + int64_t f4g1 = f4 * (int64_t) g1; + int64_t f4g2 = f4 * (int64_t) g2; + int64_t f4g3 = f4 * (int64_t) g3; + int64_t f4g4 = f4 * (int64_t) g4; + int64_t f4g5 = f4 * (int64_t) g5; + int64_t f4g6_19 = f4 * (int64_t) g6_19; + int64_t f4g7_19 = f4 * (int64_t) g7_19; + int64_t f4g8_19 = f4 * (int64_t) g8_19; + int64_t f4g9_19 = f4 * (int64_t) g9_19; + int64_t f5g0 = f5 * (int64_t) g0; + int64_t f5g1_2 = f5_2 * (int64_t) g1; + int64_t f5g2 = f5 * (int64_t) g2; + int64_t f5g3_2 = f5_2 * (int64_t) g3; + int64_t f5g4 = f5 * (int64_t) g4; + int64_t f5g5_38 = f5_2 * (int64_t) g5_19; + int64_t f5g6_19 = f5 * (int64_t) g6_19; + int64_t f5g7_38 = f5_2 * (int64_t) g7_19; + int64_t f5g8_19 = f5 * (int64_t) g8_19; + int64_t f5g9_38 = f5_2 * (int64_t) g9_19; + int64_t f6g0 = f6 * (int64_t) g0; + int64_t f6g1 = f6 * (int64_t) g1; + int64_t f6g2 = f6 * (int64_t) g2; + int64_t f6g3 = f6 * (int64_t) g3; + int64_t f6g4_19 = f6 * (int64_t) g4_19; + int64_t f6g5_19 = f6 * (int64_t) g5_19; + int64_t f6g6_19 = f6 * (int64_t) g6_19; + int64_t f6g7_19 = f6 * (int64_t) g7_19; + int64_t f6g8_19 = f6 * (int64_t) g8_19; + int64_t f6g9_19 = f6 * (int64_t) g9_19; + int64_t f7g0 = f7 * (int64_t) g0; + int64_t f7g1_2 = f7_2 * (int64_t) g1; + int64_t f7g2 = f7 * (int64_t) g2; + int64_t f7g3_38 = f7_2 * (int64_t) g3_19; + int64_t f7g4_19 = f7 * (int64_t) g4_19; + int64_t f7g5_38 = f7_2 * (int64_t) g5_19; + int64_t f7g6_19 = f7 * (int64_t) g6_19; + int64_t f7g7_38 = f7_2 * (int64_t) g7_19; + int64_t f7g8_19 = f7 * (int64_t) g8_19; + int64_t f7g9_38 = f7_2 * (int64_t) g9_19; + int64_t f8g0 = f8 * (int64_t) g0; + int64_t f8g1 = f8 * (int64_t) g1; + int64_t f8g2_19 = f8 * (int64_t) g2_19; + int64_t f8g3_19 = f8 * (int64_t) g3_19; + int64_t f8g4_19 = f8 * (int64_t) g4_19; + int64_t f8g5_19 = f8 * (int64_t) g5_19; + int64_t f8g6_19 = f8 * (int64_t) g6_19; + int64_t f8g7_19 = f8 * (int64_t) g7_19; + int64_t f8g8_19 = f8 * (int64_t) g8_19; + int64_t f8g9_19 = f8 * (int64_t) g9_19; + int64_t f9g0 = f9 * (int64_t) g0; + int64_t f9g1_38 = f9_2 * (int64_t) g1_19; + int64_t f9g2_19 = f9 * (int64_t) g2_19; + int64_t f9g3_38 = f9_2 * (int64_t) g3_19; + int64_t f9g4_19 = f9 * (int64_t) g4_19; + int64_t f9g5_38 = f9_2 * (int64_t) g5_19; + int64_t f9g6_19 = f9 * (int64_t) g6_19; + int64_t f9g7_38 = f9_2 * (int64_t) g7_19; + int64_t f9g8_19 = f9 * (int64_t) g8_19; + int64_t f9g9_38 = f9_2 * (int64_t) g9_19; + int64_t h0 = f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38; + int64_t h1 = f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19; + int64_t h2 = f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38; + int64_t h3 = f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19; + int64_t h4 = f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38; + int64_t h5 = f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19; + int64_t h6 = f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38; + int64_t h7 = f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19; + int64_t h8 = f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38; + int64_t h9 = f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0 ; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + + carry1 = (h1 + (int64_t) (1 << 24)) >> 25; + h2 += carry1; + h1 -= carry1 << 25; + carry5 = (h5 + (int64_t) (1 << 24)) >> 25; + h6 += carry5; + h5 -= carry5 << 25; + + carry2 = (h2 + (int64_t) (1 << 25)) >> 26; + h3 += carry2; + h2 -= carry2 << 26; + carry6 = (h6 + (int64_t) (1 << 25)) >> 26; + h7 += carry6; + h6 -= carry6 << 26; + + carry3 = (h3 + (int64_t) (1 << 24)) >> 25; + h4 += carry3; + h3 -= carry3 << 25; + carry7 = (h7 + (int64_t) (1 << 24)) >> 25; + h8 += carry7; + h7 -= carry7 << 25; + + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry8 = (h8 + (int64_t) (1 << 25)) >> 26; + h9 += carry8; + h8 -= carry8 << 26; + + carry9 = (h9 + (int64_t) (1 << 24)) >> 25; + h0 += carry9 * 19; + h9 -= carry9 << 25; + + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + + +/* +h = f * 121666 +Can overlap h with f. + +Preconditions: + |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. + +Postconditions: + |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +*/ + +void fe_mul121666(fe h, fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int64_t h0 = f0 * (int64_t) 121666; + int64_t h1 = f1 * (int64_t) 121666; + int64_t h2 = f2 * (int64_t) 121666; + int64_t h3 = f3 * (int64_t) 121666; + int64_t h4 = f4 * (int64_t) 121666; + int64_t h5 = f5 * (int64_t) 121666; + int64_t h6 = f6 * (int64_t) 121666; + int64_t h7 = f7 * (int64_t) 121666; + int64_t h8 = f8 * (int64_t) 121666; + int64_t h9 = f9 * (int64_t) 121666; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + + carry9 = (h9 + (int64_t) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + carry1 = (h1 + (int64_t) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry3 = (h3 + (int64_t) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry5 = (h5 + (int64_t) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + carry7 = (h7 + (int64_t) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry0 = (h0 + (int64_t) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry2 = (h2 + (int64_t) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry4 = (h4 + (int64_t) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry6 = (h6 + (int64_t) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + carry8 = (h8 + (int64_t) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + + +/* +h = -f + +Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + +Postconditions: + |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +*/ + +void fe_neg(fe h, const fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t h0 = -f0; + int32_t h1 = -f1; + int32_t h2 = -f2; + int32_t h3 = -f3; + int32_t h4 = -f4; + int32_t h5 = -f5; + int32_t h6 = -f6; + int32_t h7 = -f7; + int32_t h8 = -f8; + int32_t h9 = -f9; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} + + +void fe_pow22523(fe out, const fe z) { + fe t0; + fe t1; + fe t2; + int i; + fe_sq(t0, z); + + for (i = 1; i < 1; ++i) { + fe_sq(t0, t0); + } + + fe_sq(t1, t0); + + for (i = 1; i < 2; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t1, z, t1); + fe_mul(t0, t0, t1); + fe_sq(t0, t0); + + for (i = 1; i < 1; ++i) { + fe_sq(t0, t0); + } + + fe_mul(t0, t1, t0); + fe_sq(t1, t0); + + for (i = 1; i < 5; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t0, t1, t0); + fe_sq(t1, t0); + + for (i = 1; i < 10; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t1, t1, t0); + fe_sq(t2, t1); + + for (i = 1; i < 20; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t2, t1); + fe_sq(t1, t1); + + for (i = 1; i < 10; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t0, t1, t0); + fe_sq(t1, t0); + + for (i = 1; i < 50; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t1, t1, t0); + fe_sq(t2, t1); + + for (i = 1; i < 100; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t2, t1); + fe_sq(t1, t1); + + for (i = 1; i < 50; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t0, t1, t0); + fe_sq(t0, t0); + + for (i = 1; i < 2; ++i) { + fe_sq(t0, t0); + } + + fe_mul(out, t0, z); + return; +} + + +/* +h = f * f +Can overlap h with f. + +Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + +Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. +*/ + +/* +See fe_mul.c for discussion of implementation strategy. +*/ + +void fe_sq(fe h, const fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t f0_2 = 2 * f0; + int32_t f1_2 = 2 * f1; + int32_t f2_2 = 2 * f2; + int32_t f3_2 = 2 * f3; + int32_t f4_2 = 2 * f4; + int32_t f5_2 = 2 * f5; + int32_t f6_2 = 2 * f6; + int32_t f7_2 = 2 * f7; + int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */ + int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */ + int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */ + int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */ + int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */ + int64_t f0f0 = f0 * (int64_t) f0; + int64_t f0f1_2 = f0_2 * (int64_t) f1; + int64_t f0f2_2 = f0_2 * (int64_t) f2; + int64_t f0f3_2 = f0_2 * (int64_t) f3; + int64_t f0f4_2 = f0_2 * (int64_t) f4; + int64_t f0f5_2 = f0_2 * (int64_t) f5; + int64_t f0f6_2 = f0_2 * (int64_t) f6; + int64_t f0f7_2 = f0_2 * (int64_t) f7; + int64_t f0f8_2 = f0_2 * (int64_t) f8; + int64_t f0f9_2 = f0_2 * (int64_t) f9; + int64_t f1f1_2 = f1_2 * (int64_t) f1; + int64_t f1f2_2 = f1_2 * (int64_t) f2; + int64_t f1f3_4 = f1_2 * (int64_t) f3_2; + int64_t f1f4_2 = f1_2 * (int64_t) f4; + int64_t f1f5_4 = f1_2 * (int64_t) f5_2; + int64_t f1f6_2 = f1_2 * (int64_t) f6; + int64_t f1f7_4 = f1_2 * (int64_t) f7_2; + int64_t f1f8_2 = f1_2 * (int64_t) f8; + int64_t f1f9_76 = f1_2 * (int64_t) f9_38; + int64_t f2f2 = f2 * (int64_t) f2; + int64_t f2f3_2 = f2_2 * (int64_t) f3; + int64_t f2f4_2 = f2_2 * (int64_t) f4; + int64_t f2f5_2 = f2_2 * (int64_t) f5; + int64_t f2f6_2 = f2_2 * (int64_t) f6; + int64_t f2f7_2 = f2_2 * (int64_t) f7; + int64_t f2f8_38 = f2_2 * (int64_t) f8_19; + int64_t f2f9_38 = f2 * (int64_t) f9_38; + int64_t f3f3_2 = f3_2 * (int64_t) f3; + int64_t f3f4_2 = f3_2 * (int64_t) f4; + int64_t f3f5_4 = f3_2 * (int64_t) f5_2; + int64_t f3f6_2 = f3_2 * (int64_t) f6; + int64_t f3f7_76 = f3_2 * (int64_t) f7_38; + int64_t f3f8_38 = f3_2 * (int64_t) f8_19; + int64_t f3f9_76 = f3_2 * (int64_t) f9_38; + int64_t f4f4 = f4 * (int64_t) f4; + int64_t f4f5_2 = f4_2 * (int64_t) f5; + int64_t f4f6_38 = f4_2 * (int64_t) f6_19; + int64_t f4f7_38 = f4 * (int64_t) f7_38; + int64_t f4f8_38 = f4_2 * (int64_t) f8_19; + int64_t f4f9_38 = f4 * (int64_t) f9_38; + int64_t f5f5_38 = f5 * (int64_t) f5_38; + int64_t f5f6_38 = f5_2 * (int64_t) f6_19; + int64_t f5f7_76 = f5_2 * (int64_t) f7_38; + int64_t f5f8_38 = f5_2 * (int64_t) f8_19; + int64_t f5f9_76 = f5_2 * (int64_t) f9_38; + int64_t f6f6_19 = f6 * (int64_t) f6_19; + int64_t f6f7_38 = f6 * (int64_t) f7_38; + int64_t f6f8_38 = f6_2 * (int64_t) f8_19; + int64_t f6f9_38 = f6 * (int64_t) f9_38; + int64_t f7f7_38 = f7 * (int64_t) f7_38; + int64_t f7f8_38 = f7_2 * (int64_t) f8_19; + int64_t f7f9_76 = f7_2 * (int64_t) f9_38; + int64_t f8f8_19 = f8 * (int64_t) f8_19; + int64_t f8f9_38 = f8 * (int64_t) f9_38; + int64_t f9f9_38 = f9 * (int64_t) f9_38; + int64_t h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38; + int64_t h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38; + int64_t h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19; + int64_t h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38; + int64_t h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38; + int64_t h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38; + int64_t h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19; + int64_t h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38; + int64_t h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38; + int64_t h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry1 = (h1 + (int64_t) (1 << 24)) >> 25; + h2 += carry1; + h1 -= carry1 << 25; + carry5 = (h5 + (int64_t) (1 << 24)) >> 25; + h6 += carry5; + h5 -= carry5 << 25; + carry2 = (h2 + (int64_t) (1 << 25)) >> 26; + h3 += carry2; + h2 -= carry2 << 26; + carry6 = (h6 + (int64_t) (1 << 25)) >> 26; + h7 += carry6; + h6 -= carry6 << 26; + carry3 = (h3 + (int64_t) (1 << 24)) >> 25; + h4 += carry3; + h3 -= carry3 << 25; + carry7 = (h7 + (int64_t) (1 << 24)) >> 25; + h8 += carry7; + h7 -= carry7 << 25; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry8 = (h8 + (int64_t) (1 << 25)) >> 26; + h9 += carry8; + h8 -= carry8 << 26; + carry9 = (h9 + (int64_t) (1 << 24)) >> 25; + h0 += carry9 * 19; + h9 -= carry9 << 25; + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + + +/* +h = 2 * f * f +Can overlap h with f. + +Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + +Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. +*/ + +/* +See fe_mul.c for discussion of implementation strategy. +*/ + +void fe_sq2(fe h, const fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t f0_2 = 2 * f0; + int32_t f1_2 = 2 * f1; + int32_t f2_2 = 2 * f2; + int32_t f3_2 = 2 * f3; + int32_t f4_2 = 2 * f4; + int32_t f5_2 = 2 * f5; + int32_t f6_2 = 2 * f6; + int32_t f7_2 = 2 * f7; + int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */ + int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */ + int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */ + int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */ + int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */ + int64_t f0f0 = f0 * (int64_t) f0; + int64_t f0f1_2 = f0_2 * (int64_t) f1; + int64_t f0f2_2 = f0_2 * (int64_t) f2; + int64_t f0f3_2 = f0_2 * (int64_t) f3; + int64_t f0f4_2 = f0_2 * (int64_t) f4; + int64_t f0f5_2 = f0_2 * (int64_t) f5; + int64_t f0f6_2 = f0_2 * (int64_t) f6; + int64_t f0f7_2 = f0_2 * (int64_t) f7; + int64_t f0f8_2 = f0_2 * (int64_t) f8; + int64_t f0f9_2 = f0_2 * (int64_t) f9; + int64_t f1f1_2 = f1_2 * (int64_t) f1; + int64_t f1f2_2 = f1_2 * (int64_t) f2; + int64_t f1f3_4 = f1_2 * (int64_t) f3_2; + int64_t f1f4_2 = f1_2 * (int64_t) f4; + int64_t f1f5_4 = f1_2 * (int64_t) f5_2; + int64_t f1f6_2 = f1_2 * (int64_t) f6; + int64_t f1f7_4 = f1_2 * (int64_t) f7_2; + int64_t f1f8_2 = f1_2 * (int64_t) f8; + int64_t f1f9_76 = f1_2 * (int64_t) f9_38; + int64_t f2f2 = f2 * (int64_t) f2; + int64_t f2f3_2 = f2_2 * (int64_t) f3; + int64_t f2f4_2 = f2_2 * (int64_t) f4; + int64_t f2f5_2 = f2_2 * (int64_t) f5; + int64_t f2f6_2 = f2_2 * (int64_t) f6; + int64_t f2f7_2 = f2_2 * (int64_t) f7; + int64_t f2f8_38 = f2_2 * (int64_t) f8_19; + int64_t f2f9_38 = f2 * (int64_t) f9_38; + int64_t f3f3_2 = f3_2 * (int64_t) f3; + int64_t f3f4_2 = f3_2 * (int64_t) f4; + int64_t f3f5_4 = f3_2 * (int64_t) f5_2; + int64_t f3f6_2 = f3_2 * (int64_t) f6; + int64_t f3f7_76 = f3_2 * (int64_t) f7_38; + int64_t f3f8_38 = f3_2 * (int64_t) f8_19; + int64_t f3f9_76 = f3_2 * (int64_t) f9_38; + int64_t f4f4 = f4 * (int64_t) f4; + int64_t f4f5_2 = f4_2 * (int64_t) f5; + int64_t f4f6_38 = f4_2 * (int64_t) f6_19; + int64_t f4f7_38 = f4 * (int64_t) f7_38; + int64_t f4f8_38 = f4_2 * (int64_t) f8_19; + int64_t f4f9_38 = f4 * (int64_t) f9_38; + int64_t f5f5_38 = f5 * (int64_t) f5_38; + int64_t f5f6_38 = f5_2 * (int64_t) f6_19; + int64_t f5f7_76 = f5_2 * (int64_t) f7_38; + int64_t f5f8_38 = f5_2 * (int64_t) f8_19; + int64_t f5f9_76 = f5_2 * (int64_t) f9_38; + int64_t f6f6_19 = f6 * (int64_t) f6_19; + int64_t f6f7_38 = f6 * (int64_t) f7_38; + int64_t f6f8_38 = f6_2 * (int64_t) f8_19; + int64_t f6f9_38 = f6 * (int64_t) f9_38; + int64_t f7f7_38 = f7 * (int64_t) f7_38; + int64_t f7f8_38 = f7_2 * (int64_t) f8_19; + int64_t f7f9_76 = f7_2 * (int64_t) f9_38; + int64_t f8f8_19 = f8 * (int64_t) f8_19; + int64_t f8f9_38 = f8 * (int64_t) f9_38; + int64_t f9f9_38 = f9 * (int64_t) f9_38; + int64_t h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38; + int64_t h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38; + int64_t h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19; + int64_t h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38; + int64_t h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38; + int64_t h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38; + int64_t h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19; + int64_t h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38; + int64_t h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38; + int64_t h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + h0 += h0; + h1 += h1; + h2 += h2; + h3 += h3; + h4 += h4; + h5 += h5; + h6 += h6; + h7 += h7; + h8 += h8; + h9 += h9; + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry1 = (h1 + (int64_t) (1 << 24)) >> 25; + h2 += carry1; + h1 -= carry1 << 25; + carry5 = (h5 + (int64_t) (1 << 24)) >> 25; + h6 += carry5; + h5 -= carry5 << 25; + carry2 = (h2 + (int64_t) (1 << 25)) >> 26; + h3 += carry2; + h2 -= carry2 << 26; + carry6 = (h6 + (int64_t) (1 << 25)) >> 26; + h7 += carry6; + h6 -= carry6 << 26; + carry3 = (h3 + (int64_t) (1 << 24)) >> 25; + h4 += carry3; + h3 -= carry3 << 25; + carry7 = (h7 + (int64_t) (1 << 24)) >> 25; + h8 += carry7; + h7 -= carry7 << 25; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry8 = (h8 + (int64_t) (1 << 25)) >> 26; + h9 += carry8; + h8 -= carry8 << 26; + carry9 = (h9 + (int64_t) (1 << 24)) >> 25; + h0 += carry9 * 19; + h9 -= carry9 << 25; + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + + +/* +h = f - g +Can overlap h with f or g. + +Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + +Postconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +void fe_sub(fe h, const fe f, const fe g) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t h0 = f0 - g0; + int32_t h1 = f1 - g1; + int32_t h2 = f2 - g2; + int32_t h3 = f3 - g3; + int32_t h4 = f4 - g4; + int32_t h5 = f5 - g5; + int32_t h6 = f6 - g6; + int32_t h7 = f7 - g7; + int32_t h8 = f8 - g8; + int32_t h9 = f9 - g9; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} + + + +/* +Preconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. + +Write p=2^255-19; q=floor(h/p). +Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). + +Proof: + Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. + Also have |h-2^230 h9|<2^231 so |19 2^(-255)(h-2^230 h9)|<1/4. + + Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). + Then 0> 25; + q = (h0 + q) >> 26; + q = (h1 + q) >> 25; + q = (h2 + q) >> 26; + q = (h3 + q) >> 25; + q = (h4 + q) >> 26; + q = (h5 + q) >> 25; + q = (h6 + q) >> 26; + q = (h7 + q) >> 25; + q = (h8 + q) >> 26; + q = (h9 + q) >> 25; + /* Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. */ + h0 += 19 * q; + /* Goal: Output h-2^255 q, which is between 0 and 2^255-20. */ + carry0 = h0 >> 26; + h1 += carry0; + h0 -= carry0 << 26; + carry1 = h1 >> 25; + h2 += carry1; + h1 -= carry1 << 25; + carry2 = h2 >> 26; + h3 += carry2; + h2 -= carry2 << 26; + carry3 = h3 >> 25; + h4 += carry3; + h3 -= carry3 << 25; + carry4 = h4 >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry5 = h5 >> 25; + h6 += carry5; + h5 -= carry5 << 25; + carry6 = h6 >> 26; + h7 += carry6; + h6 -= carry6 << 26; + carry7 = h7 >> 25; + h8 += carry7; + h7 -= carry7 << 25; + carry8 = h8 >> 26; + h9 += carry8; + h8 -= carry8 << 26; + carry9 = h9 >> 25; + h9 -= carry9 << 25; + + /* h10 = carry9 */ + /* + Goal: Output h0+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. + Have h0+...+2^230 h9 between 0 and 2^255-1; + evidently 2^255 h10-2^255 q = 0. + Goal: Output h0+...+2^230 h9. + */ + s[0] = (unsigned char) (h0 >> 0); + s[1] = (unsigned char) (h0 >> 8); + s[2] = (unsigned char) (h0 >> 16); + s[3] = (unsigned char) ((h0 >> 24) | (h1 << 2)); + s[4] = (unsigned char) (h1 >> 6); + s[5] = (unsigned char) (h1 >> 14); + s[6] = (unsigned char) ((h1 >> 22) | (h2 << 3)); + s[7] = (unsigned char) (h2 >> 5); + s[8] = (unsigned char) (h2 >> 13); + s[9] = (unsigned char) ((h2 >> 21) | (h3 << 5)); + s[10] = (unsigned char) (h3 >> 3); + s[11] = (unsigned char) (h3 >> 11); + s[12] = (unsigned char) ((h3 >> 19) | (h4 << 6)); + s[13] = (unsigned char) (h4 >> 2); + s[14] = (unsigned char) (h4 >> 10); + s[15] = (unsigned char) (h4 >> 18); + s[16] = (unsigned char) (h5 >> 0); + s[17] = (unsigned char) (h5 >> 8); + s[18] = (unsigned char) (h5 >> 16); + s[19] = (unsigned char) ((h5 >> 24) | (h6 << 1)); + s[20] = (unsigned char) (h6 >> 7); + s[21] = (unsigned char) (h6 >> 15); + s[22] = (unsigned char) ((h6 >> 23) | (h7 << 3)); + s[23] = (unsigned char) (h7 >> 5); + s[24] = (unsigned char) (h7 >> 13); + s[25] = (unsigned char) ((h7 >> 21) | (h8 << 4)); + s[26] = (unsigned char) (h8 >> 4); + s[27] = (unsigned char) (h8 >> 12); + s[28] = (unsigned char) ((h8 >> 20) | (h9 << 6)); + s[29] = (unsigned char) (h9 >> 2); + s[30] = (unsigned char) (h9 >> 10); + s[31] = (unsigned char) (h9 >> 18); +} diff --git a/src/cryptoconditions/src/include/ed25519/src/fe.h b/src/cryptoconditions/src/include/ed25519/src/fe.h new file mode 100644 index 000000000..b4b62d282 --- /dev/null +++ b/src/cryptoconditions/src/include/ed25519/src/fe.h @@ -0,0 +1,41 @@ +#ifndef FE_H +#define FE_H + +#include "fixedint.h" + + +/* + fe means field element. + Here the field is \Z/(2^255-19). + An element t, entries t[0]...t[9], represents the integer + t[0]+2^26 t[1]+2^51 t[2]+2^77 t[3]+2^102 t[4]+...+2^230 t[9]. + Bounds on each t[i] vary depending on context. +*/ + + +typedef int32_t fe[10]; + + +void fe_0(fe h); +void fe_1(fe h); + +void fe_frombytes(fe h, const unsigned char *s); +void fe_tobytes(unsigned char *s, const fe h); + +void fe_copy(fe h, const fe f); +int fe_isnegative(const fe f); +int fe_isnonzero(const fe f); +void fe_cmov(fe f, const fe g, unsigned int b); +void fe_cswap(fe f, fe g, unsigned int b); + +void fe_neg(fe h, const fe f); +void fe_add(fe h, const fe f, const fe g); +void fe_invert(fe out, const fe z); +void fe_sq(fe h, const fe f); +void fe_sq2(fe h, const fe f); +void fe_mul(fe h, const fe f, const fe g); +void fe_mul121666(fe h, fe f); +void fe_pow22523(fe out, const fe z); +void fe_sub(fe h, const fe f, const fe g); + +#endif diff --git a/src/cryptoconditions/src/include/ed25519/src/fixedint.h b/src/cryptoconditions/src/include/ed25519/src/fixedint.h new file mode 100644 index 000000000..1a8745b1e --- /dev/null +++ b/src/cryptoconditions/src/include/ed25519/src/fixedint.h @@ -0,0 +1,72 @@ +/* + Portable header to provide the 32 and 64 bits type. + + Not a compatible replacement for , do not blindly use it as such. +*/ + +#if ((defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined(__WATCOMC__) && (defined(_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_) || defined(__UINT_FAST64_TYPE__)) )) && !defined(FIXEDINT_H_INCLUDED) + #include + #define FIXEDINT_H_INCLUDED + + #if defined(__WATCOMC__) && __WATCOMC__ >= 1250 && !defined(UINT64_C) + #include + #define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX)) + #endif +#endif + + +#ifndef FIXEDINT_H_INCLUDED + #define FIXEDINT_H_INCLUDED + + #include + + /* (u)int32_t */ + #ifndef uint32_t + #if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long uint32_t; + #elif (UINT_MAX == 0xffffffffUL) + typedef unsigned int uint32_t; + #elif (USHRT_MAX == 0xffffffffUL) + typedef unsigned short uint32_t; + #endif + #endif + + + #ifndef int32_t + #if (LONG_MAX == 0x7fffffffL) + typedef signed long int32_t; + #elif (INT_MAX == 0x7fffffffL) + typedef signed int int32_t; + #elif (SHRT_MAX == 0x7fffffffL) + typedef signed short int32_t; + #endif + #endif + + + /* (u)int64_t */ + #if (defined(__STDC__) && defined(__STDC_VERSION__) && __STDC__ && __STDC_VERSION__ >= 199901L) + typedef long long int64_t; + typedef unsigned long long uint64_t; + + #define UINT64_C(v) v ##ULL + #define INT64_C(v) v ##LL + #elif defined(__GNUC__) + __extension__ typedef long long int64_t; + __extension__ typedef unsigned long long uint64_t; + + #define UINT64_C(v) v ##ULL + #define INT64_C(v) v ##LL + #elif defined(__MWERKS__) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) || defined(__APPLE_CC__) || defined(_LONG_LONG) || defined(_CRAYC) + typedef long long int64_t; + typedef unsigned long long uint64_t; + + #define UINT64_C(v) v ##ULL + #define INT64_C(v) v ##LL + #elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined(__BORLANDC__) && __BORLANDC__ > 0x460) || defined(__alpha) || defined(__DECC) + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; + + #define UINT64_C(v) v ##UI64 + #define INT64_C(v) v ##I64 + #endif +#endif diff --git a/src/cryptoconditions/src/include/ed25519/src/ge.c b/src/cryptoconditions/src/include/ed25519/src/ge.c new file mode 100644 index 000000000..87c691bff --- /dev/null +++ b/src/cryptoconditions/src/include/ed25519/src/ge.c @@ -0,0 +1,467 @@ +#include "ge.h" +#include "precomp_data.h" + + +/* +r = p + q +*/ + +void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { + fe t0; + fe_add(r->X, p->Y, p->X); + fe_sub(r->Y, p->Y, p->X); + fe_mul(r->Z, r->X, q->YplusX); + fe_mul(r->Y, r->Y, q->YminusX); + fe_mul(r->T, q->T2d, p->T); + fe_mul(r->X, p->Z, q->Z); + fe_add(t0, r->X, r->X); + fe_sub(r->X, r->Z, r->Y); + fe_add(r->Y, r->Z, r->Y); + fe_add(r->Z, t0, r->T); + fe_sub(r->T, t0, r->T); +} + + +static void slide(signed char *r, const unsigned char *a) { + int i; + int b; + int k; + + for (i = 0; i < 256; ++i) { + r[i] = 1 & (a[i >> 3] >> (i & 7)); + } + + for (i = 0; i < 256; ++i) + if (r[i]) { + for (b = 1; b <= 6 && i + b < 256; ++b) { + if (r[i + b]) { + if (r[i] + (r[i + b] << b) <= 15) { + r[i] += r[i + b] << b; + r[i + b] = 0; + } else if (r[i] - (r[i + b] << b) >= -15) { + r[i] -= r[i + b] << b; + + for (k = i + b; k < 256; ++k) { + if (!r[k]) { + r[k] = 1; + break; + } + + r[k] = 0; + } + } else { + break; + } + } + } + } +} + +/* +r = a * A + b * B +where a = a[0]+256*a[1]+...+256^31 a[31]. +and b = b[0]+256*b[1]+...+256^31 b[31]. +B is the Ed25519 base point (x,4/5) with x positive. +*/ + +void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b) { + signed char aslide[256]; + signed char bslide[256]; + ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */ + ge_p1p1 t; + ge_p3 u; + ge_p3 A2; + int i; + slide(aslide, a); + slide(bslide, b); + ge_p3_to_cached(&Ai[0], A); + ge_p3_dbl(&t, A); + ge_p1p1_to_p3(&A2, &t); + ge_add(&t, &A2, &Ai[0]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[1], &u); + ge_add(&t, &A2, &Ai[1]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[2], &u); + ge_add(&t, &A2, &Ai[2]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[3], &u); + ge_add(&t, &A2, &Ai[3]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[4], &u); + ge_add(&t, &A2, &Ai[4]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[5], &u); + ge_add(&t, &A2, &Ai[5]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[6], &u); + ge_add(&t, &A2, &Ai[6]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[7], &u); + ge_p2_0(r); + + for (i = 255; i >= 0; --i) { + if (aslide[i] || bslide[i]) { + break; + } + } + + for (; i >= 0; --i) { + ge_p2_dbl(&t, r); + + if (aslide[i] > 0) { + ge_p1p1_to_p3(&u, &t); + ge_add(&t, &u, &Ai[aslide[i] / 2]); + } else if (aslide[i] < 0) { + ge_p1p1_to_p3(&u, &t); + ge_sub(&t, &u, &Ai[(-aslide[i]) / 2]); + } + + if (bslide[i] > 0) { + ge_p1p1_to_p3(&u, &t); + ge_madd(&t, &u, &Bi[bslide[i] / 2]); + } else if (bslide[i] < 0) { + ge_p1p1_to_p3(&u, &t); + ge_msub(&t, &u, &Bi[(-bslide[i]) / 2]); + } + + ge_p1p1_to_p2(r, &t); + } +} + + +static const fe d = { + -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116 +}; + +static const fe sqrtm1 = { + -32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482 +}; + +int ge_frombytes_negate_vartime(ge_p3 *h, const unsigned char *s) { + fe u; + fe v; + fe v3; + fe vxx; + fe check; + fe_frombytes(h->Y, s); + fe_1(h->Z); + fe_sq(u, h->Y); + fe_mul(v, u, d); + fe_sub(u, u, h->Z); /* u = y^2-1 */ + fe_add(v, v, h->Z); /* v = dy^2+1 */ + fe_sq(v3, v); + fe_mul(v3, v3, v); /* v3 = v^3 */ + fe_sq(h->X, v3); + fe_mul(h->X, h->X, v); + fe_mul(h->X, h->X, u); /* x = uv^7 */ + fe_pow22523(h->X, h->X); /* x = (uv^7)^((q-5)/8) */ + fe_mul(h->X, h->X, v3); + fe_mul(h->X, h->X, u); /* x = uv^3(uv^7)^((q-5)/8) */ + fe_sq(vxx, h->X); + fe_mul(vxx, vxx, v); + fe_sub(check, vxx, u); /* vx^2-u */ + + if (fe_isnonzero(check)) { + fe_add(check, vxx, u); /* vx^2+u */ + + if (fe_isnonzero(check)) { + return -1; + } + + fe_mul(h->X, h->X, sqrtm1); + } + + if (fe_isnegative(h->X) == (s[31] >> 7)) { + fe_neg(h->X, h->X); + } + + fe_mul(h->T, h->X, h->Y); + return 0; +} + + +/* +r = p + q +*/ + +void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) { + fe t0; + fe_add(r->X, p->Y, p->X); + fe_sub(r->Y, p->Y, p->X); + fe_mul(r->Z, r->X, q->yplusx); + fe_mul(r->Y, r->Y, q->yminusx); + fe_mul(r->T, q->xy2d, p->T); + fe_add(t0, p->Z, p->Z); + fe_sub(r->X, r->Z, r->Y); + fe_add(r->Y, r->Z, r->Y); + fe_add(r->Z, t0, r->T); + fe_sub(r->T, t0, r->T); +} + + +/* +r = p - q +*/ + +void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) { + fe t0; + + fe_add(r->X, p->Y, p->X); + fe_sub(r->Y, p->Y, p->X); + fe_mul(r->Z, r->X, q->yminusx); + fe_mul(r->Y, r->Y, q->yplusx); + fe_mul(r->T, q->xy2d, p->T); + fe_add(t0, p->Z, p->Z); + fe_sub(r->X, r->Z, r->Y); + fe_add(r->Y, r->Z, r->Y); + fe_sub(r->Z, t0, r->T); + fe_add(r->T, t0, r->T); +} + + +/* +r = p +*/ + +void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p) { + fe_mul(r->X, p->X, p->T); + fe_mul(r->Y, p->Y, p->Z); + fe_mul(r->Z, p->Z, p->T); +} + + + +/* +r = p +*/ + +void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p) { + fe_mul(r->X, p->X, p->T); + fe_mul(r->Y, p->Y, p->Z); + fe_mul(r->Z, p->Z, p->T); + fe_mul(r->T, p->X, p->Y); +} + + +void ge_p2_0(ge_p2 *h) { + fe_0(h->X); + fe_1(h->Y); + fe_1(h->Z); +} + + + +/* +r = 2 * p +*/ + +void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p) { + fe t0; + + fe_sq(r->X, p->X); + fe_sq(r->Z, p->Y); + fe_sq2(r->T, p->Z); + fe_add(r->Y, p->X, p->Y); + fe_sq(t0, r->Y); + fe_add(r->Y, r->Z, r->X); + fe_sub(r->Z, r->Z, r->X); + fe_sub(r->X, t0, r->Y); + fe_sub(r->T, r->T, r->Z); +} + + +void ge_p3_0(ge_p3 *h) { + fe_0(h->X); + fe_1(h->Y); + fe_1(h->Z); + fe_0(h->T); +} + + +/* +r = 2 * p +*/ + +void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p) { + ge_p2 q; + ge_p3_to_p2(&q, p); + ge_p2_dbl(r, &q); +} + + + +/* +r = p +*/ + +static const fe d2 = { + -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199 +}; + +void ge_p3_to_cached(ge_cached *r, const ge_p3 *p) { + fe_add(r->YplusX, p->Y, p->X); + fe_sub(r->YminusX, p->Y, p->X); + fe_copy(r->Z, p->Z); + fe_mul(r->T2d, p->T, d2); +} + + +/* +r = p +*/ + +void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p) { + fe_copy(r->X, p->X); + fe_copy(r->Y, p->Y); + fe_copy(r->Z, p->Z); +} + + +void ge_p3_tobytes(unsigned char *s, const ge_p3 *h) { + fe recip; + fe x; + fe y; + fe_invert(recip, h->Z); + fe_mul(x, h->X, recip); + fe_mul(y, h->Y, recip); + fe_tobytes(s, y); + s[31] ^= fe_isnegative(x) << 7; +} + + +static unsigned char equal(signed char b, signed char c) { + unsigned char ub = b; + unsigned char uc = c; + unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */ + uint64_t y = x; /* 0: yes; 1..255: no */ + y -= 1; /* large: yes; 0..254: no */ + y >>= 63; /* 1: yes; 0: no */ + return (unsigned char) y; +} + +static unsigned char negative(signed char b) { + uint64_t x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */ + x >>= 63; /* 1: yes; 0: no */ + return (unsigned char) x; +} + +static void cmov(ge_precomp *t, const ge_precomp *u, unsigned char b) { + fe_cmov(t->yplusx, u->yplusx, b); + fe_cmov(t->yminusx, u->yminusx, b); + fe_cmov(t->xy2d, u->xy2d, b); +} + + +static void select(ge_precomp *t, int pos, signed char b) { + ge_precomp minust; + unsigned char bnegative = negative(b); + unsigned char babs = b - (((-bnegative) & b) << 1); + fe_1(t->yplusx); + fe_1(t->yminusx); + fe_0(t->xy2d); + cmov(t, &base[pos][0], equal(babs, 1)); + cmov(t, &base[pos][1], equal(babs, 2)); + cmov(t, &base[pos][2], equal(babs, 3)); + cmov(t, &base[pos][3], equal(babs, 4)); + cmov(t, &base[pos][4], equal(babs, 5)); + cmov(t, &base[pos][5], equal(babs, 6)); + cmov(t, &base[pos][6], equal(babs, 7)); + cmov(t, &base[pos][7], equal(babs, 8)); + fe_copy(minust.yplusx, t->yminusx); + fe_copy(minust.yminusx, t->yplusx); + fe_neg(minust.xy2d, t->xy2d); + cmov(t, &minust, bnegative); +} + +/* +h = a * B +where a = a[0]+256*a[1]+...+256^31 a[31] +B is the Ed25519 base point (x,4/5) with x positive. + +Preconditions: + a[31] <= 127 +*/ + +void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) { + signed char e[64]; + signed char carry; + ge_p1p1 r; + ge_p2 s; + ge_precomp t; + int i; + + for (i = 0; i < 32; ++i) { + e[2 * i + 0] = (a[i] >> 0) & 15; + e[2 * i + 1] = (a[i] >> 4) & 15; + } + + /* each e[i] is between 0 and 15 */ + /* e[63] is between 0 and 7 */ + carry = 0; + + for (i = 0; i < 63; ++i) { + e[i] += carry; + carry = e[i] + 8; + carry >>= 4; + e[i] -= carry << 4; + } + + e[63] += carry; + /* each e[i] is between -8 and 8 */ + ge_p3_0(h); + + for (i = 1; i < 64; i += 2) { + select(&t, i / 2, e[i]); + ge_madd(&r, h, &t); + ge_p1p1_to_p3(h, &r); + } + + ge_p3_dbl(&r, h); + ge_p1p1_to_p2(&s, &r); + ge_p2_dbl(&r, &s); + ge_p1p1_to_p2(&s, &r); + ge_p2_dbl(&r, &s); + ge_p1p1_to_p2(&s, &r); + ge_p2_dbl(&r, &s); + ge_p1p1_to_p3(h, &r); + + for (i = 0; i < 64; i += 2) { + select(&t, i / 2, e[i]); + ge_madd(&r, h, &t); + ge_p1p1_to_p3(h, &r); + } +} + + +/* +r = p - q +*/ + +void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { + fe t0; + + fe_add(r->X, p->Y, p->X); + fe_sub(r->Y, p->Y, p->X); + fe_mul(r->Z, r->X, q->YminusX); + fe_mul(r->Y, r->Y, q->YplusX); + fe_mul(r->T, q->T2d, p->T); + fe_mul(r->X, p->Z, q->Z); + fe_add(t0, r->X, r->X); + fe_sub(r->X, r->Z, r->Y); + fe_add(r->Y, r->Z, r->Y); + fe_sub(r->Z, t0, r->T); + fe_add(r->T, t0, r->T); +} + + +void ge_tobytes(unsigned char *s, const ge_p2 *h) { + fe recip; + fe x; + fe y; + fe_invert(recip, h->Z); + fe_mul(x, h->X, recip); + fe_mul(y, h->Y, recip); + fe_tobytes(s, y); + s[31] ^= fe_isnegative(x) << 7; +} diff --git a/src/cryptoconditions/src/include/ed25519/src/ge.h b/src/cryptoconditions/src/include/ed25519/src/ge.h new file mode 100644 index 000000000..17fde2df1 --- /dev/null +++ b/src/cryptoconditions/src/include/ed25519/src/ge.h @@ -0,0 +1,74 @@ +#ifndef GE_H +#define GE_H + +#include "fe.h" + + +/* +ge means group element. + +Here the group is the set of pairs (x,y) of field elements (see fe.h) +satisfying -x^2 + y^2 = 1 + d x^2y^2 +where d = -121665/121666. + +Representations: + ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z + ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT + ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T + ge_precomp (Duif): (y+x,y-x,2dxy) +*/ + +typedef struct { + fe X; + fe Y; + fe Z; +} ge_p2; + +typedef struct { + fe X; + fe Y; + fe Z; + fe T; +} ge_p3; + +typedef struct { + fe X; + fe Y; + fe Z; + fe T; +} ge_p1p1; + +typedef struct { + fe yplusx; + fe yminusx; + fe xy2d; +} ge_precomp; + +typedef struct { + fe YplusX; + fe YminusX; + fe Z; + fe T2d; +} ge_cached; + +void ge_p3_tobytes(unsigned char *s, const ge_p3 *h); +void ge_tobytes(unsigned char *s, const ge_p2 *h); +int ge_frombytes_negate_vartime(ge_p3 *h, const unsigned char *s); + +void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q); +void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q); +void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b); +void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q); +void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q); +void ge_scalarmult_base(ge_p3 *h, const unsigned char *a); + +void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p); +void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p); +void ge_p2_0(ge_p2 *h); +void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p); +void ge_p3_0(ge_p3 *h); +void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p); +void ge_p3_to_cached(ge_cached *r, const ge_p3 *p); +void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p); + +#endif diff --git a/src/cryptoconditions/src/include/ed25519/src/key_exchange.c b/src/cryptoconditions/src/include/ed25519/src/key_exchange.c new file mode 100644 index 000000000..abd75da2c --- /dev/null +++ b/src/cryptoconditions/src/include/ed25519/src/key_exchange.c @@ -0,0 +1,79 @@ +#include "ed25519.h" +#include "fe.h" + +void ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key) { + unsigned char e[32]; + unsigned int i; + + fe x1; + fe x2; + fe z2; + fe x3; + fe z3; + fe tmp0; + fe tmp1; + + int pos; + unsigned int swap; + unsigned int b; + + /* copy the private key and make sure it's valid */ + for (i = 0; i < 32; ++i) { + e[i] = private_key[i]; + } + + e[0] &= 248; + e[31] &= 63; + e[31] |= 64; + + /* unpack the public key and convert edwards to montgomery */ + /* due to CodesInChaos: montgomeryX = (edwardsY + 1)*inverse(1 - edwardsY) mod p */ + fe_frombytes(x1, public_key); + fe_1(tmp1); + fe_add(tmp0, x1, tmp1); + fe_sub(tmp1, tmp1, x1); + fe_invert(tmp1, tmp1); + fe_mul(x1, tmp0, tmp1); + + fe_1(x2); + fe_0(z2); + fe_copy(x3, x1); + fe_1(z3); + + swap = 0; + for (pos = 254; pos >= 0; --pos) { + b = e[pos / 8] >> (pos & 7); + b &= 1; + swap ^= b; + fe_cswap(x2, x3, swap); + fe_cswap(z2, z3, swap); + swap = b; + + /* from montgomery.h */ + fe_sub(tmp0, x3, z3); + fe_sub(tmp1, x2, z2); + fe_add(x2, x2, z2); + fe_add(z2, x3, z3); + fe_mul(z3, tmp0, x2); + fe_mul(z2, z2, tmp1); + fe_sq(tmp0, tmp1); + fe_sq(tmp1, x2); + fe_add(x3, z3, z2); + fe_sub(z2, z3, z2); + fe_mul(x2, tmp1, tmp0); + fe_sub(tmp1, tmp1, tmp0); + fe_sq(z2, z2); + fe_mul121666(z3, tmp1); + fe_sq(x3, x3); + fe_add(tmp0, tmp0, z3); + fe_mul(z3, x1, z2); + fe_mul(z2, tmp1, tmp0); + } + + fe_cswap(x2, x3, swap); + fe_cswap(z2, z3, swap); + + fe_invert(z2, z2); + fe_mul(x2, x2, z2); + fe_tobytes(shared_secret, x2); +} diff --git a/src/cryptoconditions/src/include/ed25519/src/keypair.c b/src/cryptoconditions/src/include/ed25519/src/keypair.c new file mode 100644 index 000000000..dc1b8eccc --- /dev/null +++ b/src/cryptoconditions/src/include/ed25519/src/keypair.c @@ -0,0 +1,16 @@ +#include "ed25519.h" +#include "sha512.h" +#include "ge.h" + + +void ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed) { + ge_p3 A; + + sha512(seed, 32, private_key); + private_key[0] &= 248; + private_key[31] &= 63; + private_key[31] |= 64; + + ge_scalarmult_base(&A, private_key); + ge_p3_tobytes(public_key, &A); +} diff --git a/src/cryptoconditions/src/include/ed25519/src/precomp_data.h b/src/cryptoconditions/src/include/ed25519/src/precomp_data.h new file mode 100644 index 000000000..ff23986c3 --- /dev/null +++ b/src/cryptoconditions/src/include/ed25519/src/precomp_data.h @@ -0,0 +1,1391 @@ +static const ge_precomp Bi[8] = { + { + { 25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605 }, + { -12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378 }, + { -8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546 }, + }, + { + { 15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024 }, + { 16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574 }, + { 30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357 }, + }, + { + { 10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380 }, + { 4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306 }, + { 19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942 }, + }, + { + { 5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766 }, + { -30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701 }, + { 28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300 }, + }, + { + { -22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877 }, + { -6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951 }, + { 4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784 }, + }, + { + { -25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436 }, + { 25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918 }, + { 23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877 }, + }, + { + { -33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800 }, + { -25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305 }, + { -13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300 }, + }, + { + { -3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876 }, + { -24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619 }, + { -3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683 }, + }, +}; + + +/* base[i][j] = (j+1)*256^i*B */ +static const ge_precomp base[32][8] = { + { + { + { 25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605 }, + { -12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378 }, + { -8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546 }, + }, + { + { -12815894, -12976347, -21581243, 11784320, -25355658, -2750717, -11717903, -3814571, -358445, -10211303 }, + { -21703237, 6903825, 27185491, 6451973, -29577724, -9554005, -15616551, 11189268, -26829678, -5319081 }, + { 26966642, 11152617, 32442495, 15396054, 14353839, -12752335, -3128826, -9541118, -15472047, -4166697 }, + }, + { + { 15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024 }, + { 16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574 }, + { 30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357 }, + }, + { + { -17036878, 13921892, 10945806, -6033431, 27105052, -16084379, -28926210, 15006023, 3284568, -6276540 }, + { 23599295, -8306047, -11193664, -7687416, 13236774, 10506355, 7464579, 9656445, 13059162, 10374397 }, + { 7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664, -3839045, -641708, -101325 }, + }, + { + { 10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380 }, + { 4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306 }, + { 19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942 }, + }, + { + { -15371964, -12862754, 32573250, 4720197, -26436522, 5875511, -19188627, -15224819, -9818940, -12085777 }, + { -8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240, -15689887, 1762328, 14866737 }, + { -18199695, -15951423, -10473290, 1707278, -17185920, 3916101, -28236412, 3959421, 27914454, 4383652 }, + }, + { + { 5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766 }, + { -30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701 }, + { 28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300 }, + }, + { + { 14499471, -2729599, -33191113, -4254652, 28494862, 14271267, 30290735, 10876454, -33154098, 2381726 }, + { -7195431, -2655363, -14730155, 462251, -27724326, 3941372, -6236617, 3696005, -32300832, 15351955 }, + { 27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425 }, + }, + }, + { + { + { -13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171 }, + { 27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510 }, + { 17228999, -15661624, -1233527, 300140, -1224870, -11714777, 30364213, -9038194, 18016357, 4397660 }, + }, + { + { -10958843, -7690207, 4776341, -14954238, 27850028, -15602212, -26619106, 14544525, -17477504, 982639 }, + { 29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899, -4120128, -21047696, 9934963 }, + { 5793303, 16271923, -24131614, -10116404, 29188560, 1206517, -14747930, 4559895, -30123922, -10897950 }, + }, + { + { -27643952, -11493006, 16282657, -11036493, 28414021, -15012264, 24191034, 4541697, -13338309, 5500568 }, + { 12650548, -1497113, 9052871, 11355358, -17680037, -8400164, -17430592, 12264343, 10874051, 13524335 }, + { 25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038, 5080568, -22528059, 5376628 }, + }, + { + { -26088264, -4011052, -17013699, -3537628, -6726793, 1920897, -22321305, -9447443, 4535768, 1569007 }, + { -2255422, 14606630, -21692440, -8039818, 28430649, 8775819, -30494562, 3044290, 31848280, 12543772 }, + { -22028579, 2943893, -31857513, 6777306, 13784462, -4292203, -27377195, -2062731, 7718482, 14474653 }, + }, + { + { 2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965, -7236665, 24316168, -5253567 }, + { 13741529, 10911568, -33233417, -8603737, -20177830, -1033297, 33040651, -13424532, -20729456, 8321686 }, + { 21060490, -2212744, 15712757, -4336099, 1639040, 10656336, 23845965, -11874838, -9984458, 608372 }, + }, + { + { -13672732, -15087586, -10889693, -7557059, -6036909, 11305547, 1123968, -6780577, 27229399, 23887 }, + { -23244140, -294205, -11744728, 14712571, -29465699, -2029617, 12797024, -6440308, -1633405, 16678954 }, + { -29500620, 4770662, -16054387, 14001338, 7830047, 9564805, -1508144, -4795045, -17169265, 4904953 }, + }, + { + { 24059557, 14617003, 19037157, -15039908, 19766093, -14906429, 5169211, 16191880, 2128236, -4326833 }, + { -16981152, 4124966, -8540610, -10653797, 30336522, -14105247, -29806336, 916033, -6882542, -2986532 }, + { -22630907, 12419372, -7134229, -7473371, -16478904, 16739175, 285431, 2763829, 15736322, 4143876 }, + }, + { + { 2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801, -14594663, 23527084, -16458268 }, + { 33431127, -11130478, -17838966, -15626900, 8909499, 8376530, -32625340, 4087881, -15188911, -14416214 }, + { 1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038 }, + }, + }, + { + { + { 6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800 }, + { 4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645 }, + { -4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413, 9208236, 15886429, 16489664 }, + }, + { + { 1996075, 10375649, 14346367, 13311202, -6874135, -16438411, -13693198, 398369, -30606455, -712933 }, + { -25307465, 9795880, -2777414, 14878809, -33531835, 14780363, 13348553, 12076947, -30836462, 5113182 }, + { -17770784, 11797796, 31950843, 13929123, -25888302, 12288344, -30341101, -7336386, 13847711, 5387222 }, + }, + { + { -18582163, -3416217, 17824843, -2340966, 22744343, -10442611, 8763061, 3617786, -19600662, 10370991 }, + { 20246567, -14369378, 22358229, -543712, 18507283, -10413996, 14554437, -8746092, 32232924, 16763880 }, + { 9648505, 10094563, 26416693, 14745928, -30374318, -6472621, 11094161, 15689506, 3140038, -16510092 }, + }, + { + { -16160072, 5472695, 31895588, 4744994, 8823515, 10365685, -27224800, 9448613, -28774454, 366295 }, + { 19153450, 11523972, -11096490, -6503142, -24647631, 5420647, 28344573, 8041113, 719605, 11671788 }, + { 8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916, -15266516, 27000813, -10195553 }, + }, + { + { -15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065, 5336097, 6750977, -14521026 }, + { 11836410, -3979488, 26297894, 16080799, 23455045, 15735944, 1695823, -8819122, 8169720, 16220347 }, + { -18115838, 8653647, 17578566, -6092619, -8025777, -16012763, -11144307, -2627664, -5990708, -14166033 }, + }, + { + { -23308498, -10968312, 15213228, -10081214, -30853605, -11050004, 27884329, 2847284, 2655861, 1738395 }, + { -27537433, -14253021, -25336301, -8002780, -9370762, 8129821, 21651608, -3239336, -19087449, -11005278 }, + { 1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092, 5821408, 10478196, 8544890 }, + }, + { + { 32173121, -16129311, 24896207, 3921497, 22579056, -3410854, 19270449, 12217473, 17789017, -3395995 }, + { -30552961, -2228401, -15578829, -10147201, 13243889, 517024, 15479401, -3853233, 30460520, 1052596 }, + { -11614875, 13323618, 32618793, 8175907, -15230173, 12596687, 27491595, -4612359, 3179268, -9478891 }, + }, + { + { 31947069, -14366651, -4640583, -15339921, -15125977, -6039709, -14756777, -16411740, 19072640, -9511060 }, + { 11685058, 11822410, 3158003, -13952594, 33402194, -4165066, 5977896, -5215017, 473099, 5040608 }, + { -20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606 }, + }, + }, + { + { + { 7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389 }, + { -19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016 }, + { -11295600, -3793569, -15782110, -7964573, 12708869, -8456199, 2014099, -9050574, -2369172, -5877341 }, + }, + { + { -22472376, -11568741, -27682020, 1146375, 18956691, 16640559, 1192730, -3714199, 15123619, 10811505 }, + { 14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363, 15776356, -28886779, -11974553 }, + { -28241164, -8072475, -4978962, -5315317, 29416931, 1847569, -20654173, -16484855, 4714547, -9600655 }, + }, + { + { 15200332, 8368572, 19679101, 15970074, -31872674, 1959451, 24611599, -4543832, -11745876, 12340220 }, + { 12876937, -10480056, 33134381, 6590940, -6307776, 14872440, 9613953, 8241152, 15370987, 9608631 }, + { -4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868, 15866074, -28210621, -8814099 }, + }, + { + { 26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233, 858697, 20571223, 8420556 }, + { 14620715, 13067227, -15447274, 8264467, 14106269, 15080814, 33531827, 12516406, -21574435, -12476749 }, + { 236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519, 7256740, 8791136, 15069930 }, + }, + { + { 1276410, -9371918, 22949635, -16322807, -23493039, -5702186, 14711875, 4874229, -30663140, -2331391 }, + { 5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175, -7912378, -33069337, 9234253 }, + { 20590503, -9018988, 31529744, -7352666, -2706834, 10650548, 31559055, -11609587, 18979186, 13396066 }, + }, + { + { 24474287, 4968103, 22267082, 4407354, 24063882, -8325180, -18816887, 13594782, 33514650, 7021958 }, + { -11566906, -6565505, -21365085, 15928892, -26158305, 4315421, -25948728, -3916677, -21480480, 12868082 }, + { -28635013, 13504661, 19988037, -2132761, 21078225, 6443208, -21446107, 2244500, -12455797, -8089383 }, + }, + { + { -30595528, 13793479, -5852820, 319136, -25723172, -6263899, 33086546, 8957937, -15233648, 5540521 }, + { -11630176, -11503902, -8119500, -7643073, 2620056, 1022908, -23710744, -1568984, -16128528, -14962807 }, + { 23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819, 892185, -11513277, -15205948 }, + }, + { + { 9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819, 4763127, -19179614, 5867134 }, + { -32765025, 1927590, 31726409, -4753295, 23962434, -16019500, 27846559, 5931263, -29749703, -16108455 }, + { 27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629 }, + }, + }, + { + { + { -8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069 }, + { -32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746 }, + { 24687205, 8613276, -30667046, -3233545, 1863892, -1830544, 19206234, 7134917, -11284482, -828919 }, + }, + { + { 11334899, -9218022, 8025293, 12707519, 17523892, -10476071, 10243738, -14685461, -5066034, 16498837 }, + { 8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925, -14124238, 6536641, 10543906 }, + { -28946384, 15479763, -17466835, 568876, -1497683, 11223454, -2669190, -16625574, -27235709, 8876771 }, + }, + { + { -25742899, -12566864, -15649966, -846607, -33026686, -796288, -33481822, 15824474, -604426, -9039817 }, + { 10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697, -4890037, 1657394, 3084098 }, + { 10477963, -7470260, 12119566, -13250805, 29016247, -5365589, 31280319, 14396151, -30233575, 15272409 }, + }, + { + { -12288309, 3169463, 28813183, 16658753, 25116432, -5630466, -25173957, -12636138, -25014757, 1950504 }, + { -26180358, 9489187, 11053416, -14746161, -31053720, 5825630, -8384306, -8767532, 15341279, 8373727 }, + { 28685821, 7759505, -14378516, -12002860, -31971820, 4079242, 298136, -10232602, -2878207, 15190420 }, + }, + { + { -32932876, 13806336, -14337485, -15794431, -24004620, 10940928, 8669718, 2742393, -26033313, -6875003 }, + { -1580388, -11729417, -25979658, -11445023, -17411874, -10912854, 9291594, -16247779, -12154742, 6048605 }, + { -30305315, 14843444, 1539301, 11864366, 20201677, 1900163, 13934231, 5128323, 11213262, 9168384 }, + }, + { + { -26280513, 11007847, 19408960, -940758, -18592965, -4328580, -5088060, -11105150, 20470157, -16398701 }, + { -23136053, 9282192, 14855179, -15390078, -7362815, -14408560, -22783952, 14461608, 14042978, 5230683 }, + { 29969567, -2741594, -16711867, -8552442, 9175486, -2468974, 21556951, 3506042, -5933891, -12449708 }, + }, + { + { -3144746, 8744661, 19704003, 4581278, -20430686, 6830683, -21284170, 8971513, -28539189, 15326563 }, + { -19464629, 10110288, -17262528, -3503892, -23500387, 1355669, -15523050, 15300988, -20514118, 9168260 }, + { -5353335, 4488613, -23803248, 16314347, 7780487, -15638939, -28948358, 9601605, 33087103, -9011387 }, + }, + { + { -19443170, -15512900, -20797467, -12445323, -29824447, 10229461, -27444329, -15000531, -5996870, 15664672 }, + { 23294591, -16632613, -22650781, -8470978, 27844204, 11461195, 13099750, -2460356, 18151676, 13417686 }, + { -24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665 }, + }, + }, + { + { + { 11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182 }, + { -31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277 }, + { 14098400, 6555944, 23007258, 5757252, -15427832, -12950502, 30123440, 4617780, -16900089, -655628 }, + }, + { + { -4026201, -15240835, 11893168, 13718664, -14809462, 1847385, -15819999, 10154009, 23973261, -12684474 }, + { -26531820, -3695990, -1908898, 2534301, -31870557, -16550355, 18341390, -11419951, 32013174, -10103539 }, + { -25479301, 10876443, -11771086, -14625140, -12369567, 1838104, 21911214, 6354752, 4425632, -837822 }, + }, + { + { -10433389, -14612966, 22229858, -3091047, -13191166, 776729, -17415375, -12020462, 4725005, 14044970 }, + { 19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390, -1411784, -19522291, -16109756 }, + { -24864089, 12986008, -10898878, -5558584, -11312371, -148526, 19541418, 8180106, 9282262, 10282508 }, + }, + { + { -26205082, 4428547, -8661196, -13194263, 4098402, -14165257, 15522535, 8372215, 5542595, -10702683 }, + { -10562541, 14895633, 26814552, -16673850, -17480754, -2489360, -2781891, 6993761, -18093885, 10114655 }, + { -20107055, -929418, 31422704, 10427861, -7110749, 6150669, -29091755, -11529146, 25953725, -106158 }, + }, + { + { -4234397, -8039292, -9119125, 3046000, 2101609, -12607294, 19390020, 6094296, -3315279, 12831125 }, + { -15998678, 7578152, 5310217, 14408357, -33548620, -224739, 31575954, 6326196, 7381791, -2421839 }, + { -20902779, 3296811, 24736065, -16328389, 18374254, 7318640, 6295303, 8082724, -15362489, 12339664 }, + }, + { + { 27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414, 15768922, 25091167, 14856294 }, + { -18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300, -12695493, -22182473, -9012899 }, + { -11423429, -5421590, 11632845, 3405020, 30536730, -11674039, -27260765, 13866390, 30146206, 9142070 }, + }, + { + { 3924129, -15307516, -13817122, -10054960, 12291820, -668366, -27702774, 9326384, -8237858, 4171294 }, + { -15921940, 16037937, 6713787, 16606682, -21612135, 2790944, 26396185, 3731949, 345228, -5462949 }, + { -21327538, 13448259, 25284571, 1143661, 20614966, -8849387, 2031539, -12391231, -16253183, -13582083 }, + }, + { + { 31016211, -16722429, 26371392, -14451233, -5027349, 14854137, 17477601, 3842657, 28012650, -16405420 }, + { -5075835, 9368966, -8562079, -4600902, -15249953, 6970560, -9189873, 16292057, -8867157, 3507940 }, + { 29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396 }, + }, + }, + { + { + { -12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567 }, + { 20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127 }, + { -16665533, -10650790, -6160345, -13305760, 9192020, -1802462, 17271490, 12349094, 26939669, -3752294 }, + }, + { + { -12889898, 9373458, 31595848, 16374215, 21471720, 13221525, -27283495, -12348559, -3698806, 117887 }, + { 22263325, -6560050, 3984570, -11174646, -15114008, -566785, 28311253, 5358056, -23319780, 541964 }, + { 16259219, 3261970, 2309254, -15534474, -16885711, -4581916, 24134070, -16705829, -13337066, -13552195 }, + }, + { + { 9378160, -13140186, -22845982, -12745264, 28198281, -7244098, -2399684, -717351, 690426, 14876244 }, + { 24977353, -314384, -8223969, -13465086, 28432343, -1176353, -13068804, -12297348, -22380984, 6618999 }, + { -1538174, 11685646, 12944378, 13682314, -24389511, -14413193, 8044829, -13817328, 32239829, -5652762 }, + }, + { + { -18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647, -10350059, 32779359, 5095274 }, + { -33008130, -5214506, -32264887, -3685216, 9460461, -9327423, -24601656, 14506724, 21639561, -2630236 }, + { -16400943, -13112215, 25239338, 15531969, 3987758, -4499318, -1289502, -6863535, 17874574, 558605 }, + }, + { + { -13600129, 10240081, 9171883, 16131053, -20869254, 9599700, 33499487, 5080151, 2085892, 5119761 }, + { -22205145, -2519528, -16381601, 414691, -25019550, 2170430, 30634760, -8363614, -31999993, -5759884 }, + { -6845704, 15791202, 8550074, -1312654, 29928809, -12092256, 27534430, -7192145, -22351378, 12961482 }, + }, + { + { -24492060, -9570771, 10368194, 11582341, -23397293, -2245287, 16533930, 8206996, -30194652, -5159638 }, + { -11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630, 7031275, 7589640, 8945490 }, + { -32152748, 8917967, 6661220, -11677616, -1192060, -15793393, 7251489, -11182180, 24099109, -14456170 }, + }, + { + { 5019558, -7907470, 4244127, -14714356, -26933272, 6453165, -19118182, -13289025, -6231896, -10280736 }, + { 10853594, 10721687, 26480089, 5861829, -22995819, 1972175, -1866647, -10557898, -3363451, -6441124 }, + { -17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661, -2008168, -13866408, 7421392 }, + }, + { + { 8139927, -6546497, 32257646, -5890546, 30375719, 1886181, -21175108, 15441252, 28826358, -4123029 }, + { 6267086, 9695052, 7709135, -16603597, -32869068, -1886135, 14795160, -7840124, 13746021, -1742048 }, + { 28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958 }, + }, + }, + { + { + { 24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593 }, + { 26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071 }, + { -11618399, -14554430, -24321212, 7655128, -1369274, 5214312, -27400540, 10258390, -17646694, -8186692 }, + }, + { + { 11431204, 15823007, 26570245, 14329124, 18029990, 4796082, -31446179, 15580664, 9280358, -3973687 }, + { -160783, -10326257, -22855316, -4304997, -20861367, -13621002, -32810901, -11181622, -15545091, 4387441 }, + { -20799378, 12194512, 3937617, -5805892, -27154820, 9340370, -24513992, 8548137, 20617071, -7482001 }, + }, + { + { -938825, -3930586, -8714311, 16124718, 24603125, -6225393, -13775352, -11875822, 24345683, 10325460 }, + { -19855277, -1568885, -22202708, 8714034, 14007766, 6928528, 16318175, -1010689, 4766743, 3552007 }, + { -21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514, 14481909, 10988822, -3994762 }, + }, + { + { 15564307, -14311570, 3101243, 5684148, 30446780, -8051356, 12677127, -6505343, -8295852, 13296005 }, + { -9442290, 6624296, -30298964, -11913677, -4670981, -2057379, 31521204, 9614054, -30000824, 12074674 }, + { 4771191, -135239, 14290749, -13089852, 27992298, 14998318, -1413936, -1556716, 29832613, -16391035 }, + }, + { + { 7064884, -7541174, -19161962, -5067537, -18891269, -2912736, 25825242, 5293297, -27122660, 13101590 }, + { -2298563, 2439670, -7466610, 1719965, -27267541, -16328445, 32512469, -5317593, -30356070, -4190957 }, + { -30006540, 10162316, -33180176, 3981723, -16482138, -13070044, 14413974, 9515896, 19568978, 9628812 }, + }, + { + { 33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894, -6106839, -6291786, 3437740 }, + { -18978877, 3884493, 19469877, 12726490, 15913552, 13614290, -22961733, 70104, 7463304, 4176122 }, + { -27124001, 10659917, 11482427, -16070381, 12771467, -6635117, -32719404, -5322751, 24216882, 5944158 }, + }, + { + { 8894125, 7450974, -2664149, -9765752, -28080517, -12389115, 19345746, 14680796, 11632993, 5847885 }, + { 26942781, -2315317, 9129564, -4906607, 26024105, 11769399, -11518837, 6367194, -9727230, 4782140 }, + { 19916461, -4828410, -22910704, -11414391, 25606324, -5972441, 33253853, 8220911, 6358847, -1873857 }, + }, + { + { 801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388, -4480480, -13538503, 1387155 }, + { 19646058, 5720633, -11416706, 12814209, 11607948, 12749789, 14147075, 15156355, -21866831, 11835260 }, + { 19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483 }, + }, + }, + { + { + { -3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677 }, + { 32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815 }, + { 22865569, -4652735, 27603668, -12545395, 14348958, 8234005, 24808405, 5719875, 28483275, 2841751 }, + }, + { + { -16420968, -1113305, -327719, -12107856, 21886282, -15552774, -1887966, -315658, 19932058, -12739203 }, + { -11656086, 10087521, -8864888, -5536143, -19278573, -3055912, 3999228, 13239134, -4777469, -13910208 }, + { 1382174, -11694719, 17266790, 9194690, -13324356, 9720081, 20403944, 11284705, -14013818, 3093230 }, + }, + { + { 16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424, 16271225, -24049421, -6691850 }, + { -21911077, -5927941, -4611316, -5560156, -31744103, -10785293, 24123614, 15193618, -21652117, -16739389 }, + { -9935934, -4289447, -25279823, 4372842, 2087473, 10399484, 31870908, 14690798, 17361620, 11864968 }, + }, + { + { -11307610, 6210372, 13206574, 5806320, -29017692, -13967200, -12331205, -7486601, -25578460, -16240689 }, + { 14668462, -12270235, 26039039, 15305210, 25515617, 4542480, 10453892, 6577524, 9145645, -6443880 }, + { 5974874, 3053895, -9433049, -10385191, -31865124, 3225009, -7972642, 3936128, -5652273, -3050304 }, + }, + { + { 30625386, -4729400, -25555961, -12792866, -20484575, 7695099, 17097188, -16303496, -27999779, 1803632 }, + { -3553091, 9865099, -5228566, 4272701, -5673832, -16689700, 14911344, 12196514, -21405489, 7047412 }, + { 20093277, 9920966, -11138194, -5343857, 13161587, 12044805, -32856851, 4124601, -32343828, -10257566 }, + }, + { + { -20788824, 14084654, -13531713, 7842147, 19119038, -13822605, 4752377, -8714640, -21679658, 2288038 }, + { -26819236, -3283715, 29965059, 3039786, -14473765, 2540457, 29457502, 14625692, -24819617, 12570232 }, + { -1063558, -11551823, 16920318, 12494842, 1278292, -5869109, -21159943, -3498680, -11974704, 4724943 }, + }, + { + { 17960970, -11775534, -4140968, -9702530, -8876562, -1410617, -12907383, -8659932, -29576300, 1903856 }, + { 23134274, -14279132, -10681997, -1611936, 20684485, 15770816, -12989750, 3190296, 26955097, 14109738 }, + { 15308788, 5320727, -30113809, -14318877, 22902008, 7767164, 29425325, -11277562, 31960942, 11934971 }, + }, + { + { -27395711, 8435796, 4109644, 12222639, -24627868, 14818669, 20638173, 4875028, 10491392, 1379718 }, + { -13159415, 9197841, 3875503, -8936108, -1383712, -5879801, 33518459, 16176658, 21432314, 12180697 }, + { -11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883 }, + }, + }, + { + { + { 5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912 }, + { -26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358 }, + { 3296484, 6223048, 24680646, -12246460, -23052020, 5903205, -8862297, -4639164, 12376617, 3188849 }, + }, + { + { 29190488, -14659046, 27549113, -1183516, 3520066, -10697301, 32049515, -7309113, -16109234, -9852307 }, + { -14744486, -9309156, 735818, -598978, -20407687, -5057904, 25246078, -15795669, 18640741, -960977 }, + { -6928835, -16430795, 10361374, 5642961, 4910474, 12345252, -31638386, -494430, 10530747, 1053335 }, + }, + { + { -29265967, -14186805, -13538216, -12117373, -19457059, -10655384, -31462369, -2948985, 24018831, 15026644 }, + { -22592535, -3145277, -2289276, 5953843, -13440189, 9425631, 25310643, 13003497, -2314791, -15145616 }, + { -27419985, -603321, -8043984, -1669117, -26092265, 13987819, -27297622, 187899, -23166419, -2531735 }, + }, + { + { -21744398, -13810475, 1844840, 5021428, -10434399, -15911473, 9716667, 16266922, -5070217, 726099 }, + { 29370922, -6053998, 7334071, -15342259, 9385287, 2247707, -13661962, -4839461, 30007388, -15823341 }, + { -936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109, 730663, 9835848, 4555336 }, + }, + { + { -23376435, 1410446, -22253753, -12899614, 30867635, 15826977, 17693930, 544696, -11985298, 12422646 }, + { 31117226, -12215734, -13502838, 6561947, -9876867, -12757670, -5118685, -4096706, 29120153, 13924425 }, + { -17400879, -14233209, 19675799, -2734756, -11006962, -5858820, -9383939, -11317700, 7240931, -237388 }, + }, + { + { -31361739, -11346780, -15007447, -5856218, -22453340, -12152771, 1222336, 4389483, 3293637, -15551743 }, + { -16684801, -14444245, 11038544, 11054958, -13801175, -3338533, -24319580, 7733547, 12796905, -6335822 }, + { -8759414, -10817836, -25418864, 10783769, -30615557, -9746811, -28253339, 3647836, 3222231, -11160462 }, + }, + { + { 18606113, 1693100, -25448386, -15170272, 4112353, 10045021, 23603893, -2048234, -7550776, 2484985 }, + { 9255317, -3131197, -12156162, -1004256, 13098013, -9214866, 16377220, -2102812, -19802075, -3034702 }, + { -22729289, 7496160, -5742199, 11329249, 19991973, -3347502, -31718148, 9936966, -30097688, -10618797 }, + }, + { + { 21878590, -5001297, 4338336, 13643897, -3036865, 13160960, 19708896, 5415497, -7360503, -4109293 }, + { 27736861, 10103576, 12500508, 8502413, -3413016, -9633558, 10436918, -1550276, -23659143, -8132100 }, + { 19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688 }, + }, + }, + { + { + { 12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186 }, + { 2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610 }, + { -2529932, -2229646, 445613, 10720828, -13849527, -11505937, -23507731, 16354465, 15067285, -14147707 }, + }, + { + { 7840942, 14037873, -33364863, 15934016, -728213, -3642706, 21403988, 1057586, -19379462, -12403220 }, + { 915865, -16469274, 15608285, -8789130, -24357026, 6060030, -17371319, 8410997, -7220461, 16527025 }, + { 32922597, -556987, 20336074, -16184568, 10903705, -5384487, 16957574, 52992, 23834301, 6588044 }, + }, + { + { 32752030, 11232950, 3381995, -8714866, 22652988, -10744103, 17159699, 16689107, -20314580, -1305992 }, + { -4689649, 9166776, -25710296, -10847306, 11576752, 12733943, 7924251, -2752281, 1976123, -7249027 }, + { 21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041, -3371252, 12331345, -8237197 }, + }, + { + { 8651614, -4477032, -16085636, -4996994, 13002507, 2950805, 29054427, -5106970, 10008136, -4667901 }, + { 31486080, 15114593, -14261250, 12951354, 14369431, -7387845, 16347321, -13662089, 8684155, -10532952 }, + { 19443825, 11385320, 24468943, -9659068, -23919258, 2187569, -26263207, -6086921, 31316348, 14219878 }, + }, + { + { -28594490, 1193785, 32245219, 11392485, 31092169, 15722801, 27146014, 6992409, 29126555, 9207390 }, + { 32382935, 1110093, 18477781, 11028262, -27411763, -7548111, -4980517, 10843782, -7957600, -14435730 }, + { 2814918, 7836403, 27519878, -7868156, -20894015, -11553689, -21494559, 8550130, 28346258, 1994730 }, + }, + { + { -19578299, 8085545, -14000519, -3948622, 2785838, -16231307, -19516951, 7174894, 22628102, 8115180 }, + { -30405132, 955511, -11133838, -15078069, -32447087, -13278079, -25651578, 3317160, -9943017, 930272 }, + { -15303681, -6833769, 28856490, 1357446, 23421993, 1057177, 24091212, -1388970, -22765376, -10650715 }, + }, + { + { -22751231, -5303997, -12907607, -12768866, -15811511, -7797053, -14839018, -16554220, -1867018, 8398970 }, + { -31969310, 2106403, -4736360, 1362501, 12813763, 16200670, 22981545, -6291273, 18009408, -15772772 }, + { -17220923, -9545221, -27784654, 14166835, 29815394, 7444469, 29551787, -3727419, 19288549, 1325865 }, + }, + { + { 15100157, -15835752, -23923978, -1005098, -26450192, 15509408, 12376730, -3479146, 33166107, -8042750 }, + { 20909231, 13023121, -9209752, 16251778, -5778415, -8094914, 12412151, 10018715, 2213263, -13878373 }, + { 32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348 }, + }, + }, + { + { + { 9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144 }, + { -22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195 }, + { 5408411, -1136691, -4969122, 10561668, 24145918, 14240566, 31319731, -4235541, 19985175, -3436086 }, + }, + { + { -13994457, 16616821, 14549246, 3341099, 32155958, 13648976, -17577068, 8849297, 65030, 8370684 }, + { -8320926, -12049626, 31204563, 5839400, -20627288, -1057277, -19442942, 6922164, 12743482, -9800518 }, + { -2361371, 12678785, 28815050, 4759974, -23893047, 4884717, 23783145, 11038569, 18800704, 255233 }, + }, + { + { -5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847, 9066957, 19258688, -14753793 }, + { -2936654, -10827535, -10432089, 14516793, -3640786, 4372541, -31934921, 2209390, -1524053, 2055794 }, + { 580882, 16705327, 5468415, -2683018, -30926419, -14696000, -7203346, -8994389, -30021019, 7394435 }, + }, + { + { 23838809, 1822728, -15738443, 15242727, 8318092, -3733104, -21672180, -3492205, -4821741, 14799921 }, + { 13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804, 13496856, -9056018, 7402518 }, + { 2286874, -4435931, -20042458, -2008336, -13696227, 5038122, 11006906, -15760352, 8205061, 1607563 }, + }, + { + { 14414086, -8002132, 3331830, -3208217, 22249151, -5594188, 18364661, -2906958, 30019587, -9029278 }, + { -27688051, 1585953, -10775053, 931069, -29120221, -11002319, -14410829, 12029093, 9944378, 8024 }, + { 4368715, -3709630, 29874200, -15022983, -20230386, -11410704, -16114594, -999085, -8142388, 5640030 }, + }, + { + { 10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887, -16694564, 15219798, -14327783 }, + { 27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605, -1173195, -18342183, 9742717 }, + { 6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614, 7406442, 12420155, 1994844 }, + }, + { + { 14012521, -5024720, -18384453, -9578469, -26485342, -3936439, -13033478, -10909803, 24319929, -6446333 }, + { 16412690, -4507367, 10772641, 15929391, -17068788, -4658621, 10555945, -10484049, -30102368, -4739048 }, + { 22397382, -7767684, -9293161, -12792868, 17166287, -9755136, -27333065, 6199366, 21880021, -12250760 }, + }, + { + { -4283307, 5368523, -31117018, 8163389, -30323063, 3209128, 16557151, 8890729, 8840445, 4957760 }, + { -15447727, 709327, -6919446, -10870178, -29777922, 6522332, -21720181, 12130072, -14796503, 5005757 }, + { -2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112 }, + }, + }, + { + { + { -19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468 }, + { 3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184 }, + { 10379208, -6045554, 8877319, 1473647, -29291284, -12507580, 16690915, 2553332, -3132688, 16400289 }, + }, + { + { 15716668, 1254266, -18472690, 7446274, -8448918, 6344164, -22097271, -7285580, 26894937, 9132066 }, + { 24158887, 12938817, 11085297, -8177598, -28063478, -4457083, -30576463, 64452, -6817084, -2692882 }, + { 13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710, -3418511, -4688006, 2364226 }, + }, + { + { 16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024, -11697457, 15445875, -7798101 }, + { 29004207, -7867081, 28661402, -640412, -12794003, -7943086, 31863255, -4135540, -278050, -15759279 }, + { -6122061, -14866665, -28614905, 14569919, -10857999, -3591829, 10343412, -6976290, -29828287, -10815811 }, + }, + { + { 27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636, 15372179, 17293797, 960709 }, + { 20263915, 11434237, -5765435, 11236810, 13505955, -10857102, -16111345, 6493122, -19384511, 7639714 }, + { -2830798, -14839232, 25403038, -8215196, -8317012, -16173699, 18006287, -16043750, 29994677, -15808121 }, + }, + { + { 9769828, 5202651, -24157398, -13631392, -28051003, -11561624, -24613141, -13860782, -31184575, 709464 }, + { 12286395, 13076066, -21775189, -1176622, -25003198, 4057652, -32018128, -8890874, 16102007, 13205847 }, + { 13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170, 8525972, 10151379, 10394400 }, + }, + { + { 4024660, -16137551, 22436262, 12276534, -9099015, -2686099, 19698229, 11743039, -33302334, 8934414 }, + { -15879800, -4525240, -8580747, -2934061, 14634845, -698278, -9449077, 3137094, -11536886, 11721158 }, + { 17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229, 8835153, -9205489, -1280045 }, + }, + { + { -461409, -7830014, 20614118, 16688288, -7514766, -4807119, 22300304, 505429, 6108462, -6183415 }, + { -5070281, 12367917, -30663534, 3234473, 32617080, -8422642, 29880583, -13483331, -26898490, -7867459 }, + { -31975283, 5726539, 26934134, 10237677, -3173717, -605053, 24199304, 3795095, 7592688, -14992079 }, + }, + { + { 21594432, -14964228, 17466408, -4077222, 32537084, 2739898, 6407723, 12018833, -28256052, 4298412 }, + { -20650503, -11961496, -27236275, 570498, 3767144, -1717540, 13891942, -1569194, 13717174, 10805743 }, + { -14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836 }, + }, + }, + { + { + { 12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022 }, + { 18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429 }, + { -6789020, -3146043, 16192429, 13241070, 15898607, -14206114, -10084880, -6661110, -2403099, 5276065 }, + }, + { + { 30169808, -5317648, 26306206, -11750859, 27814964, 7069267, 7152851, 3684982, 1449224, 13082861 }, + { 10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382, 15056736, -21016438, -8202000 }, + { -33150110, 3261608, 22745853, 7948688, 19370557, -15177665, -26171976, 6482814, -10300080, -11060101 }, + }, + { + { 32869458, -5408545, 25609743, 15678670, -10687769, -15471071, 26112421, 2521008, -22664288, 6904815 }, + { 29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737, 3841096, -29003639, -6657642 }, + { 10340844, -6630377, -18656632, -2278430, 12621151, -13339055, 30878497, -11824370, -25584551, 5181966 }, + }, + { + { 25940115, -12658025, 17324188, -10307374, -8671468, 15029094, 24396252, -16450922, -2322852, -12388574 }, + { -21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390, 12641087, 20603771, -6561742 }, + { -18882287, -11673380, 24849422, 11501709, 13161720, -4768874, 1925523, 11914390, 4662781, 7820689 }, + }, + { + { 12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456, 12172924, 16136752, 15264020 }, + { -10349955, -14680563, -8211979, 2330220, -17662549, -14545780, 10658213, 6671822, 19012087, 3772772 }, + { 3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732, -15762884, 20527771, 12988982 }, + }, + { + { -14822485, -5797269, -3707987, 12689773, -898983, -10914866, -24183046, -10564943, 3299665, -12424953 }, + { -16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197, 6461331, -25583147, 8991218 }, + { -17226263, 1816362, -1673288, -6086439, 31783888, -8175991, -32948145, 7417950, -30242287, 1507265 }, + }, + { + { 29692663, 6829891, -10498800, 4334896, 20945975, -11906496, -28887608, 8209391, 14606362, -10647073 }, + { -3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695, 9761487, 4170404, -2085325 }, + { -11587470, 14855945, -4127778, -1531857, -26649089, 15084046, 22186522, 16002000, -14276837, -8400798 }, + }, + { + { -4811456, 13761029, -31703877, -2483919, -3312471, 7869047, -7113572, -9620092, 13240845, 10965870 }, + { -7742563, -8256762, -14768334, -13656260, -23232383, 12387166, 4498947, 14147411, 29514390, 4302863 }, + { -13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927 }, + }, + }, + { + { + { -2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267 }, + { -9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663 }, + { 22470984, 12369526, 23446014, -5441109, -21520802, -9698723, -11772496, -11574455, -25083830, 4271862 }, + }, + { + { -25169565, -10053642, -19909332, 15361595, -5984358, 2159192, 75375, -4278529, -32526221, 8469673 }, + { 15854970, 4148314, -8893890, 7259002, 11666551, 13824734, -30531198, 2697372, 24154791, -9460943 }, + { 15446137, -15806644, 29759747, 14019369, 30811221, -9610191, -31582008, 12840104, 24913809, 9815020 }, + }, + { + { -4709286, -5614269, -31841498, -12288893, -14443537, 10799414, -9103676, 13438769, 18735128, 9466238 }, + { 11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821, -10896103, -22728655, 16199064 }, + { 14576810, 379472, -26786533, -8317236, -29426508, -10812974, -102766, 1876699, 30801119, 2164795 }, + }, + { + { 15995086, 3199873, 13672555, 13712240, -19378835, -4647646, -13081610, -15496269, -13492807, 1268052 }, + { -10290614, -3659039, -3286592, 10948818, 23037027, 3794475, -3470338, -12600221, -17055369, 3565904 }, + { 29210088, -9419337, -5919792, -4952785, 10834811, -13327726, -16512102, -10820713, -27162222, -14030531 }, + }, + { + { -13161890, 15508588, 16663704, -8156150, -28349942, 9019123, -29183421, -3769423, 2244111, -14001979 }, + { -5152875, -3800936, -9306475, -6071583, 16243069, 14684434, -25673088, -16180800, 13491506, 4641841 }, + { 10813417, 643330, -19188515, -728916, 30292062, -16600078, 27548447, -7721242, 14476989, -12767431 }, + }, + { + { 10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937, -1644259, -27912810, 12651324 }, + { -31185513, -813383, 22271204, 11835308, 10201545, 15351028, 17099662, 3988035, 21721536, -3148940 }, + { 10202177, -6545839, -31373232, -9574638, -32150642, -8119683, -12906320, 3852694, 13216206, 14842320 }, + }, + { + { -15815640, -10601066, -6538952, -7258995, -6984659, -6581778, -31500847, 13765824, -27434397, 9900184 }, + { 14465505, -13833331, -32133984, -14738873, -27443187, 12990492, 33046193, 15796406, -7051866, -8040114 }, + { 30924417, -8279620, 6359016, -12816335, 16508377, 9071735, -25488601, 15413635, 9524356, -7018878 }, + }, + { + { 12274201, -13175547, 32627641, -1785326, 6736625, 13267305, 5237659, -5109483, 15663516, 4035784 }, + { -2951309, 8903985, 17349946, 601635, -16432815, -4612556, -13732739, -15889334, -22258478, 4659091 }, + { -16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585 }, + }, + }, + { + { + { -8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208 }, + { 10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864 }, + { 17932739, -12437276, -24039557, 10749060, 11316803, 7535897, 22503767, 5561594, -3646624, 3898661 }, + }, + { + { 7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531, 7152530, 21831162, 1245233 }, + { 26958459, -14658026, 4314586, 8346991, -5677764, 11960072, -32589295, -620035, -30402091, -16716212 }, + { -12165896, 9166947, 33491384, 13673479, 29787085, 13096535, 6280834, 14587357, -22338025, 13987525 }, + }, + { + { -24349909, 7778775, 21116000, 15572597, -4833266, -5357778, -4300898, -5124639, -7469781, -2858068 }, + { 9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781, 6439245, -14581012, 4091397 }, + { -8426427, 1470727, -28109679, -1596990, 3978627, -5123623, -19622683, 12092163, 29077877, -14741988 }, + }, + { + { 5269168, -6859726, -13230211, -8020715, 25932563, 1763552, -5606110, -5505881, -20017847, 2357889 }, + { 32264008, -15407652, -5387735, -1160093, -2091322, -3946900, 23104804, -12869908, 5727338, 189038 }, + { 14609123, -8954470, -6000566, -16622781, -14577387, -7743898, -26745169, 10942115, -25888931, -14884697 }, + }, + { + { 20513500, 5557931, -15604613, 7829531, 26413943, -2019404, -21378968, 7471781, 13913677, -5137875 }, + { -25574376, 11967826, 29233242, 12948236, -6754465, 4713227, -8940970, 14059180, 12878652, 8511905 }, + { -25656801, 3393631, -2955415, -7075526, -2250709, 9366908, -30223418, 6812974, 5568676, -3127656 }, + }, + { + { 11630004, 12144454, 2116339, 13606037, 27378885, 15676917, -17408753, -13504373, -14395196, 8070818 }, + { 27117696, -10007378, -31282771, -5570088, 1127282, 12772488, -29845906, 10483306, -11552749, -1028714 }, + { 10637467, -5688064, 5674781, 1072708, -26343588, -6982302, -1683975, 9177853, -27493162, 15431203 }, + }, + { + { 20525145, 10892566, -12742472, 12779443, -29493034, 16150075, -28240519, 14943142, -15056790, -7935931 }, + { -30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767, -3239766, -3356550, 9594024 }, + { -23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683, -6492290, 13352335, -10977084 }, + }, + { + { -1931799, -5407458, 3304649, -12884869, 17015806, -4877091, -29783850, -7752482, -13215537, -319204 }, + { 20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742, 15077870, -22750759, 14523817 }, + { 27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667 }, + }, + }, + { + { + { 11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504 }, + { -12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768 }, + { -19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043, 13794114, -19414307, -15621255 }, + }, + { + { 6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603, 6970005, -1691065, -9004790 }, + { 1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622, -5475723, -16796596, -5031438 }, + { -22273315, -13524424, -64685, -4334223, -18605636, -10921968, -20571065, -7007978, -99853, -10237333 }, + }, + { + { 17747465, 10039260, 19368299, -4050591, -20630635, -16041286, 31992683, -15857976, -29260363, -5511971 }, + { 31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999, -3744247, 4882242, -10626905 }, + { 29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198, 3272828, -5190932, -4162409 }, + }, + { + { 12501286, 4044383, -8612957, -13392385, -32430052, 5136599, -19230378, -3529697, 330070, -3659409 }, + { 6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522, -8573892, -271295, 12071499 }, + { -8365515, -4042521, 25133448, -4517355, -6211027, 2265927, -32769618, 1936675, -5159697, 3829363 }, + }, + { + { 28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550, -6567787, 26333140, 14267664 }, + { -11067219, 11871231, 27385719, -10559544, -4585914, -11189312, 10004786, -8709488, -21761224, 8930324 }, + { -21197785, -16396035, 25654216, -1725397, 12282012, 11008919, 1541940, 4757911, -26491501, -16408940 }, + }, + { + { 13537262, -7759490, -20604840, 10961927, -5922820, -13218065, -13156584, 6217254, -15943699, 13814990 }, + { -17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681, 9257833, -1956526, -1776914 }, + { -25045300, -10191966, 15366585, 15166509, -13105086, 8423556, -29171540, 12361135, -18685978, 4578290 }, + }, + { + { 24579768, 3711570, 1342322, -11180126, -27005135, 14124956, -22544529, 14074919, 21964432, 8235257 }, + { -6528613, -2411497, 9442966, -5925588, 12025640, -1487420, -2981514, -1669206, 13006806, 2355433 }, + { -16304899, -13605259, -6632427, -5142349, 16974359, -10911083, 27202044, 1719366, 1141648, -12796236 }, + }, + { + { -12863944, -13219986, -8318266, -11018091, -6810145, -4843894, 13475066, -3133972, 32674895, 13715045 }, + { 11423335, -5468059, 32344216, 8962751, 24989809, 9241752, -13265253, 16086212, -28740881, -15642093 }, + { -1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347 }, + }, + }, + { + { + { -28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191 }, + { -15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507 }, + { -12632451, 11257346, -32692994, 13548177, -721004, 10879011, 31168030, 13952092, -29571492, -3635906 }, + }, + { + { 3877321, -9572739, 32416692, 5405324, -11004407, -13656635, 3759769, 11935320, 5611860, 8164018 }, + { -16275802, 14667797, 15906460, 12155291, -22111149, -9039718, 32003002, -8832289, 5773085, -8422109 }, + { -23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725, 12376320, 31632953, 190926 }, + }, + { + { -24593607, -16138885, -8423991, 13378746, 14162407, 6901328, -8288749, 4508564, -25341555, -3627528 }, + { 8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941, -14786005, -1672488, 827625 }, + { -32720583, -16289296, -32503547, 7101210, 13354605, 2659080, -1800575, -14108036, -24878478, 1541286 }, + }, + { + { 2901347, -1117687, 3880376, -10059388, -17620940, -3612781, -21802117, -3567481, 20456845, -1885033 }, + { 27019610, 12299467, -13658288, -1603234, -12861660, -4861471, -19540150, -5016058, 29439641, 15138866 }, + { 21536104, -6626420, -32447818, -10690208, -22408077, 5175814, -5420040, -16361163, 7779328, 109896 }, + }, + { + { 30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390, 12180118, 23177719, -554075 }, + { 26572847, 3405927, -31701700, 12890905, -19265668, 5335866, -6493768, 2378492, 4439158, -13279347 }, + { -22716706, 3489070, -9225266, -332753, 18875722, -1140095, 14819434, -12731527, -17717757, -5461437 }, + }, + { + { -5056483, 16566551, 15953661, 3767752, -10436499, 15627060, -820954, 2177225, 8550082, -15114165 }, + { -18473302, 16596775, -381660, 15663611, 22860960, 15585581, -27844109, -3582739, -23260460, -8428588 }, + { -32480551, 15707275, -8205912, -5652081, 29464558, 2713815, -22725137, 15860482, -21902570, 1494193 }, + }, + { + { -19562091, -14087393, -25583872, -9299552, 13127842, 759709, 21923482, 16529112, 8742704, 12967017 }, + { -28464899, 1553205, 32536856, -10473729, -24691605, -406174, -8914625, -2933896, -29903758, 15553883 }, + { 21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572, 14513274, 19375923, -12647961 }, + }, + { + { 8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818, -6222716, 2862653, 9455043 }, + { 29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124, -2990080, 15511449, 4789663 }, + { -20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362 }, + }, + }, + { + { + { -33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860 }, + { 2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466 }, + { -24645692, 13317462, -30449259, -15653928, 21365574, -10869657, 11344424, 864440, -2499677, -16710063 }, + }, + { + { -26432803, 6148329, -17184412, -14474154, 18782929, -275997, -22561534, 211300, 2719757, 4940997 }, + { -1323882, 3911313, -6948744, 14759765, -30027150, 7851207, 21690126, 8518463, 26699843, 5276295 }, + { -13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586, 149635, -15452774, 7159369 }, + }, + { + { 9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009, 8312176, 22477218, -8403385 }, + { 18155857, -16504990, 19744716, 9006923, 15154154, -10538976, 24256460, -4864995, -22548173, 9334109 }, + { 2986088, -4911893, 10776628, -3473844, 10620590, -7083203, -21413845, 14253545, -22587149, 536906 }, + }, + { + { 4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551, 10589625, 10838060, -15420424 }, + { -19342404, 867880, 9277171, -3218459, -14431572, -1986443, 19295826, -15796950, 6378260, 699185 }, + { 7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039, 15693155, -5045064, -13373962 }, + }, + { + { -7737563, -5869402, -14566319, -7406919, 11385654, 13201616, 31730678, -10962840, -3918636, -9669325 }, + { 10188286, -15770834, -7336361, 13427543, 22223443, 14896287, 30743455, 7116568, -21786507, 5427593 }, + { 696102, 13206899, 27047647, -10632082, 15285305, -9853179, 10798490, -4578720, 19236243, 12477404 }, + }, + { + { -11229439, 11243796, -17054270, -8040865, -788228, -8167967, -3897669, 11180504, -23169516, 7733644 }, + { 17800790, -14036179, -27000429, -11766671, 23887827, 3149671, 23466177, -10538171, 10322027, 15313801 }, + { 26246234, 11968874, 32263343, -5468728, 6830755, -13323031, -15794704, -101982, -24449242, 10890804 }, + }, + { + { -31365647, 10271363, -12660625, -6267268, 16690207, -13062544, -14982212, 16484931, 25180797, -5334884 }, + { -586574, 10376444, -32586414, -11286356, 19801893, 10997610, 2276632, 9482883, 316878, 13820577 }, + { -9882808, -4510367, -2115506, 16457136, -11100081, 11674996, 30756178, -7515054, 30696930, -3712849 }, + }, + { + { 32988917, -9603412, 12499366, 7910787, -10617257, -11931514, -7342816, -9985397, -32349517, 7392473 }, + { -8855661, 15927861, 9866406, -3649411, -2396914, -16655781, -30409476, -9134995, 25112947, -2926644 }, + { -2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319 }, + }, + }, + { + { + { -11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599 }, + { -9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768 }, + { -27757857, 247744, -15194774, -9002551, 23288161, -10011936, -23869595, 6503646, 20650474, 1804084 }, + }, + { + { -27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995, -10329713, 27842616, -202328 }, + { -15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656, 5031932, -11375082, 12714369 }, + { 20807691, -7270825, 29286141, 11421711, -27876523, -13868230, -21227475, 1035546, -19733229, 12796920 }, + }, + { + { 12076899, -14301286, -8785001, -11848922, -25012791, 16400684, -17591495, -12899438, 3480665, -15182815 }, + { -32361549, 5457597, 28548107, 7833186, 7303070, -11953545, -24363064, -15921875, -33374054, 2771025 }, + { -21389266, 421932, 26597266, 6860826, 22486084, -6737172, -17137485, -4210226, -24552282, 15673397 }, + }, + { + { -20184622, 2338216, 19788685, -9620956, -4001265, -8740893, -20271184, 4733254, 3727144, -12934448 }, + { 6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594, 7975683, 31123697, -10958981 }, + { 30069250, -11435332, 30434654, 2958439, 18399564, -976289, 12296869, 9204260, -16432438, 9648165 }, + }, + { + { 32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266, 5248604, -26008332, -11377501 }, + { 17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711, 15298639, 2662509, -16297073 }, + { -1172927, -7558695, -4366770, -4287744, -21346413, -8434326, 32087529, -1222777, 32247248, -14389861 }, + }, + { + { 14312628, 1221556, 17395390, -8700143, -4945741, -8684635, -28197744, -9637817, -16027623, -13378845 }, + { -1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502, 9803137, 17597934, 2346211 }, + { 18510800, 15337574, 26171504, 981392, -22241552, 7827556, -23491134, -11323352, 3059833, -11782870 }, + }, + { + { 10141598, 6082907, 17829293, -1947643, 9830092, 13613136, -25556636, -5544586, -33502212, 3592096 }, + { 33114168, -15889352, -26525686, -13343397, 33076705, 8716171, 1151462, 1521897, -982665, -6837803 }, + { -32939165, -4255815, 23947181, -324178, -33072974, -12305637, -16637686, 3891704, 26353178, 693168 }, + }, + { + { 30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294, -400668, 31375464, 14369965 }, + { -14370654, -7772529, 1510301, 6434173, -18784789, -6262728, 32732230, -13108839, 17901441, 16011505 }, + { 18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598 }, + }, + }, + { + { + { 5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782 }, + { 5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900 }, + { -31399660, 11407555, 25755363, 6891399, -3256938, 14872274, -24849353, 8141295, -10632534, -585479 }, + }, + { + { -12675304, 694026, -5076145, 13300344, 14015258, -14451394, -9698672, -11329050, 30944593, 1130208 }, + { 8247766, -6710942, -26562381, -7709309, -14401939, -14648910, 4652152, 2488540, 23550156, -271232 }, + { 17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737, -5908146, -408818, -137719 }, + }, + { + { 16091085, -16253926, 18599252, 7340678, 2137637, -1221657, -3364161, 14550936, 3260525, -7166271 }, + { -4910104, -13332887, 18550887, 10864893, -16459325, -7291596, -23028869, -13204905, -12748722, 2701326 }, + { -8574695, 16099415, 4629974, -16340524, -20786213, -6005432, -10018363, 9276971, 11329923, 1862132 }, + }, + { + { 14763076, -15903608, -30918270, 3689867, 3511892, 10313526, -21951088, 12219231, -9037963, -940300 }, + { 8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216, -2909717, -15438168, 11595570 }, + { 15214962, 3537601, -26238722, -14058872, 4418657, -15230761, 13947276, 10730794, -13489462, -4363670 }, + }, + { + { -2538306, 7682793, 32759013, 263109, -29984731, -7955452, -22332124, -10188635, 977108, 699994 }, + { -12466472, 4195084, -9211532, 550904, -15565337, 12917920, 19118110, -439841, -30534533, -14337913 }, + { 31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237, -10051775, 12493932, -5409317 }, + }, + { + { -25680606, 5260744, -19235809, -6284470, -3695942, 16566087, 27218280, 2607121, 29375955, 6024730 }, + { 842132, -2794693, -4763381, -8722815, 26332018, -12405641, 11831880, 6985184, -9940361, 2854096 }, + { -4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645, 960770, 12121869, 16648078 }, + }, + { + { -15218652, 14667096, -13336229, 2013717, 30598287, -464137, -31504922, -7882064, 20237806, 2838411 }, + { -19288047, 4453152, 15298546, -16178388, 22115043, -15972604, 12544294, -13470457, 1068881, -12499905 }, + { -9558883, -16518835, 33238498, 13506958, 30505848, -1114596, -8486907, -2630053, 12521378, 4845654 }, + }, + { + { -28198521, 10744108, -2958380, 10199664, 7759311, -13088600, 3409348, -873400, -6482306, -12885870 }, + { -23561822, 6230156, -20382013, 10655314, -24040585, -11621172, 10477734, -1240216, -3113227, 13974498 }, + { 12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579 }, + }, + }, + { + { + { 14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677 }, + { 10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647 }, + { -2453894, 15725973, -20436342, -10410672, -5803908, -11040220, -7135870, -11642895, 18047436, -15281743 }, + }, + { + { -25173001, -11307165, 29759956, 11776784, -22262383, -15820455, 10993114, -12850837, -17620701, -9408468 }, + { 21987233, 700364, -24505048, 14972008, -7774265, -5718395, 32155026, 2581431, -29958985, 8773375 }, + { -25568350, 454463, -13211935, 16126715, 25240068, 8594567, 20656846, 12017935, -7874389, -13920155 }, + }, + { + { 6028182, 6263078, -31011806, -11301710, -818919, 2461772, -31841174, -5468042, -1721788, -2776725 }, + { -12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845, -4166698, 28408820, 6816612 }, + { -10358094, -8237829, 19549651, -12169222, 22082623, 16147817, 20613181, 13982702, -10339570, 5067943 }, + }, + { + { -30505967, -3821767, 12074681, 13582412, -19877972, 2443951, -19719286, 12746132, 5331210, -10105944 }, + { 30528811, 3601899, -1957090, 4619785, -27361822, -15436388, 24180793, -12570394, 27679908, -1648928 }, + { 9402404, -13957065, 32834043, 10838634, -26580150, -13237195, 26653274, -8685565, 22611444, -12715406 }, + }, + { + { 22190590, 1118029, 22736441, 15130463, -30460692, -5991321, 19189625, -4648942, 4854859, 6622139 }, + { -8310738, -2953450, -8262579, -3388049, -10401731, -271929, 13424426, -3567227, 26404409, 13001963 }, + { -31241838, -15415700, -2994250, 8939346, 11562230, -12840670, -26064365, -11621720, -15405155, 11020693 }, + }, + { + { 1866042, -7949489, -7898649, -10301010, 12483315, 13477547, 3175636, -12424163, 28761762, 1406734 }, + { -448555, -1777666, 13018551, 3194501, -9580420, -11161737, 24760585, -4347088, 25577411, -13378680 }, + { -24290378, 4759345, -690653, -1852816, 2066747, 10693769, -29595790, 9884936, -9368926, 4745410 }, + }, + { + { -9141284, 6049714, -19531061, -4341411, -31260798, 9944276, -15462008, -11311852, 10931924, -11931931 }, + { -16561513, 14112680, -8012645, 4817318, -8040464, -11414606, -22853429, 10856641, -20470770, 13434654 }, + { 22759489, -10073434, -16766264, -1871422, 13637442, -10168091, 1765144, -12654326, 28445307, -5364710 }, + }, + { + { 29875063, 12493613, 2795536, -3786330, 1710620, 15181182, -10195717, -8788675, 9074234, 1167180 }, + { -26205683, 11014233, -9842651, -2635485, -26908120, 7532294, -18716888, -9535498, 3843903, 9367684 }, + { -10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895 }, + }, + }, + { + { + { 22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501 }, + { -11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413 }, + { 6314175, -10264892, -32772502, 15957557, -10157730, 168750, -8618807, 14290061, 27108877, -1180880 }, + }, + { + { -8586597, -7170966, 13241782, 10960156, -32991015, -13794596, 33547976, -11058889, -27148451, 981874 }, + { 22833440, 9293594, -32649448, -13618667, -9136966, 14756819, -22928859, -13970780, -10479804, -16197962 }, + { -7768587, 3326786, -28111797, 10783824, 19178761, 14905060, 22680049, 13906969, -15933690, 3797899 }, + }, + { + { 21721356, -4212746, -12206123, 9310182, -3882239, -13653110, 23740224, -2709232, 20491983, -8042152 }, + { 9209270, -15135055, -13256557, -6167798, -731016, 15289673, 25947805, 15286587, 30997318, -6703063 }, + { 7392032, 16618386, 23946583, -8039892, -13265164, -1533858, -14197445, -2321576, 17649998, -250080 }, + }, + { + { -9301088, -14193827, 30609526, -3049543, -25175069, -1283752, -15241566, -9525724, -2233253, 7662146 }, + { -17558673, 1763594, -33114336, 15908610, -30040870, -12174295, 7335080, -8472199, -3174674, 3440183 }, + { -19889700, -5977008, -24111293, -9688870, 10799743, -16571957, 40450, -4431835, 4862400, 1133 }, + }, + { + { -32856209, -7873957, -5422389, 14860950, -16319031, 7956142, 7258061, 311861, -30594991, -7379421 }, + { -3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763, 16527196, 18278453, 15405622 }, + { -4381906, 8508652, -19898366, -3674424, -5984453, 15149970, -13313598, 843523, -21875062, 13626197 }, + }, + { + { 2281448, -13487055, -10915418, -2609910, 1879358, 16164207, -10783882, 3953792, 13340839, 15928663 }, + { 31727126, -7179855, -18437503, -8283652, 2875793, -16390330, -25269894, -7014826, -23452306, 5964753 }, + { 4100420, -5959452, -17179337, 6017714, -18705837, 12227141, -26684835, 11344144, 2538215, -7570755 }, + }, + { + { -9433605, 6123113, 11159803, -2156608, 30016280, 14966241, -20474983, 1485421, -629256, -15958862 }, + { -26804558, 4260919, 11851389, 9658551, -32017107, 16367492, -20205425, -13191288, 11659922, -11115118 }, + { 26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568, -10170080, 33100372, -1306171 }, + }, + { + { 15121113, -5201871, -10389905, 15427821, -27509937, -15992507, 21670947, 4486675, -5931810, -14466380 }, + { 16166486, -9483733, -11104130, 6023908, -31926798, -1364923, 2340060, -16254968, -10735770, -10039824 }, + { 28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270 }, + }, + }, + { + { + { -817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438 }, + { -31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584 }, + { -594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117, -12613632, -19773211, -10713562 }, + }, + { + { 30464590, -11262872, -4127476, -12734478, 19835327, -7105613, -24396175, 2075773, -17020157, 992471 }, + { 18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841, 8080033, -11574335, -10601610 }, + { 19598397, 10334610, 12555054, 2555664, 18821899, -10339780, 21873263, 16014234, 26224780, 16452269 }, + }, + { + { -30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804, -7618186, -20533829, 3698650 }, + { 14187449, 3448569, -10636236, -10810935, -22663880, -3433596, 7268410, -10890444, 27394301, 12015369 }, + { 19695761, 16087646, 28032085, 12999827, 6817792, 11427614, 20244189, -1312777, -13259127, -3402461 }, + }, + { + { 30860103, 12735208, -1888245, -4699734, -16974906, 2256940, -8166013, 12298312, -8550524, -10393462 }, + { -5719826, -11245325, -1910649, 15569035, 26642876, -7587760, -5789354, -15118654, -4976164, 12651793 }, + { -2848395, 9953421, 11531313, -5282879, 26895123, -12697089, -13118820, -16517902, 9768698, -2533218 }, + }, + { + { -24719459, 1894651, -287698, -4704085, 15348719, -8156530, 32767513, 12765450, 4940095, 10678226 }, + { 18860224, 15980149, -18987240, -1562570, -26233012, -11071856, -7843882, 13944024, -24372348, 16582019 }, + { -15504260, 4970268, -29893044, 4175593, -20993212, -2199756, -11704054, 15444560, -11003761, 7989037 }, + }, + { + { 31490452, 5568061, -2412803, 2182383, -32336847, 4531686, -32078269, 6200206, -19686113, -14800171 }, + { -17308668, -15879940, -31522777, -2831, -32887382, 16375549, 8680158, -16371713, 28550068, -6857132 }, + { -28126887, -5688091, 16837845, -1820458, -6850681, 12700016, -30039981, 4364038, 1155602, 5988841 }, + }, + { + { 21890435, -13272907, -12624011, 12154349, -7831873, 15300496, 23148983, -4470481, 24618407, 8283181 }, + { -33136107, -10512751, 9975416, 6841041, -31559793, 16356536, 3070187, -7025928, 1466169, 10740210 }, + { -1509399, -15488185, -13503385, -10655916, 32799044, 909394, -13938903, -5779719, -32164649, -15327040 }, + }, + { + { 3960823, -14267803, -28026090, -15918051, -19404858, 13146868, 15567327, 951507, -3260321, -573935 }, + { 24740841, 5052253, -30094131, 8961361, 25877428, 6165135, -24368180, 14397372, -7380369, -6144105 }, + { -28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814 }, + }, + }, + { + { + { 793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852 }, + { 5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581 }, + { -4859255, -3779343, -2917758, -6748019, 7778750, 11688288, -30404353, -9871238, -1558923, -9863646 }, + }, + { + { 10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958, 14783338, -30581476, -15757844 }, + { 10566929, 12612572, -31944212, 11118703, -12633376, 12362879, 21752402, 8822496, 24003793, 14264025 }, + { 27713862, -7355973, -11008240, 9227530, 27050101, 2504721, 23886875, -13117525, 13958495, -5732453 }, + }, + { + { -23481610, 4867226, -27247128, 3900521, 29838369, -8212291, -31889399, -10041781, 7340521, -15410068 }, + { 4646514, -8011124, -22766023, -11532654, 23184553, 8566613, 31366726, -1381061, -15066784, -10375192 }, + { -17270517, 12723032, -16993061, 14878794, 21619651, -6197576, 27584817, 3093888, -8843694, 3849921 }, + }, + { + { -9064912, 2103172, 25561640, -15125738, -5239824, 9582958, 32477045, -9017955, 5002294, -15550259 }, + { -12057553, -11177906, 21115585, -13365155, 8808712, -12030708, 16489530, 13378448, -25845716, 12741426 }, + { -5946367, 10645103, -30911586, 15390284, -3286982, -7118677, 24306472, 15852464, 28834118, -7646072 }, + }, + { + { -17335748, -9107057, -24531279, 9434953, -8472084, -583362, -13090771, 455841, 20461858, 5491305 }, + { 13669248, -16095482, -12481974, -10203039, -14569770, -11893198, -24995986, 11293807, -28588204, -9421832 }, + { 28497928, 6272777, -33022994, 14470570, 8906179, -1225630, 18504674, -14165166, 29867745, -8795943 }, + }, + { + { -16207023, 13517196, -27799630, -13697798, 24009064, -6373891, -6367600, -13175392, 22853429, -4012011 }, + { 24191378, 16712145, -13931797, 15217831, 14542237, 1646131, 18603514, -11037887, 12876623, -2112447 }, + { 17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753, 608397, 16031844, 3723494 }, + }, + { + { -28632773, 12763728, -20446446, 7577504, 33001348, -13017745, 17558842, -7872890, 23896954, -4314245 }, + { -20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064, 7229064, -9919646, -8826859 }, + { 28816045, 298879, -28165016, -15920938, 19000928, -1665890, -12680833, -2949325, -18051778, -2082915 }, + }, + { + { 16000882, -344896, 3493092, -11447198, -29504595, -13159789, 12577740, 16041268, -19715240, 7847707 }, + { 10151868, 10572098, 27312476, 7922682, 14825339, 4723128, -32855931, -6519018, -10020567, 3852848 }, + { -11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224 }, + }, + }, + { + { + { -25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391 }, + { 15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215 }, + { -23169824, -10781249, 13588192, -1628807, -3798557, -1074929, -19273607, 5402699, -29815713, -9841101 }, + }, + { + { 23190676, 2384583, -32714340, 3462154, -29903655, -1529132, -11266856, 8911517, -25205859, 2739713 }, + { 21374101, -3554250, -33524649, 9874411, 15377179, 11831242, -33529904, 6134907, 4931255, 11987849 }, + { -7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539, 13861388, -30076310, 10117930 }, + }, + { + { -29501170, -10744872, -26163768, 13051539, -25625564, 5089643, -6325503, 6704079, 12890019, 15728940 }, + { -21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376, -10428139, 12885167, 8311031 }, + { -17516482, 5352194, 10384213, -13811658, 7506451, 13453191, 26423267, 4384730, 1888765, -5435404 }, + }, + { + { -25817338, -3107312, -13494599, -3182506, 30896459, -13921729, -32251644, -12707869, -19464434, -3340243 }, + { -23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245, 14845197, 17151279, -9854116 }, + { -24830458, -12733720, -15165978, 10367250, -29530908, -265356, 22825805, -7087279, -16866484, 16176525 }, + }, + { + { -23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182, -10363426, -28746253, -10197509 }, + { -10626600, -4486402, -13320562, -5125317, 3432136, -6393229, 23632037, -1940610, 32808310, 1099883 }, + { 15030977, 5768825, -27451236, -2887299, -6427378, -15361371, -15277896, -6809350, 2051441, -15225865 }, + }, + { + { -3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398, -14154188, -22686354, 16633660 }, + { 4577086, -16752288, 13249841, -15304328, 19958763, -14537274, 18559670, -10759549, 8402478, -9864273 }, + { -28406330, -1051581, -26790155, -907698, -17212414, -11030789, 9453451, -14980072, 17983010, 9967138 }, + }, + { + { -25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990, 7806337, 17507396, 3651560 }, + { -10420457, -4118111, 14584639, 15971087, -15768321, 8861010, 26556809, -5574557, -18553322, -11357135 }, + { 2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121, 8459447, -5605463, -7621941 }, + }, + { + { -4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813, -849066, 17258084, -7977739 }, + { 18164541, -10595176, -17154882, -1542417, 19237078, -9745295, 23357533, -15217008, 26908270, 12150756 }, + { -30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819 }, + }, + }, + { + { + { -6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347 }, + { -27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028 }, + { 21260961, -8424752, -16831886, -11920822, -23677961, 3968121, -3651949, -6215466, -3556191, -7913075 }, + }, + { + { 16544754, 13250366, -16804428, 15546242, -4583003, 12757258, -2462308, -8680336, -18907032, -9662799 }, + { -2415239, -15577728, 18312303, 4964443, -15272530, -12653564, 26820651, 16690659, 25459437, -4564609 }, + { -25144690, 11425020, 28423002, -11020557, -6144921, -15826224, 9142795, -2391602, -6432418, -1644817 }, + }, + { + { -23104652, 6253476, 16964147, -3768872, -25113972, -12296437, -27457225, -16344658, 6335692, 7249989 }, + { -30333227, 13979675, 7503222, -12368314, -11956721, -4621693, -30272269, 2682242, 25993170, -12478523 }, + { 4364628, 5930691, 32304656, -10044554, -8054781, 15091131, 22857016, -10598955, 31820368, 15075278 }, + }, + { + { 31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788, -9650886, -17970238, 12833045 }, + { 19073683, 14851414, -24403169, -11860168, 7625278, 11091125, -19619190, 2074449, -9413939, 14905377 }, + { 24483667, -11935567, -2518866, -11547418, -1553130, 15355506, -25282080, 9253129, 27628530, -7555480 }, + }, + { + { 17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324, -9157582, -14110875, 15297016 }, + { 510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417, -11864220, 8683221, 2921426 }, + { 18606791, 11874196, 27155355, -5281482, -24031742, 6265446, -25178240, -1278924, 4674690, 13890525 }, + }, + { + { 13609624, 13069022, -27372361, -13055908, 24360586, 9592974, 14977157, 9835105, 4389687, 288396 }, + { 9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062, 8317628, 23388070, 16052080 }, + { 12720016, 11937594, -31970060, -5028689, 26900120, 8561328, -20155687, -11632979, -14754271, -10812892 }, + }, + { + { 15961858, 14150409, 26716931, -665832, -22794328, 13603569, 11829573, 7467844, -28822128, 929275 }, + { 11038231, -11582396, -27310482, -7316562, -10498527, -16307831, -23479533, -9371869, -21393143, 2465074 }, + { 20017163, -4323226, 27915242, 1529148, 12396362, 15675764, 13817261, -9658066, 2463391, -4622140 }, + }, + { + { -16358878, -12663911, -12065183, 4996454, -1256422, 1073572, 9583558, 12851107, 4003896, 12673717 }, + { -1731589, -15155870, -3262930, 16143082, 19294135, 13385325, 14741514, -9103726, 7903886, 2348101 }, + { 24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127 }, + }, + }, + { + { + { -12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632 }, + { -26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415 }, + { -31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345, -6118678, 30958054, 8292160 }, + }, + { + { 31429822, -13959116, 29173532, 15632448, 12174511, -2760094, 32808831, 3977186, 26143136, -3148876 }, + { 22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633, -1674433, -3758243, -2304625 }, + { -15491917, 8012313, -2514730, -12702462, -23965846, -10254029, -1612713, -1535569, -16664475, 8194478 }, + }, + { + { 27338066, -7507420, -7414224, 10140405, -19026427, -6589889, 27277191, 8855376, 28572286, 3005164 }, + { 26287124, 4821776, 25476601, -4145903, -3764513, -15788984, -18008582, 1182479, -26094821, -13079595 }, + { -7171154, 3178080, 23970071, 6201893, -17195577, -4489192, -21876275, -13982627, 32208683, -1198248 }, + }, + { + { -16657702, 2817643, -10286362, 14811298, 6024667, 13349505, -27315504, -10497842, -27672585, -11539858 }, + { 15941029, -9405932, -21367050, 8062055, 31876073, -238629, -15278393, -1444429, 15397331, -4130193 }, + { 8934485, -13485467, -23286397, -13423241, -32446090, 14047986, 31170398, -1441021, -27505566, 15087184 }, + }, + { + { -18357243, -2156491, 24524913, -16677868, 15520427, -6360776, -15502406, 11461896, 16788528, -5868942 }, + { -1947386, 16013773, 21750665, 3714552, -17401782, -16055433, -3770287, -10323320, 31322514, -11615635 }, + { 21426655, -5650218, -13648287, -5347537, -28812189, -4920970, -18275391, -14621414, 13040862, -12112948 }, + }, + { + { 11293895, 12478086, -27136401, 15083750, -29307421, 14748872, 14555558, -13417103, 1613711, 4896935 }, + { -25894883, 15323294, -8489791, -8057900, 25967126, -13425460, 2825960, -4897045, -23971776, -11267415 }, + { -15924766, -5229880, -17443532, 6410664, 3622847, 10243618, 20615400, 12405433, -23753030, -8436416 }, + }, + { + { -7091295, 12556208, -20191352, 9025187, -17072479, 4333801, 4378436, 2432030, 23097949, -566018 }, + { 4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264, 10103221, -18512313, 2424778 }, + { 366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678, 1344109, -3642553, 12412659 }, + }, + { + { -24001791, 7690286, 14929416, -168257, -32210835, -13412986, 24162697, -15326504, -3141501, 11179385 }, + { 18289522, -14724954, 8056945, 16430056, -21729724, 7842514, -6001441, -1486897, -18684645, -11443503 }, + { 476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329 }, + }, + }, + { + { + { 20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056 }, + { -13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838 }, + { 24900749, 14435722, 17209120, -15292541, -22592275, 9878983, -7689309, -16335821, -24568481, 11788948 }, + }, + { + { -3118155, -11395194, -13802089, 14797441, 9652448, -6845904, -20037437, 10410733, -24568470, -1458691 }, + { -15659161, 16736706, -22467150, 10215878, -9097177, 7563911, 11871841, -12505194, -18513325, 8464118 }, + { -23400612, 8348507, -14585951, -861714, -3950205, -6373419, 14325289, 8628612, 33313881, -8370517 }, + }, + { + { -20186973, -4967935, 22367356, 5271547, -1097117, -4788838, -24805667, -10236854, -8940735, -5818269 }, + { -6948785, -1795212, -32625683, -16021179, 32635414, -7374245, 15989197, -12838188, 28358192, -4253904 }, + { -23561781, -2799059, -32351682, -1661963, -9147719, 10429267, -16637684, 4072016, -5351664, 5596589 }, + }, + { + { -28236598, -3390048, 12312896, 6213178, 3117142, 16078565, 29266239, 2557221, 1768301, 15373193 }, + { -7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902, -4504991, -24660491, 3442910 }, + { -30210571, 5124043, 14181784, 8197961, 18964734, -11939093, 22597931, 7176455, -18585478, 13365930 }, + }, + { + { -7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107, -8570186, -9689599, -3031667 }, + { 25008904, -10771599, -4305031, -9638010, 16265036, 15721635, 683793, -11823784, 15723479, -15163481 }, + { -9660625, 12374379, -27006999, -7026148, -7724114, -12314514, 11879682, 5400171, 519526, -1235876 }, + }, + { + { 22258397, -16332233, -7869817, 14613016, -22520255, -2950923, -20353881, 7315967, 16648397, 7605640 }, + { -8081308, -8464597, -8223311, 9719710, 19259459, -15348212, 23994942, -5281555, -9468848, 4763278 }, + { -21699244, 9220969, -15730624, 1084137, -25476107, -2852390, 31088447, -7764523, -11356529, 728112 }, + }, + { + { 26047220, -11751471, -6900323, -16521798, 24092068, 9158119, -4273545, -12555558, -29365436, -5498272 }, + { 17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007, 12327945, 10750447, 10014012 }, + { -10312768, 3936952, 9156313, -8897683, 16498692, -994647, -27481051, -666732, 3424691, 7540221 }, + }, + { + { 30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422, -16317219, -9244265, 15258046 }, + { 13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406, 2711395, 1062915, -5136345 }, + { -19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310 }, + }, + }, + { + { + { 19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937 }, + { 31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636 }, + { -9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222, 9677543, -32294889, -6456008 }, + }, + { + { -2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579, -7839692, -7852844, -8138429 }, + { -15236356, -15433509, 7766470, 746860, 26346930, -10221762, -27333451, 10754588, -9431476, 5203576 }, + { 31834314, 14135496, -770007, 5159118, 20917671, -16768096, -7467973, -7337524, 31809243, 7347066 }, + }, + { + { -9606723, -11874240, 20414459, 13033986, 13716524, -11691881, 19797970, -12211255, 15192876, -2087490 }, + { -12663563, -2181719, 1168162, -3804809, 26747877, -14138091, 10609330, 12694420, 33473243, -13382104 }, + { 33184999, 11180355, 15832085, -11385430, -1633671, 225884, 15089336, -11023903, -6135662, 14480053 }, + }, + { + { 31308717, -5619998, 31030840, -1897099, 15674547, -6582883, 5496208, 13685227, 27595050, 8737275 }, + { -20318852, -15150239, 10933843, -16178022, 8335352, -7546022, -31008351, -12610604, 26498114, 66511 }, + { 22644454, -8761729, -16671776, 4884562, -3105614, -13559366, 30540766, -4286747, -13327787, -7515095 }, + }, + { + { -28017847, 9834845, 18617207, -2681312, -3401956, -13307506, 8205540, 13585437, -17127465, 15115439 }, + { 23711543, -672915, 31206561, -8362711, 6164647, -9709987, -33535882, -1426096, 8236921, 16492939 }, + { -23910559, -13515526, -26299483, -4503841, 25005590, -7687270, 19574902, 10071562, 6708380, -6222424 }, + }, + { + { 2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017, 9328700, 29955601, -11678310 }, + { 3096359, 9271816, -21620864, -15521844, -14847996, -7592937, -25892142, -12635595, -9917575, 6216608 }, + { -32615849, 338663, -25195611, 2510422, -29213566, -13820213, 24822830, -6146567, -26767480, 7525079 }, + }, + { + { -23066649, -13985623, 16133487, -7896178, -3389565, 778788, -910336, -2782495, -19386633, 11994101 }, + { 21691500, -13624626, -641331, -14367021, 3285881, -3483596, -25064666, 9718258, -7477437, 13381418 }, + { 18445390, -4202236, 14979846, 11622458, -1727110, -3582980, 23111648, -6375247, 28535282, 15779576 }, + }, + { + { 30098053, 3089662, -9234387, 16662135, -21306940, 11308411, -14068454, 12021730, 9955285, -16303356 }, + { 9734894, -14576830, -7473633, -9138735, 2060392, 11313496, -18426029, 9924399, 20194861, 13380996 }, + { -26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099 }, + }, + }, + { + { + { -26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728 }, + { -13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658 }, + { -10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944, 6114064, 33514190, 2333242 }, + }, + { + { -21433588, -12421821, 8119782, 7219913, -21830522, -9016134, -6679750, -12670638, 24350578, -13450001 }, + { -4116307, -11271533, -23886186, 4843615, -30088339, 690623, -31536088, -10406836, 8317860, 12352766 }, + { 18200138, -14475911, -33087759, -2696619, -23702521, -9102511, -23552096, -2287550, 20712163, 6719373 }, + }, + { + { 26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530, -3763210, 26224235, -3297458 }, + { -17168938, -14854097, -3395676, -16369877, -19954045, 14050420, 21728352, 9493610, 18620611, -16428628 }, + { -13323321, 13325349, 11432106, 5964811, 18609221, 6062965, -5269471, -9725556, -30701573, -16479657 }, + }, + { + { -23860538, -11233159, 26961357, 1640861, -32413112, -16737940, 12248509, -5240639, 13735342, 1934062 }, + { 25089769, 6742589, 17081145, -13406266, 21909293, -16067981, -15136294, -3765346, -21277997, 5473616 }, + { 31883677, -7961101, 1083432, -11572403, 22828471, 13290673, -7125085, 12469656, 29111212, -5451014 }, + }, + { + { 24244947, -15050407, -26262976, 2791540, -14997599, 16666678, 24367466, 6388839, -10295587, 452383 }, + { -25640782, -3417841, 5217916, 16224624, 19987036, -4082269, -24236251, -5915248, 15766062, 8407814 }, + { -20406999, 13990231, 15495425, 16395525, 5377168, 15166495, -8917023, -4388953, -8067909, 2276718 }, + }, + { + { 30157918, 12924066, -17712050, 9245753, 19895028, 3368142, -23827587, 5096219, 22740376, -7303417 }, + { 2041139, -14256350, 7783687, 13876377, -25946985, -13352459, 24051124, 13742383, -15637599, 13295222 }, + { 33338237, -8505733, 12532113, 7977527, 9106186, -1715251, -17720195, -4612972, -4451357, -14669444 }, + }, + { + { -20045281, 5454097, -14346548, 6447146, 28862071, 1883651, -2469266, -4141880, 7770569, 9620597 }, + { 23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528, -1694323, -33502340, -14767970 }, + { 1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801, 1220118, 30494170, -11440799 }, + }, + { + { -5037580, -13028295, -2970559, -3061767, 15640974, -6701666, -26739026, 926050, -1684339, -13333647 }, + { 13908495, -3549272, 30919928, -6273825, -21521863, 7989039, 9021034, 9078865, 3353509, 4033511 }, + { -29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032 }, + }, + }, + { + { + { 9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834 }, + { -23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461 }, + { 29262576, 16756590, 26350592, -8793563, 8529671, -11208050, 13617293, -9937143, 11465739, 8317062 }, + }, + { + { -25493081, -6962928, 32500200, -9419051, -23038724, -2302222, 14898637, 3848455, 20969334, -5157516 }, + { -20384450, -14347713, -18336405, 13884722, -33039454, 2842114, -21610826, -3649888, 11177095, 14989547 }, + { -24496721, -11716016, 16959896, 2278463, 12066309, 10137771, 13515641, 2581286, -28487508, 9930240 }, + }, + { + { -17751622, -2097826, 16544300, -13009300, -15914807, -14949081, 18345767, -13403753, 16291481, -5314038 }, + { -33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774, 6957617, 4368891, 9788741 }, + { 16660756, 7281060, -10830758, 12911820, 20108584, -8101676, -21722536, -8613148, 16250552, -11111103 }, + }, + { + { -19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584, 10604807, -30190403, 4782747 }, + { -1354539, 14736941, -7367442, -13292886, 7710542, -14155590, -9981571, 4383045, 22546403, 437323 }, + { 31665577, -12180464, -16186830, 1491339, -18368625, 3294682, 27343084, 2786261, -30633590, -14097016 }, + }, + { + { -14467279, -683715, -33374107, 7448552, 19294360, 14334329, -19690631, 2355319, -19284671, -6114373 }, + { 15121312, -15796162, 6377020, -6031361, -10798111, -12957845, 18952177, 15496498, -29380133, 11754228 }, + { -2637277, -13483075, 8488727, -14303896, 12728761, -1622493, 7141596, 11724556, 22761615, -10134141 }, + }, + { + { 16918416, 11729663, -18083579, 3022987, -31015732, -13339659, -28741185, -12227393, 32851222, 11717399 }, + { 11166634, 7338049, -6722523, 4531520, -29468672, -7302055, 31474879, 3483633, -1193175, -4030831 }, + { -185635, 9921305, 31456609, -13536438, -12013818, 13348923, 33142652, 6546660, -19985279, -3948376 }, + }, + { + { -32460596, 11266712, -11197107, -7899103, 31703694, 3855903, -8537131, -12833048, -30772034, -15486313 }, + { -18006477, 12709068, 3991746, -6479188, -21491523, -10550425, -31135347, -16049879, 10928917, 3011958 }, + { -6957757, -15594337, 31696059, 334240, 29576716, 14796075, -30831056, -12805180, 18008031, 10258577 }, + }, + { + { -22448644, 15655569, 7018479, -4410003, -30314266, -1201591, -1853465, 1367120, 25127874, 6671743 }, + { 29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684 }, + { -20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476 }, + }, + }, +}; diff --git a/src/cryptoconditions/src/include/ed25519/src/sc.c b/src/cryptoconditions/src/include/ed25519/src/sc.c new file mode 100644 index 000000000..ca5bad2ca --- /dev/null +++ b/src/cryptoconditions/src/include/ed25519/src/sc.c @@ -0,0 +1,809 @@ +#include "fixedint.h" +#include "sc.h" + +static uint64_t load_3(const unsigned char *in) { + uint64_t result; + + result = (uint64_t) in[0]; + result |= ((uint64_t) in[1]) << 8; + result |= ((uint64_t) in[2]) << 16; + + return result; +} + +static uint64_t load_4(const unsigned char *in) { + uint64_t result; + + result = (uint64_t) in[0]; + result |= ((uint64_t) in[1]) << 8; + result |= ((uint64_t) in[2]) << 16; + result |= ((uint64_t) in[3]) << 24; + + return result; +} + +/* +Input: + s[0]+256*s[1]+...+256^63*s[63] = s + +Output: + s[0]+256*s[1]+...+256^31*s[31] = s mod l + where l = 2^252 + 27742317777372353535851937790883648493. + Overwrites s in place. +*/ + +void sc_reduce(unsigned char *s) { + int64_t s0 = 2097151 & load_3(s); + int64_t s1 = 2097151 & (load_4(s + 2) >> 5); + int64_t s2 = 2097151 & (load_3(s + 5) >> 2); + int64_t s3 = 2097151 & (load_4(s + 7) >> 7); + int64_t s4 = 2097151 & (load_4(s + 10) >> 4); + int64_t s5 = 2097151 & (load_3(s + 13) >> 1); + int64_t s6 = 2097151 & (load_4(s + 15) >> 6); + int64_t s7 = 2097151 & (load_3(s + 18) >> 3); + int64_t s8 = 2097151 & load_3(s + 21); + int64_t s9 = 2097151 & (load_4(s + 23) >> 5); + int64_t s10 = 2097151 & (load_3(s + 26) >> 2); + int64_t s11 = 2097151 & (load_4(s + 28) >> 7); + int64_t s12 = 2097151 & (load_4(s + 31) >> 4); + int64_t s13 = 2097151 & (load_3(s + 34) >> 1); + int64_t s14 = 2097151 & (load_4(s + 36) >> 6); + int64_t s15 = 2097151 & (load_3(s + 39) >> 3); + int64_t s16 = 2097151 & load_3(s + 42); + int64_t s17 = 2097151 & (load_4(s + 44) >> 5); + int64_t s18 = 2097151 & (load_3(s + 47) >> 2); + int64_t s19 = 2097151 & (load_4(s + 49) >> 7); + int64_t s20 = 2097151 & (load_4(s + 52) >> 4); + int64_t s21 = 2097151 & (load_3(s + 55) >> 1); + int64_t s22 = 2097151 & (load_4(s + 57) >> 6); + int64_t s23 = (load_4(s + 60) >> 3); + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + int64_t carry10; + int64_t carry11; + int64_t carry12; + int64_t carry13; + int64_t carry14; + int64_t carry15; + int64_t carry16; + + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + s23 = 0; + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + s22 = 0; + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + s21 = 0; + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + s20 = 0; + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + s19 = 0; + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + s18 = 0; + carry6 = (s6 + (1 << 20)) >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry12 = (s12 + (1 << 20)) >> 21; + s13 += carry12; + s12 -= carry12 << 21; + carry14 = (s14 + (1 << 20)) >> 21; + s15 += carry14; + s14 -= carry14 << 21; + carry16 = (s16 + (1 << 20)) >> 21; + s17 += carry16; + s16 -= carry16 << 21; + carry7 = (s7 + (1 << 20)) >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; + s12 += carry11; + s11 -= carry11 << 21; + carry13 = (s13 + (1 << 20)) >> 21; + s14 += carry13; + s13 -= carry13 << 21; + carry15 = (s15 + (1 << 20)) >> 21; + s16 += carry15; + s15 -= carry15 << 21; + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + s17 = 0; + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + s16 = 0; + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + s15 = 0; + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + s14 = 0; + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + s13 = 0; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = (s0 + (1 << 20)) >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry2 = (s2 + (1 << 20)) >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry4 = (s4 + (1 << 20)) >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry6 = (s6 + (1 << 20)) >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry1 = (s1 + (1 << 20)) >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry3 = (s3 + (1 << 20)) >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry5 = (s5 + (1 << 20)) >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry7 = (s7 + (1 << 20)) >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; + s12 += carry11; + s11 -= carry11 << 21; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry11 = s11 >> 21; + s12 += carry11; + s11 -= carry11 << 21; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 << 21; + + s[0] = (unsigned char) (s0 >> 0); + s[1] = (unsigned char) (s0 >> 8); + s[2] = (unsigned char) ((s0 >> 16) | (s1 << 5)); + s[3] = (unsigned char) (s1 >> 3); + s[4] = (unsigned char) (s1 >> 11); + s[5] = (unsigned char) ((s1 >> 19) | (s2 << 2)); + s[6] = (unsigned char) (s2 >> 6); + s[7] = (unsigned char) ((s2 >> 14) | (s3 << 7)); + s[8] = (unsigned char) (s3 >> 1); + s[9] = (unsigned char) (s3 >> 9); + s[10] = (unsigned char) ((s3 >> 17) | (s4 << 4)); + s[11] = (unsigned char) (s4 >> 4); + s[12] = (unsigned char) (s4 >> 12); + s[13] = (unsigned char) ((s4 >> 20) | (s5 << 1)); + s[14] = (unsigned char) (s5 >> 7); + s[15] = (unsigned char) ((s5 >> 15) | (s6 << 6)); + s[16] = (unsigned char) (s6 >> 2); + s[17] = (unsigned char) (s6 >> 10); + s[18] = (unsigned char) ((s6 >> 18) | (s7 << 3)); + s[19] = (unsigned char) (s7 >> 5); + s[20] = (unsigned char) (s7 >> 13); + s[21] = (unsigned char) (s8 >> 0); + s[22] = (unsigned char) (s8 >> 8); + s[23] = (unsigned char) ((s8 >> 16) | (s9 << 5)); + s[24] = (unsigned char) (s9 >> 3); + s[25] = (unsigned char) (s9 >> 11); + s[26] = (unsigned char) ((s9 >> 19) | (s10 << 2)); + s[27] = (unsigned char) (s10 >> 6); + s[28] = (unsigned char) ((s10 >> 14) | (s11 << 7)); + s[29] = (unsigned char) (s11 >> 1); + s[30] = (unsigned char) (s11 >> 9); + s[31] = (unsigned char) (s11 >> 17); +} + + + +/* +Input: + a[0]+256*a[1]+...+256^31*a[31] = a + b[0]+256*b[1]+...+256^31*b[31] = b + c[0]+256*c[1]+...+256^31*c[31] = c + +Output: + s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l + where l = 2^252 + 27742317777372353535851937790883648493. +*/ + +void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c) { + int64_t a0 = 2097151 & load_3(a); + int64_t a1 = 2097151 & (load_4(a + 2) >> 5); + int64_t a2 = 2097151 & (load_3(a + 5) >> 2); + int64_t a3 = 2097151 & (load_4(a + 7) >> 7); + int64_t a4 = 2097151 & (load_4(a + 10) >> 4); + int64_t a5 = 2097151 & (load_3(a + 13) >> 1); + int64_t a6 = 2097151 & (load_4(a + 15) >> 6); + int64_t a7 = 2097151 & (load_3(a + 18) >> 3); + int64_t a8 = 2097151 & load_3(a + 21); + int64_t a9 = 2097151 & (load_4(a + 23) >> 5); + int64_t a10 = 2097151 & (load_3(a + 26) >> 2); + int64_t a11 = (load_4(a + 28) >> 7); + int64_t b0 = 2097151 & load_3(b); + int64_t b1 = 2097151 & (load_4(b + 2) >> 5); + int64_t b2 = 2097151 & (load_3(b + 5) >> 2); + int64_t b3 = 2097151 & (load_4(b + 7) >> 7); + int64_t b4 = 2097151 & (load_4(b + 10) >> 4); + int64_t b5 = 2097151 & (load_3(b + 13) >> 1); + int64_t b6 = 2097151 & (load_4(b + 15) >> 6); + int64_t b7 = 2097151 & (load_3(b + 18) >> 3); + int64_t b8 = 2097151 & load_3(b + 21); + int64_t b9 = 2097151 & (load_4(b + 23) >> 5); + int64_t b10 = 2097151 & (load_3(b + 26) >> 2); + int64_t b11 = (load_4(b + 28) >> 7); + int64_t c0 = 2097151 & load_3(c); + int64_t c1 = 2097151 & (load_4(c + 2) >> 5); + int64_t c2 = 2097151 & (load_3(c + 5) >> 2); + int64_t c3 = 2097151 & (load_4(c + 7) >> 7); + int64_t c4 = 2097151 & (load_4(c + 10) >> 4); + int64_t c5 = 2097151 & (load_3(c + 13) >> 1); + int64_t c6 = 2097151 & (load_4(c + 15) >> 6); + int64_t c7 = 2097151 & (load_3(c + 18) >> 3); + int64_t c8 = 2097151 & load_3(c + 21); + int64_t c9 = 2097151 & (load_4(c + 23) >> 5); + int64_t c10 = 2097151 & (load_3(c + 26) >> 2); + int64_t c11 = (load_4(c + 28) >> 7); + int64_t s0; + int64_t s1; + int64_t s2; + int64_t s3; + int64_t s4; + int64_t s5; + int64_t s6; + int64_t s7; + int64_t s8; + int64_t s9; + int64_t s10; + int64_t s11; + int64_t s12; + int64_t s13; + int64_t s14; + int64_t s15; + int64_t s16; + int64_t s17; + int64_t s18; + int64_t s19; + int64_t s20; + int64_t s21; + int64_t s22; + int64_t s23; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + int64_t carry10; + int64_t carry11; + int64_t carry12; + int64_t carry13; + int64_t carry14; + int64_t carry15; + int64_t carry16; + int64_t carry17; + int64_t carry18; + int64_t carry19; + int64_t carry20; + int64_t carry21; + int64_t carry22; + + s0 = c0 + a0 * b0; + s1 = c1 + a0 * b1 + a1 * b0; + s2 = c2 + a0 * b2 + a1 * b1 + a2 * b0; + s3 = c3 + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0; + s4 = c4 + a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0; + s5 = c5 + a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0; + s6 = c6 + a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + a6 * b0; + s7 = c7 + a0 * b7 + a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 + a6 * b1 + a7 * b0; + s8 = c8 + a0 * b8 + a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 + a6 * b2 + a7 * b1 + a8 * b0; + s9 = c9 + a0 * b9 + a1 * b8 + a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 + a6 * b3 + a7 * b2 + a8 * b1 + a9 * b0; + s10 = c10 + a0 * b10 + a1 * b9 + a2 * b8 + a3 * b7 + a4 * b6 + a5 * b5 + a6 * b4 + a7 * b3 + a8 * b2 + a9 * b1 + a10 * b0; + s11 = c11 + a0 * b11 + a1 * b10 + a2 * b9 + a3 * b8 + a4 * b7 + a5 * b6 + a6 * b5 + a7 * b4 + a8 * b3 + a9 * b2 + a10 * b1 + a11 * b0; + s12 = a1 * b11 + a2 * b10 + a3 * b9 + a4 * b8 + a5 * b7 + a6 * b6 + a7 * b5 + a8 * b4 + a9 * b3 + a10 * b2 + a11 * b1; + s13 = a2 * b11 + a3 * b10 + a4 * b9 + a5 * b8 + a6 * b7 + a7 * b6 + a8 * b5 + a9 * b4 + a10 * b3 + a11 * b2; + s14 = a3 * b11 + a4 * b10 + a5 * b9 + a6 * b8 + a7 * b7 + a8 * b6 + a9 * b5 + a10 * b4 + a11 * b3; + s15 = a4 * b11 + a5 * b10 + a6 * b9 + a7 * b8 + a8 * b7 + a9 * b6 + a10 * b5 + a11 * b4; + s16 = a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5; + s17 = a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6; + s18 = a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7; + s19 = a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8; + s20 = a9 * b11 + a10 * b10 + a11 * b9; + s21 = a10 * b11 + a11 * b10; + s22 = a11 * b11; + s23 = 0; + carry0 = (s0 + (1 << 20)) >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry2 = (s2 + (1 << 20)) >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry4 = (s4 + (1 << 20)) >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry6 = (s6 + (1 << 20)) >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry12 = (s12 + (1 << 20)) >> 21; + s13 += carry12; + s12 -= carry12 << 21; + carry14 = (s14 + (1 << 20)) >> 21; + s15 += carry14; + s14 -= carry14 << 21; + carry16 = (s16 + (1 << 20)) >> 21; + s17 += carry16; + s16 -= carry16 << 21; + carry18 = (s18 + (1 << 20)) >> 21; + s19 += carry18; + s18 -= carry18 << 21; + carry20 = (s20 + (1 << 20)) >> 21; + s21 += carry20; + s20 -= carry20 << 21; + carry22 = (s22 + (1 << 20)) >> 21; + s23 += carry22; + s22 -= carry22 << 21; + carry1 = (s1 + (1 << 20)) >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry3 = (s3 + (1 << 20)) >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry5 = (s5 + (1 << 20)) >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry7 = (s7 + (1 << 20)) >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; + s12 += carry11; + s11 -= carry11 << 21; + carry13 = (s13 + (1 << 20)) >> 21; + s14 += carry13; + s13 -= carry13 << 21; + carry15 = (s15 + (1 << 20)) >> 21; + s16 += carry15; + s15 -= carry15 << 21; + carry17 = (s17 + (1 << 20)) >> 21; + s18 += carry17; + s17 -= carry17 << 21; + carry19 = (s19 + (1 << 20)) >> 21; + s20 += carry19; + s19 -= carry19 << 21; + carry21 = (s21 + (1 << 20)) >> 21; + s22 += carry21; + s21 -= carry21 << 21; + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + s23 = 0; + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + s22 = 0; + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + s21 = 0; + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + s20 = 0; + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + s19 = 0; + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + s18 = 0; + carry6 = (s6 + (1 << 20)) >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry12 = (s12 + (1 << 20)) >> 21; + s13 += carry12; + s12 -= carry12 << 21; + carry14 = (s14 + (1 << 20)) >> 21; + s15 += carry14; + s14 -= carry14 << 21; + carry16 = (s16 + (1 << 20)) >> 21; + s17 += carry16; + s16 -= carry16 << 21; + carry7 = (s7 + (1 << 20)) >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; + s12 += carry11; + s11 -= carry11 << 21; + carry13 = (s13 + (1 << 20)) >> 21; + s14 += carry13; + s13 -= carry13 << 21; + carry15 = (s15 + (1 << 20)) >> 21; + s16 += carry15; + s15 -= carry15 << 21; + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + s17 = 0; + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + s16 = 0; + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + s15 = 0; + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + s14 = 0; + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + s13 = 0; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = (s0 + (1 << 20)) >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry2 = (s2 + (1 << 20)) >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry4 = (s4 + (1 << 20)) >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry6 = (s6 + (1 << 20)) >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry1 = (s1 + (1 << 20)) >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry3 = (s3 + (1 << 20)) >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry5 = (s5 + (1 << 20)) >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry7 = (s7 + (1 << 20)) >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; + s12 += carry11; + s11 -= carry11 << 21; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry11 = s11 >> 21; + s12 += carry11; + s11 -= carry11 << 21; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 << 21; + + s[0] = (unsigned char) (s0 >> 0); + s[1] = (unsigned char) (s0 >> 8); + s[2] = (unsigned char) ((s0 >> 16) | (s1 << 5)); + s[3] = (unsigned char) (s1 >> 3); + s[4] = (unsigned char) (s1 >> 11); + s[5] = (unsigned char) ((s1 >> 19) | (s2 << 2)); + s[6] = (unsigned char) (s2 >> 6); + s[7] = (unsigned char) ((s2 >> 14) | (s3 << 7)); + s[8] = (unsigned char) (s3 >> 1); + s[9] = (unsigned char) (s3 >> 9); + s[10] = (unsigned char) ((s3 >> 17) | (s4 << 4)); + s[11] = (unsigned char) (s4 >> 4); + s[12] = (unsigned char) (s4 >> 12); + s[13] = (unsigned char) ((s4 >> 20) | (s5 << 1)); + s[14] = (unsigned char) (s5 >> 7); + s[15] = (unsigned char) ((s5 >> 15) | (s6 << 6)); + s[16] = (unsigned char) (s6 >> 2); + s[17] = (unsigned char) (s6 >> 10); + s[18] = (unsigned char) ((s6 >> 18) | (s7 << 3)); + s[19] = (unsigned char) (s7 >> 5); + s[20] = (unsigned char) (s7 >> 13); + s[21] = (unsigned char) (s8 >> 0); + s[22] = (unsigned char) (s8 >> 8); + s[23] = (unsigned char) ((s8 >> 16) | (s9 << 5)); + s[24] = (unsigned char) (s9 >> 3); + s[25] = (unsigned char) (s9 >> 11); + s[26] = (unsigned char) ((s9 >> 19) | (s10 << 2)); + s[27] = (unsigned char) (s10 >> 6); + s[28] = (unsigned char) ((s10 >> 14) | (s11 << 7)); + s[29] = (unsigned char) (s11 >> 1); + s[30] = (unsigned char) (s11 >> 9); + s[31] = (unsigned char) (s11 >> 17); +} diff --git a/src/cryptoconditions/src/include/ed25519/src/sc.h b/src/cryptoconditions/src/include/ed25519/src/sc.h new file mode 100644 index 000000000..e29e7fa5a --- /dev/null +++ b/src/cryptoconditions/src/include/ed25519/src/sc.h @@ -0,0 +1,12 @@ +#ifndef SC_H +#define SC_H + +/* +The set of scalars is \Z/l +where l = 2^252 + 27742317777372353535851937790883648493. +*/ + +void sc_reduce(unsigned char *s); +void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c); + +#endif diff --git a/src/cryptoconditions/src/include/ed25519/src/seed.c b/src/cryptoconditions/src/include/ed25519/src/seed.c new file mode 100644 index 000000000..11a2e3ec4 --- /dev/null +++ b/src/cryptoconditions/src/include/ed25519/src/seed.c @@ -0,0 +1,40 @@ +#include "ed25519.h" + +#ifndef ED25519_NO_SEED + +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +int ed25519_create_seed(unsigned char *seed) { +#ifdef _WIN32 + HCRYPTPROV prov; + + if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { + return 1; + } + + if (!CryptGenRandom(prov, 32, seed)) { + CryptReleaseContext(prov, 0); + return 1; + } + + CryptReleaseContext(prov, 0); +#else + FILE *f = fopen("/dev/urandom", "rb"); + + if (f == NULL) { + return 1; + } + + fread(seed, 1, 32, f); + fclose(f); +#endif + + return 0; +} + +#endif diff --git a/src/cryptoconditions/src/include/ed25519/src/sha512.c b/src/cryptoconditions/src/include/ed25519/src/sha512.c new file mode 100644 index 000000000..cb8ae7175 --- /dev/null +++ b/src/cryptoconditions/src/include/ed25519/src/sha512.c @@ -0,0 +1,275 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#include "fixedint.h" +#include "sha512.h" + +/* the K array */ +static const uint64_t K[80] = { + UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd), + UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc), + UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019), + UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118), + UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe), + UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2), + UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1), + UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694), + UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3), + UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65), + UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483), + UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5), + UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210), + UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4), + UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725), + UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70), + UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926), + UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df), + UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8), + UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b), + UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001), + UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30), + UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910), + UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8), + UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53), + UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8), + UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb), + UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3), + UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60), + UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec), + UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9), + UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b), + UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207), + UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178), + UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6), + UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b), + UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493), + UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c), + UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a), + UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817) +}; + +/* Various logical functions */ + +#define ROR64c(x, y) \ + ( ((((x)&UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)(y)&UINT64_C(63))) | \ + ((x)<<((uint64_t)(64-((y)&UINT64_C(63)))))) & UINT64_C(0xFFFFFFFFFFFFFFFF)) + +#define STORE64H(x, y) \ + { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \ + (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \ + (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ + (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } + +#define LOAD64H(x, y) \ + { x = (((uint64_t)((y)[0] & 255))<<56)|(((uint64_t)((y)[1] & 255))<<48) | \ + (((uint64_t)((y)[2] & 255))<<40)|(((uint64_t)((y)[3] & 255))<<32) | \ + (((uint64_t)((y)[4] & 255))<<24)|(((uint64_t)((y)[5] & 255))<<16) | \ + (((uint64_t)((y)[6] & 255))<<8)|(((uint64_t)((y)[7] & 255))); } + + +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) ROR64c(x, n) +#define R(x, n) (((x) &UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)n)) +#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39)) +#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41)) +#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) +#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6)) +#ifndef MIN + #define MIN(x, y) ( ((x)<(y))?(x):(y) ) +#endif + +/* compress 1024-bits */ +static int sha512_compress(sha512_context *md, unsigned char *buf) +{ + uint64_t S[8], W[80], t0, t1; + int i; + + /* copy state into S */ + for (i = 0; i < 8; i++) { + S[i] = md->state[i]; + } + + /* copy the state into 1024-bits into W[0..15] */ + for (i = 0; i < 16; i++) { + LOAD64H(W[i], buf + (8*i)); + } + + /* fill W[16..79] */ + for (i = 16; i < 80; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; + } + +/* Compress */ + #define RND(a,b,c,d,e,f,g,h,i) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c);\ + d += t0; \ + h = t0 + t1; + + for (i = 0; i < 80; i += 8) { + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7); + } + + #undef RND + + + + /* feedback */ + for (i = 0; i < 8; i++) { + md->state[i] = md->state[i] + S[i]; + } + + return 0; +} + + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return 0 if successful +*/ +int sha512_init(sha512_context * md) { + if (md == NULL) return 1; + + md->curlen = 0; + md->length = 0; + md->state[0] = UINT64_C(0x6a09e667f3bcc908); + md->state[1] = UINT64_C(0xbb67ae8584caa73b); + md->state[2] = UINT64_C(0x3c6ef372fe94f82b); + md->state[3] = UINT64_C(0xa54ff53a5f1d36f1); + md->state[4] = UINT64_C(0x510e527fade682d1); + md->state[5] = UINT64_C(0x9b05688c2b3e6c1f); + md->state[6] = UINT64_C(0x1f83d9abfb41bd6b); + md->state[7] = UINT64_C(0x5be0cd19137e2179); + + return 0; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return 0 if successful +*/ +int sha512_update (sha512_context * md, const unsigned char *in, size_t inlen) +{ + size_t n; + size_t i; + int err; + if (md == NULL) return 1; + if (in == NULL) return 1; + if (md->curlen > sizeof(md->buf)) { + return 1; + } + while (inlen > 0) { + if (md->curlen == 0 && inlen >= 128) { + if ((err = sha512_compress (md, (unsigned char *)in)) != 0) { + return err; + } + md->length += 128 * 8; + in += 128; + inlen -= 128; + } else { + n = MIN(inlen, (128 - md->curlen)); + + for (i = 0; i < n; i++) { + md->buf[i + md->curlen] = in[i]; + } + + + md->curlen += n; + in += n; + inlen -= n; + if (md->curlen == 128) { + if ((err = sha512_compress (md, md->buf)) != 0) { + return err; + } + md->length += 8*128; + md->curlen = 0; + } + } + } + return 0; +} + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (64 bytes) + @return 0 if successful +*/ + int sha512_final(sha512_context * md, unsigned char *out) + { + int i; + + if (md == NULL) return 1; + if (out == NULL) return 1; + + if (md->curlen >= sizeof(md->buf)) { + return 1; + } + + /* increase the length of the message */ + md->length += md->curlen * UINT64_C(8); + + /* append the '1' bit */ + md->buf[md->curlen++] = (unsigned char)0x80; + + /* if the length is currently above 112 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->curlen > 112) { + while (md->curlen < 128) { + md->buf[md->curlen++] = (unsigned char)0; + } + sha512_compress(md, md->buf); + md->curlen = 0; + } + + /* pad upto 120 bytes of zeroes + * note: that from 112 to 120 is the 64 MSB of the length. We assume that you won't hash + * > 2^64 bits of data... :-) + */ +while (md->curlen < 120) { + md->buf[md->curlen++] = (unsigned char)0; +} + + /* store length */ +STORE64H(md->length, md->buf+120); +sha512_compress(md, md->buf); + + /* copy output */ +for (i = 0; i < 8; i++) { + STORE64H(md->state[i], out+(8*i)); +} + +return 0; +} + +int sha512(const unsigned char *message, size_t message_len, unsigned char *out) +{ + sha512_context ctx; + int ret; + if ((ret = sha512_init(&ctx))) return ret; + if ((ret = sha512_update(&ctx, message, message_len))) return ret; + if ((ret = sha512_final(&ctx, out))) return ret; + return 0; +} diff --git a/src/cryptoconditions/src/include/ed25519/src/sha512.h b/src/cryptoconditions/src/include/ed25519/src/sha512.h new file mode 100644 index 000000000..a34dd5e42 --- /dev/null +++ b/src/cryptoconditions/src/include/ed25519/src/sha512.h @@ -0,0 +1,21 @@ +#ifndef SHA512_H +#define SHA512_H + +#include + +#include "fixedint.h" + +/* state */ +typedef struct sha512_context_ { + uint64_t length, state[8]; + size_t curlen; + unsigned char buf[128]; +} sha512_context; + + +int sha512_init(sha512_context * md); +int sha512_final(sha512_context * md, unsigned char *out); +int sha512_update(sha512_context * md, const unsigned char *in, size_t inlen); +int sha512(const unsigned char *message, size_t message_len, unsigned char *out); + +#endif diff --git a/src/cryptoconditions/src/include/ed25519/src/sign.c b/src/cryptoconditions/src/include/ed25519/src/sign.c new file mode 100644 index 000000000..199a8393b --- /dev/null +++ b/src/cryptoconditions/src/include/ed25519/src/sign.c @@ -0,0 +1,31 @@ +#include "ed25519.h" +#include "sha512.h" +#include "ge.h" +#include "sc.h" + + +void ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key) { + sha512_context hash; + unsigned char hram[64]; + unsigned char r[64]; + ge_p3 R; + + + sha512_init(&hash); + sha512_update(&hash, private_key + 32, 32); + sha512_update(&hash, message, message_len); + sha512_final(&hash, r); + + sc_reduce(r); + ge_scalarmult_base(&R, r); + ge_p3_tobytes(signature, &R); + + sha512_init(&hash); + sha512_update(&hash, signature, 32); + sha512_update(&hash, public_key, 32); + sha512_update(&hash, message, message_len); + sha512_final(&hash, hram); + + sc_reduce(hram); + sc_muladd(signature + 32, hram, private_key, r); +} diff --git a/src/cryptoconditions/src/include/ed25519/src/verify.c b/src/cryptoconditions/src/include/ed25519/src/verify.c new file mode 100644 index 000000000..32f988edc --- /dev/null +++ b/src/cryptoconditions/src/include/ed25519/src/verify.c @@ -0,0 +1,77 @@ +#include "ed25519.h" +#include "sha512.h" +#include "ge.h" +#include "sc.h" + +static int consttime_equal(const unsigned char *x, const unsigned char *y) { + unsigned char r = 0; + + r = x[0] ^ y[0]; + #define F(i) r |= x[i] ^ y[i] + F(1); + F(2); + F(3); + F(4); + F(5); + F(6); + F(7); + F(8); + F(9); + F(10); + F(11); + F(12); + F(13); + F(14); + F(15); + F(16); + F(17); + F(18); + F(19); + F(20); + F(21); + F(22); + F(23); + F(24); + F(25); + F(26); + F(27); + F(28); + F(29); + F(30); + F(31); + #undef F + + return !r; +} + +int ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key) { + unsigned char h[64]; + unsigned char checker[32]; + sha512_context hash; + ge_p3 A; + ge_p2 R; + + if (signature[63] & 224) { + return 0; + } + + if (ge_frombytes_negate_vartime(&A, public_key) != 0) { + return 0; + } + + sha512_init(&hash); + sha512_update(&hash, signature, 32); + sha512_update(&hash, public_key, 32); + sha512_update(&hash, message, message_len); + sha512_final(&hash, h); + + sc_reduce(h); + ge_double_scalarmult_vartime(&R, h, &A, signature + 32); + ge_tobytes(checker, &R); + + if (!consttime_equal(checker, signature)) { + return 0; + } + + return 1; +} diff --git a/src/cryptoconditions/src/include/ed25519/test.c b/src/cryptoconditions/src/include/ed25519/test.c new file mode 100644 index 000000000..e2159a9af --- /dev/null +++ b/src/cryptoconditions/src/include/ed25519/test.c @@ -0,0 +1,150 @@ +#include +#include +#include +#include + +/* #define ED25519_DLL */ +#include "src/ed25519.h" + +#include "src/ge.h" +#include "src/sc.h" + + +int main() { + unsigned char public_key[32], private_key[64], seed[32], scalar[32]; + unsigned char other_public_key[32], other_private_key[64]; + unsigned char shared_secret[32], other_shared_secret[32]; + unsigned char signature[64]; + + clock_t start; + clock_t end; + int i; + + const unsigned char message[] = "Hello, world!"; + const int message_len = strlen((char*) message); + + /* create a random seed, and a keypair out of that seed */ + ed25519_create_seed(seed); + ed25519_create_keypair(public_key, private_key, seed); + + /* create signature on the message with the keypair */ + ed25519_sign(signature, message, message_len, public_key, private_key); + + /* verify the signature */ + if (ed25519_verify(signature, message, message_len, public_key)) { + printf("valid signature\n"); + } else { + printf("invalid signature\n"); + } + + /* create scalar and add it to the keypair */ + ed25519_create_seed(scalar); + ed25519_add_scalar(public_key, private_key, scalar); + + /* create signature with the new keypair */ + ed25519_sign(signature, message, message_len, public_key, private_key); + + /* verify the signature with the new keypair */ + if (ed25519_verify(signature, message, message_len, public_key)) { + printf("valid signature\n"); + } else { + printf("invalid signature\n"); + } + + /* make a slight adjustment and verify again */ + signature[44] ^= 0x10; + if (ed25519_verify(signature, message, message_len, public_key)) { + printf("did not detect signature change\n"); + } else { + printf("correctly detected signature change\n"); + } + + /* generate two keypairs for testing key exchange */ + ed25519_create_seed(seed); + ed25519_create_keypair(public_key, private_key, seed); + ed25519_create_seed(seed); + ed25519_create_keypair(other_public_key, other_private_key, seed); + + /* create two shared secrets - from both perspectives - and check if they're equal */ + ed25519_key_exchange(shared_secret, other_public_key, private_key); + ed25519_key_exchange(other_shared_secret, public_key, other_private_key); + + for (i = 0; i < 32; ++i) { + if (shared_secret[i] != other_shared_secret[i]) { + printf("key exchange was incorrect\n"); + break; + } + } + + if (i == 32) { + printf("key exchange was correct\n"); + } + + /* test performance */ + printf("testing seed generation performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_create_seed(seed); + } + end = clock(); + + printf("%fus per seed\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + + printf("testing key generation performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_create_keypair(public_key, private_key, seed); + } + end = clock(); + + printf("%fus per keypair\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + printf("testing sign performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_sign(signature, message, message_len, public_key, private_key); + } + end = clock(); + + printf("%fus per signature\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + printf("testing verify performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_verify(signature, message, message_len, public_key); + } + end = clock(); + + printf("%fus per signature\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + + printf("testing keypair scalar addition performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_add_scalar(public_key, private_key, scalar); + } + end = clock(); + + printf("%fus per keypair\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + printf("testing public key scalar addition performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_add_scalar(public_key, NULL, scalar); + } + end = clock(); + + printf("%fus per key\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + printf("testing key exchange performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_key_exchange(shared_secret, other_public_key, private_key); + } + end = clock(); + + printf("%fus per shared secret\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + return 0; +} diff --git a/src/cryptoconditions/src/include/libbase58.h b/src/cryptoconditions/src/include/libbase58.h new file mode 100644 index 000000000..fafe6539f --- /dev/null +++ b/src/cryptoconditions/src/include/libbase58.h @@ -0,0 +1,23 @@ +#ifndef LIBBASE58_H +#define LIBBASE58_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern bool (*b58_sha256_impl)(void *, const void *, size_t); + +extern bool b58tobin(void *bin, size_t *binsz, const char *b58, size_t b58sz); +extern int b58check(const void *bin, size_t binsz, const char *b58, size_t b58sz); + +extern bool b58enc(char *b58, size_t *b58sz, const void *bin, size_t binsz); +extern bool b58check_enc(char *b58c, size_t *b58c_sz, uint8_t ver, const void *data, size_t datasz); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/cryptoconditions/src/include/secp256k1/.gitignore b/src/cryptoconditions/src/include/secp256k1/.gitignore new file mode 100644 index 000000000..87fea161b --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/.gitignore @@ -0,0 +1,49 @@ +bench_inv +bench_ecdh +bench_sign +bench_verify +bench_schnorr_verify +bench_recover +bench_internal +tests +exhaustive_tests +gen_context +*.exe +*.so +*.a +!.gitignore + +Makefile +configure +.libs/ +Makefile.in +aclocal.m4 +autom4te.cache/ +config.log +config.status +*.tar.gz +*.la +libtool +.deps/ +.dirstamp +*.lo +*.o +*~ +src/libsecp256k1-config.h +src/libsecp256k1-config.h.in +src/ecmult_static_context.h +build-aux/config.guess +build-aux/config.sub +build-aux/depcomp +build-aux/install-sh +build-aux/ltmain.sh +build-aux/m4/libtool.m4 +build-aux/m4/lt~obsolete.m4 +build-aux/m4/ltoptions.m4 +build-aux/m4/ltsugar.m4 +build-aux/m4/ltversion.m4 +build-aux/missing +build-aux/compile +build-aux/test-driver +src/stamp-h1 +libsecp256k1.pc diff --git a/src/cryptoconditions/src/include/secp256k1/.travis.yml b/src/cryptoconditions/src/include/secp256k1/.travis.yml new file mode 100644 index 000000000..243952924 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/.travis.yml @@ -0,0 +1,69 @@ +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/cryptoconditions/src/include/secp256k1/COPYING b/src/cryptoconditions/src/include/secp256k1/COPYING new file mode 100644 index 000000000..4522a5990 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/COPYING @@ -0,0 +1,19 @@ +Copyright (c) 2013 Pieter Wuille + +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/cryptoconditions/src/include/secp256k1/Makefile.am b/src/cryptoconditions/src/include/secp256k1/Makefile.am new file mode 100644 index 000000000..c071fbe27 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/Makefile.am @@ -0,0 +1,177 @@ +ACLOCAL_AMFLAGS = -I build-aux/m4 + +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 +noinst_HEADERS = +noinst_HEADERS += src/scalar.h +noinst_HEADERS += src/scalar_4x64.h +noinst_HEADERS += src/scalar_8x32.h +noinst_HEADERS += src/scalar_low.h +noinst_HEADERS += src/scalar_impl.h +noinst_HEADERS += src/scalar_4x64_impl.h +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_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/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/util.h +noinst_HEADERS += src/testrand.h +noinst_HEADERS += src/testrand_impl.h +noinst_HEADERS += src/hash.h +noinst_HEADERS += src/hash_impl.h +noinst_HEADERS += src/field.h +noinst_HEADERS += src/field_impl.h +noinst_HEADERS += src/bench.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 + +if USE_EXTERNAL_ASM +COMMON_LIB = libsecp256k1_common.la +noinst_LTLIBRARIES = $(COMMON_LIB) +else +COMMON_LIB = +endif + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libsecp256k1.pc + +if USE_EXTERNAL_ASM +if USE_ASM_ARM +libsecp256k1_common_la_SOURCES = src/asm/field_10x26_arm.s +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) + +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) +bench_internal_SOURCES = src/bench_internal.c +bench_internal_LDADD = $(SECP_LIBS) $(COMMON_LIB) +bench_internal_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES) +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) +if !ENABLE_COVERAGE +tests_CPPFLAGS += -DVERIFY +endif +tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) +tests_LDFLAGS = -static +TESTS += tests +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) +if !ENABLE_COVERAGE +exhaustive_tests_CPPFLAGS += -DVERIFY +endif +exhaustive_tests_LDADD = $(SECP_LIBS) +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_JNI + +$(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 + +.stamp-java: $(JAVA_FILES) + @echo Compiling $^ + $(AM_V_at)$(CLASSPATH_ENV) javac $^ + @touch $@ + +if USE_TESTS + +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 + +endif +endif + +if USE_ECMULT_STATIC_PRECOMPUTATION +CPPFLAGS_FOR_BUILD +=-I$(top_srcdir) +CFLAGS_FOR_BUILD += -Wall -Wextra -Wno-unused-function + +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 $@ + +$(gen_context_BIN): $(gen_context_OBJECTS) + $(CC_FOR_BUILD) $^ -o $@ + +$(libsecp256k1_la_OBJECTS): src/ecmult_static_context.h +$(tests_OBJECTS): src/ecmult_static_context.h +$(bench_internal_OBJECTS): src/ecmult_static_context.h + +src/ecmult_static_context.h: $(gen_context_BIN) + ./$(gen_context_BIN) + +CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h $(JAVAROOT)/$(JAVAORG)/*.class .stamp-java +endif + +EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h $(JAVA_FILES) + +if ENABLE_MODULE_ECDH +include src/modules/ecdh/Makefile.am.include +endif + +if ENABLE_MODULE_RECOVERY +include src/modules/recovery/Makefile.am.include +endif diff --git a/src/cryptoconditions/src/include/secp256k1/README.md b/src/cryptoconditions/src/include/secp256k1/README.md new file mode 100644 index 000000000..8cd344ea8 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/README.md @@ -0,0 +1,61 @@ +libsecp256k1 +============ + +[![Build Status](https://travis-ci.org/bitcoin-core/secp256k1.svg?branch=master)](https://travis-ci.org/bitcoin-core/secp256k1) + +Optimized C library for EC operations on curve secp256k1. + +This library is a work in progress and is being used to research best practices. Use at your own risk. + +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.) +* Very efficient implementation. + +Implementation details +---------------------- + +* General + * No runtime heap allocation. + * Extensive testing infrastructure. + * Structured to facilitate review and analysis. + * Intended to be portable to any system with a C89 compiler and uint64_t support. + * 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). +* 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. +* 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. + * Use a unified addition/doubling formula where necessary to avoid data-dependent branches. + * Point/x comparison without a field inversion by comparison in the Jacobian coordinate space. +* Point multiplication for verification (a*P + b*G). + * 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. +* 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. + +Build steps +----------- + +libsecp256k1 is built using autotools: + + $ ./autogen.sh + $ ./configure + $ make + $ ./tests + $ sudo make install # optional diff --git a/src/cryptoconditions/src/include/secp256k1/TODO b/src/cryptoconditions/src/include/secp256k1/TODO new file mode 100644 index 000000000..a300e1c5e --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/TODO @@ -0,0 +1,3 @@ +* 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/cryptoconditions/src/include/secp256k1/autogen.sh b/src/cryptoconditions/src/include/secp256k1/autogen.sh new file mode 100755 index 000000000..65286b935 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/autogen.sh @@ -0,0 +1,3 @@ +#!/bin/sh +set -e +autoreconf -if --warnings=all diff --git a/src/cryptoconditions/src/include/secp256k1/build-aux/m4/ax_jni_include_dir.m4 b/src/cryptoconditions/src/include/secp256k1/build-aux/m4/ax_jni_include_dir.m4 new file mode 100644 index 000000000..1fc362761 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/build-aux/m4/ax_jni_include_dir.m4 @@ -0,0 +1,140 @@ +# =========================================================================== +# 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/cryptoconditions/src/include/secp256k1/build-aux/m4/ax_prog_cc_for_build.m4 b/src/cryptoconditions/src/include/secp256k1/build-aux/m4/ax_prog_cc_for_build.m4 new file mode 100644 index 000000000..77fd346a7 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/build-aux/m4/ax_prog_cc_for_build.m4 @@ -0,0 +1,125 @@ +# =========================================================================== +# 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/cryptoconditions/src/include/secp256k1/build-aux/m4/bitcoin_secp.m4 b/src/cryptoconditions/src/include/secp256k1/build-aux/m4/bitcoin_secp.m4 new file mode 100644 index 000000000..b74acb8c1 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/build-aux/m4/bitcoin_secp.m4 @@ -0,0 +1,69 @@ +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_MSG_CHECKING(for x86_64 assembly availability) +AC_COMPILE_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]) +]) + +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 +]) + +dnl +AC_DEFUN([SECP_GMP_CHECK],[ +if test x"$has_gmp" != 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="$CPPFLAGS_TEMP" + LIBS="$LIBS_TEMP" +fi +]) diff --git a/src/cryptoconditions/src/include/secp256k1/configure.ac b/src/cryptoconditions/src/include/secp256k1/configure.ac new file mode 100644 index 000000000..e5fcbcb4e --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/configure.ac @@ -0,0 +1,493 @@ +AC_PREREQ([2.60]) +AC_INIT([libsecp256k1],[0.1]) +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 + +AC_PATH_TOOL(AR, ar) +AC_PATH_TOOL(RANLIB, ranlib) +AC_PATH_TOOL(STRIP, strip) +AX_PROG_CC_FOR_BUILD + +if test "x$CFLAGS" = "x"; then + CFLAGS="-g" +fi + +AM_PROG_CC_C_O + +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 + +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" + 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 + CPPFLAGS="$CPPFLAGS -isystem /opt/local/include" + LDFLAGS="$LDFLAGS -L/opt/local/lib" + fi + fi + fi + ;; +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" + ]) + +AC_ARG_ENABLE(benchmark, + AS_HELP_STRING([--enable-benchmark],[compile benchmark (default is no)]), + [use_benchmark=$enableval], + [use_benchmark=no]) + +AC_ARG_ENABLE(coverage, + AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis]), + [enable_coverage=$enableval], + [enable_coverage=no]) + +AC_ARG_ENABLE(tests, + AS_HELP_STRING([--enable-tests],[compile tests (default is yes)]), + [use_tests=$enableval], + [use_tests=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(experimental, + AS_HELP_STRING([--enable-experimental],[allow experimental configure options (default is no)]), + [use_experimental=$enableval], + [use_experimental=no]) + +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]) + +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(module_ecdh, + AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation (experimental)]), + [enable_module_ecdh=$enableval], + [enable_module_ecdh=no]) + +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]) + +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_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" +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]) + fi + else + AC_MSG_RESULT([ok]) + set_precomp=yes + fi +else + set_precomp=no +fi + +if test x"$req_asm" = x"auto"; then + SECP_64BIT_ASM_CHECK + if test x"$has_64bit_asm" = x"yes"; then + set_asm=x86_64 + fi + if test x"$set_asm" = x; then + set_asm=no + fi +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]) + 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]) + fi + ;; + no) + ;; + *) + AC_MSG_ERROR([invalid bignum implementation selection]) + ;; + esac +fi + +# select assembly optimization +use_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]) + ;; +arm) + use_external_asm=yes + ;; +no) + ;; +*) + AC_MSG_ERROR([invalid assembly optimizations]) + ;; +esac + +# select field implementation +case $set_field in +64bit) + AC_DEFINE(USE_FIELD_5X52, 1, [Define this symbol to use the FIELD_5X52 implementation]) + ;; +32bit) + AC_DEFINE(USE_FIELD_10X26, 1, [Define this symbol to use the FIELD_10X26 implementation]) + ;; +*) + AC_MSG_ERROR([invalid field 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]) + ;; +*) + AC_MSG_ERROR([invalid bignum implementation]) + ;; +esac + +#select scalar implementation +case $set_scalar in +64bit) + AC_DEFINE(USE_SCALAR_4X64, 1, [Define this symbol to use the 4x64 scalar implementation]) + ;; +32bit) + AC_DEFINE(USE_SCALAR_8X32, 1, [Define this symbol to use the 8x32 scalar implementation]) + ;; +*) + AC_MSG_ERROR([invalid scalar implementation]) + ;; +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 +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 + +if test x"$set_bignum" = x"gmp"; then + SECP_LIBS="$SECP_LIBS $GMP_LIBS" + SECP_INCLUDES="$SECP_INCLUDES $GMP_CPPFLAGS" +fi + +if test x"$use_endomorphism" = x"yes"; then + AC_DEFINE(USE_ENDOMORPHISM, 1, [Define this symbol to use endomorphism optimization]) +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]) +fi + +if test x"$enable_module_ecdh" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module]) +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]) +fi + +AC_C_BIGENDIAN() + +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]) +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]) + +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.]) + fi +fi + +AC_CONFIG_HEADERS([src/libsecp256k1-config.h]) +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) +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([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" + +AC_OUTPUT diff --git a/src/cryptoconditions/src/include/secp256k1/contrib/lax_der_parsing.c b/src/cryptoconditions/src/include/secp256k1/contrib/lax_der_parsing.c new file mode 100644 index 000000000..5b141a994 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/contrib/lax_der_parsing.c @@ -0,0 +1,150 @@ +/********************************************************************** + * Copyright (c) 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + +#include "lax_der_parsing.h" + +int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) { + size_t rpos, rlen, spos, slen; + size_t pos = 0; + size_t lenbyte; + unsigned char tmpsig[64] = {0}; + int overflow = 0; + + /* Hack to initialize sig with a correctly-parsed but invalid signature. */ + secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig); + + /* Sequence tag byte */ + if (pos == inputlen || input[pos] != 0x30) { + return 0; + } + pos++; + + /* Sequence length bytes */ + if (pos == inputlen) { + return 0; + } + lenbyte = input[pos++]; + if (lenbyte & 0x80) { + lenbyte -= 0x80; + if (pos + lenbyte > inputlen) { + return 0; + } + pos += lenbyte; + } + + /* Integer tag byte for R */ + if (pos == inputlen || input[pos] != 0x02) { + return 0; + } + pos++; + + /* Integer length for R */ + if (pos == inputlen) { + return 0; + } + lenbyte = input[pos++]; + if (lenbyte & 0x80) { + lenbyte -= 0x80; + if (pos + lenbyte > inputlen) { + return 0; + } + while (lenbyte > 0 && input[pos] == 0) { + pos++; + lenbyte--; + } + if (lenbyte >= sizeof(size_t)) { + return 0; + } + rlen = 0; + while (lenbyte > 0) { + rlen = (rlen << 8) + input[pos]; + pos++; + lenbyte--; + } + } else { + rlen = lenbyte; + } + if (rlen > inputlen - pos) { + return 0; + } + rpos = pos; + pos += rlen; + + /* Integer tag byte for S */ + if (pos == inputlen || input[pos] != 0x02) { + return 0; + } + pos++; + + /* Integer length for S */ + if (pos == inputlen) { + return 0; + } + lenbyte = input[pos++]; + if (lenbyte & 0x80) { + lenbyte -= 0x80; + if (pos + lenbyte > inputlen) { + return 0; + } + while (lenbyte > 0 && input[pos] == 0) { + pos++; + lenbyte--; + } + if (lenbyte >= sizeof(size_t)) { + return 0; + } + slen = 0; + while (lenbyte > 0) { + slen = (slen << 8) + input[pos]; + pos++; + lenbyte--; + } + } else { + slen = lenbyte; + } + if (slen > inputlen - pos) { + return 0; + } + spos = pos; + pos += slen; + + /* Ignore leading zeroes in R */ + while (rlen > 0 && input[rpos] == 0) { + rlen--; + rpos++; + } + /* Copy R value */ + if (rlen > 32) { + overflow = 1; + } else { + memcpy(tmpsig + 32 - rlen, input + rpos, rlen); + } + + /* Ignore leading zeroes in S */ + while (slen > 0 && input[spos] == 0) { + slen--; + spos++; + } + /* Copy S value */ + if (slen > 32) { + overflow = 1; + } else { + memcpy(tmpsig + 64 - slen, input + spos, slen); + } + + if (!overflow) { + overflow = !secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig); + } + if (overflow) { + memset(tmpsig, 0, 64); + secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig); + } + return 1; +} + diff --git a/src/cryptoconditions/src/include/secp256k1/contrib/lax_der_parsing.h b/src/cryptoconditions/src/include/secp256k1/contrib/lax_der_parsing.h new file mode 100644 index 000000000..7eaf63bf6 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/contrib/lax_der_parsing.h @@ -0,0 +1,91 @@ +/********************************************************************** + * Copyright (c) 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/**** + * Please do not link this file directly. It is not part of the libsecp256k1 + * project and does not promise any stability in its API, functionality or + * presence. Projects which use this code should instead copy this header + * and its accompanying .c file directly into their codebase. + ****/ + +/* This file defines a function that parses DER with various errors and + * violations. This is not a part of the library itself, because the allowed + * violations are chosen arbitrarily and do not follow or establish any + * standard. + * + * In many places it matters that different implementations do not only accept + * the same set of valid signatures, but also reject the same set of signatures. + * The only means to accomplish that is by strictly obeying a standard, and not + * accepting anything else. + * + * Nonetheless, sometimes there is a need for compatibility with systems that + * use signatures which do not strictly obey DER. The snippet below shows how + * certain violations are easily supported. You may need to adapt it. + * + * Do not use this for new systems. Use well-defined DER or compact signatures + * instead if you have the choice (see secp256k1_ecdsa_signature_parse_der and + * secp256k1_ecdsa_signature_parse_compact). + * + * The supported violations are: + * - All numbers are parsed as nonnegative integers, even though X.609-0207 + * section 8.3.3 specifies that integers are always encoded as two's + * complement. + * - Integers can have length 0, even though section 8.3.1 says they can't. + * - Integers with overly long padding are accepted, violation section + * 8.3.2. + * - 127-byte long length descriptors are accepted, even though section + * 8.1.3.5.c says that they are not. + * - Trailing garbage data inside or after the signature is ignored. + * - The length descriptor of the sequence is ignored. + * + * Compared to for example OpenSSL, many violations are NOT supported: + * - Using overly long tag descriptors for the sequence or integers inside, + * violating section 8.1.2.2. + * - Encoding primitive integers as constructed values, violating section + * 8.3.1. + */ + +#ifndef SECP256K1_CONTRIB_LAX_DER_PARSING_H +#define SECP256K1_CONTRIB_LAX_DER_PARSING_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Parse a signature in "lax DER" 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: input: a 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. In addition, it will accept signatures + * which violate the DER spec in various ways. Its purpose is to allow + * validation of the Bitcoin blockchain, which includes non-DER signatures + * from before the network rules were updated to enforce DER. Note that + * the set of supported violations is a strict subset of what OpenSSL will + * accept. + * + * After the call, sig will always be initialized. If parsing failed or the + * encoded numbers are out of range, signature validation with it is + * guaranteed to fail for every message and public key. + */ +int ecdsa_signature_parse_der_lax( + 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); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_CONTRIB_LAX_DER_PARSING_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/contrib/lax_der_privatekey_parsing.c b/src/cryptoconditions/src/include/secp256k1/contrib/lax_der_privatekey_parsing.c new file mode 100644 index 000000000..c2e63b4b8 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/contrib/lax_der_privatekey_parsing.c @@ -0,0 +1,113 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + +#include "lax_der_privatekey_parsing.h" + +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; + int lenb = 0; + int len = 0; + memset(out32, 0, 32); + /* sequence header */ + if (end < privkey+1 || *privkey != 0x30) { + return 0; + } + privkey++; + /* sequence length constructor */ + if (end < privkey+1 || !(*privkey & 0x80)) { + return 0; + } + lenb = *privkey & ~0x80; privkey++; + if (lenb < 1 || lenb > 2) { + return 0; + } + if (end < privkey+lenb) { + return 0; + } + /* sequence length */ + len = privkey[lenb-1] | (lenb > 1 ? privkey[lenb-2] << 8 : 0); + privkey += lenb; + if (end < privkey+len) { + return 0; + } + /* sequence element 0: version number (=1) */ + if (end < privkey+3 || privkey[0] != 0x02 || privkey[1] != 0x01 || privkey[2] != 0x01) { + return 0; + } + privkey += 3; + /* sequence element 1: octet string, up to 32 bytes */ + 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 (!secp256k1_ec_seckey_verify(ctx, out32)) { + memset(out32, 0, 32); + return 0; + } + return 1; +} + +int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *privkey, size_t *privkeylen, const unsigned char *key32, int compressed) { + secp256k1_pubkey pubkey; + size_t pubkeylen = 0; + if (!secp256k1_ec_pubkey_create(ctx, &pubkey, key32)) { + *privkeylen = 0; + return 0; + } + if (compressed) { + static const unsigned char begin[] = { + 0x30,0x81,0xD3,0x02,0x01,0x01,0x04,0x20 + }; + static const unsigned char middle[] = { + 0xA0,0x81,0x85,0x30,0x81,0x82,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48, + 0xCE,0x3D,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,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04, + 0x21,0x02,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,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,0xA1,0x24,0x03,0x22,0x00 + }; + unsigned char *ptr = privkey; + memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin); + memcpy(ptr, key32, 32); ptr += 32; + memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle); + pubkeylen = 33; + secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED); + ptr += pubkeylen; + *privkeylen = ptr - privkey; + } else { + static const unsigned char begin[] = { + 0x30,0x82,0x01,0x13,0x02,0x01,0x01,0x04,0x20 + }; + static const unsigned char middle[] = { + 0xA0,0x81,0xA5,0x30,0x81,0xA2,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48, + 0xCE,0x3D,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,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04, + 0x41,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,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,0xA1,0x44,0x03,0x42,0x00 + }; + unsigned char *ptr = privkey; + memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin); + memcpy(ptr, key32, 32); ptr += 32; + memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle); + pubkeylen = 65; + secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_UNCOMPRESSED); + ptr += pubkeylen; + *privkeylen = ptr - privkey; + } + return 1; +} diff --git a/src/cryptoconditions/src/include/secp256k1/contrib/lax_der_privatekey_parsing.h b/src/cryptoconditions/src/include/secp256k1/contrib/lax_der_privatekey_parsing.h new file mode 100644 index 000000000..fece261fb --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/contrib/lax_der_privatekey_parsing.h @@ -0,0 +1,90 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/**** + * Please do not link this file directly. It is not part of the libsecp256k1 + * project and does not promise any stability in its API, functionality or + * presence. Projects which use this code should instead copy this header + * and its accompanying .c file directly into their codebase. + ****/ + +/* This file contains code snippets that parse DER private keys with + * various errors and violations. This is not a part of the library + * itself, because the allowed violations are chosen arbitrarily and + * do not follow or establish any standard. + * + * It also contains code to serialize private keys in a compatible + * manner. + * + * These functions are meant for compatibility with applications + * that require BER encoded keys. When working with secp256k1-specific + * code, the simple 32-byte private keys normally used by the + * library are sufficient. + */ + +#ifndef SECP256K1_CONTRIB_BER_PRIVATEKEY_H +#define SECP256K1_CONTRIB_BER_PRIVATEKEY_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** 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) + * 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 + * privkey will be stored. + * In: seckey: pointer to a 32-byte secret key to export. + * compressed: 1 if the key should be exported in + * compressed format, 0 otherwise + * + * This function is purely meant for compatibility with applications that + * require BER encoded keys. When working with secp256k1-specific code, the + * simple 32-byte private keys are sufficient. + * + * Note that this function does not guarantee correct DER output. It is + * guaranteed to be parsable by secp256k1_ec_privkey_import_der + */ +SECP256K1_WARN_UNUSED_RESULT int ec_privkey_export_der( + const secp256k1_context* ctx, + unsigned char *privkey, + size_t *privkeylen, + const unsigned char *seckey, + int compressed +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Import a private key in DER format. + * Returns: 1 if a private key was extracted. + * Args: ctx: pointer to a context object (cannot be NULL). + * Out: seckey: pointer to a 32-byte array for storing the private key. + * (cannot be NULL). + * In: privkey: pointer to a private key in DER format (cannot be NULL). + * privkeylen: length of the DER private key pointed to be privkey. + * + * This function will accept more than just strict DER, and even allow some BER + * violations. The public key stored inside the DER-encoded private key is not + * verified for correctness, nor are the curve parameters. Use this function + * only if you know in advance it is supposed to contain a secp256k1 private + * key. + */ +SECP256K1_WARN_UNUSED_RESULT int ec_privkey_import_der( + const secp256k1_context* ctx, + unsigned char *seckey, + const unsigned char *privkey, + size_t privkeylen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_CONTRIB_BER_PRIVATEKEY_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/include/secp256k1.h b/src/cryptoconditions/src/include/secp256k1/include/secp256k1.h new file mode 100644 index 000000000..3e9c098d1 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/include/secp256k1.h @@ -0,0 +1,621 @@ +#ifndef SECP256K1_H +#define SECP256K1_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* These 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 + * 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, + * 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, + * counts, flags, booleans. + * 5. Opaque data pointers follow the function pointer they are to be passed to. + */ + +/** Opaque data structure that holds context information (precomputed tables etc.). + * + * 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. + * + * 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 constructed context can safely be used from multiple threads + * simultaneously, but API call 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. + * + * 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 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. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_pubkey; + +/** Opaque data structured that holds a parsed ECDSA signature. + * + * 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 the secp256k1_ecdsa_signature_serialize_* and + * secp256k1_ecdsa_signature_parse_* functions. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_ecdsa_signature; + +/** A pointer to a function to deterministically generate a nonce. + * + * Returns: 1 if a nonce was successfully generated. 0 will cause signing to fail. + * Out: nonce32: pointer to a 32-byte array to be filled by the function. + * In: msg32: the 32-byte message hash being verified (will not be NULL) + * key32: pointer to a 32-byte secret key (will not be NULL) + * algo16: pointer to a 16-byte array describing the signature + * algorithm (will be NULL for ECDSA for compatibility). + * data: Arbitrary data pointer that is passed through. + * attempt: how many iterations we have tried to find a nonce. + * This will almost always be 0, but different attempt values + * are required to result in a different nonce. + * + * Except for test cases, this function should compute some cryptographic hash of + * the message, the algorithm, the key and the attempt. + */ +typedef int (*secp256k1_nonce_function)( + unsigned char *nonce32, + const unsigned char *msg32, + const unsigned char *key32, + const unsigned char *algo16, + void *data, + unsigned int attempt +); + +# if !defined(SECP256K1_GNUC_PREREQ) +# if defined(__GNUC__)&&defined(__GNUC_MINOR__) +# define SECP256K1_GNUC_PREREQ(_maj,_min) \ + ((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min)) +# else +# define SECP256K1_GNUC_PREREQ(_maj,_min) 0 +# 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 +# endif +# else +# define SECP256K1_INLINE inline +# 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"))) +# else +# define SECP256K1_API +# endif +#endif + +/**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 +# define SECP256K1_WARN_UNUSED_RESULT +# endif +# if !defined(SECP256K1_BUILD) && defined(__GNUC__) && SECP256K1_GNUC_PREREQ(3, 4) +# define SECP256K1_ARG_NONNULL(_x) __attribute__ ((__nonnull__(_x))) +# else +# define SECP256K1_ARG_NONNULL(_x) +# 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. */ +#define SECP256K1_FLAGS_BIT_CONTEXT_VERIFY (1 << 8) +#define SECP256K1_FLAGS_BIT_CONTEXT_SIGN (1 << 9) +#define SECP256K1_FLAGS_BIT_COMPRESSION (1 << 8) + +/** Flags to pass to secp256k1_context_create. */ +#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. */ +#define SECP256K1_EC_COMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION) +#define SECP256K1_EC_UNCOMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION) + +/** Prefix byte used to tag various encoded curvepoints for specific purposes */ +#define SECP256K1_TAG_PUBKEY_EVEN 0x02 +#define SECP256K1_TAG_PUBKEY_ODD 0x03 +#define SECP256K1_TAG_PUBKEY_UNCOMPRESSED 0x04 +#define SECP256K1_TAG_PUBKEY_HYBRID_EVEN 0x06 +#define SECP256K1_TAG_PUBKEY_HYBRID_ODD 0x07 + +/** Create a secp256k1 context object. + * + * Returns: a newly created context object. + * In: flags: which parts of the context to initialize. + * + * See also secp256k1_context_randomize. + */ +SECP256K1_API secp256k1_context* secp256k1_context_create( + unsigned int flags +) SECP256K1_WARN_UNUSED_RESULT; + +/** Copies a secp256k1 context object. + * + * Returns: a newly created context object. + * Args: ctx: an existing context to copy (cannot be NULL) + */ +SECP256K1_API secp256k1_context* secp256k1_context_clone( + const secp256k1_context* ctx +) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT; + +/** Destroy a secp256k1 context object. + * + * The context pointer may not be used afterwards. + * Args: ctx: an existing context to destroy (cannot be NULL) + */ +SECP256K1_API void secp256k1_context_destroy( + secp256k1_context* ctx +); + +/** 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 + * explicitly in the header. + * + * The philosophy is that these shouldn't be dealt with through a + * specific return value, as calling code should not have branches to deal with + * the case that this code itself is broken. + * + * On the other hand, during debug stage, one would want to be informed about + * such mistakes, and the default (crashing) may be inadvisable. + * When this callback is triggered, the API function called is guaranteed not + * 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. + */ +SECP256K1_API void secp256k1_context_set_illegal_callback( + 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. + * + * This can only trigger in case of a hardware failure, miscompilation, + * memory corruption, serious bug in the library, or other error would can + * otherwise result in undefined behaviour. It will not trigger due to mere + * incorrect usage of the API (see 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. + */ +SECP256K1_API void secp256k1_context_set_error_callback( + secp256k1_context* ctx, + void (*fun)(const char* message, void* data), + const void* data +) 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. + * 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 + * inputlen: length of the array pointed to by input + * + * This function supports parsing compressed (33 bytes, header byte 0x02 or + * 0x03), uncompressed (65 bytes, header byte 0x04), or hybrid (65 bytes, header + * 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 unsigned char *input, + size_t inputlen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** 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 + * compressed==1) byte array to place the serialized key + * in. + * In/Out: outputlen: a 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 + * 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, + unsigned char *output, + size_t *outputlen, + const secp256k1_pubkey* pubkey, + unsigned int flags +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** 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 + * + * 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. + */ +SECP256K1_API int secp256k1_ecdsa_signature_parse_compact( + 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 + * 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 + * 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 unsigned char *input, + size_t inputlen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** 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 + * 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 + */ +SECP256K1_API int secp256k1_ecdsa_signature_serialize_der( + const secp256k1_context* ctx, + unsigned char *output, + size_t *outputlen, + 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 + * + * See secp256k1_ecdsa_signature_parse_compact for details about the encoding. + */ +SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact( + const secp256k1_context* ctx, + unsigned char *output64, + 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) + * + * 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. + * + * For details, see the comments for that function. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify( + const secp256k1_context* ctx, + const secp256k1_ecdsa_signature *sig, + const unsigned char *msg32, + 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, + * 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) + * + * 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 + * is done by negating the S value modulo the order of the curve, 'flipping' + * the sign of the random point R which is not included in the signature. + * + * Forgery of the same message isn't universally problematic, but in systems + * where message malleability or uniqueness of signatures is important this can + * cause issues. This forgery can be blocked by all verifiers forcing signers + * to use a normalized form. + * + * The lower-S form reduces the size of signatures slightly on average when + * variable length encodings (such as DER) are used and is cheap to verify, + * making it a good choice. Security of always using lower-S is assured because + * anyone can trivially modify a signature after the fact to enforce this + * property anyway. + * + * The lower S value is always between 0x1 and + * 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, + * inclusive. + * + * No other forms of ECDSA malleability are known and none seem likely, but + * there is no formal proof that ECDSA, even with this additional restriction, + * is free of other malleability. Commonly used serialization schemes will also + * accept various non-unique encodings, so care should be taken when this + * property is required for an application. + * + * The secp256k1_ecdsa_sign function will by default create signatures in the + * lower-S form, and secp256k1_ecdsa_verify will not accept others. In case + * signatures come from a system that cannot enforce this property, + * secp256k1_ecdsa_signature_normalize must be called before verification. + */ +SECP256K1_API int secp256k1_ecdsa_signature_normalize( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature *sigout, + const secp256k1_ecdsa_signature *sigin +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3); + +/** An implementation of RFC6979 (using HMAC-SHA256) as nonce generation function. + * 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; + +/** A default safe nonce generation function (currently equal to secp256k1_nonce_function_rfc6979). */ +SECP256K1_API extern 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) + * + * 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, + secp256k1_ecdsa_signature *sig, + const unsigned char *msg32, + 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. + * + * 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) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_verify( + 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) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create( + 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. + * + * Returns: 1 always + * Args: ctx: pointer to a context object + * In/Out: pubkey: pointer to the public key to be negated (cannot be NULL) + */ +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); + +/** 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) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_negate( + 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. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add( + const secp256k1_context* ctx, + unsigned char *seckey, + const unsigned char *tweak +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** 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. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const unsigned char *tweak +) 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. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul( + const secp256k1_context* ctx, + unsigned char *seckey, + const unsigned char *tweak +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** 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. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const unsigned char *tweak +) 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 + * 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) + * + * 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, + * 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. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize( + 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) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine( + const secp256k1_context* ctx, + secp256k1_pubkey *out, + const secp256k1_pubkey * const * ins, + size_t n +) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/include/secp256k1_ecdh.h b/src/cryptoconditions/src/include/secp256k1/include/secp256k1_ecdh.h new file mode 100644 index 000000000..88492dc1a --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/include/secp256k1_ecdh.h @@ -0,0 +1,31 @@ +#ifndef SECP256K1_ECDH_H +#define SECP256K1_ECDH_H + +#include "secp256k1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** 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 + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh( + const secp256k1_context* ctx, + unsigned char *result, + const secp256k1_pubkey *pubkey, + const unsigned char *privkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_ECDH_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/include/secp256k1_recovery.h b/src/cryptoconditions/src/include/secp256k1/include/secp256k1_recovery.h new file mode 100644 index 000000000..cf6c5ed7f --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/include/secp256k1_recovery.h @@ -0,0 +1,110 @@ +#ifndef SECP256K1_RECOVERY_H +#define SECP256K1_RECOVERY_H + +#include "secp256k1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Opaque data structured that holds a parsed ECDSA signature, + * supporting pubkey recovery. + * + * 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 65 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage or transmission, use + * the secp256k1_ecdsa_signature_serialize_* and + * secp256k1_ecdsa_signature_parse_* functions. + * + * Furthermore, it is guaranteed that identical signatures (including their + * recoverability) will have identical representation, so they can be + * memcmp'ed. + */ +typedef struct { + unsigned char data[65]; +} secp256k1_ecdsa_recoverable_signature; + +/** 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 + * 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 unsigned char *input64, + int recid +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** 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). + */ +SECP256K1_API int secp256k1_ecdsa_recoverable_signature_convert( + 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) + */ +SECP256K1_API int secp256k1_ecdsa_recoverable_signature_serialize_compact( + const secp256k1_context* ctx, + unsigned char *output64, + int *recid, + 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) + */ +SECP256K1_API int secp256k1_ecdsa_sign_recoverable( + const secp256k1_context* ctx, + secp256k1_ecdsa_recoverable_signature *sig, + const unsigned char *msg32, + 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); + +/** Recover an ECDSA public key from a signature. + * + * 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) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_recover( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const secp256k1_ecdsa_recoverable_signature *sig, + const unsigned char *msg32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_RECOVERY_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/libsecp256k1.pc.in b/src/cryptoconditions/src/include/secp256k1/libsecp256k1.pc.in new file mode 100644 index 000000000..a0d006f11 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/libsecp256k1.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsecp256k1 +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/cryptoconditions/src/include/secp256k1/obj/.gitignore b/src/cryptoconditions/src/include/secp256k1/obj/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/src/cryptoconditions/src/include/secp256k1/sage/group_prover.sage b/src/cryptoconditions/src/include/secp256k1/sage/group_prover.sage new file mode 100644 index 000000000..8521f0799 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/sage/group_prover.sage @@ -0,0 +1,322 @@ +# This code supports verifying group implementations which have branches +# or conditional statements (like cmovs), by allowing each execution path +# to independently set assumptions on input or intermediary variables. +# +# The general approach is: +# * A constraint is a tuple of two sets of symbolic expressions: +# the first of which are required to evaluate to zero, the second of which +# are required to evaluate to nonzero. +# - A constraint is said to be conflicting if any of its nonzero expressions +# is in the ideal with basis the zero expressions (in other words: when the +# zero expressions imply that one of the nonzero expressions are zero). +# * There is a list of laws that describe the intended behaviour, including +# laws for addition and doubling. Each law is called with the symbolic point +# coordinates as arguments, and returns: +# - A constraint describing the assumptions under which it is applicable, +# called "assumeLaw" +# - A constraint describing the requirements of the law, called "require" +# * Implementations are transliterated into functions that operate as well on +# algebraic input points, and are called once per combination of branches +# executed. Each execution returns: +# - A constraint describing the assumptions this implementation requires +# (such as Z1=1), called "assumeFormula" +# - A constraint describing the assumptions this specific branch requires, +# but which is by construction guaranteed to cover the entire space by +# merging the results from all branches, called "assumeBranch" +# - The result of the computation +# * All combinations of laws with implementation branches are tried, and: +# - If the combination of assumeLaw, assumeFormula, and assumeBranch results +# in a conflict, it means this law does not apply to this branch, and it is +# skipped. +# - For others, we try to prove the require constraints hold, assuming the +# information in assumeLaw + assumeFormula + assumeBranch, and if this does +# not succeed, we fail. +# + To prove an expression is zero, we check whether it belongs to the +# ideal with the assumed zero expressions as basis. This test is exact. +# + To prove an expression is nonzero, we check whether each of its +# factors is contained in the set of nonzero assumptions' factors. +# This test is not exact, so various combinations of original and +# reduced expressions' factors are tried. +# - If we succeed, we print out the assumptions from assumeFormula that +# weren't implied by assumeLaw already. Those from assumeBranch are skipped, +# 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 + +class fastfrac: + """Fractions over rings.""" + + def __init__(self,R,top,bot=1): + """Construct a fractional, given a ring, a numerator, and denominator.""" + self.R = R + if parent(top) == ZZ or parent(top) == R: + self.top = R(top) + self.bot = R(bot) + elif top.__class__ == fastfrac: + self.top = top.top + self.bot = top.bot * bot + else: + self.top = R(numerator(top)) + self.bot = R(denominator(top)) * bot + + def iszero(self,I): + """Return whether this fraction is zero given an ideal.""" + return self.top in I and self.bot not in I + + def reduce(self,assumeZero): + zero = self.R.ideal(map(numerator, assumeZero)) + return fastfrac(self.R, zero.reduce(self.top)) / fastfrac(self.R, zero.reduce(self.bot)) + + def __add__(self,other): + """Add two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top + self.bot * other,self.bot) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.bot + self.bot * other.top,self.bot * other.bot) + return NotImplemented + + def __sub__(self,other): + """Subtract two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top - self.bot * other,self.bot) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.bot - self.bot * other.top,self.bot * other.bot) + return NotImplemented + + def __neg__(self): + """Return the negation of a fraction.""" + return fastfrac(self.R,-self.top,self.bot) + + def __mul__(self,other): + """Multiply two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top * other,self.bot) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.top,self.bot * other.bot) + return NotImplemented + + def __rmul__(self,other): + """Multiply something else with a fraction.""" + return self.__mul__(other) + + def __div__(self,other): + """Divide two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top,self.bot * other) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.bot,self.bot * other.top) + return NotImplemented + + def __pow__(self,other): + """Compute a power of a fraction.""" + if parent(other) == ZZ: + if other < 0: + # Negative powers require flipping top and bottom + return fastfrac(self.R,self.bot ^ (-other),self.top ^ (-other)) + else: + return fastfrac(self.R,self.top ^ other,self.bot ^ other) + return NotImplemented + + def __str__(self): + return "fastfrac((" + str(self.top) + ") / (" + str(self.bot) + "))" + def __repr__(self): + return "%s" % self + + def numerator(self): + return self.top + +class constraints: + """A set of constraints, consisting of zero and nonzero expressions. + + Constraints can either be used to express knowledge or a requirement. + + Both the fields zero and nonzero are maps from expressions to description + strings. The expressions that are the keys in zero are required to be zero, + and the expressions that are the keys in nonzero are required to be nonzero. + + Note that (a != 0) and (b != 0) is the same as (a*b != 0), so all keys in + nonzero could be multiplied into a single key. This is often much less + efficient to work with though, so we keep them separate inside the + constraints. This allows higher-level code to do fast checks on the individual + nonzero elements, or combine them if needed for stronger checks. + + We can't multiply the different zero elements, as it would suffice for one of + the factors to be zero, instead of all of them. Instead, the zero elements are + typically combined into an ideal first. + """ + + def __init__(self, **kwargs): + if 'zero' in kwargs: + self.zero = dict(kwargs['zero']) + else: + self.zero = dict() + if 'nonzero' in kwargs: + self.nonzero = dict(kwargs['nonzero']) + else: + self.nonzero = dict() + + def negate(self): + return constraints(zero=self.nonzero, nonzero=self.zero) + + def __add__(self, other): + zero = self.zero.copy() + zero.update(other.zero) + nonzero = self.nonzero.copy() + nonzero.update(other.nonzero) + return constraints(zero=zero, nonzero=nonzero) + + def __str__(self): + return "constraints(zero=%s,nonzero=%s)" % (self.zero, self.nonzero) + + def __repr__(self): + return "%s" % self + + +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)) + if 1 in zero: + return True + # First a cheap check whether any of the individual nonzero terms conflict on + # their own. + for nonzero in con.nonzero: + if nonzero.iszero(zero): + return True + # It can be the case that entries in the nonzero set do not individually + # conflict with the zero set, but their combination does. For example, knowing + # that either x or y is zero is equivalent to having x*y in the zero set. + # Having x or y individually in the nonzero set is not a conflict, but both + # simultaneously is, so that is the right thing to check for. + if reduce(lambda a,b: a * b, con.nonzero, fastfrac(R, 1)).iszero(zero): + return True + return False + + +def get_nonzero_set(R, assume): + """Calculate a simple set of nonzero expressions""" + zero = R.ideal(map(numerator, assume.zero)) + nonzero = set() + for nz in map(numerator, assume.nonzero): + for (f,n) in nz.factor(): + nonzero.add(f) + rnz = zero.reduce(nz) + for (f,n) in rnz.factor(): + nonzero.add(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)) + nonzero = get_nonzero_set(R, assume) + expl = set() + ok = True + for expr in exprs: + if numerator(expr) in zero: + 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: + ok = False + if ok: + return (True, None) + ok = True + for (f, n) in zero.reduce(numerator(allexprs)).factor(): + if 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: + 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: + expl.add(exprs[expr]) + if expl: + return (False, list(expl)) + else: + return (True, None) + + +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)) + nonzero = prod(x for x in assume.nonzero) + expl = [] + for expr in exprs: + if not expr.iszero(zero): + expl.append(exprs[expr]) + if not expl: + return (True, None) + return (False, expl) + + +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)) + nonzero = get_nonzero_set(R, assume) + ret = set() + # Iterate over the extra zero expressions + for base in assumeExtra.zero: + if base not in zero: + add = [] + for (f, n) in numerator(base).factor(): + if f not in nonzero: + add += ["%s" % f] + if add: + ret.add((" * ".join(add)) + " = 0 [%s]" % assumeExtra.zero[base]) + # Iterate over the extra nonzero expressions + for nz in assumeExtra.nonzero: + 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)) + return ", ".join(x for x in ret) + + +def check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require): + """Check a set of zero and nonzero requirements, given a set of zero and nonzero assumptions""" + assume = assumeLaw + assumeAssert + assumeBranch + + if conflicts(R, assume): + # This formula does not apply + return None + + describe = describe_extra(R, assumeLaw + assumeBranch, assumeAssert) + + ok, msg = prove_zero(R, require.zero, assume) + if not ok: + return "FAIL, %s fails (assuming %s)" % (str(msg), describe) + + res, expl = prove_nonzero(R, require.nonzero, assume) + if not res: + return "FAIL, %s fails (assuming %s)" % (str(expl), describe) + + if describe != "": + return "OK (assuming %s)" % describe + else: + return "OK" + + +def concrete_verify(c): + for k in c.zero: + if k != 0: + return (False, c.zero[k]) + for k in c.nonzero: + if k == 0: + return (False, c.nonzero[k]) + return (True, None) diff --git a/src/cryptoconditions/src/include/secp256k1/sage/secp256k1.sage b/src/cryptoconditions/src/include/secp256k1/sage/secp256k1.sage new file mode 100644 index 000000000..a97e732f7 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/sage/secp256k1.sage @@ -0,0 +1,306 @@ +# Test libsecp256k1' group operation implementations using prover.sage + +import sys + +load("group_prover.sage") +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 + return jacobianpoint(rx, ry, rz) + +def formula_secp256k1_gej_add_var(branch, a, b): + """libsecp256k1's secp256k1_gej_add_var""" + if branch == 0: + return (constraints(), constraints(nonzero={a.Infinity : 'a_infinite'}), b) + if branch == 1: + return (constraints(), constraints(zero={a.Infinity : 'a_finite'}, nonzero={b.Infinity : 'b_infinite'}), a) + z22 = b.Z^2 + z12 = a.Z^2 + u1 = a.X * z22 + u2 = b.X * z12 + s1 = a.Y * z22 + s1 = s1 * b.Z + s2 = b.Y * z12 + s2 = s2 * a.Z + h = -u1 + h = h + u2 + i = -s1 + i = i + s2 + 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 + h2 = h^2 + h3 = h2 * h + h = h * b.Z + rz = a.Z * h + t = u1 * h2 + rx = t + rx = rx * 2 + rx = rx + h3 + rx = -rx + rx = rx + i2 + ry = -rx + ry = ry + t + ry = ry * 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)) + +def formula_secp256k1_gej_add_ge_var(branch, a, b): + """libsecp256k1's secp256k1_gej_add_ge_var, which assume bz==1""" + if branch == 0: + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(nonzero={a.Infinity : 'a_infinite'}), b) + if branch == 1: + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite'}, nonzero={b.Infinity : 'b_infinite'}), a) + z12 = a.Z^2 + u1 = a.X + u2 = b.X * z12 + s1 = a.Y + s2 = b.Y * z12 + s2 = s2 * a.Z + h = -u1 + h = h + u2 + i = -s1 + i = i + s2 + 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 + t = u1 * h2 + rx = t + rx = rx * 2 + rx = rx + h3 + rx = -rx + rx = rx + i2 + ry = -rx + ry = ry + t + ry = ry * 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)) + +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: + 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)) + azz = a.Z * bzinv + z12 = azz^2 + u1 = a.X + u2 = b.X * z12 + s1 = a.Y + s2 = b.Y * z12 + s2 = s2 * azz + h = -u1 + h = h + u2 + i = -s1 + i = i + s2 + 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 + h2 = h^2 + h3 = h * h2 + rz = a.Z + rz = rz * h + t = u1 * h2 + rx = t + rx = rx * 2 + rx = rx + h3 + rx = -rx + rx = rx + i2 + ry = -rx + ry = ry + t + ry = ry * 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)) + +def formula_secp256k1_gej_add_ge(branch, a, b): + """libsecp256k1's secp256k1_gej_add_ge""" + zeroes = {} + nonzeroes = {} + a_infinity = False + if (branch & 4) != 0: + nonzeroes.update({a.Infinity : 'a_infinite'}) + a_infinity = True + else: + zeroes.update({a.Infinity : 'a_finite'}) + zz = a.Z^2 + u1 = a.X + u2 = b.X * zz + s1 = a.Y + s2 = b.Y * zz + s2 = s2 * a.Z + t = u1 + t = t + u2 + m = s1 + m = m + s2 + rr = t^2 + m_alt = -u2 + tt = u1 * m_alt + rr = rr + tt + degenerate = (branch & 3) == 3 + if (branch & 1) != 0: + 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 + if not degenerate: + rr_alt = rr + m_alt = m + n = m_alt^2 + q = n * t + 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 + t = t + q + t = t * rr_alt + t = t + n + ry = -t + rx = rx * 4 + ry = ry * 4 + if a_infinity: + rx = b.X + ry = b.Y + rz = 1 + if infinity: + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zeroes, nonzero=nonzeroes), point_at_infinity()) + 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): + """libsecp256k1's old secp256k1_gej_add_ge, which fails when ay+by=0 but ax!=bx""" + a_infinity = (branch & 1) != 0 + zero = {} + nonzero = {} + if a_infinity: + nonzero.update({a.Infinity : 'a_infinite'}) + else: + zero.update({a.Infinity : 'a_finite'}) + zz = a.Z^2 + u1 = a.X + u2 = b.X * zz + s1 = a.Y + s2 = b.Y * zz + s2 = s2 * a.Z + z = a.Z + t = u1 + t = t + u2 + m = s1 + m = m + s2 + n = m^2 + q = n * t + n = n^2 + rr = t^2 + t = u1 * u2 + t = -t + rr = rr + t + t = rr^2 + rz = m * z + infinity = False + if (branch & 2) != 0: + if not a_infinity: + infinity = True + else: + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(nonzero={z : 'conflict_a'}, zero={z : 'conflict_b'}), point_at_infinity()) + zero.update({rz : 'r.z=0'}) + else: + nonzero.update({rz : 'r.z!=0'}) + rz = rz * (0 if a_infinity else 2) + rx = t + q = -q + rx = rx + q + q = q * 3 + t = t * 2 + t = t + q + t = t * rr + t = t + n + ry = -t + rx = rx * (0 if a_infinity else 4) + ry = ry * (0 if a_infinity else 4) + t = b.X + t = t * (1 if a_infinity else 0) + rx = rx + t + t = b.Y + t = t * (1 if a_infinity else 0) + ry = ry + t + t = (1 if a_infinity else 0) + rz = rz + t + if infinity: + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), point_at_infinity()) + 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) + + 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) diff --git a/src/cryptoconditions/src/include/secp256k1/sage/weierstrass_prover.sage b/src/cryptoconditions/src/include/secp256k1/sage/weierstrass_prover.sage new file mode 100644 index 000000000..03ef2ec90 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/sage/weierstrass_prover.sage @@ -0,0 +1,264 @@ +# Prover implementation for Weierstrass curves of the form +# y^2 = x^3 + A * x + B, specifically with a = 0 and b = 7, with group laws +# operating on affine and Jacobian coordinates, including the point at infinity +# represented by a 4th variable in coordinates. + +load("group_prover.sage") + + +class affinepoint: + def __init__(self, x, y, infinity=0): + self.x = x + self.y = y + self.infinity = infinity + def __str__(self): + return "affinepoint(x=%s,y=%s,inf=%s)" % (self.x, self.y, self.infinity) + + +class jacobianpoint: + def __init__(self, x, y, z, infinity=0): + self.X = x + self.Y = y + self.Z = z + self.Infinity = infinity + def __str__(self): + return "jacobianpoint(X=%s,Y=%s,Z=%s,inf=%s)" % (self.X, self.Y, self.Z, self.Infinity) + + +def point_at_infinity(): + return jacobianpoint(1, 1, 1, 1) + + +def negate(p): + if p.__class__ == affinepoint: + return affinepoint(p.x, -p.y) + if p.__class__ == jacobianpoint: + return jacobianpoint(p.X, -p.Y, p.Z) + assert(False) + + +def on_weierstrass_curve(A, B, p): + """Return a set of zero-expressions for an affine point to be on the curve""" + return constraints(zero={p.x^3 + A*p.x + B - p.y^2: 'on_curve'}) + + +def tangential_to_weierstrass_curve(A, B, p12, p3): + """Return a set of zero-expressions for ((x12,y12),(x3,y3)) to be a line that is tangential to the curve at (x12,y12)""" + return constraints(zero={ + (p12.y - p3.y) * (p12.y * 2) - (p12.x^2 * 3 + A) * (p12.x - p3.x): 'tangential_to_curve' + }) + + +def colinear(p1, p2, p3): + """Return a set of zero-expressions for ((x1,y1),(x2,y2),(x3,y3)) to be collinear""" + return constraints(zero={ + (p1.y - p2.y) * (p1.x - p3.x) - (p1.y - p3.y) * (p1.x - p2.x): 'colinear_1', + (p2.y - p3.y) * (p2.x - p1.x) - (p2.y - p1.y) * (p2.x - p3.x): 'colinear_2', + (p3.y - p1.y) * (p3.x - p2.x) - (p3.y - p2.y) * (p3.x - p1.x): 'colinear_3' + }) + + +def good_affine_point(p): + return constraints(nonzero={p.x : 'nonzero_x', p.y : 'nonzero_y'}) + + +def good_jacobian_point(p): + return constraints(nonzero={p.X : 'nonzero_X', p.Y : 'nonzero_Y', p.Z^6 : 'nonzero_Z'}) + + +def good_point(p): + return constraints(nonzero={p.Z^6 : 'nonzero_X'}) + + +def finite(p, *affine_fns): + con = good_point(p) + constraints(zero={p.Infinity : 'finite_point'}) + if p.Z != 0: + return con + reduce(lambda a, b: a + b, (f(affinepoint(p.X / p.Z^2, p.Y / p.Z^3)) for f in affine_fns), con) + else: + return con + +def infinite(p): + return constraints(nonzero={p.Infinity : 'infinite_point'}) + + +def law_jacobian_weierstrass_add(A, B, pa, pb, pA, pB, pC): + """Check whether the passed set of coordinates is a valid Jacobian add, given assumptions""" + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + on_weierstrass_curve(A, B, pb) + + finite(pA) + + finite(pB) + + constraints(nonzero={pa.x - pb.x : 'different_x'})) + require = (finite(pC, lambda pc: on_weierstrass_curve(A, B, pc) + + colinear(pa, pb, negate(pc)))) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_double(A, B, pa, pb, pA, pB, pC): + """Check whether the passed set of coordinates is a valid Jacobian doubling, given assumptions""" + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + on_weierstrass_curve(A, B, pb) + + finite(pA) + + finite(pB) + + constraints(zero={pa.x - pb.x : 'equal_x', pa.y - pb.y : 'equal_y'})) + require = (finite(pC, lambda pc: on_weierstrass_curve(A, B, pc) + + tangential_to_weierstrass_curve(A, B, pa, negate(pc)))) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_opposites(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + on_weierstrass_curve(A, B, pb) + + finite(pA) + + finite(pB) + + constraints(zero={pa.x - pb.x : 'equal_x', pa.y + pb.y : 'opposite_y'})) + require = infinite(pC) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_infinite_a(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pb) + + infinite(pA) + + finite(pB)) + require = finite(pC, lambda pc: constraints(zero={pc.x - pb.x : 'c.x=b.x', pc.y - pb.y : 'c.y=b.y'})) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_infinite_b(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + infinite(pB) + + finite(pA)) + require = finite(pC, lambda pc: constraints(zero={pc.x - pa.x : 'c.x=a.x', pc.y - pa.y : 'c.y=a.y'})) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_infinite_ab(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + infinite(pA) + + infinite(pB)) + require = infinite(pC) + return (assumeLaw, require) + + +laws_jacobian_weierstrass = { + 'add': law_jacobian_weierstrass_add, + 'double': law_jacobian_weierstrass_double, + 'add_opposite': law_jacobian_weierstrass_add_opposites, + 'add_infinite_a': law_jacobian_weierstrass_add_infinite_a, + 'add_infinite_b': law_jacobian_weierstrass_add_infinite_b, + 'add_infinite_ab': law_jacobian_weierstrass_add_infinite_ab +} + + +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) + points = [] + for x in xrange(0, p): + for y in xrange(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): + for pa in points: + for pb in points: + for ia in xrange(2): + for ib in xrange(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): + assumeAssert, assumeBranch, pC = formula(branch, pA, pB) + pC.X = F(pC.X) + pC.Y = F(pC.Y) + pC.Z = F(pC.Z) + pC.Infinity = F(pC.Infinity) + r, e = concrete_verify(assumeAssert + assumeBranch) + if r: + match = False + for key in laws_jacobian_weierstrass: + assumeLaw, require = laws_jacobian_weierstrass[key](A, B, pa, pb, pA, pB, pC) + 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) + 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 + + +def check_symbolic_function(R, assumeAssert, assumeBranch, f, A, B, pa, pb, pA, pB, pC): + assumeLaw, require = f(A, B, pa, pb, pA, pB, pC) + return check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require) + +def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula): + """Verify an implementation of addition of Jacobian points on a Weierstrass curve symbolically""" + R. = PolynomialRing(QQ,8,order='invlex') + lift = lambda x: fastfrac(R,x) + ax = lift(ax) + ay = lift(ay) + Az = lift(Az) + bx = lift(bx) + by = lift(by) + Bz = lift(Bz) + Ai = lift(Ai) + Bi = lift(Bi) + + pa = affinepoint(ax, ay, Ai) + pb = affinepoint(bx, by, Bi) + pA = jacobianpoint(ax * Az^2, ay * Az^3, Az, Ai) + pB = jacobianpoint(bx * Bz^2, by * Bz^3, Bz, Bi) + + res = {} + + for key in laws_jacobian_weierstrass: + res[key] = [] + + print ("Formula " + name + ":") + count = 0 + for branch in xrange(branches): + assumeFormula, assumeBranch, pC = formula(branch, pA, pB) + 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)) + + for key in res: + 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 diff --git a/src/cryptoconditions/src/include/secp256k1/src/asm/field_10x26_arm.s b/src/cryptoconditions/src/include/secp256k1/src/asm/field_10x26_arm.s new file mode 100644 index 000000000..5a9cc3ffc --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/asm/field_10x26_arm.s @@ -0,0 +1,919 @@ +@ 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 http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ +/* +ARM implementation of field_10x26 inner loops. + +Note: + +- To avoid unnecessary loads and make use of available registers, two + 'passes' have every time been interleaved, with the odd passes accumulating c' and d' + which will be added to c and d respectively in the even passes + +*/ + + .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 + .set field_R0, 0x3d10 + .set field_R1, 0x400 + .set field_not_M, 0xfc000000 @ ~M = ~0x3ffffff + + .align 2 + .global secp256k1_fe_mul_inner + .type secp256k1_fe_mul_inner, %function + @ Arguments: + @ r0 r Restrict: can overlap with a, not with b + @ r1 a + @ r2 b + @ Stack (total 4+10*4 = 44) + @ sp + #0 saved 'r' pointer + @ sp + #4 + 4*X t0,t1,t2,t3,t4,t5,t6,t7,u8,t9 +secp256k1_fe_mul_inner: + stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r14} + sub sp, sp, #48 @ frame=44 + alignment + str r0, [sp, #0] @ save result address, we need it only at the end + + /****************************************** + * Main computation code. + ****************************************** + + Allocation: + r0,r14,r7,r8 scratch + r1 a (pointer) + r2 b (pointer) + r3:r4 c + r5:r6 d + r11:r12 c' + r9:r10 d' + + Note: do not write to r[] here, it may overlap with a[] + */ + + /* A - interleaved with B */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #9*4] @ b[9] + ldr r0, [r1, #1*4] @ a[1] + umull r5, r6, r7, r8 @ d = a[0] * b[9] + ldr r14, [r2, #8*4] @ b[8] + umull r9, r10, r0, r8 @ d' = a[1] * b[9] + ldr r7, [r1, #2*4] @ a[2] + umlal r5, r6, r0, r14 @ d += a[1] * b[8] + ldr r8, [r2, #7*4] @ b[7] + umlal r9, r10, r7, r14 @ d' += a[2] * b[8] + ldr r0, [r1, #3*4] @ a[3] + umlal r5, r6, r7, r8 @ d += a[2] * b[7] + ldr r14, [r2, #6*4] @ b[6] + umlal r9, r10, r0, r8 @ d' += a[3] * b[7] + ldr r7, [r1, #4*4] @ a[4] + umlal r5, r6, r0, r14 @ d += a[3] * b[6] + ldr r8, [r2, #5*4] @ b[5] + umlal r9, r10, r7, r14 @ d' += a[4] * b[6] + ldr r0, [r1, #5*4] @ a[5] + umlal r5, r6, r7, r8 @ d += a[4] * b[5] + ldr r14, [r2, #4*4] @ b[4] + umlal r9, r10, r0, r8 @ d' += a[5] * b[5] + ldr r7, [r1, #6*4] @ a[6] + umlal r5, r6, r0, r14 @ d += a[5] * b[4] + ldr r8, [r2, #3*4] @ b[3] + umlal r9, r10, r7, r14 @ d' += a[6] * b[4] + ldr r0, [r1, #7*4] @ a[7] + umlal r5, r6, r7, r8 @ d += a[6] * b[3] + ldr r14, [r2, #2*4] @ b[2] + umlal r9, r10, r0, r8 @ d' += a[7] * b[3] + ldr r7, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r14 @ d += a[7] * b[2] + ldr r8, [r2, #1*4] @ b[1] + umlal r9, r10, r7, r14 @ d' += a[8] * b[2] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r8 @ d += a[8] * b[1] + ldr r14, [r2, #0*4] @ b[0] + umlal r9, r10, r0, r8 @ d' += a[9] * b[1] + ldr r7, [r1, #0*4] @ a[0] + umlal r5, r6, r0, r14 @ d += a[9] * b[0] + @ r7,r14 used in B + + bic r0, r5, field_not_M @ t9 = d & M + str r0, [sp, #4 + 4*9] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + /* B */ + umull r3, r4, r7, r14 @ c = a[0] * b[0] + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u0 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u0 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t0 = c & M + str r14, [sp, #4 + 0*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u0 * R1 + umlal r3, r4, r0, r14 + + /* C - interleaved with D */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #2*4] @ b[2] + ldr r14, [r2, #1*4] @ b[1] + umull r11, r12, r7, r8 @ c' = a[0] * b[2] + ldr r0, [r1, #1*4] @ a[1] + umlal r3, r4, r7, r14 @ c += a[0] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r11, r12, r0, r14 @ c' += a[1] * b[1] + ldr r7, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r8 @ c += a[1] * b[0] + ldr r14, [r2, #9*4] @ b[9] + umlal r11, r12, r7, r8 @ c' += a[2] * b[0] + ldr r0, [r1, #3*4] @ a[3] + umlal r5, r6, r7, r14 @ d += a[2] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umull r9, r10, r0, r14 @ d' = a[3] * b[9] + ldr r7, [r1, #4*4] @ a[4] + umlal r5, r6, r0, r8 @ d += a[3] * b[8] + ldr r14, [r2, #7*4] @ b[7] + umlal r9, r10, r7, r8 @ d' += a[4] * b[8] + ldr r0, [r1, #5*4] @ a[5] + umlal r5, r6, r7, r14 @ d += a[4] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r9, r10, r0, r14 @ d' += a[5] * b[7] + ldr r7, [r1, #6*4] @ a[6] + umlal r5, r6, r0, r8 @ d += a[5] * b[6] + ldr r14, [r2, #5*4] @ b[5] + umlal r9, r10, r7, r8 @ d' += a[6] * b[6] + ldr r0, [r1, #7*4] @ a[7] + umlal r5, r6, r7, r14 @ d += a[6] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r9, r10, r0, r14 @ d' += a[7] * b[5] + ldr r7, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r8 @ d += a[7] * b[4] + ldr r14, [r2, #3*4] @ b[3] + umlal r9, r10, r7, r8 @ d' += a[8] * b[4] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r14 @ d += a[8] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r9, r10, r0, r14 @ d' += a[9] * b[3] + umlal r5, r6, r0, r8 @ d += a[9] * b[2] + + bic r0, r5, field_not_M @ u1 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u1 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t1 = c & M + str r14, [sp, #4 + 1*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u1 * R1 + umlal r3, r4, r0, r14 + + /* D */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u2 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u2 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t2 = c & M + str r14, [sp, #4 + 2*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u2 * R1 + umlal r3, r4, r0, r14 + + /* E - interleaved with F */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #4*4] @ b[4] + umull r11, r12, r7, r8 @ c' = a[0] * b[4] + ldr r8, [r2, #3*4] @ b[3] + umlal r3, r4, r7, r8 @ c += a[0] * b[3] + ldr r7, [r1, #1*4] @ a[1] + umlal r11, r12, r7, r8 @ c' += a[1] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r3, r4, r7, r8 @ c += a[1] * b[2] + ldr r7, [r1, #2*4] @ a[2] + umlal r11, r12, r7, r8 @ c' += a[2] * b[2] + ldr r8, [r2, #1*4] @ b[1] + umlal r3, r4, r7, r8 @ c += a[2] * b[1] + ldr r7, [r1, #3*4] @ a[3] + umlal r11, r12, r7, r8 @ c' += a[3] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r3, r4, r7, r8 @ c += a[3] * b[0] + ldr r7, [r1, #4*4] @ a[4] + umlal r11, r12, r7, r8 @ c' += a[4] * b[0] + ldr r8, [r2, #9*4] @ b[9] + umlal r5, r6, r7, r8 @ d += a[4] * b[9] + ldr r7, [r1, #5*4] @ a[5] + umull r9, r10, r7, r8 @ d' = a[5] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umlal r5, r6, r7, r8 @ d += a[5] * b[8] + ldr r7, [r1, #6*4] @ a[6] + umlal r9, r10, r7, r8 @ d' += a[6] * b[8] + ldr r8, [r2, #7*4] @ b[7] + umlal r5, r6, r7, r8 @ d += a[6] * b[7] + ldr r7, [r1, #7*4] @ a[7] + umlal r9, r10, r7, r8 @ d' += a[7] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r5, r6, r7, r8 @ d += a[7] * b[6] + ldr r7, [r1, #8*4] @ a[8] + umlal r9, r10, r7, r8 @ d' += a[8] * b[6] + ldr r8, [r2, #5*4] @ b[5] + umlal r5, r6, r7, r8 @ d += a[8] * b[5] + ldr r7, [r1, #9*4] @ a[9] + umlal r9, r10, r7, r8 @ d' += a[9] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r5, r6, r7, r8 @ d += a[9] * b[4] + + bic r0, r5, field_not_M @ u3 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u3 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t3 = c & M + str r14, [sp, #4 + 3*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u3 * R1 + umlal r3, r4, r0, r14 + + /* F */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u4 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u4 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t4 = c & M + str r14, [sp, #4 + 4*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u4 * R1 + umlal r3, r4, r0, r14 + + /* G - interleaved with H */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #6*4] @ b[6] + ldr r14, [r2, #5*4] @ b[5] + umull r11, r12, r7, r8 @ c' = a[0] * b[6] + ldr r0, [r1, #1*4] @ a[1] + umlal r3, r4, r7, r14 @ c += a[0] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r11, r12, r0, r14 @ c' += a[1] * b[5] + ldr r7, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r8 @ c += a[1] * b[4] + ldr r14, [r2, #3*4] @ b[3] + umlal r11, r12, r7, r8 @ c' += a[2] * b[4] + ldr r0, [r1, #3*4] @ a[3] + umlal r3, r4, r7, r14 @ c += a[2] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r11, r12, r0, r14 @ c' += a[3] * b[3] + ldr r7, [r1, #4*4] @ a[4] + umlal r3, r4, r0, r8 @ c += a[3] * b[2] + ldr r14, [r2, #1*4] @ b[1] + umlal r11, r12, r7, r8 @ c' += a[4] * b[2] + ldr r0, [r1, #5*4] @ a[5] + umlal r3, r4, r7, r14 @ c += a[4] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r11, r12, r0, r14 @ c' += a[5] * b[1] + ldr r7, [r1, #6*4] @ a[6] + umlal r3, r4, r0, r8 @ c += a[5] * b[0] + ldr r14, [r2, #9*4] @ b[9] + umlal r11, r12, r7, r8 @ c' += a[6] * b[0] + ldr r0, [r1, #7*4] @ a[7] + umlal r5, r6, r7, r14 @ d += a[6] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umull r9, r10, r0, r14 @ d' = a[7] * b[9] + ldr r7, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r8 @ d += a[7] * b[8] + ldr r14, [r2, #7*4] @ b[7] + umlal r9, r10, r7, r8 @ d' += a[8] * b[8] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r14 @ d += a[8] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r9, r10, r0, r14 @ d' += a[9] * b[7] + umlal r5, r6, r0, r8 @ d += a[9] * b[6] + + bic r0, r5, field_not_M @ u5 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u5 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t5 = c & M + str r14, [sp, #4 + 5*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u5 * R1 + umlal r3, r4, r0, r14 + + /* H */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u6 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u6 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t6 = c & M + str r14, [sp, #4 + 6*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u6 * R1 + umlal r3, r4, r0, r14 + + /* I - interleaved with J */ + ldr r8, [r2, #8*4] @ b[8] + ldr r7, [r1, #0*4] @ a[0] + ldr r14, [r2, #7*4] @ b[7] + umull r11, r12, r7, r8 @ c' = a[0] * b[8] + ldr r0, [r1, #1*4] @ a[1] + umlal r3, r4, r7, r14 @ c += a[0] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r11, r12, r0, r14 @ c' += a[1] * b[7] + ldr r7, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r8 @ c += a[1] * b[6] + ldr r14, [r2, #5*4] @ b[5] + umlal r11, r12, r7, r8 @ c' += a[2] * b[6] + ldr r0, [r1, #3*4] @ a[3] + umlal r3, r4, r7, r14 @ c += a[2] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r11, r12, r0, r14 @ c' += a[3] * b[5] + ldr r7, [r1, #4*4] @ a[4] + umlal r3, r4, r0, r8 @ c += a[3] * b[4] + ldr r14, [r2, #3*4] @ b[3] + umlal r11, r12, r7, r8 @ c' += a[4] * b[4] + ldr r0, [r1, #5*4] @ a[5] + umlal r3, r4, r7, r14 @ c += a[4] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r11, r12, r0, r14 @ c' += a[5] * b[3] + ldr r7, [r1, #6*4] @ a[6] + umlal r3, r4, r0, r8 @ c += a[5] * b[2] + ldr r14, [r2, #1*4] @ b[1] + umlal r11, r12, r7, r8 @ c' += a[6] * b[2] + ldr r0, [r1, #7*4] @ a[7] + umlal r3, r4, r7, r14 @ c += a[6] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r11, r12, r0, r14 @ c' += a[7] * b[1] + ldr r7, [r1, #8*4] @ a[8] + umlal r3, r4, r0, r8 @ c += a[7] * b[0] + ldr r14, [r2, #9*4] @ b[9] + umlal r11, r12, r7, r8 @ c' += a[8] * b[0] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r14 @ d += a[8] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umull r9, r10, r0, r14 @ d' = a[9] * b[9] + umlal r5, r6, r0, r8 @ d += a[9] * b[8] + + bic r0, r5, field_not_M @ u7 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u7 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t7 = c & M + str r14, [sp, #4 + 7*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u7 * R1 + umlal r3, r4, r0, r14 + + /* J */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u8 = d & M + str r0, [sp, #4 + 8*4] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u8 * R0 + umlal r3, r4, r0, r14 + + /****************************************** + * compute and write back result + ****************************************** + Allocation: + r0 r + r3:r4 c + r5:r6 d + r7 t0 + r8 t1 + r9 t2 + r11 u8 + r12 t9 + r1,r2,r10,r14 scratch + + Note: do not read from a[] after here, it may overlap with r[] + */ + ldr r0, [sp, #0] + add r1, sp, #4 + 3*4 @ r[3..7] = t3..7, r11=u8, r12=t9 + ldmia r1, {r2,r7,r8,r9,r10,r11,r12} + add r1, r0, #3*4 + stmia r1, {r2,r7,r8,r9,r10} + + bic r2, r3, field_not_M @ r[8] = c & M + str r2, [r0, #8*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u8 * R1 + umlal r3, r4, r11, r14 + movw r14, field_R0 @ c += d * R0 + umlal r3, r4, r5, r14 + adds r3, r3, r12 @ c += t9 + adc r4, r4, #0 + + add r1, sp, #4 + 0*4 @ r7,r8,r9 = t0,t1,t2 + ldmia r1, {r7,r8,r9} + + ubfx r2, r3, #0, #22 @ r[9] = c & (M >> 4) + str r2, [r0, #9*4] + mov r3, r3, lsr #22 @ c >>= 22 + orr r3, r3, r4, asl #10 + mov r4, r4, lsr #22 + movw r14, field_R1 << 4 @ c += d * (R1 << 4) + umlal r3, r4, r5, r14 + + movw r14, field_R0 >> 4 @ d = c * (R0 >> 4) + t0 (64x64 multiply+add) + umull r5, r6, r3, r14 @ d = c.lo * (R0 >> 4) + adds r5, r5, r7 @ d.lo += t0 + mla r6, r14, r4, r6 @ d.hi += c.hi * (R0 >> 4) + adc r6, r6, 0 @ d.hi += carry + + bic r2, r5, field_not_M @ r[0] = d & M + str r2, [r0, #0*4] + + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + movw r14, field_R1 >> 4 @ d += c * (R1 >> 4) + t1 (64x64 multiply+add) + umull r1, r2, r3, r14 @ tmp = c.lo * (R1 >> 4) + adds r5, r5, r8 @ d.lo += t1 + adc r6, r6, #0 @ d.hi += carry + adds r5, r5, r1 @ d.lo += tmp.lo + mla r2, r14, r4, r2 @ tmp.hi += c.hi * (R1 >> 4) + adc r6, r6, r2 @ d.hi += carry + tmp.hi + + bic r2, r5, field_not_M @ r[1] = d & M + str r2, [r0, #1*4] + mov r5, r5, lsr #26 @ d >>= 26 (ignore hi) + orr r5, r5, r6, asl #6 + + add r5, r5, r9 @ d += t2 + str r5, [r0, #2*4] @ r[2] = d + + add sp, sp, #48 + ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc} + .size secp256k1_fe_mul_inner, .-secp256k1_fe_mul_inner + + .align 2 + .global secp256k1_fe_sqr_inner + .type secp256k1_fe_sqr_inner, %function + @ Arguments: + @ r0 r Can overlap with a + @ r1 a + @ Stack (total 4+10*4 = 44) + @ sp + #0 saved 'r' pointer + @ sp + #4 + 4*X t0,t1,t2,t3,t4,t5,t6,t7,u8,t9 +secp256k1_fe_sqr_inner: + stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r14} + sub sp, sp, #48 @ frame=44 + alignment + str r0, [sp, #0] @ save result address, we need it only at the end + /****************************************** + * Main computation code. + ****************************************** + + Allocation: + r0,r14,r2,r7,r8 scratch + r1 a (pointer) + r3:r4 c + r5:r6 d + r11:r12 c' + r9:r10 d' + + Note: do not write to r[] here, it may overlap with a[] + */ + /* A interleaved with B */ + ldr r0, [r1, #1*4] @ a[1]*2 + ldr r7, [r1, #0*4] @ a[0] + mov r0, r0, asl #1 + ldr r14, [r1, #9*4] @ a[9] + umull r3, r4, r7, r7 @ c = a[0] * a[0] + ldr r8, [r1, #8*4] @ a[8] + mov r7, r7, asl #1 + umull r5, r6, r7, r14 @ d = a[0]*2 * a[9] + ldr r7, [r1, #2*4] @ a[2]*2 + umull r9, r10, r0, r14 @ d' = a[1]*2 * a[9] + ldr r14, [r1, #7*4] @ a[7] + umlal r5, r6, r0, r8 @ d += a[1]*2 * a[8] + mov r7, r7, asl #1 + ldr r0, [r1, #3*4] @ a[3]*2 + umlal r9, r10, r7, r8 @ d' += a[2]*2 * a[8] + ldr r8, [r1, #6*4] @ a[6] + umlal r5, r6, r7, r14 @ d += a[2]*2 * a[7] + mov r0, r0, asl #1 + ldr r7, [r1, #4*4] @ a[4]*2 + umlal r9, r10, r0, r14 @ d' += a[3]*2 * a[7] + ldr r14, [r1, #5*4] @ a[5] + mov r7, r7, asl #1 + umlal r5, r6, r0, r8 @ d += a[3]*2 * a[6] + umlal r9, r10, r7, r8 @ d' += a[4]*2 * a[6] + umlal r5, r6, r7, r14 @ d += a[4]*2 * a[5] + umlal r9, r10, r14, r14 @ d' += a[5] * a[5] + + bic r0, r5, field_not_M @ t9 = d & M + str r0, [sp, #4 + 9*4] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + /* B */ + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u0 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u0 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t0 = c & M + str r14, [sp, #4 + 0*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u0 * R1 + umlal r3, r4, r0, r14 + + /* C interleaved with D */ + ldr r0, [r1, #0*4] @ a[0]*2 + ldr r14, [r1, #1*4] @ a[1] + mov r0, r0, asl #1 + ldr r8, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r14 @ c += a[0]*2 * a[1] + mov r7, r8, asl #1 @ a[2]*2 + umull r11, r12, r14, r14 @ c' = a[1] * a[1] + ldr r14, [r1, #9*4] @ a[9] + umlal r11, r12, r0, r8 @ c' += a[0]*2 * a[2] + ldr r0, [r1, #3*4] @ a[3]*2 + ldr r8, [r1, #8*4] @ a[8] + umlal r5, r6, r7, r14 @ d += a[2]*2 * a[9] + mov r0, r0, asl #1 + ldr r7, [r1, #4*4] @ a[4]*2 + umull r9, r10, r0, r14 @ d' = a[3]*2 * a[9] + ldr r14, [r1, #7*4] @ a[7] + umlal r5, r6, r0, r8 @ d += a[3]*2 * a[8] + mov r7, r7, asl #1 + ldr r0, [r1, #5*4] @ a[5]*2 + umlal r9, r10, r7, r8 @ d' += a[4]*2 * a[8] + ldr r8, [r1, #6*4] @ a[6] + mov r0, r0, asl #1 + umlal r5, r6, r7, r14 @ d += a[4]*2 * a[7] + umlal r9, r10, r0, r14 @ d' += a[5]*2 * a[7] + umlal r5, r6, r0, r8 @ d += a[5]*2 * a[6] + umlal r9, r10, r8, r8 @ d' += a[6] * a[6] + + bic r0, r5, field_not_M @ u1 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u1 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t1 = c & M + str r14, [sp, #4 + 1*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u1 * R1 + umlal r3, r4, r0, r14 + + /* D */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u2 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u2 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t2 = c & M + str r14, [sp, #4 + 2*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u2 * R1 + umlal r3, r4, r0, r14 + + /* E interleaved with F */ + ldr r7, [r1, #0*4] @ a[0]*2 + ldr r0, [r1, #1*4] @ a[1]*2 + ldr r14, [r1, #2*4] @ a[2] + mov r7, r7, asl #1 + ldr r8, [r1, #3*4] @ a[3] + ldr r2, [r1, #4*4] + umlal r3, r4, r7, r8 @ c += a[0]*2 * a[3] + mov r0, r0, asl #1 + umull r11, r12, r7, r2 @ c' = a[0]*2 * a[4] + mov r2, r2, asl #1 @ a[4]*2 + umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[3] + ldr r8, [r1, #9*4] @ a[9] + umlal r3, r4, r0, r14 @ c += a[1]*2 * a[2] + ldr r0, [r1, #5*4] @ a[5]*2 + umlal r11, r12, r14, r14 @ c' += a[2] * a[2] + ldr r14, [r1, #8*4] @ a[8] + mov r0, r0, asl #1 + umlal r5, r6, r2, r8 @ d += a[4]*2 * a[9] + ldr r7, [r1, #6*4] @ a[6]*2 + umull r9, r10, r0, r8 @ d' = a[5]*2 * a[9] + mov r7, r7, asl #1 + ldr r8, [r1, #7*4] @ a[7] + umlal r5, r6, r0, r14 @ d += a[5]*2 * a[8] + umlal r9, r10, r7, r14 @ d' += a[6]*2 * a[8] + umlal r5, r6, r7, r8 @ d += a[6]*2 * a[7] + umlal r9, r10, r8, r8 @ d' += a[7] * a[7] + + bic r0, r5, field_not_M @ u3 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u3 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t3 = c & M + str r14, [sp, #4 + 3*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u3 * R1 + umlal r3, r4, r0, r14 + + /* F */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u4 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u4 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t4 = c & M + str r14, [sp, #4 + 4*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u4 * R1 + umlal r3, r4, r0, r14 + + /* G interleaved with H */ + ldr r7, [r1, #0*4] @ a[0]*2 + ldr r0, [r1, #1*4] @ a[1]*2 + mov r7, r7, asl #1 + ldr r8, [r1, #5*4] @ a[5] + ldr r2, [r1, #6*4] @ a[6] + umlal r3, r4, r7, r8 @ c += a[0]*2 * a[5] + ldr r14, [r1, #4*4] @ a[4] + mov r0, r0, asl #1 + umull r11, r12, r7, r2 @ c' = a[0]*2 * a[6] + ldr r7, [r1, #2*4] @ a[2]*2 + umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[5] + mov r7, r7, asl #1 + ldr r8, [r1, #3*4] @ a[3] + umlal r3, r4, r0, r14 @ c += a[1]*2 * a[4] + mov r0, r2, asl #1 @ a[6]*2 + umlal r11, r12, r7, r14 @ c' += a[2]*2 * a[4] + ldr r14, [r1, #9*4] @ a[9] + umlal r3, r4, r7, r8 @ c += a[2]*2 * a[3] + ldr r7, [r1, #7*4] @ a[7]*2 + umlal r11, r12, r8, r8 @ c' += a[3] * a[3] + mov r7, r7, asl #1 + ldr r8, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r14 @ d += a[6]*2 * a[9] + umull r9, r10, r7, r14 @ d' = a[7]*2 * a[9] + umlal r5, r6, r7, r8 @ d += a[7]*2 * a[8] + umlal r9, r10, r8, r8 @ d' += a[8] * a[8] + + bic r0, r5, field_not_M @ u5 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u5 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t5 = c & M + str r14, [sp, #4 + 5*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u5 * R1 + umlal r3, r4, r0, r14 + + /* H */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u6 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u6 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t6 = c & M + str r14, [sp, #4 + 6*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u6 * R1 + umlal r3, r4, r0, r14 + + /* I interleaved with J */ + ldr r7, [r1, #0*4] @ a[0]*2 + ldr r0, [r1, #1*4] @ a[1]*2 + mov r7, r7, asl #1 + ldr r8, [r1, #7*4] @ a[7] + ldr r2, [r1, #8*4] @ a[8] + umlal r3, r4, r7, r8 @ c += a[0]*2 * a[7] + ldr r14, [r1, #6*4] @ a[6] + mov r0, r0, asl #1 + umull r11, r12, r7, r2 @ c' = a[0]*2 * a[8] + ldr r7, [r1, #2*4] @ a[2]*2 + umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[7] + ldr r8, [r1, #5*4] @ a[5] + umlal r3, r4, r0, r14 @ c += a[1]*2 * a[6] + ldr r0, [r1, #3*4] @ a[3]*2 + mov r7, r7, asl #1 + umlal r11, r12, r7, r14 @ c' += a[2]*2 * a[6] + ldr r14, [r1, #4*4] @ a[4] + mov r0, r0, asl #1 + umlal r3, r4, r7, r8 @ c += a[2]*2 * a[5] + mov r2, r2, asl #1 @ a[8]*2 + umlal r11, r12, r0, r8 @ c' += a[3]*2 * a[5] + umlal r3, r4, r0, r14 @ c += a[3]*2 * a[4] + umlal r11, r12, r14, r14 @ c' += a[4] * a[4] + ldr r8, [r1, #9*4] @ a[9] + umlal r5, r6, r2, r8 @ d += a[8]*2 * a[9] + @ r8 will be used in J + + bic r0, r5, field_not_M @ u7 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u7 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t7 = c & M + str r14, [sp, #4 + 7*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u7 * R1 + umlal r3, r4, r0, r14 + + /* J */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + umlal r5, r6, r8, r8 @ d += a[9] * a[9] + + bic r0, r5, field_not_M @ u8 = d & M + str r0, [sp, #4 + 8*4] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u8 * R0 + umlal r3, r4, r0, r14 + + /****************************************** + * compute and write back result + ****************************************** + Allocation: + r0 r + r3:r4 c + r5:r6 d + r7 t0 + r8 t1 + r9 t2 + r11 u8 + r12 t9 + r1,r2,r10,r14 scratch + + Note: do not read from a[] after here, it may overlap with r[] + */ + ldr r0, [sp, #0] + add r1, sp, #4 + 3*4 @ r[3..7] = t3..7, r11=u8, r12=t9 + ldmia r1, {r2,r7,r8,r9,r10,r11,r12} + add r1, r0, #3*4 + stmia r1, {r2,r7,r8,r9,r10} + + bic r2, r3, field_not_M @ r[8] = c & M + str r2, [r0, #8*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u8 * R1 + umlal r3, r4, r11, r14 + movw r14, field_R0 @ c += d * R0 + umlal r3, r4, r5, r14 + adds r3, r3, r12 @ c += t9 + adc r4, r4, #0 + + add r1, sp, #4 + 0*4 @ r7,r8,r9 = t0,t1,t2 + ldmia r1, {r7,r8,r9} + + ubfx r2, r3, #0, #22 @ r[9] = c & (M >> 4) + str r2, [r0, #9*4] + mov r3, r3, lsr #22 @ c >>= 22 + orr r3, r3, r4, asl #10 + mov r4, r4, lsr #22 + movw r14, field_R1 << 4 @ c += d * (R1 << 4) + umlal r3, r4, r5, r14 + + movw r14, field_R0 >> 4 @ d = c * (R0 >> 4) + t0 (64x64 multiply+add) + umull r5, r6, r3, r14 @ d = c.lo * (R0 >> 4) + adds r5, r5, r7 @ d.lo += t0 + mla r6, r14, r4, r6 @ d.hi += c.hi * (R0 >> 4) + adc r6, r6, 0 @ d.hi += carry + + bic r2, r5, field_not_M @ r[0] = d & M + str r2, [r0, #0*4] + + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + movw r14, field_R1 >> 4 @ d += c * (R1 >> 4) + t1 (64x64 multiply+add) + umull r1, r2, r3, r14 @ tmp = c.lo * (R1 >> 4) + adds r5, r5, r8 @ d.lo += t1 + adc r6, r6, #0 @ d.hi += carry + adds r5, r5, r1 @ d.lo += tmp.lo + mla r2, r14, r4, r2 @ tmp.hi += c.hi * (R1 >> 4) + adc r6, r6, r2 @ d.hi += carry + tmp.hi + + bic r2, r5, field_not_M @ r[1] = d & M + str r2, [r0, #1*4] + mov r5, r5, lsr #26 @ d >>= 26 (ignore hi) + orr r5, r5, r6, asl #6 + + add r5, r5, r9 @ d += t2 + str r5, [r0, #2*4] @ r[2] = d + + add sp, sp, #48 + ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc} + .size secp256k1_fe_sqr_inner, .-secp256k1_fe_sqr_inner + diff --git a/src/cryptoconditions/src/include/secp256k1/src/basic-config.h b/src/cryptoconditions/src/include/secp256k1/src/basic-config.h new file mode 100644 index 000000000..fc588061c --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/basic-config.h @@ -0,0 +1,33 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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/cryptoconditions/src/include/secp256k1/src/bench.h b/src/cryptoconditions/src/include/secp256k1/src/bench.h new file mode 100644 index 000000000..d5ebe0130 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/bench.h @@ -0,0 +1,66 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_BENCH_H +#define SECP256K1_BENCH_H + +#include +#include +#include "sys/time.h" + +static double gettimedouble(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_usec * 0.000001 + tv.tv_sec; +} + +void print_number(double x) { + double y = x; + int c = 0; + if (y < 0.0) { + y = -y; + } + while (y > 0 && y < 100.0) { + y *= 10.0; + c++; + } + printf("%.*f", c, x); +} + +void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), void (*teardown)(void*), void* data, int count, int iter) { + int i; + double min = HUGE_VAL; + double sum = 0.0; + double max = 0.0; + for (i = 0; i < count; i++) { + double begin, total; + if (setup != NULL) { + setup(data); + } + begin = gettimedouble(); + benchmark(data); + total = gettimedouble() - begin; + if (teardown != NULL) { + teardown(data); + } + if (total < min) { + min = total; + } + if (total > max) { + max = total; + } + 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"); +} + +#endif /* SECP256K1_BENCH_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/bench_ecdh.c b/src/cryptoconditions/src/include/secp256k1/src/bench_ecdh.c new file mode 100644 index 000000000..2de5126d6 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/bench_ecdh.c @@ -0,0 +1,54 @@ +/********************************************************************** + * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include + +#include "include/secp256k1.h" +#include "include/secp256k1_ecdh.h" +#include "util.h" +#include "bench.h" + +typedef struct { + secp256k1_context *ctx; + secp256k1_pubkey point; + unsigned char scalar[32]; +} bench_ecdh; + +static void bench_ecdh_setup(void* arg) { + int i; + bench_ecdh *data = (bench_ecdh*)arg; + const unsigned char point[] = { + 0x03, + 0x54, 0x94, 0xc1, 0x5d, 0x32, 0x09, 0x97, 0x06, + 0xc2, 0x39, 0x5f, 0x94, 0x34, 0x87, 0x45, 0xfd, + 0x75, 0x7c, 0xe3, 0x0e, 0x4e, 0x8c, 0x90, 0xfb, + 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) { + int i; + unsigned char res[32]; + bench_ecdh *data = (bench_ecdh*)arg; + + for (i = 0; i < 20000; i++) { + CHECK(secp256k1_ecdh(data->ctx, res, &data->point, data->scalar) == 1); + } +} + +int main(void) { + bench_ecdh data; + + run_benchmark("ecdh", bench_ecdh, bench_ecdh_setup, NULL, &data, 10, 20000); + return 0; +} diff --git a/src/cryptoconditions/src/include/secp256k1/src/bench_internal.c b/src/cryptoconditions/src/include/secp256k1/src/bench_internal.c new file mode 100644 index 000000000..9b30c50d0 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/bench_internal.c @@ -0,0 +1,382 @@ +/********************************************************************** + * Copyright (c) 2014-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ +#include + +#include "include/secp256k1.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" + +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; + unsigned char data[64]; + int wnaf[256]; +} bench_inv; + +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_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_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); +} + +void bench_scalar_add(void* arg) { + 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); + } +} + +void bench_scalar_negate(void* arg) { + int i; + bench_inv *data = (bench_inv*)arg; + + for (i = 0; i < 2000000; i++) { + secp256k1_scalar_negate(&data->scalar_x, &data->scalar_x); + } +} + +void bench_scalar_sqr(void* arg) { + int i; + bench_inv *data = (bench_inv*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_scalar_sqr(&data->scalar_x, &data->scalar_x); + } +} + +void bench_scalar_mul(void* arg) { + int i; + bench_inv *data = (bench_inv*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_scalar_mul(&data->scalar_x, &data->scalar_x, &data->scalar_y); + } +} + +#ifdef USE_ENDOMORPHISM +void bench_scalar_split(void* arg) { + int i; + 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); + } +} +#endif + +void bench_scalar_inverse(void* arg) { + int i; + 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); + } +} + +void bench_scalar_inverse_var(void* arg) { + 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); + } +} + +void bench_field_normalize(void* arg) { + int i; + bench_inv *data = (bench_inv*)arg; + + for (i = 0; i < 2000000; i++) { + secp256k1_fe_normalize(&data->fe_x); + } +} + +void bench_field_normalize_weak(void* arg) { + int i; + bench_inv *data = (bench_inv*)arg; + + for (i = 0; i < 2000000; i++) { + secp256k1_fe_normalize_weak(&data->fe_x); + } +} + +void bench_field_mul(void* arg) { + 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); + } +} + +void bench_field_sqr(void* arg) { + int i; + bench_inv *data = (bench_inv*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_fe_sqr(&data->fe_x, &data->fe_x); + } +} + +void bench_field_inverse(void* arg) { + 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); + } +} + +void bench_field_inverse_var(void* arg) { + 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); + } +} + +void bench_field_sqrt(void* arg) { + int i; + bench_inv *data = (bench_inv*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_fe_sqrt(&data->fe_x, &data->fe_x); + secp256k1_fe_add(&data->fe_x, &data->fe_y); + } +} + +void bench_group_double_var(void* arg) { + int i; + bench_inv *data = (bench_inv*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_gej_double_var(&data->gej_x, &data->gej_x, NULL); + } +} + +void bench_group_add_var(void* arg) { + 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); + } +} + +void bench_group_add_affine(void* arg) { + 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); + } +} + +void bench_group_add_affine_var(void* arg) { + 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); + } +} + +void bench_group_jacobi_var(void* arg) { + int i; + bench_inv *data = (bench_inv*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_gej_has_quad_y_var(&data->gej_x); + } +} + +void bench_ecmult_wnaf(void* arg) { + 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); + } +} + +void bench_wnaf_const(void* arg) { + 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); + } +} + + +void bench_sha256(void* arg) { + int i; + bench_inv *data = (bench_inv*)arg; + secp256k1_sha256 sha; + + for (i = 0; i < 20000; i++) { + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, data->data, 32); + secp256k1_sha256_finalize(&sha, data->data); + } +} + +void bench_hmac_sha256(void* arg) { + int i; + bench_inv *data = (bench_inv*)arg; + secp256k1_hmac_sha256 hmac; + + for (i = 0; i < 20000; 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) { + int i; + bench_inv *data = (bench_inv*)arg; + secp256k1_rfc6979_hmac_sha256 rng; + + for (i = 0; i < 20000; 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) { + int i; + (void)arg; + for (i = 0; i < 20; i++) { + secp256k1_context_destroy(secp256k1_context_create(SECP256K1_CONTEXT_VERIFY)); + } +} + +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; + } + 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 + return 0; +} diff --git a/src/cryptoconditions/src/include/secp256k1/src/bench_recover.c b/src/cryptoconditions/src/include/secp256k1/src/bench_recover.c new file mode 100644 index 000000000..506fc1880 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/bench_recover.c @@ -0,0 +1,60 @@ +/********************************************************************** + * Copyright (c) 2014-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include "include/secp256k1.h" +#include "include/secp256k1_recovery.h" +#include "util.h" +#include "bench.h" + +typedef struct { + secp256k1_context *ctx; + unsigned char msg[32]; + unsigned char sig[64]; +} bench_recover; + +void bench_recover(void* arg) { + int i; + bench_recover *data = (bench_recover*)arg; + secp256k1_pubkey pubkey; + unsigned char pubkeyc[33]; + + for (i = 0; i < 20000; i++) { + int j; + size_t pubkeylen = 33; + secp256k1_ecdsa_recoverable_signature sig; + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(data->ctx, &sig, data->sig, i % 2)); + CHECK(secp256k1_ecdsa_recover(data->ctx, &pubkey, &sig, data->msg)); + CHECK(secp256k1_ec_pubkey_serialize(data->ctx, pubkeyc, &pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED)); + for (j = 0; j < 32; j++) { + data->sig[j + 32] = data->msg[j]; /* Move former message to S. */ + data->msg[j] = data->sig[j]; /* Move former R to message. */ + data->sig[j] = pubkeyc[j + 1]; /* Move recovered pubkey X coordinate to R (which must be a valid X coordinate). */ + } + } +} + +void bench_recover_setup(void* arg) { + int i; + bench_recover *data = (bench_recover*)arg; + + for (i = 0; i < 32; i++) { + data->msg[i] = 1 + i; + } + for (i = 0; i < 64; i++) { + data->sig[i] = 65 + i; + } +} + +int main(void) { + bench_recover data; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + + run_benchmark("ecdsa_recover", bench_recover, bench_recover_setup, NULL, &data, 10, 20000); + + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/src/cryptoconditions/src/include/secp256k1/src/bench_sign.c b/src/cryptoconditions/src/include/secp256k1/src/bench_sign.c new file mode 100644 index 000000000..544b43963 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/bench_sign.c @@ -0,0 +1,56 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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/cryptoconditions/src/include/secp256k1/src/bench_verify.c b/src/cryptoconditions/src/include/secp256k1/src/bench_verify.c new file mode 100644 index 000000000..418defa0a --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/bench_verify.c @@ -0,0 +1,112 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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/cryptoconditions/src/include/secp256k1/src/ecdsa.h b/src/cryptoconditions/src/include/secp256k1/src/ecdsa.h new file mode 100644 index 000000000..80590c7cc --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/ecdsa.h @@ -0,0 +1,21 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_ECDSA_H +#define SECP256K1_ECDSA_H + +#include + +#include "scalar.h" +#include "group.h" +#include "ecmult.h" + +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_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/cryptoconditions/src/include/secp256k1/src/ecdsa_impl.h b/src/cryptoconditions/src/include/secp256k1/src/ecdsa_impl.h new file mode 100644 index 000000000..c3400042d --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/ecdsa_impl.h @@ -0,0 +1,313 @@ +/********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + + +#ifndef SECP256K1_ECDSA_IMPL_H +#define SECP256K1_ECDSA_IMPL_H + +#include "scalar.h" +#include "field.h" +#include "group.h" +#include "ecmult.h" +#include "ecmult_gen.h" +#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' + */ +static const secp256k1_fe secp256k1_ecdsa_const_order_as_fe = SECP256K1_FE_CONST( + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, + 0xBAAEDCE6UL, 0xAF48A03BUL, 0xBFD25E8CUL, 0xD0364141UL +); + +/** 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' + */ +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; + if (*sigp >= sigend) { + return -1; + } + b1 = *((*sigp)++); + if (b1 == 0xFF) { + /* X.690-0207 8.1.3.5.c the value 0xFF shall not be used. */ + return -1; + } + if ((b1 & 0x80) == 0) { + /* X.690-0207 8.1.3.4 short form length octets */ + return b1; + } + if (b1 == 0x80) { + /* Indefinite length is not allowed in DER. */ + return -1; + } + /* X.690-207 8.1.3.5 long form length octets */ + lenleft = b1 & 0x7F; + if (lenleft > sigend - *sigp) { + return -1; + } + if (**sigp == 0) { + /* Not the shortest possible length encoding. */ + return -1; + } + if ((size_t)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; + } + while (lenleft > 0) { + ret = (ret << 8) | **sigp; + if (ret + lenleft > (size_t)(sigend - *sigp)) { + /* Result exceeds the length of the passed array. */ + return -1; + } + (*sigp)++; + lenleft--; + } + if (ret < 128) { + /* Not the shortest possible length encoding. */ + return -1; + } + return ret; +} + +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; + + 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) { + /* Exceeds bounds or not at least length 1 (X.690-0207 8.3.1). */ + return 0; + } + if (**sig == 0x00 && rlen > 1 && (((*sig)[1]) & 0x80) == 0x00) { + /* Excessive 0x00 padding. */ + return 0; + } + if (**sig == 0xFF && rlen > 1 && (((*sig)[1]) & 0x80) == 0x80) { + /* Excessive 0xFF padding. */ + return 0; + } + if ((**sig & 0x80) == 0x80) { + /* Negative. */ + overflow = 1; + } + while (rlen > 0 && **sig == 0) { + /* Skip leading zero bytes */ + rlen--; + (*sig)++; + } + if (rlen > 32) { + overflow = 1; + } + if (!overflow) { + memcpy(ra + 32 - rlen, *sig, rlen); + secp256k1_scalar_set_b32(r, ra, &overflow); + } + if (overflow) { + secp256k1_scalar_set_int(r, 0); + } + (*sig) += rlen; + return 1; +} + +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; + 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 */ + return 0; + } + if (sig + rlen != sigend) { + /* Garbage after tuple. */ + return 0; + } + + if (!secp256k1_der_parse_integer(rr, &sig, sigend)) { + return 0; + } + if (!secp256k1_der_parse_integer(rs, &sig, sigend)) { + return 0; + } + + if (sig != sigend) { + /* Trailing garbage inside tuple. */ + return 0; + } + + return 1; +} + +static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const secp256k1_scalar* ar, const secp256k1_scalar* as) { + unsigned char r[33] = {0}, s[33] = {0}; + unsigned char *rp = r, *sp = s; + size_t lenR = 33, lenS = 33; + secp256k1_scalar_get_b32(&r[1], ar); + secp256k1_scalar_get_b32(&s[1], as); + while (lenR > 1 && rp[0] == 0 && rp[1] < 0x80) { lenR--; rp++; } + while (lenS > 1 && sp[0] == 0 && sp[1] < 0x80) { lenS--; sp++; } + if (*size < 6+lenS+lenR) { + *size = 6 + lenS + lenR; + return 0; + } + *size = 6 + lenS + lenR; + sig[0] = 0x30; + sig[1] = 4 + lenS + lenR; + sig[2] = 0x02; + sig[3] = lenR; + memcpy(sig+4, rp, lenR); + sig[4+lenR] = 0x02; + sig[5+lenR] = lenS; + memcpy(sig+lenR+6, sp, lenS); + 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) { + unsigned char c[32]; + secp256k1_scalar sn, u1, u2; +#if !defined(EXHAUSTIVE_TEST_ORDER) + secp256k1_fe xr; +#endif + secp256k1_gej pubkeyj; + secp256k1_gej pr; + + if (secp256k1_scalar_is_zero(sigr) || secp256k1_scalar_is_zero(sigs)) { + return 0; + } + + secp256k1_scalar_inverse_var(&sn, sigs); + 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); + if (secp256k1_gej_is_infinity(&pr)) { + return 0; + } + +#if defined(EXHAUSTIVE_TEST_ORDER) +{ + secp256k1_scalar computed_r; + secp256k1_ge pr_ge; + secp256k1_ge_set_gej(&pr_ge, &pr); + secp256k1_fe_normalize(&pr_ge.x); + + secp256k1_fe_get_b32(c, &pr_ge.x); + secp256k1_scalar_set_b32(&computed_r, c, NULL); + return secp256k1_scalar_eq(sigr, &computed_r); +} +#else + secp256k1_scalar_get_b32(c, sigr); + secp256k1_fe_set_b32(&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), + * compute the remainder modulo n, and compare it to xr. However: + * + * xr == X(pr) mod n + * <=> exists h. (xr + h * n < p && xr + h * n == X(pr)) + * [Since 2 * n > p, h can only be 0 or 1] + * <=> (xr == X(pr)) || (xr + n < p && xr + n == X(pr)) + * [In Jacobian coordinates, X(pr) is pr.x / pr.z^2 mod p] + * <=> (xr == pr.x / pr.z^2 mod p) || (xr + n < p && xr + n == pr.x / pr.z^2 mod p) + * [Multiplying both sides of the equations by pr.z^2 mod p] + * <=> (xr * pr.z^2 mod p == pr.x) || (xr + n < p && (xr + n) * pr.z^2 mod p == pr.x) + * + * Thus, we can avoid the inversion, but we have to check both cases separately. + * secp256k1_gej_eq_x implements the (xr * pr.z^2 mod p == pr.x) test. + */ + if (secp256k1_gej_eq_x_var(&xr, &pr)) { + /* xr * pr.z^2 mod p == pr.x, so the signature is valid. */ + return 1; + } + if (secp256k1_fe_cmp_var(&xr, &secp256k1_ecdsa_const_p_minus_order) >= 0) { + /* xr + n >= p, so we can skip testing the second case. */ + return 0; + } + secp256k1_fe_add(&xr, &secp256k1_ecdsa_const_order_as_fe); + if (secp256k1_gej_eq_x_var(&xr, &pr)) { + /* (xr + n) * pr.z^2 mod p == pr.x, so the signature is valid. */ + return 1; + } + return 0; +#endif +} + +static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid) { + unsigned char b[32]; + secp256k1_gej rp; + secp256k1_ge r; + secp256k1_scalar n; + int overflow = 0; + + secp256k1_ecmult_gen(ctx, &rp, nonce); + secp256k1_ge_set_gej(&r, &rp); + secp256k1_fe_normalize(&r.x); + 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); + } + secp256k1_scalar_mul(&n, sigr, seckey); + secp256k1_scalar_add(&n, &n, message); + secp256k1_scalar_inverse(sigs, nonce); + secp256k1_scalar_mul(sigs, sigs, &n); + 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; + } + } + return 1; +} + +#endif /* SECP256K1_ECDSA_IMPL_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/eckey.h b/src/cryptoconditions/src/include/secp256k1/src/eckey.h new file mode 100644 index 000000000..b621f1e6c --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/eckey.h @@ -0,0 +1,25 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_ECKEY_H +#define SECP256K1_ECKEY_H + +#include + +#include "group.h" +#include "scalar.h" +#include "ecmult.h" +#include "ecmult_gen.h" + +static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char *pub, size_t size); +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_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); + +#endif /* SECP256K1_ECKEY_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/eckey_impl.h b/src/cryptoconditions/src/include/secp256k1/src/eckey_impl.h new file mode 100644 index 000000000..1ab9a68ec --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/eckey_impl.h @@ -0,0 +1,100 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_ECKEY_IMPL_H +#define SECP256K1_ECKEY_IMPL_H + +#include "eckey.h" + +#include "scalar.h" +#include "field.h" +#include "group.h" +#include "ecmult_gen.h" + +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)) { + secp256k1_fe x, y; + if (!secp256k1_fe_set_b32(&x, pub+1) || !secp256k1_fe_set_b32(&y, pub+33)) { + return 0; + } + secp256k1_ge_set_xy(elem, &x, &y); + if ((pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD) && + secp256k1_fe_is_odd(&y) != (pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD)) { + return 0; + } + return secp256k1_ge_is_valid_var(elem); + } else { + return 0; + } +} + +static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *pub, size_t *size, int compressed) { + if (secp256k1_ge_is_infinity(elem)) { + return 0; + } + secp256k1_fe_normalize_var(&elem->x); + secp256k1_fe_normalize_var(&elem->y); + secp256k1_fe_get_b32(&pub[1], &elem->x); + if (compressed) { + *size = 33; + pub[0] = secp256k1_fe_is_odd(&elem->y) ? SECP256K1_TAG_PUBKEY_ODD : SECP256K1_TAG_PUBKEY_EVEN; + } else { + *size = 65; + pub[0] = SECP256K1_TAG_PUBKEY_UNCOMPRESSED; + secp256k1_fe_get_b32(&pub[33], &elem->y); + } + return 1; +} + +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; +} + +static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx, 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); + + if (secp256k1_gej_is_infinity(&pt)) { + return 0; + } + secp256k1_ge_set_gej(key, &pt); + return 1; +} + +static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak) { + if (secp256k1_scalar_is_zero(tweak)) { + return 0; + } + + secp256k1_scalar_mul(key, key, tweak); + return 1; +} + +static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak) { + secp256k1_scalar zero; + 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_ge_set_gej(key, &pt); + return 1; +} + +#endif /* SECP256K1_ECKEY_IMPL_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/ecmult.h b/src/cryptoconditions/src/include/secp256k1/src/ecmult.h new file mode 100644 index 000000000..6d44aba60 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/ecmult.h @@ -0,0 +1,31 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_ECMULT_H +#define SECP256K1_ECMULT_H + +#include "num.h" +#include "group.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 */ +#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); + +/** 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); + +#endif /* SECP256K1_ECMULT_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/ecmult_const.h b/src/cryptoconditions/src/include/secp256k1/src/ecmult_const.h new file mode 100644 index 000000000..72bf7d758 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/ecmult_const.h @@ -0,0 +1,15 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_ECMULT_CONST_H +#define SECP256K1_ECMULT_CONST_H + +#include "scalar.h" +#include "group.h" + +static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q); + +#endif /* SECP256K1_ECMULT_CONST_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/ecmult_const_impl.h b/src/cryptoconditions/src/include/secp256k1/src/ecmult_const_impl.h new file mode 100644 index 000000000..7d7a172b7 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/ecmult_const_impl.h @@ -0,0 +1,240 @@ +/********************************************************************** + * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_ECMULT_CONST_IMPL_H +#define SECP256K1_ECMULT_CONST_IMPL_H + +#include "scalar.h" +#include "group.h" +#include "ecmult_const.h" +#include "ecmult_impl.h" + +#ifdef USE_ENDOMORPHISM + #define WNAF_BITS 128 +#else + #define WNAF_BITS 256 +#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; \ + 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++) { \ + /* 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); \ + } \ + (r)->infinity = 0; \ + secp256k1_fe_negate(&neg_y, &(r)->y, 1); \ + secp256k1_fe_cmov(&(r)->y, &neg_y, (n) != abs_n); \ +} while(0) + + +/** 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 *scalar) { + secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_ge tmpa; + secp256k1_fe Z; + + 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 + + int i; + secp256k1_scalar sc = *scalar; + + /* 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); +#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. + */ + 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_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; + 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 + } + + secp256k1_fe_mul(&r->z, &r->z, &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); + + /* 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 + + /* Apply the correction */ + secp256k1_ge_from_storage(&correction, &correction_1_stor); + secp256k1_ge_neg(&correction, &correction); + secp256k1_gej_add_ge(r, r, &correction); + +#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 + } +} + +#endif /* SECP256K1_ECMULT_CONST_IMPL_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/ecmult_gen.h b/src/cryptoconditions/src/include/secp256k1/src/ecmult_gen.h new file mode 100644 index 000000000..7564b7015 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/ecmult_gen.h @@ -0,0 +1,43 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_ECMULT_GEN_H +#define SECP256K1_ECMULT_GEN_H + +#include "scalar.h" +#include "group.h" + +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; +} 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_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); + +static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const unsigned char *seed32); + +#endif /* SECP256K1_ECMULT_GEN_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/ecmult_gen_impl.h b/src/cryptoconditions/src/include/secp256k1/src/ecmult_gen_impl.h new file mode 100644 index 000000000..714f02e94 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/ecmult_gen_impl.h @@ -0,0 +1,210 @@ +/********************************************************************** + * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_ECMULT_GEN_IMPL_H +#define SECP256K1_ECMULT_GEN_IMPL_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); + } + + /* 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 + secp256k1_ecmult_gen_blind(ctx, NULL); +} + +static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context* ctx) { + return ctx->prec != NULL; +} + +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) { +#ifndef USE_ECMULT_STATIC_PRECOMPUTATION + free(ctx->prec); +#endif + secp256k1_scalar_clear(&ctx->blind); + secp256k1_gej_clear(&ctx->initial); + ctx->prec = NULL; +} + +static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp256k1_gej *r, const secp256k1_scalar *gn) { + secp256k1_ge add; + secp256k1_ge_storage adds; + secp256k1_scalar gnb; + int bits; + int i, j; + 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++) { + /** 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. + * See also: + * "A word of warning", CHES 2013 Rump Session, by Daniel J. Bernstein and Peter Schwabe + * (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) + */ + secp256k1_ge_storage_cmov(&adds, &(*ctx->prec)[j][i], i == bits); + } + secp256k1_ge_from_storage(&add, &adds); + secp256k1_gej_add_ge(r, r, &add); + } + bits = 0; + secp256k1_ge_clear(&add); + secp256k1_scalar_clear(&gnb); +} + +/* 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_gej gb; + secp256k1_fe s; + unsigned char nonce32[32]; + secp256k1_rfc6979_hmac_sha256 rng; + int retry; + unsigned char keydata[64] = {0}; + 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); + } + /* The prior blinding value (if not reset) is chained forward by including it in the hash. */ + secp256k1_scalar_get_b32(nonce32, &ctx->blind); + /** 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); + 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. */ + 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_clear(&b); + secp256k1_gej_clear(&gb); +} + +#endif /* SECP256K1_ECMULT_GEN_IMPL_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/ecmult_impl.h b/src/cryptoconditions/src/include/secp256k1/src/ecmult_impl.h new file mode 100644 index 000000000..93d3794cb --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/ecmult_impl.h @@ -0,0 +1,406 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_ECMULT_IMPL_H +#define SECP256K1_ECMULT_IMPL_H + +#include + +#include "group.h" +#include "scalar.h" +#include "ecmult.h" + +#if defined(EXHAUSTIVE_TEST_ORDER) +/* We need to lower these values for exhaustive tests because + * the tables cannot have infinities in them (this breaks the + * 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 +#endif + +/** The number of entries a table with precomputed multiples needs to have. */ +#define ECMULT_TABLE_SIZE(w) (1 << ((w)-2)) + +/** 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. + */ +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; + int i; + + VERIFY_CHECK(!a->infinity); + + 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. + */ + 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; + + zr[0] = d.z; + for (i = 1; i < n; i++) { + secp256k1_gej_add_ge_var(&prej[i], &prej[i-1], &d_ge, &zr[i]); + } + + /* + * 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. + */ + secp256k1_fe_mul(&prej[n-1].z, &prej[n-1].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); +} + +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]); + } + + 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); + } +#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; + } 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); + } +#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), + * with the following guarantees: + * - each wnaf[i] is either 0, or an odd integer between -(1<<(w-1) - 1) and (1<<(w-1) - 1) + * - two non-zero entries in wnaf are separated by at least w-1 zeroes. + * - the number of set values in wnaf is returned. This number is at most 256, and at most one more + * 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; + int last_set_bit = -1; + int bit = 0; + int sign = 1; + int carry = 0; + + VERIFY_CHECK(wnaf != NULL); + VERIFY_CHECK(0 <= len && len <= 256); + VERIFY_CHECK(a != NULL); + VERIFY_CHECK(2 <= w && w <= 31); + + memset(wnaf, 0, len * sizeof(wnaf[0])); + + if (secp256k1_scalar_get_bits(&s, 255, 1)) { + secp256k1_scalar_negate(&s, &s); + sign = -1; + } + + while (bit < len) { + int now; + int word; + if (secp256k1_scalar_get_bits(&s, bit, 1) == (unsigned int)carry) { + bit++; + continue; + } + + now = w; + if (now > len - bit) { + now = len - bit; + } + + word = secp256k1_scalar_get_bits_var(&s, bit, now) + carry; + + carry = (word >> (w-1)) & 1; + word -= carry << w; + + wnaf[bit] = sign * word; + last_set_bit = bit; + + bit += now; + } +#ifdef VERIFY + CHECK(carry == 0); + while (bit < 256) { + CHECK(secp256k1_scalar_get_bits(&s, bit++, 1) == 0); + } +#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)]; + 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. */ + 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 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 i; + int bits; + +#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); + + /* 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; + } +#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); + +#ifdef USE_ENDOMORPHISM + for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { + secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]); + } + + /* 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; + } +#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); + } + if (i < bits_ng_1 && (n = wnaf_ng_1[i])) { + ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->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_gej_add_zinv_var(r, r, &tmpa, &Z); + } +#endif + } + + if (!r->infinity) { + secp256k1_fe_mul(&r->z, &r->z, &Z); + } +} + +#endif /* SECP256K1_ECMULT_IMPL_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/field.h b/src/cryptoconditions/src/include/secp256k1/src/field.h new file mode 100644 index 000000000..bb6692ad5 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/field.h @@ -0,0 +1,132 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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. + */ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#if defined(USE_FIELD_10X26) +#include "field_10x26.h" +#elif defined(USE_FIELD_5X52) +#include "field_5x52.h" +#else +#error "Please select field implementation" +#endif + +#include "util.h" + +/** Normalize a field element. */ +static void secp256k1_fe_normalize(secp256k1_fe *r); + +/** Weakly normalize a field element: reduce it magnitude to 1, but don't fully normalize. */ +static void secp256k1_fe_normalize_weak(secp256k1_fe *r); + +/** Normalize a field element, without constant-time guarantee. */ +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); + +/** 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); + +/** Set a field element equal to a small integer. Resulting field element is normalized. */ +static void secp256k1_fe_set_int(secp256k1_fe *r, int a); + +/** Sets a field element equal to zero, initializing all fields. */ +static void secp256k1_fe_clear(secp256k1_fe *a); + +/** Verify whether a field element is zero. Requires the input to be normalized. */ +static int secp256k1_fe_is_zero(const secp256k1_fe *a); + +/** Check the "oddness" of a field element. Requires the input to be normalized. */ +static int secp256k1_fe_is_odd(const secp256k1_fe *a); + +/** Compare two field elements. Requires magnitude-1 inputs. */ +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 */ +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); + +/** 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); + +/** 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); + +/** 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); + +/** Adds a field element to another. The result has the sum of the inputs' magnitudes as magnitude. */ +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). */ +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). */ +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); + +/** 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). */ +static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a); + +/** Potentially faster version of secp256k1_fe_inv, without constant-time guarantee. */ +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. */ +static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a); + +/** Convert a field element back from the storage type. */ +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. */ +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. */ +static void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag); + +#endif /* SECP256K1_FIELD_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/field_10x26.h b/src/cryptoconditions/src/include/secp256k1/src/field_10x26.h new file mode 100644 index 000000000..727c5267f --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/field_10x26.h @@ -0,0 +1,48 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_FIELD_REPR_H +#define SECP256K1_FIELD_REPR_H + +#include + +typedef struct { + /* X = sum(i=0..9, elem[i]*2^26) mod n */ + uint32_t n[10]; +#ifdef VERIFY + int magnitude; + int normalized; +#endif +} secp256k1_fe; + +/* Unpacks a constant into a overlapping multi-limbed FE element. */ +#define SECP256K1_FE_CONST_INNER(d7, d6, d5, d4, d3, d2, d1, d0) { \ + (d0) & 0x3FFFFFFUL, \ + (((uint32_t)d0) >> 26) | (((uint32_t)(d1) & 0xFFFFFUL) << 6), \ + (((uint32_t)d1) >> 20) | (((uint32_t)(d2) & 0x3FFFUL) << 12), \ + (((uint32_t)d2) >> 14) | (((uint32_t)(d3) & 0xFFUL) << 18), \ + (((uint32_t)d3) >> 8) | (((uint32_t)(d4) & 0x3UL) << 24), \ + (((uint32_t)d4) >> 2) & 0x3FFFFFFUL, \ + (((uint32_t)d4) >> 28) | (((uint32_t)(d5) & 0x3FFFFFUL) << 4), \ + (((uint32_t)d5) >> 22) | (((uint32_t)(d6) & 0xFFFFUL) << 10), \ + (((uint32_t)d6) >> 16) | (((uint32_t)(d7) & 0x3FFUL) << 16), \ + (((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; + +#define SECP256K1_FE_STORAGE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{ (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }} +#define SECP256K1_FE_STORAGE_CONST_GET(d) d.n[7], d.n[6], d.n[5], d.n[4],d.n[3], d.n[2], d.n[1], d.n[0] + +#endif /* SECP256K1_FIELD_REPR_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/field_10x26_impl.h b/src/cryptoconditions/src/include/secp256k1/src/field_10x26_impl.h new file mode 100644 index 000000000..94f8132fc --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/field_10x26_impl.h @@ -0,0 +1,1161 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_FIELD_REPR_IMPL_H +#define SECP256K1_FIELD_REPR_IMPL_H + +#include "util.h" +#include "num.h" +#include "field.h" + +#ifdef VERIFY +static void secp256k1_fe_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); + if (a->normalized) { + r &= (a->magnitude <= 1); + if (r && (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(r == 1); +} +#endif + +static void secp256k1_fe_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]; + + /* Reduce t9 at the start so there will be at most a single carry from the first pass */ + uint32_t m; + uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; m = t2; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; m &= t3; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; m &= t4; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; m &= t5; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; m &= t6; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; m &= t7; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; m &= t8; + + /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t9 >> 23 == 0); + + /* At most a single final reduction is needed; check if the value is >= the field characteristic */ + x = (t9 >> 22) | ((t9 == 0x03FFFFFUL) & (m == 0x3FFFFFFUL) + & ((t1 + 0x40UL + ((t0 + 0x3D1UL) >> 26)) > 0x3FFFFFFUL)); + + /* Apply the final reduction (for constant-time behaviour, we do it always) */ + t0 += x * 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; + + /* If t9 didn't carry to bit 22 already, then it should have after any final reduction */ + VERIFY_CHECK(t9 >> 22 == x); + + /* Mask off the possible multiple of 2^256 from the final reduction */ + t9 &= 0x03FFFFFUL; + + 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) { + 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]; + + /* Reduce t9 at the start so there will be at most a single carry from the first pass */ + uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; + + /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t9 >> 23 == 0); + + 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) { + 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]; + + /* Reduce t9 at the start so there will be at most a single carry from the first pass */ + uint32_t m; + uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; m = t2; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; m &= t3; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; m &= t4; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; m &= t5; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; m &= t6; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; m &= t7; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; m &= t8; + + /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t9 >> 23 == 0); + + /* At most a single final reduction is needed; check if the value is >= the field characteristic */ + x = (t9 >> 22) | ((t9 == 0x03FFFFFUL) & (m == 0x3FFFFFFUL) + & ((t1 + 0x40UL + ((t0 + 0x3D1UL) >> 26)) > 0x3FFFFFFUL)); + + if (x) { + t0 += 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; + + /* If t9 didn't carry to bit 22 already, then it should have after any final reduction */ + VERIFY_CHECK(t9 >> 22 == x); + + /* Mask off the possible multiple of 2^256 from the final reduction */ + t9 &= 0x03FFFFFUL; + } + + 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) { + 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]; + + /* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */ + uint32_t z0, z1; + + /* Reduce t9 at the start so there will be at most a single carry from the first pass */ + uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; z0 = t0; z1 = t0 ^ 0x3D0UL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; z0 |= t1; z1 &= t1 ^ 0x40UL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; z0 |= t2; z1 &= t2; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; z0 |= t3; z1 &= t3; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; z0 |= t4; z1 &= t4; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; z0 |= t5; z1 &= t5; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; z0 |= t6; z1 &= t6; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; z0 |= t7; z1 &= t7; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; z0 |= t8; z1 &= t8; + z0 |= t9; z1 &= t9 ^ 0x3C00000UL; + + /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t9 >> 23 == 0); + + return (z0 == 0) | (z1 == 0x3FFFFFFUL); +} + +static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r) { + uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint32_t z0, z1; + uint32_t x; + + t0 = r->n[0]; + t9 = r->n[9]; + + /* Reduce t9 at the start so there will be at most a single carry from the first pass */ + x = t9 >> 22; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x3D1UL; + + /* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */ + z0 = t0 & 0x3FFFFFFUL; + z1 = z0 ^ 0x3D0UL; + + /* Fast return path should catch the majority of cases */ + if ((z0 != 0UL) & (z1 != 0x3FFFFFFUL)) { + return 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 &= 0x03FFFFFUL; + t1 += (x << 6); + + t1 += (t0 >> 26); + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; z0 |= t1; z1 &= t1 ^ 0x40UL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; z0 |= t2; z1 &= t2; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; z0 |= t3; z1 &= t3; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; z0 |= t4; z1 &= t4; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; z0 |= t5; z1 &= t5; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; z0 |= t6; z1 &= t6; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; z0 |= t7; z1 &= t7; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; z0 |= t8; z1 &= t8; + z0 |= t9; z1 &= t9 ^ 0x3C00000UL; + + /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t9 >> 23 == 0); + + return (z0 == 0) | (z1 == 0x3FFFFFFUL); +} + +SECP256K1_INLINE static void secp256k1_fe_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) { + 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 + return a->n[0] & 1; +} + +SECP256K1_INLINE static void secp256k1_fe_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) { + 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; + } + if (a->n[i] < b->n[i]) { + return -1; + } + } + return 0; +} + +static int secp256k1_fe_set_b32(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); + r->n[3] = (uint32_t)((a[22] >> 6) & 0x3) | ((uint32_t)a[21] << 2) | ((uint32_t)a[20] << 10) | ((uint32_t)a[19] << 18); + r->n[4] = (uint32_t)a[18] | ((uint32_t)a[17] << 8) | ((uint32_t)a[16] << 16) | ((uint32_t)(a[15] & 0x3) << 24); + r->n[5] = (uint32_t)((a[15] >> 2) & 0x3f) | ((uint32_t)a[14] << 6) | ((uint32_t)a[13] << 14) | ((uint32_t)(a[12] & 0xf) << 22); + r->n[6] = (uint32_t)((a[12] >> 4) & 0xf) | ((uint32_t)a[11] << 4) | ((uint32_t)a[10] << 12) | ((uint32_t)(a[9] & 0x3f) << 20); + 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; +} + +/** 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 + 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); + r[3] = (a->n[8] >> 16) & 0xff; + r[4] = (a->n[8] >> 8) & 0xff; + r[5] = a->n[8] & 0xff; + r[6] = (a->n[7] >> 18) & 0xff; + r[7] = (a->n[7] >> 10) & 0xff; + r[8] = (a->n[7] >> 2) & 0xff; + r[9] = ((a->n[7] & 0x3) << 6) | ((a->n[6] >> 20) & 0x3f); + r[10] = (a->n[6] >> 12) & 0xff; + r[11] = (a->n[6] >> 4) & 0xff; + r[12] = ((a->n[6] & 0xf) << 4) | ((a->n[5] >> 22) & 0xf); + r[13] = (a->n[5] >> 14) & 0xff; + r[14] = (a->n[5] >> 6) & 0xff; + r[15] = ((a->n[5] & 0x3f) << 2) | ((a->n[4] >> 24) & 0x3); + r[16] = (a->n[4] >> 16) & 0xff; + r[17] = (a->n[4] >> 8) & 0xff; + r[18] = a->n[4] & 0xff; + r[19] = (a->n[3] >> 18) & 0xff; + r[20] = (a->n[3] >> 10) & 0xff; + r[21] = (a->n[3] >> 2) & 0xff; + r[22] = ((a->n[3] & 0x3) << 6) | ((a->n[2] >> 20) & 0x3f); + r[23] = (a->n[2] >> 12) & 0xff; + r[24] = (a->n[2] >> 4) & 0xff; + r[25] = ((a->n[2] & 0xf) << 4) | ((a->n[1] >> 22) & 0xf); + r[26] = (a->n[1] >> 14) & 0xff; + r[27] = (a->n[1] >> 6) & 0xff; + r[28] = ((a->n[1] & 0x3f) << 2) | ((a->n[0] >> 24) & 0x3); + r[29] = (a->n[0] >> 16) & 0xff; + r[30] = (a->n[0] >> 8) & 0xff; + 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 + 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]; + r->n[3] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[3]; + r->n[4] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[4]; + r->n[5] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[5]; + r->n[6] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[6]; + 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) { + r->n[0] *= a; + r->n[1] *= a; + r->n[2] *= a; + r->n[3] *= a; + r->n[4] *= a; + r->n[5] *= a; + r->n[6] *= 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 + 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]; + r->n[5] += a->n[5]; + r->n[6] += a->n[6]; + 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 +} + +#if defined(USE_EXTERNAL_ASM) + +/* External assembler implementation */ +void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t *a, const uint32_t * SECP256K1_RESTRICT b); +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; + uint64_t u0, u1, u2, u3, u4, u5, u6, u7, u8; + uint32_t t9, t1, t0, t2, t3, t4, t5, t6, t7; + const uint32_t M = 0x3FFFFFFUL, R0 = 0x3D10UL, R1 = 0x400UL; + + VERIFY_BITS(a[0], 30); + VERIFY_BITS(a[1], 30); + VERIFY_BITS(a[2], 30); + VERIFY_BITS(a[3], 30); + VERIFY_BITS(a[4], 30); + VERIFY_BITS(a[5], 30); + VERIFY_BITS(a[6], 30); + VERIFY_BITS(a[7], 30); + VERIFY_BITS(a[8], 30); + VERIFY_BITS(a[9], 26); + VERIFY_BITS(b[0], 30); + VERIFY_BITS(b[1], 30); + VERIFY_BITS(b[2], 30); + VERIFY_BITS(b[3], 30); + VERIFY_BITS(b[4], 30); + VERIFY_BITS(b[5], 30); + VERIFY_BITS(b[6], 30); + VERIFY_BITS(b[7], 30); + VERIFY_BITS(b[8], 30); + 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). + * Note that [x 0 0 0 0 0 0 0 0 0 0] = [x*R1 x*R0]. + */ + + d = (uint64_t)a[0] * b[9] + + (uint64_t)a[1] * b[8] + + (uint64_t)a[2] * b[7] + + (uint64_t)a[3] * b[6] + + (uint64_t)a[4] * b[5] + + (uint64_t)a[5] * b[4] + + (uint64_t)a[6] * b[3] + + (uint64_t)a[7] * b[2] + + (uint64_t)a[8] * b[1] + + (uint64_t)a[9] * b[0]; + /* VERIFY_BITS(d, 64); */ + /* [d 0 0 0 0 0 0 0 0 0] = [p9 0 0 0 0 0 0 0 0 0] */ + t9 = d & M; d >>= 26; + VERIFY_BITS(t9, 26); + VERIFY_BITS(d, 38); + /* [d t9 0 0 0 0 0 0 0 0 0] = [p9 0 0 0 0 0 0 0 0 0] */ + + c = (uint64_t)a[0] * b[0]; + VERIFY_BITS(c, 60); + /* [d t9 0 0 0 0 0 0 0 0 c] = [p9 0 0 0 0 0 0 0 0 p0] */ + d += (uint64_t)a[1] * b[9] + + (uint64_t)a[2] * b[8] + + (uint64_t)a[3] * b[7] + + (uint64_t)a[4] * b[6] + + (uint64_t)a[5] * b[5] + + (uint64_t)a[6] * b[4] + + (uint64_t)a[7] * b[3] + + (uint64_t)a[8] * b[2] + + (uint64_t)a[9] * b[1]; + VERIFY_BITS(d, 63); + /* [d t9 0 0 0 0 0 0 0 0 c] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + u0 = d & M; d >>= 26; c += u0 * R0; + VERIFY_BITS(u0, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 61); + /* [d u0 t9 0 0 0 0 0 0 0 0 c-u0*R0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + t0 = c & M; c >>= 26; c += u0 * R1; + VERIFY_BITS(t0, 26); + VERIFY_BITS(c, 37); + /* [d u0 t9 0 0 0 0 0 0 0 c-u0*R1 t0-u0*R0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + + c += (uint64_t)a[0] * b[1] + + (uint64_t)a[1] * b[0]; + VERIFY_BITS(c, 62); + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p10 p9 0 0 0 0 0 0 0 p1 p0] */ + d += (uint64_t)a[2] * b[9] + + (uint64_t)a[3] * b[8] + + (uint64_t)a[4] * b[7] + + (uint64_t)a[5] * b[6] + + (uint64_t)a[6] * b[5] + + (uint64_t)a[7] * b[4] + + (uint64_t)a[8] * b[3] + + (uint64_t)a[9] * b[2]; + VERIFY_BITS(d, 63); + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + u1 = d & M; d >>= 26; c += u1 * R0; + VERIFY_BITS(u1, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 63); + /* [d u1 0 t9 0 0 0 0 0 0 0 c-u1*R0 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + t1 = c & M; c >>= 26; c += u1 * R1; + VERIFY_BITS(t1, 26); + VERIFY_BITS(c, 38); + /* [d u1 0 t9 0 0 0 0 0 0 c-u1*R1 t1-u1*R0 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + + c += (uint64_t)a[0] * b[2] + + (uint64_t)a[1] * b[1] + + (uint64_t)a[2] * b[0]; + VERIFY_BITS(c, 62); + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + d += (uint64_t)a[3] * b[9] + + (uint64_t)a[4] * b[8] + + (uint64_t)a[5] * b[7] + + (uint64_t)a[6] * b[6] + + (uint64_t)a[7] * b[5] + + (uint64_t)a[8] * b[4] + + (uint64_t)a[9] * b[3]; + VERIFY_BITS(d, 63); + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + u2 = d & M; d >>= 26; c += u2 * R0; + VERIFY_BITS(u2, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 63); + /* [d u2 0 0 t9 0 0 0 0 0 0 c-u2*R0 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + t2 = c & M; c >>= 26; c += u2 * R1; + VERIFY_BITS(t2, 26); + VERIFY_BITS(c, 38); + /* [d u2 0 0 t9 0 0 0 0 0 c-u2*R1 t2-u2*R0 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[3] + + (uint64_t)a[1] * b[2] + + (uint64_t)a[2] * b[1] + + (uint64_t)a[3] * b[0]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + d += (uint64_t)a[4] * b[9] + + (uint64_t)a[5] * b[8] + + (uint64_t)a[6] * b[7] + + (uint64_t)a[7] * b[6] + + (uint64_t)a[8] * b[5] + + (uint64_t)a[9] * b[4]; + VERIFY_BITS(d, 63); + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + u3 = d & M; d >>= 26; c += u3 * R0; + VERIFY_BITS(u3, 26); + VERIFY_BITS(d, 37); + /* VERIFY_BITS(c, 64); */ + /* [d u3 0 0 0 t9 0 0 0 0 0 c-u3*R0 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + t3 = c & M; c >>= 26; c += u3 * R1; + VERIFY_BITS(t3, 26); + VERIFY_BITS(c, 39); + /* [d u3 0 0 0 t9 0 0 0 0 c-u3*R1 t3-u3*R0 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[4] + + (uint64_t)a[1] * b[3] + + (uint64_t)a[2] * b[2] + + (uint64_t)a[3] * b[1] + + (uint64_t)a[4] * b[0]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[5] * b[9] + + (uint64_t)a[6] * b[8] + + (uint64_t)a[7] * b[7] + + (uint64_t)a[8] * b[6] + + (uint64_t)a[9] * b[5]; + VERIFY_BITS(d, 62); + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + u4 = d & M; d >>= 26; c += u4 * R0; + VERIFY_BITS(u4, 26); + VERIFY_BITS(d, 36); + /* VERIFY_BITS(c, 64); */ + /* [d u4 0 0 0 0 t9 0 0 0 0 c-u4*R0 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + t4 = c & M; c >>= 26; c += u4 * R1; + VERIFY_BITS(t4, 26); + VERIFY_BITS(c, 39); + /* [d u4 0 0 0 0 t9 0 0 0 c-u4*R1 t4-u4*R0 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[5] + + (uint64_t)a[1] * b[4] + + (uint64_t)a[2] * b[3] + + (uint64_t)a[3] * b[2] + + (uint64_t)a[4] * b[1] + + (uint64_t)a[5] * b[0]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[6] * b[9] + + (uint64_t)a[7] * b[8] + + (uint64_t)a[8] * b[7] + + (uint64_t)a[9] * b[6]; + VERIFY_BITS(d, 62); + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + u5 = d & M; d >>= 26; c += u5 * R0; + VERIFY_BITS(u5, 26); + VERIFY_BITS(d, 36); + /* VERIFY_BITS(c, 64); */ + /* [d u5 0 0 0 0 0 t9 0 0 0 c-u5*R0 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + t5 = c & M; c >>= 26; c += u5 * R1; + VERIFY_BITS(t5, 26); + VERIFY_BITS(c, 39); + /* [d u5 0 0 0 0 0 t9 0 0 c-u5*R1 t5-u5*R0 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[6] + + (uint64_t)a[1] * b[5] + + (uint64_t)a[2] * b[4] + + (uint64_t)a[3] * b[3] + + (uint64_t)a[4] * b[2] + + (uint64_t)a[5] * b[1] + + (uint64_t)a[6] * b[0]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[7] * b[9] + + (uint64_t)a[8] * b[8] + + (uint64_t)a[9] * b[7]; + VERIFY_BITS(d, 61); + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + u6 = d & M; d >>= 26; c += u6 * R0; + VERIFY_BITS(u6, 26); + VERIFY_BITS(d, 35); + /* VERIFY_BITS(c, 64); */ + /* [d u6 0 0 0 0 0 0 t9 0 0 c-u6*R0 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + t6 = c & M; c >>= 26; c += u6 * R1; + VERIFY_BITS(t6, 26); + VERIFY_BITS(c, 39); + /* [d u6 0 0 0 0 0 0 t9 0 c-u6*R1 t6-u6*R0 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[7] + + (uint64_t)a[1] * b[6] + + (uint64_t)a[2] * b[5] + + (uint64_t)a[3] * b[4] + + (uint64_t)a[4] * b[3] + + (uint64_t)a[5] * b[2] + + (uint64_t)a[6] * b[1] + + (uint64_t)a[7] * b[0]; + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x8000007C00000007ULL); + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[8] * b[9] + + (uint64_t)a[9] * b[8]; + VERIFY_BITS(d, 58); + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + u7 = d & M; d >>= 26; c += u7 * R0; + VERIFY_BITS(u7, 26); + VERIFY_BITS(d, 32); + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x800001703FFFC2F7ULL); + /* [d u7 0 0 0 0 0 0 0 t9 0 c-u7*R0 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + t7 = c & M; c >>= 26; c += u7 * R1; + VERIFY_BITS(t7, 26); + VERIFY_BITS(c, 38); + /* [d u7 0 0 0 0 0 0 0 t9 c-u7*R1 t7-u7*R0 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[8] + + (uint64_t)a[1] * b[7] + + (uint64_t)a[2] * b[6] + + (uint64_t)a[3] * b[5] + + (uint64_t)a[4] * b[4] + + (uint64_t)a[5] * b[3] + + (uint64_t)a[6] * b[2] + + (uint64_t)a[7] * b[1] + + (uint64_t)a[8] * b[0]; + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x9000007B80000008ULL); + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[9] * b[9]; + VERIFY_BITS(d, 57); + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + u8 = d & M; d >>= 26; c += u8 * R0; + VERIFY_BITS(u8, 26); + VERIFY_BITS(d, 31); + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x9000016FBFFFC2F8ULL); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 t4 t3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + r[3] = t3; + VERIFY_BITS(r[3], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 t4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[4] = t4; + VERIFY_BITS(r[4], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[5] = t5; + VERIFY_BITS(r[5], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[6] = t6; + VERIFY_BITS(r[6], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[7] = t7; + VERIFY_BITS(r[7], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + r[8] = c & M; c >>= 26; c += u8 * R1; + VERIFY_BITS(r[8], 26); + VERIFY_BITS(c, 39); + /* [d u8 0 0 0 0 0 0 0 0 t9+c-u8*R1 r8-u8*R0 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 0 0 t9+c r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += d * R0 + t9; + VERIFY_BITS(c, 45); + /* [d 0 0 0 0 0 0 0 0 0 c-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[9] = c & (M >> 4); c >>= 22; c += d * (R1 << 4); + VERIFY_BITS(r[9], 22); + VERIFY_BITS(c, 46); + /* [d 0 0 0 0 0 0 0 0 r9+((c-d*R1<<4)<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 -d*R1 r9+(c<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + d = c * (R0 >> 4) + t0; + VERIFY_BITS(d, 56); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 d-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[0] = d & M; d >>= 26; + VERIFY_BITS(r[0], 26); + VERIFY_BITS(d, 30); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1+d r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += c * (R1 >> 4) + t1; + VERIFY_BITS(d, 53); + VERIFY_CHECK(d <= 0x10000003FFFFBFULL); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 d-c*R1>>4 r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [r9 r8 r7 r6 r5 r4 r3 t2 d r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[1] = d & M; d >>= 26; + VERIFY_BITS(r[1], 26); + VERIFY_BITS(d, 27); + VERIFY_CHECK(d <= 0x4000000ULL); + /* [r9 r8 r7 r6 r5 r4 r3 t2+d r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += t2; + VERIFY_BITS(d, 27); + /* [r9 r8 r7 r6 r5 r4 r3 d r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[2] = d; + VERIFY_BITS(r[2], 27); + /* [r9 r8 r7 r6 r5 r4 r3 r2 r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ +} + +SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t *a) { + uint64_t c, d; + uint64_t u0, u1, u2, u3, u4, u5, u6, u7, u8; + uint32_t t9, t0, t1, t2, t3, t4, t5, t6, t7; + const uint32_t M = 0x3FFFFFFUL, R0 = 0x3D10UL, R1 = 0x400UL; + + VERIFY_BITS(a[0], 30); + VERIFY_BITS(a[1], 30); + VERIFY_BITS(a[2], 30); + VERIFY_BITS(a[3], 30); + VERIFY_BITS(a[4], 30); + VERIFY_BITS(a[5], 30); + VERIFY_BITS(a[6], 30); + VERIFY_BITS(a[7], 30); + VERIFY_BITS(a[8], 30); + VERIFY_BITS(a[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]*a[x-i], i=0..x). + * Note that [x 0 0 0 0 0 0 0 0 0 0] = [x*R1 x*R0]. + */ + + d = (uint64_t)(a[0]*2) * a[9] + + (uint64_t)(a[1]*2) * a[8] + + (uint64_t)(a[2]*2) * a[7] + + (uint64_t)(a[3]*2) * a[6] + + (uint64_t)(a[4]*2) * a[5]; + /* VERIFY_BITS(d, 64); */ + /* [d 0 0 0 0 0 0 0 0 0] = [p9 0 0 0 0 0 0 0 0 0] */ + t9 = d & M; d >>= 26; + VERIFY_BITS(t9, 26); + VERIFY_BITS(d, 38); + /* [d t9 0 0 0 0 0 0 0 0 0] = [p9 0 0 0 0 0 0 0 0 0] */ + + c = (uint64_t)a[0] * a[0]; + VERIFY_BITS(c, 60); + /* [d t9 0 0 0 0 0 0 0 0 c] = [p9 0 0 0 0 0 0 0 0 p0] */ + d += (uint64_t)(a[1]*2) * a[9] + + (uint64_t)(a[2]*2) * a[8] + + (uint64_t)(a[3]*2) * a[7] + + (uint64_t)(a[4]*2) * a[6] + + (uint64_t)a[5] * a[5]; + VERIFY_BITS(d, 63); + /* [d t9 0 0 0 0 0 0 0 0 c] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + u0 = d & M; d >>= 26; c += u0 * R0; + VERIFY_BITS(u0, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 61); + /* [d u0 t9 0 0 0 0 0 0 0 0 c-u0*R0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + t0 = c & M; c >>= 26; c += u0 * R1; + VERIFY_BITS(t0, 26); + VERIFY_BITS(c, 37); + /* [d u0 t9 0 0 0 0 0 0 0 c-u0*R1 t0-u0*R0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + + c += (uint64_t)(a[0]*2) * a[1]; + VERIFY_BITS(c, 62); + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p10 p9 0 0 0 0 0 0 0 p1 p0] */ + d += (uint64_t)(a[2]*2) * a[9] + + (uint64_t)(a[3]*2) * a[8] + + (uint64_t)(a[4]*2) * a[7] + + (uint64_t)(a[5]*2) * a[6]; + VERIFY_BITS(d, 63); + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + u1 = d & M; d >>= 26; c += u1 * R0; + VERIFY_BITS(u1, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 63); + /* [d u1 0 t9 0 0 0 0 0 0 0 c-u1*R0 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + t1 = c & M; c >>= 26; c += u1 * R1; + VERIFY_BITS(t1, 26); + VERIFY_BITS(c, 38); + /* [d u1 0 t9 0 0 0 0 0 0 c-u1*R1 t1-u1*R0 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[2] + + (uint64_t)a[1] * a[1]; + VERIFY_BITS(c, 62); + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + d += (uint64_t)(a[3]*2) * a[9] + + (uint64_t)(a[4]*2) * a[8] + + (uint64_t)(a[5]*2) * a[7] + + (uint64_t)a[6] * a[6]; + VERIFY_BITS(d, 63); + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + u2 = d & M; d >>= 26; c += u2 * R0; + VERIFY_BITS(u2, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 63); + /* [d u2 0 0 t9 0 0 0 0 0 0 c-u2*R0 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + t2 = c & M; c >>= 26; c += u2 * R1; + VERIFY_BITS(t2, 26); + VERIFY_BITS(c, 38); + /* [d u2 0 0 t9 0 0 0 0 0 c-u2*R1 t2-u2*R0 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[3] + + (uint64_t)(a[1]*2) * a[2]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + d += (uint64_t)(a[4]*2) * a[9] + + (uint64_t)(a[5]*2) * a[8] + + (uint64_t)(a[6]*2) * a[7]; + VERIFY_BITS(d, 63); + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + u3 = d & M; d >>= 26; c += u3 * R0; + VERIFY_BITS(u3, 26); + VERIFY_BITS(d, 37); + /* VERIFY_BITS(c, 64); */ + /* [d u3 0 0 0 t9 0 0 0 0 0 c-u3*R0 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + t3 = c & M; c >>= 26; c += u3 * R1; + VERIFY_BITS(t3, 26); + VERIFY_BITS(c, 39); + /* [d u3 0 0 0 t9 0 0 0 0 c-u3*R1 t3-u3*R0 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[4] + + (uint64_t)(a[1]*2) * a[3] + + (uint64_t)a[2] * a[2]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + d += (uint64_t)(a[5]*2) * a[9] + + (uint64_t)(a[6]*2) * a[8] + + (uint64_t)a[7] * a[7]; + VERIFY_BITS(d, 62); + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + u4 = d & M; d >>= 26; c += u4 * R0; + VERIFY_BITS(u4, 26); + VERIFY_BITS(d, 36); + /* VERIFY_BITS(c, 64); */ + /* [d u4 0 0 0 0 t9 0 0 0 0 c-u4*R0 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + t4 = c & M; c >>= 26; c += u4 * R1; + VERIFY_BITS(t4, 26); + VERIFY_BITS(c, 39); + /* [d u4 0 0 0 0 t9 0 0 0 c-u4*R1 t4-u4*R0 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[5] + + (uint64_t)(a[1]*2) * a[4] + + (uint64_t)(a[2]*2) * a[3]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)(a[6]*2) * a[9] + + (uint64_t)(a[7]*2) * a[8]; + VERIFY_BITS(d, 62); + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + u5 = d & M; d >>= 26; c += u5 * R0; + VERIFY_BITS(u5, 26); + VERIFY_BITS(d, 36); + /* VERIFY_BITS(c, 64); */ + /* [d u5 0 0 0 0 0 t9 0 0 0 c-u5*R0 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + t5 = c & M; c >>= 26; c += u5 * R1; + VERIFY_BITS(t5, 26); + VERIFY_BITS(c, 39); + /* [d u5 0 0 0 0 0 t9 0 0 c-u5*R1 t5-u5*R0 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[6] + + (uint64_t)(a[1]*2) * a[5] + + (uint64_t)(a[2]*2) * a[4] + + (uint64_t)a[3] * a[3]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)(a[7]*2) * a[9] + + (uint64_t)a[8] * a[8]; + VERIFY_BITS(d, 61); + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + u6 = d & M; d >>= 26; c += u6 * R0; + VERIFY_BITS(u6, 26); + VERIFY_BITS(d, 35); + /* VERIFY_BITS(c, 64); */ + /* [d u6 0 0 0 0 0 0 t9 0 0 c-u6*R0 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + t6 = c & M; c >>= 26; c += u6 * R1; + VERIFY_BITS(t6, 26); + VERIFY_BITS(c, 39); + /* [d u6 0 0 0 0 0 0 t9 0 c-u6*R1 t6-u6*R0 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[7] + + (uint64_t)(a[1]*2) * a[6] + + (uint64_t)(a[2]*2) * a[5] + + (uint64_t)(a[3]*2) * a[4]; + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x8000007C00000007ULL); + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)(a[8]*2) * a[9]; + VERIFY_BITS(d, 58); + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + u7 = d & M; d >>= 26; c += u7 * R0; + VERIFY_BITS(u7, 26); + VERIFY_BITS(d, 32); + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x800001703FFFC2F7ULL); + /* [d u7 0 0 0 0 0 0 0 t9 0 c-u7*R0 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + t7 = c & M; c >>= 26; c += u7 * R1; + VERIFY_BITS(t7, 26); + VERIFY_BITS(c, 38); + /* [d u7 0 0 0 0 0 0 0 t9 c-u7*R1 t7-u7*R0 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[8] + + (uint64_t)(a[1]*2) * a[7] + + (uint64_t)(a[2]*2) * a[6] + + (uint64_t)(a[3]*2) * a[5] + + (uint64_t)a[4] * a[4]; + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x9000007B80000008ULL); + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[9] * a[9]; + VERIFY_BITS(d, 57); + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + u8 = d & M; d >>= 26; c += u8 * R0; + VERIFY_BITS(u8, 26); + VERIFY_BITS(d, 31); + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x9000016FBFFFC2F8ULL); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 t4 t3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + r[3] = t3; + VERIFY_BITS(r[3], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 t4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[4] = t4; + VERIFY_BITS(r[4], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[5] = t5; + VERIFY_BITS(r[5], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[6] = t6; + VERIFY_BITS(r[6], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[7] = t7; + VERIFY_BITS(r[7], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + r[8] = c & M; c >>= 26; c += u8 * R1; + VERIFY_BITS(r[8], 26); + VERIFY_BITS(c, 39); + /* [d u8 0 0 0 0 0 0 0 0 t9+c-u8*R1 r8-u8*R0 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 0 0 t9+c r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += d * R0 + t9; + VERIFY_BITS(c, 45); + /* [d 0 0 0 0 0 0 0 0 0 c-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[9] = c & (M >> 4); c >>= 22; c += d * (R1 << 4); + VERIFY_BITS(r[9], 22); + VERIFY_BITS(c, 46); + /* [d 0 0 0 0 0 0 0 0 r9+((c-d*R1<<4)<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 -d*R1 r9+(c<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + d = c * (R0 >> 4) + t0; + VERIFY_BITS(d, 56); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 d-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[0] = d & M; d >>= 26; + VERIFY_BITS(r[0], 26); + VERIFY_BITS(d, 30); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1+d r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += c * (R1 >> 4) + t1; + VERIFY_BITS(d, 53); + VERIFY_CHECK(d <= 0x10000003FFFFBFULL); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 d-c*R1>>4 r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [r9 r8 r7 r6 r5 r4 r3 t2 d r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[1] = d & M; d >>= 26; + VERIFY_BITS(r[1], 26); + VERIFY_BITS(d, 27); + VERIFY_CHECK(d <= 0x4000000ULL); + /* [r9 r8 r7 r6 r5 r4 r3 t2+d r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += t2; + VERIFY_BITS(d, 27); + /* [r9 r8 r7 r6 r5 r4 r3 d r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[2] = d; + VERIFY_BITS(r[2], 27); + /* [r9 r8 r7 r6 r5 r4 r3 r2 r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ +} +#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_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_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) { + uint32_t mask0, mask1; + mask0 = flag + ~((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); + 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); + r->n[5] = (r->n[5] & mask0) | (a->n[5] & mask1); + r->n[6] = (r->n[6] & mask0) | (a->n[6] & mask1); + 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_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) { + uint32_t mask0, mask1; + mask0 = flag + ~((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); + 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); + r->n[5] = (r->n[5] & mask0) | (a->n[5] & mask1); + r->n[6] = (r->n[6] & mask0) | (a->n[6] & mask1); + 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 + 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; + r->n[3] = a->n[3] >> 18 | a->n[4] << 8; + r->n[4] = a->n[4] >> 24 | a->n[5] << 2 | a->n[6] << 28; + r->n[5] = a->n[6] >> 4 | a->n[7] << 22; + r->n[6] = a->n[7] >> 10 | a->n[8] << 16; + 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) { + 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); + r->n[3] = a->n[2] >> 14 | ((a->n[3] << 18) & 0x3FFFFFFUL); + r->n[4] = a->n[3] >> 8 | ((a->n[4] << 24) & 0x3FFFFFFUL); + r->n[5] = (a->n[4] >> 2) & 0x3FFFFFFUL; + r->n[6] = a->n[4] >> 28 | ((a->n[5] << 4) & 0x3FFFFFFUL); + 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 +} + +#endif /* SECP256K1_FIELD_REPR_IMPL_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/field_5x52.h b/src/cryptoconditions/src/include/secp256k1/src/field_5x52.h new file mode 100644 index 000000000..bccd8feb4 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/field_5x52.h @@ -0,0 +1,47 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_FIELD_REPR_H +#define SECP256K1_FIELD_REPR_H + +#include + +typedef struct { + /* X = sum(i=0..4, elem[i]*2^52) mod n */ + uint64_t n[5]; +#ifdef VERIFY + int magnitude; + int normalized; +#endif +} secp256k1_fe; + +/* Unpacks a constant into a overlapping multi-limbed FE element. */ +#define SECP256K1_FE_CONST_INNER(d7, d6, d5, d4, d3, d2, d1, d0) { \ + (d0) | (((uint64_t)(d1) & 0xFFFFFUL) << 32), \ + ((uint64_t)(d1) >> 20) | (((uint64_t)(d2)) << 12) | (((uint64_t)(d3) & 0xFFUL) << 44), \ + ((uint64_t)(d3) >> 8) | (((uint64_t)(d4) & 0xFFFFFFFUL) << 24), \ + ((uint64_t)(d4) >> 28) | (((uint64_t)(d5)) << 4) | (((uint64_t)(d6) & 0xFFFFUL) << 36), \ + ((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; + +#define SECP256K1_FE_STORAGE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{ \ + (d0) | (((uint64_t)(d1)) << 32), \ + (d2) | (((uint64_t)(d3)) << 32), \ + (d4) | (((uint64_t)(d5)) << 32), \ + (d6) | (((uint64_t)(d7)) << 32) \ +}} + +#endif /* SECP256K1_FIELD_REPR_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/field_5x52_asm_impl.h b/src/cryptoconditions/src/include/secp256k1/src/field_5x52_asm_impl.h new file mode 100644 index 000000000..1fc3171f6 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/field_5x52_asm_impl.h @@ -0,0 +1,502 @@ +/********************************************************************** + * Copyright (c) 2013-2014 Diederik Huys, Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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/cryptoconditions/src/include/secp256k1/src/field_5x52_impl.h b/src/cryptoconditions/src/include/secp256k1/src/field_5x52_impl.h new file mode 100644 index 000000000..957c61b01 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/field_5x52_impl.h @@ -0,0 +1,496 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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 "util.h" +#include "num.h" +#include "field.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) { + const uint64_t *d = a->n; + int m = a->normalized ? 1 : 2 * a->magnitude, r = 1; + /* 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); + if (a->normalized) { + r &= (a->magnitude <= 1); + if (r && (d[4] == 0x0FFFFFFFFFFFFULL) && ((d[3] & d[2] & d[1]) == 0xFFFFFFFFFFFFFULL)) { + r &= (d[0] < 0xFFFFEFFFFFC2FULL); + } + } + VERIFY_CHECK(r == 1); +} +#endif + +static void secp256k1_fe_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 */ + uint64_t m; + uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; m = t1; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; m &= t2; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; m &= t3; + + /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t4 >> 49 == 0); + + /* At most a single final reduction is needed; check if the value is >= the field characteristic */ + x = (t4 >> 48) | ((t4 == 0x0FFFFFFFFFFFFULL) & (m == 0xFFFFFFFFFFFFFULL) + & (t0 >= 0xFFFFEFFFFFC2FULL)); + + /* Apply the final reduction (for constant-time behaviour, we do it always) */ + t0 += x * 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; + + /* If t4 didn't carry to bit 48 already, then it should have after any final reduction */ + VERIFY_CHECK(t4 >> 48 == x); + + /* Mask off the possible multiple of 2^256 from the final reduction */ + 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) { + 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 */ + uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; + + /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ + 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) { + 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 */ + uint64_t m; + uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; m = t1; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; m &= t2; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; m &= t3; + + /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t4 >> 49 == 0); + + /* At most a single final reduction is needed; check if the value is >= the field characteristic */ + x = (t4 >> 48) | ((t4 == 0x0FFFFFFFFFFFFULL) & (m == 0xFFFFFFFFFFFFFULL) + & (t0 >= 0xFFFFEFFFFFC2FULL)); + + if (x) { + t0 += 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; + + /* If t4 didn't carry to bit 48 already, then it should have after any final reduction */ + VERIFY_CHECK(t4 >> 48 == x); + + /* Mask off the possible multiple of 2^256 from the final reduction */ + 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 int secp256k1_fe_normalizes_to_zero(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 */ + uint64_t z0, z1; + + /* Reduce t4 at the start so there will be at most a single carry from the first pass */ + uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; z0 = t0; z1 = t0 ^ 0x1000003D0ULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; z0 |= t1; z1 &= t1; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; z0 |= t2; z1 &= t2; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; z0 |= t3; z1 &= t3; + z0 |= t4; z1 &= t4 ^ 0xF000000000000ULL; + + /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t4 >> 49 == 0); + + return (z0 == 0) | (z1 == 0xFFFFFFFFFFFFFULL); +} + +static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r) { + uint64_t t0, t1, t2, t3, t4; + uint64_t z0, z1; + uint64_t x; + + t0 = r->n[0]; + t4 = r->n[4]; + + /* Reduce t4 at the start so there will be at most a single carry from the first pass */ + x = t4 >> 48; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x1000003D1ULL; + + /* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */ + z0 = t0 & 0xFFFFFFFFFFFFFULL; + z1 = z0 ^ 0x1000003D0ULL; + + /* Fast return path should catch the majority of cases */ + if ((z0 != 0ULL) & (z1 != 0xFFFFFFFFFFFFFULL)) { + return 0; + } + + t1 = r->n[1]; + t2 = r->n[2]; + t3 = r->n[3]; + + t4 &= 0x0FFFFFFFFFFFFULL; + + t1 += (t0 >> 52); + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; z0 |= t1; z1 &= t1; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; z0 |= t2; z1 &= t2; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; z0 |= t3; z1 &= t3; + z0 |= t4; z1 &= t4 ^ 0xF000000000000ULL; + + /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t4 >> 49 == 0); + + return (z0 == 0) | (z1 == 0xFFFFFFFFFFFFFULL); +} + +SECP256K1_INLINE static void secp256k1_fe_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) { + 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 + return a->n[0] & 1; +} + +SECP256K1_INLINE static void secp256k1_fe_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) { + 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; + } + if (a->n[i] < b->n[i]) { + return -1; + } + } + return 0; +} + +static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) { + r->n[0] = (uint64_t)a[31] + | ((uint64_t)a[30] << 8) + | ((uint64_t)a[29] << 16) + | ((uint64_t)a[28] << 24) + | ((uint64_t)a[27] << 32) + | ((uint64_t)a[26] << 40) + | ((uint64_t)(a[25] & 0xF) << 48); + r->n[1] = (uint64_t)((a[25] >> 4) & 0xF) + | ((uint64_t)a[24] << 4) + | ((uint64_t)a[23] << 12) + | ((uint64_t)a[22] << 20) + | ((uint64_t)a[21] << 28) + | ((uint64_t)a[20] << 36) + | ((uint64_t)a[19] << 44); + r->n[2] = (uint64_t)a[18] + | ((uint64_t)a[17] << 8) + | ((uint64_t)a[16] << 16) + | ((uint64_t)a[15] << 24) + | ((uint64_t)a[14] << 32) + | ((uint64_t)a[13] << 40) + | ((uint64_t)(a[12] & 0xF) << 48); + r->n[3] = (uint64_t)((a[12] >> 4) & 0xF) + | ((uint64_t)a[11] << 4) + | ((uint64_t)a[10] << 12) + | ((uint64_t)a[9] << 20) + | ((uint64_t)a[8] << 28) + | ((uint64_t)a[7] << 36) + | ((uint64_t)a[6] << 44); + r->n[4] = (uint64_t)a[5] + | ((uint64_t)a[4] << 8) + | ((uint64_t)a[3] << 16) + | ((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; +} + +/** 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 + r[0] = (a->n[4] >> 40) & 0xFF; + r[1] = (a->n[4] >> 32) & 0xFF; + r[2] = (a->n[4] >> 24) & 0xFF; + r[3] = (a->n[4] >> 16) & 0xFF; + r[4] = (a->n[4] >> 8) & 0xFF; + r[5] = a->n[4] & 0xFF; + r[6] = (a->n[3] >> 44) & 0xFF; + r[7] = (a->n[3] >> 36) & 0xFF; + r[8] = (a->n[3] >> 28) & 0xFF; + r[9] = (a->n[3] >> 20) & 0xFF; + r[10] = (a->n[3] >> 12) & 0xFF; + r[11] = (a->n[3] >> 4) & 0xFF; + r[12] = ((a->n[2] >> 48) & 0xF) | ((a->n[3] & 0xF) << 4); + r[13] = (a->n[2] >> 40) & 0xFF; + r[14] = (a->n[2] >> 32) & 0xFF; + r[15] = (a->n[2] >> 24) & 0xFF; + r[16] = (a->n[2] >> 16) & 0xFF; + r[17] = (a->n[2] >> 8) & 0xFF; + r[18] = a->n[2] & 0xFF; + r[19] = (a->n[1] >> 44) & 0xFF; + r[20] = (a->n[1] >> 36) & 0xFF; + r[21] = (a->n[1] >> 28) & 0xFF; + r[22] = (a->n[1] >> 20) & 0xFF; + r[23] = (a->n[1] >> 12) & 0xFF; + r[24] = (a->n[1] >> 4) & 0xFF; + r[25] = ((a->n[0] >> 48) & 0xF) | ((a->n[1] & 0xF) << 4); + r[26] = (a->n[0] >> 40) & 0xFF; + r[27] = (a->n[0] >> 32) & 0xFF; + r[28] = (a->n[0] >> 24) & 0xFF; + r[29] = (a->n[0] >> 16) & 0xFF; + r[30] = (a->n[0] >> 8) & 0xFF; + 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 + 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) { + 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 + 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_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_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) { + uint64_t mask0, mask1; + mask0 = flag + ~((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_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) { + uint64_t mask0, mask1; + mask0 = flag + ~((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); +} + +static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) { +#ifdef VERIFY + VERIFY_CHECK(a->normalized); +#endif + 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) { + 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 +} + +#endif /* SECP256K1_FIELD_REPR_IMPL_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/field_5x52_int128_impl.h b/src/cryptoconditions/src/include/secp256k1/src/field_5x52_int128_impl.h new file mode 100644 index 000000000..95a0d1791 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/field_5x52_int128_impl.h @@ -0,0 +1,277 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_FIELD_INNER5X52_IMPL_H +#define SECP256K1_FIELD_INNER5X52_IMPL_H + +#include + +#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(uint64_t *r, const uint64_t *a, const uint64_t * SECP256K1_RESTRICT b) { + uint128_t 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; + + VERIFY_BITS(a[0], 56); + VERIFY_BITS(a[1], 56); + VERIFY_BITS(a[2], 56); + VERIFY_BITS(a[3], 56); + VERIFY_BITS(a[4], 52); + VERIFY_BITS(b[0], 56); + VERIFY_BITS(b[1], 56); + VERIFY_BITS(b[2], 56); + VERIFY_BITS(b[3], 56); + VERIFY_BITS(b[4], 52); + VERIFY_CHECK(r != 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). + * 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); + /* [d 0 0 0] = [p3 0 0 0] */ + c = (uint128_t)a4 * b[4]; + VERIFY_BITS(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; + 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] */ + + 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); + /* [d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + t4 = d & M; d >>= 52; + VERIFY_BITS(t4, 52); + VERIFY_BITS(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); + /* [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); + /* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + u0 = d & M; d >>= 52; + VERIFY_BITS(u0, 52); + VERIFY_BITS(d, 63); + /* [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); + /* [d 0 t4 t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + r[0] = c & M; c >>= 52; + VERIFY_BITS(r[0], 52); + VERIFY_BITS(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); + /* [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); + /* [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); + /* [d 0 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ + r[1] = c & M; c >>= 52; + VERIFY_BITS(r[1], 52); + VERIFY_BITS(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); + /* [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); + /* [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] */ + + /* [d 0 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[2] = c & M; 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); + /* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[3] = c & M; c >>= 52; + VERIFY_BITS(r[3], 52); + VERIFY_BITS(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; + 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; + uint64_t a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4]; + int64_t t3, t4, tx, u0; + const uint64_t M = 0xFFFFFFFFFFFFFULL, R = 0x1000003D10ULL; + + VERIFY_BITS(a[0], 56); + VERIFY_BITS(a[1], 56); + VERIFY_BITS(a[2], 56); + VERIFY_BITS(a[3], 56); + VERIFY_BITS(a[4], 52); + + /** [... a b c] is a shorthand for ... + a<<104 + b<<52 + c<<0 mod n. + * px is a shorthand for sum(a[i]*a[x-i], i=0..x). + * 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); + /* [d 0 0 0] = [p3 0 0 0] */ + c = (uint128_t)a4 * a4; + VERIFY_BITS(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; + 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] */ + + 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); + /* [d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + t4 = d & M; d >>= 52; + VERIFY_BITS(t4, 52); + VERIFY_BITS(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); + /* [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); + /* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + u0 = d & M; d >>= 52; + VERIFY_BITS(u0, 52); + VERIFY_BITS(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); + /* [d 0 t4 t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + r[0] = c & M; c >>= 52; + VERIFY_BITS(r[0], 52); + VERIFY_BITS(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); + /* [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); + /* [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); + /* [d 0 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ + r[1] = c & M; c >>= 52; + VERIFY_BITS(r[1], 52); + VERIFY_BITS(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); + /* [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); + /* [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; + 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); + /* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[3] = c & M; c >>= 52; + VERIFY_BITS(r[3], 52); + VERIFY_BITS(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; + VERIFY_BITS(r[4], 49); + /* [r4 r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ +} + +#endif /* SECP256K1_FIELD_INNER5X52_IMPL_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/field_impl.h b/src/cryptoconditions/src/include/secp256k1/src/field_impl.h new file mode 100644 index 000000000..20428648a --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/field_impl.h @@ -0,0 +1,315 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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 "util.h" + +#if defined(USE_FIELD_10X26) +#include "field_10x26_impl.h" +#elif defined(USE_FIELD_5X52) +#include "field_5x52_impl.h" +#else +#error "Please select field 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_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); +} + +static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *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. + * + * As (p+1)/4 is an even number, it will have the same result for a and for + * (-a). Only one of these two numbers actually has a square root however, + * so we test at the end by squaring and comparing to the input. + * Also because (p+1)/4 is an even number, the computed square root is + * 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; + + /** 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: + * 1, [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223] + */ + + secp256k1_fe_sqr(&x2, a); + secp256k1_fe_mul(&x2, &x2, a); + + secp256k1_fe_sqr(&x3, &x2); + secp256k1_fe_mul(&x3, &x3, a); + + x6 = x3; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x6, &x6); + } + secp256k1_fe_mul(&x6, &x6, &x3); + + x9 = x6; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x9, &x9); + } + secp256k1_fe_mul(&x9, &x9, &x3); + + x11 = x9; + for (j=0; j<2; j++) { + secp256k1_fe_sqr(&x11, &x11); + } + secp256k1_fe_mul(&x11, &x11, &x2); + + x22 = x11; + for (j=0; j<11; j++) { + secp256k1_fe_sqr(&x22, &x22); + } + secp256k1_fe_mul(&x22, &x22, &x11); + + x44 = x22; + for (j=0; j<22; j++) { + secp256k1_fe_sqr(&x44, &x44); + } + secp256k1_fe_mul(&x44, &x44, &x22); + + x88 = x44; + for (j=0; j<44; j++) { + secp256k1_fe_sqr(&x88, &x88); + } + secp256k1_fe_mul(&x88, &x88, &x44); + + x176 = x88; + for (j=0; j<88; j++) { + secp256k1_fe_sqr(&x176, &x176); + } + secp256k1_fe_mul(&x176, &x176, &x88); + + x220 = x176; + for (j=0; j<44; j++) { + secp256k1_fe_sqr(&x220, &x220); + } + secp256k1_fe_mul(&x220, &x220, &x44); + + x223 = x220; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x223, &x223); + } + secp256k1_fe_mul(&x223, &x223, &x3); + + /* The final result is then assembled using a sliding window over the blocks. */ + + t1 = x223; + for (j=0; j<23; j++) { + secp256k1_fe_sqr(&t1, &t1); + } + secp256k1_fe_mul(&t1, &t1, &x22); + for (j=0; j<6; j++) { + secp256k1_fe_sqr(&t1, &t1); + } + secp256k1_fe_mul(&t1, &t1, &x2); + secp256k1_fe_sqr(&t1, &t1); + secp256k1_fe_sqr(r, &t1); + + /* Check that a square root was actually calculated */ + + secp256k1_fe_sqr(&t1, r); + return secp256k1_fe_equal(&t1, a); +} + +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; + + /** 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] + */ + + secp256k1_fe_sqr(&x2, a); + secp256k1_fe_mul(&x2, &x2, a); + + secp256k1_fe_sqr(&x3, &x2); + secp256k1_fe_mul(&x3, &x3, a); + + x6 = x3; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x6, &x6); + } + secp256k1_fe_mul(&x6, &x6, &x3); + + x9 = x6; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x9, &x9); + } + secp256k1_fe_mul(&x9, &x9, &x3); + + x11 = x9; + for (j=0; j<2; j++) { + secp256k1_fe_sqr(&x11, &x11); + } + secp256k1_fe_mul(&x11, &x11, &x2); + + x22 = x11; + for (j=0; j<11; j++) { + secp256k1_fe_sqr(&x22, &x22); + } + secp256k1_fe_mul(&x22, &x22, &x11); + + x44 = x22; + for (j=0; j<22; j++) { + secp256k1_fe_sqr(&x44, &x44); + } + secp256k1_fe_mul(&x44, &x44, &x22); + + x88 = x44; + for (j=0; j<44; j++) { + secp256k1_fe_sqr(&x88, &x88); + } + secp256k1_fe_mul(&x88, &x88, &x44); + + x176 = x88; + for (j=0; j<88; j++) { + secp256k1_fe_sqr(&x176, &x176); + } + secp256k1_fe_mul(&x176, &x176, &x88); + + x220 = x176; + for (j=0; j<44; j++) { + secp256k1_fe_sqr(&x220, &x220); + } + secp256k1_fe_mul(&x220, &x220, &x44); + + x223 = x220; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x223, &x223); + } + secp256k1_fe_mul(&x223, &x223, &x3); + + /* The final result is then assembled using a sliding window over the blocks. */ + + 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); +} + +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_inv_all_var(secp256k1_fe *r, const secp256k1_fe *a, size_t len) { + secp256k1_fe u; + size_t i; + if (len < 1) { + return; + } + + VERIFY_CHECK((r + len <= a) || (a + len <= r)); + + r[0] = a[0]; + + i = 0; + while (++i < len) { + secp256k1_fe_mul(&r[i], &r[i - 1], &a[i]); + } + + secp256k1_fe_inv_var(&u, &r[--i]); + + while (i > 0) { + size_t j = i--; + secp256k1_fe_mul(&r[j], &r[i], &u); + secp256k1_fe_mul(&u, &u, &a[j]); + } + + r[0] = u; +} + +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 +} + +#endif /* SECP256K1_FIELD_IMPL_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/gen_context.c b/src/cryptoconditions/src/include/secp256k1/src/gen_context.c new file mode 100644 index 000000000..1835fd491 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/gen_context.c @@ -0,0 +1,74 @@ +/********************************************************************** + * Copyright (c) 2013, 2014, 2015 Thomas Daede, Cory Fields * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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/cryptoconditions/src/include/secp256k1/src/group.h b/src/cryptoconditions/src/include/secp256k1/src/group.h new file mode 100644 index 000000000..ea1302deb --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/group.h @@ -0,0 +1,144 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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. */ +typedef struct { + secp256k1_fe x; + secp256k1_fe y; + int infinity; /* whether this represents the point at infinity */ +} secp256k1_ge; + +#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. */ +typedef struct { + secp256k1_fe x; /* actual X: x/z^2 */ + secp256k1_fe y; /* actual Y: y/z^3 */ + secp256k1_fe z; + int infinity; /* whether this represents the point at infinity */ +} secp256k1_gej; + +#define SECP256K1_GEJ_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)), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1), 0} +#define SECP256K1_GEJ_CONST_INFINITY {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), 1} + +typedef struct { + secp256k1_fe_storage x; + secp256k1_fe_storage y; +} secp256k1_ge_storage; + +#define SECP256K1_GE_STORAGE_CONST(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {SECP256K1_FE_STORAGE_CONST((a),(b),(c),(d),(e),(f),(g),(h)), SECP256K1_FE_STORAGE_CONST((i),(j),(k),(l),(m),(n),(o),(p))} + +#define SECP256K1_GE_STORAGE_CONST_GET(t) SECP256K1_FE_STORAGE_CONST_GET(t.x), SECP256K1_FE_STORAGE_CONST_GET(t.y) + +/** 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); + +/** 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); + +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 */ +static void secp256k1_ge_set_gej(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); + +/** 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); + +/** 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 (jacobian) equal to the point at infinity. */ +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). */ +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) */ +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. 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). */ +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). */ +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). */ +static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b); + +/** 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). */ +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); + +/** Clear a secp256k1_ge to prevent leaking sensitive information. */ +static void secp256k1_ge_clear(secp256k1_ge *r); + +/** Convert a group element to the storage type. */ +static void secp256k1_ge_to_storage(secp256k1_ge_storage *r, const secp256k1_ge *a); + +/** 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. */ +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); + +#endif /* SECP256K1_GROUP_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/group_impl.h b/src/cryptoconditions/src/include/secp256k1/src/group_impl.h new file mode 100644 index 000000000..b31b6c12e --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/group_impl.h @@ -0,0 +1,700 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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() + */ +#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; +# 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; +# 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; +#endif + +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_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; +} + +static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const secp256k1_fe *y) { + r->infinity = 0; + r->x = *x; + r->y = *y; +} + +static int secp256k1_ge_is_infinity(const secp256k1_ge *a) { + return a->infinity; +} + +static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a) { + *r = *a; + secp256k1_fe_normalize_weak(&r->y); + secp256k1_fe_negate(&r->y, &r->y, 1); +} + +static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a) { + secp256k1_fe z2, z3; + r->infinity = a->infinity; + secp256k1_fe_inv(&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; +} + +static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a) { + secp256k1_fe z2, z3; + r->infinity = a->infinity; + if (a->infinity) { + return; + } + 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; +} + +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; + size_t i; + size_t count = 0; + az = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * len); + for (i = 0; i < len; i++) { + if (!a[i].infinity) { + az[count++] = a[i].z; + } + } + + azi = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * count); + secp256k1_fe_inv_all_var(azi, az, count); + free(az); + + 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++]); + } + } + 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); + } + } +} + +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; + secp256k1_fe zs; + + 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; + zs = zr[i]; + + /* Work our way backwards, using the z-ratios to scale the x/y values. */ + while (i > 0) { + if (i != len - 1) { + secp256k1_fe_mul(&zs, &zs, &zr[i]); + } + i--; + secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zs); + } + } +} + +static void secp256k1_gej_set_infinity(secp256k1_gej *r) { + r->infinity = 1; + secp256k1_fe_clear(&r->x); + secp256k1_fe_clear(&r->y); + secp256k1_fe_clear(&r->z); +} + +static void secp256k1_gej_clear(secp256k1_gej *r) { + r->infinity = 0; + secp256k1_fe_clear(&r->x); + secp256k1_fe_clear(&r->y); + secp256k1_fe_clear(&r->z); +} + +static void secp256k1_ge_clear(secp256k1_ge *r) { + r->infinity = 0; + secp256k1_fe_clear(&r->x); + secp256k1_fe_clear(&r->y); +} + +static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x) { + secp256k1_fe x2, x3, c; + 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_normalize_var(&r->y); + if (secp256k1_fe_is_odd(&r->y) != odd) { + secp256k1_fe_negate(&r->y, &r->y, 1); + } + return 1; + +} + +static void secp256k1_gej_set_ge(secp256k1_gej *r, const secp256k1_ge *a) { + r->infinity = a->infinity; + r->x = a->x; + r->y = a->y; + secp256k1_fe_set_int(&r->z, 1); +} + +static int secp256k1_gej_eq_x_var(const secp256k1_fe *x, const secp256k1_gej *a) { + secp256k1_fe r, r2; + 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); +} + +static void secp256k1_gej_neg(secp256k1_gej *r, const secp256k1_gej *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); +} + +static int secp256k1_gej_is_infinity(const secp256k1_gej *a) { + return a->infinity; +} + +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); +} + +static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) { + secp256k1_fe y2, x3, c; + 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); +} + +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. + */ + secp256k1_fe t1,t2,t3,t4; + /** 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. + * + * Having said this, if this function receives a point on a sextic twist, e.g. by + * a fault attack, it is possible for y to be 0. This happens for y^2 = x^3 + 6, + * since -6 does have a cube root mod p. For this point, this function will not set + * 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 (rzr != NULL) { + secp256k1_fe_set_int(rzr, 1); + } + return; + } + + 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); +} + +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; + + if (a->infinity) { + VERIFY_CHECK(rzr == NULL); + *r = *b; + return; + } + + if (b->infinity) { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 1); + } + *r = *a; + return; + } + + r->infinity = 0; + secp256k1_fe_sqr(&z22, &b->z); + secp256k1_fe_sqr(&z12, &a->z); + secp256k1_fe_mul(&u1, &a->x, &z22); + secp256k1_fe_mul(&u2, &b->x, &z12); + 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); + if (secp256k1_fe_normalizes_to_zero_var(&h)) { + if (secp256k1_fe_normalizes_to_zero_var(&i)) { + secp256k1_gej_double_var(r, a, rzr); + } else { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 0); + } + r->infinity = 1; + } + return; + } + secp256k1_fe_sqr(&i2, &i); + secp256k1_fe_sqr(&h2, &h); + secp256k1_fe_mul(&h3, &h, &h2); + secp256k1_fe_mul(&h, &h, &b->z); + if (rzr != NULL) { + *rzr = h; + } + secp256k1_fe_mul(&r->z, &a->z, &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_add(&r->y, &h3); +} + +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; + if (a->infinity) { + VERIFY_CHECK(rzr == NULL); + secp256k1_gej_set_ge(r, b); + return; + } + if (b->infinity) { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 1); + } + *r = *a; + return; + } + r->infinity = 0; + + secp256k1_fe_sqr(&z12, &a->z); + u1 = a->x; secp256k1_fe_normalize_weak(&u1); + secp256k1_fe_mul(&u2, &b->x, &z12); + s1 = a->y; secp256k1_fe_normalize_weak(&s1); + 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); + if (secp256k1_fe_normalizes_to_zero_var(&h)) { + if (secp256k1_fe_normalizes_to_zero_var(&i)) { + secp256k1_gej_double_var(r, a, rzr); + } else { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 0); + } + r->infinity = 1; + } + return; + } + secp256k1_fe_sqr(&i2, &i); + secp256k1_fe_sqr(&h2, &h); + secp256k1_fe_mul(&h3, &h, &h2); + if (rzr != NULL) { + *rzr = h; + } + secp256k1_fe_mul(&r->z, &a->z, &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_add(&r->y, &h3); +} + +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; + + if (b->infinity) { + *r = *a; + return; + } + if (a->infinity) { + secp256k1_fe bzinv2, bzinv3; + r->infinity = b->infinity; + secp256k1_fe_sqr(&bzinv2, bzinv); + secp256k1_fe_mul(&bzinv3, &bzinv2, bzinv); + secp256k1_fe_mul(&r->x, &b->x, &bzinv2); + secp256k1_fe_mul(&r->y, &b->y, &bzinv3); + secp256k1_fe_set_int(&r->z, 1); + 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 + * by bzinv, and get: (rx,ry,rz*bzinv) = (ax,ay,az*bzinv) + (bx,by,1). + * This means that (rx,ry,rz) can be calculated as + * (ax,ay,az*bzinv) + (bx,by,1), when not applying the bzinv factor to rz. + * The variable az below holds the modified Z coordinate for a, which is used + * for the computation of rx and ry, but not for rz. + */ + secp256k1_fe_mul(&az, &a->z, bzinv); + + secp256k1_fe_sqr(&z12, &az); + u1 = a->x; secp256k1_fe_normalize_weak(&u1); + secp256k1_fe_mul(&u2, &b->x, &z12); + s1 = a->y; secp256k1_fe_normalize_weak(&s1); + 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); + 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; + } + return; + } + secp256k1_fe_sqr(&i2, &i); + 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_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_add(&r->y, &h3); +} + + +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); + secp256k1_fe zz, u1, u2, s1, s2, t, tt, m, n, q, rr; + secp256k1_fe m_alt, rr_alt; + int infinity, degenerate; + VERIFY_CHECK(!b->infinity); + VERIFY_CHECK(a->infinity == 0 || a->infinity == 1); + + /** 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: + * lambda = ((x1 + x2)^2 - x1 * x2 + a) / (y1 + y2), with a = 0 for secp256k1's curve equation. + * x3 = lambda^2 - (x1 + x2) + * 2*y3 = lambda * (x1 + x2 - 2 * x3) - (y1 + y2). + * + * Substituting x_i = Xi / Zi^2 and yi = Yi / Zi^3, for i=1,2,3, gives: + * U1 = X1*Z2^2, U2 = X2*Z1^2 + * S1 = Y1*Z2^3, S2 = Y2*Z1^3 + * Z = Z1*Z2 + * T = U1+U2 + * M = S1+S2 + * 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 + * (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 + * of distinct points and doubling. However, it breaks down in the + * case that either point is infinity, or that y1 = -y2. We handle + * these cases in the following ways: + * + * - If b is infinity we simply bail by means of a VERIFY_CHECK. + * + * - If a is infinity, we detect this, and at the end of the + * computation replace the result (which will be meaningless, + * but we compute to be constant-time) with b.x : b.y : 1. + * + * - If a = -b, we have y1 = -y2, which is a degenerate case. + * But here the answer is infinity, so we simply set the + * infinity flag of the result, overriding the computed values + * without even needing to cmov. + * + * - If y1 = -y2 but x1 != x2, which does occur thanks to certain + * properties of our curve (specifically, 1 has nontrivial cube + * roots in our field, and the curve equation has no x coefficient) + * then the answer is not infinity but also not given by the above + * equation. In this case, we cmov in place an alternate expression + * for lambda. Specifically (y1 - y2)/(x1 - x2). Where both these + * expressions for lambda are defined, they are equal, and can be + * obtained from each other by multiplication by (y1 + y2)/(y1 + y2) + * then substitution of x^3 + 7 for y^2 (using the curve equation). + * For all pairs of nonzero points (a, b) at least one is defined, + * so this covers everything. + */ + + secp256k1_fe_sqr(&zz, &a->z); /* z = Z1^2 */ + u1 = a->x; secp256k1_fe_normalize_weak(&u1); /* u1 = U1 = X1*Z2^2 (1) */ + 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) */ + 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) */ + 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); + /* 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_cmov(&rr_alt, &rr, !degenerate); + secp256k1_fe_cmov(&m_alt, &m, !degenerate); + /* Now Ralt / Malt = lambda and is guaranteed not to be 0/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) */ + /* 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(&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_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; +} + +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_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 */ +} + +static void secp256k1_ge_to_storage(secp256k1_ge_storage *r, const secp256k1_ge *a) { + secp256k1_fe x, y; + VERIFY_CHECK(!a->infinity); + x = a->x; + secp256k1_fe_normalize(&x); + y = a->y; + secp256k1_fe_normalize(&y); + secp256k1_fe_to_storage(&r->x, &x); + secp256k1_fe_to_storage(&r->y, &y); +} + +static void secp256k1_ge_from_storage(secp256k1_ge *r, const secp256k1_ge_storage *a) { + secp256k1_fe_from_storage(&r->x, &a->x); + secp256k1_fe_from_storage(&r->y, &a->y); + r->infinity = 0; +} + +static SECP256K1_INLINE void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_ge_storage *a, int flag) { + secp256k1_fe_storage_cmov(&r->x, &a->x, flag); + 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 + ); + *r = *a; + secp256k1_fe_mul(&r->x, &r->x, &beta); +} +#endif + +static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) { + secp256k1_fe yz; + + if (a->infinity) { + return 0; + } + + /* 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); +} + +#endif /* SECP256K1_GROUP_IMPL_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/hash.h b/src/cryptoconditions/src/include/secp256k1/src/hash.h new file mode 100644 index 000000000..de26e4b89 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/hash.h @@ -0,0 +1,41 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_HASH_H +#define SECP256K1_HASH_H + +#include +#include + +typedef struct { + uint32_t s[8]; + uint32_t buf[16]; /* In big endian */ + size_t bytes; +} secp256k1_sha256; + +static void secp256k1_sha256_initialize(secp256k1_sha256 *hash); +static void secp256k1_sha256_write(secp256k1_sha256 *hash, const unsigned char *data, size_t size); +static void secp256k1_sha256_finalize(secp256k1_sha256 *hash, unsigned char *out32); + +typedef struct { + secp256k1_sha256 inner, outer; +} secp256k1_hmac_sha256; + +static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t size); +static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256 *hash, const unsigned char *data, size_t size); +static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256 *hash, unsigned char *out32); + +typedef struct { + unsigned char v[32]; + unsigned char k[32]; + int retry; +} secp256k1_rfc6979_hmac_sha256; + +static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256 *rng, const unsigned char *key, size_t keylen); +static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256 *rng, unsigned char *out, size_t outlen); +static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256 *rng); + +#endif /* SECP256K1_HASH_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/hash_impl.h b/src/cryptoconditions/src/include/secp256k1/src/hash_impl.h new file mode 100644 index 000000000..c06db9e33 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/hash_impl.h @@ -0,0 +1,281 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_HASH_IMPL_H +#define SECP256K1_HASH_IMPL_H + +#include "hash.h" + +#include +#include +#include + +#define Ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z)))) +#define Maj(x,y,z) (((x) & (y)) | ((z) & ((x) | (y)))) +#define Sigma0(x) (((x) >> 2 | (x) << 30) ^ ((x) >> 13 | (x) << 19) ^ ((x) >> 22 | (x) << 10)) +#define Sigma1(x) (((x) >> 6 | (x) << 26) ^ ((x) >> 11 | (x) << 21) ^ ((x) >> 25 | (x) << 7)) +#define sigma0(x) (((x) >> 7 | (x) << 25) ^ ((x) >> 18 | (x) << 14) ^ ((x) >> 3)) +#define sigma1(x) (((x) >> 17 | (x) << 15) ^ ((x) >> 19 | (x) << 13) ^ ((x) >> 10)) + +#define Round(a,b,c,d,e,f,g,h,k,w) do { \ + uint32_t t1 = (h) + Sigma1(e) + Ch((e), (f), (g)) + (k) + (w); \ + uint32_t t2 = Sigma0(a) + Maj((a), (b), (c)); \ + (d) += t1; \ + (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; + hash->s[2] = 0x3c6ef372ul; + hash->s[3] = 0xa54ff53aul; + hash->s[4] = 0x510e527ful; + hash->s[5] = 0x9b05688cul; + hash->s[6] = 0x1f83d9abul; + hash->s[7] = 0x5be0cd19ul; + hash->bytes = 0; +} + +/** Perform one SHA-256 transformation, processing 16 big endian 32-bit words. */ +static void secp256k1_sha256_transform(uint32_t* s, const uint32_t* chunk) { + 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, 0xe49b69c1, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, c, d, e, f, g, 0xefbe4786, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, b, c, d, e, f, 0x0fc19dc6, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, a, b, c, d, e, 0x240ca1cc, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, h, a, b, c, d, 0x2de92c6f, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, g, h, a, b, c, 0x4a7484aa, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, f, g, h, a, b, 0x5cb0a9dc, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, e, f, g, h, a, 0x76f988da, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, d, e, f, g, h, 0x983e5152, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, c, d, e, f, g, 0xa831c66d, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0xb00327c8, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0xbf597fc7, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0xc6e00bf3, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xd5a79147, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0x06ca6351, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0x14292967, w15 += sigma1(w13) + w8 + sigma0(w0)); + + Round(a, b, c, d, e, f, g, h, 0x27b70a85, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, c, d, e, f, g, 0x2e1b2138, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, b, c, d, e, f, 0x4d2c6dfc, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, a, b, c, d, e, 0x53380d13, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, h, a, b, c, d, 0x650a7354, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, g, h, a, b, c, 0x766a0abb, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, f, g, h, a, b, 0x81c2c92e, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, e, f, g, h, a, 0x92722c85, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, d, e, f, g, h, 0xa2bfe8a1, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, c, d, e, f, g, 0xa81a664b, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0xc24b8b70, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0xc76c51a3, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0xd192e819, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xd6990624, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0xf40e3585, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0x106aa070, w15 += sigma1(w13) + w8 + sigma0(w0)); + + Round(a, b, c, d, e, f, g, h, 0x19a4c116, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, c, d, e, f, g, 0x1e376c08, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, b, c, d, e, f, 0x2748774c, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, a, b, c, d, e, 0x34b0bcb5, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, h, a, b, c, d, 0x391c0cb3, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, g, h, a, b, c, 0x4ed8aa4a, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, f, g, h, a, b, 0x5b9cca4f, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, e, f, g, h, a, 0x682e6ff3, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, d, e, f, g, h, 0x748f82ee, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, c, d, e, f, g, 0x78a5636f, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0x84c87814, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0x8cc70208, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0x90befffa, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xa4506ceb, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0xbef9a3f7, w14 + sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0xc67178f2, w15 + sigma1(w13) + w8 + sigma0(w0)); + + s[0] += a; + s[1] += b; + s[2] += c; + s[3] += d; + s[4] += e; + s[5] += f; + s[6] += g; + s[7] += h; +} + +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) { + /* Fill the buffer, and process it. */ + memcpy(((unsigned char*)hash->buf) + bufsize, data, 64 - bufsize); + data += 64 - bufsize; + len -= 64 - bufsize; + 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); + } +} + +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); + secp256k1_sha256_write(hash, pad, 1 + ((119 - (hash->bytes % 64)) % 64)); + secp256k1_sha256_write(hash, (const unsigned char*)sizedesc, 8); + for (i = 0; i < 8; i++) { + out[i] = BE32(hash->s[i]); + hash->s[i] = 0; + } + memcpy(out32, (const unsigned char*)out, 32); +} + +static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t keylen) { + int n; + unsigned char rkey[64]; + if (keylen <= 64) { + memcpy(rkey, key, keylen); + memset(rkey + keylen, 0, 64 - keylen); + } else { + secp256k1_sha256 sha256; + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, key, keylen); + secp256k1_sha256_finalize(&sha256, rkey); + memset(rkey + 32, 0, 32); + } + + secp256k1_sha256_initialize(&hash->outer); + for (n = 0; n < 64; n++) { + rkey[n] ^= 0x5c; + } + secp256k1_sha256_write(&hash->outer, rkey, 64); + + secp256k1_sha256_initialize(&hash->inner); + for (n = 0; n < 64; n++) { + rkey[n] ^= 0x5c ^ 0x36; + } + secp256k1_sha256_write(&hash->inner, rkey, 64); + memset(rkey, 0, 64); +} + +static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256 *hash, const unsigned char *data, size_t size) { + secp256k1_sha256_write(&hash->inner, data, size); +} + +static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256 *hash, unsigned char *out32) { + unsigned char temp[32]; + secp256k1_sha256_finalize(&hash->inner, temp); + secp256k1_sha256_write(&hash->outer, temp, 32); + memset(temp, 0, 32); + secp256k1_sha256_finalize(&hash->outer, out32); +} + + +static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256 *rng, const unsigned char *key, size_t keylen) { + secp256k1_hmac_sha256 hmac; + static const unsigned char zero[1] = {0x00}; + static const unsigned char one[1] = {0x01}; + + memset(rng->v, 0x01, 32); /* RFC6979 3.2.b. */ + memset(rng->k, 0x00, 32); /* RFC6979 3.2.c. */ + + /* RFC6979 3.2.d. */ + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_write(&hmac, zero, 1); + secp256k1_hmac_sha256_write(&hmac, key, keylen); + secp256k1_hmac_sha256_finalize(&hmac, rng->k); + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_finalize(&hmac, rng->v); + + /* RFC6979 3.2.f. */ + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_write(&hmac, one, 1); + secp256k1_hmac_sha256_write(&hmac, key, keylen); + secp256k1_hmac_sha256_finalize(&hmac, rng->k); + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_finalize(&hmac, rng->v); + rng->retry = 0; +} + +static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256 *rng, unsigned char *out, size_t outlen) { + /* RFC6979 3.2.h. */ + static const unsigned char zero[1] = {0x00}; + if (rng->retry) { + secp256k1_hmac_sha256 hmac; + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_write(&hmac, zero, 1); + secp256k1_hmac_sha256_finalize(&hmac, rng->k); + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_finalize(&hmac, rng->v); + } + + while (outlen > 0) { + secp256k1_hmac_sha256 hmac; + int now = outlen; + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_finalize(&hmac, rng->v); + if (now > 32) { + now = 32; + } + memcpy(out, rng->v, now); + out += now; + outlen -= now; + } + + rng->retry = 1; +} + +static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256 *rng) { + memset(rng->k, 0, 32); + memset(rng->v, 0, 32); + rng->retry = 0; +} + +#undef BE32 +#undef Round +#undef sigma1 +#undef sigma0 +#undef Sigma1 +#undef Sigma0 +#undef Maj +#undef Ch + +#endif /* SECP256K1_HASH_IMPL_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java b/src/cryptoconditions/src/include/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java new file mode 100644 index 000000000..1c67802fb --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java @@ -0,0 +1,446 @@ +/* + * 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/cryptoconditions/src/include/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java b/src/cryptoconditions/src/include/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java new file mode 100644 index 000000000..c00d08899 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java @@ -0,0 +1,226 @@ +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/cryptoconditions/src/include/secp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java b/src/cryptoconditions/src/include/secp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java new file mode 100644 index 000000000..04732ba04 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java @@ -0,0 +1,45 @@ +/* + * 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/cryptoconditions/src/include/secp256k1/src/java/org/bitcoin/Secp256k1Context.java b/src/cryptoconditions/src/include/secp256k1/src/java/org/bitcoin/Secp256k1Context.java new file mode 100644 index 000000000..216c986a8 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/java/org/bitcoin/Secp256k1Context.java @@ -0,0 +1,51 @@ +/* + * 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/cryptoconditions/src/include/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c b/src/cryptoconditions/src/include/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c new file mode 100644 index 000000000..bcef7b32c --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c @@ -0,0 +1,377 @@ +#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/cryptoconditions/src/include/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h b/src/cryptoconditions/src/include/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h new file mode 100644 index 000000000..fe613c9e9 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h @@ -0,0 +1,119 @@ +/* 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/cryptoconditions/src/include/secp256k1/src/java/org_bitcoin_Secp256k1Context.c b/src/cryptoconditions/src/include/secp256k1/src/java/org_bitcoin_Secp256k1Context.c new file mode 100644 index 000000000..a52939e7e --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/java/org_bitcoin_Secp256k1Context.c @@ -0,0 +1,15 @@ +#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/cryptoconditions/src/include/secp256k1/src/java/org_bitcoin_Secp256k1Context.h b/src/cryptoconditions/src/include/secp256k1/src/java/org_bitcoin_Secp256k1Context.h new file mode 100644 index 000000000..0d2bc84b7 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/java/org_bitcoin_Secp256k1Context.h @@ -0,0 +1,22 @@ +/* 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/cryptoconditions/src/include/secp256k1/src/modules/ecdh/Makefile.am.include b/src/cryptoconditions/src/include/secp256k1/src/modules/ecdh/Makefile.am.include new file mode 100644 index 000000000..e3088b469 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/modules/ecdh/Makefile.am.include @@ -0,0 +1,8 @@ +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 diff --git a/src/cryptoconditions/src/include/secp256k1/src/modules/ecdh/main_impl.h b/src/cryptoconditions/src/include/secp256k1/src/modules/ecdh/main_impl.h new file mode 100644 index 000000000..bd8739eeb --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/modules/ecdh/main_impl.h @@ -0,0 +1,54 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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" + +int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const secp256k1_pubkey *point, const unsigned char *scalar) { + int ret = 0; + int overflow = 0; + secp256k1_gej res; + secp256k1_ge pt; + secp256k1_scalar s; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(result != NULL); + ARG_CHECK(point != NULL); + ARG_CHECK(scalar != NULL); + + 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; + } + + secp256k1_scalar_clear(&s); + return ret; +} + +#endif /* SECP256K1_MODULE_ECDH_MAIN_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/modules/ecdh/tests_impl.h b/src/cryptoconditions/src/include/secp256k1/src/modules/ecdh/tests_impl.h new file mode 100644 index 000000000..0c53f8ee0 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/modules/ecdh/tests_impl.h @@ -0,0 +1,105 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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); + 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 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); +} + +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) { + secp256k1_sha256 sha; + unsigned char s_b32[32]; + unsigned char output_ecdh[32]; + unsigned char output_ser[32]; + unsigned char point_ser[33]; + size_t point_ser_len = sizeof(point_ser); + secp256k1_scalar s; + + 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); + /* 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)); + 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); + } +} + +void test_bad_scalar(void) { + unsigned char s_zero[32] = { 0 }; + unsigned char s_overflow[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 + }; + unsigned char s_rand[32] = { 0 }; + unsigned char output[32]; + secp256k1_scalar rand; + secp256k1_pubkey point; + + /* Create random point */ + random_scalar_order(&rand); + secp256k1_scalar_get_b32(s_rand, &rand); + 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); + /* ...and a good one */ + s_overflow[31] -= 1; + CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow) == 1); +} + +void run_ecdh_tests(void) { + test_ecdh_api(); + test_ecdh_generator_basepoint(); + test_bad_scalar(); +} + +#endif /* SECP256K1_MODULE_ECDH_TESTS_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/modules/recovery/Makefile.am.include b/src/cryptoconditions/src/include/secp256k1/src/modules/recovery/Makefile.am.include new file mode 100644 index 000000000..bf23c26e7 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/modules/recovery/Makefile.am.include @@ -0,0 +1,8 @@ +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 diff --git a/src/cryptoconditions/src/include/secp256k1/src/modules/recovery/main_impl.h b/src/cryptoconditions/src/include/secp256k1/src/modules/recovery/main_impl.h new file mode 100755 index 000000000..2f6691c5a --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/modules/recovery/main_impl.h @@ -0,0 +1,193 @@ +/********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_RECOVERY_MAIN_H +#define SECP256K1_MODULE_RECOVERY_MAIN_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; + if (sizeof(secp256k1_scalar) == 32) { + /* When the secp256k1_scalar type is exactly 32 byte, use its + * representation inside secp256k1_ecdsa_signature, as conversion is very fast. + * Note that secp256k1_ecdsa_signature_save must use the same representation. */ + memcpy(r, &sig->data[0], 32); + memcpy(s, &sig->data[32], 32); + } else { + secp256k1_scalar_set_b32(r, &sig->data[0], NULL); + secp256k1_scalar_set_b32(s, &sig->data[32], NULL); + } + *recid = sig->data[64]; +} + +static void secp256k1_ecdsa_recoverable_signature_save(secp256k1_ecdsa_recoverable_signature* sig, const secp256k1_scalar* r, const secp256k1_scalar* s, int recid) { + if (sizeof(secp256k1_scalar) == 32) { + memcpy(&sig->data[0], r, 32); + memcpy(&sig->data[32], s, 32); + } else { + secp256k1_scalar_get_b32(&sig->data[0], r); + secp256k1_scalar_get_b32(&sig->data[32], s); + } + sig->data[64] = recid; +} + +int secp256k1_ecdsa_recoverable_signature_parse_compact(const secp256k1_context* ctx, secp256k1_ecdsa_recoverable_signature* sig, const unsigned char *input64, int recid) { + secp256k1_scalar r, s; + int ret = 1; + int overflow = 0; + + (void)ctx; + ARG_CHECK(sig != NULL); + ARG_CHECK(input64 != NULL); + ARG_CHECK(recid >= 0 && recid <= 3); + + secp256k1_scalar_set_b32(&r, &input64[0], &overflow); + ret &= !overflow; + secp256k1_scalar_set_b32(&s, &input64[32], &overflow); + ret &= !overflow; + if (ret) { + secp256k1_ecdsa_recoverable_signature_save(sig, &r, &s, recid); + } else { + memset(sig, 0, sizeof(*sig)); + } + return ret; +} + +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; + ARG_CHECK(output64 != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(recid != NULL); + + secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, recid, sig); + secp256k1_scalar_get_b32(&output64[0], &r); + secp256k1_scalar_get_b32(&output64[32], &s); + return 1; +} + +int secp256k1_ecdsa_recoverable_signature_convert(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const secp256k1_ecdsa_recoverable_signature* sigin) { + secp256k1_scalar r, s; + int recid; + + (void)ctx; + ARG_CHECK(sig != NULL); + ARG_CHECK(sigin != NULL); + + secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, sigin); + secp256k1_ecdsa_signature_save(sig, &r, &s); + 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) { + unsigned char brx[32]; + secp256k1_fe fx; + secp256k1_ge x; + secp256k1_gej xj; + secp256k1_scalar rn, u1, u2; + secp256k1_gej qj; + int r; + + if (secp256k1_scalar_is_zero(sigr) || secp256k1_scalar_is_zero(sigs)) { + return 0; + } + + secp256k1_scalar_get_b32(brx, sigr); + r = secp256k1_fe_set_b32(&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) { + if (secp256k1_fe_cmp_var(&fx, &secp256k1_ecdsa_const_p_minus_order) >= 0) { + return 0; + } + secp256k1_fe_add(&fx, &secp256k1_ecdsa_const_order_as_fe); + } + if (!secp256k1_ge_set_xo_var(&x, &fx, recid & 1)) { + return 0; + } + secp256k1_gej_set_ge(&xj, &x); + secp256k1_scalar_inverse_var(&rn, sigr); + 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_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) { + secp256k1_scalar r, s; + secp256k1_scalar sec, non, msg; + int recid; + 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); + 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)); + } + return ret; +} + +int secp256k1_ecdsa_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msg32) { + 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(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_pubkey_save(pubkey, &q); + return 1; + } else { + memset(pubkey, 0, sizeof(*pubkey)); + return 0; + } +} + +#endif /* SECP256K1_MODULE_RECOVERY_MAIN_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/modules/recovery/tests_impl.h b/src/cryptoconditions/src/include/secp256k1/src/modules/recovery/tests_impl.h new file mode 100644 index 000000000..5c9bbe861 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/modules/recovery/tests_impl.h @@ -0,0 +1,393 @@ +/********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_RECOVERY_TESTS_H +#define SECP256K1_MODULE_RECOVERY_TESTS_H + +static int recovery_test_nonce_function(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + (void) msg32; + (void) key32; + (void) algo16; + (void) data; + + /* On the first run, return 0 to force a second run */ + if (counter == 0) { + memset(nonce32, 0, 32); + return 1; + } + /* On the second run, return an overflow to force a third run */ + if (counter == 1) { + memset(nonce32, 0xff, 32); + return 1; + } + /* 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); +} + +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 }; + unsigned char over_privkey[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 }; + + 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 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); + /* 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); + /* 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); + /* This one will succeed. */ + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, NULL, NULL) == 1); + CHECK(ecount == 5); + + /* 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 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 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_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 */ + 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); +} + +void test_ecdsa_recovery_end_to_end(void) { + unsigned char extra[32] = {0x00}; + unsigned char privkey[32]; + unsigned char message[32]; + secp256k1_ecdsa_signature signature[5]; + secp256k1_ecdsa_recoverable_signature rsignature[5]; + unsigned char sig[74]; + secp256k1_pubkey pubkey; + secp256k1_pubkey recpubkey; + int recid = 0; + + /* Generate a random key and message. */ + { + secp256k1_scalar msg, key; + random_scalar_order_test(&msg); + 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); + + /* 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); + extra[31] = 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); + 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); + /* 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); + /* 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); + /* Recover again */ + CHECK(secp256k1_ecdsa_recover(ctx, &recpubkey, &rsignature[4], message) == 0 || + memcmp(&pubkey, &recpubkey, sizeof(pubkey)) != 0); +} + +/* Tests several edge cases. */ +void test_ecdsa_recovery_edge_cases(void) { + const unsigned char msg32[32] = { + 'T', 'h', 'i', 's', ' ', 'i', 's', ' ', + 'a', ' ', 'v', 'e', 'r', 'y', ' ', 's', + 'e', 'c', 'r', 'e', 't', ' ', 'm', 'e', + 's', 's', 'a', 'g', 'e', '.', '.', '.' + }; + 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. */ + 0x67, 0xCB, 0x28, 0x5F, 0x9C, 0xD1, 0x94, 0xE8, + 0x40, 0xD6, 0x29, 0x39, 0x7A, 0xF5, 0x56, 0x96, + 0x62, 0xFD, 0xE4, 0x46, 0x49, 0x99, 0x59, 0x63, + 0x17, 0x9A, 0x7D, 0xD1, 0x7B, 0xD2, 0x35, 0x32, + 0x4B, 0x1B, 0x7D, 0xF3, 0x4C, 0xE1, 0xF6, 0x8E, + 0x69, 0x4F, 0xF6, 0xF1, 0x1A, 0xC7, 0x51, 0xDD, + 0x7D, 0xD7, 0x3E, 0x38, 0x7E, 0xE4, 0xFC, 0x86, + 0x6E, 0x1B, 0xE8, 0xEC, 0xC7, 0xDD, 0x95, 0x57 + }; + secp256k1_pubkey pubkey; + /* signature (r,s) = (4,4), which can be recovered with all 4 recids. */ + const unsigned char sigb64[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, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + }; + secp256k1_pubkey pubkeyb; + secp256k1_ecdsa_recoverable_signature rsig; + 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)); + + for (recid = 0; recid < 4; recid++) { + int i; + int recid2; + /* (4,4) encoded in DER. */ + unsigned char sigbder[8] = {0x30, 0x06, 0x02, 0x01, 0x04, 0x02, 0x01, 0x04}; + unsigned char sigcder_zr[7] = {0x30, 0x05, 0x02, 0x00, 0x02, 0x01, 0x01}; + unsigned char sigcder_zs[7] = {0x30, 0x05, 0x02, 0x01, 0x01, 0x02, 0x00}; + unsigned char sigbderalt1[39] = { + 0x30, 0x25, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x02, 0x01, 0x04, + }; + unsigned char sigbderalt2[39] = { + 0x30, 0x25, 0x02, 0x01, 0x04, 0x02, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + }; + unsigned char sigbderalt3[40] = { + 0x30, 0x26, 0x02, 0x21, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x01, 0x04, + }; + unsigned char sigbderalt4[40] = { + 0x30, 0x26, 0x02, 0x01, 0x04, 0x02, 0x21, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + }; + /* (order + r,4) encoded in DER. */ + unsigned char sigbderlong[40] = { + 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, 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); + 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); + /* 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); + } + /* 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); + /* 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); + sigbderalt3[4] = 1; + 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); + /* 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); + 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); + for(i = 0; i < 8; i++) { + int c; + unsigned char orig = sigbder[i]; + /*Try every single-byte change.*/ + for (c = 0; c < 256; c++) { + if (c == orig ) { + continue; + } + sigbder[i] = c; + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder)) == 0 || secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + } + sigbder[i] = orig; + } + } + + /* Test r/s equal to zero */ + { + /* (1,1) encoded in DER. */ + unsigned char sigcder[8] = {0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01}; + unsigned char sigc64[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, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, + }; + 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); + 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); + 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); + } +} + +void run_recovery_tests(void) { + int i; + for (i = 0; i < count; i++) { + test_ecdsa_recovery_api(); + } + for (i = 0; i < 64*count; i++) { + test_ecdsa_recovery_end_to_end(); + } + test_ecdsa_recovery_edge_cases(); +} + +#endif /* SECP256K1_MODULE_RECOVERY_TESTS_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/num.h b/src/cryptoconditions/src/include/secp256k1/src/num.h new file mode 100644 index 000000000..49f2dd791 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/num.h @@ -0,0 +1,74 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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/cryptoconditions/src/include/secp256k1/src/num_gmp.h b/src/cryptoconditions/src/include/secp256k1/src/num_gmp.h new file mode 100644 index 000000000..3619844bd --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/num_gmp.h @@ -0,0 +1,20 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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/cryptoconditions/src/include/secp256k1/src/num_gmp_impl.h b/src/cryptoconditions/src/include/secp256k1/src/num_gmp_impl.h new file mode 100644 index 000000000..0ae2a8ba0 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/num_gmp_impl.h @@ -0,0 +1,288 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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/cryptoconditions/src/include/secp256k1/src/num_impl.h b/src/cryptoconditions/src/include/secp256k1/src/num_impl.h new file mode 100644 index 000000000..c45193b03 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/num_impl.h @@ -0,0 +1,24 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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/cryptoconditions/src/include/secp256k1/src/scalar.h b/src/cryptoconditions/src/include/secp256k1/src/scalar.h new file mode 100644 index 000000000..59304cb66 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/scalar.h @@ -0,0 +1,106 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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 + +#if defined(EXHAUSTIVE_TEST_ORDER) +#include "scalar_low.h" +#elif defined(USE_SCALAR_4X64) +#include "scalar_4x64.h" +#elif defined(USE_SCALAR_8X32) +#include "scalar_8x32.h" +#else +#error "Please select scalar 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 from a scalar. Not constant time. */ +static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count); + +/** Set a scalar from a big endian byte array. */ +static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *bin, int *overflow); + +/** Set a scalar to an unsigned integer. */ +static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v); + +/** Convert a scalar to a byte array. */ +static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a); + +/** Add two scalars together (modulo the group order). Returns whether it overflowed. */ +static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b); + +/** Conditionally add a power of two to a scalar. The result is not allowed to overflow. */ +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag); + +/** 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); + +/** Compute the inverse of a scalar (modulo the group order), without constant-time guarantee. */ +static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *a); + +/** Compute the complement of a scalar (modulo the group order). */ +static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a); + +/** Check whether a scalar equals zero. */ +static int secp256k1_scalar_is_zero(const secp256k1_scalar *a); + +/** Check whether a scalar equals one. */ +static int secp256k1_scalar_is_one(const secp256k1_scalar *a); + +/** Check whether a scalar, considered as an nonnegative integer, is even. */ +static int secp256k1_scalar_is_even(const secp256k1_scalar *a); + +/** Check whether a scalar is higher than the group order divided by 2. */ +static int secp256k1_scalar_is_high(const secp256k1_scalar *a); + +/** Conditionally negate a number, in constant time. + * 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 + +/** 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); + +#endif /* SECP256K1_SCALAR_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/scalar_4x64.h b/src/cryptoconditions/src/include/secp256k1/src/scalar_4x64.h new file mode 100644 index 000000000..19c7495d1 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/scalar_4x64.h @@ -0,0 +1,19 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_SCALAR_REPR_H +#define SECP256K1_SCALAR_REPR_H + +#include + +/** A scalar modulo the group order of the secp256k1 curve. */ +typedef struct { + uint64_t d[4]; +} secp256k1_scalar; + +#define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{((uint64_t)(d1)) << 32 | (d0), ((uint64_t)(d3)) << 32 | (d2), ((uint64_t)(d5)) << 32 | (d4), ((uint64_t)(d7)) << 32 | (d6)}} + +#endif /* SECP256K1_SCALAR_REPR_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/scalar_4x64_impl.h b/src/cryptoconditions/src/include/secp256k1/src/scalar_4x64_impl.h new file mode 100644 index 000000000..db1ebf94b --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/scalar_4x64_impl.h @@ -0,0 +1,949 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_SCALAR_REPR_IMPL_H +#define SECP256K1_SCALAR_REPR_IMPL_H + +/* Limbs of the secp256k1 order. */ +#define SECP256K1_N_0 ((uint64_t)0xBFD25E8CD0364141ULL) +#define SECP256K1_N_1 ((uint64_t)0xBAAEDCE6AF48A03BULL) +#define SECP256K1_N_2 ((uint64_t)0xFFFFFFFFFFFFFFFEULL) +#define SECP256K1_N_3 ((uint64_t)0xFFFFFFFFFFFFFFFFULL) + +/* Limbs of 2^256 minus the secp256k1 order. */ +#define SECP256K1_N_C_0 (~SECP256K1_N_0 + 1) +#define SECP256K1_N_C_1 (~SECP256K1_N_1) +#define SECP256K1_N_C_2 (1) + +/* Limbs of half the secp256k1 order. */ +#define SECP256K1_N_H_0 ((uint64_t)0xDFE92F46681B20A0ULL) +#define SECP256K1_N_H_1 ((uint64_t)0x5D576E7357A4501DULL) +#define SECP256K1_N_H_2 ((uint64_t)0xFFFFFFFFFFFFFFFFULL) +#define SECP256K1_N_H_3 ((uint64_t)0x7FFFFFFFFFFFFFFFULL) + +SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { + r->d[0] = 0; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; +} + +SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { + r->d[0] = v; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + VERIFY_CHECK((offset + count - 1) >> 6 == offset >> 6); + return (a->d[offset >> 6] >> (offset & 0x3F)) & ((((uint64_t)1) << count) - 1); +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + VERIFY_CHECK(count < 32); + VERIFY_CHECK(offset + count <= 256); + if ((offset + count - 1) >> 6 == offset >> 6) { + return secp256k1_scalar_get_bits(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); + } +} + +SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { + int yes = 0; + int no = 0; + no |= (a->d[3] < SECP256K1_N_3); /* No need for a > check. */ + no |= (a->d[2] < SECP256K1_N_2); + yes |= (a->d[2] > SECP256K1_N_2) & ~no; + no |= (a->d[1] < SECP256K1_N_1); + yes |= (a->d[1] > SECP256K1_N_1) & ~no; + yes |= (a->d[0] >= SECP256K1_N_0) & ~no; + return yes; +} + +SECP256K1_INLINE static int secp256k1_scalar_reduce(secp256k1_scalar *r, unsigned int overflow) { + uint128_t 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; + 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); + VERIFY_CHECK(overflow == 0 || overflow == 1); + secp256k1_scalar_reduce(r, overflow); + return overflow; +} + +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { + uint128_t t; + 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 +} + +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; + over = secp256k1_scalar_reduce(r, secp256k1_scalar_check_overflow(r)); + if (overflow) { + *overflow = over; + } +} + +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_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *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_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *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; + 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. */ + no |= (a->d[1] < SECP256K1_N_H_1) & ~yes; + yes |= (a->d[1] > SECP256K1_N_H_1) & ~no; + yes |= (a->d[0] > SECP256K1_N_H_0) & ~no; + return yes; +} + +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; + 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; + return 2 * (mask == 0) - 1; +} + +/* Inspired by the macros in OpenSSL's crypto/bn/asm/x86_64-gcc.c. */ + +/** Add a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd(a,b) { \ + uint64_t tl, th; \ + { \ + uint128_t t = (uint128_t)a * b; \ + th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \ + tl = t; \ + } \ + c0 += tl; /* overflow is handled on the next line */ \ + th += (c0 < tl) ? 1 : 0; /* 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) */ \ + VERIFY_CHECK((c1 >= th) || (c2 != 0)); \ +} + +/** Add a*b to the number defined by (c0,c1). c1 must never overflow. */ +#define muladd_fast(a,b) { \ + uint64_t tl, th; \ + { \ + uint128_t t = (uint128_t)a * b; \ + th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \ + tl = t; \ + } \ + c0 += tl; /* overflow is handled on the next line */ \ + th += (c0 < tl) ? 1 : 0; /* 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; \ + c1 += over; /* overflow is handled on the next line */ \ + c2 += (c1 < over) ? 1 : 0; /* 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) */ \ + VERIFY_CHECK((c1 != 0) | (c0 >= (a))); \ + VERIFY_CHECK(c2 == 0); \ +} + +/** Extract the lowest 64 bits of (c0,c1,c2) into n, and left shift the number 64 bits. */ +#define extract(n) { \ + (n) = c0; \ + c0 = c1; \ + c1 = c2; \ + c2 = 0; \ +} + +/** Extract the lowest 64 bits of (c0,c1,c2) into n, and left shift the number 64 bits. c2 is required to be zero. */ +#define extract_fast(n) { \ + (n) = c0; \ + c0 = c1; \ + c1 = 0; \ + VERIFY_CHECK(c2 == 0); \ +} + +static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l) { +#ifdef USE_ASM_X86_64 + /* Reduce 512 bits into 385. */ + uint64_t m0, m1, m2, m3, m4, m5, m6; + uint64_t p0, p1, p2, p3, p4; + uint64_t c; + + __asm__ __volatile__( + /* Preload. */ + "movq 32(%%rsi), %%r11\n" + "movq 40(%%rsi), %%r12\n" + "movq 48(%%rsi), %%r13\n" + "movq 56(%%rsi), %%r14\n" + /* Initialize r8,r9,r10 */ + "movq 0(%%rsi), %%r8\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" + /* (r8,r9) += n0 * c0 */ + "movq %8, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + /* extract m0 */ + "movq %%r8, %q0\n" + "xorq %%r8, %%r8\n" + /* (r9,r10) += l1 */ + "addq 8(%%rsi), %%r9\n" + "adcq $0, %%r10\n" + /* (r9,r10,r8) += n1 * c0 */ + "movq %8, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += n0 * c1 */ + "movq %9, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* extract m1 */ + "movq %%r9, %q1\n" + "xorq %%r9, %%r9\n" + /* (r10,r8,r9) += l2 */ + "addq 16(%%rsi), %%r10\n" + "adcq $0, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += n2 * c0 */ + "movq %8, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += n1 * c1 */ + "movq %9, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += n0 */ + "addq %%r11, %%r10\n" + "adcq $0, %%r8\n" + "adcq $0, %%r9\n" + /* extract m2 */ + "movq %%r10, %q2\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += l3 */ + "addq 24(%%rsi), %%r8\n" + "adcq $0, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += n3 * c0 */ + "movq %8, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += n2 * c1 */ + "movq %9, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += n1 */ + "addq %%r12, %%r8\n" + "adcq $0, %%r9\n" + "adcq $0, %%r10\n" + /* extract m3 */ + "movq %%r8, %q3\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += n3 * c1 */ + "movq %9, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += n2 */ + "addq %%r13, %%r9\n" + "adcq $0, %%r10\n" + "adcq $0, %%r8\n" + /* extract m4 */ + "movq %%r9, %q4\n" + /* (r10,r8) += n3 */ + "addq %%r14, %%r10\n" + "adcq $0, %%r8\n" + /* extract m5 */ + "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) + : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc"); + + /* Reduce 385 bits into 258. */ + __asm__ __volatile__( + /* Preload */ + "movq %q9, %%r11\n" + "movq %q10, %%r12\n" + "movq %q11, %%r13\n" + /* Initialize (r8,r9,r10) */ + "movq %q5, %%r8\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" + /* (r8,r9) += m4 * c0 */ + "movq %12, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + /* extract p0 */ + "movq %%r8, %q0\n" + "xorq %%r8, %%r8\n" + /* (r9,r10) += m1 */ + "addq %q6, %%r9\n" + "adcq $0, %%r10\n" + /* (r9,r10,r8) += m5 * c0 */ + "movq %12, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += m4 * c1 */ + "movq %13, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* extract p1 */ + "movq %%r9, %q1\n" + "xorq %%r9, %%r9\n" + /* (r10,r8,r9) += m2 */ + "addq %q7, %%r10\n" + "adcq $0, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += m6 * c0 */ + "movq %12, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += m5 * c1 */ + "movq %13, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += m4 */ + "addq %%r11, %%r10\n" + "adcq $0, %%r8\n" + "adcq $0, %%r9\n" + /* extract p2 */ + "movq %%r10, %q2\n" + /* (r8,r9) += m3 */ + "addq %q8, %%r8\n" + "adcq $0, %%r9\n" + /* (r8,r9) += m6 * c1 */ + "movq %13, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + /* (r8,r9) += m5 */ + "addq %%r12, %%r8\n" + "adcq $0, %%r9\n" + /* extract p3 */ + "movq %%r8, %q3\n" + /* (r9) += m6 */ + "addq %%r13, %%r9\n" + /* 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) + : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "cc"); + + /* Reduce 258 bits into 256. */ + __asm__ __volatile__( + /* Preload */ + "movq %q5, %%r10\n" + /* (rax,rdx) = p4 * c0 */ + "movq %7, %%rax\n" + "mulq %%r10\n" + /* (rax,rdx) += p0 */ + "addq %q1, %%rax\n" + "adcq $0, %%rdx\n" + /* extract r0 */ + "movq %%rax, 0(%q6)\n" + /* Move to (r8,r9) */ + "movq %%rdx, %%r8\n" + "xorq %%r9, %%r9\n" + /* (r8,r9) += p1 */ + "addq %q2, %%r8\n" + "adcq $0, %%r9\n" + /* (r8,r9) += p4 * c1 */ + "movq %8, %%rax\n" + "mulq %%r10\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + /* Extract r1 */ + "movq %%r8, 8(%q6)\n" + "xorq %%r8, %%r8\n" + /* (r9,r8) += p4 */ + "addq %%r10, %%r9\n" + "adcq $0, %%r8\n" + /* (r9,r8) += p2 */ + "addq %q3, %%r9\n" + "adcq $0, %%r8\n" + /* Extract r2 */ + "movq %%r9, 16(%q6)\n" + "xorq %%r9, %%r9\n" + /* (r8,r9) += p3 */ + "addq %q4, %%r8\n" + "adcq $0, %%r9\n" + /* Extract r3 */ + "movq %%r8, 24(%q6)\n" + /* 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) + : "rax", "rdx", "r8", "r9", "r10", "cc", "memory"); +#else + uint128_t c; + uint64_t 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; + uint64_t p0, p1, p2, p3; + uint32_t p4; + + /* Reduce 512 bits into 385. */ + /* m[0..6] = l[0..3] + n[0..3] * SECP256K1_N_C. */ + c0 = l[0]; c1 = 0; c2 = 0; + muladd_fast(n0, SECP256K1_N_C_0); + extract_fast(m0); + sumadd_fast(l[1]); + muladd(n1, SECP256K1_N_C_0); + muladd(n0, SECP256K1_N_C_1); + extract(m1); + sumadd(l[2]); + muladd(n2, SECP256K1_N_C_0); + muladd(n1, SECP256K1_N_C_1); + sumadd(n0); + extract(m2); + sumadd(l[3]); + muladd(n3, SECP256K1_N_C_0); + muladd(n2, SECP256K1_N_C_1); + sumadd(n1); + extract(m3); + muladd(n3, SECP256K1_N_C_1); + sumadd(n2); + extract(m4); + sumadd_fast(n3); + extract_fast(m5); + VERIFY_CHECK(c0 <= 1); + m6 = c0; + + /* Reduce 385 bits into 258. */ + /* p[0..4] = m[0..3] + m[4..6] * SECP256K1_N_C. */ + c0 = m0; c1 = 0; c2 = 0; + muladd_fast(m4, SECP256K1_N_C_0); + extract_fast(p0); + sumadd_fast(m1); + muladd(m5, SECP256K1_N_C_0); + muladd(m4, SECP256K1_N_C_1); + extract(p1); + sumadd(m2); + muladd(m6, SECP256K1_N_C_0); + muladd(m5, SECP256K1_N_C_1); + sumadd(m4); + extract(p2); + sumadd_fast(m3); + muladd_fast(m6, SECP256K1_N_C_1); + sumadd_fast(m5); + extract_fast(p3); + p4 = c0 + m6; + VERIFY_CHECK(p4 <= 2); + + /* 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; +#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) { +#ifdef USE_ASM_X86_64 + const uint64_t *pb = b->d; + __asm__ __volatile__( + /* Preload */ + "movq 0(%%rdi), %%r15\n" + "movq 8(%%rdi), %%rbx\n" + "movq 16(%%rdi), %%rcx\n" + "movq 0(%%rdx), %%r11\n" + "movq 8(%%rdx), %%r12\n" + "movq 16(%%rdx), %%r13\n" + "movq 24(%%rdx), %%r14\n" + /* (rax,rdx) = a0 * b0 */ + "movq %%r15, %%rax\n" + "mulq %%r11\n" + /* Extract l0 */ + "movq %%rax, 0(%%rsi)\n" + /* (r8,r9,r10) = (rdx) */ + "movq %%rdx, %%r8\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += a0 * b1 */ + "movq %%r15, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += a1 * b0 */ + "movq %%rbx, %%rax\n" + "mulq %%r11\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) += a0 * b2 */ + "movq %%r15, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += a1 * b1 */ + "movq %%rbx, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += a2 * b0 */ + "movq %%rcx, %%rax\n" + "mulq %%r11\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) += a0 * b3 */ + "movq %%r15, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* Preload a3 */ + "movq 24(%%rdi), %%r15\n" + /* (r10,r8,r9) += a1 * b2 */ + "movq %%rbx, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += a2 * b1 */ + "movq %%rcx, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += a3 * b0 */ + "movq %%r15, %%rax\n" + "mulq %%r11\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) += a1 * b3 */ + "movq %%rbx, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += a2 * b2 */ + "movq %%rcx, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += a3 * b1 */ + "movq %%r15, %%rax\n" + "mulq %%r12\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) += a2 * b3 */ + "movq %%rcx, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += a3 * b2 */ + "movq %%r15, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l5 */ + "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 */ + "movq %%r10, 48(%%rsi)\n" + /* Extract l7 */ + "movq %%r8, 56(%%rsi)\n" + : "+d"(pb) + : "S"(l), "D"(a->d) + : "rax", "rbx", "rcx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "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], b->d[0]); + extract_fast(l[0]); + muladd(a->d[0], b->d[1]); + muladd(a->d[1], b->d[0]); + extract(l[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]); + 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]); + muladd(a->d[1], b->d[3]); + muladd(a->d[2], b->d[2]); + muladd(a->d[3], b->d[1]); + extract(l[4]); + muladd(a->d[2], b->d[3]); + muladd(a->d[3], b->d[2]); + extract(l[5]); + muladd_fast(a->d[3], b->d[3]); + extract_fast(l[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; +#endif +} + +#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) { + uint64_t l[8]; + 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; +} + +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); +} + +#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] = 0; + r1->d[3] = 0; + r2->d[0] = a->d[2]; + r2->d[1] = a->d[3]; + r2->d[2] = 0; + r2->d[3] = 0; +} +#endif + +SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *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; +} + +SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift) { + uint64_t l[8]; + unsigned int shiftlimbs; + unsigned int shiftlow; + unsigned int shifthigh; + VERIFY_CHECK(shift >= 256); + secp256k1_scalar_mul_512(l, a, b); + shiftlimbs = shift >> 6; + shiftlow = shift & 0x3F; + shifthigh = 64 - shiftlow; + r->d[0] = shift < 512 ? (l[0 + shiftlimbs] >> shiftlow | (shift < 448 && shiftlow ? (l[1 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[1] = shift < 448 ? (l[1 + shiftlimbs] >> shiftlow | (shift < 384 && shiftlow ? (l[2 + shiftlimbs] << shifthigh) : 0)) : 0; + 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); +} + +#endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/scalar_8x32.h b/src/cryptoconditions/src/include/secp256k1/src/scalar_8x32.h new file mode 100644 index 000000000..2c9a348e2 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/scalar_8x32.h @@ -0,0 +1,19 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_SCALAR_REPR_H +#define SECP256K1_SCALAR_REPR_H + +#include + +/** A scalar modulo the group order of the secp256k1 curve. */ +typedef struct { + uint32_t d[8]; +} secp256k1_scalar; + +#define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{(d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7)}} + +#endif /* SECP256K1_SCALAR_REPR_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/scalar_8x32_impl.h b/src/cryptoconditions/src/include/secp256k1/src/scalar_8x32_impl.h new file mode 100644 index 000000000..4f9ed61fe --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/scalar_8x32_impl.h @@ -0,0 +1,721 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_SCALAR_REPR_IMPL_H +#define SECP256K1_SCALAR_REPR_IMPL_H + +/* Limbs of the secp256k1 order. */ +#define SECP256K1_N_0 ((uint32_t)0xD0364141UL) +#define SECP256K1_N_1 ((uint32_t)0xBFD25E8CUL) +#define SECP256K1_N_2 ((uint32_t)0xAF48A03BUL) +#define SECP256K1_N_3 ((uint32_t)0xBAAEDCE6UL) +#define SECP256K1_N_4 ((uint32_t)0xFFFFFFFEUL) +#define SECP256K1_N_5 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_6 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_7 ((uint32_t)0xFFFFFFFFUL) + +/* Limbs of 2^256 minus the secp256k1 order. */ +#define SECP256K1_N_C_0 (~SECP256K1_N_0 + 1) +#define SECP256K1_N_C_1 (~SECP256K1_N_1) +#define SECP256K1_N_C_2 (~SECP256K1_N_2) +#define SECP256K1_N_C_3 (~SECP256K1_N_3) +#define SECP256K1_N_C_4 (1) + +/* Limbs of half the secp256k1 order. */ +#define SECP256K1_N_H_0 ((uint32_t)0x681B20A0UL) +#define SECP256K1_N_H_1 ((uint32_t)0xDFE92F46UL) +#define SECP256K1_N_H_2 ((uint32_t)0x57A4501DUL) +#define SECP256K1_N_H_3 ((uint32_t)0x5D576E73UL) +#define SECP256K1_N_H_4 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_H_5 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_H_6 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_H_7 ((uint32_t)0x7FFFFFFFUL) + +SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { + r->d[0] = 0; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; + r->d[4] = 0; + r->d[5] = 0; + r->d[6] = 0; + r->d[7] = 0; +} + +SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { + r->d[0] = v; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; + r->d[4] = 0; + r->d[5] = 0; + r->d[6] = 0; + r->d[7] = 0; +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + VERIFY_CHECK((offset + count - 1) >> 5 == offset >> 5); + return (a->d[offset >> 5] >> (offset & 0x1F)) & ((1 << count) - 1); +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + VERIFY_CHECK(count < 32); + VERIFY_CHECK(offset + count <= 256); + if ((offset + count - 1) >> 5 == offset >> 5) { + return secp256k1_scalar_get_bits(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); + } +} + +SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { + int yes = 0; + int no = 0; + no |= (a->d[7] < SECP256K1_N_7); /* No need for a > check. */ + no |= (a->d[6] < SECP256K1_N_6); /* No need for a > check. */ + no |= (a->d[5] < SECP256K1_N_5); /* No need for a > check. */ + no |= (a->d[4] < SECP256K1_N_4); + yes |= (a->d[4] > SECP256K1_N_4) & ~no; + no |= (a->d[3] < SECP256K1_N_3) & ~yes; + yes |= (a->d[3] > SECP256K1_N_3) & ~no; + no |= (a->d[2] < SECP256K1_N_2) & ~yes; + yes |= (a->d[2] > SECP256K1_N_2) & ~no; + no |= (a->d[1] < SECP256K1_N_1) & ~yes; + yes |= (a->d[1] > SECP256K1_N_1) & ~no; + yes |= (a->d[0] >= SECP256K1_N_0) & ~no; + return yes; +} + +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; + r->d[1] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[2] + overflow * SECP256K1_N_C_2; + r->d[2] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[3] + overflow * SECP256K1_N_C_3; + r->d[3] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[4] + overflow * SECP256K1_N_C_4; + r->d[4] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[5]; + r->d[5] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[6]; + r->d[6] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[7]; + r->d[7] = t & 0xFFFFFFFFUL; + 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]; + r->d[0] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[1] + b->d[1]; + r->d[1] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[2] + b->d[2]; + r->d[2] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[3] + b->d[3]; + r->d[3] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[4] + b->d[4]; + r->d[4] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[5] + b->d[5]; + r->d[5] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[6] + b->d[6]; + r->d[6] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[7] + b->d[7]; + r->d[7] = t & 0xFFFFFFFFULL; t >>= 32; + overflow = t + secp256k1_scalar_check_overflow(r); + VERIFY_CHECK(overflow == 0 || overflow == 1); + secp256k1_scalar_reduce(r, overflow); + return overflow; +} + +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { + uint64_t t; + VERIFY_CHECK(bit < 256); + bit += ((uint32_t) flag - 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)); + r->d[1] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[2] + (((uint32_t)((bit >> 5) == 2)) << (bit & 0x1F)); + r->d[2] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[3] + (((uint32_t)((bit >> 5) == 3)) << (bit & 0x1F)); + r->d[3] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[4] + (((uint32_t)((bit >> 5) == 4)) << (bit & 0x1F)); + r->d[4] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[5] + (((uint32_t)((bit >> 5) == 5)) << (bit & 0x1F)); + r->d[5] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[6] + (((uint32_t)((bit >> 5) == 6)) << (bit & 0x1F)); + 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 + 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; + over = secp256k1_scalar_reduce(r, secp256k1_scalar_check_overflow(r)); + if (overflow) { + *overflow = over; + } +} + +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_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *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; + r->d[0] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[1]) + SECP256K1_N_1; + r->d[1] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[2]) + SECP256K1_N_2; + r->d[2] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[3]) + SECP256K1_N_3; + r->d[3] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[4]) + SECP256K1_N_4; + r->d[4] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[5]) + SECP256K1_N_5; + r->d[5] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[6]) + SECP256K1_N_6; + r->d[6] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[7]) + SECP256K1_N_7; + r->d[7] = t & nonzero; +} + +SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *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; + 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. */ + no |= (a->d[5] < SECP256K1_N_H_5) & ~yes; /* No need for a > check. */ + no |= (a->d[4] < SECP256K1_N_H_4) & ~yes; /* No need for a > check. */ + no |= (a->d[3] < SECP256K1_N_H_3) & ~yes; + yes |= (a->d[3] > SECP256K1_N_H_3) & ~no; + no |= (a->d[2] < SECP256K1_N_H_2) & ~yes; + yes |= (a->d[2] > SECP256K1_N_H_2) & ~no; + no |= (a->d[1] < SECP256K1_N_H_1) & ~yes; + yes |= (a->d[1] > SECP256K1_N_H_1) & ~no; + yes |= (a->d[0] > SECP256K1_N_H_0) & ~no; + return yes; +} + +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; + uint32_t nonzero = 0xFFFFFFFFUL * (secp256k1_scalar_is_zero(r) == 0); + uint64_t t = (uint64_t)(r->d[0] ^ mask) + ((SECP256K1_N_0 + 1) & mask); + 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; + t += (uint64_t)(r->d[2] ^ mask) + (SECP256K1_N_2 & mask); + r->d[2] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[3] ^ mask) + (SECP256K1_N_3 & mask); + r->d[3] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[4] ^ mask) + (SECP256K1_N_4 & mask); + r->d[4] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[5] ^ mask) + (SECP256K1_N_5 & mask); + r->d[5] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[6] ^ mask) + (SECP256K1_N_6 & mask); + r->d[6] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[7] ^ mask) + (SECP256K1_N_7 & mask); + r->d[7] = t & nonzero; + return 2 * (mask == 0) - 1; +} + + +/* Inspired by the macros in OpenSSL's crypto/bn/asm/x86_64-gcc.c. */ + +/** Add a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd(a,b) { \ + uint32_t tl, th; \ + { \ + uint64_t t = (uint64_t)a * b; \ + th = t >> 32; /* at most 0xFFFFFFFE */ \ + tl = t; \ + } \ + c0 += tl; /* overflow is handled on the next line */ \ + th += (c0 < tl) ? 1 : 0; /* 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) */ \ + VERIFY_CHECK((c1 >= th) || (c2 != 0)); \ +} + +/** Add a*b to the number defined by (c0,c1). c1 must never overflow. */ +#define muladd_fast(a,b) { \ + uint32_t tl, th; \ + { \ + uint64_t t = (uint64_t)a * b; \ + th = t >> 32; /* at most 0xFFFFFFFE */ \ + tl = t; \ + } \ + c0 += tl; /* overflow is handled on the next line */ \ + th += (c0 < tl) ? 1 : 0; /* 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; \ + c1 += over; /* overflow is handled on the next line */ \ + c2 += (c1 < over) ? 1 : 0; /* 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) */ \ + VERIFY_CHECK((c1 != 0) | (c0 >= (a))); \ + VERIFY_CHECK(c2 == 0); \ +} + +/** Extract the lowest 32 bits of (c0,c1,c2) into n, and left shift the number 32 bits. */ +#define extract(n) { \ + (n) = c0; \ + c0 = c1; \ + c1 = c2; \ + c2 = 0; \ +} + +/** Extract the lowest 32 bits of (c0,c1,c2) into n, and left shift the number 32 bits. c2 is required to be zero. */ +#define extract_fast(n) { \ + (n) = c0; \ + c0 = c1; \ + c1 = 0; \ + VERIFY_CHECK(c2 == 0); \ +} + +static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint32_t *l) { + uint64_t c; + uint32_t n0 = l[8], n1 = l[9], n2 = l[10], n3 = l[11], n4 = l[12], n5 = l[13], n6 = l[14], n7 = l[15]; + uint32_t m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12; + uint32_t p0, p1, p2, p3, p4, p5, p6, p7, p8; + + /* 96 bit accumulator. */ + uint32_t c0, c1, c2; + + /* Reduce 512 bits into 385. */ + /* m[0..12] = l[0..7] + n[0..7] * SECP256K1_N_C. */ + c0 = l[0]; c1 = 0; c2 = 0; + muladd_fast(n0, SECP256K1_N_C_0); + extract_fast(m0); + sumadd_fast(l[1]); + muladd(n1, SECP256K1_N_C_0); + muladd(n0, SECP256K1_N_C_1); + extract(m1); + sumadd(l[2]); + muladd(n2, SECP256K1_N_C_0); + muladd(n1, SECP256K1_N_C_1); + muladd(n0, SECP256K1_N_C_2); + extract(m2); + sumadd(l[3]); + muladd(n3, SECP256K1_N_C_0); + muladd(n2, SECP256K1_N_C_1); + muladd(n1, SECP256K1_N_C_2); + muladd(n0, SECP256K1_N_C_3); + extract(m3); + sumadd(l[4]); + muladd(n4, SECP256K1_N_C_0); + muladd(n3, SECP256K1_N_C_1); + muladd(n2, SECP256K1_N_C_2); + muladd(n1, SECP256K1_N_C_3); + sumadd(n0); + extract(m4); + sumadd(l[5]); + muladd(n5, SECP256K1_N_C_0); + muladd(n4, SECP256K1_N_C_1); + muladd(n3, SECP256K1_N_C_2); + muladd(n2, SECP256K1_N_C_3); + sumadd(n1); + extract(m5); + sumadd(l[6]); + muladd(n6, SECP256K1_N_C_0); + muladd(n5, SECP256K1_N_C_1); + muladd(n4, SECP256K1_N_C_2); + muladd(n3, SECP256K1_N_C_3); + sumadd(n2); + extract(m6); + sumadd(l[7]); + muladd(n7, SECP256K1_N_C_0); + muladd(n6, SECP256K1_N_C_1); + muladd(n5, SECP256K1_N_C_2); + muladd(n4, SECP256K1_N_C_3); + sumadd(n3); + extract(m7); + muladd(n7, SECP256K1_N_C_1); + muladd(n6, SECP256K1_N_C_2); + muladd(n5, SECP256K1_N_C_3); + sumadd(n4); + extract(m8); + muladd(n7, SECP256K1_N_C_2); + muladd(n6, SECP256K1_N_C_3); + sumadd(n5); + extract(m9); + muladd(n7, SECP256K1_N_C_3); + sumadd(n6); + extract(m10); + sumadd_fast(n7); + extract_fast(m11); + VERIFY_CHECK(c0 <= 1); + m12 = c0; + + /* Reduce 385 bits into 258. */ + /* p[0..8] = m[0..7] + m[8..12] * SECP256K1_N_C. */ + c0 = m0; c1 = 0; c2 = 0; + muladd_fast(m8, SECP256K1_N_C_0); + extract_fast(p0); + sumadd_fast(m1); + muladd(m9, SECP256K1_N_C_0); + muladd(m8, SECP256K1_N_C_1); + extract(p1); + sumadd(m2); + muladd(m10, SECP256K1_N_C_0); + muladd(m9, SECP256K1_N_C_1); + muladd(m8, SECP256K1_N_C_2); + extract(p2); + sumadd(m3); + muladd(m11, SECP256K1_N_C_0); + muladd(m10, SECP256K1_N_C_1); + muladd(m9, SECP256K1_N_C_2); + muladd(m8, SECP256K1_N_C_3); + extract(p3); + sumadd(m4); + muladd(m12, SECP256K1_N_C_0); + muladd(m11, SECP256K1_N_C_1); + muladd(m10, SECP256K1_N_C_2); + muladd(m9, SECP256K1_N_C_3); + sumadd(m8); + extract(p4); + sumadd(m5); + muladd(m12, SECP256K1_N_C_1); + muladd(m11, SECP256K1_N_C_2); + muladd(m10, SECP256K1_N_C_3); + sumadd(m9); + extract(p5); + sumadd(m6); + muladd(m12, SECP256K1_N_C_2); + muladd(m11, SECP256K1_N_C_3); + sumadd(m10); + extract(p6); + sumadd_fast(m7); + muladd_fast(m12, SECP256K1_N_C_3); + sumadd_fast(m11); + extract_fast(p7); + p8 = c0 + m12; + VERIFY_CHECK(p8 <= 2); + + /* Reduce 258 bits into 256. */ + /* r[0..7] = p[0..7] + p[8] * SECP256K1_N_C. */ + c = p0 + (uint64_t)SECP256K1_N_C_0 * p8; + r->d[0] = c & 0xFFFFFFFFUL; c >>= 32; + c += p1 + (uint64_t)SECP256K1_N_C_1 * p8; + r->d[1] = c & 0xFFFFFFFFUL; c >>= 32; + c += p2 + (uint64_t)SECP256K1_N_C_2 * p8; + r->d[2] = c & 0xFFFFFFFFUL; c >>= 32; + c += p3 + (uint64_t)SECP256K1_N_C_3 * p8; + r->d[3] = c & 0xFFFFFFFFUL; c >>= 32; + c += p4 + (uint64_t)p8; + r->d[4] = c & 0xFFFFFFFFUL; c >>= 32; + c += p5; + r->d[5] = c & 0xFFFFFFFFUL; c >>= 32; + c += p6; + r->d[6] = c & 0xFFFFFFFFUL; c >>= 32; + c += p7; + r->d[7] = c & 0xFFFFFFFFUL; c >>= 32; + + /* Final reduction of r. */ + secp256k1_scalar_reduce(r, c + secp256k1_scalar_check_overflow(r)); +} + +static void secp256k1_scalar_mul_512(uint32_t *l, const secp256k1_scalar *a, const secp256k1_scalar *b) { + /* 96 bit accumulator. */ + uint32_t c0 = 0, c1 = 0, c2 = 0; + + /* l[0..15] = a[0..7] * b[0..7]. */ + muladd_fast(a->d[0], b->d[0]); + extract_fast(l[0]); + muladd(a->d[0], b->d[1]); + muladd(a->d[1], b->d[0]); + extract(l[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]); + 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]); + muladd(a->d[0], b->d[4]); + muladd(a->d[1], b->d[3]); + muladd(a->d[2], b->d[2]); + muladd(a->d[3], b->d[1]); + muladd(a->d[4], b->d[0]); + extract(l[4]); + muladd(a->d[0], b->d[5]); + muladd(a->d[1], b->d[4]); + muladd(a->d[2], b->d[3]); + muladd(a->d[3], b->d[2]); + muladd(a->d[4], b->d[1]); + muladd(a->d[5], b->d[0]); + extract(l[5]); + muladd(a->d[0], b->d[6]); + muladd(a->d[1], b->d[5]); + muladd(a->d[2], b->d[4]); + muladd(a->d[3], b->d[3]); + muladd(a->d[4], b->d[2]); + muladd(a->d[5], b->d[1]); + muladd(a->d[6], b->d[0]); + extract(l[6]); + muladd(a->d[0], b->d[7]); + muladd(a->d[1], b->d[6]); + muladd(a->d[2], b->d[5]); + muladd(a->d[3], b->d[4]); + muladd(a->d[4], b->d[3]); + muladd(a->d[5], b->d[2]); + muladd(a->d[6], b->d[1]); + muladd(a->d[7], b->d[0]); + extract(l[7]); + muladd(a->d[1], b->d[7]); + muladd(a->d[2], b->d[6]); + muladd(a->d[3], b->d[5]); + muladd(a->d[4], b->d[4]); + muladd(a->d[5], b->d[3]); + muladd(a->d[6], b->d[2]); + muladd(a->d[7], b->d[1]); + extract(l[8]); + muladd(a->d[2], b->d[7]); + muladd(a->d[3], b->d[6]); + muladd(a->d[4], b->d[5]); + muladd(a->d[5], b->d[4]); + muladd(a->d[6], b->d[3]); + muladd(a->d[7], b->d[2]); + extract(l[9]); + muladd(a->d[3], b->d[7]); + muladd(a->d[4], b->d[6]); + muladd(a->d[5], b->d[5]); + muladd(a->d[6], b->d[4]); + muladd(a->d[7], b->d[3]); + extract(l[10]); + muladd(a->d[4], b->d[7]); + muladd(a->d[5], b->d[6]); + muladd(a->d[6], b->d[5]); + muladd(a->d[7], b->d[4]); + extract(l[11]); + muladd(a->d[5], b->d[7]); + muladd(a->d[6], b->d[6]); + muladd(a->d[7], b->d[5]); + extract(l[12]); + muladd(a->d[6], b->d[7]); + muladd(a->d[7], b->d[6]); + extract(l[13]); + muladd_fast(a->d[7], b->d[7]); + extract_fast(l[14]); + VERIFY_CHECK(c1 == 0); + 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_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; +} + +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); +} + +#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[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[4] = 0; + r2->d[5] = 0; + r2->d[6] = 0; + r2->d[7] = 0; +} +#endif + +SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *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; +} + +SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift) { + uint32_t l[16]; + unsigned int shiftlimbs; + unsigned int shiftlow; + unsigned int shifthigh; + VERIFY_CHECK(shift >= 256); + secp256k1_scalar_mul_512(l, a, b); + shiftlimbs = shift >> 5; + shiftlow = shift & 0x1F; + shifthigh = 32 - shiftlow; + r->d[0] = shift < 512 ? (l[0 + shiftlimbs] >> shiftlow | (shift < 480 && shiftlow ? (l[1 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[1] = shift < 480 ? (l[1 + shiftlimbs] >> shiftlow | (shift < 448 && shiftlow ? (l[2 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[2] = shift < 448 ? (l[2 + shiftlimbs] >> shiftlow | (shift < 416 && shiftlow ? (l[3 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[3] = shift < 416 ? (l[3 + shiftlimbs] >> shiftlow | (shift < 384 && shiftlow ? (l[4 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[4] = shift < 384 ? (l[4 + shiftlimbs] >> shiftlow | (shift < 352 && shiftlow ? (l[5 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[5] = shift < 352 ? (l[5 + shiftlimbs] >> shiftlow | (shift < 320 && shiftlow ? (l[6 + shiftlimbs] << shifthigh) : 0)) : 0; + 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); +} + +#endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/scalar_impl.h b/src/cryptoconditions/src/include/secp256k1/src/scalar_impl.h new file mode 100644 index 000000000..fa790570f --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/scalar_impl.h @@ -0,0 +1,333 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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" +#endif + +#if defined(EXHAUSTIVE_TEST_ORDER) +#include "scalar_low_impl.h" +#elif defined(USE_SCALAR_4X64) +#include "scalar_4x64_impl.h" +#elif defined(USE_SCALAR_8X32) +#include "scalar_8x32_impl.h" +#else +#error "Please select scalar 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); + + secp256k1_scalar_sqr(&x8, &x6); + secp256k1_scalar_sqr(&x8, &x8); + secp256k1_scalar_mul(&x8, &x8, &x2); + + 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_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) { + return !(a->d[0] & 1); +} +#endif + +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 +} + +#ifdef USE_ENDOMORPHISM +#if defined(EXHAUSTIVE_TEST_ORDER) +/** + * 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 + * nontrivial to get full test coverage for the exhaustive tests. We therefore + * (arbitrarily) set k2 = k + 5 and k1 = k - k2 * lambda. + */ +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; +} +#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} + * + * "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: + * + * - 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 + * 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. + * + * g1, g2 are precomputed constants used to replace division with a rounded multiplication + * when decomposing the scalar for an endomorphism-based point multiplication. + * + * The possibility of using precomputed estimates is mentioned in "Guide to Elliptic Curve + * Cryptography" (Hankerson, Menezes, Vanstone) in section 3.5. + * + * The derivation is described in the paper "Efficient Software Implementation of Public-Key + * 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) + * + * (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 a in r1 and r2, such that r1 + lambda * r2 == a (mod order). + */ + +static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + 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 + ); + static const secp256k1_scalar minus_b2 = SECP256K1_SCALAR_CONST( + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, + 0x8A280AC5UL, 0x0774346DUL, 0xD765CDA8UL, 0x3DB1562CUL + ); + static const secp256k1_scalar g1 = SECP256K1_SCALAR_CONST( + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00003086UL, + 0xD221A7D4UL, 0x6BCDE86CUL, 0x90E49284UL, 0xEB153DABUL + ); + static const secp256k1_scalar g2 = SECP256K1_SCALAR_CONST( + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x0000E443UL, + 0x7ED6010EUL, 0x88286F54UL, 0x7FA90ABFUL, 0xE4C42212UL + ); + VERIFY_CHECK(r1 != a); + VERIFY_CHECK(r2 != a); + /* 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(&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 +#endif + +#endif /* SECP256K1_SCALAR_IMPL_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/scalar_low.h b/src/cryptoconditions/src/include/secp256k1/src/scalar_low.h new file mode 100644 index 000000000..5836febc5 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/scalar_low.h @@ -0,0 +1,15 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_SCALAR_REPR_H +#define SECP256K1_SCALAR_REPR_H + +#include + +/** A scalar modulo the group order of the secp256k1 curve. */ +typedef uint32_t secp256k1_scalar; + +#endif /* SECP256K1_SCALAR_REPR_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/scalar_low_impl.h b/src/cryptoconditions/src/include/secp256k1/src/scalar_low_impl.h new file mode 100644 index 000000000..c80e70c5a --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/scalar_low_impl.h @@ -0,0 +1,114 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_SCALAR_REPR_IMPL_H +#define SECP256K1_SCALAR_REPR_IMPL_H + +#include "scalar.h" + +#include + +SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *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 + 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 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) { + *r = (*a + *b) % EXHAUSTIVE_TEST_ORDER; + return *r < *b; +} + +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { + if (flag && bit < 32) + *r += (1 << bit); +#ifdef VERIFY + VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); +#endif +} + +static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) { + const int base = 0x100 % EXHAUSTIVE_TEST_ORDER; + int i; + *r = 0; + for (i = 0; i < 32; i++) { + *r = ((*r * base) + b32[i]) % EXHAUSTIVE_TEST_ORDER; + } + /* just deny overflow, it basically always happens */ + if (overflow) *overflow = 0; +} + +static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* 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) { + return *a == 0; +} + +static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) { + if (*a == 0) { + *r = 0; + } else { + *r = EXHAUSTIVE_TEST_ORDER - *a; + } +} + +SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) { + return *a == 1; +} + +static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { + return *a > EXHAUSTIVE_TEST_ORDER / 2; +} + +static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { + if (flag) secp256k1_scalar_negate(r, 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; +} + +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; +} + +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + *r = (*a * *a) % EXHAUSTIVE_TEST_ORDER; +} + +static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + *r1 = *a; + *r2 = 0; +} + +SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) { + return *a == *b; +} + +#endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/secp256k1.c b/src/cryptoconditions/src/include/secp256k1/src/secp256k1.c new file mode 100644 index 000000000..cecb1550b --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/secp256k1.c @@ -0,0 +1,584 @@ +/********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include "include/secp256k1.h" + +#include "util.h" +#include "num_impl.h" +#include "field_impl.h" +#include "scalar_impl.h" +#include "group_impl.h" +#include "ecmult_impl.h" +#include "ecmult_const_impl.h" +#include "ecmult_gen_impl.h" +#include "ecdsa_impl.h" +#include "eckey_impl.h" +#include "hash_impl.h" + +#define ARG_CHECK(cond) do { \ + if (EXPECT(!(cond), 0)) { \ + secp256k1_callback_call(&ctx->illegal_callback, #cond); \ + return 0; \ + } \ +} 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 +}; + + +struct secp256k1_context_struct { + secp256k1_ecmult_context ecmult_ctx; + secp256k1_ecmult_gen_context ecmult_gen_ctx; + secp256k1_callback illegal_callback; + secp256k1_callback error_callback; +}; + +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; + + if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) { + secp256k1_callback_call(&ret->illegal_callback, + "Invalid flags"); + free(ret); + return NULL; + } + + secp256k1_ecmult_context_init(&ret->ecmult_ctx); + secp256k1_ecmult_gen_context_init(&ret->ecmult_gen_ctx); + + if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) { + secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx, &ret->error_callback); + } + if (flags & SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) { + secp256k1_ecmult_context_build(&ret->ecmult_ctx, &ret->error_callback); + } + + 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); + return ret; +} + +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); + + free(ctx); + } +} + +void secp256k1_context_set_illegal_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) { + if (fun == NULL) { + fun = 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) { + if (fun == NULL) { + fun = default_error_callback_fn; + } + ctx->error_callback.fn = fun; + ctx->error_callback.data = data; +} + +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); + } + 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); + } +} + +int secp256k1_ec_pubkey_parse(const secp256k1_context* ctx, secp256k1_pubkey* pubkey, const unsigned char *input, size_t inputlen) { + secp256k1_ge Q; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + memset(pubkey, 0, sizeof(*pubkey)); + ARG_CHECK(input != NULL); + if (!secp256k1_eckey_pubkey_parse(&Q, input, inputlen)) { + return 0; + } + secp256k1_pubkey_save(pubkey, &Q); + secp256k1_ge_clear(&Q); + return 1; +} + +int secp256k1_ec_pubkey_serialize(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_pubkey* pubkey, unsigned int flags) { + secp256k1_ge Q; + size_t len; + int ret = 0; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(outputlen != NULL); + ARG_CHECK(*outputlen >= ((flags & SECP256K1_FLAGS_BIT_COMPRESSION) ? 33 : 65)); + len = *outputlen; + *outputlen = 0; + ARG_CHECK(output != NULL); + memset(output, 0, len); + ARG_CHECK(pubkey != NULL); + ARG_CHECK((flags & SECP256K1_FLAGS_TYPE_MASK) == SECP256K1_FLAGS_TYPE_COMPRESSION); + if (secp256k1_pubkey_load(ctx, &Q, pubkey)) { + ret = secp256k1_eckey_pubkey_serialize(&Q, output, &len, flags & SECP256K1_FLAGS_BIT_COMPRESSION); + if (ret) { + *outputlen = len; + } + } + return ret; +} + +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) { + /* When the secp256k1_scalar type is exactly 32 byte, use its + * representation inside secp256k1_ecdsa_signature, as conversion is very fast. + * Note that secp256k1_ecdsa_signature_save must use the same representation. */ + memcpy(r, &sig->data[0], 32); + memcpy(s, &sig->data[32], 32); + } else { + secp256k1_scalar_set_b32(r, &sig->data[0], NULL); + secp256k1_scalar_set_b32(s, &sig->data[32], NULL); + } +} + +static void secp256k1_ecdsa_signature_save(secp256k1_ecdsa_signature* sig, const secp256k1_scalar* r, const secp256k1_scalar* s) { + if (sizeof(secp256k1_scalar) == 32) { + memcpy(&sig->data[0], r, 32); + memcpy(&sig->data[32], s, 32); + } else { + secp256k1_scalar_get_b32(&sig->data[0], r); + secp256k1_scalar_get_b32(&sig->data[32], s); + } +} + +int secp256k1_ecdsa_signature_parse_der(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) { + secp256k1_scalar r, s; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(input != NULL); + + if (secp256k1_ecdsa_sig_parse(&r, &s, input, inputlen)) { + secp256k1_ecdsa_signature_save(sig, &r, &s); + return 1; + } else { + memset(sig, 0, sizeof(*sig)); + return 0; + } +} + +int secp256k1_ecdsa_signature_parse_compact(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input64) { + secp256k1_scalar r, s; + int ret = 1; + int overflow = 0; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(input64 != NULL); + + secp256k1_scalar_set_b32(&r, &input64[0], &overflow); + ret &= !overflow; + secp256k1_scalar_set_b32(&s, &input64[32], &overflow); + ret &= !overflow; + if (ret) { + secp256k1_ecdsa_signature_save(sig, &r, &s); + } else { + memset(sig, 0, sizeof(*sig)); + } + return ret; +} + +int secp256k1_ecdsa_signature_serialize_der(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_ecdsa_signature* sig) { + secp256k1_scalar r, s; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(outputlen != NULL); + ARG_CHECK(sig != NULL); + + secp256k1_ecdsa_signature_load(ctx, &r, &s, sig); + return secp256k1_ecdsa_sig_serialize(output, outputlen, &r, &s); +} + +int secp256k1_ecdsa_signature_serialize_compact(const secp256k1_context* ctx, unsigned char *output64, const secp256k1_ecdsa_signature* sig) { + secp256k1_scalar r, s; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output64 != NULL); + ARG_CHECK(sig != NULL); + + secp256k1_ecdsa_signature_load(ctx, &r, &s, sig); + secp256k1_scalar_get_b32(&output64[0], &r); + secp256k1_scalar_get_b32(&output64[32], &s); + return 1; +} + +int secp256k1_ecdsa_signature_normalize(const secp256k1_context* ctx, secp256k1_ecdsa_signature *sigout, const secp256k1_ecdsa_signature *sigin) { + secp256k1_scalar r, s; + int ret = 0; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sigin != NULL); + + secp256k1_ecdsa_signature_load(ctx, &r, &s, sigin); + ret = secp256k1_scalar_is_high(&s); + if (sigout != NULL) { + if (ret) { + secp256k1_scalar_negate(&s, &s); + } + secp256k1_ecdsa_signature_save(sigout, &r, &s); + } + + return ret; +} + +int secp256k1_ecdsa_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msg32, 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(sig != NULL); + ARG_CHECK(pubkey != NULL); + + secp256k1_scalar_set_b32(&m, msg32, 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)); +} + +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; + secp256k1_rfc6979_hmac_sha256 rng; + unsigned int i; + /* 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. + * - 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); + if (data != NULL) { + memcpy(keydata + 64, data, 32); + keylen = 96; + } + if (algo16 != NULL) { + memcpy(keydata + keylen, algo16, 16); + keylen += 16; + } + secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, keylen); + memset(keydata, 0, sizeof(keydata)); + for (i = 0; i <= counter; i++) { + secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); + } + secp256k1_rfc6979_hmac_sha256_finalize(&rng); + return 1; +} + +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; + 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); + 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 (!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); + } + if (ret) { + secp256k1_ecdsa_signature_save(signature, &r, &s); + } else { + memset(signature, 0, sizeof(*signature)); + } + 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); + secp256k1_scalar_clear(&sec); + return ret; +} + +int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *seckey) { + secp256k1_gej pj; + secp256k1_ge p; + secp256k1_scalar sec; + int overflow; + int ret = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + memset(pubkey, 0, sizeof(*pubkey)); + 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); + return ret; +} + +int secp256k1_ec_privkey_negate(const secp256k1_context* ctx, unsigned char *seckey) { + secp256k1_scalar sec; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(seckey != NULL); + + secp256k1_scalar_set_b32(&sec, seckey, NULL); + secp256k1_scalar_negate(&sec, &sec); + secp256k1_scalar_get_b32(seckey, &sec); + + return 1; +} + +int secp256k1_ec_pubkey_negate(const secp256k1_context* ctx, secp256k1_pubkey *pubkey) { + int ret = 0; + secp256k1_ge p; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + + ret = secp256k1_pubkey_load(ctx, &p, pubkey); + memset(pubkey, 0, sizeof(*pubkey)); + if (ret) { + secp256k1_ge_neg(&p, &p); + secp256k1_pubkey_save(pubkey, &p); + } + return ret; +} + +int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) { + secp256k1_scalar term; + secp256k1_scalar sec; + int ret = 0; + int overflow = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(seckey != NULL); + ARG_CHECK(tweak != 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); + } + + 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; + secp256k1_scalar term; + 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); + + secp256k1_scalar_set_b32(&term, tweak, &overflow); + ret = !overflow && secp256k1_pubkey_load(ctx, &p, pubkey); + memset(pubkey, 0, sizeof(*pubkey)); + if (ret) { + if (secp256k1_eckey_pubkey_tweak_add(&ctx->ecmult_ctx, &p, &term)) { + secp256k1_pubkey_save(pubkey, &p); + } else { + ret = 0; + } + } + + return ret; +} + +int secp256k1_ec_privkey_tweak_mul(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) { + secp256k1_scalar factor; + secp256k1_scalar sec; + int ret = 0; + int overflow = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(seckey != NULL); + ARG_CHECK(tweak != 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_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) { + 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); + + secp256k1_scalar_set_b32(&factor, tweak, &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)) { + secp256k1_pubkey_save(pubkey, &p); + } else { + ret = 0; + } + } + + return ret; +} + +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); + return 1; +} + +int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey *pubnonce, const secp256k1_pubkey * const *pubnonces, size_t n) { + size_t i; + secp256k1_gej Qj; + secp256k1_ge Q; + + ARG_CHECK(pubnonce != NULL); + memset(pubnonce, 0, sizeof(*pubnonce)); + ARG_CHECK(n >= 1); + ARG_CHECK(pubnonces != NULL); + + secp256k1_gej_set_infinity(&Qj); + + for (i = 0; i < n; i++) { + secp256k1_pubkey_load(ctx, &Q, pubnonces[i]); + secp256k1_gej_add_ge(&Qj, &Qj, &Q); + } + if (secp256k1_gej_is_infinity(&Qj)) { + return 0; + } + secp256k1_ge_set_gej(&Q, &Qj); + secp256k1_pubkey_save(pubnonce, &Q); + return 1; +} + +#ifdef ENABLE_MODULE_ECDH +# include "modules/ecdh/main_impl.h" +#endif + +#ifdef ENABLE_MODULE_RECOVERY +# include "modules/recovery/main_impl.h" +#endif diff --git a/src/cryptoconditions/src/include/secp256k1/src/testrand.h b/src/cryptoconditions/src/include/secp256k1/src/testrand.h new file mode 100644 index 000000000..f1f9be077 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/testrand.h @@ -0,0 +1,38 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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 + +/* 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); + +/** Generate a pseudorandom number in the range [0..2**32-1]. */ +static uint32_t secp256k1_rand32(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); + +/** Generate a pseudorandom number in the range [0..range-1]. */ +static uint32_t secp256k1_rand_int(uint32_t range); + +/** Generate a pseudorandom 32-byte array. */ +static void secp256k1_rand256(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); + +/** Generate pseudorandom bytes with long sequences of zero and one bits. */ +static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len); + +#endif /* SECP256K1_TESTRAND_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/testrand_impl.h b/src/cryptoconditions/src/include/secp256k1/src/testrand_impl.h new file mode 100644 index 000000000..30a91e529 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/testrand_impl.h @@ -0,0 +1,110 @@ +/********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_TESTRAND_IMPL_H +#define SECP256K1_TESTRAND_IMPL_H + +#include +#include + +#include "testrand.h" +#include "hash.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; + +SECP256K1_INLINE static void secp256k1_rand_seed(const unsigned char *seed16) { + secp256k1_rfc6979_hmac_sha256_initialize(&secp256k1_test_rng, seed16, 16); +} + +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; + } + 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; +} + +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; + } + while(1) { + uint32_t x = secp256k1_rand_bits(bits); + if (x < trange) { + return (mult == 1) ? x : (x % range); + } + } +} + +static void secp256k1_rand256(unsigned char *b32) { + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, b32, 32); +} + +static void secp256k1_rand_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); + while (now > 0 && bits < len * 8) { + bytes[bits / 8] |= val << (bits % 8); + now--; + bits++; + } + } +} + +static void secp256k1_rand256_test(unsigned char *b32) { + secp256k1_rand_bytes_test(b32, 32); +} + +#endif /* SECP256K1_TESTRAND_IMPL_H */ diff --git a/src/cryptoconditions/src/include/secp256k1/src/tests.c b/src/cryptoconditions/src/include/secp256k1/src/tests.c new file mode 100644 index 000000000..f307b99d5 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/tests.c @@ -0,0 +1,4536 @@ +/********************************************************************** + * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#include +#include +#include + +#include + +#include "secp256k1.c" +#include "include/secp256k1.h" +#include "testrand_impl.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" + +#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 +#endif + +static int count = 64; +static secp256k1_context *ctx = NULL; + +static void counting_illegal_callback_fn(const char* str, void* data) { + /* Dummy callback function that just counts. */ + int32_t *p; + (void)str; + p = data; + (*p)++; +} + +static void uncounting_illegal_callback_fn(const char* str, void* data) { + /* Dummy callback function that just counts (backwards). */ + int32_t *p; + (void)str; + p = data; + (*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; + } + } 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); +} + +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); +} + +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; +} + +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); +} + +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); +} + +void run_context_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); + + secp256k1_gej pubj; + secp256k1_ge pub; + secp256k1_scalar msg, key, nonce; + secp256k1_scalar sigr, sigs; + + memset(&zero_pubkey, 0, sizeof(zero_pubkey)); + + 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); + + /*** 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); + } + + /* Verify that the error callback makes it across the clone. */ + CHECK(vrfy->error_callback.fn != sign->error_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); + + /*** attempt to use them ***/ + random_scalar_order_test(&msg); + random_scalar_order_test(&key); + secp256k1_ecmult_gen(&both->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)); + + /* 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)); + + /* 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)); + + /* cleanup */ + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(both); + /* Defined as no-op. */ + secp256k1_context_destroy(NULL); +} + +/***** HASH TESTS *****/ + +void run_sha256_tests(void) { + static const char *inputs[8] = { + "", "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" + }; + static const unsigned char outputs[8][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}, + {0xf3, 0x0c, 0xeb, 0x2b, 0xb2, 0x82, 0x9e, 0x79, 0xe4, 0xca, 0x97, 0x53, 0xd3, 0x5a, 0x8e, 0xcc, 0x00, 0x26, 0x2d, 0x16, 0x4c, 0xc0, 0x77, 0x08, 0x02, 0x95, 0x38, 0x1c, 0xbd, 0x64, 0x3f, 0x0d}, + {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} + }; + int i; + for (i = 0; i < 8; i++) { + unsigned char out[32]; + secp256k1_sha256 hasher; + secp256k1_sha256_initialize(&hasher); + secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i]), strlen(inputs[i])); + secp256k1_sha256_finalize(&hasher, out); + CHECK(memcmp(out, outputs[i], 32) == 0); + if (strlen(inputs[i]) > 0) { + int split = secp256k1_rand_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); + secp256k1_sha256_finalize(&hasher, out); + CHECK(memcmp(out, outputs[i], 32) == 0); + } + } +} + +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", + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19", + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + }; + static const char *inputs[6] = { + "\x48\x69\x20\x54\x68\x65\x72\x65", + "\x77\x68\x61\x74\x20\x64\x6f\x20\x79\x61\x20\x77\x61\x6e\x74\x20\x66\x6f\x72\x20\x6e\x6f\x74\x68\x69\x6e\x67\x3f", + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd", + "\x54\x65\x73\x74\x20\x55\x73\x69\x6e\x67\x20\x4c\x61\x72\x67\x65\x72\x20\x54\x68\x61\x6e\x20\x42\x6c\x6f\x63\x6b\x2d\x53\x69\x7a\x65\x20\x4b\x65\x79\x20\x2d\x20\x48\x61\x73\x68\x20\x4b\x65\x79\x20\x46\x69\x72\x73\x74", + "\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20\x74\x65\x73\x74\x20\x75\x73\x69\x6e\x67\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x6b\x65\x79\x20\x61\x6e\x64\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x64\x61\x74\x61\x2e\x20\x54\x68\x65\x20\x6b\x65\x79\x20\x6e\x65\x65\x64\x73\x20\x74\x6f\x20\x62\x65\x20\x68\x61\x73\x68\x65\x64\x20\x62\x65\x66\x6f\x72\x65\x20\x62\x65\x69\x6e\x67\x20\x75\x73\x65\x64\x20\x62\x79\x20\x74\x68\x65\x20\x48\x4d\x41\x43\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x2e" + }; + static const unsigned char outputs[6][32] = { + {0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0x0b, 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7}, + {0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e, 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7, 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83, 0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43}, + {0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46, 0x85, 0x4d, 0xb8, 0xeb, 0xd0, 0x91, 0x81, 0xa7, 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8, 0xc1, 0x22, 0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe}, + {0x82, 0x55, 0x8a, 0x38, 0x9a, 0x44, 0x3c, 0x0e, 0xa4, 0xcc, 0x81, 0x98, 0x99, 0xf2, 0x08, 0x3a, 0x85, 0xf0, 0xfa, 0xa3, 0xe5, 0x78, 0xf8, 0x07, 0x7a, 0x2e, 0x3f, 0xf4, 0x67, 0x29, 0x66, 0x5b}, + {0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f, 0x0d, 0x8a, 0x26, 0xaa, 0xcb, 0xf5, 0xb7, 0x7f, 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14, 0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54}, + {0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb, 0x27, 0x63, 0x5f, 0xbc, 0xd5, 0xb0, 0xe9, 0x44, 0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07, 0x13, 0x93, 0x8a, 0x7f, 0x51, 0x53, 0x5c, 0x3a, 0x35, 0xe2} + }; + int i; + for (i = 0; i < 6; i++) { + secp256k1_hmac_sha256 hasher; + unsigned char out[32]; + 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); + if (strlen(inputs[i]) > 0) { + int split = secp256k1_rand_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); + } + } +} + +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}, + {0x2b, 0xf0, 0xff, 0xf1, 0xd3, 0xc3, 0x78, 0xa2, 0x2d, 0xc5, 0xde, 0x1d, 0x85, 0x65, 0x22, 0x32, 0x5c, 0x65, 0xb5, 0x04, 0x49, 0x1a, 0x0c, 0xbd, 0x01, 0xcb, 0x8f, 0x3a, 0xa6, 0x7f, 0xfd, 0x4a}, + {0xf5, 0x28, 0xb4, 0x10, 0xcb, 0x54, 0x1f, 0x77, 0x00, 0x0d, 0x7a, 0xfb, 0x6c, 0x5b, 0x53, 0xc5, 0xc4, 0x71, 0xea, 0xb4, 0x3e, 0x46, 0x6d, 0x9a, 0xc5, 0x19, 0x0c, 0x39, 0xc8, 0x2f, 0xd8, 0x2e} + }; + + static const unsigned char key2[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, 0xff, 0xff, 0xff, 0xff, 0xff, 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}; + static const unsigned char out2[3][32] = { + {0x9c, 0x23, 0x6c, 0x16, 0x5b, 0x82, 0xae, 0x0c, 0xd5, 0x90, 0x65, 0x9e, 0x10, 0x0b, 0x6b, 0xab, 0x30, 0x36, 0xe7, 0xba, 0x8b, 0x06, 0x74, 0x9b, 0xaf, 0x69, 0x81, 0xe1, 0x6f, 0x1a, 0x2b, 0x95}, + {0xdf, 0x47, 0x10, 0x61, 0x62, 0x5b, 0xc0, 0xea, 0x14, 0xb6, 0x82, 0xfe, 0xee, 0x2c, 0x9c, 0x02, 0xf2, 0x35, 0xda, 0x04, 0x20, 0x4c, 0x1d, 0x62, 0xa1, 0x53, 0x6c, 0x6e, 0x17, 0xae, 0xd7, 0xa9}, + {0x75, 0x97, 0x88, 0x7c, 0xbd, 0x76, 0x32, 0x1f, 0x32, 0xe3, 0x04, 0x40, 0x67, 0x9a, 0x22, 0xcf, 0x7f, 0x8d, 0x9d, 0x2e, 0xac, 0x39, 0x0e, 0x58, 0x1f, 0xea, 0x09, 0x1c, 0xe2, 0x02, 0xba, 0x94} + }; + + secp256k1_rfc6979_hmac_sha256 rng; + unsigned char out[32]; + int i; + + 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); + } + 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); + } + 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); + } + 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))); + } + } + } + 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); + } + } +} + +/* 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; + 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); + } + /* 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); + } +} + +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]); + } + } +} + +/***** NUM TESTS *****/ + +#ifndef USE_NUM_NONE +void random_num_negate(secp256k1_num *num) { + if (secp256k1_rand_bits(1)) { + secp256k1_num_negate(num); + } +} + +void random_num_order_test(secp256k1_num *num) { + secp256k1_scalar sc; + random_scalar_order_test(&sc); + secp256k1_scalar_get_num(num, &sc); +} + +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) { + 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) { + 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); + } + 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; + int i; + /* squares mod 5 are 1, 4 */ + const int jacobi5[10] = { 0, 1, -1, -1, 1, 0, 1, -1, -1, 1 }; + + /* 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]); + } + + /** 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) { + int i; + for (i = 0; i < 100*count; i++) { + test_num_negate(); + test_num_add_sub(); + test_num_mod(); + test_num_jacobi(); + } +} +#endif + +/***** SCALAR TESTS *****/ + +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); + + /* Set 's1' to a random scalar, with value 's1num'. */ + random_scalar_order_test(&s1); + + /* Set 's2' to a random scalar, with value 'snum2', and byte array representation 'c'. */ + 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. */ + secp256k1_scalar n; + secp256k1_scalar_set_int(&n, 0); + 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)); + for (j = 0; j < 4; j++) { + secp256k1_scalar_add(&n, &n, &n); + } + secp256k1_scalar_add(&n, &n, &t); + } + CHECK(secp256k1_scalar_eq(&n, &s)); + } + + { + /* Test that fetching groups of randomly-sized bits from a scalar and recursing n(i)=b*n(i-1)+p(i) reconstructs it. */ + secp256k1_scalar n; + int i = 0; + secp256k1_scalar_set_int(&n, 0); + while (i < 256) { + secp256k1_scalar t; + int j; + int now = secp256k1_rand_int(15) + 1; + if (now + i > 256) { + now = 256 - i; + } + secp256k1_scalar_set_int(&t, secp256k1_scalar_get_bits_var(&s, 256 - now - i, now)); + for (j = 0; j < now; j++) { + secp256k1_scalar_add(&n, &n, &n); + } + secp256k1_scalar_add(&n, &n, &t); + i += now; + } + 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; + secp256k1_scalar_add(&r1, &s1, &s2); + secp256k1_scalar_add(&r2, &s2, &s1); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + + { + secp256k1_scalar r1, r2; + secp256k1_scalar b; + int i; + /* Test add_bit. */ + int bit = secp256k1_rand_bits(8); + secp256k1_scalar_set_int(&b, 1); + CHECK(secp256k1_scalar_is_one(&b)); + for (i = 0; i < bit; i++) { + secp256k1_scalar_add(&b, &b, &b); + } + r1 = s1; + r2 = s1; + if (!secp256k1_scalar_add(&r1, &r1, &b)) { + /* No overflow happened. */ + secp256k1_scalar_cadd_bit(&r2, bit, 1); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + /* cadd is a noop when flag is zero */ + secp256k1_scalar_cadd_bit(&r2, bit, 0); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + } + + { + /* Test commutativity of mul. */ + secp256k1_scalar r1, r2; + secp256k1_scalar_mul(&r1, &s1, &s2); + secp256k1_scalar_mul(&r2, &s2, &s1); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + + { + /* Test associativity of add. */ + secp256k1_scalar r1, r2; + secp256k1_scalar_add(&r1, &s1, &s2); + secp256k1_scalar_add(&r1, &r1, &s); + secp256k1_scalar_add(&r2, &s2, &s); + secp256k1_scalar_add(&r2, &s1, &r2); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + + { + /* Test associativity of mul. */ + secp256k1_scalar r1, r2; + secp256k1_scalar_mul(&r1, &s1, &s2); + secp256k1_scalar_mul(&r1, &r1, &s); + secp256k1_scalar_mul(&r2, &s2, &s); + secp256k1_scalar_mul(&r2, &s1, &r2); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + + { + /* Test distributitivity of mul over add. */ + secp256k1_scalar r1, r2, t; + secp256k1_scalar_add(&r1, &s1, &s2); + secp256k1_scalar_mul(&r1, &r1, &s); + secp256k1_scalar_mul(&r2, &s1, &s); + secp256k1_scalar_mul(&t, &s2, &s); + secp256k1_scalar_add(&r2, &r2, &t); + 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); + 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); + 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)); + } + +} + +void run_scalar_tests(void) { + int i; + for (i = 0; i < 128 * count; i++) { + scalar_test(); + } + + { + /* (-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); + 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)); + } +#endif + + { + /* Does check_overflow check catch all ones? */ + static const secp256k1_scalar overflowed = SECP256K1_SCALAR_CONST( + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL + ); + CHECK(secp256k1_scalar_check_overflow(&overflowed)); + } + + { + /* Static test vectors. + * These were reduced from ~10^12 random vectors based on comparison-decision + * and edge-case coverage on 32-bit and 64-bit implementations. + * The responses were generated with Sage 5.9. + */ + secp256k1_scalar x; + 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, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, + 0xff, 0xff, 0x03, 0x00, 0xc0, 0xff, 0xff, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff}}, + {{0xef, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x80, 0xff}}, + {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0x3f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0x00}, + {0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x1e, 0xf8, 0xff, 0xff, 0xff, 0xfd, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, + 0x00, 0x00, 0x00, 0xf8, 0xff, 0x03, 0x00, 0xe0, + 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, + 0xf3, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x1c, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, + 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x1f, 0x00, 0x00, 0x80, 0xff, 0xff, 0x3f, + 0x00, 0xfe, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0xfc, 0x9f, + 0xff, 0xff, 0xff, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0x0f, 0xfc, 0xff, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, + {0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x00, 0x00, 0xf8, 0xff, 0x0f, 0xc0, 0xff, 0xff, + 0xff, 0x1f, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x07, 0x80, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, + 0xf7, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xf0}, + {0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0x00, 0xf8, 0xff, 0x03, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x03, 0xc0, 0xff, 0x0f, 0xfc, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, + 0xff, 0x01, 0x00, 0x00, 0x00, 0x3f, 0x00, 0xc0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0x8f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x7f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x03, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0xff, 0x7f}, + {0xff, 0xcf, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xff, + 0xbf, 0xff, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, + 0xff, 0xff, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0x01, 0xfc, 0xff, 0x01, 0x00, 0xfe, 0xff}, + {0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00}}, + {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x7f, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0xff, 0x01, 0x00, 0xf0, 0xff, 0xff, + 0xe0, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x00}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xfc, 0xff, 0xff, 0x3f, 0xf0, 0xff, 0xff, 0x3f, + 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x7e, 0x00, 0x00}}, + {{0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x1f, 0x00, 0x00, 0xfe, 0x07, 0x00}, + {0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xfb, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60}}, + {{0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, + 0x80, 0x7f, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0xff, 0xff, 0x1f, 0x00, 0xf0, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00}}, + {{0x80, 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}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, + 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0xff, 0xff, 0xcf, 0xff, 0x1f, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x7e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00}, + {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0x7f, 0x00, 0x80, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, + {0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x80, + 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x7f, 0xf8, 0xff, 0xff, 0x1f, 0x00, 0xfe}}, + {{0xff, 0xff, 0xff, 0x3f, 0xf8, 0xff, 0xff, 0xff, + 0xff, 0x03, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0x01, 0x80, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 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, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{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, 0xff}, + {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, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xc0, + 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff}}, + {{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, 0xff}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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}}, + {{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}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x7e, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x07, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0xff, 0x01, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, + 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, + 0xff, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x3f, 0x00, 0x00, 0xc0, 0xf1, 0x7f, 0x00}}, + {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x00}, + {0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, + 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1f, + 0x00, 0x00, 0xfc, 0xff, 0xff, 0x01, 0xff, 0xff}}, + {{0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0x03, 0xe0, 0x01, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xfc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, + {0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xff, 0xff, 0xf0, 0x07, 0x00, 0x3c, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x07, 0xe0, 0xff, 0x00, 0x00, 0x00}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x80, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x7f, 0xfe, 0xff, 0x1f, + 0x00, 0xfe, 0xff, 0x03, 0x00, 0x00, 0xfe, 0xff}}, + {{0xff, 0xff, 0x81, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, + 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xf0}, + {0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, + 0xf8, 0x07, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xc7, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff}}, + {{0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0x03, 0xfb, + 0xfa, 0x8a, 0x7d, 0xdf, 0x13, 0x86, 0xe2, 0x03}, + {0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0x03, 0xfb, + 0xfa, 0x8a, 0x7d, 0xdf, 0x13, 0x86, 0xe2, 0x03}} + }; + unsigned char res[33][2][32] = { + {{0x0c, 0x3b, 0x0a, 0xca, 0x8d, 0x1a, 0x2f, 0xb9, + 0x8a, 0x7b, 0x53, 0x5a, 0x1f, 0xc5, 0x22, 0xa1, + 0x07, 0x2a, 0x48, 0xea, 0x02, 0xeb, 0xb3, 0xd6, + 0x20, 0x1e, 0x86, 0xd0, 0x95, 0xf6, 0x92, 0x35}, + {0xdc, 0x90, 0x7a, 0x07, 0x2e, 0x1e, 0x44, 0x6d, + 0xf8, 0x15, 0x24, 0x5b, 0x5a, 0x96, 0x37, 0x9c, + 0x37, 0x7b, 0x0d, 0xac, 0x1b, 0x65, 0x58, 0x49, + 0x43, 0xb7, 0x31, 0xbb, 0xa7, 0xf4, 0x97, 0x15}}, + {{0xf1, 0xf7, 0x3a, 0x50, 0xe6, 0x10, 0xba, 0x22, + 0x43, 0x4d, 0x1f, 0x1f, 0x7c, 0x27, 0xca, 0x9c, + 0xb8, 0xb6, 0xa0, 0xfc, 0xd8, 0xc0, 0x05, 0x2f, + 0xf7, 0x08, 0xe1, 0x76, 0xdd, 0xd0, 0x80, 0xc8}, + {0xe3, 0x80, 0x80, 0xb8, 0xdb, 0xe3, 0xa9, 0x77, + 0x00, 0xb0, 0xf5, 0x2e, 0x27, 0xe2, 0x68, 0xc4, + 0x88, 0xe8, 0x04, 0xc1, 0x12, 0xbf, 0x78, 0x59, + 0xe6, 0xa9, 0x7c, 0xe1, 0x81, 0xdd, 0xb9, 0xd5}}, + {{0x96, 0xe2, 0xee, 0x01, 0xa6, 0x80, 0x31, 0xef, + 0x5c, 0xd0, 0x19, 0xb4, 0x7d, 0x5f, 0x79, 0xab, + 0xa1, 0x97, 0xd3, 0x7e, 0x33, 0xbb, 0x86, 0x55, + 0x60, 0x20, 0x10, 0x0d, 0x94, 0x2d, 0x11, 0x7c}, + {0xcc, 0xab, 0xe0, 0xe8, 0x98, 0x65, 0x12, 0x96, + 0x38, 0x5a, 0x1a, 0xf2, 0x85, 0x23, 0x59, 0x5f, + 0xf9, 0xf3, 0xc2, 0x81, 0x70, 0x92, 0x65, 0x12, + 0x9c, 0x65, 0x1e, 0x96, 0x00, 0xef, 0xe7, 0x63}}, + {{0xac, 0x1e, 0x62, 0xc2, 0x59, 0xfc, 0x4e, 0x5c, + 0x83, 0xb0, 0xd0, 0x6f, 0xce, 0x19, 0xf6, 0xbf, + 0xa4, 0xb0, 0xe0, 0x53, 0x66, 0x1f, 0xbf, 0xc9, + 0x33, 0x47, 0x37, 0xa9, 0x3d, 0x5d, 0xb0, 0x48}, + {0x86, 0xb9, 0x2a, 0x7f, 0x8e, 0xa8, 0x60, 0x42, + 0x26, 0x6d, 0x6e, 0x1c, 0xa2, 0xec, 0xe0, 0xe5, + 0x3e, 0x0a, 0x33, 0xbb, 0x61, 0x4c, 0x9f, 0x3c, + 0xd1, 0xdf, 0x49, 0x33, 0xcd, 0x72, 0x78, 0x18}}, + {{0xf7, 0xd3, 0xcd, 0x49, 0x5c, 0x13, 0x22, 0xfb, + 0x2e, 0xb2, 0x2f, 0x27, 0xf5, 0x8a, 0x5d, 0x74, + 0xc1, 0x58, 0xc5, 0xc2, 0x2d, 0x9f, 0x52, 0xc6, + 0x63, 0x9f, 0xba, 0x05, 0x76, 0x45, 0x7a, 0x63}, + {0x8a, 0xfa, 0x55, 0x4d, 0xdd, 0xa3, 0xb2, 0xc3, + 0x44, 0xfd, 0xec, 0x72, 0xde, 0xef, 0xc0, 0x99, + 0xf5, 0x9f, 0xe2, 0x52, 0xb4, 0x05, 0x32, 0x58, + 0x57, 0xc1, 0x8f, 0xea, 0xc3, 0x24, 0x5b, 0x94}}, + {{0x05, 0x83, 0xee, 0xdd, 0x64, 0xf0, 0x14, 0x3b, + 0xa0, 0x14, 0x4a, 0x3a, 0x41, 0x82, 0x7c, 0xa7, + 0x2c, 0xaa, 0xb1, 0x76, 0xbb, 0x59, 0x64, 0x5f, + 0x52, 0xad, 0x25, 0x29, 0x9d, 0x8f, 0x0b, 0xb0}, + {0x7e, 0xe3, 0x7c, 0xca, 0xcd, 0x4f, 0xb0, 0x6d, + 0x7a, 0xb2, 0x3e, 0xa0, 0x08, 0xb9, 0xa8, 0x2d, + 0xc2, 0xf4, 0x99, 0x66, 0xcc, 0xac, 0xd8, 0xb9, + 0x72, 0x2a, 0x4a, 0x3e, 0x0f, 0x7b, 0xbf, 0xf4}}, + {{0x8c, 0x9c, 0x78, 0x2b, 0x39, 0x61, 0x7e, 0xf7, + 0x65, 0x37, 0x66, 0x09, 0x38, 0xb9, 0x6f, 0x70, + 0x78, 0x87, 0xff, 0xcf, 0x93, 0xca, 0x85, 0x06, + 0x44, 0x84, 0xa7, 0xfe, 0xd3, 0xa4, 0xe3, 0x7e}, + {0xa2, 0x56, 0x49, 0x23, 0x54, 0xa5, 0x50, 0xe9, + 0x5f, 0xf0, 0x4d, 0xe7, 0xdc, 0x38, 0x32, 0x79, + 0x4f, 0x1c, 0xb7, 0xe4, 0xbb, 0xf8, 0xbb, 0x2e, + 0x40, 0x41, 0x4b, 0xcc, 0xe3, 0x1e, 0x16, 0x36}}, + {{0x0c, 0x1e, 0xd7, 0x09, 0x25, 0x40, 0x97, 0xcb, + 0x5c, 0x46, 0xa8, 0xda, 0xef, 0x25, 0xd5, 0xe5, + 0x92, 0x4d, 0xcf, 0xa3, 0xc4, 0x5d, 0x35, 0x4a, + 0xe4, 0x61, 0x92, 0xf3, 0xbf, 0x0e, 0xcd, 0xbe}, + {0xe4, 0xaf, 0x0a, 0xb3, 0x30, 0x8b, 0x9b, 0x48, + 0x49, 0x43, 0xc7, 0x64, 0x60, 0x4a, 0x2b, 0x9e, + 0x95, 0x5f, 0x56, 0xe8, 0x35, 0xdc, 0xeb, 0xdc, + 0xc7, 0xc4, 0xfe, 0x30, 0x40, 0xc7, 0xbf, 0xa4}}, + {{0xd4, 0xa0, 0xf5, 0x81, 0x49, 0x6b, 0xb6, 0x8b, + 0x0a, 0x69, 0xf9, 0xfe, 0xa8, 0x32, 0xe5, 0xe0, + 0xa5, 0xcd, 0x02, 0x53, 0xf9, 0x2c, 0xe3, 0x53, + 0x83, 0x36, 0xc6, 0x02, 0xb5, 0xeb, 0x64, 0xb8}, + {0x1d, 0x42, 0xb9, 0xf9, 0xe9, 0xe3, 0x93, 0x2c, + 0x4c, 0xee, 0x6c, 0x5a, 0x47, 0x9e, 0x62, 0x01, + 0x6b, 0x04, 0xfe, 0xa4, 0x30, 0x2b, 0x0d, 0x4f, + 0x71, 0x10, 0xd3, 0x55, 0xca, 0xf3, 0x5e, 0x80}}, + {{0x77, 0x05, 0xf6, 0x0c, 0x15, 0x9b, 0x45, 0xe7, + 0xb9, 0x11, 0xb8, 0xf5, 0xd6, 0xda, 0x73, 0x0c, + 0xda, 0x92, 0xea, 0xd0, 0x9d, 0xd0, 0x18, 0x92, + 0xce, 0x9a, 0xaa, 0xee, 0x0f, 0xef, 0xde, 0x30}, + {0xf1, 0xf1, 0xd6, 0x9b, 0x51, 0xd7, 0x77, 0x62, + 0x52, 0x10, 0xb8, 0x7a, 0x84, 0x9d, 0x15, 0x4e, + 0x07, 0xdc, 0x1e, 0x75, 0x0d, 0x0c, 0x3b, 0xdb, + 0x74, 0x58, 0x62, 0x02, 0x90, 0x54, 0x8b, 0x43}}, + {{0xa6, 0xfe, 0x0b, 0x87, 0x80, 0x43, 0x67, 0x25, + 0x57, 0x5d, 0xec, 0x40, 0x50, 0x08, 0xd5, 0x5d, + 0x43, 0xd7, 0xe0, 0xaa, 0xe0, 0x13, 0xb6, 0xb0, + 0xc0, 0xd4, 0xe5, 0x0d, 0x45, 0x83, 0xd6, 0x13}, + {0x40, 0x45, 0x0a, 0x92, 0x31, 0xea, 0x8c, 0x60, + 0x8c, 0x1f, 0xd8, 0x76, 0x45, 0xb9, 0x29, 0x00, + 0x26, 0x32, 0xd8, 0xa6, 0x96, 0x88, 0xe2, 0xc4, + 0x8b, 0xdb, 0x7f, 0x17, 0x87, 0xcc, 0xc8, 0xf2}}, + {{0xc2, 0x56, 0xe2, 0xb6, 0x1a, 0x81, 0xe7, 0x31, + 0x63, 0x2e, 0xbb, 0x0d, 0x2f, 0x81, 0x67, 0xd4, + 0x22, 0xe2, 0x38, 0x02, 0x25, 0x97, 0xc7, 0x88, + 0x6e, 0xdf, 0xbe, 0x2a, 0xa5, 0x73, 0x63, 0xaa}, + {0x50, 0x45, 0xe2, 0xc3, 0xbd, 0x89, 0xfc, 0x57, + 0xbd, 0x3c, 0xa3, 0x98, 0x7e, 0x7f, 0x36, 0x38, + 0x92, 0x39, 0x1f, 0x0f, 0x81, 0x1a, 0x06, 0x51, + 0x1f, 0x8d, 0x6a, 0xff, 0x47, 0x16, 0x06, 0x9c}}, + {{0x33, 0x95, 0xa2, 0x6f, 0x27, 0x5f, 0x9c, 0x9c, + 0x64, 0x45, 0xcb, 0xd1, 0x3c, 0xee, 0x5e, 0x5f, + 0x48, 0xa6, 0xaf, 0xe3, 0x79, 0xcf, 0xb1, 0xe2, + 0xbf, 0x55, 0x0e, 0xa2, 0x3b, 0x62, 0xf0, 0xe4}, + {0x14, 0xe8, 0x06, 0xe3, 0xbe, 0x7e, 0x67, 0x01, + 0xc5, 0x21, 0x67, 0xd8, 0x54, 0xb5, 0x7f, 0xa4, + 0xf9, 0x75, 0x70, 0x1c, 0xfd, 0x79, 0xdb, 0x86, + 0xad, 0x37, 0x85, 0x83, 0x56, 0x4e, 0xf0, 0xbf}}, + {{0xbc, 0xa6, 0xe0, 0x56, 0x4e, 0xef, 0xfa, 0xf5, + 0x1d, 0x5d, 0x3f, 0x2a, 0x5b, 0x19, 0xab, 0x51, + 0xc5, 0x8b, 0xdd, 0x98, 0x28, 0x35, 0x2f, 0xc3, + 0x81, 0x4f, 0x5c, 0xe5, 0x70, 0xb9, 0xeb, 0x62}, + {0xc4, 0x6d, 0x26, 0xb0, 0x17, 0x6b, 0xfe, 0x6c, + 0x12, 0xf8, 0xe7, 0xc1, 0xf5, 0x2f, 0xfa, 0x91, + 0x13, 0x27, 0xbd, 0x73, 0xcc, 0x33, 0x31, 0x1c, + 0x39, 0xe3, 0x27, 0x6a, 0x95, 0xcf, 0xc5, 0xfb}}, + {{0x30, 0xb2, 0x99, 0x84, 0xf0, 0x18, 0x2a, 0x6e, + 0x1e, 0x27, 0xed, 0xa2, 0x29, 0x99, 0x41, 0x56, + 0xe8, 0xd4, 0x0d, 0xef, 0x99, 0x9c, 0xf3, 0x58, + 0x29, 0x55, 0x1a, 0xc0, 0x68, 0xd6, 0x74, 0xa4}, + {0x07, 0x9c, 0xe7, 0xec, 0xf5, 0x36, 0x73, 0x41, + 0xa3, 0x1c, 0xe5, 0x93, 0x97, 0x6a, 0xfd, 0xf7, + 0x53, 0x18, 0xab, 0xaf, 0xeb, 0x85, 0xbd, 0x92, + 0x90, 0xab, 0x3c, 0xbf, 0x30, 0x82, 0xad, 0xf6}}, + {{0xc6, 0x87, 0x8a, 0x2a, 0xea, 0xc0, 0xa9, 0xec, + 0x6d, 0xd3, 0xdc, 0x32, 0x23, 0xce, 0x62, 0x19, + 0xa4, 0x7e, 0xa8, 0xdd, 0x1c, 0x33, 0xae, 0xd3, + 0x4f, 0x62, 0x9f, 0x52, 0xe7, 0x65, 0x46, 0xf4}, + {0x97, 0x51, 0x27, 0x67, 0x2d, 0xa2, 0x82, 0x87, + 0x98, 0xd3, 0xb6, 0x14, 0x7f, 0x51, 0xd3, 0x9a, + 0x0b, 0xd0, 0x76, 0x81, 0xb2, 0x4f, 0x58, 0x92, + 0xa4, 0x86, 0xa1, 0xa7, 0x09, 0x1d, 0xef, 0x9b}}, + {{0xb3, 0x0f, 0x2b, 0x69, 0x0d, 0x06, 0x90, 0x64, + 0xbd, 0x43, 0x4c, 0x10, 0xe8, 0x98, 0x1c, 0xa3, + 0xe1, 0x68, 0xe9, 0x79, 0x6c, 0x29, 0x51, 0x3f, + 0x41, 0xdc, 0xdf, 0x1f, 0xf3, 0x60, 0xbe, 0x33}, + {0xa1, 0x5f, 0xf7, 0x1d, 0xb4, 0x3e, 0x9b, 0x3c, + 0xe7, 0xbd, 0xb6, 0x06, 0xd5, 0x60, 0x06, 0x6d, + 0x50, 0xd2, 0xf4, 0x1a, 0x31, 0x08, 0xf2, 0xea, + 0x8e, 0xef, 0x5f, 0x7d, 0xb6, 0xd0, 0xc0, 0x27}}, + {{0x62, 0x9a, 0xd9, 0xbb, 0x38, 0x36, 0xce, 0xf7, + 0x5d, 0x2f, 0x13, 0xec, 0xc8, 0x2d, 0x02, 0x8a, + 0x2e, 0x72, 0xf0, 0xe5, 0x15, 0x9d, 0x72, 0xae, + 0xfc, 0xb3, 0x4f, 0x02, 0xea, 0xe1, 0x09, 0xfe}, + {0x00, 0x00, 0x00, 0x00, 0xfa, 0x0a, 0x3d, 0xbc, + 0xad, 0x16, 0x0c, 0xb6, 0xe7, 0x7c, 0x8b, 0x39, + 0x9a, 0x43, 0xbb, 0xe3, 0xc2, 0x55, 0x15, 0x14, + 0x75, 0xac, 0x90, 0x9b, 0x7f, 0x9a, 0x92, 0x00}}, + {{0x8b, 0xac, 0x70, 0x86, 0x29, 0x8f, 0x00, 0x23, + 0x7b, 0x45, 0x30, 0xaa, 0xb8, 0x4c, 0xc7, 0x8d, + 0x4e, 0x47, 0x85, 0xc6, 0x19, 0xe3, 0x96, 0xc2, + 0x9a, 0xa0, 0x12, 0xed, 0x6f, 0xd7, 0x76, 0x16}, + {0x45, 0xaf, 0x7e, 0x33, 0xc7, 0x7f, 0x10, 0x6c, + 0x7c, 0x9f, 0x29, 0xc1, 0xa8, 0x7e, 0x15, 0x84, + 0xe7, 0x7d, 0xc0, 0x6d, 0xab, 0x71, 0x5d, 0xd0, + 0x6b, 0x9f, 0x97, 0xab, 0xcb, 0x51, 0x0c, 0x9f}}, + {{0x9e, 0xc3, 0x92, 0xb4, 0x04, 0x9f, 0xc8, 0xbb, + 0xdd, 0x9e, 0xc6, 0x05, 0xfd, 0x65, 0xec, 0x94, + 0x7f, 0x2c, 0x16, 0xc4, 0x40, 0xac, 0x63, 0x7b, + 0x7d, 0xb8, 0x0c, 0xe4, 0x5b, 0xe3, 0xa7, 0x0e}, + {0x43, 0xf4, 0x44, 0xe8, 0xcc, 0xc8, 0xd4, 0x54, + 0x33, 0x37, 0x50, 0xf2, 0x87, 0x42, 0x2e, 0x00, + 0x49, 0x60, 0x62, 0x02, 0xfd, 0x1a, 0x7c, 0xdb, + 0x29, 0x6c, 0x6d, 0x54, 0x53, 0x08, 0xd1, 0xc8}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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}}, + {{0x27, 0x59, 0xc7, 0x35, 0x60, 0x71, 0xa6, 0xf1, + 0x79, 0xa5, 0xfd, 0x79, 0x16, 0xf3, 0x41, 0xf0, + 0x57, 0xb4, 0x02, 0x97, 0x32, 0xe7, 0xde, 0x59, + 0xe2, 0x2d, 0x9b, 0x11, 0xea, 0x2c, 0x35, 0x92}, + {0x27, 0x59, 0xc7, 0x35, 0x60, 0x71, 0xa6, 0xf1, + 0x79, 0xa5, 0xfd, 0x79, 0x16, 0xf3, 0x41, 0xf0, + 0x57, 0xb4, 0x02, 0x97, 0x32, 0xe7, 0xde, 0x59, + 0xe2, 0x2d, 0x9b, 0x11, 0xea, 0x2c, 0x35, 0x92}}, + {{0x28, 0x56, 0xac, 0x0e, 0x4f, 0x98, 0x09, 0xf0, + 0x49, 0xfa, 0x7f, 0x84, 0xac, 0x7e, 0x50, 0x5b, + 0x17, 0x43, 0x14, 0x89, 0x9c, 0x53, 0xa8, 0x94, + 0x30, 0xf2, 0x11, 0x4d, 0x92, 0x14, 0x27, 0xe8}, + {0x39, 0x7a, 0x84, 0x56, 0x79, 0x9d, 0xec, 0x26, + 0x2c, 0x53, 0xc1, 0x94, 0xc9, 0x8d, 0x9e, 0x9d, + 0x32, 0x1f, 0xdd, 0x84, 0x04, 0xe8, 0xe2, 0x0a, + 0x6b, 0xbe, 0xbb, 0x42, 0x40, 0x67, 0x30, 0x6c}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x45, 0x51, 0x23, 0x19, 0x50, 0xb7, 0x5f, 0xc4, + 0x40, 0x2d, 0xa1, 0x73, 0x2f, 0xc9, 0xbe, 0xbd}, + {0x27, 0x59, 0xc7, 0x35, 0x60, 0x71, 0xa6, 0xf1, + 0x79, 0xa5, 0xfd, 0x79, 0x16, 0xf3, 0x41, 0xf0, + 0x57, 0xb4, 0x02, 0x97, 0x32, 0xe7, 0xde, 0x59, + 0xe2, 0x2d, 0x9b, 0x11, 0xea, 0x2c, 0x35, 0x92}}, + {{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}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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}}, + {{0x1c, 0xc4, 0xf7, 0xda, 0x0f, 0x65, 0xca, 0x39, + 0x70, 0x52, 0x92, 0x8e, 0xc3, 0xc8, 0x15, 0xea, + 0x7f, 0x10, 0x9e, 0x77, 0x4b, 0x6e, 0x2d, 0xdf, + 0xe8, 0x30, 0x9d, 0xda, 0xe8, 0x9a, 0x65, 0xae}, + {0x02, 0xb0, 0x16, 0xb1, 0x1d, 0xc8, 0x57, 0x7b, + 0xa2, 0x3a, 0xa2, 0xa3, 0x38, 0x5c, 0x8f, 0xeb, + 0x66, 0x37, 0x91, 0xa8, 0x5f, 0xef, 0x04, 0xf6, + 0x59, 0x75, 0xe1, 0xee, 0x92, 0xf6, 0x0e, 0x30}}, + {{0x8d, 0x76, 0x14, 0xa4, 0x14, 0x06, 0x9f, 0x9a, + 0xdf, 0x4a, 0x85, 0xa7, 0x6b, 0xbf, 0x29, 0x6f, + 0xbc, 0x34, 0x87, 0x5d, 0xeb, 0xbb, 0x2e, 0xa9, + 0xc9, 0x1f, 0x58, 0xd6, 0x9a, 0x82, 0xa0, 0x56}, + {0xd4, 0xb9, 0xdb, 0x88, 0x1d, 0x04, 0xe9, 0x93, + 0x8d, 0x3f, 0x20, 0xd5, 0x86, 0xa8, 0x83, 0x07, + 0xdb, 0x09, 0xd8, 0x22, 0x1f, 0x7f, 0xf1, 0x71, + 0xc8, 0xe7, 0x5d, 0x47, 0xaf, 0x8b, 0x72, 0xe9}}, + {{0x83, 0xb9, 0x39, 0xb2, 0xa4, 0xdf, 0x46, 0x87, + 0xc2, 0xb8, 0xf1, 0xe6, 0x4c, 0xd1, 0xe2, 0xa9, + 0xe4, 0x70, 0x30, 0x34, 0xbc, 0x52, 0x7c, 0x55, + 0xa6, 0xec, 0x80, 0xa4, 0xe5, 0xd2, 0xdc, 0x73}, + {0x08, 0xf1, 0x03, 0xcf, 0x16, 0x73, 0xe8, 0x7d, + 0xb6, 0x7e, 0x9b, 0xc0, 0xb4, 0xc2, 0xa5, 0x86, + 0x02, 0x77, 0xd5, 0x27, 0x86, 0xa5, 0x15, 0xfb, + 0xae, 0x9b, 0x8c, 0xa9, 0xf9, 0xf8, 0xa8, 0x4a}}, + {{0x8b, 0x00, 0x49, 0xdb, 0xfa, 0xf0, 0x1b, 0xa2, + 0xed, 0x8a, 0x9a, 0x7a, 0x36, 0x78, 0x4a, 0xc7, + 0xf7, 0xad, 0x39, 0xd0, 0x6c, 0x65, 0x7a, 0x41, + 0xce, 0xd6, 0xd6, 0x4c, 0x20, 0x21, 0x6b, 0xc7}, + {0xc6, 0xca, 0x78, 0x1d, 0x32, 0x6c, 0x6c, 0x06, + 0x91, 0xf2, 0x1a, 0xe8, 0x43, 0x16, 0xea, 0x04, + 0x3c, 0x1f, 0x07, 0x85, 0xf7, 0x09, 0x22, 0x08, + 0xba, 0x13, 0xfd, 0x78, 0x1e, 0x3f, 0x6f, 0x62}}, + {{0x25, 0x9b, 0x7c, 0xb0, 0xac, 0x72, 0x6f, 0xb2, + 0xe3, 0x53, 0x84, 0x7a, 0x1a, 0x9a, 0x98, 0x9b, + 0x44, 0xd3, 0x59, 0xd0, 0x8e, 0x57, 0x41, 0x40, + 0x78, 0xa7, 0x30, 0x2f, 0x4c, 0x9c, 0xb9, 0x68}, + {0xb7, 0x75, 0x03, 0x63, 0x61, 0xc2, 0x48, 0x6e, + 0x12, 0x3d, 0xbf, 0x4b, 0x27, 0xdf, 0xb1, 0x7a, + 0xff, 0x4e, 0x31, 0x07, 0x83, 0xf4, 0x62, 0x5b, + 0x19, 0xa5, 0xac, 0xa0, 0x32, 0x58, 0x0d, 0xa7}}, + {{0x43, 0x4f, 0x10, 0xa4, 0xca, 0xdb, 0x38, 0x67, + 0xfa, 0xae, 0x96, 0xb5, 0x6d, 0x97, 0xff, 0x1f, + 0xb6, 0x83, 0x43, 0xd3, 0xa0, 0x2d, 0x70, 0x7a, + 0x64, 0x05, 0x4c, 0xa7, 0xc1, 0xa5, 0x21, 0x51}, + {0xe4, 0xf1, 0x23, 0x84, 0xe1, 0xb5, 0x9d, 0xf2, + 0xb8, 0x73, 0x8b, 0x45, 0x2b, 0x35, 0x46, 0x38, + 0x10, 0x2b, 0x50, 0xf8, 0x8b, 0x35, 0xcd, 0x34, + 0xc8, 0x0e, 0xf6, 0xdb, 0x09, 0x35, 0xf0, 0xda}}, + {{0xdb, 0x21, 0x5c, 0x8d, 0x83, 0x1d, 0xb3, 0x34, + 0xc7, 0x0e, 0x43, 0xa1, 0x58, 0x79, 0x67, 0x13, + 0x1e, 0x86, 0x5d, 0x89, 0x63, 0xe6, 0x0a, 0x46, + 0x5c, 0x02, 0x97, 0x1b, 0x62, 0x43, 0x86, 0xf5}, + {0xdb, 0x21, 0x5c, 0x8d, 0x83, 0x1d, 0xb3, 0x34, + 0xc7, 0x0e, 0x43, 0xa1, 0x58, 0x79, 0x67, 0x13, + 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); + secp256k1_scalar_set_b32(&y, chal[i][1], &overflow); + CHECK(!overflow); + secp256k1_scalar_set_b32(&r1, res[i][0], &overflow); + CHECK(!overflow); + 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)); + } + 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)); + } + } +} + +/***** 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) { + secp256k1_fe r; + 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) { + 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); +} + +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, + 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, + 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40 + }; + static const secp256k1_fe_storage fes = SECP256K1_FE_STORAGE_CONST( + 0x00010203UL, 0x04050607UL, 0x11121314UL, 0x15161718UL, + 0x22232425UL, 0x26272829UL, 0x33343536UL, 0x37383940UL + ); + static const secp256k1_fe fe = SECP256K1_FE_CONST( + 0x00010203UL, 0x04050607UL, 0x11121314UL, 0x15161718UL, + 0x22232425UL, 0x26272829UL, 0x33343536UL, 0x37383940UL + ); + secp256k1_fe fe2; + 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)); + secp256k1_fe_from_storage(&fe2, &fes); + CHECK(secp256k1_fe_equal_var(&fe, &fe2)); + /* Check conversion from fe. */ + secp256k1_fe_get_b32(b322, &fe); + CHECK(memcmp(b322, b32, 32) == 0); + secp256k1_fe_to_storage(&fes2, &fe); + CHECK(memcmp(&fes2, &fes, sizeof(fes)) == 0); +} + +int fe_memcmp(const secp256k1_fe *a, const secp256k1_fe *b) { + secp256k1_fe t = *b; +#ifdef VERIFY + t.magnitude = a->magnitude; + t.normalized = a->normalized; +#endif + return memcmp(a, &t, sizeof(secp256k1_fe)); +} + +void run_field_misc(void) { + secp256k1_fe x; + secp256k1_fe y; + secp256k1_fe z; + secp256k1_fe q; + secp256k1_fe fe5 = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 5); + int i, j; + for (i = 0; i < 5*count; i++) { + secp256k1_fe_storage xs, ys, zs; + random_fe(&x); + random_fe_non_zero(&y); + /* Test the fe equality and comparison operations. */ + CHECK(secp256k1_fe_cmp_var(&x, &x) == 0); + CHECK(secp256k1_fe_equal_var(&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); + secp256k1_fe_cmov(&x, &x, 1); + CHECK(fe_memcmp(&x, &z) != 0); + CHECK(fe_memcmp(&x, &q) == 0); + secp256k1_fe_cmov(&q, &z, 1); + VERIFY_CHECK(!q.normalized && q.magnitude == z.magnitude); + CHECK(fe_memcmp(&q, &z) == 0); + secp256k1_fe_normalize_var(&x); + secp256k1_fe_normalize_var(&z); + CHECK(!secp256k1_fe_equal_var(&x, &z)); + secp256k1_fe_normalize_var(&q); + secp256k1_fe_cmov(&q, &z, (i&1)); + VERIFY_CHECK(q.normalized && q.magnitude == 1); + for (j = 0; j < 6; j++) { + secp256k1_fe_negate(&z, &z, j+1); + secp256k1_fe_normalize_var(&q); + secp256k1_fe_cmov(&q, &z, (j&1)); + VERIFY_CHECK(!q.normalized && q.magnitude == (j+2)); + } + secp256k1_fe_normalize_var(&z); + /* Test storage conversion and conditional moves. */ + secp256k1_fe_to_storage(&xs, &x); + secp256k1_fe_to_storage(&ys, &y); + 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); + secp256k1_fe_storage_cmov(&ys, &xs, 1); + CHECK(memcmp(&xs, &ys, sizeof(xs)) == 0); + secp256k1_fe_from_storage(&x, &xs); + secp256k1_fe_from_storage(&y, &ys); + secp256k1_fe_from_storage(&z, &zs); + /* Test that mul_int, mul, and add agree. */ + secp256k1_fe_add(&y, &x); + secp256k1_fe_add(&y, &x); + z = x; + secp256k1_fe_mul_int(&z, 3); + CHECK(check_fe_equal(&y, &z)); + secp256k1_fe_add(&y, &x); + secp256k1_fe_add(&z, &x); + CHECK(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)); + 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)); + } +} + +void run_field_inv(void) { + secp256k1_fe x, xi, xii; + 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)); + } +} + +void run_field_inv_var(void) { + secp256k1_fe x, xi, xii; + 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)); + } +} + +void run_field_inv_all_var(void) { + secp256k1_fe x[16], xi[16], xii[16]; + 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])); + } + } +} + +void run_sqr(void) { + secp256k1_fe x, s; + + { + 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); + secp256k1_fe_sqr(&s, &x); + } + } +} + +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)); + + if (k != NULL) { + /* Check that the returned root is +/- the given known answer */ + secp256k1_fe_negate(&r2, &r1, 1); + secp256k1_fe_add(&r1, k); secp256k1_fe_add(&r2, k); + secp256k1_fe_normalize(&r1); secp256k1_fe_normalize(&r2); + CHECK(secp256k1_fe_is_zero(&r1) || secp256k1_fe_is_zero(&r2)); + } +} + +void run_sqrt(void) { + secp256k1_fe ns, x, s, t; + int i; + + /* Check sqrt(0) is 0 */ + secp256k1_fe_set_int(&x, 0); + secp256k1_fe_sqr(&s, &x); + test_sqrt(&s, &x); + + /* Check sqrt of small squares (and their negatives) */ + for (i = 1; i <= 100; i++) { + secp256k1_fe_set_int(&x, i); + secp256k1_fe_sqr(&s, &x); + test_sqrt(&s, &x); + secp256k1_fe_negate(&t, &s, 1); + test_sqrt(&t, NULL); + } + + /* Consistency checks for large random values */ + for (i = 0; i < 10; i++) { + int j; + random_fe_non_square(&ns); + for (j = 0; j < count; j++) { + random_fe(&x); + secp256k1_fe_sqr(&s, &x); + test_sqrt(&s, &x); + secp256k1_fe_negate(&t, &s, 1); + test_sqrt(&t, NULL); + secp256k1_fe_mul(&t, &s, &ns); + test_sqrt(&t, NULL); + } + } +} + +/***** GROUP TESTS *****/ + +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)); +} + +/* 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) { + secp256k1_gej a2; + secp256k1_gej b2; + int ret = 1; + ret &= a->infinity == b->infinity; + if (ret && !a->infinity) { + a2 = *a; + b2 = *b; + secp256k1_fe_normalize(&a2.x); + secp256k1_fe_normalize(&a2.y); + secp256k1_fe_normalize(&a2.z); + secp256k1_fe_normalize(&b2.x); + secp256k1_fe_normalize(&b2.y); + secp256k1_fe_normalize(&b2.z); + ret &= secp256k1_fe_cmp_var(&a2.x, &b2.x) == 0; + ret &= secp256k1_fe_cmp_var(&a2.y, &b2.y) == 0; + ret &= secp256k1_fe_cmp_var(&a2.z, &b2.z) == 0; + } + 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) { + 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. + */ + 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_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; + secp256k1_ge g; + random_group_element_test(&g); +#ifdef USE_ENDOMORPHISM + if (i >= runs - 2) { + secp256k1_ge_mul_lambda(&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]); + 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]); + 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); + } + } + + /* 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; + } + } + 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); + secp256k1_fe_inv_var(&zfi3, &zf); + secp256k1_fe_sqr(&zfi2, &zfi3); + secp256k1_fe_mul(&zfi3, &zfi3, &zfi2); + + for (i1 = 0; i1 < 1 + 4 * runs; i1++) { + int i2; + for (i2 = 0; i2 < 1 + 4 * runs; i2++) { + /* Compute reference result using gej + gej (var). */ + secp256k1_gej refj, resj; + secp256k1_ge ref; + secp256k1_fe zr; + secp256k1_gej_add_var(&refj, &gej[i1], &gej[i2], secp256k1_gej_is_infinity(&gej[i1]) ? NULL : &zr); + /* 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)); + } + 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); + 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)); + } + + /* Test gej + ge (var, with additional Z factor). */ + { + 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); + secp256k1_gej_add_zinv_var(&resj, &gej[i1], &ge2_zfi, &zf); + ge_equals_gej(&ref, &resj); + } + + /* 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); + } + + /* Test doubling (var). */ + if ((i1 == 0 && i2 == 0) || ((i1 + 3)/4 == (i2 + 3)/4 && ((i1 + 3)%4)/2 == ((i2 + 3)%4)/2)) { + secp256k1_fe zr2; + /* Normal doubling with Z ratio result. */ + secp256k1_gej_double_var(&resj, &gej[i1], &zr2); + ge_equals_gej(&ref, &resj); + /* Check Z ratio. */ + secp256k1_fe_mul(&zr2, &zr2, &gej[i1].z); + CHECK(secp256k1_fe_equal_var(&zr2, &resj.z)); + /* Normal doubling. */ + secp256k1_gej_double_var(&resj, &gej[i2], NULL); + ge_equals_gej(&ref, &resj); + } + + /* Test adding opposites. */ + if ((i1 == 0 && i2 == 0) || ((i1 + 3)/4 == (i2 + 3)/4 && ((i1 + 3)%4)/2 != ((i2 + 3)%4)/2)) { + CHECK(secp256k1_ge_is_infinity(&ref)); + } + + /* Test adding infinity. */ + if (i1 == 0) { + CHECK(secp256k1_ge_is_infinity(&ge[i1])); + CHECK(secp256k1_gej_is_infinity(&gej[i1])); + ge_equals_gej(&ref, &gej[i2]); + } + if (i2 == 0) { + CHECK(secp256k1_ge_is_infinity(&ge[i2])); + CHECK(secp256k1_gej_is_infinity(&gej[i2])); + ge_equals_gej(&ref, &gej[i1]); + } + } + } + + /* 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)); + 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); + if (swap != i) { + secp256k1_gej t = gej_shuffled[i]; + gej_shuffled[i] = gej_shuffled[swap]; + gej_shuffled[swap] = t; + } + } + for (i = 0; i < 4 * runs + 1; i++) { + secp256k1_gej_add_var(&sum, &sum, &gej_shuffled[i], NULL); + } + CHECK(secp256k1_gej_is_infinity(&sum)); + free(gej_shuffled); + } + + /* Test batch gej -> ge conversion with and 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); + for (i = 0; i < 4 * runs + 1; i++) { + secp256k1_fe s; + 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]); + } + free(ge_set_table); + free(ge_set_all); + free(zr); + } + + free(ge); + free(gej); + free(zinv); +} + +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 + * points would be negatives of each other and their sum is + * infinity. This is cool because it "covers up" any degeneracy + * in the addition algorithm that would cause the xy coordinates + * of the sum to be wrong (since infinity has no xy coordinates). + * HOWEVER, if the x-coordinates are different, infinity is the + * wrong answer, and such degeneracies are exposed. This is the + * root of https://github.com/bitcoin-core/secp256k1/issues/257 + * 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] + * + * # 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() + */ + secp256k1_gej aj = SECP256K1_GEJ_CONST( + 0x8d24cd95, 0x0a355af1, 0x3c543505, 0x44238d30, + 0x0643d79f, 0x05a59614, 0x2f8ec030, 0xd58977cb, + 0x001e337a, 0x38093dcd, 0x6c0f386d, 0x0b1293a8, + 0x4d72c879, 0xd7681924, 0x44e6d2f3, 0x9190117d + ); + secp256k1_gej bj = SECP256K1_GEJ_CONST( + 0xc7b74206, 0x1f788cd9, 0xabd0937d, 0x164a0d86, + 0x95f6ff75, 0xf19a4ce9, 0xd013bd7b, 0xbf92d2a7, + 0xffe1cc85, 0xc7f6c232, 0x93f0c792, 0xf4ed6c57, + 0xb28d3786, 0x2897e6db, 0xbb192d0b, 0x6e6feab2 + ); + secp256k1_gej sumj = SECP256K1_GEJ_CONST( + 0x671a63c0, 0x3efdad4c, 0x389a7798, 0x24356027, + 0xb3d69010, 0x278625c3, 0x5c86d390, 0x184a8f7a, + 0x5f6409c2, 0x2ce01f2b, 0x511fd375, 0x25071d08, + 0xda651801, 0x70e95caf, 0x8f0d893c, 0xbed8fbbe + ); + secp256k1_ge b; + secp256k1_gej resj; + secp256k1_ge res; + secp256k1_ge_set_gej(&b, &bj); + + secp256k1_gej_add_var(&resj, &aj, &bj, NULL); + secp256k1_ge_set_gej(&res, &resj); + ge_equals_gej(&res, &sumj); + + secp256k1_gej_add_ge(&resj, &aj, &b); + secp256k1_ge_set_gej(&res, &resj); + ge_equals_gej(&res, &sumj); + + secp256k1_gej_add_ge_var(&resj, &aj, &b, NULL); + secp256k1_ge_set_gej(&res, &resj); + ge_equals_gej(&res, &sumj); +} + +void run_ge(void) { + int i; + for (i = 0; i < count * 32; i++) { + test_ge(); + } + test_add_neg_y_diff_x(); +} + +void test_ec_combine(void) { + secp256k1_scalar sum = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); + secp256k1_pubkey data[6]; + const secp256k1_pubkey* d[6]; + secp256k1_pubkey sd; + secp256k1_pubkey sd2; + secp256k1_gej Qj; + secp256k1_ge Q; + int i; + for (i = 1; i <= 6; i++) { + secp256k1_scalar s; + random_scalar_order_test(&s); + secp256k1_scalar_add(&sum, &sum, &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_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); + } +} + +void run_ec_combine(void) { + int i; + for (i = 0; i < count * 8; i++) { + test_ec_combine(); + } +} + +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; + /* Return values of the above calls. */ + int res_quad, 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); + + if (res_quad) { + secp256k1_fe_normalize_var(&ge_quad.x); + 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 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) { + int i; + for (i = 0; i < count * 4; i++) { + secp256k1_fe fe; + random_fe_test(&fe); + test_group_decompress(&fe); + } +} + +/***** ECMULT TESTS *****/ + +void run_ecmult_chain(void) { + /* random starting point A (on the curve) */ + secp256k1_gej a = SECP256K1_GEJ_CONST( + 0x8b30bbe9, 0xae2a9906, 0x96b22f67, 0x0709dff3, + 0x727fd8bc, 0x04d3362c, 0x6c7bf458, 0xe2846004, + 0xa357ae91, 0x5c4a6528, 0x1309edf2, 0x0504740f, + 0x0eb33439, 0x90216b4f, 0x81063cb6, 0x5f2f7e0f + ); + /* two random initial factors xn and gn */ + secp256k1_scalar xn = SECP256K1_SCALAR_CONST( + 0x84cc5452, 0xf7fde1ed, 0xb4d38a8c, 0xe9b1b84c, + 0xcef31f14, 0x6e569be9, 0x705d357a, 0x42985407 + ); + secp256k1_scalar gn = SECP256K1_SCALAR_CONST( + 0xa1e58d22, 0x553dcd42, 0xb2398062, 0x5d4c57a9, + 0x6e9323d4, 0x2b3152e5, 0xca2c3990, 0xedc7c9de + ); + /* two small multipliers to be applied to xn and gn in every iteration: */ + 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); + /* actual points */ + secp256k1_gej x; + secp256k1_gej x2; + int i; + + /* the point being computed */ + x = a; + 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); + /* 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); + secp256k1_scalar_mul(&ge, &ge, &xn); + secp256k1_scalar_add(&ge, &ge, &gn); + /* modify xn and gn */ + secp256k1_scalar_mul(&xn, &xn, &xf); + secp256k1_scalar_mul(&gn, &gn, &gf); + + /* verify */ + if (i == 19999) { + /* expected result after 19999 iterations */ + secp256k1_gej rp = SECP256K1_GEJ_CONST( + 0xD6E96687, 0xF9B10D09, 0x2A6F3543, 0x9D86CEBE, + 0xA4535D0D, 0x409F5358, 0x6440BD74, 0xB933E830, + 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)); + } + } + /* 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)); +} + +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); + 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_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); + CHECK(secp256k1_eckey_pubkey_serialize(&res3, pub, &psize, 0) == 0); + 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_ge_set_gej(&res3, &res1); + CHECK(secp256k1_ge_is_infinity(&res3)); + secp256k1_ecmult(&ctx->ecmult_ctx, &res1, point, &one, &zero); + secp256k1_ge_set_gej(&res3, &res1); + ge_equals_gej(&res3, point); + secp256k1_ecmult(&ctx->ecmult_ctx, &res1, point, &zero, &one); + secp256k1_ge_set_gej(&res3, &res1); + ge_equals_ge(&res3, &secp256k1_ge_const_g); +} + +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( + 0x7603CB59, 0xB0EF6C63, 0xFE608479, 0x2A0C378C, + 0xDB3233A8, 0x0F8A9A09, 0xA877DEAD, 0x31B38C45 + ); + for (i = 0; i < 500; i++) { + secp256k1_ge p; + if (secp256k1_ge_set_xo_var(&p, &x, 1)) { + 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)); +} + +void ecmult_const_random_mult(void) { + /* random starting point A (on the curve) */ + secp256k1_ge a = SECP256K1_GE_CONST( + 0x6d986544, 0x57ff52b8, 0xcf1b8126, 0x5b802a5b, + 0xa97f9263, 0xb1e88044, 0x93351325, 0x91bc450a, + 0x535c59f7, 0x325e5d2b, 0xc391fbe8, 0x3c12787c, + 0x337e4a98, 0xe82a9011, 0x0123ba37, 0xdd769c7d + ); + /* random initial factor xn */ + secp256k1_scalar xn = SECP256K1_SCALAR_CONST( + 0x649d4f77, 0xc4242df7, 0x7f2079c9, 0x14530327, + 0xa31b876a, 0xd2d8ce2a, 0x2236d5c6, 0xd7b2029b + ); + /* expected xn * A (from sage) */ + secp256k1_ge expected_b = SECP256K1_GE_CONST( + 0x23773684, 0x4d209dc7, 0x098a786f, 0x20d06fcd, + 0x070a38bf, 0xc11ac651, 0x03004319, 0x1e2a8786, + 0xed8c3b8e, 0xc06dd57b, 0xd06ea66e, 0x45492b0f, + 0xb84e4e1b, 0xfb77e21f, 0x96baae2a, 0x63dec956 + ); + secp256k1_gej b; + secp256k1_ecmult_const(&b, &a, &xn); + + CHECK(secp256k1_ge_is_valid_var(&a)); + ge_equals_gej(&expected_b, &b); +} + +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); + + secp256k1_ecmult_const(&res1, &secp256k1_ge_const_g, &a); + secp256k1_ecmult_const(&res2, &secp256k1_ge_const_g, &b); + secp256k1_ge_set_gej(&mid1, &res1); + secp256k1_ge_set_gej(&mid2, &res2); + secp256k1_ecmult_const(&res1, &mid1, &b); + secp256k1_ecmult_const(&res2, &mid2, &a); + secp256k1_ge_set_gej(&mid1, &res1); + secp256k1_ge_set_gej(&mid2, &res2); + ge_equals_ge(&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); + secp256k1_scalar negone; + secp256k1_gej res1; + secp256k1_ge res2; + secp256k1_ge point; + secp256k1_scalar_negate(&negone, &one); + + 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); + secp256k1_ge_set_gej(&res2, &res1); + ge_equals_ge(&res2, &point); + secp256k1_ecmult_const(&res1, &point, &negone); + secp256k1_gej_neg(&res1, &res1); + secp256k1_ge_set_gej(&res2, &res1); + ge_equals_ge(&res2, &point); +} + +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, + 0xcd83d307, 0x827bed62, 0x05fad0ce, 0x18fae63b + ); + const secp256k1_gej expected_point = SECP256K1_GEJ_CONST( + 0x5494c15d, 0x32099706, 0xc2395f94, 0x348745fd, + 0x757ce30e, 0x4e8c90fb, 0xa2bad184, 0xf883c69f, + 0x5d195d20, 0xe191bf7f, 0x1be3e55f, 0x56a80196, + 0x6071ad01, 0xf1462f66, 0xc997fa94, 0xdb858435 + ); + secp256k1_gej point; + secp256k1_ge res; + int i; + + secp256k1_gej_set_ge(&point, &secp256k1_ge_const_g); + for (i = 0; i < 100; ++i) { + secp256k1_ge tmp; + secp256k1_ge_set_gej(&tmp, &point); + secp256k1_ecmult_const(&point, &tmp, &scalar); + } + secp256k1_ge_set_gej(&res, &point); + ge_equals_gej(&res, &expected_point); +} + +void run_ecmult_const_tests(void) { + ecmult_const_mult_zero_one(); + ecmult_const_random_mult(); + ecmult_const_commutativity(); + ecmult_const_chain_multiply(); +} + +void test_wnaf(const secp256k1_scalar *number, int w) { + secp256k1_scalar x, two, t; + int wnaf[256]; + int zeroes = -1; + int i; + int bits; + secp256k1_scalar_set_int(&x, 0); + secp256k1_scalar_set_int(&two, 2); + bits = secp256k1_ecmult_wnaf(wnaf, 256, number, w); + CHECK(bits <= 256); + for (i = bits-1; i >= 0; i--) { + int v = wnaf[i]; + secp256k1_scalar_mul(&x, &x, &two); + if (v) { + CHECK(zeroes == -1 || zeroes >= w-1); /* check that distance between non-zero elements is at least w-1 */ + zeroes=0; + CHECK((v & 1) == 1); /* check non-zero elements are odd */ + CHECK(v <= (1 << (w-1)) - 1); /* check range below */ + CHECK(v >= -(1 << (w-1)) - 1); /* check range above */ + } else { + CHECK(zeroes != -1); /* check that no unnecessary zero padding exists */ + zeroes++; + } + 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); + } + 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; + + if (!secp256k1_scalar_get_bits(&neg1, 0, 1)) { + secp256k1_scalar_negate(&neg1, &neg1); + sign1 = -1; + } + 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; + int wnaf[256] = {0}; + int i; + int skew; + secp256k1_scalar num = *number; + + 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); + } +#endif + skew = secp256k1_wnaf_const(wnaf, num, w); + + for (i = WNAF_SIZE(w); i >= 0; --i) { + secp256k1_scalar t; + 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 */ + + 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); + } + /* 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) { + 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); + /* Random tests */ + for (i = 0; i < count; i++) { + random_scalar_order(&n); + test_wnaf(&n, 4+(i%10)); + test_constant_wnaf_negate(&n); + test_constant_wnaf(&n, 4 + (i % 10)); + } + secp256k1_scalar_set_int(&n, 0); + CHECK(secp256k1_scalar_cond_negate(&n, 1) == -1); + CHECK(secp256k1_scalar_is_zero(&n)); + CHECK(secp256k1_scalar_cond_negate(&n, 0) == 1); + 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)); + } + for (i = 1; i <= 36; i++ ) { + secp256k1_scalar_set_int(&x, i); + 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); + } + CHECK(secp256k1_gej_is_infinity(&r)); + } +} + +void run_ecmult_constants(void) { + test_ecmult_constants(); +} + +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 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); + CHECK(!gej_xyz_equals_gej(&pgej, &pgej2)); + CHECK(!gej_xyz_equals_gej(&i, &ctx->ecmult_gen_ctx.initial)); + secp256k1_ge_set_gej(&pge, &pgej); + ge_equals_gej(&pge, &pgej2); +} + +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)); +} + +void run_ecmult_gen_blind(void) { + int i; + test_ecmult_gen_blind_reset(); + 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; + const unsigned char zero[32] = {0}; + unsigned char tmp[32]; + + random_scalar_order_test(&full); + secp256k1_scalar_split_lambda(&s1, &slam, &full); + + /* check that both are <= 128 bits in size */ + if (secp256k1_scalar_is_high(&s1)) { + secp256k1_scalar_negate(&s1, &s1); + } + if (secp256k1_scalar_is_high(&slam)) { + secp256k1_scalar_negate(&slam, &slam); + } + + secp256k1_scalar_get_b32(tmp, &s1); + CHECK(memcmp(zero, tmp, 16) == 0); + secp256k1_scalar_get_b32(tmp, &slam); + CHECK(memcmp(zero, tmp, 16) == 0); +} + +void run_endomorphism_tests(void) { + test_scalar_split(); +} +#endif + +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); + for (i = 0; i < 256; i++) { + /* Try all type bytes. */ + int xpass; + int ypass; + int ysign; + pubkeyc[0] = i; + /* What sign does this point have? */ + ysign = (input[63] & 1) + 2; + /* For the current type (i) do we expect parsing to work? Handled all of compressed/uncompressed/hybrid. */ + xpass = xvalid && (pubkeyclen == 33) && ((i & 254) == 2); + /* Do we expect a parse and re-serialize as uncompressed to give a matching y? */ + ypass = xvalid && yvalid && ((i & 4) == ((pubkeyclen == 65) << 2)) && + ((i == 4) || ((i & 251) == ysign)) && ((pubkeyclen == 33) || (pubkeyclen == 65)); + if (xpass || ypass) { + /* These cases must parse. */ + 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)); + outl = 65; + VG_UNDEF(pubkeyo, 65); + CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyo, &outl, &pubkey, SECP256K1_EC_COMPRESSED) == 1); + VG_CHECK(pubkeyo, outl); + CHECK(outl == 33); + CHECK(memcmp(&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); + memset(&pubkey, 0, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + secp256k1_pubkey_save(&pubkey, &ge); + VG_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); + CHECK(outl == 65); + CHECK(pubkeyo[0] == 4); + CHECK(memcmp(&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_context_set_illegal_callback(ctx, NULL, NULL); +} + +void run_ec_pubkey_parse_test(void) { +#define SECP256K1_EC_PARSE_TEST_NVALID (12) + const unsigned char valid[SECP256K1_EC_PARSE_TEST_NVALID][64] = { + { + /* Point with leading and trailing zeros in x and y serialization. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x52, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x64, 0xef, 0xa1, 0x7b, 0x77, 0x61, 0xe1, 0xe4, 0x27, 0x06, 0x98, 0x9f, 0xb4, 0x83, + 0xb8, 0xd2, 0xd4, 0x9b, 0xf7, 0x8f, 0xae, 0x98, 0x03, 0xf0, 0x99, 0xb8, 0x34, 0xed, 0xeb, 0x00 + }, + { + /* Point with x equal to a 3rd root of unity.*/ + 0x7a, 0xe9, 0x6a, 0x2b, 0x65, 0x7c, 0x07, 0x10, 0x6e, 0x64, 0x47, 0x9e, 0xac, 0x34, 0x34, 0xe9, + 0x9c, 0xf0, 0x49, 0x75, 0x12, 0xf5, 0x89, 0x95, 0xc1, 0x39, 0x6c, 0x28, 0x71, 0x95, 0x01, 0xee, + 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, + }, + { + /* Point with largest x. (1/2) */ + 0xff, 0xff, 0xff, 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, + 0x0e, 0x99, 0x4b, 0x14, 0xea, 0x72, 0xf8, 0xc3, 0xeb, 0x95, 0xc7, 0x1e, 0xf6, 0x92, 0x57, 0x5e, + 0x77, 0x50, 0x58, 0x33, 0x2d, 0x7e, 0x52, 0xd0, 0x99, 0x5c, 0xf8, 0x03, 0x88, 0x71, 0xb6, 0x7d, + }, + { + /* Point with largest x. (2/2) */ + 0xff, 0xff, 0xff, 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, + 0xf1, 0x66, 0xb4, 0xeb, 0x15, 0x8d, 0x07, 0x3c, 0x14, 0x6a, 0x38, 0xe1, 0x09, 0x6d, 0xa8, 0xa1, + 0x88, 0xaf, 0xa7, 0xcc, 0xd2, 0x81, 0xad, 0x2f, 0x66, 0xa3, 0x07, 0xfb, 0x77, 0x8e, 0x45, 0xb2, + }, + { + /* Point with smallest x. (1/2) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, + 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, + }, + { + /* Point with smallest x. (2/2) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, + 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, + }, + { + /* Point with largest y. (1/3) */ + 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, 0x2e, + }, + { + /* Point with largest y. (2/3) */ + 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, + 0xff, 0xff, 0xff, 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, 0x2e, + }, + { + /* Point with largest y. (3/3) */ + 0x14, 0x6d, 0x3b, 0x65, 0xad, 0xd9, 0xf5, 0x4c, 0xcc, 0xa2, 0x85, 0x33, 0xc8, 0x8e, 0x2c, 0xbc, + 0x63, 0xf7, 0x44, 0x3e, 0x16, 0x58, 0x78, 0x3a, 0xb4, 0x1f, 0x8e, 0xf9, 0x7c, 0x2a, 0x10, 0xb5, + 0xff, 0xff, 0xff, 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, 0x2e, + }, + { + /* Point with smallest y. (1/3) */ + 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, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, + }, + { + /* Point with smallest y. (2/3) */ + 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, 0x01, + }, + { + /* Point with smallest y. (3/3) */ + 0x14, 0x6d, 0x3b, 0x65, 0xad, 0xd9, 0xf5, 0x4c, 0xcc, 0xa2, 0x85, 0x33, 0xc8, 0x8e, 0x2c, 0xbc, + 0x63, 0xf7, 0x44, 0x3e, 0x16, 0x58, 0x78, 0x3a, 0xb4, 0x1f, 0x8e, 0xf9, 0x7c, 0x2a, 0x10, 0xb5, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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 + } + }; +#define SECP256K1_EC_PARSE_TEST_NXVALID (4) + const unsigned char onlyxvalid[SECP256K1_EC_PARSE_TEST_NXVALID][64] = { + { + /* Valid if y overflow ignored (y = 1 mod p). (1/3) */ + 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, 0x30, + }, + { + /* Valid if y overflow ignored (y = 1 mod p). (2/3) */ + 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, + 0xff, 0xff, 0xff, 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, + }, + { + /* Valid if y overflow ignored (y = 1 mod p). (3/3)*/ + 0x14, 0x6d, 0x3b, 0x65, 0xad, 0xd9, 0xf5, 0x4c, 0xcc, 0xa2, 0x85, 0x33, 0xc8, 0x8e, 0x2c, 0xbc, + 0x63, 0xf7, 0x44, 0x3e, 0x16, 0x58, 0x78, 0x3a, 0xb4, 0x1f, 0x8e, 0xf9, 0x7c, 0x2a, 0x10, 0xb5, + 0xff, 0xff, 0xff, 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, + }, + { + /* x on curve, y is from y^2 = x^3 + 8. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 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 + } + }; +#define SECP256K1_EC_PARSE_TEST_NINVALID (7) + const unsigned char invalid[SECP256K1_EC_PARSE_TEST_NINVALID][64] = { + { + /* x is third root of -8, y is -1 * (x^3+7); also on the curve for y^2 = x^3 + 9. */ + 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, 0x01, + }, + { + /* Valid if x overflow ignored (x = 1 mod p). */ + 0xff, 0xff, 0xff, 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, + 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, + }, + { + /* Valid if x overflow ignored (x = 1 mod p). */ + 0xff, 0xff, 0xff, 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, + 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, + }, + { + /* x is -1, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 5. */ + 0xff, 0xff, 0xff, 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, 0x2e, + 0xf4, 0x84, 0x14, 0x5c, 0xb0, 0x14, 0x9b, 0x82, 0x5d, 0xff, 0x41, 0x2f, 0xa0, 0x52, 0xa8, 0x3f, + 0xcb, 0x72, 0xdb, 0x61, 0xd5, 0x6f, 0x37, 0x70, 0xce, 0x06, 0x6b, 0x73, 0x49, 0xa2, 0xaa, 0x28, + }, + { + /* x is -1, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 5. */ + 0xff, 0xff, 0xff, 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, 0x2e, + 0x0b, 0x7b, 0xeb, 0xa3, 0x4f, 0xeb, 0x64, 0x7d, 0xa2, 0x00, 0xbe, 0xd0, 0x5f, 0xad, 0x57, 0xc0, + 0x34, 0x8d, 0x24, 0x9e, 0x2a, 0x90, 0xc8, 0x8f, 0x31, 0xf9, 0x94, 0x8b, 0xb6, 0x5d, 0x52, 0x07, + }, + { + /* x is zero, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 7. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8f, 0x53, 0x7e, 0xef, 0xdf, 0xc1, 0x60, 0x6a, 0x07, 0x27, 0xcd, 0x69, 0xb4, 0xa7, 0x33, 0x3d, + 0x38, 0xed, 0x44, 0xe3, 0x93, 0x2a, 0x71, 0x79, 0xee, 0xcb, 0x4b, 0x6f, 0xba, 0x93, 0x60, 0xdc, + }, + { + /* x is zero, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 7. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0xac, 0x81, 0x10, 0x20, 0x3e, 0x9f, 0x95, 0xf8, 0xd8, 0x32, 0x96, 0x4b, 0x58, 0xcc, 0xc2, + 0xc7, 0x12, 0xbb, 0x1c, 0x6c, 0xd5, 0x8e, 0x86, 0x11, 0x34, 0xb4, 0x8f, 0x45, 0x6c, 0x9b, 0x53 + } + }; + const unsigned char pubkeyc[66] = { + /* Serialization of G. */ + 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, 0x00 + }; + unsigned char sout[65]; + unsigned char shortkey[2]; + 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); + /* 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); + /* 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); + } + /* 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); + } + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(&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); + /* 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); + /* 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); + /* 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); + /* 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); + /* 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_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(len == 0); + CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, NULL, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 0); + CHECK(ecount == 2); + 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); + CHECK(len == 0); + len = 65; + CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, &len, &pubkey, ~0) == 0); + CHECK(ecount == 4); + 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); + 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); + /* 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); + } + for (i = 0; i < SECP256K1_EC_PARSE_TEST_NXVALID; i++) { + ec_pubkey_parse_pointtest(onlyxvalid[i], 1, 0); + } + for (i = 0; i < SECP256K1_EC_PARSE_TEST_NINVALID; i++) { + ec_pubkey_parse_pointtest(invalid[i], 0, 0); + } +} + +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, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41 + }; + const unsigned char zeros[sizeof(secp256k1_pubkey)] = {0x00}; + unsigned char ctmp[33]; + unsigned char ctmp2[33]; + secp256k1_pubkey pubkey; + secp256k1_pubkey pubkey2; + secp256k1_pubkey pubkey_one; + 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); + /* Maximum value is too large, reject. */ + memset(ctmp, 255, 32); + 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); + /* Zero is too small, reject. */ + memset(ctmp, 0, 32); + 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); + /* One must be accepted. */ + ctmp[31] = 0x01; + 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); + 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); + 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); + /* -1 must be accepted. */ + ctmp[31] = 0x40; + 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); + pubkey_negone = pubkey; + /* Tweak of zero leaves the value changed. */ + memset(ctmp2, 0, 32); + CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, ctmp2) == 1); + CHECK(memcmp(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); + /* 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); + memcpy(&pubkey, &pubkey2, sizeof(pubkey)); + /* Overflowing key tweak zeroizes. */ + memcpy(ctmp, orderc, 32); + ctmp[31] = 0x40; + CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, orderc) == 0); + CHECK(memcmp(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); + memcpy(ctmp, orderc, 32); + ctmp[31] = 0x40; + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, orderc) == 0); + CHECK(memcmp(&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); + memcpy(&pubkey, &pubkey2, sizeof(pubkey)); + /* Private key tweaks results in a key of zero. */ + ctmp2[31] = 1; + CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp2, ctmp) == 0); + CHECK(memcmp(zeros, ctmp2, 32) == 0); + ctmp2[31] = 1; + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 0); + CHECK(memcmp(&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); + ctmp2[31] = 2; + 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); + /* Tweak mul * 2 = 1+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); + /* 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); + 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); + /* 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; + 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; + 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; + 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; + 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); + 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); + /* 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 *)); + 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); + 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); + 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); + 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); + /* 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); + /* 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); + 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); + /* 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); +} + +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)); +} + +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); + secp256k1_ge_set_gej(&pub, &pubj); + getrec = secp256k1_rand_bits(1); + random_sign(&sigr, &sigs, &key, &msg, getrec?&recid:NULL); + if (getrec) { + CHECK(recid >= 0 && recid < 4); + } + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &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)); +} + +void run_ecdsa_sign_verify(void) { + int i; + for (i = 0; i < 10*count; i++) { + test_ecdsa_sign_verify(); + } +} + +/** Dummy nonce generation function that just uses a precomputed nonce, and fails if it is not accepted. Use only for testing. */ +static int precomputed_nonce_function(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + (void)msg32; + (void)key32; + (void)algo16; + memcpy(nonce32, data, 32); + return (counter == 0); +} + +static int nonce_function_test_fail(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + /* Dummy nonce generator that has a fatal error on the first counter value. */ + if (counter == 0) { + return 0; + } + return nonce_function_rfc6979(nonce32, msg32, key32, algo16, data, counter - 1); +} + +static int nonce_function_test_retry(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + /* Dummy nonce generator that produces unacceptable nonces for the first several counter values. */ + if (counter < 3) { + memset(nonce32, counter==0 ? 0 : 255, 32); + if (counter == 2) { + nonce32[31]--; + } + return 1; + } + if (counter < 5) { + static const unsigned char order[] = { + 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 + }; + memcpy(nonce32, order, 32); + if (counter == 4) { + nonce32[31]++; + } + return 1; + } + /* Retry rate of 6979 is negligible esp. as we only call this in deterministic tests. */ + /* If someone does fine a case where it retries for secp256k1, we'd like to know. */ + if (counter > 5) { + return 0; + } + return nonce_function_rfc6979(nonce32, msg32, key32, algo16, data, counter - 5); +} + +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; +} + +void test_ecdsa_end_to_end(void) { + unsigned char extra[32] = {0x00}; + unsigned char privkey[32]; + unsigned char message[32]; + unsigned char privkey2[32]; + secp256k1_ecdsa_signature signature[6]; + secp256k1_scalar r, s; + unsigned char sig[74]; + size_t siglen = 74; + unsigned char pubkeyc[65]; + size_t pubkeyclen = 65; + secp256k1_pubkey pubkey; + secp256k1_pubkey pubkey_tmp; + unsigned char seckey[300]; + size_t seckeylen = 300; + + /* Generate a random key and message. */ + { + secp256k1_scalar msg, key; + random_scalar_order_test(&msg); + 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); + + /* 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)); + memset(&pubkey, 0, sizeof(pubkey)); + 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); + + /* 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); + + /* Optionally tweak the keys using addition. */ + if (secp256k1_rand_int(3) == 0) { + int ret1; + int ret2; + unsigned char rnd[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); + CHECK(ret1 == ret2); + if (ret1 == 0) { + return; + } + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, privkey) == 1); + CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); + } + + /* Optionally tweak the keys using multiplication. */ + if (secp256k1_rand_int(3) == 0) { + int ret1; + int ret2; + unsigned char rnd[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); + CHECK(ret1 == ret2); + if (ret1 == 0) { + return; + } + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, privkey) == 1); + CHECK(memcmp(&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); + extra[31] = 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); + /* 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); + /* 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]); + 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); + 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); + + /* Serialize/parse DER and verify again */ + 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); + /* 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); +} + +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); + } + if (len == 65) { + in[0] = secp256k1_rand_bits(1) ? 4 : (secp256k1_rand_bits(1) ? 6 : 7); + } else { + in[0] = secp256k1_rand_bits(1) ? 2 : 3; + } + if (secp256k1_rand_bits(3) == 0) { + in[0] = secp256k1_rand_bits(8); + } + if (len > 1) { + secp256k1_rand256(&in[1]); + } + if (len > 33) { + secp256k1_rand256(&in[33]); + } + if (secp256k1_eckey_pubkey_parse(&elem, in, len)) { + unsigned char out[65]; + unsigned char firstb; + int res; + size_t size = len; + firstb = in[0]; + /* 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); + /* ... except for the type of hybrid inputs. */ + if ((in[0] != 6) && (in[0] != 7)) { + CHECK(in[0] == out[0]); + } + size = 65; + 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 that the X9.62 hybrid type is checked. */ + in[0] = secp256k1_rand_bits(1) ? 6 : 7; + res = secp256k1_eckey_pubkey_parse(&elem2, in, size); + if (firstb == 2 || firstb == 3) { + if (in[0] == firstb + 4) { + CHECK(res); + } else { + CHECK(!res); + } + } + if (res) { + ge_equals_ge(&elem,&elem2); + CHECK(secp256k1_eckey_pubkey_serialize(&elem, out, &size, 0)); + CHECK(memcmp(&in[1], &out[1], 64) == 0); + } + } +} + +void run_random_pubkeys(void) { + int i; + for (i = 0; i < 10*count; i++) { + test_random_pubkeys(); + } +} + +void run_ecdsa_end_to_end(void) { + int 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 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; + + secp256k1_ecdsa_signature sig_der; + unsigned char roundtrip_der[2048]; + unsigned char compact_der[64]; + size_t len_der = 2048; + int parsed_der = 0, valid_der = 0, roundtrips_der = 0; + + secp256k1_ecdsa_signature sig_der_lax; + unsigned char roundtrip_der_lax[2048]; + unsigned char compact_der_lax[64]; + 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); + 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); + } + 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; + } + + 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); + } + 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; + } + + if (certainly_der) { + ret |= (!parsed_der) << 2; + } + if (certainly_not_der) { + ret |= (parsed_der) << 17; + } + if (valid_der) { + ret |= (!roundtrips_der) << 3; + } + + 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 |= (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; +} + +static void assign_big_endian(unsigned char *ptr, size_t ptrlen, uint32_t val) { + size_t i; + for (i = 0; i < ptrlen; i++) { + int shift = ptrlen - 1 - i; + if (shift >= 4) { + ptr[i] = 0; + } else { + ptr[i] = (val >> shift) & 0xFF; + } + } +} + +static void damage_array(unsigned char *sig, size_t *len) { + int pos; + int action = secp256k1_rand_bits(3); + if (action < 1 && *len > 3) { + /* Delete a byte. */ + pos = secp256k1_rand_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); + memmove(sig + pos + 1, sig + pos, *len - pos); + sig[pos] = secp256k1_rand_bits(8); + (*len)++; + return; + } else if (action < 4) { + /* Modify a byte. */ + sig[secp256k1_rand_int(*len)] += 1 + secp256k1_rand_int(255); + return; + } else { /* action < 8 */ + /* Modify a bit. */ + sig[secp256k1_rand_int(*len)] ^= 1 << secp256k1_rand_bits(3); + return; + } +} + +static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly_der, int* certainly_not_der) { + int der; + int nlow[2], nlen[2], nlenlen[2], nhbit[2], nhbyte[2], nzlen[2]; + size_t tlen, elen, glen; + int indet; + int n; + + *len = 0; + der = secp256k1_rand_bits(2) == 0; + *certainly_der = der; + *certainly_not_der = 0; + indet = der ? 0 : secp256k1_rand_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); + /* 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; + 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)); + /* 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)); + /* 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); + if (nzlen[n] > ((nlen[n] == 0 || nhbit[n]) ? 1 : 0)) { + *certainly_not_der = 1; + } + CHECK(nlen[n] + nzlen[n] <= 300); + /* The length of the length descriptor for the number. 0 means short encoding, anything else is long encoding. */ + 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; + nlenlen[n] += add; + if (add != 0) { + *certainly_not_der = 1; + } + } + CHECK(nlen[n] + nzlen[n] + nlenlen[n] <= 427); + } + + /* The total length of the data to go, so far */ + tlen = 2 + nlenlen[0] + nlen[0] + nzlen[0] + 2 + nlenlen[1] + nlen[1] + nzlen[1]; + 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; + if (elen != 0) { + *certainly_not_der = 1; + } + tlen += elen; + 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; + if (glen != 0) { + *certainly_not_der = 1; + } + CHECK(tlen + glen <= 990); + + /* Write the tuple header. */ + sig[(*len)++] = 0x30; + if (indet) { + /* Indeterminate length */ + sig[(*len)++] = 0x80; + *certainly_not_der = 1; + } 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; + tlenlen += add; + if (add != 0) { + *certainly_not_der = 1; + } + } + if (tlenlen == 0) { + /* Short length notation */ + sig[(*len)++] = tlen; + } else { + /* Long length notation */ + sig[(*len)++] = 128 + tlenlen; + assign_big_endian(sig + *len, tlenlen, tlen); + *len += tlenlen; + } + tlen += tlenlen; + } + tlen += 2; + CHECK(tlen + glen <= 1119); + + for (n = 0; n < 2; n++) { + /* Write the integer header. */ + sig[(*len)++] = 0x02; + if (nlenlen[n] == 0) { + /* Short length notation */ + sig[(*len)++] = nlen[n] + nzlen[n]; + } else { + /* Long length notation. */ + sig[(*len)++] = 128 + nlenlen[n]; + assign_big_endian(sig + *len, nlenlen[n], nlen[n] + nzlen[n]); + *len += nlenlen[n]; + } + /* Write zero padding */ + while (nzlen[n] > 0) { + sig[(*len)++] = 0x00; + nzlen[n]--; + } + if (nlen[n] == 32 && !nlow[n]) { + /* Special extra 16 0xFF bytes in "high" 32-byte numbers */ + int i; + for (i = 0; i < 16; i++) { + sig[(*len)++] = 0xFF; + } + nlen[n] -= 16; + } + /* Write first byte of number */ + if (nlen[n] > 0) { + sig[(*len)++] = nhbyte[n]; + nlen[n]--; + } + /* Generate remaining random bytes of number */ + secp256k1_rand_bytes_test(sig + *len, nlen[n]); + *len += nlen[n]; + nlen[n] = 0; + } + + /* Generate random garbage inside tuple. */ + secp256k1_rand_bytes_test(sig + *len, elen); + *len += elen; + + /* Generate end-of-contents bytes. */ + if (indet) { + sig[(*len)++] = 0; + sig[(*len)++] = 0; + tlen += 2; + } + CHECK(tlen + glen <= 1121); + + /* Generate random garbage outside tuple. */ + secp256k1_rand_bytes_test(sig + *len, glen); + *len += glen; + tlen += glen; + CHECK(tlen <= 1121); + CHECK(tlen == *len); +} + +void run_ecdsa_der_parse(void) { + int i,j; + for (i = 0; i < 200 * count; i++) { + unsigned char buffer[2048]; + size_t buflen = 0; + int certainly_der = 0; + int certainly_not_der = 0; + random_ber_signature(buffer, &buflen, &certainly_der, &certainly_not_der); + CHECK(buflen <= 2048); + for (j = 0; j < 16; j++) { + int ret = 0; + if (j > 0) { + damage_array(buffer, &buflen); + /* We don't know anything anymore about the DERness of the result */ + certainly_der = 0; + certainly_not_der = 0; + } + ret = test_ecdsa_der_parse(buffer, buflen, certainly_der, certainly_not_der); + if (ret != 0) { + size_t k; + fprintf(stderr, "Failure %x on ", ret); + for (k = 0; k < buflen; k++) { + fprintf(stderr, "%02x ", buffer[k]); + } + fprintf(stderr, "\n"); + } + CHECK(ret == 0); + } + } +} + +/* Tests several edge cases. */ +void test_ecdsa_edge_cases(void) { + int t; + secp256k1_ecdsa_signature sig; + + /* Test the case where ECDSA recomputes a point that is infinity. */ + { + secp256k1_gej keyj; + secp256k1_ge key; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 1); + 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_ge_set_gej(&key, &keyj); + msg = ss; + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + } + + /* Verify signature with r of zero fails. */ + { + const unsigned char pubkey_mods_zero[33] = { + 0x02, 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 + }; + secp256k1_ge key; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 1); + 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); + } + + /* Verify signature with s of zero fails. */ + { + const unsigned char pubkey[33] = { + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 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 + }; + secp256k1_ge key; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 0); + 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); + } + + /* Verify signature with message 0 passes. */ + { + const unsigned char pubkey[33] = { + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 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 + }; + const unsigned char pubkey2[33] = { + 0x02, 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 + }; + secp256k1_ge key; + secp256k1_ge key2; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 2); + secp256k1_scalar_set_int(&msg, 0); + 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); + 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); + 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); + } + + /* Verify signature with message 1 passes. */ + { + const unsigned char pubkey[33] = { + 0x02, 0x14, 0x4e, 0x5a, 0x58, 0xef, 0x5b, 0x22, + 0x6f, 0xd2, 0xe2, 0x07, 0x6a, 0x77, 0xcf, 0x05, + 0xb4, 0x1d, 0xe7, 0x4a, 0x30, 0x98, 0x27, 0x8c, + 0x93, 0xe6, 0xe6, 0x3c, 0x0b, 0xc4, 0x73, 0x76, + 0x25 + }; + const unsigned char pubkey2[33] = { + 0x02, 0x8a, 0xd5, 0x37, 0xed, 0x73, 0xd9, 0x40, + 0x1d, 0xa0, 0x33, 0xd2, 0xdc, 0xf0, 0xaf, 0xae, + 0x34, 0xcf, 0x5f, 0x96, 0x4c, 0x73, 0x28, 0x0f, + 0x92, 0xc0, 0xf6, 0x9d, 0xd9, 0xb2, 0x09, 0x10, + 0x62 + }; + const unsigned char csr[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x45, 0x51, 0x23, 0x19, 0x50, 0xb7, 0x5f, 0xc4, + 0x40, 0x2d, 0xa1, 0x72, 0x2f, 0xc9, 0xba, 0xeb + }; + secp256k1_ge key; + secp256k1_ge key2; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 1); + secp256k1_scalar_set_int(&msg, 1); + 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); + 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); + 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); + } + + /* Verify signature with message -1 passes. */ + { + const unsigned char pubkey[33] = { + 0x03, 0xaf, 0x97, 0xff, 0x7d, 0x3a, 0xf6, 0xa0, + 0x02, 0x94, 0xbd, 0x9f, 0x4b, 0x2e, 0xd7, 0x52, + 0x28, 0xdb, 0x49, 0x2a, 0x65, 0xcb, 0x1e, 0x27, + 0x57, 0x9c, 0xba, 0x74, 0x20, 0xd5, 0x1d, 0x20, + 0xf1 + }; + const unsigned char csr[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x45, 0x51, 0x23, 0x19, 0x50, 0xb7, 0x5f, 0xc4, + 0x40, 0x2d, 0xa1, 0x72, 0x2f, 0xc9, 0xba, 0xee + }; + secp256k1_ge key; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 1); + secp256k1_scalar_set_int(&msg, 1); + 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); + secp256k1_scalar_negate(&ss, &ss); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &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); + } + + /* 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, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }; + static const unsigned char nonce2[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 + }; + const unsigned char key[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 msg[32] = { + 0x86, 0x41, 0x99, 0x81, 0x06, 0x23, 0x44, 0x53, + 0xaa, 0x5f, 0x9d, 0x6a, 0x31, 0x78, 0xf4, 0xf7, + 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); + 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); + /* 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); + 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); + 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); + 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); + } + + /* Nonce function corner cases. */ + for (t = 0; t < 2; t++) { + static const unsigned char zero[32] = {0x00}; + int i; + unsigned char key[32]; + unsigned char msg[32]; + secp256k1_ecdsa_signature sig2; + secp256k1_scalar sr[512], ss; + const unsigned char *extra; + extra = t == 0 ? NULL : zero; + memset(msg, 0, 32); + 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(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(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(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(!is_empty_signature(&sig)); + 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); + /* The default nonce function is deterministic. */ + CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, NULL, extra) == 1); + CHECK(!is_empty_signature(&sig2)); + CHECK(memcmp(&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(!is_empty_signature(&sig2)); + secp256k1_ecdsa_signature_load(ctx, &sr[i], &ss, &sig2); + for (j = 0; j < i; j++) { + CHECK(!secp256k1_scalar_eq(&sr[i], &sr[j])); + } + } + msg[0] = 0; + msg[31] = 2; + /* The default nonce function changes output with different keys. */ + for(i = 256; i < 512; i++) { + int j; + key[0] = i - 256; + 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); + for (j = 0; j < i; j++) { + CHECK(!secp256k1_scalar_eq(&sr[i], &sr[j])); + } + } + key[0] = 0; + } + + { + /* Check that optional nonce arguments do not have equivalent effect. */ + const unsigned char zeros[32] = {0}; + unsigned char nonce[32]; + 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); + CHECK(nonce_function_rfc6979(nonce, zeros, zeros, NULL, NULL, 0) == 1); + VG_CHECK(nonce,32); + CHECK(nonce_function_rfc6979(nonce2, zeros, zeros, zeros, NULL, 0) == 1); + VG_CHECK(nonce2,32); + CHECK(nonce_function_rfc6979(nonce3, zeros, zeros, NULL, (void *)zeros, 0) == 1); + VG_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); + } + + + /* Privkey export where pubkey is the point at infinity. */ + { + unsigned char privkey[300]; + unsigned char seckey[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, + }; + size_t outlen = 300; + CHECK(!ec_privkey_export_der(ctx, privkey, &outlen, seckey, 0)); + outlen = 300; + CHECK(!ec_privkey_export_der(ctx, privkey, &outlen, seckey, 1)); + } +} + +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; +} + +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)); + + 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); + + EC_KEY_free(ec_key); +} + +void run_ecdsa_openssl(void) { + int i; + for (i = 0; i < 10*count; i++) { + test_ecdsa_openssl(); + } +} +#endif + +#ifdef ENABLE_MODULE_ECDH +# include "modules/ecdh/tests_impl.h" +#endif + +#ifdef ENABLE_MODULE_RECOVERY +# include "modules/recovery/tests_impl.h" +#endif + +int main(int argc, char **argv) { + unsigned char seed16[16] = {0}; + unsigned char run32[32] = {0}; + /* 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++; + } + } 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; + } + fclose(frand); + } + secp256k1_rand_seed(seed16); + + 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]); + + /* 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)); + } + + run_rand_bits(); + run_rand_int(); + + run_sha256_tests(); + run_hmac_sha256_tests(); + run_rfc6979_hmac_sha256_tests(); + +#ifndef USE_NUM_NONE + /* num tests */ + run_num_smalltests(); +#endif + + /* scalar tests */ + run_scalar_tests(); + + /* field tests */ + run_field_inv(); + run_field_inv_var(); + run_field_inv_all_var(); + run_field_misc(); + run_field_convert(); + run_sqr(); + run_sqrt(); + + /* group tests */ + run_ge(); + run_group_decompress(); + + /* ecmult tests */ + run_wnaf(); + run_point_times_order(); + run_ecmult_chain(); + run_ecmult_constants(); + run_ecmult_gen_blind(); + run_ecmult_const_tests(); + run_ec_combine(); + + /* endomorphism tests */ +#ifdef USE_ENDOMORPHISM + run_endomorphism_tests(); +#endif + + /* EC point parser test */ + run_ec_pubkey_parse_test(); + + /* EC key edge cases */ + run_eckey_edge_case_test(); + +#ifdef ENABLE_MODULE_ECDH + /* ecdh tests */ + run_ecdh_tests(); +#endif + + /* ecdsa tests */ + 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 + +#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]); + + /* shutdown */ + secp256k1_context_destroy(ctx); + + printf("no problems found\n"); + return 0; +} diff --git a/src/cryptoconditions/src/include/secp256k1/src/tests_exhaustive.c b/src/cryptoconditions/src/include/secp256k1/src/tests_exhaustive.c new file mode 100644 index 000000000..b040bb073 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/tests_exhaustive.c @@ -0,0 +1,470 @@ +/*********************************************************************** + * Copyright (c) 2016 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#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" + +#ifdef ENABLE_MODULE_RECOVERY +#include "src/modules/recovery/main_impl.h" +#include "include/secp256k1_recovery.h" +#endif + +/** 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)); +} + +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 random_fe(secp256k1_fe *x) { + unsigned char bin[32]; + do { + secp256k1_rand256(bin); + if (secp256k1_fe_set_b32(x, bin)) { + return; + } + } while(1); +} +/** END stolen from tests.c */ + +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; + int *idata = data; + (void)msg32; + (void)key32; + (void)algo16; + /* Some nonces cannot be used because they'd cause s and/or r to be zero. + * The signing function has retry logic here that just re-calls the nonce + * function with an increased `attempt`. So if attempt > 0 this means we + * need to change the nonce to avoid an infinite loop. */ + if (attempt > 0) { + *idata = (*idata + 1) % EXHAUSTIVE_TEST_ORDER; + } + secp256k1_scalar_set_int(&s, *idata); + secp256k1_scalar_get_b32(nonce32, &s); + return 1; +} + +#ifdef USE_ENDOMORPHISM +void test_exhaustive_endomorphism(const secp256k1_ge *group, int order) { + int i; + for (i = 0; i < order; i++) { + secp256k1_ge res; + secp256k1_ge_mul_lambda(&res, &group[i]); + ge_equals_ge(&group[i * EXHAUSTIVE_TEST_LAMBDA % EXHAUSTIVE_TEST_ORDER], &res); + } +} +#endif + +void test_exhaustive_addition(const secp256k1_ge *group, const secp256k1_gej *groupj, int order) { + int i, j; + + /* 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++) { + CHECK(!secp256k1_ge_is_infinity(&group[i])); + CHECK(!secp256k1_gej_is_infinity(&groupj[i])); + } + + /* Check all addition formulae */ + for (j = 0; j < order; j++) { + secp256k1_fe fe_inv; + secp256k1_fe_inv(&fe_inv, &groupj[j].z); + for (i = 0; i < 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); + /* add_ge */ + if (j > 0) { + secp256k1_gej_add_ge(&tmp, &groupj[i], &group[j]); + ge_equals_gej(&group[(i + j) % order], &tmp); + } + /* add_ge_var */ + secp256k1_gej_add_ge_var(&tmp, &groupj[i], &group[j], NULL); + ge_equals_gej(&group[(i + j) % order], &tmp); + /* 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 doubling */ + for (i = 0; i < 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_var(&tmp, &groupj[i], NULL); + ge_equals_gej(&group[(2 * i) % order], &tmp); + } + + /* Check negation */ + for (i = 1; i < order; i++) { + secp256k1_ge tmp; + secp256k1_gej tmpj; + secp256k1_ge_neg(&tmp, &group[i]); + ge_equals_ge(&group[order - i], &tmp); + secp256k1_gej_neg(&tmpj, &groupj[i]); + ge_equals_gej(&group[order - i], &tmpj); + } +} + +void test_exhaustive_ecmult(const secp256k1_context *ctx, const secp256k1_ge *group, const secp256k1_gej *groupj, int order) { + 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++) { + 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); + + if (i > 0) { + secp256k1_ecmult_const(&tmp, &group[i], &ng); + ge_equals_gej(&group[(i * j) % order], &tmp); + } + } + } + } +} + +void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k) { + 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); +} + +void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *group, int order) { + 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_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 k, should_verify; + unsigned char msg32[32]; + + 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); + + /* 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); + + /* Verify by calling verify */ + secp256k1_ecdsa_signature_save(&sig, &r_s, &s_s); + memcpy(&nonconst_ge, &group[sk_s], sizeof(nonconst_ge)); + secp256k1_pubkey_save(&pk, &nonconst_ge); + secp256k1_scalar_get_b32(msg32, &msg_s); + CHECK(should_verify == + secp256k1_ecdsa_verify(ctx, &sig, msg32, &pk)); + } + } + } + } +} + +void test_exhaustive_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_ecdsa_signature sig; + secp256k1_scalar sk, msg, r, s, expected_r; + unsigned char sk32[32], msg32[32]; + 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(ctx, &sig, msg32, sk32, secp256k1_nonce_function_smallint, &k); + + 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); + + /* Overflow means we've tried every possible nonce */ + if (k < starting_k) { + break; + } + } + } + } + + /* We would like to verify zero-knowledge here by counting how often every + * possible (s, r) tuple appears, but because the group order is larger + * than the field order, when coercing the x-values to scalar values, some + * appear more often than others, so we are actually not zero-knowledge. + * (This effect also appears in the real code, but the difference is on the + * order of 1/2^128th the field order, so the deviation is not useful to a + * computationally bounded attacker.) + */ +} + +#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); + + 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); + 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); + + /* 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); + + /* Overflow means we've tried every possible nonce */ + if (k < starting_k) { + break; + } + } + } + } +} + +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]; + + 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 < 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); + + /* 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)); + } + } + } + } +} +#endif + +int main(void) { + int i; + secp256k1_gej groupj[EXHAUSTIVE_TEST_ORDER]; + secp256k1_ge group[EXHAUSTIVE_TEST_ORDER]; + + /* 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 */ + + /* 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)); + } + } + + /* 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); + +#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); + return 0; +} + diff --git a/src/cryptoconditions/src/include/secp256k1/src/util.h b/src/cryptoconditions/src/include/secp256k1/src/util.h new file mode 100644 index 000000000..b0441d8e3 --- /dev/null +++ b/src/cryptoconditions/src/include/secp256k1/src/util.h @@ -0,0 +1,113 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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 +#include + +typedef struct { + void (*fn)(const char *text, void* data); + const void* data; +} secp256k1_callback; + +static SECP256K1_INLINE void secp256k1_callback_call(const secp256k1_callback * const cb, const char * const text) { + cb->fn(text, (void*)cb->data); +} + +#ifdef DETERMINISTIC +#define TEST_FAILURE(msg) do { \ + fprintf(stderr, "%s\n", msg); \ + abort(); \ +} while(0); +#else +#define TEST_FAILURE(msg) do { \ + fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, msg); \ + abort(); \ +} while(0) +#endif + +#ifdef HAVE_BUILTIN_EXPECT +#define EXPECT(x,c) __builtin_expect((x),(c)) +#else +#define EXPECT(x,c) (x) +#endif + +#ifdef DETERMINISTIC +#define CHECK(cond) do { \ + if (EXPECT(!(cond), 0)) { \ + TEST_FAILURE("test condition failed"); \ + } \ +} while(0) +#else +#define CHECK(cond) do { \ + if (EXPECT(!(cond), 0)) { \ + TEST_FAILURE("test condition failed: " #cond); \ + } \ +} 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) +#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) +#endif + +static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_t size) { + void *ret = malloc(size); + if (ret == NULL) { + secp256k1_callback_call(cb, "Out of memory"); + } + return ret; +} + +/* Macro for restrict, when available and not in a VERIFY build. */ +#if defined(SECP256K1_BUILD) && defined(VERIFY) +# define SECP256K1_RESTRICT +#else +# if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) ) +# if SECP256K1_GNUC_PREREQ(3,0) +# define SECP256K1_RESTRICT __restrict__ +# elif (defined(_MSC_VER) && _MSC_VER >= 1400) +# define SECP256K1_RESTRICT __restrict +# else +# define SECP256K1_RESTRICT +# endif +# else +# define SECP256K1_RESTRICT restrict +# endif +#endif + +#if defined(_WIN32) +# define I64FORMAT "I64d" +# define I64uFORMAT "I64u" +#else +# define I64FORMAT "lld" +# 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; +#endif + +#endif /* SECP256K1_UTIL_H */ diff --git a/src/cryptoconditions/src/include/sha256.c b/src/cryptoconditions/src/include/sha256.c new file mode 100644 index 000000000..e273836ce --- /dev/null +++ b/src/cryptoconditions/src/include/sha256.c @@ -0,0 +1,264 @@ +/*- + * Copyright 2005 Colin Percival + * Copyright 2013 Christian Mehlis & René Kijewski + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD: src/lib/libmd/sha256c.c,v 1.2 2006/01/17 15:35:56 phk Exp $ + */ + +/** + * @ingroup sys_crypto + * @{ + * + * @file sha256.c + * @brief SHA256 hash function implementation + * + * @author Colin Percival + * @author Christian Mehlis + * @author Rene Kijewski + * + * @} + */ + +#include "sha256.h" + +#define memcpy __builtin_memcpy +#define memset __builtin_memset + +#ifdef __BIG_ENDIAN__ +/* Copy a vector of big-endian uint32_t into a vector of bytes */ +#define be32enc_vect memcpy + +/* Copy a vector of bytes into a vector of big-endian uint32_t */ +#define be32dec_vect memcpy + +#else /* !__BIG_ENDIAN__ */ + +/* + * Encode a length len/4 vector of (uint32_t) into a length len vector of + * (unsigned char) in big-endian form. Assumes len is a multiple of 4. + */ +static void be32enc_vect(void *dst_, const void *src_, size_t len) +{ + uint32_t *dst = dst_; + const uint32_t *src = src_; + size_t i; + + for (i = 0; i < len / 4; i++) { + dst[i] = __builtin_bswap32(src[i]); + } +} + +/* + * Decode a big-endian length len vector of (unsigned char) into a length + * len/4 vector of (uint32_t). Assumes len is a multiple of 4. + */ +#define be32dec_vect be32enc_vect + +#endif /* __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__ */ + +/* Elementary functions used by SHA256 */ +#define Ch(x, y, z) ((x & (y ^ z)) ^ z) +#define Maj(x, y, z) ((x & (y | z)) | (y & z)) +#define SHR(x, n) (x >> n) +#define ROTR(x, n) ((x >> n) | (x << (32 - n))) +#define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3)) +#define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10)) + +static const uint32_t K[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, +}; + +/* + * SHA256 block compression function. The 256-bit state is transformed via + * the 512-bit input block to produce a new state. + */ +static void sha256_transform(uint32_t *state, const unsigned char block[64]) +{ + uint32_t W[64]; + uint32_t S[8]; + int i; + + /* 1. Prepare message schedule W. */ + be32dec_vect(W, block, 64); + for (i = 16; i < 64; i++) { + W[i] = s1(W[i - 2]) + W[i - 7] + s0(W[i - 15]) + W[i - 16]; + } + + /* 2. Initialize working variables. */ + memcpy(S, state, 32); + + /* 3. Mix. */ + for (i = 0; i < 64; ++i) { + uint32_t e = S[(68 - i) % 8], f = S[(69 - i) % 8]; + uint32_t g = S[(70 - i) % 8], h = S[(71 - i) % 8]; + uint32_t t0 = h + S1(e) + Ch(e, f, g) + W[i] + K[i]; + + uint32_t a = S[(64 - i) % 8], b = S[(65 - i) % 8]; + uint32_t c = S[(66 - i) % 8], d = S[(67 - i) % 8]; + uint32_t t1 = S0(a) + Maj(a, b, c); + + S[(67 - i) % 8] = d + t0; + S[(71 - i) % 8] = t0 + t1; + } + + /* 4. Mix local working variables into global state */ + for (i = 0; i < 8; i++) { + state[i] += S[i]; + } +} + +/* Add padding and terminating bit-count. */ +static void sha256_pad(sha256_context_t *ctx) +{ + + 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, + }; + /* + * Convert length to a vector of bytes -- we do this now rather + * than later because the length will change after we pad. + */ + unsigned char len[8]; + be32enc_vect(len, ctx->count, 8); + + /* Add 1--64 bytes so that the resulting length is 56 mod 64 */ + uint32_t r = (ctx->count[1] >> 3) & 0x3f; + uint32_t plen = (r < 56) ? (56 - r) : (120 - r); + sha256_update(ctx, PAD, (size_t) plen); + + /* Add the terminating bit-count */ + sha256_update(ctx, len, 8); +} + +/* SHA-256 initialization. Begins a SHA-256 operation. */ +void sha256_init(sha256_context_t *ctx) +{ + /* Zero bits processed so far */ + ctx->count[0] = ctx->count[1] = 0; + + /* Magic initialization constants */ + ctx->state[0] = 0x6A09E667; + ctx->state[1] = 0xBB67AE85; + ctx->state[2] = 0x3C6EF372; + ctx->state[3] = 0xA54FF53A; + ctx->state[4] = 0x510E527F; + ctx->state[5] = 0x9B05688C; + ctx->state[6] = 0x1F83D9AB; + ctx->state[7] = 0x5BE0CD19; +} + +/* Add bytes into the hash */ +void sha256_update(sha256_context_t *ctx, const void *in, size_t len) +{ + /* Number of bytes left in the buffer from previous updates */ + uint32_t r = (ctx->count[1] >> 3) & 0x3f; + + /* Convert the length into a number of bits */ + uint32_t bitlen1 = ((uint32_t) len) << 3; + uint32_t bitlen0 = ((uint32_t) len) >> 29; + + /* Update number of bits */ + if ((ctx->count[1] += bitlen1) < bitlen1) { + ctx->count[0]++; + } + + ctx->count[0] += bitlen0; + + /* Handle the case where we don't need to perform any transforms */ + if (len < 64 - r) { + memcpy(&ctx->buf[r], in, len); + return; + } + + /* Finish the current block */ + const unsigned char *src = in; + + memcpy(&ctx->buf[r], src, 64 - r); + sha256_transform(ctx->state, ctx->buf); + src += 64 - r; + len -= 64 - r; + + /* Perform complete blocks */ + while (len >= 64) { + sha256_transform(ctx->state, src); + src += 64; + len -= 64; + } + + /* Copy left over data into buffer */ + memcpy(ctx->buf, src, len); +} + +/* + * SHA-256 finalization. Pads the input data, exports the hash value, + * and clears the context state. + */ +void sha256_final(unsigned char digest[32], sha256_context_t *ctx) +{ + /* Add padding */ + sha256_pad(ctx); + + /* Write the hash */ + be32enc_vect(digest, ctx->state, 32); + + /* Clear the context state */ + memset((void *) ctx, 0, sizeof(*ctx)); +} + +unsigned char *sha256(const unsigned char *d, size_t n, unsigned char *md) +{ + sha256_context_t c __attribute__((aligned(4))); + static unsigned char m[SHA256_DIGEST_LENGTH] __attribute__((aligned(4))); + + if (md == NULL) { + md = m; + } + + sha256_init(&c); + sha256_update(&c, d, n); + sha256_final(md, &c); + + return md; +} diff --git a/src/cryptoconditions/src/include/sha256.h b/src/cryptoconditions/src/include/sha256.h new file mode 100644 index 000000000..7bbf3f43a --- /dev/null +++ b/src/cryptoconditions/src/include/sha256.h @@ -0,0 +1,114 @@ +/*- + * Copyright 2005 Colin Percival + * Copyright 2013 Christian Mehlis & René Kijewski + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD: src/lib/libmd/sha256.h,v 1.1.2.1 2005/06/24 13:32:25 cperciva Exp $ + */ + +/** + * @defgroup sys_sha256 SHA264 + * @ingroup sys + * @brief SHA264 hash generator + */ + +/** + * @ingroup sys_crypto + * @{ + * + * @file sha256.h + * @brief Header definitions for the SHA256 hash function + * + * @author Colin Percival + * @author Christian Mehlis + * @author Rene Kijewski + */ + +#ifndef _SHA256_H_ +#define _SHA256_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SHA256_DIGEST_LENGTH 32 + +/** + * @brief Context for ciper operatins based on sha256 + */ +typedef struct { + /** global state */ + uint32_t state[8]; + /** processed bytes counter */ + uint32_t count[2]; + /** data buffer */ + unsigned char buf[64]; +} sha256_context_t; + +/** + * @brief SHA-256 initialization. Begins a SHA-256 operation. + * + * @param ctx sha256_context_t handle to init + */ +void sha256_init(sha256_context_t *ctx); + +/** + * @brief Add bytes into the hash + * + * @param ctx sha256_context_t handle to use + * @param in pointer to the input buffer + * @param len length of the buffer + */ +void sha256_update(sha256_context_t *ctx, const void *in, size_t len); + +/** + * @brief SHA-256 finalization. Pads the input data, exports the hash value, + * and clears the context state. + * + * @param digest resulting digest, this is the hash of all the bytes + * @param ctx sha256_context_t handle to use + */ +void sha256_final(unsigned char digest[32], sha256_context_t *ctx); + +/** + * @brief A wrapper function to simplify the generation of a hash, this is + * usefull for generating sha256 for one buffer + * + * @param d pointer to the buffer to generate hash from + * @param n length of the buffer + * @param md optional pointer to an array for the result, length must be + * SHA256_DIGEST_LENGTH + * if md == NULL, one static buffer is used + */ +unsigned char *sha256(const unsigned char *d, size_t n, unsigned char *md); + +#ifdef __cplusplus +} +#endif + +/** @} */ +#endif /* _SHA256_H_ */ diff --git a/src/cryptoconditions/src/include/tweetnacl.c b/src/cryptoconditions/src/include/tweetnacl.c new file mode 100644 index 000000000..8ac0a1806 --- /dev/null +++ b/src/cryptoconditions/src/include/tweetnacl.c @@ -0,0 +1,809 @@ +#include "tweetnacl.h" +#define FOR(i,n) for (i = 0;i < n;++i) +#define sv static void + +typedef unsigned char u8; +typedef unsigned long u32; +typedef unsigned long long u64; +typedef long long i64; +typedef i64 gf[16]; +extern void randombytes(u8 *,u64); + +static const u8 + _0[16], + _9[32] = {9}; +static const gf + gf0, + gf1 = {1}, + _121665 = {0xDB41,1}, + D = {0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203}, + D2 = {0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406}, + X = {0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169}, + Y = {0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666}, + I = {0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83}; + +static u32 L32(u32 x,int c) { return (x << c) | ((x&0xffffffff) >> (32 - c)); } + +static u32 ld32(const u8 *x) +{ + u32 u = x[3]; + u = (u<<8)|x[2]; + u = (u<<8)|x[1]; + return (u<<8)|x[0]; +} + +static u64 dl64(const u8 *x) +{ + u64 i,u=0; + FOR(i,8) u=(u<<8)|x[i]; + return u; +} + +sv st32(u8 *x,u32 u) +{ + int i; + FOR(i,4) { x[i] = u; u >>= 8; } +} + +sv ts64(u8 *x,u64 u) +{ + int i; + for (i = 7;i >= 0;--i) { x[i] = u; u >>= 8; } +} + +static int vn(const u8 *x,const u8 *y,int n) +{ + u32 i,d = 0; + FOR(i,n) d |= x[i]^y[i]; + return (1 & ((d - 1) >> 8)) - 1; +} + +int crypto_verify_16(const u8 *x,const u8 *y) +{ + return vn(x,y,16); +} + +int crypto_verify_32(const u8 *x,const u8 *y) +{ + return vn(x,y,32); +} + +sv core(u8 *out,const u8 *in,const u8 *k,const u8 *c,int h) +{ + u32 w[16],x[16],y[16],t[4]; + int i,j,m; + + FOR(i,4) { + x[5*i] = ld32(c+4*i); + x[1+i] = ld32(k+4*i); + x[6+i] = ld32(in+4*i); + x[11+i] = ld32(k+16+4*i); + } + + FOR(i,16) y[i] = x[i]; + + FOR(i,20) { + FOR(j,4) { + FOR(m,4) t[m] = x[(5*j+4*m)%16]; + t[1] ^= L32(t[0]+t[3], 7); + t[2] ^= L32(t[1]+t[0], 9); + t[3] ^= L32(t[2]+t[1],13); + t[0] ^= L32(t[3]+t[2],18); + FOR(m,4) w[4*j+(j+m)%4] = t[m]; + } + FOR(m,16) x[m] = w[m]; + } + + if (h) { + FOR(i,16) x[i] += y[i]; + FOR(i,4) { + x[5*i] -= ld32(c+4*i); + x[6+i] -= ld32(in+4*i); + } + FOR(i,4) { + st32(out+4*i,x[5*i]); + st32(out+16+4*i,x[6+i]); + } + } else + FOR(i,16) st32(out + 4 * i,x[i] + y[i]); +} + +int crypto_core_salsa20(u8 *out,const u8 *in,const u8 *k,const u8 *c) +{ + core(out,in,k,c,0); + return 0; +} + +int crypto_core_hsalsa20(u8 *out,const u8 *in,const u8 *k,const u8 *c) +{ + core(out,in,k,c,1); + return 0; +} + +static const u8 sigma[16] = "expand 32-byte k"; + +int crypto_stream_salsa20_xor(u8 *c,const u8 *m,u64 b,const u8 *n,const u8 *k) +{ + u8 z[16],x[64]; + u32 u,i; + if (!b) return 0; + FOR(i,16) z[i] = 0; + FOR(i,8) z[i] = n[i]; + while (b >= 64) { + crypto_core_salsa20(x,z,k,sigma); + FOR(i,64) c[i] = (m?m[i]:0) ^ x[i]; + u = 1; + for (i = 8;i < 16;++i) { + u += (u32) z[i]; + z[i] = u; + u >>= 8; + } + b -= 64; + c += 64; + if (m) m += 64; + } + if (b) { + crypto_core_salsa20(x,z,k,sigma); + FOR(i,b) c[i] = (m?m[i]:0) ^ x[i]; + } + return 0; +} + +int crypto_stream_salsa20(u8 *c,u64 d,const u8 *n,const u8 *k) +{ + return crypto_stream_salsa20_xor(c,0,d,n,k); +} + +int crypto_stream(u8 *c,u64 d,const u8 *n,const u8 *k) +{ + u8 s[32]; + crypto_core_hsalsa20(s,n,k,sigma); + return crypto_stream_salsa20(c,d,n+16,s); +} + +int crypto_stream_xor(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *k) +{ + u8 s[32]; + crypto_core_hsalsa20(s,n,k,sigma); + return crypto_stream_salsa20_xor(c,m,d,n+16,s); +} + +sv add1305(u32 *h,const u32 *c) +{ + u32 j,u = 0; + FOR(j,17) { + u += h[j] + c[j]; + h[j] = u & 255; + u >>= 8; + } +} + +static const u32 minusp[17] = { + 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252 +} ; + +int crypto_onetimeauth(u8 *out,const u8 *m,u64 n,const u8 *k) +{ + u32 s,i,j,u,x[17],r[17],h[17],c[17],g[17]; + + FOR(j,17) r[j]=h[j]=0; + FOR(j,16) r[j]=k[j]; + r[3]&=15; + r[4]&=252; + r[7]&=15; + r[8]&=252; + r[11]&=15; + r[12]&=252; + r[15]&=15; + + while (n > 0) { + FOR(j,17) c[j] = 0; + for (j = 0;(j < 16) && (j < n);++j) c[j] = m[j]; + c[j] = 1; + m += j; n -= j; + add1305(h,c); + FOR(i,17) { + x[i] = 0; + FOR(j,17) x[i] += h[j] * ((j <= i) ? r[i - j] : 320 * r[i + 17 - j]); + } + FOR(i,17) h[i] = x[i]; + u = 0; + FOR(j,16) { + u += h[j]; + h[j] = u & 255; + u >>= 8; + } + u += h[16]; h[16] = u & 3; + u = 5 * (u >> 2); + FOR(j,16) { + u += h[j]; + h[j] = u & 255; + u >>= 8; + } + u += h[16]; h[16] = u; + } + + FOR(j,17) g[j] = h[j]; + add1305(h,minusp); + s = -(h[16] >> 7); + FOR(j,17) h[j] ^= s & (g[j] ^ h[j]); + + FOR(j,16) c[j] = k[j + 16]; + c[16] = 0; + add1305(h,c); + FOR(j,16) out[j] = h[j]; + return 0; +} + +int crypto_onetimeauth_verify(const u8 *h,const u8 *m,u64 n,const u8 *k) +{ + u8 x[16]; + crypto_onetimeauth(x,m,n,k); + return crypto_verify_16(h,x); +} + +int crypto_secretbox(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *k) +{ + int i; + if (d < 32) return -1; + crypto_stream_xor(c,m,d,n,k); + crypto_onetimeauth(c + 16,c + 32,d - 32,c); + FOR(i,16) c[i] = 0; + return 0; +} + +int crypto_secretbox_open(u8 *m,const u8 *c,u64 d,const u8 *n,const u8 *k) +{ + int i; + u8 x[32]; + if (d < 32) return -1; + crypto_stream(x,32,n,k); + if (crypto_onetimeauth_verify(c + 16,c + 32,d - 32,x) != 0) return -1; + crypto_stream_xor(m,c,d,n,k); + FOR(i,32) m[i] = 0; + return 0; +} + +sv set25519(gf r, const gf a) +{ + int i; + FOR(i,16) r[i]=a[i]; +} + +sv car25519(gf o) +{ + int i; + i64 c; + FOR(i,16) { + o[i]+=(1LL<<16); + c=o[i]>>16; + o[(i+1)*(i<15)]+=c-1+37*(c-1)*(i==15); + o[i]-=c<<16; + } +} + +sv sel25519(gf p,gf q,int b) +{ + i64 t,i,c=~(b-1); + FOR(i,16) { + t= c&(p[i]^q[i]); + p[i]^=t; + q[i]^=t; + } +} + +sv pack25519(u8 *o,const gf n) +{ + int i,j,b; + gf m,t; + FOR(i,16) t[i]=n[i]; + car25519(t); + car25519(t); + car25519(t); + FOR(j,2) { + m[0]=t[0]-0xffed; + for(i=1;i<15;i++) { + m[i]=t[i]-0xffff-((m[i-1]>>16)&1); + m[i-1]&=0xffff; + } + m[15]=t[15]-0x7fff-((m[14]>>16)&1); + b=(m[15]>>16)&1; + m[14]&=0xffff; + sel25519(t,m,1-b); + } + FOR(i,16) { + o[2*i]=t[i]&0xff; + o[2*i+1]=t[i]>>8; + } +} + +static int neq25519(const gf a, const gf b) +{ + u8 c[32],d[32]; + pack25519(c,a); + pack25519(d,b); + return crypto_verify_32(c,d); +} + +static u8 par25519(const gf a) +{ + u8 d[32]; + pack25519(d,a); + return d[0]&1; +} + +sv unpack25519(gf o, const u8 *n) +{ + int i; + FOR(i,16) o[i]=n[2*i]+((i64)n[2*i+1]<<8); + o[15]&=0x7fff; +} + +sv A(gf o,const gf a,const gf b) +{ + int i; + FOR(i,16) o[i]=a[i]+b[i]; +} + +sv Z(gf o,const gf a,const gf b) +{ + int i; + FOR(i,16) o[i]=a[i]-b[i]; +} + +sv M(gf o,const gf a,const gf b) +{ + i64 i,j,t[31]; + FOR(i,31) t[i]=0; + FOR(i,16) FOR(j,16) t[i+j]+=a[i]*b[j]; + FOR(i,15) t[i]+=38*t[i+16]; + FOR(i,16) o[i]=t[i]; + car25519(o); + car25519(o); +} + +sv S(gf o,const gf a) +{ + M(o,a,a); +} + +sv inv25519(gf o,const gf i) +{ + gf c; + int a; + FOR(a,16) c[a]=i[a]; + for(a=253;a>=0;a--) { + S(c,c); + if(a!=2&&a!=4) M(c,c,i); + } + FOR(a,16) o[a]=c[a]; +} + +sv pow2523(gf o,const gf i) +{ + gf c; + int a; + FOR(a,16) c[a]=i[a]; + for(a=250;a>=0;a--) { + S(c,c); + if(a!=1) M(c,c,i); + } + FOR(a,16) o[a]=c[a]; +} + +int crypto_scalarmult(u8 *q,const u8 *n,const u8 *p) +{ + u8 z[32]; + i64 x[80],r,i; + gf a,b,c,d,e,f; + FOR(i,31) z[i]=n[i]; + z[31]=(n[31]&127)|64; + z[0]&=248; + unpack25519(x,p); + FOR(i,16) { + b[i]=x[i]; + d[i]=a[i]=c[i]=0; + } + a[0]=d[0]=1; + for(i=254;i>=0;--i) { + r=(z[i>>3]>>(i&7))&1; + sel25519(a,b,r); + sel25519(c,d,r); + A(e,a,c); + Z(a,a,c); + A(c,b,d); + Z(b,b,d); + S(d,e); + S(f,a); + M(a,c,a); + M(c,b,e); + A(e,a,c); + Z(a,a,c); + S(b,a); + Z(c,d,f); + M(a,c,_121665); + A(a,a,d); + M(c,c,a); + M(a,d,f); + M(d,b,x); + S(b,e); + sel25519(a,b,r); + sel25519(c,d,r); + } + FOR(i,16) { + x[i+16]=a[i]; + x[i+32]=c[i]; + x[i+48]=b[i]; + x[i+64]=d[i]; + } + inv25519(x+32,x+32); + M(x+16,x+16,x+32); + pack25519(q,x+16); + return 0; +} + +int crypto_scalarmult_base(u8 *q,const u8 *n) +{ + return crypto_scalarmult(q,n,_9); +} + +int crypto_box_keypair(u8 *y,u8 *x) +{ + randombytes(x,32); + return crypto_scalarmult_base(y,x); +} + +int crypto_box_beforenm(u8 *k,const u8 *y,const u8 *x) +{ + u8 s[32]; + crypto_scalarmult(s,x,y); + return crypto_core_hsalsa20(k,_0,s,sigma); +} + +int crypto_box_afternm(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *k) +{ + return crypto_secretbox(c,m,d,n,k); +} + +int crypto_box_open_afternm(u8 *m,const u8 *c,u64 d,const u8 *n,const u8 *k) +{ + return crypto_secretbox_open(m,c,d,n,k); +} + +int crypto_box(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *y,const u8 *x) +{ + u8 k[32]; + crypto_box_beforenm(k,y,x); + return crypto_box_afternm(c,m,d,n,k); +} + +int crypto_box_open(u8 *m,const u8 *c,u64 d,const u8 *n,const u8 *y,const u8 *x) +{ + u8 k[32]; + crypto_box_beforenm(k,y,x); + return crypto_box_open_afternm(m,c,d,n,k); +} + +static u64 R(u64 x,int c) { return (x >> c) | (x << (64 - c)); } +static u64 Ch(u64 x,u64 y,u64 z) { return (x & y) ^ (~x & z); } +static u64 Maj(u64 x,u64 y,u64 z) { return (x & y) ^ (x & z) ^ (y & z); } +static u64 Sigma0(u64 x) { return R(x,28) ^ R(x,34) ^ R(x,39); } +static u64 Sigma1(u64 x) { return R(x,14) ^ R(x,18) ^ R(x,41); } +static u64 sigma0(u64 x) { return R(x, 1) ^ R(x, 8) ^ (x >> 7); } +static u64 sigma1(u64 x) { return R(x,19) ^ R(x,61) ^ (x >> 6); } + +static const u64 K[80] = +{ + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL +}; + +int crypto_hashblocks(u8 *x,const u8 *m,u64 n) +{ + u64 z[8],b[8],a[8],w[16],t; + int i,j; + + FOR(i,8) z[i] = a[i] = dl64(x + 8 * i); + + while (n >= 128) { + FOR(i,16) w[i] = dl64(m + 8 * i); + + FOR(i,80) { + FOR(j,8) b[j] = a[j]; + t = a[7] + Sigma1(a[4]) + Ch(a[4],a[5],a[6]) + K[i] + w[i%16]; + b[7] = t + Sigma0(a[0]) + Maj(a[0],a[1],a[2]); + b[3] += t; + FOR(j,8) a[(j+1)%8] = b[j]; + if (i%16 == 15) + FOR(j,16) + w[j] += w[(j+9)%16] + sigma0(w[(j+1)%16]) + sigma1(w[(j+14)%16]); + } + + FOR(i,8) { a[i] += z[i]; z[i] = a[i]; } + + m += 128; + n -= 128; + } + + FOR(i,8) ts64(x+8*i,z[i]); + + return n; +} + +static const u8 iv[64] = { + 0x6a,0x09,0xe6,0x67,0xf3,0xbc,0xc9,0x08, + 0xbb,0x67,0xae,0x85,0x84,0xca,0xa7,0x3b, + 0x3c,0x6e,0xf3,0x72,0xfe,0x94,0xf8,0x2b, + 0xa5,0x4f,0xf5,0x3a,0x5f,0x1d,0x36,0xf1, + 0x51,0x0e,0x52,0x7f,0xad,0xe6,0x82,0xd1, + 0x9b,0x05,0x68,0x8c,0x2b,0x3e,0x6c,0x1f, + 0x1f,0x83,0xd9,0xab,0xfb,0x41,0xbd,0x6b, + 0x5b,0xe0,0xcd,0x19,0x13,0x7e,0x21,0x79 +} ; + +int crypto_hash(u8 *out,const u8 *m,u64 n) +{ + u8 h[64],x[256]; + u64 i,b = n; + + FOR(i,64) h[i] = iv[i]; + + crypto_hashblocks(h,m,n); + m += n; + n &= 127; + m -= n; + + FOR(i,256) x[i] = 0; + FOR(i,n) x[i] = m[i]; + x[n] = 128; + + n = 256-128*(n<112); + x[n-9] = b >> 61; + ts64(x+n-8,b<<3); + crypto_hashblocks(h,x,n); + + FOR(i,64) out[i] = h[i]; + + return 0; +} + +sv add(gf p[4],gf q[4]) +{ + gf a,b,c,d,t,e,f,g,h; + + Z(a, p[1], p[0]); + Z(t, q[1], q[0]); + M(a, a, t); + A(b, p[0], p[1]); + A(t, q[0], q[1]); + M(b, b, t); + M(c, p[3], q[3]); + M(c, c, D2); + M(d, p[2], q[2]); + A(d, d, d); + Z(e, b, a); + Z(f, d, c); + A(g, d, c); + A(h, b, a); + + M(p[0], e, f); + M(p[1], h, g); + M(p[2], g, f); + M(p[3], e, h); +} + +sv cswap(gf p[4],gf q[4],u8 b) +{ + int i; + FOR(i,4) + sel25519(p[i],q[i],b); +} + +sv pack(u8 *r,gf p[4]) +{ + gf tx, ty, zi; + inv25519(zi, p[2]); + M(tx, p[0], zi); + M(ty, p[1], zi); + pack25519(r, ty); + r[31] ^= par25519(tx) << 7; +} + +sv scalarmult(gf p[4],gf q[4],const u8 *s) +{ + int i; + set25519(p[0],gf0); + set25519(p[1],gf1); + set25519(p[2],gf1); + set25519(p[3],gf0); + for (i = 255;i >= 0;--i) { + u8 b = (s[i/8]>>(i&7))&1; + cswap(p,q,b); + add(q,p); + add(p,p); + cswap(p,q,b); + } +} + +sv scalarbase(gf p[4],const u8 *s) +{ + gf q[4]; + set25519(q[0],X); + set25519(q[1],Y); + set25519(q[2],gf1); + M(q[3],X,Y); + scalarmult(p,q,s); +} + +int crypto_sign_keypair(u8 *pk, u8 *sk) +{ + u8 d[64]; + gf p[4]; + int i; + + randombytes(sk, 32); + crypto_hash(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + + scalarbase(p,d); + pack(pk,p); + + FOR(i,32) sk[32 + i] = pk[i]; + return 0; +} + +static const u64 L[32] = {0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10}; + +sv modL(u8 *r,i64 x[64]) +{ + i64 carry,i,j; + for (i = 63;i >= 32;--i) { + carry = 0; + for (j = i - 32;j < i - 12;++j) { + x[j] += carry - 16 * x[i] * L[j - (i - 32)]; + carry = (x[j] + 128) >> 8; + x[j] -= carry << 8; + } + x[j] += carry; + x[i] = 0; + } + carry = 0; + FOR(j,32) { + x[j] += carry - (x[31] >> 4) * L[j]; + carry = x[j] >> 8; + x[j] &= 255; + } + FOR(j,32) x[j] -= carry * L[j]; + FOR(i,32) { + x[i+1] += x[i] >> 8; + r[i] = x[i] & 255; + } +} + +sv reduce(u8 *r) +{ + i64 x[64],i; + FOR(i,64) x[i] = (u64) r[i]; + FOR(i,64) r[i] = 0; + modL(r,x); +} + +int crypto_sign(u8 *sm,u64 *smlen,const u8 *m,u64 n,const u8 *sk) +{ + u8 d[64],h[64],r[64]; + i64 i,j,x[64]; + gf p[4]; + + crypto_hash(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + + *smlen = n+64; + FOR(i,n) sm[64 + i] = m[i]; + FOR(i,32) sm[32 + i] = d[32 + i]; + + crypto_hash(r, sm+32, n+32); + reduce(r); + scalarbase(p,r); + pack(sm,p); + + FOR(i,32) sm[i+32] = sk[i+32]; + crypto_hash(h,sm,n + 64); + reduce(h); + + FOR(i,64) x[i] = 0; + FOR(i,32) x[i] = (u64) r[i]; + FOR(i,32) FOR(j,32) x[i+j] += h[i] * (u64) d[j]; + modL(sm + 32,x); + + return 0; +} + +static int unpackneg(gf r[4],const u8 p[32]) +{ + gf t, chk, num, den, den2, den4, den6; + set25519(r[2],gf1); + unpack25519(r[1],p); + S(num,r[1]); + M(den,num,D); + Z(num,num,r[2]); + A(den,r[2],den); + + S(den2,den); + S(den4,den2); + M(den6,den4,den2); + M(t,den6,num); + M(t,t,den); + + pow2523(t,t); + M(t,t,num); + M(t,t,den); + M(t,t,den); + M(r[0],t,den); + + S(chk,r[0]); + M(chk,chk,den); + if (neq25519(chk, num)) M(r[0],r[0],I); + + S(chk,r[0]); + M(chk,chk,den); + if (neq25519(chk, num)) return -1; + + if (par25519(r[0]) == (p[31]>>7)) Z(r[0],gf0,r[0]); + + M(r[3],r[0],r[1]); + return 0; +} + +int crypto_sign_open(u8 *m,u64 *mlen,const u8 *sm,u64 n,const u8 *pk) +{ + int i; + u8 t[32],h[64]; + gf p[4],q[4]; + + *mlen = -1; + if (n < 64) return -1; + + if (unpackneg(q,pk)) return -1; + + FOR(i,n) m[i] = sm[i]; + FOR(i,32) m[i+32] = pk[i]; + crypto_hash(h,m,n); + reduce(h); + scalarmult(p,q,h); + + scalarbase(q,sm + 32); + add(p,q); + pack(t,p); + + n -= 64; + if (crypto_verify_32(sm, t)) { + FOR(i,n) m[i] = 0; + return -1; + } + + FOR(i,n) m[i] = sm[i + 64]; + *mlen = n; + return 0; +} diff --git a/src/cryptoconditions/src/include/tweetnacl.h b/src/cryptoconditions/src/include/tweetnacl.h new file mode 100644 index 000000000..9277fbf8f --- /dev/null +++ b/src/cryptoconditions/src/include/tweetnacl.h @@ -0,0 +1,272 @@ +#ifndef TWEETNACL_H +#define TWEETNACL_H +#define crypto_auth_PRIMITIVE "hmacsha512256" +#define crypto_auth crypto_auth_hmacsha512256 +#define crypto_auth_verify crypto_auth_hmacsha512256_verify +#define crypto_auth_BYTES crypto_auth_hmacsha512256_BYTES +#define crypto_auth_KEYBYTES crypto_auth_hmacsha512256_KEYBYTES +#define crypto_auth_IMPLEMENTATION crypto_auth_hmacsha512256_IMPLEMENTATION +#define crypto_auth_VERSION crypto_auth_hmacsha512256_VERSION +#define crypto_auth_hmacsha512256_tweet_BYTES 32 +#define crypto_auth_hmacsha512256_tweet_KEYBYTES 32 +extern int crypto_auth_hmacsha512256_tweet(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *); +extern int crypto_auth_hmacsha512256_tweet_verify(const unsigned char *,const unsigned char *,unsigned long long,const unsigned char *); +#define crypto_auth_hmacsha512256_tweet_VERSION "-" +#define crypto_auth_hmacsha512256 crypto_auth_hmacsha512256_tweet +#define crypto_auth_hmacsha512256_verify crypto_auth_hmacsha512256_tweet_verify +#define crypto_auth_hmacsha512256_BYTES crypto_auth_hmacsha512256_tweet_BYTES +#define crypto_auth_hmacsha512256_KEYBYTES crypto_auth_hmacsha512256_tweet_KEYBYTES +#define crypto_auth_hmacsha512256_VERSION crypto_auth_hmacsha512256_tweet_VERSION +#define crypto_auth_hmacsha512256_IMPLEMENTATION "crypto_auth/hmacsha512256/tweet" +#define crypto_box_PRIMITIVE "curve25519xsalsa20poly1305" +#define crypto_box crypto_box_curve25519xsalsa20poly1305 +#define crypto_box_open crypto_box_curve25519xsalsa20poly1305_open +#define crypto_box_keypair crypto_box_curve25519xsalsa20poly1305_keypair +#define crypto_box_beforenm crypto_box_curve25519xsalsa20poly1305_beforenm +#define crypto_box_afternm crypto_box_curve25519xsalsa20poly1305_afternm +#define crypto_box_open_afternm crypto_box_curve25519xsalsa20poly1305_open_afternm +#define crypto_box_PUBLICKEYBYTES crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES +#define crypto_box_SECRETKEYBYTES crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES +#define crypto_box_BEFORENMBYTES crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES +#define crypto_box_NONCEBYTES crypto_box_curve25519xsalsa20poly1305_NONCEBYTES +#define crypto_box_ZEROBYTES crypto_box_curve25519xsalsa20poly1305_ZEROBYTES +#define crypto_box_BOXZEROBYTES crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES +#define crypto_box_IMPLEMENTATION crypto_box_curve25519xsalsa20poly1305_IMPLEMENTATION +#define crypto_box_VERSION crypto_box_curve25519xsalsa20poly1305_VERSION +#define crypto_box_curve25519xsalsa20poly1305_tweet_PUBLICKEYBYTES 32 +#define crypto_box_curve25519xsalsa20poly1305_tweet_SECRETKEYBYTES 32 +#define crypto_box_curve25519xsalsa20poly1305_tweet_BEFORENMBYTES 32 +#define crypto_box_curve25519xsalsa20poly1305_tweet_NONCEBYTES 24 +#define crypto_box_curve25519xsalsa20poly1305_tweet_ZEROBYTES 32 +#define crypto_box_curve25519xsalsa20poly1305_tweet_BOXZEROBYTES 16 +extern int crypto_box_curve25519xsalsa20poly1305_tweet(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *,const unsigned char *); +extern int crypto_box_curve25519xsalsa20poly1305_tweet_open(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *,const unsigned char *); +extern int crypto_box_curve25519xsalsa20poly1305_tweet_keypair(unsigned char *,unsigned char *); +extern int crypto_box_curve25519xsalsa20poly1305_tweet_beforenm(unsigned char *,const unsigned char *,const unsigned char *); +extern int crypto_box_curve25519xsalsa20poly1305_tweet_afternm(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); +extern int crypto_box_curve25519xsalsa20poly1305_tweet_open_afternm(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); +#define crypto_box_curve25519xsalsa20poly1305_tweet_VERSION "-" +#define crypto_box_curve25519xsalsa20poly1305 crypto_box_curve25519xsalsa20poly1305_tweet +#define crypto_box_curve25519xsalsa20poly1305_open crypto_box_curve25519xsalsa20poly1305_tweet_open +#define crypto_box_curve25519xsalsa20poly1305_keypair crypto_box_curve25519xsalsa20poly1305_tweet_keypair +#define crypto_box_curve25519xsalsa20poly1305_beforenm crypto_box_curve25519xsalsa20poly1305_tweet_beforenm +#define crypto_box_curve25519xsalsa20poly1305_afternm crypto_box_curve25519xsalsa20poly1305_tweet_afternm +#define crypto_box_curve25519xsalsa20poly1305_open_afternm crypto_box_curve25519xsalsa20poly1305_tweet_open_afternm +#define crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES crypto_box_curve25519xsalsa20poly1305_tweet_PUBLICKEYBYTES +#define crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES crypto_box_curve25519xsalsa20poly1305_tweet_SECRETKEYBYTES +#define crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES crypto_box_curve25519xsalsa20poly1305_tweet_BEFORENMBYTES +#define crypto_box_curve25519xsalsa20poly1305_NONCEBYTES crypto_box_curve25519xsalsa20poly1305_tweet_NONCEBYTES +#define crypto_box_curve25519xsalsa20poly1305_ZEROBYTES crypto_box_curve25519xsalsa20poly1305_tweet_ZEROBYTES +#define crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES crypto_box_curve25519xsalsa20poly1305_tweet_BOXZEROBYTES +#define crypto_box_curve25519xsalsa20poly1305_VERSION crypto_box_curve25519xsalsa20poly1305_tweet_VERSION +#define crypto_box_curve25519xsalsa20poly1305_IMPLEMENTATION "crypto_box/curve25519xsalsa20poly1305/tweet" +#define crypto_core_PRIMITIVE "salsa20" +#define crypto_core crypto_core_salsa20 +#define crypto_core_OUTPUTBYTES crypto_core_salsa20_OUTPUTBYTES +#define crypto_core_INPUTBYTES crypto_core_salsa20_INPUTBYTES +#define crypto_core_KEYBYTES crypto_core_salsa20_KEYBYTES +#define crypto_core_CONSTBYTES crypto_core_salsa20_CONSTBYTES +#define crypto_core_IMPLEMENTATION crypto_core_salsa20_IMPLEMENTATION +#define crypto_core_VERSION crypto_core_salsa20_VERSION +#define crypto_core_salsa20_tweet_OUTPUTBYTES 64 +#define crypto_core_salsa20_tweet_INPUTBYTES 16 +#define crypto_core_salsa20_tweet_KEYBYTES 32 +#define crypto_core_salsa20_tweet_CONSTBYTES 16 +extern int crypto_core_salsa20_tweet(unsigned char *,const unsigned char *,const unsigned char *,const unsigned char *); +#define crypto_core_salsa20_tweet_VERSION "-" +#define crypto_core_salsa20 crypto_core_salsa20_tweet +#define crypto_core_salsa20_OUTPUTBYTES crypto_core_salsa20_tweet_OUTPUTBYTES +#define crypto_core_salsa20_INPUTBYTES crypto_core_salsa20_tweet_INPUTBYTES +#define crypto_core_salsa20_KEYBYTES crypto_core_salsa20_tweet_KEYBYTES +#define crypto_core_salsa20_CONSTBYTES crypto_core_salsa20_tweet_CONSTBYTES +#define crypto_core_salsa20_VERSION crypto_core_salsa20_tweet_VERSION +#define crypto_core_salsa20_IMPLEMENTATION "crypto_core/salsa20/tweet" +#define crypto_core_hsalsa20_tweet_OUTPUTBYTES 32 +#define crypto_core_hsalsa20_tweet_INPUTBYTES 16 +#define crypto_core_hsalsa20_tweet_KEYBYTES 32 +#define crypto_core_hsalsa20_tweet_CONSTBYTES 16 +extern int crypto_core_hsalsa20_tweet(unsigned char *,const unsigned char *,const unsigned char *,const unsigned char *); +#define crypto_core_hsalsa20_tweet_VERSION "-" +#define crypto_core_hsalsa20 crypto_core_hsalsa20_tweet +#define crypto_core_hsalsa20_OUTPUTBYTES crypto_core_hsalsa20_tweet_OUTPUTBYTES +#define crypto_core_hsalsa20_INPUTBYTES crypto_core_hsalsa20_tweet_INPUTBYTES +#define crypto_core_hsalsa20_KEYBYTES crypto_core_hsalsa20_tweet_KEYBYTES +#define crypto_core_hsalsa20_CONSTBYTES crypto_core_hsalsa20_tweet_CONSTBYTES +#define crypto_core_hsalsa20_VERSION crypto_core_hsalsa20_tweet_VERSION +#define crypto_core_hsalsa20_IMPLEMENTATION "crypto_core/hsalsa20/tweet" +#define crypto_hashblocks_PRIMITIVE "sha512" +#define crypto_hashblocks crypto_hashblocks_sha512 +#define crypto_hashblocks_STATEBYTES crypto_hashblocks_sha512_STATEBYTES +#define crypto_hashblocks_BLOCKBYTES crypto_hashblocks_sha512_BLOCKBYTES +#define crypto_hashblocks_IMPLEMENTATION crypto_hashblocks_sha512_IMPLEMENTATION +#define crypto_hashblocks_VERSION crypto_hashblocks_sha512_VERSION +#define crypto_hashblocks_sha512_tweet_STATEBYTES 64 +#define crypto_hashblocks_sha512_tweet_BLOCKBYTES 128 +extern int crypto_hashblocks_sha512_tweet(unsigned char *,const unsigned char *,unsigned long long); +#define crypto_hashblocks_sha512_tweet_VERSION "-" +#define crypto_hashblocks_sha512 crypto_hashblocks_sha512_tweet +#define crypto_hashblocks_sha512_STATEBYTES crypto_hashblocks_sha512_tweet_STATEBYTES +#define crypto_hashblocks_sha512_BLOCKBYTES crypto_hashblocks_sha512_tweet_BLOCKBYTES +#define crypto_hashblocks_sha512_VERSION crypto_hashblocks_sha512_tweet_VERSION +#define crypto_hashblocks_sha512_IMPLEMENTATION "crypto_hashblocks/sha512/tweet" +#define crypto_hashblocks_sha256_tweet_STATEBYTES 32 +#define crypto_hashblocks_sha256_tweet_BLOCKBYTES 64 +extern int crypto_hashblocks_sha256_tweet(unsigned char *,const unsigned char *,unsigned long long); +#define crypto_hashblocks_sha256_tweet_VERSION "-" +#define crypto_hashblocks_sha256 crypto_hashblocks_sha256_tweet +#define crypto_hashblocks_sha256_STATEBYTES crypto_hashblocks_sha256_tweet_STATEBYTES +#define crypto_hashblocks_sha256_BLOCKBYTES crypto_hashblocks_sha256_tweet_BLOCKBYTES +#define crypto_hashblocks_sha256_VERSION crypto_hashblocks_sha256_tweet_VERSION +#define crypto_hashblocks_sha256_IMPLEMENTATION "crypto_hashblocks/sha256/tweet" +#define crypto_hash_PRIMITIVE "sha512" +#define crypto_hash crypto_hash_sha512 +#define crypto_hash_BYTES crypto_hash_sha512_BYTES +#define crypto_hash_IMPLEMENTATION crypto_hash_sha512_IMPLEMENTATION +#define crypto_hash_VERSION crypto_hash_sha512_VERSION +#define crypto_hash_sha512_tweet_BYTES 64 +extern int crypto_hash_sha512_tweet(unsigned char *,const unsigned char *,unsigned long long); +#define crypto_hash_sha512_tweet_VERSION "-" +#define crypto_hash_sha512 crypto_hash_sha512_tweet +#define crypto_hash_sha512_BYTES crypto_hash_sha512_tweet_BYTES +#define crypto_hash_sha512_VERSION crypto_hash_sha512_tweet_VERSION +#define crypto_hash_sha512_IMPLEMENTATION "crypto_hash/sha512/tweet" +#define crypto_hash_sha256_tweet_BYTES 32 +extern int crypto_hash_sha256_tweet(unsigned char *,const unsigned char *,unsigned long long); +#define crypto_hash_sha256_tweet_VERSION "-" +#define crypto_hash_sha256 crypto_hash_sha256_tweet +#define crypto_hash_sha256_BYTES crypto_hash_sha256_tweet_BYTES +#define crypto_hash_sha256_VERSION crypto_hash_sha256_tweet_VERSION +#define crypto_hash_sha256_IMPLEMENTATION "crypto_hash/sha256/tweet" +#define crypto_onetimeauth_PRIMITIVE "poly1305" +#define crypto_onetimeauth crypto_onetimeauth_poly1305 +#define crypto_onetimeauth_verify crypto_onetimeauth_poly1305_verify +#define crypto_onetimeauth_BYTES crypto_onetimeauth_poly1305_BYTES +#define crypto_onetimeauth_KEYBYTES crypto_onetimeauth_poly1305_KEYBYTES +#define crypto_onetimeauth_IMPLEMENTATION crypto_onetimeauth_poly1305_IMPLEMENTATION +#define crypto_onetimeauth_VERSION crypto_onetimeauth_poly1305_VERSION +#define crypto_onetimeauth_poly1305_tweet_BYTES 16 +#define crypto_onetimeauth_poly1305_tweet_KEYBYTES 32 +extern int crypto_onetimeauth_poly1305_tweet(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *); +extern int crypto_onetimeauth_poly1305_tweet_verify(const unsigned char *,const unsigned char *,unsigned long long,const unsigned char *); +#define crypto_onetimeauth_poly1305_tweet_VERSION "-" +#define crypto_onetimeauth_poly1305 crypto_onetimeauth_poly1305_tweet +#define crypto_onetimeauth_poly1305_verify crypto_onetimeauth_poly1305_tweet_verify +#define crypto_onetimeauth_poly1305_BYTES crypto_onetimeauth_poly1305_tweet_BYTES +#define crypto_onetimeauth_poly1305_KEYBYTES crypto_onetimeauth_poly1305_tweet_KEYBYTES +#define crypto_onetimeauth_poly1305_VERSION crypto_onetimeauth_poly1305_tweet_VERSION +#define crypto_onetimeauth_poly1305_IMPLEMENTATION "crypto_onetimeauth/poly1305/tweet" +#define crypto_scalarmult_PRIMITIVE "curve25519" +#define crypto_scalarmult crypto_scalarmult_curve25519 +#define crypto_scalarmult_base crypto_scalarmult_curve25519_base +#define crypto_scalarmult_BYTES crypto_scalarmult_curve25519_BYTES +#define crypto_scalarmult_SCALARBYTES crypto_scalarmult_curve25519_SCALARBYTES +#define crypto_scalarmult_IMPLEMENTATION crypto_scalarmult_curve25519_IMPLEMENTATION +#define crypto_scalarmult_VERSION crypto_scalarmult_curve25519_VERSION +#define crypto_scalarmult_curve25519_tweet_BYTES 32 +#define crypto_scalarmult_curve25519_tweet_SCALARBYTES 32 +extern int crypto_scalarmult_curve25519_tweet(unsigned char *,const unsigned char *,const unsigned char *); +extern int crypto_scalarmult_curve25519_tweet_base(unsigned char *,const unsigned char *); +#define crypto_scalarmult_curve25519_tweet_VERSION "-" +#define crypto_scalarmult_curve25519 crypto_scalarmult_curve25519_tweet +#define crypto_scalarmult_curve25519_base crypto_scalarmult_curve25519_tweet_base +#define crypto_scalarmult_curve25519_BYTES crypto_scalarmult_curve25519_tweet_BYTES +#define crypto_scalarmult_curve25519_SCALARBYTES crypto_scalarmult_curve25519_tweet_SCALARBYTES +#define crypto_scalarmult_curve25519_VERSION crypto_scalarmult_curve25519_tweet_VERSION +#define crypto_scalarmult_curve25519_IMPLEMENTATION "crypto_scalarmult/curve25519/tweet" +#define crypto_secretbox_PRIMITIVE "xsalsa20poly1305" +#define crypto_secretbox crypto_secretbox_xsalsa20poly1305 +#define crypto_secretbox_open crypto_secretbox_xsalsa20poly1305_open +#define crypto_secretbox_KEYBYTES crypto_secretbox_xsalsa20poly1305_KEYBYTES +#define crypto_secretbox_NONCEBYTES crypto_secretbox_xsalsa20poly1305_NONCEBYTES +#define crypto_secretbox_ZEROBYTES crypto_secretbox_xsalsa20poly1305_ZEROBYTES +#define crypto_secretbox_BOXZEROBYTES crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES +#define crypto_secretbox_IMPLEMENTATION crypto_secretbox_xsalsa20poly1305_IMPLEMENTATION +#define crypto_secretbox_VERSION crypto_secretbox_xsalsa20poly1305_VERSION +#define crypto_secretbox_xsalsa20poly1305_tweet_KEYBYTES 32 +#define crypto_secretbox_xsalsa20poly1305_tweet_NONCEBYTES 24 +#define crypto_secretbox_xsalsa20poly1305_tweet_ZEROBYTES 32 +#define crypto_secretbox_xsalsa20poly1305_tweet_BOXZEROBYTES 16 +extern int crypto_secretbox_xsalsa20poly1305_tweet(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); +extern int crypto_secretbox_xsalsa20poly1305_tweet_open(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); +#define crypto_secretbox_xsalsa20poly1305_tweet_VERSION "-" +#define crypto_secretbox_xsalsa20poly1305 crypto_secretbox_xsalsa20poly1305_tweet +#define crypto_secretbox_xsalsa20poly1305_open crypto_secretbox_xsalsa20poly1305_tweet_open +#define crypto_secretbox_xsalsa20poly1305_KEYBYTES crypto_secretbox_xsalsa20poly1305_tweet_KEYBYTES +#define crypto_secretbox_xsalsa20poly1305_NONCEBYTES crypto_secretbox_xsalsa20poly1305_tweet_NONCEBYTES +#define crypto_secretbox_xsalsa20poly1305_ZEROBYTES crypto_secretbox_xsalsa20poly1305_tweet_ZEROBYTES +#define crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES crypto_secretbox_xsalsa20poly1305_tweet_BOXZEROBYTES +#define crypto_secretbox_xsalsa20poly1305_VERSION crypto_secretbox_xsalsa20poly1305_tweet_VERSION +#define crypto_secretbox_xsalsa20poly1305_IMPLEMENTATION "crypto_secretbox/xsalsa20poly1305/tweet" +#define crypto_sign_PRIMITIVE "ed25519" +#define crypto_sign crypto_sign_ed25519 +#define crypto_sign_open crypto_sign_ed25519_open +#define crypto_sign_keypair crypto_sign_ed25519_keypair +#define crypto_sign_BYTES crypto_sign_ed25519_BYTES +#define crypto_sign_PUBLICKEYBYTES crypto_sign_ed25519_PUBLICKEYBYTES +#define crypto_sign_SECRETKEYBYTES crypto_sign_ed25519_SECRETKEYBYTES +#define crypto_sign_IMPLEMENTATION crypto_sign_ed25519_IMPLEMENTATION +#define crypto_sign_VERSION crypto_sign_ed25519_VERSION +#define crypto_sign_ed25519_tweet_BYTES 64 +#define crypto_sign_ed25519_tweet_PUBLICKEYBYTES 32 +#define crypto_sign_ed25519_tweet_SECRETKEYBYTES 64 +extern int crypto_sign_ed25519_tweet(unsigned char *,unsigned long long *,const unsigned char *,unsigned long long,const unsigned char *); +extern int crypto_sign_ed25519_tweet_open(unsigned char *,unsigned long long *,const unsigned char *,unsigned long long,const unsigned char *); +extern int crypto_sign_ed25519_tweet_keypair(unsigned char *,unsigned char *); +#define crypto_sign_ed25519_tweet_VERSION "-" +#define crypto_sign_ed25519 crypto_sign_ed25519_tweet +#define crypto_sign_ed25519_open crypto_sign_ed25519_tweet_open +#define crypto_sign_ed25519_keypair crypto_sign_ed25519_tweet_keypair +#define crypto_sign_ed25519_BYTES crypto_sign_ed25519_tweet_BYTES +#define crypto_sign_ed25519_PUBLICKEYBYTES crypto_sign_ed25519_tweet_PUBLICKEYBYTES +#define crypto_sign_ed25519_SECRETKEYBYTES crypto_sign_ed25519_tweet_SECRETKEYBYTES +#define crypto_sign_ed25519_VERSION crypto_sign_ed25519_tweet_VERSION +#define crypto_sign_ed25519_IMPLEMENTATION "crypto_sign/ed25519/tweet" +#define crypto_stream_PRIMITIVE "xsalsa20" +#define crypto_stream crypto_stream_xsalsa20 +#define crypto_stream_xor crypto_stream_xsalsa20_xor +#define crypto_stream_KEYBYTES crypto_stream_xsalsa20_KEYBYTES +#define crypto_stream_NONCEBYTES crypto_stream_xsalsa20_NONCEBYTES +#define crypto_stream_IMPLEMENTATION crypto_stream_xsalsa20_IMPLEMENTATION +#define crypto_stream_VERSION crypto_stream_xsalsa20_VERSION +#define crypto_stream_xsalsa20_tweet_KEYBYTES 32 +#define crypto_stream_xsalsa20_tweet_NONCEBYTES 24 +extern int crypto_stream_xsalsa20_tweet(unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); +extern int crypto_stream_xsalsa20_tweet_xor(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); +#define crypto_stream_xsalsa20_tweet_VERSION "-" +#define crypto_stream_xsalsa20 crypto_stream_xsalsa20_tweet +#define crypto_stream_xsalsa20_xor crypto_stream_xsalsa20_tweet_xor +#define crypto_stream_xsalsa20_KEYBYTES crypto_stream_xsalsa20_tweet_KEYBYTES +#define crypto_stream_xsalsa20_NONCEBYTES crypto_stream_xsalsa20_tweet_NONCEBYTES +#define crypto_stream_xsalsa20_VERSION crypto_stream_xsalsa20_tweet_VERSION +#define crypto_stream_xsalsa20_IMPLEMENTATION "crypto_stream/xsalsa20/tweet" +#define crypto_stream_salsa20_tweet_KEYBYTES 32 +#define crypto_stream_salsa20_tweet_NONCEBYTES 8 +extern int crypto_stream_salsa20_tweet(unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); +extern int crypto_stream_salsa20_tweet_xor(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); +#define crypto_stream_salsa20_tweet_VERSION "-" +#define crypto_stream_salsa20 crypto_stream_salsa20_tweet +#define crypto_stream_salsa20_xor crypto_stream_salsa20_tweet_xor +#define crypto_stream_salsa20_KEYBYTES crypto_stream_salsa20_tweet_KEYBYTES +#define crypto_stream_salsa20_NONCEBYTES crypto_stream_salsa20_tweet_NONCEBYTES +#define crypto_stream_salsa20_VERSION crypto_stream_salsa20_tweet_VERSION +#define crypto_stream_salsa20_IMPLEMENTATION "crypto_stream/salsa20/tweet" +#define crypto_verify_PRIMITIVE "16" +#define crypto_verify crypto_verify_16 +#define crypto_verify_BYTES crypto_verify_16_BYTES +#define crypto_verify_IMPLEMENTATION crypto_verify_16_IMPLEMENTATION +#define crypto_verify_VERSION crypto_verify_16_VERSION +#define crypto_verify_16_tweet_BYTES 16 +extern int crypto_verify_16_tweet(const unsigned char *,const unsigned char *); +#define crypto_verify_16_tweet_VERSION "-" +#define crypto_verify_16 crypto_verify_16_tweet +#define crypto_verify_16_BYTES crypto_verify_16_tweet_BYTES +#define crypto_verify_16_VERSION crypto_verify_16_tweet_VERSION +#define crypto_verify_16_IMPLEMENTATION "crypto_verify/16/tweet" +#define crypto_verify_32_tweet_BYTES 32 +extern int crypto_verify_32_tweet(const unsigned char *,const unsigned char *); +#define crypto_verify_32_tweet_VERSION "-" +#define crypto_verify_32 crypto_verify_32_tweet +#define crypto_verify_32_BYTES crypto_verify_32_tweet_BYTES +#define crypto_verify_32_VERSION crypto_verify_32_tweet_VERSION +#define crypto_verify_32_IMPLEMENTATION "crypto_verify/32/tweet" +#endif diff --git a/src/cryptoconditions/src/internal.h b/src/cryptoconditions/src/internal.h new file mode 100644 index 000000000..76955f406 --- /dev/null +++ b/src/cryptoconditions/src/internal.h @@ -0,0 +1,84 @@ +#include +#include +#include "include/cJSON.h" +#include "asn/asn_application.h" +#include "cryptoconditions.h" + +#ifndef INTERNAL_H +#define INTERNAL_H + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define BUF_SIZE 1024 * 1024 + +typedef char bool; + + +/* + * Condition Type + */ +typedef struct CCType { + int typeId; + char name[100]; + Condition_PR asnType; + int (*visitChildren)(CC *cond, CCVisitor visitor); + unsigned char *(*fingerprint)(const CC *cond); + unsigned long (*getCost)(const CC *cond); + uint32_t (*getSubtypes)(const CC *cond); + CC *(*fromJSON)(const cJSON *params, char *err); + void (*toJSON)(const CC *cond, cJSON *params); + CC *(*fromFulfillment)(const Fulfillment_t *ffill); + Fulfillment_t *(*toFulfillment)(const CC *cond); + int (*isFulfilled)(const CC *cond); + void (*free)(struct CC *cond); +} CCType; + + +/* + * Globals + */ +struct CCType *CCTypeRegistry[32]; +int CCTypeRegistryLength; + + +/* + * Internal API + */ +uint32_t fromAsnSubtypes(ConditionTypes_t types); +CC *mkAnon(const Condition_t *asnCond); +void asnCondition(const CC *cond, Condition_t *asn); +Condition_t *asnConditionNew(const CC *cond); +Fulfillment_t *asnFulfillmentNew(const CC *cond); +struct CC *fulfillmentToCC(Fulfillment_t *ffill); +struct CCType *getTypeByAsnEnum(Condition_PR present); + + +/* + * Utility functions + */ +unsigned char *base64_encode(const unsigned char *data, size_t input_length); +unsigned char *base64_decode(const unsigned char *data_, size_t *output_length); +unsigned char *hashFingerprintContents(asn_TYPE_descriptor_t *asnType, void *fp); +void dumpStr(unsigned char *str, size_t len); +int checkString(const cJSON *value, char *key, char *err); +int checkDecodeBase64(const cJSON *value, char *key, char *err, unsigned char **data, size_t *size); +int jsonGetBase64(const cJSON *params, char *key, char *err, unsigned char **data, size_t *size); +int jsonGetBase64Optional(const cJSON *params, char *key, char *err, unsigned char **data, size_t *size); +void jsonAddBase64(cJSON *params, char *key, unsigned char *bin, size_t size); +char* cc_hex_encode(const uint8_t *bin, size_t len); +uint8_t* cc_hex_decode(const char* hex); +bool checkDecodeHex(const cJSON *params, char *key, char *err, uint8_t **data, size_t *size); +bool jsonGetHex(const cJSON *params, char *key, char *err, unsigned char **data, size_t *size); +void jsonAddHex(cJSON *params, char *key, uint8_t *bin, size_t size); +int jsonGetHexOptional(const cJSON *params, char *key, char *err, unsigned char **data, size_t *size); + + +#ifdef __cplusplus +} +#endif + +#endif /* INTERNAL_H */ diff --git a/src/cryptoconditions/src/json_rpc.c b/src/cryptoconditions/src/json_rpc.c new file mode 100644 index 000000000..42c13612c --- /dev/null +++ b/src/cryptoconditions/src/json_rpc.c @@ -0,0 +1,327 @@ +#include "cryptoconditions.h" +#include "internal.h" +#include +#include + + +static cJSON *jsonCondition(CC *cond) { + cJSON *root = cJSON_CreateObject(); + + char *uri = cc_conditionUri(cond); + cJSON_AddItemToObject(root, "uri", cJSON_CreateString(uri)); + free(uri); + + unsigned char buf[1000]; + size_t conditionBinLength = cc_conditionBinary(cond, buf); + jsonAddHex(root, "bin", buf, conditionBinLength); + + return root; +} + + +static cJSON *jsonFulfillment(CC *cond) { + uint8_t buf[1000000]; + size_t fulfillmentBinLength = cc_fulfillmentBinary(cond, buf, 1000000); + + cJSON *root = cJSON_CreateObject(); + jsonAddHex(root, "fulfillment", buf, fulfillmentBinLength); + return root; +} + + +CC *cc_conditionFromJSON(cJSON *params, char *err) { + if (!params || !cJSON_IsObject(params)) { + strcpy(err, "Condition params must be an object"); + return NULL; + } + cJSON *typeName = cJSON_GetObjectItem(params, "type"); + if (!typeName || !cJSON_IsString(typeName)) { + strcpy(err, "\"type\" must be a string"); + return NULL; + } + for (int i=0; ivaluestring, CCTypeRegistry[i]->name)) { + return CCTypeRegistry[i]->fromJSON(params, err); + } + } + } + strcpy(err, "cannot detect type of condition"); + return NULL; +} + + +CC *cc_conditionFromJSONString(const char *data, char *err) { + cJSON *params = cJSON_Parse(data); + CC *out = cc_conditionFromJSON(params, err); + cJSON_Delete(params); + return out; +} + + +static cJSON *jsonEncodeCondition(cJSON *params, char *err) { + CC *cond = cc_conditionFromJSON(params, err); + cJSON *out = NULL; + if (cond != NULL) { + out = jsonCondition(cond); + cc_free(cond); + } + return out; +} + + +static cJSON *jsonEncodeFulfillment(cJSON *params, char *err) { + CC *cond = cc_conditionFromJSON(params, err); + cJSON *out = NULL; + if (cond != NULL) { + out = jsonFulfillment(cond); + cc_free(cond); + } + return out; +} + + +static cJSON *jsonErr(char *err) { + cJSON *out = cJSON_CreateObject(); + cJSON_AddItemToObject(out, "error", cJSON_CreateString(err)); + return out; +} + + +static cJSON *jsonVerifyFulfillment(cJSON *params, char *err) { + unsigned char *ffill_bin = 0, *msg = 0, *cond_bin = 0; + size_t ffill_bin_len, msg_len, cond_bin_len; + cJSON *out = 0; + + if (!(jsonGetHex(params, "fulfillment", err, &ffill_bin, &ffill_bin_len) && + jsonGetHex(params, "message", err, &msg, &msg_len) && + jsonGetHex(params, "condition", err, &cond_bin, &cond_bin_len))) + goto END; + + CC *cond = cc_readFulfillmentBinary(ffill_bin, ffill_bin_len); + + if (!cond) { + strcpy(err, "Invalid fulfillment payload"); + goto END; + } + + int valid = cc_verify(cond, msg, msg_len, 1, cond_bin, cond_bin_len, &jsonVerifyEval, NULL); + cc_free(cond); + out = cJSON_CreateObject(); + cJSON_AddItemToObject(out, "valid", cJSON_CreateBool(valid)); + +END: + free(ffill_bin); free(msg); free(cond_bin); + return out; +} + + +static cJSON *jsonDecodeFulfillment(cJSON *params, char *err) { + size_t ffill_bin_len; + unsigned char *ffill_bin; + if (!jsonGetHex(params, "fulfillment", err, &ffill_bin, &ffill_bin_len)) + return NULL; + + CC *cond = cc_readFulfillmentBinary(ffill_bin, ffill_bin_len); + free(ffill_bin); + if (!cond) { + strcpy(err, "Invalid fulfillment payload"); + return NULL; + } + cJSON *out = jsonCondition(cond); + cc_free(cond); + return out; +} + + +static cJSON *jsonDecodeCondition(cJSON *params, char *err) { + size_t cond_bin_len; + unsigned char *cond_bin; + if (!jsonGetHex(params, "bin", err, &cond_bin, &cond_bin_len)) + return NULL; + + CC *cond = cc_readConditionBinary(cond_bin, cond_bin_len); + free(cond_bin); + + if (!cond) { + strcpy(err, "Invalid condition payload"); + return NULL; + } + + cJSON *out = jsonCondition(cond); + cJSON_AddItemToObject(out, "condition", cc_conditionToJSON(cond)); + cc_free(cond); + return out; +} + + +static cJSON *jsonSignTreeEd25519(cJSON *params, char *err) { + cJSON *out = 0; + unsigned char *msg = 0, *sk = 0; + + cJSON *condition_item = cJSON_GetObjectItem(params, "condition"); + CC *cond = cc_conditionFromJSON(condition_item, err); + if (cond == NULL) { + goto END; + } + + size_t skLength; + if (!jsonGetHex(params, "privateKey", err, &sk, &skLength)) { + goto END; + } + + if (skLength != 32) { + strcpy(err, "privateKey wrong length"); + } + + size_t msgLength; + if (!jsonGetHex(params, "message", err, &msg, &msgLength)) { + goto END; + } + + int nSigned = cc_signTreeEd25519(cond, sk, msg, msgLength); + out = cJSON_CreateObject(); + cJSON_AddItemToObject(out, "num_signed", cJSON_CreateNumber(nSigned)); + cJSON_AddItemToObject(out, "condition", cc_conditionToJSON(cond)); + +END: + cc_free(cond); + free(msg); + free(sk); + return out; +} + + +static cJSON *jsonSignTreeSecp256k1(cJSON *params, char *err) { + cJSON *out = 0; + unsigned char *msg = 0, *sk = 0; + + cJSON *condition_item = cJSON_GetObjectItem(params, "condition"); + CC *cond = cc_conditionFromJSON(condition_item, err); + if (cond == NULL) { + goto END; + } + + size_t skLength; + if (!jsonGetHex(params, "privateKey", err, &sk, &skLength)) { + goto END; + } + + if (skLength != SECP256K1_SK_SIZE) { + strcpy(err, "privateKey wrong length"); + } + + size_t msgLength; + if (!jsonGetHex(params, "message", err, &msg, &msgLength)) { + goto END; + } + + char msgHash[32]; + sha256(msg, msgLength, msgHash); + int nSigned = cc_signTreeSecp256k1Msg32(cond, sk, msgHash); + out = cJSON_CreateObject(); + cJSON_AddItemToObject(out, "num_signed", cJSON_CreateNumber(nSigned)); + cJSON_AddItemToObject(out, "condition", cc_conditionToJSON(cond)); + +END: + cc_free(cond); + free(msg); + free(sk); + return out; +} + + +cJSON *cc_conditionToJSON(const CC *cond) { + cJSON *params = cJSON_CreateObject(); + cJSON_AddItemToObject(params, "type", cJSON_CreateString(cond->type->name)); + cond->type->toJSON(cond, params); + return params; +} + + +char *cc_conditionToJSONString(const CC *cond) { + assert(cond != NULL); + cJSON *params = cc_conditionToJSON(cond); + char *out = cJSON_Print(params); + cJSON_Delete(params); + return out; +} + + +static cJSON *jsonListMethods(cJSON *params, char *err); + + +typedef struct JsonMethod { + char *name; + cJSON* (*method)(cJSON *params, char *err); + char *description; +} JsonMethod; + + +static JsonMethod cc_jsonMethods[] = { + {"encodeCondition", &jsonEncodeCondition, "Encode a JSON condition to binary"}, + {"decodeCondition", &jsonDecodeCondition, "Decode a binary condition"}, + {"encodeFulfillment", &jsonEncodeFulfillment, "Encode a JSON condition to a fulfillment"}, + {"decodeFulfillment", &jsonDecodeFulfillment, "Decode a binary fulfillment"}, + {"verifyFulfillment", &jsonVerifyFulfillment, "Verify a fulfillment"}, + {"signTreeEd25519", &jsonSignTreeEd25519, "Sign ed25519 condition nodes"}, + {"signTreeSecp256k1", &jsonSignTreeSecp256k1, "Sign secp256k1 condition nodes"}, + {"listMethods", &jsonListMethods, "List available methods"} +}; + + +static int nJsonMethods = sizeof(cc_jsonMethods) / sizeof(*cc_jsonMethods); + + +static cJSON *jsonListMethods(cJSON *params, char *err) { + cJSON *list = cJSON_CreateArray(); + for (int i=0; ivaluestring)) { + return method.method(params, err); + } + } + + return jsonErr("invalid method"); +} + + +char *cc_jsonRPC(char* input) { + char err[1000] = "\0"; + cJSON *out; + cJSON *root = cJSON_Parse(input); + if (!root) out = jsonErr("Error parsing JSON request"); + else { + out = execJsonRPC(root, err); + if (NULL == out) out = jsonErr(err); + } + char *res = cJSON_Print(out); + cJSON_Delete(out); + cJSON_Delete(root); + return res; +} diff --git a/src/cryptoconditions/src/prefix.c b/src/cryptoconditions/src/prefix.c new file mode 100644 index 000000000..66d432bd2 --- /dev/null +++ b/src/cryptoconditions/src/prefix.c @@ -0,0 +1,123 @@ + +#include "asn/Condition.h" +#include "asn/Fulfillment.h" +#include "asn/PrefixFingerprintContents.h" +#include "asn/OCTET_STRING.h" +#include "include/cJSON.h" +#include "cryptoconditions.h" + + +struct CCType CC_PrefixType; + + +static int prefixVisitChildren(CC *cond, CCVisitor visitor) { + size_t prefixedLength = cond->prefixLength + visitor.msgLength; + unsigned char *prefixed = malloc(prefixedLength); + memcpy(prefixed, cond->prefix, cond->prefixLength); + memcpy(prefixed + cond->prefixLength, visitor.msg, visitor.msgLength); + visitor.msg = prefixed; + visitor.msgLength = prefixedLength; + int res = cc_visit(cond->subcondition, visitor); + free(prefixed); + return res; +} + + +static unsigned char *prefixFingerprint(const CC *cond) { + PrefixFingerprintContents_t *fp = calloc(1, sizeof(PrefixFingerprintContents_t)); + asnCondition(cond->subcondition, &fp->subcondition); // TODO: check asnCondition for safety + fp->maxMessageLength = cond->maxMessageLength; + OCTET_STRING_fromBuf(&fp->prefix, cond->prefix, cond->prefixLength); + return hashFingerprintContents(&asn_DEF_PrefixFingerprintContents, fp); +} + + +static unsigned long prefixCost(const CC *cond) { + return 1024 + cond->prefixLength + cond->maxMessageLength + + cond->subcondition->type->getCost(cond->subcondition); +} + + +static CC *prefixFromFulfillment(const Fulfillment_t *ffill) { + PrefixFulfillment_t *p = ffill->choice.prefixSha256; + CC *sub = fulfillmentToCC(p->subfulfillment); + if (!sub) return 0; + CC *cond = cc_new(CC_Prefix); + cond->maxMessageLength = p->maxMessageLength; + cond->prefix = calloc(1, p->prefix.size); + memcpy(cond->prefix, p->prefix.buf, p->prefix.size); + cond->prefixLength = p->prefix.size; + cond->subcondition = sub; + return cond; +} + + +static Fulfillment_t *prefixToFulfillment(const CC *cond) { + Fulfillment_t *ffill = asnFulfillmentNew(cond->subcondition); + if (!ffill) { + return NULL; + } + PrefixFulfillment_t *pf = calloc(1, sizeof(PrefixFulfillment_t)); + OCTET_STRING_fromBuf(&pf->prefix, cond->prefix, cond->prefixLength); + pf->maxMessageLength = cond->maxMessageLength; + pf->subfulfillment = ffill; + + ffill = calloc(1, sizeof(Fulfillment_t)); + ffill->present = Fulfillment_PR_prefixSha256; + ffill->choice.prefixSha256 = pf; + return ffill; +} + + +static uint32_t prefixSubtypes(const CC *cond) { + return cc_typeMask(cond->subcondition) & ~(1 << CC_Prefix); +} + + +static CC *prefixFromJSON(const cJSON *params, char *err) { + cJSON *mml_item = cJSON_GetObjectItem(params, "maxMessageLength"); + if (!cJSON_IsNumber(mml_item)) { + strcpy(err, "maxMessageLength must be a number"); + return NULL; + } + + cJSON *subcond_item = cJSON_GetObjectItem(params, "subfulfillment"); + CC *sub = cc_conditionFromJSON(subcond_item, err); + if (!sub) { + return NULL; + } + + CC *cond = cc_new(CC_Prefix); + cond->maxMessageLength = (unsigned long) mml_item->valuedouble; + cond->subcondition = sub; + + if (!jsonGetBase64(params, "prefix", err, &cond->prefix, &cond->prefixLength)) { + cc_free(cond); + return NULL; + } + + return cond; +} + + +static void prefixToJSON(const CC *cond, cJSON *params) { + cJSON_AddNumberToObject(params, "maxMessageLength", (double)cond->maxMessageLength); + unsigned char *b64 = base64_encode(cond->prefix, cond->prefixLength); + cJSON_AddStringToObject(params, "prefix", b64); + free(b64); + cJSON_AddItemToObject(params, "subfulfillment", cc_conditionToJSON(cond->subcondition)); +} + + +int prefixIsFulfilled(const CC *cond) { + return cc_isFulfilled(cond->subcondition); +} + + +static void prefixFree(CC *cond) { + free(cond->prefix); + cc_free(cond->subcondition); +} + + +struct CCType CC_PrefixType = { 1, "prefix-sha-256", Condition_PR_prefixSha256, &prefixVisitChildren, &prefixFingerprint, &prefixCost, &prefixSubtypes, &prefixFromJSON, &prefixToJSON, &prefixFromFulfillment, &prefixToFulfillment, &prefixIsFulfilled, &prefixFree }; diff --git a/src/cryptoconditions/src/preimage.c b/src/cryptoconditions/src/preimage.c new file mode 100644 index 000000000..2e3ec7c77 --- /dev/null +++ b/src/cryptoconditions/src/preimage.c @@ -0,0 +1,74 @@ + +#include "asn/Condition.h" +#include "asn/Fulfillment.h" +#include "asn/OCTET_STRING.h" +#include "include/cJSON.h" +#include "include/sha256.h" +#include "cryptoconditions.h" + + +struct CCType CC_PreimageType; + + +static CC *preimageFromJSON(const cJSON *params, char *err) { + CC *cond = cc_new(CC_Preimage); + if (!jsonGetBase64(params, "preimage", err, &cond->preimage, &cond->preimageLength)) { + free(cond); + return NULL; + } + return cond; +} + + +static void preimageToJSON(const CC *cond, cJSON *params) { + jsonAddBase64(params, "preimage", cond->preimage, cond->preimageLength); +} + + +static unsigned long preimageCost(const CC *cond) { + return (unsigned long) cond->preimageLength; +} + + +static unsigned char *preimageFingerprint(const CC *cond) { + unsigned char *hash = calloc(1, 32); + sha256(cond->preimage, cond->preimageLength, hash); + return hash; +} + + +static CC *preimageFromFulfillment(const Fulfillment_t *ffill) { + CC *cond = cc_new(CC_Preimage); + PreimageFulfillment_t p = ffill->choice.preimageSha256; + cond->preimage = calloc(1, p.preimage.size); + memcpy(cond->preimage, p.preimage.buf, p.preimage.size); + cond->preimageLength = p.preimage.size; + return cond; +} + + +static Fulfillment_t *preimageToFulfillment(const CC *cond) { + Fulfillment_t *ffill = calloc(1, sizeof(Fulfillment_t)); + ffill->present = Fulfillment_PR_preimageSha256; + PreimageFulfillment_t *pf = &ffill->choice.preimageSha256; + OCTET_STRING_fromBuf(&pf->preimage, cond->preimage, cond->preimageLength); + return ffill; +} + + +int preimageIsFulfilled(const CC *cond) { + return 1; +} + + +static void preimageFree(CC *cond) { + free(cond->preimage); +} + + +static uint32_t preimageSubtypes(const CC *cond) { + return 0; +} + + +struct CCType CC_PreimageType = { 0, "preimage-sha-256", Condition_PR_preimageSha256, 0, &preimageFingerprint, &preimageCost, &preimageSubtypes, &preimageFromJSON, &preimageToJSON, &preimageFromFulfillment, &preimageToFulfillment, &preimageIsFulfilled, &preimageFree }; diff --git a/src/cryptoconditions/src/secp256k1.c b/src/cryptoconditions/src/secp256k1.c new file mode 100644 index 000000000..73f7a9164 --- /dev/null +++ b/src/cryptoconditions/src/secp256k1.c @@ -0,0 +1,283 @@ +#define _GNU_SOURCE 1 + +#include +#include +#include + +#include "asn/Condition.h" +#include "asn/Fulfillment.h" +#include "asn/Secp256k1Fulfillment.h" +#include "asn/Secp256k1FingerprintContents.h" +#include "asn/OCTET_STRING.h" +#include "include/cJSON.h" +#include "include/secp256k1/include/secp256k1.h" +#include "cryptoconditions.h" +#include "internal.h" + + +struct CCType CC_Secp256k1Type; + + +static const size_t SECP256K1_PK_SIZE = 33; +static const size_t SECP256K1_SK_SIZE = 32; +static const size_t SECP256K1_SIG_SIZE = 64; + + +secp256k1_context *ec_ctx_sign = 0, *ec_ctx_verify = 0; +pthread_mutex_t cc_secp256k1ContextLock = PTHREAD_MUTEX_INITIALIZER; + + +void lockSign() { + pthread_mutex_lock(&cc_secp256k1ContextLock); + if (!ec_ctx_sign) { + ec_ctx_sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + } + unsigned char ent[32]; +#ifdef SYS_getrandom + int read = syscall(SYS_getrandom, ent, 32, 0); +#else + FILE *fp = fopen("/dev/urandom", "r"); + int read = (int) fread(&ent, 1, 32, fp); + fclose(fp); +#endif + if (read != 32) { + fprintf(stderr, "Could not read 32 bytes entropy from system\n"); + exit(1); + } + if (!secp256k1_context_randomize(ec_ctx_sign, ent)) { + fprintf(stderr, "Could not randomize secp256k1 context\n"); + exit(1); + } +} + + +void unlockSign() { + pthread_mutex_unlock(&cc_secp256k1ContextLock); +} + + +void initVerify() { + if (!ec_ctx_verify) { + pthread_mutex_lock(&cc_secp256k1ContextLock); + if (!ec_ctx_verify) + ec_ctx_verify = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + pthread_mutex_unlock(&cc_secp256k1ContextLock); + } +} + + +static unsigned char *secp256k1Fingerprint(const CC *cond) { + Secp256k1FingerprintContents_t *fp = calloc(1, sizeof(Secp256k1FingerprintContents_t)); + OCTET_STRING_fromBuf(&fp->publicKey, cond->publicKey, SECP256K1_PK_SIZE); + return hashFingerprintContents(&asn_DEF_Secp256k1FingerprintContents, fp); +} + + +int secp256k1Verify(CC *cond, CCVisitor visitor) { + if (cond->type->typeId != CC_Secp256k1Type.typeId) return 1; + initVerify(); + + int rc; + + // parse pubkey + secp256k1_pubkey pk; + rc = secp256k1_ec_pubkey_parse(ec_ctx_verify, &pk, cond->publicKey, SECP256K1_PK_SIZE); + if (rc != 1) return 0; + + // parse siganature + secp256k1_ecdsa_signature sig; + rc = secp256k1_ecdsa_signature_parse_compact(ec_ctx_verify, &sig, cond->signature); + if (rc != 1) return 0; + + // Only accepts lower S signatures + rc = secp256k1_ecdsa_verify(ec_ctx_verify, &sig, visitor.msg, &pk); + if (rc != 1) return 0; + + return 1; +} + + +int cc_secp256k1VerifyTreeMsg32(const CC *cond, const unsigned char *msg32) { + int subtypes = cc_typeMask(cond); + if (subtypes & (1 << CC_PrefixType.typeId) && + subtypes & (1 << CC_Secp256k1Type.typeId)) { + // No support for prefix currently, due to pending protocol decision on + // how to combine message and prefix into 32 byte hash + return 0; + } + CCVisitor visitor = {&secp256k1Verify, msg32, 0, NULL}; + int out = cc_visit(cond, visitor); + return out; +} + + +/* + * Signing data + */ +typedef struct CCSecp256k1SigningData { + const unsigned char *pk; + const unsigned char *sk; + int nSigned; +} CCSecp256k1SigningData; + + +/* + * Visitor that signs an secp256k1 condition if it has a matching public key + */ +static int secp256k1Sign(CC *cond, CCVisitor visitor) { + if (cond->type->typeId != CC_Secp256k1) return 1; + CCSecp256k1SigningData *signing = (CCSecp256k1SigningData*) visitor.context; + if (0 != memcmp(cond->publicKey, signing->pk, SECP256K1_PK_SIZE)) return 1; + + secp256k1_ecdsa_signature sig; + lockSign(); + int rc = secp256k1_ecdsa_sign(ec_ctx_sign, &sig, visitor.msg, signing->sk, NULL, NULL); + unlockSign(); + + if (rc != 1) return 0; + + if (!cond->signature) cond->signature = calloc(1, SECP256K1_SIG_SIZE); + secp256k1_ecdsa_signature_serialize_compact(ec_ctx_verify, cond->signature, &sig); + + signing->nSigned++; + return 1; +} + + +/* + * Sign secp256k1 conditions in a tree + */ +int cc_signTreeSecp256k1Msg32(CC *cond, const unsigned char *privateKey, const unsigned char *msg32) { + if (cc_typeMask(cond) & (1 << CC_Prefix)) { + // No support for prefix currently, due to pending protocol decision on + // how to combine message and prefix into 32 byte hash + return 0; + } + + // derive the pubkey + secp256k1_pubkey spk; + lockSign(); + int rc = secp256k1_ec_pubkey_create(ec_ctx_sign, &spk, privateKey); + unlockSign(); + if (rc != 1) { + fprintf(stderr, "Cryptoconditions couldn't derive secp256k1 pubkey\n"); + return 0; + } + + // serialize pubkey + unsigned char *publicKey = calloc(1, SECP256K1_PK_SIZE); + size_t ol = SECP256K1_PK_SIZE; + secp256k1_ec_pubkey_serialize(ec_ctx_verify, publicKey, &ol, &spk, SECP256K1_EC_COMPRESSED); + + // sign + CCSecp256k1SigningData signing = {publicKey, privateKey, 0}; + CCVisitor visitor = {&secp256k1Sign, msg32, 32, &signing}; + cc_visit(cond, visitor); + + free(publicKey); + return signing.nSigned; +} + + +static unsigned long secp256k1Cost(const CC *cond) { + return 131072; +} + + +static CC *cc_secp256k1Condition(const unsigned char *publicKey, const unsigned char *signature) { + // Check that pk parses + initVerify(); + secp256k1_pubkey spk; + int rc = secp256k1_ec_pubkey_parse(ec_ctx_verify, &spk, publicKey, SECP256K1_PK_SIZE); + if (!rc) { + return NULL; + } + + unsigned char *pk = 0, *sig = 0; + + pk = calloc(1, SECP256K1_PK_SIZE); + memcpy(pk, publicKey, SECP256K1_PK_SIZE); + if (signature) { + sig = calloc(1, SECP256K1_SIG_SIZE); + memcpy(sig, signature, SECP256K1_SIG_SIZE); + } + + CC *cond = cc_new(CC_Secp256k1); + cond->publicKey = pk; + cond->signature = sig; + return cond; +} + + +static CC *secp256k1FromJSON(const cJSON *params, char *err) { + CC *cond = 0; + unsigned char *pk = 0, *sig = 0; + size_t pkSize, sigSize; + + if (!jsonGetHex(params, "publicKey", err, &pk, &pkSize)) goto END; + + if (!jsonGetHexOptional(params, "signature", err, &sig, &sigSize)) goto END; + if (sig && SECP256K1_SIG_SIZE != sigSize) { + strcpy(err, "signature has incorrect length"); + goto END; + } + + cond = cc_secp256k1Condition(pk, sig); + if (!cond) { + strcpy(err, "invalid public key"); + } +END: + free(pk); + free(sig); + return cond; +} + + +static void secp256k1ToJSON(const CC *cond, cJSON *params) { + jsonAddHex(params, "publicKey", cond->publicKey, SECP256K1_PK_SIZE); + if (cond->signature) { + jsonAddHex(params, "signature", cond->signature, SECP256K1_SIG_SIZE); + } +} + + +static CC *secp256k1FromFulfillment(const Fulfillment_t *ffill) { + return cc_secp256k1Condition(ffill->choice.secp256k1Sha256.publicKey.buf, + ffill->choice.secp256k1Sha256.signature.buf); +} + + +static Fulfillment_t *secp256k1ToFulfillment(const CC *cond) { + if (!cond->signature) { + return NULL; + } + + Fulfillment_t *ffill = calloc(1, sizeof(Fulfillment_t)); + ffill->present = Fulfillment_PR_secp256k1Sha256; + Secp256k1Fulfillment_t *sec = &ffill->choice.secp256k1Sha256; + + OCTET_STRING_fromBuf(&sec->publicKey, cond->publicKey, SECP256K1_PK_SIZE); + OCTET_STRING_fromBuf(&sec->signature, cond->signature, SECP256K1_SIG_SIZE); + return ffill; +} + + +int secp256k1IsFulfilled(const CC *cond) { + return cond->signature > 0; +} + + +static void secp256k1Free(CC *cond) { + free(cond->publicKey); + if (cond->signature) { + free(cond->signature); + } +} + + +static uint32_t secp256k1Subtypes(const CC *cond) { + return 0; +} + + +struct CCType CC_Secp256k1Type = { 5, "secp256k1-sha-256", Condition_PR_secp256k1Sha256, 0, &secp256k1Fingerprint, &secp256k1Cost, &secp256k1Subtypes, &secp256k1FromJSON, &secp256k1ToJSON, &secp256k1FromFulfillment, &secp256k1ToFulfillment, &secp256k1IsFulfilled, &secp256k1Free }; diff --git a/src/cryptoconditions/src/stamp-h1 b/src/cryptoconditions/src/stamp-h1 new file mode 100644 index 000000000..8f14c586a --- /dev/null +++ b/src/cryptoconditions/src/stamp-h1 @@ -0,0 +1 @@ +timestamp for src/cryptoconditions-config.h diff --git a/src/cryptoconditions/src/threshold.c b/src/cryptoconditions/src/threshold.c new file mode 100644 index 000000000..9c1800302 --- /dev/null +++ b/src/cryptoconditions/src/threshold.c @@ -0,0 +1,229 @@ + +#include "asn/Condition.h" +#include "asn/Fulfillment.h" +#include "asn/ThresholdFingerprintContents.h" +#include "asn/OCTET_STRING.h" +#include "include/cJSON.h" +#include "cryptoconditions.h" +#include "internal.h" + + +struct CCType CC_ThresholdType; + + +static uint32_t thresholdSubtypes(const CC *cond) { + uint32_t mask = 0; + for (int i=0; isize; i++) { + mask |= cc_typeMask(cond->subconditions[i]); + } + mask &= ~(1 << CC_Threshold); + return mask; +} + + +static int cmpCostDesc(const void *a, const void *b) { + return (int) ( *(unsigned long*)b - *(unsigned long*)a ); +} + + +static unsigned long thresholdCost(const CC *cond) { + CC *sub; + unsigned long *costs = calloc(1, cond->size * sizeof(unsigned long)); + for (int i=0; isize; i++) { + sub = cond->subconditions[i]; + costs[i] = cc_getCost(sub); + } + qsort(costs, cond->size, sizeof(unsigned long), cmpCostDesc); + unsigned long cost = 0; + for (int i=0; ithreshold; i++) { + cost += costs[i]; + } + free(costs); + return cost + 1024 * cond->size; +} + + +static int thresholdVisitChildren(CC *cond, CCVisitor visitor) { + for (int i=0; isize; i++) { + if (!cc_visit(cond->subconditions[i], visitor)) { + return 0; + } + } + return 1; +} + + +static int cmpConditionBin(const void *a, const void *b) { + /* Compare conditions by their ASN binary representation */ + unsigned char bufa[BUF_SIZE], bufb[BUF_SIZE]; + asn_enc_rval_t r0 = der_encode_to_buffer(&asn_DEF_Condition, *(Condition_t**)a, bufa, BUF_SIZE); + asn_enc_rval_t r1 = der_encode_to_buffer(&asn_DEF_Condition, *(Condition_t**)b, bufb, BUF_SIZE); + + // below copied from ASN lib + size_t commonLen = r0.encoded < r1.encoded ? r0.encoded : r1.encoded; + int ret = memcmp(bufa, bufb, commonLen); + + if (ret == 0) + return r0.encoded < r1.encoded ? -1 : 1; + return 0; +} + + +static unsigned char *thresholdFingerprint(const CC *cond) { + /* Create fingerprint */ + ThresholdFingerprintContents_t *fp = calloc(1, sizeof(ThresholdFingerprintContents_t)); + fp->threshold = cond->threshold; + for (int i=0; isize; i++) { + Condition_t *asnCond = asnConditionNew(cond->subconditions[i]); + asn_set_add(&fp->subconditions2, asnCond); + } + qsort(fp->subconditions2.list.array, cond->size, sizeof(Condition_t*), cmpConditionBin); + return hashFingerprintContents(&asn_DEF_ThresholdFingerprintContents, fp); +} + + +static int cmpConditionCost(const void *a, const void *b) { + CC *ca = *((CC**)a); + CC *cb = *((CC**)b); + + int out = cc_getCost(ca) - cc_getCost(cb); + if (out != 0) return out; + + // Do an additional sort to establish consistent order + // between conditions with the same cost. + Condition_t *asna = asnConditionNew(ca); + Condition_t *asnb = asnConditionNew(cb); + out = cmpConditionBin(&asna, &asnb); + ASN_STRUCT_FREE(asn_DEF_Condition, asna); + ASN_STRUCT_FREE(asn_DEF_Condition, asnb); + return out; +} + + +static CC *thresholdFromFulfillment(const Fulfillment_t *ffill) { + ThresholdFulfillment_t *t = ffill->choice.thresholdSha256; + int threshold = t->subfulfillments.list.count; + int size = threshold + t->subconditions.list.count; + + CC **subconditions = calloc(size, sizeof(CC*)); + + for (int i=0; isubfulfillments.list.array[i]) : + mkAnon(t->subconditions.list.array[i-threshold]); + + if (!subconditions[i]) { + for (int j=0; jthreshold = threshold; + cond->size = size; + cond->subconditions = subconditions; + return cond; +} + + +static Fulfillment_t *thresholdToFulfillment(const CC *cond) { + CC *sub; + Fulfillment_t *fulfillment; + + // Make a copy of subconditions so we can leave original order alone + CC** subconditions = malloc(cond->size*sizeof(CC*)); + memcpy(subconditions, cond->subconditions, cond->size*sizeof(CC*)); + + qsort(subconditions, cond->size, sizeof(CC*), cmpConditionCost); + + ThresholdFulfillment_t *tf = calloc(1, sizeof(ThresholdFulfillment_t)); + + int needed = cond->threshold; + + for (int i=0; isize; i++) { + sub = subconditions[i]; + if (needed && (fulfillment = asnFulfillmentNew(sub))) { + asn_set_add(&tf->subfulfillments, fulfillment); + needed--; + } else { + asn_set_add(&tf->subconditions, asnConditionNew(sub)); + } + } + + free(subconditions); + + if (needed) { + ASN_STRUCT_FREE(asn_DEF_ThresholdFulfillment, tf); + return NULL; + } + + fulfillment = calloc(1, sizeof(Fulfillment_t)); + fulfillment->present = Fulfillment_PR_thresholdSha256; + fulfillment->choice.thresholdSha256 = tf; + return fulfillment; +} + + +static CC *thresholdFromJSON(const cJSON *params, char *err) { + cJSON *threshold_item = cJSON_GetObjectItem(params, "threshold"); + if (!cJSON_IsNumber(threshold_item)) { + strcpy(err, "threshold must be a number"); + return NULL; + } + + cJSON *subfulfillments_item = cJSON_GetObjectItem(params, "subfulfillments"); + if (!cJSON_IsArray(subfulfillments_item)) { + strcpy(err, "subfulfullments must be an array"); + return NULL; + } + + CC *cond = cc_new(CC_Threshold); + cond->threshold = (long) threshold_item->valuedouble; + cond->size = cJSON_GetArraySize(subfulfillments_item); + cond->subconditions = calloc(cond->size, sizeof(CC*)); + + cJSON *sub; + for (int i=0; isize; i++) { + sub = cJSON_GetArrayItem(subfulfillments_item, i); + cond->subconditions[i] = cc_conditionFromJSON(sub, err); + if (err[0]) return NULL; + } + + return cond; +} + + +static void thresholdToJSON(const CC *cond, cJSON *params) { + cJSON *subs = cJSON_CreateArray(); + cJSON_AddNumberToObject(params, "threshold", cond->threshold); + for (int i=0; isize; i++) { + cJSON_AddItemToArray(subs, cc_conditionToJSON(cond->subconditions[i])); + } + cJSON_AddItemToObject(params, "subfulfillments", subs); +} + + +static int thresholdIsFulfilled(const CC *cond) { + int nFulfilled = 0; + for (int i=0; isize; i++) { + if (cc_isFulfilled(cond->subconditions[i])) { + nFulfilled++; + } + if (nFulfilled == cond->threshold) { + return 1; + } + } + return 0; +} + + +static void thresholdFree(CC *cond) { + for (int i=0; isize; i++) { + cc_free(cond->subconditions[i]); + } + free(cond->subconditions); +} + + +struct CCType CC_ThresholdType = { 2, "threshold-sha-256", Condition_PR_thresholdSha256, &thresholdVisitChildren, &thresholdFingerprint, &thresholdCost, &thresholdSubtypes, &thresholdFromJSON, &thresholdToJSON, &thresholdFromFulfillment, &thresholdToFulfillment, &thresholdIsFulfilled, &thresholdFree }; diff --git a/src/cryptoconditions/src/utils.c b/src/cryptoconditions/src/utils.c new file mode 100644 index 000000000..b795932b3 --- /dev/null +++ b/src/cryptoconditions/src/utils.c @@ -0,0 +1,289 @@ +#include +#include +#include +#include +#include + +#include "include/cJSON.h" +#include "include/sha256.h" +#include "asn/asn_application.h" +#include "cryptoconditions.h" +#include "internal.h" + + +static unsigned char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/'}; +static unsigned char *decoding_table = NULL; +static int mod_table[] = {0, 2, 1}; + + +void build_decoding_table() { + decoding_table = malloc(256); + for (int i = 0; i < 64; i++) + decoding_table[(unsigned char) encoding_table[i]] = i; +} + + +unsigned char *base64_encode(const unsigned char *data, size_t input_length) { + + size_t output_length = 4 * ((input_length + 2) / 3); + + unsigned char *encoded_data = malloc(output_length + 1); + if (encoded_data == NULL) return NULL; + + for (int i = 0, j = 0; i < input_length;) { + + uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0; + uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0; + uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0; + + uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; + + encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F]; + encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F]; + encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F]; + encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F]; + } + + int strip = mod_table[input_length % 3]; + for (int i = 0; i < strip; i++) + encoded_data[output_length - 1 - i] = '\0'; + // make sure there's a null termination for string protocol + encoded_data[output_length] = '\0'; + + + // url safe + for (int i=0; i> 2 * 8) & 0xFF; + if (j < *output_length) decoded_data[j++] = (triple >> 1 * 8) & 0xFF; + if (j < *output_length) decoded_data[j++] = (triple >> 0 * 8) & 0xFF; + } + + return decoded_data; +} + + +void base64_cleanup() { + free(decoding_table); +} + + +void dumpStr(unsigned char *str, size_t len) { + if (-1 == len) len = strlen(str); + fprintf(stderr, "len:%i ", (int)len); + for (int i=0; ivaluestring, size); + if (!*data) { + sprintf(err, "%s must be valid base64 string", key); + return 0; + } + return 1; +} + + +int jsonGetBase64(const cJSON *params, char *key, char *err, unsigned char **data, size_t *size) +{ + cJSON *item = cJSON_GetObjectItem(params, key); + if (!item) { + sprintf(err, "%s is required", key); + return 0; + } + return checkDecodeBase64(item, key, err, data, size); +} + + +int jsonGetBase64Optional(const cJSON *params, char *key, char *err, unsigned char **data, size_t *size) { + cJSON *item = cJSON_GetObjectItem(params, key); + if (!item) { + return 1; + } + return checkDecodeBase64(item, key, err, data, size); +} + + +void jsonAddBase64(cJSON *params, char *key, unsigned char *bin, size_t size) { + unsigned char *b64 = base64_encode(bin, size); + cJSON_AddItemToObject(params, key, cJSON_CreateString(b64)); + free(b64); +} + + +unsigned char *hashFingerprintContents(asn_TYPE_descriptor_t *asnType, void *fp) { + unsigned char buf[BUF_SIZE]; + asn_enc_rval_t rc = der_encode_to_buffer(asnType, fp, buf, BUF_SIZE); + ASN_STRUCT_FREE(*asnType, fp); + if (rc.encoded < 1) { + fprintf(stderr, "Encoding fingerprint failed\n"); + return 0; + } + unsigned char *hash = malloc(32); + sha256(buf, rc.encoded, hash); + return hash; +} + + +char* cc_hex_encode(const uint8_t *bin, size_t len) +{ + char* hex = malloc(len*2+1); + if (bin == NULL) return hex; + char map[16] = "0123456789ABCDEF"; + for (int i=0; i> 4]; + hex[i*2+1] = map[bin[i] & 0x0F]; + } + hex[len*2] = '\0'; + return hex; +} + + +uint8_t* cc_hex_decode(const char* hex) +{ + size_t len = strlen(hex); + + if (len % 2 == 1) return NULL; + + uint8_t* bin = calloc(1, len/2); + + for (int i=0; i 15) goto ERR; + + bin[i/2] += c << (i%2 ? 0 : 4); + } + return bin; +ERR: + free(bin); + return NULL; +} + + +bool checkDecodeHex(const cJSON *value, char *key, char *err, unsigned char **data, size_t *size) { + if (!checkString(value, key, err)) + return 0; + + *data = cc_hex_decode(value->valuestring); + if (!*data) { + sprintf(err, "%s must be valid hex string", key); + return 0; + } + *size = strlen(value->valuestring) / 2; + return 1; +} + + +bool jsonGetHex(const cJSON *params, char *key, char *err, unsigned char **data, size_t *size) +{ + cJSON *item = cJSON_GetObjectItem(params, key); + if (!item) { + sprintf(err, "%s is required", key); + return 0; + } + return checkDecodeHex(item, key, err, data, size); +} + + +void jsonAddHex(cJSON *params, char *key, unsigned char *bin, size_t size) { + unsigned char *hex = cc_hex_encode(bin, size); + cJSON_AddItemToObject(params, key, cJSON_CreateString(hex)); + free(hex); +} + + +int jsonGetHexOptional(const cJSON *params, char *key, char *err, unsigned char **data, size_t *size) { + cJSON *item = cJSON_GetObjectItem(params, key); + if (!item) { + return 1; + } + return checkDecodeHex(item, key, err, data, size); +} + + diff --git a/src/cryptoconditions/test-requirements.txt b/src/cryptoconditions/test-requirements.txt new file mode 100644 index 000000000..3e7a933b9 --- /dev/null +++ b/src/cryptoconditions/test-requirements.txt @@ -0,0 +1,3 @@ +base58==0.2.5 +secp256k1 +pytest diff --git a/src/cryptoconditions/tests/__init__.py b/src/cryptoconditions/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/cryptoconditions/tests/test_ed25519.py b/src/cryptoconditions/tests/test_ed25519.py new file mode 100644 index 000000000..ba97bdd9d --- /dev/null +++ b/src/cryptoconditions/tests/test_ed25519.py @@ -0,0 +1,73 @@ +import json +import base64 +from .test_vectors import jsonRPC + + +def test_sign_ed25519_pass_simple(): + res = jsonRPC('signTreeEd25519', { + 'condition': { + 'type': 'ed25519-sha-256', + 'publicKey': "E0x0Ws4GhWhO_zBoUyaLbuqCz6hDdq11Ft1Dgbe9y9k", + }, + 'privateKey': 'D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A', + 'message': '', + }) + + assert res == { + "num_signed": 1, + "condition": { + "type": "ed25519-sha-256", + "publicKey": "E0x0Ws4GhWhO_zBoUyaLbuqCz6hDdq11Ft1Dgbe9y9k", + "signature": "jcuovSRpHwqiC781KzSM1Jd0Qtyfge0cMGttUdLOVdjJlSBFLTtgpinASOaJpd-VGjhSGWkp1hPWuMAAZq6pAg" + } + } + + +def test_sign_ed25519_pass_prefix(): + res = jsonRPC('signTreeEd25519', { + 'condition': { + 'type': 'prefix-sha-256', + 'prefix': 'YmJi', + 'maxMessageLength': 3, + 'subfulfillment': { + 'type': 'ed25519-sha-256', + 'publicKey': "E0x0Ws4GhWhO_zBoUyaLbuqCz6hDdq11Ft1Dgbe9y9k", + } + }, + 'privateKey': 'D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A', + 'message': '', + }) + + assert res == { + "num_signed": 1, + 'condition': { + 'type': 'prefix-sha-256', + 'prefix': 'YmJi', + 'maxMessageLength': 3, + 'subfulfillment': { + 'type': 'ed25519-sha-256', + 'publicKey': "E0x0Ws4GhWhO_zBoUyaLbuqCz6hDdq11Ft1Dgbe9y9k", + 'signature': '4Y6keUFEl4KgIum9e3MBdlhlp32FRas-1N1vhtdy5q3JEPdqMvmXo2Rb99fC6j_3vflh8_QtOEW5rj4utjMOBg', + } + }, + } + + +def test_sign_ed25519_fail(): + # privateKey doesnt match publicKey + res = jsonRPC('signTreeEd25519', { + 'condition': { + 'type': 'ed25519-sha-256', + 'publicKey': "E0x0Ws4GhWhO_zBoUyaLbuqCz6hDdq11Ft1Dgbe9y9k", + }, + 'privateKey': '225A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A', + 'message': '', + }) + + assert res == { + "num_signed": 0, + "condition": { + "type": "ed25519-sha-256", + "publicKey": "E0x0Ws4GhWhO_zBoUyaLbuqCz6hDdq11Ft1Dgbe9y9k", + } + } diff --git a/src/cryptoconditions/tests/test_failure_modes.py b/src/cryptoconditions/tests/test_failure_modes.py new file mode 100644 index 000000000..59b0b3f24 --- /dev/null +++ b/src/cryptoconditions/tests/test_failure_modes.py @@ -0,0 +1,85 @@ +import json +import ctypes +import base64 +from .test_vectors import jsonRPC, so, decode_base64 as d64, encode_base64 as e64 + + +''' +These tests are aimed at edge cases of serverside deployment. + +As such, the main functions to test are decoding and verifying fulfillment payloads. +''' + +cc_rfb = lambda f: so.cc_readFulfillmentBinary(f, len(f)) +cc_rcb = lambda f: so.cc_readConditionBinary(f, len(f)) + +unhex = lambda s: base64.b16decode(s.upper()) + + +def test_decode_valid_fulfillment(): + f = unhex('a42480206ee12ed43d7dce6fc0b2be20e6808380baafc03d400404bbf95165d7527b373a8100') + assert cc_rfb(f) + + +def test_decode_invalid_fulfillment(): + # This fulfillment payload has an invalid type ID but is otherwise valid + invalid_type_id = unhex('bf632480206ee12ed43d7dce6fc0b2be20e6808380baafc03d400404bbf95165d7527b373a8100') + assert cc_rfb(invalid_type_id) == 0 + assert cc_rfb('\0') == 0 + assert cc_rfb('') == 0 + + +def test_large_fulfillment(): + # This payload is valid and very large + f = unhex("a1839896b28083989680") + 10000000 * b'e' + \ + unhex("81030186a0a226a42480206ee12ed43d7dce6fc0b2be20e68083" + "80baafc03d400404bbf95165d7527b373a8100") + cond = cc_rfb(f) + buflen = len(f) + 1000 # Wiggle room + buf = ctypes.create_string_buffer(buflen) + assert so.cc_fulfillmentBinary(cond, buf, buflen) + so.cc_free(cond) + + +def test_decode_valid_condition(): + # Valid preimage + assert cc_rcb(d64(b'oCWAIMqXgRLKG73K-sIxs5oj3E2nhu_4FHxOcrmAd4Wv7ki7gQEB')) + + # Somewhat bogus condition (prefix with no subtypes) but nonetheless valid structure + assert cc_rcb(unhex("a10a8001618101ff82020700")) + + +def test_decode_invalid_condition(): + assert 0 == cc_rcb("") + assert 0 == cc_rcb("\0") + + # Bogus type ID + assert 0 == cc_rcb(unhex('bf630c80016181030186a082020700')) + + +def test_validate_empty_sigs(): + #// TODO: test failure mode: empty sig / null pointer + pass + + +def test_non_canonical_secp256k1(): + cond = { + "type": "secp256k1-sha-256", + "publicKey": "02D5D969305535AC29A77079C11D4F0DD40661CF96E04E974A5E8D7E374EE225AA", + # Signature is correct, but non canonical; validation should fail + "signature": "9C2D6FF39F340BBAF65E884BDFFAE7436A7B7568839C5BA117FA6818245FB9DAC32138302B2C208E6E424DD9C702790AE10B241B89C8D0A06B01CB708334AB6E" + } + res = jsonRPC('verifyFulfillment', { + 'fulfillment': jsonRPC('encodeFulfillment', cond)['fulfillment'], + 'condition': jsonRPC('encodeCondition', cond)['bin'], + 'message': '' + }) + assert res['valid'] == False + + +def test_malleability_checked(): + assert cc_rfb(b'\xa2\x13\xa0\x0f\xa0\x05\x80\x03abc\xa0\x06\x80\x04abcd\xa1\x00') + assert not cc_rfb(b'\xa2\x13\xa0\x0f\xa0\x06\x80\x04abcd\xa0\x05\x80\x03abc\xa1\x00') + + +so.cc_conditionUri.restype = ctypes.c_char_p diff --git a/src/cryptoconditions/tests/test_secp256k1.py b/src/cryptoconditions/tests/test_secp256k1.py new file mode 100644 index 000000000..fe3c72b67 --- /dev/null +++ b/src/cryptoconditions/tests/test_secp256k1.py @@ -0,0 +1,66 @@ +import json +import base64 +import hashlib +import secp256k1 +from .test_vectors import jsonRPC + + +key = secp256k1.PrivateKey() + + +def test_sign_secp256k1_pass_simple(): + pubkey = encode_b16(key.pubkey.serialize()) + msg = '' + res = jsonRPC('signTreeSecp256k1', { + 'condition': { + 'type': 'secp256k1-sha-256', + 'publicKey': pubkey, + }, + 'privateKey': encode_b16(key.private_key), + 'message': msg, + }) + + sig = encode_b16(key.ecdsa_serialize_compact(key.ecdsa_sign(msg.encode()))) + + assert res == { + "num_signed": 1, + "condition": { + "type": "secp256k1-sha-256", + "publicKey": pubkey, + "signature": sig + } + } + cond_bin = jsonRPC('encodeCondition', res['condition'])['bin'] + ffill_bin = jsonRPC('encodeFulfillment', res['condition'])['fulfillment'] + assert jsonRPC('verifyFulfillment', { + 'fulfillment': ffill_bin, + 'message': msg, + 'condition': cond_bin + }) == {'valid': True} + + + +def test_sign_secp256k1_fail(): + # privateKey doesnt match publicKey + pubkey = encode_b16(key.pubkey.serialize()) + msg = '' + res = jsonRPC('signTreeSecp256k1', { + 'condition': { + 'type': 'secp256k1-sha-256', + 'publicKey': pubkey, + }, + 'privateKey': encode_b16(b'0' * 32), + 'message': msg, + }) + + assert res == { + "num_signed": 0, + "condition": { + "type": "secp256k1-sha-256", + "publicKey": pubkey, + } + } + + +def encode_b16(s): + return base64.b16encode(s).decode() diff --git a/src/cryptoconditions/tests/test_vectors.py b/src/cryptoconditions/tests/test_vectors.py new file mode 100644 index 000000000..884d695be --- /dev/null +++ b/src/cryptoconditions/tests/test_vectors.py @@ -0,0 +1,152 @@ +import json +import ctypes +import base64 +import pytest +import os.path +from ctypes import * + + +v0000 = '0000_test-minimal-preimage' +v0001 = '0001_test-minimal-prefix' +v0002 = '0002_test-minimal-threshold' +v0003 = '0003_test-minimal-rsa' +v0004 = '0004_test-minimal-ed25519' +v0005 = '0005_test-basic-preimage' +v0006 = '0006_test-basic-prefix' +v0007 = '0007_test-basic-prefix-two-levels-deep' +v0010 = '0010_test-basic-threshold-same-fulfillment-twice' +v0015 = '0015_test-basic-ed25519' +v0016 = '0016_test-advanced-notarized-receipt' +v0017 = '0017_test-advanced-notarized-receipt-multiple-notaries' + +# These contain RSA conditions which are not implemented yet +#v0008 = '0008_test-basic-threshold' +#v0009 = '0009_test-basic-threshold-same-condition-twice' +#v0011 = '0011_test-basic-threshold-two-levels-deep' +#v0012 = '0012_test-basic-threshold-schroedinger' +#v0013 = '0013_test-basic-rsa' +#v0014 = '0014_test-basic-rsa4096' + +# Custom test vectors +v1000 = '1000_test-minimal-eval' +v1001 = '1001_test-minimal-secp256k1' + + +all_vectors = {v0000, v0001, v0002, v0004, v0005, v0006, v0007, v0010, + v0015, v0016, v0017, v1000, v1001} + + +@pytest.mark.parametrize('vectors_file', all_vectors) +def test_encodeCondition(vectors_file): + vectors = _read_vectors(vectors_file) + response = jsonRPC('encodeCondition', vectors['json']) + assert response == { + 'uri': vectors['conditionUri'], + 'bin': vectors['conditionBinary'], + } + + +@pytest.mark.parametrize('vectors_file', all_vectors) +def test_encodeFulfillment(vectors_file): + vectors = _read_vectors(vectors_file) + response = jsonRPC('encodeFulfillment', vectors['json']) + assert response == { + 'fulfillment': vectors['fulfillment'], + } + + +@pytest.mark.parametrize('vectors_file', all_vectors) +def test_verifyFulfillment(vectors_file): + vectors = _read_vectors(vectors_file) + req = { + 'fulfillment': vectors['fulfillment'], + 'message': vectors['message'], + 'condition': vectors['conditionBinary'], + } + assert jsonRPC('verifyFulfillment', req) == {'valid': True} + + +@pytest.mark.parametrize('vectors_file', all_vectors) +def test_decodeFulfillment(vectors_file): + vectors = _read_vectors(vectors_file) + response = jsonRPC('decodeFulfillment', { + 'fulfillment': vectors['fulfillment'], + }) + assert response == { + 'uri': vectors['conditionUri'], + 'bin': vectors['conditionBinary'], + } + + +@pytest.mark.parametrize('vectors_file', all_vectors) +def test_decodeCondition(vectors_file): + vectors = _read_vectors(vectors_file) + response = jsonRPC('decodeCondition', { + 'bin': vectors['conditionBinary'], + }) + assert response['uri'] == vectors['conditionUri'] + + +@pytest.mark.parametrize('vectors_file', all_vectors) +def test_json_condition_json_parse(vectors_file): + vectors = _read_vectors(vectors_file) + err = ctypes.create_string_buffer(100) + cc = so.cc_conditionFromJSONString(json.dumps(vectors['json']).encode(), err) + out_ptr = so.cc_conditionToJSONString(cc) + out = ctypes.cast(out_ptr, c_char_p).value.decode() + assert json.loads(out) == vectors['json'] + + +def b16_to_b64(b16): + return base64.urlsafe_b64encode(base64.b16decode(b16)).rstrip('=') + + +def decode_base64(data): + """Decode base64, padding being optional. + + :param data: Base64 data as an ASCII byte string + :returns: The decoded byte string. + """ + missing_padding = len(data) % 4 + if missing_padding: + data += '=' * (4 - missing_padding) + return base64.urlsafe_b64decode(data) + + +def encode_base64(data): + if type(data) == str: + data = data.encode() + return base64.urlsafe_b64encode(data).rstrip(b'=').decode() + + +def b16_to_b64(b16): + if type(b16) == str: + b16 = b16.encode() + return encode_base64(base64.b16decode(b16)) + + +def b64_to_b16(b64): + #if type(b64) == str: + # b64 = b64.encode() + return base64.b16encode(decode_base64(b64)).decode() + + +def _read_vectors(name): + path = 'tests/vectors/%s.json' % name + if os.path.isfile(path): + return json.load(open(path)) + raise IOError("Vectors file not found: %s.json" % name) + + +so = cdll.LoadLibrary('.libs/libcryptoconditions.so') +so.cc_jsonRPC.restype = c_char_p + + + +def jsonRPC(method, params): + req = json.dumps({ + 'method': method, + 'params': params, + }) + out = so.cc_jsonRPC(req.encode()) + return json.loads(out.decode()) diff --git a/src/cryptoconditions/tests/vectors/0000_test-minimal-preimage.json b/src/cryptoconditions/tests/vectors/0000_test-minimal-preimage.json new file mode 100644 index 000000000..a0cf7be8f --- /dev/null +++ b/src/cryptoconditions/tests/vectors/0000_test-minimal-preimage.json @@ -0,0 +1,13 @@ +{ + "json": { + "type": "preimage-sha-256", + "preimage": "" + }, + "cost": 0, + "subtypes": [], + "fingerprintContents": "", + "fulfillment": "A0028000", + "conditionBinary": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100", + "conditionUri": "ni:///sha-256;47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU?fpt=preimage-sha-256&cost=0", + "message": "" +} \ No newline at end of file diff --git a/src/cryptoconditions/tests/vectors/0001_test-minimal-prefix.json b/src/cryptoconditions/tests/vectors/0001_test-minimal-prefix.json new file mode 100644 index 000000000..e2e942e9f --- /dev/null +++ b/src/cryptoconditions/tests/vectors/0001_test-minimal-prefix.json @@ -0,0 +1,20 @@ +{ + "json": { + "type": "prefix-sha-256", + "maxMessageLength": 0, + "prefix": "", + "subfulfillment": { + "type": "preimage-sha-256", + "preimage": "" + } + }, + "cost": 1024, + "subtypes": [ + "preimage-sha-256" + ], + "fingerprintContents": "302E8000810100A227A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100", + "fulfillment": "A10B8000810100A204A0028000", + "conditionBinary": "A12A8020BB1AC5260C0141B7E54B26EC2330637C5597BF811951AC09E744AD20FF77E2878102040082020780", + "conditionUri": "ni:///sha-256;uxrFJgwBQbflSybsIzBjfFWXv4EZUawJ50StIP934oc?fpt=prefix-sha-256&cost=1024&subtypes=preimage-sha-256", + "message": "" +} \ No newline at end of file diff --git a/src/cryptoconditions/tests/vectors/0002_test-minimal-threshold.json b/src/cryptoconditions/tests/vectors/0002_test-minimal-threshold.json new file mode 100644 index 000000000..67d2d551e --- /dev/null +++ b/src/cryptoconditions/tests/vectors/0002_test-minimal-threshold.json @@ -0,0 +1,21 @@ +{ + "json": { + "type": "threshold-sha-256", + "threshold": 1, + "subfulfillments": [ + { + "type": "preimage-sha-256", + "preimage": "" + } + ] + }, + "cost": 1024, + "subtypes": [ + "preimage-sha-256" + ], + "fingerprintContents": "302C800101A127A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100", + "fulfillment": "A208A004A0028000A100", + "conditionBinary": "A22A8020B4B84136DF48A71D73F4985C04C6767A778ECB65BA7023B4506823BEEE7631B98102040082020780", + "conditionUri": "ni:///sha-256;tLhBNt9Ipx1z9JhcBMZ2eneOy2W6cCO0UGgjvu52Mbk?fpt=threshold-sha-256&cost=1024&subtypes=preimage-sha-256", + "message": "" +} \ No newline at end of file diff --git a/src/cryptoconditions/tests/vectors/0003_test-minimal-rsa.json b/src/cryptoconditions/tests/vectors/0003_test-minimal-rsa.json new file mode 100644 index 000000000..881a8c527 --- /dev/null +++ b/src/cryptoconditions/tests/vectors/0003_test-minimal-rsa.json @@ -0,0 +1,14 @@ +{ + "json": { + "type": "rsa-sha-256", + "modulus": "4e-LJNb3awnIHtd1KqJi8ETwSodNQ4CdMc6mEvmbDJeotDdBU-Pu89ZmFoQ-DkHCkyZLcbYXPbHPDWzVWMWGV3Bvzwl_cExIPlnL_f1bPue8gNdAxeDwR_PoX8DXWBV3am8_I8XcXnlxOaaILjgzakpfs2E3Yg_zZj264yhHKAGGL3Ly-HsgK5yJrdfNWwoHb3xT41A59n7RfsgV5bQwXMYxlwaNXm5Xm6beX04-V99eTgcv8s5MZutFIzlzh1J1ljnwJXv1fb1cRD-1FYzOCj02rce6AfM6C7bbsr-YnWBxEvI0TZk-d-VjwdNh3t9X2pbvLPxoXwArY4JGpbMJuQ", + "signature": "vULWVp9lma7UVflrwO0I7RSAvzbNnhRn-cb3RGHJ46dJM0svZASqX59rr-dsNH0GklCzXRyXDHkwWe5zOoGT8w-nj-x8rkWePd_XYzgF1HaUDQy1PX-zidza6vboz0jEtWNUMOTyvN_lBcLA_Be0DZPH7bfCYev0OJWnBeAkqgVJpmD3CjIVBkdSLb5rY1IEl8_4-NXXR2iifFuG5YC-P83Jbxl2KTy6DVjfxgtRi2MqbcHpUMQ-Ix_ho3mqbdzFLHDt-FHGwBI6lkJhz9s4V81s1a3DfY2izJJO2uHYTPYSRYfydMH6NpfaKQHwJp8DskPAO2FOA4Xhlh-sUAD5uw" + }, + "cost": 65536, + "subtypes": [], + "fingerprintContents": "3082010480820100E1EF8B24D6F76B09C81ED7752AA262F044F04A874D43809D31CEA612F99B0C97A8B4374153E3EEF3D66616843E0E41C293264B71B6173DB1CF0D6CD558C58657706FCF097F704C483E59CBFDFD5B3EE7BC80D740C5E0F047F3E85FC0D75815776A6F3F23C5DC5E797139A6882E38336A4A5FB36137620FF3663DBAE328472801862F72F2F87B202B9C89ADD7CD5B0A076F7C53E35039F67ED17EC815E5B4305CC63197068D5E6E579BA6DE5F4E3E57DF5E4E072FF2CE4C66EB452339738752759639F0257BF57DBD5C443FB5158CCE0A3D36ADC7BA01F33A0BB6DBB2BF989D607112F2344D993E77E563C1D361DEDF57DA96EF2CFC685F002B638246A5B309B9", + "fulfillment": "A382020880820100E1EF8B24D6F76B09C81ED7752AA262F044F04A874D43809D31CEA612F99B0C97A8B4374153E3EEF3D66616843E0E41C293264B71B6173DB1CF0D6CD558C58657706FCF097F704C483E59CBFDFD5B3EE7BC80D740C5E0F047F3E85FC0D75815776A6F3F23C5DC5E797139A6882E38336A4A5FB36137620FF3663DBAE328472801862F72F2F87B202B9C89ADD7CD5B0A076F7C53E35039F67ED17EC815E5B4305CC63197068D5E6E579BA6DE5F4E3E57DF5E4E072FF2CE4C66EB452339738752759639F0257BF57DBD5C443FB5158CCE0A3D36ADC7BA01F33A0BB6DBB2BF989D607112F2344D993E77E563C1D361DEDF57DA96EF2CFC685F002B638246A5B309B981820100BD42D6569F6599AED455F96BC0ED08ED1480BF36CD9E1467F9C6F74461C9E3A749334B2F6404AA5F9F6BAFE76C347D069250B35D1C970C793059EE733A8193F30FA78FEC7CAE459E3DDFD7633805D476940D0CB53D7FB389DCDAEAF6E8CF48C4B5635430E4F2BCDFE505C2C0FC17B40D93C7EDB7C261EBF43895A705E024AA0549A660F70A32150647522DBE6B63520497CFF8F8D5D74768A27C5B86E580BE3FCDC96F1976293CBA0D58DFC60B518B632A6DC1E950C43E231FE1A379AA6DDCC52C70EDF851C6C0123A964261CFDB3857CD6CD5ADC37D8DA2CC924EDAE1D84CF6124587F274C1FA3697DA2901F0269F03B243C03B614E0385E1961FAC5000F9BB", + "conditionBinary": "A3278020B31FA8206E4EA7E515337B3B33082B877651801085ED84FB4DAEB247BF698D7F8103010000", + "conditionUri": "ni:///sha-256;sx-oIG5Op-UVM3s7Mwgrh3ZRgBCF7YT7Ta6yR79pjX8?fpt=rsa-sha-256&cost=65536", + "message": "" +} \ No newline at end of file diff --git a/src/cryptoconditions/tests/vectors/0004_test-minimal-ed25519.json b/src/cryptoconditions/tests/vectors/0004_test-minimal-ed25519.json new file mode 100644 index 000000000..669dc6d98 --- /dev/null +++ b/src/cryptoconditions/tests/vectors/0004_test-minimal-ed25519.json @@ -0,0 +1,14 @@ +{ + "json": { + "type": "ed25519-sha-256", + "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + "signature": "5VZDAMNgrHKQhuLMgG6CioSHfx645dl02HPgZSJJAVVfuIIVkKM7rMYeOXAc-bRr0lv18FlbviRlUUFDjnoQCw" + }, + "cost": 131072, + "subtypes": [], + "fingerprintContents": "30228020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A", + "fulfillment": "A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140E5564300C360AC729086E2CC806E828A84877F1EB8E5D974D873E065224901555FB8821590A33BACC61E39701CF9B46BD25BF5F0595BBE24655141438E7A100B", + "conditionBinary": "A4278020799239ABA8FC4FF7EABFBC4C44E69E8BDFED993324E12ED64792ABE289CF1D5F8103020000", + "conditionUri": "ni:///sha-256;eZI5q6j8T_fqv7xMROaei9_tmTMk4S7WR5Kr4onPHV8?fpt=ed25519-sha-256&cost=131072", + "message": "" +} \ No newline at end of file diff --git a/src/cryptoconditions/tests/vectors/0005_test-basic-preimage.json b/src/cryptoconditions/tests/vectors/0005_test-basic-preimage.json new file mode 100644 index 000000000..585fe2e47 --- /dev/null +++ b/src/cryptoconditions/tests/vectors/0005_test-basic-preimage.json @@ -0,0 +1,13 @@ +{ + "json": { + "type": "preimage-sha-256", + "preimage": "YWFh" + }, + "cost": 3, + "subtypes": [], + "fingerprintContents": "616161", + "fulfillment": "A0058003616161", + "conditionBinary": "A02580209834876DCFB05CB167A5C24953EBA58C4AC89B1ADF57F28F2F9D09AF107EE8F0810103", + "conditionUri": "ni:///sha-256;mDSHbc-wXLFnpcJJU-uljErImxrfV_KPL50JrxB-6PA?fpt=preimage-sha-256&cost=3", + "message": "" +} \ No newline at end of file diff --git a/src/cryptoconditions/tests/vectors/0006_test-basic-prefix.json b/src/cryptoconditions/tests/vectors/0006_test-basic-prefix.json new file mode 100644 index 000000000..59cd7b361 --- /dev/null +++ b/src/cryptoconditions/tests/vectors/0006_test-basic-prefix.json @@ -0,0 +1,21 @@ +{ + "json": { + "type": "prefix-sha-256", + "maxMessageLength": 0, + "prefix": "YWFh", + "subfulfillment": { + "type": "ed25519-sha-256", + "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + "signature": "UGoepoMY5i1AY12tBD4Zh-vCbltcRAb3vfhacziPv-XCRaxJ9HcOvHh3CCcKpqh2n-_okw_Q6h7mSzFAfXaVCQ" + } + }, + "cost": 132099, + "subtypes": [ + "ed25519-sha-256" + ], + "fingerprintContents": "30338003616161810100A229A4278020799239ABA8FC4FF7EABFBC4C44E69E8BDFED993324E12ED64792ABE289CF1D5F8103020000", + "fulfillment": "A1708003616161810100A266A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140506A1EA68318E62D40635DAD043E1987EBC26E5B5C4406F7BDF85A73388FBFE5C245AC49F4770EBC787708270AA6A8769FEFE8930FD0EA1EE64B31407D769509", + "conditionBinary": "A12B8020451FE15F16299D495993FE692DB989E56A5230A90476F77392A3CD3213C0733F810302040382020308", + "conditionUri": "ni:///sha-256;RR_hXxYpnUlZk_5pLbmJ5WpSMKkEdvdzkqPNMhPAcz8?fpt=prefix-sha-256&cost=132099&subtypes=ed25519-sha-256", + "message": "" +} \ No newline at end of file diff --git a/src/cryptoconditions/tests/vectors/0007_test-basic-prefix-two-levels-deep.json b/src/cryptoconditions/tests/vectors/0007_test-basic-prefix-two-levels-deep.json new file mode 100644 index 000000000..93dfb249e --- /dev/null +++ b/src/cryptoconditions/tests/vectors/0007_test-basic-prefix-two-levels-deep.json @@ -0,0 +1,26 @@ +{ + "json": { + "type": "prefix-sha-256", + "maxMessageLength": 3, + "prefix": "YmJi", + "subfulfillment": { + "type": "prefix-sha-256", + "maxMessageLength": 6, + "prefix": "YWFh", + "subfulfillment": { + "type": "ed25519-sha-256", + "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + "signature": "pCNg9H99uG218DfIECQiNyB9et1uPlMX4hKyB-Jb7SrLSFrQvLtXdVcmDsu71ncY1sq630W61lXRuM6EYJ6XAQ" + } + } + }, + "cost": 133135, + "subtypes": [ + "ed25519-sha-256" + ], + "fingerprintContents": "30378003626262810103A22DA12B80207F19C9BB3BC767DE39657E11D16068F8CAB00E3E3C23916DF967B584A28B26DC810302040982020308", + "fulfillment": "A17C8003626262810103A272A1708003616161810106A266A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140A42360F47F7DB86DB5F037C810242237207D7ADD6E3E5317E212B207E25BED2ACB485AD0BCBB577557260ECBBBD67718D6CABADF45BAD655D1B8CE84609E9701", + "conditionBinary": "A12B8020177350AD8566C528B92D9B5382DF2C68D9BA9F9FA41D43DBDD8E40B118DD9641810302080F82020308", + "conditionUri": "ni:///sha-256;F3NQrYVmxSi5LZtTgt8saNm6n5-kHUPb3Y5AsRjdlkE?fpt=prefix-sha-256&cost=133135&subtypes=ed25519-sha-256", + "message": "7A7A7A" +} \ No newline at end of file diff --git a/src/cryptoconditions/tests/vectors/0008_test-basic-threshold.json b/src/cryptoconditions/tests/vectors/0008_test-basic-threshold.json new file mode 100644 index 000000000..56b4984d8 --- /dev/null +++ b/src/cryptoconditions/tests/vectors/0008_test-basic-threshold.json @@ -0,0 +1,39 @@ +{ + "json": { + "type": "threshold-sha-256", + "threshold": 2, + "subfulfillments": [ + { + "type": "rsa-sha-256", + "modulus": "u7ChoxT_ai_HmuBvYQ3I1EIckz7QxDVOaurZHvlH6k-PYysuL3i4oHp4wzJIlcfLCRbrM0wQCQ5e254Cle0v8d6v0fSfVEEuQ-3iywbP_DlTAzCaTOPqwjQdmvMYgzfuur09Skr49D4VfW8C-dHySkJ3cEnJMKojP-1cY7B_clzea7JEBPy_wLhyR-rLfbBEeFumbt6OeSESRwFQQB4KhHG9428tXsuWD1cboX2vOB2DeN7CHhAS5LN2yebEa7Z9aO7xK6mhWWf0hti8kbPisG-l_KabdSQmrwKcsUnqWG3thR66Fgh2rNhfBiJx-tc9FfXw8CLiYTCSJr7jVnrQUvl0bKi_rPDU9BcwCFwJegICgwHZKgxUXAOXRywu7lthIiAl1jRw7mgc4yUnR86coj8Z5m8vY4bmrRORHXrazTjOXKj_LC2Zq7D1w7qEdQlhPmYyoSzm73jaTIIOkINAUwA9EZf2pXsBAP7hUshMixszu5ZXGYh4D6-9aXz4NwqZ2o-q91aH2VHMZTPHi44c4uHVzrmhFikgGvQ3R1yUAn-lJhS0MAt33PGArEnKo0AWjzJi_R7osTgCzqNXVLQjuDP6FMXdDEdt3l1ee_c3TWHySMO6uRywVQvRy-9wUH7o2xzzmTB-Io1PRZKmbFhXPP7MY5ZoBvr4gQnMsJkj6ls", + "signature": "QSAOQpYe6kYMgnbyOatmt2-PKGnqzMX56ZRcCrxjsUMGVvPaLSF6QzJ-6M4RJ9FKhddK9BL-k3A0W6a0QAPXca7dK6D8JQYn_fYWMa_WTpvucUsbr7afkBvutvlZsWpUsyippnTIg8r8T8L3sEdtvKqNm_1H9zTjRza_ZulzsxXCq_RyewYso8QCk261CEnhgV3RYEpgTe-maM_oI7AIv4979XzrG0HlGP9TrpEe-YgFcbPRdCNB1g48sr65PrXxePjve6DG8ikLmVXYPWD5Q9ZEVx7I1fBk2V8doedZ1pINhKVMvEl4NkCnsM89Ao5ifsetJakV9tjCSy4JkVKKAsW-8BBsGLPznFmZVut6J7M3MT3Pk-dYZ2BqSsWhq2-btETcHa0fVG-u3mrpNIhASQF2kTcdUe4dZQ785YGzDGhDCH5wFBkPRGAmfYlNUlgU3aNDuaBIncCMajQzR0LOowtJJRLSlXRSF91l7P7LnKGNIS-EpQbXACbtMyiWvSSmMFoMve6d_Yq7hQf4QOuUz6jpKOaRDCdfe2jRH5ZGPBAqWWYTR4aVvHZx-ycwkTnGzS8Vyn4kBS5H9ONO-LhE7DZKhb3JvoQlz_cqd76Y2QFphqZnEJgl8e8Y4IFwqHtDaOfBQsGdrRBgXE80HVIJmHrzGrqOLklj8-B5TdEfC3I" + }, + { + "type": "prefix-sha-256", + "maxMessageLength": 0, + "prefix": "YWFh", + "subfulfillment": { + "type": "ed25519-sha-256", + "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + "signature": "NEydEqAqV7Nf2WYZPO6V1du7xndVP86_QUwY2nUAKd05xuU63Yks7-RCNYMf-OXzaGiO93zPb5l_1BGmp-v5Ag" + } + }, + { + "type": "ed25519-sha-256", + "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + "signature": "UGoepoMY5i1AY12tBD4Zh-vCbltcRAb3vfhacziPv-XCRaxJ9HcOvHh3CCcKpqh2n-_okw_Q6h7mSzFAfXaVCQ" + } + ] + }, + "cost": 397315, + "subtypes": [ + "ed25519-sha-256", + "prefix-sha-256", + "rsa-sha-256" + ], + "fingerprintContents": "308184800102A17FA12B8020451FE15F16299D495993FE692DB989E56A5230A90476F77392A3CD3213C0733F810302040382020308A32780204DD2EA7F85B3EACB8F19058E8360955C32E74C124392A1F44660739709C539C38103040000A4278020799239ABA8FC4FF7EABFBC4C44E69E8BDFED993324E12ED64792ABE289CF1D5F8103020000", + "fulfillment": "A2820106A081D8A1708003616161810100A266A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140344C9D12A02A57B35FD966193CEE95D5DBBBC677553FCEBF414C18DA750029DD39C6E53ADD892CEFE44235831FF8E5F368688EF77CCF6F997FD411A6A7EBF902A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140506A1EA68318E62D40635DAD043E1987EBC26E5B5C4406F7BDF85A73388FBFE5C245AC49F4770EBC787708270AA6A8769FEFE8930FD0EA1EE64B31407D769509A129A32780204DD2EA7F85B3EACB8F19058E8360955C32E74C124392A1F44660739709C539C38103040000", + "conditionBinary": "A22B8020B6ACF4083E438BE4356F25FF92C295E9C8E1BAB141B4607BA48511EBA35AEFCC810306100382020358", + "conditionUri": "ni:///sha-256;tqz0CD5Di-Q1byX_ksKV6cjhurFBtGB7pIUR66Na78w?fpt=threshold-sha-256&cost=397315&subtypes=prefix-sha-256,rsa-sha-256,ed25519-sha-256", + "message": "616161" +} diff --git a/src/cryptoconditions/tests/vectors/0009_test-basic-threshold-same-condition-twice.json b/src/cryptoconditions/tests/vectors/0009_test-basic-threshold-same-condition-twice.json new file mode 100644 index 000000000..a669b8d5f --- /dev/null +++ b/src/cryptoconditions/tests/vectors/0009_test-basic-threshold-same-condition-twice.json @@ -0,0 +1,54 @@ +{ + "json": { + "type": "threshold-sha-256", + "threshold": 1, + "subfulfillments": [ + { + "type": "prefix-sha-256", + "maxMessageLength": 0, + "prefix": "YWFh", + "subfulfillment": { + "type": "ed25519-sha-256", + "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + "signature": "UGoepoMY5i1AY12tBD4Zh-vCbltcRAb3vfhacziPv-XCRaxJ9HcOvHh3CCcKpqh2n-_okw_Q6h7mSzFAfXaVCQ" + } + }, + { + "type": "rsa-sha-256", + "modulus": "u7ChoxT_ai_HmuBvYQ3I1EIckz7QxDVOaurZHvlH6k-PYysuL3i4oHp4wzJIlcfLCRbrM0wQCQ5e254Cle0v8d6v0fSfVEEuQ-3iywbP_DlTAzCaTOPqwjQdmvMYgzfuur09Skr49D4VfW8C-dHySkJ3cEnJMKojP-1cY7B_clzea7JEBPy_wLhyR-rLfbBEeFumbt6OeSESRwFQQB4KhHG9428tXsuWD1cboX2vOB2DeN7CHhAS5LN2yebEa7Z9aO7xK6mhWWf0hti8kbPisG-l_KabdSQmrwKcsUnqWG3thR66Fgh2rNhfBiJx-tc9FfXw8CLiYTCSJr7jVnrQUvl0bKi_rPDU9BcwCFwJegICgwHZKgxUXAOXRywu7lthIiAl1jRw7mgc4yUnR86coj8Z5m8vY4bmrRORHXrazTjOXKj_LC2Zq7D1w7qEdQlhPmYyoSzm73jaTIIOkINAUwA9EZf2pXsBAP7hUshMixszu5ZXGYh4D6-9aXz4NwqZ2o-q91aH2VHMZTPHi44c4uHVzrmhFikgGvQ3R1yUAn-lJhS0MAt33PGArEnKo0AWjzJi_R7osTgCzqNXVLQjuDP6FMXdDEdt3l1ee_c3TWHySMO6uRywVQvRy-9wUH7o2xzzmTB-Io1PRZKmbFhXPP7MY5ZoBvr4gQnMsJkj6ls", + "signature": "fss4we3Ml_SM2IUwwkbFSRKGf1EbxN3QjFc8ogg6yf2qNFwXdo2t6YNCSBjbUqNy4oD7pWgvZSFalz-3KGdJO2T4-zQVM5felzo2xzxSuR4DQBtvpWP3d4T6r4ggQFvukx6jX3qNlCUUrlhsH8yo7e-biQTRuOEcnNjdu38VG3cX4STnLVejyjP0TkrXvDvYf9wge6rqrbax8XVPx48bQmbd84bl1STBo4cGUjqoIPEYsFN3Law-SoZ700WkvDWSCe9sHIPPVDKcJUjRrwUd_tr1PoBA9_OwYThrJ7UMj98w-k_X1fPfTbB0RMW4Z9V_P9L5r0AXRyHlYEJILwy4oEx4b74N47mHskntvTC32yuSic9E0OfLLmvewIpukErdNo5AwKf5LoSGWAaLDdbF7989xvK3YBB8AfWqr6jjf3ZmKIb8XS2yXduOP9T6ixFPj-pjn0VfAugCY_a85IO6LiChBeliMO84t5OO-qUKmdnj1qMd5Piy_H28FnXN_57X6tSUmOfo1oNTvMKBCWBg5azgw5dZNCYu18OxfmpxsXPgWF8Th7omsxSFqCCOOBoFPoFdVu3Lj_2pa9pLgJF5FzWZKKNEBnwBjPf6MD_PkybcjX9atigIbUvTgEbwKVNZrgENagDTgT984cfm7baOB4eYWTuaV6ycCgpMLSgk-gA" + }, + { + "type": "prefix-sha-256", + "maxMessageLength": 0, + "prefix": "YWFh", + "subfulfillment": { + "type": "ed25519-sha-256", + "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + "signature": "UGoepoMY5i1AY12tBD4Zh-vCbltcRAb3vfhacziPv-XCRaxJ9HcOvHh3CCcKpqh2n-_okw_Q6h7mSzFAfXaVCQ" + } + }, + { + "type": "rsa-sha-256", + "modulus": "u7ChoxT_ai_HmuBvYQ3I1EIckz7QxDVOaurZHvlH6k-PYysuL3i4oHp4wzJIlcfLCRbrM0wQCQ5e254Cle0v8d6v0fSfVEEuQ-3iywbP_DlTAzCaTOPqwjQdmvMYgzfuur09Skr49D4VfW8C-dHySkJ3cEnJMKojP-1cY7B_clzea7JEBPy_wLhyR-rLfbBEeFumbt6OeSESRwFQQB4KhHG9428tXsuWD1cboX2vOB2DeN7CHhAS5LN2yebEa7Z9aO7xK6mhWWf0hti8kbPisG-l_KabdSQmrwKcsUnqWG3thR66Fgh2rNhfBiJx-tc9FfXw8CLiYTCSJr7jVnrQUvl0bKi_rPDU9BcwCFwJegICgwHZKgxUXAOXRywu7lthIiAl1jRw7mgc4yUnR86coj8Z5m8vY4bmrRORHXrazTjOXKj_LC2Zq7D1w7qEdQlhPmYyoSzm73jaTIIOkINAUwA9EZf2pXsBAP7hUshMixszu5ZXGYh4D6-9aXz4NwqZ2o-q91aH2VHMZTPHi44c4uHVzrmhFikgGvQ3R1yUAn-lJhS0MAt33PGArEnKo0AWjzJi_R7osTgCzqNXVLQjuDP6FMXdDEdt3l1ee_c3TWHySMO6uRywVQvRy-9wUH7o2xzzmTB-Io1PRZKmbFhXPP7MY5ZoBvr4gQnMsJkj6ls", + "signature": "fss4we3Ml_SM2IUwwkbFSRKGf1EbxN3QjFc8ogg6yf2qNFwXdo2t6YNCSBjbUqNy4oD7pWgvZSFalz-3KGdJO2T4-zQVM5felzo2xzxSuR4DQBtvpWP3d4T6r4ggQFvukx6jX3qNlCUUrlhsH8yo7e-biQTRuOEcnNjdu38VG3cX4STnLVejyjP0TkrXvDvYf9wge6rqrbax8XVPx48bQmbd84bl1STBo4cGUjqoIPEYsFN3Law-SoZ700WkvDWSCe9sHIPPVDKcJUjRrwUd_tr1PoBA9_OwYThrJ7UMj98w-k_X1fPfTbB0RMW4Z9V_P9L5r0AXRyHlYEJILwy4oEx4b74N47mHskntvTC32yuSic9E0OfLLmvewIpukErdNo5AwKf5LoSGWAaLDdbF7989xvK3YBB8AfWqr6jjf3ZmKIb8XS2yXduOP9T6ixFPj-pjn0VfAugCY_a85IO6LiChBeliMO84t5OO-qUKmdnj1qMd5Piy_H28FnXN_57X6tSUmOfo1oNTvMKBCWBg5azgw5dZNCYu18OxfmpxsXPgWF8Th7omsxSFqCCOOBoFPoFdVu3Lj_2pa9pLgJF5FzWZKKNEBnwBjPf6MD_PkybcjX9atigIbUvTgEbwKVNZrgENagDTgT984cfm7baOB4eYWTuaV6ycCgpMLSgk-gA" + }, + { + "type": "preimage-sha-256", + "preimage": "YWFh" + } + ] + }, + "cost": 267264, + "subtypes": [ + "ed25519-sha-256", + "prefix-sha-256", + "preimage-sha-256", + "rsa-sha-256" + ], + "fingerprintContents": "3081D9800101A181D3A02580209834876DCFB05CB167A5C24953EBA58C4AC89B1ADF57F28F2F9D09AF107EE8F0810103A12B8020451FE15F16299D495993FE692DB989E56A5230A90476F77392A3CD3213C0733F810302040382020308A12B8020451FE15F16299D495993FE692DB989E56A5230A90476F77392A3CD3213C0733F810302040382020308A32780204DD2EA7F85B3EACB8F19058E8360955C32E74C124392A1F44660739709C539C38103040000A32780204DD2EA7F85B3EACB8F19058E8360955C32E74C124392A1F44660739709C539C38103040000", + "fulfillment": "A281B8A007A0058003616161A181ACA12B8020451FE15F16299D495993FE692DB989E56A5230A90476F77392A3CD3213C0733F810302040382020308A12B8020451FE15F16299D495993FE692DB989E56A5230A90476F77392A3CD3213C0733F810302040382020308A32780204DD2EA7F85B3EACB8F19058E8360955C32E74C124392A1F44660739709C539C38103040000A32780204DD2EA7F85B3EACB8F19058E8360955C32E74C124392A1F44660739709C539C38103040000", + "conditionBinary": "A22B80209A0B2C63DF80686E6020D0CA21CBFE668CCEC3D1AF82713FEAE9B8DD4A0F9BB78103041400820203D8", + "conditionUri": "ni:///sha-256;mgssY9-AaG5gINDKIcv-ZozOw9GvgnE_6um43UoPm7c?fpt=threshold-sha-256&cost=267264&subtypes=preimage-sha-256,prefix-sha-256,rsa-sha-256,ed25519-sha-256", + "message": "" +} diff --git a/src/cryptoconditions/tests/vectors/0010_test-basic-threshold-same-fulfillment-twice.json b/src/cryptoconditions/tests/vectors/0010_test-basic-threshold-same-fulfillment-twice.json new file mode 100644 index 000000000..9a1927d4b --- /dev/null +++ b/src/cryptoconditions/tests/vectors/0010_test-basic-threshold-same-fulfillment-twice.json @@ -0,0 +1,48 @@ +{ + "json": { + "type": "threshold-sha-256", + "threshold": 4, + "subfulfillments": [ + { + "type": "prefix-sha-256", + "maxMessageLength": 0, + "prefix": "YWFh", + "subfulfillment": { + "type": "ed25519-sha-256", + "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + "signature": "UGoepoMY5i1AY12tBD4Zh-vCbltcRAb3vfhacziPv-XCRaxJ9HcOvHh3CCcKpqh2n-_okw_Q6h7mSzFAfXaVCQ" + } + }, + { + "type": "ed25519-sha-256", + "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + "signature": "5VZDAMNgrHKQhuLMgG6CioSHfx645dl02HPgZSJJAVVfuIIVkKM7rMYeOXAc-bRr0lv18FlbviRlUUFDjnoQCw" + }, + { + "type": "prefix-sha-256", + "maxMessageLength": 0, + "prefix": "YWFh", + "subfulfillment": { + "type": "ed25519-sha-256", + "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + "signature": "UGoepoMY5i1AY12tBD4Zh-vCbltcRAb3vfhacziPv-XCRaxJ9HcOvHh3CCcKpqh2n-_okw_Q6h7mSzFAfXaVCQ" + } + }, + { + "type": "ed25519-sha-256", + "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + "signature": "5VZDAMNgrHKQhuLMgG6CioSHfx645dl02HPgZSJJAVVfuIIVkKM7rMYeOXAc-bRr0lv18FlbviRlUUFDjnoQCw" + } + ] + }, + "cost": 530438, + "subtypes": [ + "ed25519-sha-256", + "prefix-sha-256" + ], + "fingerprintContents": "3081B2800104A181ACA12B8020451FE15F16299D495993FE692DB989E56A5230A90476F77392A3CD3213C0733F810302040382020308A12B8020451FE15F16299D495993FE692DB989E56A5230A90476F77392A3CD3213C0733F810302040382020308A4278020799239ABA8FC4FF7EABFBC4C44E69E8BDFED993324E12ED64792ABE289CF1D5F8103020000A4278020799239ABA8FC4FF7EABFBC4C44E69E8BDFED993324E12ED64792ABE289CF1D5F8103020000", + "fulfillment": "A28201B6A08201B0A1708003616161810100A266A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140506A1EA68318E62D40635DAD043E1987EBC26E5B5C4406F7BDF85A73388FBFE5C245AC49F4770EBC787708270AA6A8769FEFE8930FD0EA1EE64B31407D769509A1708003616161810100A266A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140506A1EA68318E62D40635DAD043E1987EBC26E5B5C4406F7BDF85A73388FBFE5C245AC49F4770EBC787708270AA6A8769FEFE8930FD0EA1EE64B31407D769509A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140E5564300C360AC729086E2CC806E828A84877F1EB8E5D974D873E065224901555FB8821590A33BACC61E39701CF9B46BD25BF5F0595BBE24655141438E7A100BA4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140E5564300C360AC729086E2CC806E828A84877F1EB8E5D974D873E065224901555FB8821590A33BACC61E39701CF9B46BD25BF5F0595BBE24655141438E7A100BA100", + "conditionBinary": "A22B80208E433EF5D3EAA00A2B34A05CA7C22DD392973A19F1A243268CB53111BDF1C844810308180682020348", + "conditionUri": "ni:///sha-256;jkM-9dPqoAorNKBcp8It05KXOhnxokMmjLUxEb3xyEQ?fpt=threshold-sha-256&cost=530438&subtypes=prefix-sha-256,ed25519-sha-256", + "message": "" +} diff --git a/src/cryptoconditions/tests/vectors/0011_test-basic-threshold-two-levels-deep.json b/src/cryptoconditions/tests/vectors/0011_test-basic-threshold-two-levels-deep.json new file mode 100644 index 000000000..af153f473 --- /dev/null +++ b/src/cryptoconditions/tests/vectors/0011_test-basic-threshold-two-levels-deep.json @@ -0,0 +1,50 @@ +{ + "json": { + "type": "threshold-sha-256", + "threshold": 2, + "subfulfillments": [ + { + "type": "threshold-sha-256", + "threshold": 2, + "subfulfillments": [ + { + "type": "rsa-sha-256", + "modulus": "u7ChoxT_ai_HmuBvYQ3I1EIckz7QxDVOaurZHvlH6k-PYysuL3i4oHp4wzJIlcfLCRbrM0wQCQ5e254Cle0v8d6v0fSfVEEuQ-3iywbP_DlTAzCaTOPqwjQdmvMYgzfuur09Skr49D4VfW8C-dHySkJ3cEnJMKojP-1cY7B_clzea7JEBPy_wLhyR-rLfbBEeFumbt6OeSESRwFQQB4KhHG9428tXsuWD1cboX2vOB2DeN7CHhAS5LN2yebEa7Z9aO7xK6mhWWf0hti8kbPisG-l_KabdSQmrwKcsUnqWG3thR66Fgh2rNhfBiJx-tc9FfXw8CLiYTCSJr7jVnrQUvl0bKi_rPDU9BcwCFwJegICgwHZKgxUXAOXRywu7lthIiAl1jRw7mgc4yUnR86coj8Z5m8vY4bmrRORHXrazTjOXKj_LC2Zq7D1w7qEdQlhPmYyoSzm73jaTIIOkINAUwA9EZf2pXsBAP7hUshMixszu5ZXGYh4D6-9aXz4NwqZ2o-q91aH2VHMZTPHi44c4uHVzrmhFikgGvQ3R1yUAn-lJhS0MAt33PGArEnKo0AWjzJi_R7osTgCzqNXVLQjuDP6FMXdDEdt3l1ee_c3TWHySMO6uRywVQvRy-9wUH7o2xzzmTB-Io1PRZKmbFhXPP7MY5ZoBvr4gQnMsJkj6ls", + "signature": "fss4we3Ml_SM2IUwwkbFSRKGf1EbxN3QjFc8ogg6yf2qNFwXdo2t6YNCSBjbUqNy4oD7pWgvZSFalz-3KGdJO2T4-zQVM5felzo2xzxSuR4DQBtvpWP3d4T6r4ggQFvukx6jX3qNlCUUrlhsH8yo7e-biQTRuOEcnNjdu38VG3cX4STnLVejyjP0TkrXvDvYf9wge6rqrbax8XVPx48bQmbd84bl1STBo4cGUjqoIPEYsFN3Law-SoZ700WkvDWSCe9sHIPPVDKcJUjRrwUd_tr1PoBA9_OwYThrJ7UMj98w-k_X1fPfTbB0RMW4Z9V_P9L5r0AXRyHlYEJILwy4oEx4b74N47mHskntvTC32yuSic9E0OfLLmvewIpukErdNo5AwKf5LoSGWAaLDdbF7989xvK3YBB8AfWqr6jjf3ZmKIb8XS2yXduOP9T6ixFPj-pjn0VfAugCY_a85IO6LiChBeliMO84t5OO-qUKmdnj1qMd5Piy_H28FnXN_57X6tSUmOfo1oNTvMKBCWBg5azgw5dZNCYu18OxfmpxsXPgWF8Th7omsxSFqCCOOBoFPoFdVu3Lj_2pa9pLgJF5FzWZKKNEBnwBjPf6MD_PkybcjX9atigIbUvTgEbwKVNZrgENagDTgT984cfm7baOB4eYWTuaV6ycCgpMLSgk-gA" + }, + { + "type": "prefix-sha-256", + "maxMessageLength": 0, + "prefix": "YWFh", + "subfulfillment": { + "type": "ed25519-sha-256", + "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + "signature": "UGoepoMY5i1AY12tBD4Zh-vCbltcRAb3vfhacziPv-XCRaxJ9HcOvHh3CCcKpqh2n-_okw_Q6h7mSzFAfXaVCQ" + } + }, + { + "type": "ed25519-sha-256", + "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + "signature": "5VZDAMNgrHKQhuLMgG6CioSHfx645dl02HPgZSJJAVVfuIIVkKM7rMYeOXAc-bRr0lv18FlbviRlUUFDjnoQCw" + } + ] + }, + { + "type": "preimage-sha-256", + "preimage": "YWFh" + } + ] + }, + "cost": 399366, + "subtypes": [ + "ed25519-sha-256", + "prefix-sha-256", + "preimage-sha-256", + "rsa-sha-256" + ], + "fingerprintContents": "3059800102A154A02580209834876DCFB05CB167A5C24953EBA58C4AC89B1ADF57F28F2F9D09AF107EE8F0810103A22B8020B6ACF4083E438BE4356F25FF92C295E9C8E1BAB141B4607BA48511EBA35AEFCC810306100382020358", + "fulfillment": "A2820117A0820111A0058003616161A2820106A081D8A1708003616161810100A266A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140506A1EA68318E62D40635DAD043E1987EBC26E5B5C4406F7BDF85A73388FBFE5C245AC49F4770EBC787708270AA6A8769FEFE8930FD0EA1EE64B31407D769509A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140E5564300C360AC729086E2CC806E828A84877F1EB8E5D974D873E065224901555FB8821590A33BACC61E39701CF9B46BD25BF5F0595BBE24655141438E7A100BA129A32780204DD2EA7F85B3EACB8F19058E8360955C32E74C124392A1F44660739709C539C38103040000A100", + "conditionBinary": "A22B80200C99630A201A99B0748D2BADB205E5CA939692C687D1C4A697E39BA8BA1EBE718103061806820203D8", + "conditionUri": "ni:///sha-256;DJljCiAambB0jSutsgXlypOWksaH0cSml-ObqLoevnE?fpt=threshold-sha-256&cost=399366&subtypes=preimage-sha-256,prefix-sha-256,rsa-sha-256,ed25519-sha-256", + "message": "" +} diff --git a/src/cryptoconditions/tests/vectors/0012_test-basic-threshold-schroedinger.json b/src/cryptoconditions/tests/vectors/0012_test-basic-threshold-schroedinger.json new file mode 100644 index 000000000..4285ac44d --- /dev/null +++ b/src/cryptoconditions/tests/vectors/0012_test-basic-threshold-schroedinger.json @@ -0,0 +1,25 @@ +{ + "json": { + "type": "threshold-sha-256", + "threshold": 1, + "subfulfillments": [ + { + "type": "preimage-sha-256", + "preimage": "YWFh" + }, + { + "type": "preimage-sha-256", + "preimage": "YWFh" + } + ] + }, + "cost": 2051, + "subtypes": [ + "preimage-sha-256" + ], + "fingerprintContents": "3053800101A14EA02580209834876DCFB05CB167A5C24953EBA58C4AC89B1ADF57F28F2F9D09AF107EE8F0810103A02580209834876DCFB05CB167A5C24953EBA58C4AC89B1ADF57F28F2F9D09AF107EE8F0810103", + "fulfillment": "A232A007A0058003616161A127A02580209834876DCFB05CB167A5C24953EBA58C4AC89B1ADF57F28F2F9D09AF107EE8F0810103", + "conditionBinary": "A22A8020E4FDB4652C6F17A38B2ABE9AA00640B1E184FE7A8D0C971B5D24F7EDA6FC68BF8102080382020780", + "conditionUri": "ni:///sha-256;5P20ZSxvF6OLKr6aoAZAseGE_nqNDJcbXST37ab8aL8?fpt=threshold-sha-256&cost=2051&subtypes=preimage-sha-256", + "message": "" +} \ No newline at end of file diff --git a/src/cryptoconditions/tests/vectors/0013_test-basic-rsa.json b/src/cryptoconditions/tests/vectors/0013_test-basic-rsa.json new file mode 100644 index 000000000..5681fc5c1 --- /dev/null +++ b/src/cryptoconditions/tests/vectors/0013_test-basic-rsa.json @@ -0,0 +1,14 @@ +{ + "json": { + "type": "rsa-sha-256", + "modulus": "4e-LJNb3awnIHtd1KqJi8ETwSodNQ4CdMc6mEvmbDJeotDdBU-Pu89ZmFoQ-DkHCkyZLcbYXPbHPDWzVWMWGV3Bvzwl_cExIPlnL_f1bPue8gNdAxeDwR_PoX8DXWBV3am8_I8XcXnlxOaaILjgzakpfs2E3Yg_zZj264yhHKAGGL3Ly-HsgK5yJrdfNWwoHb3xT41A59n7RfsgV5bQwXMYxlwaNXm5Xm6beX04-V99eTgcv8s5MZutFIzlzh1J1ljnwJXv1fb1cRD-1FYzOCj02rce6AfM6C7bbsr-YnWBxEvI0TZk-d-VjwdNh3t9X2pbvLPxoXwArY4JGpbMJuQ", + "signature": "SOiUXv4AdVbVv01fJJ5ICPcwfilRHTJi2u9h2ICY-apKi8BiOoyXVzj2XWv0WdVD8onXPLx69Oo6M_vz7ERARHkR1yKUCR5WGDNijkmncu1gjebERZWpHj4X1s9ew7JSjWPSrdZGOYmxLuxXffZHCWDfaDKp2Ew2DRwhetZMhiW9tZT7CtoIbN7LveWA1CS_l0bS8MMSgm27sArWi1LEy31HFWujXjqYHJc4Y3ksyA0EoYAhClJBWGW2Szphd0sdOXXXipiwgh7lXKD4YwXUJSnhDrAVzv1AL7WbKruN7uUqbyRH0ihGA9IZzU6M-c_91UmIicN4C1ndalfvfXMmIA" + }, + "cost": 65536, + "subtypes": [], + "fingerprintContents": "3082010480820100E1EF8B24D6F76B09C81ED7752AA262F044F04A874D43809D31CEA612F99B0C97A8B4374153E3EEF3D66616843E0E41C293264B71B6173DB1CF0D6CD558C58657706FCF097F704C483E59CBFDFD5B3EE7BC80D740C5E0F047F3E85FC0D75815776A6F3F23C5DC5E797139A6882E38336A4A5FB36137620FF3663DBAE328472801862F72F2F87B202B9C89ADD7CD5B0A076F7C53E35039F67ED17EC815E5B4305CC63197068D5E6E579BA6DE5F4E3E57DF5E4E072FF2CE4C66EB452339738752759639F0257BF57DBD5C443FB5158CCE0A3D36ADC7BA01F33A0BB6DBB2BF989D607112F2344D993E77E563C1D361DEDF57DA96EF2CFC685F002B638246A5B309B9", + "fulfillment": "A382020880820100E1EF8B24D6F76B09C81ED7752AA262F044F04A874D43809D31CEA612F99B0C97A8B4374153E3EEF3D66616843E0E41C293264B71B6173DB1CF0D6CD558C58657706FCF097F704C483E59CBFDFD5B3EE7BC80D740C5E0F047F3E85FC0D75815776A6F3F23C5DC5E797139A6882E38336A4A5FB36137620FF3663DBAE328472801862F72F2F87B202B9C89ADD7CD5B0A076F7C53E35039F67ED17EC815E5B4305CC63197068D5E6E579BA6DE5F4E3E57DF5E4E072FF2CE4C66EB452339738752759639F0257BF57DBD5C443FB5158CCE0A3D36ADC7BA01F33A0BB6DBB2BF989D607112F2344D993E77E563C1D361DEDF57DA96EF2CFC685F002B638246A5B309B98182010048E8945EFE007556D5BF4D5F249E4808F7307E29511D3262DAEF61D88098F9AA4A8BC0623A8C975738F65D6BF459D543F289D73CBC7AF4EA3A33FBF3EC4440447911D72294091E561833628E49A772ED608DE6C44595A91E3E17D6CF5EC3B2528D63D2ADD6463989B12EEC577DF6470960DF6832A9D84C360D1C217AD64C8625BDB594FB0ADA086CDECBBDE580D424BF9746D2F0C312826DBBB00AD68B52C4CB7D47156BA35E3A981C973863792CC80D04A180210A52415865B64B3A61774B1D3975D78A98B0821EE55CA0F86305D42529E10EB015CEFD402FB59B2ABB8DEEE52A6F2447D2284603D219CD4E8CF9CFFDD5498889C3780B59DD6A57EF7D732620", + "conditionBinary": "A3278020B31FA8206E4EA7E515337B3B33082B877651801085ED84FB4DAEB247BF698D7F8103010000", + "conditionUri": "ni:///sha-256;sx-oIG5Op-UVM3s7Mwgrh3ZRgBCF7YT7Ta6yR79pjX8?fpt=rsa-sha-256&cost=65536", + "message": "616161" +} \ No newline at end of file diff --git a/src/cryptoconditions/tests/vectors/0014_test-basic-rsa4096.json b/src/cryptoconditions/tests/vectors/0014_test-basic-rsa4096.json new file mode 100644 index 000000000..ab17137b2 --- /dev/null +++ b/src/cryptoconditions/tests/vectors/0014_test-basic-rsa4096.json @@ -0,0 +1,14 @@ +{ + "json": { + "type": "rsa-sha-256", + "modulus": "u7ChoxT_ai_HmuBvYQ3I1EIckz7QxDVOaurZHvlH6k-PYysuL3i4oHp4wzJIlcfLCRbrM0wQCQ5e254Cle0v8d6v0fSfVEEuQ-3iywbP_DlTAzCaTOPqwjQdmvMYgzfuur09Skr49D4VfW8C-dHySkJ3cEnJMKojP-1cY7B_clzea7JEBPy_wLhyR-rLfbBEeFumbt6OeSESRwFQQB4KhHG9428tXsuWD1cboX2vOB2DeN7CHhAS5LN2yebEa7Z9aO7xK6mhWWf0hti8kbPisG-l_KabdSQmrwKcsUnqWG3thR66Fgh2rNhfBiJx-tc9FfXw8CLiYTCSJr7jVnrQUvl0bKi_rPDU9BcwCFwJegICgwHZKgxUXAOXRywu7lthIiAl1jRw7mgc4yUnR86coj8Z5m8vY4bmrRORHXrazTjOXKj_LC2Zq7D1w7qEdQlhPmYyoSzm73jaTIIOkINAUwA9EZf2pXsBAP7hUshMixszu5ZXGYh4D6-9aXz4NwqZ2o-q91aH2VHMZTPHi44c4uHVzrmhFikgGvQ3R1yUAn-lJhS0MAt33PGArEnKo0AWjzJi_R7osTgCzqNXVLQjuDP6FMXdDEdt3l1ee_c3TWHySMO6uRywVQvRy-9wUH7o2xzzmTB-Io1PRZKmbFhXPP7MY5ZoBvr4gQnMsJkj6ls", + "signature": "QSAOQpYe6kYMgnbyOatmt2-PKGnqzMX56ZRcCrxjsUMGVvPaLSF6QzJ-6M4RJ9FKhddK9BL-k3A0W6a0QAPXca7dK6D8JQYn_fYWMa_WTpvucUsbr7afkBvutvlZsWpUsyippnTIg8r8T8L3sEdtvKqNm_1H9zTjRza_ZulzsxXCq_RyewYso8QCk261CEnhgV3RYEpgTe-maM_oI7AIv4979XzrG0HlGP9TrpEe-YgFcbPRdCNB1g48sr65PrXxePjve6DG8ikLmVXYPWD5Q9ZEVx7I1fBk2V8doedZ1pINhKVMvEl4NkCnsM89Ao5ifsetJakV9tjCSy4JkVKKAsW-8BBsGLPznFmZVut6J7M3MT3Pk-dYZ2BqSsWhq2-btETcHa0fVG-u3mrpNIhASQF2kTcdUe4dZQ785YGzDGhDCH5wFBkPRGAmfYlNUlgU3aNDuaBIncCMajQzR0LOowtJJRLSlXRSF91l7P7LnKGNIS-EpQbXACbtMyiWvSSmMFoMve6d_Yq7hQf4QOuUz6jpKOaRDCdfe2jRH5ZGPBAqWWYTR4aVvHZx-ycwkTnGzS8Vyn4kBS5H9ONO-LhE7DZKhb3JvoQlz_cqd76Y2QFphqZnEJgl8e8Y4IFwqHtDaOfBQsGdrRBgXE80HVIJmHrzGrqOLklj8-B5TdEfC3I" + }, + "cost": 262144, + "subtypes": [], + "fingerprintContents": "3082020480820200BBB0A1A314FF6A2FC79AE06F610DC8D4421C933ED0C4354E6AEAD91EF947EA4F8F632B2E2F78B8A07A78C3324895C7CB0916EB334C10090E5EDB9E0295ED2FF1DEAFD1F49F54412E43EDE2CB06CFFC395303309A4CE3EAC2341D9AF3188337EEBABD3D4A4AF8F43E157D6F02F9D1F24A42777049C930AA233FED5C63B07F725CDE6BB24404FCBFC0B87247EACB7DB044785BA66EDE8E792112470150401E0A8471BDE36F2D5ECB960F571BA17DAF381D8378DEC21E1012E4B376C9E6C46BB67D68EEF12BA9A15967F486D8BC91B3E2B06FA5FCA69B752426AF029CB149EA586DED851EBA160876ACD85F062271FAD73D15F5F0F022E261309226BEE3567AD052F9746CA8BFACF0D4F41730085C097A02028301D92A0C545C0397472C2EEE5B61222025D63470EE681CE3252747CE9CA23F19E66F2F6386E6AD13911D7ADACD38CE5CA8FF2C2D99ABB0F5C3BA847509613E6632A12CE6EF78DA4C820E90834053003D1197F6A57B0100FEE152C84C8B1B33BB96571988780FAFBD697CF8370A99DA8FAAF75687D951CC6533C78B8E1CE2E1D5CEB9A11629201AF437475C94027FA52614B4300B77DCF180AC49CAA340168F3262FD1EE8B13802CEA35754B423B833FA14C5DD0C476DDE5D5E7BF7374D61F248C3BAB91CB0550BD1CBEF70507EE8DB1CF399307E228D4F4592A66C58573CFECC63966806FAF88109CCB09923EA5B", + "fulfillment": "A382040880820200BBB0A1A314FF6A2FC79AE06F610DC8D4421C933ED0C4354E6AEAD91EF947EA4F8F632B2E2F78B8A07A78C3324895C7CB0916EB334C10090E5EDB9E0295ED2FF1DEAFD1F49F54412E43EDE2CB06CFFC395303309A4CE3EAC2341D9AF3188337EEBABD3D4A4AF8F43E157D6F02F9D1F24A42777049C930AA233FED5C63B07F725CDE6BB24404FCBFC0B87247EACB7DB044785BA66EDE8E792112470150401E0A8471BDE36F2D5ECB960F571BA17DAF381D8378DEC21E1012E4B376C9E6C46BB67D68EEF12BA9A15967F486D8BC91B3E2B06FA5FCA69B752426AF029CB149EA586DED851EBA160876ACD85F062271FAD73D15F5F0F022E261309226BEE3567AD052F9746CA8BFACF0D4F41730085C097A02028301D92A0C545C0397472C2EEE5B61222025D63470EE681CE3252747CE9CA23F19E66F2F6386E6AD13911D7ADACD38CE5CA8FF2C2D99ABB0F5C3BA847509613E6632A12CE6EF78DA4C820E90834053003D1197F6A57B0100FEE152C84C8B1B33BB96571988780FAFBD697CF8370A99DA8FAAF75687D951CC6533C78B8E1CE2E1D5CEB9A11629201AF437475C94027FA52614B4300B77DCF180AC49CAA340168F3262FD1EE8B13802CEA35754B423B833FA14C5DD0C476DDE5D5E7BF7374D61F248C3BAB91CB0550BD1CBEF70507EE8DB1CF399307E228D4F4592A66C58573CFECC63966806FAF88109CCB09923EA5B8182020041200E42961EEA460C8276F239AB66B76F8F2869EACCC5F9E9945C0ABC63B1430656F3DA2D217A43327EE8CE1127D14A85D74AF412FE9370345BA6B44003D771AEDD2BA0FC250627FDF61631AFD64E9BEE714B1BAFB69F901BEEB6F959B16A54B328A9A674C883CAFC4FC2F7B0476DBCAA8D9BFD47F734E34736BF66E973B315C2ABF4727B062CA3C402936EB50849E1815DD1604A604DEFA668CFE823B008BF8F7BF57CEB1B41E518FF53AE911EF9880571B3D1742341D60E3CB2BEB93EB5F178F8EF7BA0C6F2290B9955D83D60F943D644571EC8D5F064D95F1DA1E759D6920D84A54CBC49783640A7B0CF3D028E627EC7AD25A915F6D8C24B2E0991528A02C5BEF0106C18B3F39C599956EB7A27B337313DCF93E75867606A4AC5A1AB6F9BB444DC1DAD1F546FAEDE6AE934884049017691371D51EE1D650EFCE581B30C6843087E7014190F4460267D894D525814DDA343B9A0489DC08C6A34334742CEA30B492512D295745217DD65ECFECB9CA18D212F84A506D70026ED332896BD24A6305A0CBDEE9DFD8ABB8507F840EB94CFA8E928E6910C275F7B68D11F96463C102A596613478695BC7671FB27309139C6CD2F15CA7E24052E47F4E34EF8B844EC364A85BDC9BE8425CFF72A77BE98D9016986A667109825F1EF18E08170A87B4368E7C142C19DAD10605C4F341D5209987AF31ABA8E2E4963F3E0794DD11F0B72", + "conditionBinary": "A32780204DD2EA7F85B3EACB8F19058E8360955C32E74C124392A1F44660739709C539C38103040000", + "conditionUri": "ni:///sha-256;TdLqf4Wz6suPGQWOg2CVXDLnTBJDkqH0RmBzlwnFOcM?fpt=rsa-sha-256&cost=262144", + "message": "616161" +} \ No newline at end of file diff --git a/src/cryptoconditions/tests/vectors/0015_test-basic-ed25519.json b/src/cryptoconditions/tests/vectors/0015_test-basic-ed25519.json new file mode 100644 index 000000000..89247eb1b --- /dev/null +++ b/src/cryptoconditions/tests/vectors/0015_test-basic-ed25519.json @@ -0,0 +1,14 @@ +{ + "json": { + "type": "ed25519-sha-256", + "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + "signature": "UGoepoMY5i1AY12tBD4Zh-vCbltcRAb3vfhacziPv-XCRaxJ9HcOvHh3CCcKpqh2n-_okw_Q6h7mSzFAfXaVCQ" + }, + "cost": 131072, + "subtypes": [], + "fingerprintContents": "30228020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A", + "fulfillment": "A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140506A1EA68318E62D40635DAD043E1987EBC26E5B5C4406F7BDF85A73388FBFE5C245AC49F4770EBC787708270AA6A8769FEFE8930FD0EA1EE64B31407D769509", + "conditionBinary": "A4278020799239ABA8FC4FF7EABFBC4C44E69E8BDFED993324E12ED64792ABE289CF1D5F8103020000", + "conditionUri": "ni:///sha-256;eZI5q6j8T_fqv7xMROaei9_tmTMk4S7WR5Kr4onPHV8?fpt=ed25519-sha-256&cost=131072", + "message": "616161" +} \ No newline at end of file diff --git a/src/cryptoconditions/tests/vectors/0016_test-advanced-notarized-receipt.json b/src/cryptoconditions/tests/vectors/0016_test-advanced-notarized-receipt.json new file mode 100644 index 000000000..f1dc75bf8 --- /dev/null +++ b/src/cryptoconditions/tests/vectors/0016_test-advanced-notarized-receipt.json @@ -0,0 +1,33 @@ +{ + "json": { + "type": "threshold-sha-256", + "threshold": 2, + "subfulfillments": [ + { + "type": "prefix-sha-256", + "maxMessageLength": 0, + "prefix": "aHR0cHM6Ly9ub3RhcnkuZXhhbXBsZS9jYXNlcy82NTdjMTJkYS04ZGNhLTQzYjAtOTdjYS04ZWU4YzM4YWI5Zjcvc3RhdGUvZXhlY3V0ZWQ", + "subfulfillment": { + "type": "ed25519-sha-256", + "publicKey": "LlMeiL_oxBn5Ya2ckB3ivdjnoOcUhFUFnonreZhrJSQ", + "signature": "5f3bvsLo21m8tqaA5KcFb91GUAtOmaNxnyVzUDahSSg-KHzt7c2eWlDNl2q0EfhP9Wmqveb24M5qC90MSoJGCA" + } + }, + { + "type": "preimage-sha-256", + "preimage": "aHR0cHM6Ly9ub3RhcnkuZXhhbXBsZS9jYXNlcy82NTdjMTJkYS04ZGNhLTQzYjAtOTdjYS04ZWU4YzM4YWI5Zjcvc3RhdGUvZXhlY3V0ZWQ" + } + ] + }, + "cost": 134304, + "subtypes": [ + "ed25519-sha-256", + "prefix-sha-256", + "preimage-sha-256" + ], + "fingerprintContents": "3059800102A154A02580200B4AC3A1E0932CB71B74309FAD7D15DF51BD4D1359ED59FF7C917B35DF24464A810150A12B80203F94525555CF4C5234BF77CB108501D97B9D8A28D1E7A3A7FE8D3D7F031FDEBD810302045082020308", + "fulfillment": "A282011AA0820114A052805068747470733A2F2F6E6F746172792E6578616D706C652F63617365732F36353763313264612D386463612D343362302D393763612D3865653863333861623966372F73746174652F6578656375746564A181BD805068747470733A2F2F6E6F746172792E6578616D706C652F63617365732F36353763313264612D386463612D343362302D393763612D3865653863333861623966372F73746174652F6578656375746564810100A266A46480202E531E88BFE8C419F961AD9C901DE2BDD8E7A0E7148455059E89EB79986B25248140E5FDDBBEC2E8DB59BCB6A680E4A7056FDD46500B4E99A3719F25735036A149283E287CEDEDCD9E5A50CD976AB411F84FF569AABDE6F6E0CE6A0BDD0C4A824608A100", + "conditionBinary": "A22B802009E391004628725E88F8557E954FB2A0EAE2B7C151C47DF3C4AF22F8C16988F98103020CA0820203C8", + "conditionUri": "ni:///sha-256;CeORAEYocl6I-FV-lU-yoOrit8FRxH3zxK8i-MFpiPk?fpt=threshold-sha-256&cost=134304&subtypes=preimage-sha-256,prefix-sha-256,ed25519-sha-256", + "message": "" +} diff --git a/src/cryptoconditions/tests/vectors/0017_test-advanced-notarized-receipt-multiple-notaries.json b/src/cryptoconditions/tests/vectors/0017_test-advanced-notarized-receipt-multiple-notaries.json new file mode 100644 index 000000000..a8e98bc20 --- /dev/null +++ b/src/cryptoconditions/tests/vectors/0017_test-advanced-notarized-receipt-multiple-notaries.json @@ -0,0 +1,74 @@ +{ + "json": { + "type": "threshold-sha-256", + "threshold": 2, + "subfulfillments": [ + { + "type": "prefix-sha-256", + "maxMessageLength": 0, + "prefix": "Y2FzZXMvNjU3YzEyZGEtOGRjYS00M2IwLTk3Y2EtOGVlOGMzOGFiOWY3L3N0YXRlL2V4ZWN1dGVk", + "subfulfillment": { + "type": "threshold-sha-256", + "threshold": 3, + "subfulfillments": [ + { + "type": "prefix-sha-256", + "maxMessageLength": 1025, + "prefix": "aHR0cHM6Ly9ub3Rhcnk0LmV4YW1wbGUv", + "subfulfillment": { + "type": "ed25519-sha-256", + "publicKey": "Rkvo5cq-MB4FMv5iIUjPy8zxG_MXop9cyHg5xpUf6pg", + "signature": "rlayC5_mc7O1szd9hCV8kIVronrH_IYj89BwVubxNmV2CacPp9ASs8UqqLrQPtxIKp7kA-aA5IPjr1mlxK05BA" + } + }, + { + "type": "prefix-sha-256", + "maxMessageLength": 1024, + "prefix": "aHR0cHM6Ly9ub3RhcnkxLmV4YW1wbGUv", + "subfulfillment": { + "type": "ed25519-sha-256", + "publicKey": "LlMeiL_oxBn5Ya2ckB3ivdjnoOcUhFUFnonreZhrJSQ", + "signature": "hzAaGAj3PCA_DpyBBvEwcQiB2s2sgHwQ00m3mCDcs0B8d7nSPbQoJ2QL3EE4P9xOynYZwXA36HA3pcfPM4F6Dg" + } + }, + { + "type": "prefix-sha-256", + "maxMessageLength": 1024, + "prefix": "aHR0cHM6Ly9ub3RhcnkyLmV4YW1wbGUv", + "subfulfillment": { + "type": "ed25519-sha-256", + "publicKey": "WQI-doqchYdsYeuqo07BjmSFf6dmksVamWNfm4jlr5A", + "signature": "rPnug4hbpY9ixCtImejOqRWpGS90iMFZLOlZVgtS-Ho3kOA208aVS4dVQUjRMcy682nGimajE3_o-kNooWWgCg" + } + }, + { + "type": "prefix-sha-256", + "maxMessageLength": 1024, + "prefix": "aHR0cHM6Ly9ub3RhcnkzLmV4YW1wbGUv", + "subfulfillment": { + "type": "ed25519-sha-256", + "publicKey": "mpisbb_wkOluONgfBUd9-Gs7uw7_wxG8e0LNrJnWvdk", + "signature": "l6MrDGHOFRA2ytNZacn5XrVEZepdYpupZav4pqkX8Q3RSr5V0zBUQ45oyRWmtnwd34oMFtLYAfjQuoXv7pu_Dw" + } + } + ] + } + }, + { + "type": "preimage-sha-256", + "preimage": "aHR0cHM6Ly9ub3RhcnkuZXhhbXBsZS9jYXNlcy82NTdjMTJkYS04ZGNhLTQzYjAtOTdjYS04ZWU4YzM4YWI5Zjcvc3RhdGUvZXhlY3V0ZWQ" + } + ] + }, + "cost": 406738, + "subtypes": [ + "ed25519-sha-256", + "prefix-sha-256", + "preimage-sha-256" + ], + "fingerprintContents": "3059800102A154A02580200B4AC3A1E0932CB71B74309FAD7D15DF51BD4D1359ED59FF7C917B35DF24464A810150A12B8020062F2C1BDD08661FE7FEFAC20E02DA8B0184FCD36F6C6C54C53CC28D2E54DD118103062C8282020328", + "fulfillment": "A2820272A082026CA052805068747470733A2F2F6E6F746172792E6578616D706C652F63617365732F36353763313264612D386463612D343362302D393763612D3865653863333861623966372F73746174652F6578656375746564A1820214803963617365732F36353763313264612D386463612D343362302D393763612D3865653863333861623966372F73746174652F6578656375746564810100A28201D2A28201CEA082019BA18186801868747470733A2F2F6E6F74617279312E6578616D706C652F81020400A266A46480202E531E88BFE8C419F961AD9C901DE2BDD8E7A0E7148455059E89EB79986B2524814087301A1808F73C203F0E9C8106F130710881DACDAC807C10D349B79820DCB3407C77B9D23DB42827640BDC41383FDC4ECA7619C17037E87037A5C7CF33817A0EA18186801868747470733A2F2F6E6F74617279322E6578616D706C652F81020400A266A464802059023E768A9C85876C61EBAAA34EC18E64857FA76692C55A99635F9B88E5AF908140ACF9EE83885BA58F62C42B4899E8CEA915A9192F7488C1592CE959560B52F87A3790E036D3C6954B87554148D131CCBAF369C68A66A3137FE8FA4368A165A00AA18186801868747470733A2F2F6E6F74617279332E6578616D706C652F81020400A266A46480209A98AC6DBFF090E96E38D81F05477DF86B3BBB0EFFC311BC7B42CDAC99D6BDD9814097A32B0C61CE151036CAD35969C9F95EB54465EA5D629BA965ABF8A6A917F10DD14ABE55D33054438E68C915A6B67C1DDF8A0C16D2D801F8D0BA85EFEE9BBF0FA12DA12B8020EE0BC02F977C264B6C306ED1B168FEB4FD600950AD21750CE8A86ECBD4603538810302081982020308A100", + "conditionBinary": "A22B8020424A704949529267B621B3D79119D729B2382CED8B296C3C028FA97D350F6D0781030634D2820203C8", + "conditionUri": "ni:///sha-256;QkpwSUlSkme2IbPXkRnXKbI4LO2LKWw8Ao-pfTUPbQc?fpt=threshold-sha-256&cost=406738&subtypes=preimage-sha-256,prefix-sha-256,ed25519-sha-256", + "message": "" +} diff --git a/src/cryptoconditions/tests/vectors/1000_test-minimal-eval.json b/src/cryptoconditions/tests/vectors/1000_test-minimal-eval.json new file mode 100644 index 000000000..74e52beee --- /dev/null +++ b/src/cryptoconditions/tests/vectors/1000_test-minimal-eval.json @@ -0,0 +1,13 @@ +{ + "json": { + "type": "eval-sha-256", + "code": "VEVTVAE" + }, + "cost": 131072, + "subtypes": [], + "fingerprintContents": "", + "fulfillment": "AF0780055445535401", + "conditionBinary": "AF278020FD9DA5ADD8CF3164C4F46EF3B8B4925001F414718A13CEDEDD27A27CA93D5A238103100000", + "conditionUri": "ni:///sha-256;_Z2lrdjPMWTE9G7zuLSSUAH0FHGKE87e3SeifKk9WiM?fpt=eval-sha-256&cost=1048576", + "message": "" +} diff --git a/src/cryptoconditions/tests/vectors/1001_test-minimal-secp256k1.json b/src/cryptoconditions/tests/vectors/1001_test-minimal-secp256k1.json new file mode 100644 index 000000000..a85b29d91 --- /dev/null +++ b/src/cryptoconditions/tests/vectors/1001_test-minimal-secp256k1.json @@ -0,0 +1,14 @@ +{ + "json": { + "type": "secp256k1-sha-256", + "publicKey": "02D5D969305535AC29A77079C11D4F0DD40661CF96E04E974A5E8D7E374EE225AA", + "signature": "9C2D6FF39F340BBAF65E884BDFFAE7436A7B7568839C5BA117FA6818245FB9DA3CDEC7CFD4D3DF7191BDB22638FD86F3D9A3B8CB257FCF9B54D0931C4D0195D3" + }, + "cost": 131072, + "subtypes": [], + "fingerprintContents": "", + "fulfillment": "A565802102D5D969305535AC29A77079C11D4F0DD40661CF96E04E974A5E8D7E374EE225AA81409C2D6FF39F340BBAF65E884BDFFAE7436A7B7568839C5BA117FA6818245FB9DA3CDEC7CFD4D3DF7191BDB22638FD86F3D9A3B8CB257FCF9B54D0931C4D0195D3", + "conditionBinary": "A52780209C2850F5147E9903DD317C650AC1D6E80E695280789887F2E3179E5C65C9DF3A8103020000", + "conditionUri": "ni:///sha-256;nChQ9RR-mQPdMXxlCsHW6A5pUoB4mIfy4xeeXGXJ3zo?fpt=secp256k1-sha-256&cost=131072", + "message": "" +} diff --git a/src/ctest b/src/ctest new file mode 100755 index 000000000..1bb1a92a8 --- /dev/null +++ b/src/ctest @@ -0,0 +1 @@ +./komodod -ac_name=CTEST -ac_supply=1000000 -ac_perc=100000 -ac_pubkey=02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92 -ac_reward=300000000 -addnode=136.243.58.134 & diff --git a/src/deprecation.cpp b/src/deprecation.cpp new file mode 100644 index 000000000..9f394e720 --- /dev/null +++ b/src/deprecation.cpp @@ -0,0 +1,61 @@ +// Copyright (c) 2017 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "deprecation.h" + +#include "clientversion.h" +#include "init.h" +#include "ui_interface.h" +#include "util.h" +#include "chainparams.h" + +static const std::string CLIENT_VERSION_STR = FormatVersion(CLIENT_VERSION); + +void EnforceNodeDeprecation(int nHeight, bool forceLogging) { + + // Do not enforce deprecation in regtest or on testnet + std::string networkID = Params().NetworkIDString(); + if (networkID != "main") return; + + int blocksToDeprecation = DEPRECATION_HEIGHT - nHeight; + bool disableDeprecation = (GetArg("-disabledeprecation", "") == CLIENT_VERSION_STR); + if (blocksToDeprecation <= 0) { + // In order to ensure we only log once per process when deprecation is + // disabled (to avoid log spam), we only need to log in two cases: + // - The deprecating block just arrived + // - This can be triggered more than once if a block chain reorg + // occurs, but that's an irregular event that won't cause spam. + // - The node is starting + if (blocksToDeprecation == 0 || forceLogging) { + auto msg = strprintf(_("This version has been deprecated as of block height %d."), + DEPRECATION_HEIGHT) + " " + + _("You should upgrade to the latest version of Zcash."); + if (!disableDeprecation) { + msg += " " + strprintf(_("To disable deprecation for this version, set %s%s."), + "-disabledeprecation=", CLIENT_VERSION_STR); + } + LogPrintf("*** %s\n", msg); + uiInterface.ThreadSafeMessageBox(msg, "", CClientUIInterface::MSG_ERROR); + } + if (!disableDeprecation) { + StartShutdown(); + } + } else if (blocksToDeprecation == DEPRECATION_WARN_LIMIT || + (blocksToDeprecation < DEPRECATION_WARN_LIMIT && forceLogging)) { + std::string msg; + if (disableDeprecation) { + msg = strprintf(_("This version will be deprecated at block height %d."), + DEPRECATION_HEIGHT) + " " + + _("You should upgrade to the latest version of Zcash."); + } else { + msg = strprintf(_("This version will be deprecated at block height %d, and will automatically shut down."), + DEPRECATION_HEIGHT) + " " + + _("You should upgrade to the latest version of Zcash.") + " " + + strprintf(_("To disable deprecation for this version, set %s%s."), + "-disabledeprecation=", CLIENT_VERSION_STR); + } + LogPrintf("*** %s\n", msg); + uiInterface.ThreadSafeMessageBox(msg, "", CClientUIInterface::MSG_WARNING); + } +} \ No newline at end of file diff --git a/src/deprecation.h b/src/deprecation.h new file mode 100644 index 000000000..78ed8ebda --- /dev/null +++ b/src/deprecation.h @@ -0,0 +1,25 @@ +// Copyright (c) 2017 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef ZCASH_DEPRECATION_H +#define ZCASH_DEPRECATION_H + +// Deprecation policy: +// * Shut down 16 weeks' worth of blocks after the estimated release block height. +// * A warning is shown during the 2 weeks' worth of blocks prior to shut down. +static const int APPROX_RELEASE_HEIGHT = 800000; +static const int WEEKS_UNTIL_DEPRECATION = 52; +static const int DEPRECATION_HEIGHT = APPROX_RELEASE_HEIGHT + (WEEKS_UNTIL_DEPRECATION * 7 * 24 * 24); + +// Number of blocks before deprecation to warn users +static const int DEPRECATION_WARN_LIMIT = 60 * 24 * 60; // 2 months + +/** + * Checks whether the node is deprecated based on the current block height, and + * shuts down the node with an error if so (and deprecation is not disabled for + * the current client version). + */ +void EnforceNodeDeprecation(int nHeight, bool forceLogging=false); + +#endif // ZCASH_DEPRECATION_H diff --git a/src/dpowassets b/src/dpowassets index 3fe8aa1b0..14bb39912 100755 --- a/src/dpowassets +++ b/src/dpowassets @@ -26,41 +26,10 @@ curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dp curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"AXO\",\"pubkey\":\"$pubkey\"}" curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"ETOMIC\",\"pubkey\":\"$pubkey\"}" curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"BTCH\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"CHAIN\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"VOTE\",\"pubkey\":\"$pubkey\"}" - - -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"USD\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"EUR\",\"pubkey\":\"$pubkey\"}" - # -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"JPY\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"GBP\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"AUD\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"CAD\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"CHF\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"NZD\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"CNY\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"RUB\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"MXN\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"BRL\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"INR\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"HKD\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"TRY\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"ZAR\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"PLN\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"NOK\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"SEK\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"DKK\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"CZK\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"HUF\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"ILS\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"KRW\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"MYR\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"PHP\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"RON\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"SGD\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"THB\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"BGN\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"IDR\",\"pubkey\":\"$pubkey\"}" -#curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"HRK\",\"pubkey\":\"$pubkey\"}" +curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"CHAIN\",\"pubkey\":\"$pubkey\"}" +curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"VOTE2018\",\"pubkey\":\"$pubkey\"}" +curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"NINJA\",\"pubkey\":\"$pubkey\"}" +curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"OOT\",\"pubkey\":\"$pubkey\"}" +curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"BNTN\",\"pubkey\":\"$pubkey\"}" +curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"PRLPAY\",\"pubkey\":\"$pubkey\"}" diff --git a/src/eccryptoverify.cpp b/src/eccryptoverify.cpp deleted file mode 100644 index e894e1122..000000000 --- a/src/eccryptoverify.cpp +++ /dev/null @@ -1,68 +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 http://www.opensource.org/licenses/mit-license.php. - -#include "eccryptoverify.h" - -namespace { - -int CompareBigEndian(const unsigned char *c1, size_t c1len, const unsigned char *c2, size_t c2len) { - while (c1len > c2len) { - if (*c1) - return 1; - c1++; - c1len--; - } - while (c2len > c1len) { - if (*c2) - return -1; - c2++; - c2len--; - } - while (c1len > 0) { - if (*c1 > *c2) - return 1; - if (*c2 > *c1) - return -1; - c1++; - c2++; - c1len--; - } - return 0; -} - -/** Order of secp256k1's generator minus 1. */ -const unsigned char vchMaxModOrder[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 -}; - -/** Half of the order of secp256k1's generator minus 1. */ -const unsigned char vchMaxModHalfOrder[32] = { - 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 -}; - -const unsigned char vchZero[1] = {0}; -} // anon namespace - -namespace eccrypto { - -bool Check(const unsigned char *vch) { - return vch && - CompareBigEndian(vch, 32, vchZero, 0) > 0 && - CompareBigEndian(vch, 32, vchMaxModOrder, 32) <= 0; -} - -bool CheckSignatureElement(const unsigned char *vch, int len, bool half) { - return vch && - CompareBigEndian(vch, len, vchZero, 0) > 0 && - CompareBigEndian(vch, len, half ? vchMaxModHalfOrder : vchMaxModOrder, 32) <= 0; -} - -} // namespace eccrypto diff --git a/src/eccryptoverify.h b/src/eccryptoverify.h deleted file mode 100644 index c67c1e44f..000000000 --- a/src/eccryptoverify.h +++ /dev/null @@ -1,21 +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 http://www.opensource.org/licenses/mit-license.php. - -#ifndef BITCOIN_ECCRYPTOVERIFY_H -#define BITCOIN_ECCRYPTOVERIFY_H - -#include -#include - -class uint256; - -namespace eccrypto { - -bool Check(const unsigned char *vch); -bool CheckSignatureElement(const unsigned char *vch, int len, bool half); - -} // eccrypto namespace - -#endif // BITCOIN_ECCRYPTOVERIFY_H diff --git a/src/ecwrapper.cpp b/src/ecwrapper.cpp deleted file mode 100644 index ae2748806..000000000 --- a/src/ecwrapper.cpp +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright (c) 2009-2014 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 "ecwrapper.h" - -#include "serialize.h" -#include "uint256.h" - -#include -#include - -namespace { - -class ecgroup_order -{ -public: - static const EC_GROUP* get() - { - static const ecgroup_order wrapper; - return wrapper.pgroup; - } - -private: - ecgroup_order() - : pgroup(EC_GROUP_new_by_curve_name(NID_secp256k1)) - { - } - - ~ecgroup_order() - { - EC_GROUP_free(pgroup); - } - - EC_GROUP* pgroup; -}; - -/** - * Perform ECDSA key recovery (see SEC1 4.1.6) for curves over (mod p)-fields - * recid selects which key is recovered - * if check is non-zero, additional checks are performed - */ -int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check) -{ - if (!eckey) return 0; - - int ret = 0; - BN_CTX *ctx = NULL; - - BIGNUM *x = NULL; - BIGNUM *e = NULL; - BIGNUM *order = NULL; - BIGNUM *sor = NULL; - BIGNUM *eor = NULL; - BIGNUM *field = NULL; - EC_POINT *R = NULL; - EC_POINT *O = NULL; - EC_POINT *Q = NULL; - BIGNUM *rr = NULL; - BIGNUM *zero = NULL; - int n = 0; - int i = recid / 2; - - const BIGNUM *sig_r, *sig_s; - ECDSA_SIG_get0(ecsig, &sig_r, &sig_s); - - const EC_GROUP *group = EC_KEY_get0_group(eckey); - if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; } - BN_CTX_start(ctx); - order = BN_CTX_get(ctx); - if (!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; } - x = BN_CTX_get(ctx); - if (!BN_copy(x, order)) { ret=-1; goto err; } - if (!BN_mul_word(x, i)) { ret=-1; goto err; } - if (!BN_add(x, x, sig_r)) { ret=-1; goto err; } - field = BN_CTX_get(ctx); - if (!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret=-2; goto err; } - if (BN_cmp(x, field) >= 0) { ret=0; goto err; } - if ((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } - if (!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret=0; goto err; } - if (check) - { - if ((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } - if (!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret=-2; goto err; } - if (!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; } - } - if ((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } - n = EC_GROUP_get_degree(group); - e = BN_CTX_get(ctx); - if (!BN_bin2bn(msg, msglen, e)) { ret=-1; goto err; } - if (8*msglen > n) BN_rshift(e, e, 8-(n & 7)); - zero = BN_CTX_get(ctx); - if (!BN_zero(zero)) { ret=-1; goto err; } - if (!BN_mod_sub(e, zero, e, order, ctx)) { ret=-1; goto err; } - rr = BN_CTX_get(ctx); - if (!BN_mod_inverse(rr, sig_r, order, ctx)) { ret=-1; goto err; } - sor = BN_CTX_get(ctx); - if (!BN_mod_mul(sor, sig_s, rr, order, ctx)) { ret=-1; goto err; } - eor = BN_CTX_get(ctx); - if (!BN_mod_mul(eor, e, rr, order, ctx)) { ret=-1; goto err; } - if (!EC_POINT_mul(group, Q, eor, R, sor, ctx)) { ret=-2; goto err; } - if (!EC_KEY_set_public_key(eckey, Q)) { ret=-2; goto err; } - - ret = 1; - -err: - if (ctx) { - BN_CTX_end(ctx); - BN_CTX_free(ctx); - } - if (R != NULL) EC_POINT_free(R); - if (O != NULL) EC_POINT_free(O); - if (Q != NULL) EC_POINT_free(Q); - return ret; -} - -} // anon namespace - -CECKey::CECKey() { - pkey = EC_KEY_new(); - assert(pkey != NULL); - int result = EC_KEY_set_group(pkey, ecgroup_order::get()); - assert(result); -} - -CECKey::~CECKey() { - EC_KEY_free(pkey); -} - -void CECKey::GetPubKey(std::vector &pubkey, bool fCompressed) { - EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); - int nSize = i2o_ECPublicKey(pkey, NULL); - assert(nSize); - assert(nSize <= 65); - pubkey.clear(); - pubkey.resize(nSize); - unsigned char *pbegin(begin_ptr(pubkey)); - int nSize2 = i2o_ECPublicKey(pkey, &pbegin); - assert(nSize == nSize2); -} - -bool CECKey::SetPubKey(const unsigned char* pubkey, size_t size) { - return o2i_ECPublicKey(&pkey, &pubkey, size) != NULL; -} - -bool CECKey::Verify(const uint256 &hash, const std::vector& vchSig) { - if (vchSig.empty()) - return false; - - // New versions of OpenSSL will reject non-canonical DER signatures. de/re-serialize first. - unsigned char *norm_der = NULL; - ECDSA_SIG *norm_sig = ECDSA_SIG_new(); - const unsigned char* sigptr = &vchSig[0]; - assert(norm_sig); - if (d2i_ECDSA_SIG(&norm_sig, &sigptr, vchSig.size()) == NULL) - { - /* As of OpenSSL 1.0.0p d2i_ECDSA_SIG frees and nulls the pointer on - * error. But OpenSSL's own use of this function redundantly frees the - * result. As ECDSA_SIG_free(NULL) is a no-op, and in the absence of a - * clear contract for the function behaving the same way is more - * conservative. - */ - ECDSA_SIG_free(norm_sig); - return false; - } - int derlen = i2d_ECDSA_SIG(norm_sig, &norm_der); - ECDSA_SIG_free(norm_sig); - if (derlen <= 0) - return false; - - // -1 = error, 0 = bad sig, 1 = good - bool ret = ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), norm_der, derlen, pkey) == 1; - OPENSSL_free(norm_der); - return ret; -} - -bool CECKey::Recover(const uint256 &hash, const unsigned char *p64, int rec) -{ - if (rec<0 || rec>=3) - return false; - ECDSA_SIG *sig = ECDSA_SIG_new(); - BIGNUM *sig_r = BN_bin2bn(&p64[0], 32, nullptr); - BIGNUM *sig_s = BN_bin2bn(&p64[32], 32, nullptr); - assert(sig && sig_r && sig_s); - bool ret = ECDSA_SIG_set0(sig, sig_r, sig_s); - assert(ret); - ret = ECDSA_SIG_recover_key_GFp(pkey, sig, (unsigned char*)&hash, sizeof(hash), rec, 0) == 1; - ECDSA_SIG_free(sig); - return ret; -} - -bool CECKey::TweakPublic(const unsigned char vchTweak[32]) { - bool ret = true; - BN_CTX *ctx = BN_CTX_new(); - BN_CTX_start(ctx); - BIGNUM *bnTweak = BN_CTX_get(ctx); - BIGNUM *bnOrder = BN_CTX_get(ctx); - BIGNUM *bnOne = BN_CTX_get(ctx); - const EC_GROUP *group = EC_KEY_get0_group(pkey); - EC_GROUP_get_order(group, bnOrder, ctx); // what a grossly inefficient way to get the (constant) group order... - BN_bin2bn(vchTweak, 32, bnTweak); - if (BN_cmp(bnTweak, bnOrder) >= 0) - ret = false; // extremely unlikely - EC_POINT *point = EC_POINT_dup(EC_KEY_get0_public_key(pkey), group); - BN_one(bnOne); - EC_POINT_mul(group, point, bnTweak, point, bnOne, ctx); - if (EC_POINT_is_at_infinity(group, point)) - ret = false; // ridiculously unlikely - EC_KEY_set_public_key(pkey, point); - EC_POINT_free(point); - BN_CTX_end(ctx); - BN_CTX_free(ctx); - return ret; -} - -bool CECKey::SanityCheck() -{ - const EC_GROUP *pgroup = ecgroup_order::get(); - if(pgroup == NULL) - return false; - // TODO Is there more EC functionality that could be missing? - return true; -} diff --git a/src/ecwrapper.h b/src/ecwrapper.h deleted file mode 100644 index efb6cd18a..000000000 --- a/src/ecwrapper.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2009-2014 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_ECWRAPPER_H -#define BITCOIN_ECWRAPPER_H - -#include -#include - -#include - -class uint256; - -/** RAII Wrapper around OpenSSL's EC_KEY */ -class CECKey { -private: - EC_KEY *pkey; - -public: - CECKey(); - ~CECKey(); - - void GetPubKey(std::vector& pubkey, bool fCompressed); - bool SetPubKey(const unsigned char* pubkey, size_t size); - bool Verify(const uint256 &hash, const std::vector& vchSig); - - /** - * reconstruct public key from a compact signature - * This is only slightly more CPU intensive than just verifying it. - * If this function succeeds, the recovered public key is guaranteed to be valid - * (the signature is a valid signature of the given data for that key) - */ - bool Recover(const uint256 &hash, const unsigned char *p64, int rec); - - bool TweakPublic(const unsigned char vchTweak[32]); - static bool SanityCheck(); -}; - -#endif // BITCOIN_ECWRAPPER_H diff --git a/src/fiat-cli b/src/fiat-cli index 81ed6b56a..ce2e7a51d 100755 --- a/src/fiat-cli +++ b/src/fiat-cli @@ -1,37 +1,5 @@ #!/bin/bash #set -x -#echo aud; fiat/aud $1 $2 $3 $4 -#echo bgn; fiat/bgn $1 $2 $3 $4 -#echo cad; fiat/cad $1 $2 $3 $4 -#echo chf; fiat/chf $1 $2 $3 $4 -#echo cny; fiat/cny $1 $2 $3 $4 -#echo czk; fiat/czk $1 $2 $3 $4 -#echo dkk; fiat/dkk $1 $2 $3 $4 -#echo eur; fiat/eur $1 $2 $3 $4 -#echo gbp; fiat/gbp $1 $2 $3 $4 -#echo hkd; fiat/hkd $1 $2 $3 $4 -#echo hrk; fiat/hrk $1 $2 $3 $4 -#echo huf; fiat/huf $1 $2 $3 $4 -#echo idr; fiat/idr $1 $2 $3 $4 -#echo ils; fiat/ils $1 $2 $3 $4 -#echo inr; fiat/inr $1 $2 $3 $4 -#echo jpy; fiat/jpy $1 $2 $3 $4 -#echo krw; fiat/krw $1 $2 $3 $4 -#echo mxn; fiat/mxn $1 $2 $3 $4 -#echo myr; fiat/myr $1 $2 $3 $4 -#echo nok; fiat/nok $1 $2 $3 $4 -#echo nzd; fiat/nzd $1 $2 $3 $4 -#echo php; fiat/php $1 $2 $3 $4 -#echo pln; fiat/pln $1 $2 $3 $4 -#echo brl; fiat/brl $1 $2 $3 $4 -#echo ron; fiat/ron $1 $2 $3 $4 -#echo rub; fiat/rub $1 $2 $3 $4 -#echo sek; fiat/sek $1 $2 $3 $4 -#echo sgd; fiat/sgd $1 $2 $3 $4 -#echo thb; fiat/thb $1 $2 $3 $4 -#echo try; fiat/try $1 $2 $3 $4 -#echo usd; fiat/usd $1 $2 $3 $4 -#echo zar; fiat/zar $1 $2 $3 $4 echo revs; fiat/revs $1 $2 $3 $4 echo supernet; fiat/supernet $1 $2 $3 $4 echo dex; fiat/dex $1 $2 $3 $4 @@ -40,11 +8,9 @@ echo jumblr; fiat/jumblr $1 $2 $3 $4 echo bet; fiat/bet $1 $2 $3 $4 echo crypto; fiat/crypto $1 $2 $3 $4 echo hodl; fiat/hodl $1 $2 $3 $4 -#echo shark; fiat/shark $1 $2 $3 $4 echo mshark; fiat/mshark $1 $2 $3 $4 echo bots; fiat/bots $1 $2 $3 $4 echo mgw; fiat/mgw $1 $2 $3 $4 -#echo mvp; fiat/mvp $1 $2 $3 $4 echo coqui; fiat/coqui $1 $2 $3 $4 echo wlc; fiat/wlc $1 $2 $3 $4 echo kv; fiat/kv $1 $2 $3 $4 @@ -56,4 +22,9 @@ echo etomic; fiat/etomic $1 $2 $3 $4 echo btch; fiat/btch $1 $2 $3 $4 echo pizza; fiat/pizza $1 $2 $3 $4 echo beer; fiat/beer $1 $2 $3 $4 -echo vote; fiat/vote $1 $2 $3 $4 +echo vote2018; fiat/vote2018 $1 $2 $3 $4 +echo ninja; fiat/ninja $1 $2 $3 $4 +echo oot; fiat/oot $1 $2 $3 $4 +echo bntn; fiat/bntn $1 $2 $3 $4 +echo chain; fiat/chain $1 $2 $3 $4 +echo prlpay; fiat/prlpay $1 $2 $3 $4 diff --git a/src/fiat/bntn b/src/fiat/bntn new file mode 100755 index 000000000..7ae6f4e8d --- /dev/null +++ b/src/fiat/bntn @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=BNTN $1 $2 $3 $4 $5 $6 diff --git a/src/fiat/chain b/src/fiat/chain new file mode 100755 index 000000000..c55d30fbd --- /dev/null +++ b/src/fiat/chain @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=CHAIN $1 $2 $3 $4 $5 $6 diff --git a/src/fiat/ninja b/src/fiat/ninja new file mode 100755 index 000000000..f7722abb0 --- /dev/null +++ b/src/fiat/ninja @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=NINJA $1 $2 $3 $4 $5 $6 diff --git a/src/fiat/oot b/src/fiat/oot new file mode 100755 index 000000000..4c7c6b683 --- /dev/null +++ b/src/fiat/oot @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=OOT $1 $2 $3 $4 $5 $6 diff --git a/src/fiat/prlpay b/src/fiat/prlpay new file mode 100755 index 000000000..3d1063d13 --- /dev/null +++ b/src/fiat/prlpay @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=PRLPAY $1 $2 $3 $4 $5 $6 diff --git a/src/fiat/vote b/src/fiat/vote deleted file mode 100755 index 67f47953e..000000000 --- a/src/fiat/vote +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -./komodo-cli -ac_name=VOTE $1 $2 $3 $4 $5 $6 diff --git a/src/fiat/vote2018 b/src/fiat/vote2018 new file mode 100755 index 000000000..4e385d76f --- /dev/null +++ b/src/fiat/vote2018 @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=VOTE2018 $1 $2 $3 $4 $5 $6 diff --git a/src/gtest/main.cpp b/src/gtest/main.cpp index 84e6f867c..d2ae0b23d 100644 --- a/src/gtest/main.cpp +++ b/src/gtest/main.cpp @@ -1,15 +1,30 @@ -#include "gtest/gtest.h" +#include "gmock/gmock.h" #include "crypto/common.h" +#include "pubkey.h" +#include "zcash/JoinSplit.hpp" +#include "util.h" -#include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp" -#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp" +#include +#include + +struct ECCryptoClosure +{ + ECCVerifyHandle handle; +}; + +ECCryptoClosure instance_of_eccryptoclosure; + +ZCJoinSplit* params; int main(int argc, char **argv) { assert(init_and_check_sodium() != -1); libsnark::default_r1cs_ppzksnark_pp::init_public_params(); libsnark::inhibit_profiling_info = true; libsnark::inhibit_profiling_counters = true; + boost::filesystem::path pk_path = ZC_GetParamsDir() / "sprout-proving.key"; + boost::filesystem::path vk_path = ZC_GetParamsDir() / "sprout-verifying.key"; + params = ZCJoinSplit::Prepared(vk_path.string(), pk_path.string()); - testing::InitGoogleTest(&argc, argv); + testing::InitGoogleMock(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/src/gtest/test_checkblock.cpp b/src/gtest/test_checkblock.cpp index dde02118f..e098cfdae 100644 --- a/src/gtest/test_checkblock.cpp +++ b/src/gtest/test_checkblock.cpp @@ -33,6 +33,40 @@ TEST(CheckBlock, VersionTooLow) { EXPECT_FALSE(CheckBlock(0,0,block, state, verifier, false, false)); } + +// Test that a Sprout tx with negative version is still rejected +// by CheckBlock under Sprout consensus rules. +TEST(CheckBlock, BlockSproutRejectsBadVersion) { + SelectParams(CBaseChainParams::MAIN); + + CMutableTransaction mtx; + mtx.vin.resize(1); + mtx.vin[0].prevout.SetNull(); + mtx.vin[0].scriptSig = CScript() << 1 << OP_0; + mtx.vout.resize(1); + mtx.vout[0].scriptPubKey = CScript() << OP_TRUE; + mtx.vout[0].nValue = 0; + mtx.vout.push_back(CTxOut( + GetBlockSubsidy(1, Params().GetConsensus())/5, + Params().GetFoundersRewardScriptAtHeight(1))); + mtx.fOverwintered = false; + mtx.nVersion = -1; + mtx.nVersionGroupId = 0; + + CTransaction tx {mtx}; + CBlock block; + block.vtx.push_back(tx); + + MockCValidationState state; + CBlockIndex indexPrev {Params().GenesisBlock()}; + + auto verifier = libzcash::ProofVerifier::Strict(); + + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-version-too-low", false)).Times(1); + EXPECT_FALSE(CheckBlock(block, state, verifier, false, false)); +} + + TEST(ContextualCheckBlock, BadCoinbaseHeight) { SelectParams(CBaseChainParams::MAIN); @@ -75,3 +109,125 @@ TEST(ContextualCheckBlock, BadCoinbaseHeight) { block.vtx[0] = tx4; EXPECT_TRUE(ContextualCheckBlock(block, state, &indexPrev)); } + +// Test that a block evaluated under Sprout rules cannot contain Overwinter transactions. +// This test assumes that mainnet Overwinter activation is at least height 2. +TEST(ContextualCheckBlock, BlockSproutRulesRejectOverwinterTx) { + SelectParams(CBaseChainParams::MAIN); + + CMutableTransaction mtx; + mtx.vin.resize(1); + mtx.vin[0].prevout.SetNull(); + mtx.vin[0].scriptSig = CScript() << 1 << OP_0; + mtx.vout.resize(1); + mtx.vout[0].scriptPubKey = CScript() << OP_TRUE; + mtx.vout[0].nValue = 0; + + mtx.fOverwintered = true; + mtx.nVersion = 3; + mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID; + + CTransaction tx {mtx}; + CBlock block; + block.vtx.push_back(tx); + + MockCValidationState state; + CBlockIndex indexPrev {Params().GenesisBlock()}; + + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-not-active", false)).Times(1); + EXPECT_FALSE(ContextualCheckBlock(block, state, &indexPrev)); +} + + +// Test block evaluated under Sprout rules will accept Sprout transactions +TEST(ContextualCheckBlock, BlockSproutRulesAcceptSproutTx) { + SelectParams(CBaseChainParams::REGTEST); + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); + + CMutableTransaction mtx; + mtx.vin.resize(1); + mtx.vin[0].prevout.SetNull(); + mtx.vin[0].scriptSig = CScript() << 1 << OP_0; + mtx.vout.resize(1); + mtx.vout[0].scriptPubKey = CScript() << OP_TRUE; + mtx.vout[0].nValue = 0; + mtx.vout.push_back(CTxOut( + GetBlockSubsidy(1, Params().GetConsensus())/5, + Params().GetFoundersRewardScriptAtHeight(1))); + mtx.fOverwintered = false; + mtx.nVersion = 1; + + CTransaction tx {mtx}; + CBlock block; + block.vtx.push_back(tx); + MockCValidationState state; + CBlockIndex indexPrev {Params().GenesisBlock()}; + + EXPECT_TRUE(ContextualCheckBlock(block, state, &indexPrev)); + + // Revert to default + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); +} + + +// Test block evaluated under Overwinter rules will accept Overwinter transactions +TEST(ContextualCheckBlock, BlockOverwinterRulesAcceptOverwinterTx) { + SelectParams(CBaseChainParams::REGTEST); + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1); + + CMutableTransaction mtx; + mtx.vin.resize(1); + mtx.vin[0].prevout.SetNull(); + mtx.vin[0].scriptSig = CScript() << 1 << OP_0; + mtx.vout.resize(1); + mtx.vout[0].scriptPubKey = CScript() << OP_TRUE; + mtx.vout[0].nValue = 0; + mtx.vout.push_back(CTxOut( + GetBlockSubsidy(1, Params().GetConsensus())/5, + Params().GetFoundersRewardScriptAtHeight(1))); + mtx.fOverwintered = true; + mtx.nVersion = 3; + mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID; + + CTransaction tx {mtx}; + CBlock block; + block.vtx.push_back(tx); + MockCValidationState state; + CBlockIndex indexPrev {Params().GenesisBlock()}; + + EXPECT_TRUE(ContextualCheckBlock(block, state, &indexPrev)); + + // Revert to default + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); +} + + + +// Test block evaluated under Overwinter rules will reject Sprout transactions +TEST(ContextualCheckBlock, BlockOverwinterRulesRejectSproutTx) { + SelectParams(CBaseChainParams::REGTEST); + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1); + + CMutableTransaction mtx; + mtx.vin.resize(1); + mtx.vin[0].prevout.SetNull(); + mtx.vin[0].scriptSig = CScript() << 1 << OP_0; + mtx.vout.resize(1); + mtx.vout[0].scriptPubKey = CScript() << OP_TRUE; + mtx.vout[0].nValue = 0; + + mtx.nVersion = 2; + + CTransaction tx {mtx}; + CBlock block; + block.vtx.push_back(tx); + + MockCValidationState state; + CBlockIndex indexPrev {Params().GenesisBlock()}; + + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-active", false)).Times(1); + EXPECT_FALSE(ContextualCheckBlock(block, state, &indexPrev)); + + // Revert to default + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); +} \ No newline at end of file diff --git a/src/gtest/test_checktransaction.cpp b/src/gtest/test_checktransaction.cpp index 305977103..bec9cdd6f 100644 --- a/src/gtest/test_checktransaction.cpp +++ b/src/gtest/test_checktransaction.cpp @@ -45,6 +45,8 @@ public: CMutableTransaction GetValidTransaction() { + uint32_t consensusBranchId = SPROUT_BRANCH_ID; + CMutableTransaction mtx; mtx.vin.resize(2); mtx.vin[0].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000001"); @@ -74,7 +76,7 @@ CMutableTransaction GetValidTransaction() { // Empty output script. CScript scriptCode; CTransaction signTx(mtx); - uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL); + uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId); if (dataToBeSigned == one) { throw std::runtime_error("SignatureHash failed"); } @@ -352,23 +354,27 @@ TEST(checktransaction_tests, bad_txns_prevout_null) { } TEST(checktransaction_tests, bad_txns_invalid_joinsplit_signature) { + SelectParams(CBaseChainParams::REGTEST); + CMutableTransaction mtx = GetValidTransaction(); mtx.joinSplitSig[0] += 1; CTransaction tx(mtx); MockCValidationState state; EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1); - CheckTransactionWithoutProofVerification(tx, state); + ContextualCheckTransaction(tx, state, 0, 100); } TEST(checktransaction_tests, non_canonical_ed25519_signature) { + SelectParams(CBaseChainParams::REGTEST); + CMutableTransaction mtx = GetValidTransaction(); // Check that the signature is valid before we add L { CTransaction tx(mtx); MockCValidationState state; - EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state)); + EXPECT_TRUE(ContextualCheckTransaction(tx, state, 0, 100)); } // Copied from libsodium/crypto_sign/ed25519/ref10/open.c @@ -389,5 +395,380 @@ TEST(checktransaction_tests, non_canonical_ed25519_signature) { MockCValidationState state; EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1); + ContextualCheckTransaction(tx, state, 0, 100); +} + +TEST(checktransaction_tests, OverwinterConstructors) { + CMutableTransaction mtx; + mtx.fOverwintered = true; + mtx.nVersion = 3; + mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID; + mtx.nExpiryHeight = 20; + + // Check constructor with overwinter fields + CTransaction tx(mtx); + EXPECT_EQ(tx.nVersion, mtx.nVersion); + EXPECT_EQ(tx.fOverwintered, mtx.fOverwintered); + EXPECT_EQ(tx.nVersionGroupId, mtx.nVersionGroupId); + EXPECT_EQ(tx.nExpiryHeight, mtx.nExpiryHeight); + + // Check constructor of mutable transaction struct + CMutableTransaction mtx2(tx); + EXPECT_EQ(mtx2.nVersion, mtx.nVersion); + EXPECT_EQ(mtx2.fOverwintered, mtx.fOverwintered); + EXPECT_EQ(mtx2.nVersionGroupId, mtx.nVersionGroupId); + EXPECT_EQ(mtx2.nExpiryHeight, mtx.nExpiryHeight); + EXPECT_TRUE(mtx2.GetHash() == mtx.GetHash()); + + // Check assignment of overwinter fields + CTransaction tx2 = tx; + EXPECT_EQ(tx2.nVersion, mtx.nVersion); + EXPECT_EQ(tx2.fOverwintered, mtx.fOverwintered); + EXPECT_EQ(tx2.nVersionGroupId, mtx.nVersionGroupId); + EXPECT_EQ(tx2.nExpiryHeight, mtx.nExpiryHeight); + EXPECT_TRUE(tx2 == tx); +} + +TEST(checktransaction_tests, OverwinterSerialization) { + CMutableTransaction mtx; + mtx.fOverwintered = true; + mtx.nVersion = 3; + mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID; + mtx.nExpiryHeight = 99; + + // Check round-trip serialization and deserialization from mtx to tx. + { + CDataStream ss(SER_DISK, PROTOCOL_VERSION); + ss << mtx; + CTransaction tx; + ss >> tx; + EXPECT_EQ(mtx.nVersion, tx.nVersion); + EXPECT_EQ(mtx.fOverwintered, tx.fOverwintered); + EXPECT_EQ(mtx.nVersionGroupId, tx.nVersionGroupId); + EXPECT_EQ(mtx.nExpiryHeight, tx.nExpiryHeight); + + EXPECT_EQ(mtx.GetHash(), CMutableTransaction(tx).GetHash()); + EXPECT_EQ(tx.GetHash(), CTransaction(mtx).GetHash()); + } + + // Also check mtx to mtx + { + CDataStream ss(SER_DISK, PROTOCOL_VERSION); + ss << mtx; + CMutableTransaction mtx2; + ss >> mtx2; + EXPECT_EQ(mtx.nVersion, mtx2.nVersion); + EXPECT_EQ(mtx.fOverwintered, mtx2.fOverwintered); + EXPECT_EQ(mtx.nVersionGroupId, mtx2.nVersionGroupId); + EXPECT_EQ(mtx.nExpiryHeight, mtx2.nExpiryHeight); + + EXPECT_EQ(mtx.GetHash(), mtx2.GetHash()); + } + + // Also check tx to tx + { + CTransaction tx(mtx); + CDataStream ss(SER_DISK, PROTOCOL_VERSION); + ss << tx; + CTransaction tx2; + ss >> tx2; + EXPECT_EQ(tx.nVersion, tx2.nVersion); + EXPECT_EQ(tx.fOverwintered, tx2.fOverwintered); + EXPECT_EQ(tx.nVersionGroupId, tx2.nVersionGroupId); + EXPECT_EQ(tx.nExpiryHeight, tx2.nExpiryHeight); + + EXPECT_EQ(mtx.GetHash(), CMutableTransaction(tx).GetHash()); + EXPECT_EQ(tx.GetHash(), tx2.GetHash()); + } +} + +TEST(checktransaction_tests, OverwinterDefaultValues) { + // Check default values (this will fail when defaults change; test should then be updated) + CTransaction tx; + EXPECT_EQ(tx.nVersion, 1); + EXPECT_EQ(tx.fOverwintered, false); + EXPECT_EQ(tx.nVersionGroupId, 0); + EXPECT_EQ(tx.nExpiryHeight, 0); +} + +// A valid v3 transaction with no joinsplits +TEST(checktransaction_tests, OverwinterValidTx) { + CMutableTransaction mtx = GetValidTransaction(); + mtx.vjoinsplit.resize(0); + mtx.fOverwintered = true; + mtx.nVersion = 3; + mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID; + mtx.nExpiryHeight = 0; + CTransaction tx(mtx); + MockCValidationState state; + EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state)); +} + +TEST(checktransaction_tests, OverwinterExpiryHeight) { + CMutableTransaction mtx = GetValidTransaction(); + mtx.vjoinsplit.resize(0); + mtx.fOverwintered = true; + mtx.nVersion = 3; + mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID; + mtx.nExpiryHeight = 0; + + { + CTransaction tx(mtx); + MockCValidationState state; + EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state)); + } + + { + mtx.nExpiryHeight = TX_EXPIRY_HEIGHT_THRESHOLD - 1; + CTransaction tx(mtx); + MockCValidationState state; + EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state)); + } + + { + 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); + CheckTransactionWithoutProofVerification(tx, state); + } + + { + 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); + CheckTransactionWithoutProofVerification(tx, state); + } +} + + +// Test that a Sprout tx with a negative version number is detected +// given the new Overwinter logic +TEST(checktransaction_tests, SproutTxVersionTooLow) { + CMutableTransaction mtx = GetValidTransaction(); + mtx.vjoinsplit.resize(0); + mtx.fOverwintered = false; + mtx.nVersion = -1; + + CTransaction tx(mtx); + MockCValidationState state; + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-version-too-low", false)).Times(1); + CheckTransactionWithoutProofVerification(tx, state); +} + + + +// Subclass of CTransaction which doesn't call UpdateHash when constructing +// from a CMutableTransaction. This enables us to create a CTransaction +// with bad values which normally trigger an exception during construction. +class UNSAFE_CTransaction : public CTransaction { + public: + UNSAFE_CTransaction(const CMutableTransaction &tx) : CTransaction(tx, true) {} +}; + + +// Test bad Overwinter version number in CheckTransactionWithoutProofVerification +TEST(checktransaction_tests, OverwinterVersionNumberLow) { + CMutableTransaction mtx = GetValidTransaction(); + mtx.vjoinsplit.resize(0); + mtx.fOverwintered = true; + mtx.nVersion = OVERWINTER_MIN_TX_VERSION - 1; + mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID; + mtx.nExpiryHeight = 0; + + UNSAFE_CTransaction tx(mtx); + MockCValidationState state; + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-overwinter-version-too-low", false)).Times(1); + CheckTransactionWithoutProofVerification(tx, state); +} + +// Test bad Overwinter version number in ContextualCheckTransaction +TEST(checktransaction_tests, OverwinterVersionNumberHigh) { + SelectParams(CBaseChainParams::REGTEST); + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); + + CMutableTransaction mtx = GetValidTransaction(); + mtx.vjoinsplit.resize(0); + mtx.fOverwintered = true; + mtx.nVersion = OVERWINTER_MAX_TX_VERSION + 1; + mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID; + mtx.nExpiryHeight = 0; + + UNSAFE_CTransaction tx(mtx); + MockCValidationState state; + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-overwinter-version-too-high", false)).Times(1); + ContextualCheckTransaction(tx, state, 1, 100); + + // Revert to default + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); +} + + +// Test bad Overwinter version group id +TEST(checktransaction_tests, OverwinterBadVersionGroupId) { + CMutableTransaction mtx = GetValidTransaction(); + mtx.vjoinsplit.resize(0); + mtx.fOverwintered = true; + mtx.nVersion = 3; + mtx.nExpiryHeight = 0; + mtx.nVersionGroupId = 0x12345678; + + UNSAFE_CTransaction tx(mtx); + MockCValidationState state; + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-version-group-id", false)).Times(1); CheckTransactionWithoutProofVerification(tx, state); } + +// This tests an Overwinter transaction checked against Sprout +TEST(checktransaction_tests, OverwinterNotActive) { + SelectParams(CBaseChainParams::TESTNET); + + CMutableTransaction mtx = GetValidTransaction(); + mtx.fOverwintered = true; + mtx.nVersion = 3; + mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID; + mtx.nExpiryHeight = 0; + + CTransaction tx(mtx); + MockCValidationState state; + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-not-active", false)).Times(1); + ContextualCheckTransaction(tx, state, 1, 100); +} + +// This tests a transaction without the fOverwintered flag set, against the Overwinter consensus rule set. +TEST(checktransaction_tests, OverwinterFlagNotSet) { + SelectParams(CBaseChainParams::REGTEST); + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); + + CMutableTransaction mtx = GetValidTransaction(); + mtx.fOverwintered = false; + mtx.nVersion = 3; + mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID; + mtx.nExpiryHeight = 0; + + CTransaction tx(mtx); + MockCValidationState state; + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-flag-not-set", false)).Times(1); + ContextualCheckTransaction(tx, state, 1, 100); + + // Revert to default + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); +} + + +// Overwinter (NU0) does not allow soft fork to version 4 Overwintered tx. +TEST(checktransaction_tests, OverwinterInvalidSoftForkVersion) { + CMutableTransaction mtx = GetValidTransaction(); + mtx.fOverwintered = true; + mtx.nVersion = 4; // This is not allowed + mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID; + mtx.nExpiryHeight = 0; + + CDataStream ss(SER_DISK, PROTOCOL_VERSION); + try { + ss << mtx; + FAIL() << "Expected std::ios_base::failure 'Unknown transaction format'"; + } + catch(std::ios_base::failure & err) { + EXPECT_THAT(err.what(), testing::HasSubstr(std::string("Unknown transaction format"))); + } + catch(...) { + FAIL() << "Expected std::ios_base::failure 'Unknown transaction format', got some other exception"; + } +} + + +// Test CreateNewContextualCMutableTransaction sets default values based on height +TEST(checktransaction_tests, OverwinteredContextualCreateTx) { + SelectParams(CBaseChainParams::REGTEST); + const Consensus::Params& consensusParams = Params().GetConsensus(); + int activationHeight = 5; + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, activationHeight); + + { + CMutableTransaction mtx = CreateNewContextualCMutableTransaction( + consensusParams, activationHeight - 1); + + EXPECT_EQ(mtx.nVersion, 1); + EXPECT_EQ(mtx.fOverwintered, false); + EXPECT_EQ(mtx.nVersionGroupId, 0); + EXPECT_EQ(mtx.nExpiryHeight, 0); + } + + // Overwinter activates + { + CMutableTransaction mtx = CreateNewContextualCMutableTransaction( + consensusParams, activationHeight ); + + EXPECT_EQ(mtx.nVersion, 3); + EXPECT_EQ(mtx.fOverwintered, true); + EXPECT_EQ(mtx.nVersionGroupId, OVERWINTER_VERSION_GROUP_ID); + EXPECT_EQ(mtx.nExpiryHeight, 0); + } + + // Revert to default + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); +} + +// Test a v1 transaction which has a malformed header, perhaps modified in-flight +TEST(checktransaction_tests, BadTxReceivedOverNetwork) +{ + // First four bytes <01 00 00 00> have been modified to be (-4 as an int32) + std::string goodPrefix = "01000000"; + std::string badPrefix = "fcffffff"; + std::string hexTx = "0176c6541939b95f8d8b7779a77a0863b2a0267e281a050148326f0ea07c3608fb000000006a47304402207c68117a6263486281af0cc5d3bee6db565b6dce19ffacc4cb361906eece82f8022007f604382dee2c1fde41c4e6e7c1ae36cfa28b5b27350c4bfaa27f555529eace01210307ff9bef60f2ac4ceb1169a9f7d2c773d6c7f4ab6699e1e5ebc2e0c6d291c733feffffff02c0d45407000000001976a9145eaaf6718517ec8a291c6e64b16183292e7011f788ac5ef44534000000001976a91485e12fb9967c96759eae1c6b1e9c07ce977b638788acbe000000"; + + // Good v1 tx + { + std::vector txData(ParseHex(goodPrefix + hexTx )); + CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); + CTransaction tx; + ssData >> tx; + EXPECT_EQ(tx.nVersion, 1); + EXPECT_EQ(tx.fOverwintered, false); + } + + // Good v1 mutable tx + { + std::vector txData(ParseHex(goodPrefix + hexTx )); + CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); + CMutableTransaction mtx; + ssData >> mtx; + EXPECT_EQ(mtx.nVersion, 1); + } + + // Bad tx + { + std::vector txData(ParseHex(badPrefix + hexTx )); + CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); + try { + CTransaction tx; + ssData >> tx; + FAIL() << "Expected std::ios_base::failure 'Unknown transaction format'"; + } + catch(std::ios_base::failure & err) { + EXPECT_THAT(err.what(), testing::HasSubstr(std::string("Unknown transaction format"))); + } + catch(...) { + FAIL() << "Expected std::ios_base::failure 'Unknown transaction format', got some other exception"; + } + } + + // Bad mutable tx + { + std::vector txData(ParseHex(badPrefix + hexTx )); + CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); + try { + CMutableTransaction mtx; + ssData >> mtx; + FAIL() << "Expected std::ios_base::failure 'Unknown transaction format'"; + } + catch(std::ios_base::failure & err) { + EXPECT_THAT(err.what(), testing::HasSubstr(std::string("Unknown transaction format"))); + } + catch(...) { + FAIL() << "Expected std::ios_base::failure 'Unknown transaction format', got some other exception"; + } + } +} \ No newline at end of file diff --git a/src/gtest/test_circuit.cpp b/src/gtest/test_circuit.cpp index f8a0416a7..2cc9dbcbb 100644 --- a/src/gtest/test_circuit.cpp +++ b/src/gtest/test_circuit.cpp @@ -7,10 +7,11 @@ #include #include -#include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp" -#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp" -#include "libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp" -#include "libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.hpp" +#include +#include +#include +#include + #include "zcash/IncrementalMerkleTree.hpp" using namespace libsnark; diff --git a/src/gtest/test_deprecation.cpp b/src/gtest/test_deprecation.cpp new file mode 100644 index 000000000..eb2d7bfc6 --- /dev/null +++ b/src/gtest/test_deprecation.cpp @@ -0,0 +1,122 @@ +#include +#include + +#include "clientversion.h" +#include "deprecation.h" +#include "init.h" +#include "ui_interface.h" +#include "util.h" +#include "chainparams.h" + +using ::testing::StrictMock; + +static const std::string CLIENT_VERSION_STR = FormatVersion(CLIENT_VERSION); +extern std::atomic fRequestShutdown; + +class MockUIInterface { +public: + MOCK_METHOD3(ThreadSafeMessageBox, bool(const std::string& message, + const std::string& caption, + unsigned int style)); +}; + +static bool ThreadSafeMessageBox(MockUIInterface *mock, + const std::string& message, + const std::string& caption, + unsigned int style) +{ + return mock->ThreadSafeMessageBox(message, caption, style); +} + +class DeprecationTest : public ::testing::Test { +protected: + virtual void SetUp() { + uiInterface.ThreadSafeMessageBox.disconnect_all_slots(); + uiInterface.ThreadSafeMessageBox.connect(boost::bind(ThreadSafeMessageBox, &mock_, _1, _2, _3)); + SelectParams(CBaseChainParams::MAIN); + + } + + virtual void TearDown() { + fRequestShutdown = false; + mapArgs.clear(); + } + + StrictMock mock_; +}; + +TEST_F(DeprecationTest, NonDeprecatedNodeKeepsRunning) { + EXPECT_FALSE(ShutdownRequested()); + EnforceNodeDeprecation(DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT - 1); + EXPECT_FALSE(ShutdownRequested()); +} + +TEST_F(DeprecationTest, NodeNearDeprecationIsWarned) { + EXPECT_FALSE(ShutdownRequested()); + EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_WARNING)); + EnforceNodeDeprecation(DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT); + EXPECT_FALSE(ShutdownRequested()); +} + +TEST_F(DeprecationTest, NodeNearDeprecationWarningIsNotDuplicated) { + EXPECT_FALSE(ShutdownRequested()); + EnforceNodeDeprecation(DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT + 1); + EXPECT_FALSE(ShutdownRequested()); +} + +TEST_F(DeprecationTest, NodeNearDeprecationWarningIsRepeatedOnStartup) { + EXPECT_FALSE(ShutdownRequested()); + EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_WARNING)); + EnforceNodeDeprecation(DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT + 1, true); + EXPECT_FALSE(ShutdownRequested()); +} + +TEST_F(DeprecationTest, DeprecatedNodeShutsDown) { + EXPECT_FALSE(ShutdownRequested()); + EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_ERROR)); + EnforceNodeDeprecation(DEPRECATION_HEIGHT); + EXPECT_TRUE(ShutdownRequested()); +} + +TEST_F(DeprecationTest, DeprecatedNodeErrorIsNotDuplicated) { + EXPECT_FALSE(ShutdownRequested()); + EnforceNodeDeprecation(DEPRECATION_HEIGHT + 1); + EXPECT_TRUE(ShutdownRequested()); +} + +TEST_F(DeprecationTest, DeprecatedNodeErrorIsRepeatedOnStartup) { + EXPECT_FALSE(ShutdownRequested()); + EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_ERROR)); + EnforceNodeDeprecation(DEPRECATION_HEIGHT + 1, true); + EXPECT_TRUE(ShutdownRequested()); +} + +TEST_F(DeprecationTest, DeprecatedNodeShutsDownIfOldVersionDisabled) { + EXPECT_FALSE(ShutdownRequested()); + mapArgs["-disabledeprecation"] = "1.0.0"; + EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_ERROR)); + EnforceNodeDeprecation(DEPRECATION_HEIGHT); + EXPECT_TRUE(ShutdownRequested()); +} + +TEST_F(DeprecationTest, DeprecatedNodeKeepsRunningIfCurrentVersionDisabled) { + EXPECT_FALSE(ShutdownRequested()); + mapArgs["-disabledeprecation"] = CLIENT_VERSION_STR; + EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_ERROR)); + EnforceNodeDeprecation(DEPRECATION_HEIGHT); + EXPECT_FALSE(ShutdownRequested()); +} + +TEST_F(DeprecationTest, DeprecatedNodeIgnoredOnRegtest) { + SelectParams(CBaseChainParams::REGTEST); + EXPECT_FALSE(ShutdownRequested()); + EnforceNodeDeprecation(DEPRECATION_HEIGHT+1); + EXPECT_FALSE(ShutdownRequested()); +} + +TEST_F(DeprecationTest, DeprecatedNodeIgnoredOnTestnet) { + SelectParams(CBaseChainParams::TESTNET); + EXPECT_FALSE(ShutdownRequested()); + EnforceNodeDeprecation(DEPRECATION_HEIGHT+1); + EXPECT_FALSE(ShutdownRequested()); +} \ No newline at end of file diff --git a/src/gtest/test_foundersreward.cpp b/src/gtest/test_foundersreward.cpp index ea6fb0ab1..63649cee1 100644 --- a/src/gtest/test_foundersreward.cpp +++ b/src/gtest/test_foundersreward.cpp @@ -81,6 +81,8 @@ TEST(founders_reward_test, create_testnet_2of3multisig) { std::cout << s << std::endl; pWallet->Flush(true); + + ECC_Stop(); } #endif diff --git a/src/gtest/test_httprpc.cpp b/src/gtest/test_httprpc.cpp new file mode 100644 index 000000000..c630973fb --- /dev/null +++ b/src/gtest/test_httprpc.cpp @@ -0,0 +1,62 @@ +#include +#include + +#include "httprpc.cpp" +#include "httpserver.h" + +using ::testing::Return; + +class MockHTTPRequest : public HTTPRequest { +public: + MOCK_METHOD0(GetPeer, CService()); + MOCK_METHOD0(GetRequestMethod, HTTPRequest::RequestMethod()); + MOCK_METHOD1(GetHeader, std::pair(const std::string& hdr)); + MOCK_METHOD2(WriteHeader, void(const std::string& hdr, const std::string& value)); + MOCK_METHOD2(WriteReply, void(int nStatus, const std::string& strReply)); + + MockHTTPRequest() : HTTPRequest(nullptr) {} + void CleanUp() { + // So the parent destructor doesn't try to send a reply + replySent = true; + } +}; + +TEST(HTTPRPC, FailsOnGET) { + MockHTTPRequest req; + EXPECT_CALL(req, GetRequestMethod()) + .WillRepeatedly(Return(HTTPRequest::GET)); + EXPECT_CALL(req, WriteReply(HTTP_BAD_METHOD, "JSONRPC server handles only POST requests")) + .Times(1); + EXPECT_FALSE(HTTPReq_JSONRPC(&req, "")); + req.CleanUp(); +} + +TEST(HTTPRPC, FailsWithoutAuthHeader) { + MockHTTPRequest req; + EXPECT_CALL(req, GetRequestMethod()) + .WillRepeatedly(Return(HTTPRequest::POST)); + EXPECT_CALL(req, GetHeader("authorization")) + .WillRepeatedly(Return(std::make_pair(false, ""))); + EXPECT_CALL(req, WriteHeader("WWW-Authenticate", "Basic realm=\"jsonrpc\"")) + .Times(1); + EXPECT_CALL(req, WriteReply(HTTP_UNAUTHORIZED, "")) + .Times(1); + EXPECT_FALSE(HTTPReq_JSONRPC(&req, "")); + req.CleanUp(); +} + +TEST(HTTPRPC, FailsWithBadAuth) { + MockHTTPRequest req; + EXPECT_CALL(req, GetRequestMethod()) + .WillRepeatedly(Return(HTTPRequest::POST)); + EXPECT_CALL(req, GetHeader("authorization")) + .WillRepeatedly(Return(std::make_pair(true, "Basic spam:eggs"))); + EXPECT_CALL(req, GetPeer()) + .WillRepeatedly(Return(CService("127.0.0.1:1337"))); + EXPECT_CALL(req, WriteHeader("WWW-Authenticate", "Basic realm=\"jsonrpc\"")) + .Times(1); + EXPECT_CALL(req, WriteReply(HTTP_UNAUTHORIZED, "")) + .Times(1); + EXPECT_FALSE(HTTPReq_JSONRPC(&req, "")); + req.CleanUp(); +} diff --git a/src/gtest/test_joinsplit.cpp b/src/gtest/test_joinsplit.cpp index 17ac7ecb2..979d0d518 100644 --- a/src/gtest/test_joinsplit.cpp +++ b/src/gtest/test_joinsplit.cpp @@ -5,6 +5,7 @@ #include #include "zcash/prf.h" +#include "util.h" #include "zcash/JoinSplit.hpp" #include "zcash/Note.hpp" @@ -13,6 +14,8 @@ using namespace libzcash; +extern ZCJoinSplit* params; + void test_full_api(ZCJoinSplit* js) { // Create verification context. @@ -86,7 +89,7 @@ void test_full_api(ZCJoinSplit* js) // Recipient should decrypt // Now the recipient should spend the money again auto h_sig = js->h_sig(randomSeed, nullifiers, pubKeyHash); - ZCNoteDecryption decryptor(recipient_key.viewing_key()); + ZCNoteDecryption decryptor(recipient_key.receiving_key()); auto note_pt = NotePlaintext::decrypt( decryptor, @@ -219,8 +222,6 @@ void invokeAPIFailure( TEST(joinsplit, h_sig) { - auto js = ZCJoinSplit::Unopened(); - /* // by Taylor Hornby @@ -284,7 +285,7 @@ for test_input in TEST_VECTORS: }; BOOST_FOREACH(std::vector& v, tests) { - auto expected = js->h_sig( + auto expected = ZCJoinSplit::h_sig( uint256S(v[0]), {uint256S(v[1]), uint256S(v[2])}, uint256S(v[3]) @@ -292,8 +293,6 @@ for test_input in TEST_VECTORS: EXPECT_EQ(expected, uint256S(v[4])); } - - delete js; } void increment_note_witnesses( @@ -311,8 +310,6 @@ void increment_note_witnesses( TEST(joinsplit, full_api_test) { - auto js = ZCJoinSplit::Generate(); - { std::vector witnesses; ZCIncrementalMerkleTree tree; @@ -331,7 +328,7 @@ TEST(joinsplit, full_api_test) increment_note_witnesses(note5.cm(), witnesses, tree); // Should work - invokeAPI(js, + invokeAPI(params, { JSInput(), JSInput() @@ -345,7 +342,7 @@ TEST(joinsplit, full_api_test) tree.root()); // lhs > MAX_MONEY - invokeAPIFailure(js, + invokeAPIFailure(params, { JSInput(), JSInput() @@ -360,7 +357,7 @@ TEST(joinsplit, full_api_test) "nonsensical vpub_old value"); // rhs > MAX_MONEY - invokeAPIFailure(js, + invokeAPIFailure(params, { JSInput(), JSInput() @@ -375,7 +372,7 @@ TEST(joinsplit, full_api_test) "nonsensical vpub_new value"); // input witness for the wrong element - invokeAPIFailure(js, + invokeAPIFailure(params, { JSInput(witnesses[0], note1, sk), JSInput() @@ -391,7 +388,7 @@ TEST(joinsplit, full_api_test) // input witness doesn't match up with // real root - invokeAPIFailure(js, + invokeAPIFailure(params, { JSInput(witnesses[1], note1, sk), JSInput() @@ -406,7 +403,7 @@ TEST(joinsplit, full_api_test) "joinsplit not anchored to the correct root"); // input is in the tree now! this should work - invokeAPI(js, + invokeAPI(params, { JSInput(witnesses[1], note1, sk), JSInput() @@ -420,7 +417,7 @@ TEST(joinsplit, full_api_test) tree.root()); // Wrong secret key - invokeAPIFailure(js, + invokeAPIFailure(params, { JSInput(witnesses[1], note1, SpendingKey::random()), JSInput() @@ -435,7 +432,7 @@ TEST(joinsplit, full_api_test) "input note not authorized to spend with given key"); // Absurd input value - invokeAPIFailure(js, + invokeAPIFailure(params, { JSInput(witnesses[3], note3, sk), JSInput() @@ -450,7 +447,7 @@ TEST(joinsplit, full_api_test) "nonsensical input note value"); // Absurd total input value - invokeAPIFailure(js, + invokeAPIFailure(params, { JSInput(witnesses[4], note4, sk), JSInput(witnesses[5], note5, sk) @@ -465,7 +462,7 @@ TEST(joinsplit, full_api_test) "nonsensical left hand size of joinsplit balance"); // Absurd output value - invokeAPIFailure(js, + invokeAPIFailure(params, { JSInput(), JSInput() @@ -480,7 +477,7 @@ TEST(joinsplit, full_api_test) "nonsensical output value"); // Absurd total output value - invokeAPIFailure(js, + invokeAPIFailure(params, { JSInput(), JSInput() @@ -495,7 +492,7 @@ TEST(joinsplit, full_api_test) "nonsensical right hand side of joinsplit balance"); // Absurd total output value - invokeAPIFailure(js, + invokeAPIFailure(params, { JSInput(), JSInput() @@ -510,22 +507,7 @@ TEST(joinsplit, full_api_test) "invalid joinsplit balance"); } - test_full_api(js); - - js->saveProvingKey("./zcashTest.pk"); - js->saveVerifyingKey("./zcashTest.vk"); - - delete js; - - js = ZCJoinSplit::Unopened(); - - js->setProvingKeyPath("./zcashTest.pk"); - js->loadProvingKey(); - js->loadVerifyingKey("./zcashTest.vk"); - - test_full_api(js); - - delete js; + test_full_api(params); } TEST(joinsplit, note_plaintexts) diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index e94aea53e..76b57cd9f 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -43,7 +43,64 @@ TEST(keystore_tests, store_and_retrieve_note_decryptor) { keyStore.AddSpendingKey(sk); EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut)); - EXPECT_EQ(ZCNoteDecryption(sk.viewing_key()), decOut); + EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut); +} + +TEST(keystore_tests, StoreAndRetrieveViewingKey) { + CBasicKeyStore keyStore; + libzcash::ViewingKey vkOut; + libzcash::SpendingKey skOut; + ZCNoteDecryption decOut; + + auto sk = libzcash::SpendingKey::random(); + auto vk = sk.viewing_key(); + auto addr = sk.address(); + + // Sanity-check: we can't get a viewing key we haven't added + EXPECT_FALSE(keyStore.HaveViewingKey(addr)); + EXPECT_FALSE(keyStore.GetViewingKey(addr, vkOut)); + + // and we shouldn't have a spending key or decryptor either + EXPECT_FALSE(keyStore.HaveSpendingKey(addr)); + EXPECT_FALSE(keyStore.GetSpendingKey(addr, skOut)); + EXPECT_FALSE(keyStore.GetNoteDecryptor(addr, decOut)); + + // and we can't find it in our list of addresses + std::set addresses; + keyStore.GetPaymentAddresses(addresses); + EXPECT_FALSE(addresses.count(addr)); + + keyStore.AddViewingKey(vk); + EXPECT_TRUE(keyStore.HaveViewingKey(addr)); + EXPECT_TRUE(keyStore.GetViewingKey(addr, vkOut)); + EXPECT_EQ(vk, vkOut); + + // We should still not have the spending key... + EXPECT_FALSE(keyStore.HaveSpendingKey(addr)); + EXPECT_FALSE(keyStore.GetSpendingKey(addr, skOut)); + + // ... but we should have a decryptor + EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut)); + EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut); + + // ... and we should find it in our list of addresses + addresses.clear(); + keyStore.GetPaymentAddresses(addresses); + EXPECT_TRUE(addresses.count(addr)); + + keyStore.RemoveViewingKey(vk); + EXPECT_FALSE(keyStore.HaveViewingKey(addr)); + EXPECT_FALSE(keyStore.GetViewingKey(addr, vkOut)); + EXPECT_FALSE(keyStore.HaveSpendingKey(addr)); + EXPECT_FALSE(keyStore.GetSpendingKey(addr, skOut)); + addresses.clear(); + keyStore.GetPaymentAddresses(addresses); + EXPECT_FALSE(addresses.count(addr)); + + // We still have a decryptor because those are cached in memory + // (and also we only remove viewing keys when adding a spending key) + EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut)); + EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut); } #ifdef ENABLE_WALLET @@ -72,13 +129,13 @@ TEST(keystore_tests, store_and_retrieve_spending_key_in_encrypted_store) { ASSERT_TRUE(keyStore.GetSpendingKey(addr, keyOut)); ASSERT_EQ(sk, keyOut); EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut)); - EXPECT_EQ(ZCNoteDecryption(sk.viewing_key()), decOut); + EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut); ASSERT_TRUE(keyStore.EncryptKeys(vMasterKey)); ASSERT_TRUE(keyStore.HaveSpendingKey(addr)); ASSERT_FALSE(keyStore.GetSpendingKey(addr, keyOut)); EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut)); - EXPECT_EQ(ZCNoteDecryption(sk.viewing_key()), decOut); + EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut); // Unlocking with a random key should fail uint256 r2 {GetRandHash()}; @@ -109,19 +166,19 @@ TEST(keystore_tests, store_and_retrieve_spending_key_in_encrypted_store) { ASSERT_TRUE(keyStore.GetSpendingKey(addr2, keyOut)); ASSERT_EQ(sk2, keyOut); EXPECT_TRUE(keyStore.GetNoteDecryptor(addr2, decOut)); - EXPECT_EQ(ZCNoteDecryption(sk2.viewing_key()), decOut); + EXPECT_EQ(ZCNoteDecryption(sk2.receiving_key()), decOut); ASSERT_TRUE(keyStore.Lock()); ASSERT_TRUE(keyStore.HaveSpendingKey(addr2)); ASSERT_FALSE(keyStore.GetSpendingKey(addr2, keyOut)); EXPECT_TRUE(keyStore.GetNoteDecryptor(addr2, decOut)); - EXPECT_EQ(ZCNoteDecryption(sk2.viewing_key()), decOut); + EXPECT_EQ(ZCNoteDecryption(sk2.receiving_key()), decOut); ASSERT_TRUE(keyStore.Unlock(vMasterKey)); ASSERT_TRUE(keyStore.GetSpendingKey(addr2, keyOut)); ASSERT_EQ(sk2, keyOut); EXPECT_TRUE(keyStore.GetNoteDecryptor(addr2, decOut)); - EXPECT_EQ(ZCNoteDecryption(sk2.viewing_key()), decOut); + EXPECT_EQ(ZCNoteDecryption(sk2.receiving_key()), decOut); keyStore.GetPaymentAddresses(addrs); ASSERT_EQ(2, addrs.size()); diff --git a/src/gtest/test_mempool.cpp b/src/gtest/test_mempool.cpp index d73b99d11..9256f196a 100644 --- a/src/gtest/test_mempool.cpp +++ b/src/gtest/test_mempool.cpp @@ -1,89 +1,263 @@ #include #include - + +#include "consensus/upgrades.h" +#include "consensus/validation.h" #include "core_io.h" +#include "main.h" #include "primitives/transaction.h" #include "txmempool.h" #include "policy/fees.h" +#include "util.h" + +// Implementation is in test_checktransaction.cpp +extern CMutableTransaction GetValidTransaction(); + +// Fake the input of transaction 5295156213414ed77f6e538e7e8ebe14492156906b9fe995b242477818789364 +// - 532639cc6bebed47c1c69ae36dd498c68a012e74ad12729adbd3dbb56f8f3f4a, 0 +class FakeCoinsViewDB : public CCoinsView { +public: + FakeCoinsViewDB() {} + + bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { + return false; + } + + bool GetNullifier(const uint256 &nf) const { + return false; + } + + bool GetCoins(const uint256 &txid, CCoins &coins) const { + CTxOut txOut; + txOut.nValue = 4288035; + CCoins newCoins; + newCoins.vout.resize(2); + newCoins.vout[0] = txOut; + newCoins.nHeight = 92045; + coins.swap(newCoins); + return true; + } + + bool HaveCoins(const uint256 &txid) const { + return true; + } + + uint256 GetBestBlock() const { + uint256 a; + return a; + } + + uint256 GetBestAnchor() const { + uint256 a; + return a; + } + + bool BatchWrite(CCoinsMap &mapCoins, + const uint256 &hashBlock, + const uint256 &hashAnchor, + CAnchorsMap &mapAnchors, + CNullifiersMap &mapNullifiers) { + return false; + } + + bool GetStats(CCoinsStats &stats) const { + return false; + } +}; + +TEST(Mempool, PriorityStatsDoNotCrash) { + // Test for security issue 2017-04-11.a + // https://z.cash/blog/security-announcement-2017-04-12.html + + // Trigger transaction in block 92046 + std::string triggerTx = "02000000014a3f8f6fb5dbd3db9a7212ad742e018ac698d46de39ac6c147edeb6bcc392653000000006b483045022100da0514afd80d3bbd0743458efe3b2abd18f727b4268b124c3885094c26ea09cd02207d37d7934ec90618fc5a345cb2a6d1755d8b1a432ea1df517a85e36628449196012103e9b41072e9d2cbe04e6b22a6ac4862ec3f5a76b3823b071ded0dfd5455a0803fffffffff000000000001236e4100000000000000000000000000cf592f6776810cf9fb961d80e683f5529a6b34894b00446c396022512a02dc2e96918294bffdc988a2627d9b12a9f3176b671e286fc62e3c7441cf35ea5e03d561bd5817ca5827cb2761f88dde280a6da5281af2cc69053816f31abd2170722f72c258c7c6b865c97ff8ae53b697f3b77c239a73e1d0296c2c73d21c3b50059d82866cf9f6e2f5dbbf9b4de9caa3cf836080373311978e1f1b1150ce564336ebf0fd502c2e783ff23252fba4efbb110c28c4fbe31efb6a67bc24c0ad8fd8346c5c9ed1791b555b3e43a309aa8d855a294847368ebdf30365d4dfa9b25dd8ed7adf272ecc08f4756fb9d93b8e20d45548dd4aeab6abb76a2081b6e36a9af9d38ebae378df422f40589769c07a3c12e5da253330314cbc4beaa863fac7ab10055d0310089a44c45179a39b2c4e210cec2e053f54983d744abed49f66959f45785ea90325a310fba15f946e245a0e64caec303f2a3e1d457e3e3ca5d892956c1a93b05e0b0373cf862d7bbb5908148b475c03eec96c9b9ecc7d2d78716d2c2e8ccf96175b25738dfb5b0d356a7e30604ee014c6be1db5e43af0fa6ad3f4e917a9f84b4d6f030cad0ffe0738e1effe570b0552b407ca9c26023b74b99f431cc28b79116f717703158404e343b1b47a0556f593441dc64758930f19e84d5ee468fd9a7958c6c63503054f60680f7147e88bf6da65415450230ef7437481023fc5d94872d5aa18bf3b0212b4c0d938e6c84debb8a4e65f99970c59193873a72b2440f19a652073abd960021bfef4e1e52b8f353c6e517bb97053afd4c8035defc27c3fd16faba5bc924a4179f69cfdcdb82253b5f6472a99d4b78ad2c6c18c45ed4dda5bf2adc019c99b55702f4e7b3fcaeb6f3b84ad411d36e901cba9d49ac1d6b916aa88794fb23501aeb0c585cbc2bed952846f41a03bd5c74dfe004e7ac21f7a20d32b009ccf6f70b3e577d25c679421225522b6290d5fa00a5d9a02b97a62aab60e040a03efa946d87c5e65dbf10d66df5b0834c262c31c23f3c2643451e614695003fb3a95bf21444bebb45cdcb8169245e34a76f754c89c3a90f36598a71ef4645eef4c82f1fb322536097fcf0cbe061e80ae887dbb88d8ed910be9ef18b8794930addab1a140b16c4b50f93926b1e5df03ee6e4b5ec6d7f0ed49fbbae50330ae94c5ae9182f4b58870022e423e7d80adccdb90680f7a7fe11a4ed4fe005a0af2d22bf9e7d1bef7caf4f37f5777e4aa6c9b9ea44f5973575c20fb3482fe357c19fc0c20594f492f5694e3e8eb3599e968fd23b5bdd6c4bf5aee1374b38aafe59dd5af83011e642a9427b5ff03e7a4cce92ee201a0fac0acb69d6ad3b7e4c26dfefaa53a737889e759c4b5695c1a7fd5d988e531acf66dae5067f252a25a102d92916b2d84c730645e15a78d3dce1c787634f6f7323cb949a5b6ad004e208cb8c6b734761629c13b9974dc80b082f83357f3bc703d835acbbf72aba225ffe69396c151d2646fac9bd1acc184dd047ebfaadc6b60a9185ce80c7bc8ac5dbb2219cbc0d35af91673b95d28335f0ee2774b8084871d54ca8eab3a285e4b4adf3f34b4263d67474bb5de2e1e37aa7a4ecbd5b49575caaa9e7208c2b9871946b11f2c54cd1ca7660dff44cf206e7da46ab57dd49ec0aa06ded7980f1557cc7c84023690b4df77f26d6b4eff7553b9a8628c28e8e5c38c6170bc61af0969b072586fa740f68ab33c0f62d0507cc8fe41c680b2f077e49cc2691048006311b46cd5ed18e63089f11c115b797ed5fbcd86836a4da2ab90a00745077f2f13bc9e390fc2f92b941d4fb70a3f098548953566141670317fc17e0ba81e98b8a94919992fb008c5480f4018f3a1ea673fe94a6ba3363656a944855d7c799ccb73d95d4ed6ce04c26f79c4cf79f883f0f810519f28eabe8cf6d833f24227f5074763c7b80f1431e5463cc07eff2f1d6cbfaf0411d38e62528a39b093ed7b51fa8c1008e5c1ce4bf39e67b1703554cefef44b71457bfddf43d23a54fa0145fa0e716d02a5304d85345a2b4ebf98c5010d0df468c8cbfc2db22083b0f5a74d4324ee74b46daa5ab70f2575ef5390e6aa2acb8d3b3eb2065e8c06fd6276aca283f5850e7a8b4da37455430df55621e4af59bb355ba2db0ac6cae6ceed2f538ec8c928ee895bd190fd9c1dff4956bad27d567023bc847dd64d83bac399f8d10248a077c9b2f50d5dda4789e09eae4ed8609da085b6370f6529f3c7b8b13442f9a1cc93565734a81c38c6360235ba23ddf87d1b44413c24b363552a322b01d88d1cae6c5ccd88f8cef15776035934fd24adce913282983d9b55181b382ed61242fb4a5e459c3cad8d38626ef8ccdccb9899d5962dec3f3cc2422f109d902e764186cf166ed88c383f428f195dd5fec4f781bcd2308dec66927f41c9e06369c6806ed8ec9a59c096b29b2a74dc85f4f7cd77a23650c0662b5c2602ef5e41cdc13d16074500aac461f823e3bba7178bcffa000db4ffc9b1618395824ffbee1cad1d9c138a20e0b8bbea2d9a07fade932f81c3daf2d64c4991daf4a1b8b531f9b958a252c6c38cd463342aef3e03e3dae3370581d6cddf5af3ef1585780cf83db1909a1daca156018cd2f7483e53a5fccda49640de60b24523617c7ae84ec5fa987ba8a108"; + CTransaction tx; + ASSERT_TRUE(DecodeHexTx(tx, triggerTx)); + ASSERT_EQ(tx.GetHash().GetHex(), "5295156213414ed77f6e538e7e8ebe14492156906b9fe995b242477818789364"); + + // Fake its inputs + FakeCoinsViewDB fakeDB; + CCoinsViewCache view(&fakeDB); + + CTxMemPool testPool(CFeeRate(0)); + + // Values taken from core dump (parameters of entry) + CAmount nFees = 0; + int64_t nTime = 0x58e5fed9; + unsigned int nHeight = 92045; + double dPriority = view.GetPriority(tx, nHeight); + + CTxMemPoolEntry entry(tx, nFees, nTime, dPriority, nHeight, true, false, SPROUT_BRANCH_ID); + + // Check it does not crash (ie. the death test fails) + EXPECT_NONFATAL_FAILURE(EXPECT_DEATH(testPool.addUnchecked(tx.GetHash(), entry), ""), ""); - // Fake the input of transaction 5295156213414ed77f6e538e7e8ebe14492156906b9fe995b242477818789364 - // - 532639cc6bebed47c1c69ae36dd498c68a012e74ad12729adbd3dbb56f8f3f4a, 0 - class FakeCoinsViewDB : public CCoinsView { - public: - FakeCoinsViewDB() {} - - bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { - return false; - } - - bool GetNullifier(const uint256 &nf) const { - return false; - } - - bool GetCoins(const uint256 &txid, CCoins &coins) const { - CTxOut txOut; - txOut.nValue = 4288035; - CCoins newCoins; - newCoins.vout.resize(2); - newCoins.vout[0] = txOut; - newCoins.nHeight = 92045; - coins.swap(newCoins); - return true; - } - - bool HaveCoins(const uint256 &txid) const { - return true; - } - - uint256 GetBestBlock() const { - uint256 a; - return a; - } - - uint256 GetBestAnchor() const { - uint256 a; - return a; - } - - bool BatchWrite(CCoinsMap &mapCoins, - const uint256 &hashBlock, - const uint256 &hashAnchor, - CAnchorsMap &mapAnchors, - CNullifiersMap &mapNullifiers) { - return false; - } - - bool GetStats(CCoinsStats &stats) const { - return false; - } - }; - - TEST(Mempool, PriorityStatsDoNotCrash) { - // Test for security issue 2017-04-11.a - // https://z.cash/blog/security-announcement-2017-04-12.html - - // Trigger transaction in block 92046 - std::string triggerTx = "02000000014a3f8f6fb5dbd3db9a7212ad742e018ac698d46de39ac6c147edeb6bcc392653000000006b483045022100da0514afd80d3bbd0743458efe3b2abd18f727b4268b124c3885094c26ea09cd02207d37d7934ec90618fc5a345cb2a6d1755d8b1a432ea1df517a85e36628449196012103e9b41072e9d2cbe04e6b22a6ac4862ec3f5a76b3823b071ded0dfd5455a0803fffffffff000000000001236e4100000000000000000000000000cf592f6776810cf9fb961d80e683f5529a6b34894b00446c396022512a02dc2e96918294bffdc988a2627d9b12a9f3176b671e286fc62e3c7441cf35ea5e03d561bd5817ca5827cb2761f88dde280a6da5281af2cc69053816f31abd2170722f72c258c7c6b865c97ff8ae53b697f3b77c239a73e1d0296c2c73d21c3b50059d82866cf9f6e2f5dbbf9b4de9caa3cf836080373311978e1f1b1150ce564336ebf0fd502c2e783ff23252fba4efbb110c28c4fbe31efb6a67bc24c0ad8fd8346c5c9ed1791b555b3e43a309aa8d855a294847368ebdf30365d4dfa9b25dd8ed7adf272ecc08f4756fb9d93b8e20d45548dd4aeab6abb76a2081b6e36a9af9d38ebae378df422f40589769c07a3c12e5da253330314cbc4beaa863fac7ab10055d0310089a44c45179a39b2c4e210cec2e053f54983d744abed49f66959f45785ea90325a310fba15f946e245a0e64caec303f2a3e1d457e3e3ca5d892956c1a93b05e0b0373cf862d7bbb5908148b475c03eec96c9b9ecc7d2d78716d2c2e8ccf96175b25738dfb5b0d356a7e30604ee014c6be1db5e43af0fa6ad3f4e917a9f84b4d6f030cad0ffe0738e1effe570b0552b407ca9c26023b74b99f431cc28b79116f717703158404e343b1b47a0556f593441dc64758930f19e84d5ee468fd9a7958c6c63503054f60680f7147e88bf6da65415450230ef7437481023fc5d94872d5aa18bf3b0212b4c0d938e6c84debb8a4e65f99970c59193873a72b2440f19a652073abd960021bfef4e1e52b8f353c6e517bb97053afd4c8035defc27c3fd16faba5bc924a4179f69cfdcdb82253b5f6472a99d4b78ad2c6c18c45ed4dda5bf2adc019c99b55702f4e7b3fcaeb6f3b84ad411d36e901cba9d49ac1d6b916aa88794fb23501aeb0c585cbc2bed952846f41a03bd5c74dfe004e7ac21f7a20d32b009ccf6f70b3e577d25c679421225522b6290d5fa00a5d9a02b97a62aab60e040a03efa946d87c5e65dbf10d66df5b0834c262c31c23f3c2643451e614695003fb3a95bf21444bebb45cdcb8169245e34a76f754c89c3a90f36598a71ef4645eef4c82f1fb322536097fcf0cbe061e80ae887dbb88d8ed910be9ef18b8794930addab1a140b16c4b50f93926b1e5df03ee6e4b5ec6d7f0ed49fbbae50330ae94c5ae9182f4b58870022e423e7d80adccdb90680f7a7fe11a4ed4fe005a0af2d22bf9e7d1bef7caf4f37f5777e4aa6c9b9ea44f5973575c20fb3482fe357c19fc0c20594f492f5694e3e8eb3599e968fd23b5bdd6c4bf5aee1374b38aafe59dd5af83011e642a9427b5ff03e7a4cce92ee201a0fac0acb69d6ad3b7e4c26dfefaa53a737889e759c4b5695c1a7fd5d988e531acf66dae5067f252a25a102d92916b2d84c730645e15a78d3dce1c787634f6f7323cb949a5b6ad004e208cb8c6b734761629c13b9974dc80b082f83357f3bc703d835acbbf72aba225ffe69396c151d2646fac9bd1acc184dd047ebfaadc6b60a9185ce80c7bc8ac5dbb2219cbc0d35af91673b95d28335f0ee2774b8084871d54ca8eab3a285e4b4adf3f34b4263d67474bb5de2e1e37aa7a4ecbd5b49575caaa9e7208c2b9871946b11f2c54cd1ca7660dff44cf206e7da46ab57dd49ec0aa06ded7980f1557cc7c84023690b4df77f26d6b4eff7553b9a8628c28e8e5c38c6170bc61af0969b072586fa740f68ab33c0f62d0507cc8fe41c680b2f077e49cc2691048006311b46cd5ed18e63089f11c115b797ed5fbcd86836a4da2ab90a00745077f2f13bc9e390fc2f92b941d4fb70a3f098548953566141670317fc17e0ba81e98b8a94919992fb008c5480f4018f3a1ea673fe94a6ba3363656a944855d7c799ccb73d95d4ed6ce04c26f79c4cf79f883f0f810519f28eabe8cf6d833f24227f5074763c7b80f1431e5463cc07eff2f1d6cbfaf0411d38e62528a39b093ed7b51fa8c1008e5c1ce4bf39e67b1703554cefef44b71457bfddf43d23a54fa0145fa0e716d02a5304d85345a2b4ebf98c5010d0df468c8cbfc2db22083b0f5a74d4324ee74b46daa5ab70f2575ef5390e6aa2acb8d3b3eb2065e8c06fd6276aca283f5850e7a8b4da37455430df55621e4af59bb355ba2db0ac6cae6ceed2f538ec8c928ee895bd190fd9c1dff4956bad27d567023bc847dd64d83bac399f8d10248a077c9b2f50d5dda4789e09eae4ed8609da085b6370f6529f3c7b8b13442f9a1cc93565734a81c38c6360235ba23ddf87d1b44413c24b363552a322b01d88d1cae6c5ccd88f8cef15776035934fd24adce913282983d9b55181b382ed61242fb4a5e459c3cad8d38626ef8ccdccb9899d5962dec3f3cc2422f109d902e764186cf166ed88c383f428f195dd5fec4f781bcd2308dec66927f41c9e06369c6806ed8ec9a59c096b29b2a74dc85f4f7cd77a23650c0662b5c2602ef5e41cdc13d16074500aac461f823e3bba7178bcffa000db4ffc9b1618395824ffbee1cad1d9c138a20e0b8bbea2d9a07fade932f81c3daf2d64c4991daf4a1b8b531f9b958a252c6c38cd463342aef3e03e3dae3370581d6cddf5af3ef1585780cf83db1909a1daca156018cd2f7483e53a5fccda49640de60b24523617c7ae84ec5fa987ba8a108"; - CTransaction tx; - ASSERT_TRUE(DecodeHexTx(tx, triggerTx)); - ASSERT_EQ(tx.GetHash().GetHex(), "5295156213414ed77f6e538e7e8ebe14492156906b9fe995b242477818789364"); - - // Fake its inputs - FakeCoinsViewDB fakeDB; - CCoinsViewCache view(&fakeDB); - - CTxMemPool testPool(CFeeRate(0)); - - // Values taken from core dump (parameters of entry) - CAmount nFees = 0; - int64_t nTime = 0x58e5fed9; - unsigned int nHeight = 92045; - double dPriority = view.GetPriority(tx, nHeight); - - CTxMemPoolEntry entry(tx, nFees, nTime, dPriority, nHeight, true); - - // 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); } +TEST(Mempool, TxInputLimit) { + SelectParams(CBaseChainParams::TESTNET); + + CTxMemPool pool(::minRelayTxFee); + bool missingInputs; + + // Create an obviously-invalid transaction + // We intentionally set tx.nVersion = 0 to reliably trigger an error, as + // it's the first check that occurs after the -mempooltxinputlimit check, + // and it means that we don't have to mock out a lot of global state. + CMutableTransaction mtx; + mtx.nVersion = 0; + mtx.vin.resize(10); + + // Check it fails as expected + CValidationState state1; + CTransaction tx1(mtx); + EXPECT_FALSE(AcceptToMemoryPool(pool, state1, tx1, false, &missingInputs)); + EXPECT_EQ(state1.GetRejectReason(), "bad-txns-version-too-low"); + + // Set a limit + mapArgs["-mempooltxinputlimit"] = "10"; + + // Check it still fails as expected + CValidationState state2; + EXPECT_FALSE(AcceptToMemoryPool(pool, state2, tx1, false, &missingInputs)); + EXPECT_EQ(state2.GetRejectReason(), "bad-txns-version-too-low"); + + // Resize the transaction + mtx.vin.resize(11); + + // Check it now fails due to exceeding the limit + CValidationState state3; + CTransaction tx3(mtx); + EXPECT_FALSE(AcceptToMemoryPool(pool, state3, tx3, false, &missingInputs)); + // The -mempooltxinputlimit check doesn't set a reason + EXPECT_EQ(state3.GetRejectReason(), ""); + + // Clear the limit + mapArgs.erase("-mempooltxinputlimit"); + + // Check it no longer fails due to exceeding the limit + CValidationState state4; + EXPECT_FALSE(AcceptToMemoryPool(pool, state4, tx3, false, &missingInputs)); + EXPECT_EQ(state4.GetRejectReason(), "bad-txns-version-too-low"); +} + +// Valid overwinter v3 format tx gets rejected because overwinter hasn't activated yet. +TEST(Mempool, OverwinterNotActiveYet) { + SelectParams(CBaseChainParams::REGTEST); + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); + + CTxMemPool pool(::minRelayTxFee); + bool missingInputs; + CMutableTransaction mtx = GetValidTransaction(); + mtx.vjoinsplit.resize(0); // no joinsplits + mtx.fOverwintered = true; + mtx.nVersion = 3; + mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID; + mtx.nExpiryHeight = 0; + CValidationState state1; + + CTransaction tx1(mtx); + EXPECT_FALSE(AcceptToMemoryPool(pool, state1, tx1, false, &missingInputs)); + EXPECT_EQ(state1.GetRejectReason(), "tx-overwinter-not-active"); + + // Revert to default + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); +} + + +// Sprout transaction version 3 when Overwinter is not active: +// 1. pass CheckTransaction (and CheckTransactionWithoutProofVerification) +// 2. pass ContextualCheckTransaction +// 3. fail IsStandardTx +TEST(Mempool, SproutV3TxFailsAsExpected) { + SelectParams(CBaseChainParams::TESTNET); + + CTxMemPool pool(::minRelayTxFee); + bool missingInputs; + CMutableTransaction mtx = GetValidTransaction(); + mtx.vjoinsplit.resize(0); // no joinsplits + mtx.fOverwintered = false; + mtx.nVersion = 3; + CValidationState state1; + CTransaction tx1(mtx); + + EXPECT_FALSE(AcceptToMemoryPool(pool, state1, tx1, false, &missingInputs)); + EXPECT_EQ(state1.GetRejectReason(), "version"); +} + + +// Sprout transaction version 3 when Overwinter is always active: +// 1. pass CheckTransaction (and CheckTransactionWithoutProofVerification) +// 2. fails ContextualCheckTransaction +TEST(Mempool, SproutV3TxWhenOverwinterActive) { + SelectParams(CBaseChainParams::REGTEST); + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); + + CTxMemPool pool(::minRelayTxFee); + bool missingInputs; + CMutableTransaction mtx = GetValidTransaction(); + mtx.vjoinsplit.resize(0); // no joinsplits + mtx.fOverwintered = false; + mtx.nVersion = 3; + CValidationState state1; + CTransaction tx1(mtx); + + EXPECT_FALSE(AcceptToMemoryPool(pool, state1, tx1, false, &missingInputs)); + EXPECT_EQ(state1.GetRejectReason(), "tx-overwinter-flag-not-set"); + + // Revert to default + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); +} + + +// Sprout transaction with negative version, rejected by the mempool in CheckTransaction +// under Sprout consensus rules, should still be rejected under Overwinter consensus rules. +// 1. fails CheckTransaction (specifically CheckTransactionWithoutProofVerification) +TEST(Mempool, SproutNegativeVersionTxWhenOverwinterActive) { + SelectParams(CBaseChainParams::REGTEST); + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); + + CTxMemPool pool(::minRelayTxFee); + bool missingInputs; + CMutableTransaction mtx = GetValidTransaction(); + mtx.vjoinsplit.resize(0); // no joinsplits + mtx.fOverwintered = false; + + // A Sprout transaction with version -3 is created using Sprout code (as found in Zcashd <= 1.0.14). + // First four bytes of transaction, parsed as an uint32_t, has the value: 0xfffffffd + // This test simulates an Overwinter node receiving this transaction, but incorrectly deserializing the + // transaction due to a (pretend) bug of not detecting the most significant bit, which leads + // to not setting fOverwintered and not masking off the most significant bit of the header field. + // The resulting Sprout tx with nVersion -3 should be rejected by the Overwinter node's mempool. + { + mtx.nVersion = -3; + EXPECT_EQ(mtx.nVersion, static_cast(0xfffffffd)); + + CTransaction tx1(mtx); + EXPECT_EQ(tx1.nVersion, -3); + + CValidationState state1; + EXPECT_FALSE(AcceptToMemoryPool(pool, state1, tx1, false, &missingInputs)); + EXPECT_EQ(state1.GetRejectReason(), "bad-txns-version-too-low"); + } + + // A Sprout transaction with version -3 created using Overwinter code (as found in Zcashd >= 1.0.15). + // First four bytes of transaction, parsed as an uint32_t, has the value: 0x80000003 + // This test simulates the same pretend bug described above. + // The resulting Sprout tx with nVersion -2147483645 should be rejected by the Overwinter node's mempool. + { + mtx.nVersion = static_cast((1 << 31) | 3); + EXPECT_EQ(mtx.nVersion, static_cast(0x80000003)); + + CTransaction tx1(mtx); + EXPECT_EQ(tx1.nVersion, -2147483645); + + CValidationState state1; + EXPECT_FALSE(AcceptToMemoryPool(pool, state1, tx1, false, &missingInputs)); + EXPECT_EQ(state1.GetRejectReason(), "bad-txns-version-too-low"); + } + + // Revert to default + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); +} diff --git a/src/gtest/test_merkletree.cpp b/src/gtest/test_merkletree.cpp index 6bac9ab3c..d603b0aa6 100644 --- a/src/gtest/test_merkletree.cpp +++ b/src/gtest/test_merkletree.cpp @@ -19,10 +19,10 @@ #include "zcash/IncrementalMerkleTree.hpp" #include "zcash/util.h" -#include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp" -#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp" -#include "libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp" -#include "libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.hpp" +#include +#include +#include +#include #include diff --git a/src/gtest/test_metrics.cpp b/src/gtest/test_metrics.cpp index c199b323e..143fe46d6 100644 --- a/src/gtest/test_metrics.cpp +++ b/src/gtest/test_metrics.cpp @@ -92,3 +92,44 @@ TEST(Metrics, GetLocalSolPS) { SetMockTime(104); EXPECT_EQ(1, GetLocalSolPS()); } + +TEST(Metrics, EstimateNetHeightInner) { + // Ensure that the (rounded) current height is returned if the tip is current + SetMockTime(15000); + EXPECT_EQ(100, EstimateNetHeightInner(100, 14100, 50, 7500, 0, 150)); + SetMockTime(15150); + EXPECT_EQ(100, EstimateNetHeightInner(101, 14250, 50, 7500, 0, 150)); + + // Ensure that correct estimates are returned if the tip is in the past + SetMockTime(15300); // Tip is 2 blocks behind + EXPECT_EQ(100, EstimateNetHeightInner(100, 14100, 50, 7500, 0, 150)); + SetMockTime(15900); // Tip is 6 blocks behind + EXPECT_EQ(110, EstimateNetHeightInner(100, 14100, 50, 7500, 0, 150)); + + // Check estimates during resync + SetMockTime(15000); + EXPECT_EQ(100, EstimateNetHeightInner( 0, 0, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner( 7, 600, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner( 8, 600, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(10, 750, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(11, 900, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(20, 2100, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(49, 6450, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(50, 6600, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(51, 6750, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(55, 7350, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(56, 7500, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(57, 7650, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(75, 10350, 50, 7500, 0, 150)); + + // More complex calculations: + SetMockTime(20000); + // - Checkpoint spacing: 200 + // -> Average spacing: 175 + // -> estimated height: 127 -> 130 + EXPECT_EQ(130, EstimateNetHeightInner(100, 14100, 50, 5250, 0, 150)); + // - Checkpoint spacing: 50 + // -> Average spacing: 100 + // -> estimated height: 153 -> 150 + EXPECT_EQ(150, EstimateNetHeightInner(100, 14100, 50, 12000, 0, 150)); +} diff --git a/src/gtest/test_paymentdisclosure.cpp b/src/gtest/test_paymentdisclosure.cpp new file mode 100644 index 000000000..ddab3c7e6 --- /dev/null +++ b/src/gtest/test_paymentdisclosure.cpp @@ -0,0 +1,212 @@ +#include + +#include "main.h" +#include "utilmoneystr.h" +#include "chainparams.h" +#include "utilstrencodings.h" +#include "zcash/Address.hpp" +#include "wallet/wallet.h" +#include "amount.h" +#include +#include +#include +#include +#include +#include +#include "util.h" + +#include "paymentdisclosure.h" +#include "paymentdisclosuredb.h" + +#include "sodium.h" + +#include +#include +#include + +using namespace std; + +/* + To run tests: + ./zcash-gtest --gtest_filter="paymentdisclosure.*" + + Note: As an experimental feature, writing your own tests may require option flags to be set. + mapArgs["-experimentalfeatures"] = true; + mapArgs["-paymentdisclosure"] = true; +*/ + +#define NUM_TRIES 10000 + +#define DUMP_DATABASE_TO_STDOUT false + +static boost::uuids::random_generator uuidgen; + +static uint256 random_uint256() +{ + uint256 ret; + randombytes_buf(ret.begin(), 32); + return ret; +} + +// Subclass of PaymentDisclosureDB to add debugging methods +class PaymentDisclosureDBTest : public PaymentDisclosureDB { +public: + PaymentDisclosureDBTest(const boost::filesystem::path& dbPath) : PaymentDisclosureDB(dbPath) {} + + void DebugDumpAllStdout() { + ASSERT_NE(db, nullptr); + std::lock_guard guard(lock_); + + // Iterate over each item in the database and print them + leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions()); + + for (it->SeekToFirst(); it->Valid(); it->Next()) { + cout << it->key().ToString() << " : "; + // << it->value().ToString() << endl; + try { + std::string strValue = it->value().ToString(); + PaymentDisclosureInfo info; + CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION); + ssValue >> info; + cout << info.ToString() << std::endl; + } catch (const std::exception& e) { + cout << e.what() << std::endl; + } + } + + if (false == it->status().ok()) { + cerr << "An error was found iterating over the database" << endl; + cerr << it->status().ToString() << endl; + } + + delete it; + } +}; + + + +// This test creates random payment disclosure blobs and checks that they can be +// 1. inserted and retrieved from a database +// 2. serialized and deserialized without corruption +// Note that the zpd: prefix is not part of the payment disclosure blob itself. It is only +// used as convention to improve the user experience when sharing payment disclosure blobs. +TEST(paymentdisclosure, mainnet) { + ECC_Start(); + SelectParams(CBaseChainParams::MAIN); + + boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); + boost::filesystem::create_directories(pathTemp); + mapArgs["-datadir"] = pathTemp.string(); + + std::cout << "Test payment disclosure database created in folder: " << pathTemp.native() << std::endl; + + PaymentDisclosureDBTest mydb(pathTemp); + + for (int i=0; i vch(&buffer[0], &buffer[0] + 32); + uint256 joinSplitPrivKey = uint256(vch); + + // Create payment disclosure key and info data to store in test database + size_t js = random_uint256().GetCheapHash() % std::numeric_limits::max(); + uint8_t n = random_uint256().GetCheapHash() % std::numeric_limits::max(); + PaymentDisclosureKey key { random_uint256(), js, n}; + PaymentDisclosureInfo info; + info.esk = random_uint256(); + info.joinSplitPrivKey = joinSplitPrivKey; + info.zaddr = libzcash::SpendingKey::random().address(); + ASSERT_TRUE(mydb.Put(key, info)); + + // Retrieve info from test database into new local variable and test it matches + PaymentDisclosureInfo info2; + ASSERT_TRUE(mydb.Get(key, info2)); + ASSERT_EQ(info, info2); + + // Modify this local variable and confirm it no longer matches + info2.esk = random_uint256(); + info2.joinSplitPrivKey = random_uint256(); + info2.zaddr = libzcash::SpendingKey::random().address(); + ASSERT_NE(info, info2); + + // Using the payment info object, let's create a dummy payload + PaymentDisclosurePayload payload; + payload.version = PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL; + payload.esk = info.esk; + payload.txid = key.hash; + payload.js = key.js; + payload.n = key.n; + payload.message = "random-" + boost::uuids::to_string(uuidgen()); // random message + payload.zaddr = info.zaddr; + + // Serialize and hash the payload to generate a signature + uint256 dataToBeSigned = SerializeHash(payload, SER_GETHASH, 0); + + // Compute the payload signature + unsigned char payloadSig[64]; + if (!(crypto_sign_detached(&payloadSig[0], NULL, + dataToBeSigned.begin(), 32, + &buffer[0] // buffer containing both private and public key required + ) == 0)) + { + throw std::runtime_error("crypto_sign_detached failed"); + } + + // Sanity check + if (!(crypto_sign_verify_detached(&payloadSig[0], + dataToBeSigned.begin(), 32, + joinSplitPubKey.begin() + ) == 0)) + { + throw std::runtime_error("crypto_sign_verify_detached failed"); + } + + // Convert signature buffer to boost array + boost::array arrayPayloadSig; + memcpy(arrayPayloadSig.data(), &payloadSig[0], 64); + + // Payment disclosure blob to pass around + PaymentDisclosure pd = {payload, arrayPayloadSig}; + + // Test payment disclosure constructors + PaymentDisclosure pd2(payload, arrayPayloadSig); + ASSERT_EQ(pd, pd2); + PaymentDisclosure pd3(joinSplitPubKey, key, info, payload.message); + ASSERT_EQ(pd, pd3); + + // Verify serialization and deserialization works + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << pd; + std::string ssHexString = HexStr(ss.begin(), ss.end()); + + PaymentDisclosure pdTmp; + CDataStream ssTmp(ParseHex(ssHexString), SER_NETWORK, PROTOCOL_VERSION); + ssTmp >> pdTmp; + ASSERT_EQ(pd, pdTmp); + + CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); + ss2 << pdTmp; + std::string ss2HexString = HexStr(ss2.begin(), ss2.end()); + ASSERT_EQ(ssHexString, ss2HexString); + + // Verify marker + ASSERT_EQ(pd.payload.marker, PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES); + ASSERT_EQ(pdTmp.payload.marker, PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES); + ASSERT_EQ(0, ssHexString.find("706462ff")); // Little endian encoding of PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES value + + // Sanity check + PaymentDisclosure pdDummy; + ASSERT_NE(pd, pdDummy); + } + +#if DUMP_DATABASE_TO_STDOUT == true + mydb.DebugDumpAllStdout(); +#endif + + ECC_Stop(); +} diff --git a/src/gtest/test_proofs.cpp b/src/gtest/test_proofs.cpp index 49202f1f6..e33b1cc0c 100644 --- a/src/gtest/test_proofs.cpp +++ b/src/gtest/test_proofs.cpp @@ -3,10 +3,9 @@ #include -#include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp" -#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp" -#include "zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp" -#include "relations/constraint_satisfaction_problems/r1cs/examples/r1cs_examples.hpp" +#include +#include +#include using namespace libzcash; @@ -22,6 +21,51 @@ typedef libsnark::default_r1cs_ppzksnark_pp::Fqe_type curve_Fq2; #include "version.h" #include "utilstrencodings.h" +TEST(proofs, g1_pairing_at_infinity) +{ + for (size_t i = 0; i < 100; i++) { + auto r1 = curve_G1::random_element(); + auto r2 = curve_G2::random_element(); + ASSERT_TRUE( + curve_pp::reduced_pairing(curve_G1::zero(), r2) == + curve_GT::one() + ); + ASSERT_TRUE( + curve_pp::final_exponentiation( + curve_pp::double_miller_loop( + curve_pp::precompute_G1(curve_G1::zero()), + curve_pp::precompute_G2(r2), + curve_pp::precompute_G1(curve_G1::zero()), + curve_pp::precompute_G2(r2) + ) + ) == + curve_GT::one() + ); + ASSERT_TRUE( + curve_pp::final_exponentiation( + curve_pp::double_miller_loop( + curve_pp::precompute_G1(r1), + curve_pp::precompute_G2(r2), + curve_pp::precompute_G1(curve_G1::zero()), + curve_pp::precompute_G2(r2) + ) + ) == + curve_pp::reduced_pairing(r1, r2) + ); + ASSERT_TRUE( + curve_pp::final_exponentiation( + curve_pp::double_miller_loop( + curve_pp::precompute_G1(curve_G1::zero()), + curve_pp::precompute_G2(r2), + curve_pp::precompute_G1(r1), + curve_pp::precompute_G2(r2) + ) + ) == + curve_pp::reduced_pairing(r1, r2) + ); + } +} + TEST(proofs, g2_subgroup_check) { // all G2 elements are order r diff --git a/src/gtest/test_transaction.cpp b/src/gtest/test_transaction.cpp index e75588685..cf2394438 100644 --- a/src/gtest/test_transaction.cpp +++ b/src/gtest/test_transaction.cpp @@ -77,7 +77,7 @@ TEST(Transaction, JSDescriptionRandomized) { *params, pubKeyHash, rt, inputs, outputs, inputMap, outputMap, - 0, 0, false, GenZero); + 0, 0, false, nullptr, GenZero); #ifdef __LP64__ // required for building on MacOS boost::array expectedInputMap {1, 0}; @@ -95,7 +95,7 @@ TEST(Transaction, JSDescriptionRandomized) { *params, pubKeyHash, rt, inputs, outputs, inputMap, outputMap, - 0, 0, false, GenMax); + 0, 0, false, nullptr, GenMax); #ifdef __LP64__ // required for building on MacOS boost::array expectedInputMap {0, 1}; diff --git a/src/gtest/test_upgrades.cpp b/src/gtest/test_upgrades.cpp new file mode 100644 index 000000000..1066a28d0 --- /dev/null +++ b/src/gtest/test_upgrades.cpp @@ -0,0 +1,173 @@ +#include + +#include "chainparams.h" +#include "consensus/upgrades.h" + +#include + +class UpgradesTest : public ::testing::Test { +protected: + virtual void SetUp() { + } + + virtual void TearDown() { + // Revert to default + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); + } +}; + +TEST_F(UpgradesTest, NetworkUpgradeState) { + SelectParams(CBaseChainParams::REGTEST); + const Consensus::Params& params = Params().GetConsensus(); + + // Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT + EXPECT_EQ( + NetworkUpgradeState(0, params, Consensus::UPGRADE_TESTDUMMY), + UPGRADE_DISABLED); + EXPECT_EQ( + NetworkUpgradeState(1000000, params, Consensus::UPGRADE_TESTDUMMY), + UPGRADE_DISABLED); + + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); + + EXPECT_EQ( + NetworkUpgradeState(0, params, Consensus::UPGRADE_TESTDUMMY), + UPGRADE_ACTIVE); + EXPECT_EQ( + NetworkUpgradeState(1000000, params, Consensus::UPGRADE_TESTDUMMY), + UPGRADE_ACTIVE); + + int nActivationHeight = 100; + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, nActivationHeight); + + EXPECT_EQ( + NetworkUpgradeState(0, params, Consensus::UPGRADE_TESTDUMMY), + UPGRADE_PENDING); + EXPECT_EQ( + NetworkUpgradeState(nActivationHeight - 1, params, Consensus::UPGRADE_TESTDUMMY), + UPGRADE_PENDING); + EXPECT_EQ( + NetworkUpgradeState(nActivationHeight, params, Consensus::UPGRADE_TESTDUMMY), + UPGRADE_ACTIVE); + EXPECT_EQ( + NetworkUpgradeState(1000000, params, Consensus::UPGRADE_TESTDUMMY), + UPGRADE_ACTIVE); +} + +TEST_F(UpgradesTest, CurrentEpoch) { + SelectParams(CBaseChainParams::REGTEST); + const Consensus::Params& params = Params().GetConsensus(); + auto nBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_TESTDUMMY].nBranchId; + + // Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT + EXPECT_EQ(CurrentEpoch(0, params), Consensus::BASE_SPROUT); + EXPECT_EQ(CurrentEpochBranchId(0, params), 0); + EXPECT_EQ(CurrentEpoch(1000000, params), Consensus::BASE_SPROUT); + EXPECT_EQ(CurrentEpochBranchId(1000000, params), 0); + + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); + + EXPECT_EQ(CurrentEpoch(0, params), Consensus::UPGRADE_TESTDUMMY); + EXPECT_EQ(CurrentEpochBranchId(0, params), nBranchId); + EXPECT_EQ(CurrentEpoch(1000000, params), Consensus::UPGRADE_TESTDUMMY); + EXPECT_EQ(CurrentEpochBranchId(1000000, params), nBranchId); + + int nActivationHeight = 100; + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, nActivationHeight); + + EXPECT_EQ(CurrentEpoch(0, params), Consensus::BASE_SPROUT); + EXPECT_EQ(CurrentEpochBranchId(0, params), 0); + EXPECT_EQ(CurrentEpoch(nActivationHeight - 1, params), Consensus::BASE_SPROUT); + EXPECT_EQ(CurrentEpochBranchId(nActivationHeight - 1, params), 0); + EXPECT_EQ(CurrentEpoch(nActivationHeight, params), Consensus::UPGRADE_TESTDUMMY); + EXPECT_EQ(CurrentEpochBranchId(nActivationHeight, params), nBranchId); + EXPECT_EQ(CurrentEpoch(1000000, params), Consensus::UPGRADE_TESTDUMMY); + EXPECT_EQ(CurrentEpochBranchId(1000000, params), nBranchId); +} + +TEST_F(UpgradesTest, IsActivationHeight) { + SelectParams(CBaseChainParams::REGTEST); + const Consensus::Params& params = Params().GetConsensus(); + + // Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT + EXPECT_FALSE(IsActivationHeight(-1, params, Consensus::UPGRADE_TESTDUMMY)); + EXPECT_FALSE(IsActivationHeight(0, params, Consensus::UPGRADE_TESTDUMMY)); + EXPECT_FALSE(IsActivationHeight(1, params, Consensus::UPGRADE_TESTDUMMY)); + EXPECT_FALSE(IsActivationHeight(1000000, params, Consensus::UPGRADE_TESTDUMMY)); + + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); + + EXPECT_FALSE(IsActivationHeight(-1, params, Consensus::UPGRADE_TESTDUMMY)); + EXPECT_TRUE(IsActivationHeight(0, params, Consensus::UPGRADE_TESTDUMMY)); + EXPECT_FALSE(IsActivationHeight(1, params, Consensus::UPGRADE_TESTDUMMY)); + EXPECT_FALSE(IsActivationHeight(1000000, params, Consensus::UPGRADE_TESTDUMMY)); + + int nActivationHeight = 100; + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, nActivationHeight); + + EXPECT_FALSE(IsActivationHeight(-1, params, Consensus::UPGRADE_TESTDUMMY)); + EXPECT_FALSE(IsActivationHeight(0, params, Consensus::UPGRADE_TESTDUMMY)); + EXPECT_FALSE(IsActivationHeight(1, params, Consensus::UPGRADE_TESTDUMMY)); + EXPECT_FALSE(IsActivationHeight(nActivationHeight - 1, params, Consensus::UPGRADE_TESTDUMMY)); + EXPECT_TRUE(IsActivationHeight(nActivationHeight, params, Consensus::UPGRADE_TESTDUMMY)); + EXPECT_FALSE(IsActivationHeight(nActivationHeight + 1, params, Consensus::UPGRADE_TESTDUMMY)); + EXPECT_FALSE(IsActivationHeight(1000000, params, Consensus::UPGRADE_TESTDUMMY)); +} + +TEST_F(UpgradesTest, IsActivationHeightForAnyUpgrade) { + SelectParams(CBaseChainParams::REGTEST); + const Consensus::Params& params = Params().GetConsensus(); + + // Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT + EXPECT_FALSE(IsActivationHeightForAnyUpgrade(-1, params)); + EXPECT_FALSE(IsActivationHeightForAnyUpgrade(0, params)); + EXPECT_FALSE(IsActivationHeightForAnyUpgrade(1, params)); + EXPECT_FALSE(IsActivationHeightForAnyUpgrade(1000000, params)); + + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); + + EXPECT_FALSE(IsActivationHeightForAnyUpgrade(-1, params)); + EXPECT_TRUE(IsActivationHeightForAnyUpgrade(0, params)); + EXPECT_FALSE(IsActivationHeightForAnyUpgrade(1, params)); + EXPECT_FALSE(IsActivationHeightForAnyUpgrade(1000000, params)); + + int nActivationHeight = 100; + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, nActivationHeight); + + EXPECT_FALSE(IsActivationHeightForAnyUpgrade(-1, params)); + EXPECT_FALSE(IsActivationHeightForAnyUpgrade(0, params)); + EXPECT_FALSE(IsActivationHeightForAnyUpgrade(1, params)); + EXPECT_FALSE(IsActivationHeightForAnyUpgrade(nActivationHeight - 1, params)); + EXPECT_TRUE(IsActivationHeightForAnyUpgrade(nActivationHeight, params)); + EXPECT_FALSE(IsActivationHeightForAnyUpgrade(nActivationHeight + 1, params)); + EXPECT_FALSE(IsActivationHeightForAnyUpgrade(1000000, params)); +} + +TEST_F(UpgradesTest, NextActivationHeight) { + SelectParams(CBaseChainParams::REGTEST); + const Consensus::Params& params = Params().GetConsensus(); + + // Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT + EXPECT_EQ(NextActivationHeight(-1, params), boost::none); + EXPECT_EQ(NextActivationHeight(0, params), boost::none); + EXPECT_EQ(NextActivationHeight(1, params), boost::none); + EXPECT_EQ(NextActivationHeight(1000000, params), boost::none); + + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); + + EXPECT_EQ(NextActivationHeight(-1, params), boost::none); + EXPECT_EQ(NextActivationHeight(0, params), boost::none); + EXPECT_EQ(NextActivationHeight(1, params), boost::none); + EXPECT_EQ(NextActivationHeight(1000000, params), boost::none); + + int nActivationHeight = 100; + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, nActivationHeight); + + EXPECT_EQ(NextActivationHeight(-1, params), boost::none); + EXPECT_EQ(NextActivationHeight(0, params), nActivationHeight); + EXPECT_EQ(NextActivationHeight(1, params), nActivationHeight); + EXPECT_EQ(NextActivationHeight(nActivationHeight - 1, params), nActivationHeight); + EXPECT_EQ(NextActivationHeight(nActivationHeight, params), boost::none); + EXPECT_EQ(NextActivationHeight(nActivationHeight + 1, params), boost::none); + EXPECT_EQ(NextActivationHeight(1000000, params), boost::none); +} diff --git a/src/gtest/test_validation.cpp b/src/gtest/test_validation.cpp new file mode 100644 index 000000000..db382e96b --- /dev/null +++ b/src/gtest/test_validation.cpp @@ -0,0 +1,152 @@ +#include + +#include "consensus/upgrades.h" +#include "consensus/validation.h" +#include "main.h" +#include "utiltest.h" + +extern ZCJoinSplit* params; + +extern bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos); + +void ExpectOptionalAmount(CAmount expected, boost::optional actual) { + EXPECT_TRUE((bool)actual); + if (actual) { + EXPECT_EQ(expected, *actual); + } +} + +// Fake an empty view +class FakeCoinsViewDB : public CCoinsView { +public: + FakeCoinsViewDB() {} + + bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { + return false; + } + + bool GetNullifier(const uint256 &nf) const { + return false; + } + + bool GetCoins(const uint256 &txid, CCoins &coins) const { + return false; + } + + bool HaveCoins(const uint256 &txid) const { + return false; + } + + uint256 GetBestBlock() const { + uint256 a; + return a; + } + + uint256 GetBestAnchor() const { + uint256 a; + return a; + } + + bool BatchWrite(CCoinsMap &mapCoins, + const uint256 &hashBlock, + const uint256 &hashAnchor, + CAnchorsMap &mapAnchors, + CNullifiersMap &mapNullifiers) { + return false; + } + + bool GetStats(CCoinsStats &stats) const { + return false; + } +}; + +TEST(Validation, ContextualCheckInputsPassesWithCoinbase) { + // Create fake coinbase transaction + CMutableTransaction mtx; + mtx.vin.resize(1); + CTransaction tx(mtx); + ASSERT_TRUE(tx.IsCoinBase()); + + // Fake an empty view + FakeCoinsViewDB fakeDB; + CCoinsViewCache view(&fakeDB); + + auto consensusBranchId = SPROUT_BRANCH_ID; + CValidationState state; + PrecomputedTransactionData txdata(tx); + EXPECT_TRUE(ContextualCheckInputs(tx, state, view, false, 0, false, txdata, Params(CBaseChainParams::MAIN).GetConsensus(), consensusBranchId)); +} + +TEST(Validation, ReceivedBlockTransactions) { + auto sk = libzcash::SpendingKey::random(); + + // Create a fake genesis block + CBlock block1; + block1.vtx.push_back(GetValidReceive(*params, sk, 5, true)); + block1.hashMerkleRoot = block1.BuildMerkleTree(); + CBlockIndex fakeIndex1 {block1}; + + // Create a fake child block + CBlock block2; + block2.hashPrevBlock = block1.GetHash(); + block2.vtx.push_back(GetValidReceive(*params, sk, 10, true)); + block2.hashMerkleRoot = block2.BuildMerkleTree(); + CBlockIndex fakeIndex2 {block2}; + fakeIndex2.pprev = &fakeIndex1; + + CDiskBlockPos pos1; + CDiskBlockPos pos2; + + // Set initial state of indices + ASSERT_TRUE(fakeIndex1.RaiseValidity(BLOCK_VALID_TREE)); + ASSERT_TRUE(fakeIndex2.RaiseValidity(BLOCK_VALID_TREE)); + EXPECT_TRUE(fakeIndex1.IsValid(BLOCK_VALID_TREE)); + EXPECT_TRUE(fakeIndex2.IsValid(BLOCK_VALID_TREE)); + EXPECT_FALSE(fakeIndex1.IsValid(BLOCK_VALID_TRANSACTIONS)); + EXPECT_FALSE(fakeIndex2.IsValid(BLOCK_VALID_TRANSACTIONS)); + + // Sprout pool values should not be set + EXPECT_FALSE((bool)fakeIndex1.nSproutValue); + EXPECT_FALSE((bool)fakeIndex1.nChainSproutValue); + EXPECT_FALSE((bool)fakeIndex2.nSproutValue); + EXPECT_FALSE((bool)fakeIndex2.nChainSproutValue); + + // Mark the second block's transactions as received first + CValidationState state; + EXPECT_TRUE(ReceivedBlockTransactions(block2, state, &fakeIndex2, pos2)); + EXPECT_FALSE(fakeIndex1.IsValid(BLOCK_VALID_TRANSACTIONS)); + EXPECT_TRUE(fakeIndex2.IsValid(BLOCK_VALID_TRANSACTIONS)); + + // Sprout pool value delta should now be set for the second block, + // but not any chain totals + EXPECT_FALSE((bool)fakeIndex1.nSproutValue); + EXPECT_FALSE((bool)fakeIndex1.nChainSproutValue); + { + SCOPED_TRACE("ExpectOptionalAmount call"); + ExpectOptionalAmount(20, fakeIndex2.nSproutValue); + } + EXPECT_FALSE((bool)fakeIndex2.nChainSproutValue); + + // Now mark the first block's transactions as received + EXPECT_TRUE(ReceivedBlockTransactions(block1, state, &fakeIndex1, pos1)); + EXPECT_TRUE(fakeIndex1.IsValid(BLOCK_VALID_TRANSACTIONS)); + EXPECT_TRUE(fakeIndex2.IsValid(BLOCK_VALID_TRANSACTIONS)); + + // Sprout pool values should now be set for both blocks + { + SCOPED_TRACE("ExpectOptionalAmount call"); + ExpectOptionalAmount(10, fakeIndex1.nSproutValue); + } + { + SCOPED_TRACE("ExpectOptionalAmount call"); + ExpectOptionalAmount(10, fakeIndex1.nChainSproutValue); + } + { + SCOPED_TRACE("ExpectOptionalAmount call"); + ExpectOptionalAmount(20, fakeIndex2.nSproutValue); + } + { + SCOPED_TRACE("ExpectOptionalAmount call"); + ExpectOptionalAmount(30, fakeIndex2.nChainSproutValue); + } +} diff --git a/src/gtest/utils.cpp b/src/gtest/utils.cpp index cf025162c..527e613e2 100644 --- a/src/gtest/utils.cpp +++ b/src/gtest/utils.cpp @@ -1,7 +1,3 @@ -#include "zcash/JoinSplit.hpp" - -ZCJoinSplit* params = ZCJoinSplit::Unopened(); - int GenZero(int n) { return 0; diff --git a/src/hash.h b/src/hash.h index 077155562..06fcced0a 100644 --- a/src/hash.h +++ b/src/hash.h @@ -12,6 +12,8 @@ #include "uint256.h" #include "version.h" +#include "sodium.h" + #include typedef uint256 ChainCode; @@ -150,6 +152,47 @@ public: } }; + +/** A writer stream (for serialization) that computes a 256-bit BLAKE2b hash. */ +class CBLAKE2bWriter +{ +private: + crypto_generichash_blake2b_state state; + +public: + int nType; + int nVersion; + + CBLAKE2bWriter(int nTypeIn, int nVersionIn, const unsigned char* personal) : nType(nTypeIn), nVersion(nVersionIn) { + assert(crypto_generichash_blake2b_init_salt_personal( + &state, + NULL, 0, // No key. + 32, + NULL, // No salt. + personal) == 0); + } + + CBLAKE2bWriter& write(const char *pch, size_t size) { + crypto_generichash_blake2b_update(&state, (const unsigned char*)pch, size); + return (*this); + } + + // invalidates the object + uint256 GetHash() { + uint256 result; + crypto_generichash_blake2b_final(&state, (unsigned char*)&result, 32); + return result; + } + + template + CBLAKE2bWriter& operator<<(const T& obj) { + // Serialize to this stream + ::Serialize(*this, obj, nType, nVersion); + return (*this); + } +}; + + /** Compute the 256-bit hash of an object's serialization. */ template uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION) diff --git a/src/httprpc.cpp b/src/httprpc.cpp index d6b317537..519798d34 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -17,7 +17,7 @@ static const char *WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\""; /** Simple one-shot callback timer to be used by the RPC mechanism to e.g. - * re-lock the wellet. + * re-lock the wallet. */ class HTTPRPCTimer : public RPCTimerBase { diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 4215a0f26..59ba7c3bc 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -72,13 +72,35 @@ private: std::deque queue; bool running; size_t maxDepth; + int numThreads; + + /** RAII object to keep track of number of running worker threads */ + class ThreadCounter + { + public: + WorkQueue &wq; + ThreadCounter(WorkQueue &w): wq(w) + { + boost::lock_guard lock(wq.cs); + wq.numThreads += 1; + } + ~ThreadCounter() + { + boost::lock_guard lock(wq.cs); + wq.numThreads -= 1; + wq.cond.notify_all(); + } + }; public: WorkQueue(size_t maxDepth) : running(true), - maxDepth(maxDepth) + maxDepth(maxDepth), + numThreads(0) { } - /* Precondition: worker threads have all stopped */ + /*( Precondition: worker threads have all stopped + * (call WaitExit) + */ ~WorkQueue() { while (!queue.empty()) { @@ -100,6 +122,7 @@ public: /** Thread function */ void Run() { + ThreadCounter count(*this); while (running) { WorkItem* i = 0; { @@ -122,6 +145,13 @@ public: running = false; cond.notify_all(); } + /** Wait for worker threads to exit */ + void WaitExit() + { + boost::unique_lock lock(cs); + while (numThreads > 0) + cond.wait(lock); + } /** Return current depth of queue */ size_t Depth() @@ -155,6 +185,8 @@ static std::vector rpc_allow_subnets; static WorkQueue* workQueue = 0; //! Handlers for (sub)paths std::vector pathHandlers; +//! Bound listening sockets +std::vector boundSockets; /** Check if a network address is allowed to access the HTTP server */ static bool ClientAllowed(const CNetAddr& netaddr) @@ -264,10 +296,17 @@ 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"); + evhttp_send_error(req, HTTP_SERVUNAVAIL, NULL); +} + /** Event dispatcher thread */ static void ThreadHTTP(struct event_base* base, struct evhttp* http) { - RenameThread("bitcoin-http"); + RenameThread("zcash-http"); LogPrint("http", "Entering http event loop\n"); event_base_dispatch(base); // Event loop will be interrupted by InterruptHTTPServer() @@ -278,7 +317,6 @@ static void ThreadHTTP(struct event_base* base, struct evhttp* http) static bool HTTPBindAddresses(struct evhttp* http) { int defaultPort = GetArg("-rpcport", BaseParams().RPCPort()); - int nBound = 0; std::vector > endpoints; // Determine what addresses to bind to @@ -304,19 +342,20 @@ 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); - if (evhttp_bind_socket(http, i->first.empty() ? NULL : i->first.c_str(), i->second) == 0) { - nBound += 1; + 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); } else { LogPrintf("Binding RPC on address %s port %i failed.\n", i->first, i->second); } } - return nBound > 0; + return !boundSockets.empty(); } /** Simple wrapper to set thread name and run work queue */ static void HTTPWorkQueueRun(WorkQueue* queue) { - RenameThread("bitcoin-httpworker"); + RenameThread("zcash-httpworker"); queue->Run(); } @@ -399,23 +438,33 @@ bool InitHTTPServer() return true; } -bool StartHTTPServer(boost::thread_group& threadGroup) +boost::thread threadHTTP; + +bool StartHTTPServer() { LogPrint("http", "Starting HTTP server\n"); int rpcThreads = std::max((long)GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L); LogPrintf("HTTP: starting %d worker threads\n", rpcThreads); - threadGroup.create_thread(boost::bind(&ThreadHTTP, eventBase, eventHTTP)); + threadHTTP = boost::thread(boost::bind(&ThreadHTTP, eventBase, eventHTTP)); - for (int i = 0; i < rpcThreads; i++) - threadGroup.create_thread(boost::bind(&HTTPWorkQueueRun, workQueue)); + for (int i = 0; i < rpcThreads; i++) { + boost::thread rpc_worker(HTTPWorkQueueRun, workQueue); + rpc_worker.detach(); + } return true; } void InterruptHTTPServer() { LogPrint("http", "Interrupting HTTP server\n"); - if (eventBase) - event_base_loopbreak(eventBase); + if (eventHTTP) { + // Unlisten sockets + BOOST_FOREACH (evhttp_bound_socket *socket, boundSockets) { + evhttp_del_accept_socket(eventHTTP, socket); + } + // Reject requests on current connections + evhttp_set_gencb(eventHTTP, http_reject_request_cb, NULL); + } if (workQueue) workQueue->Interrupt(); } @@ -423,7 +472,27 @@ void InterruptHTTPServer() void StopHTTPServer() { LogPrint("http", "Stopping HTTP server\n"); - delete workQueue; + if (workQueue) { + LogPrint("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"); + // 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 + // Before this was solved with event_base_loopexit, but that didn't work as expected in + // at least libevent 2.0.21 and always introduced a delay. In libevent + // master that appears to be solved, so in the future that solution + // could be used again (if desirable). + // (see discussion in https://github.com/bitcoin/bitcoin/pull/6990) + if (!threadHTTP.try_join_for(boost::chrono::milliseconds(2000))) { + LogPrintf("HTTP event loop did not exit within allotted time, sending loopbreak\n"); + event_base_loopbreak(eventBase); + threadHTTP.join(); + } + } if (eventHTTP) { evhttp_free(eventHTTP); eventHTTP = 0; @@ -432,6 +501,7 @@ void StopHTTPServer() event_base_free(eventBase); eventBase = 0; } + LogPrint("http", "Stopped HTTP server\n"); } struct event_base* EventBase() diff --git a/src/httpserver.h b/src/httpserver.h index b377dc19f..93fb5d8d6 100644 --- a/src/httpserver.h +++ b/src/httpserver.h @@ -28,7 +28,7 @@ bool InitHTTPServer(); * This is separate from InitHTTPServer to give users race-condition-free time * to register their handlers between InitHTTPServer and StartHTTPServer. */ -bool StartHTTPServer(boost::thread_group& threadGroup); +bool StartHTTPServer(); /** Interrupt HTTP server threads */ void InterruptHTTPServer(); /** Stop HTTP server */ @@ -56,11 +56,14 @@ class HTTPRequest { private: struct evhttp_request* req; + + // For test access +protected: bool replySent; public: HTTPRequest(struct evhttp_request* req); - ~HTTPRequest(); + virtual ~HTTPRequest(); enum RequestMethod { UNKNOWN, @@ -76,17 +79,17 @@ public: /** Get CService (address:ip) for the origin of the http request. */ - CService GetPeer(); + virtual CService GetPeer(); /** Get request method. */ - RequestMethod GetRequestMethod(); + virtual RequestMethod GetRequestMethod(); /** * Get the request header specified by hdr, or an empty string. * Return an pair (isPresent,string). */ - std::pair GetHeader(const std::string& hdr); + virtual std::pair GetHeader(const std::string& hdr); /** * Read request body. @@ -101,7 +104,7 @@ public: * * @note call this before calling WriteErrorReply or Reply. */ - void WriteHeader(const std::string& hdr, const std::string& value); + virtual void WriteHeader(const std::string& hdr, const std::string& value); /** * Write HTTP reply. @@ -111,7 +114,7 @@ public: * @note Can be called only once. As this will give the request back to the * main thread, do not call any other HTTPRequest methods after calling this. */ - void WriteReply(int nStatus, const std::string& strReply = ""); + virtual void WriteReply(int nStatus, const std::string& strReply = ""); }; /** Event handler closure. diff --git a/src/init.cpp b/src/init.cpp index 79552a76f..7268c362c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -16,6 +16,7 @@ #endif #include "checkpoints.h" #include "compat/sanity.h" +#include "consensus/upgrades.h" #include "consensus/validation.h" #include "httpserver.h" #include "httprpc.h" @@ -44,8 +45,10 @@ #include #endif +#include #include #include +#include #include #include #include @@ -53,12 +56,16 @@ #include #include -#include "libsnark/common/profiling.hpp" +#include #if ENABLE_ZMQ #include "zmq/zmqnotificationinterface.h" #endif +#if ENABLE_PROTON +#include "amqp/amqpnotificationinterface.h" +#endif + using namespace std; extern void ThreadSendAlert(); @@ -75,7 +82,11 @@ bool fFeeEstimatesInitialized = false; static CZMQNotificationInterface* pzmqNotificationInterface = NULL; #endif -#ifdef _WIN32 +#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 // anyway. @@ -154,6 +165,7 @@ public: static CCoinsViewDB *pcoinsdbview = NULL; static CCoinsViewErrorCatcher *pcoinscatcher = NULL; +static boost::scoped_ptr globalVerifyHandle; void Interrupt(boost::thread_group& threadGroup) { @@ -237,7 +249,15 @@ void Shutdown() } #endif -#ifndef _WIN32 +#if ENABLE_PROTON + if (pAMQPNotificationInterface) { + UnregisterValidationInterface(pAMQPNotificationInterface); + delete pAMQPNotificationInterface; + pAMQPNotificationInterface = NULL; + } +#endif + +#ifndef WIN32 try { boost::filesystem::remove(GetPidFile()); } catch (const boost::filesystem::filesystem_error& e) { @@ -251,6 +271,7 @@ void Shutdown() #endif delete pzcashParams; pzcashParams = NULL; + globalVerifyHandle.reset(); ECC_Stop(); LogPrintf("%s: done\n", __func__); } @@ -312,7 +333,7 @@ std::string HelpMessage(HelpMessageMode mode) const bool showDebug = GetBoolArg("-help-debug", false); // 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 + // 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")); @@ -329,10 +350,13 @@ std::string HelpMessage(HelpMessageMode mode) #endif } strUsage += HelpMessageOpt("-datadir=", _("Specify data directory")); + strUsage += HelpMessageOpt("-disabledeprecation=", strprintf(_("Disable block-height node deprecation and automatic shutdown (example: -disabledeprecation=%s)"), + FormatVersion(CLIENT_VERSION))); 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("-maxorphantx=", strprintf(_("Keep at most unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS)); + strUsage += HelpMessageOpt("-mempooltxinputlimit=", _("Set the maximum number of transparent inputs in a transaction that the mempool will accept (default: 0 = no limit applied)")); strUsage += HelpMessageOpt("-par=", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"), -(int)boost::thread::hardware_concurrency(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS)); #ifndef _WIN32 @@ -366,20 +390,13 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-onion=", strprintf(_("Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s)"), "-proxy")); strUsage += HelpMessageOpt("-onlynet=", _("Only connect to nodes in network (ipv4, ipv6 or onion)")); strUsage += HelpMessageOpt("-permitbaremultisig", strprintf(_("Relay non-P2SH multisig (default: %u)"), 1)); - strUsage += HelpMessageOpt("-port=", strprintf(_("Listen for connections on (default: %u or testnet: %u)"), 8233, 18233)); + strUsage += HelpMessageOpt("-port=", strprintf(_("Listen for connections on (default: %u or testnet: %u)"), 7770, 17770)); strUsage += HelpMessageOpt("-proxy=", _("Connect through SOCKS5 proxy")); strUsage += HelpMessageOpt("-proxyrandomize", strprintf(_("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)"), 1)); strUsage += HelpMessageOpt("-seednode=", _("Connect to a node to retrieve peer addresses, and disconnect")); strUsage += HelpMessageOpt("-timeout=", strprintf(_("Specify connection timeout in milliseconds (minimum: 1, default: %d)"), DEFAULT_CONNECT_TIMEOUT)); strUsage += HelpMessageOpt("-torcontrol=:", strprintf(_("Tor control port to use if onion listening enabled (default: %s)"), DEFAULT_TOR_CONTROL)); strUsage += HelpMessageOpt("-torpassword=", _("Tor control port password (default: empty)")); -#ifdef USE_UPNP -#if USE_UPNP - strUsage += HelpMessageOpt("-upnp", _("Use UPnP to map the listening port (default: 1 when listening and no -proxy)")); -#else - strUsage += HelpMessageOpt("-upnp", strprintf(_("Use UPnP to map the listening port (default: %u)"), 0)); -#endif -#endif 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")); @@ -389,16 +406,18 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls")); strUsage += HelpMessageOpt("-keypool=", strprintf(_("Set key pool size to (default: %u)"), 100)); if (showDebug) - strUsage += HelpMessageOpt("-mintxfee=", strprintf("Fees (in BTC/Kb) smaller than this are considered zero fee for transaction creation (default: %s)", - FormatMoney(CWallet::minTxFee.GetFeePerK()))); - strUsage += HelpMessageOpt("-paytxfee=", strprintf(_("Fee (in BTC/kB) to add to transactions you send (default: %s)"), FormatMoney(payTxFee.GetFeePerK()))); - strUsage += HelpMessageOpt("-rescan", _("Rescan the blockchain for missing wallet transactions") + " " + _("on startup")); + strUsage += HelpMessageOpt("-mintxfee=", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)", + CURRENCY_UNIT, FormatMoney(CWallet::minTxFee.GetFeePerK()))); + 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.dat") + " " + _("on startup")); strUsage += HelpMessageOpt("-sendfreetransactions", strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), 0)); strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), 1)); 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("-maxtxfee=", strprintf(_("Maximum total fees to use in a single wallet transaction; setting this too low may abort large transactions (default: %s)"), - FormatMoney(maxTxFee))); + strUsage += HelpMessageOpt("-txexpirydelta", strprintf(_("Set the number of blocks after which a transaction that has not been mined will become invalid (default: %u)"), DEFAULT_TX_EXPIRY_DELTA)); + strUsage += HelpMessageOpt("-maxtxfee=", strprintf(_("Maximum total fees (in %s) to use in a single wallet transaction; setting this too low may abort large transactions (default: %s)"), + CURRENCY_UNIT, FormatMoney(maxTxFee))); strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format") + " " + _("on startup")); strUsage += HelpMessageOpt("-wallet=", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), "wallet.dat")); strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), true)); @@ -415,6 +434,14 @@ 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) { @@ -426,6 +453,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-fuzzmessagestest=", "Randomly fuzz 1 of every network messages"); strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", 1)); strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", 0)); + 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, " "rand, reindex, rpc, selectcoins, tor, zmq, zrpc, zrpcunsafe (implies zrpc)"; // Don't translate these @@ -441,7 +469,8 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-relaypriority", strprintf("Require high priority for relaying free or low-fee transactions (default: %u)", 0)); strUsage += HelpMessageOpt("-maxsigcachesize=", strprintf("Limit size of signature cache to entries (default: %u)", 50000)); } - strUsage += HelpMessageOpt("-minrelaytxfee=", strprintf(_("Fees (in BTC/Kb) smaller than this are considered zero fee for relaying (default: %s)"), FormatMoney(::minRelayTxFee.GetFeePerK()))); + strUsage += HelpMessageOpt("-minrelaytxfee=", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for relaying (default: %s)"), + CURRENCY_UNIT, FormatMoney(::minRelayTxFee.GetFeePerK()))); strUsage += HelpMessageOpt("-printtoconsole", _("Send trace/debug info to console instead of debug.log file")); if (showDebug) { @@ -486,7 +515,7 @@ std::string HelpMessage(HelpMessageMode mode) 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("-rpcuser=", _("Username for JSON-RPC connections")); strUsage += HelpMessageOpt("-rpcpassword=", _("Password for JSON-RPC connections")); - strUsage += HelpMessageOpt("-rpcport=", strprintf(_("Listen for JSON-RPC connections on (default: %u or testnet: %u)"), 8232, 18232)); + strUsage += HelpMessageOpt("-rpcport=", strprintf(_("Listen for JSON-RPC connections on (default: %u or testnet: %u)"), 7771, 17771)); strUsage += HelpMessageOpt("-rpcallowip=", _("Allow JSON-RPC connections from specified source. Valid for 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). This option can be specified multiple times")); strUsage += HelpMessageOpt("-rpcthreads=", strprintf(_("Set the number of threads to service RPC calls (default: %d)"), DEFAULT_HTTP_THREADS)); if (showDebug) { @@ -640,8 +669,7 @@ void ThreadImport(std::vector vImportFiles) bool InitSanityCheck(void) { if(!ECC_InitSanityCheck()) { - InitError("OpenSSL appears to lack support for elliptic curve cryptography. For more " - "information, visit https://en.bitcoin.it/wiki/OpenSSL_and_EC_Libraries"); + InitError("Elliptic curve cryptography sanity check failure. Aborting."); return false; } if (!glibc_sanity_test() || !glibcxx_sanity_test()) @@ -670,18 +698,14 @@ static void ZC_LoadParams() return; } - pzcashParams = ZCJoinSplit::Unopened(); - LogPrintf("Loading verifying key from %s\n", vk_path.string().c_str()); gettimeofday(&tv_start, 0); - pzcashParams->loadVerifyingKey(vk_path.string()); + pzcashParams = ZCJoinSplit::Prepared(vk_path.string(), pk_path.string()); gettimeofday(&tv_end, 0); elapsed = float(tv_end.tv_sec-tv_start.tv_sec) + (tv_end.tv_usec-tv_start.tv_usec)/float(1000000); LogPrintf("Loaded verifying key in %fs seconds.\n", elapsed); - - pzcashParams->setProvingKeyPath(pk_path.string()); } bool AppInitServers(boost::thread_group& threadGroup) @@ -696,7 +720,7 @@ bool AppInitServers(boost::thread_group& threadGroup) return false; if (GetBoolArg("-rest", false) && !StartREST()) return false; - if (!StartHTTPServer(threadGroup)) + if (!StartHTTPServer()) return false; return true; } @@ -775,6 +799,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (mapArgs.count("-developerencryptwallet")) { return InitError(_("Wallet encryption requires -experimentalfeatures.")); } + else if (mapArgs.count("-paymentdisclosure")) { + return InitError(_("Payment disclosure requires -experimentalfeatures.")); + } else if (mapArgs.count("-zmergetoaddress")) { + return InitError(_("RPC method z_mergetoaddress requires -experimentalfeatures.")); + } } // Set this early so that parameter interactions go to console @@ -808,19 +837,13 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // to protect privacy, do not listen by default if a default proxy server is specified if (SoftSetBoolArg("-listen", false)) LogPrintf("%s: parameter interaction: -proxy set -> setting -listen=0\n", __func__); - // to protect privacy, do not use UPNP when a proxy is set. The user may still specify -listen=1 - // to listen locally, so don't rely on this happening through -listen below. - if (SoftSetBoolArg("-upnp", false)) - LogPrintf("%s: parameter interaction: -proxy set -> setting -upnp=0\n", __func__); // to protect privacy, do not discover addresses by default if (SoftSetBoolArg("-discover", false)) LogPrintf("%s: parameter interaction: -proxy set -> setting -discover=0\n", __func__); } if (!GetBoolArg("-listen", DEFAULT_LISTEN)) { - // do not map ports or try to retrieve public IP when not listening (pointless) - if (SoftSetBoolArg("-upnp", false)) - LogPrintf("%s: parameter interaction: -listen=0 -> setting -upnp=0\n", __func__); + // do not try to retrieve public IP when not listening (pointless) if (SoftSetBoolArg("-discover", false)) LogPrintf("%s: parameter interaction: -listen=0 -> setting -discover=0\n", __func__); if (SoftSetBoolArg("-listenonion", false)) @@ -901,14 +924,17 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) InitWarning(_("Warning: Unsupported argument -benchmark ignored, use -debug=bench.")); // Checkmempool and checkblockindex default to true in regtest mode - mempool.setSanityCheck(GetBoolArg("-checkmempool", chainparams.DefaultConsistencyChecks())); + int ratio = std::min(std::max(GetArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000); + if (ratio != 0) { + mempool.setSanityCheck(1.0 / ratio); + } fCheckBlockIndex = GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks()); fCheckpointsEnabled = GetBoolArg("-checkpoints", true); // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency nScriptCheckThreads = GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS); if (nScriptCheckThreads <= 0) - nScriptCheckThreads += boost::thread::hardware_concurrency(); + nScriptCheckThreads += GetNumCores(); if (nScriptCheckThreads <= 1) nScriptCheckThreads = 0; else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS) @@ -991,6 +1017,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } } nTxConfirmTarget = GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); + expiryDelta = GetArg("-txexpirydelta", DEFAULT_TX_EXPIRY_DELTA); bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", true); fSendFreeTransactions = GetBoolArg("-sendfreetransactions", false); @@ -1016,6 +1043,49 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } #endif + // Default value of 0 for mempooltxinputlimit means no limit is applied + if (mapArgs.count("-mempooltxinputlimit")) { + int64_t limit = GetArg("-mempooltxinputlimit", 0); + if (limit < 0) { + return InitError(_("Mempool limit on transparent inputs to a transaction cannot be negative")); + } else if (limit > 0) { + LogPrintf("Mempool configured to reject transactions with greater than %lld transparent inputs\n", limit); + } + } + + if (!mapMultiArgs["-nuparams"].empty()) { + // Allow overriding network upgrade parameters for testing + if (Params().NetworkIDString() != "regtest") { + return InitError("Network upgrade parameters may only be overridden on regtest."); + } + const vector& deployments = mapMultiArgs["-nuparams"]; + for (auto i : deployments) { + std::vector vDeploymentParams; + boost::split(vDeploymentParams, i, boost::is_any_of(":")); + if (vDeploymentParams.size() != 2) { + return InitError("Network upgrade parameters malformed, expecting hexBranchId:activationHeight"); + } + int nActivationHeight; + if (!ParseInt32(vDeploymentParams[1], &nActivationHeight)) { + return InitError(strprintf("Invalid nActivationHeight (%s)", vDeploymentParams[1])); + } + bool found = false; + // Exclude Sprout from upgrades + for (auto i = Consensus::BASE_SPROUT + 1; i < Consensus::MAX_NETWORK_UPGRADES; ++i) + { + if (vDeploymentParams[0].compare(HexInt(NetworkUpgradeInfo[i].nBranchId)) == 0) { + UpdateNetworkUpgradeParameters(Consensus::UpgradeIndex(i), nActivationHeight); + found = true; + LogPrintf("Setting network upgrade activation parameters for %s to height=%d\n", vDeploymentParams[0], nActivationHeight); + break; + } + } + if (!found) { + return InitError(strprintf("Invalid network upgrade (%s)", vDeploymentParams[0])); + } + } + } + // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log // Initialize libsodium @@ -1025,6 +1095,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // Initialize elliptic curve code ECC_Start(); + globalVerifyHandle.reset(new ECCVerifyHandle()); // Sanity check if (!InitSanityCheck()) @@ -1247,6 +1318,21 @@ 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 (!fExperimentalMode) { + return InitError(_("AMQP support requires -experimentalfeatures.")); + } + + RegisterValidationInterface(pAMQPNotificationInterface); + } +#endif + // ********************************************************* Step 7: load block chain fReindex = GetBoolArg("-reindex", false); @@ -1352,6 +1438,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) break; } + if (!fReindex) { + uiInterface.InitMessage(_("Rewinding blocks if needed...")); + if (!RewindBlockIndex(chainparams)) { + strLoadError = _("Unable to rewind the database to a pre-upgrade state. You will need to redownload the blockchain"); + break; + } + } + uiInterface.InitMessage(_("Verifying blocks...")); if (fHavePruned && GetArg("-checkblocks", 288) > MIN_BLOCKS_TO_KEEP) { LogPrintf("Prune: pruned datadir may not have more than %d blocks; -checkblocks=%d may fail\n", @@ -1453,10 +1547,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) InitWarning(msg); } else if (nLoadWalletRet == DB_TOO_NEW) - strErrors << _("Error loading wallet.dat: Wallet requires newer version of Bitcoin Core") << "\n"; + strErrors << _("Error loading wallet.dat: Wallet requires newer version of Zcash") << "\n"; else if (nLoadWalletRet == DB_NEED_REWRITE) { - strErrors << _("Wallet needed to be rewritten: restart Bitcoin Core to complete") << "\n"; + strErrors << _("Wallet needed to be rewritten: restart Zcash to complete") << "\n"; LogPrintf("%s", strErrors.str()); return InitError(strErrors.str()); } diff --git a/src/key.cpp b/src/key.cpp index 4a6a1d25c..c93f8d15d 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2017 The Zcash developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -7,17 +8,152 @@ #include "arith_uint256.h" #include "crypto/common.h" #include "crypto/hmac_sha512.h" -#include "eccryptoverify.h" #include "pubkey.h" #include "random.h" #include -#include "ecwrapper.h" +#include -static secp256k1_context_t* secp256k1_context = NULL; +static secp256k1_context* secp256k1_context_sign = NULL; + +/** These functions are taken from the libsecp256k1 distribution and are very ugly. */ + +/** + * This parses a format loosely based on a DER encoding of the ECPrivateKey type from + * section C.4 of SEC 1 , with the following caveats: + * + * * The octet-length of the SEQUENCE must be encoded as 1 or 2 octets. It is not + * required to be encoded as one octet if it is less than 256, as DER would require. + * * The octet-length of the SEQUENCE must not be greater than the remaining + * length of the key encoding, but need not match it (i.e. the encoding may contain + * junk after the encoded SEQUENCE). + * * The privateKey OCTET STRING is zero-filled on the left to 32 octets. + * * Anything after the encoding of the privateKey OCTET STRING is ignored, whether + * or not it is validly encoded DER. + * + * 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; + memset(out32, 0, 32); + /* sequence header */ + if (end - privkey < 1 || *privkey != 0x30u) { + return 0; + } + privkey++; + /* sequence length constructor */ + if (end - privkey < 1 || !(*privkey & 0x80u)) { + return 0; + } + size_t lenb = *privkey & ~0x80u; privkey++; + if (lenb < 1 || lenb > 2) { + return 0; + } + if (end - privkey < lenb) { + return 0; + } + /* sequence length */ + size_t len = privkey[lenb-1] | (lenb > 1 ? privkey[lenb-2] << 8 : 0u); + privkey += lenb; + if (end - privkey < len) { + return 0; + } + /* sequence element 0: version number (=1) */ + if (end - privkey < 3 || privkey[0] != 0x02u || privkey[1] != 0x01u || privkey[2] != 0x01u) { + return 0; + } + privkey += 3; + /* sequence element 1: octet string, up to 32 bytes */ + if (end - privkey < 2 || privkey[0] != 0x04u) { + return 0; + } + size_t oslen = privkey[1]; + privkey += 2; + if (oslen > 32 || end - privkey < oslen) { + return 0; + } + memcpy(out32 + (32 - oslen), privkey, oslen); + if (!secp256k1_ec_seckey_verify(ctx, out32)) { + memset(out32, 0, 32); + return 0; + } + return 1; +} + +/** + * This serializes to a DER encoding of the ECPrivateKey type from section C.4 of SEC 1 + * . 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 + * 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); + secp256k1_pubkey pubkey; + size_t pubkeylen = 0; + if (!secp256k1_ec_pubkey_create(ctx, &pubkey, key32)) { + *privkeylen = 0; + return 0; + } + if (compressed) { + static const unsigned char begin[] = { + 0x30,0x81,0xD3,0x02,0x01,0x01,0x04,0x20 + }; + static const unsigned char middle[] = { + 0xA0,0x81,0x85,0x30,0x81,0x82,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48, + 0xCE,0x3D,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,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04, + 0x21,0x02,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,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,0xA1,0x24,0x03,0x22,0x00 + }; + unsigned char *ptr = privkey; + 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); + } else { + static const unsigned char begin[] = { + 0x30,0x82,0x01,0x13,0x02,0x01,0x01,0x04,0x20 + }; + static const unsigned char middle[] = { + 0xA0,0x81,0xA5,0x30,0x81,0xA2,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48, + 0xCE,0x3D,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,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04, + 0x41,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,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,0xA1,0x44,0x03,0x42,0x00 + }; + unsigned char *ptr = privkey; + 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); + } + return 1; +} bool CKey::Check(const unsigned char *vch) { - return eccrypto::Check(vch); + return secp256k1_ec_seckey_verify(secp256k1_context_sign, vch); } void CKey::MakeNewKey(bool fCompressedIn) { @@ -29,7 +165,7 @@ void CKey::MakeNewKey(bool fCompressedIn) { } bool CKey::SetPrivKey(const CPrivKey &privkey, bool fCompressedIn) { - if (!secp256k1_ec_privkey_import(secp256k1_context, (unsigned char*)begin(), &privkey[0], privkey.size())) + if (!ec_privkey_import_der(secp256k1_context_sign, (unsigned char*)begin(), &privkey[0], privkey.size())) return false; fCompressed = fCompressedIn; fValid = true; @@ -39,10 +175,11 @@ bool CKey::SetPrivKey(const CPrivKey &privkey, bool fCompressedIn) { CPrivKey CKey::GetPrivKey() const { assert(fValid); CPrivKey privkey; - int privkeylen, ret; - privkey.resize(279); - privkeylen = 279; - ret = secp256k1_ec_privkey_export(secp256k1_context, begin(), (unsigned char*)&privkey[0], &privkeylen, fCompressed); + 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); assert(ret); privkey.resize(privkeylen); return privkey; @@ -50,11 +187,13 @@ CPrivKey CKey::GetPrivKey() const { CPubKey CKey::GetPubKey() const { assert(fValid); + secp256k1_pubkey pubkey; + size_t clen = CPubKey::PUBLIC_KEY_SIZE; CPubKey result; - int clen = 65; - int ret = secp256k1_ec_pubkey_create(secp256k1_context, (unsigned char*)result.begin(), &clen, begin(), fCompressed); - assert((int)result.size() == clen); + int ret = secp256k1_ec_pubkey_create(secp256k1_context_sign, &pubkey, begin()); assert(ret); + secp256k1_ec_pubkey_serialize(secp256k1_context_sign, (unsigned char*)result.begin(), &clen, &pubkey, fCompressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED); + assert(result.size() == clen); assert(result.IsValid()); return result; } @@ -62,12 +201,14 @@ CPubKey CKey::GetPubKey() const { bool CKey::Sign(const uint256 &hash, std::vector& vchSig, uint32_t test_case) const { if (!fValid) return false; - vchSig.resize(72); - int nSigLen = 72; + vchSig.resize(CPubKey::SIGNATURE_SIZE); + size_t nSigLen = CPubKey::SIGNATURE_SIZE; unsigned char extra_entropy[32] = {0}; WriteLE32(extra_entropy, test_case); - int ret = secp256k1_ecdsa_sign(secp256k1_context, hash.begin(), (unsigned char*)&vchSig[0], &nSigLen, begin(), secp256k1_nonce_function_rfc6979, test_case ? extra_entropy : NULL); + secp256k1_ecdsa_signature sig; + int ret = secp256k1_ecdsa_sign(secp256k1_context_sign, &sig, hash.begin(), begin(), secp256k1_nonce_function_rfc6979, test_case ? extra_entropy : NULL); assert(ret); + secp256k1_ecdsa_signature_serialize_der(secp256k1_context_sign, (unsigned char*)&vchSig[0], &nSigLen, &sig); vchSig.resize(nSigLen); return true; } @@ -77,7 +218,7 @@ bool CKey::VerifyPubKey(const CPubKey& pubkey) const { return false; } unsigned char rnd[8]; - std::string str = "Bitcoin key verification\n"; + std::string str = "Zcash key verification\n"; GetRandBytes(rnd, sizeof(rnd)); uint256 hash; CHash256().Write((unsigned char*)str.data(), str.size()).Write(rnd, sizeof(rnd)).Finalize(hash.begin()); @@ -89,9 +230,12 @@ bool CKey::VerifyPubKey(const CPubKey& pubkey) const { bool CKey::SignCompact(const uint256 &hash, std::vector& vchSig) const { if (!fValid) return false; - vchSig.resize(65); + vchSig.resize(CPubKey::COMPACT_SIGNATURE_SIZE); int rec = -1; - int ret = secp256k1_ecdsa_sign_compact(secp256k1_context, hash.begin(), &vchSig[1], begin(), secp256k1_nonce_function_rfc6979, NULL, &rec); + secp256k1_ecdsa_recoverable_signature sig; + int ret = secp256k1_ecdsa_sign_recoverable(secp256k1_context_sign, &sig, hash.begin(), begin(), secp256k1_nonce_function_rfc6979, NULL); + assert(ret); + secp256k1_ecdsa_recoverable_signature_serialize_compact(secp256k1_context_sign, (unsigned char*)&vchSig[1], &rec, &sig); assert(ret); assert(rec != -1); vchSig[0] = 27 + rec + (fCompressed ? 4 : 0); @@ -99,7 +243,7 @@ bool CKey::SignCompact(const uint256 &hash, std::vector& vchSig) } bool CKey::Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck=false) { - if (!secp256k1_ec_privkey_import(secp256k1_context, (unsigned char*)begin(), &privkey[0], privkey.size())) + if (!ec_privkey_import_der(secp256k1_context_sign, (unsigned char*)begin(), &privkey[0], privkey.size())) return false; fCompressed = vchPubKey.IsCompressed(); fValid = true; @@ -117,15 +261,15 @@ bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const LockObject(out); if ((nChild >> 31) == 0) { CPubKey pubkey = GetPubKey(); - assert(pubkey.begin() + 33 == pubkey.end()); + assert(pubkey.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE); BIP32Hash(cc, nChild, *pubkey.begin(), pubkey.begin()+1, out); } else { - assert(begin() + 32 == end()); + assert(size() == 32); BIP32Hash(cc, nChild, 0, begin(), out); } memcpy(ccChild.begin(), out+32, 32); memcpy((unsigned char*)keyChild.begin(), begin(), 32); - bool ret = secp256k1_ec_privkey_tweak_add(secp256k1_context, (unsigned char*)keyChild.begin(), out); + bool ret = secp256k1_ec_privkey_tweak_add(secp256k1_context_sign, (unsigned char*)keyChild.begin(), out); UnlockObject(out); keyChild.fCompressed = true; keyChild.fValid = ret; @@ -183,20 +327,16 @@ void CExtKey::Decode(const unsigned char code[74]) { } bool ECC_InitSanityCheck() { - if (!CECKey::SanityCheck()) { - return false; - } CKey key; key.MakeNewKey(true); CPubKey pubkey = key.GetPubKey(); return key.VerifyPubKey(pubkey); } - void ECC_Start() { - assert(secp256k1_context == NULL); + assert(secp256k1_context_sign == NULL); - secp256k1_context_t *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); assert(ctx != NULL); { @@ -209,12 +349,12 @@ void ECC_Start() { UnlockObject(seed); } - secp256k1_context = ctx; + secp256k1_context_sign = ctx; } void ECC_Stop() { - secp256k1_context_t *ctx = secp256k1_context; - secp256k1_context = NULL; + secp256k1_context *ctx = secp256k1_context_sign; + secp256k1_context_sign = NULL; if (ctx) { secp256k1_context_destroy(ctx); diff --git a/src/key.h b/src/key.h index 021eac2a8..c2b75935c 100644 --- a/src/key.h +++ b/src/key.h @@ -1,5 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2017 The Zcash developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -15,25 +16,30 @@ #include -/** - * secp256k1: - * const unsigned int PRIVATE_KEY_SIZE = 279; - * const unsigned int PUBLIC_KEY_SIZE = 65; - * const unsigned int SIGNATURE_SIZE = 72; - * - * see www.keylength.com - * script supports up to 75 for single byte push - */ - /** * secure_allocator is defined in allocators.h - * CPrivKey is a serialized private key, with all parameters included (279 bytes) + * CPrivKey is a serialized private key, with all parameters included + * (PRIVATE_KEY_SIZE bytes) */ typedef std::vector > CPrivKey; /** An encapsulated private key. */ class CKey { +public: + /** + * secp256k1: + */ + static const unsigned int PRIVATE_KEY_SIZE = 279; + static const unsigned int COMPRESSED_PRIVATE_KEY_SIZE = 214; + /** + * see www.keylength.com + * script supports up to 75 for single byte push + */ + static_assert( + PRIVATE_KEY_SIZE >= COMPRESSED_PRIVATE_KEY_SIZE, + "COMPRESSED_PRIVATE_KEY_SIZE is larger than PRIVATE_KEY_SIZE"); + private: //! Whether this private key is valid. We check for correctness when modifying the key //! data, so fValid should always correspond to the actual state. diff --git a/src/keystore.cpp b/src/keystore.cpp index f32ba0c32..323fe710c 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -89,6 +89,40 @@ bool CBasicKeyStore::AddSpendingKey(const libzcash::SpendingKey &sk) LOCK(cs_SpendingKeyStore); auto address = sk.address(); mapSpendingKeys[address] = sk; - mapNoteDecryptors.insert(std::make_pair(address, ZCNoteDecryption(sk.viewing_key()))); + mapNoteDecryptors.insert(std::make_pair(address, ZCNoteDecryption(sk.receiving_key()))); return true; } + +bool CBasicKeyStore::AddViewingKey(const libzcash::ViewingKey &vk) +{ + LOCK(cs_SpendingKeyStore); + auto address = vk.address(); + mapViewingKeys[address] = vk; + mapNoteDecryptors.insert(std::make_pair(address, ZCNoteDecryption(vk.sk_enc))); + return true; +} + +bool CBasicKeyStore::RemoveViewingKey(const libzcash::ViewingKey &vk) +{ + LOCK(cs_SpendingKeyStore); + mapViewingKeys.erase(vk.address()); + return true; +} + +bool CBasicKeyStore::HaveViewingKey(const libzcash::PaymentAddress &address) const +{ + LOCK(cs_SpendingKeyStore); + return mapViewingKeys.count(address) > 0; +} + +bool CBasicKeyStore::GetViewingKey(const libzcash::PaymentAddress &address, + libzcash::ViewingKey &vkOut) const +{ + LOCK(cs_SpendingKeyStore); + ViewingKeyMap::const_iterator mi = mapViewingKeys.find(address); + if (mi != mapViewingKeys.end()) { + vkOut = mi->second; + return true; + } + return false; +} diff --git a/src/keystore.h b/src/keystore.h index 84595cfb0..b1ad32a42 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -55,12 +55,19 @@ public: virtual bool HaveSpendingKey(const libzcash::PaymentAddress &address) const =0; virtual bool GetSpendingKey(const libzcash::PaymentAddress &address, libzcash::SpendingKey& skOut) const =0; virtual void GetPaymentAddresses(std::set &setAddress) const =0; + + //! Support for viewing keys + virtual bool AddViewingKey(const libzcash::ViewingKey &vk) =0; + virtual bool RemoveViewingKey(const libzcash::ViewingKey &vk) =0; + virtual bool HaveViewingKey(const libzcash::PaymentAddress &address) const =0; + virtual bool GetViewingKey(const libzcash::PaymentAddress &address, libzcash::ViewingKey& vkOut) const =0; }; typedef std::map KeyMap; typedef std::map ScriptMap; typedef std::set WatchOnlySet; typedef std::map SpendingKeyMap; +typedef std::map ViewingKeyMap; typedef std::map NoteDecryptorMap; /** Basic key store, that keeps keys in an address->secret map */ @@ -71,6 +78,7 @@ protected: ScriptMap mapScripts; WatchOnlySet setWatchOnly; SpendingKeyMap mapSpendingKeys; + ViewingKeyMap mapViewingKeys; NoteDecryptorMap mapNoteDecryptors; public: @@ -166,8 +174,19 @@ public: setAddress.insert((*mi).first); mi++; } + ViewingKeyMap::const_iterator mvi = mapViewingKeys.begin(); + while (mvi != mapViewingKeys.end()) + { + setAddress.insert((*mvi).first); + mvi++; + } } } + + virtual bool AddViewingKey(const libzcash::ViewingKey &vk); + virtual bool RemoveViewingKey(const libzcash::ViewingKey &vk); + virtual bool HaveViewingKey(const libzcash::PaymentAddress &address) const; + virtual bool GetViewingKey(const libzcash::PaymentAddress &address, libzcash::ViewingKey& vkOut) const; }; typedef std::vector > CKeyingMaterial; diff --git a/src/komodo-tx.cpp b/src/komodo-tx.cpp index bce749d7f..da2a9ec2e 100644 --- a/src/komodo-tx.cpp +++ b/src/komodo-tx.cpp @@ -6,6 +6,7 @@ #include "clientversion.h" #include "coins.h" #include "consensus/consensus.h" +#include "consensus/upgrades.h" #include "core_io.h" #include "keystore.h" #include "primitives/transaction.h" @@ -83,7 +84,7 @@ static bool AppInitRawTx(int argc, char* argv[]) strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N")); strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", _("Add address-based output to TX")); strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT", _("Add raw script output to TX")); - strUsage += HelpMessageOpt("sign=SIGHASH-FLAGS", _("Add zero or more signatures to transaction") + ". " + + strUsage += HelpMessageOpt("sign=HEIGHT:SIGHASH-FLAGS", _("Add zero or more signatures to transaction") + ". " + _("This command requires JSON registers:") + _("prevtxs=JSON object") + ", " + _("privatekeys=JSON object") + ". " + @@ -170,12 +171,21 @@ static void RegisterLoad(const string& strInput) static void MutateTxVersion(CMutableTransaction& tx, const string& cmdVal) { int64_t newVersion = atoi64(cmdVal); - if (newVersion < CTransaction::MIN_CURRENT_VERSION || newVersion > CTransaction::MAX_CURRENT_VERSION) + if (newVersion < CTransaction::SPROUT_MIN_CURRENT_VERSION || newVersion > CTransaction::SPROUT_MAX_CURRENT_VERSION) throw runtime_error("Invalid TX version requested"); tx.nVersion = (int) newVersion; } +static void MutateTxExpiry(CMutableTransaction& tx, const string& cmdVal) +{ + int64_t newExpiry = atoi64(cmdVal); + if (newExpiry >= TX_EXPIRY_HEIGHT_THRESHOLD) { + throw runtime_error("Invalid TX expiry requested"); + } + tx.nExpiryHeight = (int) newExpiry; +} + static void MutateTxLocktime(CMutableTransaction& tx, const string& cmdVal) { int64_t newLocktime = atoi64(cmdVal); @@ -335,11 +345,39 @@ vector ParseHexUO(map& o, string strKey) return ParseHexUV(o[strKey], strKey); } +static CAmount AmountFromValue(const UniValue& value) +{ + if (!value.isNum() && !value.isStr()) + throw runtime_error("Amount is not a number or string"); + CAmount amount; + if (!ParseFixedPoint(value.getValStr(), 8, &amount)) + throw runtime_error("Invalid amount"); + if (!MoneyRange(amount)) + throw runtime_error("Amount out of range"); + return amount; +} -static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) +static void MutateTxSign(CMutableTransaction& tx, const string& strInput) { - int nHashType = SIGHASH_ALL; + // separate HEIGHT:SIGHASH-FLAGS in string + size_t pos = strInput.find(':'); + if ((pos == 0) || + (pos == (strInput.size() - 1))) + throw runtime_error("Invalid sighash flag separator"); + + // extract and validate HEIGHT + string strHeight = strInput.substr(0, pos); + int nHeight = atoi(strHeight); + if (nHeight <= 0) { + throw runtime_error("invalid height"); + } + // extract and validate SIGHASH-FLAGS + int nHashType = SIGHASH_ALL; + string flagStr; + if (pos != string::npos) { + flagStr = strInput.substr(pos + 1, string::npos); + } if (flagStr.size() > 0) if (!findSighashFlags(nHashType, flagStr)) throw runtime_error("unknown sighash flag/sign option"); @@ -407,7 +445,10 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) if ((unsigned int)nOut >= coins->vout.size()) coins->vout.resize(nOut+1); coins->vout[nOut].scriptPubKey = scriptPubKey; - coins->vout[nOut].nValue = 0; // we don't know the actual output value + coins->vout[nOut].nValue = 0; + if (prevOut.exists("amount")) { + coins->vout[nOut].nValue = AmountFromValue(prevOut["amount"]); + } } // if redeemScript given and private keys given, @@ -426,6 +467,9 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); + // Grab the consensus branch ID for the given height + auto consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus()); + // Sign what we can: for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { CTxIn& txin = mergedTx.vin[i]; @@ -435,17 +479,19 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) continue; } const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey; + const CAmount& amount = coins->vout[txin.prevout.n].nValue; - txin.scriptSig.clear(); + SignatureData sigdata; // Only sign SIGHASH_SINGLE if there's a corresponding output: if (!fHashSingle || (i < mergedTx.vout.size())) - SignSignature(keystore, prevPubKey, mergedTx, i, nHashType); + ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata, consensusBranchId); // ... and merge in other signatures: - BOOST_FOREACH(const CTransaction& txv, txVariants) { - txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); - } - if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i))) + BOOST_FOREACH(const CTransaction& txv, txVariants) + sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i), consensusBranchId); + UpdateTransaction(mergedTx, i, sigdata); + + if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i, amount), consensusBranchId)) fComplete = false; } @@ -459,9 +505,15 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) class Secp256k1Init { + ECCVerifyHandle globalVerifyHandle; + public: - Secp256k1Init() { ECC_Start(); } - ~Secp256k1Init() { ECC_Stop(); } + Secp256k1Init() { + ECC_Start(); + } + ~Secp256k1Init() { + ECC_Stop(); + } }; static void MutateTx(CMutableTransaction& tx, const string& command, @@ -473,6 +525,8 @@ static void MutateTx(CMutableTransaction& tx, const string& command, MutateTxVersion(tx, commandVal); else if (command == "locktime") MutateTxLocktime(tx, commandVal); + else if (command == "expiry") + MutateTxExpiry(tx, commandVal); else if (command == "delin") MutateTxDelInput(tx, commandVal); diff --git a/src/komodo.h b/src/komodo.h index 63569e60d..859e3db9d 100644 --- a/src/komodo.h +++ b/src/komodo.h @@ -73,7 +73,7 @@ int32_t komodo_currentheight() int32_t komodo_parsestatefile(struct komodo_state *sp,FILE *fp,char *symbol,char *dest) { static int32_t errs; - int32_t func,ht,notarized_height,num,matched=0; uint256 notarized_hash,notarized_desttxid; uint8_t pubkeys[64][33]; + int32_t func,ht,notarized_height,num,matched=0,MoMdepth; uint256 MoM,notarized_hash,notarized_desttxid; uint8_t pubkeys[64][33]; if ( (func= fgetc(fp)) != EOF ) { if ( ASSETCHAINS_SYMBOL[0] == 0 && strcmp(symbol,"KMD") == 0 ) @@ -97,7 +97,7 @@ int32_t komodo_parsestatefile(struct komodo_state *sp,FILE *fp,char *symbol,char } } else printf("illegal num.%d\n",num); } - else if ( func == 'N' ) + else if ( func == 'N' || func == 'M' ) { if ( fread(¬arized_height,1,sizeof(notarized_height),fp) != sizeof(notarized_height) ) errs++; @@ -105,10 +105,22 @@ int32_t komodo_parsestatefile(struct komodo_state *sp,FILE *fp,char *symbol,char errs++; if ( fread(¬arized_desttxid,1,sizeof(notarized_desttxid),fp) != sizeof(notarized_desttxid) ) errs++; - if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 && sp != 0 ) - printf("%s load[%s.%d -> %s] NOTARIZED %d %s\n",ASSETCHAINS_SYMBOL,symbol,sp->NUM_NPOINTS,dest,notarized_height,notarized_hash.ToString().c_str()); + if ( func == 'M' ) + { + if ( fread(&MoM,1,sizeof(MoM),fp) != sizeof(MoM) ) + errs++; + if ( fread(&MoMdepth,1,sizeof(MoMdepth),fp) != sizeof(MoMdepth) ) + errs++; + if ( 1 && ASSETCHAINS_SYMBOL[0] != 0 && sp != 0 ) + printf("%s load[%s.%d -> %s] NOTARIZED %d %s MoM.%s %d\n",ASSETCHAINS_SYMBOL,symbol,sp->NUM_NPOINTS,dest,notarized_height,notarized_hash.ToString().c_str(),MoM.ToString().c_str(),MoMdepth); + } + else + { + memset(&MoM,0,sizeof(MoM)); + MoMdepth = 0; + } //if ( matched != 0 ) global independent states -> inside *sp - komodo_eventadd_notarized(sp,symbol,ht,dest,notarized_hash,notarized_desttxid,notarized_height); + komodo_eventadd_notarized(sp,symbol,ht,dest,notarized_hash,notarized_desttxid,notarized_height);//,MoM,MoMdepth); } else if ( func == 'U' ) // deprecated { @@ -189,7 +201,7 @@ int32_t komodo_parsestatefile(struct komodo_state *sp,FILE *fp,char *symbol,char //printf("load pvals ht.%d numpvals.%d\n",ht,numpvals); } else printf("error loading pvals[%d]\n",numpvals); } - // else printf("[%s] %s illegal func.(%d %c)\n",ASSETCHAINS_SYMBOL,symbol,func,func); + else printf("[%s] %s illegal func.(%d %c)\n",ASSETCHAINS_SYMBOL,symbol,func,func); return(func); } else return(-1); } @@ -208,7 +220,7 @@ int32_t memread(void *dest,int32_t size,uint8_t *filedata,long *fposp,long datal int32_t komodo_parsestatefiledata(struct komodo_state *sp,uint8_t *filedata,long *fposp,long datalen,char *symbol,char *dest) { static int32_t errs; - int32_t func= -1,ht,notarized_height,num,matched=0; uint256 notarized_hash,notarized_desttxid; uint8_t pubkeys[64][33]; long fpos = *fposp; + int32_t func= -1,ht,notarized_height,MoMdepth,num,matched=0; uint256 MoM,notarized_hash,notarized_desttxid; uint8_t pubkeys[64][33]; long fpos = *fposp; if ( fpos < datalen ) { func = filedata[fpos++]; @@ -231,7 +243,7 @@ int32_t komodo_parsestatefiledata(struct komodo_state *sp,uint8_t *filedata,long } } else printf("illegal num.%d\n",num); } - else if ( func == 'N' ) + else if ( func == 'N' || func == 'M' ) { if ( memread(¬arized_height,sizeof(notarized_height),filedata,&fpos,datalen) != sizeof(notarized_height) ) errs++; @@ -239,10 +251,21 @@ int32_t komodo_parsestatefiledata(struct komodo_state *sp,uint8_t *filedata,long errs++; if ( memread(¬arized_desttxid,sizeof(notarized_desttxid),filedata,&fpos,datalen) != sizeof(notarized_desttxid) ) errs++; - if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 && sp != 0 ) - printf("%s load[%s.%d -> %s] NOTARIZED %d %s\n",ASSETCHAINS_SYMBOL,symbol,sp->NUM_NPOINTS,dest,notarized_height,notarized_hash.ToString().c_str()); - //if ( matched != 0 ) global independent states -> inside *sp - komodo_eventadd_notarized(sp,symbol,ht,dest,notarized_hash,notarized_desttxid,notarized_height); + if ( func == 'M' ) + { + if ( memread(&MoM,sizeof(MoM),filedata,&fpos,datalen) != sizeof(MoM) ) + errs++; + if ( memread(&MoMdepth,sizeof(MoMdepth),filedata,&fpos,datalen) != sizeof(MoMdepth) ) + errs++; + if ( 1 && ASSETCHAINS_SYMBOL[0] != 0 && sp != 0 ) + printf("%s load[%s.%d -> %s] NOTARIZED %d %s MoM.%s %d\n",ASSETCHAINS_SYMBOL,symbol,sp->NUM_NPOINTS,dest,notarized_height,notarized_hash.ToString().c_str(),MoM.ToString().c_str(),MoMdepth); + } + else + { + memset(&MoM,0,sizeof(MoM)); + MoMdepth = 0; + } + komodo_eventadd_notarized(sp,symbol,ht,dest,notarized_hash,notarized_desttxid,notarized_height);//,MoM,MoMdepth); } else if ( func == 'U' ) // deprecated { @@ -319,16 +342,16 @@ int32_t komodo_parsestatefiledata(struct komodo_state *sp,uint8_t *filedata,long //printf("load pvals ht.%d numpvals.%d\n",ht,numpvals); } else printf("error loading pvals[%d]\n",numpvals); } - // else printf("[%s] %s illegal func.(%d %c)\n",ASSETCHAINS_SYMBOL,symbol,func,func); + else printf("[%s] %s illegal func.(%d %c)\n",ASSETCHAINS_SYMBOL,symbol,func,func); *fposp = fpos; return(func); } return(-1); } -void komodo_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotaries,uint8_t notaryid,uint256 txhash,uint64_t voutmask,uint8_t numvouts,uint32_t *pvals,uint8_t numpvals,int32_t KMDheight,uint32_t KMDtimestamp,uint64_t opretvalue,uint8_t *opretbuf,uint16_t opretlen,uint16_t vout) +void komodo_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotaries,uint8_t notaryid,uint256 txhash,uint64_t voutmask,uint8_t numvouts,uint32_t *pvals,uint8_t numpvals,int32_t KMDheight,uint32_t KMDtimestamp,uint64_t opretvalue,uint8_t *opretbuf,uint16_t opretlen,uint16_t vout)//,uint256 MoM,int32_t MoMdepth) { - static FILE *fp; static int32_t errs,didinit; + static FILE *fp; static int32_t errs,didinit; static uint256 zero; struct komodo_state *sp; char fname[512],symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; int32_t retval,ht,func; uint8_t num,pubkeys[64][33]; if ( didinit == 0 ) { @@ -457,6 +480,9 @@ void komodo_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotar //printf("ht.%d func N ht.%d errs.%d\n",height,NOTARIZED_HEIGHT,errs); if ( sp != 0 ) { + //if ( sp->MoMdepth > 0 && sp->MoM != zero ) + // fputc('M',fp); + //else fputc('N',fp); if ( fwrite(&height,1,sizeof(height),fp) != sizeof(height) ) errs++; @@ -466,7 +492,14 @@ void komodo_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotar errs++; if ( fwrite(&sp->NOTARIZED_DESTTXID,1,sizeof(sp->NOTARIZED_DESTTXID),fp) != sizeof(sp->NOTARIZED_DESTTXID) ) errs++; - komodo_eventadd_notarized(sp,symbol,height,dest,sp->NOTARIZED_HASH,sp->NOTARIZED_DESTTXID,sp->NOTARIZED_HEIGHT); + /*if ( sp->MoMdepth > 0 && sp->MoM != zero ) + { + if ( fwrite(&sp->MoM,1,sizeof(sp->MoM),fp) != sizeof(sp->MoM) ) + errs++; + if ( fwrite(&sp->MoMdepth,1,sizeof(sp->MoMdepth),fp) != sizeof(sp->MoMdepth) ) + errs++; + }*/ + komodo_eventadd_notarized(sp,symbol,height,dest,sp->NOTARIZED_HASH,sp->NOTARIZED_DESTTXID,sp->NOTARIZED_HEIGHT);//,sp->MoM,sp->MoMdepth); } } fflush(fp); @@ -476,7 +509,7 @@ void komodo_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotar int32_t komodo_voutupdate(int32_t *isratificationp,int32_t notaryid,uint8_t *scriptbuf,int32_t scriptlen,int32_t height,uint256 txhash,int32_t i,int32_t j,uint64_t *voutmaskp,int32_t *specialtxp,int32_t *notarizedheightp,uint64_t value,int32_t notarized,uint64_t signedmask,uint32_t timestamp) { static uint256 zero; static FILE *signedfp; - CBlockIndex *pindex; int32_t opretlen,nid,k,len = 0; uint256 kmdtxid,desttxid; uint8_t crypto777[33]; struct komodo_state *sp; char symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; + int32_t opretlen,nid,k,len = 0; uint256 srchash,desttxid; uint8_t crypto777[33]; struct komodo_state *sp; char symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; if ( (sp= komodo_stateptr(symbol,dest)) == 0 ) return(-1); if ( scriptlen == 35 && scriptbuf[0] == 33 && scriptbuf[34] == 0xac ) @@ -534,23 +567,59 @@ int32_t komodo_voutupdate(int32_t *isratificationp,int32_t notaryid,uint8_t *scr printf("[%s] notarized.%d notarizedht.%d sp.Nht %d sp.ht %d opretlen.%d (%c %c %c)\n",ASSETCHAINS_SYMBOL,notarized,*notarizedheightp,sp->NOTARIZED_HEIGHT,sp->CURRENT_HEIGHT,opretlen,scriptbuf[len+32*2+4],scriptbuf[len+32*2+4+1],scriptbuf[len+32*2+4+2]); if ( j == 1 && opretlen >= 32*2+4 && strcmp(ASSETCHAINS_SYMBOL[0]==0?"KMD":ASSETCHAINS_SYMBOL,(char *)&scriptbuf[len+32*2+4]) == 0 ) { - len += iguana_rwbignum(0,&scriptbuf[len],32,(uint8_t *)&kmdtxid); + len += iguana_rwbignum(0,&scriptbuf[len],32,(uint8_t *)&srchash); len += iguana_rwnum(0,&scriptbuf[len],sizeof(*notarizedheightp),(uint8_t *)notarizedheightp); len += iguana_rwbignum(0,&scriptbuf[len],32,(uint8_t *)&desttxid); - if ( notarized != 0 && IsInitialBlockDownload() == 0 && ((pindex= mapBlockIndex[kmdtxid]) == 0 || pindex->nHeight != *notarizedheightp) ) + if ( strcmp("PIZZA",ASSETCHAINS_SYMBOL) == 0 && opretlen == 110 ) { - fprintf(stderr,"%s possible FORK detected. notarized.%d %s not in this chain! last notarization %d -> rewindtarget.%d\n",ASSETCHAINS_SYMBOL,*notarizedheightp,kmdtxid.ToString().c_str(),sp->NOTARIZED_HEIGHT,sp->NOTARIZED_HEIGHT); - notarized = 0; + notarized = 1; } - if ( notarized != 0 && *notarizedheightp > sp->NOTARIZED_HEIGHT && *notarizedheightp < height && (height < sp->CURRENT_HEIGHT-1000 || komodo_verifynotarization(ASSETCHAINS_SYMBOL[0]==0?(char *)"KMD":ASSETCHAINS_SYMBOL,(char *)(ASSETCHAINS_SYMBOL[0] == 0 ? "BTC" : "KMD"),height,*notarizedheightp,kmdtxid,desttxid) == 0) ) + static int32_t last_rewind; + int32_t rewindtarget,validated = 0; + CBlockIndex *pindex;// + if ( IsInitialBlockDownload() == 0 && ((pindex= mapBlockIndex[srchash]) == 0 || pindex->nHeight != *notarizedheightp) ) + { + if ( sp->NOTARIZED_HEIGHT > 0 && sp->NOTARIZED_HEIGHT < *notarizedheightp ) + rewindtarget = sp->NOTARIZED_HEIGHT - 1; + else if ( *notarizedheightp > 101 ) + rewindtarget = *notarizedheightp - 101; + else rewindtarget = 0; + if ( rewindtarget != 0 && rewindtarget > KOMODO_REWIND && rewindtarget > last_rewind ) + { + if ( last_rewind != 0 ) + { + //KOMODO_REWIND = rewindtarget; + fprintf(stderr,"%s FORK detected. notarized.%d %s not in this chain! last notarization %d -> rewindtarget.%d\n",ASSETCHAINS_SYMBOL,*notarizedheightp,srchash.ToString().c_str(),sp->NOTARIZED_HEIGHT,rewindtarget); + } + last_rewind = rewindtarget; + } + } else validated = 1; + if ( notarized != 0 && *notarizedheightp > sp->NOTARIZED_HEIGHT && *notarizedheightp < height && validated != 0 ) { + int32_t nameoffset = (int32_t)strlen(ASSETCHAINS_SYMBOL) + 1; sp->NOTARIZED_HEIGHT = *notarizedheightp; - sp->NOTARIZED_HASH = kmdtxid; + sp->NOTARIZED_HASH = srchash; sp->NOTARIZED_DESTTXID = desttxid; - komodo_stateupdate(height,0,0,0,zero,0,0,0,0,0,0,0,0,0,0); - len += 4; - if ( ASSETCHAINS_SYMBOL[0] != 0 ) - printf("[%s] ht.%d NOTARIZED.%d %s.%s %sTXID.%s lens.(%d %d)\n",ASSETCHAINS_SYMBOL,height,*notarizedheightp,ASSETCHAINS_SYMBOL[0]==0?"KMD":ASSETCHAINS_SYMBOL,kmdtxid.ToString().c_str(),ASSETCHAINS_SYMBOL[0]==0?"BTC":"KMD",desttxid.ToString().c_str(),opretlen,len); + /*memset(&sp->MoM,0,sizeof(sp->MoM)); + sp->MoMdepth = 0; + if ( len+36 <= opretlen ) + { + len += iguana_rwbignum(0,&scriptbuf[len+nameoffset],32,(uint8_t *)&sp->MoM); + len += iguana_rwnum(0,&scriptbuf[len+nameoffset],sizeof(sp->MoMdepth),(uint8_t *)&sp->MoMdepth); + if ( sp->MoM == zero || sp->MoMdepth > 1440 || sp->MoMdepth < 0 ) + { + memset(&sp->MoM,0,sizeof(sp->MoM)); + sp->MoMdepth = 0; + } + else + { + //printf("VALID %s MoM.%s [%d]\n",ASSETCHAINS_SYMBOL,sp->MoM.ToString().c_str(),sp->MoMdepth); + } + }*/ + komodo_stateupdate(height,0,0,0,zero,0,0,0,0,0,0,0,0,0,0);//,sp->MoM,sp->MoMdepth); + len += nameoffset; + //if ( ASSETCHAINS_SYMBOL[0] != 0 ) + // printf("[%s] ht.%d NOTARIZED.%d %s.%s %sTXID.%s lens.(%d %d) MoM.%s %d\n",ASSETCHAINS_SYMBOL,height,*notarizedheightp,ASSETCHAINS_SYMBOL[0]==0?"KMD":ASSETCHAINS_SYMBOL,srchash.ToString().c_str(),ASSETCHAINS_SYMBOL[0]==0?"BTC":"KMD",desttxid.ToString().c_str(),opretlen,len,sp->MoM.ToString().c_str(),sp->MoMdepth); if ( ASSETCHAINS_SYMBOL[0] == 0 ) { if ( signedfp == 0 ) @@ -572,11 +641,11 @@ int32_t komodo_voutupdate(int32_t *isratificationp,int32_t notaryid,uint8_t *scr //for (i=0; i= KOMODO_MAINNET_START ) - printf("notarized.%d %llx reject ht.%d NOTARIZED.%d prev.%d %s.%s DESTTXID.%s (%s)\n",notarized,(long long)signedmask,height,*notarizedheightp,sp->NOTARIZED_HEIGHT,ASSETCHAINS_SYMBOL[0]==0?"KMD":ASSETCHAINS_SYMBOL,kmdtxid.ToString().c_str(),desttxid.ToString().c_str(),(char *)&scriptbuf[len]); + } else if ( *notarizedheightp != sp->NOTARIZED_HEIGHT ) + printf("validated.%d notarized.%d %llx reject ht.%d NOTARIZED.%d prev.%d %s.%s DESTTXID.%s (%s) len.%d opretlen.%d\n",validated,notarized,(long long)signedmask,height,*notarizedheightp,sp->NOTARIZED_HEIGHT,ASSETCHAINS_SYMBOL[0]==0?"KMD":ASSETCHAINS_SYMBOL,srchash.ToString().c_str(),desttxid.ToString().c_str(),(char *)&scriptbuf[len],len,opretlen); } else if ( i == 0 && j == 1 && opretlen == 149 ) { @@ -601,7 +670,7 @@ int32_t komodo_voutupdate(int32_t *isratificationp,int32_t notaryid,uint8_t *scr } if ( *isratificationp == 0 && (signedmask != 0 || (scriptbuf[len] != 'X' && scriptbuf[len] != 'A')) ) // && scriptbuf[len] != 'I') - komodo_stateupdate(height,0,0,0,txhash,0,0,0,0,0,0,value,&scriptbuf[len],opretlen,j); + komodo_stateupdate(height,0,0,0,txhash,0,0,0,0,0,0,value,&scriptbuf[len],opretlen,j);//,zero,0); } } return(notaryid); @@ -640,7 +709,7 @@ void komodo_connectblock(CBlockIndex *pindex,CBlock& block) { static int32_t hwmheight; uint64_t signedmask,voutmask; char symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; struct komodo_state *sp; - uint8_t scriptbuf[4096],pubkeys[64][33],rmd160[20],scriptPubKey[35]; uint256 kmdtxid,zero,btctxid,txhash; + uint8_t scriptbuf[10001],pubkeys[64][33],rmd160[20],scriptPubKey[35]; uint256 zero,btctxid,txhash; int32_t i,j,k,numnotaries,notarized,scriptlen,isratification,nid,numvalid,specialtx,notarizedheight,notaryid,len,numvouts,numvins,height,txn_count; memset(&zero,0,sizeof(zero)); komodo_init(pindex->nHeight); @@ -660,7 +729,7 @@ void komodo_connectblock(CBlockIndex *pindex,CBlock& block) if ( pindex->nHeight != hwmheight ) printf("%s hwmheight.%d vs pindex->nHeight.%d t.%u reorg.%d\n",ASSETCHAINS_SYMBOL,hwmheight,pindex->nHeight,(uint32_t)pindex->nTime,hwmheight-pindex->nHeight); komodo_event_rewind(sp,symbol,pindex->nHeight); - komodo_stateupdate(pindex->nHeight,0,0,0,zero,0,0,0,0,-pindex->nHeight,pindex->nTime,0,0,0,0); + komodo_stateupdate(pindex->nHeight,0,0,0,zero,0,0,0,0,-pindex->nHeight,pindex->nTime,0,0,0,0);//,zero,0); } komodo_currentheight_set(chainActive.Tip()->nHeight); if ( pindex != 0 ) @@ -792,7 +861,7 @@ void komodo_connectblock(CBlockIndex *pindex,CBlock& block) if ( ((signedmask & 1) != 0 && numvalid >= KOMODO_MINRATIFY) || bitweight(signedmask) > (numnotaries/3) ) { memset(&txhash,0,sizeof(txhash)); - komodo_stateupdate(height,pubkeys,numvalid,0,txhash,0,0,0,0,0,0,0,0,0,0); + komodo_stateupdate(height,pubkeys,numvalid,0,txhash,0,0,0,0,0,0,0,0,0,0);//,zero,0); printf("RATIFIED! >>>>>>>>>> new notaries.%d newheight.%d from height.%d\n",numvalid,(((height+KOMODO_ELECTION_GAP/2)/KOMODO_ELECTION_GAP)+1)*KOMODO_ELECTION_GAP,height); } else printf("signedmask.%llx numvalid.%d wt.%d numnotaries.%d\n",(long long)signedmask,numvalid,bitweight(signedmask),numnotaries); } @@ -801,7 +870,7 @@ void komodo_connectblock(CBlockIndex *pindex,CBlock& block) if ( NOTARY_PUBKEY33[0] != 0 && ASSETCHAINS_SYMBOL[0] == 0 ) printf("%s ht.%d\n",ASSETCHAINS_SYMBOL[0] == 0 ? "KMD" : ASSETCHAINS_SYMBOL,height); if ( pindex->nHeight == hwmheight ) - komodo_stateupdate(height,0,0,0,zero,0,0,0,0,height,(uint32_t)pindex->nTime,0,0,0,0); + komodo_stateupdate(height,0,0,0,zero,0,0,0,0,height,(uint32_t)pindex->nTime,0,0,0,0);//,zero,0); } else fprintf(stderr,"komodo_connectblock: unexpected null pindex\n"); //KOMODO_INITDONE = (uint32_t)time(NULL); //fprintf(stderr,"%s end connect.%d\n",ASSETCHAINS_SYMBOL,pindex->nHeight); diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index e9ddd4fc0..afaba8fe1 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -804,7 +804,7 @@ void komodo_index2pubkey33(uint8_t *pubkey33,CBlockIndex *pindex,int32_t height) return(komodo_electednotary(&numnotaries,pubkey33,height,timestamp)); }*/ -int32_t komodo_eligiblenotary(uint8_t pubkeys[66][33],int32_t *mids,int32_t *nonzpkeysp,int32_t height) +int32_t komodo_eligiblenotary(uint8_t pubkeys[66][33],int32_t *mids,uint32_t blocktimes[66],int32_t *nonzpkeysp,int32_t height) { int32_t i,j,n,duplicate; CBlock block; CBlockIndex *pindex; uint8_t notarypubs33[64][33]; memset(mids,-1,sizeof(*mids)*66); @@ -813,6 +813,7 @@ int32_t komodo_eligiblenotary(uint8_t pubkeys[66][33],int32_t *mids,int32_t *non { if ( (pindex= komodo_chainactive(height-i)) != 0 ) { + blocktimes[i] = pindex->nTime; if ( komodo_blockload(block,pindex) == 0 ) { komodo_block2pubkey33(pubkeys[i],&block); @@ -849,10 +850,10 @@ int32_t komodo_minerids(uint8_t *minerids,int32_t height,int32_t width) // depre return(-1); } -int32_t komodo_is_special(uint8_t pubkeys[66][33],int32_t mids[66],int32_t height,uint8_t pubkey33[33],uint32_t timestamp) +int32_t komodo_is_special(uint8_t pubkeys[66][33],int32_t mids[66],uint32_t blocktimes[66],int32_t height,uint8_t pubkey33[33],uint32_t blocktime) { int32_t i,j,notaryid=0,minerid,limit,nid; uint8_t destpubkey33[33]; - komodo_chosennotary(¬aryid,height,pubkey33,timestamp); + komodo_chosennotary(¬aryid,height,pubkey33,blocktimes[0]); if ( height >= 82000 ) { if ( notaryid >= 0 ) @@ -861,14 +862,21 @@ int32_t komodo_is_special(uint8_t pubkeys[66][33],int32_t mids[66],int32_t heigh { if ( mids[i] == notaryid ) { - //for (j=0; j<66; j++) - // fprintf(stderr,"%d ",mids[j]); - //fprintf(stderr,"ht.%d repeat notaryid.%d in mids[%d]\n",height,notaryid,i); if ( height > 792000 ) + { + //for (j=0; j<66; j++) + // fprintf(stderr,"%d ",mids[j]); + //fprintf(stderr,"ht.%d repeat notaryid.%d in mids[%d]\n",height,notaryid,i); return(-1); - else break; + } else break; } } + if ( blocktime != 0 && blocktimes[1] != 0 && blocktime < blocktimes[1]+57 ) + { + //fprintf(stderr,"lag.%d ht.%d n.%d blocktimes[%u vs %u %u]\n",blocktime-blocktimes[1],height,notaryid,blocktime,blocktimes[0],blocktimes[1]); + if ( height > 807000 ) + return(-2); + } return(1); } else return(0); } @@ -883,7 +891,7 @@ int32_t komodo_is_special(uint8_t pubkeys[66][33],int32_t mids[66],int32_t heigh else limit = 66; for (i=1; i %.8f\n",txn_count,n,dstr(total),dstr((total * ASSETCHAINS_COMMISSION) / COIN)); + return((total * ASSETCHAINS_COMMISSION) / COIN); +} + +uint32_t komodo_stake(int32_t validateflag,arith_uint256 bnTarget,int32_t nHeight,uint256 txid,int32_t vout,uint32_t blocktime,uint32_t prevtime,char *destaddr) +{ + CBlockIndex *pindex; uint8_t hashbuf[128]; char address[64]; bits256 addrhash; arith_uint256 hashval; uint256 hash,pasthash; int64_t diff=0; int32_t segid,minage,i,iter=0; uint32_t txtime,winner = 0; uint64_t value,coinage,supply = ASSETCHAINS_SUPPLY + nHeight*ASSETCHAINS_REWARD/SATOSHIDEN; + txtime = komodo_txtime(&value,txid,vout,address); + if ( value == 0 || txtime == 0 ) + return(0); + if ( (minage= nHeight*3) > 6000 ) + minage = 6000; + if ( blocktime > txtime+minage && (pindex= komodo_chainactive(nHeight>200?nHeight-200:1)) != 0 ) + { + vcalc_sha256(0,(uint8_t *)&addrhash,(uint8_t *)address,(int32_t)strlen(address)); + segid = ((nHeight + addrhash.uints[0]) & 0x3f); + pasthash = pindex->GetBlockHash(); + memcpy(hashbuf,&pasthash,sizeof(pasthash)); + memcpy(&hashbuf[sizeof(pasthash)],&addrhash,sizeof(addrhash)); + vcalc_sha256(0,(uint8_t *)&hash,hashbuf,(int32_t)sizeof(uint256)*2); + //fprintf(stderr,"(%s) vs. (%s) %s %.8f txtime.%u\n",address,destaddr,hash.ToString().c_str(),dstr(value),txtime); + for (iter=0; iter<3600; iter++) + { + diff = (iter + blocktime - txtime - minage); + if ( diff > 3600*24 ) + break; + coinage = (value * diff) * ((diff >> 16) + 1); + hashval = arith_uint256(supply * 64) * (UintToArith256(hash) / arith_uint256(coinage+1)); + if ( hashval <= bnTarget ) + { + winner = 1; + if ( validateflag == 0 ) + { + blocktime += iter; + blocktime += segid * 2; + } + break; + } + if ( validateflag != 0 ) + { + for (i=31; i>=24; i--) + fprintf(stderr,"%02x",((uint8_t *)&hashval)[i]); + fprintf(stderr," vs target "); + for (i=31; i>=24; i--) + fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]); + fprintf(stderr," segid.%d iter.%d winner.%d coinage.%llu %d ht.%d gap.%d %.8f diff.%d\n",segid,iter,winner,(long long)coinage,(int32_t)(blocktime - txtime),nHeight,(int32_t)(blocktime - prevtime),dstr(value),(int32_t)diff); + break; + } + } + //fprintf(stderr,"iterated until i.%d winner.%d\n",i,winner); + if ( 0 ) + { + for (i=31; i>=24; i--) + fprintf(stderr,"%02x",((uint8_t *)&hashval)[i]); + fprintf(stderr," vs "); + for (i=31; i>=24; i--) + fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]); + fprintf(stderr," segid.%d iter.%d winner.%d coinage.%llu %d ht.%d t.%u %.8f diff.%d\n",segid,iter,winner,(long long)coinage,(int32_t)(blocktime - txtime),nHeight,blocktime,dstr(value),(int32_t)diff); + } + } + if ( nHeight < 2 ) + return(blocktime); + return(blocktime * winner); +} + +#define KOMODO_POWMINMULT 16 +arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc) +{ + CBlockIndex *pindex; arith_uint256 bnTarget,hashval,sum,ave; bool fNegative,fOverflow; int32_t i,n,ht,percPoS,diff; + *percPoSp = percPoS = 0; + sum = arith_uint256(0); + ave = sum; + for (i=n=0; i<100; i++) + { + ht = height - 100 + i; + if ( (pindex= komodo_chainactive(ht)) != 0 ) + { + bnTarget.SetCompact(pindex->nBits,&fNegative,&fOverflow); + bnTarget = (bnTarget / arith_uint256(KOMODO_POWMINMULT)); + hashval = UintToArith256(pindex->GetBlockHash()); + if ( hashval <= bnTarget ) // PoW is never as easy as PoS/64, some PoS will be counted as PoW + { + sum += hashval; + n++; + } else percPoS++; + } + } + *percPoSp = percPoS; + target = (target / arith_uint256(KOMODO_POWMINMULT)); + if ( n > 0 ) + { + ave = (sum / arith_uint256(n)); + if ( ave > target ) + ave = target; + } else return(target); + if ( percPoS < goalperc ) // increase PoW diff -> lower bnTarget + { + bnTarget = (ave * arith_uint256(goalperc)) / arith_uint256(percPoS + goalperc); + if ( 1 ) + { + for (i=31; i>=24; i--) + fprintf(stderr,"%02x",((uint8_t *)&ave)[i]); + fprintf(stderr," increase diff -> "); + for (i=31; i>=24; i--) + fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]); + fprintf(stderr," floor diff "); + for (i=31; i>=24; i--) + fprintf(stderr,"%02x",((uint8_t *)&target)[i]); + fprintf(stderr," ht.%d percPoS.%d vs goal.%d -> diff %d\n",height,percPoS,goalperc,goalperc - percPoS); + } + } + else if ( percPoS > goalperc ) // decrease PoW diff -> raise bnTarget + { + bnTarget = ((ave * arith_uint256(goalperc)) + (target * arith_uint256(percPoS))) / arith_uint256(percPoS + goalperc); + if ( 1 ) + { + for (i=31; i>=24; i--) + fprintf(stderr,"%02x",((uint8_t *)&ave)[i]); + fprintf(stderr," decrease diff -> "); + for (i=31; i>=24; i--) + fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]); + fprintf(stderr," floor diff "); + for (i=31; i>=24; i--) + fprintf(stderr,"%02x",((uint8_t *)&target)[i]); + fprintf(stderr," ht.%d percPoS.%d vs goal.%d -> diff %d\n",height,percPoS,goalperc,goalperc - percPoS); + } + } + else bnTarget = ave; // recent ave is perfect + return(bnTarget); +} + +int32_t komodo_check_deposit(int32_t height,const CBlock& block,uint32_t prevtime) // verify above block is valid pax pricing { static uint256 array[64]; static int32_t numbanned,indallvouts; - int32_t i,j,k,n,ht,baseid,txn_count,activation,num,opretlen,offset=1,errs=0,matched=0,kmdheights[256],otherheights[256]; uint256 hash,txids[256]; char symbol[KOMODO_ASSETCHAIN_MAXLEN],base[KOMODO_ASSETCHAIN_MAXLEN]; uint16_t vouts[256]; int8_t baseids[256]; uint8_t *script,opcode,rmd160s[256*20]; uint64_t total,available,deposited,issued,withdrawn,approved,redeemed,checktoshis,seed; int64_t values[256],srcvalues[256]; struct pax_transaction *pax; struct komodo_state *sp; + int32_t i,j,k,n,ht,baseid,txn_count,activation,num,opretlen,offset=1,errs=0,matched=0,kmdheights[256],otherheights[256]; uint256 hash,txids[256]; char symbol[KOMODO_ASSETCHAIN_MAXLEN],base[KOMODO_ASSETCHAIN_MAXLEN]; uint16_t vouts[256]; int8_t baseids[256]; uint8_t *script,opcode,rmd160s[256*20]; uint64_t total,subsidy,available,deposited,issued,withdrawn,approved,redeemed,checktoshis,seed; int64_t values[256],srcvalues[256]; struct pax_transaction *pax; struct komodo_state *sp; activation = 235300; if ( *(int32_t *)&array[0] == 0 ) numbanned = komodo_bannedset(&indallvouts,array,(int32_t)(sizeof(array)/sizeof(*array))); @@ -684,7 +831,7 @@ int32_t komodo_check_deposit(int32_t height,const CBlock& block) // verify above } n = block.vtx[0].vout.size(); script = (uint8_t *)block.vtx[0].vout[n-1].scriptPubKey.data(); - if ( n <= 2 || script[0] != 0x6a ) + //if ( n <= 2 || script[0] != 0x6a ) { int64_t val,prevtotal = 0; int32_t overflow = 0; total = 0; @@ -711,14 +858,74 @@ int32_t komodo_check_deposit(int32_t height,const CBlock& block) // verify above if ( height >= activation ) return(-1); } + else if ( block.nBits == KOMODO_MINDIFF_NBITS && total > 0 ) // to deal with fee stealing + { + fprintf(stderr,"notary mined ht.%d with extra %.8f\n",height,dstr(total)); + if ( height > KOMODO_NOTARIES_HEIGHT1 ) + return(-1); + } } else { + if ( ASSETCHAINS_STAKED != 0 && height >= 2 ) + { + arith_uint256 bnTarget,hashval; int32_t PoSperc; bool fNegative,fOverflow; CBlockIndex *previndex; uint32_t eligible,isPoS = 0; + bnTarget.SetCompact(block.nBits, &fNegative, &fOverflow); + if ( txn_count > 1 ) + { + if ( prevtime == 0 ) + { + if ( (previndex= mapBlockIndex[block.hashPrevBlock]) != 0 ) + prevtime = (uint32_t)previndex->nTime; + } + eligible = komodo_stake(1,bnTarget,height,block.vtx[txn_count-1].vin[0].prevout.hash,block.vtx[txn_count-1].vin[0].prevout.n,block.nTime,prevtime,(char *)""); + if ( eligible == 0 || eligible > block.nTime ) + { + fprintf(stderr,"PoS failure ht.%d eligible.%u vs blocktime.%u, lag.%d -> check to see if it is PoW block\n",height,eligible,(uint32_t)block.nTime,(int32_t)(eligible - block.nTime)); + } else isPoS = 1; + } + if ( isPoS == 0 && height > 100 ) + { + if ( ASSETCHAINS_STAKED == 100 ) + { + fprintf(stderr,"ht.%d 100%% PoS after height 100 rule violated for -ac_staking=100\n",height); + return(-1); + } + // check PoW + bnTarget = komodo_PoWtarget(&PoSperc,bnTarget,height,ASSETCHAINS_STAKED); + hashval = UintToArith256(block.GetHash()); + if ( hashval > bnTarget ) + { + /*for (i=31; i>=0; i--) + fprintf(stderr,"%02x",((uint8_t *)&hashval)[i]); + fprintf(stderr," > "); + for (i=31; i>=0; i--) + fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]); + fprintf(stderr," ht.%d PoW diff violation PoSperc.%d vs goalperc.%d\n",height,PoSperc,(int32_t)ASSETCHAINS_STAKED);*/ + return(-1); + } + } + } + if ( ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 && ASSETCHAINS_COMMISSION != 0 && block.vtx[0].vout.size() > 1 ) + { + script = (uint8_t *)block.vtx[0].vout[1].scriptPubKey.data(); + if ( script[0] != 33 || script[34] != OP_CHECKSIG || memcmp(script+1,ASSETCHAINS_OVERRIDE_PUBKEY33,33) != 0 ) + return(-1); + if ( (checktoshis = komodo_commission(block)) != 0 ) + { + if ( block.vtx[0].vout[1].nValue != checktoshis ) + { + fprintf(stderr,"checktoshis %.8f vs actual vout[1] %.8f\n",dstr(checktoshis),dstr(block.vtx[0].vout[1].nValue)); + return(-1); + } else return(0); + } + } if ( overflow != 0 || total > 0 ) return(-1); } return(0); } +/* //fprintf(stderr,"ht.%d n.%d nValue %.8f (%d %d %d)\n",height,n,dstr(block.vtx[0].vout[1].nValue),KOMODO_PAX,komodo_isrealtime(&ht),KOMODO_PASSPORT_INITDONE); offset += komodo_scriptitemlen(&opretlen,&script[offset]); //printf("offset.%d opretlen.%d [%02x %02x %02x %02x]\n",offset,opretlen,script[0],script[1],script[2],script[3]); @@ -987,16 +1194,16 @@ int32_t komodo_check_deposit(int32_t height,const CBlock& block) // verify above } if ( ASSETCHAINS_SYMBOL[0] == 0 ) { - /*if ( height > 0 && (height < chainActive.Tip()->nHeight || (height >= chainActive.Tip()->nHeight && komodo_isrealtime(&ht) != 0)) && matched != num ) - { - printf("WOULD REJECT %s: ht.%d (%c) matched.%d vs num.%d tip.%d isRT.%d\n",symbol,height,opcode,matched,num,(int32_t)chainActive.Tip()->nHeight,komodo_isrealtime(&ht)); + //if ( height > 0 && (height < chainActive.Tip()->nHeight || (height >= chainActive.Tip()->nHeight && komodo_isrealtime(&ht) != 0)) && matched != num ) + //{ + // printf("WOULD REJECT %s: ht.%d (%c) matched.%d vs num.%d tip.%d isRT.%d\n",symbol,height,opcode,matched,num,(int32_t)chainActive.Tip()->nHeight,komodo_isrealtime(&ht)); // can easily happen depending on order of loading - if ( height > 200000 ) - { - printf("REJECT: ht.%d (%c) matched.%d vs num.%d\n",height,opcode,matched,num); - return(-1); - } - }*/ // disabled 'X' path + //if ( height > 200000 ) + //{ + // printf("REJECT: ht.%d (%c) matched.%d vs num.%d\n",height,opcode,matched,num); + // return(-1); + //} + //} // disabled 'X' path } else { @@ -1025,7 +1232,7 @@ int32_t komodo_check_deposit(int32_t height,const CBlock& block) // verify above printf("not proper vout with opreturn format %s ht.%d cmp.%d %d\n",ASSETCHAINS_SYMBOL,height,script[offset] == opcode,(int32_t)block.vtx[0].vout[n-1].scriptPubKey.size()); return(-1); } - return(0); + return(0);*/ } const char *komodo_opreturn(int32_t height,uint64_t value,uint8_t *opretbuf,int32_t opretlen,uint256 txid,uint16_t vout,char *source) diff --git a/src/komodo_globals.h b/src/komodo_globals.h index 2a7cdaf3d..00eb4ac1d 100644 --- a/src/komodo_globals.h +++ b/src/komodo_globals.h @@ -18,10 +18,10 @@ uint32_t komodo_heightstamp(int32_t height); void komodo_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotaries,uint8_t notaryid,uint256 txhash,uint64_t voutmask,uint8_t numvouts,uint32_t *pvals,uint8_t numpvals,int32_t kheight,uint32_t ktime,uint64_t opretvalue,uint8_t *opretbuf,uint16_t opretlen,uint16_t vout); void komodo_init(int32_t height); +//int32_t komodo_MoMdata(int32_t *notarized_htp,uint256 *MoMp,uint256 *kmdtxidp,int32_t nHeight,uint256 *MoMoMp,int32_t *MoMoMoffsetp,int32_t *MoMoMdepthp,int32_t *kmdstartip,int32_t *kmdendip); int32_t komodo_notarizeddata(int32_t nHeight,uint256 *notarized_hashp,uint256 *notarized_desttxidp); char *komodo_issuemethod(char *userpass,char *method,char *params,uint16_t port); void komodo_init(int32_t height); -//void komodo_assetchain_pubkeys(char *jsonstr); int32_t komodo_chosennotary(int32_t *notaryidp,int32_t height,uint8_t *pubkey33,uint32_t timestamp); int32_t komodo_isrealtime(int32_t *kmdheightp); uint64_t komodo_paxtotal(); @@ -45,15 +45,15 @@ struct komodo_state KOMODO_STATES[34]; int COINBASE_MATURITY = _COINBASE_MATURITY;//100; int32_t IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,ASSETCHAINS_SEED,KOMODO_ON_DEMAND,KOMODO_EXTERNAL_NOTARIES,KOMODO_PASSPORT_INITDONE,KOMODO_PAX,KOMODO_EXCHANGEWALLET,KOMODO_REWIND; -int32_t KOMODO_LASTMINED,prevKOMODO_LASTMINED,JUMBLR_PAUSE; -std::string NOTARY_PUBKEY,ASSETCHAINS_NOTARIES; -uint8_t NOTARY_PUBKEY33[33]; +int32_t KOMODO_LASTMINED,prevKOMODO_LASTMINED,JUMBLR_PAUSE,ASSETCHAINS_CC; +std::string NOTARY_PUBKEY,ASSETCHAINS_NOTARIES,ASSETCHAINS_OVERRIDE_PUBKEY; +uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33]; -char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; +char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN],ASSETCHAINS_USERPASS[4096]; uint16_t ASSETCHAINS_PORT; uint32_t ASSETCHAIN_INIT; uint32_t ASSETCHAINS_MAGIC = 2387029918; -uint64_t ASSETCHAINS_SUPPLY = 10; +uint64_t ASSETCHAINS_ENDSUBSIDY,ASSETCHAINS_REWARD,ASSETCHAINS_HALVING,ASSETCHAINS_DECAY,ASSETCHAINS_COMMISSION,ASSETCHAINS_STAKED,ASSETCHAINS_SUPPLY = 10; uint32_t KOMODO_INITDONE; char KMDUSERPASS[4096],BTCUSERPASS[4096]; uint16_t KMD_PORT = 7771,BITCOIND_PORT = 7771; diff --git a/src/komodo_interest.h b/src/komodo_interest.h index 63d0152ca..07b12cfe1 100644 --- a/src/komodo_interest.h +++ b/src/komodo_interest.h @@ -19,8 +19,9 @@ #define dstr(x) ((double)(x) / SATOSHIDEN) #define KOMODO_ENDOFERA 7777777 -#define KOMODO_INTEREST ((uint64_t)(0.05 * COIN)) // 5% +#define KOMODO_INTEREST ((uint64_t)5000000) //((uint64_t)(0.05 * COIN)) // 5% int64_t MAX_MONEY = 200000000 * 100000000LL; +extern uint8_t NOTARY_PUBKEY33[]; #ifdef notanymore uint64_t komodo_earned_interest(int32_t height,int64_t paidinterest) @@ -82,7 +83,7 @@ uint64_t komodo_moneysupply(int32_t height) uint64_t _komodo_interestnew(uint64_t nValue,uint32_t nLockTime,uint32_t tiptime) { int32_t minutes; uint64_t interest = 0; - if ( tiptime > nLockTime && (minutes= (tiptime - nLockTime) / 60) >= 60 ) + if ( nLockTime >= LOCKTIME_THRESHOLD && tiptime > nLockTime && (minutes= (tiptime - nLockTime) / 60) >= 60 ) { if ( minutes > 365 * 24 * 60 ) minutes = 365 * 24 * 60; @@ -152,7 +153,7 @@ uint64_t komodo_interest(int32_t txheight,uint64_t nValue,uint32_t nLockTime,uin interest = (numerator * minutes) / ((uint64_t)365 * 24 * 60); interestnew = _komodo_interestnew(nValue,nLockTime,tiptime); if ( interest < interestnew ) - printf("path0 current interest %.8f vs new %.8f for ht.%d %.8f locktime.%u tiptime.%u\n",dstr(interest),dstr(interestnew),txheight,dstr(nValue),nLockTime,tiptime); + printf("pathA current interest %.8f vs new %.8f for ht.%d %.8f locktime.%u tiptime.%u\n",dstr(interest),dstr(interestnew),txheight,dstr(nValue),nLockTime,tiptime); } else interest = _komodo_interestnew(nValue,nLockTime,tiptime); } @@ -162,7 +163,7 @@ uint64_t komodo_interest(int32_t txheight,uint64_t nValue,uint32_t nLockTime,uin interest = (numerator / denominator) / COIN; interestnew = _komodo_interestnew(nValue,nLockTime,tiptime); if ( interest < interestnew ) - printf("path0 current interest %.8f vs new %.8f for ht.%d %.8f locktime.%u tiptime.%u\n",dstr(interest),dstr(interestnew),txheight,dstr(nValue),nLockTime,tiptime); + printf("pathB current interest %.8f vs new %.8f for ht.%d %.8f locktime.%u tiptime.%u\n",dstr(interest),dstr(interestnew),txheight,dstr(nValue),nLockTime,tiptime); } else interest = _komodo_interestnew(nValue,nLockTime,tiptime); } @@ -187,8 +188,8 @@ uint64_t komodo_interest(int32_t txheight,uint64_t nValue,uint32_t nLockTime,uin interest = ((numerator * minutes) / ((uint64_t)365 * 24 * 60)); //fprintf(stderr,"interest %llu %.8f <- numerator.%llu minutes.%d\n",(long long)interest,(double)interest/COIN,(long long)numerator,(int32_t)minutes); interestnew = _komodo_interestnew(nValue,nLockTime,tiptime); - if ( interest < interestnew )//|| (interestnew < 0.9999*interest && (interest-interestnew) > 50000) ) - printf("path1 current interest %.8f vs new %.8f for ht.%d %.8f locktime.%u tiptime.%u\n",dstr(interest),dstr(interestnew),txheight,dstr(nValue),nLockTime,tiptime); + if ( interest < interestnew ) + fprintf(stderr,"pathC current interest %.8f vs new %.8f for ht.%d %.8f locktime.%u tiptime.%u\n",dstr(interest),dstr(interestnew),txheight,dstr(nValue),nLockTime,tiptime); } else interest = _komodo_interestnew(nValue,nLockTime,tiptime); } diff --git a/src/komodo_kv.h b/src/komodo_kv.h index 9aca2d387..7c20becb1 100644 --- a/src/komodo_kv.h +++ b/src/komodo_kv.h @@ -182,7 +182,7 @@ void komodo_kvupdate(uint8_t *opretbuf,int32_t opretlen,uint64_t value) ptr->value = (uint8_t *)calloc(1,valuesize); memcpy(ptr->value,valueptr,valuesize); } - } + } else fprintf(stderr,"newflag.%d zero or protected %d\n",newflag,(ptr->flags & KOMODO_KVPROTECTED)); /*for (i=0; i<32; i++) printf("%02x",((uint8_t *)&ptr->pubkey)[i]); printf(" <- "); @@ -191,10 +191,10 @@ void komodo_kvupdate(uint8_t *opretbuf,int32_t opretlen,uint64_t value) printf(" new pubkey\n");*/ memcpy(&ptr->pubkey,&pubkey,sizeof(ptr->pubkey)); ptr->height = height; - ptr->flags = flags | 1; + ptr->flags = flags; // jl777 used to or in KVPROTECTED portable_mutex_unlock(&KOMODO_KV_mutex); - } //else printf("size mismatch %d vs %d\n",opretlen,coresize); - } + } else fprintf(stderr,"size mismatch %d vs %d\n",opretlen,coresize); + } else fprintf(stderr,"not enough fee\n"); } #endif diff --git a/src/komodo_notary.h b/src/komodo_notary.h index 0f9779129..411914f3a 100644 --- a/src/komodo_notary.h +++ b/src/komodo_notary.h @@ -198,8 +198,6 @@ const char *Notaries_elected1[][2] = {"xrobesx_NA", "03f0cc6d142d14a40937f12dbd99dbd9021328f45759e26f1877f2a838876709e1" }, }; -uint32_t komodo_heightstamp(int32_t height); - int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp) { static uint8_t elected_pubkeys0[64][33],elected_pubkeys1[64][33],did0,did1; static int32_t n0,n1; @@ -544,5 +542,38 @@ void komodo_init(int32_t height) didinit = 1; komodo_stateupdate(0,0,0,0,zero,0,0,0,0,0,0,0,0,0,0); } -} + } +/*void komodo_assetchain_pubkeys(char *jsonstr) +{ + cJSON *array; int32_t i,n; uint8_t pubkeys[64][33]; char *hexstr; + memset(pubkeys,0,sizeof(pubkeys)); + if ( (array= cJSON_Parse(jsonstr)) != 0 ) + { + if ( (n= cJSON_GetArraySize(array)) > 0 ) + { + for (i=0; i +#include +#include +#include + +uint64_t ASSETCHAINS_COMMISSION; +uint32_t ASSETCHAINS_MAGIC = 2387029918; +uint8_t ASSETCHAINS_OVERRIDE_PUBKEY33[33]; + +struct sha256_vstate { uint64_t length; uint32_t state[8],curlen; uint8_t buf[64]; }; +struct rmd160_vstate { uint64_t length; uint8_t buf[64]; uint32_t curlen, state[5]; }; +union _bits256 { uint8_t bytes[32]; uint16_t ushorts[16]; uint32_t uints[8]; uint64_t ulongs[4]; uint64_t txid; }; +typedef union _bits256 bits256; + +// following is ported from libtom + +#define STORE32L(x, y) \ +{ (y)[3] = (uint8_t)(((x)>>24)&255); (y)[2] = (uint8_t)(((x)>>16)&255); \ +(y)[1] = (uint8_t)(((x)>>8)&255); (y)[0] = (uint8_t)((x)&255); } + +#define LOAD32L(x, y) \ +{ x = (uint32_t)(((uint64_t)((y)[3] & 255)<<24) | \ +((uint32_t)((y)[2] & 255)<<16) | \ +((uint32_t)((y)[1] & 255)<<8) | \ +((uint32_t)((y)[0] & 255))); } + +#define STORE64L(x, y) \ +{ (y)[7] = (uint8_t)(((x)>>56)&255); (y)[6] = (uint8_t)(((x)>>48)&255); \ +(y)[5] = (uint8_t)(((x)>>40)&255); (y)[4] = (uint8_t)(((x)>>32)&255); \ +(y)[3] = (uint8_t)(((x)>>24)&255); (y)[2] = (uint8_t)(((x)>>16)&255); \ +(y)[1] = (uint8_t)(((x)>>8)&255); (y)[0] = (uint8_t)((x)&255); } + +#define LOAD64L(x, y) \ +{ x = (((uint64_t)((y)[7] & 255))<<56)|(((uint64_t)((y)[6] & 255))<<48)| \ +(((uint64_t)((y)[5] & 255))<<40)|(((uint64_t)((y)[4] & 255))<<32)| \ +(((uint64_t)((y)[3] & 255))<<24)|(((uint64_t)((y)[2] & 255))<<16)| \ +(((uint64_t)((y)[1] & 255))<<8)|(((uint64_t)((y)[0] & 255))); } + +#define STORE32H(x, y) \ +{ (y)[0] = (uint8_t)(((x)>>24)&255); (y)[1] = (uint8_t)(((x)>>16)&255); \ +(y)[2] = (uint8_t)(((x)>>8)&255); (y)[3] = (uint8_t)((x)&255); } + +#define LOAD32H(x, y) \ +{ x = (uint32_t)(((uint64_t)((y)[0] & 255)<<24) | \ +((uint32_t)((y)[1] & 255)<<16) | \ +((uint32_t)((y)[2] & 255)<<8) | \ +((uint32_t)((y)[3] & 255))); } + +#define STORE64H(x, y) \ +{ (y)[0] = (uint8_t)(((x)>>56)&255); (y)[1] = (uint8_t)(((x)>>48)&255); \ +(y)[2] = (uint8_t)(((x)>>40)&255); (y)[3] = (uint8_t)(((x)>>32)&255); \ +(y)[4] = (uint8_t)(((x)>>24)&255); (y)[5] = (uint8_t)(((x)>>16)&255); \ +(y)[6] = (uint8_t)(((x)>>8)&255); (y)[7] = (uint8_t)((x)&255); } + +#define LOAD64H(x, y) \ +{ x = (((uint64_t)((y)[0] & 255))<<56)|(((uint64_t)((y)[1] & 255))<<48) | \ +(((uint64_t)((y)[2] & 255))<<40)|(((uint64_t)((y)[3] & 255))<<32) | \ +(((uint64_t)((y)[4] & 255))<<24)|(((uint64_t)((y)[5] & 255))<<16) | \ +(((uint64_t)((y)[6] & 255))<<8)|(((uint64_t)((y)[7] & 255))); } + +// Various logical functions +#define RORc(x, y) ( ((((uint32_t)(x)&0xFFFFFFFFUL)>>(uint32_t)((y)&31)) | ((uint32_t)(x)<<(uint32_t)(32-((y)&31)))) & 0xFFFFFFFFUL) +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) RORc((x),(n)) +#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) +#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) +#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) +#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) +#define MIN(x, y) ( ((x)<(y))?(x):(y) ) + +static inline int32_t sha256_vcompress(struct sha256_vstate * md,uint8_t *buf) +{ + uint32_t S[8],W[64],t0,t1,i; + for (i=0; i<8; i++) // copy state into S + S[i] = md->state[i]; + for (i=0; i<16; i++) // copy the state into 512-bits into W[0..15] + LOAD32H(W[i],buf + (4*i)); + for (i=16; i<64; i++) // fill W[16..63] + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; + +#define RND(a,b,c,d,e,f,g,h,i,ki) \ +t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; \ +t1 = Sigma0(a) + Maj(a, b, c); \ +d += t0; \ +h = t0 + t1; + + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x71374491); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcf); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba5); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25b); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b01); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a7); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c1); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc6); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dc); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c8); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf3); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x14292967); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a85); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b2138); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d13); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a7354); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c85); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a1); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664b); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a3); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd6990624); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e3585); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa070); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c08); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774c); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4a); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc70208); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506ceb); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2); +#undef RND + for (i=0; i<8; i++) // feedback + md->state[i] = md->state[i] + S[i]; + return(0); +} + +#undef RORc +#undef Ch +#undef Maj +#undef S +#undef R +#undef Sigma0 +#undef Sigma1 +#undef Gamma0 +#undef Gamma1 + +static inline void sha256_vinit(struct sha256_vstate * md) +{ + md->curlen = 0; + md->length = 0; + md->state[0] = 0x6A09E667UL; + md->state[1] = 0xBB67AE85UL; + md->state[2] = 0x3C6EF372UL; + md->state[3] = 0xA54FF53AUL; + md->state[4] = 0x510E527FUL; + md->state[5] = 0x9B05688CUL; + md->state[6] = 0x1F83D9ABUL; + md->state[7] = 0x5BE0CD19UL; +} + +static inline int32_t sha256_vprocess(struct sha256_vstate *md,const uint8_t *in,uint64_t inlen) +{ + uint64_t n; int32_t err; + if ( md->curlen > sizeof(md->buf) ) + return(-1); + while ( inlen > 0 ) + { + if ( md->curlen == 0 && inlen >= 64 ) + { + if ( (err= sha256_vcompress(md,(uint8_t *)in)) != 0 ) + return(err); + md->length += 64 * 8, in += 64, inlen -= 64; + } + else + { + n = MIN(inlen,64 - md->curlen); + memcpy(md->buf + md->curlen,in,(size_t)n); + md->curlen += n, in += n, inlen -= n; + if ( md->curlen == 64 ) + { + if ( (err= sha256_vcompress(md,md->buf)) != 0 ) + return(err); + md->length += 8*64; + md->curlen = 0; + } + } + } + return(0); +} + +static inline int32_t sha256_vdone(struct sha256_vstate *md,uint8_t *out) +{ + int32_t i; + if ( md->curlen >= sizeof(md->buf) ) + return(-1); + md->length += md->curlen * 8; // increase the length of the message + md->buf[md->curlen++] = (uint8_t)0x80; // append the '1' bit + // if len > 56 bytes we append zeros then compress. Then we can fall back to padding zeros and length encoding like normal. + if ( md->curlen > 56 ) + { + while ( md->curlen < 64 ) + md->buf[md->curlen++] = (uint8_t)0; + sha256_vcompress(md,md->buf); + md->curlen = 0; + } + while ( md->curlen < 56 ) // pad upto 56 bytes of zeroes + md->buf[md->curlen++] = (uint8_t)0; + STORE64H(md->length,md->buf+56); // store length + sha256_vcompress(md,md->buf); + for (i=0; i<8; i++) // copy output + STORE32H(md->state[i],out+(4*i)); + return(0); +} + +void vcalc_sha256(char deprecated[(256 >> 3) * 2 + 1],uint8_t hash[256 >> 3],uint8_t *src,int32_t len) +{ + struct sha256_vstate md; + sha256_vinit(&md); + sha256_vprocess(&md,src,len); + sha256_vdone(&md,hash); +} + +bits256 bits256_doublesha256(char *deprecated,uint8_t *data,int32_t datalen) +{ + bits256 hash,hash2; int32_t i; + vcalc_sha256(0,hash.bytes,data,datalen); + vcalc_sha256(0,hash2.bytes,hash.bytes,sizeof(hash)); + for (i=0; i>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) + +/* the ten basic operations FF() through III() */ +#define FF(a, b, c, d, e, x, s) \ +(a) += F((b), (c), (d)) + (x);\ +(a) = ROLc((a), (s)) + (e);\ +(c) = ROLc((c), 10); + +#define GG(a, b, c, d, e, x, s) \ +(a) += G((b), (c), (d)) + (x) + 0x5a827999UL;\ +(a) = ROLc((a), (s)) + (e);\ +(c) = ROLc((c), 10); + +#define HH(a, b, c, d, e, x, s) \ +(a) += H((b), (c), (d)) + (x) + 0x6ed9eba1UL;\ +(a) = ROLc((a), (s)) + (e);\ +(c) = ROLc((c), 10); + +#define II(a, b, c, d, e, x, s) \ +(a) += I((b), (c), (d)) + (x) + 0x8f1bbcdcUL;\ +(a) = ROLc((a), (s)) + (e);\ +(c) = ROLc((c), 10); + +#define JJ(a, b, c, d, e, x, s) \ +(a) += J((b), (c), (d)) + (x) + 0xa953fd4eUL;\ +(a) = ROLc((a), (s)) + (e);\ +(c) = ROLc((c), 10); + +#define FFF(a, b, c, d, e, x, s) \ +(a) += F((b), (c), (d)) + (x);\ +(a) = ROLc((a), (s)) + (e);\ +(c) = ROLc((c), 10); + +#define GGG(a, b, c, d, e, x, s) \ +(a) += G((b), (c), (d)) + (x) + 0x7a6d76e9UL;\ +(a) = ROLc((a), (s)) + (e);\ +(c) = ROLc((c), 10); + +#define HHH(a, b, c, d, e, x, s) \ +(a) += H((b), (c), (d)) + (x) + 0x6d703ef3UL;\ +(a) = ROLc((a), (s)) + (e);\ +(c) = ROLc((c), 10); + +#define III(a, b, c, d, e, x, s) \ +(a) += I((b), (c), (d)) + (x) + 0x5c4dd124UL;\ +(a) = ROLc((a), (s)) + (e);\ +(c) = ROLc((c), 10); + +#define JJJ(a, b, c, d, e, x, s) \ +(a) += J((b), (c), (d)) + (x) + 0x50a28be6UL;\ +(a) = ROLc((a), (s)) + (e);\ +(c) = ROLc((c), 10); + +static int32_t rmd160_vcompress(struct rmd160_vstate *md,uint8_t *buf) +{ + uint32_t aa,bb,cc,dd,ee,aaa,bbb,ccc,ddd,eee,X[16]; + int i; + + /* load words X */ + for (i = 0; i < 16; i++){ + LOAD32L(X[i], buf + (4 * i)); + } + + /* load state */ + aa = aaa = md->state[0]; + bb = bbb = md->state[1]; + cc = ccc = md->state[2]; + dd = ddd = md->state[3]; + ee = eee = md->state[4]; + + /* round 1 */ + FF(aa, bb, cc, dd, ee, X[ 0], 11); + FF(ee, aa, bb, cc, dd, X[ 1], 14); + FF(dd, ee, aa, bb, cc, X[ 2], 15); + FF(cc, dd, ee, aa, bb, X[ 3], 12); + FF(bb, cc, dd, ee, aa, X[ 4], 5); + FF(aa, bb, cc, dd, ee, X[ 5], 8); + FF(ee, aa, bb, cc, dd, X[ 6], 7); + FF(dd, ee, aa, bb, cc, X[ 7], 9); + FF(cc, dd, ee, aa, bb, X[ 8], 11); + FF(bb, cc, dd, ee, aa, X[ 9], 13); + FF(aa, bb, cc, dd, ee, X[10], 14); + FF(ee, aa, bb, cc, dd, X[11], 15); + FF(dd, ee, aa, bb, cc, X[12], 6); + FF(cc, dd, ee, aa, bb, X[13], 7); + FF(bb, cc, dd, ee, aa, X[14], 9); + FF(aa, bb, cc, dd, ee, X[15], 8); + + /* round 2 */ + GG(ee, aa, bb, cc, dd, X[ 7], 7); + GG(dd, ee, aa, bb, cc, X[ 4], 6); + GG(cc, dd, ee, aa, bb, X[13], 8); + GG(bb, cc, dd, ee, aa, X[ 1], 13); + GG(aa, bb, cc, dd, ee, X[10], 11); + GG(ee, aa, bb, cc, dd, X[ 6], 9); + GG(dd, ee, aa, bb, cc, X[15], 7); + GG(cc, dd, ee, aa, bb, X[ 3], 15); + GG(bb, cc, dd, ee, aa, X[12], 7); + GG(aa, bb, cc, dd, ee, X[ 0], 12); + GG(ee, aa, bb, cc, dd, X[ 9], 15); + GG(dd, ee, aa, bb, cc, X[ 5], 9); + GG(cc, dd, ee, aa, bb, X[ 2], 11); + GG(bb, cc, dd, ee, aa, X[14], 7); + GG(aa, bb, cc, dd, ee, X[11], 13); + GG(ee, aa, bb, cc, dd, X[ 8], 12); + + /* round 3 */ + HH(dd, ee, aa, bb, cc, X[ 3], 11); + HH(cc, dd, ee, aa, bb, X[10], 13); + HH(bb, cc, dd, ee, aa, X[14], 6); + HH(aa, bb, cc, dd, ee, X[ 4], 7); + HH(ee, aa, bb, cc, dd, X[ 9], 14); + HH(dd, ee, aa, bb, cc, X[15], 9); + HH(cc, dd, ee, aa, bb, X[ 8], 13); + HH(bb, cc, dd, ee, aa, X[ 1], 15); + HH(aa, bb, cc, dd, ee, X[ 2], 14); + HH(ee, aa, bb, cc, dd, X[ 7], 8); + HH(dd, ee, aa, bb, cc, X[ 0], 13); + HH(cc, dd, ee, aa, bb, X[ 6], 6); + HH(bb, cc, dd, ee, aa, X[13], 5); + HH(aa, bb, cc, dd, ee, X[11], 12); + HH(ee, aa, bb, cc, dd, X[ 5], 7); + HH(dd, ee, aa, bb, cc, X[12], 5); + + /* round 4 */ + II(cc, dd, ee, aa, bb, X[ 1], 11); + II(bb, cc, dd, ee, aa, X[ 9], 12); + II(aa, bb, cc, dd, ee, X[11], 14); + II(ee, aa, bb, cc, dd, X[10], 15); + II(dd, ee, aa, bb, cc, X[ 0], 14); + II(cc, dd, ee, aa, bb, X[ 8], 15); + II(bb, cc, dd, ee, aa, X[12], 9); + II(aa, bb, cc, dd, ee, X[ 4], 8); + II(ee, aa, bb, cc, dd, X[13], 9); + II(dd, ee, aa, bb, cc, X[ 3], 14); + II(cc, dd, ee, aa, bb, X[ 7], 5); + II(bb, cc, dd, ee, aa, X[15], 6); + II(aa, bb, cc, dd, ee, X[14], 8); + II(ee, aa, bb, cc, dd, X[ 5], 6); + II(dd, ee, aa, bb, cc, X[ 6], 5); + II(cc, dd, ee, aa, bb, X[ 2], 12); + + /* round 5 */ + JJ(bb, cc, dd, ee, aa, X[ 4], 9); + JJ(aa, bb, cc, dd, ee, X[ 0], 15); + JJ(ee, aa, bb, cc, dd, X[ 5], 5); + JJ(dd, ee, aa, bb, cc, X[ 9], 11); + JJ(cc, dd, ee, aa, bb, X[ 7], 6); + JJ(bb, cc, dd, ee, aa, X[12], 8); + JJ(aa, bb, cc, dd, ee, X[ 2], 13); + JJ(ee, aa, bb, cc, dd, X[10], 12); + JJ(dd, ee, aa, bb, cc, X[14], 5); + JJ(cc, dd, ee, aa, bb, X[ 1], 12); + JJ(bb, cc, dd, ee, aa, X[ 3], 13); + JJ(aa, bb, cc, dd, ee, X[ 8], 14); + JJ(ee, aa, bb, cc, dd, X[11], 11); + JJ(dd, ee, aa, bb, cc, X[ 6], 8); + JJ(cc, dd, ee, aa, bb, X[15], 5); + JJ(bb, cc, dd, ee, aa, X[13], 6); + + /* parallel round 1 */ + JJJ(aaa, bbb, ccc, ddd, eee, X[ 5], 8); + JJJ(eee, aaa, bbb, ccc, ddd, X[14], 9); + JJJ(ddd, eee, aaa, bbb, ccc, X[ 7], 9); + JJJ(ccc, ddd, eee, aaa, bbb, X[ 0], 11); + JJJ(bbb, ccc, ddd, eee, aaa, X[ 9], 13); + JJJ(aaa, bbb, ccc, ddd, eee, X[ 2], 15); + JJJ(eee, aaa, bbb, ccc, ddd, X[11], 15); + JJJ(ddd, eee, aaa, bbb, ccc, X[ 4], 5); + JJJ(ccc, ddd, eee, aaa, bbb, X[13], 7); + JJJ(bbb, ccc, ddd, eee, aaa, X[ 6], 7); + JJJ(aaa, bbb, ccc, ddd, eee, X[15], 8); + JJJ(eee, aaa, bbb, ccc, ddd, X[ 8], 11); + JJJ(ddd, eee, aaa, bbb, ccc, X[ 1], 14); + JJJ(ccc, ddd, eee, aaa, bbb, X[10], 14); + JJJ(bbb, ccc, ddd, eee, aaa, X[ 3], 12); + JJJ(aaa, bbb, ccc, ddd, eee, X[12], 6); + + /* parallel round 2 */ + III(eee, aaa, bbb, ccc, ddd, X[ 6], 9); + III(ddd, eee, aaa, bbb, ccc, X[11], 13); + III(ccc, ddd, eee, aaa, bbb, X[ 3], 15); + III(bbb, ccc, ddd, eee, aaa, X[ 7], 7); + III(aaa, bbb, ccc, ddd, eee, X[ 0], 12); + III(eee, aaa, bbb, ccc, ddd, X[13], 8); + III(ddd, eee, aaa, bbb, ccc, X[ 5], 9); + III(ccc, ddd, eee, aaa, bbb, X[10], 11); + III(bbb, ccc, ddd, eee, aaa, X[14], 7); + III(aaa, bbb, ccc, ddd, eee, X[15], 7); + III(eee, aaa, bbb, ccc, ddd, X[ 8], 12); + III(ddd, eee, aaa, bbb, ccc, X[12], 7); + III(ccc, ddd, eee, aaa, bbb, X[ 4], 6); + III(bbb, ccc, ddd, eee, aaa, X[ 9], 15); + III(aaa, bbb, ccc, ddd, eee, X[ 1], 13); + III(eee, aaa, bbb, ccc, ddd, X[ 2], 11); + + /* parallel round 3 */ + HHH(ddd, eee, aaa, bbb, ccc, X[15], 9); + HHH(ccc, ddd, eee, aaa, bbb, X[ 5], 7); + HHH(bbb, ccc, ddd, eee, aaa, X[ 1], 15); + HHH(aaa, bbb, ccc, ddd, eee, X[ 3], 11); + HHH(eee, aaa, bbb, ccc, ddd, X[ 7], 8); + HHH(ddd, eee, aaa, bbb, ccc, X[14], 6); + HHH(ccc, ddd, eee, aaa, bbb, X[ 6], 6); + HHH(bbb, ccc, ddd, eee, aaa, X[ 9], 14); + HHH(aaa, bbb, ccc, ddd, eee, X[11], 12); + HHH(eee, aaa, bbb, ccc, ddd, X[ 8], 13); + HHH(ddd, eee, aaa, bbb, ccc, X[12], 5); + HHH(ccc, ddd, eee, aaa, bbb, X[ 2], 14); + HHH(bbb, ccc, ddd, eee, aaa, X[10], 13); + HHH(aaa, bbb, ccc, ddd, eee, X[ 0], 13); + HHH(eee, aaa, bbb, ccc, ddd, X[ 4], 7); + HHH(ddd, eee, aaa, bbb, ccc, X[13], 5); + + /* parallel round 4 */ + GGG(ccc, ddd, eee, aaa, bbb, X[ 8], 15); + GGG(bbb, ccc, ddd, eee, aaa, X[ 6], 5); + GGG(aaa, bbb, ccc, ddd, eee, X[ 4], 8); + GGG(eee, aaa, bbb, ccc, ddd, X[ 1], 11); + GGG(ddd, eee, aaa, bbb, ccc, X[ 3], 14); + GGG(ccc, ddd, eee, aaa, bbb, X[11], 14); + GGG(bbb, ccc, ddd, eee, aaa, X[15], 6); + GGG(aaa, bbb, ccc, ddd, eee, X[ 0], 14); + GGG(eee, aaa, bbb, ccc, ddd, X[ 5], 6); + GGG(ddd, eee, aaa, bbb, ccc, X[12], 9); + GGG(ccc, ddd, eee, aaa, bbb, X[ 2], 12); + GGG(bbb, ccc, ddd, eee, aaa, X[13], 9); + GGG(aaa, bbb, ccc, ddd, eee, X[ 9], 12); + GGG(eee, aaa, bbb, ccc, ddd, X[ 7], 5); + GGG(ddd, eee, aaa, bbb, ccc, X[10], 15); + GGG(ccc, ddd, eee, aaa, bbb, X[14], 8); + + /* parallel round 5 */ + FFF(bbb, ccc, ddd, eee, aaa, X[12] , 8); + FFF(aaa, bbb, ccc, ddd, eee, X[15] , 5); + FFF(eee, aaa, bbb, ccc, ddd, X[10] , 12); + FFF(ddd, eee, aaa, bbb, ccc, X[ 4] , 9); + FFF(ccc, ddd, eee, aaa, bbb, X[ 1] , 12); + FFF(bbb, ccc, ddd, eee, aaa, X[ 5] , 5); + FFF(aaa, bbb, ccc, ddd, eee, X[ 8] , 14); + FFF(eee, aaa, bbb, ccc, ddd, X[ 7] , 6); + FFF(ddd, eee, aaa, bbb, ccc, X[ 6] , 8); + FFF(ccc, ddd, eee, aaa, bbb, X[ 2] , 13); + FFF(bbb, ccc, ddd, eee, aaa, X[13] , 6); + FFF(aaa, bbb, ccc, ddd, eee, X[14] , 5); + FFF(eee, aaa, bbb, ccc, ddd, X[ 0] , 15); + FFF(ddd, eee, aaa, bbb, ccc, X[ 3] , 13); + FFF(ccc, ddd, eee, aaa, bbb, X[ 9] , 11); + FFF(bbb, ccc, ddd, eee, aaa, X[11] , 11); + + /* combine results */ + ddd += cc + md->state[1]; /* final result for md->state[0] */ + md->state[1] = md->state[2] + dd + eee; + md->state[2] = md->state[3] + ee + aaa; + md->state[3] = md->state[4] + aa + bbb; + md->state[4] = md->state[0] + bb + ccc; + md->state[0] = ddd; + + return 0; +} + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return 0 if successful + */ +int rmd160_vinit(struct rmd160_vstate * md) +{ + md->state[0] = 0x67452301UL; + md->state[1] = 0xefcdab89UL; + md->state[2] = 0x98badcfeUL; + md->state[3] = 0x10325476UL; + md->state[4] = 0xc3d2e1f0UL; + md->curlen = 0; + md->length = 0; + return 0; +} +#define HASH_PROCESS(func_name, compress_name, state_var, block_size) \ +int func_name (struct rmd160_vstate * md, const unsigned char *in, unsigned long inlen) \ +{ \ +unsigned long n; \ +int err; \ +if (md->curlen > sizeof(md->buf)) { \ +return -1; \ +} \ +while (inlen > 0) { \ +if (md->curlen == 0 && inlen >= block_size) { \ +if ((err = compress_name (md, (unsigned char *)in)) != 0) { \ +return err; \ +} \ +md->length += block_size * 8; \ +in += block_size; \ +inlen -= block_size; \ +} else { \ +n = MIN(inlen, (block_size - md->curlen)); \ +memcpy(md->buf + md->curlen, in, (size_t)n); \ +md->curlen += n; \ +in += n; \ +inlen -= n; \ +if (md->curlen == block_size) { \ +if ((err = compress_name (md, md->buf)) != 0) { \ +return err; \ +} \ +md->length += 8*block_size; \ +md->curlen = 0; \ +} \ +} \ +} \ +return 0; \ +} + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return 0 if successful + */ +HASH_PROCESS(rmd160_vprocess, rmd160_vcompress, rmd160, 64) + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (20 bytes) + @return 0 if successful + */ +int rmd160_vdone(struct rmd160_vstate * md, unsigned char *out) +{ + int i; + if (md->curlen >= sizeof(md->buf)) { + return -1; + } + /* increase the length of the message */ + md->length += md->curlen * 8; + + /* append the '1' bit */ + md->buf[md->curlen++] = (unsigned char)0x80; + + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->curlen > 56) { + while (md->curlen < 64) { + md->buf[md->curlen++] = (unsigned char)0; + } + rmd160_vcompress(md, md->buf); + md->curlen = 0; + } + /* pad upto 56 bytes of zeroes */ + while (md->curlen < 56) { + md->buf[md->curlen++] = (unsigned char)0; + } + /* store length */ + STORE64L(md->length, md->buf+56); + rmd160_vcompress(md, md->buf); + /* copy output */ + for (i = 0; i < 5; i++) { + STORE32L(md->state[i], out+(4*i)); + } + return 0; +} + +void calc_rmd160(char deprecated[41],uint8_t buf[20],uint8_t *msg,int32_t len) +{ + struct rmd160_vstate md; + rmd160_vinit(&md); + rmd160_vprocess(&md,msg,len); + rmd160_vdone(&md, buf); +} +#undef F +#undef G +#undef H +#undef I +#undef J +#undef ROLc +#undef FF +#undef GG +#undef HH +#undef II +#undef JJ +#undef FFF +#undef GGG +#undef HHH +#undef III +#undef JJJ + +static const uint32_t crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +uint32_t calc_crc32(uint32_t crc,const void *buf,size_t size) +{ + const uint8_t *p; + + p = (const uint8_t *)buf; + crc = crc ^ ~0U; + + while (size--) + crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + + return crc ^ ~0U; +} + +void calc_rmd160_sha256(uint8_t rmd160[20],uint8_t *data,int32_t datalen) +{ + bits256 hash; + vcalc_sha256(0,hash.bytes,data,datalen); + calc_rmd160(0,rmd160,hash.bytes,sizeof(hash)); +} + +int32_t iguana_rwnum(int32_t rwflag,uint8_t *serialized,int32_t len,void *endianedp) +{ + int32_t i; uint64_t x; + if ( rwflag == 0 ) + { + x = 0; + for (i=len-1; i>=0; i--) + { + x <<= 8; + x |= serialized[i]; + } + switch ( len ) + { + case 1: *(uint8_t *)endianedp = (uint8_t)x; break; + case 2: *(uint16_t *)endianedp = (uint16_t)x; break; + case 4: *(uint32_t *)endianedp = (uint32_t)x; break; + case 8: *(uint64_t *)endianedp = (uint64_t)x; break; + } + } + else + { + x = 0; + switch ( len ) + { + case 1: x = *(uint8_t *)endianedp; break; + case 2: x = *(uint16_t *)endianedp; break; + case 4: x = *(uint32_t *)endianedp; break; + case 8: x = *(uint64_t *)endianedp; break; + } + for (i=0; i>= 8) + serialized[i] = (uint8_t)(x & 0xff); + } + return(len); +} + +uint32_t komodo_assetmagic(char *symbol,uint64_t supply,uint8_t *extraptr,int32_t extralen) +{ + uint8_t buf[512]; uint32_t crc0=0; int32_t len = 0; bits256 hash; + if ( strcmp(symbol,"KMD") == 0 ) + return(0x8de4eef9); + len = iguana_rwnum(1,&buf[len],sizeof(supply),(void *)&supply); + strcpy((char *)&buf[len],symbol); + len += strlen(symbol); + if ( extraptr != 0 && extralen != 0 ) + { + vcalc_sha256(0,hash.bytes,extraptr,extralen); + crc0 = hash.uints[0]; + } + return(calc_crc32(crc0,buf,len)); +} + +uint16_t komodo_assetport(uint32_t magic,int32_t extralen) +{ + if ( magic == 0x8de4eef9 ) + return(7770); + else if ( extralen == 0 ) + return(8000 + (magic % 7777)); + else return(16000 + (magic % 49500)); +} + +uint16_t komodo_port(char *symbol,uint64_t supply,uint32_t *magicp,uint8_t *extraptr,int32_t extralen) +{ + if ( symbol == 0 || symbol[0] == 0 || strcmp("KMD",symbol) == 0 ) + { + *magicp = 0x8de4eef9; + return(7770); + } + *magicp = komodo_assetmagic(symbol,supply,extraptr,extralen); + return(komodo_assetport(*magicp,extralen)); +} + +uint16_t komodo_calcport(char *name,uint64_t supply,uint64_t endsubsidy,uint64_t reward,uint64_t halving,uint64_t decay) +{ + uint8_t extrabuf[4096],*extraptr=0; int32_t extralen=0; + if ( halving != 0 && halving < 1440 ) + { + halving = 1440; + printf("halving must be at least 1440 blocks\n"); + } + if ( decay == 100000000 && endsubsidy == 0 ) + { + decay = 0; + printf("decay of 100000000 means linear and that needs endsubsidy\n"); + } + else if ( decay > 100000000 ) + { + decay = 0; + printf("decay cant be more than 100000000\n"); + } + if ( endsubsidy != 0 || reward != 0 || halving != 0 || decay != 0 || ASSETCHAINS_COMMISSION != 0 ) + { + printf("end.%llu reward.%llu halving.%llu decay.%llu perc.%llu\n",(long long)endsubsidy,(long long)reward,(long long)halving,(long long)decay,(long long)ASSETCHAINS_COMMISSION); + extraptr = extrabuf; + memcpy(extraptr,ASSETCHAINS_OVERRIDE_PUBKEY33,33), extralen = 33; + extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(endsubsidy),(void *)&endsubsidy); + extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(reward),(void *)&reward); + extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(halving),(void *)&halving); + extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(decay),(void *)&decay); + extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_COMMISSION),(void *)&ASSETCHAINS_COMMISSION); + } + return(komodo_port(name,supply,&ASSETCHAINS_MAGIC,extraptr,extralen)); +} + +int main(int argc, char * argv[]) +{ + uint16_t rpcport; int32_t i,j,offset=0,num = 1; uint64_t supply=10,endsubsidy,reward,halving,decay; uint8_t *allocated=0; + endsubsidy = reward = halving = decay = 0; + if ( argc < 2 ) + { + printf("%s name supply endsubsidy reward halving decay\n",argv[0]); + printf("%s -gen num name supply endsubsidy reward halving decay\n",argv[0]); + return(-1); + } + if ( strcmp(argv[1],"-gen") == 0 ) + { + num = atoi(argv[2]); + offset = 2; + allocated = calloc(1,1 << 16); + } + if ( argc > offset + 2 ) + supply = (long long)atof(argv[offset + 2]); + if ( argc > offset + 3 ) + endsubsidy = (long long)atof(argv[offset + 3]); + if ( argc > offset + 4 ) + reward = (long long)atof(argv[offset + 4]); + if ( argc > offset + 5 ) + halving = (long long)atof(argv[offset + 5]); + if ( argc > offset + 6 ) + decay = (long long)atof(argv[offset + 6]); + rpcport = 1 + komodo_calcport(argv[offset + 1],supply,endsubsidy,reward,halving,decay); + printf("./komodod -ac_name=%s -ac_supply=%llu -ac_end=%llu -ac_reward=%llu -ac_halving=%llu -ac_decay=%llu & # rpcport %u\n",argv[offset + 1],(long long)supply,(long long)endsubsidy,(long long)reward,(long long)halving,(long long)decay,rpcport); + if ( allocated != 0 ) + { + char name[64],newname[64]; + strcpy(name,argv[offset + 1]); + allocated[rpcport] = 1; + allocated[rpcport-1] = 1; + for (i=0; i userpass.(%s)\n",fname,KMDUSERPASS); - } // else printf("couldnt open.(%s)\n",fname); + } //else printf("couldnt open.(%s)\n",fname); } uint16_t komodo_userpass(char *userpass,char *symbol) @@ -1420,55 +1422,51 @@ uint16_t komodo_userpass(char *userpass,char *symbol) { port = komodo_userpass(username,password,fp); sprintf(userpass,"%s:%s",username,password); + if ( strcmp(symbol,ASSETCHAINS_SYMBOL) == 0 ) + strcpy(ASSETCHAINS_USERPASS,userpass); fclose(fp); return((int32_t)strlen(userpass)); } return(port); } -uint32_t komodo_assetmagic(char *symbol,uint64_t supply) +uint32_t komodo_assetmagic(char *symbol,uint64_t supply,uint8_t *extraptr,int32_t extralen) { - uint8_t buf[512]; int32_t len = 0; + uint8_t buf[512]; uint32_t crc0=0; int32_t len = 0; bits256 hash; if ( strcmp(symbol,"KMD") == 0 ) return(0x8de4eef9); len = iguana_rwnum(1,&buf[len],sizeof(supply),(void *)&supply); strcpy((char *)&buf[len],symbol); len += strlen(symbol); - return(calc_crc32(0,buf,len)); -} - -/*int32_t komodo_shortflag(char *symbol) -{ - int32_t i,shortflag = 0; - if ( symbol[0] == '-' ) + if ( extraptr != 0 && extralen != 0 ) { - shortflag = 1; - for (i=0; symbol[i+1]!=0; i++) - symbol[i] = symbol[i+1]; - symbol[i] = 0; + vcalc_sha256(0,hash.bytes,extraptr,extralen); + crc0 = hash.uints[0]; } - return(shortflag); -}*/ + return(calc_crc32(crc0,buf,len)); +} -uint16_t komodo_assetport(uint32_t magic) +uint16_t komodo_assetport(uint32_t magic,int32_t extralen) { if ( magic == 0x8de4eef9 ) return(7770); - else return(8000 + (magic % 7777)); + else if ( extralen == 0 ) + return(8000 + (magic % 7777)); + else return(16000 + (magic % 49500)); } -uint16_t komodo_port(char *symbol,uint64_t supply,uint32_t *magicp) +uint16_t komodo_port(char *symbol,uint64_t supply,uint32_t *magicp,uint8_t *extraptr,int32_t extralen) { if ( symbol == 0 || symbol[0] == 0 || strcmp("KMD",symbol) == 0 ) { *magicp = 0x8de4eef9; return(7770); } - *magicp = komodo_assetmagic(symbol,supply); - return(komodo_assetport(*magicp)); + *magicp = komodo_assetmagic(symbol,supply,extraptr,extralen); + return(komodo_assetport(*magicp,extralen)); } -void komodo_ports(uint16_t ports[MAX_CURRENCIES]) +/*void komodo_ports(uint16_t ports[MAX_CURRENCIES]) { int32_t i; uint32_t magic; for (i=0; i 100 ) + ASSETCHAINS_STAKED = 100; + if ( ASSETCHAINS_HALVING != 0 && ASSETCHAINS_HALVING < 1440 ) + { + ASSETCHAINS_HALVING = 1440; + printf("ASSETCHAINS_HALVING must be at least 1440 blocks\n"); + } + if ( ASSETCHAINS_DECAY == 100000000 && ASSETCHAINS_ENDSUBSIDY == 0 ) + { + ASSETCHAINS_DECAY = 0; + printf("ASSETCHAINS_DECAY of 100000000 means linear and that needs ASSETCHAINS_ENDSUBSIDY\n"); + } + else if ( ASSETCHAINS_DECAY > 100000000 ) + { + ASSETCHAINS_DECAY = 0; + printf("ASSETCHAINS_DECAY cant be more than 100000000\n"); + } + if ( strlen(ASSETCHAINS_OVERRIDE_PUBKEY.c_str()) == 66 && ASSETCHAINS_COMMISSION > 0 && ASSETCHAINS_COMMISSION <= 100000000 ) + decode_hex(ASSETCHAINS_OVERRIDE_PUBKEY33,33,(char *)ASSETCHAINS_OVERRIDE_PUBKEY.c_str()); + else if ( ASSETCHAINS_COMMISSION != 0 ) + { + ASSETCHAINS_COMMISSION = 0; + printf("ASSETCHAINS_COMMISSION needs an ASETCHAINS_OVERRIDE_PUBKEY and cant be more than 100000000 (100%%)\n"); + } + if ( ASSETCHAINS_ENDSUBSIDY != 0 || ASSETCHAINS_REWARD != 0 || ASSETCHAINS_HALVING != 0 || ASSETCHAINS_DECAY != 0 || ASSETCHAINS_COMMISSION != 0 ) + { + fprintf(stderr,"end.%llu blocks, reward %.8f halving.%llu blocks, decay.%llu perc %.4f%% ac_pub=[%02x...]\n",(long long)ASSETCHAINS_ENDSUBSIDY,dstr(ASSETCHAINS_REWARD),(long long)ASSETCHAINS_HALVING,(long long)ASSETCHAINS_DECAY,dstr(ASSETCHAINS_COMMISSION)*100,ASSETCHAINS_OVERRIDE_PUBKEY33[0]); + extraptr = extrabuf; + memcpy(extraptr,ASSETCHAINS_OVERRIDE_PUBKEY33,33), extralen = 33; + extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_ENDSUBSIDY),(void *)&ASSETCHAINS_ENDSUBSIDY); + extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_REWARD),(void *)&ASSETCHAINS_REWARD); + extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_HALVING),(void *)&ASSETCHAINS_HALVING); + extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_DECAY),(void *)&ASSETCHAINS_DECAY); + val = ASSETCHAINS_COMMISSION | ((ASSETCHAINS_STAKED & 0xff) << 32); + extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(val),(void *)&val); + } addn = GetArg("-seednode",""); if ( strlen(addn.c_str()) > 0 ) ASSETCHAINS_SEED = 1; strncpy(ASSETCHAINS_SYMBOL,name.c_str(),sizeof(ASSETCHAINS_SYMBOL)-1); if ( (baseid= komodo_baseid(ASSETCHAINS_SYMBOL)) >= 0 && baseid < 32 ) MAX_MONEY = komodo_maxallowed(baseid); - else MAX_MONEY = (ASSETCHAINS_SUPPLY+1) * SATOSHIDEN; + else if ( ASSETCHAINS_REWARD == 0 ) + MAX_MONEY = (ASSETCHAINS_SUPPLY+1) * SATOSHIDEN; + else MAX_MONEY = (ASSETCHAINS_SUPPLY+1) * SATOSHIDEN + ASSETCHAINS_REWARD * (ASSETCHAINS_ENDSUBSIDY==0 ? 10000000 : ASSETCHAINS_ENDSUBSIDY); + MAX_MONEY += (MAX_MONEY * ASSETCHAINS_COMMISSION) / SATOSHIDEN; //printf("baseid.%d MAX_MONEY.%s %.8f\n",baseid,ASSETCHAINS_SYMBOL,(double)MAX_MONEY/SATOSHIDEN); - ASSETCHAINS_PORT = komodo_port(ASSETCHAINS_SYMBOL,ASSETCHAINS_SUPPLY,&ASSETCHAINS_MAGIC); + ASSETCHAINS_PORT = komodo_port(ASSETCHAINS_SYMBOL,ASSETCHAINS_SUPPLY,&ASSETCHAINS_MAGIC,extraptr,extralen); while ( (dirname= (char *)GetDataDir(false).string().c_str()) == 0 || dirname[0] == 0 ) { fprintf(stderr,"waiting for datadir\n"); @@ -1561,8 +1605,9 @@ void komodo_args(char *argv0) int32_t komodo_baseid(char *origbase); extern int COINBASE_MATURITY; komodo_configfile(ASSETCHAINS_SYMBOL,ASSETCHAINS_PORT + 1); + komodo_userpass(ASSETCHAINS_USERPASS,ASSETCHAINS_SYMBOL); COINBASE_MATURITY = 1; - LogPrintf("ASSETCHAINS_PORT %s %u\n",ASSETCHAINS_SYMBOL,ASSETCHAINS_PORT); + //fprintf(stderr,"ASSETCHAINS_PORT %s %u (%s)\n",ASSETCHAINS_SYMBOL,ASSETCHAINS_PORT,ASSETCHAINS_USERPASS); } //ASSETCHAINS_NOTARIES = GetArg("-ac_notaries",""); //komodo_assetchain_pubkeys((char *)ASSETCHAINS_NOTARIES.c_str()); @@ -1570,6 +1615,7 @@ void komodo_args(char *argv0) for (i=0; i<4; i++) sprintf(&magicstr[i<<1],"%02x",magic[i]); magicstr[8] = 0; +#ifndef FROM_CLI sprintf(fname,"gen%s",ASSETCHAINS_SYMBOL); if ( (fp= fopen(fname,"wb")) != 0 ) { @@ -1577,6 +1623,7 @@ void komodo_args(char *argv0) fclose(fp); //printf("created (%s)\n",fname); } else printf("error creating (%s)\n",fname); +#endif } else { @@ -1615,7 +1662,7 @@ void komodo_args(char *argv0) break; } } - BITCOIND_PORT = GetArg("-rpcport", BaseParams().RPCPort()); + BITCOIND_PORT = GetArg("-rpcport", BaseParams().RPCPort()); //fprintf(stderr,"%s chain params initialized\n",ASSETCHAINS_SYMBOL); } diff --git a/src/leveldbwrapper.cpp b/src/leveldbwrapper.cpp index 26cacf95a..b5d024abb 100644 --- a/src/leveldbwrapper.cpp +++ b/src/leveldbwrapper.cpp @@ -13,7 +13,7 @@ #include #include -void HandleError(const leveldb::Status& status) throw(leveldb_error) +void HandleError(const leveldb::Status& status) { if (status.ok()) return; @@ -81,7 +81,7 @@ CLevelDBWrapper::~CLevelDBWrapper() options.env = NULL; } -bool CLevelDBWrapper::WriteBatch(CLevelDBBatch& batch, bool fSync) throw(leveldb_error) +bool CLevelDBWrapper::WriteBatch(CLevelDBBatch& batch, bool fSync) { leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch); HandleError(status); diff --git a/src/leveldbwrapper.h b/src/leveldbwrapper.h index c65e84270..639f736a5 100644 --- a/src/leveldbwrapper.h +++ b/src/leveldbwrapper.h @@ -22,7 +22,7 @@ public: leveldb_error(const std::string& msg) : std::runtime_error(msg) {} }; -void HandleError(const leveldb::Status& status) throw(leveldb_error); +void HandleError(const leveldb::Status& status); /** Batch of changes queued to be written to a CLevelDBWrapper */ class CLevelDBBatch @@ -90,7 +90,7 @@ public: ~CLevelDBWrapper(); template - bool Read(const K& key, V& value) const throw(leveldb_error) + bool Read(const K& key, V& value) const { CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(ssKey.GetSerializeSize(key)); @@ -115,7 +115,7 @@ public: } template - bool Write(const K& key, const V& value, bool fSync = false) throw(leveldb_error) + bool Write(const K& key, const V& value, bool fSync = false) { CLevelDBBatch batch; batch.Write(key, value); @@ -123,7 +123,7 @@ public: } template - bool Exists(const K& key) const throw(leveldb_error) + bool Exists(const K& key) const { CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(ssKey.GetSerializeSize(key)); @@ -142,14 +142,14 @@ public: } template - bool Erase(const K& key, bool fSync = false) throw(leveldb_error) + bool Erase(const K& key, bool fSync = false) { CLevelDBBatch batch; batch.Erase(key); return WriteBatch(batch, fSync); } - bool WriteBatch(CLevelDBBatch& batch, bool fSync = false) throw(leveldb_error); + bool WriteBatch(CLevelDBBatch& batch, bool fSync = false); // not available for LevelDB; provide for compatibility with BDB bool Flush() @@ -157,7 +157,7 @@ public: return true; } - bool Sync() throw(leveldb_error) + bool Sync() { CLevelDBBatch batch; return WriteBatch(batch, true); diff --git a/src/main.cpp b/src/main.cpp index 48324ca42..bed29a89f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,7 +13,9 @@ #include "chainparams.h" #include "checkpoints.h" #include "checkqueue.h" +#include "consensus/upgrades.h" #include "consensus/validation.h" +#include "deprecation.h" #include "init.h" #include "merkleblock.h" #include "metrics.h" @@ -27,6 +29,7 @@ #include "utilmoneystr.h" #include "validationinterface.h" #include "wallet/asyncrpcoperation_sendmany.h" +#include "wallet/asyncrpcoperation_shieldcoinbase.h" #include @@ -50,7 +53,9 @@ using namespace std; CCriticalSection cs_main; extern uint8_t NOTARY_PUBKEY33[33]; -extern int32_t KOMODO_LOADINGBLOCKS; +extern int32_t KOMODO_LOADINGBLOCKS,KOMODO_LONGESTCHAIN; +int32_t KOMODO_NEWBLOCKS; +void komodo_block2pubkey33(uint8_t *pubkey33,CBlock *block); BlockMap mapBlockIndex; CChain chainActive; @@ -73,6 +78,8 @@ size_t nCoinCacheUsage = 5000 * 300; uint64_t nPruneTarget = 0; bool fAlerts = DEFAULT_ALERTS; +unsigned int expiryDelta = DEFAULT_TX_EXPIRY_DELTA; + /** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */ CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); @@ -100,30 +107,30 @@ const string strMessageMagic = "Komodo Signed Message:\n"; // Internal stuff namespace { - + struct CBlockIndexWorkComparator { bool operator()(CBlockIndex *pa, CBlockIndex *pb) const { // First sort by most total work, ... if (pa->nChainWork > pb->nChainWork) return false; if (pa->nChainWork < pb->nChainWork) return true; - + // ... then by earliest time received, ... if (pa->nSequenceId < pb->nSequenceId) return false; if (pa->nSequenceId > pb->nSequenceId) return true; - + // Use pointer address as tie breaker (should only happen with blocks // loaded from disk, as those all have id 0). if (pa < pb) return false; if (pa > pb) return true; - + // Identical blocks. return false; } }; - + CBlockIndex *pindexBestInvalid; - + /** * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and * as good as our current tip or better. Entries may be failed, though, and pruning nodes may be @@ -133,10 +140,10 @@ namespace { /** Number of nodes with fSyncStarted. */ int nSyncStarted = 0; /** All pairs A->B, where A (or one if its ancestors) misses transactions, but B has transactions. - * Pruned nodes may have entries where B is missing data. - */ + * Pruned nodes may have entries where B is missing data. + */ multimap mapBlocksUnlinked; - + CCriticalSection cs_LastBlockFile; std::vector vinfoBlockFile; int nLastBlockFile = 0; @@ -145,7 +152,7 @@ namespace { * or if we allocate more file space when we're in prune mode */ bool fCheckForPruning = false; - + /** * Every received block is assigned a unique and increasing identifier, so we * know which one to give priority in case of a fork. @@ -153,14 +160,14 @@ namespace { CCriticalSection cs_nBlockSequenceId; /** Blocks loaded from disk are assigned id 0, so start the counter at 1. */ uint32_t nBlockSequenceId = 1; - + /** * Sources of received blocks, saved to be able to send them reject * messages or ban them when processing happens afterwards. Protected by * cs_main. */ map mapBlockSource; - + /** * Filter for transactions that were recently rejected by * AcceptToMemoryPool. These are not rerequested until the chain tip @@ -183,7 +190,7 @@ namespace { */ boost::scoped_ptr recentRejects; uint256 hashRecentRejectsChainTip; - + /** Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */ struct QueuedBlock { uint256 hash; @@ -193,16 +200,16 @@ namespace { 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; - + /** Dirty block index entries. */ set setDirtyBlockIndex; - + /** Dirty block file entries. */ set setDirtyFileInfo; } // anon namespace @@ -213,306 +220,306 @@ namespace { // namespace { - -struct CBlockReject { - unsigned char chRejectCode; - string strRejectReason; - uint256 hashBlock; -}; - -/** - * Maintain validation-specific state about nodes, protected by cs_main, instead - * by CNode's own locks. This simplifies asynchronous operation, where - * processing of incoming data is done after the ProcessMessage call returns, - * and we're no longer holding the node's locks. - */ -struct CNodeState { - //! The peer's address - CService address; - //! Whether we have a fully established connection. - bool fCurrentlyConnected; - //! Accumulated misbehaviour score for this peer. - int nMisbehavior; - //! Whether this peer should be disconnected and banned (unless whitelisted). - bool fShouldBan; - //! String name of this peer (debugging/logging purposes). - std::string name; - //! List of asynchronously-determined block rejections to notify this peer about. - std::vector rejects; - //! The best known block we know this peer has announced. - CBlockIndex *pindexBestKnownBlock; - //! The hash of the last unknown block this peer has announced. - uint256 hashLastUnknownBlock; - //! The last full block we both have. - CBlockIndex *pindexLastCommonBlock; - //! Whether we've started headers synchronization with this peer. - bool fSyncStarted; - //! Since when we're stalling block download progress (in microseconds), or 0. - int64_t nStallingSince; - list vBlocksInFlight; - int nBlocksInFlight; - int nBlocksInFlightValidHeaders; - //! Whether we consider this a preferred download peer. - bool fPreferredDownload; - - CNodeState() { - fCurrentlyConnected = false; - nMisbehavior = 0; - fShouldBan = false; - pindexBestKnownBlock = NULL; - hashLastUnknownBlock.SetNull(); - pindexLastCommonBlock = NULL; - fSyncStarted = false; - nStallingSince = 0; - nBlocksInFlight = 0; - nBlocksInFlightValidHeaders = 0; - fPreferredDownload = false; + + struct CBlockReject { + unsigned char chRejectCode; + string strRejectReason; + uint256 hashBlock; + }; + + /** + * Maintain validation-specific state about nodes, protected by cs_main, instead + * by CNode's own locks. This simplifies asynchronous operation, where + * processing of incoming data is done after the ProcessMessage call returns, + * and we're no longer holding the node's locks. + */ + struct CNodeState { + //! The peer's address + CService address; + //! Whether we have a fully established connection. + bool fCurrentlyConnected; + //! Accumulated misbehaviour score for this peer. + int nMisbehavior; + //! Whether this peer should be disconnected and banned (unless whitelisted). + bool fShouldBan; + //! String name of this peer (debugging/logging purposes). + std::string name; + //! List of asynchronously-determined block rejections to notify this peer about. + std::vector rejects; + //! The best known block we know this peer has announced. + CBlockIndex *pindexBestKnownBlock; + //! The hash of the last unknown block this peer has announced. + uint256 hashLastUnknownBlock; + //! The last full block we both have. + CBlockIndex *pindexLastCommonBlock; + //! Whether we've started headers synchronization with this peer. + bool fSyncStarted; + //! Since when we're stalling block download progress (in microseconds), or 0. + int64_t nStallingSince; + list vBlocksInFlight; + int nBlocksInFlight; + int nBlocksInFlightValidHeaders; + //! Whether we consider this a preferred download peer. + bool fPreferredDownload; + + CNodeState() { + fCurrentlyConnected = false; + nMisbehavior = 0; + fShouldBan = false; + pindexBestKnownBlock = NULL; + hashLastUnknownBlock.SetNull(); + pindexLastCommonBlock = NULL; + fSyncStarted = false; + nStallingSince = 0; + nBlocksInFlight = 0; + nBlocksInFlightValidHeaders = 0; + fPreferredDownload = false; + } + }; + + /** Map maintaining per-node state. Requires cs_main. */ + map mapNodeState; + + // Requires cs_main. + CNodeState *State(NodeId pnode) { + map::iterator it = mapNodeState.find(pnode); + if (it == mapNodeState.end()) + return NULL; + return &it->second; } -}; - -/** Map maintaining per-node state. Requires cs_main. */ -map mapNodeState; - -// Requires cs_main. -CNodeState *State(NodeId pnode) { - map::iterator it = mapNodeState.find(pnode); - if (it == mapNodeState.end()) - return NULL; - return &it->second; -} - -int GetHeight() -{ - LOCK(cs_main); - return chainActive.Height(); -} - -void UpdatePreferredDownload(CNode* node, CNodeState* state) -{ - nPreferredDownload -= state->fPreferredDownload; - - // Whether this node should be marked as a preferred download node. - state->fPreferredDownload = (!node->fInbound || node->fWhitelisted) && !node->fOneShot && !node->fClient; - - 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) -{ - return nTime + 500000 * consensusParams.nPowTargetSpacing * (4 + nValidatedQueuedBefore); -} - -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.address = pnode->addr; -} - -void FinalizeNode(NodeId nodeid) { - LOCK(cs_main); - CNodeState *state = State(nodeid); - - if (state->fSyncStarted) - nSyncStarted--; - - if (state->nMisbehavior == 0 && state->fCurrentlyConnected) { - AddressCurrentlyConnected(state->address); + + int GetHeight() + { + LOCK(cs_main); + return chainActive.Height(); } - - BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) - mapBlocksInFlight.erase(entry.hash); - EraseOrphansFor(nodeid); - nPreferredDownload -= state->fPreferredDownload; - - mapNodeState.erase(nodeid); -} -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); - BOOST_FOREACH(const uint256& removed, vNoSpendsRemaining) - pcoinsTip->Uncache(removed);*/ -} + void UpdatePreferredDownload(CNode* node, CNodeState* state) + { + nPreferredDownload -= state->fPreferredDownload; + + // Whether this node should be marked as a preferred download node. + state->fPreferredDownload = (!node->fInbound || node->fWhitelisted) && !node->fOneShot && !node->fClient; + + nPreferredDownload += state->fPreferredDownload; + } -// Requires cs_main. -// Returns a bool indicating whether we requested this block. -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; - state->vBlocksInFlight.erase(itInFlight->second.second); - state->nBlocksInFlight--; - state->nStallingSince = 0; - mapBlocksInFlight.erase(itInFlight); - return true; + // Returns time at which to timeout block request (nTime in microseconds) + int64_t GetBlockTimeout(int64_t nTime, int nValidatedQueuedBefore, const Consensus::Params &consensusParams) + { + return nTime + 500000 * consensusParams.nPowTargetSpacing * (4 + nValidatedQueuedBefore); } - return false; -} - -// Requires cs_main. -void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL) { - CNodeState *state = State(nodeid); - assert(state != NULL); - - // Make sure it's not listed somewhere already. - MarkBlockAsReceived(hash); - - int64_t nNow = GetTimeMicros(); - QueuedBlock newentry = {hash, pindex, nNow, pindex != NULL, GetBlockTimeout(nNow, nQueuedValidatedHeaders, consensusParams)}; - nQueuedValidatedHeaders += newentry.fValidatedHeaders; - list::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); - state->nBlocksInFlight++; - state->nBlocksInFlightValidHeaders += newentry.fValidatedHeaders; - mapBlocksInFlight[hash] = std::make_pair(nodeid, it); -} - -/** Check whether the last unknown block a peer advertized is not yet known. */ -void ProcessBlockAvailability(NodeId nodeid) { - CNodeState *state = State(nodeid); - assert(state != NULL); - - if (!state->hashLastUnknownBlock.IsNull()) { - BlockMap::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock); - if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0) - { - if (state->pindexBestKnownBlock == NULL || itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) - state->pindexBestKnownBlock = itOld->second; - state->hashLastUnknownBlock.SetNull(); + + 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.address = pnode->addr; + } + + void FinalizeNode(NodeId nodeid) { + LOCK(cs_main); + CNodeState *state = State(nodeid); + + if (state->fSyncStarted) + nSyncStarted--; + + if (state->nMisbehavior == 0 && state->fCurrentlyConnected) { + AddressCurrentlyConnected(state->address); } + + BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) + mapBlocksInFlight.erase(entry.hash); + EraseOrphansFor(nodeid); + nPreferredDownload -= state->fPreferredDownload; + + mapNodeState.erase(nodeid); } -} - -/** Update tracking information about which blocks a peer is assumed to have. */ -void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) { - CNodeState *state = State(nodeid); - assert(state != NULL); - - /*ProcessBlockAvailability(nodeid); - - BlockMap::iterator it = mapBlockIndex.find(hash); - if (it != mapBlockIndex.end() && it->second->nChainWork > 0) { - // An actually better block was announced. - if (state->pindexBestKnownBlock == NULL || it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) - state->pindexBestKnownBlock = it->second; - } else*/ + + void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) { - // An unknown block was announced; just assume that the latest one is the best one. - state->hashLastUnknownBlock = hash; + /* 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); + BOOST_FOREACH(const uint256& removed, vNoSpendsRemaining) + pcoinsTip->Uncache(removed);*/ } -} - -/** Find the last common ancestor two blocks have. - * Both pa and pb must be non-NULL. */ -CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) { - if (pa->nHeight > pb->nHeight) { - pa = pa->GetAncestor(pb->nHeight); - } else if (pb->nHeight > pa->nHeight) { - pb = pb->GetAncestor(pa->nHeight); + + // Requires cs_main. + // Returns a bool indicating whether we requested this block. + 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; + state->vBlocksInFlight.erase(itInFlight->second.second); + state->nBlocksInFlight--; + state->nStallingSince = 0; + mapBlocksInFlight.erase(itInFlight); + return true; + } + return false; } - - while (pa != pb && pa && pb) { - pa = pa->pprev; - pb = pb->pprev; + + // Requires cs_main. + void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL) { + CNodeState *state = State(nodeid); + assert(state != NULL); + + // Make sure it's not listed somewhere already. + MarkBlockAsReceived(hash); + + int64_t nNow = GetTimeMicros(); + QueuedBlock newentry = {hash, pindex, nNow, pindex != NULL, GetBlockTimeout(nNow, nQueuedValidatedHeaders, consensusParams)}; + nQueuedValidatedHeaders += newentry.fValidatedHeaders; + list::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); + state->nBlocksInFlight++; + state->nBlocksInFlightValidHeaders += newentry.fValidatedHeaders; + mapBlocksInFlight[hash] = std::make_pair(nodeid, it); } - - // Eventually all chain branches meet at the genesis block. - assert(pa == pb); - return pa; -} - -/** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has - * at most count entries. */ -void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector& vBlocks, NodeId& nodeStaller) { - if (count == 0) - return; - - vBlocks.reserve(vBlocks.size() + count); - CNodeState *state = State(nodeid); - assert(state != NULL); - - // Make sure pindexBestKnownBlock is up to date, we'll need it. - ProcessBlockAvailability(nodeid); - - if (state->pindexBestKnownBlock == NULL || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork) { - // This peer has nothing interesting. - return; + + /** Check whether the last unknown block a peer advertized is not yet known. */ + void ProcessBlockAvailability(NodeId nodeid) { + CNodeState *state = State(nodeid); + assert(state != NULL); + + if (!state->hashLastUnknownBlock.IsNull()) { + BlockMap::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock); + if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0) + { + if (state->pindexBestKnownBlock == NULL || itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) + state->pindexBestKnownBlock = itOld->second; + state->hashLastUnknownBlock.SetNull(); + } + } } - - if (state->pindexLastCommonBlock == NULL) { - // Bootstrap quickly by guessing a parent of our best tip is the forking point. - // Guessing wrong in either direction is not a problem. - state->pindexLastCommonBlock = chainActive[std::min(state->pindexBestKnownBlock->nHeight, chainActive.Height())]; + + /** Update tracking information about which blocks a peer is assumed to have. */ + void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) { + CNodeState *state = State(nodeid); + assert(state != NULL); + + /*ProcessBlockAvailability(nodeid); + + BlockMap::iterator it = mapBlockIndex.find(hash); + if (it != mapBlockIndex.end() && it->second->nChainWork > 0) { + // An actually better block was announced. + if (state->pindexBestKnownBlock == NULL || it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) + state->pindexBestKnownBlock = it->second; + } else*/ + { + // An unknown block was announced; just assume that the latest one is the best one. + state->hashLastUnknownBlock = hash; + } } - - // If the peer reorganized, our previous pindexLastCommonBlock may not be an ancestor - // of its current tip anymore. Go back enough to fix that. - state->pindexLastCommonBlock = LastCommonAncestor(state->pindexLastCommonBlock, state->pindexBestKnownBlock); - if (state->pindexLastCommonBlock == state->pindexBestKnownBlock) - return; - - std::vector vToFetch; - CBlockIndex *pindexWalk = state->pindexLastCommonBlock; - // Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last - // linked block we have in common with this peer. The +1 is so we can detect stalling, namely if we would be able to - // download that next block if the window were 1 larger. - int nWindowEnd = state->pindexLastCommonBlock->nHeight + BLOCK_DOWNLOAD_WINDOW; - int nMaxHeight = std::min(state->pindexBestKnownBlock->nHeight, nWindowEnd + 1); - NodeId waitingfor = -1; - while (pindexWalk->nHeight < nMaxHeight) { - // Read up to 128 (or more, if more blocks than that are needed) successors of pindexWalk (towards - // pindexBestKnownBlock) into vToFetch. We fetch 128, because CBlockIndex::GetAncestor may be as expensive - // as iterating over ~100 CBlockIndex* entries anyway. - int nToFetch = std::min(nMaxHeight - pindexWalk->nHeight, std::max(count - vBlocks.size(), 128)); - vToFetch.resize(nToFetch); - pindexWalk = state->pindexBestKnownBlock->GetAncestor(pindexWalk->nHeight + nToFetch); - vToFetch[nToFetch - 1] = pindexWalk; - for (unsigned int i = nToFetch - 1; i > 0; i--) { - vToFetch[i - 1] = vToFetch[i]->pprev; - } - - // Iterate over those blocks in vToFetch (in forward direction), adding the ones that - // are not yet downloaded and not in flight to vBlocks. In the mean time, update - // pindexLastCommonBlock as long as all ancestors are already downloaded, or if it's - // already part of our chain (and therefore don't need it even if pruned). - BOOST_FOREACH(CBlockIndex* pindex, vToFetch) { - if (!pindex->IsValid(BLOCK_VALID_TREE)) { - // We consider the chain that this peer is on invalid. - return; + + /** Find the last common ancestor two blocks have. + * Both pa and pb must be non-NULL. */ + CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) { + if (pa->nHeight > pb->nHeight) { + pa = pa->GetAncestor(pb->nHeight); + } else if (pb->nHeight > pa->nHeight) { + pb = pb->GetAncestor(pa->nHeight); + } + + while (pa != pb && pa && pb) { + pa = pa->pprev; + pb = pb->pprev; + } + + // Eventually all chain branches meet at the genesis block. + assert(pa == pb); + return pa; + } + + /** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has + * at most count entries. */ + void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector& vBlocks, NodeId& nodeStaller) { + if (count == 0) + return; + + vBlocks.reserve(vBlocks.size() + count); + CNodeState *state = State(nodeid); + assert(state != NULL); + + // Make sure pindexBestKnownBlock is up to date, we'll need it. + ProcessBlockAvailability(nodeid); + + if (state->pindexBestKnownBlock == NULL || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork) { + // This peer has nothing interesting. + return; + } + + if (state->pindexLastCommonBlock == NULL) { + // Bootstrap quickly by guessing a parent of our best tip is the forking point. + // Guessing wrong in either direction is not a problem. + state->pindexLastCommonBlock = chainActive[std::min(state->pindexBestKnownBlock->nHeight, chainActive.Height())]; + } + + // If the peer reorganized, our previous pindexLastCommonBlock may not be an ancestor + // of its current tip anymore. Go back enough to fix that. + state->pindexLastCommonBlock = LastCommonAncestor(state->pindexLastCommonBlock, state->pindexBestKnownBlock); + if (state->pindexLastCommonBlock == state->pindexBestKnownBlock) + return; + + std::vector vToFetch; + CBlockIndex *pindexWalk = state->pindexLastCommonBlock; + // Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last + // linked block we have in common with this peer. The +1 is so we can detect stalling, namely if we would be able to + // download that next block if the window were 1 larger. + int nWindowEnd = state->pindexLastCommonBlock->nHeight + BLOCK_DOWNLOAD_WINDOW; + int nMaxHeight = std::min(state->pindexBestKnownBlock->nHeight, nWindowEnd + 1); + NodeId waitingfor = -1; + while (pindexWalk->nHeight < nMaxHeight) { + // Read up to 128 (or more, if more blocks than that are needed) successors of pindexWalk (towards + // pindexBestKnownBlock) into vToFetch. We fetch 128, because CBlockIndex::GetAncestor may be as expensive + // as iterating over ~100 CBlockIndex* entries anyway. + int nToFetch = std::min(nMaxHeight - pindexWalk->nHeight, std::max(count - vBlocks.size(), 128)); + vToFetch.resize(nToFetch); + pindexWalk = state->pindexBestKnownBlock->GetAncestor(pindexWalk->nHeight + nToFetch); + vToFetch[nToFetch - 1] = pindexWalk; + for (unsigned int i = nToFetch - 1; i > 0; i--) { + vToFetch[i - 1] = vToFetch[i]->pprev; } - if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) { - if (pindex->nChainTx) - state->pindexLastCommonBlock = pindex; - } else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) { - // The block is not already downloaded, and not yet in flight. - if (pindex->nHeight > nWindowEnd) { - // We reached the end of the window. - if (vBlocks.size() == 0 && waitingfor != nodeid) { - // We aren't able to fetch anything, but we would be if the download window was one larger. - nodeStaller = waitingfor; - } + + // Iterate over those blocks in vToFetch (in forward direction), adding the ones that + // are not yet downloaded and not in flight to vBlocks. In the meantime, update + // pindexLastCommonBlock as long as all ancestors are already downloaded, or if it's + // already part of our chain (and therefore don't need it even if pruned). + BOOST_FOREACH(CBlockIndex* pindex, vToFetch) { + if (!pindex->IsValid(BLOCK_VALID_TREE)) { + // We consider the chain that this peer is on invalid. return; } - vBlocks.push_back(pindex); - if (vBlocks.size() == count) { - return; + if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) { + if (pindex->nChainTx) + state->pindexLastCommonBlock = pindex; + } else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) { + // The block is not already downloaded, and not yet in flight. + if (pindex->nHeight > nWindowEnd) { + // We reached the end of the window. + if (vBlocks.size() == 0 && waitingfor != nodeid) { + // We aren't able to fetch anything, but we would be if the download window was one larger. + nodeStaller = waitingfor; + } + return; + } + vBlocks.push_back(pindex); + if (vBlocks.size() == count) { + return; + } + } else if (waitingfor == -1) { + // This is the first already-in-flight block. + waitingfor = mapBlocksInFlight[pindex->GetBlockHash()].first; } - } else if (waitingfor == -1) { - // This is the first already-in-flight block. - waitingfor = mapBlocksInFlight[pindex->GetBlockHash()].first; } } } -} - + } // anon namespace bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { @@ -558,6 +565,9 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc CBlockIndex* pindex = (*mi).second; if (pindex != 0 && chain.Contains(pindex)) return pindex; + if (pindex != 0 && pindex->GetAncestor(chain.Height()) == chain.Tip()) { + return chain.Tip(); + } } } return chain.Genesis(); @@ -581,7 +591,7 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(c uint256 hash = tx.GetHash(); if (mapOrphanTransactions.count(hash)) return false; - + // Ignore big transactions, to avoid a // send-big-orphans memory exhaustion attack. If a peer has a legitimate // large transaction with a missing parent then we assume @@ -595,12 +605,12 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(c LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString()); return false; } - + mapOrphanTransactions[hash].tx = tx; mapOrphanTransactions[hash].fromPeer = peer; BOOST_FOREACH(const CTxIn& txin, tx.vin) - mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash); - + mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash); + LogPrint("mempool", "stored orphan tx %s (mapsz %u prevsz %u)\n", hash.ToString(), mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size()); return true; @@ -650,25 +660,31 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) EXCLUSIVE_LOCKS_REQUIRE map::iterator it = mapOrphanTransactions.lower_bound(randomhash); if (it == mapOrphanTransactions.end()) it = mapOrphanTransactions.begin(); - EraseOrphanTx(it->first); - ++nEvicted; + EraseOrphanTx(it->first); + ++nEvicted; } return nEvicted; } - - - - - -bool IsStandardTx(const CTransaction& tx, string& reason) +bool IsStandardTx(const CTransaction& tx, string& reason, const int nHeight) { - if (tx.nVersion > CTransaction::MAX_CURRENT_VERSION || tx.nVersion < CTransaction::MIN_CURRENT_VERSION) { - reason = "version"; - return false; + bool isOverwinter = NetworkUpgradeActive(nHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER); + + if (isOverwinter) { + // Overwinter standard rules apply + if (tx.nVersion > CTransaction::OVERWINTER_MAX_CURRENT_VERSION || tx.nVersion < CTransaction::OVERWINTER_MIN_CURRENT_VERSION) { + reason = "overwinter-version"; + return false; + } + } else { + // Sprout standard rules apply + if (tx.nVersion > CTransaction::SPROUT_MAX_CURRENT_VERSION || tx.nVersion < CTransaction::SPROUT_MIN_CURRENT_VERSION) { + reason = "version"; + return false; + } } - + BOOST_FOREACH(const CTxIn& txin, tx.vin) { // Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed @@ -687,7 +703,7 @@ bool IsStandardTx(const CTransaction& tx, string& reason) return false; } } - + unsigned int v=0,nDataOut = 0; txnouttype whichType; BOOST_FOREACH(const CTxOut& txout, tx.vout) @@ -713,13 +729,13 @@ bool IsStandardTx(const CTransaction& tx, string& reason) } v++; } - + // only one OP_RETURN txout is permitted if (nDataOut > 1) { reason = "multi-op-return"; return false; } - + return true; } @@ -738,17 +754,25 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) } else if (!txin.IsFinal()) { - printf("non-final txin seq.%x locktime.%u vs nTime.%u\n",txin.nSequence,(uint32_t)tx.nLockTime,(uint32_t)nBlockTime); + //printf("non-final txin seq.%x locktime.%u vs nTime.%u\n",txin.nSequence,(uint32_t)tx.nLockTime,(uint32_t)nBlockTime); return false; } } return true; } +bool IsExpiredTx(const CTransaction &tx, int nBlockHeight) +{ + if (tx.nExpiryHeight == 0 || tx.IsCoinBase()) { + return false; + } + return static_cast(nBlockHeight) > tx.nExpiryHeight; +} + bool CheckFinalTx(const CTransaction &tx, int flags) { AssertLockHeld(cs_main); - + // By convention a negative value for flags indicates that the // current network-enforced consensus rules should be used. In // a future soft-fork scenario that would mean checking which @@ -756,7 +780,7 @@ bool CheckFinalTx(const CTransaction &tx, int flags) // appropriate flags. At the present time no soft-forks are // scheduled, so no flags are set. flags = std::max(flags, 0); - + // CheckFinalTx() uses chainActive.Height()+1 to evaluate // nLockTime because when IsFinalTx() is called within // CBlock::AcceptBlock(), the height of the block *being* @@ -764,15 +788,15 @@ bool CheckFinalTx(const CTransaction &tx, int flags) // transaction can be part of the *next* block, we need to call // 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: const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST) - ? chainActive.Tip()->GetMedianTimePast() - : GetAdjustedTime(); - + ? chainActive.Tip()->GetMedianTimePast() + : GetAdjustedTime(); + return IsFinalTx(tx, nBlockHeight, nBlockTime); } @@ -785,15 +809,15 @@ bool CheckFinalTx(const CTransaction &tx, int flags) * 2. P2SH scripts with a crazy number of expensive * CHECKSIG/CHECKMULTISIG operations */ -bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) +bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, uint32_t consensusBranchId) { if (tx.IsCoinBase()) return true; // Coinbases don't use vin normally - + for (unsigned int i = 0; i < tx.vin.size(); i++) { const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]); - + vector > vSolutions; txnouttype whichType; // get the scriptPubKey corresponding to this input: @@ -803,7 +827,7 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions); if (nArgsExpected < 0) return false; - + // Transactions with extra stuff in their scriptSigs are // non-standard. Note that this EvalScript() call will // be quick, because if there are any operations @@ -811,9 +835,9 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) // IsStandardTx() will have already returned false // and this method isn't called. vector > stack; - if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker())) + if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), consensusBranchId)) return false; - + if (whichType == TX_SCRIPTHASH) { if (stack.empty()) @@ -836,11 +860,11 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) return (sigops <= MAX_P2SH_SIGOPS); } } - + if (stack.size() != (unsigned int)nArgsExpected) return false; } - + return true; } @@ -862,7 +886,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in { if (tx.IsCoinBase()) return 0; - + unsigned int nSigOps = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) { @@ -873,7 +897,80 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in return nSigOps; } -bool CheckTransaction(const CTransaction& tx, CValidationState &state,libzcash::ProofVerifier& verifier) +/** + * Check a transaction contextually against a set of consensus rules valid at a given block height. + * + * Notes: + * 1. AcceptToMemoryPool calls CheckTransaction and this function. + * 2. ProcessNewBlock calls AcceptBlock, which calls CheckBlock (which calls CheckTransaction) + * and ContextualCheckBlock (which calls this function). + */ +bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, const int nHeight, const int dosLevel) +{ + bool isOverwinter = NetworkUpgradeActive(nHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER); + bool isSprout = !isOverwinter; + + // If Sprout rules apply, reject transactions which are intended for Overwinter and beyond + if (isSprout && tx.fOverwintered) { + return state.DoS(dosLevel, error("ContextualCheckTransaction(): overwinter is not active yet"), + REJECT_INVALID, "tx-overwinter-not-active"); + } + + // If Overwinter rules apply: + if (isOverwinter) { + // Reject transactions with valid version but missing overwinter flag + if (tx.nVersion >= OVERWINTER_MIN_TX_VERSION && !tx.fOverwintered) { + return state.DoS(dosLevel, error("ContextualCheckTransaction(): overwinter flag must be set"), + REJECT_INVALID, "tx-overwinter-flag-not-set"); + } + + // Reject transactions with invalid version + if (tx.fOverwintered && tx.nVersion > OVERWINTER_MAX_TX_VERSION ) { + return state.DoS(100, error("CheckTransaction(): overwinter version too high"), + REJECT_INVALID, "bad-tx-overwinter-version-too-high"); + } + + // Reject transactions intended for Sprout + if (!tx.fOverwintered) { + return state.DoS(dosLevel, error("ContextualCheckTransaction: overwinter is active"), + REJECT_INVALID, "tx-overwinter-active"); + } + + // Check that all transactions are unexpired + if (IsExpiredTx(tx, nHeight)) { + return state.DoS(dosLevel, error("ContextualCheckTransaction(): transaction is expired"), REJECT_INVALID, "tx-overwinter-expired"); + } + } + + if (!(tx.IsCoinBase() || tx.vjoinsplit.empty())) { + auto consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus()); + // Empty output script. + CScript scriptCode; + uint256 dataToBeSigned; + 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"), + REJECT_INVALID, "error-computing-signature-hash"); + } + + BOOST_STATIC_ASSERT(crypto_sign_PUBLICKEYBYTES == 32); + + // We rely on libsodium to check that the signature is canonical. + // https://github.com/jedisct1/libsodium/commit/62911edb7ff2275cccd74bf1c8aefcc4d76924e0 + if (crypto_sign_verify_detached(&tx.joinSplitSig[0], + dataToBeSigned.begin(), 32, + tx.joinSplitPubKey.begin() + ) != 0) { + return state.DoS(100, error("CheckTransaction(): invalid joinsplit signature"), + REJECT_INVALID, "bad-txns-invalid-joinsplit-signature"); + } + } + return true; +} + +bool CheckTransaction(const CTransaction& tx, CValidationState &state, + libzcash::ProofVerifier& verifier) { static uint256 array[64]; static int32_t numbanned,indallvouts; int32_t j,k,n; if ( *(int32_t *)&array[0] == 0 ) @@ -892,11 +989,11 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state,libzcash:: } } } - // Don't count coinbase transactions because mining skews the count + // Don't count coinbase transactions because mining skews the count if (!tx.IsCoinBase()) { transactionsValidated.increment(); } - + if (!CheckTransactionWithoutProofVerification(tx, state)) { return false; } else { @@ -904,7 +1001,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state,libzcash:: BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { if (!joinsplit.Verify(*pzcashParams, verifier, tx.joinSplitPubKey)) { return state.DoS(100, error("CheckTransaction(): joinsplit does not verify"), - REJECT_INVALID, "bad-txns-joinsplit-verification-failed"); + REJECT_INVALID, "bad-txns-joinsplit-verification-failed"); } } return true; @@ -914,13 +1011,48 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state,libzcash:: bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidationState &state) { // Basic checks that don't depend on any context - - // Check transaction version - if (tx.nVersion < MIN_TX_VERSION) { + + /** + * Previously: + * 1. The consensus rule below was: + * if (tx.nVersion < SPROUT_MIN_TX_VERSION) { ... } + * which checked if tx.nVersion fell within the range: + * INT32_MIN <= tx.nVersion < SPROUT_MIN_TX_VERSION + * 2. The parser allowed tx.nVersion to be negative + * + * Now: + * 1. The consensus rule checks to see if tx.Version falls within the range: + * 0 <= tx.nVersion < SPROUT_MIN_TX_VERSION + * 2. The previous consensus rule checked for negative values within the range: + * INT32_MIN <= tx.nVersion < 0 + * This is unnecessary for Overwinter transactions since the parser now + * interprets the sign bit as fOverwintered, so tx.nVersion is always >=0, + * and when Overwinter is not active ContextualCheckTransaction rejects + * transactions with fOverwintered set. When fOverwintered is set, + * this function and ContextualCheckTransaction will together check to + * ensure tx.nVersion avoids the following ranges: + * 0 <= tx.nVersion < OVERWINTER_MIN_TX_VERSION + * OVERWINTER_MAX_TX_VERSION < tx.nVersion <= INT32_MAX + */ + if (!tx.fOverwintered && tx.nVersion < SPROUT_MIN_TX_VERSION) { return state.DoS(100, error("CheckTransaction(): version too low"), REJECT_INVALID, "bad-txns-version-too-low"); } - + else if (tx.fOverwintered) { + if (tx.nVersion < OVERWINTER_MIN_TX_VERSION) { + return state.DoS(100, error("CheckTransaction(): overwinter version too low"), + REJECT_INVALID, "bad-tx-overwinter-version-too-low"); + } + if (tx.nVersionGroupId != OVERWINTER_VERSION_GROUP_ID) { + return state.DoS(100, error("CheckTransaction(): unknown tx version group id"), + REJECT_INVALID, "bad-tx-version-group-id"); + } + if (tx.nExpiryHeight >= TX_EXPIRY_HEIGHT_THRESHOLD) { + return state.DoS(100, error("CheckTransaction(): expiry height is too high"), + REJECT_INVALID, "bad-tx-expiry-height-too-high"); + } + } + // Transactions can contain empty `vin` and `vout` so long as // `vjoinsplit` is non-empty. if (tx.vin.empty() && tx.vjoinsplit.empty()) @@ -929,13 +1061,13 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio if (tx.vout.empty() && tx.vjoinsplit.empty()) return state.DoS(10, error("CheckTransaction(): vout empty"), REJECT_INVALID, "bad-txns-vout-empty"); - + // Size limits BOOST_STATIC_ASSERT(MAX_BLOCK_SIZE > MAX_TX_SIZE); // sanity if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_TX_SIZE) return state.DoS(100, error("CheckTransaction(): size limits failed"), REJECT_INVALID, "bad-txns-oversize"); - + // Check for negative or overflow output values CAmount nValueOut = 0; BOOST_FOREACH(const CTxOut& txout, tx.vout) @@ -953,7 +1085,7 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio return state.DoS(100, error("CheckTransaction(): txout total out of range"), REJECT_INVALID, "bad-txns-txouttotal-toolarge"); } - + // Ensure that joinsplit values are well-formed BOOST_FOREACH(const JSDescription& joinsplit, tx.vjoinsplit) { @@ -961,34 +1093,34 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio return state.DoS(100, error("CheckTransaction(): joinsplit.vpub_old negative"), REJECT_INVALID, "bad-txns-vpub_old-negative"); } - + if (joinsplit.vpub_new < 0) { return state.DoS(100, error("CheckTransaction(): joinsplit.vpub_new negative"), REJECT_INVALID, "bad-txns-vpub_new-negative"); } - + if (joinsplit.vpub_old > MAX_MONEY) { return state.DoS(100, error("CheckTransaction(): joinsplit.vpub_old too high"), REJECT_INVALID, "bad-txns-vpub_old-toolarge"); } - + if (joinsplit.vpub_new > MAX_MONEY) { return state.DoS(100, error("CheckTransaction(): joinsplit.vpub_new too high"), REJECT_INVALID, "bad-txns-vpub_new-toolarge"); } - + if (joinsplit.vpub_new != 0 && joinsplit.vpub_old != 0) { return state.DoS(100, error("CheckTransaction(): joinsplit.vpub_new and joinsplit.vpub_old both nonzero"), REJECT_INVALID, "bad-txns-vpubs-both-nonzero"); } - + nValueOut += joinsplit.vpub_old; if (!MoneyRange(nValueOut)) { return state.DoS(100, error("CheckTransaction(): txout total out of range"), REJECT_INVALID, "bad-txns-txouttotal-toolarge"); } } - + // Ensure input values do not exceed MAX_MONEY // We have not resolved the txin values at this stage, // but we do know what the joinsplits claim to add @@ -998,15 +1130,15 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio for (std::vector::const_iterator it(tx.vjoinsplit.begin()); it != tx.vjoinsplit.end(); ++it) { nValueIn += it->vpub_new; - + if (!MoneyRange(it->vpub_new) || !MoneyRange(nValueIn)) { return state.DoS(100, error("CheckTransaction(): txin total out of range"), REJECT_INVALID, "bad-txns-txintotal-toolarge"); } } } - - + + // Check for duplicate inputs set vInOutPoints; BOOST_FOREACH(const CTxIn& txin, tx.vin) @@ -1016,7 +1148,7 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio REJECT_INVALID, "bad-txns-inputs-duplicate"); vInOutPoints.insert(txin.prevout); } - + // Check for duplicate joinsplit nullifiers in this transaction set vJoinSplitNullifiers; BOOST_FOREACH(const JSDescription& joinsplit, tx.vjoinsplit) @@ -1025,19 +1157,19 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio { if (vJoinSplitNullifiers.count(nf)) return state.DoS(100, error("CheckTransaction(): duplicate nullifiers"), - REJECT_INVALID, "bad-joinsplits-nullifiers-duplicate"); - + REJECT_INVALID, "bad-joinsplits-nullifiers-duplicate"); + vJoinSplitNullifiers.insert(nf); } } - + if (tx.IsCoinBase()) { // There should be no joinsplits in a coinbase transaction if (tx.vjoinsplit.size() > 0) return state.DoS(100, error("CheckTransaction(): coinbase has joinsplits"), REJECT_INVALID, "bad-cb-has-joinsplits"); - + 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"); @@ -1045,35 +1177,11 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio else { BOOST_FOREACH(const CTxIn& txin, tx.vin) - if (txin.prevout.IsNull()) - return state.DoS(10, error("CheckTransaction(): prevout is null"), - REJECT_INVALID, "bad-txns-prevout-null"); - - if (tx.vjoinsplit.size() > 0) { - // Empty output script. - CScript scriptCode; - uint256 dataToBeSigned; - try { - dataToBeSigned = SignatureHash(scriptCode, tx, NOT_AN_INPUT, SIGHASH_ALL); - } catch (std::logic_error ex) { - return state.DoS(100, error("CheckTransaction(): error computing signature hash"), - REJECT_INVALID, "error-computing-signature-hash"); - } - - BOOST_STATIC_ASSERT(crypto_sign_PUBLICKEYBYTES == 32); - - // We rely on libsodium to check that the signature is canonical. - // https://github.com/jedisct1/libsodium/commit/62911edb7ff2275cccd74bf1c8aefcc4d76924e0 - if (crypto_sign_verify_detached(&tx.joinSplitSig[0], - dataToBeSigned.begin(), 32, - tx.joinSplitPubKey.begin() - ) != 0) { - return state.DoS(100, error("CheckTransaction(): invalid joinsplit signature"), - REJECT_INVALID, "bad-txns-invalid-joinsplit-signature"); - } - } + if (txin.prevout.IsNull()) + return state.DoS(10, error("CheckTransaction(): prevout is null"), + REJECT_INVALID, "bad-txns-prevout-null"); } - + return true; } @@ -1089,9 +1197,9 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF if (dPriorityDelta > 0 || nFeeDelta > 0) return 0; } - + CAmount nMinFee = ::minRelayTxFee.GetFee(nBytes); - + if (fAllowFree) { // There is a free transaction area in blocks created by most miners, @@ -1101,7 +1209,7 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF if (nBytes < (DEFAULT_BLOCK_PRIORITY_SIZE - 1000)) nMinFee = 0; } - + if (!MoneyRange(nMinFee)) nMinFee = MAX_MONEY; return nMinFee; @@ -1113,6 +1221,21 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa AssertLockHeld(cs_main); if (pfMissingInputs) *pfMissingInputs = false; + + int nextBlockHeight = chainActive.Height() + 1; + auto consensusBranchId = CurrentEpochBranchId(nextBlockHeight, Params().GetConsensus()); + + // Node operator can choose to reject tx by number of transparent inputs + static_assert(std::numeric_limits::max() >= std::numeric_limits::max(), "size_t too small"); + size_t limit = (size_t) GetArg("-mempooltxinputlimit", 0); + if (limit > 0) { + size_t n = tx.vin.size(); + if (n > limit) { + LogPrint("mempool", "Dropping txid %s : too many transparent inputs %zu > limit %zu\n", tx.GetHash().ToString(), n, limit ); + return false; + } + } + auto verifier = libzcash::ProofVerifier::Strict(); if ( komodo_validate_interest(tx,chainActive.Tip()->nHeight+1,chainActive.Tip()->GetMedianTimePast() + 777,0) < 0 ) { @@ -1121,9 +1244,16 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } if (!CheckTransaction(tx, state, verifier)) { - fprintf(stderr,"accept failure.0\n"); + return error("AcceptToMemoryPool: CheckTransaction failed"); } + // 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, nextBlockHeight, 10)) + { + return error("AcceptToMemoryPool: ContextualCheckTransaction failed"); + } + // Coinbase is only valid in a block, not as a loose transaction if (tx.IsCoinBase()) { @@ -1132,9 +1262,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } // Rather not work on nonstandard transactions (unless -testnet/-regtest) string reason; - if (Params().RequireStandard() && !IsStandardTx(tx, reason)) + if (Params().RequireStandard() && !IsStandardTx(tx, reason, nextBlockHeight)) { - fprintf(stderr,"AcceptToMemoryPool nonstandard transaction: %s\n",reason.c_str()); + fprintf(stderr,"AcceptToMemoryPool reject nonstandard transaction: %s\n",reason.c_str()); return state.DoS(0,error("AcceptToMemoryPool: nonstandard transaction: %s", reason),REJECT_NONSTANDARD, reason); } // Only accept nLockTime-using transactions that can be mined in the next @@ -1145,99 +1275,100 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa //fprintf(stderr,"AcceptToMemoryPool reject non-final\n"); return state.DoS(0, false, REJECT_NONSTANDARD, "non-final"); } - // is it already in the memory pool? + // is it already in the memory pool? uint256 hash = tx.GetHash(); if (pool.exists(hash)) { fprintf(stderr,"already in mempool\n"); return false; } - + // Check for conflicts with in-memory transactions { - LOCK(pool.cs); // protect pool.mapNextTx - for (unsigned int i = 0; i < tx.vin.size(); i++) - { - COutPoint outpoint = tx.vin[i].prevout; - if (pool.mapNextTx.count(outpoint)) + LOCK(pool.cs); // protect pool.mapNextTx + for (unsigned int i = 0; i < tx.vin.size(); i++) { - static uint32_t counter; - // Disable replacement feature for now - //if ( counter++ < 100 ) + COutPoint outpoint = tx.vin[i].prevout; + if (pool.mapNextTx.count(outpoint)) + { + static uint32_t counter; + // Disable replacement feature for now + //if ( counter++ < 100 ) fprintf(stderr,"Disable replacement feature for now\n"); - return false; + return false; + } } - } - BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { - BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) { - if (pool.mapNullifiers.count(nf)) + BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) + { + BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) { - fprintf(stderr,"pool.mapNullifiers.count\n"); - return false; + if (pool.mapNullifiers.count(nf)) + { + fprintf(stderr,"pool.mapNullifiers.count\n"); + return false; + } } } } - } - + { CCoinsView dummy; CCoinsViewCache view(&dummy); int64_t interest; CAmount nValueIn = 0; { - LOCK(pool.cs); - CCoinsViewMemPool viewMemPool(pcoinsTip, pool); - view.SetBackend(viewMemPool); - - // do we already have it? - if (view.HaveCoins(hash)) - { - fprintf(stderr,"view.HaveCoins(hash) error\n"); - return false; - } - - // 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). - BOOST_FOREACH(const CTxIn txin, tx.vin) { - if (!view.HaveCoins(txin.prevout.hash)) { - if (pfMissingInputs) - *pfMissingInputs = true; - //fprintf(stderr,"missing inputs\n"); + LOCK(pool.cs); + CCoinsViewMemPool viewMemPool(pcoinsTip, pool); + view.SetBackend(viewMemPool); + + // do we already have it? + if (view.HaveCoins(hash)) + { + fprintf(stderr,"view.HaveCoins(hash) error\n"); return false; } - } - - // are the actual inputs available? - if (!view.HaveInputs(tx)) - { - //fprintf(stderr,"accept failure.1\n"); - return state.Invalid(error("AcceptToMemoryPool: inputs already spent"),REJECT_DUPLICATE, "bad-txns-inputs-spent"); - } - // are the joinsplit's requirements met? - if (!view.HaveJoinSplitRequirements(tx)) - { - fprintf(stderr,"accept failure.2\n"); - return state.Invalid(error("AcceptToMemoryPool: joinsplit requirements not met"),REJECT_DUPLICATE, "bad-txns-joinsplit-requirements-not-met"); - } - - // Bring the best block into scope - view.GetBestBlock(); - - nValueIn = view.GetValueIn(chainActive.Tip()->nHeight,&interest,tx,chainActive.Tip()->nTime); + + // 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). + BOOST_FOREACH(const CTxIn txin, tx.vin) + { + if (!view.HaveCoins(txin.prevout.hash)) + { + if (pfMissingInputs) + *pfMissingInputs = true; + //fprintf(stderr,"missing inputs\n"); + return false; + } + } + + // are the actual inputs available? + if (!view.HaveInputs(tx)) + { + //fprintf(stderr,"accept failure.1\n"); + return state.Invalid(error("AcceptToMemoryPool: inputs already spent"),REJECT_DUPLICATE, "bad-txns-inputs-spent"); + } + // are the joinsplit's requirements met? + if (!view.HaveJoinSplitRequirements(tx)) + { + //fprintf(stderr,"accept failure.2\n"); + return state.Invalid(error("AcceptToMemoryPool: joinsplit requirements not met"),REJECT_DUPLICATE, "bad-txns-joinsplit-requirements-not-met"); + } + + // Bring the best block into scope + view.GetBestBlock(); + + nValueIn = view.GetValueIn(chainActive.Tip()->nHeight,&interest,tx,chainActive.Tip()->nTime); if ( 0 && interest != 0 ) fprintf(stderr,"add interest %.8f\n",(double)interest/COIN); - // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool - view.SetBackend(dummy); + // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool + view.SetBackend(dummy); } - + // Check for non-standard pay-to-script-hash in inputs - if (Params().RequireStandard() && !AreInputsStandard(tx, view)) - { - fprintf(stderr,"accept failure.3\n"); - return error("AcceptToMemoryPool: nonstandard transaction input"); - } - + if (Params().RequireStandard() && !AreInputsStandard(tx, view, consensusBranchId)) + return error("AcceptToMemoryPool: reject nonstandard transaction input"); + // Check that the transaction doesn't have an excessive number of // sigops, making it impossible to mine. Since the coinbase transaction // itself can contain sigops MAX_STANDARD_TX_SIGOPS is less than @@ -1250,14 +1381,30 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa fprintf(stderr,"accept failure.4\n"); 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"); } - + CAmount nValueOut = tx.GetValueOut(); CAmount nFees = nValueIn-nValueOut; double dPriority = view.GetPriority(tx, chainActive.Height()); - - CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), mempool.HasNoInputsOf(tx)); + + // Keep track of transactions that spend a coinbase, which we re-scan + // during reorgs to ensure COINBASE_MATURITY is still met. + bool fSpendsCoinbase = false; + BOOST_FOREACH(const CTxIn &txin, tx.vin) { + const CCoins *coins = view.AccessCoins(txin.prevout.hash); + if (coins->IsCoinBase()) { + fSpendsCoinbase = true; + break; + } + } + + // Grab the branch ID we expect this transaction to commit to. We don't + // yet know if it does, but if the entry gets added to the mempool, then + // 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); unsigned int nSize = entry.GetTxSize(); - + // 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. @@ -1266,17 +1413,17 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa CAmount txMinFee = GetMinRelayFee(tx, nSize, true); if (fLimitFree && nFees < txMinFee) { - fprintf(stderr,"accept failure.5\n"); + //fprintf(stderr,"accept failure.5\n"); return state.DoS(0, error("AcceptToMemoryPool: not enough fees %s, %d < %d",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", false) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) { fprintf(stderr,"accept failure.6\n"); 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. @@ -1286,9 +1433,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa 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; @@ -1302,21 +1449,22 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); dFreeCount += nSize; } - + if (fRejectAbsurdFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000 && nFees > nValueOut/19 ) { fprintf(stderr,"accept failure.8\n"); return error("AcceptToMemoryPool: absurdly high fees %s, %d > %d",hash.ToString(), nFees, ::minRelayTxFee.GetFee(nSize) * 10000); } - + // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!ContextualCheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true, Params().GetConsensus())) + PrecomputedTransactionData txdata(tx); + if (!ContextualCheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId)) { - fprintf(stderr,"accept failure.9\n"); + //fprintf(stderr,"accept failure.9\n"); return error("AcceptToMemoryPool: ConnectInputs failed %s", hash.ToString()); } - + // Check again against just the consensus-critical mandatory script // verification flags, in case of bugs in the standard flags that cause // transactions to pass as valid when they're actually invalid. For @@ -1326,20 +1474,20 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // There is a similar check in CreateNewBlock() to prevent creating // invalid blocks, however allowing such transactions into the mempool // can be exploited as a DoS attack. - if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, Params().GetConsensus())) + if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId)) { fprintf(stderr,"accept failure.10\n"); return error("AcceptToMemoryPool: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s", hash.ToString()); } - + // Store transaction in memory if ( komodo_is_notarytx(tx) == 0 ) KOMODO_ON_DEMAND++; pool.addUnchecked(hash, entry, !IsInitialBlockDownload()); } - + SyncWithWallets(tx, NULL); - + return true; } @@ -1347,14 +1495,14 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow) { CBlockIndex *pindexSlow = NULL; - + LOCK(cs_main); - + if (mempool.lookup(hash, txOut)) { return true; } - + if (fTxIndex) { CDiskTxPos postx; if (pblocktree->ReadTxIndex(hash, postx)) { @@ -1375,7 +1523,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock return true; } } - + if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it int nHeight = -1; { @@ -1387,7 +1535,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock if (nHeight > 0) pindexSlow = chainActive[nHeight]; } - + if (pindexSlow) { CBlock block; if (ReadBlockFromDisk(block, pindexSlow)) { @@ -1400,23 +1548,23 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock } } } - + return false; } /*char *komodo_getspendscript(uint256 hash,int32_t n) -{ - CTransaction tx; uint256 hashBlock; - if ( !GetTransaction(hash,tx,hashBlock,true) ) - { - printf("null GetTransaction\n"); - return(0); - } - if ( n >= 0 && n < tx.vout.size() ) - return((char *)tx.vout[n].scriptPubKey.ToString().c_str()); - else printf("getspendscript illegal n.%d\n",n); - return(0); -}*/ + { + CTransaction tx; uint256 hashBlock; + if ( !GetTransaction(hash,tx,hashBlock,true) ) + { + printf("null GetTransaction\n"); + return(0); + } + if ( n >= 0 && n < tx.vout.size() ) + return((char *)tx.vout[n].scriptPubKey.ToString().c_str()); + else printf("getspendscript illegal n.%d\n",n); + return(0); + }*/ ////////////////////////////////////////////////////////////////////////////// @@ -1430,18 +1578,18 @@ bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::M CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); if (fileout.IsNull()) return error("WriteBlockToDisk: OpenBlockFile failed"); - + // Write index header unsigned int nSize = fileout.GetSerializeSize(block); fileout << FLATDATA(messageStart) << nSize; - + // Write block long fileOutPos = ftell(fileout.Get()); if (fileOutPos < 0) return error("WriteBlockToDisk: ftell failed"); pos.nPos = (unsigned int)fileOutPos; fileout << block; - + return true; } @@ -1449,7 +1597,7 @@ bool ReadBlockFromDisk(int32_t height,CBlock& block, const CDiskBlockPos& pos) { uint8_t pubkey33[33]; block.SetNull(); - + // Open history file to read CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); if (filein.IsNull()) @@ -1457,7 +1605,7 @@ bool ReadBlockFromDisk(int32_t height,CBlock& block, const CDiskBlockPos& pos) //fprintf(stderr,"readblockfromdisk err A\n"); return false;//error("ReadBlockFromDisk: OpenBlockFile failed for %s", pos.ToString()); } - + // Read block try { filein >> block; @@ -1468,10 +1616,10 @@ bool ReadBlockFromDisk(int32_t height,CBlock& block, const CDiskBlockPos& pos) } // Check the header komodo_block2pubkey33(pubkey33,(CBlock *)&block); - if (!(CheckEquihashSolution(&block, Params()) && CheckProofOfWork(height,pubkey33,block.GetHash(), block.nBits, Params().GetConsensus()))) + if (!(CheckEquihashSolution(&block, Params()) && CheckProofOfWork(height,pubkey33,block.GetHash(), block.nBits, Params().GetConsensus(),block.nTime))) { int32_t i; for (i=0; i<33; i++) - printf("%02x",pubkey33[i]); + fprintf(stderr,"%02x",pubkey33[i]); fprintf(stderr," warning unexpected diff at ht.%d\n",height); return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString()); @@ -1487,18 +1635,19 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex) return false; if (block.GetHash() != pindex->GetBlockHash()) return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s", - pindex->ToString(), pindex->GetBlockPos().ToString()); + pindex->ToString(), pindex->GetBlockPos().ToString()); return true; } //uint64_t komodo_moneysupply(int32_t height); extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; extern uint32_t ASSETCHAINS_MAGIC; -extern uint64_t ASSETCHAINS_SUPPLY; +extern uint64_t ASSETCHAINS_STAKED,ASSETCHAINS_ENDSUBSIDY,ASSETCHAINS_REWARD,ASSETCHAINS_HALVING,ASSETCHAINS_LINEAR,ASSETCHAINS_COMMISSION,ASSETCHAINS_SUPPLY; CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) { - CAmount nSubsidy = 3 * COIN; + static uint64_t cached_subsidy; static int32_t cached_numhalvings; + int32_t numhalvings,i; uint64_t numerator; CAmount nSubsidy = 3 * COIN; if ( ASSETCHAINS_SYMBOL[0] == 0 ) { if ( nHeight == 1 ) @@ -1511,29 +1660,67 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) { if ( nHeight == 1 ) return(ASSETCHAINS_SUPPLY * COIN + (ASSETCHAINS_MAGIC & 0xffffff)); - else return(10000); - } -/* - // Mining slow start - // The subsidy is ramped up linearly, skipping the middle payout of - // MAX_SUBSIDY/2 to keep the monetary curve consistent with no slow start. - if (nHeight < consensusParams.nSubsidySlowStartInterval / 2) { - nSubsidy /= consensusParams.nSubsidySlowStartInterval; - nSubsidy *= nHeight; - return nSubsidy; - } else if (nHeight < consensusParams.nSubsidySlowStartInterval) { - nSubsidy /= consensusParams.nSubsidySlowStartInterval; - nSubsidy *= (nHeight+1); - return nSubsidy; - } - - assert(nHeight > consensusParams.SubsidySlowStartShift()); - int halvings = (nHeight - consensusParams.SubsidySlowStartShift()) / consensusParams.nSubsidyHalvingInterval;*/ + else if ( ASSETCHAINS_ENDSUBSIDY == 0 || nHeight < ASSETCHAINS_ENDSUBSIDY ) + { + if ( ASSETCHAINS_REWARD == 0 ) + return(10000); + else if ( ASSETCHAINS_ENDSUBSIDY != 0 && nHeight >= ASSETCHAINS_ENDSUBSIDY ) + return(0); + else + { + nSubsidy = ASSETCHAINS_REWARD; + if ( ASSETCHAINS_HALVING != 0 ) + { + if ( (numhalvings= (nHeight / ASSETCHAINS_HALVING)) > 0 ) + { + if ( numhalvings >= 64 && ASSETCHAINS_DECAY == 0 ) + return(0); + if ( ASSETCHAINS_DECAY == 0 ) + nSubsidy >>= numhalvings; + else if ( ASSETCHAINS_DECAY == 100000000 && ASSETCHAINS_ENDSUBSIDY != 0 ) + { + numerator = (ASSETCHAINS_ENDSUBSIDY - nHeight); + nSubsidy = (nSubsidy * numerator) / ASSETCHAINS_ENDSUBSIDY; + } + else + { + if ( cached_subsidy > 0 && cached_numhalvings == numhalvings ) + nSubsidy = cached_subsidy; + else + { + for (i=0; i consensusParams.SubsidySlowStartShift()); + int halvings = (nHeight - consensusParams.SubsidySlowStartShift()) / consensusParams.nSubsidyHalvingInterval;*/ // Force block reward to zero when right shift is undefined. //int halvings = nHeight / consensusParams.nSubsidyHalvingInterval; //if (halvings >= 64) // return 0; - + // Subsidy is cut in half every 840,000 blocks which will occur approximately every 4 years. //nSubsidy >>= halvings; return nSubsidy; @@ -1564,10 +1751,10 @@ bool IsInitialBlockDownload() ptr = pindexBestHeader; else if ( pindexBestHeader != 0 && pindexBestHeader->nHeight > ptr->nHeight ) ptr = pindexBestHeader; - if ( ASSETCHAINS_SYMBOL[0] == 0 ) - state = ((chainActive.Height() < ptr->nHeight - 24*60) || - ptr->GetBlockTime() < (GetTime() - chainParams.MaxTipAge())); - else state = (chainActive.Height() < ptr->nHeight - 10); + //if ( ASSETCHAINS_SYMBOL[0] == 0 ) + state = ((chainActive.Height() < ptr->nHeight - 24*60) || + ptr->GetBlockTime() < (GetTime() - chainParams.MaxTipAge())); + //else state = (chainActive.Height() < ptr->nHeight - 24*60); //fprintf(stderr,"state.%d ht.%d vs %d, t.%u %u\n",state,(int32_t)chainActive.Height(),(uint32_t)ptr->nHeight,(int32_t)ptr->GetBlockTime(),(uint32_t)(GetTime() - chainParams.MaxTipAge())); if (!state) { @@ -1587,25 +1774,25 @@ void CheckForkWarningConditions() // (we assume we don't get stuck on a fork before the last checkpoint) if (IsInitialBlockDownload()) return; - + // If our best fork is no longer within 288 blocks (+/- 12 hours if no one mines it) // of our head, drop it if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->nHeight >= 288) pindexBestForkTip = NULL; - + if (pindexBestForkTip || (pindexBestInvalid && pindexBestInvalid->nChainWork > chainActive.Tip()->nChainWork + (GetBlockProof(*chainActive.Tip()) * 6))) { if (!fLargeWorkForkFound && pindexBestForkBase) { std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") + - pindexBestForkBase->phashBlock->ToString() + std::string("'"); + pindexBestForkBase->phashBlock->ToString() + std::string("'"); CAlert::Notify(warning, true); } if (pindexBestForkTip && pindexBestForkBase) { LogPrintf("%s: Warning: Large valid fork found\n forking the chain at height %d (%s)\n lasting to height %d (%s).\nChain state database corruption likely.\n", __func__, - pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString(), - pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString()); + pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString(), + pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString()); fLargeWorkForkFound = true; } else @@ -1637,7 +1824,7 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) break; pfork = pfork->pprev; } - + // We define a condition where we should warn the user about as a fork of at least 7 blocks // with a tip within 72 blocks (+/- 3 hours if no one mines it) of ours // We use 7 blocks rather arbitrarily as it represents just under 10% of sustained network @@ -1646,13 +1833,13 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) // We define it this way because it allows us to only store the highest fork tip (+ base) which meets // the 7-block condition and from this always have the most-likely-to-cause-warning fork if (pfork && (!pindexBestForkTip || (pindexBestForkTip && pindexNewForkTip->nHeight > pindexBestForkTip->nHeight)) && - pindexNewForkTip->nChainWork - pfork->nChainWork > (GetBlockProof(*pfork) * 7) && - chainActive.Height() - pindexNewForkTip->nHeight < 72) + pindexNewForkTip->nChainWork - pfork->nChainWork > (GetBlockProof(*pfork) * 7) && + chainActive.Height() - pindexNewForkTip->nHeight < 72) { pindexBestForkTip = pindexNewForkTip; pindexBestForkBase = pfork; } - + CheckForkWarningConditions(); } @@ -1661,13 +1848,13 @@ void Misbehaving(NodeId pnode, int howmuch) { if (howmuch == 0) return; - + CNodeState *state = State(pnode); if (state == NULL) return; - + state->nMisbehavior += howmuch; - int banscore = GetArg("-banscore", 100); + int banscore = GetArg("-banscore", 101); if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore) { LogPrintf("%s: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", __func__, state->name, state->nMisbehavior-howmuch, state->nMisbehavior); @@ -1680,16 +1867,16 @@ void static InvalidChainFound(CBlockIndex* pindexNew) { if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork) pindexBestInvalid = pindexNew; - + LogPrintf("%s: invalid block=%s height=%d log2_work=%.8g date=%s\n", __func__, - pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, - log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", - pindexNew->GetBlockTime())); + pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, + log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", + pindexNew->GetBlockTime())); CBlockIndex *tip = chainActive.Tip(); assert (tip); LogPrintf("%s: current best=%s height=%d log2_work=%.8g date=%s\n", __func__, - tip->GetBlockHash().ToString(), chainActive.Height(), log(tip->nChainWork.getdouble())/log(2.0), - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", tip->GetBlockTime())); + tip->GetBlockHash().ToString(), chainActive.Height(), log(tip->nChainWork.getdouble())/log(2.0), + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", tip->GetBlockTime())); CheckForkWarningConditions(); } @@ -1712,7 +1899,7 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state } } -void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight) +void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight) { if (!tx.IsCoinBase()) // mark inputs spent { @@ -1720,7 +1907,7 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach BOOST_FOREACH(const CTxIn &txin, tx.vin) { CCoinsModifier coins = inputs.ModifyCoins(txin.prevout.hash); unsigned nPos = txin.prevout.n; - + if (nPos >= coins->vout.size() || coins->vout[nPos].IsNull()) assert(false); // mark an outpoint spent, and construct undo information @@ -1742,15 +1929,15 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight); // add outputs } -void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, int nHeight) +void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight) { CTxUndo txundo; - UpdateCoins(tx, state, inputs, txundo, nHeight); + UpdateCoins(tx, inputs, txundo, nHeight); } bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; - if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, cacheStore), &error)) { + if (!VerifyScript(scriptSig, scriptPubKey, nFlags, ServerTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), consensusBranchId, &error)) { return ::error("CScriptCheck(): %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error)); } return true; @@ -1764,17 +1951,17 @@ int GetSpendHeight(const CCoinsViewCache& inputs) } namespace Consensus { -bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams) -{ + bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams) + { // 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())); - + // are the JoinSplit's requirements met? if (!inputs.HaveJoinSplitRequirements(tx)) return state.Invalid(error("CheckInputs(): %s JoinSplit requirements not met", tx.GetHash().ToString())); - + CAmount nValueIn = 0; CAmount nFees = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) @@ -1782,30 +1969,30 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins const COutPoint &prevout = tx.vin[i].prevout; const CCoins *coins = inputs.AccessCoins(prevout.hash); assert(coins); - + 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"); + error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight), + REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); } - + // Ensure that coinbases cannot be spent to transparent outputs // Disabled on regtest if (fCoinbaseEnforcedProtectionEnabled && consensusParams.fCoinbaseMustBeProtected && !tx.vout.empty()) { return state.Invalid( - error("CheckInputs(): tried to spend coinbase with transparent outputs"), - REJECT_INVALID, "bad-txns-coinbase-spend-has-transparent-outputs"); + error("CheckInputs(): tried to spend coinbase with transparent outputs"), + REJECT_INVALID, "bad-txns-coinbase-spend-has-transparent-outputs"); } } - + // Check for negative or overflow input values nValueIn += coins->vout[prevout.n].nValue; #ifdef KOMODO_ENABLE_INTEREST - if ( ASSETCHAINS_SYMBOL[0] == 0 && chainActive.Tip() != 0 && chainActive.Tip()->nHeight >= 60000 ) + if ( ASSETCHAINS_SYMBOL[0] == 0 && nSpendHeight > 60000 )//chainActive.Tip() != 0 && chainActive.Tip()->nHeight >= 60000 ) { if ( coins->vout[prevout.n].nValue >= 10*COIN ) { @@ -1821,18 +2008,20 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins 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"); - + } - + nValueIn += tx.GetJoinSplitValueIn(); if (!MoneyRange(nValueIn)) return state.DoS(100, error("CheckInputs(): vpub_old values out of range"), REJECT_INVALID, "bad-txns-inputvalues-outofrange"); - + if (nValueIn < tx.GetValueOut()) + { + fprintf(stderr,"spentheight.%d valuein %s vs %s error\n",nSpendHeight,FormatMoney(nValueIn).c_str(), FormatMoney(tx.GetValueOut()).c_str()); return state.DoS(100, error("CheckInputs(): %s value in (%s) < value out (%s) diff %.8f", tx.GetHash().ToString(), FormatMoney(nValueIn), FormatMoney(tx.GetValueOut()),((double)nValueIn - tx.GetValueOut())/COIN),REJECT_INVALID, "bad-txns-in-belowout"); - + } // Tally transaction fees CAmount nTxFee = nValueIn - tx.GetValueOut(); if (nTxFee < 0) @@ -1842,24 +2031,35 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins if (!MoneyRange(nFees)) return state.DoS(100, error("CheckInputs(): nFees out of range"), REJECT_INVALID, "bad-txns-fee-outofrange"); - return true; -} + return true; + } }// namespace Consensus -bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams, std::vector *pvChecks) +bool ContextualCheckInputs( + const CTransaction& tx, + CValidationState &state, + const CCoinsViewCache &inputs, + bool fScriptChecks, + unsigned int flags, + bool cacheStore, + PrecomputedTransactionData& txdata, + const Consensus::Params& consensusParams, + uint32_t consensusBranchId, + std::vector *pvChecks) { - if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs), consensusParams)) - return false; - if (!tx.IsCoinBase()) { + if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs), consensusParams)) { + return false; + } + if (pvChecks) pvChecks->reserve(tx.vin.size()); - + // The first loop above does all the inexpensive checks. // Only if ALL inputs pass do we perform expensive ECDSA signature checks. // Helps prevent CPU exhaustion attacks. - + // Skip ECDSA signature verification when connecting blocks // before the last block chain checkpoint. This is safe because block merkle hashes are // still computed and checked, and any change will be caught at the next checkpoint. @@ -1868,9 +2068,9 @@ bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, cons const COutPoint &prevout = tx.vin[i].prevout; const CCoins* coins = inputs.AccessCoins(prevout.hash); assert(coins); - + // Verify signature - CScriptCheck check(*coins, tx, i, flags, cacheStore); + CScriptCheck check(*coins, tx, i, flags, cacheStore, consensusBranchId, &txdata); if (pvChecks) { pvChecks->push_back(CScriptCheck()); check.swap(pvChecks->back()); @@ -1882,9 +2082,9 @@ bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, cons // arguments; if so, don't trigger DoS protection to // avoid splitting the network between upgraded and // non-upgraded nodes. - CScriptCheck check(*coins, tx, i, - flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore); - if (check()) + CScriptCheck check2(*coins, tx, i, + flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore, consensusBranchId, &txdata); + if (check2()) return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); } // Failures of other flags indicate a transaction that is @@ -1899,123 +2099,123 @@ bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, cons } } } - + return true; } /*bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams, std::vector *pvChecks) -{ - if (!NonContextualCheckInputs(tx, state, inputs, fScriptChecks, flags, cacheStore, consensusParams, pvChecks)) { - fprintf(stderr,"ContextualCheckInputs failure.0\n"); - return false; - } + { + if (!NonContextualCheckInputs(tx, state, inputs, fScriptChecks, flags, cacheStore, consensusParams, pvChecks)) { + fprintf(stderr,"ContextualCheckInputs failure.0\n"); + return false; + } + + if (!tx.IsCoinBase()) + { + // While checking, GetBestBlock() refers to the parent block. + // This is also true for mempool checks. + CBlockIndex *pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second; + int nSpendHeight = pindexPrev->nHeight + 1; + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + const COutPoint &prevout = tx.vin[i].prevout; + const CCoins *coins = inputs.AccessCoins(prevout.hash); + // Assertion is okay because NonContextualCheckInputs ensures the inputs + // are available. + assert(coins); + + // If prev is coinbase, check that it's matured + if (coins->IsCoinBase()) { + if ( ASSETCHAINS_SYMBOL[0] == 0 ) + COINBASE_MATURITY = _COINBASE_MATURITY; + if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) { + fprintf(stderr,"ContextualCheckInputs failure.1 i.%d of %d\n",i,(int32_t)tx.vin.size()); + + return state.Invalid( + error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight),REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); + } + } + } + } + + return true; + }*/ - if (!tx.IsCoinBase()) +namespace { + + bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart) { - // While checking, GetBestBlock() refers to the parent block. - // This is also true for mempool checks. - CBlockIndex *pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second; - int nSpendHeight = pindexPrev->nHeight + 1; - for (unsigned int i = 0; i < tx.vin.size(); i++) - { - const COutPoint &prevout = tx.vin[i].prevout; - const CCoins *coins = inputs.AccessCoins(prevout.hash); - // Assertion is okay because NonContextualCheckInputs ensures the inputs - // are available. - assert(coins); - - // If prev is coinbase, check that it's matured - if (coins->IsCoinBase()) { - if ( ASSETCHAINS_SYMBOL[0] == 0 ) - COINBASE_MATURITY = _COINBASE_MATURITY; - if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) { - fprintf(stderr,"ContextualCheckInputs failure.1 i.%d of %d\n",i,(int32_t)tx.vin.size()); - - return state.Invalid( - error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight),REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); - } - } + // Open history file to append + CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); + if (fileout.IsNull()) + return error("%s: OpenUndoFile failed", __func__); + + // Write index header + unsigned int nSize = fileout.GetSerializeSize(blockundo); + fileout << FLATDATA(messageStart) << nSize; + + // Write undo data + long fileOutPos = ftell(fileout.Get()); + if (fileOutPos < 0) + return error("%s: ftell failed", __func__); + pos.nPos = (unsigned int)fileOutPos; + fileout << blockundo; + + // calculate & write checksum + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + hasher << hashBlock; + hasher << blockundo; + fileout << hasher.GetHash(); + + return true; + } + + bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uint256& hashBlock) + { + // Open history file to read + CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); + if (filein.IsNull()) + return error("%s: OpenBlockFile failed", __func__); + + // Read block + uint256 hashChecksum; + try { + filein >> blockundo; + filein >> hashChecksum; + } + catch (const std::exception& e) { + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); } + + // Verify checksum + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + hasher << hashBlock; + hasher << blockundo; + if (hashChecksum != hasher.GetHash()) + return error("%s: Checksum mismatch", __func__); + + return true; } - - return true; -}*/ - -namespace { - -bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart) -{ - // Open history file to append - CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); - if (fileout.IsNull()) - return error("%s: OpenUndoFile failed", __func__); - - // Write index header - unsigned int nSize = fileout.GetSerializeSize(blockundo); - fileout << FLATDATA(messageStart) << nSize; - - // Write undo data - long fileOutPos = ftell(fileout.Get()); - if (fileOutPos < 0) - return error("%s: ftell failed", __func__); - pos.nPos = (unsigned int)fileOutPos; - fileout << blockundo; - - // calculate & write checksum - CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); - hasher << hashBlock; - hasher << blockundo; - fileout << hasher.GetHash(); - - return true; -} - -bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uint256& hashBlock) -{ - // Open history file to read - CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); - if (filein.IsNull()) - return error("%s: OpenBlockFile failed", __func__); - - // Read block - uint256 hashChecksum; - try { - filein >> blockundo; - filein >> hashChecksum; + + /** Abort with a message */ + bool AbortNode(const std::string& strMessage, const std::string& userMessage="") + { + strMiscWarning = strMessage; + LogPrintf("*** %s\n", strMessage); + uiInterface.ThreadSafeMessageBox( + userMessage.empty() ? _("Error: A fatal internal error occurred, see debug.log for details") : userMessage, + "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return false; } - catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + + bool AbortNode(CValidationState& state, const std::string& strMessage, const std::string& userMessage="") + { + AbortNode(strMessage, userMessage); + return state.Error(strMessage); } - - // Verify checksum - CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); - hasher << hashBlock; - hasher << blockundo; - if (hashChecksum != hasher.GetHash()) - return error("%s: Checksum mismatch", __func__); - - return true; -} - -/** Abort with a message */ -bool AbortNode(const std::string& strMessage, const std::string& userMessage="") -{ - strMiscWarning = strMessage; - LogPrintf("*** %s\n", strMessage); - uiInterface.ThreadSafeMessageBox( - userMessage.empty() ? _("Error: A fatal internal error occurred, see debug.log for details") : userMessage, - "", CClientUIInterface::MSG_ERROR); - StartShutdown(); - return false; -} - -bool AbortNode(CValidationState& state, const std::string& strMessage, const std::string& userMessage="") -{ - AbortNode(strMessage, userMessage); - return state.Error(strMessage); -} - + } // anon namespace /** @@ -2028,7 +2228,7 @@ bool AbortNode(CValidationState& state, const std::string& strMessage, const std static bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const COutPoint& out) { bool fClean = true; - + CCoinsModifier coins = view.ModifyCoins(out.hash); if (undo.nHeight != 0) { // undo data contains height: this is the last output of the prevout tx being spent @@ -2047,17 +2247,17 @@ static bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const CO if (coins->vout.size() < out.n+1) coins->vout.resize(out.n+1); coins->vout[out.n] = undo.txout; - + return fClean; } bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) { assert(pindex->GetBlockHash() == view.GetBestBlock()); - + if (pfClean) *pfClean = false; - + bool fClean = true; komodo_disconnect(pindex,block); CBlockUndo blockUndo; @@ -2066,41 +2266,41 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex return error("DisconnectBlock(): no undo data available"); if (!UndoReadFromDisk(blockUndo, pos, pindex->pprev->GetBlockHash())) return error("DisconnectBlock(): failure reading undo data"); - + if (blockUndo.vtxundo.size() + 1 != block.vtx.size()) return error("DisconnectBlock(): block and undo data inconsistent"); - + // undo transactions in reverse order for (int i = block.vtx.size() - 1; i >= 0; i--) { const CTransaction &tx = block.vtx[i]; uint256 hash = tx.GetHash(); - + // Check that all outputs are available and match the outputs in the block itself // exactly. { - CCoinsModifier outs = view.ModifyCoins(hash); - outs->ClearUnspendable(); - - CCoins outsBlock(tx, pindex->nHeight); - // The CCoins serialization does not serialize negative numbers. - // No network rules currently depend on the version here, so an inconsistency is harmless - // but it must be corrected before txout nversion ever influences a network rule. - if (outsBlock.nVersion < 0) - outs->nVersion = outsBlock.nVersion; - if (*outs != outsBlock) - fClean = fClean && error("DisconnectBlock(): added transaction mismatch? database corrupted"); - - // remove outputs - outs->Clear(); + CCoinsModifier outs = view.ModifyCoins(hash); + outs->ClearUnspendable(); + + CCoins outsBlock(tx, pindex->nHeight); + // The CCoins serialization does not serialize negative numbers. + // No network rules currently depend on the version here, so an inconsistency is harmless + // but it must be corrected before txout nversion ever influences a network rule. + if (outsBlock.nVersion < 0) + outs->nVersion = outsBlock.nVersion; + if (*outs != outsBlock) + fClean = fClean && error("DisconnectBlock(): added transaction mismatch? database corrupted"); + + // remove outputs + outs->Clear(); } - + // unspend nullifiers BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) { view.SetNullifier(nf, false); } } - + // restore inputs if (i > 0) { // not coinbases const CTxUndo &txundo = blockUndo.vtxundo[i-1]; @@ -2114,27 +2314,27 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex } } } - + // set the old best anchor back view.PopAnchor(blockUndo.old_tree_root); - + // move best block pointer to prevout block view.SetBestBlock(pindex->pprev->GetBlockHash()); - + if (pfClean) { *pfClean = fClean; return true; } - + return fClean; } void static FlushBlockFile(bool fFinalize = false) { LOCK(cs_LastBlockFile); - + CDiskBlockPos posOld(nLastBlockFile, 0); - + FILE *fileOld = OpenBlockFile(posOld); if (fileOld) { if (fFinalize) @@ -2142,7 +2342,7 @@ void static FlushBlockFile(bool fFinalize = false) FileCommit(fileOld); fclose(fileOld); } - + fileOld = OpenUndoFile(posOld); if (fileOld) { if (fFinalize) @@ -2170,39 +2370,39 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const int64_t nPowTargetSpacing) { if (bestHeader == NULL || initialDownloadCheck()) return; - + static int64_t lastAlertTime = 0; int64_t now = GetAdjustedTime(); if (lastAlertTime > now-60*60*24) return; // Alert at most once per day - + const int SPAN_HOURS=4; const int SPAN_SECONDS=SPAN_HOURS*60*60; int BLOCKS_EXPECTED = SPAN_SECONDS / nPowTargetSpacing; - + boost::math::poisson_distribution poisson(BLOCKS_EXPECTED); - + std::string strWarning; int64_t startTime = GetAdjustedTime()-SPAN_SECONDS; - + LOCK(cs); const CBlockIndex* i = bestHeader; int nBlocks = 0; while (i->GetBlockTime() >= startTime) { ++nBlocks; i = i->pprev; - if (i == NULL) return; // Ran out of chain, we must not be fully sync'ed + if (i == NULL) return; // Ran out of chain, we must not be fully synced } - + // How likely is it to find that many by chance? double p = boost::math::pdf(poisson, nBlocks); - + LogPrint("partitioncheck", "%s : Found %d blocks in the last %d hours\n", __func__, nBlocks, SPAN_HOURS); LogPrint("partitioncheck", "%s : likelihood: %g\n", __func__, p); - + // Aim for one false-positive about every fifty years of normal running: const int FIFTY_YEARS = 50*365*24*60*60; double alertThreshold = 1.0 / (FIFTY_YEARS / SPAN_SECONDS); - + if (p <= alertThreshold && nBlocks < BLOCKS_EXPECTED) { // Many fewer blocks than expected: alert! @@ -2229,16 +2429,12 @@ static int64_t nTimeIndex = 0; static int64_t nTimeCallbacks = 0; static int64_t nTimeTotal = 0; -bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck) +bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck,bool fCheckPOW) { const CChainParams& chainparams = Params(); + //fprintf(stderr,"connectblock ht.%d\n",(int32_t)pindex->nHeight); AssertLockHeld(cs_main); -/*<<<<<<< HEA - // Check it again in case a previous version let a bad block in - bool fExpensiveChecks = (!fCheckpointsEnabled || pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints())); -======= -*/ bool fExpensiveChecks = true; if (fCheckpointsEnabled) { CBlockIndex *pindexLastCheckpoint = Checkpoints::GetLastCheckpoint(chainparams.Checkpoints()); @@ -2247,18 +2443,17 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin fExpensiveChecks = false; } } -//>>>>>>> zcash/master auto verifier = libzcash::ProofVerifier::Strict(); auto disabledVerifier = libzcash::ProofVerifier::Disabled(); - + // Check it again to verify JoinSplit proofs, and in case a previous version let a bad block in - if (!CheckBlock(pindex->nHeight,pindex,block, state, fExpensiveChecks ? verifier : disabledVerifier, !fJustCheck, !fJustCheck)) + if (!CheckBlock(pindex->nHeight,pindex,block, state, fExpensiveChecks ? verifier : disabledVerifier, fCheckPOW, !fJustCheck)) return false; - + // verify that the view's current state corresponds to the previous block uint256 hashPrevBlock = pindex->pprev == NULL ? uint256() : pindex->pprev->GetBlockHash(); assert(hashPrevBlock == view.GetBestBlock()); - + // Special case for the genesis block, skipping connection of its transactions // (its coinbase is unspendable) if (block.GetHash() == chainparams.GetConsensus().hashGenesisBlock) { @@ -2272,7 +2467,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } return true; } - + bool fScriptChecks = (!fCheckpointsEnabled || pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints())); //if ( KOMODO_TESTNET_EXPIRATION != 0 && pindex->nHeight > KOMODO_TESTNET_EXPIRATION ) // "testnet" // return(false); @@ -2284,25 +2479,15 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"), REJECT_INVALID, "bad-txns-BIP30"); } - - unsigned int flags = SCRIPT_VERIFY_P2SH; - - // Start enforcing the DERSIG (BIP66) rules, for block.nVersion=3 blocks, - // when 75% of the network has upgraded: - if (block.nVersion >= 3) { - flags |= SCRIPT_VERIFY_DERSIG; - } - - // Start enforcing CHECKLOCKTIMEVERIFY, (BIP65) for block.nVersion=4 - // blocks, when 75% of the network has upgraded: - if (block.nVersion >= 4) { - flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; - } - + + unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; + + // DERSIG (BIP66) is also always enforced, but does not have a flag. + CBlockUndo blockundo; - + CCheckQueueControl control(fExpensiveChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); - + int64_t nTimeStart = GetTimeMicros(); CAmount nFees = 0; int nInputs = 0; @@ -2312,7 +2497,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin std::vector > vPos; vPos.reserve(block.vtx.size()); blockundo.vtxundo.reserve(block.vtx.size() - 1); - + // Construct the incremental merkle tree at the current // block position, auto old_tree_root = view.GetBestAnchor(); @@ -2324,13 +2509,18 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // This should never fail: we should always be able to get the root // that is on the tip of our chain assert(view.GetAnchorAt(old_tree_root, tree)); - + { // Consistency check: the root of the tree we're given should // match what we asked for. assert(tree.root() == old_tree_root); } - + + // Grab the consensus branch ID for the block's height + auto consensusBranchId = CurrentEpochBranchId(pindex->nHeight, Params().GetConsensus()); + + std::vector txdata; + txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated for (unsigned int i = 0; i < block.vtx.size(); i++) { const CTransaction &tx = block.vtx[i]; @@ -2339,18 +2529,18 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (nSigOps > MAX_BLOCK_SIGOPS) return state.DoS(100, error("ConnectBlock(): too many sigops"), REJECT_INVALID, "bad-blk-sigops"); -//fprintf(stderr,"ht.%d vout0 t%u\n",pindex->nHeight,tx.nLockTime); + //fprintf(stderr,"ht.%d vout0 t%u\n",pindex->nHeight,tx.nLockTime); if (!tx.IsCoinBase()) { if (!view.HaveInputs(tx)) return state.DoS(100, error("ConnectBlock(): inputs missing/spent"), REJECT_INVALID, "bad-txns-inputs-missingorspent"); - + // are the JoinSplit's requirements met? if (!view.HaveJoinSplitRequirements(tx)) return state.DoS(100, error("ConnectBlock(): JoinSplit requirements not met"), REJECT_INVALID, "bad-txns-joinsplit-requirements-not-met"); - + // Add in sigops done by pay-to-script-hash inputs; // this is to prevent a "rogue miner" from creating // an incredibly-expensive-to-validate block. @@ -2358,11 +2548,17 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (nSigOps > MAX_BLOCK_SIGOPS) return state.DoS(100, error("ConnectBlock(): too many sigops"), REJECT_INVALID, "bad-blk-sigops"); - + } + + txdata.emplace_back(tx); + + if (!tx.IsCoinBase()) + { nFees += view.GetValueIn(chainActive.Tip()->nHeight,&interest,tx,chainActive.Tip()->nTime) - tx.GetValueOut(); sum += interest; + std::vector vChecks; - if (!ContextualCheckInputs(tx, state, view, fExpensiveChecks, flags, false, chainparams.GetConsensus(), nScriptCheckThreads ? &vChecks : NULL)) + if (!ContextualCheckInputs(tx, state, view, fExpensiveChecks, flags, false, txdata[i], chainparams.GetConsensus(), consensusBranchId, nScriptCheckThreads ? &vChecks : NULL)) return false; control.Add(vChecks); } @@ -2372,45 +2568,59 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (i > 0) { blockundo.vtxundo.push_back(CTxUndo()); } - UpdateCoins(tx, state, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight); - + UpdateCoins(tx, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight); + BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { BOOST_FOREACH(const uint256 ¬e_commitment, joinsplit.commitments) { // Insert the note commitments into our temporary tree. - + tree.append(note_commitment); } } - + vPos.push_back(std::make_pair(tx.GetHash(), pos)); pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } - + view.PushAnchor(tree); if (!fJustCheck) { pindex->hashAnchorEnd = tree.root(); } blockundo.old_tree_root = old_tree_root; - + 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); - - CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus()); - if (block.vtx[0].vout[0].nValue > blockReward) - //if (block.vtx[0].GetValueOut() > blockReward) - return state.DoS(100, - error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)", - block.vtx[0].GetValueOut(), blockReward), - REJECT_INVALID, "bad-cb-amount"); - + + CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus()) + sum; + if ( ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 && ASSETCHAINS_COMMISSION != 0 && block.vtx[0].vout.size() > 1 ) + { + uint64_t checktoshis; + if ( (checktoshis = komodo_commission(block)) != 0 ) + { + if ( block.vtx[0].vout.size() == 2 && block.vtx[0].vout[1].nValue == checktoshis ) + blockReward += checktoshis; + else fprintf(stderr,"checktoshis %.8f vs actual vout[1] %.8f\n",dstr(checktoshis),dstr(block.vtx[0].vout[1].nValue)); + } + } + if ( block.vtx[0].GetValueOut() > blockReward+1 ) + { + if ( ASSETCHAINS_SYMBOL[0] != 0 || pindex->nHeight >= KOMODO_NOTARIES_HEIGHT1 || block.vtx[0].vout[0].nValue > blockReward ) + { + return state.DoS(100, + error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)", + block.vtx[0].GetValueOut(), blockReward), + REJECT_INVALID, "bad-cb-amount"); + } else if ( NOTARY_PUBKEY33[0] != 0 ) + fprintf(stderr,"allow nHeight.%d coinbase %.8f vs %.8f interest %.8f\n",(int32_t)pindex->nHeight,dstr(block.vtx[0].GetValueOut()),dstr(blockReward),dstr(sum)); + } 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); - + if (fJustCheck) return true; - + // Write undo information to disk if (pindex->GetUndoPos().IsNull() || !pindex->IsValid(BLOCK_VALID_SCRIPTS)) { @@ -2420,31 +2630,42 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return error("ConnectBlock(): FindUndoPos failed"); 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->nStatus |= BLOCK_HAVE_UNDO; } - + + // Now that all consensus rules have been validated, set nCachedBranchId. + // Move this if BLOCK_VALID_CONSENSUS is ever altered. + static_assert(BLOCK_VALID_CONSENSUS == BLOCK_VALID_SCRIPTS, + "nCachedBranchId must be set after all consensus rules have been validated."); + if (IsActivationHeightForAnyUpgrade(pindex->nHeight, Params().GetConsensus())) { + pindex->nStatus |= BLOCK_ACTIVATES_UPGRADE; + pindex->nCachedBranchId = CurrentEpochBranchId(pindex->nHeight, chainparams.GetConsensus()); + } else if (pindex->pprev) { + pindex->nCachedBranchId = pindex->pprev->nCachedBranchId; + } + pindex->RaiseValidity(BLOCK_VALID_SCRIPTS); setDirtyBlockIndex.insert(pindex); } - + if (fTxIndex) if (!pblocktree->WriteTxIndex(vPos)) return AbortNode(state, "Failed to write transaction index"); - + // add this block to the view's block chain 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); - + // Watch for changes to the previous coinbase transaction. static uint256 hashPrevBestCoinBase; GetMainSignals().UpdatedTransaction(hashPrevBestCoinBase); 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); @@ -2474,88 +2695,88 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { std::set setFilesToPrune; bool fFlushForPrune = false; try { - if (fPruneMode && fCheckForPruning && !fReindex) { - FindFilesToPrune(setFilesToPrune); - fCheckForPruning = false; - if (!setFilesToPrune.empty()) { - fFlushForPrune = true; - if (!fHavePruned) { - pblocktree->WriteFlag("prunedblockfiles", true); - fHavePruned = true; + if (fPruneMode && fCheckForPruning && !fReindex) { + FindFilesToPrune(setFilesToPrune); + fCheckForPruning = false; + if (!setFilesToPrune.empty()) { + fFlushForPrune = true; + if (!fHavePruned) { + pblocktree->WriteFlag("prunedblockfiles", true); + fHavePruned = true; + } } } - } - int64_t nNow = GetTimeMicros(); - // Avoid writing/flushing immediately after startup. - if (nLastWrite == 0) { - nLastWrite = nNow; - } - if (nLastFlush == 0) { - nLastFlush = nNow; - } - if (nLastSetChain == 0) { - nLastSetChain = nNow; - } - size_t cacheSize = pcoinsTip->DynamicMemoryUsage(); - // The cache is large and close to the limit, but we have time now (not in the middle of a block processing). - bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize * (10.0/9) > nCoinCacheUsage; - // The cache is over the limit, we have to write now. - bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nCoinCacheUsage; - // It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload after a crash. - bool fPeriodicWrite = mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000; - // It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage. - bool fPeriodicFlush = mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000; - // Combine all conditions that result in a full cache flush. - bool fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune; - // Write blocks and block index to disk. - if (fDoFullFlush || fPeriodicWrite) { - // Depend on nMinDiskSpace to ensure we can write block index - if (!CheckDiskSpace(0)) - return state.Error("out of disk space"); - // First make sure all block and undo data is flushed to disk. - FlushBlockFile(); - // Then update all block file information (which may refer to block and undo files). - { - std::vector > vFiles; - vFiles.reserve(setDirtyFileInfo.size()); - for (set::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) { - vFiles.push_back(make_pair(*it, &vinfoBlockFile[*it])); - setDirtyFileInfo.erase(it++); - } - std::vector vBlocks; - vBlocks.reserve(setDirtyBlockIndex.size()); - for (set::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) { - vBlocks.push_back(*it); - setDirtyBlockIndex.erase(it++); - } - if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) { - return AbortNode(state, "Files to write to block index database"); + int64_t nNow = GetTimeMicros(); + // Avoid writing/flushing immediately after startup. + if (nLastWrite == 0) { + nLastWrite = nNow; + } + if (nLastFlush == 0) { + nLastFlush = nNow; + } + if (nLastSetChain == 0) { + nLastSetChain = nNow; + } + size_t cacheSize = pcoinsTip->DynamicMemoryUsage(); + // The cache is large and close to the limit, but we have time now (not in the middle of a block processing). + bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize * (10.0/9) > nCoinCacheUsage; + // The cache is over the limit, we have to write now. + bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nCoinCacheUsage; + // It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload after a crash. + bool fPeriodicWrite = mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000; + // It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage. + bool fPeriodicFlush = mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000; + // Combine all conditions that result in a full cache flush. + bool fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune; + // Write blocks and block index to disk. + if (fDoFullFlush || fPeriodicWrite) { + // Depend on nMinDiskSpace to ensure we can write block index + if (!CheckDiskSpace(0)) + return state.Error("out of disk space"); + // First make sure all block and undo data is flushed to disk. + FlushBlockFile(); + // Then update all block file information (which may refer to block and undo files). + { + std::vector > vFiles; + vFiles.reserve(setDirtyFileInfo.size()); + for (set::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) { + vFiles.push_back(make_pair(*it, &vinfoBlockFile[*it])); + setDirtyFileInfo.erase(it++); + } + std::vector vBlocks; + vBlocks.reserve(setDirtyBlockIndex.size()); + for (set::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) { + vBlocks.push_back(*it); + setDirtyBlockIndex.erase(it++); + } + if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) { + return AbortNode(state, "Files to write to block index database"); + } } + // Finally remove any pruned files + if (fFlushForPrune) + UnlinkPrunedFiles(setFilesToPrune); + nLastWrite = nNow; + } + // Flush best chain related state. This can only be done if the blocks / block index write was also done. + if (fDoFullFlush) { + // Typical CCoins structures on disk are around 128 bytes in size. + // Pushing a new one to the database can cause it to be written + // twice (once in the log, and once in the tables). This is already + // an overestimation, as most will delete an existing entry or + // overwrite one. Still, use a conservative safety factor of 2. + if (!CheckDiskSpace(128 * 2 * 2 * pcoinsTip->GetCacheSize())) + return state.Error("out of disk space"); + // Flush the chainstate (which may refer to block index entries). + if (!pcoinsTip->Flush()) + return AbortNode(state, "Failed to write to coin database"); + nLastFlush = nNow; + } + if ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000) { + // Update best block in wallet (so we can detect restored wallets). + GetMainSignals().SetBestChain(chainActive.GetLocator()); + nLastSetChain = nNow; } - // Finally remove any pruned files - if (fFlushForPrune) - UnlinkPrunedFiles(setFilesToPrune); - nLastWrite = nNow; - } - // Flush best chain related state. This can only be done if the blocks / block index write was also done. - if (fDoFullFlush) { - // Typical CCoins structures on disk are around 128 bytes in size. - // Pushing a new one to the database can cause it to be written - // twice (once in the log, and once in the tables). This is already - // an overestimation, as most will delete an existing entry or - // overwrite one. Still, use a conservative safety factor of 2. - if (!CheckDiskSpace(128 * 2 * 2 * pcoinsTip->GetCacheSize())) - return state.Error("out of disk space"); - // Flush the chainstate (which may refer to block index entries). - if (!pcoinsTip->Flush()) - return AbortNode(state, "Failed to write to coin database"); - nLastFlush = nNow; - } - if ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000) { - // Update best block in wallet (so we can detect restored wallets). - GetMainSignals().SetBestChain(chainActive.GetLocator()); - nLastSetChain = nNow; - } } catch (const std::runtime_error& e) { return AbortNode(state, std::string("System error while flushing: ") + e.what()); } @@ -2577,18 +2798,18 @@ void PruneAndFlush() { void static UpdateTip(CBlockIndex *pindexNew) { const CChainParams& chainParams = Params(); chainActive.SetTip(pindexNew); - + // New best block nTimeBestReceived = GetTime(); mempool.AddTransactionsUpdated(1); - + KOMODO_NEWBLOCKS++; LogPrintf("%s: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%.1fMiB(%utx)\n", __func__, - chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), - Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); - + chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), + Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); + cvBlockChange.notify_all(); - + // Check the version of the last 100 blocks to see if we need to upgrade: static bool fWarned = false; if (!IsInitialBlockDownload() && !fWarned) @@ -2613,11 +2834,13 @@ void static UpdateTip(CBlockIndex *pindexNew) { } } -/** Disconnect chainActive's tip. */ -bool static DisconnectTip(CValidationState &state) { +/** + * Disconnect chainActive's tip. You probably want to call mempool.removeForReorg and + * mempool.removeWithoutBranchId after this, with cs_main held. + */ +bool static DisconnectTip(CValidationState &state, bool fBare = false) { CBlockIndex *pindexDelete = chainActive.Tip(); assert(pindexDelete); - mempool.check(pcoinsTip); // Read block from disk. CBlock block; if (!ReadBlockFromDisk(block, pindexDelete)) @@ -2636,21 +2859,23 @@ bool static DisconnectTip(CValidationState &state) { // Write the chain state to disk, if necessary. if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED)) return false; - // Resurrect mempool transactions from the disconnected block. - BOOST_FOREACH(const CTransaction &tx, block.vtx) { - // ignore validation errors in resurrected transactions - list removed; - CValidationState stateDummy; - if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) - mempool.remove(tx, removed, true); - } - if (anchorBeforeDisconnect != anchorAfterDisconnect) { - // The anchor may not change between block disconnects, - // in which case we don't want to evict from the mempool yet! - mempool.removeWithAnchor(anchorBeforeDisconnect); - } - mempool.removeCoinbaseSpends(pcoinsTip, pindexDelete->nHeight); - mempool.check(pcoinsTip); + + if (!fBare) { + // Resurrect mempool transactions from the disconnected block. + BOOST_FOREACH(const CTransaction &tx, block.vtx) { + // ignore validation errors in resurrected transactions + list removed; + CValidationState stateDummy; + if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) + mempool.remove(tx, removed, true); + } + if (anchorBeforeDisconnect != anchorAfterDisconnect) { + // The anchor may not change between block disconnects, + // in which case we don't want to evict from the mempool yet! + mempool.removeWithAnchor(anchorBeforeDisconnect); + } + } + // Update chainActive and related variables. UpdateTip(pindexDelete->pprev); // Get the current commitment tree @@ -2676,11 +2901,11 @@ static int64_t nTimePostConnect = 0; /** * Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock * corresponding to pindexNew, to bypass loading it again from disk. + * You probably want to call mempool.removeWithoutBranchId after this, with cs_main held. */ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *pblock) { assert(pindexNew->pprev == chainActive.Tip()); - mempool.check(pcoinsTip); // Read block from disk. int64_t nTime1 = GetTimeMicros(); CBlock block; @@ -2698,7 +2923,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * LogPrint("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); + bool rv = ConnectBlock(*pblock, state, pindexNew, view, false, true); GetMainSignals().BlockChecked(*pblock, state); if (!rv) { if (state.IsInvalid()) @@ -2720,7 +2945,10 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * // Remove conflicting transactions from the mempool. list txConflicted; mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted, !IsInitialBlockDownload()); - mempool.check(pcoinsTip); + + // Remove transactions that expire at new block height from mempool + mempool.removeExpired(pindexNew->nHeight); + // Update chainActive & related variables. UpdateTip(pindexNew); // Tell wallet about transactions that went from mempool @@ -2735,7 +2963,9 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * // Update cached incremental witnesses //fprintf(stderr,"chaintip true\n"); GetMainSignals().ChainTip(pindexNew, pblock, oldTree, true); - + + 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); @@ -2749,7 +2979,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * static CBlockIndex* FindMostWorkChain() { do { CBlockIndex *pindexNew = NULL; - + // Find the best candidate header. { std::set::reverse_iterator it = setBlockIndexCandidates.rbegin(); @@ -2757,14 +2987,14 @@ static CBlockIndex* FindMostWorkChain() { return NULL; pindexNew = *it; } - + // Check whether all blocks on the path between the currently active chain and the candidate are valid. // Just going until the active chain is an optimization, as we know all blocks in it are valid already. CBlockIndex *pindexTest = pindexNew; bool fInvalidAncestor = false; while (pindexTest && !chainActive.Contains(pindexTest)) { assert(pindexTest->nChainTx || pindexTest->nHeight == 0); - + // Pruned nodes may have entries in setBlockIndexCandidates for // which block files have been deleted. Remove those as candidates // for the most work chain if we come across them; we can't switch @@ -2821,77 +3051,113 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo bool fInvalidFound = false; const CBlockIndex *pindexOldTip = chainActive.Tip(); const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork); - + + // - On ChainDB initialization, pindexOldTip will be null, so there are no removable blocks. + // - If pindexMostWork is in a chain that doesn't have the same genesis block as our chain, + // then pindexFork will be null, and we would need to remove the entire chain including + // our genesis block. In practice this (probably) won't happen because of checks elsewhere. + auto reorgLength = pindexOldTip ? pindexOldTip->nHeight - (pindexFork ? pindexFork->nHeight : -1) : 0; + static_assert(MAX_REORG_LENGTH > 0, "We must be able to reorg some distance"); + if (reorgLength > MAX_REORG_LENGTH) { + auto msg = strprintf(_( + "A block chain reorganization has been detected that would roll back %d blocks! " + "This is larger than the maximum of %d blocks, and so the node is shutting down for your safety." + ), reorgLength, MAX_REORG_LENGTH) + "\n\n" + + _("Reorganization details") + ":\n" + + "- " + strprintf(_("Current tip: %s, height %d, work %s"), + pindexOldTip->phashBlock->GetHex(), pindexOldTip->nHeight, pindexOldTip->nChainWork.GetHex()) + "\n" + + "- " + strprintf(_("New tip: %s, height %d, work %s"), + pindexMostWork->phashBlock->GetHex(), pindexMostWork->nHeight, pindexMostWork->nChainWork.GetHex()) + "\n" + + "- " + strprintf(_("Fork point: %s, height %d"), + pindexFork->phashBlock->GetHex(), pindexFork->nHeight) + "\n\n" + + _("Please help, human!"); + LogPrintf("*** %s\n", msg); + uiInterface.ThreadSafeMessageBox(msg, "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return false; + } + // Disconnect active blocks which are no longer in the best chain. + bool fBlocksDisconnected = false; while (chainActive.Tip() && chainActive.Tip() != pindexFork) { if (!DisconnectTip(state)) return false; + fBlocksDisconnected = true; } if ( KOMODO_REWIND != 0 ) { - fprintf(stderr,"rewind start ht.%d\n",chainActive.Tip()->nHeight); - while ( KOMODO_REWIND > 0 && chainActive.Tip()->nHeight > KOMODO_REWIND ) + CBlockIndex *tipindex; + fprintf(stderr,">>>>>>>>>>> rewind start ht.%d -> KOMODO_REWIND.%d\n",chainActive.Tip()->nHeight,KOMODO_REWIND); + while ( KOMODO_REWIND > 0 && (tipindex= chainActive.Tip()) != 0 && tipindex->nHeight > KOMODO_REWIND ) { + fBlocksDisconnected = true; + fprintf(stderr,"%d ",(int32_t)tipindex->nHeight); + InvalidateBlock(state,tipindex); if ( !DisconnectTip(state) ) - { - InvalidateBlock(state,chainActive.Tip()); break; - } } - fprintf(stderr,"reached rewind.%d, best to do: ./komodo-cli stop\n",KOMODO_REWIND); - sleep(60); + fprintf(stderr,"reached rewind.%d, best to do: ./komodo-cli -ac_name=%s stop\n",KOMODO_REWIND,ASSETCHAINS_SYMBOL); + sleep(20); + fprintf(stderr,"resuming normal operations\n"); KOMODO_REWIND = 0; - return(true); + //return(true); } // Build list of new blocks to connect. std::vector vpindexToConnect; bool fContinue = true; int nHeight = pindexFork ? pindexFork->nHeight : -1; while (fContinue && nHeight != pindexMostWork->nHeight) { - // Don't iterate the entire list of potential improvements toward the best tip, as we likely only need - // a few blocks along the way. - int nTargetHeight = std::min(nHeight + 32, pindexMostWork->nHeight); - vpindexToConnect.clear(); - vpindexToConnect.reserve(nTargetHeight - nHeight); - CBlockIndex *pindexIter = pindexMostWork->GetAncestor(nTargetHeight); - while (pindexIter && pindexIter->nHeight != nHeight) { - vpindexToConnect.push_back(pindexIter); - pindexIter = pindexIter->pprev; - } - nHeight = nTargetHeight; - - // Connect new blocks. - BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) { - if (!ConnectTip(state, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL)) { - if (state.IsInvalid()) { - // The block violates a consensus rule. - if (!state.CorruptionPossible()) - InvalidChainFound(vpindexToConnect.back()); - state = CValidationState(); - fInvalidFound = true; - fContinue = false; - break; + // Don't iterate the entire list of potential improvements toward the best tip, as we likely only need + // a few blocks along the way. + int nTargetHeight = std::min(nHeight + 32, pindexMostWork->nHeight); + vpindexToConnect.clear(); + vpindexToConnect.reserve(nTargetHeight - nHeight); + CBlockIndex *pindexIter = pindexMostWork->GetAncestor(nTargetHeight); + while (pindexIter && pindexIter->nHeight != nHeight) { + vpindexToConnect.push_back(pindexIter); + pindexIter = pindexIter->pprev; + } + nHeight = nTargetHeight; + + // Connect new blocks. + BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) { + if (!ConnectTip(state, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL)) { + if (state.IsInvalid()) { + // The block violates a consensus rule. + if (!state.CorruptionPossible()) + InvalidChainFound(vpindexToConnect.back()); + state = CValidationState(); + fInvalidFound = true; + fContinue = false; + break; + } else { + // A system error occurred (disk space, database error, ...). + return false; + } } else { - // A system error occurred (disk space, database error, ...). - return false; - } - } else { - PruneBlockIndexCandidates(); - if (!pindexOldTip || chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) { - // We're in a better position than we were. Return temporarily to release the lock. - fContinue = false; - break; + PruneBlockIndexCandidates(); + if (!pindexOldTip || chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) { + // We're in a better position than we were. Return temporarily to release the lock. + fContinue = false; + break; + } } } } + + if (fBlocksDisconnected) { + mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); } - + mempool.removeWithoutBranchId( + CurrentEpochBranchId(chainActive.Tip()->nHeight + 1, Params().GetConsensus())); + mempool.check(pcoinsTip); + // Callbacks/notifications for a new best chain. if (fInvalidFound) CheckForkWarningConditionsOnNewFork(vpindexToConnect.back()); else CheckForkWarningConditions(); - + return true; } @@ -2906,23 +3172,23 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { const CChainParams& chainParams = Params(); do { boost::this_thread::interruption_point(); - + bool fInitialDownload; { LOCK(cs_main); pindexMostWork = FindMostWorkChain(); - + // Whether we have anything to do at all. if (pindexMostWork == NULL || pindexMostWork == chainActive.Tip()) return true; - + if (!ActivateBestChainStep(state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL)) return false; pindexNewTip = chainActive.Tip(); fInitialDownload = IsInitialBlockDownload(); } // When we reach this point, we switched to a new tip (stored in pindexNewTip). - + // Notifications/callbacks that can run without cs_main if (!fInitialDownload) { uint256 hashNewTip = pindexNewTip->GetBlockHash(); @@ -2935,8 +3201,8 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { if (nLocalServices & NODE_NETWORK) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) - if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) - pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip)); + if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) + pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip)); } // Notify external listeners about the new tip. GetMainSignals().UpdatedBlockTip(pindexNewTip); @@ -2944,23 +3210,23 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { } //else fprintf(stderr,"initial download skips propagation\n"); } while(pindexMostWork != chainActive.Tip()); CheckBlockIndex(); - + // Write changes periodically to disk, after relay. if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) { return false; } - + return true; } bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) { AssertLockHeld(cs_main); - + // Mark the block itself as invalid. pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); setBlockIndexCandidates.erase(pindex); - + while (chainActive.Contains(pindex)) { CBlockIndex *pindexWalk = chainActive.Tip(); pindexWalk->nStatus |= BLOCK_FAILED_CHILD; @@ -2969,11 +3235,14 @@ bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) { // ActivateBestChain considers blocks already in chainActive // unconditionally valid already, so force disconnect away from it. if (!DisconnectTip(state)) { + mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); + mempool.removeWithoutBranchId( + CurrentEpochBranchId(chainActive.Tip()->nHeight + 1, Params().GetConsensus())); return false; } } //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(); @@ -2983,16 +3252,19 @@ bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) { } it++; } - + InvalidChainFound(pindex); + mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); + mempool.removeWithoutBranchId( + CurrentEpochBranchId(chainActive.Tip()->nHeight + 1, Params().GetConsensus())); return true; } bool ReconsiderBlock(CValidationState& state, CBlockIndex *pindex) { AssertLockHeld(cs_main); - + int nHeight = pindex->nHeight; - + // Remove the invalidity flag from this block and all its descendants. BlockMap::iterator it = mapBlockIndex.begin(); while (it != mapBlockIndex.end()) { @@ -3009,7 +3281,7 @@ bool ReconsiderBlock(CValidationState& state, CBlockIndex *pindex) { } it++; } - + // Remove the invalidity flag from all ancestors too. while (pindex != NULL) { if (pindex->nStatus & BLOCK_FAILED_MASK) { @@ -3028,7 +3300,7 @@ CBlockIndex* AddToBlockIndex(const CBlockHeader& block) BlockMap::iterator it = mapBlockIndex.find(hash); if (it != mapBlockIndex.end()) return it->second; - + // Construct new block index object CBlockIndex* pindexNew = new CBlockIndex(block); assert(pindexNew); @@ -3049,9 +3321,9 @@ CBlockIndex* AddToBlockIndex(const CBlockHeader& block) pindexNew->RaiseValidity(BLOCK_VALID_TREE); if (pindexBestHeader == NULL || pindexBestHeader->nChainWork < pindexNew->nChainWork) pindexBestHeader = pindexNew; - + setDirtyBlockIndex.insert(pindexNew); - + return pindexNew; } @@ -3060,23 +3332,41 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl { pindexNew->nTx = block.vtx.size(); pindexNew->nChainTx = 0; + CAmount sproutValue = 0; + for (auto tx : block.vtx) { + for (auto js : tx.vjoinsplit) { + sproutValue += js.vpub_old; + sproutValue -= js.vpub_new; + } + } + pindexNew->nSproutValue = sproutValue; + pindexNew->nChainSproutValue = boost::none; pindexNew->nFile = pos.nFile; pindexNew->nDataPos = pos.nPos; pindexNew->nUndoPos = 0; pindexNew->nStatus |= BLOCK_HAVE_DATA; pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS); setDirtyBlockIndex.insert(pindexNew); - + if (pindexNew->pprev == NULL || pindexNew->pprev->nChainTx) { // If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS. deque queue; queue.push_back(pindexNew); - + // Recursively process any descendant blocks that now may be eligible to be connected. while (!queue.empty()) { CBlockIndex *pindex = queue.front(); queue.pop_front(); pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx; + if (pindex->pprev) { + if (pindex->pprev->nChainSproutValue && pindex->nSproutValue) { + pindex->nChainSproutValue = *pindex->pprev->nChainSproutValue + *pindex->nSproutValue; + } else { + pindex->nChainSproutValue = boost::none; + } + } else { + pindex->nChainSproutValue = pindex->nSproutValue; + } { LOCK(cs_nBlockSequenceId); pindex->nSequenceId = nBlockSequenceId++; @@ -3097,19 +3387,19 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl mapBlocksUnlinked.insert(std::make_pair(pindexNew->pprev, pindexNew)); } } - + return true; } bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false) { LOCK(cs_LastBlockFile); - + unsigned int nFile = fKnown ? pos.nFile : nLastBlockFile; if (vinfoBlockFile.size() <= nFile) { vinfoBlockFile.resize(nFile + 1); } - + if (!fKnown) { while (vinfoBlockFile[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { nFile++; @@ -3120,7 +3410,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd pos.nFile = nFile; pos.nPos = vinfoBlockFile[nFile].nSize; } - + if (nFile != nLastBlockFile) { if (!fKnown) { LogPrintf("Leaving block file %i: %s\n", nFile, vinfoBlockFile[nFile].ToString()); @@ -3128,13 +3418,13 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd FlushBlockFile(!fKnown); nLastBlockFile = nFile; } - + vinfoBlockFile[nFile].AddBlock(nHeight, nTime); if (fKnown) vinfoBlockFile[nFile].nSize = std::max(pos.nPos + nAddSize, vinfoBlockFile[nFile].nSize); else vinfoBlockFile[nFile].nSize += nAddSize; - + if (!fKnown) { unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; unsigned int nNewChunks = (vinfoBlockFile[nFile].nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; @@ -3153,7 +3443,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd return state.Error("out of disk space"); } } - + setDirtyFileInfo.insert(nFile); return true; } @@ -3161,14 +3451,14 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize) { pos.nFile = nFile; - + LOCK(cs_LastBlockFile); - + unsigned int nNewSize; pos.nPos = vinfoBlockFile[nFile].nUndoSize; nNewSize = vinfoBlockFile[nFile].nUndoSize += nAddSize; setDirtyFileInfo.insert(nFile); - + unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; unsigned int nNewChunks = (nNewSize + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; if (nNewChunks > nOldChunks) { @@ -3185,7 +3475,7 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne else return state.Error("out of disk space"); } - + return true; } @@ -3209,37 +3499,82 @@ bool CheckBlockHeader(int32_t height,CBlockIndex *pindex, const CBlockHeader& bl } if (blockhdr.GetBlockTime() > GetAdjustedTime() + 60) return state.Invalid(error("CheckBlockHeader(): block timestamp too far in the future"),REJECT_INVALID, "time-too-new"); + else if ( ASSETCHAINS_STAKED != 0 && pindex != 0 && pindex->pprev != 0 && pindex->nTime <= pindex->pprev->nTime ) + { + fprintf(stderr,"ht.%d %u vs ht.%d %u, is not monotonic\n",pindex->nHeight,pindex->nTime,pindex->pprev->nHeight,pindex->pprev->nTime); + return state.Invalid(error("CheckBlockHeader(): block timestamp needs to always increase"),REJECT_INVALID, "time-too-new"); + } // Check block version - //if (block.nVersion < MIN_BLOCK_VERSION) - // return state.DoS(100, error("CheckBlockHeader(): block version too low"),REJECT_INVALID, "version-too-low"); - + if (height > 0 && blockhdr.nVersion < MIN_BLOCK_VERSION) + { + fprintf(stderr,"nVersion.%d vs min %d\n",blockhdr.nVersion,MIN_BLOCK_VERSION); + return state.DoS(100, error("CheckBlockHeader(): block version too low"),REJECT_INVALID, "version-too-low"); + } // Check Equihash solution is valid - if ( fCheckPOW && !CheckEquihashSolution(&blockhdr, Params()) ) - return state.DoS(100, error("CheckBlockHeader(): Equihash solution invalid"),REJECT_INVALID, "invalid-solution"); + /*if ( fCheckPOW && !CheckEquihashSolution(&blockhdr, Params()) ) + return state.DoS(100, error("CheckBlockHeader(): Equihash solution invalid"),REJECT_INVALID, "invalid-solution");*/ // Check proof of work matches claimed amount /*komodo_index2pubkey33(pubkey33,pindex,height); - if ( fCheckPOW && !CheckProofOfWork(height,pubkey33,blockhdr.GetHash(), blockhdr.nBits, Params().GetConsensus()) ) - return state.DoS(50, error("CheckBlockHeader(): proof of work failed"),REJECT_INVALID, "high-hash");*/ + if ( fCheckPOW && !CheckProofOfWork(height,pubkey33,blockhdr.GetHash(), blockhdr.nBits, Params().GetConsensus(),blockhdr.nTime) ) + return state.DoS(50, error("CheckBlockHeader(): proof of work failed"),REJECT_INVALID, "high-hash");*/ return true; } -int32_t komodo_check_deposit(int32_t height,const CBlock& block); +int32_t komodo_check_deposit(int32_t height,const CBlock& block,uint32_t prevtime); + +int32_t komodo_reverify_blockcheck(CValidationState& state,int32_t height,CBlockIndex *pindex) +{ + static int32_t oneshot; + CBlockIndex *tipindex; int32_t rewindtarget; + if ( KOMODO_REWIND != 0 ) + oneshot = KOMODO_REWIND; + if ( oneshot == 0 && IsInitialBlockDownload() == 0 && (tipindex= chainActive.Tip()) != 0 ) + { + // if 200 blocks behind longestchain and no blocks for 2 hours + if ( KOMODO_LONGESTCHAIN > height+200 && KOMODO_NEWBLOCKS == 0 ) + { + if ( GetAdjustedTime() > tipindex->nTime+3600*2 ) + { + fprintf(stderr,"possible fork: tip.%d longest.%d newblock.%d lag.%d blocktime.%u\n",tipindex->nHeight,KOMODO_LONGESTCHAIN,height,(int32_t)(GetAdjustedTime() - tipindex->nTime),tipindex->nTime); + /*KOMODO_REWIND = tipindex->nHeight - 11; + rewindtarget = tipindex->nHeight - 11; + fprintf(stderr,"rewindtarget <- %d\n",rewindtarget); + oneshot = 1; + while ( rewindtarget > 0 && (tipindex= chainActive.Tip()) != 0 && tipindex->nHeight > rewindtarget ) + { + fprintf(stderr,"%d ",(int32_t)tipindex->nHeight); + InvalidateBlock(state,tipindex); + if ( !DisconnectTip(state) ) + break; + } + tipindex = chainActive.Tip(); + fprintf(stderr,"rewind done to %d\n",tipindex!=0?tipindex->nHeight:-1);*/ + } + } + } + return(0); +} + bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidationState& state, libzcash::ProofVerifier& verifier, bool fCheckPOW, bool fCheckMerkleRoot) { uint8_t pubkey33[33]; - // These are checks that are independent of context. - + // These are checks that are independent of context. + // Check that the header is valid (particularly PoW). This is mostly // redundant with the call in AcceptBlockHeader. if (!CheckBlockHeader(height,pindex,block,state,fCheckPOW)) + { + //fprintf(stderr,"CheckBlockHeader error in CheckBlock fCheckPOW.%d\n",fCheckPOW); return false; + } + if ( fCheckPOW && !CheckEquihashSolution(&block, Params()) ) + return state.DoS(100, error("CheckBlockHeader(): Equihash solution invalid"),REJECT_INVALID, "invalid-solution"); komodo_block2pubkey33(pubkey33,(CBlock *)&block); - if ( fCheckPOW && !CheckProofOfWork(height,pubkey33,block.GetHash(), block.nBits, Params().GetConsensus()) ) - return state.DoS(50, error("CheckBlock(): proof of work failed"),REJECT_INVALID, "high-hash"); - + if ( fCheckPOW && !CheckProofOfWork(height,pubkey33,block.GetHash(), block.nBits, Params().GetConsensus(),block.nTime) ) + return state.DoS(1, error("CheckBlock(): proof of work failed"),REJECT_INVALID, "high-hash"); // Check the merkle root. if (fCheckMerkleRoot) { bool mutated; @@ -3247,7 +3582,7 @@ bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidat if (block.hashMerkleRoot != hashMerkleRoot2) return state.DoS(100, error("CheckBlock(): hashMerkleRoot mismatch"), REJECT_INVALID, "bad-txnmrklroot", true); - + // Check for merkle tree malleability (CVE-2012-2459): repeating sequences // of transactions in a block without affecting the merkle root of a block, // while still invalidating it. @@ -3255,16 +3590,16 @@ bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidat return state.DoS(100, error("CheckBlock(): duplicate transaction"), REJECT_INVALID, "bad-txns-duplicate", true); } - + // All potential-corruption validation must be done before we do any // transaction validation, as otherwise we may mark the header as invalid // because we receive the wrong transactions for it. - + // Size limits if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) return state.DoS(100, error("CheckBlock(): size limits failed"), REJECT_INVALID, "bad-blk-length"); - + // First transaction must be coinbase, the rest must not be if (block.vtx.empty() || !block.vtx[0].IsCoinBase()) return state.DoS(100, error("CheckBlock(): first tx is not coinbase"), @@ -3273,12 +3608,12 @@ bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidat if (block.vtx[i].IsCoinBase()) return state.DoS(100, error("CheckBlock(): more than one coinbase"), REJECT_INVALID, "bad-cb-multiple"); - + // Check transactions BOOST_FOREACH(const CTransaction& tx, block.vtx) { - if ( komodo_validate_interest(tx,komodo_block2height((CBlock *)&block),block.nTime,1) < 0 ) - return error("CheckBlock: komodo_validate_interest failed"); + if ( komodo_validate_interest(tx,height == 0 ? komodo_block2height((CBlock *)&block) : height,block.nTime,1) < 0 ) + return error("CheckBlock: komodo_validate_interest failed"); if (!CheckTransaction(tx, state, verifier)) return error("CheckBlock(): CheckTransaction failed"); } @@ -3290,10 +3625,11 @@ bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidat if (nSigOps > MAX_BLOCK_SIGOPS) return state.DoS(100, error("CheckBlock(): out-of-bounds SigOpCount"), REJECT_INVALID, "bad-blk-sigops", true); - if ( komodo_check_deposit(ASSETCHAINS_SYMBOL[0] == 0 ? height : pindex != 0 ? (int32_t)pindex->nHeight : chainActive.Tip()->nHeight+1,block) < 0 ) + if ( komodo_check_deposit(height,block,(pindex==0||pindex->pprev==0)?0:pindex->pprev->nTime) < 0 ) + //if ( komodo_check_deposit(ASSETCHAINS_SYMBOL[0] == 0 ? height : pindex != 0 ? (int32_t)pindex->nHeight : chainActive.Tip()->nHeight+1,block,pindex==0||pindex->pprev==0?0:pindex->pprev->nTime) < 0 ) { static uint32_t counter; - if ( counter++ < 100 ) + //if ( counter++ < 100 && ASSETCHAINS_STAKED == 0 ) fprintf(stderr,"check deposit rejection\n"); return(false); } @@ -3307,11 +3643,11 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta uint256 hash = block.GetHash(); if (hash == consensusParams.hashGenesisBlock) return true; - + assert(pindexPrev); - + int nHeight = pindexPrev->nHeight+1; - + // Check proof of work if ( (nHeight < 235300 || nHeight > 236000) && block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) { @@ -3319,28 +3655,30 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta return state.DoS(100, error("%s: incorrect proof of work", __func__), REJECT_INVALID, "bad-diffbits"); } - + // Check timestamp against prev if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) return state.Invalid(error("%s: block's timestamp is too early", __func__), REJECT_INVALID, "time-too-old"); - + if (fCheckpointsEnabled) { + // Check that the block chain matches the known block chain up to a checkpoint if (!Checkpoints::CheckBlock(chainParams.Checkpoints(), nHeight, hash)) { - CBlockIndex *heightblock = chainActive[nHeight]; - if ( heightblock != 0 && heightblock->GetBlockHash() == hash ) - { - //fprintf(stderr,"got a pre notarization block that matches height.%d\n",(int32_t)nHeight); - return true; - } return state.DoS(100, error("%s: rejected by checkpoint lock-in at %d", __func__, nHeight),REJECT_CHECKPOINT, "checkpoint mismatch"); + /*CBlockIndex *heightblock = chainActive[nHeight]; + if ( heightblock != 0 && heightblock->GetBlockHash() == hash ) + { + //fprintf(stderr,"got a pre notarization block that matches height.%d\n",(int32_t)nHeight); + return true; + }*/ + return state.DoS(100, error("%s: rejected by checkpoint lock-in at %d", __func__, nHeight),REJECT_CHECKPOINT, "checkpoint mismatch"); } // Don't accept any forks from the main chain prior to last checkpoint CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(chainParams.Checkpoints()); int32_t notarized_height; if (pcheckpoint && nHeight > 1 && nHeight < pcheckpoint->nHeight ) - return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d) vs %d", __func__, nHeight,pcheckpoint->nHeight)); + return state.DoS(1, error("%s: forked chain older than last checkpoint (height %d) vs %d", __func__, nHeight,pcheckpoint->nHeight)); else if ( komodo_checkpoint(¬arized_height,nHeight,hash) < 0 ) { CBlockIndex *heightblock = chainActive[nHeight]; @@ -3348,14 +3686,14 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta { //fprintf(stderr,"got a pre notarization block that matches height.%d\n",(int32_t)nHeight); return true; - } else return state.DoS(100, error("%s: forked chain %d older than last notarized (height %d) vs %d", __func__,nHeight, notarized_height)); + } else return state.DoS(1, error("%s: forked chain %d older than last notarized (height %d) vs %d", __func__,nHeight, notarized_height)); } } // Reject block.nVersion < 4 blocks if (block.nVersion < 4) return state.Invalid(error("%s : rejected nVersion<4 block", __func__), REJECT_OBSOLETE, "bad-version"); - + return true; } @@ -3363,23 +3701,28 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn { const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1; const Consensus::Params& consensusParams = Params().GetConsensus(); - + // Check that all transactions are finalized BOOST_FOREACH(const CTransaction& tx, block.vtx) { + + // Check transaction contextually against consensus rules at block height + if (!ContextualCheckTransaction(tx, state, nHeight, 100)) { + return false; // Failure reason has been set in validation state object + } + int nLockTimeFlags = 0; int64_t nLockTimeCutoff = (nLockTimeFlags & LOCKTIME_MEDIAN_TIME_PAST) - ? pindexPrev->GetMedianTimePast() - : block.GetBlockTime(); + ? pindexPrev->GetMedianTimePast() + : block.GetBlockTime(); if (!IsFinalTx(tx, nHeight, nLockTimeCutoff)) { return state.DoS(10, error("%s: contains a non-final transaction", __func__), REJECT_INVALID, "bad-txns-nonfinal"); } } - - // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height - // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): - // Since MIN_BLOCK_VERSION = 4 all blocks with nHeight > 0 should satisfy this. - // This rule is not applied to the genesis block, which didn't include the height - // in the coinbase. + + // Enforce BIP 34 rule that the coinbase starts with serialized block height. + // In Zcash this has been enforced since launch, except that the genesis + // block didn't include the height in the coinbase (see Zcash protocol spec + // section '6.8 Bitcoin Improvement Proposals'). if (nHeight > 0) { CScript expect = CScript() << nHeight; @@ -3388,7 +3731,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn return state.DoS(100, error("%s: block height mismatch in coinbase", __func__), REJECT_INVALID, "bad-cb-height"); } } - + return true; } @@ -3405,61 +3748,39 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc pindex = miSelf->second; if (ppindex) *ppindex = pindex; - if (pindex != 0 && pindex->nStatus & BLOCK_FAILED_MASK) + if ( pindex != 0 && pindex->nStatus & BLOCK_FAILED_MASK ) return state.Invalid(error("%s: block is marked invalid", __func__), 0, "duplicate"); - if ( pindex != 0 && IsInitialBlockDownload() == 0 ) // jl777 - { - if (!CheckBlockHeader(pindex->nHeight,pindex, block, state)) - { - pindex->nStatus |= BLOCK_FAILED_MASK; - fprintf(stderr,"known block failing CheckBlockHeader %d\n",(int32_t)pindex->nHeight); - return false; - } - CBlockIndex* pindexPrev = NULL; - if (hash != chainparams.GetConsensus().hashGenesisBlock) - { - BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); - if (mi == mapBlockIndex.end()) - { - pindex->nStatus |= BLOCK_FAILED_MASK; - fprintf(stderr,"known block.%d failing to find prevblock\n",(int32_t)pindex->nHeight); - return state.DoS(10, error("%s: prev block not found", __func__), 0, "bad-prevblk"); - } - pindexPrev = (*mi).second; - if (pindexPrev == 0 || (pindexPrev->nStatus & BLOCK_FAILED_MASK) ) - { - pindex->nStatus |= BLOCK_FAILED_MASK; - fprintf(stderr,"known block.%d found invalid prevblock\n",(int32_t)pindex->nHeight); - return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); - } - } - if (!ContextualCheckBlockHeader(block, state, pindexPrev)) - { - pindex->nStatus |= BLOCK_FAILED_MASK; - //fprintf(stderr,"known block.%d failing ContextualCheckBlockHeader\n",(int32_t)pindex->nHeight); - return false; - } - } - return true; + if ( pindex != 0 ) + return true; } - - if (!CheckBlockHeader(*ppindex!=0?(*ppindex)->nHeight:0,*ppindex, block, state)) + + if (!CheckBlockHeader(*ppindex!=0?(*ppindex)->nHeight:0,*ppindex, block, state,0)) + { + fprintf(stderr,"CheckBlockHeader failed\n"); return false; - + } // Get prev block index CBlockIndex* pindexPrev = NULL; if (hash != chainparams.GetConsensus().hashGenesisBlock) { BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); if (mi == mapBlockIndex.end()) + { return state.DoS(10, error("%s: prev block not found", __func__), 0, "bad-prevblk"); + } pindexPrev = (*mi).second; if (pindexPrev == 0 || (pindexPrev->nStatus & BLOCK_FAILED_MASK) ) return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); } if (!ContextualCheckBlockHeader(block, state, pindexPrev)) + { + fprintf(stderr,"ContextualCheckBlockHeader failed\n"); return false; + } if (pindex == NULL) - pindex = AddToBlockIndex(block); + { + if ( (pindex= AddToBlockIndex(block)) == 0 ) + fprintf(stderr,"couldnt add to block index\n"); + } if (ppindex) *ppindex = pindex; return true; @@ -3469,15 +3790,19 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, { const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); - + CBlockIndex *&pindex = *ppindex; if (!AcceptBlockHeader(block, state, &pindex)) + { + //fprintf(stderr,"AcceptBlockHeader rejected\n"); return false; + } if ( pindex == 0 ) { - fprintf(stderr,"AcceptBlock error null pindex\n"); + fprintf(stderr,"unexpected AcceptBlock error null pindex\n"); return false; } + //fprintf(stderr,"acceptblockheader passed\n"); // Try to process all requested blocks that we don't have, but only // process an unrequested block if it's new and has enough work to // advance our tip, and isn't too many blocks ahead. @@ -3489,7 +3814,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, // regardless of whether pruning is enabled; it should generally be safe to // not process unrequested blocks. bool fTooFarAhead = (pindex->nHeight > int(chainActive.Height() + MIN_BLOCKS_TO_KEEP)); - + // TODO: deal better with return value and error conditions for duplicate // and unrequested blocks. if (fAlreadyHave) return true; @@ -3498,19 +3823,21 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, if (!fHasMoreWork) return true; // Don't process less-work chains if (fTooFarAhead) return true; // Block height is too high } - + // See method docstring for why this is always disabled auto verifier = libzcash::ProofVerifier::Disabled(); - if ((!CheckBlock(pindex->nHeight,pindex,block, state, verifier)) || !ContextualCheckBlock(block, state, pindex->pprev)) { + if ((!CheckBlock(pindex->nHeight,pindex,block, state, verifier,0)) || !ContextualCheckBlock(block, state, pindex->pprev)) + { if (state.IsInvalid() && !state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); } + fprintf(stderr,"CheckBlock or ContextualCheckBlock failed\n"); return false; } - + int nHeight = pindex->nHeight; - + // Write block to history file try { unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); @@ -3527,10 +3854,10 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, } catch (const std::runtime_error& e) { return AbortNode(state, std::string("System error: ") + e.what()); } - + if (fCheckForPruning) FlushStateToDisk(state, FLUSH_STATE_NONE); // we just allocated more disk space for block files - + return true; } @@ -3556,18 +3883,21 @@ bool ProcessNewBlock(int32_t height,CValidationState &state, CNode* pfrom, CBloc if ( chainActive.Tip() != 0 ) komodo_currentheight_set(chainActive.Tip()->nHeight); if ( ASSETCHAINS_SYMBOL[0] == 0 ) - checked = CheckBlock(height!=0?height:komodo_block2height(pblock),0,*pblock, state, verifier); - else checked = CheckBlock(height!=0?height:komodo_block2height(pblock),0,*pblock, state, verifier); + checked = CheckBlock(height!=0?height:komodo_block2height(pblock),0,*pblock, state, verifier,0); + else checked = CheckBlock(height!=0?height:komodo_block2height(pblock),0,*pblock, state, verifier,0); { LOCK(cs_main); bool fRequested = MarkBlockAsReceived(pblock->GetHash()); fRequested |= fForceProcessing; - if (!checked) { + if (!checked) + { if ( pfrom != 0 ) + { Misbehaving(pfrom->GetId(), 1); + } return error("%s: CheckBlock FAILED", __func__); } - + // Store to disk CBlockIndex *pindex = NULL; bool ret = AcceptBlock(*pblock, state, &pindex, fRequested, dbp); @@ -3578,10 +3908,10 @@ bool ProcessNewBlock(int32_t height,CValidationState &state, CNode* pfrom, CBloc if (!ret) return error("%s: AcceptBlock FAILED", __func__); } - + if (!ActivateBestChain(state, pblock)) return error("%s: ActivateBestChain failed", __func__); - + return true; } @@ -3589,14 +3919,14 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex { AssertLockHeld(cs_main); assert(pindexPrev == chainActive.Tip()); - + CCoinsViewCache viewNew(pcoinsTip); CBlockIndex indexDummy(block); indexDummy.pprev = pindexPrev; indexDummy.nHeight = pindexPrev->nHeight + 1; // JoinSplit proofs are verified in ConnectBlock auto verifier = libzcash::ProofVerifier::Disabled(); - + // NOTE: CheckBlockHeader is called by CheckBlock if (!ContextualCheckBlockHeader(block, state, pindexPrev)) { @@ -3613,13 +3943,13 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex fprintf(stderr,"TestBlockValidity failure C\n"); return false; } - if (!ConnectBlock(block, state, &indexDummy, viewNew, true)) + if (!ConnectBlock(block, state, &indexDummy, viewNew, true,fCheckPOW)) { fprintf(stderr,"TestBlockValidity failure D\n"); return false; } assert(state.IsValid()); - + return true; } @@ -3649,7 +3979,7 @@ void PruneOneBlockFile(const int fileNumber) pindex->nDataPos = 0; pindex->nUndoPos = 0; setDirtyBlockIndex.insert(pindex); - + // Prune from mapBlocksUnlinked -- any block we prune would have // to be downloaded again in order to consider its chain, at which // point it would be considered as a candidate for @@ -3664,7 +3994,7 @@ void PruneOneBlockFile(const int fileNumber) } } } - + vinfoBlockFile[fileNumber].SetNull(); setDirtyFileInfo.insert(fileNumber); } @@ -3690,7 +4020,7 @@ void FindFilesToPrune(std::set& setFilesToPrune) if (chainActive.Tip()->nHeight <= Params().PruneAfterHeight()) { return; } - + unsigned int nLastBlockWeCanPrune = chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP; uint64_t nCurrentUsage = CalculateCurrentUsage(); // We don't check to prune until after we've allocated new space for files @@ -3699,21 +4029,21 @@ void FindFilesToPrune(std::set& setFilesToPrune) uint64_t nBuffer = BLOCKFILE_CHUNK_SIZE + UNDOFILE_CHUNK_SIZE; uint64_t nBytesToPrune; int count=0; - + if (nCurrentUsage + nBuffer >= nPruneTarget) { for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) { nBytesToPrune = vinfoBlockFile[fileNumber].nSize + vinfoBlockFile[fileNumber].nUndoSize; - + if (vinfoBlockFile[fileNumber].nSize == 0) continue; - + if (nCurrentUsage + nBuffer < nPruneTarget) // are we below our target? break; - + // don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip but keep scanning if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) continue; - + PruneOneBlockFile(fileNumber); // Queue up the files for removal setFilesToPrune.insert(fileNumber); @@ -3721,26 +4051,28 @@ void FindFilesToPrune(std::set& setFilesToPrune) count++; } } - + LogPrint("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); + nPruneTarget/1024/1024, nCurrentUsage/1024/1024, + ((int64_t)nPruneTarget - (int64_t)nCurrentUsage)/1024/1024, + nLastBlockWeCanPrune, count); } bool CheckDiskSpace(uint64_t nAdditionalBytes) { uint64_t nFreeBytesAvailable = boost::filesystem::space(GetDataDir()).available; - + // Check for nMinDiskSpace bytes (currently 50MB) if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes) return AbortNode("Disk space is low!", _("Error: Disk space is low!")); - + return true; } + FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly) { + static int32_t didinit[1000]; long fsize,fpos; int32_t incr = 16*1024*1024; if (pos.IsNull()) return NULL; boost::filesystem::path path = GetBlockPosFilename(pos, prefix); @@ -3752,6 +4084,26 @@ FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly) LogPrintf("Unable to open file %s\n", path.string()); return NULL; } + if ( pos.nFile < sizeof(didinit)/sizeof(*didinit) && didinit[pos.nFile] == 0 && strcmp(prefix,(char *)"blk") == 0 ) + { + fpos = ftell(file); + fseek(file,0,SEEK_END); + fsize = ftell(file); + if ( fsize > incr ) + { + char *ignore = (char *)malloc(incr); + if ( ignore != 0 ) + { + rewind(file); + while ( fread(ignore,1,incr,file) == incr ) + fprintf(stderr,"."); + free(ignore); + fprintf(stderr,"blk.%d loaded %ld bytes set fpos.%ld loading.%d\n",(int)pos.nFile,(long)ftell(file),(long)fpos,KOMODO_LOADINGBLOCKS); + } + } + fseek(file,fpos,SEEK_SET); + didinit[pos.nFile] = 1; + } if (pos.nPos) { if (fseek(file, pos.nPos, SEEK_SET)) { LogPrintf("Unable to seek to position %u of %s\n", pos.nPos, path.string()); @@ -3779,30 +4131,32 @@ CBlockIndex * InsertBlockIndex(uint256 hash) { if (hash.IsNull()) return NULL; - + // Return existing BlockMap::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end()) return (*mi).second; - + // Create new CBlockIndex* pindexNew = new CBlockIndex(); if (!pindexNew) throw runtime_error("LoadBlockIndex(): new CBlockIndex failed"); mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); - + return pindexNew; } +//void komodo_pindex_init(CBlockIndex *pindex,int32_t height); + bool static LoadBlockIndexDB() { const CChainParams& chainparams = Params(); if (!pblocktree->LoadBlockIndexGuts()) return false; - + boost::this_thread::interruption_point(); - + // Calculate nChainWork vector > vSortedByHeight; vSortedByHeight.reserve(mapBlockIndex.size()); @@ -3810,6 +4164,7 @@ bool static LoadBlockIndexDB() { CBlockIndex* pindex = item.second; vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex)); + //komodo_pindex_init(pindex,(int32_t)pindex->nHeight); } sort(vSortedByHeight.begin(), vSortedByHeight.end()); BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight) @@ -3822,13 +4177,33 @@ bool static LoadBlockIndexDB() if (pindex->pprev) { if (pindex->pprev->nChainTx) { pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx; + if (pindex->pprev->nChainSproutValue && pindex->nSproutValue) { + pindex->nChainSproutValue = *pindex->pprev->nChainSproutValue + *pindex->nSproutValue; + } else { + pindex->nChainSproutValue = boost::none; + } } else { pindex->nChainTx = 0; + pindex->nChainSproutValue = boost::none; mapBlocksUnlinked.insert(std::make_pair(pindex->pprev, pindex)); } } else { pindex->nChainTx = pindex->nTx; + pindex->nChainSproutValue = pindex->nSproutValue; + } + } + // Construct in-memory chain of branch IDs. + // Relies on invariant: a block that does not activate a network upgrade + // will always be valid under the same consensus rules as its parent. + // Genesis block has a branch ID of zero by definition, but has no + // validity status because it is side-loaded into a fresh chain. + // Activation blocks will have branch IDs set (read from disk). + if (pindex->pprev) { + if (pindex->IsValid(BLOCK_VALID_CONSENSUS) && !pindex->nCachedBranchId) { + pindex->nCachedBranchId = pindex->pprev->nCachedBranchId; } + } else { + pindex->nCachedBranchId = SPROUT_BRANCH_ID; } if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == NULL)) setBlockIndexCandidates.insert(pindex); @@ -3838,8 +4213,9 @@ bool static LoadBlockIndexDB() pindex->BuildSkip(); if (pindex->IsValid(BLOCK_VALID_TREE) && (pindexBestHeader == NULL || CBlockIndexWorkComparator()(pindexBestHeader, pindex))) pindexBestHeader = pindex; + //komodo_pindex_init(pindex,(int32_t)pindex->nHeight); } - + // Load block file info pblocktree->ReadLastBlockFile(nLastBlockFile); vinfoBlockFile.resize(nLastBlockFile + 1); @@ -3856,7 +4232,7 @@ bool static LoadBlockIndexDB() break; } } - + // Check presence of blk files LogPrintf("Checking all blk files are present...\n"); set setBlkDataFiles; @@ -3866,6 +4242,7 @@ bool static LoadBlockIndexDB() if (pindex->nStatus & BLOCK_HAVE_DATA) { setBlkDataFiles.insert(pindex->nFile); } + //komodo_pindex_init(pindex,(int32_t)pindex->nHeight); } for (std::set::iterator it = setBlkDataFiles.begin(); it != setBlkDataFiles.end(); it++) { @@ -3874,21 +4251,21 @@ bool static LoadBlockIndexDB() return false; } } - + // Check whether we have ever pruned block & undo files pblocktree->ReadFlag("prunedblockfiles", fHavePruned); if (fHavePruned) LogPrintf("LoadBlockIndexDB(): Block files have previously been pruned\n"); - + // Check whether we need to continue reindexing bool fReindexing = false; pblocktree->ReadReindexing(fReindexing); fReindex |= fReindexing; - + // Check whether we have a transaction index pblocktree->ReadFlag("txindex", fTxIndex); LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled"); - + // Fill in-memory data BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) { @@ -3901,8 +4278,9 @@ bool static LoadBlockIndexDB() if (pindex->pprev) { pindex->pprev->hashAnchorEnd = pindex->hashAnchor; } + //komodo_pindex_init(pindex,(int32_t)pindex->nHeight); } - + // Load pointer to end of best chain BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); if (it == mapBlockIndex.end()) @@ -3910,14 +4288,16 @@ bool static LoadBlockIndexDB() chainActive.SetTip(it->second); // Set hashAnchorEnd for the end of best chain it->second->hashAnchorEnd = pcoinsTip->GetBestAnchor(); - + PruneBlockIndexCandidates(); - + LogPrintf("%s: hashBestChain=%s height=%d date=%s progress=%f\n", __func__, - chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), - Checkpoints::GuessVerificationProgress(chainparams.Checkpoints(), chainActive.Tip())); - + chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), + Checkpoints::GuessVerificationProgress(chainparams.Checkpoints(), chainActive.Tip())); + + EnforceNodeDeprecation(chainActive.Height(), true); + return true; } @@ -3936,7 +4316,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth LOCK(cs_main); if (chainActive.Tip() == NULL || chainActive.Tip()->pprev == NULL) return true; - + // Verify blocks in the best chain if (nCheckDepth <= 0) nCheckDepth = 1000000000; // suffices until the year 19000 @@ -3962,7 +4342,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth if (!ReadBlockFromDisk(block, pindex)) return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); // check level 1: verify block validity - if (nCheckLevel >= 1 && !CheckBlock(pindex->nHeight,pindex,block, state, verifier)) + if (nCheckLevel >= 1 && !CheckBlock(pindex->nHeight,pindex,block, state, verifier,0)) return error("VerifyDB(): *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); // check level 2: verify undo validity if (nCheckLevel >= 2 && pindex) { @@ -3990,7 +4370,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth } if (pindexFailure) return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainActive.Height() - pindexFailure->nHeight + 1, nGoodTransactions); - + // check level 4: try reconnecting blocks if (nCheckLevel >= 4) { CBlockIndex *pindex = pindexState; @@ -4001,13 +4381,148 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth CBlock block; if (!ReadBlockFromDisk(block, pindex)) return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); - if (!ConnectBlock(block, state, pindex, coins)) + if (!ConnectBlock(block, state, pindex, coins,false, true)) return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); } } - + LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", chainActive.Height() - pindexState->nHeight, nGoodTransactions); + + return true; +} +bool RewindBlockIndex(const CChainParams& params) +{ + LOCK(cs_main); + + // RewindBlockIndex is called after LoadBlockIndex, so at this point every block + // index will have nCachedBranchId set based on the values previously persisted + // to disk. By definition, a set nCachedBranchId means that the block was + // fully-validated under the corresponding consensus rules. Thus we can quickly + // identify whether the current active chain matches our expected sequence of + // consensus rule changes, with two checks: + // + // - BLOCK_ACTIVATES_UPGRADE is set only on blocks that activate upgrades. + // - nCachedBranchId for each block matches what we expect. + auto sufficientlyValidated = [¶ms](const CBlockIndex* pindex) { + auto consensus = params.GetConsensus(); + bool fFlagSet = pindex->nStatus & BLOCK_ACTIVATES_UPGRADE; + bool fFlagExpected = IsActivationHeightForAnyUpgrade(pindex->nHeight, consensus); + return fFlagSet == fFlagExpected && + pindex->nCachedBranchId && + *pindex->nCachedBranchId == CurrentEpochBranchId(pindex->nHeight, consensus); + }; + + int nHeight = 1; + while (nHeight <= chainActive.Height()) { + if (!sufficientlyValidated(chainActive[nHeight])) { + break; + } + nHeight++; + } + + // nHeight is now the height of the first insufficiently-validated block, or tipheight + 1 + auto rewindLength = chainActive.Height() - nHeight; + if (rewindLength > 0 && rewindLength > MAX_REORG_LENGTH) { + auto pindexOldTip = chainActive.Tip(); + auto pindexRewind = chainActive[nHeight - 1]; + auto msg = strprintf(_( + "A block chain rewind has been detected that would roll back %d blocks! " + "This is larger than the maximum of %d blocks, and so the node is shutting down for your safety." + ), rewindLength, MAX_REORG_LENGTH) + "\n\n" + + _("Rewind details") + ":\n" + + "- " + strprintf(_("Current tip: %s, height %d"), + pindexOldTip->phashBlock->GetHex(), pindexOldTip->nHeight) + "\n" + + "- " + strprintf(_("Rewinding to: %s, height %d"), + pindexRewind->phashBlock->GetHex(), pindexRewind->nHeight) + "\n\n" + + _("Please help, human!"); + LogPrintf("*** %s\n", msg); + uiInterface.ThreadSafeMessageBox(msg, "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return false; + } + + CValidationState state; + CBlockIndex* pindex = chainActive.Tip(); + while (chainActive.Height() >= nHeight) { + if (fPruneMode && !(chainActive.Tip()->nStatus & BLOCK_HAVE_DATA)) { + // If pruning, don't try rewinding past the HAVE_DATA point; + // since older blocks can't be served anyway, there's + // no need to walk further, and trying to DisconnectTip() + // will fail (and require a needless reindex/redownload + // of the blockchain). + break; + } + if (!DisconnectTip(state, true)) { + return error("RewindBlockIndex: unable to disconnect block at height %i", pindex->nHeight); + } + // Occasionally flush state to disk. + if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) + return false; + } + + // Reduce validity flag and have-data flags. + // We do this after actual disconnecting, otherwise we'll end up writing the lack of data + // to disk before writing the chainstate, resulting in a failure to continue if interrupted. + for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) { + CBlockIndex* pindexIter = it->second; + + // Note: If we encounter an insufficiently validated block that + // is on chainActive, it must be because we are a pruning node, and + // this block or some successor doesn't HAVE_DATA, so we were unable to + // rewind all the way. Blocks remaining on chainActive at this point + // must not have their validity reduced. + if (!sufficientlyValidated(pindexIter) && !chainActive.Contains(pindexIter)) { + // Reduce validity + pindexIter->nStatus = + std::min(pindexIter->nStatus & BLOCK_VALID_MASK, BLOCK_VALID_TREE) | + (pindexIter->nStatus & ~BLOCK_VALID_MASK); + // Remove have-data flags + pindexIter->nStatus &= ~(BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO); + // Remove branch ID + pindexIter->nStatus &= ~BLOCK_ACTIVATES_UPGRADE; + pindexIter->nCachedBranchId = boost::none; + // Remove storage location + pindexIter->nFile = 0; + pindexIter->nDataPos = 0; + pindexIter->nUndoPos = 0; + // Remove various other things + pindexIter->nTx = 0; + pindexIter->nChainTx = 0; + pindexIter->nSproutValue = boost::none; + pindexIter->nChainSproutValue = boost::none; + pindexIter->nSequenceId = 0; + // Make sure it gets written + setDirtyBlockIndex.insert(pindexIter); + if (pindexIter == pindexBestInvalid) + { + //fprintf(stderr,"Reset invalid block marker if it was pointing to this block\n"); + pindexBestInvalid = NULL; + } + + // Update indices + setBlockIndexCandidates.erase(pindexIter); + auto ret = mapBlocksUnlinked.equal_range(pindexIter->pprev); + while (ret.first != ret.second) { + if (ret.first->second == pindexIter) { + mapBlocksUnlinked.erase(ret.first++); + } else { + ++ret.first; + } + } + } else if (pindexIter->IsValid(BLOCK_VALID_TRANSACTIONS) && pindexIter->nChainTx) { + setBlockIndexCandidates.insert(pindexIter); + } + } + + PruneBlockIndexCandidates(); + + CheckBlockIndex(); + + if (!FlushStateToDisk(state, FLUSH_STATE_ALWAYS)) { + return false; + } + return true; } @@ -4034,7 +4549,7 @@ void UnloadBlockIndex() setDirtyFileInfo.clear(); mapNodeState.clear(); recentRejects.reset(NULL); - + BOOST_FOREACH(BlockMap::value_type& entry, mapBlockIndex) { delete entry.second; } @@ -4051,7 +4566,6 @@ bool LoadBlockIndex() KOMODO_LOADINGBLOCKS = 0; return false; } - //KOMODO_LOADINGBLOCKS = 0; fprintf(stderr,"finished loading blocks %s\n",ASSETCHAINS_SYMBOL); return true; } @@ -4060,19 +4574,19 @@ bool LoadBlockIndex() bool InitBlockIndex() { const CChainParams& chainparams = Params(); LOCK(cs_main); - + // Initialize global variables that cannot be constructed at startup. recentRejects.reset(new CRollingBloomFilter(120000, 0.000001)); - + // Check whether we're already initialized if (chainActive.Genesis() != NULL) return true; - + // Use the provided setting for -txindex in the new database fTxIndex = GetBoolArg("-txindex", true); pblocktree->WriteFlag("txindex", fTxIndex); LogPrintf("Initializing databases...\n"); - + // Only add the genesis block if not reindexing (in which case we reuse the one already on disk) if (!fReindex) { try { @@ -4086,6 +4600,8 @@ bool InitBlockIndex() { if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) return error("LoadBlockIndex(): writing genesis block to disk failed"); CBlockIndex *pindex = AddToBlockIndex(block); + if ( pindex == 0 ) + return error("LoadBlockIndex(): couldnt add to block index"); if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) return error("LoadBlockIndex(): genesis block not accepted"); if (!ActivateBestChain(state, &block)) @@ -4096,7 +4612,7 @@ bool InitBlockIndex() { return error("LoadBlockIndex(): failed to initialize block database: %s", e.what()); } } - + return true; } @@ -4108,7 +4624,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) // Map of disk positions for blocks with unknown parent (only used for reindex) static std::multimap mapBlocksUnknownParent; int64_t nStart = GetTimeMillis(); - + int nLoaded = 0; try { // This takes over fileIn and calls fclose() on it in the CBufferedFile destructor @@ -4116,7 +4632,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) uint64_t nRewind = blkdat.GetPos(); while (!blkdat.eof()) { boost::this_thread::interruption_point(); - + blkdat.SetPos(nRewind); nRewind++; // start one byte further next time, in case of failure blkdat.SetLimit(); // remove former limit @@ -4147,17 +4663,17 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) CBlock block; blkdat >> block; nRewind = blkdat.GetPos(); - + // 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(), - block.hashPrevBlock.ToString()); + block.hashPrevBlock.ToString()); if (dbp) mapBlocksUnknownParent.insert(std::make_pair(block.hashPrevBlock, *dbp)); continue; } - + // process in case the block isn't known yet if (mapBlockIndex.count(hash) == 0 || (mapBlockIndex[hash]->nStatus & BLOCK_HAVE_DATA) == 0) { CValidationState state; @@ -4168,7 +4684,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) } else if (hash != chainparams.GetConsensus().hashGenesisBlock && mapBlockIndex[hash]->nHeight % 1000 == 0) { LogPrintf("Block Import: already had block %s at height %d\n", hash.ToString(), mapBlockIndex[hash]->nHeight); } - + // Recursively process earlier encountered successors of this block deque queue; queue.push_back(hash); @@ -4181,7 +4697,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) if (ReadBlockFromDisk(mapBlockIndex[hash]!=0?mapBlockIndex[hash]->nHeight:0,block, it->second)) { LogPrintf("%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(), - head.ToString()); + head.ToString()); CValidationState dummy; if (ProcessNewBlock(0,dummy, NULL, &block, true, &it->second)) { @@ -4211,9 +4727,9 @@ void static CheckBlockIndex() if (!fCheckBlockIndex) { return; } - + LOCK(cs_main); - + // During a reindex, we read the genesis block and call CheckBlockIndex before ActivateBestChain, // so we have the genesis block in mapBlockIndex but no active chain. (A few of the tests when // iterating the block tree require that chainActive has been initialized.) @@ -4221,20 +4737,20 @@ void static CheckBlockIndex() assert(mapBlockIndex.size() <= 1); return; } - + // Build forward-pointing map of the entire block tree. std::multimap forward; for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) { forward.insert(std::make_pair(it->second->pprev, it->second)); } - + assert(forward.size() == mapBlockIndex.size()); - + std::pair::iterator,std::multimap::iterator> rangeGenesis = forward.equal_range(NULL); CBlockIndex *pindex = rangeGenesis.first->second; rangeGenesis.first++; assert(rangeGenesis.first == rangeGenesis.second); // There is only one index entry with parent NULL. - + // Iterate over the entire block tree, using depth-first search. // Along the way, remember whether there are blocks on the path from genesis // block being explored which are the first to have certain properties. @@ -4256,7 +4772,7 @@ void static CheckBlockIndex() if (pindex->pprev != NULL && pindexFirstNotTransactionsValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TRANSACTIONS) pindexFirstNotTransactionsValid = pindex; if (pindex->pprev != NULL && pindexFirstNotChainValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) pindexFirstNotChainValid = pindex; if (pindex->pprev != NULL && pindexFirstNotScriptsValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) pindexFirstNotScriptsValid = pindex; - + // Begin: actual consistency checks. if (pindex->pprev == NULL) { // Genesis block checks. @@ -4342,7 +4858,7 @@ void static CheckBlockIndex() } // assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); // Perhaps too slow // End: actual consistency checks. - + // Try descending into the first subnode. std::pair::iterator,std::multimap::iterator> range = forward.equal_range(pindex); if (range.first != range.second) { @@ -4385,7 +4901,7 @@ void static CheckBlockIndex() } } } - + // Check that we actually traversed the entire map. assert(nNodes == forward.size()); } @@ -4400,20 +4916,20 @@ std::string GetWarnings(const std::string& strFor) int nPriority = 0; string strStatusBar; string strRPC; - + 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", false)) strStatusBar = strRPC = "testsafemode enabled"; - + // Misc warnings like out of disk space and clock is wrong if (strMiscWarning != "") { nPriority = 1000; strStatusBar = strMiscWarning; } - + if (fLargeWorkForkFound) { nPriority = 2000; @@ -4424,7 +4940,7 @@ std::string GetWarnings(const std::string& strFor) 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); @@ -4441,7 +4957,7 @@ std::string GetWarnings(const std::string& strFor) } } } - + if (strFor == "statusbar") return strStatusBar; else if (strFor == "rpc") @@ -4467,7 +4983,7 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { switch (inv.type) { - case MSG_TX: + case MSG_TX: { assert(recentRejects); if (chainActive.Tip()->GetBlockHash() != hashRecentRejectsChainTip) @@ -4479,14 +4995,14 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) hashRecentRejectsChainTip = chainActive.Tip()->GetBlockHash(); recentRejects->reset(); } - + return recentRejects->contains(inv.hash) || - mempool.exists(inv.hash) || - mapOrphanTransactions.count(inv.hash) || - pcoinsTip->HaveCoins(inv.hash); + mempool.exists(inv.hash) || + mapOrphanTransactions.count(inv.hash) || + pcoinsTip->HaveCoins(inv.hash); } - case MSG_BLOCK: - return mapBlockIndex.count(inv.hash); + case MSG_BLOCK: + return mapBlockIndex.count(inv.hash); } // Don't know what it is, just say we already got one return true; @@ -4495,21 +5011,21 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) void static ProcessGetData(CNode* pfrom) { std::deque::iterator it = pfrom->vRecvGetData.begin(); - + vector vNotFound; - + LOCK(cs_main); - + while (it != pfrom->vRecvGetData.end()) { // Don't bother if send buffer is too full to respond anyway if (pfrom->nSendSize >= SendBufferSize()) break; - + const CInv &inv = *it; { boost::this_thread::interruption_point(); it++; - + if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) { bool send = false; @@ -4524,8 +5040,8 @@ void static ProcessGetData(CNode* pfrom) // chain if they are valid, and no more than a month older (both in time, and in // best equivalent proof of work) than the best header chain we know about. send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != NULL) && - (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() < nOneMonth) && - (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, Params().GetConsensus()) < nOneMonth); + (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() < nOneMonth) && + (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, Params().GetConsensus()) < nOneMonth); if (!send) { LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId()); } @@ -4613,17 +5129,17 @@ void static ProcessGetData(CNode* pfrom) vNotFound.push_back(inv); } } - + // Track requests for our stuff. GetMainSignals().Inventory(inv.hash); - + if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) break; } } - + pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it); - + if (!vNotFound.empty()) { // Let the peer know that we didn't find what it asked for, so it doesn't // have to wait around forever. Currently only SPV clients actually care @@ -4646,10 +5162,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n"); return true; } - - - - + + + + if (strCommand == "version") { // Each connection can only send one version message @@ -4659,7 +5175,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 1); return false; } - + int64_t nTime; CAddress addrMe; CAddress addrFrom; @@ -4674,7 +5190,20 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fDisconnect = true; return false; } - + + // When Overwinter is active, reject incoming connections from non-Overwinter nodes + const Consensus::Params& params = Params().GetConsensus(); + if (NetworkUpgradeActive(GetHeight(), params, Consensus::UPGRADE_OVERWINTER) + && pfrom->nVersion < params.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion) + { + LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion); + pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, + strprintf("Version must be %d or greater", + params.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion)); + pfrom->fDisconnect = true; + return false; + } + if (pfrom->nVersion == 10300) pfrom->nVersion = 300; if (!vRecv.empty()) @@ -4689,7 +5218,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, 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) { @@ -4697,26 +5226,26 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fDisconnect = true; return true; } - + pfrom->addrLocal = addrMe; if (pfrom->fInbound && addrMe.IsRoutable()) { SeenLocal(addrMe); } - + // Be shy and don't send version until we hear if (pfrom->fInbound) pfrom->PushVersion(); - + pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); - + // Potentially mark this peer as a preferred download peer. UpdatePreferredDownload(pfrom, State(pfrom->GetId())); - + // Change version pfrom->PushMessage("verack"); pfrom->ssSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); - + if (!pfrom->fInbound) { // Advertise our address @@ -4733,7 +5262,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushAddress(addr); } } - + // Get recent addresses if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || addrman.size() < 1000) { @@ -4748,56 +5277,72 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, addrman.Good(addrFrom); } } - + // Relay alerts { LOCK(cs_mapAlerts); BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) - item.second.RelayTo(pfrom); + item.second.RelayTo(pfrom); } - + pfrom->fSuccessfullyConnected = true; - + string remoteAddr; if (fLogIPs) remoteAddr = ", peeraddr=" + pfrom->addr.ToString(); - + LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, peer=%d%s\n", pfrom->cleanSubVer, pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString(), pfrom->id, remoteAddr); - + int64_t nTimeOffset = nTime - GetTime(); pfrom->nTimeOffset = nTimeOffset; AddTimeData(pfrom->addr, nTimeOffset); } - - + + else if (pfrom->nVersion == 0) { // Must have a version message before anything else Misbehaving(pfrom->GetId(), 1); return false; } - - + + else if (strCommand == "verack") { pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); - + // Mark this node as currently connected, so we update its timestamp later. if (pfrom->fNetworkNode) { LOCK(cs_main); State(pfrom->GetId())->fCurrentlyConnected = true; } } - - + + + // Disconnect existing peer connection when: + // 1. The version message has been received + // 2. Overwinter is active + // 3. Peer version is pre-Overwinter + else if (NetworkUpgradeActive(GetHeight(), chainparams.GetConsensus(), Consensus::UPGRADE_OVERWINTER) + && (pfrom->nVersion < chainparams.GetConsensus().vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion)) + { + LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion); + pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, + strprintf("Version must be %d or greater", + chainparams.GetConsensus().vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion)); + pfrom->fDisconnect = true; + return false; + } + + else if (strCommand == "addr") { vector vAddr; vRecv >> vAddr; - + // Don't want addr from older versions unless seeding if (pfrom->nVersion < CADDR_TIME_VERSION && addrman.size() > 1000) return true; @@ -4806,7 +5351,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 20); return error("message addr size() = %u", vAddr.size()); } - + // Store the new addresses vector vAddrOk; int64_t nNow = GetAdjustedTime(); @@ -4814,7 +5359,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, BOOST_FOREACH(CAddress& addr, vAddr) { boost::this_thread::interruption_point(); - + if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) addr.nTime = nNow - 5 * 24 * 60 * 60; pfrom->AddAddressKnown(addr); @@ -4858,8 +5403,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pfrom->fOneShot) pfrom->fDisconnect = true; } - - + + else if (strCommand == "inv") { vector vInv; @@ -4869,24 +5414,24 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 20); return error("message inv size() = %u", vInv.size()); } - + LOCK(cs_main); - + std::vector vToFetch; - + for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { const CInv &inv = vInv[nInv]; - + boost::this_thread::interruption_point(); pfrom->AddInventoryKnown(inv); - + 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)) { @@ -4910,21 +5455,21 @@ 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); } } - + // Track requests for our stuff GetMainSignals().Inventory(inv.hash); - + if (pfrom->nSendSize > (SendBufferSize() * 2)) { Misbehaving(pfrom->GetId(), 50); return error("send buffer size() = %u", pfrom->nSendSize); } } - + if (!vToFetch.empty()) pfrom->PushMessage("getdata", vToFetch); } - - + + else if (strCommand == "getdata") { vector vInv; @@ -4934,29 +5479,29 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 20); 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); - + if ((fDebug && vInv.size() > 0) || (vInv.size() == 1)) LogPrint("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); } - - + + else if (strCommand == "getblocks") { CBlockLocator locator; uint256 hashStop; vRecv >> locator >> hashStop; - + LOCK(cs_main); - + // Find the last block the caller has in the main chain CBlockIndex* pindex = FindForkInGlobalIndex(chainActive, locator); - + // Send the rest of the chain if (pindex) pindex = chainActive.Next(pindex); @@ -4980,19 +5525,19 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } } - - + + else if (strCommand == "getheaders") { CBlockLocator locator; uint256 hashStop; vRecv >> locator >> hashStop; - + LOCK(cs_main); - + if (IsInitialBlockDownload()) return true; - + CBlockIndex* pindex = NULL; if (locator.IsNull()) { @@ -5009,7 +5554,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pindex) pindex = chainActive.Next(pindex); } - + // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end vector vHeaders; int nLimit = MAX_HEADERS_RESULTS; @@ -5032,37 +5577,37 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, fprintf(stderr,"you can ignore redundant getheaders from peer.%d %d prev.%d\n",(int32_t)pfrom->id,(int32_t)(pindex ? pindex->nHeight : -1),pfrom->lasthdrsreq); } } - - + + else if (strCommand == "tx") { vector vWorkQueue; vector vEraseQueue; CTransaction tx; vRecv >> tx; - + CInv inv(MSG_TX, tx.GetHash()); pfrom->AddInventoryKnown(inv); - + LOCK(cs_main); - + bool fMissingInputs = false; CValidationState state; - + pfrom->setAskFor.erase(inv.hash); mapAlreadyAskedFor.erase(inv); - + if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) { mempool.check(pcoinsTip); RelayTransaction(tx); vWorkQueue.push_back(inv.hash); - + LogPrint("mempool", "AcceptToMemoryPool: peer=%d %s: accepted %s (poolsz %u)\n", - pfrom->id, pfrom->cleanSubVer, - tx.GetHash().ToString(), - mempool.mapTx.size()); - + pfrom->id, pfrom->cleanSubVer, + tx.GetHash().ToString(), + mempool.mapTx.size()); + // Recursively process any orphan transactions that depended on this one set setMisbehaving; for (unsigned int i = 0; i < vWorkQueue.size(); i++) @@ -5082,8 +5627,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get // anyone relaying LegitTxX banned) CValidationState stateDummy; - - + + if (setMisbehaving.count(fromPeer)) continue; if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) @@ -5113,15 +5658,15 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, mempool.check(pcoinsTip); } } - + BOOST_FOREACH(uint256 hash, vEraseQueue) - EraseOrphanTx(hash); + EraseOrphanTx(hash); } // TODO: currently, prohibit joinsplits from entering mapOrphans else if (fMissingInputs && tx.vjoinsplit.size() == 0) { AddOrphanTx(tx, pfrom->GetId()); - + // 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); @@ -5130,7 +5675,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } else { assert(recentRejects); recentRejects->insert(tx.GetHash()); - + if (pfrom->fWhitelisted) { // Always relay transactions received from whitelisted peers, even // if they were already in the mempool or rejected from it due @@ -5146,7 +5691,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, RelayTransaction(tx); } else { LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s (code %d))\n", - tx.GetHash().ToString(), pfrom->id, state.GetRejectReason(), state.GetRejectCode()); + tx.GetHash().ToString(), pfrom->id, state.GetRejectReason(), state.GetRejectCode()); } } } @@ -5154,20 +5699,20 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (state.IsInvalid(nDoS)) { 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->id, pfrom->cleanSubVer, + state.GetRejectReason()); pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); if (nDoS > 0) Misbehaving(pfrom->GetId(), nDoS); } } - - + + else if (strCommand == "headers" && !fImporting && !fReindex) // Ignore headers received while importing { std::vector headers; - + // 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) { @@ -5179,14 +5724,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vRecv >> headers[n]; ReadCompactSize(vRecv); // ignore tx count; assume it is 0. } - + LOCK(cs_main); - + if (nCount == 0) { // Nothing interesting. Stop asking this peers for more headers. return true; } - + CBlockIndex *pindexLast = NULL; BOOST_FOREACH(const CBlockHeader& header, headers) { CValidationState state; @@ -5196,17 +5741,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } if (!AcceptBlockHeader(header, state, &pindexLast)) { int nDoS; - if (state.IsInvalid(nDoS)) { + if (state.IsInvalid(nDoS)) + { if (nDoS > 0) Misbehaving(pfrom->GetId(), nDoS/nDoS); return error("invalid header received"); } } } - + if (pindexLast) UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash()); - + if (nCount == MAX_HEADERS_RESULTS && pindexLast) { // 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 @@ -5218,20 +5764,20 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexLast), uint256()); } } - + CheckBlockIndex(); } - + else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing { CBlock block; vRecv >> block; - + CInv inv(MSG_BLOCK, block.GetHash()); LogPrint("net", "received block %s peer=%d\n", inv.hash.ToString(), pfrom->id); - + pfrom->AddInventoryKnown(inv); - + CValidationState state; // Process all blocks from whitelisted peers, even if not requested, // unless we're still syncing with the network. @@ -5248,10 +5794,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), nDoS); } } - + } - - + + // This asymmetric behavior for inbound and outbound connections was introduced // to prevent a fingerprinting attack: an attacker can send specific fake addresses // to users' AddrMan and later request them by sending getaddr messages. @@ -5266,18 +5812,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } pfrom->fSentAddr = true; - + pfrom->vAddrToSend.clear(); vector vAddr = addrman.GetAddr(); BOOST_FOREACH(const CAddress &addr, vAddr) - pfrom->PushAddress(addr); + pfrom->PushAddress(addr); } - - + + else if (strCommand == "mempool") { LOCK2(cs_main, pfrom->cs_filter); - + std::vector vtxid; mempool.queryHashes(vtxid); vector vInv; @@ -5287,7 +5833,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, bool fInMemPool = mempool.lookup(hash, tx); if (!fInMemPool) continue; // another thread removed since queryHashes, maybe... if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(tx)) || - (!pfrom->pfilter)) + (!pfrom->pfilter)) vInv.push_back(inv); if (vInv.size() == MAX_INV_SZ) { pfrom->PushMessage("inv", vInv); @@ -5297,8 +5843,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (vInv.size() > 0) pfrom->PushMessage("inv", vInv); } - - + + else if (strCommand == "ping") { if (pfrom->nVersion > BIP0031_VERSION) @@ -5319,8 +5865,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushMessage("pong", nonce); } } - - + + else if (strCommand == "pong") { int64_t pingUsecEnd = nTimeReceived; @@ -5328,10 +5874,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, size_t nAvail = vRecv.in_avail(); bool bPingFinished = false; std::string sProblem; - + if (nAvail >= sizeof(nonce)) { vRecv >> nonce; - + // Only process pong message if there is an outstanding ping (old ping without nonce should never pong) if (pfrom->nPingNonceSent != 0) { if (nonce == pfrom->nPingNonceSent) { @@ -5363,27 +5909,27 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, bPingFinished = true; sProblem = "Short payload"; } - + if (!(sProblem.empty())) { LogPrint("net", "pong peer=%d %s: %s, %x expected, %x received, %u bytes\n", - pfrom->id, - pfrom->cleanSubVer, - sProblem, - pfrom->nPingNonceSent, - nonce, - nAvail); + pfrom->id, + pfrom->cleanSubVer, + sProblem, + pfrom->nPingNonceSent, + nonce, + nAvail); } if (bPingFinished) { pfrom->nPingNonceSent = 0; } } - - + + else if (fAlerts && strCommand == "alert") { CAlert alert; vRecv >> alert; - + uint256 alertHash = alert.GetHash(); if (pfrom->setKnown.count(alertHash) == 0) { @@ -5394,7 +5940,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) - alert.RelayTo(pnode); + alert.RelayTo(pnode); } } else { @@ -5408,13 +5954,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } } - - + + else if (strCommand == "filterload") { CBloomFilter filter; vRecv >> filter; - + if (!filter.IsWithinSizeConstraints()) // There is no excuse for sending a too-large filter Misbehaving(pfrom->GetId(), 100); @@ -5427,13 +5973,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } pfrom->fRelayTxes = true; } - - + + else if (strCommand == "filteradd") { vector vData; vRecv >> vData; - + // 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) @@ -5447,8 +5993,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 100); } } - - + + else if (strCommand == "filterclear") { LOCK(pfrom->cs_filter); @@ -5456,18 +6002,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->pfilter = new CBloomFilter(); pfrom->fRelayTxes = true; } - - + + else if (strCommand == "reject") { if (fDebug) { try { string strMsg; unsigned char ccode; string strReason; vRecv >> LIMITED_STRING(strMsg, CMessageHeader::COMMAND_SIZE) >> ccode >> LIMITED_STRING(strReason, MAX_REJECT_MESSAGE_LENGTH); - + ostringstream ss; ss << strMsg << " code " << itostr(ccode) << ": " << strReason; - + if (strMsg == "block" || strMsg == "tx") { uint256 hash; @@ -5485,14 +6031,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // We do not care about the NOTFOUND message, but logging an Unknown Command // message would be undesirable as we transmit it ourselves. } - + else { // Ignore unknown commands for extensibility LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id); } - - - + + + return true; } @@ -5501,7 +6047,7 @@ bool ProcessMessages(CNode* pfrom) { //if (fDebug) // LogPrintf("%s(%u messages)\n", __func__, pfrom->vRecvMsg.size()); - + // // Message format // (4) message start @@ -5511,41 +6057,41 @@ bool ProcessMessages(CNode* pfrom) // (x) data // bool fOk = true; - + if (!pfrom->vRecvGetData.empty()) ProcessGetData(pfrom); - + // this maintains the order of responses if (!pfrom->vRecvGetData.empty()) return fOk; - + std::deque::iterator it = pfrom->vRecvMsg.begin(); while (!pfrom->fDisconnect && it != pfrom->vRecvMsg.end()) { // Don't bother if send buffer is too full to respond anyway if (pfrom->nSendSize >= SendBufferSize()) break; - + // get next message CNetMessage& msg = *it; - + //if (fDebug) // LogPrintf("%s(message %u msgsz, %u bytes, complete:%s)\n", __func__, // msg.hdr.nMessageSize, msg.vRecv.size(), // msg.complete() ? "Y" : "N"); - + // end, if an incomplete message is found if (!msg.complete()) break; - + // at this point, any failure means we can delete the current message it++; - + // Scan for message start if (memcmp(msg.hdr.pchMessageStart, Params().MessageStart(), MESSAGE_START_SIZE) != 0) { LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->id); fOk = false; break; } - + // Read header CMessageHeader& hdr = msg.hdr; if (!hdr.IsValid(Params().MessageStart())) @@ -5554,10 +6100,10 @@ bool ProcessMessages(CNode* pfrom) continue; } string strCommand = hdr.GetCommand(); - + // Message size unsigned int nMessageSize = hdr.nMessageSize; - + // Checksum CDataStream& vRecv = msg.vRecv; uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); @@ -5565,10 +6111,10 @@ bool ProcessMessages(CNode* pfrom) if (nChecksum != hdr.nChecksum) { LogPrintf("%s(%s, %u bytes): CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", __func__, - SanitizeString(strCommand), nMessageSize, nChecksum, hdr.nChecksum); + SanitizeString(strCommand), nMessageSize, nChecksum, hdr.nChecksum); continue; } - + // Process message bool fRet = false; try @@ -5602,17 +6148,17 @@ bool ProcessMessages(CNode* pfrom) } catch (...) { PrintExceptionContinue(NULL, "ProcessMessages()"); } - + if (!fRet) LogPrintf("%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->id); - + break; } - + // In case the connection got shut down, its receive buffer was wiped if (!pfrom->fDisconnect) pfrom->vRecvMsg.erase(pfrom->vRecvMsg.begin(), it); - + return fOk; } @@ -5624,7 +6170,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // Don't send anything until we get its version message if (pto->nVersion == 0) return true; - + // // Message: ping // @@ -5653,11 +6199,11 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->PushMessage("ping"); } } - + TRY_LOCK(cs_main, lockMain); // Acquire cs_main for IsInitialBlockDownload() and CNodeState() if (!lockMain) return true; - + // Address refresh broadcast static int64_t nLastRebroadcast; if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60)) @@ -5668,14 +6214,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // Periodically clear addrKnown to allow refresh broadcasts if (nLastRebroadcast) pnode->addrKnown.reset(); - + // Rebroadcast our address AdvertizeLocal(pnode); } if (!vNodes.empty()) nLastRebroadcast = GetTime(); } - + // // Message: addr // @@ -5701,7 +6247,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) if (!vAddr.empty()) pto->PushMessage("addr", vAddr); } - + CNodeState &state = *State(pto->GetId()); if (state.fShouldBan) { if (pto->fWhitelisted) @@ -5717,11 +6263,11 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } state.fShouldBan = false; } - + BOOST_FOREACH(const CBlockReject& reject, state.rejects) - pto->PushMessage("reject", (string)"block", reject.chRejectCode, reject.strRejectReason, reject.hashBlock); + pto->PushMessage("reject", (string)"block", reject.chRejectCode, reject.strRejectReason, reject.hashBlock); state.rejects.clear(); - + // Start block sync if (pindexBestHeader == NULL) pindexBestHeader = chainActive.Tip(); @@ -5736,7 +6282,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->PushMessage("getheaders", chainActive.GetLocator(pindexStart), uint256()); } } - + // Resend wallet transactions that haven't gotten in a block yet // Except during reindex, importing and IBD, when old wallet // transactions become unconfirmed and spams other nodes. @@ -5744,7 +6290,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) { GetMainSignals().Broadcast(nTimeBestReceived); } - + // // Message: inventory // @@ -5758,7 +6304,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) { if (pto->setInventoryKnown.count(inv)) continue; - + // trickle out tx inv to protect privacy if (inv.type == MSG_TX && !fSendTrickle) { @@ -5769,14 +6315,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle) 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); continue; } } - + // returns true if wasn't already contained in the set if (pto->setInventoryKnown.insert(inv).second) { @@ -5792,7 +6338,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } if (!vInv.empty()) pto->PushMessage("inv", vInv); - + // Detect whether we're stalling int64_t nNow = GetTimeMicros(); if (!pto->fDisconnect && state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) { @@ -5824,7 +6370,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->fDisconnect = true; } } - + // // Message: getdata (blocks) // @@ -5837,7 +6383,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) 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(), - pindex->nHeight, pto->id); + pindex->nHeight, pto->id); } if (state.nBlocksInFlight == 0 && staller != -1) { if (State(staller)->nStallingSince == 0) { @@ -5846,7 +6392,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } } } - + // // Message: getdata (non-blocks) // @@ -5871,14 +6417,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } if (!vGetData.empty()) pto->PushMessage("getdata", vGetData); - + } return true; } - std::string CBlockFileInfo::ToString() const { - return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast)); - } +std::string CBlockFileInfo::ToString() const { + return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast)); +} @@ -5892,7 +6438,7 @@ public: for (; it1 != mapBlockIndex.end(); it1++) delete (*it1).second; mapBlockIndex.clear(); - + // orphan transactions mapOrphanTransactions.clear(); mapOrphanTransactionsByPrev.clear(); @@ -5901,6 +6447,24 @@ public: extern "C" const char* getDataDir() { - return GetDataDir().string().c_str(); + return GetDataDir().string().c_str(); } + +// Set default values of new CMutableTransaction based on consensus rules at given height. +CMutableTransaction CreateNewContextualCMutableTransaction(const Consensus::Params& consensusParams, int nHeight) +{ + CMutableTransaction mtx; + + bool isOverwintered = NetworkUpgradeActive(nHeight, consensusParams, Consensus::UPGRADE_OVERWINTER); + if (isOverwintered) { + mtx.fOverwintered = true; + mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID; + mtx.nVersion = 3; + // Expiry height is not set. Only fields required for a parser to treat as a valid Overwinter V3 tx. + + // TODO: In future, when moving from Overwinter to Sapling, it will be useful + // to set the expiry height to: min(activation_height - 1, default_expiry_height) + } + return mtx; +} diff --git a/src/main.h b/src/main.h index bcdd04a5e..cb10e0168 100644 --- a/src/main.h +++ b/src/main.h @@ -15,11 +15,12 @@ #include "chainparams.h" #include "coins.h" #include "consensus/consensus.h" +#include "consensus/upgrades.h" #include "net.h" #include "primitives/block.h" #include "primitives/transaction.h" #include "script/script.h" -#include "script/sigcache.h" +#include "script/serverchecker.h" #include "script/standard.h" #include "sync.h" #include "tinyformat.h" @@ -44,9 +45,11 @@ class CInv; class CScriptCheck; class CValidationInterface; class CValidationState; +class PrecomputedTransactionData; struct CNodeStateStats; #define DEFAULT_MEMPOOL_EXPIRY 1 +#define _COINBASE_MATURITY 100 /** 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; @@ -57,6 +60,8 @@ static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = DEFAULT_BLOCK_MAX_SIZE / static const bool DEFAULT_ALERTS = true; /** Minimum alert priority for enabling safe mode. */ static const int ALERT_PRIORITY_SAFE_MODE = 4000; +/** Maximum reorg length we will accept before we shut down and alert the user. */ +static const unsigned int MAX_REORG_LENGTH = _COINBASE_MATURITY - 1; /** 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 */ @@ -65,6 +70,8 @@ static const unsigned int MAX_STANDARD_TX_SIGOPS = MAX_BLOCK_SIGOPS/5; static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 100; /** Default for -maxorphantx, maximum number of orphan transactions kept in memory */ static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100; +/** Default for -txexpirydelta, in number of blocks */ +static const unsigned int DEFAULT_TX_EXPIRY_DELTA = 20; /** The maximum size of a blk?????.dat file (since 0.8) */ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB /** The pre-allocation chunk size for blk?????.dat files (since 0.8) */ @@ -107,6 +114,7 @@ struct BlockHasher size_t operator()(const uint256& hash) const { return hash.GetCheapHash(); } }; +extern unsigned int expiryDelta; extern CScript COINBASE_FLAGS; extern CCriticalSection cs_main; extern CTxMemPool mempool; @@ -305,7 +313,7 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF * @param[in] mapInputs Map of previous transactions that have outputs we're spending * @return True if all inputs (scriptSigs) use only standard transaction forms */ -bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs); +bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, uint32_t consensusBranchId); /** * Count ECDSA signature operations the old-fashioned (pre-0.6) way @@ -330,11 +338,17 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& ma * instead of being performed inline. */ bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &view, bool fScriptChecks, - unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams, + unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, + const Consensus::Params& consensusParams, uint32_t consensusBranchId, std::vector *pvChecks = NULL); +/** Check a transaction contextually against a set of consensus rules */ +bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, int nHeight, int dosLevel); + /** Apply the effects of this transaction on the UTXO set represented by view */ -void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, int nHeight); +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); @@ -343,7 +357,18 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio /** Check for standard transaction types * @return True if all outputs (scriptPubKeys) use only standard transaction forms */ -bool IsStandardTx(const CTransaction& tx, std::string& reason); +bool IsStandardTx(const CTransaction& tx, std::string& reason, int nHeight = 0); + +namespace Consensus { + +/** + * Check whether all inputs of this transaction are valid (no double spends and amounts) + * This does not modify the UTXO set. This does not check scripts and sigs. + * Preconditions: tx.IsCoinBase() is false. + */ +bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams); + +} // namespace Consensus /** * Check if transaction is final and can be included in a block with the @@ -351,6 +376,12 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason); */ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime); +/** + * Check if transaction is expired and can be included in a block with the + * specified height. Consensus critical. + */ +bool IsExpiredTx(const CTransaction &tx, int nBlockHeight); + /** * Check if transaction will be final in the next block to be created. * @@ -368,27 +399,33 @@ class CScriptCheck { private: CScript scriptPubKey; + CAmount amount; const CTransaction *ptxTo; unsigned int nIn; unsigned int nFlags; bool cacheStore; + uint32_t consensusBranchId; ScriptError error; + PrecomputedTransactionData *txdata; public: - CScriptCheck(): ptxTo(0), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {} - CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn) : - scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey), - ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR) { } + CScriptCheck(): amount(0), ptxTo(0), nIn(0), nFlags(0), cacheStore(false), consensusBranchId(0), error(SCRIPT_ERR_UNKNOWN_ERROR) {} + CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, uint32_t consensusBranchIdIn, PrecomputedTransactionData* txdataIn) : + scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey), amount(txFromIn.vout[txToIn.vin[nInIn].prevout.n].nValue), + ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), consensusBranchId(consensusBranchIdIn), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata(txdataIn) { } bool operator()(); void swap(CScriptCheck &check) { scriptPubKey.swap(check.scriptPubKey); std::swap(ptxTo, check.ptxTo); + std::swap(amount, check.amount); std::swap(nIn, check.nIn); std::swap(nFlags, check.nFlags); std::swap(cacheStore, check.cacheStore); + std::swap(consensusBranchId, check.consensusBranchId); std::swap(error, check.error); + std::swap(txdata, check.txdata); } ScriptError GetScriptError() const { return error; } @@ -410,7 +447,7 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex); bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool* pfClean = NULL); /** Apply the effects of this block (with given index) on the UTXO set represented by coins */ -bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck = false); +bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck = false,bool fCheckPOW = false); /** Context-independent validity checks */ bool CheckBlockHeader(int32_t height,CBlockIndex *pindex,const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true); @@ -437,6 +474,13 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc +/** + * When there are blocks in the active chain with missing data (e.g. if the + * activation height and branch ID of a particular upgrade have been altered), + * rewind the chainstate and remove them from the block index. + */ +bool RewindBlockIndex(const CChainParams& params); + class CBlockFileInfo { public: @@ -508,7 +552,7 @@ bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex); /** Remove invalidity status from a block and its descendants. */ bool ReconsiderBlock(CValidationState& state, CBlockIndex *pindex); -/** The currently-connected chain of blocks. */ +/** The currently-connected chain of blocks (protected by cs_main). */ extern CChain chainActive; /** Global variable that points to the active CCoinsView (protected by cs_main) */ @@ -524,8 +568,7 @@ extern CBlockTreeDB *pblocktree; */ int GetSpendHeight(const CCoinsViewCache& inputs); -namespace Consensus { -bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams); -} +/** Return a CMutableTransaction with contextual default values based on set of consensus rules at height */ +CMutableTransaction CreateNewContextualCMutableTransaction(const Consensus::Params& consensusParams, int nHeight); #endif // BITCOIN_MAIN_H diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp index f8e877df2..a368b31ad 100644 --- a/src/merkleblock.cpp +++ b/src/merkleblock.cpp @@ -62,12 +62,12 @@ CMerkleBlock::CMerkleBlock(const CBlock& block, const std::set& txids) uint256 CPartialMerkleTree::CalcHash(int height, unsigned int pos, const std::vector &vTxid) { if (height == 0) { - // hash at height 0 is the txids themself + // hash at height 0 is the txid itself return vTxid[pos]; } else { // calculate left hash uint256 left = CalcHash(height-1, pos*2, vTxid), right; - // calculate right hash if not beyond the end of the array - copy left hash otherwise1 + // calculate right hash if not beyond the end of the array - copy left hash otherwise if (pos*2+1 < CalcTreeWidth(height-1)) right = CalcHash(height-1, pos*2+1, vTxid); else diff --git a/src/merkleblock.h b/src/merkleblock.h index 904c22abc..0181723af 100644 --- a/src/merkleblock.h +++ b/src/merkleblock.h @@ -67,7 +67,7 @@ protected: return (nTransactions+(1 << height)-1) >> height; } - /** calculate the hash of a node in the merkle tree (at leaf level: the txid's themselves) */ + /** calculate the hash of a node in the merkle tree (at leaf level: the txid itself) */ uint256 CalcHash(int height, unsigned int pos, const std::vector &vTxid); /** recursive function that traverses tree nodes, storing the data as bits and hashes */ diff --git a/src/metrics.cpp b/src/metrics.cpp index b45b5dc6b..6c8f80fe5 100644 --- a/src/metrics.cpp +++ b/src/metrics.cpp @@ -5,11 +5,13 @@ #include "metrics.h" #include "chainparams.h" +#include "checkpoints.h" #include "main.h" #include "ui_interface.h" #include "util.h" #include "utiltime.h" #include "utilmoneystr.h" +#include "utilstrencodings.h" #include #include @@ -50,6 +52,12 @@ bool AtomicTimer::running() return threads > 0; } +uint64_t AtomicTimer::threadCount() +{ + std::unique_lock lock(mtx); + return threads; +} + double AtomicTimer::rate(const AtomicCounter& count) { std::unique_lock lock(mtx); @@ -101,6 +109,36 @@ double GetLocalSolPS() return miningTimer.rate(solutionTargetChecks); } +int EstimateNetHeightInner(int height, int64_t tipmediantime, + int heightLastCheckpoint, int64_t timeLastCheckpoint, + int64_t genesisTime, int64_t targetSpacing) +{ + // We average the target spacing with the observed spacing to the last + // checkpoint (either from below or above depending on the current height), + // and use that to estimate the current network height. + int medianHeight = height > CBlockIndex::nMedianTimeSpan ? + height - (1 + ((CBlockIndex::nMedianTimeSpan - 1) / 2)) : + height / 2; + double checkpointSpacing = medianHeight > heightLastCheckpoint ? + (double (tipmediantime - timeLastCheckpoint)) / (medianHeight - heightLastCheckpoint) : + (double (timeLastCheckpoint - genesisTime)) / heightLastCheckpoint; + double averageSpacing = (targetSpacing + checkpointSpacing) / 2; + int netheight = medianHeight + ((GetTime() - tipmediantime) / averageSpacing); + // Round to nearest ten to reduce noise + return ((netheight + 5) / 10) * 10; +} + +int EstimateNetHeight(int height, int64_t tipmediantime, CChainParams chainParams) +{ + auto checkpointData = chainParams.Checkpoints(); + return EstimateNetHeightInner( + height, tipmediantime, + Checkpoints::GetTotalBlocksEstimate(checkpointData), + checkpointData.nTimeLastCheckpoint, + chainParams.GenesisBlock().nTime, + chainParams.GetConsensus().nPowTargetSpacing); +} + void TriggerRefresh() { *nNextRefresh = GetTime(); @@ -167,17 +205,25 @@ int printStats(bool mining) int lines = 4; int height; + int64_t tipmediantime; size_t connections; int64_t netsolps; { LOCK2(cs_main, cs_vNodes); height = chainActive.Height(); + tipmediantime = chainActive.Tip()->GetMedianTimePast(); connections = vNodes.size(); netsolps = GetNetworkHashPS(120, -1); } auto localsolps = GetLocalSolPS(); - std::cout << " " << _("Block height") << " | " << height << std::endl; + if (IsInitialBlockDownload()) { + int netheight = EstimateNetHeight(height, tipmediantime, Params()); + int downloadPercent = height * 100 / netheight; + std::cout << " " << _("Downloading blocks") << " | " << height << " / ~" << netheight << " (" << downloadPercent << "%)" << std::endl; + } else { + std::cout << " " << _("Block height") << " | " << height << std::endl; + } std::cout << " " << _("Connections") << " | " << connections << std::endl; std::cout << " " << _("Network solution rate") << " | " << netsolps << " Sol/s" << std::endl; if (mining && miningTimer.running()) { @@ -196,15 +242,8 @@ int printMiningStatus(bool mining) int lines = 1; if (mining) { - int nThreads = GetArg("-genproclimit", 1); - if (nThreads < 0) { - // In regtest threads defaults to 1 - if (Params().DefaultMinerThreads()) - nThreads = Params().DefaultMinerThreads(); - else - nThreads = boost::thread::hardware_concurrency(); - } - if (miningTimer.running()) { + auto nThreads = miningTimer.threadCount(); + if (nThreads > 0) { std::cout << strprintf(_("You are mining with the %s solver on %d threads."), GetArg("-equihashsolver", "default"), nThreads) << std::endl; } else { @@ -339,20 +378,19 @@ int printMessageBox(size_t cols) int lines = 2 + u->size(); std::cout << _("Messages:") << std::endl; for (auto it = u->cbegin(); it != u->cend(); ++it) { - std::cout << *it << std::endl; + auto msg = FormatParagraph(*it, cols, 2); + std::cout << "- " << msg << std::endl; // Handle newlines and wrapped lines size_t i = 0; size_t j = 0; - while (j < it->size()) { - i = it->find('\n', j); + while (j < msg.size()) { + i = msg.find('\n', j); if (i == std::string::npos) { - i = it->size(); + i = msg.size(); } else { // Newline lines++; } - // Wrapped lines - lines += ((i-j) / cols); j = i + 1; } } diff --git a/src/metrics.h b/src/metrics.h index 3e830f823..2d60d30ca 100644 --- a/src/metrics.h +++ b/src/metrics.h @@ -49,6 +49,8 @@ public: bool running(); + uint64_t threadCount(); + double rate(const AtomicCounter& count); }; @@ -61,6 +63,9 @@ void TrackMinedBlock(uint256 hash); void MarkStartTime(); double GetLocalSolPS(); +int EstimateNetHeightInner(int height, int64_t tipmediantime, + int heightLastCheckpoint, int64_t timeLastCheckpoint, + int64_t genesisTime, int64_t targetSpacing); void TriggerRefresh(); diff --git a/src/miner.cpp b/src/miner.cpp index 7c44915d2..01817c759 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -12,6 +12,7 @@ #include "base58.h" #include "chainparams.h" #include "consensus/consensus.h" +#include "consensus/upgrades.h" #include "consensus/validation.h" #ifdef ENABLE_MINING #include "crypto/equihash.h" @@ -62,7 +63,7 @@ public: set setDependsOn; CFeeRate feeRate; double dPriority; - + COrphan(const CTransaction* ptxIn) : ptx(ptxIn), feeRate(0), dPriority(0) { } @@ -76,10 +77,10 @@ typedef boost::tuple TxPriority; class TxPriorityCompare { bool byFee; - + public: TxPriorityCompare(bool _byFee) : byFee(_byFee) { } - + bool operator()(const TxPriority& a, const TxPriority& b) { if (byFee) @@ -99,22 +100,18 @@ public: void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) { - pblock->nTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); - - // Updating time can change work required on testnet: - if (consensusParams.fPowAllowMinDifficultyBlocks) - pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams); + pblock->nTime = 1 + std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); } #include "komodo_defs.h" extern int32_t ASSETCHAINS_SEED,IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,ASSETCHAIN_INIT,KOMODO_INITDONE,KOMODO_ON_DEMAND,KOMODO_INITDONE,KOMODO_PASSPORT_INITDONE; +extern uint64_t ASSETCHAINS_REWARD,ASSETCHAINS_COMMISSION,ASSETCHAINS_STAKED; extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; extern std::string NOTARY_PUBKEY; -extern uint8_t NOTARY_PUBKEY33[33]; +extern uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33]; uint32_t Mining_start,Mining_height; int32_t komodo_chosennotary(int32_t *notaryidp,int32_t height,uint8_t *pubkey33,uint32_t timestamp); -int32_t komodo_is_special(int32_t height,uint8_t pubkey33[33]); int32_t komodo_pax_opreturn(int32_t height,uint8_t *opret,int32_t maxsize); uint64_t komodo_paxtotal(); int32_t komodo_baseid(char *origbase); @@ -122,10 +119,12 @@ int32_t komodo_is_issuer(); int32_t komodo_gateway_deposits(CMutableTransaction *txNew,char *symbol,int32_t tokomodo); int32_t komodo_isrealtime(int32_t *kmdheightp); int32_t komodo_validate_interest(const CTransaction &tx,int32_t txheight,uint32_t nTime,int32_t dispflag); +uint64_t komodo_commission(const CBlock &block); +int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blocktimep,uint32_t *txtimep,uint256 *utxotxidp,int32_t *utxovoutp,uint64_t *utxovaluep,uint8_t *utxosig); CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) { - uint64_t deposits; int32_t isrealtime,kmdheight; const CChainParams& chainparams = Params(); + uint64_t deposits; int32_t isrealtime,kmdheight; uint32_t blocktime; const CChainParams& chainparams = Params(); // Create new block std::unique_ptr pblocktemplate(new CBlockTemplate()); if(!pblocktemplate.get()) @@ -160,59 +159,60 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) // -blockversion=N to test forking scenarios if (Params().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 - + // Largest block you're willing to create: unsigned int nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE); // Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity: nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize)); - + // 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); - + // 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); - + // Collect memory pool transactions into the block CAmount nFees = 0; - + { LOCK2(cs_main, mempool.cs); CBlockIndex* pindexPrev = chainActive.Tip(); const int nHeight = pindexPrev->nHeight + 1; + uint32_t consensusBranchId = CurrentEpochBranchId(nHeight, chainparams.GetConsensus()); pblock->nTime = GetAdjustedTime(); const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); CCoinsViewCache view(pcoinsTip); - uint32_t expired; + uint32_t expired; uint64_t commission; // Priority order to process transactions list vOrphan; // list memory doesn't move map > mapDependers; bool fPrintPriority = GetBoolArg("-printpriority", false); - + // This vector will be sorted into a priority queue: vector vecPriority; vecPriority.reserve(mempool.mapTx.size()); - for (map::iterator mi = mempool.mapTx.begin(); + for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi) { - const CTransaction& tx = mi->second.GetTx(); - + 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)) + ? nMedianTimePast + : pblock->GetBlockTime(); + + if (tx.IsCoinBase() || !IsFinalTx(tx, nHeight, nLockTimeCutoff) || IsExpiredTx(tx, nHeight)) continue; - if ( komodo_validate_interest(tx,nHeight,(uint32_t)pblock->nTime,2) < 0 ) + if ( komodo_validate_interest(tx,nHeight,(uint32_t)pblock->nTime,0) < 0 ) { fprintf(stderr,"CreateNewBlock: komodo_validate_interest failure\n"); continue; @@ -238,7 +238,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) vOrphan.pop_back(); break; } - + // Has to wait for dependencies if (!porphan) { @@ -248,71 +248,71 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) } mapDependers[txin.prevout.hash].push_back(porphan); porphan->setDependsOn.insert(txin.prevout.hash); - nTotalIn += mempool.mapTx[txin.prevout.hash].GetTx().vout[txin.prevout.n].nValue; + 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; } nTotalIn += tx.GetJoinSplitValueIn(); - + 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->second.GetTx())); + vecPriority.push_back(TxPriority(dPriority, feeRate, &(mi->GetTx()))); } - + // Collect transactions into block uint64_t nBlockSize = 1000; uint64_t nBlockTx = 0; int64_t interest; int nBlockSigOps = 100; bool fSortedByFee = (nBlockPrioritySize <= 0); - + TxPriorityCompare comparer(fSortedByFee); std::make_heap(vecPriority.begin(), vecPriority.end(), comparer); - + while (!vecPriority.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; - + // Legacy limits on sigOps: unsigned int nTxSigOps = GetLegacySigOpCount(tx); - if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) + if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS-1) continue; - + // Skip free transactions if we're past the minimum block size: const uint256& hash = tx.GetHash(); double dPriorityDelta = 0; @@ -320,7 +320,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) 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 && @@ -330,25 +330,26 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) comparer = TxPriorityCompare(fSortedByFee); std::make_heap(vecPriority.begin(), vecPriority.end(), comparer); } - + if (!view.HaveInputs(tx)) continue; - + CAmount nTxFees = view.GetValueIn(chainActive.Tip()->nHeight,&interest,tx,chainActive.Tip()->nTime)-tx.GetValueOut(); - + nTxSigOps += GetP2SHSigOpCount(tx, view); - if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) + if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS-1) 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; - if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, Params().GetConsensus())) + PrecomputedTransactionData txdata(tx); + if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId)) continue; - - UpdateCoins(tx, state, view, nHeight); - + + UpdateCoins(tx, view, nHeight); + // Added pblock->vtx.push_back(tx); pblocktemplate->vTxFees.push_back(nTxFees); @@ -357,12 +358,12 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) ++nBlockTx; nBlockSigOps += nTxSigOps; nFees += nTxFees; - + if (fPrintPriority) { LogPrintf("priority %.1f fee %s txid %s\n",dPriority, feeRate.ToString(), tx.GetHash().ToString()); } - + // Add transactions that depend on this one to the priority queue if (mapDependers.count(hash)) { @@ -380,47 +381,93 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) } } } - + nLastBlockTx = nBlockTx; nLastBlockSize = nBlockSize; - LogPrintf("CreateNewBlock(): total size %u\n", nBlockSize); - + blocktime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + //pblock->nTime = blocktime + 1; + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus()); + //LogPrintf("CreateNewBlock(): total size %u blocktime.%u nBits.%08x\n", nBlockSize,blocktime,pblock->nBits); + if ( ASSETCHAINS_SYMBOL[0] != 0 && ASSETCHAINS_STAKED != 0 && NOTARY_PUBKEY33[0] != 0 ) + { + uint64_t txfees,utxovalue; uint32_t txtime; uint256 utxotxid,revtxid; int32_t i,siglen,numsigs,utxovout; uint8_t utxosig[128],*ptr; + CMutableTransaction txStaked = CreateNewContextualCMutableTransaction(Params().GetConsensus(), chainActive.Height() + 1); + blocktime += 2; + if ( (siglen= komodo_staked(txStaked,pblock->nBits,&blocktime,&txtime,&utxotxid,&utxovout,&utxovalue,utxosig)) > 0 ) + { + CAmount txfees = 0; + pblock->vtx.push_back(txStaked); + pblocktemplate->vTxFees.push_back(txfees); + pblocktemplate->vTxSigOps.push_back(GetLegacySigOpCount(txStaked)); + nFees += txfees; + pblock->nTime = blocktime; + if ( GetAdjustedTime() < pblock->nTime ) + { + printf("need to wait %d seconds to submit: ",(int32_t)(pblock->nTime - GetAdjustedTime())); + while ( GetAdjustedTime()+30 < pblock->nTime ) + { + sleep(30); + fprintf(stderr,"%d ",(int32_t)(pblock->nTime - GetAdjustedTime())); + } + fprintf(stderr,"finished waiting\n"); + //sleep(pblock->nTime - GetAdjustedTime()); + } + + } else fprintf(stderr,"no utxos eligible for staking\n"); + } + // Create coinbase tx - CMutableTransaction txNew; - //txNew.nLockTime = (uint32_t)time(NULL) - 60; + 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()); + txNew.nLockTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + txNew.nExpiryHeight = 0; // Add fees txNew.vout[0].nValue += nFees; txNew.vin[0].scriptSig = CScript() << nHeight << OP_0; - if ( ASSETCHAINS_SYMBOL[0] == 0 ) - { - /*int32_t i,opretlen; uint8_t opret[256],*ptr; - if ( (nHeight % 60) == 0 || komodo_gateway_deposits(&txNew,(char *)"KMD",1) == 0 ) - { - if ( (opretlen= komodo_pax_opreturn((int32_t)nHeight,opret,sizeof(opret))) > 0 ) // have pricefeed - { - txNew.vout.resize(2); - txNew.vout[1].scriptPubKey.resize(opretlen); - ptr = (uint8_t *)txNew.vout[1].scriptPubKey.data(); - for (i=0; i 1 ) - fprintf(stderr,"%s txNew numvouts.%d\n",ASSETCHAINS_SYMBOL,(int32_t)txNew.vout.size()); - } + + /*if ( ASSETCHAINS_SYMBOL[0] == 0 ) + { + int32_t i,opretlen; uint8_t opret[256],*ptr; + if ( (nHeight % 60) == 0 || komodo_gateway_deposits(&txNew,(char *)"KMD",1) == 0 ) + { + if ( (opretlen= komodo_pax_opreturn((int32_t)nHeight,opret,sizeof(opret))) > 0 ) // have pricefeed + { + txNew.vout.resize(2); + txNew.vout[1].scriptPubKey.resize(opretlen); + ptr = (uint8_t *)txNew.vout[1].scriptPubKey.data(); + for (i=0; i 1 ) + fprintf(stderr,"%s txNew numvouts.%d\n",ASSETCHAINS_SYMBOL,(int32_t)txNew.vout.size()); + }*/ pblock->vtx[0] = txNew; + if ( ASSETCHAINS_SYMBOL[0] != 0 && ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 && ASSETCHAINS_COMMISSION != 0 && (commission= komodo_commission(pblocktemplate->block)) != 0 ) + { + int32_t i; uint8_t *ptr; + txNew.vout.resize(2); + txNew.vout[1].nValue = commission; + txNew.vout[1].scriptPubKey.resize(35); + ptr = (uint8_t *)txNew.vout[1].scriptPubKey.data(); + ptr[0] = 33; + for (i=0; i<33; i++) + ptr[i+1] = ASSETCHAINS_OVERRIDE_PUBKEY33[i]; + ptr[34] = OP_CHECKSIG; + //printf("autocreate commision vout\n"); + pblock->vtx[0] = txNew; + } pblocktemplate->vTxFees[0] = -nFees; // Randomise nonce arith_uint256 nonce = UintToArith256(GetRandHash()); @@ -428,70 +475,80 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) nonce <<= 32; nonce >>= 16; pblock->nNonce = ArithToUint256(nonce); - + // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); pblock->hashReserved = uint256(); - //UpdateTime(pblock, Params().GetConsensus(), pindexPrev); - pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus()); + if ( ASSETCHAINS_SYMBOL[0] == 0 || ASSETCHAINS_STAKED == 0 || NOTARY_PUBKEY33[0] == 0 ) + { + UpdateTime(pblock, Params().GetConsensus(), pindexPrev); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus()); + } + if ( ASSETCHAINS_SYMBOL[0] == 0 && NOTARY_PUBKEY33[0] != 0 && pblock->nTime < pindexPrev->nTime+60 ) + { + pblock->nTime = pindexPrev->nTime + 60; + fprintf(stderr,"block.nTime %u vs prev.%u, gettime.%u vs adjusted.%u\n",(uint32_t)pblock->nTime,(uint32_t)(pindexPrev->nTime + 60),(uint32_t)pblock->GetBlockTime(),(uint32_t)(GetAdjustedTime() + 60)); + while ( pblock->GetBlockTime() > GetAdjustedTime() + 60 ) + sleep(1); + } pblock->nSolution.clear(); pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); - + CValidationState state; if ( !TestBlockValidity(state, *pblock, pindexPrev, false, false)) { - static uint32_t counter; - if ( counter++ < 100 ) - fprintf(stderr,"warning: testblockvalidity failed\n"); + //static uint32_t counter; + //if ( counter++ < 100 && ASSETCHAINS_STAKED == 0 ) + //fprintf(stderr,"warning: miner testblockvalidity failed\n"); return(0); } } - + return pblocktemplate.release(); } /* -#ifdef ENABLE_WALLET -boost::optional GetMinerScriptPubKey(CReserveKey& reservekey) -#else -boost::optional GetMinerScriptPubKey() -#endif -{ - CKeyID keyID; - CBitcoinAddress addr; - if (addr.SetString(GetArg("-mineraddress", ""))) { - addr.GetKeyID(keyID); - } else { -#ifdef ENABLE_WALLET - CPubKey pubkey; - if (!reservekey.GetReservedKey(pubkey)) { - return boost::optional(); - } - keyID = pubkey.GetID(); -#else - return boost::optional(); -#endif - } - - CScript scriptPubKey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG; - return scriptPubKey; -} - -#ifdef ENABLE_WALLET -CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey) -{ - boost::optional scriptPubKey = GetMinerScriptPubKey(reservekey); -#else -CBlockTemplate* CreateNewBlockWithKey() -{ - boost::optional scriptPubKey = GetMinerScriptPubKey(); -#endif - - if (!scriptPubKey) { - return NULL; - } - return CreateNewBlock(*scriptPubKey); -}*/ + #ifdef ENABLE_WALLET + boost::optional GetMinerScriptPubKey(CReserveKey& reservekey) + #else + boost::optional GetMinerScriptPubKey() + #endif + { + CKeyID keyID; + CBitcoinAddress addr; + if (addr.SetString(GetArg("-mineraddress", ""))) { + addr.GetKeyID(keyID); + } else { + #ifdef ENABLE_WALLET + CPubKey pubkey; + if (!reservekey.GetReservedKey(pubkey)) { + return boost::optional(); + } + keyID = pubkey.GetID(); + #else + return boost::optional(); + #endif + } + + CScript scriptPubKey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG; + return scriptPubKey; + } + + #ifdef ENABLE_WALLET + CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey) + { + boost::optional scriptPubKey = GetMinerScriptPubKey(reservekey); + #else + CBlockTemplate* CreateNewBlockWithKey() + { + boost::optional scriptPubKey = GetMinerScriptPubKey(); + #endif + + if (!scriptPubKey) { + return NULL; + } + return CreateNewBlock(*scriptPubKey); + }*/ ////////////////////////////////////////////////////////////////////////////// // @@ -514,7 +571,7 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& CMutableTransaction txCoinbase(pblock->vtx[0]); txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)) + COINBASE_FLAGS; assert(txCoinbase.vin[0].scriptSig.size() <= 100); - + pblock->vtx[0] = txCoinbase; pblock->hashMerkleRoot = pblock->BuildMerkleTree(); } @@ -524,7 +581,6 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& // // Internal miner // -int8_t komodo_minerid(int32_t height,uint8_t *pubkey33); CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey) { @@ -549,12 +605,6 @@ CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey) script[34] = OP_CHECKSIG; //scriptPubKey = CScript() << ToByteVector(pubkey) << OP_CHECKSIG; } - if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 ) - { - for (i=0; i<65; i++) - fprintf(stderr,"%d ",komodo_minerid(chainActive.Tip()->nHeight-i,0)); - fprintf(stderr," minerids.special %d from ht.%d\n",komodo_is_special(chainActive.Tip()->nHeight+1,NOTARY_PUBKEY33),chainActive.Tip()->nHeight); - } return CreateNewBlock(scriptPubKey); } @@ -566,7 +616,7 @@ static bool ProcessBlockFound(CBlock* pblock) { LogPrintf("%s\n", pblock->ToString()); LogPrintf("generated %s height.%d\n", FormatMoney(pblock->vtx[0].vout[0].nValue),chainActive.Tip()->nHeight+1); - + // Found a solution { LOCK(cs_main); @@ -581,11 +631,11 @@ static bool ProcessBlockFound(CBlock* pblock) for (i=31; i>=0; i--) fprintf(stderr,"%02x",((uint8_t *)&hash)[i]); fprintf(stderr," <- chainTip (stale)\n"); - + return error("KomodoMiner: generated block is stale"); } } - + #ifdef ENABLE_WALLET // Remove key from key pool if ( IS_KOMODO_NOTARY == 0 ) @@ -602,22 +652,24 @@ static bool ProcessBlockFound(CBlock* pblock) wallet.mapRequestCount[pblock->GetHash()] = 0; } #endif - + // Process this block the same as if we had received it from another node CValidationState state; if (!ProcessNewBlock(chainActive.Tip()->nHeight+1,state, NULL, pblock, true, NULL)) return error("KomodoMiner: ProcessNewBlock, block not accepted"); - + TrackMinedBlock(pblock->GetHash()); - + return true; } int32_t komodo_baseid(char *origbase); -int32_t komodo_eligiblenotary(uint8_t pubkeys[66][33],int32_t *mids,int32_t *nonzpkeysp,int32_t height); +int32_t komodo_eligiblenotary(uint8_t pubkeys[66][33],int32_t *mids,uint32_t *blocktimes,int32_t *nonzpkeysp,int32_t height); +arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc); int32_t FOUND_BLOCK,KOMODO_MAYBEMINED; extern int32_t KOMODO_LASTMINED; int32_t roundrobin_delay; +arith_uint256 HASHTarget; #ifdef ENABLE_WALLET void static BitcoinMiner(CWallet *pwallet) @@ -629,30 +681,29 @@ void static BitcoinMiner() SetThreadPriority(THREAD_PRIORITY_LOWEST); RenameThread("komodo-miner"); const CChainParams& chainparams = Params(); - + #ifdef ENABLE_WALLET // Each thread has its own key CReserveKey reservekey(pwallet); #endif - + // Each thread has its own counter unsigned int nExtraNonce = 0; - + unsigned int n = chainparams.EquihashN(); unsigned int k = chainparams.EquihashK(); - int32_t notaryid = -1; + uint8_t *script; uint64_t total,checktoshis; int32_t i,j,notaryid = -1; while ( (ASSETCHAIN_INIT == 0 || KOMODO_INITDONE == 0) ) //chainActive.Tip()->nHeight != 235300 && { sleep(1); if ( komodo_baseid(ASSETCHAINS_SYMBOL) < 0 ) break; } - //sleep(60); komodo_chosennotary(¬aryid,chainActive.Tip()->nHeight,NOTARY_PUBKEY33,(uint32_t)chainActive.Tip()->GetBlockTime()); - + std::string solver; //if ( notaryid >= 0 || ASSETCHAINS_SYMBOL[0] != 0 ) - solver = "tromp"; + solver = "tromp"; //else solver = "default"; assert(solver == "tromp" || solver == "default"); LogPrint("pow", "Using Equihash solver \"%s\" with n = %u, k = %u\n", solver, n, k); @@ -661,13 +712,13 @@ void static BitcoinMiner() std::mutex m_cs; bool cancelSolver = false; boost::signals2::connection c = uiInterface.NotifyBlockTip.connect( - [&m_cs, &cancelSolver](const uint256& hashNewTip) mutable { - std::lock_guard lock{m_cs}; - cancelSolver = true; - } - ); + [&m_cs, &cancelSolver](const uint256& hashNewTip) mutable { + std::lock_guard lock{m_cs}; + cancelSolver = true; + } + ); miningTimer.start(); - + try { if ( ASSETCHAINS_SYMBOL[0] != 0 ) fprintf(stderr,"try %s Mining with %s\n",ASSETCHAINS_SYMBOL,solver.c_str()); @@ -690,16 +741,16 @@ void static BitcoinMiner() break; MilliSleep(5000); //fprintf(stderr,"fvNodesEmpty %d IsInitialBlockDownload(%s) %d\n",(int32_t)fvNodesEmpty,ASSETCHAINS_SYMBOL,(int32_t)IsInitialBlockDownload()); - + } while (true); //fprintf(stderr,"%s Found peers\n",ASSETCHAINS_SYMBOL); miningTimer.start(); } /*while ( ASSETCHAINS_SYMBOL[0] != 0 && chainActive.Tip()->nHeight < ASSETCHAINS_MINHEIGHT ) - { - fprintf(stderr,"%s waiting for block 100, ht.%d\n",ASSETCHAINS_SYMBOL,chainActive.Tip()->nHeight); - sleep(3); - }*/ + { + fprintf(stderr,"%s waiting for block 100, ht.%d\n",ASSETCHAINS_SYMBOL,chainActive.Tip()->nHeight); + sleep(3); + }*/ // // Create new block // @@ -710,7 +761,7 @@ void static BitcoinMiner() Mining_height = pindexPrev->nHeight+1; Mining_start = (uint32_t)time(NULL); } - if ( ASSETCHAINS_SYMBOL[0] != 0 ) + if ( ASSETCHAINS_SYMBOL[0] != 0 && ASSETCHAINS_STAKED == 0 ) { //fprintf(stderr,"%s create new block ht.%d\n",ASSETCHAINS_SYMBOL,Mining_height); sleep(3); @@ -723,7 +774,7 @@ void static BitcoinMiner() if ( ptr == 0 ) { static uint32_t counter; - if ( counter++ < 100 ) + if ( counter++ < 100 && ASSETCHAINS_STAKED == 0 ) fprintf(stderr,"created illegal block, retry\n"); continue; } @@ -741,30 +792,33 @@ void static BitcoinMiner() CBlock *pblock = &pblocktemplate->block; if ( ASSETCHAINS_SYMBOL[0] != 0 ) { - if ( pblock->vtx.size() == 1 && pblock->vtx[0].vout.size() == 1 && Mining_height > ASSETCHAINS_MINHEIGHT ) + if ( ASSETCHAINS_REWARD == 0 ) { - static uint32_t counter; - if ( counter++ < 10 ) - fprintf(stderr,"skip generating %s on-demand block, no tx avail\n",ASSETCHAINS_SYMBOL); - sleep(10); - continue; - } else fprintf(stderr,"%s vouts.%d mining.%d vs %d\n",ASSETCHAINS_SYMBOL,(int32_t)pblock->vtx[0].vout.size(),Mining_height,ASSETCHAINS_MINHEIGHT); + if ( pblock->vtx.size() == 1 && pblock->vtx[0].vout.size() == 1 && Mining_height > ASSETCHAINS_MINHEIGHT ) + { + static uint32_t counter; + if ( counter++ < 10 ) + fprintf(stderr,"skip generating %s on-demand block, no tx avail\n",ASSETCHAINS_SYMBOL); + sleep(10); + continue; + } else fprintf(stderr,"%s vouts.%d mining.%d vs %d\n",ASSETCHAINS_SYMBOL,(int32_t)pblock->vtx[0].vout.size(),Mining_height,ASSETCHAINS_MINHEIGHT); + } } IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); LogPrintf("Running KomodoMiner.%s with %u transactions in block (%u bytes)\n",solver.c_str(),pblock->vtx.size(),::GetSerializeSize(*pblock,SER_NETWORK,PROTOCOL_VERSION)); // // Search // - uint8_t pubkeys[66][33]; int mids[256],gpucount,nonzpkeys,i,j,externalflag; uint32_t savebits; int64_t nStart = GetTime(); + uint8_t pubkeys[66][33]; uint32_t blocktimes[66]; int mids[256],gpucount,nonzpkeys,i,j,externalflag; uint32_t savebits; int64_t nStart = GetTime(); savebits = pblock->nBits; - arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits); + HASHTarget = arith_uint256().SetCompact(pblock->nBits); roundrobin_delay = ROUNDROBIN_DELAY; - if ( ASSETCHAINS_SYMBOL[0] == 0 && notaryid >= 0 )//komodo_is_special(pindexPrev->nHeight+1,NOTARY_PUBKEY33) > 0 ) + if ( ASSETCHAINS_SYMBOL[0] == 0 && notaryid >= 0 ) { j = 65; if ( (Mining_height >= 235300 && Mining_height < 236000) || (Mining_height % KOMODO_ELECTION_GAP) > 64 || (Mining_height % KOMODO_ELECTION_GAP) == 0 ) { - komodo_eligiblenotary(pubkeys,mids,&nonzpkeys,pindexPrev->nHeight); + komodo_eligiblenotary(pubkeys,mids,blocktimes,&nonzpkeys,pindexPrev->nHeight); if ( nonzpkeys > 0 ) { for (i=0; i<33; i++) @@ -780,12 +834,6 @@ void static BitcoinMiner() break; if ( externalflag == 0 && i != 66 ) printf("VIOLATION at %d\n",i); - for (i=0; i<66; i++) - { break; - for (j=0; j<33; j++) - printf("%02x",pubkeys[i][j]); - printf(" p%d -> %d\n",i,komodo_minerid(pindexPrev->nHeight-i,pubkeys[i])); - } for (j=gpucount=0; j<65; j++) { if ( mids[j] >= 0 || notaryid == 34 ) @@ -814,19 +862,32 @@ void static BitcoinMiner() } else fprintf(stderr,"no nonz pubkeys\n"); if ( (Mining_height >= 235300 && Mining_height < 236000) || (j == 65 && Mining_height > KOMODO_MAYBEMINED+1 && Mining_height > KOMODO_LASTMINED+64) ) { - hashTarget = arith_uint256().SetCompact(KOMODO_MINDIFF_NBITS); + HASHTarget = arith_uint256().SetCompact(KOMODO_MINDIFF_NBITS); fprintf(stderr,"I am the chosen one for %s ht.%d\n",ASSETCHAINS_SYMBOL,pindexPrev->nHeight+1); } //else fprintf(stderr,"duplicate at j.%d\n",j); } else Mining_start = 0; } else Mining_start = 0; + if ( ASSETCHAINS_STAKED != 0 && NOTARY_PUBKEY33[0] == 0 ) + { + int32_t percPoS,z; + if ( Mining_height <= 100 ) + { + sleep(60); + continue; + } + HASHTarget = komodo_PoWtarget(&percPoS,HASHTarget,Mining_height,ASSETCHAINS_STAKED); + for (z=31; z>=0; z--) + fprintf(stderr,"%02x",((uint8_t *)&HASHTarget)[z]); + fprintf(stderr," PoW for staked coin\n"); + } while (true) { /*if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 && pblock->vtx[0].vout.size() == 1 && Mining_height > ASSETCHAINS_MINHEIGHT ) // skips when it shouldnt - { - fprintf(stderr,"skip generating %s on-demand block, no tx avail\n",ASSETCHAINS_SYMBOL); - sleep(10); - break; - }*/ + { + fprintf(stderr,"skip generating %s on-demand block, no tx avail\n",ASSETCHAINS_SYMBOL); + sleep(10); + break; + }*/ // Hash state KOMODO_CHOSEN_ONE = 0; crypto_generichash_blake2b_state state; @@ -843,226 +904,242 @@ void static BitcoinMiner() crypto_generichash_blake2b_update(&curr_state,pblock->nNonce.begin(),pblock->nNonce.size()); // (x_1, x_2, ...) = A(I, V, n, k) LogPrint("pow", "Running Equihash solver \"%s\" with nNonce = %s\n",solver, pblock->nNonce.ToString()); + arith_uint256 hashTarget = HASHTarget; //fprintf(stderr,"running solver\n"); std::function)> validBlock = #ifdef ENABLE_WALLET - [&pblock, &hashTarget, &pwallet, &reservekey, &m_cs, &cancelSolver, &chainparams] + [&pblock, &hashTarget, &pwallet, &reservekey, &m_cs, &cancelSolver, &chainparams] #else - [&pblock, &hashTarget, &m_cs, &cancelSolver, &chainparams] + [&pblock, &hashTarget, &m_cs, &cancelSolver, &chainparams] #endif - (std::vector soln) { + (std::vector soln) { // Write the solution to the hash and compute the result. LogPrint("pow", "- Checking solution against target\n"); pblock->nSolution = soln; solutionTargetChecks.increment(); - if ( UintToArith256(pblock->GetHash()) > hashTarget ) + if ( UintToArith256(pblock->GetHash()) > HASHTarget ) { - //if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 ) + //if ( ASSETCHAINS_SYMBOL[0] != 0 ) // fprintf(stderr," missed target\n"); return false; } - if ( /*ASSETCHAINS_SYMBOL[0] == 0 &&*/ Mining_start != 0 && time(NULL) < Mining_start+roundrobin_delay ) + CValidationState state; + if ( !TestBlockValidity(state, *pblock, chainActive.Tip(), true, false)) { - //printf("Round robin diff sleep %d\n",(int32_t)(Mining_start+roundrobin_delay-time(NULL))); - int32_t nseconds = Mining_start+roundrobin_delay-time(NULL); - if ( nseconds > 0 ) - sleep(nseconds); - MilliSleep((rand() % 1700) + 1); + fprintf(stderr,"Invalid block mined, try again\n"); + return(false); } - else if ( ASSETCHAINS_SYMBOL[0] != 0 ) + if ( ASSETCHAINS_STAKED == 0 ) { - sleep(rand() % 30); + if ( Mining_start != 0 && time(NULL) < Mining_start+roundrobin_delay ) + { + //printf("Round robin diff sleep %d\n",(int32_t)(Mining_start+roundrobin_delay-time(NULL))); + //int32_t nseconds = Mining_start+roundrobin_delay-time(NULL); + //if ( nseconds > 0 ) + // sleep(nseconds); + MilliSleep((rand() % 1700) + 1); + } + else if ( ASSETCHAINS_SYMBOL[0] != 0 ) + { + sleep(rand() % 30); + } + } + else + { + if ( NOTARY_PUBKEY33[0] != 0 ) + { + printf("need to wait %d seconds to submit\n",(int32_t)(pblock->nTime - GetAdjustedTime())); + while ( GetAdjustedTime() < pblock->nTime ) + sleep(1); + } + else + { + uint256 tmp = pblock->GetHash(); + int32_t z; for (z=31; z>=0; z--) + fprintf(stderr,"%02x",((uint8_t *)&tmp)[z]); + fprintf(stderr," mined block!\n"); + } } KOMODO_CHOSEN_ONE = 1; // Found a solution SetThreadPriority(THREAD_PRIORITY_NORMAL); LogPrintf("KomodoMiner:\n"); - LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", pblock->GetHash().GetHex(), hashTarget.GetHex()); + LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", pblock->GetHash().GetHex(), HASHTarget.GetHex()); #ifdef ENABLE_WALLET if (ProcessBlockFound(pblock, *pwallet, reservekey)) { #else - if (ProcessBlockFound(pblock)) { + if (ProcessBlockFound(pblock)) { #endif - // Ignore chain updates caused by us + // Ignore chain updates caused by us + std::lock_guard lock{m_cs}; + cancelSolver = false; + } + KOMODO_CHOSEN_ONE = 0; + SetThreadPriority(THREAD_PRIORITY_LOWEST); + // In regression test mode, stop mining after a block is found. + if (chainparams.MineBlocksOnDemand()) { + // Increment here because throwing skips the call below + ehSolverRuns.increment(); + throw boost::thread_interrupted(); + } + //if ( ASSETCHAINS_SYMBOL[0] == 0 && NOTARY_PUBKEY33[0] != 0 ) + // sleep(1800); + return true; + }; + std::function cancelled = [&m_cs, &cancelSolver](EhSolverCancelCheck pos) { std::lock_guard lock{m_cs}; - cancelSolver = false; - } - KOMODO_CHOSEN_ONE = 0; - SetThreadPriority(THREAD_PRIORITY_LOWEST); - // In regression test mode, stop mining after a block is found. - if (chainparams.MineBlocksOnDemand()) { - // Increment here because throwing skips the call below - ehSolverRuns.increment(); - throw boost::thread_interrupted(); - } - //if ( ASSETCHAINS_SYMBOL[0] == 0 && NOTARY_PUBKEY33[0] != 0 ) - // sleep(1800); - return true; - }; - std::function cancelled = [&m_cs, &cancelSolver](EhSolverCancelCheck pos) { - std::lock_guard lock{m_cs}; - return cancelSolver; - }; - - // TODO: factor this out into a function with the same API for each solver. - if (solver == "tromp" ) { //&& notaryid >= 0 ) { - // Create solver and initialize it. - equi eq(1); - eq.setstate(&curr_state); - - // Intialization done, start algo driver. - eq.digit0(0); - eq.xfull = eq.bfull = eq.hfull = 0; - eq.showbsizes(0); - for (u32 r = 1; r < WK; r++) { - (r&1) ? eq.digitodd(r, 0) : eq.digiteven(r, 0); + return cancelSolver; + }; + + // TODO: factor this out into a function with the same API for each solver. + if (solver == "tromp" ) { //&& notaryid >= 0 ) { + // Create solver and initialize it. + equi eq(1); + eq.setstate(&curr_state); + + // Initialization done, start algo driver. + eq.digit0(0); eq.xfull = eq.bfull = eq.hfull = 0; - eq.showbsizes(r); - } - eq.digitK(0); - ehSolverRuns.increment(); - - // Convert solution indices to byte array (decompress) and pass it to validBlock method. - for (size_t s = 0; s < eq.nsols; s++) { - LogPrint("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]; + eq.showbsizes(0); + for (u32 r = 1; r < WK; r++) { + (r&1) ? eq.digitodd(r, 0) : eq.digiteven(r, 0); + eq.xfull = eq.bfull = eq.hfull = 0; + eq.showbsizes(r); } - std::vector sol_char = GetMinimalFromIndices(index_vector, DIGITBITS); - - if (validBlock(sol_char)) { - // If we find a POW solution, do not try other solutions - // because they become invalid as we created a new block in blockchain. - break; + eq.digitK(0); + ehSolverRuns.increment(); + + // Convert solution indices to byte array (decompress) and pass it to validBlock method. + for (size_t s = 0; s < eq.nsols; s++) { + LogPrint("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]; + } + std::vector sol_char = GetMinimalFromIndices(index_vector, DIGITBITS); + + if (validBlock(sol_char)) { + // If we find a POW solution, do not try other solutions + // because they become invalid as we created a new block in blockchain. + break; + } + } + } else { + try { + // If we find a valid block, we rebuild + bool found = EhOptimisedSolve(n, k, curr_state, validBlock, cancelled); + ehSolverRuns.increment(); + if (found) { + int32_t i; uint256 hash = pblock->GetHash(); + for (i=0; i<32; i++) + fprintf(stderr,"%02x",((uint8_t *)&hash)[i]); + fprintf(stderr," <- %s Block found %d\n",ASSETCHAINS_SYMBOL,Mining_height); + FOUND_BLOCK = 1; + KOMODO_MAYBEMINED = Mining_height; + break; + } + } catch (EhSolverCancelledException&) { + LogPrint("pow", "Equihash solver cancelled\n"); + std::lock_guard lock{m_cs}; + cancelSolver = false; } } - } else { - try { - // If we find a valid block, we rebuild - bool found = EhOptimisedSolve(n, k, curr_state, validBlock, cancelled); - ehSolverRuns.increment(); - if (found) { - int32_t i; uint256 hash = pblock->GetHash(); - for (i=0; i<32; i++) - fprintf(stderr,"%02x",((uint8_t *)&hash)[i]); - fprintf(stderr," <- %s Block found %d\n",ASSETCHAINS_SYMBOL,Mining_height); - FOUND_BLOCK = 1; - KOMODO_MAYBEMINED = Mining_height; + + // Check for stop or if block needs to be rebuilt + boost::this_thread::interruption_point(); + // Regtest mode doesn't require peers + if ( FOUND_BLOCK != 0 ) + { + FOUND_BLOCK = 0; + fprintf(stderr,"FOUND_BLOCK!\n"); + //sleep(2000); + } + if (vNodes.empty() && chainparams.MiningRequiresPeers()) + { + if ( ASSETCHAINS_SYMBOL[0] == 0 || Mining_height > ASSETCHAINS_MINHEIGHT ) + { + fprintf(stderr,"no nodes, break\n"); break; } - } catch (EhSolverCancelledException&) { - LogPrint("pow", "Equihash solver cancelled\n"); - std::lock_guard lock{m_cs}; - cancelSolver = false; } - } - - // Check for stop or if block needs to be rebuilt - boost::this_thread::interruption_point(); - // Regtest mode doesn't require peers - if ( FOUND_BLOCK != 0 ) - { - FOUND_BLOCK = 0; - fprintf(stderr,"FOUND_BLOCK!\n"); - //sleep(2000); - } - if (vNodes.empty() && chainparams.MiningRequiresPeers()) - { - if ( ASSETCHAINS_SYMBOL[0] == 0 || Mining_height > ASSETCHAINS_MINHEIGHT ) + if ((UintToArith256(pblock->nNonce) & 0xffff) == 0xffff) { - fprintf(stderr,"no nodes, break\n"); + //if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 ) + fprintf(stderr,"0xffff, break\n"); break; } + if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60) + { + if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 ) + fprintf(stderr,"timeout, break\n"); + break; + } + if ( pindexPrev != chainActive.Tip() ) + { + if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 ) + fprintf(stderr,"Tip advanced, break\n"); + break; + } + // Update nNonce and nTime + pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1); + pblock->nBits = savebits; + if ( ASSETCHAINS_STAKED == 0 || NOTARY_PUBKEY33[0] == 0 ) + UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); + if (chainparams.GetConsensus().fPowAllowMinDifficultyBlocks) + { + // Changing pblock->nTime can change work required on testnet: + HASHTarget.SetCompact(pblock->nBits); + } } - if ((UintToArith256(pblock->nNonce) & 0xffff) == 0xffff) - { - //if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 ) - fprintf(stderr,"0xffff, break\n"); - break; - } - if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60) - { - if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 ) - fprintf(stderr,"timeout, break\n"); - break; - } - if ( pindexPrev != chainActive.Tip() ) - { - if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 ) - fprintf(stderr,"Tip advanced, break\n"); - break; - } - // Update nNonce and nTime - pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1); - pblock->nBits = savebits; - //UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); - if (chainparams.GetConsensus().fPowAllowMinDifficultyBlocks) - { - // Changing pblock->nTime can change work required on testnet: - hashTarget.SetCompact(pblock->nBits); - } - /*CValidationState tmpstate; - if ( !TestBlockValidity(tmpstate, *pblock, pindexPrev, false, false)) - { - fprintf(stderr,"formerly valid mining block became invalid, likely due to tx expiration\n"); - break; - }*/ } } - } - catch (const boost::thread_interrupted&) - { - miningTimer.stop(); - c.disconnect(); - LogPrintf("KomodoMiner terminated\n"); - throw; - } - catch (const std::runtime_error &e) - { + catch (const boost::thread_interrupted&) + { + miningTimer.stop(); + c.disconnect(); + LogPrintf("KomodoMiner terminated\n"); + throw; + } + catch (const std::runtime_error &e) + { + miningTimer.stop(); + c.disconnect(); + LogPrintf("KomodoMiner runtime error: %s\n", e.what()); + return; + } miningTimer.stop(); c.disconnect(); - LogPrintf("KomodoMiner runtime error: %s\n", e.what()); - return; } - miningTimer.stop(); - c.disconnect(); -} - + #ifdef ENABLE_WALLET -void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads) + void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads) #else -void GenerateBitcoins(bool fGenerate, int nThreads) + void GenerateBitcoins(bool fGenerate, int nThreads) #endif -{ - static boost::thread_group* minerThreads = NULL; - - if (nThreads < 0) { - // In regtest threads defaults to 1 - if (Params().DefaultMinerThreads()) - nThreads = Params().DefaultMinerThreads(); - else - nThreads = boost::thread::hardware_concurrency(); - } - - if (minerThreads != NULL) { - minerThreads->interrupt_all(); - delete minerThreads; - minerThreads = NULL; - } - - if (nThreads == 0 || !fGenerate) - return; - - minerThreads = new boost::thread_group(); - for (int i = 0; i < nThreads; i++) { + static boost::thread_group* minerThreads = NULL; + + if (nThreads < 0) + nThreads = GetNumCores(); + + if (minerThreads != NULL) + { + minerThreads->interrupt_all(); + delete minerThreads; + minerThreads = NULL; + } + + if (nThreads == 0 || !fGenerate) + return; + + minerThreads = new boost::thread_group(); + for (int i = 0; i < nThreads; i++) { #ifdef ENABLE_WALLET - minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet)); + minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet)); #else - minerThreads->create_thread(&BitcoinMiner); + minerThreads->create_thread(&BitcoinMiner); #endif + } } -} - + #endif // ENABLE_MINING diff --git a/src/net.cpp b/src/net.cpp index 0dcdae7db..fe6ee1b77 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -7,6 +7,7 @@ #include "config/bitcoin-config.h" #endif +#include "main.h" #include "net.h" #include "addrman.h" @@ -23,13 +24,6 @@ #include #endif -#ifdef USE_UPNP -#include -#include -#include -#include -#endif - #include #include @@ -542,24 +536,22 @@ void CNode::AddWhitelistedRange(const CSubNet &subnet) { vWhitelistedRange.push_back(subnet); } -#undef X -#define X(name) stats.name = name void CNode::copyStats(CNodeStats &stats) { stats.nodeid = this->GetId(); - X(nServices); - X(nLastSend); - X(nLastRecv); - X(nTimeConnected); - X(nTimeOffset); - X(addrName); - X(nVersion); - X(cleanSubVer); - X(fInbound); - X(nStartingHeight); - X(nSendBytes); - X(nRecvBytes); - X(fWhitelisted); + stats.nServices = nServices; + stats.nLastSend = nLastSend; + stats.nLastRecv = nLastRecv; + stats.nTimeConnected = nTimeConnected; + stats.nTimeOffset = nTimeOffset; + stats.addrName = addrName; + stats.nVersion = nVersion; + stats.cleanSubVer = cleanSubVer; + stats.fInbound = fInbound; + stats.nStartingHeight = nStartingHeight; + stats.nSendBytes = nSendBytes; + stats.nRecvBytes = nRecvBytes; + stats.fWhitelisted = fWhitelisted; // It is common for nodes with good ping times to suddenly become lagged, // due to a new block arriving or other large transfer. @@ -579,7 +571,6 @@ void CNode::copyStats(CNodeStats &stats) // Leave string empty if addrLocal invalid (not filled in yet) stats.addrLocal = addrLocal.IsValid() ? addrLocal.ToString() : ""; } -#undef X // requires LOCK(cs_vRecvMsg) bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes) @@ -820,6 +811,34 @@ 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; + int height; + { + LOCK(cs_main); + height = chainActive.Height(); + } + + const Consensus::Params& params = Params().GetConsensus(); + int nActivationHeight = params.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight; + + if (nActivationHeight > 0 && + height < nActivationHeight && + height >= nActivationHeight - NETWORK_UPGRADE_PEER_PREFERENCE_BLOCK_PERIOD) + { + // Find any nodes which don't support Overwinter protocol version + BOOST_FOREACH(const CNodeRef &node, vEvictionCandidates) { + if (node->nVersion < params.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion) { + vTmpEvictionCandidates.push_back(node); + } + } + + // Prioritize these nodes by replacing eviction set with them + if (vTmpEvictionCandidates.size() > 0) { + vEvictionCandidates = vTmpEvictionCandidates; + } + } + // Deterministically select 4 peers to protect by netgroup. // An attacker cannot predict which netgroups will be protected. static CompareNetGroupKeyed comparerNetGroupKeyed; @@ -1053,7 +1072,7 @@ void ThreadSocketHandler() // happens when optimistic write failed, we choose to first drain the // write buffer in this case before receiving more. This avoids // needlessly queueing received data, if the remote peer is not themselves - // receiving data. This means properly utilizing TCP flow control signalling. + // receiving data. This means properly utilizing TCP flow control signaling. // * Otherwise, if there is no (complete) message in the receive buffer, // or there is space left in the buffer, select() for receiving data. // * (if neither of the above applies, there is certainly one message @@ -1216,131 +1235,6 @@ void ThreadSocketHandler() } - - - - - - - -#ifdef USE_UPNP -void ThreadMapPort() -{ - std::string port = strprintf("%u", GetListenPort()); - const char * multicastif = 0; - const char * minissdpdpath = 0; - struct UPNPDev * devlist = 0; - char lanaddr[64]; - -#ifndef UPNPDISCOVER_SUCCESS - /* miniupnpc 1.5 */ - devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0); -#elif MINIUPNPC_API_VERSION < 14 - /* miniupnpc 1.6 */ - int error = 0; - devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error); -#else - /* miniupnpc 1.9.20150730 */ - int error = 0; - devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error); -#endif - - struct UPNPUrls urls; - struct IGDdatas data; - int r; - - r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); - if (r == 1) - { - if (fDiscover) { - char externalIPAddress[40]; - r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress); - if(r != UPNPCOMMAND_SUCCESS) - LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r); - else - { - if(externalIPAddress[0]) - { - LogPrintf("UPnP: ExternalIPAddress = %s\n", externalIPAddress); - AddLocal(CNetAddr(externalIPAddress), LOCAL_UPNP); - } - else - LogPrintf("UPnP: GetExternalIPAddress failed.\n"); - } - } - - string strDesc = "Bitcoin " + FormatFullVersion(); - - try { - while (true) { -#ifndef UPNPDISCOVER_SUCCESS - /* miniupnpc 1.5 */ - r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, - port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0); -#else - /* miniupnpc 1.6 */ - r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, - port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); -#endif - - if(r!=UPNPCOMMAND_SUCCESS) - LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", - port, port, lanaddr, r, strupnperror(r)); - else - LogPrintf("UPnP Port Mapping successful.\n");; - - MilliSleep(20*60*1000); // Refresh every 20 minutes - } - } - catch (const boost::thread_interrupted&) - { - r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); - LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r); - freeUPNPDevlist(devlist); devlist = 0; - FreeUPNPUrls(&urls); - throw; - } - } else { - LogPrintf("No valid UPnP IGDs found\n"); - freeUPNPDevlist(devlist); devlist = 0; - if (r != 0) - FreeUPNPUrls(&urls); - } -} - -void MapPort(bool fUseUPnP) -{ - static boost::thread* upnp_thread = NULL; - - if (fUseUPnP) - { - if (upnp_thread) { - upnp_thread->interrupt(); - upnp_thread->join(); - delete upnp_thread; - } - upnp_thread = new boost::thread(boost::bind(&TraceThread, "upnp", &ThreadMapPort)); - } - else if (upnp_thread) { - upnp_thread->interrupt(); - upnp_thread->join(); - delete upnp_thread; - upnp_thread = NULL; - } -} - -#else -void MapPort(bool) -{ - // Intentionally left blank. -} -#endif - - - - - - void ThreadDNSAddressSeed() { // goal: only query DNS seeds if address need is acute @@ -1769,7 +1663,7 @@ bool BindListenPort(const CService &addrBind, string& strError, bool fWhiteliste { int nErr = WSAGetLastError(); if (nErr == WSAEADDRINUSE) - strError = strprintf(_("Unable to bind to %s on this computer. Bitcoin Core is probably already running."), addrBind.ToString()); + strError = strprintf(_("Unable to bind to %s on this computer. Zcash is probably already running."), addrBind.ToString()); else strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToString(), NetworkErrorString(nErr)); LogPrintf("%s\n", strError); @@ -1880,9 +1774,6 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) else threadGroup.create_thread(boost::bind(&TraceThread, "dnsseed", &ThreadDNSAddressSeed)); - // Map ports with UPnP - MapPort(GetBoolArg("-upnp", DEFAULT_UPNP)); - // Send and receive from sockets, accept connections threadGroup.create_thread(boost::bind(&TraceThread, "net", &ThreadSocketHandler)); @@ -1902,7 +1793,6 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) bool StopNode() { LogPrintf("StopNode()\n"); - MapPort(false); if (semOutbound) for (int i=0; ipost(); diff --git a/src/net.h b/src/net.h index 6f28f3872..afb8c9659 100644 --- a/src/net.h +++ b/src/net.h @@ -51,18 +51,14 @@ static const unsigned int MAX_ADDR_TO_SEND = 1000; static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 2 * 1024 * 1024; /** -listen default */ static const bool DEFAULT_LISTEN = true; -/** -upnp default */ -#ifdef USE_UPNP -static const bool DEFAULT_UPNP = USE_UPNP; -#else -static const bool DEFAULT_UPNP = false; -#endif /** The maximum number of entries in mapAskFor */ static const size_t MAPASKFOR_MAX_SZ = MAX_INV_SZ; /** The maximum number of entries in setAskFor (larger due to getdata latency)*/ 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 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; unsigned int ReceiveFloodSize(); unsigned int SendBufferSize(); @@ -75,7 +71,6 @@ 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); -void MapPort(bool fUseUPnP); unsigned short GetListenPort(); bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false); void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler); @@ -118,7 +113,7 @@ enum LOCAL_NONE, // unknown LOCAL_IF, // address a local interface listens on LOCAL_BIND, // address explicit bound to - LOCAL_UPNP, // address reported by UPnP + LOCAL_UPNP, // unused (was: address reported by UPnP) LOCAL_MANUAL, // address explicitly specified (-externalip=) LOCAL_MAX diff --git a/src/netbase.cpp b/src/netbase.cpp index 6a28532f1..7369b0167 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -146,7 +146,7 @@ bool static LookupIntern(const char *pszName, std::vector& vIP, unsign return false; do { - // Should set the timeout limit to a resonable value to avoid + // Should set the timeout limit to a reasonable value to avoid // generating unnecessary checking call during the polling loop, // while it can still response to stop request quick enough. // 2 seconds looks fine in our situation. diff --git a/src/netbase.h b/src/netbase.h index 83ce22663..ea3bdbd8d 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -61,7 +61,7 @@ class CNetAddr bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0) bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor) bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12) - bool IsRFC2544() const; // IPv4 inter-network communcations (192.18.0.0/15) + bool IsRFC2544() const; // IPv4 inter-network communications (192.18.0.0/15) bool IsRFC6598() const; // IPv4 ISP-level NAT (100.64.0.0/10) bool IsRFC5737() const; // IPv4 documentation addresses (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24) bool IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32) diff --git a/src/obj-test/.gitignore b/src/obj-test/.gitignore deleted file mode 100644 index d6b7ef32c..000000000 --- a/src/obj-test/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/src/paymentdisclosure.cpp b/src/paymentdisclosure.cpp new file mode 100644 index 000000000..a33b1c604 --- /dev/null +++ b/src/paymentdisclosure.cpp @@ -0,0 +1,63 @@ +// Copyright (c) 2017 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "paymentdisclosure.h" +#include "util.h" + +std::string PaymentDisclosureInfo::ToString() const { + return strprintf("PaymentDisclosureInfo(version=%d, esk=%s, joinSplitPrivKey=, address=%s)", + version, esk.ToString(), CZCPaymentAddress(zaddr).ToString()); +} + +std::string PaymentDisclosure::ToString() const { + std::string s = HexStr(payloadSig.begin(), payloadSig.end()); + return strprintf("PaymentDisclosure(payload=%s, payloadSig=%s)", payload.ToString(), s); +} + +std::string PaymentDisclosurePayload::ToString() const { + return strprintf("PaymentDisclosurePayload(version=%d, esk=%s, txid=%s, js=%d, n=%d, address=%s, message=%s)", + version, esk.ToString(), txid.ToString(), js, n, CZCPaymentAddress(zaddr).ToString(), message); +} + +PaymentDisclosure::PaymentDisclosure(const uint256 &joinSplitPubKey, const PaymentDisclosureKey &key, const PaymentDisclosureInfo &info, const std::string &message) +{ + // Populate payload member variable + payload.version = info.version; // experimental = 0, production = 1 etc. + payload.esk = info.esk; + payload.txid = key.hash; + payload.js = key.js; + payload.n = key.n; + payload.zaddr = info.zaddr; + payload.message = message; + + // 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()); + + // Prepare buffer to store ed25519 key pair in libsodium-compatible format + unsigned char bufferKeyPair[64]; + memcpy(&bufferKeyPair[0], info.joinSplitPrivKey.begin(), 32); + memcpy(&bufferKeyPair[32], joinSplitPubKey.begin(), 32); + + // Compute payload signature member variable + if (!(crypto_sign_detached(payloadSig.data(), NULL, + dataToBeSigned.begin(), 32, + &bufferKeyPair[0] + ) == 0)) + { + throw std::runtime_error("crypto_sign_detached failed"); + } + + // Sanity check + if (!(crypto_sign_verify_detached(payloadSig.data(), + dataToBeSigned.begin(), 32, + joinSplitPubKey.begin()) == 0)) + { + throw std::runtime_error("crypto_sign_verify_detached failed"); + } + + std::string sigString = HexStr(payloadSig.data(), payloadSig.data() + payloadSig.size()); + LogPrint("paymentdisclosure", "Payment Disclosure: signature = %s\n", sigString); +} diff --git a/src/paymentdisclosure.h b/src/paymentdisclosure.h new file mode 100644 index 000000000..e6a995ab4 --- /dev/null +++ b/src/paymentdisclosure.h @@ -0,0 +1,147 @@ +// Copyright (c) 2017 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef ZCASH_PAYMENTDISCLOSURE_H +#define ZCASH_PAYMENTDISCLOSURE_H + +#include "uint256.h" +#include "clientversion.h" +#include "serialize.h" +#include "streams.h" +#include "version.h" + +// For JSOutPoint +#include "wallet/wallet.h" + +#include +#include + + +// Ensure that the two different protocol messages, payment disclosure blobs and transactions, +// which are signed with the same key, joinSplitPrivKey, have disjoint encodings such that an +// encoding from one context will be rejected in the other. We know that the set of valid +// transaction versions is currently ({1..INT32_MAX}) so we will use a negative value for +// payment disclosure of -10328976 which in hex is 0xFF626470. Serialization is in little endian +// format, so a payment disclosure hex string begins 706462FF, which in ISO-8859-1 is "pdbÿ". +#define PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES -10328976 + +#define PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL 0 + +#define PAYMENT_DISCLOSURE_BLOB_STRING_PREFIX "zpd:" + +typedef JSOutPoint PaymentDisclosureKey; + +struct PaymentDisclosureInfo { + uint8_t version; // 0 = experimental, 1 = first production version, etc. + uint256 esk; // zcash/NoteEncryption.cpp + uint256 joinSplitPrivKey; // primitives/transaction.h + // ed25519 - not tied to implementation e.g. libsodium, see ed25519 rfc + + libzcash::PaymentAddress zaddr; + + PaymentDisclosureInfo() : version(PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL) { + } + + PaymentDisclosureInfo(uint8_t v, uint256 esk, uint256 key, libzcash::PaymentAddress zaddr) : version(v), esk(esk), joinSplitPrivKey(key), zaddr(zaddr) { } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(version); + READWRITE(esk); + READWRITE(joinSplitPrivKey); + READWRITE(zaddr); + } + + std::string ToString() const; + + friend bool operator==(const PaymentDisclosureInfo& a, const PaymentDisclosureInfo& b) { + return (a.version == b.version && a.esk == b.esk && a.joinSplitPrivKey == b.joinSplitPrivKey && a.zaddr == b.zaddr); + } + + friend bool operator!=(const PaymentDisclosureInfo& a, const PaymentDisclosureInfo& b) { + return !(a == b); + } + +}; + + +struct PaymentDisclosurePayload { + int32_t marker = PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES; // to be disjoint from transaction encoding + uint8_t version; // 0 = experimental, 1 = first production version, etc. + uint256 esk; // zcash/NoteEncryption.cpp + uint256 txid; // primitives/transaction.h + size_t js; // Index into CTransaction.vjoinsplit + uint8_t n; // Index into JSDescription fields of length ZC_NUM_JS_OUTPUTS + libzcash::PaymentAddress zaddr; // zcash/Address.hpp + std::string message; // parameter to RPC call + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(marker); + READWRITE(version); + READWRITE(esk); + READWRITE(txid); + READWRITE(js); + READWRITE(n); + READWRITE(zaddr); + READWRITE(message); + } + + std::string ToString() const; + + friend bool operator==(const PaymentDisclosurePayload& a, const PaymentDisclosurePayload& b) { + return ( + a.version == b.version && + a.esk == b.esk && + a.txid == b.txid && + a.js == b.js && + a.n == b.n && + a.zaddr == b.zaddr && + a.message == b.message + ); + } + + friend bool operator!=(const PaymentDisclosurePayload& a, const PaymentDisclosurePayload& b) { + return !(a == b); + } +}; + +struct PaymentDisclosure { + PaymentDisclosurePayload payload; + boost::array payloadSig; + // We use boost array because serialize doesn't like char buffer, otherwise we could do: unsigned char payloadSig[64]; + + PaymentDisclosure() {}; + PaymentDisclosure(const PaymentDisclosurePayload payload, const boost::array sig) : payload(payload), payloadSig(sig) {}; + PaymentDisclosure(const uint256& joinSplitPubKey, const PaymentDisclosureKey& key, const PaymentDisclosureInfo& info, const std::string& message); + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(payload); + READWRITE(payloadSig); + } + + std::string ToString() const; + + friend bool operator==(const PaymentDisclosure& a, const PaymentDisclosure& b) { + return (a.payload == b.payload && a.payloadSig == b.payloadSig); + } + + friend bool operator!=(const PaymentDisclosure& a, const PaymentDisclosure& b) { + return !(a == b); + } +}; + + + +typedef std::pair PaymentDisclosureKeyInfo; + + +#endif // ZCASH_PAYMENTDISCLOSURE_H diff --git a/src/paymentdisclosuredb.cpp b/src/paymentdisclosuredb.cpp new file mode 100644 index 000000000..ef32f2845 --- /dev/null +++ b/src/paymentdisclosuredb.cpp @@ -0,0 +1,93 @@ +// Copyright (c) 2017 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "paymentdisclosuredb.h" + +#include "util.h" +#include "leveldbwrapper.h" + +#include + +using namespace std; + +static boost::filesystem::path emptyPath; + +/** + * Static method to return the shared/default payment disclosure database. + */ +shared_ptr PaymentDisclosureDB::sharedInstance() { + // Thread-safe in C++11 and gcc 4.3 + static shared_ptr ptr = std::make_shared(); + return ptr; +} + +// C++11 delegated constructor +PaymentDisclosureDB::PaymentDisclosureDB() : PaymentDisclosureDB(emptyPath) { +} + +PaymentDisclosureDB::PaymentDisclosureDB(const boost::filesystem::path& dbPath) { + boost::filesystem::path path(dbPath); + if (path.empty()) { + path = GetDataDir() / "paymentdisclosure"; + LogPrintf("PaymentDisclosure: using default path for database: %s\n", path.string()); + } else { + LogPrintf("PaymentDisclosure: using custom path for database: %s\n", path.string()); + } + + TryCreateDirectory(path); + options.create_if_missing = true; + leveldb::Status status = leveldb::DB::Open(options, path.string(), &db); + HandleError(status); // throws exception + LogPrintf("PaymentDisclosure: Opened LevelDB successfully\n"); +} + +PaymentDisclosureDB::~PaymentDisclosureDB() { + if (db != nullptr) { + delete db; + } +} + +bool PaymentDisclosureDB::Put(const PaymentDisclosureKey& key, const PaymentDisclosureInfo& info) +{ + if (db == nullptr) { + return false; + } + + std::lock_guard guard(lock_); + + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + ssValue.reserve(ssValue.GetSerializeSize(info)); + ssValue << info; + leveldb::Slice slice(&ssValue[0], ssValue.size()); + + leveldb::Status status = db->Put(writeOptions, key.ToString(), slice); + HandleError(status); + return true; +} + +bool PaymentDisclosureDB::Get(const PaymentDisclosureKey& key, PaymentDisclosureInfo& info) +{ + if (db == nullptr) { + return false; + } + + std::lock_guard guard(lock_); + + std::string strValue; + leveldb::Status status = db->Get(readOptions, key.ToString(), &strValue); + if (!status.ok()) { + if (status.IsNotFound()) + return false; + LogPrintf("PaymentDisclosure: LevelDB read failure: %s\n", status.ToString()); + HandleError(status); + } + + try { + CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION); + ssValue >> info; + } catch (const std::exception&) { + return false; + } + return true; +} diff --git a/src/paymentdisclosuredb.h b/src/paymentdisclosuredb.h new file mode 100644 index 000000000..9352cac8f --- /dev/null +++ b/src/paymentdisclosuredb.h @@ -0,0 +1,42 @@ +// Copyright (c) 2017 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef ZCASH_PAYMENTDISCLOSUREDB_H +#define ZCASH_PAYMENTDISCLOSUREDB_H + +#include "paymentdisclosure.h" + +#include +#include +#include +#include +#include + +#include + +#include + + +class PaymentDisclosureDB +{ +protected: + leveldb::DB* db = nullptr; + leveldb::Options options; + leveldb::ReadOptions readOptions; + leveldb::WriteOptions writeOptions; + mutable std::mutex lock_; + +public: + static std::shared_ptr sharedInstance(); + + PaymentDisclosureDB(); + PaymentDisclosureDB(const boost::filesystem::path& dbPath); + ~PaymentDisclosureDB(); + + bool Put(const PaymentDisclosureKey& key, const PaymentDisclosureInfo& info); + bool Get(const PaymentDisclosureKey& key, PaymentDisclosureInfo& info); +}; + + +#endif // ZCASH_PAYMENTDISCLOSUREDB_H diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index dd534d803..e1fb1c3c6 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -16,14 +16,12 @@ void TxConfirmStats::Initialize(std::vector& defaultBuckets, { decay = _decay; dataTypeString = _dataTypeString; - //for (unsigned int i = 0; i < defaultBuckets.size(); i++) { - // buckets.push_back(defaultBuckets[i]); - // bucketMap[defaultBuckets[i]] = i; buckets.insert(buckets.end(), defaultBuckets.begin(), defaultBuckets.end()); buckets.push_back(std::numeric_limits::infinity()); for (unsigned int i = 0; i < buckets.size(); i++) { bucketMap[buckets[i]] = i; } + confAvg.resize(maxConfirms); curBlockConf.resize(maxConfirms); unconfTxs.resize(maxConfirms); @@ -57,7 +55,6 @@ unsigned int TxConfirmStats::FindBucketIndex(double val) { extern char ASSETCHAINS_SYMBOL[]; auto it = bucketMap.lower_bound(val); - //assert(it != bucketMap.end()); if ( it != bucketMap.end() ) { static uint32_t counter; @@ -72,7 +69,6 @@ void TxConfirmStats::Record(int blocksToConfirm, double val) // blocksToConfirm is 1-based if (blocksToConfirm < 1) return; - //unsigned int bucketindex = bucketMap.lower_bound(val)->second; unsigned int bucketindex = FindBucketIndex(val); for (size_t i = blocksToConfirm; i <= curBlockConf.size(); i++) { curBlockConf[i - 1][bucketindex]++; @@ -264,7 +260,6 @@ void TxConfirmStats::Read(CAutoFile& filein) unsigned int TxConfirmStats::NewTx(unsigned int nBlockHeight, double val) { - //unsigned int bucketindex = bucketMap.lower_bound(val)->second; unsigned int bucketindex = FindBucketIndex(val); unsigned int blockIndex = nBlockHeight % unconfTxs.size(); unconfTxs[blockIndex][bucketindex]++; @@ -325,7 +320,6 @@ CBlockPolicyEstimator::CBlockPolicyEstimator(const CFeeRate& _minRelayFee) for (double bucketBoundary = minTrackedFee.GetFeePerK(); bucketBoundary <= MAX_FEERATE; bucketBoundary *= FEE_SPACING) { vfeelist.push_back(bucketBoundary); } - //vfeelist.push_back(INF_FEERATE); feeStats.Initialize(vfeelist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY, "FeeRate"); minTrackedPriority = AllowFreeThreshold() < MIN_PRIORITY ? MIN_PRIORITY : AllowFreeThreshold(); @@ -333,7 +327,6 @@ CBlockPolicyEstimator::CBlockPolicyEstimator(const CFeeRate& _minRelayFee) for (double bucketBoundary = minTrackedPriority; bucketBoundary <= MAX_PRIORITY; bucketBoundary *= PRI_SPACING) { vprilist.push_back(bucketBoundary); } - //vprilist.push_back(INF_PRIORITY); priStats.Initialize(vprilist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY, "Priority"); feeUnlikely = CFeeRate(0); diff --git a/src/policy/fees.h b/src/policy/fees.h index 999fa4dc1..6d43189be 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -28,7 +28,7 @@ class CTxMemPoolEntry; * 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 * be included in a block within the next 5 blocks, you would start by looking - * at the bucket with with the highest fee transactions and verifying that a + * at the bucket with the highest fee 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 @@ -71,7 +71,7 @@ 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 tx's included due to + * 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 * in a block. There is always a bucket into which any given double value @@ -100,7 +100,7 @@ private: // and calcuate the totals for the current block to update the moving averages std::vector > curBlockConf; // curBlockConf[Y][X] - // Sum the total priority/fee of all tx's in each bucket + // Sum the total priority/fee of all txs 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 @@ -214,7 +214,7 @@ static const double FEE_SPACING = 1.1; static const double PRI_SPACING = 2; /** - * We want to be able to estimate fees or priorities that are needed on tx's to be included in + * We want to be able to estimate fees or priorities that are needed on txs 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 */ diff --git a/src/pow.cpp b/src/pow.cpp index ce82a4e1b..2b20f545b 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -19,6 +19,7 @@ #ifdef ENABLE_RUST #include "librustzcash.h" #endif // ENABLE_RUST +uint32_t komodo_chainactive_timestamp(); unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params) { @@ -115,9 +116,8 @@ bool CheckEquihashSolution(const CBlockHeader *pblock, const CChainParams& param return true; } -uint32_t komodo_chainactive_timestamp(); int32_t komodo_chosennotary(int32_t *notaryidp,int32_t height,uint8_t *pubkey33,uint32_t timestamp); -int32_t komodo_is_special(uint8_t pubkeys[66][33],int32_t mids[66],int32_t height,uint8_t pubkey33[33],uint32_t timestamp); +int32_t komodo_is_special(uint8_t pubkeys[66][33],int32_t mids[66],uint32_t blocktimes[66],int32_t height,uint8_t pubkey33[33],uint32_t blocktime); int32_t komodo_currentheight(); CBlockIndex *komodo_chainactive(int32_t height); void komodo_index2pubkey33(uint8_t *pubkey33,CBlockIndex *pindex,int32_t height); @@ -126,18 +126,19 @@ extern uint64_t ASSETCHAINS_STAKED; extern char ASSETCHAINS_SYMBOL[]; #define KOMODO_ELECTION_GAP 2000 -int32_t komodo_eligiblenotary(uint8_t pubkeys[66][33],int32_t *mids,int32_t *nonzpkeysp,int32_t height); +int32_t komodo_eligiblenotary(uint8_t pubkeys[66][33],int32_t *mids,uint32_t blocktimes[66],int32_t *nonzpkeysp,int32_t height); int32_t KOMODO_LOADINGBLOCKS = 1; extern std::string NOTARY_PUBKEY; -bool CheckProofOfWork(int32_t height,uint8_t *pubkey33,uint256 hash, unsigned int nBits, const Consensus::Params& params) +bool CheckProofOfWork(int32_t height,uint8_t *pubkey33,uint256 hash,unsigned int nBits,const Consensus::Params& params,uint32_t blocktime) { extern int32_t KOMODO_REWIND; - bool fNegative,fOverflow; uint8_t origpubkey33[33]; int32_t i,nonzpkeys=0,nonz=0,special=0,special2=0,notaryid=-1,flag = 0, mids[66]; uint32_t timestamp = 0; CBlockIndex *pindex=0; + bool fNegative,fOverflow; uint8_t origpubkey33[33]; int32_t i,nonzpkeys=0,nonz=0,special=0,special2=0,notaryid=-1,flag = 0, mids[66]; uint32_t tiptime,blocktimes[66]; arith_uint256 bnTarget; uint8_t pubkeys[66][33]; memcpy(origpubkey33,pubkey33,33); - timestamp = komodo_chainactive_timestamp(); + memset(blocktimes,0,sizeof(blocktimes)); + tiptime = komodo_chainactive_timestamp(); bnTarget.SetCompact(nBits, &fNegative, &fOverflow); if ( height == 0 ) { @@ -146,7 +147,7 @@ bool CheckProofOfWork(int32_t height,uint8_t *pubkey33,uint256 hash, unsigned in } if ( height > 34000 && ASSETCHAINS_SYMBOL[0] == 0 ) // 0 -> non-special notary { - special = komodo_chosennotary(¬aryid,height,pubkey33,timestamp); + special = komodo_chosennotary(¬aryid,height,pubkey33,tiptime); for (i=0; i<33; i++) { if ( pubkey33[i] != 0 ) @@ -157,8 +158,8 @@ bool CheckProofOfWork(int32_t height,uint8_t *pubkey33,uint256 hash, unsigned in //fprintf(stderr,"ht.%d null pubkey checkproof return\n",height); return(true); // will come back via different path with pubkey set } - flag = komodo_eligiblenotary(pubkeys,mids,&nonzpkeys,height); - special2 = komodo_is_special(pubkeys,mids,height,pubkey33,timestamp); + flag = komodo_eligiblenotary(pubkeys,mids,blocktimes,&nonzpkeys,height); + special2 = komodo_is_special(pubkeys,mids,blocktimes,height,pubkey33,blocktime); if ( notaryid >= 0 ) { if ( height > 10000 && height < 80000 && (special != 0 || special2 > 0) ) @@ -175,7 +176,7 @@ bool CheckProofOfWork(int32_t height,uint8_t *pubkey33,uint256 hash, unsigned in flag = 0; else fprintf(stderr,"ht.%d notaryid.%d special.%d flag.%d special2.%d\n",height,notaryid,special,flag,special2); } - if ( flag != 0 || special2 > 0 ) + if ( (flag != 0 || special2 > 0) && special2 != -2 ) { //fprintf(stderr,"EASY MINING ht.%d\n",height); bnTarget.SetCompact(KOMODO_MINDIFF_NBITS,&fNegative,&fOverflow); @@ -190,8 +191,34 @@ bool CheckProofOfWork(int32_t height,uint8_t *pubkey33,uint256 hash, unsigned in if ( KOMODO_LOADINGBLOCKS != 0 ) return true; if ( ASSETCHAINS_SYMBOL[0] != 0 || height > 792000 ) + { + if ( 0 && height > 792000 ) + { + for (i=31; i>=0; i--) + printf("%02x",((uint8_t *)&hash)[i]); + printf(" hash vs "); + for (i=31; i>=0; i--) + printf("%02x",((uint8_t *)&bnTarget)[i]); + printf(" ht.%d special.%d notaryid.%d ht.%d mod.%d error\n",height,special,notaryid,height,(height % 35)); + for (i=0; i<33; i++) + printf("%02x",pubkey33[i]); + printf(" <- pubkey\n"); + for (i=0; i<33; i++) + printf("%02x",origpubkey33[i]); + printf(" <- origpubkey\n"); + for (i=0; i<66; i++) + printf("%d ",mids[i]); + printf(" minerids from ht.%d\n",height); + } return false; + } } + /*for (i=31; i>=0; i--) + fprintf(stderr,"%02x",((uint8_t *)&hash)[i]); + fprintf(stderr," hash vs "); + for (i=31; i>=0; i--) + fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]); + fprintf(stderr," height.%d notaryid.%d PoW valid\n",height,notaryid);*/ return true; } diff --git a/src/pow.h b/src/pow.h index 3edec74ac..213fe228d 100644 --- a/src/pow.h +++ b/src/pow.h @@ -25,7 +25,7 @@ unsigned int CalculateNextWorkRequired(arith_uint256 bnAvg, bool CheckEquihashSolution(const CBlockHeader *pblock, const CChainParams&); /** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */ -bool CheckProofOfWork(int32_t height,uint8_t *pubkey33,uint256 hash, unsigned int nBits, const Consensus::Params&); +bool CheckProofOfWork(int32_t height,uint8_t *pubkey33,uint256 hash, unsigned int nBits, const Consensus::Params&,uint32_t blocktime); arith_uint256 GetBlockProof(const CBlockIndex& block); /** Return the time it would take to redo the work difference between from and to, assuming the current hashrate corresponds to the difficulty at tip, in seconds. */ diff --git a/src/pow/tromp/equi_miner.h b/src/pow/tromp/equi_miner.h index 9559c178e..ae527827a 100644 --- a/src/pow/tromp/equi_miner.h +++ b/src/pow/tromp/equi_miner.h @@ -3,7 +3,7 @@ // Fix N, K, such that n = N/(k+1) is integer // Fix M = 2^{n+1} hashes each of length N bits, -// H_0, ... , H_{M-1}, generated fom (n+1)-bit indices. +// H_0, ... , H_{M-1}, generated from (n+1)-bit indices. // Problem: find binary tree on 2^K distinct indices, // for which the exclusive-or of leaf hashes is all 0s. // Additionally, it should satisfy the Wagner conditions: diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 9f4a0eae0..d6ad31c3a 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -16,13 +16,12 @@ JSDescription::JSDescription(ZCJoinSplit& params, const boost::array& outputs, CAmount vpub_old, CAmount vpub_new, - bool computeProof) : vpub_old(vpub_old), vpub_new(vpub_new), anchor(anchor) + bool computeProof, + uint256 *esk // payment disclosure + ) : vpub_old(vpub_old), vpub_new(vpub_new), anchor(anchor) { boost::array notes; - if (computeProof) { - params.loadProvingKey(); - } proof = params.prove( inputs, outputs, @@ -37,7 +36,8 @@ JSDescription::JSDescription(ZCJoinSplit& params, vpub_old, vpub_new, anchor, - computeProof + computeProof, + esk // payment disclosure ); } @@ -57,7 +57,9 @@ JSDescription JSDescription::Randomized( CAmount vpub_old, CAmount vpub_new, bool computeProof, - std::function gen) + uint256 *esk, // payment disclosure + std::function gen + ) { // Randomize the order of the inputs and outputs inputMap = {0, 1}; @@ -70,7 +72,9 @@ JSDescription JSDescription::Randomized( return JSDescription( params, pubKeyHash, anchor, inputs, outputs, - vpub_old, vpub_new, computeProof); + vpub_old, vpub_new, computeProof, + esk // payment disclosure + ); } bool JSDescription::Verify( @@ -147,8 +151,9 @@ std::string CTxOut::ToString() const return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30)); } -CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::MIN_CURRENT_VERSION), nLockTime(0) {} -CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime), +CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::SPROUT_MIN_CURRENT_VERSION), fOverwintered(false), nVersionGroupId(0), nExpiryHeight(0), nLockTime(0) {} +CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId), nExpiryHeight(tx.nExpiryHeight), + vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime), vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig) { @@ -164,19 +169,34 @@ void CTransaction::UpdateHash() const *const_cast(&hash) = SerializeHash(*this); } -CTransaction::CTransaction() : nVersion(CTransaction::MIN_CURRENT_VERSION), vin(), vout(), nLockTime(0), vjoinsplit(), joinSplitPubKey(), joinSplitSig() { } +CTransaction::CTransaction() : nVersion(CTransaction::SPROUT_MIN_CURRENT_VERSION), fOverwintered(false), nVersionGroupId(0), nExpiryHeight(0), vin(), vout(), nLockTime(0), vjoinsplit(), joinSplitPubKey(), joinSplitSig() { } -CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime), vjoinsplit(tx.vjoinsplit), - joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig) +CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId), nExpiryHeight(tx.nExpiryHeight), + vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime), + vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig) { UpdateHash(); } +// Protected constructor which only derived classes can call. +// For developer testing only. +CTransaction::CTransaction( + const CMutableTransaction &tx, + bool evilDeveloperFlag) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId), nExpiryHeight(tx.nExpiryHeight), + vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime), + vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig) +{ + assert(evilDeveloperFlag); +} + CTransaction& CTransaction::operator=(const CTransaction &tx) { + *const_cast(&fOverwintered) = tx.fOverwintered; *const_cast(&nVersion) = tx.nVersion; + *const_cast(&nVersionGroupId) = tx.nVersionGroupId; *const_cast*>(&vin) = tx.vin; *const_cast*>(&vout) = tx.vout; *const_cast(&nLockTime) = tx.nLockTime; + *const_cast(&nExpiryHeight) = tx.nExpiryHeight; *const_cast*>(&vjoinsplit) = tx.vjoinsplit; *const_cast(&joinSplitPubKey) = tx.joinSplitPubKey; *const_cast(&joinSplitSig) = tx.joinSplitSig; @@ -249,12 +269,24 @@ unsigned int CTransaction::CalculateModifiedSize(unsigned int nTxSize) const std::string CTransaction::ToString() const { std::string str; - str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%u, vout.size=%u, nLockTime=%u)\n", - GetHash().ToString().substr(0,10), - nVersion, - vin.size(), - vout.size(), - nLockTime); + if (!fOverwintered) { + str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%u, vout.size=%u, nLockTime=%u)\n", + GetHash().ToString().substr(0,10), + nVersion, + vin.size(), + vout.size(), + nLockTime); + } else if (nVersion >= 3) { + str += strprintf("CTransaction(hash=%s, ver=%d, fOverwintered=%d, nVersionGroupId=%08x, vin.size=%u, vout.size=%u, nLockTime=%u, nExpiryHeight=%u)\n", + GetHash().ToString().substr(0,10), + nVersion, + fOverwintered, + nVersionGroupId, + vin.size(), + vout.size(), + nLockTime, + nExpiryHeight); + } for (unsigned int i = 0; i < vin.size(); i++) str += " " + vin[i].ToString() + "\n"; for (unsigned int i = 0; i < vout.size(); i++) diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 699b7a89f..35c12abc8 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -81,7 +81,8 @@ public: const boost::array& outputs, CAmount vpub_old, CAmount vpub_new, - bool computeProof = true // Set to false in some tests + bool computeProof = true, // Set to false in some tests + uint256 *esk = nullptr // payment disclosure ); static JSDescription Randomized( @@ -100,6 +101,7 @@ public: CAmount vpub_old, CAmount vpub_new, bool computeProof = true, // Set to false in some tests + uint256 *esk = nullptr, // payment disclosure std::function gen = GetRandInt ); @@ -310,6 +312,10 @@ public: std::string ToString() const; }; +// Overwinter version group id +static constexpr uint32_t OVERWINTER_VERSION_GROUP_ID = 0x03C48270; +static_assert(OVERWINTER_VERSION_GROUP_ID != 0, "version group id must be non-zero as specified in ZIP 202"); + struct CMutableTransaction; /** The basic transaction that is broadcasted on the network and contained in @@ -322,14 +328,29 @@ private: const uint256 hash; void UpdateHash() const; +protected: + /** Developer testing only. Set evilDeveloperFlag to true. + * Convert a CMutableTransaction into a CTransaction without invoking UpdateHash() + */ + CTransaction(const CMutableTransaction &tx, bool evilDeveloperFlag); + public: typedef boost::array joinsplit_sig_t; - // Transactions that include a list of JoinSplits are version 2. - static const int32_t MIN_CURRENT_VERSION = 1; - static const int32_t MAX_CURRENT_VERSION = 2; + // Transactions that include a list of JoinSplits are >= version 2. + static const int32_t SPROUT_MIN_CURRENT_VERSION = 1; + static const int32_t SPROUT_MAX_CURRENT_VERSION = 2; + static const int32_t OVERWINTER_MIN_CURRENT_VERSION = 3; + static const int32_t OVERWINTER_MAX_CURRENT_VERSION = 3; + + static_assert(SPROUT_MIN_CURRENT_VERSION >= SPROUT_MIN_TX_VERSION, + "standard rule for tx version should be consistent with network rule"); + + static_assert(OVERWINTER_MIN_CURRENT_VERSION >= OVERWINTER_MIN_TX_VERSION, + "standard rule for tx version should be consistent with network rule"); - static_assert(MIN_CURRENT_VERSION >= MIN_TX_VERSION, + static_assert( (OVERWINTER_MAX_CURRENT_VERSION <= OVERWINTER_MAX_TX_VERSION && + OVERWINTER_MAX_CURRENT_VERSION >= OVERWINTER_MIN_CURRENT_VERSION), "standard rule for tx version should be consistent with network rule"); // The local variables are made const to prevent unintended modification @@ -337,10 +358,13 @@ public: // actually immutable; deserialization and assignment are implemented, // and bypass the constness. This is safe, as they update the entire // structure, including the hash. + const bool fOverwintered; const int32_t nVersion; + const uint32_t nVersionGroupId; const std::vector vin; const std::vector vout; const uint32_t nLockTime; + const uint32_t nExpiryHeight; const std::vector vjoinsplit; const uint256 joinSplitPubKey; const joinsplit_sig_t joinSplitSig = {{0}}; @@ -357,11 +381,34 @@ public: template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - READWRITE(*const_cast(&this->nVersion)); + if (ser_action.ForRead()) { + // When deserializing, unpack the 4 byte header to extract fOverwintered and nVersion. + uint32_t header; + READWRITE(header); + *const_cast(&fOverwintered) = header >> 31; + *const_cast(&this->nVersion) = header & 0x7FFFFFFF; + } else { + uint32_t header = GetHeader(); + READWRITE(header); + } nVersion = this->nVersion; + if (fOverwintered) { + READWRITE(*const_cast(&this->nVersionGroupId)); + } + + bool isOverwinterV3 = fOverwintered && + nVersionGroupId == OVERWINTER_VERSION_GROUP_ID && + nVersion == 3; + if (fOverwintered && !isOverwinterV3) { + throw std::ios_base::failure("Unknown transaction format"); + } + READWRITE(*const_cast*>(&vin)); READWRITE(*const_cast*>(&vout)); READWRITE(*const_cast(&nLockTime)); + if (isOverwinterV3) { + READWRITE(*const_cast(&nExpiryHeight)); + } if (nVersion >= 2) { READWRITE(*const_cast*>(&vjoinsplit)); if (vjoinsplit.size() > 0) { @@ -381,6 +428,16 @@ public: return hash; } + uint32_t GetHeader() const { + // When serializing v1 and v2, the 4 byte header is nVersion + uint32_t header = this->nVersion; + // When serializing Overwintered tx, the 4 byte header is the combination of fOverwintered and nVersion + if (fOverwintered) { + header |= 1 << 31; + } + return header; + } + // Return sum of txouts. CAmount GetValueOut() const; // GetValueIn() is a method on CCoinsViewCache, because @@ -416,10 +473,13 @@ public: /** A mutable version of CTransaction. */ struct CMutableTransaction { + bool fOverwintered; int32_t nVersion; + uint32_t nVersionGroupId; std::vector vin; std::vector vout; uint32_t nLockTime; + uint32_t nExpiryHeight; std::vector vjoinsplit; uint256 joinSplitPubKey; CTransaction::joinsplit_sig_t joinSplitSig = {{0}}; @@ -431,11 +491,39 @@ struct CMutableTransaction template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - READWRITE(this->nVersion); + if (ser_action.ForRead()) { + // When deserializing, unpack the 4 byte header to extract fOverwintered and nVersion. + uint32_t header; + READWRITE(header); + fOverwintered = header >> 31; + this->nVersion = header & 0x7FFFFFFF; + } else { + // When serializing v1 and v2, the 4 byte header is nVersion + uint32_t header = this->nVersion; + // When serializing Overwintered tx, the 4 byte header is the combination of fOverwintered and nVersion + if (fOverwintered) { + header |= 1 << 31; + } + READWRITE(header); + } nVersion = this->nVersion; + if (fOverwintered) { + READWRITE(nVersionGroupId); + } + + bool isOverwinterV3 = fOverwintered && + nVersionGroupId == OVERWINTER_VERSION_GROUP_ID && + nVersion == 3; + if (fOverwintered && !isOverwinterV3) { + throw std::ios_base::failure("Unknown transaction format"); + } + READWRITE(vin); READWRITE(vout); READWRITE(nLockTime); + if (isOverwinterV3) { + READWRITE(nExpiryHeight); + } if (nVersion >= 2) { READWRITE(vjoinsplit); if (vjoinsplit.size() > 0) { diff --git a/src/protocol.h b/src/protocol.h index b5e65032a..b0208b01c 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -71,10 +71,6 @@ enum { // 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. NODE_NETWORK = (1 << 0), - // NODE_GETUTXO means the node is capable of responding to the getutxo protocol request. - // Bitcoin Core does not support this but a patch set called Bitcoin XT does. - // See BIP 64 for details on how this is implemented. - NODE_GETUTXO = (1 << 1), // Bits 24-31 are reserved for temporary experiments. Just pick a bit that // isn't getting used, or one not being used much, and notify the diff --git a/src/pubkey.cpp b/src/pubkey.cpp index bdab13760..0b87bb526 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -1,73 +1,101 @@ // Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2017 The Zcash developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "pubkey.h" -#include "eccryptoverify.h" +#include +#include + +namespace +{ +/* Global secp256k1_context object used for verification. */ +secp256k1_context* secp256k1_context_verify = NULL; +} -#include "ecwrapper.h" bool CPubKey::Verify(const uint256 &hash, const std::vector& vchSig) const { if (!IsValid()) return false; - CECKey key; - if (!key.SetPubKey(begin(), size())) + secp256k1_pubkey pubkey; + secp256k1_ecdsa_signature sig; + if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &(*this)[0], size())) { return false; - if (!key.Verify(hash, vchSig)) + } + if (vchSig.size() == 0) { return false; - return true; + } + /* Zcash, unlike Bitcoin, has always enforced strict DER signatures. */ + if (!secp256k1_ecdsa_signature_parse_der(secp256k1_context_verify, &sig, &vchSig[0], vchSig.size())) { + return false; + } + /* libsecp256k1's ECDSA verification requires lower-S signatures, which have + * not historically been enforced in Bitcoin or Zcash, so normalize them first. */ + secp256k1_ecdsa_signature_normalize(secp256k1_context_verify, &sig, &sig); + return secp256k1_ecdsa_verify(secp256k1_context_verify, &sig, hash.begin(), &pubkey); } bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector& vchSig) { - if (vchSig.size() != 65) + if (vchSig.size() != COMPACT_SIGNATURE_SIZE) return false; int recid = (vchSig[0] - 27) & 3; bool fComp = ((vchSig[0] - 27) & 4) != 0; - CECKey key; - if (!key.Recover(hash, &vchSig[1], recid)) + secp256k1_pubkey pubkey; + secp256k1_ecdsa_recoverable_signature sig; + if (!secp256k1_ecdsa_recoverable_signature_parse_compact(secp256k1_context_verify, &sig, &vchSig[1], recid)) { + return false; + } + if (!secp256k1_ecdsa_recover(secp256k1_context_verify, &pubkey, &sig, hash.begin())) { return false; - std::vector pubkey; - key.GetPubKey(pubkey, fComp); - Set(pubkey.begin(), pubkey.end()); + } + unsigned char pub[PUBLIC_KEY_SIZE]; + size_t publen = PUBLIC_KEY_SIZE; + secp256k1_ec_pubkey_serialize(secp256k1_context_verify, pub, &publen, &pubkey, fComp ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED); + Set(pub, pub + publen); return true; } bool CPubKey::IsFullyValid() const { if (!IsValid()) return false; - CECKey key; - if (!key.SetPubKey(begin(), size())) - return false; - return true; + secp256k1_pubkey pubkey; + return secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &(*this)[0], size()); } bool CPubKey::Decompress() { if (!IsValid()) return false; - CECKey key; - if (!key.SetPubKey(begin(), size())) + secp256k1_pubkey pubkey; + if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &(*this)[0], size())) { return false; - std::vector pubkey; - key.GetPubKey(pubkey, false); - Set(pubkey.begin(), pubkey.end()); + } + unsigned char pub[PUBLIC_KEY_SIZE]; + size_t publen = PUBLIC_KEY_SIZE; + secp256k1_ec_pubkey_serialize(secp256k1_context_verify, pub, &publen, &pubkey, SECP256K1_EC_UNCOMPRESSED); + Set(pub, pub + publen); return true; } bool CPubKey::Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const { assert(IsValid()); assert((nChild >> 31) == 0); - assert(begin() + 33 == end()); + assert(size() == COMPRESSED_PUBLIC_KEY_SIZE); unsigned char out[64]; BIP32Hash(cc, nChild, *begin(), begin()+1, out); memcpy(ccChild.begin(), out+32, 32); - CECKey key; - bool ret = key.SetPubKey(begin(), size()); - ret &= key.TweakPublic(out); - std::vector pubkey; - key.GetPubKey(pubkey, true); - pubkeyChild.Set(pubkey.begin(), pubkey.end()); - return ret; + secp256k1_pubkey pubkey; + if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &(*this)[0], size())) { + return false; + } + if (!secp256k1_ec_pubkey_tweak_add(secp256k1_context_verify, &pubkey, out)) { + return false; + } + unsigned char pub[COMPRESSED_PUBLIC_KEY_SIZE]; + size_t publen = COMPRESSED_PUBLIC_KEY_SIZE; + secp256k1_ec_pubkey_serialize(secp256k1_context_verify, pub, &publen, &pubkey, SECP256K1_EC_COMPRESSED); + pubkeyChild.Set(pub, pub + publen); + return true; } void CExtPubKey::Encode(unsigned char code[74]) const { @@ -76,8 +104,8 @@ void CExtPubKey::Encode(unsigned char code[74]) const { code[5] = (nChild >> 24) & 0xFF; code[6] = (nChild >> 16) & 0xFF; code[7] = (nChild >> 8) & 0xFF; code[8] = (nChild >> 0) & 0xFF; memcpy(code+9, chaincode.begin(), 32); - assert(pubkey.size() == 33); - memcpy(code+41, pubkey.begin(), 33); + assert(pubkey.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE); + memcpy(code+41, pubkey.begin(), CPubKey::COMPRESSED_PUBLIC_KEY_SIZE); } void CExtPubKey::Decode(const unsigned char code[74]) { @@ -95,3 +123,35 @@ bool CExtPubKey::Derive(CExtPubKey &out, unsigned int nChild) const { out.nChild = nChild; return pubkey.Derive(out.pubkey, out.chaincode, nChild, chaincode); } + +/* static */ bool CPubKey::CheckLowS(const std::vector& vchSig) { + secp256k1_ecdsa_signature sig; + + /* Zcash, unlike Bitcoin, has always enforced strict DER signatures. */ + if (!secp256k1_ecdsa_signature_parse_der(secp256k1_context_verify, &sig, &vchSig[0], vchSig.size())) { + return false; + } + return (!secp256k1_ecdsa_signature_normalize(secp256k1_context_verify, NULL, &sig)); +} + +/* static */ int ECCVerifyHandle::refcount = 0; + +ECCVerifyHandle::ECCVerifyHandle() +{ + if (refcount == 0) { + assert(secp256k1_context_verify == NULL); + secp256k1_context_verify = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + assert(secp256k1_context_verify != NULL); + } + refcount++; +} + +ECCVerifyHandle::~ECCVerifyHandle() +{ + refcount--; + if (refcount == 0) { + assert(secp256k1_context_verify != NULL); + secp256k1_context_destroy(secp256k1_context_verify); + secp256k1_context_verify = NULL; + } +} diff --git a/src/pubkey.h b/src/pubkey.h index cce9c826e..237237e05 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -1,5 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2017 The Zcash developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -13,16 +14,6 @@ #include #include -/** - * secp256k1: - * const unsigned int PRIVATE_KEY_SIZE = 279; - * const unsigned int PUBLIC_KEY_SIZE = 65; - * const unsigned int SIGNATURE_SIZE = 72; - * - * see www.keylength.com - * script supports up to 75 for single byte push - */ - /** A reference to a CKey: the Hash160 of its serialized public key */ class CKeyID : public uint160 { @@ -36,21 +27,37 @@ typedef uint256 ChainCode; /** An encapsulated public key. */ class CPubKey { +public: + /** + * secp256k1: + */ + static const unsigned int PUBLIC_KEY_SIZE = 65; + static const unsigned int COMPRESSED_PUBLIC_KEY_SIZE = 33; + static const unsigned int SIGNATURE_SIZE = 72; + static const unsigned int COMPACT_SIGNATURE_SIZE = 65; + /** + * see www.keylength.com + * script supports up to 75 for single byte push + */ + static_assert( + PUBLIC_KEY_SIZE >= COMPRESSED_PUBLIC_KEY_SIZE, + "COMPRESSED_PUBLIC_KEY_SIZE is larger than PUBLIC_KEY_SIZE"); + private: /** * Just store the serialized data. * Its length can very cheaply be computed from the first byte. */ - unsigned char vch[65]; + unsigned char vch[PUBLIC_KEY_SIZE]; //! Compute the length of a pubkey with a given first byte. unsigned int static GetLen(unsigned char chHeader) { if (chHeader == 2 || chHeader == 3) - return 33; + return COMPRESSED_PUBLIC_KEY_SIZE; if (chHeader == 4 || chHeader == 6 || chHeader == 7) - return 65; + return PUBLIC_KEY_SIZE; return 0; } @@ -129,7 +136,7 @@ public: void Unserialize(Stream& s, int nType, int nVersion) { unsigned int len = ::ReadCompactSize(s); - if (len <= 65) { + if (len <= PUBLIC_KEY_SIZE) { s.read((char*)vch, len); } else { // invalid pubkey, skip available data @@ -168,7 +175,7 @@ public: //! Check whether this is a compressed public key. bool IsCompressed() const { - return size() == 33; + return size() == COMPRESSED_PUBLIC_KEY_SIZE; } /** @@ -177,6 +184,11 @@ public: */ bool Verify(const uint256& hash, const std::vector& vchSig) const; + /** + * Check whether a signature is normalized (lower-S). + */ + static bool CheckLowS(const std::vector& vchSig); + //! Recover a public key from a compact signature. bool RecoverCompact(const uint256& hash, const std::vector& vchSig); @@ -205,4 +217,15 @@ struct CExtPubKey { bool Derive(CExtPubKey& out, unsigned int nChild) const; }; +/** Users of this module must hold an ECCVerifyHandle. The constructor and + * destructor of these are not allowed to run in parallel, though. */ +class ECCVerifyHandle +{ + static int refcount; + +public: + ECCVerifyHandle(); + ~ECCVerifyHandle(); +}; + #endif // BITCOIN_PUBKEY_H diff --git a/src/rest.cpp b/src/rest.cpp index 1a1e5268b..eb0bf8fc4 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -405,7 +405,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) boost::split(uriParts, strUriParams, boost::is_any_of("/")); } - // throw exception in case of a empty request + // throw exception in case of an empty request std::string strRequestMutable = req->ReadBody(); if (strRequestMutable.length() == 0 && uriParts.size() == 0) return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "Error: empty request"); @@ -485,7 +485,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) if (vOutPoints.size() > MAX_GETUTXOS_OUTPOINTS) return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, strprintf("Error: max outpoints exceeded (max: %d, tried: %d)", MAX_GETUTXOS_OUTPOINTS, vOutPoints.size())); - // check spentness and form a bitmap (as well as a JSON capable human-readble string representation) + // check spentness and form a bitmap (as well as a JSON capable human-readable string representation) vector bitmap; vector outs; std::string bitmapStringRepresentation; diff --git a/src/reverselock.h b/src/reverselock.h index db5c626c9..fac1ccb79 100644 --- a/src/reverselock.h +++ b/src/reverselock.h @@ -15,10 +15,12 @@ public: explicit reverse_lock(Lock& lock) : lock(lock) { lock.unlock(); + lock.swap(templock); } - ~reverse_lock() noexcept(false) { - lock.lock(); + ~reverse_lock() { + templock.lock(); + templock.swap(lock); } private: @@ -26,6 +28,7 @@ private: reverse_lock& operator=(reverse_lock const&); Lock& lock; + Lock templock; }; #endif // BITCOIN_REVERSELOCK_H diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 9919d161d..1877c1d34 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -3,8 +3,12 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "amount.h" +#include "chain.h" +#include "chainparams.h" #include "checkpoints.h" #include "consensus/validation.h" +#include "cc/betprotocol.h" #include "main.h" #include "primitives/transaction.h" #include "rpcserver.h" @@ -74,6 +78,25 @@ double GetNetworkDifficulty(const CBlockIndex* blockindex) return GetDifficultyINTERNAL(blockindex, true); } +static UniValue ValuePoolDesc( + const std::string &name, + const boost::optional chainValue, + const boost::optional valueDelta) +{ + UniValue rv(UniValue::VOBJ); + rv.push_back(Pair("id", name)); + rv.push_back(Pair("monitored", (bool)chainValue)); + if (chainValue) { + rv.push_back(Pair("chainValue", ValueFromAmount(*chainValue))); + rv.push_back(Pair("chainValueZat", *chainValue)); + } + if (valueDelta) { + rv.push_back(Pair("valueDelta", ValueFromAmount(*valueDelta))); + rv.push_back(Pair("valueDeltaZat", *valueDelta)); + } + return rv; +} + UniValue blockheaderToJSON(const CBlockIndex* blockindex) { UniValue result(UniValue::VOBJ); @@ -135,6 +158,10 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); result.push_back(Pair("anchor", blockindex->hashAnchorEnd.GetHex())); + UniValue valuePools(UniValue::VARR); + valuePools.push_back(ValuePoolDesc("sprout", blockindex->nChainSproutValue, blockindex->nSproutValue)); + result.push_back(Pair("valuePools", valuePools)); + if (blockindex->pprev) result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); CBlockIndex *pnext = chainActive.Next(blockindex); @@ -148,7 +175,7 @@ UniValue getblockcount(const UniValue& params, bool fHelp) if (fHelp || params.size() != 0) throw runtime_error( "getblockcount\n" - "\nReturns the number of blocks in the longest block chain.\n" + "\nReturns the number of blocks in the best valid block chain.\n" "\nResult:\n" "n (numeric) The current block count\n" "\nExamples:\n" @@ -200,10 +227,9 @@ UniValue mempoolToJSON(bool fVerbose = false) { LOCK(mempool.cs); UniValue o(UniValue::VOBJ); - BOOST_FOREACH(const PAIRTYPE(uint256, CTxMemPoolEntry)& entry, mempool.mapTx) + BOOST_FOREACH(const CTxMemPoolEntry& e, mempool.mapTx) { - const uint256& hash = entry.first; - const CTxMemPoolEntry& e = entry.second; + 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()))); @@ -260,7 +286,7 @@ UniValue getrawmempool(const UniValue& params, bool fHelp) "{ (json object)\n" " \"transactionid\" : { (json object)\n" " \"size\" : n, (numeric) transaction size in bytes\n" - " \"fee\" : n, (numeric) transaction fee in bitcoins\n" + " \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\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" @@ -519,7 +545,6 @@ UniValue gettxoutsetinfo(const UniValue& params, bool fHelp) #define KOMODO_KVBINARY 2 extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; uint64_t komodo_interest(int32_t txheight,uint64_t nValue,uint32_t nLockTime,uint32_t tiptime); -//uint32_t komodo_txtime(uint256 hash); uint64_t komodo_paxprice(uint64_t *seedp,int32_t height,char *base,char *rel,uint64_t basevolume); int32_t komodo_paxprices(int32_t *heights,uint64_t *prices,int32_t max,char *base,char *rel); int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); @@ -527,22 +552,7 @@ char *bitcoin_address(char *coinaddr,uint8_t addrtype,uint8_t *pubkey_or_rmd160, //uint32_t komodo_interest_args(int32_t *txheightp,uint32_t *tiptimep,uint64_t *valuep,uint256 hash,int32_t n); int32_t komodo_minerids(uint8_t *minerids,int32_t height,int32_t width); int32_t komodo_kvsearch(uint256 *refpubkeyp,int32_t current_height,uint32_t *flagsp,int32_t *heightp,uint8_t value[IGUANA_MAXSCRIPTSIZE],uint8_t *key,int32_t keylen); -/*uint64_t conv_NXTpassword(unsigned char *mysecret,unsigned char *mypublic,uint8_t *pass,int32_t passlen); - - -UniValue passphrasewif(const UniValue& params, bool fHelp) -{ - UniValue ret(UniValue::VOBJ); char *passphrase,wifstr[64],coinaddr[64]; uint8_t tmptype,pubkey33[33]; void *ctx; uint256 privkey,pubkey; - passphrase = params[0].get_str().c_str(); - conv_NXTpassword((void *)&privkey,(void *)&pubkey,(uint8_t *)passphrase,(int32_t)strlen(passphrase)); - ctx = bitcoin_ctx(); - bitcoin_priv2pub(ctx,pubkey33,coinaddr,privkey,0,60); - bitcoin_priv2wif(0,wifstr,privkey,188); - free(ctx); - ret.push_back(Pair("address",coinaddr)); - ret.push_back(Pair("wif",wifstr)); - return ret; -}*/ +int32_t komodo_MoM(int32_t *notarized_htp,uint256 *MoMp,uint256 *kmdtxidp,int32_t nHeight,uint256 *MoMoMp,int32_t *MoMoMoffsetp,int32_t *MoMoMdepthp,int32_t *kmdstartip,int32_t *kmdendip); UniValue kvsearch(const UniValue& params, bool fHelp) { @@ -579,6 +589,136 @@ UniValue kvsearch(const UniValue& params, bool fHelp) return ret; } +/* +UniValue height_MoM(const UniValue& params, bool fHelp) +{ + int32_t height,depth,notarized_height,MoMoMdepth,MoMoMoffset,kmdstarti,kmdendi; uint256 MoM,MoMoM,kmdtxid; uint32_t timestamp = 0; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR); + if ( fHelp || params.size() != 1 ) + throw runtime_error("height_MoM height\n"); + LOCK(cs_main); + height = atoi(params[0].get_str().c_str()); + if ( height <= 0 ) + { + if ( chainActive.Tip() == 0 ) + { + ret.push_back(Pair("error",(char *)"no active chain yet")); + return(ret); + } + height = chainActive.Tip()->nHeight; + } + //fprintf(stderr,"height_MoM height.%d\n",height); + depth = komodo_MoM(¬arized_height,&MoM,&kmdtxid,height,&MoMoM,&MoMoMoffset,&MoMoMdepth,&kmdstarti,&kmdendi); + ret.push_back(Pair("coin",(char *)(ASSETCHAINS_SYMBOL[0] == 0 ? "KMD" : ASSETCHAINS_SYMBOL))); + ret.push_back(Pair("height",height)); + ret.push_back(Pair("timestamp",(uint64_t)timestamp)); + if ( depth > 0 ) + { + ret.push_back(Pair("depth",depth)); + ret.push_back(Pair("notarized_height",notarized_height)); + ret.push_back(Pair("MoM",MoM.GetHex())); + ret.push_back(Pair("kmdtxid",kmdtxid.GetHex())); + if ( ASSETCHAINS_SYMBOL[0] != 0 ) + { + ret.push_back(Pair("MoMoM",MoMoM.GetHex())); + ret.push_back(Pair("MoMoMoffset",MoMoMoffset)); + ret.push_back(Pair("MoMoMdepth",MoMoMdepth)); + ret.push_back(Pair("kmdstarti",kmdstarti)); + ret.push_back(Pair("kmdendi",kmdendi)); + } + } else ret.push_back(Pair("error",(char *)"no MoM for height")); + + return ret; +} + +UniValue txMoMproof(const UniValue& params, bool fHelp) +{ + uint256 hash, notarisationHash, MoM,MoMoM; int32_t notarisedHeight, depth; CBlockIndex* blockIndex; + std::vector branch; + int nIndex,MoMoMdepth,MoMoMoffset,kmdstarti,kmdendi; + + // parse params and get notarisation data for tx + { + if ( fHelp || params.size() != 1) + throw runtime_error("txMoMproof needs a txid"); + + hash = uint256S(params[0].get_str()); + + uint256 blockHash; + CTransaction tx; + if (!GetTransaction(hash, tx, blockHash, true)) + throw runtime_error("cannot find transaction"); + + blockIndex = mapBlockIndex[blockHash]; + + depth = komodo_MoM(¬arisedHeight, &MoM, ¬arisationHash, blockIndex->nHeight,&MoMoM,&MoMoMoffset,&MoMoMdepth,&kmdstarti,&kmdendi); + + if (!depth) + throw runtime_error("notarisation not found"); + + // index of block in MoM leaves + nIndex = notarisedHeight - blockIndex->nHeight; + } + + // build merkle chain from blocks to MoM + { + // since the merkle branch code is tied up in a block class + // and we want to make a merkle branch for something that isnt transactions + CBlock fakeBlock; + for (int i=0; ihashMerkleRoot; + CTransaction fakeTx; + // first value in CTransaction memory is it's hash + memcpy((void*)&fakeTx, mRoot.begin(), 32); + fakeBlock.vtx.push_back(fakeTx); + } + branch = fakeBlock.GetMerkleBranch(nIndex); + + // Check branch + if (MoM != CBlock::CheckMerkleBranch(blockIndex->hashMerkleRoot, branch, nIndex)) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Failed merkle block->MoM"); + } + + // Now get the tx merkle branch + { + CBlock block; + + if (fHavePruned && !(blockIndex->nStatus & BLOCK_HAVE_DATA) && blockIndex->nTx > 0) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)"); + + if(!ReadBlockFromDisk(block, blockIndex)) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); + + // Locate the transaction in the block + int nTxIndex; + for (nTxIndex = 0; nTxIndex < (int)block.vtx.size(); nTxIndex++) + if (block.vtx[nTxIndex].GetHash() == hash) + break; + + if (nTxIndex == (int)block.vtx.size()) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Error locating tx in block"); + + std::vector txBranch = block.GetMerkleBranch(nTxIndex); + + // Check branch + if (block.hashMerkleRoot != CBlock::CheckMerkleBranch(hash, txBranch, nTxIndex)) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Failed merkle tx->block"); + + // concatenate branches + nIndex = (nIndex << txBranch.size()) + nTxIndex; + branch.insert(branch.begin(), txBranch.begin(), txBranch.end()); + } + + // Check the proof + if (MoM != CBlock::CheckMerkleBranch(hash, branch, nIndex)) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Failed validating MoM"); + + // Encode and return + CDataStream ssProof(SER_NETWORK, PROTOCOL_VERSION); + ssProof << MoMProof(nIndex, branch, notarisationHash); + return HexStr(ssProof.begin(), ssProof.end()); +} +*/ + UniValue minerids(const UniValue& params, bool fHelp) { uint32_t timestamp = 0; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR); uint8_t minerids[2000],pubkeys[65][33]; int32_t i,j,n,numnotaries,tally[129]; @@ -812,24 +952,24 @@ UniValue gettxout(const UniValue& params, bool fHelp) "\nArguments:\n" "1. \"txid\" (string, required) The transaction id\n" "2. n (numeric, required) vout value\n" - "3. includemempool (boolean, optional) Whether to included the mem pool\n" + "3. includemempool (boolean, optional) Whether to include the mempool\n" "\nResult:\n" "{\n" " \"bestblock\" : \"hash\", (string) the block hash\n" " \"confirmations\" : n, (numeric) The number of confirmations\n" - " \"value\" : x.xxx, (numeric) The transaction value in btc\n" + " \"value\" : x.xxx, (numeric) The transaction value in " + CURRENCY_UNIT + "\n" " \"scriptPubKey\" : { (json object)\n" " \"asm\" : \"code\", (string) \n" " \"hex\" : \"hex\", (string) \n" " \"reqSigs\" : n, (numeric) Number of required signatures\n" " \"type\" : \"pubkeyhash\", (string) The type, eg pubkeyhash\n" - " \"addresses\" : [ (array of string) array of bitcoin addresses\n" - " \"bitcoinaddress\" (string) bitcoin address\n" + " \"addresses\" : [ (array of string) array of Zcash addresses\n" + " \"zcashaddress\" (string) Zcash address\n" " ,...\n" " ]\n" " },\n" - " \"version\" : n, (numeric) The version\n" - " \"coinbase\" : true|false (boolean) Coinbase or not\n" + " \"version\" : n, (numeric) The version\n" + " \"coinbase\" : true|false (boolean) Coinbase or not\n" "}\n" "\nExamples:\n" @@ -943,12 +1083,45 @@ static UniValue SoftForkDesc(const std::string &name, int version, CBlockIndex* return rv; } +static UniValue NetworkUpgradeDesc(const Consensus::Params& consensusParams, Consensus::UpgradeIndex idx, int height) +{ + UniValue rv(UniValue::VOBJ); + auto upgrade = NetworkUpgradeInfo[idx]; + rv.push_back(Pair("name", upgrade.strName)); + rv.push_back(Pair("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; + } + rv.push_back(Pair("info", upgrade.strInfo)); + return rv; +} + +void NetworkUpgradeDescPushBack( + UniValue& networkUpgrades, + const Consensus::Params& consensusParams, + Consensus::UpgradeIndex idx, + int height) +{ + // Network upgrades with an activation height of NO_ACTIVATION_HEIGHT are + // 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( + HexInt(NetworkUpgradeInfo[idx].nBranchId), + NetworkUpgradeDesc(consensusParams, idx, height))); + } +} + UniValue getblockchaininfo(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) throw runtime_error( "getblockchaininfo\n" "Returns an object containing various state info regarding block chain processing.\n" + "\nNote that when the chain tip is at the last block before a network upgrade activation,\n" + "consensus.chaintip != consensus.nextblock.\n" "\nResult:\n" "{\n" " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" @@ -971,7 +1144,19 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) " },\n" " \"reject\": { ... } (object) progress toward rejecting pre-softfork blocks (same fields as \"enforce\")\n" " }, ...\n" - " ]\n" + " ],\n" + " \"upgrades\": { (object) status of network upgrades\n" + " \"xxxx\" : { (string) branch ID of the upgrade\n" + " \"name\": \"xxxx\", (string) name of upgrade\n" + " \"activationheight\": xxxxxx, (numeric) block height of activation\n" + " \"status\": \"xxxx\", (string) status of upgrade\n" + " \"info\": \"xxxx\", (string) additional information about upgrade\n" + " }, ...\n" + " },\n" + " \"consensus\": { (object) branch IDs of the current and upcoming consensus rules\n" + " \"chaintip\": \"xxxxxxxx\", (string) branch ID used to validate the current chain tip\n" + " \"nextblock\": \"xxxxxxxx\" (string) branch ID that the next block will be validated under\n" + " }\n" "}\n" "\nExamples:\n" + HelpExampleCli("getblockchaininfo", "") @@ -998,14 +1183,29 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) obj.push_back(Pair("commitments", tree.size())); #endif - const Consensus::Params& consensusParams = Params().GetConsensus(); CBlockIndex* tip = chainActive.Tip(); + UniValue valuePools(UniValue::VARR); + valuePools.push_back(ValuePoolDesc("sprout", tip->nChainSproutValue, boost::none)); + obj.push_back(Pair("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)); + 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)); + + 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)); + if (fPruneMode) { CBlockIndex *block = chainActive.Tip(); diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index da8fdc4b6..799dca34f 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -103,23 +103,37 @@ static const CRPCConvertParam vRPCConvertParams[] = { "zcbenchmark", 1 }, { "zcbenchmark", 2 }, { "getblocksubsidy", 0}, - { "z_listreceivedbyaddress", 1}, + { "z_listaddresses", 0}, + { "z_listreceivedbyaddress", 1}, { "z_getbalance", 1}, { "z_gettotalbalance", 0}, + { "z_gettotalbalance", 1}, + { "z_gettotalbalance", 2}, + { "z_mergetoaddress", 0}, + { "z_mergetoaddress", 2}, + { "z_mergetoaddress", 3}, + { "z_mergetoaddress", 4}, { "z_sendmany", 1}, { "z_sendmany", 2}, { "z_sendmany", 3}, + { "z_shieldcoinbase", 2}, + { "z_shieldcoinbase", 3}, { "z_getoperationstatus", 0}, { "z_getoperationresult", 0}, { "z_importkey", 1 }, { "paxprice", 4 }, { "paxprices", 3 }, { "paxpending", 0 }, - { "notaries", 1 }, + { "notaries", 2 }, + //{ "height_MoM", 1 }, + //{ "txMoMproof", 1 }, { "minerids", 1 }, { "kvsearch", 1 }, { "kvupdate", 4 }, { "z_importkey", 2 }, + { "z_importviewingkey", 2 }, + { "z_getpaymentdisclosure", 1}, + { "z_getpaymentdisclosure", 2} }; class CRPCConvertTable @@ -156,7 +170,7 @@ UniValue ParseNonRFCJSONValue(const std::string& strVal) UniValue jVal; if (!jVal.read(std::string("[")+strVal+std::string("]")) || !jVal.isArray() || jVal.size()!=1) - throw runtime_error(string("Error parsing JSON:")+strVal); + throw runtime_error(string("Error JSON:")+strVal); return jVal[0]; } diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index f2fdc1415..d141c42c4 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -206,8 +206,13 @@ UniValue generate(const UniValue& params, bool fHelp) UniValue blockHashes(UniValue::VARR); unsigned int n = Params().EquihashN(); unsigned int k = Params().EquihashK(); + uint64_t lastTime = 0; while (nHeight < nHeightEnd) { + // Validation may fail if block generation is too fast + if (GetTime() == lastTime) MilliSleep(1001); + lastTime = GetTime(); + #ifdef ENABLE_WALLET std::unique_ptr pblocktemplate(CreateNewBlockWithKey(reservekey)); #else @@ -252,7 +257,7 @@ UniValue generate(const UniValue& params, bool fHelp) LOCK(cs_main); pblock->nSolution = soln; solutionTargetChecks.increment(); - return CheckProofOfWork(chainActive.Height(),NOTARY_PUBKEY33,pblock->GetHash(), pblock->nBits, Params().GetConsensus()); + return CheckProofOfWork(chainActive.Height(),NOTARY_PUBKEY33,pblock->GetHash(), pblock->nBits, Params().GetConsensus(),pblock->nTime); }; bool found = EhBasicSolveUncancellable(n, k, curr_state, validBlock); ehSolverRuns.increment(); diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp index e17285630..988c5d6c3 100644 --- a/src/rpcmisc.cpp +++ b/src/rpcmisc.cpp @@ -46,6 +46,7 @@ int32_t Jumblr_secretaddradd(char *secretaddr); uint64_t komodo_interestsum(); int32_t komodo_longestchain(); int32_t komodo_notarized_height(uint256 *hashp,uint256 *txidp); +uint32_t komodo_chainactive_timestamp(); int32_t komodo_whoami(char *pubkeystr,int32_t height,uint32_t timestamp); extern int32_t KOMODO_LASTMINED,JUMBLR_PAUSE; extern char ASSETCHAINS_SYMBOL[]; @@ -65,7 +66,7 @@ UniValue getinfo(const UniValue& params, bool fHelp) " \"version\": xxxxx, (numeric) the server version\n" " \"protocolversion\": xxxxx, (numeric) the protocol version\n" " \"walletversion\": xxxxx, (numeric) the wallet version\n" - " \"balance\": xxxxxxx, (numeric) the total bitcoin balance of the wallet\n" + " \"balance\": xxxxxxx, (numeric) the total Zcash 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" " \"connections\": xxxxx, (numeric) the number of connections\n" @@ -75,8 +76,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 btc/kb\n" - " \"relayfee\": x.xxxx, (numeric) minimum relay fee for non-free transactions in btc/kb\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" " \"errors\": \"...\" (string) any error messages\n" "}\n" "\nExamples:\n" @@ -249,14 +250,14 @@ UniValue validateaddress(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) throw runtime_error( - "validateaddress \"bitcoinaddress\"\n" - "\nReturn information about the given bitcoin address.\n" + "validateaddress \"zcashaddress\"\n" + "\nReturn information about the given Zcash address.\n" "\nArguments:\n" - "1. \"bitcoinaddress\" (string, required) The bitcoin address to validate\n" + "1. \"zcashaddress\" (string, required) The Zcash address to validate\n" "\nResult:\n" "{\n" " \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n" - " \"address\" : \"bitcoinaddress\", (string) The bitcoin address validated\n" + " \"address\" : \"zcashaddress\", (string) The Zcash address validated\n" " \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\n" " \"ismine\" : true|false, (boolean) If the address is yours or not\n" " \"isscript\" : true|false, (boolean) If the key is a script\n" @@ -321,7 +322,8 @@ UniValue z_validateaddress(const UniValue& params, bool fHelp) "}\n" "\nExamples:\n" - + HelpExampleCli("validateaddress", "\"zcWsmqT4X2V4jgxbgiCzyrAfRT1vi1F4sn7M5Pkh66izzw8Uk7LBGAH3DtcSMJeUb2pi3W4SQF8LMKkU2cUuVP68yAGcomL\"") + + HelpExampleCli("z_validateaddress", "\"zcWsmqT4X2V4jgxbgiCzyrAfRT1vi1F4sn7M5Pkh66izzw8Uk7LBGAH3DtcSMJeUb2pi3W4SQF8LMKkU2cUuVP68yAGcomL\"") + + HelpExampleRpc("z_validateaddress", "\"zcWsmqT4X2V4jgxbgiCzyrAfRT1vi1F4sn7M5Pkh66izzw8Uk7LBGAH3DtcSMJeUb2pi3W4SQF8LMKkU2cUuVP68yAGcomL\"") ); @@ -439,9 +441,9 @@ UniValue createmultisig(const UniValue& params, bool fHelp) "\nArguments:\n" "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n" - "2. \"keys\" (string, required) A json array of keys which are bitcoin addresses or hex-encoded public keys\n" + "2. \"keys\" (string, required) A json array of keys which are Zcash addresses or hex-encoded public keys\n" " [\n" - " \"key\" (string) bitcoin address or hex-encoded public key\n" + " \"key\" (string) Zcash address or hex-encoded public key\n" " ,...\n" " ]\n" @@ -453,9 +455,9 @@ UniValue createmultisig(const UniValue& params, bool fHelp) "\nExamples:\n" "\nCreate a multisig address from 2 addresses\n" - + HelpExampleCli("createmultisig", "2 \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + + + HelpExampleCli("createmultisig", "2 \"[\\\"t16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"t171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + "\nAs a json rpc call\n" - + HelpExampleRpc("createmultisig", "2, \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + + HelpExampleRpc("createmultisig", "2, \"[\\\"t16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"t171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") ; throw runtime_error(msg); } @@ -476,10 +478,10 @@ UniValue verifymessage(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 3) throw runtime_error( - "verifymessage \"bitcoinaddress\" \"signature\" \"message\"\n" + "verifymessage \"zcashaddress\" \"signature\" \"message\"\n" "\nVerify a signed message\n" "\nArguments:\n" - "1. \"bitcoinaddress\" (string, required) The bitcoin address to use for the signature.\n" + "1. \"zcashaddress\" (string, required) The Zcash address to use for the signature.\n" "2. \"signature\" (string, required) The signature provided by the signer in base 64 encoding (see signmessage).\n" "3. \"message\" (string, required) The message that was signed.\n" "\nResult:\n" @@ -488,11 +490,11 @@ UniValue verifymessage(const UniValue& params, bool fHelp) "\nUnlock the wallet for 30 seconds\n" + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + "\nCreate the signature\n" - + HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"my message\"") + + + HelpExampleCli("signmessage", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\" \"my message\"") + "\nVerify the signature\n" - + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"signature\" \"my message\"") + + + HelpExampleCli("verifymessage", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\" \"signature\" \"my message\"") + "\nAs json rpc\n" - + HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", \"signature\", \"my message\"") + + HelpExampleRpc("verifymessage", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", \"signature\", \"my message\"") ); LOCK(cs_main); diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index ee2c21d4d..a7a4bf051 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -13,6 +13,7 @@ #include "timedata.h" #include "util.h" #include "version.h" +#include "deprecation.h" #include @@ -141,7 +142,7 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp) obj.push_back(Pair("pingwait", stats.dPingWait)); obj.push_back(Pair("version", stats.nVersion)); // Use the sanitized form of subver here, to avoid tricksy remote peers from - // corrupting or modifiying the JSON output by putting special characters in + // 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)); @@ -164,6 +165,7 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp) return ret; } +int32_t KOMODO_LONGESTCHAIN; int32_t komodo_longestchain() { int32_t ht,n=0,num=0,maxheight=0,height = 0; @@ -190,8 +192,12 @@ int32_t komodo_longestchain() height = ht; } if ( num > (n >> 1) ) + { + KOMODO_LONGESTCHAIN = height; return(height); - else return(0); + } + KOMODO_LONGESTCHAIN = 0; + return(0); } UniValue addnode(const UniValue& params, bool fHelp) @@ -285,7 +291,7 @@ UniValue getaddednodeinfo(const UniValue& params, bool fHelp) " \"connected\" : true|false, (boolean) If connected\n" " \"addresses\" : [\n" " {\n" - " \"address\" : \"192.168.0.201:8233\", (string) The bitcoin server host and port\n" + " \"address\" : \"192.168.0.201:8233\", (string) The Zcash server host and port\n" " \"connected\" : \"outbound\" (string) connection, inbound or outbound\n" " }\n" " ,...\n" @@ -428,6 +434,33 @@ static UniValue GetNetworksInfo() return networks; } +UniValue getdeprecationinfo(const UniValue& params, bool fHelp) +{ + const CChainParams& chainparams = Params(); + if (fHelp || params.size() != 0 || chainparams.NetworkIDString() != "main") + throw runtime_error( + "getdeprecationinfo\n" + "Returns an object containing current version and deprecation block height. Applicable only on mainnet.\n" + "\nResult:\n" + "{\n" + " \"version\": xxxxx, (numeric) the server version\n" + " \"subversion\": \"/MagicBean:x.y.z[-v]/\", (string) the server subversion string\n" + " \"deprecationheight\": xxxxx, (numeric) the block height at which this version will deprecate and shut down (unless -disabledeprecation is set)\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getdeprecationinfo", "") + + HelpExampleRpc("getdeprecationinfo", "") + ); + + 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)); + + return obj; +} + UniValue getnetworkinfo(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) @@ -451,7 +484,7 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp) " }\n" " ,...\n" " ],\n" - " \"relayfee\": x.xxxxxxxx, (numeric) minimum relay fee for non-free transactions in btc/kb\n" + " \"relayfee\": x.xxxxxxxx, (numeric) minimum relay fee for non-free transactions in " + CURRENCY_UNIT + "/kB\n" " \"localaddresses\": [ (array) list of local addresses\n" " {\n" " \"address\": \"xxxx\", (string) network address\n" diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index dac9d49e8..e918df2c9 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -4,6 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "base58.h" +#include "consensus/upgrades.h" #include "consensus/validation.h" #include "core_io.h" #include "init.h" @@ -117,8 +118,15 @@ uint64_t komodo_interest(int32_t txheight,uint64_t nValue,uint32_t nLockTime,uin void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) { entry.push_back(Pair("txid", tx.GetHash().GetHex())); + entry.push_back(Pair("overwintered", tx.fOverwintered)); entry.push_back(Pair("version", tx.nVersion)); + if (tx.fOverwintered) { + entry.push_back(Pair("versiongroupid", HexInt(tx.nVersionGroupId))); + } entry.push_back(Pair("locktime", (int64_t)tx.nLockTime)); + if (tx.fOverwintered) { + entry.push_back(Pair("expiryheight", (int64_t)tx.nExpiryHeight)); + } UniValue vin(UniValue::VARR); BOOST_FOREACH(const CTxIn& txin, tx.vin) { UniValue in(UniValue::VOBJ); @@ -144,14 +152,12 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) const CTxOut& txout = tx.vout[i]; UniValue out(UniValue::VOBJ); out.push_back(Pair("value", ValueFromAmount(txout.nValue))); - if ( pindex != 0 && tx.nLockTime != 0 && (tipindex= chainActive.Tip()) != 0 ) + if ( pindex != 0 && tx.nLockTime > 500000000 && (tipindex= chainActive.Tip()) != 0 ) { - extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; interest = komodo_interest(pindex->nHeight,txout.nValue,tx.nLockTime,tipindex->nTime); - if ( 0 && strcmp("REVS",ASSETCHAINS_SYMBOL) == 0 ) - fprintf(stderr,"TxtoJSON interest %llu %.8f (%d %llu %u %u)\n",(long long)interest,(double)interest/COIN,(int32_t)pindex->nHeight,(long long)txout.nValue,(uint32_t)tx.nLockTime,(int32_t)tipindex->nTime); out.push_back(Pair("interest", ValueFromAmount(interest))); } + out.push_back(Pair("valueZat", txout.nValue)); out.push_back(Pair("n", (int64_t)i)); UniValue o(UniValue::VOBJ); ScriptPubKeyToJSON(txout.scriptPubKey, o, true); @@ -204,6 +210,7 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp) " \"txid\" : \"id\", (string) The transaction id (same as provided)\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" " \"vin\" : [ (array of json objects)\n" " {\n" " \"txid\": \"id\", (string) The transaction id\n" @@ -218,7 +225,7 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp) " ],\n" " \"vout\" : [ (array of json objects)\n" " {\n" - " \"value\" : x.xxx, (numeric) The value in btc\n" + " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" " \"n\" : n, (numeric) index\n" " \"scriptPubKey\" : { (json object)\n" " \"asm\" : \"asm\", (string) the asm\n" @@ -226,7 +233,7 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp) " \"reqSigs\" : n, (numeric) The required sigs\n" " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" " \"addresses\" : [ (json array of string)\n" - " \"bitcoinaddress\" (string) bitcoin address\n" + " \"zcashaddress\" (string) Zcash address\n" " ,...\n" " ]\n" " }\n" @@ -235,8 +242,8 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp) " ],\n" " \"vjoinsplit\" : [ (array of json objects, only for version >= 2)\n" " {\n" - " \"vpub_old\" : x.xxx, (numeric) public input value in ZEC\n" - " \"vpub_new\" : x.xxx, (numeric) public output value in ZEC\n" + " \"vpub_old\" : x.xxx, (numeric) public input value in KMD\n" + " \"vpub_new\" : x.xxx, (numeric) public output value in KMD\n" " \"anchor\" : \"hex\", (string) the anchor\n" " \"nullifiers\" : [ (json array of string)\n" " \"hex\" (string) input note nullifier\n" @@ -481,7 +488,7 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp) " ]\n" "2. \"addresses\" (string, required) a json object with addresses as keys and amounts as values\n" " {\n" - " \"address\": x.xxx (numeric, required) The key is the bitcoin address, the value is the btc amount\n" + " \"address\": x.xxx (numeric, required) The key is the Zcash address, the value is the " + CURRENCY_UNIT + " amount\n" " ,...\n" " }\n" @@ -499,7 +506,16 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp) UniValue inputs = params[0].get_array(); UniValue sendTo = params[1].get_obj(); - CMutableTransaction rawTx; + int nextBlockHeight = chainActive.Height() + 1; + CMutableTransaction rawTx = CreateNewContextualCMutableTransaction( + Params().GetConsensus(), nextBlockHeight); + + if (NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER)) { + rawTx.nExpiryHeight = nextBlockHeight + expiryDelta; + if (rawTx.nExpiryHeight >= TX_EXPIRY_HEIGHT_THRESHOLD){ + throw JSONRPCError(RPC_INVALID_PARAMETER, "nExpiryHeight must be less than TX_EXPIRY_HEIGHT_THRESHOLD."); + } + } for (size_t idx = 0; idx < inputs.size(); idx++) { const UniValue& input = inputs[idx]; @@ -523,7 +539,7 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp) BOOST_FOREACH(const string& name_, addrList) { CBitcoinAddress address(name_); if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+name_); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Zcash address: ")+name_); if (setAddress.count(address)) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_); @@ -552,8 +568,11 @@ UniValue decoderawtransaction(const UniValue& params, bool fHelp) "\nResult:\n" "{\n" " \"txid\" : \"id\", (string) The transaction id\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" " \"locktime\" : ttt, (numeric) The lock time\n" + " \"expiryheight\" : n, (numeric, optional) Last valid block height for mining transaction (Overwintered txs)\n" " \"vin\" : [ (array of json objects)\n" " {\n" " \"txid\": \"id\", (string) The transaction id\n" @@ -568,7 +587,7 @@ UniValue decoderawtransaction(const UniValue& params, bool fHelp) " ],\n" " \"vout\" : [ (array of json objects)\n" " {\n" - " \"value\" : x.xxx, (numeric) The value in btc\n" + " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" " \"n\" : n, (numeric) index\n" " \"scriptPubKey\" : { (json object)\n" " \"asm\" : \"asm\", (string) the asm\n" @@ -576,7 +595,7 @@ UniValue decoderawtransaction(const UniValue& params, bool fHelp) " \"reqSigs\" : n, (numeric) The required sigs\n" " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" " \"addresses\" : [ (json array of string)\n" - " \"12tvKAXCxZjSmdNbao16dKXC8tRWfcF5oc\" (string) bitcoin address\n" + " \"t12tvKAXCxZjSmdNbao16dKXC8tRWfcF5oc\" (string) zcash address\n" " ,...\n" " ]\n" " }\n" @@ -585,8 +604,8 @@ UniValue decoderawtransaction(const UniValue& params, bool fHelp) " ],\n" " \"vjoinsplit\" : [ (array of json objects, only for version >= 2)\n" " {\n" - " \"vpub_old\" : x.xxx, (numeric) public input value in ZEC\n" - " \"vpub_new\" : x.xxx, (numeric) public output value in ZEC\n" + " \"vpub_old\" : x.xxx, (numeric) public input value in KMD\n" + " \"vpub_new\" : x.xxx, (numeric) public output value in KMD\n" " \"anchor\" : \"hex\", (string) the anchor\n" " \"nullifiers\" : [ (json array of string)\n" " \"hex\" (string) input note nullifier\n" @@ -646,7 +665,7 @@ UniValue decodescript(const UniValue& params, bool fHelp) " \"type\":\"type\", (string) The output type\n" " \"reqSigs\": n, (numeric) The required signatures\n" " \"addresses\": [ (json array of string)\n" - " \"address\" (string) bitcoin address\n" + " \"address\" (string) Zcash address\n" " ,...\n" " ],\n" " \"p2sh\",\"address\" (string) script address\n" @@ -707,7 +726,8 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) " \"txid\":\"id\", (string, required) The transaction id\n" " \"vout\":n, (numeric, required) The output number\n" " \"scriptPubKey\": \"hex\", (string, required) script key\n" - " \"redeemScript\": \"hex\" (string, required for P2SH) redeem script\n" + " \"redeemScript\": \"hex\", (string, required for P2SH) redeem script\n" + " \"amount\": value (numeric, required) The amount spent\n" " }\n" " ,...\n" " ]\n" @@ -845,7 +865,10 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) if ((unsigned int)nOut >= coins->vout.size()) coins->vout.resize(nOut+1); coins->vout[nOut].scriptPubKey = scriptPubKey; - coins->vout[nOut].nValue = 0; // we don't know the actual output value + coins->vout[nOut].nValue = 0; + if (prevOut.exists("amount")) { + coins->vout[nOut].nValue = AmountFromValue(find_value(prevOut, "amount")); + } } // if redeemScript given and not using the local wallet (private keys @@ -888,9 +911,15 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); + // Grab the current consensus branch ID + auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); + // Script verification errors UniValue vErrors(UniValue::VARR); + // Use CTransaction for the constant parts of the + // transaction to avoid rehashing. + const CTransaction txConst(mergedTx); // Sign what we can: for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { CTxIn& txin = mergedTx.vin[i]; @@ -900,18 +929,22 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) continue; } const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey; + const CAmount& amount = coins->vout[txin.prevout.n].nValue; - txin.scriptSig.clear(); + SignatureData sigdata; // Only sign SIGHASH_SINGLE if there's a corresponding output: if (!fHashSingle || (i < mergedTx.vout.size())) - SignSignature(keystore, prevPubKey, mergedTx, i, nHashType); + ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata, consensusBranchId); // ... and merge in other signatures: BOOST_FOREACH(const CMutableTransaction& txv, txVariants) { - txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); + sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i), consensusBranchId); } + + UpdateTransaction(mergedTx, i, sigdata); + ScriptError serror = SCRIPT_ERR_OK; - if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i), &serror)) { + if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), consensusBranchId, &serror)) { TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); } } diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 94521004d..ba55be91a 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -264,6 +264,7 @@ static const CRPCCommand vRPCCommands[] = /* P2P networking */ { "network", "getnetworkinfo", &getnetworkinfo, true }, + { "network", "getdeprecationinfo", &getdeprecationinfo, true }, { "network", "addnode", &addnode, true }, { "network", "disconnectnode", &disconnectnode, true }, { "network", "getaddednodeinfo", &getaddednodeinfo, true }, @@ -295,14 +296,14 @@ static const CRPCCommand vRPCCommands[] = { "blockchain", "paxpending", &paxpending, true }, { "blockchain", "paxprices", &paxprices, true }, { "blockchain", "notaries", ¬aries, true }, + //{ "blockchain", "height_MoM", &height_MoM, true }, + //{ "blockchain", "txMoMproof", &txMoMproof, true }, { "blockchain", "minerids", &minerids, true }, { "blockchain", "kvsearch", &kvsearch, true }, { "blockchain", "kvupdate", &kvupdate, true }, /* Mining */ -#ifdef ENABLE_WALLET { "mining", "getblocktemplate", &getblocktemplate, true }, -#endif { "mining", "getmininginfo", &getmininginfo, true }, { "mining", "getlocalsolps", &getlocalsolps, true }, { "mining", "getnetworksolps", &getnetworksolps, true }, @@ -398,7 +399,9 @@ static const CRPCCommand vRPCCommands[] = { "wallet", "z_listreceivedbyaddress",&z_listreceivedbyaddress,false }, { "wallet", "z_getbalance", &z_getbalance, false }, { "wallet", "z_gettotalbalance", &z_gettotalbalance, false }, + { "wallet", "z_mergetoaddress", &z_mergetoaddress, false }, { "wallet", "z_sendmany", &z_sendmany, false }, + { "wallet", "z_shieldcoinbase", &z_shieldcoinbase, false }, { "wallet", "z_getoperationstatus", &z_getoperationstatus, true }, { "wallet", "z_getoperationresult", &z_getoperationresult, true }, { "wallet", "z_listoperationids", &z_listoperationids, true }, @@ -406,11 +409,14 @@ static const CRPCCommand vRPCCommands[] = { "wallet", "z_listaddresses", &z_listaddresses, true }, { "wallet", "z_exportkey", &z_exportkey, true }, { "wallet", "z_importkey", &z_importkey, true }, + { "wallet", "z_exportviewingkey", &z_exportviewingkey, true }, + { "wallet", "z_importviewingkey", &z_importviewingkey, true }, { "wallet", "z_exportwallet", &z_exportwallet, true }, { "wallet", "z_importwallet", &z_importwallet, true }, - - { "wallet", "paxdeposit", &paxdeposit, true }, - { "wallet", "paxwithdraw", &paxwithdraw, true } + + // TODO: rearrange into another category + { "disclosure", "z_getpaymentdisclosure", &z_getpaymentdisclosure, true }, + { "disclosure", "z_validatepaymentdisclosure", &z_validatepaymentdisclosure, true } #endif // ENABLE_WALLET }; @@ -436,169 +442,13 @@ const CRPCCommand *CRPCTable::operator[](const std::string &name) const bool StartRPC() { -/*<<<<<<< HEA - std::string addr; - int port = defaultPort; - SplitHostPort(strEndpoint, port, addr); - return ip::tcp::endpoint(boost::asio::ip::address::from_string(addr), port); -} - -void StartRPCThreads() -{ - rpc_allow_subnets.clear(); - rpc_allow_subnets.push_back(CSubNet("127.0.0.0/8")); // always allow IPv4 local subnet - rpc_allow_subnets.push_back(CSubNet("::1")); // always allow IPv6 localhost - if (mapMultiArgs.count("-rpcallowip")) - { - const vector& vAllow = mapMultiArgs["-rpcallowip"]; - BOOST_FOREACH(string strAllow, vAllow) - { - CSubNet subnet(strAllow); - if(!subnet.IsValid()) - { - uiInterface.ThreadSafeMessageBox( - strprintf("Invalid -rpcallowip subnet specification: %s. 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).", strAllow), - "", CClientUIInterface::MSG_ERROR); - StartShutdown(); - return; - } - rpc_allow_subnets.push_back(subnet); - } - } - std::string strAllowed; - BOOST_FOREACH(const CSubNet &subnet, rpc_allow_subnets) - strAllowed += subnet.ToString() + " "; - LogPrint("rpc", "Allowing RPC connections from: %s\n", strAllowed); - - if (mapArgs["-rpcpassword"] == "") - { - LogPrintf("No rpcpassword set - using random cookie authentication\n"); - if (!GenerateAuthCookie(&strRPCUserColonPass)) { - uiInterface.ThreadSafeMessageBox( - _("Error: A fatal internal error occured, see debug.log for details"), // Same message as AbortNode - "", CClientUIInterface::MSG_ERROR); - StartShutdown(); - return; - } - } else { - strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; - } - - assert(rpc_io_service == NULL); - rpc_io_service = new boost::asio::io_service(); - rpc_ssl_context = new ssl::context(*rpc_io_service, ssl::context::sslv23); - - const bool fUseSSL = GetBoolArg("-rpcssl", false); - - if (fUseSSL) - { - rpc_ssl_context->set_options(ssl::context::no_sslv2 | ssl::context::no_sslv3); - - boost::filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert")); - if (!pathCertFile.is_complete()) pathCertFile = boost::filesystem::path(GetDataDir()) / pathCertFile; - if (boost::filesystem::exists(pathCertFile)) rpc_ssl_context->use_certificate_chain_file(pathCertFile.string()); - else LogPrintf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string()); - - boost::filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem")); - if (!pathPKFile.is_complete()) pathPKFile = boost::filesystem::path(GetDataDir()) / pathPKFile; - if (boost::filesystem::exists(pathPKFile)) rpc_ssl_context->use_private_key_file(pathPKFile.string(), ssl::context::pem); - else LogPrintf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string()); - - string strCiphers = GetArg("-rpcsslciphers", "TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH"); - SSL_CTX_set_cipher_list(rpc_ssl_context->impl(), strCiphers.c_str()); - } - - std::vector vEndpoints; - bool bBindAny = false; - int defaultPort = GetArg("-rpcport", BaseParams().RPCPort()); - if (!mapArgs.count("-rpcallowip")) // Default to loopback if not allowing external IPs - { - vEndpoints.push_back(ip::tcp::endpoint(boost::asio::ip::address_v6::loopback(), defaultPort)); - vEndpoints.push_back(ip::tcp::endpoint(boost::asio::ip::address_v4::loopback(), defaultPort)); - if (mapArgs.count("-rpcbind")) - { - LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n"); - } - } else if (mapArgs.count("-rpcbind")) // Specific bind address - { - BOOST_FOREACH(const std::string &addr, mapMultiArgs["-rpcbind"]) - { - try { - vEndpoints.push_back(ParseEndpoint(addr, defaultPort)); - } - catch (const boost::system::system_error&) - { - uiInterface.ThreadSafeMessageBox( - strprintf(_("Could not parse -rpcbind value %s as network address"), addr), - "", CClientUIInterface::MSG_ERROR); - StartShutdown(); - return; - } - } - } else { // No specific bind address specified, bind to any - vEndpoints.push_back(ip::tcp::endpoint(boost::asio::ip::address_v6::any(), defaultPort)); - vEndpoints.push_back(ip::tcp::endpoint(boost::asio::ip::address_v4::any(), defaultPort)); - // Prefer making the socket dual IPv6/IPv4 instead of binding - // to both addresses separately. - bBindAny = true; - } - - bool fListening = false; - std::string strerr; - std::string straddress; - BOOST_FOREACH(const ip::tcp::endpoint &endpoint, vEndpoints) - { - try { - boost::asio::ip::address bindAddress = endpoint.address(); - straddress = bindAddress.to_string(); - LogPrintf("Binding RPC on address %s port %i (IPv4+IPv6 bind any: %i)\n", straddress, endpoint.port(), bBindAny); - boost::system::error_code v6_only_error; - boost::shared_ptr acceptor(new ip::tcp::acceptor(*rpc_io_service)); - - acceptor->open(endpoint.protocol()); - acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); - - // Try making the socket dual IPv6/IPv4 when listening on the IPv6 "any" address - acceptor->set_option(boost::asio::ip::v6_only( - !bBindAny || bindAddress != boost::asio::ip::address_v6::any()), v6_only_error); - - acceptor->bind(endpoint); - acceptor->listen(socket_base::max_connections); - - RPCListen(acceptor, *rpc_ssl_context, fUseSSL); - - fListening = true; - rpc_acceptors.push_back(acceptor); - // If dual IPv6/IPv4 bind successful, skip binding to IPv4 separately - if(bBindAny && bindAddress == boost::asio::ip::address_v6::any() && !v6_only_error) - break; - } - catch (const boost::system::system_error& e) - { - LogPrintf("ERROR: Binding RPC on address %s port %i failed: %s\n", straddress, endpoint.port(), e.what()); - strerr = strprintf(_("An error occurred while setting up the RPC address %s port %u for listening: %s"), straddress, endpoint.port(), e.what()); - } - } - - if (!fListening) { - uiInterface.ThreadSafeMessageBox(strerr, "", CClientUIInterface::MSG_ERROR); - StartShutdown(); - return; - } - - rpc_worker_group = new boost::thread_group(); - for (int i = 0; i < GetArg("-rpcthreads", 4); i++) - rpc_worker_group->create_thread(boost::bind(&boost::asio::io_service::run, rpc_io_service)); -======= LogPrint("rpc", "Starting RPC\n"); ->>>>>>> zcash/master*/ fRPCRunning = true; g_rpcSignals.Started(); // Launch one async rpc worker. The ability to launch multiple workers is not recommended at present and thus the option is disabled. - //for (int i=0; i<32; i++) - getAsyncRPCQueue()->addWorker(); -/* + getAsyncRPCQueue()->addWorker(); +/* int n = GetArg("-rpcasyncthreads", 1); if (n<1) { LogPrintf("ERROR: Invalid value %d for -rpcasyncthreads. Must be at least 1.\n", n); @@ -729,112 +579,6 @@ UniValue CRPCTable::execute(const std::string &strMethod, const UniValue ¶ms throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus); } -/*<<<<<<< HEA - if (!HTTPAuthorized(mapHeaders)) - { - LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string()); - //Deter brute-forcing We don't support exposing the RPC port, so this shouldn't result in a DoS. - MilliSleep(250); - - conn->stream() << HTTPError(HTTP_UNAUTHORIZED, false) << std::flush; - return false; - } - JSONRequest jreq; - try - { - // Parse request - Value valRequest; - if (!read_string(strRequest, valRequest)) - { - fprintf(stderr,"CANTPARSE.(%s)\n",strRequest.c_str()); - throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); - } - // Return immediately if in warmup - { - LOCK(cs_rpcWarmup); - if (fRPCInWarmup) - throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus); - } - - string strReply; - - // singleton request - if (valRequest.type() == obj_type) { - jreq.parse(valRequest); - - Value result = tableRPC.execute(jreq.strMethod, jreq.params); - - // Send reply - strReply = JSONRPCReply(result, Value::null, jreq.id); - - // array of requests - } else if (valRequest.type() == array_type) - strReply = JSONRPCExecBatch(valRequest.get_array()); - else - throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error"); - - conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, strReply.size()) << strReply << std::flush; - } - catch (const Object& objError) - { - ErrorReply(conn->stream(), objError, jreq.id); - return false; - } - catch (const std::exception& e) - { - ErrorReply(conn->stream(), JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); - return false; - } - return true; -} - -void ServiceConnection(AcceptedConnection *conn) -{ - bool fRun = true; - while (fRun && !ShutdownRequested()) - { - int nProto = 0; - map mapHeaders; - string strRequest, strMethod, strURI; - - // Read HTTP request line - if (!ReadHTTPRequestLine(conn->stream(), nProto, strMethod, strURI)) - break; - - // Read HTTP message headers and body - ReadHTTPMessage(conn->stream(), mapHeaders, strRequest, nProto, MAX_SIZE); - - // TODO #1856: Re-enable support for persistent connections. - // We have disabled support for HTTP Keep-Alive until resolution of #1680, upstream rpc deadlock. - // Close connection immediately. - fRun = false; - - // HTTP Keep-Alive is false; close connection immediately - //if ((mapHeaders["connection"] == "close") || (!GetBoolArg("-rpckeepalive", true))) - // fRun = false; - - - // Process via JSON-RPC API - if (strURI == "/") { - if (!HTTPReq_JSONRPC(conn, strRequest, mapHeaders, fRun)) - break; - - // Process via HTTP REST API - } else if (strURI.substr(0, 6) == "/rest/" && GetBoolArg("-rest", false)) { - if (!HTTPReq_REST(conn, strURI, strRequest, mapHeaders, fRun)) - break; - - } else { - conn->stream() << HTTPError(HTTP_NOT_FOUND, false) << std::flush; - break; - } - } -} - -json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array ¶ms) const -{ -======= ->>>>>>> zcash/master*/ // Find method const CRPCCommand *pcmd = tableRPC[strMethod]; if (!pcmd) @@ -857,13 +601,13 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s std::string HelpExampleCli(const std::string& methodname, const std::string& args) { - return "> zcash-cli " + methodname + " " + args + "\n"; + return "> komodo-cli " + methodname + " " + args + "\n"; } std::string HelpExampleRpc(const std::string& methodname, const std::string& args) { return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", " - "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8232/\n"; + "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:7771/\n"; } void RPCRegisterTimerInterface(RPCTimerInterface *iface) diff --git a/src/rpcserver.h b/src/rpcserver.h index f59972472..c4e0a6d1d 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -238,6 +238,7 @@ extern UniValue getinfo(const UniValue& params, bool fHelp); extern UniValue getwalletinfo(const UniValue& params, bool fHelp); extern UniValue getblockchaininfo(const UniValue& params, bool fHelp); extern UniValue getnetworkinfo(const UniValue& params, bool fHelp); +extern UniValue getdeprecationinfo(const UniValue& params, bool fHelp); extern UniValue setmocktime(const UniValue& params, bool fHelp); extern UniValue resendwallettransactions(const UniValue& params, bool fHelp); extern UniValue zc_benchmark(const UniValue& params, bool fHelp); @@ -284,6 +285,8 @@ extern UniValue getblocksubsidy(const UniValue& params, bool fHelp); extern UniValue z_exportkey(const UniValue& params, bool fHelp); // in rpcdump.cpp extern UniValue z_importkey(const UniValue& params, bool fHelp); // in rpcdump.cpp +extern UniValue z_exportviewingkey(const UniValue& params, bool fHelp); // in rpcdump.cpp +extern UniValue z_importviewingkey(const UniValue& params, bool fHelp); // in rpcdump.cpp extern UniValue z_getnewaddress(const UniValue& params, bool fHelp); // in rpcwallet.cpp extern UniValue z_listaddresses(const UniValue& params, bool fHelp); // in rpcwallet.cpp extern UniValue z_exportwallet(const UniValue& params, bool fHelp); // in rpcdump.cpp @@ -291,12 +294,18 @@ extern UniValue z_importwallet(const UniValue& params, bool fHelp); // in rpcdum extern UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp); // in rpcwallet.cpp extern UniValue z_getbalance(const UniValue& params, bool fHelp); // in rpcwallet.cpp extern UniValue z_gettotalbalance(const UniValue& params, bool fHelp); // in rpcwallet.cpp +extern UniValue z_mergetoaddress(const UniValue& params, bool fHelp); // in rpcwallet.cpp extern UniValue z_sendmany(const UniValue& params, bool fHelp); // in rpcwallet.cpp +extern UniValue z_shieldcoinbase(const UniValue& params, bool fHelp); // in rpcwallet.cpp extern UniValue z_getoperationstatus(const UniValue& params, bool fHelp); // in rpcwallet.cpp extern UniValue z_getoperationresult(const UniValue& params, bool fHelp); // in rpcwallet.cpp extern UniValue z_listoperationids(const UniValue& params, bool fHelp); // in rpcwallet.cpp extern UniValue z_validateaddress(const UniValue& params, bool fHelp); // in rpcmisc.cpp +extern UniValue z_getpaymentdisclosure(const UniValue& params, bool fHelp); // in rpcdisclosure.cpp +extern UniValue z_validatepaymentdisclosure(const UniValue ¶ms, bool fHelp); // in rpcdisclosure.cpp +//extern UniValue height_MoM(const UniValue& params, bool fHelp); +//extern UniValue txMoMproof(const UniValue& params, bool fHelp); extern UniValue notaries(const UniValue& params, bool fHelp); extern UniValue minerids(const UniValue& params, bool fHelp); extern UniValue kvsearch(const UniValue& params, bool fHelp); diff --git a/src/scheduler.cpp b/src/scheduler.cpp index 184ddc28a..8729f2a5a 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -20,13 +20,6 @@ CScheduler::~CScheduler() } -#if BOOST_VERSION < 105000 -static boost::system_time toPosixTime(const boost::chrono::system_clock::time_point& t) -{ - return boost::posix_time::from_time_t(boost::chrono::system_clock::to_time_t(t)); -} -#endif - void CScheduler::serviceQueue() { boost::unique_lock lock(newTaskMutex); @@ -45,20 +38,13 @@ void CScheduler::serviceQueue() // Wait until either there is a new task, or until // the time of the first item on the queue: -// wait_until needs boost 1.50 or later; older versions have timed_wait: -#if BOOST_VERSION < 105000 - while (!shouldStop() && !taskQueue.empty() && - newTaskScheduled.timed_wait(lock, toPosixTime(taskQueue.begin()->first))) { - // Keep waiting until timeout - } -#else // Some boost versions have a conflicting overload of wait_until that returns void. // Explicitly use a template here to avoid hitting that overload. while (!shouldStop() && !taskQueue.empty() && newTaskScheduled.wait_until<>(lock, taskQueue.begin()->first) != boost::cv_status::timeout) { // Keep waiting until timeout } -#endif + // If there are multiple threads, the queue can empty while we're waiting (another // thread may service the task we were waiting on). if (shouldStop() || taskQueue.empty()) diff --git a/src/script/cc.cpp b/src/script/cc.cpp new file mode 100644 index 000000000..965fae4b0 --- /dev/null +++ b/src/script/cc.cpp @@ -0,0 +1,137 @@ +#include "cryptoconditions/include/cryptoconditions.h" +#include "script/cc.h" + + +bool IsCryptoConditionsEnabled() +{ + return 0 != ASSETCHAINS_CC; +} + + +bool IsSupportedCryptoCondition(const CC *cond) +{ + int mask = cc_typeMask(cond); + + if (mask & ~CCEnabledTypes) return false; + + // Also require that the condition have at least one signable node + if (!(mask & CCSigningNodes)) return false; + + return true; +} + + +bool IsSignedCryptoCondition(const CC *cond) +{ + if (!cc_isFulfilled(cond)) return false; + if (1 << cc_typeId(cond) & CCSigningNodes) return true; + if (cc_typeId(cond) == CC_Threshold) + for (int i=0; isize; i++) + if (IsSignedCryptoCondition(cond->subconditions[i])) return true; + return false; +} + + +static unsigned char* CopyPubKey(CPubKey pkIn) +{ + unsigned char* pk = (unsigned char*) malloc(33); + memcpy(pk, pkIn.begin(), 33); // TODO: compressed? + return pk; +} + + +CC* CCNewThreshold(int t, std::vector v) +{ + CC *cond = cc_new(CC_Threshold); + cond->threshold = t; + cond->size = v.size(); + cond->subconditions = (CC**) calloc(v.size(), sizeof(CC*)); + memcpy(cond->subconditions, v.data(), v.size() * sizeof(CC*)); + return cond; +} + + +CC* CCNewSecp256k1(CPubKey k) +{ + CC *cond = cc_new(CC_Secp256k1); + cond->publicKey = CopyPubKey(k); + return cond; +} + + +CC* CCNewEval(std::vector code) +{ + CC *cond = cc_new(CC_Eval); + cond->code = (unsigned char*) malloc(code.size()); + memcpy(cond->code, code.data(), code.size()); + cond->codeLength = code.size(); + return cond; +} + + +CScript CCPubKey(const CC *cond) +{ + unsigned char buf[1000]; + size_t len = cc_conditionBinary(cond, buf); + return CScript() << std::vector(buf, buf+len) << OP_CHECKCRYPTOCONDITION; +} + + +CScript CCSig(const CC *cond) +{ + unsigned char buf[10000]; + size_t len = cc_fulfillmentBinary(cond, buf, 10000); + auto ffill = std::vector(buf, buf+len); + ffill.push_back(1); // SIGHASH_ALL + return CScript() << ffill; +} + + +std::string CCShowStructure(CC *cond) +{ + std::string out; + if (cc_isAnon(cond)) { + out = "A" + std::to_string(cc_typeId(cond)); + } + else if (cc_typeId(cond) == CC_Threshold) { + out += "(" + std::to_string(cond->threshold) + " of "; + for (int i=0; isize; i++) { + out += CCShowStructure(cond->subconditions[i]); + if (i < cond->size - 1) out += ","; + } + out += ")"; + } + else { + out = std::to_string(cc_typeId(cond)); + } + return out; +} + + +CC* CCPrune(CC *cond) +{ + std::vector ffillBin; + GetPushData(CCSig(cond), ffillBin); + return cc_readFulfillmentBinary(ffillBin.data(), ffillBin.size()-1); +} + + +bool GetPushData(const CScript &sig, std::vector &data) +{ + opcodetype opcode; + auto pc = sig.begin(); + if (sig.GetOp(pc, opcode, data)) return opcode > OP_0 && opcode <= OP_PUSHDATA4; + return false; +} + + +bool GetOpReturnData(const CScript &sig, std::vector &data) +{ + auto pc = sig.begin(); + opcodetype opcode; + if (sig.GetOp2(pc, opcode, NULL)) + if (opcode == OP_RETURN) + if (sig.GetOp(pc, opcode, data)) + return opcode > OP_0 && opcode <= OP_PUSHDATA4; + return false; +} diff --git a/src/script/cc.h b/src/script/cc.h new file mode 100644 index 000000000..ad1666b86 --- /dev/null +++ b/src/script/cc.h @@ -0,0 +1,83 @@ +#ifndef SCRIPT_CC_H +#define SCRIPT_CC_H + +#include "pubkey.h" +#include "script/script.h" +#include "cryptoconditions/include/cryptoconditions.h" + + +extern int32_t ASSETCHAINS_CC; +bool IsCryptoConditionsEnabled(); + +// Limit acceptable condition types +// Prefix not enabled because no current use case, ambiguity on how to combine with secp256k1 +// RSA not enabled because no current use case, not implemented +const int CCEnabledTypes = 1 << CC_Secp256k1 | \ + 1 << CC_Threshold | \ + 1 << CC_Eval | \ + 1 << CC_Preimage | \ + 1 << CC_Ed25519; + +const int CCSigningNodes = 1 << CC_Ed25519 | 1 << CC_Secp256k1; + + +/* + * Check if the server can accept the condition based on it's structure / types + */ +bool IsSupportedCryptoCondition(const CC *cond); + + +/* + * Check if crypto condition is signed. Can only accept signed conditions. + */ +bool IsSignedCryptoCondition(const CC *cond); + + +/* + * Construct crypto conditions + */ +CC* CCNewPreimage(std::vector preimage); +CC* CCNewEval(std::vector code); +CC* CCNewSecp256k1(CPubKey k); +CC* CCNewThreshold(int t, std::vector v); + + +/* + * Turn a condition into a scriptPubKey + */ +CScript CCPubKey(const CC *cond); + + +/* + * Turn a condition into a scriptSig + * + * Note: This will fail in undefined ways if the condition is missing signatures + */ +CScript CCSig(const CC *cond); + + +/* + * Produces a string showing the structure of a CC condition + */ +std::string CCShowStructure(CC *cond); + + +/* + * Take a signed CC, encode it, and decode it again. This has the effect + * of removing branches unneccesary for fulfillment. + */ +CC* CCPrune(CC *cond); + + +/* + * Get PUSHDATA from a script + */ +bool GetPushData(const CScript &sig, std::vector &data); + +/* + * Get OP_RETURN data from a script + */ +bool GetOpReturnData(const CScript &sig, std::vector &data); + + +#endif /* SCRIPT_CC_H */ diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 6c17eab70..5438102c3 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -5,14 +5,16 @@ #include "interpreter.h" +#include "consensus/upgrades.h" #include "primitives/transaction.h" #include "crypto/ripemd160.h" #include "crypto/sha1.h" #include "crypto/sha256.h" -#include "eccryptoverify.h" #include "pubkey.h" #include "script/script.h" #include "uint256.h" +#include "cryptoconditions/include/cryptoconditions.h" + using namespace std; @@ -102,7 +104,7 @@ bool static IsValidSignatureEncoding(const std::vector &sig) { // excluding the sighash byte. // * R-length: 1-byte length descriptor of the R value that follows. // * R: arbitrary-length big-endian encoded R value. It must use the shortest - // possible encoding for a positive integers (which means no null bytes at + // possible encoding for a positive integer (which means no null bytes at // the start, except a single one when the next byte has its highest bit set). // * S-length: 1-byte length descriptor of the S value that follows. // * S: arbitrary-length big-endian encoded S value. The same rules apply. @@ -165,16 +167,14 @@ bool static IsLowDERSignature(const valtype &vchSig, ScriptError* serror) { if (!IsValidSignatureEncoding(vchSig)) { return set_error(serror, SCRIPT_ERR_SIG_DER); } - unsigned int nLenR = vchSig[3]; - unsigned int nLenS = vchSig[5+nLenR]; - const unsigned char *S = &vchSig[6+nLenR]; + // https://bitcoin.stackexchange.com/a/12556: + // Also note that inside transaction signatures, an extra hashtype byte + // follows the actual signature data. + std::vector vchSigCopy(vchSig.begin(), vchSig.begin() + vchSig.size() - 1); // If the S value is above the order of the curve divided by two, its // complement modulo the order could have been used instead, which is // one byte shorter when encoded correctly. - if (!eccrypto::CheckSignatureElement(S, nLenS, true)) - return set_error(serror, SCRIPT_ERR_SIG_HIGH_S); - - return true; + return CPubKey::CheckLowS(vchSigCopy); } bool static IsDefinedHashtypeSignature(const valtype &vchSig) { @@ -194,7 +194,7 @@ bool static CheckSignatureEncoding(const valtype &vchSig, unsigned int flags, Sc if (vchSig.size() == 0) { return true; } - if ((flags & (SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC)) != 0 && !IsValidSignatureEncoding(vchSig)) { + if (!IsValidSignatureEncoding(vchSig)) { return set_error(serror, SCRIPT_ERR_SIG_DER); } else if ((flags & SCRIPT_VERIFY_LOW_S) != 0 && !IsLowDERSignature(vchSig, serror)) { // serror is set @@ -235,7 +235,13 @@ bool static CheckMinimalPush(const valtype& data, opcodetype opcode) { return true; } -bool EvalScript(vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) +bool EvalScript( + vector >& stack, + const CScript& script, + unsigned int flags, + const BaseSignatureChecker& checker, + uint32_t consensusBranchId, + ScriptError* serror) { static const CScriptNum bnZero(0); static const CScriptNum bnOne(1); @@ -832,7 +838,7 @@ bool EvalScript(vector >& stack, const CScript& script, un //serror is set return false; } - bool fSuccess = checker.CheckSig(vchSig, vchPubKey, script); + bool fSuccess = checker.CheckSig(vchSig, vchPubKey, script, consensusBranchId); popstack(stack); popstack(stack); @@ -890,7 +896,7 @@ bool EvalScript(vector >& stack, const CScript& script, un } // Check signature - bool fOk = checker.CheckSig(vchSig, vchPubKey, script); + bool fOk = checker.CheckSig(vchSig, vchPubKey, script, consensusBranchId); if (fOk) { isig++; @@ -934,6 +940,37 @@ bool EvalScript(vector >& stack, const CScript& script, un } break; + case OP_CHECKCRYPTOCONDITION: + case OP_CHECKCRYPTOCONDITIONVERIFY: + { + if (!IsCryptoConditionsEnabled()) { + goto INTERPRETER_DEFAULT; + } + + if (stack.size() < 2) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + int fResult = checker.CheckCryptoCondition(stacktop(-1), stacktop(-2), script, consensusBranchId); + if (fResult == -1) { + return set_error(serror, SCRIPT_ERR_CRYPTOCONDITION_INVALID_FULFILLMENT); + } + + popstack(stack); + popstack(stack); + + stack.push_back(fResult == 1 ? vchTrue : vchFalse); + + if (opcode == OP_CHECKCRYPTOCONDITIONVERIFY) + { + if (fResult == 1) + popstack(stack); + else + return set_error(serror, SCRIPT_ERR_CRYPTOCONDITION_VERIFY); + } + } + break; + +INTERPRETER_DEFAULT: default: return set_error(serror, SCRIPT_ERR_BAD_OPCODE); } @@ -1054,15 +1091,148 @@ public: } }; +const unsigned char ZCASH_PREVOUTS_HASH_PERSONALIZATION[crypto_generichash_blake2b_PERSONALBYTES] = + {'Z','c','a','s','h','P','r','e','v','o','u','t','H','a','s','h'}; +const unsigned char ZCASH_SEQUENCE_HASH_PERSONALIZATION[crypto_generichash_blake2b_PERSONALBYTES] = + {'Z','c','a','s','h','S','e','q','u','e','n','c','H','a','s','h'}; +const unsigned char ZCASH_OUTPUTS_HASH_PERSONALIZATION[crypto_generichash_blake2b_PERSONALBYTES] = + {'Z','c','a','s','h','O','u','t','p','u','t','s','H','a','s','h'}; +const unsigned char ZCASH_JOINSPLITS_HASH_PERSONALIZATION[crypto_generichash_blake2b_PERSONALBYTES] = + {'Z','c','a','s','h','J','S','p','l','i','t','s','H','a','s','h'}; + +uint256 GetPrevoutHash(const CTransaction& txTo) { + CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_PREVOUTS_HASH_PERSONALIZATION); + for (unsigned int n = 0; n < txTo.vin.size(); n++) { + ss << txTo.vin[n].prevout; + } + return ss.GetHash(); +} + +uint256 GetSequenceHash(const CTransaction& txTo) { + CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_SEQUENCE_HASH_PERSONALIZATION); + for (unsigned int n = 0; n < txTo.vin.size(); n++) { + ss << txTo.vin[n].nSequence; + } + return ss.GetHash(); +} + +uint256 GetOutputsHash(const CTransaction& txTo) { + CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_OUTPUTS_HASH_PERSONALIZATION); + for (unsigned int n = 0; n < txTo.vout.size(); n++) { + ss << txTo.vout[n]; + } + return ss.GetHash(); +} + +uint256 GetJoinSplitsHash(const CTransaction& txTo) { + CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_JOINSPLITS_HASH_PERSONALIZATION); + for (unsigned int n = 0; n < txTo.vjoinsplit.size(); n++) { + ss << txTo.vjoinsplit[n]; + } + ss << txTo.joinSplitPubKey; + return ss.GetHash(); +} + } // anon namespace -uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) +PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo) +{ + hashPrevouts = GetPrevoutHash(txTo); + hashSequence = GetSequenceHash(txTo); + hashOutputs = GetOutputsHash(txTo); + hashJoinSplits = GetJoinSplitsHash(txTo); +} + +SigVersion SignatureHashVersion(const CTransaction& txTo) +{ + if (txTo.fOverwintered) { + return SIGVERSION_OVERWINTER; + } else { + return SIGVERSION_SPROUT; + } +} + +uint256 SignatureHash( + const CScript& scriptCode, + const CTransaction& txTo, + unsigned int nIn, + int nHashType, + const CAmount& amount, + uint32_t consensusBranchId, + const PrecomputedTransactionData* cache) { if (nIn >= txTo.vin.size() && nIn != NOT_AN_INPUT) { // nIn out of range throw logic_error("input index is out of range"); } + auto sigversion = SignatureHashVersion(txTo); + + if (sigversion == SIGVERSION_OVERWINTER) { + uint256 hashPrevouts; + uint256 hashSequence; + uint256 hashOutputs; + uint256 hashJoinSplits; + + if (!(nHashType & SIGHASH_ANYONECANPAY)) { + hashPrevouts = cache ? cache->hashPrevouts : GetPrevoutHash(txTo); + } + + if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { + hashSequence = cache ? cache->hashSequence : GetSequenceHash(txTo); + } + + if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { + hashOutputs = cache ? cache->hashOutputs : GetOutputsHash(txTo); + } else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) { + CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_OUTPUTS_HASH_PERSONALIZATION); + ss << txTo.vout[nIn]; + hashOutputs = ss.GetHash(); + } + + if (!txTo.vjoinsplit.empty()) { + hashJoinSplits = cache ? cache->hashJoinSplits : GetJoinSplitsHash(txTo); + } + + uint32_t leConsensusBranchId = htole32(consensusBranchId); + unsigned char personalization[16] = {}; + memcpy(personalization, "ZcashSigHash", 12); + memcpy(personalization+12, &leConsensusBranchId, 4); + + CBLAKE2bWriter ss(SER_GETHASH, 0, personalization); + // Header + ss << txTo.GetHeader(); + // Version group ID + ss << txTo.nVersionGroupId; + // Input prevouts/nSequence (none/all, depending on flags) + ss << hashPrevouts; + ss << hashSequence; + // Outputs (none/one/all, depending on flags) + ss << hashOutputs; + // JoinSplits + ss << hashJoinSplits; + // Locktime + ss << txTo.nLockTime; + // Expiry height + ss << txTo.nExpiryHeight; + // Sighash type + ss << nHashType; + + // If this hash is for a transparent input signature + // (i.e. not for txTo.joinSplitSig): + if (nIn != NOT_AN_INPUT){ + // The input being signed (replacing the scriptSig with scriptCode + amount) + // The prevout may already be contained in hashPrevout, and the nSequence + // may already be contained in hashSequence. + ss << txTo.vin[nIn].prevout; + ss << scriptCode; + ss << amount; + ss << txTo.vin[nIn].nSequence; + } + + return ss.GetHash(); + } + // Check for invalid use of SIGHASH_SINGLE if ((nHashType & 0x1f) == SIGHASH_SINGLE) { if (nIn >= txTo.vout.size()) { @@ -1080,12 +1250,17 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig return ss.GetHash(); } -bool TransactionSignatureChecker::VerifySignature(const std::vector& vchSig, const CPubKey& pubkey, const uint256& sighash) const +bool TransactionSignatureChecker::VerifySignature( + const std::vector& vchSig, const CPubKey& pubkey, const uint256& sighash) const { return pubkey.Verify(sighash, vchSig); } -bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn, const vector& vchPubKey, const CScript& scriptCode) const +bool TransactionSignatureChecker::CheckSig( + const vector& vchSigIn, + const vector& vchPubKey, + const CScript& scriptCode, + uint32_t consensusBranchId) const { CPubKey pubkey(vchPubKey); if (!pubkey.IsValid()) @@ -1100,7 +1275,7 @@ bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn uint256 sighash; try { - sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType); + sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, consensusBranchId, this->txdata); } catch (logic_error ex) { return false; } @@ -1111,6 +1286,49 @@ bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn return true; } + +int TransactionSignatureChecker::CheckCryptoCondition( + const std::vector& condBin, + const std::vector& ffillBin, + const CScript& scriptCode, + uint32_t consensusBranchId) const +{ + // Hash type is one byte tacked on to the end of the fulfillment + if (ffillBin.empty()) + return false; + + CC *cond = cc_readFulfillmentBinary((unsigned char*)ffillBin.data(), ffillBin.size()-1); + if (!cond) return -1; + + if (!IsSupportedCryptoCondition(cond)) return 0; + if (!IsSignedCryptoCondition(cond)) return 0; + + uint256 sighash; + int nHashType = ffillBin.back(); + try { + sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, consensusBranchId, this->txdata); + } catch (logic_error ex) { + return 0; + } + + VerifyEval eval = [] (CC *cond, void *checker) { + return ((TransactionSignatureChecker*)checker)->CheckEvalCondition(cond); + }; + + int out = cc_verify(cond, (const unsigned char*)&sighash, 32, 0, + condBin.data(), condBin.size(), eval, (void*)this); + cc_free(cond); + return out; +} + + +int TransactionSignatureChecker::CheckEvalCondition(const CC *cond) const +{ + fprintf(stderr, "Cannot check crypto-condition Eval outside of server\n"); + return 0; +} + + bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const { // There are two times of nLockTime: lock-by-blockheight @@ -1129,7 +1347,10 @@ bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) con // Now that we know we're comparing apples-to-apples, the // comparison is a simple numeric one. if (nLockTime > (int64_t)txTo->nLockTime) + { + //fprintf(stderr,"CLTV error: nLockTime %llu > %u txTo->nLockTime\n",*(long long *)&nLockTime,(uint32_t)txTo->nLockTime); return false; + } // Finally the nLockTime feature can be disabled and thus // CHECKLOCKTIMEVERIFY bypassed if every txin has been @@ -1142,13 +1363,53 @@ bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) con // inputs, but testing just this input minimizes the data // required to prove correct CHECKLOCKTIMEVERIFY execution. if (txTo->vin[nIn].IsFinal()) + { + //fprintf(stderr,"CLTV error: nonfinal vin.%d nSequence.%u vs %u\n",(int32_t)nIn,(uint32_t)txTo->vin[nIn].nSequence,(uint32_t)std::numeric_limits::max()); return false; + } + + return true; +} + + +/* + * Allow larger opcode in case of crypto condition scriptSig + */ +bool EvalCryptoConditionSig( + vector >& stack, + const CScript& scriptSig, + ScriptError* serror) +{ + CScript::const_iterator pc = scriptSig.begin(); + opcodetype opcode; + valtype vchPushValue; + set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); + + if (!scriptSig.GetOp(pc, opcode, vchPushValue)) + return set_error(serror, SCRIPT_ERR_BAD_OPCODE); + if (opcode == 0 || opcode > OP_PUSHDATA4) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + if (pc != scriptSig.end()) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + if (vchPushValue.size() > MAX_SCRIPT_CRYPTOCONDITION_FULFILLMENT_SIZE) + return set_error(serror, SCRIPT_ERR_PUSH_SIZE); + + stack.push_back(vchPushValue); + return true; } -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) +bool VerifyScript( + const CScript& scriptSig, + const CScript& scriptPubKey, + unsigned int flags, + const BaseSignatureChecker& checker, + uint32_t consensusBranchId, + ScriptError* serror) { set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); @@ -1157,12 +1418,17 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigne } vector > stack, stackCopy; - if (!EvalScript(stack, scriptSig, flags, checker, serror)) + if (IsCryptoConditionsEnabled() && scriptPubKey.IsPayToCryptoCondition()) { + if (!EvalCryptoConditionSig(stack, scriptSig, serror)) + // serror is set + return false; + } + else if (!EvalScript(stack, scriptSig, flags, checker, consensusBranchId, serror)) // serror is set return false; if (flags & SCRIPT_VERIFY_P2SH) stackCopy = stack; - if (!EvalScript(stack, scriptPubKey, flags, checker, serror)) + if (!EvalScript(stack, scriptPubKey, flags, checker, consensusBranchId, serror)) // serror is set return false; if (stack.empty()) @@ -1189,7 +1455,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigne CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); popstack(stack); - if (!EvalScript(stack, pubKey2, flags, checker, serror)) + if (!EvalScript(stack, pubKey2, flags, checker, consensusBranchId, serror)) // serror is set return false; if (stack.empty()) diff --git a/src/script/interpreter.h b/src/script/interpreter.h index b94916fac..7aa0d5099 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -8,6 +8,7 @@ #include "script_error.h" #include "primitives/transaction.h" +#include "script/cc.h" #include #include @@ -45,7 +46,8 @@ enum SCRIPT_VERIFY_STRICTENC = (1U << 1), // Passing a non-strict-DER signature to a checksig operation causes script failure (softfork safe, BIP62 rule 1) - SCRIPT_VERIFY_DERSIG = (1U << 2), + // In Zcash this is required, and validation of non-strict-DER signatures is not implemented. + //SCRIPT_VERIFY_DERSIG = (1U << 2), // Passing a non-strict-DER signature or one with S > order/2 to a checksig operation causes script failure // (softfork safe, BIP62 rule 5). @@ -87,12 +89,36 @@ enum SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), }; -uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); +struct PrecomputedTransactionData +{ + uint256 hashPrevouts, hashSequence, hashOutputs, hashJoinSplits; + + PrecomputedTransactionData(const CTransaction& tx); +}; + +enum SigVersion +{ + SIGVERSION_SPROUT = 0, + SIGVERSION_OVERWINTER = 1, +}; + +uint256 SignatureHash( + const CScript &scriptCode, + const CTransaction& txTo, + unsigned int nIn, + int nHashType, + const CAmount& amount, + uint32_t consensusBranchId, + const PrecomputedTransactionData* cache = NULL); class BaseSignatureChecker { public: - virtual bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode) const + virtual bool CheckSig( + const std::vector& scriptSig, + const std::vector& vchPubKey, + const CScript& scriptCode, + uint32_t consensusBranchId) const { return false; } @@ -102,22 +128,39 @@ public: return false; } + virtual int CheckCryptoCondition( + const std::vector& condBin, + const std::vector& ffillBin, + const CScript& scriptCode, + uint32_t consensusBranchId) const + { + return false; + } + virtual ~BaseSignatureChecker() {} }; class TransactionSignatureChecker : public BaseSignatureChecker { -private: +protected: const CTransaction* txTo; unsigned int nIn; + const CAmount amount; + const PrecomputedTransactionData* txdata; -protected: virtual bool VerifySignature(const std::vector& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; public: - TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn) : txTo(txToIn), nIn(nInIn) {} - bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode) const; + TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(NULL) {} + TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {} + bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, uint32_t consensusBranchId) const; bool CheckLockTime(const CScriptNum& nLockTime) const; + int CheckCryptoCondition( + const std::vector& condBin, + const std::vector& ffillBin, + const CScript& scriptCode, + uint32_t consensusBranchId) const; + virtual int CheckEvalCondition(const CC *cond) const; }; class MutableTransactionSignatureChecker : public TransactionSignatureChecker @@ -126,10 +169,22 @@ private: const CTransaction txTo; public: - MutableTransactionSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn) : TransactionSignatureChecker(&txTo, nInIn), txTo(*txToIn) {} + MutableTransactionSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amount) : TransactionSignatureChecker(&txTo, nInIn, amount), txTo(*txToIn) {} }; -bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* error = NULL); -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* error = NULL); +bool EvalScript( + std::vector >& stack, + const CScript& script, + unsigned int flags, + const BaseSignatureChecker& checker, + uint32_t consensusBranchId, + ScriptError* error = NULL); +bool VerifyScript( + const CScript& scriptSig, + const CScript& scriptPubKey, + unsigned int flags, + const BaseSignatureChecker& checker, + uint32_t consensusBranchId, + ScriptError* serror = NULL); #endif // BITCOIN_SCRIPT_INTERPRETER_H diff --git a/src/script/script.cpp b/src/script/script.cpp index fd3392473..df29b6244 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -7,6 +7,8 @@ #include "tinyformat.h" #include "utilstrencodings.h" +#include "script/cc.h" +#include "cryptoconditions/include/cryptoconditions.h" namespace { inline std::string ValueString(const std::vector& vch) @@ -138,8 +140,11 @@ const char* GetOpName(opcodetype opcode) case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY"; case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG"; case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY"; + case OP_CHECKCRYPTOCONDITION : return "OP_CHECKCRYPTOCONDITION"; + case OP_CHECKCRYPTOCONDITIONVERIFY + : return "OP_CHECKCRYPTOCONDITIONVERIFY"; - // expanson + // expansion case OP_NOP1 : return "OP_NOP1"; case OP_NOP2 : return "OP_NOP2"; case OP_NOP3 : return "OP_NOP3"; @@ -220,6 +225,36 @@ bool CScript::IsPayToScriptHash() const this->at(22) == OP_EQUAL); } +bool CScript::IsPayToCryptoCondition() const +{ + const_iterator pc = this->begin(); + vector data; + opcodetype opcode; + if (this->GetOp(pc, opcode, data)) + // Sha256 conditions are <76 bytes + if (opcode > OP_0 && opcode < OP_PUSHDATA1) + if (this->GetOp(pc, opcode, data)) + if (opcode == OP_CHECKCRYPTOCONDITION) + if (pc == this->end()) + return 1; + return 0; +} + +bool CScript::MayAcceptCryptoCondition() const +{ + // Get the type mask of the condition + const_iterator pc = this->begin(); + vector data; + opcodetype opcode; + if (!this->GetOp(pc, opcode, data)) return false; + if (!(opcode > OP_0 && opcode < OP_PUSHDATA1)) return false; + CC *cond = cc_readConditionBinary(data.data(), data.size()); + if (!cond) return false; + bool out = IsSupportedCryptoCondition(cond); + cc_free(cond); + return out; +} + bool CScript::IsPushOnly() const { const_iterator pc = begin(); diff --git a/src/script/script.h b/src/script/script.h index e0e89185f..6b8c07b07 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -19,6 +19,9 @@ static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes +// Max size of pushdata in a CC sig in bytes +static const unsigned int MAX_SCRIPT_CRYPTOCONDITION_FULFILLMENT_SIZE = 2048; + // Maximum script length in bytes static const int MAX_SCRIPT_SIZE = 10000; @@ -154,6 +157,8 @@ enum opcodetype OP_CHECKSIGVERIFY = 0xad, OP_CHECKMULTISIG = 0xae, OP_CHECKMULTISIGVERIFY = 0xaf, + OP_CHECKCRYPTOCONDITION = 0xcc, + OP_CHECKCRYPTOCONDITIONVERIFY = 0xcd, // expansion OP_NOP1 = 0xb0, @@ -168,13 +173,13 @@ enum opcodetype OP_NOP9 = 0xb8, OP_NOP10 = 0xb9, - // template matching params OP_SMALLDATA = 0xf9, OP_SMALLINTEGER = 0xfa, OP_PUBKEYS = 0xfb, OP_PUBKEYHASH = 0xfd, OP_PUBKEY = 0xfe, + OP_CRYPTOCONDITION = 0xfc, OP_INVALIDOPCODE = 0xff, }; @@ -562,6 +567,8 @@ public: unsigned int GetSigOpCount(const CScript& scriptSig) const; bool IsPayToScriptHash() const; + bool IsPayToCryptoCondition() const; + bool MayAcceptCryptoCondition() const; /** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */ bool IsPushOnly() const; diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index f1aa1fb40..41c8a24e0 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -67,6 +67,8 @@ const char* ScriptErrorString(const ScriptError serror) return "NOPx reserved for soft-fork upgrades"; case SCRIPT_ERR_PUBKEYTYPE: return "Public key is neither compressed or uncompressed"; + case SCRIPT_ERR_CRYPTOCONDITION_INVALID_FULFILLMENT: + return "Crypto-Condition payload is invalid"; case SCRIPT_ERR_UNKNOWN_ERROR: case SCRIPT_ERR_ERROR_COUNT: default: break; diff --git a/src/script/script_error.h b/src/script/script_error.h index bb10b8a29..35c8fbfd8 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -52,7 +52,11 @@ typedef enum ScriptError_t /* softfork safeness */ SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS, - SCRIPT_ERR_ERROR_COUNT + SCRIPT_ERR_ERROR_COUNT, + + /* crypto-condition script errors */ + SCRIPT_ERR_CRYPTOCONDITION_VERIFY, + SCRIPT_ERR_CRYPTOCONDITION_INVALID_FULFILLMENT } ScriptError; #define SCRIPT_ERR_LAST SCRIPT_ERR_ERROR_COUNT diff --git a/src/script/sigcache.cpp b/src/script/serverchecker.cpp similarity index 66% rename from src/script/sigcache.cpp rename to src/script/serverchecker.cpp index 099b4ad0e..4de69001a 100644 --- a/src/script/sigcache.cpp +++ b/src/script/serverchecker.cpp @@ -3,7 +3,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "sigcache.h" +#include "serverchecker.h" +#include "script/cc.h" +#include "cc/eval.h" #include "pubkey.h" #include "random.h" @@ -26,13 +28,13 @@ private: //! sigdata_type is (signature hash, signature, public key): typedef boost::tuple, CPubKey> sigdata_type; std::set< sigdata_type> setValid; - boost::shared_mutex cs_sigcache; + boost::shared_mutex cs_serverchecker; public: bool Get(const uint256 &hash, const std::vector& vchSig, const CPubKey& pubKey) { - boost::shared_lock lock(cs_sigcache); + boost::shared_lock lock(cs_serverchecker); sigdata_type k(hash, vchSig, pubKey); std::set::iterator mi = setValid.find(k); @@ -45,12 +47,12 @@ public: { // DoS prevention: limit cache size to less than 10MB // (~200 bytes per cache entry times 50,000 entries) - // Since there are a maximum of 20,000 signature operations per block + // Since there can be no more than 20,000 signature operations per block // 50,000 is a reasonable default. - int64_t nMaxCacheSize = GetArg("-maxsigcachesize", 50000); + int64_t nMaxCacheSize = GetArg("-maxservercheckersize", 50000); if (nMaxCacheSize <= 0) return; - boost::unique_lock lock(cs_sigcache); + boost::unique_lock lock(cs_serverchecker); while (static_cast(setValid.size()) > nMaxCacheSize) { @@ -74,7 +76,7 @@ public: } -bool CachingTransactionSignatureChecker::VerifySignature(const std::vector& vchSig, const CPubKey& pubkey, const uint256& sighash) const +bool ServerTransactionSignatureChecker::VerifySignature(const std::vector& vchSig, const CPubKey& pubkey, const uint256& sighash) const { static CSignatureCache signatureCache; @@ -88,3 +90,17 @@ bool CachingTransactionSignatureChecker::VerifySignature(const std::vector + +class CPubKey; + +class ServerTransactionSignatureChecker : public TransactionSignatureChecker +{ +private: + bool store; + +public: + ServerTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nIn, const CAmount& amount, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nIn, amount, txdataIn), store(storeIn) {} + + bool VerifySignature(const std::vector& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; + int CheckEvalCondition(const CC *cond) const; +}; + +#endif // BITCOIN_SCRIPT_SERVERCHECKER_H diff --git a/src/script/sigcache.h b/src/script/sigcache.h deleted file mode 100644 index b299038da..000000000 --- a/src/script/sigcache.h +++ /dev/null @@ -1,26 +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 http://www.opensource.org/licenses/mit-license.php. - -#ifndef BITCOIN_SCRIPT_SIGCACHE_H -#define BITCOIN_SCRIPT_SIGCACHE_H - -#include "script/interpreter.h" - -#include - -class CPubKey; - -class CachingTransactionSignatureChecker : public TransactionSignatureChecker -{ -private: - bool store; - -public: - CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, bool storeIn=true) : TransactionSignatureChecker(txToIn, nInIn), store(storeIn) {} - - bool VerifySignature(const std::vector& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; -}; - -#endif // BITCOIN_SCRIPT_SIGCACHE_H diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 75e6c6d9d..1aade8477 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -17,9 +17,9 @@ using namespace std; typedef vector valtype; -TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), checker(txTo, nIn) {} +TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {} -bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, const CKeyID& address, const CScript& scriptCode) const +bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, const CKeyID& address, const CScript& scriptCode, uint32_t consensusBranchId) const { CKey key; if (!keystore->GetKey(address, key)) @@ -27,7 +27,7 @@ bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, uint256 hash; try { - hash = SignatureHash(scriptCode, *txTo, nIn, nHashType); + hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, consensusBranchId); } catch (logic_error ex) { return false; } @@ -38,16 +38,16 @@ bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, return true; } -static bool Sign1(const CKeyID& address, const BaseSignatureCreator& creator, const CScript& scriptCode, CScript& scriptSigRet) +static bool Sign1(const CKeyID& address, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector& ret, uint32_t consensusBranchId) { vector vchSig; - if (!creator.CreateSig(vchSig, address, scriptCode)) + if (!creator.CreateSig(vchSig, address, scriptCode, consensusBranchId)) return false; - scriptSigRet << vchSig; + ret.push_back(vchSig); return true; } -static bool SignN(const vector& multisigdata, const BaseSignatureCreator& creator, const CScript& scriptCode, CScript& scriptSigRet) +static bool SignN(const vector& multisigdata, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector& ret, uint32_t consensusBranchId) { int nSigned = 0; int nRequired = multisigdata.front()[0]; @@ -55,7 +55,7 @@ static bool SignN(const vector& multisigdata, const BaseSignatureCreato { const valtype& pubkey = multisigdata[i]; CKeyID keyID = CPubKey(pubkey).GetID(); - if (Sign1(keyID, creator, scriptCode, scriptSigRet)) + if (Sign1(keyID, creator, scriptCode, ret, consensusBranchId)) ++nSigned; } return nSigned==nRequired; @@ -68,9 +68,11 @@ static bool SignN(const vector& multisigdata, const BaseSignatureCreato * Returns false if scriptPubKey could not be completely satisfied. */ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptPubKey, - CScript& scriptSigRet, txnouttype& whichTypeRet) + std::vector& ret, txnouttype& whichTypeRet, uint32_t consensusBranchId) { - scriptSigRet.clear(); + CScript scriptRet; + uint160 h160; + ret.clear(); vector vSolutions; if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) @@ -84,85 +86,127 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP return false; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); - return Sign1(keyID, creator, scriptPubKey, scriptSigRet); + return Sign1(keyID, creator, scriptPubKey, ret, consensusBranchId); case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); - if (!Sign1(keyID, creator, scriptPubKey, scriptSigRet)) + if (!Sign1(keyID, creator, scriptPubKey, ret, consensusBranchId)) return false; else { CPubKey vch; creator.KeyStore().GetPubKey(keyID, vch); - scriptSigRet << ToByteVector(vch); + ret.push_back(ToByteVector(vch)); } return true; case TX_SCRIPTHASH: - return creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptSigRet); + if (creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptRet)) { + ret.push_back(std::vector(scriptRet.begin(), scriptRet.end())); + return true; + } + return false; case TX_MULTISIG: - scriptSigRet << OP_0; // workaround CHECKMULTISIG bug - return (SignN(vSolutions, creator, scriptPubKey, scriptSigRet)); + ret.push_back(valtype()); // workaround CHECKMULTISIG bug + return (SignN(vSolutions, creator, scriptPubKey, ret, consensusBranchId)); + + default: + return false; } - return false; } -bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPubKey, CScript& scriptSig) +static CScript PushAll(const vector& values) { + CScript result; + BOOST_FOREACH(const valtype& v, values) { + if (v.size() == 0) { + result << OP_0; + } else if (v.size() == 1 && v[0] >= 1 && v[0] <= 16) { + result << CScript::EncodeOP_N(v[0]); + } else { + result << v; + } + } + return result; +} + +bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata, uint32_t consensusBranchId) +{ + CScript script = fromPubKey; + bool solved = true; + std::vector result; txnouttype whichType; - if (!SignStep(creator, fromPubKey, scriptSig, whichType)) - return false; + solved = SignStep(creator, script, result, whichType, consensusBranchId); + CScript subscript; - if (whichType == TX_SCRIPTHASH) + if (solved && whichType == TX_SCRIPTHASH) { - // Solver returns the subscript that need to be evaluated; + // Solver returns the subscript that needs to be evaluated; // the final scriptSig is the signatures from that // and then the serialized subscript: - CScript subscript = scriptSig; - - txnouttype subType; - bool fSolved = - SignStep(creator, subscript, scriptSig, subType) && subType != TX_SCRIPTHASH; - // Append serialized subscript whether or not it is completely signed: - scriptSig << static_cast(subscript); - if (!fSolved) return false; + script = subscript = CScript(result[0].begin(), result[0].end()); + solved = solved && SignStep(creator, script, result, whichType, consensusBranchId) && whichType != TX_SCRIPTHASH; + result.push_back(std::vector(subscript.begin(), subscript.end())); } + sigdata.scriptSig = PushAll(result); + // Test solution - return VerifyScript(scriptSig, fromPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker()); + return solved && VerifyScript(sigdata.scriptSig, fromPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker(), consensusBranchId); +} + +SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn) +{ + SignatureData data; + assert(tx.vin.size() > nIn); + data.scriptSig = tx.vin[nIn].scriptSig; + return data; } -bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType) +void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const SignatureData& data) +{ + assert(tx.vin.size() > nIn); + tx.vin[nIn].scriptSig = data.scriptSig; +} + +bool SignSignature( + const CKeyStore &keystore, + const CScript& fromPubKey, + CMutableTransaction& txTo, + unsigned int nIn, + const CAmount& amount, + int nHashType, + uint32_t consensusBranchId) { assert(nIn < txTo.vin.size()); - CTxIn& txin = txTo.vin[nIn]; CTransaction txToConst(txTo); - TransactionSignatureCreator creator(&keystore, &txToConst, nIn, nHashType); + TransactionSignatureCreator creator(&keystore, &txToConst, nIn, amount, nHashType); - return ProduceSignature(creator, fromPubKey, txin.scriptSig); + SignatureData sigdata; + bool ret = ProduceSignature(creator, fromPubKey, sigdata, consensusBranchId); + UpdateTransaction(txTo, nIn, sigdata); + return ret; } -bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType) +bool SignSignature( + const CKeyStore &keystore, + const CTransaction& txFrom, + CMutableTransaction& txTo, + unsigned int nIn, + int nHashType, + uint32_t consensusBranchId) { assert(nIn < txTo.vin.size()); CTxIn& txin = txTo.vin[nIn]; assert(txin.prevout.n < txFrom.vout.size()); const CTxOut& txout = txFrom.vout[txin.prevout.n]; - return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, nHashType); + return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, txout.nValue, nHashType, consensusBranchId); } -static CScript PushAll(const vector& values) -{ - CScript result; - BOOST_FOREACH(const valtype& v, values) - result << v; - return result; -} - -static CScript CombineMultisig(const CScript& scriptPubKey, const BaseSignatureChecker& checker, +static vector CombineMultisig(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const vector& vSolutions, - const vector& sigs1, const vector& sigs2) + const vector& sigs1, const vector& sigs2, uint32_t consensusBranchId) { // Combine all the signatures we've got: set allsigs; @@ -190,7 +234,7 @@ static CScript CombineMultisig(const CScript& scriptPubKey, const BaseSignatureC if (sigs.count(pubkey)) continue; // Already got a sig for this pubkey - if (checker.CheckSig(sig, pubkey, scriptPubKey)) + if (checker.CheckSig(sig, pubkey, scriptPubKey, consensusBranchId)) { sigs[pubkey] = sig; break; @@ -199,87 +243,100 @@ static CScript CombineMultisig(const CScript& scriptPubKey, const BaseSignatureC } // Now build a merged CScript: unsigned int nSigsHave = 0; - CScript result; result << OP_0; // pop-one-too-many workaround + std::vector result; result.push_back(valtype()); // pop-one-too-many workaround for (unsigned int i = 0; i < nPubKeys && nSigsHave < nSigsRequired; i++) { if (sigs.count(vSolutions[i+1])) { - result << sigs[vSolutions[i+1]]; + result.push_back(sigs[vSolutions[i+1]]); ++nSigsHave; } } // Fill any missing with OP_0: for (unsigned int i = nSigsHave; i < nSigsRequired; i++) - result << OP_0; + result.push_back(valtype()); return result; } -static CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, +namespace +{ +struct Stacks +{ + std::vector script; + + Stacks() {} + explicit Stacks(const std::vector& scriptSigStack_) : script(scriptSigStack_) {} + explicit Stacks(const SignatureData& data, uint32_t consensusBranchId) { + EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), consensusBranchId); + } + + SignatureData Output() const { + SignatureData result; + result.scriptSig = PushAll(script); + return result; + } +}; +} + +static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const txnouttype txType, const vector& vSolutions, - vector& sigs1, vector& sigs2) + Stacks sigs1, Stacks sigs2, uint32_t consensusBranchId) { switch (txType) { case TX_NONSTANDARD: case TX_NULL_DATA: // Don't know anything about this, assume bigger one is correct: - if (sigs1.size() >= sigs2.size()) - return PushAll(sigs1); - return PushAll(sigs2); + if (sigs1.script.size() >= sigs2.script.size()) + return sigs1; + return sigs2; case TX_PUBKEY: case TX_PUBKEYHASH: // Signatures are bigger than placeholders or empty scripts: - if (sigs1.empty() || sigs1[0].empty()) - return PushAll(sigs2); - return PushAll(sigs1); + if (sigs1.script.empty() || sigs1.script[0].empty()) + return sigs2; + return sigs1; case TX_SCRIPTHASH: - if (sigs1.empty() || sigs1.back().empty()) - return PushAll(sigs2); - else if (sigs2.empty() || sigs2.back().empty()) - return PushAll(sigs1); + if (sigs1.script.empty() || sigs1.script.back().empty()) + return sigs2; + else if (sigs2.script.empty() || sigs2.script.back().empty()) + return sigs1; else { // Recur to combine: - valtype spk = sigs1.back(); + valtype spk = sigs1.script.back(); CScript pubKey2(spk.begin(), spk.end()); txnouttype txType2; vector > vSolutions2; Solver(pubKey2, txType2, vSolutions2); - sigs1.pop_back(); - sigs2.pop_back(); - CScript result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2); - result << spk; + sigs1.script.pop_back(); + sigs2.script.pop_back(); + Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, consensusBranchId); + result.script.push_back(spk); return result; } case TX_MULTISIG: - return CombineMultisig(scriptPubKey, checker, vSolutions, sigs1, sigs2); + return Stacks(CombineMultisig(scriptPubKey, checker, vSolutions, sigs1.script, sigs2.script, consensusBranchId)); + default: + return Stacks(); } - - return CScript(); } -CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, - const CScript& scriptSig1, const CScript& scriptSig2) -{ - TransactionSignatureChecker checker(&txTo, nIn); - return CombineSignatures(scriptPubKey, checker, scriptSig1, scriptSig2); -} - -CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, - const CScript& scriptSig1, const CScript& scriptSig2) +SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, + const SignatureData& scriptSig1, const SignatureData& scriptSig2, + uint32_t consensusBranchId) { txnouttype txType; vector > vSolutions; Solver(scriptPubKey, txType, vSolutions); - vector stack1; - EvalScript(stack1, scriptSig1, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker()); - vector stack2; - EvalScript(stack2, scriptSig2, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker()); - - return CombineSignatures(scriptPubKey, checker, txType, vSolutions, stack1, stack2); + return CombineSignatures( + scriptPubKey, checker, txType, vSolutions, + Stacks(scriptSig1, consensusBranchId), + Stacks(scriptSig2, consensusBranchId), + consensusBranchId).Output(); } namespace { @@ -289,7 +346,11 @@ class DummySignatureChecker : public BaseSignatureChecker public: DummySignatureChecker() {} - bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode) const + bool CheckSig( + const std::vector& scriptSig, + const std::vector& vchPubKey, + const CScript& scriptCode, + uint32_t consensusBranchId) const { return true; } @@ -302,7 +363,11 @@ const BaseSignatureChecker& DummySignatureCreator::Checker() const return dummyChecker; } -bool DummySignatureCreator::CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode) const +bool DummySignatureCreator::CreateSig( + std::vector& vchSig, + const CKeyID& keyid, + const CScript& scriptCode, + uint32_t consensusBranchId) const { // Create a dummy signature that is a valid DER-encoding vchSig.assign(72, '\000'); diff --git a/src/script/sign.h b/src/script/sign.h index 13f45007d..f531ad0d8 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -27,7 +27,7 @@ public: virtual const BaseSignatureChecker& Checker() const =0; /** Create a singular (non-script) signature. */ - virtual bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode) const =0; + virtual bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, uint32_t consensusBranchId) const =0; }; /** A signature creator for transactions. */ @@ -35,33 +35,67 @@ class TransactionSignatureCreator : public BaseSignatureCreator { const CTransaction* txTo; unsigned int nIn; int nHashType; + CAmount amount; const TransactionSignatureChecker checker; public: - TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, int nHashTypeIn=SIGHASH_ALL); + TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn=SIGHASH_ALL); const BaseSignatureChecker& Checker() const { return checker; } - bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode) const; + bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, uint32_t consensusBranchId) const; }; -/** A signature creator that just produces 72-byte empty signatyres. */ +class MutableTransactionSignatureCreator : public TransactionSignatureCreator { + CTransaction tx; + +public: + MutableTransactionSignatureCreator(const CKeyStore* keystoreIn, const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amount, int nHashTypeIn) : TransactionSignatureCreator(keystoreIn, &tx, nInIn, amount, nHashTypeIn), tx(*txToIn) {} +}; + +/** A signature creator that just produces 72-byte empty signatures. */ class DummySignatureCreator : public BaseSignatureCreator { public: DummySignatureCreator(const CKeyStore* keystoreIn) : BaseSignatureCreator(keystoreIn) {} const BaseSignatureChecker& Checker() const; - bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode) const; + bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, uint32_t consensusBranchId) const; +}; + +struct SignatureData { + CScript scriptSig; + + SignatureData() {} + explicit SignatureData(const CScript& script) : scriptSig(script) {} }; /** Produce a script signature using a generic signature creator. */ -bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& scriptPubKey, CScript& scriptSig); +bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata, uint32_t consensusBranchId); /** Produce a script signature for a transaction. */ -bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); -bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); +bool SignSignature( + const CKeyStore &keystore, + const CScript& fromPubKey, + CMutableTransaction& txTo, + unsigned int nIn, + const CAmount& amount, + int nHashType, + uint32_t consensusBranchId); +bool SignSignature( + const CKeyStore& keystore, + const CTransaction& txFrom, + CMutableTransaction& txTo, + unsigned int nIn, + int nHashType, + uint32_t consensusBranchId); /** Combine two script signatures using a generic signature checker, intelligently, possibly with OP_0 placeholders. */ -CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const CScript& scriptSig1, const CScript& scriptSig2); +SignatureData CombineSignatures( + const CScript& scriptPubKey, + const BaseSignatureChecker& checker, + const SignatureData& scriptSig1, + const SignatureData& scriptSig2, + uint32_t consensusBranchId); -/** Combine two script signatures on transactions. */ -CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CScript& scriptSig1, const CScript& scriptSig2); +/** Extract signature data from a transaction, and insert it. */ +SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn); +void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const SignatureData& data); #endif // BITCOIN_SCRIPT_SIGN_H diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 71838853d..bdba59eca 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -9,6 +9,7 @@ #include "script/script.h" #include "util.h" #include "utilstrencodings.h" +#include "script/cc.h" #include @@ -68,6 +69,17 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector + #if defined(BUILD_BITCOIN_INTERNAL) && defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" #if defined(_WIN32) @@ -46,7 +48,6 @@ enum { zcashconsensus_SCRIPT_FLAGS_VERIFY_NONE = 0, zcashconsensus_SCRIPT_FLAGS_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts - zcashconsensus_SCRIPT_FLAGS_VERIFY_DERSIG = (1U << 2), // enforce strict DER (BIP66) compliance zcashconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), // enable CHECKLOCKTIMEVERIFY (BIP65) }; diff --git a/src/secp256k1/.gitignore b/src/secp256k1/.gitignore index 076ff1295..87fea161b 100644 --- a/src/secp256k1/.gitignore +++ b/src/secp256k1/.gitignore @@ -1,9 +1,13 @@ bench_inv +bench_ecdh bench_sign bench_verify +bench_schnorr_verify bench_recover bench_internal tests +exhaustive_tests +gen_context *.exe *.so *.a @@ -22,16 +26,24 @@ config.status libtool .deps/ .dirstamp -build-aux/ *.lo *.o *~ src/libsecp256k1-config.h src/libsecp256k1-config.h.in -m4/libtool.m4 -m4/ltoptions.m4 -m4/ltsugar.m4 -m4/ltversion.m4 -m4/lt~obsolete.m4 +src/ecmult_static_context.h +build-aux/config.guess +build-aux/config.sub +build-aux/depcomp +build-aux/install-sh +build-aux/ltmain.sh +build-aux/m4/libtool.m4 +build-aux/m4/lt~obsolete.m4 +build-aux/m4/ltoptions.m4 +build-aux/m4/ltsugar.m4 +build-aux/m4/ltversion.m4 +build-aux/missing +build-aux/compile +build-aux/test-driver src/stamp-h1 libsecp256k1.pc diff --git a/src/secp256k1/.travis.yml b/src/secp256k1/.travis.yml index 0d8089cfe..243952924 100644 --- a/src/secp256k1/.travis.yml +++ b/src/secp256k1/.travis.yml @@ -6,22 +6,30 @@ addons: compiler: - clang - gcc +cache: + directories: + - src/java/guava/ env: global: - - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no ASM=no BUILD=check EXTRAFLAGS= HOST= + - 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 + - SCALAR=32bit RECOVERY=yes + - SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes - SCALAR=64bit - - FIELD=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 - FIELD=32bit ENDOMORPHISM=yes - BIGNUM=no - - BIGNUM=no ENDOMORPHISM=yes + - BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes + - BIGNUM=no STATICPRECOMPUTATION=no - BUILD=distcheck - - EXTRAFLAGS=CFLAGS=-DDETERMINISTIC + - EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC + - EXTRAFLAGS=CFLAGS=-O0 + - BUILD=check-java ECDH=yes EXPERIMENTAL=yes matrix: fast_finish: true include: @@ -51,9 +59,11 @@ matrix: 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-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR $EXTRAFLAGS $USE_HOST && make -j2 $BUILD + - ./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/Makefile.am b/src/secp256k1/Makefile.am index 5f388f3fd..c071fbe27 100644 --- a/src/secp256k1/Makefile.am +++ b/src/secp256k1/Makefile.am @@ -1,14 +1,22 @@ ACLOCAL_AMFLAGS = -I build-aux/m4 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 noinst_HEADERS = noinst_HEADERS += src/scalar.h noinst_HEADERS += src/scalar_4x64.h noinst_HEADERS += src/scalar_8x32.h +noinst_HEADERS += src/scalar_low.h noinst_HEADERS += src/scalar_impl.h noinst_HEADERS += src/scalar_4x64_impl.h 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 @@ -19,6 +27,8 @@ 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_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 @@ -30,6 +40,7 @@ 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/util.h noinst_HEADERS += src/testrand.h noinst_HEADERS += src/testrand_impl.h @@ -38,40 +49,129 @@ noinst_HEADERS += src/hash_impl.h noinst_HEADERS += src/field.h noinst_HEADERS += src/field_impl.h noinst_HEADERS += src/bench.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 + +if USE_EXTERNAL_ASM +COMMON_LIB = libsecp256k1_common.la +noinst_LTLIBRARIES = $(COMMON_LIB) +else +COMMON_LIB = +endif pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libsecp256k1.pc +if USE_EXTERNAL_ASM +if USE_ASM_ARM +libsecp256k1_common_la_SOURCES = src/asm/field_10x26_arm.s +endif +endif + libsecp256k1_la_SOURCES = src/secp256k1.c -libsecp256k1_la_CPPFLAGS = -I$(top_srcdir)/include $(SECP_INCLUDES) -libsecp256k1_la_LIBADD = $(SECP_LIBS) +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) noinst_PROGRAMS = if USE_BENCHMARK -noinst_PROGRAMS += bench_verify bench_recover bench_sign bench_internal +noinst_PROGRAMS += bench_verify bench_sign bench_internal bench_verify_SOURCES = src/bench_verify.c -bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) -bench_verify_LDFLAGS = -static -bench_recover_SOURCES = src/bench_recover.c -bench_recover_LDADD = libsecp256k1.la $(SECP_LIBS) -bench_recover_LDFLAGS = -static +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) -bench_sign_LDFLAGS = -static +bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) bench_internal_SOURCES = src/bench_internal.c -bench_internal_LDADD = $(SECP_LIBS) -bench_internal_LDFLAGS = -static -bench_internal_CPPFLAGS = $(SECP_INCLUDES) +bench_internal_LDADD = $(SECP_LIBS) $(COMMON_LIB) +bench_internal_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES) endif +TESTS = if USE_TESTS noinst_PROGRAMS += tests tests_SOURCES = src/tests.c -tests_CPPFLAGS = -DVERIFY $(SECP_INCLUDES) $(SECP_TEST_INCLUDES) -tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS) -tests_LDFLAGS = -static -pthread -TESTS = tests +tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES) +if !ENABLE_COVERAGE +tests_CPPFLAGS += -DVERIFY +endif +tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) +tests_LDFLAGS = -static +TESTS += tests +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) +if !ENABLE_COVERAGE +exhaustive_tests_CPPFLAGS += -DVERIFY +endif +exhaustive_tests_LDADD = $(SECP_LIBS) +exhaustive_tests_LDFLAGS = -static +TESTS += exhaustive_tests endif -EXTRA_DIST = autogen.sh +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_JNI + +$(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 + +.stamp-java: $(JAVA_FILES) + @echo Compiling $^ + $(AM_V_at)$(CLASSPATH_ENV) javac $^ + @touch $@ + +if USE_TESTS + +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 + +endif +endif + +if USE_ECMULT_STATIC_PRECOMPUTATION +CPPFLAGS_FOR_BUILD +=-I$(top_srcdir) +CFLAGS_FOR_BUILD += -Wall -Wextra -Wno-unused-function + +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 $@ + +$(gen_context_BIN): $(gen_context_OBJECTS) + $(CC_FOR_BUILD) $^ -o $@ + +$(libsecp256k1_la_OBJECTS): src/ecmult_static_context.h +$(tests_OBJECTS): src/ecmult_static_context.h +$(bench_internal_OBJECTS): src/ecmult_static_context.h + +src/ecmult_static_context.h: $(gen_context_BIN) + ./$(gen_context_BIN) + +CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h $(JAVAROOT)/$(JAVAORG)/*.class .stamp-java +endif + +EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h $(JAVA_FILES) + +if ENABLE_MODULE_ECDH +include src/modules/ecdh/Makefile.am.include +endif + +if ENABLE_MODULE_RECOVERY +include src/modules/recovery/Makefile.am.include +endif diff --git a/src/secp256k1/README.md b/src/secp256k1/README.md index 6095db422..8cd344ea8 100644 --- a/src/secp256k1/README.md +++ b/src/secp256k1/README.md @@ -1,7 +1,7 @@ libsecp256k1 ============ -[![Build Status](https://travis-ci.org/bitcoin/secp256k1.svg?branch=master)](https://travis-ci.org/bitcoin/secp256k1) +[![Build Status](https://travis-ci.org/bitcoin-core/secp256k1.svg?branch=master)](https://travis-ci.org/bitcoin-core/secp256k1) Optimized C library for EC operations on curve secp256k1. diff --git a/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4 b/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4 new file mode 100644 index 000000000..1fc362761 --- /dev/null +++ b/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4 @@ -0,0 +1,140 @@ +# =========================================================================== +# 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 new file mode 100644 index 000000000..77fd346a7 --- /dev/null +++ b/src/secp256k1/build-aux/m4/ax_prog_cc_for_build.m4 @@ -0,0 +1,125 @@ +# =========================================================================== +# 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 4a398d6c9..b74acb8c1 100644 --- a/src/secp256k1/build-aux/m4/bitcoin_secp.m4 +++ b/src/secp256k1/build-aux/m4/bitcoin_secp.m4 @@ -3,21 +3,20 @@ AC_DEFUN([SECP_INT128_CHECK],[ has_int128=$ac_cv_type___int128 ]) -dnl +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_MSG_CHECKING(for x86_64 assembly availability) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]],[[ uint64_t a = 11, tmp; - __asm__ __volatile__("movq $0x100000000,%1; mulq %%rsi" : "+a"(a) : "S"(tmp) : "cc", "%rdx"); + __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]) ]) dnl AC_DEFUN([SECP_OPENSSL_CHECK],[ -if test x"$use_pkgconfig" = x"yes"; then - : #NOP + 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 @@ -27,11 +26,16 @@ if test x"$use_pkgconfig" = x"yes"; then LIBS="$TEMP_LIBS" fi ]) -else - 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 = 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([[ @@ -42,6 +46,10 @@ if test x"$has_libcrypto" = x"yes" && test x"$has_openssl_ec" = x; then 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 diff --git a/src/secp256k1/configure.ac b/src/secp256k1/configure.ac index 3dc182951..e5fcbcb4e 100644 --- a/src/secp256k1/configure.ac +++ b/src/secp256k1/configure.ac @@ -17,24 +17,19 @@ PKG_PROG_PKG_CONFIG AC_PATH_TOOL(AR, ar) AC_PATH_TOOL(RANLIB, ranlib) AC_PATH_TOOL(STRIP, strip) +AX_PROG_CC_FOR_BUILD if test "x$CFLAGS" = "x"; then - CFLAGS="-O3 -g" + CFLAGS="-g" fi +AM_PROG_CC_C_O + AC_PROG_CC_C89 if test x"$ac_cv_prog_cc_c89" = x"no"; then AC_MSG_ERROR([c89 compiler support required]) fi - -case $host in - *mingw*) - use_pkgconfig=no - ;; - *) - use_pkgconfig=yes - ;; -esac +AM_PROG_AS case $host_os in *darwin*) @@ -80,22 +75,70 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])], 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" + ]) AC_ARG_ENABLE(benchmark, AS_HELP_STRING([--enable-benchmark],[compile benchmark (default is no)]), [use_benchmark=$enableval], [use_benchmark=no]) +AC_ARG_ENABLE(coverage, + AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis]), + [enable_coverage=$enableval], + [enable_coverage=no]) + AC_ARG_ENABLE(tests, AS_HELP_STRING([--enable-tests],[compile tests (default is yes)]), [use_tests=$enableval], [use_tests=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(experimental, + AS_HELP_STRING([--enable-experimental],[allow experimental configure options (default is no)]), + [use_experimental=$enableval], + [use_experimental=no]) + +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]) + 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(module_ecdh, + AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation (experimental)]), + [enable_module_ecdh=$enableval], + [enable_module_ecdh=no]) + +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]) @@ -105,8 +148,8 @@ AC_ARG_WITH([bignum], [AS_HELP_STRING([--with-bignum=gmp|no|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|no|auto] -[Specify assembly optimizations to use. Default is auto])],[req_asm=$withval], [req_asm=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]) @@ -116,6 +159,42 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_expect(0,0);}]])], [ AC_MSG_RESULT([no]) ]) +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" +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]) + fi + else + AC_MSG_RESULT([ok]) + set_precomp=yes + fi +else + set_precomp=no +fi + if test x"$req_asm" = x"auto"; then SECP_64BIT_ASM_CHECK if test x"$has_64bit_asm" = x"yes"; then @@ -133,6 +212,8 @@ else AC_MSG_ERROR([x86_64 assembly optimization requested but not available]) fi ;; + arm) + ;; no) ;; *) @@ -225,10 +306,15 @@ else fi # select assembly optimization +use_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]) ;; +arm) + use_external_asm=yes + ;; no) ;; *) @@ -283,16 +369,48 @@ esac if test x"$use_tests" = x"yes"; then SECP_OPENSSL_CHECK if test x"$has_openssl_ec" = x"yes"; 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 + 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 +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 @@ -305,22 +423,67 @@ if test x"$use_endomorphism" = x"yes"; then AC_DEFINE(USE_ENDOMORPHISM, 1, [Define this symbol to use endomorphism optimization]) 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]) +fi + +if test x"$enable_module_ecdh" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module]) +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]) +fi + AC_C_BIGENDIAN() +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]) +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]) + +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.]) + fi +fi AC_CONFIG_HEADERS([src/libsecp256k1-config.h]) 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) +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([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" diff --git a/src/secp256k1/contrib/lax_der_parsing.c b/src/secp256k1/contrib/lax_der_parsing.c new file mode 100644 index 000000000..5b141a994 --- /dev/null +++ b/src/secp256k1/contrib/lax_der_parsing.c @@ -0,0 +1,150 @@ +/********************************************************************** + * Copyright (c) 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + +#include "lax_der_parsing.h" + +int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) { + size_t rpos, rlen, spos, slen; + size_t pos = 0; + size_t lenbyte; + unsigned char tmpsig[64] = {0}; + int overflow = 0; + + /* Hack to initialize sig with a correctly-parsed but invalid signature. */ + secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig); + + /* Sequence tag byte */ + if (pos == inputlen || input[pos] != 0x30) { + return 0; + } + pos++; + + /* Sequence length bytes */ + if (pos == inputlen) { + return 0; + } + lenbyte = input[pos++]; + if (lenbyte & 0x80) { + lenbyte -= 0x80; + if (pos + lenbyte > inputlen) { + return 0; + } + pos += lenbyte; + } + + /* Integer tag byte for R */ + if (pos == inputlen || input[pos] != 0x02) { + return 0; + } + pos++; + + /* Integer length for R */ + if (pos == inputlen) { + return 0; + } + lenbyte = input[pos++]; + if (lenbyte & 0x80) { + lenbyte -= 0x80; + if (pos + lenbyte > inputlen) { + return 0; + } + while (lenbyte > 0 && input[pos] == 0) { + pos++; + lenbyte--; + } + if (lenbyte >= sizeof(size_t)) { + return 0; + } + rlen = 0; + while (lenbyte > 0) { + rlen = (rlen << 8) + input[pos]; + pos++; + lenbyte--; + } + } else { + rlen = lenbyte; + } + if (rlen > inputlen - pos) { + return 0; + } + rpos = pos; + pos += rlen; + + /* Integer tag byte for S */ + if (pos == inputlen || input[pos] != 0x02) { + return 0; + } + pos++; + + /* Integer length for S */ + if (pos == inputlen) { + return 0; + } + lenbyte = input[pos++]; + if (lenbyte & 0x80) { + lenbyte -= 0x80; + if (pos + lenbyte > inputlen) { + return 0; + } + while (lenbyte > 0 && input[pos] == 0) { + pos++; + lenbyte--; + } + if (lenbyte >= sizeof(size_t)) { + return 0; + } + slen = 0; + while (lenbyte > 0) { + slen = (slen << 8) + input[pos]; + pos++; + lenbyte--; + } + } else { + slen = lenbyte; + } + if (slen > inputlen - pos) { + return 0; + } + spos = pos; + pos += slen; + + /* Ignore leading zeroes in R */ + while (rlen > 0 && input[rpos] == 0) { + rlen--; + rpos++; + } + /* Copy R value */ + if (rlen > 32) { + overflow = 1; + } else { + memcpy(tmpsig + 32 - rlen, input + rpos, rlen); + } + + /* Ignore leading zeroes in S */ + while (slen > 0 && input[spos] == 0) { + slen--; + spos++; + } + /* Copy S value */ + if (slen > 32) { + overflow = 1; + } else { + memcpy(tmpsig + 64 - slen, input + spos, slen); + } + + if (!overflow) { + overflow = !secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig); + } + if (overflow) { + memset(tmpsig, 0, 64); + secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig); + } + return 1; +} + diff --git a/src/secp256k1/contrib/lax_der_parsing.h b/src/secp256k1/contrib/lax_der_parsing.h new file mode 100644 index 000000000..7eaf63bf6 --- /dev/null +++ b/src/secp256k1/contrib/lax_der_parsing.h @@ -0,0 +1,91 @@ +/********************************************************************** + * Copyright (c) 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/**** + * Please do not link this file directly. It is not part of the libsecp256k1 + * project and does not promise any stability in its API, functionality or + * presence. Projects which use this code should instead copy this header + * and its accompanying .c file directly into their codebase. + ****/ + +/* This file defines a function that parses DER with various errors and + * violations. This is not a part of the library itself, because the allowed + * violations are chosen arbitrarily and do not follow or establish any + * standard. + * + * In many places it matters that different implementations do not only accept + * the same set of valid signatures, but also reject the same set of signatures. + * The only means to accomplish that is by strictly obeying a standard, and not + * accepting anything else. + * + * Nonetheless, sometimes there is a need for compatibility with systems that + * use signatures which do not strictly obey DER. The snippet below shows how + * certain violations are easily supported. You may need to adapt it. + * + * Do not use this for new systems. Use well-defined DER or compact signatures + * instead if you have the choice (see secp256k1_ecdsa_signature_parse_der and + * secp256k1_ecdsa_signature_parse_compact). + * + * The supported violations are: + * - All numbers are parsed as nonnegative integers, even though X.609-0207 + * section 8.3.3 specifies that integers are always encoded as two's + * complement. + * - Integers can have length 0, even though section 8.3.1 says they can't. + * - Integers with overly long padding are accepted, violation section + * 8.3.2. + * - 127-byte long length descriptors are accepted, even though section + * 8.1.3.5.c says that they are not. + * - Trailing garbage data inside or after the signature is ignored. + * - The length descriptor of the sequence is ignored. + * + * Compared to for example OpenSSL, many violations are NOT supported: + * - Using overly long tag descriptors for the sequence or integers inside, + * violating section 8.1.2.2. + * - Encoding primitive integers as constructed values, violating section + * 8.3.1. + */ + +#ifndef SECP256K1_CONTRIB_LAX_DER_PARSING_H +#define SECP256K1_CONTRIB_LAX_DER_PARSING_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Parse a signature in "lax DER" 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: input: a 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. In addition, it will accept signatures + * which violate the DER spec in various ways. Its purpose is to allow + * validation of the Bitcoin blockchain, which includes non-DER signatures + * from before the network rules were updated to enforce DER. Note that + * the set of supported violations is a strict subset of what OpenSSL will + * accept. + * + * After the call, sig will always be initialized. If parsing failed or the + * encoded numbers are out of range, signature validation with it is + * guaranteed to fail for every message and public key. + */ +int ecdsa_signature_parse_der_lax( + 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); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_CONTRIB_LAX_DER_PARSING_H */ diff --git a/src/secp256k1/contrib/lax_der_privatekey_parsing.c b/src/secp256k1/contrib/lax_der_privatekey_parsing.c new file mode 100644 index 000000000..c2e63b4b8 --- /dev/null +++ b/src/secp256k1/contrib/lax_der_privatekey_parsing.c @@ -0,0 +1,113 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + +#include "lax_der_privatekey_parsing.h" + +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; + int lenb = 0; + int len = 0; + memset(out32, 0, 32); + /* sequence header */ + if (end < privkey+1 || *privkey != 0x30) { + return 0; + } + privkey++; + /* sequence length constructor */ + if (end < privkey+1 || !(*privkey & 0x80)) { + return 0; + } + lenb = *privkey & ~0x80; privkey++; + if (lenb < 1 || lenb > 2) { + return 0; + } + if (end < privkey+lenb) { + return 0; + } + /* sequence length */ + len = privkey[lenb-1] | (lenb > 1 ? privkey[lenb-2] << 8 : 0); + privkey += lenb; + if (end < privkey+len) { + return 0; + } + /* sequence element 0: version number (=1) */ + if (end < privkey+3 || privkey[0] != 0x02 || privkey[1] != 0x01 || privkey[2] != 0x01) { + return 0; + } + privkey += 3; + /* sequence element 1: octet string, up to 32 bytes */ + 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 (!secp256k1_ec_seckey_verify(ctx, out32)) { + memset(out32, 0, 32); + return 0; + } + return 1; +} + +int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *privkey, size_t *privkeylen, const unsigned char *key32, int compressed) { + secp256k1_pubkey pubkey; + size_t pubkeylen = 0; + if (!secp256k1_ec_pubkey_create(ctx, &pubkey, key32)) { + *privkeylen = 0; + return 0; + } + if (compressed) { + static const unsigned char begin[] = { + 0x30,0x81,0xD3,0x02,0x01,0x01,0x04,0x20 + }; + static const unsigned char middle[] = { + 0xA0,0x81,0x85,0x30,0x81,0x82,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48, + 0xCE,0x3D,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,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04, + 0x21,0x02,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,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,0xA1,0x24,0x03,0x22,0x00 + }; + unsigned char *ptr = privkey; + memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin); + memcpy(ptr, key32, 32); ptr += 32; + memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle); + pubkeylen = 33; + secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED); + ptr += pubkeylen; + *privkeylen = ptr - privkey; + } else { + static const unsigned char begin[] = { + 0x30,0x82,0x01,0x13,0x02,0x01,0x01,0x04,0x20 + }; + static const unsigned char middle[] = { + 0xA0,0x81,0xA5,0x30,0x81,0xA2,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48, + 0xCE,0x3D,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,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04, + 0x41,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,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,0xA1,0x44,0x03,0x42,0x00 + }; + unsigned char *ptr = privkey; + memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin); + memcpy(ptr, key32, 32); ptr += 32; + memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle); + pubkeylen = 65; + secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_UNCOMPRESSED); + ptr += pubkeylen; + *privkeylen = ptr - privkey; + } + return 1; +} diff --git a/src/secp256k1/contrib/lax_der_privatekey_parsing.h b/src/secp256k1/contrib/lax_der_privatekey_parsing.h new file mode 100644 index 000000000..fece261fb --- /dev/null +++ b/src/secp256k1/contrib/lax_der_privatekey_parsing.h @@ -0,0 +1,90 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/**** + * Please do not link this file directly. It is not part of the libsecp256k1 + * project and does not promise any stability in its API, functionality or + * presence. Projects which use this code should instead copy this header + * and its accompanying .c file directly into their codebase. + ****/ + +/* This file contains code snippets that parse DER private keys with + * various errors and violations. This is not a part of the library + * itself, because the allowed violations are chosen arbitrarily and + * do not follow or establish any standard. + * + * It also contains code to serialize private keys in a compatible + * manner. + * + * These functions are meant for compatibility with applications + * that require BER encoded keys. When working with secp256k1-specific + * code, the simple 32-byte private keys normally used by the + * library are sufficient. + */ + +#ifndef SECP256K1_CONTRIB_BER_PRIVATEKEY_H +#define SECP256K1_CONTRIB_BER_PRIVATEKEY_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** 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) + * 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 + * privkey will be stored. + * In: seckey: pointer to a 32-byte secret key to export. + * compressed: 1 if the key should be exported in + * compressed format, 0 otherwise + * + * This function is purely meant for compatibility with applications that + * require BER encoded keys. When working with secp256k1-specific code, the + * simple 32-byte private keys are sufficient. + * + * Note that this function does not guarantee correct DER output. It is + * guaranteed to be parsable by secp256k1_ec_privkey_import_der + */ +SECP256K1_WARN_UNUSED_RESULT int ec_privkey_export_der( + const secp256k1_context* ctx, + unsigned char *privkey, + size_t *privkeylen, + const unsigned char *seckey, + int compressed +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Import a private key in DER format. + * Returns: 1 if a private key was extracted. + * Args: ctx: pointer to a context object (cannot be NULL). + * Out: seckey: pointer to a 32-byte array for storing the private key. + * (cannot be NULL). + * In: privkey: pointer to a private key in DER format (cannot be NULL). + * privkeylen: length of the DER private key pointed to be privkey. + * + * This function will accept more than just strict DER, and even allow some BER + * violations. The public key stored inside the DER-encoded private key is not + * verified for correctness, nor are the curve parameters. Use this function + * only if you know in advance it is supposed to contain a secp256k1 private + * key. + */ +SECP256K1_WARN_UNUSED_RESULT int ec_privkey_import_der( + const secp256k1_context* ctx, + unsigned char *seckey, + const unsigned char *privkey, + size_t privkeylen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_CONTRIB_BER_PRIVATEKEY_H */ diff --git a/src/secp256k1/include/secp256k1.h b/src/secp256k1/include/secp256k1.h index 06afd4c65..3e9c098d1 100644 --- a/src/secp256k1/include/secp256k1.h +++ b/src/secp256k1/include/secp256k1.h @@ -1,9 +1,96 @@ -#ifndef _SECP256K1_ -# define _SECP256K1_ +#ifndef SECP256K1_H +#define SECP256K1_H -# ifdef __cplusplus +#ifdef __cplusplus extern "C" { -# endif +#endif + +#include + +/* These 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 + * 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, + * 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, + * counts, flags, booleans. + * 5. Opaque data pointers follow the function pointer they are to be passed to. + */ + +/** Opaque data structure that holds context information (precomputed tables etc.). + * + * 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. + * + * 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 constructed context can safely be used from multiple threads + * simultaneously, but API call 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. + * + * 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 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. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_pubkey; + +/** Opaque data structured that holds a parsed ECDSA signature. + * + * 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 the secp256k1_ecdsa_signature_serialize_* and + * secp256k1_ecdsa_signature_parse_* functions. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_ecdsa_signature; + +/** A pointer to a function to deterministically generate a nonce. + * + * Returns: 1 if a nonce was successfully generated. 0 will cause signing to fail. + * Out: nonce32: pointer to a 32-byte array to be filled by the function. + * In: msg32: the 32-byte message hash being verified (will not be NULL) + * key32: pointer to a 32-byte secret key (will not be NULL) + * algo16: pointer to a 16-byte array describing the signature + * algorithm (will be NULL for ECDSA for compatibility). + * data: Arbitrary data pointer that is passed through. + * attempt: how many iterations we have tried to find a nonce. + * This will almost always be 0, but different attempt values + * are required to result in a different nonce. + * + * Except for test cases, this function should compute some cryptographic hash of + * the message, the algorithm, the key and the attempt. + */ +typedef int (*secp256k1_nonce_function)( + unsigned char *nonce32, + const unsigned char *msg32, + const unsigned char *key32, + const unsigned char *algo16, + void *data, + unsigned int attempt +); # if !defined(SECP256K1_GNUC_PREREQ) # if defined(__GNUC__)&&defined(__GNUC_MINOR__) @@ -26,6 +113,20 @@ extern "C" { # define SECP256K1_INLINE inline # 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"))) +# else +# define SECP256K1_API +# endif +#endif + /**Warning attributes * NONNULL is not used if SECP256K1_BUILD is set to avoid the compiler optimizing out * some paranoid null checks. */ @@ -40,308 +141,481 @@ extern "C" { # define SECP256K1_ARG_NONNULL(_x) # endif -/** Opaque data structure that holds context information (precomputed tables etc.). - * Only functions that take a pointer to a non-const context require exclusive - * access to it. Multiple functions that take a pointer to a const context may - * run simultaneously. - */ -typedef struct secp256k1_context_struct secp256k1_context_t; +/** 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. */ +#define SECP256K1_FLAGS_BIT_CONTEXT_VERIFY (1 << 8) +#define SECP256K1_FLAGS_BIT_CONTEXT_SIGN (1 << 9) +#define SECP256K1_FLAGS_BIT_COMPRESSION (1 << 8) /** Flags to pass to secp256k1_context_create. */ -# define SECP256K1_CONTEXT_VERIFY (1 << 0) -# define SECP256K1_CONTEXT_SIGN (1 << 1) +#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. */ +#define SECP256K1_EC_COMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION) +#define SECP256K1_EC_UNCOMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION) + +/** Prefix byte used to tag various encoded curvepoints for specific purposes */ +#define SECP256K1_TAG_PUBKEY_EVEN 0x02 +#define SECP256K1_TAG_PUBKEY_ODD 0x03 +#define SECP256K1_TAG_PUBKEY_UNCOMPRESSED 0x04 +#define SECP256K1_TAG_PUBKEY_HYBRID_EVEN 0x06 +#define SECP256K1_TAG_PUBKEY_HYBRID_ODD 0x07 /** Create a secp256k1 context object. + * * Returns: a newly created context object. * In: flags: which parts of the context to initialize. + * + * See also secp256k1_context_randomize. */ -secp256k1_context_t* secp256k1_context_create( - int flags +SECP256K1_API secp256k1_context* secp256k1_context_create( + unsigned int flags ) SECP256K1_WARN_UNUSED_RESULT; /** Copies a secp256k1 context object. + * * Returns: a newly created context object. - * In: ctx: an existing context to copy + * Args: ctx: an existing context to copy (cannot be NULL) */ -secp256k1_context_t* secp256k1_context_clone( - const secp256k1_context_t* ctx -) SECP256K1_WARN_UNUSED_RESULT; +SECP256K1_API secp256k1_context* secp256k1_context_clone( + const secp256k1_context* ctx +) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT; /** Destroy a secp256k1 context object. + * * The context pointer may not be used afterwards. + * Args: ctx: an existing context to destroy (cannot be NULL) */ -void secp256k1_context_destroy( - secp256k1_context_t* ctx +SECP256K1_API void secp256k1_context_destroy( + secp256k1_context* ctx +); + +/** 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 + * explicitly in the header. + * + * The philosophy is that these shouldn't be dealt with through a + * specific return value, as calling code should not have branches to deal with + * the case that this code itself is broken. + * + * On the other hand, during debug stage, one would want to be informed about + * such mistakes, and the default (crashing) may be inadvisable. + * When this callback is triggered, the API function called is guaranteed not + * 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. + */ +SECP256K1_API void secp256k1_context_set_illegal_callback( + 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. + * + * This can only trigger in case of a hardware failure, miscompilation, + * memory corruption, serious bug in the library, or other error would can + * otherwise result in undefined behaviour. It will not trigger due to mere + * incorrect usage of the API (see 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. + */ +SECP256K1_API void secp256k1_context_set_error_callback( + secp256k1_context* ctx, + void (*fun)(const char* message, void* data), + const void* data +) 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. + * 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 + * inputlen: length of the array pointed to by input + * + * This function supports parsing compressed (33 bytes, header byte 0x02 or + * 0x03), uncompressed (65 bytes, header byte 0x04), or hybrid (65 bytes, header + * 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 unsigned char *input, + size_t inputlen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** 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 + * compressed==1) byte array to place the serialized key + * in. + * In/Out: outputlen: a 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 + * 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, + unsigned char *output, + size_t *outputlen, + const secp256k1_pubkey* pubkey, + unsigned int flags +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** 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 + * + * 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. + */ +SECP256K1_API int secp256k1_ecdsa_signature_parse_compact( + 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 + * 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 + * 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 unsigned char *input, + size_t inputlen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** 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 + * 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 + */ +SECP256K1_API int secp256k1_ecdsa_signature_serialize_der( + const secp256k1_context* ctx, + unsigned char *output, + size_t *outputlen, + 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 + * + * See secp256k1_ecdsa_signature_parse_compact for details about the encoding. + */ +SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact( + const secp256k1_context* ctx, + unsigned char *output64, + 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 signature - * -1: invalid public key - * -2: invalid signature - * In: ctx: a secp256k1 context object, initialized for verification. + * 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) - * sig: the signature being verified (cannot be NULL) - * siglen: the length of the signature - * pubkey: the public key to verify with (cannot be NULL) - * pubkeylen: the length of pubkey + * pubkey: pointer to an initialized public key to verify with (cannot be NULL) + * + * 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. + * + * For details, see the comments for that function. */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify( - const secp256k1_context_t* ctx, - const unsigned char *msg32, - const unsigned char *sig, - int siglen, - const unsigned char *pubkey, - int pubkeylen -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify( + const secp256k1_context* ctx, + const secp256k1_ecdsa_signature *sig, + const unsigned char *msg32, + const secp256k1_pubkey *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); -/** A pointer to a function to deterministically generate a nonce. - * Returns: 1 if a nonce was successfully generated. 0 will cause signing to fail. - * In: msg32: the 32-byte message hash being verified (will not be NULL) - * key32: pointer to a 32-byte secret key (will not be NULL) - * attempt: how many iterations we have tried to find a nonce. - * This will almost always be 0, but different attempt values - * are required to result in a different nonce. - * data: Arbitrary data pointer that is passed through. - * Out: nonce32: pointer to a 32-byte array to be filled by the function. - * Except for test cases, this function should compute some cryptographic hash of - * the message, the key and the attempt. +/** 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, + * 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) + * + * 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 + * is done by negating the S value modulo the order of the curve, 'flipping' + * the sign of the random point R which is not included in the signature. + * + * Forgery of the same message isn't universally problematic, but in systems + * where message malleability or uniqueness of signatures is important this can + * cause issues. This forgery can be blocked by all verifiers forcing signers + * to use a normalized form. + * + * The lower-S form reduces the size of signatures slightly on average when + * variable length encodings (such as DER) are used and is cheap to verify, + * making it a good choice. Security of always using lower-S is assured because + * anyone can trivially modify a signature after the fact to enforce this + * property anyway. + * + * The lower S value is always between 0x1 and + * 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, + * inclusive. + * + * No other forms of ECDSA malleability are known and none seem likely, but + * there is no formal proof that ECDSA, even with this additional restriction, + * is free of other malleability. Commonly used serialization schemes will also + * accept various non-unique encodings, so care should be taken when this + * property is required for an application. + * + * The secp256k1_ecdsa_sign function will by default create signatures in the + * lower-S form, and secp256k1_ecdsa_verify will not accept others. In case + * signatures come from a system that cannot enforce this property, + * secp256k1_ecdsa_signature_normalize must be called before verification. */ -typedef int (*secp256k1_nonce_function_t)( - unsigned char *nonce32, - const unsigned char *msg32, - const unsigned char *key32, - unsigned int attempt, - const void *data -); +SECP256K1_API int secp256k1_ecdsa_signature_normalize( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature *sigout, + const secp256k1_ecdsa_signature *sigin +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3); /** An implementation of RFC6979 (using HMAC-SHA256) as nonce generation function. * If a data pointer is passed, it is assumed to be a pointer to 32 bytes of * extra entropy. */ -extern const secp256k1_nonce_function_t secp256k1_nonce_function_rfc6979; +SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_rfc6979; /** A default safe nonce generation function (currently equal to secp256k1_nonce_function_rfc6979). */ -extern const secp256k1_nonce_function_t secp256k1_nonce_function_default; - +SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_default; /** Create an ECDSA signature. + * * Returns: 1: signature created - * 0: the nonce generation function failed, the private key was invalid, or there is not - * enough space in the signature (as indicated by siglen). - * In: ctx: pointer to a context object, initialized for signing (cannot be NULL) - * 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 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/Out: siglen: pointer to an int with the length of sig, which will be updated - * to contain the actual signature length (<=72). - * - * The sig always has an s value in the lower half of the range (From 0x1 - * to 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, - * inclusive), unlike many other implementations. - * With ECDSA a third-party can can forge a second distinct signature - * of the same message given a single initial signature without knowing - * the key by setting s to its additive inverse mod-order, 'flipping' the - * sign of the random point R which is not included in the signature. - * Since the forgery is of the same message this isn't universally - * problematic, but in systems where message malleability or uniqueness - * of signatures is important this can cause issues. This forgery can be - * blocked by all verifiers forcing signers to use a canonical form. The - * lower-S form reduces the size of signatures slightly on average when - * variable length encodings (such as DER) are used and is cheap to - * verify, making it a good choice. Security of always using lower-S is - * assured because anyone can trivially modify a signature after the - * fact to enforce this property. Adjusting it inside the signing - * function avoids the need to re-serialize or have curve specific - * constants outside of the library. By always using a canonical form - * even in applications where it isn't needed it becomes possible to - * impose a requirement later if a need is discovered. - * No other forms of ECDSA malleability are known and none seem likely, - * but there is no formal proof that ECDSA, even with this additional - * restriction, is free of other malleability. Commonly used serialization - * schemes will also accept various non-unique encodings, so care should - * be taken when this property is required for an application. - */ -int secp256k1_ecdsa_sign( - const secp256k1_context_t* ctx, - const unsigned char *msg32, - unsigned char *sig, - int *siglen, - const unsigned char *seckey, - secp256k1_nonce_function_t noncefp, - const void *ndata -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); - -/** Create a compact ECDSA signature (64 byte + recovery id). - * Returns: 1: signature created - * 0: the nonce generation function failed, or the secret key was invalid. - * In: ctx: pointer to a context object, initialized for signing (cannot be NULL) - * msg32: the 32-byte message hash being signed (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) - * Out: sig: pointer to a 64-byte array where the signature will be placed (cannot be NULL) - * In case 0 is returned, the returned signature length will be zero. - * recid: pointer to an int, which will be updated to contain the recovery id (can be NULL) + * + * The created signature is always in lower-S form. See + * secp256k1_ecdsa_signature_normalize for more details. */ -int secp256k1_ecdsa_sign_compact( - const secp256k1_context_t* ctx, - const unsigned char *msg32, - unsigned char *sig64, - const unsigned char *seckey, - secp256k1_nonce_function_t noncefp, - const void *ndata, - int *recid +SECP256K1_API int secp256k1_ecdsa_sign( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature *sig, + const unsigned char *msg32, + 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); -/** Recover an ECDSA public key from a compact signature. - * Returns: 1: public key successfully recovered (which guarantees a correct signature). - * 0: otherwise. - * In: ctx: pointer to a context object, initialized for verification (cannot be NULL) - * msg32: the 32-byte message hash assumed to be signed (cannot be NULL) - * sig64: signature as 64 byte array (cannot be NULL) - * compressed: whether to recover a compressed or uncompressed pubkey - * recid: the recovery id (0-3, as returned by ecdsa_sign_compact) - * Out: pubkey: pointer to a 33 or 65 byte array to put the pubkey (cannot be NULL) - * pubkeylen: pointer to an int that will contain the pubkey length (cannot be NULL) - */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_recover_compact( - const secp256k1_context_t* ctx, - const unsigned char *msg32, - const unsigned char *sig64, - unsigned char *pubkey, - int *pubkeylen, - int compressed, - int recid -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); - /** Verify an ECDSA secret key. + * * Returns: 1: secret key is valid * 0: secret key is invalid - * In: ctx: pointer to a context object (cannot be NULL) - * seckey: pointer to a 32-byte secret key (cannot be NULL) - */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_verify( - const secp256k1_context_t* ctx, - const unsigned char *seckey -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); - -/** Just validate a public key. - * Returns: 1: public key is valid - * 0: public key is invalid - * In: ctx: pointer to a context object (cannot be NULL) - * pubkey: pointer to a 33-byte or 65-byte public key (cannot be NULL). - * pubkeylen: length of pubkey + * Args: ctx: pointer to a context object (cannot be NULL) + * In: seckey: pointer to a 32-byte secret key (cannot be NULL) */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_verify( - const secp256k1_context_t* ctx, - const unsigned char *pubkey, - int pubkeylen +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_verify( + const secp256k1_context* ctx, + const unsigned char *seckey ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); /** Compute the public key for a secret key. - * In: ctx: pointer to a context object, initialized for signing (cannot be NULL) - * compressed: whether the computed public key should be compressed - * seckey: pointer to a 32-byte private key (cannot be NULL) - * Out: pubkey: pointer to a 33-byte (if compressed) or 65-byte (if uncompressed) - * area to store the public key (cannot be NULL) - * pubkeylen: pointer to int that will be updated to contains the pubkey's - * length (cannot be NULL) + * * 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) */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create( - const secp256k1_context_t* ctx, - unsigned char *pubkey, - int *pubkeylen, - const unsigned char *seckey, - int compressed -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const unsigned char *seckey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -/** Decompress a public key. - * In: ctx: pointer to a context object (cannot be NULL) - * In/Out: pubkey: pointer to a 65-byte array to put the decompressed public key. - * It must contain a 33-byte or 65-byte public key already (cannot be NULL) - * pubkeylen: pointer to the size of the public key pointed to by pubkey (cannot be NULL) - * It will be updated to reflect the new size. - * Returns: 0: pubkey was invalid - * 1: pubkey was valid, and was replaced with its decompressed version +/** Negates a private 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) */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_decompress( - const secp256k1_context_t* ctx, - unsigned char *pubkey, - int *pubkeylen -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); +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); -/** Export a private key in DER format. - * In: ctx: pointer to a context object, initialized for signing (cannot be NULL) +/** 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) */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_export( - const secp256k1_context_t* ctx, - const unsigned char *seckey, - unsigned char *privkey, - int *privkeylen, - int compressed -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_negate( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); -/** Import a private key in DER format. */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_import( - const secp256k1_context_t* ctx, - unsigned char *seckey, - const unsigned char *privkey, - int privkeylen +/** 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. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add( + const secp256k1_context* ctx, + unsigned char *seckey, + const unsigned char *tweak ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -/** Tweak a private key by adding tweak to it. */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add( - const secp256k1_context_t* ctx, - unsigned char *seckey, - const unsigned char *tweak +/** 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. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const unsigned char *tweak ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -/** Tweak a public key by adding tweak times the generator to it. - * In: ctx: pointer to a context object, initialized for verification (cannot be NULL) +/** 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. */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add( - const secp256k1_context_t* ctx, - unsigned char *pubkey, - int pubkeylen, - const unsigned char *tweak -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); - -/** Tweak a private key by multiplying it with tweak. */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul( - const secp256k1_context_t* ctx, - unsigned char *seckey, - const unsigned char *tweak +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul( + const secp256k1_context* ctx, + unsigned char *seckey, + const unsigned char *tweak ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -/** Tweak a public key by multiplying it with tweak. - * In: ctx: pointer to a context object, initialized for verification (cannot be NULL) +/** 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. */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul( - const secp256k1_context_t* ctx, - unsigned char *pubkey, - int pubkeylen, - const unsigned char *tweak -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); - -/** Updates the context randomization. +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const unsigned char *tweak +) 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 * 0: error - * In: ctx: pointer to a context object (cannot be NULL) - * seed32: pointer to a 32-byte random seed (NULL resets to initial state) + * Args: ctx: pointer to a context object (cannot be NULL) + * 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, + * 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. */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize( - secp256k1_context_t* ctx, - const unsigned char *seed32 +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize( + 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) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine( + const secp256k1_context* ctx, + secp256k1_pubkey *out, + const secp256k1_pubkey * const * ins, + size_t n +) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -# ifdef __cplusplus +#ifdef __cplusplus } -# endif - #endif + +#endif /* SECP256K1_H */ diff --git a/src/secp256k1/include/secp256k1_ecdh.h b/src/secp256k1/include/secp256k1_ecdh.h new file mode 100644 index 000000000..88492dc1a --- /dev/null +++ b/src/secp256k1/include/secp256k1_ecdh.h @@ -0,0 +1,31 @@ +#ifndef SECP256K1_ECDH_H +#define SECP256K1_ECDH_H + +#include "secp256k1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** 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 + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh( + const secp256k1_context* ctx, + unsigned char *result, + const secp256k1_pubkey *pubkey, + const unsigned char *privkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_ECDH_H */ diff --git a/src/secp256k1/include/secp256k1_recovery.h b/src/secp256k1/include/secp256k1_recovery.h new file mode 100644 index 000000000..cf6c5ed7f --- /dev/null +++ b/src/secp256k1/include/secp256k1_recovery.h @@ -0,0 +1,110 @@ +#ifndef SECP256K1_RECOVERY_H +#define SECP256K1_RECOVERY_H + +#include "secp256k1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Opaque data structured that holds a parsed ECDSA signature, + * supporting pubkey recovery. + * + * 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 65 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage or transmission, use + * the secp256k1_ecdsa_signature_serialize_* and + * secp256k1_ecdsa_signature_parse_* functions. + * + * Furthermore, it is guaranteed that identical signatures (including their + * recoverability) will have identical representation, so they can be + * memcmp'ed. + */ +typedef struct { + unsigned char data[65]; +} secp256k1_ecdsa_recoverable_signature; + +/** 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 + * 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 unsigned char *input64, + int recid +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** 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). + */ +SECP256K1_API int secp256k1_ecdsa_recoverable_signature_convert( + 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) + */ +SECP256K1_API int secp256k1_ecdsa_recoverable_signature_serialize_compact( + const secp256k1_context* ctx, + unsigned char *output64, + int *recid, + 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) + */ +SECP256K1_API int secp256k1_ecdsa_sign_recoverable( + const secp256k1_context* ctx, + secp256k1_ecdsa_recoverable_signature *sig, + const unsigned char *msg32, + 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); + +/** Recover an ECDSA public key from a signature. + * + * 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) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_recover( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const secp256k1_ecdsa_recoverable_signature *sig, + const unsigned char *msg32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_RECOVERY_H */ diff --git a/src/secp256k1/libsecp256k1.pc.in b/src/secp256k1/libsecp256k1.pc.in index 1c72dd000..a0d006f11 100644 --- a/src/secp256k1/libsecp256k1.pc.in +++ b/src/secp256k1/libsecp256k1.pc.in @@ -5,7 +5,7 @@ includedir=@includedir@ Name: libsecp256k1 Description: Optimized C library for EC operations on curve secp256k1 -URL: https://github.com/bitcoin/secp256k1 +URL: https://github.com/bitcoin-core/secp256k1 Version: @PACKAGE_VERSION@ Cflags: -I${includedir} Libs.private: @SECP_LIBS@ diff --git a/src/secp256k1/sage/group_prover.sage b/src/secp256k1/sage/group_prover.sage new file mode 100644 index 000000000..8521f0799 --- /dev/null +++ b/src/secp256k1/sage/group_prover.sage @@ -0,0 +1,322 @@ +# This code supports verifying group implementations which have branches +# or conditional statements (like cmovs), by allowing each execution path +# to independently set assumptions on input or intermediary variables. +# +# The general approach is: +# * A constraint is a tuple of two sets of symbolic expressions: +# the first of which are required to evaluate to zero, the second of which +# are required to evaluate to nonzero. +# - A constraint is said to be conflicting if any of its nonzero expressions +# is in the ideal with basis the zero expressions (in other words: when the +# zero expressions imply that one of the nonzero expressions are zero). +# * There is a list of laws that describe the intended behaviour, including +# laws for addition and doubling. Each law is called with the symbolic point +# coordinates as arguments, and returns: +# - A constraint describing the assumptions under which it is applicable, +# called "assumeLaw" +# - A constraint describing the requirements of the law, called "require" +# * Implementations are transliterated into functions that operate as well on +# algebraic input points, and are called once per combination of branches +# executed. Each execution returns: +# - A constraint describing the assumptions this implementation requires +# (such as Z1=1), called "assumeFormula" +# - A constraint describing the assumptions this specific branch requires, +# but which is by construction guaranteed to cover the entire space by +# merging the results from all branches, called "assumeBranch" +# - The result of the computation +# * All combinations of laws with implementation branches are tried, and: +# - If the combination of assumeLaw, assumeFormula, and assumeBranch results +# in a conflict, it means this law does not apply to this branch, and it is +# skipped. +# - For others, we try to prove the require constraints hold, assuming the +# information in assumeLaw + assumeFormula + assumeBranch, and if this does +# not succeed, we fail. +# + To prove an expression is zero, we check whether it belongs to the +# ideal with the assumed zero expressions as basis. This test is exact. +# + To prove an expression is nonzero, we check whether each of its +# factors is contained in the set of nonzero assumptions' factors. +# This test is not exact, so various combinations of original and +# reduced expressions' factors are tried. +# - If we succeed, we print out the assumptions from assumeFormula that +# weren't implied by assumeLaw already. Those from assumeBranch are skipped, +# 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 + +class fastfrac: + """Fractions over rings.""" + + def __init__(self,R,top,bot=1): + """Construct a fractional, given a ring, a numerator, and denominator.""" + self.R = R + if parent(top) == ZZ or parent(top) == R: + self.top = R(top) + self.bot = R(bot) + elif top.__class__ == fastfrac: + self.top = top.top + self.bot = top.bot * bot + else: + self.top = R(numerator(top)) + self.bot = R(denominator(top)) * bot + + def iszero(self,I): + """Return whether this fraction is zero given an ideal.""" + return self.top in I and self.bot not in I + + def reduce(self,assumeZero): + zero = self.R.ideal(map(numerator, assumeZero)) + return fastfrac(self.R, zero.reduce(self.top)) / fastfrac(self.R, zero.reduce(self.bot)) + + def __add__(self,other): + """Add two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top + self.bot * other,self.bot) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.bot + self.bot * other.top,self.bot * other.bot) + return NotImplemented + + def __sub__(self,other): + """Subtract two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top - self.bot * other,self.bot) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.bot - self.bot * other.top,self.bot * other.bot) + return NotImplemented + + def __neg__(self): + """Return the negation of a fraction.""" + return fastfrac(self.R,-self.top,self.bot) + + def __mul__(self,other): + """Multiply two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top * other,self.bot) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.top,self.bot * other.bot) + return NotImplemented + + def __rmul__(self,other): + """Multiply something else with a fraction.""" + return self.__mul__(other) + + def __div__(self,other): + """Divide two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top,self.bot * other) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.bot,self.bot * other.top) + return NotImplemented + + def __pow__(self,other): + """Compute a power of a fraction.""" + if parent(other) == ZZ: + if other < 0: + # Negative powers require flipping top and bottom + return fastfrac(self.R,self.bot ^ (-other),self.top ^ (-other)) + else: + return fastfrac(self.R,self.top ^ other,self.bot ^ other) + return NotImplemented + + def __str__(self): + return "fastfrac((" + str(self.top) + ") / (" + str(self.bot) + "))" + def __repr__(self): + return "%s" % self + + def numerator(self): + return self.top + +class constraints: + """A set of constraints, consisting of zero and nonzero expressions. + + Constraints can either be used to express knowledge or a requirement. + + Both the fields zero and nonzero are maps from expressions to description + strings. The expressions that are the keys in zero are required to be zero, + and the expressions that are the keys in nonzero are required to be nonzero. + + Note that (a != 0) and (b != 0) is the same as (a*b != 0), so all keys in + nonzero could be multiplied into a single key. This is often much less + efficient to work with though, so we keep them separate inside the + constraints. This allows higher-level code to do fast checks on the individual + nonzero elements, or combine them if needed for stronger checks. + + We can't multiply the different zero elements, as it would suffice for one of + the factors to be zero, instead of all of them. Instead, the zero elements are + typically combined into an ideal first. + """ + + def __init__(self, **kwargs): + if 'zero' in kwargs: + self.zero = dict(kwargs['zero']) + else: + self.zero = dict() + if 'nonzero' in kwargs: + self.nonzero = dict(kwargs['nonzero']) + else: + self.nonzero = dict() + + def negate(self): + return constraints(zero=self.nonzero, nonzero=self.zero) + + def __add__(self, other): + zero = self.zero.copy() + zero.update(other.zero) + nonzero = self.nonzero.copy() + nonzero.update(other.nonzero) + return constraints(zero=zero, nonzero=nonzero) + + def __str__(self): + return "constraints(zero=%s,nonzero=%s)" % (self.zero, self.nonzero) + + def __repr__(self): + return "%s" % self + + +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)) + if 1 in zero: + return True + # First a cheap check whether any of the individual nonzero terms conflict on + # their own. + for nonzero in con.nonzero: + if nonzero.iszero(zero): + return True + # It can be the case that entries in the nonzero set do not individually + # conflict with the zero set, but their combination does. For example, knowing + # that either x or y is zero is equivalent to having x*y in the zero set. + # Having x or y individually in the nonzero set is not a conflict, but both + # simultaneously is, so that is the right thing to check for. + if reduce(lambda a,b: a * b, con.nonzero, fastfrac(R, 1)).iszero(zero): + return True + return False + + +def get_nonzero_set(R, assume): + """Calculate a simple set of nonzero expressions""" + zero = R.ideal(map(numerator, assume.zero)) + nonzero = set() + for nz in map(numerator, assume.nonzero): + for (f,n) in nz.factor(): + nonzero.add(f) + rnz = zero.reduce(nz) + for (f,n) in rnz.factor(): + nonzero.add(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)) + nonzero = get_nonzero_set(R, assume) + expl = set() + ok = True + for expr in exprs: + if numerator(expr) in zero: + 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: + ok = False + if ok: + return (True, None) + ok = True + for (f, n) in zero.reduce(numerator(allexprs)).factor(): + if 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: + 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: + expl.add(exprs[expr]) + if expl: + return (False, list(expl)) + else: + return (True, None) + + +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)) + nonzero = prod(x for x in assume.nonzero) + expl = [] + for expr in exprs: + if not expr.iszero(zero): + expl.append(exprs[expr]) + if not expl: + return (True, None) + return (False, expl) + + +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)) + nonzero = get_nonzero_set(R, assume) + ret = set() + # Iterate over the extra zero expressions + for base in assumeExtra.zero: + if base not in zero: + add = [] + for (f, n) in numerator(base).factor(): + if f not in nonzero: + add += ["%s" % f] + if add: + ret.add((" * ".join(add)) + " = 0 [%s]" % assumeExtra.zero[base]) + # Iterate over the extra nonzero expressions + for nz in assumeExtra.nonzero: + 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)) + return ", ".join(x for x in ret) + + +def check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require): + """Check a set of zero and nonzero requirements, given a set of zero and nonzero assumptions""" + assume = assumeLaw + assumeAssert + assumeBranch + + if conflicts(R, assume): + # This formula does not apply + return None + + describe = describe_extra(R, assumeLaw + assumeBranch, assumeAssert) + + ok, msg = prove_zero(R, require.zero, assume) + if not ok: + return "FAIL, %s fails (assuming %s)" % (str(msg), describe) + + res, expl = prove_nonzero(R, require.nonzero, assume) + if not res: + return "FAIL, %s fails (assuming %s)" % (str(expl), describe) + + if describe != "": + return "OK (assuming %s)" % describe + else: + return "OK" + + +def concrete_verify(c): + for k in c.zero: + if k != 0: + return (False, c.zero[k]) + for k in c.nonzero: + if k == 0: + return (False, c.nonzero[k]) + return (True, None) diff --git a/src/secp256k1/sage/secp256k1.sage b/src/secp256k1/sage/secp256k1.sage new file mode 100644 index 000000000..a97e732f7 --- /dev/null +++ b/src/secp256k1/sage/secp256k1.sage @@ -0,0 +1,306 @@ +# Test libsecp256k1' group operation implementations using prover.sage + +import sys + +load("group_prover.sage") +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 + return jacobianpoint(rx, ry, rz) + +def formula_secp256k1_gej_add_var(branch, a, b): + """libsecp256k1's secp256k1_gej_add_var""" + if branch == 0: + return (constraints(), constraints(nonzero={a.Infinity : 'a_infinite'}), b) + if branch == 1: + return (constraints(), constraints(zero={a.Infinity : 'a_finite'}, nonzero={b.Infinity : 'b_infinite'}), a) + z22 = b.Z^2 + z12 = a.Z^2 + u1 = a.X * z22 + u2 = b.X * z12 + s1 = a.Y * z22 + s1 = s1 * b.Z + s2 = b.Y * z12 + s2 = s2 * a.Z + h = -u1 + h = h + u2 + i = -s1 + i = i + s2 + 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 + h2 = h^2 + h3 = h2 * h + h = h * b.Z + rz = a.Z * h + t = u1 * h2 + rx = t + rx = rx * 2 + rx = rx + h3 + rx = -rx + rx = rx + i2 + ry = -rx + ry = ry + t + ry = ry * 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)) + +def formula_secp256k1_gej_add_ge_var(branch, a, b): + """libsecp256k1's secp256k1_gej_add_ge_var, which assume bz==1""" + if branch == 0: + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(nonzero={a.Infinity : 'a_infinite'}), b) + if branch == 1: + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite'}, nonzero={b.Infinity : 'b_infinite'}), a) + z12 = a.Z^2 + u1 = a.X + u2 = b.X * z12 + s1 = a.Y + s2 = b.Y * z12 + s2 = s2 * a.Z + h = -u1 + h = h + u2 + i = -s1 + i = i + s2 + 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 + t = u1 * h2 + rx = t + rx = rx * 2 + rx = rx + h3 + rx = -rx + rx = rx + i2 + ry = -rx + ry = ry + t + ry = ry * 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)) + +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: + 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)) + azz = a.Z * bzinv + z12 = azz^2 + u1 = a.X + u2 = b.X * z12 + s1 = a.Y + s2 = b.Y * z12 + s2 = s2 * azz + h = -u1 + h = h + u2 + i = -s1 + i = i + s2 + 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 + h2 = h^2 + h3 = h * h2 + rz = a.Z + rz = rz * h + t = u1 * h2 + rx = t + rx = rx * 2 + rx = rx + h3 + rx = -rx + rx = rx + i2 + ry = -rx + ry = ry + t + ry = ry * 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)) + +def formula_secp256k1_gej_add_ge(branch, a, b): + """libsecp256k1's secp256k1_gej_add_ge""" + zeroes = {} + nonzeroes = {} + a_infinity = False + if (branch & 4) != 0: + nonzeroes.update({a.Infinity : 'a_infinite'}) + a_infinity = True + else: + zeroes.update({a.Infinity : 'a_finite'}) + zz = a.Z^2 + u1 = a.X + u2 = b.X * zz + s1 = a.Y + s2 = b.Y * zz + s2 = s2 * a.Z + t = u1 + t = t + u2 + m = s1 + m = m + s2 + rr = t^2 + m_alt = -u2 + tt = u1 * m_alt + rr = rr + tt + degenerate = (branch & 3) == 3 + if (branch & 1) != 0: + 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 + if not degenerate: + rr_alt = rr + m_alt = m + n = m_alt^2 + q = n * t + 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 + t = t + q + t = t * rr_alt + t = t + n + ry = -t + rx = rx * 4 + ry = ry * 4 + if a_infinity: + rx = b.X + ry = b.Y + rz = 1 + if infinity: + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zeroes, nonzero=nonzeroes), point_at_infinity()) + 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): + """libsecp256k1's old secp256k1_gej_add_ge, which fails when ay+by=0 but ax!=bx""" + a_infinity = (branch & 1) != 0 + zero = {} + nonzero = {} + if a_infinity: + nonzero.update({a.Infinity : 'a_infinite'}) + else: + zero.update({a.Infinity : 'a_finite'}) + zz = a.Z^2 + u1 = a.X + u2 = b.X * zz + s1 = a.Y + s2 = b.Y * zz + s2 = s2 * a.Z + z = a.Z + t = u1 + t = t + u2 + m = s1 + m = m + s2 + n = m^2 + q = n * t + n = n^2 + rr = t^2 + t = u1 * u2 + t = -t + rr = rr + t + t = rr^2 + rz = m * z + infinity = False + if (branch & 2) != 0: + if not a_infinity: + infinity = True + else: + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(nonzero={z : 'conflict_a'}, zero={z : 'conflict_b'}), point_at_infinity()) + zero.update({rz : 'r.z=0'}) + else: + nonzero.update({rz : 'r.z!=0'}) + rz = rz * (0 if a_infinity else 2) + rx = t + q = -q + rx = rx + q + q = q * 3 + t = t * 2 + t = t + q + t = t * rr + t = t + n + ry = -t + rx = rx * (0 if a_infinity else 4) + ry = ry * (0 if a_infinity else 4) + t = b.X + t = t * (1 if a_infinity else 0) + rx = rx + t + t = b.Y + t = t * (1 if a_infinity else 0) + ry = ry + t + t = (1 if a_infinity else 0) + rz = rz + t + if infinity: + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), point_at_infinity()) + 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) + + 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) diff --git a/src/secp256k1/sage/weierstrass_prover.sage b/src/secp256k1/sage/weierstrass_prover.sage new file mode 100644 index 000000000..03ef2ec90 --- /dev/null +++ b/src/secp256k1/sage/weierstrass_prover.sage @@ -0,0 +1,264 @@ +# Prover implementation for Weierstrass curves of the form +# y^2 = x^3 + A * x + B, specifically with a = 0 and b = 7, with group laws +# operating on affine and Jacobian coordinates, including the point at infinity +# represented by a 4th variable in coordinates. + +load("group_prover.sage") + + +class affinepoint: + def __init__(self, x, y, infinity=0): + self.x = x + self.y = y + self.infinity = infinity + def __str__(self): + return "affinepoint(x=%s,y=%s,inf=%s)" % (self.x, self.y, self.infinity) + + +class jacobianpoint: + def __init__(self, x, y, z, infinity=0): + self.X = x + self.Y = y + self.Z = z + self.Infinity = infinity + def __str__(self): + return "jacobianpoint(X=%s,Y=%s,Z=%s,inf=%s)" % (self.X, self.Y, self.Z, self.Infinity) + + +def point_at_infinity(): + return jacobianpoint(1, 1, 1, 1) + + +def negate(p): + if p.__class__ == affinepoint: + return affinepoint(p.x, -p.y) + if p.__class__ == jacobianpoint: + return jacobianpoint(p.X, -p.Y, p.Z) + assert(False) + + +def on_weierstrass_curve(A, B, p): + """Return a set of zero-expressions for an affine point to be on the curve""" + return constraints(zero={p.x^3 + A*p.x + B - p.y^2: 'on_curve'}) + + +def tangential_to_weierstrass_curve(A, B, p12, p3): + """Return a set of zero-expressions for ((x12,y12),(x3,y3)) to be a line that is tangential to the curve at (x12,y12)""" + return constraints(zero={ + (p12.y - p3.y) * (p12.y * 2) - (p12.x^2 * 3 + A) * (p12.x - p3.x): 'tangential_to_curve' + }) + + +def colinear(p1, p2, p3): + """Return a set of zero-expressions for ((x1,y1),(x2,y2),(x3,y3)) to be collinear""" + return constraints(zero={ + (p1.y - p2.y) * (p1.x - p3.x) - (p1.y - p3.y) * (p1.x - p2.x): 'colinear_1', + (p2.y - p3.y) * (p2.x - p1.x) - (p2.y - p1.y) * (p2.x - p3.x): 'colinear_2', + (p3.y - p1.y) * (p3.x - p2.x) - (p3.y - p2.y) * (p3.x - p1.x): 'colinear_3' + }) + + +def good_affine_point(p): + return constraints(nonzero={p.x : 'nonzero_x', p.y : 'nonzero_y'}) + + +def good_jacobian_point(p): + return constraints(nonzero={p.X : 'nonzero_X', p.Y : 'nonzero_Y', p.Z^6 : 'nonzero_Z'}) + + +def good_point(p): + return constraints(nonzero={p.Z^6 : 'nonzero_X'}) + + +def finite(p, *affine_fns): + con = good_point(p) + constraints(zero={p.Infinity : 'finite_point'}) + if p.Z != 0: + return con + reduce(lambda a, b: a + b, (f(affinepoint(p.X / p.Z^2, p.Y / p.Z^3)) for f in affine_fns), con) + else: + return con + +def infinite(p): + return constraints(nonzero={p.Infinity : 'infinite_point'}) + + +def law_jacobian_weierstrass_add(A, B, pa, pb, pA, pB, pC): + """Check whether the passed set of coordinates is a valid Jacobian add, given assumptions""" + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + on_weierstrass_curve(A, B, pb) + + finite(pA) + + finite(pB) + + constraints(nonzero={pa.x - pb.x : 'different_x'})) + require = (finite(pC, lambda pc: on_weierstrass_curve(A, B, pc) + + colinear(pa, pb, negate(pc)))) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_double(A, B, pa, pb, pA, pB, pC): + """Check whether the passed set of coordinates is a valid Jacobian doubling, given assumptions""" + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + on_weierstrass_curve(A, B, pb) + + finite(pA) + + finite(pB) + + constraints(zero={pa.x - pb.x : 'equal_x', pa.y - pb.y : 'equal_y'})) + require = (finite(pC, lambda pc: on_weierstrass_curve(A, B, pc) + + tangential_to_weierstrass_curve(A, B, pa, negate(pc)))) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_opposites(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + on_weierstrass_curve(A, B, pb) + + finite(pA) + + finite(pB) + + constraints(zero={pa.x - pb.x : 'equal_x', pa.y + pb.y : 'opposite_y'})) + require = infinite(pC) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_infinite_a(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pb) + + infinite(pA) + + finite(pB)) + require = finite(pC, lambda pc: constraints(zero={pc.x - pb.x : 'c.x=b.x', pc.y - pb.y : 'c.y=b.y'})) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_infinite_b(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + infinite(pB) + + finite(pA)) + require = finite(pC, lambda pc: constraints(zero={pc.x - pa.x : 'c.x=a.x', pc.y - pa.y : 'c.y=a.y'})) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_infinite_ab(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + infinite(pA) + + infinite(pB)) + require = infinite(pC) + return (assumeLaw, require) + + +laws_jacobian_weierstrass = { + 'add': law_jacobian_weierstrass_add, + 'double': law_jacobian_weierstrass_double, + 'add_opposite': law_jacobian_weierstrass_add_opposites, + 'add_infinite_a': law_jacobian_weierstrass_add_infinite_a, + 'add_infinite_b': law_jacobian_weierstrass_add_infinite_b, + 'add_infinite_ab': law_jacobian_weierstrass_add_infinite_ab +} + + +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) + points = [] + for x in xrange(0, p): + for y in xrange(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): + for pa in points: + for pb in points: + for ia in xrange(2): + for ib in xrange(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): + assumeAssert, assumeBranch, pC = formula(branch, pA, pB) + pC.X = F(pC.X) + pC.Y = F(pC.Y) + pC.Z = F(pC.Z) + pC.Infinity = F(pC.Infinity) + r, e = concrete_verify(assumeAssert + assumeBranch) + if r: + match = False + for key in laws_jacobian_weierstrass: + assumeLaw, require = laws_jacobian_weierstrass[key](A, B, pa, pb, pA, pB, pC) + 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) + 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 + + +def check_symbolic_function(R, assumeAssert, assumeBranch, f, A, B, pa, pb, pA, pB, pC): + assumeLaw, require = f(A, B, pa, pb, pA, pB, pC) + return check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require) + +def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula): + """Verify an implementation of addition of Jacobian points on a Weierstrass curve symbolically""" + R. = PolynomialRing(QQ,8,order='invlex') + lift = lambda x: fastfrac(R,x) + ax = lift(ax) + ay = lift(ay) + Az = lift(Az) + bx = lift(bx) + by = lift(by) + Bz = lift(Bz) + Ai = lift(Ai) + Bi = lift(Bi) + + pa = affinepoint(ax, ay, Ai) + pb = affinepoint(bx, by, Bi) + pA = jacobianpoint(ax * Az^2, ay * Az^3, Az, Ai) + pB = jacobianpoint(bx * Bz^2, by * Bz^3, Bz, Bi) + + res = {} + + for key in laws_jacobian_weierstrass: + res[key] = [] + + print ("Formula " + name + ":") + count = 0 + for branch in xrange(branches): + assumeFormula, assumeBranch, pC = formula(branch, pA, pB) + 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)) + + for key in res: + 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 diff --git a/src/secp256k1/src/asm/field_10x26_arm.s b/src/secp256k1/src/asm/field_10x26_arm.s new file mode 100644 index 000000000..5a9cc3ffc --- /dev/null +++ b/src/secp256k1/src/asm/field_10x26_arm.s @@ -0,0 +1,919 @@ +@ 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 http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ +/* +ARM implementation of field_10x26 inner loops. + +Note: + +- To avoid unnecessary loads and make use of available registers, two + 'passes' have every time been interleaved, with the odd passes accumulating c' and d' + which will be added to c and d respectively in the even passes + +*/ + + .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 + .set field_R0, 0x3d10 + .set field_R1, 0x400 + .set field_not_M, 0xfc000000 @ ~M = ~0x3ffffff + + .align 2 + .global secp256k1_fe_mul_inner + .type secp256k1_fe_mul_inner, %function + @ Arguments: + @ r0 r Restrict: can overlap with a, not with b + @ r1 a + @ r2 b + @ Stack (total 4+10*4 = 44) + @ sp + #0 saved 'r' pointer + @ sp + #4 + 4*X t0,t1,t2,t3,t4,t5,t6,t7,u8,t9 +secp256k1_fe_mul_inner: + stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r14} + sub sp, sp, #48 @ frame=44 + alignment + str r0, [sp, #0] @ save result address, we need it only at the end + + /****************************************** + * Main computation code. + ****************************************** + + Allocation: + r0,r14,r7,r8 scratch + r1 a (pointer) + r2 b (pointer) + r3:r4 c + r5:r6 d + r11:r12 c' + r9:r10 d' + + Note: do not write to r[] here, it may overlap with a[] + */ + + /* A - interleaved with B */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #9*4] @ b[9] + ldr r0, [r1, #1*4] @ a[1] + umull r5, r6, r7, r8 @ d = a[0] * b[9] + ldr r14, [r2, #8*4] @ b[8] + umull r9, r10, r0, r8 @ d' = a[1] * b[9] + ldr r7, [r1, #2*4] @ a[2] + umlal r5, r6, r0, r14 @ d += a[1] * b[8] + ldr r8, [r2, #7*4] @ b[7] + umlal r9, r10, r7, r14 @ d' += a[2] * b[8] + ldr r0, [r1, #3*4] @ a[3] + umlal r5, r6, r7, r8 @ d += a[2] * b[7] + ldr r14, [r2, #6*4] @ b[6] + umlal r9, r10, r0, r8 @ d' += a[3] * b[7] + ldr r7, [r1, #4*4] @ a[4] + umlal r5, r6, r0, r14 @ d += a[3] * b[6] + ldr r8, [r2, #5*4] @ b[5] + umlal r9, r10, r7, r14 @ d' += a[4] * b[6] + ldr r0, [r1, #5*4] @ a[5] + umlal r5, r6, r7, r8 @ d += a[4] * b[5] + ldr r14, [r2, #4*4] @ b[4] + umlal r9, r10, r0, r8 @ d' += a[5] * b[5] + ldr r7, [r1, #6*4] @ a[6] + umlal r5, r6, r0, r14 @ d += a[5] * b[4] + ldr r8, [r2, #3*4] @ b[3] + umlal r9, r10, r7, r14 @ d' += a[6] * b[4] + ldr r0, [r1, #7*4] @ a[7] + umlal r5, r6, r7, r8 @ d += a[6] * b[3] + ldr r14, [r2, #2*4] @ b[2] + umlal r9, r10, r0, r8 @ d' += a[7] * b[3] + ldr r7, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r14 @ d += a[7] * b[2] + ldr r8, [r2, #1*4] @ b[1] + umlal r9, r10, r7, r14 @ d' += a[8] * b[2] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r8 @ d += a[8] * b[1] + ldr r14, [r2, #0*4] @ b[0] + umlal r9, r10, r0, r8 @ d' += a[9] * b[1] + ldr r7, [r1, #0*4] @ a[0] + umlal r5, r6, r0, r14 @ d += a[9] * b[0] + @ r7,r14 used in B + + bic r0, r5, field_not_M @ t9 = d & M + str r0, [sp, #4 + 4*9] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + /* B */ + umull r3, r4, r7, r14 @ c = a[0] * b[0] + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u0 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u0 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t0 = c & M + str r14, [sp, #4 + 0*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u0 * R1 + umlal r3, r4, r0, r14 + + /* C - interleaved with D */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #2*4] @ b[2] + ldr r14, [r2, #1*4] @ b[1] + umull r11, r12, r7, r8 @ c' = a[0] * b[2] + ldr r0, [r1, #1*4] @ a[1] + umlal r3, r4, r7, r14 @ c += a[0] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r11, r12, r0, r14 @ c' += a[1] * b[1] + ldr r7, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r8 @ c += a[1] * b[0] + ldr r14, [r2, #9*4] @ b[9] + umlal r11, r12, r7, r8 @ c' += a[2] * b[0] + ldr r0, [r1, #3*4] @ a[3] + umlal r5, r6, r7, r14 @ d += a[2] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umull r9, r10, r0, r14 @ d' = a[3] * b[9] + ldr r7, [r1, #4*4] @ a[4] + umlal r5, r6, r0, r8 @ d += a[3] * b[8] + ldr r14, [r2, #7*4] @ b[7] + umlal r9, r10, r7, r8 @ d' += a[4] * b[8] + ldr r0, [r1, #5*4] @ a[5] + umlal r5, r6, r7, r14 @ d += a[4] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r9, r10, r0, r14 @ d' += a[5] * b[7] + ldr r7, [r1, #6*4] @ a[6] + umlal r5, r6, r0, r8 @ d += a[5] * b[6] + ldr r14, [r2, #5*4] @ b[5] + umlal r9, r10, r7, r8 @ d' += a[6] * b[6] + ldr r0, [r1, #7*4] @ a[7] + umlal r5, r6, r7, r14 @ d += a[6] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r9, r10, r0, r14 @ d' += a[7] * b[5] + ldr r7, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r8 @ d += a[7] * b[4] + ldr r14, [r2, #3*4] @ b[3] + umlal r9, r10, r7, r8 @ d' += a[8] * b[4] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r14 @ d += a[8] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r9, r10, r0, r14 @ d' += a[9] * b[3] + umlal r5, r6, r0, r8 @ d += a[9] * b[2] + + bic r0, r5, field_not_M @ u1 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u1 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t1 = c & M + str r14, [sp, #4 + 1*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u1 * R1 + umlal r3, r4, r0, r14 + + /* D */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u2 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u2 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t2 = c & M + str r14, [sp, #4 + 2*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u2 * R1 + umlal r3, r4, r0, r14 + + /* E - interleaved with F */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #4*4] @ b[4] + umull r11, r12, r7, r8 @ c' = a[0] * b[4] + ldr r8, [r2, #3*4] @ b[3] + umlal r3, r4, r7, r8 @ c += a[0] * b[3] + ldr r7, [r1, #1*4] @ a[1] + umlal r11, r12, r7, r8 @ c' += a[1] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r3, r4, r7, r8 @ c += a[1] * b[2] + ldr r7, [r1, #2*4] @ a[2] + umlal r11, r12, r7, r8 @ c' += a[2] * b[2] + ldr r8, [r2, #1*4] @ b[1] + umlal r3, r4, r7, r8 @ c += a[2] * b[1] + ldr r7, [r1, #3*4] @ a[3] + umlal r11, r12, r7, r8 @ c' += a[3] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r3, r4, r7, r8 @ c += a[3] * b[0] + ldr r7, [r1, #4*4] @ a[4] + umlal r11, r12, r7, r8 @ c' += a[4] * b[0] + ldr r8, [r2, #9*4] @ b[9] + umlal r5, r6, r7, r8 @ d += a[4] * b[9] + ldr r7, [r1, #5*4] @ a[5] + umull r9, r10, r7, r8 @ d' = a[5] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umlal r5, r6, r7, r8 @ d += a[5] * b[8] + ldr r7, [r1, #6*4] @ a[6] + umlal r9, r10, r7, r8 @ d' += a[6] * b[8] + ldr r8, [r2, #7*4] @ b[7] + umlal r5, r6, r7, r8 @ d += a[6] * b[7] + ldr r7, [r1, #7*4] @ a[7] + umlal r9, r10, r7, r8 @ d' += a[7] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r5, r6, r7, r8 @ d += a[7] * b[6] + ldr r7, [r1, #8*4] @ a[8] + umlal r9, r10, r7, r8 @ d' += a[8] * b[6] + ldr r8, [r2, #5*4] @ b[5] + umlal r5, r6, r7, r8 @ d += a[8] * b[5] + ldr r7, [r1, #9*4] @ a[9] + umlal r9, r10, r7, r8 @ d' += a[9] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r5, r6, r7, r8 @ d += a[9] * b[4] + + bic r0, r5, field_not_M @ u3 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u3 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t3 = c & M + str r14, [sp, #4 + 3*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u3 * R1 + umlal r3, r4, r0, r14 + + /* F */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u4 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u4 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t4 = c & M + str r14, [sp, #4 + 4*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u4 * R1 + umlal r3, r4, r0, r14 + + /* G - interleaved with H */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #6*4] @ b[6] + ldr r14, [r2, #5*4] @ b[5] + umull r11, r12, r7, r8 @ c' = a[0] * b[6] + ldr r0, [r1, #1*4] @ a[1] + umlal r3, r4, r7, r14 @ c += a[0] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r11, r12, r0, r14 @ c' += a[1] * b[5] + ldr r7, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r8 @ c += a[1] * b[4] + ldr r14, [r2, #3*4] @ b[3] + umlal r11, r12, r7, r8 @ c' += a[2] * b[4] + ldr r0, [r1, #3*4] @ a[3] + umlal r3, r4, r7, r14 @ c += a[2] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r11, r12, r0, r14 @ c' += a[3] * b[3] + ldr r7, [r1, #4*4] @ a[4] + umlal r3, r4, r0, r8 @ c += a[3] * b[2] + ldr r14, [r2, #1*4] @ b[1] + umlal r11, r12, r7, r8 @ c' += a[4] * b[2] + ldr r0, [r1, #5*4] @ a[5] + umlal r3, r4, r7, r14 @ c += a[4] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r11, r12, r0, r14 @ c' += a[5] * b[1] + ldr r7, [r1, #6*4] @ a[6] + umlal r3, r4, r0, r8 @ c += a[5] * b[0] + ldr r14, [r2, #9*4] @ b[9] + umlal r11, r12, r7, r8 @ c' += a[6] * b[0] + ldr r0, [r1, #7*4] @ a[7] + umlal r5, r6, r7, r14 @ d += a[6] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umull r9, r10, r0, r14 @ d' = a[7] * b[9] + ldr r7, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r8 @ d += a[7] * b[8] + ldr r14, [r2, #7*4] @ b[7] + umlal r9, r10, r7, r8 @ d' += a[8] * b[8] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r14 @ d += a[8] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r9, r10, r0, r14 @ d' += a[9] * b[7] + umlal r5, r6, r0, r8 @ d += a[9] * b[6] + + bic r0, r5, field_not_M @ u5 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u5 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t5 = c & M + str r14, [sp, #4 + 5*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u5 * R1 + umlal r3, r4, r0, r14 + + /* H */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u6 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u6 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t6 = c & M + str r14, [sp, #4 + 6*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u6 * R1 + umlal r3, r4, r0, r14 + + /* I - interleaved with J */ + ldr r8, [r2, #8*4] @ b[8] + ldr r7, [r1, #0*4] @ a[0] + ldr r14, [r2, #7*4] @ b[7] + umull r11, r12, r7, r8 @ c' = a[0] * b[8] + ldr r0, [r1, #1*4] @ a[1] + umlal r3, r4, r7, r14 @ c += a[0] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r11, r12, r0, r14 @ c' += a[1] * b[7] + ldr r7, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r8 @ c += a[1] * b[6] + ldr r14, [r2, #5*4] @ b[5] + umlal r11, r12, r7, r8 @ c' += a[2] * b[6] + ldr r0, [r1, #3*4] @ a[3] + umlal r3, r4, r7, r14 @ c += a[2] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r11, r12, r0, r14 @ c' += a[3] * b[5] + ldr r7, [r1, #4*4] @ a[4] + umlal r3, r4, r0, r8 @ c += a[3] * b[4] + ldr r14, [r2, #3*4] @ b[3] + umlal r11, r12, r7, r8 @ c' += a[4] * b[4] + ldr r0, [r1, #5*4] @ a[5] + umlal r3, r4, r7, r14 @ c += a[4] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r11, r12, r0, r14 @ c' += a[5] * b[3] + ldr r7, [r1, #6*4] @ a[6] + umlal r3, r4, r0, r8 @ c += a[5] * b[2] + ldr r14, [r2, #1*4] @ b[1] + umlal r11, r12, r7, r8 @ c' += a[6] * b[2] + ldr r0, [r1, #7*4] @ a[7] + umlal r3, r4, r7, r14 @ c += a[6] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r11, r12, r0, r14 @ c' += a[7] * b[1] + ldr r7, [r1, #8*4] @ a[8] + umlal r3, r4, r0, r8 @ c += a[7] * b[0] + ldr r14, [r2, #9*4] @ b[9] + umlal r11, r12, r7, r8 @ c' += a[8] * b[0] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r14 @ d += a[8] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umull r9, r10, r0, r14 @ d' = a[9] * b[9] + umlal r5, r6, r0, r8 @ d += a[9] * b[8] + + bic r0, r5, field_not_M @ u7 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u7 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t7 = c & M + str r14, [sp, #4 + 7*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u7 * R1 + umlal r3, r4, r0, r14 + + /* J */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u8 = d & M + str r0, [sp, #4 + 8*4] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u8 * R0 + umlal r3, r4, r0, r14 + + /****************************************** + * compute and write back result + ****************************************** + Allocation: + r0 r + r3:r4 c + r5:r6 d + r7 t0 + r8 t1 + r9 t2 + r11 u8 + r12 t9 + r1,r2,r10,r14 scratch + + Note: do not read from a[] after here, it may overlap with r[] + */ + ldr r0, [sp, #0] + add r1, sp, #4 + 3*4 @ r[3..7] = t3..7, r11=u8, r12=t9 + ldmia r1, {r2,r7,r8,r9,r10,r11,r12} + add r1, r0, #3*4 + stmia r1, {r2,r7,r8,r9,r10} + + bic r2, r3, field_not_M @ r[8] = c & M + str r2, [r0, #8*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u8 * R1 + umlal r3, r4, r11, r14 + movw r14, field_R0 @ c += d * R0 + umlal r3, r4, r5, r14 + adds r3, r3, r12 @ c += t9 + adc r4, r4, #0 + + add r1, sp, #4 + 0*4 @ r7,r8,r9 = t0,t1,t2 + ldmia r1, {r7,r8,r9} + + ubfx r2, r3, #0, #22 @ r[9] = c & (M >> 4) + str r2, [r0, #9*4] + mov r3, r3, lsr #22 @ c >>= 22 + orr r3, r3, r4, asl #10 + mov r4, r4, lsr #22 + movw r14, field_R1 << 4 @ c += d * (R1 << 4) + umlal r3, r4, r5, r14 + + movw r14, field_R0 >> 4 @ d = c * (R0 >> 4) + t0 (64x64 multiply+add) + umull r5, r6, r3, r14 @ d = c.lo * (R0 >> 4) + adds r5, r5, r7 @ d.lo += t0 + mla r6, r14, r4, r6 @ d.hi += c.hi * (R0 >> 4) + adc r6, r6, 0 @ d.hi += carry + + bic r2, r5, field_not_M @ r[0] = d & M + str r2, [r0, #0*4] + + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + movw r14, field_R1 >> 4 @ d += c * (R1 >> 4) + t1 (64x64 multiply+add) + umull r1, r2, r3, r14 @ tmp = c.lo * (R1 >> 4) + adds r5, r5, r8 @ d.lo += t1 + adc r6, r6, #0 @ d.hi += carry + adds r5, r5, r1 @ d.lo += tmp.lo + mla r2, r14, r4, r2 @ tmp.hi += c.hi * (R1 >> 4) + adc r6, r6, r2 @ d.hi += carry + tmp.hi + + bic r2, r5, field_not_M @ r[1] = d & M + str r2, [r0, #1*4] + mov r5, r5, lsr #26 @ d >>= 26 (ignore hi) + orr r5, r5, r6, asl #6 + + add r5, r5, r9 @ d += t2 + str r5, [r0, #2*4] @ r[2] = d + + add sp, sp, #48 + ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc} + .size secp256k1_fe_mul_inner, .-secp256k1_fe_mul_inner + + .align 2 + .global secp256k1_fe_sqr_inner + .type secp256k1_fe_sqr_inner, %function + @ Arguments: + @ r0 r Can overlap with a + @ r1 a + @ Stack (total 4+10*4 = 44) + @ sp + #0 saved 'r' pointer + @ sp + #4 + 4*X t0,t1,t2,t3,t4,t5,t6,t7,u8,t9 +secp256k1_fe_sqr_inner: + stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r14} + sub sp, sp, #48 @ frame=44 + alignment + str r0, [sp, #0] @ save result address, we need it only at the end + /****************************************** + * Main computation code. + ****************************************** + + Allocation: + r0,r14,r2,r7,r8 scratch + r1 a (pointer) + r3:r4 c + r5:r6 d + r11:r12 c' + r9:r10 d' + + Note: do not write to r[] here, it may overlap with a[] + */ + /* A interleaved with B */ + ldr r0, [r1, #1*4] @ a[1]*2 + ldr r7, [r1, #0*4] @ a[0] + mov r0, r0, asl #1 + ldr r14, [r1, #9*4] @ a[9] + umull r3, r4, r7, r7 @ c = a[0] * a[0] + ldr r8, [r1, #8*4] @ a[8] + mov r7, r7, asl #1 + umull r5, r6, r7, r14 @ d = a[0]*2 * a[9] + ldr r7, [r1, #2*4] @ a[2]*2 + umull r9, r10, r0, r14 @ d' = a[1]*2 * a[9] + ldr r14, [r1, #7*4] @ a[7] + umlal r5, r6, r0, r8 @ d += a[1]*2 * a[8] + mov r7, r7, asl #1 + ldr r0, [r1, #3*4] @ a[3]*2 + umlal r9, r10, r7, r8 @ d' += a[2]*2 * a[8] + ldr r8, [r1, #6*4] @ a[6] + umlal r5, r6, r7, r14 @ d += a[2]*2 * a[7] + mov r0, r0, asl #1 + ldr r7, [r1, #4*4] @ a[4]*2 + umlal r9, r10, r0, r14 @ d' += a[3]*2 * a[7] + ldr r14, [r1, #5*4] @ a[5] + mov r7, r7, asl #1 + umlal r5, r6, r0, r8 @ d += a[3]*2 * a[6] + umlal r9, r10, r7, r8 @ d' += a[4]*2 * a[6] + umlal r5, r6, r7, r14 @ d += a[4]*2 * a[5] + umlal r9, r10, r14, r14 @ d' += a[5] * a[5] + + bic r0, r5, field_not_M @ t9 = d & M + str r0, [sp, #4 + 9*4] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + /* B */ + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u0 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u0 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t0 = c & M + str r14, [sp, #4 + 0*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u0 * R1 + umlal r3, r4, r0, r14 + + /* C interleaved with D */ + ldr r0, [r1, #0*4] @ a[0]*2 + ldr r14, [r1, #1*4] @ a[1] + mov r0, r0, asl #1 + ldr r8, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r14 @ c += a[0]*2 * a[1] + mov r7, r8, asl #1 @ a[2]*2 + umull r11, r12, r14, r14 @ c' = a[1] * a[1] + ldr r14, [r1, #9*4] @ a[9] + umlal r11, r12, r0, r8 @ c' += a[0]*2 * a[2] + ldr r0, [r1, #3*4] @ a[3]*2 + ldr r8, [r1, #8*4] @ a[8] + umlal r5, r6, r7, r14 @ d += a[2]*2 * a[9] + mov r0, r0, asl #1 + ldr r7, [r1, #4*4] @ a[4]*2 + umull r9, r10, r0, r14 @ d' = a[3]*2 * a[9] + ldr r14, [r1, #7*4] @ a[7] + umlal r5, r6, r0, r8 @ d += a[3]*2 * a[8] + mov r7, r7, asl #1 + ldr r0, [r1, #5*4] @ a[5]*2 + umlal r9, r10, r7, r8 @ d' += a[4]*2 * a[8] + ldr r8, [r1, #6*4] @ a[6] + mov r0, r0, asl #1 + umlal r5, r6, r7, r14 @ d += a[4]*2 * a[7] + umlal r9, r10, r0, r14 @ d' += a[5]*2 * a[7] + umlal r5, r6, r0, r8 @ d += a[5]*2 * a[6] + umlal r9, r10, r8, r8 @ d' += a[6] * a[6] + + bic r0, r5, field_not_M @ u1 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u1 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t1 = c & M + str r14, [sp, #4 + 1*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u1 * R1 + umlal r3, r4, r0, r14 + + /* D */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u2 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u2 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t2 = c & M + str r14, [sp, #4 + 2*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u2 * R1 + umlal r3, r4, r0, r14 + + /* E interleaved with F */ + ldr r7, [r1, #0*4] @ a[0]*2 + ldr r0, [r1, #1*4] @ a[1]*2 + ldr r14, [r1, #2*4] @ a[2] + mov r7, r7, asl #1 + ldr r8, [r1, #3*4] @ a[3] + ldr r2, [r1, #4*4] + umlal r3, r4, r7, r8 @ c += a[0]*2 * a[3] + mov r0, r0, asl #1 + umull r11, r12, r7, r2 @ c' = a[0]*2 * a[4] + mov r2, r2, asl #1 @ a[4]*2 + umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[3] + ldr r8, [r1, #9*4] @ a[9] + umlal r3, r4, r0, r14 @ c += a[1]*2 * a[2] + ldr r0, [r1, #5*4] @ a[5]*2 + umlal r11, r12, r14, r14 @ c' += a[2] * a[2] + ldr r14, [r1, #8*4] @ a[8] + mov r0, r0, asl #1 + umlal r5, r6, r2, r8 @ d += a[4]*2 * a[9] + ldr r7, [r1, #6*4] @ a[6]*2 + umull r9, r10, r0, r8 @ d' = a[5]*2 * a[9] + mov r7, r7, asl #1 + ldr r8, [r1, #7*4] @ a[7] + umlal r5, r6, r0, r14 @ d += a[5]*2 * a[8] + umlal r9, r10, r7, r14 @ d' += a[6]*2 * a[8] + umlal r5, r6, r7, r8 @ d += a[6]*2 * a[7] + umlal r9, r10, r8, r8 @ d' += a[7] * a[7] + + bic r0, r5, field_not_M @ u3 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u3 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t3 = c & M + str r14, [sp, #4 + 3*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u3 * R1 + umlal r3, r4, r0, r14 + + /* F */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u4 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u4 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t4 = c & M + str r14, [sp, #4 + 4*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u4 * R1 + umlal r3, r4, r0, r14 + + /* G interleaved with H */ + ldr r7, [r1, #0*4] @ a[0]*2 + ldr r0, [r1, #1*4] @ a[1]*2 + mov r7, r7, asl #1 + ldr r8, [r1, #5*4] @ a[5] + ldr r2, [r1, #6*4] @ a[6] + umlal r3, r4, r7, r8 @ c += a[0]*2 * a[5] + ldr r14, [r1, #4*4] @ a[4] + mov r0, r0, asl #1 + umull r11, r12, r7, r2 @ c' = a[0]*2 * a[6] + ldr r7, [r1, #2*4] @ a[2]*2 + umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[5] + mov r7, r7, asl #1 + ldr r8, [r1, #3*4] @ a[3] + umlal r3, r4, r0, r14 @ c += a[1]*2 * a[4] + mov r0, r2, asl #1 @ a[6]*2 + umlal r11, r12, r7, r14 @ c' += a[2]*2 * a[4] + ldr r14, [r1, #9*4] @ a[9] + umlal r3, r4, r7, r8 @ c += a[2]*2 * a[3] + ldr r7, [r1, #7*4] @ a[7]*2 + umlal r11, r12, r8, r8 @ c' += a[3] * a[3] + mov r7, r7, asl #1 + ldr r8, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r14 @ d += a[6]*2 * a[9] + umull r9, r10, r7, r14 @ d' = a[7]*2 * a[9] + umlal r5, r6, r7, r8 @ d += a[7]*2 * a[8] + umlal r9, r10, r8, r8 @ d' += a[8] * a[8] + + bic r0, r5, field_not_M @ u5 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u5 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t5 = c & M + str r14, [sp, #4 + 5*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u5 * R1 + umlal r3, r4, r0, r14 + + /* H */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u6 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u6 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t6 = c & M + str r14, [sp, #4 + 6*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u6 * R1 + umlal r3, r4, r0, r14 + + /* I interleaved with J */ + ldr r7, [r1, #0*4] @ a[0]*2 + ldr r0, [r1, #1*4] @ a[1]*2 + mov r7, r7, asl #1 + ldr r8, [r1, #7*4] @ a[7] + ldr r2, [r1, #8*4] @ a[8] + umlal r3, r4, r7, r8 @ c += a[0]*2 * a[7] + ldr r14, [r1, #6*4] @ a[6] + mov r0, r0, asl #1 + umull r11, r12, r7, r2 @ c' = a[0]*2 * a[8] + ldr r7, [r1, #2*4] @ a[2]*2 + umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[7] + ldr r8, [r1, #5*4] @ a[5] + umlal r3, r4, r0, r14 @ c += a[1]*2 * a[6] + ldr r0, [r1, #3*4] @ a[3]*2 + mov r7, r7, asl #1 + umlal r11, r12, r7, r14 @ c' += a[2]*2 * a[6] + ldr r14, [r1, #4*4] @ a[4] + mov r0, r0, asl #1 + umlal r3, r4, r7, r8 @ c += a[2]*2 * a[5] + mov r2, r2, asl #1 @ a[8]*2 + umlal r11, r12, r0, r8 @ c' += a[3]*2 * a[5] + umlal r3, r4, r0, r14 @ c += a[3]*2 * a[4] + umlal r11, r12, r14, r14 @ c' += a[4] * a[4] + ldr r8, [r1, #9*4] @ a[9] + umlal r5, r6, r2, r8 @ d += a[8]*2 * a[9] + @ r8 will be used in J + + bic r0, r5, field_not_M @ u7 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u7 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t7 = c & M + str r14, [sp, #4 + 7*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u7 * R1 + umlal r3, r4, r0, r14 + + /* J */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + umlal r5, r6, r8, r8 @ d += a[9] * a[9] + + bic r0, r5, field_not_M @ u8 = d & M + str r0, [sp, #4 + 8*4] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u8 * R0 + umlal r3, r4, r0, r14 + + /****************************************** + * compute and write back result + ****************************************** + Allocation: + r0 r + r3:r4 c + r5:r6 d + r7 t0 + r8 t1 + r9 t2 + r11 u8 + r12 t9 + r1,r2,r10,r14 scratch + + Note: do not read from a[] after here, it may overlap with r[] + */ + ldr r0, [sp, #0] + add r1, sp, #4 + 3*4 @ r[3..7] = t3..7, r11=u8, r12=t9 + ldmia r1, {r2,r7,r8,r9,r10,r11,r12} + add r1, r0, #3*4 + stmia r1, {r2,r7,r8,r9,r10} + + bic r2, r3, field_not_M @ r[8] = c & M + str r2, [r0, #8*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u8 * R1 + umlal r3, r4, r11, r14 + movw r14, field_R0 @ c += d * R0 + umlal r3, r4, r5, r14 + adds r3, r3, r12 @ c += t9 + adc r4, r4, #0 + + add r1, sp, #4 + 0*4 @ r7,r8,r9 = t0,t1,t2 + ldmia r1, {r7,r8,r9} + + ubfx r2, r3, #0, #22 @ r[9] = c & (M >> 4) + str r2, [r0, #9*4] + mov r3, r3, lsr #22 @ c >>= 22 + orr r3, r3, r4, asl #10 + mov r4, r4, lsr #22 + movw r14, field_R1 << 4 @ c += d * (R1 << 4) + umlal r3, r4, r5, r14 + + movw r14, field_R0 >> 4 @ d = c * (R0 >> 4) + t0 (64x64 multiply+add) + umull r5, r6, r3, r14 @ d = c.lo * (R0 >> 4) + adds r5, r5, r7 @ d.lo += t0 + mla r6, r14, r4, r6 @ d.hi += c.hi * (R0 >> 4) + adc r6, r6, 0 @ d.hi += carry + + bic r2, r5, field_not_M @ r[0] = d & M + str r2, [r0, #0*4] + + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + movw r14, field_R1 >> 4 @ d += c * (R1 >> 4) + t1 (64x64 multiply+add) + umull r1, r2, r3, r14 @ tmp = c.lo * (R1 >> 4) + adds r5, r5, r8 @ d.lo += t1 + adc r6, r6, #0 @ d.hi += carry + adds r5, r5, r1 @ d.lo += tmp.lo + mla r2, r14, r4, r2 @ tmp.hi += c.hi * (R1 >> 4) + adc r6, r6, r2 @ d.hi += carry + tmp.hi + + bic r2, r5, field_not_M @ r[1] = d & M + str r2, [r0, #1*4] + mov r5, r5, lsr #26 @ d >>= 26 (ignore hi) + orr r5, r5, r6, asl #6 + + add r5, r5, r9 @ d += t2 + str r5, [r0, #2*4] @ r[2] = d + + add sp, sp, #48 + ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc} + .size secp256k1_fe_sqr_inner, .-secp256k1_fe_sqr_inner + diff --git a/src/secp256k1/src/basic-config.h b/src/secp256k1/src/basic-config.h new file mode 100644 index 000000000..fc588061c --- /dev/null +++ b/src/secp256k1/src/basic-config.h @@ -0,0 +1,33 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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.h b/src/secp256k1/src/bench.h index db5f68cee..d5ebe0130 100644 --- a/src/secp256k1/src/bench.h +++ b/src/secp256k1/src/bench.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_BENCH_H_ -#define _SECP256K1_BENCH_H_ +#ifndef SECP256K1_BENCH_H +#define SECP256K1_BENCH_H #include #include @@ -20,8 +20,10 @@ static double gettimedouble(void) { void print_number(double x) { double y = x; int c = 0; - if (y < 0.0) y = -y; - while (y < 100.0) { + if (y < 0.0) { + y = -y; + } + while (y > 0 && y < 100.0) { y *= 10.0; c++; } @@ -35,13 +37,21 @@ void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), v double max = 0.0; for (i = 0; i < count; i++) { double begin, total; - if (setup) setup(data); + if (setup != NULL) { + setup(data); + } begin = gettimedouble(); benchmark(data); total = gettimedouble() - begin; - if (teardown) teardown(data); - if (total < min) min = total; - if (total > max) max = total; + if (teardown != NULL) { + teardown(data); + } + if (total < min) { + min = total; + } + if (total > max) { + max = total; + } sum += total; } printf("%s: min ", name); @@ -53,4 +63,4 @@ void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), v printf("us\n"); } -#endif +#endif /* SECP256K1_BENCH_H */ diff --git a/src/secp256k1/src/bench_ecdh.c b/src/secp256k1/src/bench_ecdh.c new file mode 100644 index 000000000..2de5126d6 --- /dev/null +++ b/src/secp256k1/src/bench_ecdh.c @@ -0,0 +1,54 @@ +/********************************************************************** + * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include + +#include "include/secp256k1.h" +#include "include/secp256k1_ecdh.h" +#include "util.h" +#include "bench.h" + +typedef struct { + secp256k1_context *ctx; + secp256k1_pubkey point; + unsigned char scalar[32]; +} bench_ecdh; + +static void bench_ecdh_setup(void* arg) { + int i; + bench_ecdh *data = (bench_ecdh*)arg; + const unsigned char point[] = { + 0x03, + 0x54, 0x94, 0xc1, 0x5d, 0x32, 0x09, 0x97, 0x06, + 0xc2, 0x39, 0x5f, 0x94, 0x34, 0x87, 0x45, 0xfd, + 0x75, 0x7c, 0xe3, 0x0e, 0x4e, 0x8c, 0x90, 0xfb, + 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) { + int i; + unsigned char res[32]; + bench_ecdh *data = (bench_ecdh*)arg; + + for (i = 0; i < 20000; i++) { + CHECK(secp256k1_ecdh(data->ctx, res, &data->point, data->scalar) == 1); + } +} + +int main(void) { + bench_ecdh data; + + run_benchmark("ecdh", bench_ecdh, bench_ecdh_setup, NULL, &data, 10, 20000); + return 0; +} diff --git a/src/secp256k1/src/bench_internal.c b/src/secp256k1/src/bench_internal.c index a960549b9..9b30c50d0 100644 --- a/src/secp256k1/src/bench_internal.c +++ b/src/secp256k1/src/bench_internal.c @@ -13,20 +13,22 @@ #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" typedef struct { - secp256k1_scalar_t scalar_x, scalar_y; - secp256k1_fe_t fe_x, fe_y; - secp256k1_ge_t ge_x, ge_y; - secp256k1_gej_t gej_x, gej_y; - unsigned char data[32]; + secp256k1_scalar scalar_x, scalar_y; + secp256k1_fe fe_x, fe_y; + secp256k1_ge ge_x, ge_y; + secp256k1_gej gej_x, gej_y; + unsigned char data[64]; int wnaf[256]; -} bench_inv_t; +} bench_inv; void bench_setup(void* arg) { - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; static const unsigned char init_x[32] = { 0x02, 0x03, 0x05, 0x07, 0x0b, 0x0d, 0x11, 0x13, @@ -51,11 +53,12 @@ void bench_setup(void* arg) { 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); } void bench_scalar_add(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 2000000; i++) { secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); @@ -64,7 +67,7 @@ void bench_scalar_add(void* arg) { void bench_scalar_negate(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 2000000; i++) { secp256k1_scalar_negate(&data->scalar_x, &data->scalar_x); @@ -73,7 +76,7 @@ void bench_scalar_negate(void* arg) { void bench_scalar_sqr(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 200000; i++) { secp256k1_scalar_sqr(&data->scalar_x, &data->scalar_x); @@ -82,7 +85,7 @@ void bench_scalar_sqr(void* arg) { void bench_scalar_mul(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 200000; i++) { secp256k1_scalar_mul(&data->scalar_x, &data->scalar_x, &data->scalar_y); @@ -92,11 +95,11 @@ void bench_scalar_mul(void* arg) { #ifdef USE_ENDOMORPHISM void bench_scalar_split(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 20000; i++) { - secp256k1_scalar_t l, r; - secp256k1_scalar_split_lambda_var(&l, &r, &data->scalar_x); + 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); } } @@ -104,7 +107,7 @@ void bench_scalar_split(void* arg) { void bench_scalar_inverse(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 2000; i++) { secp256k1_scalar_inverse(&data->scalar_x, &data->scalar_x); @@ -114,7 +117,7 @@ void bench_scalar_inverse(void* arg) { void bench_scalar_inverse_var(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 2000; i++) { secp256k1_scalar_inverse_var(&data->scalar_x, &data->scalar_x); @@ -124,7 +127,7 @@ void bench_scalar_inverse_var(void* arg) { void bench_field_normalize(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 2000000; i++) { secp256k1_fe_normalize(&data->fe_x); @@ -133,7 +136,7 @@ void bench_field_normalize(void* arg) { void bench_field_normalize_weak(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 2000000; i++) { secp256k1_fe_normalize_weak(&data->fe_x); @@ -142,7 +145,7 @@ void bench_field_normalize_weak(void* arg) { void bench_field_mul(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 200000; i++) { secp256k1_fe_mul(&data->fe_x, &data->fe_x, &data->fe_y); @@ -151,7 +154,7 @@ void bench_field_mul(void* arg) { void bench_field_sqr(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 200000; i++) { secp256k1_fe_sqr(&data->fe_x, &data->fe_x); @@ -160,7 +163,7 @@ void bench_field_sqr(void* arg) { void bench_field_inverse(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 20000; i++) { secp256k1_fe_inv(&data->fe_x, &data->fe_x); @@ -170,7 +173,7 @@ void bench_field_inverse(void* arg) { void bench_field_inverse_var(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 20000; i++) { secp256k1_fe_inv_var(&data->fe_x, &data->fe_x); @@ -178,37 +181,37 @@ void bench_field_inverse_var(void* arg) { } } -void bench_field_sqrt_var(void* arg) { +void bench_field_sqrt(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 20000; i++) { - secp256k1_fe_sqrt_var(&data->fe_x, &data->fe_x); + secp256k1_fe_sqrt(&data->fe_x, &data->fe_x); secp256k1_fe_add(&data->fe_x, &data->fe_y); } } void bench_group_double_var(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 200000; i++) { - secp256k1_gej_double_var(&data->gej_x, &data->gej_x); + secp256k1_gej_double_var(&data->gej_x, &data->gej_x, NULL); } } void bench_group_add_var(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + 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); + secp256k1_gej_add_var(&data->gej_x, &data->gej_x, &data->gej_y, NULL); } } void bench_group_add_affine(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + 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); @@ -217,19 +220,38 @@ void bench_group_add_affine(void* arg) { void bench_group_add_affine_var(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + 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); + secp256k1_gej_add_ge_var(&data->gej_x, &data->gej_x, &data->ge_y, NULL); + } +} + +void bench_group_jacobi_var(void* arg) { + int i; + bench_inv *data = (bench_inv*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_gej_has_quad_y_var(&data->gej_x); } } void bench_ecmult_wnaf(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + 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); + } +} + +void bench_wnaf_const(void* arg) { + int i; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 20000; i++) { - secp256k1_ecmult_wnaf(data->wnaf, &data->scalar_x, WINDOW_A); + secp256k1_wnaf_const(data->wnaf, data->scalar_x, WINDOW_A); secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); } } @@ -237,8 +259,8 @@ void bench_ecmult_wnaf(void* arg) { void bench_sha256(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; - secp256k1_sha256_t sha; + bench_inv *data = (bench_inv*)arg; + secp256k1_sha256 sha; for (i = 0; i < 20000; i++) { secp256k1_sha256_initialize(&sha); @@ -249,8 +271,8 @@ void bench_sha256(void* arg) { void bench_hmac_sha256(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; - secp256k1_hmac_sha256_t hmac; + bench_inv *data = (bench_inv*)arg; + secp256k1_hmac_sha256 hmac; for (i = 0; i < 20000; i++) { secp256k1_hmac_sha256_initialize(&hmac, data->data, 32); @@ -261,15 +283,46 @@ void bench_hmac_sha256(void* arg) { void bench_rfc6979_hmac_sha256(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; - secp256k1_rfc6979_hmac_sha256_t rng; + bench_inv *data = (bench_inv*)arg; + secp256k1_rfc6979_hmac_sha256 rng; for (i = 0; i < 20000; i++) { - secp256k1_rfc6979_hmac_sha256_initialize(&rng, data->data, 32, data->data, 32, NULL, 0); + secp256k1_rfc6979_hmac_sha256_initialize(&rng, data->data, 64); secp256k1_rfc6979_hmac_sha256_generate(&rng, data->data, 32); } } +void bench_context_verify(void* arg) { + int i; + (void)arg; + for (i = 0; i < 20; i++) { + secp256k1_context_destroy(secp256k1_context_create(SECP256K1_CONTEXT_VERIFY)); + } +} + +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; @@ -278,14 +331,16 @@ int have_flag(int argc, char** argv, char *flag) { return 1; } while (argv != NULL && argv != argm) { - if (strcmp(*argv, flag) == 0) return 1; + if (strcmp(*argv, flag) == 0) { + return 1; + } argv++; } return 0; } int main(int argc, char **argv) { - bench_inv_t data; + 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); @@ -302,17 +357,26 @@ int main(int argc, char **argv) { 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_var", bench_field_sqrt_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 return 0; } diff --git a/src/secp256k1/src/bench_recover.c b/src/secp256k1/src/bench_recover.c index 56faed11a..506fc1880 100644 --- a/src/secp256k1/src/bench_recover.c +++ b/src/secp256k1/src/bench_recover.c @@ -1,46 +1,55 @@ /********************************************************************** - * Copyright (c) 2014 Pieter Wuille * + * Copyright (c) 2014-2015 Pieter Wuille * * Distributed under the MIT software license, see the accompanying * * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ #include "include/secp256k1.h" +#include "include/secp256k1_recovery.h" #include "util.h" #include "bench.h" typedef struct { - secp256k1_context_t *ctx; + secp256k1_context *ctx; unsigned char msg[32]; unsigned char sig[64]; -} bench_recover_t; +} bench_recover; void bench_recover(void* arg) { int i; - bench_recover_t *data = (bench_recover_t*)arg; - unsigned char pubkey[33]; + bench_recover *data = (bench_recover*)arg; + secp256k1_pubkey pubkey; + unsigned char pubkeyc[33]; for (i = 0; i < 20000; i++) { int j; - int pubkeylen = 33; - CHECK(secp256k1_ecdsa_recover_compact(data->ctx, data->msg, data->sig, pubkey, &pubkeylen, 1, i % 2)); + size_t pubkeylen = 33; + secp256k1_ecdsa_recoverable_signature sig; + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(data->ctx, &sig, data->sig, i % 2)); + CHECK(secp256k1_ecdsa_recover(data->ctx, &pubkey, &sig, data->msg)); + CHECK(secp256k1_ec_pubkey_serialize(data->ctx, pubkeyc, &pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED)); for (j = 0; j < 32; j++) { data->sig[j + 32] = data->msg[j]; /* Move former message to S. */ data->msg[j] = data->sig[j]; /* Move former R to message. */ - data->sig[j] = pubkey[j + 1]; /* Move recovered pubkey X coordinate to R (which must be a valid X coordinate). */ + data->sig[j] = pubkeyc[j + 1]; /* Move recovered pubkey X coordinate to R (which must be a valid X coordinate). */ } } } void bench_recover_setup(void* arg) { int i; - bench_recover_t *data = (bench_recover_t*)arg; + bench_recover *data = (bench_recover*)arg; - for (i = 0; i < 32; i++) data->msg[i] = 1 + i; - for (i = 0; i < 64; i++) data->sig[i] = 65 + i; + for (i = 0; i < 32; i++) { + data->msg[i] = 1 + i; + } + for (i = 0; i < 64; i++) { + data->sig[i] = 65 + i; + } } int main(void) { - bench_recover_t data; + bench_recover data; data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); diff --git a/src/secp256k1/src/bench_sign.c b/src/secp256k1/src/bench_sign.c index 072a37af5..544b43963 100644 --- a/src/secp256k1/src/bench_sign.c +++ b/src/secp256k1/src/bench_sign.c @@ -9,41 +9,47 @@ #include "bench.h" typedef struct { - secp256k1_context_t* ctx; + secp256k1_context* ctx; unsigned char msg[32]; unsigned char key[32]; -} bench_sign_t; +} bench_sign; static void bench_sign_setup(void* arg) { int i; - bench_sign_t *data = (bench_sign_t*)arg; + 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; + 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(void* arg) { +static void bench_sign_run(void* arg) { int i; - bench_sign_t *data = (bench_sign_t*)arg; + bench_sign *data = (bench_sign*)arg; - unsigned char sig[64]; + unsigned char sig[74]; for (i = 0; i < 20000; i++) { + size_t siglen = 74; int j; - int recid = 0; - CHECK(secp256k1_ecdsa_sign_compact(data->ctx, data->msg, sig, data->key, NULL, NULL, &recid)); + 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]; /* Move former R to message. */ - data->key[j] = sig[j + 32]; /* Move former S to key. */ + data->msg[j] = sig[j]; + data->key[j] = sig[j + 32]; } } } int main(void) { - bench_sign_t data; + bench_sign data; data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); - run_benchmark("ecdsa_sign", bench_sign, bench_sign_setup, NULL, &data, 10, 20000); + 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 index c8c82752c..418defa0a 100644 --- a/src/secp256k1/src/bench_verify.c +++ b/src/secp256k1/src/bench_verify.c @@ -11,14 +11,23 @@ #include "util.h" #include "bench.h" +#ifdef ENABLE_OPENSSL_TESTS +#include +#include +#include +#endif + typedef struct { - secp256k1_context_t *ctx; + secp256k1_context *ctx; unsigned char msg[32]; unsigned char key[32]; unsigned char sig[72]; - int siglen; + size_t siglen; unsigned char pubkey[33]; - int pubkeylen; + size_t pubkeylen; +#ifdef ENABLE_OPENSSL_TESTS + EC_GROUP* ec_group; +#endif } benchmark_verify_t; static void benchmark_verify(void* arg) { @@ -26,30 +35,77 @@ static void benchmark_verify(void* arg) { 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_ecdsa_verify(data->ctx, data->msg, data->sig, data->siglen, data->pubkey, data->pubkeylen) == (i == 0)); + 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; + 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; - secp256k1_ecdsa_sign(data.ctx, data.msg, data.sig, &data.siglen, data.key, NULL, NULL); + 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_create(data.ctx, data.pubkey, &data.pubkeylen, data.key, 1)); + 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/ecdsa.h b/src/secp256k1/src/ecdsa.h index 4ef78e8af..80590c7cc 100644 --- a/src/secp256k1/src/ecdsa.h +++ b/src/secp256k1/src/ecdsa.h @@ -4,21 +4,18 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECDSA_ -#define _SECP256K1_ECDSA_ +#ifndef SECP256K1_ECDSA_H +#define SECP256K1_ECDSA_H + +#include #include "scalar.h" #include "group.h" #include "ecmult.h" -typedef struct { - secp256k1_scalar_t r, s; -} secp256k1_ecdsa_sig_t; - -static int secp256k1_ecdsa_sig_parse(secp256k1_ecdsa_sig_t *r, const unsigned char *sig, int size); -static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, int *size, const secp256k1_ecdsa_sig_t *a); -static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context_t *ctx, const secp256k1_ecdsa_sig_t *sig, const secp256k1_ge_t *pubkey, const secp256k1_scalar_t *message); -static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context_t *ctx, secp256k1_ecdsa_sig_t *sig, const secp256k1_scalar_t *seckey, const secp256k1_scalar_t *message, const secp256k1_scalar_t *nonce, int *recid); -static int secp256k1_ecdsa_sig_recover(const secp256k1_ecmult_context_t *ctx, const secp256k1_ecdsa_sig_t *sig, secp256k1_ge_t *pubkey, const secp256k1_scalar_t *message, int recid); +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_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 +#endif /* SECP256K1_ECDSA_H */ diff --git a/src/secp256k1/src/ecdsa_impl.h b/src/secp256k1/src/ecdsa_impl.h index ed1d22818..c3400042d 100644 --- a/src/secp256k1/src/ecdsa_impl.h +++ b/src/secp256k1/src/ecdsa_impl.h @@ -1,12 +1,12 @@ /********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * + * Copyright (c) 2013-2015 Pieter Wuille * * Distributed under the MIT software license, see the accompanying * * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECDSA_IMPL_H_ -#define _SECP256K1_ECDSA_IMPL_H_ +#ifndef SECP256K1_ECDSA_IMPL_H +#define SECP256K1_ECDSA_IMPL_H #include "scalar.h" #include "field.h" @@ -28,7 +28,7 @@ * sage: '%x' % (EllipticCurve ([F (a), F (b)]).order()) * 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141' */ -static const secp256k1_fe_t secp256k1_ecdsa_const_order_as_fe = SECP256K1_FE_CONST( +static const secp256k1_fe secp256k1_ecdsa_const_order_as_fe = SECP256K1_FE_CONST( 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, 0xBAAEDCE6UL, 0xAF48A03BUL, 0xBFD25E8CUL, 0xD0364141UL ); @@ -42,82 +42,148 @@ static const secp256k1_fe_t secp256k1_ecdsa_const_order_as_fe = SECP256K1_FE_CON * sage: '%x' % (p - EllipticCurve ([F (a), F (b)]).order()) * '14551231950b75fc4402da1722fc9baee' */ -static const secp256k1_fe_t secp256k1_ecdsa_const_p_minus_order = SECP256K1_FE_CONST( +static const secp256k1_fe secp256k1_ecdsa_const_p_minus_order = SECP256K1_FE_CONST( 0, 0, 0, 1, 0x45512319UL, 0x50B75FC4UL, 0x402DA172UL, 0x2FC9BAEEUL ); -static int secp256k1_ecdsa_sig_parse(secp256k1_ecdsa_sig_t *r, const unsigned char *sig, int size) { - unsigned char ra[32] = {0}, sa[32] = {0}; - const unsigned char *rp; - const unsigned char *sp; - int lenr; - int lens; - int overflow; - if (sig[0] != 0x30) { - return 0; +static int secp256k1_der_read_len(const unsigned char **sigp, const unsigned char *sigend) { + int lenleft, b1; + size_t ret = 0; + if (*sigp >= sigend) { + return -1; } - lenr = sig[3]; - if (5+lenr >= size) { - return 0; + b1 = *((*sigp)++); + if (b1 == 0xFF) { + /* X.690-0207 8.1.3.5.c the value 0xFF shall not be used. */ + return -1; } - lens = sig[lenr+5]; - if (sig[1] != lenr+lens+4) { - return 0; + if ((b1 & 0x80) == 0) { + /* X.690-0207 8.1.3.4 short form length octets */ + return b1; } - if (lenr+lens+6 > size) { - return 0; + if (b1 == 0x80) { + /* Indefinite length is not allowed in DER. */ + return -1; + } + /* X.690-207 8.1.3.5 long form length octets */ + lenleft = b1 & 0x7F; + if (lenleft > sigend - *sigp) { + return -1; + } + if (**sigp == 0) { + /* Not the shortest possible length encoding. */ + return -1; + } + if ((size_t)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; } - if (sig[2] != 0x02) { + while (lenleft > 0) { + ret = (ret << 8) | **sigp; + if (ret + lenleft > (size_t)(sigend - *sigp)) { + /* Result exceeds the length of the passed array. */ + return -1; + } + (*sigp)++; + lenleft--; + } + if (ret < 128) { + /* Not the shortest possible length encoding. */ + return -1; + } + return ret; +} + +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; + + if (*sig == sigend || **sig != 0x02) { + /* Not a primitive integer (X.690-0207 8.3.1). */ return 0; } - if (lenr == 0) { + (*sig)++; + rlen = secp256k1_der_read_len(sig, sigend); + if (rlen <= 0 || (*sig) + rlen > sigend) { + /* Exceeds bounds or not at least length 1 (X.690-0207 8.3.1). */ return 0; } - if (sig[lenr+4] != 0x02) { + if (**sig == 0x00 && rlen > 1 && (((*sig)[1]) & 0x80) == 0x00) { + /* Excessive 0x00 padding. */ return 0; } - if (lens == 0) { + if (**sig == 0xFF && rlen > 1 && (((*sig)[1]) & 0x80) == 0x80) { + /* Excessive 0xFF padding. */ return 0; } - sp = sig + 6 + lenr; - while (lens > 0 && sp[0] == 0) { - lens--; - sp++; + if ((**sig & 0x80) == 0x80) { + /* Negative. */ + overflow = 1; + } + while (rlen > 0 && **sig == 0) { + /* Skip leading zero bytes */ + rlen--; + (*sig)++; + } + if (rlen > 32) { + overflow = 1; + } + if (!overflow) { + memcpy(ra + 32 - rlen, *sig, rlen); + secp256k1_scalar_set_b32(r, ra, &overflow); + } + if (overflow) { + secp256k1_scalar_set_int(r, 0); } - if (lens > 32) { + (*sig) += rlen; + return 1; +} + +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; + if (sig == sigend || *(sig++) != 0x30) { + /* The encoding doesn't start with a constructed sequence (X.690-0207 8.9.1). */ return 0; } - rp = sig + 4; - while (lenr > 0 && rp[0] == 0) { - lenr--; - rp++; + rlen = secp256k1_der_read_len(&sig, sigend); + if (rlen < 0 || sig + rlen > sigend) { + /* Tuple exceeds bounds */ + return 0; } - if (lenr > 32) { + if (sig + rlen != sigend) { + /* Garbage after tuple. */ return 0; } - memcpy(ra + 32 - lenr, rp, lenr); - memcpy(sa + 32 - lens, sp, lens); - overflow = 0; - secp256k1_scalar_set_b32(&r->r, ra, &overflow); - if (overflow) { + + if (!secp256k1_der_parse_integer(rr, &sig, sigend)) { return 0; } - secp256k1_scalar_set_b32(&r->s, sa, &overflow); - if (overflow) { + if (!secp256k1_der_parse_integer(rs, &sig, sigend)) { + return 0; + } + + if (sig != sigend) { + /* Trailing garbage inside tuple. */ return 0; } + return 1; } -static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, int *size, const secp256k1_ecdsa_sig_t *a) { +static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const secp256k1_scalar* ar, const secp256k1_scalar* as) { unsigned char r[33] = {0}, s[33] = {0}; unsigned char *rp = r, *sp = s; - int lenR = 33, lenS = 33; - secp256k1_scalar_get_b32(&r[1], &a->r); - secp256k1_scalar_get_b32(&s[1], &a->s); + size_t lenR = 33, lenS = 33; + secp256k1_scalar_get_b32(&r[1], ar); + secp256k1_scalar_get_b32(&s[1], as); while (lenR > 1 && rp[0] == 0 && rp[1] < 0x80) { lenR--; rp++; } while (lenS > 1 && sp[0] == 0 && sp[1] < 0x80) { lenS--; sp++; } if (*size < 6+lenS+lenR) { + *size = 6 + lenS + lenR; return 0; } *size = 6 + lenS + lenR; @@ -132,26 +198,41 @@ static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, int *size, const se return 1; } -static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context_t *ctx, const secp256k1_ecdsa_sig_t *sig, const secp256k1_ge_t *pubkey, const secp256k1_scalar_t *message) { +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) { unsigned char c[32]; - secp256k1_scalar_t sn, u1, u2; - secp256k1_fe_t xr; - secp256k1_gej_t pubkeyj; - secp256k1_gej_t pr; + secp256k1_scalar sn, u1, u2; +#if !defined(EXHAUSTIVE_TEST_ORDER) + secp256k1_fe xr; +#endif + secp256k1_gej pubkeyj; + secp256k1_gej pr; - if (secp256k1_scalar_is_zero(&sig->r) || secp256k1_scalar_is_zero(&sig->s)) { + if (secp256k1_scalar_is_zero(sigr) || secp256k1_scalar_is_zero(sigs)) { return 0; } - secp256k1_scalar_inverse_var(&sn, &sig->s); + secp256k1_scalar_inverse_var(&sn, sigs); secp256k1_scalar_mul(&u1, &sn, message); - secp256k1_scalar_mul(&u2, &sn, &sig->r); + secp256k1_scalar_mul(&u2, &sn, sigr); secp256k1_gej_set_ge(&pubkeyj, pubkey); secp256k1_ecmult(ctx, &pr, &pubkeyj, &u2, &u1); if (secp256k1_gej_is_infinity(&pr)) { return 0; } - secp256k1_scalar_get_b32(c, &sig->r); + +#if defined(EXHAUSTIVE_TEST_ORDER) +{ + secp256k1_scalar computed_r; + secp256k1_ge pr_ge; + secp256k1_ge_set_gej(&pr_ge, &pr); + secp256k1_fe_normalize(&pr_ge.x); + + secp256k1_fe_get_b32(c, &pr_ge.x); + secp256k1_scalar_set_b32(&computed_r, c, NULL); + return secp256k1_scalar_eq(sigr, &computed_r); +} +#else + secp256k1_scalar_get_b32(c, sigr); secp256k1_fe_set_b32(&xr, c); /** We now have the recomputed R point in pr, and its claimed x coordinate (modulo n) @@ -171,11 +252,11 @@ static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context_t *ctx, con * secp256k1_gej_eq_x implements the (xr * pr.z^2 mod p == pr.x) test. */ if (secp256k1_gej_eq_x_var(&xr, &pr)) { - /* xr.x == xr * xr.z^2 mod p, so the signature is valid. */ + /* xr * pr.z^2 mod p == pr.x, so the signature is valid. */ return 1; } if (secp256k1_fe_cmp_var(&xr, &secp256k1_ecdsa_const_p_minus_order) >= 0) { - /* xr + p >= n, so we can skip testing the second case. */ + /* xr + n >= p, so we can skip testing the second case. */ return 0; } secp256k1_fe_add(&xr, &secp256k1_ecdsa_const_order_as_fe); @@ -184,46 +265,14 @@ static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context_t *ctx, con return 1; } return 0; +#endif } -static int secp256k1_ecdsa_sig_recover(const secp256k1_ecmult_context_t *ctx, const secp256k1_ecdsa_sig_t *sig, secp256k1_ge_t *pubkey, const secp256k1_scalar_t *message, int recid) { - unsigned char brx[32]; - secp256k1_fe_t fx; - secp256k1_ge_t x; - secp256k1_gej_t xj; - secp256k1_scalar_t rn, u1, u2; - secp256k1_gej_t qj; - - if (secp256k1_scalar_is_zero(&sig->r) || secp256k1_scalar_is_zero(&sig->s)) { - return 0; - } - - secp256k1_scalar_get_b32(brx, &sig->r); - VERIFY_CHECK(secp256k1_fe_set_b32(&fx, brx)); /* brx comes from a scalar, so is less than the order; certainly less than p */ - if (recid & 2) { - if (secp256k1_fe_cmp_var(&fx, &secp256k1_ecdsa_const_p_minus_order) >= 0) { - return 0; - } - secp256k1_fe_add(&fx, &secp256k1_ecdsa_const_order_as_fe); - } - if (!secp256k1_ge_set_xo_var(&x, &fx, recid & 1)) { - return 0; - } - secp256k1_gej_set_ge(&xj, &x); - secp256k1_scalar_inverse_var(&rn, &sig->r); - secp256k1_scalar_mul(&u1, &rn, message); - secp256k1_scalar_negate(&u1, &u1); - secp256k1_scalar_mul(&u2, &rn, &sig->s); - secp256k1_ecmult(ctx, &qj, &xj, &u2, &u1); - secp256k1_ge_set_gej_var(pubkey, &qj); - return !secp256k1_gej_is_infinity(&qj); -} - -static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context_t *ctx, secp256k1_ecdsa_sig_t *sig, const secp256k1_scalar_t *seckey, const secp256k1_scalar_t *message, const secp256k1_scalar_t *nonce, int *recid) { +static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid) { unsigned char b[32]; - secp256k1_gej_t rp; - secp256k1_ge_t r; - secp256k1_scalar_t n; + secp256k1_gej rp; + secp256k1_ge r; + secp256k1_scalar n; int overflow = 0; secp256k1_ecmult_gen(ctx, &rp, nonce); @@ -231,28 +280,29 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context_t *ctx, s secp256k1_fe_normalize(&r.x); secp256k1_fe_normalize(&r.y); secp256k1_fe_get_b32(b, &r.x); - secp256k1_scalar_set_b32(&sig->r, b, &overflow); - if (secp256k1_scalar_is_zero(&sig->r)) { - /* P.x = order is on the curve, so technically sig->r could end up zero, which would be an invalid signature. */ - secp256k1_gej_clear(&rp); - secp256k1_ge_clear(&r); - return 0; - } + 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); } - secp256k1_scalar_mul(&n, &sig->r, seckey); + secp256k1_scalar_mul(&n, sigr, seckey); secp256k1_scalar_add(&n, &n, message); - secp256k1_scalar_inverse(&sig->s, nonce); - secp256k1_scalar_mul(&sig->s, &sig->s, &n); + secp256k1_scalar_inverse(sigs, nonce); + secp256k1_scalar_mul(sigs, sigs, &n); secp256k1_scalar_clear(&n); secp256k1_gej_clear(&rp); secp256k1_ge_clear(&r); - if (secp256k1_scalar_is_zero(&sig->s)) { + if (secp256k1_scalar_is_zero(sigs)) { return 0; } - if (secp256k1_scalar_is_high(&sig->s)) { - secp256k1_scalar_negate(&sig->s, &sig->s); + if (secp256k1_scalar_is_high(sigs)) { + secp256k1_scalar_negate(sigs, sigs); if (recid) { *recid ^= 1; } @@ -260,4 +310,4 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context_t *ctx, s return 1; } -#endif +#endif /* SECP256K1_ECDSA_IMPL_H */ diff --git a/src/secp256k1/src/eckey.h b/src/secp256k1/src/eckey.h index 53b818485..b621f1e6c 100644 --- a/src/secp256k1/src/eckey.h +++ b/src/secp256k1/src/eckey.h @@ -4,23 +4,22 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECKEY_ -#define _SECP256K1_ECKEY_ +#ifndef SECP256K1_ECKEY_H +#define SECP256K1_ECKEY_H + +#include #include "group.h" #include "scalar.h" #include "ecmult.h" #include "ecmult_gen.h" -static int secp256k1_eckey_pubkey_parse(secp256k1_ge_t *elem, const unsigned char *pub, int size); -static int secp256k1_eckey_pubkey_serialize(secp256k1_ge_t *elem, unsigned char *pub, int *size, int compressed); - -static int secp256k1_eckey_privkey_parse(secp256k1_scalar_t *key, const unsigned char *privkey, int privkeylen); -static int secp256k1_eckey_privkey_serialize(const secp256k1_ecmult_gen_context_t *ctx, unsigned char *privkey, int *privkeylen, const secp256k1_scalar_t *key, int compressed); +static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char *pub, size_t size); +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_t *key, const secp256k1_scalar_t *tweak); -static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context_t *ctx, secp256k1_ge_t *key, const secp256k1_scalar_t *tweak); -static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar_t *key, const secp256k1_scalar_t *tweak); -static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context_t *ctx, secp256k1_ge_t *key, const secp256k1_scalar_t *tweak); +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_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); -#endif +#endif /* SECP256K1_ECKEY_H */ diff --git a/src/secp256k1/src/eckey_impl.h b/src/secp256k1/src/eckey_impl.h index a332bd34e..1ab9a68ec 100644 --- a/src/secp256k1/src/eckey_impl.h +++ b/src/secp256k1/src/eckey_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECKEY_IMPL_H_ -#define _SECP256K1_ECKEY_IMPL_H_ +#ifndef SECP256K1_ECKEY_IMPL_H +#define SECP256K1_ECKEY_IMPL_H #include "eckey.h" @@ -14,17 +14,18 @@ #include "group.h" #include "ecmult_gen.h" -static int secp256k1_eckey_pubkey_parse(secp256k1_ge_t *elem, const unsigned char *pub, int size) { - if (size == 33 && (pub[0] == 0x02 || pub[0] == 0x03)) { - secp256k1_fe_t x; - return secp256k1_fe_set_b32(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == 0x03); +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)) { - secp256k1_fe_t x, y; + secp256k1_fe x, y; if (!secp256k1_fe_set_b32(&x, pub+1) || !secp256k1_fe_set_b32(&y, pub+33)) { return 0; } secp256k1_ge_set_xy(elem, &x, &y); - if ((pub[0] == 0x06 || pub[0] == 0x07) && secp256k1_fe_is_odd(&y) != (pub[0] == 0x07)) { + if ((pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD) && + secp256k1_fe_is_odd(&y) != (pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD)) { return 0; } return secp256k1_ge_is_valid_var(elem); @@ -33,7 +34,7 @@ static int secp256k1_eckey_pubkey_parse(secp256k1_ge_t *elem, const unsigned cha } } -static int secp256k1_eckey_pubkey_serialize(secp256k1_ge_t *elem, unsigned char *pub, int *size, int compressed) { +static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *pub, size_t *size, int compressed) { if (secp256k1_ge_is_infinity(elem)) { return 0; } @@ -42,119 +43,16 @@ static int secp256k1_eckey_pubkey_serialize(secp256k1_ge_t *elem, unsigned char secp256k1_fe_get_b32(&pub[1], &elem->x); if (compressed) { *size = 33; - pub[0] = 0x02 | (secp256k1_fe_is_odd(&elem->y) ? 0x01 : 0x00); + pub[0] = secp256k1_fe_is_odd(&elem->y) ? SECP256K1_TAG_PUBKEY_ODD : SECP256K1_TAG_PUBKEY_EVEN; } else { *size = 65; - pub[0] = 0x04; + pub[0] = SECP256K1_TAG_PUBKEY_UNCOMPRESSED; secp256k1_fe_get_b32(&pub[33], &elem->y); } return 1; } -static int secp256k1_eckey_privkey_parse(secp256k1_scalar_t *key, const unsigned char *privkey, int privkeylen) { - unsigned char c[32] = {0}; - const unsigned char *end = privkey + privkeylen; - int lenb = 0; - int len = 0; - int overflow = 0; - /* sequence header */ - if (end < privkey+1 || *privkey != 0x30) { - return 0; - } - privkey++; - /* sequence length constructor */ - if (end < privkey+1 || !(*privkey & 0x80)) { - return 0; - } - lenb = *privkey & ~0x80; privkey++; - if (lenb < 1 || lenb > 2) { - return 0; - } - if (end < privkey+lenb) { - return 0; - } - /* sequence length */ - len = privkey[lenb-1] | (lenb > 1 ? privkey[lenb-2] << 8 : 0); - privkey += lenb; - if (end < privkey+len) { - return 0; - } - /* sequence element 0: version number (=1) */ - if (end < privkey+3 || privkey[0] != 0x02 || privkey[1] != 0x01 || privkey[2] != 0x01) { - return 0; - } - privkey += 3; - /* sequence element 1: octet string, up to 32 bytes */ - if (end < privkey+2 || privkey[0] != 0x04 || privkey[1] > 0x20 || end < privkey+2+privkey[1]) { - return 0; - } - memcpy(c + 32 - privkey[1], privkey + 2, privkey[1]); - secp256k1_scalar_set_b32(key, c, &overflow); - memset(c, 0, 32); - return !overflow; -} - -static int secp256k1_eckey_privkey_serialize(const secp256k1_ecmult_gen_context_t *ctx, unsigned char *privkey, int *privkeylen, const secp256k1_scalar_t *key, int compressed) { - secp256k1_gej_t rp; - secp256k1_ge_t r; - int pubkeylen = 0; - secp256k1_ecmult_gen(ctx, &rp, key); - secp256k1_ge_set_gej(&r, &rp); - if (compressed) { - static const unsigned char begin[] = { - 0x30,0x81,0xD3,0x02,0x01,0x01,0x04,0x20 - }; - static const unsigned char middle[] = { - 0xA0,0x81,0x85,0x30,0x81,0x82,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48, - 0xCE,0x3D,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,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04, - 0x21,0x02,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,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,0xA1,0x24,0x03,0x22,0x00 - }; - unsigned char *ptr = privkey; - memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin); - secp256k1_scalar_get_b32(ptr, key); ptr += 32; - memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle); - if (!secp256k1_eckey_pubkey_serialize(&r, ptr, &pubkeylen, 1)) { - return 0; - } - ptr += pubkeylen; - *privkeylen = ptr - privkey; - } else { - static const unsigned char begin[] = { - 0x30,0x82,0x01,0x13,0x02,0x01,0x01,0x04,0x20 - }; - static const unsigned char middle[] = { - 0xA0,0x81,0xA5,0x30,0x81,0xA2,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48, - 0xCE,0x3D,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,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04, - 0x41,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,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,0xA1,0x44,0x03,0x42,0x00 - }; - unsigned char *ptr = privkey; - memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin); - secp256k1_scalar_get_b32(ptr, key); ptr += 32; - memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle); - if (!secp256k1_eckey_pubkey_serialize(&r, ptr, &pubkeylen, 0)) { - return 0; - } - ptr += pubkeylen; - *privkeylen = ptr - privkey; - } - return 1; -} - -static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar_t *key, const secp256k1_scalar_t *tweak) { +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; @@ -162,9 +60,9 @@ static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar_t *key, const secp return 1; } -static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context_t *ctx, secp256k1_ge_t *key, const secp256k1_scalar_t *tweak) { - secp256k1_gej_t pt; - secp256k1_scalar_t one; +static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx, 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); @@ -176,7 +74,7 @@ static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context_t *ct return 1; } -static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar_t *key, const secp256k1_scalar_t *tweak) { +static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak) { if (secp256k1_scalar_is_zero(tweak)) { return 0; } @@ -185,9 +83,9 @@ static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar_t *key, const secp return 1; } -static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context_t *ctx, secp256k1_ge_t *key, const secp256k1_scalar_t *tweak) { - secp256k1_scalar_t zero; - secp256k1_gej_t pt; +static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak) { + secp256k1_scalar zero; + secp256k1_gej pt; if (secp256k1_scalar_is_zero(tweak)) { return 0; } @@ -199,4 +97,4 @@ static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context_t *ct return 1; } -#endif +#endif /* SECP256K1_ECKEY_IMPL_H */ diff --git a/src/secp256k1/src/ecmult.h b/src/secp256k1/src/ecmult.h index bab9e4ef5..6d44aba60 100644 --- a/src/secp256k1/src/ecmult.h +++ b/src/secp256k1/src/ecmult.h @@ -4,28 +4,28 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECMULT_ -#define _SECP256K1_ECMULT_ +#ifndef SECP256K1_ECMULT_H +#define SECP256K1_ECMULT_H #include "num.h" #include "group.h" typedef struct { /* For accelerating the computation of a*P + b*G: */ - secp256k1_ge_storage_t (*pre_g)[]; /* odd multiples of the generator */ + secp256k1_ge_storage (*pre_g)[]; /* odd multiples of the generator */ #ifdef USE_ENDOMORPHISM - secp256k1_ge_storage_t (*pre_g_128)[]; /* odd multiples of 2^128*generator */ + secp256k1_ge_storage (*pre_g_128)[]; /* odd multiples of 2^128*generator */ #endif -} secp256k1_ecmult_context_t; +} secp256k1_ecmult_context; -static void secp256k1_ecmult_context_init(secp256k1_ecmult_context_t *ctx); -static void secp256k1_ecmult_context_build(secp256k1_ecmult_context_t *ctx); -static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context_t *dst, - const secp256k1_ecmult_context_t *src); -static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context_t *ctx); -static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context_t *ctx); +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); /** Double multiply: R = na*A + ng*G */ -static void secp256k1_ecmult(const secp256k1_ecmult_context_t *ctx, secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_scalar_t *na, const secp256k1_scalar_t *ng); +static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng); -#endif +#endif /* SECP256K1_ECMULT_H */ diff --git a/src/secp256k1/src/ecmult_const.h b/src/secp256k1/src/ecmult_const.h new file mode 100644 index 000000000..72bf7d758 --- /dev/null +++ b/src/secp256k1/src/ecmult_const.h @@ -0,0 +1,15 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_ECMULT_CONST_H +#define SECP256K1_ECMULT_CONST_H + +#include "scalar.h" +#include "group.h" + +static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q); + +#endif /* SECP256K1_ECMULT_CONST_H */ diff --git a/src/secp256k1/src/ecmult_const_impl.h b/src/secp256k1/src/ecmult_const_impl.h new file mode 100644 index 000000000..7d7a172b7 --- /dev/null +++ b/src/secp256k1/src/ecmult_const_impl.h @@ -0,0 +1,240 @@ +/********************************************************************** + * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_ECMULT_CONST_IMPL_H +#define SECP256K1_ECMULT_CONST_IMPL_H + +#include "scalar.h" +#include "group.h" +#include "ecmult_const.h" +#include "ecmult_impl.h" + +#ifdef USE_ENDOMORPHISM + #define WNAF_BITS 128 +#else + #define WNAF_BITS 256 +#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; \ + 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++) { \ + /* 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); \ + } \ + (r)->infinity = 0; \ + secp256k1_fe_negate(&neg_y, &(r)->y, 1); \ + secp256k1_fe_cmov(&(r)->y, &neg_y, (n) != abs_n); \ +} while(0) + + +/** 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 *scalar) { + secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_ge tmpa; + secp256k1_fe Z; + + 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 + + int i; + secp256k1_scalar sc = *scalar; + + /* 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); +#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. + */ + 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_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; + 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 + } + + secp256k1_fe_mul(&r->z, &r->z, &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); + + /* 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 + + /* Apply the correction */ + secp256k1_ge_from_storage(&correction, &correction_1_stor); + secp256k1_ge_neg(&correction, &correction); + secp256k1_gej_add_ge(r, r, &correction); + +#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 + } +} + +#endif /* SECP256K1_ECMULT_CONST_IMPL_H */ diff --git a/src/secp256k1/src/ecmult_gen.h b/src/secp256k1/src/ecmult_gen.h index 3745633c4..7564b7015 100644 --- a/src/secp256k1/src/ecmult_gen.h +++ b/src/secp256k1/src/ecmult_gen.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECMULT_GEN_ -#define _SECP256K1_ECMULT_GEN_ +#ifndef SECP256K1_ECMULT_GEN_H +#define SECP256K1_ECMULT_GEN_H #include "scalar.h" #include "group.h" @@ -23,21 +23,21 @@ typedef struct { * 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_t (*prec)[64][16]; /* prec[j][i] = 16^j * i * G + U_i */ - secp256k1_scalar_t blind; - secp256k1_gej_t initial; -} secp256k1_ecmult_gen_context_t; + secp256k1_ge_storage (*prec)[64][16]; /* prec[j][i] = 16^j * i * G + U_i */ + secp256k1_scalar blind; + secp256k1_gej initial; +} secp256k1_ecmult_gen_context; -static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context_t* ctx); -static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context_t* ctx); -static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context_t *dst, - const secp256k1_ecmult_gen_context_t* src); -static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context_t* ctx); -static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context_t* ctx); +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_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_t* ctx, secp256k1_gej_t *r, const secp256k1_scalar_t *a); +static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context* ctx, secp256k1_gej *r, const secp256k1_scalar *a); -static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context_t *ctx, const unsigned char *seed32); +static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const unsigned char *seed32); -#endif +#endif /* SECP256K1_ECMULT_GEN_H */ diff --git a/src/secp256k1/src/ecmult_gen_impl.h b/src/secp256k1/src/ecmult_gen_impl.h index 4697753ac..714f02e94 100644 --- a/src/secp256k1/src/ecmult_gen_impl.h +++ b/src/secp256k1/src/ecmult_gen_impl.h @@ -4,29 +4,33 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECMULT_GEN_IMPL_H_ -#define _SECP256K1_ECMULT_GEN_IMPL_H_ +#ifndef SECP256K1_ECMULT_GEN_IMPL_H +#define SECP256K1_ECMULT_GEN_IMPL_H #include "scalar.h" #include "group.h" #include "ecmult_gen.h" #include "hash_impl.h" - -static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context_t *ctx) { +#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_t *ctx) { - secp256k1_ge_t prec[1024]; - secp256k1_gej_t gj; - secp256k1_gej_t nums_gej; +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; } - - ctx->prec = (secp256k1_ge_storage_t (*)[64][16])checked_malloc(sizeof(*ctx->prec)); +#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); @@ -34,77 +38,93 @@ static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context_t *c /* 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_t nums_x; - secp256k1_ge_t nums_ge; - VERIFY_CHECK(secp256k1_fe_set_b32(&nums_x, nums_b32)); - VERIFY_CHECK(secp256k1_ge_set_xo_var(&nums_ge, &nums_x, 0)); + 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); + secp256k1_gej_add_ge_var(&nums_gej, &nums_gej, &secp256k1_ge_const_g, NULL); } /* compute prec. */ { - secp256k1_gej_t precj[1024]; /* Jacobian versions of prec. */ - secp256k1_gej_t gbase; - secp256k1_gej_t numsbase; + 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); + 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); + secp256k1_gej_double_var(&gbase, &gbase, NULL); } /* Multiply numbase by 2. */ - secp256k1_gej_double_var(&numsbase, &numsbase); + 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); + secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL); } } - secp256k1_ge_set_all_gej_var(1024, prec, precj); + 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 secp256k1_ecmult_gen_blind(ctx, NULL); } -static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context_t* ctx) { +static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context* ctx) { return ctx->prec != NULL; } -static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context_t *dst, - const secp256k1_ecmult_gen_context_t *src) { +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 { - dst->prec = (secp256k1_ge_storage_t (*)[64][16])checked_malloc(sizeof(*dst->prec)); +#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_t *ctx) { +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; } -static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context_t *ctx, secp256k1_gej_t *r, const secp256k1_scalar_t *gn) { - secp256k1_ge_t add; - secp256k1_ge_storage_t adds; - secp256k1_scalar_t gnb; +static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp256k1_gej *r, const secp256k1_scalar *gn) { + secp256k1_ge add; + secp256k1_ge_storage adds; + secp256k1_scalar gnb; int bits; int i, j; memset(&adds, 0, sizeof(adds)); @@ -136,14 +156,15 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context_t *ctx, secp } /* Setup blinding values for secp256k1_ecmult_gen. */ -static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context_t *ctx, const unsigned char *seed32) { - secp256k1_scalar_t b; - secp256k1_gej_t gb; - secp256k1_fe_t s; +static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const unsigned char *seed32) { + secp256k1_scalar b; + secp256k1_gej gb; + secp256k1_fe s; unsigned char nonce32[32]; - secp256k1_rfc6979_hmac_sha256_t rng; + secp256k1_rfc6979_hmac_sha256 rng; int retry; - if (!seed32) { + unsigned char keydata[64] = {0}; + 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); @@ -155,13 +176,18 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context_t *ctx, cons * 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. */ - secp256k1_rfc6979_hmac_sha256_initialize(&rng, seed32 ? seed32 : nonce32, 32, nonce32, 32, NULL, 0); + memcpy(keydata, nonce32, 32); + if (seed32 != NULL) { + memcpy(keydata + 32, seed32, 32); + } + secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, seed32 ? 64 : 32); + 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); + } 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); @@ -170,7 +196,7 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context_t *ctx, cons 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); + } while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > order. */ secp256k1_rfc6979_hmac_sha256_finalize(&rng); memset(nonce32, 0, 32); secp256k1_ecmult_gen(ctx, &gb, &b); @@ -181,4 +207,4 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context_t *ctx, cons secp256k1_gej_clear(&gb); } -#endif +#endif /* SECP256K1_ECMULT_GEN_IMPL_H */ diff --git a/src/secp256k1/src/ecmult_impl.h b/src/secp256k1/src/ecmult_impl.h index 1b2856f83..93d3794cb 100644 --- a/src/secp256k1/src/ecmult_impl.h +++ b/src/secp256k1/src/ecmult_impl.h @@ -4,16 +4,32 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECMULT_IMPL_H_ -#define _SECP256K1_ECMULT_IMPL_H_ +#ifndef SECP256K1_ECMULT_IMPL_H +#define SECP256K1_ECMULT_IMPL_H + +#include #include "group.h" #include "scalar.h" #include "ecmult.h" +#if defined(EXHAUSTIVE_TEST_ORDER) +/* We need to lower these values for exhaustive tests because + * the tables cannot have infinities in them (this breaks the + * 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 @@ -23,63 +39,109 @@ /** One table for window size 16: 1.375 MiB. */ #define WINDOW_G 16 #endif +#endif -/** Fill a table 'pre' with precomputed odd multiples of a. W determines the size of the table. - * pre will contains the values [1*a,3*a,5*a,...,(2^(w-1)-1)*a], so it needs place for - * 2^(w-2) entries. - * - * There are two versions of this function: - * - secp256k1_ecmult_precomp_wnaf_gej, which operates on group elements in jacobian notation, - * fast to precompute, but slower to use in later additions. - * - secp256k1_ecmult_precomp_wnaf_ge, which operates on group elements in affine notations, - * (much) slower to precompute, but a bit faster to use in later additions. - * To compute a*P + b*G, we use the jacobian version for P, and the affine version for G, as - * G is constant, so it only needs to be done once in advance. +/** The number of entries a table with precomputed multiples needs to have. */ +#define ECMULT_TABLE_SIZE(w) (1 << ((w)-2)) + +/** 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. */ -static void secp256k1_ecmult_table_precomp_gej_var(secp256k1_gej_t *pre, const secp256k1_gej_t *a, int w) { - secp256k1_gej_t d; +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; int i; - pre[0] = *a; - secp256k1_gej_double_var(&d, &pre[0]); - for (i = 1; i < (1 << (w-2)); i++) { - secp256k1_gej_add_var(&pre[i], &d, &pre[i-1]); + + VERIFY_CHECK(!a->infinity); + + 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. + */ + 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; + + zr[0] = d.z; + for (i = 1; i < n; i++) { + secp256k1_gej_add_ge_var(&prej[i], &prej[i-1], &d_ge, &zr[i]); } + + /* + * 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. + */ + secp256k1_fe_mul(&prej[n-1].z, &prej[n-1].z, &d.z); } -static void secp256k1_ecmult_table_precomp_ge_storage_var(secp256k1_ge_storage_t *pre, const secp256k1_gej_t *a, int w) { - secp256k1_gej_t d; +/** 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); +} + +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; - const int table_size = 1 << (w-2); - secp256k1_gej_t *prej = (secp256k1_gej_t *)checked_malloc(sizeof(secp256k1_gej_t) * table_size); - secp256k1_ge_t *prea = (secp256k1_ge_t *)checked_malloc(sizeof(secp256k1_ge_t) * table_size); - prej[0] = *a; - secp256k1_gej_double_var(&d, a); - for (i = 1; i < table_size; i++) { - secp256k1_gej_add_var(&prej[i], &d, &prej[i-1]); - } - secp256k1_ge_set_all_gej_var(table_size, prea, prej); - for (i = 0; i < table_size; 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]); } - free(prej); + free(prea); + free(prej); + free(zr); } -/** The number of entries a table with precomputed multiples needs to have. */ -#define ECMULT_TABLE_SIZE(w) (1 << ((w)-2)) - /** The following two macro retrieves a particular odd multiple from a table * of precomputed multiples. */ -#define ECMULT_TABLE_GET_GEJ(r,pre,n,w) do { \ +#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_gej_neg((r), &(pre)[(-(n)-1)/2]); \ + 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)); \ @@ -92,15 +154,15 @@ static void secp256k1_ecmult_table_precomp_ge_storage_var(secp256k1_ge_storage_t } \ } while(0) -static void secp256k1_ecmult_context_init(secp256k1_ecmult_context_t *ctx) { +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_t *ctx) { - secp256k1_gej_t gj; +static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const secp256k1_callback *cb) { + secp256k1_gej gj; if (ctx->pre_g != NULL) { return; @@ -109,35 +171,35 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context_t *ctx) { /* get the generator */ secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g); - ctx->pre_g = (secp256k1_ge_storage_t (*)[])checked_malloc(sizeof((*ctx->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_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_table_precomp_ge_storage_var(*ctx->pre_g, &gj, WINDOW_G); + secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g, &gj, cb); #ifdef USE_ENDOMORPHISM { - secp256k1_gej_t g_128j; + secp256k1_gej g_128j; int i; - ctx->pre_g_128 = (secp256k1_ge_storage_t (*)[])checked_malloc(sizeof((*ctx->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G)); + 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); + secp256k1_gej_double_var(&g_128j, &g_128j, NULL); } - secp256k1_ecmult_table_precomp_ge_storage_var(*ctx->pre_g_128, &g_128j, WINDOW_G); + secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g_128, &g_128j, cb); } #endif } -static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context_t *dst, - const secp256k1_ecmult_context_t *src) { +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; } else { size_t size = sizeof((*dst->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G); - dst->pre_g = (secp256k1_ge_storage_t (*)[])checked_malloc(size); + dst->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, size); memcpy(dst->pre_g, src->pre_g, size); } #ifdef USE_ENDOMORPHISM @@ -145,17 +207,17 @@ static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context_t *dst, 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_t (*)[])checked_malloc(size); + 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_t *ctx) { +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_t *ctx) { +static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx) { free(ctx->pre_g); #ifdef USE_ENDOMORPHISM free(ctx->pre_g_128); @@ -168,54 +230,68 @@ static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context_t *ctx) { * - each wnaf[i] is either 0, or an odd integer between -(1<<(w-1) - 1) and (1<<(w-1) - 1) * - two non-zero entries in wnaf are separated by at least w-1 zeroes. * - the number of set values in wnaf is returned. This number is at most 256, and at most one more - * - than the number of bits in the (absolute value) of the input. + * than the number of bits in the (absolute value) of the input. */ -static int secp256k1_ecmult_wnaf(int *wnaf, const secp256k1_scalar_t *a, int w) { - secp256k1_scalar_t s = *a; - int set_bits = 0; +static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a, int w) { + secp256k1_scalar s = *a; + int last_set_bit = -1; int bit = 0; int sign = 1; + int carry = 0; + + VERIFY_CHECK(wnaf != NULL); + VERIFY_CHECK(0 <= len && len <= 256); + VERIFY_CHECK(a != NULL); + VERIFY_CHECK(2 <= w && w <= 31); + + memset(wnaf, 0, len * sizeof(wnaf[0])); if (secp256k1_scalar_get_bits(&s, 255, 1)) { secp256k1_scalar_negate(&s, &s); sign = -1; } - while (bit < 256) { + while (bit < len) { int now; int word; - if (secp256k1_scalar_get_bits(&s, bit, 1) == 0) { + if (secp256k1_scalar_get_bits(&s, bit, 1) == (unsigned int)carry) { bit++; continue; } - while (set_bits < bit) { - wnaf[set_bits++] = 0; - } + now = w; - if (bit + now > 256) { - now = 256 - bit; - } - word = secp256k1_scalar_get_bits_var(&s, bit, now); - if (word & (1 << (w-1))) { - secp256k1_scalar_add_bit(&s, bit + w); - wnaf[set_bits++] = sign * (word - (1 << w)); - } else { - wnaf[set_bits++] = sign * word; + if (now > len - bit) { + now = len - bit; } + + word = secp256k1_scalar_get_bits_var(&s, bit, now) + carry; + + carry = (word >> (w-1)) & 1; + word -= carry << w; + + wnaf[bit] = sign * word; + last_set_bit = bit; + bit += now; } - return set_bits; +#ifdef VERIFY + CHECK(carry == 0); + while (bit < 256) { + CHECK(secp256k1_scalar_get_bits(&s, bit++, 1) == 0); + } +#endif + return last_set_bit + 1; } -static void secp256k1_ecmult(const secp256k1_ecmult_context_t *ctx, secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_scalar_t *na, const secp256k1_scalar_t *ng) { - secp256k1_gej_t tmpj; - secp256k1_gej_t pre_a[ECMULT_TABLE_SIZE(WINDOW_A)]; - secp256k1_ge_t tmpa; +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)]; + secp256k1_ge tmpa; + secp256k1_fe Z; #ifdef USE_ENDOMORPHISM - secp256k1_gej_t pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)]; - secp256k1_scalar_t na_1, na_lam; + secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_scalar na_1, na_lam; /* Splitted G factors. */ - secp256k1_scalar_t ng_1, ng_128; + secp256k1_scalar ng_1, ng_128; int wnaf_na_1[130]; int wnaf_na_lam[130]; int bits_na_1; @@ -227,7 +303,7 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context_t *ctx, secp256k1_ge #else int wnaf_na[256]; int bits_na; - int wnaf_ng[257]; + int wnaf_ng[256]; int bits_ng; #endif int i; @@ -235,11 +311,11 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context_t *ctx, secp256k1_ge #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_var(&na_1, &na_lam, na); + secp256k1_scalar_split_lambda(&na_1, &na_lam, na); /* build wnaf representation for na_1 and na_lam. */ - bits_na_1 = secp256k1_ecmult_wnaf(wnaf_na_1, &na_1, WINDOW_A); - bits_na_lam = secp256k1_ecmult_wnaf(wnaf_na_lam, &na_lam, WINDOW_A); + 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; @@ -248,24 +324,33 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context_t *ctx, secp256k1_ge } #else /* build wnaf representation for na. */ - bits_na = secp256k1_ecmult_wnaf(wnaf_na, na, WINDOW_A); + bits_na = secp256k1_ecmult_wnaf(wnaf_na, 256, na, WINDOW_A); bits = bits_na; #endif - /* calculate odd multiples of a */ - secp256k1_ecmult_table_precomp_gej_var(pre_a, a, WINDOW_A); + /* 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); #ifdef USE_ENDOMORPHISM for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { - secp256k1_gej_mul_lambda(&pre_a_lam[i], &pre_a[i]); + secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]); } /* 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, &ng_1, WINDOW_G); - bits_ng_128 = secp256k1_ecmult_wnaf(wnaf_ng_128, &ng_128, WINDOW_G); + 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; } @@ -273,7 +358,7 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context_t *ctx, secp256k1_ge bits = bits_ng_128; } #else - bits_ng = secp256k1_ecmult_wnaf(wnaf_ng, ng, WINDOW_G); + bits_ng = secp256k1_ecmult_wnaf(wnaf_ng, 256, ng, WINDOW_G); if (bits_ng > bits) { bits = bits_ng; } @@ -281,37 +366,41 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context_t *ctx, secp256k1_ge secp256k1_gej_set_infinity(r); - for (i = bits-1; i >= 0; i--) { + for (i = bits - 1; i >= 0; i--) { int n; - secp256k1_gej_double_var(r, r); + secp256k1_gej_double_var(r, r, NULL); #ifdef USE_ENDOMORPHISM if (i < bits_na_1 && (n = wnaf_na_1[i])) { - ECMULT_TABLE_GET_GEJ(&tmpj, pre_a, n, WINDOW_A); - secp256k1_gej_add_var(r, r, &tmpj); + 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_GEJ(&tmpj, pre_a_lam, n, WINDOW_A); - secp256k1_gej_add_var(r, r, &tmpj); + ECMULT_TABLE_GET_GE(&tmpa, pre_a_lam, 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_gej_add_ge_var(r, r, &tmpa); + 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_ge_var(r, r, &tmpa); + secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); } #else if (i < bits_na && (n = wnaf_na[i])) { - ECMULT_TABLE_GET_GEJ(&tmpj, pre_a, n, WINDOW_A); - secp256k1_gej_add_var(r, r, &tmpj); + 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_gej_add_ge_var(r, r, &tmpa); + secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); } #endif } + + if (!r->infinity) { + secp256k1_fe_mul(&r->z, &r->z, &Z); + } } -#endif +#endif /* SECP256K1_ECMULT_IMPL_H */ diff --git a/src/secp256k1/src/field.h b/src/secp256k1/src/field.h index 41b280892..bb6692ad5 100644 --- a/src/secp256k1/src/field.h +++ b/src/secp256k1/src/field.h @@ -4,13 +4,13 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_ -#define _SECP256K1_FIELD_ +#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 properaties into account: + * 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 @@ -30,90 +30,103 @@ #error "Please select field implementation" #endif +#include "util.h" + /** Normalize a field element. */ -static void secp256k1_fe_normalize(secp256k1_fe_t *r); +static void secp256k1_fe_normalize(secp256k1_fe *r); /** Weakly normalize a field element: reduce it magnitude to 1, but don't fully normalize. */ -static void secp256k1_fe_normalize_weak(secp256k1_fe_t *r); +static void secp256k1_fe_normalize_weak(secp256k1_fe *r); /** Normalize a field element, without constant-time guarantee. */ -static void secp256k1_fe_normalize_var(secp256k1_fe_t *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_t *r); +static int secp256k1_fe_normalizes_to_zero(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_t *r); +static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r); /** Set a field element equal to a small integer. Resulting field element is normalized. */ -static void secp256k1_fe_set_int(secp256k1_fe_t *r, int a); +static void secp256k1_fe_set_int(secp256k1_fe *r, int a); + +/** Sets a field element equal to zero, initializing all fields. */ +static void secp256k1_fe_clear(secp256k1_fe *a); /** Verify whether a field element is zero. Requires the input to be normalized. */ -static int secp256k1_fe_is_zero(const secp256k1_fe_t *a); +static int secp256k1_fe_is_zero(const secp256k1_fe *a); /** Check the "oddness" of a field element. Requires the input to be normalized. */ -static int secp256k1_fe_is_odd(const secp256k1_fe_t *a); +static int secp256k1_fe_is_odd(const secp256k1_fe *a); /** Compare two field elements. Requires magnitude-1 inputs. */ -static int secp256k1_fe_equal_var(const secp256k1_fe_t *a, const secp256k1_fe_t *b); +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 */ -static int secp256k1_fe_cmp_var(const secp256k1_fe_t *a, const secp256k1_fe_t *b); +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 succesful, the resulting field element is normalized. */ -static int secp256k1_fe_set_b32(secp256k1_fe_t *r, const unsigned char *a); +/** 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); /** 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_t *a); +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_t *r, const secp256k1_fe_t *a, int m); +static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k1_fe *a, int m); /** 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_t *r, int a); +static void secp256k1_fe_mul_int(secp256k1_fe *r, int a); /** Adds a field element to another. The result has the sum of the inputs' magnitudes as magnitude. */ -static void secp256k1_fe_add(secp256k1_fe_t *r, const secp256k1_fe_t *a); +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). */ -static void secp256k1_fe_mul(secp256k1_fe_t *r, const secp256k1_fe_t *a, const secp256k1_fe_t * SECP256K1_RESTRICT b); +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). */ -static void secp256k1_fe_sqr(secp256k1_fe_t *r, const secp256k1_fe_t *a); +static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a); -/** Sets a field element to be the (modular) square root (if any exist) of another. Requires the - * input's magnitude to be at most 8. The output magnitude is 1 (but not guaranteed to be - * normalized). Return value indicates whether a square root was found. */ -static int secp256k1_fe_sqrt_var(secp256k1_fe_t *r, const secp256k1_fe_t *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); /** 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). */ -static void secp256k1_fe_inv(secp256k1_fe_t *r, const secp256k1_fe_t *a); +static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a); /** Potentially faster version of secp256k1_fe_inv, without constant-time guarantee. */ -static void secp256k1_fe_inv_var(secp256k1_fe_t *r, const secp256k1_fe_t *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(size_t len, secp256k1_fe_t *r, const secp256k1_fe_t *a); +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. */ -static void secp256k1_fe_to_storage(secp256k1_fe_storage_t *r, const secp256k1_fe_t*); +static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a); /** Convert a field element back from the storage type. */ -static void secp256k1_fe_from_storage(secp256k1_fe_t *r, const secp256k1_fe_storage_t*); +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. */ -static void secp256k1_fe_storage_cmov(secp256k1_fe_storage_t *r, const secp256k1_fe_storage_t *a, int flag); +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. */ -static void secp256k1_fe_cmov(secp256k1_fe_t *r, const secp256k1_fe_t *a, int flag); +static void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag); -#endif +#endif /* SECP256K1_FIELD_H */ diff --git a/src/secp256k1/src/field_10x26.h b/src/secp256k1/src/field_10x26.h index 44bce6525..727c5267f 100644 --- a/src/secp256k1/src/field_10x26.h +++ b/src/secp256k1/src/field_10x26.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_REPR_ -#define _SECP256K1_FIELD_REPR_ +#ifndef SECP256K1_FIELD_REPR_H +#define SECP256K1_FIELD_REPR_H #include @@ -16,20 +16,20 @@ typedef struct { int magnitude; int normalized; #endif -} secp256k1_fe_t; +} secp256k1_fe; /* Unpacks a constant into a overlapping multi-limbed FE element. */ #define SECP256K1_FE_CONST_INNER(d7, d6, d5, d4, d3, d2, d1, d0) { \ (d0) & 0x3FFFFFFUL, \ - ((d0) >> 26) | ((d1) & 0xFFFFFUL) << 6, \ - ((d1) >> 20) | ((d2) & 0x3FFFUL) << 12, \ - ((d2) >> 14) | ((d3) & 0xFFUL) << 18, \ - ((d3) >> 8) | ((d4) & 0x3) << 24, \ - ((d4) >> 2) & 0x3FFFFFFUL, \ - ((d4) >> 28) | ((d5) & 0x3FFFFFUL) << 4, \ - ((d5) >> 22) | ((d6) & 0xFFFF) << 10, \ - ((d6) >> 16) | ((d7) & 0x3FF) << 16, \ - ((d7) >> 10) \ + (((uint32_t)d0) >> 26) | (((uint32_t)(d1) & 0xFFFFFUL) << 6), \ + (((uint32_t)d1) >> 20) | (((uint32_t)(d2) & 0x3FFFUL) << 12), \ + (((uint32_t)d2) >> 14) | (((uint32_t)(d3) & 0xFFUL) << 18), \ + (((uint32_t)d3) >> 8) | (((uint32_t)(d4) & 0x3UL) << 24), \ + (((uint32_t)d4) >> 2) & 0x3FFFFFFUL, \ + (((uint32_t)d4) >> 28) | (((uint32_t)(d5) & 0x3FFFFFUL) << 4), \ + (((uint32_t)d5) >> 22) | (((uint32_t)(d6) & 0xFFFFUL) << 10), \ + (((uint32_t)d6) >> 16) | (((uint32_t)(d7) & 0x3FFUL) << 16), \ + (((uint32_t)d7) >> 10) \ } #ifdef VERIFY @@ -40,8 +40,9 @@ typedef struct { typedef struct { uint32_t n[8]; -} secp256k1_fe_storage_t; +} secp256k1_fe_storage; #define SECP256K1_FE_STORAGE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{ (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }} +#define SECP256K1_FE_STORAGE_CONST_GET(d) d.n[7], d.n[6], d.n[5], d.n[4],d.n[3], d.n[2], d.n[1], d.n[0] -#endif +#endif /* SECP256K1_FIELD_REPR_H */ diff --git a/src/secp256k1/src/field_10x26_impl.h b/src/secp256k1/src/field_10x26_impl.h index 871b91f91..94f8132fc 100644 --- a/src/secp256k1/src/field_10x26_impl.h +++ b/src/secp256k1/src/field_10x26_impl.h @@ -4,17 +4,15 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_REPR_IMPL_H_ -#define _SECP256K1_FIELD_REPR_IMPL_H_ +#ifndef SECP256K1_FIELD_REPR_IMPL_H +#define SECP256K1_FIELD_REPR_IMPL_H -#include -#include #include "util.h" #include "num.h" #include "field.h" #ifdef VERIFY -static void secp256k1_fe_verify(const secp256k1_fe_t *a) { +static void secp256k1_fe_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); @@ -40,13 +38,9 @@ static void secp256k1_fe_verify(const secp256k1_fe_t *a) { } VERIFY_CHECK(r == 1); } -#else -static void secp256k1_fe_verify(const secp256k1_fe_t *a) { - (void)a; -} #endif -static void secp256k1_fe_normalize(secp256k1_fe_t *r) { +static void secp256k1_fe_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]; @@ -101,7 +95,7 @@ static void secp256k1_fe_normalize(secp256k1_fe_t *r) { #endif } -static void secp256k1_fe_normalize_weak(secp256k1_fe_t *r) { +static void secp256k1_fe_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]; @@ -132,7 +126,7 @@ static void secp256k1_fe_normalize_weak(secp256k1_fe_t *r) { #endif } -static void secp256k1_fe_normalize_var(secp256k1_fe_t *r) { +static void secp256k1_fe_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]; @@ -188,7 +182,7 @@ static void secp256k1_fe_normalize_var(secp256k1_fe_t *r) { #endif } -static int secp256k1_fe_normalizes_to_zero(secp256k1_fe_t *r) { +static int secp256k1_fe_normalizes_to_zero(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]; @@ -217,7 +211,7 @@ static int secp256k1_fe_normalizes_to_zero(secp256k1_fe_t *r) { return (z0 == 0) | (z1 == 0x3FFFFFFUL); } -static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe_t *r) { +static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r) { uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; uint32_t z0, z1; uint32_t x; @@ -252,7 +246,7 @@ static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe_t *r) { t9 &= 0x03FFFFFUL; t1 += (x << 6); - t1 += (t0 >> 26); t0 = z0; + t1 += (t0 >> 26); t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; z0 |= t1; z1 &= t1 ^ 0x40UL; t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; z0 |= t2; z1 &= t2; t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; z0 |= t3; z1 &= t3; @@ -269,7 +263,7 @@ static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe_t *r) { return (z0 == 0) | (z1 == 0x3FFFFFFUL); } -SECP256K1_INLINE static void secp256k1_fe_set_int(secp256k1_fe_t *r, int a) { +SECP256K1_INLINE static void secp256k1_fe_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 @@ -279,7 +273,7 @@ SECP256K1_INLINE static void secp256k1_fe_set_int(secp256k1_fe_t *r, int a) { #endif } -SECP256K1_INLINE static int secp256k1_fe_is_zero(const secp256k1_fe_t *a) { +SECP256K1_INLINE static int secp256k1_fe_is_zero(const secp256k1_fe *a) { const uint32_t *t = a->n; #ifdef VERIFY VERIFY_CHECK(a->normalized); @@ -288,7 +282,7 @@ SECP256K1_INLINE static int secp256k1_fe_is_zero(const secp256k1_fe_t *a) { 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_t *a) { +SECP256K1_INLINE static int secp256k1_fe_is_odd(const secp256k1_fe *a) { #ifdef VERIFY VERIFY_CHECK(a->normalized); secp256k1_fe_verify(a); @@ -296,7 +290,7 @@ SECP256K1_INLINE static int secp256k1_fe_is_odd(const secp256k1_fe_t *a) { return a->n[0] & 1; } -SECP256K1_INLINE static void secp256k1_fe_clear(secp256k1_fe_t *a) { +SECP256K1_INLINE static void secp256k1_fe_clear(secp256k1_fe *a) { int i; #ifdef VERIFY a->magnitude = 0; @@ -307,7 +301,7 @@ SECP256K1_INLINE static void secp256k1_fe_clear(secp256k1_fe_t *a) { } } -static int secp256k1_fe_cmp_var(const secp256k1_fe_t *a, const secp256k1_fe_t *b) { +static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { int i; #ifdef VERIFY VERIFY_CHECK(a->normalized); @@ -326,18 +320,18 @@ static int secp256k1_fe_cmp_var(const secp256k1_fe_t *a, const secp256k1_fe_t *b return 0; } -static int secp256k1_fe_set_b32(secp256k1_fe_t *r, const unsigned char *a) { - int i; - r->n[0] = r->n[1] = r->n[2] = r->n[3] = r->n[4] = 0; - r->n[5] = r->n[6] = r->n[7] = r->n[8] = r->n[9] = 0; - for (i=0; i<32; i++) { - int j; - for (j=0; j<4; j++) { - int limb = (8*i+2*j)/26; - int shift = (8*i+2*j)%26; - r->n[limb] |= (uint32_t)((a[31-i] >> (2*j)) & 0x3) << shift; - } - } +static int secp256k1_fe_set_b32(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); + r->n[3] = (uint32_t)((a[22] >> 6) & 0x3) | ((uint32_t)a[21] << 2) | ((uint32_t)a[20] << 10) | ((uint32_t)a[19] << 18); + r->n[4] = (uint32_t)a[18] | ((uint32_t)a[17] << 8) | ((uint32_t)a[16] << 16) | ((uint32_t)(a[15] & 0x3) << 24); + r->n[5] = (uint32_t)((a[15] >> 2) & 0x3f) | ((uint32_t)a[14] << 6) | ((uint32_t)a[13] << 14) | ((uint32_t)(a[12] & 0xf) << 22); + r->n[6] = (uint32_t)((a[12] >> 4) & 0xf) | ((uint32_t)a[11] << 4) | ((uint32_t)a[10] << 12) | ((uint32_t)(a[9] & 0x3f) << 20); + 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; } @@ -350,25 +344,46 @@ static int secp256k1_fe_set_b32(secp256k1_fe_t *r, const unsigned char *a) { } /** 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_t *a) { - int i; +static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a) { #ifdef VERIFY VERIFY_CHECK(a->normalized); secp256k1_fe_verify(a); #endif - for (i=0; i<32; i++) { - int j; - int c = 0; - for (j=0; j<4; j++) { - int limb = (8*i+2*j)/26; - int shift = (8*i+2*j)%26; - c |= ((a->n[limb] >> shift) & 0x3) << (2 * j); - } - r[31-i] = c; - } + 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); + r[3] = (a->n[8] >> 16) & 0xff; + r[4] = (a->n[8] >> 8) & 0xff; + r[5] = a->n[8] & 0xff; + r[6] = (a->n[7] >> 18) & 0xff; + r[7] = (a->n[7] >> 10) & 0xff; + r[8] = (a->n[7] >> 2) & 0xff; + r[9] = ((a->n[7] & 0x3) << 6) | ((a->n[6] >> 20) & 0x3f); + r[10] = (a->n[6] >> 12) & 0xff; + r[11] = (a->n[6] >> 4) & 0xff; + r[12] = ((a->n[6] & 0xf) << 4) | ((a->n[5] >> 22) & 0xf); + r[13] = (a->n[5] >> 14) & 0xff; + r[14] = (a->n[5] >> 6) & 0xff; + r[15] = ((a->n[5] & 0x3f) << 2) | ((a->n[4] >> 24) & 0x3); + r[16] = (a->n[4] >> 16) & 0xff; + r[17] = (a->n[4] >> 8) & 0xff; + r[18] = a->n[4] & 0xff; + r[19] = (a->n[3] >> 18) & 0xff; + r[20] = (a->n[3] >> 10) & 0xff; + r[21] = (a->n[3] >> 2) & 0xff; + r[22] = ((a->n[3] & 0x3) << 6) | ((a->n[2] >> 20) & 0x3f); + r[23] = (a->n[2] >> 12) & 0xff; + r[24] = (a->n[2] >> 4) & 0xff; + r[25] = ((a->n[2] & 0xf) << 4) | ((a->n[1] >> 22) & 0xf); + r[26] = (a->n[1] >> 14) & 0xff; + r[27] = (a->n[1] >> 6) & 0xff; + r[28] = ((a->n[1] & 0x3f) << 2) | ((a->n[0] >> 24) & 0x3); + r[29] = (a->n[0] >> 16) & 0xff; + r[30] = (a->n[0] >> 8) & 0xff; + r[31] = a->n[0] & 0xff; } -SECP256K1_INLINE static void secp256k1_fe_negate(secp256k1_fe_t *r, const secp256k1_fe_t *a, int m) { +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); @@ -390,7 +405,7 @@ SECP256K1_INLINE static void secp256k1_fe_negate(secp256k1_fe_t *r, const secp25 #endif } -SECP256K1_INLINE static void secp256k1_fe_mul_int(secp256k1_fe_t *r, int a) { +SECP256K1_INLINE static void secp256k1_fe_mul_int(secp256k1_fe *r, int a) { r->n[0] *= a; r->n[1] *= a; r->n[2] *= a; @@ -408,7 +423,7 @@ SECP256K1_INLINE static void secp256k1_fe_mul_int(secp256k1_fe_t *r, int a) { #endif } -SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe_t *r, const secp256k1_fe_t *a) { +SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a) { #ifdef VERIFY secp256k1_fe_verify(a); #endif @@ -429,6 +444,14 @@ SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe_t *r, const secp256k1 #endif } +#if defined(USE_EXTERNAL_ASM) + +/* External assembler implementation */ +void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t *a, const uint32_t * SECP256K1_RESTRICT b); +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 @@ -1037,9 +1060,9 @@ SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t VERIFY_BITS(r[2], 27); /* [r9 r8 r7 r6 r5 r4 r3 r2 r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ } +#endif - -static void secp256k1_fe_mul(secp256k1_fe_t *r, const secp256k1_fe_t *a, const secp256k1_fe_t * SECP256K1_RESTRICT b) { +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); @@ -1055,7 +1078,7 @@ static void secp256k1_fe_mul(secp256k1_fe_t *r, const secp256k1_fe_t *a, const s #endif } -static void secp256k1_fe_sqr(secp256k1_fe_t *r, const secp256k1_fe_t *a) { +static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) { #ifdef VERIFY VERIFY_CHECK(a->magnitude <= 8); secp256k1_fe_verify(a); @@ -1068,7 +1091,7 @@ static void secp256k1_fe_sqr(secp256k1_fe_t *r, const secp256k1_fe_t *a) { #endif } -static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe_t *r, const secp256k1_fe_t *a, int flag) { +static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { uint32_t mask0, mask1; mask0 = flag + ~((uint32_t)0); mask1 = ~mask0; @@ -1083,12 +1106,14 @@ static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe_t *r, const secp256k r->n[8] = (r->n[8] & mask0) | (a->n[8] & mask1); r->n[9] = (r->n[9] & mask0) | (a->n[9] & mask1); #ifdef VERIFY - r->magnitude = (r->magnitude & mask0) | (a->magnitude & mask1); - r->normalized = (r->normalized & mask0) | (a->normalized & mask1); + if (a->magnitude > r->magnitude) { + r->magnitude = a->magnitude; + } + r->normalized &= a->normalized; #endif } -static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage_t *r, const secp256k1_fe_storage_t *a, int flag) { +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); mask1 = ~mask0; @@ -1102,7 +1127,7 @@ static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage_t *r r->n[7] = (r->n[7] & mask0) | (a->n[7] & mask1); } -static void secp256k1_fe_to_storage(secp256k1_fe_storage_t *r, const secp256k1_fe_t *a) { +static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) { #ifdef VERIFY VERIFY_CHECK(a->normalized); #endif @@ -1116,7 +1141,7 @@ static void secp256k1_fe_to_storage(secp256k1_fe_storage_t *r, const secp256k1_f r->n[7] = a->n[8] >> 16 | a->n[9] << 10; } -static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe_t *r, const secp256k1_fe_storage_t *a) { +static SECP256K1_INLINE void secp256k1_fe_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); @@ -1133,4 +1158,4 @@ static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe_t *r, const #endif } -#endif +#endif /* SECP256K1_FIELD_REPR_IMPL_H */ diff --git a/src/secp256k1/src/field_5x52.h b/src/secp256k1/src/field_5x52.h index 4513d36f4..bccd8feb4 100644 --- a/src/secp256k1/src/field_5x52.h +++ b/src/secp256k1/src/field_5x52.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_REPR_ -#define _SECP256K1_FIELD_REPR_ +#ifndef SECP256K1_FIELD_REPR_H +#define SECP256K1_FIELD_REPR_H #include @@ -16,15 +16,15 @@ typedef struct { int magnitude; int normalized; #endif -} secp256k1_fe_t; +} secp256k1_fe; /* Unpacks a constant into a overlapping multi-limbed FE element. */ #define SECP256K1_FE_CONST_INNER(d7, d6, d5, d4, d3, d2, d1, d0) { \ - (d0) | ((uint64_t)(d1) & 0xFFFFFUL) << 32, \ - ((d1) >> 20) | ((uint64_t)(d2)) << 12 | ((uint64_t)(d3) & 0xFFUL) << 44, \ - ((d3) >> 8) | ((uint64_t)(d4) & 0xFFFFFFFUL) << 24, \ - ((d4) >> 28) | ((uint64_t)(d5)) << 4 | ((uint64_t)(d6) & 0xFFFFUL) << 36, \ - ((d6) >> 16) | ((uint64_t)(d7)) << 16 \ + (d0) | (((uint64_t)(d1) & 0xFFFFFUL) << 32), \ + ((uint64_t)(d1) >> 20) | (((uint64_t)(d2)) << 12) | (((uint64_t)(d3) & 0xFFUL) << 44), \ + ((uint64_t)(d3) >> 8) | (((uint64_t)(d4) & 0xFFFFFFFUL) << 24), \ + ((uint64_t)(d4) >> 28) | (((uint64_t)(d5)) << 4) | (((uint64_t)(d6) & 0xFFFFUL) << 36), \ + ((uint64_t)(d6) >> 16) | (((uint64_t)(d7)) << 16) \ } #ifdef VERIFY @@ -35,13 +35,13 @@ typedef struct { typedef struct { uint64_t n[4]; -} secp256k1_fe_storage_t; +} secp256k1_fe_storage; #define SECP256K1_FE_STORAGE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{ \ - (d0) | ((uint64_t)(d1)) << 32, \ - (d2) | ((uint64_t)(d3)) << 32, \ - (d4) | ((uint64_t)(d5)) << 32, \ - (d6) | ((uint64_t)(d7)) << 32 \ + (d0) | (((uint64_t)(d1)) << 32), \ + (d2) | (((uint64_t)(d3)) << 32), \ + (d4) | (((uint64_t)(d5)) << 32), \ + (d6) | (((uint64_t)(d7)) << 32) \ }} -#endif +#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 index 98cc004bf..1fc3171f6 100644 --- a/src/secp256k1/src/field_5x52_asm_impl.h +++ b/src/secp256k1/src/field_5x52_asm_impl.h @@ -11,8 +11,8 @@ * - December 2014, Pieter Wuille: converted from YASM to GCC inline assembly */ -#ifndef _SECP256K1_FIELD_INNER5X52_IMPL_H_ -#define _SECP256K1_FIELD_INNER5X52_IMPL_H_ +#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) { /** @@ -499,4 +499,4 @@ __asm__ __volatile__( ); } -#endif +#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 bda4c3dfc..957c61b01 100644 --- a/src/secp256k1/src/field_5x52_impl.h +++ b/src/secp256k1/src/field_5x52_impl.h @@ -4,14 +4,13 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_REPR_IMPL_H_ -#define _SECP256K1_FIELD_REPR_IMPL_H_ +#ifndef SECP256K1_FIELD_REPR_IMPL_H +#define SECP256K1_FIELD_REPR_IMPL_H #if defined HAVE_CONFIG_H #include "libsecp256k1-config.h" #endif -#include #include "util.h" #include "num.h" #include "field.h" @@ -31,7 +30,7 @@ */ #ifdef VERIFY -static void secp256k1_fe_verify(const secp256k1_fe_t *a) { +static void secp256k1_fe_verify(const secp256k1_fe *a) { const uint64_t *d = a->n; int m = a->normalized ? 1 : 2 * a->magnitude, r = 1; /* secp256k1 'p' value defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */ @@ -50,13 +49,9 @@ static void secp256k1_fe_verify(const secp256k1_fe_t *a) { } VERIFY_CHECK(r == 1); } -#else -static void secp256k1_fe_verify(const secp256k1_fe_t *a) { - (void)a; -} #endif -static void secp256k1_fe_normalize(secp256k1_fe_t *r) { +static void secp256k1_fe_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 */ @@ -99,7 +94,7 @@ static void secp256k1_fe_normalize(secp256k1_fe_t *r) { #endif } -static void secp256k1_fe_normalize_weak(secp256k1_fe_t *r) { +static void secp256k1_fe_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 */ @@ -123,7 +118,7 @@ static void secp256k1_fe_normalize_weak(secp256k1_fe_t *r) { #endif } -static void secp256k1_fe_normalize_var(secp256k1_fe_t *r) { +static void secp256k1_fe_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 */ @@ -167,7 +162,7 @@ static void secp256k1_fe_normalize_var(secp256k1_fe_t *r) { #endif } -static int secp256k1_fe_normalizes_to_zero(secp256k1_fe_t *r) { +static int secp256k1_fe_normalizes_to_zero(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 */ @@ -190,7 +185,7 @@ static int secp256k1_fe_normalizes_to_zero(secp256k1_fe_t *r) { return (z0 == 0) | (z1 == 0xFFFFFFFFFFFFFULL); } -static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe_t *r) { +static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r) { uint64_t t0, t1, t2, t3, t4; uint64_t z0, z1; uint64_t x; @@ -219,7 +214,7 @@ static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe_t *r) { t4 &= 0x0FFFFFFFFFFFFULL; - t1 += (t0 >> 52); t0 = z0; + t1 += (t0 >> 52); t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; z0 |= t1; z1 &= t1; t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; z0 |= t2; z1 &= t2; t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; z0 |= t3; z1 &= t3; @@ -231,7 +226,7 @@ static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe_t *r) { return (z0 == 0) | (z1 == 0xFFFFFFFFFFFFFULL); } -SECP256K1_INLINE static void secp256k1_fe_set_int(secp256k1_fe_t *r, int a) { +SECP256K1_INLINE static void secp256k1_fe_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 @@ -241,7 +236,7 @@ SECP256K1_INLINE static void secp256k1_fe_set_int(secp256k1_fe_t *r, int a) { #endif } -SECP256K1_INLINE static int secp256k1_fe_is_zero(const secp256k1_fe_t *a) { +SECP256K1_INLINE static int secp256k1_fe_is_zero(const secp256k1_fe *a) { const uint64_t *t = a->n; #ifdef VERIFY VERIFY_CHECK(a->normalized); @@ -250,7 +245,7 @@ SECP256K1_INLINE static int secp256k1_fe_is_zero(const secp256k1_fe_t *a) { return (t[0] | t[1] | t[2] | t[3] | t[4]) == 0; } -SECP256K1_INLINE static int secp256k1_fe_is_odd(const secp256k1_fe_t *a) { +SECP256K1_INLINE static int secp256k1_fe_is_odd(const secp256k1_fe *a) { #ifdef VERIFY VERIFY_CHECK(a->normalized); secp256k1_fe_verify(a); @@ -258,7 +253,7 @@ SECP256K1_INLINE static int secp256k1_fe_is_odd(const secp256k1_fe_t *a) { return a->n[0] & 1; } -SECP256K1_INLINE static void secp256k1_fe_clear(secp256k1_fe_t *a) { +SECP256K1_INLINE static void secp256k1_fe_clear(secp256k1_fe *a) { int i; #ifdef VERIFY a->magnitude = 0; @@ -269,7 +264,7 @@ SECP256K1_INLINE static void secp256k1_fe_clear(secp256k1_fe_t *a) { } } -static int secp256k1_fe_cmp_var(const secp256k1_fe_t *a, const secp256k1_fe_t *b) { +static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { int i; #ifdef VERIFY VERIFY_CHECK(a->normalized); @@ -288,17 +283,41 @@ static int secp256k1_fe_cmp_var(const secp256k1_fe_t *a, const secp256k1_fe_t *b return 0; } -static int secp256k1_fe_set_b32(secp256k1_fe_t *r, const unsigned char *a) { - int i; - r->n[0] = r->n[1] = r->n[2] = r->n[3] = r->n[4] = 0; - for (i=0; i<32; i++) { - int j; - for (j=0; j<2; j++) { - int limb = (8*i+4*j)/52; - int shift = (8*i+4*j)%52; - r->n[limb] |= (uint64_t)((a[31-i] >> (4*j)) & 0xF) << shift; - } - } +static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) { + r->n[0] = (uint64_t)a[31] + | ((uint64_t)a[30] << 8) + | ((uint64_t)a[29] << 16) + | ((uint64_t)a[28] << 24) + | ((uint64_t)a[27] << 32) + | ((uint64_t)a[26] << 40) + | ((uint64_t)(a[25] & 0xF) << 48); + r->n[1] = (uint64_t)((a[25] >> 4) & 0xF) + | ((uint64_t)a[24] << 4) + | ((uint64_t)a[23] << 12) + | ((uint64_t)a[22] << 20) + | ((uint64_t)a[21] << 28) + | ((uint64_t)a[20] << 36) + | ((uint64_t)a[19] << 44); + r->n[2] = (uint64_t)a[18] + | ((uint64_t)a[17] << 8) + | ((uint64_t)a[16] << 16) + | ((uint64_t)a[15] << 24) + | ((uint64_t)a[14] << 32) + | ((uint64_t)a[13] << 40) + | ((uint64_t)(a[12] & 0xF) << 48); + r->n[3] = (uint64_t)((a[12] >> 4) & 0xF) + | ((uint64_t)a[11] << 4) + | ((uint64_t)a[10] << 12) + | ((uint64_t)a[9] << 20) + | ((uint64_t)a[8] << 28) + | ((uint64_t)a[7] << 36) + | ((uint64_t)a[6] << 44); + r->n[4] = (uint64_t)a[5] + | ((uint64_t)a[4] << 8) + | ((uint64_t)a[3] << 16) + | ((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; } @@ -311,25 +330,46 @@ static int secp256k1_fe_set_b32(secp256k1_fe_t *r, const unsigned char *a) { } /** 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_t *a) { - int i; +static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a) { #ifdef VERIFY VERIFY_CHECK(a->normalized); secp256k1_fe_verify(a); #endif - for (i=0; i<32; i++) { - int j; - int c = 0; - for (j=0; j<2; j++) { - int limb = (8*i+4*j)/52; - int shift = (8*i+4*j)%52; - c |= ((a->n[limb] >> shift) & 0xF) << (4 * j); - } - r[31-i] = c; - } + r[0] = (a->n[4] >> 40) & 0xFF; + r[1] = (a->n[4] >> 32) & 0xFF; + r[2] = (a->n[4] >> 24) & 0xFF; + r[3] = (a->n[4] >> 16) & 0xFF; + r[4] = (a->n[4] >> 8) & 0xFF; + r[5] = a->n[4] & 0xFF; + r[6] = (a->n[3] >> 44) & 0xFF; + r[7] = (a->n[3] >> 36) & 0xFF; + r[8] = (a->n[3] >> 28) & 0xFF; + r[9] = (a->n[3] >> 20) & 0xFF; + r[10] = (a->n[3] >> 12) & 0xFF; + r[11] = (a->n[3] >> 4) & 0xFF; + r[12] = ((a->n[2] >> 48) & 0xF) | ((a->n[3] & 0xF) << 4); + r[13] = (a->n[2] >> 40) & 0xFF; + r[14] = (a->n[2] >> 32) & 0xFF; + r[15] = (a->n[2] >> 24) & 0xFF; + r[16] = (a->n[2] >> 16) & 0xFF; + r[17] = (a->n[2] >> 8) & 0xFF; + r[18] = a->n[2] & 0xFF; + r[19] = (a->n[1] >> 44) & 0xFF; + r[20] = (a->n[1] >> 36) & 0xFF; + r[21] = (a->n[1] >> 28) & 0xFF; + r[22] = (a->n[1] >> 20) & 0xFF; + r[23] = (a->n[1] >> 12) & 0xFF; + r[24] = (a->n[1] >> 4) & 0xFF; + r[25] = ((a->n[0] >> 48) & 0xF) | ((a->n[1] & 0xF) << 4); + r[26] = (a->n[0] >> 40) & 0xFF; + r[27] = (a->n[0] >> 32) & 0xFF; + r[28] = (a->n[0] >> 24) & 0xFF; + r[29] = (a->n[0] >> 16) & 0xFF; + r[30] = (a->n[0] >> 8) & 0xFF; + r[31] = a->n[0] & 0xFF; } -SECP256K1_INLINE static void secp256k1_fe_negate(secp256k1_fe_t *r, const secp256k1_fe_t *a, int m) { +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); @@ -346,7 +386,7 @@ SECP256K1_INLINE static void secp256k1_fe_negate(secp256k1_fe_t *r, const secp25 #endif } -SECP256K1_INLINE static void secp256k1_fe_mul_int(secp256k1_fe_t *r, int a) { +SECP256K1_INLINE static void secp256k1_fe_mul_int(secp256k1_fe *r, int a) { r->n[0] *= a; r->n[1] *= a; r->n[2] *= a; @@ -359,7 +399,7 @@ SECP256K1_INLINE static void secp256k1_fe_mul_int(secp256k1_fe_t *r, int a) { #endif } -SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe_t *r, const secp256k1_fe_t *a) { +SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a) { #ifdef VERIFY secp256k1_fe_verify(a); #endif @@ -375,7 +415,7 @@ SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe_t *r, const secp256k1 #endif } -static void secp256k1_fe_mul(secp256k1_fe_t *r, const secp256k1_fe_t *a, const secp256k1_fe_t * SECP256K1_RESTRICT b) { +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); @@ -391,7 +431,7 @@ static void secp256k1_fe_mul(secp256k1_fe_t *r, const secp256k1_fe_t *a, const s #endif } -static void secp256k1_fe_sqr(secp256k1_fe_t *r, const secp256k1_fe_t *a) { +static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) { #ifdef VERIFY VERIFY_CHECK(a->magnitude <= 8); secp256k1_fe_verify(a); @@ -404,7 +444,7 @@ static void secp256k1_fe_sqr(secp256k1_fe_t *r, const secp256k1_fe_t *a) { #endif } -static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe_t *r, const secp256k1_fe_t *a, int flag) { +static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { uint64_t mask0, mask1; mask0 = flag + ~((uint64_t)0); mask1 = ~mask0; @@ -414,12 +454,14 @@ static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe_t *r, const secp256k r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1); r->n[4] = (r->n[4] & mask0) | (a->n[4] & mask1); #ifdef VERIFY - r->magnitude = (r->magnitude & mask0) | (a->magnitude & mask1); - r->normalized = (r->normalized & mask0) | (a->normalized & mask1); + if (a->magnitude > r->magnitude) { + r->magnitude = a->magnitude; + } + r->normalized &= a->normalized; #endif } -static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage_t *r, const secp256k1_fe_storage_t *a, int flag) { +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); mask1 = ~mask0; @@ -429,7 +471,7 @@ static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage_t *r r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1); } -static void secp256k1_fe_to_storage(secp256k1_fe_storage_t *r, const secp256k1_fe_t *a) { +static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) { #ifdef VERIFY VERIFY_CHECK(a->normalized); #endif @@ -439,7 +481,7 @@ static void secp256k1_fe_to_storage(secp256k1_fe_storage_t *r, const secp256k1_f r->n[3] = a->n[3] >> 36 | a->n[4] << 16; } -static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe_t *r, const secp256k1_fe_storage_t *a) { +static SECP256K1_INLINE void secp256k1_fe_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); @@ -451,4 +493,4 @@ static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe_t *r, const #endif } -#endif +#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 9280bb5ea..95a0d1791 100644 --- a/src/secp256k1/src/field_5x52_int128_impl.h +++ b/src/secp256k1/src/field_5x52_int128_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_INNER5X52_IMPL_H_ -#define _SECP256K1_FIELD_INNER5X52_IMPL_H_ +#ifndef SECP256K1_FIELD_INNER5X52_IMPL_H +#define SECP256K1_FIELD_INNER5X52_IMPL_H #include @@ -137,7 +137,7 @@ SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t 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;; + c += d * R + t3; VERIFY_BITS(c, 100); /* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ r[3] = c & M; c >>= 52; @@ -259,7 +259,7 @@ SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint64_t *r, const uint64_t 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;; + c += d * R + t3; VERIFY_BITS(c, 100); /* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ r[3] = c & M; c >>= 52; @@ -274,4 +274,4 @@ SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint64_t *r, const uint64_t /* [r4 r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ } -#endif +#endif /* SECP256K1_FIELD_INNER5X52_IMPL_H */ diff --git a/src/secp256k1/src/field_impl.h b/src/secp256k1/src/field_impl.h index e6ec11e8f..20428648a 100644 --- a/src/secp256k1/src/field_impl.h +++ b/src/secp256k1/src/field_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_IMPL_H_ -#define _SECP256K1_FIELD_IMPL_H_ +#ifndef SECP256K1_FIELD_IMPL_H +#define SECP256K1_FIELD_IMPL_H #if defined HAVE_CONFIG_H #include "libsecp256k1-config.h" @@ -21,15 +21,31 @@ #error "Please select field implementation" #endif -SECP256K1_INLINE static int secp256k1_fe_equal_var(const secp256k1_fe_t *a, const secp256k1_fe_t *b) { - secp256k1_fe_t na; +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_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); } -static int secp256k1_fe_sqrt_var(secp256k1_fe_t *r, const secp256k1_fe_t *a) { - secp256k1_fe_t x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1; +static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *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. + * + * As (p+1)/4 is an even number, it will have the same result for a and for + * (-a). Only one of these two numbers actually has a square root however, + * so we test at the end by squaring and comparing to the input. + * Also because (p+1)/4 is an even number, the computed square root is + * 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; /** The binary representation of (p + 1)/4 has 3 blocks of 1s, with lengths in @@ -114,11 +130,11 @@ static int secp256k1_fe_sqrt_var(secp256k1_fe_t *r, const secp256k1_fe_t *a) { /* Check that a square root was actually calculated */ secp256k1_fe_sqr(&t1, r); - return secp256k1_fe_equal_var(&t1, a); + return secp256k1_fe_equal(&t1, a); } -static void secp256k1_fe_inv(secp256k1_fe_t *r, const secp256k1_fe_t *a) { - secp256k1_fe_t x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1; +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; /** The binary representation of (p - 2) has 5 blocks of 1s, with lengths in @@ -207,11 +223,15 @@ static void secp256k1_fe_inv(secp256k1_fe_t *r, const secp256k1_fe_t *a) { secp256k1_fe_mul(r, a, &t1); } -static void secp256k1_fe_inv_var(secp256k1_fe_t *r, const secp256k1_fe_t *a) { +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_t n, m; + 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, @@ -220,21 +240,28 @@ static void secp256k1_fe_inv_var(secp256k1_fe_t *r, const secp256k1_fe_t *a) { 0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F }; unsigned char b[32]; - secp256k1_fe_t c = *a; + 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); - VERIFY_CHECK(secp256k1_fe_set_b32(r, b)); + 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_inv_all_var(size_t len, secp256k1_fe_t *r, const secp256k1_fe_t *a) { - secp256k1_fe_t u; +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; @@ -252,7 +279,7 @@ static void secp256k1_fe_inv_all_var(size_t len, secp256k1_fe_t *r, const secp25 secp256k1_fe_inv_var(&u, &r[--i]); while (i > 0) { - int j = i--; + size_t j = i--; secp256k1_fe_mul(&r[j], &r[i], &u); secp256k1_fe_mul(&u, &u, &a[j]); } @@ -260,4 +287,29 @@ static void secp256k1_fe_inv_all_var(size_t len, secp256k1_fe_t *r, const secp25 r[0] = u; } +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 +} + +#endif /* SECP256K1_FIELD_IMPL_H */ diff --git a/src/secp256k1/src/gen_context.c b/src/secp256k1/src/gen_context.c new file mode 100644 index 000000000..1835fd491 --- /dev/null +++ b/src/secp256k1/src/gen_context.c @@ -0,0 +1,74 @@ +/********************************************************************** + * Copyright (c) 2013, 2014, 2015 Thomas Daede, Cory Fields * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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 0b08b3b99..ea1302deb 100644 --- a/src/secp256k1/src/group.h +++ b/src/secp256k1/src/group.h @@ -4,118 +4,141 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_GROUP_ -#define _SECP256K1_GROUP_ +#ifndef SECP256K1_GROUP_H +#define SECP256K1_GROUP_H #include "num.h" #include "field.h" /** A group element of the secp256k1 curve, in affine coordinates. */ typedef struct { - secp256k1_fe_t x; - secp256k1_fe_t y; + secp256k1_fe x; + secp256k1_fe y; int infinity; /* whether this represents the point at infinity */ -} secp256k1_ge_t; +} secp256k1_ge; #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. */ typedef struct { - secp256k1_fe_t x; /* actual X: x/z^2 */ - secp256k1_fe_t y; /* actual Y: y/z^3 */ - secp256k1_fe_t z; + secp256k1_fe x; /* actual X: x/z^2 */ + secp256k1_fe y; /* actual Y: y/z^3 */ + secp256k1_fe z; int infinity; /* whether this represents the point at infinity */ -} secp256k1_gej_t; +} secp256k1_gej; #define SECP256K1_GEJ_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)), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1), 0} #define SECP256K1_GEJ_CONST_INFINITY {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), 1} typedef struct { - secp256k1_fe_storage_t x; - secp256k1_fe_storage_t y; -} secp256k1_ge_storage_t; + secp256k1_fe_storage x; + secp256k1_fe_storage y; +} secp256k1_ge_storage; #define SECP256K1_GE_STORAGE_CONST(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {SECP256K1_FE_STORAGE_CONST((a),(b),(c),(d),(e),(f),(g),(h)), SECP256K1_FE_STORAGE_CONST((i),(j),(k),(l),(m),(n),(o),(p))} -/** Set a group element equal to the point at infinity */ -static void secp256k1_ge_set_infinity(secp256k1_ge_t *r); +#define SECP256K1_GE_STORAGE_CONST_GET(t) SECP256K1_FE_STORAGE_CONST_GET(t.x), SECP256K1_FE_STORAGE_CONST_GET(t.y) /** Set a group element equal to the point with given X and Y coordinates */ -static void secp256k1_ge_set_xy(secp256k1_ge_t *r, const secp256k1_fe_t *x, const secp256k1_fe_t *y); +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_t *r, const secp256k1_fe_t *x, int odd); +static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd); /** Check whether a group element is the point at infinity. */ -static int secp256k1_ge_is_infinity(const secp256k1_ge_t *a); +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_t *a); +static int secp256k1_ge_is_valid_var(const secp256k1_ge *a); -static void secp256k1_ge_neg(secp256k1_ge_t *r, const secp256k1_ge_t *a); +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 */ -static void secp256k1_ge_set_gej(secp256k1_ge_t *r, secp256k1_gej_t *a); +static void secp256k1_ge_set_gej(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(size_t len, secp256k1_ge_t *r, const secp256k1_gej_t *a); +static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb); +/** 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); -/** Set a group element (jacobian) equal to the point at infinity. */ -static void secp256k1_gej_set_infinity(secp256k1_gej_t *r); +/** 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 (jacobian) equal to the point with given X and Y coordinates. */ -static void secp256k1_gej_set_xy(secp256k1_gej_t *r, const secp256k1_fe_t *x, const secp256k1_fe_t *y); +/** Set a group element (jacobian) equal to the point at infinity. */ +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_t *r, const secp256k1_ge_t *a); +static void secp256k1_gej_set_ge(secp256k1_gej *r, const secp256k1_ge *a); /** Compare the X coordinate of a group element (jacobian). */ -static int secp256k1_gej_eq_x_var(const secp256k1_fe_t *x, const secp256k1_gej_t *a); +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) */ -static void secp256k1_gej_neg(secp256k1_gej_t *r, const secp256k1_gej_t *a); +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_t *a); +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. */ -static void secp256k1_gej_double_var(secp256k1_gej_t *r, const secp256k1_gej_t *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 sum of a and b. */ -static void secp256k1_gej_add_var(secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_gej_t *b); +/** 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). */ +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). */ +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). */ -static void secp256k1_gej_add_ge(secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_ge_t *b); +static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b); /** 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. */ -static void secp256k1_gej_add_ge_var(secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_ge_t *b); + 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). */ +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_gej_mul_lambda(secp256k1_gej_t *r, const secp256k1_gej_t *a); +static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a); #endif -/** Clear a secp256k1_gej_t to prevent leaking sensitive information. */ -static void secp256k1_gej_clear(secp256k1_gej_t *r); +/** Clear a secp256k1_gej to prevent leaking sensitive information. */ +static void secp256k1_gej_clear(secp256k1_gej *r); -/** Clear a secp256k1_ge_t to prevent leaking sensitive information. */ -static void secp256k1_ge_clear(secp256k1_ge_t *r); +/** Clear a secp256k1_ge to prevent leaking sensitive information. */ +static void secp256k1_ge_clear(secp256k1_ge *r); /** Convert a group element to the storage type. */ -static void secp256k1_ge_to_storage(secp256k1_ge_storage_t *r, const secp256k1_ge_t*); +static void secp256k1_ge_to_storage(secp256k1_ge_storage *r, const secp256k1_ge *a); /** Convert a group element back from the storage type. */ -static void secp256k1_ge_from_storage(secp256k1_ge_t *r, const secp256k1_ge_storage_t*); +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. */ -static void secp256k1_ge_storage_cmov(secp256k1_ge_storage_t *r, const secp256k1_ge_storage_t *a, int flag); +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_t *r, const secp256k1_fe_t *b); +static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *b); -#endif +#endif /* SECP256K1_GROUP_H */ diff --git a/src/secp256k1/src/group_impl.h b/src/secp256k1/src/group_impl.h index 0f64576fb..b31b6c12e 100644 --- a/src/secp256k1/src/group_impl.h +++ b/src/secp256k1/src/group_impl.h @@ -4,47 +4,101 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_GROUP_IMPL_H_ -#define _SECP256K1_GROUP_IMPL_H_ - -#include +#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() + */ +#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; +# 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; +# 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_t secp256k1_ge_const_g = SECP256K1_GE_CONST( +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 ); -static void secp256k1_ge_set_infinity(secp256k1_ge_t *r) { - r->infinity = 1; +const int CURVE_B = 7; +#endif + +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_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; } -static void secp256k1_ge_set_xy(secp256k1_ge_t *r, const secp256k1_fe_t *x, const secp256k1_fe_t *y) { +static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const secp256k1_fe *y) { r->infinity = 0; r->x = *x; r->y = *y; } -static int secp256k1_ge_is_infinity(const secp256k1_ge_t *a) { +static int secp256k1_ge_is_infinity(const secp256k1_ge *a) { return a->infinity; } -static void secp256k1_ge_neg(secp256k1_ge_t *r, const secp256k1_ge_t *a) { +static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a) { *r = *a; secp256k1_fe_normalize_weak(&r->y); secp256k1_fe_negate(&r->y, &r->y, 1); } -static void secp256k1_ge_set_gej(secp256k1_ge_t *r, secp256k1_gej_t *a) { - secp256k1_fe_t z2, z3; +static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a) { + secp256k1_fe z2, z3; r->infinity = a->infinity; secp256k1_fe_inv(&a->z, &a->z); secp256k1_fe_sqr(&z2, &a->z); @@ -56,8 +110,8 @@ static void secp256k1_ge_set_gej(secp256k1_ge_t *r, secp256k1_gej_t *a) { r->y = a->y; } -static void secp256k1_ge_set_gej_var(secp256k1_ge_t *r, secp256k1_gej_t *a) { - secp256k1_fe_t z2, z3; +static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a) { + secp256k1_fe z2, z3; r->infinity = a->infinity; if (a->infinity) { return; @@ -72,73 +126,106 @@ static void secp256k1_ge_set_gej_var(secp256k1_ge_t *r, secp256k1_gej_t *a) { r->y = a->y; } -static void secp256k1_ge_set_all_gej_var(size_t len, secp256k1_ge_t *r, const secp256k1_gej_t *a) { - secp256k1_fe_t *az; - secp256k1_fe_t *azi; +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; size_t i; size_t count = 0; - az = (secp256k1_fe_t *)checked_malloc(sizeof(secp256k1_fe_t) * len); + az = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * len); for (i = 0; i < len; i++) { if (!a[i].infinity) { az[count++] = a[i].z; } } - azi = (secp256k1_fe_t *)checked_malloc(sizeof(secp256k1_fe_t) * count); - secp256k1_fe_inv_all_var(count, azi, az); + azi = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * count); + secp256k1_fe_inv_all_var(azi, az, count); free(az); count = 0; for (i = 0; i < len; i++) { r[i].infinity = a[i].infinity; if (!a[i].infinity) { - secp256k1_fe_t zi2, zi3; - secp256k1_fe_t *zi = &azi[count++]; - secp256k1_fe_sqr(&zi2, zi); - secp256k1_fe_mul(&zi3, &zi2, zi); - secp256k1_fe_mul(&r[i].x, &a[i].x, &zi2); - secp256k1_fe_mul(&r[i].y, &a[i].y, &zi3); + secp256k1_ge_set_gej_zinv(&r[i], &a[i], &azi[count++]); } } free(azi); } -static void secp256k1_gej_set_infinity(secp256k1_gej_t *r) { - r->infinity = 1; - secp256k1_fe_set_int(&r->x, 0); - secp256k1_fe_set_int(&r->y, 0); - secp256k1_fe_set_int(&r->z, 0); +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); + } + } } -static void secp256k1_gej_set_xy(secp256k1_gej_t *r, const secp256k1_fe_t *x, const secp256k1_fe_t *y) { - r->infinity = 0; - r->x = *x; - r->y = *y; - secp256k1_fe_set_int(&r->z, 1); +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; + secp256k1_fe zs; + + 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; + zs = zr[i]; + + /* Work our way backwards, using the z-ratios to scale the x/y values. */ + while (i > 0) { + if (i != len - 1) { + secp256k1_fe_mul(&zs, &zs, &zr[i]); + } + i--; + secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zs); + } + } +} + +static void secp256k1_gej_set_infinity(secp256k1_gej *r) { + r->infinity = 1; + secp256k1_fe_clear(&r->x); + secp256k1_fe_clear(&r->y); + secp256k1_fe_clear(&r->z); } -static void secp256k1_gej_clear(secp256k1_gej_t *r) { +static void secp256k1_gej_clear(secp256k1_gej *r) { r->infinity = 0; secp256k1_fe_clear(&r->x); secp256k1_fe_clear(&r->y); secp256k1_fe_clear(&r->z); } -static void secp256k1_ge_clear(secp256k1_ge_t *r) { +static void secp256k1_ge_clear(secp256k1_ge *r) { r->infinity = 0; secp256k1_fe_clear(&r->x); secp256k1_fe_clear(&r->y); } -static int secp256k1_ge_set_xo_var(secp256k1_ge_t *r, const secp256k1_fe_t *x, int odd) { - secp256k1_fe_t x2, x3, c; +static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x) { + secp256k1_fe x2, x3, c; r->x = *x; secp256k1_fe_sqr(&x2, x); secp256k1_fe_mul(&x3, x, &x2); r->infinity = 0; - secp256k1_fe_set_int(&c, 7); + secp256k1_fe_set_int(&c, CURVE_B); secp256k1_fe_add(&c, &x3); - if (!secp256k1_fe_sqrt_var(&r->y, &c)) { + 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_normalize_var(&r->y); @@ -146,24 +233,25 @@ static int secp256k1_ge_set_xo_var(secp256k1_ge_t *r, const secp256k1_fe_t *x, i secp256k1_fe_negate(&r->y, &r->y, 1); } return 1; + } -static void secp256k1_gej_set_ge(secp256k1_gej_t *r, const secp256k1_ge_t *a) { +static void secp256k1_gej_set_ge(secp256k1_gej *r, const secp256k1_ge *a) { r->infinity = a->infinity; r->x = a->x; r->y = a->y; secp256k1_fe_set_int(&r->z, 1); } -static int secp256k1_gej_eq_x_var(const secp256k1_fe_t *x, const secp256k1_gej_t *a) { - secp256k1_fe_t r, r2; +static int secp256k1_gej_eq_x_var(const secp256k1_fe *x, const secp256k1_gej *a) { + secp256k1_fe r, r2; 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); } -static void secp256k1_gej_neg(secp256k1_gej_t *r, const secp256k1_gej_t *a) { +static void secp256k1_gej_neg(secp256k1_gej *r, const secp256k1_gej *a) { r->infinity = a->infinity; r->x = a->x; r->y = a->y; @@ -172,12 +260,12 @@ static void secp256k1_gej_neg(secp256k1_gej_t *r, const secp256k1_gej_t *a) { secp256k1_fe_negate(&r->y, &r->y, 1); } -static int secp256k1_gej_is_infinity(const secp256k1_gej_t *a) { +static int secp256k1_gej_is_infinity(const secp256k1_gej *a) { return a->infinity; } -static int secp256k1_gej_is_valid_var(const secp256k1_gej_t *a) { - secp256k1_fe_t y2, x3, z2, z6; +static int secp256k1_gej_is_valid_var(const secp256k1_gej *a) { + secp256k1_fe y2, x3, z2, z6; if (a->infinity) { return 0; } @@ -190,38 +278,59 @@ static int secp256k1_gej_is_valid_var(const secp256k1_gej_t *a) { 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, 7); + secp256k1_fe_mul_int(&z6, CURVE_B); secp256k1_fe_add(&x3, &z6); secp256k1_fe_normalize_weak(&x3); return secp256k1_fe_equal_var(&y2, &x3); } -static int secp256k1_ge_is_valid_var(const secp256k1_ge_t *a) { - secp256k1_fe_t y2, x3, c; +static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) { + secp256k1_fe y2, x3, c; 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, 7); + secp256k1_fe_set_int(&c, CURVE_B); secp256k1_fe_add(&x3, &c); secp256k1_fe_normalize_weak(&x3); return secp256k1_fe_equal_var(&y2, &x3); } -static void secp256k1_gej_double_var(secp256k1_gej_t *r, const secp256k1_gej_t *a) { - /* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate */ - secp256k1_fe_t t1,t2,t3,t4; +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. + */ + secp256k1_fe t1,t2,t3,t4; /** 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. + * + * Having said this, if this function receives a point on a sextic twist, e.g. by + * a fault attack, it is possible for y to be 0. This happens for y^2 = x^3 + 6, + * since -6 does have a cube root mod p. For this point, this function will not set + * 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 (rzr != NULL) { + secp256k1_fe_set_int(rzr, 1); + } return; } + 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); @@ -244,17 +353,29 @@ static void secp256k1_gej_double_var(secp256k1_gej_t *r, const secp256k1_gej_t * secp256k1_fe_add(&r->y, &t2); /* Y' = 36*X^3*Y^2 - 27*X^6 - 8*Y^4 (4) */ } -static void secp256k1_gej_add_var(secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_gej_t *b) { +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); +} + +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_t z22, z12, u1, u2, s1, s2, h, i, i2, h2, h3, t; + secp256k1_fe z22, z12, u1, u2, s1, s2, h, i, i2, h2, h3, t; + if (a->infinity) { + VERIFY_CHECK(rzr == NULL); *r = *b; return; } + if (b->infinity) { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 1); + } *r = *a; return; } + r->infinity = 0; secp256k1_fe_sqr(&z22, &b->z); secp256k1_fe_sqr(&z12, &a->z); @@ -266,8 +387,11 @@ static void secp256k1_gej_add_var(secp256k1_gej_t *r, const secp256k1_gej_t *a, secp256k1_fe_negate(&i, &s1, 1); secp256k1_fe_add(&i, &s2); if (secp256k1_fe_normalizes_to_zero_var(&h)) { if (secp256k1_fe_normalizes_to_zero_var(&i)) { - secp256k1_gej_double_var(r, a); + secp256k1_gej_double_var(r, a, rzr); } else { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 0); + } r->infinity = 1; } return; @@ -275,7 +399,11 @@ static void secp256k1_gej_add_var(secp256k1_gej_t *r, const secp256k1_gej_t *a, secp256k1_fe_sqr(&i2, &i); secp256k1_fe_sqr(&h2, &h); secp256k1_fe_mul(&h3, &h, &h2); - secp256k1_fe_mul(&r->z, &a->z, &b->z); secp256k1_fe_mul(&r->z, &r->z, &h); + secp256k1_fe_mul(&h, &h, &b->z); + if (rzr != NULL) { + *rzr = h; + } + secp256k1_fe_mul(&r->z, &a->z, &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); @@ -283,21 +411,23 @@ static void secp256k1_gej_add_var(secp256k1_gej_t *r, const secp256k1_gej_t *a, secp256k1_fe_add(&r->y, &h3); } -static void secp256k1_gej_add_ge_var(secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_ge_t *b) { +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_t z12, u1, u2, s1, s2, h, i, i2, h2, h3, t; + secp256k1_fe z12, u1, u2, s1, s2, h, i, i2, h2, h3, t; if (a->infinity) { - r->infinity = b->infinity; - r->x = b->x; - r->y = b->y; - secp256k1_fe_set_int(&r->z, 1); + VERIFY_CHECK(rzr == NULL); + secp256k1_gej_set_ge(r, b); return; } if (b->infinity) { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 1); + } *r = *a; return; } r->infinity = 0; + secp256k1_fe_sqr(&z12, &a->z); u1 = a->x; secp256k1_fe_normalize_weak(&u1); secp256k1_fe_mul(&u2, &b->x, &z12); @@ -307,7 +437,69 @@ static void secp256k1_gej_add_ge_var(secp256k1_gej_t *r, const secp256k1_gej_t * secp256k1_fe_negate(&i, &s1, 1); secp256k1_fe_add(&i, &s2); if (secp256k1_fe_normalizes_to_zero_var(&h)) { if (secp256k1_fe_normalizes_to_zero_var(&i)) { - secp256k1_gej_double_var(r, a); + secp256k1_gej_double_var(r, a, rzr); + } else { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 0); + } + r->infinity = 1; + } + return; + } + secp256k1_fe_sqr(&i2, &i); + secp256k1_fe_sqr(&h2, &h); + secp256k1_fe_mul(&h3, &h, &h2); + if (rzr != NULL) { + *rzr = h; + } + secp256k1_fe_mul(&r->z, &a->z, &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_add(&r->y, &h3); +} + +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; + + if (b->infinity) { + *r = *a; + return; + } + if (a->infinity) { + secp256k1_fe bzinv2, bzinv3; + r->infinity = b->infinity; + secp256k1_fe_sqr(&bzinv2, bzinv); + secp256k1_fe_mul(&bzinv3, &bzinv2, bzinv); + secp256k1_fe_mul(&r->x, &b->x, &bzinv2); + secp256k1_fe_mul(&r->y, &b->y, &bzinv3); + secp256k1_fe_set_int(&r->z, 1); + 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 + * by bzinv, and get: (rx,ry,rz*bzinv) = (ax,ay,az*bzinv) + (bx,by,1). + * This means that (rx,ry,rz) can be calculated as + * (ax,ay,az*bzinv) + (bx,by,1), when not applying the bzinv factor to rz. + * The variable az below holds the modified Z coordinate for a, which is used + * for the computation of rx and ry, but not for rz. + */ + secp256k1_fe_mul(&az, &a->z, bzinv); + + secp256k1_fe_sqr(&z12, &az); + u1 = a->x; secp256k1_fe_normalize_weak(&u1); + secp256k1_fe_mul(&u2, &b->x, &z12); + s1 = a->y; secp256k1_fe_normalize_weak(&s1); + 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); + 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; } @@ -324,11 +516,13 @@ static void secp256k1_gej_add_ge_var(secp256k1_gej_t *r, const secp256k1_gej_t * secp256k1_fe_add(&r->y, &h3); } -static void secp256k1_gej_add_ge(secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_ge_t *b) { - /* Operations: 7 mul, 5 sqr, 5 normalize, 17 mul_int/add/negate/cmov */ - static const secp256k1_fe_t fe_1 = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1); - secp256k1_fe_t zz, u1, u2, s1, s2, z, t, m, n, q, rr; - int infinity; + +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); + secp256k1_fe zz, u1, u2, s1, s2, t, tt, m, n, q, rr; + secp256k1_fe m_alt, rr_alt; + int infinity, degenerate; VERIFY_CHECK(!b->infinity); VERIFY_CHECK(a->infinity == 0 || a->infinity == 1); @@ -352,53 +546,102 @@ static void secp256k1_gej_add_ge(secp256k1_gej_t *r, const secp256k1_gej_t *a, c * Y3 = 4*(R*(3*Q-2*R^2)-M^4) * Z3 = 2*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 + * of distinct points and doubling. However, it breaks down in the + * case that either point is infinity, or that y1 = -y2. We handle + * these cases in the following ways: + * + * - If b is infinity we simply bail by means of a VERIFY_CHECK. + * + * - If a is infinity, we detect this, and at the end of the + * computation replace the result (which will be meaningless, + * but we compute to be constant-time) with b.x : b.y : 1. + * + * - If a = -b, we have y1 = -y2, which is a degenerate case. + * But here the answer is infinity, so we simply set the + * infinity flag of the result, overriding the computed values + * without even needing to cmov. + * + * - If y1 = -y2 but x1 != x2, which does occur thanks to certain + * properties of our curve (specifically, 1 has nontrivial cube + * roots in our field, and the curve equation has no x coefficient) + * then the answer is not infinity but also not given by the above + * equation. In this case, we cmov in place an alternate expression + * for lambda. Specifically (y1 - y2)/(x1 - x2). Where both these + * expressions for lambda are defined, they are equal, and can be + * obtained from each other by multiplication by (y1 + y2)/(y1 + y2) + * then substitution of x^3 + 7 for y^2 (using the curve equation). + * For all pairs of nonzero points (a, b) at least one is defined, + * so this covers everything. */ secp256k1_fe_sqr(&zz, &a->z); /* z = Z1^2 */ u1 = a->x; secp256k1_fe_normalize_weak(&u1); /* u1 = U1 = X1*Z2^2 (1) */ 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) */ - secp256k1_fe_mul(&s2, &b->y, &zz); /* s2 = Y2*Z2^2 (1) */ + 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) */ - z = a->z; /* z = Z = Z1*Z2 (8) */ t = u1; secp256k1_fe_add(&t, &u2); /* t = T = U1+U2 (2) */ m = s1; secp256k1_fe_add(&m, &s2); /* m = M = S1+S2 (2) */ - secp256k1_fe_sqr(&n, &m); /* n = M^2 (1) */ - secp256k1_fe_mul(&q, &n, &t); /* q = Q = T*M^2 (1) */ - secp256k1_fe_sqr(&n, &n); /* n = M^4 (1) */ secp256k1_fe_sqr(&rr, &t); /* rr = T^2 (1) */ - secp256k1_fe_mul(&t, &u1, &u2); secp256k1_fe_negate(&t, &t, 1); /* t = -U1*U2 (2) */ - secp256k1_fe_add(&rr, &t); /* rr = R = T^2-U1*U2 (3) */ - secp256k1_fe_sqr(&t, &rr); /* t = R^2 (1) */ - secp256k1_fe_mul(&r->z, &m, &z); /* r->z = M*Z (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); + /* 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_cmov(&rr_alt, &rr, !degenerate); + secp256k1_fe_cmov(&m_alt, &m, !degenerate); + /* Now Ralt / Malt = lambda and is guaranteed not to be 0/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) */ + /* 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(&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 * (1 - a->infinity)); /* r->z = Z3 = 2*M*Z (2) */ - r->x = t; /* r->x = R^2 (1) */ + 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(&r->x, &q); /* r->x = R^2-Q (3) */ - secp256k1_fe_normalize(&r->x); - secp256k1_fe_mul_int(&q, 3); /* q = -3*Q (6) */ - secp256k1_fe_mul_int(&t, 2); /* t = 2*R^2 (2) */ - secp256k1_fe_add(&t, &q); /* t = 2*R^2-3*Q (8) */ - secp256k1_fe_mul(&t, &t, &rr); /* t = R*(2*R^2-3*Q) (1) */ - secp256k1_fe_add(&t, &n); /* t = R*(2*R^2-3*Q)+M^4 (2) */ - secp256k1_fe_negate(&r->y, &t, 2); /* r->y = R*(3*Q-2*R^2)-M^4 (3) */ + 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 * (1 - a->infinity)); /* r->x = X3 = 4*(R^2-Q) */ - secp256k1_fe_mul_int(&r->y, 4 * (1 - a->infinity)); /* r->y = Y3 = 4*R*(3*Q-2*R^2)-4*M^4 (4) */ + 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, the above code results in r->x, r->y, and r->z all equal to 0. - * Replace r with b->x, b->y, 1 in that case. - */ + /** 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; } -static void secp256k1_gej_rescale(secp256k1_gej_t *r, const secp256k1_fe_t *s) { +static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *s) { /* Operations: 4 mul, 1 sqr */ - secp256k1_fe_t zz; + secp256k1_fe zz; VERIFY_CHECK(!secp256k1_fe_is_zero(s)); secp256k1_fe_sqr(&zz, s); secp256k1_fe_mul(&r->x, &r->x, &zz); /* r->x *= s^2 */ @@ -407,8 +650,8 @@ static void secp256k1_gej_rescale(secp256k1_gej_t *r, const secp256k1_fe_t *s) { secp256k1_fe_mul(&r->z, &r->z, s); /* r->z *= s */ } -static void secp256k1_ge_to_storage(secp256k1_ge_storage_t *r, const secp256k1_ge_t *a) { - secp256k1_fe_t x, y; +static void secp256k1_ge_to_storage(secp256k1_ge_storage *r, const secp256k1_ge *a) { + secp256k1_fe x, y; VERIFY_CHECK(!a->infinity); x = a->x; secp256k1_fe_normalize(&x); @@ -418,20 +661,20 @@ static void secp256k1_ge_to_storage(secp256k1_ge_storage_t *r, const secp256k1_g secp256k1_fe_to_storage(&r->y, &y); } -static void secp256k1_ge_from_storage(secp256k1_ge_t *r, const secp256k1_ge_storage_t *a) { +static void secp256k1_ge_from_storage(secp256k1_ge *r, const secp256k1_ge_storage *a) { secp256k1_fe_from_storage(&r->x, &a->x); secp256k1_fe_from_storage(&r->y, &a->y); r->infinity = 0; } -static SECP256K1_INLINE void secp256k1_ge_storage_cmov(secp256k1_ge_storage_t *r, const secp256k1_ge_storage_t *a, int flag) { +static SECP256K1_INLINE void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_ge_storage *a, int flag) { secp256k1_fe_storage_cmov(&r->x, &a->x, flag); secp256k1_fe_storage_cmov(&r->y, &a->y, flag); } #ifdef USE_ENDOMORPHISM -static void secp256k1_gej_mul_lambda(secp256k1_gej_t *r, const secp256k1_gej_t *a) { - static const secp256k1_fe_t beta = SECP256K1_FE_CONST( +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 ); @@ -440,4 +683,18 @@ static void secp256k1_gej_mul_lambda(secp256k1_gej_t *r, const secp256k1_gej_t * } #endif -#endif +static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) { + secp256k1_fe yz; + + if (a->infinity) { + return 0; + } + + /* 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); +} + +#endif /* SECP256K1_GROUP_IMPL_H */ diff --git a/src/secp256k1/src/hash.h b/src/secp256k1/src/hash.h index 843423d7f..de26e4b89 100644 --- a/src/secp256k1/src/hash.h +++ b/src/secp256k1/src/hash.h @@ -4,38 +4,38 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_HASH_ -#define _SECP256K1_HASH_ +#ifndef SECP256K1_HASH_H +#define SECP256K1_HASH_H #include #include typedef struct { - uint32_t s[32]; + uint32_t s[8]; uint32_t buf[16]; /* In big endian */ size_t bytes; -} secp256k1_sha256_t; +} secp256k1_sha256; -static void secp256k1_sha256_initialize(secp256k1_sha256_t *hash); -static void secp256k1_sha256_write(secp256k1_sha256_t *hash, const unsigned char *data, size_t size); -static void secp256k1_sha256_finalize(secp256k1_sha256_t *hash, unsigned char *out32); +static void secp256k1_sha256_initialize(secp256k1_sha256 *hash); +static void secp256k1_sha256_write(secp256k1_sha256 *hash, const unsigned char *data, size_t size); +static void secp256k1_sha256_finalize(secp256k1_sha256 *hash, unsigned char *out32); typedef struct { - secp256k1_sha256_t inner, outer; -} secp256k1_hmac_sha256_t; + secp256k1_sha256 inner, outer; +} secp256k1_hmac_sha256; -static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256_t *hash, const unsigned char *key, size_t size); -static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256_t *hash, const unsigned char *data, size_t size); -static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256_t *hash, unsigned char *out32); +static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t size); +static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256 *hash, const unsigned char *data, size_t size); +static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256 *hash, unsigned char *out32); typedef struct { unsigned char v[32]; unsigned char k[32]; int retry; -} secp256k1_rfc6979_hmac_sha256_t; +} secp256k1_rfc6979_hmac_sha256; -static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256_t *rng, const unsigned char *key, size_t keylen, const unsigned char *msg, size_t msglen, const unsigned char *rnd, size_t rndlen); -static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256_t *rng, unsigned char *out, size_t outlen); -static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256_t *rng); +static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256 *rng, const unsigned char *key, size_t keylen); +static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256 *rng, unsigned char *out, size_t outlen); +static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256 *rng); -#endif +#endif /* SECP256K1_HASH_H */ diff --git a/src/secp256k1/src/hash_impl.h b/src/secp256k1/src/hash_impl.h index 9828827bc..c06db9e33 100644 --- a/src/secp256k1/src/hash_impl.h +++ b/src/secp256k1/src/hash_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_HASH_IMPL_H_ -#define _SECP256K1_HASH_IMPL_H_ +#ifndef SECP256K1_HASH_IMPL_H +#define SECP256K1_HASH_IMPL_H #include "hash.h" @@ -33,7 +33,7 @@ #define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) #endif -static void secp256k1_sha256_initialize(secp256k1_sha256_t *hash) { +static void secp256k1_sha256_initialize(secp256k1_sha256 *hash) { hash->s[0] = 0x6a09e667ul; hash->s[1] = 0xbb67ae85ul; hash->s[2] = 0x3c6ef372ul; @@ -128,7 +128,7 @@ static void secp256k1_sha256_transform(uint32_t* s, const uint32_t* chunk) { s[7] += h; } -static void secp256k1_sha256_write(secp256k1_sha256_t *hash, const unsigned char *data, size_t len) { +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) { @@ -145,7 +145,7 @@ static void secp256k1_sha256_write(secp256k1_sha256_t *hash, const unsigned char } } -static void secp256k1_sha256_finalize(secp256k1_sha256_t *hash, unsigned char *out32) { +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]; @@ -161,14 +161,14 @@ static void secp256k1_sha256_finalize(secp256k1_sha256_t *hash, unsigned char *o memcpy(out32, (const unsigned char*)out, 32); } -static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256_t *hash, const unsigned char *key, size_t keylen) { +static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t keylen) { int n; unsigned char rkey[64]; if (keylen <= 64) { memcpy(rkey, key, keylen); memset(rkey + keylen, 0, 64 - keylen); } else { - secp256k1_sha256_t sha256; + secp256k1_sha256 sha256; secp256k1_sha256_initialize(&sha256); secp256k1_sha256_write(&sha256, key, keylen); secp256k1_sha256_finalize(&sha256, rkey); @@ -189,11 +189,11 @@ static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256_t *hash, cons memset(rkey, 0, 64); } -static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256_t *hash, const unsigned char *data, size_t size) { +static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256 *hash, const unsigned char *data, size_t size) { secp256k1_sha256_write(&hash->inner, data, size); } -static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256_t *hash, unsigned char *out32) { +static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256 *hash, unsigned char *out32) { unsigned char temp[32]; secp256k1_sha256_finalize(&hash->inner, temp); secp256k1_sha256_write(&hash->outer, temp, 32); @@ -202,8 +202,8 @@ static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256_t *hash, unsign } -static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256_t *rng, const unsigned char *key, size_t keylen, const unsigned char *msg, size_t msglen, const unsigned char *rnd, size_t rndlen) { - secp256k1_hmac_sha256_t hmac; +static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256 *rng, const unsigned char *key, size_t keylen) { + secp256k1_hmac_sha256 hmac; static const unsigned char zero[1] = {0x00}; static const unsigned char one[1] = {0x01}; @@ -215,11 +215,6 @@ static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha2 secp256k1_hmac_sha256_write(&hmac, rng->v, 32); secp256k1_hmac_sha256_write(&hmac, zero, 1); secp256k1_hmac_sha256_write(&hmac, key, keylen); - secp256k1_hmac_sha256_write(&hmac, msg, msglen); - if (rnd && rndlen) { - /* RFC6979 3.6 "Additional data". */ - secp256k1_hmac_sha256_write(&hmac, rnd, rndlen); - } secp256k1_hmac_sha256_finalize(&hmac, rng->k); secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); secp256k1_hmac_sha256_write(&hmac, rng->v, 32); @@ -230,11 +225,6 @@ static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha2 secp256k1_hmac_sha256_write(&hmac, rng->v, 32); secp256k1_hmac_sha256_write(&hmac, one, 1); secp256k1_hmac_sha256_write(&hmac, key, keylen); - secp256k1_hmac_sha256_write(&hmac, msg, msglen); - if (rnd && rndlen) { - /* RFC6979 3.6 "Additional data". */ - secp256k1_hmac_sha256_write(&hmac, rnd, rndlen); - } secp256k1_hmac_sha256_finalize(&hmac, rng->k); secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); secp256k1_hmac_sha256_write(&hmac, rng->v, 32); @@ -242,11 +232,11 @@ static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha2 rng->retry = 0; } -static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256_t *rng, unsigned char *out, size_t outlen) { +static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256 *rng, unsigned char *out, size_t outlen) { /* RFC6979 3.2.h. */ static const unsigned char zero[1] = {0x00}; if (rng->retry) { - secp256k1_hmac_sha256_t hmac; + secp256k1_hmac_sha256 hmac; secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); secp256k1_hmac_sha256_write(&hmac, rng->v, 32); secp256k1_hmac_sha256_write(&hmac, zero, 1); @@ -257,7 +247,7 @@ static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256 } while (outlen > 0) { - secp256k1_hmac_sha256_t hmac; + secp256k1_hmac_sha256 hmac; int now = outlen; secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); secp256k1_hmac_sha256_write(&hmac, rng->v, 32); @@ -273,21 +263,19 @@ static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256 rng->retry = 1; } -static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256_t *rng) { +static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256 *rng) { memset(rng->k, 0, 32); memset(rng->v, 0, 32); rng->retry = 0; } - +#undef BE32 #undef Round -#undef sigma0 #undef sigma1 -#undef Sigma0 +#undef sigma0 #undef Sigma1 -#undef Ch +#undef Sigma0 #undef Maj -#undef ReadBE32 -#undef WriteBE32 +#undef Ch -#endif +#endif /* SECP256K1_HASH_IMPL_H */ diff --git a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java index 90a498eaa..1c67802fb 100644 --- a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java +++ b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java @@ -1,60 +1,446 @@ +/* + * 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/sipa/secp256k1 + *

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 { - public static final boolean enabled; - static { - boolean isEnabled = true; - try { - System.loadLibrary("javasecp256k1"); - } catch (UnsatisfiedLinkError e) { - isEnabled = false; - } - enabled = isEnabled; - } - + + 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) { + 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 = ByteBuffer.allocateDirect(32 + 8 + 520 + 520); + if (byteBuff == null || byteBuff.capacity() < 520) { + byteBuff = ByteBuffer.allocateDirect(520); byteBuff.order(ByteOrder.nativeOrder()); nativeECDSABuffer.set(byteBuff); } byteBuff.rewind(); byteBuff.put(data); - byteBuff.putInt(signature.length); - byteBuff.putInt(pub.length); byteBuff.put(signature); byteBuff.put(pub); - return secp256k1_ecdsa_verify(byteBuff) == 1; + + 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; } /** - * @param byteBuff signature format is byte[32] data, - * native-endian int signatureLength, native-endian int pubkeyLength, - * byte[signatureLength] signature, byte[pubkeyLength] pub - * @returns 1 for valid signature, anything else for invalid + * 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 */ - private static native int secp256k1_ecdsa_verify(ByteBuffer byteBuff); + 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 new file mode 100644 index 000000000..c00d08899 --- /dev/null +++ b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java @@ -0,0 +1,226 @@ +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 new file mode 100644 index 000000000..04732ba04 --- /dev/null +++ b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java @@ -0,0 +1,45 @@ +/* + * 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 new file mode 100644 index 000000000..216c986a8 --- /dev/null +++ b/src/secp256k1/src/java/org/bitcoin/Secp256k1Context.java @@ -0,0 +1,51 @@ +/* + * 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 index bb4cd7072..bcef7b32c 100644 --- a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c +++ b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c @@ -1,23 +1,377 @@ +#include +#include +#include #include "org_bitcoin_NativeSecp256k1.h" #include "include/secp256k1.h" +#include "include/secp256k1_ecdh.h" +#include "include/secp256k1_recovery.h" -JNIEXPORT jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify - (JNIEnv* env, jclass classObject, jobject byteBufferObject) + +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) { - unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); - int sigLen = *((int*)(data + 32)); - int pubLen = *((int*)(data + 32 + 4)); + 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); - return secp256k1_ecdsa_verify(data, 32, data+32+8, sigLen, data+32+8+sigLen, pubLen); } -static void __javasecp256k1_attach(void) __attribute__((constructor)); -static void __javasecp256k1_detach(void) __attribute__((destructor)); +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); -static void __javasecp256k1_attach(void) { - secp256k1_start(SECP256K1_START_VERIFY); + (void)classObject;(void)env; } -static void __javasecp256k1_detach(void) { - secp256k1_stop(); +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 index d7fb004fa..fe613c9e9 100644 --- a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h +++ b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h @@ -1,5 +1,6 @@ /* 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 @@ -7,13 +8,110 @@ #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;)I + * 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 */ -JNIEXPORT jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify - (JNIEnv *, jclass, jobject); +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen); + #ifdef __cplusplus } diff --git a/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c b/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c new file mode 100644 index 000000000..a52939e7e --- /dev/null +++ b/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c @@ -0,0 +1,15 @@ +#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 new file mode 100644 index 000000000..0d2bc84b7 --- /dev/null +++ b/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.h @@ -0,0 +1,22 @@ +/* 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/modules/ecdh/Makefile.am.include b/src/secp256k1/src/modules/ecdh/Makefile.am.include new file mode 100644 index 000000000..e3088b469 --- /dev/null +++ b/src/secp256k1/src/modules/ecdh/Makefile.am.include @@ -0,0 +1,8 @@ +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 diff --git a/src/secp256k1/src/modules/ecdh/main_impl.h b/src/secp256k1/src/modules/ecdh/main_impl.h new file mode 100644 index 000000000..bd8739eeb --- /dev/null +++ b/src/secp256k1/src/modules/ecdh/main_impl.h @@ -0,0 +1,54 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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" + +int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const secp256k1_pubkey *point, const unsigned char *scalar) { + int ret = 0; + int overflow = 0; + secp256k1_gej res; + secp256k1_ge pt; + secp256k1_scalar s; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(result != NULL); + ARG_CHECK(point != NULL); + ARG_CHECK(scalar != NULL); + + 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; + } + + secp256k1_scalar_clear(&s); + return ret; +} + +#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 new file mode 100644 index 000000000..0c53f8ee0 --- /dev/null +++ b/src/secp256k1/src/modules/ecdh/tests_impl.h @@ -0,0 +1,105 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://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); + 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 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); +} + +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) { + secp256k1_sha256 sha; + unsigned char s_b32[32]; + unsigned char output_ecdh[32]; + unsigned char output_ser[32]; + unsigned char point_ser[33]; + size_t point_ser_len = sizeof(point_ser); + secp256k1_scalar s; + + 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); + /* 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)); + 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); + } +} + +void test_bad_scalar(void) { + unsigned char s_zero[32] = { 0 }; + unsigned char s_overflow[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 + }; + unsigned char s_rand[32] = { 0 }; + unsigned char output[32]; + secp256k1_scalar rand; + secp256k1_pubkey point; + + /* Create random point */ + random_scalar_order(&rand); + secp256k1_scalar_get_b32(s_rand, &rand); + 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); + /* ...and a good one */ + s_overflow[31] -= 1; + CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow) == 1); +} + +void run_ecdh_tests(void) { + test_ecdh_api(); + test_ecdh_generator_basepoint(); + test_bad_scalar(); +} + +#endif /* SECP256K1_MODULE_ECDH_TESTS_H */ diff --git a/src/secp256k1/src/modules/recovery/Makefile.am.include b/src/secp256k1/src/modules/recovery/Makefile.am.include new file mode 100644 index 000000000..bf23c26e7 --- /dev/null +++ b/src/secp256k1/src/modules/recovery/Makefile.am.include @@ -0,0 +1,8 @@ +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 diff --git a/src/secp256k1/src/modules/recovery/main_impl.h b/src/secp256k1/src/modules/recovery/main_impl.h new file mode 100755 index 000000000..2f6691c5a --- /dev/null +++ b/src/secp256k1/src/modules/recovery/main_impl.h @@ -0,0 +1,193 @@ +/********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_RECOVERY_MAIN_H +#define SECP256K1_MODULE_RECOVERY_MAIN_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; + if (sizeof(secp256k1_scalar) == 32) { + /* When the secp256k1_scalar type is exactly 32 byte, use its + * representation inside secp256k1_ecdsa_signature, as conversion is very fast. + * Note that secp256k1_ecdsa_signature_save must use the same representation. */ + memcpy(r, &sig->data[0], 32); + memcpy(s, &sig->data[32], 32); + } else { + secp256k1_scalar_set_b32(r, &sig->data[0], NULL); + secp256k1_scalar_set_b32(s, &sig->data[32], NULL); + } + *recid = sig->data[64]; +} + +static void secp256k1_ecdsa_recoverable_signature_save(secp256k1_ecdsa_recoverable_signature* sig, const secp256k1_scalar* r, const secp256k1_scalar* s, int recid) { + if (sizeof(secp256k1_scalar) == 32) { + memcpy(&sig->data[0], r, 32); + memcpy(&sig->data[32], s, 32); + } else { + secp256k1_scalar_get_b32(&sig->data[0], r); + secp256k1_scalar_get_b32(&sig->data[32], s); + } + sig->data[64] = recid; +} + +int secp256k1_ecdsa_recoverable_signature_parse_compact(const secp256k1_context* ctx, secp256k1_ecdsa_recoverable_signature* sig, const unsigned char *input64, int recid) { + secp256k1_scalar r, s; + int ret = 1; + int overflow = 0; + + (void)ctx; + ARG_CHECK(sig != NULL); + ARG_CHECK(input64 != NULL); + ARG_CHECK(recid >= 0 && recid <= 3); + + secp256k1_scalar_set_b32(&r, &input64[0], &overflow); + ret &= !overflow; + secp256k1_scalar_set_b32(&s, &input64[32], &overflow); + ret &= !overflow; + if (ret) { + secp256k1_ecdsa_recoverable_signature_save(sig, &r, &s, recid); + } else { + memset(sig, 0, sizeof(*sig)); + } + return ret; +} + +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; + ARG_CHECK(output64 != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(recid != NULL); + + secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, recid, sig); + secp256k1_scalar_get_b32(&output64[0], &r); + secp256k1_scalar_get_b32(&output64[32], &s); + return 1; +} + +int secp256k1_ecdsa_recoverable_signature_convert(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const secp256k1_ecdsa_recoverable_signature* sigin) { + secp256k1_scalar r, s; + int recid; + + (void)ctx; + ARG_CHECK(sig != NULL); + ARG_CHECK(sigin != NULL); + + secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, sigin); + secp256k1_ecdsa_signature_save(sig, &r, &s); + 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) { + unsigned char brx[32]; + secp256k1_fe fx; + secp256k1_ge x; + secp256k1_gej xj; + secp256k1_scalar rn, u1, u2; + secp256k1_gej qj; + int r; + + if (secp256k1_scalar_is_zero(sigr) || secp256k1_scalar_is_zero(sigs)) { + return 0; + } + + secp256k1_scalar_get_b32(brx, sigr); + r = secp256k1_fe_set_b32(&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) { + if (secp256k1_fe_cmp_var(&fx, &secp256k1_ecdsa_const_p_minus_order) >= 0) { + return 0; + } + secp256k1_fe_add(&fx, &secp256k1_ecdsa_const_order_as_fe); + } + if (!secp256k1_ge_set_xo_var(&x, &fx, recid & 1)) { + return 0; + } + secp256k1_gej_set_ge(&xj, &x); + secp256k1_scalar_inverse_var(&rn, sigr); + 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_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) { + secp256k1_scalar r, s; + secp256k1_scalar sec, non, msg; + int recid; + 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); + 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)); + } + return ret; +} + +int secp256k1_ecdsa_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msg32) { + 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(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_pubkey_save(pubkey, &q); + return 1; + } else { + memset(pubkey, 0, sizeof(*pubkey)); + return 0; + } +} + +#endif /* SECP256K1_MODULE_RECOVERY_MAIN_H */ diff --git a/src/secp256k1/src/modules/recovery/tests_impl.h b/src/secp256k1/src/modules/recovery/tests_impl.h new file mode 100644 index 000000000..5c9bbe861 --- /dev/null +++ b/src/secp256k1/src/modules/recovery/tests_impl.h @@ -0,0 +1,393 @@ +/********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_RECOVERY_TESTS_H +#define SECP256K1_MODULE_RECOVERY_TESTS_H + +static int recovery_test_nonce_function(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + (void) msg32; + (void) key32; + (void) algo16; + (void) data; + + /* On the first run, return 0 to force a second run */ + if (counter == 0) { + memset(nonce32, 0, 32); + return 1; + } + /* On the second run, return an overflow to force a third run */ + if (counter == 1) { + memset(nonce32, 0xff, 32); + return 1; + } + /* 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); +} + +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 }; + unsigned char over_privkey[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 }; + + 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 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); + /* 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); + /* 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); + /* This one will succeed. */ + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, NULL, NULL) == 1); + CHECK(ecount == 5); + + /* 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 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 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_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 */ + 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); +} + +void test_ecdsa_recovery_end_to_end(void) { + unsigned char extra[32] = {0x00}; + unsigned char privkey[32]; + unsigned char message[32]; + secp256k1_ecdsa_signature signature[5]; + secp256k1_ecdsa_recoverable_signature rsignature[5]; + unsigned char sig[74]; + secp256k1_pubkey pubkey; + secp256k1_pubkey recpubkey; + int recid = 0; + + /* Generate a random key and message. */ + { + secp256k1_scalar msg, key; + random_scalar_order_test(&msg); + 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); + + /* 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); + extra[31] = 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); + 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); + /* 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); + /* 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); + /* Recover again */ + CHECK(secp256k1_ecdsa_recover(ctx, &recpubkey, &rsignature[4], message) == 0 || + memcmp(&pubkey, &recpubkey, sizeof(pubkey)) != 0); +} + +/* Tests several edge cases. */ +void test_ecdsa_recovery_edge_cases(void) { + const unsigned char msg32[32] = { + 'T', 'h', 'i', 's', ' ', 'i', 's', ' ', + 'a', ' ', 'v', 'e', 'r', 'y', ' ', 's', + 'e', 'c', 'r', 'e', 't', ' ', 'm', 'e', + 's', 's', 'a', 'g', 'e', '.', '.', '.' + }; + 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. */ + 0x67, 0xCB, 0x28, 0x5F, 0x9C, 0xD1, 0x94, 0xE8, + 0x40, 0xD6, 0x29, 0x39, 0x7A, 0xF5, 0x56, 0x96, + 0x62, 0xFD, 0xE4, 0x46, 0x49, 0x99, 0x59, 0x63, + 0x17, 0x9A, 0x7D, 0xD1, 0x7B, 0xD2, 0x35, 0x32, + 0x4B, 0x1B, 0x7D, 0xF3, 0x4C, 0xE1, 0xF6, 0x8E, + 0x69, 0x4F, 0xF6, 0xF1, 0x1A, 0xC7, 0x51, 0xDD, + 0x7D, 0xD7, 0x3E, 0x38, 0x7E, 0xE4, 0xFC, 0x86, + 0x6E, 0x1B, 0xE8, 0xEC, 0xC7, 0xDD, 0x95, 0x57 + }; + secp256k1_pubkey pubkey; + /* signature (r,s) = (4,4), which can be recovered with all 4 recids. */ + const unsigned char sigb64[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, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + }; + secp256k1_pubkey pubkeyb; + secp256k1_ecdsa_recoverable_signature rsig; + 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)); + + for (recid = 0; recid < 4; recid++) { + int i; + int recid2; + /* (4,4) encoded in DER. */ + unsigned char sigbder[8] = {0x30, 0x06, 0x02, 0x01, 0x04, 0x02, 0x01, 0x04}; + unsigned char sigcder_zr[7] = {0x30, 0x05, 0x02, 0x00, 0x02, 0x01, 0x01}; + unsigned char sigcder_zs[7] = {0x30, 0x05, 0x02, 0x01, 0x01, 0x02, 0x00}; + unsigned char sigbderalt1[39] = { + 0x30, 0x25, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x02, 0x01, 0x04, + }; + unsigned char sigbderalt2[39] = { + 0x30, 0x25, 0x02, 0x01, 0x04, 0x02, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + }; + unsigned char sigbderalt3[40] = { + 0x30, 0x26, 0x02, 0x21, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x01, 0x04, + }; + unsigned char sigbderalt4[40] = { + 0x30, 0x26, 0x02, 0x01, 0x04, 0x02, 0x21, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + }; + /* (order + r,4) encoded in DER. */ + unsigned char sigbderlong[40] = { + 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, 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); + 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); + /* 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); + } + /* 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); + /* 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); + sigbderalt3[4] = 1; + 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); + /* 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); + 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); + for(i = 0; i < 8; i++) { + int c; + unsigned char orig = sigbder[i]; + /*Try every single-byte change.*/ + for (c = 0; c < 256; c++) { + if (c == orig ) { + continue; + } + sigbder[i] = c; + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder)) == 0 || secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + } + sigbder[i] = orig; + } + } + + /* Test r/s equal to zero */ + { + /* (1,1) encoded in DER. */ + unsigned char sigcder[8] = {0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01}; + unsigned char sigc64[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, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, + }; + 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); + 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); + 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); + } +} + +void run_recovery_tests(void) { + int i; + for (i = 0; i < count; i++) { + test_ecdsa_recovery_api(); + } + for (i = 0; i < 64*count; i++) { + test_ecdsa_recovery_end_to_end(); + } + test_ecdsa_recovery_edge_cases(); +} + +#endif /* SECP256K1_MODULE_RECOVERY_TESTS_H */ diff --git a/src/secp256k1/src/num.h b/src/secp256k1/src/num.h index 339b6bb6e..49f2dd791 100644 --- a/src/secp256k1/src/num.h +++ b/src/secp256k1/src/num.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_NUM_ -#define _SECP256K1_NUM_ +#ifndef SECP256K1_NUM_H +#define SECP256K1_NUM_H #ifndef USE_NUM_NONE @@ -20,49 +20,55 @@ #endif /** Copy a number. */ -static void secp256k1_num_copy(secp256k1_num_t *r, const secp256k1_num_t *a); +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_t *a); +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_t *r, const unsigned char *a, unsigned int alen); +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_t *r, const secp256k1_num_t *a, const secp256k1_num_t *m); +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_t *a, const secp256k1_num_t *b); +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_t *a, const secp256k1_num_t *b); +static int secp256k1_num_eq(const secp256k1_num *a, const secp256k1_num *b); /** Add two (signed) numbers. */ -static void secp256k1_num_add(secp256k1_num_t *r, const secp256k1_num_t *a, const secp256k1_num_t *b); +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_t *r, const secp256k1_num_t *a, const secp256k1_num_t *b); +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_t *r, const secp256k1_num_t *a, const secp256k1_num_t *b); +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_t *r, const secp256k1_num_t *m); +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_t *r, int 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_t *a); +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_t *a); +static int secp256k1_num_is_neg(const secp256k1_num *a); /** Change a number's sign. */ -static void secp256k1_num_negate(secp256k1_num_t *r); +static void secp256k1_num_negate(secp256k1_num *r); #endif -#endif +#endif /* SECP256K1_NUM_H */ diff --git a/src/secp256k1/src/num_gmp.h b/src/secp256k1/src/num_gmp.h index baa1f2bf2..3619844bd 100644 --- a/src/secp256k1/src/num_gmp.h +++ b/src/secp256k1/src/num_gmp.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_NUM_REPR_ -#define _SECP256K1_NUM_REPR_ +#ifndef SECP256K1_NUM_REPR_H +#define SECP256K1_NUM_REPR_H #include @@ -15,6 +15,6 @@ typedef struct { mp_limb_t data[2*NUM_LIMBS]; int neg; int limbs; -} secp256k1_num_t; +} secp256k1_num; -#endif +#endif /* SECP256K1_NUM_REPR_H */ diff --git a/src/secp256k1/src/num_gmp_impl.h b/src/secp256k1/src/num_gmp_impl.h index dbbc458d5..0ae2a8ba0 100644 --- a/src/secp256k1/src/num_gmp_impl.h +++ b/src/secp256k1/src/num_gmp_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_NUM_REPR_IMPL_H_ -#define _SECP256K1_NUM_REPR_IMPL_H_ +#ifndef SECP256K1_NUM_REPR_IMPL_H +#define SECP256K1_NUM_REPR_IMPL_H #include #include @@ -15,18 +15,18 @@ #include "num.h" #ifdef VERIFY -static void secp256k1_num_sanity(const secp256k1_num_t *a) { +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_t *r, const secp256k1_num_t *a) { +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_t *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; @@ -42,7 +42,7 @@ static void secp256k1_num_get_bin(unsigned char *r, unsigned int rlen, const sec memset(tmp, 0, sizeof(tmp)); } -static void secp256k1_num_set_bin(secp256k1_num_t *r, const unsigned char *a, unsigned int alen) { +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); @@ -59,7 +59,7 @@ static void secp256k1_num_set_bin(secp256k1_num_t *r, const unsigned char *a, un } } -static void secp256k1_num_add_abs(secp256k1_num_t *r, const secp256k1_num_t *a, const secp256k1_num_t *b) { +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) { @@ -68,8 +68,9 @@ static void secp256k1_num_add_abs(secp256k1_num_t *r, const secp256k1_num_t *a, } } -static void secp256k1_num_sub_abs(secp256k1_num_t *r, const secp256k1_num_t *a, const secp256k1_num_t *b) { +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) { @@ -77,7 +78,7 @@ static void secp256k1_num_sub_abs(secp256k1_num_t *r, const secp256k1_num_t *a, } } -static void secp256k1_num_mod(secp256k1_num_t *r, const secp256k1_num_t *m) { +static void secp256k1_num_mod(secp256k1_num *r, const secp256k1_num *m) { secp256k1_num_sanity(r); secp256k1_num_sanity(m); @@ -97,7 +98,7 @@ static void secp256k1_num_mod(secp256k1_num_t *r, const secp256k1_num_t *m) { } } -static void secp256k1_num_mod_inverse(secp256k1_num_t *r, const secp256k1_num_t *a, const secp256k1_num_t *m) { +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]; @@ -125,6 +126,7 @@ static void secp256k1_num_mod_inverse(secp256k1_num_t *r, const secp256k1_num_t } 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; @@ -142,15 +144,41 @@ static void secp256k1_num_mod_inverse(secp256k1_num_t *r, const secp256k1_num_t memset(v, 0, sizeof(v)); } -static int secp256k1_num_is_zero(const secp256k1_num_t *a) { +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_t *a) { +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_t *a, const secp256k1_num_t *b) { +static int secp256k1_num_cmp(const secp256k1_num *a, const secp256k1_num *b) { if (a->limbs > b->limbs) { return 1; } @@ -160,7 +188,7 @@ static int secp256k1_num_cmp(const secp256k1_num_t *a, const secp256k1_num_t *b) return mpn_cmp(a->data, b->data, a->limbs); } -static int secp256k1_num_eq(const secp256k1_num_t *a, const secp256k1_num_t *b) { +static int secp256k1_num_eq(const secp256k1_num *a, const secp256k1_num *b) { if (a->limbs > b->limbs) { return 0; } @@ -173,7 +201,7 @@ static int secp256k1_num_eq(const secp256k1_num_t *a, const secp256k1_num_t *b) return mpn_cmp(a->data, b->data, a->limbs) == 0; } -static void secp256k1_num_subadd(secp256k1_num_t *r, const secp256k1_num_t *a, const secp256k1_num_t *b, int bneg) { +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) { @@ -192,19 +220,19 @@ static void secp256k1_num_subadd(secp256k1_num_t *r, const secp256k1_num_t *a, c } } -static void secp256k1_num_add(secp256k1_num_t *r, const secp256k1_num_t *a, const secp256k1_num_t *b) { +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_t *r, const secp256k1_num_t *a, const secp256k1_num_t *b) { +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_t *r, const secp256k1_num_t *a, const secp256k1_num_t *b) { +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); @@ -231,13 +259,13 @@ static void secp256k1_num_mul(secp256k1_num_t *r, const secp256k1_num_t *a, cons memset(tmp, 0, sizeof(tmp)); } -static void secp256k1_num_shift(secp256k1_num_t *r, int bits) { - int i; +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); @@ -253,8 +281,8 @@ static void secp256k1_num_shift(secp256k1_num_t *r, int bits) { } } -static void secp256k1_num_negate(secp256k1_num_t *r) { +static void secp256k1_num_negate(secp256k1_num *r) { r->neg ^= 1; } -#endif +#endif /* SECP256K1_NUM_REPR_IMPL_H */ diff --git a/src/secp256k1/src/num_impl.h b/src/secp256k1/src/num_impl.h index 0b0e3a072..c45193b03 100644 --- a/src/secp256k1/src/num_impl.h +++ b/src/secp256k1/src/num_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_NUM_IMPL_H_ -#define _SECP256K1_NUM_IMPL_H_ +#ifndef SECP256K1_NUM_IMPL_H +#define SECP256K1_NUM_IMPL_H #if defined HAVE_CONFIG_H #include "libsecp256k1-config.h" @@ -21,4 +21,4 @@ #error "Please select num implementation" #endif -#endif +#endif /* SECP256K1_NUM_IMPL_H */ diff --git a/src/secp256k1/src/scalar.h b/src/secp256k1/src/scalar.h index f5d09f8d4..59304cb66 100644 --- a/src/secp256k1/src/scalar.h +++ b/src/secp256k1/src/scalar.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_ -#define _SECP256K1_SCALAR_ +#ifndef SECP256K1_SCALAR_H +#define SECP256K1_SCALAR_H #include "num.h" @@ -13,7 +13,9 @@ #include "libsecp256k1-config.h" #endif -#if defined(USE_SCALAR_4X64) +#if defined(EXHAUSTIVE_TEST_ORDER) +#include "scalar_low.h" +#elif defined(USE_SCALAR_4X64) #include "scalar_4x64.h" #elif defined(USE_SCALAR_8X32) #include "scalar_8x32.h" @@ -22,72 +24,83 @@ #endif /** Clear a scalar to prevent the leak of sensitive data. */ -static void secp256k1_scalar_clear(secp256k1_scalar_t *r); +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_t *a, unsigned int offset, unsigned int count); +static unsigned int secp256k1_scalar_get_bits(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_t *a, unsigned int offset, unsigned int count); +static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count); /** Set a scalar from a big endian byte array. */ -static void secp256k1_scalar_set_b32(secp256k1_scalar_t *r, const unsigned char *bin, int *overflow); +static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *bin, int *overflow); /** Set a scalar to an unsigned integer. */ -static void secp256k1_scalar_set_int(secp256k1_scalar_t *r, unsigned int v); +static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v); /** Convert a scalar to a byte array. */ -static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar_t* a); +static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a); /** Add two scalars together (modulo the group order). Returns whether it overflowed. */ -static int secp256k1_scalar_add(secp256k1_scalar_t *r, const secp256k1_scalar_t *a, const secp256k1_scalar_t *b); +static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b); -/** Add a power of two to a scalar. The result is not allowed to overflow. */ -static void secp256k1_scalar_add_bit(secp256k1_scalar_t *r, unsigned int bit); +/** Conditionally add a power of two to a scalar. The result is not allowed to overflow. */ +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag); /** Multiply two scalars (modulo the group order). */ -static void secp256k1_scalar_mul(secp256k1_scalar_t *r, const secp256k1_scalar_t *a, const secp256k1_scalar_t *b); +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_t *r, const secp256k1_scalar_t *a); +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_t *r, const secp256k1_scalar_t *a); +static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *a); /** Compute the inverse of a scalar (modulo the group order), without constant-time guarantee. */ -static void secp256k1_scalar_inverse_var(secp256k1_scalar_t *r, const secp256k1_scalar_t *a); +static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *a); /** Compute the complement of a scalar (modulo the group order). */ -static void secp256k1_scalar_negate(secp256k1_scalar_t *r, const secp256k1_scalar_t *a); +static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a); /** Check whether a scalar equals zero. */ -static int secp256k1_scalar_is_zero(const secp256k1_scalar_t *a); +static int secp256k1_scalar_is_zero(const secp256k1_scalar *a); /** Check whether a scalar equals one. */ -static int secp256k1_scalar_is_one(const secp256k1_scalar_t *a); +static int secp256k1_scalar_is_one(const secp256k1_scalar *a); + +/** Check whether a scalar, considered as an nonnegative integer, is even. */ +static int secp256k1_scalar_is_even(const secp256k1_scalar *a); /** Check whether a scalar is higher than the group order divided by 2. */ -static int secp256k1_scalar_is_high(const secp256k1_scalar_t *a); +static int secp256k1_scalar_is_high(const secp256k1_scalar *a); + +/** Conditionally negate a number, in constant time. + * 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_t *r, const secp256k1_scalar_t *a); +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_t *r); +static void secp256k1_scalar_order_get_num(secp256k1_num *r); #endif /** Compare two scalars. */ -static int secp256k1_scalar_eq(const secp256k1_scalar_t *a, const secp256k1_scalar_t *b); +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_t *r1, secp256k1_scalar_t *r2, const secp256k1_scalar_t *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_var(secp256k1_scalar_t *r1, secp256k1_scalar_t *r2, const secp256k1_scalar_t *a); +static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a); #endif /** 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_t *r, const secp256k1_scalar_t *a, const secp256k1_scalar_t *b, unsigned int shift); +static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift); -#endif +#endif /* SECP256K1_SCALAR_H */ diff --git a/src/secp256k1/src/scalar_4x64.h b/src/secp256k1/src/scalar_4x64.h index 82899aa7b..19c7495d1 100644 --- a/src/secp256k1/src/scalar_4x64.h +++ b/src/secp256k1/src/scalar_4x64.h @@ -4,16 +4,16 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_REPR_ -#define _SECP256K1_SCALAR_REPR_ +#ifndef SECP256K1_SCALAR_REPR_H +#define SECP256K1_SCALAR_REPR_H #include /** A scalar modulo the group order of the secp256k1 curve. */ typedef struct { uint64_t d[4]; -} secp256k1_scalar_t; +} secp256k1_scalar; #define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{((uint64_t)(d1)) << 32 | (d0), ((uint64_t)(d3)) << 32 | (d2), ((uint64_t)(d5)) << 32 | (d4), ((uint64_t)(d7)) << 32 | (d6)}} -#endif +#endif /* SECP256K1_SCALAR_REPR_H */ diff --git a/src/secp256k1/src/scalar_4x64_impl.h b/src/secp256k1/src/scalar_4x64_impl.h index ff365292f..db1ebf94b 100644 --- a/src/secp256k1/src/scalar_4x64_impl.h +++ b/src/secp256k1/src/scalar_4x64_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_ -#define _SECP256K1_SCALAR_REPR_IMPL_H_ +#ifndef SECP256K1_SCALAR_REPR_IMPL_H +#define SECP256K1_SCALAR_REPR_IMPL_H /* Limbs of the secp256k1 order. */ #define SECP256K1_N_0 ((uint64_t)0xBFD25E8CD0364141ULL) @@ -24,26 +24,26 @@ #define SECP256K1_N_H_2 ((uint64_t)0xFFFFFFFFFFFFFFFFULL) #define SECP256K1_N_H_3 ((uint64_t)0x7FFFFFFFFFFFFFFFULL) -SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar_t *r) { +SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { r->d[0] = 0; r->d[1] = 0; r->d[2] = 0; r->d[3] = 0; } -SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar_t *r, unsigned int v) { +SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { r->d[0] = v; r->d[1] = 0; r->d[2] = 0; r->d[3] = 0; } -SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar_t *a, unsigned int offset, unsigned int count) { +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { VERIFY_CHECK((offset + count - 1) >> 6 == offset >> 6); return (a->d[offset >> 6] >> (offset & 0x3F)) & ((((uint64_t)1) << count) - 1); } -SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar_t *a, unsigned int offset, unsigned int 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); VERIFY_CHECK(offset + count <= 256); if ((offset + count - 1) >> 6 == offset >> 6) { @@ -54,7 +54,7 @@ SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256 } } -SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar_t *a) { +SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { int yes = 0; int no = 0; no |= (a->d[3] < SECP256K1_N_3); /* No need for a > check. */ @@ -66,7 +66,7 @@ SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scal return yes; } -SECP256K1_INLINE static int secp256k1_scalar_reduce(secp256k1_scalar_t *r, unsigned int overflow) { +SECP256K1_INLINE static int secp256k1_scalar_reduce(secp256k1_scalar *r, unsigned int overflow) { uint128_t t; VERIFY_CHECK(overflow <= 1); t = (uint128_t)r->d[0] + overflow * SECP256K1_N_C_0; @@ -80,7 +80,7 @@ SECP256K1_INLINE static int secp256k1_scalar_reduce(secp256k1_scalar_t *r, unsig return overflow; } -static int secp256k1_scalar_add(secp256k1_scalar_t *r, const secp256k1_scalar_t *a, const secp256k1_scalar_t *b) { +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; @@ -96,9 +96,10 @@ static int secp256k1_scalar_add(secp256k1_scalar_t *r, const secp256k1_scalar_t return overflow; } -static void secp256k1_scalar_add_bit(secp256k1_scalar_t *r, unsigned int bit) { +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { uint128_t t; 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)); @@ -113,7 +114,7 @@ static void secp256k1_scalar_add_bit(secp256k1_scalar_t *r, unsigned int bit) { #endif } -static void secp256k1_scalar_set_b32(secp256k1_scalar_t *r, const unsigned char *b32, int *overflow) { +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; @@ -125,18 +126,18 @@ static void secp256k1_scalar_set_b32(secp256k1_scalar_t *r, const unsigned char } } -static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar_t* a) { +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_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar_t *a) { +SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) { return (a->d[0] | a->d[1] | a->d[2] | a->d[3]) == 0; } -static void secp256k1_scalar_negate(secp256k1_scalar_t *r, const secp256k1_scalar_t *a) { +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; @@ -148,11 +149,11 @@ static void secp256k1_scalar_negate(secp256k1_scalar_t *r, const secp256k1_scala r->d[3] = t & nonzero; } -SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar_t *a) { +SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *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_t *a) { +static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { int yes = 0; int no = 0; no |= (a->d[3] < SECP256K1_N_H_3); @@ -164,6 +165,22 @@ static int secp256k1_scalar_is_high(const secp256k1_scalar_t *a) { return yes; } +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; + 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; + return 2 * (mask == 0) - 1; +} + /* Inspired by the macros in OpenSSL's crypto/bn/asm/x86_64-gcc.c. */ /** Add a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ @@ -250,7 +267,7 @@ static int secp256k1_scalar_is_high(const secp256k1_scalar_t *a) { VERIFY_CHECK(c2 == 0); \ } -static void secp256k1_scalar_reduce_512(secp256k1_scalar_t *r, const uint64_t *l) { +static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l) { #ifdef USE_ASM_X86_64 /* Reduce 512 bits into 385. */ uint64_t m0, m1, m2, m3, m4, m5, m6; @@ -265,8 +282,8 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar_t *r, const uint64_t *l "movq 56(%%rsi), %%r14\n" /* Initialize r8,r9,r10 */ "movq 0(%%rsi), %%r8\n" - "movq $0, %%r9\n" - "movq $0, %%r10\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" /* (r8,r9) += n0 * c0 */ "movq %8, %%rax\n" "mulq %%r11\n" @@ -274,7 +291,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar_t *r, const uint64_t *l "adcq %%rdx, %%r9\n" /* extract m0 */ "movq %%r8, %q0\n" - "movq $0, %%r8\n" + "xorq %%r8, %%r8\n" /* (r9,r10) += l1 */ "addq 8(%%rsi), %%r9\n" "adcq $0, %%r10\n" @@ -292,7 +309,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar_t *r, const uint64_t *l "adcq $0, %%r8\n" /* extract m1 */ "movq %%r9, %q1\n" - "movq $0, %%r9\n" + "xorq %%r9, %%r9\n" /* (r10,r8,r9) += l2 */ "addq 16(%%rsi), %%r10\n" "adcq $0, %%r8\n" @@ -315,7 +332,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar_t *r, const uint64_t *l "adcq $0, %%r9\n" /* extract m2 */ "movq %%r10, %q2\n" - "movq $0, %%r10\n" + "xorq %%r10, %%r10\n" /* (r8,r9,r10) += l3 */ "addq 24(%%rsi), %%r8\n" "adcq $0, %%r9\n" @@ -338,7 +355,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar_t *r, const uint64_t *l "adcq $0, %%r10\n" /* extract m3 */ "movq %%r8, %q3\n" - "movq $0, %%r8\n" + "xorq %%r8, %%r8\n" /* (r9,r10,r8) += n3 * c1 */ "movq %9, %%rax\n" "mulq %%r14\n" @@ -370,8 +387,8 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar_t *r, const uint64_t *l "movq %q11, %%r13\n" /* Initialize (r8,r9,r10) */ "movq %q5, %%r8\n" - "movq $0, %%r9\n" - "movq $0, %%r10\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" /* (r8,r9) += m4 * c0 */ "movq %12, %%rax\n" "mulq %%r11\n" @@ -379,7 +396,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar_t *r, const uint64_t *l "adcq %%rdx, %%r9\n" /* extract p0 */ "movq %%r8, %q0\n" - "movq $0, %%r8\n" + "xorq %%r8, %%r8\n" /* (r9,r10) += m1 */ "addq %q6, %%r9\n" "adcq $0, %%r10\n" @@ -397,7 +414,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar_t *r, const uint64_t *l "adcq $0, %%r8\n" /* extract p1 */ "movq %%r9, %q1\n" - "movq $0, %%r9\n" + "xorq %%r9, %%r9\n" /* (r10,r8,r9) += m2 */ "addq %q7, %%r10\n" "adcq $0, %%r8\n" @@ -455,7 +472,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar_t *r, const uint64_t *l "movq %%rax, 0(%q6)\n" /* Move to (r8,r9) */ "movq %%rdx, %%r8\n" - "movq $0, %%r9\n" + "xorq %%r9, %%r9\n" /* (r8,r9) += p1 */ "addq %q2, %%r8\n" "adcq $0, %%r9\n" @@ -466,7 +483,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar_t *r, const uint64_t *l "adcq %%rdx, %%r9\n" /* Extract r1 */ "movq %%r8, 8(%q6)\n" - "movq $0, %%r8\n" + "xorq %%r8, %%r8\n" /* (r9,r8) += p4 */ "addq %%r10, %%r9\n" "adcq $0, %%r8\n" @@ -475,7 +492,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar_t *r, const uint64_t *l "adcq $0, %%r8\n" /* Extract r2 */ "movq %%r9, 16(%q6)\n" - "movq $0, %%r9\n" + "xorq %%r9, %%r9\n" /* (r8,r9) += p3 */ "addq %q4, %%r8\n" "adcq $0, %%r9\n" @@ -559,7 +576,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar_t *r, const uint64_t *l secp256k1_scalar_reduce(r, c + secp256k1_scalar_check_overflow(r)); } -static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar_t *a, const secp256k1_scalar_t *b) { +static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, const secp256k1_scalar *b) { #ifdef USE_ASM_X86_64 const uint64_t *pb = b->d; __asm__ __volatile__( @@ -721,12 +738,12 @@ static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar_t *a, extract(l[5]); muladd_fast(a->d[3], b->d[3]); extract_fast(l[6]); - VERIFY_CHECK(c1 <= 0); + VERIFY_CHECK(c1 == 0); l[7] = c0; #endif } -static void secp256k1_scalar_sqr_512(uint64_t l[8], const secp256k1_scalar_t *a) { +static void secp256k1_scalar_sqr_512(uint64_t l[8], const secp256k1_scalar *a) { #ifdef USE_ASM_X86_64 __asm__ __volatile__( /* Preload */ @@ -871,19 +888,32 @@ static void secp256k1_scalar_sqr_512(uint64_t l[8], const secp256k1_scalar_t *a) #undef extract #undef extract_fast -static void secp256k1_scalar_mul(secp256k1_scalar_t *r, const secp256k1_scalar_t *a, const secp256k1_scalar_t *b) { +static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { uint64_t l[8]; secp256k1_scalar_mul_512(l, a, b); secp256k1_scalar_reduce_512(r, l); } -static void secp256k1_scalar_sqr(secp256k1_scalar_t *r, const secp256k1_scalar_t *a) { +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; +} + +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_t *r1, secp256k1_scalar_t *r2, const secp256k1_scalar_t *a) { +#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] = 0; @@ -893,12 +923,13 @@ static void secp256k1_scalar_split_128(secp256k1_scalar_t *r1, secp256k1_scalar_ r2->d[2] = 0; r2->d[3] = 0; } +#endif -SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar_t *a, const secp256k1_scalar_t *b) { +SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *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; } -SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar_t *r, const secp256k1_scalar_t *a, const secp256k1_scalar_t *b, unsigned int shift) { +SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift) { uint64_t l[8]; unsigned int shiftlimbs; unsigned int shiftlow; @@ -912,9 +943,7 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar_t * r->d[1] = shift < 448 ? (l[1 + shiftlimbs] >> shiftlow | (shift < 384 && shiftlow ? (l[2 + shiftlimbs] << shifthigh) : 0)) : 0; 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; - if ((l[(shift - 1) >> 6] >> ((shift - 1) & 0x3f)) & 1) { - secp256k1_scalar_add_bit(r, 0); - } + secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 6] >> ((shift - 1) & 0x3f)) & 1); } -#endif +#endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/secp256k1/src/scalar_8x32.h b/src/secp256k1/src/scalar_8x32.h index f17017e24..2c9a348e2 100644 --- a/src/secp256k1/src/scalar_8x32.h +++ b/src/secp256k1/src/scalar_8x32.h @@ -4,16 +4,16 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_REPR_ -#define _SECP256K1_SCALAR_REPR_ +#ifndef SECP256K1_SCALAR_REPR_H +#define SECP256K1_SCALAR_REPR_H #include /** A scalar modulo the group order of the secp256k1 curve. */ typedef struct { uint32_t d[8]; -} secp256k1_scalar_t; +} secp256k1_scalar; #define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{(d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7)}} -#endif +#endif /* SECP256K1_SCALAR_REPR_H */ diff --git a/src/secp256k1/src/scalar_8x32_impl.h b/src/secp256k1/src/scalar_8x32_impl.h index 22b31d411..4f9ed61fe 100644 --- a/src/secp256k1/src/scalar_8x32_impl.h +++ b/src/secp256k1/src/scalar_8x32_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_ -#define _SECP256K1_SCALAR_REPR_IMPL_H_ +#ifndef SECP256K1_SCALAR_REPR_IMPL_H +#define SECP256K1_SCALAR_REPR_IMPL_H /* Limbs of the secp256k1 order. */ #define SECP256K1_N_0 ((uint32_t)0xD0364141UL) @@ -34,7 +34,7 @@ #define SECP256K1_N_H_6 ((uint32_t)0xFFFFFFFFUL) #define SECP256K1_N_H_7 ((uint32_t)0x7FFFFFFFUL) -SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar_t *r) { +SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { r->d[0] = 0; r->d[1] = 0; r->d[2] = 0; @@ -45,7 +45,7 @@ SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar_t *r) { r->d[7] = 0; } -SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar_t *r, unsigned int v) { +SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { r->d[0] = v; r->d[1] = 0; r->d[2] = 0; @@ -56,12 +56,12 @@ SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar_t *r, uns r->d[7] = 0; } -SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar_t *a, unsigned int offset, unsigned int count) { +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { VERIFY_CHECK((offset + count - 1) >> 5 == offset >> 5); return (a->d[offset >> 5] >> (offset & 0x1F)) & ((1 << count) - 1); } -SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar_t *a, unsigned int offset, unsigned int 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); VERIFY_CHECK(offset + count <= 256); if ((offset + count - 1) >> 5 == offset >> 5) { @@ -72,7 +72,7 @@ SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256 } } -SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar_t *a) { +SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { int yes = 0; int no = 0; no |= (a->d[7] < SECP256K1_N_7); /* No need for a > check. */ @@ -90,7 +90,7 @@ SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scal return yes; } -SECP256K1_INLINE static int secp256k1_scalar_reduce(secp256k1_scalar_t *r, uint32_t overflow) { +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; @@ -112,7 +112,7 @@ SECP256K1_INLINE static int secp256k1_scalar_reduce(secp256k1_scalar_t *r, uint3 return overflow; } -static int secp256k1_scalar_add(secp256k1_scalar_t *r, const secp256k1_scalar_t *a, const secp256k1_scalar_t *b) { +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]; r->d[0] = t & 0xFFFFFFFFULL; t >>= 32; @@ -136,9 +136,10 @@ static int secp256k1_scalar_add(secp256k1_scalar_t *r, const secp256k1_scalar_t return overflow; } -static void secp256k1_scalar_add_bit(secp256k1_scalar_t *r, unsigned int bit) { +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { uint64_t t; VERIFY_CHECK(bit < 256); + bit += ((uint32_t) flag - 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)); @@ -161,7 +162,7 @@ static void secp256k1_scalar_add_bit(secp256k1_scalar_t *r, unsigned int bit) { #endif } -static void secp256k1_scalar_set_b32(secp256k1_scalar_t *r, const unsigned char *b32, int *overflow) { +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; @@ -177,7 +178,7 @@ static void secp256k1_scalar_set_b32(secp256k1_scalar_t *r, const unsigned char } } -static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar_t* a) { +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]; @@ -188,11 +189,11 @@ static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar_ bin[28] = a->d[0] >> 24; bin[29] = a->d[0] >> 16; bin[30] = a->d[0] >> 8; bin[31] = a->d[0]; } -SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar_t *a) { +SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *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_t *r, const secp256k1_scalar_t *a) { +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; r->d[0] = t & nonzero; t >>= 32; @@ -212,11 +213,11 @@ static void secp256k1_scalar_negate(secp256k1_scalar_t *r, const secp256k1_scala r->d[7] = t & nonzero; } -SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar_t *a) { +SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *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_t *a) { +static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { int yes = 0; int no = 0; no |= (a->d[7] < SECP256K1_N_H_7); @@ -234,6 +235,31 @@ static int secp256k1_scalar_is_high(const secp256k1_scalar_t *a) { return yes; } +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; + uint32_t nonzero = 0xFFFFFFFFUL * (secp256k1_scalar_is_zero(r) == 0); + uint64_t t = (uint64_t)(r->d[0] ^ mask) + ((SECP256K1_N_0 + 1) & mask); + 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; + t += (uint64_t)(r->d[2] ^ mask) + (SECP256K1_N_2 & mask); + r->d[2] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[3] ^ mask) + (SECP256K1_N_3 & mask); + r->d[3] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[4] ^ mask) + (SECP256K1_N_4 & mask); + r->d[4] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[5] ^ mask) + (SECP256K1_N_5 & mask); + r->d[5] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[6] ^ mask) + (SECP256K1_N_6 & mask); + r->d[6] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[7] ^ mask) + (SECP256K1_N_7 & mask); + r->d[7] = t & nonzero; + return 2 * (mask == 0) - 1; +} + + /* Inspired by the macros in OpenSSL's crypto/bn/asm/x86_64-gcc.c. */ /** Add a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ @@ -320,7 +346,7 @@ static int secp256k1_scalar_is_high(const secp256k1_scalar_t *a) { VERIFY_CHECK(c2 == 0); \ } -static void secp256k1_scalar_reduce_512(secp256k1_scalar_t *r, const uint32_t *l) { +static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint32_t *l) { uint64_t c; uint32_t n0 = l[8], n1 = l[9], n2 = l[10], n3 = l[11], n4 = l[12], n5 = l[13], n6 = l[14], n7 = l[15]; uint32_t m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12; @@ -462,7 +488,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar_t *r, const uint32_t *l secp256k1_scalar_reduce(r, c + secp256k1_scalar_check_overflow(r)); } -static void secp256k1_scalar_mul_512(uint32_t *l, const secp256k1_scalar_t *a, const secp256k1_scalar_t *b) { +static void secp256k1_scalar_mul_512(uint32_t *l, const secp256k1_scalar *a, const secp256k1_scalar *b) { /* 96 bit accumulator. */ uint32_t c0 = 0, c1 = 0, c2 = 0; @@ -550,7 +576,7 @@ static void secp256k1_scalar_mul_512(uint32_t *l, const secp256k1_scalar_t *a, c l[15] = c0; } -static void secp256k1_scalar_sqr_512(uint32_t *l, const secp256k1_scalar_t *a) { +static void secp256k1_scalar_sqr_512(uint32_t *l, const secp256k1_scalar *a) { /* 96 bit accumulator. */ uint32_t c0 = 0, c1 = 0, c2 = 0; @@ -618,20 +644,36 @@ static void secp256k1_scalar_sqr_512(uint32_t *l, const secp256k1_scalar_t *a) { #undef extract #undef extract_fast -static void secp256k1_scalar_mul(secp256k1_scalar_t *r, const secp256k1_scalar_t *a, const secp256k1_scalar_t *b) { +static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { uint32_t l[16]; secp256k1_scalar_mul_512(l, a, b); secp256k1_scalar_reduce_512(r, l); } -static void secp256k1_scalar_sqr(secp256k1_scalar_t *r, const secp256k1_scalar_t *a) { +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; +} + +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); } #ifdef USE_ENDOMORPHISM -static void secp256k1_scalar_split_128(secp256k1_scalar_t *r1, secp256k1_scalar_t *r2, const secp256k1_scalar_t *a) { +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]; @@ -651,11 +693,11 @@ static void secp256k1_scalar_split_128(secp256k1_scalar_t *r1, secp256k1_scalar_ } #endif -SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar_t *a, const secp256k1_scalar_t *b) { +SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *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; } -SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar_t *r, const secp256k1_scalar_t *a, const secp256k1_scalar_t *b, unsigned int shift) { +SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift) { uint32_t l[16]; unsigned int shiftlimbs; unsigned int shiftlow; @@ -673,9 +715,7 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar_t * r->d[5] = shift < 352 ? (l[5 + shiftlimbs] >> shiftlow | (shift < 320 && shiftlow ? (l[6 + shiftlimbs] << shifthigh) : 0)) : 0; 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; - if ((l[(shift - 1) >> 5] >> ((shift - 1) & 0x1f)) & 1) { - secp256k1_scalar_add_bit(r, 0); - } + secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 5] >> ((shift - 1) & 0x1f)) & 1); } -#endif +#endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/secp256k1/src/scalar_impl.h b/src/secp256k1/src/scalar_impl.h index 33824983e..fa790570f 100644 --- a/src/secp256k1/src/scalar_impl.h +++ b/src/secp256k1/src/scalar_impl.h @@ -4,10 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_IMPL_H_ -#define _SECP256K1_SCALAR_IMPL_H_ - -#include +#ifndef SECP256K1_SCALAR_IMPL_H +#define SECP256K1_SCALAR_IMPL_H #include "group.h" #include "scalar.h" @@ -16,7 +14,9 @@ #include "libsecp256k1-config.h" #endif -#if defined(USE_SCALAR_4X64) +#if defined(EXHAUSTIVE_TEST_ORDER) +#include "scalar_low_impl.h" +#elif defined(USE_SCALAR_4X64) #include "scalar_4x64_impl.h" #elif defined(USE_SCALAR_8X32) #include "scalar_8x32_impl.h" @@ -25,109 +25,120 @@ #endif #ifndef USE_NUM_NONE -static void secp256k1_scalar_get_num(secp256k1_num_t *r, const secp256k1_scalar_t *a) { +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_t *r) { +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_t *r, const secp256k1_scalar_t *x) { - secp256k1_scalar_t *t; +static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) { +#if defined(EXHAUSTIVE_TEST_ORDER) int i; - /* First compute x ^ (2^N - 1) for some values of N. */ - secp256k1_scalar_t x2, x3, x4, x6, x7, x8, x15, x30, x60, x120, x127; - - secp256k1_scalar_sqr(&x2, x); - secp256k1_scalar_mul(&x2, &x2, x); - - secp256k1_scalar_sqr(&x3, &x2); - secp256k1_scalar_mul(&x3, &x3, x); + *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(&x4, &x3); - secp256k1_scalar_mul(&x4, &x4, x); + 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, &x4); + secp256k1_scalar_sqr(&x6, &u13); secp256k1_scalar_sqr(&x6, &x6); - secp256k1_scalar_mul(&x6, &x6, &x2); - - secp256k1_scalar_sqr(&x7, &x6); - secp256k1_scalar_mul(&x7, &x7, x); + secp256k1_scalar_mul(&x6, &x6, &u11); - secp256k1_scalar_sqr(&x8, &x7); - secp256k1_scalar_mul(&x8, &x8, x); + secp256k1_scalar_sqr(&x8, &x6); + secp256k1_scalar_sqr(&x8, &x8); + secp256k1_scalar_mul(&x8, &x8, &x2); - secp256k1_scalar_sqr(&x15, &x8); - for (i = 0; i < 6; i++) { - secp256k1_scalar_sqr(&x15, &x15); + secp256k1_scalar_sqr(&x14, &x8); + for (i = 0; i < 5; i++) { + secp256k1_scalar_sqr(&x14, &x14); } - secp256k1_scalar_mul(&x15, &x15, &x7); + secp256k1_scalar_mul(&x14, &x14, &x6); - secp256k1_scalar_sqr(&x30, &x15); - for (i = 0; i < 14; i++) { - secp256k1_scalar_sqr(&x30, &x30); + secp256k1_scalar_sqr(&x28, &x14); + for (i = 0; i < 13; i++) { + secp256k1_scalar_sqr(&x28, &x28); } - secp256k1_scalar_mul(&x30, &x30, &x15); + secp256k1_scalar_mul(&x28, &x28, &x14); - secp256k1_scalar_sqr(&x60, &x30); - for (i = 0; i < 29; i++) { - secp256k1_scalar_sqr(&x60, &x60); + secp256k1_scalar_sqr(&x56, &x28); + for (i = 0; i < 27; i++) { + secp256k1_scalar_sqr(&x56, &x56); } - secp256k1_scalar_mul(&x60, &x60, &x30); + secp256k1_scalar_mul(&x56, &x56, &x28); - secp256k1_scalar_sqr(&x120, &x60); - for (i = 0; i < 59; i++) { - secp256k1_scalar_sqr(&x120, &x120); + secp256k1_scalar_sqr(&x112, &x56); + for (i = 0; i < 55; i++) { + secp256k1_scalar_sqr(&x112, &x112); } - secp256k1_scalar_mul(&x120, &x120, &x60); + secp256k1_scalar_mul(&x112, &x112, &x56); - secp256k1_scalar_sqr(&x127, &x120); - for (i = 0; i < 6; i++) { - secp256k1_scalar_sqr(&x127, &x127); + secp256k1_scalar_sqr(&x126, &x112); + for (i = 0; i < 13; i++) { + secp256k1_scalar_sqr(&x126, &x126); } - secp256k1_scalar_mul(&x127, &x127, &x7); + secp256k1_scalar_mul(&x126, &x126, &x14); - /* Then accumulate the final result (t starts at x127). */ - t = &x127; - for (i = 0; i < 2; i++) { /* 0 */ + /* 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, x); /* 1 */ + 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 < 2; i++) { /* 0 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, x); /* 1 */ - for (i = 0; i < 2; i++) { /* 0 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, x); /* 1 */ - for (i = 0; i < 2; i++) { /* 0 */ + for (i = 0; i < 4; i++) { /* 0 */ secp256k1_scalar_sqr(t, t); } - secp256k1_scalar_mul(t, t, x); /* 1 */ - for (i = 0; i < 4; i++) { /* 0 */ + secp256k1_scalar_mul(t, t, &u5); /* 101 */ + for (i = 0; i < 5; i++) { /* 0 */ secp256k1_scalar_sqr(t, t); } - secp256k1_scalar_mul(t, t, &x3); /* 111 */ - for (i = 0; i < 3; i++) { /* 0 */ + secp256k1_scalar_mul(t, t, &u11); /* 1011 */ + for (i = 0; i < 4; i++) { secp256k1_scalar_sqr(t, t); } - secp256k1_scalar_mul(t, t, &x2); /* 11 */ + secp256k1_scalar_mul(t, t, &u11); /* 1011 */ for (i = 0; i < 4; i++) { /* 0 */ secp256k1_scalar_sqr(t, t); } @@ -136,38 +147,26 @@ static void secp256k1_scalar_inverse(secp256k1_scalar_t *r, const secp256k1_scal secp256k1_scalar_sqr(t, t); } secp256k1_scalar_mul(t, t, &x3); /* 111 */ - for (i = 0; i < 4; i++) { /* 00 */ + for (i = 0; i < 6; i++) { /* 00 */ secp256k1_scalar_sqr(t, t); } - secp256k1_scalar_mul(t, t, &x2); /* 11 */ - for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_mul(t, t, &u13); /* 1101 */ + for (i = 0; i < 4; i++) { /* 0 */ secp256k1_scalar_sqr(t, t); } - secp256k1_scalar_mul(t, t, x); /* 1 */ - for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_mul(t, t, &u5); /* 101 */ + for (i = 0; i < 3; i++) { secp256k1_scalar_sqr(t, t); } - secp256k1_scalar_mul(t, t, x); /* 1 */ + secp256k1_scalar_mul(t, t, &x3); /* 111 */ for (i = 0; i < 5; i++) { /* 0 */ secp256k1_scalar_sqr(t, t); } - secp256k1_scalar_mul(t, t, &x4); /* 1111 */ - for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_mul(t, t, &u9); /* 1001 */ + for (i = 0; i < 6; i++) { /* 000 */ secp256k1_scalar_sqr(t, t); } - secp256k1_scalar_mul(t, t, x); /* 1 */ - for (i = 0; i < 3; i++) { /* 00 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, x); /* 1 */ - for (i = 0; i < 4; i++) { /* 000 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, x); /* 1 */ - for (i = 0; i < 2; i++) { /* 0 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, x); /* 1 */ + secp256k1_scalar_mul(t, t, &u5); /* 101 */ for (i = 0; i < 10; i++) { /* 0000000 */ secp256k1_scalar_sqr(t, t); } @@ -180,50 +179,34 @@ static void secp256k1_scalar_inverse(secp256k1_scalar_t *r, const secp256k1_scal secp256k1_scalar_sqr(t, t); } secp256k1_scalar_mul(t, t, &x8); /* 11111111 */ - for (i = 0; i < 2; i++) { /* 0 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, x); /* 1 */ - for (i = 0; i < 3; i++) { /* 00 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, x); /* 1 */ - for (i = 0; i < 3; i++) { /* 00 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, x); /* 1 */ for (i = 0; i < 5; i++) { /* 0 */ secp256k1_scalar_sqr(t, t); } - secp256k1_scalar_mul(t, t, &x4); /* 1111 */ - for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_mul(t, t, &u9); /* 1001 */ + for (i = 0; i < 6; i++) { /* 00 */ secp256k1_scalar_sqr(t, t); } - secp256k1_scalar_mul(t, t, x); /* 1 */ - for (i = 0; i < 5; i++) { /* 000 */ + secp256k1_scalar_mul(t, t, &u11); /* 1011 */ + for (i = 0; i < 4; i++) { secp256k1_scalar_sqr(t, t); } - secp256k1_scalar_mul(t, t, &x2); /* 11 */ - for (i = 0; i < 4; i++) { /* 00 */ + 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 < 2; i++) { /* 0 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, x); /* 1 */ - for (i = 0; i < 8; i++) { /* 000000 */ + for (i = 0; i < 6; i++) { /* 00 */ secp256k1_scalar_sqr(t, t); } - secp256k1_scalar_mul(t, t, &x2); /* 11 */ - for (i = 0; i < 3; i++) { /* 0 */ + secp256k1_scalar_mul(t, t, &u13); /* 1101 */ + for (i = 0; i < 10; i++) { /* 000000 */ secp256k1_scalar_sqr(t, t); } - secp256k1_scalar_mul(t, t, &x2); /* 11 */ - for (i = 0; i < 3; i++) { /* 00 */ + secp256k1_scalar_mul(t, t, &u13); /* 1101 */ + for (i = 0; i < 4; i++) { secp256k1_scalar_sqr(t, t); } - secp256k1_scalar_mul(t, t, x); /* 1 */ + secp256k1_scalar_mul(t, t, &u9); /* 1001 */ for (i = 0; i < 6; i++) { /* 00000 */ secp256k1_scalar_sqr(t, t); } @@ -234,24 +217,45 @@ static void secp256k1_scalar_inverse(secp256k1_scalar_t *r, const secp256k1_scal secp256k1_scalar_mul(r, t, &x6); /* 111111 */ } -static void secp256k1_scalar_inverse_var(secp256k1_scalar_t *r, const secp256k1_scalar_t *x) { +SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) { + return !(a->d[0] & 1); +} +#endif + +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_t n, m; - secp256k1_scalar_get_b32(b, x); + 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 } #ifdef USE_ENDOMORPHISM +#if defined(EXHAUSTIVE_TEST_ORDER) +/** + * 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 + * nontrivial to get full test coverage for the exhaustive tests. We therefore + * (arbitrarily) set k2 = k + 5 and k1 = k - k2 * lambda. + */ +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; +} +#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, @@ -290,30 +294,31 @@ static void secp256k1_scalar_inverse_var(secp256k1_scalar_t *r, const secp256k1_ * The function below splits a in r1 and r2, such that r1 + lambda * r2 == a (mod order). */ -static void secp256k1_scalar_split_lambda_var(secp256k1_scalar_t *r1, secp256k1_scalar_t *r2, const secp256k1_scalar_t *a) { - secp256k1_scalar_t c1, c2; - static const secp256k1_scalar_t minus_lambda = SECP256K1_SCALAR_CONST( +static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + 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_t minus_b1 = SECP256K1_SCALAR_CONST( + static const secp256k1_scalar minus_b1 = SECP256K1_SCALAR_CONST( 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, 0xE4437ED6UL, 0x010E8828UL, 0x6F547FA9UL, 0x0ABFE4C3UL ); - static const secp256k1_scalar_t minus_b2 = SECP256K1_SCALAR_CONST( + static const secp256k1_scalar minus_b2 = SECP256K1_SCALAR_CONST( 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, 0x8A280AC5UL, 0x0774346DUL, 0xD765CDA8UL, 0x3DB1562CUL ); - static const secp256k1_scalar_t g1 = SECP256K1_SCALAR_CONST( + static const secp256k1_scalar g1 = SECP256K1_SCALAR_CONST( 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00003086UL, 0xD221A7D4UL, 0x6BCDE86CUL, 0x90E49284UL, 0xEB153DABUL ); - static const secp256k1_scalar_t g2 = SECP256K1_SCALAR_CONST( + static const secp256k1_scalar g2 = SECP256K1_SCALAR_CONST( 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x0000E443UL, 0x7ED6010EUL, 0x88286F54UL, 0x7FA90ABFUL, 0xE4C42212UL ); VERIFY_CHECK(r1 != a); VERIFY_CHECK(r2 != a); + /* 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(&c1, &c1, &minus_b1); @@ -323,5 +328,6 @@ static void secp256k1_scalar_split_lambda_var(secp256k1_scalar_t *r1, secp256k1_ secp256k1_scalar_add(r1, r1, a); } #endif - #endif + +#endif /* SECP256K1_SCALAR_IMPL_H */ diff --git a/src/secp256k1/src/scalar_low.h b/src/secp256k1/src/scalar_low.h new file mode 100644 index 000000000..5836febc5 --- /dev/null +++ b/src/secp256k1/src/scalar_low.h @@ -0,0 +1,15 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_SCALAR_REPR_H +#define SECP256K1_SCALAR_REPR_H + +#include + +/** A scalar modulo the group order of the secp256k1 curve. */ +typedef uint32_t secp256k1_scalar; + +#endif /* SECP256K1_SCALAR_REPR_H */ diff --git a/src/secp256k1/src/scalar_low_impl.h b/src/secp256k1/src/scalar_low_impl.h new file mode 100644 index 000000000..c80e70c5a --- /dev/null +++ b/src/secp256k1/src/scalar_low_impl.h @@ -0,0 +1,114 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_SCALAR_REPR_IMPL_H +#define SECP256K1_SCALAR_REPR_IMPL_H + +#include "scalar.h" + +#include + +SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *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 + 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 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) { + *r = (*a + *b) % EXHAUSTIVE_TEST_ORDER; + return *r < *b; +} + +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { + if (flag && bit < 32) + *r += (1 << bit); +#ifdef VERIFY + VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); +#endif +} + +static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) { + const int base = 0x100 % EXHAUSTIVE_TEST_ORDER; + int i; + *r = 0; + for (i = 0; i < 32; i++) { + *r = ((*r * base) + b32[i]) % EXHAUSTIVE_TEST_ORDER; + } + /* just deny overflow, it basically always happens */ + if (overflow) *overflow = 0; +} + +static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* 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) { + return *a == 0; +} + +static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) { + if (*a == 0) { + *r = 0; + } else { + *r = EXHAUSTIVE_TEST_ORDER - *a; + } +} + +SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) { + return *a == 1; +} + +static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { + return *a > EXHAUSTIVE_TEST_ORDER / 2; +} + +static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { + if (flag) secp256k1_scalar_negate(r, 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; +} + +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; +} + +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + *r = (*a * *a) % EXHAUSTIVE_TEST_ORDER; +} + +static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + *r1 = *a; + *r2 = 0; +} + +SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) { + return *a == *b; +} + +#endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/secp256k1/src/secp256k1.c b/src/secp256k1/src/secp256k1.c index d6192dc4e..cecb1550b 100644 --- a/src/secp256k1/src/secp256k1.c +++ b/src/secp256k1/src/secp256k1.c @@ -4,8 +4,6 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#define SECP256K1_BUILD (1) - #include "include/secp256k1.h" #include "util.h" @@ -14,150 +12,346 @@ #include "scalar_impl.h" #include "group_impl.h" #include "ecmult_impl.h" +#include "ecmult_const_impl.h" #include "ecmult_gen_impl.h" #include "ecdsa_impl.h" #include "eckey_impl.h" #include "hash_impl.h" +#define ARG_CHECK(cond) do { \ + if (EXPECT(!(cond), 0)) { \ + secp256k1_callback_call(&ctx->illegal_callback, #cond); \ + return 0; \ + } \ +} 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 +}; + + struct secp256k1_context_struct { - secp256k1_ecmult_context_t ecmult_ctx; - secp256k1_ecmult_gen_context_t ecmult_gen_ctx; + secp256k1_ecmult_context ecmult_ctx; + secp256k1_ecmult_gen_context ecmult_gen_ctx; + secp256k1_callback illegal_callback; + secp256k1_callback error_callback; }; -secp256k1_context_t* secp256k1_context_create(int flags) { - secp256k1_context_t* ret = (secp256k1_context_t*)checked_malloc(sizeof(secp256k1_context_t)); +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; + + if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) { + secp256k1_callback_call(&ret->illegal_callback, + "Invalid flags"); + free(ret); + return NULL; + } secp256k1_ecmult_context_init(&ret->ecmult_ctx); secp256k1_ecmult_gen_context_init(&ret->ecmult_gen_ctx); - if (flags & SECP256K1_CONTEXT_SIGN) { - secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx); + if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) { + secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx, &ret->error_callback); } - if (flags & SECP256K1_CONTEXT_VERIFY) { - secp256k1_ecmult_context_build(&ret->ecmult_ctx); + if (flags & SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) { + secp256k1_ecmult_context_build(&ret->ecmult_ctx, &ret->error_callback); } return ret; } -secp256k1_context_t* secp256k1_context_clone(const secp256k1_context_t* ctx) { - secp256k1_context_t* ret = (secp256k1_context_t*)checked_malloc(sizeof(secp256k1_context_t)); - secp256k1_ecmult_context_clone(&ret->ecmult_ctx, &ctx->ecmult_ctx); - secp256k1_ecmult_gen_context_clone(&ret->ecmult_gen_ctx, &ctx->ecmult_gen_ctx); +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); return ret; } -void secp256k1_context_destroy(secp256k1_context_t* ctx) { - secp256k1_ecmult_context_clear(&ctx->ecmult_ctx); - 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); - free(ctx); + free(ctx); + } } -int secp256k1_ecdsa_verify(const secp256k1_context_t* ctx, const unsigned char *msg32, const unsigned char *sig, int siglen, const unsigned char *pubkey, int pubkeylen) { - secp256k1_ge_t q; - secp256k1_ecdsa_sig_t s; - secp256k1_scalar_t m; - int ret = -3; - DEBUG_CHECK(ctx != NULL); - DEBUG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); - DEBUG_CHECK(msg32 != NULL); - DEBUG_CHECK(sig != NULL); - DEBUG_CHECK(pubkey != NULL); +void secp256k1_context_set_illegal_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) { + if (fun == NULL) { + fun = default_illegal_callback_fn; + } + ctx->illegal_callback.fn = fun; + ctx->illegal_callback.data = data; +} - secp256k1_scalar_set_b32(&m, msg32, NULL); +void secp256k1_context_set_error_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) { + if (fun == NULL) { + fun = default_error_callback_fn; + } + ctx->error_callback.fn = fun; + ctx->error_callback.data = data; +} - if (secp256k1_eckey_pubkey_parse(&q, pubkey, pubkeylen)) { - if (secp256k1_ecdsa_sig_parse(&s, sig, siglen)) { - if (secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &s, &q, &m)) { - /* success is 1, all other values are fail */ - ret = 1; - } else { - ret = 0; - } - } else { - ret = -2; - } +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 { - ret = -1; + /* 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); } + 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); + } +} + +int secp256k1_ec_pubkey_parse(const secp256k1_context* ctx, secp256k1_pubkey* pubkey, const unsigned char *input, size_t inputlen) { + secp256k1_ge Q; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + memset(pubkey, 0, sizeof(*pubkey)); + ARG_CHECK(input != NULL); + if (!secp256k1_eckey_pubkey_parse(&Q, input, inputlen)) { + return 0; + } + secp256k1_pubkey_save(pubkey, &Q); + secp256k1_ge_clear(&Q); + return 1; +} +int secp256k1_ec_pubkey_serialize(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_pubkey* pubkey, unsigned int flags) { + secp256k1_ge Q; + size_t len; + int ret = 0; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(outputlen != NULL); + ARG_CHECK(*outputlen >= ((flags & SECP256K1_FLAGS_BIT_COMPRESSION) ? 33 : 65)); + len = *outputlen; + *outputlen = 0; + ARG_CHECK(output != NULL); + memset(output, 0, len); + ARG_CHECK(pubkey != NULL); + ARG_CHECK((flags & SECP256K1_FLAGS_TYPE_MASK) == SECP256K1_FLAGS_TYPE_COMPRESSION); + if (secp256k1_pubkey_load(ctx, &Q, pubkey)) { + ret = secp256k1_eckey_pubkey_serialize(&Q, output, &len, flags & SECP256K1_FLAGS_BIT_COMPRESSION); + if (ret) { + *outputlen = len; + } + } return ret; } -static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, unsigned int counter, const void *data) { - secp256k1_rfc6979_hmac_sha256_t rng; - unsigned int i; - secp256k1_rfc6979_hmac_sha256_initialize(&rng, key32, 32, msg32, 32, (const unsigned char*)data, data != NULL ? 32 : 0); - for (i = 0; i <= counter; i++) { - secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); - } - secp256k1_rfc6979_hmac_sha256_finalize(&rng); - 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) { + /* When the secp256k1_scalar type is exactly 32 byte, use its + * representation inside secp256k1_ecdsa_signature, as conversion is very fast. + * Note that secp256k1_ecdsa_signature_save must use the same representation. */ + memcpy(r, &sig->data[0], 32); + memcpy(s, &sig->data[32], 32); + } else { + secp256k1_scalar_set_b32(r, &sig->data[0], NULL); + secp256k1_scalar_set_b32(s, &sig->data[32], NULL); + } } -const secp256k1_nonce_function_t secp256k1_nonce_function_rfc6979 = nonce_function_rfc6979; -const secp256k1_nonce_function_t secp256k1_nonce_function_default = nonce_function_rfc6979; +static void secp256k1_ecdsa_signature_save(secp256k1_ecdsa_signature* sig, const secp256k1_scalar* r, const secp256k1_scalar* s) { + if (sizeof(secp256k1_scalar) == 32) { + memcpy(&sig->data[0], r, 32); + memcpy(&sig->data[32], s, 32); + } else { + secp256k1_scalar_get_b32(&sig->data[0], r); + secp256k1_scalar_get_b32(&sig->data[32], s); + } +} -int secp256k1_ecdsa_sign(const secp256k1_context_t* ctx, const unsigned char *msg32, unsigned char *signature, int *signaturelen, const unsigned char *seckey, secp256k1_nonce_function_t noncefp, const void* noncedata) { - secp256k1_ecdsa_sig_t sig; - secp256k1_scalar_t sec, non, msg; - int ret = 0; +int secp256k1_ecdsa_signature_parse_der(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) { + secp256k1_scalar r, s; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(input != NULL); + + if (secp256k1_ecdsa_sig_parse(&r, &s, input, inputlen)) { + secp256k1_ecdsa_signature_save(sig, &r, &s); + return 1; + } else { + memset(sig, 0, sizeof(*sig)); + return 0; + } +} + +int secp256k1_ecdsa_signature_parse_compact(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input64) { + secp256k1_scalar r, s; + int ret = 1; int overflow = 0; - unsigned int count = 0; - DEBUG_CHECK(ctx != NULL); - DEBUG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); - DEBUG_CHECK(msg32 != NULL); - DEBUG_CHECK(signature != NULL); - DEBUG_CHECK(signaturelen != NULL); - DEBUG_CHECK(seckey != NULL); - if (noncefp == NULL) { - noncefp = secp256k1_nonce_function_default; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(input64 != NULL); + + secp256k1_scalar_set_b32(&r, &input64[0], &overflow); + ret &= !overflow; + secp256k1_scalar_set_b32(&s, &input64[32], &overflow); + ret &= !overflow; + if (ret) { + secp256k1_ecdsa_signature_save(sig, &r, &s); + } else { + memset(sig, 0, sizeof(*sig)); } + return ret; +} - secp256k1_scalar_set_b32(&sec, seckey, &overflow); - /* Fail if the secret key is invalid. */ - if (!overflow && !secp256k1_scalar_is_zero(&sec)) { - secp256k1_scalar_set_b32(&msg, msg32, NULL); - while (1) { - unsigned char nonce32[32]; - ret = noncefp(nonce32, msg32, seckey, count, noncedata); - if (!ret) { - break; - } - secp256k1_scalar_set_b32(&non, nonce32, &overflow); - memset(nonce32, 0, 32); - if (!secp256k1_scalar_is_zero(&non) && !overflow) { - if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &sig, &sec, &msg, &non, NULL)) { - break; - } - } - count++; - } +int secp256k1_ecdsa_signature_serialize_der(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_ecdsa_signature* sig) { + secp256k1_scalar r, s; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(outputlen != NULL); + ARG_CHECK(sig != NULL); + + secp256k1_ecdsa_signature_load(ctx, &r, &s, sig); + return secp256k1_ecdsa_sig_serialize(output, outputlen, &r, &s); +} + +int secp256k1_ecdsa_signature_serialize_compact(const secp256k1_context* ctx, unsigned char *output64, const secp256k1_ecdsa_signature* sig) { + secp256k1_scalar r, s; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output64 != NULL); + ARG_CHECK(sig != NULL); + + secp256k1_ecdsa_signature_load(ctx, &r, &s, sig); + secp256k1_scalar_get_b32(&output64[0], &r); + secp256k1_scalar_get_b32(&output64[32], &s); + return 1; +} + +int secp256k1_ecdsa_signature_normalize(const secp256k1_context* ctx, secp256k1_ecdsa_signature *sigout, const secp256k1_ecdsa_signature *sigin) { + secp256k1_scalar r, s; + int ret = 0; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sigin != NULL); + + secp256k1_ecdsa_signature_load(ctx, &r, &s, sigin); + ret = secp256k1_scalar_is_high(&s); + if (sigout != NULL) { if (ret) { - ret = secp256k1_ecdsa_sig_serialize(signature, signaturelen, &sig); + secp256k1_scalar_negate(&s, &s); } - secp256k1_scalar_clear(&msg); - secp256k1_scalar_clear(&non); - secp256k1_scalar_clear(&sec); - } - if (!ret) { - *signaturelen = 0; + secp256k1_ecdsa_signature_save(sigout, &r, &s); } + return ret; } -int secp256k1_ecdsa_sign_compact(const secp256k1_context_t* ctx, const unsigned char *msg32, unsigned char *sig64, const unsigned char *seckey, secp256k1_nonce_function_t noncefp, const void* noncedata, int *recid) { - secp256k1_ecdsa_sig_t sig; - secp256k1_scalar_t sec, non, msg; +int secp256k1_ecdsa_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msg32, 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(sig != NULL); + ARG_CHECK(pubkey != NULL); + + secp256k1_scalar_set_b32(&m, msg32, 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)); +} + +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; + secp256k1_rfc6979_hmac_sha256 rng; + unsigned int i; + /* 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. + * - 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); + if (data != NULL) { + memcpy(keydata + 64, data, 32); + keylen = 96; + } + if (algo16 != NULL) { + memcpy(keydata + keylen, algo16, 16); + keylen += 16; + } + secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, keylen); + memset(keydata, 0, sizeof(keydata)); + for (i = 0; i <= counter; i++) { + secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); + } + secp256k1_rfc6979_hmac_sha256_finalize(&rng); + return 1; +} + +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; + secp256k1_scalar sec, non, msg; int ret = 0; int overflow = 0; - unsigned int count = 0; - DEBUG_CHECK(ctx != NULL); - DEBUG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); - DEBUG_CHECK(msg32 != NULL); - DEBUG_CHECK(sig64 != NULL); - DEBUG_CHECK(seckey != NULL); + 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); if (noncefp == NULL) { noncefp = secp256k1_nonce_function_default; } @@ -165,139 +359,112 @@ int secp256k1_ecdsa_sign_compact(const secp256k1_context_t* ctx, const unsigned 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) { - unsigned char nonce32[32]; - ret = noncefp(nonce32, msg32, seckey, count, noncedata); + ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count); if (!ret) { break; } secp256k1_scalar_set_b32(&non, nonce32, &overflow); - memset(nonce32, 0, 32); - if (!secp256k1_scalar_is_zero(&non) && !overflow) { - if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &sig, &sec, &msg, &non, recid)) { + if (!overflow && !secp256k1_scalar_is_zero(&non)) { + if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, NULL)) { break; } } count++; } - if (ret) { - secp256k1_scalar_get_b32(sig64, &sig.r); - secp256k1_scalar_get_b32(sig64 + 32, &sig.s); - } + memset(nonce32, 0, 32); secp256k1_scalar_clear(&msg); secp256k1_scalar_clear(&non); secp256k1_scalar_clear(&sec); } - if (!ret) { - memset(sig64, 0, 64); - } - return ret; -} - -int secp256k1_ecdsa_recover_compact(const secp256k1_context_t* ctx, const unsigned char *msg32, const unsigned char *sig64, unsigned char *pubkey, int *pubkeylen, int compressed, int recid) { - secp256k1_ge_t q; - secp256k1_ecdsa_sig_t sig; - secp256k1_scalar_t m; - int ret = 0; - int overflow = 0; - DEBUG_CHECK(ctx != NULL); - DEBUG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); - DEBUG_CHECK(msg32 != NULL); - DEBUG_CHECK(sig64 != NULL); - DEBUG_CHECK(pubkey != NULL); - DEBUG_CHECK(pubkeylen != NULL); - DEBUG_CHECK(recid >= 0 && recid <= 3); - - secp256k1_scalar_set_b32(&sig.r, sig64, &overflow); - if (!overflow) { - secp256k1_scalar_set_b32(&sig.s, sig64 + 32, &overflow); - if (!overflow) { - secp256k1_scalar_set_b32(&m, msg32, NULL); - - if (secp256k1_ecdsa_sig_recover(&ctx->ecmult_ctx, &sig, &q, &m, recid)) { - ret = secp256k1_eckey_pubkey_serialize(&q, pubkey, pubkeylen, compressed); - } - } + if (ret) { + secp256k1_ecdsa_signature_save(signature, &r, &s); + } else { + memset(signature, 0, sizeof(*signature)); } return ret; } -int secp256k1_ec_seckey_verify(const secp256k1_context_t* ctx, const unsigned char *seckey) { - secp256k1_scalar_t sec; +int secp256k1_ec_seckey_verify(const secp256k1_context* ctx, const unsigned char *seckey) { + secp256k1_scalar sec; int ret; int overflow; - DEBUG_CHECK(ctx != NULL); - DEBUG_CHECK(seckey != NULL); - (void)ctx; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(seckey != NULL); secp256k1_scalar_set_b32(&sec, seckey, &overflow); - ret = !secp256k1_scalar_is_zero(&sec) && !overflow; + ret = !overflow && !secp256k1_scalar_is_zero(&sec); secp256k1_scalar_clear(&sec); return ret; } -int secp256k1_ec_pubkey_verify(const secp256k1_context_t* ctx, const unsigned char *pubkey, int pubkeylen) { - secp256k1_ge_t q; - DEBUG_CHECK(ctx != NULL); - DEBUG_CHECK(pubkey != NULL); - (void)ctx; - - return secp256k1_eckey_pubkey_parse(&q, pubkey, pubkeylen); -} - -int secp256k1_ec_pubkey_create(const secp256k1_context_t* ctx, unsigned char *pubkey, int *pubkeylen, const unsigned char *seckey, int compressed) { - secp256k1_gej_t pj; - secp256k1_ge_t p; - secp256k1_scalar_t sec; +int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *seckey) { + secp256k1_gej pj; + secp256k1_ge p; + secp256k1_scalar sec; int overflow; int ret = 0; - DEBUG_CHECK(ctx != NULL); - DEBUG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); - DEBUG_CHECK(pubkey != NULL); - DEBUG_CHECK(pubkeylen != NULL); - DEBUG_CHECK(seckey != NULL); + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + memset(pubkey, 0, sizeof(*pubkey)); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(seckey != NULL); secp256k1_scalar_set_b32(&sec, seckey, &overflow); - if (!overflow) { + ret = (!overflow) & (!secp256k1_scalar_is_zero(&sec)); + if (ret) { secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &sec); - secp256k1_scalar_clear(&sec); secp256k1_ge_set_gej(&p, &pj); - ret = secp256k1_eckey_pubkey_serialize(&p, pubkey, pubkeylen, compressed); - } - if (!ret) { - *pubkeylen = 0; + secp256k1_pubkey_save(pubkey, &p); } + secp256k1_scalar_clear(&sec); return ret; } -int secp256k1_ec_pubkey_decompress(const secp256k1_context_t* ctx, unsigned char *pubkey, int *pubkeylen) { - secp256k1_ge_t p; +int secp256k1_ec_privkey_negate(const secp256k1_context* ctx, unsigned char *seckey) { + secp256k1_scalar sec; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(seckey != NULL); + + secp256k1_scalar_set_b32(&sec, seckey, NULL); + secp256k1_scalar_negate(&sec, &sec); + secp256k1_scalar_get_b32(seckey, &sec); + + return 1; +} + +int secp256k1_ec_pubkey_negate(const secp256k1_context* ctx, secp256k1_pubkey *pubkey) { int ret = 0; - DEBUG_CHECK(pubkey != NULL); - DEBUG_CHECK(pubkeylen != NULL); - (void)ctx; + secp256k1_ge p; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); - if (secp256k1_eckey_pubkey_parse(&p, pubkey, *pubkeylen)) { - ret = secp256k1_eckey_pubkey_serialize(&p, pubkey, pubkeylen, 0); + ret = secp256k1_pubkey_load(ctx, &p, pubkey); + memset(pubkey, 0, sizeof(*pubkey)); + if (ret) { + secp256k1_ge_neg(&p, &p); + secp256k1_pubkey_save(pubkey, &p); } return ret; } -int secp256k1_ec_privkey_tweak_add(const secp256k1_context_t* ctx, unsigned char *seckey, const unsigned char *tweak) { - secp256k1_scalar_t term; - secp256k1_scalar_t sec; +int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) { + secp256k1_scalar term; + secp256k1_scalar sec; int ret = 0; int overflow = 0; - DEBUG_CHECK(ctx != NULL); - DEBUG_CHECK(seckey != NULL); - DEBUG_CHECK(tweak != NULL); - (void)ctx; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(seckey != NULL); + ARG_CHECK(tweak != NULL); secp256k1_scalar_set_b32(&term, tweak, &overflow); secp256k1_scalar_set_b32(&sec, seckey, NULL); - ret = secp256k1_eckey_privkey_tweak_add(&sec, &term) && !overflow; + ret = !overflow && secp256k1_eckey_privkey_tweak_add(&sec, &term); + memset(seckey, 0, 32); if (ret) { secp256k1_scalar_get_b32(seckey, &sec); } @@ -307,45 +474,43 @@ int secp256k1_ec_privkey_tweak_add(const secp256k1_context_t* ctx, unsigned char return ret; } -int secp256k1_ec_pubkey_tweak_add(const secp256k1_context_t* ctx, unsigned char *pubkey, int pubkeylen, const unsigned char *tweak) { - secp256k1_ge_t p; - secp256k1_scalar_t term; +int secp256k1_ec_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak) { + secp256k1_ge p; + secp256k1_scalar term; int ret = 0; int overflow = 0; - DEBUG_CHECK(ctx != NULL); - DEBUG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); - DEBUG_CHECK(pubkey != NULL); - DEBUG_CHECK(tweak != NULL); + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(tweak != NULL); secp256k1_scalar_set_b32(&term, tweak, &overflow); - if (!overflow) { - ret = secp256k1_eckey_pubkey_parse(&p, pubkey, pubkeylen); - if (ret) { - ret = secp256k1_eckey_pubkey_tweak_add(&ctx->ecmult_ctx, &p, &term); - } - if (ret) { - int oldlen = pubkeylen; - ret = secp256k1_eckey_pubkey_serialize(&p, pubkey, &pubkeylen, oldlen <= 33); - VERIFY_CHECK(pubkeylen == oldlen); + ret = !overflow && secp256k1_pubkey_load(ctx, &p, pubkey); + memset(pubkey, 0, sizeof(*pubkey)); + if (ret) { + if (secp256k1_eckey_pubkey_tweak_add(&ctx->ecmult_ctx, &p, &term)) { + secp256k1_pubkey_save(pubkey, &p); + } else { + ret = 0; } } return ret; } -int secp256k1_ec_privkey_tweak_mul(const secp256k1_context_t* ctx, unsigned char *seckey, const unsigned char *tweak) { - secp256k1_scalar_t factor; - secp256k1_scalar_t sec; +int secp256k1_ec_privkey_tweak_mul(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) { + secp256k1_scalar factor; + secp256k1_scalar sec; int ret = 0; int overflow = 0; - DEBUG_CHECK(ctx != NULL); - DEBUG_CHECK(seckey != NULL); - DEBUG_CHECK(tweak != NULL); - (void)ctx; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(seckey != NULL); + ARG_CHECK(tweak != NULL); secp256k1_scalar_set_b32(&factor, tweak, &overflow); secp256k1_scalar_set_b32(&sec, seckey, NULL); - ret = secp256k1_eckey_privkey_tweak_mul(&sec, &factor) && !overflow; + ret = !overflow && secp256k1_eckey_privkey_tweak_mul(&sec, &factor); + memset(seckey, 0, 32); if (ret) { secp256k1_scalar_get_b32(seckey, &sec); } @@ -355,65 +520,65 @@ int secp256k1_ec_privkey_tweak_mul(const secp256k1_context_t* ctx, unsigned char return ret; } -int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context_t* ctx, unsigned char *pubkey, int pubkeylen, const unsigned char *tweak) { - secp256k1_ge_t p; - secp256k1_scalar_t factor; +int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak) { + secp256k1_ge p; + secp256k1_scalar factor; int ret = 0; int overflow = 0; - DEBUG_CHECK(ctx != NULL); - DEBUG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); - DEBUG_CHECK(pubkey != NULL); - DEBUG_CHECK(tweak != NULL); + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(tweak != NULL); secp256k1_scalar_set_b32(&factor, tweak, &overflow); - if (!overflow) { - ret = secp256k1_eckey_pubkey_parse(&p, pubkey, pubkeylen); - if (ret) { - ret = secp256k1_eckey_pubkey_tweak_mul(&ctx->ecmult_ctx, &p, &factor); - } - if (ret) { - int oldlen = pubkeylen; - ret = secp256k1_eckey_pubkey_serialize(&p, pubkey, &pubkeylen, oldlen <= 33); - VERIFY_CHECK(pubkeylen == oldlen); + 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)) { + secp256k1_pubkey_save(pubkey, &p); + } else { + ret = 0; } } return ret; } -int secp256k1_ec_privkey_export(const secp256k1_context_t* ctx, const unsigned char *seckey, unsigned char *privkey, int *privkeylen, int compressed) { - secp256k1_scalar_t key; - int ret = 0; - DEBUG_CHECK(seckey != NULL); - DEBUG_CHECK(privkey != NULL); - DEBUG_CHECK(privkeylen != NULL); - DEBUG_CHECK(ctx != NULL); - DEBUG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); - - secp256k1_scalar_set_b32(&key, seckey, NULL); - ret = secp256k1_eckey_privkey_serialize(&ctx->ecmult_gen_ctx, privkey, privkeylen, &key, compressed); - secp256k1_scalar_clear(&key); - return ret; +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); + return 1; } -int secp256k1_ec_privkey_import(const secp256k1_context_t* ctx, unsigned char *seckey, const unsigned char *privkey, int privkeylen) { - secp256k1_scalar_t key; - int ret = 0; - DEBUG_CHECK(seckey != NULL); - DEBUG_CHECK(privkey != NULL); - (void)ctx; +int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey *pubnonce, const secp256k1_pubkey * const *pubnonces, size_t n) { + size_t i; + secp256k1_gej Qj; + secp256k1_ge Q; - ret = secp256k1_eckey_privkey_parse(&key, privkey, privkeylen); - if (ret) { - secp256k1_scalar_get_b32(seckey, &key); - } - secp256k1_scalar_clear(&key); - return ret; -} + ARG_CHECK(pubnonce != NULL); + memset(pubnonce, 0, sizeof(*pubnonce)); + ARG_CHECK(n >= 1); + ARG_CHECK(pubnonces != NULL); -int secp256k1_context_randomize(secp256k1_context_t* ctx, const unsigned char *seed32) { - DEBUG_CHECK(ctx != NULL); - DEBUG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); - secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32); + secp256k1_gej_set_infinity(&Qj); + + for (i = 0; i < n; i++) { + secp256k1_pubkey_load(ctx, &Q, pubnonces[i]); + secp256k1_gej_add_ge(&Qj, &Qj, &Q); + } + if (secp256k1_gej_is_infinity(&Qj)) { + return 0; + } + secp256k1_ge_set_gej(&Q, &Qj); + secp256k1_pubkey_save(pubnonce, &Q); return 1; } + +#ifdef ENABLE_MODULE_ECDH +# include "modules/ecdh/main_impl.h" +#endif + +#ifdef ENABLE_MODULE_RECOVERY +# include "modules/recovery/main_impl.h" +#endif diff --git a/src/secp256k1/src/testrand.h b/src/secp256k1/src/testrand.h index 041bb92c4..f1f9be077 100644 --- a/src/secp256k1/src/testrand.h +++ b/src/secp256k1/src/testrand.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_TESTRAND_H_ -#define _SECP256K1_TESTRAND_H_ +#ifndef SECP256K1_TESTRAND_H +#define SECP256K1_TESTRAND_H #if defined HAVE_CONFIG_H #include "libsecp256k1-config.h" @@ -16,13 +16,23 @@ /** Seed the pseudorandom number generator for testing. */ SECP256K1_INLINE static void secp256k1_rand_seed(const unsigned char *seed16); -/** Generate a pseudorandom 32-bit number. */ +/** Generate a pseudorandom number in the range [0..2**32-1]. */ static uint32_t secp256k1_rand32(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); + +/** Generate a pseudorandom number in the range [0..range-1]. */ +static uint32_t secp256k1_rand_int(uint32_t range); + /** Generate a pseudorandom 32-byte array. */ static void secp256k1_rand256(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); -#endif +/** Generate pseudorandom bytes with long sequences of zero and one bits. */ +static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len); + +#endif /* SECP256K1_TESTRAND_H */ diff --git a/src/secp256k1/src/testrand_impl.h b/src/secp256k1/src/testrand_impl.h index 21c69f1c5..30a91e529 100644 --- a/src/secp256k1/src/testrand_impl.h +++ b/src/secp256k1/src/testrand_impl.h @@ -1,11 +1,11 @@ /********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * + * Copyright (c) 2013-2015 Pieter Wuille * * Distributed under the MIT software license, see the accompanying * * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_TESTRAND_IMPL_H_ -#define _SECP256K1_TESTRAND_IMPL_H_ +#ifndef SECP256K1_TESTRAND_IMPL_H +#define SECP256K1_TESTRAND_IMPL_H #include #include @@ -13,12 +13,14 @@ #include "testrand.h" #include "hash.h" -static secp256k1_rfc6979_hmac_sha256_t secp256k1_test_rng; +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; SECP256K1_INLINE static void secp256k1_rand_seed(const unsigned char *seed16) { - secp256k1_rfc6979_hmac_sha256_initialize(&secp256k1_test_rng, (const unsigned char*)"TestRNG", 7, seed16, 16, NULL, 0); + secp256k1_rfc6979_hmac_sha256_initialize(&secp256k1_test_rng, seed16, 16); } SECP256K1_INLINE static uint32_t secp256k1_rand32(void) { @@ -29,32 +31,80 @@ SECP256K1_INLINE static uint32_t secp256k1_rand32(void) { 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; +} + +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; + } + while(1) { + uint32_t x = secp256k1_rand_bits(bits); + if (x < trange) { + return (mult == 1) ? x : (x % range); + } + } +} + static void secp256k1_rand256(unsigned char *b32) { secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, b32, 32); } -static void secp256k1_rand256_test(unsigned char *b32) { - int bits=0; - uint64_t ent = 0; - int entleft = 0; - memset(b32, 0, 32); - while (bits < 256) { +static void secp256k1_rand_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; - if (entleft < 12) { - ent |= ((uint64_t)secp256k1_rand32()) << entleft; - entleft += 32; - } - now = 1 + ((ent % 64)*((ent >> 6) % 32)+16)/31; - val = 1 & (ent >> 11); - ent >>= 12; - entleft -= 12; - while (now > 0 && bits < 256) { - b32[bits / 8] |= val << (bits % 8); + now = 1 + (secp256k1_rand_bits(6) * secp256k1_rand_bits(5) + 16) / 31; + val = secp256k1_rand_bits(1); + while (now > 0 && bits < len * 8) { + bytes[bits / 8] |= val << (bits % 8); now--; bits++; } } } -#endif +static void secp256k1_rand256_test(unsigned char *b32) { + secp256k1_rand_bytes_test(b32, 32); +} + +#endif /* SECP256K1_TESTRAND_IMPL_H */ diff --git a/src/secp256k1/src/tests.c b/src/secp256k1/src/tests.c index d0e05057f..f307b99d5 100644 --- a/src/secp256k1/src/tests.c +++ b/src/secp256k1/src/tests.c @@ -10,10 +10,12 @@ #include #include +#include #include #include "secp256k1.c" +#include "include/secp256k1.h" #include "testrand_impl.h" #ifdef ENABLE_OPENSSL_TESTS @@ -23,10 +25,40 @@ #include "openssl/obj_mac.h" #endif +#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 +#endif + static int count = 64; -static secp256k1_context_t *ctx = NULL; +static secp256k1_context *ctx = NULL; + +static void counting_illegal_callback_fn(const char* str, void* data) { + /* Dummy callback function that just counts. */ + int32_t *p; + (void)str; + p = data; + (*p)++; +} -void random_field_element_test(secp256k1_fe_t *fe) { +static void uncounting_illegal_callback_fn(const char* str, void* data) { + /* Dummy callback function that just counts (backwards). */ + int32_t *p; + (void)str; + p = data; + (*p)--; +} + +void random_field_element_test(secp256k1_fe *fe) { do { unsigned char b32[32]; secp256k1_rand256_test(b32); @@ -36,9 +68,9 @@ void random_field_element_test(secp256k1_fe_t *fe) { } while(1); } -void random_field_element_magnitude(secp256k1_fe_t *fe) { - secp256k1_fe_t zero; - int n = secp256k1_rand32() % 9; +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; @@ -47,23 +79,22 @@ void random_field_element_magnitude(secp256k1_fe_t *fe) { secp256k1_fe_negate(&zero, &zero, 0); secp256k1_fe_mul_int(&zero, n - 1); secp256k1_fe_add(fe, &zero); -#ifdef VERIFY - CHECK(fe->magnitude == n); -#endif + VERIFY_CHECK(fe->magnitude == n); } -void random_group_element_test(secp256k1_ge_t *ge) { - secp256k1_fe_t fe; +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_rand32() & 1)) { + if (secp256k1_ge_set_xo_var(ge, &fe, secp256k1_rand_bits(1))) { + secp256k1_fe_normalize(&ge->y); break; } } while(1); } -void random_group_element_jacobian_test(secp256k1_gej_t *gej, const secp256k1_ge_t *ge) { - secp256k1_fe_t z2, z3; +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)) { @@ -77,7 +108,7 @@ void random_group_element_jacobian_test(secp256k1_gej_t *gej, const secp256k1_ge gej->infinity = ge->infinity; } -void random_scalar_order_test(secp256k1_scalar_t *num) { +void random_scalar_order_test(secp256k1_scalar *num) { do { unsigned char b32[32]; int overflow = 0; @@ -90,7 +121,7 @@ void random_scalar_order_test(secp256k1_scalar_t *num) { } while(1); } -void random_scalar_order(secp256k1_scalar_t *num) { +void random_scalar_order(secp256k1_scalar *num) { do { unsigned char b32[32]; int overflow = 0; @@ -104,19 +135,34 @@ void random_scalar_order(secp256k1_scalar_t *num) { } void run_context_tests(void) { - secp256k1_context_t *none = secp256k1_context_create(0); - secp256k1_context_t *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); - secp256k1_context_t *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); - secp256k1_context_t *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); - - secp256k1_gej_t pubj; - secp256k1_ge_t pub; - secp256k1_scalar_t msg, key, nonce; - secp256k1_ecdsa_sig_t sig; + 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); + + secp256k1_gej pubj; + secp256k1_ge pub; + secp256k1_scalar msg, key, nonce; + secp256k1_scalar sigr, sigs; + + memset(&zero_pubkey, 0, sizeof(zero_pubkey)); + + 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); /*** clone and destroy all of them to make sure cloning was complete ***/ { - secp256k1_context_t *ctx_tmp; + 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); @@ -124,30 +170,82 @@ void run_context_tests(void) { ctx_tmp = both; both = secp256k1_context_clone(both); secp256k1_context_destroy(ctx_tmp); } + /* Verify that the error callback makes it across the clone. */ + CHECK(vrfy->error_callback.fn != sign->error_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); + /*** attempt to use them ***/ random_scalar_order_test(&msg); random_scalar_order_test(&key); secp256k1_ecmult_gen(&both->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, &sig, &key, &msg, &nonce, NULL)); + } while(!secp256k1_ecdsa_sig_sign(&both->ecmult_gen_ctx, &sigr, &sigs, &key, &msg, &nonce, NULL)); /* try signing */ - CHECK(secp256k1_ecdsa_sig_sign(&sign->ecmult_gen_ctx, &sig, &key, &msg, &nonce, NULL)); - CHECK(secp256k1_ecdsa_sig_sign(&both->ecmult_gen_ctx, &sig, &key, &msg, &nonce, NULL)); + 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)); /* try verifying */ - CHECK(secp256k1_ecdsa_sig_verify(&vrfy->ecmult_ctx, &sig, &pub, &msg)); - CHECK(secp256k1_ecdsa_sig_verify(&both->ecmult_ctx, &sig, &pub, &msg)); + CHECK(secp256k1_ecdsa_sig_verify(&vrfy->ecmult_ctx, &sigr, &sigs, &pub, &msg)); + CHECK(secp256k1_ecdsa_sig_verify(&both->ecmult_ctx, &sigr, &sigs, &pub, &msg)); /* cleanup */ secp256k1_context_destroy(none); secp256k1_context_destroy(sign); secp256k1_context_destroy(vrfy); secp256k1_context_destroy(both); + /* Defined as no-op. */ + secp256k1_context_destroy(NULL); } /***** HASH TESTS *****/ @@ -172,13 +270,13 @@ void run_sha256_tests(void) { int i; for (i = 0; i < 8; i++) { unsigned char out[32]; - secp256k1_sha256_t hasher; + secp256k1_sha256 hasher; secp256k1_sha256_initialize(&hasher); secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i]), strlen(inputs[i])); secp256k1_sha256_finalize(&hasher, out); CHECK(memcmp(out, outputs[i], 32) == 0); if (strlen(inputs[i]) > 0) { - int split = secp256k1_rand32() % strlen(inputs[i]); + int split = secp256k1_rand_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); @@ -215,14 +313,14 @@ void run_hmac_sha256_tests(void) { }; int i; for (i = 0; i < 6; i++) { - secp256k1_hmac_sha256_t hasher; + secp256k1_hmac_sha256 hasher; unsigned char out[32]; 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); if (strlen(inputs[i]) > 0) { - int split = secp256k1_rand32() % strlen(inputs[i]); + int split = secp256k1_rand_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); @@ -233,42 +331,39 @@ void run_hmac_sha256_tests(void) { } void run_rfc6979_hmac_sha256_tests(void) { - static const unsigned char key1[32] = {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}; - static const unsigned char msg1[32] = {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}; + 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}, {0x2b, 0xf0, 0xff, 0xf1, 0xd3, 0xc3, 0x78, 0xa2, 0x2d, 0xc5, 0xde, 0x1d, 0x85, 0x65, 0x22, 0x32, 0x5c, 0x65, 0xb5, 0x04, 0x49, 0x1a, 0x0c, 0xbd, 0x01, 0xcb, 0x8f, 0x3a, 0xa6, 0x7f, 0xfd, 0x4a}, {0xf5, 0x28, 0xb4, 0x10, 0xcb, 0x54, 0x1f, 0x77, 0x00, 0x0d, 0x7a, 0xfb, 0x6c, 0x5b, 0x53, 0xc5, 0xc4, 0x71, 0xea, 0xb4, 0x3e, 0x46, 0x6d, 0x9a, 0xc5, 0x19, 0x0c, 0x39, 0xc8, 0x2f, 0xd8, 0x2e} }; - static const unsigned char key2[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 msg2[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}; + static const unsigned char key2[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, 0xff, 0xff, 0xff, 0xff, 0xff, 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}; static const unsigned char out2[3][32] = { {0x9c, 0x23, 0x6c, 0x16, 0x5b, 0x82, 0xae, 0x0c, 0xd5, 0x90, 0x65, 0x9e, 0x10, 0x0b, 0x6b, 0xab, 0x30, 0x36, 0xe7, 0xba, 0x8b, 0x06, 0x74, 0x9b, 0xaf, 0x69, 0x81, 0xe1, 0x6f, 0x1a, 0x2b, 0x95}, {0xdf, 0x47, 0x10, 0x61, 0x62, 0x5b, 0xc0, 0xea, 0x14, 0xb6, 0x82, 0xfe, 0xee, 0x2c, 0x9c, 0x02, 0xf2, 0x35, 0xda, 0x04, 0x20, 0x4c, 0x1d, 0x62, 0xa1, 0x53, 0x6c, 0x6e, 0x17, 0xae, 0xd7, 0xa9}, {0x75, 0x97, 0x88, 0x7c, 0xbd, 0x76, 0x32, 0x1f, 0x32, 0xe3, 0x04, 0x40, 0x67, 0x9a, 0x22, 0xcf, 0x7f, 0x8d, 0x9d, 0x2e, 0xac, 0x39, 0x0e, 0x58, 0x1f, 0xea, 0x09, 0x1c, 0xe2, 0x02, 0xba, 0x94} }; - secp256k1_rfc6979_hmac_sha256_t rng; + secp256k1_rfc6979_hmac_sha256 rng; unsigned char out[32]; - unsigned char zero[1] = {0}; int i; - secp256k1_rfc6979_hmac_sha256_initialize(&rng, key1, 32, msg1, 32, NULL, 1); + 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); } secp256k1_rfc6979_hmac_sha256_finalize(&rng); - secp256k1_rfc6979_hmac_sha256_initialize(&rng, key1, 32, msg1, 32, zero, 1); + 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); } secp256k1_rfc6979_hmac_sha256_finalize(&rng); - secp256k1_rfc6979_hmac_sha256_initialize(&rng, key2, 32, msg2, 32, zero, 0); + 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); @@ -276,30 +371,102 @@ void run_rfc6979_hmac_sha256_tests(void) { 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))); + } + } + } + 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); + } + } +} + +/* 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; + 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); + } + /* 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); + } +} + +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]); + } + } +} + /***** NUM TESTS *****/ #ifndef USE_NUM_NONE -void random_num_negate(secp256k1_num_t *num) { - if (secp256k1_rand32() & 1) { +void random_num_negate(secp256k1_num *num) { + if (secp256k1_rand_bits(1)) { secp256k1_num_negate(num); } } -void random_num_order_test(secp256k1_num_t *num) { - secp256k1_scalar_t sc; +void random_num_order_test(secp256k1_num *num) { + secp256k1_scalar sc; random_scalar_order_test(&sc); secp256k1_scalar_get_num(num, &sc); } -void random_num_order(secp256k1_num_t *num) { - secp256k1_scalar_t sc; +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_t n1; - secp256k1_num_t n2; + secp256k1_num n1; + secp256k1_num n2; random_num_order_test(&n1); /* n1 = R */ random_num_negate(&n1); secp256k1_num_copy(&n2, &n1); /* n2 = R */ @@ -318,16 +485,17 @@ void test_num_negate(void) { } void test_num_add_sub(void) { - secp256k1_num_t n1; - secp256k1_num_t n2; - secp256k1_num_t n1p2, n2p1, n1m2, n2m1; - int r = secp256k1_rand32(); + 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 (r & 1) { + if (secp256k1_rand_bits(1)) { random_num_negate(&n1); } random_num_order_test(&n2); /* n2 = R2 */ - if (r & 2) { + if (secp256k1_rand_bits(1)) { random_num_negate(&n2); } secp256k1_num_add(&n1p2, &n1, &n2); /* n1p2 = R1 + R2 */ @@ -344,6 +512,110 @@ void test_num_add_sub(void) { 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) { + 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); + } + 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; + int i; + /* squares mod 5 are 1, 4 */ + const int jacobi5[10] = { 0, 1, -1, -1, 1, 0, 1, -1, -1, 1 }; + + /* 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]); + } + + /** 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) { @@ -351,6 +623,8 @@ void run_num_smalltests(void) { for (i = 0; i < 100*count; i++) { test_num_negate(); test_num_add_sub(); + test_num_mod(); + test_num_jacobi(); } } #endif @@ -358,12 +632,12 @@ void run_num_smalltests(void) { /***** SCALAR TESTS *****/ void scalar_test(void) { - secp256k1_scalar_t s; - secp256k1_scalar_t s1; - secp256k1_scalar_t s2; + secp256k1_scalar s; + secp256k1_scalar s1; + secp256k1_scalar s2; #ifndef USE_NUM_NONE - secp256k1_num_t snum, s1num, s2num; - secp256k1_num_t order, half_order; + secp256k1_num snum, s1num, s2num; + secp256k1_num order, half_order; #endif unsigned char c[32]; @@ -390,10 +664,10 @@ void scalar_test(void) { { int i; /* Test that fetching groups of 4 bits from a scalar and recursing n(i)=16*n(i-1)+p(i) reconstructs it. */ - secp256k1_scalar_t n; + secp256k1_scalar n; secp256k1_scalar_set_int(&n, 0); for (i = 0; i < 256; i += 4) { - secp256k1_scalar_t t; + secp256k1_scalar t; int j; secp256k1_scalar_set_int(&t, secp256k1_scalar_get_bits(&s, 256 - 4 - i, 4)); for (j = 0; j < 4; j++) { @@ -406,13 +680,13 @@ void scalar_test(void) { { /* Test that fetching groups of randomly-sized bits from a scalar and recursing n(i)=b*n(i-1)+p(i) reconstructs it. */ - secp256k1_scalar_t n; + secp256k1_scalar n; int i = 0; secp256k1_scalar_set_int(&n, 0); while (i < 256) { - secp256k1_scalar_t t; + secp256k1_scalar t; int j; - int now = (secp256k1_rand32() % 15) + 1; + int now = secp256k1_rand_int(15) + 1; if (now + i > 256) { now = 256 - i; } @@ -429,9 +703,9 @@ void scalar_test(void) { #ifndef USE_NUM_NONE { /* Test that adding the scalars together is equal to adding their numbers together modulo the order. */ - secp256k1_num_t rnum; - secp256k1_num_t r2num; - secp256k1_scalar_t r; + 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); @@ -440,10 +714,10 @@ void scalar_test(void) { } { - /* Test that multipying the scalars is equal to multiplying their numbers modulo the order. */ - secp256k1_scalar_t r; - secp256k1_num_t r2num; - secp256k1_num_t rnum; + /* 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); @@ -457,9 +731,9 @@ void scalar_test(void) { } { - secp256k1_scalar_t neg; - secp256k1_num_t negnum; - secp256k1_num_t negnum2; + 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. */ @@ -484,12 +758,12 @@ void scalar_test(void) { { /* Test secp256k1_scalar_mul_shift_var. */ - secp256k1_scalar_t r; - secp256k1_num_t one; - secp256k1_num_t rnum; - secp256k1_num_t rnum2; + secp256k1_scalar r; + secp256k1_num one; + secp256k1_num rnum; + secp256k1_num rnum2; unsigned char cone[1] = {0x01}; - unsigned int shift = 256 + (secp256k1_rand32() % 257); + 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); @@ -499,15 +773,29 @@ void scalar_test(void) { 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_t inv; + secp256k1_scalar inv; #ifndef USE_NUM_NONE - secp256k1_num_t invnum; - secp256k1_num_t invnum2; + secp256k1_num invnum; + secp256k1_num invnum2; #endif secp256k1_scalar_inverse(&inv, &s); #ifndef USE_NUM_NONE @@ -521,23 +809,27 @@ void scalar_test(void) { 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_t r1, r2; + secp256k1_scalar r1, r2; secp256k1_scalar_add(&r1, &s1, &s2); secp256k1_scalar_add(&r2, &s2, &s1); CHECK(secp256k1_scalar_eq(&r1, &r2)); } { - secp256k1_scalar_t r1, r2; - secp256k1_scalar_t b; + secp256k1_scalar r1, r2; + secp256k1_scalar b; int i; /* Test add_bit. */ - int bit = secp256k1_rand32() % 256; + int bit = secp256k1_rand_bits(8); secp256k1_scalar_set_int(&b, 1); CHECK(secp256k1_scalar_is_one(&b)); for (i = 0; i < bit; i++) { @@ -547,14 +839,17 @@ void scalar_test(void) { r2 = s1; if (!secp256k1_scalar_add(&r1, &r1, &b)) { /* No overflow happened. */ - secp256k1_scalar_add_bit(&r2, bit); + secp256k1_scalar_cadd_bit(&r2, bit, 1); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + /* cadd is a noop when flag is zero */ + secp256k1_scalar_cadd_bit(&r2, bit, 0); CHECK(secp256k1_scalar_eq(&r1, &r2)); } } { /* Test commutativity of mul. */ - secp256k1_scalar_t r1, r2; + secp256k1_scalar r1, r2; secp256k1_scalar_mul(&r1, &s1, &s2); secp256k1_scalar_mul(&r2, &s2, &s1); CHECK(secp256k1_scalar_eq(&r1, &r2)); @@ -562,7 +857,7 @@ void scalar_test(void) { { /* Test associativity of add. */ - secp256k1_scalar_t r1, r2; + secp256k1_scalar r1, r2; secp256k1_scalar_add(&r1, &s1, &s2); secp256k1_scalar_add(&r1, &r1, &s); secp256k1_scalar_add(&r2, &s2, &s); @@ -572,7 +867,7 @@ void scalar_test(void) { { /* Test associativity of mul. */ - secp256k1_scalar_t r1, r2; + secp256k1_scalar r1, r2; secp256k1_scalar_mul(&r1, &s1, &s2); secp256k1_scalar_mul(&r1, &r1, &s); secp256k1_scalar_mul(&r2, &s2, &s); @@ -582,7 +877,7 @@ void scalar_test(void) { { /* Test distributitivity of mul over add. */ - secp256k1_scalar_t r1, r2, t; + secp256k1_scalar r1, r2, t; secp256k1_scalar_add(&r1, &s1, &s2); secp256k1_scalar_mul(&r1, &r1, &s); secp256k1_scalar_mul(&r2, &s1, &s); @@ -593,7 +888,7 @@ void scalar_test(void) { { /* Test square. */ - secp256k1_scalar_t r1, r2; + secp256k1_scalar r1, r2; secp256k1_scalar_sqr(&r1, &s1); secp256k1_scalar_mul(&r2, &s1, &s1); CHECK(secp256k1_scalar_eq(&r1, &r2)); @@ -601,7 +896,7 @@ void scalar_test(void) { { /* Test multiplicative identity. */ - secp256k1_scalar_t r1, v1; + secp256k1_scalar r1, v1; secp256k1_scalar_set_int(&v1,1); secp256k1_scalar_mul(&r1, &s1, &v1); CHECK(secp256k1_scalar_eq(&r1, &s1)); @@ -609,7 +904,7 @@ void scalar_test(void) { { /* Test additive identity. */ - secp256k1_scalar_t r1, v0; + secp256k1_scalar r1, v0; secp256k1_scalar_set_int(&v0,0); secp256k1_scalar_add(&r1, &s1, &v0); CHECK(secp256k1_scalar_eq(&r1, &s1)); @@ -617,7 +912,7 @@ void scalar_test(void) { { /* Test zero product property. */ - secp256k1_scalar_t r1, v0; + secp256k1_scalar r1, v0; secp256k1_scalar_set_int(&v0,0); secp256k1_scalar_mul(&r1, &s1, &v0); CHECK(secp256k1_scalar_eq(&r1, &v0)); @@ -633,7 +928,7 @@ void run_scalar_tests(void) { { /* (-1)+1 should be zero. */ - secp256k1_scalar_t s, o; + secp256k1_scalar s, o; secp256k1_scalar_set_int(&s, 1); CHECK(secp256k1_scalar_is_one(&s)); secp256k1_scalar_negate(&o, &s); @@ -646,8 +941,8 @@ void run_scalar_tests(void) { #ifndef USE_NUM_NONE { /* A scalar with value of the curve order should be 0. */ - secp256k1_num_t order; - secp256k1_scalar_t zero; + secp256k1_num order; + secp256k1_scalar zero; unsigned char bin[32]; int overflow = 0; secp256k1_scalar_order_get_num(&order); @@ -657,11 +952,605 @@ void run_scalar_tests(void) { CHECK(secp256k1_scalar_is_zero(&zero)); } #endif + + { + /* Does check_overflow check catch all ones? */ + static const secp256k1_scalar overflowed = SECP256K1_SCALAR_CONST( + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL + ); + CHECK(secp256k1_scalar_check_overflow(&overflowed)); + } + + { + /* Static test vectors. + * These were reduced from ~10^12 random vectors based on comparison-decision + * and edge-case coverage on 32-bit and 64-bit implementations. + * The responses were generated with Sage 5.9. + */ + secp256k1_scalar x; + 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, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, + 0xff, 0xff, 0x03, 0x00, 0xc0, 0xff, 0xff, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff}}, + {{0xef, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x80, 0xff}}, + {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0x3f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0x00}, + {0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x1e, 0xf8, 0xff, 0xff, 0xff, 0xfd, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, + 0x00, 0x00, 0x00, 0xf8, 0xff, 0x03, 0x00, 0xe0, + 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, + 0xf3, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x1c, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, + 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x1f, 0x00, 0x00, 0x80, 0xff, 0xff, 0x3f, + 0x00, 0xfe, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0xfc, 0x9f, + 0xff, 0xff, 0xff, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0x0f, 0xfc, 0xff, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, + {0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x00, 0x00, 0xf8, 0xff, 0x0f, 0xc0, 0xff, 0xff, + 0xff, 0x1f, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x07, 0x80, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, + 0xf7, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xf0}, + {0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0x00, 0xf8, 0xff, 0x03, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x03, 0xc0, 0xff, 0x0f, 0xfc, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, + 0xff, 0x01, 0x00, 0x00, 0x00, 0x3f, 0x00, 0xc0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0x8f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x7f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x03, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0xff, 0x7f}, + {0xff, 0xcf, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xff, + 0xbf, 0xff, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, + 0xff, 0xff, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0x01, 0xfc, 0xff, 0x01, 0x00, 0xfe, 0xff}, + {0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00}}, + {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x7f, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0xff, 0x01, 0x00, 0xf0, 0xff, 0xff, + 0xe0, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x00}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xfc, 0xff, 0xff, 0x3f, 0xf0, 0xff, 0xff, 0x3f, + 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x7e, 0x00, 0x00}}, + {{0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x1f, 0x00, 0x00, 0xfe, 0x07, 0x00}, + {0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xfb, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60}}, + {{0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, + 0x80, 0x7f, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0xff, 0xff, 0x1f, 0x00, 0xf0, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00}}, + {{0x80, 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}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, + 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0xff, 0xff, 0xcf, 0xff, 0x1f, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x7e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00}, + {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0x7f, 0x00, 0x80, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, + {0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x80, + 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x7f, 0xf8, 0xff, 0xff, 0x1f, 0x00, 0xfe}}, + {{0xff, 0xff, 0xff, 0x3f, 0xf8, 0xff, 0xff, 0xff, + 0xff, 0x03, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0x01, 0x80, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 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, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{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, 0xff}, + {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, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xc0, + 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff}}, + {{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, 0xff}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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}}, + {{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}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x7e, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x07, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0xff, 0x01, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, + 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, + 0xff, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x3f, 0x00, 0x00, 0xc0, 0xf1, 0x7f, 0x00}}, + {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x00}, + {0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, + 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1f, + 0x00, 0x00, 0xfc, 0xff, 0xff, 0x01, 0xff, 0xff}}, + {{0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0x03, 0xe0, 0x01, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xfc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, + {0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xff, 0xff, 0xf0, 0x07, 0x00, 0x3c, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x07, 0xe0, 0xff, 0x00, 0x00, 0x00}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x80, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x7f, 0xfe, 0xff, 0x1f, + 0x00, 0xfe, 0xff, 0x03, 0x00, 0x00, 0xfe, 0xff}}, + {{0xff, 0xff, 0x81, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, + 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xf0}, + {0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, + 0xf8, 0x07, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xc7, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff}}, + {{0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0x03, 0xfb, + 0xfa, 0x8a, 0x7d, 0xdf, 0x13, 0x86, 0xe2, 0x03}, + {0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0x03, 0xfb, + 0xfa, 0x8a, 0x7d, 0xdf, 0x13, 0x86, 0xe2, 0x03}} + }; + unsigned char res[33][2][32] = { + {{0x0c, 0x3b, 0x0a, 0xca, 0x8d, 0x1a, 0x2f, 0xb9, + 0x8a, 0x7b, 0x53, 0x5a, 0x1f, 0xc5, 0x22, 0xa1, + 0x07, 0x2a, 0x48, 0xea, 0x02, 0xeb, 0xb3, 0xd6, + 0x20, 0x1e, 0x86, 0xd0, 0x95, 0xf6, 0x92, 0x35}, + {0xdc, 0x90, 0x7a, 0x07, 0x2e, 0x1e, 0x44, 0x6d, + 0xf8, 0x15, 0x24, 0x5b, 0x5a, 0x96, 0x37, 0x9c, + 0x37, 0x7b, 0x0d, 0xac, 0x1b, 0x65, 0x58, 0x49, + 0x43, 0xb7, 0x31, 0xbb, 0xa7, 0xf4, 0x97, 0x15}}, + {{0xf1, 0xf7, 0x3a, 0x50, 0xe6, 0x10, 0xba, 0x22, + 0x43, 0x4d, 0x1f, 0x1f, 0x7c, 0x27, 0xca, 0x9c, + 0xb8, 0xb6, 0xa0, 0xfc, 0xd8, 0xc0, 0x05, 0x2f, + 0xf7, 0x08, 0xe1, 0x76, 0xdd, 0xd0, 0x80, 0xc8}, + {0xe3, 0x80, 0x80, 0xb8, 0xdb, 0xe3, 0xa9, 0x77, + 0x00, 0xb0, 0xf5, 0x2e, 0x27, 0xe2, 0x68, 0xc4, + 0x88, 0xe8, 0x04, 0xc1, 0x12, 0xbf, 0x78, 0x59, + 0xe6, 0xa9, 0x7c, 0xe1, 0x81, 0xdd, 0xb9, 0xd5}}, + {{0x96, 0xe2, 0xee, 0x01, 0xa6, 0x80, 0x31, 0xef, + 0x5c, 0xd0, 0x19, 0xb4, 0x7d, 0x5f, 0x79, 0xab, + 0xa1, 0x97, 0xd3, 0x7e, 0x33, 0xbb, 0x86, 0x55, + 0x60, 0x20, 0x10, 0x0d, 0x94, 0x2d, 0x11, 0x7c}, + {0xcc, 0xab, 0xe0, 0xe8, 0x98, 0x65, 0x12, 0x96, + 0x38, 0x5a, 0x1a, 0xf2, 0x85, 0x23, 0x59, 0x5f, + 0xf9, 0xf3, 0xc2, 0x81, 0x70, 0x92, 0x65, 0x12, + 0x9c, 0x65, 0x1e, 0x96, 0x00, 0xef, 0xe7, 0x63}}, + {{0xac, 0x1e, 0x62, 0xc2, 0x59, 0xfc, 0x4e, 0x5c, + 0x83, 0xb0, 0xd0, 0x6f, 0xce, 0x19, 0xf6, 0xbf, + 0xa4, 0xb0, 0xe0, 0x53, 0x66, 0x1f, 0xbf, 0xc9, + 0x33, 0x47, 0x37, 0xa9, 0x3d, 0x5d, 0xb0, 0x48}, + {0x86, 0xb9, 0x2a, 0x7f, 0x8e, 0xa8, 0x60, 0x42, + 0x26, 0x6d, 0x6e, 0x1c, 0xa2, 0xec, 0xe0, 0xe5, + 0x3e, 0x0a, 0x33, 0xbb, 0x61, 0x4c, 0x9f, 0x3c, + 0xd1, 0xdf, 0x49, 0x33, 0xcd, 0x72, 0x78, 0x18}}, + {{0xf7, 0xd3, 0xcd, 0x49, 0x5c, 0x13, 0x22, 0xfb, + 0x2e, 0xb2, 0x2f, 0x27, 0xf5, 0x8a, 0x5d, 0x74, + 0xc1, 0x58, 0xc5, 0xc2, 0x2d, 0x9f, 0x52, 0xc6, + 0x63, 0x9f, 0xba, 0x05, 0x76, 0x45, 0x7a, 0x63}, + {0x8a, 0xfa, 0x55, 0x4d, 0xdd, 0xa3, 0xb2, 0xc3, + 0x44, 0xfd, 0xec, 0x72, 0xde, 0xef, 0xc0, 0x99, + 0xf5, 0x9f, 0xe2, 0x52, 0xb4, 0x05, 0x32, 0x58, + 0x57, 0xc1, 0x8f, 0xea, 0xc3, 0x24, 0x5b, 0x94}}, + {{0x05, 0x83, 0xee, 0xdd, 0x64, 0xf0, 0x14, 0x3b, + 0xa0, 0x14, 0x4a, 0x3a, 0x41, 0x82, 0x7c, 0xa7, + 0x2c, 0xaa, 0xb1, 0x76, 0xbb, 0x59, 0x64, 0x5f, + 0x52, 0xad, 0x25, 0x29, 0x9d, 0x8f, 0x0b, 0xb0}, + {0x7e, 0xe3, 0x7c, 0xca, 0xcd, 0x4f, 0xb0, 0x6d, + 0x7a, 0xb2, 0x3e, 0xa0, 0x08, 0xb9, 0xa8, 0x2d, + 0xc2, 0xf4, 0x99, 0x66, 0xcc, 0xac, 0xd8, 0xb9, + 0x72, 0x2a, 0x4a, 0x3e, 0x0f, 0x7b, 0xbf, 0xf4}}, + {{0x8c, 0x9c, 0x78, 0x2b, 0x39, 0x61, 0x7e, 0xf7, + 0x65, 0x37, 0x66, 0x09, 0x38, 0xb9, 0x6f, 0x70, + 0x78, 0x87, 0xff, 0xcf, 0x93, 0xca, 0x85, 0x06, + 0x44, 0x84, 0xa7, 0xfe, 0xd3, 0xa4, 0xe3, 0x7e}, + {0xa2, 0x56, 0x49, 0x23, 0x54, 0xa5, 0x50, 0xe9, + 0x5f, 0xf0, 0x4d, 0xe7, 0xdc, 0x38, 0x32, 0x79, + 0x4f, 0x1c, 0xb7, 0xe4, 0xbb, 0xf8, 0xbb, 0x2e, + 0x40, 0x41, 0x4b, 0xcc, 0xe3, 0x1e, 0x16, 0x36}}, + {{0x0c, 0x1e, 0xd7, 0x09, 0x25, 0x40, 0x97, 0xcb, + 0x5c, 0x46, 0xa8, 0xda, 0xef, 0x25, 0xd5, 0xe5, + 0x92, 0x4d, 0xcf, 0xa3, 0xc4, 0x5d, 0x35, 0x4a, + 0xe4, 0x61, 0x92, 0xf3, 0xbf, 0x0e, 0xcd, 0xbe}, + {0xe4, 0xaf, 0x0a, 0xb3, 0x30, 0x8b, 0x9b, 0x48, + 0x49, 0x43, 0xc7, 0x64, 0x60, 0x4a, 0x2b, 0x9e, + 0x95, 0x5f, 0x56, 0xe8, 0x35, 0xdc, 0xeb, 0xdc, + 0xc7, 0xc4, 0xfe, 0x30, 0x40, 0xc7, 0xbf, 0xa4}}, + {{0xd4, 0xa0, 0xf5, 0x81, 0x49, 0x6b, 0xb6, 0x8b, + 0x0a, 0x69, 0xf9, 0xfe, 0xa8, 0x32, 0xe5, 0xe0, + 0xa5, 0xcd, 0x02, 0x53, 0xf9, 0x2c, 0xe3, 0x53, + 0x83, 0x36, 0xc6, 0x02, 0xb5, 0xeb, 0x64, 0xb8}, + {0x1d, 0x42, 0xb9, 0xf9, 0xe9, 0xe3, 0x93, 0x2c, + 0x4c, 0xee, 0x6c, 0x5a, 0x47, 0x9e, 0x62, 0x01, + 0x6b, 0x04, 0xfe, 0xa4, 0x30, 0x2b, 0x0d, 0x4f, + 0x71, 0x10, 0xd3, 0x55, 0xca, 0xf3, 0x5e, 0x80}}, + {{0x77, 0x05, 0xf6, 0x0c, 0x15, 0x9b, 0x45, 0xe7, + 0xb9, 0x11, 0xb8, 0xf5, 0xd6, 0xda, 0x73, 0x0c, + 0xda, 0x92, 0xea, 0xd0, 0x9d, 0xd0, 0x18, 0x92, + 0xce, 0x9a, 0xaa, 0xee, 0x0f, 0xef, 0xde, 0x30}, + {0xf1, 0xf1, 0xd6, 0x9b, 0x51, 0xd7, 0x77, 0x62, + 0x52, 0x10, 0xb8, 0x7a, 0x84, 0x9d, 0x15, 0x4e, + 0x07, 0xdc, 0x1e, 0x75, 0x0d, 0x0c, 0x3b, 0xdb, + 0x74, 0x58, 0x62, 0x02, 0x90, 0x54, 0x8b, 0x43}}, + {{0xa6, 0xfe, 0x0b, 0x87, 0x80, 0x43, 0x67, 0x25, + 0x57, 0x5d, 0xec, 0x40, 0x50, 0x08, 0xd5, 0x5d, + 0x43, 0xd7, 0xe0, 0xaa, 0xe0, 0x13, 0xb6, 0xb0, + 0xc0, 0xd4, 0xe5, 0x0d, 0x45, 0x83, 0xd6, 0x13}, + {0x40, 0x45, 0x0a, 0x92, 0x31, 0xea, 0x8c, 0x60, + 0x8c, 0x1f, 0xd8, 0x76, 0x45, 0xb9, 0x29, 0x00, + 0x26, 0x32, 0xd8, 0xa6, 0x96, 0x88, 0xe2, 0xc4, + 0x8b, 0xdb, 0x7f, 0x17, 0x87, 0xcc, 0xc8, 0xf2}}, + {{0xc2, 0x56, 0xe2, 0xb6, 0x1a, 0x81, 0xe7, 0x31, + 0x63, 0x2e, 0xbb, 0x0d, 0x2f, 0x81, 0x67, 0xd4, + 0x22, 0xe2, 0x38, 0x02, 0x25, 0x97, 0xc7, 0x88, + 0x6e, 0xdf, 0xbe, 0x2a, 0xa5, 0x73, 0x63, 0xaa}, + {0x50, 0x45, 0xe2, 0xc3, 0xbd, 0x89, 0xfc, 0x57, + 0xbd, 0x3c, 0xa3, 0x98, 0x7e, 0x7f, 0x36, 0x38, + 0x92, 0x39, 0x1f, 0x0f, 0x81, 0x1a, 0x06, 0x51, + 0x1f, 0x8d, 0x6a, 0xff, 0x47, 0x16, 0x06, 0x9c}}, + {{0x33, 0x95, 0xa2, 0x6f, 0x27, 0x5f, 0x9c, 0x9c, + 0x64, 0x45, 0xcb, 0xd1, 0x3c, 0xee, 0x5e, 0x5f, + 0x48, 0xa6, 0xaf, 0xe3, 0x79, 0xcf, 0xb1, 0xe2, + 0xbf, 0x55, 0x0e, 0xa2, 0x3b, 0x62, 0xf0, 0xe4}, + {0x14, 0xe8, 0x06, 0xe3, 0xbe, 0x7e, 0x67, 0x01, + 0xc5, 0x21, 0x67, 0xd8, 0x54, 0xb5, 0x7f, 0xa4, + 0xf9, 0x75, 0x70, 0x1c, 0xfd, 0x79, 0xdb, 0x86, + 0xad, 0x37, 0x85, 0x83, 0x56, 0x4e, 0xf0, 0xbf}}, + {{0xbc, 0xa6, 0xe0, 0x56, 0x4e, 0xef, 0xfa, 0xf5, + 0x1d, 0x5d, 0x3f, 0x2a, 0x5b, 0x19, 0xab, 0x51, + 0xc5, 0x8b, 0xdd, 0x98, 0x28, 0x35, 0x2f, 0xc3, + 0x81, 0x4f, 0x5c, 0xe5, 0x70, 0xb9, 0xeb, 0x62}, + {0xc4, 0x6d, 0x26, 0xb0, 0x17, 0x6b, 0xfe, 0x6c, + 0x12, 0xf8, 0xe7, 0xc1, 0xf5, 0x2f, 0xfa, 0x91, + 0x13, 0x27, 0xbd, 0x73, 0xcc, 0x33, 0x31, 0x1c, + 0x39, 0xe3, 0x27, 0x6a, 0x95, 0xcf, 0xc5, 0xfb}}, + {{0x30, 0xb2, 0x99, 0x84, 0xf0, 0x18, 0x2a, 0x6e, + 0x1e, 0x27, 0xed, 0xa2, 0x29, 0x99, 0x41, 0x56, + 0xe8, 0xd4, 0x0d, 0xef, 0x99, 0x9c, 0xf3, 0x58, + 0x29, 0x55, 0x1a, 0xc0, 0x68, 0xd6, 0x74, 0xa4}, + {0x07, 0x9c, 0xe7, 0xec, 0xf5, 0x36, 0x73, 0x41, + 0xa3, 0x1c, 0xe5, 0x93, 0x97, 0x6a, 0xfd, 0xf7, + 0x53, 0x18, 0xab, 0xaf, 0xeb, 0x85, 0xbd, 0x92, + 0x90, 0xab, 0x3c, 0xbf, 0x30, 0x82, 0xad, 0xf6}}, + {{0xc6, 0x87, 0x8a, 0x2a, 0xea, 0xc0, 0xa9, 0xec, + 0x6d, 0xd3, 0xdc, 0x32, 0x23, 0xce, 0x62, 0x19, + 0xa4, 0x7e, 0xa8, 0xdd, 0x1c, 0x33, 0xae, 0xd3, + 0x4f, 0x62, 0x9f, 0x52, 0xe7, 0x65, 0x46, 0xf4}, + {0x97, 0x51, 0x27, 0x67, 0x2d, 0xa2, 0x82, 0x87, + 0x98, 0xd3, 0xb6, 0x14, 0x7f, 0x51, 0xd3, 0x9a, + 0x0b, 0xd0, 0x76, 0x81, 0xb2, 0x4f, 0x58, 0x92, + 0xa4, 0x86, 0xa1, 0xa7, 0x09, 0x1d, 0xef, 0x9b}}, + {{0xb3, 0x0f, 0x2b, 0x69, 0x0d, 0x06, 0x90, 0x64, + 0xbd, 0x43, 0x4c, 0x10, 0xe8, 0x98, 0x1c, 0xa3, + 0xe1, 0x68, 0xe9, 0x79, 0x6c, 0x29, 0x51, 0x3f, + 0x41, 0xdc, 0xdf, 0x1f, 0xf3, 0x60, 0xbe, 0x33}, + {0xa1, 0x5f, 0xf7, 0x1d, 0xb4, 0x3e, 0x9b, 0x3c, + 0xe7, 0xbd, 0xb6, 0x06, 0xd5, 0x60, 0x06, 0x6d, + 0x50, 0xd2, 0xf4, 0x1a, 0x31, 0x08, 0xf2, 0xea, + 0x8e, 0xef, 0x5f, 0x7d, 0xb6, 0xd0, 0xc0, 0x27}}, + {{0x62, 0x9a, 0xd9, 0xbb, 0x38, 0x36, 0xce, 0xf7, + 0x5d, 0x2f, 0x13, 0xec, 0xc8, 0x2d, 0x02, 0x8a, + 0x2e, 0x72, 0xf0, 0xe5, 0x15, 0x9d, 0x72, 0xae, + 0xfc, 0xb3, 0x4f, 0x02, 0xea, 0xe1, 0x09, 0xfe}, + {0x00, 0x00, 0x00, 0x00, 0xfa, 0x0a, 0x3d, 0xbc, + 0xad, 0x16, 0x0c, 0xb6, 0xe7, 0x7c, 0x8b, 0x39, + 0x9a, 0x43, 0xbb, 0xe3, 0xc2, 0x55, 0x15, 0x14, + 0x75, 0xac, 0x90, 0x9b, 0x7f, 0x9a, 0x92, 0x00}}, + {{0x8b, 0xac, 0x70, 0x86, 0x29, 0x8f, 0x00, 0x23, + 0x7b, 0x45, 0x30, 0xaa, 0xb8, 0x4c, 0xc7, 0x8d, + 0x4e, 0x47, 0x85, 0xc6, 0x19, 0xe3, 0x96, 0xc2, + 0x9a, 0xa0, 0x12, 0xed, 0x6f, 0xd7, 0x76, 0x16}, + {0x45, 0xaf, 0x7e, 0x33, 0xc7, 0x7f, 0x10, 0x6c, + 0x7c, 0x9f, 0x29, 0xc1, 0xa8, 0x7e, 0x15, 0x84, + 0xe7, 0x7d, 0xc0, 0x6d, 0xab, 0x71, 0x5d, 0xd0, + 0x6b, 0x9f, 0x97, 0xab, 0xcb, 0x51, 0x0c, 0x9f}}, + {{0x9e, 0xc3, 0x92, 0xb4, 0x04, 0x9f, 0xc8, 0xbb, + 0xdd, 0x9e, 0xc6, 0x05, 0xfd, 0x65, 0xec, 0x94, + 0x7f, 0x2c, 0x16, 0xc4, 0x40, 0xac, 0x63, 0x7b, + 0x7d, 0xb8, 0x0c, 0xe4, 0x5b, 0xe3, 0xa7, 0x0e}, + {0x43, 0xf4, 0x44, 0xe8, 0xcc, 0xc8, 0xd4, 0x54, + 0x33, 0x37, 0x50, 0xf2, 0x87, 0x42, 0x2e, 0x00, + 0x49, 0x60, 0x62, 0x02, 0xfd, 0x1a, 0x7c, 0xdb, + 0x29, 0x6c, 0x6d, 0x54, 0x53, 0x08, 0xd1, 0xc8}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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}}, + {{0x27, 0x59, 0xc7, 0x35, 0x60, 0x71, 0xa6, 0xf1, + 0x79, 0xa5, 0xfd, 0x79, 0x16, 0xf3, 0x41, 0xf0, + 0x57, 0xb4, 0x02, 0x97, 0x32, 0xe7, 0xde, 0x59, + 0xe2, 0x2d, 0x9b, 0x11, 0xea, 0x2c, 0x35, 0x92}, + {0x27, 0x59, 0xc7, 0x35, 0x60, 0x71, 0xa6, 0xf1, + 0x79, 0xa5, 0xfd, 0x79, 0x16, 0xf3, 0x41, 0xf0, + 0x57, 0xb4, 0x02, 0x97, 0x32, 0xe7, 0xde, 0x59, + 0xe2, 0x2d, 0x9b, 0x11, 0xea, 0x2c, 0x35, 0x92}}, + {{0x28, 0x56, 0xac, 0x0e, 0x4f, 0x98, 0x09, 0xf0, + 0x49, 0xfa, 0x7f, 0x84, 0xac, 0x7e, 0x50, 0x5b, + 0x17, 0x43, 0x14, 0x89, 0x9c, 0x53, 0xa8, 0x94, + 0x30, 0xf2, 0x11, 0x4d, 0x92, 0x14, 0x27, 0xe8}, + {0x39, 0x7a, 0x84, 0x56, 0x79, 0x9d, 0xec, 0x26, + 0x2c, 0x53, 0xc1, 0x94, 0xc9, 0x8d, 0x9e, 0x9d, + 0x32, 0x1f, 0xdd, 0x84, 0x04, 0xe8, 0xe2, 0x0a, + 0x6b, 0xbe, 0xbb, 0x42, 0x40, 0x67, 0x30, 0x6c}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x45, 0x51, 0x23, 0x19, 0x50, 0xb7, 0x5f, 0xc4, + 0x40, 0x2d, 0xa1, 0x73, 0x2f, 0xc9, 0xbe, 0xbd}, + {0x27, 0x59, 0xc7, 0x35, 0x60, 0x71, 0xa6, 0xf1, + 0x79, 0xa5, 0xfd, 0x79, 0x16, 0xf3, 0x41, 0xf0, + 0x57, 0xb4, 0x02, 0x97, 0x32, 0xe7, 0xde, 0x59, + 0xe2, 0x2d, 0x9b, 0x11, 0xea, 0x2c, 0x35, 0x92}}, + {{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}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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}}, + {{0x1c, 0xc4, 0xf7, 0xda, 0x0f, 0x65, 0xca, 0x39, + 0x70, 0x52, 0x92, 0x8e, 0xc3, 0xc8, 0x15, 0xea, + 0x7f, 0x10, 0x9e, 0x77, 0x4b, 0x6e, 0x2d, 0xdf, + 0xe8, 0x30, 0x9d, 0xda, 0xe8, 0x9a, 0x65, 0xae}, + {0x02, 0xb0, 0x16, 0xb1, 0x1d, 0xc8, 0x57, 0x7b, + 0xa2, 0x3a, 0xa2, 0xa3, 0x38, 0x5c, 0x8f, 0xeb, + 0x66, 0x37, 0x91, 0xa8, 0x5f, 0xef, 0x04, 0xf6, + 0x59, 0x75, 0xe1, 0xee, 0x92, 0xf6, 0x0e, 0x30}}, + {{0x8d, 0x76, 0x14, 0xa4, 0x14, 0x06, 0x9f, 0x9a, + 0xdf, 0x4a, 0x85, 0xa7, 0x6b, 0xbf, 0x29, 0x6f, + 0xbc, 0x34, 0x87, 0x5d, 0xeb, 0xbb, 0x2e, 0xa9, + 0xc9, 0x1f, 0x58, 0xd6, 0x9a, 0x82, 0xa0, 0x56}, + {0xd4, 0xb9, 0xdb, 0x88, 0x1d, 0x04, 0xe9, 0x93, + 0x8d, 0x3f, 0x20, 0xd5, 0x86, 0xa8, 0x83, 0x07, + 0xdb, 0x09, 0xd8, 0x22, 0x1f, 0x7f, 0xf1, 0x71, + 0xc8, 0xe7, 0x5d, 0x47, 0xaf, 0x8b, 0x72, 0xe9}}, + {{0x83, 0xb9, 0x39, 0xb2, 0xa4, 0xdf, 0x46, 0x87, + 0xc2, 0xb8, 0xf1, 0xe6, 0x4c, 0xd1, 0xe2, 0xa9, + 0xe4, 0x70, 0x30, 0x34, 0xbc, 0x52, 0x7c, 0x55, + 0xa6, 0xec, 0x80, 0xa4, 0xe5, 0xd2, 0xdc, 0x73}, + {0x08, 0xf1, 0x03, 0xcf, 0x16, 0x73, 0xe8, 0x7d, + 0xb6, 0x7e, 0x9b, 0xc0, 0xb4, 0xc2, 0xa5, 0x86, + 0x02, 0x77, 0xd5, 0x27, 0x86, 0xa5, 0x15, 0xfb, + 0xae, 0x9b, 0x8c, 0xa9, 0xf9, 0xf8, 0xa8, 0x4a}}, + {{0x8b, 0x00, 0x49, 0xdb, 0xfa, 0xf0, 0x1b, 0xa2, + 0xed, 0x8a, 0x9a, 0x7a, 0x36, 0x78, 0x4a, 0xc7, + 0xf7, 0xad, 0x39, 0xd0, 0x6c, 0x65, 0x7a, 0x41, + 0xce, 0xd6, 0xd6, 0x4c, 0x20, 0x21, 0x6b, 0xc7}, + {0xc6, 0xca, 0x78, 0x1d, 0x32, 0x6c, 0x6c, 0x06, + 0x91, 0xf2, 0x1a, 0xe8, 0x43, 0x16, 0xea, 0x04, + 0x3c, 0x1f, 0x07, 0x85, 0xf7, 0x09, 0x22, 0x08, + 0xba, 0x13, 0xfd, 0x78, 0x1e, 0x3f, 0x6f, 0x62}}, + {{0x25, 0x9b, 0x7c, 0xb0, 0xac, 0x72, 0x6f, 0xb2, + 0xe3, 0x53, 0x84, 0x7a, 0x1a, 0x9a, 0x98, 0x9b, + 0x44, 0xd3, 0x59, 0xd0, 0x8e, 0x57, 0x41, 0x40, + 0x78, 0xa7, 0x30, 0x2f, 0x4c, 0x9c, 0xb9, 0x68}, + {0xb7, 0x75, 0x03, 0x63, 0x61, 0xc2, 0x48, 0x6e, + 0x12, 0x3d, 0xbf, 0x4b, 0x27, 0xdf, 0xb1, 0x7a, + 0xff, 0x4e, 0x31, 0x07, 0x83, 0xf4, 0x62, 0x5b, + 0x19, 0xa5, 0xac, 0xa0, 0x32, 0x58, 0x0d, 0xa7}}, + {{0x43, 0x4f, 0x10, 0xa4, 0xca, 0xdb, 0x38, 0x67, + 0xfa, 0xae, 0x96, 0xb5, 0x6d, 0x97, 0xff, 0x1f, + 0xb6, 0x83, 0x43, 0xd3, 0xa0, 0x2d, 0x70, 0x7a, + 0x64, 0x05, 0x4c, 0xa7, 0xc1, 0xa5, 0x21, 0x51}, + {0xe4, 0xf1, 0x23, 0x84, 0xe1, 0xb5, 0x9d, 0xf2, + 0xb8, 0x73, 0x8b, 0x45, 0x2b, 0x35, 0x46, 0x38, + 0x10, 0x2b, 0x50, 0xf8, 0x8b, 0x35, 0xcd, 0x34, + 0xc8, 0x0e, 0xf6, 0xdb, 0x09, 0x35, 0xf0, 0xda}}, + {{0xdb, 0x21, 0x5c, 0x8d, 0x83, 0x1d, 0xb3, 0x34, + 0xc7, 0x0e, 0x43, 0xa1, 0x58, 0x79, 0x67, 0x13, + 0x1e, 0x86, 0x5d, 0x89, 0x63, 0xe6, 0x0a, 0x46, + 0x5c, 0x02, 0x97, 0x1b, 0x62, 0x43, 0x86, 0xf5}, + {0xdb, 0x21, 0x5c, 0x8d, 0x83, 0x1d, 0xb3, 0x34, + 0xc7, 0x0e, 0x43, 0xa1, 0x58, 0x79, 0x67, 0x13, + 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); + secp256k1_scalar_set_b32(&y, chal[i][1], &overflow); + CHECK(!overflow); + secp256k1_scalar_set_b32(&r1, res[i][0], &overflow); + CHECK(!overflow); + 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)); + } + 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)); + } + } } /***** FIELD TESTS *****/ -void random_fe(secp256k1_fe_t *x) { +void random_fe(secp256k1_fe *x) { unsigned char bin[32]; do { secp256k1_rand256(bin); @@ -671,7 +1560,17 @@ void random_fe(secp256k1_fe_t *x) { } while(1); } -void random_fe_non_zero(secp256k1_fe_t *nz) { +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); @@ -684,25 +1583,25 @@ void random_fe_non_zero(secp256k1_fe_t *nz) { CHECK(tries >= 0); } -void random_fe_non_square(secp256k1_fe_t *ns) { - secp256k1_fe_t r; +void random_fe_non_square(secp256k1_fe *ns) { + secp256k1_fe r; random_fe_non_zero(ns); - if (secp256k1_fe_sqrt_var(&r, ns)) { + if (secp256k1_fe_sqrt(&r, ns)) { secp256k1_fe_negate(ns, ns, 1); } } -int check_fe_equal(const secp256k1_fe_t *a, const secp256k1_fe_t *b) { - secp256k1_fe_t an = *a; - secp256k1_fe_t bn = *b; +int check_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_t *a, const secp256k1_fe_t *ai) { - secp256k1_fe_t x; - secp256k1_fe_t one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1); +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); } @@ -714,17 +1613,17 @@ void run_field_convert(void) { 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40 }; - static const secp256k1_fe_storage_t fes = SECP256K1_FE_STORAGE_CONST( + static const secp256k1_fe_storage fes = SECP256K1_FE_STORAGE_CONST( 0x00010203UL, 0x04050607UL, 0x11121314UL, 0x15161718UL, 0x22232425UL, 0x26272829UL, 0x33343536UL, 0x37383940UL ); - static const secp256k1_fe_t fe = SECP256K1_FE_CONST( + static const secp256k1_fe fe = SECP256K1_FE_CONST( 0x00010203UL, 0x04050607UL, 0x11121314UL, 0x15161718UL, 0x22232425UL, 0x26272829UL, 0x33343536UL, 0x37383940UL ); - secp256k1_fe_t fe2; + secp256k1_fe fe2; unsigned char b322[32]; - secp256k1_fe_storage_t fes2; + secp256k1_fe_storage fes2; /* Check conversions to fe. */ CHECK(secp256k1_fe_set_b32(&fe2, b32)); CHECK(secp256k1_fe_equal_var(&fe, &fe2)); @@ -737,15 +1636,24 @@ void run_field_convert(void) { CHECK(memcmp(&fes2, &fes, sizeof(fes)) == 0); } +int fe_memcmp(const secp256k1_fe *a, const secp256k1_fe *b) { + secp256k1_fe t = *b; +#ifdef VERIFY + t.magnitude = a->magnitude; + t.normalized = a->normalized; +#endif + return memcmp(a, &t, sizeof(secp256k1_fe)); +} + void run_field_misc(void) { - secp256k1_fe_t x; - secp256k1_fe_t y; - secp256k1_fe_t z; - secp256k1_fe_t q; - secp256k1_fe_t fe5 = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 5); - int i; + secp256k1_fe x; + secp256k1_fe y; + secp256k1_fe z; + secp256k1_fe q; + secp256k1_fe fe5 = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 5); + int i, j; for (i = 0; i < 5*count; i++) { - secp256k1_fe_storage_t xs, ys, zs; + secp256k1_fe_storage xs, ys, zs; random_fe(&x); random_fe_non_zero(&y); /* Test the fe equality and comparison operations. */ @@ -756,14 +1664,27 @@ void run_field_misc(void) { /* 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); secp256k1_fe_cmov(&x, &x, 1); - CHECK(memcmp(&x, &z, sizeof(x)) != 0); - CHECK(memcmp(&x, &q, sizeof(x)) == 0); + CHECK(fe_memcmp(&x, &z) != 0); + CHECK(fe_memcmp(&x, &q) == 0); secp256k1_fe_cmov(&q, &z, 1); - CHECK(memcmp(&q, &z, sizeof(q)) == 0); - /* Test storage conversion and conditional moves. */ - secp256k1_fe_normalize(&z); + VERIFY_CHECK(!q.normalized && q.magnitude == z.magnitude); + CHECK(fe_memcmp(&q, &z) == 0); + secp256k1_fe_normalize_var(&x); + secp256k1_fe_normalize_var(&z); CHECK(!secp256k1_fe_equal_var(&x, &z)); + secp256k1_fe_normalize_var(&q); + secp256k1_fe_cmov(&q, &z, (i&1)); + VERIFY_CHECK(q.normalized && q.magnitude == 1); + for (j = 0; j < 6; j++) { + secp256k1_fe_negate(&z, &z, j+1); + secp256k1_fe_normalize_var(&q); + secp256k1_fe_cmov(&q, &z, (j&1)); + VERIFY_CHECK(!q.normalized && q.magnitude == (j+2)); + } + secp256k1_fe_normalize_var(&z); + /* Test storage conversion and conditional moves. */ secp256k1_fe_to_storage(&xs, &x); secp256k1_fe_to_storage(&ys, &y); secp256k1_fe_to_storage(&zs, &z); @@ -797,7 +1718,7 @@ void run_field_misc(void) { } void run_field_inv(void) { - secp256k1_fe_t x, xi, xii; + secp256k1_fe x, xi, xii; int i; for (i = 0; i < 10*count; i++) { random_fe_non_zero(&x); @@ -809,7 +1730,7 @@ void run_field_inv(void) { } void run_field_inv_var(void) { - secp256k1_fe_t x, xi, xii; + secp256k1_fe x, xi, xii; int i; for (i = 0; i < 10*count; i++) { random_fe_non_zero(&x); @@ -821,21 +1742,21 @@ void run_field_inv_var(void) { } void run_field_inv_all_var(void) { - secp256k1_fe_t x[16], xi[16], xii[16]; + secp256k1_fe x[16], xi[16], xii[16]; int i; /* Check it's safe to call for 0 elements */ - secp256k1_fe_inv_all_var(0, xi, x); + secp256k1_fe_inv_all_var(xi, x, 0); for (i = 0; i < count; i++) { size_t j; - size_t len = (secp256k1_rand32() & 15) + 1; + 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(len, xi, x); + 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(len, xii, xi); + secp256k1_fe_inv_all_var(xii, xi, len); for (j = 0; j < len; j++) { CHECK(check_fe_equal(&x[j], &xii[j])); } @@ -843,7 +1764,7 @@ void run_field_inv_all_var(void) { } void run_sqr(void) { - secp256k1_fe_t x, s; + secp256k1_fe x, s; { int i; @@ -858,9 +1779,9 @@ void run_sqr(void) { } } -void test_sqrt(const secp256k1_fe_t *a, const secp256k1_fe_t *k) { - secp256k1_fe_t r1, r2; - int v = secp256k1_fe_sqrt_var(&r1, a); +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)); if (k != NULL) { @@ -873,7 +1794,7 @@ void test_sqrt(const secp256k1_fe_t *a, const secp256k1_fe_t *k) { } void run_sqrt(void) { - secp256k1_fe_t ns, x, s, t; + secp256k1_fe ns, x, s, t; int i; /* Check sqrt(0) is 0 */ @@ -908,19 +1829,19 @@ void run_sqrt(void) { /***** GROUP TESTS *****/ -void ge_equals_ge(const secp256k1_ge_t *a, const secp256k1_ge_t *b) { +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(&b->y, &b->y)); + CHECK(secp256k1_fe_equal_var(&a->y, &b->y)); } /* This compares jacobian points including their Z, not just their geometric meaning. */ -int gej_xyz_equals_gej(const secp256k1_gej_t *a, const secp256k1_gej_t *b) { - secp256k1_gej_t a2; - secp256k1_gej_t b2; +int gej_xyz_equals_gej(const secp256k1_gej *a, const secp256k1_gej *b) { + secp256k1_gej a2; + secp256k1_gej b2; int ret = 1; ret &= a->infinity == b->infinity; if (ret && !a->infinity) { @@ -939,9 +1860,9 @@ int gej_xyz_equals_gej(const secp256k1_gej_t *a, const secp256k1_gej_t *b) { return ret; } -void ge_equals_gej(const secp256k1_ge_t *a, const secp256k1_gej_t *b) { - secp256k1_fe_t z2s; - secp256k1_fe_t u1, u2, s1, s2; +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; @@ -958,21 +1879,39 @@ void ge_equals_gej(const secp256k1_ge_t *a, const secp256k1_gej_t *b) { 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 eachother, using all applicable methods. + * 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. */ - secp256k1_ge_t *ge = (secp256k1_ge_t *)malloc(sizeof(secp256k1_ge_t) * (1 + 4 * runs)); - secp256k1_gej_t *gej = (secp256k1_gej_t *)malloc(sizeof(secp256k1_gej_t) * (1 + 4 * runs)); + 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_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; - secp256k1_ge_t g; + secp256k1_ge g; random_group_element_test(&g); +#ifdef USE_ENDOMORPHISM + if (i >= runs - 2) { + secp256k1_ge_mul_lambda(&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); @@ -990,18 +1929,65 @@ void test_ge(void) { } } + /* 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; + } + } + 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); + secp256k1_fe_inv_var(&zfi3, &zf); + secp256k1_fe_sqr(&zfi2, &zfi3); + secp256k1_fe_mul(&zfi3, &zfi3, &zfi2); + for (i1 = 0; i1 < 1 + 4 * runs; i1++) { int i2; for (i2 = 0; i2 < 1 + 4 * runs; i2++) { /* Compute reference result using gej + gej (var). */ - secp256k1_gej_t refj, resj; - secp256k1_ge_t ref; - secp256k1_gej_add_var(&refj, &gej[i1], &gej[i2]); + secp256k1_gej refj, resj; + secp256k1_ge ref; + secp256k1_fe zr; + secp256k1_gej_add_var(&refj, &gej[i1], &gej[i2], secp256k1_gej_is_infinity(&gej[i1]) ? NULL : &zr); + /* 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)); + } secp256k1_ge_set_gej_var(&ref, &refj); - /* Test gej + ge (var). */ - secp256k1_gej_add_ge_var(&resj, &gej[i1], &ge[i2]); + /* 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); + 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)); + } + + /* Test gej + ge (var, with additional Z factor). */ + { + 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); + secp256k1_gej_add_zinv_var(&resj, &gej[i1], &ge2_zfi, &zf); + ge_equals_gej(&ref, &resj); + } /* Test gej + ge (const). */ if (i2 != 0) { @@ -1012,10 +1998,15 @@ void test_ge(void) { /* Test doubling (var). */ if ((i1 == 0 && i2 == 0) || ((i1 + 3)/4 == (i2 + 3)/4 && ((i1 + 3)%4)/2 == ((i2 + 3)%4)/2)) { - /* Normal doubling. */ - secp256k1_gej_double_var(&resj, &gej[i1]); + secp256k1_fe zr2; + /* Normal doubling with Z ratio result. */ + secp256k1_gej_double_var(&resj, &gej[i1], &zr2); ge_equals_gej(&ref, &resj); - secp256k1_gej_double_var(&resj, &gej[i2]); + /* Check Z ratio. */ + secp256k1_fe_mul(&zr2, &zr2, &gej[i1].z); + CHECK(secp256k1_fe_equal_var(&zr2, &resj.z)); + /* Normal doubling. */ + secp256k1_gej_double_var(&resj, &gej[i2], NULL); ge_equals_gej(&ref, &resj); } @@ -1040,41 +2031,121 @@ void test_ge(void) { /* Test adding all points together in random order equals infinity. */ { - secp256k1_gej_t sum = SECP256K1_GEJ_CONST_INFINITY; - secp256k1_gej_t *gej_shuffled = (secp256k1_gej_t *)malloc((4 * runs + 1) * sizeof(secp256k1_gej_t)); + secp256k1_gej sum = SECP256K1_GEJ_CONST_INFINITY; + 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_rand32() % (4 * runs + 1 - i); + int swap = i + secp256k1_rand_int(4 * runs + 1 - i); if (swap != i) { - secp256k1_gej_t t = gej_shuffled[i]; + secp256k1_gej t = gej_shuffled[i]; gej_shuffled[i] = gej_shuffled[swap]; gej_shuffled[swap] = t; } } for (i = 0; i < 4 * runs + 1; i++) { - secp256k1_gej_add_var(&sum, &sum, &gej_shuffled[i]); + secp256k1_gej_add_var(&sum, &sum, &gej_shuffled[i], NULL); } CHECK(secp256k1_gej_is_infinity(&sum)); free(gej_shuffled); } - /* Test batch gej -> ge conversion. */ + /* Test batch gej -> ge conversion with and without known z ratios. */ { - secp256k1_ge_t *ge_set_all = (secp256k1_ge_t *)malloc((4 * runs + 1) * sizeof(secp256k1_ge_t)); - secp256k1_ge_set_all_gej_var(4 * runs + 1, ge_set_all, gej); + 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); for (i = 0; i < 4 * runs + 1; i++) { - secp256k1_fe_t s; + secp256k1_fe s; 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]); } + free(ge_set_table); free(ge_set_all); + free(zr); } free(ge); free(gej); + free(zinv); +} + +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 + * points would be negatives of each other and their sum is + * infinity. This is cool because it "covers up" any degeneracy + * in the addition algorithm that would cause the xy coordinates + * of the sum to be wrong (since infinity has no xy coordinates). + * HOWEVER, if the x-coordinates are different, infinity is the + * wrong answer, and such degeneracies are exposed. This is the + * root of https://github.com/bitcoin-core/secp256k1/issues/257 + * 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] + * + * # 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() + */ + secp256k1_gej aj = SECP256K1_GEJ_CONST( + 0x8d24cd95, 0x0a355af1, 0x3c543505, 0x44238d30, + 0x0643d79f, 0x05a59614, 0x2f8ec030, 0xd58977cb, + 0x001e337a, 0x38093dcd, 0x6c0f386d, 0x0b1293a8, + 0x4d72c879, 0xd7681924, 0x44e6d2f3, 0x9190117d + ); + secp256k1_gej bj = SECP256K1_GEJ_CONST( + 0xc7b74206, 0x1f788cd9, 0xabd0937d, 0x164a0d86, + 0x95f6ff75, 0xf19a4ce9, 0xd013bd7b, 0xbf92d2a7, + 0xffe1cc85, 0xc7f6c232, 0x93f0c792, 0xf4ed6c57, + 0xb28d3786, 0x2897e6db, 0xbb192d0b, 0x6e6feab2 + ); + secp256k1_gej sumj = SECP256K1_GEJ_CONST( + 0x671a63c0, 0x3efdad4c, 0x389a7798, 0x24356027, + 0xb3d69010, 0x278625c3, 0x5c86d390, 0x184a8f7a, + 0x5f6409c2, 0x2ce01f2b, 0x511fd375, 0x25071d08, + 0xda651801, 0x70e95caf, 0x8f0d893c, 0xbed8fbbe + ); + secp256k1_ge b; + secp256k1_gej resj; + secp256k1_ge res; + secp256k1_ge_set_gej(&b, &bj); + + secp256k1_gej_add_var(&resj, &aj, &bj, NULL); + secp256k1_ge_set_gej(&res, &resj); + ge_equals_gej(&res, &sumj); + + secp256k1_gej_add_ge(&resj, &aj, &b); + secp256k1_ge_set_gej(&res, &resj); + ge_equals_gej(&res, &sumj); + + secp256k1_gej_add_ge_var(&resj, &aj, &b, NULL); + secp256k1_ge_set_gej(&res, &resj); + ge_equals_gej(&res, &sumj); } void run_ge(void) { @@ -1082,36 +2153,142 @@ void run_ge(void) { for (i = 0; i < count * 32; i++) { test_ge(); } + test_add_neg_y_diff_x(); +} + +void test_ec_combine(void) { + secp256k1_scalar sum = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); + secp256k1_pubkey data[6]; + const secp256k1_pubkey* d[6]; + secp256k1_pubkey sd; + secp256k1_pubkey sd2; + secp256k1_gej Qj; + secp256k1_ge Q; + int i; + for (i = 1; i <= 6; i++) { + secp256k1_scalar s; + random_scalar_order_test(&s); + secp256k1_scalar_add(&sum, &sum, &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_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); + } +} + +void run_ec_combine(void) { + int i; + for (i = 0; i < count * 8; i++) { + test_ec_combine(); + } +} + +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; + /* Return values of the above calls. */ + int res_quad, 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); + + if (res_quad) { + secp256k1_fe_normalize_var(&ge_quad.x); + 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 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) { + int i; + for (i = 0; i < count * 4; i++) { + secp256k1_fe fe; + random_fe_test(&fe); + test_group_decompress(&fe); + } } /***** ECMULT TESTS *****/ void run_ecmult_chain(void) { /* random starting point A (on the curve) */ - secp256k1_gej_t a = SECP256K1_GEJ_CONST( + secp256k1_gej a = SECP256K1_GEJ_CONST( 0x8b30bbe9, 0xae2a9906, 0x96b22f67, 0x0709dff3, 0x727fd8bc, 0x04d3362c, 0x6c7bf458, 0xe2846004, 0xa357ae91, 0x5c4a6528, 0x1309edf2, 0x0504740f, 0x0eb33439, 0x90216b4f, 0x81063cb6, 0x5f2f7e0f ); /* two random initial factors xn and gn */ - secp256k1_scalar_t xn = SECP256K1_SCALAR_CONST( + secp256k1_scalar xn = SECP256K1_SCALAR_CONST( 0x84cc5452, 0xf7fde1ed, 0xb4d38a8c, 0xe9b1b84c, 0xcef31f14, 0x6e569be9, 0x705d357a, 0x42985407 ); - secp256k1_scalar_t gn = SECP256K1_SCALAR_CONST( + secp256k1_scalar gn = SECP256K1_SCALAR_CONST( 0xa1e58d22, 0x553dcd42, 0xb2398062, 0x5d4c57a9, 0x6e9323d4, 0x2b3152e5, 0xca2c3990, 0xedc7c9de ); /* two small multipliers to be applied to xn and gn in every iteration: */ - static const secp256k1_scalar_t xf = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0x1337); - static const secp256k1_scalar_t gf = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0x7113); + 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_t ae = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1); - secp256k1_scalar_t ge = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); + 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); /* actual points */ - secp256k1_gej_t x = a; - secp256k1_gej_t x2; + secp256k1_gej x; + secp256k1_gej x2; int i; /* the point being computed */ @@ -1131,7 +2308,7 @@ void run_ecmult_chain(void) { /* verify */ if (i == 19999) { /* expected result after 19999 iterations */ - secp256k1_gej_t rp = SECP256K1_GEJ_CONST( + secp256k1_gej rp = SECP256K1_GEJ_CONST( 0xD6E96687, 0xF9B10D09, 0x2A6F3543, 0x9D86CEBE, 0xA4535D0D, 0x409F5358, 0x6440BD74, 0xB933E830, 0xB95CBCA2, 0xC77DA786, 0x539BE8FD, 0x53354D2D, @@ -1139,30 +2316,32 @@ void run_ecmult_chain(void) { ); secp256k1_gej_neg(&rp, &rp); - secp256k1_gej_add_var(&rp, &rp, &x); + secp256k1_gej_add_var(&rp, &rp, &x, NULL); CHECK(secp256k1_gej_is_infinity(&rp)); } } /* 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); + secp256k1_gej_add_var(&x2, &x2, &x, NULL); CHECK(secp256k1_gej_is_infinity(&x2)); } -void test_point_times_order(const secp256k1_gej_t *point) { +void test_point_times_order(const secp256k1_gej *point) { /* X * (point + G) + (order-X) * (pointer + G) = 0 */ - secp256k1_scalar_t x; - secp256k1_scalar_t nx; - secp256k1_gej_t res1, res2; - secp256k1_ge_t res3; + 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]; - int psize = 65; + size_t psize = 65; 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_gej_add_var(&res1, &res1, &res2); + 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); @@ -1171,19 +2350,29 @@ void test_point_times_order(const secp256k1_gej_t *point) { CHECK(secp256k1_eckey_pubkey_serialize(&res3, pub, &psize, 0) == 0); 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_ge_set_gej(&res3, &res1); + CHECK(secp256k1_ge_is_infinity(&res3)); + secp256k1_ecmult(&ctx->ecmult_ctx, &res1, point, &one, &zero); + secp256k1_ge_set_gej(&res3, &res1); + ge_equals_gej(&res3, point); + secp256k1_ecmult(&ctx->ecmult_ctx, &res1, point, &zero, &one); + secp256k1_ge_set_gej(&res3, &res1); + ge_equals_ge(&res3, &secp256k1_ge_const_g); } void run_point_times_order(void) { int i; - secp256k1_fe_t x = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 2); - static const secp256k1_fe_t xr = SECP256K1_FE_CONST( + secp256k1_fe x = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 2); + static const secp256k1_fe xr = SECP256K1_FE_CONST( 0x7603CB59, 0xB0EF6C63, 0xFE608479, 0x2A0C378C, 0xDB3233A8, 0x0F8A9A09, 0xA877DEAD, 0x31B38C45 ); for (i = 0; i < 500; i++) { - secp256k1_ge_t p; + secp256k1_ge p; if (secp256k1_ge_set_xo_var(&p, &x, 1)) { - secp256k1_gej_t j; + secp256k1_gej j; CHECK(secp256k1_ge_is_valid_var(&p)); secp256k1_gej_set_ge(&j, &p); CHECK(secp256k1_gej_is_valid_var(&j)); @@ -1195,15 +2384,118 @@ void run_point_times_order(void) { CHECK(secp256k1_fe_equal_var(&x, &xr)); } -void test_wnaf(const secp256k1_scalar_t *number, int w) { - secp256k1_scalar_t x, two, t; +void ecmult_const_random_mult(void) { + /* random starting point A (on the curve) */ + secp256k1_ge a = SECP256K1_GE_CONST( + 0x6d986544, 0x57ff52b8, 0xcf1b8126, 0x5b802a5b, + 0xa97f9263, 0xb1e88044, 0x93351325, 0x91bc450a, + 0x535c59f7, 0x325e5d2b, 0xc391fbe8, 0x3c12787c, + 0x337e4a98, 0xe82a9011, 0x0123ba37, 0xdd769c7d + ); + /* random initial factor xn */ + secp256k1_scalar xn = SECP256K1_SCALAR_CONST( + 0x649d4f77, 0xc4242df7, 0x7f2079c9, 0x14530327, + 0xa31b876a, 0xd2d8ce2a, 0x2236d5c6, 0xd7b2029b + ); + /* expected xn * A (from sage) */ + secp256k1_ge expected_b = SECP256K1_GE_CONST( + 0x23773684, 0x4d209dc7, 0x098a786f, 0x20d06fcd, + 0x070a38bf, 0xc11ac651, 0x03004319, 0x1e2a8786, + 0xed8c3b8e, 0xc06dd57b, 0xd06ea66e, 0x45492b0f, + 0xb84e4e1b, 0xfb77e21f, 0x96baae2a, 0x63dec956 + ); + secp256k1_gej b; + secp256k1_ecmult_const(&b, &a, &xn); + + CHECK(secp256k1_ge_is_valid_var(&a)); + ge_equals_gej(&expected_b, &b); +} + +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); + + secp256k1_ecmult_const(&res1, &secp256k1_ge_const_g, &a); + secp256k1_ecmult_const(&res2, &secp256k1_ge_const_g, &b); + secp256k1_ge_set_gej(&mid1, &res1); + secp256k1_ge_set_gej(&mid2, &res2); + secp256k1_ecmult_const(&res1, &mid1, &b); + secp256k1_ecmult_const(&res2, &mid2, &a); + secp256k1_ge_set_gej(&mid1, &res1); + secp256k1_ge_set_gej(&mid2, &res2); + ge_equals_ge(&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); + secp256k1_scalar negone; + secp256k1_gej res1; + secp256k1_ge res2; + secp256k1_ge point; + secp256k1_scalar_negate(&negone, &one); + + 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); + secp256k1_ge_set_gej(&res2, &res1); + ge_equals_ge(&res2, &point); + secp256k1_ecmult_const(&res1, &point, &negone); + secp256k1_gej_neg(&res1, &res1); + secp256k1_ge_set_gej(&res2, &res1); + ge_equals_ge(&res2, &point); +} + +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, + 0xcd83d307, 0x827bed62, 0x05fad0ce, 0x18fae63b + ); + const secp256k1_gej expected_point = SECP256K1_GEJ_CONST( + 0x5494c15d, 0x32099706, 0xc2395f94, 0x348745fd, + 0x757ce30e, 0x4e8c90fb, 0xa2bad184, 0xf883c69f, + 0x5d195d20, 0xe191bf7f, 0x1be3e55f, 0x56a80196, + 0x6071ad01, 0xf1462f66, 0xc997fa94, 0xdb858435 + ); + secp256k1_gej point; + secp256k1_ge res; + int i; + + secp256k1_gej_set_ge(&point, &secp256k1_ge_const_g); + for (i = 0; i < 100; ++i) { + secp256k1_ge tmp; + secp256k1_ge_set_gej(&tmp, &point); + secp256k1_ecmult_const(&point, &tmp, &scalar); + } + secp256k1_ge_set_gej(&res, &point); + ge_equals_gej(&res, &expected_point); +} + +void run_ecmult_const_tests(void) { + ecmult_const_mult_zero_one(); + ecmult_const_random_mult(); + ecmult_const_commutativity(); + ecmult_const_chain_multiply(); +} + +void test_wnaf(const secp256k1_scalar *number, int w) { + secp256k1_scalar x, two, t; int wnaf[256]; int zeroes = -1; int i; int bits; secp256k1_scalar_set_int(&x, 0); secp256k1_scalar_set_int(&two, 2); - bits = secp256k1_ecmult_wnaf(wnaf, number, w); + bits = secp256k1_ecmult_wnaf(wnaf, 256, number, w); CHECK(bits <= 256); for (i = bits-1; i >= 0; i--) { int v = wnaf[i]; @@ -1229,20 +2521,89 @@ void test_wnaf(const secp256k1_scalar_t *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; + + if (!secp256k1_scalar_get_bits(&neg1, 0, 1)) { + secp256k1_scalar_negate(&neg1, &neg1); + sign1 = -1; + } + 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; + int wnaf[256] = {0}; + int i; + int skew; + secp256k1_scalar num = *number; + + 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); + } +#endif + skew = secp256k1_wnaf_const(wnaf, num, w); + + for (i = WNAF_SIZE(w); i >= 0; --i) { + secp256k1_scalar t; + 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 */ + + 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); + } + /* 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) { int i; - secp256k1_scalar_t n; + 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); + /* Random tests */ for (i = 0; i < count; i++) { random_scalar_order(&n); test_wnaf(&n, 4+(i%10)); + test_constant_wnaf_negate(&n); + test_constant_wnaf(&n, 4 + (i % 10)); } + secp256k1_scalar_set_int(&n, 0); + CHECK(secp256k1_scalar_cond_negate(&n, 1) == -1); + CHECK(secp256k1_scalar_is_zero(&n)); + CHECK(secp256k1_scalar_cond_negate(&n, 0) == 1); + CHECK(secp256k1_scalar_is_zero(&n)); } void test_ecmult_constants(void) { /* Test ecmult_gen() for [0..36) and [order-36..0). */ - secp256k1_scalar_t x; - secp256k1_gej_t r; - secp256k1_ge_t ng; + secp256k1_scalar x; + secp256k1_gej r; + secp256k1_ge ng; int i; int j; secp256k1_ge_neg(&ng, &secp256k1_ge_const_g); @@ -1276,14 +2637,14 @@ void run_ecmult_constants(void) { } void test_ecmult_gen_blind(void) { - /* Test ecmult_gen() blinding and confirm that the blinding changes, the affline points match, and the z's don't match. */ - secp256k1_scalar_t key; - secp256k1_scalar_t b; + /* 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_t pgej; - secp256k1_gej_t pgej2; - secp256k1_gej_t i; - secp256k1_ge_t pge; + secp256k1_gej pgej; + secp256k1_gej pgej2; + secp256k1_gej i; + secp256k1_ge pge; random_scalar_order_test(&key); secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pgej, &key); secp256k1_rand256(seed32); @@ -1300,8 +2661,8 @@ void test_ecmult_gen_blind(void) { void test_ecmult_gen_blind_reset(void) { /* Test ecmult_gen() blinding reset and confirm that the blinding is consistent. */ - secp256k1_scalar_t b; - secp256k1_gej_t initial; + 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; @@ -1318,35 +2679,702 @@ void run_ecmult_gen_blind(void) { } } +#ifdef USE_ENDOMORPHISM +/***** ENDOMORPHISH TESTS *****/ +void test_scalar_split(void) { + secp256k1_scalar full; + secp256k1_scalar s1, slam; + const unsigned char zero[32] = {0}; + unsigned char tmp[32]; + + random_scalar_order_test(&full); + secp256k1_scalar_split_lambda(&s1, &slam, &full); + + /* check that both are <= 128 bits in size */ + if (secp256k1_scalar_is_high(&s1)) { + secp256k1_scalar_negate(&s1, &s1); + } + if (secp256k1_scalar_is_high(&slam)) { + secp256k1_scalar_negate(&slam, &slam); + } -void random_sign(secp256k1_ecdsa_sig_t *sig, const secp256k1_scalar_t *key, const secp256k1_scalar_t *msg, int *recid) { - secp256k1_scalar_t nonce; + secp256k1_scalar_get_b32(tmp, &s1); + CHECK(memcmp(zero, tmp, 16) == 0); + secp256k1_scalar_get_b32(tmp, &slam); + CHECK(memcmp(zero, tmp, 16) == 0); +} + +void run_endomorphism_tests(void) { + test_scalar_split(); +} +#endif + +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); + for (i = 0; i < 256; i++) { + /* Try all type bytes. */ + int xpass; + int ypass; + int ysign; + pubkeyc[0] = i; + /* What sign does this point have? */ + ysign = (input[63] & 1) + 2; + /* For the current type (i) do we expect parsing to work? Handled all of compressed/uncompressed/hybrid. */ + xpass = xvalid && (pubkeyclen == 33) && ((i & 254) == 2); + /* Do we expect a parse and re-serialize as uncompressed to give a matching y? */ + ypass = xvalid && yvalid && ((i & 4) == ((pubkeyclen == 65) << 2)) && + ((i == 4) || ((i & 251) == ysign)) && ((pubkeyclen == 33) || (pubkeyclen == 65)); + if (xpass || ypass) { + /* These cases must parse. */ + 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)); + outl = 65; + VG_UNDEF(pubkeyo, 65); + CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyo, &outl, &pubkey, SECP256K1_EC_COMPRESSED) == 1); + VG_CHECK(pubkeyo, outl); + CHECK(outl == 33); + CHECK(memcmp(&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); + memset(&pubkey, 0, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + secp256k1_pubkey_save(&pubkey, &ge); + VG_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); + CHECK(outl == 65); + CHECK(pubkeyo[0] == 4); + CHECK(memcmp(&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_context_set_illegal_callback(ctx, NULL, NULL); +} + +void run_ec_pubkey_parse_test(void) { +#define SECP256K1_EC_PARSE_TEST_NVALID (12) + const unsigned char valid[SECP256K1_EC_PARSE_TEST_NVALID][64] = { + { + /* Point with leading and trailing zeros in x and y serialization. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x52, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x64, 0xef, 0xa1, 0x7b, 0x77, 0x61, 0xe1, 0xe4, 0x27, 0x06, 0x98, 0x9f, 0xb4, 0x83, + 0xb8, 0xd2, 0xd4, 0x9b, 0xf7, 0x8f, 0xae, 0x98, 0x03, 0xf0, 0x99, 0xb8, 0x34, 0xed, 0xeb, 0x00 + }, + { + /* Point with x equal to a 3rd root of unity.*/ + 0x7a, 0xe9, 0x6a, 0x2b, 0x65, 0x7c, 0x07, 0x10, 0x6e, 0x64, 0x47, 0x9e, 0xac, 0x34, 0x34, 0xe9, + 0x9c, 0xf0, 0x49, 0x75, 0x12, 0xf5, 0x89, 0x95, 0xc1, 0x39, 0x6c, 0x28, 0x71, 0x95, 0x01, 0xee, + 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, + }, + { + /* Point with largest x. (1/2) */ + 0xff, 0xff, 0xff, 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, + 0x0e, 0x99, 0x4b, 0x14, 0xea, 0x72, 0xf8, 0xc3, 0xeb, 0x95, 0xc7, 0x1e, 0xf6, 0x92, 0x57, 0x5e, + 0x77, 0x50, 0x58, 0x33, 0x2d, 0x7e, 0x52, 0xd0, 0x99, 0x5c, 0xf8, 0x03, 0x88, 0x71, 0xb6, 0x7d, + }, + { + /* Point with largest x. (2/2) */ + 0xff, 0xff, 0xff, 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, + 0xf1, 0x66, 0xb4, 0xeb, 0x15, 0x8d, 0x07, 0x3c, 0x14, 0x6a, 0x38, 0xe1, 0x09, 0x6d, 0xa8, 0xa1, + 0x88, 0xaf, 0xa7, 0xcc, 0xd2, 0x81, 0xad, 0x2f, 0x66, 0xa3, 0x07, 0xfb, 0x77, 0x8e, 0x45, 0xb2, + }, + { + /* Point with smallest x. (1/2) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, + 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, + }, + { + /* Point with smallest x. (2/2) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, + 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, + }, + { + /* Point with largest y. (1/3) */ + 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, 0x2e, + }, + { + /* Point with largest y. (2/3) */ + 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, + 0xff, 0xff, 0xff, 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, 0x2e, + }, + { + /* Point with largest y. (3/3) */ + 0x14, 0x6d, 0x3b, 0x65, 0xad, 0xd9, 0xf5, 0x4c, 0xcc, 0xa2, 0x85, 0x33, 0xc8, 0x8e, 0x2c, 0xbc, + 0x63, 0xf7, 0x44, 0x3e, 0x16, 0x58, 0x78, 0x3a, 0xb4, 0x1f, 0x8e, 0xf9, 0x7c, 0x2a, 0x10, 0xb5, + 0xff, 0xff, 0xff, 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, 0x2e, + }, + { + /* Point with smallest y. (1/3) */ + 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, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, + }, + { + /* Point with smallest y. (2/3) */ + 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, 0x01, + }, + { + /* Point with smallest y. (3/3) */ + 0x14, 0x6d, 0x3b, 0x65, 0xad, 0xd9, 0xf5, 0x4c, 0xcc, 0xa2, 0x85, 0x33, 0xc8, 0x8e, 0x2c, 0xbc, + 0x63, 0xf7, 0x44, 0x3e, 0x16, 0x58, 0x78, 0x3a, 0xb4, 0x1f, 0x8e, 0xf9, 0x7c, 0x2a, 0x10, 0xb5, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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 + } + }; +#define SECP256K1_EC_PARSE_TEST_NXVALID (4) + const unsigned char onlyxvalid[SECP256K1_EC_PARSE_TEST_NXVALID][64] = { + { + /* Valid if y overflow ignored (y = 1 mod p). (1/3) */ + 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, 0x30, + }, + { + /* Valid if y overflow ignored (y = 1 mod p). (2/3) */ + 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, + 0xff, 0xff, 0xff, 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, + }, + { + /* Valid if y overflow ignored (y = 1 mod p). (3/3)*/ + 0x14, 0x6d, 0x3b, 0x65, 0xad, 0xd9, 0xf5, 0x4c, 0xcc, 0xa2, 0x85, 0x33, 0xc8, 0x8e, 0x2c, 0xbc, + 0x63, 0xf7, 0x44, 0x3e, 0x16, 0x58, 0x78, 0x3a, 0xb4, 0x1f, 0x8e, 0xf9, 0x7c, 0x2a, 0x10, 0xb5, + 0xff, 0xff, 0xff, 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, + }, + { + /* x on curve, y is from y^2 = x^3 + 8. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 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 + } + }; +#define SECP256K1_EC_PARSE_TEST_NINVALID (7) + const unsigned char invalid[SECP256K1_EC_PARSE_TEST_NINVALID][64] = { + { + /* x is third root of -8, y is -1 * (x^3+7); also on the curve for y^2 = x^3 + 9. */ + 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, 0x01, + }, + { + /* Valid if x overflow ignored (x = 1 mod p). */ + 0xff, 0xff, 0xff, 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, + 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, + }, + { + /* Valid if x overflow ignored (x = 1 mod p). */ + 0xff, 0xff, 0xff, 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, + 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, + }, + { + /* x is -1, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 5. */ + 0xff, 0xff, 0xff, 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, 0x2e, + 0xf4, 0x84, 0x14, 0x5c, 0xb0, 0x14, 0x9b, 0x82, 0x5d, 0xff, 0x41, 0x2f, 0xa0, 0x52, 0xa8, 0x3f, + 0xcb, 0x72, 0xdb, 0x61, 0xd5, 0x6f, 0x37, 0x70, 0xce, 0x06, 0x6b, 0x73, 0x49, 0xa2, 0xaa, 0x28, + }, + { + /* x is -1, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 5. */ + 0xff, 0xff, 0xff, 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, 0x2e, + 0x0b, 0x7b, 0xeb, 0xa3, 0x4f, 0xeb, 0x64, 0x7d, 0xa2, 0x00, 0xbe, 0xd0, 0x5f, 0xad, 0x57, 0xc0, + 0x34, 0x8d, 0x24, 0x9e, 0x2a, 0x90, 0xc8, 0x8f, 0x31, 0xf9, 0x94, 0x8b, 0xb6, 0x5d, 0x52, 0x07, + }, + { + /* x is zero, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 7. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8f, 0x53, 0x7e, 0xef, 0xdf, 0xc1, 0x60, 0x6a, 0x07, 0x27, 0xcd, 0x69, 0xb4, 0xa7, 0x33, 0x3d, + 0x38, 0xed, 0x44, 0xe3, 0x93, 0x2a, 0x71, 0x79, 0xee, 0xcb, 0x4b, 0x6f, 0xba, 0x93, 0x60, 0xdc, + }, + { + /* x is zero, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 7. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0xac, 0x81, 0x10, 0x20, 0x3e, 0x9f, 0x95, 0xf8, 0xd8, 0x32, 0x96, 0x4b, 0x58, 0xcc, 0xc2, + 0xc7, 0x12, 0xbb, 0x1c, 0x6c, 0xd5, 0x8e, 0x86, 0x11, 0x34, 0xb4, 0x8f, 0x45, 0x6c, 0x9b, 0x53 + } + }; + const unsigned char pubkeyc[66] = { + /* Serialization of G. */ + 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, 0x00 + }; + unsigned char sout[65]; + unsigned char shortkey[2]; + 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); + /* 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); + /* 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); + } + /* 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); + } + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(&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); + /* 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); + /* 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); + /* 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); + /* 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); + /* 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_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(len == 0); + CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, NULL, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 0); + CHECK(ecount == 2); + 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); + CHECK(len == 0); + len = 65; + CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, &len, &pubkey, ~0) == 0); + CHECK(ecount == 4); + 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); + 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); + /* 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); + } + for (i = 0; i < SECP256K1_EC_PARSE_TEST_NXVALID; i++) { + ec_pubkey_parse_pointtest(onlyxvalid[i], 1, 0); + } + for (i = 0; i < SECP256K1_EC_PARSE_TEST_NINVALID; i++) { + ec_pubkey_parse_pointtest(invalid[i], 0, 0); + } +} + +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, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41 + }; + const unsigned char zeros[sizeof(secp256k1_pubkey)] = {0x00}; + unsigned char ctmp[33]; + unsigned char ctmp2[33]; + secp256k1_pubkey pubkey; + secp256k1_pubkey pubkey2; + secp256k1_pubkey pubkey_one; + 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); + /* Maximum value is too large, reject. */ + memset(ctmp, 255, 32); + 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); + /* Zero is too small, reject. */ + memset(ctmp, 0, 32); + 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); + /* One must be accepted. */ + ctmp[31] = 0x01; + 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); + 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); + 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); + /* -1 must be accepted. */ + ctmp[31] = 0x40; + 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); + pubkey_negone = pubkey; + /* Tweak of zero leaves the value changed. */ + memset(ctmp2, 0, 32); + CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, ctmp2) == 1); + CHECK(memcmp(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); + /* 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); + memcpy(&pubkey, &pubkey2, sizeof(pubkey)); + /* Overflowing key tweak zeroizes. */ + memcpy(ctmp, orderc, 32); + ctmp[31] = 0x40; + CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, orderc) == 0); + CHECK(memcmp(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); + memcpy(ctmp, orderc, 32); + ctmp[31] = 0x40; + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, orderc) == 0); + CHECK(memcmp(&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); + memcpy(&pubkey, &pubkey2, sizeof(pubkey)); + /* Private key tweaks results in a key of zero. */ + ctmp2[31] = 1; + CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp2, ctmp) == 0); + CHECK(memcmp(zeros, ctmp2, 32) == 0); + ctmp2[31] = 1; + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 0); + CHECK(memcmp(&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); + ctmp2[31] = 2; + 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); + /* Tweak mul * 2 = 1+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); + /* 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); + 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); + /* 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; + 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; + 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; + 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; + 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); + 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); + /* 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 *)); + 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); + 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); + 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); + 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); + /* 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); + /* 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); + 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); + /* 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); +} + +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, sig, key, msg, &nonce, recid)); + } while(!secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, sigr, sigs, key, msg, &nonce, recid)); } void test_ecdsa_sign_verify(void) { - secp256k1_gej_t pubj; - secp256k1_ge_t pub; - secp256k1_scalar_t one; - secp256k1_scalar_t msg, key; - secp256k1_ecdsa_sig_t sig; + 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); secp256k1_ge_set_gej(&pub, &pubj); - getrec = secp256k1_rand32()&1; - random_sign(&sig, &key, &msg, getrec?&recid:NULL); + getrec = secp256k1_rand_bits(1); + random_sign(&sigr, &sigs, &key, &msg, getrec?&recid:NULL); if (getrec) { CHECK(recid >= 0 && recid < 4); } - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sig, &pub, &msg)); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sigr, &sigs, &pub, &msg)); secp256k1_scalar_set_int(&one, 1); secp256k1_scalar_add(&msg, &msg, &one); - CHECK(!secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sig, &pub, &msg)); + CHECK(!secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sigr, &sigs, &pub, &msg)); } void run_ecdsa_sign_verify(void) { @@ -1357,22 +3385,23 @@ void run_ecdsa_sign_verify(void) { } /** Dummy nonce generation function that just uses a precomputed nonce, and fails if it is not accepted. Use only for testing. */ -static int precomputed_nonce_function(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, unsigned int counter, const void *data) { +static int precomputed_nonce_function(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { (void)msg32; (void)key32; + (void)algo16; memcpy(nonce32, data, 32); return (counter == 0); } -static int nonce_function_test_fail(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, unsigned int counter, const void *data) { +static int nonce_function_test_fail(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { /* Dummy nonce generator that has a fatal error on the first counter value. */ if (counter == 0) { return 0; } - return nonce_function_rfc6979(nonce32, msg32, key32, counter - 1, data); + return nonce_function_rfc6979(nonce32, msg32, key32, algo16, data, counter - 1); } -static int nonce_function_test_retry(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, unsigned int counter, const void *data) { +static int nonce_function_test_retry(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { /* Dummy nonce generator that produces unacceptable nonces for the first several counter values. */ if (counter < 3) { memset(nonce32, counter==0 ? 0 : 255, 32); @@ -1394,17 +3423,17 @@ static int nonce_function_test_retry(unsigned char *nonce32, const unsigned char } return 1; } - /* Retry rate of 6979 is negligible esp. as we only call this in determinstic tests. */ + /* Retry rate of 6979 is negligible esp. as we only call this in deterministic tests. */ /* If someone does fine a case where it retries for secp256k1, we'd like to know. */ if (counter > 5) { return 0; } - return nonce_function_rfc6979(nonce32, msg32, key32, counter - 5, data); + return nonce_function_rfc6979(nonce32, msg32, key32, algo16, data, counter - 5); } -int is_empty_compact_signature(const unsigned char *sig64) { - static const unsigned char res[64] = {0}; - return memcmp(sig64, res, 64) == 0; +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; } void test_ecdsa_end_to_end(void) { @@ -1412,26 +3441,20 @@ void test_ecdsa_end_to_end(void) { unsigned char privkey[32]; unsigned char message[32]; unsigned char privkey2[32]; - unsigned char csignature[64]; - unsigned char signature[72]; - unsigned char signature2[72]; - unsigned char signature3[72]; - unsigned char signature4[72]; - unsigned char pubkey[65]; - unsigned char recpubkey[65]; + secp256k1_ecdsa_signature signature[6]; + secp256k1_scalar r, s; + unsigned char sig[74]; + size_t siglen = 74; + unsigned char pubkeyc[65]; + size_t pubkeyclen = 65; + secp256k1_pubkey pubkey; + secp256k1_pubkey pubkey_tmp; unsigned char seckey[300]; - int signaturelen = 72; - int signaturelen2 = 72; - int signaturelen3 = 72; - int signaturelen4 = 72; - int recid = 0; - int recpubkeylen = 0; - int pubkeylen = 65; - int seckeylen = 300; + size_t seckeylen = 300; /* Generate a random key and message. */ { - secp256k1_scalar_t msg, key; + secp256k1_scalar msg, key; random_scalar_order_test(&msg); random_scalar_order_test(&key); secp256k1_scalar_get_b32(privkey, &key); @@ -1440,117 +3463,127 @@ void test_ecdsa_end_to_end(void) { /* Construct and verify corresponding public key. */ CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, pubkey, &pubkeylen, privkey, (secp256k1_rand32() & 3) != 0) == 1); - if (secp256k1_rand32() & 1) { - CHECK(secp256k1_ec_pubkey_decompress(ctx, pubkey, &pubkeylen)); - } - CHECK(secp256k1_ec_pubkey_verify(ctx, pubkey, pubkeylen)); + 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)); + memset(&pubkey, 0, sizeof(pubkey)); + 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); /* Verify private key import and export. */ - CHECK(secp256k1_ec_privkey_export(ctx, privkey, seckey, &seckeylen, secp256k1_rand32() % 2) == 1); - CHECK(secp256k1_ec_privkey_import(ctx, privkey2, seckey, seckeylen) == 1); + 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); /* Optionally tweak the keys using addition. */ - if (secp256k1_rand32() % 3 == 0) { + if (secp256k1_rand_int(3) == 0) { int ret1; int ret2; unsigned char rnd[32]; - unsigned char pubkey2[65]; - int pubkeylen2 = 65; + secp256k1_pubkey pubkey2; secp256k1_rand256_test(rnd); ret1 = secp256k1_ec_privkey_tweak_add(ctx, privkey, rnd); - ret2 = secp256k1_ec_pubkey_tweak_add(ctx, pubkey, pubkeylen, rnd); + ret2 = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, rnd); CHECK(ret1 == ret2); if (ret1 == 0) { return; } - CHECK(secp256k1_ec_pubkey_create(ctx, pubkey2, &pubkeylen2, privkey, pubkeylen == 33) == 1); - CHECK(memcmp(pubkey, pubkey2, pubkeylen) == 0); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, privkey) == 1); + CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); } /* Optionally tweak the keys using multiplication. */ - if (secp256k1_rand32() % 3 == 0) { + if (secp256k1_rand_int(3) == 0) { int ret1; int ret2; unsigned char rnd[32]; - unsigned char pubkey2[65]; - int pubkeylen2 = 65; + secp256k1_pubkey pubkey2; secp256k1_rand256_test(rnd); ret1 = secp256k1_ec_privkey_tweak_mul(ctx, privkey, rnd); - ret2 = secp256k1_ec_pubkey_tweak_mul(ctx, pubkey, pubkeylen, rnd); + ret2 = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, rnd); CHECK(ret1 == ret2); if (ret1 == 0) { return; } - CHECK(secp256k1_ec_pubkey_create(ctx, pubkey2, &pubkeylen2, privkey, pubkeylen == 33) == 1); - CHECK(memcmp(pubkey, pubkey2, pubkeylen) == 0); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, privkey) == 1); + CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); } /* Sign. */ - CHECK(secp256k1_ecdsa_sign(ctx, message, signature, &signaturelen, privkey, NULL, NULL) == 1); - CHECK(signaturelen > 0); - CHECK(secp256k1_ecdsa_sign(ctx, message, signature2, &signaturelen2, privkey, NULL, extra) == 1); - CHECK(signaturelen2 > 0); + 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, message, signature3, &signaturelen3, privkey, NULL, extra) == 1); - CHECK(signaturelen3 > 0); + CHECK(secp256k1_ecdsa_sign(ctx, &signature[2], message, privkey, NULL, extra) == 1); extra[31] = 0; extra[0] = 1; - CHECK(secp256k1_ecdsa_sign(ctx, message, signature4, &signaturelen4, privkey, NULL, extra) == 1); - CHECK(signaturelen3 > 0); - CHECK((signaturelen != signaturelen2) || (memcmp(signature, signature2, signaturelen) != 0)); - CHECK((signaturelen != signaturelen3) || (memcmp(signature, signature3, signaturelen) != 0)); - CHECK((signaturelen3 != signaturelen2) || (memcmp(signature3, signature2, signaturelen3) != 0)); - CHECK((signaturelen4 != signaturelen3) || (memcmp(signature4, signature3, signaturelen4) != 0)); - CHECK((signaturelen4 != signaturelen2) || (memcmp(signature4, signature2, signaturelen4) != 0)); - CHECK((signaturelen4 != signaturelen) || (memcmp(signature4, signature, signaturelen4) != 0)); + 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); /* Verify. */ - CHECK(secp256k1_ecdsa_verify(ctx, message, signature, signaturelen, pubkey, pubkeylen) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, message, signature2, signaturelen2, pubkey, pubkeylen) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, message, signature3, signaturelen3, pubkey, pubkeylen) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, message, signature4, signaturelen4, pubkey, pubkeylen) == 1); - /* Destroy signature and verify again. */ - signature[signaturelen - 1 - secp256k1_rand32() % 20] += 1 + (secp256k1_rand32() % 255); - CHECK(secp256k1_ecdsa_verify(ctx, message, signature, signaturelen, pubkey, pubkeylen) != 1); - - /* Compact sign. */ - CHECK(secp256k1_ecdsa_sign_compact(ctx, message, csignature, privkey, NULL, NULL, &recid) == 1); - CHECK(!is_empty_compact_signature(csignature)); - /* Recover. */ - CHECK(secp256k1_ecdsa_recover_compact(ctx, message, csignature, recpubkey, &recpubkeylen, pubkeylen == 33, recid) == 1); - CHECK(recpubkeylen == pubkeylen); - CHECK(memcmp(pubkey, recpubkey, pubkeylen) == 0); - /* Destroy signature and verify again. */ - csignature[secp256k1_rand32() % 64] += 1 + (secp256k1_rand32() % 255); - CHECK(secp256k1_ecdsa_recover_compact(ctx, message, csignature, recpubkey, &recpubkeylen, pubkeylen == 33, recid) != 1 || - memcmp(pubkey, recpubkey, pubkeylen) != 0); - CHECK(recpubkeylen == pubkeylen); - + 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]); + 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); + 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); + + /* Serialize/parse DER and verify again */ + 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); + /* 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); } void test_random_pubkeys(void) { - secp256k1_ge_t elem; - secp256k1_ge_t elem2; + secp256k1_ge elem; + secp256k1_ge elem2; unsigned char in[65]; /* Generate some randomly sized pubkeys. */ - uint32_t r = secp256k1_rand32(); - int len = (r & 3) == 0 ? 65 : 33; - r>>=2; - if ((r & 3) == 0) { - len = (r & 252) >> 3; + size_t len = secp256k1_rand_bits(2) == 0 ? 65 : 33; + if (secp256k1_rand_bits(2) == 0) { + len = secp256k1_rand_bits(6); } - r>>=8; if (len == 65) { - in[0] = (r & 2) ? 4 : (r & 1? 6 : 7); + in[0] = secp256k1_rand_bits(1) ? 4 : (secp256k1_rand_bits(1) ? 6 : 7); } else { - in[0] = (r & 1) ? 2 : 3; + in[0] = secp256k1_rand_bits(1) ? 2 : 3; } - r>>=2; - if ((r & 7) == 0) { - in[0] = (r & 2040) >> 3; + if (secp256k1_rand_bits(3) == 0) { + in[0] = secp256k1_rand_bits(8); } - r>>=11; if (len > 1) { secp256k1_rand256(&in[1]); } @@ -1561,7 +3594,7 @@ void test_random_pubkeys(void) { unsigned char out[65]; unsigned char firstb; int res; - int size = len; + size_t size = len; firstb = in[0]; /* If the pubkey can be parsed, it should round-trip... */ CHECK(secp256k1_eckey_pubkey_serialize(&elem, out, &size, len == 33)); @@ -1577,7 +3610,7 @@ void test_random_pubkeys(void) { CHECK(secp256k1_eckey_pubkey_parse(&elem2, in, size)); ge_equals_ge(&elem,&elem2); /* Check that the X9.62 hybrid type is checked. */ - in[0] = (r & 1) ? 6 : 7; + in[0] = secp256k1_rand_bits(1) ? 6 : 7; res = secp256k1_eckey_pubkey_parse(&elem2, in, size); if (firstb == 2 || firstb == 3) { if (in[0] == firstb + 4) { @@ -1608,185 +3641,508 @@ void run_ecdsa_end_to_end(void) { } } +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; + + secp256k1_ecdsa_signature sig_der; + unsigned char roundtrip_der[2048]; + unsigned char compact_der[64]; + size_t len_der = 2048; + int parsed_der = 0, valid_der = 0, roundtrips_der = 0; + + secp256k1_ecdsa_signature sig_der_lax; + unsigned char roundtrip_der_lax[2048]; + unsigned char compact_der_lax[64]; + 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); + 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); + } + 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; + } + + 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); + } + 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; + } + + if (certainly_der) { + ret |= (!parsed_der) << 2; + } + if (certainly_not_der) { + ret |= (parsed_der) << 17; + } + if (valid_der) { + ret |= (!roundtrips_der) << 3; + } + + 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 |= (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; +} + +static void assign_big_endian(unsigned char *ptr, size_t ptrlen, uint32_t val) { + size_t i; + for (i = 0; i < ptrlen; i++) { + int shift = ptrlen - 1 - i; + if (shift >= 4) { + ptr[i] = 0; + } else { + ptr[i] = (val >> shift) & 0xFF; + } + } +} + +static void damage_array(unsigned char *sig, size_t *len) { + int pos; + int action = secp256k1_rand_bits(3); + if (action < 1 && *len > 3) { + /* Delete a byte. */ + pos = secp256k1_rand_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); + memmove(sig + pos + 1, sig + pos, *len - pos); + sig[pos] = secp256k1_rand_bits(8); + (*len)++; + return; + } else if (action < 4) { + /* Modify a byte. */ + sig[secp256k1_rand_int(*len)] += 1 + secp256k1_rand_int(255); + return; + } else { /* action < 8 */ + /* Modify a bit. */ + sig[secp256k1_rand_int(*len)] ^= 1 << secp256k1_rand_bits(3); + return; + } +} + +static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly_der, int* certainly_not_der) { + int der; + int nlow[2], nlen[2], nlenlen[2], nhbit[2], nhbyte[2], nzlen[2]; + size_t tlen, elen, glen; + int indet; + int n; + + *len = 0; + der = secp256k1_rand_bits(2) == 0; + *certainly_der = der; + *certainly_not_der = 0; + indet = der ? 0 : secp256k1_rand_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); + /* 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; + 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)); + /* 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)); + /* 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); + if (nzlen[n] > ((nlen[n] == 0 || nhbit[n]) ? 1 : 0)) { + *certainly_not_der = 1; + } + CHECK(nlen[n] + nzlen[n] <= 300); + /* The length of the length descriptor for the number. 0 means short encoding, anything else is long encoding. */ + 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; + nlenlen[n] += add; + if (add != 0) { + *certainly_not_der = 1; + } + } + CHECK(nlen[n] + nzlen[n] + nlenlen[n] <= 427); + } + + /* The total length of the data to go, so far */ + tlen = 2 + nlenlen[0] + nlen[0] + nzlen[0] + 2 + nlenlen[1] + nlen[1] + nzlen[1]; + 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; + if (elen != 0) { + *certainly_not_der = 1; + } + tlen += elen; + 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; + if (glen != 0) { + *certainly_not_der = 1; + } + CHECK(tlen + glen <= 990); + + /* Write the tuple header. */ + sig[(*len)++] = 0x30; + if (indet) { + /* Indeterminate length */ + sig[(*len)++] = 0x80; + *certainly_not_der = 1; + } 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; + tlenlen += add; + if (add != 0) { + *certainly_not_der = 1; + } + } + if (tlenlen == 0) { + /* Short length notation */ + sig[(*len)++] = tlen; + } else { + /* Long length notation */ + sig[(*len)++] = 128 + tlenlen; + assign_big_endian(sig + *len, tlenlen, tlen); + *len += tlenlen; + } + tlen += tlenlen; + } + tlen += 2; + CHECK(tlen + glen <= 1119); + + for (n = 0; n < 2; n++) { + /* Write the integer header. */ + sig[(*len)++] = 0x02; + if (nlenlen[n] == 0) { + /* Short length notation */ + sig[(*len)++] = nlen[n] + nzlen[n]; + } else { + /* Long length notation. */ + sig[(*len)++] = 128 + nlenlen[n]; + assign_big_endian(sig + *len, nlenlen[n], nlen[n] + nzlen[n]); + *len += nlenlen[n]; + } + /* Write zero padding */ + while (nzlen[n] > 0) { + sig[(*len)++] = 0x00; + nzlen[n]--; + } + if (nlen[n] == 32 && !nlow[n]) { + /* Special extra 16 0xFF bytes in "high" 32-byte numbers */ + int i; + for (i = 0; i < 16; i++) { + sig[(*len)++] = 0xFF; + } + nlen[n] -= 16; + } + /* Write first byte of number */ + if (nlen[n] > 0) { + sig[(*len)++] = nhbyte[n]; + nlen[n]--; + } + /* Generate remaining random bytes of number */ + secp256k1_rand_bytes_test(sig + *len, nlen[n]); + *len += nlen[n]; + nlen[n] = 0; + } + + /* Generate random garbage inside tuple. */ + secp256k1_rand_bytes_test(sig + *len, elen); + *len += elen; + + /* Generate end-of-contents bytes. */ + if (indet) { + sig[(*len)++] = 0; + sig[(*len)++] = 0; + tlen += 2; + } + CHECK(tlen + glen <= 1121); + + /* Generate random garbage outside tuple. */ + secp256k1_rand_bytes_test(sig + *len, glen); + *len += glen; + tlen += glen; + CHECK(tlen <= 1121); + CHECK(tlen == *len); +} + +void run_ecdsa_der_parse(void) { + int i,j; + for (i = 0; i < 200 * count; i++) { + unsigned char buffer[2048]; + size_t buflen = 0; + int certainly_der = 0; + int certainly_not_der = 0; + random_ber_signature(buffer, &buflen, &certainly_der, &certainly_not_der); + CHECK(buflen <= 2048); + for (j = 0; j < 16; j++) { + int ret = 0; + if (j > 0) { + damage_array(buffer, &buflen); + /* We don't know anything anymore about the DERness of the result */ + certainly_der = 0; + certainly_not_der = 0; + } + ret = test_ecdsa_der_parse(buffer, buflen, certainly_der, certainly_not_der); + if (ret != 0) { + size_t k; + fprintf(stderr, "Failure %x on ", ret); + for (k = 0; k < buflen; k++) { + fprintf(stderr, "%02x ", buffer[k]); + } + fprintf(stderr, "\n"); + } + CHECK(ret == 0); + } + } +} + /* Tests several edge cases. */ void test_ecdsa_edge_cases(void) { - const unsigned char msg32[32] = { - 'T', 'h', 'i', 's', ' ', 'i', 's', ' ', - 'a', ' ', 'v', 'e', 'r', 'y', ' ', 's', - 'e', 'c', 'r', 'e', 't', ' ', 'm', 'e', - 's', 's', 'a', 'g', 'e', '.', '.', '.' - }; - 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. */ - 0x67, 0xCB, 0x28, 0x5F, 0x9C, 0xD1, 0x94, 0xE8, - 0x40, 0xD6, 0x29, 0x39, 0x7A, 0xF5, 0x56, 0x96, - 0x62, 0xFD, 0xE4, 0x46, 0x49, 0x99, 0x59, 0x63, - 0x17, 0x9A, 0x7D, 0xD1, 0x7B, 0xD2, 0x35, 0x32, - 0x4B, 0x1B, 0x7D, 0xF3, 0x4C, 0xE1, 0xF6, 0x8E, - 0x69, 0x4F, 0xF6, 0xF1, 0x1A, 0xC7, 0x51, 0xDD, - 0x7D, 0xD7, 0x3E, 0x38, 0x7E, 0xE4, 0xFC, 0x86, - 0x6E, 0x1B, 0xE8, 0xEC, 0xC7, 0xDD, 0x95, 0x57 - }; - unsigned char pubkey[65]; int t; - int pubkeylen = 65; - /* signature (r,s) = (4,4), which can be recovered with all 4 recids. */ - const unsigned char sigb64[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, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, - }; - unsigned char pubkeyb[33]; - int pubkeyblen = 33; - int recid; + secp256k1_ecdsa_signature sig; - CHECK(!secp256k1_ecdsa_recover_compact(ctx, msg32, sig64, pubkey, &pubkeylen, 0, 0)); - CHECK(secp256k1_ecdsa_recover_compact(ctx, msg32, sig64, pubkey, &pubkeylen, 0, 1)); - CHECK(!secp256k1_ecdsa_recover_compact(ctx, msg32, sig64, pubkey, &pubkeylen, 0, 2)); - CHECK(!secp256k1_ecdsa_recover_compact(ctx, msg32, sig64, pubkey, &pubkeylen, 0, 3)); + /* Test the case where ECDSA recomputes a point that is infinity. */ + { + secp256k1_gej keyj; + secp256k1_ge key; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 1); + 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_ge_set_gej(&key, &keyj); + msg = ss; + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + } - for (recid = 0; recid < 4; recid++) { - int i; - int recid2; - /* (4,4) encoded in DER. */ - unsigned char sigbder[8] = {0x30, 0x06, 0x02, 0x01, 0x04, 0x02, 0x01, 0x04}; - unsigned char sigcder_zr[7] = {0x30, 0x05, 0x02, 0x00, 0x02, 0x01, 0x01}; - unsigned char sigcder_zs[7] = {0x30, 0x05, 0x02, 0x01, 0x01, 0x02, 0x00}; - unsigned char sigbderalt1[39] = { - 0x30, 0x25, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x04, 0x02, 0x01, 0x04, - }; - unsigned char sigbderalt2[39] = { - 0x30, 0x25, 0x02, 0x01, 0x04, 0x02, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + /* Verify signature with r of zero fails. */ + { + const unsigned char pubkey_mods_zero[33] = { + 0x02, 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 }; - unsigned char sigbderalt3[40] = { - 0x30, 0x26, 0x02, 0x21, 0x00, 0x00, 0x00, 0x00, + secp256k1_ge key; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 1); + 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); + } + + /* Verify signature with s of zero fails. */ + { + const unsigned char pubkey[33] = { + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x01, 0x04, + 0x01 }; - unsigned char sigbderalt4[40] = { - 0x30, 0x26, 0x02, 0x01, 0x04, 0x02, 0x21, 0x00, + secp256k1_ge key; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 0); + 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); + } + + /* Verify signature with message 0 passes. */ + { + const unsigned char pubkey[33] = { + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x02 }; - /* (order + r,4) encoded in DER. */ - unsigned char sigbderlong[40] = { - 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, 0x45, 0x02, 0x01, 0x04 + const unsigned char pubkey2[33] = { + 0x02, 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 }; - CHECK(secp256k1_ecdsa_recover_compact(ctx, msg32, sigb64, pubkeyb, &pubkeyblen, 1, recid)); - CHECK(secp256k1_ecdsa_verify(ctx, msg32, sigbder, sizeof(sigbder), pubkeyb, pubkeyblen) == 1); - for (recid2 = 0; recid2 < 4; recid2++) { - unsigned char pubkey2b[33]; - int pubkey2blen = 33; - CHECK(secp256k1_ecdsa_recover_compact(ctx, msg32, sigb64, pubkey2b, &pubkey2blen, 1, recid2)); - /* Verifying with (order + r,4) should always fail. */ - CHECK(secp256k1_ecdsa_verify(ctx, msg32, sigbderlong, sizeof(sigbderlong), pubkey2b, pubkey2blen) != 1); - } - /* DER parsing tests. */ - /* Zero length r/s. */ - CHECK(secp256k1_ecdsa_verify(ctx, msg32, sigcder_zr, sizeof(sigcder_zr), pubkeyb, pubkeyblen) == -2); - CHECK(secp256k1_ecdsa_verify(ctx, msg32, sigcder_zs, sizeof(sigcder_zs), pubkeyb, pubkeyblen) == -2); - /* Leading zeros. */ - CHECK(secp256k1_ecdsa_verify(ctx, msg32, sigbderalt1, sizeof(sigbderalt1), pubkeyb, pubkeyblen) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, msg32, sigbderalt2, sizeof(sigbderalt2), pubkeyb, pubkeyblen) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, msg32, sigbderalt3, sizeof(sigbderalt3), pubkeyb, pubkeyblen) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, msg32, sigbderalt4, sizeof(sigbderalt4), pubkeyb, pubkeyblen) == 1); - sigbderalt3[4] = 1; - CHECK(secp256k1_ecdsa_verify(ctx, msg32, sigbderalt3, sizeof(sigbderalt3), pubkeyb, pubkeyblen) == -2); - sigbderalt4[7] = 1; - CHECK(secp256k1_ecdsa_verify(ctx, msg32, sigbderalt4, sizeof(sigbderalt4), pubkeyb, pubkeyblen) == -2); - /* Damage signature. */ - sigbder[7]++; - CHECK(secp256k1_ecdsa_verify(ctx, msg32, sigbder, sizeof(sigbder), pubkeyb, pubkeyblen) == 0); - sigbder[7]--; - CHECK(secp256k1_ecdsa_verify(ctx, msg32, sigbder, 6, pubkeyb, pubkeyblen) == -2); - CHECK(secp256k1_ecdsa_verify(ctx, msg32, sigbder, sizeof(sigbder)-1, pubkeyb, pubkeyblen) == -2); - for(i = 0; i < 8; i++) { - int c; - unsigned char orig = sigbder[i]; - /*Try every single-byte change.*/ - for (c = 0; c < 256; c++) { - if (c == orig ) { - continue; - } - sigbder[i] = c; - CHECK(secp256k1_ecdsa_verify(ctx, msg32, sigbder, sizeof(sigbder), pubkeyb, pubkeyblen) == - (i==4 || i==7) ? 0 : -2 ); - } - sigbder[i] = orig; - } + secp256k1_ge key; + secp256k1_ge key2; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 2); + secp256k1_scalar_set_int(&msg, 0); + 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); + 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); + 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); } - /* Test the case where ECDSA recomputes a point that is infinity. */ + /* Verify signature with message 1 passes. */ { - secp256k1_gej_t keyj; - secp256k1_ge_t key; - secp256k1_scalar_t msg; - secp256k1_ecdsa_sig_t sig; - secp256k1_scalar_set_int(&sig.s, 1); - secp256k1_scalar_negate(&sig.s, &sig.s); - secp256k1_scalar_inverse(&sig.s, &sig.s); - secp256k1_scalar_set_int(&sig.r, 1); - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &keyj, &sig.r); - secp256k1_ge_set_gej(&key, &keyj); - msg = sig.s; - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sig, &key, &msg) == 0); + const unsigned char pubkey[33] = { + 0x02, 0x14, 0x4e, 0x5a, 0x58, 0xef, 0x5b, 0x22, + 0x6f, 0xd2, 0xe2, 0x07, 0x6a, 0x77, 0xcf, 0x05, + 0xb4, 0x1d, 0xe7, 0x4a, 0x30, 0x98, 0x27, 0x8c, + 0x93, 0xe6, 0xe6, 0x3c, 0x0b, 0xc4, 0x73, 0x76, + 0x25 + }; + const unsigned char pubkey2[33] = { + 0x02, 0x8a, 0xd5, 0x37, 0xed, 0x73, 0xd9, 0x40, + 0x1d, 0xa0, 0x33, 0xd2, 0xdc, 0xf0, 0xaf, 0xae, + 0x34, 0xcf, 0x5f, 0x96, 0x4c, 0x73, 0x28, 0x0f, + 0x92, 0xc0, 0xf6, 0x9d, 0xd9, 0xb2, 0x09, 0x10, + 0x62 + }; + const unsigned char csr[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x45, 0x51, 0x23, 0x19, 0x50, 0xb7, 0x5f, 0xc4, + 0x40, 0x2d, 0xa1, 0x72, 0x2f, 0xc9, 0xba, 0xeb + }; + secp256k1_ge key; + secp256k1_ge key2; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 1); + secp256k1_scalar_set_int(&msg, 1); + 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); + 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); + 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); } - /* Test r/s equal to zero */ + /* Verify signature with message -1 passes. */ { - /* (1,1) encoded in DER. */ - unsigned char sigcder[8] = {0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01}; - unsigned char sigc64[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, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + const unsigned char pubkey[33] = { + 0x03, 0xaf, 0x97, 0xff, 0x7d, 0x3a, 0xf6, 0xa0, + 0x02, 0x94, 0xbd, 0x9f, 0x4b, 0x2e, 0xd7, 0x52, + 0x28, 0xdb, 0x49, 0x2a, 0x65, 0xcb, 0x1e, 0x27, + 0x57, 0x9c, 0xba, 0x74, 0x20, 0xd5, 0x1d, 0x20, + 0xf1 + }; + const unsigned char csr[32] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x45, 0x51, 0x23, 0x19, 0x50, 0xb7, 0x5f, 0xc4, + 0x40, 0x2d, 0xa1, 0x72, 0x2f, 0xc9, 0xba, 0xee }; - unsigned char pubkeyc[65]; - int pubkeyclen = 65; - CHECK(secp256k1_ecdsa_recover_compact(ctx, msg32, sigc64, pubkeyc, &pubkeyclen, 0, 0) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, msg32, sigcder, sizeof(sigcder), pubkeyc, pubkeyclen) == 1); - sigcder[4] = 0; - sigc64[31] = 0; - CHECK(secp256k1_ecdsa_recover_compact(ctx, msg32, sigc64, pubkeyb, &pubkeyblen, 1, 0) == 0); - CHECK(secp256k1_ecdsa_verify(ctx, msg32, sigcder, sizeof(sigcder), pubkeyc, pubkeyclen) == 0); - sigcder[4] = 1; - sigcder[7] = 0; - sigc64[31] = 1; - sigc64[63] = 0; - CHECK(secp256k1_ecdsa_recover_compact(ctx, msg32, sigc64, pubkeyb, &pubkeyblen, 1, 0) == 0); - CHECK(secp256k1_ecdsa_verify(ctx, msg32, sigcder, sizeof(sigcder), pubkeyc, pubkeyclen) == 0); - } - - /*Signature where s would be zero.*/ + secp256k1_ge key; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 1); + secp256k1_scalar_set_int(&msg, 1); + 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); + secp256k1_scalar_negate(&ss, &ss); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &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); + } + + /* Signature where s would be zero. */ { - const unsigned char nonce[32] = { + 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1810,21 +4166,72 @@ void test_ecdsa_edge_cases(void) { 0xb8, 0x12, 0xe0, 0x0b, 0x81, 0x7a, 0x77, 0x62, 0x65, 0xdf, 0xdd, 0x31, 0xb9, 0x3e, 0x29, 0xa9, }; - unsigned char sig[72]; - int siglen = 72; - CHECK(secp256k1_ecdsa_sign(ctx, msg, sig, &siglen, key, precomputed_nonce_function, nonce) == 0); - CHECK(siglen == 0); - CHECK(secp256k1_ecdsa_sign(ctx, msg, sig, &siglen, key, precomputed_nonce_function, nonce2) == 0); - CHECK(siglen == 0); + 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); 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); + /* 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); siglen = 72; - CHECK(secp256k1_ecdsa_sign(ctx, msg, sig, &siglen, key, precomputed_nonce_function, nonce) == 1); - CHECK(siglen > 0); - CHECK(secp256k1_ecdsa_sign(ctx, msg, sig, &siglen, key, precomputed_nonce_function, nonce2) == 1); - CHECK(siglen > 0); + 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); siglen = 10; - CHECK(secp256k1_ecdsa_sign(ctx, msg, sig, &siglen, key, precomputed_nonce_function, nonce) != 1); - CHECK(siglen == 0); + /* 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); + 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); } /* Nonce function corner cases. */ @@ -1833,65 +4240,43 @@ void test_ecdsa_edge_cases(void) { int i; unsigned char key[32]; unsigned char msg[32]; - unsigned char sig[72]; - unsigned char sig2[72]; - secp256k1_ecdsa_sig_t s[512]; - int siglen = 72; - int siglen2 = 72; - int recid2; + secp256k1_ecdsa_signature sig2; + secp256k1_scalar sr[512], ss; const unsigned char *extra; extra = t == 0 ? NULL : zero; memset(msg, 0, 32); msg[31] = 1; /* High key results in signature failure. */ memset(key, 0xFF, 32); - CHECK(secp256k1_ecdsa_sign(ctx, msg, sig, &siglen, key, NULL, extra) == 0); - CHECK(siglen == 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, msg, sig, &siglen, key, NULL, extra) == 0); - CHECK(siglen == 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, msg, sig, &siglen, key, nonce_function_test_fail, extra) == 0); - CHECK(siglen == 0); - CHECK(secp256k1_ecdsa_sign_compact(ctx, msg, sig, key, nonce_function_test_fail, extra, &recid) == 0); - CHECK(is_empty_compact_signature(sig)); + 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. */ - siglen = 72; - CHECK(secp256k1_ecdsa_sign(ctx, msg, sig, &siglen, key, nonce_function_test_retry, extra) == 1); - CHECK(siglen > 0); - CHECK(secp256k1_ecdsa_sign(ctx, msg, sig2, &siglen2, key, nonce_function_rfc6979, extra) == 1); - CHECK(siglen > 0); - CHECK((siglen == siglen2) && (memcmp(sig, sig2, siglen) == 0)); - CHECK(secp256k1_ecdsa_sign_compact(ctx, msg, sig, key, nonce_function_test_retry, extra, &recid) == 1); - CHECK(!is_empty_compact_signature(sig)); - CHECK(secp256k1_ecdsa_sign_compact(ctx, msg, sig2, key, nonce_function_rfc6979, extra, &recid2) == 1); - CHECK(!is_empty_compact_signature(sig2)); - CHECK((recid == recid2) && (memcmp(sig, sig2, 64) == 0)); - /* The default nonce function is determinstic. */ - siglen = 72; - siglen2 = 72; - CHECK(secp256k1_ecdsa_sign(ctx, msg, sig, &siglen, key, NULL, extra) == 1); - CHECK(siglen > 0); - CHECK(secp256k1_ecdsa_sign(ctx, msg, sig2, &siglen2, key, NULL, extra) == 1); - CHECK(siglen2 > 0); - CHECK((siglen == siglen2) && (memcmp(sig, sig2, siglen) == 0)); - CHECK(secp256k1_ecdsa_sign_compact(ctx, msg, sig, key, NULL, extra, &recid) == 1); - CHECK(!is_empty_compact_signature(sig)); - CHECK(secp256k1_ecdsa_sign_compact(ctx, msg, sig2, key, NULL, extra, &recid2) == 1); - CHECK(!is_empty_compact_signature(sig)); - CHECK((recid == recid2) && (memcmp(sig, sig2, 64) == 0)); + 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(!is_empty_signature(&sig2)); + CHECK(memcmp(&sig, &sig2, sizeof(sig)) == 0); + /* The default nonce function is deterministic. */ + CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, NULL, extra) == 1); + CHECK(!is_empty_signature(&sig2)); + CHECK(memcmp(&sig, &sig2, sizeof(sig)) == 0); /* The default nonce function changes output with different messages. */ for(i = 0; i < 256; i++) { int j; - siglen2 = 72; msg[0] = i; - CHECK(secp256k1_ecdsa_sign(ctx, msg, sig2, &siglen2, key, NULL, extra) == 1); - CHECK(!is_empty_compact_signature(sig)); - CHECK(secp256k1_ecdsa_sig_parse(&s[i], sig2, siglen2)); + 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); for (j = 0; j < i; j++) { - CHECK(!secp256k1_scalar_eq(&s[i].r, &s[j].r)); + CHECK(!secp256k1_scalar_eq(&sr[i], &sr[j])); } } msg[0] = 0; @@ -1899,17 +4284,45 @@ void test_ecdsa_edge_cases(void) { /* The default nonce function changes output with different keys. */ for(i = 256; i < 512; i++) { int j; - siglen2 = 72; key[0] = i - 256; - CHECK(secp256k1_ecdsa_sign(ctx, msg, sig2, &siglen2, key, NULL, extra) == 1); - CHECK(secp256k1_ecdsa_sig_parse(&s[i], sig2, siglen2)); + 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); for (j = 0; j < i; j++) { - CHECK(!secp256k1_scalar_eq(&s[i].r, &s[j].r)); + CHECK(!secp256k1_scalar_eq(&sr[i], &sr[j])); } } key[0] = 0; } + { + /* Check that optional nonce arguments do not have equivalent effect. */ + const unsigned char zeros[32] = {0}; + unsigned char nonce[32]; + 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); + CHECK(nonce_function_rfc6979(nonce, zeros, zeros, NULL, NULL, 0) == 1); + VG_CHECK(nonce,32); + CHECK(nonce_function_rfc6979(nonce2, zeros, zeros, zeros, NULL, 0) == 1); + VG_CHECK(nonce2,32); + CHECK(nonce_function_rfc6979(nonce3, zeros, zeros, NULL, (void *)zeros, 0) == 1); + VG_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); + } + + /* Privkey export where pubkey is the point at infinity. */ { unsigned char privkey[300]; @@ -1919,9 +4332,10 @@ void test_ecdsa_edge_cases(void) { 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41, }; - int outlen = 300; - CHECK(!secp256k1_ec_privkey_export(ctx, seckey, privkey, &outlen, 0)); - CHECK(!secp256k1_ec_privkey_export(ctx, seckey, privkey, &outlen, 1)); + size_t outlen = 300; + CHECK(!ec_privkey_export_der(ctx, privkey, &outlen, seckey, 0)); + outlen = 300; + CHECK(!ec_privkey_export_der(ctx, privkey, &outlen, seckey, 1)); } } @@ -1930,46 +4344,48 @@ void run_ecdsa_edge_cases(void) { } #ifdef ENABLE_OPENSSL_TESTS -EC_KEY *get_openssl_key(const secp256k1_scalar_t *key) { +EC_KEY *get_openssl_key(const unsigned char *key32) { unsigned char privkey[300]; - int privkeylen; + size_t privkeylen; const unsigned char* pbegin = privkey; - int compr = secp256k1_rand32() & 1; + int compr = secp256k1_rand_bits(1); EC_KEY *ec_key = EC_KEY_new_by_curve_name(NID_secp256k1); - CHECK(secp256k1_eckey_privkey_serialize(&ctx->ecmult_gen_ctx, privkey, &privkeylen, key, compr)); + 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; } void test_ecdsa_openssl(void) { - secp256k1_gej_t qj; - secp256k1_ge_t q; - secp256k1_ecdsa_sig_t sig; - secp256k1_scalar_t one; - secp256k1_scalar_t msg2; - secp256k1_scalar_t key, msg; + 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; - int secp_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(&key); - CHECK(ec_key); + 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(&sig, signature, sigsize)); - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sig, &q, &msg)); + 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, &sig, &q, &msg2)); + CHECK(!secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sigr, &sigs, &q, &msg2)); - random_sign(&sig, &key, &msg, NULL); - CHECK(secp256k1_ecdsa_sig_serialize(signature, &secp_sigsize, &sig)); + 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); EC_KEY_free(ec_key); @@ -1983,6 +4399,14 @@ void run_ecdsa_openssl(void) { } #endif +#ifdef ENABLE_MODULE_ECDH +# include "modules/ecdh/tests_impl.h" +#endif + +#ifdef ENABLE_MODULE_RECOVERY +# include "modules/recovery/tests_impl.h" +#endif + int main(int argc, char **argv) { unsigned char seed16[16] = {0}; unsigned char run32[32] = {0}; @@ -2007,7 +4431,7 @@ int main(int argc, char **argv) { } } else { FILE *frand = fopen("/dev/urandom", "r"); - if (!frand || !fread(&seed16, sizeof(seed16), 1, frand)) { + if ((frand == NULL) || !fread(&seed16, sizeof(seed16), 1, frand)) { uint64_t t = time(NULL) * (uint64_t)1337; seed16[0] ^= t; seed16[1] ^= t >> 8; @@ -2028,12 +4452,14 @@ int main(int argc, char **argv) { /* initialize */ run_context_tests(); ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); - - if (secp256k1_rand32() & 1) { + if (secp256k1_rand_bits(1)) { secp256k1_rand256(run32); - CHECK(secp256k1_context_randomize(ctx, secp256k1_rand32() & 1 ? run32 : NULL)); + CHECK(secp256k1_context_randomize(ctx, secp256k1_rand_bits(1) ? run32 : NULL)); } + run_rand_bits(); + run_rand_int(); + run_sha256_tests(); run_hmac_sha256_tests(); run_rfc6979_hmac_sha256_tests(); @@ -2057,6 +4483,7 @@ int main(int argc, char **argv) { /* group tests */ run_ge(); + run_group_decompress(); /* ecmult tests */ run_wnaf(); @@ -2064,9 +4491,28 @@ int main(int argc, char **argv) { run_ecmult_chain(); run_ecmult_constants(); run_ecmult_gen_blind(); + run_ecmult_const_tests(); + run_ec_combine(); + + /* endomorphism tests */ +#ifdef USE_ENDOMORPHISM + run_endomorphism_tests(); +#endif + + /* EC point parser test */ + run_ec_pubkey_parse_test(); + + /* EC key edge cases */ + run_eckey_edge_case_test(); + +#ifdef ENABLE_MODULE_ECDH + /* ecdh tests */ + run_ecdh_tests(); +#endif /* ecdsa tests */ run_random_pubkeys(); + run_ecdsa_der_parse(); run_ecdsa_sign_verify(); run_ecdsa_end_to_end(); run_ecdsa_edge_cases(); @@ -2074,10 +4520,17 @@ int main(int argc, char **argv) { run_ecdsa_openssl(); #endif +#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]); /* shutdown */ secp256k1_context_destroy(ctx); + + printf("no problems found\n"); return 0; } diff --git a/src/secp256k1/src/tests_exhaustive.c b/src/secp256k1/src/tests_exhaustive.c new file mode 100644 index 000000000..b040bb073 --- /dev/null +++ b/src/secp256k1/src/tests_exhaustive.c @@ -0,0 +1,470 @@ +/*********************************************************************** + * Copyright (c) 2016 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#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" + +#ifdef ENABLE_MODULE_RECOVERY +#include "src/modules/recovery/main_impl.h" +#include "include/secp256k1_recovery.h" +#endif + +/** 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)); +} + +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 random_fe(secp256k1_fe *x) { + unsigned char bin[32]; + do { + secp256k1_rand256(bin); + if (secp256k1_fe_set_b32(x, bin)) { + return; + } + } while(1); +} +/** END stolen from tests.c */ + +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; + int *idata = data; + (void)msg32; + (void)key32; + (void)algo16; + /* Some nonces cannot be used because they'd cause s and/or r to be zero. + * The signing function has retry logic here that just re-calls the nonce + * function with an increased `attempt`. So if attempt > 0 this means we + * need to change the nonce to avoid an infinite loop. */ + if (attempt > 0) { + *idata = (*idata + 1) % EXHAUSTIVE_TEST_ORDER; + } + secp256k1_scalar_set_int(&s, *idata); + secp256k1_scalar_get_b32(nonce32, &s); + return 1; +} + +#ifdef USE_ENDOMORPHISM +void test_exhaustive_endomorphism(const secp256k1_ge *group, int order) { + int i; + for (i = 0; i < order; i++) { + secp256k1_ge res; + secp256k1_ge_mul_lambda(&res, &group[i]); + ge_equals_ge(&group[i * EXHAUSTIVE_TEST_LAMBDA % EXHAUSTIVE_TEST_ORDER], &res); + } +} +#endif + +void test_exhaustive_addition(const secp256k1_ge *group, const secp256k1_gej *groupj, int order) { + int i, j; + + /* 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++) { + CHECK(!secp256k1_ge_is_infinity(&group[i])); + CHECK(!secp256k1_gej_is_infinity(&groupj[i])); + } + + /* Check all addition formulae */ + for (j = 0; j < order; j++) { + secp256k1_fe fe_inv; + secp256k1_fe_inv(&fe_inv, &groupj[j].z); + for (i = 0; i < 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); + /* add_ge */ + if (j > 0) { + secp256k1_gej_add_ge(&tmp, &groupj[i], &group[j]); + ge_equals_gej(&group[(i + j) % order], &tmp); + } + /* add_ge_var */ + secp256k1_gej_add_ge_var(&tmp, &groupj[i], &group[j], NULL); + ge_equals_gej(&group[(i + j) % order], &tmp); + /* 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 doubling */ + for (i = 0; i < 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_var(&tmp, &groupj[i], NULL); + ge_equals_gej(&group[(2 * i) % order], &tmp); + } + + /* Check negation */ + for (i = 1; i < order; i++) { + secp256k1_ge tmp; + secp256k1_gej tmpj; + secp256k1_ge_neg(&tmp, &group[i]); + ge_equals_ge(&group[order - i], &tmp); + secp256k1_gej_neg(&tmpj, &groupj[i]); + ge_equals_gej(&group[order - i], &tmpj); + } +} + +void test_exhaustive_ecmult(const secp256k1_context *ctx, const secp256k1_ge *group, const secp256k1_gej *groupj, int order) { + 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++) { + 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); + + if (i > 0) { + secp256k1_ecmult_const(&tmp, &group[i], &ng); + ge_equals_gej(&group[(i * j) % order], &tmp); + } + } + } + } +} + +void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k) { + 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); +} + +void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *group, int order) { + 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_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 k, should_verify; + unsigned char msg32[32]; + + 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); + + /* 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); + + /* Verify by calling verify */ + secp256k1_ecdsa_signature_save(&sig, &r_s, &s_s); + memcpy(&nonconst_ge, &group[sk_s], sizeof(nonconst_ge)); + secp256k1_pubkey_save(&pk, &nonconst_ge); + secp256k1_scalar_get_b32(msg32, &msg_s); + CHECK(should_verify == + secp256k1_ecdsa_verify(ctx, &sig, msg32, &pk)); + } + } + } + } +} + +void test_exhaustive_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_ecdsa_signature sig; + secp256k1_scalar sk, msg, r, s, expected_r; + unsigned char sk32[32], msg32[32]; + 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(ctx, &sig, msg32, sk32, secp256k1_nonce_function_smallint, &k); + + 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); + + /* Overflow means we've tried every possible nonce */ + if (k < starting_k) { + break; + } + } + } + } + + /* We would like to verify zero-knowledge here by counting how often every + * possible (s, r) tuple appears, but because the group order is larger + * than the field order, when coercing the x-values to scalar values, some + * appear more often than others, so we are actually not zero-knowledge. + * (This effect also appears in the real code, but the difference is on the + * order of 1/2^128th the field order, so the deviation is not useful to a + * computationally bounded attacker.) + */ +} + +#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); + + 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); + 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); + + /* 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); + + /* Overflow means we've tried every possible nonce */ + if (k < starting_k) { + break; + } + } + } + } +} + +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]; + + 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 < 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); + + /* 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)); + } + } + } + } +} +#endif + +int main(void) { + int i; + secp256k1_gej groupj[EXHAUSTIVE_TEST_ORDER]; + secp256k1_ge group[EXHAUSTIVE_TEST_ORDER]; + + /* 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 */ + + /* 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)); + } + } + + /* 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); + +#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); + return 0; +} + diff --git a/src/secp256k1/src/util.h b/src/secp256k1/src/util.h index ae98639f7..b0441d8e3 100644 --- a/src/secp256k1/src/util.h +++ b/src/secp256k1/src/util.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_UTIL_H_ -#define _SECP256K1_UTIL_H_ +#ifndef SECP256K1_UTIL_H +#define SECP256K1_UTIL_H #if defined HAVE_CONFIG_H #include "libsecp256k1-config.h" @@ -15,6 +15,15 @@ #include #include +typedef struct { + void (*fn)(const char *text, void* data); + const void* data; +} secp256k1_callback; + +static SECP256K1_INLINE void secp256k1_callback_call(const secp256k1_callback * const cb, const char * const text) { + cb->fn(text, (void*)cb->data); +} + #ifdef DETERMINISTIC #define TEST_FAILURE(msg) do { \ fprintf(stderr, "%s\n", msg); \ @@ -47,23 +56,23 @@ } while(0) #endif -/* Like assert(), but safe to use on expressions with side effects. */ -#ifndef NDEBUG -#define DEBUG_CHECK CHECK -#else -#define DEBUG_CHECK(cond) do { (void)(cond); } while(0) -#endif - -/* Like DEBUG_CHECK(), but when VERIFY is defined instead of NDEBUG not defined. */ -#ifdef VERIFY +/* 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) #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) #endif -static SECP256K1_INLINE void *checked_malloc(size_t size) { +static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_t size) { void *ret = malloc(size); - CHECK(ret != NULL); + if (ret == NULL) { + secp256k1_callback_call(cb, "Out of memory"); + } return ret; } @@ -101,4 +110,4 @@ static SECP256K1_INLINE void *checked_malloc(size_t size) { SECP256K1_GNUC_EXT typedef unsigned __int128 uint128_t; #endif -#endif +#endif /* SECP256K1_UTIL_H */ diff --git a/src/sendalert.cpp b/src/sendalert.cpp index fbe6dc03a..3b7b53734 100644 --- a/src/sendalert.cpp +++ b/src/sendalert.cpp @@ -71,9 +71,9 @@ void ThreadSendAlert() // CAlert alert; alert.nRelayUntil = GetTime() + 15 * 60; - alert.nExpiration = GetTime() + 90 * 24 * 60 * 60; - alert.nID = 1001; // use https://github.com/zcash/zcash/wiki/specification#assigned-numbers to keep track of alert IDs - alert.nCancel = 0; // cancels previous messages up to this ID number + alert.nExpiration = GetTime() + 12 * 30 * 24 * 60 * 60; + alert.nID = 1004; // use https://github.com/zcash/zcash/wiki/specification#assigned-numbers to keep track of alert IDs + alert.nCancel = 1001; // cancels previous messages up to this ID number // These versions are protocol versions // 170002 : 1.0.0 @@ -81,19 +81,23 @@ void ThreadSendAlert() alert.nMaxVer = 170002; // - // main.cpp: + // main.cpp: // 1000 for Misc warnings like out of disk space and clock is wrong - // 2000 for longer invalid proof-of-work chain + // 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 = 1500; + alert.nPriority = 4000; alert.strComment = ""; - alert.strStatusBar = "Your client is out of date and potentially vulnerable to denial of service. Please update to the most recent version of Zcash (1.0.5). More info at: https://z.cash/support/security.html"; - alert.strRPCError = "Your client is out of date and potentially vulnerable to denial of service. Please update to the most recent version of Zcash (1.0.5). More info at: https://z.cash/support/security.html"; + alert.strStatusBar = "Your client version 1.0.10 has degraded networking behavior. Please update to the most recent version of Zcash (1.0.10-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/")); - alert.setSubVer.insert(std::string("/MagicBean:1.0.3/")); + const std::vector useragents = {"MagicBean", "BeanStalk", "AppleSeed", "EleosZcash"}; + + BOOST_FOREACH(const std::string& useragent, useragents) { + alert.setSubVer.insert(std::string("/"+useragent+":1.0.10/")); + } // Sanity check assert(alert.strComment.length() <= 65536); // max length in alert.h diff --git a/src/snark/.gitignore b/src/snark/.gitignore new file mode 100644 index 000000000..ea2a20f44 --- /dev/null +++ b/src/snark/.gitignore @@ -0,0 +1,51 @@ +*.o +*.a +*.so +*.d +libsnark/gtests +depinst/ +depsrc/ +README.html +doxygen/ +libsnark/gtests +libsnark/gadgetlib2/examples/tutorial +libsnark/gadgetlib2/tests/gadgetlib2_test + +libsnark/algebra/curves/tests/test_bilinearity +libsnark/algebra/curves/tests/test_groups +libsnark/algebra/fields/tests/test_fields +libsnark/common/routing_algorithms/profiling/profile_routing_algorithms +libsnark/common/routing_algorithms/tests/test_routing_algorithms +libsnark/gadgetlib1/gadgets/cpu_checkers/fooram/examples/test_fooram +libsnark/gadgetlib1/gadgets/hashes/knapsack/tests/test_knapsack_gadget +libsnark/gadgetlib1/gadgets/hashes/sha256/tests/test_sha256_gadget +libsnark/gadgetlib1/gadgets/merkle_tree/tests/test_merkle_tree_gadgets +libsnark/gadgetlib1/gadgets/routing/profiling/profile_routing_gadgets +libsnark/gadgetlib1/gadgets/set_commitment/tests/test_set_commitment_gadget +libsnark/gadgetlib1/gadgets/verifiers/tests/test_r1cs_ppzksnark_verifier_gadget +libsnark/reductions/ram_to_r1cs/examples/demo_arithmetization +libsnark/relations/arithmetic_programs/qap/tests/test_qap +libsnark/relations/arithmetic_programs/ssp/tests/test_ssp +libsnark/zk_proof_systems/pcd/r1cs_pcd/r1cs_mp_ppzkpcd/profiling/profile_r1cs_mp_ppzkpcd +libsnark/zk_proof_systems/pcd/r1cs_pcd/r1cs_mp_ppzkpcd/tests/test_r1cs_mp_ppzkpcd +libsnark/zk_proof_systems/pcd/r1cs_pcd/r1cs_sp_ppzkpcd/profiling/profile_r1cs_sp_ppzkpcd +libsnark/zk_proof_systems/pcd/r1cs_pcd/r1cs_sp_ppzkpcd/tests/test_r1cs_sp_ppzkpcd +libsnark/zk_proof_systems/ppzkadsnark/r1cs_ppzkadsnark/examples/demo_r1cs_ppzkadsnark +libsnark/zk_proof_systems/ppzksnark/bacs_ppzksnark/profiling/profile_bacs_ppzksnark +libsnark/zk_proof_systems/ppzksnark/bacs_ppzksnark/tests/test_bacs_ppzksnark +libsnark/zk_proof_systems/ppzksnark/r1cs_gg_ppzksnark/profiling/profile_r1cs_gg_ppzksnark +libsnark/zk_proof_systems/ppzksnark/r1cs_gg_ppzksnark/tests/test_r1cs_gg_ppzksnark +libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/profiling/profile_r1cs_ppzksnark +libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/tests/test_r1cs_ppzksnark +libsnark/zk_proof_systems/ppzksnark/ram_ppzksnark/examples/demo_ram_ppzksnark +libsnark/zk_proof_systems/ppzksnark/ram_ppzksnark/examples/demo_ram_ppzksnark_generator +libsnark/zk_proof_systems/ppzksnark/ram_ppzksnark/examples/demo_ram_ppzksnark_prover +libsnark/zk_proof_systems/ppzksnark/ram_ppzksnark/examples/demo_ram_ppzksnark_verifier +libsnark/zk_proof_systems/ppzksnark/ram_ppzksnark/profiling/profile_ram_ppzksnark +libsnark/zk_proof_systems/ppzksnark/ram_ppzksnark/tests/test_ram_ppzksnark +libsnark/zk_proof_systems/ppzksnark/tbcs_ppzksnark/profiling/profile_tbcs_ppzksnark +libsnark/zk_proof_systems/ppzksnark/tbcs_ppzksnark/tests/test_tbcs_ppzksnark +libsnark/zk_proof_systems/ppzksnark/uscs_ppzksnark/profiling/profile_uscs_ppzksnark +libsnark/zk_proof_systems/ppzksnark/uscs_ppzksnark/tests/test_uscs_ppzksnark +libsnark/zk_proof_systems/zksnark/ram_zksnark/profiling/profile_ram_zksnark +libsnark/zk_proof_systems/zksnark/ram_zksnark/tests/test_ram_zksnark diff --git a/src/snark/AUTHORS b/src/snark/AUTHORS new file mode 100644 index 000000000..1b2d7a247 --- /dev/null +++ b/src/snark/AUTHORS @@ -0,0 +1,19 @@ +SCIPR Lab: + Eli Ben-Sasson + Alessandro Chiesa + Daniel Genkin + Shaul Kfir + Eran Tromer + Madars Virza + +External contributors: + Michael Backes + Manuel Barbosa + Dario Fiore + Jens Groth + Joshua A. Kroll + Shigeo MITSUNARI + Raphael Reischuk + Tadanori TERUYA + Sean Bowe + Daira Hopwood diff --git a/src/snark/LICENSE b/src/snark/LICENSE new file mode 100644 index 000000000..81cea11e1 --- /dev/null +++ b/src/snark/LICENSE @@ -0,0 +1,24 @@ +The libsnark library is developed by SCIPR Lab (http://scipr-lab.org) +and contributors. + +Copyright (c) 2012-2014 SCIPR Lab and contributors (see AUTHORS file). + +All files, with the exceptions below, are released under the MIT License: + + 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/snark/Makefile b/src/snark/Makefile new file mode 100644 index 000000000..b865f992f --- /dev/null +++ b/src/snark/Makefile @@ -0,0 +1,282 @@ +#******************************************************************************** +# Makefile for the libsnark library. +#******************************************************************************** +#* @author This file is part of libsnark, developed by SCIPR Lab +#* and contributors (see AUTHORS). +#* @copyright MIT license (see LICENSE file) +#*******************************************************************************/ + +# To override these, use "make OPTFLAGS=..." etc. +CURVE = BN128 +OPTFLAGS = -O2 -march=native -mtune=native +FEATUREFLAGS = -DUSE_ASM -DMONTGOMERY_OUTPUT + +# Initialize this using "CXXFLAGS=... make". The makefile appends to that. +CXXFLAGS += -std=c++11 -Wall -Wextra -Wno-unused-parameter -Wno-comment -Wfatal-errors $(OPTFLAGS) $(FEATUREFLAGS) -DCURVE_$(CURVE) + +DEPSRC = depsrc +DEPINST = depinst + +CXXFLAGS += -I$(DEPINST)/include -Ilibsnark +LDFLAGS += -L$(DEPINST)/lib -Wl,-rpath,$(DEPINST)/lib +LDLIBS += -lgmpxx -lgmp -lboost_program_options-mt -lsodium +# List of .a files to include within libsnark.a and libsnark.so: +AR_LIBS = +# List of library files to install: +INSTALL_LIBS = $(LIB_FILE) +# Sentinel file to check existence of this directory (since directories don't work as a Make dependency): +DEPINST_EXISTS = $(DEPINST)/.exists + +ifneq ($(NO_GTEST),1) + # Compile GTest from sourcecode if we can (e.g., Ubuntu). Otherwise use precompiled one (e.g., Fedora). + # See https://github.com/google/googletest/blob/master/googletest/docs/FAQ.md#why-is-it-not-recommended-to-install-a-pre-compiled-copy-of-google-test-for-example-into-usrlocal + ifneq ($(NO_COMPILE_LIBGTEST),1) + GTESTDIR=/usr/src/gtest + COMPILE_LIBGTEST = $(shell test -d $(GTESTDIR) && echo -n 1) + endif + GTEST_LDLIBS += -lgtest -lpthread +endif + +ifneq ($(NO_SUPERCOP),1) + SUPERCOP_LDLIBS += -lsupercop + INSTALL_LIBS += depinst/lib/libsupercop.a + # Would have been nicer to roll supercop into libsnark.a ("AR_LIBS += $(DEPINST)/lib/libsupercop.a"), but it doesn't support position-independent code (libsnark issue #20). +endif + +LIB_SRCS = \ + libsnark/algebra/curves/alt_bn128/alt_bn128_g1.cpp \ + libsnark/algebra/curves/alt_bn128/alt_bn128_g2.cpp \ + libsnark/algebra/curves/alt_bn128/alt_bn128_init.cpp \ + libsnark/algebra/curves/alt_bn128/alt_bn128_pairing.cpp \ + libsnark/algebra/curves/alt_bn128/alt_bn128_pp.cpp \ + libsnark/common/profiling.cpp \ + libsnark/common/utils.cpp \ + libsnark/gadgetlib1/constraint_profiling.cpp \ + +ifeq ($(CURVE),BN128) + LIB_SRCS += \ + libsnark/algebra/curves/bn128/bn128_g1.cpp \ + libsnark/algebra/curves/bn128/bn128_g2.cpp \ + libsnark/algebra/curves/bn128/bn128_gt.cpp \ + libsnark/algebra/curves/bn128/bn128_init.cpp \ + libsnark/algebra/curves/bn128/bn128_pairing.cpp \ + libsnark/algebra/curves/bn128/bn128_pp.cpp + + CXXFLAGS += -DBN_SUPPORT_SNARK + AR_LIBS += $(DEPINST)/lib/libzm.a +endif + +# FIXME: most of these are broken due to removed code. +DISABLED_EXECUTABLES = \ + libsnark/common/routing_algorithms/profiling/profile_routing_algorithms \ + libsnark/common/routing_algorithms/tests/test_routing_algorithms \ + libsnark/gadgetlib1/gadgets/cpu_checkers/fooram/examples/test_fooram \ + libsnark/gadgetlib1/gadgets/hashes/knapsack/tests/test_knapsack_gadget \ + libsnark/gadgetlib1/gadgets/routing/profiling/profile_routing_gadgets \ + libsnark/gadgetlib1/gadgets/set_commitment/tests/test_set_commitment_gadget \ + libsnark/gadgetlib1/gadgets/verifiers/tests/test_r1cs_ppzksnark_verifier_gadget \ + libsnark/reductions/ram_to_r1cs/examples/demo_arithmetization \ + libsnark/relations/arithmetic_programs/ssp/tests/test_ssp \ + libsnark/zk_proof_systems/pcd/r1cs_pcd/r1cs_mp_ppzkpcd/profiling/profile_r1cs_mp_ppzkpcd \ + libsnark/zk_proof_systems/pcd/r1cs_pcd/r1cs_mp_ppzkpcd/tests/test_r1cs_mp_ppzkpcd \ + libsnark/zk_proof_systems/pcd/r1cs_pcd/r1cs_sp_ppzkpcd/profiling/profile_r1cs_sp_ppzkpcd \ + libsnark/zk_proof_systems/pcd/r1cs_pcd/r1cs_sp_ppzkpcd/tests/test_r1cs_sp_ppzkpcd \ + libsnark/zk_proof_systems/ppzksnark/bacs_ppzksnark/profiling/profile_bacs_ppzksnark \ + libsnark/zk_proof_systems/ppzksnark/bacs_ppzksnark/tests/test_bacs_ppzksnark \ + libsnark/zk_proof_systems/ppzksnark/r1cs_gg_ppzksnark/profiling/profile_r1cs_gg_ppzksnark \ + libsnark/zk_proof_systems/ppzksnark/r1cs_gg_ppzksnark/tests/test_r1cs_gg_ppzksnark \ + libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/profiling/profile_r1cs_ppzksnark \ + libsnark/zk_proof_systems/ppzksnark/ram_ppzksnark/examples/demo_ram_ppzksnark \ + libsnark/zk_proof_systems/ppzksnark/ram_ppzksnark/examples/demo_ram_ppzksnark_generator \ + libsnark/zk_proof_systems/ppzksnark/ram_ppzksnark/examples/demo_ram_ppzksnark_prover \ + libsnark/zk_proof_systems/ppzksnark/ram_ppzksnark/examples/demo_ram_ppzksnark_verifier \ + libsnark/zk_proof_systems/ppzksnark/ram_ppzksnark/profiling/profile_ram_ppzksnark \ + libsnark/zk_proof_systems/ppzksnark/ram_ppzksnark/tests/test_ram_ppzksnark \ + libsnark/zk_proof_systems/ppzksnark/tbcs_ppzksnark/profiling/profile_tbcs_ppzksnark \ + libsnark/zk_proof_systems/ppzksnark/tbcs_ppzksnark/tests/test_tbcs_ppzksnark \ + libsnark/zk_proof_systems/ppzksnark/uscs_ppzksnark/profiling/profile_uscs_ppzksnark \ + libsnark/zk_proof_systems/ppzksnark/uscs_ppzksnark/tests/test_uscs_ppzksnark \ + libsnark/zk_proof_systems/zksnark/ram_zksnark/profiling/profile_ram_zksnark \ + libsnark/zk_proof_systems/zksnark/ram_zksnark/tests/test_ram_zksnark + +EXECUTABLES = + +EXECUTABLES_WITH_GTEST = + +EXECUTABLES_WITH_SUPERCOP = \ + libsnark/zk_proof_systems/ppzkadsnark/r1cs_ppzkadsnark/examples/demo_r1cs_ppzkadsnark + +GTEST_TESTS = libsnark/gtests + +GTEST_SRCS = \ + libsnark/algebra/curves/tests/test_bilinearity.cpp \ + libsnark/algebra/curves/tests/test_groups.cpp \ + libsnark/algebra/fields/tests/test_bigint.cpp \ + libsnark/algebra/fields/tests/test_fields.cpp \ + libsnark/gadgetlib1/gadgets/hashes/sha256/tests/test_sha256_gadget.cpp \ + libsnark/gadgetlib1/gadgets/merkle_tree/tests/test_merkle_tree_gadgets.cpp \ + libsnark/relations/arithmetic_programs/qap/tests/test_qap.cpp \ + libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/tests/test_r1cs_ppzksnark.cpp \ + libsnark/gtests.cpp + +DOCS = README.html + +LIBSNARK_A = libsnark.a + +# For documentation of the following options, see README.md . + +ifeq ($(NO_PROCPS),1) + CXXFLAGS += -DNO_PROCPS +else + LDLIBS += -lprocps +endif + +ifeq ($(LOWMEM),1) + CXXFLAGS += -DLOWMEM +endif + +ifeq ($(PROFILE_OP_COUNTS),1) + STATIC = 1 + CXXFLAGS += -DPROFILE_OP_COUNTS +endif + +ifeq ($(STATIC),1) + CXXFLAGS += -static -DSTATIC +else + CXXFLAGS += -fPIC +endif + +ifeq ($(MULTICORE),1) + CXXFLAGS += -DMULTICORE -fopenmp +endif + +ifeq ($(CPPDEBUG),1) + CXXFLAGS += -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC + DEBUG = 1 +endif + +ifeq ($(DEBUG),1) + CXXFLAGS += -DDEBUG -ggdb3 +endif + +ifeq ($(PERFORMANCE),1) + OPTFLAGS = -O3 -march=native -mtune=native + CXXFLAGS += -DNDEBUG + # Enable link-time optimization: + CXXFLAGS += -flto -fuse-linker-plugin + LDFLAGS += -flto +endif + +LIB_OBJS =$(patsubst %.cpp,%.o,$(LIB_SRCS)) +EXEC_OBJS =$(patsubst %,%.o,$(EXECUTABLES) $(EXECUTABLES_WITH_GTEST) $(EXECUTABLES_WITH_SUPERCOP)) +GTEST_OBJS =$(patsubst %.cpp,%.o,$(GTEST_SRCS)) + +all: \ + $(if $(NO_GTEST),,$(EXECUTABLES_WITH_GTEST) $(GTEST_TESTS)) \ + $(if $(NO_SUPERCOP),,$(EXECUTABLES_WITH_SUPERCOP)) \ + $(EXECUTABLES) \ + $(if $(NO_DOCS),,doc) + +doc: $(DOCS) + +$(DEPINST_EXISTS): + # Create placeholder directories for installed dependencies. Some make settings (including the default) require actually running ./prepare-depends.sh to populate this directory. + mkdir -p $(DEPINST)/lib $(DEPINST)/include + touch $@ + +# In order to detect changes to #include dependencies. -MMD below generates a .d file for each .o file. Include the .d file. +-include $(patsubst %.o,%.d, $(LIB_OBJS) $(GTEST_OBJS) $(EXEC_OBJS) ) + +$(LIB_OBJS) $(if $(NO_GTEST),,$(GTEST_OBJS)) $(EXEC_OBJS): %.o: %.cpp + $(CXX) -o $@ $< -c -MMD $(CXXFLAGS) + +LIBGTEST_A = $(DEPINST)/lib/libgtest.a + +$(LIBGTEST_A): $(GTESTDIR)/libsnark/gtest-all.cc $(DEPINST_EXISTS) + $(CXX) -o $(DEPINST)/lib/gtest-all.o -I $(GTESTDIR) -c -isystem $(GTESTDIR)/include $< $(CXXFLAGS) + $(AR) -rv $(LIBGTEST_A) $(DEPINST)/lib/gtest-all.o + +# libsnark.a will contains all of our relevant object files, and we also mash in the .a files of relevant dependencies built by ./prepare-depends.sh +$(LIBSNARK_A): $(LIB_OBJS) $(AR_LIBS) + $(AR) q $(LIBSNARK_A) $(LIB_OBJS) + if [ -n "$(AR_LIBS)" ]; then mkdir -p tmp-ar; cd tmp-ar; for AR_LIB in $(AR_LIBS); do $(AR) x $$AR_LIB; done; $(AR) qc $(LIBSNARK_A) tmp-ar/*; cd ..; rm -r tmp-ar; fi; + $(AR) s $(LIBSNARK_A) + +libsnark.so: $(LIBSNARK_A) $(DEPINST_EXISTS) + $(CXX) -o $@ --shared -Wl,--whole-archive $(LIBSNARK_A) $(CXXFLAGS) $(LDFLAGS) -Wl,--no-whole-archive $(LDLIBS) + +libsnark/gadgetlib2/tests/gadgetlib2_test: \ + libsnark/gadgetlib2/tests/adapters_UTEST.cpp \ + libsnark/gadgetlib2/tests/constraint_UTEST.cpp \ + libsnark/gadgetlib2/tests/gadget_UTEST.cpp \ + libsnark/gadgetlib2/tests/integration_UTEST.cpp \ + libsnark/gadgetlib2/tests/protoboard_UTEST.cpp \ + libsnark/gadgetlib2/tests/variable_UTEST.cpp + +$(EXECUTABLES): %: %.o $(LIBSNARK_A) $(DEPINST_EXISTS) + $(CXX) -o $@ $@.o $(LIBSNARK_A) $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) + +$(EXECUTABLES_WITH_GTEST): %: %.o $(LIBSNARK_A) $(if $(COMPILE_LIBGTEST),$(LIBGTEST_A)) $(DEPINST_EXISTS) + $(CXX) -o $@ $@.o $(LIBSNARK_A) $(CXXFLAGS) $(LDFLAGS) $(GTEST_LDLIBS) $(LDLIBS) + +$(EXECUTABLES_WITH_SUPERCOP): %: %.o $(LIBSNARK_A) $(DEPINST_EXISTS) + $(CXX) -o $@ $@.o $(LIBSNARK_A) $(CXXFLAGS) $(LDFLAGS) $(SUPERCOP_LDLIBS) $(LDLIBS) + +$(GTEST_TESTS): %: $(GTEST_OBJS) $(LIBSNARK_A) $(if $(COMPILE_LIBGTEST),$(LIBGTEST_A)) $(DEPINST_EXISTS) + $(CXX) -o $@ $(GTEST_OBJS) $(LIBSNARK_A) $(CXXFLAGS) $(LDFLAGS) $(GTEST_LDLIBS) $(LDLIBS) + + +ifeq ($(STATIC),1) +LIB_FILE = $(LIBSNARK_A) +else +LIB_FILE = libsnark.so +endif + +lib: $(LIB_FILE) + +$(DOCS): %.html: %.md + markdown_py -f $@ $^ -x toc -x extra --noisy +# TODO: Would be nice to enable "-x smartypants" but Ubuntu 12.04 doesn't support that. +# TODO: switch to redcarpet, to produce same output as GitHub's processing of README.md. But what about TOC? + +ifeq ($(PREFIX),) +install: + $(error Please provide PREFIX. E.g. make install PREFIX=/usr) +else +HEADERS_SRC=$(shell find libsnark -name '*.hpp' -o -name '*.tcc') +HEADERS_DEST=$(patsubst libsnark/%,$(PREFIX)/include/libsnark/%,$(HEADERS_SRC)) + +$(HEADERS_DEST): $(PREFIX)/include/libsnark/%: libsnark/% + mkdir -p $(shell dirname $@) + cp $< $@ + +install: $(INSTALL_LIBS) $(HEADERS_DEST) $(DEPINST_EXISTS) + mkdir -p $(PREFIX)/lib + cp -v $(INSTALL_LIBS) $(PREFIX)/lib/ +ifneq ($(NO_COPY_DEPINST),1) + cp -rv $(DEPINST)/include $(PREFIX) +endif +endif + +check: $(GTEST_TESTS) + $(GTEST_TESTS) + +doxy: + doxygen doxygen.conf + +# Clean generated files, except locally-compiled dependencies +clean: + $(RM) \ + $(LIB_OBJS) $(GTEST_OBJS) $(EXEC_OBJS) \ + $(EXECUTABLES) $(EXECUTABLES_WITH_GTEST) $(EXECUTABLES_WITH_SUPERCOP) $(GTEST_TESTS) \ + $(DOCS) \ + ${patsubst %.o,%.d,${LIB_OBJS} ${GTEST_OBJS} ${EXEC_OBJS}} \ + libsnark.so $(LIBSNARK_A) \ + $(RM) -fr doxygen/ \ + $(RM) $(LIBGTEST_A) $(DEPINST)/lib/gtest-all.o + +# Clean all, including locally-compiled dependencies +clean-all: clean + $(RM) -fr $(DEPSRC) $(DEPINST) + +.PHONY: all clean clean-all doc doxy lib install diff --git a/src/snark/README.md b/src/snark/README.md new file mode 100644 index 000000000..89183b43a --- /dev/null +++ b/src/snark/README.md @@ -0,0 +1,628 @@ +libsnark: a C++ library for zkSNARK proofs +================================================================================ + +-------------------------------------------------------------------------------- +Authors +-------------------------------------------------------------------------------- + +The libsnark library is developed by the [SCIPR Lab] project and contributors +and is released under the MIT License (see the [LICENSE] file). + +Copyright (c) 2012-2014 SCIPR Lab and contributors (see [AUTHORS] file). + +-------------------------------------------------------------------------------- +[TOC] + + + +-------------------------------------------------------------------------------- +Overview +-------------------------------------------------------------------------------- + +This library implements __zkSNARK__ schemes, which are a cryptographic method +for proving/verifying, in zero knowledge, the integrity of computations. + +A computation can be expressed as an NP statement, in forms such as the following: + +- "The C program _foo_, when executed, returns exit code 0 if given the input _bar_ and some additional input _qux_." +- "The Boolean circuit _foo_ is satisfiable by some input _qux_." +- "The arithmetic circuit _foo_ accepts the partial assignment _bar_, when extended into some full assignment _qux_." +- "The set of constraints _foo_ is satisfiable by the partial assignment _bar_, when extended into some full assignment _qux_." + +A prover who knows the witness for the NP statement (i.e., a satisfying input/assignment) can produce a short proof attesting to the truth of the NP statement. This proof can be verified by anyone, and offers the following properties. + +- __Zero knowledge:__ + the verifier learns nothing from the proof beside the truth of the statement (i.e., the value _qux_, in the above examples, remains secret). +- __Succinctness:__ + the proof is short and easy to verify. +- __Non-interactivity:__ + the proof is a string (i.e. it does not require back-and-forth interaction between the prover and the verifier). +- __Soundness:__ + the proof is computationally sound (i.e., it is infeasible to fake a proof of a false NP statement). Such a proof system is also called an _argument_. +- __Proof of knowledge:__ + the proof attests not just that the NP statement is true, but also that the + prover knows why (e.g., knows a valid _qux_). + +These properties are summarized by the _zkSNARK_ acronym, which stands for _Zero-Knowledge Succinct Non-interactive ARgument of Knowledge_ (though zkSNARKs are also knows as +_succinct non-interactive computationally-sound zero-knowledge proofs of knowledge_). +For formal definitions and theoretical discussions about these, see +\[BCCT12], \[BCIOP13], and the references therein. + +The libsnark library currently provides a C++ implementation of: + +1. General-purpose proof systems: + 1. A preprocessing zkSNARK for the NP-complete language "R1CS" + (_Rank-1 Constraint Systems_), which is a language that is similar to arithmetic + circuit satisfiability. + 2. A preprocessing SNARK for a language of arithmetic circuits, "BACS" + (_Bilinear Arithmetic Circuit Satisfiability_). This simplifies the writing + of NP statements when the additional flexibility of R1CS is not needed. + Internally, it reduces to R1CS. + 3. A preprocessing SNARK for the language "USCS" + (_Unitary-Square Constraint Systems_). This abstracts and implements the core + contribution of \[DFGK14] + 4. A preprocessing SNARK for a language of Boolean circuits, "TBCS" + (_Two-input Boolean Circuit Satisfiability_). Internally, it reduces to USCS. + This is much more efficient than going through R1CS. + 5. ADSNARK, a preprocessing SNARKs for proving statements on authenticated + data, as described in \[BBFR15]. + 6. Proof-Carrying Data (PCD). This uses recursive composition of SNARKs, as + explained in \[BCCT13] and optimized in \[BCTV14b]. +2. Gadget libraries (gadgetlib1 and gadgetlib2) for constructing R1CS + instances out of modular "gadget" classes. +3. Examples of applications that use the above proof systems to prove + statements about: + 1. Several toy examples. + 2. Execution of TinyRAM machine code, as explained in \[BCTV14a] and + \[BCGTV13]. (Such machine code can be obtained, e.g., by compiling from C.) + This is easily adapted to any other Random Access Machine that satisfies a + simple load-store interface. + 3. A scalable for TinyRAM using Proof-Carrying Data, as explained in \[BCTV14b] + 4. Zero-knowledge cluster MapReduce, as explained in \[CTV15]. + +The zkSNARK construction implemented by libsnark follows, extends, and +optimizes the approach described in \[BCTV14], itself an extension of +\[BCGTV13], following the approach of \[BCIOP13] and \[GGPR13]. An alternative +implementation of the basic approach is the _Pinocchio_ system of \[PGHR13]. +See these references for discussions of efficiency aspects that arise in +practical use of such constructions, as well as security and trust +considerations. + +This scheme is a _preprocessing zkSNARK_ (_ppzkSNARK_): before proofs can be +created and verified, one needs to first decide on a size/circuit/system +representing the NP statements to be proved, and run a _generator_ algorithm to +create corresponding public parameters (a long proving key and a short +verification key). + +Using the library involves the following high-level steps: + +1. Express the statements to be proved as an R1CS (or any of the other + languages above, such as arithmetic circuits, Boolean circuits, or TinyRAM). + This is done by writing C++ code that constructs an R1CS, and linking this code + together with libsnark +2. Use libsnark's generator algorithm to create the public parameters for this + statement (once and for all). +3. Use libsnark's prover algorithm to create proofs of true statements about + the satisfiability of the R1CS. +4. Use libsnark's verifier algorithm to check proofs for alleged statements. + + +-------------------------------------------------------------------------------- +The NP-complete language R1CS +-------------------------------------------------------------------------------- + +The ppzkSNARK supports proving/verifying membership in a specific NP-complete +language: R1CS (*rank-1 constraint systems*). An instance of the language is +specified by a set of equations over a prime field F, and each equation looks like: + < A, (1,X) > * < B , (1,X) > = < C, (1,X) > +where A,B,C are vectors over F, and X is a vector of variables. + +In particular, arithmetic (as well as boolean) circuits are easily reducible to +this language by converting each gate into a rank-1 constraint. See \[BCGTV13] +Appendix E (and "System of Rank 1 Quadratic Equations") for more details about this. + + +-------------------------------------------------------------------------------- +Elliptic curve choices +-------------------------------------------------------------------------------- + +The ppzkSNARK can be instantiated with different parameter choices, depending on +which elliptic curve is used. The libsnark library currently provides three +options: + +* "edwards": + an instantiation based on an Edwards curve, providing 80 bits of security. + +* "bn128": + an instantiation based on a Barreto-Naehrig curve, providing 128 + bits of security. The underlying curve implementation is + \[ate-pairing], which has incorporated our patch that changes the + BN curve to one suitable for SNARK applications. + + * This implementation uses dynamically-generated machine code for the curve + arithmetic. Some modern systems disallow execution of code on the heap, and + will thus block this implementation. + + For example, on Fedora 20 at its default settings, you will get the error + `zmInit ERR:can't protect` when running this code. To solve this, + run `sudo setsebool -P allow_execheap 1` to allow execution, + or use `make CURVE=ALT_BN128` instead. + +* "alt_bn128": + an alternative to "bn128", somewhat slower but avoids dynamic code generation. + +Note that bn128 requires an x86-64 CPU while the other curve choices +should be architecture-independent; see [portability](#portability). + + +-------------------------------------------------------------------------------- +Gadget libraries +-------------------------------------------------------------------------------- + +The libsnark library currently provides two libraries for conveniently constructing +R1CS instances out of reusable "gadgets". Both libraries provide a way to construct +gadgets on other gadgets as well as additional explicit equations. In this way, +complex R1CS instances can be built bottom up. + +### gadgetlib1 + +This is a low-level library which expose all features of the preprocessing +zkSNARK for R1CS. Its design is based on templates (as does the ppzkSNARK code) +to efficiently support working on multiple elliptic curves simultaneously. This +library is used for most of the constraint-building in libsnark, both internal +(reductions and Proof-Carrying Data) and examples applications. + +### gadgetlib2 + +This is an alternative library for constructing systems of polynomial equations +and, in particular, also R1CS instances. It is better documented and easier to +use than gadgetlib1, and its interface does not use templates. However, fewer +useful gadgets are provided. + + +-------------------------------------------------------------------------------- +Security +-------------------------------------------------------------------------------- + +The theoretical security of the underlying mathematical constructions, and the +requisite assumptions, are analyzed in detailed in the aforementioned research +papers. + +** +This code is a research-quality proof of concept, and has not +yet undergone extensive review or testing. It is thus not suitable, +as is, for use in critical or production systems. +** + +Known issues include the following: + +* The ppzkSNARK's generator and prover exhibit data-dependent running times + and memory usage. These form timing and cache-contention side channels, + which may be an issue in some applications. + +* Randomness is retrieved from /dev/urandom, but this should be + changed to a carefully considered (depending on system and threat + model) external, high-quality randomness source when creating + long-term proving/verification keys. + + +-------------------------------------------------------------------------------- +Build instructions +-------------------------------------------------------------------------------- + +The libsnark library relies on the following: + +- C++ build environment +- GMP for certain bit-integer arithmetic +- libprocps for reporting memory usage +- GTest for some of the unit tests + +So far we have tested these only on Linux, though we have been able to make the library work, +with some features disabled (such as memory profiling or GTest tests), on Windows via Cygwin +and on Mac OS X. (If you succeed in achieving more complete ports of the library, please +let us know!) See also the notes on [portability](#portability) below. + +For example, on a fresh install of Ubuntu 14.04, install the following packages: + + $ sudo apt-get install build-essential git libgmp3-dev libprocps3-dev libgtest-dev python-markdown libboost-all-dev libssl-dev + +Or, on Fedora 20: + + $ sudo yum install gcc-c++ make git gmp-devel procps-ng-devel gtest-devel python-markdown + +Run the following, to fetch dependencies from their GitHub repos and compile them. +(Not required if you set `CURVE` to other than the default `BN128` and also set `NO_SUPERCOP=1`.) + + $ ./prepare-depends.sh + +Then, to compile the library, tests, profiling harness and documentation, run: + + $ make + +To create just the HTML documentation, run + + $ make doc + +and then view the resulting `README.html` (which contains the very text you are reading now). + +To create Doxygen documentation summarizing all files, classes and functions, +with some (currently sparse) comments, install the `doxygen` and `graphviz` packages, then run + + $ make doxy + +(this may take a few minutes). Then view the resulting [`doxygen/index.html`](doxygen/index.html). + +### Using libsnark as a library + +To develop an application that uses libsnark, you could add it within the libsnark directory tree and adjust the Makefile, but it is far better to build libsnark as a (shared or static) library. You can then write your code in a separate directory tree, and link it against libsnark. + + +To build just the shared object library `libsnark.so`, run: + + $ make lib + +To build just the static library `libsnark.a`, run: + + $ make lib STATIC=1 + +Note that static compilation requires static versions of all libraries it depends on. +It may help to minimize these dependencies by appending +`CURVE=ALT_BN128 NO_PROCPS=1 NO_GTEST=1 NO_SUPERCOP=1`. On Fedora 21, the requisite +library RPM dependencies are then: +`boost-static glibc-static gmp-static libstdc++-static openssl-static zlib-static + boost-devel glibc-devel gmp-devel gmp-devel libstdc++-devel openssl-devel openssl-devel`. + +To build *and install* the libsnark library: + + $ make install PREFIX=/install/path + +This will install `libsnark.so` into `/install/path/lib`; so your application should be linked using `-L/install/path/lib -lsnark`. It also installs the requisite headers into `/install/path/include`; so your application should be compiled using `-I/install/path/include`. + +In addition, unless you use `NO_SUPERCOP=1`, `libsupercop.a` will be installed and should be linked in using `-lsupercop`. + + +### Building on Windows using Cygwin +Install Cygwin using the graphical installer, including the `g++`, `libgmp` +and `git` packages. Then disable the dependencies not easily supported under CygWin, +using: + + $ make NO_PROCPS=1 NO_GTEST=1 NO_DOCS=1 + + +### Building on Mac OS X + +On Mac OS X, install GMP from MacPorts (`port install gmp`). Then disable the +dependencies not easily supported under CygWin, using: + + $ make NO_PROCPS=1 NO_GTEST=1 NO_DOCS=1 + +MacPorts does not write its libraries into standard system folders, so you +might need to explicitly provide the paths to the header files and libraries by +appending `CXXFLAGS=-I/opt/local/include LDFLAGS=-L/opt/local/lib` to the line +above. Similarly, to pass the paths to ate-pairing you would run +`INC_DIR=-I/opt/local/include LIB_DIR=-L/opt/local/lib ./prepare-depends.sh` +instead of `./prepare-depends.sh` above. + +-------------------------------------------------------------------------------- +Tutorials +-------------------------------------------------------------------------------- + +libsnark includes a tutorial, and some usage examples, for the high-level API. + +* `src/gadgetlib1/examples1` contains a simple example for constructing a + constraint system using gadgetlib1. + +* `src/gadgetlib2/examples` contains a tutorial for using gadgetlib2 to express + NP statements as constraint systems. It introduces basic terminology, design + overview, and recommended programming style. It also shows how to invoke + ppzkSNARKs on such constraint systems. The main file, `tutorial.cpp`, builds + into a standalone executable. + +* `src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/profiling/profile_r1cs_ppzksnark.cpp` + constructs a simple constraint system and runs the ppzksnark. See below for how to + run it. + + +-------------------------------------------------------------------------------- +Executing profiling example +-------------------------------------------------------------------------------- + +The command + + $ src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/profiling/profile_r1cs_ppzksnark 1000 10 Fr + +exercises the ppzkSNARK (first generator, then prover, then verifier) on an +R1CS instance with 1000 equations and an input consisting of 10 field elements. + +(If you get the error `zmInit ERR:can't protect`, see the discussion +[above](#elliptic-curve-choices).) + +The command + + $ src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/profiling/profile_r1cs_ppzksnark 1000 10 bytes + +does the same but now the input consists of 10 bytes. + + +-------------------------------------------------------------------------------- +Build options +-------------------------------------------------------------------------------- + +The following flags change the behavior of the compiled code. + +* `make FEATUREFLAGS='-Dname1 -Dname2 ...'` + + Override the active conditional #define names (you can see the default at the top of the Makefile). + The next bullets list the most important conditionally-#defined features. + For example, `make FEATUREFLAGS='-DBINARY_OUTPUT'` enables binary output and disables the default + assembly optimizations and Montgomery-representation output. + +* define `BINARY_OUTPUT` + + In serialization, output raw binary data (instead of decimal, when not set). + +* `make CURVE=choice` / define `CURVE_choice` (where `choice` is one of: + ALT_BN128, BN128, EDWARDS, MNT4, MNT6) + + Set the default curve to one of the above (see [elliptic curve choices](#elliptic-curve-choices)). + +* `make DEBUG=1` / define `DEBUG` + + Print additional information for debugging purposes. + +* `make LOWMEM=1` / define `LOWMEM` + + Limit the size of multi-exponentiation tables, for low-memory platforms. + +* `make NO_DOCS=1` + + Do not generate HTML documentation, e.g. on platforms where Markdown is not easily available. + +* `make NO_PROCPS=1` + + Do not link against libprocps. This disables memory profiling. + +* `make NO_GTEST=1` + + Do not link against GTest. The tutorial and test suite of gadgetlib2 tutorial won't be compiled. + +* `make NO_SUPERCOP=1` + + Do not link against SUPERCOP for optimized crypto. The ADSNARK executables will not be built. + +* `make MULTICORE=1` + + Enable parallelized execution of the ppzkSNARK generator and prover, using OpenMP. + This will utilize all cores on the CPU for heavyweight parallelizable operations such as + FFT and multiexponentiation. The default is single-core. + + To override the maximum number of cores used, set the environment variable `OMP_NUM_THREADS` + at runtime (not compile time), e.g., `OMP_NUM_THREADS=8 test_r1cs_sp_ppzkpc`. It defaults + to the autodetected number of cores, but on some devices, dynamic core management confused + OpenMP's autodetection, so setting `OMP_NUM_THREADS` is necessary for full utilization. + +* define `NO_PT_COMPRESSION` + + Do not use point compression. + This gives much faster serialization times, at the expense of ~2x larger + sizes for serialized keys and proofs. + +* define `MONTGOMERY_OUTPUT` (on by default) + + Serialize Fp elements as their Montgomery representations. If this + option is disabled then Fp elements are serialized as their + equivalence classes, which is slower but produces human-readable + output. + +* `make PROFILE_OP_COUNTS=1` / define `PROFILE_OP_COUNTS` + + Collect counts for field and curve operations inside static variables + of the corresponding algebraic objects. This option works for all + curves except bn128. + +* define `USE_ASM` (on by default) + + Use unrolled assembly routines for F[p] arithmetic and faster heap in + multi-exponentiation. (When not set, use GMP's `mpn_*` routines instead.) + +* define `USE_MIXED_ADDITION` + + Convert each element of the proving key and verification key to + affine coordinates. This allows using mixed addition formulas in + multiexponentiation and results in slightly faster prover and + verifier runtime at expense of increased proving time. + +* `make PERFORMANCE=1` + + Enables compiler optimizations such as link-time optimization, and disables debugging aids. + (On some distributions this causes a `plugin needed to handle lto object` link error and `undefined reference`s, which can be remedied by `AR=gcc-ar make ...`.) + +Not all combinations are tested together or supported by every part of the codebase. + + +-------------------------------------------------------------------------------- +Portability +-------------------------------------------------------------------------------- + +libsnark is written in fairly standard C++11. + +However, having been developed on Linux on x86-64 CPUs, libsnark has some limitations +with respect to portability. Specifically: + +1. libsnark's algebraic data structures assume little-endian byte order. + +2. Profiling routines use `clock_gettime` and `readproc` calls, which are Linux-specific. + +3. Random-number generation is done by reading from `/dev/urandom`, which is + specific to Unix-like systems. + +4. libsnark binary serialization routines (see `BINARY_OUTPUT` above) assume + a fixed machine word size (i.e. sizeof(mp_limb_t) for GMP's limb data type). + Objects serialized in binary on a 64-bit system cannot be de-serialized on + a 32-bit system, and vice versa. + (The decimal serialization routines have no such limitation.) + +5. libsnark requires a C++ compiler with good C++11 support. It has been + tested with g++ 4.7, g++ 4.8, and clang 3.4. + +6. On x86-64, we by default use highly optimized assembly implementations for some + operations (see `USE_ASM` above). On other architectures we fall back to a + portable C++ implementation, which is slower. + +Tested configurations include: + +* Debian jessie with g++ 4.7 on x86-64 +* Debian jessie with clang 3.4 on x86-64 +* Fedora 20/21 with g++ 4.8.2/4.9.2 on x86-64 and i686 +* Ubuntu 14.04 LTS with g++ 4.8 on x86-64 +* Ubuntu 14.04 LTS with g++ 4.8 on x86-32, for EDWARDS and ALT_BN128 curve choices +* Debian wheezy with g++ 4.7 on ARM little endian (Debian armel port) inside QEMU, for EDWARDS and ALT_BN128 curve choices +* Windows 7 with g++ 4.8.3 under Cygwin 1.7.30 on x86-64 with NO_PROCPS=1, NO_GTEST=1 and NO_DOCS=1, for EDWARDS and ALT_BN128 curve choices +* Mac OS X 10.9.4 (Mavericks) with Apple LLVM version 5.1 (based on LLVM 3.4svn) on x86-64 with NO_PROCPS=1, NO_GTEST=1 and NO_DOCS=1 + + +-------------------------------------------------------------------------------- +Directory structure +-------------------------------------------------------------------------------- + +The directory structure of the libsnark library is as follows: + +* src/ --- main C++ source code, containing the following modules: + * algebra/ --- fields and elliptic curve groups + * common/ --- miscellaneous utilities + * gadgetlib1/ --- gadgetlib1, a library to construct R1CS instances + * gadgets/ --- basic gadgets for gadgetlib1 + * gadgetlib2/ --- gadgetlib2, a library to construct R1CS instances + * qap/ --- quadratic arithmetic program + * domains/ --- support for fast interpolation/evaluation, by providing + FFTs and Lagrange-coefficient computations for various domains + * relations/ --- interfaces for expressing statement (relations between instances and witnesses) as various NP-complete languages + * constraint_satisfaction_problems/ --- R1CS and USCS languages + * circuit_satisfaction_problems/ --- Boolean and arithmetic circuit satisfiability languages + * ram_computations/ --- RAM computation languages + * zk_proof_systems --- interfaces and implementations of the proof systems + * reductions --- reductions between languages (used internally, but contains many examples of building constraints) + + Some of these module directories have the following subdirectories: + + * ... + * examples/ --- example code and tutorials for this module + * tests/ --- unit tests for this module + + In particular, the top-level API examples are at `src/r1cs_ppzksnark/examples/` and `src/gadgetlib2/examples/`. + +* depsrc/ --- created by `prepare_depends.sh` for retrieved sourcecode and local builds of external code + (currently: \[ate-pairing], and its dependency xbyak). + +* depinst/ --- created by `prepare_depends.sh` and `Makefile` + for local installation of locally-compiled dependencies. + +* doxygen/ --- created by `make doxy` and contains a Doxygen summary of all files, classes etc. in libsnark. + + +-------------------------------------------------------------------------------- +Further considerations +-------------------------------------------------------------------------------- + +### Multiexponentiation window size + +The ppzkSNARK's generator has to solve a fixed-base multi-exponentiation +problem. We use a window-based method in which the optimal window size depends +on the size of the multiexponentiation instance *and* the platform. + +On our benchmarking platform (a 3.40 GHz Intel Core i7-4770 CPU), we have +computed for each curve optimal windows, provided as +"fixed_base_exp_window_table" initialization sequences, for each curve; see +`X_init.cpp` for X=edwards,bn128,alt_bn128. + +Performance on other platforms may not be optimal (but probably not be far off). +Future releases of the libsnark library will include a tool that generates +optimal window sizes. + + +-------------------------------------------------------------------------------- +References +-------------------------------------------------------------------------------- + +\[BBFR15] [ + _ADSNARK: nearly practical and privacy-preserving proofs on authenticated data_ +](https://eprint.iacr.org/2014/617), + Michael Backes, Manuel Barbosa, Dario Fiore, Raphael M. Reischuk, + IEEE Symposium on Security and Privacy (Oakland) 2015 + +\[BCCT12] [ + _From extractable collision resistance to succinct non-Interactive arguments of knowledge, and back again_ +](http://eprint.iacr.org/2011/443), + Nir Bitansky, Ran Canetti, Alessandro Chiesa, Eran Tromer, + Innovations in Computer Science (ITCS) 2012 + +\[BCCT13] [ + _Recursive composition and bootstrapping for SNARKs and proof-carrying data_ +](http://eprint.iacr.org/2012/095) + Nir Bitansky, Ran Canetti, Alessandro Chiesa, Eran Tromer, + Symposium on Theory of Computing (STOC) 13 + +\[BCGTV13] [ + _SNARKs for C: Verifying Program Executions Succinctly and in Zero Knowledge_ +](http://eprint.iacr.org/2013/507), + Eli Ben-Sasson, Alessandro Chiesa, Daniel Genkin, Eran Tromer, Madars Virza, + CRYPTO 2013 + +\[BCIOP13] [ + _Succinct Non-Interactive Arguments via Linear Interactive Proofs_ +](http://eprint.iacr.org/2012/718), + Nir Bitansky, Alessandro Chiesa, Yuval Ishai, Rafail Ostrovsky, Omer Paneth, + Theory of Cryptography Conference 2013 + +\[BCTV14a] [ + _Succinct Non-Interactive Zero Knowledge for a von Neumann Architecture_ +](http://eprint.iacr.org/2013/879), + Eli Ben-Sasson, Alessandro Chiesa, Eran Tromer, Madars Virza, + USENIX Security 2014 + +\[BCTV14b] [ + _Scalable succinct non-interactive arguments via cycles of elliptic curves_ +](https://eprint.iacr.org/2014/595), + Eli Ben-Sasson, Alessandro Chiesa, Eran Tromer, Madars Virza, + CRYPTO 2014 + +\[CTV15] [ + _Cluster computing in zero knowledge_ +](https://eprint.iacr.org/2015/377), + Alessandro Chiesa, Eran Tromer, Madars Virza, + Eurocrypt 2015 + +\[DFGK14] [ + Square span programs with applications to succinct NIZK arguments +](https://eprint.iacr.org/2014/718), + George Danezis, Cedric Fournet, Jens Groth, Markulf Kohlweiss, + ASIACCS 2014 + +\[GGPR13] [ + _Quadratic span programs and succinct NIZKs without PCPs_ +](http://eprint.iacr.org/2012/215), + Rosario Gennaro, Craig Gentry, Bryan Parno, Mariana Raykova, + EUROCRYPT 2013 + +\[ate-pairing] [ + _High-Speed Software Implementation of the Optimal Ate Pairing over Barreto-Naehrig Curves_ +](https://github.com/herumi/ate-pairing), + MITSUNARI Shigeo, TERUYA Tadanori + +\[PGHR13] [ + _Pinocchio: Nearly Practical Verifiable Computation_ +](http://eprint.iacr.org/2013/279), + Bryan Parno, Craig Gentry, Jon Howell, Mariana Raykova, + IEEE Symposium on Security and Privacy (Oakland) 2013 + +[SCIPR Lab]: http://www.scipr-lab.org/ (Succinct Computational Integrity and Privacy Research Lab) + +[LICENSE]: LICENSE (LICENSE file in top directory of libsnark distribution) + +[AUTHORS]: AUTHORS (AUTHORS file in top directory of libsnark distribution) diff --git a/src/snark/doxygen.conf b/src/snark/doxygen.conf new file mode 100644 index 000000000..5fbe61681 --- /dev/null +++ b/src/snark/doxygen.conf @@ -0,0 +1,1807 @@ +# Doxyfile 1.8.2 + +# 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 sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = libsnark + +# 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 = + +# 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 = + +# 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 = + +# 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 = + +# 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 = + +# 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. Note that you specify absolute paths here, but also +# relative paths, which will be relative from the directory where doxygen is +# started. + +STRIP_FROM_PATH = src + +# 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'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. + +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 = tcc=C++ + +# If MARKDOWN_SUPPORT is enabled (the default) 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. + +MARKDOWN_SUPPORT = YES + +# 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 by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. + +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); 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 the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data 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 (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = 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 + +# Similar to the SYMBOL_CACHE_SIZE 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 appear 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. + +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 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_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +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. + +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 = YES + +# 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 + +# 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. 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. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# 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. + +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 +# 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 README.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, 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 = *.md *.c *.h *.cpp *.hpp *.tcc *.inc *.cc + +# 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 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 = Debug \ + Release + +# 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. + +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 = + +# 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 = "perl -pe 's/^(libsnark: .*)$/$1 {#mainpage}/ if $.==1; s!//+ *(TODO|FIXME|XXX)!/// \\todo!'" + # The 1st replacement marks README.md as the main page. + # The 2nd replacement identifies additional TODO notations. + # These should be done with FILTER_PATTERNS instead, but it looks like shell escaping is different there. + +# 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 = YES + +# 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, C++ and Fortran comments will always remain visible. + +STRIP_CODE_COMMENTS = NO + +# 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 = doxygen + +# 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 left blank doxygen will +# generate a default style sheet. Note that it is recommended to use +# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this +# tag will in the future become obsolete. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional +# user-defined cascading style sheet that is 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 therefor more +# robust against future updates. Doxygen will copy the style sheet file to +# the output directory. + +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. + +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. +# 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 = NO + +# 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. + +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. + +HTML_INDEX_NUM_ENTRIES = 0 + +# 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 (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW 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. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = YES + +# 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 + +# 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_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 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 may also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = YES + +# 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. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# 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 = amsfonts + +# 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 = NO + +# 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 = NO + +# 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 + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# 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 style sheet 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. For each +# tag file the location of the external documentation should be added. 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. 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 = NO + +# 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 use the Helvetica font for 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 Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +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 +# 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 the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# managable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + +# 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 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. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# 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/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_g1.cpp b/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_g1.cpp new file mode 100644 index 000000000..bf7f43d6f --- /dev/null +++ b/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_g1.cpp @@ -0,0 +1,524 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include "algebra/curves/alt_bn128/alt_bn128_g1.hpp" + +namespace libsnark { + +#ifdef PROFILE_OP_COUNTS +long long alt_bn128_G1::add_cnt = 0; +long long alt_bn128_G1::dbl_cnt = 0; +#endif + +std::vector alt_bn128_G1::wnaf_window_table; +std::vector alt_bn128_G1::fixed_base_exp_window_table; +alt_bn128_G1 alt_bn128_G1::G1_zero; +alt_bn128_G1 alt_bn128_G1::G1_one; + +alt_bn128_G1::alt_bn128_G1() +{ + this->X = G1_zero.X; + this->Y = G1_zero.Y; + this->Z = G1_zero.Z; +} + +void alt_bn128_G1::print() const +{ + if (this->is_zero()) + { + printf("O\n"); + } + else + { + alt_bn128_G1 copy(*this); + copy.to_affine_coordinates(); + gmp_printf("(%Nd , %Nd)\n", + copy.X.as_bigint().data, alt_bn128_Fq::num_limbs, + copy.Y.as_bigint().data, alt_bn128_Fq::num_limbs); + } +} + +void alt_bn128_G1::print_coordinates() const +{ + if (this->is_zero()) + { + printf("O\n"); + } + else + { + gmp_printf("(%Nd : %Nd : %Nd)\n", + this->X.as_bigint().data, alt_bn128_Fq::num_limbs, + this->Y.as_bigint().data, alt_bn128_Fq::num_limbs, + this->Z.as_bigint().data, alt_bn128_Fq::num_limbs); + } +} + +void alt_bn128_G1::to_affine_coordinates() +{ + if (this->is_zero()) + { + this->X = alt_bn128_Fq::zero(); + this->Y = alt_bn128_Fq::one(); + this->Z = alt_bn128_Fq::zero(); + } + else + { + alt_bn128_Fq Z_inv = Z.inverse(); + alt_bn128_Fq Z2_inv = Z_inv.squared(); + alt_bn128_Fq Z3_inv = Z2_inv * Z_inv; + this->X = this->X * Z2_inv; + this->Y = this->Y * Z3_inv; + this->Z = alt_bn128_Fq::one(); + } +} + +void alt_bn128_G1::to_special() +{ + this->to_affine_coordinates(); +} + +bool alt_bn128_G1::is_special() const +{ + return (this->is_zero() || this->Z == alt_bn128_Fq::one()); +} + +bool alt_bn128_G1::is_zero() const +{ + return (this->Z.is_zero()); +} + +bool alt_bn128_G1::operator==(const alt_bn128_G1 &other) const +{ + if (this->is_zero()) + { + return other.is_zero(); + } + + if (other.is_zero()) + { + return false; + } + + /* now neither is O */ + + // using Jacobian coordinates so: + // (X1:Y1:Z1) = (X2:Y2:Z2) + // iff + // X1/Z1^2 == X2/Z2^2 and Y1/Z1^3 == Y2/Z2^3 + // iff + // X1 * Z2^2 == X2 * Z1^2 and Y1 * Z2^3 == Y2 * Z1^3 + + alt_bn128_Fq Z1_squared = (this->Z).squared(); + alt_bn128_Fq Z2_squared = (other.Z).squared(); + + if ((this->X * Z2_squared) != (other.X * Z1_squared)) + { + return false; + } + + alt_bn128_Fq Z1_cubed = (this->Z) * Z1_squared; + alt_bn128_Fq Z2_cubed = (other.Z) * Z2_squared; + + if ((this->Y * Z2_cubed) != (other.Y * Z1_cubed)) + { + return false; + } + + return true; +} + +bool alt_bn128_G1::operator!=(const alt_bn128_G1& other) const +{ + return !(operator==(other)); +} + +alt_bn128_G1 alt_bn128_G1::operator+(const alt_bn128_G1 &other) const +{ + // handle special cases having to do with O + if (this->is_zero()) + { + return other; + } + + if (other.is_zero()) + { + return *this; + } + + // no need to handle points of order 2,4 + // (they cannot exist in a prime-order subgroup) + + // check for doubling case + + // using Jacobian coordinates so: + // (X1:Y1:Z1) = (X2:Y2:Z2) + // iff + // X1/Z1^2 == X2/Z2^2 and Y1/Z1^3 == Y2/Z2^3 + // iff + // X1 * Z2^2 == X2 * Z1^2 and Y1 * Z2^3 == Y2 * Z1^3 + + alt_bn128_Fq Z1Z1 = (this->Z).squared(); + alt_bn128_Fq Z2Z2 = (other.Z).squared(); + + alt_bn128_Fq U1 = this->X * Z2Z2; + alt_bn128_Fq U2 = other.X * Z1Z1; + + alt_bn128_Fq Z1_cubed = (this->Z) * Z1Z1; + alt_bn128_Fq Z2_cubed = (other.Z) * Z2Z2; + + alt_bn128_Fq S1 = (this->Y) * Z2_cubed; // S1 = Y1 * Z2 * Z2Z2 + alt_bn128_Fq S2 = (other.Y) * Z1_cubed; // S2 = Y2 * Z1 * Z1Z1 + + if (U1 == U2 && S1 == S2) + { + // dbl case; nothing of above can be reused + return this->dbl(); + } + + // rest of add case + alt_bn128_Fq H = U2 - U1; // H = U2-U1 + alt_bn128_Fq S2_minus_S1 = S2-S1; + alt_bn128_Fq I = (H+H).squared(); // I = (2 * H)^2 + alt_bn128_Fq J = H * I; // J = H * I + alt_bn128_Fq r = S2_minus_S1 + S2_minus_S1; // r = 2 * (S2-S1) + alt_bn128_Fq V = U1 * I; // V = U1 * I + alt_bn128_Fq X3 = r.squared() - J - (V+V); // X3 = r^2 - J - 2 * V + alt_bn128_Fq S1_J = S1 * J; + alt_bn128_Fq Y3 = r * (V-X3) - (S1_J+S1_J); // Y3 = r * (V-X3)-2 S1 J + alt_bn128_Fq Z3 = ((this->Z+other.Z).squared()-Z1Z1-Z2Z2) * H; // Z3 = ((Z1+Z2)^2-Z1Z1-Z2Z2) * H + + return alt_bn128_G1(X3, Y3, Z3); +} + +alt_bn128_G1 alt_bn128_G1::operator-() const +{ + return alt_bn128_G1(this->X, -(this->Y), this->Z); +} + + +alt_bn128_G1 alt_bn128_G1::operator-(const alt_bn128_G1 &other) const +{ + return (*this) + (-other); +} + +alt_bn128_G1 alt_bn128_G1::add(const alt_bn128_G1 &other) const +{ + // handle special cases having to do with O + if (this->is_zero()) + { + return other; + } + + if (other.is_zero()) + { + return *this; + } + + // no need to handle points of order 2,4 + // (they cannot exist in a prime-order subgroup) + + // handle double case + if (this->operator==(other)) + { + return this->dbl(); + } + +#ifdef PROFILE_OP_COUNTS + this->add_cnt++; +#endif + // NOTE: does not handle O and pts of order 2,4 + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + + alt_bn128_Fq Z1Z1 = (this->Z).squared(); // Z1Z1 = Z1^2 + alt_bn128_Fq Z2Z2 = (other.Z).squared(); // Z2Z2 = Z2^2 + alt_bn128_Fq U1 = (this->X) * Z2Z2; // U1 = X1 * Z2Z2 + alt_bn128_Fq U2 = (other.X) * Z1Z1; // U2 = X2 * Z1Z1 + alt_bn128_Fq S1 = (this->Y) * (other.Z) * Z2Z2; // S1 = Y1 * Z2 * Z2Z2 + alt_bn128_Fq S2 = (other.Y) * (this->Z) * Z1Z1; // S2 = Y2 * Z1 * Z1Z1 + alt_bn128_Fq H = U2 - U1; // H = U2-U1 + alt_bn128_Fq S2_minus_S1 = S2-S1; + alt_bn128_Fq I = (H+H).squared(); // I = (2 * H)^2 + alt_bn128_Fq J = H * I; // J = H * I + alt_bn128_Fq r = S2_minus_S1 + S2_minus_S1; // r = 2 * (S2-S1) + alt_bn128_Fq V = U1 * I; // V = U1 * I + alt_bn128_Fq X3 = r.squared() - J - (V+V); // X3 = r^2 - J - 2 * V + alt_bn128_Fq S1_J = S1 * J; + alt_bn128_Fq Y3 = r * (V-X3) - (S1_J+S1_J); // Y3 = r * (V-X3)-2 S1 J + alt_bn128_Fq Z3 = ((this->Z+other.Z).squared()-Z1Z1-Z2Z2) * H; // Z3 = ((Z1+Z2)^2-Z1Z1-Z2Z2) * H + + return alt_bn128_G1(X3, Y3, Z3); +} + +alt_bn128_G1 alt_bn128_G1::mixed_add(const alt_bn128_G1 &other) const +{ +#ifdef DEBUG + assert(other.is_special()); +#endif + + // handle special cases having to do with O + if (this->is_zero()) + { + return other; + } + + if (other.is_zero()) + { + return *this; + } + + // no need to handle points of order 2,4 + // (they cannot exist in a prime-order subgroup) + + // check for doubling case + + // using Jacobian coordinates so: + // (X1:Y1:Z1) = (X2:Y2:Z2) + // iff + // X1/Z1^2 == X2/Z2^2 and Y1/Z1^3 == Y2/Z2^3 + // iff + // X1 * Z2^2 == X2 * Z1^2 and Y1 * Z2^3 == Y2 * Z1^3 + + // we know that Z2 = 1 + + const alt_bn128_Fq Z1Z1 = (this->Z).squared(); + + const alt_bn128_Fq &U1 = this->X; + const alt_bn128_Fq U2 = other.X * Z1Z1; + + const alt_bn128_Fq Z1_cubed = (this->Z) * Z1Z1; + + const alt_bn128_Fq &S1 = (this->Y); // S1 = Y1 * Z2 * Z2Z2 + const alt_bn128_Fq S2 = (other.Y) * Z1_cubed; // S2 = Y2 * Z1 * Z1Z1 + + if (U1 == U2 && S1 == S2) + { + // dbl case; nothing of above can be reused + return this->dbl(); + } + +#ifdef PROFILE_OP_COUNTS + this->add_cnt++; +#endif + + // NOTE: does not handle O and pts of order 2,4 + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl + alt_bn128_Fq H = U2-(this->X); // H = U2-X1 + alt_bn128_Fq HH = H.squared() ; // HH = H&2 + alt_bn128_Fq I = HH+HH; // I = 4*HH + I = I + I; + alt_bn128_Fq J = H*I; // J = H*I + alt_bn128_Fq r = S2-(this->Y); // r = 2*(S2-Y1) + r = r + r; + alt_bn128_Fq V = (this->X) * I ; // V = X1*I + alt_bn128_Fq X3 = r.squared()-J-V-V; // X3 = r^2-J-2*V + alt_bn128_Fq Y3 = (this->Y)*J; // Y3 = r*(V-X3)-2*Y1*J + Y3 = r*(V-X3) - Y3 - Y3; + alt_bn128_Fq Z3 = ((this->Z)+H).squared() - Z1Z1 - HH; // Z3 = (Z1+H)^2-Z1Z1-HH + + return alt_bn128_G1(X3, Y3, Z3); +} + +alt_bn128_G1 alt_bn128_G1::dbl() const +{ +#ifdef PROFILE_OP_COUNTS + this->dbl_cnt++; +#endif + // handle point at infinity + if (this->is_zero()) + { + return (*this); + } + + // no need to handle points of order 2,4 + // (they cannot exist in a prime-order subgroup) + + // NOTE: does not handle O and pts of order 2,4 + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + + alt_bn128_Fq A = (this->X).squared(); // A = X1^2 + alt_bn128_Fq B = (this->Y).squared(); // B = Y1^2 + alt_bn128_Fq C = B.squared(); // C = B^2 + alt_bn128_Fq D = (this->X + B).squared() - A - C; + D = D+D; // D = 2 * ((X1 + B)^2 - A - C) + alt_bn128_Fq E = A + A + A; // E = 3 * A + alt_bn128_Fq F = E.squared(); // F = E^2 + alt_bn128_Fq X3 = F - (D+D); // X3 = F - 2 D + alt_bn128_Fq eightC = C+C; + eightC = eightC + eightC; + eightC = eightC + eightC; + alt_bn128_Fq Y3 = E * (D - X3) - eightC; // Y3 = E * (D - X3) - 8 * C + alt_bn128_Fq Y1Z1 = (this->Y)*(this->Z); + alt_bn128_Fq Z3 = Y1Z1 + Y1Z1; // Z3 = 2 * Y1 * Z1 + + return alt_bn128_G1(X3, Y3, Z3); +} + +bool alt_bn128_G1::is_well_formed() const +{ + if (this->is_zero()) + { + return true; + } + else + { + /* + y^2 = x^3 + b + + We are using Jacobian coordinates, so equation we need to check is actually + + (y/z^3)^2 = (x/z^2)^3 + b + y^2 / z^6 = x^3 / z^6 + b + y^2 = x^3 + b z^6 + */ + alt_bn128_Fq X2 = this->X.squared(); + alt_bn128_Fq Y2 = this->Y.squared(); + alt_bn128_Fq Z2 = this->Z.squared(); + + alt_bn128_Fq X3 = this->X * X2; + alt_bn128_Fq Z3 = this->Z * Z2; + alt_bn128_Fq Z6 = Z3.squared(); + + return (Y2 == X3 + alt_bn128_coeff_b * Z6); + } +} + +alt_bn128_G1 alt_bn128_G1::zero() +{ + return G1_zero; +} + +alt_bn128_G1 alt_bn128_G1::one() +{ + return G1_one; +} + +alt_bn128_G1 alt_bn128_G1::random_element() +{ + return (scalar_field::random_element().as_bigint()) * G1_one; +} + +std::ostream& operator<<(std::ostream &out, const alt_bn128_G1 &g) +{ + alt_bn128_G1 copy(g); + copy.to_affine_coordinates(); + + out << (copy.is_zero() ? 1 : 0) << OUTPUT_SEPARATOR; +#ifdef NO_PT_COMPRESSION + out << copy.X << OUTPUT_SEPARATOR << copy.Y; +#else + /* storing LSB of Y */ + out << copy.X << OUTPUT_SEPARATOR << (copy.Y.as_bigint().data[0] & 1); +#endif + + return out; +} + +std::istream& operator>>(std::istream &in, alt_bn128_G1 &g) +{ + char is_zero; + alt_bn128_Fq tX, tY; + +#ifdef NO_PT_COMPRESSION + in >> is_zero >> tX >> tY; + is_zero -= '0'; +#else + in.read((char*)&is_zero, 1); // this reads is_zero; + is_zero -= '0'; + consume_OUTPUT_SEPARATOR(in); + + unsigned char Y_lsb; + in >> tX; + consume_OUTPUT_SEPARATOR(in); + in.read((char*)&Y_lsb, 1); + Y_lsb -= '0'; + + // y = +/- sqrt(x^3 + b) + if (!is_zero) + { + alt_bn128_Fq tX2 = tX.squared(); + alt_bn128_Fq tY2 = tX2*tX + alt_bn128_coeff_b; + tY = tY2.sqrt(); + + if ((tY.as_bigint().data[0] & 1) != Y_lsb) + { + tY = -tY; + } + } +#endif + // using Jacobian coordinates + if (!is_zero) + { + g.X = tX; + g.Y = tY; + g.Z = alt_bn128_Fq::one(); + } + else + { + g = alt_bn128_G1::zero(); + } + + return in; +} + +std::ostream& operator<<(std::ostream& out, const std::vector &v) +{ + out << v.size() << "\n"; + for (const alt_bn128_G1& t : v) + { + out << t << OUTPUT_NEWLINE; + } + + return out; +} + +std::istream& operator>>(std::istream& in, std::vector &v) +{ + v.clear(); + + size_t s; + in >> s; + consume_newline(in); + + v.reserve(s); + + for (size_t i = 0; i < s; ++i) + { + alt_bn128_G1 g; + in >> g; + consume_OUTPUT_NEWLINE(in); + v.emplace_back(g); + } + + return in; +} + +template<> +void batch_to_special_all_non_zeros(std::vector &vec) +{ + std::vector Z_vec; + Z_vec.reserve(vec.size()); + + for (auto &el: vec) + { + Z_vec.emplace_back(el.Z); + } + batch_invert(Z_vec); + + const alt_bn128_Fq one = alt_bn128_Fq::one(); + + for (size_t i = 0; i < vec.size(); ++i) + { + alt_bn128_Fq Z2 = Z_vec[i].squared(); + alt_bn128_Fq Z3 = Z_vec[i] * Z2; + + vec[i].X = vec[i].X * Z2; + vec[i].Y = vec[i].Y * Z3; + vec[i].Z = one; + } +} + +} // libsnark diff --git a/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_g1.hpp b/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_g1.hpp new file mode 100644 index 000000000..da11a2e8c --- /dev/null +++ b/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_g1.hpp @@ -0,0 +1,95 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef ALT_BN128_G1_HPP_ +#define ALT_BN128_G1_HPP_ +#include +#include "algebra/curves/alt_bn128/alt_bn128_init.hpp" +#include "algebra/curves/curve_utils.hpp" + +namespace libsnark { + +class alt_bn128_G1; +std::ostream& operator<<(std::ostream &, const alt_bn128_G1&); +std::istream& operator>>(std::istream &, alt_bn128_G1&); + +class alt_bn128_G1 { +public: +#ifdef PROFILE_OP_COUNTS + static long long add_cnt; + static long long dbl_cnt; +#endif + static std::vector wnaf_window_table; + static std::vector fixed_base_exp_window_table; + static alt_bn128_G1 G1_zero; + static alt_bn128_G1 G1_one; + + typedef alt_bn128_Fq base_field; + typedef alt_bn128_Fr scalar_field; + + alt_bn128_Fq X, Y, Z; + + // using Jacobian coordinates + alt_bn128_G1(); + alt_bn128_G1(const alt_bn128_Fq& X, const alt_bn128_Fq& Y, const alt_bn128_Fq& Z) : X(X), Y(Y), Z(Z) {}; + + void print() const; + void print_coordinates() const; + + void to_affine_coordinates(); + void to_special(); + bool is_special() const; + + bool is_zero() const; + + bool operator==(const alt_bn128_G1 &other) const; + bool operator!=(const alt_bn128_G1 &other) const; + + alt_bn128_G1 operator+(const alt_bn128_G1 &other) const; + alt_bn128_G1 operator-() const; + alt_bn128_G1 operator-(const alt_bn128_G1 &other) const; + + alt_bn128_G1 add(const alt_bn128_G1 &other) const; + alt_bn128_G1 mixed_add(const alt_bn128_G1 &other) const; + alt_bn128_G1 dbl() const; + + bool is_well_formed() const; + + static alt_bn128_G1 zero(); + static alt_bn128_G1 one(); + static alt_bn128_G1 random_element(); + + static size_t size_in_bits() { return base_field::size_in_bits() + 1; } + static bigint base_field_char() { return base_field::field_char(); } + static bigint order() { return scalar_field::field_char(); } + + friend std::ostream& operator<<(std::ostream &out, const alt_bn128_G1 &g); + friend std::istream& operator>>(std::istream &in, alt_bn128_G1 &g); +}; + +template +alt_bn128_G1 operator*(const bigint &lhs, const alt_bn128_G1 &rhs) +{ + return scalar_mul(rhs, lhs); +} + +template& modulus_p> +alt_bn128_G1 operator*(const Fp_model &lhs, const alt_bn128_G1 &rhs) +{ + return scalar_mul(rhs, lhs.as_bigint()); +} + +std::ostream& operator<<(std::ostream& out, const std::vector &v); +std::istream& operator>>(std::istream& in, std::vector &v); + +template +void batch_to_special_all_non_zeros(std::vector &vec); +template<> +void batch_to_special_all_non_zeros(std::vector &vec); + +} // libsnark +#endif // ALT_BN128_G1_HPP_ diff --git a/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_g2.cpp b/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_g2.cpp new file mode 100644 index 000000000..c4152e437 --- /dev/null +++ b/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_g2.cpp @@ -0,0 +1,505 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include "algebra/curves/alt_bn128/alt_bn128_g2.hpp" + +namespace libsnark { + +#ifdef PROFILE_OP_COUNTS +long long alt_bn128_G2::add_cnt = 0; +long long alt_bn128_G2::dbl_cnt = 0; +#endif + +std::vector alt_bn128_G2::wnaf_window_table; +std::vector alt_bn128_G2::fixed_base_exp_window_table; +alt_bn128_G2 alt_bn128_G2::G2_zero; +alt_bn128_G2 alt_bn128_G2::G2_one; + +alt_bn128_G2::alt_bn128_G2() +{ + this->X = G2_zero.X; + this->Y = G2_zero.Y; + this->Z = G2_zero.Z; +} + +alt_bn128_Fq2 alt_bn128_G2::mul_by_b(const alt_bn128_Fq2 &elt) +{ + return alt_bn128_Fq2(alt_bn128_twist_mul_by_b_c0 * elt.c0, alt_bn128_twist_mul_by_b_c1 * elt.c1); +} + +void alt_bn128_G2::print() const +{ + if (this->is_zero()) + { + printf("O\n"); + } + else + { + alt_bn128_G2 copy(*this); + copy.to_affine_coordinates(); + gmp_printf("(%Nd*z + %Nd , %Nd*z + %Nd)\n", + copy.X.c1.as_bigint().data, alt_bn128_Fq::num_limbs, + copy.X.c0.as_bigint().data, alt_bn128_Fq::num_limbs, + copy.Y.c1.as_bigint().data, alt_bn128_Fq::num_limbs, + copy.Y.c0.as_bigint().data, alt_bn128_Fq::num_limbs); + } +} + +void alt_bn128_G2::print_coordinates() const +{ + if (this->is_zero()) + { + printf("O\n"); + } + else + { + gmp_printf("(%Nd*z + %Nd : %Nd*z + %Nd : %Nd*z + %Nd)\n", + this->X.c1.as_bigint().data, alt_bn128_Fq::num_limbs, + this->X.c0.as_bigint().data, alt_bn128_Fq::num_limbs, + this->Y.c1.as_bigint().data, alt_bn128_Fq::num_limbs, + this->Y.c0.as_bigint().data, alt_bn128_Fq::num_limbs, + this->Z.c1.as_bigint().data, alt_bn128_Fq::num_limbs, + this->Z.c0.as_bigint().data, alt_bn128_Fq::num_limbs); + } +} + +void alt_bn128_G2::to_affine_coordinates() +{ + if (this->is_zero()) + { + this->X = alt_bn128_Fq2::zero(); + this->Y = alt_bn128_Fq2::one(); + this->Z = alt_bn128_Fq2::zero(); + } + else + { + alt_bn128_Fq2 Z_inv = Z.inverse(); + alt_bn128_Fq2 Z2_inv = Z_inv.squared(); + alt_bn128_Fq2 Z3_inv = Z2_inv * Z_inv; + this->X = this->X * Z2_inv; + this->Y = this->Y * Z3_inv; + this->Z = alt_bn128_Fq2::one(); + } +} + +void alt_bn128_G2::to_special() +{ + this->to_affine_coordinates(); +} + +bool alt_bn128_G2::is_special() const +{ + return (this->is_zero() || this->Z == alt_bn128_Fq2::one()); +} + +bool alt_bn128_G2::is_zero() const +{ + return (this->Z.is_zero()); +} + +bool alt_bn128_G2::operator==(const alt_bn128_G2 &other) const +{ + if (this->is_zero()) + { + return other.is_zero(); + } + + if (other.is_zero()) + { + return false; + } + + /* now neither is O */ + + // using Jacobian coordinates so: + // (X1:Y1:Z1) = (X2:Y2:Z2) + // iff + // X1/Z1^2 == X2/Z2^2 and Y1/Z1^3 == Y2/Z2^3 + // iff + // X1 * Z2^2 == X2 * Z1^2 and Y1 * Z2^3 == Y2 * Z1^3 + + alt_bn128_Fq2 Z1_squared = (this->Z).squared(); + alt_bn128_Fq2 Z2_squared = (other.Z).squared(); + + if ((this->X * Z2_squared) != (other.X * Z1_squared)) + { + return false; + } + + alt_bn128_Fq2 Z1_cubed = (this->Z) * Z1_squared; + alt_bn128_Fq2 Z2_cubed = (other.Z) * Z2_squared; + + if ((this->Y * Z2_cubed) != (other.Y * Z1_cubed)) + { + return false; + } + + return true; +} + +bool alt_bn128_G2::operator!=(const alt_bn128_G2& other) const +{ + return !(operator==(other)); +} + +alt_bn128_G2 alt_bn128_G2::operator+(const alt_bn128_G2 &other) const +{ + // handle special cases having to do with O + if (this->is_zero()) + { + return other; + } + + if (other.is_zero()) + { + return *this; + } + + // no need to handle points of order 2,4 + // (they cannot exist in a prime-order subgroup) + + // check for doubling case + + // using Jacobian coordinates so: + // (X1:Y1:Z1) = (X2:Y2:Z2) + // iff + // X1/Z1^2 == X2/Z2^2 and Y1/Z1^3 == Y2/Z2^3 + // iff + // X1 * Z2^2 == X2 * Z1^2 and Y1 * Z2^3 == Y2 * Z1^3 + + alt_bn128_Fq2 Z1Z1 = (this->Z).squared(); + alt_bn128_Fq2 Z2Z2 = (other.Z).squared(); + + alt_bn128_Fq2 U1 = this->X * Z2Z2; + alt_bn128_Fq2 U2 = other.X * Z1Z1; + + alt_bn128_Fq2 Z1_cubed = (this->Z) * Z1Z1; + alt_bn128_Fq2 Z2_cubed = (other.Z) * Z2Z2; + + alt_bn128_Fq2 S1 = (this->Y) * Z2_cubed; // S1 = Y1 * Z2 * Z2Z2 + alt_bn128_Fq2 S2 = (other.Y) * Z1_cubed; // S2 = Y2 * Z1 * Z1Z1 + + if (U1 == U2 && S1 == S2) + { + // dbl case; nothing of above can be reused + return this->dbl(); + } + + // rest of add case + alt_bn128_Fq2 H = U2 - U1; // H = U2-U1 + alt_bn128_Fq2 S2_minus_S1 = S2-S1; + alt_bn128_Fq2 I = (H+H).squared(); // I = (2 * H)^2 + alt_bn128_Fq2 J = H * I; // J = H * I + alt_bn128_Fq2 r = S2_minus_S1 + S2_minus_S1; // r = 2 * (S2-S1) + alt_bn128_Fq2 V = U1 * I; // V = U1 * I + alt_bn128_Fq2 X3 = r.squared() - J - (V+V); // X3 = r^2 - J - 2 * V + alt_bn128_Fq2 S1_J = S1 * J; + alt_bn128_Fq2 Y3 = r * (V-X3) - (S1_J+S1_J); // Y3 = r * (V-X3)-2 S1 J + alt_bn128_Fq2 Z3 = ((this->Z+other.Z).squared()-Z1Z1-Z2Z2) * H; // Z3 = ((Z1+Z2)^2-Z1Z1-Z2Z2) * H + + return alt_bn128_G2(X3, Y3, Z3); +} + +alt_bn128_G2 alt_bn128_G2::operator-() const +{ + return alt_bn128_G2(this->X, -(this->Y), this->Z); +} + + +alt_bn128_G2 alt_bn128_G2::operator-(const alt_bn128_G2 &other) const +{ + return (*this) + (-other); +} + +alt_bn128_G2 alt_bn128_G2::add(const alt_bn128_G2 &other) const +{ + // handle special cases having to do with O + if (this->is_zero()) + { + return other; + } + + if (other.is_zero()) + { + return *this; + } + + // no need to handle points of order 2,4 + // (they cannot exist in a prime-order subgroup) + + // handle double case + if (this->operator==(other)) + { + return this->dbl(); + } + +#ifdef PROFILE_OP_COUNTS + this->add_cnt++; +#endif + // NOTE: does not handle O and pts of order 2,4 + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-projective.html#addition-add-1998-cmo-2 + + alt_bn128_Fq2 Z1Z1 = (this->Z).squared(); // Z1Z1 = Z1^2 + alt_bn128_Fq2 Z2Z2 = (other.Z).squared(); // Z2Z2 = Z2^2 + alt_bn128_Fq2 U1 = (this->X) * Z2Z2; // U1 = X1 * Z2Z2 + alt_bn128_Fq2 U2 = (other.X) * Z1Z1; // U2 = X2 * Z1Z1 + alt_bn128_Fq2 S1 = (this->Y) * (other.Z) * Z2Z2; // S1 = Y1 * Z2 * Z2Z2 + alt_bn128_Fq2 S2 = (other.Y) * (this->Z) * Z1Z1; // S2 = Y2 * Z1 * Z1Z1 + alt_bn128_Fq2 H = U2 - U1; // H = U2-U1 + alt_bn128_Fq2 S2_minus_S1 = S2-S1; + alt_bn128_Fq2 I = (H+H).squared(); // I = (2 * H)^2 + alt_bn128_Fq2 J = H * I; // J = H * I + alt_bn128_Fq2 r = S2_minus_S1 + S2_minus_S1; // r = 2 * (S2-S1) + alt_bn128_Fq2 V = U1 * I; // V = U1 * I + alt_bn128_Fq2 X3 = r.squared() - J - (V+V); // X3 = r^2 - J - 2 * V + alt_bn128_Fq2 S1_J = S1 * J; + alt_bn128_Fq2 Y3 = r * (V-X3) - (S1_J+S1_J); // Y3 = r * (V-X3)-2 S1 J + alt_bn128_Fq2 Z3 = ((this->Z+other.Z).squared()-Z1Z1-Z2Z2) * H; // Z3 = ((Z1+Z2)^2-Z1Z1-Z2Z2) * H + + return alt_bn128_G2(X3, Y3, Z3); +} + +alt_bn128_G2 alt_bn128_G2::mixed_add(const alt_bn128_G2 &other) const +{ +#ifdef DEBUG + assert(other.is_special()); +#endif + + // handle special cases having to do with O + if (this->is_zero()) + { + return other; + } + + if (other.is_zero()) + { + return *this; + } + + // no need to handle points of order 2,4 + // (they cannot exist in a prime-order subgroup) + + // check for doubling case + + // using Jacobian coordinates so: + // (X1:Y1:Z1) = (X2:Y2:Z2) + // iff + // X1/Z1^2 == X2/Z2^2 and Y1/Z1^3 == Y2/Z2^3 + // iff + // X1 * Z2^2 == X2 * Z1^2 and Y1 * Z2^3 == Y2 * Z1^3 + + // we know that Z2 = 1 + + const alt_bn128_Fq2 Z1Z1 = (this->Z).squared(); + + const alt_bn128_Fq2 &U1 = this->X; + const alt_bn128_Fq2 U2 = other.X * Z1Z1; + + const alt_bn128_Fq2 Z1_cubed = (this->Z) * Z1Z1; + + const alt_bn128_Fq2 &S1 = (this->Y); // S1 = Y1 * Z2 * Z2Z2 + const alt_bn128_Fq2 S2 = (other.Y) * Z1_cubed; // S2 = Y2 * Z1 * Z1Z1 + + if (U1 == U2 && S1 == S2) + { + // dbl case; nothing of above can be reused + return this->dbl(); + } + +#ifdef PROFILE_OP_COUNTS + this->add_cnt++; +#endif + + // NOTE: does not handle O and pts of order 2,4 + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl + alt_bn128_Fq2 H = U2-(this->X); // H = U2-X1 + alt_bn128_Fq2 HH = H.squared() ; // HH = H&2 + alt_bn128_Fq2 I = HH+HH; // I = 4*HH + I = I + I; + alt_bn128_Fq2 J = H*I; // J = H*I + alt_bn128_Fq2 r = S2-(this->Y); // r = 2*(S2-Y1) + r = r + r; + alt_bn128_Fq2 V = (this->X) * I ; // V = X1*I + alt_bn128_Fq2 X3 = r.squared()-J-V-V; // X3 = r^2-J-2*V + alt_bn128_Fq2 Y3 = (this->Y)*J; // Y3 = r*(V-X3)-2*Y1*J + Y3 = r*(V-X3) - Y3 - Y3; + alt_bn128_Fq2 Z3 = ((this->Z)+H).squared() - Z1Z1 - HH; // Z3 = (Z1+H)^2-Z1Z1-HH + + return alt_bn128_G2(X3, Y3, Z3); +} + +alt_bn128_G2 alt_bn128_G2::dbl() const +{ +#ifdef PROFILE_OP_COUNTS + this->dbl_cnt++; +#endif + // handle point at infinity + if (this->is_zero()) + { + return (*this); + } + + // NOTE: does not handle O and pts of order 2,4 + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-projective.html#doubling-dbl-2007-bl + + alt_bn128_Fq2 A = (this->X).squared(); // A = X1^2 + alt_bn128_Fq2 B = (this->Y).squared(); // B = Y1^2 + alt_bn128_Fq2 C = B.squared(); // C = B^2 + alt_bn128_Fq2 D = (this->X + B).squared() - A - C; + D = D+D; // D = 2 * ((X1 + B)^2 - A - C) + alt_bn128_Fq2 E = A + A + A; // E = 3 * A + alt_bn128_Fq2 F = E.squared(); // F = E^2 + alt_bn128_Fq2 X3 = F - (D+D); // X3 = F - 2 D + alt_bn128_Fq2 eightC = C+C; + eightC = eightC + eightC; + eightC = eightC + eightC; + alt_bn128_Fq2 Y3 = E * (D - X3) - eightC; // Y3 = E * (D - X3) - 8 * C + alt_bn128_Fq2 Y1Z1 = (this->Y)*(this->Z); + alt_bn128_Fq2 Z3 = Y1Z1 + Y1Z1; // Z3 = 2 * Y1 * Z1 + + return alt_bn128_G2(X3, Y3, Z3); +} + +alt_bn128_G2 alt_bn128_G2::mul_by_q() const +{ + return alt_bn128_G2(alt_bn128_twist_mul_by_q_X * (this->X).Frobenius_map(1), + alt_bn128_twist_mul_by_q_Y * (this->Y).Frobenius_map(1), + (this->Z).Frobenius_map(1)); +} + +bool alt_bn128_G2::is_well_formed() const +{ + if (this->is_zero()) + { + return true; + } + else + { + /* + y^2 = x^3 + b + + We are using Jacobian coordinates, so equation we need to check is actually + + (y/z^3)^2 = (x/z^2)^3 + b + y^2 / z^6 = x^3 / z^6 + b + y^2 = x^3 + b z^6 + */ + alt_bn128_Fq2 X2 = this->X.squared(); + alt_bn128_Fq2 Y2 = this->Y.squared(); + alt_bn128_Fq2 Z2 = this->Z.squared(); + + alt_bn128_Fq2 X3 = this->X * X2; + alt_bn128_Fq2 Z3 = this->Z * Z2; + alt_bn128_Fq2 Z6 = Z3.squared(); + + return (Y2 == X3 + alt_bn128_twist_coeff_b * Z6); + } +} + +alt_bn128_G2 alt_bn128_G2::zero() +{ + return G2_zero; +} + +alt_bn128_G2 alt_bn128_G2::one() +{ + return G2_one; +} + +alt_bn128_G2 alt_bn128_G2::random_element() +{ + return (alt_bn128_Fr::random_element().as_bigint()) * G2_one; +} + +std::ostream& operator<<(std::ostream &out, const alt_bn128_G2 &g) +{ + alt_bn128_G2 copy(g); + copy.to_affine_coordinates(); + out << (copy.is_zero() ? 1 : 0) << OUTPUT_SEPARATOR; +#ifdef NO_PT_COMPRESSION + out << copy.X << OUTPUT_SEPARATOR << copy.Y; +#else + /* storing LSB of Y */ + out << copy.X << OUTPUT_SEPARATOR << (copy.Y.c0.as_bigint().data[0] & 1); +#endif + + return out; +} + +std::istream& operator>>(std::istream &in, alt_bn128_G2 &g) +{ + char is_zero; + alt_bn128_Fq2 tX, tY; + +#ifdef NO_PT_COMPRESSION + in >> is_zero >> tX >> tY; + is_zero -= '0'; +#else + in.read((char*)&is_zero, 1); // this reads is_zero; + is_zero -= '0'; + consume_OUTPUT_SEPARATOR(in); + + unsigned char Y_lsb; + in >> tX; + consume_OUTPUT_SEPARATOR(in); + in.read((char*)&Y_lsb, 1); + Y_lsb -= '0'; + + // y = +/- sqrt(x^3 + b) + if (!is_zero) + { + alt_bn128_Fq2 tX2 = tX.squared(); + alt_bn128_Fq2 tY2 = tX2 * tX + alt_bn128_twist_coeff_b; + tY = tY2.sqrt(); + + if ((tY.c0.as_bigint().data[0] & 1) != Y_lsb) + { + tY = -tY; + } + } +#endif + // using projective coordinates + if (!is_zero) + { + g.X = tX; + g.Y = tY; + g.Z = alt_bn128_Fq2::one(); + } + else + { + g = alt_bn128_G2::zero(); + } + + return in; +} + +template<> +void batch_to_special_all_non_zeros(std::vector &vec) +{ + std::vector Z_vec; + Z_vec.reserve(vec.size()); + + for (auto &el: vec) + { + Z_vec.emplace_back(el.Z); + } + batch_invert(Z_vec); + + const alt_bn128_Fq2 one = alt_bn128_Fq2::one(); + + for (size_t i = 0; i < vec.size(); ++i) + { + alt_bn128_Fq2 Z2 = Z_vec[i].squared(); + alt_bn128_Fq2 Z3 = Z_vec[i] * Z2; + + vec[i].X = vec[i].X * Z2; + vec[i].Y = vec[i].Y * Z3; + vec[i].Z = one; + } +} + +} // libsnark diff --git a/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_g2.hpp b/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_g2.hpp new file mode 100644 index 000000000..a996a2d1a --- /dev/null +++ b/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_g2.hpp @@ -0,0 +1,96 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef ALT_BN128_G2_HPP_ +#define ALT_BN128_G2_HPP_ +#include +#include "algebra/curves/alt_bn128/alt_bn128_init.hpp" +#include "algebra/curves/curve_utils.hpp" + +namespace libsnark { + +class alt_bn128_G2; +std::ostream& operator<<(std::ostream &, const alt_bn128_G2&); +std::istream& operator>>(std::istream &, alt_bn128_G2&); + +class alt_bn128_G2 { +public: +#ifdef PROFILE_OP_COUNTS + static long long add_cnt; + static long long dbl_cnt; +#endif + static std::vector wnaf_window_table; + static std::vector fixed_base_exp_window_table; + static alt_bn128_G2 G2_zero; + static alt_bn128_G2 G2_one; + + typedef alt_bn128_Fq base_field; + typedef alt_bn128_Fq2 twist_field; + typedef alt_bn128_Fr scalar_field; + + alt_bn128_Fq2 X, Y, Z; + + // using Jacobian coordinates + alt_bn128_G2(); + alt_bn128_G2(const alt_bn128_Fq2& X, const alt_bn128_Fq2& Y, const alt_bn128_Fq2& Z) : X(X), Y(Y), Z(Z) {}; + + static alt_bn128_Fq2 mul_by_b(const alt_bn128_Fq2 &elt); + + void print() const; + void print_coordinates() const; + + void to_affine_coordinates(); + void to_special(); + bool is_special() const; + + bool is_zero() const; + + bool operator==(const alt_bn128_G2 &other) const; + bool operator!=(const alt_bn128_G2 &other) const; + + alt_bn128_G2 operator+(const alt_bn128_G2 &other) const; + alt_bn128_G2 operator-() const; + alt_bn128_G2 operator-(const alt_bn128_G2 &other) const; + + alt_bn128_G2 add(const alt_bn128_G2 &other) const; + alt_bn128_G2 mixed_add(const alt_bn128_G2 &other) const; + alt_bn128_G2 dbl() const; + alt_bn128_G2 mul_by_q() const; + + bool is_well_formed() const; + + static alt_bn128_G2 zero(); + static alt_bn128_G2 one(); + static alt_bn128_G2 random_element(); + + static size_t size_in_bits() { return twist_field::size_in_bits() + 1; } + static bigint base_field_char() { return base_field::field_char(); } + static bigint order() { return scalar_field::field_char(); } + + friend std::ostream& operator<<(std::ostream &out, const alt_bn128_G2 &g); + friend std::istream& operator>>(std::istream &in, alt_bn128_G2 &g); +}; + +template +alt_bn128_G2 operator*(const bigint &lhs, const alt_bn128_G2 &rhs) +{ + return scalar_mul(rhs, lhs); +} + +template& modulus_p> +alt_bn128_G2 operator*(const Fp_model &lhs, const alt_bn128_G2 &rhs) +{ + return scalar_mul(rhs, lhs.as_bigint()); +} + +template +void batch_to_special_all_non_zeros(std::vector &vec); +template<> +void batch_to_special_all_non_zeros(std::vector &vec); + +} // libsnark +#endif // ALT_BN128_G2_HPP_ diff --git a/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_init.cpp b/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_init.cpp new file mode 100644 index 000000000..7c23773d6 --- /dev/null +++ b/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_init.cpp @@ -0,0 +1,273 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include "algebra/curves/alt_bn128/alt_bn128_init.hpp" +#include "algebra/curves/alt_bn128/alt_bn128_g1.hpp" +#include "algebra/curves/alt_bn128/alt_bn128_g2.hpp" + +namespace libsnark { + +bigint alt_bn128_modulus_r; +bigint alt_bn128_modulus_q; + +alt_bn128_Fq alt_bn128_coeff_b; +alt_bn128_Fq2 alt_bn128_twist; +alt_bn128_Fq2 alt_bn128_twist_coeff_b; +alt_bn128_Fq alt_bn128_twist_mul_by_b_c0; +alt_bn128_Fq alt_bn128_twist_mul_by_b_c1; +alt_bn128_Fq2 alt_bn128_twist_mul_by_q_X; +alt_bn128_Fq2 alt_bn128_twist_mul_by_q_Y; + +bigint alt_bn128_ate_loop_count; +bool alt_bn128_ate_is_loop_count_neg; +bigint<12*alt_bn128_q_limbs> alt_bn128_final_exponent; +bigint alt_bn128_final_exponent_z; +bool alt_bn128_final_exponent_is_z_neg; + +void init_alt_bn128_params() +{ + typedef bigint bigint_r; + typedef bigint bigint_q; + + assert(sizeof(mp_limb_t) == 8 || sizeof(mp_limb_t) == 4); // Montgomery assumes this + + /* parameters for scalar field Fr */ + + alt_bn128_modulus_r = bigint_r("21888242871839275222246405745257275088548364400416034343698204186575808495617"); + assert(alt_bn128_Fr::modulus_is_valid()); + if (sizeof(mp_limb_t) == 8) + { + alt_bn128_Fr::Rsquared = bigint_r("944936681149208446651664254269745548490766851729442924617792859073125903783"); + alt_bn128_Fr::Rcubed = bigint_r("5866548545943845227489894872040244720403868105578784105281690076696998248512"); + alt_bn128_Fr::inv = 0xc2e1f593efffffff; + } + if (sizeof(mp_limb_t) == 4) + { + alt_bn128_Fr::Rsquared = bigint_r("944936681149208446651664254269745548490766851729442924617792859073125903783"); + alt_bn128_Fr::Rcubed = bigint_r("5866548545943845227489894872040244720403868105578784105281690076696998248512"); + alt_bn128_Fr::inv = 0xefffffff; + } + alt_bn128_Fr::num_bits = 254; + alt_bn128_Fr::euler = bigint_r("10944121435919637611123202872628637544274182200208017171849102093287904247808"); + alt_bn128_Fr::s = 28; + alt_bn128_Fr::t = bigint_r("81540058820840996586704275553141814055101440848469862132140264610111"); + alt_bn128_Fr::t_minus_1_over_2 = bigint_r("40770029410420498293352137776570907027550720424234931066070132305055"); + alt_bn128_Fr::multiplicative_generator = alt_bn128_Fr("5"); + alt_bn128_Fr::root_of_unity = alt_bn128_Fr("19103219067921713944291392827692070036145651957329286315305642004821462161904"); + alt_bn128_Fr::nqr = alt_bn128_Fr("5"); + alt_bn128_Fr::nqr_to_t = alt_bn128_Fr("19103219067921713944291392827692070036145651957329286315305642004821462161904"); + + /* parameters for base field Fq */ + + alt_bn128_modulus_q = bigint_q("21888242871839275222246405745257275088696311157297823662689037894645226208583"); + assert(alt_bn128_Fq::modulus_is_valid()); + if (sizeof(mp_limb_t) == 8) + { + alt_bn128_Fq::Rsquared = bigint_q("3096616502983703923843567936837374451735540968419076528771170197431451843209"); + alt_bn128_Fq::Rcubed = bigint_q("14921786541159648185948152738563080959093619838510245177710943249661917737183"); + alt_bn128_Fq::inv = 0x87d20782e4866389; + } + if (sizeof(mp_limb_t) == 4) + { + alt_bn128_Fq::Rsquared = bigint_q("3096616502983703923843567936837374451735540968419076528771170197431451843209"); + alt_bn128_Fq::Rcubed = bigint_q("14921786541159648185948152738563080959093619838510245177710943249661917737183"); + alt_bn128_Fq::inv = 0xe4866389; + } + alt_bn128_Fq::num_bits = 254; + alt_bn128_Fq::euler = bigint_q("10944121435919637611123202872628637544348155578648911831344518947322613104291"); + alt_bn128_Fq::s = 1; + alt_bn128_Fq::t = bigint_q("10944121435919637611123202872628637544348155578648911831344518947322613104291"); + alt_bn128_Fq::t_minus_1_over_2 = bigint_q("5472060717959818805561601436314318772174077789324455915672259473661306552145"); + alt_bn128_Fq::multiplicative_generator = alt_bn128_Fq("3"); + alt_bn128_Fq::root_of_unity = alt_bn128_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582"); + alt_bn128_Fq::nqr = alt_bn128_Fq("3"); + alt_bn128_Fq::nqr_to_t = alt_bn128_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582"); + + /* parameters for twist field Fq2 */ + alt_bn128_Fq2::euler = bigint<2*alt_bn128_q_limbs>("239547588008311421220994022608339370399626158265550411218223901127035046843189118723920525909718935985594116157406550130918127817069793474323196511433944"); + alt_bn128_Fq2::s = 4; + alt_bn128_Fq2::t = bigint<2*alt_bn128_q_limbs>("29943448501038927652624252826042421299953269783193801402277987640879380855398639840490065738714866998199264519675818766364765977133724184290399563929243"); + alt_bn128_Fq2::t_minus_1_over_2 = bigint<2*alt_bn128_q_limbs>("14971724250519463826312126413021210649976634891596900701138993820439690427699319920245032869357433499099632259837909383182382988566862092145199781964621"); + alt_bn128_Fq2::non_residue = alt_bn128_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582"); + alt_bn128_Fq2::nqr = alt_bn128_Fq2(alt_bn128_Fq("2"),alt_bn128_Fq("1")); + alt_bn128_Fq2::nqr_to_t = alt_bn128_Fq2(alt_bn128_Fq("5033503716262624267312492558379982687175200734934877598599011485707452665730"),alt_bn128_Fq("314498342015008975724433667930697407966947188435857772134235984660852259084")); + alt_bn128_Fq2::Frobenius_coeffs_c1[0] = alt_bn128_Fq("1"); + alt_bn128_Fq2::Frobenius_coeffs_c1[1] = alt_bn128_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582"); + + /* parameters for Fq6 */ + alt_bn128_Fq6::non_residue = alt_bn128_Fq2(alt_bn128_Fq("9"),alt_bn128_Fq("1")); + alt_bn128_Fq6::Frobenius_coeffs_c1[0] = alt_bn128_Fq2(alt_bn128_Fq("1"),alt_bn128_Fq("0")); + alt_bn128_Fq6::Frobenius_coeffs_c1[1] = alt_bn128_Fq2(alt_bn128_Fq("21575463638280843010398324269430826099269044274347216827212613867836435027261"),alt_bn128_Fq("10307601595873709700152284273816112264069230130616436755625194854815875713954")); + alt_bn128_Fq6::Frobenius_coeffs_c1[2] = alt_bn128_Fq2(alt_bn128_Fq("21888242871839275220042445260109153167277707414472061641714758635765020556616"),alt_bn128_Fq("0")); + alt_bn128_Fq6::Frobenius_coeffs_c1[3] = alt_bn128_Fq2(alt_bn128_Fq("3772000881919853776433695186713858239009073593817195771773381919316419345261"),alt_bn128_Fq("2236595495967245188281701248203181795121068902605861227855261137820944008926")); + alt_bn128_Fq6::Frobenius_coeffs_c1[4] = alt_bn128_Fq2(alt_bn128_Fq("2203960485148121921418603742825762020974279258880205651966"),alt_bn128_Fq("0")); + alt_bn128_Fq6::Frobenius_coeffs_c1[5] = alt_bn128_Fq2(alt_bn128_Fq("18429021223477853657660792034369865839114504446431234726392080002137598044644"),alt_bn128_Fq("9344045779998320333812420223237981029506012124075525679208581902008406485703")); + alt_bn128_Fq6::Frobenius_coeffs_c2[0] = alt_bn128_Fq2(alt_bn128_Fq("1"),alt_bn128_Fq("0")); + alt_bn128_Fq6::Frobenius_coeffs_c2[1] = alt_bn128_Fq2(alt_bn128_Fq("2581911344467009335267311115468803099551665605076196740867805258568234346338"),alt_bn128_Fq("19937756971775647987995932169929341994314640652964949448313374472400716661030")); + alt_bn128_Fq6::Frobenius_coeffs_c2[2] = alt_bn128_Fq2(alt_bn128_Fq("2203960485148121921418603742825762020974279258880205651966"),alt_bn128_Fq("0")); + alt_bn128_Fq6::Frobenius_coeffs_c2[3] = alt_bn128_Fq2(alt_bn128_Fq("5324479202449903542726783395506214481928257762400643279780343368557297135718"),alt_bn128_Fq("16208900380737693084919495127334387981393726419856888799917914180988844123039")); + alt_bn128_Fq6::Frobenius_coeffs_c2[4] = alt_bn128_Fq2(alt_bn128_Fq("21888242871839275220042445260109153167277707414472061641714758635765020556616"),alt_bn128_Fq("0")); + alt_bn128_Fq6::Frobenius_coeffs_c2[5] = alt_bn128_Fq2(alt_bn128_Fq("13981852324922362344252311234282257507216387789820983642040889267519694726527"),alt_bn128_Fq("7629828391165209371577384193250820201684255241773809077146787135900891633097")); + + /* parameters for Fq12 */ + + alt_bn128_Fq12::non_residue = alt_bn128_Fq2(alt_bn128_Fq("9"),alt_bn128_Fq("1")); + alt_bn128_Fq12::Frobenius_coeffs_c1[0] = alt_bn128_Fq2(alt_bn128_Fq("1"),alt_bn128_Fq("0")); + alt_bn128_Fq12::Frobenius_coeffs_c1[1] = alt_bn128_Fq2(alt_bn128_Fq("8376118865763821496583973867626364092589906065868298776909617916018768340080"),alt_bn128_Fq("16469823323077808223889137241176536799009286646108169935659301613961712198316")); + alt_bn128_Fq12::Frobenius_coeffs_c1[2] = alt_bn128_Fq2(alt_bn128_Fq("21888242871839275220042445260109153167277707414472061641714758635765020556617"),alt_bn128_Fq("0")); + alt_bn128_Fq12::Frobenius_coeffs_c1[3] = alt_bn128_Fq2(alt_bn128_Fq("11697423496358154304825782922584725312912383441159505038794027105778954184319"),alt_bn128_Fq("303847389135065887422783454877609941456349188919719272345083954437860409601")); + alt_bn128_Fq12::Frobenius_coeffs_c1[4] = alt_bn128_Fq2(alt_bn128_Fq("21888242871839275220042445260109153167277707414472061641714758635765020556616"),alt_bn128_Fq("0")); + alt_bn128_Fq12::Frobenius_coeffs_c1[5] = alt_bn128_Fq2(alt_bn128_Fq("3321304630594332808241809054958361220322477375291206261884409189760185844239"),alt_bn128_Fq("5722266937896532885780051958958348231143373700109372999374820235121374419868")); + alt_bn128_Fq12::Frobenius_coeffs_c1[6] = alt_bn128_Fq2(alt_bn128_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582"),alt_bn128_Fq("0")); + alt_bn128_Fq12::Frobenius_coeffs_c1[7] = alt_bn128_Fq2(alt_bn128_Fq("13512124006075453725662431877630910996106405091429524885779419978626457868503"),alt_bn128_Fq("5418419548761466998357268504080738289687024511189653727029736280683514010267")); + alt_bn128_Fq12::Frobenius_coeffs_c1[8] = alt_bn128_Fq2(alt_bn128_Fq("2203960485148121921418603742825762020974279258880205651966"),alt_bn128_Fq("0")); + alt_bn128_Fq12::Frobenius_coeffs_c1[9] = alt_bn128_Fq2(alt_bn128_Fq("10190819375481120917420622822672549775783927716138318623895010788866272024264"),alt_bn128_Fq("21584395482704209334823622290379665147239961968378104390343953940207365798982")); + alt_bn128_Fq12::Frobenius_coeffs_c1[10] = alt_bn128_Fq2(alt_bn128_Fq("2203960485148121921418603742825762020974279258880205651967"),alt_bn128_Fq("0")); + alt_bn128_Fq12::Frobenius_coeffs_c1[11] = alt_bn128_Fq2(alt_bn128_Fq("18566938241244942414004596690298913868373833782006617400804628704885040364344"),alt_bn128_Fq("16165975933942742336466353786298926857552937457188450663314217659523851788715")); + + /* choice of short Weierstrass curve and its twist */ + + alt_bn128_coeff_b = alt_bn128_Fq("3"); + alt_bn128_twist = alt_bn128_Fq2(alt_bn128_Fq("9"), alt_bn128_Fq("1")); + alt_bn128_twist_coeff_b = alt_bn128_coeff_b * alt_bn128_twist.inverse(); + alt_bn128_twist_mul_by_b_c0 = alt_bn128_coeff_b * alt_bn128_Fq2::non_residue; + alt_bn128_twist_mul_by_b_c1 = alt_bn128_coeff_b * alt_bn128_Fq2::non_residue; + alt_bn128_twist_mul_by_q_X = alt_bn128_Fq2(alt_bn128_Fq("21575463638280843010398324269430826099269044274347216827212613867836435027261"), + alt_bn128_Fq("10307601595873709700152284273816112264069230130616436755625194854815875713954")); + alt_bn128_twist_mul_by_q_Y = alt_bn128_Fq2(alt_bn128_Fq("2821565182194536844548159561693502659359617185244120367078079554186484126554"), + alt_bn128_Fq("3505843767911556378687030309984248845540243509899259641013678093033130930403")); + + /* choice of group G1 */ + alt_bn128_G1::G1_zero = alt_bn128_G1(alt_bn128_Fq::zero(), + alt_bn128_Fq::one(), + alt_bn128_Fq::zero()); + alt_bn128_G1::G1_one = alt_bn128_G1(alt_bn128_Fq("1"), + alt_bn128_Fq("2"), + alt_bn128_Fq::one()); + alt_bn128_G1::wnaf_window_table.push_back(11); + alt_bn128_G1::wnaf_window_table.push_back(24); + alt_bn128_G1::wnaf_window_table.push_back(60); + alt_bn128_G1::wnaf_window_table.push_back(127); + + alt_bn128_G1::fixed_base_exp_window_table.resize(0); + // window 1 is unbeaten in [-inf, 4.99] + alt_bn128_G1::fixed_base_exp_window_table.push_back(1); + // window 2 is unbeaten in [4.99, 10.99] + alt_bn128_G1::fixed_base_exp_window_table.push_back(5); + // window 3 is unbeaten in [10.99, 32.29] + alt_bn128_G1::fixed_base_exp_window_table.push_back(11); + // window 4 is unbeaten in [32.29, 55.23] + alt_bn128_G1::fixed_base_exp_window_table.push_back(32); + // window 5 is unbeaten in [55.23, 162.03] + alt_bn128_G1::fixed_base_exp_window_table.push_back(55); + // window 6 is unbeaten in [162.03, 360.15] + alt_bn128_G1::fixed_base_exp_window_table.push_back(162); + // window 7 is unbeaten in [360.15, 815.44] + alt_bn128_G1::fixed_base_exp_window_table.push_back(360); + // window 8 is unbeaten in [815.44, 2373.07] + alt_bn128_G1::fixed_base_exp_window_table.push_back(815); + // window 9 is unbeaten in [2373.07, 6977.75] + alt_bn128_G1::fixed_base_exp_window_table.push_back(2373); + // window 10 is unbeaten in [6977.75, 7122.23] + alt_bn128_G1::fixed_base_exp_window_table.push_back(6978); + // window 11 is unbeaten in [7122.23, 57818.46] + alt_bn128_G1::fixed_base_exp_window_table.push_back(7122); + // window 12 is never the best + alt_bn128_G1::fixed_base_exp_window_table.push_back(0); + // window 13 is unbeaten in [57818.46, 169679.14] + alt_bn128_G1::fixed_base_exp_window_table.push_back(57818); + // window 14 is never the best + alt_bn128_G1::fixed_base_exp_window_table.push_back(0); + // window 15 is unbeaten in [169679.14, 439758.91] + alt_bn128_G1::fixed_base_exp_window_table.push_back(169679); + // window 16 is unbeaten in [439758.91, 936073.41] + alt_bn128_G1::fixed_base_exp_window_table.push_back(439759); + // window 17 is unbeaten in [936073.41, 4666554.74] + alt_bn128_G1::fixed_base_exp_window_table.push_back(936073); + // window 18 is never the best + alt_bn128_G1::fixed_base_exp_window_table.push_back(0); + // window 19 is unbeaten in [4666554.74, 7580404.42] + alt_bn128_G1::fixed_base_exp_window_table.push_back(4666555); + // window 20 is unbeaten in [7580404.42, 34552892.20] + alt_bn128_G1::fixed_base_exp_window_table.push_back(7580404); + // window 21 is never the best + alt_bn128_G1::fixed_base_exp_window_table.push_back(0); + // window 22 is unbeaten in [34552892.20, inf] + alt_bn128_G1::fixed_base_exp_window_table.push_back(34552892); + + /* choice of group G2 */ + + alt_bn128_G2::G2_zero = alt_bn128_G2(alt_bn128_Fq2::zero(), + alt_bn128_Fq2::one(), + alt_bn128_Fq2::zero()); + + alt_bn128_G2::G2_one = alt_bn128_G2(alt_bn128_Fq2(alt_bn128_Fq("10857046999023057135944570762232829481370756359578518086990519993285655852781"), + alt_bn128_Fq("11559732032986387107991004021392285783925812861821192530917403151452391805634")), + alt_bn128_Fq2(alt_bn128_Fq("8495653923123431417604973247489272438418190587263600148770280649306958101930"), + alt_bn128_Fq("4082367875863433681332203403145435568316851327593401208105741076214120093531")), + alt_bn128_Fq2::one()); + alt_bn128_G2::wnaf_window_table.push_back(5); + alt_bn128_G2::wnaf_window_table.push_back(15); + alt_bn128_G2::wnaf_window_table.push_back(39); + alt_bn128_G2::wnaf_window_table.push_back(109); + + alt_bn128_G2::fixed_base_exp_window_table.resize(0); + // window 1 is unbeaten in [-inf, 5.10] + alt_bn128_G2::fixed_base_exp_window_table.push_back(1); + // window 2 is unbeaten in [5.10, 10.43] + alt_bn128_G2::fixed_base_exp_window_table.push_back(5); + // window 3 is unbeaten in [10.43, 25.28] + alt_bn128_G2::fixed_base_exp_window_table.push_back(10); + // window 4 is unbeaten in [25.28, 59.00] + alt_bn128_G2::fixed_base_exp_window_table.push_back(25); + // window 5 is unbeaten in [59.00, 154.03] + alt_bn128_G2::fixed_base_exp_window_table.push_back(59); + // window 6 is unbeaten in [154.03, 334.25] + alt_bn128_G2::fixed_base_exp_window_table.push_back(154); + // window 7 is unbeaten in [334.25, 742.58] + alt_bn128_G2::fixed_base_exp_window_table.push_back(334); + // window 8 is unbeaten in [742.58, 2034.40] + alt_bn128_G2::fixed_base_exp_window_table.push_back(743); + // window 9 is unbeaten in [2034.40, 4987.56] + alt_bn128_G2::fixed_base_exp_window_table.push_back(2034); + // window 10 is unbeaten in [4987.56, 8888.27] + alt_bn128_G2::fixed_base_exp_window_table.push_back(4988); + // window 11 is unbeaten in [8888.27, 26271.13] + alt_bn128_G2::fixed_base_exp_window_table.push_back(8888); + // window 12 is unbeaten in [26271.13, 39768.20] + alt_bn128_G2::fixed_base_exp_window_table.push_back(26271); + // window 13 is unbeaten in [39768.20, 106275.75] + alt_bn128_G2::fixed_base_exp_window_table.push_back(39768); + // window 14 is unbeaten in [106275.75, 141703.40] + alt_bn128_G2::fixed_base_exp_window_table.push_back(106276); + // window 15 is unbeaten in [141703.40, 462422.97] + alt_bn128_G2::fixed_base_exp_window_table.push_back(141703); + // window 16 is unbeaten in [462422.97, 926871.84] + alt_bn128_G2::fixed_base_exp_window_table.push_back(462423); + // window 17 is unbeaten in [926871.84, 4873049.17] + alt_bn128_G2::fixed_base_exp_window_table.push_back(926872); + // window 18 is never the best + alt_bn128_G2::fixed_base_exp_window_table.push_back(0); + // window 19 is unbeaten in [4873049.17, 5706707.88] + alt_bn128_G2::fixed_base_exp_window_table.push_back(4873049); + // window 20 is unbeaten in [5706707.88, 31673814.95] + alt_bn128_G2::fixed_base_exp_window_table.push_back(5706708); + // window 21 is never the best + alt_bn128_G2::fixed_base_exp_window_table.push_back(0); + // window 22 is unbeaten in [31673814.95, inf] + alt_bn128_G2::fixed_base_exp_window_table.push_back(31673815); + + /* pairing parameters */ + + alt_bn128_ate_loop_count = bigint_q("29793968203157093288"); + alt_bn128_ate_is_loop_count_neg = false; + alt_bn128_final_exponent = bigint<12*alt_bn128_q_limbs>("552484233613224096312617126783173147097382103762957654188882734314196910839907541213974502761540629817009608548654680343627701153829446747810907373256841551006201639677726139946029199968412598804882391702273019083653272047566316584365559776493027495458238373902875937659943504873220554161550525926302303331747463515644711876653177129578303191095900909191624817826566688241804408081892785725967931714097716709526092261278071952560171111444072049229123565057483750161460024353346284167282452756217662335528813519139808291170539072125381230815729071544861602750936964829313608137325426383735122175229541155376346436093930287402089517426973178917569713384748081827255472576937471496195752727188261435633271238710131736096299798168852925540549342330775279877006784354801422249722573783561685179618816480037695005515426162362431072245638324744480"); + alt_bn128_final_exponent_z = bigint_q("4965661367192848881"); + alt_bn128_final_exponent_is_z_neg = false; + +} +} // libsnark diff --git a/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_init.hpp b/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_init.hpp new file mode 100644 index 000000000..c3bea7673 --- /dev/null +++ b/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_init.hpp @@ -0,0 +1,57 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef ALT_BN128_INIT_HPP_ +#define ALT_BN128_INIT_HPP_ +#include "algebra/curves/public_params.hpp" +#include "algebra/fields/fp.hpp" +#include "algebra/fields/fp2.hpp" +#include "algebra/fields/fp6_3over2.hpp" +#include "algebra/fields/fp12_2over3over2.hpp" + +namespace libsnark { + +const mp_size_t alt_bn128_r_bitcount = 254; +const mp_size_t alt_bn128_q_bitcount = 254; + +const mp_size_t alt_bn128_r_limbs = (alt_bn128_r_bitcount+GMP_NUMB_BITS-1)/GMP_NUMB_BITS; +const mp_size_t alt_bn128_q_limbs = (alt_bn128_q_bitcount+GMP_NUMB_BITS-1)/GMP_NUMB_BITS; + +extern bigint alt_bn128_modulus_r; +extern bigint alt_bn128_modulus_q; + +typedef Fp_model alt_bn128_Fr; +typedef Fp_model alt_bn128_Fq; +typedef Fp2_model alt_bn128_Fq2; +typedef Fp6_3over2_model alt_bn128_Fq6; +typedef Fp12_2over3over2_model alt_bn128_Fq12; +typedef alt_bn128_Fq12 alt_bn128_GT; + +// parameters for Barreto--Naehrig curve E/Fq : y^2 = x^3 + b +extern alt_bn128_Fq alt_bn128_coeff_b; +// parameters for twisted Barreto--Naehrig curve E'/Fq2 : y^2 = x^3 + b/xi +extern alt_bn128_Fq2 alt_bn128_twist; +extern alt_bn128_Fq2 alt_bn128_twist_coeff_b; +extern alt_bn128_Fq alt_bn128_twist_mul_by_b_c0; +extern alt_bn128_Fq alt_bn128_twist_mul_by_b_c1; +extern alt_bn128_Fq2 alt_bn128_twist_mul_by_q_X; +extern alt_bn128_Fq2 alt_bn128_twist_mul_by_q_Y; + +// parameters for pairing +extern bigint alt_bn128_ate_loop_count; +extern bool alt_bn128_ate_is_loop_count_neg; +extern bigint<12*alt_bn128_q_limbs> alt_bn128_final_exponent; +extern bigint alt_bn128_final_exponent_z; +extern bool alt_bn128_final_exponent_is_z_neg; + +void init_alt_bn128_params(); + +class alt_bn128_G1; +class alt_bn128_G2; + +} // libsnark +#endif // ALT_BN128_INIT_HPP_ diff --git a/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_pairing.cpp b/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_pairing.cpp new file mode 100644 index 000000000..db556c5b2 --- /dev/null +++ b/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_pairing.cpp @@ -0,0 +1,547 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include "algebra/curves/alt_bn128/alt_bn128_pairing.hpp" +#include "algebra/curves/alt_bn128/alt_bn128_init.hpp" +#include "algebra/curves/alt_bn128/alt_bn128_g1.hpp" +#include "algebra/curves/alt_bn128/alt_bn128_g2.hpp" +#include +#include "common/profiling.hpp" +#include "common/assert_except.hpp" + +namespace libsnark { + +bool alt_bn128_ate_G1_precomp::operator==(const alt_bn128_ate_G1_precomp &other) const +{ + return (this->PX == other.PX && + this->PY == other.PY); +} + +std::ostream& operator<<(std::ostream &out, const alt_bn128_ate_G1_precomp &prec_P) +{ + out << prec_P.PX << OUTPUT_SEPARATOR << prec_P.PY; + + return out; +} + +std::istream& operator>>(std::istream &in, alt_bn128_ate_G1_precomp &prec_P) +{ + in >> prec_P.PX; + consume_OUTPUT_SEPARATOR(in); + in >> prec_P.PY; + + return in; +} + +bool alt_bn128_ate_ell_coeffs::operator==(const alt_bn128_ate_ell_coeffs &other) const +{ + return (this->ell_0 == other.ell_0 && + this->ell_VW == other.ell_VW && + this->ell_VV == other.ell_VV); +} + +std::ostream& operator<<(std::ostream &out, const alt_bn128_ate_ell_coeffs &c) +{ + out << c.ell_0 << OUTPUT_SEPARATOR << c.ell_VW << OUTPUT_SEPARATOR << c.ell_VV; + return out; +} + +std::istream& operator>>(std::istream &in, alt_bn128_ate_ell_coeffs &c) +{ + in >> c.ell_0; + consume_OUTPUT_SEPARATOR(in); + in >> c.ell_VW; + consume_OUTPUT_SEPARATOR(in); + in >> c.ell_VV; + + return in; +} + +bool alt_bn128_ate_G2_precomp::operator==(const alt_bn128_ate_G2_precomp &other) const +{ + return (this->QX == other.QX && + this->QY == other.QY && + this->coeffs == other.coeffs); +} + +std::ostream& operator<<(std::ostream& out, const alt_bn128_ate_G2_precomp &prec_Q) +{ + out << prec_Q.QX << OUTPUT_SEPARATOR << prec_Q.QY << "\n"; + out << prec_Q.coeffs.size() << "\n"; + for (const alt_bn128_ate_ell_coeffs &c : prec_Q.coeffs) + { + out << c << OUTPUT_NEWLINE; + } + return out; +} + +std::istream& operator>>(std::istream& in, alt_bn128_ate_G2_precomp &prec_Q) +{ + in >> prec_Q.QX; + consume_OUTPUT_SEPARATOR(in); + in >> prec_Q.QY; + consume_newline(in); + + prec_Q.coeffs.clear(); + size_t s; + in >> s; + + consume_newline(in); + + prec_Q.coeffs.reserve(s); + + for (size_t i = 0; i < s; ++i) + { + alt_bn128_ate_ell_coeffs c; + in >> c; + consume_OUTPUT_NEWLINE(in); + prec_Q.coeffs.emplace_back(c); + } + + return in; +} + +/* final exponentiations */ + +alt_bn128_Fq12 alt_bn128_final_exponentiation_first_chunk(const alt_bn128_Fq12 &elt) +{ + enter_block("Call to alt_bn128_final_exponentiation_first_chunk"); + + /* + Computes result = elt^((q^6-1)*(q^2+1)). + Follows, e.g., Beuchat et al page 9, by computing result as follows: + elt^((q^6-1)*(q^2+1)) = (conj(elt) * elt^(-1))^(q^2+1) + More precisely: + A = conj(elt) + B = elt.inverse() + C = A * B + D = C.Frobenius_map(2) + result = D * C + */ + + const alt_bn128_Fq12 A = alt_bn128_Fq12(elt.c0,-elt.c1); + const alt_bn128_Fq12 B = elt.inverse(); + const alt_bn128_Fq12 C = A * B; + const alt_bn128_Fq12 D = C.Frobenius_map(2); + const alt_bn128_Fq12 result = D * C; + + leave_block("Call to alt_bn128_final_exponentiation_first_chunk"); + + return result; +} + +alt_bn128_Fq12 alt_bn128_exp_by_neg_z(const alt_bn128_Fq12 &elt) +{ + enter_block("Call to alt_bn128_exp_by_neg_z"); + + alt_bn128_Fq12 result = elt.cyclotomic_exp(alt_bn128_final_exponent_z); + if (!alt_bn128_final_exponent_is_z_neg) + { + result = result.unitary_inverse(); + } + + leave_block("Call to alt_bn128_exp_by_neg_z"); + + return result; +} + +alt_bn128_Fq12 alt_bn128_final_exponentiation_last_chunk(const alt_bn128_Fq12 &elt) +{ + enter_block("Call to alt_bn128_final_exponentiation_last_chunk"); + + /* + Follows Laura Fuentes-Castaneda et al. "Faster hashing to G2" + by computing: + + result = elt^(q^3 * (12*z^3 + 6z^2 + 4z - 1) + + q^2 * (12*z^3 + 6z^2 + 6z) + + q * (12*z^3 + 6z^2 + 4z) + + 1 * (12*z^3 + 12z^2 + 6z + 1)) + which equals + + result = elt^( 2z * ( 6z^2 + 3z + 1 ) * (q^4 - q^2 + 1)/r ). + + Using the following addition chain: + + A = exp_by_neg_z(elt) // = elt^(-z) + B = A^2 // = elt^(-2*z) + C = B^2 // = elt^(-4*z) + D = C * B // = elt^(-6*z) + E = exp_by_neg_z(D) // = elt^(6*z^2) + F = E^2 // = elt^(12*z^2) + G = epx_by_neg_z(F) // = elt^(-12*z^3) + H = conj(D) // = elt^(6*z) + I = conj(G) // = elt^(12*z^3) + J = I * E // = elt^(12*z^3 + 6*z^2) + K = J * H // = elt^(12*z^3 + 6*z^2 + 6*z) + L = K * B // = elt^(12*z^3 + 6*z^2 + 4*z) + M = K * E // = elt^(12*z^3 + 12*z^2 + 6*z) + N = M * elt // = elt^(12*z^3 + 12*z^2 + 6*z + 1) + O = L.Frobenius_map(1) // = elt^(q*(12*z^3 + 6*z^2 + 4*z)) + P = O * N // = elt^(q*(12*z^3 + 6*z^2 + 4*z) * (12*z^3 + 12*z^2 + 6*z + 1)) + Q = K.Frobenius_map(2) // = elt^(q^2 * (12*z^3 + 6*z^2 + 6*z)) + R = Q * P // = elt^(q^2 * (12*z^3 + 6*z^2 + 6*z) + q*(12*z^3 + 6*z^2 + 4*z) * (12*z^3 + 12*z^2 + 6*z + 1)) + S = conj(elt) // = elt^(-1) + T = S * L // = elt^(12*z^3 + 6*z^2 + 4*z - 1) + U = T.Frobenius_map(3) // = elt^(q^3(12*z^3 + 6*z^2 + 4*z - 1)) + V = U * R // = elt^(q^3(12*z^3 + 6*z^2 + 4*z - 1) + q^2 * (12*z^3 + 6*z^2 + 6*z) + q*(12*z^3 + 6*z^2 + 4*z) * (12*z^3 + 12*z^2 + 6*z + 1)) + result = V + + */ + + const alt_bn128_Fq12 A = alt_bn128_exp_by_neg_z(elt); + const alt_bn128_Fq12 B = A.cyclotomic_squared(); + const alt_bn128_Fq12 C = B.cyclotomic_squared(); + const alt_bn128_Fq12 D = C * B; + const alt_bn128_Fq12 E = alt_bn128_exp_by_neg_z(D); + const alt_bn128_Fq12 F = E.cyclotomic_squared(); + const alt_bn128_Fq12 G = alt_bn128_exp_by_neg_z(F); + const alt_bn128_Fq12 H = D.unitary_inverse(); + const alt_bn128_Fq12 I = G.unitary_inverse(); + const alt_bn128_Fq12 J = I * E; + const alt_bn128_Fq12 K = J * H; + const alt_bn128_Fq12 L = K * B; + const alt_bn128_Fq12 M = K * E; + const alt_bn128_Fq12 N = M * elt; + const alt_bn128_Fq12 O = L.Frobenius_map(1); + const alt_bn128_Fq12 P = O * N; + const alt_bn128_Fq12 Q = K.Frobenius_map(2); + const alt_bn128_Fq12 R = Q * P; + const alt_bn128_Fq12 S = elt.unitary_inverse(); + const alt_bn128_Fq12 T = S * L; + const alt_bn128_Fq12 U = T.Frobenius_map(3); + const alt_bn128_Fq12 V = U * R; + + const alt_bn128_Fq12 result = V; + + leave_block("Call to alt_bn128_final_exponentiation_last_chunk"); + + return result; +} + +alt_bn128_GT alt_bn128_final_exponentiation(const alt_bn128_Fq12 &elt) +{ + enter_block("Call to alt_bn128_final_exponentiation"); + /* OLD naive version: + alt_bn128_GT result = elt^alt_bn128_final_exponent; + */ + alt_bn128_Fq12 A = alt_bn128_final_exponentiation_first_chunk(elt); + alt_bn128_GT result = alt_bn128_final_exponentiation_last_chunk(A); + + leave_block("Call to alt_bn128_final_exponentiation"); + return result; +} + +/* ate pairing */ + +void doubling_step_for_flipped_miller_loop(const alt_bn128_Fq two_inv, + alt_bn128_G2 ¤t, + alt_bn128_ate_ell_coeffs &c) +{ + const alt_bn128_Fq2 X = current.X, Y = current.Y, Z = current.Z; + + const alt_bn128_Fq2 A = two_inv * (X * Y); // A = X1 * Y1 / 2 + const alt_bn128_Fq2 B = Y.squared(); // B = Y1^2 + const alt_bn128_Fq2 C = Z.squared(); // C = Z1^2 + const alt_bn128_Fq2 D = C+C+C; // D = 3 * C + const alt_bn128_Fq2 E = alt_bn128_twist_coeff_b * D; // E = twist_b * D + const alt_bn128_Fq2 F = E+E+E; // F = 3 * E + const alt_bn128_Fq2 G = two_inv * (B+F); // G = (B+F)/2 + const alt_bn128_Fq2 H = (Y+Z).squared() - (B+C); // H = (Y1+Z1)^2-(B+C) + const alt_bn128_Fq2 I = E-B; // I = E-B + const alt_bn128_Fq2 J = X.squared(); // J = X1^2 + const alt_bn128_Fq2 E_squared = E.squared(); // E_squared = E^2 + + current.X = A * (B-F); // X3 = A * (B-F) + current.Y = G.squared() - (E_squared+E_squared+E_squared); // Y3 = G^2 - 3*E^2 + current.Z = B * H; // Z3 = B * H + c.ell_0 = alt_bn128_twist * I; // ell_0 = xi * I + c.ell_VW = -H; // ell_VW = - H (later: * yP) + c.ell_VV = J+J+J; // ell_VV = 3*J (later: * xP) +} + +void mixed_addition_step_for_flipped_miller_loop(const alt_bn128_G2 base, + alt_bn128_G2 ¤t, + alt_bn128_ate_ell_coeffs &c) +{ + const alt_bn128_Fq2 X1 = current.X, Y1 = current.Y, Z1 = current.Z; + const alt_bn128_Fq2 &x2 = base.X, &y2 = base.Y; + + const alt_bn128_Fq2 D = X1 - x2 * Z1; // D = X1 - X2*Z1 + const alt_bn128_Fq2 E = Y1 - y2 * Z1; // E = Y1 - Y2*Z1 + const alt_bn128_Fq2 F = D.squared(); // F = D^2 + const alt_bn128_Fq2 G = E.squared(); // G = E^2 + const alt_bn128_Fq2 H = D*F; // H = D*F + const alt_bn128_Fq2 I = X1 * F; // I = X1 * F + const alt_bn128_Fq2 J = H + Z1*G - (I+I); // J = H + Z1*G - (I+I) + + current.X = D * J; // X3 = D*J + current.Y = E * (I-J)-(H * Y1); // Y3 = E*(I-J)-(H*Y1) + current.Z = Z1 * H; // Z3 = Z1*H + c.ell_0 = alt_bn128_twist * (E * x2 - D * y2); // ell_0 = xi * (E * X2 - D * Y2) + c.ell_VV = - E; // ell_VV = - E (later: * xP) + c.ell_VW = D; // ell_VW = D (later: * yP ) +} + +alt_bn128_ate_G1_precomp alt_bn128_ate_precompute_G1(const alt_bn128_G1& P) +{ + enter_block("Call to alt_bn128_ate_precompute_G1"); + + alt_bn128_G1 Pcopy = P; + Pcopy.to_affine_coordinates(); + + alt_bn128_ate_G1_precomp result; + result.PX = Pcopy.X; + result.PY = Pcopy.Y; + + leave_block("Call to alt_bn128_ate_precompute_G1"); + return result; +} + +alt_bn128_ate_G2_precomp alt_bn128_ate_precompute_G2(const alt_bn128_G2& Q) +{ + enter_block("Call to alt_bn128_ate_precompute_G2"); + + alt_bn128_G2 Qcopy(Q); + Qcopy.to_affine_coordinates(); + + alt_bn128_Fq two_inv = (alt_bn128_Fq("2").inverse()); // could add to global params if needed + + alt_bn128_ate_G2_precomp result; + result.QX = Qcopy.X; + result.QY = Qcopy.Y; + + alt_bn128_G2 R; + R.X = Qcopy.X; + R.Y = Qcopy.Y; + R.Z = alt_bn128_Fq2::one(); + + const bigint &loop_count = alt_bn128_ate_loop_count; + bool found_one = false; + alt_bn128_ate_ell_coeffs c; + + for (long i = loop_count.max_bits(); i >= 0; --i) + { + const bool bit = loop_count.test_bit(i); + if (!found_one) + { + /* this skips the MSB itself */ + found_one |= bit; + continue; + } + + doubling_step_for_flipped_miller_loop(two_inv, R, c); + result.coeffs.push_back(c); + + if (bit) + { + mixed_addition_step_for_flipped_miller_loop(Qcopy, R, c); + result.coeffs.push_back(c); + } + } + + alt_bn128_G2 Q1 = Qcopy.mul_by_q(); + assert_except(Q1.Z == alt_bn128_Fq2::one()); + alt_bn128_G2 Q2 = Q1.mul_by_q(); + assert_except(Q2.Z == alt_bn128_Fq2::one()); + + if (alt_bn128_ate_is_loop_count_neg) + { + R.Y = - R.Y; + } + Q2.Y = - Q2.Y; + + mixed_addition_step_for_flipped_miller_loop(Q1, R, c); + result.coeffs.push_back(c); + + mixed_addition_step_for_flipped_miller_loop(Q2, R, c); + result.coeffs.push_back(c); + + leave_block("Call to alt_bn128_ate_precompute_G2"); + return result; +} + +alt_bn128_Fq12 alt_bn128_ate_miller_loop(const alt_bn128_ate_G1_precomp &prec_P, + const alt_bn128_ate_G2_precomp &prec_Q) +{ + enter_block("Call to alt_bn128_ate_miller_loop"); + + alt_bn128_Fq12 f = alt_bn128_Fq12::one(); + + bool found_one = false; + size_t idx = 0; + + const bigint &loop_count = alt_bn128_ate_loop_count; + alt_bn128_ate_ell_coeffs c; + + for (long i = loop_count.max_bits(); i >= 0; --i) + { + const bool bit = loop_count.test_bit(i); + if (!found_one) + { + /* this skips the MSB itself */ + found_one |= bit; + continue; + } + + /* code below gets executed for all bits (EXCEPT the MSB itself) of + alt_bn128_param_p (skipping leading zeros) in MSB to LSB + order */ + + c = prec_Q.coeffs[idx++]; + f = f.squared(); + f = f.mul_by_024(c.ell_0, prec_P.PY * c.ell_VW, prec_P.PX * c.ell_VV); + + if (bit) + { + c = prec_Q.coeffs[idx++]; + f = f.mul_by_024(c.ell_0, prec_P.PY * c.ell_VW, prec_P.PX * c.ell_VV); + } + + } + + if (alt_bn128_ate_is_loop_count_neg) + { + f = f.inverse(); + } + + c = prec_Q.coeffs[idx++]; + f = f.mul_by_024(c.ell_0,prec_P.PY * c.ell_VW,prec_P.PX * c.ell_VV); + + c = prec_Q.coeffs[idx++]; + f = f.mul_by_024(c.ell_0,prec_P.PY * c.ell_VW,prec_P.PX * c.ell_VV); + + leave_block("Call to alt_bn128_ate_miller_loop"); + return f; +} + +alt_bn128_Fq12 alt_bn128_ate_double_miller_loop(const alt_bn128_ate_G1_precomp &prec_P1, + const alt_bn128_ate_G2_precomp &prec_Q1, + const alt_bn128_ate_G1_precomp &prec_P2, + const alt_bn128_ate_G2_precomp &prec_Q2) +{ + enter_block("Call to alt_bn128_ate_double_miller_loop"); + + alt_bn128_Fq12 f = alt_bn128_Fq12::one(); + + bool found_one = false; + size_t idx = 0; + + const bigint &loop_count = alt_bn128_ate_loop_count; + for (long i = loop_count.max_bits(); i >= 0; --i) + { + const bool bit = loop_count.test_bit(i); + if (!found_one) + { + /* this skips the MSB itself */ + found_one |= bit; + continue; + } + + /* code below gets executed for all bits (EXCEPT the MSB itself) of + alt_bn128_param_p (skipping leading zeros) in MSB to LSB + order */ + + alt_bn128_ate_ell_coeffs c1 = prec_Q1.coeffs[idx]; + alt_bn128_ate_ell_coeffs c2 = prec_Q2.coeffs[idx]; + ++idx; + + f = f.squared(); + + f = f.mul_by_024(c1.ell_0, prec_P1.PY * c1.ell_VW, prec_P1.PX * c1.ell_VV); + f = f.mul_by_024(c2.ell_0, prec_P2.PY * c2.ell_VW, prec_P2.PX * c2.ell_VV); + + if (bit) + { + alt_bn128_ate_ell_coeffs c1 = prec_Q1.coeffs[idx]; + alt_bn128_ate_ell_coeffs c2 = prec_Q2.coeffs[idx]; + ++idx; + + f = f.mul_by_024(c1.ell_0, prec_P1.PY * c1.ell_VW, prec_P1.PX * c1.ell_VV); + f = f.mul_by_024(c2.ell_0, prec_P2.PY * c2.ell_VW, prec_P2.PX * c2.ell_VV); + } + } + + if (alt_bn128_ate_is_loop_count_neg) + { + f = f.inverse(); + } + + alt_bn128_ate_ell_coeffs c1 = prec_Q1.coeffs[idx]; + alt_bn128_ate_ell_coeffs c2 = prec_Q2.coeffs[idx]; + ++idx; + f = f.mul_by_024(c1.ell_0, prec_P1.PY * c1.ell_VW, prec_P1.PX * c1.ell_VV); + f = f.mul_by_024(c2.ell_0, prec_P2.PY * c2.ell_VW, prec_P2.PX * c2.ell_VV); + + c1 = prec_Q1.coeffs[idx]; + c2 = prec_Q2.coeffs[idx]; + ++idx; + f = f.mul_by_024(c1.ell_0, prec_P1.PY * c1.ell_VW, prec_P1.PX * c1.ell_VV); + f = f.mul_by_024(c2.ell_0, prec_P2.PY * c2.ell_VW, prec_P2.PX * c2.ell_VV); + + leave_block("Call to alt_bn128_ate_double_miller_loop"); + + return f; +} + +alt_bn128_Fq12 alt_bn128_ate_pairing(const alt_bn128_G1& P, const alt_bn128_G2 &Q) +{ + enter_block("Call to alt_bn128_ate_pairing"); + alt_bn128_ate_G1_precomp prec_P = alt_bn128_ate_precompute_G1(P); + alt_bn128_ate_G2_precomp prec_Q = alt_bn128_ate_precompute_G2(Q); + alt_bn128_Fq12 result = alt_bn128_ate_miller_loop(prec_P, prec_Q); + leave_block("Call to alt_bn128_ate_pairing"); + return result; +} + +alt_bn128_GT alt_bn128_ate_reduced_pairing(const alt_bn128_G1 &P, const alt_bn128_G2 &Q) +{ + enter_block("Call to alt_bn128_ate_reduced_pairing"); + const alt_bn128_Fq12 f = alt_bn128_ate_pairing(P, Q); + const alt_bn128_GT result = alt_bn128_final_exponentiation(f); + leave_block("Call to alt_bn128_ate_reduced_pairing"); + return result; +} + +/* choice of pairing */ + +alt_bn128_G1_precomp alt_bn128_precompute_G1(const alt_bn128_G1& P) +{ + return alt_bn128_ate_precompute_G1(P); +} + +alt_bn128_G2_precomp alt_bn128_precompute_G2(const alt_bn128_G2& Q) +{ + return alt_bn128_ate_precompute_G2(Q); +} + +alt_bn128_Fq12 alt_bn128_miller_loop(const alt_bn128_G1_precomp &prec_P, + const alt_bn128_G2_precomp &prec_Q) +{ + return alt_bn128_ate_miller_loop(prec_P, prec_Q); +} + +alt_bn128_Fq12 alt_bn128_double_miller_loop(const alt_bn128_G1_precomp &prec_P1, + const alt_bn128_G2_precomp &prec_Q1, + const alt_bn128_G1_precomp &prec_P2, + const alt_bn128_G2_precomp &prec_Q2) +{ + return alt_bn128_ate_double_miller_loop(prec_P1, prec_Q1, prec_P2, prec_Q2); +} + +alt_bn128_Fq12 alt_bn128_pairing(const alt_bn128_G1& P, + const alt_bn128_G2 &Q) +{ + return alt_bn128_ate_pairing(P, Q); +} + +alt_bn128_GT alt_bn128_reduced_pairing(const alt_bn128_G1 &P, + const alt_bn128_G2 &Q) +{ + return alt_bn128_ate_reduced_pairing(P, Q); +} +} // libsnark diff --git a/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_pairing.hpp b/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_pairing.hpp new file mode 100644 index 000000000..15d325485 --- /dev/null +++ b/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_pairing.hpp @@ -0,0 +1,92 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef ALT_BN128_PAIRING_HPP_ +#define ALT_BN128_PAIRING_HPP_ +#include +#include "algebra/curves/alt_bn128/alt_bn128_init.hpp" + +namespace libsnark { + +/* final exponentiation */ + +alt_bn128_GT alt_bn128_final_exponentiation(const alt_bn128_Fq12 &elt); + +/* ate pairing */ + +struct alt_bn128_ate_G1_precomp { + alt_bn128_Fq PX; + alt_bn128_Fq PY; + + bool operator==(const alt_bn128_ate_G1_precomp &other) const; + friend std::ostream& operator<<(std::ostream &out, const alt_bn128_ate_G1_precomp &prec_P); + friend std::istream& operator>>(std::istream &in, alt_bn128_ate_G1_precomp &prec_P); +}; + +struct alt_bn128_ate_ell_coeffs { + alt_bn128_Fq2 ell_0; + alt_bn128_Fq2 ell_VW; + alt_bn128_Fq2 ell_VV; + + bool operator==(const alt_bn128_ate_ell_coeffs &other) const; + friend std::ostream& operator<<(std::ostream &out, const alt_bn128_ate_ell_coeffs &dc); + friend std::istream& operator>>(std::istream &in, alt_bn128_ate_ell_coeffs &dc); +}; + +struct alt_bn128_ate_G2_precomp { + alt_bn128_Fq2 QX; + alt_bn128_Fq2 QY; + std::vector coeffs; + + bool operator==(const alt_bn128_ate_G2_precomp &other) const; + friend std::ostream& operator<<(std::ostream &out, const alt_bn128_ate_G2_precomp &prec_Q); + friend std::istream& operator>>(std::istream &in, alt_bn128_ate_G2_precomp &prec_Q); +}; + +alt_bn128_ate_G1_precomp alt_bn128_ate_precompute_G1(const alt_bn128_G1& P); +alt_bn128_ate_G2_precomp alt_bn128_ate_precompute_G2(const alt_bn128_G2& Q); + +alt_bn128_Fq12 alt_bn128_ate_miller_loop(const alt_bn128_ate_G1_precomp &prec_P, + const alt_bn128_ate_G2_precomp &prec_Q); +alt_bn128_Fq12 alt_bn128_ate_double_miller_loop(const alt_bn128_ate_G1_precomp &prec_P1, + const alt_bn128_ate_G2_precomp &prec_Q1, + const alt_bn128_ate_G1_precomp &prec_P2, + const alt_bn128_ate_G2_precomp &prec_Q2); + +alt_bn128_Fq12 alt_bn128_ate_pairing(const alt_bn128_G1& P, + const alt_bn128_G2 &Q); +alt_bn128_GT alt_bn128_ate_reduced_pairing(const alt_bn128_G1 &P, + const alt_bn128_G2 &Q); + +/* choice of pairing */ + +typedef alt_bn128_ate_G1_precomp alt_bn128_G1_precomp; +typedef alt_bn128_ate_G2_precomp alt_bn128_G2_precomp; + +alt_bn128_G1_precomp alt_bn128_precompute_G1(const alt_bn128_G1& P); + +alt_bn128_G2_precomp alt_bn128_precompute_G2(const alt_bn128_G2& Q); + +alt_bn128_Fq12 alt_bn128_miller_loop(const alt_bn128_G1_precomp &prec_P, + const alt_bn128_G2_precomp &prec_Q); + +alt_bn128_Fq12 alt_bn128_double_miller_loop(const alt_bn128_G1_precomp &prec_P1, + const alt_bn128_G2_precomp &prec_Q1, + const alt_bn128_G1_precomp &prec_P2, + const alt_bn128_G2_precomp &prec_Q2); + +alt_bn128_Fq12 alt_bn128_pairing(const alt_bn128_G1& P, + const alt_bn128_G2 &Q); + +alt_bn128_GT alt_bn128_reduced_pairing(const alt_bn128_G1 &P, + const alt_bn128_G2 &Q); + +alt_bn128_GT alt_bn128_affine_reduced_pairing(const alt_bn128_G1 &P, + const alt_bn128_G2 &Q); + +} // libsnark +#endif // ALT_BN128_PAIRING_HPP_ diff --git a/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_pp.cpp b/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_pp.cpp new file mode 100644 index 000000000..25ea924d8 --- /dev/null +++ b/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_pp.cpp @@ -0,0 +1,58 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include "algebra/curves/alt_bn128/alt_bn128_pp.hpp" + +namespace libsnark { + +void alt_bn128_pp::init_public_params() +{ + init_alt_bn128_params(); +} + +alt_bn128_GT alt_bn128_pp::final_exponentiation(const alt_bn128_Fq12 &elt) +{ + return alt_bn128_final_exponentiation(elt); +} + +alt_bn128_G1_precomp alt_bn128_pp::precompute_G1(const alt_bn128_G1 &P) +{ + return alt_bn128_precompute_G1(P); +} + +alt_bn128_G2_precomp alt_bn128_pp::precompute_G2(const alt_bn128_G2 &Q) +{ + return alt_bn128_precompute_G2(Q); +} + +alt_bn128_Fq12 alt_bn128_pp::miller_loop(const alt_bn128_G1_precomp &prec_P, + const alt_bn128_G2_precomp &prec_Q) +{ + return alt_bn128_miller_loop(prec_P, prec_Q); +} + +alt_bn128_Fq12 alt_bn128_pp::double_miller_loop(const alt_bn128_G1_precomp &prec_P1, + const alt_bn128_G2_precomp &prec_Q1, + const alt_bn128_G1_precomp &prec_P2, + const alt_bn128_G2_precomp &prec_Q2) +{ + return alt_bn128_double_miller_loop(prec_P1, prec_Q1, prec_P2, prec_Q2); +} + +alt_bn128_Fq12 alt_bn128_pp::pairing(const alt_bn128_G1 &P, + const alt_bn128_G2 &Q) +{ + return alt_bn128_pairing(P, Q); +} + +alt_bn128_Fq12 alt_bn128_pp::reduced_pairing(const alt_bn128_G1 &P, + const alt_bn128_G2 &Q) +{ + return alt_bn128_reduced_pairing(P, Q); +} + +} // libsnark diff --git a/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_pp.hpp b/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_pp.hpp new file mode 100644 index 000000000..ec8059dcb --- /dev/null +++ b/src/snark/libsnark/algebra/curves/alt_bn128/alt_bn128_pp.hpp @@ -0,0 +1,50 @@ +/** @file +***************************************************************************** +* @author This file is part of libsnark, developed by SCIPR Lab +* and contributors (see AUTHORS). +* @copyright MIT license (see LICENSE file) +*****************************************************************************/ + +#ifndef ALT_BN128_PP_HPP_ +#define ALT_BN128_PP_HPP_ +#include "algebra/curves/public_params.hpp" +#include "algebra/curves/alt_bn128/alt_bn128_init.hpp" +#include "algebra/curves/alt_bn128/alt_bn128_g1.hpp" +#include "algebra/curves/alt_bn128/alt_bn128_g2.hpp" +#include "algebra/curves/alt_bn128/alt_bn128_pairing.hpp" + +namespace libsnark { + +class alt_bn128_pp { +public: + typedef alt_bn128_Fr Fp_type; + typedef alt_bn128_G1 G1_type; + typedef alt_bn128_G2 G2_type; + typedef alt_bn128_G1_precomp G1_precomp_type; + typedef alt_bn128_G2_precomp G2_precomp_type; + typedef alt_bn128_Fq Fq_type; + typedef alt_bn128_Fq2 Fqe_type; + typedef alt_bn128_Fq12 Fqk_type; + typedef alt_bn128_GT GT_type; + + static const bool has_affine_pairing = false; + + static void init_public_params(); + static alt_bn128_GT final_exponentiation(const alt_bn128_Fq12 &elt); + static alt_bn128_G1_precomp precompute_G1(const alt_bn128_G1 &P); + static alt_bn128_G2_precomp precompute_G2(const alt_bn128_G2 &Q); + static alt_bn128_Fq12 miller_loop(const alt_bn128_G1_precomp &prec_P, + const alt_bn128_G2_precomp &prec_Q); + static alt_bn128_Fq12 double_miller_loop(const alt_bn128_G1_precomp &prec_P1, + const alt_bn128_G2_precomp &prec_Q1, + const alt_bn128_G1_precomp &prec_P2, + const alt_bn128_G2_precomp &prec_Q2); + static alt_bn128_Fq12 pairing(const alt_bn128_G1 &P, + const alt_bn128_G2 &Q); + static alt_bn128_Fq12 reduced_pairing(const alt_bn128_G1 &P, + const alt_bn128_G2 &Q); +}; + +} // libsnark + +#endif // ALT_BN128_PP_HPP_ diff --git a/src/snark/libsnark/algebra/curves/curve_utils.hpp b/src/snark/libsnark/algebra/curves/curve_utils.hpp new file mode 100644 index 000000000..33a8e1e17 --- /dev/null +++ b/src/snark/libsnark/algebra/curves/curve_utils.hpp @@ -0,0 +1,22 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef CURVE_UTILS_HPP_ +#define CURVE_UTILS_HPP_ +#include + +#include "algebra/fields/bigint.hpp" + +namespace libsnark { + +template +GroupT scalar_mul(const GroupT &base, const bigint &scalar); + +} // libsnark +#include "algebra/curves/curve_utils.tcc" + +#endif // CURVE_UTILS_HPP_ diff --git a/src/snark/libsnark/algebra/curves/curve_utils.tcc b/src/snark/libsnark/algebra/curves/curve_utils.tcc new file mode 100644 index 000000000..251d75d8b --- /dev/null +++ b/src/snark/libsnark/algebra/curves/curve_utils.tcc @@ -0,0 +1,37 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef CURVE_UTILS_TCC_ +#define CURVE_UTILS_TCC_ + +namespace libsnark { + +template +GroupT scalar_mul(const GroupT &base, const bigint &scalar) +{ + GroupT result = GroupT::zero(); + + bool found_one = false; + for (long i = scalar.max_bits() - 1; i >= 0; --i) + { + if (found_one) + { + result = result.dbl(); + } + + if (scalar.test_bit(i)) + { + found_one = true; + result = result + base; + } + } + + return result; +} + +} // libsnark +#endif // CURVE_UTILS_TCC_ diff --git a/src/snark/libsnark/algebra/curves/public_params.hpp b/src/snark/libsnark/algebra/curves/public_params.hpp new file mode 100644 index 000000000..07e047560 --- /dev/null +++ b/src/snark/libsnark/algebra/curves/public_params.hpp @@ -0,0 +1,103 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef PUBLIC_PARAMS_HPP_ +#define PUBLIC_PARAMS_HPP_ +#include + +namespace libsnark { + +/* + for every curve the user should define corresponding + public_params with the following typedefs: + + Fp_type + G1_type + G2_type + G1_precomp_type + G2_precomp_type + affine_ate_G1_precomp_type + affine_ate_G2_precomp_type + Fq_type + Fqe_type + Fqk_type + GT_type + + one should also define the following static methods: + + void init_public_params(); + + GT final_exponentiation(const Fqk &elt); + + G1_precomp precompute_G1(const G1 &P); + G2_precomp precompute_G2(const G2 &Q); + + Fqk miller_loop(const G1_precomp &prec_P, + const G2_precomp &prec_Q); + + affine_ate_G1_precomp affine_ate_precompute_G1(const G1 &P); + affine_ate_G2_precomp affine_ate_precompute_G2(const G2 &Q); + + + Fqk affine_ate_miller_loop(const affine_ate_G1_precomp &prec_P, + const affine_ate_G2_precomp &prec_Q); + Fqk affine_ate_e_over_e_miller_loop(const affine_ate_G1_precomp &prec_P1, + const affine_ate_G2_precomp &prec_Q1, + const affine_ate_G1_precomp &prec_P2, + const affine_ate_G2_precomp &prec_Q2); + Fqk affine_ate_e_times_e_over_e_miller_loop(const affine_ate_G1_precomp &prec_P1, + const affine_ate_G2_precomp &prec_Q1, + const affine_ate_G1_precomp &prec_P2, + const affine_ate_G2_precomp &prec_Q2, + const affine_ate_G1_precomp &prec_P3, + const affine_ate_G2_precomp &prec_Q3); + Fqk double_miller_loop(const G1_precomp &prec_P1, + const G2_precomp &prec_Q1, + const G1_precomp &prec_P2, + const G2_precomp &prec_Q2); + + Fqk pairing(const G1 &P, + const G2 &Q); + GT reduced_pairing(const G1 &P, + const G2 &Q); + GT affine_reduced_pairing(const G1 &P, + const G2 &Q); +*/ + +template +using Fr = typename EC_ppT::Fp_type; +template +using G1 = typename EC_ppT::G1_type; +template +using G2 = typename EC_ppT::G2_type; +template +using G1_precomp = typename EC_ppT::G1_precomp_type; +template +using G2_precomp = typename EC_ppT::G2_precomp_type; +template +using affine_ate_G1_precomp = typename EC_ppT::affine_ate_G1_precomp_type; +template +using affine_ate_G2_precomp = typename EC_ppT::affine_ate_G2_precomp_type; +template +using Fq = typename EC_ppT::Fq_type; +template +using Fqe = typename EC_ppT::Fqe_type; +template +using Fqk = typename EC_ppT::Fqk_type; +template +using GT = typename EC_ppT::GT_type; + +template +using Fr_vector = std::vector >; +template +using G1_vector = std::vector >; +template +using G2_vector = std::vector >; + +} // libsnark + +#endif // PUBLIC_PARAMS_HPP_ diff --git a/src/snark/libsnark/algebra/curves/tests/test_bilinearity.cpp b/src/snark/libsnark/algebra/curves/tests/test_bilinearity.cpp new file mode 100644 index 000000000..fe6593bae --- /dev/null +++ b/src/snark/libsnark/algebra/curves/tests/test_bilinearity.cpp @@ -0,0 +1,121 @@ +/** + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ +#include "common/profiling.hpp" +#ifdef CURVE_BN128 +#include "algebra/curves/bn128/bn128_pp.hpp" +#endif +#include "algebra/curves/alt_bn128/alt_bn128_pp.hpp" + +#include + +using namespace libsnark; + +template +void pairing_test() +{ + GT GT_one = GT::one(); + + printf("Running bilinearity tests:\n"); + G1 P = (Fr::random_element()) * G1::one(); + //G1 P = Fr("2") * G1::one(); + G2 Q = (Fr::random_element()) * G2::one(); + //G2 Q = Fr("3") * G2::one(); + + printf("P:\n"); + P.print(); + P.print_coordinates(); + printf("Q:\n"); + Q.print(); + Q.print_coordinates(); + printf("\n\n"); + + Fr s = Fr::random_element(); + //Fr s = Fr("2"); + G1 sP = s * P; + G2 sQ = s * Q; + + printf("Pairing bilinearity tests (three must match):\n"); + GT ans1 = ppT::reduced_pairing(sP, Q); + GT ans2 = ppT::reduced_pairing(P, sQ); + GT ans3 = ppT::reduced_pairing(P, Q)^s; + ans1.print(); + ans2.print(); + ans3.print(); + EXPECT_EQ(ans1, ans2); + EXPECT_EQ(ans2, ans3); + + EXPECT_NE(ans1, GT_one); + EXPECT_EQ((ans1^Fr::field_char()), GT_one); + printf("\n\n"); +} + +template +void double_miller_loop_test() +{ + const G1 P1 = (Fr::random_element()) * G1::one(); + const G1 P2 = (Fr::random_element()) * G1::one(); + const G2 Q1 = (Fr::random_element()) * G2::one(); + const G2 Q2 = (Fr::random_element()) * G2::one(); + + const G1_precomp prec_P1 = ppT::precompute_G1(P1); + const G1_precomp prec_P2 = ppT::precompute_G1(P2); + const G2_precomp prec_Q1 = ppT::precompute_G2(Q1); + const G2_precomp prec_Q2 = ppT::precompute_G2(Q2); + + const Fqk ans_1 = ppT::miller_loop(prec_P1, prec_Q1); + const Fqk ans_2 = ppT::miller_loop(prec_P2, prec_Q2); + const Fqk ans_12 = ppT::double_miller_loop(prec_P1, prec_Q1, prec_P2, prec_Q2); + EXPECT_EQ(ans_1 * ans_2, ans_12); +} + +template +void affine_pairing_test() +{ + GT GT_one = GT::one(); + + printf("Running bilinearity tests:\n"); + G1 P = (Fr::random_element()) * G1::one(); + G2 Q = (Fr::random_element()) * G2::one(); + + printf("P:\n"); + P.print(); + printf("Q:\n"); + Q.print(); + printf("\n\n"); + + Fr s = Fr::random_element(); + G1 sP = s * P; + G2 sQ = s * Q; + + printf("Pairing bilinearity tests (three must match):\n"); + GT ans1 = ppT::affine_reduced_pairing(sP, Q); + GT ans2 = ppT::affine_reduced_pairing(P, sQ); + GT ans3 = ppT::affine_reduced_pairing(P, Q)^s; + ans1.print(); + ans2.print(); + ans3.print(); + EXPECT_EQ(ans1, ans2); + EXPECT_EQ(ans2, ans3); + + EXPECT_NE(ans1, GT_one); + EXPECT_EQ((ans1^Fr::field_char()), GT_one); + printf("\n\n"); +} + +TEST(algebra, bilinearity) +{ + start_profiling(); + alt_bn128_pp::init_public_params(); + pairing_test(); + double_miller_loop_test(); + +#ifdef CURVE_BN128 // BN128 has fancy dependencies so it may be disabled + bn128_pp::init_public_params(); + pairing_test(); + double_miller_loop_test(); +#endif +} diff --git a/src/snark/libsnark/algebra/curves/tests/test_groups.cpp b/src/snark/libsnark/algebra/curves/tests/test_groups.cpp new file mode 100644 index 000000000..7bb7c31cc --- /dev/null +++ b/src/snark/libsnark/algebra/curves/tests/test_groups.cpp @@ -0,0 +1,153 @@ +/** + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ +#include "common/profiling.hpp" +#ifdef CURVE_BN128 +#include "algebra/curves/bn128/bn128_pp.hpp" +#endif +#include "algebra/curves/alt_bn128/alt_bn128_pp.hpp" +#include + +#include + +using namespace libsnark; + +template +void test_mixed_add() +{ + GroupT base, el, result; + + base = GroupT::zero(); + el = GroupT::zero(); + el.to_special(); + result = base.mixed_add(el); + EXPECT_EQ(result, base + el); + + base = GroupT::zero(); + el = GroupT::random_element(); + el.to_special(); + result = base.mixed_add(el); + EXPECT_EQ(result, base + el); + + base = GroupT::random_element(); + el = GroupT::zero(); + el.to_special(); + result = base.mixed_add(el); + EXPECT_EQ(result, base + el); + + base = GroupT::random_element(); + el = GroupT::random_element(); + el.to_special(); + result = base.mixed_add(el); + EXPECT_EQ(result, base + el); + + base = GroupT::random_element(); + el = base; + el.to_special(); + result = base.mixed_add(el); + EXPECT_EQ(result, base.dbl()); +} + +template +void test_group() +{ + bigint<1> rand1 = bigint<1>("76749407"); + bigint<1> rand2 = bigint<1>("44410867"); + bigint<1> randsum = bigint<1>("121160274"); + + GroupT zero = GroupT::zero(); + EXPECT_EQ(zero, zero); + GroupT one = GroupT::one(); + EXPECT_EQ(one, one); + GroupT two = bigint<1>(2l) * GroupT::one(); + EXPECT_EQ(two, two); + GroupT five = bigint<1>(5l) * GroupT::one(); + + GroupT three = bigint<1>(3l) * GroupT::one(); + GroupT four = bigint<1>(4l) * GroupT::one(); + + EXPECT_EQ(two+five, three+four); + + GroupT a = GroupT::random_element(); + GroupT b = GroupT::random_element(); + + EXPECT_NE(one, zero); + EXPECT_NE(a, zero); + EXPECT_NE(a, one); + + EXPECT_NE(b, zero); + EXPECT_NE(b, one); + + EXPECT_EQ(a.dbl(), a + a); + EXPECT_EQ(b.dbl(), b + b); + EXPECT_EQ(one.add(two), three); + EXPECT_EQ(two.add(one), three); + EXPECT_EQ(a + b, b + a); + EXPECT_EQ(a - a, zero); + EXPECT_EQ(a - b, a + (-b)); + EXPECT_EQ(a - b, (-b) + a); + + // handle special cases + EXPECT_EQ(zero + (-a), -a); + EXPECT_EQ(zero - a, -a); + EXPECT_EQ(a - zero, a); + EXPECT_EQ(a + zero, a); + EXPECT_EQ(zero + a, a); + + EXPECT_EQ((a + b).dbl(), (a + b) + (b + a)); + EXPECT_EQ(bigint<1>("2") * (a + b), (a + b) + (b + a)); + + EXPECT_EQ((rand1 * a) + (rand2 * a), (randsum * a)); + + EXPECT_EQ(GroupT::order() * a, zero); + EXPECT_EQ(GroupT::order() * one, zero); + EXPECT_NE((GroupT::order() * a) - a, zero); + EXPECT_NE((GroupT::order() * one) - one, zero); + + test_mixed_add(); +} + +template +void test_mul_by_q() +{ + GroupT a = GroupT::random_element(); + EXPECT_EQ((GroupT::base_field_char()*a), a.mul_by_q()); +} + +template +void test_output() +{ + GroupT g = GroupT::zero(); + + for (size_t i = 0; i < 1000; ++i) + { + std::stringstream ss; + ss << g; + GroupT gg; + ss >> gg; + EXPECT_EQ(g, gg); + /* use a random point in next iteration */ + g = GroupT::random_element(); + } +} + +TEST(algebra, groups) +{ + alt_bn128_pp::init_public_params(); + test_group >(); + test_output >(); + test_group >(); + test_output >(); + test_mul_by_q >(); + +#ifdef CURVE_BN128 // BN128 has fancy dependencies so it may be disabled + bn128_pp::init_public_params(); + test_group >(); + test_output >(); + test_group >(); + test_output >(); +#endif +} diff --git a/src/snark/libsnark/algebra/evaluation_domain/domains/basic_radix2_domain.hpp b/src/snark/libsnark/algebra/evaluation_domain/domains/basic_radix2_domain.hpp new file mode 100644 index 000000000..3e127a063 --- /dev/null +++ b/src/snark/libsnark/algebra/evaluation_domain/domains/basic_radix2_domain.hpp @@ -0,0 +1,45 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for the "basic radix-2" evaluation domain. + + Roughly, the domain has size m = 2^k and consists of the m-th roots of unity. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef BASIC_RADIX2_DOMAIN_HPP_ +#define BASIC_RADIX2_DOMAIN_HPP_ + +#include "algebra/evaluation_domain/evaluation_domain.hpp" + +namespace libsnark { + +template +class basic_radix2_domain : public evaluation_domain { +public: + + FieldT omega; + + basic_radix2_domain(const size_t m); + + void FFT(std::vector &a); + void iFFT(std::vector &a); + void cosetFFT(std::vector &a, const FieldT &g); + void icosetFFT(std::vector &a, const FieldT &g); + std::vector lagrange_coeffs(const FieldT &t); + FieldT get_element(const size_t idx); + FieldT compute_Z(const FieldT &t); + void add_poly_Z(const FieldT &coeff, std::vector &H); + void divide_by_Z_on_coset(std::vector &P); + +}; + +} // libsnark + +#include "algebra/evaluation_domain/domains/basic_radix2_domain.tcc" + +#endif // BASIC_RADIX2_DOMAIN_HPP_ diff --git a/src/snark/libsnark/algebra/evaluation_domain/domains/basic_radix2_domain.tcc b/src/snark/libsnark/algebra/evaluation_domain/domains/basic_radix2_domain.tcc new file mode 100644 index 000000000..d315e8319 --- /dev/null +++ b/src/snark/libsnark/algebra/evaluation_domain/domains/basic_radix2_domain.tcc @@ -0,0 +1,112 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for the "basic radix-2" evaluation domain. + + See basic_radix2_domain.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef BASIC_RADIX2_DOMAIN_TCC_ +#define BASIC_RADIX2_DOMAIN_TCC_ + +#include "algebra/evaluation_domain/domains/basic_radix2_domain_aux.hpp" + +namespace libsnark { + +template +basic_radix2_domain::basic_radix2_domain(const size_t m) : evaluation_domain(m) +{ + assert(m > 1); + const size_t logm = log2(m); + assert(logm <= (FieldT::s)); + + omega = get_root_of_unity(m); +} + +template +void basic_radix2_domain::FFT(std::vector &a) +{ + enter_block("Execute FFT"); + assert(a.size() == this->m); + _basic_radix2_FFT(a, omega); + leave_block("Execute FFT"); +} + +template +void basic_radix2_domain::iFFT(std::vector &a) +{ + enter_block("Execute inverse FFT"); + assert(a.size() == this->m); + _basic_radix2_FFT(a, omega.inverse()); + + const FieldT sconst = FieldT(a.size()).inverse(); + for (size_t i = 0; i < a.size(); ++i) + { + a[i] *= sconst; + } + leave_block("Execute inverse FFT"); +} + +template +void basic_radix2_domain::cosetFFT(std::vector &a, const FieldT &g) +{ + enter_block("Execute coset FFT"); + _multiply_by_coset(a, g); + FFT(a); + leave_block("Execute coset FFT"); +} + +template +void basic_radix2_domain::icosetFFT(std::vector &a, const FieldT &g) +{ + enter_block("Execute inverse coset IFFT"); + iFFT(a); + _multiply_by_coset(a, g.inverse()); + leave_block("Execute inverse coset IFFT"); +} + +template +std::vector basic_radix2_domain::lagrange_coeffs(const FieldT &t) +{ + return _basic_radix2_lagrange_coeffs(this->m, t); +} + +template +FieldT basic_radix2_domain::get_element(const size_t idx) +{ + return omega^idx; +} + +template +FieldT basic_radix2_domain::compute_Z(const FieldT &t) +{ + return (t^this->m) - FieldT::one(); +} + +template +void basic_radix2_domain::add_poly_Z(const FieldT &coeff, std::vector &H) +{ + assert(H.size() == this->m+1); + H[this->m] += coeff; + H[0] -= coeff; +} + +template +void basic_radix2_domain::divide_by_Z_on_coset(std::vector &P) +{ + const FieldT coset = FieldT::multiplicative_generator; + const FieldT Z_inverse_at_coset = this->compute_Z(coset).inverse(); + for (size_t i = 0; i < this->m; ++i) + { + P[i] *= Z_inverse_at_coset; + } +} + +} // libsnark + +#endif // BASIC_RADIX2_DOMAIN_TCC_ diff --git a/src/snark/libsnark/algebra/evaluation_domain/domains/basic_radix2_domain_aux.hpp b/src/snark/libsnark/algebra/evaluation_domain/domains/basic_radix2_domain_aux.hpp new file mode 100644 index 000000000..c42ab2f6f --- /dev/null +++ b/src/snark/libsnark/algebra/evaluation_domain/domains/basic_radix2_domain_aux.hpp @@ -0,0 +1,48 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for auxiliary functions for the "basic radix-2" evaluation domain. + + These functions compute the radix-2 FFT (in single- or multi-thread mode) and, + also compute Lagrange coefficients. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef BASIC_RADIX2_DOMAIN_AUX_HPP_ +#define BASIC_RADIX2_DOMAIN_AUX_HPP_ + +namespace libsnark { + +/** + * Compute the radix-2 FFT of the vector a over the set S={omega^{0},...,omega^{m-1}}. + */ +template +void _basic_radix2_FFT(std::vector &a, const FieldT &omega); + +/** + * A multi-thread version of _basic_radix2_FFT. + */ +template +void _parallel_basic_radix2_FFT(std::vector &a, const FieldT &omega); + +/** + * Translate the vector a to a coset defined by g. + */ +template +void _multiply_by_coset(std::vector &a, const FieldT &g); + +/** + * Compute the m Lagrange coefficients, relative to the set S={omega^{0},...,omega^{m-1}}, at the field element t. + */ +template +std::vector _basic_radix2_lagrange_coeffs(const size_t m, const FieldT &t); + +} // libsnark + +#include "algebra/evaluation_domain/domains/basic_radix2_domain_aux.tcc" + +#endif // BASIC_RADIX2_DOMAIN_AUX_HPP_ diff --git a/src/snark/libsnark/algebra/evaluation_domain/domains/basic_radix2_domain_aux.tcc b/src/snark/libsnark/algebra/evaluation_domain/domains/basic_radix2_domain_aux.tcc new file mode 100644 index 000000000..138b82dbc --- /dev/null +++ b/src/snark/libsnark/algebra/evaluation_domain/domains/basic_radix2_domain_aux.tcc @@ -0,0 +1,242 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for auxiliary functions for the "basic radix-2" evaluation domain. + + See basic_radix2_domain_aux.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef BASIC_RADIX2_DOMAIN_AUX_TCC_ +#define BASIC_RADIX2_DOMAIN_AUX_TCC_ + +#include +#ifdef MULTICORE +#include +#endif +#include "algebra/fields/field_utils.hpp" +#include "common/profiling.hpp" +#include "common/utils.hpp" + +namespace libsnark { + +#ifdef MULTICORE +#define _basic_radix2_FFT _basic_parallel_radix2_FFT +#else +#define _basic_radix2_FFT _basic_serial_radix2_FFT +#endif + +/* + Below we make use of pseudocode from [CLRS 2n Ed, pp. 864]. + Also, note that it's the caller's responsibility to multiply by 1/N. + */ +template +void _basic_serial_radix2_FFT(std::vector &a, const FieldT &omega) +{ + const size_t n = a.size(), logn = log2(n); + assert(n == (1u << logn)); + + /* swapping in place (from Storer's book) */ + for (size_t k = 0; k < n; ++k) + { + const size_t rk = bitreverse(k, logn); + if (k < rk) + std::swap(a[k], a[rk]); + } + + size_t m = 1; // invariant: m = 2^{s-1} + for (size_t s = 1; s <= logn; ++s) + { + // w_m is 2^s-th root of unity now + const FieldT w_m = omega^(n/(2*m)); + + asm volatile ("/* pre-inner */"); + for (size_t k = 0; k < n; k += 2*m) + { + FieldT w = FieldT::one(); + for (size_t j = 0; j < m; ++j) + { + const FieldT t = w * a[k+j+m]; + a[k+j+m] = a[k+j] - t; + a[k+j] += t; + w *= w_m; + } + } + asm volatile ("/* post-inner */"); + m *= 2; + } +} + +template +void _basic_parallel_radix2_FFT_inner(std::vector &a, const FieldT &omega, const size_t log_cpus) +{ + const size_t num_cpus = 1ul< > tmp(num_cpus); + for (size_t j = 0; j < num_cpus; ++j) + { + tmp[j].resize(1ul<<(log_m-log_cpus), FieldT::zero()); + } + +#ifdef MULTICORE + #pragma omp parallel for +#endif + for (size_t j = 0; j < num_cpus; ++j) + { + const FieldT omega_j = omega^j; + const FieldT omega_step = omega^(j<<(log_m - log_cpus)); + + FieldT elt = FieldT::one(); + for (size_t i = 0; i < 1ul<<(log_m - log_cpus); ++i) + { + for (size_t s = 0; s < num_cpus; ++s) + { + // invariant: elt is omega^(j*idx) + const size_t idx = (i + (s<<(log_m - log_cpus))) % (1u << log_m); + tmp[j][i] += a[idx] * elt; + elt *= omega_step; + } + elt *= omega_j; + } + } + leave_block("Shuffle inputs"); + + enter_block("Execute sub-FFTs"); + const FieldT omega_num_cpus = omega^num_cpus; + +#ifdef MULTICORE + #pragma omp parallel for +#endif + for (size_t j = 0; j < num_cpus; ++j) + { + _basic_serial_radix2_FFT(tmp[j], omega_num_cpus); + } + leave_block("Execute sub-FFTs"); + + enter_block("Re-shuffle outputs"); + +#ifdef MULTICORE + #pragma omp parallel for +#endif + for (size_t i = 0; i < num_cpus; ++i) + { + for (size_t j = 0; j < 1ul<<(log_m - log_cpus); ++j) + { + // now: i = idx >> (log_m - log_cpus) and j = idx % (1u << (log_m - log_cpus)), for idx = ((i<<(log_m-log_cpus))+j) % (1u << log_m) + a[(j< +void _basic_parallel_radix2_FFT(std::vector &a, const FieldT &omega) +{ +#ifdef MULTICORE + const size_t num_cpus = omp_get_max_threads(); +#else + const size_t num_cpus = 1; +#endif + const size_t log_cpus = ((num_cpus & (num_cpus - 1)) == 0 ? log2(num_cpus) : log2(num_cpus) - 1); + +#ifdef DEBUG + print_indent(); printf("* Invoking parallel FFT on 2^%zu CPUs (omp_get_max_threads = %zu)\n", log_cpus, num_cpus); +#endif + + if (log_cpus == 0) + { + _basic_serial_radix2_FFT(a, omega); + } + else + { + _basic_parallel_radix2_FFT_inner(a, omega, log_cpus); + } +} + +template +void _multiply_by_coset(std::vector &a, const FieldT &g) +{ + //enter_block("Multiply by coset"); + FieldT u = g; + for (size_t i = 1; i < a.size(); ++i) + { + a[i] *= u; + u *= g; + } + //leave_block("Multiply by coset"); +} + +template +std::vector _basic_radix2_lagrange_coeffs(const size_t m, const FieldT &t) +{ + if (m == 1) + { + return std::vector(1, FieldT::one()); + } + + assert(m == (1u << log2(m))); + + const FieldT omega = get_root_of_unity(m); + + std::vector u(m, FieldT::zero()); + + /* + If t equals one of the roots of unity in S={omega^{0},...,omega^{m-1}} + then output 1 at the right place, and 0 elsewhere + */ + + if ((t^m) == (FieldT::one())) + { + FieldT omega_i = FieldT::one(); + for (size_t i = 0; i < m; ++i) + { + if (omega_i == t) // i.e., t equals omega^i + { + u[i] = FieldT::one(); + return u; + } + + omega_i *= omega; + } + } + + /* + Otherwise, if t does not equal any of the roots of unity in S, + then compute each L_{i,S}(t) as Z_{S}(t) * v_i / (t-\omega^i) + where: + - Z_{S}(t) = \prod_{j} (t-\omega^j) = (t^m-1), and + - v_{i} = 1 / \prod_{j \neq i} (\omega^i-\omega^j). + Below we use the fact that v_{0} = 1/m and v_{i+1} = \omega * v_{i}. + */ + + const FieldT Z = (t^m)-FieldT::one(); + FieldT l = Z * FieldT(m).inverse(); + FieldT r = FieldT::one(); + for (size_t i = 0; i < m; ++i) + { + u[i] = l * (t - r).inverse(); + l *= omega; + r *= omega; + } + + return u; +} + +} // libsnark + +#endif // BASIC_RADIX2_DOMAIN_AUX_TCC_ diff --git a/src/snark/libsnark/algebra/evaluation_domain/evaluation_domain.hpp b/src/snark/libsnark/algebra/evaluation_domain/evaluation_domain.hpp new file mode 100644 index 000000000..d8d91ea68 --- /dev/null +++ b/src/snark/libsnark/algebra/evaluation_domain/evaluation_domain.hpp @@ -0,0 +1,125 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for evaluation domains. + + Roughly, given a desired size m for the domain, the constructor selects + a choice of domain S with size ~m that has been selected so to optimize + - computations of Lagrange polynomials, and + - FFT/iFFT computations. + An evaluation domain also provides other functions, e.g., accessing + individual elements in S or evaluating its vanishing polynomial. + + The descriptions below make use of the definition of a *Lagrange polynomial*, + which we recall. Given a field F, a subset S=(a_i)_i of F, and an index idx + in {0,...,|S-1|}, the idx-th Lagrange polynomial (wrt to subset S) is defined to be + \f[ L_{idx,S}(z) := prod_{k \neq idx} (z - a_k) / prod_{k \neq idx} (a_{idx} - a_k) \f] + Note that, by construction: + \f[ \forall j \neq idx: L_{idx,S}(a_{idx}) = 1 \text{ and } L_{idx,S}(a_j) = 0 \f] + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef EVALUATION_DOMAIN_HPP_ +#define EVALUATION_DOMAIN_HPP_ + +#include + +namespace libsnark { + +/** + * An evaluation domain. + */ +template +class evaluation_domain { +public: + + const size_t m; + + /** + * Construct an evaluation domain S of size m, if possible. + * + * (See the function get_evaluation_domain below.) + */ + evaluation_domain(const size_t m) : m(m) {}; + + /** + * Get the idx-th element in S. + */ + virtual FieldT get_element(const size_t idx) = 0; + + /** + * Compute the FFT, over the domain S, of the vector a. + */ + virtual void FFT(std::vector &a) = 0; + + /** + * Compute the inverse FFT, over the domain S, of the vector a. + */ + virtual void iFFT(std::vector &a) = 0; + + /** + * Compute the FFT, over the domain g*S, of the vector a. + */ + virtual void cosetFFT(std::vector &a, const FieldT &g) = 0; + + /** + * Compute the inverse FFT, over the domain g*S, of the vector a. + */ + virtual void icosetFFT(std::vector &a, const FieldT &g) = 0; + + /** + * Evaluate all Lagrange polynomials. + * + * The inputs are: + * - an integer m + * - an element t + * The output is a vector (b_{0},...,b_{m-1}) + * where b_{i} is the evaluation of L_{i,S}(z) at z = t. + */ + virtual std::vector lagrange_coeffs(const FieldT &t) = 0; + + /** + * Evaluate the vanishing polynomial of S at the field element t. + */ + virtual FieldT compute_Z(const FieldT &t) = 0; + + /** + * Add the coefficients of the vanishing polynomial of S to the coefficients of the polynomial H. + */ + virtual void add_poly_Z(const FieldT &coeff, std::vector &H) = 0; + + /** + * Multiply by the evaluation, on a coset of S, of the inverse of the vanishing polynomial of S. + */ + virtual void divide_by_Z_on_coset(std::vector &P) = 0; +}; + +/** + * Return an evaluation domain object in which the domain S has size |S| >= min_size. + * The function chooses from different supported domains, depending on min_size. + */ +template +std::shared_ptr > get_evaluation_domain(const size_t min_size); + +/** + * Naive evaluation of a *single* Lagrange polynomial, used for testing purposes. + * + * The inputs are: + * - an integer m + * - a domain S = (a_{0},...,a_{m-1}) of size m + * - a field element t + * - an index idx in {0,...,m-1} + * The output is the polynomial L_{idx,S}(z) evaluated at z = t. + */ +template +FieldT lagrange_eval(const size_t m, const std::vector &domain, const FieldT &t, const size_t idx); + +} // libsnark + +#include "algebra/evaluation_domain/evaluation_domain.tcc" + +#endif // EVALUATION_DOMAIN_HPP_ diff --git a/src/snark/libsnark/algebra/evaluation_domain/evaluation_domain.tcc b/src/snark/libsnark/algebra/evaluation_domain/evaluation_domain.tcc new file mode 100644 index 000000000..8e3ea7a62 --- /dev/null +++ b/src/snark/libsnark/algebra/evaluation_domain/evaluation_domain.tcc @@ -0,0 +1,117 @@ +/** @file + ***************************************************************************** + + Imeplementation of interfaces for evaluation domains. + + See evaluation_domain.hpp . + + We currently implement, and select among, three types of domains: + - "basic radix-2": the domain has size m = 2^k and consists of the m-th roots of unity + - "extended radix-2": the domain has size m = 2^{k+1} and consists of "the m-th roots of unity" union "a coset" + - "step radix-2": the domain has size m = 2^k + 2^r and consists of "the 2^k-th roots of unity" union "a coset of 2^r-th roots of unity" + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef EVALUATION_DOMAIN_TCC_ +#define EVALUATION_DOMAIN_TCC_ + +#include +#include "algebra/fields/field_utils.hpp" +#include "algebra/evaluation_domain/domains/basic_radix2_domain.hpp" + +namespace libsnark { + +template +std::shared_ptr > get_evaluation_domain(const size_t min_size) +{ + assert(min_size > 1); + const size_t log_min_size = log2(min_size); + assert(log_min_size <= (FieldT::s+1)); + + std::shared_ptr > result; + if (min_size == (1u << log_min_size)) + { + if (log_min_size == FieldT::s+1) + { + if (!inhibit_profiling_info) + { + print_indent(); printf("* Selected domain: extended_radix2\n"); + } + assert(0); + } + else + { + if (!inhibit_profiling_info) + { + print_indent(); printf("* Selected domain: basic_radix2\n"); + } + result.reset(new basic_radix2_domain(min_size)); + } + } + else + { + const size_t big = 1ul<<(log2(min_size)-1); + const size_t small = min_size - big; + const size_t rounded_small = (1ul<(big + rounded_small)); + } + else + { + if (!inhibit_profiling_info) + { + print_indent(); printf("* Selected domain: extended_radix2\n"); + } + assert(0); + } + } + else + { + if (!inhibit_profiling_info) + { + print_indent(); printf("* Selected domain: step_radix2\n"); + } + assert(0); + } + } + + return result; +} + +template +FieldT lagrange_eval(const size_t m, const std::vector &domain, const FieldT &t, const size_t idx) +{ + assert(m == domain.size()); + assert(idx < m); + + FieldT num = FieldT::one(); + FieldT denom = FieldT::one(); + + for (size_t k = 0; k < m; ++k) + { + if (k == idx) + { + continue; + } + + num *= t - domain[k]; + denom *= domain[idx] - domain[k]; + } + + return num * denom.inverse(); +} + +} // libsnark + +#endif // EVALUATION_DOMAIN_TCC_ diff --git a/src/snark/libsnark/algebra/exponentiation/exponentiation.hpp b/src/snark/libsnark/algebra/exponentiation/exponentiation.hpp new file mode 100644 index 000000000..a8a2c925c --- /dev/null +++ b/src/snark/libsnark/algebra/exponentiation/exponentiation.hpp @@ -0,0 +1,31 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for (square-and-multiply) exponentiation. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef EXPONENTIATION_HPP_ +#define EXPONENTIATION_HPP_ + +#include + +#include "algebra/fields/bigint.hpp" + +namespace libsnark { + +template +FieldT power(const FieldT &base, const bigint &exponent); + +template +FieldT power(const FieldT &base, const unsigned long exponent); + +} // libsnark + +#include "algebra/exponentiation/exponentiation.tcc" + +#endif // EXPONENTIATION_HPP_ diff --git a/src/snark/libsnark/algebra/exponentiation/exponentiation.tcc b/src/snark/libsnark/algebra/exponentiation/exponentiation.tcc new file mode 100644 index 000000000..dd557eb12 --- /dev/null +++ b/src/snark/libsnark/algebra/exponentiation/exponentiation.tcc @@ -0,0 +1,53 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for (square-and-multiply) exponentiation. + + See exponentiation.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef EXPONENTIATION_TCC_ +#define EXPONENTIATION_TCC_ + +#include "common/utils.hpp" + +namespace libsnark { + +template +FieldT power(const FieldT &base, const bigint &exponent) +{ + FieldT result = FieldT::one(); + + bool found_one = false; + + for (long i = exponent.max_bits() - 1; i >= 0; --i) + { + if (found_one) + { + result = result * result; + } + + if (exponent.test_bit(i)) + { + found_one = true; + result = result * base; + } + } + + return result; +} + +template +FieldT power(const FieldT &base, const unsigned long exponent) +{ + return power(base, bigint<1>(exponent)); +} + +} // libsnark + +#endif // EXPONENTIATION_TCC_ diff --git a/src/snark/libsnark/algebra/fields/bigint.hpp b/src/snark/libsnark/algebra/fields/bigint.hpp new file mode 100644 index 000000000..50f78b780 --- /dev/null +++ b/src/snark/libsnark/algebra/fields/bigint.hpp @@ -0,0 +1,70 @@ +/** @file + ***************************************************************************** + Declaration of bigint wrapper class around GMP's MPZ long integers. + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef BIGINT_HPP_ +#define BIGINT_HPP_ +#include +#include +#include +#include "common/serialization.hpp" + +namespace libsnark { + +template class bigint; +template std::ostream& operator<<(std::ostream &, const bigint&); +template std::istream& operator>>(std::istream &, bigint&); + +/** + * Wrapper class around GMP's MPZ long integers. It supports arithmetic operations, + * serialization and randomization. Serialization is fragile, see common/serialization.hpp. + */ + +template +class bigint { +public: + static const mp_size_t N = n; + + mp_limb_t data[n] = {0}; + + bigint() = default; + bigint(const unsigned long x); /// Initialize from a small integer + bigint(const char* s); /// Initialize from a string containing an integer in decimal notation + bigint(const mpz_t r); /// Initialize from MPZ element + + void print() const; + void print_hex() const; + bool operator==(const bigint& other) const; + bool operator!=(const bigint& other) const; + void clear(); + bool is_zero() const; + size_t max_bits() const { return n * GMP_NUMB_BITS; } + size_t num_bits() const; + + unsigned long as_ulong() const; /* return the last limb of the integer */ + void to_mpz(mpz_t r) const; + bool test_bit(const std::size_t bitno) const; + + template inline void operator+=(const bigint& other); + template inline bigint operator*(const bigint& other) const; + template static inline void div_qr(bigint& quotient, bigint& remainder, + const bigint& dividend, const bigint& divisor); + template inline bigint shorten(const bigint& q, const char *msg) const; + + inline void limit(const bigint& q, const char *msg) const; + bool operator>(const bigint& other) const; + + bigint& randomize(); + + friend std::ostream& operator<< (std::ostream &out, const bigint &b); + friend std::istream& operator>> (std::istream &in, bigint &b); +}; + +} // libsnark +#include "algebra/fields/bigint.tcc" +#endif diff --git a/src/snark/libsnark/algebra/fields/bigint.tcc b/src/snark/libsnark/algebra/fields/bigint.tcc new file mode 100644 index 000000000..81befd96a --- /dev/null +++ b/src/snark/libsnark/algebra/fields/bigint.tcc @@ -0,0 +1,278 @@ +/** @file + ***************************************************************************** + Implementation of bigint wrapper class around GMP's MPZ long integers. + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef BIGINT_TCC_ +#define BIGINT_TCC_ +#include +#include +#include +#include "sodium.h" + +namespace libsnark { + +template +bigint::bigint(const unsigned long x) /// Initialize from a small integer +{ + static_assert(ULONG_MAX <= GMP_NUMB_MAX, "unsigned long does not fit in a GMP limb"); + this->data[0] = x; +} + +template +bigint::bigint(const char* s) /// Initialize from a string containing an integer in decimal notation +{ + size_t l = strlen(s); + unsigned char* s_copy = new unsigned char[l]; + + for (size_t i = 0; i < l; ++i) + { + assert(s[i] >= '0' && s[i] <= '9'); + s_copy[i] = s[i] - '0'; + } + + mp_size_t limbs_written = mpn_set_str(this->data, s_copy, l, 10); + assert(limbs_written <= n); + + delete[] s_copy; +} + +template +bigint::bigint(const mpz_t r) /// Initialize from MPZ element +{ + mpz_t k; + mpz_init_set(k, r); + + for (size_t i = 0; i < n; ++i) + { + data[i] = mpz_get_ui(k); + mpz_fdiv_q_2exp(k, k, GMP_NUMB_BITS); + } + + assert(mpz_sgn(k) == 0); + mpz_clear(k); +} + +template +void bigint::print() const +{ + gmp_printf("%Nd\n", this->data, n); +} + +template +void bigint::print_hex() const +{ + gmp_printf("%Nx\n", this->data, n); +} + +template +bool bigint::operator==(const bigint& other) const +{ + return (mpn_cmp(this->data, other.data, n) == 0); +} + +template +bool bigint::operator!=(const bigint& other) const +{ + return !(operator==(other)); +} + +template +void bigint::clear() +{ + mpn_zero(this->data, n); +} + +template +bool bigint::is_zero() const +{ + for (mp_size_t i = 0; i < n; ++i) + { + if (this->data[i]) + { + return false; + } + } + + return true; +} + +template +size_t bigint::num_bits() const +{ +/* + for (long i = max_bits(); i >= 0; --i) + { + if (this->test_bit(i)) + { + return i+1; + } + } + + return 0; +*/ + for (long i = n-1; i >= 0; --i) + { + mp_limb_t x = this->data[i]; + if (x == 0) + { + continue; + } + else + { + return ((i+1) * GMP_NUMB_BITS) - __builtin_clzl(x); + } + } + return 0; +} + +template +unsigned long bigint::as_ulong() const +{ + return this->data[0]; +} + +template +void bigint::to_mpz(mpz_t r) const +{ + mpz_set_ui(r, 0); + + for (int i = n-1; i >= 0; --i) + { + mpz_mul_2exp(r, r, GMP_NUMB_BITS); + mpz_add_ui(r, r, this->data[i]); + } +} + +template +bool bigint::test_bit(const std::size_t bitno) const +{ + if (bitno >= n * GMP_NUMB_BITS) + { + return false; + } + else + { + const std::size_t part = bitno/GMP_NUMB_BITS; + const std::size_t bit = bitno - (GMP_NUMB_BITS*part); + const mp_limb_t one = 1; + return (this->data[part] & (one< template +inline void bigint::operator+=(const bigint& other) +{ + static_assert(n >= m, "first arg must not be smaller than second arg for bigint in-place add"); + mpn_add(data, data, n, other.data, m); +} + +template template +inline bigint bigint::operator*(const bigint& other) const +{ + static_assert(n >= m, "first arg must not be smaller than second arg for bigint mul"); + bigint res; + mpn_mul(res.data, data, n, other.data, m); + return res; +} + +template template +inline void bigint::div_qr(bigint& quotient, bigint& remainder, + const bigint& dividend, const bigint& divisor) +{ + static_assert(n >= d, "dividend must not be smaller than divisor for bigint::div_qr"); + assert(divisor.data[d-1] != 0); + mpn_tdiv_qr(quotient.data, remainder.data, 0, dividend.data, n, divisor.data, d); +} + +// Return a copy shortened to m limbs provided it is less than limit, throwing std::domain_error if not in range. +template template +inline bigint bigint::shorten(const bigint& q, const char *msg) const +{ + static_assert(m <= n, "number of limbs must not increase for bigint::shorten"); + for (mp_size_t i = m; i < n; i++) { // high-order limbs + if (data[i] != 0) { + throw std::domain_error(msg); + } + } + bigint res; + mpn_copyi(res.data, data, m); + res.limit(q, msg); + return res; +} + +template +inline void bigint::limit(const bigint& q, const char *msg) const +{ + if (!(q > *this)) { + throw std::domain_error(msg); + } +} + +template +inline bool bigint::operator>(const bigint& other) const +{ + return mpn_cmp(this->data, other.data, n) > 0; +} + +template +bigint& bigint::randomize() +{ + assert(GMP_NUMB_BITS == sizeof(mp_limb_t) * 8); + + randombytes_buf(this->data, sizeof(mp_limb_t) * n); + + return (*this); +} + + +template +std::ostream& operator<<(std::ostream &out, const bigint &b) +{ +#ifdef BINARY_OUTPUT + out.write((char*)b.data, sizeof(b.data[0]) * n); +#else + mpz_t t; + mpz_init(t); + b.to_mpz(t); + + out << t; + + mpz_clear(t); +#endif + return out; +} + +template +std::istream& operator>>(std::istream &in, bigint &b) +{ +#ifdef BINARY_OUTPUT + in.read((char*)b.data, sizeof(b.data[0]) * n); +#else + std::string s; + in >> s; + + size_t l = s.size(); + unsigned char* s_copy = new unsigned char[l]; + + for (size_t i = 0; i < l; ++i) + { + assert(s[i] >= '0' && s[i] <= '9'); + s_copy[i] = s[i] - '0'; + } + + mp_size_t limbs_written = mpn_set_str(b.data, s_copy, l, 10); + assert(limbs_written <= n); + + delete[] s_copy; +#endif + return in; +} + +} // libsnark +#endif // BIGINT_TCC_ diff --git a/src/snark/libsnark/algebra/fields/field_utils.hpp b/src/snark/libsnark/algebra/fields/field_utils.hpp new file mode 100644 index 000000000..a07ecfe28 --- /dev/null +++ b/src/snark/libsnark/algebra/fields/field_utils.hpp @@ -0,0 +1,51 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef FIELD_UTILS_HPP_ +#define FIELD_UTILS_HPP_ +#include + +#include "common/utils.hpp" +#include "algebra/fields/bigint.hpp" + +namespace libsnark { + +// returns root of unity of order n (for n a power of 2), if one exists +template +FieldT get_root_of_unity(const size_t n); + +template +std::vector pack_int_vector_into_field_element_vector(const std::vector &v, const size_t w); + +template +std::vector pack_bit_vector_into_field_element_vector(const bit_vector &v, const size_t chunk_bits); + +template +std::vector pack_bit_vector_into_field_element_vector(const bit_vector &v); + +template +std::vector convert_bit_vector_to_field_element_vector(const bit_vector &v); + +template +bit_vector convert_field_element_vector_to_bit_vector(const std::vector &v); + +template +bit_vector convert_field_element_to_bit_vector(const FieldT &el); + +template +bit_vector convert_field_element_to_bit_vector(const FieldT &el, const size_t bitcount); + +template +FieldT convert_bit_vector_to_field_element(const bit_vector &v); + +template +void batch_invert(std::vector &vec); + +} // libsnark +#include "algebra/fields/field_utils.tcc" + +#endif // FIELD_UTILS_HPP_ diff --git a/src/snark/libsnark/algebra/fields/field_utils.tcc b/src/snark/libsnark/algebra/fields/field_utils.tcc new file mode 100644 index 000000000..13197b226 --- /dev/null +++ b/src/snark/libsnark/algebra/fields/field_utils.tcc @@ -0,0 +1,183 @@ +/** @file + ***************************************************************************** + Implementation of misc. math and serialization utility functions + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef FIELD_UTILS_TCC_ +#define FIELD_UTILS_TCC_ + +#include "common/utils.hpp" + +namespace libsnark { + +template +FieldT coset_shift() +{ + return FieldT::multiplicative_generator.squared(); +} + +template +FieldT get_root_of_unity(const size_t n) +{ + const size_t logn = log2(n); + assert(n == (1u << logn)); + assert(logn <= FieldT::s); + + FieldT omega = FieldT::root_of_unity; + for (size_t i = FieldT::s; i > logn; --i) + { + omega *= omega; + } + + return omega; +} + +template +std::vector pack_int_vector_into_field_element_vector(const std::vector &v, const size_t w) +{ + const size_t chunk_bits = FieldT::capacity(); + const size_t repacked_size = div_ceil(v.size() * w, chunk_bits); + std::vector result(repacked_size); + + for (size_t i = 0; i < repacked_size; ++i) + { + bigint b; + for (size_t j = 0; j < chunk_bits; ++j) + { + const size_t word_index = (i * chunk_bits + j) / w; + const size_t pos_in_word = (i * chunk_bits + j) % w; + const size_t word_or_0 = (word_index < v.size() ? v[word_index] : 0); + const size_t bit = (word_or_0 >> pos_in_word) & 1; + + b.data[j / GMP_NUMB_BITS] |= bit << (j % GMP_NUMB_BITS); + } + result[i] = FieldT(b); + } + + return result; +} + +template +std::vector pack_bit_vector_into_field_element_vector(const bit_vector &v, const size_t chunk_bits) +{ + assert(chunk_bits <= FieldT::capacity()); + + const size_t repacked_size = div_ceil(v.size(), chunk_bits); + std::vector result(repacked_size); + + for (size_t i = 0; i < repacked_size; ++i) + { + bigint b; + for (size_t j = 0; j < chunk_bits; ++j) + { + b.data[j / GMP_NUMB_BITS] |= ((i * chunk_bits + j) < v.size() && v[i * chunk_bits + j] ? 1ll : 0ll) << (j % GMP_NUMB_BITS); + } + result[i] = FieldT(b); + } + + return result; +} + +template +std::vector pack_bit_vector_into_field_element_vector(const bit_vector &v) +{ + return pack_bit_vector_into_field_element_vector(v, FieldT::capacity()); +} + +template +std::vector convert_bit_vector_to_field_element_vector(const bit_vector &v) +{ + std::vector result; + result.reserve(v.size()); + + for (const bool b : v) + { + result.emplace_back(b ? FieldT::one() : FieldT::zero()); + } + + return result; +} + +template +bit_vector convert_field_element_vector_to_bit_vector(const std::vector &v) +{ + bit_vector result; + + for (const FieldT &el : v) + { + const bit_vector el_bits = convert_field_element_to_bit_vector(el); + result.insert(result.end(), el_bits.begin(), el_bits.end()); + } + + return result; +} + +template +bit_vector convert_field_element_to_bit_vector(const FieldT &el) +{ + bit_vector result; + + bigint b = el.as_bigint(); + for (size_t i = 0; i < FieldT::size_in_bits(); ++i) + { + result.push_back(b.test_bit(i)); + } + + return result; +} + +template +bit_vector convert_field_element_to_bit_vector(const FieldT &el, const size_t bitcount) +{ + bit_vector result = convert_field_element_to_bit_vector(el); + result.resize(bitcount); + + return result; +} + +template +FieldT convert_bit_vector_to_field_element(const bit_vector &v) +{ + assert(v.size() <= FieldT::size_in_bits()); + + FieldT res = FieldT::zero(); + FieldT c = FieldT::one(); + for (bool b : v) + { + res += b ? c : FieldT::zero(); + c += c; + } + return res; +} + +template +void batch_invert(std::vector &vec) +{ + std::vector prod; + prod.reserve(vec.size()); + + FieldT acc = FieldT::one(); + + for (auto el : vec) + { + assert(!el.is_zero()); + prod.emplace_back(acc); + acc = acc * el; + } + + FieldT acc_inverse = acc.inverse(); + + for (long i = vec.size()-1; i >= 0; --i) + { + const FieldT old_el = vec[i]; + vec[i] = acc_inverse * prod[i]; + acc_inverse = acc_inverse * old_el; + } +} + +} // libsnark +#endif // FIELD_UTILS_TCC_ diff --git a/src/snark/libsnark/algebra/fields/fp.hpp b/src/snark/libsnark/algebra/fields/fp.hpp new file mode 100644 index 000000000..9679275d1 --- /dev/null +++ b/src/snark/libsnark/algebra/fields/fp.hpp @@ -0,0 +1,182 @@ +/** @file + ***************************************************************************** + Declaration of arithmetic in the finite field F[p], for prime p of fixed length. + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef FP_HPP_ +#define FP_HPP_ + +#include "algebra/fields/bigint.hpp" +#include "algebra/exponentiation/exponentiation.hpp" + +namespace libsnark { + +template& modulus> +class Fp_model; + +template& modulus> +std::ostream& operator<<(std::ostream &, const Fp_model&); + +template& modulus> +std::istream& operator>>(std::istream &, Fp_model &); + +/** + * Arithmetic in the finite field F[p], for prime p of fixed length. + * + * This class implements Fp-arithmetic, for a large prime p, using a fixed number + * of words. It is optimized for tight memory consumption, so the modulus p is + * passed as a template parameter, to avoid per-element overheads. + * + * The implementation is mostly a wrapper around GMP's MPN (constant-size integers). + * But for the integer sizes of interest for libsnark (3 to 5 limbs of 64 bits each), + * we implement performance-critical routines, like addition and multiplication, + * using hand-optimized assembly code. +*/ +template& modulus> +class Fp_model { +public: + bigint mont_repr; +public: + static const mp_size_t num_limbs = n; + static const constexpr bigint& mod = modulus; +#ifdef PROFILE_OP_COUNTS + static long long add_cnt; + static long long sub_cnt; + static long long mul_cnt; + static long long sqr_cnt; + static long long inv_cnt; +#endif + static size_t num_bits; + static bigint euler; // (modulus-1)/2 + static size_t s; // modulus = 2^s * t + 1 + static bigint t; // with t odd + static bigint t_minus_1_over_2; // (t-1)/2 + static Fp_model nqr; // a quadratic nonresidue + static Fp_model nqr_to_t; // nqr^t + static Fp_model multiplicative_generator; // generator of Fp^* + static Fp_model root_of_unity; // generator^((modulus-1)/2^s) + static mp_limb_t inv; // modulus^(-1) mod W, where W = 2^(word size) + static bigint Rsquared; // R^2, where R = W^k, where k = ?? + static bigint Rcubed; // R^3 + + static bool modulus_is_valid() { return modulus.data[n-1] != 0; } // mpn inverse assumes that highest limb is non-zero + + Fp_model() {}; + Fp_model(const bigint &b); + Fp_model(const long x, const bool is_unsigned=false); + + void set_ulong(const unsigned long x); + + void mul_reduce(const bigint &other); + + void clear(); + + /* Return the standard (not Montgomery) representation of the + Field element's requivalence class. I.e. Fp(2).as_bigint() + would return bigint(2) */ + bigint as_bigint() const; + /* Return the last limb of the standard representation of the + field element. E.g. on 64-bit architectures Fp(123).as_ulong() + and Fp(2^64+123).as_ulong() would both return 123. */ + unsigned long as_ulong() const; + + bool operator==(const Fp_model& other) const; + bool operator!=(const Fp_model& other) const; + bool is_zero() const; + + void print() const; + + Fp_model& operator+=(const Fp_model& other); + Fp_model& operator-=(const Fp_model& other); + Fp_model& operator*=(const Fp_model& other); + Fp_model& operator^=(const unsigned long pow); + + template + Fp_model& operator^=(const bigint &pow); + + Fp_model operator+(const Fp_model& other) const; + Fp_model operator-(const Fp_model& other) const; + Fp_model operator*(const Fp_model& other) const; + Fp_model operator-() const; + Fp_model squared() const; + Fp_model& invert(); + Fp_model inverse() const; + Fp_model sqrt() const; // HAS TO BE A SQUARE (else does not terminate) + + Fp_model operator^(const unsigned long pow) const; + template + Fp_model operator^(const bigint &pow) const; + + static size_t size_in_bits() { return num_bits; } + static size_t capacity() { return num_bits - 1; } + static bigint field_char() { return modulus; } + + static Fp_model zero(); + static Fp_model one(); + static Fp_model random_element(); + + friend std::ostream& operator<< (std::ostream &out, const Fp_model &p); + friend std::istream& operator>> (std::istream &in, Fp_model &p); +}; + +#ifdef PROFILE_OP_COUNTS +template& modulus> +long long Fp_model::add_cnt = 0; + +template& modulus> +long long Fp_model::sub_cnt = 0; + +template& modulus> +long long Fp_model::mul_cnt = 0; + +template& modulus> +long long Fp_model::sqr_cnt = 0; + +template& modulus> +long long Fp_model::inv_cnt = 0; +#endif + +template& modulus> +size_t Fp_model::num_bits; + +template& modulus> +bigint Fp_model::euler; + +template& modulus> +size_t Fp_model::s; + +template& modulus> +bigint Fp_model::t; + +template& modulus> +bigint Fp_model::t_minus_1_over_2; + +template& modulus> +Fp_model Fp_model::nqr; + +template& modulus> +Fp_model Fp_model::nqr_to_t; + +template& modulus> +Fp_model Fp_model::multiplicative_generator; + +template& modulus> +Fp_model Fp_model::root_of_unity; + +template& modulus> +mp_limb_t Fp_model::inv; + +template& modulus> +bigint Fp_model::Rsquared; + +template& modulus> +bigint Fp_model::Rcubed; + +} // libsnark +#include "algebra/fields/fp.tcc" + +#endif // FP_HPP_ diff --git a/src/snark/libsnark/algebra/fields/fp.tcc b/src/snark/libsnark/algebra/fields/fp.tcc new file mode 100644 index 000000000..566e99324 --- /dev/null +++ b/src/snark/libsnark/algebra/fields/fp.tcc @@ -0,0 +1,790 @@ +/** @file + ***************************************************************************** + Implementation of arithmetic in the finite field F[p], for prime p of fixed length. + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef FP_TCC_ +#define FP_TCC_ +#include +#include +#include + +#include "algebra/fields/fp_aux.tcc" +#include "algebra/fields/field_utils.hpp" +#include "common/assert_except.hpp" + +namespace libsnark { + +template& modulus> +void Fp_model::mul_reduce(const bigint &other) +{ + /* stupid pre-processor tricks; beware */ +#if defined(__x86_64__) && defined(USE_ASM) + if (n == 3) + { // Use asm-optimized Comba multiplication and reduction + mp_limb_t res[2*n]; + mp_limb_t c0, c1, c2; + COMBA_3_BY_3_MUL(c0, c1, c2, res, this->mont_repr.data, other.data); + + mp_limb_t k; + mp_limb_t tmp1, tmp2, tmp3; + REDUCE_6_LIMB_PRODUCT(k, tmp1, tmp2, tmp3, inv, res, modulus.data); + + /* subtract t > mod */ + __asm__ + ("/* check for overflow */ \n\t" + MONT_CMP(16) + MONT_CMP(8) + MONT_CMP(0) + + "/* subtract mod if overflow */ \n\t" + "subtract%=: \n\t" + MONT_FIRSTSUB + MONT_NEXTSUB(8) + MONT_NEXTSUB(16) + "done%=: \n\t" + : + : [tmp] "r" (res+n), [M] "r" (modulus.data) + : "cc", "memory", "%rax"); + mpn_copyi(this->mont_repr.data, res+n, n); + } + else if (n == 4) + { // use asm-optimized "CIOS method" + + mp_limb_t tmp[n+1]; + mp_limb_t T0=0, T1=1, cy=2, u=3; // TODO: fix this + + __asm__ (MONT_PRECOMPUTE + MONT_FIRSTITER(1) + MONT_FIRSTITER(2) + MONT_FIRSTITER(3) + MONT_FINALIZE(3) + MONT_ITERFIRST(1) + MONT_ITERITER(1, 1) + MONT_ITERITER(1, 2) + MONT_ITERITER(1, 3) + MONT_FINALIZE(3) + MONT_ITERFIRST(2) + MONT_ITERITER(2, 1) + MONT_ITERITER(2, 2) + MONT_ITERITER(2, 3) + MONT_FINALIZE(3) + MONT_ITERFIRST(3) + MONT_ITERITER(3, 1) + MONT_ITERITER(3, 2) + MONT_ITERITER(3, 3) + MONT_FINALIZE(3) + "/* check for overflow */ \n\t" + MONT_CMP(24) + MONT_CMP(16) + MONT_CMP(8) + MONT_CMP(0) + + "/* subtract mod if overflow */ \n\t" + "subtract%=: \n\t" + MONT_FIRSTSUB + MONT_NEXTSUB(8) + MONT_NEXTSUB(16) + MONT_NEXTSUB(24) + "done%=: \n\t" + : + : [tmp] "r" (tmp), [A] "r" (this->mont_repr.data), [B] "r" (other.data), [inv] "r" (inv), [M] "r" (modulus.data), + [T0] "r" (T0), [T1] "r" (T1), [cy] "r" (cy), [u] "r" (u) + : "cc", "memory", "%rax", "%rdx" + ); + mpn_copyi(this->mont_repr.data, tmp, n); + } + else if (n == 5) + { // use asm-optimized "CIOS method" + + mp_limb_t tmp[n+1]; + mp_limb_t T0=0, T1=1, cy=2, u=3; // TODO: fix this + + __asm__ (MONT_PRECOMPUTE + MONT_FIRSTITER(1) + MONT_FIRSTITER(2) + MONT_FIRSTITER(3) + MONT_FIRSTITER(4) + MONT_FINALIZE(4) + MONT_ITERFIRST(1) + MONT_ITERITER(1, 1) + MONT_ITERITER(1, 2) + MONT_ITERITER(1, 3) + MONT_ITERITER(1, 4) + MONT_FINALIZE(4) + MONT_ITERFIRST(2) + MONT_ITERITER(2, 1) + MONT_ITERITER(2, 2) + MONT_ITERITER(2, 3) + MONT_ITERITER(2, 4) + MONT_FINALIZE(4) + MONT_ITERFIRST(3) + MONT_ITERITER(3, 1) + MONT_ITERITER(3, 2) + MONT_ITERITER(3, 3) + MONT_ITERITER(3, 4) + MONT_FINALIZE(4) + MONT_ITERFIRST(4) + MONT_ITERITER(4, 1) + MONT_ITERITER(4, 2) + MONT_ITERITER(4, 3) + MONT_ITERITER(4, 4) + MONT_FINALIZE(4) + "/* check for overflow */ \n\t" + MONT_CMP(32) + MONT_CMP(24) + MONT_CMP(16) + MONT_CMP(8) + MONT_CMP(0) + + "/* subtract mod if overflow */ \n\t" + "subtract%=: \n\t" + MONT_FIRSTSUB + MONT_NEXTSUB(8) + MONT_NEXTSUB(16) + MONT_NEXTSUB(24) + MONT_NEXTSUB(32) + "done%=: \n\t" + : + : [tmp] "r" (tmp), [A] "r" (this->mont_repr.data), [B] "r" (other.data), [inv] "r" (inv), [M] "r" (modulus.data), + [T0] "r" (T0), [T1] "r" (T1), [cy] "r" (cy), [u] "r" (u) + : "cc", "memory", "%rax", "%rdx" + ); + mpn_copyi(this->mont_repr.data, tmp, n); + } + else +#endif + { + mp_limb_t res[2*n]; + mpn_mul_n(res, this->mont_repr.data, other.data, n); + + /* + The Montgomery reduction here is based on Algorithm 14.32 in + Handbook of Applied Cryptography + . + */ + for (size_t i = 0; i < n; ++i) + { + mp_limb_t k = inv * res[i]; + /* calculate res = res + k * mod * b^i */ + mp_limb_t carryout = mpn_addmul_1(res+i, modulus.data, n, k); + carryout = mpn_add_1(res+n+i, res+n+i, n-i, carryout); + assert(carryout == 0); + } + + if (mpn_cmp(res+n, modulus.data, n) >= 0) + { + const mp_limb_t borrow = mpn_sub(res+n, res+n, n, modulus.data, n); + assert(borrow == 0); + } + + mpn_copyi(this->mont_repr.data, res+n, n); + } +} + +template& modulus> +Fp_model::Fp_model(const bigint &b) +{ + mpn_copyi(this->mont_repr.data, Rsquared.data, n); + mul_reduce(b); +} + +template& modulus> +Fp_model::Fp_model(const long x, const bool is_unsigned) +{ + if (is_unsigned || x >= 0) + { + this->mont_repr.data[0] = x; + } + else + { + const mp_limb_t borrow = mpn_sub_1(this->mont_repr.data, modulus.data, n, -x); + assert(borrow == 0); + } + + mul_reduce(Rsquared); +} + +template& modulus> +void Fp_model::set_ulong(const unsigned long x) +{ + this->mont_repr.clear(); + this->mont_repr.data[0] = x; + mul_reduce(Rsquared); +} + +template& modulus> +void Fp_model::clear() +{ + this->mont_repr.clear(); +} + +template& modulus> +bigint Fp_model::as_bigint() const +{ + bigint one; + one.clear(); + one.data[0] = 1; + + Fp_model res(*this); + res.mul_reduce(one); + + return (res.mont_repr); +} + +template& modulus> +unsigned long Fp_model::as_ulong() const +{ + return this->as_bigint().as_ulong(); +} + +template& modulus> +bool Fp_model::operator==(const Fp_model& other) const +{ + return (this->mont_repr == other.mont_repr); +} + +template& modulus> +bool Fp_model::operator!=(const Fp_model& other) const +{ + return (this->mont_repr != other.mont_repr); +} + +template& modulus> +bool Fp_model::is_zero() const +{ + return (this->mont_repr.is_zero()); // zero maps to zero +} + +template& modulus> +void Fp_model::print() const +{ + Fp_model tmp; + tmp.mont_repr.data[0] = 1; + tmp.mul_reduce(this->mont_repr); + + tmp.mont_repr.print(); +} + +template& modulus> +Fp_model Fp_model::zero() +{ + Fp_model res; + res.mont_repr.clear(); + return res; +} + +template& modulus> +Fp_model Fp_model::one() +{ + Fp_model res; + res.mont_repr.data[0] = 1; + res.mul_reduce(Rsquared); + return res; +} + +template& modulus> +Fp_model& Fp_model::operator+=(const Fp_model& other) +{ +#ifdef PROFILE_OP_COUNTS + this->add_cnt++; +#endif +#if defined(__x86_64__) && defined(USE_ASM) + if (n == 3) + { + __asm__ + ("/* perform bignum addition */ \n\t" + ADD_FIRSTADD + ADD_NEXTADD(8) + ADD_NEXTADD(16) + "/* if overflow: subtract */ \n\t" + "/* (tricky point: if A and B are in the range we do not need to do anything special for the possible carry flag) */ \n\t" + "jc subtract%= \n\t" + + "/* check for overflow */ \n\t" + ADD_CMP(16) + ADD_CMP(8) + ADD_CMP(0) + + "/* subtract mod if overflow */ \n\t" + "subtract%=: \n\t" + ADD_FIRSTSUB + ADD_NEXTSUB(8) + ADD_NEXTSUB(16) + "done%=: \n\t" + : + : [A] "r" (this->mont_repr.data), [B] "r" (other.mont_repr.data), [mod] "r" (modulus.data) + : "cc", "memory", "%rax"); + } + else if (n == 4) + { + __asm__ + ("/* perform bignum addition */ \n\t" + ADD_FIRSTADD + ADD_NEXTADD(8) + ADD_NEXTADD(16) + ADD_NEXTADD(24) + "/* if overflow: subtract */ \n\t" + "/* (tricky point: if A and B are in the range we do not need to do anything special for the possible carry flag) */ \n\t" + "jc subtract%= \n\t" + + "/* check for overflow */ \n\t" + ADD_CMP(24) + ADD_CMP(16) + ADD_CMP(8) + ADD_CMP(0) + + "/* subtract mod if overflow */ \n\t" + "subtract%=: \n\t" + ADD_FIRSTSUB + ADD_NEXTSUB(8) + ADD_NEXTSUB(16) + ADD_NEXTSUB(24) + "done%=: \n\t" + : + : [A] "r" (this->mont_repr.data), [B] "r" (other.mont_repr.data), [mod] "r" (modulus.data) + : "cc", "memory", "%rax"); + } + else if (n == 5) + { + __asm__ + ("/* perform bignum addition */ \n\t" + ADD_FIRSTADD + ADD_NEXTADD(8) + ADD_NEXTADD(16) + ADD_NEXTADD(24) + ADD_NEXTADD(32) + "/* if overflow: subtract */ \n\t" + "/* (tricky point: if A and B are in the range we do not need to do anything special for the possible carry flag) */ \n\t" + "jc subtract%= \n\t" + + "/* check for overflow */ \n\t" + ADD_CMP(32) + ADD_CMP(24) + ADD_CMP(16) + ADD_CMP(8) + ADD_CMP(0) + + "/* subtract mod if overflow */ \n\t" + "subtract%=: \n\t" + ADD_FIRSTSUB + ADD_NEXTSUB(8) + ADD_NEXTSUB(16) + ADD_NEXTSUB(24) + ADD_NEXTSUB(32) + "done%=: \n\t" + : + : [A] "r" (this->mont_repr.data), [B] "r" (other.mont_repr.data), [mod] "r" (modulus.data) + : "cc", "memory", "%rax"); + } + else +#endif + { + mp_limb_t scratch[n+1]; + const mp_limb_t carry = mpn_add_n(scratch, this->mont_repr.data, other.mont_repr.data, n); + scratch[n] = carry; + + if (carry || mpn_cmp(scratch, modulus.data, n) >= 0) + { + const mp_limb_t borrow = mpn_sub(scratch, scratch, n+1, modulus.data, n); + assert(borrow == 0); + } + + mpn_copyi(this->mont_repr.data, scratch, n); + } + + return *this; +} + +template& modulus> +Fp_model& Fp_model::operator-=(const Fp_model& other) +{ +#ifdef PROFILE_OP_COUNTS + this->sub_cnt++; +#endif +#if defined(__x86_64__) && defined(USE_ASM) + if (n == 3) + { + __asm__ + (SUB_FIRSTSUB + SUB_NEXTSUB(8) + SUB_NEXTSUB(16) + + "jnc done%=\n\t" + + SUB_FIRSTADD + SUB_NEXTADD(8) + SUB_NEXTADD(16) + + "done%=:\n\t" + : + : [A] "r" (this->mont_repr.data), [B] "r" (other.mont_repr.data), [mod] "r" (modulus.data) + : "cc", "memory", "%rax"); + } + else if (n == 4) + { + __asm__ + (SUB_FIRSTSUB + SUB_NEXTSUB(8) + SUB_NEXTSUB(16) + SUB_NEXTSUB(24) + + "jnc done%=\n\t" + + SUB_FIRSTADD + SUB_NEXTADD(8) + SUB_NEXTADD(16) + SUB_NEXTADD(24) + + "done%=:\n\t" + : + : [A] "r" (this->mont_repr.data), [B] "r" (other.mont_repr.data), [mod] "r" (modulus.data) + : "cc", "memory", "%rax"); + } + else if (n == 5) + { + __asm__ + (SUB_FIRSTSUB + SUB_NEXTSUB(8) + SUB_NEXTSUB(16) + SUB_NEXTSUB(24) + SUB_NEXTSUB(32) + + "jnc done%=\n\t" + + SUB_FIRSTADD + SUB_NEXTADD(8) + SUB_NEXTADD(16) + SUB_NEXTADD(24) + SUB_NEXTADD(32) + + "done%=:\n\t" + : + : [A] "r" (this->mont_repr.data), [B] "r" (other.mont_repr.data), [mod] "r" (modulus.data) + : "cc", "memory", "%rax"); + } + else +#endif + { + mp_limb_t scratch[n+1]; + if (mpn_cmp(this->mont_repr.data, other.mont_repr.data, n) < 0) + { + const mp_limb_t carry = mpn_add_n(scratch, this->mont_repr.data, modulus.data, n); + scratch[n] = carry; + } + else + { + mpn_copyi(scratch, this->mont_repr.data, n); + scratch[n] = 0; + } + + const mp_limb_t borrow = mpn_sub(scratch, scratch, n+1, other.mont_repr.data, n); + assert(borrow == 0); + + mpn_copyi(this->mont_repr.data, scratch, n); + } + return *this; +} + +template& modulus> +Fp_model& Fp_model::operator*=(const Fp_model& other) +{ +#ifdef PROFILE_OP_COUNTS + this->mul_cnt++; +#endif + + mul_reduce(other.mont_repr); + return *this; +} + +template& modulus> +Fp_model& Fp_model::operator^=(const unsigned long pow) +{ + (*this) = power >(*this, pow); + return (*this); +} + +template& modulus> +template +Fp_model& Fp_model::operator^=(const bigint &pow) +{ + (*this) = power, m>(*this, pow); + return (*this); +} + +template& modulus> +Fp_model Fp_model::operator+(const Fp_model& other) const +{ + Fp_model r(*this); + return (r += other); +} + +template& modulus> +Fp_model Fp_model::operator-(const Fp_model& other) const +{ + Fp_model r(*this); + return (r -= other); +} + +template& modulus> +Fp_model Fp_model::operator*(const Fp_model& other) const +{ + Fp_model r(*this); + return (r *= other); +} + +template& modulus> +Fp_model Fp_model::operator^(const unsigned long pow) const +{ + Fp_model r(*this); + return (r ^= pow); +} + +template& modulus> +template +Fp_model Fp_model::operator^(const bigint &pow) const +{ + Fp_model r(*this); + return (r ^= pow); +} + +template& modulus> +Fp_model Fp_model::operator-() const +{ +#ifdef PROFILE_OP_COUNTS + this->sub_cnt++; +#endif + + if (this->is_zero()) + { + return (*this); + } + else + { + Fp_model r; + mpn_sub_n(r.mont_repr.data, modulus.data, this->mont_repr.data, n); + return r; + } +} + +template& modulus> +Fp_model Fp_model::squared() const +{ +#ifdef PROFILE_OP_COUNTS + this->sqr_cnt++; + this->mul_cnt--; // zero out the upcoming mul +#endif + /* stupid pre-processor tricks; beware */ +#if defined(__x86_64__) && defined(USE_ASM) + if (n == 3) + { // use asm-optimized Comba squaring + mp_limb_t res[2*n]; + mp_limb_t c0, c1, c2; + COMBA_3_BY_3_SQR(c0, c1, c2, res, this->mont_repr.data); + + mp_limb_t k; + mp_limb_t tmp1, tmp2, tmp3; + REDUCE_6_LIMB_PRODUCT(k, tmp1, tmp2, tmp3, inv, res, modulus.data); + + /* subtract t > mod */ + __asm__ volatile + ("/* check for overflow */ \n\t" + MONT_CMP(16) + MONT_CMP(8) + MONT_CMP(0) + + "/* subtract mod if overflow */ \n\t" + "subtract%=: \n\t" + MONT_FIRSTSUB + MONT_NEXTSUB(8) + MONT_NEXTSUB(16) + "done%=: \n\t" + : + : [tmp] "r" (res+n), [M] "r" (modulus.data) + : "cc", "memory", "%rax"); + + Fp_model r; + mpn_copyi(r.mont_repr.data, res+n, n); + return r; + } + else +#endif + { + Fp_model r(*this); + return (r *= r); + } +} + +template& modulus> +Fp_model& Fp_model::invert() +{ +#ifdef PROFILE_OP_COUNTS + this->inv_cnt++; +#endif + + assert(!this->is_zero()); + + bigint g; /* gp should have room for vn = n limbs */ + + mp_limb_t s[n+1]; /* sp should have room for vn+1 limbs */ + mp_size_t sn; + + bigint v = modulus; // both source operands are destroyed by mpn_gcdext + + /* computes gcd(u, v) = g = u*s + v*t, so s*u will be 1 (mod v) */ + const mp_size_t gn = mpn_gcdext(g.data, s, &sn, this->mont_repr.data, n, v.data, n); + assert(gn == 1 && g.data[0] == 1); /* inverse exists */ + + mp_limb_t q; /* division result fits into q, as sn <= n+1 */ + /* sn < 0 indicates negative sn; will fix up later */ + + if (std::abs(sn) >= n) + { + /* if sn could require modulus reduction, do it here */ + mpn_tdiv_qr(&q, this->mont_repr.data, 0, s, std::abs(sn), modulus.data, n); + } + else + { + /* otherwise just copy it over */ + mpn_zero(this->mont_repr.data, n); + mpn_copyi(this->mont_repr.data, s, std::abs(sn)); + } + + /* fix up the negative sn */ + if (sn < 0) + { + const mp_limb_t borrow = mpn_sub_n(this->mont_repr.data, modulus.data, this->mont_repr.data, n); + assert(borrow == 0); + } + + mul_reduce(Rcubed); + return *this; +} + +template& modulus> +Fp_model Fp_model::inverse() const +{ + Fp_model r(*this); + return (r.invert()); +} + +template& modulus> +Fp_model Fp_model::random_element() /// returns random element of Fp_model +{ + /* note that as Montgomery representation is a bijection then + selecting a random element of {xR} is the same as selecting a + random element of {x} */ + Fp_model r; + do + { + r.mont_repr.randomize(); + + /* clear all bits higher than MSB of modulus */ + size_t bitno = GMP_NUMB_BITS * n - 1; + while (modulus.test_bit(bitno) == false) + { + const std::size_t part = bitno/GMP_NUMB_BITS; + const std::size_t bit = bitno - (GMP_NUMB_BITS*part); + + r.mont_repr.data[part] &= ~(1ul<= modulus -- repeat (rejection sampling) */ + while (mpn_cmp(r.mont_repr.data, modulus.data, n) >= 0); + + return r; +} + +template& modulus> +Fp_model Fp_model::sqrt() const +{ + if (is_zero()) { + return *this; + } + + Fp_model one = Fp_model::one(); + + size_t v = Fp_model::s; + Fp_model z = Fp_model::nqr_to_t; + Fp_model w = (*this)^Fp_model::t_minus_1_over_2; + Fp_model x = (*this) * w; + Fp_model b = x * w; // b = (*this)^t + + + // check if square with euler's criterion + Fp_model check = b; + for (size_t i = 0; i < v-1; ++i) + { + check = check.squared(); + } + if (check != one) + { + assert_except(0); + } + + + // compute square root with Tonelli--Shanks + // (does not terminate if not a square!) + + while (b != one) + { + size_t m = 0; + Fp_model b2m = b; + while (b2m != one) + { + /* invariant: b2m = b^(2^m) after entering this loop */ + b2m = b2m.squared(); + m += 1; + } + + int j = v-m-1; + w = z; + while (j > 0) + { + w = w.squared(); + --j; + } // w = z^2^(v-m-1) + + z = w.squared(); + b = b * z; + x = x * w; + v = m; + } + + return x; +} + +template& modulus> +std::ostream& operator<<(std::ostream &out, const Fp_model &p) +{ +#ifndef MONTGOMERY_OUTPUT + Fp_model tmp; + tmp.mont_repr.data[0] = 1; + tmp.mul_reduce(p.mont_repr); + out << tmp.mont_repr; +#else + out << p.mont_repr; +#endif + return out; +} + +template& modulus> +std::istream& operator>>(std::istream &in, Fp_model &p) +{ +#ifndef MONTGOMERY_OUTPUT + in >> p.mont_repr; + p.mul_reduce(Fp_model::Rsquared); +#else + in >> p.mont_repr; +#endif + return in; +} + +} // libsnark +#endif // FP_TCC_ diff --git a/src/snark/libsnark/algebra/fields/fp12_2over3over2.hpp b/src/snark/libsnark/algebra/fields/fp12_2over3over2.hpp new file mode 100644 index 000000000..1de9d88b4 --- /dev/null +++ b/src/snark/libsnark/algebra/fields/fp12_2over3over2.hpp @@ -0,0 +1,116 @@ +/** @file + ***************************************************************************** + Declaration of arithmetic in the finite field F[((p^2)^3)^2]. + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef FP12_2OVER3OVER2_HPP_ +#define FP12_2OVER3OVER2_HPP_ +#include "algebra/fields/fp.hpp" +#include "algebra/fields/fp2.hpp" +#include "algebra/fields/fp6_3over2.hpp" +#include + +namespace libsnark { + +template& modulus> +class Fp12_2over3over2_model; + +template& modulus> +std::ostream& operator<<(std::ostream &, const Fp12_2over3over2_model &); + +template& modulus> +std::istream& operator>>(std::istream &, Fp12_2over3over2_model &); + +/** + * Arithmetic in the finite field F[((p^2)^3)^2]. + * + * Let p := modulus. This interface provides arithmetic for the extension field + * Fp12 = Fp6[W]/(W^2-V) where Fp6 = Fp2[V]/(V^3-non_residue) and non_residue is in Fp2 + * + * ASSUMPTION: p = 1 (mod 6) + */ +template& modulus> +class Fp12_2over3over2_model { +public: + typedef Fp_model my_Fp; + typedef Fp2_model my_Fp2; + typedef Fp6_3over2_model my_Fp6; + + static Fp2_model non_residue; + static Fp2_model Frobenius_coeffs_c1[12]; // non_residue^((modulus^i-1)/6) for i=0,...,11 + + my_Fp6 c0, c1; + Fp12_2over3over2_model() {}; + Fp12_2over3over2_model(const my_Fp6& c0, const my_Fp6& c1) : c0(c0), c1(c1) {}; + + void clear() { c0.clear(); c1.clear(); } + void print() const { printf("c0/c1:\n"); c0.print(); c1.print(); } + + static Fp12_2over3over2_model zero(); + static Fp12_2over3over2_model one(); + static Fp12_2over3over2_model random_element(); + + bool is_zero() const { return c0.is_zero() && c1.is_zero(); } + bool operator==(const Fp12_2over3over2_model &other) const; + bool operator!=(const Fp12_2over3over2_model &other) const; + + Fp12_2over3over2_model operator+(const Fp12_2over3over2_model &other) const; + Fp12_2over3over2_model operator-(const Fp12_2over3over2_model &other) const; + Fp12_2over3over2_model operator*(const Fp12_2over3over2_model &other) const; + Fp12_2over3over2_model operator-() const; + Fp12_2over3over2_model squared() const; // default is squared_complex + Fp12_2over3over2_model squared_karatsuba() const; + Fp12_2over3over2_model squared_complex() const; + Fp12_2over3over2_model inverse() const; + Fp12_2over3over2_model Frobenius_map(unsigned long power) const; + Fp12_2over3over2_model unitary_inverse() const; + Fp12_2over3over2_model cyclotomic_squared() const; + + Fp12_2over3over2_model mul_by_024(const my_Fp2 &ell_0, const my_Fp2 &ell_VW, const my_Fp2 &ell_VV) const; + + static my_Fp6 mul_by_non_residue(const my_Fp6 &elt); + + template + Fp12_2over3over2_model cyclotomic_exp(const bigint &exponent) const; + + static bigint base_field_char() { return modulus; } + static size_t extension_degree() { return 12; } + + friend std::ostream& operator<< (std::ostream &out, const Fp12_2over3over2_model &el); + friend std::istream& operator>> (std::istream &in, Fp12_2over3over2_model &el); +}; + +template& modulus> +std::ostream& operator<<(std::ostream& out, const std::vector > &v); + +template& modulus> +std::istream& operator>>(std::istream& in, std::vector > &v); + +template& modulus> +Fp12_2over3over2_model operator*(const Fp_model &lhs, const Fp12_2over3over2_model &rhs); + +template& modulus> +Fp12_2over3over2_model operator*(const Fp2_model &lhs, const Fp12_2over3over2_model &rhs); + +template& modulus> +Fp12_2over3over2_model operator*(const Fp6_3over2_model &lhs, const Fp12_2over3over2_model &rhs); + +template& modulus, mp_size_t m> +Fp12_2over3over2_model operator^(const Fp12_2over3over2_model &self, const bigint &exponent); + +template& modulus, mp_size_t m, const bigint& exp_modulus> +Fp12_2over3over2_model operator^(const Fp12_2over3over2_model &self, const Fp_model &exponent); + +template& modulus> +Fp2_model Fp12_2over3over2_model::non_residue; + +template& modulus> +Fp2_model Fp12_2over3over2_model::Frobenius_coeffs_c1[12]; + +} // libsnark +#include "algebra/fields/fp12_2over3over2.tcc" +#endif // FP12_2OVER3OVER2_HPP_ diff --git a/src/snark/libsnark/algebra/fields/fp12_2over3over2.tcc b/src/snark/libsnark/algebra/fields/fp12_2over3over2.tcc new file mode 100644 index 000000000..2fbc0b649 --- /dev/null +++ b/src/snark/libsnark/algebra/fields/fp12_2over3over2.tcc @@ -0,0 +1,412 @@ +/** @file + ***************************************************************************** + Implementation of arithmetic in the finite field F[((p^2)^3)^2]. + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef FP12_2OVER3OVER2_TCC_ +#define FP12_2OVER3OVER2_TCC_ + +namespace libsnark { + +template& modulus> +Fp6_3over2_model Fp12_2over3over2_model::mul_by_non_residue(const Fp6_3over2_model &elt) +{ + return Fp6_3over2_model(non_residue * elt.c2, elt.c0, elt.c1); +} + +template& modulus> +Fp12_2over3over2_model Fp12_2over3over2_model::zero() +{ + return Fp12_2over3over2_model(my_Fp6::zero(), my_Fp6::zero()); +} + +template& modulus> +Fp12_2over3over2_model Fp12_2over3over2_model::one() +{ + return Fp12_2over3over2_model(my_Fp6::one(), my_Fp6::zero()); +} + +template& modulus> +Fp12_2over3over2_model Fp12_2over3over2_model::random_element() +{ + Fp12_2over3over2_model r; + r.c0 = my_Fp6::random_element(); + r.c1 = my_Fp6::random_element(); + + return r; +} + +template& modulus> +bool Fp12_2over3over2_model::operator==(const Fp12_2over3over2_model &other) const +{ + return (this->c0 == other.c0 && this->c1 == other.c1); +} + +template& modulus> +bool Fp12_2over3over2_model::operator!=(const Fp12_2over3over2_model &other) const +{ + return !(operator==(other)); +} + +template& modulus> +Fp12_2over3over2_model Fp12_2over3over2_model::operator+(const Fp12_2over3over2_model &other) const +{ + return Fp12_2over3over2_model(this->c0 + other.c0, + this->c1 + other.c1); +} + +template& modulus> +Fp12_2over3over2_model Fp12_2over3over2_model::operator-(const Fp12_2over3over2_model &other) const +{ + return Fp12_2over3over2_model(this->c0 - other.c0, + this->c1 - other.c1); +} + +template& modulus> +Fp12_2over3over2_model operator*(const Fp_model &lhs, const Fp12_2over3over2_model &rhs) +{ + return Fp12_2over3over2_model(lhs*rhs.c0, + lhs*rhs.c1); +} + +template& modulus> +Fp12_2over3over2_model operator*(const Fp2_model &lhs, const Fp12_2over3over2_model &rhs) +{ + return Fp12_2over3over2_model(lhs*rhs.c0, + lhs*rhs.c1); +} + +template& modulus> +Fp12_2over3over2_model operator*(const Fp6_3over2_model &lhs, const Fp12_2over3over2_model &rhs) +{ + return Fp12_2over3over2_model(lhs*rhs.c0, + lhs*rhs.c1); +} + +template& modulus> +Fp12_2over3over2_model Fp12_2over3over2_model::operator*(const Fp12_2over3over2_model &other) const +{ + /* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 3 (Karatsuba) */ + + const my_Fp6 &A = other.c0, &B = other.c1, + &a = this->c0, &b = this->c1; + const my_Fp6 aA = a * A; + const my_Fp6 bB = b * B; + + return Fp12_2over3over2_model(aA + Fp12_2over3over2_model::mul_by_non_residue(bB), + (a + b)*(A+B) - aA - bB); +} + +template& modulus> +Fp12_2over3over2_model Fp12_2over3over2_model::operator-() const +{ + return Fp12_2over3over2_model(-this->c0, + -this->c1); +} + +template& modulus> +Fp12_2over3over2_model Fp12_2over3over2_model::squared() const +{ + return squared_complex(); +} + +template& modulus> +Fp12_2over3over2_model Fp12_2over3over2_model::squared_karatsuba() const +{ + /* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 3 (Karatsuba squaring) */ + + const my_Fp6 &a = this->c0, &b = this->c1; + const my_Fp6 asq = a.squared(); + const my_Fp6 bsq = b.squared(); + + return Fp12_2over3over2_model(asq + Fp12_2over3over2_model::mul_by_non_residue(bsq), + (a + b).squared() - asq - bsq); +} + +template& modulus> +Fp12_2over3over2_model Fp12_2over3over2_model::squared_complex() const +{ + /* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 3 (Complex squaring) */ + + const my_Fp6 &a = this->c0, &b = this->c1; + const my_Fp6 ab = a * b; + + return Fp12_2over3over2_model((a + b) * (a + Fp12_2over3over2_model::mul_by_non_residue(b)) - ab - Fp12_2over3over2_model::mul_by_non_residue(ab), + ab + ab); +} + +template& modulus> +Fp12_2over3over2_model Fp12_2over3over2_model::inverse() const +{ + /* From "High-Speed Software Implementation of the Optimal Ate Pairing over Barreto-Naehrig Curves"; Algorithm 8 */ + + const my_Fp6 &a = this->c0, &b = this->c1; + const my_Fp6 t0 = a.squared(); + const my_Fp6 t1 = b.squared(); + const my_Fp6 t2 = t0 - Fp12_2over3over2_model::mul_by_non_residue(t1); + const my_Fp6 t3 = t2.inverse(); + const my_Fp6 c0 = a * t3; + const my_Fp6 c1 = - (b * t3); + + return Fp12_2over3over2_model(c0, c1); +} + +template& modulus> +Fp12_2over3over2_model Fp12_2over3over2_model::Frobenius_map(unsigned long power) const +{ + return Fp12_2over3over2_model(c0.Frobenius_map(power), + Frobenius_coeffs_c1[power % 12] * c1.Frobenius_map(power)); +} + +template& modulus> +Fp12_2over3over2_model Fp12_2over3over2_model::unitary_inverse() const +{ + return Fp12_2over3over2_model(this->c0, + -this->c1); +} + +template& modulus> +Fp12_2over3over2_model Fp12_2over3over2_model::cyclotomic_squared() const +{ + /* OLD: naive implementation + return (*this).squared(); + */ + my_Fp2 z0 = this->c0.c0; + my_Fp2 z4 = this->c0.c1; + my_Fp2 z3 = this->c0.c2; + my_Fp2 z2 = this->c1.c0; + my_Fp2 z1 = this->c1.c1; + my_Fp2 z5 = this->c1.c2; + + my_Fp2 t0, t1, t2, t3, t4, t5, tmp; + + // t0 + t1*y = (z0 + z1*y)^2 = a^2 + tmp = z0 * z1; + t0 = (z0 + z1) * (z0 + my_Fp6::non_residue * z1) - tmp - my_Fp6::non_residue * tmp; + t1 = tmp + tmp; + // t2 + t3*y = (z2 + z3*y)^2 = b^2 + tmp = z2 * z3; + t2 = (z2 + z3) * (z2 + my_Fp6::non_residue * z3) - tmp - my_Fp6::non_residue * tmp; + t3 = tmp + tmp; + // t4 + t5*y = (z4 + z5*y)^2 = c^2 + tmp = z4 * z5; + t4 = (z4 + z5) * (z4 + my_Fp6::non_residue * z5) - tmp - my_Fp6::non_residue * tmp; + t5 = tmp + tmp; + + // for A + + // z0 = 3 * t0 - 2 * z0 + z0 = t0 - z0; + z0 = z0 + z0; + z0 = z0 + t0; + // z1 = 3 * t1 + 2 * z1 + z1 = t1 + z1; + z1 = z1 + z1; + z1 = z1 + t1; + + // for B + + // z2 = 3 * (xi * t5) + 2 * z2 + tmp = my_Fp6::non_residue * t5; + z2 = tmp + z2; + z2 = z2 + z2; + z2 = z2 + tmp; + + // z3 = 3 * t4 - 2 * z3 + z3 = t4 - z3; + z3 = z3 + z3; + z3 = z3 + t4; + + // for C + + // z4 = 3 * t2 - 2 * z4 + z4 = t2 - z4; + z4 = z4 + z4; + z4 = z4 + t2; + + // z5 = 3 * t3 + 2 * z5 + z5 = t3 + z5; + z5 = z5 + z5; + z5 = z5 + t3; + + return Fp12_2over3over2_model(my_Fp6(z0,z4,z3),my_Fp6(z2,z1,z5)); +} + +template& modulus> +Fp12_2over3over2_model Fp12_2over3over2_model::mul_by_024(const Fp2_model &ell_0, + const Fp2_model &ell_VW, + const Fp2_model &ell_VV) const +{ + /* OLD: naive implementation + Fp12_2over3over2_model a(my_Fp6(ell_0, my_Fp2::zero(), ell_VV), + my_Fp6(my_Fp2::zero(), ell_VW, my_Fp2::zero())); + + return (*this) * a; + */ + my_Fp2 z0 = this->c0.c0; + my_Fp2 z1 = this->c0.c1; + my_Fp2 z2 = this->c0.c2; + my_Fp2 z3 = this->c1.c0; + my_Fp2 z4 = this->c1.c1; + my_Fp2 z5 = this->c1.c2; + + my_Fp2 x0 = ell_0; + my_Fp2 x2 = ell_VV; + my_Fp2 x4 = ell_VW; + + my_Fp2 t0, t1, t2, s0, T3, T4, D0, D2, D4, S1; + + D0 = z0 * x0; + D2 = z2 * x2; + D4 = z4 * x4; + t2 = z0 + z4; + t1 = z0 + z2; + s0 = z1 + z3 + z5; + + // For z.a_.a_ = z0. + S1 = z1 * x2; + T3 = S1 + D4; + T4 = my_Fp6::non_residue * T3 + D0; + z0 = T4; + + // For z.a_.b_ = z1 + T3 = z5 * x4; + S1 = S1 + T3; + T3 = T3 + D2; + T4 = my_Fp6::non_residue * T3; + T3 = z1 * x0; + S1 = S1 + T3; + T4 = T4 + T3; + z1 = T4; + + // For z.a_.c_ = z2 + t0 = x0 + x2; + T3 = t1 * t0 - D0 - D2; + T4 = z3 * x4; + S1 = S1 + T4; + T3 = T3 + T4; + + // For z.b_.a_ = z3 (z3 needs z2) + t0 = z2 + z4; + z2 = T3; + t1 = x2 + x4; + T3 = t0 * t1 - D2 - D4; + T4 = my_Fp6::non_residue * T3; + T3 = z3 * x0; + S1 = S1 + T3; + T4 = T4 + T3; + z3 = T4; + + // For z.b_.b_ = z4 + T3 = z5 * x2; + S1 = S1 + T3; + T4 = my_Fp6::non_residue * T3; + t0 = x0 + x4; + T3 = t2 * t0 - D0 - D4; + T4 = T4 + T3; + z4 = T4; + + // For z.b_.c_ = z5. + t0 = x0 + x2 + x4; + T3 = s0 * t0 - S1; + z5 = T3; + + return Fp12_2over3over2_model(my_Fp6(z0,z1,z2),my_Fp6(z3,z4,z5)); + +} + +template& modulus, mp_size_t m> +Fp12_2over3over2_model operator^(const Fp12_2over3over2_model &self, const bigint &exponent) +{ + return power >(self, exponent); +} + +template& modulus, mp_size_t m, const bigint& exp_modulus> +Fp12_2over3over2_model operator^(const Fp12_2over3over2_model &self, const Fp_model &exponent) +{ + return self^(exponent.as_bigint()); +} + + +template& modulus> +template +Fp12_2over3over2_model Fp12_2over3over2_model::cyclotomic_exp(const bigint &exponent) const +{ + Fp12_2over3over2_model res = Fp12_2over3over2_model::one(); + + bool found_one = false; + for (long i = m-1; i >= 0; --i) + { + for (long j = GMP_NUMB_BITS - 1; j >= 0; --j) + { + if (found_one) + { + res = res.cyclotomic_squared(); + } + + if (exponent.data[i] & (1ul<& modulus> +std::ostream& operator<<(std::ostream &out, const Fp12_2over3over2_model &el) +{ + out << el.c0 << OUTPUT_SEPARATOR << el.c1; + return out; +} + +template& modulus> +std::istream& operator>>(std::istream &in, Fp12_2over3over2_model &el) +{ + in >> el.c0 >> el.c1; + return in; +} + +template& modulus> +std::ostream& operator<<(std::ostream& out, const std::vector > &v) +{ + out << v.size() << "\n"; + for (const Fp12_2over3over2_model& t : v) + { + out << t << OUTPUT_NEWLINE; + } + + return out; +} + +template& modulus> +std::istream& operator>>(std::istream& in, std::vector > &v) +{ + v.clear(); + + size_t s; + in >> s; + + char b; + in.read(&b, 1); + + v.reserve(s); + + for (size_t i = 0; i < s; ++i) + { + Fp12_2over3over2_model el; + in >> el; + v.emplace_back(el); + } + + return in; +} + +} // libsnark +#endif // FP12_2OVER3OVER2_TCC_ diff --git a/src/snark/libsnark/algebra/fields/fp2.hpp b/src/snark/libsnark/algebra/fields/fp2.hpp new file mode 100644 index 000000000..f07726918 --- /dev/null +++ b/src/snark/libsnark/algebra/fields/fp2.hpp @@ -0,0 +1,120 @@ +/** @file + ***************************************************************************** + Implementation of arithmetic in the finite field F[p^2]. + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef FP2_HPP_ +#define FP2_HPP_ +#include "algebra/fields/fp.hpp" +#include + +namespace libsnark { + +template& modulus> +class Fp2_model; + +template& modulus> +std::ostream& operator<<(std::ostream &, const Fp2_model &); + +template& modulus> +std::istream& operator>>(std::istream &, Fp2_model &); + +/** + * Arithmetic in the field F[p^3]. + * + * Let p := modulus. This interface provides arithmetic for the extension field + * Fp2 = Fp[U]/(U^2-non_residue), where non_residue is in Fp. + * + * ASSUMPTION: p = 1 (mod 6) + */ +template& modulus> +class Fp2_model { +public: + typedef Fp_model my_Fp; + + static bigint<2*n> euler; // (modulus^2-1)/2 + static size_t s; // modulus^2 = 2^s * t + 1 + static bigint<2*n> t; // with t odd + static bigint<2*n> t_minus_1_over_2; // (t-1)/2 + static my_Fp non_residue; // X^4-non_residue irreducible over Fp; used for constructing Fp2 = Fp[X] / (X^2 - non_residue) + static Fp2_model nqr; // a quadratic nonresidue in Fp2 + static Fp2_model nqr_to_t; // nqr^t + static my_Fp Frobenius_coeffs_c1[2]; // non_residue^((modulus^i-1)/2) for i=0,1 + + my_Fp c0, c1; + Fp2_model() {}; + Fp2_model(const my_Fp& c0, const my_Fp& c1) : c0(c0), c1(c1) {}; + + void clear() { c0.clear(); c1.clear(); } + void print() const { printf("c0/c1:\n"); c0.print(); c1.print(); } + + static Fp2_model zero(); + static Fp2_model one(); + static Fp2_model random_element(); + + bool is_zero() const { return c0.is_zero() && c1.is_zero(); } + bool operator==(const Fp2_model &other) const; + bool operator!=(const Fp2_model &other) const; + + Fp2_model operator+(const Fp2_model &other) const; + Fp2_model operator-(const Fp2_model &other) const; + Fp2_model operator*(const Fp2_model &other) const; + Fp2_model operator-() const; + Fp2_model squared() const; // default is squared_complex + Fp2_model inverse() const; + Fp2_model Frobenius_map(unsigned long power) const; + Fp2_model sqrt() const; // HAS TO BE A SQUARE (else does not terminate) + Fp2_model squared_karatsuba() const; + Fp2_model squared_complex() const; + + template + Fp2_model operator^(const bigint &other) const; + + static size_t size_in_bits() { return 2*my_Fp::size_in_bits(); } + static bigint base_field_char() { return modulus; } + + friend std::ostream& operator<< (std::ostream &out, const Fp2_model &el); + friend std::istream& operator>> (std::istream &in, Fp2_model &el); +}; + +template& modulus> +std::ostream& operator<<(std::ostream& out, const std::vector > &v); + +template& modulus> +std::istream& operator>>(std::istream& in, std::vector > &v); + +template& modulus> +Fp2_model operator*(const Fp_model &lhs, const Fp2_model &rhs); + +template& modulus> +bigint<2*n> Fp2_model::euler; + +template& modulus> +size_t Fp2_model::s; + +template& modulus> +bigint<2*n> Fp2_model::t; + +template& modulus> +bigint<2*n> Fp2_model::t_minus_1_over_2; + +template& modulus> +Fp_model Fp2_model::non_residue; + +template& modulus> +Fp2_model Fp2_model::nqr; + +template& modulus> +Fp2_model Fp2_model::nqr_to_t; + +template& modulus> +Fp_model Fp2_model::Frobenius_coeffs_c1[2]; + +} // libsnark +#include "algebra/fields/fp2.tcc" + +#endif // FP2_HPP_ diff --git a/src/snark/libsnark/algebra/fields/fp2.tcc b/src/snark/libsnark/algebra/fields/fp2.tcc new file mode 100644 index 000000000..1632a04c7 --- /dev/null +++ b/src/snark/libsnark/algebra/fields/fp2.tcc @@ -0,0 +1,261 @@ +/** @file + ***************************************************************************** + Implementation of arithmetic in the finite field F[p^2]. + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef FP2_TCC_ +#define FP2_TCC_ + +#include "algebra/fields/field_utils.hpp" + +namespace libsnark { + +template& modulus> +Fp2_model Fp2_model::zero() +{ + return Fp2_model(my_Fp::zero(), my_Fp::zero()); +} + +template& modulus> +Fp2_model Fp2_model::one() +{ + return Fp2_model(my_Fp::one(), my_Fp::zero()); +} + +template& modulus> +Fp2_model Fp2_model::random_element() +{ + Fp2_model r; + r.c0 = my_Fp::random_element(); + r.c1 = my_Fp::random_element(); + + return r; +} + +template& modulus> +bool Fp2_model::operator==(const Fp2_model &other) const +{ + return (this->c0 == other.c0 && this->c1 == other.c1); +} + +template& modulus> +bool Fp2_model::operator!=(const Fp2_model &other) const +{ + return !(operator==(other)); +} + +template& modulus> +Fp2_model Fp2_model::operator+(const Fp2_model &other) const +{ + return Fp2_model(this->c0 + other.c0, + this->c1 + other.c1); +} + +template& modulus> +Fp2_model Fp2_model::operator-(const Fp2_model &other) const +{ + return Fp2_model(this->c0 - other.c0, + this->c1 - other.c1); +} + +template& modulus> +Fp2_model operator*(const Fp_model &lhs, const Fp2_model &rhs) +{ + return Fp2_model(lhs*rhs.c0, + lhs*rhs.c1); +} + +template& modulus> +Fp2_model Fp2_model::operator*(const Fp2_model &other) const +{ + /* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 3 (Karatsuba) */ + const my_Fp + &A = other.c0, &B = other.c1, + &a = this->c0, &b = this->c1; + const my_Fp aA = a * A; + const my_Fp bB = b * B; + + return Fp2_model(aA + non_residue * bB, + (a + b)*(A+B) - aA - bB); +} + +template& modulus> +Fp2_model Fp2_model::operator-() const +{ + return Fp2_model(-this->c0, + -this->c1); +} + +template& modulus> +Fp2_model Fp2_model::squared() const +{ + return squared_complex(); +} + +template& modulus> +Fp2_model Fp2_model::squared_karatsuba() const +{ + /* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 3 (Karatsuba squaring) */ + const my_Fp &a = this->c0, &b = this->c1; + const my_Fp asq = a.squared(); + const my_Fp bsq = b.squared(); + + return Fp2_model(asq + non_residue * bsq, + (a + b).squared() - asq - bsq); +} + +template& modulus> +Fp2_model Fp2_model::squared_complex() const +{ + /* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 3 (Complex squaring) */ + const my_Fp &a = this->c0, &b = this->c1; + const my_Fp ab = a * b; + + return Fp2_model((a + b) * (a + non_residue * b) - ab - non_residue * ab, + ab + ab); +} + +template& modulus> +Fp2_model Fp2_model::inverse() const +{ + const my_Fp &a = this->c0, &b = this->c1; + + /* From "High-Speed Software Implementation of the Optimal Ate Pairing over Barreto-Naehrig Curves"; Algorithm 8 */ + const my_Fp t0 = a.squared(); + const my_Fp t1 = b.squared(); + const my_Fp t2 = t0 - non_residue * t1; + const my_Fp t3 = t2.inverse(); + const my_Fp c0 = a * t3; + const my_Fp c1 = - (b * t3); + + return Fp2_model(c0, c1); +} + +template& modulus> +Fp2_model Fp2_model::Frobenius_map(unsigned long power) const +{ + return Fp2_model(c0, + Frobenius_coeffs_c1[power % 2] * c1); +} + +template& modulus> +Fp2_model Fp2_model::sqrt() const +{ + if (is_zero()) { + return *this; + } + + Fp2_model one = Fp2_model::one(); + + size_t v = Fp2_model::s; + Fp2_model z = Fp2_model::nqr_to_t; + Fp2_model w = (*this)^Fp2_model::t_minus_1_over_2; + Fp2_model x = (*this) * w; + Fp2_model b = x * w; // b = (*this)^t + + + // check if square with euler's criterion + Fp2_model check = b; + for (size_t i = 0; i < v-1; ++i) + { + check = check.squared(); + } + if (check != one) + { + assert_except(0); + } + + + // compute square root with Tonelli--Shanks + // (does not terminate if not a square!) + + while (b != one) + { + size_t m = 0; + Fp2_model b2m = b; + while (b2m != one) + { + /* invariant: b2m = b^(2^m) after entering this loop */ + b2m = b2m.squared(); + m += 1; + } + + int j = v-m-1; + w = z; + while (j > 0) + { + w = w.squared(); + --j; + } // w = z^2^(v-m-1) + + z = w.squared(); + b = b * z; + x = x * w; + v = m; + } + + return x; +} + +template& modulus> +template +Fp2_model Fp2_model::operator^(const bigint &pow) const +{ + return power, m>(*this, pow); +} + +template& modulus> +std::ostream& operator<<(std::ostream &out, const Fp2_model &el) +{ + out << el.c0 << OUTPUT_SEPARATOR << el.c1; + return out; +} + +template& modulus> +std::istream& operator>>(std::istream &in, Fp2_model &el) +{ + in >> el.c0 >> el.c1; + return in; +} + +template& modulus> +std::ostream& operator<<(std::ostream& out, const std::vector > &v) +{ + out << v.size() << "\n"; + for (const Fp2_model& t : v) + { + out << t << OUTPUT_NEWLINE; + } + + return out; +} + +template& modulus> +std::istream& operator>>(std::istream& in, std::vector > &v) +{ + v.clear(); + + size_t s; + in >> s; + + char b; + in.read(&b, 1); + + v.reserve(s); + + for (size_t i = 0; i < s; ++i) + { + Fp2_model el; + in >> el; + v.emplace_back(el); + } + + return in; +} + +} // libsnark +#endif // FP2_TCC_ diff --git a/src/snark/libsnark/algebra/fields/fp6_3over2.hpp b/src/snark/libsnark/algebra/fields/fp6_3over2.hpp new file mode 100644 index 000000000..335d61c53 --- /dev/null +++ b/src/snark/libsnark/algebra/fields/fp6_3over2.hpp @@ -0,0 +1,104 @@ +/** @file + ***************************************************************************** + Declaration of arithmetic in the finite field F[(p^2)^3] + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef FP6_3OVER2_HPP_ +#define FP6_3OVER2_HPP_ +#include "algebra/fields/fp.hpp" +#include "algebra/fields/fp2.hpp" +#include + +namespace libsnark { + +template& modulus> +class Fp6_3over2_model; + +template& modulus> +std::ostream& operator<<(std::ostream &, const Fp6_3over2_model &); + +template& modulus> +std::istream& operator>>(std::istream &, Fp6_3over2_model &); + +/** + * Arithmetic in the finite field F[(p^2)^3]. + * + * Let p := modulus. This interface provides arithmetic for the extension field + * Fp6 = Fp2[V]/(V^3-non_residue) where non_residue is in Fp. + * + * ASSUMPTION: p = 1 (mod 6) + */ +template& modulus> +class Fp6_3over2_model { +public: + typedef Fp_model my_Fp; + typedef Fp2_model my_Fp2; + + static my_Fp2 non_residue; + static my_Fp2 Frobenius_coeffs_c1[6]; // non_residue^((modulus^i-1)/3) for i=0,1,2,3,4,5 + static my_Fp2 Frobenius_coeffs_c2[6]; // non_residue^((2*modulus^i-2)/3) for i=0,1,2,3,4,5 + + my_Fp2 c0, c1, c2; + Fp6_3over2_model() {}; + Fp6_3over2_model(const my_Fp2& c0, const my_Fp2& c1, const my_Fp2& c2) : c0(c0), c1(c1), c2(c2) {}; + + void clear() { c0.clear(); c1.clear(); c2.clear(); } + void print() const { printf("c0/c1/c2:\n"); c0.print(); c1.print(); c2.print(); } + + static Fp6_3over2_model zero(); + static Fp6_3over2_model one(); + static Fp6_3over2_model random_element(); + + bool is_zero() const { return c0.is_zero() && c1.is_zero() && c2.is_zero(); } + bool operator==(const Fp6_3over2_model &other) const; + bool operator!=(const Fp6_3over2_model &other) const; + + Fp6_3over2_model operator+(const Fp6_3over2_model &other) const; + Fp6_3over2_model operator-(const Fp6_3over2_model &other) const; + Fp6_3over2_model operator*(const Fp6_3over2_model &other) const; + Fp6_3over2_model operator-() const; + Fp6_3over2_model squared() const; + Fp6_3over2_model inverse() const; + Fp6_3over2_model Frobenius_map(unsigned long power) const; + + static my_Fp2 mul_by_non_residue(const my_Fp2 &elt); + + template + Fp6_3over2_model operator^(const bigint &other) const; + + static bigint base_field_char() { return modulus; } + static size_t extension_degree() { return 6; } + + friend std::ostream& operator<< (std::ostream &out, const Fp6_3over2_model &el); + friend std::istream& operator>> (std::istream &in, Fp6_3over2_model &el); +}; + +template& modulus> +std::ostream& operator<<(std::ostream& out, const std::vector > &v); + +template& modulus> +std::istream& operator>>(std::istream& in, std::vector > &v); + +template& modulus> +Fp6_3over2_model operator*(const Fp_model &lhs, const Fp6_3over2_model &rhs); + +template& modulus> +Fp6_3over2_model operator*(const Fp2_model &lhs, const Fp6_3over2_model &rhs); + +template& modulus> +Fp2_model Fp6_3over2_model::non_residue; + +template& modulus> +Fp2_model Fp6_3over2_model::Frobenius_coeffs_c1[6]; + +template& modulus> +Fp2_model Fp6_3over2_model::Frobenius_coeffs_c2[6]; + +} // libsnark +#include "algebra/fields/fp6_3over2.tcc" + +#endif // FP6_3OVER2_HPP_ diff --git a/src/snark/libsnark/algebra/fields/fp6_3over2.tcc b/src/snark/libsnark/algebra/fields/fp6_3over2.tcc new file mode 100644 index 000000000..f4fffde04 --- /dev/null +++ b/src/snark/libsnark/algebra/fields/fp6_3over2.tcc @@ -0,0 +1,216 @@ +/** @file + ***************************************************************************** + Implementation of arithmetic in the finite field F[(p^2)^3]. + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef FP6_3OVER2_TCC_ +#define FP6_3OVER2_TCC_ +#include "algebra/fields/field_utils.hpp" + +namespace libsnark { + +template& modulus> +Fp2_model Fp6_3over2_model::mul_by_non_residue(const Fp2_model &elt) +{ + return Fp2_model(non_residue * elt); +} + +template& modulus> +Fp6_3over2_model Fp6_3over2_model::zero() +{ + return Fp6_3over2_model(my_Fp2::zero(), my_Fp2::zero(), my_Fp2::zero()); +} + +template& modulus> +Fp6_3over2_model Fp6_3over2_model::one() +{ + return Fp6_3over2_model(my_Fp2::one(), my_Fp2::zero(), my_Fp2::zero()); +} + +template& modulus> +Fp6_3over2_model Fp6_3over2_model::random_element() +{ + Fp6_3over2_model r; + r.c0 = my_Fp2::random_element(); + r.c1 = my_Fp2::random_element(); + r.c2 = my_Fp2::random_element(); + + return r; +} + +template& modulus> +bool Fp6_3over2_model::operator==(const Fp6_3over2_model &other) const +{ + return (this->c0 == other.c0 && this->c1 == other.c1 && this->c2 == other.c2); +} + +template& modulus> +bool Fp6_3over2_model::operator!=(const Fp6_3over2_model &other) const +{ + return !(operator==(other)); +} + +template& modulus> +Fp6_3over2_model Fp6_3over2_model::operator+(const Fp6_3over2_model &other) const +{ + return Fp6_3over2_model(this->c0 + other.c0, + this->c1 + other.c1, + this->c2 + other.c2); +} + +template& modulus> +Fp6_3over2_model Fp6_3over2_model::operator-(const Fp6_3over2_model &other) const +{ + return Fp6_3over2_model(this->c0 - other.c0, + this->c1 - other.c1, + this->c2 - other.c2); +} + +template& modulus> +Fp6_3over2_model operator*(const Fp_model &lhs, const Fp6_3over2_model &rhs) +{ + return Fp6_3over2_model(lhs*rhs.c0, + lhs*rhs.c1, + lhs*rhs.c2); +} + +template& modulus> +Fp6_3over2_model operator*(const Fp2_model &lhs, const Fp6_3over2_model &rhs) +{ + return Fp6_3over2_model(lhs*rhs.c0, + lhs*rhs.c1, + lhs*rhs.c2); +} + +template& modulus> +Fp6_3over2_model Fp6_3over2_model::operator*(const Fp6_3over2_model &other) const +{ + /* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 4 (Karatsuba) */ + + const my_Fp2 &A = other.c0, &B = other.c1, &C = other.c2, + &a = this->c0, &b = this->c1, &c = this->c2; + const my_Fp2 aA = a*A; + const my_Fp2 bB = b*B; + const my_Fp2 cC = c*C; + + return Fp6_3over2_model(aA + Fp6_3over2_model::mul_by_non_residue((b+c)*(B+C)-bB-cC), + (a+b)*(A+B)-aA-bB+Fp6_3over2_model::mul_by_non_residue(cC), + (a+c)*(A+C)-aA+bB-cC); +} + +template& modulus> +Fp6_3over2_model Fp6_3over2_model::operator-() const +{ + return Fp6_3over2_model(-this->c0, + -this->c1, + -this->c2); +} + +template& modulus> +Fp6_3over2_model Fp6_3over2_model::squared() const +{ + /* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 4 (CH-SQR2) */ + + const my_Fp2 &a = this->c0, &b = this->c1, &c = this->c2; + const my_Fp2 s0 = a.squared(); + const my_Fp2 ab = a*b; + const my_Fp2 s1 = ab + ab; + const my_Fp2 s2 = (a - b + c).squared(); + const my_Fp2 bc = b*c; + const my_Fp2 s3 = bc + bc; + const my_Fp2 s4 = c.squared(); + + return Fp6_3over2_model(s0 + Fp6_3over2_model::mul_by_non_residue(s3), + s1 + Fp6_3over2_model::mul_by_non_residue(s4), + s1 + s2 + s3 - s0 - s4); +} + +template& modulus> +Fp6_3over2_model Fp6_3over2_model::inverse() const +{ + /* From "High-Speed Software Implementation of the Optimal Ate Pairing over Barreto-Naehrig Curves"; Algorithm 17 */ + + const my_Fp2 &a = this->c0, &b = this->c1, &c = this->c2; + const my_Fp2 t0 = a.squared(); + const my_Fp2 t1 = b.squared(); + const my_Fp2 t2 = c.squared(); + const my_Fp2 t3 = a*b; + const my_Fp2 t4 = a*c; + const my_Fp2 t5 = b*c; + const my_Fp2 c0 = t0 - Fp6_3over2_model::mul_by_non_residue(t5); + const my_Fp2 c1 = Fp6_3over2_model::mul_by_non_residue(t2) - t3; + const my_Fp2 c2 = t1 - t4; // typo in paper referenced above. should be "-" as per Scott, but is "*" + const my_Fp2 t6 = (a * c0 + Fp6_3over2_model::mul_by_non_residue((c * c1 + b * c2))).inverse(); + return Fp6_3over2_model(t6 * c0, t6 * c1, t6 * c2); +} + +template& modulus> +Fp6_3over2_model Fp6_3over2_model::Frobenius_map(unsigned long power) const +{ + return Fp6_3over2_model(c0.Frobenius_map(power), + Frobenius_coeffs_c1[power % 6] * c1.Frobenius_map(power), + Frobenius_coeffs_c2[power % 6] * c2.Frobenius_map(power)); +} + +template& modulus> +template +Fp6_3over2_model Fp6_3over2_model::operator^(const bigint &pow) const +{ + return power, m>(*this, pow); +} + +template& modulus> +std::ostream& operator<<(std::ostream &out, const Fp6_3over2_model &el) +{ + out << el.c0 << OUTPUT_SEPARATOR << el.c1 << OUTPUT_SEPARATOR << el.c2; + return out; +} + +template& modulus> +std::istream& operator>>(std::istream &in, Fp6_3over2_model &el) +{ + in >> el.c0 >> el.c1 >> el.c2; + return in; +} + +template& modulus> +std::ostream& operator<<(std::ostream& out, const std::vector > &v) +{ + out << v.size() << "\n"; + for (const Fp6_3over2_model& t : v) + { + out << t << OUTPUT_NEWLINE; + } + + return out; +} + +template& modulus> +std::istream& operator>>(std::istream& in, std::vector > &v) +{ + v.clear(); + + size_t s; + in >> s; + + char b; + in.read(&b, 1); + + v.reserve(s); + + for (size_t i = 0; i < s; ++i) + { + Fp6_3over2_model el; + in >> el; + v.emplace_back(el); + } + + return in; +} + +} // libsnark +#endif // FP6_3_OVER_2_TCC_ diff --git a/src/snark/libsnark/algebra/fields/fp_aux.tcc b/src/snark/libsnark/algebra/fields/fp_aux.tcc new file mode 100644 index 000000000..7f8a3eadf --- /dev/null +++ b/src/snark/libsnark/algebra/fields/fp_aux.tcc @@ -0,0 +1,389 @@ +/** @file + ***************************************************************************** + Assembly code snippets for F[p] finite field arithmetic, used by fp.tcc . + Specific to x86-64, and used only if USE_ASM is defined. + On other architectures or without USE_ASM, fp.tcc uses a portable + C++ implementation instead. + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef FP_AUX_TCC_ +#define FP_AUX_TCC_ + +namespace libsnark { + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +/* addq is faster than adcq, even if preceded by clc */ +#define ADD_FIRSTADD \ + "movq (%[B]), %%rax \n\t" \ + "addq %%rax, (%[A]) \n\t" + +#define ADD_NEXTADD(ofs) \ + "movq " STR(ofs) "(%[B]), %%rax \n\t" \ + "adcq %%rax, " STR(ofs) "(%[A]) \n\t" + +#define ADD_CMP(ofs) \ + "movq " STR(ofs) "(%[mod]), %%rax \n\t" \ + "cmpq %%rax, " STR(ofs) "(%[A]) \n\t" \ + "jb done%= \n\t" \ + "ja subtract%= \n\t" + +#define ADD_FIRSTSUB \ + "movq (%[mod]), %%rax \n\t" \ + "subq %%rax, (%[A]) \n\t" + +#define ADD_FIRSTSUB \ + "movq (%[mod]), %%rax \n\t" \ + "subq %%rax, (%[A]) \n\t" + +#define ADD_NEXTSUB(ofs) \ + "movq " STR(ofs) "(%[mod]), %%rax \n\t" \ + "sbbq %%rax, " STR(ofs) "(%[A]) \n\t" + +#define SUB_FIRSTSUB \ + "movq (%[B]), %%rax\n\t" \ + "subq %%rax, (%[A])\n\t" + +#define SUB_NEXTSUB(ofs) \ + "movq " STR(ofs) "(%[B]), %%rax\n\t" \ + "sbbq %%rax, " STR(ofs) "(%[A])\n\t" + +#define SUB_FIRSTADD \ + "movq (%[mod]), %%rax\n\t" \ + "addq %%rax, (%[A])\n\t" + +#define SUB_NEXTADD(ofs) \ + "movq " STR(ofs) "(%[mod]), %%rax\n\t" \ + "adcq %%rax, " STR(ofs) "(%[A])\n\t" + +#define MONT_CMP(ofs) \ + "movq " STR(ofs) "(%[M]), %%rax \n\t" \ + "cmpq %%rax, " STR(ofs) "(%[tmp]) \n\t" \ + "jb done%= \n\t" \ + "ja subtract%= \n\t" + +#define MONT_FIRSTSUB \ + "movq (%[M]), %%rax \n\t" \ + "subq %%rax, (%[tmp]) \n\t" + +#define MONT_NEXTSUB(ofs) \ + "movq " STR(ofs) "(%[M]), %%rax \n\t" \ + "sbbq %%rax, " STR(ofs) "(%[tmp]) \n\t" + +/* + The x86-64 Montgomery multiplication here is similar + to Algorithm 2 (CIOS method) in http://eprint.iacr.org/2012/140.pdf + and the PowerPC pseudocode of gmp-ecm library (c) Paul Zimmermann and Alexander Kruppa + (see comments on top of powerpc64/mulredc.m4). +*/ + +#define MONT_PRECOMPUTE \ + "xorq %[cy], %[cy] \n\t" \ + "movq 0(%[A]), %%rax \n\t" \ + "mulq 0(%[B]) \n\t" \ + "movq %%rax, %[T0] \n\t" \ + "movq %%rdx, %[T1] # T1:T0 <- A[0] * B[0] \n\t" \ + "mulq %[inv] \n\t" \ + "movq %%rax, %[u] # u <- T0 * inv \n\t" \ + "mulq 0(%[M]) \n\t" \ + "addq %[T0], %%rax \n\t" \ + "adcq %%rdx, %[T1] \n\t" \ + "adcq $0, %[cy] # cy:T1 <- (M[0]*u + T1 * b + T0) / b\n\t" + +#define MONT_FIRSTITER(j) \ + "xorq %[T0], %[T0] \n\t" \ + "movq 0(%[A]), %%rax \n\t" \ + "mulq " STR((j*8)) "(%[B]) \n\t" \ + "addq %[T1], %%rax \n\t" \ + "movq %%rax, " STR(((j-1)*8)) "(%[tmp]) \n\t" \ + "adcq $0, %%rdx \n\t" \ + "movq %%rdx, %[T1] # now T1:tmp[j-1] <-- X[0] * Y[j] + T1\n\t" \ + "movq " STR((j*8)) "(%[M]), %%rax \n\t" \ + "mulq %[u] \n\t" \ + "addq %%rax, " STR(((j-1)*8)) "(%[tmp]) \n\t" \ + "adcq %[cy], %%rdx \n\t" \ + "adcq $0, %[T0] \n\t" \ + "xorq %[cy], %[cy] \n\t" \ + "addq %%rdx, %[T1] \n\t" \ + "adcq %[T0], %[cy] # cy:T1:tmp[j-1] <---- (X[0] * Y[j] + T1) + (M[j] * u + cy * b) \n\t" + +#define MONT_ITERFIRST(i) \ + "xorq %[cy], %[cy] \n\t" \ + "movq " STR((i*8)) "(%[A]), %%rax \n\t" \ + "mulq 0(%[B]) \n\t" \ + "addq 0(%[tmp]), %%rax \n\t" \ + "adcq 8(%[tmp]), %%rdx \n\t" \ + "adcq $0, %[cy] \n\t" \ + "movq %%rax, %[T0] \n\t" \ + "movq %%rdx, %[T1] # cy:T1:T0 <- A[i] * B[0] + tmp[1] * b + tmp[0]\n\t" \ + "mulq %[inv] \n\t" \ + "movq %%rax, %[u] # u <- T0 * inv\n\t" \ + "mulq 0(%[M]) \n\t" \ + "addq %[T0], %%rax \n\t" \ + "adcq %%rdx, %[T1] \n\t" \ + "adcq $0, %[cy] # cy:T1 <- (M[0]*u + cy * b * b + T1 * b + T0) / b\n\t" + +#define MONT_ITERITER(i, j) \ + "xorq %[T0], %[T0] \n\t" \ + "movq " STR((i*8)) "(%[A]), %%rax \n\t" \ + "mulq " STR((j*8)) "(%[B]) \n\t" \ + "addq %[T1], %%rax \n\t" \ + "movq %%rax, " STR(((j-1)*8)) "(%[tmp]) \n\t" \ + "adcq $0, %%rdx \n\t" \ + "movq %%rdx, %[T1] # now T1:tmp[j-1] <-- X[i] * Y[j] + T1 \n\t" \ + "movq " STR((j*8)) "(%[M]), %%rax \n\t" \ + "mulq %[u] \n\t" \ + "addq %%rax, " STR(((j-1)*8)) "(%[tmp]) \n\t" \ + "adcq %[cy], %%rdx \n\t" \ + "adcq $0, %[T0] \n\t" \ + "xorq %[cy], %[cy] \n\t" \ + "addq %%rdx, %[T1] \n\t" \ + "adcq %[T0], %[cy] # cy:T1:tmp[j-1] <-- (X[i] * Y[j] + T1) + M[j] * u + cy * b \n\t" \ + "addq " STR(((j+1)*8)) "(%[tmp]), %[T1] \n\t" \ + "adcq $0, %[cy] # cy:T1:tmp[j-1] <-- (X[i] * Y[j] + T1) + M[j] * u + (tmp[j+1] + cy) * b \n\t" + +#define MONT_FINALIZE(j) \ + "movq %[T1], " STR((j*8)) "(%[tmp]) \n\t" \ + "movq %[cy], " STR(((j+1)*8)) "(%[tmp]) \n\t" + +/* + Comba multiplication and squaring routines are based on the + public-domain tomsfastmath library by Tom St Denis + + + + Compared to the above, we save 5-20% of cycles by using careful register + renaming to implement Comba forward operation. + */ + +#define COMBA_3_BY_3_MUL(c0_, c1_, c2_, res_, A_, B_) \ + asm volatile ( \ + "movq 0(%[A]), %%rax \n\t" \ + "mulq 0(%[B]) \n\t" \ + "movq %%rax, 0(%[res]) \n\t" \ + "movq %%rdx, %[c0] \n\t" \ + \ + "xorq %[c1], %[c1] \n\t" \ + "movq 0(%[A]), %%rax \n\t" \ + "mulq 8(%[B]) \n\t" \ + "addq %%rax, %[c0] \n\t" \ + "adcq %%rdx, %[c1] \n\t" \ + \ + "xorq %[c2], %[c2] \n\t" \ + "movq 8(%[A]), %%rax \n\t" \ + "mulq 0(%[B]) \n\t" \ + "addq %%rax, %[c0] \n\t" \ + "movq %[c0], 8(%[res]) \n\t" \ + "adcq %%rdx, %[c1] \n\t" \ + "adcq $0, %[c2] \n\t" \ + \ + "// register renaming (c1, c2, c0)\n\t" \ + "xorq %[c0], %[c0] \n\t" \ + "movq 0(%[A]), %%rax \n\t" \ + "mulq 16(%[B]) \n\t" \ + "addq %%rax, %[c1] \n\t" \ + "adcq %%rdx, %[c2] \n\t" \ + "adcq $0, %[c0] \n\t" \ + \ + "movq 8(%[A]), %%rax \n\t" \ + "mulq 8(%[B]) \n\t" \ + "addq %%rax, %[c1] \n\t" \ + "adcq %%rdx, %[c2] \n\t" \ + "adcq $0, %[c0] \n\t" \ + \ + "movq 16(%[A]), %%rax \n\t" \ + "mulq 0(%[B]) \n\t" \ + "addq %%rax, %[c1] \n\t" \ + "movq %[c1], 16(%[res]) \n\t" \ + "adcq %%rdx, %[c2] \n\t" \ + "adcq $0, %[c0] \n\t" \ + \ + "// register renaming (c2, c0, c1)\n\t" \ + "xorq %[c1], %[c1] \n\t" \ + "movq 8(%[A]), %%rax \n\t" \ + "mulq 16(%[B]) \n\t" \ + "addq %%rax, %[c2] \n\t" \ + "adcq %%rdx, %[c0] \n\t" \ + "adcq $0, %[c1] \n\t" \ + \ + "movq 16(%[A]), %%rax \n\t" \ + "mulq 8(%[B]) \n\t" \ + "addq %%rax, %[c2] \n\t" \ + "movq %[c2], 24(%[res]) \n\t" \ + "adcq %%rdx, %[c0] \n\t" \ + "adcq $0, %[c1] \n\t" \ + \ + "// register renaming (c0, c1, c2)\n\t" \ + "xorq %[c2], %[c2] \n\t" \ + "movq 16(%[A]), %%rax \n\t" \ + "mulq 16(%[B]) \n\t" \ + "addq %%rax, %[c0] \n\t" \ + "movq %[c0], 32(%[res]) \n\t" \ + "adcq %%rdx, %[c1] \n\t" \ + "movq %[c1], 40(%[res]) \n\t" \ + : [c0] "=&r" (c0_), [c1] "=&r" (c1_), [c2] "=&r" (c2_) \ + : [res] "r" (res_), [A] "r" (A_), [B] "r" (B_) \ + : "%rax", "%rdx", "cc", "memory") + +#define COMBA_3_BY_3_SQR(c0_, c1_, c2_, res_, A_) \ + asm volatile ( \ + "xorq %[c1], %[c1] \n\t" \ + "xorq %[c2], %[c2] \n\t" \ + "movq 0(%[A]), %%rax \n\t" \ + "mulq %%rax \n\t" \ + "movq %%rax, 0(%[res]) \n\t" \ + "movq %%rdx, %[c0] \n\t" \ + \ + "movq 0(%[A]), %%rax \n\t" \ + "mulq 8(%[A]) \n\t" \ + "addq %%rax, %[c0] \n\t" \ + "adcq %%rdx, %[c1] \n\t" \ + "addq %%rax, %[c0] \n\t" \ + "movq %[c0], 8(%[res]) \n\t" \ + "adcq %%rdx, %[c1] \n\t" \ + "adcq $0, %[c2] \n\t" \ + \ + "// register renaming (c1, c2, c0)\n\t" \ + "movq 0(%[A]), %%rax \n\t" \ + "xorq %[c0], %[c0] \n\t" \ + "mulq 16(%[A]) \n\t" \ + "addq %%rax, %[c1] \n\t" \ + "adcq %%rdx, %[c2] \n\t" \ + "adcq $0, %[c0] \n\t" \ + "addq %%rax, %[c1] \n\t" \ + "adcq %%rdx, %[c2] \n\t" \ + "adcq $0, %[c0] \n\t" \ + \ + "movq 8(%[A]), %%rax \n\t" \ + "mulq %%rax \n\t" \ + "addq %%rax, %[c1] \n\t" \ + "movq %[c1], 16(%[res]) \n\t" \ + "adcq %%rdx, %[c2] \n\t" \ + "adcq $0, %[c0] \n\t" \ + \ + "// register renaming (c2, c0, c1)\n\t" \ + "movq 8(%[A]), %%rax \n\t" \ + "xorq %[c1], %[c1] \n\t" \ + "mulq 16(%[A]) \n\t" \ + "addq %%rax, %[c2] \n\t" \ + "adcq %%rdx, %[c0] \n\t" \ + "adcq $0, %[c1] \n\t" \ + "addq %%rax, %[c2] \n\t" \ + "movq %[c2], 24(%[res]) \n\t" \ + "adcq %%rdx, %[c0] \n\t" \ + "adcq $0, %[c1] \n\t" \ + \ + "// register renaming (c0, c1, c2)\n\t" \ + "movq 16(%[A]), %%rax \n\t" \ + "mulq %%rax \n\t" \ + "addq %%rax, %[c0] \n\t" \ + "movq %[c0], 32(%[res]) \n\t" \ + "adcq %%rdx, %[c1] \n\t" \ + "movq %[c1], 40(%[res]) \n\t" \ + \ + : [c0] "=&r" (c0_), [c1] "=&r" (c1_), [c2] "=&r" (c2_) \ + : [res] "r" (res_), [A] "r" (A_) \ + : "%rax", "%rdx", "cc", "memory") + +/* + The Montgomery reduction here is based on Algorithm 14.32 in + Handbook of Applied Cryptography + . + */ +#define REDUCE_6_LIMB_PRODUCT(k_, tmp1_, tmp2_, tmp3_, inv_, res_, mod_) \ + __asm__ volatile \ + ("///////////////////////////////////\n\t" \ + "movq 0(%[res]), %%rax \n\t" \ + "mulq %[modprime] \n\t" \ + "movq %%rax, %[k] \n\t" \ + \ + "movq (%[mod]), %%rax \n\t" \ + "mulq %[k] \n\t" \ + "movq %%rax, %[tmp1] \n\t" \ + "movq %%rdx, %[tmp2] \n\t" \ + \ + "xorq %[tmp3], %[tmp3] \n\t" \ + "movq 8(%[mod]), %%rax \n\t" \ + "mulq %[k] \n\t" \ + "addq %[tmp1], 0(%[res]) \n\t" \ + "adcq %%rax, %[tmp2] \n\t" \ + "adcq %%rdx, %[tmp3] \n\t" \ + \ + "xorq %[tmp1], %[tmp1] \n\t" \ + "movq 16(%[mod]), %%rax \n\t" \ + "mulq %[k] \n\t" \ + "addq %[tmp2], 8(%[res]) \n\t" \ + "adcq %%rax, %[tmp3] \n\t" \ + "adcq %%rdx, %[tmp1] \n\t" \ + \ + "addq %[tmp3], 16(%[res]) \n\t" \ + "adcq %[tmp1], 24(%[res]) \n\t" \ + "adcq $0, 32(%[res]) \n\t" \ + "adcq $0, 40(%[res]) \n\t" \ + \ + "///////////////////////////////////\n\t" \ + "movq 8(%[res]), %%rax \n\t" \ + "mulq %[modprime] \n\t" \ + "movq %%rax, %[k] \n\t" \ + \ + "movq (%[mod]), %%rax \n\t" \ + "mulq %[k] \n\t" \ + "movq %%rax, %[tmp1] \n\t" \ + "movq %%rdx, %[tmp2] \n\t" \ + \ + "xorq %[tmp3], %[tmp3] \n\t" \ + "movq 8(%[mod]), %%rax \n\t" \ + "mulq %[k] \n\t" \ + "addq %[tmp1], 8(%[res]) \n\t" \ + "adcq %%rax, %[tmp2] \n\t" \ + "adcq %%rdx, %[tmp3] \n\t" \ + \ + "xorq %[tmp1], %[tmp1] \n\t" \ + "movq 16(%[mod]), %%rax \n\t" \ + "mulq %[k] \n\t" \ + "addq %[tmp2], 16(%[res]) \n\t" \ + "adcq %%rax, %[tmp3] \n\t" \ + "adcq %%rdx, %[tmp1] \n\t" \ + \ + "addq %[tmp3], 24(%[res]) \n\t" \ + "adcq %[tmp1], 32(%[res]) \n\t" \ + "adcq $0, 40(%[res]) \n\t" \ + \ + "///////////////////////////////////\n\t" \ + "movq 16(%[res]), %%rax \n\t" \ + "mulq %[modprime] \n\t" \ + "movq %%rax, %[k] \n\t" \ + \ + "movq (%[mod]), %%rax \n\t" \ + "mulq %[k] \n\t" \ + "movq %%rax, %[tmp1] \n\t" \ + "movq %%rdx, %[tmp2] \n\t" \ + \ + "xorq %[tmp3], %[tmp3] \n\t" \ + "movq 8(%[mod]), %%rax \n\t" \ + "mulq %[k] \n\t" \ + "addq %[tmp1], 16(%[res]) \n\t" \ + "adcq %%rax, %[tmp2] \n\t" \ + "adcq %%rdx, %[tmp3] \n\t" \ + \ + "xorq %[tmp1], %[tmp1] \n\t" \ + "movq 16(%[mod]), %%rax \n\t" \ + "mulq %[k] \n\t" \ + "addq %[tmp2], 24(%[res]) \n\t" \ + "adcq %%rax, %[tmp3] \n\t" \ + "adcq %%rdx, %[tmp1] \n\t" \ + \ + "addq %[tmp3], 32(%[res]) \n\t" \ + "adcq %[tmp1], 40(%[res]) \n\t" \ + : [k] "=&r" (k_), [tmp1] "=&r" (tmp1_), [tmp2] "=&r" (tmp2_), [tmp3] "=&r" (tmp3_) \ + : [modprime] "r" (inv_), [res] "r" (res_), [mod] "r" (mod_) \ + : "%rax", "%rdx", "cc", "memory") + +} // libsnark +#endif // FP_AUX_TCC_ diff --git a/src/snark/libsnark/algebra/fields/tests/test_bigint.cpp b/src/snark/libsnark/algebra/fields/tests/test_bigint.cpp new file mode 100644 index 000000000..d2da59e73 --- /dev/null +++ b/src/snark/libsnark/algebra/fields/tests/test_bigint.cpp @@ -0,0 +1,97 @@ +/** + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include "algebra/fields/bigint.hpp" + +#include + +using namespace libsnark; + +TEST(algebra, bigint) +{ + static_assert(ULONG_MAX == 0xFFFFFFFFFFFFFFFFul, "unsigned long not 64-bit"); + static_assert(GMP_NUMB_BITS == 64, "GMP limb not 64-bit"); + + const char *b1_decimal = "76749407"; + const char *b2_decimal = "435020359732196472065729437602"; + const char *b3_decimal = "33387554642372758038536799358397002014"; + const char *b2_binary = "0000000000000000000000000000010101111101101000000110100001011010" + "1101101010001001000001101000101000100110011001110001111110100010"; + + bigint<1> b0 = bigint<1>(0ul); + bigint<1> b1 = bigint<1>(b1_decimal); + bigint<2> b2 = bigint<2>(b2_decimal); + + EXPECT_EQ(b0.as_ulong(), 0ul); + EXPECT_TRUE(b0.is_zero()); + EXPECT_EQ(b1.as_ulong(), 76749407ul); + EXPECT_FALSE(b1.is_zero()); + EXPECT_EQ(b2.as_ulong(), 15747124762497195938ul); + EXPECT_FALSE(b2.is_zero()); + EXPECT_NE(b0, b1); + EXPECT_FALSE(b0 == b1); + + EXPECT_EQ(b2.max_bits(), 128); + EXPECT_EQ(b2.num_bits(), 99); + for (size_t i = 0; i < 128; i++) { + EXPECT_EQ(b2.test_bit(i), (b2_binary[127-i] == '1')); + } + + bigint<3> b3 = b2 * b1; + + EXPECT_EQ(b3, bigint<3>(b3_decimal)); + EXPECT_FALSE(b3.is_zero()); + + bigint<3> b3a { b3 }; + EXPECT_EQ(b3a, bigint<3>(b3_decimal)); + EXPECT_EQ(b3a, b3); + EXPECT_FALSE(b3a.is_zero()); + + mpz_t m3; + mpz_init(m3); + b3.to_mpz(m3); + bigint<3> b3b { m3 }; + EXPECT_EQ(b3b, b3); + + bigint<2> quotient; + bigint<2> remainder; + bigint<3>::div_qr(quotient, remainder, b3, b2); + EXPECT_LT(quotient.num_bits(), GMP_NUMB_BITS); + EXPECT_EQ(quotient.as_ulong(), b1.as_ulong()); + bigint<1> b1inc = bigint<1>("76749408"); + bigint<1> b1a = quotient.shorten(b1inc, "test"); + EXPECT_EQ(b1a, b1); + EXPECT_TRUE(remainder.is_zero()); + remainder.limit(b2, "test"); + + EXPECT_THROW((void)(quotient.shorten(b1, "test")), std::domain_error); + EXPECT_THROW(remainder.limit(remainder, "test"), std::domain_error); + + bigint<1> br = bigint<1>("42"); + b3 += br; + EXPECT_NE(b3, b3a); + EXPECT_GT(b3, b3a); + EXPECT_FALSE(b3a > b3); + + bigint<3>::div_qr(quotient, remainder, b3, b2); + EXPECT_LT(quotient.num_bits(), GMP_NUMB_BITS); + EXPECT_EQ(quotient.as_ulong(), b1.as_ulong()); + EXPECT_LT(remainder.num_bits(), GMP_NUMB_BITS); + EXPECT_EQ(remainder.as_ulong(), 42); + + b3a.clear(); + EXPECT_TRUE(b3a.is_zero()); + EXPECT_EQ(b3a.num_bits(), 0); + EXPECT_FALSE(b3.is_zero()); + + bigint<4> bx = bigint<4>().randomize(); + bigint<4> by = bigint<4>().randomize(); + EXPECT_FALSE(bx == by); + + // TODO: test serialization +} + diff --git a/src/snark/libsnark/algebra/fields/tests/test_fields.cpp b/src/snark/libsnark/algebra/fields/tests/test_fields.cpp new file mode 100644 index 000000000..969800d8b --- /dev/null +++ b/src/snark/libsnark/algebra/fields/tests/test_fields.cpp @@ -0,0 +1,191 @@ +/** + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ +#include "common/profiling.hpp" +#ifdef CURVE_BN128 +#include "algebra/curves/bn128/bn128_pp.hpp" +#endif +#include "algebra/curves/alt_bn128/alt_bn128_pp.hpp" +#include "algebra/fields/fp6_3over2.hpp" +#include "algebra/fields/fp12_2over3over2.hpp" + +#include + +using namespace libsnark; + +template +void test_field() +{ + bigint<1> rand1 = bigint<1>("76749407"); + bigint<1> rand2 = bigint<1>("44410867"); + bigint<1> randsum = bigint<1>("121160274"); + + FieldT zero = FieldT::zero(); + FieldT one = FieldT::one(); + FieldT a = FieldT::random_element(); + FieldT a_ser; + a_ser = reserialize(a); + EXPECT_EQ(a_ser, a); + + FieldT b = FieldT::random_element(); + FieldT c = FieldT::random_element(); + FieldT d = FieldT::random_element(); + + EXPECT_NE(a, zero); + EXPECT_NE(a, one); + + EXPECT_EQ(a * a, a.squared()); + EXPECT_EQ((a + b).squared(), a.squared() + a*b + b*a + b.squared()); + EXPECT_EQ((a + b)*(c + d), a*c + a*d + b*c + b*d); + EXPECT_EQ(a - b, a + (-b)); + EXPECT_EQ(a - b, (-b) + a); + + EXPECT_EQ((a ^ rand1) * (a ^ rand2), (a^randsum)); + + EXPECT_EQ(a * a.inverse(), one); + EXPECT_EQ((a + b) * c.inverse(), a * c.inverse() + (b.inverse() * c).inverse()); + +} + +template +void test_sqrt() +{ + for (size_t i = 0; i < 100; ++i) + { + FieldT a = FieldT::random_element(); + FieldT asq = a.squared(); + EXPECT_TRUE(asq.sqrt() == a || asq.sqrt() == -a); + } +} + +template +void test_two_squarings() +{ + FieldT a = FieldT::random_element(); + EXPECT_EQ(a.squared(), a * a); + EXPECT_EQ(a.squared(), a.squared_complex()); + EXPECT_EQ(a.squared(), a.squared_karatsuba()); +} + +template +void test_Frobenius() +{ + FieldT a = FieldT::random_element(); + EXPECT_EQ(a.Frobenius_map(0), a); + FieldT a_q = a ^ FieldT::base_field_char(); + for (size_t power = 1; power < 10; ++power) + { + const FieldT a_qi = a.Frobenius_map(power); + EXPECT_EQ(a_qi, a_q); + + a_q = a_q ^ FieldT::base_field_char(); + } +} + +template +void test_unitary_inverse() +{ + EXPECT_EQ(FieldT::extension_degree() % 2, 0); + FieldT a = FieldT::random_element(); + FieldT aqcubed_minus1 = a.Frobenius_map(FieldT::extension_degree()/2) * a.inverse(); + EXPECT_EQ(aqcubed_minus1.inverse(), aqcubed_minus1.unitary_inverse()); +} + +template +void test_all_fields() +{ + test_field >(); + test_field >(); + test_field >(); + test_field >(); + + test_sqrt >(); + test_sqrt >(); + test_sqrt >(); + + test_Frobenius >(); + test_Frobenius >(); + + test_unitary_inverse >(); +} + +template +void test_Fp4_tom_cook() +{ + typedef typename Fp4T::my_Fp FieldT; + for (size_t i = 0; i < 100; ++i) + { + const Fp4T a = Fp4T::random_element(); + const Fp4T b = Fp4T::random_element(); + const Fp4T correct_res = a * b; + + Fp4T res; + + const FieldT + &a0 = a.c0.c0, + &a1 = a.c1.c0, + &a2 = a.c0.c1, + &a3 = a.c1.c1; + + const FieldT + &b0 = b.c0.c0, + &b1 = b.c1.c0, + &b2 = b.c0.c1, + &b3 = b.c1.c1; + + FieldT + &c0 = res.c0.c0, + &c1 = res.c1.c0, + &c2 = res.c0.c1, + &c3 = res.c1.c1; + + const FieldT v0 = a0 * b0; + const FieldT v1 = (a0 + a1 + a2 + a3) * (b0 + b1 + b2 + b3); + const FieldT v2 = (a0 - a1 + a2 - a3) * (b0 - b1 + b2 - b3); + const FieldT v3 = (a0 + FieldT(2)*a1 + FieldT(4)*a2 + FieldT(8)*a3) * (b0 + FieldT(2)*b1 + FieldT(4)*b2 + FieldT(8)*b3); + const FieldT v4 = (a0 - FieldT(2)*a1 + FieldT(4)*a2 - FieldT(8)*a3) * (b0 - FieldT(2)*b1 + FieldT(4)*b2 - FieldT(8)*b3); + const FieldT v5 = (a0 + FieldT(3)*a1 + FieldT(9)*a2 + FieldT(27)*a3) * (b0 + FieldT(3)*b1 + FieldT(9)*b2 + FieldT(27)*b3); + const FieldT v6 = a3 * b3; + + const FieldT beta = Fp4T::non_residue; + + c0 = v0 + beta*(FieldT(4).inverse()*v0 - FieldT(6).inverse()*(v1 + v2) + FieldT(24).inverse() * (v3 + v4) - FieldT(5) * v6); + c1 = - FieldT(3).inverse()*v0 + v1 - FieldT(2).inverse()*v2 - FieldT(4).inverse()*v3 + FieldT(20).inverse() * v4 + FieldT(30).inverse() * v5 - FieldT(12) * v6 + beta * ( - FieldT(12).inverse() * (v0 - v1) + FieldT(24).inverse()*(v2 - v3) - FieldT(120).inverse() * (v4 - v5) - FieldT(3) * v6); + c2 = - (FieldT(5)*(FieldT(4).inverse()))* v0 + (FieldT(2)*(FieldT(3).inverse()))*(v1 + v2) - FieldT(24).inverse()*(v3 + v4) + FieldT(4)*v6 + beta*v6; + c3 = FieldT(12).inverse() * (FieldT(5)*v0 - FieldT(7)*v1) - FieldT(24).inverse()*(v2 - FieldT(7)*v3 + v4 + v5) + FieldT(15)*v6; + + EXPECT_EQ(res, correct_res); + + // {v0, v3, v4, v5} + const FieldT u = (FieldT::one() - beta).inverse(); + EXPECT_EQ(v0, u * c0 + beta * u * c2 - beta * u * FieldT(2).inverse() * v1 - beta * u * FieldT(2).inverse() * v2 + beta * v6); + EXPECT_EQ(v3, - FieldT(15) * u * c0 - FieldT(30) * u * c1 - FieldT(3) * (FieldT(4) + beta) * u * c2 - FieldT(6) * (FieldT(4) + beta) * u * c3 + (FieldT(24) - FieldT(3) * beta * FieldT(2).inverse()) * u * v1 + (-FieldT(8) + beta * FieldT(2).inverse()) * u * v2 + - FieldT(3) * (-FieldT(16) + beta) * v6); + EXPECT_EQ(v4, - FieldT(15) * u * c0 + FieldT(30) * u * c1 - FieldT(3) * (FieldT(4) + beta) * u * c2 + FieldT(6) * (FieldT(4) + beta) * u * c3 + (FieldT(24) - FieldT(3) * beta * FieldT(2).inverse()) * u * v2 + (-FieldT(8) + beta * FieldT(2).inverse()) * u * v1 + - FieldT(3) * (-FieldT(16) + beta) * v6); + EXPECT_EQ(v5, - FieldT(80) * u * c0 - FieldT(240) * u * c1 - FieldT(8) * (FieldT(9) + beta) * u * c2 - FieldT(24) * (FieldT(9) + beta) * u * c3 - FieldT(2) * (-FieldT(81) + beta) * u * v1 + (-FieldT(81) + beta) * u * v2 + - FieldT(8) * (-FieldT(81) + beta) * v6); + + // c0 + beta c2 - (beta v1)/2 - (beta v2)/ 2 - (-1 + beta) beta v6, + // -15 c0 - 30 c1 - 3 (4 + beta) c2 - 6 (4 + beta) c3 + (24 - (3 beta)/2) v1 + (-8 + beta/2) v2 + 3 (-16 + beta) (-1 + beta) v6, + // -15 c0 + 30 c1 - 3 (4 + beta) c2 + 6 (4 + beta) c3 + (-8 + beta/2) v1 + (24 - (3 beta)/2) v2 + 3 (-16 + beta) (-1 + beta) v6, + // -80 c0 - 240 c1 - 8 (9 + beta) c2 - 24 (9 + beta) c3 - 2 (-81 + beta) v1 + (-81 + beta) v2 + 8 (-81 + beta) (-1 + beta) v6 + } +} + +TEST(algebra, fields) +{ + alt_bn128_pp::init_public_params(); + test_field(); + test_Frobenius(); + test_all_fields(); + +#ifdef CURVE_BN128 // BN128 has fancy dependencies so it may be disabled + bn128_pp::init_public_params(); + test_field >(); + test_field >(); +#endif +} diff --git a/src/snark/libsnark/algebra/knowledge_commitment/knowledge_commitment.hpp b/src/snark/libsnark/algebra/knowledge_commitment/knowledge_commitment.hpp new file mode 100644 index 000000000..902423134 --- /dev/null +++ b/src/snark/libsnark/algebra/knowledge_commitment/knowledge_commitment.hpp @@ -0,0 +1,84 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for: + - a knowledge commitment, and + - a knowledge commitment vector. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef KNOWLEDGE_COMMITMENT_HPP_ +#define KNOWLEDGE_COMMITMENT_HPP_ + +#include "algebra/fields/fp.hpp" +#include "common/data_structures/sparse_vector.hpp" + +namespace libsnark { + +/********************** Knowledge commitment *********************************/ + +/** + * A knowledge commitment is a pair (g,h) where g is in T1 and h in T2, + * and T1 and T2 are groups (written additively). + * + * Such pairs form a group by defining: + * - "zero" = (0,0) + * - "one" = (1,1) + * - a * (g,h) + b * (g',h') := ( a * g + b * g', a * h + b * h'). + */ +template +struct knowledge_commitment { + + T1 g; + T2 h; + + knowledge_commitment() = default; + knowledge_commitment(const knowledge_commitment &other) = default; + knowledge_commitment(knowledge_commitment &&other) = default; + knowledge_commitment(const T1 &g, const T2 &h); + + knowledge_commitment& operator=(const knowledge_commitment &other) = default; + knowledge_commitment& operator=(knowledge_commitment &&other) = default; + knowledge_commitment operator+(const knowledge_commitment &other) const; + + bool is_zero() const; + bool operator==(const knowledge_commitment &other) const; + bool operator!=(const knowledge_commitment &other) const; + + static knowledge_commitment zero(); + static knowledge_commitment one(); + + void print() const; + + static size_t size_in_bits(); +}; + +template +knowledge_commitment operator*(const bigint &lhs, const knowledge_commitment &rhs); + +template &modulus_p> +knowledge_commitment operator*(const Fp_model &lhs, const knowledge_commitment &rhs); + +template +std::ostream& operator<<(std::ostream& out, const knowledge_commitment &kc); + +template +std::istream& operator>>(std::istream& in, knowledge_commitment &kc); + +/******************** Knowledge commitment vector ****************************/ + +/** + * A knowledge commitment vector is a sparse vector of knowledge commitments. + */ +template +using knowledge_commitment_vector = sparse_vector >; + +} // libsnark + +#include "algebra/knowledge_commitment/knowledge_commitment.tcc" + +#endif // KNOWLEDGE_COMMITMENT_HPP_ diff --git a/src/snark/libsnark/algebra/knowledge_commitment/knowledge_commitment.tcc b/src/snark/libsnark/algebra/knowledge_commitment/knowledge_commitment.tcc new file mode 100644 index 000000000..15b2926c8 --- /dev/null +++ b/src/snark/libsnark/algebra/knowledge_commitment/knowledge_commitment.tcc @@ -0,0 +1,111 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for: + - a knowledge commitment, and + - a knowledge commitment vector. + + See knowledge_commitment.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef KNOWLEDGE_COMMITMENT_TCC_ +#define KNOWLEDGE_COMMITMENT_TCC_ + +namespace libsnark { + +template +knowledge_commitment::knowledge_commitment(const T1 &g, const T2 &h) : + g(g), h(h) +{ +} + +template +knowledge_commitment knowledge_commitment::zero() +{ + return knowledge_commitment(T1::zero(), T2::zero()); +} + +template +knowledge_commitment knowledge_commitment::one() +{ + return knowledge_commitment(T1::one(), T2::one()); +} + +template +knowledge_commitment knowledge_commitment::operator+(const knowledge_commitment &other) const +{ + return knowledge_commitment(this->g + other.g, + this->h + other.h); +} + +template +bool knowledge_commitment::is_zero() const +{ + return (g.is_zero() && h.is_zero()); +} + +template +bool knowledge_commitment::operator==(const knowledge_commitment &other) const +{ + return (this->g == other.g && + this->h == other.h); +} + +template +bool knowledge_commitment::operator!=(const knowledge_commitment &other) const +{ + return !((*this) == other); +} + +template +knowledge_commitment operator*(const bigint &lhs, const knowledge_commitment &rhs) +{ + return knowledge_commitment(lhs * rhs.g, + lhs * rhs.h); +} + +template &modulus_p> +knowledge_commitment operator*(const Fp_model &lhs, const knowledge_commitment &rhs) +{ + return (lhs.as_bigint()) * rhs; +} + +template +void knowledge_commitment::print() const +{ + printf("knowledge_commitment.g:\n"); + g.print(); + printf("knowledge_commitment.h:\n"); + h.print(); +} + +template +size_t knowledge_commitment::size_in_bits() +{ + return T1::size_in_bits() + T2::size_in_bits(); +} + +template +std::ostream& operator<<(std::ostream& out, const knowledge_commitment &kc) +{ + out << kc.g << OUTPUT_SEPARATOR << kc.h; + return out; +} + +template +std::istream& operator>>(std::istream& in, knowledge_commitment &kc) +{ + in >> kc.g; + consume_OUTPUT_SEPARATOR(in); + in >> kc.h; + return in; +} + +} // libsnark + +#endif // KNOWLEDGE_COMMITMENT_TCC_ diff --git a/src/snark/libsnark/algebra/scalar_multiplication/kc_multiexp.hpp b/src/snark/libsnark/algebra/scalar_multiplication/kc_multiexp.hpp new file mode 100644 index 000000000..27e48f821 --- /dev/null +++ b/src/snark/libsnark/algebra/scalar_multiplication/kc_multiexp.hpp @@ -0,0 +1,55 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef KC_MULTIEXP_HPP_ +#define KC_MULTIEXP_HPP_ + +/* + Split out from multiexp to prevent cyclical + dependencies. I.e. previously multiexp depended on + knowledge_commitment, which depended on sparse_vector, which + depended on multiexp (to do accumulate). + + Will probably go away in more general exp refactoring. +*/ + +#include "algebra/knowledge_commitment/knowledge_commitment.hpp" + +namespace libsnark { + +template +knowledge_commitment opt_window_wnaf_exp(const knowledge_commitment &base, + const bigint &scalar, const size_t scalar_bits); + +template +knowledge_commitment kc_multi_exp_with_mixed_addition(const knowledge_commitment_vector &vec, + const size_t min_idx, + const size_t max_idx, + typename std::vector::const_iterator scalar_start, + typename std::vector::const_iterator scalar_end, + const size_t chunks, + const bool use_multiexp=false); + +template +void kc_batch_to_special(std::vector > &vec); + +template +knowledge_commitment_vector kc_batch_exp(const size_t scalar_size, + const size_t T1_window, + const size_t T2_window, + const window_table &T1_table, + const window_table &T2_table, + const FieldT &T1_coeff, + const FieldT &T2_coeff, + const std::vector &v, + const size_t suggested_num_chunks); + +} // libsnark + +#include "algebra/scalar_multiplication/kc_multiexp.tcc" + +#endif // KC_MULTIEXP_HPP_ diff --git a/src/snark/libsnark/algebra/scalar_multiplication/kc_multiexp.tcc b/src/snark/libsnark/algebra/scalar_multiplication/kc_multiexp.tcc new file mode 100644 index 000000000..e9c08d4bc --- /dev/null +++ b/src/snark/libsnark/algebra/scalar_multiplication/kc_multiexp.tcc @@ -0,0 +1,274 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef KC_MULTIEXP_TCC_ +#define KC_MULTIEXP_TCC_ + +namespace libsnark { + +template +knowledge_commitment opt_window_wnaf_exp(const knowledge_commitment &base, + const bigint &scalar, const size_t scalar_bits) +{ + return knowledge_commitment(opt_window_wnaf_exp(base.g, scalar, scalar_bits), + opt_window_wnaf_exp(base.h, scalar, scalar_bits)); +} + +template +knowledge_commitment kc_multi_exp_with_mixed_addition(const knowledge_commitment_vector &vec, + const size_t min_idx, + const size_t max_idx, + typename std::vector::const_iterator scalar_start, + typename std::vector::const_iterator scalar_end, + const size_t chunks, + const bool use_multiexp) +{ + enter_block("Process scalar vector"); + auto index_it = std::lower_bound(vec.indices.begin(), vec.indices.end(), min_idx); + const size_t offset = index_it - vec.indices.begin(); + + auto value_it = vec.values.begin() + offset; + + const FieldT zero = FieldT::zero(); + const FieldT one = FieldT::one(); + + std::vector p; + std::vector > g; + + knowledge_commitment acc = knowledge_commitment::zero(); + + size_t num_skip = 0; + size_t num_add = 0; + size_t num_other = 0; + + const size_t scalar_length = std::distance(scalar_start, scalar_end); + + while (index_it != vec.indices.end() && *index_it < max_idx) + { + const size_t scalar_position = (*index_it) - min_idx; + assert(scalar_position < scalar_length); + + const FieldT scalar = *(scalar_start + scalar_position); + + if (scalar == zero) + { + // do nothing + ++num_skip; + } + else if (scalar == one) + { +#ifdef USE_MIXED_ADDITION + acc.g = acc.g.mixed_add(value_it->g); + acc.h = acc.h.mixed_add(value_it->h); +#else + acc.g = acc.g + value_it->g; + acc.h = acc.h + value_it->h; +#endif + ++num_add; + } + else + { + p.emplace_back(scalar); + g.emplace_back(*value_it); + ++num_other; + } + + ++index_it; + ++value_it; + } + + //print_indent(); printf("* Elements of w skipped: %zu (%0.2f%%)\n", num_skip, 100.*num_skip/(num_skip+num_add+num_other)); + //print_indent(); printf("* Elements of w processed with special addition: %zu (%0.2f%%)\n", num_add, 100.*num_add/(num_skip+num_add+num_other)); + //print_indent(); printf("* Elements of w remaining: %zu (%0.2f%%)\n", num_other, 100.*num_other/(num_skip+num_add+num_other)); + leave_block("Process scalar vector"); + + return acc + multi_exp, FieldT>(g.begin(), g.end(), p.begin(), p.end(), chunks, use_multiexp); +} + +template +void kc_batch_to_special(std::vector > &vec) +{ + enter_block("Batch-convert knowledge-commitments to special form"); + + std::vector g_vec; + g_vec.reserve(vec.size()); + + for (size_t i = 0; i < vec.size(); ++i) + { + if (!vec[i].g.is_zero()) + { + g_vec.emplace_back(vec[i].g); + } + } + + batch_to_special_all_non_zeros(g_vec); + auto g_it = g_vec.begin(); + T1 T1_zero_special = T1::zero(); + T1_zero_special.to_special(); + + for (size_t i = 0; i < vec.size(); ++i) + { + if (!vec[i].g.is_zero()) + { + vec[i].g = *g_it; + ++g_it; + } + else + { + vec[i].g = T1_zero_special; + } + } + + g_vec.clear(); + + std::vector h_vec; + h_vec.reserve(vec.size()); + + for (size_t i = 0; i < vec.size(); ++i) + { + if (!vec[i].h.is_zero()) + { + h_vec.emplace_back(vec[i].h); + } + } + + batch_to_special_all_non_zeros(h_vec); + auto h_it = h_vec.begin(); + T2 T2_zero_special = T2::zero(); + T2_zero_special.to_special(); + + for (size_t i = 0; i < vec.size(); ++i) + { + if (!vec[i].h.is_zero()) + { + vec[i].h = *h_it; + ++h_it; + } + else + { + vec[i].h = T2_zero_special; + } + } + + g_vec.clear(); + + leave_block("Batch-convert knowledge-commitments to special form"); +} + +template +knowledge_commitment_vector kc_batch_exp_internal(const size_t scalar_size, + const size_t T1_window, + const size_t T2_window, + const window_table &T1_table, + const window_table &T2_table, + const FieldT &T1_coeff, + const FieldT &T2_coeff, + const std::vector &v, + const size_t start_pos, + const size_t end_pos, + const size_t expected_size) +{ + knowledge_commitment_vector res; + + res.values.reserve(expected_size); + res.indices.reserve(expected_size); + + for (size_t pos = start_pos; pos != end_pos; ++pos) + { + if (!v[pos].is_zero()) + { + res.values.emplace_back(knowledge_commitment(windowed_exp(scalar_size, T1_window, T1_table, T1_coeff * v[pos]), + windowed_exp(scalar_size, T2_window, T2_table, T2_coeff * v[pos]))); + res.indices.emplace_back(pos); + } + } + + return res; +} + +template +knowledge_commitment_vector kc_batch_exp(const size_t scalar_size, + const size_t T1_window, + const size_t T2_window, + const window_table &T1_table, + const window_table &T2_table, + const FieldT &T1_coeff, + const FieldT &T2_coeff, + const std::vector &v, + const size_t suggested_num_chunks) +{ + knowledge_commitment_vector res; + res.domain_size_ = v.size(); + + size_t nonzero = 0; + for (size_t i = 0; i < v.size(); ++i) + { + nonzero += (v[i].is_zero() ? 0 : 1); + } + + const size_t num_chunks = std::max((size_t)1, std::min(nonzero, suggested_num_chunks)); + + if (!inhibit_profiling_info) + { + print_indent(); printf("Non-zero coordinate count: %zu/%zu (%0.2f%%)\n", nonzero, v.size(), 100.*nonzero/v.size()); + } + + std::vector > tmp(num_chunks); + std::vector chunk_pos(num_chunks+1); + + const size_t chunk_size = nonzero / num_chunks; + const size_t last_chunk = nonzero - chunk_size * (num_chunks - 1); + + chunk_pos[0] = 0; + + size_t cnt = 0; + size_t chunkno = 1; + + for (size_t i = 0; i < v.size(); ++i) + { + cnt += (v[i].is_zero() ? 0 : 1); + if (cnt == chunk_size && chunkno < num_chunks) + { + chunk_pos[chunkno] = i; + cnt = 0; + ++chunkno; + } + } + + chunk_pos[num_chunks] = v.size(); + +#ifdef MULTICORE +#pragma omp parallel for +#endif + for (size_t i = 0; i < num_chunks; ++i) + { + tmp[i] = kc_batch_exp_internal(scalar_size, T1_window, T2_window, T1_table, T2_table, T1_coeff, T2_coeff, v, + chunk_pos[i], chunk_pos[i+1], i == num_chunks - 1 ? last_chunk : chunk_size); +#ifdef USE_MIXED_ADDITION + kc_batch_to_special(tmp[i].values); +#endif + } + + if (num_chunks == 1) + { + tmp[0].domain_size_ = v.size(); + return tmp[0]; + } + else + { + for (size_t i = 0; i < num_chunks; ++i) + { + res.values.insert(res.values.end(), tmp[i].values.begin(), tmp[i].values.end()); + res.indices.insert(res.indices.end(), tmp[i].indices.begin(), tmp[i].indices.end()); + } + return res; + } +} + +} // libsnark + +#endif // KC_MULTIEXP_TCC_ diff --git a/src/snark/libsnark/algebra/scalar_multiplication/multiexp.hpp b/src/snark/libsnark/algebra/scalar_multiplication/multiexp.hpp new file mode 100644 index 000000000..eaf72d61f --- /dev/null +++ b/src/snark/libsnark/algebra/scalar_multiplication/multiexp.hpp @@ -0,0 +1,110 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for multi-exponentiation routines. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef MULTIEXP_HPP_ +#define MULTIEXP_HPP_ + +namespace libsnark { + +/** + * Naive multi-exponentiation individually multiplies each base by the + * corresponding scalar and adds up the results. + */ +template +T naive_exp(typename std::vector::const_iterator vec_start, + typename std::vector::const_iterator vec_end, + typename std::vector::const_iterator scalar_start, + typename std::vector::const_iterator scalar_end); + +template +T naive_plain_exp(typename std::vector::const_iterator vec_start, + typename std::vector::const_iterator vec_end, + typename std::vector::const_iterator scalar_start, + typename std::vector::const_iterator scalar_end); + +/** + * Naive multi-exponentiation uses a variant of the Bos-Coster algorithm [1], + * and implementation suggestions from [2]. + * + * [1] = Bos and Coster, "Addition chain heuristics", CRYPTO '89 + * [2] = Bernstein, Duif, Lange, Schwabe, and Yang, "High-speed high-security signatures", CHES '11 + */ +template +T multi_exp(typename std::vector::const_iterator vec_start, + typename std::vector::const_iterator vec_end, + typename std::vector::const_iterator scalar_start, + typename std::vector::const_iterator scalar_end, + const size_t chunks, + const bool use_multiexp=false); + + +/** + * A variant of multi_exp that takes advantage of the method mixed_add (instead of the operator '+'). + */ +template +T multi_exp_with_mixed_addition(typename std::vector::const_iterator vec_start, + typename std::vector::const_iterator vec_end, + typename std::vector::const_iterator scalar_start, + typename std::vector::const_iterator scalar_end, + const size_t chunks, + const bool use_multiexp); + +/** + * A window table stores window sizes for different instance sizes for fixed-base multi-scalar multiplications. + */ +template +using window_table = std::vector >; + +/** + * Compute window size for the given number of scalars. + */ +template +size_t get_exp_window_size(const size_t num_scalars); + +/** + * Compute table of window sizes. + */ +template +window_table get_window_table(const size_t scalar_size, + const size_t window, + const T &g); + +template +T windowed_exp(const size_t scalar_size, + const size_t window, + const window_table &powers_of_g, + const FieldT &pow); + +template +std::vector batch_exp(const size_t scalar_size, + const size_t window, + const window_table &table, + const std::vector &v); + +template +std::vector batch_exp_with_coeff(const size_t scalar_size, + const size_t window, + const window_table &table, + const FieldT &coeff, + const std::vector &v); + +// defined in every curve +template +void batch_to_special_all_non_zeros(std::vector &vec); + +template +void batch_to_special(std::vector &vec); + +} // libsnark + +#include "algebra/scalar_multiplication/multiexp.tcc" + +#endif // MULTIEXP_HPP_ diff --git a/src/snark/libsnark/algebra/scalar_multiplication/multiexp.tcc b/src/snark/libsnark/algebra/scalar_multiplication/multiexp.tcc new file mode 100644 index 000000000..a6b14c4df --- /dev/null +++ b/src/snark/libsnark/algebra/scalar_multiplication/multiexp.tcc @@ -0,0 +1,590 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for multi-exponentiation routines. + + See multiexp.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef MULTIEXP_TCC_ +#define MULTIEXP_TCC_ + +#include "algebra/fields/fp_aux.tcc" + +#include +#include +#include + +#include "common/profiling.hpp" +#include "common/utils.hpp" +#include "algebra/scalar_multiplication/wnaf.hpp" + +namespace libsnark { + +template +class ordered_exponent { +// to use std::push_heap and friends later +public: + size_t idx; + bigint r; + + ordered_exponent(const size_t idx, const bigint &r) : idx(idx), r(r) {}; + + bool operator<(const ordered_exponent &other) const + { +#if defined(__x86_64__) && defined(USE_ASM) + if (n == 3) + { + long res; + __asm__ + ("// check for overflow \n\t" + "mov $0, %[res] \n\t" + ADD_CMP(16) + ADD_CMP(8) + ADD_CMP(0) + "jmp done%= \n\t" + "subtract%=: \n\t" + "mov $1, %[res] \n\t" + "done%=: \n\t" + : [res] "=&r" (res) + : [A] "r" (other.r.data), [mod] "r" (this->r.data) + : "cc", "%rax"); + return res; + } + else if (n == 4) + { + long res; + __asm__ + ("// check for overflow \n\t" + "mov $0, %[res] \n\t" + ADD_CMP(24) + ADD_CMP(16) + ADD_CMP(8) + ADD_CMP(0) + "jmp done%= \n\t" + "subtract%=: \n\t" + "mov $1, %[res] \n\t" + "done%=: \n\t" + : [res] "=&r" (res) + : [A] "r" (other.r.data), [mod] "r" (this->r.data) + : "cc", "%rax"); + return res; + } + else if (n == 5) + { + long res; + __asm__ + ("// check for overflow \n\t" + "mov $0, %[res] \n\t" + ADD_CMP(32) + ADD_CMP(24) + ADD_CMP(16) + ADD_CMP(8) + ADD_CMP(0) + "jmp done%= \n\t" + "subtract%=: \n\t" + "mov $1, %[res] \n\t" + "done%=: \n\t" + : [res] "=&r" (res) + : [A] "r" (other.r.data), [mod] "r" (this->r.data) + : "cc", "%rax"); + return res; + } + else +#endif + { + return (mpn_cmp(this->r.data, other.r.data, n) < 0); + } + } +}; + +template +T naive_exp(typename std::vector::const_iterator vec_start, + typename std::vector::const_iterator vec_end, + typename std::vector::const_iterator scalar_start, + typename std::vector::const_iterator scalar_end) +{ + T result(T::zero()); + + typename std::vector::const_iterator vec_it; + typename std::vector::const_iterator scalar_it; + + for (vec_it = vec_start, scalar_it = scalar_start; vec_it != vec_end; ++vec_it, ++scalar_it) + { + bigint scalar_bigint = scalar_it->as_bigint(); + result = result + opt_window_wnaf_exp(*vec_it, scalar_bigint, scalar_bigint.num_bits()); + } + assert(scalar_it == scalar_end); + + return result; +} + +template +T naive_plain_exp(typename std::vector::const_iterator vec_start, + typename std::vector::const_iterator vec_end, + typename std::vector::const_iterator scalar_start, + typename std::vector::const_iterator scalar_end) +{ + T result(T::zero()); + + typename std::vector::const_iterator vec_it; + typename std::vector::const_iterator scalar_it; + + for (vec_it = vec_start, scalar_it = scalar_start; vec_it != vec_end; ++vec_it, ++scalar_it) + { + result = result + (*scalar_it) * (*vec_it); + } + assert(scalar_it == scalar_end); + + return result; +} + +/* + The multi-exponentiation algorithm below is a variant of the Bos-Coster algorithm + [Bos and Coster, "Addition chain heuristics", CRYPTO '89]. + The implementation uses suggestions from + [Bernstein, Duif, Lange, Schwabe, and Yang, "High-speed high-security signatures", CHES '11]. +*/ +template +T multi_exp_inner(typename std::vector::const_iterator vec_start, + typename std::vector::const_iterator vec_end, + typename std::vector::const_iterator scalar_start, + typename std::vector::const_iterator scalar_end) +{ + const mp_size_t n = std::remove_reference::type::num_limbs; + + if (vec_start == vec_end) + { + return T::zero(); + } + + if (vec_start + 1 == vec_end) + { + return (*scalar_start)*(*vec_start); + } + + std::vector > opt_q; + const size_t vec_len = scalar_end - scalar_start; + const size_t odd_vec_len = (vec_len % 2 == 1 ? vec_len : vec_len + 1); + opt_q.reserve(odd_vec_len); + std::vector g; + g.reserve(odd_vec_len); + + typename std::vector::const_iterator vec_it; + typename std::vector::const_iterator scalar_it; + size_t i; + for (i=0, vec_it = vec_start, scalar_it = scalar_start; vec_it != vec_end; ++vec_it, ++scalar_it, ++i) + { + g.emplace_back(*vec_it); + + opt_q.emplace_back(ordered_exponent(i, scalar_it->as_bigint())); + } + std::make_heap(opt_q.begin(),opt_q.end()); + assert(scalar_it == scalar_end); + + if (vec_len != odd_vec_len) + { + g.emplace_back(T::zero()); + opt_q.emplace_back(ordered_exponent(odd_vec_len - 1, bigint(0ul))); + } + assert(g.size() % 2 == 1); + assert(opt_q.size() == g.size()); + + T opt_result = T::zero(); + + while (true) + { + ordered_exponent &a = opt_q[0]; + ordered_exponent &b = (opt_q[1] < opt_q[2] ? opt_q[2] : opt_q[1]); + + const size_t abits = a.r.num_bits(); + + if (b.r.is_zero()) + { + // opt_result = opt_result + (a.r * g[a.idx]); + opt_result = opt_result + opt_window_wnaf_exp(g[a.idx], a.r, abits); + break; + } + + const size_t bbits = b.r.num_bits(); + const size_t limit = (abits-bbits >= 20 ? 20 : abits-bbits); + + if (bbits < 1ul< (x-y) A + y (B+A) + mpn_sub_n(a.r.data, a.r.data, b.r.data, n); + g[b.idx] = g[b.idx] + g[a.idx]; + } + + // regardless of whether a was cleared or subtracted from we push it down, then take back up + + /* heapify A down */ + size_t a_pos = 0; + while (2*a_pos + 2< odd_vec_len) + { + // this is a max-heap so to maintain a heap property we swap with the largest of the two + if (opt_q[2*a_pos+1] < opt_q[2*a_pos+2]) + { + std::swap(opt_q[a_pos], opt_q[2*a_pos+2]); + a_pos = 2*a_pos+2; + } + else + { + std::swap(opt_q[a_pos], opt_q[2*a_pos+1]); + a_pos = 2*a_pos+1; + } + } + + /* now heapify A up appropriate amount of times */ + while (a_pos > 0 && opt_q[(a_pos-1)/2] < opt_q[a_pos]) + { + std::swap(opt_q[a_pos], opt_q[(a_pos-1)/2]); + a_pos = (a_pos-1) / 2; + } + } + + return opt_result; +} + +template +T multi_exp(typename std::vector::const_iterator vec_start, + typename std::vector::const_iterator vec_end, + typename std::vector::const_iterator scalar_start, + typename std::vector::const_iterator scalar_end, + const size_t chunks, + const bool use_multiexp) +{ + const size_t total = vec_end - vec_start; + if (total < chunks) + { + return naive_exp(vec_start, vec_end, scalar_start, scalar_end); + } + + const size_t one = total/chunks; + + std::vector partial(chunks, T::zero()); + + if (use_multiexp) + { +#ifdef MULTICORE +#pragma omp parallel for +#endif + for (size_t i = 0; i < chunks; ++i) + { + partial[i] = multi_exp_inner(vec_start + i*one, + (i == chunks-1 ? vec_end : vec_start + (i+1)*one), + scalar_start + i*one, + (i == chunks-1 ? scalar_end : scalar_start + (i+1)*one)); + } + } + else + { +#ifdef MULTICORE +#pragma omp parallel for +#endif + for (size_t i = 0; i < chunks; ++i) + { + partial[i] = naive_exp(vec_start + i*one, + (i == chunks-1 ? vec_end : vec_start + (i+1)*one), + scalar_start + i*one, + (i == chunks-1 ? scalar_end : scalar_start + (i+1)*one)); + } + } + + T final = T::zero(); + + for (size_t i = 0; i < chunks; ++i) + { + final = final + partial[i]; + } + + return final; +} + +template +T multi_exp_with_mixed_addition(typename std::vector::const_iterator vec_start, + typename std::vector::const_iterator vec_end, + typename std::vector::const_iterator scalar_start, + typename std::vector::const_iterator scalar_end, + const size_t chunks, + const bool use_multiexp) +{ + assert(std::distance(vec_start, vec_end) == std::distance(scalar_start, scalar_end)); + enter_block("Process scalar vector"); + auto value_it = vec_start; + auto scalar_it = scalar_start; + + const FieldT zero = FieldT::zero(); + const FieldT one = FieldT::one(); + std::vector p; + std::vector g; + + T acc = T::zero(); + + size_t num_skip = 0; + size_t num_add = 0; + size_t num_other = 0; + + for (; scalar_it != scalar_end; ++scalar_it, ++value_it) + { + if (*scalar_it == zero) + { + // do nothing + ++num_skip; + } + else if (*scalar_it == one) + { +#ifdef USE_MIXED_ADDITION + acc = acc.mixed_add(*value_it); +#else + acc = acc + (*value_it); +#endif + ++num_add; + } + else + { + p.emplace_back(*scalar_it); + g.emplace_back(*value_it); + ++num_other; + } + } + //print_indent(); printf("* Elements of w skipped: %zu (%0.2f%%)\n", num_skip, 100.*num_skip/(num_skip+num_add+num_other)); + //print_indent(); printf("* Elements of w processed with special addition: %zu (%0.2f%%)\n", num_add, 100.*num_add/(num_skip+num_add+num_other)); + //print_indent(); printf("* Elements of w remaining: %zu (%0.2f%%)\n", num_other, 100.*num_other/(num_skip+num_add+num_other)); + + leave_block("Process scalar vector"); + + return acc + multi_exp(g.begin(), g.end(), p.begin(), p.end(), chunks, use_multiexp); +} + +template +size_t get_exp_window_size(const size_t num_scalars) +{ + if (T::fixed_base_exp_window_table.empty()) + { +#ifdef LOWMEM + return 14; +#else + return 17; +#endif + } + size_t window = 1; + for (long i = T::fixed_base_exp_window_table.size()-1; i >= 0; --i) + { +#ifdef DEBUG + if (!inhibit_profiling_info) + { + printf("%ld %zu %zu\n", i, num_scalars, T::fixed_base_exp_window_table[i]); + } +#endif + if (T::fixed_base_exp_window_table[i] != 0 && num_scalars >= T::fixed_base_exp_window_table[i]) + { + window = i+1; + break; + } + } + + if (!inhibit_profiling_info) + { + print_indent(); printf("Choosing window size %zu for %zu elements\n", window, num_scalars); + } + +#ifdef LOWMEM + window = std::min((size_t)14, window); +#endif + return window; +} + +template +window_table get_window_table(const size_t scalar_size, + const size_t window, + const T &g) +{ + const size_t in_window = 1ul< powers_of_g(outerc, std::vector(in_window, T::zero())); + + T gouter = g; + + for (size_t outer = 0; outer < outerc; ++outer) + { + T ginner = T::zero(); + size_t cur_in_window = outer == outerc-1 ? last_in_window : in_window; + for (size_t inner = 0; inner < cur_in_window; ++inner) + { + powers_of_g[outer][inner] = ginner; + ginner = ginner + gouter; + } + + for (size_t i = 0; i < window; ++i) + { + gouter = gouter + gouter; + } + } + + return powers_of_g; +} + +template +T windowed_exp(const size_t scalar_size, + const size_t window, + const window_table &powers_of_g, + const FieldT &pow) +{ + const size_t outerc = (scalar_size+window-1)/window; + const bigint pow_val = pow.as_bigint(); + + /* exp */ + T res = powers_of_g[0][0]; + + for (size_t outer = 0; outer < outerc; ++outer) + { + size_t inner = 0; + for (size_t i = 0; i < window; ++i) + { + if (pow_val.test_bit(outer*window + i)) + { + inner |= 1u << i; + } + } + + res = res + powers_of_g[outer][inner]; + } + + return res; +} + +template +std::vector batch_exp(const size_t scalar_size, + const size_t window, + const window_table &table, + const std::vector &v) +{ + if (!inhibit_profiling_info) + { + print_indent(); + } + std::vector res(v.size(), table[0][0]); + +#ifdef MULTICORE +#pragma omp parallel for +#endif + for (size_t i = 0; i < v.size(); ++i) + { + res[i] = windowed_exp(scalar_size, window, table, v[i]); + + if (!inhibit_profiling_info && (i % 10000 == 0)) + { + printf("."); + fflush(stdout); + } + } + + if (!inhibit_profiling_info) + { + printf(" DONE!\n"); + } + + return res; +} + +template +std::vector batch_exp_with_coeff(const size_t scalar_size, + const size_t window, + const window_table &table, + const FieldT &coeff, + const std::vector &v) +{ + if (!inhibit_profiling_info) + { + print_indent(); + } + std::vector res(v.size(), table[0][0]); + +#ifdef MULTICORE +#pragma omp parallel for +#endif + for (size_t i = 0; i < v.size(); ++i) + { + res[i] = windowed_exp(scalar_size, window, table, coeff * v[i]); + + if (!inhibit_profiling_info && (i % 10000 == 0)) + { + printf("."); + fflush(stdout); + } + } + + if (!inhibit_profiling_info) + { + printf(" DONE!\n"); + } + + return res; +} + +template +void batch_to_special(std::vector &vec) +{ + enter_block("Batch-convert elements to special form"); + + std::vector non_zero_vec; + for (size_t i = 0; i < vec.size(); ++i) + { + if (!vec[i].is_zero()) + { + non_zero_vec.emplace_back(vec[i]); + } + } + + batch_to_special_all_non_zeros(non_zero_vec); + auto it = non_zero_vec.begin(); + T zero_special = T::zero(); + zero_special.to_special(); + + for (size_t i = 0; i < vec.size(); ++i) + { + if (!vec[i].is_zero()) + { + vec[i] = *it; + ++it; + } + else + { + vec[i] = zero_special; + } + } + leave_block("Batch-convert elements to special form"); +} + +} // libsnark + +#endif // MULTIEXP_TCC_ diff --git a/src/snark/libsnark/algebra/scalar_multiplication/wnaf.hpp b/src/snark/libsnark/algebra/scalar_multiplication/wnaf.hpp new file mode 100644 index 000000000..a7ecd598e --- /dev/null +++ b/src/snark/libsnark/algebra/scalar_multiplication/wnaf.hpp @@ -0,0 +1,39 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for wNAF ("width-w Non-Adjacent Form") exponentiation routines. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef WNAF_HPP_ +#define WNAF_HPP_ + +namespace libsnark { + +/** + * Find the wNAF representation of the given scalar relative to the given window size. + */ +template +std::vector find_wnaf(const size_t window_size, const bigint &scalar); + +/** + * In additive notation, use wNAF exponentiation (with the given window size) to compute scalar * base. + */ +template +T fixed_window_wnaf_exp(const size_t window_size, const T &base, const bigint &scalar); + +/** + * In additive notation, use wNAF exponentiation (with the window size determined by T) to compute scalar * base. + */ +template +T opt_window_wnaf_exp(const T &base, const bigint &scalar, const size_t scalar_bits); + +} // libsnark + +#include "algebra/scalar_multiplication/wnaf.tcc" + +#endif // WNAF_HPP_ diff --git a/src/snark/libsnark/algebra/scalar_multiplication/wnaf.tcc b/src/snark/libsnark/algebra/scalar_multiplication/wnaf.tcc new file mode 100644 index 000000000..a5e47e8e2 --- /dev/null +++ b/src/snark/libsnark/algebra/scalar_multiplication/wnaf.tcc @@ -0,0 +1,123 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for wNAF ("weighted Non-Adjacent Form") exponentiation routines. + + See wnaf.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef WNAF_TCC_ +#define WNAF_TCC_ + +namespace libsnark { + +template +std::vector find_wnaf(const size_t window_size, const bigint &scalar) +{ + const size_t length = scalar.max_bits(); // upper bound + std::vector res(length+1); + bigint c = scalar; + long j = 0; + while (!c.is_zero()) + { + long u; + if ((c.data[0] & 1) == 1) + { + u = c.data[0] % (1u << (window_size+1)); + if (u > (1 << window_size)) + { + u = u - (1 << (window_size+1)); + } + + if (u > 0) + { + mpn_sub_1(c.data, c.data, n, u); + } + else + { + mpn_add_1(c.data, c.data, n, -u); + } + } + else + { + u = 0; + } + res[j] = u; + ++j; + + mpn_rshift(c.data, c.data, n, 1); // c = c/2 + } + + return res; +} + +template +T fixed_window_wnaf_exp(const size_t window_size, const T &base, const bigint &scalar) +{ + std::vector naf = find_wnaf(window_size, scalar); + std::vector table(1ul<<(window_size-1)); + T tmp = base; + T dbl = base.dbl(); + for (size_t i = 0; i < 1ul<<(window_size-1); ++i) + { + table[i] = tmp; + tmp = tmp + dbl; + } + + T res = T::zero(); + bool found_nonzero = false; + for (long i = naf.size()-1; i >= 0; --i) + { + if (found_nonzero) + { + res = res.dbl(); + } + + if (naf[i] != 0) + { + found_nonzero = true; + if (naf[i] > 0) + { + res = res + table[naf[i]/2]; + } + else + { + res = res - table[(-naf[i])/2]; + } + } + } + + return res; +} + +template +T opt_window_wnaf_exp(const T &base, const bigint &scalar, const size_t scalar_bits) +{ + size_t best = 0; + for (long i = T::wnaf_window_table.size() - 1; i >= 0; --i) + { + if (scalar_bits >= T::wnaf_window_table[i]) + { + best = i+1; + break; + } + } + + if (best > 0) + { + return fixed_window_wnaf_exp(best, base, scalar); + } + else + { + return scalar * base; + } +} + +} // libsnark + +#endif // WNAF_TCC_ diff --git a/src/snark/libsnark/common/assert_except.hpp b/src/snark/libsnark/common/assert_except.hpp new file mode 100644 index 000000000..781923044 --- /dev/null +++ b/src/snark/libsnark/common/assert_except.hpp @@ -0,0 +1,12 @@ +#ifndef ASSERT_except_H +#define ASSERT_except_H + +#include + +inline void assert_except(bool condition) { + if (!condition) { + throw std::runtime_error("Assertion failed."); + } +} + +#endif diff --git a/src/snark/libsnark/common/data_structures/accumulation_vector.hpp b/src/snark/libsnark/common/data_structures/accumulation_vector.hpp new file mode 100644 index 000000000..37e0c9841 --- /dev/null +++ b/src/snark/libsnark/common/data_structures/accumulation_vector.hpp @@ -0,0 +1,74 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for an accumulation vector. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef ACCUMULATION_VECTOR_HPP_ +#define ACCUMULATION_VECTOR_HPP_ + +#include "common/data_structures/sparse_vector.hpp" + +namespace libsnark { + +template +class accumulation_vector; + +template +std::ostream& operator<<(std::ostream &out, const accumulation_vector &v); + +template +std::istream& operator>>(std::istream &in, accumulation_vector &v); + +/** + * An accumulation vector comprises an accumulation value and a sparse vector. + * The method "accumulate_chunk" allows one to accumlate portions of the sparse + * vector into the accumualation value. + */ +template +class accumulation_vector { +public: + T first; + sparse_vector rest; + + accumulation_vector() = default; + accumulation_vector(const accumulation_vector &other) = default; + accumulation_vector(accumulation_vector &&other) = default; + accumulation_vector(T &&first, sparse_vector &&rest) : first(std::move(first)), rest(std::move(rest)) {}; + accumulation_vector(T &&first, std::vector &&v) : first(std::move(first)), rest(std::move(v)) {} + accumulation_vector(std::vector &&v) : first(T::zero()), rest(std::move(v)) {}; + + accumulation_vector& operator=(const accumulation_vector &other) = default; + accumulation_vector& operator=(accumulation_vector &&other) = default; + + bool operator==(const accumulation_vector &other) const; + + bool is_fully_accumulated() const; + + size_t domain_size() const; + size_t size() const; + size_t size_in_bits() const; + + template + accumulation_vector accumulate_chunk(const typename std::vector::const_iterator &it_begin, + const typename std::vector::const_iterator &it_end, + const size_t offset) const; + +}; + +template +std::ostream& operator<<(std::ostream &out, const accumulation_vector &v); + +template +std::istream& operator>>(std::istream &in, accumulation_vector &v); + +} // libsnark + +#include "common/data_structures/accumulation_vector.tcc" + +#endif // ACCUMULATION_VECTOR_HPP_ diff --git a/src/snark/libsnark/common/data_structures/accumulation_vector.tcc b/src/snark/libsnark/common/data_structures/accumulation_vector.tcc new file mode 100644 index 000000000..9e524aba7 --- /dev/null +++ b/src/snark/libsnark/common/data_structures/accumulation_vector.tcc @@ -0,0 +1,84 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for an accumulation vector. + + See accumulation_vector.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef ACCUMULATION_VECTOR_TCC_ +#define ACCUMULATION_VECTOR_TCC_ + +namespace libsnark { + +template +bool accumulation_vector::operator==(const accumulation_vector &other) const +{ + return (this->first == other.first && this->rest == other.rest); +} + +template +bool accumulation_vector::is_fully_accumulated() const +{ + return rest.empty(); +} + +template +size_t accumulation_vector::domain_size() const +{ + return rest.domain_size(); +} + +template +size_t accumulation_vector::size() const +{ + return rest.domain_size(); +} + +template +size_t accumulation_vector::size_in_bits() const +{ + const size_t first_size_in_bits = T::size_in_bits(); + const size_t rest_size_in_bits = rest.size_in_bits(); + return first_size_in_bits + rest_size_in_bits; +} + +template +template +accumulation_vector accumulation_vector::accumulate_chunk(const typename std::vector::const_iterator &it_begin, + const typename std::vector::const_iterator &it_end, + const size_t offset) const +{ + std::pair > acc_result = rest.template accumulate(it_begin, it_end, offset); + T new_first = first + acc_result.first; + return accumulation_vector(std::move(new_first), std::move(acc_result.second)); +} + +template +std::ostream& operator<<(std::ostream& out, const accumulation_vector &v) +{ + out << v.first << OUTPUT_NEWLINE; + out << v.rest << OUTPUT_NEWLINE; + + return out; +} + +template +std::istream& operator>>(std::istream& in, accumulation_vector &v) +{ + in >> v.first; + consume_OUTPUT_NEWLINE(in); + in >> v.rest; + consume_OUTPUT_NEWLINE(in); + + return in; +} + +} // libsnark + +#endif // ACCUMULATION_VECTOR_TCC_ diff --git a/src/snark/libsnark/common/data_structures/merkle_tree.hpp b/src/snark/libsnark/common/data_structures/merkle_tree.hpp new file mode 100644 index 000000000..6f0c851ba --- /dev/null +++ b/src/snark/libsnark/common/data_structures/merkle_tree.hpp @@ -0,0 +1,71 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for a Merkle tree. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef MERKLE_TREE_HPP_ +#define MERKLE_TREE_HPP_ + +#include +#include +#include "common/utils.hpp" + +namespace libsnark { + +/** + * A Merkle tree is maintained as two maps: + * - a map from addresses to values, and + * - a map from addresses to hashes. + * + * The second map maintains the intermediate hashes of a Merkle tree + * built atop the values currently stored in the tree (the + * implementation admits a very efficient support for sparse + * trees). Besides offering methods to load and store values, the + * class offers methods to retrieve the root of the Merkle tree and to + * obtain the authentication paths for (the value at) a given address. + */ + +typedef bit_vector merkle_authentication_node; +typedef std::vector merkle_authentication_path; + +template +class merkle_tree { +private: + + typedef typename HashT::hash_value_type hash_value_type; + typedef typename HashT::merkle_authentication_path_type merkle_authentication_path_type; + +public: + + std::vector hash_defaults; + std::map values; + std::map hashes; + + size_t depth; + size_t value_size; + size_t digest_size; + + merkle_tree(const size_t depth, const size_t value_size); + merkle_tree(const size_t depth, const size_t value_size, const std::vector &contents_as_vector); + merkle_tree(const size_t depth, const size_t value_size, const std::map &contents); + + bit_vector get_value(const size_t address) const; + void set_value(const size_t address, const bit_vector &value); + + hash_value_type get_root() const; + merkle_authentication_path_type get_path(const size_t address) const; + + void dump() const; +}; + +} // libsnark + +#include "common/data_structures/merkle_tree.tcc" + +#endif // MERKLE_TREE_HPP_ diff --git a/src/snark/libsnark/common/data_structures/merkle_tree.tcc b/src/snark/libsnark/common/data_structures/merkle_tree.tcc new file mode 100644 index 000000000..281700b33 --- /dev/null +++ b/src/snark/libsnark/common/data_structures/merkle_tree.tcc @@ -0,0 +1,246 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for Merkle tree. + + See merkle_tree.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef MERKLE_TREE_TCC +#define MERKLE_TREE_TCC + +#include + +#include "common/profiling.hpp" +#include "common/utils.hpp" + +namespace libsnark { + +template +typename HashT::hash_value_type two_to_one_CRH(const typename HashT::hash_value_type &l, + const typename HashT::hash_value_type &r) +{ + typename HashT::hash_value_type new_input; + new_input.insert(new_input.end(), l.begin(), l.end()); + new_input.insert(new_input.end(), r.begin(), r.end()); + + const size_t digest_size = HashT::get_digest_len(); + assert(l.size() == digest_size); + assert(r.size() == digest_size); + + return HashT::get_hash(new_input); +} + +template +merkle_tree::merkle_tree(const size_t depth, const size_t value_size) : + depth(depth), value_size(value_size) +{ + assert(depth < sizeof(size_t) * 8); + + digest_size = HashT::get_digest_len(); + assert(value_size <= digest_size); + + hash_value_type last(digest_size); + hash_defaults.reserve(depth+1); + hash_defaults.emplace_back(last); + for (size_t i = 0; i < depth; ++i) + { + last = two_to_one_CRH(last, last); + hash_defaults.emplace_back(last); + } + + std::reverse(hash_defaults.begin(), hash_defaults.end()); +} + +template +merkle_tree::merkle_tree(const size_t depth, + const size_t value_size, + const std::vector &contents_as_vector) : + merkle_tree(depth, value_size) +{ + assert(log2(contents_as_vector.size()) <= depth); + for (size_t address = 0; address < contents_as_vector.size(); ++address) + { + const size_t idx = address + (1ul< 0; --layer) + { + for (size_t idx = idx_begin; idx < idx_end; idx += 2) + { + hash_value_type l = hashes[idx]; // this is sound, because idx_begin is always a left child + hash_value_type r = (idx + 1 < idx_end ? hashes[idx+1] : hash_defaults[layer]); + + hash_value_type h = two_to_one_CRH(l, r); + hashes[(idx-1)/2] = h; + } + + idx_begin = (idx_begin-1)/2; + idx_end = (idx_end-1)/2; + } +} + +template +merkle_tree::merkle_tree(const size_t depth, + const size_t value_size, + const std::map &contents) : + merkle_tree(depth, value_size) +{ + + if (!contents.empty()) + { + assert(contents.rbegin()->first < 1ul<first; + const bit_vector value = it->second; + const size_t idx = address + (1ul< 0; --layer) + { + auto next_last_it = hashes.begin(); + + for (auto it = hashes.begin(); it != last_it; ++it) + { + const size_t idx = it->first; + const hash_value_type hash = it->second; + + if (idx % 2 == 0) + { + // this is the right child of its parent and by invariant we are missing the left child + hashes[(idx-1)/2] = two_to_one_CRH(hash_defaults[layer], hash); + } + else + { + if (std::next(it) == last_it || std::next(it)->first != idx + 1) + { + // this is the left child of its parent and is missing its right child + hashes[(idx-1)/2] = two_to_one_CRH(hash, hash_defaults[layer]); + } + else + { + // typical case: this is the left child of the parent and adjecent to it there is a right child + hashes[(idx-1)/2] = two_to_one_CRH(hash, std::next(it)->second); + ++it; + } + } + } + + last_it = next_last_it; + } + } +} + +template +bit_vector merkle_tree::get_value(const size_t address) const +{ + assert(log2(address) <= depth); + + auto it = values.find(address); + bit_vector padded_result = (it == values.end() ? bit_vector(digest_size) : it->second); + padded_result.resize(value_size); + + return padded_result; +} + +template +void merkle_tree::set_value(const size_t address, + const bit_vector &value) +{ + assert(log2(address) <= depth); + size_t idx = address + (1ul<=0; --layer) + { + idx = (idx-1)/2; + + auto it = hashes.find(2*idx+1); + hash_value_type l = (it == hashes.end() ? hash_defaults[layer+1] : it->second); + + it = hashes.find(2*idx+2); + hash_value_type r = (it == hashes.end() ? hash_defaults[layer+1] : it->second); + + hash_value_type h = two_to_one_CRH(l, r); + hashes[idx] = h; + } +} + +template +typename HashT::hash_value_type merkle_tree::get_root() const +{ + auto it = hashes.find(0); + return (it == hashes.end() ? hash_defaults[0] : it->second); +} + +template +typename HashT::merkle_authentication_path_type merkle_tree::get_path(const size_t address) const +{ + typename HashT::merkle_authentication_path_type result(depth); + assert(log2(address) <= depth); + size_t idx = address + (1ul< 0; --layer) + { + size_t sibling_idx = ((idx + 1) ^ 1) - 1; + auto it = hashes.find(sibling_idx); + if (layer == depth) + { + auto it2 = values.find(sibling_idx - ((1ul<second); + result[layer-1].resize(digest_size); + } + else + { + result[layer-1] = (it == hashes.end() ? hash_defaults[layer] : it->second); + } + + idx = (idx-1)/2; + } + + return result; +} + +template +void merkle_tree::dump() const +{ + for (size_t i = 0; i < 1ul< ", i); + const bit_vector value = (it == values.end() ? bit_vector(value_size) : it->second); + for (bool b : value) + { + printf("%d", b ? 1 : 0); + } + printf("\n"); + } + printf("\n"); +} + +} // libsnark + +#endif // MERKLE_TREE_TCC diff --git a/src/snark/libsnark/common/data_structures/sparse_vector.hpp b/src/snark/libsnark/common/data_structures/sparse_vector.hpp new file mode 100644 index 000000000..6c5a10447 --- /dev/null +++ b/src/snark/libsnark/common/data_structures/sparse_vector.hpp @@ -0,0 +1,79 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for a sparse vector. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef SPARSE_VECTOR_HPP_ +#define SPARSE_VECTOR_HPP_ + +#include + +namespace libsnark { + +template +struct sparse_vector; + +template +std::ostream& operator<<(std::ostream &out, const sparse_vector &v); + +template +std::istream& operator>>(std::istream &in, sparse_vector &v); + +/** + * A sparse vector is a list of indices along with corresponding values. + * The indices are selected from the set {0,1,...,domain_size-1}. + */ +template +struct sparse_vector { + + std::vector indices; + std::vector values; + size_t domain_size_ = 0; + + sparse_vector() = default; + sparse_vector(const sparse_vector &other) = default; + sparse_vector(sparse_vector &&other) = default; + sparse_vector(std::vector &&v); /* constructor from std::vector */ + + sparse_vector& operator=(const sparse_vector &other) = default; + sparse_vector& operator=(sparse_vector &&other) = default; + + T operator[](const size_t idx) const; + + bool operator==(const sparse_vector &other) const; + bool operator==(const std::vector &other) const; + + bool is_valid() const; + bool empty() const; + + size_t domain_size() const; // return domain_size_ + size_t size() const; // return the number of indices (representing the number of non-zero entries) + size_t size_in_bits() const; // return the number bits needed to store the sparse vector + + /* return a pair consisting of the accumulated value and the sparse vector of non-accumulated values */ + template + std::pair > accumulate(const typename std::vector::const_iterator &it_begin, + const typename std::vector::const_iterator &it_end, + const size_t offset) const; + + friend std::ostream& operator<< (std::ostream &out, const sparse_vector &v); + friend std::istream& operator>> (std::istream &in, sparse_vector &v); +}; + +template +std::ostream& operator<<(std::ostream& out, const sparse_vector &v); + +template +std::istream& operator>>(std::istream& in, sparse_vector &v); + +} // libsnark + +#include "common/data_structures/sparse_vector.tcc" + +#endif // SPARSE_VECTOR_HPP_ diff --git a/src/snark/libsnark/common/data_structures/sparse_vector.tcc b/src/snark/libsnark/common/data_structures/sparse_vector.tcc new file mode 100644 index 000000000..cfc5d7559 --- /dev/null +++ b/src/snark/libsnark/common/data_structures/sparse_vector.tcc @@ -0,0 +1,316 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for a sparse vector. + + See sparse_vector.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef SPARSE_VECTOR_TCC_ +#define SPARSE_VECTOR_TCC_ + +#include "algebra/scalar_multiplication/multiexp.hpp" + +#include + +namespace libsnark { + +template +sparse_vector::sparse_vector(std::vector &&v) : + values(std::move(v)), domain_size_(values.size()) +{ + indices.resize(domain_size_); + std::iota(indices.begin(), indices.end(), 0); +} + +template +T sparse_vector::operator[](const size_t idx) const +{ + auto it = std::lower_bound(indices.begin(), indices.end(), idx); + return (it != indices.end() && *it == idx) ? values[it - indices.begin()] : T(); +} + +template +bool sparse_vector::operator==(const sparse_vector &other) const +{ + if (this->domain_size_ != other.domain_size_) + { + return false; + } + + size_t this_pos = 0, other_pos = 0; + while (this_pos < this->indices.size() && other_pos < other.indices.size()) + { + if (this->indices[this_pos] == other.indices[other_pos]) + { + if (this->values[this_pos] != other.values[other_pos]) + { + return false; + } + ++this_pos; + ++other_pos; + } + else if (this->indices[this_pos] < other.indices[other_pos]) + { + if (!this->values[this_pos].is_zero()) + { + return false; + } + ++this_pos; + } + else + { + if (!other.values[other_pos].is_zero()) + { + return false; + } + ++other_pos; + } + } + + /* at least one of the vectors has been exhausted, so other must be empty */ + while (this_pos < this->indices.size()) + { + if (!this->values[this_pos].is_zero()) + { + return false; + } + ++this_pos; + } + + while (other_pos < other.indices.size()) + { + if (!other.values[other_pos].is_zero()) + { + return false; + } + ++other_pos; + } + + return true; +} + +template +bool sparse_vector::operator==(const std::vector &other) const +{ + if (this->domain_size_ < other.size()) + { + return false; + } + + size_t j = 0; + for (size_t i = 0; i < other.size(); ++i) + { + if (this->indices[j] == i) + { + if (this->values[j] != other[j]) + { + return false; + } + ++j; + } + else + { + if (!other[j].is_zero()) + { + return false; + } + } + } + + return true; +} + +template +bool sparse_vector::is_valid() const +{ + if (values.size() == indices.size() && values.size() <= domain_size_) + { + return false; + } + + for (size_t i = 0; i + 1 < indices.size(); ++i) + { + if (indices[i] >= indices[i+1]) + { + return false; + } + } + + if (!indices.empty() && indices[indices.size()-1] >= domain_size_) + { + return false; + } + + return true; +} + +template +bool sparse_vector::empty() const +{ + return indices.empty(); +} + +template +size_t sparse_vector::domain_size() const +{ + return domain_size_; +} + +template +size_t sparse_vector::size() const +{ + return indices.size(); +} + +template +size_t sparse_vector::size_in_bits() const +{ + return indices.size() * (sizeof(size_t) * 8 + T::size_in_bits()); +} + +template +template +std::pair > sparse_vector::accumulate(const typename std::vector::const_iterator &it_begin, + const typename std::vector::const_iterator &it_end, + const size_t offset) const +{ + // TODO: does not really belong here. + const size_t chunks = 1; + const bool use_multiexp = true; + + T accumulated_value = T::zero(); + sparse_vector resulting_vector; + resulting_vector.domain_size_ = domain_size_; + + const size_t range_len = it_end - it_begin; + bool in_block = false; + size_t first_pos = -1, last_pos = -1; // g++ -flto emits unitialized warning, even though in_block guards for such cases. + + for (size_t i = 0; i < indices.size(); ++i) + { + const bool matching_pos = (offset <= indices[i] && indices[i] < offset + range_len); + // printf("i = %zu, pos[i] = %zu, offset = %zu, w_size = %zu\n", i, indices[i], offset, w_size); + bool copy_over; + + if (in_block) + { + if (matching_pos && last_pos == i-1) + { + // block can be extended, do it + last_pos = i; + copy_over = false; + } + else + { + // block has ended here + in_block = false; + copy_over = true; + +#ifdef DEBUG + print_indent(); printf("doing multiexp for w_%zu ... w_%zu\n", indices[first_pos], indices[last_pos]); +#endif + accumulated_value = accumulated_value + multi_exp(values.begin() + first_pos, + values.begin() + last_pos + 1, + it_begin + (indices[first_pos] - offset), + it_begin + (indices[last_pos] - offset) + 1, + chunks, use_multiexp); + } + } + else + { + if (matching_pos) + { + // block can be started + first_pos = i; + last_pos = i; + in_block = true; + copy_over = false; + } + else + { + copy_over = true; + } + } + + if (copy_over) + { + resulting_vector.indices.emplace_back(indices[i]); + resulting_vector.values.emplace_back(values[i]); + } + } + + if (in_block) + { +#ifdef DEBUG + print_indent(); printf("doing multiexp for w_%zu ... w_%zu\n", indices[first_pos], indices[last_pos]); +#endif + accumulated_value = accumulated_value + multi_exp(values.begin() + first_pos, + values.begin() + last_pos + 1, + it_begin + (indices[first_pos] - offset), + it_begin + (indices[last_pos] - offset) + 1, + chunks, use_multiexp); + } + + return std::make_pair(accumulated_value, resulting_vector); +} + +template +std::ostream& operator<<(std::ostream& out, const sparse_vector &v) +{ + out << v.domain_size_ << "\n"; + out << v.indices.size() << "\n"; + for (const size_t& i : v.indices) + { + out << i << "\n"; + } + + out << v.values.size() << "\n"; + for (const T& t : v.values) + { + out << t << OUTPUT_NEWLINE; + } + + return out; +} + +template +std::istream& operator>>(std::istream& in, sparse_vector &v) +{ + in >> v.domain_size_; + consume_newline(in); + + size_t s; + in >> s; + consume_newline(in); + v.indices.resize(s); + for (size_t i = 0; i < s; ++i) + { + in >> v.indices[i]; + consume_newline(in); + } + + v.values.clear(); + in >> s; + consume_newline(in); + v.values.reserve(s); + + for (size_t i = 0; i < s; ++i) + { + T t; + in >> t; + consume_OUTPUT_NEWLINE(in); + v.values.emplace_back(t); + } + + return in; +} + +} // libsnark + +#endif // SPARSE_VECTOR_TCC_ diff --git a/src/snark/libsnark/common/default_types/ec_pp.hpp b/src/snark/libsnark/common/default_types/ec_pp.hpp new file mode 100644 index 000000000..b08c2da88 --- /dev/null +++ b/src/snark/libsnark/common/default_types/ec_pp.hpp @@ -0,0 +1,53 @@ +/** @file + ***************************************************************************** + + This file defines default_ec_pp based on the CURVE=... make flag, which selects + which elliptic curve is used to implement group arithmetic and pairings. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef EC_PP_HPP_ +#define EC_PP_HPP_ + +/************************ Pick the elliptic curve ****************************/ + +#ifdef CURVE_ALT_BN128 +#include "algebra/curves/alt_bn128/alt_bn128_pp.hpp" +namespace libsnark { +typedef alt_bn128_pp default_ec_pp; +} // libsnark +#endif + +#ifdef CURVE_BN128 +#include "algebra/curves/bn128/bn128_pp.hpp" +namespace libsnark { +typedef bn128_pp default_ec_pp; +} // libsnark +#endif + +#ifdef CURVE_EDWARDS +#include "algebra/curves/edwards/edwards_pp.hpp" +namespace libsnark { +typedef edwards_pp default_ec_pp; +} // libsnark +#endif + +#ifdef CURVE_MNT4 +#include "algebra/curves/mnt/mnt4/mnt4_pp.hpp" +namespace libsnark { +typedef mnt4_pp default_ec_pp; +} // libsnark +#endif + +#ifdef CURVE_MNT6 +#include "algebra/curves/mnt/mnt6/mnt6_pp.hpp" +namespace libsnark { +typedef mnt6_pp default_ec_pp; +} // libsnark +#endif + +#endif // EC_PP_HPP_ diff --git a/src/snark/libsnark/common/default_types/r1cs_ppzksnark_pp.hpp b/src/snark/libsnark/common/default_types/r1cs_ppzksnark_pp.hpp new file mode 100644 index 000000000..c819b4a85 --- /dev/null +++ b/src/snark/libsnark/common/default_types/r1cs_ppzksnark_pp.hpp @@ -0,0 +1,22 @@ +/** @file + ***************************************************************************** + + This file defines default_r1cs_ppzksnark_pp based on the elliptic curve + choice selected in ec_pp.hpp. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef R1CS_PPZKSNARK_PP_HPP_ +#define R1CS_PPZKSNARK_PP_HPP_ + +#include "common/default_types/ec_pp.hpp" + +namespace libsnark { +typedef default_ec_pp default_r1cs_ppzksnark_pp; +} // libsnark + +#endif // R1CS_PPZKSNARK_PP_HPP_ diff --git a/src/snark/libsnark/common/profiling.cpp b/src/snark/libsnark/common/profiling.cpp new file mode 100644 index 000000000..ae8f85a4c --- /dev/null +++ b/src/snark/libsnark/common/profiling.cpp @@ -0,0 +1,379 @@ +/** @file + ***************************************************************************** + + Implementation of functions for profiling code blocks. + + See profiling.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include "common/profiling.hpp" +#include +#include +#include +#include +#include +#include +#include +#include "common/default_types/ec_pp.hpp" +#include "common/utils.hpp" + +#ifndef NO_PROCPS +#include +#endif + +namespace libsnark { + +long long get_nsec_time() +{ + auto timepoint = std::chrono::high_resolution_clock::now(); + return std::chrono::duration_cast(timepoint.time_since_epoch()).count(); +} + +/* Return total CPU time consumed by all threads of the process, in nanoseconds. */ +long long get_nsec_cpu_time() +{ + ::timespec ts; + if ( ::clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) ) + throw ::std::runtime_error("clock_gettime(CLOCK_PROCESS_CPUTIME_ID) failed"); + // If we expected this to work, don't silently ignore failures, because that would hide the problem and incur an unnecessarily system-call overhead. So if we ever observe this exception, we should probably add a suitable #ifdef . + //TODO: clock_gettime(CLOCK_PROCESS_CPUTIME_ID) is not supported by native Windows. What about Cygwin? Should we #ifdef on CLOCK_PROCESS_CPUTIME_ID or on __linux__? + return ts.tv_sec * 1000000000ll + ts.tv_nsec; +} + +long long start_time, last_time; +long long start_cpu_time, last_cpu_time; + +void start_profiling() +{ + printf("Reset time counters for profiling\n"); + + last_time = start_time = get_nsec_time(); + last_cpu_time = start_cpu_time = get_nsec_cpu_time(); +} + +std::map invocation_counts; +std::map enter_times; +std::map last_times; +std::map cumulative_times; +//TODO: Instead of analogous maps for time and cpu_time, use a single struct-valued map +std::map enter_cpu_times; +std::map last_cpu_times; +std::map, long long> op_counts; +std::map, long long> cumulative_op_counts; // ((msg, data_point), value) + // TODO: Convert op_counts and cumulative_op_counts from pair to structs +size_t indentation = 0; + +std::vector block_names; + +std::list > op_data_points = { +#ifdef PROFILE_OP_COUNTS + std::make_pair("Fradd", &Fr::add_cnt), + std::make_pair("Frsub", &Fr::sub_cnt), + std::make_pair("Frmul", &Fr::mul_cnt), + std::make_pair("Frinv", &Fr::inv_cnt), + std::make_pair("Fqadd", &Fq::add_cnt), + std::make_pair("Fqsub", &Fq::sub_cnt), + std::make_pair("Fqmul", &Fq::mul_cnt), + std::make_pair("Fqinv", &Fq::inv_cnt), + std::make_pair("G1add", &G1::add_cnt), + std::make_pair("G1dbl", &G1::dbl_cnt), + std::make_pair("G2add", &G2::add_cnt), + std::make_pair("G2dbl", &G2::dbl_cnt) +#endif +}; + +bool inhibit_profiling_info = false; +bool inhibit_profiling_counters = false; + +void clear_profiling_counters() +{ + invocation_counts.clear(); + last_times.clear(); + last_cpu_times.clear(); + cumulative_times.clear(); +} + +void print_cumulative_time_entry(const std::string &key, const long long factor) +{ + const double total_ms = (cumulative_times.at(key) * 1e-6); + const size_t cnt = invocation_counts.at(key); + const double avg_ms = total_ms / cnt; + printf(" %-45s: %12.5fms = %lld * %0.5fms (%zu invocations, %0.5fms = %lld * %0.5fms per invocation)\n", key.c_str(), total_ms, factor, total_ms/factor, cnt, avg_ms, factor, avg_ms/factor); +} + +void print_cumulative_times(const long long factor) +{ + printf("Dumping times:\n"); + for (auto& kv : cumulative_times) + { + print_cumulative_time_entry(kv.first, factor); + } +} + +void print_cumulative_op_counts(const bool only_fq) +{ +#ifdef PROFILE_OP_COUNTS + printf("Dumping operation counts:\n"); + for (auto& msg : invocation_counts) + { + printf(" %-45s: ", msg.first.c_str()); + bool first = true; + for (auto& data_point : op_data_points) + { + if (only_fq && data_point.first.compare(0, 2, "Fq") != 0) + { + continue; + } + + if (!first) + { + printf(", "); + } + printf("%-5s = %7.0f (%3zu)", + data_point.first.c_str(), + 1. * cumulative_op_counts[std::make_pair(msg.first, data_point.first)] / msg.second, + msg.second); + first = false; + } + printf("\n"); + } +#else + UNUSED(only_fq); +#endif +} + +void print_op_profiling(const std::string &msg) +{ +#ifdef PROFILE_OP_COUNTS + printf("\n"); + print_indent(); + + printf("(opcounts) = ("); + bool first = true; + for (std::pair p : op_data_points) + { + if (!first) + { + printf(", "); + } + + printf("%s=%lld", p.first.c_str(), *(p.second)-op_counts[std::make_pair(msg, p.first)]); + first = false; + } + printf(")"); +#else + UNUSED(msg); +#endif +} + +static void print_times_from_last_and_start(long long now, long long last, + long long cpu_now, long long cpu_last) +{ + long long time_from_start = now - start_time; + long long time_from_last = now - last; + + long long cpu_time_from_start = cpu_now - start_cpu_time; + long long cpu_time_from_last = cpu_now - cpu_last; + + if (time_from_last != 0) { + double parallelism_from_last = 1.0 * cpu_time_from_last / time_from_last; + printf("[%0.4fs x%0.2f]", time_from_last * 1e-9, parallelism_from_last); + } else { + printf("[ ]"); + } + if (time_from_start != 0) { + double parallelism_from_start = 1.0 * cpu_time_from_start / time_from_start; + printf("\t(%0.4fs x%0.2f from start)", time_from_start * 1e-9, parallelism_from_start); + } +} + +void print_time(const char* msg) +{ + if (inhibit_profiling_info) + { + return; + } + + long long now = get_nsec_time(); + long long cpu_now = get_nsec_cpu_time(); + + printf("%-35s\t", msg); + print_times_from_last_and_start(now, last_time, cpu_now, last_cpu_time); +#ifdef PROFILE_OP_COUNTS + print_op_profiling(msg); +#endif + printf("\n"); + + fflush(stdout); + last_time = now; + last_cpu_time = cpu_now; +} + +void print_header(const char *msg) +{ + printf("\n================================================================================\n"); + printf("%s\n", msg); + printf("================================================================================\n\n"); +} + +void print_indent() +{ + for (size_t i = 0; i < indentation; ++i) + { + printf(" "); + } +} + +void op_profiling_enter(const std::string &msg) +{ + for (std::pair p : op_data_points) + { + op_counts[std::make_pair(msg, p.first)] = *(p.second); + } +} + +void enter_block(const std::string &msg, const bool indent) +{ + if (inhibit_profiling_counters) + { + return; + } + + block_names.emplace_back(msg); + long long t = get_nsec_time(); + enter_times[msg] = t; + long long cpu_t = get_nsec_cpu_time(); + enter_cpu_times[msg] = cpu_t; + + if (inhibit_profiling_info) + { + return; + } + +#ifdef MULTICORE +#pragma omp critical +#endif + { + op_profiling_enter(msg); + + print_indent(); + printf("(enter) %-35s\t", msg.c_str()); + print_times_from_last_and_start(t, t, cpu_t, cpu_t); + printf("\n"); + fflush(stdout); + + if (indent) + { + ++indentation; + } + } +} + +void leave_block(const std::string &msg, const bool indent) +{ + if (inhibit_profiling_counters) + { + return; + } + +#ifndef MULTICORE + assert(*(--block_names.end()) == msg); +#endif + block_names.pop_back(); + + ++invocation_counts[msg]; + + long long t = get_nsec_time(); + last_times[msg] = (t - enter_times[msg]); + cumulative_times[msg] += (t - enter_times[msg]); + + long long cpu_t = get_nsec_cpu_time(); + last_cpu_times[msg] = (cpu_t - enter_cpu_times[msg]); + +#ifdef PROFILE_OP_COUNTS + for (std::pair p : op_data_points) + { + cumulative_op_counts[std::make_pair(msg, p.first)] += *(p.second)-op_counts[std::make_pair(msg, p.first)]; + } +#endif + + if (inhibit_profiling_info) + { + return; + } + +#ifdef MULTICORE +#pragma omp critical +#endif + { + if (indent) + { + --indentation; + } + + print_indent(); + printf("(leave) %-35s\t", msg.c_str()); + print_times_from_last_and_start(t, enter_times[msg], cpu_t, enter_cpu_times[msg]); + print_op_profiling(msg); + printf("\n"); + fflush(stdout); + } +} + +void print_mem(const std::string &s) +{ +#ifndef NO_PROCPS + struct proc_t usage; + look_up_our_self(&usage); + if (s.empty()) + { + printf("* Peak vsize (physical memory+swap) in mebibytes: %lu\n", usage.vsize >> 20); + } + else + { + printf("* Peak vsize (physical memory+swap) in mebibytes (%s): %lu\n", s.c_str(), usage.vsize >> 20); + } +#else + printf("* Memory profiling not supported in NO_PROCPS mode\n"); +#endif +} + +void print_compilation_info() +{ +#ifdef __GNUC__ + printf("g++ version: %s\n", __VERSION__); + //printf("Compiled on %s %s\n", __DATE__, __TIME__); +#endif +#ifdef STATIC + printf("STATIC: yes\n"); +#else + printf("STATIC: no\n"); +#endif +#ifdef MULTICORE + printf("MULTICORE: yes\n"); +#else + printf("MULTICORE: no\n"); +#endif +#ifdef DEBUG + printf("DEBUG: yes\n"); +#else + printf("DEBUG: no\n"); +#endif +#ifdef PROFILE_OP_COUNTS + printf("PROFILE_OP_COUNTS: yes\n"); +#else + printf("PROFILE_OP_COUNTS: no\n"); +#endif +#ifdef _GLIBCXX_DEBUG + printf("_GLIBCXX_DEBUG: yes\n"); +#else + printf("_GLIBCXX_DEBUG: no\n"); +#endif +} + +} // libsnark diff --git a/src/snark/libsnark/common/profiling.hpp b/src/snark/libsnark/common/profiling.hpp new file mode 100644 index 000000000..9619117f4 --- /dev/null +++ b/src/snark/libsnark/common/profiling.hpp @@ -0,0 +1,51 @@ +/** @file + ***************************************************************************** + + Declaration of functions for profiling code blocks. + + Reports time, operation counts, memory usage, and others. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef PROFILING_HPP_ +#define PROFILING_HPP_ + +#include +#include +#include +#include + +namespace libsnark { + +void start_profiling(); +long long get_nsec_time(); +void print_time(const char* msg); +void print_header(const char* msg); + +void print_indent(); + +extern bool inhibit_profiling_info; +extern bool inhibit_profiling_counters; +extern std::map invocation_counts; +extern std::map last_times; +extern std::map cumulative_times; + +void clear_profiling_counters(); + +void print_cumulative_time_entry(const std::string &key, const long long factor=1); +void print_cumulative_times(const long long factor=1); +void print_cumulative_op_counts(const bool only_fq=false); + +void enter_block(const std::string &msg, const bool indent=true); +void leave_block(const std::string &msg, const bool indent=true); + +void print_mem(const std::string &s = ""); +void print_compilation_info(); + +} // libsnark + +#endif // PROFILING_HPP_ diff --git a/src/snark/libsnark/common/serialization.hpp b/src/snark/libsnark/common/serialization.hpp new file mode 100644 index 000000000..6757e1966 --- /dev/null +++ b/src/snark/libsnark/common/serialization.hpp @@ -0,0 +1,104 @@ +/** @file + ***************************************************************************** + + Declaration of serialization routines and constants. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef SERIALIZATION_HPP_ +#define SERIALIZATION_HPP_ + +#include +#include +#include +#include +#include + +namespace libsnark { + +/* + * @todo + * The serialization is fragile. Should be rewritten using a standard, portable-format + * library like boost::serialize. + * + * However, for now the following conventions are used within the code. + * + * All algebraic objects support either binary or decimal output using + * the standard C++ stream operators (operator<<, operator>>). + * + * The binary mode is activated by defining a BINARY_OUTPUT + * preprocessor macro (e.g. g++ -DBINARY_OUTPUT ...). + * + * Binary output assumes that the stream is to be binary read at its + * current position so any white space should be consumed beforehand. + * + * Consecutive algebraic objects are separated by OUTPUT_NEWLINE and + * within themselves (e.g. X and Y coordinates for field elements) with + * OUTPUT_SEPARATOR (as defined below). + * + * Therefore to dump two integers, two Fp elements and another integer + * one would: + * + * out << 3 << "\n"; + * out << 4 << "\n"; + * out << FieldT(56) << OUTPUT_NEWLINE; + * out << FieldT(78) << OUTPUT_NEWLINE; + * out << 9 << "\n"; + * + * Then reading back it its reader's responsibility (!) to consume "\n" + * after 4, but Fp::operator<< will correctly consume OUTPUT_NEWLINE. + * + * The reader should also consume "\n" after 9, so that another field + * element can be properly chained. This is especially important for + * binary output. + * + * The binary serialization of algebraic objects is currently *not* + * portable between machines of different word sizes. + */ + +#ifdef BINARY_OUTPUT +#define OUTPUT_NEWLINE "" +#define OUTPUT_SEPARATOR "" +#else +#define OUTPUT_NEWLINE "\n" +#define OUTPUT_SEPARATOR " " +#endif + +inline void consume_newline(std::istream &in); +inline void consume_OUTPUT_NEWLINE(std::istream &in); +inline void consume_OUTPUT_SEPARATOR(std::istream &in); + +inline void output_bool(std::ostream &out, const bool b); + +inline void output_bool_vector(std::ostream &out, const std::vector &v); + +template +T reserialize(const T &obj); + +template +std::ostream& operator<<(std::ostream& out, const std::vector &v); + +template +std::istream& operator>>(std::ostream& out, std::vector &v); + +template +std::ostream& operator<<(std::ostream& out, const std::map &m); + +template +std::istream& operator>>(std::istream& in, std::map &m); + +template +std::ostream& operator<<(std::ostream& out, const std::set &s); + +template +std::istream& operator>>(std::istream& in, std::set &s); + +} // libsnark + +#include "common/serialization.tcc" + +#endif // SERIALIZATION_HPP_ diff --git a/src/snark/libsnark/common/serialization.tcc b/src/snark/libsnark/common/serialization.tcc new file mode 100644 index 000000000..398f97850 --- /dev/null +++ b/src/snark/libsnark/common/serialization.tcc @@ -0,0 +1,180 @@ +/** @file + ***************************************************************************** + + Implementation of serialization routines. + + See serialization.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef SERIALIZATION_TCC_ +#define SERIALIZATION_TCC_ + +#include +#include +#include "common/utils.hpp" + +namespace libsnark { + +inline void consume_newline(std::istream &in) +{ + char c; + in.read(&c, 1); +} + +inline void consume_OUTPUT_NEWLINE(std::istream &in) +{ +#ifdef BINARY_OUTPUT + // nothing to consume + UNUSED(in); +#else + char c; + in.read(&c, 1); +#endif +} + +inline void consume_OUTPUT_SEPARATOR(std::istream &in) +{ +#ifdef BINARY_OUTPUT + // nothing to consume + UNUSED(in); +#else + char c; + in.read(&c, 1); +#endif +} + +inline void output_bool(std::ostream &out, const bool b) +{ + out << (b ? 1 : 0) << "\n"; +} + +inline void output_bool_vector(std::ostream &out, const std::vector &v) +{ + out << v.size() << "\n"; + for (const bool b : v) + { + output_bool(out, b); + } +} + +template +T reserialize(const T &obj) +{ + std::stringstream ss; + ss << obj; + T tmp; + ss >> tmp; + assert(obj == tmp); + return tmp; +} + +template +std::ostream& operator<<(std::ostream& out, const std::vector &v) +{ + static_assert(!std::is_same::value, "this does not work for std::vector"); + out << v.size() << "\n"; + for (const T& t : v) + { + out << t << OUTPUT_NEWLINE; + } + + return out; +} + +template +std::istream& operator>>(std::istream& in, std::vector &v) +{ + static_assert(!std::is_same::value, "this does not work for std::vector"); + size_t size; + in >> size; + consume_newline(in); + + v.resize(0); + for (size_t i = 0; i < size; ++i) + { + T elt; + in >> elt; + consume_OUTPUT_NEWLINE(in); + v.push_back(elt); + } + + return in; +} + +template +std::ostream& operator<<(std::ostream& out, const std::map &m) +{ + out << m.size() << "\n"; + + for (auto &it : m) + { + out << it.first << "\n"; + out << it.second << "\n"; + } + + return out; +} + +template +std::istream& operator>>(std::istream& in, std::map &m) +{ + m.clear(); + size_t size; + in >> size; + consume_newline(in); + + for (size_t i = 0; i < size; ++i) + { + T1 k; + T2 v; + in >> k; + consume_newline(in); + in >> v; + consume_newline(in); + m[k] = v; + } + + return in; +} + +template +std::ostream& operator<<(std::ostream& out, const std::set &s) +{ + out << s.size() << "\n"; + + for (auto &el : s) + { + out << el << "\n"; + } + + return out; +} + + +template +std::istream& operator>>(std::istream& in, std::set &s) +{ + s.clear(); + size_t size; + in >> size; + consume_newline(in); + + for (size_t i = 0; i < size; ++i) + { + T el; + in >> el; + consume_newline(in); + s.insert(el); + } + + return in; +} + +} + +#endif // SERIALIZATION_TCC_ diff --git a/src/snark/libsnark/common/template_utils.hpp b/src/snark/libsnark/common/template_utils.hpp new file mode 100644 index 000000000..8dbfd261d --- /dev/null +++ b/src/snark/libsnark/common/template_utils.hpp @@ -0,0 +1,26 @@ +/** @file + ***************************************************************************** + + Declaration of functions for supporting the use of templates. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef TEMPLATE_UTILS_HPP_ +#define TEMPLATE_UTILS_HPP_ + +namespace libsnark { + +/* A commonly used SFINAE helper type */ +template +struct void_type +{ + typedef void type; +}; + +} // libsnark + +#endif // TEMPLATE_UTILS_HPP_ diff --git a/src/snark/libsnark/common/utils.cpp b/src/snark/libsnark/common/utils.cpp new file mode 100644 index 000000000..dd114fdf0 --- /dev/null +++ b/src/snark/libsnark/common/utils.cpp @@ -0,0 +1,102 @@ +/** @file + ***************************************************************************** + Implementation of misc math and serialization utility functions + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include +#include +#include +#include +#include "common/utils.hpp" + +namespace libsnark { + +size_t log2(size_t n) +/* returns ceil(log2(n)), so 1ul< 1) + { + n >>= 1; + r++; + } + + return r; +} + +size_t bitreverse(size_t n, const size_t l) +{ + size_t r = 0; + for (size_t k = 0; k < l; ++k) + { + r = (r << 1) | (n & 1); + n >>= 1; + } + return r; +} + +bit_vector int_list_to_bits(const std::initializer_list &l, const size_t wordsize) +{ + bit_vector res(wordsize*l.size()); + for (size_t i = 0; i < l.size(); ++i) + { + for (size_t j = 0; j < wordsize; ++j) + { + res[i*wordsize + j] = (*(l.begin()+i) & (1ul<<(wordsize-1-j))); + } + } + return res; +} + +long long div_ceil(long long x, long long y) +{ + return (x + (y-1)) / y; +} + +bool is_little_endian() +{ + uint64_t a = 0x12345678; + unsigned char *c = (unsigned char*)(&a); + return (*c = 0x78); +} + +std::string FORMAT(const std::string &prefix, const char* format, ...) +{ + const static size_t MAX_FMT = 256; + char buf[MAX_FMT]; + va_list args; + va_start(args, format); + vsnprintf(buf, MAX_FMT, format, args); + va_end(args); + + return prefix + std::string(buf); +} + +void serialize_bit_vector(std::ostream &out, const bit_vector &v) +{ + out << v.size() << "\n"; + for (size_t i = 0; i < v.size(); ++i) + { + out << v[i] << "\n"; + } +} + +void deserialize_bit_vector(std::istream &in, bit_vector &v) +{ + size_t size; + in >> size; + v.resize(size); + for (size_t i = 0; i < size; ++i) + { + bool b; + in >> b; + v[i] = b; + } +} +} // libsnark diff --git a/src/snark/libsnark/common/utils.hpp b/src/snark/libsnark/common/utils.hpp new file mode 100644 index 000000000..d7d9e8947 --- /dev/null +++ b/src/snark/libsnark/common/utils.hpp @@ -0,0 +1,57 @@ +/** @file + ***************************************************************************** + Declaration of misc math and serialization utility functions + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef UTILS_HPP_ +#define UTILS_HPP_ + +#include +#include +#include +#include +#include + +namespace libsnark { + +typedef std::vector bit_vector; + +/// returns ceil(log2(n)), so 1ul< &l, const size_t wordsize); +long long div_ceil(long long x, long long y); + +bool is_little_endian(); + +std::string FORMAT(const std::string &prefix, const char* format, ...); + +/* A variadic template to suppress unused argument warnings */ +template +void UNUSED(Types&&...) {} + +#ifdef DEBUG +#define FMT FORMAT +#else +#define FMT(...) (UNUSED(__VA_ARGS__), "") +#endif + +void serialize_bit_vector(std::ostream &out, const bit_vector &v); +void deserialize_bit_vector(std::istream &in, bit_vector &v); + +template +size_t size_in_bits(const std::vector &v); + +#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) + +} // libsnark + +#include "common/utils.tcc" /* note that utils has a templatized part (utils.tcc) and non-templatized part (utils.cpp) */ +#endif // UTILS_HPP_ diff --git a/src/snark/libsnark/common/utils.tcc b/src/snark/libsnark/common/utils.tcc new file mode 100644 index 000000000..f97178f8c --- /dev/null +++ b/src/snark/libsnark/common/utils.tcc @@ -0,0 +1,23 @@ +/** @file + ***************************************************************************** + Implementation of templatized utility functions + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef UTILS_TCC_ +#define UTILS_TCC_ + +namespace libsnark { + +template +size_t size_in_bits(const std::vector &v) +{ + return v.size() * T::size_in_bits(); +} + +} // libsnark + +#endif // UTILS_TCC_ diff --git a/src/snark/libsnark/gadgetlib1/constraint_profiling.cpp b/src/snark/libsnark/gadgetlib1/constraint_profiling.cpp new file mode 100644 index 000000000..bc17e63bc --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/constraint_profiling.cpp @@ -0,0 +1,48 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for profiling constraints. + + See constraint_profiling.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include "gadgetlib1/constraint_profiling.hpp" +#include "common/profiling.hpp" + +namespace libsnark { + +size_t constraint_profiling_indent = 0; +std::vector constraint_profiling_table; + +size_t PRINT_CONSTRAINT_PROFILING() +{ + size_t accounted = 0; + print_indent(); + printf("Constraint profiling:\n"); + for (constraint_profiling_entry &ent : constraint_profiling_table) + { + if (ent.indent == 0) + { + accounted += ent.count; + } + + print_indent(); + for (size_t i = 0; i < ent.indent; ++i) + { + printf(" "); + } + printf("* Number of constraints in [%s]: %zu\n", ent.annotation.c_str(), ent.count); + } + + constraint_profiling_table.clear(); + constraint_profiling_indent = 0; + + return accounted; +} + +} diff --git a/src/snark/libsnark/gadgetlib1/constraint_profiling.hpp b/src/snark/libsnark/gadgetlib1/constraint_profiling.hpp new file mode 100644 index 000000000..df8a55de1 --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/constraint_profiling.hpp @@ -0,0 +1,42 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for profiling constraints. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef CONSTRAINT_PROFILING_HPP_ +#define CONSTRAINT_PROFILING_HPP_ + +#include +#include +#include +#include + +namespace libsnark { + +extern size_t constraint_profiling_indent; + +struct constraint_profiling_entry { + size_t indent; + std::string annotation; + size_t count; +}; + +extern std::vector constraint_profiling_table; + +#define PROFILE_CONSTRAINTS(pb, annotation) \ + for (size_t _num_constraints_before = pb.num_constraints(), _iter = (++constraint_profiling_indent, 0), _cp_pos = constraint_profiling_table.size(); \ + _iter == 0; \ + constraint_profiling_table.insert(constraint_profiling_table.begin() + _cp_pos, constraint_profiling_entry{--constraint_profiling_indent, annotation, pb.num_constraints() - _num_constraints_before}), \ + _iter = 1) + +size_t PRINT_CONSTRAINT_PROFILING(); // returns # of top level constraints + +} // libsnark + +#endif // CONSTRAINT_PROFILING_HPP_ diff --git a/src/snark/libsnark/gadgetlib1/examples/simple_example.hpp b/src/snark/libsnark/gadgetlib1/examples/simple_example.hpp new file mode 100644 index 000000000..faa3a9605 --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/examples/simple_example.hpp @@ -0,0 +1,23 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef SIMPLE_EXAMPLE_HPP_ +#define SIMPLE_EXAMPLE_HPP_ + +#include "examples/r1cs_examples.hpp" + +namespace libsnark { + +template +r1cs_example gen_r1cs_example_from_protoboard(const size_t num_constraints, + const size_t num_inputs); + +} // libsnark + +#include "gadgetlib1/examples/simple_example.tcc" + +#endif // SIMPLE_EXAMPLE_HPP_ diff --git a/src/snark/libsnark/gadgetlib1/examples/simple_example.tcc b/src/snark/libsnark/gadgetlib1/examples/simple_example.tcc new file mode 100644 index 000000000..9d500b5c7 --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/examples/simple_example.tcc @@ -0,0 +1,54 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef SIMPLE_EXAMPLE_TCC_ +#define SIMPLE_EXAMPLE_TCC_ + +#include +#include "gadgetlib1/gadgets/basic_gadgets.hpp" + +namespace libsnark { + +/* NOTE: all examples here actually generate one constraint less to account for soundness constraint in QAP */ + +template +r1cs_example gen_r1cs_example_from_protoboard(const size_t num_constraints, + const size_t num_inputs) +{ + const size_t new_num_constraints = num_constraints - 1; + + /* construct dummy example: inner products of two vectors */ + protoboard pb; + pb_variable_array A; + pb_variable_array B; + pb_variable res; + + // the variables on the protoboard are (ONE (constant 1 term), res, A[0], ..., A[num_constraints-1], B[0], ..., B[num_constraints-1]) + res.allocate(pb, "res"); + A.allocate(pb, new_num_constraints, "A"); + B.allocate(pb, new_num_constraints, "B"); + + inner_product_gadget compute_inner_product(pb, A, B, res, "compute_inner_product"); + compute_inner_product.generate_r1cs_constraints(); + + /* fill in random example */ + for (size_t i = 0; i < new_num_constraints; ++i) + { + pb.val(A[i]) = FieldT::random_element(); + pb.val(B[i]) = FieldT::random_element(); + } + + compute_inner_product.generate_r1cs_witness(); + + pb.constraint_system.num_inputs = num_inputs; + const r1cs_variable_assignment va = pb.values; + const r1cs_variable_assignment input(va.begin(), va.begin() + num_inputs); + return r1cs_example(pb.constraint_system, input, va, num_inputs); +} + +} // libsnark +#endif // R1CS_EXAMPLES_TCC_ diff --git a/src/snark/libsnark/gadgetlib1/gadget.hpp b/src/snark/libsnark/gadgetlib1/gadget.hpp new file mode 100644 index 000000000..dbeaa9d4b --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadget.hpp @@ -0,0 +1,27 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef GADGET_HPP_ +#define GADGET_HPP_ + +#include "gadgetlib1/protoboard.hpp" + +namespace libsnark { + +template +class gadget { +protected: + protoboard &pb; + const std::string annotation_prefix; +public: + gadget(protoboard &pb, const std::string &annotation_prefix=""); +}; + +} // libsnark +#include "gadgetlib1/gadget.tcc" + +#endif // GADGET_HPP_ diff --git a/src/snark/libsnark/gadgetlib1/gadget.tcc b/src/snark/libsnark/gadgetlib1/gadget.tcc new file mode 100644 index 000000000..120229bbe --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadget.tcc @@ -0,0 +1,23 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef GADGET_TCC_ +#define GADGET_TCC_ + +namespace libsnark { + +template +gadget::gadget(protoboard &pb, const std::string &annotation_prefix) : + pb(pb), annotation_prefix(annotation_prefix) +{ +#ifdef DEBUG + assert(annotation_prefix != ""); +#endif +} + +} // libsnark +#endif // GADGET_TCC_ diff --git a/src/snark/libsnark/gadgetlib1/gadgets/basic_gadgets.hpp b/src/snark/libsnark/gadgetlib1/gadgets/basic_gadgets.hpp new file mode 100644 index 000000000..08e596bee --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/basic_gadgets.hpp @@ -0,0 +1,351 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef BASIC_GADGETS_HPP_ +#define BASIC_GADGETS_HPP_ + +#include +#include + +#include "gadgetlib1/gadget.hpp" + +namespace libsnark { + +/* forces lc to take value 0 or 1 by adding constraint lc * (1-lc) = 0 */ +template +void generate_boolean_r1cs_constraint(protoboard &pb, const pb_linear_combination &lc, const std::string &annotation_prefix=""); + +template +void generate_r1cs_equals_const_constraint(protoboard &pb, const pb_linear_combination &lc, const FieldT& c, const std::string &annotation_prefix=""); + +template +class packing_gadget : public gadget { +private: + /* no internal variables */ +public: + const pb_linear_combination_array bits; + const pb_linear_combination packed; + + packing_gadget(protoboard &pb, + const pb_linear_combination_array &bits, + const pb_linear_combination &packed, + const std::string &annotation_prefix="") : + gadget(pb, annotation_prefix), bits(bits), packed(packed) {} + + void generate_r1cs_constraints(const bool enforce_bitness); + /* adds constraint result = \sum bits[i] * 2^i */ + + void generate_r1cs_witness_from_packed(); + void generate_r1cs_witness_from_bits(); +}; + +template +class multipacking_gadget : public gadget { +private: + std::vector > packers; +public: + const pb_linear_combination_array bits; + const pb_linear_combination_array packed_vars; + + const size_t chunk_size; + const size_t num_chunks; + // const size_t last_chunk_size; + + multipacking_gadget(protoboard &pb, + const pb_linear_combination_array &bits, + const pb_linear_combination_array &packed_vars, + const size_t chunk_size, + const std::string &annotation_prefix=""); + void generate_r1cs_constraints(const bool enforce_bitness); + void generate_r1cs_witness_from_packed(); + void generate_r1cs_witness_from_bits(); +}; + +template +class field_vector_copy_gadget : public gadget { +public: + const pb_variable_array source; + const pb_variable_array target; + const pb_linear_combination do_copy; + + field_vector_copy_gadget(protoboard &pb, + const pb_variable_array &source, + const pb_variable_array &target, + const pb_linear_combination &do_copy, + const std::string &annotation_prefix=""); + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + +template +class bit_vector_copy_gadget : public gadget { +public: + const pb_variable_array source_bits; + const pb_variable_array target_bits; + const pb_linear_combination do_copy; + + pb_variable_array packed_source; + pb_variable_array packed_target; + + std::shared_ptr > pack_source; + std::shared_ptr > pack_target; + std::shared_ptr > copier; + + const size_t chunk_size; + const size_t num_chunks; + + bit_vector_copy_gadget(protoboard &pb, + const pb_variable_array &source_bits, + const pb_variable_array &target_bits, + const pb_linear_combination &do_copy, + const size_t chunk_size, + const std::string &annotation_prefix=""); + void generate_r1cs_constraints(const bool enforce_source_bitness, const bool enforce_target_bitness); + void generate_r1cs_witness(); +}; + +template +class dual_variable_gadget : public gadget { +private: + std::shared_ptr > consistency_check; +public: + pb_variable packed; + pb_variable_array bits; + + dual_variable_gadget(protoboard &pb, + const size_t width, + const std::string &annotation_prefix="") : + gadget(pb, annotation_prefix) + { + packed.allocate(pb, FMT(this->annotation_prefix, " packed")); + bits.allocate(pb, width, FMT(this->annotation_prefix, " bits")); + consistency_check.reset(new packing_gadget(pb, + bits, + packed, + FMT(this->annotation_prefix, " consistency_check"))); + } + + dual_variable_gadget(protoboard &pb, + const pb_variable_array &bits, + const std::string &annotation_prefix="") : + gadget(pb, annotation_prefix), bits(bits) + { + packed.allocate(pb, FMT(this->annotation_prefix, " packed")); + consistency_check.reset(new packing_gadget(pb, + bits, + packed, + FMT(this->annotation_prefix, " consistency_check"))); + } + + dual_variable_gadget(protoboard &pb, + const pb_variable &packed, + const size_t width, + const std::string &annotation_prefix="") : + gadget(pb, annotation_prefix), packed(packed) + { + bits.allocate(pb, width, FMT(this->annotation_prefix, " bits")); + consistency_check.reset(new packing_gadget(pb, + bits, + packed, + FMT(this->annotation_prefix, " consistency_check"))); + } + + void generate_r1cs_constraints(const bool enforce_bitness); + void generate_r1cs_witness_from_packed(); + void generate_r1cs_witness_from_bits(); +}; + +/* + the gadgets below are Fp specific: + I * X = R + (1-R) * X = 0 + + if X = 0 then R = 0 + if X != 0 then R = 1 and I = X^{-1} +*/ + +template +class disjunction_gadget : public gadget { +private: + pb_variable inv; +public: + const pb_variable_array inputs; + const pb_variable output; + + disjunction_gadget(protoboard& pb, + const pb_variable_array &inputs, + const pb_variable &output, + const std::string &annotation_prefix="") : + gadget(pb, annotation_prefix), inputs(inputs), output(output) + { + assert(inputs.size() >= 1); + inv.allocate(pb, FMT(this->annotation_prefix, " inv")); + } + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + +template +void test_disjunction_gadget(const size_t n); + +template +class conjunction_gadget : public gadget { +private: + pb_variable inv; +public: + const pb_variable_array inputs; + const pb_variable output; + + conjunction_gadget(protoboard& pb, + const pb_variable_array &inputs, + const pb_variable &output, + const std::string &annotation_prefix="") : + gadget(pb, annotation_prefix), inputs(inputs), output(output) + { + assert(inputs.size() >= 1); + inv.allocate(pb, FMT(this->annotation_prefix, " inv")); + } + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + +template +void test_conjunction_gadget(const size_t n); + +template +class comparison_gadget : public gadget { +private: + pb_variable_array alpha; + pb_variable alpha_packed; + std::shared_ptr > pack_alpha; + + std::shared_ptr > all_zeros_test; + pb_variable not_all_zeros; +public: + const size_t n; + const pb_linear_combination A; + const pb_linear_combination B; + const pb_variable less; + const pb_variable less_or_eq; + + comparison_gadget(protoboard& pb, + const size_t n, + const pb_linear_combination &A, + const pb_linear_combination &B, + const pb_variable &less, + const pb_variable &less_or_eq, + const std::string &annotation_prefix="") : + gadget(pb, annotation_prefix), n(n), A(A), B(B), less(less), less_or_eq(less_or_eq) + { + alpha.allocate(pb, n, FMT(this->annotation_prefix, " alpha")); + alpha.emplace_back(less_or_eq); // alpha[n] is less_or_eq + + alpha_packed.allocate(pb, FMT(this->annotation_prefix, " alpha_packed")); + not_all_zeros.allocate(pb, FMT(this->annotation_prefix, " not_all_zeros")); + + pack_alpha.reset(new packing_gadget(pb, alpha, alpha_packed, + FMT(this->annotation_prefix, " pack_alpha"))); + + all_zeros_test.reset(new disjunction_gadget(pb, + pb_variable_array(alpha.begin(), alpha.begin() + n), + not_all_zeros, + FMT(this->annotation_prefix, " all_zeros_test"))); + }; + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + +template +void test_comparison_gadget(const size_t n); + +template +class inner_product_gadget : public gadget { +private: + /* S_i = \sum_{k=0}^{i+1} A[i] * B[i] */ + pb_variable_array S; +public: + const pb_linear_combination_array A; + const pb_linear_combination_array B; + const pb_variable result; + + inner_product_gadget(protoboard& pb, + const pb_linear_combination_array &A, + const pb_linear_combination_array &B, + const pb_variable &result, + const std::string &annotation_prefix="") : + gadget(pb, annotation_prefix), A(A), B(B), result(result) + { + assert(A.size() >= 1); + assert(A.size() == B.size()); + + S.allocate(pb, A.size()-1, FMT(this->annotation_prefix, " S")); + } + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + +template +void test_inner_product_gadget(const size_t n); + +template +class loose_multiplexing_gadget : public gadget { +/* + this implements loose multiplexer: + index not in bounds -> success_flag = 0 + index in bounds && success_flag = 1 -> result is correct + however if index is in bounds we can also set success_flag to 0 (and then result will be forced to be 0) +*/ +public: + pb_variable_array alpha; +private: + std::shared_ptr > compute_result; +public: + const pb_linear_combination_array arr; + const pb_variable index; + const pb_variable result; + const pb_variable success_flag; + + loose_multiplexing_gadget(protoboard& pb, + const pb_linear_combination_array &arr, + const pb_variable &index, + const pb_variable &result, + const pb_variable &success_flag, + const std::string &annotation_prefix="") : + gadget(pb, annotation_prefix), arr(arr), index(index), result(result), success_flag(success_flag) + { + alpha.allocate(pb, arr.size(), FMT(this->annotation_prefix, " alpha")); + compute_result.reset(new inner_product_gadget(pb, alpha, arr, result, FMT(this->annotation_prefix, " compute_result"))); + }; + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + +template +void test_loose_multiplexing_gadget(const size_t n); + +template +void create_linear_combination_constraints(protoboard &pb, + const std::vector &base, + const std::vector > &v, + const VarT &target, + const std::string &annotation_prefix); + +template +void create_linear_combination_witness(protoboard &pb, + const std::vector &base, + const std::vector > &v, + const VarT &target); + +} // libsnark +#include "gadgetlib1/gadgets/basic_gadgets.tcc" + +#endif // BASIC_GADGETS_HPP_ diff --git a/src/snark/libsnark/gadgetlib1/gadgets/basic_gadgets.tcc b/src/snark/libsnark/gadgetlib1/gadgets/basic_gadgets.tcc new file mode 100644 index 000000000..213b1906f --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/basic_gadgets.tcc @@ -0,0 +1,705 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef BASIC_GADGETS_TCC_ +#define BASIC_GADGETS_TCC_ + +#include "common/profiling.hpp" +#include "common/utils.hpp" + +namespace libsnark { + +template +void generate_boolean_r1cs_constraint(protoboard &pb, const pb_linear_combination &lc, const std::string &annotation_prefix) +/* forces lc to take value 0 or 1 by adding constraint lc * (1-lc) = 0 */ +{ + pb.add_r1cs_constraint(r1cs_constraint(lc, 1-lc, 0), + FMT(annotation_prefix, " boolean_r1cs_constraint")); +} + +template +void generate_r1cs_equals_const_constraint(protoboard &pb, const pb_linear_combination &lc, const FieldT& c, const std::string &annotation_prefix) +{ + pb.add_r1cs_constraint(r1cs_constraint(1, lc, c), + FMT(annotation_prefix, " constness_constraint")); +} + +template +void packing_gadget::generate_r1cs_constraints(const bool enforce_bitness) +/* adds constraint result = \sum bits[i] * 2^i */ +{ + this->pb.add_r1cs_constraint(r1cs_constraint(1, pb_packing_sum(bits), packed), FMT(this->annotation_prefix, " packing_constraint")); + + if (enforce_bitness) + { + for (size_t i = 0; i < bits.size(); ++i) + { + generate_boolean_r1cs_constraint(this->pb, bits[i], FMT(this->annotation_prefix, " bitness_%zu", i)); + } + } +} + +template +void packing_gadget::generate_r1cs_witness_from_packed() +{ + packed.evaluate(this->pb); + assert(this->pb.lc_val(packed).as_bigint().num_bits() <= bits.size()); + bits.fill_with_bits_of_field_element(this->pb, this->pb.lc_val(packed)); +} + +template +void packing_gadget::generate_r1cs_witness_from_bits() +{ + bits.evaluate(this->pb); + this->pb.lc_val(packed) = bits.get_field_element_from_bits(this->pb); +} + +template +multipacking_gadget::multipacking_gadget(protoboard &pb, + const pb_linear_combination_array &bits, + const pb_linear_combination_array &packed_vars, + const size_t chunk_size, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), bits(bits), packed_vars(packed_vars), + chunk_size(chunk_size), + num_chunks(div_ceil(bits.size(), chunk_size)) + // last_chunk_size(bits.size() - (num_chunks-1) * chunk_size) +{ + assert(packed_vars.size() == num_chunks); + for (size_t i = 0; i < num_chunks; ++i) + { + packers.emplace_back(packing_gadget(this->pb, pb_linear_combination_array(bits.begin() + i * chunk_size, + bits.begin() + std::min((i+1) * chunk_size, bits.size())), + packed_vars[i], FMT(this->annotation_prefix, " packers_%zu", i))); + } +} + +template +void multipacking_gadget::generate_r1cs_constraints(const bool enforce_bitness) +{ + for (size_t i = 0; i < num_chunks; ++i) + { + packers[i].generate_r1cs_constraints(enforce_bitness); + } +} + +template +void multipacking_gadget::generate_r1cs_witness_from_packed() +{ + for (size_t i = 0; i < num_chunks; ++i) + { + packers[i].generate_r1cs_witness_from_packed(); + } +} + +template +void multipacking_gadget::generate_r1cs_witness_from_bits() +{ + for (size_t i = 0; i < num_chunks; ++i) + { + packers[i].generate_r1cs_witness_from_bits(); + } +} + +template +size_t multipacking_num_chunks(const size_t num_bits) +{ + return div_ceil(num_bits, FieldT::capacity()); +} + +template +field_vector_copy_gadget::field_vector_copy_gadget(protoboard &pb, + const pb_variable_array &source, + const pb_variable_array &target, + const pb_linear_combination &do_copy, + const std::string &annotation_prefix) : +gadget(pb, annotation_prefix), source(source), target(target), do_copy(do_copy) +{ + assert(source.size() == target.size()); +} + +template +void field_vector_copy_gadget::generate_r1cs_constraints() +{ + for (size_t i = 0; i < source.size(); ++i) + { + this->pb.add_r1cs_constraint(r1cs_constraint(do_copy, source[i] - target[i], 0), + FMT(this->annotation_prefix, " copying_check_%zu", i)); + } +} + +template +void field_vector_copy_gadget::generate_r1cs_witness() +{ + do_copy.evaluate(this->pb); + assert(this->pb.lc_val(do_copy) == FieldT::one() || this->pb.lc_val(do_copy) == FieldT::zero()); + if (this->pb.lc_val(do_copy) != FieldT::zero()) + { + for (size_t i = 0; i < source.size(); ++i) + { + this->pb.val(target[i]) = this->pb.val(source[i]); + } + } +} + +template +bit_vector_copy_gadget::bit_vector_copy_gadget(protoboard &pb, + const pb_variable_array &source_bits, + const pb_variable_array &target_bits, + const pb_linear_combination &do_copy, + const size_t chunk_size, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), source_bits(source_bits), target_bits(target_bits), do_copy(do_copy), + chunk_size(chunk_size), num_chunks(div_ceil(source_bits.size(), chunk_size)) +{ + assert(source_bits.size() == target_bits.size()); + + packed_source.allocate(pb, num_chunks, FMT(annotation_prefix, " packed_source")); + pack_source.reset(new multipacking_gadget(pb, source_bits, packed_source, chunk_size, FMT(annotation_prefix, " pack_source"))); + + packed_target.allocate(pb, num_chunks, FMT(annotation_prefix, " packed_target")); + pack_target.reset(new multipacking_gadget(pb, target_bits, packed_target, chunk_size, FMT(annotation_prefix, " pack_target"))); + + copier.reset(new field_vector_copy_gadget(pb, packed_source, packed_target, do_copy, FMT(annotation_prefix, " copier"))); +} + +template +void bit_vector_copy_gadget::generate_r1cs_constraints(const bool enforce_source_bitness, const bool enforce_target_bitness) +{ + pack_source->generate_r1cs_constraints(enforce_source_bitness); + pack_target->generate_r1cs_constraints(enforce_target_bitness); + + copier->generate_r1cs_constraints(); +} + +template +void bit_vector_copy_gadget::generate_r1cs_witness() +{ + do_copy.evaluate(this->pb); + assert(this->pb.lc_val(do_copy) == FieldT::zero() || this->pb.lc_val(do_copy) == FieldT::one()); + if (this->pb.lc_val(do_copy) == FieldT::one()) + { + for (size_t i = 0; i < source_bits.size(); ++i) + { + this->pb.val(target_bits[i]) = this->pb.val(source_bits[i]); + } + } + + pack_source->generate_r1cs_witness_from_bits(); + pack_target->generate_r1cs_witness_from_bits(); +} + +template +void dual_variable_gadget::generate_r1cs_constraints(const bool enforce_bitness) +{ + consistency_check->generate_r1cs_constraints(enforce_bitness); +} + +template +void dual_variable_gadget::generate_r1cs_witness_from_packed() +{ + consistency_check->generate_r1cs_witness_from_packed(); +} + +template +void dual_variable_gadget::generate_r1cs_witness_from_bits() +{ + consistency_check->generate_r1cs_witness_from_bits(); +} + +template +void disjunction_gadget::generate_r1cs_constraints() +{ + /* inv * sum = output */ + linear_combination a1, b1, c1; + a1.add_term(inv); + for (size_t i = 0; i < inputs.size(); ++i) + { + b1.add_term(inputs[i]); + } + c1.add_term(output); + + this->pb.add_r1cs_constraint(r1cs_constraint(a1, b1, c1), FMT(this->annotation_prefix, " inv*sum=output")); + + /* (1-output) * sum = 0 */ + linear_combination a2, b2, c2; + a2.add_term(ONE); + a2.add_term(output, -1); + for (size_t i = 0; i < inputs.size(); ++i) + { + b2.add_term(inputs[i]); + } + c2.add_term(ONE, 0); + + this->pb.add_r1cs_constraint(r1cs_constraint(a2, b2, c2), FMT(this->annotation_prefix, " (1-output)*sum=0")); +} + +template +void disjunction_gadget::generate_r1cs_witness() +{ + FieldT sum = FieldT::zero(); + + for (size_t i = 0; i < inputs.size(); ++i) + { + sum += this->pb.val(inputs[i]); + } + + if (sum.is_zero()) + { + this->pb.val(inv) = FieldT::zero(); + this->pb.val(output) = FieldT::zero(); + } + else + { + this->pb.val(inv) = sum.inverse(); + this->pb.val(output) = FieldT::one(); + } +} + +template +void test_disjunction_gadget(const size_t n) +{ + printf("testing disjunction_gadget on all %zu bit strings\n", n); + + protoboard pb; + pb_variable_array inputs; + inputs.allocate(pb, n, "inputs"); + + pb_variable output; + output.allocate(pb, "output"); + + disjunction_gadget d(pb, inputs, output, "d"); + d.generate_r1cs_constraints(); + + for (size_t w = 0; w < 1ul< +void conjunction_gadget::generate_r1cs_constraints() +{ + /* inv * (n-sum) = 1-output */ + linear_combination a1, b1, c1; + a1.add_term(inv); + b1.add_term(ONE, inputs.size()); + for (size_t i = 0; i < inputs.size(); ++i) + { + b1.add_term(inputs[i], -1); + } + c1.add_term(ONE); + c1.add_term(output, -1); + + this->pb.add_r1cs_constraint(r1cs_constraint(a1, b1, c1), FMT(this->annotation_prefix, " inv*(n-sum)=(1-output)")); + + /* output * (n-sum) = 0 */ + linear_combination a2, b2, c2; + a2.add_term(output); + b2.add_term(ONE, inputs.size()); + for (size_t i = 0; i < inputs.size(); ++i) + { + b2.add_term(inputs[i], -1); + } + c2.add_term(ONE, 0); + + this->pb.add_r1cs_constraint(r1cs_constraint(a2, b2, c2), FMT(this->annotation_prefix, " output*(n-sum)=0")); +} + +template +void conjunction_gadget::generate_r1cs_witness() +{ + FieldT sum = FieldT(inputs.size()); + + for (size_t i = 0; i < inputs.size(); ++i) + { + sum -= this->pb.val(inputs[i]); + } + + if (sum.is_zero()) + { + this->pb.val(inv) = FieldT::zero(); + this->pb.val(output) = FieldT::one(); + } + else + { + this->pb.val(inv) = sum.inverse(); + this->pb.val(output) = FieldT::zero(); + } +} + +template +void test_conjunction_gadget(const size_t n) +{ + printf("testing conjunction_gadget on all %zu bit strings\n", n); + + protoboard pb; + pb_variable_array inputs; + inputs.allocate(pb, n, "inputs"); + + pb_variable output; + output.allocate(pb, "output"); + + conjunction_gadget c(pb, inputs, output, "c"); + c.generate_r1cs_constraints(); + + for (size_t w = 0; w < 1ul< +void comparison_gadget::generate_r1cs_constraints() +{ + /* + packed(alpha) = 2^n + B - A + + not_all_zeros = \bigvee_{i=0}^{n-1} alpha_i + + if B - A > 0, then 2^n + B - A > 2^n, + so alpha_n = 1 and not_all_zeros = 1 + if B - A = 0, then 2^n + B - A = 2^n, + so alpha_n = 1 and not_all_zeros = 0 + if B - A < 0, then 2^n + B - A \in {0, 1, \ldots, 2^n-1}, + so alpha_n = 0 + + therefore alpha_n = less_or_eq and alpha_n * not_all_zeros = less + */ + + /* not_all_zeros to be Boolean, alpha_i are Boolean by packing gadget */ + generate_boolean_r1cs_constraint(this->pb, not_all_zeros, + FMT(this->annotation_prefix, " not_all_zeros")); + + /* constraints for packed(alpha) = 2^n + B - A */ + pack_alpha->generate_r1cs_constraints(true); + this->pb.add_r1cs_constraint(r1cs_constraint(1, (FieldT(2)^n) + B - A, alpha_packed), FMT(this->annotation_prefix, " main_constraint")); + + /* compute result */ + all_zeros_test->generate_r1cs_constraints(); + this->pb.add_r1cs_constraint(r1cs_constraint(less_or_eq, not_all_zeros, less), + FMT(this->annotation_prefix, " less")); +} + +template +void comparison_gadget::generate_r1cs_witness() +{ + A.evaluate(this->pb); + B.evaluate(this->pb); + + /* unpack 2^n + B - A into alpha_packed */ + this->pb.val(alpha_packed) = (FieldT(2)^n) + this->pb.lc_val(B) - this->pb.lc_val(A); + pack_alpha->generate_r1cs_witness_from_packed(); + + /* compute result */ + all_zeros_test->generate_r1cs_witness(); + this->pb.val(less) = this->pb.val(less_or_eq) * this->pb.val(not_all_zeros); +} + +template +void test_comparison_gadget(const size_t n) +{ + printf("testing comparison_gadget on all %zu bit inputs\n", n); + + protoboard pb; + + pb_variable A, B, less, less_or_eq; + A.allocate(pb, "A"); + B.allocate(pb, "B"); + less.allocate(pb, "less"); + less_or_eq.allocate(pb, "less_or_eq"); + + comparison_gadget cmp(pb, n, A, B, less, less_or_eq, "cmp"); + cmp.generate_r1cs_constraints(); + + for (size_t a = 0; a < 1ul< +void inner_product_gadget::generate_r1cs_constraints() +{ + /* + S_i = \sum_{k=0}^{i+1} A[i] * B[i] + S[0] = A[0] * B[0] + S[i+1] - S[i] = A[i] * B[i] + */ + for (size_t i = 0; i < A.size(); ++i) + { + this->pb.add_r1cs_constraint( + r1cs_constraint(A[i], B[i], + (i == A.size()-1 ? result : S[i]) + (i == 0 ? 0 * ONE : -S[i-1])), + FMT(this->annotation_prefix, " S_%zu", i)); + } +} + +template +void inner_product_gadget::generate_r1cs_witness() +{ + FieldT total = FieldT::zero(); + for (size_t i = 0; i < A.size(); ++i) + { + A[i].evaluate(this->pb); + B[i].evaluate(this->pb); + + total += this->pb.lc_val(A[i]) * this->pb.lc_val(B[i]); + this->pb.val(i == A.size()-1 ? result : S[i]) = total; + } +} + +template +void test_inner_product_gadget(const size_t n) +{ + printf("testing inner_product_gadget on all %zu bit strings\n", n); + + protoboard pb; + pb_variable_array A; + A.allocate(pb, n, "A"); + pb_variable_array B; + B.allocate(pb, n, "B"); + + pb_variable result; + result.allocate(pb, "result"); + + inner_product_gadget g(pb, A, B, result, "g"); + g.generate_r1cs_constraints(); + + for (size_t i = 0; i < 1ul< +void loose_multiplexing_gadget::generate_r1cs_constraints() +{ + /* \alpha_i (index - i) = 0 */ + for (size_t i = 0; i < arr.size(); ++i) + { + this->pb.add_r1cs_constraint( + r1cs_constraint(alpha[i], index - i, 0), + FMT(this->annotation_prefix, " alpha_%zu", i)); + } + + /* 1 * (\sum \alpha_i) = success_flag */ + linear_combination a, b, c; + a.add_term(ONE); + for (size_t i = 0; i < arr.size(); ++i) + { + b.add_term(alpha[i]); + } + c.add_term(success_flag); + this->pb.add_r1cs_constraint(r1cs_constraint(a, b, c), FMT(this->annotation_prefix, " main_constraint")); + + /* now success_flag is constrained to either 0 (if index is out of + range) or \alpha_i. constrain it and \alpha_i to zero */ + generate_boolean_r1cs_constraint(this->pb, success_flag, FMT(this->annotation_prefix, " success_flag")); + + /* compute result */ + compute_result->generate_r1cs_constraints(); +} + +template +void loose_multiplexing_gadget::generate_r1cs_witness() +{ + /* assumes that idx can be fit in ulong; true for our purposes for now */ + const bigint valint = this->pb.val(index).as_bigint(); + unsigned long idx = valint.as_ulong(); + const bigint arrsize(arr.size()); + + if (idx >= arr.size() || mpn_cmp(valint.data, arrsize.data, FieldT::num_limbs) >= 0) + { + for (size_t i = 0; i < arr.size(); ++i) + { + this->pb.val(alpha[i]) = FieldT::zero(); + } + + this->pb.val(success_flag) = FieldT::zero(); + } + else + { + for (size_t i = 0; i < arr.size(); ++i) + { + this->pb.val(alpha[i]) = (i == idx ? FieldT::one() : FieldT::zero()); + } + + this->pb.val(success_flag) = FieldT::one(); + } + + compute_result->generate_r1cs_witness(); +} + +template +void test_loose_multiplexing_gadget(const size_t n) +{ + printf("testing loose_multiplexing_gadget on 2**%zu pb_variable array inputs\n", n); + protoboard pb; + + pb_variable_array arr; + arr.allocate(pb, 1ul< index, result, success_flag; + index.allocate(pb, "index"); + result.allocate(pb, "result"); + success_flag.allocate(pb, "success_flag"); + + loose_multiplexing_gadget g(pb, arr, index, result, success_flag, "g"); + g.generate_r1cs_constraints(); + + for (size_t i = 0; i < 1ul< +void create_linear_combination_constraints(protoboard &pb, + const std::vector &base, + const std::vector > &v, + const VarT &target, + const std::string &annotation_prefix) +{ + for (size_t i = 0; i < base.size(); ++i) + { + linear_combination a, b, c; + + a.add_term(ONE); + b.add_term(ONE, base[i]); + + for (auto &p : v) + { + b.add_term(p.first.all_vars[i], p.second); + } + + c.add_term(target.all_vars[i]); + + pb.add_r1cs_constraint(r1cs_constraint(a, b, c), FMT(annotation_prefix, " linear_combination_%zu", i)); + } +} + +template +void create_linear_combination_witness(protoboard &pb, + const std::vector &base, + const std::vector > &v, + const VarT &target) +{ + for (size_t i = 0; i < base.size(); ++i) + { + pb.val(target.all_vars[i]) = base[i]; + + for (auto &p : v) + { + pb.val(target.all_vars[i]) += p.second * pb.val(p.first.all_vars[i]); + } + } +} + +} // libsnark +#endif // BASIC_GADGETS_TCC_ diff --git a/src/snark/libsnark/gadgetlib1/gadgets/gadget_from_r1cs.hpp b/src/snark/libsnark/gadgetlib1/gadgets/gadget_from_r1cs.hpp new file mode 100644 index 000000000..e4b8a2acf --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/gadget_from_r1cs.hpp @@ -0,0 +1,45 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for a gadget that can be created from an R1CS constraint system. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef GADGET_FROM_R1CS_HPP_ +#define GADGET_FROM_R1CS_HPP_ + +#include + +#include "gadgetlib1/gadget.hpp" + +namespace libsnark { + +template +class gadget_from_r1cs : public gadget { + +private: + const std::vector > vars; + const r1cs_constraint_system cs; + std::map cs_to_vars; + +public: + + gadget_from_r1cs(protoboard &pb, + const std::vector > &vars, + const r1cs_constraint_system &cs, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness(const r1cs_primary_input &primary_input, + const r1cs_auxiliary_input &auxiliary_input); +}; + +} // libsnark + +#include "gadgetlib1/gadgets/gadget_from_r1cs.tcc" + +#endif // GADGET_FROM_R1CS_HPP_ diff --git a/src/snark/libsnark/gadgetlib1/gadgets/gadget_from_r1cs.tcc b/src/snark/libsnark/gadgetlib1/gadgets/gadget_from_r1cs.tcc new file mode 100644 index 000000000..bc59b4587 --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/gadget_from_r1cs.tcc @@ -0,0 +1,123 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for a gadget that can be created from an R1CS constraint system. + + See gadget_from_r1cs.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef GADGET_FROM_R1CS_TCC_ +#define GADGET_FROM_R1CS_TCC_ + +namespace libsnark { + +template +gadget_from_r1cs::gadget_from_r1cs(protoboard &pb, + const std::vector > &vars, + const r1cs_constraint_system &cs, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), + vars(vars), + cs(cs) +{ + cs_to_vars[0] = 0; /* constant term maps to constant term */ + + size_t cs_var_idx = 1; + for (auto va : vars) + { +#ifdef DEBUG + printf("gadget_from_r1cs: translating a block of variables with length %zu\n", va.size()); +#endif + for (auto v : va) + { + cs_to_vars[cs_var_idx] = v.index; + +#ifdef DEBUG + if (v.index != 0) + { + // handle annotations, except for re-annotating constant term + const std::map::const_iterator it = cs.variable_annotations.find(cs_var_idx); + + std::string annotation = FMT(annotation_prefix, " variable_%zu", cs_var_idx); + if (it != cs.variable_annotations.end()) + { + annotation = annotation_prefix + " " + it->second; + } + + pb.augment_variable_annotation(v, annotation); + } +#endif + ++cs_var_idx; + } + } + +#ifdef DEBUG + printf("gadget_from_r1cs: sum of all block lengths: %zu\n", cs_var_idx-1); + printf("gadget_from_r1cs: cs.num_variables(): %zu\n", cs.num_variables()); +#endif + + assert(cs_var_idx - 1 == cs.num_variables()); +} + +template +void gadget_from_r1cs::generate_r1cs_constraints() +{ + for (size_t i = 0; i < cs.num_constraints(); ++i) + { + const r1cs_constraint &constr = cs.constraints[i]; + r1cs_constraint translated_constr; + + for (const linear_term &t: constr.a.terms) + { + translated_constr.a.terms.emplace_back(linear_term(pb_variable(cs_to_vars[t.index]), t.coeff)); + } + + for (const linear_term &t: constr.b.terms) + { + translated_constr.b.terms.emplace_back(linear_term(pb_variable(cs_to_vars[t.index]), t.coeff)); + } + + for (const linear_term &t: constr.c.terms) + { + translated_constr.c.terms.emplace_back(linear_term(pb_variable(cs_to_vars[t.index]), t.coeff)); + } + + std::string annotation = FMT(this->annotation_prefix, " constraint_%zu", i); + +#ifdef DEBUG + auto it = cs.constraint_annotations.find(i); + if (it != cs.constraint_annotations.end()) + { + annotation = this->annotation_prefix + " " + it->second; + } +#endif + this->pb.add_r1cs_constraint(translated_constr, annotation); + } +} + +template +void gadget_from_r1cs::generate_r1cs_witness(const r1cs_primary_input &primary_input, + const r1cs_auxiliary_input &auxiliary_input) +{ + assert(cs.num_inputs() == primary_input.size()); + assert(cs.num_variables() == primary_input.size() + auxiliary_input.size()); + + for (size_t i = 0; i < primary_input.size(); ++i) + { + this->pb.val(pb_variable(cs_to_vars[i+1])) = primary_input[i]; + } + + for (size_t i = 0; i < auxiliary_input.size(); ++i) + { + this->pb.val(pb_variable(cs_to_vars[primary_input.size()+i+1])) = auxiliary_input[i]; + } +} + +} // libsnark + +#endif // GADGET_FROM_R1CS_TCC_ diff --git a/src/snark/libsnark/gadgetlib1/gadgets/hashes/digest_selector_gadget.hpp b/src/snark/libsnark/gadgetlib1/gadgets/hashes/digest_selector_gadget.hpp new file mode 100644 index 000000000..a7598b9be --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/hashes/digest_selector_gadget.hpp @@ -0,0 +1,42 @@ +/** + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ +#ifndef DIGEST_SELECTOR_GADGET_HPP_ +#define DIGEST_SELECTOR_GADGET_HPP_ + +#include + +#include "gadgetlib1/gadgets/basic_gadgets.hpp" +#include "gadgetlib1/gadgets/hashes/hash_io.hpp" + +namespace libsnark { + +template +class digest_selector_gadget : public gadget { +public: + size_t digest_size; + digest_variable input; + pb_linear_combination is_right; + digest_variable left; + digest_variable right; + + digest_selector_gadget(protoboard &pb, + const size_t digest_size, + const digest_variable &input, + const pb_linear_combination &is_right, + const digest_variable &left, + const digest_variable &right, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + +} // libsnark + +#include "gadgetlib1/gadgets/hashes/digest_selector_gadget.tcc" + +#endif // DIGEST_SELECTOR_GADGET_HPP_ diff --git a/src/snark/libsnark/gadgetlib1/gadgets/hashes/digest_selector_gadget.tcc b/src/snark/libsnark/gadgetlib1/gadgets/hashes/digest_selector_gadget.tcc new file mode 100644 index 000000000..422ee170a --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/hashes/digest_selector_gadget.tcc @@ -0,0 +1,62 @@ +/** + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ +#ifndef DIGEST_SELECTOR_GADGET_TCC_ +#define DIGEST_SELECTOR_GADGET_TCC_ + +namespace libsnark { + +template +digest_selector_gadget::digest_selector_gadget(protoboard &pb, + const size_t digest_size, + const digest_variable &input, + const pb_linear_combination &is_right, + const digest_variable &left, + const digest_variable &right, + const std::string &annotation_prefix) : +gadget(pb, annotation_prefix), digest_size(digest_size), input(input), is_right(is_right), left(left), right(right) +{ +} + +template +void digest_selector_gadget::generate_r1cs_constraints() +{ + for (size_t i = 0; i < digest_size; ++i) + { + /* + input = is_right * right + (1-is_right) * left + input - left = is_right(right - left) + */ + this->pb.add_r1cs_constraint(r1cs_constraint(is_right, right.bits[i] - left.bits[i], input.bits[i] - left.bits[i]), + FMT(this->annotation_prefix, " propagate_%zu", i)); + } +} + +template +void digest_selector_gadget::generate_r1cs_witness() +{ + is_right.evaluate(this->pb); + + assert(this->pb.lc_val(is_right) == FieldT::one() || this->pb.lc_val(is_right) == FieldT::zero()); + if (this->pb.lc_val(is_right) == FieldT::one()) + { + for (size_t i = 0; i < digest_size; ++i) + { + this->pb.val(right.bits[i]) = this->pb.val(input.bits[i]); + } + } + else + { + for (size_t i = 0; i < digest_size; ++i) + { + this->pb.val(left.bits[i]) = this->pb.val(input.bits[i]); + } + } +} + +} // libsnark + +#endif // DIGEST_SELECTOR_GADGET_TCC_ diff --git a/src/snark/libsnark/gadgetlib1/gadgets/hashes/hash_io.hpp b/src/snark/libsnark/gadgetlib1/gadgets/hashes/hash_io.hpp new file mode 100644 index 000000000..80ca19c61 --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/hashes/hash_io.hpp @@ -0,0 +1,63 @@ +/** + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ +#ifndef HASH_IO_HPP_ +#define HASH_IO_HPP_ +#include +#include +#include "gadgetlib1/gadgets/basic_gadgets.hpp" + +namespace libsnark { + +template +class digest_variable : public gadget { +public: + size_t digest_size; + pb_variable_array bits; + + digest_variable(protoboard &pb, + const size_t digest_size, + const std::string &annotation_prefix); + + digest_variable(protoboard &pb, + const size_t digest_size, + const pb_variable_array &partial_bits, + const pb_variable &padding, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness(const bit_vector& contents); + bit_vector get_digest() const; +}; + +template +class block_variable : public gadget { +public: + size_t block_size; + pb_variable_array bits; + + block_variable(protoboard &pb, + const size_t block_size, + const std::string &annotation_prefix); + + block_variable(protoboard &pb, + const std::vector > &parts, + const std::string &annotation_prefix); + + block_variable(protoboard &pb, + const digest_variable &left, + const digest_variable &right, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness(const bit_vector& contents); + bit_vector get_block() const; +}; + +} // libsnark +#include "gadgetlib1/gadgets/hashes/hash_io.tcc" + +#endif // HASH_IO_HPP_ diff --git a/src/snark/libsnark/gadgetlib1/gadgets/hashes/hash_io.tcc b/src/snark/libsnark/gadgetlib1/gadgets/hashes/hash_io.tcc new file mode 100644 index 000000000..b122d8f98 --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/hashes/hash_io.tcc @@ -0,0 +1,105 @@ +/** + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ +#ifndef HASH_IO_TCC_ +#define HASH_IO_TCC_ + +namespace libsnark { + +template +digest_variable::digest_variable(protoboard &pb, + const size_t digest_size, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), digest_size(digest_size) +{ + bits.allocate(pb, digest_size, FMT(this->annotation_prefix, " bits")); +} + +template +digest_variable::digest_variable(protoboard &pb, + const size_t digest_size, + const pb_variable_array &partial_bits, + const pb_variable &padding, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), digest_size(digest_size) +{ + assert(bits.size() <= digest_size); + bits = partial_bits; + while (bits.size() != digest_size) + { + bits.emplace_back(padding); + } +} + +template +void digest_variable::generate_r1cs_constraints() +{ + for (size_t i = 0; i < digest_size; ++i) + { + generate_boolean_r1cs_constraint(this->pb, bits[i], FMT(this->annotation_prefix, " bits_%zu", i)); + } +} + +template +void digest_variable::generate_r1cs_witness(const bit_vector& contents) +{ + bits.fill_with_bits(this->pb, contents); +} + +template +bit_vector digest_variable::get_digest() const +{ + return bits.get_bits(this->pb); +} + +template +block_variable::block_variable(protoboard &pb, + const size_t block_size, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), block_size(block_size) +{ + bits.allocate(pb, block_size, FMT(this->annotation_prefix, " bits")); +} + +template +block_variable::block_variable(protoboard &pb, + const std::vector > &parts, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix) +{ + for (auto &part : parts) + { + bits.insert(bits.end(), part.begin(), part.end()); + } +} + +template +block_variable::block_variable(protoboard &pb, + const digest_variable &left, + const digest_variable &right, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix) +{ + assert(left.bits.size() == right.bits.size()); + block_size = 2 * left.bits.size(); + bits.insert(bits.end(), left.bits.begin(), left.bits.end()); + bits.insert(bits.end(), right.bits.begin(), right.bits.end()); +} + +template +void block_variable::generate_r1cs_witness(const bit_vector& contents) +{ + bits.fill_with_bits(this->pb, contents); +} + +template +bit_vector block_variable::get_block() const +{ + return bits.get_bits(this->pb); +} + +} // libsnark +#endif // HASH_IO_TCC_ diff --git a/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_aux.hpp b/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_aux.hpp new file mode 100644 index 000000000..e0c7a7e0b --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_aux.hpp @@ -0,0 +1,160 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for auxiliary gadgets for the SHA256 gadget. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef SHA256_AUX_HPP_ +#define SHA256_AUX_HPP_ + +#include "gadgetlib1/gadgets/basic_gadgets.hpp" + +namespace libsnark { + +template +class lastbits_gadget : public gadget { +public: + pb_variable X; + size_t X_bits; + pb_variable result; + pb_linear_combination_array result_bits; + + pb_linear_combination_array full_bits; + std::shared_ptr > unpack_bits; + std::shared_ptr > pack_result; + + lastbits_gadget(protoboard &pb, + const pb_variable &X, + const size_t X_bits, + const pb_variable &result, + const pb_linear_combination_array &result_bits, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + +template +class XOR3_gadget : public gadget { +private: + pb_variable tmp; +public: + pb_linear_combination A; + pb_linear_combination B; + pb_linear_combination C; + bool assume_C_is_zero; + pb_linear_combination out; + + XOR3_gadget(protoboard &pb, + const pb_linear_combination &A, + const pb_linear_combination &B, + const pb_linear_combination &C, + const bool assume_C_is_zero, + const pb_linear_combination &out, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + +/* Page 10 of http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf */ +template +class small_sigma_gadget : public gadget { +private: + pb_variable_array W; + pb_variable result; +public: + pb_variable_array result_bits; + std::vector > > compute_bits; + std::shared_ptr > pack_result; + + small_sigma_gadget(protoboard &pb, + const pb_variable_array &W, + const pb_variable &result, + const size_t rot1, + const size_t rot2, + const size_t shift, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + +/* Page 10 of http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf */ +template +class big_sigma_gadget : public gadget { +private: + pb_linear_combination_array W; + pb_variable result; +public: + pb_variable_array result_bits; + std::vector > > compute_bits; + std::shared_ptr > pack_result; + + big_sigma_gadget(protoboard &pb, + const pb_linear_combination_array &W, + const pb_variable &result, + const size_t rot1, + const size_t rot2, + const size_t rot3, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + +/* Page 10 of http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf */ +template +class choice_gadget : public gadget { +private: + pb_variable_array result_bits; +public: + pb_linear_combination_array X; + pb_linear_combination_array Y; + pb_linear_combination_array Z; + pb_variable result; + std::shared_ptr > pack_result; + + choice_gadget(protoboard &pb, + const pb_linear_combination_array &X, + const pb_linear_combination_array &Y, + const pb_linear_combination_array &Z, + const pb_variable &result, const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + +/* Page 10 of http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf */ +template +class majority_gadget : public gadget { +private: + pb_variable_array result_bits; + std::shared_ptr > pack_result; +public: + pb_linear_combination_array X; + pb_linear_combination_array Y; + pb_linear_combination_array Z; + pb_variable result; + + majority_gadget(protoboard &pb, + const pb_linear_combination_array &X, + const pb_linear_combination_array &Y, + const pb_linear_combination_array &Z, + const pb_variable &result, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + +} // libsnark + +#include "gadgetlib1/gadgets/hashes/sha256/sha256_aux.tcc" + +#endif // SHA256_AUX_HPP_ diff --git a/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_aux.tcc b/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_aux.tcc new file mode 100644 index 000000000..8ab67be5f --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_aux.tcc @@ -0,0 +1,297 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for auxiliary gadgets for the SHA256 gadget. + + See sha256_aux.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef SHA256_AUX_TCC_ +#define SHA256_AUX_TCC_ + +namespace libsnark { + +template +lastbits_gadget::lastbits_gadget(protoboard &pb, + const pb_variable &X, + const size_t X_bits, + const pb_variable &result, + const pb_linear_combination_array &result_bits, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), + X(X), + X_bits(X_bits), + result(result), + result_bits(result_bits) +{ + full_bits = result_bits; + for (size_t i = result_bits.size(); i < X_bits; ++i) + { + pb_variable full_bits_overflow; + full_bits_overflow.allocate(pb, FMT(this->annotation_prefix, " full_bits_%zu", i)); + full_bits.emplace_back(full_bits_overflow); + } + + unpack_bits.reset(new packing_gadget(pb, full_bits, X, FMT(this->annotation_prefix, " unpack_bits"))); + pack_result.reset(new packing_gadget(pb, result_bits, result, FMT(this->annotation_prefix, " pack_result"))); +} + +template +void lastbits_gadget::generate_r1cs_constraints() +{ + unpack_bits->generate_r1cs_constraints(true); + pack_result->generate_r1cs_constraints(false); +} + +template +void lastbits_gadget::generate_r1cs_witness() +{ + unpack_bits->generate_r1cs_witness_from_packed(); + pack_result->generate_r1cs_witness_from_bits(); +} + +template +XOR3_gadget::XOR3_gadget(protoboard &pb, + const pb_linear_combination &A, + const pb_linear_combination &B, + const pb_linear_combination &C, + const bool assume_C_is_zero, + const pb_linear_combination &out, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), + A(A), + B(B), + C(C), + assume_C_is_zero(assume_C_is_zero), + out(out) +{ + if (!assume_C_is_zero) + { + tmp.allocate(pb, FMT(this->annotation_prefix, " tmp")); + } +} + +template +void XOR3_gadget::generate_r1cs_constraints() +{ + /* + tmp = A + B - 2AB i.e. tmp = A xor B + out = tmp + C - 2tmp C i.e. out = tmp xor C + */ + if (assume_C_is_zero) + { + this->pb.add_r1cs_constraint(r1cs_constraint(2*A, B, A + B - out), FMT(this->annotation_prefix, " implicit_tmp_equals_out")); + } + else + { + this->pb.add_r1cs_constraint(r1cs_constraint(2*A, B, A + B - tmp), FMT(this->annotation_prefix, " tmp")); + this->pb.add_r1cs_constraint(r1cs_constraint(2 * tmp, C, tmp + C - out), FMT(this->annotation_prefix, " out")); + } +} + +template +void XOR3_gadget::generate_r1cs_witness() +{ + if (assume_C_is_zero) + { + this->pb.lc_val(out) = this->pb.lc_val(A) + this->pb.lc_val(B) - FieldT(2) * this->pb.lc_val(A) * this->pb.lc_val(B); + } + else + { + this->pb.val(tmp) = this->pb.lc_val(A) + this->pb.lc_val(B) - FieldT(2) * this->pb.lc_val(A) * this->pb.lc_val(B); + this->pb.lc_val(out) = this->pb.val(tmp) + this->pb.lc_val(C) - FieldT(2) * this->pb.val(tmp) * this->pb.lc_val(C); + } +} + +#define SHA256_GADGET_ROTR(A, i, k) A[((i)+(k)) % 32] + +/* Page 10 of http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf */ +template +small_sigma_gadget::small_sigma_gadget(protoboard &pb, + const pb_variable_array &W, + const pb_variable &result, + const size_t rot1, + const size_t rot2, + const size_t shift, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), + W(W), + result(result) +{ + result_bits.allocate(pb, 32, FMT(this->annotation_prefix, " result_bits")); + compute_bits.resize(32); + for (size_t i = 0; i < 32; ++i) + { + compute_bits[i].reset(new XOR3_gadget(pb, SHA256_GADGET_ROTR(W, i, rot1), SHA256_GADGET_ROTR(W, i, rot2), + (i + shift < 32 ? W[i+shift] : ONE), + (i + shift >= 32), result_bits[i], + FMT(this->annotation_prefix, " compute_bits_%zu", i))); + } + pack_result.reset(new packing_gadget(pb, result_bits, result, FMT(this->annotation_prefix, " pack_result"))); +} + +template +void small_sigma_gadget::generate_r1cs_constraints() +{ + for (size_t i = 0; i < 32; ++i) + { + compute_bits[i]->generate_r1cs_constraints(); + } + + pack_result->generate_r1cs_constraints(false); +} + +template +void small_sigma_gadget::generate_r1cs_witness() +{ + for (size_t i = 0; i < 32; ++i) + { + compute_bits[i]->generate_r1cs_witness(); + } + + pack_result->generate_r1cs_witness_from_bits(); +} + +template +big_sigma_gadget::big_sigma_gadget(protoboard &pb, + const pb_linear_combination_array &W, + const pb_variable &result, + const size_t rot1, + const size_t rot2, + const size_t rot3, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), + W(W), + result(result) +{ + result_bits.allocate(pb, 32, FMT(this->annotation_prefix, " result_bits")); + compute_bits.resize(32); + for (size_t i = 0; i < 32; ++i) + { + compute_bits[i].reset(new XOR3_gadget(pb, SHA256_GADGET_ROTR(W, i, rot1), SHA256_GADGET_ROTR(W, i, rot2), SHA256_GADGET_ROTR(W, i, rot3), false, result_bits[i], + FMT(this->annotation_prefix, " compute_bits_%zu", i))); + } + + pack_result.reset(new packing_gadget(pb, result_bits, result, FMT(this->annotation_prefix, " pack_result"))); +} + +template +void big_sigma_gadget::generate_r1cs_constraints() +{ + for (size_t i = 0; i < 32; ++i) + { + compute_bits[i]->generate_r1cs_constraints(); + } + + pack_result->generate_r1cs_constraints(false); +} + +template +void big_sigma_gadget::generate_r1cs_witness() +{ + for (size_t i = 0; i < 32; ++i) + { + compute_bits[i]->generate_r1cs_witness(); + } + + pack_result->generate_r1cs_witness_from_bits(); +} + +/* Page 10 of http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf */ +template +choice_gadget::choice_gadget(protoboard &pb, + const pb_linear_combination_array &X, + const pb_linear_combination_array &Y, + const pb_linear_combination_array &Z, + const pb_variable &result, const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), + X(X), + Y(Y), + Z(Z), + result(result) +{ + result_bits.allocate(pb, 32, FMT(this->annotation_prefix, " result_bits")); + pack_result.reset(new packing_gadget(pb, result_bits, result, FMT(this->annotation_prefix, " result"))); +} + +template +void choice_gadget::generate_r1cs_constraints() +{ + for (size_t i = 0; i < 32; ++i) + { + /* + result = x * y + (1-x) * z + result - z = x * (y - z) + */ + this->pb.add_r1cs_constraint(r1cs_constraint(X[i], Y[i] - Z[i], result_bits[i] - Z[i]), FMT(this->annotation_prefix, " result_bits_%zu", i)); + } + pack_result->generate_r1cs_constraints(false); +} + +template +void choice_gadget::generate_r1cs_witness() +{ + for (size_t i = 0; i < 32; ++i) + { + this->pb.val(result_bits[i]) = this->pb.lc_val(X[i]) * this->pb.lc_val(Y[i]) + (FieldT::one() - this->pb.lc_val(X[i])) * this->pb.lc_val(Z[i]); + } + pack_result->generate_r1cs_witness_from_bits(); +} + +/* Page 10 of http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf */ +template +majority_gadget::majority_gadget(protoboard &pb, + const pb_linear_combination_array &X, + const pb_linear_combination_array &Y, + const pb_linear_combination_array &Z, + const pb_variable &result, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), + X(X), + Y(Y), + Z(Z), + result(result) +{ + result_bits.allocate(pb, 32, FMT(this->annotation_prefix, " result_bits")); + pack_result.reset(new packing_gadget(pb, result_bits, result, FMT(this->annotation_prefix, " result"))); +} + +template +void majority_gadget::generate_r1cs_constraints() +{ + for (size_t i = 0; i < 32; ++i) + { + /* + 2*result + aux = x + y + z + x, y, z, aux -- bits + aux = x + y + z - 2*result + */ + generate_boolean_r1cs_constraint(this->pb, result_bits[i], FMT(this->annotation_prefix, " result_%zu", i)); + this->pb.add_r1cs_constraint(r1cs_constraint(X[i] + Y[i] + Z[i] - 2 * result_bits[i], + 1 - (X[i] + Y[i] + Z[i] - 2 * result_bits[i]), + 0), + FMT(this->annotation_prefix, " result_bits_%zu", i)); + } + pack_result->generate_r1cs_constraints(false); +} + +template +void majority_gadget::generate_r1cs_witness() +{ + for (size_t i = 0; i < 32; ++i) + { + const long v = (this->pb.lc_val(X[i]) + this->pb.lc_val(Y[i]) + this->pb.lc_val(Z[i])).as_ulong(); + this->pb.val(result_bits[i]) = FieldT(v / 2); + } + + pack_result->generate_r1cs_witness_from_bits(); +} + +} // libsnark + +#endif // SHA256_AUX_TCC_ diff --git a/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_components.hpp b/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_components.hpp new file mode 100644 index 000000000..c2f31e3af --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_components.hpp @@ -0,0 +1,108 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for gadgets for the SHA256 message schedule and round function. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef SHA256_COMPONENTS_HPP_ +#define SHA256_COMPONENTS_HPP_ + +#include "gadgetlib1/gadgets/basic_gadgets.hpp" +#include "gadgetlib1/gadgets/hashes/hash_io.hpp" +#include "gadgetlib1/gadgets/hashes/sha256/sha256_aux.hpp" + +namespace libsnark { + +const size_t SHA256_digest_size = 256; +const size_t SHA256_block_size = 512; + +template +pb_linear_combination_array SHA256_default_IV(protoboard &pb); + +template +class sha256_message_schedule_gadget : public gadget { +public: + std::vector > W_bits; + std::vector > > pack_W; + + std::vector > sigma0; + std::vector > sigma1; + std::vector > > compute_sigma0; + std::vector > > compute_sigma1; + std::vector > unreduced_W; + std::vector > > mod_reduce_W; +public: + pb_variable_array M; + pb_variable_array packed_W; + sha256_message_schedule_gadget(protoboard &pb, + const pb_variable_array &M, + const pb_variable_array &packed_W, + const std::string &annotation_prefix); + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + +template +class sha256_round_function_gadget : public gadget { +public: + pb_variable sigma0; + pb_variable sigma1; + std::shared_ptr > compute_sigma0; + std::shared_ptr > compute_sigma1; + pb_variable choice; + pb_variable majority; + std::shared_ptr > compute_choice; + std::shared_ptr > compute_majority; + pb_variable packed_d; + std::shared_ptr > pack_d; + pb_variable packed_h; + std::shared_ptr > pack_h; + pb_variable unreduced_new_a; + pb_variable unreduced_new_e; + std::shared_ptr > mod_reduce_new_a; + std::shared_ptr > mod_reduce_new_e; + pb_variable packed_new_a; + pb_variable packed_new_e; +public: + pb_linear_combination_array a; + pb_linear_combination_array b; + pb_linear_combination_array c; + pb_linear_combination_array d; + pb_linear_combination_array e; + pb_linear_combination_array f; + pb_linear_combination_array g; + pb_linear_combination_array h; + pb_variable W; + long K; + pb_linear_combination_array new_a; + pb_linear_combination_array new_e; + + sha256_round_function_gadget(protoboard &pb, + const pb_linear_combination_array &a, + const pb_linear_combination_array &b, + const pb_linear_combination_array &c, + const pb_linear_combination_array &d, + const pb_linear_combination_array &e, + const pb_linear_combination_array &f, + const pb_linear_combination_array &g, + const pb_linear_combination_array &h, + const pb_variable &W, + const long &K, + const pb_linear_combination_array &new_a, + const pb_linear_combination_array &new_e, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + +} // libsnark + +#include "gadgetlib1/gadgets/hashes/sha256/sha256_components.tcc" + +#endif // SHA256_COMPONENTS_HPP_ diff --git a/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_components.tcc b/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_components.tcc new file mode 100644 index 000000000..e8f233a54 --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_components.tcc @@ -0,0 +1,250 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for gadgets for the SHA256 message schedule and round function. + + See sha256_components.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef SHA256_COMPONENTS_TCC_ +#define SHA256_COMPONENTS_TCC_ + +namespace libsnark { + +const unsigned long SHA256_K[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 unsigned long SHA256_H[8] = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 +}; + +template +pb_linear_combination_array SHA256_default_IV(protoboard &pb) +{ + pb_linear_combination_array result; + result.reserve(SHA256_digest_size); + + for (size_t i = 0; i < SHA256_digest_size; ++i) + { + int iv_val = (SHA256_H[i / 32] >> (31-(i % 32))) & 1; + + pb_linear_combination iv_element; + iv_element.assign(pb, iv_val * ONE); + iv_element.evaluate(pb); + + result.emplace_back(iv_element); + } + + return result; +} + +template +sha256_message_schedule_gadget::sha256_message_schedule_gadget(protoboard &pb, + const pb_variable_array &M, + const pb_variable_array &packed_W, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), + M(M), + packed_W(packed_W) +{ + W_bits.resize(64); + + pack_W.resize(16); + for (size_t i = 0; i < 16; ++i) + { + W_bits[i] = pb_variable_array(M.rbegin() + (15-i) * 32, M.rbegin() + (16-i) * 32); + pack_W[i].reset(new packing_gadget(pb, W_bits[i], packed_W[i], FMT(this->annotation_prefix, " pack_W_%zu", i))); + } + + /* NB: some of those will be un-allocated */ + sigma0.resize(64); + sigma1.resize(64); + compute_sigma0.resize(64); + compute_sigma1.resize(64); + unreduced_W.resize(64); + mod_reduce_W.resize(64); + + for (size_t i = 16; i < 64; ++i) + { + /* allocate result variables for sigma0/sigma1 invocations */ + sigma0[i].allocate(pb, FMT(this->annotation_prefix, " sigma0_%zu", i)); + sigma1[i].allocate(pb, FMT(this->annotation_prefix, " sigma1_%zu", i)); + + /* compute sigma0/sigma1 */ + compute_sigma0[i].reset(new small_sigma_gadget(pb, W_bits[i-15], sigma0[i], 7, 18, 3, FMT(this->annotation_prefix, " compute_sigma0_%zu", i))); + compute_sigma1[i].reset(new small_sigma_gadget(pb, W_bits[i-2], sigma1[i], 17, 19, 10, FMT(this->annotation_prefix, " compute_sigma1_%zu", i))); + + /* unreduced_W = sigma0(W_{i-15}) + sigma1(W_{i-2}) + W_{i-7} + W_{i-16} before modulo 2^32 */ + unreduced_W[i].allocate(pb, FMT(this->annotation_prefix, "unreduced_W_%zu", i)); + + /* allocate the bit representation of packed_W[i] */ + W_bits[i].allocate(pb, 32, FMT(this->annotation_prefix, " W_bits_%zu", i)); + + /* and finally reduce this into packed and bit representations */ + mod_reduce_W[i].reset(new lastbits_gadget(pb, unreduced_W[i], 32+2, packed_W[i], W_bits[i], FMT(this->annotation_prefix, " mod_reduce_W_%zu", i))); + } +} + +template +void sha256_message_schedule_gadget::generate_r1cs_constraints() +{ + for (size_t i = 0; i < 16; ++i) + { + pack_W[i]->generate_r1cs_constraints(false); // do not enforce bitness here; caller be aware. + } + + for (size_t i = 16; i < 64; ++i) + { + compute_sigma0[i]->generate_r1cs_constraints(); + compute_sigma1[i]->generate_r1cs_constraints(); + + this->pb.add_r1cs_constraint(r1cs_constraint(1, + sigma0[i] + sigma1[i] + packed_W[i-16] + packed_W[i-7], + unreduced_W[i]), + FMT(this->annotation_prefix, " unreduced_W_%zu", i)); + + mod_reduce_W[i]->generate_r1cs_constraints(); + } +} + +template +void sha256_message_schedule_gadget::generate_r1cs_witness() +{ + for (size_t i = 0; i < 16; ++i) + { + pack_W[i]->generate_r1cs_witness_from_bits(); + } + + for (size_t i = 16; i < 64; ++i) + { + compute_sigma0[i]->generate_r1cs_witness(); + compute_sigma1[i]->generate_r1cs_witness(); + + this->pb.val(unreduced_W[i]) = this->pb.val(sigma0[i]) + this->pb.val(sigma1[i]) + this->pb.val(packed_W[i-16]) + this->pb.val(packed_W[i-7]); + mod_reduce_W[i]->generate_r1cs_witness(); + } +} + +template +sha256_round_function_gadget::sha256_round_function_gadget(protoboard &pb, + const pb_linear_combination_array &a, + const pb_linear_combination_array &b, + const pb_linear_combination_array &c, + const pb_linear_combination_array &d, + const pb_linear_combination_array &e, + const pb_linear_combination_array &f, + const pb_linear_combination_array &g, + const pb_linear_combination_array &h, + const pb_variable &W, + const long &K, + const pb_linear_combination_array &new_a, + const pb_linear_combination_array &new_e, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), + a(a), + b(b), + c(c), + d(d), + e(e), + f(f), + g(g), + h(h), + W(W), + K(K), + new_a(new_a), + new_e(new_e) +{ + /* compute sigma0 and sigma1 */ + sigma0.allocate(pb, FMT(this->annotation_prefix, " sigma0")); + sigma1.allocate(pb, FMT(this->annotation_prefix, " sigma1")); + compute_sigma0.reset(new big_sigma_gadget(pb, a, sigma0, 2, 13, 22, FMT(this->annotation_prefix, " compute_sigma0"))); + compute_sigma1.reset(new big_sigma_gadget(pb, e, sigma1, 6, 11, 25, FMT(this->annotation_prefix, " compute_sigma1"))); + + /* compute choice */ + choice.allocate(pb, FMT(this->annotation_prefix, " choice")); + compute_choice.reset(new choice_gadget(pb, e, f, g, choice, FMT(this->annotation_prefix, " compute_choice"))); + + /* compute majority */ + majority.allocate(pb, FMT(this->annotation_prefix, " majority")); + compute_majority.reset(new majority_gadget(pb, a, b, c, majority, FMT(this->annotation_prefix, " compute_majority"))); + + /* pack d */ + packed_d.allocate(pb, FMT(this->annotation_prefix, " packed_d")); + pack_d.reset(new packing_gadget(pb, d, packed_d, FMT(this->annotation_prefix, " pack_d"))); + + /* pack h */ + packed_h.allocate(pb, FMT(this->annotation_prefix, " packed_h")); + pack_h.reset(new packing_gadget(pb, h, packed_h, FMT(this->annotation_prefix, " pack_h"))); + + /* compute the actual results for the round */ + unreduced_new_a.allocate(pb, FMT(this->annotation_prefix, " unreduced_new_a")); + unreduced_new_e.allocate(pb, FMT(this->annotation_prefix, " unreduced_new_e")); + + packed_new_a.allocate(pb, FMT(this->annotation_prefix, " packed_new_a")); + packed_new_e.allocate(pb, FMT(this->annotation_prefix, " packed_new_e")); + + mod_reduce_new_a.reset(new lastbits_gadget(pb, unreduced_new_a, 32+3, packed_new_a, new_a, FMT(this->annotation_prefix, " mod_reduce_new_a"))); + mod_reduce_new_e.reset(new lastbits_gadget(pb, unreduced_new_e, 32+3, packed_new_e, new_e, FMT(this->annotation_prefix, " mod_reduce_new_e"))); +} + +template +void sha256_round_function_gadget::generate_r1cs_constraints() +{ + compute_sigma0->generate_r1cs_constraints(); + compute_sigma1->generate_r1cs_constraints(); + + compute_choice->generate_r1cs_constraints(); + compute_majority->generate_r1cs_constraints(); + + pack_d->generate_r1cs_constraints(false); + pack_h->generate_r1cs_constraints(false); + + this->pb.add_r1cs_constraint(r1cs_constraint(1, + packed_h + sigma1 + choice + K + W + sigma0 + majority, + unreduced_new_a), + FMT(this->annotation_prefix, " unreduced_new_a")); + + this->pb.add_r1cs_constraint(r1cs_constraint(1, + packed_d + packed_h + sigma1 + choice + K + W, + unreduced_new_e), + FMT(this->annotation_prefix, " unreduced_new_e")); + + mod_reduce_new_a->generate_r1cs_constraints(); + mod_reduce_new_e->generate_r1cs_constraints(); +} + +template +void sha256_round_function_gadget::generate_r1cs_witness() +{ + compute_sigma0->generate_r1cs_witness(); + compute_sigma1->generate_r1cs_witness(); + + compute_choice->generate_r1cs_witness(); + compute_majority->generate_r1cs_witness(); + + pack_d->generate_r1cs_witness_from_bits(); + pack_h->generate_r1cs_witness_from_bits(); + + this->pb.val(unreduced_new_a) = this->pb.val(packed_h) + this->pb.val(sigma1) + this->pb.val(choice) + FieldT(K) + this->pb.val(W) + this->pb.val(sigma0) + this->pb.val(majority); + this->pb.val(unreduced_new_e) = this->pb.val(packed_d) + this->pb.val(packed_h) + this->pb.val(sigma1) + this->pb.val(choice) + FieldT(K) + this->pb.val(W); + + mod_reduce_new_a->generate_r1cs_witness(); + mod_reduce_new_e->generate_r1cs_witness(); +} + +} // libsnark + +#endif // SHA256_COMPONENTS_TCC_ diff --git a/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp b/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp new file mode 100644 index 000000000..8cb6365c8 --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp @@ -0,0 +1,98 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for top-level SHA256 gadgets. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef SHA256_GADGET_HPP_ +#define SHA256_GADGET_HPP_ + +#include "common/data_structures/merkle_tree.hpp" +#include "gadgetlib1/gadgets/basic_gadgets.hpp" +#include "gadgetlib1/gadgets/hashes/hash_io.hpp" +#include "gadgetlib1/gadgets/hashes/sha256/sha256_components.hpp" + +namespace libsnark { + +/** + * Gadget for the SHA256 compression function. + */ +template +class sha256_compression_function_gadget : public gadget { +public: + std::vector > round_a; + std::vector > round_b; + std::vector > round_c; + std::vector > round_d; + std::vector > round_e; + std::vector > round_f; + std::vector > round_g; + std::vector > round_h; + + pb_variable_array packed_W; + std::shared_ptr > message_schedule; + std::vector > round_functions; + + pb_variable_array unreduced_output; + pb_variable_array reduced_output; + std::vector > reduce_output; +public: + pb_linear_combination_array prev_output; + pb_variable_array new_block; + digest_variable output; + + sha256_compression_function_gadget(protoboard &pb, + const pb_linear_combination_array &prev_output, + const pb_variable_array &new_block, + const digest_variable &output, + const std::string &annotation_prefix); + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + +/** + * Gadget for the SHA256 compression function, viewed as a 2-to-1 hash + * function, and using the same initialization vector as in SHA256 + * specification. Thus, any collision for + * sha256_two_to_one_hash_gadget trivially extends to a collision for + * full SHA256 (by appending the same padding). + */ +template +class sha256_two_to_one_hash_gadget : public gadget { +public: + typedef bit_vector hash_value_type; + typedef merkle_authentication_path merkle_authentication_path_type; + + std::shared_ptr > f; + + sha256_two_to_one_hash_gadget(protoboard &pb, + const digest_variable &left, + const digest_variable &right, + const digest_variable &output, + const std::string &annotation_prefix); + sha256_two_to_one_hash_gadget(protoboard &pb, + const size_t block_length, + const block_variable &input_block, + const digest_variable &output, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(const bool ensure_output_bitness=true); // TODO: ignored for now + void generate_r1cs_witness(); + + static size_t get_block_len(); + static size_t get_digest_len(); + static bit_vector get_hash(const bit_vector &input); + + static size_t expected_constraints(const bool ensure_output_bitness=true); // TODO: ignored for now +}; + +} // libsnark + +#include "gadgetlib1/gadgets/hashes/sha256/sha256_gadget.tcc" + +#endif // SHA256_GADGET_HPP_ diff --git a/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.tcc b/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.tcc new file mode 100644 index 000000000..fc7ac982a --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.tcc @@ -0,0 +1,230 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for top-level SHA256 gadgets. + + See sha256_gadget.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef SHA256_GADGET_TCC_ +#define SHA256_GADGET_TCC_ + +namespace libsnark { + +template +sha256_compression_function_gadget::sha256_compression_function_gadget(protoboard &pb, + const pb_linear_combination_array &prev_output, + const pb_variable_array &new_block, + const digest_variable &output, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), + prev_output(prev_output), + new_block(new_block), + output(output) +{ + /* message schedule and inputs for it */ + packed_W.allocate(pb, 64, FMT(this->annotation_prefix, " packed_W")); + message_schedule.reset(new sha256_message_schedule_gadget(pb, new_block, packed_W, FMT(this->annotation_prefix, " message_schedule"))); + + /* initalize */ + round_a.push_back(pb_linear_combination_array(prev_output.rbegin() + 7*32, prev_output.rbegin() + 8*32)); + round_b.push_back(pb_linear_combination_array(prev_output.rbegin() + 6*32, prev_output.rbegin() + 7*32)); + round_c.push_back(pb_linear_combination_array(prev_output.rbegin() + 5*32, prev_output.rbegin() + 6*32)); + round_d.push_back(pb_linear_combination_array(prev_output.rbegin() + 4*32, prev_output.rbegin() + 5*32)); + round_e.push_back(pb_linear_combination_array(prev_output.rbegin() + 3*32, prev_output.rbegin() + 4*32)); + round_f.push_back(pb_linear_combination_array(prev_output.rbegin() + 2*32, prev_output.rbegin() + 3*32)); + round_g.push_back(pb_linear_combination_array(prev_output.rbegin() + 1*32, prev_output.rbegin() + 2*32)); + round_h.push_back(pb_linear_combination_array(prev_output.rbegin() + 0*32, prev_output.rbegin() + 1*32)); + + /* do the rounds */ + for (size_t i = 0; i < 64; ++i) + { + round_h.push_back(round_g[i]); + round_g.push_back(round_f[i]); + round_f.push_back(round_e[i]); + round_d.push_back(round_c[i]); + round_c.push_back(round_b[i]); + round_b.push_back(round_a[i]); + + pb_variable_array new_round_a_variables; + new_round_a_variables.allocate(pb, 32, FMT(this->annotation_prefix, " new_round_a_variables_%zu", i+1)); + round_a.emplace_back(new_round_a_variables); + + pb_variable_array new_round_e_variables; + new_round_e_variables.allocate(pb, 32, FMT(this->annotation_prefix, " new_round_e_variables_%zu", i+1)); + round_e.emplace_back(new_round_e_variables); + + round_functions.push_back(sha256_round_function_gadget(pb, + round_a[i], round_b[i], round_c[i], round_d[i], + round_e[i], round_f[i], round_g[i], round_h[i], + packed_W[i], SHA256_K[i], round_a[i+1], round_e[i+1], + FMT(this->annotation_prefix, " round_functions_%zu", i))); + } + + /* finalize */ + unreduced_output.allocate(pb, 8, FMT(this->annotation_prefix, " unreduced_output")); + reduced_output.allocate(pb, 8, FMT(this->annotation_prefix, " reduced_output")); + for (size_t i = 0; i < 8; ++i) + { + reduce_output.push_back(lastbits_gadget(pb, + unreduced_output[i], + 32+1, + reduced_output[i], + pb_variable_array(output.bits.rbegin() + (7-i) * 32, output.bits.rbegin() + (8-i) * 32), + FMT(this->annotation_prefix, " reduce_output_%zu", i))); + } +} + +template +void sha256_compression_function_gadget::generate_r1cs_constraints() +{ + message_schedule->generate_r1cs_constraints(); + for (size_t i = 0; i < 64; ++i) + { + round_functions[i].generate_r1cs_constraints(); + } + + for (size_t i = 0; i < 4; ++i) + { + this->pb.add_r1cs_constraint(r1cs_constraint(1, + round_functions[3-i].packed_d + round_functions[63-i].packed_new_a, + unreduced_output[i]), + FMT(this->annotation_prefix, " unreduced_output_%zu", i)); + + this->pb.add_r1cs_constraint(r1cs_constraint(1, + round_functions[3-i].packed_h + round_functions[63-i].packed_new_e, + unreduced_output[4+i]), + FMT(this->annotation_prefix, " unreduced_output_%zu", 4+i)); + } + + for (size_t i = 0; i < 8; ++i) + { + reduce_output[i].generate_r1cs_constraints(); + } +} + +template +void sha256_compression_function_gadget::generate_r1cs_witness() +{ + message_schedule->generate_r1cs_witness(); + +#ifdef DEBUG + printf("Input:\n"); + for (size_t j = 0; j < 16; ++j) + { + printf("%lx ", this->pb.val(packed_W[j]).as_ulong()); + } + printf("\n"); +#endif + + for (size_t i = 0; i < 64; ++i) + { + round_functions[i].generate_r1cs_witness(); + } + + for (size_t i = 0; i < 4; ++i) + { + this->pb.val(unreduced_output[i]) = this->pb.val(round_functions[3-i].packed_d) + this->pb.val(round_functions[63-i].packed_new_a); + this->pb.val(unreduced_output[4+i]) = this->pb.val(round_functions[3-i].packed_h) + this->pb.val(round_functions[63-i].packed_new_e); + } + + for (size_t i = 0; i < 8; ++i) + { + reduce_output[i].generate_r1cs_witness(); + } + +#ifdef DEBUG + printf("Output:\n"); + for (size_t j = 0; j < 8; ++j) + { + printf("%lx ", this->pb.val(reduced_output[j]).as_ulong()); + } + printf("\n"); +#endif +} + +template +sha256_two_to_one_hash_gadget::sha256_two_to_one_hash_gadget(protoboard &pb, + const digest_variable &left, + const digest_variable &right, + const digest_variable &output, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix) +{ + /* concatenate block = left || right */ + pb_variable_array block; + block.insert(block.end(), left.bits.begin(), left.bits.end()); + block.insert(block.end(), right.bits.begin(), right.bits.end()); + + /* compute the hash itself */ + f.reset(new sha256_compression_function_gadget(pb, SHA256_default_IV(pb), block, output, FMT(this->annotation_prefix, " f"))); +} + +template +sha256_two_to_one_hash_gadget::sha256_two_to_one_hash_gadget(protoboard &pb, + const size_t block_length, + const block_variable &input_block, + const digest_variable &output, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix) +{ + assert(block_length == SHA256_block_size); + assert(input_block.bits.size() == block_length); + f.reset(new sha256_compression_function_gadget(pb, SHA256_default_IV(pb), input_block.bits, output, FMT(this->annotation_prefix, " f"))); +} + +template +void sha256_two_to_one_hash_gadget::generate_r1cs_constraints(const bool ensure_output_bitness) +{ + UNUSED(ensure_output_bitness); + f->generate_r1cs_constraints(); +} + +template +void sha256_two_to_one_hash_gadget::generate_r1cs_witness() +{ + f->generate_r1cs_witness(); +} + +template +size_t sha256_two_to_one_hash_gadget::get_block_len() +{ + return SHA256_block_size; +} + +template +size_t sha256_two_to_one_hash_gadget::get_digest_len() +{ + return SHA256_digest_size; +} + +template +bit_vector sha256_two_to_one_hash_gadget::get_hash(const bit_vector &input) +{ + protoboard pb; + + block_variable input_variable(pb, SHA256_block_size, "input"); + digest_variable output_variable(pb, SHA256_digest_size, "output"); + sha256_two_to_one_hash_gadget f(pb, SHA256_block_size, input_variable, output_variable, "f"); + + input_variable.generate_r1cs_witness(input); + f.generate_r1cs_witness(); + + return output_variable.get_digest(); +} + +template +size_t sha256_two_to_one_hash_gadget::expected_constraints(const bool ensure_output_bitness) +{ + UNUSED(ensure_output_bitness); + return 27280; /* hardcoded for now */ +} + +} // libsnark + +#endif // SHA256_GADGET_TCC_ diff --git a/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/tests/generate_sha256_gadget_tests.py b/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/tests/generate_sha256_gadget_tests.py new file mode 100644 index 000000000..452317ffb --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/tests/generate_sha256_gadget_tests.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +## +# @author This file is part of libsnark, developed by SCIPR Lab +# and contributors (see AUTHORS). +# @copyright MIT license (see LICENSE file) + +import random +import pypy_sha256 # PyPy's implementation of SHA256 compression function; see copyright and authorship notice within. + +BLOCK_LEN = 512 +BLOCK_BYTES = BLOCK_LEN // 8 +HASH_LEN = 256 +HASH_BYTES = HASH_LEN // 8 + +def gen_random_bytes(n): + return [random.randint(0, 255) for i in xrange(n)] + +def words_to_bytes(arr): + return sum(([x >> 24, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff] for x in arr), []) + +def bytes_to_words(arr): + l = len(arr) + assert l % 4 == 0 + return [(arr[i*4 + 3] << 24) + (arr[i*4+2] << 16) + (arr[i*4+1] << 8) + arr[i*4] for i in xrange(l//4)] + +def cpp_val(s, log_radix=32): + if log_radix == 8: + hexfmt = '0x%02x' + elif log_radix == 32: + hexfmt = '0x%08x' + s = bytes_to_words(s) + else: + raise + return 'int_list_to_bits({%s}, %d)' % (', '.join(hexfmt % x for x in s), log_radix) + +def H_bytes(x): + assert len(x) == BLOCK_BYTES + state = pypy_sha256.sha_init() + state['data'] = words_to_bytes(bytes_to_words(x)) + pypy_sha256.sha_transform(state) + return words_to_bytes(bytes_to_words(words_to_bytes(state['digest']))) + +def generate_sha256_gadget_tests(): + left = gen_random_bytes(HASH_BYTES) + right = gen_random_bytes(HASH_BYTES) + hash = H_bytes(left + right) + + print "const bit_vector left_bv = %s;" % cpp_val(left) + print "const bit_vector right_bv = %s;" % cpp_val(right) + print "const bit_vector hash_bv = %s;" % cpp_val(hash) + +if __name__ == '__main__': + random.seed(0) # for reproducibility + generate_sha256_gadget_tests() + diff --git a/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/tests/pypy_sha256.py b/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/tests/pypy_sha256.py new file mode 100644 index 000000000..496989c11 --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/tests/pypy_sha256.py @@ -0,0 +1,263 @@ +#!/usr/bin/env python +# +# SHA256 compression function implementation below is a verbatim copy of PyPy's implementation from +# https://bitbucket.org/pypy/pypy/raw/f1f064b3faf1e012f7a9a9ab08f18074637ebe8a/lib_pypy/_sha256.py . +# +# It is licensed under the MIT license and copyright PyPy Copyright holders 2003-2015 +# See https://bitbucket.org/pypy/pypy/src/tip/LICENSE for the full copyright notice. +# + +SHA_BLOCKSIZE = 64 +SHA_DIGESTSIZE = 32 + + +def new_shaobject(): + return { + 'digest': [0]*8, + 'count_lo': 0, + 'count_hi': 0, + 'data': [0]* SHA_BLOCKSIZE, + 'local': 0, + 'digestsize': 0 + } + +ROR = lambda x, y: (((x & 0xffffffff) >> (y & 31)) | (x << (32 - (y & 31)))) & 0xffffffff +Ch = lambda x, y, z: (z ^ (x & (y ^ z))) +Maj = lambda x, y, z: (((x | y) & z) | (x & y)) +S = lambda x, n: ROR(x, n) +R = lambda x, n: (x & 0xffffffff) >> n +Sigma0 = lambda x: (S(x, 2) ^ S(x, 13) ^ S(x, 22)) +Sigma1 = lambda x: (S(x, 6) ^ S(x, 11) ^ S(x, 25)) +Gamma0 = lambda x: (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +Gamma1 = lambda x: (S(x, 17) ^ S(x, 19) ^ R(x, 10)) + +def sha_transform(sha_info): + W = [] + + d = sha_info['data'] + for i in range(0,16): + W.append( (d[4*i]<<24) + (d[4*i+1]<<16) + (d[4*i+2]<<8) + d[4*i+3]) + + for i in range(16,64): + W.append( (Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]) & 0xffffffff ) + + ss = sha_info['digest'][:] + + def RND(a,b,c,d,e,f,g,h,i,ki): + t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; + t1 = Sigma0(a) + Maj(a, b, c); + d += t0; + h = t0 + t1; + return d & 0xffffffff, h & 0xffffffff + + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],0,0x428a2f98); + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],1,0x71374491); + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],2,0xb5c0fbcf); + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],3,0xe9b5dba5); + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],4,0x3956c25b); + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],5,0x59f111f1); + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],6,0x923f82a4); + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],7,0xab1c5ed5); + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],8,0xd807aa98); + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],9,0x12835b01); + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],10,0x243185be); + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],11,0x550c7dc3); + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],12,0x72be5d74); + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],13,0x80deb1fe); + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],14,0x9bdc06a7); + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],15,0xc19bf174); + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],16,0xe49b69c1); + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],17,0xefbe4786); + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],18,0x0fc19dc6); + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],19,0x240ca1cc); + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],20,0x2de92c6f); + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],21,0x4a7484aa); + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],22,0x5cb0a9dc); + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],23,0x76f988da); + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],24,0x983e5152); + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],25,0xa831c66d); + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],26,0xb00327c8); + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],27,0xbf597fc7); + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],28,0xc6e00bf3); + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],29,0xd5a79147); + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],30,0x06ca6351); + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],31,0x14292967); + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],32,0x27b70a85); + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],33,0x2e1b2138); + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],34,0x4d2c6dfc); + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],35,0x53380d13); + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],36,0x650a7354); + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],37,0x766a0abb); + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],38,0x81c2c92e); + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],39,0x92722c85); + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],40,0xa2bfe8a1); + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],41,0xa81a664b); + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],42,0xc24b8b70); + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],43,0xc76c51a3); + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],44,0xd192e819); + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],45,0xd6990624); + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],46,0xf40e3585); + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],47,0x106aa070); + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],48,0x19a4c116); + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],49,0x1e376c08); + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],50,0x2748774c); + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],51,0x34b0bcb5); + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],52,0x391c0cb3); + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],53,0x4ed8aa4a); + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],54,0x5b9cca4f); + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],55,0x682e6ff3); + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],56,0x748f82ee); + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],57,0x78a5636f); + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],58,0x84c87814); + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],59,0x8cc70208); + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],60,0x90befffa); + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],61,0xa4506ceb); + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],62,0xbef9a3f7); + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],63,0xc67178f2); + + dig = [] + for i, x in enumerate(sha_info['digest']): + dig.append( (x + ss[i]) & 0xffffffff ) + sha_info['digest'] = dig + +def sha_init(): + sha_info = new_shaobject() + sha_info['digest'] = [0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19] + sha_info['count_lo'] = 0 + sha_info['count_hi'] = 0 + sha_info['local'] = 0 + sha_info['digestsize'] = 32 + return sha_info + +def sha224_init(): + sha_info = new_shaobject() + sha_info['digest'] = [0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4] + sha_info['count_lo'] = 0 + sha_info['count_hi'] = 0 + sha_info['local'] = 0 + sha_info['digestsize'] = 28 + return sha_info + +def sha_update(sha_info, buffer): + if isinstance(buffer, str): + raise TypeError("Unicode strings must be encoded before hashing") + count = len(buffer) + buffer_idx = 0 + clo = (sha_info['count_lo'] + (count << 3)) & 0xffffffff + if clo < sha_info['count_lo']: + sha_info['count_hi'] += 1 + sha_info['count_lo'] = clo + + sha_info['count_hi'] += (count >> 29) + + if sha_info['local']: + i = SHA_BLOCKSIZE - sha_info['local'] + if i > count: + i = count + + # copy buffer + sha_info['data'][sha_info['local']:sha_info['local']+i] = buffer[buffer_idx:buffer_idx+i] + + count -= i + buffer_idx += i + + sha_info['local'] += i + if sha_info['local'] == SHA_BLOCKSIZE: + sha_transform(sha_info) + sha_info['local'] = 0 + else: + return + + while count >= SHA_BLOCKSIZE: + # copy buffer + sha_info['data'] = list(buffer[buffer_idx:buffer_idx + SHA_BLOCKSIZE]) + count -= SHA_BLOCKSIZE + buffer_idx += SHA_BLOCKSIZE + sha_transform(sha_info) + + + # copy buffer + pos = sha_info['local'] + sha_info['data'][pos:pos+count] = buffer[buffer_idx:buffer_idx + count] + sha_info['local'] = count + +def sha_final(sha_info): + lo_bit_count = sha_info['count_lo'] + hi_bit_count = sha_info['count_hi'] + count = (lo_bit_count >> 3) & 0x3f + sha_info['data'][count] = 0x80; + count += 1 + if count > SHA_BLOCKSIZE - 8: + # zero the bytes in data after the count + sha_info['data'] = sha_info['data'][:count] + ([0] * (SHA_BLOCKSIZE - count)) + sha_transform(sha_info) + # zero bytes in data + sha_info['data'] = [0] * SHA_BLOCKSIZE + else: + sha_info['data'] = sha_info['data'][:count] + ([0] * (SHA_BLOCKSIZE - count)) + + sha_info['data'][56] = (hi_bit_count >> 24) & 0xff + sha_info['data'][57] = (hi_bit_count >> 16) & 0xff + sha_info['data'][58] = (hi_bit_count >> 8) & 0xff + sha_info['data'][59] = (hi_bit_count >> 0) & 0xff + sha_info['data'][60] = (lo_bit_count >> 24) & 0xff + sha_info['data'][61] = (lo_bit_count >> 16) & 0xff + sha_info['data'][62] = (lo_bit_count >> 8) & 0xff + sha_info['data'][63] = (lo_bit_count >> 0) & 0xff + + sha_transform(sha_info) + + dig = [] + for i in sha_info['digest']: + dig.extend([ ((i>>24) & 0xff), ((i>>16) & 0xff), ((i>>8) & 0xff), (i & 0xff) ]) + return ''.join([chr(i) for i in dig]) + +class sha256(object): + digest_size = digestsize = SHA_DIGESTSIZE + block_size = SHA_BLOCKSIZE + + def __init__(self, s=None): + self._sha = sha_init() + if s: + sha_update(self._sha, s) + + def update(self, s): + sha_update(self._sha, s) + + def digest(self): + return sha_final(self._sha.copy())[:self._sha['digestsize']] + + def hexdigest(self): + return ''.join(['%.2x' % ord(i) for i in self.digest()]) + + def copy(self): + new = sha256.__new__(sha256) + new._sha = self._sha.copy() + return new + +class sha224(sha256): + digest_size = digestsize = 28 + + def __init__(self, s=None): + self._sha = sha224_init() + if s: + sha_update(self._sha, s) + + def copy(self): + new = sha224.__new__(sha224) + new._sha = self._sha.copy() + return new + +def test(): + a_str = "just a test string" + + assert 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' == sha256().hexdigest() + assert 'd7b553c6f09ac85d142415f857c5310f3bbbe7cdd787cce4b985acedd585266f' == sha256(a_str).hexdigest() + assert '8113ebf33c97daa9998762aacafe750c7cefc2b2f173c90c59663a57fe626f21' == sha256(a_str*7).hexdigest() + + s = sha256(a_str) + s.update(a_str) + assert '03d9963e05a094593190b6fc794cb1a3e1ac7d7883f0b5855268afeccc70d461' == s.hexdigest() + +if __name__ == "__main__": + test() diff --git a/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/tests/test_sha256_gadget.cpp b/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/tests/test_sha256_gadget.cpp new file mode 100644 index 000000000..0bfaf3a12 --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/hashes/sha256/tests/test_sha256_gadget.cpp @@ -0,0 +1,48 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include "common/default_types/ec_pp.hpp" +#include "common/utils.hpp" +#include "common/profiling.hpp" +#include "gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp" + +#include + +using namespace libsnark; + +template +void test_two_to_one() +{ + protoboard pb; + + digest_variable left(pb, SHA256_digest_size, "left"); + digest_variable right(pb, SHA256_digest_size, "right"); + digest_variable output(pb, SHA256_digest_size, "output"); + + sha256_two_to_one_hash_gadget f(pb, left, right, output, "f"); + f.generate_r1cs_constraints(); + printf("Number of constraints for sha256_two_to_one_hash_gadget: %zu\n", pb.num_constraints()); + + const bit_vector left_bv = int_list_to_bits({0x426bc2d8, 0x4dc86782, 0x81e8957a, 0x409ec148, 0xe6cffbe8, 0xafe6ba4f, 0x9c6f1978, 0xdd7af7e9}, 32); + const bit_vector right_bv = int_list_to_bits({0x038cce42, 0xabd366b8, 0x3ede7e00, 0x9130de53, 0x72cdf73d, 0xee825114, 0x8cb48d1b, 0x9af68ad0}, 32); + const bit_vector hash_bv = int_list_to_bits({0xeffd0b7f, 0x1ccba116, 0x2ee816f7, 0x31c62b48, 0x59305141, 0x990e5c0a, 0xce40d33d, 0x0b1167d1}, 32); + + left.generate_r1cs_witness(left_bv); + right.generate_r1cs_witness(right_bv); + + f.generate_r1cs_witness(); + output.generate_r1cs_witness(hash_bv); + + EXPECT_TRUE(pb.is_satisfied()); +} + +TEST(gadgetlib1, sha256) +{ + start_profiling(); + default_ec_pp::init_public_params(); + test_two_to_one >(); +} diff --git a/src/snark/libsnark/gadgetlib1/gadgets/merkle_tree/merkle_authentication_path_variable.hpp b/src/snark/libsnark/gadgetlib1/gadgets/merkle_tree/merkle_authentication_path_variable.hpp new file mode 100644 index 000000000..0efa7cf4d --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/merkle_tree/merkle_authentication_path_variable.hpp @@ -0,0 +1,38 @@ +/** + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef MERKLE_AUTHENTICATION_PATH_VARIABLE_HPP_ +#define MERKLE_AUTHENTICATION_PATH_VARIABLE_HPP_ + +#include "common/data_structures/merkle_tree.hpp" +#include "gadgetlib1/gadget.hpp" +#include "gadgetlib1/gadgets/hashes/hash_io.hpp" + +namespace libsnark { + +template +class merkle_authentication_path_variable : public gadget { +public: + + const size_t tree_depth; + std::vector > left_digests; + std::vector > right_digests; + + merkle_authentication_path_variable(protoboard &pb, + const size_t tree_depth, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness(const size_t address, const merkle_authentication_path &path); + merkle_authentication_path get_authentication_path(const size_t address) const; +}; + +} // libsnark + +#include "gadgetlib1/gadgets/merkle_tree/merkle_authentication_path_variable.tcc" + +#endif // MERKLE_AUTHENTICATION_PATH_VARIABLE_HPP diff --git a/src/snark/libsnark/gadgetlib1/gadgets/merkle_tree/merkle_authentication_path_variable.tcc b/src/snark/libsnark/gadgetlib1/gadgets/merkle_tree/merkle_authentication_path_variable.tcc new file mode 100644 index 000000000..d773051ab --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/merkle_tree/merkle_authentication_path_variable.tcc @@ -0,0 +1,76 @@ +/** + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef MERKLE_AUTHENTICATION_PATH_VARIABLE_TCC_ +#define MERKLE_AUTHENTICATION_PATH_VARIABLE_TCC_ + +namespace libsnark { + +template +merkle_authentication_path_variable::merkle_authentication_path_variable(protoboard &pb, + const size_t tree_depth, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), + tree_depth(tree_depth) +{ + for (size_t i = 0; i < tree_depth; ++i) + { + left_digests.emplace_back(digest_variable(pb, HashT::get_digest_len(), FMT(annotation_prefix, " left_digests_%zu", i))); + right_digests.emplace_back(digest_variable(pb, HashT::get_digest_len(), FMT(annotation_prefix, " right_digests_%zu", i))); + } +} + +template +void merkle_authentication_path_variable::generate_r1cs_constraints() +{ + for (size_t i = 0; i < tree_depth; ++i) + { + left_digests[i].generate_r1cs_constraints(); + right_digests[i].generate_r1cs_constraints(); + } +} + +template +void merkle_authentication_path_variable::generate_r1cs_witness(const size_t address, const merkle_authentication_path &path) +{ + assert(path.size() == tree_depth); + + for (size_t i = 0; i < tree_depth; ++i) + { + if (address & (1ul << (tree_depth-1-i))) + { + left_digests[i].generate_r1cs_witness(path[i]); + } + else + { + right_digests[i].generate_r1cs_witness(path[i]); + } + } +} + +template +merkle_authentication_path merkle_authentication_path_variable::get_authentication_path(const size_t address) const +{ + merkle_authentication_path result; + for (size_t i = 0; i < tree_depth; ++i) + { + if (address & (1ul << (tree_depth-1-i))) + { + result.emplace_back(left_digests[i].get_digest()); + } + else + { + result.emplace_back(right_digests[i].get_digest()); + } + } + + return result; +} + +} // libsnark + +#endif // MERKLE_AUTHENTICATION_PATH_VARIABLE_TCC diff --git a/src/snark/libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.hpp b/src/snark/libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.hpp new file mode 100644 index 000000000..b1e3a4f05 --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.hpp @@ -0,0 +1,73 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for the Merkle tree check read gadget. + + The gadget checks the following: given a root R, address A, value V, and + authentication path P, check that P is a valid authentication path for the + value V as the A-th leaf in a Merkle tree with root R. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef MERKLE_TREE_CHECK_READ_GADGET_HPP_ +#define MERKLE_TREE_CHECK_READ_GADGET_HPP_ + +#include "common/data_structures/merkle_tree.hpp" +#include "gadgetlib1/gadget.hpp" +#include "gadgetlib1/gadgets/hashes/hash_io.hpp" +#include "gadgetlib1/gadgets/hashes/digest_selector_gadget.hpp" +#include "gadgetlib1/gadgets/merkle_tree/merkle_authentication_path_variable.hpp" + +namespace libsnark { + +template +class merkle_tree_check_read_gadget : public gadget { +private: + + std::vector hashers; + std::vector > hasher_inputs; + std::vector > propagators; + std::vector > internal_output; + + std::shared_ptr > computed_root; + std::shared_ptr > check_root; + +public: + + const size_t digest_size; + const size_t tree_depth; + pb_linear_combination_array address_bits; + digest_variable leaf; + digest_variable root; + merkle_authentication_path_variable path; + pb_linear_combination read_successful; + + merkle_tree_check_read_gadget(protoboard &pb, + const size_t tree_depth, + const pb_linear_combination_array &address_bits, + const digest_variable &leaf_digest, + const digest_variable &root_digest, + const merkle_authentication_path_variable &path, + const pb_linear_combination &read_successful, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); + + static size_t root_size_in_bits(); + /* for debugging purposes */ + static size_t expected_constraints(const size_t tree_depth); +}; + +template +void test_merkle_tree_check_read_gadget(); + +} // libsnark + +#include "gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.tcc" + +#endif // MERKLE_TREE_CHECK_READ_GADGET_HPP_ diff --git a/src/snark/libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.tcc b/src/snark/libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.tcc new file mode 100644 index 000000000..6002a5886 --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.tcc @@ -0,0 +1,196 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for the Merkle tree check read. + + See merkle_tree_check_read_gadget.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef MERKLE_TREE_CHECK_READ_GADGET_TCC_ +#define MERKLE_TREE_CHECK_READ_GADGET_TCC_ + +namespace libsnark { + +template +merkle_tree_check_read_gadget::merkle_tree_check_read_gadget(protoboard &pb, + const size_t tree_depth, + const pb_linear_combination_array &address_bits, + const digest_variable &leaf, + const digest_variable &root, + const merkle_authentication_path_variable &path, + const pb_linear_combination &read_successful, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), + digest_size(HashT::get_digest_len()), + tree_depth(tree_depth), + address_bits(address_bits), + leaf(leaf), + root(root), + path(path), + read_successful(read_successful) +{ + /* + The tricky part here is ordering. For Merkle tree + authentication paths, path[0] corresponds to one layer below + the root (and path[tree_depth-1] corresponds to the layer + containing the leaf), while address_bits has the reverse order: + address_bits[0] is LSB, and corresponds to layer containing the + leaf, and address_bits[tree_depth-1] is MSB, and corresponds to + the subtree directly under the root. + */ + assert(tree_depth > 0); + assert(tree_depth == address_bits.size()); + + for (size_t i = 0; i < tree_depth-1; ++i) + { + internal_output.emplace_back(digest_variable(pb, digest_size, FMT(this->annotation_prefix, " internal_output_%zu", i))); + } + + computed_root.reset(new digest_variable(pb, digest_size, FMT(this->annotation_prefix, " computed_root"))); + + for (size_t i = 0; i < tree_depth; ++i) + { + block_variable inp(pb, path.left_digests[i], path.right_digests[i], FMT(this->annotation_prefix, " inp_%zu", i)); + hasher_inputs.emplace_back(inp); + hashers.emplace_back(HashT(pb, 2*digest_size, inp, (i == 0 ? *computed_root : internal_output[i-1]), + FMT(this->annotation_prefix, " load_hashers_%zu", i))); + } + + for (size_t i = 0; i < tree_depth; ++i) + { + /* + The propagators take a computed hash value (or leaf in the + base case) and propagate it one layer up, either in the left + or the right slot of authentication_path_variable. + */ + propagators.emplace_back(digest_selector_gadget(pb, digest_size, i < tree_depth - 1 ? internal_output[i] : leaf, + address_bits[tree_depth-1-i], path.left_digests[i], path.right_digests[i], + FMT(this->annotation_prefix, " digest_selector_%zu", i))); + } + + check_root.reset(new bit_vector_copy_gadget(pb, computed_root->bits, root.bits, read_successful, FieldT::capacity(), FMT(annotation_prefix, " check_root"))); +} + +template +void merkle_tree_check_read_gadget::generate_r1cs_constraints() +{ + /* ensure correct hash computations */ + for (size_t i = 0; i < tree_depth; ++i) + { + // Note that we check root outside and have enforced booleanity of path.left_digests/path.right_digests outside in path.generate_r1cs_constraints + hashers[i].generate_r1cs_constraints(false); + } + + /* ensure consistency of path.left_digests/path.right_digests with internal_output */ + for (size_t i = 0; i < tree_depth; ++i) + { + propagators[i].generate_r1cs_constraints(); + } + + check_root->generate_r1cs_constraints(false, false); +} + +template +void merkle_tree_check_read_gadget::generate_r1cs_witness() +{ + /* do the hash computations bottom-up */ + for (int i = tree_depth-1; i >= 0; --i) + { + /* propagate previous input */ + propagators[i].generate_r1cs_witness(); + + /* compute hash */ + hashers[i].generate_r1cs_witness(); + } + + check_root->generate_r1cs_witness(); +} + +template +size_t merkle_tree_check_read_gadget::root_size_in_bits() +{ + return HashT::get_digest_len(); +} + +template +size_t merkle_tree_check_read_gadget::expected_constraints(const size_t tree_depth) +{ + /* NB: this includes path constraints */ + const size_t hasher_constraints = tree_depth * HashT::expected_constraints(false); + const size_t propagator_constraints = tree_depth * HashT::get_digest_len(); + const size_t authentication_path_constraints = 2 * tree_depth * HashT::get_digest_len(); + const size_t check_root_constraints = 3 * div_ceil(HashT::get_digest_len(), FieldT::capacity()); + + return hasher_constraints + propagator_constraints + authentication_path_constraints + check_root_constraints; +} + +template +void test_merkle_tree_check_read_gadget() +{ + /* prepare test */ + const size_t digest_len = HashT::get_digest_len(); + const size_t tree_depth = 16; + std::vector path(tree_depth); + + bit_vector prev_hash(digest_len); + std::generate(prev_hash.begin(), prev_hash.end(), [&]() { return std::rand() % 2; }); + bit_vector leaf = prev_hash; + + bit_vector address_bits; + + size_t address = 0; + for (long level = tree_depth-1; level >= 0; --level) + { + const bool computed_is_right = (std::rand() % 2); + address |= (computed_is_right ? 1ul << (tree_depth-1-level) : 0); + address_bits.push_back(computed_is_right); + bit_vector other(digest_len); + std::generate(other.begin(), other.end(), [&]() { return std::rand() % 2; }); + + bit_vector block = prev_hash; + block.insert(computed_is_right ? block.begin() : block.end(), other.begin(), other.end()); + bit_vector h = HashT::get_hash(block); + + path[level] = other; + + prev_hash = h; + } + bit_vector root = prev_hash; + + /* execute test */ + protoboard pb; + pb_variable_array address_bits_va; + address_bits_va.allocate(pb, tree_depth, "address_bits"); + digest_variable leaf_digest(pb, digest_len, "input_block"); + digest_variable root_digest(pb, digest_len, "output_digest"); + merkle_authentication_path_variable path_var(pb, tree_depth, "path_var"); + merkle_tree_check_read_gadget ml(pb, tree_depth, address_bits_va, leaf_digest, root_digest, path_var, ONE, "ml"); + + path_var.generate_r1cs_constraints(); + ml.generate_r1cs_constraints(); + + address_bits_va.fill_with_bits(pb, address_bits); + assert(address_bits_va.get_field_element_from_bits(pb).as_ulong() == address); + leaf_digest.generate_r1cs_witness(leaf); + path_var.generate_r1cs_witness(address, path); + ml.generate_r1cs_witness(); + + /* make sure that read checker didn't accidentally overwrite anything */ + address_bits_va.fill_with_bits(pb, address_bits); + leaf_digest.generate_r1cs_witness(leaf); + root_digest.generate_r1cs_witness(root); + assert(pb.is_satisfied()); + + const size_t num_constraints = pb.num_constraints(); + const size_t expected_constraints = merkle_tree_check_read_gadget::expected_constraints(tree_depth); + assert(num_constraints == expected_constraints); +} + +} // libsnark + +#endif // MERKLE_TREE_CHECK_READ_GADGET_TCC_ diff --git a/src/snark/libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_update_gadget.hpp b/src/snark/libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_update_gadget.hpp new file mode 100644 index 000000000..6ec0ca11f --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_update_gadget.hpp @@ -0,0 +1,90 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for the Merkle tree check read gadget. + + The gadget checks the following: given two roots R1 and R2, address A, two + values V1 and V2, and authentication path P, check that + - P is a valid authentication path for the value V1 as the A-th leaf in a Merkle tree with root R1, and + - P is a valid authentication path for the value V2 as the A-th leaf in a Merkle tree with root R2. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef MERKLE_TREE_CHECK_UPDATE_GADGET_HPP_ +#define MERKLE_TREE_CHECK_UPDATE_GADGET_HPP_ + +#include "common/data_structures/merkle_tree.hpp" +#include "gadgetlib1/gadget.hpp" +#include "gadgetlib1/gadgets/hashes/hash_io.hpp" +#include "gadgetlib1/gadgets/hashes/digest_selector_gadget.hpp" +#include "gadgetlib1/gadgets/merkle_tree/merkle_authentication_path_variable.hpp" + +namespace libsnark { + +template +class merkle_tree_check_update_gadget : public gadget { +private: + + std::vector prev_hashers; + std::vector > prev_hasher_inputs; + std::vector > prev_propagators; + std::vector > prev_internal_output; + + std::vector next_hashers; + std::vector > next_hasher_inputs; + std::vector > next_propagators; + std::vector > next_internal_output; + + std::shared_ptr > computed_next_root; + std::shared_ptr > check_next_root; + +public: + + const size_t digest_size; + const size_t tree_depth; + + pb_variable_array address_bits; + digest_variable prev_leaf_digest; + digest_variable prev_root_digest; + merkle_authentication_path_variable prev_path; + digest_variable next_leaf_digest; + digest_variable next_root_digest; + merkle_authentication_path_variable next_path; + pb_linear_combination update_successful; + + /* Note that while it is necessary to generate R1CS constraints + for prev_path, it is not necessary to do so for next_path. See + comment in the implementation of generate_r1cs_constraints() */ + + merkle_tree_check_update_gadget(protoboard &pb, + const size_t tree_depth, + const pb_variable_array &address_bits, + const digest_variable &prev_leaf_digest, + const digest_variable &prev_root_digest, + const merkle_authentication_path_variable &prev_path, + const digest_variable &next_leaf_digest, + const digest_variable &next_root_digest, + const merkle_authentication_path_variable &next_path, + const pb_linear_combination &update_successful, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); + + static size_t root_size_in_bits(); + /* for debugging purposes */ + static size_t expected_constraints(const size_t tree_depth); +}; + +template +void test_merkle_tree_check_update_gadget(); + +} // libsnark + +#include "gadgetlib1/gadgets/merkle_tree/merkle_tree_check_update_gadget.tcc" + +#endif // MERKLE_TREE_CHECK_UPDATE_GADGET_HPP_ diff --git a/src/snark/libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_update_gadget.tcc b/src/snark/libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_update_gadget.tcc new file mode 100644 index 000000000..1ac08edbb --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_update_gadget.tcc @@ -0,0 +1,265 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for the Merkle tree check update gadget. + + See merkle_tree_check_update_gadget.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef MERKLE_TREE_CHECK_UPDATE_GADGET_TCC_ +#define MERKLE_TREE_CHECK_UPDATE_GADGET_TCC_ + +namespace libsnark { + +template +merkle_tree_check_update_gadget::merkle_tree_check_update_gadget(protoboard &pb, + const size_t tree_depth, + const pb_variable_array &address_bits, + const digest_variable &prev_leaf_digest, + const digest_variable &prev_root_digest, + const merkle_authentication_path_variable &prev_path, + const digest_variable &next_leaf_digest, + const digest_variable &next_root_digest, + const merkle_authentication_path_variable &next_path, + const pb_linear_combination &update_successful, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), + digest_size(HashT::get_digest_len()), + tree_depth(tree_depth), + address_bits(address_bits), + prev_leaf_digest(prev_leaf_digest), + prev_root_digest(prev_root_digest), + prev_path(prev_path), + next_leaf_digest(next_leaf_digest), + next_root_digest(next_root_digest), + next_path(next_path), + update_successful(update_successful) +{ + assert(tree_depth > 0); + assert(tree_depth == address_bits.size()); + + for (size_t i = 0; i < tree_depth-1; ++i) + { + prev_internal_output.emplace_back(digest_variable(pb, digest_size, FMT(this->annotation_prefix, " prev_internal_output_%zu", i))); + next_internal_output.emplace_back(digest_variable(pb, digest_size, FMT(this->annotation_prefix, " next_internal_output_%zu", i))); + } + + computed_next_root.reset(new digest_variable(pb, digest_size, FMT(this->annotation_prefix, " computed_root"))); + + for (size_t i = 0; i < tree_depth; ++i) + { + block_variable prev_inp(pb, prev_path.left_digests[i], prev_path.right_digests[i], FMT(this->annotation_prefix, " prev_inp_%zu", i)); + prev_hasher_inputs.emplace_back(prev_inp); + prev_hashers.emplace_back(HashT(pb, 2*digest_size, prev_inp, (i == 0 ? prev_root_digest : prev_internal_output[i-1]), + FMT(this->annotation_prefix, " prev_hashers_%zu", i))); + + block_variable next_inp(pb, next_path.left_digests[i], next_path.right_digests[i], FMT(this->annotation_prefix, " next_inp_%zu", i)); + next_hasher_inputs.emplace_back(next_inp); + next_hashers.emplace_back(HashT(pb, 2*digest_size, next_inp, (i == 0 ? *computed_next_root : next_internal_output[i-1]), + FMT(this->annotation_prefix, " next_hashers_%zu", i))); + } + + for (size_t i = 0; i < tree_depth; ++i) + { + prev_propagators.emplace_back(digest_selector_gadget(pb, digest_size, i < tree_depth -1 ? prev_internal_output[i] : prev_leaf_digest, + address_bits[tree_depth-1-i], prev_path.left_digests[i], prev_path.right_digests[i], + FMT(this->annotation_prefix, " prev_propagators_%zu", i))); + next_propagators.emplace_back(digest_selector_gadget(pb, digest_size, i < tree_depth -1 ? next_internal_output[i] : next_leaf_digest, + address_bits[tree_depth-1-i], next_path.left_digests[i], next_path.right_digests[i], + FMT(this->annotation_prefix, " next_propagators_%zu", i))); + } + + check_next_root.reset(new bit_vector_copy_gadget(pb, computed_next_root->bits, next_root_digest.bits, update_successful, FieldT::capacity(), FMT(annotation_prefix, " check_next_root"))); +} + +template +void merkle_tree_check_update_gadget::generate_r1cs_constraints() +{ + /* ensure correct hash computations */ + for (size_t i = 0; i < tree_depth; ++i) + { + prev_hashers[i].generate_r1cs_constraints(false); // we check root outside and prev_left/prev_right above + next_hashers[i].generate_r1cs_constraints(true); // however we must check right side hashes + } + + /* ensure consistency of internal_left/internal_right with internal_output */ + for (size_t i = 0; i < tree_depth; ++i) + { + prev_propagators[i].generate_r1cs_constraints(); + next_propagators[i].generate_r1cs_constraints(); + } + + /* ensure that prev auxiliary input and next auxiliary input match */ + for (size_t i = 0; i < tree_depth; ++i) + { + for (size_t j = 0; j < digest_size; ++j) + { + /* + addr * (prev_left - next_left) + (1 - addr) * (prev_right - next_right) = 0 + addr * (prev_left - next_left - prev_right + next_right) = next_right - prev_right + */ + this->pb.add_r1cs_constraint(r1cs_constraint(address_bits[tree_depth-1-i], + prev_path.left_digests[i].bits[j] - next_path.left_digests[i].bits[j] - prev_path.right_digests[i].bits[j] + next_path.right_digests[i].bits[j], + next_path.right_digests[i].bits[j] - prev_path.right_digests[i].bits[j]), + FMT(this->annotation_prefix, " aux_check_%zu_%zu", i, j)); + } + } + + /* Note that while it is necessary to generate R1CS constraints + for prev_path, it is not necessary to do so for next_path. + + This holds, because { next_path.left_inputs[i], + next_path.right_inputs[i] } is a pair { hash_output, + auxiliary_input }. The bitness for hash_output is enforced + above by next_hashers[i].generate_r1cs_constraints. + + Because auxiliary input is the same for prev_path and next_path + (enforced above), we have that auxiliary_input part is also + constrained to be boolean, because prev_path is *all* + constrained to be all boolean. */ + + check_next_root->generate_r1cs_constraints(false, false); +} + +template +void merkle_tree_check_update_gadget::generate_r1cs_witness() +{ + /* do the hash computations bottom-up */ + for (int i = tree_depth-1; i >= 0; --i) + { + /* ensure consistency of prev_path and next_path */ + if (this->pb.val(address_bits[tree_depth-1-i]) == FieldT::one()) + { + next_path.left_digests[i].generate_r1cs_witness(prev_path.left_digests[i].get_digest()); + } + else + { + next_path.right_digests[i].generate_r1cs_witness(prev_path.right_digests[i].get_digest()); + } + + /* propagate previous input */ + prev_propagators[i].generate_r1cs_witness(); + next_propagators[i].generate_r1cs_witness(); + + /* compute hash */ + prev_hashers[i].generate_r1cs_witness(); + next_hashers[i].generate_r1cs_witness(); + } + + check_next_root->generate_r1cs_witness(); +} + +template +size_t merkle_tree_check_update_gadget::root_size_in_bits() +{ + return HashT::get_digest_len(); +} + +template +size_t merkle_tree_check_update_gadget::expected_constraints(const size_t tree_depth) +{ + /* NB: this includes path constraints */ + const size_t prev_hasher_constraints = tree_depth * HashT::expected_constraints(false); + const size_t next_hasher_constraints = tree_depth * HashT::expected_constraints(true); + const size_t prev_authentication_path_constraints = 2 * tree_depth * HashT::get_digest_len(); + const size_t prev_propagator_constraints = tree_depth * HashT::get_digest_len(); + const size_t next_propagator_constraints = tree_depth * HashT::get_digest_len(); + const size_t check_next_root_constraints = 3 * div_ceil(HashT::get_digest_len(), FieldT::capacity()); + const size_t aux_equality_constraints = tree_depth * HashT::get_digest_len(); + + return (prev_hasher_constraints + next_hasher_constraints + prev_authentication_path_constraints + + prev_propagator_constraints + next_propagator_constraints + check_next_root_constraints + + aux_equality_constraints); +} + +template +void test_merkle_tree_check_update_gadget() +{ + /* prepare test */ + const size_t digest_len = HashT::get_digest_len(); + + const size_t tree_depth = 16; + std::vector prev_path(tree_depth); + + bit_vector prev_load_hash(digest_len); + std::generate(prev_load_hash.begin(), prev_load_hash.end(), [&]() { return std::rand() % 2; }); + bit_vector prev_store_hash(digest_len); + std::generate(prev_store_hash.begin(), prev_store_hash.end(), [&]() { return std::rand() % 2; }); + + bit_vector loaded_leaf = prev_load_hash; + bit_vector stored_leaf = prev_store_hash; + + bit_vector address_bits; + + size_t address = 0; + for (long level = tree_depth-1; level >= 0; --level) + { + const bool computed_is_right = (std::rand() % 2); + address |= (computed_is_right ? 1ul << (tree_depth-1-level) : 0); + address_bits.push_back(computed_is_right); + bit_vector other(digest_len); + std::generate(other.begin(), other.end(), [&]() { return std::rand() % 2; }); + + bit_vector load_block = prev_load_hash; + load_block.insert(computed_is_right ? load_block.begin() : load_block.end(), other.begin(), other.end()); + bit_vector store_block = prev_store_hash; + store_block.insert(computed_is_right ? store_block.begin() : store_block.end(), other.begin(), other.end()); + + bit_vector load_h = HashT::get_hash(load_block); + bit_vector store_h = HashT::get_hash(store_block); + + prev_path[level] = other; + + prev_load_hash = load_h; + prev_store_hash = store_h; + } + + bit_vector load_root = prev_load_hash; + bit_vector store_root = prev_store_hash; + + /* execute the test */ + protoboard pb; + pb_variable_array address_bits_va; + address_bits_va.allocate(pb, tree_depth, "address_bits"); + digest_variable prev_leaf_digest(pb, digest_len, "prev_leaf_digest"); + digest_variable prev_root_digest(pb, digest_len, "prev_root_digest"); + merkle_authentication_path_variable prev_path_var(pb, tree_depth, "prev_path_var"); + digest_variable next_leaf_digest(pb, digest_len, "next_leaf_digest"); + digest_variable next_root_digest(pb, digest_len, "next_root_digest"); + merkle_authentication_path_variable next_path_var(pb, tree_depth, "next_path_var"); + merkle_tree_check_update_gadget mls(pb, tree_depth, address_bits_va, + prev_leaf_digest, prev_root_digest, prev_path_var, + next_leaf_digest, next_root_digest, next_path_var, ONE, "mls"); + + prev_path_var.generate_r1cs_constraints(); + mls.generate_r1cs_constraints(); + + address_bits_va.fill_with_bits(pb, address_bits); + assert(address_bits_va.get_field_element_from_bits(pb).as_ulong() == address); + prev_leaf_digest.generate_r1cs_witness(loaded_leaf); + prev_path_var.generate_r1cs_witness(address, prev_path); + next_leaf_digest.generate_r1cs_witness(stored_leaf); + address_bits_va.fill_with_bits(pb, address_bits); + mls.generate_r1cs_witness(); + + /* make sure that update check will check for the right things */ + prev_leaf_digest.generate_r1cs_witness(loaded_leaf); + next_leaf_digest.generate_r1cs_witness(stored_leaf); + prev_root_digest.generate_r1cs_witness(load_root); + next_root_digest.generate_r1cs_witness(store_root); + address_bits_va.fill_with_bits(pb, address_bits); + assert(pb.is_satisfied()); + + const size_t num_constraints = pb.num_constraints(); + const size_t expected_constraints = merkle_tree_check_update_gadget::expected_constraints(tree_depth); + assert(num_constraints == expected_constraints); +} + +} // libsnark + +#endif // MERKLE_TREE_CHECK_UPDATE_GADGET_TCC_ diff --git a/src/snark/libsnark/gadgetlib1/gadgets/merkle_tree/tests/test_merkle_tree_gadgets.cpp b/src/snark/libsnark/gadgetlib1/gadgets/merkle_tree/tests/test_merkle_tree_gadgets.cpp new file mode 100644 index 000000000..27b52f9ec --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/gadgets/merkle_tree/tests/test_merkle_tree_gadgets.cpp @@ -0,0 +1,40 @@ +/** + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include "algebra/curves/alt_bn128/alt_bn128_pp.hpp" +#ifdef CURVE_BN128 +#include "algebra/curves/bn128/bn128_pp.hpp" +#endif +#include "gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.hpp" +#include "gadgetlib1/gadgets/merkle_tree/merkle_tree_check_update_gadget.hpp" +#include "gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp" + +#include + +using namespace libsnark; + +template +void test_all_merkle_tree_gadgets() +{ + typedef Fr FieldT; + test_merkle_tree_check_read_gadget >(); + + test_merkle_tree_check_update_gadget >(); +} + +TEST(gadgetlib1, merkle_tree) +{ + start_profiling(); + + alt_bn128_pp::init_public_params(); + test_all_merkle_tree_gadgets(); + +#ifdef CURVE_BN128 // BN128 has fancy dependencies so it may be disabled + bn128_pp::init_public_params(); + test_all_merkle_tree_gadgets(); +#endif +} diff --git a/src/snark/libsnark/gadgetlib1/pb_variable.hpp b/src/snark/libsnark/gadgetlib1/pb_variable.hpp new file mode 100644 index 000000000..fdf64d014 --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/pb_variable.hpp @@ -0,0 +1,144 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef PB_VARIABLE_HPP_ +#define PB_VARIABLE_HPP_ + +#include +#include +#include +#include "common/utils.hpp" +#include "relations/variable.hpp" + +namespace libsnark { + +typedef size_t lc_index_t; + +template +class protoboard; + +template +class pb_variable : public variable { +public: + pb_variable(const var_index_t index = 0) : variable(index) {}; + + void allocate(protoboard &pb, const std::string &annotation=""); +}; + +template +class pb_variable_array : private std::vector > +{ + typedef std::vector > contents; +public: + using typename contents::iterator; + using typename contents::const_iterator; + using typename contents::reverse_iterator; + using typename contents::const_reverse_iterator; + + using contents::begin; + using contents::end; + using contents::rbegin; + using contents::rend; + using contents::emplace_back; + using contents::insert; + using contents::reserve; + using contents::size; + using contents::empty; + using contents::operator[]; + using contents::resize; + + pb_variable_array() : contents() {}; + pb_variable_array(size_t count, const pb_variable &value) : contents(count, value) {}; + pb_variable_array(typename contents::const_iterator first, typename contents::const_iterator last) : contents(first, last) {}; + pb_variable_array(typename contents::const_reverse_iterator first, typename contents::const_reverse_iterator last) : contents(first, last) {}; + void allocate(protoboard &pb, const size_t n, const std::string &annotation_prefix=""); + + void fill_with_field_elements(protoboard &pb, const std::vector& vals) const; + void fill_with_bits(protoboard &pb, const bit_vector& bits) const; + void fill_with_bits_of_ulong(protoboard &pb, const unsigned long i) const; + void fill_with_bits_of_field_element(protoboard &pb, const FieldT &r) const; + + std::vector get_vals(const protoboard &pb) const; + bit_vector get_bits(const protoboard &pb) const; + + FieldT get_field_element_from_bits(const protoboard &pb) const; +}; + +/* index 0 corresponds to the constant term (used in legacy code) */ +#define ONE pb_variable(0) + +template +class pb_linear_combination : public linear_combination { +public: + bool is_variable; + lc_index_t index; + + pb_linear_combination(); + pb_linear_combination(const pb_variable &var); + + void assign(protoboard &pb, const linear_combination &lc); + void evaluate(protoboard &pb) const; + + bool is_constant() const; + FieldT constant_term() const; +}; + +template +class pb_linear_combination_array : private std::vector > +{ + typedef std::vector > contents; +public: + using typename contents::iterator; + using typename contents::const_iterator; + using typename contents::reverse_iterator; + using typename contents::const_reverse_iterator; + + using contents::begin; + using contents::end; + using contents::rbegin; + using contents::rend; + using contents::emplace_back; + using contents::insert; + using contents::reserve; + using contents::size; + using contents::empty; + using contents::operator[]; + using contents::resize; + + pb_linear_combination_array() : contents() {}; + pb_linear_combination_array(const pb_variable_array &arr) { for (auto &v : arr) this->emplace_back(pb_linear_combination(v)); }; + pb_linear_combination_array(size_t count) : contents(count) {}; + pb_linear_combination_array(size_t count, const pb_linear_combination &value) : contents(count, value) {}; + pb_linear_combination_array(typename contents::const_iterator first, typename contents::const_iterator last) : contents(first, last) {}; + pb_linear_combination_array(typename contents::const_reverse_iterator first, typename contents::const_reverse_iterator last) : contents(first, last) {}; + + void evaluate(protoboard &pb) const; + + void fill_with_field_elements(protoboard &pb, const std::vector& vals) const; + void fill_with_bits(protoboard &pb, const bit_vector& bits) const; + void fill_with_bits_of_ulong(protoboard &pb, const unsigned long i) const; + void fill_with_bits_of_field_element(protoboard &pb, const FieldT &r) const; + + std::vector get_vals(const protoboard &pb) const; + bit_vector get_bits(const protoboard &pb) const; + + FieldT get_field_element_from_bits(const protoboard &pb) const; +}; + +template +linear_combination pb_sum(const pb_linear_combination_array &v); + +template +linear_combination pb_packing_sum(const pb_linear_combination_array &v); + +template +linear_combination pb_coeff_sum(const pb_linear_combination_array &v, const std::vector &coeffs); + +} // libsnark +#include "gadgetlib1/pb_variable.tcc" + +#endif // PB_VARIABLE_HPP_ diff --git a/src/snark/libsnark/gadgetlib1/pb_variable.tcc b/src/snark/libsnark/gadgetlib1/pb_variable.tcc new file mode 100644 index 000000000..b36b3f8d7 --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/pb_variable.tcc @@ -0,0 +1,330 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef PB_VARIABLE_TCC_ +#define PB_VARIABLE_TCC_ +#include +#include "gadgetlib1/protoboard.hpp" +#include "common/utils.hpp" + +namespace libsnark { + +template +void pb_variable::allocate(protoboard &pb, const std::string &annotation) +{ + this->index = pb.allocate_var_index(annotation); +} + +/* allocates pb_variable array in MSB->LSB order */ +template +void pb_variable_array::allocate(protoboard &pb, const size_t n, const std::string &annotation_prefix) +{ +#ifdef DEBUG + assert(annotation_prefix != ""); +#endif + (*this).resize(n); + + for (size_t i = 0; i < n; ++i) + { + (*this)[i].allocate(pb, FMT(annotation_prefix, "_%zu", i)); + } +} + +template +void pb_variable_array::fill_with_field_elements(protoboard &pb, const std::vector& vals) const +{ + assert(this->size() == vals.size()); + for (size_t i = 0; i < vals.size(); ++i) + { + pb.val((*this)[i]) = vals[i]; + } +} + +template +void pb_variable_array::fill_with_bits(protoboard &pb, const bit_vector& bits) const +{ + assert(this->size() == bits.size()); + for (size_t i = 0; i < bits.size(); ++i) + { + pb.val((*this)[i]) = (bits[i] ? FieldT::one() : FieldT::zero()); + } +} + +template +void pb_variable_array::fill_with_bits_of_field_element(protoboard &pb, const FieldT &r) const +{ + const bigint rint = r.as_bigint(); + for (size_t i = 0; i < this->size(); ++i) + { + pb.val((*this)[i]) = rint.test_bit(i) ? FieldT::one() : FieldT::zero(); + } +} + +template +void pb_variable_array::fill_with_bits_of_ulong(protoboard &pb, const unsigned long i) const +{ + this->fill_with_bits_of_field_element(pb, FieldT(i, true)); +} + +template +std::vector pb_variable_array::get_vals(const protoboard &pb) const +{ + std::vector result(this->size()); + for (size_t i = 0; i < this->size(); ++i) + { + result[i] = pb.val((*this)[i]); + } + return result; +} + +template +bit_vector pb_variable_array::get_bits(const protoboard &pb) const +{ + bit_vector result; + for (size_t i = 0; i < this->size(); ++i) + { + const FieldT v = pb.val((*this)[i]); + assert(v == FieldT::zero() || v == FieldT::one()); + result.push_back(v == FieldT::one()); + } + return result; +} + +template +FieldT pb_variable_array::get_field_element_from_bits(const protoboard &pb) const +{ + FieldT result = FieldT::zero(); + + for (size_t i = 0; i < this->size(); ++i) + { + /* push in the new bit */ + const FieldT v = pb.val((*this)[this->size()-1-i]); + assert(v == FieldT::zero() || v == FieldT::one()); + result += result + v; + } + + return result; +} + +template +pb_linear_combination::pb_linear_combination() +{ + this->is_variable = false; + this->index = 0; +} + +template +pb_linear_combination::pb_linear_combination(const pb_variable &var) +{ + this->is_variable = true; + this->index = var.index; + this->terms.emplace_back(linear_term(var)); +} + +template +void pb_linear_combination::assign(protoboard &pb, const linear_combination &lc) +{ + assert(this->is_variable == false); + this->index = pb.allocate_lc_index(); + this->terms = lc.terms; +} + +template +void pb_linear_combination::evaluate(protoboard &pb) const +{ + if (this->is_variable) + { + return; // do nothing + } + + FieldT sum = 0; + for (auto term : this->terms) + { + sum += term.coeff * pb.val(pb_variable(term.index)); + } + + pb.lc_val(*this) = sum; +} + +template +bool pb_linear_combination::is_constant() const +{ + if (is_variable) + { + return (index == 0); + } + else + { + for (auto term : this->terms) + { + if (term.index != 0) + { + return false; + } + } + + return true; + } +} + +template +FieldT pb_linear_combination::constant_term() const +{ + if (is_variable) + { + return (index == 0 ? FieldT::one() : FieldT::zero()); + } + else + { + FieldT result = FieldT::zero(); + for (auto term : this->terms) + { + if (term.index == 0) + { + result += term.coeff; + } + } + return result; + } +} + +template +void pb_linear_combination_array::evaluate(protoboard &pb) const +{ + for (size_t i = 0; i < this->size(); ++i) + { + (*this)[i].evaluate(pb); + } +} + +template +void pb_linear_combination_array::fill_with_field_elements(protoboard &pb, const std::vector& vals) const +{ + assert(this->size() == vals.size()); + for (size_t i = 0; i < vals.size(); ++i) + { + pb.lc_val((*this)[i]) = vals[i]; + } +} + +template +void pb_linear_combination_array::fill_with_bits(protoboard &pb, const bit_vector& bits) const +{ + assert(this->size() == bits.size()); + for (size_t i = 0; i < bits.size(); ++i) + { + pb.lc_val((*this)[i]) = (bits[i] ? FieldT::one() : FieldT::zero()); + } +} + +template +void pb_linear_combination_array::fill_with_bits_of_field_element(protoboard &pb, const FieldT &r) const +{ + const bigint rint = r.as_bigint(); + for (size_t i = 0; i < this->size(); ++i) + { + pb.lc_val((*this)[i]) = rint.test_bit(i) ? FieldT::one() : FieldT::zero(); + } +} + +template +void pb_linear_combination_array::fill_with_bits_of_ulong(protoboard &pb, const unsigned long i) const +{ + this->fill_with_bits_of_field_element(pb, FieldT(i)); +} + +template +std::vector pb_linear_combination_array::get_vals(const protoboard &pb) const +{ + std::vector result(this->size()); + for (size_t i = 0; i < this->size(); ++i) + { + result[i] = pb.lc_val((*this)[i]); + } + return result; +} + +template +bit_vector pb_linear_combination_array::get_bits(const protoboard &pb) const +{ + bit_vector result; + for (size_t i = 0; i < this->size(); ++i) + { + const FieldT v = pb.lc_val((*this)[i]); + assert(v == FieldT::zero() || v == FieldT::one()); + result.push_back(v == FieldT::one()); + } + return result; +} + +template +FieldT pb_linear_combination_array::get_field_element_from_bits(const protoboard &pb) const +{ + FieldT result = FieldT::zero(); + + for (size_t i = 0; i < this->size(); ++i) + { + /* push in the new bit */ + const FieldT v = pb.lc_val((*this)[this->size()-1-i]); + assert(v == FieldT::zero() || v == FieldT::one()); + result += result + v; + } + + return result; +} + +template +linear_combination pb_sum(const pb_linear_combination_array &v) +{ + linear_combination result; + for (auto &term : v) + { + result = result + term; + } + + return result; +} + +template +linear_combination pb_packing_sum(const pb_linear_combination_array &v) +{ + FieldT twoi = FieldT::one(); // will hold 2^i entering each iteration + std::vector > all_terms; + for (auto &lc : v) + { + for (auto &term : lc.terms) + { + all_terms.emplace_back(twoi * term); + } + twoi += twoi; + } + + return linear_combination(all_terms); +} + +template +linear_combination pb_coeff_sum(const pb_linear_combination_array &v, const std::vector &coeffs) +{ + assert(v.size() == coeffs.size()); + std::vector > all_terms; + + auto coeff_it = coeffs.begin(); + for (auto &lc : v) + { + for (auto &term : lc.terms) + { + all_terms.emplace_back((*coeff_it) * term); + } + ++coeff_it; + } + + return linear_combination(all_terms); +} + + +} // libsnark +#endif // PB_VARIABLE_TCC diff --git a/src/snark/libsnark/gadgetlib1/protoboard.hpp b/src/snark/libsnark/gadgetlib1/protoboard.hpp new file mode 100644 index 000000000..a910a6df9 --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/protoboard.hpp @@ -0,0 +1,75 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef PROTOBOARD_HPP_ +#define PROTOBOARD_HPP_ + +#include +#include +#include +#include +#include +#include "gadgetlib1/pb_variable.hpp" +#include "relations/constraint_satisfaction_problems/r1cs/r1cs.hpp" +#include "common/utils.hpp" + +namespace libsnark { + +template +class r1cs_constraint; + +template +class r1cs_constraint_system; + +template +class protoboard { +private: + FieldT constant_term; /* only here, because pb.val() needs to be able to return reference to the constant 1 term */ + r1cs_variable_assignment values; /* values[0] will hold the value of the first allocated variable of the protoboard, *NOT* constant 1 */ + var_index_t next_free_var; + lc_index_t next_free_lc; + std::vector lc_values; +public: + r1cs_constraint_system constraint_system; + + protoboard(); + + void clear_values(); + + FieldT& val(const pb_variable &var); + FieldT val(const pb_variable &var) const; + + FieldT& lc_val(const pb_linear_combination &lc); + FieldT lc_val(const pb_linear_combination &lc) const; + + void add_r1cs_constraint(const r1cs_constraint &constr, const std::string &annotation=""); + void augment_variable_annotation(const pb_variable &v, const std::string &postfix); + bool is_satisfied() const; + void dump_variables() const; + + size_t num_constraints() const; + size_t num_inputs() const; + size_t num_variables() const; + + void set_input_sizes(const size_t primary_input_size); + + r1cs_variable_assignment full_variable_assignment() const; + r1cs_primary_input primary_input() const; + r1cs_auxiliary_input auxiliary_input() const; + r1cs_constraint_system get_constraint_system() const; + + friend class pb_variable; + friend class pb_linear_combination; + +private: + var_index_t allocate_var_index(const std::string &annotation=""); + lc_index_t allocate_lc_index(); +}; + +} // libsnark +#include "gadgetlib1/protoboard.tcc" +#endif // PROTOBOARD_HPP_ diff --git a/src/snark/libsnark/gadgetlib1/protoboard.tcc b/src/snark/libsnark/gadgetlib1/protoboard.tcc new file mode 100644 index 000000000..882af28e6 --- /dev/null +++ b/src/snark/libsnark/gadgetlib1/protoboard.tcc @@ -0,0 +1,189 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef PROTOBOARD_TCC_ +#define PROTOBOARD_TCC_ + +#include +#include +#include "common/profiling.hpp" + +namespace libsnark { + +template +protoboard::protoboard() +{ + constant_term = FieldT::one(); + +#ifdef DEBUG + constraint_system.variable_annotations[0] = "ONE"; +#endif + + next_free_var = 1; /* to account for constant 1 term */ + next_free_lc = 0; +} + +template +void protoboard::clear_values() +{ + std::fill(values.begin(), values.end(), FieldT::zero()); +} + +template +var_index_t protoboard::allocate_var_index(const std::string &annotation) +{ +#ifdef DEBUG + assert(annotation != ""); + constraint_system.variable_annotations[next_free_var] = annotation; +#else + UNUSED(annotation); +#endif + ++constraint_system.auxiliary_input_size; + values.emplace_back(FieldT::zero()); + return next_free_var++; +} + +template +lc_index_t protoboard::allocate_lc_index() +{ + lc_values.emplace_back(FieldT::zero()); + return next_free_lc++; +} + +template +FieldT& protoboard::val(const pb_variable &var) +{ + assert(var.index <= values.size()); + return (var.index == 0 ? constant_term : values[var.index-1]); +} + +template +FieldT protoboard::val(const pb_variable &var) const +{ + assert(var.index <= values.size()); + return (var.index == 0 ? constant_term : values[var.index-1]); +} + +template +FieldT& protoboard::lc_val(const pb_linear_combination &lc) +{ + if (lc.is_variable) + { + return this->val(pb_variable(lc.index)); + } + else + { + assert(lc.index < lc_values.size()); + return lc_values[lc.index]; + } +} + +template +FieldT protoboard::lc_val(const pb_linear_combination &lc) const +{ + if (lc.is_variable) + { + return this->val(pb_variable(lc.index)); + } + else + { + assert(lc.index < lc_values.size()); + return lc_values[lc.index]; + } +} + +template +void protoboard::add_r1cs_constraint(const r1cs_constraint &constr, const std::string &annotation) +{ +#ifdef DEBUG + assert(annotation != ""); + constraint_system.constraint_annotations[constraint_system.constraints.size()] = annotation; +#else + UNUSED(annotation); +#endif + constraint_system.constraints.emplace_back(constr); +} + +template +void protoboard::augment_variable_annotation(const pb_variable &v, const std::string &postfix) +{ +#ifdef DEBUG + auto it = constraint_system.variable_annotations.find(v.index); + constraint_system.variable_annotations[v.index] = (it == constraint_system.variable_annotations.end() ? "" : it->second + " ") + postfix; +#endif +} + +template +bool protoboard::is_satisfied() const +{ + return constraint_system.is_satisfied(primary_input(), auxiliary_input()); +} + +template +void protoboard::dump_variables() const +{ +#ifdef DEBUG + for (size_t i = 0; i < constraint_system.num_variables; ++i) + { + printf("%-40s --> ", constraint_system.variable_annotations[i].c_str()); + values[i].as_bigint().print_hex(); + } +#endif +} + +template +size_t protoboard::num_constraints() const +{ + return constraint_system.num_constraints(); +} + +template +size_t protoboard::num_inputs() const +{ + return constraint_system.num_inputs(); +} + +template +size_t protoboard::num_variables() const +{ + return next_free_var - 1; +} + +template +void protoboard::set_input_sizes(const size_t primary_input_size) +{ + assert(primary_input_size <= num_variables()); + constraint_system.primary_input_size = primary_input_size; + constraint_system.auxiliary_input_size = num_variables() - primary_input_size; +} + +template +r1cs_variable_assignment protoboard::full_variable_assignment() const +{ + return values; +} + +template +r1cs_primary_input protoboard::primary_input() const +{ + return r1cs_primary_input(values.begin(), values.begin() + num_inputs()); +} + +template +r1cs_auxiliary_input protoboard::auxiliary_input() const +{ + return r1cs_primary_input(values.begin() + num_inputs(), values.end()); +} + +template +r1cs_constraint_system protoboard::get_constraint_system() const +{ + return constraint_system; +} + +} // libsnark +#endif // PROTOBOARD_TCC_ diff --git a/src/snark/libsnark/gtests.cpp b/src/snark/libsnark/gtests.cpp new file mode 100644 index 000000000..74c66bdad --- /dev/null +++ b/src/snark/libsnark/gtests.cpp @@ -0,0 +1,12 @@ +#include + +#include "common/profiling.hpp" + +int main(int argc, char **argv) { + libsnark::inhibit_profiling_info = true; + libsnark::inhibit_profiling_counters = true; + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + diff --git a/src/snark/libsnark/reductions/r1cs_to_qap/r1cs_to_qap.hpp b/src/snark/libsnark/reductions/r1cs_to_qap/r1cs_to_qap.hpp new file mode 100644 index 000000000..86831cb1d --- /dev/null +++ b/src/snark/libsnark/reductions/r1cs_to_qap/r1cs_to_qap.hpp @@ -0,0 +1,70 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for a R1CS-to-QAP reduction, that is, constructing + a QAP ("Quadratic Arithmetic Program") from a R1CS ("Rank-1 Constraint System"). + + QAPs are defined in \[GGPR13], and constructed for R1CS also in \[GGPR13]. + + The implementation of the reduction follows, extends, and optimizes + the efficient approach described in Appendix E of \[BCGTV13]. + + References: + + \[BCGTV13] + "SNARKs for C: Verifying Program Executions Succinctly and in Zero Knowledge", + Eli Ben-Sasson, Alessandro Chiesa, Daniel Genkin, Eran Tromer, Madars Virza, + CRYPTO 2013, + + + \[GGPR13]: + "Quadratic span programs and succinct NIZKs without PCPs", + Rosario Gennaro, Craig Gentry, Bryan Parno, Mariana Raykova, + EUROCRYPT 2013, + + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef R1CS_TO_QAP_HPP_ +#define R1CS_TO_QAP_HPP_ + +#include "relations/arithmetic_programs/qap/qap.hpp" +#include "relations/constraint_satisfaction_problems/r1cs/r1cs.hpp" + +namespace libsnark { + +/** + * Instance map for the R1CS-to-QAP reduction. + */ +template +qap_instance r1cs_to_qap_instance_map(const r1cs_constraint_system &cs); + +/** + * Instance map for the R1CS-to-QAP reduction followed by evaluation of the resulting QAP instance. + */ +template +qap_instance_evaluation r1cs_to_qap_instance_map_with_evaluation(const r1cs_constraint_system &cs, + const FieldT &t); + +/** + * Witness map for the R1CS-to-QAP reduction. + * + * The witness map takes zero knowledge into account when d1,d2,d3 are random. + */ +template +qap_witness r1cs_to_qap_witness_map(const r1cs_constraint_system &cs, + const r1cs_primary_input &primary_input, + const r1cs_auxiliary_input &auxiliary_input, + const FieldT &d1, + const FieldT &d2, + const FieldT &d3); + +} // libsnark + +#include "reductions/r1cs_to_qap/r1cs_to_qap.tcc" + +#endif // R1CS_TO_QAP_HPP_ diff --git a/src/snark/libsnark/reductions/r1cs_to_qap/r1cs_to_qap.tcc b/src/snark/libsnark/reductions/r1cs_to_qap/r1cs_to_qap.tcc new file mode 100644 index 000000000..3d0bee273 --- /dev/null +++ b/src/snark/libsnark/reductions/r1cs_to_qap/r1cs_to_qap.tcc @@ -0,0 +1,338 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for a R1CS-to-QAP reduction. + + See r1cs_to_qap.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef R1CS_TO_QAP_TCC_ +#define R1CS_TO_QAP_TCC_ + +#include "common/profiling.hpp" +#include "common/utils.hpp" +#include "algebra/evaluation_domain/evaluation_domain.hpp" + +namespace libsnark { + +/** + * Instance map for the R1CS-to-QAP reduction. + * + * Namely, given a R1CS constraint system cs, construct a QAP instance for which: + * A := (A_0(z),A_1(z),...,A_m(z)) + * B := (B_0(z),B_1(z),...,B_m(z)) + * C := (C_0(z),C_1(z),...,C_m(z)) + * where + * m = number of variables of the QAP + * and + * each A_i,B_i,C_i is expressed in the Lagrange basis. + */ +template +qap_instance r1cs_to_qap_instance_map(const r1cs_constraint_system &cs) +{ + enter_block("Call to r1cs_to_qap_instance_map"); + + const std::shared_ptr > domain = get_evaluation_domain(cs.num_constraints() + cs.num_inputs() + 1); + + std::vector > A_in_Lagrange_basis(cs.num_variables()+1); + std::vector > B_in_Lagrange_basis(cs.num_variables()+1); + std::vector > C_in_Lagrange_basis(cs.num_variables()+1); + + enter_block("Compute polynomials A, B, C in Lagrange basis"); + /** + * add and process the constraints + * input_i * 0 = 0 + * to ensure soundness of input consistency + */ + for (size_t i = 0; i <= cs.num_inputs(); ++i) + { + A_in_Lagrange_basis[i][cs.num_constraints() + i] = FieldT::one(); + } + /* process all other constraints */ + for (size_t i = 0; i < cs.num_constraints(); ++i) + { + for (size_t j = 0; j < cs.constraints[i].a.terms.size(); ++j) + { + A_in_Lagrange_basis[cs.constraints[i].a.terms[j].index][i] += + cs.constraints[i].a.terms[j].coeff; + } + + for (size_t j = 0; j < cs.constraints[i].b.terms.size(); ++j) + { + B_in_Lagrange_basis[cs.constraints[i].b.terms[j].index][i] += + cs.constraints[i].b.terms[j].coeff; + } + + for (size_t j = 0; j < cs.constraints[i].c.terms.size(); ++j) + { + C_in_Lagrange_basis[cs.constraints[i].c.terms[j].index][i] += + cs.constraints[i].c.terms[j].coeff; + } + } + leave_block("Compute polynomials A, B, C in Lagrange basis"); + + leave_block("Call to r1cs_to_qap_instance_map"); + + return qap_instance(domain, + cs.num_variables(), + domain->m, + cs.num_inputs(), + std::move(A_in_Lagrange_basis), + std::move(B_in_Lagrange_basis), + std::move(C_in_Lagrange_basis)); +} + +/** + * Instance map for the R1CS-to-QAP reduction followed by evaluation of the resulting QAP instance. + * + * Namely, given a R1CS constraint system cs and a field element t, construct + * a QAP instance (evaluated at t) for which: + * At := (A_0(t),A_1(t),...,A_m(t)) + * Bt := (B_0(t),B_1(t),...,B_m(t)) + * Ct := (C_0(t),C_1(t),...,C_m(t)) + * Ht := (1,t,t^2,...,t^n) + * Zt := Z(t) = "vanishing polynomial of a certain set S, evaluated at t" + * where + * m = number of variables of the QAP + * n = degree of the QAP + */ +template +qap_instance_evaluation r1cs_to_qap_instance_map_with_evaluation(const r1cs_constraint_system &cs, + const FieldT &t) +{ + enter_block("Call to r1cs_to_qap_instance_map_with_evaluation"); + + const std::shared_ptr > domain = get_evaluation_domain(cs.num_constraints() + cs.num_inputs() + 1); + + std::vector At, Bt, Ct, Ht; + + At.resize(cs.num_variables()+1, FieldT::zero()); + Bt.resize(cs.num_variables()+1, FieldT::zero()); + Ct.resize(cs.num_variables()+1, FieldT::zero()); + Ht.reserve(domain->m+1); + + const FieldT Zt = domain->compute_Z(t); + + enter_block("Compute evaluations of A, B, C, H at t"); + const std::vector u = domain->lagrange_coeffs(t); + /** + * add and process the constraints + * input_i * 0 = 0 + * to ensure soundness of input consistency + */ + for (size_t i = 0; i <= cs.num_inputs(); ++i) + { + At[i] = u[cs.num_constraints() + i]; + } + /* process all other constraints */ + for (size_t i = 0; i < cs.num_constraints(); ++i) + { + for (size_t j = 0; j < cs.constraints[i].a.terms.size(); ++j) + { + At[cs.constraints[i].a.terms[j].index] += + u[i]*cs.constraints[i].a.terms[j].coeff; + } + + for (size_t j = 0; j < cs.constraints[i].b.terms.size(); ++j) + { + Bt[cs.constraints[i].b.terms[j].index] += + u[i]*cs.constraints[i].b.terms[j].coeff; + } + + for (size_t j = 0; j < cs.constraints[i].c.terms.size(); ++j) + { + Ct[cs.constraints[i].c.terms[j].index] += + u[i]*cs.constraints[i].c.terms[j].coeff; + } + } + + FieldT ti = FieldT::one(); + for (size_t i = 0; i < domain->m+1; ++i) + { + Ht.emplace_back(ti); + ti *= t; + } + leave_block("Compute evaluations of A, B, C, H at t"); + + leave_block("Call to r1cs_to_qap_instance_map_with_evaluation"); + + return qap_instance_evaluation(domain, + cs.num_variables(), + domain->m, + cs.num_inputs(), + t, + std::move(At), + std::move(Bt), + std::move(Ct), + std::move(Ht), + Zt); +} + +/** + * Witness map for the R1CS-to-QAP reduction. + * + * The witness map takes zero knowledge into account when d1,d2,d3 are random. + * + * More precisely, compute the coefficients + * h_0,h_1,...,h_n + * of the polynomial + * H(z) := (A(z)*B(z)-C(z))/Z(z) + * where + * A(z) := A_0(z) + \sum_{k=1}^{m} w_k A_k(z) + d1 * Z(z) + * B(z) := B_0(z) + \sum_{k=1}^{m} w_k B_k(z) + d2 * Z(z) + * C(z) := C_0(z) + \sum_{k=1}^{m} w_k C_k(z) + d3 * Z(z) + * Z(z) := "vanishing polynomial of set S" + * and + * m = number of variables of the QAP + * n = degree of the QAP + * + * This is done as follows: + * (1) compute evaluations of A,B,C on S = {sigma_1,...,sigma_n} + * (2) compute coefficients of A,B,C + * (3) compute evaluations of A,B,C on T = "coset of S" + * (4) compute evaluation of H on T + * (5) compute coefficients of H + * (6) patch H to account for d1,d2,d3 (i.e., add coefficients of the polynomial (A d2 + B d1 - d3) + d1*d2*Z ) + * + * The code below is not as simple as the above high-level description due to + * some reshuffling to save space. + */ +template +qap_witness r1cs_to_qap_witness_map(const r1cs_constraint_system &cs, + const r1cs_primary_input &primary_input, + const r1cs_auxiliary_input &auxiliary_input, + const FieldT &d1, + const FieldT &d2, + const FieldT &d3) +{ + enter_block("Call to r1cs_to_qap_witness_map"); + + /* sanity check */ + assert(cs.is_satisfied(primary_input, auxiliary_input)); + + const std::shared_ptr > domain = get_evaluation_domain(cs.num_constraints() + cs.num_inputs() + 1); + + r1cs_variable_assignment full_variable_assignment = primary_input; + full_variable_assignment.insert(full_variable_assignment.end(), auxiliary_input.begin(), auxiliary_input.end()); + + enter_block("Compute evaluation of polynomials A, B on set S"); + std::vector aA(domain->m, FieldT::zero()), aB(domain->m, FieldT::zero()); + + /* account for the additional constraints input_i * 0 = 0 */ + for (size_t i = 0; i <= cs.num_inputs(); ++i) + { + aA[i+cs.num_constraints()] = (i > 0 ? full_variable_assignment[i-1] : FieldT::one()); + } + /* account for all other constraints */ + for (size_t i = 0; i < cs.num_constraints(); ++i) + { + aA[i] += cs.constraints[i].a.evaluate(full_variable_assignment); + aB[i] += cs.constraints[i].b.evaluate(full_variable_assignment); + } + leave_block("Compute evaluation of polynomials A, B on set S"); + + enter_block("Compute coefficients of polynomial A"); + domain->iFFT(aA); + leave_block("Compute coefficients of polynomial A"); + + enter_block("Compute coefficients of polynomial B"); + domain->iFFT(aB); + leave_block("Compute coefficients of polynomial B"); + + enter_block("Compute ZK-patch"); + std::vector coefficients_for_H(domain->m+1, FieldT::zero()); +#ifdef MULTICORE +#pragma omp parallel for +#endif + /* add coefficients of the polynomial (d2*A + d1*B - d3) + d1*d2*Z */ + for (size_t i = 0; i < domain->m; ++i) + { + coefficients_for_H[i] = d2*aA[i] + d1*aB[i]; + } + coefficients_for_H[0] -= d3; + domain->add_poly_Z(d1*d2, coefficients_for_H); + leave_block("Compute ZK-patch"); + + enter_block("Compute evaluation of polynomial A on set T"); + domain->cosetFFT(aA, FieldT::multiplicative_generator); + leave_block("Compute evaluation of polynomial A on set T"); + + enter_block("Compute evaluation of polynomial B on set T"); + domain->cosetFFT(aB, FieldT::multiplicative_generator); + leave_block("Compute evaluation of polynomial B on set T"); + + enter_block("Compute evaluation of polynomial H on set T"); + std::vector &H_tmp = aA; // can overwrite aA because it is not used later +#ifdef MULTICORE +#pragma omp parallel for +#endif + for (size_t i = 0; i < domain->m; ++i) + { + H_tmp[i] = aA[i]*aB[i]; + } + std::vector().swap(aB); // destroy aB + + enter_block("Compute evaluation of polynomial C on set S"); + std::vector aC(domain->m, FieldT::zero()); + for (size_t i = 0; i < cs.num_constraints(); ++i) + { + aC[i] += cs.constraints[i].c.evaluate(full_variable_assignment); + } + leave_block("Compute evaluation of polynomial C on set S"); + + enter_block("Compute coefficients of polynomial C"); + domain->iFFT(aC); + leave_block("Compute coefficients of polynomial C"); + + enter_block("Compute evaluation of polynomial C on set T"); + domain->cosetFFT(aC, FieldT::multiplicative_generator); + leave_block("Compute evaluation of polynomial C on set T"); + +#ifdef MULTICORE +#pragma omp parallel for +#endif + for (size_t i = 0; i < domain->m; ++i) + { + H_tmp[i] = (H_tmp[i]-aC[i]); + } + + enter_block("Divide by Z on set T"); + domain->divide_by_Z_on_coset(H_tmp); + leave_block("Divide by Z on set T"); + + leave_block("Compute evaluation of polynomial H on set T"); + + enter_block("Compute coefficients of polynomial H"); + domain->icosetFFT(H_tmp, FieldT::multiplicative_generator); + leave_block("Compute coefficients of polynomial H"); + + enter_block("Compute sum of H and ZK-patch"); +#ifdef MULTICORE +#pragma omp parallel for +#endif + for (size_t i = 0; i < domain->m; ++i) + { + coefficients_for_H[i] += H_tmp[i]; + } + leave_block("Compute sum of H and ZK-patch"); + + leave_block("Call to r1cs_to_qap_witness_map"); + + return qap_witness(cs.num_variables(), + domain->m, + cs.num_inputs(), + d1, + d2, + d3, + full_variable_assignment, + std::move(coefficients_for_H)); +} + +} // libsnark + +#endif // R1CS_TO_QAP_TCC_ diff --git a/src/snark/libsnark/relations/arithmetic_programs/qap/qap.hpp b/src/snark/libsnark/relations/arithmetic_programs/qap/qap.hpp new file mode 100644 index 000000000..4991d203b --- /dev/null +++ b/src/snark/libsnark/relations/arithmetic_programs/qap/qap.hpp @@ -0,0 +1,193 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for a QAP ("Quadratic Arithmetic Program"). + + QAPs are defined in \[GGPR13]. + + References: + + \[GGPR13]: + "Quadratic span programs and succinct NIZKs without PCPs", + Rosario Gennaro, Craig Gentry, Bryan Parno, Mariana Raykova, + EUROCRYPT 2013, + + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef QAP_HPP_ +#define QAP_HPP_ + +#include "algebra/evaluation_domain/evaluation_domain.hpp" + +namespace libsnark { + +/* forward declaration */ +template +class qap_witness; + +/** + * A QAP instance. + * + * Specifically, the datastructure stores: + * - a choice of domain (corresponding to a certain subset of the field); + * - the number of variables, the degree, and the number of inputs; and + * - coefficients of the A,B,C polynomials in the Lagrange basis. + * + * There is no need to store the Z polynomial because it is uniquely + * determined by the domain (as Z is its vanishing polynomial). + */ +template +class qap_instance { +private: + size_t num_variables_; + size_t degree_; + size_t num_inputs_; + +public: + std::shared_ptr > domain; + + std::vector > A_in_Lagrange_basis; + std::vector > B_in_Lagrange_basis; + std::vector > C_in_Lagrange_basis; + + qap_instance(const std::shared_ptr > &domain, + const size_t num_variables, + const size_t degree, + const size_t num_inputs, + const std::vector > &A_in_Lagrange_basis, + const std::vector > &B_in_Lagrange_basis, + const std::vector > &C_in_Lagrange_basis); + + qap_instance(const std::shared_ptr > &domain, + const size_t num_variables, + const size_t degree, + const size_t num_inputs, + std::vector > &&A_in_Lagrange_basis, + std::vector > &&B_in_Lagrange_basis, + std::vector > &&C_in_Lagrange_basis); + + qap_instance(const qap_instance &other) = default; + qap_instance(qap_instance &&other) = default; + qap_instance& operator=(const qap_instance &other) = default; + qap_instance& operator=(qap_instance &&other) = default; + + size_t num_variables() const; + size_t degree() const; + size_t num_inputs() const; + + bool is_satisfied(const qap_witness &witness) const; +}; + +/** + * A QAP instance evaluation is a QAP instance that is evaluated at a field element t. + * + * Specifically, the datastructure stores: + * - a choice of domain (corresponding to a certain subset of the field); + * - the number of variables, the degree, and the number of inputs; + * - a field element t; + * - evaluations of the A,B,C (and Z) polynomials at t; + * - evaluations of all monomials of t; + * - counts about how many of the above evaluations are in fact non-zero. + */ +template +class qap_instance_evaluation { +private: + size_t num_variables_; + size_t degree_; + size_t num_inputs_; +public: + std::shared_ptr > domain; + + FieldT t; + + std::vector At, Bt, Ct, Ht; + + FieldT Zt; + + qap_instance_evaluation(const std::shared_ptr > &domain, + const size_t num_variables, + const size_t degree, + const size_t num_inputs, + const FieldT &t, + const std::vector &At, + const std::vector &Bt, + const std::vector &Ct, + const std::vector &Ht, + const FieldT &Zt); + qap_instance_evaluation(const std::shared_ptr > &domain, + const size_t num_variables, + const size_t degree, + const size_t num_inputs, + const FieldT &t, + std::vector &&At, + std::vector &&Bt, + std::vector &&Ct, + std::vector &&Ht, + const FieldT &Zt); + + qap_instance_evaluation(const qap_instance_evaluation &other) = default; + qap_instance_evaluation(qap_instance_evaluation &&other) = default; + qap_instance_evaluation& operator=(const qap_instance_evaluation &other) = default; + qap_instance_evaluation& operator=(qap_instance_evaluation &&other) = default; + + size_t num_variables() const; + size_t degree() const; + size_t num_inputs() const; + + bool is_satisfied(const qap_witness &witness) const; +}; + +/** + * A QAP witness. + */ +template +class qap_witness { +private: + size_t num_variables_; + size_t degree_; + size_t num_inputs_; + +public: + FieldT d1, d2, d3; + + std::vector coefficients_for_ABCs; + std::vector coefficients_for_H; + + qap_witness(const size_t num_variables, + const size_t degree, + const size_t num_inputs, + const FieldT &d1, + const FieldT &d2, + const FieldT &d3, + const std::vector &coefficients_for_ABCs, + const std::vector &coefficients_for_H); + + qap_witness(const size_t num_variables, + const size_t degree, + const size_t num_inputs, + const FieldT &d1, + const FieldT &d2, + const FieldT &d3, + const std::vector &coefficients_for_ABCs, + std::vector &&coefficients_for_H); + + qap_witness(const qap_witness &other) = default; + qap_witness(qap_witness &&other) = default; + qap_witness& operator=(const qap_witness &other) = default; + qap_witness& operator=(qap_witness &&other) = default; + + size_t num_variables() const; + size_t degree() const; + size_t num_inputs() const; +}; + +} // libsnark + +#include "relations/arithmetic_programs/qap/qap.tcc" + +#endif // QAP_HPP_ diff --git a/src/snark/libsnark/relations/arithmetic_programs/qap/qap.tcc b/src/snark/libsnark/relations/arithmetic_programs/qap/qap.tcc new file mode 100644 index 000000000..a4a3c96a2 --- /dev/null +++ b/src/snark/libsnark/relations/arithmetic_programs/qap/qap.tcc @@ -0,0 +1,324 @@ +/** @file +***************************************************************************** + +Implementation of interfaces for a QAP ("Quadratic Arithmetic Program"). + +See qap.hpp . + +***************************************************************************** +* @author This file is part of libsnark, developed by SCIPR Lab +* and contributors (see AUTHORS). +* @copyright MIT license (see LICENSE file) +*****************************************************************************/ + +#ifndef QAP_TCC_ +#define QAP_TCC_ + +#include "common/profiling.hpp" +#include "common/utils.hpp" +#include "algebra/evaluation_domain/evaluation_domain.hpp" +#include "algebra/scalar_multiplication/multiexp.hpp" + +namespace libsnark { + +template +qap_instance::qap_instance(const std::shared_ptr > &domain, + const size_t num_variables, + const size_t degree, + const size_t num_inputs, + const std::vector > &A_in_Lagrange_basis, + const std::vector > &B_in_Lagrange_basis, + const std::vector > &C_in_Lagrange_basis) : + num_variables_(num_variables), + degree_(degree), + num_inputs_(num_inputs), + domain(domain), + A_in_Lagrange_basis(A_in_Lagrange_basis), + B_in_Lagrange_basis(B_in_Lagrange_basis), + C_in_Lagrange_basis(C_in_Lagrange_basis) +{ +} + +template +qap_instance::qap_instance(const std::shared_ptr > &domain, + const size_t num_variables, + const size_t degree, + const size_t num_inputs, + std::vector > &&A_in_Lagrange_basis, + std::vector > &&B_in_Lagrange_basis, + std::vector > &&C_in_Lagrange_basis) : + num_variables_(num_variables), + degree_(degree), + num_inputs_(num_inputs), + domain(domain), + A_in_Lagrange_basis(std::move(A_in_Lagrange_basis)), + B_in_Lagrange_basis(std::move(B_in_Lagrange_basis)), + C_in_Lagrange_basis(std::move(C_in_Lagrange_basis)) +{ +} + +template +size_t qap_instance::num_variables() const +{ + return num_variables_; +} + +template +size_t qap_instance::degree() const +{ + return degree_; +} + +template +size_t qap_instance::num_inputs() const +{ + return num_inputs_; +} + +template +bool qap_instance::is_satisfied(const qap_witness &witness) const +{ + const FieldT t = FieldT::random_element(); + + std::vector At(this->num_variables()+1, FieldT::zero()); + std::vector Bt(this->num_variables()+1, FieldT::zero()); + std::vector Ct(this->num_variables()+1, FieldT::zero()); + std::vector Ht(this->degree()+1); + + const FieldT Zt = this->domain->compute_Z(t); + + const std::vector u = this->domain->lagrange_coeffs(t); + + for (size_t i = 0; i < this->num_variables()+1; ++i) + { + for (auto &el : A_in_Lagrange_basis[i]) + { + At[i] += u[el.first] * el.second; + } + + for (auto &el : B_in_Lagrange_basis[i]) + { + Bt[i] += u[el.first] * el.second; + } + + for (auto &el : C_in_Lagrange_basis[i]) + { + Ct[i] += u[el.first] * el.second; + } + } + + FieldT ti = FieldT::one(); + for (size_t i = 0; i < this->degree()+1; ++i) + { + Ht[i] = ti; + ti *= t; + } + + const qap_instance_evaluation eval_qap_inst(this->domain, + this->num_variables(), + this->degree(), + this->num_inputs(), + t, + std::move(At), + std::move(Bt), + std::move(Ct), + std::move(Ht), + Zt); + return eval_qap_inst.is_satisfied(witness); +} + +template +qap_instance_evaluation::qap_instance_evaluation(const std::shared_ptr > &domain, + const size_t num_variables, + const size_t degree, + const size_t num_inputs, + const FieldT &t, + const std::vector &At, + const std::vector &Bt, + const std::vector &Ct, + const std::vector &Ht, + const FieldT &Zt) : + num_variables_(num_variables), + degree_(degree), + num_inputs_(num_inputs), + domain(domain), + t(t), + At(At), + Bt(Bt), + Ct(Ct), + Ht(Ht), + Zt(Zt) +{ +} + +template +qap_instance_evaluation::qap_instance_evaluation(const std::shared_ptr > &domain, + const size_t num_variables, + const size_t degree, + const size_t num_inputs, + const FieldT &t, + std::vector &&At, + std::vector &&Bt, + std::vector &&Ct, + std::vector &&Ht, + const FieldT &Zt) : + num_variables_(num_variables), + degree_(degree), + num_inputs_(num_inputs), + domain(domain), + t(t), + At(std::move(At)), + Bt(std::move(Bt)), + Ct(std::move(Ct)), + Ht(std::move(Ht)), + Zt(Zt) +{ +} + +template +size_t qap_instance_evaluation::num_variables() const +{ + return num_variables_; +} + +template +size_t qap_instance_evaluation::degree() const +{ + return degree_; +} + +template +size_t qap_instance_evaluation::num_inputs() const +{ + return num_inputs_; +} + +template +bool qap_instance_evaluation::is_satisfied(const qap_witness &witness) const +{ + + if (this->num_variables() != witness.num_variables()) + { + return false; + } + + if (this->degree() != witness.degree()) + { + return false; + } + + if (this->num_inputs() != witness.num_inputs()) + { + return false; + } + + if (this->num_variables() != witness.coefficients_for_ABCs.size()) + { + return false; + } + + if (this->degree()+1 != witness.coefficients_for_H.size()) + { + return false; + } + + if (this->At.size() != this->num_variables()+1 || this->Bt.size() != this->num_variables()+1 || this->Ct.size() != this->num_variables()+1) + { + return false; + } + + if (this->Ht.size() != this->degree()+1) + { + return false; + } + + if (this->Zt != this->domain->compute_Z(this->t)) + { + return false; + } + + FieldT ans_A = this->At[0] + witness.d1*this->Zt; + FieldT ans_B = this->Bt[0] + witness.d2*this->Zt; + FieldT ans_C = this->Ct[0] + witness.d3*this->Zt; + FieldT ans_H = FieldT::zero(); + + ans_A = ans_A + naive_plain_exp(this->At.begin()+1, this->At.begin()+1+this->num_variables(), + witness.coefficients_for_ABCs.begin(), witness.coefficients_for_ABCs.begin()+this->num_variables()); + ans_B = ans_B + naive_plain_exp(this->Bt.begin()+1, this->Bt.begin()+1+this->num_variables(), + witness.coefficients_for_ABCs.begin(), witness.coefficients_for_ABCs.begin()+this->num_variables()); + ans_C = ans_C + naive_plain_exp(this->Ct.begin()+1, this->Ct.begin()+1+this->num_variables(), + witness.coefficients_for_ABCs.begin(), witness.coefficients_for_ABCs.begin()+this->num_variables()); + ans_H = ans_H + naive_plain_exp(this->Ht.begin(), this->Ht.begin()+this->degree()+1, + witness.coefficients_for_H.begin(), witness.coefficients_for_H.begin()+this->degree()+1); + + if (ans_A * ans_B - ans_C != ans_H * this->Zt) + { + return false; + } + + return true; +} + +template +qap_witness::qap_witness(const size_t num_variables, + const size_t degree, + const size_t num_inputs, + const FieldT &d1, + const FieldT &d2, + const FieldT &d3, + const std::vector &coefficients_for_ABCs, + const std::vector &coefficients_for_H) : + num_variables_(num_variables), + degree_(degree), + num_inputs_(num_inputs), + d1(d1), + d2(d2), + d3(d3), + coefficients_for_ABCs(coefficients_for_ABCs), + coefficients_for_H(coefficients_for_H) +{ +} + +template +qap_witness::qap_witness(const size_t num_variables, + const size_t degree, + const size_t num_inputs, + const FieldT &d1, + const FieldT &d2, + const FieldT &d3, + const std::vector &coefficients_for_ABCs, + std::vector &&coefficients_for_H) : + num_variables_(num_variables), + degree_(degree), + num_inputs_(num_inputs), + d1(d1), + d2(d2), + d3(d3), + coefficients_for_ABCs(coefficients_for_ABCs), + coefficients_for_H(std::move(coefficients_for_H)) +{ +} + + +template +size_t qap_witness::num_variables() const +{ + return num_variables_; +} + +template +size_t qap_witness::degree() const +{ + return degree_; +} + +template +size_t qap_witness::num_inputs() const +{ + return num_inputs_; +} + + +} // libsnark + +#endif // QAP_TCC_ diff --git a/src/snark/libsnark/relations/arithmetic_programs/qap/tests/test_qap.cpp b/src/snark/libsnark/relations/arithmetic_programs/qap/tests/test_qap.cpp new file mode 100644 index 000000000..e20f589c9 --- /dev/null +++ b/src/snark/libsnark/relations/arithmetic_programs/qap/tests/test_qap.cpp @@ -0,0 +1,104 @@ +/** + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ +#include +#include +#include +#include +#include + +#include "algebra/curves/alt_bn128/alt_bn128_pp.hpp" +#include "algebra/fields/field_utils.hpp" +#include "common/profiling.hpp" +#include "common/utils.hpp" +#include "reductions/r1cs_to_qap/r1cs_to_qap.hpp" +#include "relations/constraint_satisfaction_problems/r1cs/examples/r1cs_examples.hpp" + +#include + +using namespace libsnark; + +template +void test_qap(const size_t qap_degree, const size_t num_inputs, const bool binary_input) +{ + /* + We construct an instance where the QAP degree is qap_degree. + So we generate an instance of R1CS where the number of constraints qap_degree - num_inputs - 1. + See the transformation from R1CS to QAP for why this is the case. + So we need that qap_degree >= num_inputs + 1. + */ + ASSERT_LE(num_inputs + 1, qap_degree); + enter_block("Call to test_qap"); + + const size_t num_constraints = qap_degree - num_inputs - 1; + + print_indent(); printf("* QAP degree: %zu\n", qap_degree); + print_indent(); printf("* Number of inputs: %zu\n", num_inputs); + print_indent(); printf("* Number of R1CS constraints: %zu\n", num_constraints); + print_indent(); printf("* Input type: %s\n", binary_input ? "binary" : "field"); + + enter_block("Generate constraint system and assignment"); + r1cs_example example; + if (binary_input) + { + example = generate_r1cs_example_with_binary_input(num_constraints, num_inputs); + } + else + { + example = generate_r1cs_example_with_field_input(num_constraints, num_inputs); + } + leave_block("Generate constraint system and assignment"); + + enter_block("Check satisfiability of constraint system"); + EXPECT_TRUE(example.constraint_system.is_satisfied(example.primary_input, example.auxiliary_input)); + leave_block("Check satisfiability of constraint system"); + + const FieldT t = FieldT::random_element(), + d1 = FieldT::random_element(), + d2 = FieldT::random_element(), + d3 = FieldT::random_element(); + + enter_block("Compute QAP instance 1"); + qap_instance qap_inst_1 = r1cs_to_qap_instance_map(example.constraint_system); + leave_block("Compute QAP instance 1"); + + enter_block("Compute QAP instance 2"); + qap_instance_evaluation qap_inst_2 = r1cs_to_qap_instance_map_with_evaluation(example.constraint_system, t); + leave_block("Compute QAP instance 2"); + + enter_block("Compute QAP witness"); + qap_witness qap_wit = r1cs_to_qap_witness_map(example.constraint_system, example.primary_input, example.auxiliary_input, d1, d2, d3); + leave_block("Compute QAP witness"); + + enter_block("Check satisfiability of QAP instance 1"); + EXPECT_TRUE(qap_inst_1.is_satisfied(qap_wit)); + leave_block("Check satisfiability of QAP instance 1"); + + enter_block("Check satisfiability of QAP instance 2"); + EXPECT_TRUE(qap_inst_2.is_satisfied(qap_wit)); + leave_block("Check satisfiability of QAP instance 2"); + + leave_block("Call to test_qap"); +} + +TEST(relations, qap) +{ + start_profiling(); + + const size_t num_inputs = 10; + + enter_block("Test QAP with binary input"); + + test_qap >(1ul << 21, num_inputs, true); + + leave_block("Test QAP with binary input"); + + enter_block("Test QAP with field input"); + + test_qap >(1ul << 21, num_inputs, false); + + leave_block("Test QAP with field input"); +} diff --git a/src/snark/libsnark/relations/constraint_satisfaction_problems/r1cs/examples/r1cs_examples.hpp b/src/snark/libsnark/relations/constraint_satisfaction_problems/r1cs/examples/r1cs_examples.hpp new file mode 100644 index 000000000..47003e959 --- /dev/null +++ b/src/snark/libsnark/relations/constraint_satisfaction_problems/r1cs/examples/r1cs_examples.hpp @@ -0,0 +1,73 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for a R1CS example, as well as functions to sample + R1CS examples with prescribed parameters (according to some distribution). + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef R1CS_EXAMPLES_HPP_ +#define R1CS_EXAMPLES_HPP_ + +#include "relations/constraint_satisfaction_problems/r1cs/r1cs.hpp" + +namespace libsnark { + +/** + * A R1CS example comprises a R1CS constraint system, R1CS input, and R1CS witness. + */ +template +struct r1cs_example { + r1cs_constraint_system constraint_system; + r1cs_primary_input primary_input; + r1cs_auxiliary_input auxiliary_input; + + r1cs_example() = default; + r1cs_example(const r1cs_example &other) = default; + r1cs_example(const r1cs_constraint_system &constraint_system, + const r1cs_primary_input &primary_input, + const r1cs_auxiliary_input &auxiliary_input) : + constraint_system(constraint_system), + primary_input(primary_input), + auxiliary_input(auxiliary_input) + {}; + r1cs_example(r1cs_constraint_system &&constraint_system, + r1cs_primary_input &&primary_input, + r1cs_auxiliary_input &&auxiliary_input) : + constraint_system(std::move(constraint_system)), + primary_input(std::move(primary_input)), + auxiliary_input(std::move(auxiliary_input)) + {}; +}; + +/** + * Generate a R1CS example such that: + * - the number of constraints of the R1CS constraint system is num_constraints; + * - the number of variables of the R1CS constraint system is (approximately) num_constraints; + * - the number of inputs of the R1CS constraint system is num_inputs; + * - the R1CS input consists of ``full'' field elements (typically require the whole log|Field| bits to represent). + */ +template +r1cs_example generate_r1cs_example_with_field_input(const size_t num_constraints, + const size_t num_inputs); + +/** + * Generate a R1CS example such that: + * - the number of constraints of the R1CS constraint system is num_constraints; + * - the number of variables of the R1CS constraint system is (approximately) num_constraints; + * - the number of inputs of the R1CS constraint system is num_inputs; + * - the R1CS input consists of binary values (as opposed to ``full'' field elements). + */ +template +r1cs_example generate_r1cs_example_with_binary_input(const size_t num_constraints, + const size_t num_inputs); + +} // libsnark + +#include "relations/constraint_satisfaction_problems/r1cs/examples/r1cs_examples.tcc" + +#endif // R1CS_EXAMPLES_HPP_ diff --git a/src/snark/libsnark/relations/constraint_satisfaction_problems/r1cs/examples/r1cs_examples.tcc b/src/snark/libsnark/relations/constraint_satisfaction_problems/r1cs/examples/r1cs_examples.tcc new file mode 100644 index 000000000..defa07721 --- /dev/null +++ b/src/snark/libsnark/relations/constraint_satisfaction_problems/r1cs/examples/r1cs_examples.tcc @@ -0,0 +1,164 @@ +/** @file + ***************************************************************************** + + Implementation of functions to sample R1CS examples with prescribed parameters + (according to some distribution). + + See r1cs_examples.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef R1CS_EXAMPLES_TCC_ +#define R1CS_EXAMPLES_TCC_ + +#include + +#include "common/utils.hpp" + +namespace libsnark { + +template +r1cs_example generate_r1cs_example_with_field_input(const size_t num_constraints, + const size_t num_inputs) +{ + enter_block("Call to generate_r1cs_example_with_field_input"); + + assert(num_inputs <= num_constraints + 2); + + r1cs_constraint_system cs; + cs.primary_input_size = num_inputs; + cs.auxiliary_input_size = 2 + num_constraints - num_inputs; // TODO: explain this + + r1cs_variable_assignment full_variable_assignment; + FieldT a = FieldT::random_element(); + FieldT b = FieldT::random_element(); + full_variable_assignment.push_back(a); + full_variable_assignment.push_back(b); + + for (size_t i = 0; i < num_constraints-1; ++i) + { + linear_combination A, B, C; + + if (i % 2) + { + // a * b = c + A.add_term(i+1, 1); + B.add_term(i+2, 1); + C.add_term(i+3, 1); + FieldT tmp = a*b; + full_variable_assignment.push_back(tmp); + a = b; b = tmp; + } + else + { + // a + b = c + B.add_term(0, 1); + A.add_term(i+1, 1); + A.add_term(i+2, 1); + C.add_term(i+3, 1); + FieldT tmp = a+b; + full_variable_assignment.push_back(tmp); + a = b; b = tmp; + } + + cs.add_constraint(r1cs_constraint(A, B, C)); + } + + linear_combination A, B, C; + FieldT fin = FieldT::zero(); + for (size_t i = 1; i < cs.num_variables(); ++i) + { + A.add_term(i, 1); + B.add_term(i, 1); + fin = fin + full_variable_assignment[i-1]; + } + C.add_term(cs.num_variables(), 1); + cs.add_constraint(r1cs_constraint(A, B, C)); + full_variable_assignment.push_back(fin.squared()); + + /* split variable assignment */ + r1cs_primary_input primary_input(full_variable_assignment.begin(), full_variable_assignment.begin() + num_inputs); + r1cs_primary_input auxiliary_input(full_variable_assignment.begin() + num_inputs, full_variable_assignment.end()); + + /* sanity checks */ + assert(cs.num_variables() == full_variable_assignment.size()); + assert(cs.num_variables() >= num_inputs); + assert(cs.num_inputs() == num_inputs); + assert(cs.num_constraints() == num_constraints); + assert(cs.is_satisfied(primary_input, auxiliary_input)); + + leave_block("Call to generate_r1cs_example_with_field_input"); + + return r1cs_example(std::move(cs), std::move(primary_input), std::move(auxiliary_input)); +} + +template +r1cs_example generate_r1cs_example_with_binary_input(const size_t num_constraints, + const size_t num_inputs) +{ + enter_block("Call to generate_r1cs_example_with_binary_input"); + + assert(num_inputs >= 1); + + r1cs_constraint_system cs; + cs.primary_input_size = num_inputs; + cs.auxiliary_input_size = num_constraints; /* we will add one auxiliary variable per constraint */ + + r1cs_variable_assignment full_variable_assignment; + for (size_t i = 0; i < num_inputs; ++i) + { + full_variable_assignment.push_back(FieldT(std::rand() % 2)); + } + + size_t lastvar = num_inputs-1; + for (size_t i = 0; i < num_constraints; ++i) + { + ++lastvar; + const size_t u = (i == 0 ? std::rand() % num_inputs : std::rand() % i); + const size_t v = (i == 0 ? std::rand() % num_inputs : std::rand() % i); + + /* chose two random bits and XOR them together: + res = u + v - 2 * u * v + 2 * u * v = u + v - res + */ + linear_combination A, B, C; + A.add_term(u+1, 2); + B.add_term(v+1, 1); + if (u == v) + { + C.add_term(u+1, 2); + } + else + { + C.add_term(u+1, 1); + C.add_term(v+1, 1); + } + C.add_term(lastvar+1, -FieldT::one()); + + cs.add_constraint(r1cs_constraint(A, B, C)); + full_variable_assignment.push_back(full_variable_assignment[u] + full_variable_assignment[v] - full_variable_assignment[u] * full_variable_assignment[v] - full_variable_assignment[u] * full_variable_assignment[v]); + } + + /* split variable assignment */ + r1cs_primary_input primary_input(full_variable_assignment.begin(), full_variable_assignment.begin() + num_inputs); + r1cs_primary_input auxiliary_input(full_variable_assignment.begin() + num_inputs, full_variable_assignment.end()); + + /* sanity checks */ + assert(cs.num_variables() == full_variable_assignment.size()); + assert(cs.num_variables() >= num_inputs); + assert(cs.num_inputs() == num_inputs); + assert(cs.num_constraints() == num_constraints); + assert(cs.is_satisfied(primary_input, auxiliary_input)); + + leave_block("Call to generate_r1cs_example_with_binary_input"); + + return r1cs_example(std::move(cs), std::move(primary_input), std::move(auxiliary_input)); +} + +} // libsnark + +#endif // R1CS_EXAMPLES_TCC diff --git a/src/snark/libsnark/relations/constraint_satisfaction_problems/r1cs/r1cs.hpp b/src/snark/libsnark/relations/constraint_satisfaction_problems/r1cs/r1cs.hpp new file mode 100644 index 000000000..ca3acb3a9 --- /dev/null +++ b/src/snark/libsnark/relations/constraint_satisfaction_problems/r1cs/r1cs.hpp @@ -0,0 +1,153 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for: + - a R1CS constraint, + - a R1CS variable assignment, and + - a R1CS constraint system. + + Above, R1CS stands for "Rank-1 Constraint System". + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef R1CS_HPP_ +#define R1CS_HPP_ + +#include +#include +#include +#include +#include + +#include "relations/variable.hpp" + +namespace libsnark { + +/************************* R1CS constraint ***********************************/ + +template +class r1cs_constraint; + +template +std::ostream& operator<<(std::ostream &out, const r1cs_constraint &c); + +template +std::istream& operator>>(std::istream &in, r1cs_constraint &c); + +/** + * A R1CS constraint is a formal expression of the form + * + * < A , X > * < B , X > = < C , X > , + * + * where X = (x_0,x_1,...,x_m) is a vector of formal variables and A,B,C each + * consist of 1+m elements in . + * + * A R1CS constraint is used to construct a R1CS constraint system (see below). + */ +template +class r1cs_constraint { +public: + + linear_combination a, b, c; + + r1cs_constraint() {}; + r1cs_constraint(const linear_combination &a, + const linear_combination &b, + const linear_combination &c); + + r1cs_constraint(const std::initializer_list > &A, + const std::initializer_list > &B, + const std::initializer_list > &C); + + bool operator==(const r1cs_constraint &other) const; + + friend std::ostream& operator<< (std::ostream &out, const r1cs_constraint &c); + friend std::istream& operator>> (std::istream &in, r1cs_constraint &c); +}; + +/************************* R1CS variable assignment **************************/ + +/** + * A R1CS variable assignment is a vector of elements that represents + * a candidate solution to a R1CS constraint system (see below). + */ + +/* TODO: specify that it does *NOT* include the constant 1 */ +template +using r1cs_primary_input = std::vector; + +template +using r1cs_auxiliary_input = std::vector; + +template +using r1cs_variable_assignment = std::vector; /* note the changed name! (TODO: remove this comment after primary_input transition is complete) */ + +/************************* R1CS constraint system ****************************/ + +template +class r1cs_constraint_system; + +template +std::ostream& operator<<(std::ostream &out, const r1cs_constraint_system &cs); + +template +std::istream& operator>>(std::istream &in, r1cs_constraint_system &cs); + +/** + * A system of R1CS constraints looks like + * + * { < A_k , X > * < B_k , X > = < C_k , X > }_{k=1}^{n} . + * + * In other words, the system is satisfied if and only if there exist a + * USCS variable assignment for which each R1CS constraint is satisfied. + * + * NOTE: + * The 0-th variable (i.e., "x_{0}") always represents the constant 1. + * Thus, the 0-th variable is not included in num_variables. + */ +template +class r1cs_constraint_system { +public: + size_t primary_input_size; + size_t auxiliary_input_size; + + std::vector > constraints; + + r1cs_constraint_system() : primary_input_size(0), auxiliary_input_size(0) {} + + size_t num_inputs() const; + size_t num_variables() const; + size_t num_constraints() const; + +#ifdef DEBUG + std::map constraint_annotations; + std::map variable_annotations; +#endif + + bool is_valid() const; + bool is_satisfied(const r1cs_primary_input &primary_input, + const r1cs_auxiliary_input &auxiliary_input) const; + + void add_constraint(const r1cs_constraint &c); + void add_constraint(const r1cs_constraint &c, const std::string &annotation); + + void swap_AB_if_beneficial(); + + bool operator==(const r1cs_constraint_system &other) const; + + friend std::ostream& operator<< (std::ostream &out, const r1cs_constraint_system &cs); + friend std::istream& operator>> (std::istream &in, r1cs_constraint_system &cs); + + void report_linear_constraint_statistics() const; +}; + + +} // libsnark + +#include "relations/constraint_satisfaction_problems/r1cs/r1cs.tcc" + +#endif // R1CS_HPP_ diff --git a/src/snark/libsnark/relations/constraint_satisfaction_problems/r1cs/r1cs.tcc b/src/snark/libsnark/relations/constraint_satisfaction_problems/r1cs/r1cs.tcc new file mode 100644 index 000000000..0faa56a87 --- /dev/null +++ b/src/snark/libsnark/relations/constraint_satisfaction_problems/r1cs/r1cs.tcc @@ -0,0 +1,310 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for: + - a R1CS constraint, + - a R1CS variable assignment, and + - a R1CS constraint system. + + See r1cs.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef R1CS_TCC_ +#define R1CS_TCC_ + +#include +#include +#include +#include "common/utils.hpp" +#include "common/profiling.hpp" +#include "algebra/fields/bigint.hpp" + +namespace libsnark { + +template +r1cs_constraint::r1cs_constraint(const linear_combination &a, + const linear_combination &b, + const linear_combination &c) : + a(a), b(b), c(c) +{ +} + +template +r1cs_constraint::r1cs_constraint(const std::initializer_list > &A, + const std::initializer_list > &B, + const std::initializer_list > &C) +{ + for (auto lc_A : A) + { + a.terms.insert(a.terms.end(), lc_A.terms.begin(), lc_A.terms.end()); + } + for (auto lc_B : B) + { + b.terms.insert(b.terms.end(), lc_B.terms.begin(), lc_B.terms.end()); + } + for (auto lc_C : C) + { + c.terms.insert(c.terms.end(), lc_C.terms.begin(), lc_C.terms.end()); + } +} + +template +bool r1cs_constraint::operator==(const r1cs_constraint &other) const +{ + return (this->a == other.a && + this->b == other.b && + this->c == other.c); +} + +template +std::ostream& operator<<(std::ostream &out, const r1cs_constraint &c) +{ + out << c.a; + out << c.b; + out << c.c; + + return out; +} + +template +std::istream& operator>>(std::istream &in, r1cs_constraint &c) +{ + in >> c.a; + in >> c.b; + in >> c.c; + + return in; +} + +template +size_t r1cs_constraint_system::num_inputs() const +{ + return primary_input_size; +} + +template +size_t r1cs_constraint_system::num_variables() const +{ + return primary_input_size + auxiliary_input_size; +} + + +template +size_t r1cs_constraint_system::num_constraints() const +{ + return constraints.size(); +} + +template +bool r1cs_constraint_system::is_valid() const +{ + if (this->num_inputs() > this->num_variables()) return false; + + for (size_t c = 0; c < constraints.size(); ++c) + { + if (!(constraints[c].a.is_valid(this->num_variables()) && + constraints[c].b.is_valid(this->num_variables()) && + constraints[c].c.is_valid(this->num_variables()))) + { + return false; + } + } + + return true; +} + +template +void dump_r1cs_constraint(const r1cs_constraint &constraint, + const r1cs_variable_assignment &full_variable_assignment, + const std::map &variable_annotations) +{ + printf("terms for a:\n"); constraint.a.print_with_assignment(full_variable_assignment, variable_annotations); + printf("terms for b:\n"); constraint.b.print_with_assignment(full_variable_assignment, variable_annotations); + printf("terms for c:\n"); constraint.c.print_with_assignment(full_variable_assignment, variable_annotations); +} + +template +bool r1cs_constraint_system::is_satisfied(const r1cs_primary_input &primary_input, + const r1cs_auxiliary_input &auxiliary_input) const +{ + assert(primary_input.size() == num_inputs()); + assert(primary_input.size() + auxiliary_input.size() == num_variables()); + + r1cs_variable_assignment full_variable_assignment = primary_input; + full_variable_assignment.insert(full_variable_assignment.end(), auxiliary_input.begin(), auxiliary_input.end()); + + for (size_t c = 0; c < constraints.size(); ++c) + { + const FieldT ares = constraints[c].a.evaluate(full_variable_assignment); + const FieldT bres = constraints[c].b.evaluate(full_variable_assignment); + const FieldT cres = constraints[c].c.evaluate(full_variable_assignment); + + if (!(ares*bres == cres)) + { +#ifdef DEBUG + auto it = constraint_annotations.find(c); + printf("constraint %zu (%s) unsatisfied\n", c, (it == constraint_annotations.end() ? "no annotation" : it->second.c_str())); + printf(" = "); ares.print(); + printf(" = "); bres.print(); + printf(" = "); cres.print(); + printf("constraint was:\n"); + dump_r1cs_constraint(constraints[c], full_variable_assignment, variable_annotations); +#endif // DEBUG + return false; + } + } + + return true; +} + +template +void r1cs_constraint_system::add_constraint(const r1cs_constraint &c) +{ + constraints.emplace_back(c); +} + +template +void r1cs_constraint_system::add_constraint(const r1cs_constraint &c, const std::string &annotation) +{ +#ifdef DEBUG + constraint_annotations[constraints.size()] = annotation; +#endif + constraints.emplace_back(c); +} + +template +void r1cs_constraint_system::swap_AB_if_beneficial() +{ + enter_block("Call to r1cs_constraint_system::swap_AB_if_beneficial"); + + enter_block("Estimate densities"); + bit_vector touched_by_A(this->num_variables() + 1, false), touched_by_B(this->num_variables() + 1, false); + + for (size_t i = 0; i < this->constraints.size(); ++i) + { + for (size_t j = 0; j < this->constraints[i].a.terms.size(); ++j) + { + touched_by_A[this->constraints[i].a.terms[j].index] = true; + } + + for (size_t j = 0; j < this->constraints[i].b.terms.size(); ++j) + { + touched_by_B[this->constraints[i].b.terms[j].index] = true; + } + } + + size_t non_zero_A_count = 0, non_zero_B_count = 0; + for (size_t i = 0; i < this->num_variables() + 1; ++i) + { + non_zero_A_count += touched_by_A[i] ? 1 : 0; + non_zero_B_count += touched_by_B[i] ? 1 : 0; + } + + if (!inhibit_profiling_info) + { + print_indent(); printf("* Non-zero A-count (estimate): %zu\n", non_zero_A_count); + print_indent(); printf("* Non-zero B-count (estimate): %zu\n", non_zero_B_count); + } + leave_block("Estimate densities"); + + if (non_zero_B_count > non_zero_A_count) + { + enter_block("Perform the swap"); + for (size_t i = 0; i < this->constraints.size(); ++i) + { + std::swap(this->constraints[i].a, this->constraints[i].b); + } + leave_block("Perform the swap"); + } + else + { + print_indent(); printf("Swap is not beneficial, not performing\n"); + } + + leave_block("Call to r1cs_constraint_system::swap_AB_if_beneficial"); +} + +template +bool r1cs_constraint_system::operator==(const r1cs_constraint_system &other) const +{ + return (this->constraints == other.constraints && + this->primary_input_size == other.primary_input_size && + this->auxiliary_input_size == other.auxiliary_input_size); +} + +template +std::ostream& operator<<(std::ostream &out, const r1cs_constraint_system &cs) +{ + out << cs.primary_input_size << "\n"; + out << cs.auxiliary_input_size << "\n"; + + out << cs.num_constraints() << "\n"; + for (const r1cs_constraint& c : cs.constraints) + { + out << c; + } + + return out; +} + +template +std::istream& operator>>(std::istream &in, r1cs_constraint_system &cs) +{ + in >> cs.primary_input_size; + in >> cs.auxiliary_input_size; + + cs.constraints.clear(); + + size_t s; + in >> s; + + char b; + in.read(&b, 1); + + cs.constraints.reserve(s); + + for (size_t i = 0; i < s; ++i) + { + r1cs_constraint c; + in >> c; + cs.constraints.emplace_back(c); + } + + return in; +} + +template +void r1cs_constraint_system::report_linear_constraint_statistics() const +{ +#ifdef DEBUG + for (size_t i = 0; i < constraints.size(); ++i) + { + auto &constr = constraints[i]; + bool a_is_const = true; + for (auto &t : constr.a.terms) + { + a_is_const = a_is_const && (t.index == 0); + } + + bool b_is_const = true; + for (auto &t : constr.b.terms) + { + b_is_const = b_is_const && (t.index == 0); + } + + if (a_is_const || b_is_const) + { + auto it = constraint_annotations.find(i); + printf("%s\n", (it == constraint_annotations.end() ? FORMAT("", "constraint_%zu", i) : it->second).c_str()); + } + } +#endif +} + +} // libsnark +#endif // R1CS_TCC_ diff --git a/src/snark/libsnark/relations/variable.hpp b/src/snark/libsnark/relations/variable.hpp new file mode 100644 index 000000000..8c2c704a3 --- /dev/null +++ b/src/snark/libsnark/relations/variable.hpp @@ -0,0 +1,213 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for: + - a variable (i.e., x_i), + - a linear term (i.e., a_i * x_i), and + - a linear combination (i.e., sum_i a_i * x_i). + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef VARIABLE_HPP_ +#define VARIABLE_HPP_ + +#include +#include +#include +#include + +namespace libsnark { + +/** + * Mnemonic typedefs. + */ +typedef size_t var_index_t; +typedef long integer_coeff_t; + +/** + * Forward declaration. + */ +template +class linear_term; + +/** + * Forward declaration. + */ +template +class linear_combination; + +/********************************* Variable **********************************/ + +/** + * A variable represents a formal expression of the form "x_{index}". + */ +template +class variable { +public: + + var_index_t index; + + variable(const var_index_t index = 0) : index(index) {}; + + linear_term operator*(const integer_coeff_t int_coeff) const; + linear_term operator*(const FieldT &field_coeff) const; + + linear_combination operator+(const linear_combination &other) const; + linear_combination operator-(const linear_combination &other) const; + + linear_term operator-() const; + + bool operator==(const variable &other) const; +}; + +template +linear_term operator*(const integer_coeff_t int_coeff, const variable &var); + +template +linear_term operator*(const FieldT &field_coeff, const variable &var); + +template +linear_combination operator+(const integer_coeff_t int_coeff, const variable &var); + +template +linear_combination operator+(const FieldT &field_coeff, const variable &var); + +template +linear_combination operator-(const integer_coeff_t int_coeff, const variable &var); + +template +linear_combination operator-(const FieldT &field_coeff, const variable &var); + + +/****************************** Linear term **********************************/ + +/** + * A linear term represents a formal expression of the form "coeff * x_{index}". + */ +template +class linear_term { +public: + + var_index_t index = 0; + FieldT coeff; + + linear_term() {}; + linear_term(const variable &var); + linear_term(const variable &var, const integer_coeff_t int_coeff); + linear_term(const variable &var, const FieldT &field_coeff); + + linear_term operator*(const integer_coeff_t int_coeff) const; + linear_term operator*(const FieldT &field_coeff) const; + + linear_combination operator+(const linear_combination &other) const; + linear_combination operator-(const linear_combination &other) const; + + linear_term operator-() const; + + bool operator==(const linear_term &other) const; +}; + +template +linear_term operator*(const integer_coeff_t int_coeff, const linear_term <); + +template +linear_term operator*(const FieldT &field_coeff, const linear_term <); + +template +linear_combination operator+(const integer_coeff_t int_coeff, const linear_term <); + +template +linear_combination operator+(const FieldT &field_coeff, const linear_term <); + +template +linear_combination operator-(const integer_coeff_t int_coeff, const linear_term <); + +template +linear_combination operator-(const FieldT &field_coeff, const linear_term <); + + +/***************************** Linear combination ****************************/ + +template +class linear_combination; + +template +std::ostream& operator<<(std::ostream &out, const linear_combination &lc); + +template +std::istream& operator>>(std::istream &in, linear_combination &lc); + +/** + * A linear combination represents a formal expression of the form "sum_i coeff_i * x_{index_i}". + */ +template +class linear_combination { +public: + + std::vector > terms; + + linear_combination() {}; + linear_combination(const integer_coeff_t int_coeff); + linear_combination(const FieldT &field_coeff); + linear_combination(const variable &var); + linear_combination(const linear_term <); + linear_combination(const std::vector > &all_terms); + + /* for supporting range-based for loops over linear_combination */ + typename std::vector >::const_iterator begin() const; + typename std::vector >::const_iterator end() const; + + void add_term(const variable &var); + void add_term(const variable &var, const integer_coeff_t int_coeff); + void add_term(const variable &var, const FieldT &field_coeff); + + void add_term(const linear_term <); + + FieldT evaluate(const std::vector &assignment) const; + + linear_combination operator*(const integer_coeff_t int_coeff) const; + linear_combination operator*(const FieldT &field_coeff) const; + + linear_combination operator+(const linear_combination &other) const; + + linear_combination operator-(const linear_combination &other) const; + linear_combination operator-() const; + + bool operator==(const linear_combination &other) const; + + bool is_valid(const size_t num_variables) const; + + void print(const std::map &variable_annotations = std::map()) const; + void print_with_assignment(const std::vector &full_assignment, const std::map &variable_annotations = std::map()) const; + + friend std::ostream& operator<< (std::ostream &out, const linear_combination &lc); + friend std::istream& operator>> (std::istream &in, linear_combination &lc); +}; + +template +linear_combination operator*(const integer_coeff_t int_coeff, const linear_combination &lc); + +template +linear_combination operator*(const FieldT &field_coeff, const linear_combination &lc); + +template +linear_combination operator+(const integer_coeff_t int_coeff, const linear_combination &lc); + +template +linear_combination operator+(const FieldT &field_coeff, const linear_combination &lc); + +template +linear_combination operator-(const integer_coeff_t int_coeff, const linear_combination &lc); + +template +linear_combination operator-(const FieldT &field_coeff, const linear_combination &lc); + +} // libsnark + +#include "relations/variable.tcc" + +#endif // VARIABLE_HPP_ diff --git a/src/snark/libsnark/relations/variable.tcc b/src/snark/libsnark/relations/variable.tcc new file mode 100644 index 000000000..4c4cab97f --- /dev/null +++ b/src/snark/libsnark/relations/variable.tcc @@ -0,0 +1,512 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for: + - a variable (i.e., x_i), + - a linear term (i.e., a_i * x_i), and + - a linear combination (i.e., sum_i a_i * x_i). + + See variabe.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef VARIABLE_TCC_ +#define VARIABLE_TCC_ + +#include +#include + +#include "algebra/fields/bigint.hpp" + +namespace libsnark { + +template +linear_term variable::operator*(const integer_coeff_t int_coeff) const +{ + return linear_term(*this, int_coeff); +} + +template +linear_term variable::operator*(const FieldT &field_coeff) const +{ + return linear_term(*this, field_coeff); +} + +template +linear_combination variable::operator+(const linear_combination &other) const +{ + linear_combination result; + + result.add_term(*this); + result.terms.insert(result.terms.begin(), other.terms.begin(), other.terms.end()); + + return result; +} + +template +linear_combination variable::operator-(const linear_combination &other) const +{ + return (*this) + (-other); +} + +template +linear_term variable::operator-() const +{ + return linear_term(*this, -FieldT::one()); +} + +template +bool variable::operator==(const variable &other) const +{ + return (this->index == other.index); +} + +template +linear_term operator*(const integer_coeff_t int_coeff, const variable &var) +{ + return linear_term(var, int_coeff); +} + +template +linear_term operator*(const FieldT &field_coeff, const variable &var) +{ + return linear_term(var, field_coeff); +} + +template +linear_combination operator+(const integer_coeff_t int_coeff, const variable &var) +{ + return linear_combination(int_coeff) + var; +} + +template +linear_combination operator+(const FieldT &field_coeff, const variable &var) +{ + return linear_combination(field_coeff) + var; +} + +template +linear_combination operator-(const integer_coeff_t int_coeff, const variable &var) +{ + return linear_combination(int_coeff) - var; +} + +template +linear_combination operator-(const FieldT &field_coeff, const variable &var) +{ + return linear_combination(field_coeff) - var; +} + +template +linear_term::linear_term(const variable &var) : + index(var.index), coeff(FieldT::one()) +{ +} + +template +linear_term::linear_term(const variable &var, const integer_coeff_t int_coeff) : + index(var.index), coeff(FieldT(int_coeff)) +{ +} + +template +linear_term::linear_term(const variable &var, const FieldT &coeff) : + index(var.index), coeff(coeff) +{ +} + +template +linear_term linear_term::operator*(const integer_coeff_t int_coeff) const +{ + return (this->operator*(FieldT(int_coeff))); +} + +template +linear_term linear_term::operator*(const FieldT &field_coeff) const +{ + return linear_term(this->index, field_coeff * this->coeff); +} + +template +linear_combination operator+(const integer_coeff_t int_coeff, const linear_term <) +{ + return linear_combination(int_coeff) + lt; +} + +template +linear_combination operator+(const FieldT &field_coeff, const linear_term <) +{ + return linear_combination(field_coeff) + lt; +} + +template +linear_combination operator-(const integer_coeff_t int_coeff, const linear_term <) +{ + return linear_combination(int_coeff) - lt; +} + +template +linear_combination operator-(const FieldT &field_coeff, const linear_term <) +{ + return linear_combination(field_coeff) - lt; +} + +template +linear_combination linear_term::operator+(const linear_combination &other) const +{ + return linear_combination(*this) + other; +} + +template +linear_combination linear_term::operator-(const linear_combination &other) const +{ + return (*this) + (-other); +} + +template +linear_term linear_term::operator-() const +{ + return linear_term(this->index, -this->coeff); +} + +template +bool linear_term::operator==(const linear_term &other) const +{ + return (this->index == other.index && + this->coeff == other.coeff); +} + +template +linear_term operator*(const integer_coeff_t int_coeff, const linear_term <) +{ + return FieldT(int_coeff) * lt; +} + +template +linear_term operator*(const FieldT &field_coeff, const linear_term <) +{ + return linear_term(lt.index, field_coeff * lt.coeff); +} + +template +linear_combination::linear_combination(const integer_coeff_t int_coeff) +{ + this->add_term(linear_term(0, int_coeff)); +} + +template +linear_combination::linear_combination(const FieldT &field_coeff) +{ + this->add_term(linear_term(0, field_coeff)); +} + +template +linear_combination::linear_combination(const variable &var) +{ + this->add_term(var); +} + +template +linear_combination::linear_combination(const linear_term <) +{ + this->add_term(lt); +} + +template +typename std::vector >::const_iterator linear_combination::begin() const +{ + return terms.begin(); +} + +template +typename std::vector >::const_iterator linear_combination::end() const +{ + return terms.end(); +} + +template +void linear_combination::add_term(const variable &var) +{ + this->terms.emplace_back(linear_term(var.index, FieldT::one())); +} + +template +void linear_combination::add_term(const variable &var, const integer_coeff_t int_coeff) +{ + this->terms.emplace_back(linear_term(var.index, int_coeff)); +} + +template +void linear_combination::add_term(const variable &var, const FieldT &coeff) +{ + this->terms.emplace_back(linear_term(var.index, coeff)); +} + +template +void linear_combination::add_term(const linear_term &other) +{ + this->terms.emplace_back(other); +} + +template +linear_combination linear_combination::operator*(const integer_coeff_t int_coeff) const +{ + return (*this) * FieldT(int_coeff); +} + +template +FieldT linear_combination::evaluate(const std::vector &assignment) const +{ + FieldT acc = FieldT::zero(); + for (auto < : terms) + { + acc += (lt.index == 0 ? FieldT::one() : assignment[lt.index-1]) * lt.coeff; + } + return acc; +} + +template +linear_combination linear_combination::operator*(const FieldT &field_coeff) const +{ + linear_combination result; + result.terms.reserve(this->terms.size()); + for (const linear_term < : this->terms) + { + result.terms.emplace_back(lt * field_coeff); + } + return result; +} + +template +linear_combination linear_combination::operator+(const linear_combination &other) const +{ + linear_combination result; + + auto it1 = this->terms.begin(); + auto it2 = other.terms.begin(); + + /* invariant: it1 and it2 always point to unprocessed items in the corresponding linear combinations */ + while (it1 != this->terms.end() && it2 != other.terms.end()) + { + if (it1->index < it2->index) + { + result.terms.emplace_back(*it1); + ++it1; + } + else if (it1->index > it2->index) + { + result.terms.emplace_back(*it2); + ++it2; + } + else + { + /* it1->index == it2->index */ + result.terms.emplace_back(linear_term(variable(it1->index), it1->coeff + it2->coeff)); + ++it1; + ++it2; + } + } + + if (it1 != this->terms.end()) + { + result.terms.insert(result.terms.end(), it1, this->terms.end()); + } + else + { + result.terms.insert(result.terms.end(), it2, other.terms.end()); + } + + return result; +} + +template +linear_combination linear_combination::operator-(const linear_combination &other) const +{ + return (*this) + (-other); +} + +template +linear_combination linear_combination::operator-() const +{ + return (*this) * (-FieldT::one()); +} + +template +bool linear_combination::operator==(const linear_combination &other) const +{ + return (this->terms == other.terms); +} + +template +bool linear_combination::is_valid(const size_t num_variables) const +{ + /* check that all terms in linear combination are sorted */ + for (size_t i = 1; i < terms.size(); ++i) + { + if (terms[i-1].index >= terms[i].index) + { + return false; + } + } + + /* check that the variables are in proper range. as the variables + are sorted, it suffices to check the last term */ + if ((--terms.end())->index >= num_variables) + { + return false; + } + + return true; +} + +template +void linear_combination::print(const std::map &variable_annotations) const +{ + for (auto < : terms) + { + if (lt.index == 0) + { + printf(" 1 * "); + lt.coeff.print(); + } + else + { + auto it = variable_annotations.find(lt.index); + printf(" x_%zu (%s) * ", lt.index, (it == variable_annotations.end() ? "no annotation" : it->second.c_str())); + lt.coeff.print(); + } + } +} + +template +void linear_combination::print_with_assignment(const std::vector &full_assignment, const std::map &variable_annotations) const +{ + for (auto < : terms) + { + if (lt.index == 0) + { + printf(" 1 * "); + lt.coeff.print(); + } + else + { + printf(" x_%zu * ", lt.index); + lt.coeff.print(); + + auto it = variable_annotations.find(lt.index); + printf(" where x_%zu (%s) was assigned value ", lt.index, + (it == variable_annotations.end() ? "no annotation" : it->second.c_str())); + full_assignment[lt.index-1].print(); + printf(" i.e. negative of "); + (-full_assignment[lt.index-1]).print(); + } + } +} + +template +std::ostream& operator<<(std::ostream &out, const linear_combination &lc) +{ + out << lc.terms.size() << "\n"; + for (const linear_term& lt : lc.terms) + { + out << lt.index << "\n"; + out << lt.coeff << OUTPUT_NEWLINE; + } + + return out; +} + +template +std::istream& operator>>(std::istream &in, linear_combination &lc) +{ + lc.terms.clear(); + + size_t s; + in >> s; + + consume_newline(in); + + lc.terms.reserve(s); + + for (size_t i = 0; i < s; ++i) + { + linear_term lt; + in >> lt.index; + consume_newline(in); + in >> lt.coeff; + consume_OUTPUT_NEWLINE(in); + lc.terms.emplace_back(lt); + } + + return in; +} + +template +linear_combination operator*(const integer_coeff_t int_coeff, const linear_combination &lc) +{ + return lc * int_coeff; +} + +template +linear_combination operator*(const FieldT &field_coeff, const linear_combination &lc) +{ + return lc * field_coeff; +} + +template +linear_combination operator+(const integer_coeff_t int_coeff, const linear_combination &lc) +{ + return linear_combination(int_coeff) + lc; +} + +template +linear_combination operator+(const FieldT &field_coeff, const linear_combination &lc) +{ + return linear_combination(field_coeff) + lc; +} + +template +linear_combination operator-(const integer_coeff_t int_coeff, const linear_combination &lc) +{ + return linear_combination(int_coeff) - lc; +} + +template +linear_combination operator-(const FieldT &field_coeff, const linear_combination &lc) +{ + return linear_combination(field_coeff) - lc; +} + +template +linear_combination::linear_combination(const std::vector > &all_terms) +{ + if (all_terms.empty()) + { + return; + } + + terms = all_terms; + std::sort(terms.begin(), terms.end(), [](linear_term a, linear_term b) { return a.index < b.index; }); + + auto result_it = terms.begin(); + for (auto it = ++terms.begin(); it != terms.end(); ++it) + { + if (it->index == result_it->index) + { + result_it->coeff += it->coeff; + } + else + { + *(++result_it) = *it; + } + } + terms.resize((result_it - terms.begin()) + 1); +} + +} // libsnark + +#endif // VARIABLE_TCC diff --git a/src/snark/libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/examples/run_r1cs_ppzksnark.hpp b/src/snark/libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/examples/run_r1cs_ppzksnark.hpp new file mode 100644 index 000000000..fcd28abf3 --- /dev/null +++ b/src/snark/libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/examples/run_r1cs_ppzksnark.hpp @@ -0,0 +1,35 @@ +/** @file + ***************************************************************************** + + Declaration of functionality that runs the R1CS ppzkSNARK for + a given R1CS example. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef RUN_R1CS_PPZKSNARK_HPP_ +#define RUN_R1CS_PPZKSNARK_HPP_ + +#include "relations/constraint_satisfaction_problems/r1cs/examples/r1cs_examples.hpp" + +namespace libsnark { + +/** + * Runs the ppzkSNARK (generator, prover, and verifier) for a given + * R1CS example (specified by a constraint system, input, and witness). + * + * Optionally, also test the serialization routines for keys and proofs. + * (This takes additional time.) + */ +template +bool run_r1cs_ppzksnark(const r1cs_example > &example, + const bool test_serialization); + +} // libsnark + +#include "zk_proof_systems/ppzksnark/r1cs_ppzksnark/examples/run_r1cs_ppzksnark.tcc" + +#endif // RUN_R1CS_PPZKSNARK_HPP_ diff --git a/src/snark/libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/examples/run_r1cs_ppzksnark.tcc b/src/snark/libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/examples/run_r1cs_ppzksnark.tcc new file mode 100644 index 000000000..00af6fe25 --- /dev/null +++ b/src/snark/libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/examples/run_r1cs_ppzksnark.tcc @@ -0,0 +1,114 @@ +/** @file + ***************************************************************************** + + Implementation of functionality that runs the R1CS ppzkSNARK for + a given R1CS example. + + See run_r1cs_ppzksnark.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef RUN_R1CS_PPZKSNARK_TCC_ +#define RUN_R1CS_PPZKSNARK_TCC_ + +#include "zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp" + +#include +#include + +#include "common/profiling.hpp" + +namespace libsnark { + +template +typename std::enable_if::type +test_affine_verifier(const r1cs_ppzksnark_verification_key &vk, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof, + const bool expected_answer) +{ + print_header("R1CS ppzkSNARK Affine Verifier"); + const bool answer = r1cs_ppzksnark_affine_verifier_weak_IC(vk, primary_input, proof); + assert(answer == expected_answer); +} + +template +typename std::enable_if::type +test_affine_verifier(const r1cs_ppzksnark_verification_key &vk, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof, + const bool expected_answer) +{ + UNUSED(vk, primary_input, proof, expected_answer); + print_header("R1CS ppzkSNARK Affine Verifier"); + printf("Affine verifier is not supported; not testing anything.\n"); +} + +/** + * The code below provides an example of all stages of running a R1CS ppzkSNARK. + * + * Of course, in a real-life scenario, we would have three distinct entities, + * mangled into one in the demonstration below. The three entities are as follows. + * (1) The "generator", which runs the ppzkSNARK generator on input a given + * constraint system CS to create a proving and a verification key for CS. + * (2) The "prover", which runs the ppzkSNARK prover on input the proving key, + * a primary input for CS, and an auxiliary input for CS. + * (3) The "verifier", which runs the ppzkSNARK verifier on input the verification key, + * a primary input for CS, and a proof. + */ +template +bool run_r1cs_ppzksnark(const r1cs_example > &example, + const bool test_serialization) +{ + enter_block("Call to run_r1cs_ppzksnark"); + + print_header("R1CS ppzkSNARK Generator"); + r1cs_ppzksnark_keypair keypair = r1cs_ppzksnark_generator(example.constraint_system); + printf("\n"); print_indent(); print_mem("after generator"); + + print_header("Preprocess verification key"); + r1cs_ppzksnark_processed_verification_key pvk = r1cs_ppzksnark_verifier_process_vk(keypair.vk); + + if (test_serialization) + { + enter_block("Test serialization of keys"); + keypair.pk = reserialize >(keypair.pk); + keypair.vk = reserialize >(keypair.vk); + pvk = reserialize >(pvk); + leave_block("Test serialization of keys"); + } + + print_header("R1CS ppzkSNARK Prover"); + r1cs_ppzksnark_proof proof = r1cs_ppzksnark_prover(keypair.pk, example.primary_input, example.auxiliary_input, example.constraint_system); + printf("\n"); print_indent(); print_mem("after prover"); + + if (test_serialization) + { + enter_block("Test serialization of proof"); + proof = reserialize >(proof); + leave_block("Test serialization of proof"); + } + + print_header("R1CS ppzkSNARK Verifier"); + const bool ans = r1cs_ppzksnark_verifier_strong_IC(keypair.vk, example.primary_input, proof); + printf("\n"); print_indent(); print_mem("after verifier"); + printf("* The verification result is: %s\n", (ans ? "PASS" : "FAIL")); + + print_header("R1CS ppzkSNARK Online Verifier"); + const bool ans2 = r1cs_ppzksnark_online_verifier_strong_IC(pvk, example.primary_input, proof); + assert(ans == ans2); + + test_affine_verifier(keypair.vk, example.primary_input, proof, ans); + + leave_block("Call to run_r1cs_ppzksnark"); + + return ans; +} + +} // libsnark + +#endif // RUN_R1CS_PPZKSNARK_TCC_ diff --git a/src/snark/libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/profiling/profile_r1cs_ppzksnark.cpp b/src/snark/libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/profiling/profile_r1cs_ppzksnark.cpp new file mode 100644 index 000000000..5c5415028 --- /dev/null +++ b/src/snark/libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/profiling/profile_r1cs_ppzksnark.cpp @@ -0,0 +1,71 @@ +/** @file + ***************************************************************************** + Profiling program that exercises the ppzkSNARK (first generator, then prover, + then verifier) on a synthetic R1CS instance. + + The command + + $ src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/profiling/profile_r1cs_ppzksnark 1000 10 Fr + + exercises the ppzkSNARK (first generator, then prover, then verifier) on an R1CS instance with 1000 equations and an input consisting of 10 field elements. + + (If you get the error `zmInit ERR:can't protect`, see the discussion [above](#elliptic-curve-choices).) + + The command + + $ src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/profiling/profile_r1cs_ppzksnark 1000 10 bytes + + does the same but now the input consists of 10 bytes. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ +#include +#include + +#include "common/default_types/r1cs_ppzksnark_pp.hpp" +#include "common/profiling.hpp" +#include "common/utils.hpp" +#include "relations/constraint_satisfaction_problems/r1cs/examples/r1cs_examples.hpp" +#include "zk_proof_systems/ppzksnark/r1cs_ppzksnark/examples/run_r1cs_ppzksnark.hpp" + +using namespace libsnark; + +int main(int argc, const char * argv[]) +{ + default_r1cs_ppzksnark_pp::init_public_params(); + start_profiling(); + + if (argc == 2 && strcmp(argv[1], "-v") == 0) + { + print_compilation_info(); + return 0; + } + + if (argc != 3 && argc != 4) + { + printf("usage: %s num_constraints input_size [Fr|bytes]\n", argv[0]); + return 1; + } + const int num_constraints = atoi(argv[1]); + int input_size = atoi(argv[2]); + if (argc == 4) + { + assert(strcmp(argv[3], "Fr") == 0 || strcmp(argv[3], "bytes") == 0); + if (strcmp(argv[3], "bytes") == 0) + { + input_size = div_ceil(8 * input_size, Fr::capacity()); + } + } + + enter_block("Generate R1CS example"); + r1cs_example > example = generate_r1cs_example_with_field_input >(num_constraints, input_size); + leave_block("Generate R1CS example"); + + print_header("(enter) Profile R1CS ppzkSNARK"); + const bool test_serialization = true; + run_r1cs_ppzksnark(example, test_serialization); + print_header("(leave) Profile R1CS ppzkSNARK"); +} diff --git a/src/snark/libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp b/src/snark/libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp new file mode 100644 index 000000000..6095d6a08 --- /dev/null +++ b/src/snark/libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp @@ -0,0 +1,486 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for a ppzkSNARK for R1CS. + + This includes: + - class for proving key + - class for verification key + - class for processed verification key + - class for key pair (proving key & verification key) + - class for proof + - generator algorithm + - prover algorithm + - verifier algorithm (with strong or weak input consistency) + - online verifier algorithm (with strong or weak input consistency) + + The implementation instantiates (a modification of) the protocol of \[PGHR13], + by following extending, and optimizing the approach described in \[BCTV14]. + + + Acronyms: + + - R1CS = "Rank-1 Constraint Systems" + - ppzkSNARK = "PreProcessing Zero-Knowledge Succinct Non-interactive ARgument of Knowledge" + + References: + + \[BCTV14]: + "Succinct Non-Interactive Zero Knowledge for a von Neumann Architecture", + Eli Ben-Sasson, Alessandro Chiesa, Eran Tromer, Madars Virza, + USENIX Security 2014, + + + \[PGHR13]: + "Pinocchio: Nearly practical verifiable computation", + Bryan Parno, Craig Gentry, Jon Howell, Mariana Raykova, + IEEE S&P 2013, + + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef R1CS_PPZKSNARK_HPP_ +#define R1CS_PPZKSNARK_HPP_ + +#include + +#include "algebra/curves/public_params.hpp" +#include "common/data_structures/accumulation_vector.hpp" +#include "algebra/knowledge_commitment/knowledge_commitment.hpp" +#include "relations/constraint_satisfaction_problems/r1cs/r1cs.hpp" +#include "zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark_params.hpp" + +namespace libsnark { + +/******************************** Proving key ********************************/ + +template +class r1cs_ppzksnark_proving_key; + +template +std::ostream& operator<<(std::ostream &out, const r1cs_ppzksnark_proving_key &pk); + +template +std::istream& operator>>(std::istream &in, r1cs_ppzksnark_proving_key &pk); + +/** + * A proving key for the R1CS ppzkSNARK. + */ +template +class r1cs_ppzksnark_proving_key { +public: + knowledge_commitment_vector, G1 > A_query; + knowledge_commitment_vector, G1 > B_query; + knowledge_commitment_vector, G1 > C_query; + G1_vector H_query; + G1_vector K_query; + + r1cs_ppzksnark_proving_key() {}; + r1cs_ppzksnark_proving_key& operator=(const r1cs_ppzksnark_proving_key &other) = default; + r1cs_ppzksnark_proving_key(const r1cs_ppzksnark_proving_key &other) = default; + r1cs_ppzksnark_proving_key(r1cs_ppzksnark_proving_key &&other) = default; + r1cs_ppzksnark_proving_key(knowledge_commitment_vector, G1 > &&A_query, + knowledge_commitment_vector, G1 > &&B_query, + knowledge_commitment_vector, G1 > &&C_query, + G1_vector &&H_query, + G1_vector &&K_query) : + A_query(std::move(A_query)), + B_query(std::move(B_query)), + C_query(std::move(C_query)), + H_query(std::move(H_query)), + K_query(std::move(K_query)) + {}; + + size_t G1_size() const + { + return 2*(A_query.domain_size() + C_query.domain_size()) + B_query.domain_size() + H_query.size() + K_query.size(); + } + + size_t G2_size() const + { + return B_query.domain_size(); + } + + size_t G1_sparse_size() const + { + return 2*(A_query.size() + C_query.size()) + B_query.size() + H_query.size() + K_query.size(); + } + + size_t G2_sparse_size() const + { + return B_query.size(); + } + + size_t size_in_bits() const + { + return A_query.size_in_bits() + B_query.size_in_bits() + C_query.size_in_bits() + libsnark::size_in_bits(H_query) + libsnark::size_in_bits(K_query); + } + + void print_size() const + { + print_indent(); printf("* G1 elements in PK: %zu\n", this->G1_size()); + print_indent(); printf("* Non-zero G1 elements in PK: %zu\n", this->G1_sparse_size()); + print_indent(); printf("* G2 elements in PK: %zu\n", this->G2_size()); + print_indent(); printf("* Non-zero G2 elements in PK: %zu\n", this->G2_sparse_size()); + print_indent(); printf("* PK size in bits: %zu\n", this->size_in_bits()); + } + + bool operator==(const r1cs_ppzksnark_proving_key &other) const; + friend std::ostream& operator<< (std::ostream &out, const r1cs_ppzksnark_proving_key &pk); + friend std::istream& operator>> (std::istream &in, r1cs_ppzksnark_proving_key &pk); +}; + + +/******************************* Verification key ****************************/ + +template +class r1cs_ppzksnark_verification_key; + +template +std::ostream& operator<<(std::ostream &out, const r1cs_ppzksnark_verification_key &vk); + +template +std::istream& operator>>(std::istream &in, r1cs_ppzksnark_verification_key &vk); + +/** + * A verification key for the R1CS ppzkSNARK. + */ +template +class r1cs_ppzksnark_verification_key { +public: + G2 alphaA_g2; + G1 alphaB_g1; + G2 alphaC_g2; + G2 gamma_g2; + G1 gamma_beta_g1; + G2 gamma_beta_g2; + G2 rC_Z_g2; + + accumulation_vector > encoded_IC_query; + + r1cs_ppzksnark_verification_key() = default; + r1cs_ppzksnark_verification_key(const G2 &alphaA_g2, + const G1 &alphaB_g1, + const G2 &alphaC_g2, + const G2 &gamma_g2, + const G1 &gamma_beta_g1, + const G2 &gamma_beta_g2, + const G2 &rC_Z_g2, + const accumulation_vector > &eIC) : + alphaA_g2(alphaA_g2), + alphaB_g1(alphaB_g1), + alphaC_g2(alphaC_g2), + gamma_g2(gamma_g2), + gamma_beta_g1(gamma_beta_g1), + gamma_beta_g2(gamma_beta_g2), + rC_Z_g2(rC_Z_g2), + encoded_IC_query(eIC) + {}; + + size_t G1_size() const + { + return 2 + encoded_IC_query.size(); + } + + size_t G2_size() const + { + return 5; + } + + size_t size_in_bits() const + { + return (2 * G1::size_in_bits() + encoded_IC_query.size_in_bits() + 5 * G2::size_in_bits()); + } + + void print_size() const + { + print_indent(); printf("* G1 elements in VK: %zu\n", this->G1_size()); + print_indent(); printf("* G2 elements in VK: %zu\n", this->G2_size()); + print_indent(); printf("* VK size in bits: %zu\n", this->size_in_bits()); + } + + bool operator==(const r1cs_ppzksnark_verification_key &other) const; + friend std::ostream& operator<< (std::ostream &out, const r1cs_ppzksnark_verification_key &vk); + friend std::istream& operator>> (std::istream &in, r1cs_ppzksnark_verification_key &vk); + + static r1cs_ppzksnark_verification_key dummy_verification_key(const size_t input_size); +}; + + +/************************ Processed verification key *************************/ + +template +class r1cs_ppzksnark_processed_verification_key; + +template +std::ostream& operator<<(std::ostream &out, const r1cs_ppzksnark_processed_verification_key &pvk); + +template +std::istream& operator>>(std::istream &in, r1cs_ppzksnark_processed_verification_key &pvk); + +/** + * A processed verification key for the R1CS ppzkSNARK. + * + * Compared to a (non-processed) verification key, a processed verification key + * contains a small constant amount of additional pre-computed information that + * enables a faster verification time. + */ +template +class r1cs_ppzksnark_processed_verification_key { +public: + G2_precomp pp_G2_one_precomp; + G2_precomp vk_alphaA_g2_precomp; + G1_precomp vk_alphaB_g1_precomp; + G2_precomp vk_alphaC_g2_precomp; + G2_precomp vk_rC_Z_g2_precomp; + G2_precomp vk_gamma_g2_precomp; + G1_precomp vk_gamma_beta_g1_precomp; + G2_precomp vk_gamma_beta_g2_precomp; + + accumulation_vector > encoded_IC_query; + + bool operator==(const r1cs_ppzksnark_processed_verification_key &other) const; + friend std::ostream& operator<< (std::ostream &out, const r1cs_ppzksnark_processed_verification_key &pvk); + friend std::istream& operator>> (std::istream &in, r1cs_ppzksnark_processed_verification_key &pvk); +}; + + +/********************************** Key pair *********************************/ + +/** + * A key pair for the R1CS ppzkSNARK, which consists of a proving key and a verification key. + */ +template +class r1cs_ppzksnark_keypair { +public: + r1cs_ppzksnark_proving_key pk; + r1cs_ppzksnark_verification_key vk; + + r1cs_ppzksnark_keypair() = default; + r1cs_ppzksnark_keypair(const r1cs_ppzksnark_keypair &other) = default; + r1cs_ppzksnark_keypair(r1cs_ppzksnark_proving_key &&pk, + r1cs_ppzksnark_verification_key &&vk) : + pk(std::move(pk)), + vk(std::move(vk)) + {} + + r1cs_ppzksnark_keypair(r1cs_ppzksnark_keypair &&other) = default; +}; + + +/*********************************** Proof ***********************************/ + +template +class r1cs_ppzksnark_proof; + +template +std::ostream& operator<<(std::ostream &out, const r1cs_ppzksnark_proof &proof); + +template +std::istream& operator>>(std::istream &in, r1cs_ppzksnark_proof &proof); + +/** + * A proof for the R1CS ppzkSNARK. + * + * While the proof has a structure, externally one merely opaquely produces, + * serializes/deserializes, and verifies proofs. We only expose some information + * about the structure for statistics purposes. + */ +template +class r1cs_ppzksnark_proof { +public: + knowledge_commitment, G1 > g_A; + knowledge_commitment, G1 > g_B; + knowledge_commitment, G1 > g_C; + G1 g_H; + G1 g_K; + + r1cs_ppzksnark_proof() + { + // invalid proof with valid curve points + this->g_A.g = G1 ::one(); + this->g_A.h = G1::one(); + this->g_B.g = G2 ::one(); + this->g_B.h = G1::one(); + this->g_C.g = G1 ::one(); + this->g_C.h = G1::one(); + this->g_H = G1::one(); + this->g_K = G1::one(); + } + r1cs_ppzksnark_proof(knowledge_commitment, G1 > &&g_A, + knowledge_commitment, G1 > &&g_B, + knowledge_commitment, G1 > &&g_C, + G1 &&g_H, + G1 &&g_K) : + g_A(std::move(g_A)), + g_B(std::move(g_B)), + g_C(std::move(g_C)), + g_H(std::move(g_H)), + g_K(std::move(g_K)) + {}; + + size_t G1_size() const + { + return 7; + } + + size_t G2_size() const + { + return 1; + } + + size_t size_in_bits() const + { + return G1_size() * G1::size_in_bits() + G2_size() * G2::size_in_bits(); + } + + void print_size() const + { + print_indent(); printf("* G1 elements in proof: %zu\n", this->G1_size()); + print_indent(); printf("* G2 elements in proof: %zu\n", this->G2_size()); + print_indent(); printf("* Proof size in bits: %zu\n", this->size_in_bits()); + } + + bool is_well_formed() const + { + return (g_A.g.is_well_formed() && g_A.h.is_well_formed() && + g_B.g.is_well_formed() && g_B.h.is_well_formed() && + g_C.g.is_well_formed() && g_C.h.is_well_formed() && + g_H.is_well_formed() && + g_K.is_well_formed()); + } + + bool operator==(const r1cs_ppzksnark_proof &other) const; + friend std::ostream& operator<< (std::ostream &out, const r1cs_ppzksnark_proof &proof); + friend std::istream& operator>> (std::istream &in, r1cs_ppzksnark_proof &proof); +}; + + +/***************************** Main algorithms *******************************/ + +/** + * A generator algorithm for the R1CS ppzkSNARK. + * + * Given a R1CS constraint system CS, this algorithm produces proving and verification keys for CS. + */ +template +r1cs_ppzksnark_keypair r1cs_ppzksnark_generator(const r1cs_ppzksnark_constraint_system &cs); + +template +r1cs_ppzksnark_keypair r1cs_ppzksnark_generator( + const r1cs_ppzksnark_constraint_system &cs, + const Fr& t, + const Fr& alphaA, + const Fr& alphaB, + const Fr& alphaC, + const Fr& rA, + const Fr& rB, + const Fr& beta, + const Fr& gamma +); + +/** + * A prover algorithm for the R1CS ppzkSNARK. + * + * Given a R1CS primary input X and a R1CS auxiliary input Y, this algorithm + * produces a proof (of knowledge) that attests to the following statement: + * ``there exists Y such that CS(X,Y)=0''. + * Above, CS is the R1CS constraint system that was given as input to the generator algorithm. + */ +template +r1cs_ppzksnark_proof r1cs_ppzksnark_prover(const r1cs_ppzksnark_proving_key &pk, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_auxiliary_input &auxiliary_input, + const r1cs_ppzksnark_constraint_system &constraint_system); + +template +r1cs_ppzksnark_proof r1cs_ppzksnark_prover_streaming(std::ifstream &proving_key_file, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_auxiliary_input &auxiliary_input, + const r1cs_ppzksnark_constraint_system &constraint_system); + +/* + Below are four variants of verifier algorithm for the R1CS ppzkSNARK. + + These are the four cases that arise from the following two choices: + + (1) The verifier accepts a (non-processed) verification key or, instead, a processed verification key. + In the latter case, we call the algorithm an "online verifier". + + (2) The verifier checks for "weak" input consistency or, instead, "strong" input consistency. + Strong input consistency requires that |primary_input| = CS.num_inputs, whereas + weak input consistency requires that |primary_input| <= CS.num_inputs (and + the primary input is implicitly padded with zeros up to length CS.num_inputs). + */ + +/** + * A verifier algorithm for the R1CS ppzkSNARK that: + * (1) accepts a non-processed verification key, and + * (2) has weak input consistency. + */ +template +bool r1cs_ppzksnark_verifier_weak_IC(const r1cs_ppzksnark_verification_key &vk, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof); + +/** + * A verifier algorithm for the R1CS ppzkSNARK that: + * (1) accepts a non-processed verification key, and + * (2) has strong input consistency. + */ +template +bool r1cs_ppzksnark_verifier_strong_IC(const r1cs_ppzksnark_verification_key &vk, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof); + +/** + * Convert a (non-processed) verification key into a processed verification key. + */ +template +r1cs_ppzksnark_processed_verification_key r1cs_ppzksnark_verifier_process_vk(const r1cs_ppzksnark_verification_key &vk); + +/** + * A verifier algorithm for the R1CS ppzkSNARK that: + * (1) accepts a processed verification key, and + * (2) has weak input consistency. + */ +template +bool r1cs_ppzksnark_online_verifier_weak_IC(const r1cs_ppzksnark_processed_verification_key &pvk, + const r1cs_ppzksnark_primary_input &input, + const r1cs_ppzksnark_proof &proof); + +/** + * A verifier algorithm for the R1CS ppzkSNARK that: + * (1) accepts a processed verification key, and + * (2) has strong input consistency. + */ +template +bool r1cs_ppzksnark_online_verifier_strong_IC(const r1cs_ppzksnark_processed_verification_key &pvk, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof); + +/****************************** Miscellaneous ********************************/ + +/** + * For debugging purposes (of r1cs_ppzksnark_r1cs_ppzksnark_verifier_gadget): + * + * A verifier algorithm for the R1CS ppzkSNARK that: + * (1) accepts a non-processed verification key, + * (2) has weak input consistency, and + * (3) uses affine coordinates for elliptic-curve computations. + */ +template +bool r1cs_ppzksnark_affine_verifier_weak_IC(const r1cs_ppzksnark_verification_key &vk, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof); + + +} // libsnark + +#include "zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc" + +#endif // R1CS_PPZKSNARK_HPP_ diff --git a/src/snark/libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc b/src/snark/libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc new file mode 100644 index 000000000..9cc392e53 --- /dev/null +++ b/src/snark/libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc @@ -0,0 +1,886 @@ +/** @file +***************************************************************************** + +Implementation of interfaces for a ppzkSNARK for R1CS. + +See r1cs_ppzksnark.hpp . + +***************************************************************************** +* @author This file is part of libsnark, developed by SCIPR Lab +* and contributors (see AUTHORS). +* @copyright MIT license (see LICENSE file) +*****************************************************************************/ + +#ifndef R1CS_PPZKSNARK_TCC_ +#define R1CS_PPZKSNARK_TCC_ + +#include +#include +#include +#include +#include + +#include "common/profiling.hpp" +#include "common/utils.hpp" +#include "algebra/scalar_multiplication/multiexp.hpp" +#include "algebra/scalar_multiplication/kc_multiexp.hpp" +#include "reductions/r1cs_to_qap/r1cs_to_qap.hpp" + +namespace libsnark { + +template +bool r1cs_ppzksnark_proving_key::operator==(const r1cs_ppzksnark_proving_key &other) const +{ + return (this->A_query == other.A_query && + this->B_query == other.B_query && + this->C_query == other.C_query && + this->H_query == other.H_query && + this->K_query == other.K_query); +} + +template +std::ostream& operator<<(std::ostream &out, const r1cs_ppzksnark_proving_key &pk) +{ + out << pk.A_query; + out << pk.B_query; + out << pk.C_query; + out << pk.H_query; + out << pk.K_query; + + return out; +} + +template +std::istream& operator>>(std::istream &in, r1cs_ppzksnark_proving_key &pk) +{ + in >> pk.A_query; + in >> pk.B_query; + in >> pk.C_query; + in >> pk.H_query; + in >> pk.K_query; + + return in; +} + +template +bool r1cs_ppzksnark_verification_key::operator==(const r1cs_ppzksnark_verification_key &other) const +{ + return (this->alphaA_g2 == other.alphaA_g2 && + this->alphaB_g1 == other.alphaB_g1 && + this->alphaC_g2 == other.alphaC_g2 && + this->gamma_g2 == other.gamma_g2 && + this->gamma_beta_g1 == other.gamma_beta_g1 && + this->gamma_beta_g2 == other.gamma_beta_g2 && + this->rC_Z_g2 == other.rC_Z_g2 && + this->encoded_IC_query == other.encoded_IC_query); +} + +template +std::ostream& operator<<(std::ostream &out, const r1cs_ppzksnark_verification_key &vk) +{ + out << vk.alphaA_g2 << OUTPUT_NEWLINE; + out << vk.alphaB_g1 << OUTPUT_NEWLINE; + out << vk.alphaC_g2 << OUTPUT_NEWLINE; + out << vk.gamma_g2 << OUTPUT_NEWLINE; + out << vk.gamma_beta_g1 << OUTPUT_NEWLINE; + out << vk.gamma_beta_g2 << OUTPUT_NEWLINE; + out << vk.rC_Z_g2 << OUTPUT_NEWLINE; + out << vk.encoded_IC_query << OUTPUT_NEWLINE; + + return out; +} + +template +std::istream& operator>>(std::istream &in, r1cs_ppzksnark_verification_key &vk) +{ + in >> vk.alphaA_g2; + consume_OUTPUT_NEWLINE(in); + in >> vk.alphaB_g1; + consume_OUTPUT_NEWLINE(in); + in >> vk.alphaC_g2; + consume_OUTPUT_NEWLINE(in); + in >> vk.gamma_g2; + consume_OUTPUT_NEWLINE(in); + in >> vk.gamma_beta_g1; + consume_OUTPUT_NEWLINE(in); + in >> vk.gamma_beta_g2; + consume_OUTPUT_NEWLINE(in); + in >> vk.rC_Z_g2; + consume_OUTPUT_NEWLINE(in); + in >> vk.encoded_IC_query; + consume_OUTPUT_NEWLINE(in); + + return in; +} + +template +bool r1cs_ppzksnark_processed_verification_key::operator==(const r1cs_ppzksnark_processed_verification_key &other) const +{ + return (this->pp_G2_one_precomp == other.pp_G2_one_precomp && + this->vk_alphaA_g2_precomp == other.vk_alphaA_g2_precomp && + this->vk_alphaB_g1_precomp == other.vk_alphaB_g1_precomp && + this->vk_alphaC_g2_precomp == other.vk_alphaC_g2_precomp && + this->vk_rC_Z_g2_precomp == other.vk_rC_Z_g2_precomp && + this->vk_gamma_g2_precomp == other.vk_gamma_g2_precomp && + this->vk_gamma_beta_g1_precomp == other.vk_gamma_beta_g1_precomp && + this->vk_gamma_beta_g2_precomp == other.vk_gamma_beta_g2_precomp && + this->encoded_IC_query == other.encoded_IC_query); +} + +template +std::ostream& operator<<(std::ostream &out, const r1cs_ppzksnark_processed_verification_key &pvk) +{ + out << pvk.pp_G2_one_precomp << OUTPUT_NEWLINE; + out << pvk.vk_alphaA_g2_precomp << OUTPUT_NEWLINE; + out << pvk.vk_alphaB_g1_precomp << OUTPUT_NEWLINE; + out << pvk.vk_alphaC_g2_precomp << OUTPUT_NEWLINE; + out << pvk.vk_rC_Z_g2_precomp << OUTPUT_NEWLINE; + out << pvk.vk_gamma_g2_precomp << OUTPUT_NEWLINE; + out << pvk.vk_gamma_beta_g1_precomp << OUTPUT_NEWLINE; + out << pvk.vk_gamma_beta_g2_precomp << OUTPUT_NEWLINE; + out << pvk.encoded_IC_query << OUTPUT_NEWLINE; + + return out; +} + +template +std::istream& operator>>(std::istream &in, r1cs_ppzksnark_processed_verification_key &pvk) +{ + in >> pvk.pp_G2_one_precomp; + consume_OUTPUT_NEWLINE(in); + in >> pvk.vk_alphaA_g2_precomp; + consume_OUTPUT_NEWLINE(in); + in >> pvk.vk_alphaB_g1_precomp; + consume_OUTPUT_NEWLINE(in); + in >> pvk.vk_alphaC_g2_precomp; + consume_OUTPUT_NEWLINE(in); + in >> pvk.vk_rC_Z_g2_precomp; + consume_OUTPUT_NEWLINE(in); + in >> pvk.vk_gamma_g2_precomp; + consume_OUTPUT_NEWLINE(in); + in >> pvk.vk_gamma_beta_g1_precomp; + consume_OUTPUT_NEWLINE(in); + in >> pvk.vk_gamma_beta_g2_precomp; + consume_OUTPUT_NEWLINE(in); + in >> pvk.encoded_IC_query; + consume_OUTPUT_NEWLINE(in); + + return in; +} + +template +bool r1cs_ppzksnark_proof::operator==(const r1cs_ppzksnark_proof &other) const +{ + return (this->g_A == other.g_A && + this->g_B == other.g_B && + this->g_C == other.g_C && + this->g_H == other.g_H && + this->g_K == other.g_K); +} + +template +std::ostream& operator<<(std::ostream &out, const r1cs_ppzksnark_proof &proof) +{ + out << proof.g_A << OUTPUT_NEWLINE; + out << proof.g_B << OUTPUT_NEWLINE; + out << proof.g_C << OUTPUT_NEWLINE; + out << proof.g_H << OUTPUT_NEWLINE; + out << proof.g_K << OUTPUT_NEWLINE; + + return out; +} + +template +std::istream& operator>>(std::istream &in, r1cs_ppzksnark_proof &proof) +{ + in >> proof.g_A; + consume_OUTPUT_NEWLINE(in); + in >> proof.g_B; + consume_OUTPUT_NEWLINE(in); + in >> proof.g_C; + consume_OUTPUT_NEWLINE(in); + in >> proof.g_H; + consume_OUTPUT_NEWLINE(in); + in >> proof.g_K; + consume_OUTPUT_NEWLINE(in); + + return in; +} + +template +r1cs_ppzksnark_verification_key r1cs_ppzksnark_verification_key::dummy_verification_key(const size_t input_size) +{ + r1cs_ppzksnark_verification_key result; + result.alphaA_g2 = Fr::random_element() * G2::one(); + result.alphaB_g1 = Fr::random_element() * G1::one(); + result.alphaC_g2 = Fr::random_element() * G2::one(); + result.gamma_g2 = Fr::random_element() * G2::one(); + result.gamma_beta_g1 = Fr::random_element() * G1::one(); + result.gamma_beta_g2 = Fr::random_element() * G2::one(); + result.rC_Z_g2 = Fr::random_element() * G2::one(); + + G1 base = Fr::random_element() * G1::one(); + G1_vector v; + for (size_t i = 0; i < input_size; ++i) + { + v.emplace_back(Fr::random_element() * G1::one()); + } + + result.encoded_IC_query = accumulation_vector >(std::move(base), std::move(v)); + + return result; +} + +template +r1cs_ppzksnark_keypair r1cs_ppzksnark_generator(const r1cs_ppzksnark_constraint_system &cs) +{ + /* draw random element at which the QAP is evaluated */ + const Fr t = Fr::random_element(); + + const Fr alphaA = Fr::random_element(), + alphaB = Fr::random_element(), + alphaC = Fr::random_element(), + rA = Fr::random_element(), + rB = Fr::random_element(), + beta = Fr::random_element(), + gamma = Fr::random_element(); + + return r1cs_ppzksnark_generator(cs, t, alphaA, alphaB, alphaC, rA, rB, beta, gamma); +} + +template +r1cs_ppzksnark_keypair r1cs_ppzksnark_generator( + const r1cs_ppzksnark_constraint_system &cs, + const Fr& t, + const Fr& alphaA, + const Fr& alphaB, + const Fr& alphaC, + const Fr& rA, + const Fr& rB, + const Fr& beta, + const Fr& gamma +) +{ + enter_block("Call to r1cs_ppzksnark_generator"); + + /* make the B_query "lighter" if possible */ + r1cs_ppzksnark_constraint_system cs_copy(cs); + cs_copy.swap_AB_if_beneficial(); + + qap_instance_evaluation > qap_inst = r1cs_to_qap_instance_map_with_evaluation(cs_copy, t); + + print_indent(); printf("* QAP number of variables: %zu\n", qap_inst.num_variables()); + print_indent(); printf("* QAP pre degree: %zu\n", cs_copy.constraints.size()); + print_indent(); printf("* QAP degree: %zu\n", qap_inst.degree()); + print_indent(); printf("* QAP number of input variables: %zu\n", qap_inst.num_inputs()); + + enter_block("Compute query densities"); + size_t non_zero_At = 0, non_zero_Bt = 0, non_zero_Ct = 0, non_zero_Ht = 0; + for (size_t i = 0; i < qap_inst.num_variables()+1; ++i) + { + if (!qap_inst.At[i].is_zero()) + { + ++non_zero_At; + } + if (!qap_inst.Bt[i].is_zero()) + { + ++non_zero_Bt; + } + if (!qap_inst.Ct[i].is_zero()) + { + ++non_zero_Ct; + } + } + for (size_t i = 0; i < qap_inst.degree()+1; ++i) + { + if (!qap_inst.Ht[i].is_zero()) + { + ++non_zero_Ht; + } + } + leave_block("Compute query densities"); + + Fr_vector At = std::move(qap_inst.At); // qap_inst.At is now in unspecified state, but we do not use it later + Fr_vector Bt = std::move(qap_inst.Bt); // qap_inst.Bt is now in unspecified state, but we do not use it later + Fr_vector Ct = std::move(qap_inst.Ct); // qap_inst.Ct is now in unspecified state, but we do not use it later + Fr_vector Ht = std::move(qap_inst.Ht); // qap_inst.Ht is now in unspecified state, but we do not use it later + + /* append Zt to At,Bt,Ct with */ + At.emplace_back(qap_inst.Zt); + Bt.emplace_back(qap_inst.Zt); + Ct.emplace_back(qap_inst.Zt); + + const Fr rC = rA * rB; + + // construct the same-coefficient-check query (must happen before zeroing out the prefix of At) + Fr_vector Kt; + Kt.reserve(qap_inst.num_variables()+4); + for (size_t i = 0; i < qap_inst.num_variables()+1; ++i) + { + Kt.emplace_back( beta * (rA * At[i] + rB * Bt[i] + rC * Ct[i] ) ); + } + Kt.emplace_back(beta * rA * qap_inst.Zt); + Kt.emplace_back(beta * rB * qap_inst.Zt); + Kt.emplace_back(beta * rC * qap_inst.Zt); + + /* zero out prefix of At and stick it into IC coefficients */ + Fr_vector IC_coefficients; + IC_coefficients.reserve(qap_inst.num_inputs() + 1); + for (size_t i = 0; i < qap_inst.num_inputs() + 1; ++i) + { + IC_coefficients.emplace_back(At[i]); + assert(!IC_coefficients[i].is_zero()); + At[i] = Fr::zero(); + } + + const size_t g1_exp_count = 2*(non_zero_At - qap_inst.num_inputs() + non_zero_Ct) + non_zero_Bt + non_zero_Ht + Kt.size(); + const size_t g2_exp_count = non_zero_Bt; + + size_t g1_window = get_exp_window_size >(g1_exp_count); + size_t g2_window = get_exp_window_size >(g2_exp_count); + print_indent(); printf("* G1 window: %zu\n", g1_window); + print_indent(); printf("* G2 window: %zu\n", g2_window); + +#ifdef MULTICORE + const size_t chunks = omp_get_max_threads(); // to override, set OMP_NUM_THREADS env var or call omp_set_num_threads() +#else + const size_t chunks = 1; +#endif + + enter_block("Generating G1 multiexp table"); + window_table > g1_table = get_window_table(Fr::size_in_bits(), g1_window, G1::one()); + leave_block("Generating G1 multiexp table"); + + enter_block("Generating G2 multiexp table"); + window_table > g2_table = get_window_table(Fr::size_in_bits(), g2_window, G2::one()); + leave_block("Generating G2 multiexp table"); + + enter_block("Generate R1CS proving key"); + + enter_block("Generate knowledge commitments"); + enter_block("Compute the A-query", false); + knowledge_commitment_vector, G1 > A_query = kc_batch_exp(Fr::size_in_bits(), g1_window, g1_window, g1_table, g1_table, rA, rA*alphaA, At, chunks); + leave_block("Compute the A-query", false); + + enter_block("Compute the B-query", false); + knowledge_commitment_vector, G1 > B_query = kc_batch_exp(Fr::size_in_bits(), g2_window, g1_window, g2_table, g1_table, rB, rB*alphaB, Bt, chunks); + leave_block("Compute the B-query", false); + + enter_block("Compute the C-query", false); + knowledge_commitment_vector, G1 > C_query = kc_batch_exp(Fr::size_in_bits(), g1_window, g1_window, g1_table, g1_table, rC, rC*alphaC, Ct, chunks); + leave_block("Compute the C-query", false); + + enter_block("Compute the H-query", false); + G1_vector H_query = batch_exp(Fr::size_in_bits(), g1_window, g1_table, Ht); + leave_block("Compute the H-query", false); + + enter_block("Compute the K-query", false); + G1_vector K_query = batch_exp(Fr::size_in_bits(), g1_window, g1_table, Kt); +#ifdef USE_MIXED_ADDITION + batch_to_special >(K_query); +#endif + leave_block("Compute the K-query", false); + + leave_block("Generate knowledge commitments"); + + leave_block("Generate R1CS proving key"); + + enter_block("Generate R1CS verification key"); + G2 alphaA_g2 = alphaA * G2::one(); + G1 alphaB_g1 = alphaB * G1::one(); + G2 alphaC_g2 = alphaC * G2::one(); + G2 gamma_g2 = gamma * G2::one(); + G1 gamma_beta_g1 = (gamma * beta) * G1::one(); + G2 gamma_beta_g2 = (gamma * beta) * G2::one(); + G2 rC_Z_g2 = (rC * qap_inst.Zt) * G2::one(); + + enter_block("Encode IC query for R1CS verification key"); + G1 encoded_IC_base = (rA * IC_coefficients[0]) * G1::one(); + Fr_vector multiplied_IC_coefficients; + multiplied_IC_coefficients.reserve(qap_inst.num_inputs()); + for (size_t i = 1; i < qap_inst.num_inputs() + 1; ++i) + { + multiplied_IC_coefficients.emplace_back(rA * IC_coefficients[i]); + } + G1_vector encoded_IC_values = batch_exp(Fr::size_in_bits(), g1_window, g1_table, multiplied_IC_coefficients); + + leave_block("Encode IC query for R1CS verification key"); + leave_block("Generate R1CS verification key"); + + leave_block("Call to r1cs_ppzksnark_generator"); + + accumulation_vector > encoded_IC_query(std::move(encoded_IC_base), std::move(encoded_IC_values)); + + r1cs_ppzksnark_verification_key vk = r1cs_ppzksnark_verification_key(alphaA_g2, + alphaB_g1, + alphaC_g2, + gamma_g2, + gamma_beta_g1, + gamma_beta_g2, + rC_Z_g2, + encoded_IC_query); + r1cs_ppzksnark_proving_key pk = r1cs_ppzksnark_proving_key(std::move(A_query), + std::move(B_query), + std::move(C_query), + std::move(H_query), + std::move(K_query)); + + pk.print_size(); + vk.print_size(); + + return r1cs_ppzksnark_keypair(std::move(pk), std::move(vk)); +} + +template +knowledge_commitment r1cs_compute_proof_kc(const qap_witness > &qap_wit, + const knowledge_commitment_vector &kcv, + const Fr &zk_shift) +{ + knowledge_commitment returnval = kcv[0] + (zk_shift * kcv[qap_wit.num_variables()+1]); + +#ifdef DEBUG + assert(kcv.domain_size() == qap_wit.num_variables()+2); +#endif + +#ifdef MULTICORE + const size_t chunks = omp_get_max_threads(); // to override, set OMP_NUM_THREADS env var or call omp_set_num_threads() +#else + const size_t chunks = 1; +#endif + + returnval = returnval + kc_multi_exp_with_mixed_addition >( + kcv, + 1, + 1 + qap_wit.num_variables(), + qap_wit.coefficients_for_ABCs.begin(), + qap_wit.coefficients_for_ABCs.begin()+qap_wit.num_variables(), + chunks, + true + ); + + return returnval; +} + + + +template +G1 r1cs_compute_proof_K(const qap_witness> &qap_wit, const G1_vector &K_query, const G1 &zk_shift) +{ +#ifdef DEBUG + assert(K_query.size() == qap_wit.num_variables()+4); +#endif + +#ifdef MULTICORE + const size_t chunks = omp_get_max_threads(); // to override, set OMP_NUM_THREADS env var or call omp_set_num_threads() +#else + const size_t chunks = 1; +#endif + + G1 g_K = K_query[0] + zk_shift; + g_K = g_K + multi_exp_with_mixed_addition, Fr >( + K_query.begin()+1, + K_query.begin()+1+qap_wit.num_variables(), + qap_wit.coefficients_for_ABCs.begin(), + qap_wit.coefficients_for_ABCs.begin()+qap_wit.num_variables(), + chunks, + true + ); + + return g_K; +} + + +template +G1 r1cs_compute_proof_H(const qap_witness > &qap_wit, const G1_vector &H_query) +{ + G1 g_H = G1::zero(); + +#ifdef DEBUG + assert(H_query.size() == qap_wit.degree()+1); +#endif + +#ifdef MULTICORE + const size_t chunks = omp_get_max_threads(); // to override, set OMP_NUM_THREADS env var or call omp_set_num_threads() +#else + const size_t chunks = 1; +#endif + + g_H = g_H + multi_exp, Fr >( + H_query.begin(), + H_query.begin()+qap_wit.degree()+1, + qap_wit.coefficients_for_H.begin(), + qap_wit.coefficients_for_H.begin()+qap_wit.degree()+1, + chunks, + true + ); + + return g_H; +} + +template +r1cs_ppzksnark_proof r1cs_ppzksnark_prover(const r1cs_ppzksnark_proving_key &pk, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_auxiliary_input &auxiliary_input, + const r1cs_ppzksnark_constraint_system &constraint_system) +{ + enter_block("Call to r1cs_ppzksnark_prover"); + +#ifdef DEBUG + assert(constraint_system.is_satisfied(primary_input, auxiliary_input)); +#endif + + const Fr d1 = Fr::random_element(), + d2 = Fr::random_element(), + d3 = Fr::random_element(); + + enter_block("Compute the polynomial H"); + const qap_witness > qap_wit = r1cs_to_qap_witness_map(constraint_system, primary_input, auxiliary_input, d1, d2, d3); + leave_block("Compute the polynomial H"); + +#ifdef DEBUG + const Fr t = Fr::random_element(); + qap_instance_evaluation > qap_inst = r1cs_to_qap_instance_map_with_evaluation(constraint_system, t); + assert(qap_inst.is_satisfied(qap_wit)); +#endif + +#ifdef DEBUG + for (size_t i = 0; i < qap_wit.num_inputs() + 1; ++i) + { + assert(pk.A_query[i].g == G1::zero()); + } +#endif + + enter_block("Compute the proof"); + + enter_block("Compute answer to A-query", false); + auto g_A = r1cs_compute_proof_kc, G1 >(qap_wit, pk.A_query, qap_wit.d1); + leave_block("Compute answer to A-query", false); + + enter_block("Compute answer to B-query", false); + auto g_B = r1cs_compute_proof_kc, G1 >(qap_wit, pk.B_query, qap_wit.d2); + leave_block("Compute answer to B-query", false); + + enter_block("Compute answer to C-query", false); + auto g_C = r1cs_compute_proof_kc, G1 >(qap_wit, pk.C_query, qap_wit.d3); + leave_block("Compute answer to C-query", false); + + enter_block("Compute answer to H-query", false); + auto g_H = r1cs_compute_proof_H(qap_wit, pk.H_query); + leave_block("Compute answer to H-query", false); + + enter_block("Compute answer to K-query", false); + G1 zk_shift = qap_wit.d1*pk.K_query[qap_wit.num_variables()+1] + + qap_wit.d2*pk.K_query[qap_wit.num_variables()+2] + + qap_wit.d3*pk.K_query[qap_wit.num_variables()+3]; + G1 g_K = r1cs_compute_proof_K(qap_wit, pk.K_query, zk_shift); + leave_block("Compute answer to K-query", false); + + leave_block("Compute the proof"); + + leave_block("Call to r1cs_ppzksnark_prover"); + + r1cs_ppzksnark_proof proof = r1cs_ppzksnark_proof(std::move(g_A), std::move(g_B), std::move(g_C), std::move(g_H), std::move(g_K)); + + return proof; +} + +template +r1cs_ppzksnark_proof r1cs_ppzksnark_prover_streaming(std::ifstream &proving_key_file, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_auxiliary_input &auxiliary_input, + const r1cs_ppzksnark_constraint_system &constraint_system) +{ + enter_block("Call to r1cs_ppzksnark_prover_streaming"); + + const Fr d1 = Fr::random_element(), + d2 = Fr::random_element(), + d3 = Fr::random_element(); + + enter_block("Compute the polynomial H"); + const qap_witness > qap_wit = r1cs_to_qap_witness_map(constraint_system, primary_input, auxiliary_input, d1, d2, d3); + leave_block("Compute the polynomial H"); + + enter_block("Compute the proof"); + + r1cs_ppzksnark_proof proof; + + enter_block("Compute answer to A-query", false); + { + knowledge_commitment_vector, G1 > A_query; + proving_key_file >> A_query; + proof.g_A = r1cs_compute_proof_kc, G1 >(qap_wit, A_query, qap_wit.d1); + } + leave_block("Compute answer to A-query", false); + + enter_block("Compute answer to B-query", false); + { + knowledge_commitment_vector, G1 > B_query; + proving_key_file >> B_query; + proof.g_B = r1cs_compute_proof_kc, G1 >(qap_wit, B_query, qap_wit.d2); + } + leave_block("Compute answer to B-query", false); + + enter_block("Compute answer to C-query", false); + { + knowledge_commitment_vector, G1 > C_query; + proving_key_file >> C_query; + proof.g_C = r1cs_compute_proof_kc, G1 >(qap_wit, C_query, qap_wit.d3); + } + leave_block("Compute answer to C-query", false); + + enter_block("Compute answer to H-query", false); + { + G1_vector H_query; + proving_key_file >> H_query; + proof.g_H = r1cs_compute_proof_H(qap_wit, H_query); + } + leave_block("Compute answer to H-query", false); + + enter_block("Compute answer to K-query", false); + { + G1_vector K_query; + proving_key_file >> K_query; + G1 zk_shift = qap_wit.d1*K_query[qap_wit.num_variables()+1] + + qap_wit.d2*K_query[qap_wit.num_variables()+2] + + qap_wit.d3*K_query[qap_wit.num_variables()+3]; + proof.g_K = r1cs_compute_proof_K(qap_wit, K_query, zk_shift); + } + leave_block("Compute answer to K-query", false); + + leave_block("Compute the proof"); + + leave_block("Call to r1cs_ppzksnark_prover_streaming"); + + return proof; +} + +template +r1cs_ppzksnark_processed_verification_key r1cs_ppzksnark_verifier_process_vk(const r1cs_ppzksnark_verification_key &vk) +{ + enter_block("Call to r1cs_ppzksnark_verifier_process_vk"); + + r1cs_ppzksnark_processed_verification_key pvk; + pvk.pp_G2_one_precomp = ppT::precompute_G2(G2::one()); + pvk.vk_alphaA_g2_precomp = ppT::precompute_G2(vk.alphaA_g2); + pvk.vk_alphaB_g1_precomp = ppT::precompute_G1(vk.alphaB_g1); + pvk.vk_alphaC_g2_precomp = ppT::precompute_G2(vk.alphaC_g2); + pvk.vk_rC_Z_g2_precomp = ppT::precompute_G2(vk.rC_Z_g2); + pvk.vk_gamma_g2_precomp = ppT::precompute_G2(vk.gamma_g2); + pvk.vk_gamma_beta_g1_precomp = ppT::precompute_G1(vk.gamma_beta_g1); + pvk.vk_gamma_beta_g2_precomp = ppT::precompute_G2(vk.gamma_beta_g2); + + pvk.encoded_IC_query = vk.encoded_IC_query; + + leave_block("Call to r1cs_ppzksnark_verifier_process_vk"); + + return pvk; +} + +template +bool r1cs_ppzksnark_online_verifier_weak_IC(const r1cs_ppzksnark_processed_verification_key &pvk, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof) +{ + assert(pvk.encoded_IC_query.domain_size() >= primary_input.size()); + + const accumulation_vector > accumulated_IC = pvk.encoded_IC_query.template accumulate_chunk >(primary_input.begin(), primary_input.end(), 0); + const G1 &acc = accumulated_IC.first; + + if (!proof.is_well_formed()) + { + return false; + } + + G1_precomp proof_g_A_g_precomp = ppT::precompute_G1(proof.g_A.g); + G1_precomp proof_g_A_h_precomp = ppT::precompute_G1(proof.g_A.h); + Fqk kc_A_1 = ppT::miller_loop(proof_g_A_g_precomp, pvk.vk_alphaA_g2_precomp); + Fqk kc_A_2 = ppT::miller_loop(proof_g_A_h_precomp, pvk.pp_G2_one_precomp); + GT kc_A = ppT::final_exponentiation(kc_A_1 * kc_A_2.unitary_inverse()); + if (kc_A != GT::one()) + { + return false; + } + + G2_precomp proof_g_B_g_precomp = ppT::precompute_G2(proof.g_B.g); + G1_precomp proof_g_B_h_precomp = ppT::precompute_G1(proof.g_B.h); + Fqk kc_B_1 = ppT::miller_loop(pvk.vk_alphaB_g1_precomp, proof_g_B_g_precomp); + Fqk kc_B_2 = ppT::miller_loop(proof_g_B_h_precomp, pvk.pp_G2_one_precomp); + GT kc_B = ppT::final_exponentiation(kc_B_1 * kc_B_2.unitary_inverse()); + if (kc_B != GT::one()) + { + return false; + } + + G1_precomp proof_g_C_g_precomp = ppT::precompute_G1(proof.g_C.g); + G1_precomp proof_g_C_h_precomp = ppT::precompute_G1(proof.g_C.h); + Fqk kc_C_1 = ppT::miller_loop(proof_g_C_g_precomp, pvk.vk_alphaC_g2_precomp); + Fqk kc_C_2 = ppT::miller_loop(proof_g_C_h_precomp, pvk.pp_G2_one_precomp); + GT kc_C = ppT::final_exponentiation(kc_C_1 * kc_C_2.unitary_inverse()); + if (kc_C != GT::one()) + { + return false; + } + + // check that g^((A+acc)*B)=g^(H*\Prod(t-\sigma)+C) + // equivalently, via pairings, that e(g^(A+acc), g^B) = e(g^H, g^Z) + e(g^C, g^1) + G1_precomp proof_g_A_g_acc_precomp = ppT::precompute_G1(proof.g_A.g + acc); + G1_precomp proof_g_H_precomp = ppT::precompute_G1(proof.g_H); + Fqk QAP_1 = ppT::miller_loop(proof_g_A_g_acc_precomp, proof_g_B_g_precomp); + Fqk QAP_23 = ppT::double_miller_loop(proof_g_H_precomp, pvk.vk_rC_Z_g2_precomp, proof_g_C_g_precomp, pvk.pp_G2_one_precomp); + GT QAP = ppT::final_exponentiation(QAP_1 * QAP_23.unitary_inverse()); + if (QAP != GT::one()) + { + return false; + } + + G1_precomp proof_g_K_precomp = ppT::precompute_G1(proof.g_K); + G1_precomp proof_g_A_g_acc_C_precomp = ppT::precompute_G1((proof.g_A.g + acc) + proof.g_C.g); + Fqk K_1 = ppT::miller_loop(proof_g_K_precomp, pvk.vk_gamma_g2_precomp); + Fqk K_23 = ppT::double_miller_loop(proof_g_A_g_acc_C_precomp, pvk.vk_gamma_beta_g2_precomp, pvk.vk_gamma_beta_g1_precomp, proof_g_B_g_precomp); + GT K = ppT::final_exponentiation(K_1 * K_23.unitary_inverse()); + if (K != GT::one()) + { + return false; + } + + return true; +} + +template +bool r1cs_ppzksnark_verifier_weak_IC(const r1cs_ppzksnark_verification_key &vk, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof) +{ + enter_block("Call to r1cs_ppzksnark_verifier_weak_IC"); + r1cs_ppzksnark_processed_verification_key pvk = r1cs_ppzksnark_verifier_process_vk(vk); + bool result = r1cs_ppzksnark_online_verifier_weak_IC(pvk, primary_input, proof); + leave_block("Call to r1cs_ppzksnark_verifier_weak_IC"); + return result; +} + +template +bool r1cs_ppzksnark_online_verifier_strong_IC(const r1cs_ppzksnark_processed_verification_key &pvk, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof) +{ + bool result = true; + enter_block("Call to r1cs_ppzksnark_online_verifier_strong_IC"); + + if (pvk.encoded_IC_query.domain_size() != primary_input.size()) + { + print_indent(); printf("Input length differs from expected (got %zu, expected %zu).\n", primary_input.size(), pvk.encoded_IC_query.domain_size()); + result = false; + } + else + { + result = r1cs_ppzksnark_online_verifier_weak_IC(pvk, primary_input, proof); + } + + leave_block("Call to r1cs_ppzksnark_online_verifier_strong_IC"); + return result; +} + +template +bool r1cs_ppzksnark_verifier_strong_IC(const r1cs_ppzksnark_verification_key &vk, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof) +{ + enter_block("Call to r1cs_ppzksnark_verifier_strong_IC"); + r1cs_ppzksnark_processed_verification_key pvk = r1cs_ppzksnark_verifier_process_vk(vk); + bool result = r1cs_ppzksnark_online_verifier_strong_IC(pvk, primary_input, proof); + leave_block("Call to r1cs_ppzksnark_verifier_strong_IC"); + return result; +} + +template +bool r1cs_ppzksnark_affine_verifier_weak_IC(const r1cs_ppzksnark_verification_key &vk, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof) +{ + enter_block("Call to r1cs_ppzksnark_affine_verifier_weak_IC"); + assert(vk.encoded_IC_query.domain_size() >= primary_input.size()); + + affine_ate_G2_precomp pvk_pp_G2_one_precomp = ppT::affine_ate_precompute_G2(G2::one()); + affine_ate_G2_precomp pvk_vk_alphaA_g2_precomp = ppT::affine_ate_precompute_G2(vk.alphaA_g2); + affine_ate_G1_precomp pvk_vk_alphaB_g1_precomp = ppT::affine_ate_precompute_G1(vk.alphaB_g1); + affine_ate_G2_precomp pvk_vk_alphaC_g2_precomp = ppT::affine_ate_precompute_G2(vk.alphaC_g2); + affine_ate_G2_precomp pvk_vk_rC_Z_g2_precomp = ppT::affine_ate_precompute_G2(vk.rC_Z_g2); + affine_ate_G2_precomp pvk_vk_gamma_g2_precomp = ppT::affine_ate_precompute_G2(vk.gamma_g2); + affine_ate_G1_precomp pvk_vk_gamma_beta_g1_precomp = ppT::affine_ate_precompute_G1(vk.gamma_beta_g1); + affine_ate_G2_precomp pvk_vk_gamma_beta_g2_precomp = ppT::affine_ate_precompute_G2(vk.gamma_beta_g2); + + enter_block("Compute input-dependent part of A"); + const accumulation_vector > accumulated_IC = vk.encoded_IC_query.template accumulate_chunk >(primary_input.begin(), primary_input.end(), 0); + assert(accumulated_IC.is_fully_accumulated()); + const G1 &acc = accumulated_IC.first; + leave_block("Compute input-dependent part of A"); + + bool result = true; + enter_block("Check knowledge commitment for A is valid"); + affine_ate_G1_precomp proof_g_A_g_precomp = ppT::affine_ate_precompute_G1(proof.g_A.g); + affine_ate_G1_precomp proof_g_A_h_precomp = ppT::affine_ate_precompute_G1(proof.g_A.h); + Fqk kc_A_miller = ppT::affine_ate_e_over_e_miller_loop(proof_g_A_g_precomp, pvk_vk_alphaA_g2_precomp, proof_g_A_h_precomp, pvk_pp_G2_one_precomp); + GT kc_A = ppT::final_exponentiation(kc_A_miller); + + if (kc_A != GT::one()) + { + print_indent(); printf("Knowledge commitment for A query incorrect.\n"); + result = false; + } + leave_block("Check knowledge commitment for A is valid"); + + enter_block("Check knowledge commitment for B is valid"); + affine_ate_G2_precomp proof_g_B_g_precomp = ppT::affine_ate_precompute_G2(proof.g_B.g); + affine_ate_G1_precomp proof_g_B_h_precomp = ppT::affine_ate_precompute_G1(proof.g_B.h); + Fqk kc_B_miller = ppT::affine_ate_e_over_e_miller_loop(pvk_vk_alphaB_g1_precomp, proof_g_B_g_precomp, proof_g_B_h_precomp, pvk_pp_G2_one_precomp); + GT kc_B = ppT::final_exponentiation(kc_B_miller); + if (kc_B != GT::one()) + { + print_indent(); printf("Knowledge commitment for B query incorrect.\n"); + result = false; + } + leave_block("Check knowledge commitment for B is valid"); + + enter_block("Check knowledge commitment for C is valid"); + affine_ate_G1_precomp proof_g_C_g_precomp = ppT::affine_ate_precompute_G1(proof.g_C.g); + affine_ate_G1_precomp proof_g_C_h_precomp = ppT::affine_ate_precompute_G1(proof.g_C.h); + Fqk kc_C_miller = ppT::affine_ate_e_over_e_miller_loop(proof_g_C_g_precomp, pvk_vk_alphaC_g2_precomp, proof_g_C_h_precomp, pvk_pp_G2_one_precomp); + GT kc_C = ppT::final_exponentiation(kc_C_miller); + if (kc_C != GT::one()) + { + print_indent(); printf("Knowledge commitment for C query incorrect.\n"); + result = false; + } + leave_block("Check knowledge commitment for C is valid"); + + enter_block("Check QAP divisibility"); + affine_ate_G1_precomp proof_g_A_g_acc_precomp = ppT::affine_ate_precompute_G1(proof.g_A.g + acc); + affine_ate_G1_precomp proof_g_H_precomp = ppT::affine_ate_precompute_G1(proof.g_H); + Fqk QAP_miller = ppT::affine_ate_e_times_e_over_e_miller_loop(proof_g_H_precomp, pvk_vk_rC_Z_g2_precomp, proof_g_C_g_precomp, pvk_pp_G2_one_precomp, proof_g_A_g_acc_precomp, proof_g_B_g_precomp); + GT QAP = ppT::final_exponentiation(QAP_miller); + if (QAP != GT::one()) + { + print_indent(); printf("QAP divisibility check failed.\n"); + result = false; + } + leave_block("Check QAP divisibility"); + + enter_block("Check same coefficients were used"); + affine_ate_G1_precomp proof_g_K_precomp = ppT::affine_ate_precompute_G1(proof.g_K); + affine_ate_G1_precomp proof_g_A_g_acc_C_precomp = ppT::affine_ate_precompute_G1((proof.g_A.g + acc) + proof.g_C.g); + Fqk K_miller = ppT::affine_ate_e_times_e_over_e_miller_loop(proof_g_A_g_acc_C_precomp, pvk_vk_gamma_beta_g2_precomp, pvk_vk_gamma_beta_g1_precomp, proof_g_B_g_precomp, proof_g_K_precomp, pvk_vk_gamma_g2_precomp); + GT K = ppT::final_exponentiation(K_miller); + if (K != GT::one()) + { + print_indent(); printf("Same-coefficient check failed.\n"); + result = false; + } + leave_block("Check same coefficients were used"); + + leave_block("Call to r1cs_ppzksnark_affine_verifier_weak_IC"); + + return result; +} + +} // libsnark +#endif // R1CS_PPZKSNARK_TCC_ diff --git a/src/snark/libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark_params.hpp b/src/snark/libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark_params.hpp new file mode 100644 index 000000000..4054b8e3b --- /dev/null +++ b/src/snark/libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark_params.hpp @@ -0,0 +1,34 @@ +/** @file + ***************************************************************************** + + Declaration of public-parameter selector for the R1CS ppzkSNARK. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef R1CS_PPZKSNARK_PARAMS_HPP_ +#define R1CS_PPZKSNARK_PARAMS_HPP_ + +#include "relations/constraint_satisfaction_problems/r1cs/r1cs.hpp" + +namespace libsnark { + +/** + * Below are various template aliases (used for convenience). + */ + +template +using r1cs_ppzksnark_constraint_system = r1cs_constraint_system >; + +template +using r1cs_ppzksnark_primary_input = r1cs_primary_input >; + +template +using r1cs_ppzksnark_auxiliary_input = r1cs_auxiliary_input >; + +} // libsnark + +#endif // R1CS_PPZKSNARK_PARAMS_HPP_ diff --git a/src/snark/libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/tests/test_r1cs_ppzksnark.cpp b/src/snark/libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/tests/test_r1cs_ppzksnark.cpp new file mode 100644 index 000000000..6c6e51857 --- /dev/null +++ b/src/snark/libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/tests/test_r1cs_ppzksnark.cpp @@ -0,0 +1,44 @@ +/** @file + ***************************************************************************** + Test program that exercises the ppzkSNARK (first generator, then + prover, then verifier) on a synthetic R1CS instance. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ +#include +#include + +#include "algebra/curves/alt_bn128/alt_bn128_pp.hpp" +#include "common/profiling.hpp" +#include "common/utils.hpp" +#include "relations/constraint_satisfaction_problems/r1cs/examples/r1cs_examples.hpp" +#include "zk_proof_systems/ppzksnark/r1cs_ppzksnark/examples/run_r1cs_ppzksnark.hpp" + +#include + +using namespace libsnark; + +template +void test_r1cs_ppzksnark(size_t num_constraints, + size_t input_size) +{ + print_header("(enter) Test R1CS ppzkSNARK"); + + const bool test_serialization = true; + r1cs_example > example = generate_r1cs_example_with_binary_input >(num_constraints, input_size); + example.constraint_system.swap_AB_if_beneficial(); + const bool bit = run_r1cs_ppzksnark(example, test_serialization); + EXPECT_TRUE(bit); + + print_header("(leave) Test R1CS ppzkSNARK"); +} + +TEST(zk_proof_systems, r1cs_ppzksnark) +{ + start_profiling(); + + test_r1cs_ppzksnark(1000, 20); +} diff --git a/src/support/pagelocker.h b/src/support/pagelocker.h index 88b95cce7..cf42e3dfd 100644 --- a/src/support/pagelocker.h +++ b/src/support/pagelocker.h @@ -145,9 +145,9 @@ private: static void CreateInstance() { // Using a local static instance guarantees that the object is initialized - // when it's first needed and also deinitialized after all objects that use + // when it's first needed and also destructed after all objects that use // it are done with it. I can think of one unlikely scenario where we may - // have a static deinitialization order/problem, but the check in + // have a static destruction order/problem, but the check in // LockedPageManagerBase's destructor helps us detect if that ever happens. static LockedPageManager instance; LockedPageManager::_instance = &instance; diff --git a/src/test-komodo/main.cpp b/src/test-komodo/main.cpp new file mode 100644 index 000000000..527d7666e --- /dev/null +++ b/src/test-komodo/main.cpp @@ -0,0 +1,14 @@ +#include "key.h" +#include "chainparams.h" +#include "gtest/gtest.h" +#include "crypto/common.h" + + +int main(int argc, char **argv) { + assert(init_and_check_sodium() != -1); + ECC_Start(); + SelectParams(CBaseChainParams::REGTEST); + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/test-komodo/test_cryptoconditions.cpp b/src/test-komodo/test_cryptoconditions.cpp new file mode 100644 index 000000000..f1037a2ae --- /dev/null +++ b/src/test-komodo/test_cryptoconditions.cpp @@ -0,0 +1,210 @@ +#include +#include + +#include "base58.h" +#include "key.h" +#include "script/cc.h" +#include "cc/eval.h" +#include "primitives/transaction.h" +#include "script/interpreter.h" +#include "script/serverchecker.h" + +#include "testutils.h" + + +CKey notaryKey; + +std::string pubkey = "0205a8ad0c1dbc515f149af377981aab58b836af008d4d7ab21bd76faf80550b47"; +std::string secret = "UxFWWxsf1d7w7K5TvAWSkeX4H95XQKwdwGv49DXwWUTzPTTjHBbU"; + + +class CCTest : public ::testing::Test { +public: + void CCSign(CMutableTransaction &tx, CC *cond) { + tx.vin.resize(1); + PrecomputedTransactionData txdata(tx); + uint256 sighash = SignatureHash(CCPubKey(cond), tx, 0, SIGHASH_ALL, 0, 0, &txdata); + + int out = cc_signTreeSecp256k1Msg32(cond, notaryKey.begin(), sighash.begin()); + tx.vin[0].scriptSig = CCSig(cond); + } +protected: + virtual void SetUp() { + // enable CC + ASSETCHAINS_CC = 1; + // Notary key + CBitcoinSecret vchSecret; + // this returns false due to network prefix mismatch but works anyway + vchSecret.SetString(secret); + notaryKey = vchSecret.GetKey(); + } +}; + + +TEST_F(CCTest, testIsPayToCryptoCondition) +{ + CScript s = CScript() << VCH("a", 1); + ASSERT_FALSE(s.IsPayToCryptoCondition()); + + s = CScript() << VCH("a", 1) << OP_CHECKCRYPTOCONDITION; + ASSERT_TRUE(s.IsPayToCryptoCondition()); + + s = CScript() << OP_CHECKCRYPTOCONDITION; + ASSERT_FALSE(s.IsPayToCryptoCondition()); +} + + +TEST_F(CCTest, testMayAcceptCryptoCondition) +{ + CC *cond; + + // ok + CCFromJson(cond, R"!!( + { "type": "threshold-sha-256", + "threshold": 2, + "subfulfillments": [ + { "type": "secp256k1-sha-256", "publicKey": "0205a8ad0c1dbc515f149af377981aab58b836af008d4d7ab21bd76faf80550b47" } + ] + })!!"); + ASSERT_TRUE(CCPubKey(cond).MayAcceptCryptoCondition()); + + + // prefix not allowed + CCFromJson(cond, R"!!( + { "type": "prefix-sha-256", + "prefix": "abc", + "maxMessageLength": 10, + "subfulfillment": + { "type": "secp256k1-sha-256", "publicKey": "0205a8ad0c1dbc515f149af377981aab58b836af008d4d7ab21bd76faf80550b47" } + })!!"); + ASSERT_FALSE(CCPubKey(cond).MayAcceptCryptoCondition()); + + + // has no signature nodes + CCFromJson(cond, R"!!( + { "type": "threshold-sha-256", + "threshold": 1, + "subfulfillments": [ + { "type": "eval-sha-256", "code": "" }, + { "type": "eval-sha-256", "code": "" } + ] + })!!"); + ASSERT_FALSE(CCPubKey(cond).MayAcceptCryptoCondition()); +} + + +static bool CCVerify(const CMutableTransaction &mtxTo, const CC *cond) { + CAmount amount; + ScriptError error; + CTransaction txTo(mtxTo); + PrecomputedTransactionData txdata(txTo); + auto checker = ServerTransactionSignatureChecker(&txTo, 0, amount, false, txdata); + return VerifyScript(CCSig(cond), CCPubKey(cond), 0, checker, 0, &error); +}; + + +TEST_F(CCTest, testVerifyCryptoCondition) +{ + CC *cond; + CMutableTransaction mtxTo; + + // ok + cond = CCNewSecp256k1(notaryKey.GetPubKey()); + CCFromJson(cond, R"!!({ + "type": "secp256k1-sha-256", + "publicKey": "0205a8ad0c1dbc515f149af377981aab58b836af008d4d7ab21bd76faf80550b47" + })!!"); + CCSign(mtxTo, cond); + ASSERT_TRUE(CCVerify(mtxTo, cond)); + + + // has signature nodes + CCFromJson(cond, R"!!({ + "type": "threshold-sha-256", + "threshold": 1, + "subfulfillments": [ + { "type": "preimage-sha-256", "preimage": "" }, + { "type": "secp256k1-sha-256", "publicKey": "0205a8ad0c1dbc515f149af377981aab58b836af008d4d7ab21bd76faf80550b47" } + ] + })!!"); + cond->threshold = 2; + CCSign(mtxTo, cond); + ASSERT_TRUE(CCVerify(mtxTo, cond)); + + // no signatures; the preimage will get encoded as a fulfillment because it's cheaper + // and the secp256k1 node will get encoded as a condition + cond->threshold = 1; + ASSERT_FALSE(CCVerify(mtxTo, cond)); + + // here the signature is set wrong + cond->threshold = 2; + ASSERT_TRUE(CCVerify(mtxTo, cond)); + memset(cond->subconditions[1]->signature, 0, 32); + ASSERT_FALSE(CCVerify(mtxTo, cond)); +} + +extern Eval* EVAL_TEST; + +TEST_F(CCTest, testVerifyEvalCondition) +{ + + class EvalMock : public Eval + { + public: + bool Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn) + { return cond->code[0] ? Valid() : Invalid(""); } + }; + + EvalMock eval; + EVAL_TEST = &eval; + + + CC *cond; + CMutableTransaction mtxTo; + + // ok + cond = CCNewThreshold(2, { CCNewSecp256k1(notaryKey.GetPubKey()), CCNewEval({1}) }); + CCSign(mtxTo, cond); + ASSERT_TRUE(CCVerify(mtxTo, cond)); + + cond->subconditions[1]->code[0] = 0; + ASSERT_FALSE(CCVerify(mtxTo, cond)); +} + + +TEST_F(CCTest, testCryptoConditionsDisabled) +{ + CC *cond; + ScriptError error; + CMutableTransaction mtxTo; + + // ok + CCFromJson(cond, R"!!({ + "type": "secp256k1-sha-256", + "publicKey": "0205a8ad0c1dbc515f149af377981aab58b836af008d4d7ab21bd76faf80550b47" + })!!"); + CCSign(mtxTo, cond); + ASSERT_TRUE(CCVerify(mtxTo, cond)); + + ASSETCHAINS_CC = 0; + ASSERT_FALSE(CCVerify(mtxTo, cond)); +} + + +TEST_F(CCTest, testLargeCondition) +{ + CC *cond; + ScriptError error; + CMutableTransaction mtxTo; + + std::vector ccs; + for (int i=0; i<18; i++) { + ccs.push_back(CCNewSecp256k1(notaryKey.GetPubKey())); + } + cond = CCNewThreshold(16, ccs); + CCSign(mtxTo, cond); + EXPECT_EQ("(16 of 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,A5,A5)", + CCShowStructure(CCPrune(cond))); + EXPECT_EQ(1744, CCSig(cond).size()); + ASSERT_TRUE(CCVerify(mtxTo, cond)); +} diff --git a/src/test-komodo/test_eval_bet.cpp b/src/test-komodo/test_eval_bet.cpp new file mode 100644 index 000000000..6f41608b9 --- /dev/null +++ b/src/test-komodo/test_eval_bet.cpp @@ -0,0 +1,599 @@ +#include +#include + +#include "cc/betprotocol.h" +#include "cc/eval.h" +#include "base58.h" +#include "key.h" +#include "main.h" +#include "script/cc.h" +#include "primitives/transaction.h" +#include "script/interpreter.h" +#include "script/serverchecker.h" + +#include "testutils.h" + + +extern Eval* EVAL_TEST; + + +namespace TestBet { + + +static std::vector playerSecrets; +static std::vector players; + +static int Dealer = 0, Player1 = 1, Player2 = 2; + + +int CCSign(CMutableTransaction &tx, unsigned int nIn, CC *cond, std::vector keyIds) { + PrecomputedTransactionData txdata(tx); + uint256 sighash = SignatureHash(CCPubKey(cond), tx, nIn, SIGHASH_ALL, 0, 0, &txdata); + int nSigned = 0; + for (int i=0; i> evaluate( + std::vector header, std::vector body) + { + std::vector outs; + if (memcmp(header.data(), "BetHeader", 9)) { + printf("Wrong VM header\n"); + return std::make_pair(0, outs); + } + outs.push_back(CTxOut(2, CScript() << OP_RETURN << body.size())); + return std::make_pair(body.size(), outs); + } +}; + +const EvalCode EVAL_DISPUTEBET = 0xf2; + +class EvalMock : public Eval +{ +public: + uint256 MoM; + int currentHeight; + std::map txs; + std::map blocks; + std::map> spends; + + bool Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn) + { + EvalCode ecode = cond->code[0]; + std::vector vparams(cond->code+1, cond->code+cond->codeLength); + + if (ecode == EVAL_DISPUTEBET) { + MockVM vm; + return DisputePayout(vm, vparams, txTo, nIn); + } + if (ecode == EVAL_IMPORTPAYOUT) { + return ImportPayout(vparams, txTo, nIn); + } + return Invalid("invalid-code"); + } + + bool GetSpendsConfirmed(uint256 hash, std::vector &spendsOut) const + { + auto r = spends.find(hash); + if (r != spends.end()) { + spendsOut = r->second; + return true; + } + return false; + } + + bool GetTxUnconfirmed(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock) const + { + auto r = txs.find(hash); + if (r != txs.end()) { + txOut = r->second; + if (blocks.count(hash) > 0) + hashBlock = hash; + return true; + } + return false; + } + + unsigned int GetCurrentHeight() const { return currentHeight; } + + bool GetBlock(uint256 hash, CBlockIndex& blockIdx) const + { + auto r = blocks.find(hash); + if (r == blocks.end()) return false; + blockIdx = r->second; + return true; + } + + bool GetNotarisationData(uint256 notarisationHash, NotarisationData &data) const + { + if (notarisationHash == NotarisationHash()) { + data.MoM = MoM; + return true; + } + return false; + } + + static uint256 NotarisationHash() + { + uint256 h; + h.begin()[0] = 123; + return h; + } +}; + + +/* + * Generates example data that we will test with and shows how to call BetProtocol. + */ +class ExampleBet +{ +public: + BetProtocol bet; + CAmount totalPayout; + + ExampleBet() : bet(BetProtocol(EVAL_DISPUTEBET, players, 2, VCH("BetHeader", 9))), totalPayout(100) {} + ~ExampleBet() {}; + + CTransaction SessionTx() + { + return CTransaction(bet.MakeSessionTx(1)); + } + + CC* DisputeCond() + { + return bet.MakeDisputeCond(); + } + + CC* PayoutCond() + { + return bet.MakePayoutCond(SessionTx().GetHash()); + } + + CTransaction StakeTx() + { + return CTransaction(bet.MakeStakeTx(totalPayout, SessionTx().GetHash())); + } + + std::vector PlayerState(int playerIdx) + { + std::vector state; + for (int i=0; i Payouts(int playerIdx) + { + return MockVM().evaluate(bet.vmParams, PlayerState(playerIdx)).second; + } + + CMutableTransaction DisputeTx(int playerIdx) + { + return bet.MakeDisputeTx(SessionTx().GetHash(), SerializeHash(Payouts(playerIdx))); + } + + CMutableTransaction PostEvidenceTx(int playerIdx) + { + return bet.MakePostEvidenceTx(SessionTx().GetHash(), playerIdx, PlayerState(playerIdx)); + } + + CMutableTransaction AgreePayoutTx() + { + std::vector v; + return bet.MakeAgreePayoutTx(v, uint256()); + } + + MoMProof GetMoMProof() + { + int nIndex = 5; + std::vector vBranch; + vBranch.resize(3); + return MoMProof(nIndex, vBranch, EvalMock::NotarisationHash()); + } + + CMutableTransaction ImportPayoutTx() + { + CMutableTransaction disputeTx = DisputeTx(Player2); + return bet.MakeImportPayoutTx(Payouts(Player2), disputeTx, uint256(), GetMoMProof()); + } + + EvalMock SetEvalMock(int currentHeight) + { + EvalMock eval; + CTransaction sessionTx = SessionTx(); + + eval.txs[sessionTx.GetHash()] = sessionTx; + + CBlockIndex sessionBlock; + sessionBlock.nHeight = 10; + eval.blocks[sessionTx.GetHash()] = sessionBlock; + + std::vector sessionSpends; + sessionSpends.push_back(CTransaction(PostEvidenceTx(Dealer))); + sessionSpends.push_back(CTransaction()); // Invalid, should be ignored + sessionSpends.push_back(CTransaction(PostEvidenceTx(Player2))); + eval.spends[sessionTx.GetHash()] = sessionSpends; + + eval.currentHeight = currentHeight; + + MoMProof proof = GetMoMProof(); + eval.MoM = proof.Exec(DisputeTx(Player2).GetHash()); + + EVAL_TEST = &eval; + return eval; + } +}; + + +ExampleBet ebet; + + +class TestBet : public ::testing::Test { +protected: + static void SetUpTestCase() { + // Make playerSecrets + CBitcoinSecret vchSecret; + auto addKey = [&] (std::string k) { vchSecret.SetString(k); playerSecrets.push_back(vchSecret.GetKey()); }; + addKey("UwFBKf4d6wC3yqdnk3LoGrFjy7gwxrWerBT8jTFamrBbem8wSw9L"); + addKey("Up6GpWwrmx2VpqF8rD3snJXToKT56Dzc8YSoL24osXnfNdCucaMR"); + addKey("UxEHwki3A95PSHHVRzE2N67eHTeoUcqLkovxp6yDPVViv54skF8c"); + // Make playerpubkeys + for (int i=0; isubconditions[0]; + uint8_t target[100]; + sprintf((char*)target, "%c\x02\tBetHeader", EVAL_DISPUTEBET); + EXPECT_EQ(0, memcmp(target, evalCond->code, 12)); + + for (int i=0; isubconditions[1]->subconditions[i])); +} + + +TEST_F(TestBet, testSignDisputeCond) +{ + // Only one key needed to dispute + CMutableTransaction disputeTx = ebet.DisputeTx(Player1); + CC *disputeCond = ebet.DisputeCond(); + EXPECT_EQ(1, CCSign(disputeTx, 0, disputeCond, {Player1})); + + EXPECT_EQ(1, cc_isFulfilled(disputeCond->subconditions[0])); + EXPECT_EQ(1, cc_isFulfilled(disputeCond->subconditions[1])); + EXPECT_EQ(0, cc_isFulfilled(disputeCond->subconditions[1]->subconditions[0])); + EXPECT_EQ(1, cc_isFulfilled(disputeCond->subconditions[1]->subconditions[1])); + EXPECT_EQ(0, cc_isFulfilled(disputeCond->subconditions[1]->subconditions[2])); + EXPECT_EQ(1, cc_isFulfilled(disputeCond)); +} + + +TEST_F(TestBet, testDispute) +{ + EvalMock eval = ebet.SetEvalMock(12); + + // Only one key needed to dispute + CMutableTransaction disputeTx = ebet.DisputeTx(Player2); + CC *disputeCond = ebet.DisputeCond(); + EXPECT_EQ(1, CCSign(disputeTx, 0, disputeCond, {Player2})); + + // Success + EXPECT_TRUE(TestCC(disputeTx, 0, disputeCond)); + + // Set result hash to 0 and check false + uint256 nonsense; + disputeTx.vout[0].scriptPubKey = CScript() << OP_RETURN << E_MARSHAL(ss << nonsense); + EXPECT_EQ(1, CCSign(disputeTx, 0, disputeCond, {Player2})); + EXPECT_FALSE(TestCC(disputeTx, 0, disputeCond)); + EXPECT_EQ("wrong-payout", eval.state.GetRejectReason()); +} + + +TEST_F(TestBet, testDisputeInvalidOutput) +{ + EvalMock eval = ebet.SetEvalMock(11); + + // Only one key needed to dispute + CMutableTransaction disputeTx = ebet.DisputeTx(Dealer); + CC *disputeCond = ebet.DisputeCond(); + + // invalid payout hash + std::vector invalidHash = {0,1,2}; + disputeTx.vout[0].scriptPubKey = CScript() << OP_RETURN << invalidHash; + ASSERT_EQ(1, CCSign(disputeTx, 0, disputeCond, {Player1})); + EXPECT_FALSE(TestCC(disputeTx, 0, disputeCond)); + EXPECT_EQ("invalid-payout-hash", eval.state.GetRejectReason()); + + // no vout at all + disputeTx.vout.resize(0); + ASSERT_EQ(1, CCSign(disputeTx, 0, disputeCond, {Player1})); + EXPECT_FALSE(TestCC(disputeTx, 0, disputeCond)); + EXPECT_EQ("no-vouts", eval.state.GetRejectReason()); +} + + +TEST_F(TestBet, testDisputeEarly) +{ + EvalMock eval = ebet.SetEvalMock(11); + + // Only one key needed to dispute + CMutableTransaction disputeTx = ebet.DisputeTx(Dealer); + CC *disputeCond = ebet.DisputeCond(); + EXPECT_EQ(1, CCSign(disputeTx, 0, disputeCond, {Player1})); + + EXPECT_FALSE(TestCC(disputeTx, 0, disputeCond)); + EXPECT_EQ("dispute-too-soon", eval.state.GetRejectReason()); +} + + +TEST_F(TestBet, testDisputeInvalidParams) +{ + EvalMock eval = ebet.SetEvalMock(12); + + CMutableTransaction disputeTx = ebet.DisputeTx(Player2); + CC *disputeCond = ebet.DisputeCond(); + CC *evalCond = disputeCond->subconditions[0]; + + // too long + evalCond->code = (unsigned char*) realloc(evalCond->code, ++evalCond->codeLength); + ASSERT_EQ(1, CCSign(disputeTx, 0, disputeCond, {Player2})); + EXPECT_FALSE(TestCC(disputeTx, 0, disputeCond)); + EXPECT_EQ("malformed-params", eval.state.GetRejectReason()); + + // too short + eval.state = CValidationState(); + evalCond->codeLength = 1; + ASSERT_EQ(1, CCSign(disputeTx, 0, disputeCond, {Player2})); + EXPECT_FALSE(TestCC(disputeTx, 0, disputeCond)); + EXPECT_EQ("malformed-params", eval.state.GetRejectReason()); + + // is fine + eval.state = CValidationState(); + evalCond->codeLength = 12; + ASSERT_EQ(1, CCSign(disputeTx, 0, disputeCond, {Player2})); + EXPECT_TRUE(TestCC(disputeTx, 0, disputeCond)); +} + + +TEST_F(TestBet, testDisputeInvalidEvidence) +{ + EvalMock eval = ebet.SetEvalMock(12); + + CMutableTransaction disputeTx = ebet.DisputeTx(Player2); + CC *disputeCond = ebet.DisputeCond(); + CCSign(disputeTx, 0, disputeCond, {Player2}); + + CMutableTransaction mtx; + + mtx.vout.resize(1); + mtx.vout[0].scriptPubKey = CScript(); + eval.spends[ebet.SessionTx().GetHash()][1] = CTransaction(mtx); + ASSERT_TRUE(TestCC(disputeTx, 0, disputeCond)); + + mtx.vout[0].scriptPubKey << OP_RETURN; + eval.spends[ebet.SessionTx().GetHash()][1] = CTransaction(mtx); + ASSERT_TRUE(TestCC(disputeTx, 0, disputeCond)); + + mtx.vout[0].scriptPubKey = CScript() << 0; + eval.spends[ebet.SessionTx().GetHash()][1] = CTransaction(mtx); + ASSERT_TRUE(TestCC(disputeTx, 0, disputeCond)); + + eval.spends[ebet.SessionTx().GetHash()].resize(1); + eval.spends[ebet.SessionTx().GetHash()][0] = CTransaction(); + ASSERT_FALSE(TestCC(disputeTx, 0, disputeCond)); + EXPECT_EQ("no-evidence", eval.state.GetRejectReason()); +} + + +TEST_F(TestBet, testMakeStakeTx) +{ + CTransaction stakeTx = ebet.StakeTx(); + EXPECT_EQ(0, stakeTx.vin.size()); + EXPECT_EQ(1, stakeTx.vout.size()); + EXPECT_EQ(ebet.totalPayout, stakeTx.vout[0].nValue); + EXPECT_EQ(CCPubKey(ebet.PayoutCond()), stakeTx.vout[0].scriptPubKey); +} + + +TEST_F(TestBet, testMakePayoutCond) +{ + CC *payoutCond = ebet.PayoutCond(); + EXPECT_EQ("(1 of (3 of 5,5,5),(2 of (1 of 5,5,5),15))", CCShowStructure(payoutCond)); + EXPECT_EQ(0, memcmp(payoutCond->subconditions[1]->subconditions[1]->code+1, + ebet.SessionTx().GetHash().begin(), 32)); +} + + +TEST_F(TestBet, testSignPayout) +{ + + CMutableTransaction payoutTx = ebet.AgreePayoutTx(); + CC *payoutCond = ebet.PayoutCond(); + + EXPECT_EQ(0, cc_isFulfilled(payoutCond->subconditions[0])); + EXPECT_EQ(0, cc_isFulfilled(payoutCond->subconditions[1])); + EXPECT_EQ(0, cc_isFulfilled(payoutCond)); + + EXPECT_EQ(2, CCSign(payoutTx, 0, payoutCond, {Player1})); + EXPECT_EQ(0, cc_isFulfilled(payoutCond->subconditions[0])); + EXPECT_EQ(1, cc_isFulfilled(payoutCond->subconditions[1])); + EXPECT_EQ(1, cc_isFulfilled(payoutCond)); + + EXPECT_EQ(2, CCSign(payoutTx, 0, payoutCond, {Player2})); + EXPECT_EQ(0, cc_isFulfilled(payoutCond->subconditions[0])); + + EXPECT_EQ(2, CCSign(payoutTx, 0, payoutCond, {Dealer})); + EXPECT_EQ(1, cc_isFulfilled(payoutCond->subconditions[0])); +} + + +TEST_F(TestBet, testAgreePayout) +{ + EvalMock eval = ebet.SetEvalMock(12); + + CMutableTransaction payoutTx = ebet.AgreePayoutTx(); + CC *payoutCond = ebet.PayoutCond(); + + EXPECT_EQ(2, CCSign(payoutTx, 0, payoutCond, {Dealer})); + EXPECT_FALSE(TestCC(payoutTx, 0, payoutCond)); + EXPECT_EQ("(1 of (2 of (1 of 5,A5,A5),15),A2)", + CCShowStructure(CCPrune(payoutCond))); + + EXPECT_EQ(2, CCSign(payoutTx, 0, payoutCond, {Player1})); + EXPECT_FALSE(TestCC(payoutTx, 0, payoutCond)); + EXPECT_EQ("(1 of (2 of (1 of 5,A5,A5),15),A2)", + CCShowStructure(CCPrune(payoutCond))); + + EXPECT_EQ(2, CCSign(payoutTx, 0, payoutCond, {Player2})); + EXPECT_TRUE( TestCC(payoutTx, 0, payoutCond)); + EXPECT_EQ("(1 of (3 of 5,5,5),A2)", + CCShowStructure(CCPrune(payoutCond))); +} + + +TEST_F(TestBet, testImportPayout) +{ + EvalMock eval = ebet.SetEvalMock(12); + + CMutableTransaction importTx = ebet.ImportPayoutTx(); + CC *payoutCond = ebet.PayoutCond(); + EXPECT_EQ(2, CCSign(importTx, 0, payoutCond, {Player2})); + EXPECT_TRUE(TestCC(importTx, 0, payoutCond)); +} + + +TEST_F(TestBet, testImportPayoutFewVouts) +{ + EvalMock eval = ebet.SetEvalMock(12); + + CMutableTransaction importTx = ebet.ImportPayoutTx(); + importTx.vout.resize(0); + CC *payoutCond = ebet.PayoutCond(); + EXPECT_EQ(2, CCSign(importTx, 0, payoutCond, {Player2})); + EXPECT_FALSE(TestCC(importTx, 0, payoutCond)); + EXPECT_EQ("no-vouts", eval.state.GetRejectReason()); +} + + +TEST_F(TestBet, testImportPayoutInvalidPayload) +{ + EvalMock eval = ebet.SetEvalMock(12); + + CMutableTransaction importTx = ebet.ImportPayoutTx(); + importTx.vout[0].scriptPubKey.pop_back(); + CC *payoutCond = ebet.PayoutCond(); + EXPECT_EQ(2, CCSign(importTx, 0, payoutCond, {Player2})); + EXPECT_FALSE(TestCC(importTx, 0, payoutCond)); + EXPECT_EQ("invalid-payload", eval.state.GetRejectReason()); +} + + +TEST_F(TestBet, testImportPayoutWrongPayouts) +{ + EvalMock eval = ebet.SetEvalMock(12); + + CMutableTransaction importTx = ebet.ImportPayoutTx(); + importTx.vout[1].nValue = 7; + CC *payoutCond = ebet.PayoutCond(); + EXPECT_EQ(2, CCSign(importTx, 0, payoutCond, {Player2})); + ASSERT_FALSE(TestCC(importTx, 0, payoutCond)); + EXPECT_EQ("wrong-payouts", eval.state.GetRejectReason()); +} + + +TEST_F(TestBet, testImportPayoutMangleSessionId) +{ + EvalMock eval = ebet.SetEvalMock(12); + + CMutableTransaction importTx = ebet.ImportPayoutTx(); + CC *payoutCond = ebet.PayoutCond(); + payoutCond->subconditions[1]->subconditions[1]->codeLength = 31; + EXPECT_EQ(2, CCSign(importTx, 0, payoutCond, {Player2})); + ASSERT_FALSE(TestCC(importTx, 0, payoutCond)); + EXPECT_EQ("malformed-params", eval.state.GetRejectReason()); + + payoutCond = ebet.PayoutCond(); + memset(payoutCond->subconditions[1]->subconditions[1]->code+1, 1, 32); + EXPECT_EQ(2, CCSign(importTx, 0, payoutCond, {Player2})); + ASSERT_FALSE(TestCC(importTx, 0, payoutCond)); + EXPECT_EQ("wrong-session", eval.state.GetRejectReason()); +} + + +TEST_F(TestBet, testImportPayoutInvalidNotarisationHash) +{ + EvalMock eval = ebet.SetEvalMock(12); + + MoMProof proof = ebet.GetMoMProof(); + proof.notarisationHash = uint256(); + CMutableTransaction importTx = ebet.bet.MakeImportPayoutTx( + ebet.Payouts(Player2), ebet.DisputeTx(Player2), uint256(), proof); + + CC *payoutCond = ebet.PayoutCond(); + EXPECT_EQ(2, CCSign(importTx, 0, payoutCond, {Player2})); + EXPECT_FALSE(TestCC(importTx, 0, payoutCond)); + EXPECT_EQ("coudnt-load-mom", eval.state.GetRejectReason()); +} + + +TEST_F(TestBet, testImportPayoutMomFail) +{ + EvalMock eval = ebet.SetEvalMock(12); + + MoMProof proof = ebet.GetMoMProof(); + proof.nIndex ^= 1; + CMutableTransaction importTx = ebet.bet.MakeImportPayoutTx( + ebet.Payouts(Player2), ebet.DisputeTx(Player2), uint256(), proof); + + CC *payoutCond = ebet.PayoutCond(); + EXPECT_EQ(2, CCSign(importTx, 0, payoutCond, {Player2})); + EXPECT_FALSE(TestCC(importTx, 0, payoutCond)); + EXPECT_EQ("mom-check-fail", eval.state.GetRejectReason()); +} + + +} /* namespace TestBet */ diff --git a/src/test-komodo/test_eval_notarisation.cpp b/src/test-komodo/test_eval_notarisation.cpp new file mode 100644 index 000000000..86d5f58c4 --- /dev/null +++ b/src/test-komodo/test_eval_notarisation.cpp @@ -0,0 +1,204 @@ +#include +#include + +#include "cc/betprotocol.h" +#include "cc/eval.h" +#include "base58.h" +#include "core_io.h" +#include "key.h" +#include "main.h" +#include "script/cc.h" +#include "primitives/transaction.h" +#include "script/interpreter.h" +#include "script/serverchecker.h" + +#include "testutils.h" + + +extern Eval* EVAL_TEST; +extern int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); + + +namespace TestEvalNotarisation { + + +class EvalMock : public Eval +{ +public: + uint32_t nNotaries; + uint8_t notaries[64][33]; + std::map txs; + std::map blocks; + + int32_t GetNotaries(uint8_t pubkeys[64][33], int32_t height, uint32_t timestamp) const + { + memcpy(pubkeys, notaries, sizeof(notaries)); + return nNotaries; + } + + bool GetTxUnconfirmed(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock) const + { + auto r = txs.find(hash); + if (r != txs.end()) { + txOut = r->second; + if (blocks.count(hash) > 0) + hashBlock = hash; + return true; + } + return false; + } + + bool GetBlock(uint256 hash, CBlockIndex& blockIdx) const + { + auto r = blocks.find(hash); + if (r == blocks.end()) return false; + blockIdx = r->second; + return true; + } +}; + +static auto noop = [&](CMutableTransaction &mtx){}; + + +template +void SetEval(EvalMock &eval, CMutableTransaction ¬ary, Modifier modify) +{ + eval.nNotaries = komodo_notaries(eval.notaries, 780060, 1522946781); + + // make fake notary inputs + notary.vin.resize(11); + for (int i=0; i> proof); + EXPECT_EQ(data.MoM, proof.Exec(proofTxHash)); +} + + +TEST(TestEvalNotarisation, testInvalidNotaryPubkey) +{ + EvalMock eval; + CMutableTransaction notary(notaryTx); + SetEval(eval, notary, noop); + + memset(eval.notaries[10], 0, 33); + + NotarisationData data; + ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data)); +} + + +TEST(TestEvalNotarisation, testInvalidNotarisationBadOpReturn) +{ + EvalMock eval; + CMutableTransaction notary(notaryTx); + + notary.vout[1].scriptPubKey = CScript() << OP_RETURN << 0; + SetEval(eval, notary, noop); + + NotarisationData data; + ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data)); +} + + +TEST(TestEvalNotarisation, testInvalidNotarisationTxNotEnoughSigs) +{ + EvalMock eval; + CMutableTransaction notary(notaryTx); + + SetEval(eval, notary, [](CMutableTransaction &tx) { + tx.vin.resize(10); + }); + + NotarisationData data; + ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data)); +} + + +TEST(TestEvalNotarisation, testInvalidNotarisationTxDoesntExist) +{ + EvalMock eval; + CMutableTransaction notary(notaryTx); + + SetEval(eval, notary, noop); + + NotarisationData data; + ASSERT_FALSE(eval.GetNotarisationData(uint256(), data)); +} + + +TEST(TestEvalNotarisation, testInvalidNotarisationDupeNotary) +{ + EvalMock eval; + CMutableTransaction notary(notaryTx); + + SetEval(eval, notary, [](CMutableTransaction &tx) { + tx.vin[1] = tx.vin[3]; + }); + + NotarisationData data; + ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data)); +} + + +TEST(TestEvalNotarisation, testInvalidNotarisationInputNotCheckSig) +{ + EvalMock eval; + CMutableTransaction notary(notaryTx); + + SetEval(eval, notary, [&](CMutableTransaction &tx) { + int i = 1; + CMutableTransaction txIn; + txIn.vout.resize(1); + txIn.vout[0].scriptPubKey << VCH(eval.notaries[i*2], 33) << OP_RETURN; + notary.vin[i].prevout = COutPoint(txIn.GetHash(), 0); + eval.txs[txIn.GetHash()] = CTransaction(txIn); + }); + + NotarisationData data; + ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data)); +} + + + +} /* namespace TestEvalNotarisation */ diff --git a/src/test-komodo/testutils.h b/src/test-komodo/testutils.h new file mode 100644 index 000000000..df8e88cd9 --- /dev/null +++ b/src/test-komodo/testutils.h @@ -0,0 +1,15 @@ +#ifndef TESTUTILS_H +#define TESTUTILS_H + +#include "script/cc.h" + + +#define VCH(a,b) std::vector(a, a + b) + +static char ccjsonerr[1000] = "\0"; +#define CCFromJson(o,s) \ + o = cc_conditionFromJSONString(s, ccjsonerr); \ + if (!o) FAIL() << "bad json: " << ccjsonerr; + + +#endif /* TESTUTILS_H */ diff --git a/src/test/Checkpoints_tests.cpp b/src/test/Checkpoints_tests.cpp index d15203945..32b14cb93 100644 --- a/src/test/Checkpoints_tests.cpp +++ b/src/test/Checkpoints_tests.cpp @@ -22,7 +22,7 @@ BOOST_FIXTURE_TEST_SUITE(Checkpoints_tests, BasicTestingSetup) /* BOOST_AUTO_TEST_CASE(sanity) { - const Checkpoints::CCheckpointData& checkpoints = Params(CBaseChainParams::MAIN).Checkpoints(); + const CCheckpointData& checkpoints = Params(CBaseChainParams::MAIN).Checkpoints(); BOOST_CHECK(Checkpoints::GetTotalBlocksEstimate(checkpoints) >= 134444); } */ diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index bf2554875..7641b8b2d 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -8,6 +8,7 @@ +#include "consensus/upgrades.h" #include "keystore.h" #include "main.h" #include "net.h" @@ -119,6 +120,8 @@ CTransaction RandomOrphan() BOOST_AUTO_TEST_CASE(DoS_mapOrphans) { + uint32_t consensusBranchId = SPROUT_BRANCH_ID; + CKey key; key.MakeNewKey(true); CBasicKeyStore keystore; @@ -151,7 +154,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) tx.vout.resize(1); tx.vout[0].nValue = 1*CENT; tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID()); - SignSignature(keystore, txPrev, tx, 0); + SignSignature(keystore, txPrev, tx, 0, SIGHASH_ALL, consensusBranchId); AddOrphanTx(tx, i); } @@ -171,7 +174,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) tx.vin[j].prevout.n = j; tx.vin[j].prevout.hash = txPrev.GetHash(); } - SignSignature(keystore, txPrev, tx, 0); + SignSignature(keystore, txPrev, tx, 0, SIGHASH_ALL, consensusBranchId); // Re-use same signature for other inputs // (they don't have to be valid for this test) for (unsigned int j = 1; j < tx.vin.size(); j++) diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index e34750eb8..fc0600dfc 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -476,7 +476,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket) // this test could be a security issue. BOOST_CHECK(info1.GetNewBucket(nKey1) != info1.GetNewBucket(nKey2)); - // Test 31: Ports should not effect bucket placement in the addr + // Test 31: Ports should not affect bucket placement in the addr CAddrInfo info2 = CAddrInfo(addr2, source1); BOOST_CHECK(info1.GetKey() != info2.GetKey()); BOOST_CHECK(info1.GetNewBucket(nKey1) == info2.GetNewBucket(nKey1)); @@ -518,4 +518,4 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket) // than 64 buckets. BOOST_CHECK(buckets.size() > 64); } -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index 2779557ea..383616ae0 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -745,7 +745,7 @@ BOOST_AUTO_TEST_CASE(coins_coinbase_spends) BOOST_CHECK(tx.IsCoinBase()); CValidationState state; - UpdateCoins(tx, state, cache, 100); + UpdateCoins(tx, cache, 100); // Create coinbase spend CMutableTransaction mtx2; diff --git a/src/test/data/bitcoin-util-test.json b/src/test/data/bitcoin-util-test.json index c23befe23..3e9874157 100644 --- a/src/test/data/bitcoin-util-test.json +++ b/src/test/data/bitcoin-util-test.json @@ -53,7 +53,7 @@ "in=4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485:0", "set=privatekeys:[\"5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf\"]", "set=prevtxs:[{\"txid\":\"4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485\",\"vout\":0,\"scriptPubKey\":\"76a91491b24bf9f5288532960ac687abb035127b1d28a588ac\"}]", - "sign=ALL", + "sign=1:ALL", "outaddr=0.001:t1Ruz6gK4QPZoPPGpHaieupnnh62mktjQE7"], "output_cmp": "txcreatesign.hex" } diff --git a/src/test/data/script_invalid.json b/src/test/data/script_invalid.json index 7afa2abf4..7a50b8330 100644 --- a/src/test/data/script_invalid.json +++ b/src/test/data/script_invalid.json @@ -523,14 +523,14 @@ ], ["Increase DERSIG test coverage"], -["0x4a 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "DERSIG", "Overly long signature is incorrectly encoded for DERSIG"], -["0x25 0x30220220000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "DERSIG", "Missing S is incorrectly encoded for DERSIG"], -["0x27 0x3024021077777777777777777777777777777777020a7777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "S with invalid S length is incorrectly encoded for DERSIG"], -["0x27 0x302403107777777777777777777777777777777702107777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "Non-integer R is incorrectly encoded for DERSIG"], -["0x27 0x302402107777777777777777777777777777777703107777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "Non-integer S is incorrectly encoded for DERSIG"], -["0x17 0x3014020002107777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "Zero-length R is incorrectly encoded for DERSIG"], -["0x17 0x3014021077777777777777777777777777777777020001", "0 CHECKSIG NOT", "DERSIG", "Zero-length S is incorrectly encoded for DERSIG"], -["0x27 0x302402107777777777777777777777777777777702108777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "Negative S is incorrectly encoded for DERSIG"], +["0x4a 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "", "Overly long signature is incorrectly encoded"], +["0x25 0x30220220000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "", "Missing S is incorrectly encoded"], +["0x27 0x3024021077777777777777777777777777777777020a7777777777777777777777777777777701", "0 CHECKSIG NOT", "", "S with invalid S length is incorrectly encoded"], +["0x27 0x302403107777777777777777777777777777777702107777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Non-integer R is incorrectly encoded"], +["0x27 0x302402107777777777777777777777777777777703107777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Non-integer S is incorrectly encoded"], +["0x17 0x3014020002107777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Zero-length R is incorrectly encoded"], +["0x17 0x3014021077777777777777777777777777777777020001", "0 CHECKSIG NOT", "", "Zero-length S is incorrectly encoded"], +["0x27 0x302402107777777777777777777777777777777702108777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Negative S is incorrectly encoded"], ["Automatically generated test cases"], [ @@ -578,140 +578,98 @@ [ "0x47 0x304402200060558477337b9022e70534f1fea71a318caf836812465a2509931c5e7c4987022078ec32bd50ac9e03a349ba953dfd9fe1c8d2dd8bdb1d38ddca844d3d5c78c11801", "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", - "DERSIG", + "", "P2PK with too much R padding" ], [ "0x48 0x304502202de8c03fc525285c9c535631019a5f2af7c6454fa9eb392a3756a4917c420edd02210046130bf2baf7cfc065067c8b9e33a066d9c15edcea9feb0ca2d233e3597925b401", "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", - "DERSIG", + "", "P2PK with too much S padding" ], [ "0x47 0x30440220d7a0417c3f6d1a15094d1cf2a3378ca0503eb8a57630953a9e2987e21ddd0a6502207a6266d686c99090920249991d3d42065b6d43eb70187b219c0db82e4f94d1a201", "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", - "DERSIG", + "", "P2PK with too little R padding" ], [ "0x47 0x30440220005ece1335e7f757a1a1f476a7fb5bd90964e8a022489f890614a04acfb734c002206c12b8294a6513c7710e8c82d3c23d75cdbfe83200eb7efb495701958501a5d601", "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG NOT", - "DERSIG", + "", "P2PK NOT with bad sig with too much R padding" ], [ "0x47 0x30440220005ece1335e7f657a1a1f476a7fb5bd90964e8a022489f890614a04acfb734c002206c12b8294a6513c7710e8c82d3c23d75cdbfe83200eb7efb495701958501a5d601", "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG NOT", "", - "P2PK NOT with too much R padding but no DERSIG" -], -[ - "0x47 0x30440220005ece1335e7f657a1a1f476a7fb5bd90964e8a022489f890614a04acfb734c002206c12b8294a6513c7710e8c82d3c23d75cdbfe83200eb7efb495701958501a5d601", - "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG NOT", - "DERSIG", "P2PK NOT with too much R padding" ], [ "0x47 0x30440220d7a0417c3f6d1a15094d1cf2a3378ca0503eb8a57630953a9e2987e21ddd0a6502207a6266d686c99090920249991d3d42065b6d43eb70187b219c0db82e4f94d1a201", "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", - "DERSIG", - "BIP66 example 1, with DERSIG" -], -[ - "0x47 0x304402208e43c0b91f7c1e5bc58e41c8185f8a6086e111b0090187968a86f2822462d3c902200a58f4076b1133b18ff1dc83ee51676e44c60cc608d9534e0df5ace0424fc0be01", - "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", "", - "BIP66 example 2, without DERSIG" + "BIP66 example 1" ], [ "0x47 0x304402208e43c0b91f7c1e5bc58e41c8185f8a6086e111b0090187968a86f2822462d3c902200a58f4076b1133b18ff1dc83ee51676e44c60cc608d9534e0df5ace0424fc0be01", "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", - "DERSIG", - "BIP66 example 2, with DERSIG" -], -[ - "0", - "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", "", - "BIP66 example 3, without DERSIG" + "BIP66 example 2" ], [ "0", "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", - "DERSIG", - "BIP66 example 3, with DERSIG" -], -[ - "1", - "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", "", - "BIP66 example 5, without DERSIG" + "BIP66 example 3" ], [ "1", "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", - "DERSIG", - "BIP66 example 5, with DERSIG" + "", + "BIP66 example 5" ], [ "1", "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", - "DERSIG", - "BIP66 example 6, with DERSIG" + "", + "BIP66 example 6" ], [ "0 0x47 0x30440220cae00b1444babfbf6071b0ba8707f6bd373da3df494d6e74119b0430c5db810502205d5231b8c5939c8ff0c82242656d6e06edb073d42af336c99fe8837c36ea39d501 0x47 0x3044022027c2714269ca5aeecc4d70edc88ba5ee0e3da4986e9216028f489ab4f1b8efce022022bd545b4951215267e4c5ceabd4c5350331b2e4a0b6494c56f361fa5a57a1a201", "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", - "DERSIG", - "BIP66 example 7, with DERSIG" -], -[ - "0 0x47 0x30440220b119d67d389315308d1745f734a51ff3ec72e06081e84e236fdf9dc2f5d2a64802204b04e3bc38674c4422ea317231d642b56dc09d214a1ecbbf16ecca01ed996e2201 0x47 0x3044022079ea80afd538d9ada421b5101febeb6bc874e01dde5bca108c1d0479aec339a4022004576db8f66130d1df686ccf00935703689d69cf539438da1edab208b0d63c4801", - "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", "", - "BIP66 example 8, without DERSIG" + "BIP66 example 7" ], [ "0 0x47 0x30440220b119d67d389315308d1745f734a51ff3ec72e06081e84e236fdf9dc2f5d2a64802204b04e3bc38674c4422ea317231d642b56dc09d214a1ecbbf16ecca01ed996e2201 0x47 0x3044022079ea80afd538d9ada421b5101febeb6bc874e01dde5bca108c1d0479aec339a4022004576db8f66130d1df686ccf00935703689d69cf539438da1edab208b0d63c4801", "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", - "DERSIG", - "BIP66 example 8, with DERSIG" -], -[ - "0 0 0x47 0x3044022081aa9d436f2154e8b6d600516db03d78de71df685b585a9807ead4210bd883490220534bb6bdf318a419ac0749660b60e78d17d515558ef369bf872eff405b676b2e01", - "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", "", - "BIP66 example 9, without DERSIG" + "BIP66 example 8" ], [ "0 0 0x47 0x3044022081aa9d436f2154e8b6d600516db03d78de71df685b585a9807ead4210bd883490220534bb6bdf318a419ac0749660b60e78d17d515558ef369bf872eff405b676b2e01", "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", - "DERSIG", - "BIP66 example 9, with DERSIG" + "", + "BIP66 example 9" ], [ "0 0 0x47 0x30440220da6f441dc3b4b2c84cfa8db0cd5b34ed92c9e01686de5a800d40498b70c0dcac02207c2cf91b0c32b860c4cd4994be36cfb84caf8bb7c3a8e4d96a31b2022c5299c501", "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", - "DERSIG", - "BIP66 example 10, with DERSIG" -], -[ - "0 0x47 0x30440220cae00b1444babfbf6071b0ba8707f6bd373da3df494d6e74119b0430c5db810502205d5231b8c5939c8ff0c82242656d6e06edb073d42af336c99fe8837c36ea39d501 0", - "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", "", - "BIP66 example 11, without DERSIG" + "BIP66 example 10" ], [ "0 0x47 0x30440220cae00b1444babfbf6071b0ba8707f6bd373da3df494d6e74119b0430c5db810502205d5231b8c5939c8ff0c82242656d6e06edb073d42af336c99fe8837c36ea39d501 0", "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", - "DERSIG", - "BIP66 example 11, with DERSIG" + "", + "BIP66 example 11" ], [ "0x48 0x304402203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c022054e1c258c2981cdfba5df1f46661fb6541c44f77ca0092f3600331abfffb12510101", "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG", - "DERSIG", - "P2PK with multi-byte hashtype, with DERSIG" + "", + "P2PK with multi-byte hashtype" ], [ "0x48 0x304502203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c022100ab1e3da73d67e32045a20e0b999e049978ea8d6ee5480d485fcf2ce0d03b2ef001", diff --git a/src/test/data/script_valid.json b/src/test/data/script_valid.json index 4b10e3f1a..572814c80 100644 --- a/src/test/data/script_valid.json +++ b/src/test/data/script_valid.json @@ -690,16 +690,6 @@ "2-of-2 CHECKMULTISIG NOT with both pubkeys valid, but second signature invalid. Valid pubkey fails, and CHECKMULTISIG exits early, prior to evaluation of second invalid signature." ], -["Increase test coverage for DERSIG"], -["0x4a 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "", "Overly long signature is correctly encoded"], -["0x25 0x30220220000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "", "Missing S is correctly encoded"], -["0x27 0x3024021077777777777777777777777777777777020a7777777777777777777777777777777701", "0 CHECKSIG NOT", "", "S with invalid S length is correctly encoded"], -["0x27 0x302403107777777777777777777777777777777702107777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Non-integer R is correctly encoded"], -["0x27 0x302402107777777777777777777777777777777703107777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Non-integer S is correctly encoded"], -["0x17 0x3014020002107777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Zero-length R is correctly encoded"], -["0x17 0x3014021077777777777777777777777777777777020001", "0 CHECKSIG NOT", "", "Zero-length S is correctly encoded for DERSIG"], -["0x27 0x302402107777777777777777777777777777777702108777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Negative S is correctly encoded"], - ["Automatically generated test cases"], [ "0x47 0x304402200a5c6163f07b8d3b013c4d1d6dba25e780b39658d79ba37af7057a3b7f15ffa102201fd9b4eaa9943f734928b99a83592c2e7bf342ea2680f6a2bb705167966b742001", @@ -743,83 +733,17 @@ "P2SH", "P2SH(2-of-3)" ], -[ - "0x47 0x304402200060558477337b9022e70534f1fea71a318caf836812465a2509931c5e7c4987022078ec32bd50ac9e03a349ba953dfd9fe1c8d2dd8bdb1d38ddca844d3d5c78c11801", - "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", - "", - "P2PK with too much R padding but no DERSIG" -], -[ - "0x48 0x304502202de8c03fc525285c9c535631019a5f2af7c6454fa9eb392a3756a4917c420edd02210046130bf2baf7cfc065067c8b9e33a066d9c15edcea9feb0ca2d233e3597925b401", - "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", - "", - "P2PK with too much S padding but no DERSIG" -], -[ - "0x47 0x30440220d7a0417c3f6d1a15094d1cf2a3378ca0503eb8a57630953a9e2987e21ddd0a6502207a6266d686c99090920249991d3d42065b6d43eb70187b219c0db82e4f94d1a201", - "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", - "", - "P2PK with too little R padding but no DERSIG" -], -[ - "0x47 0x30440220005ece1335e7f757a1a1f476a7fb5bd90964e8a022489f890614a04acfb734c002206c12b8294a6513c7710e8c82d3c23d75cdbfe83200eb7efb495701958501a5d601", - "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG NOT", - "", - "P2PK NOT with bad sig with too much R padding but no DERSIG" -], -[ - "0x47 0x30440220d7a0417c3f6d1a15094d1cf2a3378ca0503eb8a57630953a9e2987e21ddd0a6502207a6266d686c99090920249991d3d42065b6d43eb70187b219c0db82e4f94d1a201", - "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", - "", - "BIP66 example 1, without DERSIG" -], [ "0", "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", "", - "BIP66 example 4, without DERSIG" -], -[ - "0", - "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", - "DERSIG", - "BIP66 example 4, with DERSIG" -], -[ - "1", - "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", - "", - "BIP66 example 6, without DERSIG" -], -[ - "0 0x47 0x30440220cae00b1444babfbf6071b0ba8707f6bd373da3df494d6e74119b0430c5db810502205d5231b8c5939c8ff0c82242656d6e06edb073d42af336c99fe8837c36ea39d501 0x47 0x3044022027c2714269ca5aeecc4d70edc88ba5ee0e3da4986e9216028f489ab4f1b8efce022022bd545b4951215267e4c5ceabd4c5350331b2e4a0b6494c56f361fa5a57a1a201", - "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", - "", - "BIP66 example 7, without DERSIG" -], -[ - "0 0 0x47 0x30440220da6f441dc3b4b2c84cfa8db0cd5b34ed92c9e01686de5a800d40498b70c0dcac02207c2cf91b0c32b860c4cd4994be36cfb84caf8bb7c3a8e4d96a31b2022c5299c501", - "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", - "", - "BIP66 example 10, without DERSIG" + "BIP66 example 4" ], [ "0 0x47 0x30440220b119d67d389315308d1745f734a51ff3ec72e06081e84e236fdf9dc2f5d2a64802204b04e3bc38674c4422ea317231d642b56dc09d214a1ecbbf16ecca01ed996e2201 0", "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", "", - "BIP66 example 12, without DERSIG" -], -[ - "0 0x47 0x30440220b119d67d389315308d1745f734a51ff3ec72e06081e84e236fdf9dc2f5d2a64802204b04e3bc38674c4422ea317231d642b56dc09d214a1ecbbf16ecca01ed996e2201 0", - "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", - "DERSIG", - "BIP66 example 12, with DERSIG" -], -[ - "0x48 0x304402203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c022054e1c258c2981cdfba5df1f46661fb6541c44f77ca0092f3600331abfffb12510101", - "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG", - "", - "P2PK with multi-byte hashtype, without DERSIG" + "BIP66 example 12" ], [ "0x48 0x304502203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c022100ab1e3da73d67e32045a20e0b999e049978ea8d6ee5480d485fcf2ce0d03b2ef001", diff --git a/src/test/data/sighash.json b/src/test/data/sighash.json index f9ad10e18..72d59a870 100644 --- a/src/test/data/sighash.json +++ b/src/test/data/sighash.json @@ -1,503 +1,503 @@ [ - ["raw_transaction, script, input_index, hashType, signature_hash (result)"], - ["0a70154f042bc75aff44e8731f286a9794331cced9f21ecbe909ba6fec8b220d14f1a343fc01000000056a00005353a126ba963caa7f2b9e07d2b87ed956a8b0dc049ba8374d5bbe68f4f2b02b9808392cdbed020000000400526a637ff51cea3078081a198622115e7892ea884223443643b4b838b9fb36a40fe595bd53c3350200000002acabffffffffcc43984edd96a3fee15be35ca5da72046e3e96ab82a85c24995fb4268dfee9b3030000000763515363515300ffffffff02440eb800000000000863ababacabab6aac9dfb37040000000003acabab00000000010000000000000000e5044a0200000000762cb167f99e568df7f7ebb9015ba2ad87b2f1d92a754aef697fa8404e3c2581ed87569ac8cb7b449900acf1e5b543f814499deed9ff2aaabb251194f2f385471272712495da501135ae8d1dd13134af23071428a1d017ecaa3bccc23b8b8b03000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cef45de773c6190236609b1a46b3e23a238ee1f644881a85991189d772fe460b7cf0a00cc7768096b6e3f5317b434d97a4ba969a3f90c5b374901f0355c807448a3015ca634cf338ffc2feff8e037c4e82b81249c30c74a08ab5f89537ec235818c9d7ccfd92a60d06ead38d52752db6021e0c02f7bbc7e1403c3a71fff5ff603063e4dd6b6ab831d4373bb8f7f4644855692e7d68406783860c73ba54bb870ca030c1ad82d667f5c6987732aaed4fe88e3430d072f2a8e08d483a1e8d7d22891f00a2055ac1c27678792e07b27e1f2996569530dabf9c80046d465a0ffe8f040923421ed8b38faf264e5cf75c80544bbbbce99d6796968c107cc4e09432e0173f7ee02277aaad4c0533ddb4f174656c042a6812ac4f441a9bf7ca598b3f1bce0a33363022bea6a4baa9bf55e8d44c7f9dcb210293710ae9acd81021221dbe97cc5ca54ce031bf2a0da31aa890eeb74483f08309e23bda66a36425c13550a743fc76c3d03c2030221b3475b3043581d4fcd74e1b69416c82209f63b04a21497c02601b43f1bb0020bc24a91d3b80c79646cff0658caecc76a6acaa176aa2e715a73595fa172991665c2a28f90c71d168c55ae4e1a949336376d2f23bf99bba51ea926eae2db1edd385c4ff301343921787353c4781ebda87eeca9770a132eaece42dfaf9fe8e0393bd5fd4f9ee7739e8d413507a186379e25380295d28e353792fecc20e121ea349eb7a544f8be1aa1a02996c3b191a4323e52072bb02ffc88f36bcb82cd3391f108347e4b1597fb14f871d17a94c10fda49885628e6678c2b7e77c7e773a9a0def443f3748ef2dbffbb418d64e1191678ec5516d7f016eb27b24ef8e26a9a6803ef03e253c5795c80343fe1869af0770fe2f7faefbfa0cc8c8027bc945632fb458f87de209d4d68d851420e6ce4fcc63ee754eaf89a0f6ffb0a449a4d83568e5c9196d02063575edbfb0ed4a11df8edc9b10089ea5fccbaf19c7af0cba388b61a2986263bfe976a9b8e755fbadf0e9a2c36926c7ae102666f3b94f738cf87c14e6a00ae11bf603c4394534208e1eb83c2a741864b0deed4de3acb760166feb4dfc8b8e40a19701d72ec9056650aa9a8a231d609354a28c3e1e645734388b8f62c2f40a9d9555b9b1b480f53aac83312fdf11c2f38c71656fe982b3bff986b799b282abd65768fd14035cbe61acec75b3ca23c8b8dfbc580634587fd1d8507201ef0918f41dffa3eb39235c7cced51446655923db432eaebb3a558b5b5b15d8d8fd1f506b0eb9cf154fe81cc7afc35dd9d48e7709171934e6b762e6485de27c2182a11f139fc7c1a8f635a9ad4b80a58f73d622866906cef14527534a22cd481427902f1d84ba5f4b38d793cc0085b1abf69d2acb763935d984ffd6922d43e61659a44070fc1222a97811d3b1a0463c3955104b4552d681a85896e2705dc28e981b7704fd4dff097c859450e191ca55efb5635c86f3cf6873a411fd4b8f07fc05dd932b1b14e403f422d4a7acefaa635a2f4ca1f406c839c05b17e89054f137985639aa0aa702253c769c613c63519a3e9e44e173295eb95118666633112dca7be2943636f60f4e4806d8a80268541c4b3b071b563a4b8a7cb77c417d86904e7e9046bbcc49373a9737b24000f4396d0428a46d7585bbcd067677c9d782c05d7e87b5c4ef8dc80e01e95acb6167f66f0af4c893596f0c8b4ef2e73ce8dcfa89d5f9476a56767477340c814afeee8348a3ec3a27d8b437604f47af0ba38de043666688877182d1d17c89ce78cf14d8335accebd2691f9fee2ab279be2675d8551756112c0d9cad3d0a496730d868bcf1eae9029879e58290bb751a6a540029eb72715def7cb39e53de75004040a7e6deacca212b062a43e47153360e1bab9d2d800c081e12b3eead416482e48e9d5dfb76499e14bfc79011bfa0c7107e8f67b9eb9a3f6b7db17b9650123a9a7eaa45e9af0689d9167c95f2507dbf13f3d4e771867809859ed8175169205383b1c15c1d20416314f91096674c56bea9dcdb5a97662f4730e624c1d6da58a5f1cce2e684e1a867e93125995c01720f804314a448393af530d2f2ce9eb99394fea50864f852153913f2f25d27997d10e8626407e4b6773d4872ba26303260025a5b8a09a92d4496d467dbd7accb3b7ae8b0e61e1510b0fcca2d728eef322f6c064057bb4b84fca3c9bc52291f659faf714c92a698bc7ac0d60cd6453d60e624cd88be1e8c12df00503eee1a643c02a3fbfef76b1d9f64e7b2034c391e8d57f41c8b1bac269dff9f4500549f1417b683b0c1ffee000477f0a083121302ef3fb23cdf67584b38eb49503b7b400a93e13dd7bf909d11425ce4ed20252335f12cb30067ccee4cffaae44f4ea37d2b37c7cbf988db0662899b58e0a1adcef7e6af074de8efcb394df330c", "52636a525252", 3, 1743887456, "1df6fc62e536c88c0914bc59cd253bf61755f27f7619f67a73463afe658f2f69"], - ["5f1aee89040d9e006691b6d98124825bea8956fde25e0930ada7a3074d5672ea6f25e59ae1030000000552ac52ac512151ddc5a7138896aab72e2216dcc62d140efe9c13b2a513882912d720c0aeceeaa81e210200000000b3672e14ff5aa54b8a4fb0b0793303b3ba69c169ec30a5ac24d9a1549d6ad02d2aaddfc8030000000054417a2f4464efe9b5a4f8bf14e1071c10a6762a40f9a98efe5da96f1ff85b6b4920d8b20000000008ac65ab51ab6a52526dbac29103382ffb020000000004525153ab90254e050000000008ab5252ab53ab63ab448098000000000006525263530065af338e39", "ac6a65", 0, 182861550, "ae8c39f404ab2c668e7c4f2340c5fe4c1e68bd463c5911b68b963e5ea3db1b5e"], - ["8aa10deb04f90d5e471fc5a829bd2adcd44f17439fc6b63c0669b2668e4d51ad67e50016d603000000007eb0f0ee7079ad6660227efb819c294a04d650f6dc723044a4680d20c346fc34948cc95702000000056a6a5163ac42a6917e538f417c5a35ca0aafdcd6b3d0f5a5640613ad6fe12d32787aa6f9220c70d22000000000056351516365ffffffffaf1313aaf41e05dac24290f00a2f862777830e96024107bf1c4942cea3c32398030000000600ac00ac00510a320c0e02b27fe300000000000035fa9305000000000100ebb50cdd", "6a6351ac536a53ac", 0, 1248756288, "40a5ee8d5fa08b812b9756bb95f0cab3f114533c978f8955392a13efe5820824"], - ["371f7ade02310bc501806dfbafa790004d9022295b900a937e59338d1c623089c4582a35be0100000001ab32a28425e75417726a878d55ba477c37a880dd030e81d2e66fc75ce3a821f330e63556e10200000006635100ac00acffffffff02e01536030000000000d0261a0000000000015100000000", "6a6553ac6a51", 1, -89315870, "1e19a51b20045c6a30ec4c46941b3c4055cd06435f52f7acc6e2320b1c8d60dc"], - ["21ea16d302dc64258155a19ad5b0e9d84ad43697648ebe0e85eeaaac4d8fe27d6567a5cc9803000000026551ffffffffa89a5831fc84b335db23d13d2fec9dfcdbc79b51b16c6475951e08c171ca8d200000000002526af27a71d002316f08020000000007ab6a0000ab63ab638342010000000005ac63526aab0f315df6", "006a00ac63", 1, -918284113, "2c812a691ab439532421a17c3980ed6dce697d58bdbe89799be545d03ca442ad"], - ["a4d00cf302f37150c4191465cb545b00b24a3011baa4ad1adf4c2c15cdb23a1f20ebdcd37600000000095363636aacacab6aabee4a08bca6721bc9f9636028f9062c5de108d7d3b263e17fdf8b8e268a2da6c537f12d040000000008526a5151005152ac7552652f038c8ad2030000000003510065024bf004000000000952ab5252536aacacab3127a2030000000005acac0000acd2f9a110", "5300", 1, -1377561406, "7e2803924edca56dd27896ceeb9a31771c246fb16f329c4155a0c5695ae279ea"], - ["448ec2fe0427d4262e976d6cedbce0b6295a493d0e46b9893a1e0fa0481f172ab4c176925d02000000035100ab20317ee55d536ff4638845f9f2ed101e8938a1ad04ac4c6f9e6064f28564795fa33fd9480000000004656aac53367e39fa1c0108646b6ae218e9c0d30550ed023d808a68ae28925756e229e68bb8d118a5030000000153ffffffff230331c1c94d7369e7c4f1f0d468bc46d5a60cf04476a0f0209c802feb0514b70100000000ffffffff0122b69b040000000008ac6363acac00ac63efbfd63e", "52530065ac63", 1, -1200902521, "9accb2591792b3a5517132d80e87092ab6e596b0b9d1b53e01b51bfbec1e48fd"], - ["a5dc7b5e0421c8f6fb20b183b2c8422fb7a24b85cc4ec6619c272b19223b9707c68ad2e6300100000008525300ac5200516affffffff3fb28ce660e19bf863fe7155986b6349053765ad058fbda5931ed175e7ec0f6203000000016affffffff7af9e1f0b0c24138cd1af05cc1a6cd743edbd7d2a085a42e08e5eca2944de75401000000026551243566610f5f729e148fe0aae8a39d0d9f4c26d966b3995a8b5cb9972fd989f8ce8cd3430000000008ac655200ac525365ffffffff02b58304040000000001654c67b200000000000563636aab5300000000010000000000000000964b86030000000080cb3f2cb847e1a214743d954ce55cd35e402bd0ba7e2112ba525077c68349bf101bf479c7664dc60a962c1f0d338045576ce74ef9f5223b7cdaee741469f2021daf3fa8c48a2e055f9b65836457e7aa7422bf1f709a072526feccf373bdd3dc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000183b2c4281bda92f7cc50a0bf3c53958f1f70f05a65448144ca7be98627cca7027944fd1dc2b032978847eecac2c4defcb0309d47336090378c983a120297e5660cf9e4f90b224a85745eaa0729700920fe4f813766729a9efb6f8af8cd9a1249f180c204fede8a39b4bffcf763a5d06f53d907820ea1358416a81f661f80ef2031b2c3fbd29227595e2d67e32d1285f73ede798512043f378322c228a23d6fc9e02159ceec4615d8d35eeef5fcf981032f38561beb09d19afc6e769595e49b6caeb0a08f0408f8822e11ee50b9b2b9d1c420c872406ff598a1a717b486544e6af5d902beb36c49a69b7e5ff3f902e2ce7c252085f5d183159964c7337288de61fdc13030368f671c1d6bc63a0155bd9ad3da9faefd4ea6ea8c500213bda48695eaca4c3032706feae9a9274e1fe18941a8db71d5088fcfc38dae8e99f046a3aca58de11810322dc7e08537a5de280e10da22c3a9724cd7d40b00f310ed397d4d202c7a5d355030cb2685d0bb6d4f719709cdee7f7072457b98b4395efc5c57f52821cb6a1289c021b947a7f3c7835b3aada9d1dcea8454f9744c6eb03dae1086619fcc548a40ac1581a244ce82ee4fce1ac37bacc04ff67fec0ffb9c98ba70bc082ca9b06c2a80b10b314426fdaf7ab9fa5caf1ddd8fe68cacf6a6e951a44ab84056bc3019c8b3d4967674539fbedb29b53ec28568f444473f92959ec64a7afa9449b9505be51a3f28b495c95299a18daad4492c01b803f05fbb1b0934e65ac73f7e9e3e76a91aea9c2996d68b807c9afbd1be24512f33e44cb6866344a1838375a3fcbc7e61240438eb0bd8e36863c6a0c5efd1f117aabf247ff7022aad51ea919eec0c76d9a83dea8a34505e337723addf825a398261de1e4e65766c46ab5105674e45d7cca7b65194cc037c777b2002efbd82597118e69c6e756435b40edc246d9541cc6eec1ecbb384f9798124597c818d2ed5fb0fb26e18eead130c79e36f2fafe63001e00bb4fb54ad95b45f8a142b900afaa919c2eb7d807868023f8d8fb8404eb56f2fea1b19b45b11c901088acd8fe5e1320c042f591ee8da8d3cb4494776e872c2b4ced1d6a8ee6f618723fffe2670631527c569029fb8e7ffbb2c906c513e05301662552624a23a81e6e1283625218e00d35921914864e3f8ebb80cf8ad09e124c2d679b378315d049bfb6323c28291fa873b4cf1f899aa975e4299b15e9f8977d863318e0c1ed938f46fadb59c61ee3a168f6c0ba5d24ed78b8eee01a19acc412fb7c97aab3ed7dfc0d21ab8067bd31918bf685580c19440197fba41c6a425d3b373db9689f6546ab22c017819d15020c8a03721dde165f769398657b106f533b8b7659b156a463eddd430cd53bc529ca9824f786f15b8dc7c2d9b91171383a8e393fdd2321171ef78f69572ecedc09fc20f1fb00872315d6f2a847878c6be380a88acada41785a5099275ff480951f3a165e406cc64ec623831a547c482df875e51f0855bca5f44b68fa39c52a9d7fa4540ec895573d2abae8426e65b1403a2cd6bfc445374b2d43eee0d2ff50d31f0cbdfed035b0e9e0008e5f23c572507555e382bae55ac62830ae8d61494cf9beaa000477350ff7c4b1653dae6dc706372c468737f347089496fe1e39bd0432f739d4b4040d50b9290f41b671af98e4f1784938a889773d5531e770f2cc7daea311dc048b524f91b517a8220535165ad27dcc1413ebc62b9588142c64dc8586e5b3afd5ac58d448b953e2c837c1e03609dd3a7778be3d179996d32cfd648eda38a4e2ad7c98fa696e48167804dc2a44d76d63816a3b4788000fffc61afcff2f1b85a4ec2d6570e2b5cc025539af1519c925c235fee21ec2c5b452200211a8430a176672c51bb9658d470339f8c583413fd25762cb1a40789cc2fe2210cc1699a6e329d9ad3569dc3933f48ea413bcd566a0332e8e9b0ebb4fb96bf3dcf6fc55d7d686f72e2286b472e26b008648c2d071b3b5870b74b81d504ea7c5b109ca5d6fef9726a6eb9fcb36448629b524bb23ceadb20f2753c97dd180a9801695310140716cf1799705193e8d48fc3b4cedb1a38d3d6f1da28802f49f3443a584ba3a5bca87c86717a5b06c9a65b4da8615c6312020df865838bf58ef87c096c76ec4d2aaa496e27e6abe1489407b12200c90aac6a10286122c08dd1ef8b51ddd06c2b86d0c66cbb80aba301a2e3071207598bca282d0de0d7be47d74adbac932799266c6db171033a87573a1525a0625c7f3adf4593cf43de4966bc2216c6cafae00b38416b067881f9cd8d3fc1a0f0313dbcc4ec995a5a0adf759f2b45b53e96a6a6c8cd0c3dc99f396f38a02a7e8379aec8c79f6703c403ab6af7fdf16d1f5484291b95cd07276688d273971ed9120e0ffda8092c7803bdd59610145d5c27557d4048ad6a509", "5152", 1, 1101231466, "6b451dc3e1a929fdcf80a8bc1c8417a62ddcf5b1e8e68343c0c68add5c6f0cda"], - ["359e5a4903336cb1214ef0196d97709fe39e289f022a14029d2cbe73ea40e4612574ed8305020000000665ab52acab00cfd6a2c5007ce9bfad72f5a6f1b64190c47600b3c9279fb8eb21e7dc18761b92a1c4599f0300000000ffffffffd5f05efe3e822072f6b3bc7cab03627740447d50e547dc492c1b644e9c700a820300000000ffffffff03ee183a000000000000cb3f8704000000000963006353ac6a6563ac6daeec0200000000065200abacac52eb74a657012f32cf01000000000000000000000000e347160b0201e32be95a4a900375c87a1e70a0bd7561c559ae03c26c776caa641d63fbf1cb09ded9ccf51ebd3e8aed5752a8b114dbabce322005c00c9e707e5b86efdd27b522e79fabf98fbe5a49d61f389e26fd930b1602e22797ae51cc812800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cdeab72c762f864814f7a0c8794d60bad4a89f59ba77b8157c913548dcb564fe4cc7da614ec789faed651b6238608359aec02e921875f4d68622b4f2bdd1fe183544258f85a2acfd6d7db81ef211278d810feb7ba71a87d1aad50c8de4c1d7f6a82b30e5f1976ba739c89f3f8371f1ad51731071e75fd831c8c0b23f28b4b615030cb8c1e2bb4cc84239cfa06e54ff788e7f3810ad5e41c6fd5475dcf8227f3fce021eb9ada27aacc4318f29389e8106156d43b9f4c96dd47eb86e5c5c747c5a40da0a2705ebe216ab60e080f451f901dfe4a1681f9278a716434d458e3d7ba1ca81772465a15a65674c705c7afbce0f41dd74936061a8c6dea97d633a352aaa15d572031b88909343d8b603ec65cef6c3afae7b30fcdd1d6dd55c4465b1a285df882e7202300192fc94cdcfcfe404cfd9904ac5d4f65090b841d1442d5b142f7536baa3cf02145f654e224c8a5eb7c5717d5ba18040b383e851430a42d16eda82529c9bc51d0311c832b0fb625edc84a3f1ab609b7fdb93c0d9134aa0deb7d214d6543cda7317022cf1a470c6ee79fc479344fa28ec804b8c0247e65ff9f4c6b570b2dfd00fef740929d2219e916e42a52d624f8b67fc4d661357e845b5cb747364337b60dadaf0f3bf774cd3ce36a25d02557054459a699c9d80f83b299a9ff4cb8941ba800ad4ea41c288864c173b74904789daee7dd20510b9a87fee893135963b2796b743c35afe38567f264e4b18931db612bc2c312652807dca9432e995aa49bcd6d846712dc604ec1f50731bc10d0141784e2623dbbf5b802810130893b6b22ca26dbd82b92eaea6473da05a0c110123ae2a168eb71fd4be9c550a2c5a50da6d4251334ad23256679c3a8191d0d58427715ff6acbeaf905f997a19dbb46c2de2f006d1936cecc1c1bd580ac8b4f3a89c22026e9c7a16cc775f9a60a06d6f33bf1129a073b3d929c67ebf833d81436a2d4491117155ee2f4c816461824a5bee25598985d3ea25af1671b8bd797220f916acde13a6ca8b7cc2f6b08caf9e4e2564b1b1036d45173b5328d20c4e5de1f72f84232076f0e607b132404d3120b301f0e59b6e2a5706067134b5a38d56944646474738f139801c9195c8eeafdde7d009ff14452d309a35028bdb762708f0eae985e593bc097331a307cf1ccb26420f5339d3f326b000ec8dd3be9b0c7f8f9bbcc55ce92cf437e66fe64959aba7d14a5a9acec94c8308f61fce042e111ee013c89240bb78f5654eec3e3aacff61c98f076c30e2cc191e44b0a70036e20282d1b419b6f9777bc76fe8b448b8b9936b896f490c00fad9fb712279aa102409097b9e44ba649dc10de4b7657c06593beeab316fac002b80d8a643037c1fdd6def7d5540d2b53cfd44742d03ffc421bce4f97515fb046e8b07efa2adfdc4e2af645bbb3c59d6a04f327bdfb7302042aa9046c88e953fcb70a7698a491f34bd2e7b127d3bddae35977ac6d4ff7588d44db3f0020eb9e1cd0406e8a5b6de3bb1d03ade1e2a5a3dd337c42e892444c2a0ece6b0f428b668b37a209c791523dca42e5a66f87d82ae801593036bde09b25954e160956d4c0564856a0e58ca59c8cab84a7bc4f82f6fab4a4391e0e6963a1e81103c0c597918a73b30e2aebc80b094a6d63832520a2a23879d32de39c3cf5cfde4e7f62e0bc2fc3cefaf98de2ce5f9a77e58223bbabafc35fe006b51e45269a49a20893500b375b09d7c80fba7c777ac75909c324e951e7ac9a17194e14aa2171b6f4d32aa0ec4e0e21e459765de38067bbfe124029ec5ad7ec7dd2610d4ef45361aa2ffb57da87944f40e48cd88be4bb3a5f7a885a1b381874abab13f66581ab8a0045bcc1b25f1471d31a101ac4cf537b0221173ac7481fc2344aa9913039aad53eccd537f0f8de0cb0f1bf9f1c86f90e8e9cf3a711ec978cd5404011747cb50ba4e26b87a288efd2c4f893e0a3835046a89bae6f8bb62ac56a62b8b7f71cc898a9011dd5250ad0e02e217c844af85aeb048ed847c9c8d3da7a72653f610578e92c5d85dd5233930fd80ea4ff407f0f3f426e1296c8f323a3b0643d8f25e6e2dfbabc4aa23d99d611e6be93b5f0d2a65012ce3cd487b4f6880e57195eb61f6ff90395008a545fb26ad2e76009db35d74587f8a81c6509d28481b7e73c2eb4b44c5c0a3e00dee6593ca5d4c741e123c68ba7c3eeb11e406b8b21682502b2668b2bee299bebc478ceafe9938c783d44d24d9c5e63c00143bc23ead1cd80ae4fcfd731d5ea70a2a80731398fa089553810b8362e415eef36c84dfe11976cf59bb707335ef927997dce1fb01f538607ad2d1a93a750ac2bc14984b48a6bb1750f667d8877e1cd413c63020789cc4d17f42fb26634f0555d5fb549083474d97b300e3abedb63f31fcf3c7ca304fe247b9148d016e5e59fe881d0f", "536351", 0, -715905776, "e935b698dd300074fdb13ba315666dd29447ec58d38940e7b8ba4dacd5e334ed"], - ["9519f8e001ecbc12afb8587d14dccc2cee79c27f67a3ecd2c23a3c1d9c48159f9a5e985c5a0100000006ac00ab530000ffffffff035c3c2f02000000000451ac6a53a1ab8103000000000563525363ab7517d60000000000046aab006aaba084a1", "5251ac515252", 0, 1197587702, "1e9bcab050160f24c23a6fc92c35d68c61ac8a6a79aa2601269ee0f440dd8846"], - ["9aa606a10335aaede0ae02eaac79306df74d0efba77301e7613f8dca6438b1bde35f8a7c79030000000353ac531344edc95c232498baafd493105b921d8f979726f9220e5dd106c8b984a5c1977986478e030000000565acac53531e1e579e06872bb22e300b6cc556622fb4aa93a586133c67f60ac413ac010ed2dd3e645c00000000075251ac526552abffffffff02b13fe4050000000008655100abac526a514b2ac0050000000007acabac526aac52180e0d53", "536a635253", 0, -582918023, "3c5e6ad865968e86c36681d2c4c49ff62069682f59c912aa7396e0865ef09752"], - ["bc724fbd04799ba10ce2324710633752ac95c8f7881613c10c78a457cc2aa1cf28aa6dad750000000006536a6a51acacffffffffe43a631bafd81d2947388582e8ac9704590e4780286dd9f97e984d6bb877c8b400000000086365ab6552ac52acffffffff3f53fe85f292d997605073b85f1befbe78f0adb627dafbc6fbbce4710d116b170200000009acac650000525363abffffffffcedefb69d8538d1bcc52497530c66d74119650603755b6e4ae156c39a9409d220300000004006563ab7382139704123eb30000000000003f449f01000000000751ab6aab51acab75fff1040000000006526565000063183ff60400000000036a6aacb21d6a15", "65ac6a535165650051", 1, -647041218, "f311ea4b987f1153052c4985547c69ff702390c979791a258e87e81607a9e2a5"], - ["d5d46ddf03b33dbfcc434909ab4cf4ac6bd764d80f075b934b96a18ed26426331af459ec7301000000036563abffffffff551033d067b305a41289696c2cb44b7554da3a5870e3b7d9f714320af4b7653903000000036a0052ffffffff8ebc5b0f63d3730f02b2ef3311731c91ea28f275d6c6a43baab9ec0f604a49fc0300000008ac5352ac6563ab5152bdd1a30285ab3904000000000200533d3979020000000004535351ab343fc798", "ac53", 1, 1912077243, "f70536783e81912d078d88ef9500ebe622207521d5fa3b0331810742badf05c5"], - ["7a3168da029ff840084e48d8a8619e31758fdd8996fe7a58346e6e3d44d6590d74a6a740a00300000003ab6353ffffffff6e5dfca760a2679892e1bb08a2ef327c16b168c1145a2e59c49e66c7ebfd191c0100000007635353ac6a636affffffff0413badd0100000000086a6352acabab00aca5900504000000000500ac63630000b98f0100000000096a536a65516300000063208b04000000000565ab63530000000000", "530053656a", 0, 2134166525, "cfd0515ad67f943cc5a0b303b1ac796276fe56dc6fbf0226122165b5b4f84a86"], - ["a40328bb033251069c4356d1e2aa2be7454c8a9d728ecfc4501ab6d6537e9ea4d53f4ea6b6020000000251ab5f3179c09c9615e481764917fa419b95933e41fcbd53b28cb390ab636793b65d71e1f051010000000565525352001651de4d2a8d5530492a602824ae4e5332df57478d8e153568b0eddd9b47e4fcbfe87778000000000553006351abffffffff02265320040000000000273a7703000000000351acab00000000", "636aac", 1, 1707062048, "60fef62f6cac302374a84b8532d6018c4776f5f463433ee486659e838ce03046"], - ["b837bb8a0236ca86ffdb245fcbe155ffaa5b838160e4cac9ce049b4367901cde8c525bc11a02000000025151e12d173c49a75d943f8a6a89c70e38942eab710acb8e2366b52de94ad317bfcecf77d0fc0100000008ac6a51ab00ac5353ffffffff03cb778d000000000004abac52652c71ce03000000000351636a3946b5020000000008535151006a6a5252bdac6629", "6a0053", 1, -1241762288, "fb04248b744ab15d50b45adee9912149233c36674c4d9e770047bc3c8729c403"], - ["e1a6240104a8fc66fae56dcb20d9a2fde16e7860817c214cf6d90b11c21d575f90b17f575500000000055100650065ffffffff0a3542c639ae7ddb38a065d8a33e4e1e0f09a70975114c520dc3a0f855b4df1f0300000000023d5c6e4cd5197284ea5f0177eb7937367d779c782a08351071f3e1aaf5acdc7bf8e6510100000005656500ac6affffffff4a8cf9d1b4985404c1963415e1ca0373e5e7136a580c3df39dcc5247ff772d8f030000000365ac6538b25b4a043d3ae8020000000008636563ac526a6565183da60200000000026352edf7df03000000000352ac5194f6a0020000000001510000000000", "", 0, -2143379793, "b776d4af569141006018658107b8a076d2e1efcaabf189329a5694b59a6615ff"], - ["9ea0f02e033497e89099eedb0405d1099f4128b4a80d940c803d6996211f203cc752167a0a0300000009516aac536353ab6a52ffffffff27e0c2faf88ef90fdd3c66e264800cec982664e7a10c879a76950d8e431aef9d0200000006656aac000052f2de85cb33b66e6e34450c640cbac7bb9e3e0130639670345851cd7f8e13ce15496058f6020000000153e4a36fc003bded240500000000066a526a6552657dd9f503000000000200636f5907050000000004656a5300863268340200000000000000007726c702000000005d0693996f52df1434e58b3e908eb6af24aa8f30dbd8582e5065b833108c7f1ed6788f89e34aa10a66467558b3a018f4a56f540f45a677747d3d08e1b8a59b8c139e6eae691dab788a12bd54347af4605caa23f2572b82fe0ffb557201b154e7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002906a5325863c94fd5e2601bc347fc80054defdd5f94b71cb1633c8912a0ea24c85a63a2e154d047856f48ed3627075d09bd4be96cd398515e0129456ca9bfaf2303adf534fe5100f7742315e15abe212f7a4a0c66f9f73054a6ffa88c53c0d281326e3cc33ae672c97d3566778c2a3eaa19f5fdbd6ec669236064c90ccbf1c2030da61e9c1e1f0b9018ba10c209a7acf52b67a95db41e81eb1d0a85823148cd10022573236cd539c1ea5d33ce220c18ebad1ae8d70c82c53274fa79bc2a164958c90b2afbc6ce33ce8e71236846a9abfa7661eac0322985cbb29e078dd42ec500d91d2366cc383638a99bd33f34b447c18b66487418621de0e3ba10e88d1332991996032240199b45b6a166624014c05c4d97f0f386f7933839d7292e9f29426b1fceae0305efe54aa68feeb142f7b615ba58ad29ec9baaea8019e8e5eb090b87568a4e110310e326c4c4216a72191acb21b235e8b8743a2effcd915789ba4f68dbac3dbe5602066534ab587ae8b70e46b7c5c67615a325e194d5396b875d58f7c14a6582f8cd0207d2ee2c1c03238d60344da6abda73609c347ea6e9b075ed298d26f4e9f2d57f70b1f43fdd7d0270497e6a410eddeef12c3aeea7022c090038bbfdfd9425ca92af2f749c3186af34012b1e9d1a932ad6afc3dab6d75cebd85cb7eb5f7f8f698bfd5e3a83f355c590cb3bafef03f53a522d4a5bf15eb18bd7b552e5b6271a2fca5d268f67c1e8d77feddbe6cdf28be12afe65e6c8b5f345e0b949c2b5f8d94240bb5c991f19fc7d57944d5e1890494a4c34e2bf32cb70664d23f92efe93065b03b27472fe41a131f08d837718e5db02144189fd22b784ca546eca221dfdfb874d6ea55b83d9162177f0055e6dc11471ce842e8a7daf08a42da464af989f90261e5f8224ddd8edd6ade03b42c3f83aa0f798b457b5d58322e308406b605ddb087d52bf14d6cbad20ee190beba58830f53fdff6267dadf073d92b5ba1f86441d9d049df6915dc06db870f7a1701de411791d40039956186bb658cd9a364feb7fb1829d9590225b2c33b39d9669a812e5d5c3e55e6e2622513ebf420fc381971505d1cbf833ae920ba070125c7f69548a932a9e27d1f78cd2f9d0a3e6cf671e2f5aaa4c3c0d140bc3b84095519227141955c2680388483771173d6fc5c68fa4d76864faff6862442724912a2318910632b0cb909519c488ba8cbf559f34c907be8320f633ddc0a7603ed9b020dd464fb36fbfbf943f92453aebd00a9d42d7bc5ef504a902accef2e7c91be667de0a95a4b40741f86eb93c8bd52a3fc03da633f981bc81a430f46440b3d68fdbeb9a94b6265975612ee500e4fb77b92827dc3a185b11adc2fa968ac961a6cd3d8a5fc2008c480acd339e9481e207b53e4058261ca97feaab2b4d46da6d5bba16607f2dc0d155f708baed7add0ab47f059ab91ae08cabb6fbe484d9000ed69228d9d4ca183b48e849d968b5e989a332b526ff01190044f722319a6db0e9eb0a95a01d9e26cac73f3c69c8624a8d5b8d76eb4ea56f3fec554a43e0f7a700c88b38c3c4b337cae1d96990dcfb68183be6dce974a577b4c30536b688a95237b6e4f3ea8238f20139b21150ebf765d706d682f4ebc79bbdae0aebbef616eb37598472762598c36e98e923ba958940b4d9852453f26927bd1f1677c0f41485a3476e8545b00468da71d872971572c6e4381f2c1abb3ad00ea3516a1ee2bdec8ab969260b006fe679cffa6c60565a87233dee0f7930e51567b884db0094cad733ecfb5f96d243d935389ed479db33e1ddf658338db2e00a1bd11daba571666eb9f8cbb9f747b535bc1615f10b49fc71d637dad2e37926cd8050a86cdbf8d1f373bb2b27053729ddb21377093411c6f9d3d11b4c35fc4197ef0fea907e70bd5c2e426595aa96cc8e812de2a447cf3b527e9ea5deadb0671ee76db14cb85688b43342bba0fb315b042c0eab81ac494e4187bddec4b3c82cfe3134583ab8a1d89bc3c0b4f1d86ae0e62977b8992386c36009ce6b591ed8e3e14b84d3e6801a59138ccdc5a4773f6b2f40339163abc7eca17d69057f381732ad088bfb57bdc6d349e9c835d5b96eb38e73e279af79d87039d8095a15f1804b68a78cc4ed800d7bf303db5ada2f6529bfba11402a7a03d22adca2038e84fbd48af597a3b7f5c0a917fe251dad26dba887d286c272faac129bda1f7a3b91a11047f83103209f760a65d3975833c1b19c2a514829c8adb9e88c70714254ed5dc081915264f85b19b1948dc3b67630a4c318ee5eb1c7431890500000000000000000000000098d42ab58c3e792a5d962ab420aa7c3f07157a8024bee96e308c425acd3da81e9a6c1ca722fd0ac4d5ae0b0a1726985267ce8b32f415c6f3f685f1130c46a69ff775030033f7887f875f7b998d223aae44719f31397f1a8dde5f6c2b225e2b1a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009cf397bb2cbee8f13ad61b08184060828704c0d39a6493ac7a8010e2bcc8329269692b69cc263c8d5c3e7ea8019f3482897e59d3ddd8f1594154556387f42bd253f7f3aaf94e1bf72bcc126d429104f3634208fdcbb0f0340ce697b2cca8d7e697b4553079fd3cf3116f6f697141df1add14191b4e0d06d0a34bcafde872ca8031a4ef22a3cbe8c8bc45ede6e58a1c787a0ea9ecfe441948f421ab5d56396f5480302b9b8e8fef180c557d653b7d8de85f7589ec477ac557cfe3c28151841d620c50b19384ab2929c044170c9fe72f25264696a06e0c276a19cc39b0d0eb57875dec605326ca4060ffa8b2a41329f92206085c0bd1417faf6e32702883176a8a4d0f103286b79255b8de6cc4a705f2a8636a43fa3eafa2f50702d2f26154f247d22a6300305a56719a90a9bf4f09f76019cb630f73e5b0ac4b8aa4b1812fb2bacb2332090031ddfafb8bbd3febbfe5c93798cf307f98da2f7188c688034431274d9d0b5cd600221850773a9d888ede695a8bc3116d14e9c0899a40b5036e9d615e8777ecb828e030c8ae1823101990c9904a0b741fec979402edb574e730637f5196ab6deec9617e12e4d304c0c75b17167fe66dac51fa77138d86f1c131160213b386249e55ba2424006aa28aed3d47d48b766efffb8c718d5db968cd7c03782dad8d2057e08b370a85a7a1c481da33a8f9af140a11372abe23435f8e3c6bcd61883666aa7bdb8a8076b6fb72178562e75499a1d0343e72b7c18b458eb786da1144d65cc4ea12fc15fbf5462a0abecdf9dbc9308185a03dde3efa91f02e11e1dabc1a337fa0a35d74251a45e58b75bf8a9c65c129a42f4e571be4c482e51f5a15a6070f503f2e9cd9ebd3b0ea63e422b15041253174487ebb58aa783afb80b40c010d5a952090421d12a08239108dc86335ed41273017a069bb07f64af540e1c0267347afd31993d77ea3b996db9dfe6600c322670e6701c7a25791aa2f6f8a1076e823abce13450e9a3bb5d3eaf9e9f4df9cacecbc45c3e908ec0d2c3c5d16e9dce515895390d8784fa4ad2ec62234df9d85ac3bbf462fa417f9c2760fca4870d7fb8ae35669d550291c10af02cc3305499b19771d7c8243fc9b1cd64156112d57a5102e3ce5c73d35353c36e88fa41e764bbaa0e6e3a4caf133849ee693aa9c39074fba6147d33bafb73806ea7af600315b2745dbba653bf3513b9f5ba1a082cdf3a2eb6f96b354c3d74750bab52de1a78e91d9633ff16ab43298d28c521cb5cf9d990f4f7ab7a419c04e439b7b6ad7772971eda633e85661196c27098080436ee110cec5994de6f4da45ea0a6b0b05a23f82dcffd59f595883e4a743b398a42819c2a9c41370a57ce560f592f0c7ce35fa04628653f9dcb4554696e821a9a7361e71ee526a07a44f758a68cbf45b2d21601a647f85705dcd674b52d7820234466cd3a71dd8653097727cd511f93166213439106a4471cdc0c225346d2b20759b07b9fcbb428b55c4947211821ce387eda503e329a10c37a2d212f0796f5564ef79ebea0555a597f60aa8cec1ddd6e21751afe691be79454abba6ff6dc2361fa3febef4cd9acd78e207aeb07a111b31bc0ea5f0dc2d0e4ff384249ae7baeba14ca8f621b62438673e107b3d2f26b5823098d11ed3bcfee2ab8f0500b5f0061da3b9eb25558c5e0e20249ee5f77d6768f252fb1690bb26ce5106de2d65286a693c9b69b1601b8f14388b9a27ff7b58d8f09b60a4e582e195d8cebfbd0510f72860b1464bae9c0ad75e010cf71eb3a83b6630057c7deb97fc2006c274c59863f08920c11dce7143d1bb119401905ac2ba8edb7c34754a5048381c2ff16dafa394bfe4a9c8fc1a4319f46b0f6ad2f35725f1060030cc12adf83c7e98a7d990f5fa51210e38ed658c8c53fd78f42956d4ea40a5590f30e261435d55b76720ba2b6adbe62b1a5298b04f6aa4d50623fd42d9bc0f1b5fc384d62d931e17fddcb4adfee96c1e3f0c61f1ac5eb5ec557a4aaf1c1337c929eef9b736443927ed16a913186db653c6cbb893249ff021665311e0a881b5f42d0cb0033a306506f95c0fe7825ca117ceab3f58ac64127af29f5b0fd2eae6d9dc77e552ca288858323850e4ff351316179e187b87b5e071188f92b5b4e6c2e397b08f8926418d1bdfbb90721b4273ec9e8b964a5a820573104de532cc47cacabe05180a054a1a51328604212163e904e81d8da795fefe06c0f0327badd1e63f4227c56ce4591784e2903a77649cc177835d286efa6aafcb0b21747e75908c2414e892231a6f9bf4de18e81ae22ddc70b5b7bb3c1eb362c348f28a795bd3d62b77eca7c534d977c00ed4f57f52de4c3a1399b7a9b34de1ea17fad3d6c36da97a77536d55977290ce77fb16137a39cee48b3f6feb7fae49da418177422e635025590899f2100", "", 0, -1302028225, "70ddbf7fe88db129ef81794820f1dc74d65306c0e2c1deebcf32654add29b83e"], - ["8269f5ee034788b9f5fb25c64236c653f0d1ff1ca69b6fc2c88948fe74197ddf2c8130bb8a0200000000dbbf87003a15ff1aec446f5077059a5f2448f2579d9a4d5f8fcde0d55aead739f6b0230803000000046a5153539a3df1bfd3999ee684ee68f7ddf41f3e9e4650258cd54cdc62431af51727328f334e466b030000000400ac00516af82c710413baaf020000000002ab00d339150400000000056a52ac63acb8906e00000000000852636aacac5100acd30be10500000000046aab5265b165d339", "", 2, 221466486, "b78bfbc27d5e94b20e2f4004c8911ca46f5e12710bc1da6a0c2711b39e2905e9"], - ["d2d4e72102d0d847b8d88c7b8ba4d9d0ab4bae9b17f6f2d2c5d76eb9296b43e5a83d0f5e5e0200000009536a5165516a000052e734693510fef10293f52ea7dac6ccff08cb2499b5910dde7f493f1d443773a0272723f101000000026a52135c447603cfac0602000000000163f4471f010000000004650053519a813b0500000000075352535152006ad2ad5f18031f5c30020000000000000000000000007f9a37be262109336617857c6ffd24edb63c3e2668b60642ee5537c60f6ebaee4afb6fd34a1eba4f3f970e070ea36a180aa145aebc38548fcfd2ea3374095eb8bcfc8fe5cc72f5caa185a51f9899e5e036444829f46ba005e705748701050fa2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000729be036c04c80d25bd90f5b8a8283be6909af21cc907e5beac8e01d7806d3dbf7819c7f3c11cffbdbb7237aca7a47abad6dca6a75a55bd009c98f5a0ba76fcd772ac2de6d9867eb47cec4bd6ccdabd84673f2b26f3d79a16f8945791d109da145a63e89f888f593e8dcaa0f6a065c2de50e9dcf2feeb41c6a835fc0cfcbdde0205ebace982ce85d7f9e05a17e1e107a9034b3b9817d9a581e41568c5b58040e202039705bb43faaa71afa7fc7593857fff0450bb86bb31ca39e316ac1d033974fc0b01f7b8223e74e385442335de0912a53aa279db49bf72201490fe9019ab07f45717e1702c9b08721bf85307f4da844670ae42c015d20d70b9a182c1360fa485a6022367391d8e000c5ef1bde51f2cc2c02311703481ae4d050fb394420b7ecf1def030df67041792102a31ad130179778fc60a067ca61279c1a4f041333f1c1f1bd69031d00bc365211fafd3161361604d9fdcde9f40fc5b3fb51c2b2eb41f644c19237021c258667d07ba02153179084d1e62a38f2a7962c5def866e1c2db1e4e30ff5cb030ee7d67bab4cc17435abab55761ba62c4b171bf6bed3ab338ca219f05c04a76c9380a6cfbfd8fe739377ceb1c151e590271074d3dc2e2cd9015c4ed20b5e5a5a37b244bed575b085326738900637669d7307e92c0f63143cb78ca599aacaa9a8a8be0d8f91577585ad92aa7e61ea88cd9b416f646b45e3da64a4c605ba303822f5d0549d670a7884e80b02edfd07a6df63c5a7d34cb042a14b3ed7c11194b824bfc3e5d156bdb35ca3f7287efd775f712c11a205820f728de0a495b0c90cf515d0ca7fee32fd368fefc7f5d618b85408488e98377bef966184af1414c1feac7792c8b957385d20ae551b50a948c72f13d35948ea6d7cdce9cfbf73e1f53c06a1c0954a192c79b177844cb57b8dee9474f83fec62d7af3384fe5ff982c02ad1de22407f2c500c8b677890dd760da8f4b8911d2e56465985147a2fc9f10210bc6ee353262b3922d3cd3512716e69b2a940e98054529c521ec3cdbe4fb5d2fbf1160d90eee6773b62a6c20b3b99bd7990c467624c823dfdb7444bd2a689edf798011bd7afa66290f2a4752027f3f600f995c08a810678e599dddfc72d3a78879c0e20f81bebf39a0a907fb72f14165f65aeac340696b6184a982e4a20ae6770f6b3b50131a37023ffd0888d17b1cf5ebaa5de5d83493e9d141213dc50aa1692e78e255e560a728e2158af84597be9eaa124a14fee1c3ad71076121a67fd7e1e081e4c6b4a5d36d88f8b88f6f5acd3248a5dac2d6e68abaed01830822f44e3fef5265837ce8b1efe655690ab5bca4bf6a5371c110116d0ad5d25195a48c2631425af464736f2cd10af7fa92a9a71863268dc1329bf0d5e4cdba61b02a5fb7143f2451b676dfefe68b4d1c7ef083e97b8e15e5f1284e177e4de50f0a36d31cd1ffa637f4275feee4bf65896d68bca73ddba911e5fa4855c6c418376c6d2ab1963402d34f4ef80ff67be27c6d31b2a6eef50114d72bf2a14e6dbff6627b5f25ab85059a23eb047b0148606f6886f0a93a25ec12be66650d38f697f70adc100d9a709191f582d63a94bca7e413d82c311ed7da7c97ab68b2eb9a5c710ffec33c39e6da4a1e3326e992bda69b6506df504f07a1068bdb378d8cb88157833917cfa0f29bdf30ca1550cb16478747a7ed02b6431cc57b5e235aac4dbbabf701442b80d3f2ed940da8c89e34951499b3064cc1eb729eba27a218c98570243ea105cb45fa95230964366a22de4ebe503a69774b119238fc0336f489cecf5b138f2cb6249de65415fd0fa2af08f7816f18384056b1e8899b6e400eee907b4ae40079046e0a4c2267b8bdb1eab0604fd2d9bd302939dcc3e68bb700f8dbfddcdaabd3406ff472eb3d30f82dcd63a0393f180e926b3d4fa88642854e8d6ae395baa52598603fbb34f51ea2335c25f0b8974eae0e0f8b1b8c3c008a9df904d424685d63d1ee0d5ce04b85686ecb281e3b335bf5233efddd53a13e12586f224e02956329528f4ce3cafcddfd31f3f6f1fcb73425fa10df7c749bbac828f1176a32c0141ccfb2ce2f366f82b431ab1ddd2c759173598aac384c463c534f2fc13c0eec80959171055288f45cf5cea602eb511490fbb6db1335bf386e7abbd231a0e3cd80b56f5e20f3ad494164587157a6cbace63f5fbf5d6c0280733f72ed779d518b3fc76d1c07472949fa29139cd346c689065113dedc5a0c9a782762d67b4153528f1b1d86ead6a58b44aa1891f2945a946c1735d4737f8204ce54b95050000000000000000000000002f286a5158e978c8c9dd040d6279714e6396572a5c195a7861ac5507c0dab8e5f5521cfbc777d83c6e095663e56c1e3ac72f58c45a2da0aeb5e96a2c0fa39068de4f0d9946dd72698d2dbdb8a8fc1486ab0f731f2b096057d6703458191b87e6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005dbd3131cc117dbdc66e6ef8ba9ee605c9df11a67a2303a569c6ebf3e52b5a31288a6ca7041ebddb04fb2057fae06e478be7e62a317148d4a49480974b0ec2b92c40452e0689e10bcc7d89ee81b8b4b40540d6cf29b01f7551f4a8f1388d09857601fdd2e7b753f6067ed0d93d5cc4a887d146ba770fe09b0f6369b406a823a6021d8e699d60f9d848cc54c3e8b45b717dba4690817c0c67cb5b09ecc8ef209bfe031ad489abf0bdf08356e7c5695e5cb6a0e0bab5d16c9cb2c4ba2725eda79f43c10b05d9a5019c47ea36f595cc624a35f63230b12196fbab46b75c69cc87a5d84a072e7af1d5bc501cf24fe9abed3127edf58224b9545226bbc7de7ae3e78593935b0303888eed8c724dc236598853bf150602a0745cff58753952985eb8cb23eecdb5021b08b8237309c8865e0abf39434ca68646a3372109f87e99b8e024af3ffadbfa031bbf00cc5e1e8cab636d0e88b5e9c17b5222153164d67c60a3e40b1eab1a2372020725f88da6f99d11b87350ca66069266cec1eaa45fa3f2d84a94f97803f31348021efbe3a132568b2fd8695290e8a4592e2f909b03b11986d3b1d3915dd115d278198b4590dc5f11b01cf8437ad125c7f33bf55b098b9a32c3b788a70f4ee8586e4c84333c744694b76023b6963f487066c0f57598adda22467c2f9797036e9418e89fa774798792468b2dcd22b23a9a8d9162a36c1143b778f3cdd6f67b6e8a825543486e773dde76466cc285c9a8c6e75a2367154dc9741bd7f994f272b3fd34ec66eadc32ee944751e62aa58c0fa8caf13cf4acf1e65f98ef8e1791f3bdfbef932ea2e841bd2c38727787266a68b6f90858780267e0aba8a2e9c3bd1e73be54096a93e8f3342447f0a8be6c57716b21e3e5a4e4df7496832495ea57cc2e780482000e90b6ba7d9afea51a57b77ba2cb0d901ac98d11b71c007afacbfcad7e4c9d3ed3cb9669fffb5f9e0fd2f8caabc58d60beb2ddd7dc60133b5ddf7c4c60625167241ad4d5e173bd4d13af32c993403586f776b5396a4d06b7718e0e8f9ddf1e80fe518e449ab7bd3e7812984f1f60d73fe169affdd6c0c743b899df1632b7b7e8808b1b91e6a53e1b4b7465ceb04041fe67af7074568407d5d544d300d68d916e595ed87cdef2502da2b1f7c8890d1d5abe773522873b77ac5e30024b7768b304294bd5c78db004238d4fa83015fb72f7186c274cfbb2d59c6025c12e8754175830eea4288e97a4b97a2fddd26921aca0a474273dc07e54b3fdc3b5f6d32a9b8a212afa6eac19753d1bf4b26740652303d1de66dd8a7030b934b73015f076a24e712aafac017d231948a1181b16797d81a919cf0872607835176cd948861287d608f59d179b39084482db6af96b9b0a0136f0e01d53f5b6efe79d86affeea7653af1d1f6608afdef4e80da024472c3d3cddcb4f62f13466bfd1a26666dd95ae4495f2f39bc2b61e7147449c62ce462addc304c765fc518f64dd682fd9ba93be4efeba7ecccc5dbacc9f7bef4afb4205987cc05eac9429ee8056a73cd63ca8a133cc613ce1d84deb8e07018aa3c07939f47ba990cf711cdbcb3bb477309857a04f1fae360ef86846b82c7da3e7ebe57ee23b44ab99b9bbe645af77e16f010e91c2c20982791576bf5e6cf4a431aefb8170fa19048504a2bcd5f437ce08935d3f09dc7ac1231e61ffe95c99d09c02148058c04c5c7b68ed2c6149f7dd925515ad090e6f750a049663e148ccc28c2e2391a3f780d9e25999c2d223ec88ea8a8108773d2be4055ef7e23587c1646d241f96022d40dd724df56cd20c2b947debb2e56d2dbb1f6950b889dacdb715e42e83dc307816a77751021945879e3a390c4fa571e671bbfeb8d2c4818909acca34b4a6d5544014b326cf742924191d584ec581a0f74a216200c82cb422524874094a5e1d3ef980541c378b8d4335968c170a5d734a326be56ceccb7863ab89afe5319be1f6fdcb0eb88cb097998afd174378eb63e65c28767a01263a8a520ae5b881ef3e5e3c08c42a8a93b9c9642e1444580c3ca7ba6f1cc5d2c85a78559cb3a6f99e055bc0b096220fc85539bf704adc8c017961c21d3349b9206a7ec494e64c8df658e3b5eb9b9dcc0f9aa3f816398066d0418a0e7f50f9903fb139f8d9d468928af198f7e2c224b0e95e6f5519a0120797c676e4bbf3b46fdd450fb6dccdd20ba5eb4951093bae496348d79f60a6b45af6f166c1673cee5a228e36fca50d0a19e02a9c5c1f3f9ce68e9033aae819b5867874ef3be6c9306da57f41bad2c4b366942f000000000000000080514e03000000004a2651a1c3f47d7a1c116e6d731f82da6e95c46263c675544d639909e34ed0ae8641ae1b9dd19e2853a0642ae40f7723b389a5cbd62a79e6cd19a5db98cf7b040e3ee35b651121bb2e4f22cb546a39b59aa9835285dc14b8823c949ac3f8cdf000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c2cdcc587c181ad10e3530cb8bbe58804b323f0ffba1b0e79f520c6efe5d479716683b55c25b5da5b363c6b537718ea890ea941a6eeb509138e2d92c369b05e71c4ca01ebdc2f8503217fc55154263e372dda8f617b244172f33e8c32e9a0cd032d23048e4bf7e3fa6678dbcbe313e29265a9fcf4d2dcb0a703f29eaee2257d902168b91fb9e27422ec6e55ee8d2a21d1986df424a3f3a748041fa29d2fab3b1ba0200a065593331c01be9f1134ff4b9af38bbcaaa9edd8e42083613cdc07a83e8070a0bf4f484c6c217ac26f0c891363629423330375e1c02249ffcfc87967971f7db0e9e9fb45f6a9e04195d6e538f103c818bd8810e848be1d8d3d0ae5e33f4fafe03188ad7dd491b66134d1c44068a2daf69b9bb4c4c628cef85fd1ab9f3574e11c0032e2d72cfd38687be32852753a95d81b10c31139b8aa04d828f99e2074a7b6bdc03220ddde92c94ad051a16e626a4903316adcfe4ed5b2006c34ada70f356bbbffa030e640a40026e0f40c5a83f3805d1bd31797751eb5aa362107530b70c7e0849c80307740e524bb384d96035ec1e11e937643f895b639f6f382e286bd655f1df3429a3b5efe3373a12e393d592bda471d2fcb60da86e79027a91f13f5ecd0df17ebff23fdbb4ea92bcbaa8ceda642cb10849dce86799c921b90dc1dea070b58364a762d1e0d79e7edfa1e89b73986ef02185503fc0b8cacecee4852f86a952b04e5b96413202c3d7d4fe881cf192f4541b3a5e9ce41013ff8dd32bfac38fa4ac420d1704266c429ba3d09c6394de6e6dcd8ed17da1d9ec16b5f52350f2f9b4ad595d8c11d7a2397ac6c64dfc3c894550cef8922b3019553688a663b66400caffdf6d41ea99a39997c701527d9cae04dccc4174dea47da8969266d0a99f21aebaab021be02c4048780b0aea1eb7ee39b1ad11afdf92585688aa67bbcdccdd720acc84f7d470f71d3f67a4f5b522977bde350f37bace8b0c9520c38ffaf37073b279802839d4d696e79c97d60a2450223473cd6cc4db8687e84708cb8d78329954f587517f1f0aa28c49c1eab621cf8bd1c5a3c0d408090d68c0a907c4f78645d0b0ef3ece2d2e1cab0cf8e012231e89ad6adfc9fff6d1c023704129065ed0bf736c20a635e9a7ebbb354d9185cd87216683cac25fa131aff491438d9905e19aac8e48dd1c3aa6adfdd1b1226c44e2a9d08a04626ff3fab35e51187c78604fc43cb53e934cf6e5c93e45832e3017ce576081437ff62925fb6192b010ca06dbf1ba449ced3c807c79c188cc04bbee1d3ab44201838842ccee7f98b40234d85bc6ae5a7148b8b6a59c49bda7e322d077e6eb215ab87b9323ab8781167804fa72b6c05781ed536a2618f8684f87ce2f8d53412ef310a689c541bd314dc7ddad9776fffae8f165f5cc536db8925dfe3bc04285567165a1fccd8c6565b92277d6f492ab34514961576edff18df92e0a7e0f7e9167c020c219f6165440d58e9822b0381f304fc56df20c9f7361d5b4a3d074c0bfd9fa9043fc71ceed95b1e4de5f4658c71f40e89a7f23c951f3fbdb39cc9d8b3bfaf5b3a3812ea90f171ce1273d9991358bf06cc3c94077709e9ad41f8d0d60be4d8bb23f316506f75bd8d42ebeab0a69d33400b76208748a6b12a7b037d8f8bd2cda6f1d4c1c9f2dfcd26a537ccb25b66757adb223e36e2940a043f9af77173bcc13845bb783025ab4dce3784cd96b4237df551f9287e123b3dfdf12738e6e282eb2497adc5e44cf410bb6f1dc8fcb925221de8055f2851caafe7ad7727850d4d40f203b1835310228d95976d204b44e1889a7bc846e3edc67a8b45ddf651b8a69e7c25613ec593ac946c6948ba4a5cd36f10152176561e36ae06bcd115487ac225edcc6ec8d00e1343fda8347738ec9a59043f7f51b53dafe28c8e16596152916e1d3317c951991f5faed961080a147096ea196dfdfb5f5d323ff70ae3afd995027c5844f8215d946bf1be9c47e710aa7aea1bc04dd3b1d7bbb2a0bed71962cb67e94d3cc4938a97a1607cbb926fd4a2bbea29d046e8a2028fcbb644e52c148dcfdac4238d9f46881220ce2282bfa99b2c9c151054486efc98e106e0fae7d224d84d0288cdf17096b52ac968fff4b157d03dec742232fe0898353e7ace936afa1d7cd1b2ecd14ee81b24a1d959ad173b1b09ac25d007af75b2c8f8db9fb9f425e862cc5325db2687511884c2461a0be31e1b642cb83b1b8660f5adf0ec807e13e563ad9a1dcaf4214248e07990488267815ad05fc537c8b8f7f848f5ce97328f05b9906a7d85a857d88db2e2a90f071ae9d4eda49a62fc7fbc530d421ad1657df8197e32c7b1474fde0f53aa3cfeec8827e3cf35322332ef823055cdf8ef0f338347ffbf7be1185d3fb62fcba399675e23dc458b12e8b1911c351290f041e19c1db400c", "650000525153", 1, -264916277, "51db1a19b35632d7716a418ebc120e326eadb9e643bdf465b3f0277710780052"], - ["5b3e4804012b0a783fe8ff043c6ae09772a30f8b4b5b3acfc66939b2c7824f69e11a6de66800000000003b0b672d01ed1a3605000000000363525100000000010000000000000000061654050000000011c8891109be406260037aa1e9a6d7bdf65d3ffd4088f7e950ea129b46a4a6d4341e3373984055a13c4753cb9222d9abaef5ebd2cfbe1e7b42337ebe9e08e38667b9a90c2b5f89596fb39b8c28030d57cbe98507615a2a5046d33963e779fb97000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004d6a1963fa8c598f7dfac54a1b403f2b13202467308c49a25eb485b91715f3f38bc12e47a79f4b2903169cabf4c7c4de6a4907c79ae23aa02903459a3e8e0f87203ed6b04b575aea0a6c7391c851a7d2b6940a718f29aed139de7cbce917f29ce0db6b140aec7936d6262137b3490a8c0ad5d8ea8271ffd8d050f0958a5bfba2022305de3aac77650aec9d0aff8738d6a74e38f266450288214146d664ed3f9d6c030e09c7746ca24a3c07ee516567a91a5885b380645d072e123dfb41b5603e9cfe0a1ca538f9978b67f8f22ad911d02b74dfcc445659a5285eed259f89a9764caa840b0755299d56bbd014d08e6919d984b949e8f7d50d4523141bca3f774be13f03032f32a7a23ed95c5b3e9b8c83f1474f6173cb8a40f6430b6dbc18e30472783f54021a7272e7ae56e50ad084c0b67f1b37b54110080be7e7f929790092cf6d579d88032ed446afd634cd98cb07893ca7805ef5de782a387b27cb1566cdb000ada22d430228ea81b3aaab56459c9982a700259c751d15619c93ead0b8b75c715705a07e53031bf681b54f13984bc9f1b4f5ca7087bff9d1bd9cb55b8cb7bcdfd5e920e03ab368f734bb26eaa784b87c2cf319392f93e3ec90c35e4bfada256c52bac1c607709a7edf702d2f0d7f6ec6c531a5efb680270565b80b65712ccad6855b2e81113d1696e8abfe88819f87fe05487784b9bed88e9da16110c6f1383c4f2e28c7ee9cbcc1a2a7e043349ba3c2c43bffcfbf3209655b131bebd28ea9d15db9d1cbb8bd09e1e07b49c3e1663bbd2e22c6d69ccdbbd7b146bcdfb4aacb88b482cf6a68b975d74da3f1fb7d30438ee255eb113c6482c72b6fcc5a3fed4aa0577b3c7c7992c33c190f8b211e835f5a3073ac540e74fcbfde4a29a69547d38f4d4e51a3f366794e89727393a5acdeb8b534c7014411efefcb5361836bb3449113ee8153ae26fd82ab15f55ae0a98a11b4c1a23954299ec58d60c344888a388a0c7f2ec56178ab6e180a668f60cb56ed42e12a51f4c8e2551e327fcf92536b937bbd15046194c71c183c9df93eb2c7ea4b67a2dda07775b8ecbe208a347251f797310074ea25a0fdcbbbd798065951d1ebc8585800345a09db3498fb4d5be6a522985589808563622180680c905f21141942752c6c9911fd5ba43ad797bb6460c0e34fcd161a7cc79344ebdc54cd5b2255f82fd136b907988662531f339e1bd9b2d9ab6508ac6b11fc6721e3bc8c8e333ae86fe77bf8a8831d620c6ea044c082c3e7251913d559fabee8f783833c7c29e79c87d4f8c49480e7402cb9076179f146e31cbe9f2442fb738ff54c4f727c3786457938b3bef9bb4079a41a845302590954d12c3d296077e617ffebae3064a5864b5efdee0618397171951691ab716a4ea7993aedb69d822235e52f895a8103f423b7b55dd397901c0bb4fbed677360ccb67ff4d78c19f874e6f84b5e399f1002716dd2e8ba71e25d179b1af2f5a4103deb921b7a0b9ccf8113787cf40988dabe038d7183949f9f4befb4619c09c08046dee1394b79cffae00ded2961f03df45b204000f670980f41452a71b761608efdba19dc08b49377c6dc7441cdcb80a9786c34fe34fa5fc0d37c9e7062d3567ba6528dddb2ee9265f013f6397e87b1c212ddb90183f83ae2bc676807c51bba053b6a0e0525c850b16a72b46a9d07df2242058a69e4d0ec5d5718ca16e2bb0f7cae1dbf6f2ba8e956a675b37bea937f392f3737784f3334b04e6c3d192b32157691fe2a7ac4b2c26db504d20b1e75c07954e03f882aaaed673cf5feb20b87e5aee663c9779cf653d8751b14038f7257c673e3353c0175562aa563ef2c78365b4d161700a1d56863887007139d8faffd60f32139647b63eec9e7149d7dae1e15d88a7702d813463d769d365a9e894290e9e1af0112aae709b027cce82c90a843c37e1c54bcaf70a215f1f488beec32cf5213159a4134968756bc2a81dafb41ca17304ce11a08c09849b35eba33f5fff8223a6cde05e4a44ed35d2178900d2c20c741b1214cd76e068b19a1a10d8a623c402c060d90bbe30a9dfa5b38cdcde0828ac49d052b9aa73fb3ed33a1f8ba64db77c3714ce69e046d54f51657309456b4245e12c3b45a815d3d8cd66f37fa65c2abc98f46547dfc5d436564cc523f270cd1b7abcfcb984bb5cc1512131baf4afaa423a672568274a6a2b3cc2e65554bb4f877cf79a0ce9a4a9ab5a113641720961ddc4b8c169e64c3bb144b13116777607d2fddf12222af218a540b9c863319f4032576e7fb2abab150367c96ae371f98a77635798b9b53b71554af87eb383eecf0a0ae210ad19f697b972c48e0aafc8d74c0d9908c21a7b41fb4d08ed622526caab70a20188fcb2034548aa122cd5d7a14679c3f1df6976f39bd56f2776210deb4031607cccfba0505", "51ac65", 0, 1682244068, "2905c9c19a7f29b2726ea27b9b2472a5c48ab5bc72f89b15cf3a3a17c1d77da3"], - ["130120f704bcce5de6e256ae6c8ad48ec6d9890684471e628f07c84cb3c664d60ed9952e880200000000d02b9de2768d81127c1ce122873340937dc8ed4ce32f616ade8fbb051afef7c8cfd1b98e0000000001510f7ddebdfa2bd28b58d366df485be29dff2947a4bc104d98ea6bfe646e6c16659a4075230100000000ffffffffb6785f8623755e89edc5c55c57731b7a8b865e8c848313b86086342813e0971b000000000751ab6a5352ac51ffffffff045911ff040000000009006353ab6a536a6a0007fcfa020000000008acacac65ac6aacacb7c7460200000000095151acab0051ac53ac5e68a0030000000009005363ab6a5151006500000000", "65635363005353", 0, -1353169174, "ae724cf2f77ddbb9e59f8a5ea087defcf7b1fc9f36fbf6e53e81b5375e363389"], - ["31fcf509025cd68ee8b66ffa013aaa9b4525994105aa6024dd525e5eab8b1716bb2201ccc2000000000163eda22798025677d6e5d509c1cc33d3c7672d1e438af76d1eeaaa68fb0f831bc0d31129ce000000000252ab86a61d9102b4b557030000000005ac65ab6a6a124688040000000004abab51650000000000", "51655351006a", 0, 107304899, "7d2ec9fa3037ecdbcbd0f42c9689d91236b91a8ecdf15ac9bc1c4472a35a26c3"], - ["fdf3048d020891e4ca3ab4f79b360f02a232ec08365159f1863b2231cc39b1b0bbbc460eac0000000007006a6a63635352ffffffff55f48dd0ca1bf75fac9b045789dc594017fa069ae095728b702cf4b93be29736000000000365ab6a9c301d0a02178525020000000007ab0053ab63ab5205a45003000000000152f5c7bcb3", "5365516352ac53", 1, -174574022, "fef0140b578f7b198cf3ed70a8df74b2a947b297877a49e742395087469a4aff"], - ["674dd34c01b9fa0e4d9a3c21e921450ae84a24f1c96eb78f14abaeab7e4ea2b6800a4e2d1103000000096a5252636363516a00867cf58e042e91490300000000045153526366464d0100000000076a005165526365bc247a0400000000016342b3fd0200000000026553ba3fdbdf0100000000000000006f032401000000006df8e41ec75fee3a33ceb86881b758a537b9a4c69612ab7005cb9b494e5b55edff7c12a44b4fa027a745e5944112e67603a470c6e6891fb8129ebcfeec38e00a9339def532b8900a484401b80a6017995715528a9d0e7f545e566d751c1cff740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078ef8dceb6992ecd94b0d0ef2755aff4fcd8992ba3643f7c8600a05d2a843c0d838ca5185a327db5b44238e87f4a8d9c618e2435d12d95d623e506c8a960edaa59b90188b220ac41d24784c6f8b51ec5081982ab6376a6a264ad8ca6540911b459f119e8f74d507529cfc092ec2886ced813e6b25041de02b59ca127bd3f200203200414d4f21800493ca69f138e13aca30a9e0d5ccd509e56176570d5ee6651f702008650318461bf14e2cc38e6e47ce7a835f049e25209113564874f980342ecb60b174bd4f3374780e83a86f99dca5f7db6053b4ff499418c04fb4998932f3c440f061390a8af313f601e3b025916eb7e571cbcfd816e63a08b0ca54aa20e0be67e020e48f5c09528397105f23aa6f1f967f385d620d9e47e9468482bb4e1438a2a16032332851a57443bca011f2ba77ca7ca7ae39a141a57725f52cb5406a3ffaa0c6b0214a8f73b9f9ae615a2aab28c219d1a1d5b6c828224f7e30708223873290ed5fa03149d3edc4c5a89d9cc7050842c827cb84bcbcd2a88da0658c928b4abacf33efe0222511cb97567c08499a885369932a2d157836701adc68229f489c72014f5b0389d6d8843344116022888f70957bbf08c9fe653dfb58268edadda32a5c7fa57484de8b245afce23d2d054f2a7f54d4592bb48d37b3ab9bd9c912e366e2ae7f0bce02b61b68db78fac3a34702694c18bf7dcb6cf2d87b63ec4f9abe614c875014ea746f0234e50313a219e9da92e74b997bbfae5419d7997ca68f5627eebd81ecca05e064a71e9d17179bd583eba939338c6da8721b53afc48b17cce7fdc70a9694ac11d4103792f704d0738c9e0875d3a88ad85a23660be7fa333f5ebddcb3d7142e96ad5cf2dc6f53992cd90ca4a874a5f85cd5b6a58692eb0e7070bf5b717550362a0ff2e3a45d7c1f347b5db75c3b9925f1db20b02159d78321092ba0ff58300333f095aa1449ea19365a4f0ebd404f9d1c438520a495720305bd3c257e8824a1d419cd35edfa3fe1f31dae16373bd2f24dcc4a7c1f3a948a4e4bdf1d207c44c73f7ac7c2eecfd03f494ca6f10ef0e48571e5dcf816c45d9261adfc3eb5763bd79ce778986b412d1ef2d6495a0cf3a94f06e7a679ccf7261d02bbf2301759b162ca2ac1c98c347944c366d32eddc6983b79a12285e7f662fdf6b7b02bbd68bf8c0e27443012f3c488222b129692a2ec85e02ba678f19550a392c72f3878d62c4bc7c667865bbb5d62f6ebc2f4676b5cc32e797e83b915220b9d60319e4fdcaa9ea3bdd532841bf7ddcd4b079f77ef3edbf04a78553742acf8b60d1f8fec16874376425b79ec8eaaebd4cc7871d1c28c7e782ba709d957a36cf9dd7bea4f486b603bff1ecdf1fd54e12e444f2eeefda3616a23bd00627f9ad7e714f4db8c113e6e68132f74195d42dcb5930611b86b13781699e6c6d5fb749c8deb83f524cbf7e6e6e07e702595368c0b33966ad99825d85d8fd000fbaf40b242bb43531df98fbdef4b754e86cedf42a5715c74666611e0fd41dc6b6b0f253583dd086024af1615bdc181a35f759f353f33e982ac22f4af6cbeb6ba249d6c3720c9dfa603f73749a79f62acfe587543b75c53ad6c1e3921ba0ad95a24dd62df76bd91cb9b93d706223bd1613a779c2079936f4eff9db59790987dde6865d312bded8be486d727034b9076fd275c0d06419cc9bafe8b1bf548992517a03302b0c27f1f405e59fc2cdc732ee65b9bc66ff444f2e7203affe29e6b95463313a9111fbf50408f2269b37d630293f80d12a44565326bae4cedf51c5f6d1e86e90b0e2acdcf26d75473c03bd889e220d27c1317c417b3ad3b63405b72590c6951424e4453993cc313d01bc2cab908aaa0ac58e48231338f9e04fdae1085ffe1f3f354005a425086256aee28accc1f5a44808425552b19f8fcc970174691c9fd8214a583481cc75484bb92e35194cde3d9b180c0cee8125ca41d3e32640afb5f414fb8b0dd0d0b786bafbcd3e3864f6c9159413f43f2097fd251905ced01f4f27ac6e08a664ba0c9ebe9f53e80bdbe4dad4398918d3a4f35836a2a5a023c91c9580f3e7e3263d5d7eff10e266450735411ea0ea33563a6012e9524450118fa00d982919ea6a64f37bf72d9bda7feac818911be520a38a6a4da2ccff8733c8834214556d53682281438cdd1548a778e0bc697b398a4da5f7352a048521e641fb0c0635374406c2ad94954b4ec8449b089c92e979bdde178748e276c7bb43721542845e6df6ed4216637b2eeb807da68c1b51a21f4ac421015ce78f69122e018dd1308beb155f2ba393441a5ba72e595c2320741043c87bc68ea0994d336e065cd96114838d918b09a7308e7b806ca2d1caae1b4016551913b3fc6b59876a44bef569d74f8c4a280248d4241943fd3d080a5741dd236b357b96ec1b0f", "5353635351516a", 0, -1179814581, "cdd936c96410936e0176ff09047d43ad7f021191e30e25cff995b8a0c3e59d66"], - ["b33630b8042e20a4c471c2b8a200779767ed87c3b963d3ef784174e846fbfa8ed4a1f92d3a01000000086a6553abac6aabacffffffff18b00cbc134cb064360fa3367fa435ec0b4ef755b3285f367ffdb5ccaf1f35830200000007655353ab5365ab5daff9c772a06f79c94050683f875d58004964dbe5871df8d93999302de1003c7304d5e80300000009ac0000acab5253ab00ffffffffe47ff817a1915d1f66b17d74c17ad595daf56ea4d74924f63a2f1684ef217eee0200000007ac6553655300abffffffff03e4948600000000000551ab5263652982f402000000000553ab6351653d06ad03000000000852656300acab005200000000", "ac516aac51", 2, -2072248370, "9c30b43f14ae024b160f772d61761bdefe081e1613fb974948c25d200654ec21"], - ["1bb4a88d0491c2da87db48718e23a9ba5dfc1d771da725814fb45c344727f5c12fe012aaeb0300000000ffffffffe72036f23e430851817073665edb38a80cbfb9695d21f86437c5cca92334334a03000000040065ac000f50aeb307bef0476e4286a9b69f17557275dce096249306c57d3ee7e43e1cd3278132900300000000ffffffff978bd571a89c9dfe8d7d68de0b132c183aa36d219ac9e0fd19933207cb019403030000000263ab1af052cd03fd18d2000000000006516352535251060c65040000000005530051ab52594db80400000000025100844fe664", "656500", 0, -492402049, "9b9a0997b7e776a538d70113109a7e7941c2c3d851e02d6660079a8e9f03551f"], - ["c6afc94102f11d2ce065e808ae5fabd8cc781f677e76a0074c31996dadb35ca5ba7f45366c02000000025165f4cf1b580d48d3d884e9a012ba93dec2804dd173e75de41310676cd017481b5e360ad0960000000000ffffffff04199a0702000000000663636351636a028991030000000002006322cdfa020000000007acab0052ac63510e3770030000000001ac0000000002000000000000000041a362020000000066980ec769aba8bf96f4c6c60b1a9b2a8fcc60b1b577f5590e8bf3080ae3baa37be6735c54511fa952ec45ed44adf53382b06cb9d5397d03292fbb02efb6be3a4a9d7d2297bd28ccafde29d1b11d38a59dcc69435a8489fc3e3092e4cf0019aa00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000283edefb695b42ea586345abbd6796a171f433419eb6c6da3aec35d89f4dd864c9f81774714dafc7ac8fd6ea0d33e912b9488872af1758a7193c93d1a6f5444431231ccaaf95cf4f5a95a146b730fa44f1820018cf59ca4db2d5eaa7a1da4d542ee34b52ddbc547de22c131c8ca8682776e6b71fddbdcc5cb0bb939cccd80c65021d2ce73e38a78cb46a16a1155567ef85454764ee242a4eee63e0030289b31e9303297868fca02655ebca013c9198cdbd6b52085d504a2074b7234eea9cf9f9d6480b2a0a17ab76a6827c25d94b6f1db26d1949d8b20b7c46718235578c310db1760d06d167b66db203a3b260d4deb31be2ec32cdb53ab698f8482b2f9430f793c8580301a1d99585bba466e0a5354a16b76a978380f4872680d4ca30f91296c0b2a17903276791c0045aceb9e4229d7d0465135a63ab74f48b09d2e6e9115bc7ed39837203262ccfcd4fee1c4ab9a99aec37ec30e392911103f5d59ad5ec2b01621f3b901e020aa925e598f63d69fda3fec15865169ea582e80d53df599a79b310928a11cf590226063ec0af55535750212be350859c31bf23b8e748cb7befac0da4900876368de5756efaea685a8389c1c599751916cea4cd146aa2ca57f51daf1c9151de6303c5870c27f5c0189bd2ddc39e3a36702dba42f0b5ad51b6e74efc3c1a6e7dda69a7f0e121baa6eb9a5b7929f9e62b6f3e513ce1ab89b14eca64916398c2cb6ea630089275e98b544c97f8d9e92b1db4c2cf0e2ed8baff8db23e40071dcbef02138b62c1c3873f192599e7ffd25fc3534b80d1014f14089eebb4f8c943f9032f0b49c0439b57890d0bc86f88a28be3ab71451948cf3402395ae8a809a88993ef3c01b413f86d32f4bc474fda7d63db6d4baae15e173bf9565da616185ae333a1b618af3e9950726e96e248e73ed4b245a4777a6b76baa60c57f9508faa17ef7c95db9dfe4b7755c6b0a9aa7d1d7381329560b8ceb91ca3cb29927eccc45b8f133e56459ca2d997272e1b963a327d921d567b610699196e800c8b5343639e12dde4b7757abc93d319364930948db9c83825f96e64a6f0d62e104242388f3d668bc27f491a73d265ec0e256412ff4b15969bbe229936a1052ba854f4a1c85e49036885bb7c68a6f9de7a8864806a97813460a6ae3ef13950bd6de725357897fdc45b88eb4098a32de1083500e4c33866a85d52bdc661d72e1f2d589c5f3afc3c83a7444ac2540b51b7eddc93cd5c6a1cda114d0fe6686b90374b0d5a4e885c96820cff755d59f61bb129ec585427aa7b47056dbf9a47697ebff5627a01d398a56f9d37ffc0b850df14cc489547a31ca169ba33f6d6182a166ae654560ecadd97d66e6c9ef816a08038d4c803329f3954a399292bdc87dce708e02b3a37a5aa6ccc071f9a181a2c5299a6ab40cc18d1d1204de95358ed816d0c18c0fecee07c7b5b5789949a116268ec000f7a930f6c1c59b3cdeed8ca695cb7a56b4c414a0e9505f5f7ae0855550c3ae8c715ca3d4bf7b2ed06ea3076bc77fff10cd34c336f46c7f4227be71f06693c5ee8494d276b1561d048ab96e1714cff1ff54c9be37c438800c972c706d41fdb25711bd6d3862fb998ab4e37efd233fb36b4917af9a9acb38cb2d239d68cdd72b2f78469bc7364fa6539a244a7fa65ec8f7e2a6d29f97d853720ab868d75d15afb5ac7fb878ccf1896c92d74bc26f5cdfbd8e5778b19d266c0ea4e94f7ab1f5904c24fb02f407497c579edffea8d9a7646ea58b35a024b43ef065f5614762299b99df1a07c5922ae54490cf907975c0b93178667a9701688182a5d38a89be6d16586f80b67997427b77edccc8a3e2c41dd18db3584c98041651907cec1b182da71c1b26d9cc6e22a1dae7e441f693edb9267b929a3fe8e894a927ba82e475e2901a46073c6cf575c1a37bb575f81b2eb1531dfdcde6a670ab6abbf8b5f95b0e210b56ee93b10ac16d55dbf12eb9dc86d382a507da9ef08f508dc7bbecf5c3c13f89a9fa56a1cdc27dc79b484e2e1d2234e81e0b16ea4a2eede6404af0c1df59c99bc97723aae1f6f75a3d77f3090a65ad1ef8bd89b7d2ecf61c43d96a4042fa73db841cf840ac930e4ee6af722956bed97eeeebd11954cabc74ca661e082f1651a7c95dfbc8c356a300061402b78b8e44eaf39607e46af8f84843f3ced294bcc56935b6c872396a13adad7eee20f5bc394536581e41c2dc5327ffeca6a16b59bce81e573e0be21f123a7bbcf40b4579fd11efecafdb7796d2aa4e00376bea16b9db7e443f467a6fcf3f1d44745a903000000000000000000000000e77f4a692b1b612152ef089e52c48a0ea68d8fecad812b5dc17050ac7edf996b55a2c00a977296f44d87d0f572310ca5c79c66d9d096e6cee60878702d3e8d6f16eddf339e6b632b88ebcb8df144e2402068705d2a32409fb21c8791ea9d3b51000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f85fcf65aced049850b3f92871b1f951802ac707cc43502f6fd4c4b41863e39018639190926d46b8ef323e238b5029e4a54f2a77b2d7700a4de0052381c471713435cc423c599a6fdc35bf1d5fc51e73b0d11fa70ee4e8a1a9ee0d061e1c123d5938aa223e76d07e8db0cb87fcfae176f366c6c65f3f07e205f74a2c5561da8021811339698a1a60e09a5e02868eefec358f52daceb5dfe5c235142e071d3cd7703120f4fc55beaadeb4ebc7c34c6c6b8d8edaf4a76f156302fa2f545bfeec0f3520b2a165646f56cb52c7878b437b1d1a4fd3b473f709d4821cd87bc4ac3181f87af1ebc68c45af5fe9e723d827accadbc32fbcad5036f4e93c27c8646f8d9c75a8e021b8eaedadaaa4b0afe17593633dd40c3679c98c0eb3a8726aec89c7b2aafacb2032309fa6b2aae6f81cf249c33a364912c99722a23b161e3faf83b1ff2c8ae151d031b83717c475af145169b4059314f142c155f5c8eaeb5c4d954f16dbce793391b030a7df4d1cc713c7dc1f2a63626cb6b71791aba72e5bb78754fbe4058bcb81b1302174f17c4353cc4622c2b75d2e07c0af3098242140fa64a4d9d662953069d678c7778ab0806508ef710b6f03b3162f0a11b1764e117c74befe5aaad2669005b6834881ec872d31a8cd6da023d4e9f823c1c18a2ec65fb7085fe936461c3d94da4bae1c6d5c0309504b1915374adbe05adbdb04ccb2dc45ab9207f05c9fa10402f295ff0a5e86c3c8efe735a087d4eaeaa3fc929b3e7e2f6def44c90913d0c55ae5120fbbe68f5c7abb556cb8018ba8e2391e0c1c5843a2e06aafb4f6c8ad98c7aa626bd8c26b347daccbb35f78dfc08441db8f8afb6f2448ce54e48a11dd7fb39c78d06229f95b00b5770710e0ef20bb29a4761554c2346fc05d8a3521d68a99a0cd26bb3fcdeaf4dc488c5af519ea1834a9b7a47e0f7c6cc3c97fb84537a8bff0fda39db5a6613193d2813d566d1e66f72a2c51e021ab88111933ac33488e807ae9cae2ba9f01c5f1bb47bf94cf9e11cffb9d3335960d2e896d9808ae2f4295b71f4ebdbf7b6f691447d5880afd4c3806abed195aa119131b72e638e025cfcf326875664b2a7a078a5b0aa89e2c71a5433a6c446d9aae0113ef807290cd820f4759015ddd4b94c4ca6a856bd5342a33b8b1550d676a7b200d21f1c0d55540c2c2fe845ef1d5a1ad408b0865a884e61377361bf5301dfe179080d404b8dbc121070bb6f3dcdce074328a50b5dfa63ee6ae12ea3a1149a0fa7acdb31016551a9947ddcce126066302586759a15c688f54b1f7a99d9ba9414a7f591884948cab193d2f3fa56c19a5e8ff21df2b732dffa4bd0744a1c894e3f40df20bcfd24a836eb250f08e54df48a8eae54568faca8fb2592852bb8fb3ecaeac8bc5ddfae648a09fa363c544b19b4e59c2aaeaca96357f529564f44a10d68cdf971c25e0cf0541a1e9a4a31529dd0bdf4bb8eaaa4044d774a1b44cc32591ab186027349a284cb960a57187d08177c55fabec39ebf43209d1a8ff052c63320f15fb47fa758312a21424cd97456e432912e0af8533f97610b325b1a29d33c1bc1025b8b76dbd7cc14cbfb9dc2399f8256ad23c7310a09922448e79582c3ea0fe694b989cd297b49177e7c22c34e2b5c8f28e7326b47a571c80ca1bf93823c444d359c3fa792b06e12d8ec2897e63eea825e74ca24cb8a3219a6990d660b42b1dfc5c3f601a8058eabeb7e45df44cfe366f5c6a2f1508f425befa4bb8530d98514ba687260d53984ea9db684cf0c971b0b9598a82bcb6a0d1ed1db7c4bc0b0faeec3b20e91de53b54a6c56d19299ebe698042fde436fe2cb944e176d24c6b5df447d043d3335d8446cdd651d3bf98f5862822ebb3e75e16868a8ac2e284c6245fa1ef39cfc39d99f958018f91f876b137d802841d8365c175c1034dc42e2a62b7338c1c281966cf0695b36a8c4853d1b84e27b4b6f4b9ca412dc2537aac5fb26370ccead516d9cd133ada24b470cb2038164ff79ff264cbc88aa2720f478c3921c93379e1aca4c28af301567d415adc3338e6091d29d42b3c969209e03c56a2fab23ea3080a19fb079df036278ab11cf01da7feb52b6e394a5803a2dc53dce64d5e4d7102a594c78bc0b5c328e8b69e44e46a763eed0e6abdec0d7256514a036e2d9fd2cc9acc7b24b8090c9ad25e9738fc45466c3121f62790973295c9c53f7167584eda04a695d868003c3e47fd2b40f3ef75b87b8a92bc7616d1e4c82d8d384c73624a4b72c492fc7ccb5bde3b02dbeb4bddeda0b2c3fa9e0a475ef70da3c8a087ecce68ce5525f578a601be24304e7513b4a39deea80dfe8122ef9ed5b8a5ca2860ab42c1b234bc46b2662ef3dca7359419d935683280b9589cd921b2321a59c6df8b1a92b3dd110ff23a17eff6b791c4b5d79357313b3450b", "", 1, -1806082883, "136b11c237763e8a7550cbb872497f1950bd7e67db8354b838b9d697edb836da"], - ["78b5fed203eb1f5234d3db8396e396c2c0e4000909293c6aa7c12b69275826196a320f49a90100000008526aab51516a535288a7b6aff9f87f8e6c713820cbed15ee30cafbb8c1d420a24c68110c16216d567e388d190000000009ab65656a635352ab53ffffffff1fc42a64e2e7e2d7fc3207971eb479a73b8526d3475ae7c7db641bdd4cff756f000000000252abffffffff01b9b4f704000000000352acacb39edd6f", "acac6365", 1, -312222388, "8237040b585260c6142c32d32376fd81fc0c8e98c223444a098f4cf07ca106fa"], - ["8b58b786014ef8486237a1760a17a3c5959762eaf1fb5efee0e27f9c070838bba7452739e301000000095253ab51526aab636a34791ec60430357103000000000100697e92020000000000dcf39e020000000009635365516365525263af514b010000000003ab52529569d4d8", "006a6a516a5152", 0, -1006106332, "796735219f82b66fda07ab931a3bacd84ffc06418187543a67c51a0de263ff6a"], - ["2dc75244028614d2bf61b75c181150bacb4ef05fa009684b5855ff7f8a5bb7c7c1e9a0c90b0200000001abfffffffff27031029851f364a1301113378078f6a01bfadaa87c3c1765153c319abc441700000000086565ac656aab63abffffffff044cabb4020000000006acab63ab005108eb40030000000009ac650063ab515265000b7beb05000000000863ab6aabacab525359d2de050000000004ac636563000000000299da81040000000000000000000000005446f37a2401cb2b43c310725b7fa9b6b7099b6c51beae1ac35c906eaf0ac4b7b6ece7ac09c1430e92e58d379634a5612b60d578885b20bf83fec66241cc2b1e5f71557a881e8de89fbd922d74f2b99b81de137d08bd6c48ec76a07a0db1e3b700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b23925714944312662d9e0a4a7229997b9017b09ab6201bed75ab263b468bf2fab2bbe11207b7d1e48088ecb1de33e85fce4490c50e35e309cd215537e2b36e8c5bdac4fa1a048a1eaf2bb20668c60cbc93703301f038eca785aaf3abfeef8dc195ad5234d645957b8e9b90c603a9300cba24ab2a4ba9e5ad89d37a10f80cc6c031b034ff01e0c382571b91d4925d6032e72939de9adc6346da88a63b1deda9fca03057f7e442028b7f952c9248ba6a283bafc9e962388243bac150b3319521469660a1ae6f43c874da1773dca1b1eeab278f24999b3faaa4aebd6aea0859aee5dcc061b3356503df94e63790fe2c1ebe61cf58acd104d0eaf41178ad8d9f9f491d712032d90b5bdb2927f7615787fefbae94683dd2ed1a8e4674d608eb35a853fe2c10902212cb566485418767a09d9f7a1fe048abdc0ed24015d4e5339e46f621262abe202288d8a7374694edfb1b1e9ed7a4c6b9af8e38c51a545a6f018d6fa85852bb2a80313f570c79fe5c6160eba019f41add4458049248aba6e13de740d41ef47533cde02158a57ec35ce53c404ad060f467741a60db7e26f24d19034ed2b29c862112c26af96eab14a04b785b122f523cc2f1e806b3b2849bb7be6f2293f8f69df5afddd74f641531dc5470b587a7ba9ada0f747da6587428bc2c2483fbb441c6c3f32a59cbb3feaf14f34b1f718e5607f5db10eec18adab4f45f9c3fa07bc6c2fda5ed6354968c4387d74069c69899ff4397308b809b9e3a9887bc4d93d0ec633c8691e11e15f4391f817114c1175cc38958b7b28faf16709a328b2dfa452c55ac2cd7109cb1374bebd6ef220c49601ffc853fa34681f89c035dd9721bf922c5fff6310b30639e4ff1fc227cacbdbc5e8a9552da95d7f62c0deab621e1546c624e3778b57dd24dfedfa919e1e19412d38570553a830d777cfbb57f16f772f7d5e32d5762a495ab45e087b2b0e28a019551a997c9a98aebc7ee454951c3834ebc379ca53820df17b9a00a605c50d279efb77094a00012ae3df582dcf64efd8104e4b5605b12aed19c7282fc55bc22fd087bfb98db03a104bb655b2e91672c2d125f1c1c8a8099320d105c34700e2aaeece98a6b9485cb6fb8e375b9424c41184679b1d6a96bdbb2dc82424c21ce389456f60f08db44e15ca969000a832f572499c8605e8e12ef9767f236427f731b3d296beafdcebdb1a81c766bef98be9193a64f979c87221544bf8945ce09eb2796cbd80b319d8d176ecfb662aad8871eec1c2325900e96865875d569f5f38aac2a8e1127cafd51acd27840efe57f4064ee7e9f8ac65153442ccee46776f31a08733cacbfd63a01d587c040ec3927c8f9c81b822897685c3cc56b1e3c7ff16b7686140a78742966d07533f4e30be5e034cbac70ee197f5a2b57f4ecad8285359fdc0e3cfdacedbefc8bc8e92d745be9a05cdb79d04c488d2991d9275591adafadb5a88b92c22ef6c57996573749f15da60b815ff114500b3ad6cba4a785417a78ddd25df651bab79ee7950d6721bef191d3434159e583d5969bcea7567e42162610a917c221dee170fa5bdee98fb83fd7ef82533ff3be311e970983488e1855749ef9a245e3b141bfa6b8a2819c9a1374536ea89e3f178a6bb3dfd91ff0ce86245d4189d63aceb5a7f0c755fe41e9413e301c8ee86b250375b3713d935cb2b39cd6757b0e7dbff2af4548d2bc8f3a409dc8198f7e559532661eb33edb041d8e56ce90626666af9417a9570327592ee718da14d4ebe798753c7618a5fd8c9a13a19414fef7df7cb60cac532350a78bfe70c1460d7b967c55a8dbb3babd98e0060e1aa21d40961ea6863d11aae37584aff2e27dddc74814fa046bebf2e3882794fd71770146cef29d46fec4bf7cf6dfb370069734caacb1440ecaac02c364e6f06fd4b946e61ba8e97cac3b9057dbbb97cffe5c213d3dd5f418483b97bd020061bd81e938e274ca585d0550b40b49f21d2756ad94431c653ebe7c0602849d9cff71a5debb18718bbb8a0a8657398f5c6c23cd126e8d7600c0271f7971a2103c0a0db2f570b3ddb67581724b8e90c5a0ac94682e0e7680bf7daa4f84be10c4396e844c4dfae75a15d92cb4074468ed2fe88b9375f106f7d396e049d11e49c3574f514d332c807722371af2239fba379b39524d5dc070a39c3f208b52843307c1c2e9517834ed69f3af6817e19aedd3f3808471dea78ffe611658b7451ce7badd42013d5be884292a5e1360456de371778ecab798e0e3b2c53b8e65a1882df1f39c0feab09609f27a335535bb6030000000000000000000000003466672fa63b69afa70c13987de8e7263bd3690b9c02ce07e55fc7a29b56498006fe36e815365f2ff1f82afc89e764def8d114e6cc1eea5182cf875b5abc0d67f3df7a5c52917d4cc730d1bf6edf00138405d4288baf952c809ac9dc0787cc0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ef2a9739c0dd688f9e77555b90118b8c4d7eb749db0b7605f3acdcd59ef0f653e02d47ddf218c1bb7c5da9f4d3624a55a72fb99a0159246d227aa064de05cb56e2fa5a64ea3b227b7a42f721ee6e7a04b601346f3ce283cdc99e45ea06fb3cbe6adfab91d998ad9c59acf1df7a1146c5f01ee0a3e0ff7eab778fa904af7ddc160202649723d2d138ed3d6b0d6bb30c0ff7f9d573633d38f7bfb15c0afecd2d349c0206c12cd59a752a61a783f880d8256475ea3579fdc57b7c7698c58c780f2921b30a2bb44053ae8638bd841868f930b12be85b463fe4598965937e86da4d36adf5bc0606ddd9c97896b235e9263e8653d9169897b8db0abe645bfa572644188c63fc0227b8b5fedfd24c045bb21b4f8df4dd7792360e3d6e1f492e329d5ba25e489547021b010a1d1636cf64ed07966d5c35bebe80028a14e33d4f8ef65a3672996fc57c030c76aa7afb345b772286af10cf4be7879f8fd36d2fb096917f7b94fbfb8d2ec903054282f928a7525bc91dd2ebb247ddce67e3259a04b088d308e2388d7d4e91a9022b4a0e9e483aa11bf448aa61b09dc6c060e15e9c821765ba9afb5204fb15fbfdc1ea8c314ec77f972e2c0396eb42da39d9020ad67b1cc2c2391f0f698aa6ee50cd9b874553535fddee4b733e87139a92d5998d3513400e276cc0631e7b91c35624ab393f9b8561123ba3b118ebed5b9d036797ce50f702b7a4419d151877fe478e29446bd29e1ddd05c41d179b69663b4ef80689dc5141a6cb5d78ccd45e9512671f9fa0c84ef1abc49369908ffa9b582110947c118928142aecc27a35d8b0696eb564eef4d861083e196e8a15af38f31ea580937303470558a64a7792dd7427939ac868196c5f9e85ff270603500138c742300a19ddef6b8336b6b16034e8171c387ca43c8d6a46fb9cda1f81f03b2367431fb21b770028bc49a921c0084374d71314151570b3b9b6a98c6a24e4e5f9eca350b4ca804d2993fdec19d30530537e97099ed36a96a585139342c36f88f50c89841b7ef45acb4da0f28d805ddef2027a58965fb7e79511fe3abf6e41bcff360b374cff3d70b3e1528b563fd05b6dfd480e84949ff68371567cd5d6833ae06cff4ff0bed664d14ed2bc26c8ffb6380b59858638c8ff397b9242765cc98506cb499ca6e6d9fc0b00fc22df125c6d526c76d8b10f3c5a629039f50295349ada63c1121f3b81ff86f8afffc5ad945d38b1bfd4dedaf1a73db76d0888b6126839af7978896437f40d7f4227e0fdad771828c24d4ed608b7d35065c5ff28ba09baf86c235935eacdde68bc5c0b9be1377f5e525a0d98238658f4f693548ee081350db8b1c85e2fe5da244b10593caf303d93d246bfb4414e83cb5b81e015770d64e05e97b23a93fa4b6f456066955ff96b96cd1fd727206ed6cb99d66334f107afae9463a945526c21a1f53d71a960bf84f1e9b0465a1b90cbbce7ff24c3ffd26334f384cd66900cdf5957dc87a9fdcc748df318458b857230a474541e4b75ac9e018223ee4ebbae49898e1a16b1b551c042c1956b9e09cd619a6cd1fbd7a0767f989c5b88524e16f0cc913d21632219ec581cda439370dbcead1c28e03c85c36ea7f99ead600586016e2613bc0cbb17dcb3e597860d710bc197c7345462dc8027417a42d38e71391fdc25aeae188f9ba249557e6e020d32d0f7e6e6ffbb4abf17d3536bbd97a51b03b444b98280b29bd96738ff34485154f963481f62c5ac12c5a842ea9b177f94d9aeffda870d17d608f36131060ecd41d87c9a519559f2bdd7bfe8d7fbf3c0abeba28fde0cfa042a85c023c954db88df77d45a47daef9bf89eeefa74b76095c12e080d1aabb96d7915a32963d0fabc37c976f5a3687188c2c50873540b07e0c79882f17f0d7e9dce1d58a50316be0083a33e366188769506b508308dfb91fc371f319ed1a1a0b5fab4b76ff5742c8b7607885622f8f5d024a0f1dd6e8409313ac455f3159f492e5a2b4d151abac9756deb80df1ee89bcd029f146c6e37eb86488252d95bdf11f419a09a866168da76511654f079dda825043dd33da13bcd914dcc69a687c630e1e2285385a378ccf9c20cf97bc0a656c391c37e3a27cbe5260e18e49cd1316672dc2f29a2fc0cfd2e567d3577fb7f0d5dfa7bd2683390c211732ad8a55cfd20c84694eaa356a993896bfac4e4900f5d0c3d2009ed0285f36158c132821b4c4169568e8cb4306875035fb60caed41070d55bfbab6371cd74f8b2434c4f80512b962f091501b4c575eff689d87916811b18fed6c283a549b93aac815fd2460b16b7241d02f42950e9e548989d30fee2a6826bb3608f699e4ddf05ac852ec54e06909f1972db8767be636df649d8d11545498db1682f033283e13ed134b9cc09b519979f7841557216a6995a6ac45be80d937b355a02", "", 0, 708405700, "7df168846b9a42e9f2b615e6488ae9c008249d663628e738198f1826f5d50049"], - ["890cae4a046e5bc7cde2f412d50dd0b4d0eb282f72fd99c3e36c70423302efbb5f237976d800000000086a6a526a0052ac00eeef743f680a75e7908a6a0413b78e896c697e97f4161d8c7fb28f62a7d72d9c4acc14380000000000d8f313e8f854e92881e1efa1f5f43eeda2e5b4caad58047ecd244ec218a82c598bf5b86e000000000151bf3e33810e17235fe948922a5cac027cf989cdaa5d8c594a06841087974999317b5286a90100000006635165530051ffffffff013a449f010000000008ab005151ac536351c3fb867f01ff879104000000000000000000000000af952889b7a361a74351c06c466d0f383185724618af4dc3b18d7cfd1a0bf7eb8906f64407537d461085a7b65494a791922db90e65a67aed827b308bd775cd88fed5e48f123f735b64799209ecdbda731078433959191f1d1a2af7d0a1e27e8e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000769cf5565f6754f0734d6426f7e302d24f5d2a3dbe9f2ef9fd8b8fe89568aa579690ba195565ce9b08801c72d912c92b014965a38f6a27e2b33b6c896e43dd84e2d689ce8585ba580d72de8be04143569ba2f5c11c98669de1d9fc7ecf22f1299807b81af0ff8ef07c7add772b024b8f3e03d7a297699a0401f52e2683806b140215cd5327a1591deb7d82f2c042e633ce3ab56c8725c06af258cf7576bb14c1900301a87963ef710d0442441c36ee98736adefbe0e82e050bc20e196bb08023fdee0b1c40706c7173d100b959eb6f301226b521e28987d3b7e159c7bc271e75cd3fed17cfceb32622094c718b299bad58cf744e9e65cfdf65722c8cf0b0daacf755f50216596f4ff1645c55d0ced982c34ed375ce6036f0ad4f4050d58dfb8dd416e1a103232338daba77efb9fb2276a9c72199be5ad46c195339fbf2d62b8d5d215f827602029f8afdae74585798cfaee27afe6bcf3f256027a8166d0522fd59750a6356f3030778c12c0724ba990db90600d79cfda870765bbe495ef5ae3594ca595b18d73b031062c79979624891701ebf41224cfd00ae7875102b1b369778a33aef2eec79b37c103bee5af1e6b1b8cc5b735604ee2a273207e10ec5e96229905311b9aca595eabd6a1ff5e54f7a65eceda22cb1941315becffe5eb030910befce7d5e153ce9e36d05793baa8ff44b4663fe0b73e28417edf70e17f98b0aa5c377053087acdeb1571622db00c4ed448c9ef77c85e2f01e52795133e602e707a2f3e5d906143837d7366eed6fdb7b07b6445e53e5f646f3d9035e8b4fd77f94a036fc4909cfd70fc21b2ce890cdb07584d44d1b948971fe22d6d5d668cbfc38d540b15140d4af31fc9df257c9fcbda103948a93b02f0c1114ec85a2c75890779cee5f14f3242c632053908a508f2c402e57325fc8854828da4babf20f79fb4c7a744d771b8ee52dab284ad9c5a386f253ad30d9e55419b5addab154c47344c4c44ffdb22989dc9de0870af3ca566601f42c72081995f55edb2caa15ba4dfc5384210c2c0c236fa2846301b509df221c0b3e1dc5c0d580b48a6263553f7f45c60189fb134e2603940ca64d26f12e20ec7725fa8fccb1d01b487efc5a51f7b6f89947457fcce4aabf5c1daedd92108104864d845612b385a58975718c11d4814cc66133a193719e1bf804846edc525a65931b7e2cb168246b5acf75f534ead657aa5279d854392374c84e49da62dbec50caed93e185eb030b635416480b36386b307ffd885967c39b0b8c5321ae6f67ec9563708013f58eadb791b0eca56bc201f23b8a7e300e34c8eb99f3653d4f9fc41402a8a0a00209148790a463c8ba59a471933f11779ba2edc8631bd52008482f0921d4bdb7dd6b1d65a8a497ebacd32bb4f65aa7e5b16c2e6342cfaf7c8b9fedd72fe37edfbb9e60a5506d5f5b504424be9dac4341e689fb9d2d0e0b7793476c4e1cfbb3085ea7e9672f342df6b506fd1a3b6bad80951c7031a46e8bd79b09e118ea2d86aaf11a8adf87ed384a40330a3e1acb0ad4b4edeb64fa3e836fadd18d6c2ec84a95594dfff68d40510f2c992227183b2ec54e9f3a6311a212262eddcd07f24a5b3a26c301041e5bb35c34374df8d123e111a60414a9611c4651108417b72ea09eb46d609080d3ce0243c0cdd0246ef9a0a2167f3aaeb5fe957a1fd3095619a95180220f94a837a75babe75e1c47773a2c737b447819f82b83eff9e7a86222a7be28114908aab608b7fee0b6a161e3ba8e2355395d1a03f6c0805bfb450051cab7cabcceffeb87b485241be744c367cd49cbb53ebc3b000f71eaf682c431ce210277f5f62666de8a4cfb75394b1fa4d534f37565a249fde13145ccbc556dd0476cc71046652584cb51bfb6c3b19362a5b1f857c58637a77732e49f0c7df9f8ce940059b25c1589649e1149cb6898e8973847dcaa753a84a0fe74eb587cea4091cd68c5ef1d421758428c9c14d8e2472535ee0055331cf7e989bbf1d4321c8799932244b50bde2347525389166ac21df4f5360e62b7ec1b50851df4ae870f1aea007dc2fbd10c7f91376f1b2caf6f89ce478b1124a82e9c7fd773416a70d72e98751829ae10f711f156179b7ed30ad210973fa0387f8872088d9e3ed7157d7c248a3e5e21d37c4abb4621e6dab13df4013f2a99b03785897f2b1ad19d6141c1a6dbbdd467d77558c4f77268df8cf507c32a554b87a523a03750e618958c3295676fbccdc697a3dd412877f4f0281c81cc82a7daedd732dca13c631fb0891b8fea90dd9d53232d719c8b7b0b1bf007b974d5937e4d8bf1f9a265e29aea63f8f2b34d6cb231cd359a712f0e33440cef7a7985a5abe9119089eecee9b43ca57a79c0de90f4be599270cd56e83f5c5d517e1aee9b0e6f0846268af751f6793e6b8e5883a3a903a506", "5153ac65", 3, -436933761, "916016e4626e3fc00fc2ec847824e9430ab883606c993e10ca848e8ea076774f"], - ["170dfb43015c127f3c56f5323f74a3ffcd84e40aa2be12ef65258db4f21e5def908d29d83502000000026565ecceccad016c1e480300000000076a52530053ab630000000001ce148b04000000000000000000000000bff1308c0a531d294dd10117992ff627c00b92aa9b6493779b51ba1243bee64471dd35222b088ea8dc89e8cf88322a59efd05ed0839f542e9813011b91b549a14790ed12d41c18ffa061e5088e798f0c8570de78be4f2a3a8cbf6e602e6783de00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c755356c00348d323a28fe662174c49428a5632ff3780ef038feb8e5f683d1ed3eec7182058ea7a011a4d3bc667b0c51adc62927192548cc767bc954130853ccb4f76b53e283e53bf09dac21e1ceb6d577af0820b2df6ed3fd4a385cac2b52d9258e42b10f893f11664eae709b9387a0d6d605da0e68dde0dea3b4878fa8aa6e022c3e4b6beab31764232f0beabf20e79da2ff0d46196f7ff2b929a8f5cd530d75031b2089ccd425421943b66d12c25328b2171b4af749195aea82626e4c59e0159d0b07b8330882e26cbe2449cede9a4081a72052bac410ad5cbdf33c8ed1e1e448970209fd6ef091234381dd6e5db506e9f4cb8fd2622153299b22eaa5dab117bf4503186663b55679945cc5cb4ea9f765e4da7bf13510e86966a9a9ae1ea9ccd75b5c022c2768cd58513c476b2db2b39716c8ea7e19afd25234c554f3ab98c2afeecc7502196f65d42edadb0dfa35e66ffb86c5f30af3373634e2c172f7d5afcdefa7643702225a71855abb733172836c69e7338960d69f9d72be5a8f04e2ea7c2f328e123502112bcd6117ae913a48280ae7033e034f0ae78c3bee22dc34aad0a6a09140bc5ad5e2a56068367d2d86ea6900d163263d503e398d2e552ac9fbf3d4e06c32fd27695c29963bc0ab0998dc28e887882cfe8eed5d7b3c4fa7d343aabff45dfad13052c00e291582ed2ae62d80052fefa35e625b8b3bedaa8f4102749f7f6761650f4975bc3d2728ec8b0f898c0341a22b08a1b0a77077096d2062510757f01e779afdfeb331a903ba351f60407e92c4567997a2486672ab648bf07a97848d1a6e6907d5e681398a0403edb7fa290e252f5817bbd4f69585516555e0370dfa74decde7ef927d0700075f72d252ece4842af8c3cae32e0e1d9675a1f2525235bd60f3a9875856a935e47def10973489f9a4c645b85fb5d599d885675c59a1dc3af52cd3bdee92cef8f1f58640522da30bda1985dd5bab0d94a30be0270e7f36dc435f9ed5df3998598730c98b10a2f6e01088c95697a180d8d683c5aed853e7b067e46c4e36d6880b1766ff4a3fc26a22c208a39ccab3ea7aa5ba3a3e81628e42cadfd772be65dbdfa0318ead025fa26550af16331911f5ec754d68489e3e6dfe6aec88ff7f5f0f6d94e375a50a0e6e6e1d5837dbc43ba94d9a1268652433d296e07e7fa2170063359e0cf10742bd87360619d6cf0a62867968087c24075c069c2a3c459fbe7e339474178c9121a2af454b03ea76dd60e07ed475b617cd70d80bd87e3d473c68297cc3d5e854a1e6c1640ccde390436bcf9e0f63ec1f2635845fe362b5787dbc52326fa2281db6c7a3b270a84b0b39446700365b94b480d0cf82b9c0f2e24d664ef51a8f8579d35a62fb605905e619295166439422871653945b84022d1771ed5d38af8745e364c9dbf9401497c6526aa197181e9401d3989dc5de39e1f7724fa5243153b65784f7e0ddc213efc4a8217fc7333f0775478793203145c4b4ec5917070178a12381155b700a89be6576cc585ae22b2bdfdc6394b0e042e551569113dc57d664e045633acc29b2da01db62bce6ed863ee9e38c0959267c9b44d3612d165189ac8ffb7565187387ecadd9889d31ca89fd3123432cdfbf015093e717cf9ae19b7488df5a7ede8c04c3bb7362deeef268b5ba0f8ffeec52c0f1d704ebc11744a1deb493aeb870f51561393ac6d85b8e37023b78408f3b7d4708cc04c216f06f29abe0c42d81b8fe20b6ded295d73e3c6cd68f1d1a0dce60da0db8e424b5509b7d7866fde84abf04b1a070c7b4b92d808d06b6bc2b52a080cf65bb1250dc77a702b4a81bbde034d84c9637537282d2b2daaf41edce4ca6263565733778b958461de1cb95cccb4e40ce220342a4a3ce491c59902ab904d076d5e558b11342af0a35710124adef079dd3d95e660f74209e25ff37e60a4b4bd6a4815e3c60e8ac14a8b98f549a70bd305748e5b23ef2d82b253f84252fbb158864bd216c323f7a46903674b78ac515cd0899ca6ce6cffeddf0d7eed8cde56508b65c79fc999a6f51e94f9a832c8a0603ff7ab0a24cf1a9eaf1bda53ebeed64a914c7ed781f745a3499d560e8c308466949502afa3fe76d3215d7c9ba20f427c05526406bf0192b8d942eb3f382afb3adabb525c6173a95a68fd71d3ca546b77464576d6a0d5960adba221417854b8d0539f672cde6023569374326e062eb19070c702806f9bafb4d82fba4f9937d3baf4393ba3e81d195509be32c308d66fb34b444deb73c4e2d4055f6fade9c9b6f7b786c5bb6536b403d7a946f2408f748846cafa8b8192a780d93115cad27927d1c74698f3a73aa5a01a66a8d59c8192d9da8cc11f5e8d81d41f98c7c141929c12e23426b2c41216bf95daa6558264796e0c1977672baf87622c23dc360fdc78826049c0c", "ac510063526500", 0, -542983293, "3c7bac3bf3f71360e4077e75638d2609d5a02d36618b8921cb0eb6d46ae50da1"], - ["d17c7839037cb9daf1338541333b4e55560539bec17a1ffc6fa979b60316b002095aed46df000000000900ab53536a52ac526affffffff5cf799666d14c9300dd41e4c0e54314a7123234c3030041bc0c8dccaa72563f003000000060063516aac51ffffffff95cf77861d4ee92147b5ba7fa4c5434f81d74fd4332345eaba88b45f7fa7a5ff0000000005ac530053ab8be290a4039c20840000000000016a6d5233010000000003005165b59720050000000003ac51000000000000", "6a006a6553510063", 1, 2077770832, "199ed26b22f80c9f0fcdc25f9cf8e474257f62c2bdac83f716241215f92585c3"], - ["3ab0ef5d0379d3efb4163eea6eddf3a71c067d43137704e595b1e09f0b765e36a813be374b0300000001657a468ce30336b321d45474b56d3c4e90bf9f2ae588feed381038df08bc7d9bacc1dfb53e02000000095363ab536353abab6affffffff03101b82a6771daf7aaf71d1c90ae08426bba7cffa1f4464c722cdb7d30b695202000000036363acffffffff04cf560503000000000953ab6352ab6551516356da060100000000026aac17003e040000000002abacf4589c040000000008636aab63536a6a00e0cb8aed00", "5352635300", 1, -2006953550, "2f155d20402bae9e5b1989f1a2bd0442e902af1c49ed140b3a2fecb17b22c76d"], - ["46600beb02305ced6f91f281800f9edc6dea592e3e7b41578a2f2459f3ac45a8b0cd56b3ab0300000001000122f4e9010e5c1748fe612b4dfbc2d423fbfdaf84821fa8a32703ed6f61a679c7eabc220000000000ffffffff029525de04000000000565abab5352302e250500000000040053655300000000", "63", 1, -1066443531, "03f447cc707c48c1e93c4fd5dbe61b74e17fb27dffa14b76b413410c2d494f77"], - ["71556f7004c8c1c2ae7ee7c9b103244c521803fc485d276f90b2a78cd065e2a4d27e18b9880200000009abac6aab6a00ab5100ffffffff4b0f06c64e6ca14bea709372bee1b7ba51cf0fd6c2e2d512de7a64fed0e7d52f0000000005ab525165ab7b5b1349713ebcce7d2e92f224d6770697cf6bd05066fa6dd62443e3175c745286d56f480100000002abacfffffffff3cfb4bdf005766ea035bdf273afaf8d34e3b79beca4ca186c484dfc8c59ffe6020000000153ffffffff047f397e000000000007630063acabacabcd264a05000000000152b14b8d0400000000035153002a5011050000000002ac52ee0ecee70222ed79010000000000000000000000001b032551ab0b0f4e167b4fa5c3607b155ebbe6ec4c2eeebee5d30a9d22873a0bb090e8ee7014774a25ca0cd7a6f8efd9a9fbddbeccfaa52b8f332844083ecd867e70ec00beda0941f98f27ac559fe4a546fc04fce1886cf9d20583dbce1e3fab00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000356d61d05a69fc8c1c60e8d80bcef6384452c98f9e0ae98a494347e7911ac38e93ce14ba6f6abf125e03bde9c682f768a1d442a315e2f1658a90405633ff9d32f165fe2fcc22049131429b0e7fa72557bdf348ba5058a0dfa3aac8495275ba68848285e5ac686abbd38cb480b2c399d5edfc269e4e05606860d3c89c897f49f00329233085b9f4b4cd22ca4b7f2761386403ccf85dcc7043ac1ddeb7638102fa8a0211c4d2b18f8db9ba0ccb81a219635d27af8952af08b01fda2899c637b96dff1a0a185547ed9a6b03fae51bdd58f3e88fdb8bcc4796fb3e7eeaa1713bffa84e68342c2e6ccd2a640790b1d697b33d88c9f9e3c3612960e0e7daeb83d5f550cb51cb02025e52c4c89dc58ce908053909db8d1d0e7edf5e374e4a8e92dfac9766441a2e03150f21499b7ec25ce7654a09fd1df391784099cc06db064ac9ef6c0cf1780d240206a2b10e62e6d2d6124bce11cd9bcda71b9c6b7885c65f7d6397fb4d1a99d35803048689a739bacfa6e1a32989d47e47fad670ab39ac426e4bd72982d5e0042f9002096cbfa9b18b2899b3f15cced701f1deed6d2bca19ebe9059cce91049ce81935e6ec318d1ea07100019b8a2e69b5909dacfc47b42c48f19e009a5019e81b3e453b334c3d1557b2e3c05d735feff0d5595b699707870d868355c6c01940373fd6edb5be6b8072d7968ffc7c0f888b3d22297543f905e8c55f532279af41f8263d07a5be8384ef6b9b07f470a823c18d7886cdfd49e292d06ce10cd9c1c52a9ea3b5fcb34881156effdc6c14050d7d95c11ab304fc00f43a8568dd3b0e04b64080cd47b9b3e204df074468a85f96e8ca11ec0d6935cc0adc6e67edccacfa5b68c1aec1dca878b9636ea4dc1d6dc71a119960aefeeebf365346043731754c5ff348e639e94133d47321d85bcf0013ba51f36111ce3cd38deb9554cd5c7fbd53c1cb8810da70dbdb194b828bd5ff4bc080ee4a062f8a60c9d80e4ca31e764ec6e7a845a425e5c882b58b5ac63bde576c0a022b643bc1d548f7c4ea2a3afe92984fd51baf2a483d3c5766465d60c6095db838dccd588a91b33a172c4707d969072607992131de7e554c40710a6e4ffb71e88db781bbfdfe94fe9f9843e894c322fd1289d1154ca880984511bebf1f6eb9f2a77f5d818976f28395db59ae59449e4ad70b2acc5d2fd2080d6fb391014bdfb9ec27a27ccad0a1856ae42347de39f84246340367341b7d878da2bb390f263913367123abde7f10cb3f7f469e9a1bfd6d1dba6a39cb763f8cbea2801c30871c744de66c418144c7faa0abeb28a46e26e9641001abe39ee1ac34b73dd9da9ba01ed5575b3e2bf3efb687c9c76e6e2ac8d0d6ac0cadeb422d809ac2c56ef5006385c054775bb21b55e1ad03078b1a0779bc496989d4e2cd3ba71bab2476088e9292d77d99ad39d2c9dab043e743c92d1669d7aa5404df417ff13f4ac333a999226862e1225a68f6ebb004073e2ee8d99166e9ca396ae29130a5c1b7a40c37492d9789a95559a78c61c12719f7a1a41b8f7330a52ccb390c65610fefb74dea5c61638face847b4b5d8384f33311a250724d0e6755b53c4283e761bd666d04a3531f2515bffec3554d51681ddf56e959406bb63b7ee488257ed26ec59a838b38ed49ed964f1856a9ec55db6faa203928ff4737e52afdde11841c73b8f06cfa270be1dbbf0d830ad6b853bf3fc6ddfc09feb7b1352525891e416c96e948542906458cbffca99bcda57620f239c2c322d5c8a4db89e0003e951dea5f71edb08dd2450f0e6ea77d71c6e4a5cbb2d562c6514c13b357b22cb7e2ab3a7cc38a4479918e25dbb176d0bc755b2676a5d919ae4102051dcc4956cd6f643eec03659cc81246b543579c93d47f1c2a96144b3e0aba7ac759118035ebac1daccb73d69aca5b0fa783b30eb5ec85372fbb2627f8f16b82a503eace174918c02d54b2278b6e68cc1f1e76374f95441d31d468f2cdd2e8a462a2ee3747f0a32eb45571c4dab493c0035ad15702d7dbecdf139a1042e1a79e002b210993a23ce38f85e441124306bfc4a1983aef419bbbbaa1f5f45402894bb1f5c27a72775557ccf0a1c15ef184b9618003e97303e4b06f27c7a4d003f0b001286c2ed807a22dd7baaf8c94d9f624967a49033b81833209d32d2ab28a3f8d6d6b078aab496bec0ad8ec63fabca7e15e9e23a2d541e2e13ee87bf1a2a12e72c158ee2f83cdf3a8b2d5c5f506511eff3d3fe4db9a7d58ff6e581e837b5561ba44d1344125178dd6bda89c8c3712992c42c45e2a5e9d5c80400000000000000000000000008d714886b326bad60d2e9f1d492695fbf08c1307c827490394f928350610f036b7b8dcb3c1935d680889ab8bc36baa16c2154c2f6ff8e882fb6203e97bbce5d62e3f8f1c0122584e6cde421eced48fcdb97d8fb30ed7d613f305185b7468139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e1d3ca7e170a64e5c5e484f30e427afcfb75b8349983c6a163c36859281642fdea7adf4ca9e74a68760f652c543d6d8dcc9da01974dab23800deced9a80adfcfe60d94ee828842c944ba1f0a44f3d2875143adb14d2934d1a89908135108589cd1c89595401299c6f77e9c270834112b5759acaaac00264d7cb2ddf8e95f05b03270b09d2606aa8ad98cd35e42c127966df82b8fe1b258a0d5ef4eb247c5045a0020aa186522eec9c4876e84223188506ec38bb0eb81e0e9d8ff606f3274038f3d90a23c1db5b720316fd8f828c2e5454708d1c63eae46dbab17103a9b985f5328ed62ad4d0164b4cd085dd7f645dd479f4570edb2cd0015614c009118f327312be140212d7ccfe18429da125d541133468885107f9b4f58a60b725edf0441ad3b7f28a021533353bdee06cc18666b86c63e63965ca5918de74071a30c02e9a34a39459ed0317f23255a54331f65bb4d2d7ba14c3c8e3e06170b30a63241e2fb1486c18a20103017e1469e60159d04385f44352d62d43cef5c4200d430dc9e15c5fd959d32da502147852345185c72ccf750a314f9f26a6c8279b8dbcf17109f77be896ee675ebb693324ea6d66ab06cd1e8ab7c1480b06f45beef8e642f9e4f9e57c2b7ba54804f72e89b089dc7b1a69d5127690973a8f87720e661f9bfd4fd7ba4785daf32a3be54150a06149264056650cc5d6f7eda731279ba61efa5dea91746a07d753ccb1b4588abedafc5e6b49e1869ff00883929bed686175b497d9135d9dbd75a68f83ea6d1219f8a15911fc65e2a2f816869b24a9ca049ec4129806d1bbebde8ef39f181a04af901c464067120a45d0da3556a378886ad2f2bb03d8d31b758b608a3e05f4d498d0745bf0a971a52593696e22018fb783aeac1db61dcd22f4ded591593e1d6db05c38a2a65175beeb91732c9b5611ea4c058d81e4e62e8b0fdeba2a05c7f18aa560a161f297c3e84669c579a6d4d246ec30d9f546a46f10a9bc6fdb1503e299ce3beca994bc571e74d20a551145ab2a8bc604313877d1cb9449ad6a219fd671153ae2fcc6f17e19de5252efcbb47dcfa49d05bdb2cefbc0f7701d850e22f0d6829969ffdfbbad8cb303b8334a23d01ca14d5419998046e86c063ae145c6cbe23a9011f758d70f58ad6a9ce9a6842845c0d5c324d2aa278d01fdb0dad1b6078fc6c673cf74a6a40b0cca2e61803592d2e2e93af2227901dcd4c51078fc3bd593cfdd63ed5044777e267cb0585e839051812ef0827bd1d971595caa4b28b036d15d5d94a84554d4dd5e98b2b8418bb6412f0653d432cd210ea2848b5ba6b4ea63b24cc4e1a206345e67256eb6c37dddad75849ee6436514eed560d263df2e9df68a2d2c49e09fb899b5c1d98f8e685d643d450aba37d8ae9ba4e166c338cda5ea3cb875740e03dd88cce60af9e25dc4c919cf9a9384280897169c7fbb1ffcf27610812c2d49ceab82b2bfc53b895b87d08cbed2a908cd352026f87db00a61ea6253187ddf095da6e62ce971474b070885688b96555201a58c67df8a320e3d8a543a242f183b409fcd7e71668e4609727bcef2ce22a3deedf5b6afcfa550dc4d3f4e5a28507bfdf857c58a71aca3e7073bad56f72465595190f152ccfd1b45c9b2178de37a87943aa92039cfd7f32b70581a5a29b11e7c58b06e30d6bfd00f70c7c0c8ba06787bd8cc4d71b1bb708f3b7255947ad950103759726c618e37a934515a1dc9d22df5dbebfaa5a799782e537a8f8a11e4c0acec324cc37ff385b3ccbea25a701c565e146f5218fb53c06c39632f2379d43f90990e82c2fefcac5a2f691b5a315af068118abe48cc16b7e31ce120fb8283877b2b2e3b5659bce336a40fa2285007446938c8f9ed851416b149238a7e47d738cfd2688728866a2b76459552635ad00bdcb0ec5c6381c12e4e9348b60db6f092ee6fe48e3c3206af77258a699266afd6dd4ff27b2d77ab0ed86343ac4fb435d5f02abe8388335e82064f33592c0b7d666cdb4449e0381dc736ebcf76bc60ea10dacad7a90a9293ab8e06cb83b01c3d5b3380b45fe669de2b92231fa71bec98f1242ff6c1aed5b55090fd3631d3e17b2ad37727118984966fc4ffd994be33a08958e0a9aa10dcc383595ec0b21a05e08bc2f19b0e90a43b1ccf6155591996857bfa97b7f20bca84583339de9d7847a9bd1bcd40cc275630c78b233f26eece2bea87ce7fce606c8fab5fa8c0b6845c3a422f73d86b87b6af47fbbc22444802e8de28796eea34fe298b1945c0cc725e39b17cb958d7098f99db899831b2548e6a7500396186f3d0039a32070fec46ea7967a45f1d69af453d1a7f836790a8441b557c89f2279a05a544f479ef2e7c0faf0a0a1e611327115330b4e5cd4ccf0049a8b3ecad519456cec226512b333a31e2fbbf0d085bdeecf38b7a0e", "", 2, 1953091961, "842e465a3b5eb19a3d8ff10bb73ca662f9a500b434ec30a718f6c3ff15e47b45"], - ["d67129f5011e4d72e966610932dc12c9f748f367289ba98a3984aaabc33bbe8b8ceb9fdc340000000005005163006abcc9d2ab02792cf303000000000752ab63656365529201820300000000086365006553516a634ef76555", "6a655163", 0, -429583614, "2922243e95475be2cfa1eaba00309cefe7623ceb39520afe9ccdfd0e1ef12301"], - ["ce2f5d450389f9f111fbd05f73d48c3c29f1520ef128dd1ab45d20a743ceb16cffb8fa8e9d0000000006006aac525253ffffffff1850091165d4570863ed0a286ab7b4fa0ba099da98c29f24d2ca83899f29c0000200000003536aacafbfc765fe9f9f3d46f5ecd9ef6f8f6fabb06e734e002ff36a6c263a9bbc4391ef64735c0100000002ab527b79d4c3025c9dda020000000007ac5151acabacac06b8a9000000000003ac520032bf3fab020000000000000000a8314c0400000000985882e949e548314b872da6e46f1b99f5f0d85e9df4e1568754ed0cd4e7e05e2975bf73232e3ac861e6daebeb05f566f3de94c5098bdd276cac3b8403d7f938e255a521eac3b4c6944cb055d8069233dd4cbea33b81172109c1949b88df7e3a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003da2b5535825a59af0b903a892e9bfbfedd5234f45ec0c59938088ecaf19906696160d1312ab42fb067ad15c348b9896c15c194eda2eefae0ed9b0b0495c6511cf4fdc015cd81812b51b1bb47423d9433f20374be516e0b4a07c17c404d9a9add897c9b5c668c62c1cac86755660d8499de85a7a046a382a58a4b0bcbe812cba0219583366a6691ee7b6f3cb5bc1023c8454422f8dbe0e4713a402b35769fa2a40021714ed9032de1f09302a78f8c982e2301bb34738d472a45d8c2ec583a772fd970b2a4e67669dffe124d36d3f5e7279c4f6317e94bba1e16fb768f79b164cb089e3290b60578cec5adbb9f9f0cf15a84349224b52bf4c4238e1397e577e36bde5d70329f8163395702da7ae2f21bd66ab60a91827285d1902e9329d94dcde277ad4700214fa61e2856bb526f0b73a5a88bc5aecbc5892917a4938773be93dba617bbdc1031f94e236360e8d09b84c4087364dbc2c6d93e1455843748d85f6187419364d8f0308a3b0ed02081b677cccf75dee14a48a3a744c51c669f39f0773aa362bd3d329021ca8f338758a25175f786b038f46cfa12f0c49e4beaa6ebfc59c0f90914bea3f95f6f2e9984a55a674118e5c5af962c1bb1c036aa96a5a06dcf565199e0fe1eba3abcab0fa9af1feab0a93d968c07d4965eb671a0240119c43f7023d605951934be63eff74ca2c065c76803aceb277c4f5b5bc91e0a761984fde692fa888cb57924c15d4773cc1faa30e5c9fd46cdb1b9e7622ee6a2b30fef7f6d5eba2197a62bb03f248c8fcfc6a1f1bcb5d474a580ac35b36dccbe08288324d6404ad5fa38526a886335aaf873df2efc8e5dc585667219f062ba51c37fce05bf4dca360e627b0f95d06a02ff2feaab9fa4459121b0533b174e1ae16ec364d216e7474fb3e1fd67c6c15359f4043d70043aace69a4c15f7b688d3db9b5345d0cae0470c0f7704e7c2144c5f9f3b57197bf34c3a15795a0f36cc5aa05256aaa3b162dc74d3beac284de0d804293b5d7a2413cd1eb6ab7e56276f0ca1043b2f7274dda993cd9f7f1505a6ca61e002d572f0a58bfbb7eb48f3895d42f9bc6bc8f1f6b9a9ba93278145f17666e39f74be83fc13cc29e684098ab1466636794025ffc5f1cb8bebb42c9344a26e5de664ad8e3aeafa45649d9954253681c03828d82191a92f5ae300527f76f4e481f5ffe7bdf2e2475b6622773a215a419a132db7331b144b48a4d8c94d0ced54b8a1d3a2cfab87bd799b8f95a8f912204d092aa02a538b20a69e1b7528d2377bfe72cef173cef31fd5dd7666c60c487e4cc2b4cf8412f5d93989924aa2464da54e09be6d430ef72c16b32c3b85d0893f673d910af4271d0cdf10f090ec2925b631728feea162adfc3fde558a863a98daabec535d4d018f68cfc168410dcaacfae1de4ad3652d9cb741eec3450fee95d21a9afd21db80ad448c24aa02e804d650b43cf37ddda47cc24ad9906aab4f7b91fcaa2497a88c091888fe235f8a7bc4dc3c6c47791ace6f970d1aa48007ae947885f6f3a422e1214c3aabd4f436c11a06ca318d2c5d1b067e4087784016b73eee6303cd5b1c03a4f5d5f8aa68550026b42baf7c5efa86e6799151656727efd9aae358b20622071a20dbbfab922772705e687dee7c2a41df634bfcbdc49ebdc96002685e3706819edf7f1d3a54223560d2ca0a41604fb56ca5072bfa87f7e31317b0f617cf6f793663911ae19488ea7f24b2655829620a55bd044a34121074f556025f33c04ea1151e4223ac809f685a1fa536bf7f1dd32b7f8164fbec3950ac91cf308017e5bf564b6bf18893e14df36c058b76a8078f8064c5384a82d0624e2fbc84cde54df99f446072a3938a0720914e90f18a7ffc0a62905f0ecac8c1a33502e4d234ec4825cb660055d5034528e880dddd94ab4bfe815551745ce845e4fb602735cab97f0f8190ccd4d0f68f36614823b072f9652c80c48a9017ae2639ab6d5d596f30dc8a01ffccd623feb866b99905ec73036372d18fe7071513795e7aa666ff8a8426bf00f2d1be966916212a4371e24f23842e92f74ed50c3a296540a37b0f475077ad453266e6706d68348a726c9db5cd7426e63626f55053642ea27e3ae00b62540ebca02b9144ecc51454445633f3e2db3994fcb572ba7ec4dc2f578c351c8eb67f6a4727afbaa03a907842e76081eb6abd7a33da105189e69ede561d2af7c727e62ab6f8299370b9515fa416bd4d37f911d65f97675d60c32b17acf7dd9b69b8b62adda362edb8708c4084472d7a6dcf2888faedb1321dd0000000000000000c0d3530400000000d4459434f6d84f488b8b9bc739351637530233487572e3c11ebc53a1e3805e3b9b15356ff84223c79bd32cccb47d6522c4dbb72ab80029e9e770b2ad8b944a8a6af6168f7d94600691c912e2558bd3db84caad9e2e639da3f040d45cae189bf100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7d73d8ea29becca10c1df552b79f87c22162cba0123848ab2c131f27d78e6ede1f6b7559763471c1f9894c028bb61fca085721c4bc674bc94cd9f99e4412fc76ec0321461d2b2a3b4b1a370e0ce25b3a90ec7b07bca18970c304d2735011db3c3195683858422c14291607bcfadbeb95a1864198fc98e90d0ebc1bddefc2372030e50ec82ab29d7509039ad4c63d4ec0fd73ce313b3a5303e1e84b5f01c51f796022d3eb97d9c765840d03a63f94393834de3078b54bd202d6f6879ff6f44fa8c240a1084d5ae1dd198f05daa6ed145c9bc795cdaae4df069fe93f75d37c08467349b10d349e4202ad998e7eb05eb860e7fe337087095628cbda9e23c55d91ea1d2d80215cb1ff813e585686281edbbac4e4b9c7846e769678f914ef4b72cc33aeca47e022004ddbba022629ac82e854452497a36cbd8cfac0a9520fe9597f3f0a48780190225204e5b5fab8ad2c5ca61b585bf7004a70bc3061623e470d5359f8e0a687177022a400101b14a884c0bd0a4d5656dac6618e40597661bf8f4935674a591dfdfc6031ba0cb00c93f89881ad76a6b5823de034c4d446a4a1564d620697713a7b971205c4721c775d0c0109b3e7db17cbbdb2f5b48f8ef9baf425c6919e3d51c63fe8023d6fd6cbfc774d0e8b3f87d3b14cb16a3a6a27c445b18fececeb59a8e2f9691ae5e4bdaa3abf1a1366fdd4688cf9e7b1eddcc5f9aafa2e65b7b3942f988e35a8b0d7cb09a37458d0cd49bed331338a929ad39571620fec22200f2e553be3356edf6109150ba9b6192dd041c0bce951dd854ed752fe35b16e8e842cfefa534c7f7dfff206a144ca0a06173a83ed4122f2ace62a7b7ac6ac169696d53f89c4c7d09585ba9a3f81942e445004ca0510f9e986cf28031b1d5ab1965aff7fbb5f2b16cd89feae9a53b36f9d9acdb73fea5d21497b4874a8d5c336d3717acbc6221a9caf0718e3b6095e775a5b7cb851198bf7601087683ee639211cee98f7a49db89ea5e654d1ccc6b66a11be17a1958008fcdcbaf77fd940ed82a25f40a1cb27e6e8b8dd89ef2525f71029318fdaa73f64cd0ea4966f3be320096c91cb8b90555ae1c94e7c9dea6e365536967b6394818f09189690a7ba91d60d2de0330d53bcc453c8116797751b1f3f8bb72bde0fd63157be630fb032487d9ea7fe50049d5c44c194a48b76224e44fd7295ec397f64ec44d89b8e7f7a50720de3ad09ece9f0f280f91ba92300b6cb0b49e68106d6c024463852ef248aea92f463cfe16c2f17b58c7429eeea99c810b1f8b101615135bd791b7b648f0fe6631e8fca34457f37edd9b8744cad310e196ef8c136f714c13858cd358c9dbab270189197aec5c47b20fac07b3100e9bb20466b34528f9e850fa0389dbf36b3198b82fa0128ad1530f3ac747db35e30dc574f6290df67ba6187055845dc5793b605c47b501527b2af5b5b33cec3a657dfdb2ea1020160fecc3a09921cd65d0aa0d2504ce8f2dcfd65cbb4ee34916eeee0efe07e7e9da0237aec7a9b54579f50f5d760f0a7771ed68bed6513338349c4b7eda586008de89da6921514718abcc05b823ad022d51776a2a98f3d7ad00e396a1478044d61d4b2c5cc468d1bae03068a33a17af8ace040b03280e61496ade4e59ddd71a53a69e645c279ebb0872f4a383d7c3dc68c958205542cecee8b4b8a398beb9d1941424e5c35286a50556390c618937d767d75031ccde002ba7992a6a1767f0c323dcda338d1f82019b458afc169bfb45563c7406f7d8399492fd0df18d60bf6a7d4ac7ac69e10b7654e42e470aa8490765cc44712d593c4b9726c4796bee0b06cc44b1b93f04f7036c3f8d2e4be83b8aa2ce8df6fedc207019eb2f4cc7dabea244fe5703d93fa52868e80f2d1a52eadf27db82b76a785e7b864c69fad456e74ef6060ed1c5601ed3c37f197ffaa41ed7f22fe465848f19e2b3809b275f9ea666ef9091917815f4bd2d55d34cbe6f34cc01a24e01d2e96fd536728514fea9653940ba0d762cf410c3ae06c5c4e192b7a9a407d3cb41ae2f097c6057bd5e00f525b4b93c6ebd68edd13a0a24d3803b02bdaeb3c9d3b47c213597f4aef8ebcc0970bbbf07b204859b381a20d790fbaa282caac62dc5b4bda09613f0d98ba3713017881849db45e06e4baa578927fe287fbf812a58b1d900027f89ceb8ead2d26f2c67a1ecaf0c2f62dca90921b40963fe719c7fd2f274018d9742912cc24ce02494275791cc0fd05896c3c816a893c3e7a3ed0f79dc794e8a0d904e56caec1ca101596ecd24a56a5c2d93cbe8e62db53d6b877747ecc1bdca994ffedb7b510513a9d9dcee1f5389b9516b5e9edd43e417083189828b81458ce9e8de95ffe45ce0a2ff9bc1fdcd9346053d769ead35c2a849f1825aa62e386c15bd5b71b95b6dca3d6eed11ced98b1f05", "", 0, -1886920177, "8c937d96e5a287373f87287a69b079e8ab9afc94e34d85a8ff8847a91a34e5ac"], - ["7cb331bf04430613884824b11d9de1243806a691c6f2a505ba20381a0fadcb38a0096d88740000000005abab6aab657c2121221fa0942508f8fdf0f53d92b75369ca13e15d42e4f65e8cb511b1f4b4f4e1a4a20200000001abd13667324efc03965a962d70baa4e3e1ef944d65f849c39bff635ffbe24c402d1c86787d00000000026553ffffffff59a492bd138bf90b11910968118a4eaa3769c06cbd8d8bfe4e8be95ef6d28a1100000000046553ab657f38ff2e03d2197a03000000000263ab18bfab000000000008005200ac65535100cc216e050000000006656aab51536500000000", "00", 0, 297325301, "c73d676231e173411cd852c3b12089648b09306ffdefc5348f921457b613ba30"], - ["f3f4c24a04d9626ae3bb4c4fb7aaaf64e1d338bf38dde3b2a63e71e675a2c51a0b17c40ef00000000009656a516aac52655165034da2bd38c1551b46128bf284a2a053da7d23bddeced13ae2fc37e840b0cc27f8fd0b27010000000800ac00006a656a53ffffffff10614712968671c78c48e074cd6fe8682857f6983c5a2041efe8bb900508726100000000076a51ab5253ab53a1fbe63e4ea5600929b8849c0f53992e736c8d31350a3e90b2ac7402552888cba4a7ffd3000000000165ffffffff02170b320100000000066a535263ab63e203ca020000000009526a526363535265acc40ae28c00", "0000ac65", 0, 2032497616, "a22eef3af46933ed54b8eaab67309d69950ea92bdd5acd2baca812de9de228e8"], - ["6e32a000015db2666fa653e7636a30be946d545ffd995af0fa0c2f874c6629086d84c531b80200000007526a5151abac659e7708ab02c1026d010000000004655153ab749b8a01000000000252650000000000", "525151ac630053", 0, 45710664, "05fc0eb07f39b043e997fb86b7bdb4dca927f62deb6800b1f4c8e71d34f00db2"], - ["2da8587103b0337a0e9e11907e065bf0b03da64de7ac1cf8c2fe71cd77ce5f10cf1de178310000000000ffffffff91322200a8ca284832864569ddab084bd7f0b0ec8a769b80b4d11243061b67c900000000025353925b18c1b1a372108c16211b700a7e28ad6eb46179b4b574800a3c7fd38c340db7cfe6040100000002ac6554faa2cd01f5331e0400000000075363ac656552650000000000", "ac0053ac", 2, 1186086084, "9c17e844d852e98643372cd31e64fa71e0467c1d5dd1e93981334636ebbd322e"], - ["f4f022d801c86e45c5c28c7692d3146f2aa541903d804fbcfdd4047b26ed14a492ce1f9d5203000000076a0053ac530000ffffffff03dfd912020000000000897462050000000008515352656a65656a7d80e2030000000002536a00000000", "6a00", 0, -500505332, "92b28243cb4cf738adce6bb884c9dd610bb9e0729131ef7b42390f4b035bdaec"], - ["d931ed3701f63c9907e03c63e2a74c702b62d1972bfd5d054627a31179332340c700387d34000000000951526363635352ab51ffffffff04166fb404000000000251635b9d33030000000009536351ac530065526aa2059905000000000463526500d3e42c0300000000095265ab636353ac63636bc9dd5003000000000000000005b7f90400000000d8ca0296e10eaf596eae0e6d1b7f7ca2b4a1e1dd39eb9b1556d4d5456a961706ac9de28f2fd7ee3cf5254dee322bb1ef9a2be023338d559ca50ddec0c348622db539d42cb0354d6816c83d9b09dc44c9fef4b462c5d850a2b9043e3698c2ee5e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bfc92ba13f9ecd6e7ae5e1489768e4c41e133138482ac2488f575124446f7e601b2bf41da678ec98e9a07f174795357d16b84676000f315fda694b790532ec63166885b2041d5af02cc3e372f7690359633a00c2ef7ee21f921fc350d0dba62cc0c008bd06e4ba40c8fc742da0c86e650f537a8efb6991022ecf0997ee95919e030f1dfa77139ded7e910b3e1d637036e552aac6d8fc275cd2af0d921c4cceae10032d819a90456c93799e3f17d32d7ad16376bca36f49e180be72b761289fb927d90b1c855dbf0404e837e1a5c92ec3829266a0b4a274326e94a075b06bea05ffa0820af335390dc46f11b6667c1c972ff49203b5976d894bbc17aaad1ebc0e5a8f07031a839f1b38c8462c9b24e1915e96c4b5a7e0bea21da6619bad13250669863d6502064d5b99687dccfeae720e33c98cf3e3ff2c2b7bf81eabed5d01e6ebe93684e90209d981168ddca345b7a12738983746af41a96cc8bcde6c4d343fcd298fb2bdbd0225e0af44641e265c4ea8e2d35d61c2bb15459e1c3ed6242628dac5f50d40a5320214efa8f49db05efc10e3099136403217af5ad84d4804e8a909058c20c924500538a56d0092110e01f4c57f72550f19e83e7c15a50bd77fd2edb4bcb54a493946ae5c251c03150adc23d0e5f874852242be7be18516997855a3cf09e2dafc95964af52864e3c9c0516e1a1cef4915fd30bb2ad9ef033954ea18c146d1a4da94926d94f453576ccedf95fe05ce2ead298a10885cf82bea10d24fe89071eeaed5947403b8bbe4cc78efa389ea8f30f1da4d19a18c848f1a09e6c1e41dd007636daa423881d69dcc8bb88cac9b5568dfd2367eacfa33d6da1c00721514aefdaeeee9d26dae1ef6af69f64589fadbe644c236157f247b5567517d69f6fd6508194d3020ba9812db04d888de19ca314e4ea672ac56ce59a13181ab876c72a9db4a0a36a09c5865957db05dc0ebea3feb5542e2b1bea8a9d4a92f998bb6786c645a86a2e8d17b55d54da3b14e72a8a6d5f4c0ecb5f1f22a19c47c61b4b52c47254731afe02684bdb8fa0f41e1f812d52cd4372b495ebe432f334a8827215c31f52588718b3ce30ef958d4d3ae3ac5879131396526475c972eadaf7c36d950451e1b194ce4d2491e984d4c0f1a8b41e52b4295b1ce4c2c1ea96d79c267bd9526ef3f75d724dfab6551be2c73ab8a759d73fa7270c10da955930d3af6e977aa157b1550c7791a5ee3b669fdda39d032fbdbddc4815bd4147db34e54f7199efa4b36863f6f4d4918b14c0795195930cbf1b29cc51fbad98ef22031fb9e0a6fd04f87eec5fe431d5afb408234da5e7ef5d42f5c41b5a2576605b5da9aef01aaa4c36e37fb0810d7963ca2caa80caaf3a4a4482f0c9c392ce0990cd0c875b3fa9c24d1fea4b75a77d95996834bf9900ab890fb5d2885af4afee5e497ebad3449837c2b6d735d1c8f2ebd1f3f54bf71da96df325fbb708c3f7ab509e056c1c97dfc200460874773b212c7b094647c1e0531e3e1fa58639f95c5e394ea80c546227d9ded5c6f81258dbe9492c7adca6b053eb811c239340c4d73a8558e81b69a496ef26383c53bfbb94b031719baef6e47d692e4be4ccf660ce45fc4eb31f29b228251162b2b5adf5f044207f22dc3c372a7d8d3051145dd4204322c6e76c3f87107e5efff68bcfc349b5d638152f4bea665e4d03e836f003672ccf9b7a3e9193320fed561e02df48f4b4291b2f53fa4eb986370b451b8f441006424a5b05b56beeeae69c19de870bc7c15ac3952cf8c4727996e5adfcbb3ed5fc95f01aa1057827f123b34c9aeb51d6961fd5378b7043c7272df6fbf5c0f1ca3069e5baf4f633e2a2a76f38ba6ccde3c6add647946dbad6023f70584d411eb2b4b934d44c59c36778486898966b04081b0d388b25cbb3aae46c1a00917d508487d242cfef91bb5429ca670461d2f42518a3dafd02e193ad4c5fabf682a31c026eb608945f0e455975e39472ff7a46499ad25e0d9f7875e8b7efade978ce5b01b28de60215dd9056ca1b5cc89843df1d36311af9c9a5a545ac8098c332363cb7593732366d58f445724f4caa0501b388f9bb757a16de51bb9c38d92ef0538d9d0caa72ce89e52f32f49371e73b071d93ef2e86a830e8e4552f1a7d2ad3c39dc441b257b690e152a44321c70a9050409693edd5cd520ff6551dcdfe91adf9875ca7e520d6cdad727ef7cb99adeee3645f710fb75624bb42c04554ebc71585942b6ae4c6a06fc0a50beaea7fb4cc41feb0c43286849314d0cd367f47f750b30e6999d3b00000000000000000000000000b4d8825ce8f8bd30e608ce84eb4584688b4f9267543441e292dde6a2a572516f62ff8bbd8d5115c89818e3f1defe3a851135f4163cc57c38a28cda1961c38f9a2d473c5c3f52a1367df99318db2ab077da48832b148cf6067d1ef2d5e23df023000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002cd20c116437dee968c1fd11bd952a558bb0c3fe56624c444ec196c7eb907182f25f9c4be4f1031e383443a515e8ff926ad6f47140fa8e6d66f0e2278de345f5698d5c5e83eef02cdbdfb971f8b5ba35aa3698a7dbc450bc921d49d2f77f5dae81b7397d0b7e5624804481a2351d136ac784bb4c57f41d5dbdc1b769799731a103204668a1dcd08215f8afd335f82d35ef8f732b2060300af4cc87d0c87226e1cd020079ab860588342618dddb97567eb2393bd3b2a428d11f38b8fb9f6a245e72810b0c2ca3939a66f6f9478d6ff11d74ff32663396aff463eb530ad07f0b0e708d1b2e5c27312a9b907ea0bcafd7e72a01573af2e1c2f5b524712464544b6f8659120304458c30d29ce08c030725d3ff39eb8bd9d80cac99fed0bb850c97c1880efacb0206b17cef974a3dbed9d48ac514bdd3971b713c98b23f82044eb7800bf73adf17031fef423f58363d2737b739fa44b010c5cd06f40d83f5df061f6749e4f68f77180330389079a003e57a184ab52eeaf85478047b7161b3f6381e68854f4c91b9333c03173d9f7e198e1392409bfd3ae420e2f177e4e15ecff6a52ddec3277a4218c39ecc8556a88d23a8de752cdb0a8fcb58c6609696214e95f957732f556d489f0f7be6f0773fd8bea10dc2dede9d6cf49dcf71293d19e434d0d7257e1bc6e762c611283b221409cb47eeda6322760e2ce42eceda4733941c22a176910932382b7fe5c26d8f6f7c7f56669a19814da196dd53d6cd0abc7bb8bf51e7d59f8594d980a15a20574184ac64a5b1b46527ffe78dad54c6c07468323e8df69dc53ecd0c4f4ab5ba6ae35ff6b559ee05049a04d40b9f807fe1078743796c87ff1bc1f677df88daf554e53e149af4200bb36e9202a143c03f3453264be49dfce77bd2b92e418d59fe6819310f0fb0ec91e72f17535508b28660a873720c5c75d384ae2061d32f97cdedce968d7ff2454b25f6a6a711da87d85ecc3b45f44097f5caa5d59c8573df423336afa136a52420d5d5443448b3d53a0813b44f85dff0ff23e6a81c34e8db15f641f238ff1759910b04b9aeae4076ae0af083dc09b7d9b8f4073f36b50c0b6020fffef8f6f76f9f24a8115e82fd7f407d18a8da4ca29f165762d0ee0673606e8ce116035616fc95a3df7e9a6d80f59039c95d278a80716df803b64f43ac69a9e9c1aec32b1d89e08c4f2e82226582115567f6ee655db4aec98922c0e07d592e83ace5a63d629528cc34b91ade9ac342cba121131bf3bfa3d4cacfd96ddca0e2bac721150ee6dade455a6fc99439318f3752127fece2961f97b44b35ccba742f1933790f896ecc15ae3c6b4188b406d48eb879e5d81ac26d10e009aaf452d0ae3fd77efca5194192e276dd954de9919852024cd21e80bad5aaae05549be50acb3383049d7ab8df13a2074d1101c4a70bd9183ea7f534a225a8daf012b91b19a70333fc598f922ffa7aecfb6ff8ef48c4309500b8b48fd648db335cb0c21432f175f6676af57bdb40142785ae6210cb739f4b2ac2da1f861aa7c795e756afb3cb07b2e62d97e8d864368535e96678f927bf569a173c7dc89ee19b2b4edcccb5d1966d0d7dee86292fc7c2befb17df1c1e3dc61dd87a6552e36799f077bd6e04ad09308803f4fed887209ce6c1481b3afa66c907afe17d019ae72f5a35d473488495404993fc1f3119dce6cb8136e68b57ad1fa03de2915f3f676d0fb2ed6add796dbb6433ae27e60f849892386f34bf4893dd5213513818979e97f8f69031ebde956aefc788b4570c8e9aebee90bcceebf9f266b05789ca145bc9ecb101432d72bdd7d476010d803359beb71aaf859873bc1e99d72b728a743375ea67ae493338a87a1e58e4c2d8fccf7690e092c9676b62240d45917f6731a577df948118779a200c48d9dcb46efbcd79a6fc909e9a7b7e0b7d414486ff476be92fb61a5c570ecaa8d557b226357978ee94b74da0b47d27974485384154b6e8a2a77567822c67764baa67abce8254ea83616eede86c01caa3b98d91152f38eab0d1f47df512f1bffc938aa78e6cec784042c3d8c156ce5e7b4a57e59fab7b0c486ef57f49f7d82209b2efc8e553fac9a345c173869cef6a9e24fcb96149c4e3e08c9eff85f75531a848c930bb48afda53563ba357fea4341977b5480240a64b036e97918c6a907df45552c52a2e84d6c6f4f2a875c233d878a48e839457efef7d97c329703dad08dedab9e24b2f88204802eca2041f3887b5681a0ee61e0a463255be9968ac99907be72d98b7b9a11ab84bcaf80c40cf000000000000000000dd1d0100000000921d6daa30a9b7f24b600f1c30850c6c3ebb7c66af7dd0bfb7902d4a034c3e3f2b5df8afd898a487d8a0e3246411bf6ef4ecc1652c7cfd6a9b07c6444cab8c263e80e89012e0841f215538c8bd5bd75db242e3dfac8262ba667a2c19b7d176490000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051c3090045d1cb38b9449d94cb236ec590fdfef71a57e90c3bc6187751b15bf9d8c56c31ecf97b6fff49f2e781cc849131459df0305a6060f04510e0d5d9236bfcc4d63db84940f1ca6de03f4df8838c9a58c40f33f13d31f4ecc0f1fafcd07ff38327f2f97da82e4e052a1d526787b90bc714d94be4b9b37fbc815ce510523c031cf166a9d5531364fd3bac2bf9aa2119b88b8afa820f14421199baf8eaee144c022fde4a7aeb77ce3c4797acb6d549b4d9aa0da6bc57b71c97a50fae603ad6b67c0a2e3846dd5de629e89c9c19ddba40dedd66a4df09a9b1b0ffec1c915fd090005200fac587c765a88512a77790871d09373771fd5092b71bd855c2fa2b2fc695b6021282fb3d71a8f9d736a67777bb0ba71a76f74097c6d94c7b80e3c53134ed49be0311740d25ffdef40ede100313f2c1296bcb3aeaddae1a5a27113d66d89d3b018f0305b18cad6e7513265aa05e81105ec00fa1d37c8c1e4551acadcf12360f7fed1f022b724a77c5b228803876c7e0c52015a3510fece85a73bf743909f1774f560a83031512e3860f006e1b0d7eb9d7080b9f6948bd317af71709bf5a35a8f5a2b355f4cf0ba292507b7482325bd9f9c66a48beb0a7d95daa5c1c5c1ebda3b394fff9e4b8b429f1ca6d089b5f7cf3b4fb49043407b36a3663903061b0e2256dd2aebd7a15882cfa71a3bc027e685b4b5e06a4ab514b2c7838ec6e38892e489152ae4aa428a6bfbc20fbf0dcb0352d43758832f9107523cc7dde73f8774b423d6d297f508e2cd371394822721c4c9acd0b57a93ef894656d923a4dab14bc90c4de5bace3646c6ab8bda067377302a87d3adca2df843158143ba00837d85d3d6c654f94ea7fc37a087089e81243d040e6717d0a748629e8b951017aaecf26882ce939c762a98bf4feb03ec6a5245336f119b46fbfc8129908586432d1e56e39e29a5e062e140882c7ba45011574102234185769165fa8864d8796184127db34c762b111caf4b1c70de203f36d86ecb66f6c9a186e686ae401baefb733dbeab9b1df39d0e300415d772e6db377014f669e0cf1dee77a93b10afb5539efe20a43584535797344f3ef7061a877d92c087d4677e1a293d0a69ac00d36e8d435074aa837fdab20547b304397cabe326479750cde85e53cc56fff197c2b8a50e6e9c79d2aa150118519fc58a3248e109ef9a44e0a1e5b82304387ce75fdbbe6263fda129e0998daeff223d6ec9f85aea98b63d7cc8c13e348f2b11530cdea9ef770210026bc6f914a78874c80b854a82cb8ee5fb73532ae1b31a526b740de58b503e73750fa8156a502e3f36b1aca1266d2c7f7f9e6cd41cca82a64a1159b023e0d93d5feba2966c56a28a9d26fcf659bbc90c9595b0ca816a36a3fb1fceecdc5017fb8e48bb9d2930fbae187fe5525cef720122d76ff23f213869c04a1066494f01297470ac4d0c9bfd8cd68868d9062b4207bc2476bbc5d3fc5ea7d84ea96a0f08d21144e6edb48c16ffe3ac349377585e373b2273fc092d5c10cb3579ce2713473e2ca4926a1a0ca399b334aeb0918587f58bdb29bdede9fa4baf386d182bc9e9d9e3407a283a57be482532c4c9d801d9fa70bb3e228aadced8066cd6a919b8e7e81e7d0a49594224afad5064cd4fe200f37cfa515b22161d9cb4c25981248e26f12f9f34a3d8a12d33201adbcd05d3bb524e7ccfae05f2110af0f6ee511b097cece75d97d57db9ee9d70e9224872480add98da821531feedb013e04009f244255ea8fc2652303b4cb3f93e37e2099bdc7994292a488b139a87d788088b6251393a2e37cac392a848d8e12d2dace6854266999af1c31a3d90a1289a8327dd1e3c5e685caaaa69de8ca4a895a595fd03a60b625b36612388570408c5f760285dd68d8a66beee16d1d6c4446d1d6ff380c1a3352bfa4c6699747a5ed82c3f1f46f406bfc5f3040a28cdb77de2fa5db820516c3fd9ee36d79df57d3cd2e52265726bfde04afb0d2c037d2093ff898e09e64c89a081bd5544d1403a4d07d0354a0bc5e037957b6aa3b7a61523427479189a0939c81cf9e150a19c36cda9fcbb723f161d67fdd58593d654f1826f21e2ceb816634551bbeda18c6db44b199379ced8e28722411183df91468ce3528e54f0140e13ca44be501acfdeb0ebf440f2bf4eaf23345ec02b6fd5632a3698845ebd917738ba20631af054b7955636af000bc13e9ff9449f0a32690aa0ceb56cadca9537fb06eb521f2f19da826f1d2eb0513e5935ec1a845dd9b2d2ecd41ffe7cee6a9ba94a91183924871d7ee452dd32bc0db642a14e30cf27cbc7526843e8aa8bd5bd95a763b6e38754fa9f69bbcf6bc52f0e49cf3e091fa87bc7763221f42d141d6033e6e19eb1d0766118ca6384db95b05741c6a114816eafe545ccc94ba397419f4051e2fafd94f03", "63000052ac", 0, 1165044030, "c75654769a91e2e71df9f286619ee434ca072455aa21df4e1113aa811da19e5b"], - ["21aaacb501044db319a3fd79b449821521c34df682f629ea551c9949ef816a903abcccd10503000000005e640a3504296280000000000007ac6552525252ac458c54000000000000f8a93e0300000000065353abab6565fa67710400000000001148577b", "53", 0, 2049148901, "7cdae182725a050e0bd62041f892cabaea3beba289b3cacc14975c3af7b1f9bb"], - ["cc8a445d023282d889e1660e2423a2fa49807a1bcb891611c65a0c03662e1e1c998186743600000000096552ab5200ab650000ffffffff174d3b59ea1c2672bf07b6f35a1249c0b619866551ed6df30826376d9540927501000000046a00656affffffff04f514a70100000000056500635251c15ca2030000000001537633e20100000000026a6a1b1d9b040000000007ab00536563526a0000000003e290e50000000000000000000000000050b23db4bc4347ac2da3cd5a21bce5afa8722c8cf7eec4bf176b693cd3411de9034a6c0fa2c2ea266f7da02dc7cbe4caf94510d5eab152ee1e5866ce42607203b6070a893e52865680d5d2023ac81fc82b3030fff589ec59c8efa5f046d372e500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027d289f7814f6de231e7589473b6d260e6a13d6320c7b6fe2268b0d4de715804976c6dd6117ac234cee9fc2a695193c32456459e8c1eb0935f18c9900a25bdc1c066eb1cb755299d49a2b2887cfe68551e6d606b48c8040af31e0d69299dc8e51a985714d2a2278e95c2d47fef790542e706638c4267cafd936dea7db0d929d0221e945819cdb3e731938db2f01fd7ab763f83718ed55e2123b64b059d1decbb50308ae973455a498fdf31011846ce670dffff1c5586eea9b83ec38e61d8ba2bbdd0a23d04630fd9fd5b64af810d63cd805c9f91797b2e94e7ef4cb0bc123a2cd2a452daee9e2a75247c63569af907d6f63179f9d9a2c42ab6289227448d5fc09e7450224caa9fcfd3ebeaf861a27584b17c79352bd02d5872a954928b141a68f3ac6d20224d9edf73ba8d015590310e6302b87a64632e11c2eff6cbe92e40ba31cdad47c031a1f1b35eaf8f45caca9cbd7027f6129985fe5786deb57ff099e60a63c126da2030b22ce26e9e344478967eedd1d0835b877464724c44fb850216dd2ec8bca4948022180df03edd79f52efe37dbf270c9748a8dfed31ff1bbf4d70df50f53a91f6de8805e618f2d9664b89581ed043b7713393272a0eef47c0e67678fa74de581e828a16307e71fb91fc31625e8e49b37e1009c26e3654fbd7ec747ada63f5e4f368c46cab11cb6d73480e9dc01288d4b735b9a41857332abcc3d3e633999b63fca56ada15d1d4ef2ad8e201e0889a5dbe82c3ab146eaecaa9db6935c10d114afca58c320065150f2e7be74b73c9d3a81b1780f2b81c90b3996fd2b0dd7fadb30226326f5122de57fae5c0005a1052c04e34f390ab45fc6f83d16981cae06ce342d1baedbbc8d3cc3d620fa77023157661a2a0169e2ec7f0a224f21ec4b9230ffda8c0fc3baf2352964808a6db5adb37c5f615a34924f6bfd90098bf13a2663332b152b4e6a563fa168aac65006be072fa2001c86ddaa74dd8c87720899873187cb2052ddbd357fc15d135fe570202037ef32ba39b0e40a8c9858c0ca3b2420febc7ad70e5284627f7d6fc32fdd86bb7cf67c0155ef399a189cbbce6a83c010f8124eed06681b8ae1c6ceee0296f3ff3624b23e3aaefff79ea205d76dd49074bf649e77aac65d623fc63a9ca4c0a148b7cb0b2f54247d6aa23d6cee8031b7495248f32373305bb6a4c085955943d178c368f43ef4f1464850ef78b5aaacadd7ac5cc4f472bfbd4dafc3da16f2db9e6a27e4b4e7edbea62f7bbfe5a61515f83b7d71e7bfa35b565c7068d1d5b7ebb2c11b9433e403b3107ca3bba801bd45348275a19e3ee4803b655c7de6defde5d7d4240288121784c58ef32bee4c84d31c44a59626e3f3f82ab668cef8dfc501bac3076e86085b5d0720a3e977dd17d09fcb940155a2b6b2dd13432cd0059ee42ecebc08b1d230bf9a6138c1b61193e675a90cb6baa69703adafcfa134915aa2cf870e9609bc43e85add3b0be77f59caf9139da9d60c37bb5f71414988900fa9ab5d70edd6f2883cc5e387e2fdce6d910110c0b2e27f82a3b4daa6af72e65ad28148f0431662a829d4d9d9f09fe7760c3439334ee1b361b3847029f9f2a8e56db24170054dee2de9409d12d829a9d97d05896e39ab394d0920b4eac8980f113f11ae34334df9bf590f83832d0a2449b1994dcecaa452bfd0abc68733b5dfedeab90b6be8c30381365288f8ed4a1246e3a44a50abe6f530d1a5e6b615884715e19dff84186fb0097811e00befac87ae75a39e1bd50ad68d2d625cb04d6942c63a2694370ad857b459355321ff2b832c0c89eb11510436d734acf87f30160f42927ba40dcd8e9f0c191dcd018100743f0a8e51bb513c46583b7737d63d0d8463c6266937cec6b8517df27dcf8497078f684f5f6397d35b52a73009724b0996d014ecbfe4a47d3a2973eb6fa8c6ff52bf2465a06b1c2b5d1586a987e87386f523fa94aa843672a5698f9d6145840f1b73705c299f882bc82b230011d1181564c6b2f8d50e24f84bab1d2c7b020542bf2cf5bd3e1db9187ac091a17d9a0ea9ca7a4b29aca1d217b505169a50231532194efd0a7c66397d2ebba0314e451955b1f8a15b29ed5bd33dbd45e6bd91157665400a7205d5b7e5c845254465f2f1cc5abe6242e36f849e97f9f136da87762e12c6aafb25b9ad628bee97f3b95750d44e66219e1f5c2e941dcea960634f0f18bebff7d6e818b6845a029e40dc1fbd8a875cb20d7fd4d733d88e5195e9b921b6664eda182a0c186c45879bbadc412874f17a446d9fe13d9ae9b000000000000000050ac740500000000a6edbad91aa554cd2abea33541b923bcc2eeb295f9c9b32aacb40b147641e271e63c500a70c03213403c87426e36422334685fa2317afbaf43f975f2b8ae2a93826f152329f59ca6319107a78a18db2eb1b26d4b62add8905b93ef08dd127b6600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d573133e19b46a9a432dc02c617eed48d442994cc5cb417bbe137bdca94cce1af2dfab59cb5f30d02e402cbeea50b6142762fc8ce884ac1421170a2132946cf5f8a388528c970114be73a9de381df7207ce2524f696b4398f72b66d3ca68272d4c08bb3a9c1506b4377acae12a60ef526fc8c8ec7283c27b3aca7af6275b6476021c11240035f24e4668355e28ff11ae2b4521e4865aa4e3cf3ab56f4e8bb62e45020a5ef0874cd7465d9ca6a851f41167ed615d01d194a8508e4e120a2788f53fef0a246172ef46efbd8944d40534135e01fef85d7fcd9039ece98d62c02b6fb45bc82f4d2785735d3e3736d12be056f2fb896cde97d7c76fcac8a09bd02453808973022e97ec1f07f1a82ba9059b60866f08f90f61febfb31fb6596a91e1b76f25247c02134e3f1fa63d6dd6eb8830e04f1a1f8e921a53983ea0b2fb0614a64564c5cf4c032235f90f938505d1f324b73e18ce2aebd350ff7b1360f447a4fffcf1a4a5424d032a0cecb9c01e10376ebfdc734f1121e64065c7d14f24451aedc2b835cae94a3502006e61e73db14e1a23da86f5d6177b4f6f786e20e23f4ce6fb5f34a915c6328a52d38b7debe50a9bb69e5ae30c7e1babbb83960386c4cd47a6515e0d3691329d42bd89d99b5814725c8776524127c9ae4a0889b47404833f8d5482bb85467ae8b368245a938a984d312118daeb28e8a677ef2835393e465d87756051b5e574a67e6fef9db7e3e4f3c90c6a1f06b1de9dcb6f209726a3074517bae491592b4082c238d1a8c5092599ff700be148f92bc4970f1511c89734f44d7996e50d8ee9076c0624527e5faa5433acd76ff31ab6789a3713f160370ced86e5a20b21b545c079fcaf2a2bf15b97acc842ab58d3eb0931b431c6e643d13cb046729c1b70cfcb4d6ba06ff3a5ea8e4ba841165016da0ed501303c007c5edc775e81a7fb56d6f13387ecb2c2af05decb9c8c3e2665c342cdaacb5c069ecb9b1ea2aaed900b0e398e037d36618954d70158caf0cf734dac91fc0c72ec965eff2cda19b9be52ddc53b918169a3171267cfcc82d1fc3f8d4dfeb20ab3c771e6ec4bbe95ace0f2f3fd6510f5edf4f2745a9795a22a3923ef91e13bb9dd9777e40aa2167e00d565bf318dff12bcba19c30a27340e109e3b956c406806e81bf984c9084b1e8718f161718fdbf69c73b1e7cf79dbf9fd387950f8e858619865ab33b9f7fb59cc14ed935941238cf1c19f8ef85368ab3b354e79007d99f1465b5f06b71dc357501f8e7381459302e925ace62c5eb57b823245bc8eba68f5e1457ea8b46f1a1276a9760e24266321472cb37f941d74a7e858fc4316f87cf2a1bdbd93eda9fd66a6d752b73e2ef58624383c420484968fe24958070885cff4b112ac62cf8f6fd32ed41de7d763ecbb6845ed380b6069429a300ac74eea685d6180a0731e82e9a50d0d7fdb29bb4fc2635f1738bef5d92f72a5cd02f597218d3684e443d60a294caa62f12777c0fc0ccac373cb2330b53d4ebdba6ae070dd44de84be45aad4a7b7212e58d96ce50904537348f0cdc0bcc51b00f5f49aca0c6f8159a7846bb7ea0bbaa5c3750e32a054ed9b5de77e20797dd2c817eba140b6779e0c6e91cb0f8eaa57b12502e9161f8872b9322ffc4b3e83b4a4c42255ccd8f8a0171c7cb770c06eb2c193791496e195ee567a6b39698381dd4d03651411fb57cfc02afdb7c88968f15416a9f985e5213c52db8cb15a8b273759f2546998f88e6d53ff708f836f121d25b38b80176ddc1b514d84523b909c56ec7748e8c6321b806ed95f26453ebe51b8b81e5ee8c63188061c881c9169ce975ade435e570423a85b8d16e02affe7bc228c6b5ce5187cbe1e392036466296cef057f21513c1dece2c6636ff6316d34d51107ef1ddc06676ffbc63d77c70bcdcba1bba3c1f9ccd56168c591ca06c099b1b5324343f02c4447a5133fe6f2fe96f7d4f4bd125162f527d2ff1689d79c65add2b00aba8f7db8d242c9a67d9fce20a25967ec4e053bce7e3989b5b85c0b2e26fc9faf000feb4c0a53075ac2130efd6137a4c29cb9a9cdf8f6ff20c6aa4422450270a12035e393ae69a4575b2df32129cdb5e87278b83868e30bebc985bff5eafaefb1e7010607abe29f92ec09e91ab7d57d4bab36a58f8a648b14c3abe3e87039cacd975fbcfd8f0f5530f2f1b767c776927506263ba8a47a94aa607242d998ed14d1d91d46615ac2edc9f0800cfcb649eca68d85b8b2c7d5e326f3cad4e8968b99b816b9112cc159f8e13481f30e9004c44bc6aaed7870102000000000000000000000000b8fc3be2ee777dabd971bf977869a4559149230980cd013690010b33c109fa96f6bd9be29880c33e46cd758b37b2c56dd8efc73f4d76a7e1d5eda6050ad7050f80121a9a32bb84d27996ba0f2c998d531c590af36db75e2f7a3ee2bcc0806f5e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009b0b470d253cd82da93fb647ff36efbca50d4403b4e04d3269d310a0f0401306e69c172399eab7753ed90df4877c443cf2610ad2297028f45521de524566ce369103ddb6e1ad4cb588324a1be7eb034b02935a15922b71530e3fee9747812e6244f08b4ad2b4836ce39dfd68e24cc1719ecb0809c26b0c5280e2bfd6445739580200fbf60d1fdcb06afd5835188675b1ad4c62a0c7d56f15d9fbf20c972abeb2f80319dddd512f9902224915aa3fe7f4a4373935a26f5407e73a99492e7cad04b5710b134c2c176f7ad6d6c65a902ac5d93bfc2a1df4b36406e75b13bbc70a5643f8aa024890c9f35747c7f190a92662bb10b1a7db5b7fb47e494f75242a041d41f63b022ec0606d9f4c48e3c6baf616bd7933c8185f90c5b953c72f6c486f9a56f71b3b020bd29a2cd07486e54dbf7914b0a8a985a0a94179f29c79cfffb9211556ad4a25030b47395191df3f4cf63c94dbdeab09beda3f977af33225151f976eaf7e10367d0305f231a547823cb658aa0ad8b2af715a1d77b93f4f424b649fbf9a4daaf122b4020a4d77bd3b1876b301faebf3cc51a1945df01a4b62545f2ff14b35100fc8cba8b8fe1f8f96e3dcdd10ff391f04b9edbb21de0a5532b263743312985aa04723b8acadafa161e2410b132acff0ac8b5312ec053d8965c36c8483615235cd435765abac7bdebaaca171c4b449a91eef96d35f35173b1dc69711309d49baa0643e21d9a4e416881cfc9e1cfc5707aad4ca871f80d4ff0a96814fb4a02408e242b0e2c44fb092f44ca2f17f3d60715b763ff1356fc066f11934b3d863ef9ce735bc84d42a0b84ea5ac286faeeff7632525f18ef7c932d2a905e16cc7f44a591cc3c13db4e4b1d267ed17d14b1c4f8aaf9a951aa9793d02c2273293824e91a9e71c63a1dead9081bccf2c3805925316598460174beb34a6a17a2bdba912ae578684dc6a6646d35cc3887e66461ee8b597c360f7078bf2079e849aa2446fc0bd96544cde358896f70e95b91918ad73803d36c9998fdea87dff2f47ccc4549db99a8c38bea04c1ec279408a9e36f9d887e5dfa33ce5b06a5bfbf5e536b4d6183045f02e51bab03423690e5eb005e9de4de900b0cd46fbcc4b0f1366d1eccfff1b367800c9ebd755fc821cfda35bf715130a4c678b9ca39f0f14fd13aad08ae23784a79b97a8c8b6a040841f5e5663716295e433c6549335c16a1cd110d069e6cb13c8173412242db6964578cb4a64016e65a64c8b65afb2fe51a534d70d5d8207602c225dc84905c0008bde9549d3b0593fbe950d02f03f91386c446d823651e06f4ac6b74cb616c75342a72f9581917926443346dbdfe25cf21e3a47e118f29728da382299eb1209a6edeccaf28fe6c991d4f0bd14ee16d23e192d48253a9c260a79a68e2034c94b9c724768aecd8fd5896cb7d1b727e03e763a66c27323714796a1ae8e062b38234da619793c3b0c9c7da4c4ba20681fa5287164a110cc61907e33f0756c010dc8bbd205e399add4c8f4a70985e09228bb03854a6de8641f94499f99170ad47571629239bbea39307efa9fc4d70a58ba2ba26bd8ea8a65f5d50e3d21de967d001377222fd660641a0b0bcc543a5837b87f7d93e5ff398912518a7a4cb661f2a518a39c26820282267a8f6584ae95036a1118ee3de1061d80962940f4a22ba0fa61ae2c1636eef7520f10da1c90aca60729717949ba192a0982e9078d72dc18c8164b5f760e093346d175795f632a327bd008ddf0e7fbf272aee6e6d3a9e520d33fd70451d2f49a86344f06844c52f5f842c55bad99510ebc8297c9965628b58ca591fb924a5b85943c8063e6ebf705c0dd5c6a65ad0ad2e836b11735f11feb0a972761b6705c1a5bdd3ce6a45227e6b31e57419f4146166753de60a5f1a577b33560f02fe552fac2c7830bd8c02903b6e3291c289aa1b9e1f6d88252f8634ec075096565a5ce168ff31c22dd7e2da8f4f71f45321efe81122b7058d754fee9740f12c5a0471dabbc709f43cced934720c6db09a5cbbc93c26ab3f0d1da74815a929389765e36005905904292988624c6c732a78ea8934ae00d89fbadf9b05711f100ae8e9ebea04e078b5d3dd3962f61d2a79f8e9231045fa8243158b9afbef57f9b4c8f487d86fa4b401da4f81015755d0bf4abb3c5931968dad5e7aa4341920bcca79e2db50dfbad54f26805d56685adb5c5a1de66eece04f266b4b8e821c0d539f62471096750e9383ba8ac49431f8430dd2de4e5f5ffebab733cc7a99b746d7e6de87dd1f883db17f678861440b58464d8af7bfdf65c7c49844358c870ab78c2bad79496f8df6b146a832e6a27eee5c19e4624bba920887b6a7453a57c096cc8d014258393246ff753489cb9718d4765d0bcc7a4c5131421cc0405074d6f22057bc6ac3ed3be0225b9ee3c503", "53515153acac520000", 1, 199439291, "5bfe737c63659351deb721c1ead1c54d35cab9250e93bcf177f5cd9812f8b1b9"], - ["99237e3403a0552d2841044182f70d23feda6e7a7b7cdc03eae6ec0c10505baa94e8647c950000000000947f48d57ea00540f840ca706c824c81564056e6bfcfe1fbbf06a5a8e321fd29635ed5b9010000000853520052656a5163d8acb61feca1e17d1d8cf8d686a84680bf69f58e3e64d73362e7374f4fe3e3b3b67868bf0000000008ac636a53525151acfefa2eaf04d79425020000000001acae8b4504000000000165b519fa02000000000163a6137e00000000000665650000acacfc7c8e08030000000000000000fb8a2b00000000006da8698b77d5eab763926b230b62dc16fada94c6c277e8ce2c3e5e09b95492628aaaa902190663a755aaf5215cd7849b884c04bcf692ada0506bf84180caa48af74752e7f3dd7bbfca7322952ef968a1b00196885ada4b81c60e82d35ce41f3d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000079f6948457d8d2453fc60d2d3e19dcb53d5660ec6d5c3feb10f62e01f722bfc497c6bacd99f767f612a6da999dc5cf9028d8983d5de4ae834faea103168410267b926b8f8de82d175c6e296a5d00011184f0d3536e027108b63d895fb3c46998852418ae90b3df5852fd626e9919bb90e09e74c739e25c85b971edd99ee5eb2d021b1c2bdb2df2322fab1ac0b9ba683b8d39c285bfe2009e0b56423ff11900d815030e964323feace39a6aa78be8934fbb3ca999fdace1714e4f98c8eb7ee98efc7b0b16f6bc69b8da21a2840d0ac43e66d01a4472b7a967b58941c1ff8e739b6d15df151a2e83832a0b35545755f255a2a3628bed918bd70ffb6375e704e54390a0240202fdb2ed2e90836a6dafc08a31b77659e34c0e98ebdcf66039186b296546f239032fc9eb5d43872d22340b2a8a5c5763d6655561ff2854e4b13e71e3898ab52ecb0225cbf1378741d7a0539db47958d7d0dbd28dad35381fbfc31610b5e21ec98ea10222e8b702977589da0c42a7e928d23827e6013bf21f996aede2bcf4466d1f780a0210540119f8695a2232920d5e03d0236c535b1d0d72687a6c20a11debc33bcf5141d522b3e750b628e3cb644a458b8c8fa45b9ccd74c187d57dad4f4f3a29e67b518705d397aaa655baa05e23e1a75455d61bacf19bb5f6b95f8ba2960094700e6ddb0b84e6f67cd2b12d961d6323acf04d171263541bd87f5b9ab19687fa12aa5366ea12bd6d13efa398cad8449a9bd32f2402e30edff0b6be92775c1b1adfc819c987642038ebba7efda843a067954537b44c4fe0d82710530d80f72343aff222081f09410969962a173bb5ee1dd017676ecd818a66f943e64c4590699220ac52eebf9a48ced55d75890fbc17476967ad363d0a374d922a4c1fde1753a94ddfe41d73ce6cff2b02f1143ecd818c8ff463a37e27c0b665227213ac18216f29fab31d04333d56b82f8f0b56407b849c4350ba83406b08828cea50cc99879b81378935ea7eb8f0d709b35dcde8a94cdb929fc23e868b4cb4162197d3f2ebc98275eb91c139f11dd21ff56c7b11dfc2edeb429bf9bb3d43d740fce74aa8f673ca6637dab039e1ca47b1dfcb15d8105c1b3312a1905b0938359aa20e004dc268912ba938aae3320d9ae783ab4237c2420578d9fd852204a18e0becf430e8b14f277b76345b556d4db73da5e086b634deada4456ee6ca824bd805492362134f46cde743b245bac579b93b19e9db1f34d00b7fbc2dfd39cdd35a9953aed4bda03855bef11bd11ecf033b69921e2e463cdb9690a6a1e2a7fa7412409214d420ac6a3dfb14e0f58b856b6fcfc76160b89421b14a0edf31c5671f95fe22856ecb37ed56f9b3e7c6699e5da9e625ab6e40c7b7b36c02e2db7e430b0cdf1d8f94cf4b40e20660b5e6eeb9618af19dad72e6f7fb55dbad9bd20de7f096e41e092439152ce437fb206af238e44d10a5f188ae6a30aaab33316735b90c09f11f9c59313f7eedca6dd712f5b979553a368659feea74c656ad834f2b1cf53de6e622674d9c35c7c4c8e29aacf9f907220940e051f97cc446de6015b61dfa49ba0d2384a6082efe2912a78cb06efc44b4d03f4f140f21524e9e69ccc5d60f327ef2a59af48e32a88d993db4d7bf88e35ab136e0fa454397087494b229f02f61694d5abedf818aec478473b84d6456b48d64c9d3dc264c5fc299188fba2100b30fa92a7fc66bc63dbd0c836bca22cfab9e8f5c2117c2b916c73717fc64c7c6274010b11dd8961d9da03d9aee4710d72ddc56464b688a99109cfdc2ff0898db382375962d24e4bd03b792e7bfd24d549407ee0252c789805114e28d0f24b7728906f9ea09b31bf03c605cfe44a5019e6dbf05bed1ae14fa97d12e90d1bae19a6ad52f14bbd41bfa8ba8a02c2f2248cb21e3b754e54c6a7a52b28fa193091feafb2572ad073d5e0fd5df84a520eed19cbae4fd0205c7f8ec915aab74657fb78982bb60f0a2bde0e54ef5012ae3bbd02bbf2ad2b1d02fa7a45b2fdf767ce78cba7b1d95b857163a68f7ec270e00c736677abd55f194c842aba4cbb6a751ae99f641b74cda815249ba98c97955884774c03759bc70eda0c956a443afff7f2c97abe4f0e7d673c09bda41d5edb5afe1173e87176d423d309fd463c8b4cbce67057151fdec266a1c3f6ec5faff9eaf876b8b6a65b9a8b69ae7a1d29b65b0bd73e9c0e68898d2d9d907d35f7218db61e79efcab1885f69ce73fc7bccc91d19f31cb992fe3056253cfb47102ea38ef6dbccd61d2e7656de9cbbfc077d3db9ea11a2402000000000000000000000000b5106545c4130478b3dbc5ec9f848ba7e2210cab41eaf4d54cc9ada944546f463f2ff374eaa60f63c3de6efaa5d65e8492665406445ed42280761e6fd53d58f36dc51b8a575c2f6345a4b25a4f6bd63a28ee0099152d4cfad2488cb0f3c50e64000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a141f36cb65cddf8c5c2cb51995a369b1f9162ec8b0a5787918d5cfd26495198612631850ddd5f8eaa66b6a729c4a16f78ff13814c823ea6ca73a8f87eb065d262fc0ad7c17962400bd963586fa7802b5c37f3cfbac7512e2681cabb91fa9515a6b8db3ae4f862ee81234421f5cd849e25f597ab8982f2dfa8569f9cdfee3df03173ee6f60a0f21c6b2da7a7c679caf7b68654f34320c812e7c5d52b7f593e47d0211fc40485f96e58bff94c3238ba3a784a0f60966526a55ff8c4740fc5696a6d10b08012c2840047471aa2dec485cad7a4104320db4619f39ac3cff373dec0d4ddc2c44ec6f299e3fa0e2341f24d697d7b0a16e01e79071c26e14974bf343178b40021c352a82516328ddf581581431c8ebff6d35d05afd7b4d2c2cda0edde80d248e031a4d7df79874b146e2c5a2766a4e870a71ebb48b8ef65f238660ceb08b7234da020c7b9e173c29eee921d1c864e608111d6e937c0df67d47566918310b114946a50318022a8c8a5e818c879610bf7e086de0ab72c21bbb51d9e8d73eecf5f05ca735031b04dadc3aadf80ba04dcb3d6ffe5decf26b9e3f3c2fc41f8872b7e6f6edd5cc48abc47d07bda14f15accf263e365068dc5f4902e8734ef286e4eb1678486aa7e608205294387f7cd62f0b644eb9a58398903ec9ef5d1a039c84ac37c554c0eebee463c62331254e97a850c0af7ee4dcbe0ee1e0f84beb594d0434afc02ef7bd4ee0565ac76afa025601ab14aae1c00acc71ada53b5681fba32944b6aaf7f097eef6e61ef4ee2981117b59cc985ab97ed91f8a088f67741cfe4431a2854a9ee47c080f9d12845fcbb57fe15baa2aeb01fc750eb2ce07c18dfe76e32bb2f1ce622d4905e90aa114434325ce0cbff8e8ca57de8e65374576c114fc01d934d84180441e6c37c2d3f63fd5055409e67c9fe3d69e9e8f3184c715a925a3648039c4ef2c46271e9a512f87b77b4ad8f32df6cca9b976c390ca86542c34d67d84e2bde8662f71a98fb7d01c3a5941d94d02105badb7d708181910c98e9fd9d42aa51a081f81f45627a6915639b94813426a87a779ce73e8b5099e3c4fa6df558197d4ce34cd926c31ee7f72c3a8c55613c32fe387090c62fb3d1a09d1dda069f00c20ec08ef8a352ccf97fa5ee5a46b24484b3b200ff80378b81fd82824ce1f27dd6a5bbd42785c52e47ae4b267c44825ab1eaf9d3c954d9784630f0807cf38eefad9ebd73dee2b8774447fa94454bd2ead26d8389271b38cf5f1a145e6b0472ea8556510c5a91aa9b40611a7f9ae85a47dbf01836ba6da4f56639c0942617d8f315e6163017b1bf4e748a0a9882bd053383543c568044e6c8f8caf20e4fda13fa25828da66b7129b9124e093e6831d4a4f8b8aae611e483662dc4a1d8f36fbf3034118d9e17eab00ddfb560697269583e4667863bc7903fed6278f1513e6d37004baa90cf2e9613020dd1554b268280d1dbf4aeb3788f5867e098b06ce74e46e1466e74309d277bd5d3882d983d8ead5cdccf279710ca088579183b9ed176bc87e13a1cdbd034efd39356690cc8da73d8f254df60a02dbfc0513b99ed271f54ffbda0984a4a90156fd0b169a7d4d0696c0449c8e73e9c4719f52ca71f7305748b4804fa1e4286572dea5a9183b62eb37ac223e0096002b365675b0dfad43b7073899c80fd0921ae5865cf66566aa20f043553d58604056fd2182b1c983cbafab0549afccec86093e535f835bde3f1c2d7ca5bb4d00127c7b26d6557ff11a8182c3063257076ccd2baea2cdb1da9dc16719bcf180ecde5e9920634947c82dcb3fa4c60e7f0eb8d6c262a5e9d281ffbfb7f5be74ae524ad4add84a2fc3f4586e50937d5d4d07130784c77feb3d583faddc601ff5d013f6afead658c190c9c67631594e5d880038bbafaac85058aa2154a50ee899407249e7a9c52375901a67e8b613b388e10485a26e5586061409d66b7ff620151cdaf80262a4c1ae5ca923dcf6a782a17a3c26d1889fd0b02fa62f3f11723d863f8598a1c50adaed666a05788c8c79a0d8366cbcd08ccdee2adee46caf499e2dbdc6767c8bea4ac214b766978610935a1403dd32be85f7ce566066603e12c4c1725ffa7f09284bcf8b282c91cfafeaae86bc58cd914d3e1f170602b860ed9eeea7c2cc13cbdd4d2d0b4ec36e925cb947fa8183204178a5eddc62b348695b92f29c09d57e9cea0bf8ab956599a84103b1a3d4ed668b60179da599309d3651ccaa579b7091d04b64a1792931f515c4d9d24a586e98838af9f08b54446808da82bd3ea300000000000000000997c30400000000b475fa70d9de06cdef8be6997b04b8e9f6fd5873441feddddb19a1953c8c195e6026befb982c190e0cf16ddff01d8fa36ab7326a8958a332ce5cff59a2a2af0ad516ef14d32351db950b4afe4ccefd288a772b6e7cc4aba38eabf42102da30e6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006ab375298139c3a0b0aafdc244b6fa3c991b94ef8a9f2dac03dfb6a07f458d2683f3c7550024edfba3d8c81200e613fbd22ab7bb87838652753a53f5cc70a354ae2c37e2ba0a1b7de94067b73595760c58653aac1abc0f02629d7a4836f2bddae36f01ce17ac1c934f2a9cc92ff7a4f87ff601027410c646c70eb4d5dc082139021ba3b0c3e56c8a14cd8a1f48abe27e3015382444d1be151831ae96ed3ae9f859020e5f80ee2a24f2fe5635c63e1fbfbb753e402e538ff3c191e8984416ea687b7f0b21e7e7e2ef05ba0f20836fc860ec9697e83e99ca4b328cbf3690927506a80fe7185583f8a8c7ecaaec032c4c913c35b9492c7fb3c6d518544eb7c94a930c6f4b0312c40151706d3ec311a218f5e96d1fb921bdc30fa35b3e1d868a3539fe8a719d0305cf07e9c0ee5e2458392cdafe20c496ee71fae9400095caac1d48f4fa5a50e0022dd83cac4a92675de5472a783268edc7ff3a3106ee188510d707bf063691f236032027475e581087c69ba1bfc162516358c23b38981055acc1bea09223ea78203d03137751aaabbfea1ae03f853c72d5239f6ae33af1a8497838a4eeb556ec27a5e9a59fe8398e8660d7f8da481051f4494a49005dbb8313b67a7d5125fa87c8dc70551caeeb213d152042a023ab3e60ada42c08cbd12611611abe9e1dd1796003e53bfd1d86125996bb3bf7619a562c70beab017d8db088e1837fc7b6f7bf3d7108ec2330c3c2e71f0313b751ba7207297fdbd6ccd76d7f826d3dba9a875e8a6ff7d4e3256150c682498a205f811af03f4a47b751c7ff6f6c1024c925316cc3608bc12b2626e44d5f1b385572fb4fa06b7a73b2deb4f35c165218ead18c9b321ca5323f77fb7469320c3b58204b16afc426f79d530ac885865d0be30def533b256863ddde538e24bc78e8ef089aad20bfb0ffd219ec5ba5d92944e530cbc25034763196173379a3601bb9c1bc636b58cd0c4c75a2056456b61e9d88888bffbe5c9aedf1682e2c60718bf0ed1c80dad6dccb11e09bae25b01886717336e845d8ffafa9bf39afdf361ae851c422a60fef9d52c0ebaf34e459a5bb7ba0bace46a03435e9fd47e19c303287aa05f29727383a00440ec513fd05a4696f52d8c252cf3b1ea5dd1b6ac0fe5232367eb5d33df050ebb6d0b0ca048e5175c7fe2a6df50160317311cfc1afaa9370c27a63150ddc87f180683f85c390e2d33a31efe30d4494feae3b3095af5b85fa90450d9f37c6ef9d11c19e23e6dc3c020529c6614102f51c97eeb00c5808d773c7711e8144839ea9d22c6637c11c6dfa85a800d6d0e147cacea4b794ac9970ee135d6fe517dbb5170c7fd020bb248fea1e2f01caaccdb5968e896e5615ad53c0c3a9e9191d55dcb598d7e5d9232c05e5b46494dd834888cc0701aded8861c664950dafe1a29a8ddd78399c1b6cbacd4bb90a3c8c556145fb7b86ebd91bd0d952c58874f9fba65d08c9b362cbad561aec490a5fb9057c7cce9d084b2854fb782f41176254277da637c6db0761a3b7659634256da5d3d0668789e204866c4ac6aaf3b6fee9e11e23b4e07ced4003b05347fae0f0098f3a1f87e7963b1ffb337cb301c3476b992fcaf0cdf6426b7bd6fd937043929166943e5eba4886c9119a4c64c48cb741a759ef9a1895a48ddb1d42b3740aaf34911bb4dee4dbff70138665acb97e7872ff08635faca3eebe1bac533c17e398597e9620ccfb0a0b23e51e7a48f1109600f59a5f3361d746e8635c087a488d2efea3b7a79a529e7f638cf8b4b529c86b347d4546d0b4bcdc7cbfb845b27f12e9ccba943a030ef644f79d8eb90c8a5a840cd8a96728c128b3c97860f1a58020320e56f9ca37cffe9b3fcc6f78dea07a0711c5bfd5fc87f5bae9695d3b58ae1b583a629bb10d09da0fedda26f6c3eb272dfe0fabd15c0cdcf97f4fa409c883113f4caa6564670eea9858571f727276902f97efd57285718b4450ed3104eb6d5385315a75437eb01c60c1fb1fe111860be8165e168fb79ec7e0c6a0330e3b9369ea08748ae3b4475149f06243bf956baad78b767e66d01d70dd6b3720bb73d597e31e568cc9b3c73849c41e5b603a99219c90f64d0ce734121fc0e8cab3d140373f703a621100e699b0519d49814e67b373368ed1bbeaae331d84b007361bbd46674607e3808595c56aceaac52bd7eb3f9296e6dd5efd5b2599397dac58c3320563301c093237ce7f9588a16a227a92821a4727c4e28112538d6491deea922c11d8be430475d7aa6bee99b881da0d2716242df9d7bbad7bb7332bae21701f216608a92d1748a2b06dca8d0e02ccd1289ce17844a31957d174365ef67b6d5a0eb86af951cd4bc68fcf884eeb825e9d24545c8925a97d0dabc483fdc138d765b92a03b883494b32c3ec887cade9ba1cf899c8ef41e8db23000d", "", 0, -525426326, "05ad36f31aaa3a7c4074abc9b6a534cb7247a7d91c8552fdce625e2f81d4792f"], - ["4a7d367602e28c07a0f861c9446c93020803b159076c38c76bb71bec387eb4cab3e12576810000000003ac6a5283fac29a83a7acfd0871b7f2abe73d42f3587798e51f9150329495f4983150325e243f7003000000035263ab9c959b0a017718b5000000000002526a7e7fa713010d6f7f030000000000000000000000001a5407707795b7b9d240d5ecf456eb071486ab837e1ddb56c1d4fc5ea7fa68db0c3d11f163565244923aecb18a3b984f08a2543244931a2d5cdbea041080a83863e33d6a04f13aa7d197efe48db9912796000a1cafdb389671073530d56b4286000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008cd0553361491ff254da41b9d9541f74dd4cef3da03aa0f70fb3263864fde11640647f3c5b65bcb5f9926835245b0688e02b9a709e5ac86bcc97ee5b885ccc01f9375f8f66637174df4ae2ccd28761676f6f5c2eb034105ecf47e3783070418a68afbedfd02b7a17bed2e0a867b5f1b75a4077d476d5adadc042d10e2a623ce2031d8bddbb547fe5fb8a8065490c74a6db2892417261aa9c31fbf108aa185c5af6031e7c90929308c1a708fdc5c9df7e0c927207f41ae63bebeaffeb7b548b59155e0a10382fc0ff43dc5f9e3ead0f9c3297b4efae395e9c48864d287f75d68bfe1b7b27e3cb7594c9f0b7f43fd1dc10237c165ac46c54bba48492f2af53811b860a8c0318647ffc61b467c2fd761333e497e8a630f4db71deb5ef7ec102d1e69d5c8844021828e4e3cfca64197d7d343b1c5f4c3f3cc69ff5facf6fd7f656c4fc4db339a70323684eed5e5775102cc6348d382ea8f5e6607558b5636ff99de555613fe1f5d60322a1d1d094a7ff07db0ddd3686ccb254e6b314ba6118af3e1dd2daeeaa9804b6030abb84950719e0a9eda9557adb716fa9cd0d48d4aa6b01d179b4c9c01815c0340843910ce43ad089127ddac605e5d9e3f7e4bb8ecf0c230de66d70145f34cda9049c256e9c289cdd199e29aa835746febcb77837e46df1a4f52f48f007e839171ef0bd17447101f1cc99925d67e296331ebfa64608e67171fb8a75d50d5104477279e3c2520e494e5ebb9628ae3e5b9f205cf7f45b8a5064bd04dc96762d8d58cfade997eba15262be13bcc3dca27d4df74aba7cea3ea1fd7e3f8b7fc6127f306d9bab5652cc5fd958169ce503b4a76cb5b98341d9b27783063a682080ad010d5f3eaf6d78a236dfd44cda0149e4910c09586658ad61b364589b6337fc0cc7234aea8c2cabd47f7e00ceed1b9abe8f771b82d8987082cae967f537172466d7478c4dca8bc5068a263fb3e161d172abee86c95b96df17d9171e41165a1f5fd6b8cc7fd709cbd132bf22a262191785c55d519b119a2fe70251cb44aae95ac7cf77103d41299d2a9e314d010aa0e0411a65c047d157e9e188688a09922cd68a4ae2984938a404f766613df8f36af4a8efa419d827fba85bcea5d591cf544f40af61653d9430cbe5afd147b15c05bc2921fc354a4da435163845d5e4cf21338427998af7a122505b1f3d195abb059aac9e263711aa3933d75ece78f1642247e0b5aacb4a80fb9e7cf71be4e134ee1b20923093f05dd2410d417494d6265b4d9d747f0abfc236c324722ce47c8c147a33399aadab8b6fa16185a4f4374f1ef7be3b86f4b2b861b9eddb2b1b1c3d08c1cd53dab73fbb56e7137be929be5e91853e6cbb25c5e1903a394133388bfe40e46e3f80796d11cbad37cfd82b0ebc1119bc50ea50c51597cdd4c0e778e755723dc4d2c1f18acd24510d0fefcb821fee783a50904e84653ed0bbc21af294de79d07aa82f5a6574ce658d332b6ae3f7bd97c370cd443210c4be9b883cab50c8ca410994e4088e68056d4e6cb03e1ad9ca32eb8f6fc1f98e5d54680f02f921cc06145717a226ece59b1de73c5ba7a3ef3ac994d537392d61284c1b021350b93aa0b65bff94ed3609ddb1ddc3d29920a31d19c9836a94db293405f1850dbecef0509df95fcb6aa78dab59dab539a62feb81febda3cbe75f4842e3ad075c01c1fb339537c8062d4826d5e56ebacd5ff69e4ed7e4ae75b421b4e46a2061f62da2002fc9a18351c487e2f18dbb42b36aaf6e540572d5a106e52ea71cd225e0734f31a3c4cd45e8093b5a1cdf1da21eeecf38376ab68383267c83a6c02d4813a0792e30eebebe06004aa067a4f484a6e828a7f8311b11c39afad19adba5fdb0af30389aa9a97d3c0af24834656da6057ae931e32fa77ccab57153659b66df98d6b935c7475d4dfeb00b39147b2b09cff740dc00a37af70952d49601a7ee6f39d3c83f7426d65e05ea99da219e38ab1bf3fa8275d8a81c4d912ed0c32c0f608cd6dd7abc471f416517a363b7a144cc8478adc5098baa1f6afd5bdec9cf1afc78b5f1d8c75432b37ed0afd337a2d1ca894b787f36c00dea53a1250109e6e0a1089abbb4d483ea05b95ca8299eb316d4954337faf9e5623a8b4058f66b22b2a02e873c4c14bac76973097edbbe74573fda67c75a5590cf3f6229ff8a33624018b393e1156a5c7726ea9f337d3ce3c657c7b1e797f0598e5a39fa3b772d7c10b58a363d7fdf2ab758fb1e20f653f62ea689f7074017bd5e2e0c81290ccbd9ce8691240d8094dbbdc7265948428dcdf872a588c0a1e319a2f55d9dca25321bc37431664768cd4f64a71b4323431df64cbbc5f535556d378cb2eaa4730192aa9c7380d46b7fe1015b9a498275c99eb122ad883e5f8c5b3e5375ced48dbe1e4b25b6c891a2cd2cfc4ea3966205", "65515152", 1, -1529506216, "e1504e9d9193d0c1d9bba238498bcdeae96ef268e978743909f8bd35ec2893fb"], - ["0eb08e38013522423b6b3f55571b1107d5c111363cb90baad9c72ebdab18419bfc81222050030000000563ab5252aba2984e19012802c6050000000000094ddf6b010000000000000000bf79600300000000d24c87dbb4c4d3a97d51f1b7d414dfd2ffca31e99c3270f4a87907a500a63e713e1fd51060b27a6194d35184e41317f76da7be40fe3f589156464dd915c2d35504d51de0ea8a03858c26787c8da3c4ab374240c5fe55ff3bddb7f28c084cd727000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ea2cc770e8b74849e0bfb578789e38a5be9f1ed9dac8e1dce610de463919d5f6b1df6a01aec97d76d97fc9ecbe67295dd4d2ee47e27344b7e7485ac87b53a935cffae740618b1abadf35f1d05789485827d163d07acd790c421b3ac12b279313249880a162324c98a997f89b2cc72d9335db64e339e616b97f5918ab5b15eda02016b6e101dc14bf1dbe7c3093feb34a7842cb8fbf7b61bb8f65cf7d2bc3b4620022b004be9984fc77194d768fd8a15b52a3342fa4f02b7d1b5d8a0ceffc40f69ac0b0b3b0a8b3956cac814b4e6d5da6d9c1fcfcca13b2e23b4e69607a6957b0c08062ab6de3da010423a8f8b6eb3e2ed7ade924825bca106d00fd412aad961d9512b0312c9e52192c4c5ba5b19e23d8f7d1bad56452ddeff7ec61726e70e2909697e0c03051a26485e29a3772a794b7f8190472f82d8bb0d2f0c39bdcc40a1ce10bdca5c0226b47e1b0529f629aec725dbebd5da8a08b09db7465faaf8f8cbc321b480fc09030a0c80318af7f4d9bbfe73764af7d9ea6e0bde72dbd7cb292ebed119788d098c03278e8dc8d938cbd833a4d893fa115d757aad8ebd08a94c1d74a493738121943fe3de06b3a4894cac7db75609efeba38eb693b9393c22db2807a99b09593b00d81d755b91e482c8d94c90927bd5aae9856786a43b60a36271070d481baacf19b08296577d6dd5b13146dab13485262fec9c1f1f011066c6b2cfd15ab6ff2c6c1b21c3f01b6000a56f7295d86e977b5660a01873497e75a57edebe63c10911db5896fe5851c52bd06a9327b9bfab3e25d9c769a800bd1dfbc75e365813a22dbdeb97623de1ef43aa6262519c85c492db8a6b59a58eba50d8d3b0c2f301f13f34f922a521fd778fc476e7fad21b1f6257e5f5ec951016624608183c258e9a1619ea47e5a04412d7d0d49670298f85e536ee6ba6deff3eea2c36d5ca9ae7958bf9fef253209faa431b96a84b29bdb96d2bd8379e2a3ac88acb4c56edbbed1ff4d40abac75ca8be7ec526405140f8bfda2139dd0b072d78025bf1ab1db21d174d6763b8217381ac385f50bbbc651c3bf734b95e3a81b1051898aa3c84a862fc8be660cba796b1d8b22cd3ee8469b9fb701ff47953297ff59f362dd21c159ba90d34df3240e8aba5148ed3d311499effdd7ab07a8a5501c59edb0f62b6e588c4949956fc7e62a0851c78c6ba758281d84e9d3089c8bde0b9edfc8e67a97a4f2a32ea37647c47867cff72284d5e1184b829434163e3d64906a0028cd8aaa0333be8c0b678445b96f85617538fd612a05d9b485df38a729036c4abca5515a405022f7e8b5bed7c149852b0f5e121d4eeb9ee9bce9731b119db1e2e18c049b552153b352441acb02d1574a960f406259fe3f448a8f7d7ad8d5f8df997d1645a2df48c6f41a4375b80085e0e0c2344ef8bb1461a1e2d9bbab2c6d5479efb17a6fc97fe0887d1e84a835bd8f46889fca80334acdb50e3fa314902fd1aa90d9ca2b605eddd0d589ab04cbeead839fe9f59ca3a5c92575a0c9b7b239371655189ff7bd09bb646641f9208403e64bd0561d669daada0c7c16e119b2637ac7c9440e0aacf9e3bd0597328839c7a36ace5f7e5ff1d9c72af99e7b31e917f57f411c59586d5d399d029c21f5df935e5cd628a692f22c0b034d6e8c1777ed568c4aec4ae3597d26b727f64de1879b1b484ba971bcad071abb1ad3fc1fc1abd44b43007def991508b9174f41a82cd44073a88cb1f48d18dc04b83a1e8d66b337fc42e161a63521b7fdcd1a23faf856d959e960395955c1fb3fda9a85a00e5e2c101ef59f607d90046a5326325380c3fbf3ac6c90d799c9471263b2ccead62d3ea6422b995aef9e12fe974ff4c2a5d2e85bd19cd7a8ad621cd69b5c13d87f0d568f7acde6db59950839fafae81d80209d083143e5ed0e884da3dc782bcddf8b46bacf83c7c5ad33b50962e30baaeedd537a2956764b06c95671c04f77de9ea61d87e1ea40726605dff23a3bfe1c5e0aa9b9ad2c7d7540a8a74c52f861c005443bc094fb7727dd2d970388b3d18f1c454f158c8bf454309d5fe6a6d67a39bcdc0f1f3e339ac0d6da7abe072d6d134c3b94a470317340d27a868355130d1468baf2423b6d650073e66ab37bef561bb0e537a71c453bf94d611aec1ac127ca86ef12c4f9794de3546665d7e9da2f3512eb160001f1ae7ece8a63745706d0e84b7c625151bdaa14342feb2931b894ab889d1c81d5b80bd72244bee3e53db51fd75ffd07a1dc28b7957c8506b2c49ef489609c93408e194fcb17ec11a5362f836df25e1906ceb6e56d0e4ed64414bb7c7854a1aef05632aec7772daf1ee1fa071a347063203e5e9b7c398cc342845e58485c82f9309684d24b4b4aca1dbb8283255cbfeee3fb9d99a030e00f531ece0bd3b9d1a10e0bf9024361782da350c", "53", 0, 252808238, "a4249f889a2f796087c13a5b1b7168c1a9a40a618c240af235cf4749bbd0b7a7"], - ["a33785a303a08555452af9a20a2d3f9934d8080c6a20c8f4c583955d1c96d00e499af5b52b0100000000fce5ace4dbb4a833e93f8389bd94f4b3f645200aaef654673f5fa23e492e37c17ad58fbd0200000006ac6551630063ffffffff84ef3531de830304cdf82a40dc6adaf1a7be17a1121a6b993fafa2b4e965836e030000000951515300abacab6551d49c21da0242fb12050000000009005300ac5165ac5300e2c901010000000008515152006553006300000000", "00", 0, -39698101, "e9c3a082ae3618fc70c65bbf8214e5928e82c18899f97f4d907f13a9637a9456"], - ["137977c20198fcbce246434002f0d0d178ffde4157ca6de20d45cd7b9aa58608c80af93c4103000000006bb2a18602747f7f050000000003006a6a54cc3503000000000363526307bd2f9d", "650063", 0, -1110739385, "ecf9f8c43b878dd5544b952e62ee64307f611a6395d6b121035ab8f72add2db7"], - ["96cc616c03b30686fda9e6763c5b214f19c204088026d9c7f4526d6fa367ee5033b620110b020000000151ffffffff5dcb3c783e17d7ef49065a1fee097f56a1805dbe0385755d015acdb5122c6d2201000000025151ffffffff4412e8de019611899681bb945410b81a74cb5ba92df34082c07f393085e71ee90000000003516a52ffffffff03de0a1f0500000000045252ac0015675705000000000851516352536aac00b4f3a3030000000003655151000000000285129902000000000000000000000000dd06ab591a7a2e814ff878b2906ab0d6f84bc742c7fe66353e113d09f14a5eefc339348f5a1ad9f5d2f3e426583abbb1b9b5076211a8913e0fb9f221f3f5aa08166991d2335954fcb407c1b8763cec921b9bea40d3d33fd5831f703b46a5612f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d3358a1291f2d9498999fac39f0afa94d863411e2b7a33cee9aa9f20920acbf2b6be9a96cb960f5605105e0c0f681eae57098e289e784035a3902d72def0196bf01b12816760f13fa60a73280526a0f7fb95ad96bde211268409b45a3ac82708811202736709566fb5ec68df1d88136b31f9e3419982c837874079f1c28ad0f103213ff162a0c5332d96b02a72d0c46d1bca4e6af9578d1e497c38883a341c07ea0305d75ec0ba096193e5fbb481020e217edad57c988863a6383d84012b5d7cfd090a166e897b37c97b32d7cebc65aff425b0ead37895331c2309adf975388e0eda212e4ae9a82386afcfc3f4af3ab8717d63a76f936c1c94febe9a29bb09ba8d018c021e76dc5fa0c282f109fa18916d6d15bc03eea450851ad16a0cddd7e7a4a91e640319b0f374ea398133a247dd61a4f74e8592ffa6afe9c65f8edbb62984d19c35b2031900326f830716309eba2c0b488586168efa1c029bdf35441fb4913198bdd30e03114a3b98005b20e262b788659685e8f1bfaff5a6ed17a5cd209107a448d1158d02208964665a3c016a5d0b2f1bd91f74debcb32132a7fcc554268e209e56a41de34db790bf397c97966288c5c5405286131becc86c32483e86431e882a9f4a7cb3cf1b1f700e86067bc5a57eff47cfddb4e84d599247f64f4379446deca12ec2950079632563aaec55d0e0acdf9158e03fa8014a13f9b180ac7f59b8405c986be1688acbcc73981804811ed132a6ae84815bb3c04489477132b4107b95869e45e5297ef4ba4d2f72198489eb6428b240e2f603af5b8bce0102b3a67760676bfa5af9a1aa146f63c86cab8140d2eaaa924d068df42c83582c39803015777127e8151b77e5380a3a4d8f6a8d84092b5f2ec71dcfa0b0e39880dc825d2e3dfcd48fc8a486be4c8ee665840d13c3f99b8a4321274cf9b00bef40a3cca44f7697296c4277448c111ee5730c845f6b346f4cca20ad6047c1707803ff64630e5387b9f7bf1e31c6cfa051aa1f6682a5d7e86722abaf5a24d1dac1a6337a3109b78363e9dc3a7eb47bebee030bdd500f608841df9ba5199053846e5c2391e5e886dabb708d48134c6240862ed6a7b14698738783d0d2d22f46a67ff65a2ca84d3ac100db4dcb22db22790443ed5332feb9270c1ed0f355d96d35dadfa5d91b9fb43b73eceb619777ab32b01d6ca24c647fcdcf704bb3530c3279bec5f4343a764877c909eb193f05d3d63e4a94bfcee56f4f587670098a5a18b903af582dc91c8175305866ca6c2c2e6c6c4a47f51eddf52dd56e58aed36c4899b90433cceaf49dd29741504108dc1c8a6aa159a9854f8687f4c51e74f2eab3643d3d9d59dbb921c3dad1fac263b5bcbcea8fdb5c04b15750d8bb282e2ffb945965daead18d7b11ef852c9bbe969dd582c888ce78fe15c3f8a323747f3d703a8fb9b0ef6e3e2acbcf22ccf2603d42da9d905a3e9300887befee6dd3d9f84aaef435eee9b44122aa207bc8718edc2168c448c8582ccd8507d01ea21ae7a312a4ded5b3e8bee869c17b6196b4b0a68a1f8941b147079c9d50a8713308f63c56585096574d8891c8163c8b5110f11531640e0d668b82154fad9c579abb54c820fc3aa9f70063c506b01bc315d59c0a907f20f0008289bd85cbd4106c6c7b2a50355819fac18da5f43a0092612676eb0a8b295dafd15840f7e784845be2a3e6fcc07ae995066dfefc79402c2234b1045f118b42ed030c3a70504ee85828d9f2afa236590f498e53c98af7a05405f7a47d8fb46f642defe14d1ba6bfedbec89664969c08d9777ae7ea84bafb929b3d0803e0c5a2b2561e0fcb93dcfd761006fced31d9921482fb32b655fc78e086801ab3b3157301f4a9fb4f793864fde538bfbd21673ad7bed859ab4c7c63d2a051d4cdd0b138fa95858c07d990c6e0d6fb01cc530d0c8ba46f210f15ba55d3a7130b7a934f4fc6a313638a39d0262330ee65f0cb9f3405988302c381620f7a799143063e59df0f3047e2dd69e4b96089d309a4f5c34acb14e35b6a95f0d69971b705f5f991ee78fe781176e58c6a0879bdd50253a4febfcb9f5bf476f3cfd1c1fab1fa009f08b0cd45edb16ff13df4fdeb4b1f1a42c058893991b3f49cb86222c212c1bb56cdc958d8a79d0fd42b71174653f1fbcf849bf29d2ace9870f491fe4db63f393fe24800f9479a82da59ea10ab35c4d0cf7d4f9d4df8d716a3b3648b171199a9e6b8f7586d6aacdeadf03074336d079467b7121055e59e1cca477774a76cee77ca70e59206c171eb66a93e87ae540000000000000000e9754a04000000004bcec5c09c52b0d1d11df60e7a320ee6268d46356942da86126f255758978797819623b39c0445997d254436c83dfb02a64d625faaef1158b71c9fe125c9255910890a904b1efe435533fa03134ff032ef08cb6eb9e5b0551de20734a6b014ef0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000052529859f907674aae4fa7e7c4f45132419cd3dbea1cbf3723429a35083bf8743e714f94249e39eac837a56c3ab91727c74699c16f7e2c6cc585eb0670d7d0f2c18d76152cfe13ac761c3a82a679ddbaae56210b66e7ee2e5743e34934a4e30be541fe46608e976f8e1e6772b2e04cda0d33e9330e3611b7ab3ddd1d10e2726e03217265bfe5433a4c81868b94ff1550027b9e5eecda1019ed13c2c9dad9d0770e031484ea9bad28f8819a31e1c6ec8ff8991ddfc93548d6325437d6b8d28d14ee040b1a7fc8c2e00d456ae41ec93bc80dc15936f126f6da3a7dfa22a7e6dcf05bf7d1163e2d78cdf8dc272f4b3897a743405dffa5b935933f68ef1439f89a2d8aefdf020e7ed8c086a6bd237d7e31d91d29d630f0ed3bc686f21c6bc947e855d4b98bdc022ab98c4556bd227dd0ce17489721bd958437591c981ee591e0a4a20691dc9484030d489ec1528f9a3b0a13fc4fedadead49d04a2a5bb90f9de5c3ebfa631dfd30a032db9f5010edb083c3c4973ca216d0d64d183d7874656859ce6bbdb0b4defd16c031a9004a0e0c7be61b98999e05d1e25cfb106b726151995fc463ac73911136337e8ef32eda6e768785486b1d942424d57e46f02fe9365ad35f2e76284f93b9a4e1cfd3bdafc856059d5b0f0bcde4a0d7ffc3264f02e12709614d445b409910708fb6568920e5eb3cbaf469881a3d381e5cad6d85a3763ec3eec7877e604f96622f28c43908bac0c2dd37467dfb040350f469575d455d8ba0e34b84dad44f99c20627c7c911f8bff289297d14ef40b0981df4a7b8c78ca15840be9e4dbdec8da28a73b3d05e58d61dc348d24065cbee248410ca8432832e974f4a6d096bc11bee1502ee50ade65b832032a64a49e294171d1ac667ffeaa1cc0cde497f4d4c8e940a379d4fd2473c6c29519b7979adc6c82f404cfa94b7b1be948f77cc24f020e86ecbe271e809b8d19de1ab20ab6a7b2e15d2730340eea7e0f8d5c04043de531ca6a1fa456f4227703ab38966b190db175b4afb2424a49ceae209098eb2e58cc040ad906e6ab8b1f4c5611f32101f3f9de8518cf2aa3e1a3de7e7503072361f76ea1f8a0a6bdf91ca0b0fc6d5e04b4cba06f45f40d745a02a69e282b5f469c508445902f569494d484c4ecf8620a5ce4d59582886a9bb50ec96c43d6d384a07579929a4d12d6a960ab73fb39877a895dc9e489dab651ec3e6ce6b85ca1db5c90790419205519510d4918aaf61afd99b9fa58309f9050e4a47c15fd86ddc0652546d11d8248db8c82fe4797345c1f287bcf25af9ce4ee4b7d5b2ecda03454a0e2b77d324b9e1475e1740cece6efc82f7ecd10e1e9ad0624dabca18eda76d00d62baa8e7871ab627903ab4aeabb7a27aab20f993d77588bf6b48d9a6f7cd797ac591566be547ac4b7cc9cdf1425800e68e61ad3bca13fa3e754dcca62ac52703f7775a0db7744d0d28b92e205fc4ad8f83f763a96ff01342ece226546d23826924adf13eb19ffbf2e3f5335c81568371e033085aebe095bbb9963ccd5849c767bf768272e65243657c43d9c990089c0b86644c2e18165f02c7486d3fa7358d17f769dafb869fd56a993045d61c5c56e85463ceec8e0a989e3f8a292b26513a623c68c89f7534cc3804d24aab919026de8a0c081268f859714e44a5f320a0b8b4571913913293edaf7c6ed6ea2eeabb61902078b0503f94f281ecce15d65940b83eb468c7cc25f0bbc8783b9cf4b1a1fde5a48fb5b50d6773478d8c77938cda56af63fb94059dab963c4ac5088fc946766a32f391021b71b42e8e8d9476778f24df1a6c12d69efd31f89a8787bd9f8e63e597ade815a78b07885f1cbbacbcefaab83f9f7db1978236c79c3404959bffdc12ac0947f69950c7bb2b01654627412b163323fcec82be9a51a41569b175117144b63b2a994ae3a7b66c93e8747b0e79abba1d0d9b9c304c03afb103901e1e585eae35266ba9fb36a8de2d163f7db65a09ca86856a5bfbeebd940e7b130157026fa863998723e0435e3b8b98255031361a89eab63ef4a6a26560d4d9e569c71e9328322172defc3a185d32d472bfd20de4577174e5b04d7d51e85eb687b12e66f66a3a91a355e57f5bb8082caf4a2ace9a8355e4a0f209deee07b5c786d49899a0995550cf6d164bf66bb918857b5e0114fadead8d357ae2449ad00900dca9a576b79370d2cd6320482828509430597cebd5fc6819cdfbbc278f5722b99fdae2e3772a34ac95283c793c7cdb1c11caaf880d326bfe62dcb3e5896acf90ce018c83ae9b6bca65953829204fabab7f7a3ff650a204f8098047ad97fe86d2717dc4374ec7f0c9c17b208e568a91bc44c6e92c951ba347ae4fa0671aca50cefec65ac707dc2a7d7a76c2c9d393069f9d35ac1029744499d8489ba439ecef0573738b78464601", "53", 1, 2091089239, "c279c9ffb445d262fe55b609f193c0b8db15e3c3545d9791324b769fb6e06496"], - ["bd504a0c016642b2c7121f82f0be00d1a6c0ca54d964d5f5ae8ce6f34949ff5d848c580cf00300000001acffffffff01f28e18020000000002006392c7267700", "656a65636aac", 0, 61419673, "cd93d1c352702b51d43ffeaed094d5cbb11a6651e353edac3681153c0770180a"], - ["1519b826014bdd71ca2f2c105d6cca346cd86c2fdd1a08b6d3fde9521fadd72136913cde8e020000000251ac7a2b7198025d988802000000000563005265000538bb0300000000016a000000000214f596020000000000000000000000006053ae1789765f7035b6bcd52b27743a14a4ef303c53b435ed028865ddda55d571d8f5d06015a2dabdcd3daf687669913e9cb10e4568dcb9b7ba8d9e7f251df9b453e05e2020aba16c5e030f1c0628db509ed65f6c08c8ce0ba425ff4389e5c500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000968db0e6ca1759af78105c5fe4f8efef150102c7929661e0acb6ccb4074caa77ed76b4a8193f0bb0634955b477c0af40fec5cfb6faa403ec74a2e3951b4650015357f73d195b660b39c8e1f34b1a27f8160c9e0bc0b74b37faf0e879f59674dbb36365b7607f91edec7010edfdb7c28968fda77867399e5f34e7732d7ead5412030b80ea3c9d346d7313d3b661339c3541473f7f4c05b24617a775ac45d492eb97020e7498a3f2cda1750531ab5310a692e11461468139b22793d51eb8740cad4b840b099e7ee10e0ea9ebcd3ce5911dc6270e15374aa3a3c1b4534b0a928e9e732f5b194bdc2b9f59b0fb9cdf40ba452628bc349c8083c9520bd796230a70a93d3b8a02233f4cf71d029f57e3158779140a76a114ad94330b623b64643892b54c07dd5e03253e255958ba1e9c7d529cb67cff46b8afa4ea931a62adda5ee39b2dc7aa9a880208bddcd3af9f76d7e69814fab3cf78da90ebbdd2464abcb3cc7632b57ac2d87e02007f5aecf7cd166c07347f6ef77217ae0fed66420cf0a93fb4f71996b97cf7ad03039608b6ea729b5f94013a3aa04a16c425bf2fe3e5082d0038ec210ae252055c98abd277895d8e50bf46edff89352c151a87f69523c1d86779295c376108cd8504f9882e746dad315bcd346f4673da3a614f33e1544c4a63d495ff4b1daf96e6b24d598422c0df6211628e6a54b909d8ea523a7b8b269ca6c78f24f629314358c0f26fc285d3a8b75da25283b51ae6c612a288e3b1388bed37e909c183dd8803d6e2723d24a0d9035bfdf01c059412257594006ec51efe78883e53a12711e4f82dda21eff248640d137799842c696247032fcebad8487aedb6b08af5d38f9ea66c5a1cac436f8e97d93eb49fd8bee6764f04a7e33c5325aa3483396fce0d62338887babbc8fd967e85efabf861ae5964615fdaec487c60da6d5f12d549c0abf2c5049e2cc1874c3b4bac1a9657f0bd7d5a65cc238d9169211cab4d48a0867ae9c28a0bccf48acbd797cb96b866481ac65b77e2fa951fad5da11134f8ede75d24ee237e7a5d1af7633e2a25de837e8003da59b76b1a3445c5f6f54a04a6faf6e5b7f66243990147dded7bfe29f79e360e07a6056fc53ef9ce133737c435cf28c56ce541862228a6e4b25c3ee7c6c8e9a7dc3a357e3ff6c74d53f872cc66d529fd5b3ab545ca1da23863790d586913d432cefba57e52424c3ec3ecc302b5e76efa6a41af795b54dbecebb2aa450e20c3fe646fac0d02e4ec13a25fb7eb1df7f7e770c441ff52e2550e11a82d02b9544f565ad8712e2ab7462c0891a08a00080391e1ccd5c43a63161d0d60a14576a6367e19ac6920e3ca08aa9074a3943b7513619f085c618b213619b39b9966ba132e9f91fcb537fc3d887a1ee0d0dde08dbffad2033fda42bf3f0173deb04ebfa2030757c7bac4b6863adac6eb921da855d42db4594d91a41ba5bc3d5d512baca9f08c320544231f3aeee107abf492154841dfa718d6fb29ec6fd1e26f56273cabcfc9933880954db3644c08942747fe0f10673fb58def982919454499f5fb70fd728d50cd9dc29e06d676f73af1ab542767920c64749574c4f6eab2c7bf1fe308eaa04f0c9f35226d9d8cd7203cd7b5d1b41ad08d179c324897cdb16411a1eab41fb79c73df99b5556acb759b872e26ba8d48ee4ec5610291b95414a9e2768bbd1520612924ca03d48be30ce67f8338ccd6f20a415b64972de50444ec0c85e1d2180c251742dc25c6c25dd4d85cb2ac497d5c6167f212404a1c4e4025f9db0594a33564645ea3aa10d21eac46257491154bd4481c5865b4340a0017be130d393d55f372a2a40fd2f687221cda6f1e4965021d32672e08f720863979541905b8b4e7c0010ca963f96f23f9dda5765278df5377d1cf7359ed226f70bf3a63327e5c5b1555562fcd35330428d20734645e445516b9c38c2f376ce0d1b95ffcd092dd5c7083804c4aa5f256a160b2a416d1be1a597c86424750ba18290debaa9ae3a0e44e71ac22c332148fe6f8c939e3159f9f62cdc24a3271ea11ee08e71358ccbe0032a4c9dc06da9d84f2feeb605a4530ffb6e3c9a9b89d5a662692d364a5bb7b82aa80f18120be09f5dc3a55233ce54bbf45183bb22e62a853c5d7d7830ea3958deb9805c64668772e882e169617793f6a9e1649f8f81e16a2e3ac6ae3d7c90a1e12b8e5c95262cdca07d4af123cfc0728f5fce11a2fc7fe78d0496cb81658305b709a4fccbce459a529fb4205dafe3ace1457adf3b5a183945869ff2c38759d3771e4a930919b05000000000000000000000000c2792d8238d9436a45a1e754d1f193dd6a0b618637fa7e2e0c777634a395b1fb4ae86e3fee66b618e2e81cb5f0af233685d3d788eff5649099f2137f6a90264bc8ebad997bff1f4ffa134e9c3a36d5f69ca5570ea12ed59bf4a58700502c085900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000446c8a41181f5c637311cc926a1802ebe230e0155a4c63e5a01dfddd6fa46a32b93769e52cabb0d95d8fac9097b9345d0639980a75e0e00e8ed81f207d225fdd613ec4e442cb03fdc6daedf99013636d5d57960c6e6fb6da92b9aa6c23341ceaa5e085e31f815a1ef7bbce4560b814c5e3b357b1e6f28747cd7144ee71159e6102125e6f8373b7032d92d89a4b6681dce2360551cb0c334d408899f48bc2acffba02026556e492b8b062171667ab10fb9816b0be40277b43e340d4a5d936e3df6ac40a0597bbbe52f511ffc5a5d0ae119a5c99bfcbd551f38882812a4bb23bf051013023e687d8466e63f23c0224f0098df73df1572733bd99370bbff3212d3474752a02076e9c1418d4bb3d95d4107e056a0fb2e62a2e2102d208351a4e44702115bba302158923a9cae0a2ae51c9e0f33edabb868259cbac243d280e1db2f850d104a037032299efc0cbeb8f939d2ab53dc8c7ecceb68b2c3bb0cad070993041fd40c715000324382b3f6dcd21c2ebe48c87a7a3b2e49ff997f7a30917a81e2e4739c83929ca031285afcde7fa887fe83fe60e8ca1da389e021ef6eb002eb98141b4d7d7409a5e55c5e5778068d22f817322398b7b7d36689c53ce3569de0cb65f6527678063aa2389b11d62f7d2bfd784bdb7a2e4600015068d72a3ef6ccecf7ca61c5cb44c92476ed3d32af832b940a4a570213985188c6651117df0e8eb6cc65b353d7ef34bee8d55b36f3ea316a0de868538a4891b5a9493f3ee51289dcc18e2edef1cdf0d0893c418f1b6d9b1c872531902b5312a81652b5f89019d58fb184ea85d77ecbaf13e0c16ab0845c658505f2f734bd8d83e1b2d18ac5320fbb914af608cc5d4518a8c43f2d051a5c83762843ed4370d3ff617e771b645f9fa164a696242d46fbd1f0bf79bbc272ad6637e7efa9900d05e444ea8deac9a07355234636547a358edb428519f37995c1464ef857cd1f4c053072982aebd58635d4130c1936e12a6c46230b638eb72db0a3b5b4a0a22df334ac1c0661446627959a28eb907b1c84bc8dfabc47151fba635a53b0c7b3baed9cc048874e5dd6d1cc1731214305ee3ff201c4e87207f842cd6342e76652933cd32ff5eeda0d89b7c3c8188d43519e7697d61be360ee08dbf325b41d6fe038d1adef33a65bf254b398a02f763a020c0eeb2cff12f52d74380bcc4e76b8414323770719fc15120c49ab508913781351b5d0b4edd16914353fadfb231dabf95144b91e14934b684d78a11109b75dcb79d624cbfa5993bb8a9b80e8a2752caf18fcdc8aa5c085003e577c59fcc1dab202e3537eaa5bb6c919f52ea68fbd47a343cadea666e744d138dd38d463aa0c98eb8c8246cc793249bf4669ab099981138b74a15881ee8eb2689d716ce6c42922891e837b0123770fb1871c73c5583da28bc54825d09c6deb7d9de5635ce3a51e2880b3c7bf65835d353ece6e260f687a31a25ed84a957cda659a70b45b9bfe54f764165cc1d58fab62af4a68e9fba1ffa5bdf46ecb21a2a6a10832ae355b361be948038b724e6d34354af562b2b1d816cc1ac2481effea0b7644252628c4b0bbbf4b51db0e8282bd9e2a884df9d63b2d536a1ac46c049453d29a23c5152416bea74da489fc5fd6b3398fd1eafa11f176e24a5797a10c5be32d0b7721a97946a90a5ccdb40bf65bb90cba686737afc6c2b26bb22ad2b2d7c3641dc573432c7f6d1a3c50ef73864f3515621ea88320b8a339c5c1e6ece9821508036c67203435f2b56c58c819ba8b415f1dde7b5c8bc863e35b88708288556197ba1c120019bf5b017814bba1b4c4d8daa16e54f32a2afec7a5076b5d4c7c273f08966d5dddde33592c35159199f7e430258ec8a7d581b52578cbde6f53788899aeb112112c56f051ddd448b431c62cfd53200bb0040d5fb174bc12e91cf3ebde92cc18f757155f03eed20f3fe59a37830095702ccaa85b9393ce6894bc9beb835946b67aa8d42fb01eea80b5fbc48716ee82fd0983afd08c909f80972e0a4b601c5ef93ba06cf309138d3e363b142f06d0a334e15de7784c6d21013c2fcd7fd13953ff306199fd6ac6f204225efea15bcbc9f1aacfff544eccf6d1cb1ba0699f451223cac9ec9573923eb2bf0bb1a5f4d3c0ec33e649dfa905a7327ee6a288c794f1e6a67330efe69ea324a1239be4d1d9c08a1ff81dfe45fdfaa6382d1400a8872e07b0b60b062a212ec50b7a609740ced718a9ca0dbd0e6de97959d0b3c0a274a739a50305158991c654a571f0a89f3a1a120f79f9bbed35dd980ea283aeb288da994a14fa6b96cbd463e78fddd0f6e5e513bef6723e6efe88664a38a9d9a462e97650aef215ea6a95d4e2b428829420543be04d706f8da3036b5b99dcb5fc9c34fd726707d28c094a3aa83b43b06e8f9764fe9bf527e3ccd70f500", "51ac5351516a656a", 0, 474532199, "bda7daf2e6f2b6482da6e683e1b0a94ae4d5639469f220d8a2ddd362cbb98b58"], - ["5e3982ee02680893e900a43aead0cbf38386ed44a0f67d495caddd9423c9452dae2dc49ab3000000000400abacabffffffff60c8832c879efa965f0a63eb3cd4ca0f72527b2f5d5ab622807ca419aa878dad0200000003525352c73260e4010c2a9a050000000009acac526565ab53516a517954d5", "", 0, 935203767, "0d09db1e8937557be6bb722f4e6379e0df3805e9767ae168845e3a751844ba52"], - ["630a491301befc25d945ddd7a5d98fba5089dde921627e73c7ba17249c909765ee5991b33501000000046565ac65ffffffff0194cf000400000000035252ab6525807c00", "52526a52ac6551", 0, 140865692, "4e0df8ab9c04f37b637f1343e2022775128bf3c7c115bbe292c5f2e974a7b089"], - ["72aa55be013a551bbb24f7724c0cde5ee622f497ec4d77393ace4265a6c75cce2b76b377e70100000006006aac530053ffffffff039907cf000000000001ac1660e8030000000003ab0052ea1a4e000000000002005300000000", "5151530052516aac51", 0, -369397741, "482d40c670cdaff16bd0b2c1418d165250c6a81231a714c8fb1ee831519f6557"], - ["e5912328026533874e65cdf03c746b0c60d2501358bb264949e9e19e0acb61351553c0ffa300000000036552acffffffff9c0e804e49452f2a965ef4d9bc1ecb40abc43788b3e3ed7dd73cf9eb7d5cd6900300000003656a6affffffff029fbe810500000000056351536353d7f6d705000000000300656355598622028a7edd020000000000000000000000006962fd12cdd87b01ffef0288434ac82b5f43f7bdb20fad310d8f221d00a1c1cd3195a67cf5696a735e0408f724d96d741cd4bddac1da0a70bbb4a3f638b531eee6dcb14ddbc86e622371ce57eb3a97234dd3c6b4f48bc07a4e60eb0f3d65ec4600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f29c1e168f59160cec51f44505ccd558b614d4025f3a0bc03fc2027111283cf368d19186c4ccc36e453632befb3b9041d421745cf78836728a95a14423690fedc2646949fc3afcd383ffffd8ab4834f33f012777e58d1b26063ef9388a02d3fe743191c7a9a3204e5eaa3aa2632a371f0b8f4ea9589e7812bae28cc52e890676032aade386b88ea48837d2890cd3b5ba3034d1de3b7df3f25061829a7e0225d23d0327f3cf3a9638a1d61f3f6805b930f788141963b680368b4570d5e965bce01e010a1786c6ea155cf76bee5c206127460484f2b7ab23b87e401041c65ef05b24403a21acd886ff6e9001d8654528a09bd1ded0f4d055f0f4a3e9f4043c081f85fea9032eebc1a8b8ea2b6afbfbe51d36330c6007df5f12811b29db67a2063541fda86f0329e29f09457695b0532db438bfbaffe780d286c270264a278c378ca5ff653efc0308c37450af0fb4996922b2393d2e01235bc0192106c28a8664a5d23ff9410c96031fcb0694524cf2b41774c01330bed2ad6c6d71dacd88672769403ec5c18e5939022c0d5309b3bbf9bacaca2b93124b3d9ec89c1c638dbc38008181a5a723b77868470ad2de02d56cf02bcfd4c3b8f29b48037276287fea35e7cc51215a51d7af37e60ff71e41e6bd79b94c513442549b56e8806ad46c1d0a2101482373fcd480b658dc8305a90a63a1341462003d33e70992df3f2fe61d19113d5a60c4cd3eef7149bb67ea33a4b726d579e042d2104fcabc963150724dae33de69f9af12361a33c8a778a14f0e87b40135ac78f55609c997a7b8ac9e2ca67d0e7d68250d78cd8e1068cb33c8168ac1ac3a56694af073e7e2d8887d2532783e88439d1d22e253b44951ede863a9034199135ad61e71c85f8826ff380afddf7b90286e3555396cbda2f74ff10d2c5f1fc660e508ef3d0dd6bc80bf6d99c2a67ef09511b816e6d59ed2cf4bddc3539d304fae1a002b65f078813f650e5bbfa133c5abd125d036669a4033ceed4cf560c6cf6252b529eb5a75f915398783dd62bee381586c4245db2a70853c07fc8fe422196f0520877e09af9e6b37f5f97b3b0bf8763f84579f437fb4b97508f5574e2327f1a0bb820d504df39894eacfb14aacaf5a5e2afa127a00a90935936be722779be3b4c829657ddfde852b2f44cfb37e96c003b4663cf5f5d7b78074281d48d4aff32539b5b11fd5ddb85200ed975f6cf254f134cf2b4c2d555cad59b8ab2e313928d2cbe586982eb8fb8fca7c9d8518cb905597a4b5523eab5aec606d21470100f26c4e2e39161bb448c8a167c0199a0bd9d2ba05b9e5931ca8eb7e2671758fb872757b1047f135219349239f581765cd691df27b240fdfd26bdd77f67abfd6a75dc605af4a879954d58b15b0afb4d1a68be77b87fd799367a8de2bee176065b5520fe6d249d2a96072f3b7a5b8fec1a130c85d9fa725f3127e9f97b474e1bf8cf9ee9e5197f7d058c87cb80e5f78a3c9b2423d06f38a5b92056c6d0a749090461fc8b6779150d18e8902a85d263107ebc5d05302b3f82395109797d29baac9e1a10ca408c4298d986167bc4e3953f3d106a74bcb3634f27282e1507e31e96b5a3623c620627487e943d6393ca86ff56cf7c1a4718eeca5b7f144e3771a4d81aa199b65f6817b8dce53b6f7fda47cb3327e237b13b9ea5b04d1248483a026753e1116b58d6cb7cf544815b2ea3c12486d8ea8de5eceb1ef9c735cb9bd61b71861749ea09262831603e5f36662873c1ebe069096e2bdccd91363310e7f20020b1f22b32024fda4fffb63b12c87d9340fb6a75efc8802d7a2dcc77a70fda8501a7ac972972a8f03132b14228f6ee71ba4d544a2c36c0965451625d38cf95dc8e6405df7747f821bbb30423beed8fa2c3e2732cca84e833cf43bbd33c5d9f1441750e79dfeead7c113c27f2f6118166704ede35b3b59543d2581f2c2424d5e1e837d88fbb512c370099cc3441fd8e116741971c8045503a50ebf28a3c92cf20496994295fdf1d368d9dba62131efaa34adecc0d6830823cfb2dfd25feab86ac578c7c4da4495f4f6c34432df8d63d5bfdd052e9b390d77e58ad871c8d193d3b4b7135ec99790ad41b759bf0e23c97aebd6d7eefb66806cb2bc941d5176dccbef47be62947409a1be79dd67bc6fed1fee9de8b17894008034442b1e8cbccb36d7a68e308d61ac22162d0124c41b0e22411f1fb371ce2dc29b2f3945a7b781efdd1d999bade45ec1f4fb89833a2819d404f99c4069a144f8de5036e569991360ea2ec2811bf9dc28b3c3efce000000000000000076c86601000000004d5609946832de085fa63b6275a2109a7af5d1ea4462962771d7c8094ace9a9cb6664b96fbe2c33d3a85882e8e18e5731d7633ae87a408cc8e82a05676047db4a167551108a466dcd0580785c7e70446e21005ec107b98e5f479c500be99343e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c51294a715c77f0f1183fe01856293621a27e29d37fb024e6e265f3655c8f778209156b6acd9f3914f11a01073f9059d6549860ea991b05bc9f0e30e42e241cb7ae635decda2b546ce7cfad757d9a81cac62409e59c0e32087c3bacf298dc3ad714bf07e72844431e4b489b357758a77ad1d26085e28a42cbd1ea3ddcd29427b030311823e86d20e2b94a1f50babdde98c08902766039fc9875b4ec2d72a48c624022d8ae6f214cb71876707eef8280895cdb4618f7b658b91cc8db8a0105ccec4c40a25af2a7754e19d168a1655ef22a9500943fc910d90ebaedeb7a9dfccc360ea6307bdf665d5072f235f39891f8a3f65a4c2f6872a4524041b374560b223d5dce7031761fbcadaa589657ebd6ad46998efec5e90b2a982c0f98fd87319ec914bf41b03100aede632b9b7a5cfd5dfb096d1b8fbbf7a98666d13fc73e073a4549170e4fa031cd158469e014d24b868fa89f242db4f1c4ac693f91fa02a857d8d8cea37d4ef020ec1134fa929ae0f7d2525195c89b5f8e7b6d1d49eb9ea54c65607f56042e20403281053c1c726548eaf7e395fde706447056a212635beec16eb2fc1505eb1b6c60fde930c72d0fd1b64f48a5eb59958986ed099b4d8a23f7953917f0b29d2a2a8396017bccdd8f2d9f7a79fea75abeb6cfebb58575339a34893dc0702a210bd07fdf2482204d31cef443850cb2bc557230eb0e98c0f7515073db11bbd22cef8af482582c42266dcfe1968d93f609b2f564af0ae90e64ce131dd8b3b0f4219c75f214d8bf2e3d82377d1f166c39f45dffe0e692baf6101fc02b1bb91414b8a4cb182f25141034bd37aa7d8caf8d0a226ac34044773ae0ed6b244fd94a5697d246667e2f91f1faffc25cb8b5f6a558769de18f5e84c40ea1adae1e96e86fc3e2783c7995a5e9e959a8210f6e6f99287b29ce4f06f97c602027f28fbdc69ff347477bb0cc9720e1883ae9aa024fb85c240e71470a0bfcb2cf3a2acf93cd2d012660224aff2efd6cb7ac1f94e9856d6bad74b4a19347796605255471829dfc4fab061ba349274206cf32f6118e10bbfacb41157953b1e1aa7d27bcec08145b53cf0964e8e852e90b44fefce7d9df2a9b561fbf882326481bc45aa8e581c1213839fd0fec080a7fc0281507969a16cdfb8656b7aaf1bc5b8660ec43358a3aa41cf76a9495d96d5478a2e734d281fd8be6f5170b53814010efad9a9f2f178e698dccca1fe115d21a3da12a1206c1057285ec2d888e0e84b5ba6727f109d71533bc3a4f4144658fdd1abc7411c3921e0eff063b143e00bbdac2e5eb0098e5a492c50bd66f10624444ec8864c98bbc264eecc9a97bd88dec7c1daa18bb40c438484de891390f539956aa6ef89a053f836249e7a20478426751ca7b32d24a547bcd0cbbbf58c0c4b869e04269c3517aded5acbfa3015c23475208e1f658a62b8157fe4bcbf49fc67d6de9a17cc329a1a85c330abb06d5e96a33f47a45f646b922f2ae5d5ebc70f51b67a6213bbac01736a7ecbb0c07fcff77df3809b6619a4376e22b3b92876385a61764e15ef54c18d02f9490559b2b2705748f8560da9e0c33d03cde5003f9e0eaeed7a8c571fb31160c5d51029563ceb5066833ac5e90a98bac4bf67b983d0066f279780556f519f6817300fbe1f9d493c149fb8bd1b33ad2cb75874725af15504fde0ffa3d9ab224cce88c7904257adbee2f532a642ec1527acb70f49f3683e8f7a9350456ad08d1682e66b01fe5a50c11295ae40c8bea597d7f898c9fb93d34c9faeea8de4fe029939c8e866511364f01878096c8ec7def6912fcc8ea5f67d5a384ea069ba048d47848eb8fbaf9d08cc0e570adf808ffaaf2fba24b92217985d1556573360e3e8f4ec38ee1ccd999b8a5a45dd9924ccc051c5f4b555464084b338bd500b122149268e3814c5c4043faabaac08e3aebc1ce392dea763619f0b283112f457dba86dc174858ac4645eaba699319865f19de12a5405012a5c0dc9f6aa47a619cb6627c2c9df5d6f2d00c0776cff0ca848efe6e8d924d4be504c2d3ce3d32ce14da33a940b6cc61afb76c5287ccf578c468e88ce0a0bbdf5bbe345c4748f20ddfaaa2f8bbc1bfb65ba25f548d531118043358a89957aa9f045db565037bd250eddef6f147ee6a1dc5dc4eb65debcaadc91c876b0a38bcfe810ea60b5b0cb15765d0e8d5f05dd9c26c29b408e38262d2d0069c1658e12448555bf8dc8f85de83e459100c3d5b9f074ba47f4645ded8d0fa16b07d0373f82aac996b58e4e44b761f7f9cf7a8d2d179ea0e9a2d81e37a81972209136998a91d94047de70e7d1ea2a3937c2fe556972b76edc169a0014320bd9d9e0aff193d095e31e833c648bc4b5d8c2418b1d46b2f8dacb33c6a9f3dad18612aff08bfda922bb199d081da1ccc924875eb2bb6824fa9404", "52ac0065", 1, -615482025, "61b6d166d7a925d56819cda0a5a13d910e15dd03d40e99904f2c4b0a94690cbf"], - ["188e8d4701936708624436ed22603242f473a570017a9cdfd6f8d74f5b37fc36d5065b67dc0100000007acac51ac6a0063ffffffff017fd83c0400000000076552acab0063ac608c344d0300000000000000003934870400000000151ab91ecba54234136f17de77aac2d7ce5c802e0e14c38cc51e887c3d46657ff817be5ab9410a4af39e3a7b181b1ee0671f657da1b3de37f9d28ab00cd6134a06030990df0f1b03a8dcfa3883fc1fd2e800c9100b6a72dc639c0b9e8e7d89df00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f88f22ebb3acbc49d5be1b8712268b987696f8a8c9f7d2f54aefed8959262cb2af6854f9785df8bfb67c383fe0b5dfe5d8cdb164baa11311d4831540d1e8336fbf2610f46836c965a4b14f0fb965501e7051a67accb55a5c7975dbcb7ac82561ea64641a121ce686c017522acfe7832687c7a002a1819b697f0ab5d8503cd1d302275f98ddc62a2f8393adc08bb29b06e70166501f64b05630e6348c6ac3d33f44030d32cd67497b2b2d42969a24f080dae5f9401419a209ef964d64e8cc7e07defc0a0fe5a19b9500f39d03fc0c399549617ca58ea4a3106b94adaf966c71c70a92650adc789a5a8efa9b92227557d13a134c078b4d3f1ba05450c19b9ffedfb704970320b652f21427569f2cc292cfc6979c42205bb3a3261b904b8004c843c26efd66030a8fb42a2e7c153dc8e70532a437637ebb7c9c0c07a977406b33c880ffad6d810300ff4d9bc9d51e41e2529eb864bf698a3c470e548b4b609224bfe2c62998b43f032d93ab0193ea7635daa1b7590869f7c7623c57f151bbeeaed89b85250cb0c948031386b76fc49b6edcb4506dfc42f42673290939237aa2b3e84c7da39e2abbac91f3f03c2ad92b250b3142e4e03632fd006d8a66c2c605be63229f30a8fbeaf512cb6f9966099505ed50dc7855326f32b374a2cc0dfd6b6458ace6b1c0b725f3dcdf9b4dde6168a8d532e2b3cd884845efbfab29fc542aca23553c80ea3b66d259f928c29eb9c3e0dd27138850aaeacf33bc9631a305e6b5bc92664cd17a2b0d010b019d497fae99313bdafd1494f87bc546eda123377a92723f7c641c23330032c0bcc6a2b94aaef9b2d5108a3759d7039c724137987e6b2f125768d4e8b44d6b7097065449bedf0535f5d01cb90ef46107954c4b7c3bb636f5990e7c242fc6d644209d7e82608b3b948cb3119488dffcfa458f3d887ac0c499b0908014ca89484d8077baa8f3770debc35eab90fb4228304c947232232b62d2a623ca7946d2c82981dcbb4f4a5cdeab1d40d4c2192c4a249d89d5c1e5b2de9dd6cba5cddf004533f604a4c25885e54eac6595aad3afc9559666699a14d1640219d075a426bcfda1b82ca2111a7ab31bc27162bbf28847616a45e182ad554706f70ae60476f0bae29aca9e31700165365d8140de975ef7dc544d01e1fafa057a0581b08385909af3b0af29cd9dd0a4d69d12769d8d24e62d181aeefa76e02dfa2e55af18c75896581481f4e4ef2e795c58d74613918becf2277c10ef3f174f4c9e6677a1a84a2e1ded4d1630beba95e2ed692e3a71289fca102f2a81c692a2f02424a694002d4dc811cee1911c4254f96c653963203f26012743749129b2c96cbffd238c9050d0e633a3f7050268f2809461661375eba1e6683fc6d489b23dcee685c445a08192374dcfddd365306a5580e19ee9bd17e0fc9772dc74ab3a6836c70d0c8790b2f0b268a69e3236396816bd33b6c1f9e3434e3b2f530cbdcd10f003c81d485c424286f7e25b42a1baa98e12a6c954b68325d2bf10c95a4be9505028d9029f7fd8c3e9ccc1c5aae48f47d38137349afd13df9ba1de5375a45841b7acdd66bb5e4367fe449d9e0fa547b87aa8bfda50db9eaeb06c0ca13911b2576f095572ee5318fdf9637e61fb8db8a9392c66fdd78c744472595f9ca8b8ff7d0f38f1bb716e7148975c0a1c033fda1481a23d7af490416b8c9aa34644e3dd6331d0fcce539fd68ab65903ecc61b07e64297e3e83624d4228370ba7c7f29b0bc314783bc02b64cd40cc61a9a494b92213504572a6b0c51bf91394f62fa0874bacd07c26d5584828a94fe7aeb883dfe6c8155b5ca7fcb4297ae914ffdfc0e5bd73325360247efe3811a86040a67574448c49db18f56d83290a5724bf27e4c5182cd1853386764164e0c5435cca484432a6f7d712754acbe9a60cd1bd76e72a8eb67eb13e712450a55efe180ec96962584b2ed37aefbe25de3868f58833e84195531c15cf9edd36e46b8bd38ae1d10b5f23bed08cdcc7b8bbc8207589a69723d40c507faf3525e841933b13d84e31ad6f6f8e3e96d2ac90d885c50424aef979357891a7336ee5b5099bb576baf74c7dbb3ad2272b63d2ec21854def045ac3dcc49b41b1f252c3a517887837ff2ecdf7ff45fd436fbf3f9b8a49157ccc05fb19e34eeecf49c30bfdcaf668e8e888fb6ee8c4478fc69052e396b02bed67f655975177eb93060d35bf37e70f4ff57d337855a9cc5002124db6ea20ea0ea14dee6f1d556d3ccf55921b4fb629abc85dd1a8347832fe056e3071b44afeee5cd9805000000000000000000000000aff465a53eb93bf639d36b3a33ac50daa4dfdf354f9295bf9b7cf6d7052da14d32b696b76269c8c06c2d401027060d238278cf517a4bcee4e219f49fdd222e403e0da30ab3c7240c854cdbdeb341f33463469d3a4153f5a977ef5d078043e25500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc6652356dedcf42569a269222ad3a1aab05adf2090a0a72e8438d7c288edfb42bbefb68d10182358ede5a937ee3a205b9dcd60cb256ab00f68f37688f04fa93e117d39ad3081658951360b2bae0a559d301f43c5d6c44c90f8ceb2f8c3ce1905a595ed7798f20d51fdab2868505a65aad83ac29f6517f948641faea8346399b03225f5586a92dd872c965ae6946e9ec2f4e14ee25bb27a113e9791fe5e811a1fb030a5a4943e1c84fe17fbf00a5ac5e1d5e52fddaf22fadf686992a87760c2f902f0b1323b4772a8eb0754f85384780cab3e408d1243133f54df8d1f85fbb4ca982450cc268ce288c1d9e2422187c279f2ee21c3edda97640c47369606909505c1d6402279a050ccb5ce64e109cfa2d60888c2b25af23179b224331c0cce3a1b75012af033002f46e54b2294b582ede7ec39d7787b08c3ff84432232f569df520b65970350211e13e0563ae64a7b1ba21747f28541299304ae0cdfcbd380c0903c8c5cedcde0318c9c2b263c3041036ff6539597b41bd974b833ffcfe5ad872f9a04d9db017140307a264a75a111377573282883c6f10e88eecedf4a94cff2e7216a605d8569bfb1fad363866456721afa8ac02da874eb02a836685fcbfebe799a911e6172fba08e887c1c266c1d54d7a68643c720bd763c7c78dc34fac65a6d2ca81db5692fe9404972f4bc9790f9255e7fe6e1cdd02cc7dcef5b8f56a44f5b359539d5c9f376299458e3bb359fa22175285f5c7d740561d73fa15cdde018df32e97e6f5a284e6c2499f6999bf65b9f38e7e7e3ac40c4f8d4fc5ecadbe3467e7fad82540a2a45b609812a836a01699dc99e5ac719d358e59ca0282c83553d1e8b85c9051b930f7557f1ed692447a9868c8357faebfec355d3c336d01308d6a38f281d854709f2d862b6660a001a76d658bd174c05763947d1b050fc6e353f74d85a3fb9cdf8cb71fda45640c0a00a72f22fbabebf94e17cf969600b2ada8b3737a7834e3b56d47bc227b1445af8f0d17884cb426a9afad5334017d36d23edb2ca8f57ae0c726cecb50eeea7f35082d7078a9cd13801e453980123519330707294153e574eff2cc9d75a02fe80ef199e67eec36b458023dfb7462eaaa6bde27c7c753c67b0a2a45eee5a01b1bcb6fc0d30bde6df33bc156e25b51435b30af43a973fd87d4ba61fbf7092fdcfc30fb692d83a2f9698729a7a6c7414abb7ec6ba203f90168dc39c3a009e1bd14e50859dcf8a5ce6eb8f184f143ed4582746a710e5227968b3753466f07e161fff4e05cff4c26c8e91798f871dca37540df0fd850bf073e5c41f209dbc488919d56549555c381ff88c2166a56565fc5275ccae6f45d353dcbef2f04b72b379466085bf1a6a025ff970a5b1a3f5a7e0c2f4532a022bc26a8cfa734a32f8516d73cf7940acff61d634cc6710535318c43f0c967ebc311c16c1993834b96796be4219653bda8f875caee3d34ba1cfea2addbac6d166a06caf12f4824bd71507054742e7b485c44d22ba33f6714a302051bae7f07eaa941c79339537af0574ef65635098e723fe5db4fc808544cb24fb3b515a6bf32927d76c6342137b392d44914b67c1748bf4171278a2bdf939ce028e70b68647447b432a2ebb34d9d9f74f3cd47663d88ce832e7ed0e2b11f0f94533be13949c21d44da2dce16c27927f89634314e7564206e34303566323e5be3ad42ec7f5c5fa91baf658fa0524924ead82923651912ca307cb739e442b363330fa178b80b571e7c1482df6773038dad535247e8c20097d3c22afa3e7c09e40283dc4b8aafc45850c8829282205ae5e5fb81c7ea2000dc4a745312de0ce0b623f9fdd8accc5a9e44fdc0d82d3226e455c7c05f4eb2ddfc69f56f93fa240b430e0b95e88b81b1638c8aa7af5de83793112b7596870d733018f8b61ea5df8a7843709b20bf069f60f02d024c92b66273fe9c3224d97602c3885bc2b6fc7bb4f62b622f7959035d8f3bae84bbec04f89e146998972a5715d53a1e530ef2905319559efb5d4d3eb09b7452000219e36737e8bcf2dc60539693e5e282628667dd86bd896845c6d954892b84c959b204565ebd9c1b0f4dda34884b62c506414288394d7ef83a7b22021c90b776e7fb18f810eea5157f91c8698ba819343c959a4de27f6f3c750714209fc450dfcfda155be98dac6c1fa812e76963fffed29491ed0573b22ef5d7d9a4df2cf1c3783a47ba5236b7567986325759e256bdcf3bf841339044a5c906e12174e4fe09923a2b91251458f7d23e4fb7237950954323e23944224609eca00000000000000000000000000b33d8adbcb8dc8dd92aeb451488d8085015db2e9b856fd604e8adc1e763ac17709d012007061a5f49d9bc9be77f4caf0a4f0d7fbe73cf7cfa9118b3d7761f9f87860444cdbc098b11aad42543c696c8be520e0eecc852a409849ff3284a0f4d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000624960d40c5efb2acd85ef331c7a1807a096b25259e2e11de5af925e3b3a6f7ba0cb36066c88d2e4cc91066a75971b85969bbccf6d288e8578342178c0c154ae1e4a4dae7e4c6d5ffe6202f0c6ef90632f4ba398b3578d7d3dc953065d8a9c632844a76e4ebab077c7351bace51362b34d1f79179620a237b27d7b00d1b6c7ea03031f8752997d5d164bdc61ffca7401f44c5d369d4ac9c7c386c0637fbd41dad70325938a1bd17a1993312a850684b083f4ef037f4f71319c098d484ac14b2a30610a10e31e3b3741155dd602876e88999de2833d64f1b660e801d86b9ed8242537a910bbab710ac8d6bb55048bddf0344c2c0221a9a2abea65273bacc4d2babf844f02169fb775497db281b05ac39687ca19dddacf909bbe3c3c7c6d8e09fc8cfb18270227100be3ac12338542b019df80cf7f3fa9a20363421986385be74becd988c7ae022f99ad9510426445013a765fd968e87ef1e37b64b9cafe98855d3a2b0f92a02f0207ea6b5ba37054a2d677fe5e6cf9ef6c5452dfe4ad57697fc5b5b63583e827bc030be0da3399bc1b1dc151a67d0b4a5f1acd74732fc9299543db6733d395c901e1ab3811a0e652cb5ab7a5e2fa9c4d6d70f5b1335fdc2945eb6caa5b5ae2ea4addf8051049cbce8613de8add016ec09e5322f7b2158476713b9bd865fb8e7fdfc08d05d9bd08ceca3698285cc5e891bd726e0785aad4be774d8a25ebcc39e1c15ff5c82155545b35fc690abd1caa3f4fb28045c9811223a1583eac081659fc6f837ba85505aa2dfd420b7ed5fa201c6d7c22caaa0961d675fb8eaa3ecab4060220f8e2dbc0c7c6ef2340fc291487c3bb2d58bf2f523a7cbd0af14d0983a78eb38e942a44fee5b5446d951f0e5a2ffad587dfc6d1b6b2feb266d3a248e6a6540f4cc8afd843b8cbe2966e345d15b56eb7261525a5629098c75fff43d9f0f70dc0d8d56a7afc6a138c49e6d68cdcdc87611dfc5f53f8028f95aaa7315efd1e8354db9185bc8b5a639d95a6f9891ae3d4049680e4e44190509936d41f723e6e6a362be72bd261776d2885ca43433fe751633f0bc2d0d5aa5e367605473df8050786b4108d7c91942da1f78ab6ebc225c8e7f84930e438afc4163dbc14c108bcccdf6575c7f888af2c3a47f1ebdc62bb4aa81e57162202f9dc2b093974c475bf3528b938d2f9202c0f65dfd09203dd0e3a32022432fa4f810fb6c9c99eb3d0805cf869063709e6fd6ae8e83cbd92a8e6b3ca129a7f89b470f9e424eea5aa48044e8688a1e72258c6561fe8a52f9a31b9dd4702c75e79599b452b3d05a3799bcfc322439bbe9179424c264c72c3b1aa32c89dfacd1415324ee32a236afe71241cdb0b37ebe5b223931cecd4a0fbd8e314dd16ebe2ecf27eaa8fd2291a45d5feb2f81c7ccfe0f15f861123d76f821ca9dc0a7ff1ddd4d7a89443546f9d073fe7f6ef217dac70afb4ad3be6276d1d6e7c2c83d9399cd1ce648f793bafa441dca15ca43223e987c0d60d2fa4021b709dd5fa56a2872e5ed7f7a83485f65b15bfce9ccd7a346abd751b35877c073b795fc03236d8e640d24317a86779e8b73063ec96d4083d26b46eebef3a4c57e956da376c0d735a27abd7e8351db89622659e2b9042f03d2d166764b54c8684b7a045cd7bce91d8a5a3e61e88c6e67d80855c1cd223adaf9187399bc702b8c7c920c668541fcefa50d79ed653d1edceb4737f2e282b23610f3b9c0ea7e79e09a5d95f66b6f38b8e007c1393ca99a1f070af098e6b3d5bcd84a29468a2486ca5483480569d21301f31a888de3bbeeb7f10800038f7549f6ba72bf96673bcda30972b9497b1010a10f6dca8cdf6b8c811c19ad8f45bd368b560d25979d98035c93e15a1a1137b4c3e5f76e3a8389b4715543eb02babac248ad06cb8280546171957fcaf0db8901abe3e6980e301a521d7637aba207db89a296e890e70640adee46f46d56b158f3e8535960940cac7e21b35e8499cfb76540f235a3630cde995872efbf900b32004ddefd691e7679313a20e7b12023db18a8b8aa6d4375247a701baa88ddf3679fd2dd60e44f776b593116bd63f8471c586426c1d52591042d201c7e8003963ed43e0666da26fe1081aaf0b3b1c03307922f02b65b43d3304e178ee1b6d27caa8a0b9a2dcfe03b34e78dcc75c2077274a61eab66d4146690271846dcf488d69b786e6113c48b8a40552111e2e479dd659d2387cd4d226a13bf586169c310095ff12d9eaeb02dd312942a02b0376603fd9146f756ed90d36524aa23f2086d8805a4ae1747c3da122a800c0cf5d0121287b3c505e50adeadcca2b2bb3f14c88dd6e94133fb459d3edb1153ffec9c688c03dbc81d90a8833b7fcc881ef35d3f362122e00b868dda13df93b1cfdabb0f17e2f1a33d81b448a8d681d02affdbcf4baf31bdd7001", "ac51", 0, -473689001, "e07f78466f491716bb0577ad7fa5f66e028075cb86a335ca2f0e94c4674b60e7"], - ["e2a4d5f203d41e717ccc7e8c36379cb40bffa61bfff86493ab134097798659e681d49334d90300000004636a6300ffffffff00b40f2f6c4ab39690275e09e89d8bc8a45ea7c36884b7df2b4f3483893e0b220200000006ac635165630065b7372662bfffdf3c06c1ffb8fdd048c81dfc2a3daf71be51d6c5c6df34e3b4534290af0100000005655263536affffffff03e89601050000000009536a6a65ac525165ac7925a801000000000953acac6553636300ab91264904000000000800536563ac00acacab914ef0", "6a5365ac510000", 1, -472291830, "b18bb188c82e495d5cf26388e43495047eccdc0719748389a55cdd22bf99747f"], - ["73d2c91c02d05a6f306cce4c7f22088ed3af50854478971c4a233e96d64874bafd9ddd384200000000070051ac635263513ff6cfb10696bd120b95e2b8b51a8adc6f4a65179c0a98781ab82eb62d135da1200b27e60100000001658b48799602d1016b0500000000036563ac180707030000000007ac516aab6351ac000000000300000000000000002dd67304000000009486ee138a5b0a6838b5143ba863615fc431fd36d794ae4b3f623fe3b28d7d89531394ffb0c9434752757ecf9a0f258ae97f46105800351c7880537d5d90d0656868a5cfc53d2146ecfa64a56074686aedc67e0533be90902279e9cd589afe35000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e17f8301de5a85d79525c13c946fc308bd0aa93d68bb710046a828b363d386a1dad0c2cee4828449a8d8f906e8e9fc3c2f2bd1301b07253837a86812bd03c96f4422cb97ebbf526f0e542b30672c486eb4ea45a117dada374bdd0465ed319967782ce6d72461838151bea0263afaa4f284c3fe40a9eabbeb3feaf7404ba2cef03122c767f81d77c5be838454b419c91365f3f6caccc27ff1b329984ff5ba7c0ea020dac100ec74ce6a25ec0104643fcdffb9a1de77c8826df76a7b6c3440f1e43bc0b2d75113199a0cc827c8218875df55328a0d1a8099ada82b6eee4222361178c552a5c9b24ce702a9b8fd4eff914893ae3ca030aad5b130deec9abd1c70c9190a7030278deaa7bc4f0d0c6946cefa1c94b774f4ac0ae91add074f1e38004c33a2bbe0318e3cd44a0d5226be65c99f451fd8cab3a4eb3019b77b908db735f1d003cfaeb0219d29b1099b2254f298d6b177c51e4b669988280aef77441a78f34e6d0f4ea9f0214da4ceb4782515870845fda8a2f8cb9294fd442e2f7da7674ed26f8330e17e10209486d36206e44eac1dd23dc643ced06c9909cd5ef1514abe15996fce3b92d7ba545ecfc773f8e281b6624948d0833aba2c65b634fb916dfbca8b1d03d1f5bb7ef5b93d4c5d48901e8739da8ae37e7761b0d172aea21aeb76e36a015375d4286eb94844a4dfb4ec14f7d3e21e5e472e6f134538b559a3a0e676a3cc452632c61fa3e113b4ceeac11df30d7b53915a086b8265f8063959e5d2b5ae06b666507a155a3f735bcd04cd757d6d6b6c791ca2629b46cb2d444f8e117ffd6c063cd288d13cc635622500f34d0101d397636b60c4e851f2dfd5b22223659d780f3b50e54bd028e116ef61dbcd960cf8ff27f401620d77f7d394b45adc17a291458dad4b87c2cd244ae6bdbbca452efbe41d7800638090738d38d047cccc59460cd365ca88c6b61bbab8f75cf47a87917dd9d682d85bdc5c8640b42f82ec5d6bb0f763046a9a9dec410af65e3bb5be6ccfd8bd071797f9c9e58be5902855f7aef39239324d0d53b7c8934e94fa877bad0bf1c81b11322b226a76f1a9d097b2a6ee6f3b3d3b55e4f91b084d1f242c0f36d6f0b65fc5626abdefeabc9236b7dfd9e7cf6789f1a36ed275575238943db0e4b5b85764e28cd5809a4f23c6c3bae718157caa350accc02aa4c530f5227c29e3d21566e74098a9982c58f7214bc03a8aa7a8602ebdfbffc57a508f095b84e83192f5246568313596211bdeb6141c38ef7c22e8ff87e4b1e67e00a5d1757457d8f300fca35d2ee63dc93d319c43c6254b319391e0f67368f6837fbf2016e4f4df7445b92c00f549a70d800d9fe1a6fc451ccfb280aa4716b82aa741483da7c1fc7344a2207d70d304b925d396bba6470b8842db28aa55fa88abaf5ff6bff615576eee1007cee085d6773f800ff983a2a3829b08ae9798a83bbf30034563c94e71a1678b2a6b1b6f67e87898392f82efee6175ebf92c72b9c10a25ce09de150de6cd08e22168c6d98a37ba09bea863e45b3a8ead2eb28f7617c242279fa0296deaa31d58aedf53a9b81ac8f7aed0c15080c38568c668f173cced57b32b2b030b264b93c6a0b9ebba17b3b4a3c0f6412cc22b414036cd65e3da9cdd489a6a55829f32450a1f1e2e2acccbf4c060c2bdd9c8c262df9fd0e6530a689407a39e6339ea8f92e971557c4f2cded0616bdb1be985d1dc9085a4cbd7ee8672de8de59c757aa6deb83e221c741a9bf0c12e8bbe23b793064707150c42bd9ca554d744410f8691e8a879c56f18b05d8e967817da9179a65d6105eb8d4e5d2df39316740f40e436543bab79b12390c0bdaf37e82371a2cb89c9d4af28e718a169a2ff3c8d2b1e8976cc449d8958e36a0c49ee4206a34a3ede3dca7562b86d6ce9482db54243d25cccc88e601676d6a080a57806f03531e1e7714f89a6aaaa3085dd8a1e36a5d288a80261b481e1fb1f8dae7471235b16f81cb09bf6133472c44caf0b4e8980fe7723da5afa0532035120d3f946f9b87b75d75feb50b5ccc065a50fc0c0e1ac71e727769f23766e7b31f633eddad25858db6e747cc321e2d2d5ae4155919914e54efe7cdc63bd04ce437c2522895f7d9badd1e82b6c07213b82215264e128b331bb2a425964d768844120832aabb0a022363dd8f008aff3d757583137758b77d1bf62a44ad7dcf0e2e77acd552ddc2c03c9204c46ebd129468e40edc84bbcbf714febe129e29abe0b27f5287b52c6e5abbb8ca472e00fd178248e6c7de4ffd9682bee2b648099d000000000000000016eac203000000004f15928adae2cc288c5bd985260d0a0a8b72efc8af5d89aad2034a9d37241f9b9c58aa729349ab302a29c1baa77da0dbac18fa6353e97c64265eaae33291ead90bcd56bb9f58aa65d55de4998ff575d015c49dcc120f5fc264dba14bd4bba4b900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e78982ec73409808a07ec4f493691846b61d52e02800998c763c9ed74125f69ec6613124b5160d738c79055c3c9904b0260096decf47573e46d666e9b6218ea55adb69e095427a971b9147d0a94fa95380635d54e0985273f92ae09f320f693b150ba2ad1e6bd81f45462e19e3c02b65cab93a9057449676419dc9c533f6f35c031bb29681bf769f194fd3b0f0894d31e2f026f9d4b44159815f4d2db0c5a01a10030280ad9dd8d47ba1444ce50a6b651ca566a477e634c7e2fe70d0f7cd51ed3f910a25aa90939cdb1eec8cd06450173939babfc57f11a787f734d1b63bdf0c45d4230a6c025839f71a2ae7588aff280560c1d738c2acbf0f134fd45cb0b7b72c6876032ee182887ef29f59de9dffb23f43cdd833963410a7db73cb2b2c29fff984a7310327057e50a2409f9a1207ca7c39428abc6e5c47a1f5befa5b3c2aaa91ccb3abb8021b95a5bf028420bd29888dd7f45f839a68ca1fb740230f40bfeb85482827f844021bd3c9d8f4fff3405f2c1615bf1846954bfcdedcd38e80cae87700c4f340057203270e8572418f8fedaae9050caa760e403f11998b095258bbf00d04dda874d9560075a3d3e1bfede37a32fe31786cbeb4b92b96a7a020ceea044f8665d59e016588b1aca2103452d4d1160931a53e3496052bedcb00201b60e2736917e40dfb4a1502f473e595a889bfa25c858e0cdb598af7516e348075487311a6e52ea4e7d3413f5fdafa6fa2c6deef3393d25b25f786b7717c78c30f3567075179e4e8dd70cc1e92f77f25ac7a766b21fa1855a4188f1a8a9fe8a8673a7413519fdbc12ff03812dad4ec8c5e488bbd291578dd2209ea11537bd23100a5c2fe65c8b246ce21ae2d9333a2beaac06d8226cda5d60a5a4c96079d56ef8405cb3750ef3d859db1d8134639fb5e3e09cdd98190571b59b946acbeb56cce5d2b585072ecdc8cf65c7850a478c6e3dfbcbbe8cc785605d16dfe746fc1d372f6c529d105d1a494a0df2fe266fb61149c974061159d2f3e360dc184c3d73c313879ca3530c56bd7d93a6a24723484f893298922dbc051deb608db95434184a75b501367396616e8eaaacab7124c690fff60b19b36173e75b2caef8902d71fec8c8e4255449f54d4ea631ad66480fa3b53e95b1103d4d391eeef75f3237c071d32f3c7bfafba14c7a6964c01068f0ff40319901a160a4456ea8c795f5f2db08f70d143279d345fb9e9c7fe64f0c75b51e48e3fde8232d85655cc1b7777a0b5cffabc4d4ccc6227b81832c4a8df5668550f5537890096591b7023e85b0b30a320a812fef71b64e173da3a371877febe279c90ed04c2eb8e25eae1a935fe75196ea6558aab110339756ad8d56f26385c26069e8728a75d93d9d68373334d253ca2d4707dec08e4f1740501da8068aee3d74d88dfc322171ca8e2b62c97d289d523e33832e28cb7b09e2d33b84a9f8971b4fa18f7cca27b6e18af8f83e12b3c92572384a3ba3c45f392cbc45d21c8c495fe2b9eb7c485ea3a2bd1552d18cfece62a477a2666ce6e77660b31cf84a0d0a6f524220b457fc6c72519c4bf4b7ad76804ec2b3904f9e07ab8abafb549a468b54ebe77c46296275d294799f7e7213ca3d4b30f1ea356a45e95bbbaf1b5be74bc83c86ff254d4066a679a70a4d06d660f0fd7e036261f3c3191a35d67590f9fd5585fbc32fa42c3beb74af0366fba74ab4f8d2b0176e686be757d454173ab350901ce109b31e5ca9a03f63f21ec4d9198b801f66baa395a26df51cf73cf35403edd40d6fe73f338deda9ec447eb61c8d05f60d795daa6fead0c46b5d08901be07dea4c47a628cd1962a4f56323c826536e194a67d2786ba3882d7117b0c3c78bf892b80cbb26081cbb37d812aaf49efcce43158383f58c249bbe59f70ff1299334bf5d288fa06a2c05d8e0a158ce7fb2ffda13ad0610e0155112d08a096ccc0e4e7c16a98d3db3ade3e34129459f94db14c2cac437721f7a23e83da3cc407596ee36ed7b33d1638cdd31b28f583d33ecdd0f7596db12553f5b7724e0e9ff1ceb803fd6791fc41930f328b9b2cde41425fd25203356b5414efc7527b474f9f493de901ab09ef2b507e1958a129234064684cfc29440a8fb3b87cee05cb94111448ea9f4242cc98748cb9bac5b0aeaab71717d4b0d9adeece7146d2ef5852b6b0ca4b1aa34d995d0a10d2988c6b0db03c5fd7d8b4f60959b8982f66b774948c10c0e6efa5dddf80c0ad0e332c0d1c12bf7e997564ebb6765d9da52290d1bd2974d4421b4c0df2e27c43ad4a2e278c0000000000000000a01cf10200000000d6458214d86d0c95729e081ac1deb9b245bfde5c57f6b8314151ff83d22bb05f4921e7dd509e139859e9dc4c0b363733d5153c25a81c5767991993fe2bc7140de2c130d0f15afee7b02969114e811858861783b4d026ea1e5a72cf4e33f0409200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e979e7aa1e47348de1f74f0ef39e12ae2e362cb314ddd263d8d8821dba6cc2cb651d9768178277a4966f87841efecfe191b24a87c0412a6f896b8fc98a6f40dd43c71384dcff672911977c45bc9a71f17cbc6d5a71080da7604f88071e9181ad2c2d6582d96bbc5510e508dacda1dff08039970fe2773ad810cb8170bd0a2435020bfe8d506b50949bbe4200af7439f71358967b649ded4dada0f37402e8dec0850204abd91723be458e38da27260f886ee66b442ee9e496d056a761dae4efd6c4d40b19b2a5d2f4a913a8ef2ad8a9bbb74cf704bbf30cd93d37fc2a9ccc1969ad45b922cdc389811e981baf7f31082346e020520c779dd474d69e7b8f5ede7a601631030fe25d6fb31837f8c930bca8cff0707cc6a99d20fa9452dda005aaea271b83a40325f7804eae1f53079b053491dbffa69bdf61d2483ea55d0b5ac45163fe9c5e2b031d4f0aef90524cc097bff31738fdb59908cff5682a17206864c7473ffdd90d6203069cc1ea68e07a38a783d0195b6b6ec63e4adafe71d1c0d74329622905d9f413032a43defe830ebe0bd3bba1118c84bd551a8a887f879afe87a68ae6d8189bc089966d228e8ba3f4131879893e79de6ae97bee3d34e41c89ca7f8e4e9422382199c9ad67528f2d2c07ffebb1c1706b4cb7d62e7aa357de502297df184484a81b81011bf0d86863f3f17b995f37ee94fdeb6f447d54126cc6425514a70fc0d2344ea334bd172c2d1008b897b6efd0f4c3a7c9ff6557b65e7de7808f11198c0a9e855bd76c58351b484fe0c82008d7a24b63349053039e22935e9960a03a581b31705fe1661fed9e4edb5535f00887124c35a2a64ee0512f956e446feeadf9d6418fb597c826473c097f8606410462663fa2575547da5b6c54305a68951ff356f8cd6d78fbabe2c2f2246d2e42bd962273c863c2bbcf6127c5e776a8d020c185dabf85f40fc337a7a97e04b241973b301021d6f874394014f88c668e3816abfbb5d3b6d822204c7f4ea41d68c5510dd2e69542c8cbeb9f9375d2527b3461b39210c2fab5ef576d6acc254d465e4b29ff0053eb2b0290199c51e78b38d11696736610607713f05a71e7ffaf6ee97b869de92cc8e62fca1c7e3be166b1fef5995f02668ef9c33b147edbad32c7814f8341ed9130be2e8cb3bae474ee8bbded1a06d212cf122d26502968c6b628b09e0765dd89068225f3ca99a058c037102f7afe5af454f388939b9e2a69052e129e66a296f598e21b1f725002252fb6be2fe06431ca360ce97ef555ad3ffba8bfd11b46fcb4c23e67dfb0ac7b148007bf955942a265f3e636110103f7fbe210aca9d2d4fc6a428a77d6aeb8c5aa9ec071cb4dd73622ead69bf25d0eed2b46be73603c479ce3d85facaff58516faf45ea4b63f289d6c86819c1105bb079594a5ec6f5fe0359e58bc7e5698866c7d8b3740d72af7bc3fb16feeceae1587f8c92af1cc29f21e1b5f96971a2a5659a3d88743f4278c4e627cccda348a79f0d1a88595fb38d02baee8fb5b59dd635a56a0834167c27b51f054e3940700542c6308963a4ac84a7a4cec6287fd25c0e2f7886795d1202070d0925225862436ff212bdbfeb2f19c71ad0d045f3f6f2f4b5b9e00e4a82dd2ce8701500e0efb7f735f39f0a0d4983c8c5d24cdc8b7c094022ba412985690cfb6bef5f1e9c8697b4e88cd69faab84336f518fe7551f0cc50af4b0fe2121d396c0c687a2944fcf54f9ab9e1a27e652563714fd0241601522ad583f72f907c201083b4553c472ec12234c6271197a447afd8e48a7c80c547ef58268827d11ed3e798f8812adb5a51977b8191440db789ad4199d5d1837f6aa5c83dd08ef8ea5ddacf7e43420a8bbc0c36b01a208660a7bf685a33b5251370159f9b44659b863b035e404014518b09b669c67a7b428b6bfc33b468bc423fd151b2665183b550a97b67d6cc5abee057e5f3285d02a35b7bc6674be009f8851c6c083d543f2c594a6681565409c19408d4acc0a88061701f8621f66f9bb564916f2d8b73d2d43668c80c265f481523e38b5fc3207e46466626bb8d0d31079c9bdad95e251e3a6cdafbd3f6374c07dcc1ff4d2895851201d117b2785b951961a650dcb889b6555079c267f95e2fbdfae872f5ee1d9fc57b7d155be1b9e03840835b9c42486d9441ae712b76910c53d122b6b2b351201d8e290c480276a98048e0eed1f3528b54de91ad58a4263887ce85f126b2002f30bdbe0d075c09d4c384fb8b902fd695874080015a0ef06fdbee0d1018887ddb3fd59c2e5120825313933e323a1c1183f7a129d8fe83ad55e407ec1f27013fe11a8f8e348c3bd8f98485e871720d2bd68ce0592527770e3fb52c7cc82ff816752529b55dbaea43daf7848f19563e66f413070b78156b4179ed16f35de20012e39dc3ecbc4639707", "63", 1, -463206251, "3b40fb9e34acf202596e6a7f5ca6da6407bde156632b95e92d323d87e3bde71b"], - ["37593337049f3f73bbfd2d134ba232577ae99cf4b9e92407e040af46399211880d419eb0490000000007ac65ac536aac65ffffffff6f73124ad88d0475d7e839a56b273ac67df92632f6ffa4b584041a1f37d05374000000000963005165ab0065ab53842e026a383832e24ac1eb533c20f4d2a6e68950fe4c885a936069a3d18f07e60e5ae136010000000851ac535352ac63635a5fa97c756e68b563677a86d5db075503605d92566851dc05b9e6d0a8eab0c5867347050200000009635253ab6a00abab51ffffffff0464adc1010000000005635353acac3a21da0000000000060065535153525ed28a050000000007ac6a53ab5263acf0a5b30500000000065165656365ab1edd70a2020000000000000000a99d640400000000e3e07180a02f2a2100cc04def7a4b23032651f45129b460a901dc2ad29602f26ceeee7dd027efdf2d180f10639bb539004f315f8204518cd287043040641f67e1e118a8d292917152e41f4a863568fb4ecc3e8a9c827a34868d6ede8aa4a885a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b65fbbdbacad3b4856ba2b3c0e8be0de535db73cc658955e789f1e350ae3c28ff3e3866156de3dd4c32e736e1836db2f9f89fb909ca36f89809c9bf8dadc4846b4a6e2c24b0e08b6d68efd6d985390b2505bcce189d97a4fe39ebdef72910c0e6b204a5a28e1fa596758a0831a933149a63f4c9d612a2540f3eed23ec2e1977a020c183286501d6389d4d89dde71d49cc72a286a899076068cb70837f38962c5b2032fa4dbb137365409efe648aca4e0698d448ca9d336f11ca18f207811ec8c54770b2b03fbd3e4be908982a932910c70400e6ce91de3c4d0471b61c2550ac75af6270eaa137a0de5d444f73708e7dc5da53efc93f59c9c706bf31292d650de4546e4032ce1602c2f9197a721c44d17ba077f1e5fbd3eba0682759521515d3570b1ec17030af99c5d869ba6eee909b365fcfb106c1850e2e6c2c90745bfb44d522a47989b03123cfdfff31ac3815d2b8de408dead8d8a99f8fec37e3c194aeea597996c1825031b1ef34e5e07f0b37e9f90e0d44ab00c56c68627c1008a1536b15e0d674d77510210565902bbbbf4650aceca6bdc75be916a498eb6bccf13f4c8d4ba5107762c11cb30bfa981aaadac34d6dd406976e9708f46744d509185f147371ef580187b933cc49e3ccc5024bbb7523f9ca2efe560cebaef47f976a36dbcce7618a4509a07f21fdf9de57ce230bcd6014cc502701b8c4b8bb5709f36dcc0a5d290380d9815676576a5126443ffac2508b1b506419d05840e5ef9bf738d45dca4611d2561703a9a16ab6ec53bf708360b2aebc95207edbb015564934db46be8f9355b7dc77d9bc901a827e0a1a2628067388cafed772fb2143be11e9d365f9583cb5eb20c33a2ab3a08f4b6cd991a62f6be6bbb7d1aca0dc05b0e0fd3dd8cb18a1d3ba023d030aceb4db82ab144da0215e0451b2160873a0d899957c3b3949f2c847ff1cbc3eef206fecf1ccb96d68a765153354816696138b1b3c972d0311b4a255bb1fd1d833e98c34016f3c42c4ba9c567fc46a372b2e5677eb346f4ebb514adf3996ce2880ee170ac649eeee4419cab9d71e0f3f537fba7447aa2b6906f3cdc695db28d3ca3afe27864e95d7b3718935555590726a4d46ec2678a95bb6d5f05ddd424e33875e3a58ff37f8413d332acdf2c61c7cd042fa32dc2bac5b5b020cbe1258f1a46e5f14dfe44eecaf7fb2006772b8c2e95c4276e6fda4db82a819c9a6101dd392876cf1761a821e56fd40a77408ffa525f9eaa07549ad87e2d6c1594ffd1659f51a8d3500c1c947a22b5ac967218f413b634aab7494bc714201d73b454267288c07cc13ab66eda57ec8891c53b42bbed1a4925412af743df2cd21e0c0717ca17f94c41d316687d72fcea877d4a14870409f66573a76d243f0645c4828cbcc2ea31e720373dfcaf3366e4f6e422b4319e3e7abb9495053ab13a06af724321f9770e9fb037461643d8cdd44c419a41c07e53eb73546490786f09a705d1a8bbcea320860de854b89ff3e03d30d226cbbc6fc300c0783b253c2af6a2e790d616f3253bef152ff6f4ad974958c34df23544ab820d10136796bfb15ef2b5871caaaf390a7fff5d8df6f61d8ec6775f3344701e19ff380275ff5517a632da4ed7ff34e55b695492f7f0b781eb4cf5c7d8fd3ed6280d320ccaf0b68788a2ed58820cffcf30f68b6ce444401026cb6bfdb868bbbd1ea26bf10464f16410f7776a0d065cd216d033dc7773dfd27ffe08f26a89d271c804187558f19cbf5c74ef87c681092bfb1bc907144cf542db425c54e9563e9154cc2126300d1b14b79bbf63ce65b26d02745f13e6b08c6f1de9902ee9835384e099f4bbf3f891f31e730faccb77bb106686be22cb5a045391c98cd9f54160b2860fbe06ffce33e33bf5e7af66dafde6203071692062056972cf8179bb6a5700aabd6deccc09ee420cabc7fa3b85bda7d26d1a8c1e7f549044c483e24b279d43b6be780f817081d3d69d1bf73fca31c148e1e2b436deebdf56e3d39abe743c705580794bbf2bb9cfab2adc850dc188a9e02c4b4bda78c983930baa17b545f38d476890caf9eaeaad6f47f6a0a80da9d72eb98bac6fe24dfdc27592b9be09e15edaadc5943cdcec45ca8641d33647ce6e90e9a07f4f7c1628fda2adc9d7c66c34f79ab8a077fafbc0b443cb557b5938c980e4ccd021bae3991302366459d4f707d37f78e7bd452636a0aa7293b8893ac1b925f3779d6c9fe57f248231406fe0627e0e5eee3ddf62a4f8d43ea9a4acf1c6961dc9f402a4e4c92e85da4ca9f7ad31027bce5c280200000000000000000000000040c2fc84a7e302acdefa4a63bf58adf873b5c71c5f9fd14221b51b6a651c81ecd57b62a8691beecb825f72b0076a5710d036d56837b4821c13c19bcf851faae29c47c003dbc2844bc335537aa8d7cb63db727220e4b7fb8d3353196a450ce98b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008891048355b2181f300b8b1ae0ad4a1751527689561919395be55369f4711ed98400db85c219199dd8663c155858c2248494d93172274c3cd41365a768cf3cffe6150b5e0c89413b83c64c43a1c59206fdb58a0dd670378b5b0fa7ac1f116f3a5ad2af148e3e01abb90aa6159e9da0096f44be3c9ff605b3fcfc42f0a9cc98f5022465b1107194f95f4c9b1842294654033e5f850427639362d51d9e34900d8af6030ebca3426d6ecbd65ece144ea9be87eed00d77297b708aaae65bfb91af67bdd70b24c7c9921f4234fffe1ed25e33233914640742d102de0fea6c36fe69687f97b02d46eab7902a9c706b3f3b8018f2727d757db74e3b03e2a748a34097700d431d0322add47b95f1e6b6b798b194a01e57a10659d37337842185ccd3873713f69898021efd361b654f0aa8e18076bf74e94d476ed2fabe5df69c1e67a07af78977674c03109c6c6bab5ace647c5809ac3f9f22b5ac0eced14ac1a10e23c800ebc096f766020ab114147a25ae077a387c3a58b41227326ba9ce2f4b6f036e40f0cac2da3a230315db973f4fe651f1d7cdafd438fa55c0f8522769cc15677a17e15130bd194ec3fd9803f805e045221c238a18d888ee31f0cc9126852326d9e24f5061b67cd0620fa9c0ebe53243928db6efd392a33f86f1b95b525c9d69ca394f3789804ae352a5f51afb67b87169cbbc54f0495154e1fdebbcc1080c53e8f840e07c12dd13b9da547f4ec92c20f5be0cdacc9bbea4999356db43bad00cee28dad71ef636cca9f987daff5ccea86e9babae2fe5022e97c086d570a3638eeecb9219579dfb2268db3243e9917ecb412bf2e82a43d1629fed6f663f784d29a26f91890aca825f751a8c286562fe248f2c5b299d607d21002bd17789bec17afe932b1c35c896a8c930d53f71795fb768facb4f25d07630b9ac46f5605b513a60c9e69b875eb46343b05add674538b0e5f4253fb4d1e533fad3f752316fa3ac309de2f7f48e381c5c0dc6940528868a9d4b66121d51d4d1869ee3825061ea3033f1a824552b81edb797590d91422792c67725a069fcda9e88d377265da6385793488e27d630c44c762237c52a185d779df2980b69d937e93aae239710640270f6a2dedd8acb175250761fba3e458c3a21440e96838f79f1ff64b95bd7536db692a2fbf2c448a044388e91209352d89db6a7b3dd8c0bbfad578b2e33d6c9f1767861133776d88aae18e773c02369c3429f735ef26a338901749a47a3d5b14bc1c461be3fb0483fc2f5904bfd8c47185d59e7dee4dba10bb1b9e48cc1f5f73351bde3a5d0e0cf255ffb739579ff02ebe332f691c91ffe051465b7639e5e10bc75abd51b84f6a0b62a5d5e75b635adb14ddd4bc739254d551addd2999c299be48c4cf0d3c2b2024e24a40c7fc4696c85cec4ecf97a6467abdcfedcc56ad4aa03f4bca2ac0fd210698a5b33169fe1bc0abebbf6bd3299a1b467b7838cd141557d119894d0ca306212b25690d47e4b52b4d490206eff22298400bac499a8442b5704e44fd7a1b862f3fda71fb02432a3aeacb1a360cfb4805cda0982b0a864ed62d839d50867807f56ba170f91819002768e889481e955f093d78eb107c7ee1cb3f5ab967d8913082b9d6a0f99bc69511130f33c68a0c432d01e5d2399338ba7cf1af627cb72856d6c680954e0f1afe4c6e8c165bf96926dbc21b990ecfc7e0bc6d117940c9f382fc8fc2f9d6125af334e0ffa0b313232b6fae78d681c47d71bdf1c48a91c2a7cf1412c4d2f589e0827ca75b46e5d9d6492a757db59081d840392878282eafe566e56076e70c2aa3603e501be92fb3b76c84d1ff3f744b0faef1a1dce192e53bd56d3f7f1a02e62a0949758369b19eb2cf0513993d58d3baf3224d1d63aceff5dcd1039082f6f4b67d16263efe34881423e0cab2c66690faa4b8b695c90eb67db92d0161ce569b027cc768282f942dc94884da0dc40bc295574ff0cb5394552c697b3300835881724aab997672370015fc6464a1550fcda7e9e68c7e91fd77963d6cd9a96593deffa11d00b8437e69edb6818a4a0f7c27e4d2249de6530aca0f27c318691b9835e4c90a71ab661115a38f173c0ef80993a584b1d0db30dd7ab33b0e87a3b42a0db777bd07becd3265ed7c76d2a4b7e5f9fbddac2b7f74735f77df5cabb12ec7b8580dc47235f6e4138e5e96f154a6c4c41f3f0cca6399c2948339db6ae797d9f27168dfb6cb38fe70782a4833e7c7e68e827fa64c88f50d63ad8380668c72e711baf6fcd9f0cd58fdd3a5037d0138420c4dd3bd67bae1b85c1b4d7f002f57b37f60d903cc3877435b90f47e0609b31200b70f4569e12be35a348a466ec2f1003f5a6cef50e85dc52b3370fa02e8e400efb16c1247f8c4deea76599c5a69311af74f580131339ba513d670ef86aa96308", "52655352", 1, 193514695, "d2ab576e60f56cf85cda927ce1f565863ae136b314d41022209c7b3621cc5a74"], - ["6ccbe433045a7fceb9ddbf143c05186a03bfdd8cc5b460c314604fca892733128ca4f7a79003000000007254d76a6d231ccffc50447abf0351c54c7eb665915eafad0e2d1a8e28bda72dfc1546800000000009ac656aac525252ac5117b6b777332a834080b3dd85a98d880263f158d51121dfcd30e9a6eed365c142cb0145bb03000000066363536a5200ffffffff0220f7a0cc48e62ae55eee286a81e40c0599561ca806d3d8c0b1668e6507be6a030000000765acab535353ab1b8effe502d7a1a9020000000000ed5d2d050000000007ac006551ac006355707944030000000000000000beec1d0300000000e4f832e401288858a059623f487c96ec2319752b85019620db7fc99b8ddfbed372edca9be416b16b03b34eacfac455997f64a1f5efc6c6267a254b08cbaa43b04dd50c4aaffa749c537a14891a0f9f20f944eae58193e32c99e928a1befc4cd30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000092d19aa84d1efb35a46b4e1bd44c5b183240006219886a36712c601cd8aa0d652d7b1205361a9a750929cfaa23d733aa6a3e19b7020d33dddb15d3aad4ff8e06824ddc07d0fed5295d5cb0416f271e77c6237da66d1b1ac713d3fb4c896cf53e5841701d3339d18ce8539716a2929ec0354f3f28e93a3484c0d6fc4dd2d90e290303d9cbd3ddd560629d8bb9011a3e5d6b1b6868b962b9134b04c845bcf5c0d983022231822fd1f9cd702328e7d0c5ab7c286fdfd53d31b5da04afe8d70459a299310b137a4b21107478cef8a6fc0a069069d0128ff64d07fdcc42066a0b12a10ad36213afc7e26121fcf3b2e9cb42f686e190b21e409e79a923f37c47e84e29a7b7cc02071536aa0d13397d819a1f8c4c0340f8e9892a7177ffa53fde9dc8d658f22dcf021de00c0919b9ef9489b33ee1b5ebbaeba9f963f00b2075b8d561d7d25c20620e030e7561387a94b3ef5d5840a0e38dc0bfb467986916234f42293e558a6213119f0323510149cb76fd2a12cc4fba0f0d0e89bd45bd895c621250587decee2fd0db42030c8ed51aed874e674413409c0e0f363c5cf5bd3ee99b375d7830aee4eb675b729a15e5b6247986440a7a16ab76ca5371e034a2433cafb92d25b11c87a219987a049e3237f76610d3855d6e761ccce7495b2c1df8fc95626d33f08439d7091fcd12e96b31c5774890a8ecb35a4f39fb3016c0f46b4762206e6a30c5e83c671fdec2dad21c079d8be9502d466efd7e37cc7604e4f423924ed77a745f6f4a3036a4cd0558fcee6e645e6232d33156a94f22f6b2b6ce8756f67a67d83e337b64e6dd689939a240c29e5b7329d29f75c497faf07da599167c2f46b1ed6224a6afa588072e82e3a66a6ead96429e6138ea7e92c0a820fb4cca4b2ed95b2139d175d09c93229d22a77936071c298a5eb0d69e8843bdee51d733c275e1e08c3c51abc8510830a645f0419f3e05d9502fb3ef4d78486781ad21ef24c5c6e30c99c4ba4036b0294f27d6283e37acbf26dccccc83ac7a6213937e4cd05db68ef3eb372da7be5c94698b702ba7541f0c57cbd36a8dbfdacb762510f76e710e7fb125be7a646dc47e3ac03339984ea625befc2cf3704405411f2290781ed8abdfc77907d65e0816947e1b3e5856f183ea6f7fc3b573ee3ba3eaf5e077dc3acf8b47c03e1338667f77ba2a717693168b78df59420dda27d166be8b871687eab27246518a4534f850749217c9b20ef2a0008ca4bdf232a9a5a2561575a91740875c73112a31bbe0b784e81b3b97cd2b3fdf7974a80404bb928f4df08435fe56a98de493ef938b7446b0323eef737a171269b9047bdb6d58ee898203c65f89a84570bcc416b27388998a2277bdb5469917b5e0cb20891b4b7e4bd1b8902a23ca6967b188a6ed3501abd332a96dc7aa1fc073ea79dff631465ba2378f0c6492541d132632ef11af8379ac927db79109562d413bc7c90cac060a1213082a389a4d29aae6b06983584f453459860ebf0b6981161cb92c9155f8e7fd3c15602a6b32db5795645951765234fccc876a4f056514c0949e51cef0b11716a61fc213f9e4dd57c3778315677a7f6e3d38837c46242fa89c9ba2f8e7f206f5e6dff078d9c04d3da6de01139ee065aecb28569167d2b74d16dd6b11cf0faa99f20b5490612765cefd52ed75457d11d41ba9846c6007974a527b0a9ea9ef5e600bfb75ce38e203fddd6a053531d600b6f5c73fb40b83e82420c9c405f0d65f9df8b77e44be3da895b113c6f2b4052a59dc55bcbad4da05486ed9384fb3698c8d8a34ca4fdf6f7e27a6745beb045bfeba30626a16af6184d0bacde53d5a65fda8832ade85548df7b5204052778b44431ce51be878acea27ede1ff6dc24799f478ee64b7312b8231860410c3a407cd96c1581ae12233f204a96371ef1f1f59cd38fac7fd46ee75afbe649628e698e7dd4b584dc0da0f15959a63991222f064fe9884bbeb2202ba8752ed1659097d20a799021198c657c480d82b31c433a991d8aeb889f464a227817cd75a4482ade76de9580cb05bbc448ed62cf07a3bccf7313d4ccc30212133dcfdb53ef6d3fcdc0a8cb2f4d264af5a81f76178b7d08b4302085b7f3b7aab74b619ddc20989cc6d4bffa829767911a9d630f0dba4c371dffb27437db4d6913e99b89a3bb7261d171e5756813e6503bed209b70129cb5a5ba34c9d8736d8a1e668ec01972994ad9abfb1c4fd274c2ed9174cd9a906ece146b92274e91c61affa098d32d748474e249664224458a42dc87be74b7d7fa6c3fb6898f366f305000000000000000000000000b04904247249aa08c7613006d15e8bdd31ef7bfc5d28800ee6c0940895e90c208d69bed91a1c7c19a867909eb29760578bc9e35cdad86feabeb7b8578c69b3ebae5306af519a01637b69c82b5e118211293997b04e061f853ea652afa18256d200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000279f0b68d197ba0ff60e7d0b20ef52c1229cea2fd8be73bd7b26d1d008d9613718fb43399a99dbd93265df37adfb1a59090f40564475fc5b463b01608648b33a536b14c395eb4e5fd1493353229c7157e114f1f5d9420f7181d0da958dc71d199538285f7934f192017bbba8893b0830eefc15b265a20f54a536d67ff1a0adde0223db0b564c7ce074f4464323472efb9693a8e1b28e17dd941c64346ec8b35fde030a8cce16e6bda43966d92281c65fa4b849199ce58e891729634b55dac6940f5c0b0cba47b8c7d4382122e2403b5f5bba09ce91efc71333fbed4935206150a2fba62fe9af1f3882e03e19ad6d2a425e17df9478c956b8914b06a2378bb24a53d8880328411dfb2eeab2ad4e34a77657f10d9a29207326b68fc3cb34a57ddb8a7e5e420225bfaac50057a68f7e08a6f7742235313bf986bc29f816204eac5bbe297ddd7c0320fad1c4f553beaee0d551af54b13336b8bf911abe9b83dc1d235a4885c54d410304fe0596dd2b070fdfc38febbd59e83290dca749c52907916711b0d8b3b01d5803230cb9ef2888168434a49ebd4c554ba7c0579131af1044a8e181359fe55be78c533154af61268efda3b96fd6b741ebfad88844641ebd0a5b9e9ef2413701d34bafa695254f9b739819747152c7a59dae37b659cd964092cf50bc4f993134ea17b26a1ecdf08ff83e8d1b8b3fa2dfc08e7b0bd2468cdf57a7f4b1c5efc145dc2297b3bc1b232187864cf790e6988046c2afb6fcffd783a4034487a7fe96a11a8e156607f1dd557a59c1948d2555349aa7b7a625500a0e10f3fa5881ee16146bfdbee6cdb06ba265153793ebc1685c60ad7da65e6c0c8cb29ea4d0662ca84c93a60a59cc5fc60fb4d8818b06b82d65a6282c9a08b6d6fc9752aecd6db12d792c6cdb72628e16913d493926f3764ef5b4109d4807e766d88af5e66ba3dc7b8aa6c801b02d38e09ef0eb0fdce8cf5f221d8c3f1d7ad28ae0bd97b2a99a6a7703f4783c42ca104af37b720c5bb379965b86d844da0cb46fe55c38484926f79d88eeefc9f2a5c066dc333f8f5db7b1eb8e9c75f33df6ba7b56803eec88ef7d4a9f2d025433de33204edf3a97f15e7a8dd02e322567bbbd5a6142d39aa79cf41e3a88334dcf36763184325f0b74fce3b5722e7d5e26132765f1ca49685064ce9216708630df4d862dbd7225333fefc2341d5ae0ad67b1155d06c1d440a672bb6c9ee7da585bf4c182f68e4f4d0cc9beed090c692c089ed5526ba1c0a7bd0a78b21bd3c0d7464c458ba414271342fc8df70d37df26e8930e9fa6d2118b11d8e111d32c51058d7f10b5e721346e025f00f96b0a6c87825d6d36b84b2c65e84316b2f3564aa216f3e63d191a1b09521d004aefb34a74524c631a498ff5f8f98532496bceb629d1df6b7275a90e1da891e6d9da9008c388ff5e97d9794fdc50f861a78e0bd0c37d60a620b7e6606bb6715e46838534a9404c5d1683a6ab4b1afc1066c649265a8c927c3804a904308aec8b08f9deb8b82349b67d40c8e468679279a3d723ab4e8786d0a2a2394b6d64022d205202a921b295167fca48b3fe4ffa6ac756f2f3842a4e58b149509ac3d02842652b310bf85442ca2829781e0c9aab8613d4cdfe7b979592963f6a38741d69af09c174fcd4cadec7ce758ec5ccbe6dbf2d354eaecf3ea44c9c6ef3c17d92831385d8797783dee56a40fd8428debfcf8c8ee8fc00a3facc1e270aeee39a1f1cdb618b4e105656810a8cd8a8c76a277668a1c3d239e104d9845e516582df6ed6e07517cfe55f8c9283710667a313d25d590174e0b6ffa6781886c619926d816eff95639adfabf2f68b8018273b941c8b12c36ec9d8ddc9c933c6dc95f968cc0d72d4eb598ffa8ddb5a0af5db26c6039cbb8dfbde9f87d3a51a870dd32da02ee610013df99530bbeb3513a895c2667cf30ec246e3b0ea60579422bf764222db8740987b5aed1a1d60634555b105bc51f7ae54df1f8d1bdd85bfddf742b37f1fab41e0be4eda401b69443aa0b540193c479c3abc7248a01ebd4137b0236e3f94da638c8356a8eacea8adde5d3ee690b952f4a57a3db55fec4cbb84c555c354c5e2a6ee4878ea8f3b13820bcaaa2f1efb8210330c031adb7a3870ff88008418980924ec9150b810eec3f958598e9fe6a56edf02cd888db73cbda739ab2fe686853f020f95b8c4e72516c1fd30e9f30930244934081493cf41cd195eeadcd0e61bb24e90b8e92ec03bd3915aff23a50567300875e26b805ef56de0d9531a427ba6c4053ff43c3f615800000000000000004b81c302000000000b77dd84f5668c964d4fe819a774914ff883a57622754350ec19e26596beaa9847e03b0ffa0c74fb0c3ccd4924dca72ea94d45b5c8ccfc8355cd5d236e1e62630331c1947cba8e5abce447c6e5ca7e1fd0508051356bce656bd2b7f3d509255f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cb3fef8efcb862fbc9042fe6e9ae67b3cdae6ab8af8b301715ce990048e3393da46304d41a4d244c7b2d1db6561688314438768393a7bc0563334cbed2a1ed9fb14a4b9c62bea43d2954b971fbd968b4e44cfb6c9ff2066c8acfba94d81956ebfae728507d2218913c1f5569c8bf8f2a971747303b597bdd3a93f72862ad5d8802152792f2ab4955bb763934fda5f510fb3ee9f8242caecbb950efdbb2811ba5460305bbfa151adc2b7763ac5a1d32c26014d62ffaa8bd8d57b9999fd2fbf25a17a40b0e1248b20ca0aa6d978de31c94cf956012b806fcbd03f5bf8515b40173944aef1c34bc221544785badb7168f0efb3f8074516381c6e2f9bf57d061f8f1bd1d530312d03dacf77b1c8e621c6f29b0189781e03b4dbdd609cb58f50474cbf4718b510220962f167a29a8e4524c08e71fe51bf2c601cca481ff5106ba81a58b8af1dd4f030604a2856146c27511a063f1ff28132b0258f0e6b57519997af61c9c33c451b9020bf3e40c6834c865ea297613d70ba8b165f8ff679aafdc3f4a8b28743dba14490213c901d5c80dfe0f5efe1c3c5aff7ba95dd11f9887299a9d42852fa487921e9e3eff59cf17adb175647399e599c59b77c843687fa41a54feae74846d0829036ed98feb8083cf5d3161a5a09aa7787dd8c2496f33e1b12fd4fabeb769f3a940c8e3c59cf98e3ffb1881d7850b62880c127d1794e08226f7edd5179a96a8cee92d24dbc9ab4783b8a0ec29161784d1a92d13cf3c28eaf348715f5a9175a9d59faf48e20a4941a7f6b179a802bc14ec5b9b219dcf7d6d3263cac702bcb86c97a94b95b9cabbce6933081108f76b3ef53583d391efe239e910943303b18ef3c55a87677415a0bc2d103e20d805de0bd5ad97819d7b6e4fb94c8ee67cbb1c95c027780433841d344389d24a92c3b7471aee5a7c5675b788dcddf9834f9e707639e2825f080d23cf5555b8cd6e47e35a1cd832591960f941e17182a505fdbf7b50c5ec073695aac0fd132b0f05ea14acb730757ca9fdd9937a05317fc51e21d72a2721cc30f8348f65e7ae4e277d071df1a1176afec1cc0a97ad16f14ea8b38228fc9812390a6d0e254663fe4a6269f115a1c2b82b8c35414cd2a820d19820b69e455965c648f0fc3495df63e888da924c1033bbe09868b7ce3def8aca866c1ebc8296fb37a922f9f742bfca725d674c19959953a071828fac1d21c838ca963aee8fb212d59be30c4570f1f282fa109fe2144b597007fdc462134c3967c0fdc2bf5ef1fdf648da77b4acad6d692fec7111af419e90e24ff80db89fa2805dba7821aff542568fda005fd9e7c26339339cd6dc9f8a693ad31b8ef0d051335a2f00ee48a7a708a8c3093887d9b2c27645964c9a307bd05ff8a1ac66a959ed8b6459f1873d22e6e616c30262ea1eee66145352b290d16eff8be1148aef66d98e1aa1e76f5f5772964bd77b4e619d6d1079d4ac7791b206cd28a6901581b0969cefb5942c9a54558fdb93193737d9a9747c5e1b35c5193af4ef7912065972f50b1ff8560b41e98453d87ec584cb7e0c2c33c5e746589e3db546884ff21fa2ebb5cd06853fa2a7121c50a3d8fc1e7a1a36697d0e9bbf1542675b771952fd74d19fa2e9790d7c3a34f0c28e41e5ceb9fd612011f7d6fac9b4fd63f7c8b40bf8881871f52be5faf94f56934039b8ecaa0c7aab510fe98d36c9a84e4ad4e3da6d7c88b5911c288d9c54021c022b3ee94fdf713f67d0cf85d30e53851db69f0fedffa640cf9ba685d13fe6e3baa8768698462569c1a22fcb125a9fe15072c68eea7ef1a12ea4c532fef5def27dfd761b5a7cc60a2faddec044f8112dea6a10f394bbbe4e5a57942fcd0bac69995294be55268f9498c7b3458089da9e5985b9f18e7ae956af44348e0755de7ab949f53c5432aa0ef366e070f01746df9d570f30bdbcfd7aee470519a2d8e6e406a19199888d0bd408b3877e92f64d71f9762e206ce3662370e877a21c9180b2be2112756aa9e03583ba96ccb23cc282a79d713c4c943108af16b4bd2913fa9faaf0951de683b582aa1c69462b527816dc060bdf9617132a101d830988146e148289f7e98e5ff0b48a40ed06423a86330efd1d5749ca13a558b02980e1198af71dead30402568f2a9abe6ae31d2456b9927946dfaabcc8e6f05f2c6875a9c0647ee84a25ef34d98859be4f5888b59f4f7e653bde0f2d0479c157f54e897b1bae1b41e75fe14f2996d838007b454581276c0784d8202dd0792b4f6ce928e67a4cee25d009c867bc14cba60c94812fb8f3b70635765ada95583dcb3638486467d9f75e8e4b1f352f797ea93b188d370e9466ed762ee08bf9ce8b0022fd2c5e2c0c7c5ec39add17df71f94beea32b9794e339c4bd2a8ba2c051d75382dabf43bf2d6adc82719749fc5cb10fae0dc405", "53006aac6551ac", 2, 764040020, "860078a091ceb86631fe27af994b2834a2a3ae59e5b52f2ef6d03fedd325b033"], - ["27da4dff02f423c474092a53ad9f05fdb225c95f4b02b2e64c5ce4ff85467c8b9a02cea5f9010000000600650051ab65ffffffff89f0b1b24fab59be2e13a9061a549563f1f5df69ac8ec858e57b6583db18d993030000000653ac53655165ffffffff01857dd005000000000765ac00655300abc854e59b", "6351", 1, -1820082850, "d72c2054b239833edc790cbfe1fdf3a4b48368324a71db39e962cb5e7145caca"], - ["e5302daa03355eed6a57ed68518f54743b4e84e7479b8cdf444d21c52bdd440a8dfa2dc33102000000005a65ec26bf969f0ca919a357126f6402284dc17e7a16dce7afd2029a8f65e528d745d32e02000000065153ab516a519626c525a773555c4adb42abb167f4102f2e8181941b74a8e22d3413fc80f033333f1d4e02000000020053ffffffff0264d9da000000000008ab52635252006a510b9f7300000000000853ab5151006a63ab00000000", "65655365", 0, 952444113, "db309a465426d9cb23473c3b885a239172ff64e8d416f6c606c7883f53967081"], - ["22091a4a040f67f9421bf3396162711a367b44e920660f6d7fd4235433e7c9eb10102d49790300000000ffffffff9c5f2ebf6d99e19c990c40b131539cb0e0dc191ea5574be4b29152f9dd9956e5020000000965abac526353536551e2268e94dd9d8728549059f65def0ad15bb3786a78436a59c33c4e76b838971803a042490300000004ab6a63ab237d50adcb9c05c4c9d75436b9da2bef76aef5b4cfad9fb8ad8225f8ee37860a7d684ee6000000000165ffffffff0483819d0400000000016ac5a8e301000000000452525365f5329d020000000001513922ca0400000000096563ac5165005153abc3f83f70017806f70200000000000000000000000059b79cc8a9c45921a1f2cdec0fea8cad52c2854299923a09bc265a129ddd0c475cf1832f17ec7dbf827bcd45a6264545b4abc2b2e28efa779f171a42950d4e4e5b0372fe0a12094c6dcdb5832cddc64b166e2723f354afadf4e4bab7b0bb7c7e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009921ff6a4437a2f0485d4d6d29b21cb49679b22ef35062e7981584acf47f526460062db2c3bf5f33f6e14b72063e0891177d44af25eb28df31bdb8503caa71aa2fc4f22464f597a0e5346ffadbb008554703634ded71b562a1afba8c278faee6245486d98df6a1431764e6b727b54fa9c3dbc3b3f822b1ed8c1f0589edbc88f0032e02222713dc36fe593685cabc79c4dc4e723cbe185c841eb18e349446bb22a10310f96e21b50cc4112349eadfe7facc19813b98ee7ce5d655ae7f9fc9424b31580b1e5d440461c7c62516987c6e8c1917bbc1d9f3550cae9fe5a623978b0b457fef26fe6fea20a58934eed54c1444813d58dd21d5b18c1c4c6e1040c74f7579e5fd0222842cc37625a832847f2ce69756a4c806762b51f3ed0f89d4476cef71259362031b7a39b008d8b49fc660944a77d8438f6d19a7b1498c41e55d0bdd052c7d2c90031538a519e75c1e41998127bea7df9fddbd833deaa8ce1fa5834a4379b3fa04a20327d4dbe1ad6054fe2cda467bbfd0d1f8b18ddefa183ec65a15e1c65436123d71031c9b7559a2f5fac30b05818b1d83af39b5028e9294a9508a51dbd5930f714d0698b3a7366dc53abaf027dc3b88d382feb4dfe2f6323de4959f07fc9e41d3de3c2dc7e7d6de9e48dbdaa84ce7ab6efcb542145baab41eb25e4977462042d57c29cc18f098d1a7c07637670488d8872d51d64aad1b1e99c49dc57ba389f5582157158fcb2055e4196723e33f06740a3b1956ce7d27ee6298162ba66b09175f56d34c55a4a3712a96ee49bf03bfeb7326f2ef820198e15f06d03a4d082e7231a2986fbb0b1be54a95bc789224620a819afee7faeb06a046777b66195c9e670362cff1027f42bd76e278a88df5214d8023a56a6e117f38c4b53db020b04bfe0610054d771f459afbd97e4c742175ef30e98bb358d668dad08e9fb490d1f73d9ce47c1abd0ed2b6e9565ea9dab872ac1aecb28f470d74fb18746e38dfe46bbca21be272f0a50561bbf88268ff7407015c39ae8bed3768bd335c00f6097f70f7794b6bfd2b4fa0ad8401764df686de8876d25b369aa9e3db9370321d9c264bac518288c39982860890ad1cf292d82b0bb08140c400a2626702af190ad89e55088278ae48f88b060c12523b44ed3ae699cc2c871053cee023f05dd2d2696918587f61151047b88a5baa4904fe8da00767d96cc14cc3140b43e48df1b301cc96194e91f984e0a7a51667725f295389f62e256bd8b9cdfc2e75eb66b307e30868a4eedaacc4d2464f41cf9ba5995a50d0bb8c104a9744f2fc79671ae60a79cef2f99a9b9fb012846d0e21f593a1e9cd542e0ce7573b0b8316884354d4ddd8a54420bcbf732416029dcf9c81f76ca7c1e276389082f94e72b6be5fe2f4b1f1f2f57da71b98ceedfbc043d595468f1807e193e0e4f677a40f40ace34871a6d985e67f7792d812aa538c2f199431af75393b23d7b7d5eb53785c102dee8ae029443022f3774ca24df59de4406fd8da8badc40eaf7cbeb9043503e70f9ce18b4663f0a56bbe38790c846ee3ddc2329544d21361eaeaa24a6382a04077d51ccaa48ea343403b30264b5da6dd2f722216f44b2d1476bbad6149241c290644e8c854cd418c1a7b9d7e1e5e64906c40c6289b62f58527f3b4e66c78c72078b6255918d1f0c062cec41059f813ea29751ea130cec801d811144ca5529a162f26d102349ae82b6ba4f97b6cddda768ce1e31cd9cbfb901a7cc524ebf3fe8942a39e3a063553de681bc377675fd013195d6534d5c8607a7d9a4ae2c54ff86e1d543b3975b7f77e175a27f257525e48891b0202c8db7f95373fa3e9d0402f50269171045cd71871f8dcb4fd5d7cebb5b532b98d45f390f404b148b74c40ed55e51138fdde7df6f885a477eb9cd5eb098e0197a75dae137d81c5f65c4b0881f101b1f6eeb28eea957545e667e32179ba1336a4efb020d06e6e267c29a9caf4afe109fa81e5c86eb6adf11c6871a08e7246403b3d64dc2b1f2873b03e442d17bdb52a9a0bfa7f8c1fbb88a9c376cd0ec294b331462a3c34731f264629ca6562b2cb83479d586b842bfaca422b3170e5f79000b161c4cef851518b99b14302781b9bf2a08723c23ec57dfa6de0c1371f914ee31e6bede6f23cfea530c22584b8f3bdd8c930ceb123493140a58b23e6f9feab51547eb8b144deb29f1fa6c7f9037477108c9a7648e8f2dd0b95510364fabf25aa1a66c5ae1dd593afd766d47c99045f231a56208d9c5a647a5a5bee5c4f19f11fbb9a1bd19df71b9abbce69f4c3bea6571721ac0d96f6b1227b9e533cda50cf29a1bc74c837fc2d628ac50de0b9894d19f3e1703a88a32d1f714c31c57376a3a93eb3a850e64e85a1cbafe3725c4c01a86b3497549a9337fe296af24b469c95a86ddc32bc893a7dfe3bb20a", "6a63006363ac656551", 2, -1599338667, "66234f37805164833bb1c9c108421fa6f517526702a6a16b3532f9bb20c69f6d"], - ["bb56d62f02ea104eb6d7c6f14940ce125cb4f9d45d04b277237eb25408e24f63ba58b0ff7d010000000251519e66da96e3123a6b2e57986335680414a79a8c5a75e4f5800b9eca407e87da5cbbfebba10300000001abffffffff02eedc8f0200000000086a5353536565635136aa2505000000000152000000000200000000000000002928220400000000178355900605d3edc17ddee9abec0497711de8e539d530be5e1571437fb15e7bb754d33d0ddda624802dd5f26244bcc29c59bf055e700c0a15669e76bbb6f87f80f2806a8a2236cce9adf1496646c6e4aed1971f7b4679e124aca085da8ec2e400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e89dc983cabf7b9ba160945a847aabaa4c16b31ea79ee9342fa5fedf29e202b2273f33f9790c22e54f2a83cae17185f38f50dd88b4918ca369ccf22711dfcef2c102078b995578d6ed7992cdf92f84fabe7dee8852ab07197aae85fde0f3d108254d21b7c9ee8ed40e381061dc14e561360cfc47dd190e5f8cda82701623407d0315d08ad9eea9d6a1fc3aacaeeda7e0b785677c667caebb700bb8740966c9305c03052208afb22c787e33f0064fe21a548687c1a9a710231ee85c48007526607e9a0b120cc47de72419b80707cf550f404783a3ec0cbaff61894900e58b2e750e03ae0570524a556fc497e68ef6a6361b537f4ce072b4e33839fd845e2b746616471202094053c18cc2ca995f2de812eeb039dc40674304147903d60a2641c8a4d4fb4e032e234cd7ca457ac4dd1947bf66a81c0d5fca1eb0fe08e1766600c7e2cc23c430022d99c6ca9715f87da5a73077a084cd6809f69c7de81ddf341031fa35b42f71850211d73c3c11f3aee04f8916214a02fc17ed74f7cd03887d76ea775b29f47c45ad03174287b05755f253b17d1bef7c7be9e69d5ca27c3530aaaf9dafb200a2b5af2fadf04805a7dff508c790ce84acc07b195986bc045dcaa128c16ade190bdca82fe7a21996068a28710cc259812f891ddb799403fc7a37a45a3a0f00d93352a3f5f9f6d86fe94ba6d273449f68bae828c6a11847f9f20bdf3f1efd0ce2b44e660df2386f88cdc1238970b59859e533719abc989a8826dfbdeb68231379bc74f3e98096c44eb33ce65708f44534d12684d35a8a9a68a2649f3c9d1540eebbbdc844d7c2ef750b2482810c7a1798fe5e83918de7366bd624b6c5e0603de992dbe6079326ccd0006c1a3b2a24695af025522650001ea0459ef576d7a268facb9a78bb9bed10a9ae8c48773f75406ec3ae6f97dd4f3be0d8f0934ff76702ec4b41ee5e571cae2f871129f00ced361aa7918c92f279aba43bcf0160bf7aab9c35202689eb1c7d2f55f44b11ca16901920d317669ee6af4ea1d02b688a2e7c5f518e6b6cc1c62c6a6a370d27425af5a52b04683c1c06dee2f51bfdb5605b1a76cbf3f8c0c6279cbf8f06942c05b64e4e71c063d06a02b20b736275303a4f7e27c8b589bc06d0d6fbc207b12717c345872cb1e567d550d5708df751f456e934b0c5985e023c48be9f112368072bc68716c565ec5d10608127c6da01c630620a7d8f84c4df549f6b40098992b28315faa3b8935ec84d99ab1be54e08e092398b550950568c3999e99f18d4d0a820a3650c5bafecdde7e4c32bffa30335ebd43bd6b56ffec112a86353b1efe584ad16486ffc80d836f0e2d9c0fae5b5d678afd7026a7e408c4ec82b4e5eee167cf2c1689e785502c1cd8a400c6f25431b8b645d64d25c5b7622f56781b78d3292dd787dd2133e1fdf8bad12aad36c003d5e94bc555c3951f584d8fe8fad98885ec62eb534cd604a1e95d4d5b86e238c277e8ef34bbafa443e7969f7a857a656c6af6b3e009b494dca70fb108878db455359a8daa0fe3754b1306b2672028e87f7aecc3b70f0dcaaaca538e0cbf0712d9ea5300a56703bfb4c97a4ee0eabf8da22e6158fd10ca0a1f5ad6d3f902e248862a9835bfdbd6673ff5ba62f16760d1b8287495b773c36d9f57b6b2ae13fc7ddbfc5922153649c0c972eed93fd6bf36d7b76310eeae577fee9837cd4e221886731ef42c8c76dde62650794886ee000449ac57a423ad11df3f769b19298b0a310fd0a1d7caef026c5fd2334371effbda0c92d0404961a750076bedc7f1c69251212b290d50b7608ce6b70e1bab79f405d2ab2d23b647064f72edda79214fb358bfb78c45c483590904fa42b3cf656d4edb30b4103867ea714baff58140d9cef7d04d6edcc7bb2e79afd699239b47a4bb97625ba1df948627344f603bf68602faeaf6dcd71e4a14ce9355181accd8425008bdebc586ceb8049afa917db4cf9e81830aa699a4dbdcca6387cf343bc0ecf1e4e5fdc97c051e9225679e9a05805f2d7a1218cbe1ef8e297b6eaa30e05a50074337e73f6a8fd95a86a3f7f0d1d3c7d37a6f05a9db5c49cef4c6916dcc0300687015d0b5c3db64dfdcf0a03ca2f9172c71652f1d07215ffa454bd735c80ecaf7a9c147e2692b66291e40fad2bad7b9154024d973f4a99db2ca26ffba3f63e0caafe7a7d7c7b0b921ddd441a2ea9b3119abf728ceaa690cfbc911e456bddf8424d7c183044ebb13b32d6674a58452d4694d67488e80c639fc56b2b8991efdf7d883456730890e114e840df290000000000000000bf21b30400000000208397aebb773cdd6f47f06f7de3a8544b491ebb4482fa8ca5f95d85e0256088af901da8bedb34665b469b55bfad04129562dd296a46f232c126ad9961abfd8fa4e3d408d53e8b31df006ffd3fa3051b9a10cd0baf8f58fced65cb439c7008610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036a79d38a70496b1d9501f49eb15f6727a33de328b591b8d4aeaa3cb97dfd2599fe7a25f7c32ea737d0d94d88456c0804a87da85b4ba5e4f021dd404f36cb3a956f4ec87318dc2424bfa736c2f88c9b5293a742871dc832dc21c9f7fb565b0beff403d8182fe2915e929b95d54566f96c781bacf46f5206d95f3daaf0759bb4d0228d2d04bb161b3279f16be0b1ece1b665e1c40db4a5005387105fae9b93e11df0208e50afdfd56e0240b76d7a5448baa9fd57d1d848a8da90a5862980a1e7a4bfc0a2bd818e56104b4331d8ce231c869869b4f25b13d580cf1f972ca8ebac148120b164e74c894af101c3c588c6a63b7d545e79dc692eec2fa1080527bf0e3f3f2c1031a6e955c1ff98509d40deaa6be3b29324361e1c89d0a06465ed52e4d9f24339b0221369b33b403542cfd905ca36b1cb42567080ade8ab6651d6c9404545611d4d502235776c988dc08b5f1a591ad726f1f8937d86ee822ba03a96e4dc30a09e64d31022c293fc7134c648fd5894bd4ac9a78d007e26d8f849b3166887b03821a1e1dc003052ee06c4341f7710077a2902428cc47f00438f72ada5302d6a4172d267f716fa11c97418162bdfb57c57223a6117898a4a1be946393f6f681a0fa726281c5fc35c19ea725bc51b25a84604137dd8ee14fd5df075dd129a68738b640e76f1ec5f4c3cde89b42bce3eeb752e7fef13751e80bbc1a73bf1b6c9ee062ad6c1d4a7ba0a447a660e8f40a9a108a81ec22f4448785a113823e4af66ee337e9fb42fe81b2bab1cc01d68bedde140793fa8480559a85ff68953516222c7c39cc30d450f7bcdf5b22a621326e79ae63c7c36d4a22af0b936bd91f0e93a12b501468fd7fd617009d4532770c036dfa7279983cfa82c826c923dab21d6196e7e4fff527e0ffa7b6d4c26a239819445dde789ca0e6b3151f9e6794ec2692c0c90f907f13675e3f04f916bc961d533b23aa6541e08ed80e9f60cb1a057220800b38399ca1f3435caa84dd8f9761aed53367ce900f46a46bf9e88443f40a5194f93aa6a3bec2bf28be78e9e097dd0302cdf0165d89814da288cc87a4928c8ddb3edcbc28c3f4978750b6fdffbfa09b92137c6a6738db745c9a0c8d1c2685a5e601ca65b743e75e352851349c5bfcf88e056d2f64639b4b5038facee89944b1c735859767b4ec2c580a58f8a09e0a6ac73008c672533554c153a50d153f22f81c7daa58e24f80d0e2d088232b26074f5ae26d4d68852f369980759bc5f0c42bf79583f2dd15018d27d23de5662a46a3a14a0dc761a8c5582d027781a80ae332b6a0ab81402a5828d2ccd19258f498508855574d87dbeb06ab7915dcba152d4faf04ddbe4aee1eeb0f810b7c3fd02da44eaad67e99fa5e4ddd5157386246a9220cb254b13b1cfb6f3a61dc7f543dacfcb1222bb84416a7e286d84c9f8b7d5b342bd0818ab22c48e629090eebfd12dcdf3f59565f0ea39a23baa55343245295e32312f750135ba00056429b71864b25c4b88ba4aee571dc9571c1e63b1bb033c07f6f382c9e3122428752a4af3190dd165ef109fc0e4a58a8b2a677a70138b010095fe6775103d0a072dab96bef64ec0acf9845152994c0772d48e4d65bd6525c1828b85a41850a099c06a9a8970da0709d195dff39942cdb420cd5666fa814473f2fcf3068f15362aeb0173b1cac4f9915c4ca3a6fcd5e0ed1d55c53c9a848b8d4c71fdda2a12cd8c07e1a0d0d25eb5ee1beb66472650db49c451be1790c88ccf5dcdc4b42acaf75c44c2c0c3a7ac839a3b154f95248ec010c888c8366e91c90fd60920e183b00bd5bb2b32c0a6fbc82162e2396466dd30042cc6e142b701e4e521b54ce2069ceb31bfe76741bf3c86e45a930fa52badd1145fe692843ef146adfdf8d5f05eab38e95adb6d92756eacf4faa6c66c5010267809da11ddcddbd0df9a8c470fffdfcc306b89c647267561871413dc870b3ef6e98ea5fafb36cc858cb4719264f2be0c16429bc84a684ed98d05664a2a1284001d2840eb476d05ee2792955e65f6403245f6dcfddca3858d5300291b2a77617ba3100f657173403ffb87f8be1c078dd7cbd4a91adf6aec79e8ae1f12a5f06a23cab09e73052f13edf9ff9607fc596db701c944c76ad8ee0e334a41df032656a3c89a99b25d9032d7c635f52f990f8d61e26e82181803fa5bf3f1d389d9cbdf3ce5d7d9103e4fbd428db8e6665511a86d0a18b9c7d7a243f7cfb4d8039077c8fede97e80bd1ecc710ad31a45d703460c0d8f3962852760c22069eb7135624de414db1ce1b8be830baa903644595f9cf84600ce1a7026086a66e77114c822b8e3876a05c8ee0426798a3ce91ae8e0e91f3f25928dc3d8bf39d46a15a3d69ca0c10b86d3acca2bc3a7befe68580910c1b597376d915166b363c72aedaf1ed8de96b8ef0e", "", 1, -647169879, "071b4c25ce926612c9dbeafca7339980c7a95018cc520f62a383bdadbec0d5b5"], - ["d69d0dfc03609d9ad6c5a431683984ee79090b559e24e54302448cbf1074b466e9a89866370100000005006aab6363ffffffffa130aaa56174cc506166873b3f9ff9df978953217509a87eda6b3560beb9bd4b0300000000ffffffff5f50e2b6006f6c6c583fcbb4472bc7725016bf72cf4895a5c6982f3b39afb64d0200000007ab000063006351ffffffff0407997000000000000852ac5251535365514e530e0000000000045152536a1ffaa1010000000000ff8afe040000000004526aac538c71f3b5", "51ac0053", 2, -592689762, "a09d65fb9ff0cf00ff9c6f4eb2dfdcd250ec366f53a3dc7905b593cdb169c55b"], - ["a41bfe270219d8d284924a6e3755e2b817dc467cf1b5d266a69c1a3ab132bcdfc871c7f53c03000000026351ffffffff1efa4ff0d3ad8b18813670c101df103406442998cfcc8e236540a6bb39d5e17d000000000263acffffffff04f53f9c03000000000900ac5365656552636acbd81c0500000000026aaca625ff00000000000553ac526352f781e401000000000451ab5263a8d6f70303eb5f6001000000000000000000000000ad8e0a31cdfb52abd86d1c538d2b5eea29ce80751e9879952d674fae94df2e02ff14b879cfa12659aab6f6a79debfa18fc0496b7a4134705d9cd49eddf189a78de01375a0d62bd1a328f959c5515f2886511eceda68d120ea8fc8a4ce09982cf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c274334e9ae832460455df9f05ebff403884c04ea54f361101be4b92b962e3792910eb57e2a71492a6a0a140f4294aa34f3f3ddc9e92f4c92aabed534fbbd8844bc2af896433fa54bb4a55f3c9539f10448da2d218e004a4c6642ebee10c0b44c204cbe8c5f84bb8cb7d8736694eee394a9139569d2af2e642ffcad102235f450322a6a3c4ba3be17b015f76cc14c54dba25cf9e3702a39e586d0c175847341910020b9004ccd6b2cba8cf0b01c365d4f5f34111086be790ecfe81af912ef77a21a90a012fba33cad2bbe0fd3fab1ae5fb0edafdcf1d1b3e7b41c0535a9b16f268dea12171f5b6e39a3322c6f7b8fea01f347eeaa5e3bbb00e283f9d734c85d94f2e02030abe16c10d31e656b238c1036d24b963cb6e1497fc1a5f0aecfe8c71c951b8bf021be727e2304e3cef0360fbd86846316ab732d1d2ba486dc73068180c2c6de56902272f8eb70341bfb2d4469f26ba74c901610585ca172d416d14fdf47c947fb8190218ccdf5ce88ef12ec0b6eb503d2d8193ca45e9894dd6f5292c3373bd7231e1290303ac856751e90a4b1d5a8230844c9d2a14e75a4238424bd2706dcaec1a02e21f819414b7193ba38f5c19f308263d82793d37c7fc93299a4d6d0d6357dbf71ed5df441a5d7bf7062fc0fd19e36af36684fafe321c5cb067c3c08296066034c52b1425420eeb02146b5ea51f127973883c1db4e3c290902785e74c83a9697057ab86ad18868343bd844ad3c76ffe00a8de12e6145a818e55e7795ac8cf69f5499e0c0f10fa174297f95ec273335fc9ce6f8bed1a666c0af74cf081b9b6017598a94d3c74bb33f1cd0eea1595a5b86eba5d84c857c8dcea6b052448e178221bcb71c7b11b86cef213fa3462d95f059b6844cdc812a08d08e5d997590d44cddcfc8b3fbbc5c473f1aac86bbb26c98b97807534576d8dfe0b6ac4f2e0f07577ae3ccb1361f11b855da2ff3c9ddcc64c68b838eef6f968b03a1b4ab0a4a0cf42e9c459b5ae2a39d905750456b777868e61d23a50909e8e044f6e4604f58d3c5a07459d6e5bfc1406119999dea8d5e9d14e9928e4694bc75f521b5ac9e3800aca4864d75a1ebbcbd2796452449fd12868f73fcf2c338cd0702593e24a162c67cd3aa0c4bcdc18cad3feb0524be05588125b92c415cc03a4d1ef301c09a3e09e1686b3c23492cfe2b5f85edfd60369d2924526c81ed579701a8f35916abd9fed7cc8983e38adbf5cffae2eda55517e66b9c74b3ac0cf2d1b348bec562f77da728b6b4d8d2d7855cb6ec0c8251a429cf91d13ec7cab50d74585bb5cc862f2c499ab40408e55b754e05053360899b41b6c13a26206f89ab9a364ad5d0b4f2d3c221b1ffb55399a59cc217dde7ea483cba8d723433b75a9384a400adf0793076e611ca2646cce11651882bd3b3bd9826a8e8f1e55876fa8c18264d5a4a89e62b81afad2256a0a0593c11390ffdb6f571bfa09b2e687ce1706ab32deedbd4dfb68e3101b0a023aea5a399ddb5fdd5b0a9417d546c433914e5f38ee6273862bf0b57daf0a2db0f8708664cd78ea49fa6cf2fa2d97242ea674be019b2579a79273c9ade72a5c28c50fa810e3cb74a88576f02faf89320cd4f07612229840ce2952d959388f274704c42f4025b2fde8b983a18b923bdd87fe715eda25952d75d55c9e9b523f94fd70ffb933af108a3084661dc546e4425bdaf75609dd5750cbab384b0e8881a92083098db7a6dcf703862e6c87ae5b5ec5a0214f3556f9aa8c497aa31996afc3450dd8da1c0723a5121313d6e5c0312773a72770a420de756211740250851a5ff60944e3361c96c2471c808f3dcca49420711f51f8c027aaeb38777ff2ddc3ef47a54cff32eecb8e77a496f73c5b893f109fed6291c6ac00f07cdeff35a3a577874d5c4fc0fde5bcd9b96f89c69be002f8758b7aa4fd3914ecfea9b12c72dad60c8e3c9644908cfdb28409346d4c56f52d6c176a922c4d69737d4cd2d62d037b20f9c3fb1a8ac38d97a71528acfd36d2d614a14de1af7a3143b9e06f64cc3be35fb9076efe0d45f0147af83740f23cf2a5865202c096971c4874afc59235a64901226730b82b2491474806439ed5364caf62c1704b750e236c7d63ff30a0882ca949f009bec205f57c794ec338219a34f4a03471f3eb6d2d9a74515974c4f2ee489bdce459546fca18f6ac8d2b56183297fa5363e927ac8bd9adafcccb1160c58037fd48338b84b18ceb955d8d787cf2f64d5e8a9c41d7c139849ed37f8ef36392e6093511050405fbbc281b7baba7f6be0b7ce0f8ea01000000000000000000000000f17591647ed6e64526afcc482b2db15ce47bd89884d6a342aa48774f3a7a211dff99baef9514945703c291b272a688df4615d824ef6e1624109e8cd561a3c26b35ee6bb188384d9fd221a4a335ca003fe40cd9cd56e47ebdb8b6eb2b3d9c448c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f981ffd9ebc8f7cd4e764b4de70051bd7fb90e9a9c1e32cb02b66af333a2b021871652288e4659284f12b2c5fea81b553fa801f7a19a1343872ecaee34b74460b7d0028f0c142e4081ef85aeb94118ee202348da272be9dee2eedee29e436f277abe3cd0327b92f9feb8f5f0adad2d0424f7ff9d4cd1e9b4403b57c9cfdf5609032586532c8311e5acd3e8249a07dc94c0d56a798ae10bce8f53f4671ce452220d032edeaaff99f79a874f4fb43cc1ac25641c069f4138ae4ec234e8a0cc37e4cdff0a26e019e627fb7e246e1c2291a2299717ff59d612fcb74614600275e14ff377a427159200cdfa0e4a4f91084daabb6746a5e9ac8525718e5c6b2ce5c150ae7aea032b1642126aa8f744f619e9888857e8fa4b44ae04c50927ff62577713356f1f37022ba0fd5442b9a467f9818abbe13c327aedaa4c822e36aeee62d5bc0fe87b37540219f50da6675952ffe026f962e7d00e32c9dbc2446d50a4c0f429245e0f33d0a8030b9a9f1de1c1983fe0347e6119c236af02b4b232d3c1f27cea88834e8fab1cc802156badecb098106f593303b08845291c19a8767b50a58ae7e906435870a90d04e78088b993b48099c233e06477d7783f300b3d0909cc7bca2bb38e8751d13d0505761ac149231a872c09ee9eb031aea1ec84097ea9a052cd9ab5d424e1710d032c63bcd7f8548da45aa0965c9cc434d57e500dbb490d6e3b03c954dac5642fd2ea1fedd255dcd1860a3432cd1c30de3abd43905c1cbb34f2f3e20e2ccd0a4c99f62193170c1fa465b679702ef9e990efd1c915cb8343c4bc10512e36bb952bfe16b7c9d50fb807a80ebf8d2d26b0f3167a1b817b5c50e2cbae0a9cabb20ec0d4f87a6e1f4018001995efa7a59b6be4103d79f167442cc5cd51ae3cc5136c4abbed75f466b164112adda0e7cacfde0ca46b022d74259e83086efcbce0803cbbc5c10536eea9d928d1e7c455971def284deb28e95c33a10cc06d0deb96493754d9e898d618781b3ce8f5871640331e41b9aed8fe293dd4b1e2c5ba021bc5b86d479fadd45b56efe55a02a32ab09eae4c6a857b315c9fa2bd7e966ade37f6de9a0fa0c4052b63b06a7ae7557808367276b08de0f59111eaf483900a9907a1779bbe1b19b7ca342cc882f82478d00539763fd183ad64e9c1ec057216fdfd0cb0464841e8efe3b541439a6afa691076b8d00f332489f636d0e5b4fccec64dc9b65b268789e01583409eed63ac24195e8be542536807c5df7a21aca024304b38d0c33f92517976b89b8a1024d1ce69896627ca2bacdde95d8ae3c512735cb7e2f23bb23551c85b0857816f36150154301b9b9d40c4c2b864590ea40b5274b2e1ba46e68fa7bd16ce7b79636b28cb30384f2be5992148b0e375134bba7da51879d93bd119b38b1cea4e85b68c44d22e70225843afe681328e11e0953444c436b247d3d1d01859bd1329d6c02c3345d26b09060d5d421646fafeba2ab64acfdb1fe9d025b5dcef00b6de2138a9c755b4293f3ffabd7830c8a7baf8b45c4451ea55669abc40eee520333c4c5ec49517a8c93efc5f4cea858ffafd710e10982d7d1e27045fb18009c8ca272d5a09156817b0757713e7fa15c7c981be37d790f99a8986a5b936e201e872e222e566a73683febcf885a9e16021de2275f51d4112796d0ef9c3c889f055b6fef65b8aeb472a6fc2faeb9d5e7bef6e2bc8e93259d8010413455c632118e260d7a6a5ef19f471d07a7dda950de7b09b9b330c46106040d82fd4df2bab1c5e797696e810b2836fd3ac38680aff48c9573b74d1569f063b5477e675325fa5326ba3215c39c04ec11e443d74248bde0d885e19d40d69f4d0d49445f15882a7e78202ec274c1cd03be1c644a9246b0d501bef364636591b942a60e18ebe4295c594ce85defdeaa2677368722d2cf178d95ae016cb3735ca9a9b08b186353c84c2b39b5faeac1ad4740f2bc97478253b5dcebc4cb43bc8a547ec55a6efe939d70fc248f72271e09c9aedd509533e09c6248079048d48906513b5f9353eb1b2f2115e7d30112cfa204bd1613c0a979125e7ad55f87ac7e956f38292040e2f1208106740dcbb08d9553884b5de62009794fa8e972c147893f8e88f2452dcd89fa044021c5fa286026d350d526640f2979e2b34acfb274f3e1c9414b314de07c44f67b2d119756b3f2b7ca8e7a2da1d494323ea0b39e39c049037e4bf53a6eadb188005e78dfd803212a55ad686a6eddc0ac03a9343f026d7a30b6eb2cdcd7462dd9d0bb2b6054728fd5adaf4592f48809bd573020000000000000000000000006cddc22570414d42bd93f5bb7991105efe2fd5220b5abafed1b1bcba170cdb78e48ec4a926917bc0a7cf739634ea05e9b554289df8a7f54fa32f4f46a78bbac67229559f49cfbd6cd5744ceaad52896c8d2f048a4aeef6c0c8dd7d9d930fd07b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a94c0df7b31c7ebbdb564f05c35a06f8b3f9db1c4560e4419894fec32a7a50e6f8f5071f64ca238bc7da1348059b7191eb42c1eafc878a190e1122400e36251daedf46fd2484640d933542beac084ae12f88a4ff546bc8f6e05faa34fdfc91bb5d56b138d8e4a5b45f76ea8727780285cd4539d6123e61bc4ae65a632e3737e903038fe569b6714524276800636b9066fc3ed4e09ec60d292fc6c136a25325861c031fdc31707f7828055007dadabc40cfff64a4d981077fc62303464b2c01d19bd80b2c0713a01383f32515b510f04f3fbbc1491e8d0183b43df8ed7dc05434c341b00011a1393f7ed3de4f05887dcb34274a1a81d0607428283958013d33a46bed420320f3484c91bf4851d69656503d480787d3ac9396840c65a282cb3ddef466e34c021736eb7b02d9d281ae5814c7c70e8a06fb9b0302693c2d810e670eb562c54ac9021d975dc64b65aee12f1999c5fa6e493242445cce64fe0b2327314251516465590218d45532b37819ae1ea4e17f814f670f3d0edd3a7925238408e1ba3438dfc0af030d8b9fd60d4499afcaa8562391db27735c183abb2ebe3fd48d2d5cce05784c71d8ba16546f1eefe3df6a07fb28c09876420d99d0fc478d5fe6496ecdef3c935d835ad892418393bb559e8451ed075ee9b49aab8b584c109002f3c3df89f5320d1841fff612174f4cff45484d9f30d3f0bca77bde6ddee69b54ab95d0ef44d36a8fade15b589e67fef0015db0bf592e1e12ec334848bace92528b77f6910a772c938978f474cd5b158bee7467da565d7f586e109c54b3e7e41e94a47b2067abe8f69fd3f3e6f0d139439a7fc9aa33c95c7aa6d714cd3bcc426617f6453f815d12c95fd1e550f5a538063dba7e157df66d3bd6e46afe754da57f199aa1fb44f74697288962a5ee102cf9e489b192d0d25c54f8cb8d79116217ccfe267f8aad833fadcf6f70d271a5c526ace769a71ae729e721ef28d1af32ff0dcd37c261c14963102bfb45cfa34f98fe2add25cc87426dda9ec16904b405d35cd112b7a5a155348eee24b5c099304ed3ac95c9d632fb84812376784fa8e3840b697a14887c1d3042bca53dfcc913aacaca5e8c5d65f72037ee26afc23be2d5a7c3a0c72046316ca7b03ed4e130eab252560dcb63262ecc3d7334c33d6e47265317ad439ee4a20fb4050ced16e8597b1b56cd8a07f9827ebe944e05e1eabde5142e3bdd3ab63242dba0f97499efe4cf2872e07c2f15ded84aafb50b7421229d1445c424f4f552698ec74c4210a3417d6365ab4167781c68e89c88a0613a1cd52a07948db26fb07bdbef3d307ef24a66a038405ffa5a6a36b0034b387cfd848625a3061b9de6b93fab3e472484535f8e279bb1bd7de208a98ddbae3fb63edc0e1324f9cfddf99aa62040423b5d13819c93709003ffab574407049699297824600938370f081544b72dce2f457ed8e7482bf1e1ce2b92463cd10b1a4dc746883de1c3a3a409d1637342dc851a483d77047f56c8bfd977676505887887462e5ab02c2712ce4da78eb035a29864aefa0803408d2d58270d9a2c6dc265a1cdbceb43055c558a4f567de7152a47c663f999af0e7d884dbad8bc1f030cfd4bbf92d6ef985dac714c558c6071c438fac5c73802dbfee4f48c89afb2c00cbf5a1b9347b53c83f0a67823ea55c2d707829efe30cecd9ba2690ec7253c8748c313c8bb95755e02c75c44d7a50bc53188014c88d772993997895be7ce529d338239c5b1f169e566c56ded6a9cde1f7a3190a958f7f75e232bd0b6cae551052d7f89442ce167376f32115c274c7ecc596c2d578f32a057c883d2028fc07ef8f4b4f32deb37098b57fa0e734f190b2e991f63a0af6bba5bd0b3a3786d1b2b4d9b90fdab1d7599cd2f8bee621389bca95126cd72a8de6f03c2f3727158773ecad9435d9ebee787e2a597ef4d769865066ff733794bb48d8a2c117898872f86a6bf465c6d96e5fd27e058bfdd8a68ed734105423bd6e031e123483ef7dbe935c7e3eb4e1b794a88fb543b2b76972d46629a2711836c531cc7fc8081e164e9557a6f0271719b0c0f19329a8696c78eae3582498d0b62d2ee6adcb318554bcc0d33d717ae61769c91afc08ff87273bfe3b3613f51d3d879b81b1d2d36ef85963f011a85b2e1b1ab4bacf9ece1f0fea3c449652f562684edf7661a9a52710e89f63a1d30ac3dc7b2402b942eda1e335cacc8ab824ffbc54ff57253cd6592fd06bd7a640e64532ac2640f2fab04bb443db0c510d8ef11ecf00352f3858e504f684445520774996cf7e6cf486c297b1794b939a6f0f00bcfe78666c73a63ad8b4eacae60f640076fa6796bf37ef14499a595e650e28d0c943da184b470161f35b705f04717e40d3c5031620169e0babd060da126e26ce51f9619f48643fec7cf3f611b0a", "", 0, -1265752255, "d8ebfcee483977859607fa7d14add1e107f6c096c20367d71b0fb592d6af1540"], - ["9f140bce03448d96772e345909eec99f3a9f3161c03cd2fa619c91ef40ac9636c511413ac10100000006ab6500516551ffffffff488a57b25fbbaea16326811f8e14d8418e0f6004a09999077ea1f28cc5ba9e35030000000663ac63ac0063ffffffffa0102d720c343a65e8843d8dd11e6c2b50826bfe1d49fe9d5edb168ba25c6c8403000000086a5263ac00520053ffffffff02bf9b3703000000000753ac53635353538cd5970000000000086a6a53515363515200000000", "635152656a536a53", 2, -1344830367, "bce3b3ce45ccd8a880dee59e6f866d33d79a8fc388ca8e99daa8a46e637054dc"], - ["33f9ea7b020e2c567b418a24810ce108bee0b292f14c537bdf7ba72856e598593979f7d88b010000000151ffffffff13b96ca128943827a7316b3f26d059b9010ae7a1d159d9efed30ea1b0446c2d700000000066a63ab6553631f33b84803c0ae7703000000000026c1470200000000025363fa92ec0100000000066a53530052ab00000000028f40d005000000000000000000000000b1eee09ef1f34b5fcd6dbd943301615ae0bf560bd2eafebb1da8f1d5ab3c53ee324c75b238a14e86431d7b270c967556f010365ca4610dfa753ecdbae5eac2ba3a9caddf07d04284030f6651c850548ad5c05ad50d9e8636d8e1d47c4fc56116000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001189da5d85208e01f783318ed7f48effff37ec6828cc36f80f3b6f7dd66c8fa2413cd09ac5ad7a391d3a7b4f592fe01b69a362f3cf64d5b97925fc4103cc9654af57dac917ec3ee501572e3793156455835ba542b909b6c73cd7e56f6460d0363a622fc37133b1e6494a14001d0945227e2ebbe88ed307ca29e1bb22c602924802057efe94b8bb4056aa4e8fd0577209a94e5c02c82b256b52d97056406f45a4eb031a2312463dc4ac4614e8bd0af0e6f6a5573ed09f3d875bda8c5f6270eb4e48bd0a20da829c7f0b7a5b7ce90c0077878a7e7466ed616003c29fd4422f46e84be37f13d3911f5b6d1071af1bf20f931c2a40f31c3f2a03288a40f8bcb568e247b657030fa64fd40b9dd5502f0615550417ab20c018220a8d6b8018f55ad7e9df9320200229ac369c64dcd544bed6871ca00b66a0d5f7c0348fe69e257c330a20e59185c5022b95dc61566d3ce88578cc18c1c5e86553eef03e04326ca485bdffb99c03b2810317777c9770c2a0d3cb17734a60cb27c7343adde9c4773b62d22897ae565007b3022c193fd07bac542b5de75d8b1d7dbaa60f56c5a26a90a4f7f7b524c882f83c2341ebe6bf9d340c2506a42e0e82a86416926464f39538127e66c05d806917dd33bd76e1204926beb4aed50d691bdf7e9e3aa4a485484fbffb5f98cddcbf06ae622bfc8bfd9a62352ac140e7f94b7a7b4f887ecfe0d1b4422d4aeba27df0b9279bd892ac87dd4f8ff3bf4d104c8aed8188491e710a6224690540df228cc562ddea9672768c3afb3bb32e7c862aefd0c3b436868cb38f0d4e3ee325962b55e2720d48f64d4f88f11d3c93369e17412f2591eec1140c7574a7363f4e1e90ef668b642c7a660086b90bf19b58cdb964fad1023cff1dec454597f5dee0f74cd1d605ee09a116e183ab3b2aad331f5425500de827fbd8bf446668723f1a827c59091c05fcde09411a80d461b03c7d67fca668da0644cbc1e7d3c6a035108d8124a3049e3b6ade44e7a7b12475fe3f672fa219d05a7829caaa9cde92f3f67e70e8103981901bfe05ad4b187bea3533a420b83ffe670146999e760c82e2826b5a6b9a8c9e2a01d78f639dfa3a795fe875661ac949fdf7ed3de7aa4e2c31ffeb0226bf0b047fd20dc35b215c072519842024f1e02064cdfc6c8f1e98e259c4ad12fb5bd9d8f257d0604962d32d427f4a9bfb30e21181cf293c1be132ff77ae09335066f541cbc8471b019500105ddc05cfdc6db2b7c6e214ec58754fba19e367fd2da81efb3ef699bb4ce86d15ccdd81bb5c162658dcf1e1f895da21cf80b57eb0ad3a815ce4322e78493f3960c9a0b0a7f7bb5a3e5604d9db4b453a625d8d523a36ab0abf617f39b329dca32d2b0d5f3b1a6081d0064c268f6987429076a8a1745fae306be00c01c3eccb3ae32265d450cbc79df85154a7793f611c86b895df82715d23a6b30bf98ae676ef7f8fb069549936c1e985ef4a0288dc81cd5df0d7df4f0102a60315061f4d6e56435b6ea309b6d75d6a5d3a9d9b05b4799428e123b3f61363d86bc322fadc006b10eeaaca647b75d4738a2351c93acecd205c5aefa0dfc64c8ddcd277eb02cb1a86b0479f68545e07010c2d3958494a830d3ce7c04d05946664b01db1a3e0d430e6c118d18461f0fcfcee35912d1f23e983c4bbdd2ed6d0b4dec98b148798fa60f20b93f1165e1eaa9fe12639d0657eeed4d43e34fb0565d9690d400b63ed589dd91ff3bb7ab0eeec87c03362071f85d89e6ec5a7014c17ce38f111f4410fe41c88645180f7feb8f94caf71a68b3a636dbe8078d8f2c6e33d9594d2609019b9039caffbeb758e406dee3c2e7877402fb160bc033c91652eeb62b2b8273148eb430e6b299f3ef82e425a7171ae2070d6fce5e0a68d9d05ed72bcfc650c39058b66ad7d18a394c1b2aed2ddcb476e2135afd07e9cc65d554c67c770fff39c9701f160adbfa8b6bccb7d4d7ac73b0f7dfa4b02cd0954ea437386666ce058e8973c7f1be80cfa5b49d7b0e4ea811e98595446f30777d3485e5630e264c34e8c399836a10d9f86829c398818ea95efd81627508cbbbede8a981dd9a0a03664322a79a1bff94cff88b0158bf668753039f6430dcbe9fce0ac63c355824c12c288e8882e38db04e65fa27ba4f0f7e95a5f75cc13b8821bcf74f2ffbeddd9fd92d1f6b9907c1498c4291ce75f749e578a39a593954a54e3ef7d3d7a4130c1710cd3edfd659b494cceaf5ee55c4b5b222b77d48bbaa3c3f627af79e259df367e88d27ee18587cbec910261b1558375070fb23805000000000000000000000000f24ecccf22f13cdf535b9d731d933dda5696c78cc419049035808a322a0d3e333d5868ddfe2cb2b2fdc0c7875289e0ae8fb1bebed3889021aab4eebc7867b0a6eb33fe68902694f52fbcd0b866a1b7c64fe95934d60a683db9afb62e5f5ece940000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090d99a95847ee6e8ec09b8e364d280887044ce7f2d1cd126cdac73408fe096b63f3364e1accffbbb348806bafa3303287a599f79359b7035969921b1c1e44dbaaf7d4201578c10a40080d8c2555f143ce7f0afad71ba0b89afc8e631caed11f15216c1744ea04ae4d618184d5ac7a53557ca04614d5475be8e4b18fe2f4b41560326c55fa4e4ed3e59f7509634c60406c2958d66e5955a42b00adc7eba8c2f8ddd03135f3129931b0dd67dea85c08288248662fe54bf773e42fd75b432059404fa730b19d11003e877bc42ce2d643acd7340a31069b5264d56a6d1c7f0b8a9178b69671e9a32d3a830765cf3cd1d2037a4117e97372d37cd7c53a70cf1ea11c4a9e4730304e15b54c04095188c7198f35f3f11c5680a11fd7f395ef4770c1da023e0e6a702087a15b5a790308b516757c42c81f69eca511fe94276066ce967d3e8e1da9085021d50b2263f5918dcb6602b6017c35d5f254db5fdd392cb9ec93705e2c28ad62c02050c044a84c40e08c527e3fcaa5d64d9ddc8d06f596c7088881f11ebd98ac793030baa611f4fa4565ccfaa95972e64efd426ae8701fbca8687edaa65ec2fbc5d53ca4c2e6ee100567f8e3d9717d22a55ef4cedb7ec926517bff976afc8587a5ae2590442b8efc0b05dc137c671a36c08b433c17f058845deab6803676427146b170c0c6c517f22e07cf5a9a5939a1e52ffc262b7254f9749c12ed95483d09d8edecdc17ff4630462e3c8d55c9e5b6bfba0683c4047c3a05a799f3ee95afcf5d4db8e1bff32210ce1bfd4b8a0aeb976c519a34cb3c3531dccec687c94e33547b1f332035f81b2e43ae387bdb84dba076b5e5790076f8d461d909762ef91411df0835fa95007a240cc5d95181be253f99d093c7a99512c475bbfab680e21d81e4bb51e98be56c7174d7fc943a78dc46c6d058a366c6c0d5e4b9625810f58ff6a42fc4bc7fc08dbb01daf0d42ed0578316f3a9f013d0afcdd25f9f386511d727b102121780c697b61b7903708b2a60e102e8d4e8a57ae009184a01565b949f7f5bdc78c3b1f8173e6a631050d1eeed2a2b0a4eb7106524f730f02594fc8c1faf6adaae736585dbd9e478e845fb5f7f349c3ed183fa9ebac8005dc5cfb9fc3593b4f0f40f99021af5f09b6dab4555c693d4d2070cd44abd126453564459fda82394cb37792e83cb72f7d41cb4a69c525169a822a6f141f17152c532a022ce7201c5223c8c274ef6b26aa92148043985b72f36516e70ac484d119a79eaa4c71572b18be1fe5bb221089231dd088cd6009ba39c0c6f4af1a7e0dc64c15e9bd31fb62b80aa5aee76c89f7f70f688f6280ea79d3e8d58394336c69a60be61d69c0ff41d4fd43d52dd063b5510e78ea88612a6bc7add98f17537075705b1b80043239454f0caeeb6a14604bcdfe07b42d6e3eae55f721d05a9b7c3e67ed769f1da2fc5ee3a625b3dc031371e774046b2887db60fd8a0ac7722633ca31db65044296fe043065f4c73b29f646ce0573321554d5b299b102766ebceab0c0346fc6624f58b146ed68c33695922f6e034be155f6dd242bde508538be5c48a868435354cb185016a39195873dbfb929741b58253ef99d9f0e21f9808b704d2669a5692d78319d846c449612b3b549e2dc4803510ec3f9f587231b12b27a7dc7d93957d1a31c5b74eb120dadddfc6ce99bb7b969a31abc46bd14f79d320482a28280d80fd8048d956f62c1768b46880382da01ca817c19ea065c3fa0a81762cd097d4aafb4933eb9f8f40af98cb2a8b47721d404df4bc6826e5d60b59b3c04f9f79a2f5630fa58192ae9190524683f0181ea62c5c427669b6253dc004932dd6151e541ce9bb75b6f4bf627d38698bb54f8a97d7c2ea0ec3c50da44d20b3cd69faaaea837be624359444c451b2e7bd0314080b5e46d9ed2715b21beb5261cf30c4fc7e190cd0c2a278467c7b9075f5e5eecc89f5d856c33880869a6ac0b6e886fc593deae8cdaea27231c18be495d9a68bc94fc555bf2351bc83a55465364157b0febab684697a4ad6486870ce3fc4fd124bdc5e296ca5159434dbe9c4a8712ccbfbea00fef7d6a7a5d19c75c85700255a5f825743ff7b0cee3318f49508a00793cd283e7c4d32292af7197ea9a68df9480e73639fddcc73028572f6066ab19f09b8eb6714d1a90d3b1758c621a8676779bfc9524b3550d6300a31b712aa499abadc1d80d307310dc98380eacfff411186f2cc720f8cdccbe549d66f6e8a5e91d6f4e2db651ce23110e0329ff535c1d966238482c08e572b25a87a0eea4d057e55cf242c271194edede702987989b0737e48e361d486c9778bab3576995a3e0860315e4e7c076838e832bc3cc8345cff604cf0ffcb35549cfe1ca58353f6d831325680222aa75ef5a355fdd7e5de497b3dd4c50159822ca1c1b2f06", "5363ac00", 0, 1424092484, "63b3814cefd137f46e4b8abf6c24b2d3abb5a84dbc3271b440322b0333c9ba09"], - ["48ca084802a658d2daccf3d6caee4d633701d55081437e6fb709006651a5e655290275baad0100000008636a51ab536a63533099690a6cde0504b4869ee2776d8f2852ecd0ccd7ec6c05e30ee27368b9675db019cc1e02000000046a6a6500447b7eaa03f9ae0f02000000000652acac6aac510fcade010000000007ab6a6500ac53632f09800100000000086aab6a6a6a65acabbe3edc0f012bdf18010000000000000000000000005ff5e0ea6d60c5954131c208f5843422f572a9b0703de46085deac6a4b46c6b39998ac205341fb132623cb235ecd65d1a6410892dd344885b887730c23f7fd22d91f4f132664695e46b3dc2d572bd46964d5c1b1521b391f35fc51ecbba7bef500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000420aa526dc1d06fa221fe7b0c620cddc61f7d47d5e6e8378fbcd021d97c405d81ba11b92951c69289a301f8f133d3cd476c98407d9a2c989963c6b3d1446dd38af6c0ab43ee1dd1e190f4f379c528bea0aa9a90c1c632899cb58e9c0bf3069a695bf06c0704448229b90b05c52de4d33038fd7446fe992efd7c1677d202878b903291ecbec745c5a0b7f786d9ed41bc54e073bab6ab3e712bc752df89c6c1921550302e8d1d85ee2f7b9513e4f83313d933add4c49ea49d9d3ced946b91340a849a60b1e972b6ba2eebbd7cea5ef7dead72c2b52e19eed469b5a47176d5d3c757c5e900de34b979d323d19c96949f91a2eccc60e43debd106d4f6b2f21248fd40752b9022b715002aa340c22feab4404aef2e10cf70f0bb9456c27aa17cf25dc0b57c984021a3c27a747c0ddd3f6c7a941b38ecce529afd3f34708821e70e90151bd253b51030efc9abaec7c27deab60d7ced4968e1d6226753c7d181e71f9aa330fe434f51b022626647eb3c7c47bebeaae12f6c5978830802081ee85206d92ec88112d362ecb020ba6ce43a175e7ee6a550cf3962da5d608d93abd860fc86df663efc83d46f126dd1b7e51ea3e7feed6b5509eb28dd2662209548f1ae85112bb634fdd0e0e7cf5c59abcad46c71153b331d9c07e469fff976c44e97948439479821678b796ffb47b965f6ca4e1c99802f256da71d1fbbf0f902f76b1db29db556ed2317593391e6c02466f49d9f814fb268e8f0acd01d7c9c63005fe7260682d042b23b16f884691261aa500d665cc9ce7f886067b53a394e6aecb47db718c5de535f360bd644cef2bf4a818e7d43afb3a419a79ee633969d2368f7aa3a7dacbbd4dea866e6760195dff5efc1330dc66f1c15f0167bc44268c535bb72603dba90270f130ce96dd8244beadb77ad649e23ca80826745a076116a836b3112c7e6260eda935bfca6677a7310093c3aad6f9631d9cffc9c9a94deb7db33aa00f92ffe034e2650b4e1e18db19250af4155f0fafb392903143070ac174cb72ee22e22f1064539137a8cdba81ff6307b4439f37a5e3368bda46eb666bcf0930f231bd8803036459a461e70d5b4726f720d1851baed72567d8a256b9715cfea8cf0598eb0f968cf98fd254cbbbba84f187f51b404e3b7697bb91282398541d92315950a62dd43104d3560bc8aeb13f6fc96e6ab8971bef9019c07121211c8592551b12cb2e7b63fd77181a377f0a304fd0e716094c8fdbe245b3e597358f6544db02ce0560a32a541d6f20565f2c13458241c9c95a503d13f18203bbc66224947ee608778dec0491859d3ba74099b1ef35656f23382865a12b25070251ee8b08936680894646a52b86d3702eef867ad3f5b083cfdfcbcb02c98fd58c6fc33291a957df1c9a7bdf93ad4584ee6f88c4bdfd382d59852ccf911ed2822570ce801142448d732c92156a309a448227962f63145dc6b53dceebc56a0b5d47df8908d88ad59e11015e3bfe0c0e4e1bd71ac893e22904415b55e5b427ffa09316c55077cc940c8c1f98825de8912f2e89dbd44704e2353eda79b42765bae0ae6cad8182e214710b05b089ca8e2b1629c06d1864353cc6d5923bb54e798d7959175d011eb083f28e94298d02df3765fa2940e803c7e16989ad76b98c66b9a587893ec310eb9cc1180527218c90c1b10e14c4da2f462e29d24f6c55c16a8f632dab4a6049bf0f52fb5c10079be1ea825675d5af1753bb2620f336f25188fc5ee737abbc91223ce9a80cb187664b132f05d27e3b13a123c4179fb42e297fddadb0d26199bb104f2ef3887d0cd6b6c82ecd52435384de5ad5df1d920d840ff626eee8907fe0af3b94c428eee80e9d65f831a25cfedcd8d1ed4c3594e91ee4f1e55c6cbbdba6fc68b22b01ca767c26f3f873baadb7af372907b05d8fba6a0657223682b2bdf929ad58f117645c6620927a75e0b69b070ba44a3a5cc9b9a4d0fcccf497e6a43801ce48c746fefabb4fd86e68a6e8afd0ea58e376a37d3931503e7e51042028ddf81654c10b3db03af110db71381b06871cbcba75b953a24b29227337a2df0fcc11ba20854cbeb4f8889a63b471ce67e024c36d5b1e3b07a689db2f89db5b50b8d2db3782e44bca6d5b85392d7d56b1f69a64b562ee996f66c7308bf06397954f3405210412658626a370e56f2a82ab93cdc0938703dc9866649afb6a5d925b909c00e1c2512649f1f6efc39edebb4eb7692446e14ae4e170e62810fa2b5f2d5201624a13c9c12c3279a83e8dbe2acabb64fefb2d0b350b31400e383e0458f0aa9729e066678f80c5dddc938c79dbc51da937a374b528dc40e6045d2d4e20f002e0e59c924f12a9d86f0a7e5beded9fe2cde5e72dbc7cfbabfd335ea7e64535ebfcd77b0a84783088d3dedebde9fdb4ebe65eba02bc98ceb460c3bb8c06", "006a5165acac", 1, 770613875, "c21a0e5d14b6f8b8b43729fec5536be7865efc4e6c0abcd9e1c64ac134c100b8"], - ["5bcf30040182592723a0b22b11a5313dca10084003513b5fefe8431b5cc32d66be0f1ddbc40300000000ffffffff048d532d01000000000165e485b10100000000025151cf55e1040000000007006a525365acac02fe0503000000000952ac655151656a5251000000000100000000000000007c9a6b01000000004bbdf4f70c8cf49105770af47f51236d4f6e0aa56a29cda699ec21c5d8a733b97f1b4e9ae8b59ff79473f7695b937af3f7f49879705262b435f00e30f672640b813d7885d29803a766965eedf4586b94df6ddcc6bea1a5d8716ee16b1f225afa0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076e6a1f2fac13ff58821130a2d8ea11ec8736736a73e907fd6b967cd703029aa21fb1d6ad81ec4623eef32db298a62c96173a7823bc881279ce0e0953f9e48354e947df105f8cf2a048fc39bd59f04130e660d61f359ac4560ec67ec74cbf9d07ddd0e9200faa05d5beb858aa9298c02ce4a053f77edac67323e9a259d2ca7ea0310dd30be4a5f0e439f765da14f746cf34f601e528d2af482571408757df60b49031c1a42025738f7edd0a8a02640fe79070212f8ef1b70950bf6776be2b8f029ef0a041e0cacc92ec7ccfc400f633cd2daaaaffd8c276452cb70514243567536cf4806f340a9d300d72d0483fd717035c17c1a6c7f8186fa4329240cc65220db51d3022d4cbc16d92683552982c84a9047231f542c447f521cb42912d4cd7a4778f8de02057ecd4211eb0948e53665d061ac24266081b130733dfc241613de03f2d4fec003302c25a125067759acb4b78b590f47f3ecf97d4f5d21af4809dbb5ebc697ed5102004ad88c6a096c57ee39fee9c60e70d8501ff2e1335ce76d6839f336de3ecf95021dcce6d640d444019aed6903dbe9cbca74c6faf8b34b90fa10d6a5657d299b55eb585dc02773e65a987608e6d43db37c6f36d6513b48a5bbed3da556fc13ec3a3cf59184153d1c1e091680634d14f36372e51101297e42ea276d4655dc55c27b21a902402fb9f4ba29821784e4ce1c48ecfb96b6902e9d32319169491f00549a343192cfc5e14896ebc452586a1b9e4ba2655159645fe1b46ebb58deee63e572b3e1615a0d018546863bfbbccec4506c2e493c7cdfa343b2f40e6230173ece0a39f1748d5457a9894ce47d38fa2a74c44b638bbde8ff2cb615d0b4b99742e41177354c9e371c00a2e4daf3db82ef8b8cf266c0a96dc8a22770396bc42a9978fe12c17ffddba74f139abce04892e8ba444f1123f57b222f77eae6635495318c038fc97ec16096932f376c3fe60e883a95c8c270051834f29f27eacae06e1c50c589529c4166616350df063f3680c3c3abd6a631bdfa6b79d9868353579ca1b300f521ed1539a7a6709bcc36d28ef1b6268cce85f394bacdbad157ead6155ed79239c43a456aa6274b1cb07626a7a44183c62de03f500f5eaee45c5259af8bb2e15f8854eff6310f835c9e7efabcf1ea98496fa0d4a9f46aa5911c92785bde40a525fe8a884dc875f7738959de5f70d5e5ff6531552f9f3cdc94303f4c1659b361ac65bb1e4f9d65d7166c3e407fe587fe3da4110307bc2c77d37e7d438a4f560738280f8b23aebb4fbd624c04dc8f7a3c82830ab788085734ec0c233fa2a5f4f4b187d906a2ad00c2eff498fc5cf1aa4841b9f528fa40add0544cc11dcbca70e746ab7166974f49ba8f1388219591efdd13afc55a9d16d3e6216985d903a47fc97e1b92738d090ec7e0eceb419d4897b88c2054eeffc1da2971f7013fdf6a0bba6ddf0f8fedbe017030769769a08426dc6ae7abd749be15b4885a1d92372544989b7f272df25fbad7dd1cd5ea4d457d455bc0fb9fb8f89d6f91e065ea601cc7ebf331aa16086a2b4a3984dc1284c664b99181914efc6bcff6bc283718c6184a83c39b39d0bdec510a89aa1a5f865b5831b2b74dc2ca98b19f7b6d0d306db27fdbcbf28c2df3fea3477eeefa35e48d612266476a2383df6a68654c217de0d5892e7956176b790bd630a6f5ab3bebd2340c1ecad515bc6d3ad6d1745eea3bf90d666533abec3786d25ba7983b7d8f2d474cf723f18a1a25eb8a4c02a52168f3fbaa076466424db70c96e7628e8ff29022eebb20abadac90058547b447c6a4e8a33d65825b32090e082987b0dcd5c16177c9e2c763961849646787329fffdd3243577607053e5a3e0dc7b6cb9161ba0c5da57c695a833e40e897123b31ac0d8325bd4bdf3d96d2cb4380df757a9a6fa25a75795d2809c7b80004947295fb7dbd72b0a72400a196c88b52b8823f90a44b2415f1564d57444f8be42a1c5931163fbefec13948c23805118679bc45fd63eaffb311af8b641131b23f0602c5e325e284a08eeb05d376a70f83347bed8e9075a1a2877fc79287548263f7210d54a2b1b862d05dd3a4fcd2870ec928a4ee7fb7cadeae9641c2419cb85acf25043b9fa606df1d117433b24af4c33452a0b62dd26163cad735bca59c08ac6657489fa2bce5d7bcf0198d92a9bf89ce0ad3070310862c4ea8897f338b64e64af5a395adddcdf8e0623e614eeb15a598eb900fbd0c2f9cb20119c9a00ce202ad1078da1140b7d748db41fe9d81f19a5cadc33a6e94c4b01906320c5ff4ae9127f1c3c4fa8f25f09d0dc984ccb799b5116c5c1c595bf5bb4f6a903dc11f294c84f17820b864401a6feea7d270624f3be56f99b09c13bdc954d73e3c31d3bb9aa34ede85e39e194c200316536a77a73be8dac6de57f545bcce07", "00526a53", 0, -482347469, "669ca79029e7deac8f95a9be4f6215f24bd6c2f7cf8a33ac79ce6c3ec17c4a97"], - ["0bd041e00167489f05eab1b162744dd9cbadd3aebb69edeeb681d2424545eff65570c7c93e0100000003006552ffffffff032dd6ed05000000000665ac510053655d39f201000000000652ac00516a528ce57e00000000000365ac63d6218d9c", "52", 0, -1066246780, "ce44ec3d4d348e14b590089dc3b59a5218eb38f41d2e62c01eea8efacc04d2ff"], - ["aea8936604d099dd3f1ff1487d483f3f067a6772b9bfb5bf2b8a2cf3eb2b239c7136a879f40300000003ac5252ffffffff78e901714f5170212a63e9f0f7efb1798de27e54ed969f45db50e908396a1fa10000000007535363abac5163ffffffff109afe24d72fb93755b348c4f77f54fcae10a07c7091cd36fe6d612cbb3b6c2000000000096aac6351636aab006affffffff5075e32429529c3c55d270db54b8b163888dd8e85ff1b8fc2be72509cb0a93f700000000085252520052530053ffffffff0150e4bd0400000000046363acac0000000000", "6aac656365", 2, -1387400805, "91813e2c848414544928217c8be73e27c0d42d6192798c3e0d2a5e1b7968a63a"], - ["19f2bbef03f2ab5c7e28422803875cbab9ee655507c2ad8b7d4cd8b280ac3514283580529802000000025363ffffffffe027a011de891b5aeac344c9e68b6f71a9bd88fc909d4287ead5a3b0648c1a6e01000000056aac635363ffffffff58023fff53d0ec6f7ea899729e1dd2b62c63fe4bac3d6c9b412c6bde950dd09c02000000086551acac536a5163ffffffff02f4acab050000000003525263c4bbb30300000000026aab5cb815ef", "65ac", 1, 1526047431, "048d68308af49c268368b46dd7ad4d2d6c0311ff1c107f5f0ffdb3ab9c5d35cf"], - ["8b4322f901c4f6f29753820f490c636321d29d049987a2412b1786e1dd0dbb9aa78a4cd2ab01000000016a2739a0a101085cab000000000003ac5165bf020baa", "6a536300", 0, -1345134368, "4848e0e38f42840750c72fca1ba97dfd5e301f0f9f04f653f66f1f9e9b7d9a24"], - ["7264c4a701194604c8a93d624e5953c74360fffc603e9aadd8fdfdd3ab75b6079238242e4c00000000095152656a6a00536a00ffffffff03f3877302000000000069a67b04000000000200acdefb85050000000001ac4cfb3d2b", "", 0, -199685283, "e8161ba858734ce7394e9afd05549c90d2e59cb08cf145bc801082e9995fa399"], - ["8911ed3101d5e27ffcc62f3db72763f4ea54a93fed1585d849a678748728e91b746b30a95a010000000253537d3147a1042ddeaf050000000001513df9cb040000000000d8f74603000000000865ac0051ac5300ab4af6760400000000026a510000000000", "65656500630051", 0, -1133301726, "3a5f1c2ed723960f2fcd13eabe5e7392b606e173cee1efef14148cd4acecd53f"], - ["3810b839011792b445c3935ce7bb67bd5a1c28a3b790e37e65145d4bbedf43f61fa7d71560030000000151ffffffff01b8baa603000000000200000000000000", "526553", 0, 668764128, "ab1caa94a84dc308ba8d0faa648a076124ec43e220cac3d3c23c6c177958ee70"], - ["0c9f11fc01557aa1e2e99a288a48549a3035155ff8496a20e045f931080a0c3d0f47bb366002000000016affffffff04b4803e0000000000046a53536a5a066f010000000009530065515263ab53ac1e0906050000000003656a65c6767a0300000000066363acab6a5100000000", "63636a", 0, -2134010377, "200ddaace44f4fdc9a32e0634a705bea0257b163b4a05016daedbc5fb17e2bf8"], - ["fccf5ba403627d953e3e4be7df40e0499f745b49875cecb555e259d4c0322e00f179e9101f0300000004ac0065ac93d53ecacf000bc9f4d7e19f867132eb0c58116666d188f4e7d1e6612dc2a196e681d5910100000007535163ab52006a3112e9ba343a1125f9ba567b194cca4bdce1affd47f338864cef7835a03159a5a8d69a8900000000096a00006a5163516a53ffffffff033412bd01000000000765ac65ab51656324ff7f0200000000096352536300ac53ac00cf176003000000000951acab5251536a65ab88c29831", "526a53", 2, -652258674, "8e78eb139f648a5df15c0fa08bf8ac0dcb43a2332df16bef796a631a44edae8f"], - ["771f435301ce5675eada8e629334a608eec0ef48f5fc92715627ebac3cc5840f398ad6baf70100000000ffffffff02e34fc7000000000003abac5113b1f40300000000090052ab6aac6a5152650000000002845cd105000000000000000000000000aaa975961c5addb05d28d7bbd6e615ce542698066c79f901ee2b994649bf3c7cd75e8ad34a166d254f4acbac26284cd18e673ece9f7a38ac00cc04a091307e84d00b96df1b8068dc1b85661af7bd3156aa197032a973ff7ce823823be564140200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000726ab0d869a52761c97b6bc646e3523b112331f1cf8575e9a254b958a741a7284fd2edf154311f382d1b6903bf475b1fbb46def192eec4c7674b433b757bb0e6a19aa2a89ff65ddbe748ab77636040a4eb776640a2b967efaf0f450d48c97b2c253d961d7e673a3ca0edde5b9a22be36d8de9c886cb863bb7009dc1940606740022061511c0c217e1e20f138db61bc3fae18e199a9c572f1425a488314215f01b0030ca7096f8a4a1d099e695f370693a56064ce5b02d7c319f73261d1c927c6ef760b08df6fb362d72c104cd1c83562025ed04b3e749e85be343d218bf74ddba6924102bb50bef83d94234165a61062dc62727dd2050662acaca61f75012d1721a3a9022d3be4daeda2802f0c002448aaeb77ea66da724bab52e30aeb8d5df0f01967c90316629f84097d2e17b8669a9643b895d7fd1d0224b77c6399efd58e7bd4f27aca03239e6d94238a9506a8e80c31c17155cff12509fdbc6e8b9c9ae83f2c7a192db603122557d246ecc7088ac670a1cb8baa25735c7bb897e40e894dc8c54fa5f1ece2022516bc362090a7f133af6848390ae16eba8efaf459798fd19538d563c69425ad3d9e817e792ecdffebf51edce9cd798a14545b2c5f873db9e00394c32117fbf021f5820e5330053ae0e64b2ded393b28fe37838c6dd8391852535f7a2381cd0d336ce0e4f4ddc20e8534fdcbe68ca217cf778e868a93b73f0e76747ef32f85383f8371b46498c96195b178767e1b0150a6bb0d12faa7a7eef4450a12194eb241a6d2831569c03b5239cb45ff2ac9ddbd1355b34223f532f68e08bdb9e0a20bec4281a5767190afe9a99d383c067b09bbbc304047544bca59a000db302c0168cf8a132ebdc2be5cb19327cf03950937e1d8018d7d8197298c28d1e0dac3c51fe1b8312ada6e8e061b7c9b982cfb56831a8697215cb5fad60043e61f8e70f108e26f7ddd98a5b774722d5cb1e44f5eae61dbd2e0e7f7adb1410fd571e0acd5ae2845ab32329aad001d70e347c7e9e0b7d35ed69d68218f2238f7118180da1dd084a174eea1c79ae952c444df23afba5fd5b80736495478dec34d8bc1528c7c9b72c832e15087c3af21c966c462aaec3cac6f7d032354f2703b9a81955645ec07ac59911c06598fc50000da343fe13a3274c47efc898884e965b92f1b79852ac44326dc80c87b46a88b36db5babb4f2c70f9e623245c2f17f0a318f271b9c1e85200234de30d310bfb24962a420a9bc712efeadf429f1d87a2bae4ab09f3df0e728ba5666c6bccfbf999a92a31c290141b094fa6a5fce50786e93a1116dba4e3777116e0b03826153e7797121cd6aa1329b22e1a0b6a1ec1a5e975aee97132090bb337bb40b53ecef63a6719147815c0cc09cb2f3a976e257617fc86f00829ea270b8800ddcb7db97e6b03958e34f5b460b0ac75be7c120779301fcca94a358b94f9dd28cf42ce00083d6c4295ef25fa537cde8b4fc66b48dfd89fdd20d21c065b28ef074ce13bd15db8cc82aef07e71cf0236e7e4e97a0ae9f0d2115a523c5c704b30e2e18b2185dda5a9837423fab18d75dff2d53c4c9ba63fad61b908eef1d1cefffe162117ea5e015b3409ac5a9ff25508932068c451bb66a2e62957dd399e5dcd128ec3ee815cce203cfdb031944c1180cd89ec2ba475129312d83c8c6d4d71059fd55248fd78e15be85900a1dad0d98a7f02ee38dd6f23475de6792785d190cfb9a59ffae6e4556b3a3dc69a34ae004f74150a9d1e209a614a7299ef3a7d4c1750277ce366dc934c4b921adaabd894137d7d26ad1f83711af0fc454d3f469d2aac999206e7d13bb76a6ed63e3677359bbcd78c92ad24d165be0a74ec71450423614f26499ffbcd410db308182b3f96e6c44b60fba26e18ce938ddbdaab31f724e03db1ae764fecd5fd9f1302c36404007bda1c4a9b363438e1d117a0272916d9529203659e15fb22997223c5ae4e774ceaa5b2b92db7d110a32c68fac88cad74753926a6035077a71b09fee9758f7de000f9836180307ab66907f72ef233e3345dc8ace97b549597b7ebac09f947f8cde34502591500438d1e60588d977b0712f11fb5c8db15ce6c3def67f8b4c264db08c717291ba3d867d08e7ee6ec40559436383e20ac833ac0c60741bd0a6fb95204db7d3200cbb655e96f865c8b6018c7d9fb3cb5f15d2a2137857a0ef3f57b9138c6d0c9e8ac8173d6f73e430841d6223424929ff787f0e1ab5080dd8a88a36aaaba5d81433b27c2a3881a7984e52ddb85bc4f6ba5a7e96c4be3531818745dac200000000000000000db03f03000000006df559d336634c0ae7e27324b9090778d28a4a5e5489230c7564be2a9d4a2ffda8c12f64e3228e79d718b7d98fcf986488b1591509f315c10aeed0a0be502ef5afb54f83b9cbcc5390874b3c3714e147750651fc04f26cb09642b33015d4ac9900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b4ee940a9ee50535115bb897c99a3666cc69788c600241f6637058161828d4b56a6f405d865371b5c01e81a8bfa87ba5384eef19b1264db79e6553959c4fec0140d1da4eb7b5d219915b45e0f439eac709254dc84de63bcb02c51fe6a20fd6212ba1f855297cdbcfaeaf7e2c4dd3dfdaf01cd108e212e659bec27a825f1479a4020cb21693184a0e7aca448f39f4cab95895eced0f2669a23f14cd3fb2f4bb12bc032335a9d44c49603d5881b743d5db7a1a313b078f6b97ce79293c650a3576ea720a2e032430c877d466dee9880e746d0a6d85d304d0f679f6fc2b64731a22363a0f147e4dd875182334c606745fc33b707ae51dd6c1dbea239d381454defd82a6b50200b6565bc09357bfeed21fc898177d6e2bc1327499a6514acd575017e68366320314cfd3809cf7a8274beb0a266a1c249681d4cf9510af16d38d68178b7ec2bcbe0200e3376c2d1072988afba98804728464d5d63ea80ae04c8ac8f55271a6c03d12020508746abab563c637cdedfc92531e86794e5406bc85dd4a41bfa816543a78fd02054a1156591f3bcf85e140e8989aea58377b8958026a6fad0109c920a97e49933069338f3a090bbd8ac590a32ad4aaf74c8c6eac965f5dc266eb82bfae6fc4f073296dc07f82363103508a14902bd6777e8c6ba22d74862ec92b654796c7ef33214c427a9cf63cbb5880f3edcf21fc68b00e791e2187ba9cf5b02035ff0414bdadf0aa8dd37c53aa46af4e2a1d78178040c82163663b3a391e983198cdf04dbc79b48e8e0f930e2e24e4b92b8280ffb9c07e215d33797d1532ecb702e7645d267bd8961afa0315ca8a62153030c9ae069a6f0181f90569bb30603334d07d87a859b7d470286db6c6a67c3b8a91932422219299d18bb23e890a2a710a77f9ea3a17e7b276097c0d3c8f836a2bef7bdfaaf9e9a99eb8e7ca2358a83731a288bd880c3c0099d4a20659de38c24f71a1bf3b1932105db4b06944032cb3a0c7b13e2ae836802768e60c12ef5f928e8430c4439793caa1a37c6716466563ab69a34efb008a38c6f1806d831bfdf41ccd7a71cfd531090afcafcea670749e6eb7b1ee339fa831e529f376fc881b0b98eabb22ae5f0c39cce81af76d97276c7fdda5bfa46c8dfda9d0142ea0ae8aafa512a0bd765fd66b83751c4caa7c485f5d8d1b735d9b1cf8274b8aa4d92fa19dae5dd6b5cda125a8eea85caea2a400cbb037bc3460b2a498abe5b69ed3b1790f62e13e3ca885deaad8c2ad088794f80a92c91a68bbd6c7bf928f15a0a51d77235cc32b1ba24232845b523c98ab395aebc2a6a6bc32f5cf3f7174b7207dd39de6a25acf0df17b409bf598ed92891a80205f822f60b41888441e4064f54511e04eb503f44d95e3c9ff512b79c0ed80ba30b3ed66a7f9b3c7f8ddff87bac386da1ef277640c89a8f421130cc5b5d547183dfd70d1ab5ea5cf6e5a0a80580cac3cfe60b3a97fb9479f4d0d1c853b676115173e9658720d54ae2b32b848225f468dc7fb8cfa009f6af46e28b48f72c7533329faac41940cda6230cdc9316b01098b1fca04e95dda4d94445cde83eefc0fd9e5cd7f3e0c6e07bb9aad89ddf898f233d08db81c7c810966930bfb550c13c5293127a1089d8c7737d03848b613118632ab80083757124d17b3120b8ec62c9b3a1fc4cf4661d29f6588b23c14cf6ff657bfa5bd28ef1d044bcf8f9ce94fec39d78009dcad20eafa4c351c90054459a47e3e7a9f5baabd255be541c543d91b9da4af1dcfac6de50a4ef14df968be33c9b5cbccada349f6134ed55bf0214c4391922d9b66042388cd20220072fc14929a01c710498d4de3f2978a31e36a525e6619aefbbfadfdcbaedb4a2e5c14491d824775cbc271289fce3c077fb8e99edda01137908abcd3cc5c7ae266c30f2185e10eb6dcd22a32c24af9525c29e2853275792854b85a619747e26decd7357ec0b8fa60b784b86c98f2ee0f3671537377d317981ac6b6d616b548198c9c878f850e971524997329334331f9620399b82b3f0c9518871e46220358db89f8c34461bf8aecb91ac2469cb235914d9855e1d292afe0c84ceecc2ceecd856bf619de7d07bd2d2c73876ed1e6e99745b083d90031c8bc903c95419c9b5744a66638f6c380a3fb7a14efa32e12839f57b89a95391d40c6e771edb09c4833af71b24eb2a32f053fa9fe0155bae9e936da618860cf87856e71acbc288b0f612ceb6705e6e5778e693ca3d60a7842c8a30d32b94c0d7bf0deb960212f8b59a300d32c757b1793a5fed3aab5f6a539127a80732073040980c342e6c7e48acb547ed4b6770a65cc5866ce87650b7d9d61d4da38a3f8cfb0702102b4f381a33a74b779b22dd8b502236da884db9d3a756c7e0c8d90344052abe8be2372135c58a80b39cb3552d965443bde72581dde1900", "", 0, 1724503880, "783455de6f1275b80efe33de701f5d86f4f89566950ecef66a1605d432b4f2da"], - ["fe113440034ae3fa311582ed6be7f87bb39242dff04a979f519b65c36bff4fb6d714f2477f030000000453536a5166c82cc766bd97d10dd3d06232dde7025d9ed287d33749c35df825ad4c22a3e530623822010000000463ab656a6c3891435309bac9ff30a67d51b0b56ca3f8a8d1e805d9f28c65330a6b25e9ed852bee5102000000045200ababffffffff01dab4cc0500000000066a5263536a5143295e300188bfa202000000000000000000000000aa5b78f9267032fef731a7e1e53697dc21cc76c79cf3bcfafcdcc7ee08a3ec46e08c126d16108ec7ea66dab632ef2934140bbbf6efde1e21d85ff16c02d48b55c32bbc5c1666495cce591e2b12e7cc9e5ead7f258c33d51266f02955b3526a7a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d8f8c75db22c80fcbdfb1980a306d8cad450730ee4872df7b6431ae2ed6f277836f942c62757f2611a09ff0ca24d4d0f51110012e0179ba333be89733ec8244403ff69e85b870595a7236aca26245e6330f57be18f77ee35ee99e80bb7ab760d4761d22666642133e354dfc12658e5b9a25f3dfa704bf182da181e3c0b0d278030c27833eabc79b7b6bc26cfbefbc683837420f1f00dd52a7743b16098cc7fe9e03139110a492344071e9ebeaabd693ba8e91d05066f411980f2c01117185841b060a25588760c3b8e17295a68097b42a405a4307975b81d503510daae674b41ba5f82d7fc9f898ec263a40e2084664c97e8e293d0f00a696da6758b31bbd8fb482390324921cc3b44925dd5a04699191e147a53aed1b1c9f11170b37839be33212b376031f2423511819424872264a754a1cb3c9b7bc05fd4de535e82bcb1b71f8043f3c0325df5693085906ce2db08917a8870f98d85ac7969e4c70c98f022cb7f67ba38c02011a1a0ddbdfa02b9cdc944ae5566b934393632a4d564e12db424e20f30b1fe80206718d590d2cbce6c6874e517c31adcca0191130b559042125337ab61c3b2f382fde4eb216eb5c1fa1a6c79afb71a5cd848c30c648b5b5dcfa152b96f9eb1d374da5acaa779ac7fa9aae21c8b70ddd3ca81c46031579e8658778ee01a1eabcbc9bc05343c11105abb63366a7a708fa3ed6178243a2bbcd832c0d0594b4e6e97938346b2b50cf78b186d5252f96e8ec7228034bc2e639167073db4759aa9a1a1db01af2acfcb406a14c8be383debdc3d23aa21b4fc34a9fda7e6d59001f96a170f0d72764787a07875fb10357813b281a36227b81b4b4e0d5b260a810293880885e4d114f9433ccef73e9a8dec6316dc104188f8074b0c06c7021f15406cb4e41d937a6c278e86cf32dbb3a269deb4cae077b656720887eee95bb800eae6538abbfc3590d3a46f39d8b15501fcde6c1052816d77e19bf77cc8a71426e39e390fbf0d485e5412c5ec52c429ee6552d9051e076f1861b7b1214f03b59097a628b281e22e676e3c6ef513fb26747f7ed3e860a33b1c891f961fc3b36ea2f7726a9b5c44ab7e54dd655e8f6db27c2f39b7ea3dbe4b4d05473c77f2d1eef301b711995f06ad96b46f6613118893196e0068ae2bdd0a9d7851389ebef79c8e5e5d4d3fc35df8a46ea2a0f673ee458967a7f84933cf5d8047d9fcfd323bda4a43400014b369f67755078f19268107bbcaa51815ba0de53904e0cdcd2706cfc47c6b2e97e0d44f3d7b5c0499a160c8874b5ab34cd5fa025a55b8d44ccd8ab3e93d79b13012c8407aa1e9f0ce18455038019f59b4ef461279faba97b6dca8663cf963912779cb3f8d45d94291f159381b23608e0be160249b08711ad9b324fb2dc00e8df4a940a83a68d16919d2b1db7366b7081c73de093299f7a2aebc61ddc6e1c894a85f8f1af6828bae29e03dd7bb863221dfc8a86974297ad09f1aa0a35d3efaa05be313399a4e2dc8b66b531b19a53d4417300c89772fa9ce771844eab9a4ad0661ea139fcd2134e04f34ca3e76a26c2522e49fe20100e41350c7d61120a510d62002f138b461dd306ac095902f15933e8456a942e0f52abfb129b3fe3110d6ab18167c6b0905fa70ff783b5af158d0f48596b4abc2d52d4e991ab675eb642086e16c036eca4d11cb4dcc38e9d226ed13022d650c9b108dc351c925fa617dde190fc6d47f5a65c1aa020c7e8067c30e226ca0989f10a8524f752038dd60cb9f892b686f2330b3eafa7c9bdc2975872a1e7690dc14920fba105f1250dcda142c6d830dc9f6e91f9387c4fad8e123f7b54a7f6e3143b3380829e1609cb5aa8e39cae32915031e734a433d62c2c553ad8aa6b4385d015273fd605a46d819cd5842093e24b0fa19e2e7a4f4b28ea26b903bc1f10376a456cfbd5de1ebe77e7aa36d9ded9cf1c519fd3df784efc932691ba90e21740f29dfb6996bb7f18517e4f9e1855fbba86ab0bb63b0daafe68da169aec9dd0682c73acd3cc2b588f066c02bfe315144bf35f5d82c3a0769ac7eb825661d82af86465b21625c555faf87ecca4ae8ca54a5820cdcbc0cce3a4f6bd926d304de7587035fc33ded4c74aff3627601ce8af906dd19f156b4db0a9e94e3da7627abc93682bfb26fa84ff71de8d823fedbd0f5a75d1873bfe9cb538f5ad25869752d59ff5461d387f853f238083c225bf38298e6fe976b5eb40acd2c9915febe1a6a63cd3a237ddedefb1a921afa2b99b1297193505c7f01aa0e562d62d39ebff06312b5e4b0ac8f59e92188be68ef9db231c64b5e0269033c745f50febdfa7fcebcf0281d0ffd0fd4f80d999087f06b5edea1db723f3a159fa718d76d3c25fbf39d9691fd9d84a6f898026a9154e3fecb47adad78422a32d53623e29017f892557bb0e0c", "65516363516a5163", 2, -369229188, "46afa7e6cb02ed49a692b9ebebf83155b4129b1589d060853ed64fa0814d188a"], - ["b3e31548030a0bf16ca9e07cd91ca0c4320090d33b8b953f082e3b39657148c01b2b081d450000000007650000acac53ab8096a6797092af7ddb4267af210692036a85a4be737acf23191587ea45c0e8a8c18d03330100000004656a526311bfce0a3aa26732facb0deaf80d1aa5051ebd49972a605cde43786ab6b054b0d34be8440300000009ab53656a53acab6a51ffffffff02147bd8010000000009635353ab5152acac00bc1173040000000005525352636a000000000200000000000000009df9950300000000e5bbcecc49ae91f1dfc22c5e35a6be0317d54df960541a204c7c34acd3325a73db44d7f69354fb52a89a7bc810a4ed90c4434472671cdf103df6d72f700bbe0585857cae503f3954cecc23a217be3ac58da2e7408e6e8f5f4130e7b8ab4bf908000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003a702998580d9f5b52f9bba777b15d65f8bc998cf89b8558aa4d3c5056d9a2eb844e9bc396848f47f042e44d1563931569f73988f1b7b5913a501de63399b30e7670e0e5362328a44a31bd859f64135d8712794c2d73d9b1ba345090350908c00438046605e1015f0781adada445644381f7c5c439bc7cfffce8fcea68a5ebe40214212ad33e8e2b11a53f5ce0b425786a775236991280279f9ce15ff85ff516320220ca5911eec0971d3d3188dfce2287e1b7505122d7c8e7c345da5763875e466f0b25f34c727a93423bae623640bd7625415d15691a133fbedb2e7eff576e0a970b10891114126804bbbe820bb5550e2e9abc9e8bf7e28513fe7c19cec97a0989c4032db4ac6e8884422bfc36fddcd049dee2041e8af4c7d63137c4843f844f870e36020ed5065d58b5d221b862c135f2bc2d1e5ccd55698a27ac85061f403d041d70ec02130f3f2f834e3a683c6542f7dab42d375a99f0efb97b629c8e82a8e30e25c9a70224b12dbcd876e87eb9577f81acaee6a13b869814ee529e50ae934881513181360318b587ac18a99bc3fa7bf80df09b4a8dece3cf7528b2ab78f1e523c642ceffb8f95f70f2050716d5d1630d5af1aeb6e5eda4204fb25fa23be2fd638f1c23a475b0804a2ff9cfd57eace427bc2eae298d288f23245581cf8be2af02d877d66106120f18355ea15ca41685d07bbab05208248af983b85a30be407f4f38bc13b47903bd6e3970c135d54831d0ae5ca33a3cc9f1ba00468bb5918421e31077e686908534faf1f6310b64ecd3d5da0cc4c8362c76445a64acccb12fb1111f6e61deb712f40267cf3721b40e9ba39f29fd0a766b33198737919b65f89d8fda25a3c9f8d220133acd83a966b4be74bb00b34f6acf54fca5a4b8c526292c302b7b9131e2836a47e74d9511706e4df4c06fff5aeaa48681412d471c2c151f15319c40adb6cd008ace66c6cf8b9c17d41aaa05c1e9b0ed4052023259aaf66a42b24314f9ae1874c33ba41b6015461dda999f59398e1e2ec935ffe2a46bc63e25f9f2ee972f30edcf708379b15cc6dc4d30a8a19bf8794b7b82ee1b76f7cfcf44db84736834aa28e14cd545f3bebb8ef533f69deb34bf047e86a8c2a133cd38d257b1b81c3bb77194fc8fa62af5c2c018c5fcbedfe74d50a1e682834a3fbc920583eedd7a6725be557c9e9fd2cd1da9c29f487dd2ca52fcf51e09b9110cd6bd1183f3b086a5d17486de07e96f044c1e042a93412b9822005592cececb00a4261d4e254b8e378ee7b41d06215d0dfeb8dd64357cb0b29ca47d09b8676a05823832a3741c5907f1530bc70e7c09e00cf8398af82a11c3bc085d50b29d69458df2c69dcbd1afc7c3cd79855e1c021f57a4b7d1e77ea36f99c1d4892c6b732d9ab22ba7287cd93bdb73edc12c98f0b1b3ccceb45c361d158e1c4c6cfb0357db7e00864359f04f5d202637402a4a590b5ec881f9da2bd2853dae4214598fdc78a9399cc9b44772aab261eaa7585c278dde91384ca853e9bf75199cf24f4e436700e0c4a95a178c1990b9fe0e523c919f310083736dd6acc29ff92b73866d303ec4ab624a1fd06c0c9c1bc8db226fe586d13b8f66107c74b95f0da4a699d6e550101ab622ec8aaa6cd89c5daa69ebfe969c500c2f98c41901f699c5c0936d0acddcb8d39695e15f63f207d17c4acb7f1667a124817ea41b4f5b70048e78c2b158dbfafc8342e2de3a1be2bdbe37e6514312061544abec2d0c2896ce81ef8bf9075dd19d8d7a752b8e581948659d64088fccb92a2009b50fd0183bcbc0182f4f6dfcab4e041e93962f6de17125e7af4da1ca55f98a504996a711fc9027a792113e9deb07c726114dc2b475bef8b0222ae91c4d39125cb9f26697bcc9f3336accac371ac0f50afde8e259d62a430c1473a537f28b248da68c88a5dc55c946c5f3a45c60127f6e3706c784e5a40cd523cb5cc9c034059e069243a5b6598112281f481a2453aa760aad5ab78b0914493de15eb96f4911f39b295bcf3cc198ea9fda52e36727dbf780aad8b3341efa0d0e8fa7d3df3692873dab1641e5bf37105976607180bfdcc016786b74c8d2f5c0696f1f68c10573b0bcfeb01188d5494b0eada33cb40ef59587c14fab6eb4b2c458442b9cb242f7048cf6ed02e172fdff14b9ae9f08210a27d9fe42729c4238d4d555ad6ef5b2724042a5b7b79a22dc1fdcad4d096d9139a7a98b7e75e8ba88c4790243b307c3eb04f1af332ffbc395a2da6e13873e8bee4104b3bc2b41ab4f8af473f6628f22a4192967ac7ab3ba01a105000000000000000000000000a6f9a8675451aa445b35ad6278134857494503cafff9126cce1d7b2b18887b1aae336f6f5826842370908b91941484e9472f6af1d1fcb4dade6a0a784774cdbc7985a3e14a3772a5a867f9bb8953ebed942e8be6e6d2a01da429dc511e1b9aab00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0729b72acfc2546f3c6764ab5eb09749c4da233feb491c7ad2fae83f0f2ac9ba818c7d53dd77a77c3a9446cbfe268a9fd0f6bbf6e3d728b0b7b96908adcf4fa798d777b472a63e6dad861044473dd045e2fdb98861fb70cce0800751a00077c17f6e8a58c95a8772f8338c6825e75fc11c67d325824e136d470f802fe31d1a403157810aefe12c7775ca65d5fc1ae6587bf94f69c1db5c01ec1d54f6460da8b91032168eee50f594bca1eee5f603c4764fcd3d823b7b074a5c965b4136fc72d55b80b2874bac85517246abdf767a5d29814bea92b070194ab9a2b3844ef57f5c265a81acd56ed1ef2504ab212407eb8b99677725285e772d0f340e8f057bbb4d10f4b021829be6eb8447eda4caf56da5e88786ea0114ef628ed4a985241c71e529e1e32022ccc9ba437ab84b545144fe56b804a37d8da16d66d8515cef4de4238113ca6a103052af0df8a117930c218cb5e002b3f3ead0323c1c548e17531edaeae95df9e71030eb7cb869fe88a89ec4c598a8fc133ed7f62d419a745f2eb074ddc604cdefb820215a019bee0e44560b458fb2d29142c92e95b2e0c423daf0afd7b10bba767fdc5e5113842844fd43e1d8d98bcbb7d8a2fe969fe38e5cf92008c166f495be8adcb07807dce6b2fd23111f7a78410ce7c2fb606d2e4ad4cd41c8f3192532b36bee1218ea6f877449aa0ef75ea0fa0388ccda594992cca6cc54009f88179886e4eec6da3700b592a3c3f7bd6ccd76aa6a336aa5fb3a3847193b53f63db4e0aa37e36bd4afbdd3553f61c29f489bcc3153d1c9d6d8d50e695b514ffef1d5b3b79ecde2840fd87ec314de1b92f118bdfd81e29c94327f1f977d17420eb482b8cc99818e388e1d3977931b6f9d287017177846643e19f657028ab07c78f487ea08dd1e31645522877092b2d689492a05654458aabb132587e9ff23b6c72b486c01673dc983d9ef1aa7404561bd1ef1fe55054891c558e58232721063b8e17623c00ed3421539e125e6c4fc754971d2ca6b53ce6fd798ac17ca75cdb21793e2a84a0aa60e810201f5f806a63eedfbbfd60d1f5ff1ab744d4881239e47e4e0dd6cc314d74bc36fdf383b7af7853878bf875573767f55bc28d2541e27756a8093067ce54ccd3f4a99d939f6fa1e7efc499ce0b92f5500b19c1fb04483a1970f7671a563f9c69e0f0592ea1c017c7d826286e5a23ce23c7a43bcf764dfc5f82e48894767bd66c9ed7b146f7392f98deb82d85a8ce498db5085cf9619598e91d42f1c3fcb7e5429fcbcd39337df80a9a1377c6fd6d21a9b3fc9d6765521c1b4243e5fcf75957eb322975679d000d0c85486c61dedc01939080531b57d3a5356ea5aa28d1d8be2d404dedecae819278992095d3f16dbf6f15ed958edd4b730e9672f58c0a54d5f2193cb4fa0997e291b9eb75cef05d1034602a37a9364174ee905280b93ccde76f9c876ece1ebde32eda008c22670b9a0b6190bce43e15307ee210284d17dd7b15aedf04a0dd32b9bbff73b1dda8cf066943c41af7c5f939c5bc973e44c159344e495e4223f4bc9d288d072882410b6cc0363cfa9f888a99a969fdde6f25957d4d2fef5bb1c1d28911c031942b069ee8d6d6b99b60d84d98aa7b1e1383b4391584156cc430ad278ab164ed1168dad1e22b8ee90ffdd21cbf9dfca50829d31bae997cd3494930b0d0c6c75e23bbc95557049584f76ac23261e4c01e36d3f05fb4a208936ae2113601a5e45152acc53d77248fafb36036a2041939582c2648f510d8831d3e47d3d3e39eb76e436ea03ff365138bd71168ac26d481078113f4e1efc546b6645af30e30ebaa9e35707bd39db64ea4b6069ee43525d7c5fd4d9bf07ec1830e3aa5faeaf9b1dfbb53e8e1f7a02f182d78485d60d02a545fa0c3b69cc30904fab3443b5015a101dbe7b62f26156b2de5d886c2824701d68338561689462e7340b996e27ecfe28b5a5189d46ab5e14dfe1e818f1a6ac9c50f16f478701f0085855bf8584ecfae3cd06d950814408455067c8e3d33da0253df0755e44d48ff96308bef8b1a6d833ee52b800b2a6e2dcade795d8458249edbb211c333fd8a36179f47928b73c27cab6356cd663df570697a3f2a44d6abe8699264ea88be0aa4d555ba7482a8603c9792c5aa10c244563458f91b7e8fe1516aa50f6063a9e0d2cb53932acb436c23a4a240c28c08f6c9b496e4b45c110bffab4b9297bb4912226488f727d334741cc5f46539a220bcda8b23d286c8ee0a55789b651a59c807b504560204d9a841ca564dc9d89a6af4b0e61e5aa7f7d29779fad903be3ba8bdecb3decb95b8fb76ed9878a6dac90b0c19d92a6d1365dbef47f759468091021470319ca85089497aa184a36ed79c160b6dd390e0a72b358bca5eb9a2469c287b419d760170e690469799838c82057dd96705", "6352", 1, -1980597732, "b6cd18b9f9550859ed5d3c679b348f013b98a1595831114351e5e7198576be56"], - ["e0df7f38017cbeab32f875c3b450d79af64b96f86e92722f83f71d53133a43e077ae504e490100000002ac538979f638018caa1300000000000151fe28a4c603108f7a00000000000000000000000000866a7d513dfdfd00d91c733a9901f891f90e32261f08c040d91c06cf074db8d764dbae717911c5b04e25bf5b83b7f07ca3c8a125f0caac375a736601e381fc752ec516623a4df64e9dc18ff2898b7f23c8f2bb5a5aa254726d33c886277f05a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e1077421c5d3b2ed6bef86dbb97f6a5d2b31e23ed339949434a6c5f5087f065de138e6cd8f123b8a6de08c85d032a6132dd4ccbed152f6cb1184d4b82be3026a072682400699b17aa4677f9d472d21c204e1f4ea85438eac3089197fef7c8f7aef83aa6b75931919a1d2f168e33c388fc1015c42c54f57d145fa8b4223375143022f073ca107a36bc5aeaffee298a02b791ed929fdd144f56072a3521f481446790212d23cbb81607db005a431d3ee70063d36a7ae739330cf49316499f95f84e0fa0b0bc1bebbb0fb53872485633c58afff61d53166535ec73691d355603640cf44d01548fb99e8b76b6d69bacf580087f76518c118eb8528a54e7b3d2fd21dfd9b8603035e9fd9c96a3ebee0796fc6aa71072321efd424b6b347544a840f56bebe4564020029dec1eccbbc86418b54bb1547a59ebfa2faf5949e760d90f957f7cad16650022292a49bb31e1b32c0143242a59b1820b96757f936ab9c05b75b1369cebc14d70304d92a48c1aee00556d2f45d9f66bf367c3ce47b35cd92f50360f312640b2dff030d45441971830c7db0fbbce067d8799c9799990014ca57b734f5a5a970f15212125352a946315dcab21a68c075752bded9bf45806ad8a036272d232b4f76c1bce3e4364d68ab655c3ffebe6527c59a644a4beb0f0b59917b2417d219819f1a0b3f616c388add094ad666444db215342482b6b957d9fd8e71a8100f1086ef1cd3cf1b848e6fdc0e2294c7383e91da35271a9de4b3049a113879f0552e18254af4d4a028a715d5963393e45401b26f454a82ef5a1dc31af031d17f98eb9f6788046f483cba084bc5e057e11a57fe4645cca39ac6176acdc6c575ac029c53c29d8bb361e6ddd62095baf95104c9b885a72ee66d541f374fc6f1b10803ae84e8112c761274cc150ca18665f6faf7f29ba2f27c3a1718a572bed62eb02bea2ddb8100aa972202dfa522ce06dc60294f547bc3a84094aa718761856e22a36574c37753cbd02b40f562c879685806568245f5fbf513b36ff4458647393c8e6c2cdb2da893d602648de1a5a168a81110f6f1ef43991a2b1a39149d1c58f70ab3ccdaeab26cd304bd3e19886d913d73499e3326ed79ccefefa208e10f9cede5bab65d30b15dc58d5296895cbba5b680935bf7f6f93a1edc8b3857d8760c1fd69fd4958ce98895ff4663dff97a91b25a4f9c9758fb26eb05fd194c4858a10680bb8d04f524ebd52ae8633f4d918acbc50b0e2a2ca61d822fd5ae3e64edf01ca7ffa6940c8795188a4daadf004e2b705e28f00ec07f5efe4f3ef0cde0647717fc7282d9fa6daa463073ebf7aa9b9eb69d65fa46cc0f52d5b7b7bf27f37e87c43ca512aff13adb54583652f39b6a96b3977411dd9bd71359c7100302e7065a98cc0ba631eccfbe615073fffbe8f9fd796c86326461377616c13d221c8d7fc69dde882179381900241f4f58331d6b1edf7d54d9e82ee3bee417b0dddf6c86271dd5ef8d4ab75b2a0024d34893e26947ef5be1920172a0e5e99adbd5577aed820f3440d2adf9c09e64febf594e3699b45033ef709bbe8f8b5df1970f3955a3423ca1707dbcf4056f8409ecc6e4bf292bb897dd0ac864a542394276cae44779357e18f2492be2904ecb202b4844e3688f9c08b3adf1712f561efff1df6d5ed1c56d54c97f12b8a86c75e534ff32ed1687586b086c16580d879f9b743003940c5d84ef1108ab183b14012af9471b91fc1ee39570428e621a5ca3fc98f3bbe4593bac00cc1ca2aea169b176a7ba4d5c0222b8787908315337e41ce1dea9839aee492c5efde4d697bd5876d6fb7ff91483b835b9a0f94dad71a728f392796413898747989865b2b43ee918ba9732d25f8e9781a284c61d4cbe923092f98b9289b4f0205c4a036729d2fed25e39eeeb6b13041e2d16c51518e085c96fa056768cc39df4b64f2afcd5d0143abb0b1ecd465539162c2a3a3f6b2411b2761c7fb792b957dac845cf285a865f021b600e619bb07cc3333e1b93e403ed4a019f4a3d4884a757752ba9f130f31f34a50c7cb63ee3824af3a15c0d4eabe346dece45b85f306f3d41d6a276e8ee50347f4af493525e243f0caf1a28f9be8e63451c35233f765fefa35ff88c3eeae0c3d3ee8202c1407655362d12492b62af01386ea12a122817fa2dea699a2c0b934f3e28b444e567e42c0835cf7bf91c8e4b9c14028bd519ad19617508d520c3da2ce9f7c32d778251fbee455a48003da694ef07003b697fd64af471571b358fe53ade34f0f9b69b14fcb8a8e30b1af1b1e200000000000000001b5bce0400000000e141c905ac9e54002b8c495ef2c08213fc396b3c2f2d08e0f1803931394e8dcee19818f0f646fc6ea205cd7dd821100c89307aa0a08512ce239621f6a660e0d31aa1f34728e87d4279b8fc0020ae329986a92f6f7b1a63896d7f1e9e062f502b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ad421b73a70fc37957571b9e671bdb382a77d225e5f03f04eaf1dfb445f7f8abbb47bc5a236a2b0eaa9a1b66b3bb475a019a946e6d48298f25c5180a9ed63dd70e85686aaf56a10b9f97f06f2b890557a1ff76b7cbda3922df6b9300cdb676dc06e884fc2f876c75b22bd630014eca7db7ebc09501934dfacdd6db70716ffa9f030a762acec399de33b67e5d677a98704a260cd9cb4973f01961ae4719274e45b0020e3e83b9f870dcb791ac8ccad2681e655aac524bfa8cd713db34fc49b525ea2c0b234fc735c661f29f0d46e5e87ec399f8a74d96e26c96f4c5f4bf71710eaea8d11882249d666b1a035f690eb1d72bc4f35e965fe860279727b29d1e8191e094f3030f062b048c16910cc3eeffd59fe8fffbcfcce40bf42a29d13adf932cf342f0b0022a04c3642d9e242eda204f9081a82d3010e183e399b1c09cf6c5332e0e0d1366021afa46ce8ed9dc8cb0fed99b80c09c9da6d5ce0a97b897f0ab441759a9f4b4ff031af9b13de1f73b4c59e93b1029b406d7ffd80b03c558b93f735d417d5f6d20ed030470736851fc2b257d065ed790781db6690f15f2f4c6494f1dca2d09d20354c8640fa6a6a5a6577d12597b4d422ac93671e50ff0769fc16f01a49482ebde49066390fc65f3412f7fd03fe8f9a4b63cd08c54dddaefcddd089f7463c65580e6ba5c3553c4ff9476128441544ef643370a76509adde29b91cca105f38bcd7f67251d9eee6999f7b72790d989e8ba26519de4b68df1aefeb59865b4376df0b0154a1a0ced988aa542bd236d209ed4e2cd4e23a56930137edaeb2b51ff1197c4bdaa5fbf83b818fda323f2a109e460bbe5fc6c53532f8d6671ce0c3054a02c4d9fbfe084cdea2ef3ec5ef49c687be636f2c0f59d93a30afe950e5cf416b418dce259280df7d777ed01a9a41e81156c77793c40218bec8f0e8e5cdf9523083e12c02b1bdb29747127b478e943e08c605f0e615b5e5e42f888b78e67ab8b674aebae2f366eb33069a38d6a0feccf57ab3aee11ebc0ad8cba4c3bfee0c381fcd31ee2e2e8b6c96515ac20f6359e3c2dd9c269b4324888b2d3afd5da436eb859851405fd5f99f7bbb5df5dfb736cdf029e1da54ff28746a42c90823cd146f2136faddb1c76421f9f9f3014b4a8fb8379de82694284779cbe56548f0cd70b8e30b24d4e676b4ade7731d338b2ef4550dada46ceb539358673318e213f178f6672731e3fd04a9916f1b64ac2c66947a9a89d81986359f2caa0219a9212c0bd69efe930b3a1d626a0f7c8241d1b74971df0de994a9abe20aadcaa41cdb4f89ba8c9d0f01f0be76d7b9a37f8f7ce1dbf75dc8dd1a4dff6e44c2638e04ce0a077316e72cdc7588a62fa17eed19ca2856851f467de44d5d5381798a933cd36e997bdf50ee8eb9445e12a60e94a25bb5655045f5f9f49c4d909cb5bca9b9c134421cde61b41e26ae7d78a40f4bd4253b3840dcd4a6726134293a936055299b17dd0fb5222ac15babf1b1facd7e2f2a06d1200ad5a5832c653747eec9c1e2dbd2cc010987da76167c73adaf4627b895aeec297dba41ef06acdeb7e9d9b9de9f0e0f968c8386af6e2894de71e277d1ca6058b21ed01e1ddeda759225ca17b9f861029c0c6accf911e5d0301e0df0ace792f85c17db7b2b7343ef3e7a3425a6fba6367ebc82c25ae8546faa2d166570cb8e3c0326193d426cd549332736238451eb56ccdc62670033fa79ce6f05af1cf17b2a11c6cdfd8f3fb18851d50d80b3657bbff81b0132d83b3781e67c51d75331837d124fc43d62384a37f3b2f9d6b2c773dd35d81f07d5e5a79174a5396f318816a7930a40d5383841fbaab8b66dc78fc69a31f278d0b7c4913a582c2b267a143ed095bf5b09fa918af40c071657175ccde77d43832ff6814146a635bfa68570f1235a361a02ce7d8688d22b315278d68d6a3cb83fae281c5860993ad3926e3cc54b3a33c8d04e9b3d0cdf1584d10829a6805a00840881ff0c9d744f1a41f3ce078bc3bc191b2d6904fe96ec5e8308e173f427137171ef6761c94847a6902b2c1ac58957413420ff1baab72ea513501395b10a9ee863e8efc193101b79701302ffd0afa3f9f6ea83e99a8f6fd0dd9796ea165d8ada2615c21eb6fcf90698f942947fabaa528f8c65fa26f03e9efbc9ab0e3ae050987e88d2cae8f918f1e273c687af081e702e8d680bff4a38931159a3f7ecafdc64bac38fc127b8d9740460dcc3c7d580667fb7c6e2e5a67af32604aa4de80d94560b8ca44e9c0551cf668587b6a3c5b0306fcdb5ad5c7000000000000000005345502000000000a33745f5988244a24c7ad5632bac7b47de0f6b72ae1b64c25e792aba8f53790a3ba832e831144b83f28a4f476fafc731512febb2b79f91a0f2e5f0663e2381e0f44644d0aedfed9d5e16178c48a80c0e990426ee9242f03e7a2daf28264d034000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a04ef042b441b028d18dd8960809f8bbcfd09208091f95668960185c07de270e3230cc12ed3c2b931c417f7dac245ae6c6abf935f0bf13339313b7ae2fa83abebddb48616d12e675f6c75d5b0df422cb28bcf107b63ff6862e2e2d10d9caa085cb63fe61aea8c5df7200d87390ecb153a99fda070ae363e75fbc209a7846d7f020931bc9598d2ec9e59e1a6a7c44b364a7cd7f5cce4210e71df984f84f2b32228021308172436a567f09caa43c6c2b7fcf034930fe9fb1552b1797d89b0936349020a0e1b974ffc1885b9003d51c791cf96e183f4ab5cb19fcf50b1c5336aaeef748a0d4bde532946be11497b2ae1461aa5e83df58bc9a0c4f57a24dda828225bed08020e3445e71ddecb8e363ac45da8960b60f840d5128008b2cf6eba53e45f773f400326b881be94b123012cefdc654c46c88c45fe35bc72982018e1ddfcf97ef68e6d0211809c82c410e90411380a7fb8363444534f7a05fa3f0dac8965b9a203bfa76f02205a86a6e04195565ea20c2b845b843ca027f60e75166088416ff83d472a9b7c022efb355a6c848628f0501f04a5a713f7fc9eb0706bc72976044d2b48735716536b226b4d7f5fe9c33f30bf23bf481d590bad02867604d5889b8615f112d7fe43b1d54a25399cfed7dca810054aa539de50faf29e82e09222c85b6a8e7a8eb05e64c2dc0a657441a2e4f07d8be5a337afdae3327997885b8debd4b196d39903852eec0ad304e59f371add16ffbbfd75052fce34a737c617d922213247af4b4a21cb20a0ff41ac32ba251a35e7af995c7ab8f693365e837c6d3a372c2defe89403f35fef6fad86205fc0625cab3547869056ad7473966c3ba966a44557714aa3f3b43f71a3de02a5a0dee4788855ebe225c22734ea56ffb65f5643a6361df967f9deb99eeefe1afb1866a01903cc946c6a029b7868f0e1c6f7981a80584baad2410d3329e45d33992ad467e9ff0839f92d1ae58e195bf1a6e7b458df63e1c5a46cdbb42d6f5cdaba0b5c040cb1e4670007f3f54759b1760826981f728ba836b02c9b916a1ce2b89928d430567278cc34319eb722cc7d769efd803bcb0399c39e0d72ab87484ac4b5f19fe8abb319a2b62c9c13a19f8511e5f21d0611e0e0b62fe9baf2591cd212cd1313e310fc75fa2033a086fce153af31dddedcd9ac95fa6baf0b74abfaf1e5f6c86ad8af3066c6b108d0b7b4748fc624c3307fd1dd5dbb80850416a847adaefe4672a31ad45b2c95a95be2cbab6da73a8fab13ce5864ab1c6d7fcaa15d31d6a69bd115f96b91ee35b2228427b98a4317fe192b3b725f11bddc311c2ef6ff724cffc4e48c3843bfabdcbe5bbdca1d6be617743efa152a9d24b4adfc15b5bd1505bcd09dabe37985751d123ad50019cd2e3aab9e4195b3bec8f8222dd429731a641cc790e5c09f9fa236e07ffd664611848b4a232ac16f6ddcbd41d27d3d8948167f2dc8808998d417f2bdcb066f276b418937557746fd03ad4da234b6243fe074e28f9850d3e2194acf122b3504a7950a9ce3fb2aba473681478d700829c852d898e6988d965b623f9fc4240d1e3af22eb010ab44b8abe4837b6f163fbace54965cd2ee28222ba33c5d36c2815482349b419042d0340e2bd456c83d6e39b93a9c481f38d9f6f2e92589b361589868c61c103b4c7a1437e2073464d84f9c063d124ff0b30f54ce461b9eeff2801a0648b5a7abee1703581206a5ae3ce3221c528ea721c4a7865e5bf59ab9f7a2f08a6b86ca8b79e8cc1c88a24371a94eddc8866daf4fa366b933d49ee4f0dcfbe41be8f147aca41167acc5a39c6018daf8a53052a9d3c900ed5649ba0459c31e3f0ba5a2f7b81ac50c6cd5e6fbfc8ad00e0de56dd336bb6bf5c7274d26caa9ddf74fe573f65880bd4c30b9dc6e40a1b393455b7eeace5c04a1a53c0927bd111687f0017d1ee8c991da76cdf3fca4d8606b4eccfbc2643edcd3006a1e616059c67dd84748de646ed73eba8d461feaf38246a73f0c2a59da3688f069944c716600632339876de315e0c1ce43b5fe6d47f8273f5eb04235e5c1b576fc61f7e977ab6e46e00eb32725045275f0704ad28c6af718a021f17904fdc13f59edabb7d96548fe1b6498c3c84851802486a58efc8d9a4ffad7cc405a9ac041445c9790521d23ada5d1f2d4fbee0da6fc106ec2f85a90f35530e807fcd4e56b3e93f26dca684aa06453546d94b684bf92b711848934033c9375d2f0f9ada15b748d36560623d007770233c39955756da6ca3fc7da866f44b0378c27dd675634575dced2fc44579d6515bc3c1f78889cd030ab14b48c1e12558eda92ac2044791b73f63ff09ced735cc299d1d78ef5c7611d71d1da6f0f02bf80b7d051e9388128edac3c2f28f7ce4e4419954d4eafe6626fac0b51e45caaa96b8749ad73b040e93e87720b", "6a6353ac6a52", 0, 128863541, "9d17b8ee166e4f38f2cdf2072cb72f9007e4407b4b40db53e0f41f26119050e6"], - ["3f73caa1048516a51ac43cb61efccf141e36bde3bb23fbc8848140adab2640bd358d1b116c03000000016affffffffdc0a47e8a9ac0cf76629188a28fb6b7dcfddb5d44756503df1f2f1148635b734030000000653526553ab638793c4f41fc02560475ade1fa782634a4d079d7509fa1490201b21d1dda75cb0a477c6c2010000000700516352abab52943e3c09ac0291ff4368988728ca9be4ff48ce9114b98147589a76fe47797c87e97b12bc0100000000ffffffff03038e0a0000000000085163ac6aac0052001e0f98010000000009ac51516553ac5165520651af02000000000059d8b30b", "ac6aac5252656552", 1, -1821348871, "1a923e74486b7f5bc723181d16c11390409e5d36c84fe84e4d7d919f64e603a4"], - ["ca1ff5cd024970e0b3f2003beb91a5560b5196a1644a1f8bb13dcc28292508ab94825c0155000000000765636aacabab65ffffffff1baed314a092f845ecdbe04a56a5c3ed9a3663a1388a83ddfbc1e20a8d6c416b03000000036365653fdd8492048e4ee003000000000050f766040000000001636c23f5040000000002656a9efb510100000000086a636a65516a655300000000", "0053", 0, -1881944634, "7966d2390a0a13da243c900675c0b4c93d39a0c671c3330c5f6823796d34ae27"], - ["19270bd1040a5e188346cadb8937e5d10d4aa7c30ae0da6f8b00bc035ee11aee50a1f7a909000000000453ac0052ffffffffb35db0d788f76ff4b945665faf0967ce251f9683345a90e09faf42ca0436f30002000000050063ac52005e2a1d47b43525cef005fa036e98d55e8bd0749fd263b30cc6be939c338af4200b4f4dfb000000000663636353ab65ffffffff90c479fe161bd55e30cad7b42af7c801b15d8949a83b51a8f0efafdb7eedbedd010000000751515152006351ffffffff0380ec0e00000000000852536352abacabacb5fda9000000000003ac526589720c03000000000300abac2f3c567a", "", 1, 1134438924, "477d3f629590ea43cecead36d21d737d57b9d234da296f5bd889a82acca45944"], - ["58c7fe8f04327d97d0084acd3cf89599ebd57736dca88699e6e6e4fd8b546ef61dbf5721b10000000008ac006551acac535166c110d65b24b778fdfa3312f08ce106548ab8b1d5451d0e68d661de732dddde68fa136702000000095252515351635265ab91e99ceebb7619b60f875d3397029cec3e3577ee45cdadf8a2958e47f4a17a9bb01658f101000000066353526552523891e81217bf7d7f7d009e7a1ce94a01e1e303845abbe92ebbe1b1b52dc9c5b0ce82103802000000016a4a01bfbb01f2eb13050000000006535263ac6a53643d78ef", "", 3, 1083632216, "89908b0673c687a0772784bdd701b88f984d6a2f0081beeb81eea29b64cc30ad"], - ["6f9a688f0121d26a76ebd48e458cc19722468857c0e586582a6e0a00232f6af9ca221f3306020000000452acababffffffff01b1e18705000000000765ac0053516a0000000000", "ac6563ac65", 0, 1041860068, "08eaf81ac3c9d68b2bc68481def85f9f80b59ee4ef6192f39cd139b0c65eb904"], - ["07de6e8d04533e3459ddf08070252a835dd3482c8153ea5d3184c565ed7b13905bddf1c5d80200000001524efcbf826a08d12037048c547aa3f063cf41c70a66fd0f60457f9d5b8a1fd88d6b2b77430100000008abac0065ab515251ffffffff576ae9ce06fa7b8c3e0ce7833d61286f041106c37014c9ec332a796319d7ac5001000000026552ffffffff42a1498222f999dc66a2eb0c6799a4d46e9a9f8894448413f37fcc70454afc7303000000066a6a6a526a6add1eaf010394946a0200000000096553636a656351ab636c36b603000000000252ac7fa4070300000000016ae0815e84", "655353", 1, -651918271, "89dd327bf7883045a0469af74127fb0305aeffcbd3b332b72007365d2ad51ca3"], - ["c62fc9e801405fd184205bad4379cfd51be14380865f74ffc28425fc0a6d5ce5853c10aeff0300000003abac639d7658a004bf8c0d02000000000400006a536a7018040000000001657a30b2020000000005536551ac6ae75c1e02000000000865abac63526351633682b52d", "6a", 0, -1533966398, "572d061dd55fd4ae68dff445607b76a601f150bfd8477783954a03940d1f6e1f"], - ["337b9c460249574f7bfd957cdb9d92d54cf7d7a7e0c3d75547fe5b87806684b1ce7c7733c4010000000551ab63acabffffffff6f203195836de2a6e415b6811df74fd68e55e14500cc2517c0aa7a12b1d7cf720300000000ffffffff01984ff8020000000006ab65ac536351fc20dbc100", "ac", 1, 1174314002, "b5dded3633197618f05bf1bed560e3af4112ef0c7ee8e55e022375cbb193de60"], - ["dfcad3cc01752dd9f61c10f14bdfc517442a9c7455cd4838c0d5c803061f5bcd74999ca6d30000000000ffffffff01924326010000000006ac63ac00abab3b4bcc19", "", 0, 2039059512, "0331b1358aaa707a9ee010e1bd436a0d67cd01dc5f7224a003b421022b229b83"], - ["b3647544022f27839ace0e40b03b444b91c84ba739e51d3e6a2865269de89f2949b7b5b51d0000000009ababac00ab6a5252abdca4fd972a1e679d21c1ad178fcab3c3a390cf1a4c85638e6564ee6bfd860814658ec2fc02000000056500535163ffffffff011a41280200000000060065516500512387b68b00", "63ac00525200", 1, 1409950414, "9058ae99f862a83b5f325456a3d70947287253b9af7ead16f5b04389e7dbb5f2"], - ["08080ab3017748d3b5f58657502998a921d637f1ea770bf15697b0f3da81e8d1215c2a3aa3000000000551ac515100ffffffff0399f10f03000000000965ac6a53ac6a5152ace86412050000000006636500536a6576837b030000000003acab5200000000", "0051", 0, -611008113, "1e982c7f40e9063821a1183aaa45e7bfbf9ec57d9761dbdf61689f5f863f1381"], - ["ed71727e01849d913174fbd49ab3705bdd886f13578b229c6921caa9b6d95869d49409818001000000026aac27b0872c04ae743a0000000000020051deb97400000000000865536563515300657a5fcb03000000000553656500003478c20100000000000000000000", "5265ac65ac6a", 0, 679002567, "0485ad4555486f23d3faec7148b1244d30c33e146cbff9bbb79139fecf4ab10b"], - ["16805355010799932737b72a2b9bdae6d193bc87ecbb0bedcb045e59468b4b17e59329211c0200000004ab5153512d68b00902a9eae6000000000007536552acacac522a0d71040000000007510063526300650cf0ff6b01b534b7050000000000000000000000004848cb0fe71b405abaeeb5182f0482cbbebde4d538d2c0df31fe9c269f8e3f655806154ca6916a1b09177301ded15bebb903d0bfdcb5a47d138bd105ba04f4c2aa2cdd2f56973255145b005475b2154f4d2346202e759a736b77159a07c10b0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d254d580ab94e9a59b0d81c09dd1d8f19019ff918724fd12b46d65eb576b645f8cffcc494ecdfa5141421dae8d317cee82ffe618f068d57d0a4baf1a12f1fd58391502a1e34affbbecd2289f5a4a05c425571ec7548181b2273063be3ad2ba1c23f18a093ae3f566cf7017ba2046d3cd31a95f77bb62446336a0e5b6d3fa7da40227c86d139714ab231cd38b3b43e6cbc3131c459c4c5dddbf08df6cbc79387eb0020834ef0f4b709df66b72ea5ec95d2e137da3d61f4dfccec00ada65d434e21e500a182b117641a4b8f7e3349418eea821753aec96f08ad73e28d609c3759627d4db2e2ab89f728ec2c5488aa5cdf0bd51aecd16b09e388c1fd7ff94b8efa781a4f2021cb8d49a5d5ed860aa274a19d2260a777addd4991c219120cb127d6d93ab6b4c0316be35963438281f50c79a20e8a78b5d8d276954c9677a08f535dbefa2f7337d0301050e4eb984a1f096f821711f882da1bb304c18e7e3a55f14c82c303bca5c2c022ef03c4ad281f656264392af9db0b4e8cad6ca5c32a8ea4de97f71b43475eb8e021df6c6601c2129aa95a49f816ae3a6c5703b9bcc867f3f41f214d9432f325dd2b78dbc730ed99bb2ae80e270e0f00c97e553b2894adbbf49511362c5b62e3f938303ac2cd133e866b42286ee778cf5d15fb4c15a7c9cf48b910356375ce56ed7ca62ac9bf941876189b3a4b87b715e07f56fb213797347883ba4af3c29d3cbb69c4e45edd6e9492ae8689f4701dc934e70f6b2e6669a90cca5f644563ab5b6d7fb150914d6aa39504ba8da6a3abc419f0a00a7bb293a60b1990c3bcecf324843b1d46a8e5ba10124de9056417f7f46007693737cfb03078aeb28e11bd82b6864e69f89e079e475f004547340890aac8230937ae759ea8c50a50538cf5899a38a5cd683781b40aa748b44a27ec90569e1d5e3cd6b76aa1d47ad8a8371a7f38bbf8fca76dbcf8ffb7cba9448f75ae724c3b9ae412b1cfcad322ad241257e6371790852a258c627b5ebd46424a88780003f64b30060f0ca26fe58120cc751157ddb97a7a320f479dad697b2e763448778518fc6127018027e2d7929ece21bbafcdf24675e353b5473277b9ca0d8ff4f59b85f884ade37a52dfee873b4a3e0d79a2e49bfe09aa33439d3e92e3bedd39d8e5c4ada948b1118dd65479d9490f7c30d3f0edbaacf849fb3479c5f8af5c1285a2ac613301a29039fa062680ab5cb2ebde09356c38aa80891b1e968d304a282c822e4f61e2a8436c9e826f9ecddb671331700090d7ffc7f99a72a82f2fd720271091bcf458e9d47f7460336c85f49c1338a3bff33323b28df61750f65de8ec95d17738c2c92efb8d0f5560431b0ca1e345aad0950b9222e974087b9e25377f003b688b64de1f71e9d146bce497edfa72a257f9b46f2b6a0076a83ccf1700b5e265a7e1979597f60aa2462ef26147ae040c22b38646400786d1453021b662e2957e3590174efc365c3a74fc3295275a959ca43f355ed1ec0144bc34db3f96193b9048e34c23b39e8791392fbd78b04768606e30c6213253cbf172053df5cd7f13197adb2696830af3bf8b28097c7d05ece1dfe83c334c23196ab9333ae91c569e524ee814bcd798a26d9e498c4984a68965f117c40fdc6154e08136b62faf653c2f247a25562b5a1cfdbe97d3295f040cafc63ad7834b0de8661b30abc541ddfbfb090a26ed80772079752d3226bde113379b46e4e42cb6df1788153ba41641d38a57e00cc3fb2cc4bc39a5244652be2177c57ecc967f675c5d0fb9e530f7dc115f3a0c8ddd436fd981b58687a204023a54c046cd0b736e6b9911f58a3237fea11519db1053a942bf182edc2195ce7d1bf7999c3931fa913c9fea07d59b0d76bec02ba545c4f5e79a3655813c33eeab483e2b37d81a49344c3c83fea72d75bad66d7381a1a2652d849cbf14eabd7fb30e15f822b9b5874cc6ca04506885e477de0bc5443055c683aad38593886d8d03fb36cb73b467a57e7a83747a658ef6463ee802895eae1b14241eea4e7d0b48071997679628f40af4c930ac922c46672262553c875af2535a7567350cb90618a5baef348b24b2b3e31372d313f2b0f79b058603a073f0c5f109164d30fe6b2a969582b32c6efa40b59a8b3b8bfa5c1982611c74c6faa00bb10e0d490b283e31e09708a8ad352c657c90cbe5b92e0e292567e8798811155cc5f20e089f53bb23d4020fb2bbd30b45047bf8f0a203dc5c0b29874be9568c2868ca593c51b5d6ef9527ae468f91ddc85e42d6d9c43053e4655096802462b3dffa5ba9c1ceff713551b8f4db116d3b9e6863e5fddb997e5fb8ee503583b69ad8641934d07d7c9555973621eed19c2a4c35246e27311cf38475532ee374478b626bb05f822aeea5dca0c1ed81d5414bbd9b3d8b0c5e02cd4fbe72065f50b", "525200", 0, 607907689, "ec4d92295e636458fe1217a012b5956d2ba1295318d677529315c4b729062623"], - ["db86072703859912ecaf9b5cb7e5345e0aa3460c01e46726bc9eea6b6562ea8d4d72b2e15b0100000000ffffffff5a64c671db64ff5ff1006281e39f5e62e898e4fd0f8620339f885a1b312bec6d000000000553ac655263ffffffff557ae782da530d1861ea4a5fb11f054349efff8d4552b4c9cb259a03ceda43840100000006526565536563f233c8dc03e463d50400000000056565ab53532d12100200000000075165ab5153656a0c362a02000000000500635165001f683b23010000000000000000bc40ab0000000000a15d86c7c1135a164e7f9e8e63ec31446cc36ea298b6c78fcf8544af2bf14164130d88c3d9206f5ccf08dd894896bcc3bad09303c916ca64106019567dd453b916bdbd22eab2d30b3f96d2f2e454288ce203f3d14d5477252c2163692e4414da00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c457e4c78383e56d73ad49ea38ece84eac86a64af48660c1e9f528db125fe54b0cc1e0aa166e3b899c8428027ddbc3685f79e17f52573e4a11d2b4c5a3e8a043ae3f6bf5e81ccb44a89e4160d7b3d2db799d376b00a65c5f3a6771f4ebe4ebf9dca09cda3b90483828e6d7ebe93b75a2ae4c0e4b5a19179f8ea2f68c4223bcd10229803cb626d175a7229617364683216c2b2c39e34d340ae402b478bcf68c8fa6032a3b62b166847350241e6c76a61364dc8c826228a07f506ff7f91ae0c726b2b60a03359b0ac7c3a7ab268eec95680f13fa68ad4f5825180035caa0f9881fa777011c048ad791d3c98709b192fdcd6322f3c7053f8c1656ea6289763f18c175d60c032d8b74be5b29d6b32083bd69e4c1801dffe17c85e39ae9bacd41a760cf7ce4fe0225b31c88588022ab247389c339815d4ed4a3eb8a2a0b23c2a12d7f4b156f3baa0312989d56288137e151664de563fd902dcb9b98871c4806a18e83c6abcf90757e0315888f79b02d653d02fde00c0ca1c32066226c7c27043ab16a900b4e9fde10210304a1299036c6f1c68090d3c910dc508cff1222fed5481ccd7c2a18d08ea634087f0ac4d602d7b1cfda58f5c44a0c8c1c0aec8f542429409e5a9b048c0afcb4f9f484e4d9b223bd0abcb3d129ab9df62531091cf08a1e6138e8a2c720161f3a83130660411e76502d5f1bcfe852cabb2cf1711e59c3e9dd9ac1ce984238017b9fac963e49454a7846863f5a5c924b550e6bcbb9d515ffa39ba9ef6eddb9c74208bc55fe907e0ae7951ff01f17377a580e622318a94c423a676a52f94495b4c34a867105fc19c4aa08fff63972a06819a963278a9cd8681e9c53f5985b8c77bfe9348ac2ca2addfd3cdfa41d9886a872c52d9de7943ef99f8a08dc8b2f637800ceecb7720d1e43057966acdbf5e97f3436eafbf5d11b5def466b9bba975678775f5e737bdaa7b537c019bf08d23104ead4bb6f4da4f60c1f14aeb8055fadb7872242bf2ed66290953a0077e52ec9c0531fa770598e1b8caac4056a022ebfad39b19f99e065038c0c5d33a424a8d05444a7a58486bf9198b342d2b99332dd2391faa30437ac753186766d18cae72faaa23b993610e58476909912a4c098c80bd53e7b8fec3206eaf19c473aa2c313d157dc52940d644713f8fa07b172c3943d9ad5140ff292da7387e21125dc75d4e922c2422b4eb47e21f855805def71414a871b580cb2a48e80ce4ef188628a7305becb43d862b174b820fcfddaaa370323e00d9679a0eb8c2d894dcfbebf2265584ed23ef07d572c0c66c1ae5e33abb38e949d8e0407dfa50c3efb669e63b74931c7c4b0fa3d07ed2601d4f78823b63e0a7d612b90ab4acb2e3ca58140eeb44c85c70f284043c4eda9c6a11fe9575f543802446e08d42a1a3d106b815a0def78781f0eee95e402d491bfa2022e1ceef6e9e31c6ec7d8b1b4adada3f969981ac64cb18859da69d268707167e4fb1dfaddb2157d74e8757a298157b64eb1073bb0d35326efc282268dbe2003251efe7242f4bcda097d474e45ef022a9a30823ddf7df29894f4aea4f58b0978155336c79f847d2df40e5482600c5ee6338c0c2a1615b200c4f2d1f1aa5c07effd94efabd1237e74c7c5af55620261b6eb1858a92a065a3cbee6a18effca88f213bc82d0f4b993da9bd17b4edc89535f0e138bf05e549a22b83c6c52e74110e6c5aff2324ab5e163a24a0f3af2e9072ea3d316af8b01d8ea3efdd7bc3db06ba50406816bc38d19c7fea4dcc930ebf1c5142bdd95119b70e9856f72f851122e60be9d3c8e7c534ec70fab8ea417056dfb5ff2fc4d39757ce13e5f9fc55c041a53fdbaf7b8af7cba801276f001b2d187b8a9ebdcf98628a59aa02b4beb49a90aaf36240173a3b08df83859e68bcd7d87e910c1e7781d91d891ef82118d725c300b8c92c4bc0dfa7c8aeb1cec3c1c9fb8d055997313115512e5d974f1f9e1298b49e01153bc81df6a6d8120a26bb43f0736fbfd7bf6cd13d4734f7062d70cfeeb43d7b9491ef6aeb36d5d690fbe3bc4e455493839a0403d904398ec3740c41416192cc28b163ad3eaebe2a8f9a45c6a6d9e43e20387ac7c7632337dedfbd8cd8d0df58471c4256d4c9688caa12ea0fcc11ba70cbed4dcb7259d1e775bfe2e139dab651ca1df7c6c8c6b11d4f278ebface64f63f7f240071cbd886b3902475380a0009cb054611669f2f8536a72b16c5ca0f0173e557c1e49305d4b6a2ed95cdcae1bba1035a364a63750572c5f8b3da9c865e1f25f84251cd3fdfe623af359fca62f24a1e12c3b253bf614f8c5bedf00a2ed2123bebe57e2008aca95362806dc0be8f64ac3cc20aff390df74b0b7c2603194decbf38846e2451274d1acabbddf18672cf3bffc0b4d0256b03e486c454e814e93970b878ee81731f04", "51655252526500", 0, 926312235, "65c4254a06c5f4b855d38c4b03920ace1f490b4bcefe139db103dd562a6e251c"], - ["318ce20f045c618f437e83370218f0992858df302769e8659c4bbc16da87c2d34dae3101c90100000004ab515152faa023e0c7265bacb5715713de25b00dc14b805416f8570126d083987afac19c52e18b370300000009516563535200516a008dd9cf5e5b7508fef9a1c726bcf4e61f6af7f51acf6e60ffaa8fae748a6e26db27e642ad0100000009526aabab5252536553449140aaf21eb651df525349716648bd93fa6dd7310e353fe2c060905e6afe230662c7210200000004ac51516affffffff01041be503000000000000000000033ab41f05000000000000000000000000c97acb084558b4ca90623be92f056e3293dfa0ae52d28de8f23f30e7ef229becbfc557dc11467cdef5a19e433e2bf3041fd2a956ea4a7256b706860bc02a222bc3a7f5a824ff40da0bd7fe0e878e58c186b4d303bd95655983110e809b0e6e3400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e506be44e826c4b21b26071ed8469713291009bb719edaeec2c4b0939054830aa090ea6007f54bbd819ed8ca782a2571a829c94df7a73f8c3d0ef6eaa521276082e75dd02202f736aac6d79e1a936926f1c7e17fdc7d61cb9d5a4fc8c6aa0f562287ff4fce6fd8a6bd9d7e45976392b57cec3137a0133abbb765435433695e27032257f6f0561cf6120136730a586771571218af561fb1398f9c64cf63e838f7660324fe831f0f5493693710ffb73dee82f7d38502007e999254acbded952f7856580b174a9a9c24a8e42257aca0a8098bd0e3a28ea1b0c2f684579c5c08cacac30e5405844e34db151a5ecef103379237f5a72592f3187e809b1c9f6b42b9165b1b11031f39e667cd6560ed7509571558c714c069eab0a11ac4f5dacdb110e4d23a5827032b8c9e5f665f50e28eaef22c570bcb58a9cdf5475f888fc94bed1bd87832cfa00321e9dfe1c3e8f4c4b3f8df1c1259805fe1bb4b2be156702faae9d17904617aab030f76f8504fa222a4ec81567ee2bfcc07ffb38928d9b6fa17fbd275f5ada83229020942d71db5eb26ae6ffca38ab47dcb88d6169599fe9359b91a50c49f50840df4d3dc1ea605715249d19eedeb9110e78ef5dc497657ba359c46032e2f9761d0003d45e7448e6deec27cbf62c79b8dc547280f9415af0dba24e1f1598d6cb565d4956b88065205b5b98ec7ec72e50c288efa5846888522fda2b9b7c644b39b436eda6d197d3c588d6d2e1c88b5f955480c255ba36d97277b46885e462f574c155989a8e2a34f7392cf64b346e1019e3b736fbbb6f6a28626767f29f2e8d6f4fa8cd59cf188a4eb58c2f9183e640b6daba0b0a5c638af77ce4d9f3c9ded19d8f2ac8c7c830cc5426373177b2ad83711c6507e27ebe6a433b5904f3e74ecd521b318569a9e657ca5fadf593369119e1521867175f056acf06bf5b33c92382ece60495068bde32bebd7719df32a2e666e8bf378fb9fb1b824d4ea5b8ecbfdf60aed476c3bd2639351ddc11160832c41d4999f8c14175dd2964898c15795a43efc9703e00e680fc762ec30d1ff1736f6b372dfe1d0379ca1edb2f928d6d6ba140d77bb89ee6e8d494a4e990defa1d79cd495c40a171b2eb5307832288dd4b18478afaae49ec62455f5f162f1c0836b44492c3e30c2164c6936113b340c71bb8193cb338c9b4ca4ba6ccb7adc93fd5468c1bf9bd3aaa41279aec1ff546fbe034d4a377acb83f899f13b260e33beed26a7843072b0b403b134e10a46e38d53bc2c65883714bd71060c6926c0dd8c5cf69695d55786908cd9812c956dc33711ddb7d98a84a68f087518f4a0ba941be3d181cad833c98822b80d3a70ffb53283164e417ff99f3f9fc006d6e5f549d8bcee3c67a0926a56020ac71afd795d58ed3824f59bad4f7cb9676ad2ec1f7fb6e840944ac9413c95f3b720ee432b94f2f670e96130b9ff383b7c350c799ca7d018c085edc1abb29a061a93b03af24eb50065e6c7a9732d8bf16cca7bfcd305b9bc1be1211a2b921a9a14252ec3bfcde02aceceb98d4b5985d092463402cc0667fe4bce2808daf73b25f93c9ad9b566b26f2bc8bfcb4a5f5f56734a8fe2794d495e0a580cbf125300f2a6c5a3dfe90d327d20635b18fbf37b3d226f60bb52ff915095c91f5000f99b1c98988bf747c4de62a72b68bc0b344fb12fef039d6f550ad6542c78bf7f9cf978f11bb7ca32b84d0dd94424cf4d51e0556351dc2dd168b9ef16575bf9654cc9cd49329155f02c46a89c5ddbc58b657d22a5905f0796083fd93f837e2adad2be5994930bccec145e8fdaac9ffdea98bfe29a58d59ae99e5b7a1e2816ae84d4a184212c61c9a3344163f696ad7fa23631e48b3125be3599e836ee6e74aff00def9d438f5687a0f18e9afffc08fa210d5199d83eb4c0c69cef70fd969b863b91bf9cacbc03839cad73ddc115849041bb243cd1dd3b543508eec6b3772228ac6456d4221f96ce4b04e85af96ae01a80a3a27cfeaabfa12734429d0135ba4ec8564b119f5811d0adc9a92cb64e07d6a6626790ebeb16449b746884b649324e32306c7830cbf477b6b86ed9df98022324aff207d99e22e8d831e334a62ed8be247023e6ebad1285243b3c29525532fcc90b7df3adcf15f9ce466956474ac6fd6d4c57852bda2109d13d160bd609adb67409b3e80ebcf3db106d0eeebdf5e5b221e413fa9dcf57637cbf8956b56ca7fba964bd350e01cfaf17bb53257ee9fc7a23d099036f6e140d9de4a5800f2933a506a50ff0056d06dd1ebdf2ae0cbc1903fda6f3257fe503000000000000000000000000b6d4a9f6bca27105187d482a94eacb4afb74a65dff5b7db7c41efc81845407eee3b642b4dccccbb50df4a7abb67a9a71b9417762ac8bdc28d3f464c3a015eb04d7eb63035ca4d3a877fca4cbd5c46f6be6f88625ac377f354e217f7be438e77f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066e6987598bb8898304c2115a9f36d9308a71aede20745595174b30e4636c8f938cd537268646d7b77c30852a69c0afa82bde71222ccfee6156f62ad071df096138450ca2cd1e0f76c6f45e26984a4097887a9274866ff4272a5ecf3498cb03ab4bfd84537fd450f82fe76e41aaf468d6fcf6cb122d0d8c5900837eefd98a64f03163b1faac5284897754a07b02565dfeae702b0521a071d3f21b99f72359b886e020a39701bc6c8f6f3f7f18946d3bfb94595f644f4bfc923a27123cda646aa09660b0f33c70149b34e4563ff32a9c276f4007dcc6964c096fec2bf1f4606834c7bee21cbb2231de723d76a4ce729c7d5ce0541c4741498fc81830ed2c3436d533e4f0310663778fbc3e54d9d9d97c8fb138946fa2d588affe39768d6677d5203a6c00e02251233484ef8a335b81fd9d8ddfee2c7be2cd410a8f02599dcda4a00bd23507c0205ce8d82e453015e99d451f720e3ad6ea0bb1935572703e97b17aad9487a3042031a947ffdf378dd824281e9ef30f3c2d486ae84786394759ce8e2435c06dd3b0f022325914468ecbda1b9af3975148c324d18eab6fdc606c22c8ce626553c57ed5710471c030d8eae7d66fd751aabac9d848cf4aad7e4c6d2782ea699247ac435eb45738f7d80497c2af1f02be6a06eae379b10fe8dfde4571046271adff626cd3f888947ddd3fe8ce28019951a349973568af64e607f3e7b7bddf9cb9138001d80182a6f6ae0a03c5d8675f6f90eab8e97a775fc2cc38d926350736ad21e54b2fd6bcb196149035f6801ed97b57468728bd732cc9dd9dc4e55301c91eadeeb9dcd14bcaee764e3af81f6c5a4297e210beba92b338124202bb6fb3ff57c87311a3d292f6312784224067541b38b4a24da7a3e4fbe0403cef8e956bebc8ab87b52a7f530c09492e3572b7585123a92842d825e109d2c791fb2e8280cd426956e61f740c0a4cfddad5335e9e9e7a714998adf61f0b6cbcd94d8602fcc1eb6272d3f9659c281b09e2f06968d095c7951dc7c6754fd8743c900e8cd7ece4eedfc58a0fd8da341a06d57b21694cb81cc23c0e1c58b0f7b3839a6db97a26826ea5e6fdf27fb936edc3ecfe85f9735e75ce20f8a78a6c856ea489b76946a70c4d1c5a0e8cea1595cd82cb009f6fc5258aba8f1c58ed4f793455c28b4b59acaa8fa0ea1b7b67bf0f99c4569ef2f15ba0e180b65c03145709aee45dd024c8ecb4df2d6f677e4dcb292d26273a3184650f7eee179eaf7edc7dfe629df2a5012360edfbef561363518f4e13b294b89621e512346981c39b192547106801b4195c0f903682352295c2fb0e1ee550cd90df6891d2a715f6d06a30b270e9e859fdaec7d32aa58a0368b30850d11e458f95a0698fc62e6cc9bd01b89894b3bb50640bebf1b41ee8d3419c795f93f067a9fac14994c61c79771b0bbd2cac58d27535e7d9526ba0108ff33f6b628741d935cab62fcbcf7dc91c63c50db5003357b6fee8d51dfc8cce0d5608b283eb76589a2c9d376f18a705bb7ccd90801efd6e9859ec2465898df035df8d391130e8212ead5143a9d5842f53d3fa45c6a4edc7c23eb2f8992f2756d716ef9e67142ea75e6965c33e511a36eff7557e1c4cec09354154eaa8e5bd7fa01be2a9d6e455a71eefb610b4bbae3ec342a508c3102bd370f5d08742726099ef9bf844db4538a8aef79163d2999822e27b7097b2b7ab4227850695f43fe02b53ea4ab7203983be56c81e29088eb4ddff7d7530605577093fa44d5ff74ff93e5a7c70db095bf20f437c0c6b22dfefbb59e21da44a0271d95e2371245ee358a9f1e55f3978366ccd5ac6af586a2ba0e0765029f15e3f100319ecd7d42cd0fa06ae7e5309793efdb65fac15b4c7659a0f35d41306730325b48ee9bffec944754abaa2cf25f205afd55733fd41b6179e385143460830f2a514b7184b832f426cd79dff83b0c68b28577757ca7eb508f77b0026b51fec9e7f7d81784eab9a28d3bdb7453178827238ece9a44b45145cd63223e5c9c459f16c2649affa5354c120abb77496d9eb9bd568f6879deb47ec7a30abab13b18f00b46716d9d0e11bc2d5295a70e752b7a5589a4b5f322185e6226f36aa7d73a87b67412e6e4d3d80123b1625a1783135fcf66b3cef1f52f8f7a30812843e07b441cfc9809e98c6a9cae1e08b925fca63cf71d67c23b650025b72b587394f49692822ed6af45765ace8af38ae0f1b721703bf113628968a7fa192527f7904907470c1897d0b9e4434b7f0b4bd9946b76e57f96bb510bddeb3ea8fcacfcc8625b10e00000000000000000000000000027a8580f9102b9833b08b6d65c3378b57faa3af2924e1a4ae42c063865f929addc95341225a990db25942661151e6c40f8422fd9362c3eaa3a1c386264d86303d9fabb9dacf6383910ed4f2cf19e25d44cba9891b7f660372bfd0c211fdbbccd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000069621c4a2eea91b6c2b7957acdaa5ba30274eca645a2b0a027ac2a2342b232208d5f4b0090802e66ba397d8bbdd0d48d60d7ad03b68c40f05e2faafb1662e03a08f9e5a4c30de58b0b0e5e9ce56ac43a247bf5a88bc81b0767353dfb97ee94e55a9bdcb6d4209791662af80a1f60eb450ed9d1e11525fc6f5784d325f4faf1bd022644255391cb8db423d1a530c775be1e3b1b964d540a9b6215e96aecca7e2eb5031e751c77787b19cf4b5e58c188473e71984c672657751cca02bbde3b7a56c6690b1ed2952351c9e8e2d760b82d5e11b51a5b19c811d1898ef45396386dbf7ada52056d450554a65bec34503d1958521c2106ee1b8cc2001613b380ac9dc0834357032fe7da9b5b4352ea6575a32d9f868e4fe96ae294c0385ea8ddd8e2aacc14d8eb0215f3e02a642f19b6d71ec84c433e4835d8e7741c071389afeb57307c45c44b490200aeef3b29325e7bb246986655358357627a55697b79d1c78ae254ff5308bbf70309449a1224487d3c9c4d55f7755f18ce31191dacaca7b1aae20e8e3d8abdb09f032a5ce54217ba2abfc6e2ab5eba3661bfdabdb241d471b4e4bb4a539805a10d454daa6a421d653e579cb7aa7e934921e5b02a1f93818db1589263ec7fc492688d1ffe8de03d91030f340982fd9d6e9a39598f4d6daf77430aa059aec779a95c5970240a4e39a6781e87457492958811a0912920e1ca6688d169f1ed4ed97e70a0a8ff135dec707700ac782a7639bdc08239ecb240e385b2853083e72c9badfbbc4174fcfb4d022866dea38e1f647b4b2b84778a55010bd507a879d7dc47bba8322fbdc14b5cf18fe8d6ce24b3e0ab8049cf858a63ecec8c4db21ddf2edb8a559ebb903e11a37fc1c4af5d3d16fdaf4d10e022aa1987ce78aaa9def206f51a6726210748ff0fe09718822ad493b239c8440ba7b7423269d5bd7301f4bbd76d1b4068586ddeb717bfab28591ab8773642c07843dc0e12a310bb685bfe4d63e44b4cf9d9d19ca5ec57cfdb66ad65c2bbd8a7536220a28d0553c55ddd02b3bea4b505baf50f40fbaf6ee7ba143ff3f7546564fabaaa53e0c9887d27d2e8cccfc3964cc41f69da8b4555823a1443f197faa987abf67fb92b38149d5dfcf3f0e72800f984a4fa4f17808c5a82829298a0d48518ea587943827b791c22246e18a19399ce2ee1201de84aa713ffc213d35ea460e19f196fdeaa8089d64ee1d2a93cf81c57e2d768f22bfc85f9cc8417f4a80bba96f248b188a5bdcc3f0bba235649f735defe5c2e950fe15272ba43b317cf3668e8064516ed0e5ef719e43e7a218c814402abb58b1f2e9846e94d1a83e9e3cbec229ed2be9f5299f02579f426fb78f2a1b630b2fa278649f4528836a8cccac2b13af5efd5773666d5d183390cafebb57b98b2840a417872486138ca99d211801217d4ff5d089b44964a70f86e14534b9b18e1f6e704d9ee4424d745d67126bc149eb82f903a7b469215900bb26ee9c35f2d751e1935b0af18500c9e0892d1c439ea2fd7d97bf94ab0cca7ddfb9b729722e42779531915e2d19cee52b2d3c290534f048643f00cac490dfda16c93d60d883661d33b97461f338b1e21b38bb4db92f916d328750cee953d46ac95750f975965b262787ff46b66e6e0b2cf2c574ca53dd1fa59c57d1b652930fc28ad517977d79c810ed7f553b0cbeb3e20008441628131040ed11076d2dae53a4302e854da14af79b900e04d02b318904d2c6978b3e59d72f7a301c9590eb933f971a7fe02ab5fd7863cd564beb42e082bddba37be5e6fda9e5f01f7e5112049f79dd310eed0a456d6fa625f81432bd8bbdca8bc433cecf18ba7df7d23f96a06b139cb75eb68f71e3b9e89d589b87e5c9c9693a2a8d475d1622837d6f6492ec1bcac0f9e6ab0ce30831a279650fe8f5a4a1d49e9283ac2b51f81d8eedea9c62d766c3c43cfc3703d6379c0d6b86609b3bed59c22f45c1005d1a8f5e7850e29090241c70dffe861d8cd1d7cc7c1b06f76a470171e00cc109f1dd07337837b40962d4162919dd7f303a2d204f4360bb650974bfbd0fe6f171e6199779f22bb891d5e628535fcbed9800bed0f96cd9fd2a1a00be89d4bcc562305ded405394ebd608c0b3a85b365fd113a9294e8c93c33e5e205b7e8935a48c3ce52ceb79d3d939431aa0b90090b5ce6f4b856992d1077606b490e8a1f6afcca1cf81c6452c54f67533389addc2fd7d8b14d94faab67bde60ee0209633aab6e92c38f49515eee1ed2afaa06e901d359ff84b1d1beadb575446e42a26d1db955bf39c074fae092aab37d16d95e00f03d2403858daa0878e88be4b3729fdf645fe32feb03227bbf7c07470b62574262872b84d07efdfc59094a0f5710284b0dabd8b142e67a72a760afb7480c20ba2078d1350a810ab6e0bb892ad81d2ec82350d", "52006a52656363", 1, -1692392259, "c53e5005777471e5f26d8273ad5fcf42d3ac85bba732733ed01e5cf6c68953ba"], - ["efae88850299a9387d2097cd9a95061af63d04b34ba8bbd3ca3f3982602b2cb8373d6a240203000000016306a1aec02478b15dced285f5a5dded205ce045d1b90b011f153cd373b6ee4a0132f944e20100000000bbdbe83102da141d0400000000015166f904050000000007000065abacab52151e6e3a", "ac6a", 1, -66816186, "4a0e56d105f7a59c672690db5e55ff64dce529430473d72fa23e36045d5733bb"], - ["44a1886a03ea1f6563a5705907c7bf657c09531197519f40b99e3e33705053393f0bc3c7d6020000000700630063ac51abffffffff03055db53e017363fa21cb229e5fc91f13112b68c09bb0f0f2c62d28d7294730010000000263acffffffffe3bf963d3a50f8622c4eaa109f5996443ccbbf2d21ff78afd4b32983e65093d40000000005656500ac51ffffffff032a4ea1040000000001652ea2440400000000009a8e75020000000001650000000000", "", 0, -1471420573, "bf378b9d49a32cdc281ea4575d6f91a1b9070c0e0042f17471c5ae0587e20c05"], - ["5225d6ab04f3ee7b65c246f2be0288b9c1e7ea7bbc53cde36e18c53d0c4bacff1943626120020000000165b47686723a7ee21dae98875880c4afa53c11cb55945ebdc4cbcac8de23e7f21e3275effc0200000002526a1c7599ab7bb491e71d74f49926b9ff0d19d1a6a4c72e2dabed64631c3d7161e5aaec856c0200000002656a4d45d2c483e92faa4f63a7bd5702060847a7e81f70999fd517c5f1d5d72931f734b865ae0300000008525151535363ac63ffffffff0287944b00000000000665ab65abab633e7e8d000000000009006a536365656aabab00000000", "6a536a5365", 2, -2145572815, "eae16c4363d59c21e137614a667e44940461e0a157f6184adf65cba10ab9d859"], - ["91fb393301ffa03a7927df7856701f833dff8177023f236b7124f651cdc790e0a4d4fd6e6d0200000000ffffffff0460dc3004000000000452520063aa6eb5030000000001ab62827b030000000008516a53ab65ac526a4f040d040000000008636aab535152530000000000020c61a50500000000000000000000000091f4ed160b4230255258639aea1278529ca4139130c7d18a806b8cb4735887f2620a5f64dfc9637b823fd2c71a8646c072435e758bcd103eabc62e6542d3abf5fdb2876c941f56ca0b838c4226b28b78ab03e9771b07ca62b2b5485d2387a85500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000baa64f289bbde7228756f48fb85d991345c1cb0bebaffe168762dcc14511ed7f4a718c474f18d046188cd4a4724fe00aefb99945cfbee6a958dd231c8b19f58e55bad371f95fba92ebbfbf58a63405f521401b116957a0aa291c3d3aa13ec83cf65b16ce7b79291b6bb85e0316d4b9e7262cc152946dc1a80985c77212a0fa66021542c0be58194e3dec43bd76faa1f8e494be4f335bf04466d2b9248a53d6ed3d032457db19936fb3ba1989125e0b6d9844280593a6a6c3f7718bc95efe092ca8cb0a2b5582a046af37a505d69c928aa2ede842a0a664f1c8307d94532683f0681203129d19f3d9d853ab9ff124b92fd593441734d28dd82c230d032b2808c366f19a032dd6aab7dbbda18b1257329c1baaa27da107f10d325712ab16909252f20315c40303b277f3fc2745cb2e378f93233b742ceeff902cb52296f9a1d11f28fd37961e022b3040ae49527618d4d52f20160b8d5df3355e60f3a4869121284388dd5fa627032af4f0ee79f1d19d05345a8e4a5bdef3d96b52ace85b881e4900e39ac6c003b4032a00314a466b53583ddcdd71ad9b8a3f9e56a03d19032a270ecf35aa6f97402f3c79bdf9fd7aedc168b3d5ddedceb2b271277d9d78e520660d60e90aa9597b661c52f544b3522ee2e5bc750d45f8e7e63c82ed0c027c136f85f9f5cfe0be86e4cc7afd2cfdb63119757a7f748be1c89ac21efe58c78c9d78da408fc110efdb4d85af3079a5dd190d04e2945be5ccb4dba604042ecfe86602cc64e4695064abbf5e01f5c05aefa7c538190f025b5b81b804887764b51afdc49f2f771df2f7cc8b1489bf5d61e64120952c3e5191e71e9358a1362a1604c28f26d895ec3fb3d13b2c1750b243dd93dd93846c6702471a0c162806ef6cb9f9369da61b6e766b95a480aadf0c174296ee3333a11487e5109f4d0e27114f360eb4b7fb6c8baa57d45a5d232b4df9da33c8c00fee5c9c626b51aed81df3b4d2532d1d1a9cd21fe3bbfa2128580b2aad91f8e9040bd4ffa5ba9d1f08db101b69b1a4c9091bbc9ac3980c04cdc10df0e2e42d8d4ab3e18d2de1aa4c14e4a8033c12cb214b5ad83112b965c21659995f679032916ddfae80b3f01c89c06835f713a4f24e4b32a3fc688a46878510c9f108f0d290d15d75140a5fd56de77fdd158236bd91d20f79a951d2a72a1ac820d4109ca64e6c5ef6aa34273a0fe7a0f71644b51339db6c11b70f6c0c17059f49f71f2bbd1cc40e191463027476c9f592b77e4f3f0cea91cd4e088bd01b4e04030d0fc50845fe8ad9433f6590ea009d5106553f18df3b051b0c5d3cab9a7f53c3555af5948add3ac1712df92e6791def876f7f32dd4b3ba69ce7783b4be28e52dff6b650860a66846eaa16ff56d7be994e2f70cff3bba2f73c092d1fa8088a35a9bc1c9a5443480cf7a9b95b65b0f1975a3e9617dc2440908c2a9a87b4673fe629e5111c0fd8feb3d41cb8afc0970658e9bef3fceed6e9182e959e4b83d38e114f58079786282f74a9b61ccb84de208adc7c17e142f1846c0d187eb4bf5567a69c8486962831b1685e53908990253657a355d9d1c75df8b92267641dd24bb6b902d1a93a0bf0e43144cf5c3f2b691f02dfa7c738f5dc869cb08a8053cced0908247c009d8fd465a1dfd6053d22355cca83ffdac039407a79eee1fe6b31d7ecc8f400ad4b99ff8ae32721238a1d2276b4b1fe7bd307937f23828e0a2b61e20c823cefdaaabecabd3930cc2411310b5e038ed416b9c32370ca6168e36e681db94106834384cf4f0d81ca5b53ceb470cb71ec47b442a709b307acf6c0a17ea5737a7e3226103a589d19e94c44dd3e0d0e97afac367e16fe4c1a25f64c0fb221c32647086a283d4edb9864f11347a41e4e1420af372838ad79f8319bec1761468bbbb05fb7bbd55ddad1ede38dfcd7de9d1300301c2dfd8f08746b2661cc2f0c9fd429a6b48b68858c1829c7b2b3810e3982464fe205a1b5c6ff6cb8610e988323f8c8e8a4f8ebf463069706da328ba0ed02dd91051c09ee7724ebeff24642775e88584612b0dd41afb70bbcb63611f7cd1817e759e0e4d261a94a8b2f793a61203d65b4b847c5a8386bd15505b132048ea9299820953e2faa107d6d5da0b237347d5a747427ed596ad7dc1c94e45ff6383eec3ef974f79ba733f71b98b012939e7ab0b4e42eb528eb1dc8b9c196f4bc65c28f348b606faf9322a86ba9215229afa0378acd05e6ddaf365858e0f6b2b52b0a88d367fbf92190dfa511a33e3d3bfe7d19570f337e123ff67b2a97d9499fed9f7b7030000000000000000000000007bb00e7a5ad29ae56944d5c37e160f6c7d43b3f31308703926957224c62af1165f1c7d8b733ed988606829e642f2d0304f7b1194ddf813dfa3251c7c16e11543f6270155c31b08086df082da6740ec419331277e101a86045474721cbf0085a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bdf050b062a3c664e6f474ffe7ff094dd30ff3b1ae9a0ffb01ba309b49670991d17a231658acfa5c70be9acdf3a212989aaa7f2a64cc7969dba13e89d8cc1811e18e0088d320d4b45211eacbdcbe82c6d2397db4e559900756689f75d566c2a4709d813a3afefce7b0ac44a4a12013c3108f0432d8985d6c1194e7adf1ee84e3020dd1982d091df9e20300f89672c3fe6e508ee651307b710b6adba5330f56ee55021ae627bf2697a75746f00b9e63c2b51675a44eb896ee970e4ff57f7884af04840b14c5d17372642cdc4668c469504ec78a80b2da207c41f674a8bfe4c4e5fd8dea0265b7f75a4d68d3495404dd9aa1b1db0d23792e9be824ea76836d8dce06c1940228434b3ec6e73aa99a4ca2f605fcd5ff0261056200cb187c216eb2954e89b12b032cd13401788e5692ed4ac1978290f816b46449e5627109e4b9c8ed617e993f17022765b6b29c766ccaec735c486fa7bb875166383e606de9e71323bdb8bbebe81a0307a01b111f43bab5a24d73a49cb65b9ffe33967460016e5a2b7c8c0a61f01e59031034b6c730bca82262e55ecf37a65cbe4765d6fddc804944604c6662f4801c037f9339c70ff6e51372168b22645c5cd98864a8d52ab19089d167053801a3ce5e7383aad54e77ca0011f8f6609012a240b7ca88dc2f22e2913dbb2b6e54868df565fd50c8542799cf5091dbd6f9144c05925d3115d7d92975a2dc8ce9dbc5dc1608349d59e49dc547250135230966ce207d215115fd7ab7388f9f539163c7cad988aa6fb75771b9d52bc1e97e73fb3abb1593d3ca7839ae33e3416815e4ff536eb9791830bdf72531f3fb64af76d9e614b77483b2cb22e1a32fbb06bba4b8cf00ee4d6ab3cc0ecb218260afd592adf5bc2c8ef0231947005d49ee0de6580dfb88742e6a0cc226a05b1e8aaff0f1233942e98a393d1a45910d6050d05127ee5dc9dcbc3c1f72d087e50769f0df7e5b61bdf6aaed8b0c5cbf0970b4d5671f09a15de245ec503b6a0b8525ecdf8756649cbda5d85a49df530c1161c91a0f86c1e6003f1ee487aa925bf0e4feafffa635574b5160ae4f730ab25d0a8ee924f81e154ed59bea1f466c94bf14bb37b9e4816fe7f05f5b12b45a18fa2d94ee9915850d8764eed413926c7aa694663611b662c9192a22d2bdb7ae2a9402104fd3ed957cf040d22fcd8caaf0dbdba7a2c87e7badcb66a57454006477ab2ece69bfa1562b4b3c9f9ff5db9acfd9b6c471997506223199843e280d1cbca5788c812e777b71b635f1fe8ad1b7191bb92438ecb67c66979f958c4e051cc210b20f9565d283c57050efd92a105d8d79d417bcb32195650a21d29647ffd1130efb31bbb5c9a2698c74a40456f79d834ad9c410e1bb4a106cd6c5dfcbcccfe9dd6b8811c78956376108fe8d289060ce12d1fdee45888961260cc9dc554efbc51dd7dfe44ecd529fadbaa11e4adac27334840b7792e18be2d683f88ceeb8195b376fb3f1bcb2a448a75a58ba726514259c3e5c974e7a2a2e504c20159bd23b1c361edadc2de7a7d436785154f8ff81e2e94ca33523c0d76d9b2852f596668abed77b48b58da003f6d74c1782b429ace7240068d7191b522f886e79b57c2dca787e41d431af883d9b3d9e8e301222db291f71643b2206c0c625adcbf9887ee221bc1e84ed59faa8adb603882eaa9340d9100adf56adeeacc0744d63a769a497bd3c2e91a3db21514be7a57cc5a9053d8fa67b7a2532eb6d452f9ea0d49015f76345788704626f707722060a5f5282e68e9b4c7d30a53b37b6048dc9c6e933d071d40eceab3511b4ef7bbd257ab1551c5e7da446915bc338e038b0ac2f52489695804c0117cbf627706a32b68a1d90d5cf566b069b70aa3bc53805e7ca7e7bda7bc9aacdb60101913fe56d6e33877f8cd3f6197e6bf666ab0b8a44b124603a10b0a2ac8b6aa4d11953a0ffd1084379b7d0996d0eba1a25b203be1ed5afd2fa8b18e9c39167c22889e34088de7c2d5d854005739b3e385493ef5cc61b2d89eb93d2cdedc8cee3b912b89ba7df3af9ba89ff3873cfb4ee40d432c3736a9c90628a31e2f272d5f1778267f5b3bdfa95223839f8b9b2c561289d3940834e5c0dbc190f8fbfa5177d08cf0615b78eb9e7b2b7b40b1b323019c19b24635429ed663cd8b8556cd4ae9c1a8a306b9e5c2dff348079dc7bb4663dd7122b25d9535f47c5a9dd222bfdea9a71f09e4c227a8f13932dc8344cef3dcaa0f0d571f71e3a2198db9a0bb3c9c1c5c8c5b6bbb591e53ee37acf10cfdb8c8df89bc9a73a8947b5165a982d78ec319e02c16010ce0a02aa9fbc1a19f4ca3a4d80ffc7b411450e915b4dd4f4007376bc42076abb0b5dee0eadf76bb49ad4cc9bcacf01ca995446a05bdb28db74b555b5510bf98f2b427b51dd330ba0b35b5dac7b6e01401903", "00ac5163acacac", 0, -1270584839, "1313d36eafffe046896bc685d84d85620799777588a4f8932094186c191a8ad0"], - ["14c77c7c0433358fcf0de5fd6293c5ba61c18f4e2833296b16b52d88d23d38e78b0e40430d02000000026a53a6c260280dffce3a7797a19feb89047c899ac487c99999523bdcbd7ee9c93ca3bbf938190000000003ac5253ffffffff636c6256772d3f6e0eb1bbe888276e11a3a4eeb1c0b6c3172442ee4095421a4c02000000025352e1f0a84b3eaecda488aaa8fd70769a1e8af3dbfb94216812420ac3fe0427fa79de4a0f090100000002acabffffffff02d98e28020000000001ac28ae5702000000000852516500525151530000000000", "52ac535352", 1, 1083571021, "673763abeb9d133b8da6ebc05b7eb25c80442e9f9108a956f85a3c7218692b09"], - ["620d3ddc038db171f9060e4b505b7ba54648d4f3f90280d6b815cbc08bc7d99730bad63e00030000000151ffffffff4a9b9d74a7b44a9259421cc17f449af301c1fbed02925be2447f7ac19330f4230000000009abab00516363526500ffffffff7fb91dc94f877d9ed15432cfd93f4c352721277aab76258e1805f9b1b80cf3360000000008535365525100ac534d013c6b02427e1e0000000000026a63f811ef040000000004ac00635100000000", "52", 2, 555319295, "90898463afa206339377b83203e57dba7f1357e65d376cfcdbecae770326cc44"], - ["1d8aba200271bf953d14197086ee4213cfa9a8994e4c0764ee9f6c04e2785676531544f7270100000000ffffffff0a2768fdc96e7e44aa7f4f237a2bba905fb1e83392eb1611151b6eab5baa969c03000000055351526a52e27ddb14018b498d040000000005ac530000ab0000000000", "536353ac0051", 0, -436171205, "99940597f26b79fab32648418ed103f6514e9890a4dc3a9a6fc5ae2468accfff"], - ["b88b11ee04bcbc12a34905acc5cd69255f394f8bcbd166be69dbb2d6a3eed2d27a2696413f02000000095153ac6aac5100ac52ffffffff3641e329c0b25e6d8a2345d03f86fa3945b65e52bbbc8d9e1f704dcc1a2c935b0000000002005283c075d252ffaf3802963749bd9617bd8aac83b43221c779c17c50eb437100f0151de69802000000046365635373100e0b72dea6b5755c4cc9d67d70c472180cd13fd9b0aad0b5207edded358dcb88325b0000000009006563ab6aacab6a6affffffff04d64d5e040000000005526a535152db389504000000000263acde27b3030000000009ab65515265ab6552ac3d3d490300000000096aac6351ab6a63536a00000000", "", 1, -1221938459, "09e2886873e26ca2c1d69a56423818c2901bb84e9d1ba4bcf677df0ffb14ab97"], - ["f6556a3302cac79ac24894bda9cb7c554af88da81fcb1490978e42b5cb7f92ef609d1a7fcc0200000001abffffffff0ead6cca7efd102b5a54ce3573fac6882f2f54d879646e0553cf4af7540c2be50300000000ffffffff03318d9e05000000000165036441010000000009ab6500655353656551df325f03000000000046d0828c035ac3cc04000000000000000000000000f8d914c282986930d1225347232484ed0c653d90b1bc0dc793371a49e245cfa58b9bcdb0aca5de37fb5271b882915f38c56382c788239611b1bda97f19cb8c7046cf4663337c1e5f00b7a4690de696107cb5a1fb551b5d95cd5455f7ef782e5a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007fc499e7ccd1ba73387238b89d0b0802f13e1b25e712afed726da519c0b3aa131fcd9877b683057578baf3d549e34bc318ac97220acb3b3390c102a5e6063a4d3eae802dd1fb4aa45a591d0cb8c4d637327b505ec29afb483ce19e6d4cfcbe3ca07519a1a8c2f42ed51284128d4a27a12d37b55220bfaba76f635a37c55118eb031b63d21ad1a6aea836c51a494e97761c2423f5ec29c593cfa936339e0abfc0440204adc64855042096006d590d99ee4a431175aa7f13d5e58908337a36d6167a850a0947ad6e5b20193e3d7a7050e249723b18e34cd31633e6db2f91cbbdf134eea50dc9a16c62dcf8c7e819c72c95a3426bfb3f1e12525021367b2ddfde4462eb5602097c21b44d941449df38e54ef7ee11fe0801227a21531e01acfa5f0e3eed796902296d93ab75c2b6a42735b603e11d46607c7a1b1823e9975076eda580a708ea41031cd6b71652316ebdabc56966f0d17b3fdc0ef2fe01539d5e13e33639e44953d8030c2a1e59cc118549f442decd9f83a6b2a47401df724a32e754b3b846924affcf02080bbcd88417fec72bdc14c4929ada23ee2fee8ac698d26444050e7cc12eb7b2b1e6837182f9e88f64db8b627d178a1479bdb0d1f5e4dc9c4ea8499c9f0389080abc8504bcf992056256f3bda4d564d2f2145fb152d84e07ca4e28830de45b089096921932cac39940ee9bc7a741a601f53d2878d95d21c26f567622a6de6130cfb968cfdbe72ac49702c472db342741e77fa863443e1b7cffdea9a83c8441601dcb23e0a49ee48fca4847e6ab9c2614be91d7f7e871218fbe5d94ab56cb6b93e4375c5e0a2c2d97f2212c8399ed5d253dcd5cfb9bcb33399c31157cbfb6f25a4d0cd1533d55eb2a7bbeb54fb2d37a1b68f543da91d1fbc0cd5dd4b31f01fb58aebc15f88d84ba296ffc91bce2a0235c7229a0fe602913fbe850cd505b2a0b8b0df28355337d9949dcc5bf1e99750820868e0eff8a9b2dbedd14d21848cdfd5aecd4ab2f86722cd70e9ad007103e691d04091bb0dbfb378131ff007927a205f0197be652db42868fdfe1c83619a5e41bbb773036140487e1e72c105efab2a685c51d63139cd0fd4594acd6023ff1f4b5b2d8050f0528e1596a5de863b94f5f26ed68e150c09d1c51532292ec65096f374292c2b13e354149fc00d0ca37a59393ebb8293a6df7c6fad4e8edb31ebc5e1fbf459243118382b6d0025a95669a080132f69943638333fe65af2210c07c7bce4ed65ccfed4c02dadc1bed3f2ffea7c3898a72256b64f31a802d5aab3d221e32c9abbb0db9a822ea50e9264a8546fb6c03e19dc265f036dd55b0de11a330b958d9ce4384debcf005f94d1928e6b8f240b762dd404cddef314981496acc815ebc21de0c0009ddc5253e30614af0fe461f132e4a69e656e4dca63938ec3b70d8920389ad3c96ceb8f86983d5432b1e7b4c7450311e10e4809e7b756492611cf238769ee7b5f37f9ed8d2c5fd23f87f48a1d74e76274a6b9a64e9307207fc85acbfa07ec188337e4535b01140e10e093f17d6aa4feb78f9858ce3ededaf34a89cf841a48cf9f4a03169137edf266ac0a9fa16b4ea68686a74ce006e52dd45aeb33126798aa00c0622bfa3c7d7acb5edd08bc6bab9e8632a38681e7e86991d20d09ae08e269c71ef909a50ddd246e96d36c9c858d8a8ae166dc94f4fccef69d00be1add71c996782cd4bf4a768233b16582b2b17601e458cedbcc5b260f4c4b6be83a14658880d261e44a55f1d779f5a5f34649eb92f597543f33f421368cc6a5c3e13f4daf4f1399651ca0024e25487418c9d8dc82cec9c5367d5f17d2be0abaf84e54302ddf5e42b4ae9744039a2d44a939b408087d2c0f91662869eff6e6839622a9924a4709c1e607babf45dfc64b51fbda7662b17eedb60c31e1dc1f7bf394daaba94dbccec5a5508894896792b7f877dc4d5e883d35ef64a53a3e1ae7d983404f99113de0168e4dd76be6e0f283a280047c637f3b59af3cba1317c8ec53b059c336321693e1df502d8c0fb66c2fd921d1becb7bff73e83145d08d68c7190af748602f9b9d1c232616bee27d4a5ad1c16e13ee96a6c9da064fcff74ed7e02c16baf2311214f2c52058d61a136a854d737aab2bf88e1b54c98c2879b2c2eb9df59cd99bdccba6ce8faa3b19314ec02ad90f8f469201c15560964d913786be70f40e1588011c392316c7f92114bd4b9d0831070126d9706b9021e87a513f0a60baff7dbf5f4b0377f6b7fe447f735298c400e56d3163b9b07456e1e827031b5cd188f00000000000000005f9d4104000000007b340546e53a08d9284adbffe45f52a892b3297292fecc5a1777329a99b7980539d1c4365b70d6141f14e453fd4366f1219d58a65120d68ad9df81d2f17b42ee246f3fd4022291ec76788f52715e26641174d8d242e929a5f31be3ede719725600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b66870a95a55b12c367dfebaf1b539481a813549b8c5f23ab51727e6b6ca39c6daca8a381c10ee54cd2dc94d1f0260eb2b56bdddd14ef6428aca50b9651d56ee05183edaf2d044e28e3439da882883821b8ee5273e6df72b91cf24788e7d58d12c1c037dfe89ddd665d1b0fe142f0f88a9bf0d37fafdb2aa3747687b1860931c020f83f36c240ff31aa82f48e1e3e9d82efe871302a9fb4acabac97c8a1e67b15a0209160d04e6b68a99735c461358267508b44532971481ea11ed232f5a9bb474390b2c714f32a5394bb90d83cc71dc2c4afd9db944d85d40495a2c700a9ad3e8283e0716bade902c3c8f79476b2d4a6daf0267850399472fc35b35ee41ca59264ef502025d567cd1cfd3603a25a6deb13896cedfb34d6491b918516e38944508341db30327ea082bb6e9ff9d5d49d347f6c02c346f735f2939891f8989ef18705c4ec676031d144b3978ddec1dce65bf0951a5a037041a0d9454853fcb4ac708974f681a5c02246e311195c68403daa57bc104cc92f648e7a81c4ac3a0e1b98efea5bd1bcabf031ae88c8dd188d8ea46ac273b7bddccf224c131da020e7ad06b6181aa0249875c3f9d92a7255728e35fe1b41d1aecad9c293f6f159a2d3341a9d0d45659f82b13c0f21c8eeb580c1160ef6e1c4369f464e018f81db63bb0177e75f8f1d1d347e3604693dc9adb6386efdba77d81558d0ebf45b3feabf5b450e06e833d75c9f98283d9d83889602ae4aaa5e859e7d03331303c80efbc9dc9f7623e20d3d3d0239fd4f42ef2fe266fe2f849b3539254cd02ec1681670bbdae9e907a52ebe6124f15bee779946cbfc378e35d418e46e5a4f51fdbd57b9b921ba209a584c66bf2fcfa37d7b8c5893ed204f2b4465baa8559bfaec5e94cebcde9d7b52844f21e5fb982e5adc65e0c6f900af39c13370775c995c4ec987eff486dfba964142eee31c83efde16fe755f3378e11611268b9905da1ae112f11ba7313af0f0241d13be7f1fe6d98553a45b34c24c35fb978930aa2eb45b0ee6324590cb7ef762cb4219b371cd298df0bcd08bfaee643f1a254a36050a615a8cb337fc1d1f6472a806663797b563849c3f0300a623d6d74f8ea65cfe669ef62e8214ecd66d39cf6b27c6eca6e94bc808c72784c42a2e42a6618c44f574e6c36c2e696275023cb917651e8def2f185cad670b445b82e9fd7822a19fd36dfd1e3ad32b49f825f551f74636d71c0470ecfcf04901a307dea718f29342d95d80b854c5c0d06c25073e47a44c40446c6b976c39c3f3ba067768c4414e888f6ac43dd54f43c11a1f0965c1421ff491b3ba7aa171fd700e112258ecf1c3554ed6c31b4afc0fe95c6e0fe335812afd8e07e9e9d1d0a6db87520e14733ca4d7ef570ad5e330034cd41dafe70de8e8727ddd530aefc124c4535437fa3206679873d39e5a8cf114299f0715628d0a498d111e1dc839dd5c1c12a877c0ddcffe470ecf35615a387ad775095657183b4bd331daac8ce8fd9ff84a304279ccd816d4ce8838406fe4ef1e0120d92cb179119a893f22a733e9803bd735abe80ff0aed17f9b5106ce5d05eb582775ecb41157cf6ecc48cab05eac56c0beda70ce8a24102d7a6d7fda80f3285d2b88834576494253843920046e632ad82aa7fe2933d7b4927c4a5977dc2c7289bd5fae0f3fdbfcc08f448902b5ab0c8e528a0fa0165ca5c6e718bab28c3db3d10bebb4964fdef227d2f41ba048ce6b25cfacc2b35fa11e4612989373f93514461431b14a434357a1a66967c2ef446388956014bb3d15900bea96cebfa8c4dc75763ddc73fde9c73c0fe3dff173bee412b0a8bcbf9f26c97511f4633fd8040d62f888c7464b3c8d30375d554c93e6ba25604c53d7c89013bb02210f7c95b80f987639d8b3b7bd351c2dc5eb60b3506629c259421e9b508d08a6dd60111f61097364f4b0383e0adba69c59ebf99808ad3d51a97fcc6347c919f3f529c905f2e4dc23de4e663bbb4b490f10afc5f5ba8bd173d1451c9157b51987b9982be105d12327f1729728feaa07ff93433becf42e3b89295bc13b9a61f5e16dba21d2cde0338b0fb8a1bf9b3bb81c0becbf8ace4e3a416087e70c3006b78489051c372b9d2f3c7053db2f263fe5820d71a9fa4911b54eb5c003f7356b222b48b2c0a954c1768faea3dd93a03b07e6eb89cf132de6b0e32cc3c475c6443f254ad07a01736bc13717fdd435de89b6b5e33a1fb13c74bc6f6c3c73af519f330c2f642f371a8919e0be28f77222833f1bdfeebcf49c2a82591fe2719497d6243f6452983ae010000000000000000000000009370defc1b8aa1f7c1a5c0c93f3b27cca5ed52511ba3477b735b0557fcf3fc4912d159dd2ba532347f5e43429b8083990328e012f458721cf3709a57a04a2bdbec90815f1d1794cc08ac2116ce1e69d76d6ee17a2becc9690f2f1152a2bfc09a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a29ba24818df7719462f8ae12447b86f01ba149c24d4c901bbb92660c7ed965a49e5efafdc30db6579f3fc2ef34f7661b78c78e4421357c0aa29aed2748ddee6bedfe5c5ff24e0680b44280bbafefa0768eca8fd321d4f7fcccbe5e09336131c72c31b35fa3c8d91c1c734a5aae4bfad163d7e920474df733c276a5882452a76021f648bbf4cb7fd6bc6b5c0be962a500ad0bd4cd7d654a1a75d3e7ff95d53ff2a032b1319566bc4a169c6a57bd801b0e4babb8c8f9f2e6b16f77ed87195039332270a2a2be9013d21ed64517f6296f1c390ae02133dbacaf8f979042e7bd56ea3e9151ccc49bcf8856455c843f7b6c47c274c0c1ed00c9201b24d9a1523dfb3d553d00328eeae895fd4b8eb6ac6ed77b3dbd94e7a798c8e2902b955442f0f11a8e9c6b8031c20b2ce1909f2685c03e0cd784071d5b59d60854f9bf48ba40afca0625fb354021847c4b08bd9b3b46850903b64531a2e6e564072c1cd7e92c3ea21245e2d1b89020e7f15c1eb08d0767a67ee79abd1dc1fc0e8fcd507c062bfb8cf6fddb98ea4770302849f1d9d8e1696351829abf0b03e8f8565c092b2dd69f5823b3faa907fef155c005bab0f9d702a8666d09d2af0b101fd319f83218539c72341d4b68059a0834423ec94683e1246e4aae624eee275557aa1fee01e4989278937cf20f8b1e6cfd7c5d9b555763a461a344bcf5e8239359ab6b2d55348205ea56c64f6222e6407cf4095894c375388963ab44144ff510d327fe074457d9ae9e45da65054f1381f020a38a94b97a1f83e02b13ceb08e91aa3962583207f6ad8dbe43b05ef3fe7d43fac8d41f244277cc472391e07107bd135100e16a1e03b3ebd507baf04dcb3f06c5a35549947d076e1de121c6c1a8ef2e8a63dea212263cd08045af2c536f2064c003f134d357920baffed41769f33cb1d06ac66c28d8efb3cac23984232aecbc0291160368fd5cbab08922c0b12d67bd16da0909a7a45137551748478ec2b8e747f59c8c5fa64775653a16e844a26f86f9f0dd154e9f7982c4841e102c36b1f926588171aed04876f2c725722b696dfab08eeeba44bf70204806942312838da545fefb75c6842f99141558d580228e8712b2c144444d8df7baa181d06f66f9d2180733c0c4b0b3970fcec916854f311b51f542288fb463fa7f04b5018023594887bec23ae80a76402794e6f6c530f90245121e691cea25a9e94f1e718b6807d260e99282992a41c1d13db6400ba72ec77c81729578eba2606f554225a593833793f328833229bc10397f0f9df0e79b8e420688f401cdf198d502ae1241c7ecb2e1f4326a48d731a247a3fbf931a54531a93cbb9f22033994bc989193bfd58c12d968ce21aba6a05b50c7cb8f69c1ba680076691ef86f116bbabe786bbb9a2b0fbac9124743a7e0e7279b7d1b603942751bd5e24749ac4d197753345118f7ddf760cd5d4d1693212045b8b1129b0e214f63f8f000fe976a50151e65519fec06f368fcae3bb338eab7d2dafcd3dd09116ee8eca38589eabd442df6965eb49637900f7f766f21b9f5afc1d4e9a98ec089609b3e6e97fce7ca6cfe2e61432ce68ed4748154b40cf6aa0038dc3561d3e72a9bbf7944ef9dcb3859ea73cd25977ae36da14f7d60e91af04442e3f180ad865e55bc4f917b0165505e09f02eeb137c6e4ef76a8db522c387734f29fa91f21742f6147edcf9194179f0f26b6565daeb04fc3372defbc6eb1dbe3df8ef834547d13ed1e5d110056470830fcee9635450ee1ded1c678c35f102f3de9c593f6f2eb6f14cb94bdc23f4dca970c25842c8fd17f338e87dd2674ac8f2ece7e9d8ec542b7fdb8cec6ceea08386d670e3b429ec212d15a2f66506b2882e2e021db32d136024003c774053fe5a3ed629523daaf744bb66ead26ff091baa299c1ae0d3e587ea29f3726858e3531c28d605ecd2d7aaef4310eea7594272095ed84ee77ca6f0f51708739c1de6e3641bd2c2f5ccf6e8557ee3fcb6f382f33fe7d5a09933fc8e4dbd4ea5b3c529d75f81d456c9de983832a2b9395ea177d6d864be05cbe870fe2e8cf4108e92c30b4257c992fa90fd4807f5cc90e255bede241afbe2ace56932ccbc6b0e6b460b1f83f576658057f4aa9b2f6374daf139737a35462cf9798def1349279bf87241366fdc3f16dcb4e137f13456ea8cf9f3cc7c09511423bf3843a06be4c23b17ecc4bb55f032aac3d059309aa6ca60356c4d2b5f79824729090206b839bda40eb9ec65859109e4ee5ae4fb20eab05b5d689576291d5e438e5f03b5edbc4e95bafebd934077ee88f0785f5c294910b43f0a226a9f2a05f25efecb7f9a19ea2e0b4f9a4360e757848e17cbacad94cb4ffffd36b8bda8b2573c4fe5867d877b3a461b5d321f94ff5bf27e4ae5b61a394c11cdd3083539627f040ddd8dea0b", "5352636565", 1, 243010429, "429ad3b623a4fdf5ff0a41c4d98737c9cb193f6804846e1c842639d24fbfbe5c"], - ["a252152002207934b706c5b38bb2204d91490fe3e46368590ee57777dcad0e72f074c3f32202000000095152526a5165516365ffffffff12aa3d95d6d184219935b8aae005380c490db2851fe82fdb7d0db3b978b2096b03000000020000dfbc7eaf039ff3340500000000001aee6400000000000033240301000000000853ac6a00ab525252880a79d5012d9dc401000000000000000000000000ba4cbe01878e1591722bec82604dba2eb21c4690c3b21525f1af722ad2f3126d2f7cde565cf780a333ef8955e49d5741244e9152fe2a0fe4c895a44c2c64bf3918b62e4e2726ccad039b3e2e7fe4a7b9249db9383678510515921c48ccd7a68200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f9ab3475a8cdae37033370521e537cc5eb613e7b20e3be1440422b6caa1e38a730df142ff9c92f1d18358332fd439282e3e745b2ad149b8e7bb5dbf9ed850db7d02370589242510f1fef4712750c6881f5c3c48833af5293af88d1ece85585c8fea30c3560c763a55d833cf327539aed5b8075c72753715722aff0805d2bd158020dfb055eee7cf64f57542d4cfb34933930faaffd5cc4f0caaa57adc836efc40d021e41a8849df0c893519520a06be8c8eab7f273728387d97dc9f316677615db170b20b9cb52575730390f558f883f2e8d6c206e233874fc746433bcec421fd0ba2f30474876a7f37546b4be7f42ad6c495440a3753c81d34a1df7158658b1a108bc030a3e9b3211636711de6a47fc010a82d461d376e87e457cf4cbada7db03f562ed030d3c22425278f73931ebeec82a25c68bddc0a133c67acb30fb28b1d5c6a0d86003118cea18624b9b025ba7d6439b9c4d459b86103330e1cb5a2493e0af2a214db2030fb6ca34122cc0fa48949f821124307a52cfcf63d5d5a4974bb6c3e7822fbb0702141559047a630928473c5d5f2778dc790851120a9e4dffa1f4bf142cf5d8081e80e2513d981a7a0e65ee0f8806aad52180dc1ffcc8c042f62d1c44a4280c4017f810d3539cab7ec5ed8f7ef57803ba0bb0f56f40baf98fab1e70f872d186d1e4837273d5dec9fc3e83b145ed20f5512b4c87a0065335f9e6e211256fae23e15379600fbd904dc37b646c348ee3457cd8a71c3c05c54de3a7e7563497f425a2136148e3ae229c4df65878127022d26533d6cc382c54b6640cbc122a6d9a3ec33c6bea8f108bea3ddcd0ccae6087d1838d03876953f66497c8e83c601475b10e7a04f985c4121c648ff5058802e569fc3883430f6fda4c593957ce455399e61a8f7846f418dc59cf924c063b3136f5109c6ff06e71dc59b584aadcaf8791fd74ddb117bf25fc82ce44acc28d76789bfd5d3919f6292bc23c76ec0992693697acb40312f657b3c8f0014e0580b5069075f3fd0097a2dbaf7f75fb4900a62ac1b3b19c84b1697c7545f132b9c32704ec20d7c62c18f7aba5744a7d76420c40167094ad7f838de122ed8bda673488db5dc99aca2da950e412b76b268e282a63e49d6cb76848c3b329de844d676ab0ef063ab777a8251ed22027ee825fe1a52e55ff12de98cebdb37718161be1d1455a32f7703dd84e01bf5b93ef50b2c478586156a2ce725e5010fe99a27a3b40630670e720bfc1211e7b6d3b97e0bbfee2ebb66933ffaec02a26fe9216f7b5ad174d02c3de933e767edc014e0b1213b6ae44b247a02beb9509be59704827f78e492041d9d14870e3d8a6a8c2f7f122583b28d3fb75f9b173a1b2df295747e86fd25f2eb63c6d796868e3f7442a566628f5d093bb4510c61be7caa5f6820b9162d802a2a742605d76960564478373ea7b3020ec28ba9a8b61dc026e159730c210ce2a2d27a4a04be0669a5cfac4bfdbb00e3008b35df2194bc22839db4a91ef1d1c99c90c9cd9db65c81801e072f39047d1243160fd62d56e43972a858a00881607e856bd238fa55a8682cc71c882dd9ccecc37535cd2bd2a364c0b18f2016be3085195e68406f3e954a6c60395186787c733290c9a71c08bf6c27900e38ec44be9bd3677e3ffa445bc6ddc91c03c72253c37af64be5fd6c3a355e1f544ae1a2b76009f3752d581cd575efe9f29e2747225cf97147e582976eb76600f0ffcb4c7c61237d5d4dcd312689b6c032ddcd3e7a6f9894014436cc3a1a49c55175e1683a4b886c7fa548bb47ebd4bcb146d9c949c1acc7f6c0fc7dd4c31d97d32feaba7eadf9d1a019946a714d32d34b895ca13db7c5fbc9736c030fee2762d99a066c9ae6a71269901dfc7d8af9c2ee7c1ff598108a6961dfac3ea0a9878a750eec3c44ce4d91b4ff8699c72181e3c52f949ff6b1e566195d25948f88e2329dd412067d3e1b846ccc8bda8e828fae164e6083e2da4dd959aa0fa9e16c640b44297b3315a54ca46083c0eb4a9b14bff7f97eeb607a8041ed76538d683deaee5b3c9df8ea0ad6b31e2a8663362849dcc9c7ab81540877a36dd4edc5d91e0b7ba08da41d0d717f793f5d221ffdcc6df5569b3d744ed0a63e7e633730e6ddbed936d17048376049de210516486e4d7bc7d5157b3a73cc958a3fa00108f3cbd7e99fcf55f258bdffbaa0dbdc39cf1ceafa6f6271025bd30f34723c1d805f3949c25c56fb922f6a43731c0585a1969a22ddd198b95ee1d8af4cdc8b2675731be49c395780b80e019fdc70e8565fd4a7f32246a359ddfce715b126a42d1e23a6006aee05255a421922d92b9cae9eb8a54a47b98127aad5beff7c3485db8647d7b3a53d1ef367387ae1502b894528094155d3f6af7e91d182fac8a73197f0ceaf31ba223836a7050e80f41a12c01", "ac52", 0, -1038755428, "b41c3ee80aec3990e01535eea14c0c0ef14c6c1bab95f2d6407b940497ef9c9f"], - ["033cf6c20156c448c47ab07a6fbe3322e39373daa3d853ec07b0425797a66aa02995e828fd0100000007525365ac6300acb6c9539d024bde6e020000000004536a5265315f93040000000006630051ac52538f3245f4", "0051", 0, 2032223611, "2823d7cbb65fe8a7e53f1a02474914db5f7cfaf7a9304cc676b0aa8ace80d8f6"], - ["bcfb201504cbc20e963db322f82a9b2394c776679555c9244cfd664fbf3368e8b6f247f82b0000000004ab520052ffffffff18a08dd51b2c4825563ff4154cc71db063ab601ae3f3ec38683aa37e8a07e2990100000005006aac5363ee3ab2a0e7b1c9679ef26641e3eb01ceea517ab379d33d60bbe5874e5dd6c5eb5491c001020000000853635200525300517c05c8bec8f9d596229f76c346ac37c2ee1a5d94b1321e4a55614ad6d12b4e5f6735a72201000000095163536351acac6aacffffffff02fd5849020000000009535152ab630000acabf6f63505000000000465ac0000cabb6d8a03e8849d05000000000000000000000000544138eb89f50f73c083ca71c4b3282c97d2879799e32a895bcf0c2ccf9ac3fcd640e4492174c94c1832adc6e9fc98ea2cad1360237c5407f49268cd9a57eb525bff1c02902ed27d373bf9786b39246dd7aba7ce0ba43912b9b6fd5314f501db00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000937d30fa45e8e50d5334528b93e4c196f3b659624156a02571f55707ce0d384be6d7c9875d1e8b8c0b18d88023faf6ec90a6bc4585e60c45bfd34182911cbd8fc5f47df5bcc529c0d477422d2bfaa3cde4d80c547bdad5e5804f4325555b029453f3094cfd3e169d905aedd53ef6e400f9378ed7c1f0b0b6d042557ab19385d502275f8f4dc3e75b195c751579258de3f0f3933e859eb9219d81f2b63295db82a103125b7e5535df81f86cf0a753b56def46526657fc934b91ef48d01b78dab3d31c0b2a8ec61bc2f5973c47b5778e873e3c19eaa09666c98054362024a9030b5fbd590f4b474d3b8b7e7d9daba2f80298f7cbb8b37c40cf273832e56426aeb1aab37b0321a84e6cf5844ec830a114ed3d534dc4b4049d862747bdb7c6466aedace5101d022da002378bfeb3f79ca2bb36f3138edaac8e865a1603f7e83c72195aa585bdeb03110f4d806be35aefc75eb129214faacda03281891b1cfcf417214ac8eddb9e6c02138feb666e0d15160d1302076064a3ced8663ac07d2d65cd7b32aaca614a6844032329b8c75af647de42ed5fd5bb803d39d793c5a875613d7974018a022cf08a4be334cbd92e8c80f5c824223fad93985199be1daee56dae9e78a2ac72f12a5f092793a2ac88c7475e469b9e1e26bdbc8c494df393505f5fd93ed384f10a0272fd7dc0142b472e09f18dc098d43c56cebb091943848e78c8258247589a5831546842066e28409bd2657ff5f6ec9f09e80b69b986990d04e890c0a3f8cdb4dfc408936e4cfc17fca2fe6479691feec39e325138486fa729f6f4002fb9c1c6e79bccaa01afb5ca0fdd8d2c2fdd813bc4a4109f586b52a541b474f5ad665d7bb934c58d6bba74105bc21fb84606cff398af46fae6925dc10e1d320d485f8cda602123f487b44f4174c60385c0f86f1a993d7d3d3e61726543a2a3a34c2cc46b406ab4d18abd52c71956c9b2e8856eede760b65c41e9c068a4a1ba96043420d2473ec5449fc4207fd40a3124532ec0bce8435495d581da5aa5ab9d251b22a8aae10e201c42d487339555634429c4aa360cfc2cb646dc5f90a3417f860a0b88e7bd9a6966dd8a83b24d2051182cf52b6f6d762ec9659c4d81a0b516f5b877ebf786ccd94c5eeefc55d94d5b2fcf1280b026397272b02cef74b7f15d605a8d26c4741ae83fb82442e80829a51072a7b62a7cef46ab4c0e6efe80801bafd2945cb8812e10f9ac206223bc3d094908356f318b08d7166bee05af4305a499e471cac5bab176251b26742d773b280b8dde633df55269fd5b4ba1d3be04f2592c43ad0dd9c41f871e76b5dc82848441d9733692ab4ce16fd31b00ba0f2ad0c87e417ca86c33cfc7aca9e86100489323342ecc6a3d76e1cf6989b61a5c79a5e4ee8c889ca3d93f1c9dccddaa8397d9e8d00d81a121bd432f19c6b8e3ecddf7a5fd42d9e5635f4d5d2af74b707f23a2356099ba78382e01743166d38196f04526e0794a83ab11be09a2eeae309cedd09ea6deac8f8c39efa8edb345a436c697c13f14c03bfc3ac1b451171aadde4288ca03da90c0fa6a7c513e5b426544d5bd51178afeee241b7d6beb1daf704122fed73d91502935297d981845de8d8b30754a793b1e4091fc7fad337eaefda9e5af89f6c41aa10a1cf04dfbc7299a4cbd361deb72b08967911f3b84e870ee887d312d988fb6055784293f2d240cc747924710eb6e45a182edba542a8e4214c436311b0afb7a3e9961d6ae4997f100acab5d3bfeec4fac5d5d08d91356b7d9ed081eac25cba88036357c44962a782c1a0b61d3210740a9d255dbb45c4a6d196fbaceb23f3061cae9ab3cc7cbbafecceaaf8d89f4dc250da398a6837c4ecf4ac360192b8848cc05736075bea4d824d1f45451294c6b8e6a32c251959939b8fae7baa10880c56905600270ee058b6ecd4c25d40c6d9046f65c711887ddf0728bf2da61ce710d5496b17a955de2233d3fce42c4d8dcbe073f4968c1d14549ac14c0595b67d84b6b05230fbbb5f7f690b9411eda4342a1f54bcc46fff6be45790873572cdd9b81fc9839843b65fa545e489d0a3dde4bf2b6b27d4f76914f86b7a27b48d62f89dde71ae9cc1e2de9513a2af8966a40446191b248200e30284f58f340001b5a45640200cb414b235b76f4108554a4fa0b8b64778df2c985ca0483c1e71fe5fabe2c81987ed5c2ca3751a353ad7a7bf7cce9c95913e2af9573cf63298d66db0ac55d2f2f6849a32e48a59435adf51dcd80d4489e770c6daa654b34215bea3a2ed6d161a03719cfe4c7000000000000000099b78c0500000000c4266ec9e2f9d7b8055569cf53b1099767ffe4b9621246abfc0b800e6971e72b7505ef968430d6f7fa31e6aa88347b38fcee283720e4050f4ebe9311bc55f3b09270fd9b7b9d6c637f8cbeb1dace2c9956c5112b0d32f18c4797b4a6ee1b60ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e9809efcafa858d09bf983ad132a689eac4e1bc51da8eeb42055b60cfa9c4697dac3ddbd9fbd641e3e280672ce5b1326f63730d6a9bcbef76a95dd3aef114ba04159d4a6e8a36040ae14557426c458c3fda0666f00f4e5895e9ab3c7cbad6529e78640a8507519304699a53e29c0c41fe5959c8bd6793f54649a9f9b27a6ea0d020d294d70314e3848a42ea4338634e68593c460fc89f3f8b6f19c27544602b95c0223f09b25a43e793bb3a4d7b2925c79cc10d553cd1b78a88f09971e74f89776600a2920f8ca596e7433ffab769097f10e8847992621130d68fd636e5af90e31764213ca69a2d93c548918881954e7794c456f6dac4c843303888bab65eb43a11d6e0315f767be76edbb5051aade7a5a2ad4983fb266f0c0962ca4ae618869d9bf2f4b031e5a5576272824a800fc2d45ba4bf9844a7937e658ecfe80d589aeb70d61ca1c0328d61ce2c1940cd29e073ac49fbf6cb0a911589898b9b35d214c0d3c9e47733d0300639a9aac3c1e800381dc8a1854c105d363884b008526e20842be92b75368ff021d6b863a2b92b946fed01c348102cc59692a8fd1957eaa1a3ab19fde850db56d0e427eec03a3c286fc8bdda71ad68b605575f83b9abbe54cf31ce6bad31c1eea1ffdcc31df258d46e257d5bc7a82a83632d99987444c27f5c90741a041a53800912731a8a67bba626cab7fac95fbf9e43ee6e35473970ce0e0146fe27d3a59235243766d4068c1b950de1e3aee1cb6c9c46660f08e34f28bad06fa7ef2cc0dc9303cfac9e2a725964d07a913478012216c5e9a29d516b48ef5bd80a9cecbaec3b2fe0cb7373e3aa959bf215bc7a7bf27039db2c2a1b7588866248b304c8f89b2110fdc7ecc7d87137403c8b4c340a31927181527e1edeecef64d2eea494d183bc4bee59c48c649de2b2970a8203148c62f26f82f57ee4027bb6be37d23cf0fbe3871e5d4053472c07455d854deb618db23b8e4eea1c5c600477d2d6809b54f4b81ecb113011183bb1b2aa59363f596a8ea33f032f57ee3725023de6c9f95eb7a1026f114de1f20000431ba5072957b549fbbeda9e88871012e1c37812beac7891026ef83dd4c7883b6fa51e90c768efeeed433216eb3cf9b2ce6a1baeb8a4730c8b61ecd4f98d7f0cca1fffcfd28c242a3d9994f0b2fe74f3734704a794a21e6e06c24a5cdef34c486fa9e0eb8b4d45708a886428f7ed1b185af8f230f51603b9dfbc91f861e215a84e4c67cf7b127751a7bebf40a361b69610382ce9707a2ccc9632177f592eb69dea2c07a16faaea165a90afe9b76e2fe81b81693d1f893fdfeedff873fb77f12c419f7369d2bf72a73399dcce0789080e71fa74b7204dad16c191a3f26732454ccf4e8c59df16570dd826239c06ea44100809b5c04d7a6efb4808e79d99e91decedb48b9a00b7fec9bbf19329ba9d284ef85be9cde7abd65221e95117aa9827721283f4c5e5eea14223bfe2b8c1308ab857e1ae8945b23a1fbc033a6ff0be7b82a652362d614965249032942a3e9477f7f98d920247b82b33c38bad9659149303ef0b88e35a8933390651ffcf4da34a3c8711300b3e59c55dfa4a950f17bce08c483e95701acb7f4a4536a0fab82c4074c9b5e4493f755d2a8bdedfd6e6f00d460ace8cbc65d61416adf48544e95401f1856e5bee7785e12cb52a5a1f46c2dc45fee91384e462da906416e540001d1fcfc0abc7de92e11015561bb88c8853be73741fd9d5ca3e848a8c8b55dad643238c951a5ed0bf759dfb90612676fd8a012e5503b9e2524f9f370bdf771e0a6dabc25d33d71b0e7e8c155b2a0fd3a0d30726f5da67863302fcff9f1053b547e299838e282cf796304152b107bd724e023ac1ff18f9088298a3e9f8de937da1dd7fdd0f7804d1c3bee5f6e0f6a0633bd17c849a3b2a185fe7d88d25f86acd4f251af1aedc9b2ceb2fc67ba7ef3aca78e850221136f05847e3b848805c52c05808056c8e4686a317c0e5dbc874bd2f5601a6f7fb9e20370b104876c53a15838a452bed77ff2518e9fcef2c93a41ff729a80c1c206f58a978ac56cb1166c58d49bbe415e73ece6e2bd2ec2b41867bee13182f630fc18c93efcf57ffe9299778142b962152e3a64a7005b12b97c89c285a7f44fe952566994373028a547640aa959411e4dff656fce82ea2935f57bed5a92f2781ac69e14393b357b63d8e77ef7e371f821c6ecad7a2510184a2d1eceb4f96ae90df0ffe35a7272943d81e43f88cf53181d9cf65322a77dd03ac9c96ed32909cb6d972160738c5eb38a29340400020000000000000000000000007d3ac7e830ee29c033f093a20f844e5f93407cf5722da60dcb858ec195560c2b4637950a1a3b398cc478d290551b7b7a0334085bf180bf1e3b5c88b3fde97b9ffb370d8f2be9ca8b72ecd556dc8af0dede19234249822a22c0ca409c5acf09a5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003c11c5aaf82fbb756ab6653876d176e985ab91729b955caaf12b0f2592e42c7d3c83b754ababcb9c647e90aa8951eb83642cab539699a1590bccada3d59307a14f4cd38ae16e59d378ff447cc5cfba7b06fbfe074faa99116ec3a0414a89a963dbc9484db64f58e8d91b2aac7d7e0e9b9ff70bdd0927b2131eb6884ed579bf6602070bb2d751b808ab7f65abcde45b78d03fbb427bce73060f68d0d85e54e6ca710219e0e665ecef50f9929e15e2701e9dbf3e306b289ba555e3bf650575b8627dcf0a00a2ec37eee6d562624e25466f078a1c6ef4ba7c51f3703bd71a0a8db5602f121e3f3a94e1e93aa9d6004f2f80d66d91753f5c3bd50540036353d840b0253b0702246e5e560b3805ad2ceba58e5462ba4dbdffd21e0955ae9a6df9cfd9a1922b5e0325cea40202cc7872356325378d5bea13f40cd9a33cd02a54f9416b13d71405f903036b1b392edeaf871894416354e60416e9baf4d8c79f980a7fc01c6b060b2f4a020854a9758c455c4c6b73762e66eac1efdb2834c3c6c886f7f1f4a7026b7f874302113fe2b2eec1cb5857e030363e2d387c01fb43268aac4a356248bcf956b21ebaa9c0dd68f7f291ce55ee2d05ecd28f35d14a6d5e82ee6480cc23c659352061fa06a6a5b660a7ae29c121573eeffa092cfd5690fbfaf318609f74a3b57686612471879f7ca4ac9f32fa43c91eddb0875268920860f589b843af9317ee6327e42362f1cff1b0c5f5d9a76e8da2507152deb0aee9ae210176c1e3bc46e1a458bd97541ffe30fc3a103e7315b02016946ceb7fdd15ffcf508a7e2a431014e85261dec7b4c11a9a995b171769b6861a304d8d4b27fcea7faaeb5fb4108f5c88b8ed7540f7081fd002d6289ad3748b3546cb634e41498f5a1236d3a11196f3dcb1e5e033d560cd4503ec10bc20e8f12b586e743e16b208261e6b433cc8b124f2046bbb405ab75b27302f16f7f431c5476c9a8ceb3248e495806b685fd3c0e158028f78178834db60783d3533f54197d2bf504050039ed382f08036e0584703a560655ddf88b6649a0c1525a7754a1a19785e126217554caf85d3f64c9119b9c9c54551eecf43f0d0245cbf21eea8153ac0e1b93c2eee1733d818d299fa1431874ebc0fb50ba3599e251ea4f33fb64fb1cad68a50eb040bf13489702ca9260b9614e606e66046b090b336b8b8dd97bdb6df5d19727f3b2aaf7d2307828904cd4842f4ec79bbb1440a026eaf4440d0eb29b764f69adad4dd6f225827c5995fc59b1c5e97e2c501ac4edfcc5d3f5499d73092a0eafaf6d24786f7b4fee0701ae4165e9a243f598e418e3c6b7f69218219446995a4604851a4829c90c14a94a44244c3d7c0ddbf0703a225dd69ca153c5b6f500e3df94b05008b719bed021b1c7a94619a7116244edeef9af38661009c321b74e342daf63d346caaaade59be5409645d79ba3855bab9ba96603d03e2be3c65c188e46220724d8e50ccedcb1edeae170b272f6e73f7d3c07ec9df5dee7e35357ae23a179e38e2dca9424c973d4fc3ef42db0cbb305e0583c141b2c36de5d8dd64af52e20bdb9398fe39772fd86c67c4a6d14454677c96d318f69c49406744883eba59ab63af223dacf5310a28620d55ca8c68b649079b71925efc1546b4677d89e5bb2d8bda1e41804ad2d126f137ce9e42c123b5956dd3db67297f57e59cb4fb3d90a50ae3336eb8d650f3a47ed9d543c525ae0725b29a340759e3557a28008ef481ddd784915d8c939c8d7bf39845708f78f41e0e8dbd3a1f7148324690c37d6b20ccb12911e8d0ca26c48997698c03bc301e8d22cf2166b9d234f2597c2b32fe4f9f3e3c2f95bd014a0f0bfecbd0f915a01e978219658fd4836ef43fb00590c5f10f76ab5afb521b0059ff74843069e9a6ddfb5698806540982a2d0fc5dfb74f3d7d7eeed185e1feaae5166948ea8fb84f07d1780c910a9568ecf4ca9b9972b6c93c45231cdfe9c55127ef46fe4b4033186175c6ccc4380757a6f58364d05becf5ac5289aeeb3919e6c5c3df40f3b1ed6077889d17efd8a12d97406337d9953708757fae65d44502fd1d2ee58f2b168cf384b1e88b242c6cc3699e58323278218575b1f273523733127bf1c35629d4a2631b16780685cf566680a37ae91d6e441c58cac5576dea4e42ee4e8be8a88517b02cf10ba870c200061df9cb4476fdb6e869ce2b24bbd22ad186bf4e0995a099a791d0fb4f435f2394015deb9b3cd77b22207ac100865fe9b4295e4b6e774360269f394f3e257a5312b63d49f25bd22c2e17806f5a070b54d68a1c8cd4a075d3a0acccf94ade21b33aadc4f463b3d848b5ac3bef4e3e227c1cdd586dc4d3905571c6cee48a9d1000988ac70877ab54dfdfd818a81dd51afd7185c42200bfaea720cc5af4226f081aba456593f2dbb01ebc200f", "6300ac63", 0, 1988561694, "3920a8debbfa14008442688c9a0e861354b379b281158a65a36877f0356ea164"], - ["ee614d2b022d2eb44a11ead6c885607fcc6c6b462cba11fb422036402c3d18df5c65dc463802000000075351abac656a51ffffffff7474db96ab1123890fa7f645f5135445c373408de13f3047565e8e7fc4117f700000000004ab515352b7a6c23c02c2d4c90000000000056a525252005f970a0500000000085252ac6a6551ababb7ee11db023506ed01000000000000000000000000b505023e7512cefac652ce3c6de0ddd21523e159a976bcdd4fee046ada529e167c649437d67e614bf98ebd2f4cbf20e368de5220ce1b8d0e2ef6911d722c523dc1870ca9b52baf1118a486ed3d041e1871ee513d4f434821d26875d5c9fbf14c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e51bc38f540df82005915ae6173cbe6c229f3ad3f4b75a9c105247b26f71e1896ebe01ad900679fb161607795884a7a4d824cbdb0b50195caf569b0e54a72717da6758d0e420308ac63e93b3481befd914e36366e71760820e97752815236c2138fe487b0a65e8d8d907f49b0b777ab12e8f3f6730d2de28d76efb0128af76a0302af944e285e6826db677a966f0464fd5c4f5382ff7f36dff8e040d4b6f268ce0320a92be7dfc454438335c4e3d3ca516905b6ebd10ff991dfb4f82cbde92a3f120b14e189e449930c35b70feec174448e3b413ad0e0a13c9cbc622081f296b2f5250ef536e2f7c2a64dde153dd527c77d9e490ef390e44f7187d0cc6ab8561508980216374ca36ae0b1a3e8255af426a70fb9228100063ff81644f3462048b1724157020cbe61c6449aac81c37c9468d5f8494eb501e72b7bdd629c707f5ef46b39be0202034ff21c71b796cae7a9c47b2e1b409bcca97e56a3fab915826e7358bbcec2f9030b48d8ad632960a87266c430fe3752c3daee0a2c452e915e298dfbc36a2e3d0f022ab5f421eaab6573031bda4dc076563ea7b20c9b7fb0c3bd33451701806bb9baad8eabf26c8f043252c709cf0f937e08c9b14b8986db581148d782c49ac28eeb82e5a85bdf3138daa8f112c5126a439185d441295ca3811b567226ea9795cf9f9ad7d8ae9c43232d144b0ad451a4d5410b49f85dab53a8cbace0b08eca84263b80138b9321e7f7e317a2e5e74a448bbe7e90139b65e8acfd68ead939a4c2befd0df77bfc8a1de3c0c21fa719e2e33aa1614c36b5b0cfcf0df8e3a174c700f610f2e23c4d8d770e00ef9946e0acb9f7d73c91e42f71547299ae85c5a099ed63c606a83cea5dadc11352f4168b56a622b25a4e55952dbc703d24f49f16c852976d3b168f153a6979996282fe73832796bc460c68fb04c2ca3223ab43e1e20cb6faf8743ed48d03de103dc07698b806db37cd1067ca2d7cbafd3342e0a6505ce601d5bbf5371d1e461a974d4c727bb9a592126e1e42264fcd156de30154bbdc382224bb9a1bc76f2bb71b79fc5f3407c7d625d6347a68d575cf2548515074764452337f84d16609b8b1d2c51a028e71ad70fa2c8533c3d5258c5de7ee0a82819414213e11ffa932e73ec17af04bda8977b6af7f3707a31064367ebe99cbccf1ba409372bde830c7c15d07287fe52b6a41a83c33a148b30ba2f9b56c3c43e2f257e4f726c39b944849b6b51e195a5dec4b979049ed9b57e4c238de10a136d80990dd3527723d1a8edd19dc631c4267bb69e124f1fc6906b381b81bdf737d71434add7fdb1c6ddb3e15e39d031bf8f97dd57cc01d99f86d12fd49f30efc74f3503bf640236e833b5352d233870d59633d99ea374a6a24fc6cc10c0b84ddef13f680186c862658ff15a96cb925fac2f8483249cf9a83017bc4a8506cd4c695c88c7b514405dead55e7c367f8fe1ed8e529c528afeb9c452c6c02e1c2286eb87af5970838c055fbf12ad7cdf6fe774885c2a3c832170cd99cdc46c1c52314c51fad61eb481d15602b32071a69f55446eda781e8de4f004a99e67697df091dff54b5e75d74a5a5fcc85eb1a484f874d729e46d251a2963db4b04fd4651ed3b8489f70738692fa2519675221768a15ce42a61eb90c728c54078d92e6cb1af9edae86ffe2b4c0d0a8e52b2c190dfad052678a2ceca55a6b9e5cfce1cb2b3ae1d6844306ed0bef851499592304eafc202fd073ae08b2d0688a040fe3a0959d460efb94e420554ef77604c4ea574b2c1b8aba4ea84c75220c5abee520cf4090018d2bb398749c1c5f00e8b0c4ee7909586f4e7fb20175b50cadeca7a0b553d94caab82493bc9c20f3ec33c674cce19d2ab655147d76e5ad4ca1bd8f5289d78d5b3070cf41d3d8674da3247436b019cf31cd3c5b91574b41d8c9cbac68886c39c4d66b10b137111531e3efd54b08c21634fe1847d87cb51ff8e1265495920c6139c445e0c5f7fb2c87c068e73a98874cb2c3830efaa63871e1841e95b03862a3c75da39e79d1a58b45f96038f16b0d52f3d1dffcfa2a77ad7d031d5c3917933e0cf87133e297ff112bf56876887314d439fe7fb622fdc26a41b4fb224aa6e7d65d68704793f80ca41efdcffb2784c09e1cee276fd44778c438afc62d0b77fe4f3195870bbd112676de202dd9fbcc3d005fb8dcb198556c1645959b8988fc76a6ab9aa230d2493bc77b2533e42fd8829b2e249f242ad408bf3c498e0ab671eb1440fa2d474346fafc6c8c5c15f6edb5e1d51d1d9b8377f4ec5aea589040000000000000000000000007e248f8d79380b0ca2be1b3ae157df42fb1dd73cd8ae3cfbf880e0a6d5130173fb57cc71363c50a2ba00fa181aa17a1beb97f378fa26ff8aa4939239c8a023fc48650938ebe525ab6bafe256e03e79a3ad196b020ee0aff6ce422386e659d39b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004cd2f294355b70dc685d1b4cdd3b05087431940ee62d7912472984178b4c0e637234b90ee8601f98647a2dc6e530179d4eb3948637bc50dc677cd0c898ac9313f52e5077401208b8c0d7b90e028843966a8e77975848fa4e44a6841884fac3aa6badd4e49057a781c8929b9d2502e7f22808593d62fac74840ec91468eeef055031946c8911721633e372297d48bf39b37ecddb6d92e2c8d51d8fd4650f29b3563030ded1229f5f93412222d3f27c246e6fe1267949910fef3766a7b2df5bb80803b0a2f0c02d3712ec37d286a5946ce0143388d7df3df95b4e7bc8798aa6ecd64e82f15023f89439435f5ea23e8aa00e332887171c2da6cfa4cffa136b3719f25bf81032dc2ad0ef7078576d84ce4115d907ad662f71fd8910aa3732e09357d4e5b9e10022a53404413256cf167e1566ce5d51fa5e7a995c097649291dd7c721c68f61047020b0d6e4ed675e2a0e2ddeb8ef5265ce8f99cabb6f4b19f923ce22c6e982fdfdd020a8e13d01f1fe3bb63733dc9af82f134763083e818af6922f86f9aaaea71364202192222a7f5f32d08363d2a13e7e66db8525c97595d7b01eea1bedcafaae43b20301ccd4a992c0644a046872381257e63ddfde0444ec1eb4f5894c6de63a8c09a2e250ff37b14bb2cd2aa382e83362e7692f013ad71a7fa9982dbc6a6b2d528823d66f00b54ac7977a0939658343b57eef1dead7dbbd75640df7ca7c64e043480005248d8380c1eb6ea721959793b41639c5e4bf94d9716906a0e563a92bac68ec5c7615d9e166b18c285676a4e146799d6142683a8e41385ed3073e7ee18c69a7c5f2b08ac2a2f157adefac51548895c036dee7ea369bd1198a3494bf782271715dd202f6d8e3a61929096e21e3fd87df8fd7d6aae5151c90db4466f58111184126a7bb7070f76871f4fecf2f47b8a8f8e6d51952e2e341994c775765744c5a477c068e11eb57fe12e10d9ad50f39ecfda52f80fe420c4abe7056778022a3d3f14370916512edcacc4bc37cf87f571074cc1bb596300ef63c1791a6f82b3c2afdb66dcf228564e89c8ba5030e3d847b132d843ebed30927438fd8dc5d6313483bf35bdb7eda58f8dcfa6b0b0d432a1b0ed2fdc2a1f0cd84b877e17135fa3dfea895a484535c619fcda58370ce1a5dee8c7a6d9c0cbff5162f9dd2f523f3513a0d0ee55d47961f3e8c285635cd3f4451a5837e1c420e4c90b2dea9da54af50f561aa82faafd7aad7b0798d581a560d404ef5e292b1ebb6b4c42c0c3e9dc84486a204c9ced417e9a688b1930b1cf6fb5da22706ef5bbb6aab91bd202d5865fa7ec118659cee2c74b0a51b4d634c2b15e778ba02b39728efa422ffd58e20b826256b2584586c967dacdf9bcb70d2880ce2ad4269ff4efdb5a290da4d7eb6267a171fb03a481b149f8b07cf1d7cfea9274b439816280741deeea541378695d616d12e7088f0adc9abe718033c046ff7264784a567ef13b0f7e7a76efd9b727b902d9a778097879b14437b16477017ddf636016c262331658704ce2191e43a6ef2cb6551cff1b5c7fe75558d5eec1463447667bd49b3f9f09ddf77b00ccaa6fe013252cc624163f51f5d033f96b4ced711b20326998da8363d34ac657ecdcdaf4e8821109c535978cb34635a4272598a4432b0760620d9bb3e32e3c841ce0f57ca16fce84e228935ca12eb91712ad2338ede5a06789d47bb0e60a7d6b6404c1ac9d4d1125fa817ac2fef67f14b9367116c5484da864455c0dcaf635025fe8b5075cd0bf8e9365528d8da4ddae78410c0b9362b092931082a82d8f5d282d15123b0cc9188c5a722e52070d0789f633610d17b9bfd8ceaab5ea3d3462604bceb0ddc5a0ac8bb519501034eb01d4b724833f72d447be563696df42fd137ec23db2d6289365c7beb07faf007e94cb2e8d726a601b2158f320655b4b4c527c28f771ff95c96359aa56b4753595e70c0cfd5c2579524a8c4d3d6a4d5dc71efee714f0604ab0a4b80f0bc19a34c427c761317d0a0f0f7f4ba052f2ffb7f4a5e961f6c4480872f6842e0b57cf69ecb6d9c7dd1a1aa92f6307ac13af1eebe90ba29c1bdf719ff7964bdb1ea7de6e1d46f7b3b467d2b339194f1080e585a00a288ad5045217008feb2128764037ed736e4f22f1feca2a36e9fd4b64e2121cc95597b9b04d0dc3289cf84c7aab8fac6c7b92c55d3fd88bd7bcad9184b078b9eaf17f5496f4a92efa3c16ec5f15b7b74837dc7f3b8fd64ba19406906d39b518dda12efa967ad5325d5aae199ca8369b13805b4d9899923391563afb42e0c23b8b7050115cdcb6a324a444bc2926ebd4a65e28e5d36f17c17f0a6a5fc3409ef8f0ada4085d476c05ca71b5a077d0e1f410c6555a0c7b2246939adfc888eadb2a2af2a8cee0f007694479620c697dd5c1eb9b6405b562682589a608", "", 1, 1669333877, "4e0bca14bfab687a86c78b5201fecccd4c6c0aac072994a6daaee07eac1f784c"], - ["c5bcfa2703beb79e4a9091e469a2ecbeb8702b3e51892fa5e900a05cea488b0d65d9a451370100000006536a00006563bc3c4782a559c1c0763a4201cb328fdd4eb89265bc967d9bdb43bd2f3a6d175cf8da9a250100000009ac51656a5251ac5165ffffffffbde0364e9d09854b6caf19b4a4bc09c86ce455578b5058538245c6b40354a31700000000086a00006a6a6aab650a95855204effb17000000000002acacd54ca80300000000076a536aab0052530f9c84040000000007536553ac5352ab70f5140000000000046aab51650000000002000000000000000029425105000000006482e2630d3c1b540c65b239cc10a2a6951acb7b257fd6ff86c1c543fd86caa7a4c2c6f9f802e005e43e0a6f144a7a62687b42f40f4e569d66acdec06c34f20e1a5eb6896f5cd970a4552b5f8289472ca60613b2d0688998451b2a2ad8d02fcd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d195476790f30e986dd5cbc839af1f431afb3f6999f0492de90da90c6ab31aeeef3428262394fbc6c93f176f7fe001f830d412194f57d840edb9481bcef7607a59260225546c8c3531de5f212447a6025c755e663aa4cef6df2e29b3bb86766f4be6e4051371ceab1ecdd720556441ea23c8b030356880dc4c2e07142b1cc3b03177a4e63e9dbe7fdb5fa5293e33182606f8389eacb1659621ecdae478c4d77400223e1f5eec2ed6f2bbaf0810a24d7ab4ff7ada8d2f231fe06761dfda9a096fb680a00023bc9f2adfc1ab8d59ab1d06af0d84d2ff8f88fd936051df1aea85fa332d21d4960d2cac35e977a51c8a081592cf09951d45124d95e0c4b9d524fdf3acee10208e4296c1cc52430ea8a257f8e916150aeb1bedaae81d10ab429edae35455bf70201410f1ddb04369043b438142586375025b36c43d3dfe1ef87c920f6f867fb350207fc03c5cdcd084140fb1fd87c81a7a014576eb81d25356c6e593337d8a58c9f0302280118b8ba98032894c0ca067f95fbb0a7e746f606bd60e171931a97cd602b0322ae62c7f71f0e8f3d33cbd916cfcb15bd4565858284a304d31ddd79e9266aec5ff1567b96e48f810b8dfd90025d4a042d06422ed7f7035a79a0fc6d7ec977287dd96027973cd2858ee4266fb3ce0c16568d724c3dc86f0f00a66ff678cb5ce00c8f52136055ec0b2f3112509dcbd2f255569278e6975594e8d4620f39b4bdb16b5938c454c3540f89de85aec0343c3527354fb79830c4a561be296395ac54194f670e5a7d2bac6e9188063a9e48c54bf6bce4fd9b451c31e26452037fb383a4041713295ac899adc903de3d65570aa4e89e806f254c5930f9c79265a60636b0a2608765649cbbc03de190c81e670fc19c1422eea622191b192d584eca4ff3eec8acd844e0d165c01b6ae77c16962dadce994a7b185542eba7ee78f2bbdd8fc7137765e8f4c65e44a865ec5f6b868df8674da0d6cffb9dbcd7266b5282a9d57e8ef287310f5ebe60e6e105a349d5a982202c76f2a493b5fc823b82795fa4950720d1fb9974c7f6bf13d776cf4a6200169aa053a1118cd558491e4d4579318c762a341cab19543a6af8f0d165011c4a3ef2900f461ae9cea3c58580acb9c5464c9a7e1173d1e5b2a13004701759016d92fb2f0211faab39c6c92aa42076476a54bb4efaedf6af79492be374576660dd69898f582a790e37f90d5b0e950d0ebb7bbeac8805bf21a731ae5fa89ef207e4d581d428fd3e1256767520191130de825fecbaa25bea41fea06a614e6e9b61e67847f21d1ec2f05c637e125cab490d4bb307421c6b39f3c536dbb0e965ccc5753931ae4274b93aacab1a1456f6fe69c91021f43d1eea1eace6342bdd3e17c4410bfe53d4d41861985495b0d289ac02597c2d32dd69126a926c01b46d058e3578c6a439f1e48a411a2ee414e47fb2850289cc878c18fc18de2b1a2b72e6713d8ad91137bf30774bd7e6126887c046bd741c1d65b23f5d1f331d0162313ac72b48ec585e6b8a2045a88a355ff82fe6e08cbb6bb5a7581b46903db1b1f9780ac1f1f0213b2c955c8f030deb1a5f6c834ffef70131e41e496c8022d3e3800794dc0884393fdf0ccafb582b43099df1e4fe0657a96a33c364a32c58003a4e5360db03e94a817e51d07613bf2e4ed7c8d3c7ff5304426cc44af3f01f5f3714c5b5fa36c18ec85c78ef5bd8a8a7a5acd5cf09a209a125a1a6f26fa4ed811cc388a9a90987a388cbf83fa1f8960ea16178606c031edb4200b8badfb1d77aff0c4fd6ca4116c9617af7c70f2245026c3ee29aeded6b9e34526bbf995df34d770d65d268955b8025d7d64f3222da5c7412ad5584a745f15be53b7457dff235fa8840fe2d825e6281ea814102e5bf77c796080062418cd53a0d69be78d42588fb43cc7224eb30f58b44fcd84ae6b8ce356a9c83b2dd8137b9e8218fd8c8a265f9ebdffe87b26c69e5d36ab85c66c781c0c32ee9d276f87aa7f2914cdd7afe05cb7b587a7841c1a8e56c5e65e42ca6071f87dc0e805e7b0cfc2a331635f900b091f95fcdfe7ec2221510c48e8c66f7a1fdfcb2ab9851b90a622f77ac8587b896cfbcaa0f847e9c8a282b2465060293fd0393883d35ae55b367d11b572bbe28c80f3fd653cfe94cd05b2a4e9212f56868733868482503dea7a236c4bd8d96aff6201b03281f1aae9e01c0ac12853fd50ddd91c18e6c80b51600b3ced841516d3fd2dacd20ccbe41685a32a8f8e56333f99258c631795165a3dc34e30ce48ee7f545c75ded2de0179d5062bdd403000000000000000000000000a6c76eb135f4c881a7d8b85a75717a016af78f47c6ca451cd8635c6f997bd97a036334ba00f04679139037facab6d49e6ab2b1e7d3b186b2b0738ce16a795f2a1218e416db0b80d20abbe228546affdb41eed6e20e4e331c2ef18fff35eb2f6d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001d03dbacd926f3f6650f7b6d0ff359749c4ff0611a88f3675e2ed339bce2ae60d1fad07a846c5270b525d27b08f93643291f074ec31a4c27021897a0fcf9ab3fc97c23407517fda4cde02eabfceae26753b1d36731751f30ade2e8f4fa79b74f7783242ceaac859dfbbe5951c558f42f0ff18203003d146cd3052911c53a7f8030e6a884e6a5c32d3450f681aaad19cc6a8686d310cdf7616c227289fbe84a6c1031da3befff8eb05785af83180191abb69f05d584532f7094c2e61aa562490c5f10a17328fe166ab59ef1814be56c091cc57caa8eee740c03a8f16cb74eef580b6a01a5e59768d3f5b5b162d13d8ee019048a28009ab97ec9a79e80869236db764f70200e8bc59ca0d2b233f986e2a562ff15c864f530c4df6a3bd8a96ac85a7a4913d02292527f45377cd80634769945055f44783f571d066dbd3122fb3131427a11b4b021409b570e22ee0ae745d115c4e78c7ea57ecc576b7c82317f3f08e82f30dbe18022b9ecc11498e7f051791426276c0cbf405db76ad5ffbb9997faa06b56474d27d030299550a79a49058d0b0867c8fb39eca90c8ec06ff299113bb8b659cebee2a901845030ace259fce623a4cdd181cab678ea0ef1a5fb071b687564b8802198b306c7fcc6145b358d9e6d386f4c544a90b4a63582642f5c52d89d1d492108df5f7635ca1ee22bbeb119c6fbfa5dd5d42e07a3e63e34d0c4c314922d33653657ee4936173e53f45c6a881bd0ee5eb96d5057f1addb2fd78f3c6b34729de8fbb7a40aa1e19ea3a9c6d424391cab85458f47104aca7bf1b093c1bf0e9d83c1d8acbbb21e6760e4020c4cc800655e8d26a283a429717dd5ea90a4d9e1f5c179f5e8154e8f932f5aa2a652a0cc280d4ba54889d5838dd69521a643caef1c9dba3bfb394f0fdf5861cb459a3549b5b65a6a96b92d18bd31deab247d2259f349a3d82793464d70f5caa6dcd6961f1b2c83d4b981cd2ef22966c19c534ee94160828ff15e528149371eea3668a26fc1f86c78b651f1bbb62c6d38313876bf79954313ac9316097172b3df4dc8f53fa9248783cfeacfe87aac91a6e2f64f63d5cae7023eba9fec9bd172a88a6e04d5512f6613f1e8d9f093b5881ddc7e75961b2fa73c9e3da3c7dfac0e8d18785f049a37f2ab9fcea920598b41a056bbafbf0a4d5bf32cd809729ef27934d05df9ea15675299b0fb21ca6e461ce4099f4ff1e5d4633874dbec28e13424552e691579ccc388a0cdd35f05c231cc09244afcb90e8c7ecfe73abb01a7baf1922b719ab582ea69a2a5039bbc1c513f4168bf860c8851211bf8a5d2c72080780b03121220eb09cdcc3917a1434d717bbff99f30d2b03b4661316ee0e09802b372ae2fdc1a4ff1f327adcaa7c33cad645dff526bbc01f53f9e953bd3e1d43929cf1b511b12c66adf9592d463b12e3d58c90ed514ac6301ec86ee9ff4cf9a5e87198ac61eff77757d3cc048088c6242a3233a94fcf47e6670c34108041b74e29af75b9533b26ae87db5c5c4529dbd22102da1f48237efb041f7026338d248c3b695a1227702cd941b9abe09b0a043a91c7cd2bba1b459fecb9f59cac9135403dc9c8cba965ac6f2ef61bd79d6f0f5e020d64d41adb6c4cca23b581484fd7b39ca9d695a3aa13dc341460c4ddc1f6967e53f697796174cbc25d0f146076e8c4e295e5cd5a27214dc4e7f6ba766a708518f07bdb89ab6f3fb3f3338b7f4eb276ea6238a60e62bcc8983387c112ba99031ba7062ad9c55ecda9cacf9c32a646b2657a72b3137197716cebfbe5d1c11537f5253ab48b594e651f79dd5eddb4641e8fdad71885b98840d171a9866826889837810d94187c47f43fa0eaa2e50bbd7fe25fccace97a29a8920f410cfbbb8fd2edde5ea1a0d28d418ca14aa1496172742b59a03e3c37c0612a645aa2f392aeeaa3720f8e36c7fe6414f555fac84dd71d449080058d0214573399731036a5112c3b77440a630babdbbb29814a6e207c6f689523e83bd61978cf1505f960aefbb3accd7f4b81e9d69136f5ec1d0a980f42c31e127e048ddd955b009e098158316f769e65cbb235fb01ad669bdfb51017b4094ecb828b46f21546f584820f50b95ec54bb6e8e9bd7130e40c006982546a4b96bc7702ddcd635b68dfd39dcc1cbc8487c9ec7f585fd944bd1829a332a15ca8cba520c43e2e0c981a21bef665d64be8dc63c2f0816b0610ce4fe13b0dd2c1e45643f04f838f419208dd63087bf5284387885ebae5d75b5a42f578e37530301406c08a517f0e824269e657ff478c85e7d6cbeebb7577235f2eb446a669a4a458278c14faa2b6a8954feb2c40b99f7cc1c623b6b062d1a3685a1d27d616c2985b43d7e24e2d3cd628668656a06ca0c33fde815b397565ca414f286e730fb1dc16b6e626d42afa1dd9015d8243cd890c", "acac5200", 0, -772939596, "6a86d4e2e1d4e4fc3afd29dce973170854f0004caf4d2e7f5db44cd25f512302"], - ["263890df03b63765c8b5feccede7775a02cebd40ffdd4e0882751e9b374c0ab1842fa9fa7600000000090063636a005153ac52e57118537148f1f44246f724157cdfe65385c5b60bf3b3e9d7a842c46c0563c1b9058914010000000252acffffffff5e9266f6835bd86adf4720732b5177a9fa0fd398f95203e78913c4e4c44fefc200000000025251ffffffff04f1419701000000000865ac006552520053e37cd70200000000016531f874030000000009636a516a5365ab53ab9caa3702000000000552656aac52ab8babf5", "", 2, -1413922606, "acce0285bd72f15b6e6ee23f73e8e5cfe91ea80703683dde2cb1dcabb83f8f38"], - ["88eb4eda045d62cac6e5926fa694c6e720834df880f3129c22e70b8717cd6dd1e62a765f650100000004ab525351ffffffffdc3817998a2f31c064ee03c0e939dde4c078b5fea9458dade1537c32062b14120300000009ac6a52650065ac6a0026b5b1171eba67d16df01ed47c4a83d1aba8c71248c90c839bfc1a515a6c314addce7653010000000963abac65ac65516a5164d9b577515bf485124f5039c4f6d57c74b98a03bd740ac200b6726f7856c2a20182378200000000096352636a00516a536affffffff01f821e9000000000003ab5251fec8c466", "6a5363005152", 1, -309204392, "2aa3305a9b7aa4b070fd9d94906e5fb980ae1c91df100ba3a2518f81b13b0f04"], - ["5e6292da04fb3dc6f44b57189b1c5f3d1c0c504f532449c527837e0b20147b9f07d906836b0100000007526553ac6353651ff1615d359b6b5c13282fb1c5bb42d52108d168fc3a61ee312a3e4f46fdb348a5476fec0100000001abfffffffffb112dcdd1c5463805fee65880731849275a0877751ed705d98dce7507ae077a000000000853536351525152acffffffffd5cb42ae8fba9de81791981b897dde84d84147f9bd73d55c46c843e9056ec61402000000076a6365516a6a6a6b0b86b6022532f8040000000009636352510065006a633e3fb7030000000008516aac520052536a00000000", "52ac5151536a", 0, -890669207, "c57c367c4dfef2d4236007d2ce97f455fdd8f188d18bc7285b829868e7d745e4"], - ["16be321103e110f459a9ff26cab3a01d49926dc7133a1be86c3a2c2ad2708f8c11dbed7b18020000000765ab52ac0063653c500dc73c0aec20b7541399bd79d2ce72b62c66665289835884a812d1fcae8bdb8d5bf703000000056351536565b808b00c126869a63a72148d82243b7f2b4235f15830da309682588d0a98aca5a8f583170200000009526aab00006353ab532f010ede0270aaa2020000000005535153636553bea604000000000151f3ef25b501000000000000000044bd0903000000000b8d659990a03a6820ee963160d87f5e508032ccd28bc25c85aeb6228a490b4b697f0b2ccfb10835335c5e32a0d3d8ce8b8bd7b125c53d7879443872d67b258efc93a6a8c5ca4f89d53c83ac6e14eb7ec2bbd6d3a36033cc8dc876b5c5e8ca5500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f8116f95fe21eb4d845f52d6aaba514046ef81be06d4df29e925e7e5974433bca2182cb7a3834e871fa8f7bfaf2a53eebf36e18e6ab4ab4507c030e345ed9f2934e75eb796f401b561797cf98759526bf69bc99c45b1dcdde8d00fd42aca177093b2d041d154ddd9cb751a910469f3515ecbb9d9c6f8a2e6a8bccf6d0cc6c6e5020c9315fc080c02ec10e96e33b352f54687f60be2ec9974dbc022b730295d9af802109098db375456b93df2e8a5e20354c962797b852f63d7d36fd2f2ee82d3d92e0b0abe143fa6a4c480deab66aea8fd6f390e3ce0aa1cd213c4f6f04d348abeb367264fd5d3929f6401ae3f9c8af9b74d0d0af7c6db745fa5de01d533b3e002e9df03303d0606bfe92cc70f42780066558d862e67b68d9deec739e0a885accfc508b002125f682c49fa371d51c326c7c0b472c2ba49f192e773d4e0809856e97f62bc97031ced69db8be5c52eaa9446b129e7de76eeb8defd8ff5416fd97ef0104ad0ea7e0223181d014ba31a7e7fa6104c0e35194da377e9e8d829309aea0c6a62000e71790213ca8d889d22109816f5b48727a6553c948acdbbe920fb3584e9124db97abadaee969ad1ca7f5ffe77a123c27b5d429fc59a19bc183f56a2abe6308368f563b0271445c440458cc20186f4f67969e67db8a3e435f2aee695fb25fc7c5e1e9cf370944bace612af1d2363d6e95f54051be46fbe4896c8d2d00037f937a47fe06612aaf51ef7df8e40a01da36c78769297a023698249ca4ff511c2f14c80fa7b989c2419b4b0054faef24b8d6bed728acadea958a033380ab5785ff32e44f5beddc9de012253102a3543ffecfaf8c711933ca3ba23ffdf5084d68e16ff210983e3be9bae3b0626b6cc576470ed0de35ded974bfaac841f5fe497720e2660917965063547d8ef8cd90ffc94ee4cb735ec168e0cc819d6a2cac476453effdbe0b810e8c48d66adfe300c0cfa6e281fce74fdadb163b0f28338d337745bba2a96bb1c9fcd0e55284ffaaad0f745d9425796b6e62ad9f0d863c774d17d106c95129cb7bd3862f2e7c901f63bdcf4c179ba95c7ed20b467755082e4008dfb438b8f03e93a62680c42d8e3478f38a65929eb270fa0de464868dc1d2b8b62d36d81c1ab9040df0304bd8d62cd5d8bf582139985fb97a5a7bc4473e19664f09290a6a01e6e74ae538f1d9efd1c10a11823bc18b9605ac398b8d24eb49a4ca50729f97a7a38192a709d3c95d9719c373a2a30472880ff97a8931fc51929d6e2727f9dd87a0460a3462f89d97630672423d2b17c2afe665bfc6cc3a94d30407ff36f377f2db0a0229193ae20804770bb10753b230a09d44c084de022237c7dd8e63dca3bd480c22a113c00487609bccbc6cd66f7c85594859beeb6216c51c7c202f50aef3c92d1dba45347585fcba6de0e685fc821e9cc050a3b0304102fe54090b0b926cd0fa2f143731a5df8e99749c0ac8dc6282dde57d94db1dd742772aa79345a387af6ca59d1c653bd6cb45945c8fc6719a0f7548465e1c6bb98b146e7129aeada968f60b3fdff6a1476686e45495db890b61a2e8b297b29ff728c7af47037207188176a45b9e6e094a31f32af9967c0367e4317ef8975197c4d29151e2939b52eac7fb16d287f819c7cad54f711fbe5bd66932d98a227e8054cd566c4abeec747cbd4e19541e50a9e3191de031429513bf6127b0261bc021e5e0a99f2bb32f029e7f3a4a2910ea37dea0738450847f0d00dffc5de18c3b7a31d6cf798969f6c5397a15cc00f38c6ba0599ced3185ca0f6af4cd22658c891391e8b0c9db3f80a22416e65be401246cac309016407c6a4f4156d4f45e28fc07dd2191fb72a65cfd304799a08783d64a552c91dcb2293cd7814716b69dc0733627210ec3f33d77244f37748bea32d1a0737d47ef539e385eed24fc392b28ba457d99b8570131094de40f5d5f2b5464b3846d6f78f3128d777626e42c0319a64779508b66bb3040eb0fc837f7b1d87e0a2c1bccd5af6e1bef8dd9caa24a057ae45413aba3bfcdbc548162024d067dad029dd4ca2b10619ec1f8272302d724df0ad527fa4d986d59c435736f8ebddc6fd80d55a85794001a2a4624c6a33331438ed936693dc40cf7fe2293c22fb2fcad5d1812e9ed8c07c7a1cbc4ec5f9df621a13b2fcbf7ccf4ec4e071ea9f0b78525427d0597bffc9634cbec1239ff3ba00eb36ea32706cf82345c1ee805b3b6d950d514c5ec03672dcb17d5b7cd7a40a377dbc3b34cebcb859c16692e3a9cc436df8a300a87a9090bdda7bcaf821f313b752ad25d144e19bbadee2e46164450949a423fe4b0a2aace7a84c60d49b5d7f0ad18115b85618c146f47aaadbd9f091a87b6e05a0540ede37fe22e2bc91e578b1d0356aef05db5ee2553a1d978abd67a33742eda58f11135ad9a1a72be001", "63516a6300536353", 2, -1628321804, "b374c62b60aba0c9688c8b17cd9d1ca982cbf857edbe97169dc6a77245a11ee4"], - ["b234b0d6037f55b5da27f9b9d6654ef6a53ebe7e569e0e882b6bd680abc9a11c79e0d178b90300000006536a65656a6adc4b200f0b40855b9db15b3de063b1c84212ff20c75c86a0bf03002c981f6423be10b6000200000001abebde87035f5fcca345a76d7cdde9ae9657e1f063133e9069c9871d20bf8b9d3ad4fdfd7f03000000086a53516a525365510c1a858901df694a030000000000b6c269d0", "", 0, -482461306, "5b114f3090c211d738ee8b4b5b36e79001bda57c1293f363c67699150bce77f6"], - ["2d457d3f04b3baf0bf8b1cf0b0235fdfcffda11efc44fa3994c6b3f00c2beec1f89c5d92ae020000000151ffffffff1a9bde70521c18619413c1e93b15b631437bb07b72598799aa2796e3398698a50200000003ac00abe6b41c89eac0ad8c08a1eb1c879393296a9ac7f6735573b23fb6e4b04647126c84f576a10300000008ab515153acac5363eaeeecf3b749c9f8c17fa02f80366456ebd698cbe8b4c5aa00f6645d4aae61d5b0d277e8030000000463655263ffffffff04515743020000000000b7e40d030000000003abab659575500500000000076a00abac006aacb765e5000000000009006a636a656351ac6afcab1a3d013eb12e0500000000000000000000000011463c8913f254305e1066337327e30994651e4c0fe0d97360b2f0f561004f837e134a4fa00adb7008ea40ab8d2f101d67ea18863b8a84421d0bf07c07f9406b7da84334e967c55323d64fc072b937162e75618109d5606a198d96d614ab2d80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004814118024f87fe5fd1e291191ca310fc82bbb2e223f8d6d26b5dd6ab423a6f82661e5bd458a323d59f1ce1caaae2b429a5a5cda3512b44761d6ad5c448ab58a36b8b7391f7a8fac7665d4e75fc45a206461b4465af19fe5aca17cb3533f26d8bb8e7b3694fc50606b7ad1a0e2ac62eaeeb410fdacde4c2378949dafbc7a8e6e0329bb6563c71f2f90f46e480e8d3aa11c09cd26af1fded5ddd322e98ae49ea2650309e2744d433d173772bd510578b70aadd1a53177dc942800dc8f67da6055ebc40a065ba358c48da7459b63fb7dbbec4141ea3998bf619c33077a1d3dbc0e9d299311185756858caabc079d91254925db1b19249ea96c7a7dd7cd5052fb167e0f630225dc6a2920875bcce5f6dfade3264af0c8939103239bd9bc455774c66d3a47930223666df287bd609d5e2dfa9eb11af755e6ee794836a739b59b25d57445bcf1b50313065396ccb6c783ba72e20c429b7f50332bb7dd54e2637741a2e6be4d7b0d2d030dc1d432f2c5f2ecaee83ec90e3e338493d09586d217b32be19f28c46a73cb9a022fb5c3579bebd772a0199b66919f23bd992eca6237b445a9a25fd8496fa94fb4dde18ec49ef234db53b7c95db33c0c61e70bb2fb92326e1918ad755ce7452243b1967d91c656746f088a4010a572b67343573dbc89db99a0013fa57de1ff238273fb049bded637cc1011fbadbe8d28d931eee79f716e1f43bfe006c745ddea5dbe1b552c8e42df021c0d5df9120d599f60e26eceac7a17914bd947a73a39bc181acf131a7b070c03a7b52d49cc0a35083c6344ce780a1fcb8614710ca4b2cb1d7308fa69854a7350aa952e5c7dcac33115483b796dce9adb1a4069d5cc203851840bdcfa668869e572a77f9464f202fba68b9caf48150b84dab7d8542dfa2f7aa82e3fbc9b2a7199049c4eaefe824a0950565dd2e2a0a8a9a7c15457dad010e323e134c76fd8df0fd910a9960847c077bedbae59e4111a0c121642ae28011ebca23e57edf177e11524ce23ab835bf2b7281738ce4be68f2a1f243ba1cc6e807a2986a8434cd4e723a157105023b73606a5fa710e22855536dba7fe4ceb8feffa30f0f78535cb5820e00e3fbc4215a482bdf84fc90836f13bb8f247437f5a68af538048273b7606f21f9cb438dacefc6db881017b5cfcd54068177cd5e76242bb12ea06606cc13cc65136dba8500233e5c6c5dd2441867c9cfa98eec8be51e9ff88aa51ee884441458d16d88890a9131f4909350d3226c3460f129e1e816a8879d381567eacbf1b430760209050189879680dfd3a159b2b6483aae1aa14625c401e5dedd1081963d01a754ad07edeaad5c06006ef0f774d28903025b82c6272ab8caff57b32fbddb28ff9afbe33a7c75999cbaff048b4121ba8bf1b195361a5bd902f32311e0f41105d49154b4db2ffb81e57767aec34831dbd99b9551a2f794713b89a1ca7b9746c220fc5cf819b2add29fd5c88e16863d41809e64c01cd956b5bf32acc16c9f4516f4f48c17ecd14977a46d3be2869821e9814375922e006fc70b7d8dd2e7907b6eeea4b42ca30b8f729af6f88056f7ab5811402fb27113200abe6833936cc887aa029bdefdd4a636416c702c2f78376c8dfadb12edc5801e47e5989f81986e92c7f580717fb94def797e59d8b711a0a416f7a198b07879d79cf526e280a6e297fa3009c53448158c932ddadc8dfababb4561a725f8cbe2a18e5f2d7ab6b246e3078b02272c01a9a805ee49ff23ac1990bdf5e2023b1f44e5f26388c70f2fa0259a5239e8e17922c6c19d387db14c8bc33c6f4a12c8715f7a68fe5920a5c6ffffdbf9d8766b662f253d2250fc24fe79f8032258035f012272d9b6900438e7c9b76b20f3bd52bfa09aaf26641e89a710dc76fcf385d6a7b08a248cfe92f53267e0da517dbdff7ce9241ab7455f9c5c7a66a254ef7cbe031f3293c96cabb0d342f1a74845abb33a58c2d8428b67817c14ed376aeb57c422c91704417c561454d8adc45401999c8c52071f3c63c069c03a453909a7421473d1ab6622a9482f6e446db5367642df97c249ee94d9bd507579550e0a61f2034628568fd40ebf6189167d8e804ba9b0fb8b5214e9dbe37206d75f18a78e9994af9f9cb26ca7880500305c6ac0f55f3ee076986a2480b39c6153cd319ead94325e30f53f86942027005827b716c159b251492373cad8d7997bbaaa3940a0984ede2c713cab6e2b0693b34100522ae77582eeafa59d5baa6f7a3291e7e9f931bf6950933103f4e1e1787406b93e4ef9d7ddd1fd96956377b945222e2a04251765fec2bae64fafbb77a0b05d2d2f7c2b1b6c1b4cc39013aaf3cb07b4ea21903be20112df1b5fc370978ae6971b90a3235a719cfdd1034e8a2483b4dee81bbf8aa2a2ae26eecac81faee46e57f1abc3b226099fa086405", "5253ac536a", 2, -26994064, "cc32ea971f2108e4d0921426b524721f9aba2c5d1cba4acf567005995272ca89"], - ["923f05a001de0a4301f1d4b2fd4696ffcf4197c10868df2d71cd25d82ca4159b44585c34a4010000000651ab5300ab51ffffffff01292ae8050000000003ac6aac00000000", "636551656a65", 0, 1477214083, "f0df97654015ce6dde4d13f2a575430ff5319efba996b6d45c94d9eb56cf71db"], - ["8f869c93031690f2c4fbe32e4b1f0ce3b36c3455d9316be1d7875a118aa2269be3d3669e310200000003005163ffffffff918f7073726ddaf54aba7642883eff457f4e719cb08811b02bcd0f8aca8a1603030000000252656f97efd5113396d7449d3c513eddf94ff66e189444fe1a55b5d66552f1fab47bfb12d4a90000000003ab6300ffffffff01f40922040000000007536565ab5351005af2c5cd", "", 2, 1367017162, "4bf65801eba8506064124016f6aeea99098e98a4e5abc6f1f4ae914276c2ed3b"], - ["751d5a950165dddd3a1e58242ebd845d4a8bedceae2e4d9d6a3a172a86f3f0c459b992d35f0100000008ab656a5252526a00ffffffff0207b69f010000000003abac63434ae3040000000009516a630052535253535dce849c", "52", 0, 1130715713, "d5b83082169ebc6c6a2def42c3e2503e4c5e606263f13f4039cfa43a9aebf58e"], - ["d1f299df027f4519d6865344bed835af1fbe6715ff7d2d127dd92016aa458a549f5f91f1070200000007ac65ab53ababab3fd85818c92f96a6c8c700ab57588af1d5b602d31f74c8f88dd15655bbc92a6ef02d54110300000009ac5263acab5100acac9ce5430d0375d7910100000000056300ab52ac7918bf050000000007ac51ac005153651e9eda050000000000cac24d17", "ac6352ac5251", 1, 2067075260, "3a703adf7fb4d96d0e97c8f7e60e2c82d5c04e75015a688ba1ae5f485d6a0b06"], - ["5b6bdcfb047bc45060210ab15013e620c77a067ebe2f3615a290455ed9809ff55e10c894a40000000003636563e37f71ce833882fe9303bbda640fb60d9754b058555672149a96f497e5824cdae6f946cb000000000800536a5300656a51ffffffff211b2c1702ff7d8a0f133a6d100b67e5b6b3702f07a7ad1294662fb3d200b71902000000055263635263ffffffff7adc77ca5210cedf6c5511d0b6a748578eb781d64aa5fd045af35f4d4eb7112c010000000100ffffffff02000d51000000000008ac6353515200ac65e7721d000000000007ab6aac65ab535300000000", "6a", 3, -888625896, "df0226622a0b8c9e23f7ec4fd834263a9edf664db46b2e8089408b7de36d0806"], - ["5e354c320285fff7beb8258c34bd0159897491b21339fbc5c6e1325d88aaa6999c72b43743010000000453005200923704f97a14df059a05a53405b7dd21eb58e7e02d8c5082bc475be307d0c2d9c22443530200000009536a6a6a65006aac63ffffffff0293a4b60100000000096a63006563515253538d58700000000000056500005363a2f654ed022989e70300000000000000000000000049eb7754f96d89e9d4f42371699d18ae98bcd108df702415288bec5b1b670f72f00e7481173ac94542bff189c9c149e38efab666809e412436f97c026104f48ee922c99b9e86f30ea21263027b8a9ba3b06af0e39ca24995abfcef05d9401e5f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c1e0e3561a794be12be4d654e99a4c78ce5d24412edc46fbf38333013c000bc4831f5640926bff3ef90bb11e29d582d165a8522aab49fda77e8a6bafe2dd658c1cf61274591f071e337f31f488269f2836055d6b68d594985abc4c67f20018500841183110ed3f9ff256c6766625473f435087cdaa2290d0fa25a06638a17e50032063205a57fed257a1211486c6792e8927d24725cab9ed5ecec058c4913162c50226425252aac7c5cc69f4a3227cb113b855496dfb60c41dcb1b6b7be08b8cbec00b122d80be037ac46c2ffa62dda1928b664ad3814f7077ea4024e3a587eb42318a1e68fba43e0342d6eee628bc5262c47ef40bb27b9abbff19b7ffa52dec0bb5a8022e113320366c596107a7e469e2ba913562bc5edbb57fd050c96df285db5fe4d603288cea501557ee450def6f71acc28429132a473c181799241def27c25988e64e0303c50eb9778079e86d1f21ab1c68e13c049401a9f5f7843c03df9eff309e4173030ff156d0a4291dea8b5bbcd002d5ef7db0b3d2cee368e5b278ccf172d3d78323032ea1663a547c18cdfdf5a1d831649e0ce1182fed32b49c48dc47555b4c945a36a40112af1b50831edb36760f2255278636bb17b6eea93a5ad6fc30c6e403ab52f47b8ee2c82f0cdba9f9be455328805e63444bc4b5208d33a87e18ec15aced5624a4945a7ee183221606526f751d6f2d9811f757d746ec7c9436efc18d0930e02376b6044762f393972a351692f00dc1e675210efc93c8b867fbac632fbe209d9b00307f13f7f9ce062c0d24208d47994a9d5fb02260c6c60bb0b0117df3fa61707f790770cef83f8933377da35cdbfa74c81365114a1cc11aaab8f9c60bf8534d7113ca2886d6a807ff566ba4b2a883edde88ad1ac6b20ec9b62556d39c551433dda9f87eb0bcc50f8daf54273f983c5c658449d99ffd7ff471e474acaeea0b71ffd08d31cd4e4ab4e5222fe1789beda3ab2760eb13f56a024726a0ce3b430da72f1f40e35054f6de7214a447c09b0f719fdc8ad79084e0a947cb381397007d515f705ddca587d56e4bb367d41dba256d40e177b86c8b4935813ee43698123c4c47b72f9a58af4cd55eab62947bd6ec8030ec8385b5691db3cd7b4504111ab4be3f41a3dc16c0d1f8792f54050a2749a1ddd24b4f9aef3abf9f3c27521b88916a8788be48fb432ed2bc23c05f87d353512d87f2bf00cc32344272db5bb51b76c45329c2f43267a1c811a4f3f9ec01323a8a033fe483b21467d04ac13be856bd0556b7cbde8454b7bbc3e2a0b837caee5044844898a946d66017bba73f04c0c3bd04542bc16ee076e1fdf68d56f522ac56d7b47111b3cfd7176d455a2d8f940a4304fb4b641ac42560ba59493780733693e8ee99229967afa941e89dc3c35d907ac6f5e1d376323d3ee5a1c3417c32df969e20a1a1b322e040ce1b94c728d4bef1660fad46e53c23b032e5cc9cd758d44fc2608a719a3defa848c43ef4353d3e9da6677500343ef3c2fc3c2ec70cedc59e6851fb878ca291d0380d6e3201292d892a2b8798351d608d0ce21eed250a8dcf8ded5dc5a31b68c0fdf5a28f5dba4d2f03a757fbbbeb3435a1a7fff1ece97682bf3ae7e51d50280181c00799bc22bb86d320a605076cd86b0c22faf8fef0fd899a30ebb01bb4e97e812be0faebabaf9a179846397964350569cd6c16d8fbce627ecdc5ad5dcf31b4dea0c00a20e74d7e6b68ca04c11509f712fcb73462ccb3763696c5699716a58147e80af762cf5f46045e2a4f07396c5122c5c665a02abe8cff1c9f73fd4ecfeda626092a9b3507eb3d143747ad703c17532f21fa758393d7e8851d9c327c9bacd883ff282d87d693afa6922bc550eb54534d08005d9c70d75dd0105bf09c41f3493661d18ed38104633749f9425b0bd9e8750702f8b375e36401987de6ebad2039ffc53fd632b4d6f526e0c59b0d6a3afcbb0b1ebea60e10aff9d381aa9a69569d02c256f75995453081004da5f75c5790d4a741878d2d7308eb9c41efee5e458bf112c5a111267815da76c6b5835cf6e37c97ce107dad926d3ade0e2616d08fc9dceda999513b6aa2b4cba8483746b2878786b4272ae35b458cf378095a5674ccb68998f0425e92c71cf47ff03979da9e132f24b673751b34f543ee036b3a85f028054cfdb0f758f60d4a4a85b7361964ee0b3ec8804c13ee624ce83c72f490758ca630a98bd60b904d324aac2a3d44b927c1294d76c4421fa0c8dd6d1f196046406d4b609f1bebe7aae67224e6b6ed15ad55dd14de50f6d10000000000000000e784b60400000000327e2f48d2f989a460aaa9bcfb4cb5eedafe65e2d3b34345fa6ae6ea44b0325c8883d31395bafe8f3ef5eeccb500cb617f5bdab9b5471a9fbae3f8f639464f90d77116f117e85b2a6492e11da444601e805f409dab7c8a52d0492c6c256da315000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001150e9b253c2e0a86afd0d73806145dde681467c2d9569ccd30a164aa034c3c77cfb612d7520c06a5c7fb7c2cb403c3bfa70c1471da53c3a9e33210678cd01a76a15ff410f17d3651af4a52c114999291e458a55b804bdf2e38b099b6614bf6ef88920ae88fd1bc2b1168e3ce9d46bc7a96d7b6f81e1334482977675f7389602022f2367ab043ece25b2393d48f417cf0af4cfc5efa8502a63ddb7bd0016f56f5d032370645e903bf26c6a25a67a3f775a8a143a1119993f8a3979fdc9421d9450f90b08ac58969fdf6c77f74be76915323e69e3b38c80134d3946843947d5c646c8fa1b711487016e961a04f8d9506dbe659e545d1608a139ca3d4e39df88468355320204f38e767796184c8b07d4c5ec76e5e59d4a3ddd12f777d1cf8497b5bf8bfeaa0310e245d60e80476a0e62c240efb969c5cc55fa8803cc914bfabc4ccda65f5b3a0219bd4ffcb3c548c1650657389643e06902ec741a571584b3a2cb25ec1e7ab716030a1400d59ffa342227b5ad12e11ce620cc5ea3d7758bca44d886a1dd5572015a020de28b533be263998c2933ab9e2be88c3133622348a588e65a0d98dff3f8989064d2ec2d2e7bdaf09224641106b9de70ab0988ed69000db2ab1c8432316bb91807fbed42f4ef3c86e70025e642de1949f38bc3dd1e4301651571a71f2d551896ea71eda2a58a636b25bcbaae8a9ee088a431d22ff75bbdc53476928e414ba67974f754e119c377b1886ebeabf22f2ad7a77dd68acc32220c61e96a142d419866c3a575785c594d8539991da7f1ce408f3e3b1b5f21ebed9eb0737cc3e7ddf9466893bc10dd8d7bd5f6c7bdb3490922d51db17b8d23f4755066fe547dd4e81dbfcf4614ccc5689968cadde59d14aa3d2f29e4d0d35cd1e310406d3fd89c9cc39883d7d47342176cd4b32e356cadaae9b5827bcf8b9e60005a4ac2796ef749849db7abd2612ef4778826915c40f10f862179f379c002107a3bf29ee0672c5ef18f8fbf60b29383a0ef3bd8db14fb04bf11515995749ac6030cace08f7bcb45d760a608dcefa4ad9b20731c369454f88f00e82a039d0e3637b167d712b4bfc77d94bafd7bc1ce7bc02dcd75c4443bf09973b2b6bd56ebdf600405c2894d4a3cdd9a2f214521c72592026e46d8e0ff3b05549fdb6ce80f13948eeef7acf9e5756d72b3a31fc2fa3f39990a2082ebaad0043b8e41779d4504f29d5d7caebbc41aa184d353fa348cbc991777ebdb1a5fd37f71674e0283ea9da3da822731eddf37361e37b8a0af0f3f6bd2cd2f43d4d425b81d885d7d5cea4df6f318cab45da3741ca20f65ea495879905922c4083999a96f686911b29fa5733dc426aedbbe750f3a51dcfa52e550af3184e898de0c3d33c37d6ca5f4418cab6a2a85c8d8aa7735ec5f0b82fbef81b29dbabe8c6a3848f691f6b9e00b2d8d9b54eec4f18c76a2228bea8f31d64a1979eef363a39a99aeb9a0a11704e37997662cf8c9f5286250724703c494e709c807e9e7c24ebb35dd5e6433d9b6747ed828de19a121db42778f458064d1bdd8a6db1543ad181332eeae8866eb16b8232b580526958f7395f0821d1a4b36c9441c67a868ecbdbd662882d2d90f33d90e179614d1bf59f420a149f2594f29c7603bf88d4e8a3e900cc3bb6a00467ca02966edadf0685d1014c4c8607a537331cf18a964d1ab6602f3ad375c84df4633b0a27a384a8b91dc48d9e77cc94f8ef6cba9d2725f61daa76942ead291c684fc54c189e8867d8f073d6cf9f74f44359aadb512c32a714afdb45f2b665ddf232764eced0d23e8fcd546aaef12e7de658e48d3da2daf73d846402ef78049c028a37603446edb1f5e4b7f6691a1582a1e9e21051a7694a1dfc0a5d561ec08c770a722caaad8e0081cfa685fae4b098df76bf1507c9faa5d4790f7ca5a238a26349cc46b59f126290ef0a94ea91f63bd09ea5b0b1d50240d86af662a755431e8ca7bcfee6799163d41a85c29fd2430759b338e8e7babf64de21d70cf536014c05728fd34d3df7494b439f5f480636fcb985194d70af51bf51be46d73dc28d3fae193a050d07b61214b5a8f4c53625d7174fcd14606c2830cf542cbf0aa252b74bffc19ee7965ee6aadd77a5117e0266acae50b6200ea79557eec9a73d4673d9e97cb755064034e2efb7f0236d54a816b5ac45e2c56ae00404f5021999721efd34b5e3adf8fce84980ba4bc838935ce5e53761b95caf7b974af4d3bdfabfdfa4329b9dabb1e09517eb1ea85c34d1ea2afb83b0cf86353fe2201159d0986ee10aefc5ea62bc6a15079a894de7f3ae25eed007ad99522b9c5dfb6e82d5f5d609dd1d02d61b8a3ef9d7b0989e8f2b9ee591e5008105203719e809cb7d9c4d5adeb70ea4bcbef263ce049150593c8731c11aa340f04c9849f7dc7cad305018d605a0205", "ac53", 1, 792228157, "2088ee52ecb8c714401aee4a3356838d30c800b96173e7f1ac28221179f1b3b6"], - ["c37a610b01c71369e871235b3435acdea25bc9901d24b190cf92f78f280716561b4dd56fe70200000009526a6a51650000ab654c19438e0137821b04000000000253530000000000", "526500520051", 0, -20672650, "0b75be294d98303b5368a9ed951ac29644ad35d8a8a9e9042ec3f88c8f679e98"], - ["5ab9e82d03e109ed8aae02ea4f6f829cd42795fb6bacf867be39bcf24c2217bc8e34a46af400000000046563ac5334e20bee58bf5c6c777711db1f5c62598b2f8ec98c9cce04c26e80ee38ba021322ddbd280300000005650051ab658d404a63eb0690b87ac41e8c4839a0cd7256791118fc07b8aea7aa3de8c45d7c7fd7b9950200000004636a00ac62207772046798c70400000000075353ab635263ab39a8360400000000020052f83cfd040000000007acab51ac5300526f12bb00000000000651abab00ac53000000000300000000000000005eee960100000000d6da2cdd09f3c7d6355eedc6e348be6794b04705b5249136972b38e40c30c290f376c7474abc89fd6d32e4caebeed792b393904d48d9d74f0db96b87f08f517f14811babf7dcbf682557c1b9bf74ab58897fc7e3553e0818b6eb6d1090f0e07200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000735e2f09d1454a1743630ce71ccc8d9a1706264030f1fa516a0a6833d9119e7a5eb34684ecef1ceb702425be86ce253fbd0e48a2200e905b87808159aad327c3830c58495d98279ba8ea2110f5dd58a91d3d71b9bc045d2949c61e74ddecce8854df505ea997f05cc855f04c578cff8addda37042c035c37927bab5396c9ec290208cf8b7177a87e9ab0869a6cb167431ffdec665111f51df1b3470393b71ec1090309583083cd17f0fb6ca2730c67ec8bb0b1aae62fe1597f562825c09b430db1220b1104881d2704ec983a1d77607f4d36ccfa81721af4e3b9442710bc0c7ccaf8203008855a70d4aaecc7b6a12455e645344483747e2203216c3fc83e3cd804b90c0229f21684587a8b92792312a66371e6141ebbf5cae4bd2d317e6eb93354a323f10205fe5ecbb9d30eda3ba7740e2fb20c1dc4bf8e8d3a1186013a35348d70eb27f8022cde0fa62c1a355080a920da4ea2114e5eba9f52b067b9a4b0abb63af4abba5a0227dcb90eb00e27bd55c0d3d35c4de60f5a664a1ae9ec4a225badc2c8860fbcaf021cea261d77efc5928504c5209c74f6df5f6fc22a3580b00e5ea8b068fc647293816917cba931efbff2326454b7cffe3b67f05d5fe3118a4f1d904b8b1394fb843be11a514af571e39120eda679f30903bf5ba7cf8c5181f42c747060e9d7db230032ca5cbf0b993d04efaf74aff8a75ba05ea982bc88682f6afe29d13f4e6e53a5fffba8e2b773e69ae21a52de0a5b00bdea7109cd1bda86b1fc3b8e821062334598ddb4040e29a902d9964c03b28a4a8a738f9d1c40d9de5df1f2b09fad148b8462486e24401f60d49333eb78e61475e426fb7c23316c203e64011796f310cea9c1c8f18ef44b05b5cce4c8eaacfed4c2604a781362b0e546745a416203a345f1b21dc0ef6a70096a570795c606084d167238d2bfb31013bc3f1d9fcfc1ffd7c470f004ad81a602e32da199d3911b3a23f65e5a2d0a25ba3c11892f19cbb6c3644b133f4eef14981263b4b6e7fbc5ff2cb81fda7e05e0c27eb08d86bd65e4f6d066c002f5d4c2d4610e59e21bc1a20872cd79d4faaf97ca82ecfb21a3bdac1424a7607f8464fd259fc3ccdc843745922b8f05e4f22dc1b55dc565395b0786a7b92f9cbcffe5d4041d25c6c9ac3d725842d35e1c542a204187d55cdbddec16618f0685e6737eb4cd701d293266db6b8375989d4c932e0259198326f8f685645f3a6b235dadde0817e17100ab90992a9cc3ae38c22cf9bcdf2aba92c46dc194c8377ba44fda0ad9175470899ec0bdb70cdaeea4e64edb0534f35437d0979a756d4eddc22aa3ad4bc583480e094356fc18951563bc1ebfea5c5c6f963a3673cff68a5c220a329481383a5f9f33a27be6c00e63f08cc348f68b83794e7bc44079a5d9a7dc5b5486211a450bb56104d19e1e68b35b10445edd162485c86e4c9780d65d9d1c37427695bb780a9d7cad553c4c2ca90594937677e5f6994f9a67a01bd8e6b415e976c09e26ec85bac494ba93cb4d44a8cf0ee2192e8f24e98bbf1bbc8ae5e4d942d8012885ea21310079542638e1e1771f7a94ebadc6fecfea62d7292988cd68ec731b909e46ec12f8d3d7b84a97149db7e23bd1c6f25b43a394542a1dacadfe9f9d3ee05aa61a9fbca0558fe9129f247dbff05633c90237b91f9882ee26de7cbea5b3147cc7c3f0c4db8e73996b48c575477e40505548d56b252353ea0cee4b06c819fd271237fcf109f338ef67d4af9a11eaa605900988b77a0192f80eb932d158fd5835fa59e01ee93c9919a33b1ab47f76e122ce84ec81860a5ae7d46bd8e50c74eb242c039a7e1df5721cbeabf224ece6a692162cd90e6cbf8e7afe8c0be8a2b206d99cca8d43f42356f9680dd7556df07149db25814b94ab45bef1ea1f362ae57bfc45b6e4e07cbf896ff2e4b2e5fe2e1f2d64db1b2764ad39e5441ebe26e32e15c99c750112af7c747f72d139a00dc0de75ac81e21534fe8c25105afc848234974bb73d03b676284067ce41323d19e518b0993dbe130cfcf980d97a0d35e747604240267871445046da838463b57a9c3326b37e31a5f019cca40049edcc0004ceed48be212f4aeed6413ca06382ed0eb7209ce109279fdb6505870fe365a4d104c627ef6901bf8db7651d268b6061ebaf1a185af87660ece3ca26b77c6d9385d6bc67e861afa95f6d473b174bcfa689cacad64ab3cede4dfdeaf8b99f38bb4e165a68d77aed810beae95561ce0cb670a769277ddbea1838e2020ecfa60ee95f3bbd48e5a9eba8d945c55861000000000000000034e6cf0400000000cca9d3d619fc8d7211d680966d87ecb9d6b9645b9ca31f6aa1b81bc1e6bafcb97cba5ea368eba9da03a72146ba6a314b1fca61d72e3f3252cfebd0593e33d76fe62ea728ec1c0b226d60ad73d9d6bb76f398c1fd90163c844d5e852820627b3300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000536a999386d867df0adae6a2b00f9f79673f107b803b39f4944ac453e249d6c2009f5d73f6bc2fff092c70d6c0305b9f950bb4662ec280e8806d5aeed1717bf1ce89a5052ce2670785954cd2fb8610c2151710787bc2a94bec81468233a7a313d461d521c321bf34e8df6e5913684420511b7a044a9c845466e795992629a029031a6d4e2c98d1e43bf520674b6973ac0c122a3e2ef7a8c414c573f56336195bd602014cadee939107aede54b3ae55ac898c1bbea23ef213e11e8efb2bfa7e902d2f0a2e2c5b7b81176432873285182500f5389fe222105752edbf5717e95f937d14b0138d1d8cd1cf8ce8c70926c1868d900ed49829260f7243cbdf3f0b95306c1d06030aee29be19a9937d4e87b7e6662be97f7eed3fbd2fcfbdf89f1e9c117c2cd4e30224cf4c3a5c8a17de38ddddd5d0e8732b9d7aa5d3aecf726f947d966e03f68f010320fadec34f37b9f30b33bd35d834b5d354b791ce106fe3d720aed62e9619f729030f311b2c5924632e6fa64790b76ae02b80eb547f77bb5f9a60a92cd394958df60200898a92142df4a447299f65cd8b572a1659f1bf4f2eb0b4dcd65696401bb2a1af31d96153fb92c80c7c77d05044b40b8e61ab0f9080816e29f3ecd2f74e7c03c1213617ebaeea3e8f4e9ee4b1477748b3d0db29a54f24bd3fcfe4cd0b291c38eebafaa38057915b0386de46a60e650925039740ea0b08de0cf669cc317033f23c9cef474f704b7880500333a066296264f4436b706c5ba22b1da680e26353447b36d1cd5540b3756c33b957f6007f00353daf7ba27dd4fd96aa346d621a66a3662faee7b2719eb02ac9f3cfb24c75be868459ddcc68fabae2c49a175eff57a04fe3558414a4e66e7dbe744f744b019e365b9cbae950345dade78d143c133b5299832d50a55a5a27f4cf45c1cf5a1eb3b2dcc4ef431fb82ef08e78b3b2ae3fa0aaa9e037ade725d8f0925ef39c3aa499c614b320df9a244b18c5ad78fc8772a2f6640654c9e0f25bbb2b48a6cfeaeba4c278995b9d82617c1fafeb706bcd067e8a689e325b5a376be97abe83ac05ecb8e3d33bc811b69811458fb29ae36cdf98514534bb8e8dc9f57158051a7188ab0f56c008918180ea80f2e6072a8aa59073317ad26e6552f5b3dffa13ceef34962301c6c20c0689e01b136ae7156d492ea02046740e3dd1181df5380170b11a94fe48694dbb97056055cab407bd8c8a43e390ff1c80bdc3fb70be1d1d41259c92bd69e9368a63eba401ac129c2b3d49f32ab1af331fad4e312f60ab45b4cb3e6e5f7380cb35ac68635d2ced20429660a7f7402c3cb67776482c03aed367c024fc9549c18f9e109d65d03adfaf795b68c4d25f5edbe69fd3a43f66a7ebc7ba85e3655253985fd8382e97fb41ad948a2c3d2237f55940a2bd5be4ef3bb3361b827e249ce454ed77d7a90a702779233185b90b6870f4e8030b90c2391dc739ce34dd81c5280c984b2acd21d151e9f2e007aa1dfed84d54bd170e580c7a72c5f0b61514845f481a15cd51266cf6a7be80ab6ae7d28fba090c82ee02b13bb9307150396a7b0d9184913459922ee102930bf142f38366bde2c982bd2907e5a044d24ccba17703cd3b0fa29ccf0c3c509a9e8f175194db5085ffa4786bc891e584b5ca73b0e6a464df858e446617a028e1360c513d2c338cad229f1711c2c66d2ca7490dd0abc8eee48e9eecfa117dda77036b122787f611f4e38f48e13b5249d309941f97792cd9c8df7a46ff9b8de7a53d7bab0f6e132decca6374614003ce0103caaa774ba2254980d2db3de670e8fa2d198d264bf907be7283713815cc9908950723391fe89fcdd8629cda4a760fa743d3861b7d22e151f6d3bbb3c625db4dd1badc1178e596fb57baa73fcb6a07366bf0dac095e1d5a782164be6b9d116cac74237b8133b6c3d878502c521aa688865704050b0df138fcb85010ebdbf686ab7933e388d017f46d36521a03e24e6ff79f5248da743fc5492444bff34b5716de01f393fe1a9bdfb98a21ff168040e00ba61a1d1d2e79f0d61164ad92b4e9ca1fad22d3f4613e5a35b13ca99c1562a991c64fb7d834a2c618ece909aea9f68f2ec4e7779896b3ea253616ad7de9e3ff840cefe3b269881acf53149d5463b219a42a58c503b478cf846e266b12a3030cddc05323a4ee96a0bd51a0f255bee9ef297dcb332f45d4836a414d25b4a7303fc5317769ba204783f9337bb0828e60082d91f59521966ad0331af7ea9dd52019131fcea28fa856584d077eec49d98b4cbb3e65a8a0ed38e0826030000000000000000000000007b617d34afef78aaf6f13ab83bde532e6f2e01dda1596cc29e21c61f0df2a50c9c3e66ea59af3a5bc19ddf108ab75dce7b7a0dac93e5070dc39c45ff798bc50d670e03fb76560933fa8cad7ae7c5c9ca9cf150b450c9513c3536a128cfdd19bd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000701aa0df7a0ba53e45a796367ac3d04460622b0977109a77dbf8bc231057f7cfef09b90104a2f48e95a651ebb4679c814f2bc29daf4febfc9652e45e03a7ab64d586b938e2482149b8be8090b462dbdc8c7191f99a960e28f3ce27b5002ba8bd9b60002fe2a29b5709a7be432c0a4b5fafde864317d5ea43443c36164eca228c032103a7df0ce0099f371129bff986150fe7d0ea4cf7f2df149a36be1bebc0079e032ce463446e37898130694485cf835bb3c644df57206e78296bf9124fdf91c8ff0a06e832bedf55d7a5a53133c0b9ee954f571f9b208f4502d1f1a8fafcc9346b1a23c1d108ee01a4f582f2eec233ff7896140635ceaea6d91c3da9b987d52615bd032c53619128c8be98e40095e227659cd7a4259dbec8881c8543b2c73b5f0ef96e0316bc118995a8e2a0ff674fa6611226390a064875784449ae03a826506e566edd0324dfc992117f810a380a37d8cf1352c0864348d357180ba14ac3e7bf4c0fc54b021cbbf423a4acf8fe0532a745e5a4ad97b8f5c0548bcaa53edb5129b84ed090cf022f8fa100a2561c2dbb7f4c208051fef612cf7c52626eac71fbd2c06a69f71be0de4c514935a1a58c65eb391f915165e242ca1864e9a6e913dc4a9b1216a7538e9c97d048fb4015e7dc0a75d5bbe42150256fb53913cf40ae79ca5dbb2ab432766197c79aa8fcbede0fee55bbeb6ae08b02e310a56ef0bb98b01ed62bf2efa20e5c7272aadc56b6fd24dcf955161abf68c885008196efa3946a75a4755f75c54d19cb2a9c60a44c29eea63c42e66717e406d43023eab2a3c785786d6086b072f5862d6fb612c94567f2f6475c14fe5ad243a71def74c23fd81a5f6838cb0225d0cc9d27bd7575f93c0d22d3563617c9209bcca2732c0ca2e8f0d785009fe98ea47a4019850e3c8bbdf8b0f80bbde8d4366a12e085a78d3ac5447b9aa754f40dfe1df24e53f4c982032a218b66574575607e1ef1c39ee10b4f44afe8b255c5a89740b2b61ac4a980ddc0e78fabe8bae29c0f7a7c24258af9e7fcdd2e0b615dbb35f455fd6fe7a4b9be2c0030b8816bde74b2a2b9073996a88bc178eff8a5fc895cb5fa1bd2992cb39f0bf5c1c5d830f867b28c04156dedb9854c3d58043b4e03d02a58a243854166581cc19bfac5a273b8f0206a437772ec39f4794e122b3a633acfa4b2a31bd901a582727d70b74979bdb8617362d7de373249c52ed98660894c0535aea871a7dc4dd1bfa248dd7c435e0a1eddd28c75f16e9aa532ed8aaf3738bdcef4b6c43c79b6168fc3953ec004ea85ad9f9b5df1c58e0c983d483c42e5764e22659048919289395328e5c59f345d53ac9e74fbfc2c8b91634b1eac9191b7777fd67fe05be960de78306638c17a9d3c5438b6e73efea5803849f9334f61fcccda31c9304d1be000226e90658b60ce00e0a9f3feece57df8909de82f3df4719919dc5ca5324c5e34378c932997eea62b6990c653b67da3dfeac72dc66587afdc76b287a99f4e5b67633f9f62db87064912b300ed25f206b0d3dc821f8b30f4e2124cf3a1fb9d0dd011fe40e38450e58a166e7235f8cdc2492973e9f7f341a09204c73d6579a1c6239cb2833f941cba615af786f7a44c6e885fa15fbdf0fc1fcb5dcfc3e4bfebffb6f7195e15d51480c9861e48e992d2b4a8cf75a2b48267078e3bd4a1380c32f64472bd20331de1f5d73a5376ede6d44d03ec4a8f73cc5279df833c930ca746f69ae4f2b88b0661becaceda229275b097872114626a0fcd75be78e1612d2963b296397d05bda98cc0336179030c65e1db466a67bf668a231c6227ade0bc4fcb1b72340db4389c3c613cbb58afc6561ded3a17f03841c099df91663c084241b272aa88d416179973317e3d8e895763752d90688bc86723433084d4eab4f9e9fd80285c11b5177e7a7565a8f1f3db4a1306224ade352364c5de7672df531a9b216bfbd7305b061ccaa3ab0b487162fb5b9eb2d4af3657d18e6d30439e708efb11a2623d748203c2e16076a785a20c89ba4bcc67b2c435414e1fc1bbc1d92d3b1e5c394ac76cb7ccb12f2199cb7ba220a9b0adcb03948ea970658c821c1141aee11063854c827dbd535fa2b8102c5d66e945f9b5f2f3dc77ed8a5b3ce86d4d846d386af2ced8f3b436deb3331c8bcd7dbf47d013fbf2938f807eed7a67dcb0dd7779dad330d4452c8e51d54feeaece2425a7b9365edc86c4103fca6d5f5a6de4ded825f348667112ef81c7c08dfa6fb3c9a5244c15d9cca7707623e0709d70793f2faa86306ded9d253ec36f4b0e969f39d52c60ba52d18160a2794c3037689ca473fa0b4cdade2974898c7956d832fb7c51f10d03f15c132d5dc2e76abe2de7281b26aa2822e0e7d63c87b160f239cc262714f5891a1771270b8eaf6bfab0e875169e9e9bc631168f68ca02", "ac00", 2, -1904449756, "93d83faaafd586410da2c51ac54ca90a1c7ba277f3c1f0a4509250bcbad02713"], - ["5bad4ad003654d36fe20e607b87fe2b69d7d84611e6e60ccc76d7bd2c9a34a0584bcaf6180000000000852516a63000065534b7a4ae34597d55b85b50f9af145d5a431e555bff1920cb5f6ad8b1ec44597aca44f9a8b02000000006021d235c2d374351ce844b59f3179e354c917eee912653eb28d2628928d90342187e5cc020000000563656563526388abb003d52a940400000000095353536353655100525a8f17050000000006abac52515365f52d360000000000066a525152510000000000", "5165ac6a6500", 2, 2064777289, "0f272e6235f73885c67c79d308002e8187b46fe353bd83f1689707a6d037f957"], - ["2246e41803f905369dac3f896e060b879aab77d8382e2071c64be74d932deeb1938d71e2920100000002ac6346bc8cc19e5da28e80b39a6eb834b895e0ba43c525464dc1a3ac4b4253acecb540ae7a4302000000046352536ad272c77452317e2f1872ed1670f116d21724c059027a17ab6a50b52a04e7f10e20279995020000000165ffffffff0300d2510400000000056a6a5152abe32e8b0000000000086551656aababab65542e5200000000000500526351533505359a03ea4ce10100000000000000000000000011618444ef61df8d4216b7067e34509e0dfed6f4230f2fb5fd65ec9813f2324996adc069d0e73a1d938403f3a90ab3cd63b0291fb6b2c6f34cfb90b67607181a6024ecfbfdf0d8e29f0d3fdb72ebb08aa7c004c4c821ccefdcea90538c967fc4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003372d3d97c9e20069c41d779d66c7efcd79656b33d18c84900eaf3875ff4bafe312710ea80caf7700953795131b9b659dc03c7de867aee1055279d9ba37c5b3909f212972267606f08731186ee9ee494625b1ec115d61e1e55080bcbbc1d10d9a492a4627804bfe2ad36b584baf2bfa1f39e2a7eb334107d6a19acb0b75570c0021d91a74893c664bc0d672a56e1a2ea4c0813ad621e5cbce875035ed96d0db5e4022bc23495b65b059da74966ac3d75342f25c7518a52236063fc6b037e04778af10a2a56bb3e87e668d392e37efa9a83415ed0a177803be60ffd114e9027d567a867254f2a0dbdc050450d05a5ccc378c741443b70a425b3a77a0e99223c07874ef003167205fe755fe642ccef9bab8899ece2b846717e6d36eafe59db98b6fe2b24530220c867a105b96fae89ba2cb066412c2d2ffc780fcce26774a696c49624a3e8c202101c2db5c1c5d77fa6b85706d5f34eb8c8c1a1821195f9ef2df3289205e1212702184a6c5a48077ee2ae2a19da751187e5b7d3df7657dafc38308ecdb5ebb7be4d0228d770238ef5516166b650d490f2dfc7400ce6395466084ac387800c0ef09223d58b7b71375bbc600247ea7d54f69babb51097d1e6f58c5b148a680ea909a1a2bda490b2cece95be03cf8eef94b8ed797254984b95ef4572bb8582e87f7a1a7120ce4cf3570fd8347355339cdcd4ca853ba2a1f32884a057bd3b08b8f5b7b16cab9477bd3a44961bc7bceb12d0512202dd9a2e0c4d55851f954c57f53358e935645be5b2d488660c0cb9fc951ca6b1c07ac6d14bb7f7f9e145688f8e77d36613b4def9477cfcfcc8bc718fbb1a8ba3e72dc20c9dac81537d6feef090ddbd6cc51761f26488eaa479396cb5858c34e87bd3fec6b6b4cefb67370777f4b64ca138ce1cbd38862dcdc8b9f78de5cb06c66a1e374d0e9fd24832bf3ecc3b52092cd0dd3508eb26276375746bc6d81424cb3e87f6ab3751027c625760c7d310a3c79ff74431fa1bb77eea49b2bcee5ac487d0933e541227997710cec7315bd84f0347a5a313aad56fb7ee99df996ff345fac3ee38932ef3611f23f4a78f833754c41b6aa4fcef2763684e7f1aba6e08a59ae7e13d6166f3e5566b2efe39d44d46396eaab6c9c3298f8278a90d3b833da249c65742a81319ae308d5f5a344b7557dc91b432b6deb7520f828834380ef8f3aa2acbabc720a179f4780cc3d8784b16fa509711f722912f1177405f10d6daf5737aebefde634200ef7cdcaa7069137404871958ce2c26d1104e09e11a8df290e636e59a81eaa0ebfbbc206185dc2d4786bcc9c8d22e85194f5ec2b8af67c961f393d5575f4ea48e851acc1c2de3d0a99f51d916bf97586a1a1c0962344b4e2814896577b44a05cf5a48111f49bca90f58aa95a788ac7b912b540bd8c36b78297872eda499ba7e6421a4b65b3482aab6c23fa5a708a12e333c7198489863f94c87ce2c7577e6e86f65dbf5753dd1682a29fa577a5376196957a387dbb4b32c27b8fc22043adfdf890353248fa2b391172770c0435ac5ffec7122bc72d1b6de1470fdc3d22f8f1f03c42242c32ddcc4ad32fabeba00faa16cfa049dbf5e97b85c21e02c6dce3c947bb0ebb5675a3e4ecd3ad205b2b3f336fb02703186e185db23ba9c8f25c82588efaef9bd0e22da7598b204ab541718dd8e96b5f8cf5a7e369737a405899ec1f23b43f3e49ed60b21029b213e9bbdb3ec1fc21e982de2d7ddc2ed9052be2345cceedc3b72baebd0d633bf24bc38964ceb5158c018dd86a47fe3dcf92d204b2a0e74c0ede0ac826739543894db7daf14f85d6f2d1e43d231d4eb34c56df2cc75db92345eee5f761531da4b397fa6f35657c1dc4624453b39d817556ea29a229ec6ed9d53b0f18bc3e941b33e6c200e3071b71b9351eb4e3f52b3ed62aa594cd3456b0f4c4eb19e6e29fa1032fd98560374aa91eedaa8ba18805fb649a8d1fa7baaa88c21398686c6b74ebfaa6f4af50e1d08c556402b52fe97d1d8ea7c4f8d96029a47159ef2abe8c18dd4084e78b69a3fe49951055709ef6df6f25d040262b9351f4c4b9adeeaf787d8817739dbacc50fae75983f8d3ece736528a2a0dd606b07274b6b3d618c2b2b25fe8a3452675646ce893350e53f59c48c9db479dbd35427e40d2d21c7964b53b416d842b515aea7a645b338d36f19c818fe91155c7c4aa82ad716d1490276e7b7fe79205fc1e0e273bab7b6a15e7ce8dee4117809aa6193fb66446b54168c54c97e4e3ac8cfdcee4ec60b99f63510c1589e405ae4bc6c18040000000000000000000000006ad3885d958335ad559ef351777bd489300b6bb1856d5f66891eb672aaba71159586ce11499bc208e8ce3d234789b8e4d8fad6e5908df819930b23486010e9db98965acf1c7060dbfcf8e3934f5f116f989c19f219887f7c94101ad1367485cf000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001cc284aa8056b8fadb7ce136e7459768a3345b55b210d17b211ae15bde91954e4d282029649e0b721cd04399ca9738b99dd9061fe3b2d2257680c62c35642fee7e2c10cfc419d1ddd389e9c0d3b5c8debed701ef5268768de9f5c66972901b5a2e19b5dc78c0da103ac61be49941d06d68d5149c39c90d761bee322db932a4eb020188d1fdef5cdaa501ae6dac22cad736ee6799d93c0b49cf16eff285cc987fa6030b70991891e92fda52dc2297151cdaa8afd733626370acfc0be0fbec92d9d7070b21d3ebb5a9b9df9b46ab52fd63df626ab60762a7014b5d734ab3969ad5854d6b1749bbe5f0775f0e3ea2745d554e885288fe5f64fbbaf141ec25fd46c417debd0208b0f791c8f009743700163c7089e9b398e6e9f461094ea59f4f8338eff365930312d054b451a27576b4a571e2b7f1e483f6faec9c3bf20215cc23b00f5b86b34c032651f1965f7d57e96dd34fa34536ac2e10fd77ede917cb038197126db2421ad40224babe2e81ad1acf7e992b7375192edec678306dbb7c22167e2f2ce4862a85500203771cfdb248c6e94371f8f1ab5556497a329ebb64b5d80583db6e9d088599ef439fec7a247fa026343d98905dd2d2da0bf91be1b69690187553e7fb5855abf76f72bbc6ad03fd8ced6c1cbf895fabb935834feaf6507a12da09ee7610553cf611ebe52c1635467e4b5ce9845749be6ef38715b4defd51e0239cb2fb1a8e899c6aeb67dfdf3308d5ac8c281b5d9b6f9ac99be33a5108395b1936b3e8bbb4fd22a1eb0d5835867bbe317a1d0d505a9db4b628885df944401a66b2a459f49c4d0a3c9829a9d24721c9cdd086151e9101d421ea2df1501e10a0587390426665b45bc062bd9b2a58c805570ff70f0eff3e0714570e1a1d0de294c62f3732d3e03783911df4ded95354783843ff3aba7e2bbe2f26542be7d5dc49e1e82643b6fcea8bb701b216fc72fccb9f8c056361027c8768d87c82fedd71ca92da04c7acedf7989965db0e3f323c99d07bb33d78014297ff28cd7cec5b8dff7126bf81ba5cefd62f84ecc1ede853ac531c144722ba48afac8d27cc04fb21233fc3e99bb01bc61586eacbad898c9b94ab4e9e23f71c9deb0733edea3bbbb2df802195b7d254b5aee1311a9a8189054ca17d43e3068042b54776c2e91f7247491cf83735e757c220fa5c3e7bf5e5fa212b3b9366d3562fd49f0b4098aa94d5b7dcf58783a923c6fdf82e0632b268abbb59fb177644b902ad71415f04c6c5c18e0efc040ed86c67e0c4c48f5e385b99078d2b4e4c51d6125bc897cea531e1bad6667c5638850c930fb3681644e9c327cb72500c90ab0b0b8a39725bc38d510db2f324a47dee97611680b8f0ae7e0203ff9b7b07cbbe7611d8f4c46637e440c4bcc7f6764900798187dbf6e41a4c458d59a9a9a2e74a15670a038c7e521a2f681430cec12e502b105839a5e758247a779fc82f55af7c1eac01e2c004d772ac8721b3dd9645173b0c811e0d01acd603e5156adbae2dcd256ff64f68c9720b909fb584dacaa5848017244d5735f4b90e72abba7f0473ed2cdc6e3978f473448f81dd24aceea8711b00e6423fd50a115d42176a105af15f21b53d7d961e771b72af92d7cd6d6645a78c7610411ae1de7557b5f4143258dae8378a02297b74cdfd5dc07bd7072a29aa83e70eaad70dd163344e7ae5faae5ce112eab156d87f5bccd4d1756b5afee4990b8c8dfcbef9d51385effe3cafab89d6b1741e5a2e5977dbe9cda59a251610167fe6919936a035ea15dcb7dbfeea2123057c16fa8087bbe704772818935c0b0576c22e15b90bfcdc2b2c7811e19f36fd1fc9858a44fbdb15a02c67dc96fe41dc27a4fbe321c8d5b02920d845a5c9b86ce257831f88792030360ea32797b0e08f7a8987e0494059baf8c3a4283d3eb6a216875bfd2bed1187c29f6e315106daa6dcfbe253069bafe54906ac0b1f0689e246609203f6742118c47d1b32a88a75cefd2a4f91e34a03c3e7d5e746266638cceef9701486e3205ca0aaaa328000ca2a2a46c17ad2d0cd7e4f8f9d4e5cf1229c3eb3a9a12b0af047aea514b1c358998cf77ca522ed984463bd07cc2c567f28fdd5f4e366070a49f1284abd2041273194dc4d85df8846bd1251d09e4d952e4b80f1e771c14a4d1b143d8d54d8892e55213fe051362c10c6df4e33e575fe61687b1a7e1b79945c78b158ae4cde89acbf18a717bfe12794439a99153911aa251355f4dfabc3990b794ae5a85dc9f3d4d0d71b00a6e476fdafb891775cb5c0545044680f9805dbb33003000000000000000000000000e3ca9a40289ed769796163abff6928b0a12f4d2726b0b5cdd86d25f8334f9f98a6f940fe124d85314744eb6fc6578c301a6039816e8f9cc534ac57cae369e0238b908e204f8dbc6938cf9a09a4b27c583ef056eb2993f152f514fb8f953735a2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003bb25d16aee1b133d64bf2e1d5b67d4570b4b8a770402381595a6736159e00d539c9607c0c6619cb70db4404de68f8513e9fa6ad32daf8c7b38f6ab19297003d27ced7008b0145d306b33749726f6cd3c3c65e476e5c61d74a3f1a6299eedc71f34f72cf6e041612bea4aa9c57e04c4773c67dda819ba0bb5bb9c8304174d2c4031dfb72abb0dbe6ccd17c8343f2a79e23bd72c0fb5f78aff337e757dee0d81f0a02265982e1dd547ea7b6fbf481fc4a8a23ded8fe35d90ec7f22bec3c2b662cd0510a19c77cc2861cdb25e1873af62757acdea7e4bf097c81f0a7394b618f892fc0fd184d0756aded90834238a518d0da52ed569087fc3ba08a77af72722cb0bbb200030eceef5ff08149f1e95c01a24844213baf1a13c44f94034dced8955b47a9b1c4020dd120e001ffccb14386468b86fcf0cd7b154906368ba4f17c6163fb273e30d3032a480a07896b84860adfd80118dab28533cadc85c69dc4ede934735f4a24123303062b56e01fff7f8f52d1560ca2f4b4587a0b22d73bcd30911332ed36ca922b3b030a711eb3b802186f44fb82be5f7836169d0534a1fc16bb6fc1e2018f9a77bcf535c3c31aa070ce38ef3c8bda93db06e38218e4a9127aad33921eff88ba5d1dbc7becc6d414152e2677689130b8c421fd63c3ae3a3f952d034407dfce1d3aa82d5144c201f4388f271a3b39f68429445f24f3e194a3d6b5ea87bdfb26c8603165704f3e9322478cf80ebc1617e5359068b79db2df8218e8ed4a625611f5f84bca3594d270e9e6e125a1def7c6a1afd11f8d984d497a0e560e1548e643441c132d609b6f23346bb88193a9d3621f485af503759319669513b4534728b7a4b0ff4ab2bf7bceddecb04d5fa8edd005b03f6155c242e39076c42a96ef23cdb71488d9cab1009e546d72c7b872540b261ca38702c537a942959af8ed7a89910b610e79778e04f37d12b5a41bc0b0aed5053cd7cfa51fb53ca5a2cb33fa3d01368780654d6bb1601790289a181377906476ddc8e8ba1df1501908213d2b35a85b6a06d6ada0f14f4935f45eea762a5056f9ea9a5dbb9e4d6432e6f2c8fff264718a368d0b40081ac48f36ded848904488a94bb894cc5169688955435f74b121be3eb1b0d6651bd1602e9ce5743b1e754b8e0f986d1897f4fcccda676a8c6da7c3f357f14b6604d234e2f7dc8b6a9ec7850716484d6b5fc9aeb6037240cb3d43de684a57ecd236dba30d260d765e3023b9702c708d4d76c212e29c141b4727755b907ddd647bf5859cfd48a5ab4d493dcc95c78c1e78999df71ef012592b5a20c6bc5eb239a11726d94ebe6b4689b1e45253c8654fd1229df8572f4d1a87c598c8a20e8284150628c603e39104c73b24552151ad373e87f5743ac5db911932956ade74ed97266379bd8a9b265409547154b75e43b7a25cb5255ef1edef597a65c6db5cfb8ed1e3614e007e19d5af49116c56e6cc8d1c590f76226b29761ea72f45e1c04bd27f5df1565ac5d01b0c6d07ab132b6f82f229bb56c7b383af425a70435790114b1862b64c96a81b1d8c419e857152b07b0dcb9bb213bd82c856e4a3965dcff0f57aec4aa5c92cbb31589d948fcbf904da73566d3fbeee9e2c8440e3d53b08230de46d360d5f6d61c2723837fc27ad92b696857a9c0c053924217694375ac78855114845299b2d15397a353ddc1f4af0218f8b915c10666973ca4d2911e250684ba62dbdff12805be9a3aa5058889a81929d2a95140f18494adfdca5cdf55c75b500fcbb5354dc9c9333f3653e69bc173859223522069fd497cf0809a612b5a576b69e3106f3dc1684b54641d3cd191b8e08285df955fe39f8d8d7fd8cfd85b3b8f99b16a700a80d73637e830741cfa5d53a175a587df60eeb4ea70e39e987e1d3c6d15e00d100138d3d0ba5462192ab470404bf656444582c432df21e3c39b9a0e781ec331186929eada8dbc6adf110ac85f7a8584ea1d87be4061f0cf25a05a80a4f05290093dfd87df196969d5a2698a70b1f05be735ff0bbf53a6c5a118ea3113a496496e333ea9f1bfe8f0ef984946e4faca597fd84dd28ac42c702dc2ad3379ca8cdc9aa62f15eb8ca39a1d2fba5f3e5ab4b10435e73cf4f383b3ee3f19ab7cfecf749b75a74608c014c848f27ffec2b9a04ddc5e210c2ac7c3fe2e9010bf6b17534b02705152f3d3cd2e3a22184b1508ea459cc416c86cf0e3fda10e41ab909b8b1f8cdb2fca2c8809191c58d3b2d3e50deaf3bc8d836940eefc1590c16aecefd8cbb51e8e4c96b3ede6140a690e7887a38522f98454d5dd3280c4aac994b8594d4a91fcf712688fe2f44b6a4b2e7350ca96e063d80c48b1d0024946b0e983e93f3581348a280ef56de1bcbbc8a20656468126a0ad8afb46a39ee541191910046d3f2af959f429370e47e7ba8e700", "ac52516565535353", 1, 1829739483, "2ee08477631418580f29aad1dc5a14d564316023ed8b4461979304af4de9d854"], - ["8e87076601061e118f34f9754f608802553bf8e19681aa28294a7c38eee90711b049f784d30100000005ab63ac65acffffffff042744b50500000000015227161f0100000000075352516a6351aceaf903030000000005006a65ac635d2a59020000000002ac5100f188c20300000000000000006e61db0100000000bf9f970054537ab384a68e3a8646aa510b6b09a8907016d59bd4c7ea959bc7ab16ade410439c274c77b5cd1833f7485a30185ec35aa8eb56da38daafc973139487f3b16c73431506be67385dbaeacfc409259e9e30765c3bbeea45feaf91eeb600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc11cc47f347be56c021bef74df2351b60afbed0bc89438372da840ec1a5c4527c794a5afa8df91e394a4fad1b8ba1612772bb6cfc6687102e0f04b0a2311b648167fbe6c027592513ee25bb6d69cd770219a1247d60e14e6ff53555c3ed2e5b87f791e29b24f06230cf345fd7b0278d3a5796a1c84e129abcc8633d857c1e5b031da2a8c40fd2dcbb21ef8833775944d35d7afa52b55945bfb3660302a0b1246d0321d2b1c5f307c8998a2f74c62ddf8cc040fa156191959119ca89f8d476f5a40e0b14089d815b450eebc52ce0e7e843419b09cd3082926304a7f50dda1e56c8739410676ff81fb7c128e7bc5e3f9c41ffce56674f91a385190395ee06ac56b831230305f0b079a037196eed9b6d251fdebcbfa682836d49930483ef1690ac69fc4181030ab67992de9c25b5aefdcd0117d8539dbe5f86185e8bee0a75ac062db43166dd0227dd8791ebc397bb3edfd0d356e2b501aff22041e2be24e7e63b6498f880c6e30325f8d6413fdb36e0d1a4a705fa5fbda20b9b6c4be831a510905ad449983d8925022c41fc54766f053bf2b77262683f44cb0201c087966223ad021f82758fade3f4667b74272610623d2dfe4ad4b2aed242424f59a9a10b634a51edf35feccbf69a5d74c2b6ccfddf93ae49772b1eec045fc998b1d447043f9a127a49863a28021763f899dfab10e8a4503279fec3793c3813eae1407234945cf7b2f094766422cecfe58a2cbc227f2dcecfaadd70c1dc63f20e61576f20cf5bed7ef29d083cfa5db1e2d762edbc1670b7bd81a97968614e355203b95855b63df90aa9669fff12c36eed7ac49bfc83818bebd6da9fed1066fc69a9b865d2d156b399aa15941172755a4a7b6bbf41ec808088274688c0b906bf7003d76198a6163973685c1e7579159dd772b399dbddeea0c1b0515a974bb2872d7f076739af21b6932710a07e7e411d9b23fcd7aff97d37c2be332e79fe4504cfed83c41a30198d4ac4f813bf91001c117aeffaea331d176a869195b8429929dcc798b993e5cd50f09318a6bda78478b2dc89ad1d46a539add7aafa7f630a4e24ca7751a8f3171f29adac75ed29f2eab1997eefbc9c4eb711026e36a4edc1e48aba00c83e8c5a9dc63b5695c00f5f00e1d67880a1cde9dbe63c81bbf1ee3119665b134c34a985b95a5b0367d85e691d58f054e21f08e215713734ed1b2ed466177d77150f84325fb487b73b923138c349157f7233629f426d6dcf5be387b4b93f7c233f056d0f64c5cd3776dea7087ed711a8e5e017f9252af98240294c32bd7d65fe9cab36d43fbb7d9bc07c0520ec4d10cfe4ae2222a3b646f8b62046a9f39cc5cae45259a43e914915f3c0df4f25b8ef7de14096aa7b4d996a841b9dfe0fbdb1c81273136132fc1d042b75bfc06da27de110567f261e4e772785d6b5561966238e269c74a9166d301bb08fe5d74234ebbeed6470ddd8d7e986c225a0ee32baa472b9142031e9d7c6671339e88838c6fc635c83170598b28af2c9aac903ac8d2be2395b30c335d9f5ed0c306d6d4900f56771970c09f867c13e8e275d459fce070ed307387b767bdba011fc2e26437d31037b0352dc30d9880168f287bb54ffadc34aebda28c5a74bd5ac5036edc4b8f3ae0e74886efa03c42b2cb5db6b2967f3abb1475b3eaa5df92d7c6ade2d7ae5f82742545234469aafc49807fc531b937603abdf367954f485c5077ed2707cfcfd7ddbb5676ef30094098f9372eb0fc5cd46eabd070481b2e00f25644c983a6099a5ad13010ba64f833b5bb86dc95de13aa217401968bdcb4af291926b3bf730ce4152a89dad437561f4c107d43df9d937e7afa1102ec4ff908c05805f277e874894a4465fe544d2a318162277c2899e70e9c3f2a6111689ed47f31e608d1e9ec1f470ab67510fec6c7cc25a36a4bbc4abc6cdc0c08e6266178f2240045ed8f9defd5fcc1612237020837ce6d57c2bfbf0408dfb471fa885cf9eb8522dc1039d9da432d52d6746c69c4e8e617e3a20bb9f5938ee96f8d2d1eb4d30e40b31a52711a95090ac5ab525fa1d49b89539479737a8600b052c9e70b86df1adfe297c820cd97903adf2305c2290a64c89f86199cb778a461716e944073b5e4ae52ab4baac9b2dbc1b80688cb4e77d5b08c74234b23c48c9f8b496f1aeb46fe4a003de4a749d1ef7826bd9116916261bc53335ed741234991a3af981a14983f47b8b27dfac5e1de81e0f801167080b24b55d922b9a7cf40efe41871b5cc11d9606577067fd917ceebd916706df622699980b8d3e367c670200000000000000000000000010202dcbcb11fb776ad54cf8622f6781e936aa86be28077889f9a02ac8c2038cf44730c8e5569ca8df25a3d3003aee5c305308e24ba0fd61da513a78b02ed2369f3a2e624f2fec3b69dbc6231fe156eba920311e94a88ac870351415540d8d0800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c5c13e4e7ad49b9b785a7727de80b6bd2c881bf88d102ab5c1863b46ed0c9e147e3afa7b6c5df06a234423896b4e8a73eef1500ce8e6ab2bc89dd181ce466d0479c8959e6c418034eafa67c5f93aeac910c7c8e7ed1a76a1025478ea23acbcd014ac6f9af8974939dea3a794d2bc0653f0f7a76b363b1161940a02f293483b7a022faf05cdff5aead9963b49bb853e7d5a955a8ccbc9dbc19b8a03a3b4392b0514032375d4ac5cffa322607e5445334a522529a8229422450c9316bfa338a81ac3580b139b717891c794014aa71e8b670b3dec0c213e222c2075e9f7d04df406493d58070bfa6fe5265df78f16f75c101db1875efee5e59e4e92d676cfb7cef96614a802173299ee9ddd8e3eeb52a6d240b4d4745e35bef9ce2b33b8deafc3417c4068790205fdfe2cd6726ca1d6bb06907f1810bf729da91d2b9d78cad329a5f148e3c3620327949b823f739b3cc5956e96170383a692db6571e7dea56f76ecdb8820421c42032eb3fd96cc4efabdd6dbed817c3490bd1237d1859d84a83b3551e840b13fdc95031e996d1be6024f3a70d1a7ae9f797a0052edacfcbceef5694c1158c48a4902b3dad0270b6425243ec332df96c068eee526a841b304de512e74196e09d80586848767eac4c416174e8438bc0282d25baa8a086ff578912bf355eeeb31c57d123eb204314138e7e691d8b0cd1b5faa9f6f499058e562fc230e1244d0fd7c85c10e3ef3f4312f55c3055fd2f6145777cbf9bc25dd3b3d63f47ae5fa2cae42f59330de29d7fbbd69421ca96f90336a2a7afe086cd43349b146efef77dd6fe1fef86bca21c425af21c025d11338a94fbfbd3a3ba70243cd4903b072372e1374f3c7d8160e3089aaeec9b7a82af5f38c650801e37ae3e1292232225f5006670853e058b613dbda6ef9d3d6b4c39413b88f619a90fecaae6ddb1d54b7244e4d17bc3ba84fc1acffceca21ee4ffdad1f55796e389e3a34db63f619c2544e7c00acdf8938a5da2ae951b5ae9bee1d5bbe32492ee4cc39cdd0a69704321cb8e0b6a611dbd5d2d0214e10881d96812fd23715cba243401abb67c38df3a0399c2dc2b88d11615985f2093bb0aee13b553bca3bb0d663843301609f12a3db7eeb0c91b1068c60d666b6c78beebbff544099777437c7fe33b3295a4c902d708ed435528136b5fee6dd50e8d6f12555f11b71fcb4b80df2bd7e038dce588de5d78555c9b932f4ff80db94d95fcceee9d12c6692ed4f44bc89385be54d1db7aba1931ed774073de0e23e0767a6d59debe34f6b05185aa9f2fc5830ebed467b7a09e983ac54700f49a77ab74c0ff3fb0ce3f727ebf634d38f9efd86b30943ddb0713bb5e59c10f62a4797e850c49117dfa3457e574836676f13ac1d6b8a755bd75dc89025683e18d025c489f38bef5302eeff3637b8041c1c9a61ce34eb0d03ecef048d17b74d1073d4938233c836aacf230137354b28595f555a46bb08b377c7489d904f6d3a9efcac71339fabcb5ba995600388cc3abed4a137fbff5a8903ef1ef7f92aa8f39ed79d8711394782cf0357e8dba3d1681823a5ef158e1ce10a148ba69f870136888069999a337080e5c2fcc1135dd8fcbd397c727f5b4b80db189523bb524c107914a2e933f9131ee259b5f96a940a4f721415ee86f2b3b7fad148de2f6d2d6334c3bea29f84507ac417246aa8150d844724647b52881db456ffeb72c4012f748ea032448150c0e73380ee1783e1dce84c52eba30918ad21cccf3c954fbf94415d622feaef1d8d7a19f38afcfbfdfb0c8b61eb80df8d8edb38dd89e536c27451d03dbc9feeaa11c2ecbbc9f3e4a5a7075d39c18a0d05e3f145dfaf30ae2bb5020e35e0d99c25a29072993e981f9d34bf26c137eb75d6a783268431b5357f31027ae916e1636edb273195e3a2ef10c8b3f0ce816f1a55e9b3307811b63d0bfcc2972552a4ad19cb81deaaf389c1220ae4077fb8c06a1d69137999738d197d8834d5b395022a4c0dfe1ae90a927dd0c6c8c591ce439897a97e3743c854f887784e0d70f298cb78724d6b62a81fa6a67825635b586a1c21c96d220009b5de6b022c6efb625ebb72c8f055b96a3b89385abcdb610685ee337be00eae0785a934703b49da75987b299987e4474c503cbae49e9455ac4544745a2da8328f27209bf62009dfaf846d1173c603ec82a06974c18c7b97f65d50323a8b4f521af278b9665ae18bdf68b92063d19408e8e66f3fed37af8f2a6ba0cfd91c276ed9adec7b919d191ea0ed26922a4cc6560c9cd758677e4fd53c3727964f010000000000000000000000005e3f40355546a6667fe6e3a791df6ece1abfa60619fdc10d5b54266e98a4e5ff8d4f3f791c78023d18e021c85d6fe889096b44be369272fd3d35c497a3754cf5bbd6cfbc9417b33643786c1d914ad10bc384a108754c475346eacd5ec68c58af000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e4009c6a5c1b51bc48ad853bbecbd9cbfda060be8d27270c06b6aaf9f0d8e99bd69d731dcb0b17e517e3e18dde40a503d810defbb9ea798852067557dc5511c69c30ce3830151533d63540c03c2d8530b7079b5f3f4edc79477a7158a103e624b280966ec73ee655d655f76b379dd57f1ff081afa6f076abfe102985a6877f702134a883c3b12615de6b1b27b1b39a500b30869684b749615da9a21fcdf282ea50328f5586cd19cb118d786368d16b2e080c2627ed425b63c5fbfcf27419c94e7d30a1b8131513b8befd61f992871cb27a0acf9cedbfbcaafb346c4e30db80178a7de1565286e5e41fb1285569b9356eb830aa44711cada2a64852e1f9b2b7c0872d1032a0e4dfa742350ae60b222e1bb27cec5d63da3f428a4210cf515505907ca9d6c02274c9e96b4b843545003e2ac77d5671c305a8cb4947a29cb351582ca46363776033036d3a5173f568400ae6ea10da27c39ed7d6599fddc5fec7333db4c8de75fd70229076c77be3fe0008d303f371014a8622db6242797ef846be29c2d252edba12b02000498b29103d5c8709894195b64d840feba65f08f557cc343bed8d5891e6cad3f7beb8b208b56d6a7b082c79635b914e861fcdad2f5947f6c41ee328ddeea662a8b5dd48b5f750dc18958e12196deaca6f3d17d7254bea919880b6cc8b3ef02b12718b018803df747e254828427f341a4ddb2473c89f5b2a040ab3dc4d09e57f41d850884799c6b50d1a13c67acaca194c298bc2790873b4e8064ee50a6a636479bb9b01591b0bf4bcdb5b72204f221c687365b93043ee3b282de41a0e544f3cb750929560c0d8edb221520b9cc778e6830ae8997d4f9bf379516a343b5b9df0aa8edd1e2651890ff1022c82440739d8ab845fef08130fbe5b7dd6645483f3946de8f02af67dd2584399b7525b6f9fc07dff717b094d1147b99f40d9b11813bb0dacfeeff588aefa91e2d71337c046c2c5d5f96b7c90622144541e1034e93218ccc65d75c643359ad7797d28e14e58c93384635a4a870b6b7226bd5d5f35a11a1573d649e72fff9957496b4f08addbc7ce8b339f002e4641f5ab13809e9af37f97312c27d7ba932cf4a1f0a6ee7ae9c1de49929ca2b590dccbbb6cbdd8a558d7ef5fb7e330076ceeb33938858a18eccbd9b1fb8b7abadd6d70f163cb632e75fe927112c0c991a49e48a6908479f8c8ffa80ff7261e2e57c82acc99e6e77f27346e0e0d56127132ea63a28c76c1cc9048ffe44389aef8e01f6e768eb5fcff35d237dde8cd999c05c4ae1797c1e483ce32586ae29acf5ea7694d557114b386c89d4a9e98b4c72f6dec91ea20520717ef4d817349747a4c183544c0cf3a01c75138a7d78ad4ced38dd12c0485b080cc87c2e869e52ca44c0a3d429c8dde924f8d617175b903592321b9ae2e971f23e6b610f8ef6f7be9f8e992fd16eb8171a6016d2d42b36e8b94c2951791a48595cf767dc4281bd3042516b3d5f2f3a68918a202ef8b541f8f445e1f6e99e944b4cd0e967ef46e38a852a517ef96ad0ed503c6504b6a36006765f79498c4dba83f9f6243e59e473f8c08a2c9f8b84ee6a507e904975282f5e84a907b456bba8b3e9444eafb5d36450c8a9d9af9a723c797e2d6436f8e5b1d7ca2c4285cff988857f51a6d7208f5bcded8f69dd7bbcf8372db2ba9928bb51de3573ee94a1f5156b5125fca4b3d29ba0ed33cb9d61dae1218b564ac8dba4d7723c4d03ebcae0ece8efc263ecc474201bb4fdda6137d56818e147785658474931fd1fe0ff92b472774b8d29e8f969690834d1d15beb0da7ba8d273f77540de435d3b742a03407a24150a1410418b6cf09d44425fe44b649bff700d11ec0f10b88c00253a7f4f2c8940be33de1aa0fbfb3b5a9d523f6ac6dba86fae6ea666bdc5bf2e316b5736e1aae9226abf9f370c76e71d24c1294d40736eae8f632423b89220df2d8a3c5d1003cba39e7041b1434852725433d0421347a4edb57261f47826d061bf27e587b74bd5b9d954de83d11878c19211334e7c386481aa4b54f9517a1ffd9383491f4a8086d4dd96ac34734826a4bba6f9f570b0b5acfb944bf581d322e56596f65745aa5349a2f66d6e41f7f1f9ec0ec0fe091a9df01096535a59b83934d7c3d219a13b9eb9ccdbd5030894c98853dbdcc64cfc93bfc127dd64d0a5ade440bb03f452e50f3bb9b397b1a6e4befbbed064ef4b5e99dc80916bf224ba27b44c8207c46e23bc2ee9d2d5d9009f8ba414c47d5cee35cf66b93ee3ee4f1ed14bf274fbdda97812cd3fc9fbecaf5bd0bab84ebb59122841724502bee35b3a3ca2e16ab6a8a0b72d19f62ece15a0334493b3701866b0fc90fa1a633b0d203fa348a169a3a7775324b0d5a31c9b5736b71cf721374c5098d86f4b312c4793a76cc54efc18ac49cdae3f200f80f", "53", 0, 317509884, "f09edbf798b59e2fbef80c480e999d4c7596679f7714abfcbfad6f83480fb53c"], - ["34a6bf8502d0b9b25b5379453756e1825b16d88938cfe275219a1e72f3b57193e0eb1a398e01000000045252abac3dfcd3b3f1bf14a8e5956a505f7e2842f2b59bed0ca086d5c0b2e6a53981d2672615b8d7030000000165983db1c9023e83f10000000000066500abab6553446bf4030000000004ac6a636300000000", "5351525252630053ac", 0, 2015930870, "11e9ca597f29de337124f5a11b2d4d4796323e99ef94acfd444bd13afbc390cc"], - ["be3ba9e202ad33ed4e4e4930177e5cece431c5da3d87f262f95b9bfe84adacbd6db232c5ad0000000008ac5100516a6a0000ffffffff981887a4634abaaff6af2b9f9130b94d6c0de9cb910bdaacb16e9efeba300c0a00000000046a525253ea0a53c103e01bee00000000000253652c400c00000000000453516a00e4f3e80400000000066a005351acab24cfd602", "656a", 1, 319576615, "16a5733a4a65b251b7c2c3781777047667e8d0aa8552a85d44f053aa9b191714"], - ["86214a3802ed24ed7a512e882469eba8449aa21ea22f1d63e738827fca334eb7c93c1a4abb030000000553ab6a6a65d3e196a867fe92c099bd3de01735efdb1eb69b94a45a90d15deb7fb09fcfe14ec02456ee030000000251515de197af03cf0b940400000000007b257b01000000000200000463f6020000000004635252002a653f3202864dee04000000000000000000000000c511532eed3a6c91f164f283bed3c0f1eb1eefa0c816619ba87b33916fc79cf33795d92939d4d78bd0eadc43a46c193fe5e72527bb8231211806171c6af6b7b30ce9a320fad2a8ada64816aa14013d6e2613162ea2e8932b10d83e1f4969d97f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000807b59db0f8f4696935dfb6ca0136a60a22daa61ebc1b404dff1ab44977ff7135ebab42c1bdf4bf24ce706e8371ee7215b985f188a436c5b7167ab62aabb720b9498364b734015eba0ac7bcc033aaa1bb0032be20d6fd26c3d7cb2092311e72514874be82758db0eee94dfd0ae7dd7841436b8bd98db8919472006dc55a9b1bb0220e12b3beaf93ab79ac65e71e820a9490346a27800b75203ce82812e795668950323e9ab691c5bd954d07cac2d465b66538eaa767504a437c4c5a69e800a3e16e10b13bdcdf5a726aa7d280c4f92596f9e1f878a8b081fc52d845442d117025840580339424ccc67bbdd6365d7d6cd105774fbcdb6f201ea6a53316d088653ccdb6103164aa2592a3c241fdff966fc8ef27d828a4a6e33cf02e3eb546b99cf56ce796f022daf88bf66cbbbf5548d413cf73f88c61b7119ffbf749091a979b1bd21ef8afd021fa3ce54dc70ef5306b9ea04a6c353e489fade09a88dd491464e42e605141cd4022cc27637589a3301250deb6f5e1f1cf103edf1417f00cd9ec985e792999cd4680328e036368c692f7516ffadd69d4ef8692ef8ea6c5aa54ae6beebfb4d0e84fe444765204bea445cbc4ad32d243b927a6b69bd9b4b8aa348ca1b243ab6f0fd0a9479ae6c9a368bcb475c0efa606bf48079d1a63d5417719688154965d9c5416e703899c6abcaa45405a0152e7ca54a4579db0bc1f92624b245658c901e5c9ad490d64243b60925e3bb77fca8c9336ad7d1799899834c0cba207dab69947c6a74d60b24c0384f1843c18a7cae766bcaa7a0444e5388c0d342dc6cb6614a7321f4df18d0c1003dbdb613389b9d5f26df919b35a06aa7766651cccc93960057dbfb857b974682c33121ad392e1c4eb8a8537d8ea4bbf7c3e8d8a051c523cc1b7f123a4c48192b1ea0c3231200fb747d32d2c38e6b1d693b7effe704d74aa85f8d85e479656a146618a8294c23ae45364420cb957edee1fc7e31e1de775d2f34d02901ce5f6b763decf59039f0e945ea6ef549e818a80c5121d1d6a48cb14734b7230cbd93a82621fa84bc3e935b8fed73fe2c830f469fc2610da7b33987612cb148314c70d51e54e9a024cddf040a041f6b646475a7b1051a913e3cfa097c7ffa5fa034b22432de26f3bf9efd206f267c9835eb67af43d99408a263ed01a74f900ce756a891fc1d9b7e168deb74e97a92e5d171fa82d1a3099cf985ac9969e8703fd02d0dd96fd9e07ad6b3c538335aa3f3e9f055b7e6c74433d1403f11fd4167d3516c3eec6b14c9ccac3abd0de136114b8a1f8316ae86b609e849cce4cc0da2c482b564dea471b780dd4c6388b520b199fe50b5c2099ead88c2361b7eee4b02c040b722f7c235c027186f326255c2d5c55b29f9214c80ea61d65ef111ee974c9888664211658ba022bdfb4fa8e6d3e87af9a2269e1b41ca6a7da144562169508291ec1c3461b54b0945f03399820e144ce2520eeb1ed8be9d20f25bbfdf14c479a8a6636b91327a56e565b60916afd24c86e52a2da3b52140655a2fc660a94eabd19b1a32b547850d0a3ffdfdcdead7db2b9649fa43f596f8ef6091bd1fecf36da4e63f9f0557b042d5ee179317b50ccac7a9bd50221fb7fa64b99b916f0fc21eb53d37bfa1e6229c257de8b9164aef6f464296726cfda3666b85a4aeb64292f9591a729be7d1c26b3aa85ba6de5056498fe3a8c65e2b7232c850edf75c7dd9ce850719db2ab18585c1660a70893fa1fe6c226f35415628c5f259fd3a969fc6480051b16578cc7962c1c27e47e4e275787130ec1e3cf31114717f7e1dee7c086bdc5d1d954e19f7f6953f5ca44ecfc66bf537acadcd6e672e79a07c920328c01a807bc2276bf64ee851192928d9c3315d261306ed714e91d0d1d0ba8467253d7f414f8ce73fd1564521d07d27aba4c7981c755965c9b979d533e458d5dc08f612a627d3337a02b942e1cba8cc2f8a790054bf3d51e5c2b784c9939481e6746ed0f2c12821780b244693987c8355a02ddc5978b923f9b29ed33b99948695e345894e4f297598ca23f749737b28356751150376b71d5ac5d757160a1658f4899e910dff9c9b22c262a36263e2e713ecd5ba0eada5d228db79b959ea7c34bcf3a8d247a599a64cb6f08c7c19bddada376cb47692665f2f6810094b0d96e2c1eb3ea87e2d81ae5ad3f3d5dcda0a20150c1d2cae24187fb7a9d3d2ff1c24a701b86684114a099360643022288b1a5ed8b032314eba43fbfcd9567d72259468b03551f7963c5a29ade0b85f1fd9338b04f2b71504e896d287ae0100000000000000000000000053d1acc9dd20a0d6fac61e0726bde5e949c482913b9d88ba1f56065b759b48ea99b31a88fcc3d973adaa64987ef7c4eeb021063223a65674c79c288dff7b7c943d1f1141963ae92704d98de36a3f63376af6fb713bd12bf6b24df3fb87c53c2b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005eda13d5cc3814b03eb7c1baf0b365651474d6abf23751c5503db9c2f44f8db36103e5f73e47c3dc138c3d046aaf2752697408e52ebe05b64897eef1a94519353c85d4a0ca9ff1c45bf2cebb1261cac4bbcea73efd543c57a9ae1b7e280830382e216773e02a17f23efd177b5c95ae72fd7aab02008a0c63066a99b2776d14d80318578be6d89e3313f2549c7876185ff1f1504e8091d93659359054163a7e754e02085cb19fa3695d956fd8312baaf187eccef6c29fced77947170521bd5c78046d0b1c6bc11c818274168c036e771d574a8d9bc66f4f6a78d193d54a70084958aa0a1367d97afcb2830c726345bbf1136c0ef803994d03563acafaf6da7871b0d00002261c58d27d22c766fed761411a77c4ed87d4fa2dbfd7601caef16dc0a061f27e031ea51ff40e06e319d312b3ad7cf5825bd82bbb6fb4aa34c5f1be95b68669986b02147f6b7006a790876430155e6cfd6e09a5773366fa82016a10219ff4ac5534d402190136bbe547bdba3852b4250ef3f35fb6ecf476ced9e64118958c2a27baff65022be98ada6370f32ad59146de40680d6f33c8dd64eccb1d57b2b8d90575667d7e0d5c31cc555f5e5119a8bd8fe00ade53bcbc68b5d1bc86bd29fe8aa47c14beceb5bf807b19cad55a8f0cd42f76fed8403030224c857b0581a8046f3eb787d955dbde82939c7f93384db2c524a40bd96b52bb0ca9fac4607faa05cd72e2c7d6bd1b0ea2cab15091c6da5f3bfb768859b4c424e02612c6d39e84e5242e9dc6ccdf0a9aea367c4a2991c17db0a27d279aa93aee6e3ee194c532b1e89c13ebbe22ac590e5f38d290ec2c3a1a6cda7f9e21bc94e53c2fcbb53d9dbca535590202c8516da69cd715b0913719a1cd2ea00f50d3ebf0ebce5e51710e4a299dbc3f347182d2cc979d75e93c54b2f97f7dfe41ce71c2bb665e3d7426eb44eaa6db9edbbfd192a226b7239051d1ab44df254424a788a8f6e0646dbdd9a7a1074e814a67e867b845d02293f92aa6e3e421433c8832389ce3d431c73e177be6a87ab830f73e4716adb907227327d8ea533643c486b7ec828e7496cf88c0b896ff5033c718ce2a483148f3494d2747e9e546da67cb021c4648c67ab605368a1b34c2200f5d9c1f9e55db319e9d510a6154b3c2620a568a3b17821323d93a772d24baf72243168f6f9c2d76e79824d1293a62d9e791360d8860c4ee3c317ffeeb00f9731ceb483c3a3550786cfb8ba87582c375bba5c3d3e401225955d30d7729e7aec1057d4b34157339201fb27663d6df7b16f916ce560c5cc4e928ad96dd8a7a9d3f5cdbac35e42b9b8905fd7d5ee7a4144bb3e3f61ce3428f3fca90da2bfbd4308a6a93d000a6dce99906089a5ec7e04eb62d662c651bdf2d94213a272386abdcfb1601f15319564c9d537c507e2503980bc92940da0b8cea91e06ed92625c7e6aba94f9668aed6217a4afe4091f1f3d9f555c1cc094d838768e29cf4c9b2c2b6b0f2c27fac1ec00a6a70cffa445d8061f89d584df9d972562b879d90fb9abe93843f3e4a57273caedba2cffc8d1ad7a4b6a1dcbc6889054e21a19a32de6b7e97c2bd339fe2582af3a6ce1b079710728d034ed7a97d6c71ab118e37c2d3d2cef4ae673b52db073894cf1785dab370dc9ec22632018126d48ce33dd73762060361871497cc15ccfa5cb15f1f31cae2d5e4b0d895c9a1829a43e9ec1853740461e9da095fb598dbc922da03a4ae4663934611f428361ebcac794f03648fb90e34bbfa74707a22f17f463d0bc97afbac55ac5179caec4e44820d5893a462c17f9945e9d4ddaa7dca1ab2944be65d620e9e8852a374cade035a0f998b5120dbce1545e08278141778e070d3da328fe1f7678d8373d3bc9b91fec1d30a1cb1088ee375238dbe5c5eaaaba361818b612de9252b53e336ea37cf484106424c9e0b172d3f7562487bd3cb8bb97b662ddbb74437f3505a79f5b0838b7fb4b082589057e6a07e630c53efb609e300ea78820e8c021339c72602062da151f07561faf3efcf54de2693a2bb542f07f78658c0650e38854964d6c51af88f336b54c52f63d91ab3fab705428d20a882bfbea7b25dee6828f69d20a31b60511b8f25defa2a6a25a578c033c0d0321e5bdf86d9a873519002e9aa8db8a2ba126e03f973d099220ce8ed3ec2ebefbeade39fd591af8a049a8fcf5085db4cd21d00693d4084a297a95530de02f6089ce1f929b95067129baac467785f349317a706727063301375548b8a85ce36490c6b69e1d84d57a0241e4ab11702dcf39a714f8e3e7b3e1f9c6253a3c88951f1357f8285b9fcd4a71af7c74c5d4df8f6a51ef879fd15a3043b13c28ec580c31203be44273dc156901b695161725ebf8ff8ac9a2bc3a4aea496df21f065091e35f14582ffb975fd9c6635828d45b139d62f07", "525152535251525252", 1, 874355774, "c194cefbc952cc19d73bb72fdcc2616eee20409955737998e5a6be4218ab4b42"], - ["4b1779130379773e90a7bcedaf6166731c2dc1f7e802fab53ef2b65ee2ee01032281b8d7700000000009ac51ab6a0065635300e9e7dc924a2cdf4685fc9b80c5d86b3692e5431877af828f969b1832610b7b48ab640839020000000265acfe422382ac0e0b607fffe6ad09c178d09f4eee441360b89c476556fe721babb8b0c37aa30100000007ac6551ac5251abc3d4b4f70286cc1a040000000005ab525152539511ea00000000000465ab6aac0000000003ccd31705000000000000000000000000d9487ff77afe55fee6c556c1e9d472c01a149f04eb71de893ead88bc752757e90b11251e28ad76fa8df9870d60f9015f5a8a5df8d96a8d92f2d9369cd5b8171ad1b5436b22f081ecd5d8481446f7f26009fc845e326bab8808b6243c9ec11c1d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a428780351be73ed3050765bd5a6b0bfe46ffe57f2c9f82e39ef0d844901dfb8055321878ea2d6a7eab9cc413747aa006026f9b992198c8eed012a2f60d6176487ed271caa2e85b3d636e2c0f7180df01ab818e9b6895fd9e66857cea65fa77b1b1db2160bde856ba58dcc052277cda303d2d33f69a831b256769294f9704c8b032aacaf7ec4e214cf15d4c4f208da1f88f415e5a8866972e784d78130c7c6622d031478f7ba2c86cd74c16d5a8fddd6ee1c9b6ee618740112a1ad5aa63bffc23cfa0b0eecdd268cb559db0429a8e639f390b5c17ad0de365b855e169ae0a249594b6d0478cc2c6801ad97525256ea6b3a5344f4fd707a398e7edd6d0aaadc6839d397031a748585f414320cbd09972ed0fff8bf65e0356396682a1161d2174863e30c0f031fc57333ecd583d41309c4e21a414ad348e7295b9924fb322b5a6d14eeb830d20301c35527e81094ae9bcd4ae566a04e2fcffbe650b148e59c7b0cd092ed17aee7020c5cfe7f044178eb00cae6936143c05325d79b888d39e9599f2f2641ca8eed2802265c6c011cbaab15aafc0d13ecba67d7558379caa745d131b3959777b82a8df30c8aa798f2ba97cf5597d97e6f0251c1959284b58d157d0f4c56054cc6163759eeeee0628170c3ee95face0c794b5879246d071b06898124f00a280973fae574b6b9a19ffbc4f6546f92a1d63915955a299b523d45543e21317101979d25aaf1a4ce85f766686f7f7a0d830fbb85e0a7cb4183c673eae2b5ebc36a64e7bdae22f7810638fabc7dd42311b397d6ec8eaf08f1696dc39fab22f0f39302593221fe88802dc32fc379214538eebff1f3f5a2ae8bc4368af8facd91ec1154a82322d6b9c9bd24c24d54a39b493b8a2432d82fdf193533a80d41bdb185ad7e1734ea6419187706af5fab470fbd0c80af29bfc667aaaa5ad8928a28075771f4ce1474ce2dccbcf2866d134a3bb8ce63cc240ab13042b90a52fed31d2b17ce16d830f465293122945c22edeadf341b7e7ba3d3147bef7d110ec23e6332ce38f25f35d27ae208db400c5973ca10fe5b209a651eaead5efc82268676e88fba757cf357ec1e0825151b2e4dba49486f3124d871d27165077b6771f7e585a99910b559780e7cde8f83eed55b5c4f00dd03b4f8f6f730f719d714ed0d85acf6a7f1e3a37c194ca4bb2d88a22003fd90de7e84be7545fb66b17fd300415fdefa9128181b1b89d5c40efc817202d862e3c94e6683fe1f496e67fb9185c14ff7e15ddac69401099ce985c81752b4b61f93c3ae4c340030d7ea783ef8ac7e6dfea111d696474674b474c1d5457dd245e5ff778c8b6afd1bd460f914c5b52e1b61c980c8a04924082595edb7d701d0d1f42e0aa3951032d241b3296e0a54ac548d2a9a9680ed1c1f983f24e0283a1d8d0afe8949a35df08e90bee507dcd46ef9eb6403b0b2b5d7d5ed34fc217d220464804e7f9ef17107cca9ee0626f858c266589a6ae959d6f35d691e1309361438b5e816dad576d14eaf7d01526101b96ce549f3a514f191a89fa9b0c187589420e4239e9aee2d7dee1bf743c9475a8aedaa2d1fa386341697b5344bd4fab30da2fd907884e0f165a8cce0ad057ec692aaa882a50249266278d67723a4fdeb1f044aec6c1036b5f09e865529fe218074999ce2e6b6e5ff485cfad18dbaa3de2b298a2d9a34a7bb82af8d65236643c5a618d25735a5256c0797f83f104b2b4210eebfca239b164c4244ffa9e35bf619a6a0ee65416d3005cdf27b401902cc80df3ee8edacacb045f3371cc67b14bf66bcd815c373cc0ece2edb5ee9727ab0f6f2bb6ec100c4fdc25e3369a1f250fcc5621aa06397174f3e42209fe138c9a00d4d75a6a567891adc81a44afae2843bb383b9698cbf42fb1cd63d3b87781af773b4c778d0f5ed33628e79a9b12c972125cbd3b28eb899df7ae356ed62ea39f684ac826eff9f5caddc45707381ea25a705a012fce56df09f13816c9c7a34e1a53b559af271d071ca377b236f385144bfac6364fc8bf764ca47a4a39cce788029c7bd05b9ef40d9ea303e673005aab3c5b40148ad318a3ee1a9c230c386b86fdbe6d0ebc8eda18d4ef266c4476338f2121c128a59b4c8e9a745d76d44ab6e1db64b8c6f8c90296a2233e41017e87ef069157c4f798e65c56f1819de3b1c51d48d381667d0ad275319702e0f163da86e81dacdee69ee4c8119cdb7c475e4a59e3812d741a4aaf4da742a1a6ccd7d6e091c1385516a3c8df50a8b859739b2ccef872fe4b8802a9e425db8f64f327c28e5a60c2b01000000000000000000000000ce5f74589e08caab399398c755ea75549257b9a7cebb5a733f79a12c8a0c2bf4570740b22b7a49ef59b7c3d7e0e2643df77353f9493e53aece306cf72848626a9c0fc91fae0b2ba596c75256c96fb5450777c98e043fa9453df85d4c4af42d1800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638cfbd2fc165bd2fb60148d24de56a6571d5ff0a52fd7ae813cdbbed07cba7d7733efb13067367b21b04af5bcb531c5e70f75b1cb7f6a753f9dfb05fef76ec0645596bda5cd0f3efeaa10f041603d40abb2c58a174a84fd82822ec0854e59bc381f5984b9a3899b6662db8b7cb062b9e750497bcf171f741e317dad0f6811f6021dde911cb1df4c62f5906f3a4d609f9d8bb795d6e534cd799620eedbedca98cc022d1404211e5f8d066500c7bf7e41061122e11878bfcd0effce5b2fdeb683ea010b236564cdf7f3004ce7fc8e40c6f54ac3f99756856e8fda69f652cc9cf46d110c1b4361803c964f9d58946fbc2913bc4c30804efdd655549fcacc40cf9d9958ff0227dffb689854ceafcab78ee7e486f3ce3bb6d0578821eb82cb1ea3e87117d67c020b901b8ca404a89e51e29ee0536cb51432bb80d43f46f8917a303491f37f3b72031c7287b239862351b5dcaf178c8eaba4c15a247ba17437eac4a7f7cc38d1d405030f8d66dfa75642567e8526dff5ef7720ad9f52e38d9ba2e57e25e3b64036f82b0205831e8d87bc8b1346b5d2070c251ecc81b0803629202131bedc9e648032573243e675b5b0853c1529fcaf71c05f752146ba35f659592bf18acdb2ea19ac9df27bc940201fdde229eeb6eefa051ba61a4a7fc7021a822b5859bded09c44acafac7895a4ed39688f5188fcc68521aeb53a94a71b6fcbdc509e565fa02bd0f6e5cdea177241dcef4f36428020380e0a5563e746ff070fbba992068fbec15d3294cad73ee77f0108af39d377e26bdf1ef0e08db8f14bd2d1a64811a47a013323bf75fb9ae78b40981960392fca5d10b6666e3d878ab439f5090ed239ac426032912d975127f5fb2fcc7e315f677ba125e2257d586108fbc61703543ea9d832f97b65de4de4f680b5f0407819ae90abcb2a2b28e16f83fc6dd8be37438391796d89c79f289ea1006c6e986b3d8af2896941864ae32220862b841df8e2bfcbea24418632e93ad7a54a4b5e128447e6d7e9eee81631f2a1258de3a1c18594ead1163ccf4058c086e4fa2e6cfb27bd3a9611514e93282747299c0c5333d0af8cbc136ca3a7802680a1c0fc662db07b0c03fee8233d20c67af2ae88795d75a16a09593bfefe58c1b198999fbb099ccc81a3b9b7704ef53710260447f6876089d7fdf0b1cb2e84e52fd57c9398d5e127286b6091ab2a47540b1ecee5a9099a629b3d320be43b202a579da775a0f8c47d65307f63193f76952d55e7ebe1aceb8510548ad9cd79db3c50b292f9576bfa5ed392fb2ceada2fda6b7d8528bdba12ca7362f1bd59a0b05e480553677aa2ef5e7dde4ee04d4dfe071e031d499746e30b9fabf37923f96a2977840c26a26eba7bf4dc11f88a8b02911aba8fee0313fab261525a486582bb74ef926a58bc39136c6807c7065c63c97e8a1127b9e552cf777da7986cc3baf5c6abfa4aa3526309bf58063ee14988ea5d910181382cca2962e4064dfa134c0cae378eaad8d10b60db8bcb4e3da957e558aad35845d033386a2586425a278b0599ad07a276e9bef7259392023f01168ab1d9d7519d0797dfb50887b869fb0011a39cefa3d401d73d8a6c0695a24680be302c2abd5e89236dfd1dc80a9fc01a040aeb78b22984a012a9015ad36cc1e201b6950fbcf369374d14416152d5d62972107cde474f8d8dd980ae1e5bc1a68a46b18d4a879f168e61810bc97e5ebe5ae4763525ab4a1612d73e7ad98a6d8db29b56c8b1e2f6c1f722739cd459afe73aa7a7576b76530d7b75f907b8e848a702c33c6b563ac191bb41538ec3f05250b0e37e270e02431184e4041c8004152d6a14bebc83e55364e1a634c4407e2c6893e51b6b5dfbd8c7bc9aa09d8ca7105c46b03f809dbc5f47b93f07b7da8c6f2fd6a1c3452bc04f664ca34edfe3bf4450dbe3db82626686171a606f81432ef81085fc4ac1a03ca0fd502f9e986973f0d159e4c0fd5216b516f5987fadb51da2c25c68c60c11a152b31019825f9e65252a117b45abd5ed3d93d826d7f54125d2ab07867f69c2854e3a32124ddee15bc5306b1f6c243b681cb37d8c999e41aa0e5da59d27a199d1a2daafa62c4d9b511e669bee748c6f6647692bbd475291719dc3f03285ba4bc65b4888d8c841dc5289ea39bd126f8a78f7efaa430e5776b73efa28c785eae2a650a99148c158dacb42fc46e379133fc4eee73df9a906446941e64dd861105e410486b35e2732b3938c1d1ff8f831b4e751d8e4034b7c4214a310c6bd2182efe18beb2705332ce1c96574da40832b604000000000000000000000000cd77635e675e51776ce6975519748096d64d553a4dd6a2eec0b2531b59b2a42e897ccde04954046c7bb7d51dd001e388d9883d467270d132123a8f439ff9f6bb7c922cfac4563677844fa25814e75eda15205bdfa39eba89ce75274a54d49fca0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042e9defbd88613c3612a30af34d8efc6b3b9086746da3917ce850fa1bfc50e47edcc2087dc5c152a2d5140108dbc5014e8350becd81799afc7961155aa76ea43324a66316dbcce7136b4ce9fef2df97ea5771f4647b2b447fa0f290cd6ef6b8750e310de2c29d986a97ccb5db12b4f27a78849e1a9dcf7aee3629267942b990203029ca20775f6d25436fd31c78e213736bd2132b3b35880377137d677aad2628103087c5d1da4526529666261c2d1f5844882e7682209f6ab6eb0192997539473310a147f536b9eb411315de9f0e3e1947c1fc19d424fb499c15ca8b5d77e474c811413f51866395745124492023aeddd1e85d33d9a63d753ccbf81fdb29a2b5774df032b7ee51481eabd98457d31887e9a3c121033f4a55c0b347eb7eb5e0562f03c3b02003d367bb570852d5377130eac51ae22d062e30c60dce8f83654534e7ab9cc1802191ebbff4274cbe658ccbad5f1f7d3b66dc0c9571338b83813daafd2b0f2b1d9022a827c0960f696290cb161318c94a4438a4926e9bbf0eb2575919fb5983f4880021c66ebb0a66a09655cdf39e5fcf39e093e89802860a5ef4c930db9d065d644e548bb6ada41dff43a800247537260246ed58cff2b16927716a190dc77fd845fa5f5f58602c3c693c98679b3f932c5d36dc7f5629d17d28bebc8ad92f762dd4555950208de06512a0579197577681eeeb78a0d72fa7670179cfefe278ce6881ea796b75e15bede4597da05a063cd47416466102ac88d153757baee330f4c1f91709a14bfeaded6c3e28b913798023d385b769942e90df5033d83e6cd8da03c46a0dd8a61b8419bfbc14fca3f281061bb021dc0ffeadf6f73cd722f6661a1e3d9c63ea22f3d32c4d3cfb2d7d7126cd7fe73bf4d6ecfe4f25439b675e18314ff40d0e2e5d1b7304fee6b65722d96fb60245824413bd8d3119f474ebca5b61c4ff88d0049db114ed170f814d509e312082e1cf73a7f447e0b80947da7cdf2964cce8817e0aab8ff903a7af48ecba038e9a996bb0da92d3a11ad3cd1f61477fe7f145007d1e64439fe64f59a2dd68ac40cc75f71cb3373398c986e3326ee1818dad79a43274dc7c1d4415b0165c204bf09d0b7fd06a267b2e265682cc87cb1f2d9c84ebec907817da925a361b89f3c3a6c81d4cef229bfcc6b077199d7da629c1972215d0dacfb355b5c499e7b8d8ebae14dd4889859a55bb01b3e335d483e577c940309c4432701209f50165ef6b551d22079a42c91a5147d15edbbb128cb2b9e0a500f503bc32dc530ad6d703b21b451999914f0a23c191eac2f967b75f16c1a31df1d50acbb65e3c122c337636d7e7f46ab5e581f6ce72409944e0fd77160d264e61b2eabf8385112fa0682097661d9043bc8988db60eaefb7a9727dc9b5276081605b0b14d00192defed1f153b7ba685b561fbc4e9c73ba428d736d40a50ea0e3d780707f15c2a714033a73b698b35506bb2a8ec10263d183040a06b3cd010259aaf42ecbd19b368fdf5e691744610a7cd2a73bc196265203d40cf0cb57df1e6d73ba8e20fb36df43c62b3e8433cc918df72212799acc8cce1464d342cdf51d528138fcfc2adca26bbd1a2f460848da24d1395ba624c547cefd5d16748a17e0d372f535c9e361e7b7baf110720d49e4550ff7d9eb3159e47809f30b565abfb08d26061d2adf64cd74f3c0e26cdb5ad540ea0d98c3742a8604a669d6b73360c051fe79451bb7c40491d44e77fbbe804de044a4fcc510ad1fadf89f9b1e68cb09fa23f64cfedfd29950b8e689298455cf6ab1a934cef9165ab019ca494b8aa2a371c4ff6eeeac9c227d7057c53c933be858df2b483f89a70d1ce5be1c60d5d4fc8968a6d4f4e62c37a867dae33c897db39826de3acab936232929753e4ef444c3f14a1674f32624e0dd970455a8cad745c2e63a076dd2bd3ef6a75de78e83307b896b77b1c59897f293dbb051746aca8ddb60e6105a280ae9c8bef1d4b37494251b4c209fdff09e3b590af727c5bbe679b665d9364d97619da56448e0cca0d7a29bbde2720023ecb1372c8722c1e59453c5e78b0408d72829eb1f1c0700a0509d440286dead5feab4a5dbacc190171769450cfb98fc8c3afb9a4f03314661bb235a8fd74cab2a9993dca5d4cd8ea116a1864e4924539904f6fd68484e2a67c212a58fd2bb4b52443a2ca9e5769b767c0f5561b4962e4adf7522c66cad338a7e576b7e47903266f5bdd87eb3fdf66b15e267b617007615a2867801976e79c65e2cd79acfa805449ff7f80f8f817a712ab64c8f99c674fd9743f4066bb02333ea24a7ddbaf637f5f6de6aaa2434bb990fbad94b6f9f047433322019864356dfdb17ce6ea710a133494674e2395d4813ee558e71f034d149e7a81c37cf9d788dc725a70df540365b3ed89422f9aee450c0506afd900", "5353", 2, -629875690, "09b872f723a94b60ea54590fc0b79fb1df5927d2f346f690bb1d397775b703a0"], - ["9a8e738f024c4f2a0ea0d9798c23731a778932b890a9c034702f44398fc47785500fa5572d0300000007006551636a5263ffffffff3272fc64982e6460565b78fab5b21be8175379d1e0c620b6d828da5b0df6f15000000000045152ac52ffffffff03d9af1f050000000001ab86039f050000000006abac65526a65eb4d9b0200000000046351656308b139ef", "acac6353ac5253", 0, 783240628, "901a14f3e80ee663834cf4ee657bf76a6bff8741303c245435aba668e3163234"], - ["2ca92b8903b596f66399b4fbc286ae984476fe859e0cd5bf38eee2348d83018a704b7fd7ff030000000665530053636affffffff168d587268f3e11119af4cfbd8f18fbf0ba118c2db8d85d945b686f5b459031000000000056352ab5300ffffffff44475b5bb3f71c15ae82e015289777c131491feb7c3960b0c67eae3a8f3f2c630300000000ffffffff011d8639050000000008655252650065ac5231716896", "656352ac", 2, 635848537, "d3b1a62d55e5abc3dad759107b31dbea0665002601f8e7411a374463c7004f57"], - ["d1613c6002503ea6ca8266ca1360d0ef5b481f624a0ab62d20c149e87ef77f9895152e29420200000007655200535265acffffffff6c8839515e5117259635abb496e3ba6e12e991d76f17f63c54bde364f38e5e9b0300000000ffffffff0315693e03000000000363ab006b862b000000000009006aab5365ac65ab5226d359010000000009006a5153abac656a000000000000", "6553acac63ac0063", 1, -603815286, "a42f1473525fb6f429924d8a3c203c140410809f2419c5f877b9b9ad021c5481"], - ["5455cb3c01432e337b6768e607c5e7e5c821034d454e9888d051b7cb7060b8426b64ec1450010000000363636affffffff01a88be8010000000003656552000000000158e1f804000000000000000000000000751475f75d70b36f157e8f65e942cc9524ccfc9190e2db56e7112d9049cd2d050c95fd0cdd4fff9c28717fd9f2b4677be36211eb2e0bf5ed39d007dabd60d1a2996537a179148027c33af3c554241adb6276a43cb64df54009275b74e81a71250000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a4b7420f640d7c142b469654e078391fea6e159f9e4cd1e7b497742c835d6d2ee6541e9921b3a1e3dac983e030e2428ac0c8a656d70d1bd73ca4859dce7474c67a9a9a23deee42a7c6f55f979e8fe2fd7acaef3c984c2ce2317327ebb666be81d77494c7b11c4fef923e495624cb4e99dbacb6f16054b93433bd22002ff5960320739676f7499bb28680b0a0bb4c6c3b543c056c5171550e55c40234987c9eab021d23c9103fe2d156320453d612c05bb90f0608ce325b1e7d4b64469a00f8986d0b0a476d9ab87be33fd4354bbce74bc3e26035fa989734491ee1758aca9560279c1b63a937b254158049de831878d02b18eb86d580ca106c033c9a20be924216a40317a835f491f8d823d305ba503b2de52f3d06d5f9d9c243ec82d0e3b07c308d9702267fa8afbdc1f7d44210e00aaec1775ba10c3d8dfe7e3dc3e60f201546f8127e032bf45bf81d13f298e84859dfce04d6c70a56720d40f6526a1000a54ef1a053260314c3daae1a483ba53dd41dc60e42af2a9cb80df2163cd35d0ba53b75eda1190d021d0719685bd3d8012740ca4b43d4d9b50cba7be9e36a883d0e030d110b3775566c665aba9ffb8980fc72cf00d83930ecd4e89a5663f4d901fdf493ffedaeff1991860ac9f83c13344a905acbf5f38ca74a6432573eb10f0e1e18ec4095e78873aeb06e983ecec3aa8c8a32ac826435bdd9b619d0431c5e2884ac44ffd7b9a92cd44b597d57dbcf40d0688ca19184f7d5668876ac74d17d3ed20ebfe88d7a3094306db963b468f95d920ada6965ef6694bfc8598f95bb4baab21cac2724a4ef7081da2d99130d823d034694c69d86c4e7cf435e2348579d30391a3e77ab0ce461d14fa803483d77bd4fa913ff7e489585928480e344999ed1f14e8593b05c9991a2a6e6b7ddc6fac9546ce56db5694fd842f499fff530fc263d773e82732fd51651dd0d174daf3d5d3ac67010dd3afbea04312a6a358c3b8ed840c64bdbc1a626c7ca91f826c58cef5ba13220bd7cf81de7dbf0663c4f57f55377627259dbdf641c3e3cba5845fe0d0cdafa0b81ef3e774ff837e0ab06f785f170e074760ff405a30f1295e0666d77ec8e162f5dbf1606e610d0b1e6489f7983ed1cf23c703cb9ab27eae34743b941730a534cce3f47ea9fcb78e957ad2d4e9fa40bb0343d0333fc4c6ce257004fb3554f7b3856ce547120299f7ebabcbefcaabab3e99e0dbd5485ef45a8c8c671c1a61620024e3a92b3801ebf40179fc02838f97905964be0216cd3debb2b429c26efe3929b413b4ef233dd41c0b26277444ca5e2a052698015ce5fef5a4ae48c1bc8cac2560d7e0ffe2dc4d32a81fa8c763bc8d74e64b5a04fb5c3e50a99fe9eff605173b28d36276fd12bafa2bbcb5f07df06e8c042fec67067738bec50b2fbb8855d6a87afe03dae01ead1b6a95172212c75aad6279e7114fa794e4abb580dd2e4f1238fade127348bfc3dc5a07f0e6a8e15fce7c57f8b8d6a56f284652ecff7ec103c92734576e12a3ea0150ae4300ef68643b01a46ad549fae633375dbd48472b0c7f0951b72d340627e207b8256326f81c8bd7ae99042ef7e2a4875dca1daede0d2d41a4337d1cb122dc9ecdddf9c0a25a124ac07817d223d9563fcf362373de0b04292e0e1b5e2d0d6e908604d51686c283aa14d886f39d8ab2ac0ea9b87ca805b6d48d6615e12920c3d66bfdcbd40520d8ac33111e1773f93238fece846aa2142b54114ab54ce03bb78baa7cd8d453063d644c685d108f5f6729dbb8d1f0a7fdd71b3c16f3687a87785990e0a130600aa3bc1e3587515e0110a53987484f8e0580909265abc2cb174d946c6dce5d3647a4df34a88bb3d318b96389b87a797fec4b25479eb687350304e8b27e4516ceba23dd2b256fbc0eba047ae0d0743fadfac8af71e1ce276ec3dedb979d799bed3c1157a7224fd37a0d3988a36f9d99bf2943ca0f56852558674dbd623972de50e933da9157262a6243571b13e2e571f05e6cc693f426d9c55f485a5cbfc3af30fe90669eb4923f51d63667509cbc62a811ef2e969616c451353b913a2c8d189265c56a320f3bd3fd8ef6b9d6282a755e23bede679879283e8cceca76ed0588ac69df4131eb32116f5f9b5d113a0185a7133081b7fb27693d86abe865466662b80cbf0600e43b3ea570e57c6900fbb9351d2410b91ee7402aaacfb55ee2efc6a3fe46995031e2e264646ac91fd1c6a4399e5ade4600850c80c4425166027a950b95826be0e2b7263ba63af842f7d1ceb6f5e952a11593b01ef89812e274d3f6e4307e5ab2da3aafd2f432faac3a6cf1722c8243f4df26aefeb8cd0d0b65232dd71f81c031930bccfaf8dcb2acc8aad0e1bac03e9256968c3a8d3b99e2f67e23e7b7a4b43a6f53e72a0d6da30e18caf33cfb3fd32ff330ee108", "52acac530052536552", 0, 1940604312, "e8ff3902f4734992a164fa90c5265e1398b3d331761475a65ae0e87a3263c9f4"], - ["154665a4026aa2fd146f4e8a79032d6e0454a9c82c0268ca9197b2b68ac796ea2ac56b546e010000000853ab65acac536565ffffffff44acf14606dfcb7fcd297cfe103163e3f98e9e2682871bfb60cc0952a7f171110100000001538ee733d70359dcc4010000000004ab636a008508580400000000026300055e230000000000056a00636a52e1984350", "6a", 0, 1651199864, "842e198cfb641f46a037d06831df40a409fa64af50fa4ae563e88ec964790e53"], - ["58e8d02f027ed99fdcffc2df7e96185216f95fdb98388874e61aa7cd6da183ba581b234158010000000963ab5251636a005152ffffffff47579746a4b26895843ee92a90846d79ab474fd7c3099e103179acdb4995a6130100000001ac7e85dce501d3cb110300000000002653fb75037c01440400000000000000000000000082ef75cb536bc53eebac6a5ac8d2a5a02270483727bd6612dfffcf083032cb7f0d10b0f927ae789b9975672cc8b8de06a43f68d9ed8f1027082c9084441f7eb3d6de06f487386d3936ecbdec1c9e6a7a5f42c8de77c80ff563fc493a6134718f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000423035ced872c6533bfd3694648a024f1a02cb09e64fed0cc56b6ed7643924014d7de5d4d94418c3959c08ebc16a91f2f5ff17597e85a55e86ec7d702f2e2be90d2a9e7dd8b893935a695819123f922415d5671ad77a29f425cba7ff1af219aef1469712224bb5a184c4f2c2a4b8d4f0db908762b405209e35dc0b61b992ecc10214a8ecb02598120d14c44986f004a80552ed28e773582c5e1dcd23fca25145e5030f0b026439be670fb3d76f77233df3a24abd08626993b22293de2f47a24ac8990a0cbac0bc3ff248affd7d2222ba504899da26f536541b422aa477f714cca2b3612cd6a3b7decbe5407ad80b1f85d15234c7d347aa497ddb361886e5620160c3200216d3ca2f5565cab1edaa55d64bba9567d7d2905cd41af2b376315e56f355adb6022416bf973612f9b01137c31f014c2e1155b2b86a6c252dbdf6c44e6dadc627c8032e942977d4982e4a8a24a47569538fa4855de316324009e44fbde5c3db03d013021b8906127a97f83198465f3f749cd51551495c3effe044f3688f6460ab1154640223240aae3daa867cb8cfea6d4dc63841e14e3060a49abfa46894d4ddf8d761014eadf7f6c2b899e9f4122e2cf31ca6d889e0e6247612ecde38e2b72d3b3d022db6b1d3ab800d40ad5de08f9f7c1ea1ef0c52b7c474358e5fe2e8b206834a571627da285a707697529137baf999b9e5280735659ab21ce1f9e7f6865c13a4c58a53183a8c69f50edbdf680e9c1a3ecb879a142ce907c686e88d82f83a5a46740071b4c6430fa8dcb636fd704c24952ab05b79506a9626d0172e7ae09ce01817118587265bfebbd32ceadfdd96862f774bec74e3fefcdf22296b135bb8985554c0f77d2122236da6d9f1bd95c55c498a0c1a1e1551f0481abc0f3bef2139645a6b50f952600b850c91c9413ce87bc23dec606c7f7389cb8a99d034f778c7f79b93013346cd7c93375d546a358ce73201e852f78d15460795006cb5587c93dbc45139ed414e040a7732f616fa8200fa864e953f0c6bd918231b4431a3bcb1544d8b917aee6ab7118bf34e4c6d5b6cada9ff622fa6e7508bfbb148d29d025d59a3cd2adf38b15daaf32a8f78e75241ca31a26c099ef60c464d2a04f6e7fb4af72af91c5cb19d5a43b5d84ff64f15478775e53a57655201efb684c97d31f56f8f973bc9a09d06ec5310f0c4a4b58487eef7d75d825d0b62f33929c65bff9f5c7fc682191b7b1085510cfbab0fef85fa374c4b58e8743fee62bd945243a0426a002e52e72bd6309e7b5573f2282ad3112f24bb1a377a4c6b94398f62184f1d10b341c8631122cab23c76a948a927cc953c65183a8daffe6f03189e6e0a1aad0f627c342c45dfada7819f647aac14047ec68fbf178a11d9d4a76a3a3ffe62e4fc78edc25048dca9ef8708c4d4c4235a63c94dd2ef9ac88ec51c8038f31272d2ce3fec3a223a7315191188720e4bf43fff1068401d8c68df31f45b447a645029a400f8630adb4b4d2bbe59f92657ed658e4de7650494e2040ef22e929df571263f9ad3df70a4ba64d0a0c76ebbefa90bf2639494a40e7aa96d0c0d2e9934a23bcd3a8da68eb676f208b67bba762791efebb223c63c01dd72af1cf5f3824e436d8ace97bddfd8cacb2665f38d67da411c61018c6ccf5740c659a93d1d50460c7c29f4af929e405f72461365e28f2109847d1adc3a7a7e9bd9cdf5d92d0b5b41065f8ad4a99c349b2953535563b7ee98b2247b85ee9485f5b28aa20a4eb740003f2630c659fdc2200292d7410d6ac814d0cf7057180b834ec27c3933b8cacde0ce61b17c6d84ce4b78d5bf8cdbd5aee80bd936154d596311e60f4ed546ab34e9abdd0d615705817afbc9271968ebc1cb2a97cf632285795c6140c6fededb1ebbc5fb094de81e09a4823bd670b41388133827818f8afa61abce79b5b1e2338f3d80616b7e16895b162d50ce3c66d0d6413d45043dc06c5409b039eda8272a355ee95ce8dfc73febe60370a469c11f7104e3fb5814a51f31be6d39e3e7665a976a182627e69aa8bea2176eb29b8a41add57eff802ffaa177da349b0d7d82fa312b90a2242cfa068c24c6e0a5ae847db6205c192be8258f0a183306dbd57bc1cf48fe768e8f6af085e040bddb95d1151f3f22c0c91942663980608531f6f5ee77c0f7679b1052c829aaf92d5aae7dd6a51282554dcccf63ee415a7c457a9702efd5f19f4dd1a667addda286dfe0106690a23ec7767545db3a69dac22984ffec1ff76db2ebcf99bac9c54e42608593ca3084f34b53f982bf7233bb12000000000000000000000000008e0bbd06099a4fab16867e79e6264b4a86236f3a9823c1d0bd67836e383d6757078d0966e597e893b818ea3cfe9ae2c003690d92170d942cd3f93fa05fe58124bd815c12887ce81548c2bf0871c39d004269e942575920fb85508d93c076bd6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d15240579c4039f5050bf6885bcf6bda28c6464106d1eb8bd4f95a3a00c662223c1425faef542021179c419db04ae7670af016ba2f1a13412f3bcc33773a7336442bfa96fd3333350173cda6fe259e7e9cc86dc6b4427d912709dacbab0f149320f0d16ef51d8d16759ba5e44d36f97e8e0651f391c5524e4d7cfb8407627ca70326548059d9731d9bac1492bb5ea60d89c414116c4dbcca00e75973889e67694e030de7b56dd945dd534ea78ff63319de6cf3ee429e2c407ed1ad8f094fe163ee690b213b61c9cc192c64e4b890eb5d5a83759fda99120cbeac2e77b5cc4d3deff39910e37063a1f4854846c36a726b4e5aafc6afe413df43e235210bfc2b0210353903130155ecaf7480dd096a39c363ec8be6004d7dd443bb8751a1c4ae85ce8ef72c0226a2680997630763bf4eeb888da7718a433de7e7c18ea634ada35e7355bc2acb02176e983e237bd7600f7bb0564da426eafc99a56899ac03c2daa363a5bdc2296f0213b376171038a441ea1721c8a2baf4c7bce462c67a2c4ad7871c8ff39d976caa0318ffb5d01dd3b6f7b2c742092bd97e85662f6de2357e9a5dbece5ab1fd6ea20380bad06b94508c74f4885eab2955b86964022faee9a354487df5024842d8750d9fe068df12687f938951910c275653624ae755498b778cb17aff7796b62866c2677d02ae508b1ebe27a248039ade0523dceb08f7d3befe0f2a74b3d82487dfc2aafa2dab9388619a4552910347977e76e244989c0c5a74e20787bd2ae454a98d7fb73873110837dd58312a80e99fefab86418bb805325660f2c746dab46ad747bf3e7cacc7ec94d5776a14e27fe1b94e655fc861aa2425b4d1741098a83037272405dca44aa31a621ca80bc025aee3c7411bc2899356dca2e5e010753f97de4644b62f81049e25c849675346bd490b1def1eee5ebdbf5ef41816d643985433ada53c254ed63b99331a5b92375a693c7bfe4a2dfea1ba0161fdfe1e74f8ce699b3ded075fae4607f0426d2b8e77e0440c16ad9a2ebacd96b88a060d5a9bee294cf3537858fa00dc2675e2520a22dab46e42cde8dd67cd1cd59e2c2f2599ce5f7efbfeab4c8a01eac646d394a0fc6dcd32d197d9c5edc9accc809456178a99b0ed0cbd934e4169f702631bbe7d112c4497ae022674e632a8adca1ea6014ecaf10730cfaa7705d66c3b4dba15a45c51eb876df0701f689a01c8c870a717620574e6c23dcad2a918dbfc8964eef4c1ecefcaf13935466d5d46fc1aa4555bb83967ccbefb279327ec911c2c7f0ef42ef0290496cb92ff505058be316475bdd0a404e416028811c5f79715dab0f889f359c1d3acadc896ef7a854843d13487ad2adfc47a08e442ecb70ea603406cb2e9b70518b2283bec3f2de0b402d505b48657d028af184c79175f888ebc6e3fc9bd8b57680e394b8da7e896a66966fa93e0e12079a9f96065a30a319d67290eb6916cf0c0d0dcf9fd04108fd9d9af52496fe40e05b413e19b2d795320abd60fee3acadd4469204f64200d588a2af97adf8d926a6a594b8fc7fc91532504a57152a370e2b21c67545ea0f171f08b48cfcfabe6d8a437e0b3c4b97605550d8f9ac18c659afe3ff9cb452e94bb1ced9b8135c06d554256fbd325bac22b84e4418ee2925f9d28981928d4be8dde43176908b2cfbdaa669783ff0c49eb395220cca22376f76f5cf0590d242b977a3a806ee0a84764d1af667ac20e38970adec7ecab31ca7b6e148010a319f922a95f4c15c8500ef9cef90b0667a01e465af90182c25b27828b59ed39ca6007e69919b084537f68b4b020439ae783c58ed7a460c63923bbbf67703d8e0926377f4a332146bce96aa4118481509d8149ff27fb2ba7f7d792314cd44789192f181eda67c3c86535bfaac27f76793259c2d4b13e2375e0d5b066768fa6d0058a9030aa08eeb69795ea3da14148dd1f58554f953dfd58a40553d6a24d26601ef3d2c0e952fc1a5ce5d4e1c9e2f68d3e6f4fe058a03f1ac3a74ac176a62869456efc2ad7b73c7c2021efc6da0ce91dbd606b9b020b00f64689f03245fa4c358d113095f160d2808e37257828eb4a0810b882fe936f4c4d838bafa03c13cde05f1b14e366a7e7091862fe65abe51dbe4d61a0a17c8bd41d884ac6755fdd7826484aa23852cf800983c6a06f9480f6da40efddb7364e1db78cab53039cbc50883a91b557e97a9f137b60b5f1610868b578e317c1034e67ce03755a79f5c2957eac12954d6bdd41057db147435ed6da5f7935dc807f6f148def7586f9a80af245c16ef6000000000000000000000000007aa9695c49775d6bfbafaa1078287e25e7a81f69db13b37d8d237bd0b5ae7c2cdc0d6c9ccd7d9fc4fd075ee08d85352996fa6e50687254c2498332c9c1f9c0d2e9ff2775edcf8828eedbc9c220ad54255dbb09561d3cd4a00df1d24fd620831d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000094b672b7a2826e7782bb82ae11737de3e68ccd9d81d85640f715ab5d8584d1edceef543f7f5d0cb83eec134f3300aba8ef1a7db4354eb98bce8c6d61f449bd448310886ec5dcbd90d9106fb8fe07d60284d5872aa79bdb1c57f59874fc5a4c7924810b6c8450ebab9ffc9adccca5c7f8605386e7132cc67898be6ec43a249a52030aa4cadc4da7d51038e4c30ed661ad397fefa58a609ea2ed90c56cb356be9eb90212010b99f229abecafac18eb3f7d4604b63e3919b10faef141a10cc701ee69110b0ed2d9f8c362ddf26564d4b5451bbc539f4cb50a174d1681be92f7ea7569fda108bddc13060a84821afb604996c70da366035f628c67eaf8f4f3153e81c7204d020d96d0a40e8f8467cbc48909a118408bc869e82c4e53f6ffed76db0b7b127f45030f8971ab2cd3c871e14e74564e8c5ce1a36bf682c8d3fa121f88c1ce50a35a22031da43be0aa0c0d59d7458204497a8d5d07695222fa6b885d2d3c1b1d564ddf7d031808ed87e9df58bf11ea52ec7463dd80e46b1484e0dff58398453ea1b5cb5af0020ef3370d85a7409e6f2bce772a6c160fa4a33fa0855299e067600938e05a15fdce3e590d6e52c3873b6d100be00384fde43f0379aa7f47d8d2ad50126b12aa0f07daecb5e15c6247768e675f567ef5385d1b7fe79522febdb0cffd29e1ad75f61c5010cf6ed918683ad3438db8498d1e819a77e3f5d9865c5a5b1388499558766fd2c3cb7bc444a3397037bd5245ba0de4f72ff9aa5434a9c9d8b5f1a7e225be20d4388f5f5cb3c15c84ea4360bba04822057930b63a44e43a7f6791e1ed6d91d65e00333ff2af060567e79c3beae3e74a9980e6433d5ac8685a7507eafeaf223545e91e2243456abfa44c112a0f38faf12d7ccfca0964b141ed2dd3cd98d484ea7fe64d29648ba56235ec25461e20ab3ffaa6870fb97cc2b48e48f855af222bd53d5ef1494ffa99975e3298add50ba517436fc4c452b11e3e766f686bd24324df54361dd435b7c3a1cfd85467c728141baff0bb12200e7986f1276c37211881c2bf7bac9e30351c01402d782b739f818d800fe47c0e11272ce0c13c6bc77b21e76291342f40e7004407ffbb88c0cd5fbe3357a2ff8c8eb0d34ce50145f7eaf4226ff24b4bb050315a1561afb4fc282c69bc99f2613d91efe016537e4aaa9ee6d44ced0a4db50ef6419f2d608c7877f80e607ff3767455a82fe308d290c5de110375ee3b243ffe6cc9ecd581376c3b27c9c538c7fa8a9bd023c44ffcf9b6aeada50f7af9058f79c21561062b4e9de60f7adcf1b74c37096cf0b1fd0a7d6344a2880ff4742e3a76fd773d08e395cd46dc959f05f018860fff134247eb3012d97e90eb93b300da9d77bd597ad8f35f69b440a7f0c8451cee1e12f29df55bbdb5c24f1ae78a87baed841c222d98e64c51575330d4e6283af19ab433a91a9067657ef9c07d7f168c4afd1c300eefda59c7cdd5defc0ed5836baefe4cbc6ba63f43286fda85e1dec16d0cea555ec43d9a78262619ac62a6bb31240fb5d65bebc11f8952a97cb451466340bfcbe16e44f327dd7f813a5059835f8087568007513fe49f7d0397149fba37f67331fed6ba2e9b3cf19e3f4a31bb1ead3e5982cd12997db23a49a8aa02d06460a99feb547039f5fcab2e75342c3026b64423798f007c34421120c2a223bd164a557b101c18d0351e1c3a912fdbd5a1160c1dec1b6a0b4f3502db6a1ef2a8f8fbe2b74b0c72c1994006efcd96cd0cf68fa789881ebbf7faa251bbb461fdaeb9dbcc3ffbde0a28a6d51b804f5648527a3821d7a85c5bd5ab52f684a5a10ed4eb23231a56b1845cdfa6caa1a2c6c95964570f2207783ff7f72249a0f6dd3c5565d5d825dec8606256b8c08d9746e31b048c101a410b260d8bc8ef4aa288c5894dfbd6187e4c1d58a74652494e33d3f31d58b32023f17d7e480fb006fb4fdf852ad005f2be377b7c04e50266e9822d03e22fed97b1e727cd74944addab2fbd1d59da2fc773810d4fb3a4a69598aa024a3d4389665e20f9623313ebc05224f8fbb5f51eef82dfbed804533ba259b43a98ebe34a4123f7d562d8688aac4f2230b97e26d4d47614929d626b88a31a2da3485e178f7b235db7277098a6011bd686f959717eaf20223f0e8f3386e25b8b88cb530418e84e2aa44bb69494a738bf945d93f398312f1c91944c11da44965cb6110b355506c15cd3f3b520f5bdd716c0babbabb892dad45341cc10dcc8a36bee9ef1e939feabb1fe1aa3a33774bf2a461f77efa8dc8f47ac4ead6b59fe30df7add988f0ec4db2144b88e5a244b035b674becde69a1fe1658c6d9432e73d037e12ec178ed80aba1d8a0ba2c89c99ba27b9272ec7d6fedf76453864f6241bf5e55491469cf6c54cd64f234c6ad871376ed11766c8113301619e9b979a900", "6363ac", 0, 167502891, "455b580fd754ec18bd182b43895ace9eb4bbbe31886bf727da9eef7fbf2c391f"], - ["fa84bcf9033dba0d6d2d441513c45b0d3fca6ea783da7dac27ed64ed0c3fd822fca9bcdd620300000003005351ffffffff9c80345ae2ec420276ec507611933ecaaf670936aaf0b2b0cc1c5ca930f8a28e000000000151da7b050f66dab7317adf034ad5d711e4547d240aee8aa63e13f5a55c6234514556ffed310300000008ab655351abac5151c8b19207042b62b4040000000002acac3459bf020000000003ab52653e510c040000000008ac6a6a51515200521cd4470500000000076a5152006a63513ccac9ec", "6500acac63525353", 0, 1022872894, "3e72a051d1669777e21b5633880af32c0c3e751022c31d3fabadd238c0c2efe8"], - ["0b37f89803d14061065e2f61a881c46b898c490e1a263b5b8cddc7a52f989b7b10b3710eb80200000006ac6a516a526aa7cedcac6b91644632a63a126c105f3be0b2f5d4a96612c0504dd79071d5b3d4d4e9328a03000000026a5106913bc558c602a6c6cb527fee77c3a155c541fe08c08ac00f32f34119bf9a65a942958d01000000076552ab52516a5284eb1e18013d4f3d0100000000016500000000", "006a6a6a", 1, 855527435, "4c1f9e489fcbc71d9094f219f8cfdf043b500501053f6fbd3be7a5883a675144"], - ["b4317b62015545327b317f5503116840e6820dc6901d57b362874337b5673db0fe7457738903000000016366a01fb802903ace03000000000763526a000065008d798b040000000001acb2641b3803ebe44200000000000000000000000000e31341c5db55e321fc0e9ae595e88b233e493217716bdc93f999509dd33caa8251d92e3c2855c87e040eb4d7d47c58d8de92d69c7808ac9f648a01e6b536bfb3418dbbca307896897a5a125337fb7faaf5c96f09cd7105c89b15bc45f1af104000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc3af08b3b5ff3671a26efc8021f65734cb68dda01e1af9ff256317ae2f41abf2706172f9d7fa0f94082ab91938daa87d5f6a212ab9a8bc6630b3d7dcecf52d659e95730e40b0023f69ee942c858ab3e3d508d42d9e30fb97d935eab80d36779ff3570b8413ca26f6a34e28756c412ee4f09298bae2769b8abecee632deef8100304548ce0921161a9b63fdad2a618b8e40ac73819371f77f2db3f9aab0237c1ed0216d73ee756aac36ee4b82e09df908ddc3ce60f464862f712e897ff3a6bc8fe620b25bd87eb83c7f80b7db225441ff69b7dc191cfd1d6434aca2e93dac7a54243bf12a54eab1d6ca695cd347eaed8857b7623033f9480c107f0cb04b0938eda37d30203409b68c9b5a18967fd122f267b4ff922ced9cc638290977102bc6d49953acb022b3ff7bf0222eeddc7b2010e084eef3c0195faa128f598f614795fe6068e3a000226f291f7098c09f6ebb9af1ca09800f94d5db30c88d2adb7a94d8907468ff78c031a0c26b57401987dcf943f961125f343934a9211f3d4bc61aa02ae65a3facc0e032c5aacfe26cd372705777783995f757ac1e14d4a7e168f8d0f187d835ef883bbc09dac19030f469e8654692196bb78410a576cd29b54f1ed416773206b9ff220c77cde0deca05045cc68638c51dc04b680b7e3b9f68228bbc26d11a871a82bb5e8ab6c1946692f000577b0bf3fee74415ff6efae22fb4311ba8b344752fe440aaa4595659f9b918dd30bd0c2330d5f9daee211d66f30a5e4ea67f3f830edfb9b261b00168aff97c4d569da841fd34be20feac6e4626da0c72577a21616803042bfd0d48a6d3aae7259016849855622795778595301027b0ff24431ea37b32973e5ff3823529e0e5f74581b8609f31c2029540f835669879e68185a596bc3d5f826fe4465e9d24dd7918dcf72d23d8579fee2843504a94c8fa99e0af0762b90c8e5cca6c40ac8e2d379f1f70882158e6fbc202c791c64e19efdfeac5a913098056561c6a4bfc4a6967306191d2e19991303016c388bd3e369915206e4a95f8b484fee43bb8ac961ff26b19ebb2c391b66a9547d7a68f2ecea94e554f636d1f8bc4b8ed7a4683119dcf1729b310c247ae0cad269ed12d3e866528459a80274c208c82fb27cfb90914401cfd3e0f8a62ee345cf581e0d11cdbf42eadc5c49cfd785462acbed681b70dc73c6733fdb12ae758d6729a784b95d932fe27494bbdb476a24a0f386e656d038587dc4ae1e491d13cc855815ae096398e3e6ec138da275e31461b8f44792a10c913f955d9e2b811f21a45c3ac85c0f41d67af9c93fb42adb85dd39684b5bb63316ea46e4058e5d2f62cac8c57c67646673304fa36fc590a03e0bda71f0cb858552397f0be5c8d9805fe4e2dc90fca6dc444aa9dbe5a2cfa316c7242dacd0c1d65ceadc76fe6c70760287cc0e7f4cb39cf408ba2f021d19006adfeabe78b5e7bb14c1dabd78c12db32c077666a7649042ce3e43a8aecae9e4fa87617d68b4340ccfe9018baedb3ca31b212e4d71cbe1c0bb8b97bf6f4fa4042fb01e9f20aaba395d06d48dc09e7a629df696a7f4d224c1df17a31ac77c95568b1696270ebf8a074aa144b9000da0003bb123500b24a4e0aadcb7f617a6a94c9c4f649fa70ef52efbd0396be0a4117ff1a69473e260f9711194b4c50450b621ed37f743614f20434c55d4aa3c4ac42c00c0e58b132420b13e2c44a14bc39f407493ddcafbd26dcd50227c98b1080891bb2a831863238529093b8a5edfc3132e858ba12efe4f0a2f140c82a7391a2d36c89e7ebcd53799b970f57f4893fcb613f825aedfb3977cf674386221679aab845e986507dc26715bfc41e6608922674af56fccec89169e055fc38fbdf5e9f9f52d2f1b9932705ae43e6136c702a5a23c880c9c5ae11e06665cf32109110ccdeab85351cc57828a3cbe0d433730dba360979b4b30d72df86d7dae8127a6fffa7ceaee49ba657ca5039a694ba233505af05bf5695f3b438f874e560e11f2486930c9645af61faaf246b13a5ddeca27f1eacd2d9ed54ad8ba38b85169d8db0a75696d7be32b2de494a62da931cb5eda4ef9ac14bac7c4a89f0cfb3a492d45ee453e7c232700a6b315122fbc5a96c4e45127d232cd475100e9b8b1a3a5d9307d63677b63b3ab116e0a07dc0f5067a90332c24e9eb75b818e36811c26085f510a6971de316ac82be7a729f5ccdddd86e09c431796859bf1e2eaf3c899d8e6cb1b3f0710c69470e5f7a190ebdffb29a3d74d7421398ad09395d6715bdcc14dfeb3bb190987000000000000000007623a05000000004f19b5221ae5df56e73f100c40b29f1f1f99ef5ff317a305fed39f1e1b299e36dbe4b979ee8c879b2d3684e33e2e717225e1b0b3a1a9f8db3f15451129913e3e71d6303725ff708e03d3bb0c967c8621a2c68c97003c051aa928748b64ca398500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000887c01884471d78e40c6e9d35b74ac4b349f8fb42fa7f35bbf1b2f1ebc711d29b2714dcfdd907500510581fbd5c9755b5d5fde55907a8a77db0a1f213ed430aa18bd6405587e94b06e78e66c481fae3a9198cb57ce6448d48abe1fbbe5464a6e2ed2085df2f2843c2071c5a4a5ee779e9322bcde31a14442c5e5a2ee1fcd9cb20226bb35706e4380463862961b5bb107085506337839827ffaf7666552f2dc7a320310693d699295f1fd9431f8529f1ca40c22c8f735443ac6c38eefd202bdf0a78a0a28a33a3cfd9ae90dfbc65dbaf359b74ea0ec00e9f18697e602566ef1bf5ad93c18ed55c5bb441cab5221d6957495ff5bf290784bc5ce910dc872b16b5bcc3ed90301984b2a8dd9d57dbce58a2e88ac082da853532e4d46d2f2e41b4a5591c305c40224edf0b7d5fee3a6290954a820d006581c0bd8fef8ffb3cf510035948ac226600201d6375d627e3228d80801174048bc52f04ca3e61522b82251e95ba684fc134b022be9abeceed6fd1dde3dd78014b421ef48f7b1660d376b7d6e02f8f7eeb84b70031d0ed28bf983c0665b17f1953dfa1dbf29885880d5fef6a377b0d19c79500780651cee72c004dc802b0a1dec7335875fc7bc18f6613b7814053cea91a68151f43ef504d9f0a10553f692d15bbdf30350ab9e23469f4f62a982fec3bfe05c9267ad048d1d5b461c5df946cd558c3a5e4ea22a003df018a6e7c459f99517e899ecc0e8c06cb6af4bdb9c5bad7c81e5095b2deeaaccd7fe7394d8c026769123e9df81c5dfef22aabaacb309128f68a7bfaea975aa47b465af43d5b09047cd812243cec675e5369a4bae145ef628096b4fe480548ced43893dcaf2975b6d29c3738334e893a4db704b7eae704edd47892f984db9b8816bfef25739452b360127c66c681623e576a92ba54626e29df897aaf6e566f1c85965889cda7138055108acb81b2d1ec3e0c44326764e86760df1e1b7a06580b54dca26a00d07bd0360d49bf2ae132f3dc851b9f7f3929149c60826ee5cc51459b0d474495e6d6cfbe96788ba1e0c67edad43f4ba78c6975cb031d66546348cddc8729487baec8eaca6efd5688c047643a1637c4f86e925dba37a4d192a252ba07e2d6d4d454a6d85a3bbcf5e5757b7af3024b2ad5f525519426299e0328db26fdd554ca846c7353c0f5e03f21925344ae3d4585e682dd9a439c8891686b7f45ece4ad6c61f867c80222ee50206d776017b41118e60e592cbc6cf2e0ee497d464e044afd19cbc2c5513412a4f2508458265a7100cbd8875fc8b6f00f099969a23281172977c95a7ed6a73f89b98b2ad9a9780b7dea4596d335528ebca2681fdbf351c21d7387479a6a8f8d691dea34c4f9d90f5c7b2c089165b32f00b402b605cdd237c0230c3b49a30c20a04233d34b1fd1adafbd9090f2b060b8cf6277e4f2765040ed5037e8db735b2ae1cd2f4768a56fd55dc3237c72696fc0ee40bd8b8864088619899ab0dbc8a301cde4de93ab6fd081720b06821c2b801b54e0b3863072e85da989abe616e92a2456365a5826942ab4bbf6be64308f4ea755478f0d45926efb4588c33bc4fe439c500cfa640497d472829d0c69f876dabc69aabfb26650413a342fd55e3da6795af34648bc790be46c8e1a89392f9cfde7f27eff8d76d2fd2916d6082b71d8290dfff6975ff893a5ee24cd140dcd8ecfbe3a32fa870de8ad0d609e4d19b17eb57fe963e818251fc86ebbb9b6dc3780c3c8cc84c815cb88f371c5ac4fc1f85ea9e9a5b5e3e0e7d82e78a18de6e7e3dc3d472a324201c9c4fe7d87ae9c287fbbb6f92188d65b2badb13f6e6593b222d4a4a99fcc50f4c92c3b60909e577ed2a62006c5f09fc4fc8a56e1d39c4d0b6ccd6f5112c7a80ac7771bed5076def2210845ffff066f71be94768a1eadbf099c298ce00b07fdefef9092a8fce660f6e908bc5003bcf15e1eb8090c03e209f61e1d95ebb041f509568442099495f6049efd6f220ec102495118f7f52f15444fb8b51532ef3ba6da9c028a2cfe51835c181b586deefaf05bf54875bc36d2378678c48d886728343e618b2c5107ce9290774e7a7e429e960a375e4ec98cd06a8babfbad80bd9d810b5d07112b3f7442eccc9747a7e9b1afbae4f9c81fcf036cbfbe4d4c60d2d87e9175a23f04093f54545139bc0f8dc75c6bf6d72c74aa251ce34d5e8c38f9c960b6a2f0b98b494ca39e42a5b04e72321b841ee854023d92141b277ba51f221e9068f28c9bd40676334c4ac37ddf508ced8f8ed3e1531a068006766f022d65373e300000000000000006f6bbe0100000000f6b7bbad8ca78fac5c661b1f6865db349006ff0afd38cc92d6d1efa872ce5b96ee7a6076055e7c1d13b527ff6b8da1621d70308e3f0c762a3b8364056edd2eb407d0e1247bdea99321efd1a8f8c712eeacc7f0fc60ac2066244652bbfe72016a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064b452c55af14333d0a0944fe97426c2fd88cc62e2b48e2f831b58068acd2aa3641953c02c66aa94e11754153da6785eeb5a67cdc37a0298ae5e3ca47d93f0833e8f6e8a32d78aaf47c56905d79cf48c0791fe9752151c10f22056efe979c84bcd9e8583db5863dff37274c44f4e9d17520e9aca52e3d5cd11b34de593385b480222f142e9b81d270b2e20ed93fee91182c4abf7317d487ef317645c661a9d4c2a03152345edfb654e4e16d36193766d052c00c48960fb79c73757ff874d5f743ada0a185465c002486e55c7ffe8384bb0277e833ebe932ddb4bfe63db2258fd02889a0b802a3ddb3784bd8eb57d5f825dc277be94a3dd8636a22b01b6ae85f8a25ecc021577a71e2c19b95954edd2791cf0bb8182dd2981849ab6575351f022219e1730031e6827a4ab334a1d5963920c653adf2bc7b66b978fc082bef6b88b0ee7172aa1031a1065e86ac235f7c2ed1a5ea1f46bddc6f7935439f663bf9bdae4177cd4548a031732a67d0fe10e69b7b21707800932a4d09ec8addfaa943b2ded3e45489c8ba3030bee3ff3a1968dd310bebc04d6112a7e1617f559fde1463cd8b1b3e5b8519a82ade53efb357110675a9c20f2a51e0748c0eed7ef91b1a1e361c5eb8d73e54dc2345d8c031be8bf1758242f5379f4c63c296575a9fa9bca02e8be91408c8ddc8602f3c6d4a97073cc23b6b560b4511780ea16bbfc26918271377a300a64daaa96bfbcff9edec7b7e9095ec82a58ba3e8191731f59bca1a9ac5ddb34ab4166ef68dd7182ede74be6b0516c3993fc23d325ce87352ad2551e0dbcd5bc9feb95eded6ec9a06c7eaca9564947b8629deed9928aa52b9e628df5fe0e9545fa51e8605b7908ad2751666028d1bded1bead16e94a40fde72aed1d9815da3ea1949a82cfe84932f36d33b0cc38979c75ac494f4a667155ffb220bff76c56302b88101a41559e8e5341d6b4962f92e2853b49d1678010ea2e583c5c4dd1063f17b8eb42a65f9c1ab592ca9983a48aa6b5721c47e03b759efc5c634c3a81de17fcb396c70c074c48ac81c67219a99979d1a97fb688ce0f7fbac42585cd403e146ed8ad6fe76e5dcab4dea0404451c6641ed2e13dd316bb0114c025e3a5595e08d30d128320b2508b1f1d3ef554834bf0455bfc981ba06470b5e7402cd81ff3dbecf96a5b02e40c329a9173e5e2ec28f71b61a28396f66f076412cb789a5c7dbcc98ac9e366e94851bbeb6f55c1d0b20818e8a0925df5625726f538dc2e0a282eb2c64a1acb963d4e2b026298c80a86f6bd71f56e46f43d9268ca0489a42c9f226cf86a637ab6181ca58d534f0c049eba7a10effb95b8f8bfdb09e734a7e0460cf812d9c1d01a27c866aac4723b0ebac057957a7685150c88c7dfcbcb05ebe46561f041833c80035de989efe86eb5dc64dc88718846bf5ed0a288f972b150599d414250a894c1fe20e5bb205f88bb275b6044ba27dd804806f4065d749b6f727e1c7f990d7e69742093af69aeb1058663af7a11d5adc9a16e7f7a32a2e62706d753ffac87f66f958acde75e398b39237bef82e3d6e873c8fe8b388bd556b240ac795bc0436ec83dd3c0241bf63f95e98b7a1060f4500bb5977e6391b0504216a37c37ef78a451f36643da17cef14f04e8dd301a7cc9f4fdcaf547010d278653a236b84ad60041efc63e2ac9d96a4c46223ebc68f96cdc571f844f2a8156011c446c76c06e76dac297d3a35d1e6df625c60b018509495cd6d720bb589e5739c74e6a1248d9e366664a6e3a6c6c74e6efbe1e814f89d597767d383b7b226b38da8bd24b74c24b30e1bbfc5e1a189fcb0fe72b338da81f99a6fd97ebb5fa44c3aa487a803eb092eaa6eeb1749dc8bdaf1eabb1d65de08dbf53f2900b3f8d28ff29b580f416fb5fadd93acc23b2cef5b7946f77f9246fe2b36bcb8c3b2d2ba959617d0e210257c7d2cd8e18f49fed42e985163ad0a8fffe0653ca14b8d119a6c9fc9bb1600c8483c13c27c38713a0c732b0e46e1aea30edd869325bc8bd610b47510aeeaceb277c66d26317e5e41ab8d9227b7f89df73423cded756cdb378f881296c2254dca2d9e84d91069f0e20495150fe98ded4a7088e67086328c3e7917f7dc55461362b789f9a536cc9ca5c66873c06b4e929305da2747d0044f85c4fc8b1a23177c15260e77994099ed33a7fa56ce696c2aab22fc495ececd66950fb96642f7c1997be95ad48b082923129fc9d52f2857c938472a38336a4c6cdd51f009688c03a41015e85b7dd34c484119adebb44e505819bc3c8cd78afc88cc03c9577e8f1fffb7ed4c01f50179d10e4888cdc54c247089211d8b71bfd1b451da33789de74d4ca3b5cf8e4b39c03683a1410c251ec80e5cbaa12cfaf6457c634021b35600923c7f170b344ed1d8024214ad00d3991ca03410cd9504", "63535165", 0, -1659337291, "a50acd061e7748742314f536b9048c568f3dee6acb070aa93f1ede8cc0a9ecb6"], - ["9ebdd9e20475ed0a09b0d55c26f49fdb2cb8f84c8ccf4ec936860c9815a6f953ee704ad7370100000001acffffffff699cd83250db6064f6a1349f45cc2d3d76fec3d72372a5299338085c9b7805ae010000000563acac516affffffff3668e8fe0c43529e0758d5c593d68a9e79217aa167d16bfd5fc14906f6aec5290300000009536a6aab0065635300ffffffff89a44fe143954e9acc737a71cf479550155a18e236d88668bccc67e44fac14e700000000056aac6a536536794c9d0455cf59010000000001521cd2de050000000003536a52ffa357030000000006ac655165510020e082040000000004526a6a6a494b2528", "", 0, -1346940735, "0adf5b0dbde906ad89ca42f32f5ca630876e3af5164f4601f984e73f4b6a39e2"], - ["f47e9cb0040d7035888e38d7ce54155245b68ef3f4ae4b421101e472087bc44a75431b3e54010000000551006365acffffffff2806634f410c53e42f15d457d19429bcdf676b10680cda26d3d4d8d91f54e9cc0100000001acffffffff42d2a7c5db77d4cb686b322b83727f46a36b3eb4f3dc1b5950a7fd6e581ffb340200000009ab52636aac63ab6565ffffffff4af7bfeb86f5861d7c85b3e81ed70877ae13736f26b2af22be3e7749c48f616d00000000086365ab6a00656552a1ed2e920155a6d304000000000965536a6565ab63ab6abaa73e39", "00635265510000", 3, 1002574041, "de3f0130978084ea2dca7089ed915adabe1cbe78ab573e845e0ffe1c6680942c"], - ["bc5c8d26034c25286c11cafe3a4df1e5bbfeedff181abef88ed4fb20c78bc3da4a799191e0010000000465ab6500ffffffff1d007a3c0a11d914a81bd359d118a51fa6326b07cf642e1c3fb16418adeb01530300000008ac5253636565636ad60c021ff26dca92c26e9e93cb230fdca2602392430c4f9b2f49d04ccc28c3251679ed330000000007526a63ac6352acffffffff049b963e0200000000066a6352acac00a66fe801000000000652006563acab32d71b010000000000aa7f260500000000096a6a65535263535352c71f468b030000000000000000854eb20400000000d9e98c76e928f374e9156a43ad12de000a6b473443914a8b7235d2f140302c72249074165e70ad6726c16022281f6dce179ff6629008202de39ffc937e7f9d1f36c8a781719e500c4a9716bbe153f96c991519d6af505a8bdc5ec61bd2955b3b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009b75e05f73362234db01432c9825f2cff0000aa56701082ebe544ee2c34f8bee50ae27d8247248e65326e66594e9d3959871de523fad2dbe5dfb39df66c90b1975f0290f04905c1faf6f7039646b0b617851bd17b81b044fc8285a70e92f1bdd8015625c10b420308a8c89a59389db8a9c1c6f03ff7e100c57ae9a6c11ae484f022179adc8367bbd65bafef76d3d5435ceee581386b8dbd0175a92b963a360ca67020bbc841940c6ee8f23420fc301dc6e643c7fb40c3feee68408469ba6213c425a0b2074b5e9e9de8dad1b070e8c0c17ed18a04102e93f84b58b1db42bbbc0d710ae21497cc395868c7a22939850a1b678947f17b4a7ff3a5ad434444b68f22be42d0303c979358ed2a7af2ffde334710c6b0ab074e8d96c56461531f8305851742d080311580c2f350c24d33c408c63dcb3eaa544f88bb3d38b9d08dc879112c31583a90306a2f94497e12608f81252ea3afefe9a4ce7498e5cec32119ba9008799c579d5022173ea40dac1c84686ffe35ae6124da7e8cfddfda141c8cb8b8f65d7dcea43ca032e06556819e873d1bc6da68c6b4bdb877de237d2afd2ed02756f3cd9ed1efb30715abf158da1d84863f610561e8b5ceffd8fbaaabb71286f512a8de49affb897ad4f6743b9bc41adc1efb9c15e57a3d5dde0a77b79f15ac53290f5111f9ff80695f3345ba05f4fed7982800b30c1bebfc8556ff5613bb38e7364e348a1d2c980c5ff1c7732d3c078facbf9292801177fa161892235fe9cf8cf987610220af33c09c84eea75ed8e972524ca82dc736fd78809a71938758963047b7c56a9834fff62ff2f6dcc0c12214cfe34c5c749a0ce98ab7a94a092e106b43615151090db5a2011c4167a688d870240e47e9a63609b28ce4f923c23d6185b7958dbe699b3ab8bf89c1ccc137115cdafefd5501788c3dc1ac7f9d7f03f8fbc189beefbb60e23daa70a7670659299a188e09e3be565d7f78c2241b26bcb2cd8fc6b26f53a2713b240655e36aed5d808bf48fe1023642e0c835302f80d889955ed56446ebf6939a9294b762f3330671af959fa62ee546d5e7faa1a4d0a3a7bba37065ca44ae781d7d5f377e2d10e14d92236fb925428145f6bbfc166e43be3f2c2ab776a7f01ae0e5dfcd4c350248c7548d728a5ac90f0ab49813883ac4fa3f4540831628af19a69af46e3a1b722c7e2be1830967cde78ca8f19402fe602cb4b1dde1d0cc0c7b17d2547a7416362cbb186b7c82e3d23244f83f896fbeacd531cb0eaed3001a0752c6ca1b9ae10de32ae2c2135033b0418cc11c0289e795f2f3913e61b04d421b47da5c2bd4eaa0453a04449bf4173498820e95ab3482cea94a5601dd124954cdc577707799bab1949f7a12b04a86fcfa8f36b507f501950f12a1bd49aeebf8065e0c39099d5f1366335d743e21e634b8ba82a60607dc68e5344bda18d79bd2941b0a5173401edfc7130ffe6e5f337a5570a56b9097d50ab9cf6dc2aa1d49d459a054c265c808649e25e5ab2304d0b3d17cc490ca13f7940087fc415277024adea323576331051eba4912b17b3b055746ffee204f6e8623d7afc449e1196c33b02fd382de2beb59d5824742c33b765d1a4ae114e19574c36e498b6c3627713315edd9da0f18f104681b113af11824a9c135f40bc838f00b395d9ddf8fc7cbfcd751aa773c8301b6b4246487cad524d2154ed9d2f1f0aa0c1c3cd4c2252cabc755206cba067119179f6d7923843f065628fb23aa435436e83136b4eccff06039583fdb5622c0d620c8d685b8a5c8e99728541984388bbb202052a8d8ed054447c4cfe3029f3fdd66a03a4d9222b1c8aa7d164dfb2fedd06cb951a104f28d559e779093d14be557e9aec0db333256ed93e9502e694caf9c7e99f3b7327d7252111092698ca45ecb9d202229f342bad13fa6ccc0ba66c75855ec4964427149f2742236c7dd2438167f61abdb0aaf01dc27d291dc44d79d99ee1856830134c325da5b049fd51e8140de0eaf4771ce58b66983cf6eda62361bc34fcfb16d72cf48622b81071c3976261285b0fc9906017e34ead89ebab848afeeafc3a4ced874e2ecbff436f2e753b627617cdb263e02c536d08a598e404038addfaf38fb05c92712a259ecb3c270bfe7d1bf8f25d04faf5408b1813a957ff1dcff4c899f4b0e7c3773844f9656525e59f0bf9254e3c2e494f41ba870d3be5142f8b03102e07fe6855acd5af11841c6e5a3a50241af48bb183e1c15528d98aa82594572fb984689887e88360c34a56a177499a1a111f3ebc29fb993d0000000000000000bdaab50400000000ee71db8515537c9c57d82d6b0bddd140a8f22f11a55cc6898e32a65177ff9a02529970daae19db1dd16224dcb6da6c4413f701c9d0207a6b6c62ded800eb285208a413174ee90692292b2d5f0380863fe79f8fc9b10ebbea845835c4be0b3cce000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008742baa6972172748d18ecb9dffb0969643d2807bb5123cee4b91edbdda40f45fc5237f816a68a0ec927b234c82c8ee5fcdf95dc4468c2bb115ed247697dfbb0e0c8ba5ecccd23d0086cfa71d18c895e8d6cab692e07bf89f621926eecfbf960acfe70a773efc9cf608865a4296565a8820a9f2a7c550d4fa9ba3510bd4b3e3d031cd506fc96b615f6446c655e9eb0c07808310783c3013bfef88ff3da5a3896c5030ea1ec70d1d420b8e973970720a0f8035fc690e1df27841748df02e70676b03b0b0b2c73dea58b597ac1851c55c546bf464f4cbe2b604145d548022863c5accbe60002817074eb514e26de6f5040ac810d154e031f52fa6a9fb5cb743b1c8fc8ae022eeff8c2eac9548420b8c18e6242d74d16f42cbbebc281fecdc102f533123c94020ddf6ea5890e0f0c7e3205e6ee175853d15d72b6fc3b0ebe5d3e95c67a4d0b53021b41deb2b7bb8c173161849ee43db23b2b8411782be28ed2bd0ec8984bfb19b5032ad6ce55dc97a2a3f4ec3cd36485af2f7592cd5c6b5735688facae6955711c950306f3b4636162f36173121cff2fbd51ad3677be4c870d42a1bf6c503894cafba2fb083259b0f4a50793143d2e3e6f868d0c5c7dba05007fcdab1fac0b9443ef96eeb0d86ef67b26ac59a5b984a4ec67f4cac3bbe862c70065f0953f003d0c672487529bd24ee8b4765ffa017221fed7896770224a7e5a73d66525403e905174c4c7200a0ddd2110140daec94c42e55558b5c1c3fcf7ca20e46605faa1bf4ffc77ed576855027ad817befa9101d2c29b7825ac46d1eca7a454945e729782a69b3d945b4b89cb3eff2a1ca1796ec6fcf13b107c42019f1e1455e60c9da83702786addb59973e840ee3b6775d5ad2481ff11b7fee5288b96c87f525d42ddbe36d90f0f122a1438a25d9ddb7637cff3d07ab1863ca5096ab92c9d9edb56ae5cb13c7ccc71c2ef859d5612564464385249d7be93c4d293cfc8c0e1ebf828409d2031c60ded3f735eb92b60038b1496810dfcc81df6ab56d578630e03df843709585de231d2206ed6236682a4ea482a0315fca8150885012a7e3db0a183edcffdb07297f70603dca08320409adf6042cbdee502e4dc0b5b602f076b87f16bcd5d91140850c3b8cb5706a8558cd742bd782a8ab453be8def490f6988f015f831c7c4169ede585a290b6edea38c1460d026e86206344f4bb953937534e07fac75dadc1652d2126b6bf99c0d3013c76771bf89fb64aa3b355d7bb38fb9dc65c4091be9cc1847d2c63c871ca0ded34b8bb5c9d2c8670cfdb8363e434ac31a2f2cc7f1edf5fdfc42b153d560fa246d96fa7287967ead6435a02a5128bd8511aa848238b71f2556a19ab2823dd5ceb9c89deb6b0897bdeb1f78580ec479f0be7e1e17d1411400baf9c21536cabe2fab2ea4c50e5351ed127a4afc99c0f037a2a3a88de2a511f3b48de9db3044e2cde2883c442e597f285a43c5baa14b18166403b11ca12178459406599b44c6e1cecdfd3a5d7864295ded100cd77cf695e9654e242e11ee721db8dfd581d599c3d2face220ea18fc935081afb3a5f8d97c327e905e5eae6172ae1e199b0acc880a579b904c5d738e82571b32a7412f1f21e5bf6e4f5e4d5b8fb9529a16493a37925f4e5911348c0b6f4f2b22ceed325408d881923d8933a309900b6ebad672d156b0e5f2af5247ce741b905e27a11ccbcd4e4e335796bd81cc2b089a928695f1c71159dacfcbf7b36889176dd7b697aa9370a932ab96f07c4a8f0f0f4fd69fee52ea5b9204d2224c88d83501d92e3f7dea4fb07270d8cad5864bc0470912825ed8ece071e246780b9ed8a91a4b102b88a04215640ea3ee6ae6d6791a4c5e42b9878ef5cc39e5b05a71c5613408e135950950e269266cd0c767391f7951074a1bcdb28d3c314bd2799c4d4d8456f2c1b8cbdc5eb2bf287341f0103a096be4035060b36615b8c17c08e464da07d4334d0c63f767e8517fa1fb3ae830b143e398232f55935ea77b8488f94a7829205ae8a4472ef0e6668cadf0045a4b101bbff17358d446700b4c9cb69685f0ffb46f6876c2618e9dd120d234281863c13bbe0d3fcbfc6859b84241e761a72d6acc35f8db995a220193f0ea73aedda95387aa07e921d0d76c76f69497976f35598ec5af700f70047c3a412fa5fe765e958fd30a360bb56c61ff6c7063f539968211b6c470a2a11ca1fce3dc3ac379a565ace15296ec0d534c123f79a1b88eaa02ee34e17fed967cd8e3df3b79aa54143a52be0f37c8ea27c11f31e44bb04e7d9000000000000000070a96b02000000001789e00ac17c724881cb0935ac80fdd60da5bf6577ee4d428f54723480b84725db6fc4cd0449d741a1d6eb1dcfa24272426bf6d2c477baa08fcf7bdfcd7035ea1750730470621c2275dc8ef837d824a8a4459c4b98b36acc4eae8a99aa4bd40800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c6dd41907a9519b977ecb0d944514e1df85e7ad1354d2bffdf9589fd077047c6d63c86acc5f21fcf0557a6fd52e821a22f582477d5897724131e9c98ebefe8e1fe048c6afa2c59f874a3c4ea5684c4f2a1eb87a13225c87b8d309c9931a2ea5d8063b037952ebc2c2a3e476c4f84248d8c1f3eb14b7a6d21385846afadde0b77021c66a914e6006c441a2ad4c9d2100bb93ace301e8448fb1091b832eee94f92e30229b4d7a6b71a9b3e62487d950303df088d131dacf57e82433142131e46fefc580b124060297b1a8e4dedde7bfe55257624db945835d64453755f488b6f894e3b0612b34ea0fd9de3b452f048755ccabff9f8b55b597273f50e8045b28069651fdb03172e067863134f5ad043de7246609f230e29ee756ef028f5db1b6d8600bb465c03198f63847ad26e8849cf16640e95e54a440636eda5dea78a11df60e597cfd4490208931de7172bba75e8bfc5051bff949b3a98eb8fb7be006023ac1bc231cdab6e021c366cee84b53a6c2768e9e6ec4665ed671a19c822fffccb217d01a0ebc7829703002ad327baa3d5d686b1cb68543808bc4d6c4d6bd2d3822b1eccfe4419f6bfeeb89f4d3643370140e4d045442488be7c624ce429d227fbe0b5ca1c93ce0b7a5f55699ea5442eeff95ddd03347488499d4fb4c184ff6e862e608a11278bb37e70b3be6cf5e08d6a8f3ef16b3e40812e7c6d23633c878d85a38ed94170fe746b57cf1772a77bdb886e552134028834f315482518666dd64046053301071fc6d3123a868bf3c1b391feae951b7a94410b4d7aa3a5d34997cd920dc29e4c77bcfaea13bb77573383d9863834f26f9a46754b44c4f5b61a2206d59810369ec449597921446b7c9396ee34be7a224073b4b3f9159ad8b2bbf239c4ce70832bb79bf16c6db6dad39379093f776207f6d9277a4d90917512a04213efcb266c39b6894843e297372091a8dec939b99cb1bddf4c156190d8469191db53c2bc3fbd5380a033539f07304d42a82c8d45fe9fb9cabba3a6d769d71d669bea420d77d5c7100f130b1cff07e9c39285e8b30af0f12f8f7d6e2aae10ade8f5f038d1a9393d461ef49c43de327a48375f6037f5ea74d2a9f123cc41ca805998e84afa4ffed452b0ecbeb4fb727add7e725ad2387a726eb19509f5770a1e24d68b0d6b3f8a0b9a7e9f6e7e7b13a3a44d012d77e091abf8d0c162fa2a6fcf7474ec4a4f3a0a8d30e83ff23a586369f6432067987c000a685d777a5bde335c5277280c88da582696a6fd33f984e938ffd0e6bf63474df96a94646844ec0e6a33a088735f3a0597a1f7a69f28445fe33a75d8de628387478e9407c285601d7c6e1ab618f9a960b382e8a4ee3a138ba4196a78e3e36cf3fa9bf795b0ee8fc85dea966a32bd7dafc0df2e9f3385f3c001dd3224e41bc3882fad0a696b8929f800e78a005011d05d01a8f4586fe9d597b61047c14e5a36137da3148110b942718debbea1985e3a6607d00dcb202d003591f7c1e7452742c09af9f4af835e25e584cf5e80008a7f84737a02e281f1eb1de08b7c365af265d6bbe002bada8ea225961eea1df5c46c1844057cdbab73912c8ea8651c37ac7cb4bd47b5ead6b060bf191453ff96cfea538698faef8679c22d30500d964618d43eb5b673c99323dc93b601d3e0c0a643e8b965d918441fab6900f1e52c0b8b8a07a15abf6c35a7c42c5f7724cd204b10782e7e72d31e287bd3c13efc7a2cb279de579c134fdc88f5983b1218632ce4f69cadc3198576cc0205d1cc235e0ec222ff81cc6a454ece169c3fc2da47b00528ee735390efe949f112f8103b90bc8c33ece8f113b3eeb82b97b416441dd85577c68ce7fea69f886fbb326890da0cda686d27ee9098703776edcfb22793e180947c24a574c651071f9da09f341f86cac1548bde5fd4f84978917f2a64aef8ea685abda80a979ac2789cec81f4162a6ca86dd92ef954bcd057e940f173e05c9b4b78094d1c7ec77e72d14282895013cd5aff63f4919bc9960cbdea95342f265b1d26ffec9be87dcbc1935cc75925593c0b3ca8159cdef1ba21933d6e0bb5449ea68ccebf0114fc88f8b61a10c4f92f09541a62531a8fb2f977045fa10e24a190aedfd8584da727dd7111b3a8573811bd886f71076376e375d9234eb45f901fa20e1bf6e687c231983d7ecdde9a83b93a5f89c4db24dd6f3e1956b4050b2b39595055c0e8f5e313cf0ff8b7b98d14b031a7fcff8010a2be7b8220fca108c09646219cb9d1e22233f67c7c6704cc6854030c5e17d4686fcf5541593501a51ebb3f84fbfdbe085189115b23908eb1090ec6e2fe098618ca6e989ceea83ddab020ee68aea59901b9a9295182ef29d6b085b02f48ff3e8938864c83912680e54793f8480bc3f6b8696d99f5a4b5cee071f10cae06bcfd1b2d4c0e", "00ac5251", 2, -1325687916, "d4e400985a1f96e4d085d69188ae7ba4ef94270eb88e05c0d7b627dad95f152a"], - ["8364924002c191d9b6c340d9b499d244c51c8d7df3991056d82f2d3e8fb6eb3d0ef894bdf9000000000665ac65ab0063274b06532a7988b4cac6f3d947ad25bb364e5eb669e9553f6c0075c41b47a6f92ca4704b0100000008ab5265516a536552ffffffff012e44ea040000000004515353ac00000000020000000000000000f4665e01000000005752eba3f2655435ba29e8ec0a0df735bd219ef11090becd0a05546d2ee40f3052625de2cb4c3e7a0312a56828f4377b90575207bf19c4a51ca9929978e301b950004013df107d38d3e4a968649f834cdaa47e99835af81d34d667030e43c8110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044eff4c8b97816576e02a0acde96d02dc58507fd7a5afb846f1280e680fe76d146fef0415050901ad17561065c121f4ba63701062b28614f3653aeeafb2e712dd7c7260a49e2b5aea0e6fc0de0f4c0f953948af5ff874b21c835f0e8c91fda7926ca20a6276bba49fcfe9e1124f98335481b14096c6ca5af277616c6dcc6c50a03034b738ba95bb74dbd5bcc8a087ae4267eb192605963dbce80a2cb7636d9887f0209cc16e6292419a209f41aa27e470e322fc23f2e75d89f70177c6dde43fe90f70a29a9eb997ee02396003e6471f28ebf3e7f551e33baaf27d368aac907468827a916b716304dd8e488978ac5a821ba0cb57599065574ddebfd94326c80f5c70863031a3e3f7a51684a6acd4e346ac2385532f67640a32e1324991e33456bd02ca79102054f4eb3e5b67d6c26c3f68f85188dff77ba7f261cc5966a401484444713488d031427c9ccf20cf7aed8abd58a24aa0eb9b2085aefa480a214ec657929417f046d030b03293a5bedcb43e3f8de71bd8231f55a58792a3e420e1f488f5e68611a5b6d02189d8a0c4aade43263a89710ca7252faf718c29f0e60750e02c91ea0c7c3800f0fc3b66f486c74d88de1a1ce973ee325f91bb9daf4da05f0c93c8933c07116c0c338eaff94e188d6ebb454576db0fdbf31d2655bf9f6e5575e74dc50f65673c07e43b8c695f32357de38ba642a123c65474d9158be65d6645e19b67028d96296eea5a3f8041e4627cbdd4caf022285a3a3eca289ac8a311b120e6a6fcb8e17658ac2610341e94ce28705fcc32606fbfb5bbce7b6c5d4a3ec84b58899a9d32a83fb1a1c8f55da393e39142d5c2c4fbab8a062f996bad6f029ea6bcafed5a4f72a2253f507987e5f3547ccf91a7dec0bc5f0c72aaeeffa374164177245bb853d71848e372b4ad0a0dba1b808cf014eb87ff1e648e6622fb79249dc42732ea92a083757841772e7e966782d9bf7e72593c8610082354fed14fdc9e9ba2d74b05532729650be0ffbe8904326f8986d823571a541730c96778fc163d3e376390ebf3882613fc49b7214735ac4be521a7c8d985e4b154ea13c758421b611306438dbbb5dbd39acf449aeabc989396fd9b2314384b60bfac65fdee415790851a8117be1bd69690ef24a210b2d40bc54ff45800f44a97a4603365e00903355d0fb5fce83cde57e8573e2665550516663aaf4c93f54a0924df35398e88f8fcaaba0f1d24562666e76e53daec6156555a4adeb915383ec3f2a214c586a8fa318c3f6f116aa6de8843b35302a5fc3db45c97a6e08088f56661a9d6f924c1400b8310da9e1bd5bbd131acf0f36f329470323961898b939b16cef8127dd4177eca42414150e220dae434b1e492960cf5388b3c57969dd743f7380ac7c5d6c15ac5299015aeca72f6a096090e5a6b07e492990d0b12246f8fee9ef8989e3a746fbc7a4314ff7d80df1f2cb988d307f15518c38dd1ce44d0dca9e6068be725c22db0d4430bcf34aa1f859f1c1f5163d335114f7f85afc3ca7da596b6b3d37d7e32a69696651c91b7db46ee1a02278a0231294b5e276b9c52ef85ca675fd063e11e2046811df7a1218da87499d02fedcbd1ae6303705345db19e5de25c8d6b841e6c6a9a8962527613bc626adc81fe203239ffc75ad066cf132c07a86f1b43ec7d846e039ef142680554e795be3e8ca14322130010114cd346e09d0ed57ae4bb3db9c6fb979668bb7295a08220859f44c49cde45aa6fe0131ab3cf6284e0ba905d6f0291b400f874240c728b57957e6f5230fc9e3e0f0e0d58a44b4701205d0d42f1de846bf3ced4b89fcf5b2120caf1a12ef079052f2a59a4a964a140d7af5fc7890608e196c946d6917563167b5bdbc5fd339f74529259e8775ba0025289fd3acebe3d85e00b941abafde025172c44d373d16c2d8205727faef0e774e9c686edf09081c1878443cb115e5117b96ba4147f7814c5174f43ea72d29c32330950091ba266a64bf48d3bb6a020110c24df616b451a3b5ff580b2c81c58b827dd479cb871308103d0b1b1b38449c56f51cccb5df135b34506606d6b135a461a2fa2d1db34534390d66d296bbd3336208e09d79217922bb7073a05454bbc4e0e06b8c92a58a549b641a3c1bf1db611ecce9f2f220ae79d4089a18b91de107288ca04a5e33c9f11a66212908ef9c2f11d2e4a650f2f7ed73fcd2d66f0b4bdd86b1a927c8a74d569fa4bec9363a3c7c8f13722f74eb750230614127d3c23d125eccf341282f0bb30f451cd761d7dc489e04667daeb7438b56f775484f12866f303000000000000000000000000d82d5c3ec7803c510a36ce55d9d907cfbd81faf9189ddd707d7c52282f169c30711b6f72101108e978a102d354880a367e213af8b4fe136b0ec99ce28aa1a550057340af5b2a29a09775a2970822323347d1b7337c46b6e55d795e347e4e41e5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003c30f32e1e270cf4047d9bb9905a2e84515d07d739cce97f85b6b65ee9ae31f148ce6e1c4c58f25cb21d98290ddcea3e393c94fbf5cc33105cb8a99e50b2e74f6e1f39d0c3300d0a4ed1ff82e616e249208bc33f15411e1aab030af3b138cc23df790aed23bc80d882f61a04f5f4b25c8ddc4a4027e8d0f1be71395aab0efc4d021808f76a74e2b19f216712b9b6c9c946bb14b379604317dae42261128ac54d0b021fee390bc2e3286c70e2e5b09193ea3821f5e50e6665f4739dff44c965e480810a034f161b236613e4bf6916f8dcf1661f2cb1fee2c5535db15b6d6ece2e54f10b22211a0ff4df31b578344265a2229defba5ef90737d0b69cb1d355c13451f366032735be17d3da6f3b9ac6c0b8b4f7e3f8175aff143bcef1560a5b20ede31a6c15022c7e17ed133ae9d66b62ab1f2edd6f2178870ad4c55f9d4ab90ac22d5169ecbd031bcdf26f3f7c49f0b560795495870b333d0e9777f5c5f110df5a718c5816c485032a26389b7b8f51931998e43222adaac3027f90c06825ee055ba393716dfe273803053a26c418a442dfe8f0fabe558c9bf8b7dbd132db39813f65398ba9dc34f940a87ed688cfee15762cc15c30936e35f239a943d04820e2fdb17645b013232faafc1968be47a5d071a61ad4db05978f2b4a8061e18774c66cf90d3dd142a4b1b9d5c0007fc9146de1227cbbb0ead8a1fc0fa13872886fe87cf250a395f262586cdb4d4e69c832dd83af991dc0bb3c97c7c660909811d65327f4c49a802e6351cae058f14871bae3971f814bd3461b2927130c41338a9c13eb1dc32f91697b89dc73fbda258a692e5c3097072347ce4356da28b93a1a8ab35971763646f91ed94a5f0df3868bbc314eaf731d577b20323c6eb1f3be009e0c4988149246c124fb35e06938e32341d9809dfed033a9063e9ca54b630359f7db412e1c81cfeb4c1dc585e51fc9ed14e6f385e948db70f796f6798ad6aed17e330587c3c69e1d1c7b70d3505a2446ea42634a69bbc7623ffe6a83bdb3d2d6fa66a3abe3eca4fee9845dd04611a2e44af229e8b8e9f704941ac63f14b26d499aa478dbc17ea676c1138f911456f635894242b3c5156f901612b9ecb78b2fa279ac2f750a29e74a993ba0d02e35cfbcd02292d1fffe6ce335b4c49c31e1d7773f67e62ceb2b120896a1c102b4e5d23fd414aa33ba357d05b9c27b4d4a634b051317f6f6d6515711ba67636a7efea5b0f744e96e5c778eb45ca0537b7fd385cfbb64dd0f77ddf1b1045b1d0c523cdbe375705e957ce54ee89c8e0ab0d81208ccc515246951e72ff9492dd4cfa4aece981391ab6dee37676db059f7c87ea49da9f38be9772ce3ed026449c520579232822e170b8e98dbd71fc286f9d34e51c8a2e6106064ccf2a5863ee9f13aa1a2e1a6f392c798073f7538146b4b6ed18f5b61054fdc6cf928ea744a8c47c464290c880fed4bc73ca37a9075fdeb66e512377947cbefec53eebb7772e06bc502112f4528e52f52e7695e22a2eb26b0390f5ccbc5effcb9a33c51fbfa3b23a5a4be4565908688203138f9a6c6612372d4bd015b5aa8e622d2d780f1f70ab81fc89404582a634869d9684a38a50934df952c3aa8fb592a12266bf5fea504c28c728440f8fd587e8984d872f4fad4cd674263e2e0bfd42f6642ac69ee7740fc50a6a80c8fd398e4c5054261b91ec001edcc8d2c41dfb0152cd17ec2971ca6faff0004d29fb6ef2dd4635b8612111572d7016c90d9ad30f23b0ba6b53bde4d770a7947b5f8c1a8204af7a08f2a03101ce96bc4f87415b672ee6608528a9a67d963ca2f287fb52e6938b61fa30681017aff69519d5a9bbf3388f963a1bb68b66165ea6291f25d6f5aae9e8edbb58d3c9b9f6fdc5b2b3b43b8c44fae519b8ba6fc246780ad3295af5867a1a472bddb88279b4ee7d664d00bbf367e849df3f68b6811592e0a686cb511dea34a9d140fc2cd048219c60cdef64d9d6003a6866069388fa657087282e962cdbd44fe3d1e4df6484b34cc6a8fe27e895f371388b3a617c4f1fffbe5586bc46ccfc2ca38f81b23c62a536cb6e36300871d78fc78b1ecdf66ce2a195ec39f45fd904d5a5780f36fffeb2a7e65cbd50ff9fa261edb9ceafaf55fd035ab6e248f3b408ac8abce851524d5a6ae59f46109b46047ffe15be60165763aaad5cfb435a45331a09fc09ae873f53732fb1a46e53952bbf77d839f9e4d41e88587eef887759ff903815dfdf8e7120d32ffcbad66cb1446a97bcfc63c1a7785cbadf543cd83101d0dd885021a14d1fcb2a36bad0515e166e8cedfaa4624b67aaa133f1676db9e85dc674a59878913b8b9b9363f6dc3c14afdaef801b15c8ebc3c8cda47dffc2b85a1b05cb2bbb15d02caebd9b100bb8e12b2d8480d5d4cf5f90ce14015194073055e86d94a678b09", "65ac636a006553", 1, 111584454, "ae9a1f4c33e051d22ee01be50a0fb5052d7b6f37b9210faa29eff238a0d6db3e"], - ["b6624c1601ffa40b37c26b187a5e286188219041d35d9396f6f659980f7b601f7786f4aba2020000000552abacac519b3d01290104a07e0100000000085363525252ac52530a856f5c020000000000000000be9b2a0100000000588796a289150046530d442dc20b0fbcf38328d59a54cc33274815fe0e0abdf6fb25991d054f6076f85b973f8dd0db187b7bccc298e35cd7cf1e26e3bba3273cd4b6872e89943a1c17e9666efc3f5dca740a31cd9784d731f134fb87973309030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000088aac9e3adffe00f83d962f368518664309abcd6eae62eab46f1943008820de9202660da039398fabbffc693c866ff56b1e4dfff741f8644f0df435564a3ec0972d36d6e9cc1fd3ee8c6445ac713fe1ce95f6c69b4277a4e60caf499fb39077cd5d93850be1b258daba6c1d44f13638247f5e7e189c8b287fc4d322168d96414022db15787d1ee437d0b97dd936624a1505522e79ee55b19558c1985132b4e8e3e0209e524010bbb926f9ebb179bf2ec4527b04d95d3a156bd0d18e88daa1b7976220a229951ee4ca880777b81320e65f270b108b0735e207b6bc0cca8ee435bd104ec2ef7c15acdd7f54b6cc3d4b0de941aa17f9c7bb03419f844a0ab64d7cf2b2d2f02115c4ae210216b041f21680aa978df5ad59b42fe1a66d2c09a1f16fb09e7bf690308a4a34c9ce18c98278e37fff10d81e3a5df30b963d2aa35e6c2c7b8e2ef9af803046292fa7ea38dfabf0dda3b7d570642efb06e1c0ac07b89e875a375a14e15160327eb0da766f05661ead0d02d38204d4b5de3b8db122ed14a9aa3f6f651f73da2020080d7196a585bce20e8b2bd4c898c0083f5cfd9eb09d2a39d1a87f88beb49444839f5f9dc9944d90ece91940f9a01bcf906671bf9d365fdb141d42c13b0d1cf0cd35a7cb8183c876c3ff9afe3d17e6e22d408e640a544480c40c31cdfcf065d9ece11f27f6ed9977e09c5ecbfeb121295f9262ce25fc733bf80271452451c455b718e523a362271aa9da451292fcbc8d4238b1c4b7a3e00c0b0c0c4379f9776eae04b2f78347c38b4ab4a7e6a1bd40d41c6cff3bdf4bdc74899dcfa7d10d017479e7d60c5feb0e0b6b4cf2f54973e2cc9a1d6ce8a066f8140c701efb82f2fe9079ff8930e3c424a69f4bd2705ccc09cf27090a978e0e319d5c8b91f2c68f1dff268fdd37d6a94ea8f40595d363a08c0a3a627905b0cf0e8b61134685e48fa966edda8ed7f54ff0b2bbaca160eb69a13d011c77624b56f252c9518326e7cc8befaf1b539d8617cdd43be2e1ad59169fc9f64b2675c6d5701cb777fbd9a4b090012d044acbbc0337092e6f780fbe28240648a42ef000f8d497f4fe1a8ef2f14e05f162d3f7081480338afab0d387a3b7ae7beffc7c817ddd024404f248a53c2364bd2c46b7115f117266dc488f903c0f04d8323106e6d5e9b6c372a2548fca8b03257d0191fc5450f0e9271ff4ff079a502b83f590fa8000ef37d20266fadd4e541489c695cebfb79a0d9bc9818119851daf8b6a9a02e6f88fbe9480a25d4c90245dda897ece4fde86e52e8670b64e06d00d8753d414ef015f39cbd425ae8b99786348cf77dcb2a566f9b1e7e2f6c623a42642afcc151869460dffd646130a0cad96a16ffe93d5ee7c4c0c4b3192c129600048f95fd0973ce777b8cf06dfb69e515aa52ee5b7daf8c7d4f98ed071d05f4537cf9ae15b93d304357ca401139abb7be260cec3d9f47803e7479a3cdf51826754de5289a0b917865af3c76825c1c055173cbf22f4d85f4dbcf4c56b8c34808820bf51f990ea0b00eace7dd88c39e4ea865fc4b59c15b45b71676b24c2fe8fb9f39eab1c016cba32825c80a0d621360aef80390ebcb2b35cfe1acca7bf662272c7bc50a02c55eadbd905f3f574179744b41fd549268d8efb6e6848b96e966efb991cf42eb1f6f127134ce92834a74c5a727ee1525d7ed70a051fafd038833349ae33669a767690e43676060adac5f8d424b91107723ee65cd95c425d15305c8d08bb3ac44e061dc281280f9fdb65281362a77e7dbb8ecfde405e34353b16aad79695476e672396825629e8bf49728daa932f00500accd0c25ca9bed7a6b3e9e052f5be5e73aae76465e3cb4d555719b5859c75b79bfb2a88c820cfb1ff095fd7a8022656c064622e4060371654cffa87413b88c1e7f633ea1802e73cde0afeddf7fc6a9e84aa8c47388de145462880edc5e87e9d7812d9021dbd570f0a6c690b44f6b0b70a76451cda140b83b26aef88deac4042fa04110895305eb676e345b88ede86ecf892574e3670101ab7e5487385c29a587fc88c5a95e9083c9bc17aa18ba1487ca761c13a179014eae0b624eea20a325f783f34b426aa31a0da3537986703481926db0ce1993dd3fba04e33db9589a898d58393ab43980d744a03bbc6f6fd4c11e6a5c411d4f454b8259a2aa893cc054a94cd764eb03e5146af54d52ae6ce6eac77953b08100ede643b4025fabad50e16aa8ecd7e20239cc4456a5ab54bc480dc604185ebc560409f61b1820977a7f6d0f865bcf487f8a1a5e152f0da2c2e6ce16010000000000000000000000004abba0baacb8b7c49c6087e5a887027d8dbb85bbc46bfd22cade43dfcc65cba0d610756c48c9d52ab71e29098844893be4facd7517bc40ce05b2244a84e12516c7fe09517906d1571cde877502c7a7fdb38e278009918543e1876d3212b54a93000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001146af6a917891ca2c7018b9236ad169872af0203fc2ea6c66b9b5abb8bd3a0b33d971957cabb53ae689d591dcb8867a6f6348c3910f3bba59084f7088b172271588d2ad4fe8f85ae87adbaeb0d4465c4fa7db029571576a9edb9732b287e9e922e7a120049f5551bdbfa4bd34961b405ce21752b9606429dde74d4d037c798503171ca25d475960ede1afb64546c2ca529f014d3b55ba878376469d0b5d175747022b35ec345213c3af0de572aca87d3a71bc65c2fb4d8ef0cba974460b83c538ea0b2a1f805efac1a16ae7697f447c69e9a7b5d9ce60fd40ef911602cf208c967f262cd36bfd097efde9aa900645fd755a4851007ac1545af146a3dd5b5ca7e3dfe903241ca21f76fe8afe7c15e9c24842b929ed6ef4c521d42f95af0495d2787e6345030d32e33f3917628151715c782cd7ea20801999993fd7eaadc09dc9c7a7271420022e10a87e3cba1a409c7aaec9f359d7941ac30f6e9ea654b67e971f2707eb57a0021fc559af64631556df198ff4c869c361f701d2c10784dfef3ce94032ff50c474032d4c709b0990e9fe55b55ac7ef284c71df585cd8f8a3499da32ac077213d6bebcd4b6d4759b7560e3dfda34377c6606a7bcc0dc838504474f2c688cb61bf17b3597753652a591453ec6be31d0b1e22fbc86e7c746f1c25be123433322174174f375a6c622e6e959edf54cf2ab82b48bb37eccdf9ea88ba885abcd836b95f290c1df17b6eda944e340ea4b313fdb67173f52cc1bb13639e1fa282102fcade3ef78fe39baf962f3f5bdc658c6b5d48ae718a88e4a97b870f8f5a5f63818c547d93b03cbae776e4ad00b12a10580c66156a1e4fd070ebb2f4cd9f75d4bedc5384b7412aaa92df6b93445dcc57d458df8d739788d124d7008296c40c044736709faa6137c878518f6725afcf76c53e35ea78b168d6034bc57e416d0bc9749d02fa111d65fbeb182860efca14a8736be5afd343ba7a905e01d177d6a055704cdea64f21942035a318082780f5bd4ba6c54f788358fcec8c5ee42b71bc04b64bb357762a9bfffe180251f409c67845b78e5f928d5e51e1787a5f7240bfe932dc279209bc67ba2273f16e04f95b51f5373ceafb88046856c9739952a315cc170c4648c838728ff0059d46d57aa951d99fb151ee5c5ddee1c9aa4789f00b647d4c3b3dccc85d6259940f67b31b7b3ed25142528063a2bef5ccfffd3421a1e9feea148982d7ad73166b67d44150a9a35fbe419597b9e4951e913cc6869bea3667688a94a637995675fb66b393ffc95cb3e18fcf4e875da1f3009f495d9a2c9ccf7bc3cc7556d804296892abf0698360b4c6846edf7d6d9c004d0cbaba63af7365d30db859589d6c406ac8ce96985ce742f8049d5055d8eabe302ee54dec7fcfe63e83cf11f05212138c62e5165ea32a7d48cb99069ab0f547c428e1bb81222c1ec3078269aa6b807168ef873f1e25e32675819c1fc0dc4e419c5482a23f9703b7d785cf3d276bb7732254859c2d248e9efbf182fa2929a31419cbcc4cba82d453adde408c219d19b2dd0c3367c6ebffe435ccc22caf363f436eefd080fcfed054aca28f6cf1e94e53812911eb83743546e91bd70dd65ab8b3505342390577e0a34a794853b330563f6b42d22156e0b5f2645d1ed9bfb9e5f62013cfa200d4fd3fa01b5628324364c8e14cf4818142b3d08025a39c8863a199fe20175d68efa2a88c9b04e2028fb38053c4fe6ae818a38314c4aabae53ee2cfe1902ba37ceb453c66d5b22401ad4c550cb8f690820adeb8d3add5a9e4efcb70e876a8994f9c3dab4ad6263ce86ce18bc8be62dbaea3b5c8c45f4819cd2c838211a5dc85fb5663cd985acbf0a4ce3a7f891aaae8189d46838dfcced0da0e05535f47eca2ceda9b31451cd8b2ec570f82d99c33d93ea5695dddae1b264d13c25885fc5847799481bfabafb4dc01a0c7a3d53f282c2073c22d8c48a1eb00ce0ca9e1fe09e2a2fdd0359af7159e85b70ddcbfff678977cbfacae6133173ba1231f38b1c77fda68f3f1422f444779dec35d1d6ff8cd7fc62125ad4d138810c23e4204b2ff43f90ed93a3557548758930b1bcb0e1a392a15bb2b03dec454d6a3683e06afb2e01ed3bb890ec3d588c44e9dd1f08a2046e1edb4f4d78308d792669cabbb3db8b5d39f38dbabee44ca504644add7568a5e59dfac7cf9464ad90c16efd6242225b7e29347aefdcee2ea2c68ac90573f755aa7660e30a7a1b32ed3da8028d3419bb08d774a4393cede2214457595b665f0befe9cfeca2a2615e22e05b4fbacc1ff173defafb1a340bf0fa133eb7d9388037949533c3aab1b5ed114237d65efec3035f294994a7455607a5c57ae5750c05850f9c90159975a60ea92acb1cd62beb004dc083cbe06a0cc95d8757484e5673c1cff40c0cdd53ae62d9d206", "525151", 0, -813588375, "847d8d58e3289c8d346cfa5b1f9214b1e5462fc79586b985c829fa2a982b35d5"], - ["8eea8d72049e59534741a8321bfa9bd2f69a421b45ac9ed8af37e5e1716e28631c3afa1b260200000005655265ab63ffffffff765c02ee0e8e027e0d3ecc575b23653585e11a7c54214cd412441becca5aa3c20300000005526563ac65ffffffffc67b50afb5ad3675f927861cbf20764cda9dfdd9997ef1e5355e27b71da240d000000000016ad682af07043e6ad517701a6ea8a6d5ff856094e60bbf6ae64e7340b4ecb40eccfb67c68d000000000400006353ffffffff03bbbc530200000000036551ac5f5185030000000000c4a0c900000000000153a99c51e0010000000000000000a1e1820300000000b934df8968a9cfdaf14efe4cc4ffd70c1a93234d2ca92a2010245e448bad4eb1c1549eb1d25392c5f3826521fa9b8ca4064875e63ec4dbc8751bb7cb42c9e0f749253167de42946c5f956cb2a4b296c008724f8b8619a7fa2f4c93bd152b10ba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075d70107d3ada5248c6f0be85d43d5a79e05205fb97a3ca55179972f94bb5dd98d2cc3a6c556f36ecec7dc29c00cda0b10cab3fcaa820831dc177f2b5307f7e3979d39e1e3e0952dfd0093b64b321b31ef4e6b0a3c22e3f7bbee68edf99907f03644fd42d5b8e02fa61de87c6ecce6496bbbe35022fbe31c7674c71689c1ddb50308785b344295f6c442adf5f71065fb5ac110d496c9d81208b755bf6c7400e2f90210144353fcbaf16dcac5ff8ef491757b9a9c4eb7121ad887c00fc9ae1cd7123c0b2233e019405d69739ed2f7a7c48cd933af4ed203a37125f7f2bd289f98cbb5a82edc474fe5fbcdfc6e2a26d8afc11ace1c2f45d6c9d08905b68b8cea338d3356020491864efd4c5e5cc22a48ab37e49941ecdc8660e106114ffbab7010044a715c02033520ac0d8faa3a7bce7d1837602d702e3efa25710504231efec39a502608150225726e64de2875f24dabbef59faa35944e5ef9e1568684d1d315635811095b8f021556f4fcf4e26a28cf45e0f93c6b6ee97268414bfa29f7aa1570b443faa2c51f022f900b4c4de9c473e09a72f37531dd1b0212d74e5631e7e2019750c8c3464420ea5472f4b0b97477ec536139120abe17be36c40c671c17a6b28a163d7d5e3a27f4230cf5e949b83f43a679bb483bd9280b324ae0785847ebcab6c464087f35c9b1d7ff9e5a5a283afd619f0db8ae5b983ce3f732efe2b0346be28d9276c2b94776e5159624687ca7f82f85fe4cb78ace38b68c40666fe8c0377744784226fb904c99afddec25fed2dfee076b580c9d459c7c164104ed2a810e3fcbc602360228ce08a1ce885ad533b0087c9f3d04306b5097c3a6e4915ea02254374662fe3e60ccb120c5e343328368e5c606c8d17b3463b4673ef52a9f4312fa926e68d420bf0a0b4454920047b65f0dcc7d7a2669e2c09117750000ac5cb02d107da7e3bfed0e0b4dccf05e2df7c96116b89fec935764aad376dc90ced94dadb68c8d7c80293a4faca175361be133ce66a9ced4f4dd04ac6379b5e7e46b76f0a6df05a9c6f7035bc27c431eb96194948416aabdebbf73d64ba1b0109795d4ede7853567a4d3ef150bb0ce220f6d05027977e99609aa28dced791571ad26f9dac3bfde9b968dc52ae50f2f807a1ac2a3ba365a4abbffd502ff383cc50989568df800c6c3cde192534d496198aae3ae82cbc0f370a9e1cc3162f8be205a7472ed322bc83a0516d5ed16c022b1127d48e18eba075100d74bac299016c998892ac3999b1de7fe733c846cb72969a67e53e482b4dfae41c8ece706179a806b0756bebd55e30287f24809b5d0f586399bffb3559b473a69417b95e55348d3ebc63cf6d8296bfb5147967f037a9d47b366455a684fd87d4ed070289379891c2557bdb06813d46f36bc47e67e99d86a7cd400e7719b9c6d7678f14ff9365ba6d9072e783636d9466ed9a37ea88ac5d3b7299384873468f1091792dc3cb29797e991ff5749f33781ae357e4fcd2e489eeba23f9f7b4b1d5fbd0fc7f557359d03366f60425f8875c82e914acdb6c65afb223a2889c365d3f5135abcef68270757f45d32ba88528695d04340f6d30ff168c2c230bb046918fbd0693dfb389a4ba0ec75872e4bb343d7e1b70883f02caf22019e917c55dd405558bc1cf32c90d5d9b463a9aac47fa5aeee1bbae8f137fa70b6bf33c79871fbda688e9b341a70f9a5be58cbe983eb281302a400b5c34b586b33a92497dae5b1e7b1c31a321b10461de66d1d3400bc0d6ceef7b4a5cbeb71abadc20830c1af6e6eeb9eb0469f67142b1bbb389e29df79b8b1d2f0dc002cc9942b26f5034c3313bafce0af580934c15eca30375d29c01c5267564803dbec9823c87fd518d3f485c315dfc43d2ac5674fa567d9da16b09ec500c8ef31a03d711a57b83bfecc6ed4ce8b4cd752703d88b06950a4cd7d83bd51e59d4702e507d2b7f662c4b22451f867c6a4c3a109db1b9feabcde13cf70194737a74e6974a83e36fece984ed0760b36d3a04d531f23badb9d67565ab029e2d68d015113fb4892772c72abcbd9c5f7f0679b9222aab2bd0f36b2b13490b2328efb5cdf0794cd2eed10036066e124074d7755962372fa0a6921be6b04177efef9353808c66c12d1232f99209f2aaf8f2cf37b5227bc4192a19cdb64224d6e1488f631f07a5cf4181392451083d2f6314db41759492230e2ce1c5243f352299790af3fb991afab7cc5b3a8a7d655809febaa60a6a91218bc3e43148e378a20f4df76a4c3ed5955bc79492c47ea3d8a845083b5a6902bc64d208fa5c0b791aac48f8fe4fb4a34135428d606b56e507f60acc01771cec5710d9b4ad328fa3cc9bca3fab916b062db0b71df6e146119b2ede19415757feed9322cb1650e8120b8baab0927e5ec6202c260d84a4d5b21eb34c36c9d4e07", "6a65525253", 1, -1958060010, "f35691a0c38a23dbb55f17be00e5f307b330610d1add51087ee979b58bb3b5ac"], - ["12d2440b034f63c818c24fcd55b113dd79864f13c17ea994d44906dc199d8a3462604299f10000000002ab6321e7a38c1193cf34ec2077abf72b4496ed1625f74e1e8c74f1a08fe1775107afc11a668903000000004b8816ce1584ea979330d35ace064b911117a3eb3476be1efa4b684b19f7372aa6ab1ab10000000000b0cf69d603375d670100000000016a380f89040000000005acab53ab5271f0720100000000009fc9085103502d79040000000000000000000000001102b0d1a8ade113c144c622ed5cdbb77ea1565934d8a64ac1ed82ca120916c1e18f28f6b8c507ed0f969c330a398e9eae66b7d714def8ac71e633a85ed2a736a96b22a15a0b9ae5373144f6fcb28d48237eb6435301e816c6a51a6c596e092400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b8f3f214d33998341fba65d5c8cb6672aa4aeac87208b5da66c489a3797eb0523960c6132a74602ac22d9f2f4136c15c86f14b7f15f910b9f1f0ceb2f059063fab9e33d7c96e148957d9feb99324412f0ce394f7e6b17a8caab45884811941c1b5cc88b02107e18ca0e561da8130f23feed8662ab13b726c5ebd6887d90af0c402105ddf670093bbe501962e38669b5f4d5298bb9c17c96958b2217c811a7adb72021cb94509ecac16678258b12dece67b4a1819ff543b14ef9b6d9d05b38876a0320b1413e508b84e14c2f05eefc7aaea1ba72d627221e91161293307b4bdfc8cbf85150b77e4d3d04e04ccb5c539b4991257bf32af607009103b8ea6cd8608750c580315ee3cefef4e9f9fa2d3c4d1c8954fb9bf47d6c1ad4a4453ab761f80ea05dbc70202d843596fcead7c2f403cf596aa80f97ce3d08b29ce62aaf1bace6188ee637e0326a8535ac525324dbbabf6b20823ade96af50e4e59a8ab1bd600b430d4bbdff4031845dd0485c0e069e7f9bb966e7a5bd75f786bc3dbb3be6f6838116cac49c1340323c974bdeb17de6a9f352ec1f5f9a6daaea78afc833265a2c7c051dc2fb01a95e475517d6c2564c2209ca55814b56f92f27deac6f8b065441a6506ed180a49062299469097e0b6d70676f55829c381dadb8e805991971136abf1c166fef605b62af87d2c043f6ba1847523f074fe4100f8b2ae14210030e7b6a613130732841d71300762e2c373577e211b6976ab795eb44846948d445f38ae1cc9ac9abfb7e16db668418b0b10a377d8a46e0f3bd4b60d393bf6e94bd489985f4f92d0776b8256b0c6ad59c66c5784ddd0aa0c456d530159601d8d4bc935a77dc6f83ce44dc91f2a25f35d6f2a72f64a36938d34c0523ad61f515e769c0081e0249adaf16f28cfd47642d28121e583b96c50efa3dfdd5be3d7ab9b13c3bd2b88fe8a2d9e7c061e733230b6ab43c38e04ef4cd9a816bbcb71c88ae670e5e348341a139c4628b617009093cbb866a6859a1591f8706b9132bd2074d6972f97e34c42092d9665bbe37953e0ce7d66e326092ea73b7f4dd222662c868236b55bc08e0c36a713af44765494d9fcea405893a74856e1ac2173eae49eacccac4bb56a82f9c22af680171b88f033e9770555e47c556a4067ca16a318c50f6e1fca7568cacb3eb065267b245c82e9b56029efb947a3a060649173b5f62b79c8fab211466ebdfa9af122975d5c7730f14ef2a4e42cd547dccfd7d42f42c376086d48502cbc090dae72792c92237cd595eb0ebafeb3b0860de7f9e2b4be33dd7d2a27c52ed33945362ab8f902c4fdbf19f3fcf258550e9dba2ca440c4a023b8478b8aa8863511ef0e5e58a0e86ebe3cd0cf8d866695db4380a490c4098050d0c7a34af6c2e74353bc8c7e3db399941610261af82f2ec5ce6365a8d432cbf145a4e819f25243484df800c9964279a66af35c713f7d5ffcdf7f5b11cab683e6f33b2d37299549349e748ec6c09788c001c4aaca9ee710901803773d80c0ee4e5a15d469e5bc356a63c8d5e0e188e58ab227e4d93c23da0e2734ba74f754442608714eaa366e2dfcb2a963d8a52391daf03384d088778deb20d0913a75513912cbced18705247bf95bc94d5d34d3bb4c5604b4c9a0905c95a5d20c94d101b8f6bef58141a447fe6b5898b4f204cc546f6e0363ac0bd144f520b287d379a884f0f44031e799dcc7f32da75d5c8381ffc4a44984e05b505c00bbe93519947f0f08e62fd59961b01eef40f06404ce65afdeaec8e6af6bb69cc6d5f00af919265710210482115408233822d35ac23548656f50a579f61469269010db3f0bb93d59f7d278080331a73d8d307fa0443d240b242c776eb8e067a3fc2d65a3d8e7fb67e29907c9731c051173f98ca864ea5c4980ac8e892d33d8f4824eca438f3232022dcc56419b616d58d595c8817305a6d1d62604a94c4ad9b1b920930038517244ebd8fb9e4ec89a39594eb9e81854d6a00c87f45ad6774c379dc9791abe800cd131516534c3e934fd9ab385215c3be0ddbc5d1fe599dc0d67f1458bf68e3bfd66769f1a19bf6c1ba7c7c6d3772a8db86e49b081ee904863f2305b0e8fac9199ee1951d33fe566e0ecc3f95fd05aadd1877732ec2b6aef5993fa63442accce120a0dab8abe42233dbade996b5ad8fcf61904481152bfeb6c176e04e98482dfac4852868b78f8fbc1acc50828acb6eab3953494a4d13d6822069fc0aae35ae807d7182843bdcb53387a4729a697230e4428ff76c510878fa3d8ada083685d5263b76b7fe20200000000000000000000000064c3e2d1a57f4a249517d3f63e3bbde8d7836221674ca7f804e6b95ad11a8205f943c7570d79f91935927a8901020169e2a14e77b5ccf5329ca5c0e7430ebdbdc7117573f9c7647310d179b0ab602a4c3c13c0aab3be8ccc4ccf660c5ff06b0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de9c2255d86257a3ec0e6b8a3bdc2543b05dba0bdec6ee970859cef548065ac2e3fb0837afaa667faaecbb09bdb86caffc3810f35cbc0bd6b6080768e6d793a1f1f4b7a6bad71a15d1e4ef792a550dc224707c442ac405310d5d5938df14ab954808607791187dd2995fbd47ba9cea0af80421ee25f22aea977e334b56105f660309f9d206d975bf7ec4ab625cc4adbdacb57022583aad30ba26644b7579642de30311c976d9ff8835cf301314b25cb9b318910e7d28e756723e342c423403d3a88c0b0cb087a3cced3a5d8c3b389c6e29b56d1dce22d24f2615405994d28b3244e5432032ef7eabb0fcdb6fe0c1202b9134b27ce4ae9c0c50abe2c4075168c64fc63103235972c4da78a4191cf392a43f3878becc7a751bbbe597148dbadd5b09fdf9fc021c45cd567ab38653e73d6518f79d275b398f01229337afcd99f570860bfdae4f021c36d71b6bd22cecfe4ac9f1112d7e68a287f0616d3aa450c622cc72526756e6022e9bb81e3d37342d7b0125fa40871f65c5c6754c64d46a9ccb54f742de292cf6032454879bb97415bc7085112e26211c4ba639b1af4f0e00bff4826cebe32dadf4dbabb444d80bdb466fcb7b2fb4d5af08642d0005eeeaad27803d60d8b771b93d6ee5515877733ef0c0fd9a1d42408547fb9b0803f577a73d955b061451f6a65fbcb8f01be6a57a55f788f0bb186b3f4727c7b366adb948e26e3af869cf42f3b364516d7195310fca888aec8b88d20215306064eb85b544304641b6fb1b3a3dbbdcb6a82fd6461b657ff708aac43f546669708033c1601caf07801b92cd85caf2e7e8010846a1038e35330fc14185bab513e23f6181c8615c992f32be487151250fd4c8331c0172dd68102fc03bcd9db7fb062b5c647947261a9445d1e15b6c5cf3eb4b269566ebcaa7924f071be92e048be9211d3756cb043427427456d37071fcc673ddf5614aa927cdbb14d25bc227e22250770d7a1fe3f255c289090287528b9a5d90e6f862a819ffe2b30fbb9c24d450c54062e4c11abdb3163a7f8317b41f5089100c24abab31f16607707b6f0777590d75284931fba79c1545e00c1094dd2983288bd49360685fb07b8c6f11afa77970371ccd73ec503f569b13131c1f63894581785ef2859dea25d941e3844152ecf2bc3a45dcce809206248c383d4cc502e329217d8d132f788112e38d4e8973c04d7a2dc188df142594624e0abf13f3d226ff3699633d302bf0b7fbb0bd94e15468deb8ddbbfce948d792ba5cbd2a84be5b6da55caaade4b00663666bd9afb475c68eab4cf32d57e493667e6ef9b95a681a54313f496d83be028ff5c375802108529f72c8be96639a67af001a810f49ae5723286631fec5a1b79187baacb27f1223c679cd1629bbf6907c42951f85e3982ad9cfff6ef928f0654064772f735bc7d703eb810c3a7e1240292c60928381352bd66e3bb45114e4751469c0875fa6225d373518cf1039dfd3acc2b3b35ecc53da46e1434221839ef42a1337dd9b95d51ea23b83fcbcd021e362dc39465565b74cf522f12a0bd88a2a987a78a5bc056ae15555844ce54b3010cd10f662600e5e6733471473943c8cedab4f905ed45a0903abcb27abbe00baeb8a31cddd5eef8bee67acd0201a98c17c40bc93df3151fb27bb33ba5b2cbd0cbc756159b0b2fa11cb93c256e2087502dbe431566f77f7b3ff92d6f7b3279a13b8ecc0a766150f27363a32cc1b1f747f526b331ddecfa7a57791d0caaf1af9a897db3c40979ab4c7e9da8a8563e2cab4c894b949b1e33860a56ef7a347b045f7c62fc92093118000fa280c19a73398c7360612fa776f6aa6ae929eb18e326fab848acd609c061624584b860f385c31fa63b1b8f28e62200215111532efac37bdecae760cf65386910a1e55304d5a1041f91cfdbee3e22be6f022530c1dc0d95b0b54dab432dc8140ca52b84faaafe16fd33a477264c266e99f3f01bfab16ecc7310f02e8ca9dfaee1cbd2c09461330652c2618cc0b2beeea73bf9c443c7a0ca8acee89905ae64a3fbfeeb7774858b866809d1781a21361a181cc6b2d25048e871ee82a8c0956d86cbe1b7e3c42e27e67a1501d874798260b72a477902abb16a4f76719651fcdff7979f1dfe2307e8d89641fd12c9741ef7d65b56bc7a70e7212aacd9b5875cd63acdfcad78f51389a2654bf118dda47a004781f729063ce2cc9ae2b186014f80f22cef1e1d4e4e4f25832c7a038041cb3fc1f179ae45fee1c1bb13f216bec2fb45ce20b5786b35125713b9b1e4ba401045c0000000000000000ced2f100000000006a19095d9daed068f5530705e37ae57a4a1de489cd06f17ceb0198a3c06373a455e24bc428d55d1f2bae876fd246526fbec24278537cc84fc08f04a1fc68cf2e3c92ce62dbd219bbc25b12e245330e89d996f94a47de1aeff8ee7af48200206e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ede7f40896e83c89765327ce2827e496cd3212159ccb07c7d37ceb5c48c9160558377915f0a51c40ca3bdff9b7380688b5a2dae9c6a9c2edc9ea25ec9ffe00d29d2de2d84b99b951f70629403ce7532c4b91449c62489b737783da0cc3da5e4b5bcbcea411b01d4c78ef5cf76d953c5c619ecb315222a9688a9f76ef03407943020c7da18697ac40746a4481bfe5b056414b67ff31a2d5a8a3024fde681bc566680205e96dc9ead6afdcae3f9969b52893330528fd8eebd0dfc22b6ce2d3521c07860a2cb18f103937423173144dd9c182f61d5b24b0e4acbc66d4fd52ebca53081a9b123f25acd9bf199ad3594472dff3dafa5d14ef00fa1f9a337c10e912e7a0bc31032efee4cb17e9b2d84a136e09019bc68ea50882e19df1400255c68671ab1011a8030d5054ea7e7e72dbca3c8df9ccb54310ea17724ea2801e413cfad272f16ee8dd022e75a9a2369ed45612d2245bb4857d7e5ec616767af439d0ab319a1e4af9724c0211eec32c332106f48cd82a2e784f3f59da713e2d842b06c8322af9a83878178403188e614fe8940d50d15513474fe0b6debced368b5c0457f196de7f905818c69e8b48085b3598c2be957fa493f1be52d84f0614101d1b9ab8069a647942f48eafb7f2fabf7a8516f8ff26d9f88bfe9a60884a12074379b33365433377056466c315d1341198b1657c459b75d027ae18edfc7c720108bb3717e72035f86fb50c39fd52a1b6a28d2321d5ed76647623c84f5294cf0d29fed32637e0472ae7b864afc3eea2c5d02e478256a6682f78543912990c7af33fe8af0500a7d6552bcffc6fa765b78524e9821a80e693e90deb0e503ec96854e6c105e18665d604ddd25fed7b9850da86625eef9559b6b3b4d4662c002e90da08f697d15711d995cbe841b7b9dada2f5cc8b60edcbb7e483b7ae6d4cce5885051b402e3c6e5491045c8fdfefe85c1260c6ea83b6c4aa8f9b90e38e15e919904d46ce3659153c983e4abdfc11d71ee4cac44ae4cf18d77e88ec3cc663b79b6290a7b10216ddcbf1f4bd7c44d4935b91596871f34502f26a84732e651414c19c76c910378c032970fd2dcf1f6f094486a1bfd7d33ce975859e3f8325984cf563ae541cf1b67c9769452198ebc7aa34b50582c41ff572d708c9de322f4f4b0cd5fc7f2d7aff29fad71f3ecb7490a459d9736a8c4687b9b4a92ab50d78f0dac3a6a30509a88744fc207245856a873406b881e0a70dbae0974a0ebe5de736fbdaa7bfea9f1e74110405e89344eab1ba1ed68fd92593a1c532e9cdbbba8b26d69f4ffcb03f946ec2f6d75da843c295c0b7f5c25ad6c9d5e827b9f52dca7659ef9b8fb711e85feca40a21cfa2761fa39abe326762a0ce95c41e3835895710e728c59e09bd3eb41ee5ad7eaab7795764ae0d757b26deef09bf7d21df90a50b3261c3ba20991205dbfcc92ccb384aa6f302a5edc5855573cddefc26e2a7bff7638a88650c2b1cef383b8ca60478b4651a4c47c791308dcaae1f4f7094fb5afb3bc3931a55add9c8e779391988e1b9ea4d3842300a5bd97cc6ffca62c0490fe9819f581a436716491333806c0fc910a44f06a026d3507401831952d863eb2eabcf001dd34c397f1a5a11eb6345dd7bf46f1a3f2827f1b106a5fc206a0690ab5db35ba9af79a99025f5d78b7a33afcc3c8bbcbda6d98ca09deef7d703a260b5c9e30220d62fd8d5091a57af6e2db269e62ae767125fea9357321fdc6740b5b62fdfa853bb134f8034586b88d269bd4d4b56b9d6b054caaf5d32aea5827c8dab7ddf06aca3ec267e4c29c45664c8971f15e5883a52e07c9ccedac7674a72aa562e291c6576823af9bd8092f1ab4fee921a597917d9c6ed39f97e62d0206799582f1d1390f876f744c89d959cd2f7c02bdf4f9f4295ea5b0fe214ef3fe4f27f4ddd2751229785ad84ccf5cf2c5c77c1f76a43394b162c10f02c39da7e0b41334a5cec12826d2cf8e391b8050ba609bc3696c7b1927691ffca3fcd6b7c6c42c80074bd1130e0d5d065eb15743190d55201f36ff751c8d450d7b3bd69cca56d696c1cc0433cd989b2b11b8241a7d4acf9ae83502f7298e547374e83a6a67b190903f9bd13164978ea08b045c3e489cda49c971468688cf7d479e0a0262e273c67d1239af32a0046577638059bdf6fd015f165939288696e92d52b135f06f0a54ada811bb1af094392c12d31fddc725a95559ebf5819b946970a503947b2b9a21e6c6744fe4142b8c4b5a45e76e50dbdeea1292747451f275d306e7296c1912f6bf579b2b4dfe58c2db5d3ffd6b7eee1ad5e38bf82189f6f5e0dc13d79f5ae03ebae2b497741f6bc3cd8c309cad12e9a2a1eac84cbf1f350d3ab90ae6124af71beb2cae176cceecef0f990f358ba55115c6f16dee73ca5b423bfeace5375b83f8c7a189d203", "6365", 2, -750381347, "af9f8c5599567707f27b2f6aeec73743d102621a4e0210380045c322446504f5"], - ["214e28b003af544be812464d44e22b4c8dc3d3d4b0c65efbb4fec73e41575096111d5b9f140300000006ab52ac65656affffffffd53eac49a44663d481fef714caf85b30100a519fd146b8b665aa2216257bcdcd0300000002ac00ffffffffd9cf4767ad113dac2596a58069b7a1258dbfeaa53e24af7c3ea8c4b7d056e4a3020000000453526565f505d75303e0c4a900000000000852656a655200ab52b3e63404000000000852516353ab6a53512ecbf9040000000008516a6aac0063525200000000", "ac", 2, -1283255426, "28e67ddedb91643ae4de8d46b4d80902c093ac38a94625a7846ee6bce3a24379"], - ["cd63a80803fca0fa7ca3ac8d7c80ec1a4009af3efd5f3796d12eaf70131c3d996f0db1aca90000000004ac5353acb09ac11cafb4fc220a99df884d44f1ebc3a69b364e8a9ad9e23703fd0b3ca1d093ea536e0300000009656a516552ac526353fffffffffd3190c418ad0b4a77d023da937da482d76b39a61909d3e94ec0052d558f3cd003000000036551518e48bdb302fdcae30200000000046a6500acd0fe52040000000008abacab52ac0053650000000000", "ac5352acac", 2, 696732980, "f29d9a223444116199fd6993fcb13ac8972241e1239ec100083f314021dd7382"], - ["7541bcf8042017d7bdcb49fe74f1ae00e35afbdfc3650c3a0709d6c362ed9a227a539caa2c01000000036a6351574c7a01df84a800f1c252c5d4d78c749cb862f4f71dfc74258c90405a287eff5da91f190300000009ab0053005251525152ffffffff5a8c08665b4a8f53ffbde7567fe74d837edaaafe8dc33eb35f822d176689cd440100000009abac6363ac51526a531ec30443c1ee7f324baf539a6bec33b488b259bafa176fbc3a641b7481082e60d487901903000000025300ffffffff02b5e07a00000000000763005252ab53ab57ba620200000000026a6316a1d60e", "51656553ac", 2, -1104625456, "ea5bc7822f08606f1e7424ffcf2d809bcd7bf2c26be5c0974e220af8e74cfae9"], - ["e1e836af04937d105ca23a12aa0b31de5d61b57b1172f30a591f18acde7997cf2554c2551e0000000006635251516300ffffffff9ae19b6fd7df800d68bd0d40acc76aa1aa4829a6c13826d60db53dea1e426c2a0100000008006a6a63ab52535394bc96e9f163a287acc46e6c6367f46d09e3e701b80aa3df4ae1d4e1afc19e537e4387fd0200000002ac51997f4030a370cc0a1c7a04365e931647cbaf0fb6517c4e5596a8749101da155236c89cd80300000005535365ac6a35e6f26b03a822c40000000000045263ab635f00d404000000000951ab656a52ab006565349b500300000000096a5352ac526aac526a3d81eaec", "ac00ac6a53", 3, -1056346309, "47958d5b000b153e438bd118d8197bd5573a762e5f1b16cf2a51db50c8f86b4b"], - ["6baf4cb4035df082d6fdc4087ae0ad8dba2d1d7334b9edc14ad2525e0a9f7ff3469b920e3b02000000036a65ab4c002ef79fd584210a4dbd2e4490226085b7bc1e1ae7a412d168b7ff8e429de73df4ff530100000006006a510051acffffffff9f0f5de6ff6d5ebcb56463b35296e1b96c46fead630133a3cfc66723b89b8fcd0000000005ab52ac5353ffffffff0173752a000000000004ab535152db4b6bf2", "5352", 0, 1174973847, "c422a2359177c92a63e2a1e251f5cdecd470c0811452470073c744a6db6621c3"], - ["f02b254002cd3289c08ba76c07bcaf78d94ebe15c34d5abe3c8ee3090225b4567ca66bf8b1010000000652ab00ab526a9e93a9c6722adaee59b1f41f258ebecab3f5dbdcf150fc5cb99adcb98d2f70ffa2b129b9030000000851006a6553005365ffffffff03772f510300000000056351ab52ac49808b00000000000163c9362e0500000000036a6a65aed4dd3603353fd804000000000000000000000000a8c4c7f95be6f4ec3376e150b54528483e48d9ded92f044304175109b2186e56d1c4d878850f952c9316533a16250d90f9a4db026802566a0e65afd940def6d2110ec7351445fcd693c3661a65c0e368e1d28f70a9deb8f039d172c9240b7e02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006856150647578f9963e237b6fcbb802592deb6911aca54eda13e03e2c851b1abc6ab967b6a8054b76e64973ba373566e0906c2e1a009be709d6e938c1c9418157cf2209946528972ea6dbbc5bfab2e384a15a6a91c7d6d41e9595faa20f417d0f9640df5a2a10f90d35c26230b8f3ddacd9380db46e86847c800e1e14c1cba76020a19a36c3a37b56c1d5c67db8243a5f0c96c9421e84214b613a100d1387478070320a1996ab8347c72d19908b6c831089fe21ccb03872facd53710a6555b45d0cd0a18e131d3fb7dc6e55a0e4638b172a85ca6ff2a4e461c9acb329602ab5b243ae401b9a82e8678d3e9bf52f618c62ea9ed56b806247bd89aa068b5520c07dad3c202145b65dacb748d5ed03e90eb7f96adeae0b2739bf2ee26a47abdd4a38b624b3402287eca8d7c52dec4c0ae592fc1388c6d4886ba0344f10f346101aeefa69b2eee02253cbef503ec6fe0397badd4397452b42fa58b56dac467d56d418d78b0184f83020da1e26291475dc44e4808b5e83cff69459597c6aee9bd9ce08d196c4055e6450308795f1ec16a15bd6330aa69323d1084661f5b869eb399cc5e26413a2513c1e6bfcbe114a668cac87498af7183551bdc59958845caa110f0b300699c990a1a575b8d9b45f8c64544062157a62213d289c06f8987452be2eed49254dcd1142dca1e7a0cc0f5cbb6de18cfd95bdb42f75648586e9700cb460847571aa38a4f400003367033f00b94dff3978aeaca2135c509949dabff373459183c79e57e98479f72008b724f9c5a19f47beb5787c6fef22801c8c99ebc7e68e8334a54709d7d621cc54c98b159fa664b83594ee937686b946af26c1c76caff5e8d46767b997491fb4723d111a7e16391f9c8c83d7e527f8e32978336b3f9eb00fbf73ab2d39a73bea0a7794996310ddc54a7b7f4c7b05a0d55afcaa8d640e956fcd69fca6188de6c44f314fb8db86e9486659aceb9d1f39242c9039ce400747e05f6b943c2ca00b2892c2828de1318eb344b6e529e87337f2017ab577961a94f17055682ab76e4017f0fdf381528c94df7431f271826849429702959315439b40c3ce1abf354b5120624ba1adb580ffa76a895e3748da6ca1804eb1ff3e689abd52922420a4ca7f7caee5619cb55d481fab50665fa5078c666804a02cc2f7dec1efa291799acba2bbab9b63acab07021382e8c7531e08b654d451e98ee38318cb50bfad8bcf9868eb6960fed6fc6c2d6b1ca915cd6b4df5e545ea873086d60cf6ac0b64bca6e6e4b3240054c7ef812d5272dc217b5d4aa8cb7099a5a4816d3320528d450142938bdabfde026be540b48156d1d376f8993a58793068c8945b628a282296c7f3efa76feb5f643dfa6563d5c44a7561d48ca8405cde96d1d5d1430d7d6ebc2212e5f1c2739190dd2ae3f72a15b29f0ca1566ea9b1bde8fb5bbbb270660a66a2211ed3eb26c437e7ef7a40f9c6c6cb46687b4a165def8a07fbd6bf05fd1429c3c63e5703ac4f9da315ed4faaba6b74cc3c2dc8bab0d78798389ae1c787bc57b39afc4d572796d7d64605a65633dfc56b236214d7fab67b5f481a4884430e6c07eddc7096e995d70a18d388e9ce6cb98dcea761f559031f650a26d1094cb49c12d8544961dab1b58189ccf8657f13cca57a6b49886e252f1e878b83586e8159b6e297fb7362862f914121d7b764a88b2d9e67cdd7b9f588463069f026c3e6f98bb70bf0e1058162682930465398a9dc4f3efc654f9e2cdedd457c23515a1f6d8a87a147556ef81ba732b04cc4377e8b53b23962d046a73691671354f26b91c708d110a1ba4013125394a2e7f10dd457cbf7a25280cfde15f4508fc31404961e2f0e921009d6de4963d8f0e2b38140386c30c1266944df04081d4a27016c6cd4259903011146fb3dc671e897ee27e2f51cf524f95eedb1ca939481e8d2e53eb845bcbf3463e700f8190c9c225a23fdc160553106778d59b4ec385c21466687788785981b216b5074f946e4934155d70c63a2dbd8773c475737a9302460d26d36d33695b920002356bf3e04783c6d2e2f7519735a4a55b8d90b62de8440d73cec7e2e783950272372678b07999c9ca9ce9eb658c04c5e205953d9eec7c7aa7087cd09489ad0d1545126779c480d807b9d58c0a748bd1905cb8726916a5719aa6d882521b5fc61672370a4b60aa3c432293af4e783b0d90e7601a18788b5b7106fa9f36da6bbe2bb2701704eba721ed7abcc61755ba012f9e46926ff95b79a70c58bb8c5240087a4f095ad8e00f4d23736e9dd4153952cb884800000000000000000000000000a3c0adf0aea334809857b699300464d37efb70976caf9befde18749795bcca5d35df6b0d537ab5d328b5d92e4d3ddfface2c8689c0fdb49e84bbd76eb5311f600090e4e7f12f9514d754f4326e1e2dbb96499175ffd88d7935d8237b113afe32000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a5bb1d4625c92b7a69a3373cdbe2698a3756927e0735a4317aa8518aedbc519f29ac627281415f95dcc41cbe6d04819543e447da000fdf5ce0a78505387214a33a0e07ddb4a5314255ffe8c200cbb03ec134cb2711394354fdb88d1d32b2bfbeb2c3efbda8b882c80bb790127192b1170f25992933045758e4384506108e0c80300992dc44b563ec6c523eb8c1b608aad5586b30d61f4ab50b82d718daa73dfa9032ad6cabb4d4495bdd162d1260e36b06bac43e71ff8191449c3e3951105cf4fb80a2d9fd00d2d08a8de672f89323a66e63a13d3d93007e1107b3a0e4b6d087d3e542b174cfe72cbd0fc1d5c6a802d57bebc545d0603555da51ea645d8c0b529650d021b3227d298126c36afc9d2accf3c505ee686b8ce13bd72bdc273a57f43e83c6e032a0b82403789a5d418bf911c1fe1c79f9769054c557aa8aef81c5bb9e88062380218460d1366ce7c65c84bb49dae47d0a4b38a6779bc80a6d48abcced741f622e1020ba62acbc92bfb5c1045054acef13d0ab26d90ed1792c0f4fceb7db8b52b4cc2031529215db12fda83bcd016cca996ddc3da3231f73451acadea93c106211b3de2563bdf059488039cb49daea8c51e79ea0abf2706ad762bad1462c97a3fa52e86710cb9b6f0b3b523f674d8fd158fcc291faaf753f2a63d26efa6c60fbbff9a6b8d8dd154972d972bccad9a5f14032d51dfc37e97176d55bf98fd3def175edbfc9f5492b35bd363da24c36f8dc967f9c3403896dc17a4217c022b504ff4b3d9c267abcb6a8bf807f5dc177593cf934ca1d81a5e8b7d00882bb938226c22948e67221bfe352c0e72ee78a2d857925aa5acf155da3378414393f7da7448d6fcf643ff446f0fe681ac9479554314af6977f2d258b5d57b3f63c5b44e102384c6b8918e5beaa0c21203d5f0f895288fe51dc4fcb135daefac0004ff83a06fdaa5217f9b1fb28c536a44efcd532ce385811c58c2c210a859ceea4e6b6f9f657f46231464ec1fe84c2a330e8f689771d95090eae75118d0dfc1e63ee1a5472dd8863e70d39fd175e3f35fdbc03188fc63658a605a99442f000cba0113aeb101c8d550246ad3b96926acb8df45f8a55e2b1361b26c756e28ffac3b355e760500fa896127fda3cb3b8fd99338f7525ffa4b8d55fd0b20b1c349793476a1172bc07e025c7f04b64d66c3a46d12faddbf148161ab745958f1d52b8fb45bc0f9a4cc3a1c6e31a25d493a3c696537e43ee789e363baefa9000a51c76edca55b3e741e1a3ffd9e3f9228d5e5e4955de30c98bed85ff5aabac1e7c01d38738b1bd82369cf0ad4a6234a2ba98fd1e26f0ea9fd38fdf1aad6cb30fd151cf581901dbfb85b63a3082581a9de96497f95aef1f8d915b1357707e3ca861ada0bda7dc4d356a7fc710ab4e5fbb45a2a5f03e0ea26db1e82b5ba0769656add53b6e3bdb8e9ec2fd9fb7aa12e092404024a4e8110c60d793714ea9593e25fde7e151ca531aa888cb9f1caaa6475450c2fde7a0f853bdbf8c1b3c6c869a6679fbe64a237d62cdc65f32557dc5358de6a4da3cc72b8a3b6d08d9137a001ccc76c0fbdf696f8c9aebf474a402c6ee790ba57a665b926ab9714677c917311da9ed0152c1373223c9afc4e0841753208b623f398c4de21a0e93b38c1bab83dda65e517d532509c8f93486623bb3230507755303bedc9c73e70f94ab4cdfac4e09601f44cd40b16143e9dd5ea870f47d7cf49d3ed65a60b07d4e213ad5bc0d50d9aa9f64ced5d4b14c28800f3c300212583d6a7c29bcacc3fb622b54f6b8a304b7f098781aaa0415b8bac6ede24dac93b77f15787838016f75f166c09fc7d66e1e146e2a4a077cf7c776b579097a18165d5e2c30a6b7fc392a1b0a74d6dca72bff51cc1528b8a87592917ffd5e5c3bd30ffea208c02957eff6e42ca07d371d8c6d3b8e74f2b0ffd37cd30e1a24f395db946ae2f1924acd4214d57a410f225d76ff61ecf8de7ae87ac94e55fec495786e6a49147e3461b43fc460012854a25903541aa8f1385b0255db053e6f278497356c2005df720fd7f3fcf20bdf99118bc5dc6a4cee8bbf2bb0d9e84cea99901cedd6e1f6421a914030bc51b60ab07143ef26ed8aa8611eea86a20a2fc1e45fbd277b3911e8d8a0ddcf2aad39e2e3450f6ad7f63e1925d134c446e0e3272a1c1212b257ce99428db2bf91d295632de45870f62027d23810059c490c396c339ae1b741e8bc83d87ba4400d2e1307afd2e00598d0461dfe1eb848bb1dbc36aed18649ef0da48724bc297ded41a382857dea1a3000000000000000019f9bc00000000000f5fc9186cd59b52bcb56f07bfee640553f4b406efc8965f81c1fccad358dfef1db056f0af97012f7aa793bc78a464f0e91c2398c32d2b5df19fe98aa195cc8a8d40e2deb91cc05ef2d755ca0c3f3b451beeb5e31a0321af83af6f63ad15c6900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046fa3692d1c4fa70464821fa620ab01948dedb4c913a942f3f7bb9c7cc61734cf901248352c8fe37b58c90d1e7519d3bb50ba9df194b55d83d351553f0ff040567a412e8ecc65cfc3286556e77ca1ac94a94b1c789b1c66bc0ef209adf22ef34b098a57bbf03ee9965bd4b4ee8131e3d5cf59723c560c90147d0202b362a882b03218e0f35c9511529cb27ce4b2412789645bf2e659a96c18be27ba8e4def3d10d020bdfdbade230222a121aac1732ea44227d4e5b603bbd83c7015fe0c6dfb4f6820b211409c147562bb46f5803768ce7831234e5320d8e6be3cdea30c4052a4fb30a152a1207d0837668047c27e6868ea1d3fed9a8639174ad60b243313ebc986351021227ffe9ad89f336fda4f388de15c7fa0010c197462d34640318f8a6ec4606eb032616578b106e62ee854e27e6a14bf9177f7effd10f897de4121a2e746cb25a70032dce9a7739ed16239e66e5ff552213b206a16f49f046a061493f2613bf4706da032ac5884b9c3b80f9dc04a02ccbab661f4096a5e1dae38e00640b51f6f374771c0212509ed4a098711426716c000b0fa9642aff762f29bb0c3ff72a04c1b603a9b63ab6f3e75f7cd286e867c99fab82464eb65941d0007da6888789401255cd674c03459e769a32e53a58738086d96bdefec3814c461970abf23a8bb87733f7224761cbcef7bcbdbd5d7096013e8f7c32afa13133b94c665fcc2d75073d94a42f11b3acd056e826be4aaeca50ba57105e7289f14fc87a46185814b8c8acde93cf764be798bb60a8df5eb57eea46c182deb0c73734c16920f10b611fcb6fd94a825625f256ac3783f683d9078100a17a5943f04b1fd667547b4a45da6c5b4ca91867d0732a4986efdba087c0f1018e331a7be381f382c49776391ba66a54cf291a837baa70315096e6601984de92e870a6c436506eb1cc87e1f3fa26de651a502088fb10c5617093767ac278983ef1d72091475d9a33d9f2a7e3ba39166139e1c34450844539e6076a1025e221659234644e17ec712c6983f165a7968f13216f7ea09d4096920618998a4a69b052a5197815a1758f0771039ddc5326fb79514efa938680a38b63c9659900071e0db4b0fb2584e8a806063fc064ee34a1b141a426568348a872caccc5e3619844f103948a037f60bf67ffac7c39d70ff13755c9be1aec0674396aa3873b85496008fa58da9435129b3f86f1442939ffad184e223dbad2a46caae73cafa293016ad7690582ab9ed17875cd259eab147f896bfe848fa0d699718279c9b9b0955854fc54c3a2e53f52b2b2a574fb923d726403dd869119953cb55ac09c21a2a788253847b02f16dbbdf9b6b7d36371a5b96e92bcba500594a6a87b48a142dce573819f515b30d51e702df373acf5537a6e9d1b63b9305c3eeb82a3d312369bc2b7c15080e44a45639a6e6ed2e7026cd6486c6eba04d7e62d3449bcdf2f95d8f804492b5a0b1775f7149f71dea76185cbab4d8c9a6ca8e7f29ec8302aecdedc6538df99a04483d1a12fca7a42db1db8dff6d124245781b8fd7668765381c3d65df8837af2491a6e6d2c87d1c57a9ff5a4d5a3c3fffca11f68f3d6b65dc975854fd9009d9fe98882a24a4fbe069eca0191b5539f60c9f035cfcfe88919828f7971dbcceb6bed9228602a0f07318af73a8215a0fc7119aacf994244f53a4433a82766872008ca3492e27012efcf7b45e8bfae946e610a8a5b6cd3a25a8cecea023a4a2e105bf38134b8559a0f90b1e43a5f6461c840672af1a2b1e08215324a937f618165a11b53b0f66ee9ac1168850f661debbb8f176ba5c0f2e5055016653691d10d2cec7e06285f8e9489d322f77ac49c918bff1a75371ed04a580de53da6835a880dd24777865d70296b7f6f24acba203ae368fb2210e2df8cc917fc106ec82d95066bf64ae524cf5cd0b05b6d9666decbf30e33ac14ee12e498c82dadc63567f1fc9ee299b2247396d26768b73bd11db2def7ae73263402d3358145244358d57eb3b5bff74918e0ee6f99cc3c8ba61f202ba0e8eaf1d71429573b9bc63485db27493c3db4ca298be3254be11f9a7b1f4cb9a6687957688fc8d7fb03cb084216653abff509df629a71cd7d7eaa292d59ba0490bc0a7e5e1156c712279124de5d2eff498ec4345c6a011d3a36a9d9aa4eaa0b834c6e7740e2194d42f3bf570213ae1a89554f0ed814f04ff8522fb4a435028f72b88c9839d64536754944914184bdb2227be35729d618866a13b1fdd1a0054dd9d261ba7bf99b1e23e6eff46d170364fc620f27bc6ade1c852a171dc8f4ee1b95618e358a74318a9f508d9e8037047ee5c215eeaeda15d1b1b16f867c6b46a9d6e7d0ba16c220e85167de5a6da58f2af9b2f5ea17c11a75a33d101c1d2a8103dff36a7a6532cfedde7dd52068403676b62f3c65340c", "51006a", 1, 432578750, "eea69ddb1f784b1444925271c8b54aa2fa666b010a82d16feb6c96020104b459"], - ["6c1c079b01bd30dc31df27440816f063fe02727d10dd2d29becf2969f33d5c93c3aec3b7700200000008656a5352abacac0071259570013913ee00000000000000000000", "0053516a", 0, -1869686261, "5ff87e607f414b2fe4d8813500c99df81593e0e96e5dbd9749217aa7bf9502db"], - ["4607d131011f1dbb9b145d7769b569c6bc19ba20548b402c0920b12ea5a45860c5b7439a3c01000000076352abab5151537b30d8e9017d5e4405000000000853ab5363656a5352000000000189321603000000000000000000000000a8245434fb4e22ed89dee0e8d3602f309370dc01a8f6022f7f9828c2859331590430721404b3e54f1116cbf4273be81cce0b9fdef0b2ddbfcd0aca49c78e2b4bb2af4a2934a39f33f8a7e3d08988a9bcaf722442441d61e7e05d2a0085873e1800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f826ab2d8cbec51e6e29e7023d84bcbc0c3cffedf81b7b49a5f44e9654e327f3061274eacea85014a5e5994dafa52d2ac39849e632475e00673bb99487f3a1c6fb7a71e6689f789ba056de4f580929872ed27988b6bac93af3de622d2c9647863dc908565308d7abc73124978088fc42b1414309e1740176598b0af4f5b3c58c022533fd94f597efea5283a98d72a6ec5afc07fad11c81021fc49f582f0c1d00290301b1d0613f90cf09970aad1d9933e5d6a9b2d825860f06a2c2df833b315472300b03c4c5656fe930fad0325503ad98ef8017028418c08c00ceb3083dc8a8e941d512857153d6afb289de7ec453c17c2ad8a68fdc36b41eff06b362778b11c842ae0302f7a8be2f720c6b6c3c695b0aa3d5866636e776ca4d1ab22e9c7b2b85c7280c0327ee5a8b11fc30a0790daf068c3e1775f6f0b1db7219784a0d0c05f21c7cdade0213ca2724ed7e5add37ecb2f094b159201069f4ace6973a1e78241dfbab7bdf76021dd186236c169b1de4bfbcb9b915e02e4d1e18bb1edb4de90240e72a33be848d031c602850db92ea4363170b028dc7cf9337b8528fe4975ced67a39b5a94bc2c8721203f19cd0b527b86aeb25f5f70cc35a8909477f3080a3ee59da66ecdfb62e979ed258c5b9becd7b8f8018b7e69c7c3ca83ffd24b07fd496cbe548ef22ffc81200d491bed3664dee2a54222fe38c393b4640f17523db260dadc5f100d0f899171c381cb233d340b4fd71d5b2221734466be1f908548baded264ef5ea3b473668a1da2241e0b768e4d0492e34aa967089cfba8b887bb127cf7c72665926477ed492c39a8570fd6838e5294849d19547c824c448ee5d2076e2f5df3c08a85dee86bf291fa4493c70e41520afdbfeb2f93c5c23de0f29d86771f1489ea0fc7367a11fa7abc53d381e21e170ad1cc3009c3ad7399bf46215196b39ce6820c228e49d44acff681552da1a7f820f12441bb2978cf0c1b49bf3c2828e157ca73a7303e7f89e22a4ae7f35a7dd9c78cc3d4ac23fcda4dbd514c6ad473959bc81040000077e1832d03426f3ce575ae1b786071baa818bc776cd6e2eebfa479193365e44b9de6798f6ee98c403f8678bc79307f69a00d054f6beee89d8216b3ce9c8b2a099db773c9789b5f92b0ee18e0df4551daeac9f832b8cf054fb7bad2e11ce872154e9964b8daeb7ae6e4a3bc8695a055ee0c8e5acca90a9e1fb91160449f18988d6bd69ce9dd2df97b7e08da7f99b6f60f03533360cadb26efadc7230cefbdf05644b327ec482f88cf84c8265b97457644b7533dec471017f62b53a93b562aa5e64a8e816c32573730295a84f7e60ca382efbed81bf983a133c832b23bedfa493da0927946d874278a53e3908c0636a2f07d1f22def3959878b7e1e64a0dceb7458454f43c339015b6c01943aea1b19d6d444ba1f798ce49d321968680dd2dc75c4b83fc1c75d18808421a5840aa7b93e9aa729569e7bdd290d82d50dac8cb98db8fa0a9641144b58f90c840127f9fc5175330680c39b6dea1047b8ca569e2817491c15b98988df72ddf9dc2b61c5c937f131ea6a83dd786f2a70dede77e57ea82f56802a28ec0954a5e6da1958a0235acefeccf6851dc539fae28c3aac0a08804bf33ea4ba6928a1417ff32de335174e755359755ff47099b417f673ef32ce42785573e367f650e2e64de9a6adc0d906000cc392b5629fca7fa36a2b3c83d6aea9f7657221453076b515e383c4f1a88a5af9289ba5c874835c9aead5d451a8b005ab8111af0e3e8cff86cdff78a761f63a8d144d1c7e70732dc178179a93e30eec54b7e6af162ff10ae189abd8fb121862f416e9670b70cf97636c223260f7dfc924c52663bdb67851069fe5d9a6ce2fabea1328989fb05da81deb15262523865dd271de77f4e8ef88793e68203aaec64e606e32eeb26600fbdf913c4f1579d2a2e567141ee9e8d0da1a5d62aa16c8148fd26058633ef38a7e5593bf2bf38dc8f63ff79320c13ca29423a88287db509768bc5b54b078a08b2b9a87e42aea9da9f004285ada76958312f9ba35de86d9fae3f352e6f36df3166be08383ac0f405267b00a44d44b82629c95513619d648d3573ece307b09f701e570629b6127d0e2af856f392729c3f702f9965facfd0666a9f86627e047c762f45d96f1051cd95ca1dc4348f9f2626bbd2ba2fd79f4e4b04f65ba2436639897c28fcb7c4eb5d9ef3a96614b718993840bbe4a07ba8574b991abb5c375d1470b461207c0c61ad21f6c8f6ce4068e78ea69f214fa11cf9b17636e558b0b77fa488f36d883edc01b97f0c02ffbc84d477971940248d615f10be6ce2740de21c90f39596972ea83a4e8d093b398f2c533e5db739d7d691e22d4456d4b42afe7c43f3c47abd0a9f8fe5772e88b4941185fcb9d5953ca26b2f7193f10d", "ac536a51636a65", 0, -1786274802, "10085739477362c6f2d12d690d83bef88ce842406d593fd64d5b883dade95702"], - ["73ff84f304339609758b9473a16f2a7f2387c7e8a8d69a059a34ae1911250f8b458dd99406020000000451ac51acffffffffcd468fc36635bcd26920bfe21ffa9c5465b4792109c6fe954e727f2e820732eb0200000003ab51acfe9c78c7584c444e629d6bdf880df240b3b0067bcdbe39a2327b73bf799ebb5c01e9ddbe0200000005acabacab53b3c08015547e6f5c57d95eac4929ca8bd134e5c202cd19649ab6a13524de1ea9ba1079270300000000ffffffff0171e0010100000000075251655163536a00000000", "65ac0053006a515151", 3, -981835875, "3e01b78a3a31b8db363b4b2df5cf1143f3f0905cf2f650e67388e0e9b08f11c6"], - ["f48a9d9d03ed37f1263c59233eac80682c30cba6223ea8552bc113f75b8fec8f0ea8257370000000000652635252ac638869d40da0b6bcd433d4d6d70b36c574f496789ffbfdbfdcc4c86bf94126fe628505a0a3020000000751ab5100ab0065611f87526aae7a8960aeda855ed118187f9fbf93c741c4c953c7ac4b5f551eef8f6819c0030000000852535353526a5363ffffffff04d9210b030000000000f00ff8020000000000b562fb0000000000015276441d040000000009ab535263ab525353ac01e347d3", "5353", 0, 1518285991, "a9e361394a0019e8974f2bafd0784a85df605f607d8c9ef7bb4e2737cfcce20c"], - ["ea8f36d70327f19ee777df1ce00a0f67cb80fd233d7a8e7f7946716494c83d26f3031cc44b0000000006655353656aacbe6db9dc8f934ab94897b05f2b13b6f8ebea1a2adfde4f75c83aaea58d47f968dd06ed17010000000551acab5165712215ca8ae89e770b0994d6f1ad5c76247c9250daf00d8bb34b780c2a92b505a6ef415e01000000096365656a005165530031500933024857d2000000000000ad6cc2050000000008ac006aab6aabac0000000000", "6551ac65", 1, -375164970, "e19cb5134376e9482f08f20586156ea522abf3ed4ced101fe18c4e9bd70ffa3f"], - ["61331e4304e358e96d794be8cc4e2a6f3b902b8fe12d5b35ebdfdabf3e57407a5f1ac24b0c0200000006ab53ab0051ac76ebd0d9f1ad6847135fc48597dd29f0b8b45145b98a149d949725be2df17ad46ecbca010000000001acffffffff4412bd581f448c7b517f615986619dee164234ccb4f1ce490017abe9e82846dc01000000056500006aab6e5602be92b624b957b13294c6c8639dfa283784d6f856b5bd67cd2ffb3cd2d19e1e26040000000007006a51536aab63e281637204b6dfa805000000000652ab63ab6a6ab679dc000000000004ab52656ac5f936050000000008ac5352abab525363575c54040000000007ac005151ac00630000000000", "65526300", 2, 887585537, "13e866b4bc90efd140a89a7f214372334aead7ed2c48c9997df83e85b074aa13"], - ["33f022fe0429b43b66e7e0cb97c73b6cd9a528341942fb2263c35db30e4ed0098001237ebb010000000300acab0fc5895f3b9bd91e89b64933ac8a314c906e2b7bfb484774efb9c2e57e185bb71c5c1618020000000151ffffffff49e4f68bd9a6bdc289e246d1ec59ba6e68f86087507e2f250ca587d974c7794300000000076a006a00536a51ffffffff9900d9a17c8771c82234f619759bd6559f7a54e8fa7dc1c34fe2dc56b6b5c906010000000963ab53525100ababab8cd6577803d2015504000000000852ab656a5265ac634659ae0300000000066a65ababab63fcd1c205000000000263639cf303c9", "", 2, -706112884, "a54bdc5770f71b88561917c533f8a4cd009efc601bffbda8c8951ce68ec414f8"], - ["7d0bc11503b64a5ed807183e0c692eae77b3130e3a01ba51c6556452f65113f4647aa274640200000005656a526553e64c52056f240b26c386c0ced9dd8111e5f325e6a6da724f99b8ee21ff81840c5f54bd600300000001520b74fd3255ec38a436cbe40d7e45735aa7c3486c626ba0b93f197f12c7d24b1471bbcf5c0000000009abac53ac006a51ab518f25ddca04bc55b705000000000400ac00006c7def020000000007abac006a656a6a1e37ad0000000000075352ac635100ac55761b01000000000951abac6500ac6a51638439b06902000000000000000069a8ff0100000000b8267637f0676c0dcc9b82109aeefd4c5a5067b27c5ef6bf7af6cc205e68360f6aca77919310bc354717752bbb88790be15b706830d952b6e62d7a6f5db83b5c5cf14d257964ff1b70051da2fd85985d4924d48003c084e049ef27ca83d7d5fa00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c2854f9b58f66885e0cd0287cc9cf5758ebef5cd48366b91a55493f5c9e8b17edddd63be5f50849ff67a92bbd4afa63297eafaa4ad336a30877f970a5d78d651c5fee02026fd9e035012c6d55948ae95ddd8430d8978616ba6013bae9b5a13b292967f7b06f5db169981cb7417fda6fc82fc3e1b6bb92e7647e9e06d5fdb5ec103155821a5ff43947d5377dd01a554e310a83676537ccb447ae2d30cbf44c2d06203253e9833f0ce597236df939861fd8824655c676459cfa15d9a2a47c50c9bb3fc0b135348befeff9b9b7ebcf903099ccdd493d4edb99cdedb3081ebfa1a093f17fd166be3068e325b3dc1741f9a16ecf03fb0e5b8729b1e57cf12b23cf0c3e55ba803111349490754ff1daa28892259cc8ff4d75804a2a6c340c05b4a68035b3f4124030749a0a15e7b8938ea5530ef922ff2d496060a9eb633c049722c9c04ace5f9be0210451365968e316ad320a8e90fe4d8053fc51af9b059b7b2a215ef335e98a6ac032e74fed95568c7182df7d3b7d311576a08c30c2e0726fc50f33ec8c5eb36dfae0224ee56a96daf5c19d1dca64c63232e5625ab712526da80ce34c140b073b03268bca5cbdedfcc8bdcd75f1f22147810724609b70452780f89cea6c346e400feed0851f7be8af3c4c2df8333ede0505859bcf9e718e3d4326aaca9ec2b0a1a57f2c55011510c212d98e7f17c1544378eb0d2a243b7c340c57d879b096530f52dba5380a871f88c6ce6a013beb57ae9a98700de255b95c21167ad5abc9ccecdf413dc1096a418988c876af5d1c2a417658a1d49f3f545817763ea2011381c36bf0c0b4325c24fe0b303c9bcc9d4ab56082075530ff8bc76b0ee2ecb13875699eedb44e69e604d70112dbf6fc31fca56a12f56a8c275339677d776c10c908d2ff32031ce4fd6ecb7bba53c1f142a2bd15f51885be17af7a921709cf4bcaa60d362884e862d5c015adc527e8ba3f99bde3537711e34643a6ab50e3440a67f0a40223b765c0fe39c85881398e684af8987b6855f7123d96621b41884e7af17573b5dd8810562ae1cf4bbacac5c9edddd5e0438197c79d73d48e072ca5da238457285604fdfdfda26dc6271a59f5cd3cf664b8308e98b04e23c192deb4c4b9e7224a7f28923783f94bd1783d45483e2f1b4363c60730452a4112196ca539bafa10fb3928a9ad87633377021a03bb43d59808f97cebaafebda789559b57e30e08c33de9108fbdf821fb6c562057520af55d9011ddc4ddc013cfcf6de18be9e9200094b06124bd5be679abbe4423150e3f941c0cea95f680a6344adcda63a2a168f0761b155e6fc2145f1b263f9ef4c5e89d3d8254a510ca241e53bdb72ae431a65ea69dce289ef2f090346e216b01d7da291f67972f53a015d9d8b0930b9f22e6710a1439e4d89071c55634f162abe284f995d6ed515233bef1a561e64387b26ef1f41d2c6f31e29a1ba77853a0fa624dbbf14f6533ee3e91ce37f5c43bb47424dba1267bbe80d5338ea0758cf0ecfa3087585de1d4dda0696354fa2d58ecc042fb5d04e9b585bb9c2cfca8d42eacea32a9d28c3e9de97d953ac0d0a8f7adb119f1d49536ed57eca3ad7b2813daf0e1dd0dd1ca2535e44783caf20dc024a043b6185c7e08867b6a98548e9fbb193857edfa49662aef64b43b8ac930ec78d3b29401c4828f7fdf4fb427146b3a4dcc79d1d69a108531ac0221c51c26721d7e85bb06e1e1ee48a0ad7a7f8a4dddaf4bfa71c20edf6644cdc453df105a521fd863a1d0490dec8ee8a229b7f97e334f71d71875b2a3cfd0edea4140c3f12af187ebecd29be381544e2bc081522d75649d439c155a7bd48c15576f1903f60e96d5ebe09f67bc58475c651b773c663fea11e178ec67a05fc63c00713272d9a72a21c3d28f01ebd877911937a735957e69cd7f0641417a39ea081833720fb0e9731f8f8363d0e4d6c33f57cbb96d854c6a231aab673c0a38ebaf7f559c31446e3a3adcaac291d9851aa6dfd62595948fc968df81754c8fe7e90f7358d4741813a0183a486aab39cfa1acefe0b125e9c4a2c49aa4af3dbb93e2adfbb5f5152c7961d8e6332072a6e35046fa2da3ad5ce41b40929aa3ec2b4df90d9bf5b78e6e7453cc082cb10b15615b6ccd27253bd91601cc3501478f88b75af57d69bf7e39a866e1f48f5b493c6136193de5d4d27ca529bdea34bb4d9d0c1c24dca6dfd65e2bf0d2418c7794bb6438d4298d9e5cd14e7a8a8e83aea9d103814c23d5697b7ea1993db653b3276641240a75d30ab37decd556b3466be054e98670000000000000000e2d1b60200000000e0ee6c782d9e6b5369d26cd12f0d3a74ac89f41fda7a7f6118857365aab57043a6cc42af2b2942020c1a7299b1ce92b323a42c98e4b03630a6934d8fc6ad4cbe61da6b3f821cb698a36cd505b58d5708b0adecaa38aab6b3599b789568ca62e30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060a1c617d89dd407299aaf0726b8e45568f01b15eae81980d6fcb9ea7933a21243610764d3741c73dff812e155695110689aab42e74dbfced6318ce89ac541aa45641d10e0d5e1ef062947890e9a237dc98d9ad0597637e98da21636da8c72d8bffdb31ea2a550c8c11f82f93a23f5e98be938256efee502d16c1100d885adcf0303ebb2ef45649d5538fc99019cc5c0fb4ae672fbb836f311f13fe055212e43a7020e4b6309fd7bc1e9d3ff3ab806841d883b006ab6014257cac253fea187c3e9fa0a0e6b5c6b1c0574dbb3d6e474593dd4329af1b8c890ca0a01b8450f8f16dc48e61a5d27e731523cbe6c0897497d3c01ad62070e71a3d85af2822b740439a084760312439eb975205727157df829bdd71b9aa6e390f99aa1be4a033478591b613491030aec0ae9272433d91190dcb5896eee62af3158d9271b01b016ac093251e6359f020e71cf5eb748697f7836567b521c09485fbfee49c3f4182c4ac17c20a4b47158022f7c819ef0ed17de9c38e16228989f6f45d3a318944cf5ccfd0b37540db640d50304d2a668a2732d2464ef1193e3e0866ff0fb1b4b8c53feb3f2bbfb9aac1805ec9c277fb0c1dd1872c8ed82e849cfb7a60b2b08240f8414c6d81f4409a85c56901c53b240cbbe11a2579450eabbbb5d2b84a6d40645c92d45eb479503efc9edd9245ec962751cac3fac369d1a0f257ec27fbdf179dd633b295d212eff54a019c67c14d817ea35cc7aad81baab420dbe30413b6681dbc6ea13724292c0afc84f297e03e7e266e72eaf80764534185b06fd5c31e6ccfae703191ddc9b88f8788e6923138453df2a2af6d340c440dd8de1f7de2fdd272e310e788c0cface7ecfc83250d33b998f2b75b1f99364ee27fbd7ad605b7bf0ec0d0af989543ac360e18b57013c3a8229ec6ca54f097e8b52422a1ebcbb78eac9f0a0df244082a4a641078e568e612a4bd1051112c512872a5cdd237a10e7a33f321d130247a8d4dd8364088591fe17d847dbd445fd8195c5217b96f53e3cc4281bda7067873a1e603b1e9fcc2d1b1d230f8f178ea3e8be0c58e78cc44446fbc312be55b9d9fca769b75e4f245b3727270d6522587d5865a8ae29bbdc5a756b7630f90b0c7d69795f364d3ef4347bb69a6e16a7cba611b4ba9c4455b1ff0ce6ba15d1538b21a47cb46a64452bac4960e0d8022a83ea5679ddb7d1a7b7973d1e559345beb7a0d1d32bf2ca642d0a3d3727104ac9407a6e18cf353d3bd8365c73c97b890352c3d3a041d3736b23aab347d78bfe01cc004bf4ba1d1e1cbacdfd4ad2819b6474ce2563cc189a1db65cab93b44cf191c8c385bc24d0e61295cd05f149c766e8311ebf4f449404669c704f28f364763e623aaec9ab31f4f5d5fffaee5d91aeac69830887d9ec9ce38ef26abed4fa60c68c9a0b6986c648098321a4700ae20ba179b89a4992eb884087c6e299be2a3294d0a9fe8d33a85ce8627a045396e7a11c4efd8449bd352c938bcd9539dfd7754b0e05a2a3041250806de3ef682b813aa887c4ab6610e6f2be57e58a337384c1a53f5840f2681ea53b3b21ac09fcfa2687b2cbc7a6830ab07e21d80c1c19944d0ed1127b2679ced4d1a84feb239f6e7267ed5281234c32d659de8c0cd16e7b9820b51af2fa3d49921cd687d183fd40bcad612411e9913898af2e2821664ba072235a835b060188ee90ac1d7e73b5aa27494cb0f42d9f853014cfdc4464948e4cbd2b504adcbeea2a3d4298949e643cfe336c5fac443dcab22a3e5b78e3da872de4ab49b6a33c0f5df594735b71bfd370f44d5926503cbe5c97521cf4fe3d9fa9e3ee4f31c1f6a51920bd4cad05accd8c998646da4bd069ff456f82d4b03d7400085026b36f3dd556ec1295ee501d5a2db6e6d997bf1cdcf66fafe219ad583ff86ccf8a9de753f37341f6c840c9d5ef5cec76722735ba4c5530e4ae1295e14236abb1bb3fe4bb0bdbaef39a58696c0bad9dd6943897589d71a760b275b891b63073f8ad910285e86f59f3b6db8fbc735bbe89ea308ff6c97e9d1a64418cdc0c4dd5e8100982339d171cf74d1de1fd08a2e2d17c0f77918e8023a3b9c35b01d4ebcac86efab96eedef0e0ca6019217d184df22cd066aa9080e9478e5a9e36c07feb9eea5d8ddc6db3d56b5a9d03793513eda4b3949e6fd6a4615b0555eda0c8d6160b08d3a91e3208e116aa0853552b1d85ae86df34d85e0f7be87f19b03ec60b21cb298ff430b1815d20de54aa182ecf8be8927e3abada7346b88451e4dec1b69e8a000518d1ec5d3ba8cca07ecc99811916ac6d977eae9a95865eca59436108376038b0eb387d519520b993e6356b021d446a9eb686f3f636394b46cdfecb82ceff2e43df9758c68a4b70ddb36d13be267ad8f4ac05851141ef60ca9d22de3a362fb01db0894871d814008", "536a5163acac", 2, 1668438795, "075a3200e4995286861c8da88daa90ab9bd59650ae0e5a620368d672adb21e3c"], - ["68a90293017be2f74741d5072182034f457fa173b9c9b87143e5297da21c46a5a7ca58f123020000000651535363ab00da08e4f4035b879d0300000000056a6a635300462d490100000000066353ab5253002b8dee04000000000200531fa73bce", "630053", 0, -1936509849, "5e1d2c080b706122e45c90da31408b65deeef428222ea347614c2a3e782f5f5d"], - ["dc46d3a901d63aad7c615f7e50e6670e219bb90b7d4edd1214ddfd5af4fb0cdae445c877be0000000007636aababab5251ffffffff04a3a0e6020000000004515151001dfd19040000000001005a501b030000000004516565532f929c01000000000553526a6a6500000000", "53536a536a6551", 0, 479598192, "0f9f9d62b89c96a56e1c26fdd07b694e008a0ac2c95f833df5171cea6d119258"], - ["7d382f4c02e01b0ed95dc1a387d48299ff8d55f1a49cdaa983b00191d889109e69a5ced1380000000000ffffffff9272be0beae10182cbd060181477d9dd3f1f27839111113d4c8e51f6ef9fb86301000000066a5365ac5265ffffffff0364dc9e010000000009ac51656552536aac635117c2040000000009516a51535351520051fb7ca5030000000002526500000000010000000000000000989d980300000000d211c789dc7c18f5dd5a106fc042c1d1189f1bc486c7b671c943333e0ced0d1db0a5b7a4c4be4b3e123ca54c1e1d603d2489f01b7473ab12106b0be0e1fa1c849639c829a941c03f45bce0a77c21b27f3129e3219e6070743d97351f5e05034f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000beb8c4eedc048595ada4f994bda71d414849253cb604150b1900f1613b839d125adb5acfe74fd30003be53913963f0ed6aea9ac8c9738986def9d9f4b7bd300689eec0105c4d899213fde8b20d46319b8b078ab55c914400039e3dc6bc847e1cd720ff0bca37deb51cbf921a533d2a06a56f4204384bfcc30a49e8084119714e020311984016ef70b15136e4fb075c9a1b5f429a40a1e04bc672f68a310a09c150032d2514a94ec1c7a551e173299b6af7b8366127a161bc8decb872c49e471b96d50b15d2b34df25f7d82fe181495437bd576ff9b54c67d29ed42d746294acd3ffe060b3ce89c1466ba35390c2e6ea38732cbc914e80e199492c3f869f733aca962ec021b2dd3e601cec86207e941bfe5ee36ba8fb8af6d99ceef77f2799d3ccce9068f0325750907b1d8da8503ecbdcc1b22779c0d7639d37dc09c1b75115c293a66811e021de377b346ff46c71112ea1a81c4b5e976b65b60c239e09e30cf8b8088890cf40328faffbef05a45d893ef9da5fcd3eb6074355a3ed057218d260184ae639829a7021ff4958ead6da2f794028ea53c4a2abf1b5234d8f90dc2512a71bf20b3cd4cc0d80330f3e1d673e01788b0f80b4ab3b6fedd27145984a0493bfb9a981452bc110471d3365229249c282d37bc86900ff6a21017f295cc1d37c2b0f1ea744e68158c22aa6e8451c35bb053d950b3747176aa811f4b74d7310bee42fe042f5eaea81654dba52a786f8f1f3c3f4f8c4c93ef8de9aab1e0c3589a28df6012ce5db65f2ac2b58de723b12998d4f2f6902cf0b2d0b70d6f61be1afa30929ec03c65cb6c1429ffd3d6040bb5e9e5122bec201a44114a122a92fb4010a07f25ea78361dfff2cf9823cabbdc3f9896225a621f348018cfe10e3601a22f89f50bc66e974f1f3d0e19dc070316bb2c0a44029c418fad7cfbe65ada98176c9d6aac11de0fdf4974105e5105bcd7bf1ed2b4c9e215bdaae702429ccf6c5f281efb2290cd19f080f2b93660a4715c7a02c8e34ea4e8dd447f800c7aef4c2d22fb7d8ee79ce4bc613a76bd5585fe8289461e51c65e59bfafc7a190fcfc7d6e15c0014bca57de9201223b1184e832c8946e73ffb89896c71f86e3e9089b7f3668fde753a32fd654c24253feb5feb52a43579a230759565b7769cc40779c04fa257f51cca8069566416da823f8cc5a9ed883e18b20f1ca77a7bb669edfe7085c72fb775879e16437b148930ba262761f068cbda3fc8f2a49a75556ccf5d4dd05a2b63ca9834d4ff5f10b0b8849cd6d3927828c4d08abfd3d07b4d1d9a949af569640be04fe966e517324b051b751c986f9b7dcbf7ff9375c1a9b8c40b07fb6ac2cad59c97b311776b67b12e606b85379f098d0d19105a5eab31df2c1ed6b87cb83909aa4229a17b7eb00478296eda5addb1d27cfa304c7f6a7ab628b98f476cc6d065e44d878cc10c86a93356a4e3fbaa797825530ea5db92a67ebd5aa0a09b7a1be7b1b76db0aff1b60e2138cc339795b2ea029e5d4002e300e3869bfb07c33801edcca975a204f065c4f1ebe9d75377affe07d7ac1966aea50f7d5b772b6224c2874e392034d27917bea64645373b5b20caae4e5bcf3504faeae8b2189488d86d534ffdc2a8097d0e3de1d08ae9b5bff400abc3a047efec772e2f99abb1906164ae7e6f1fb9e36faecfb60aa0d861d8b38db10c6e572654704f6532589e3f193491aaaab8e5fda8cb1bf2acb4bf3847e5558be6e7407ed2dfc33f32fcdaa8a6e3c0019ff8997435aeaf13cf1acd9cf8d075c9489e0f3f7f78d6153f98ad07e420d3b96ce2c12644268e556ee5b6f73785bff62ecbe79079cf7eee6326b095c336fd13e25844dd57cbadae0a5d7f98f55c20a6abb3ec74010704ee1ec68153d87ef76a70feb548280cc9237c922b4b071498cfd51d134dcea93502b37c724bad36aed3f4e828ee90ef06e8e94d9269a3bd3f256ab772286aa9a21db0f9d2fe8d443f5617285b95bfec6dc50b4fc35836ee68fd1f1dd3c3ff1ed40b5291aeb0998d5eba42da28362d567032e69b710c0ea9e064b1bf77fdf711ee1abde4bb429f82c8ad0ca7dcc1451b2531f78e0052e013a7f85d1d275351c4677c1a78292e47bb0a59a42011520c55a1f7ecab7d05bfaf25bf16c9a4da1210332e327cdbb437927b8f6153e36fd3b2923d7cc5a235ca0c98156337d392ee8b54af47c77e01eab4379394b60748eae761c932ea48214a922b0e8ca1f6550db7c624969d7153cd4401d9758d825e90881ef74b7d0ce58cb16d136b9c4528e01d2b2c5d9008eb711494de3b536571c1e298636fc5297d17b7adc525c1350267abf1ff0ad6129ab2d71bd1ba7d3c7bd6dc1356c8b04d35ff37b34af2d950f129991705847afa033e67088d0c7e6edad07b85942088faf51db7babc8f6758c7303f908", "53", 0, -27663804, "373bf6870aa59e6a6ed88d383c96c1d73b681586d9d236de535a0b009bdae012"], - ["7919a08b03b162540f3415988459119f87ff6a15e681499b6540714611977330de26b090cf0000000000f5d0ca0b3c523d267581466ff19f162185ba114f18aaaaf37c55a861a15a2a5aba3008200200000005ab6a65516372ccb82c9b4ffc1aaaae1bcb2af939eeb353920fffbf306f17f966255e2a8868c12800c100000000026553bbc29cb102a379110200000000066a00536363ac0cdb850500000000015300000000", "ac", 0, 1563405851, "cb44fe4e571c49c7c431001f5956423adb8025a5372f84ef5fb0b222d586ce34"], - ["1f54fbb40153c70a39490ff96a978134c290eba6b0429fc4bd009ae7918ed54799a135bcce03000000096a5151536a526a0065404e89ba03b5ae4403000000000751515300535200f8217704000000000863ac520052ac516a7406680300000000090053ab0052ac52636a00000000", "00536363", 0, 322938114, "a305f058143e2ec8ca7ab5347de346573cfff0c4d39efcf8b508acecfd0a20dc"], - ["35e36f5401988e113c4782d4668403b50b9547587dd526d92179e1d5f5eaf258c21ffdd79d02000000045163ac001b7fbf0b03ac2e710300000000015312a86000000000000453525100b9e6ee000000000003ac51528c56333c02b01871000000000000000000000000002ff4c6707680735e096407e68c03ff04e1be546a5c31ccd925cb7ec1fb48fcbe7bfbfc439fa693d5f8ed0c1c8a249441324573efa3c536ee5e1400adf151481939a3fe1908194288935df6bfeb705af269fd35e964a79424914b7bdb0d8b8233000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006733211d15fae2a552ddb697f5e041fcf48d8224303f3c2ff7b65c3bd57621c795c15867f044950bc8c34e2778e597a79131773346a68fdad21a92dd255c055091eee9fdb94836225780c6d3b5c666fbeb82ac6e3bc835a6ed301a7f780987556bba9265e12da877d46991771f7ee9c1dadf9eb8089475e97d58e7a55dd1beb5022626bf509b4e0b2eab012d357f6eccb912fab71bcdfa30085a01a265eee41fb80304b072f561e337dd19a78788b2383b3e8346b55ed875bba87491dcb5829bd5bd0a250dae0b9a25b90c572edd0df2f5792de5a15d247a43550656639ed60ae790390aa78a6801fd59726f1c62be05293b396368e17616c5f3b02d6ea07950f1dac30312445dfe54fc13864808e46d1478f3c4dcdf65790e46a11af89a4660312a70ea0305eca6a4d98aeb9bc9cd13a41c3864cec90426a209aa4e1f5f4fed800c3c3d02022e1a7096da95e15f83c1aade3068c003281e9de1ba3cf4e95c0ad1bc909a704b022ed176e947b5a08a6c7ca83f3761a3540dfa5c2fd8ee7dcd6e0e63d49df34b4903201d5e651d6af29256ebe1230c0cd636bcc9ebd3f6f8ad7754ba569bc98f4ebc6968969f49b00f44099d7f405bc6dfc4e9d2a69ef3bd294dc5d433bd3638c04d16bfe50d4f850937b5e3c8e23ad27ea950f58535dd23e9b070fc81b639f676c154358d4eb996a778ea7e5ddf40aede849e0ffe1e47b7c359934227def62cb6a6b6472c15492c3542ff659421b28a60d0f7c5c48b1e588f8d267877ecbec713efc6b4af35325c4a9661fb1e2ab9a8cd49a921a0b2959b4e81c36e197b1b54495317b9e1cbd7723d83d752eaf875033a2208efb56a8fc69fe437f7d90de783857e8d2f585c4b968d3e2af7c4635d8f6e448ed91fdffa3d30a0f5f61f19eb6961c6c07fef8a6fe7d8519ba31dff370d1b4fd343e298b4b2b16b279ffdc61e25bc91d8540f1d2531d4edc25d690688938093f65e7db2e308eb17d6040a5f1458088855cecd60d7d716df82bc14d6e88710890ee9ea56bced3ac99e1e67e7163b13de8809c8f17d4f76cdc075d5b6f7b693061c6daf5ae143d7055c9a0d993bb535cf21fd473f88aea038563c23db40ac7b3083b0208c0623e2f7c13050ddf7aa40e24a50d1d35aecd1bdff3bdf0ef3b48440fae359e08f8af9eaaceeec35f4c69d5dbdb174c44fe129647d869e4c19761439443079aa6aed88f02c21c6d911c26612d2b49fce435d36e4545b29a996735bf92f1c531ceb6f8f59588ccd9aac14fc77d3b158004617ffa691c550296901f9056cc9a1d6989bf9821bf7f43bb33b5d645be2db5cd91c0cb452fac17e617ed758b13f6bb664517b3f9d72e67bb7698c2aee42383fb45db011e595132bd7d90fcf8862577d874bc16300d2040874709e1c8e3469b04531f56f1532f4c82bc344a45bbfcb952ee697528b58a93962d6e5f850d29485197eb8b021c1b940373b4ded62cf341700acc46c924642585ff51c1293aa1becd0eb6fc5e5c83571985d11bea2f5573734a0d93d362f5ef1f71c873c9b06aa3c9f78e789c484a77cffabe698e1414f01a0dac13abb8a9443fb2f045827a1d7337d680a3fbcff4f065bfded06856284578e16c7dd2ddc2e7a5d0e362d27bbd09d59426633c2d0d4c1b985079d10b91d05a58092828e5d880a7c562ed89469198b11d40645d52f35a12332506e3db72c9bd734602ac56f90d8093f3ffdd0eab1e455b417416c3690da403db570e784b30488b9ce99b137ac813883a421d64571862deeab827a37b07d4d5d875638eecca2a4942057d253826e7610249141c90114c53752dbfc3dbdda9ed57d8f860bb8b758d042c07dd7939487e73270eec0e1f602beec06a2626a70005759c8c384988ad214ab7fae7032b8865afb3426658d8c6dc8b6495db391de9dd6b3485b6ed98952ea6e69fc4b0dd8e433151a56014cd5c5d53806909e178f5cc2e1a3015aec285ea3dbf252d02f8e19f9621d56a086487a607561a0f2572b5a2c63a9b41dbe73466341eb383654a1320bfa64251b0c2c5fafba026080140791ee155ac694af27d51380888d4b9c3777b2c6fd49dfe2b4dc5eb3f521a74fc5af8187a413a233c2ddbd4e8377758897d67072a8bdd56d812414ce0823ed444028c98c93cc0f62aab69e088d65e1cc3d89120fdfcde0d5d38aa2e3155505180a0ddae59e6d7a33e320420c9f4a5c260326bed0eab2ea16a31cfdab2414a621cef6902568dc6910968aff2d60449851f27d154cd2fa15929268c3690861f9ed37f3a2079b8cbe000000000000000064755c010000000082a5e4ed0e79c1ed83481f522f404a319641aeb779dd1dabec04010102552a0fe15ffafe8ddf9fe59d8c7359811dcad5c9fa9e4869b58150e0290d1dabade1c0dbabb0af584fe87ba663596c467ea2386d40845055fb44953a7d20f20c547a040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000093e8d0eebffd723ae40603e23cdb84f1a1747d6b9ee4de89fb8f948a049352db4cb9fce800a11682d1e54ded46e3bd482b7291544ed8f3771df9f8779a5bee6ff3664bcb6edcec424c8773d70a4e582059cf18d86c9b83b019fd6b4b19c3ec97f8bb22eafb02a12ad7e8c34004416dd004260ceb1e44c0cb1aca811936a4ef0003109f17a115fda7c4e746ab81692b44ee1de8fdbab1a7ae4ef1dd91faac262b3f0309c90a1a5b97336e1501b730e1674ee221d4886744f257217e24f3773c5601a30b0f432345a23a6a300ef8da147ee298ef0318fefcb4c82bb6071f996752833c2d2225ea960a15b581729bc0f25ffd002f702433a7b93b30b786851964859e4a240310c93fe4eea72c2cc298b663f1f995b0554da01a2ef626ba568692a9f4ddb3e4022d0cf8af1b04f6050667280ee19e5bd66f412d83e97bb2162dc28aa8a6c617f2032760e1bbc3409bb57994b05dc25de41a0da3596564ec2fa78f6f2c9035fa93ca021e1385ced4f44a6f03aadf936ec00b4b4b1904c19de03bbd7ffa7da01e6c30d5020875cb10e337cacdbf05b9735b179ec54aab77687038a36d5ce21b20a64816afa46f4172c567a34c4c251f12b778ceab51702dca4a3ebc41247f6631503c164d2f459ca6d7aba5829f7391c197d44fe70d72a4128e644cff73df147b9c67cd310be33a7334c1232946d76a33921ed018c3093f948c0ef0a873a0fe7a3a3506d2ff39107666c873af40e17d666ad7062cd3ae3843bc3d9d8f12b4b5333bbbb3007e513ca35e1f2759bca413b5c274a7bb9cb8345bb2fdc9f6eecd937fd28e3370284fa7189f268a7478e9aa353e364dafe696aada14c2ea3e1b59ed5705bc9a67e2a66d83d0d78f02951b8c21c57f3d845842751096e0ece04491e096166672d66cebbfe1c2d38ea117d0d8c1cc71d1ee2d5fd66852c721a127f165801db752df10ee7083bbcbdeb93723b9695b1aa2a5217550c80da8efb6ce5b98b3ef12b1f1b2e08e92150ec25f8b58075a326f0016509c1fdd21b4353bd68da092d1a2ccdf845d931f7526f4f8bcc03d5c4b3b1401396bf886400a2b7cc39fadb65c552eec1da43b320575900b70fdd72d580e1d6aae276f5e4641e6d0038fd5681ee540643a6a022eea71da1f40eb084ab33ad083c1375445251d5871d9bc73c1545c0436b2bcef176aa9f9cf57f797913c4870e13aac989a20b2f51dfb32b5621b6e875a4c7ba40c6fadccf79944bd25bc87ec6a2db06926d20fc8301fa76d23fe7f1e18d154accf68e1823b7945870c8c4a82c709e0ed6b2718f3c85cc519ee9f1827def2bef7ae952b5bdb127da496e17632ede046a5b77456e0f73349ce07d28cf06951c65d873e45936ef2fb7300e5323b3fc4e92d9853b93b456fa8184e9c167700e22720814be7cdfe0995ba0331f712016388c0ddcf8bd1da60900b3b08d3e14831508965242e093f461a8b1e7e382b51d685e8f9075c50d5ce9448c12d06d016e4dca8ff4046418097ea1375718890df77f638bd9dbfc4cae0407cfdfb2058f35d14579fc49b4e695c7d5ffeeabb20d55a472c9489519be94bf9d98536f87887c16055a96d483cc72d657fdb2deb1a088c04d27407b3678c74b16cb45c012639632aeca6e61e94a826881fac1f421e3249b3e0888d0f1806649272dd55bee35fdc6ae9aea49f143169f97abb9bf55f9ef5ed009db96ac765e5b2d2ba09350c90137126249f27360474f2a4931b26a08ddd4ba98278b6f95516bc51aac5e8f4d8381be4507c9c24fd7ef9db648fba63f306392ac5880af5b1438cb716d1a805b1dcef9a0c2c9f3bad12dc62a1fbaca2f8699f5e3b5a3b4400890a54cfd7902e5977c2a4384afed777e3549fb5dcd8c7cfdc503b1d0849b9520a8a40218518c3f2acf84b653e3ebf1d176d0a4c2d0ee6792c8eaabac94dcf896010e4aba3d4b91511d0698fc2e2d04b83c752f131b63c434a761544b738ecb2c38abfb398c9e2eda8170758c00f211143b61c68b50c64bcb756277310e0c8dafa0c2001f027928a130029a4505e23d3650c87a9a3a03f52c010727b45237b089ff68a5341a3c6fc969482da4a2e62dbd0cc175df14ed10c1c540ac27dfa39a4c50e04fc320438df5a22aa91fb1d8b7132c6c3c63a06c499c3d26d8aa41285ea4701c165546fccae445c13bdd3b29e27d145363acf3dcabf66687398d325b8385d2990ea7ce5d6974befaa3af3578dc2660080bdd17fc3d33be05fb7f7dcd53f67c710381d91240301ba5aa471f98573463bab0a40e5b9c5fde35777bea1cae1843cb3129a60aac91cad98efcd37b637cf5a21bda00bae9f019e0ff2b43a7df8420a524abfd179621c85976b044d854f4c25a032f2c3408de810c0b7b0d9d1c2fe54d661ad402f5d802715b5a6925b1c8282296253c28713c20b", "636551acac", 0, -2043176623, "95bbc51bb2bd0a40a74bad3102894ad63b9fa56aef94d50bfb298883f783205d"], - ["14b505d0034729f44da95b72469c1919325d531131bb0e99689300071b2eed73c8113bc88e0000000005636a510000e91989f9dd19db5c8d12676dd08d8013ea6c606440230a499ceb051db1257b03398df5d70000000003635253ffffffff148b748b363109f789521a1a0d12e19218a364e1865fd59a9c2746649b03d4a403000000055151525163ffffffff0406d365040000000004525152002e932d0500000000026a528c0d66040000000000fb6ab204000000000071790a41", "", 1, -1884621492, "dce39e723fd59a35713bb718d815352df04253f30eb3b1792aa2d48e4bb0d5ad"], - ["6620c602032ad5a8c2b24dde6d1bc0bc4a49e263f7beb3b27ebad417b5d855c07ea6ccc7c10300000003516353ffffffff9d47c2ce3f709963f0e19cab79e26d772252c501cda33a1607bd7b7518f114be0200000006ac53abac6353c96e464eea2566d6189de6b0116665ab1ed85a89cc436f1f3f649c25a816f0fe4c73b53e03000000056352006565d7f054ee02846387020000000001658ec1cb040000000008525153ab000065520000000001541a2c05000000000000000000000000dc5bdd184b1ec3fafa2ed7520cf862f0f89b0074c312e6d2d96965e4f8c934f44517066205d5b76cabc743e5267017b53ed66f4a12a2b74ab5a43ffd782a0dddea4499cafd062e473aaa21c0e1946083791ddda22359f981bc05ab8d8ce83d54000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b4a471cb90d72ac4a419af1fc42c6581358f287f6161afff81dbf0b22fdf650af4c8fa360025fae84f8d4418159a5bd548871f6a176b27b896b72ed881322762cbc9c797420599fbe1bd1bedef5405e78e9839a279c015d0761be7cca1483f01bbca3c317a0f9d6ceb4f84ce92ae5513f04a9d5c17de0f90b194fd559c7aac902284df4dd77949f9faef3bb0a73bea227460b41595d75f00ebea36400cde16aa00327655f41e6c37b9ae8bff772c0f16c52d2014fb75639e0a2ca8f94d479d6f4a80a281d7ddcdce41e452cdebdb7a741b1d2e0b1f10f88ea19766a1afd7564085332008c3a5d2b88f25be24a97445fbc99d7f1fb0761419e7e2a791be3da0a7e14fa030949f5e4c4cd85836efa2f60477df379523e8e72124d9a242f08f80a2e2f53b70321702903ea84b9acc620c305aab8f4f184ce3344e839dce53aa9cd9e259f0ba30306b3ff6cc404cfdfac48ada13a645828e8f9a54f10a89f042901aca32809af83031e62cc5ee6d9ea08a5500c38b40aa3a56ad5a976603220ac086d604bfbd5a998031c332fd550192c9533a69906d06da8e0589b9d578f7846936e06cf49007498e5c45770d122cd21c0f471633004bd71b914a20beba940917255d357ca3f13835f81b003b73f4a66e791a915ec7b4a37f027de8ce221d45079f16cab5046f09b4caf25662526d11725fb97714cb3a306791872f4d0458580eed9beef263929c214f3eb86ce0f19969c0288ddf768271935519e1cd13a75b3e9c89b2de6d59ce2571364f5b326f05136af3b1d6c2b7b5906ab3cb57ec7005e3a719be78ed6ab0a379f764565c1d826926a28acdba2b9e9939ffef3f29c1cc8697b8a6285b40204686b7ccbb5c326c54d666890cc9f4701a57a3e33cd4fc5a582226477ca39dc3c0c4aefe953a5026a05eca65d8548a6b9e7efb7c699e2722a813d057aac25ee617c88a2628f60cc92ef23a34e4a6df3f2144ba4b6cd830d05816c924e513502542aef742caec5a81c8f343c42f527991f06239c4e37d2b29a81a8e4258538a97d1b5ad97d4bbba420605646507f705a5bc31405c724e5dc244f3498b0c44aaf8e079b8be3e4e62c49fdd2b5a2f5cd9c480f610d8f841327971cab5f0a3292d171526e9cfae9cc59b0ac9e38b9107191ca162516cdcb1cc92c357e74e56c386275008e36dc1d8598b6b6aa6121a5b39da8ab310c4dd6b32bcbc3cb65e6729f669ef32b55ff338965eaba184bc74d3c048e59ddfe2d960244a5a3f5263a4d64f072afc6398ee0dbd00ad7645636162a050eb23c7a01ea63ed6d7e81ce180ba449f58b1f8cc17c93611c10bad4538138890ddb9748cb00067643a3597f9ba946f1235978c3b686f7cb8900534eeb94de895614effb0497f4dab85143306322e6c3593bca675a1c97205b810ca955a032778c0f4f2b9cabe4c54da07d3e8d17db728a56abe921c6ca88655145a72413663c2375f0b2ab43dea81daa76747c24c4f9de6f411e4bc9560b625e08aa28cdd6e2eba342dbe0603f0d857009bf9b32e09fc886a7e80453953cfbc71b74da9b9d81e9faf0431b335b97bfba3eb9a01a7c9ceb93d64a25816db6d92925df995ceecd19e87f29bad369460b7a4f68f09eda656eaeefe8aad59394d7589749dbd18833f3c96c9f32cd3cc1b31ce86bac672441449b9c16f9853b1388d0c0ad13de28332bcb590a148bb7912696533840659ed4e97dac9cd65af7cce189c5ff686a0a3cf5d7b81171ce579a3942ebec348e1d06f30866ac96e79a39472b83038be9988a59f2aac229408cc992b4eef9a1f28f4bc2a355a77bba9df4de4a74c1b3fb4d4edec67c3350df95d6b75821606ea788de31f43bbf7511c58dd58ef09c2faaddbccdb376fdf3b407da3dbed70926cd6f77b718808f26f6d162af73b2b62f610a375e89d27d34dce0f2e3003c36e1aba360d776c9cdf6080971ddca6424c9f7ea4586931ff1bbdf2a9f9b2219412fa0248f33a33369dd8243f336cc8537b7960db9f94a57a64564d33e87b9e51a60dab7c3d42e718c327a72775a5f6cfd9efa0b607d05b9bbf3f53b5b89669611b699263ff7773900cb6ac1005ff71e85a29fc418b7b76f00f5783de94a29697740ad3708369d770475d0377669f1e865400788a3a6726e0169c349c103ded33d12e12b327d5f021091bada7b833741d0fa54c195b072d1c59d8b778a78aa6c259bee94f0cefc91e90d907a721f9fa3509a3f734e1b729a5ec85c80134c0945b0f78e85cd8d7748fa137f23a47d9569db50481e15b5ea4b862e4c078300858f79d44a2060cea09e7e3017e28aa3283234ed4d8d3021560276ba2ebe184afea98bb0d6c6da06128de4fae720e1ce7e5cf288f8a53712e7a32d9e347e4ce6c1c4b53aa8425010fbf45bdfc278a0214f5b4a2f8c31f3e561fa0d", "5100", 2, 2109038873, "8a42a93347bec043f620f773843736691648dc58121c1664c80e14c46ad681ef"], - ["bfd23ded01b04038a75c11fb19c9e570f5e0491400f2f6565657c69bf81f31e18919f297ed03000000076a005165656551ffffffff04f38ec103000000000763526a516a6365bae6ab030000000000a1bbb20300000000076300515165ac65f5384801000000000353530000000000", "5365acac525252", 0, 415677267, "fb383852c20a94f8d0f63489dada2a654454b9e7973c83850cf90609c048e41d"], - ["8b96dbae033823141dbb58b652c6f1031144802cd4499e1b7b707eaad8b31dd23792f61fa7020000000752526300515152ffffffff93548e23814b220601f05969a1f5010ef7a9f5138d63831a784ab5a24603282e02000000016affffffff9dfd37f3b352200c5e8ab1c7a421d8938c53bff2e30aa57266b926f19f7dde920000000006006551536551ffffffff0450be9b03000000000653535252ab511a576505000000000963525265525100526389d6a6050000000006ab6a5365516a9c9bc3010000000008656a52ac6552ac5300000000", "526a656a635152", 2, -987330185, "9dee181c0dace90d70af2d2ce151ad54cfd963e0792b05ab7800633b76fb6a7f"], - ["4661474c02159257ab033f526fde4f279e2ee046da0a9a039fbed896c2fc7689a1916c678901000000076300525152006ad9f3080e507e587b4d930a21fe7b96296505ef7941ba7e5af7b452f5e1bd2c7c79e651d800000000002d3fa233014f8a010000000000025352000000000241eab4010000000000000000000000001454c51a51a62db3db97f05f3588cfa73d42fbd8093721f00d6b6d772cbb206795b8d8e866f40245c7fe15a2a7684070b94feb2529eebd452a68c1b69cf6c7a3f6753869ac1e3edc721c62b6d9a54265247f06f5c0f42fef4825caf8bf339b6c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e5969b87fa98ee030b76df3080f6fb08e81349470bf6821585e0287d75bfed6572ce290ff82effe36cca8f4f5585c1c0f52dde3aaed30ccb9866d73a942c23bf3f962ab107b4637b2dc76412401f21eb1463ec3e9cc06bbd39826daa1e2a2d716fc78832852f68e9d0116c1ee5a03ea0dd1ee292e5dd074693473fe4ade83e1f02085ad6d2097dbbb9f92b3cd7d9582d421fcb1f2c5f75ed8f1ce332a58b8a630a03017e3ff909b2a0b685438c550056fff47c58dbb05ed7f89bdbd921c6e04c74660a16ed97b9bf809a998ad37d0346fe40fede82c4115904bf3c32850709c6d0b36a07c5bef747752e01d0afbed76c7c62677fcbb70035bb2664064c37b3be8af7e602111ebc5d68384885f73c2b6bb8b8c1860f7b0fafa4566cefa49486411fd6612203025df4f542f6f3b96fe15a6ae3ff297fdce71c04d2040e1313a612eff907bc03030dbd986008f73dd35a849b15c189300b36cc1cf21c03bf815b530a8655538024032ec9a3b69794a6b6d24c2c8ad088b08a11164b2a5bd497c0d00d1910940a5dc203015fbbc5bc2e21ac8eeb9d473b1ea497e7a2929f2cb7d18b140f55d85016c82467400aa84adcfa8be43b064d63a7f2e9eb9194979f42dab8ca50f7c1b0df159d02a1dd1a069b115ab55c6a6e2efbce8e715c566a1d1f4da3326e3f95d53ee033072921722d6634743ef2844a7b4c48edbd539ac91827655f345cbce5c4419e723474c5181c9e16fd8d88d01d854ce9aa2e49929b0d07e862985de0ac424f3d6afcc0a1c0612055be7cbb42acf0188b2072cfea1ad031ca35995aee2c59168a4d9fe6e64c156d46746be4c502d3af0831b87c9c304db9be2b57e3b94d287814b550ccbb4ce93bbb2446def5a111e0aa560daf11ee2d1905267cd7d37e103623a0e5297a09bb95a1c22889ad4e029753ad8c6c552b1f4773728766ce87bbcaaeb3eb1497c494ec61e49d7d5227f59dde516b89df1fdb8a523a3bf7a3b883deb3d8e41f57e74e4baab71cfbf8273e8597d1d97aaba5eaca53687d50704a89b609a24e2bc8e272d897c183407e798bdd9ed6b9ae2ae78692189872b5943ff791b260a21c7edeaa4576f2996ff836ee17d51b976482520293b5dafa7b8f656597b940fc6096d80f6b4e697ed482b9987d55d9651d258e89edc2774487a3ea795d1543edff7eb101960d00ed0401c6e86efd431091baa3c2a505062bdd2ada8d6d552fd1f1a08ced18321b3ca3b6a9bec658e7606bc1a1a5dee044efbb34fdc0c2dd68316f82164cb0f09b56ee4063f7406f2ddab2c12ef14cdf864254d7d9a647c326c256fa4302f1301bfcc030348937a187e071bc62603857e53b0379d613d2d560b83dcd9b678ba8f1fd7df136449ae71b0cf248ddee97a5bf62d00bcb580c0530d4d3d25a615c6cb86cc163180908587c9a57f85ef5412a2572c2283c356464540655da8b0b6ce581f574b1ee2f7d75f99609fa5d6e340e48c2f5199d9ec96bed474f892b3e3f436dc6fe17b68b1ccb58cc259c40b552581139d3abf45395e7d8958f9b169674697ac897ca8fa54ff4c894a5aa4151b154345c043405b26937525aaadc4ab7441b705e32996b484420ae74b08b89f05bc08a70ba8d56ca7cd19a72bf622df23c2f408507b7054bbf48bd177df0436c00d6095f5224d3b8bfe3baa4c1d1b7bbacdc14f1a0070211aafd86e27a99a77dd7dba81fbc48cbaf0a974df2245682dd3027a7587fc531aa57f4a815f95298696f3447bc105a0fa407d5b729684fd7698ebb0bd6469ceb0092affded325e2c50cd501a91c1c6bd95e5d4dc6a5bcf9ac5ff241cbb8fe85add79561ca900d89d4fedee4641f12b27d115d39c187463c78ec27c48433d5bd8e055e005b414e5e1b09f0f2f9410ef2708b5109082a05ce063d0756373bd6c7ea2a23011eb2a34811969512bc76a052b7d06b1a4ca408843aa70fe2582676fc948d79da608eb24465b1716469228772f082ad57edc6a419e723a4e86789b836400c5433829d8e648c10f67dbe384687050094f711dd284ce6b00b7853e6ea9ccfa0c825f9b7ec34cbaac1739107f0e2f19313e3d823635d637c0a4a3666a8be87b32d4993fe6a90e279e56416c3229f8827c2f56ed334893cf4a64a08785220335319fde60c93cac14981aeec7ff543bfc561a81168c203f586c601c4a1719f62a79b4c8ae9e8351535a14a619a9c170a4e5b096e37232c60e254a0f194269729328bb50bea8c6764dd94205dbeffe9568af72057e971eff130a271c618618cc09bd2b9003b50000000000000000f2808104000000001acb1811f3b81029c8b800a39922c372b44a9d992f41127552968ea00125f1c0380b2bfbeea0825b23b2fa5e09e4467e65b0066d7c0cea97a33b99b31dd5dce548aa352a026d8b05bec57312c32ef46b062211a052d75ab3dbe961f4012353ba000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fba856ff0154b75da19fed0296a5f60d1670e92696e2a25035f8036deae80856cff0823cb1449fb1693a80f61ba9b905694a2225432d692d6de2d815b711d252f7756a11435f0714d98f84493cb0fdcd8bc84bef75d0836a1a5ea7b4a83017398b1d2180bbfd02373af3c97b4d5d30d8a8bf7a94223d0af9122a97174a94f8503232bc1c516551848f755e3ab39bedd33d187dff34e72dedba9ddaeb10257d736032ddfca43aa50e81f3897059684b0ebca2cf7758ab34c3f87eb80b39071c1c96a0a2d441b9281e740dc9a37a290cf262668c0041c73b03c6b0143ec53c39922bdb50f81459b73e7ced760520744727ff0c02a18f2094105cba319572fd3fb5931f1020b603db58c8a7e613b229f620eff888c52bc40851ba99658b948bc48300591ed031a29d3b256be79ffa3166b9553bb4299e169f39ada05bd01f0cb1bac14f50454032b5718f99cc245694c4784ccab6702419bc652e9dec969c9fc4593d2f402b5a20315ca3bd814d57746c8df11152454fb2c1e6990deb42df925b1270c8bbbfbd594021e38f1c1eddc5b15d5458885cb2a0420a47d342869d621bf197db37fb573114208659b79205151bcb6356edaa96d956b11ae58047f6e2fef86b175e1df61a2a794e44f872ef1e5182f65b5aa2cc54c4190f6225a0929c6e6ae8d1192aefbfed195054d1d1e95bbb987c1bf18a49559fb8f8c25d43c6eacacb5ae4169b2137f682bd5363c2492fd8aa0b81ac5648c851ad29bbbd70de841c8953a4371753bb9d70f03d18efabc1100796f294d8f3e9a79605336e3ad21b5a5cf451f6f8e8a97658a543fc3e70dd81a0229594e10dfafc1bc55d7c762f332f8efd1a543aad5c31bc6d43f01e194430d6138c470d15b9342cfd0363d05669fd9b9230327eff2ee8fded5d8be6415c2ffda0e34b3dc0b716050f12437ca340d6cb2fc0eb522f5effb3f15cdb53dcf4875de438c724334ee779d6f4e84dc55b9a38ca7637ccc0a757ba1dbe6ddc8e9b4d98574a58d5187b6aeeb025fcc31f566c15a51ed9cb4c13597f124ff273ba6162c7446a31a31e57a3014ff7aef415bf1c82a389d1022575352baf37d65529e53048c675a1919118e9027238c52006e65b56d43e7dc72a210852e9384de51f0bdd80701456953ae2989e3f8d2c5b13152124d4742039b672b1dacab90317f457d565aa43f1377f0208904f2f32459a2cc763147907fab3a10645b621836fd1ea2971985802da35692611ed1ce504b129c4438160c235882cda25023024615b1e3cac216cf9ab5734dbf9455e47e73a8070c28d6b2e65c1d9b563405089314f5db8c75168376d52f351aa4ff06ee84103e3a04d2513935cd3576af2adc90f80324e184522f0ea71ead49f459109a3a82956dfe11a89636d028234542c0a415654cb723c7f27f379fbb6d1a0a0bafcb114cdd2a45a75d46720d151b1b7395b2c404dcd267ceaca418a78ee1240e9241c9de6ace5b2a40be92d35c59cbcdc8d215523d42e62944441aa33a6c523f8bf97fc70247b341e995bbaa059ae3c5e5951fc6d359ec3db6e6dc46844e952619f3b0ab2a7dc25dba7c82b7c502661ef46ef88a4bb26429f6bfe4d98dca045702f7cdb9351ba4c76d5382b6cfe36d9f5cd4534159204d18c702f5d9adc1297b8a09b3ae0d4ee1bf53dba43cee153723d51a66cb72ada65a2ae5d5da33776b7948bb83023e99548dde3b1455ae3438badf5fd5d55ad04a8b8a35c241bc631911096e863b69ac5f6d402075ca3d86f71d48c488ec77273258da9700e71deef7178d8272f80c6aa1626057e549eee37a4cb2127b6b3854bf69afdd1d94e721f0ec8099756359dff12b3a2097b8a6af16471d2da51b2bf5fc0edeada2fdd118cb9633731a1a1d7432ee6e67737cd71239f1d057f47a65818e211255d4af9350409d45d12fc8d54968b30def32843e9d48a4d479feeb218c056810641cb4d62962a2fca660b37d8432156a5488a4b7bc1878b5c64201686ebcb22af19368706c43904b0927cd322b09e43af0540e2169aa323ce15a39c08de3a188855be4683bf3989cc39d1961cc93fb10890ea2c321b67d3d2d506b4a66fb226d46c368ed5eb2af543a3570d97cf15aac65b52a18ca2188c3db423823dd89577e59c02f2b5fcb887e274a1fa6d62403745ce5a9c544c8220829ea9d66ce036a3db6ac13bbc024dd58007cb2e574abdcd51aeb3ced4114fea6699368a6d4e044a85b602b9e74e692b9c28678913dd8e7fa1289e25f680371e5b5fa98547ea385ea29160dc41f58959db7bbdaa973d9335034db1d5abbbff3387fd47ee8512f86fbf4923c4097d180bfd4a54e81eb044d6380a97de62539ba07a8e12c1c1acd56060d39844ce34ae003974b069f6fc8106ba2a7bef0b2ce0843254a85bc494d8a307b739866ff09", "526353", 1, -939854848, "2b814fd86a37a51ccedc9053c0a1633c06ca4912f7937e25dacb54c838520db1"], - ["a1e293ca028c9a9837e7940a3d8173f05910e7f5fd862021e7d1bbde71f1a2afbf82c21a0700000000026553ffffffffe1380cfb378489113d3b4a093ff18a44191695f8d259d48c4670c5a607159081020000000451acac6a0bbdafae04073dd8000000000004526552ac49c49405000000000263aca6717104000000000963acac6aab006300acf2bdcb0300000000095165ab0052ab52516300000000", "65ac6a6563ac", 1, 1855726952, "99acafe8241d54ce7755466966d471574ad1330e5a8f4bc07ab13b84d3ba6c8d"], - ["51f4743e01cc89a7f7ad46b09faadcec812dc6eca2486c88f70c097e595bdd4685dcd20c9501000000095253ab5200ac536a6affffffff04678fb0040000000000ecccdc040000000007005263536a535144e51a040000000004ab52635340191204000000000300006a5b4d9dea0200000000000000000e6b430400000000cd7ae69b909f6b657000add78d09757d181371225fa62d476c411e82d676a4feda6f17e6f632445c9cf0acc153f049da32272cb3b54f74eef2837b6d8bcd0c4b242940e62437132502eac0995e641ba2f0130bf1c100d8f89ef4f5295b334d25000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006143a9cfe4e67e5331155ebbf3c96da5f6c19519cdcd2fb66b75cc97f2ffe13e6acd053712f3b6fabe4b67194925a5e1f812ea8f4f76f7426b808c080bf84e8807b65812010ce63de8fc296b5d823ad9f67b37583e662a035f2c7f5f5ae561ef0f18acc4232e55c0ad176138c19a0bb50bb14e2809da3808c93162e5baa5417903103a2c0de040073ae9e4cdea0ccfbce431f0e096f080a63aa8d4a4a80f4cbe5f020b444f32d1c47f837d3e9b71c726c42d79cf4c5b05e278a3c6d960006699a51e0b1ab0e1c6f2e61af949b2477459a6e7960055b38573a209e5dd5a793f6aef254f2ec0d30602b3aa049881c70d36baf107e34c111ba77c8c4c4f7eea3b9da641e40309e9490db578b65b17889809d2a7d21830920bf83041c5e009c0069547edcac9020f0b6b2d2ec141937c57e8d176193af4ae6f70b18637ee1595fe3377b1141b090204d1b6c0776e20944402e55e6dbff03a6c3c25c5b9c4bdb5869fd6cd69a5b91f031c6dea013b2890e61c6edcb7cda76ea83665ef63f012681739e7d6ba30787ffe03277abe04539e02f659e60b34272489230d316867dec236424fc676ed2f4f9c92d87584e2cae67118b1e326bac86575fca134f7a266a307a5ebbfa67a9d106b6a6a2f791b855e700f125c49c6585a2661d89624deb9370f6d20c690f8ce1723371b1f030fc08315fd4a008593e4a7d8c2c2cf7ff5fed259773ea9e32ea6353a7690bbea54771b1a2fc5df4fb7f1998e23cb8492c7e9b4f147a6bca1751a3760f1751bc0fd41e7aa550c17583dd621068b878574aa28d269983d068405cb4010c3e6fc89f72ed1b54f7f8516c9662577e9eee35aef24fcc5014ffaf25a8fea471bd3e6a4c9e033383a5bfbe77e941a1d6f0f3f1779de6fcd3b265a8acb4a62675b65496d5c13208e747104dbbd1c6af73b446ac5cdb35d7163262633d5049d467efe224ad783d6cbf51c154801765f9a8ce6495f36452662befd3801e1d3f9799749c5dbf3e7397c13ac57f3d6e84748495a02ba64eb64b380154787a10cad4e29ad5354399499dfd5efa542909da7f9cb860804e370948417516c979b7fa7db506f6d792915e857051619c697df43941b6f1d6693ecac81a61a80fa29ad2a0d783874fa3df0010305bd8bb9993a248dad5d29d012a226998183d17f34aca45c7ff271f2c3504b32456514801f30ef819481acea2c2ae0bcee480cc9e57f910e0d0f602a0ebff4d4499b94e9835b7ab5842fb11d40ac8f427755ff21dd2ccfd716bd941098a5c312dd898e837c790df35dbba7808f875bb3b41be555d786aa8a8cfb1e5c517d9474c477c0eec5898666b2f03c42488577dd0f409fb6ded1cf49db14434b1981f74e631d5a0770873bd0981916f5787db954777016c81815e94795dee30681472ef53063a75c80c0dca9559526cb5e424156d0bdc18ee2bdc3bdc58ed59d536fed46530eb26bfa0016aef7da6b16a08d9ff353ee43d4afb359500f018188c2fd43e0fc295f0c5b6763e6a3d174cc983341dafde20f5373fae5c4d439d5e2014714a70f8b7f3d0f40c60183a99c33c09088e6b92621eb377d988b98d633376bcd1e33aaa2c84d49ea1d4aa5f7e7c75d2440b741d9e9f299f8617a0a895c76abf8362cbe982cd45fcaac82e43618e716f94b82424688fe38137d66d6b016db6c971804dee8b9eb547a4c0f148868f2091a9c564d6051409c1463de643a9d48ec8bf535e990e8345e9765ada822d7cdd71ab2370b057ad46af9904257232c49d81fd597ea02c34bd80bd05e9150843a7baccf86b4f7d328c8e615fe777980a2fee52adea9f1453707cc8f4eaac9a5bea6a1a0279ba3a47126b7bd71449e303dae4580592db3f56ed7d93e639b0eb99194df9562849ea66409e4e4f93927bfc161cff4a9b7972b853ab1fc6f4e7777045f92ed560a37cf79c2daa4210d3756f4e84bda0e5a953e7c20457de1d1051c9c7821f420b9ac8e2a0130dffac727542ef4c3f2f6f6400cb6dd09d6549111f72c982b0b2a539bdeb800da648c9b904e9c69308ec1f731f4699a1fb1e7b68464e513af702203783ad0a658d2a3fceb9e5bf178b83a323e9e883a6941405737acdcc98acfdd7f76506ab87afd829cdd584087748096b038edc6732ba5cb99e80b560ecf8d0859b9d87c0aab2e2959c857723c5aa7a5895640e069cf30a12b8fdbf1db00f78583d430bcefef6b8166c92d96ddc6962f99b3f7e2ac7c521c698b8fc7ab64b3d05d7bc063e2b1dcc94063cdbbb1ca25f6b3b909386cdd1f7266391e0000000000000000161cc1020000000079affa3dcf9e8c4e92a80d8bfa9dbae9a4a9d58dd5476de07425ce68a36f96b50b6ecbe6f821c76eae2cd8a155c4f5140ead62e5fd4888f2b725416d2d215e67c585785bca2a17eeb86b717f6e4778974820b9722d431831ac15629ca8e258b600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de998c4488e7ef48debcce399746f1725930a66d09a2b004d64f449f0edc4a690c6dd1b944880b99b0b62adeb0e1d0561ef274c8bdc86509cd1dc4762236ca6847e471ecff9ad3a92114c86a31fae2ea5256459225f618ee7b72c42f69e1d0112fc2cb3a989248514619376b1f74339749e6eda1d4e1988595ba1ed85dbe5b3503253668d90175b15de1696fa1a0081d504e9b9d5ec180599386c6edbcb189f6b00312ab5bd82744eb649e226503f79a495fb5b1903d0946ced0868b7447dba3ccba0b17941c198d563ca61bcb3db5a3e242d9cfb667546a29dfaf170a25201653bdad2b39418bd1a6673b88539b1901f6ddbb4a2dd983a1a4a95f21f6db4998d5ba470303538a22b25a2eecc28e06f2a20cb10a42c534fc8fba5024622108b04bf7d3d6020ab1510d2fbac965a5192d156e515ea43fc349c9843de429100d5cb9bc8fb875022f914c1edf125951b5461e84f0b82edcf1c605703b11f868bfc08ad7f9419a500308f9ecc758a346b506953d63089ef170be888daf98ceff81e2ecdcabd263b0c60320a069750189c99899897831d1491776625bb7dfb494c84ccbadb4efcbd16260a68a9880a09591ce8a70f90b152d5df6904475f8c0ed2886b490319ad3a9bfba4337ae40f3cd0e96b5256f01f39df8bfc8db341f81de8bc450074c9ced9dc4c31d33861350f74210605cc5722a8fe9c296f90c1953387254220973982d5851b2dd6bd09d070a45e40258be687cc8c2365e4784cbfe10449dac7f0be3c514103f33761fbbb8d14634d7d5651def1c4922ecc496db48c5554bad34c70db8d9d4ba0aa1116a7e7c09c74e44e646bd63462faa0f6ad3de445c0a05b2370a72c41c46500260c10b9f82e7cdee3777b7b7d3ca0e97cf0385562d47adbdc7681d7465e05d2f3d4f20f47d9696900e65903682f09bdccdb5ac5dd30abb3912f9ed2d436bfaaee80c074be73ce5ad13f18894fca0087b5fdf328d8e73d939cfe1a6a29d3aa72ca19dc19d651471e4ac028ec099d0e41eabcf8e3118466f529d52ab0842f1b61d88c9598ecae71ed0fac0cf2dc271cd152957282400732ecde26faed564b8949863109d96bc5bcad2c68160fa6a348b780c37f2a9db9b5bcb086688aadfb92533cfddd50e59a6d176cc9b76091313789c56be502f42d90f4a86e4d31513177482d2a6bec33cbb2be3024d72f3d2cf9af625759dfc4225ca82d0b64ee862bf51afdde04f0b5b4c4095b8096b09aa16e83a9cc9b623c0df1ffe35e5d09ab6c1b9e242365a622ec63cbbb01e06a75746122e5346e8f16c8a1c3eda13dbeae5b168171566c2637ecd9ab67ea07e2081128686f7b3fe7cef6bb4cd2a981ad2e9f610c878523ae79aad8bfeb49ec922de3a175cfb250c529dcdfd3082aca565b5730a478d915de6a65e5563b3ce4949799b0ac38287191feb197abcb0e917c70173d7c000ed1fe8b97677c1dd059bbc5dae67ce34b4a7fdb0cd8cc2057e16df82a4d4c0a4502b51c72b34193366a429127a4b138b68abc27208cc7a05348ecea2c921f04fb0b0bb1d9b7956df6ff3b7c022ec3e1dcd8d6ce2852029065e1dc48dd9bf4c4b0e49445b7edbff3b9a74271faeeecb48ca240a9fefd320fb37d91632da573f1669a006512094850d1c009128dd4c99828250435bf87c895595d6a50966750c127266059afa4d99cd718d36c96c85bc74da3fe5bd5e0a7f0d48b450e54409c7322e33d13b1c8f27c2dc9a0f339a7f13bdde4399e58d9289451d5581a7b01b456e0a21e9ab82c2f692e026f750f2859c43bd231c570b23d5fab65ea2fb4632cb70dccb60b97a774ac38e2c001aafbca08e03ba4e659fcfbe425fb9a4ade4978e0c39f497f590d6c7a6a65597f924adf8d7a165dca6eebdf0b5e54efbea040b4b4e386018231115fc087884534f166d2ee1e847728a6bad5c78054aa98e37dd6d2ab1e3a304364a454cecf06f37355d317ed10ec35b313742c5be0098324aef12b51e2df9956768d2974c89cdefaa3caffec6454f228edf74b56675a88663429067f4308252d80e9d6d9bead603b5e46c19ab9eb5366ec962a215a48046fe1ac16557173c5592f45801bab964c7991edfb140670f0903d5f537726f3f25d4731b929944913c86663b36552ad82b05800da2972fc768795169931753f0be221f8471307e61cbbecd0770ca1d7ee39ed88bf4495a8d62894835ddd84784f5ed4b54354ca335718158fd90b206ebadf2b7769163621353f4c3df57532afcdf96a4a2facc1bedc425df0aab021a83e9745475dbd86990432a739cac5623d154439fabbfe8b8d47001075881975915311b4fb9f08b8848c37aca298c3749eda4470ee29290598a9fbe2de10fb6e31c1f77bc3c9e204916cf09cb4659961d5ed66329afa589a11b45f8b88b6d4147d8d0f43f0a", "6a6a00530053", 0, -68433880, "be5aa43ed45a36b3b55b5051427930e1cce17d17c5afb474a3de33e288148e62"], - ["772d123c028eb5803fc214874e5883d6535c70656f4c887128dd08e3444f19051f682fa4a302000000016a4ec5e8964b500b6f2095c8bb5a0b6be9b3c00e6401cad4a757cffba93ab4c3a3df5f804a03000000066aab0053ac6a47e56707031354190200000000056aabab51abbb08c302000000000153354c7204000000000763656aab6300006476699902df36ee000000000000000000000000006a3858752253cf990dd1f072ffa512a04e0238445f76960d04dee1000b35bbf559e96e0c305b4b686d5ca8b1b4935c4508817d005909fe955e27caac536011e79656df3bb9d79f5fd7310e5d4a492f5c0f5ae4723840573ebb93eac09bd5e647000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ed285c127ef4ad219b9bc56e7c4b98f980d33732b05bf4a7d891855aa3e0cb9c569f26d70a9dc1e3da7e2bc6dc7b8f4ce2e0b552afcb7b22077f244fe7d9216667bd12b6744a5618ab7d138b4027074b6722d34a2cb4504b2a1490a067bb44608884ab882731f3a5ab1e65696affbb2a467dacb4f24e6813f27d4986b8b3e490215d1b37f1d0707472c72958b0fb49a6bdd9c89cf86ffedcbedf39838b61b3d47022322e2b1ef6048952524f5e4607d98da6898bc6cf13a5d68c5dd6c2b35a34e030a23c8de0dc1fc5092907e0ec63668ce159bf30ee2cbf5eaabccda58a7f018454c16c6fb426f3d329b3842961ef4b5f2cbed33ac8209f1d178bfd0c368905a023e02253e254389cdbe780a20352164190f0272a0d264056af034f521e217467268d8020408a141005db6c726ecd7a0cd90993b1727d617f61076ab5382d80e6fc9a2640325e28215db7f3b734f9dac2ab2ae752575a863dce81b7ce5c0bcf95d47fa9b300222edbcee38f3ea62168e2bdc3496a9662a3605acfa67f590dfd31693e28d34700209fd46eda242a248564851aa23346d6dfb37f66fd02aee6b39020f6b4e1a7ccdd8f5b5d7d1eb900ca9d44999c9baa8a35c3bbb0958bb3cd301e99cdbd0e08f9a4eaaa51df7c2fac40c3ea16c1e9523756f7c6dc0944124356e4cfb277951c70df9d89d1987f4a8ab9797df96442887465b512843409ca321593cbc6008dc7f5104fb89486e9627ee4feb582099c66e41403a89f1b214feccb3c0638cdab387b31ebc8c9445442f8c1d9f46529367bc37c95a48d9ed7682e331e6bb64beaee46637a5a61fd9ec2331b4d7f2cb68971f9b2ffa4451d0753eee04d977585e630cf5e7cff5b67ba9475a564b4ced32d6808cfa4360f523f246814f1a7d8d3f0012d16216f29793df41d68636691dbf8084a0a46d04ccb5bfdfeb5f0448ed90d1d1216e3e2c138d5f3cf9f452576ac0a131f2b60ae73715fd56d302820878336dff75b30a65e7d7e7ff8108761a14190721bf0330804eee9fc4d41b830edb268bc972edcac8b1c00342afcd8a7c37dbbfc9c088951f8509a516b4990b2bdc1b0d87d556635c24a2f3d1ee60f148c1ccb917cc305c5948ee89d281b58a39b952b1af2ec4d40214d3387d41e9ceac1cc0c1265ddf0d0f05af830fda5c128df3fe754c2e7ace13d6f036593dbfc21e997ce710102dfd416d3ab5f37889679250d31ca7abcb23b96202e92c39eee8aecf9c948eef20e598c6b4bbb2206001855d870c728ae7b82915d84db18ef3000d0814d59d41c8eb384255cdc8779ab25bcb480717c87da009b33f5687ea7e2ac877ea77aa88f1df27d3a8875244dd0b45377832d6aa7812cba4945c400e263f6b8c0e72c86a1073112c862dc6da9c63a0060ec784674e47b0bc6a8316b9e7dae80b06315d1328ea38c5689c5cd112042007fe0f0ae1e20815500943f681174540dc86d2a9f0c91a59c747649cbd57ee74ad85dbd4ddfb3ec64c9986808c964537e0fb7ed58e37b91a29f93add87aeea96a86ae94279b314d63d4a9254e520b954fee41c56e7716b5b39b0e1239471d930f25d7cfdcdb5f0f27fb5ac9da5a971ae96907698eb850b82d984e337612fb015895a4a1b97ed256847a6af8e9f58331a87d41fce1a4fa8cd1f54f05f38ec1aebd592d45293b991ae0caad76f39dd4c19ec839cb807d2edd2e5ffdab73bf60aa6b6c952dabf0968ad8e03b5eeccfbe5894ab8cd568e5bf8a16cbcecb0d6f0e535d938b0615db6c4c4e7b1e3de435272262cabf5e6e07d672668d9a29e31e5d41e3ca60a47f5600c27d566ab73f799c3c3ea9d20dab160e78d70a1a6e02d8ed0683a3af5e8c7e4e12ed4c596696da7dd5d9b41c83ed44c5d1fb18c62f6b1dade67cf72e0d303e65437e30e930806ad7fdfa76cbed3ce0d7f9b2eee6ae787ac66a7b54af8e77149771edb5e21f4f041929840f882916d72b7fc90dbb46c742a9e87912f0bc54d7d2a61c4821a13366837c08eb04678459752525ee73011fb00cb534558123defe0d1aff65a41eb46e7916b055878de3fdb4c56269cec4dc3baf53c80145138fb0098aefd9ea199ed8d6165fe836a861be55fcddd111dc409ee6737888efa8e887d13f58e0982f7f1cf73b8428ed08aeee801ce402d3c8dc6f9eb7aac2c1aed33964ffd0987f49ac2315b337d696ca88c73f1ba780005d4527b88198b81fbdbe16d41877122e826256c10dfb45fd57e2e28299f1c4fa042178dd9d5930fae61d596fdd83fae6db2630cfc2e95531f1b49160e0f03f703000000000000000000000000db154ebd17ad1f21e8b74a221d9828a4294506eeb9ba9c6471183c5f115f450046bba44380df6abe11f0d89c8a42db050f4ae4f9aa256cf34248f6a5dd06ad3be281072ebe19f24ce274dafaeb888f4705db8af7e321c227e4e9b376961af2c40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000092b1c5704dcf16203b3fd4899c7d75ccaa38ee2ee32b4b095d2d50a55ddb8de49554abe9091baa0d5a96e9cea71528b9c32ba6dc28738f1dfe56a2d63a7fc6759cfaff5c84e71bbc603cbd196c5343f6884ba000a1806a87f1c263f6a8d8671b30e43ac73d32e53c269636a3c165a6cb7a33374f4f7312e1be07f28aee3d34de03086edc321a55b97580d4e560c295cd68e40ce859a5ac81b2808673ec42f24d1f030da7f38cd6d272a5c5f32cb3b9d672abcc028770b2a7966913cd98f91905a33b0a1b4a942eeeef7b008db4047768a93bfab9b76396e92f80b1d7f0253f102e9f230a0442d99afe7a3925993b26f4a47c2d4fe5f16d02317d57c2fc1b940005bd6a030f90aaf75f567d93eb499026877a552a4ae43fbc1d1de82745a4f0256cbfc8d9030983737a849fcc5370ef563e2607cc6e5cf7b26f7f9166f8a045dd193830379e032869a8b87135f61ddda2a4c4025cc2c3afd7b93fd2a2d96c880352f185f5bda9031c5e3d557dd7abbbfc3b5f3c389432af100546b203effca5f834afc0c8c208fe031fe60c6f522b809a0ee7f1b5bb7ebccb33d33619c51948cec2a8a2c729d22960767c786d98cca7a915d399e7c1923809ab7a69033fd8af171a59246c4f2375ffb9430ee1d6d11c4e17b824da60f75ebc2936260c140f2938c43ec79347751f4c193b8119cb87f9630a897885e2fedad1a7560d27bd4b1de192c76da87c1bd3d8619634a6026e6fcda282b893c71bf1722574ff4fe6a5cfabea345464a24a9c862ce5b6dde45194ec1f17582625bcc4f18610717ecd6461fccae6ce8faabf0b584320b6dd635919557b7e540ec824352b708134186c2da0fbe835fe92db9a90b609699c7d94c50f08cf03ec23dd4a49bf850139e873c87245582c64e6fe2ae0ec1091a24bfc5578b9e009ba04564bed277a5385483d5c5b303f70f455b91ba25c3d8fa1cee0a997cbe8896fbac20a5d895f0cb33a40047414e4babf20d2eada66df6625dd0cef3147cf29b9046057654e36a42c41edf0341a11739562be32f4bed8d722cfd376c84c17871eeb7d1ab59da5e3c7c2ef19eb5d5680e6d2e2646a830edc372d5d0d1fb7b51a44f4cf63603dd444af0191a61f91f69ce70ebd50fd05ad8a1f24bee3f2a3dcac613dbd1a60fa1c721eeaea2ec3bf93b3533ec357f8d0f5a0631b0585913a2a12fd48436fefd3fe2901ec8648ec1db3048d4b4458a4ef3d47ecd99a8717b8a835be85fc769594433b99702047a52d99ef34dc51eceac29c395d619aaeac451b3a3e62e7a85a2700fccbe54bb0e677f0b29299a2f01e58adad107de7c2ea2403672714fde641066b0b1c1a04bcfcb57b74262c68656211e4c3f94823d275afeb097147b17825d5b28d5215c7054d8aae7b07167bbf465e2e5ff87bc6efd6c0d16b872313f355ae7f150d04fdb08475dd6cebf7b1b6e3f41122da6c49463708e3cd3cbdc77b919a45569b63547aef63d83f46e5f2f1102bed100939c986d51a49c55d4239c8e502c3bc947861e954c171c4c032bc7a58ec33059a541ba4c29d4ae8b6843cfc90dd16318417e579a9028028811b68d3eb3c188e0553f0485eb61fe86ba2c15811afa6978fcb81b84ba81fe03f986f7ed0a6238d1823251eb8ac040b9a9a810b530e7e58eae2ecdb184f06bd7992a63fd98c8ab55ef6941f37c229c6541764c52ffc60cc5883962769fde7db50e845105ce159153dfa0ec4fb37f1013463df57e173b6bfe4509b5448f443fe0082da1262494b256e29ee314813e63cfde6dc4f77fce0e3e4fbc13ebb27a4e7d2351c5fc2d0c0b99691ef7e8caedc358c22ee4a6706c983f49675d3397774b560c6831f1631787eceada2318ec38ef4c9f3157be120bb75fc3f7dc51ea8a04930b8e5a7df970cb4757f9a4bd769df4bc005edeb09e9eb6b1b2075ff4334714f878fe1a9d94fce0dee4df71f4acdb8fee01f7c658af90568a9fc407d5b39451cf1ad64ce87ad440689d1b386e0c1f7f03ed0cccb9fb9276850a3218e8965c31265a3c94d07946d6dc5006f0b763b67cb614b8134dede62a7f98abf669d6fa05a2ae69c9a4eb834f9088c4f5365efe5ddfe1d975167cd7d3821047de84a4d218625fb9fd80800f883d04c026fd130f3df60db2ea024fc124380169db30d73d113594de1ae5bfafa20e1585e5bec465792ac42110570ec8a1a1b20f6365de69d2d54bb8f37f06c713d1f1fa1f3f2fe5648815d917203b520aacd984187ab04c8a5fe5a57e83559fb6650c91fe7d76e10c23409ce19fd5ffc7916d598559769368742e899337cd1d437af0acedf9158eb5b6490edb45ebd188f2a9b330909387710982694ead7ae8c3ec217ac16dfea016c406555ab856142e3e9874957bf085a224e8c3f9b6d75dad5e08a2db484ba9963a0e2d8bf324eea04", "6a525152", 0, 1018266003, "700ae3e226e466219e7b2ef9cf74a9748984d22ff74f5d43a7f6bceeafeb8401"], - ["7ddb00cd03b7aacc765775e4f59fa3a53347b74b9730bf7d506c5d0eb8eac2f67c62be378c010000000853ac5265ab520051ffffffffceefa11781f8b2fcbb235981913820c9a2ec140205fbdf9b3c48767629a9ae820200000003ab6aabefb3c6c020904a5906479892d56e060774d8f20cc336e02776f3fa9e4b4bb44efdb19b1f02000000002e4c123204c1fcab020000000008535163acac526a63b54bcc0100000000055251ac006a4fc8ed010000000000582fb90300000000066aab0051ac6500000000", "53", 1, -2008903590, "4c960e20c1d46375e399bd29503b6488cb2b51d0db6ab010f74a503d2a3ef729"], - ["15c4851c04437ad35a6a3c7874c5e2ec24ef79aeef911f43523a388a5c0fa29315e0496d63010000000363006318abfd7d6e7aea4f82427d6f35f25191d0bbf045ca04d0dbb2b9f5a9d270bd4408f4e0e3010000000900ab655151ab5251acffffffff0c9fe60c16f51e358ae132f119afb6eb256663feb659488a5d21e2c0139a720d00000000035251524f8ac2b5a3b34a89fd9469d2a6388e9a148fb44e942ccb4897cf897d49cc354d25c31d6702000000070000636552ab535cf6f6ac0128d9000000000000055165ab65536f1093f801456e3a01000000000000000000000000d2624d64e5881abe9ffbb32d987952c31ccf8e8d3e7a886f5222571f730fb7fec301bbbb675215e3fc123b003a78cb42cfe1e708e1fb6e90bc4afff4898035534292a1d53d6e8a2e043954260280a710fc452a58802b0fb42193bc95aa78164f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003a7b5d3e7e9fb1ddf182c44335f3ca6ade70d47ad6273a68f3f4714d85e515863de3ef7ad41b8e068fcaaafed496198b8aa86569f03b37431e1e592d752c3b85c815e4105d96a2732053d9ee8f631dd80ebc36129a2f918bdef1709b024ad0310fed1ef021a5d65c2b5275ca4337a238e6886cdd5314022250835c5c4786a2eb0318c3d6f8fad30d001c41c93f02d30de5cd6c1e7bff349a646729bfc9e1962ad7020fc6501b6c38aa187ad258e68e0d29bf51b3444b05113d4bd1d4d8e6101cac360b03c69d8b73a89fa05eb6ee921bd7acef3cf6adcb69f027adde0df6192910c0af05bbc29a8cdd67adaebb91bfd675858d5bf386c0d7fd7da8931fdd4df12101e4021ab57c973eba01e24f0207bca4f88e28031bf35193af2cc0d9142e7fdc2524ba02266d6cabd9ce4b2b6ecfc25414685cf6bf822e7ac386b005ef8eb4426e458e8b03066e941e8053bd33155a1aa6915b2d3d4dd91817772cbb6ca6b5ac3cd3c8fcff021f16da04bfaf289aa3a2fb998f5742a342153dff341e3edfdd7e95a08f20369c0309daa0265d387ce63da7d1c5d69eb248b5b8d85f69806adfda24f10b5030ec36606012ad9d8e6b557a3018a335118e17126eb978ea617412bc23632ceaf78dbd8536f23d4aadcfceaf447567ee734b1f63ef38bce0500bc5e5d46513ff85e3a170efd88eae3c0a9ef928ebd4584f400db85be8ab36b5fc17d536c8ca39d88e29c2220a2cc6ab10b82017355a6b0123e0214c97c5287c735a02837368c7c59b12e70c26b2b3fa9f6c9dde5bcf064873498c112e965985c09e4f100d8a5b4c188a0b0bc060457c93015db65ce31079e0f76a3cdd10f104b097a49419d21146e0f67798a3af51977587d2a3d41f6d1bc27cb578748cf6e72137ed6435d421125dbf8c6e5c50c689d745dfab2d3cf1fcc10f121764acffa5c44c0299a7317f90af865742647b441a330fdeb2a4b62ef27751660bcda71a8f5f24a3eb207a9e5bd6a282166b8abf232ea10ccc3116d007996077ca9ea597b38df697683e13b3e3fdef019620001cf0ae1192028c0b9d7dfac6b07660700f590d017867b3087b92db1af23dd6f4e3e677d44a5fa174422cde5fc80d1d31e4acdb5011a036ec137e08196278255a6a5c0056d4ceffe60165387abcc73bad6b778842e5baeefa2b67fd2b15a704f2387ed2838b1ab0d5bb1636ddbcab57633fcfceabfb55d5dab9d46fb86476f672a2f8998ca0ede0f1e6cc74670b4b7d77a72344b8faaf04897bbfe32bcaa30f6b7bdf5b01d70b1e5178e59e77adae610fb01c17b54108c84f61ca5483fa9f4918ed06c02850b3c7add334579a333e4491f64e2bda747fb9bc9022d785545caf29fe1087cba29146a41ab2709ec817ad112b315b135686989412598bc526b6b72c21e9a9c54f2a26a5ecb80e0d9da23f7bfa2ce1ce988853d8d852f1119ea782bd0e5856992a842f2e9719787be34a584eec5acf0b9557e80c99153a2999deffe08d9677d907476e6b93859caaf2fa430adbc4032c9491653a625ff4d7fcc40dff10d665d43351b82c2bdca8651ec89eaa2b31c3f4f662210190bdda396de77f4c5a3f642b904af23419ea4a408cb74106063d98e558816a342b97f6f42942428135ce32b604d3b285b4c94b50588b059539594c91c36f14125405fffff2bf58186155a6b1b21c01fa40b29fabfd7bb0d6a9a57dcf734ff7e15c430d01899142b4403e07d771c2d8f9487fa32f8f3c23d13c773a196f09fd29c5cd17f92f6d6ee46df2bf8b6fe061a0706dce46b41d3b9e747d7aeb7ded0f18fad8312971ddc3de5bbaf72945c31210096079149e4f1205437393ca48e3f70e511ed9349f2b17070f3207fbcf444fef8b86f0d170fcbbc21547740e9d5f5727264af9711aea9dae640c1459ae03dbc12b08f44769f79845b40a1d5dcba80807ee7287c6c4fc9cde9e157d5fb3d33917aa1eb76aaf7fbd36766f170e1735edac1122ea677c8c7f384def718f2ace4a47d5fc5e64662f6721cea3acaaf2748d91de39c75def704e70408ba9901a402b182d71c0750384b055a00616d99cfd0d052a80ca8e7aeeb3e25b3889305f0308bf4e8b0dd6cefbcd14291d84dffa17bba29a5275430aaa3178802d89c3741e79e05657d3536340186aaea7940e4d1d768686803cf895e52d6529b91e4752d07d132fb0b76a1a770f6ae2958d69258d6247bc5be00722ac47f559eb205e104a274dfe3d3f14defefb7818860d1ba02f05207f0a6a4fbda568089c1260ae7e1dc32b3a7f462c1de2ee6f4d56a49fb4f4a53f1134b9178eb6f1c1d84f674df1270689888694e458b808162166f2d411d10aa5cad54f24bbadb2310d7781257e5dc26b3b630e28951f0ed06bbf2ab653efee09f92e3d8d3f5f18e15b2ac367b6908ffcfd26ca53160b", "53636365", 2, 382546450, "2500c389fdf94c6e14cc923a814dabad91f376a7a873c3a582ddfb937b62e3d6"], - ["bb91b9b20477dbee99867eafc7abbb386bdddf91912c825f1311e0cd33beca05980c9b49cf000000000552acab00003a7c30089f984d91136e6fa9432c419f14bdd247f61e4254f271d14bab8af1e3045befc9020000000565520063acffffffffc16905ae3cd2a5ce04d7812eae0011b894454ff305aa4bb6fc01f836af56ff980200000003635251ffffffff0d180123afa9277243dbd83e04260dbf2ab3497ba8583b4521b58ca4754241630200000000fbdb89160411bb5405000000000452ab5300524cf305000000000551abac6565f4110704000000000551ac6300ac376d4d0200000000090065ab6a006a5300ac00000000", "0000000052", 3, -56704158, "25cf50fd167c1231fd14d9befd5b7a4b40deff5cd14307432e1f97bf8bcf9318"], - ["7e241b7004ddd45969be779fbdfbfc15eabba52b197d4beac4cd8eff78e0700f0ac9ac09d201000000096aac00ac5353ac6351413df6454fca70a5d3cdbd0139c3d6b0bc13da23651668aa358ebb68fbcad3bb3f542a0700000000095100006aacac005200934e410c757db808288d17385103be9cb0656e9f5dc7b79130e0f559d4824aaca27254f800000000056aacacacacffffffffbc2dabc86ecd1ead0cf22d5378e1887fe4e6849dfc3e118c7a9bde1723dc41880300000006510063655365ffffffff031525dd020000000005acacab52637f0cfd0200000000066565656a5152cef59a020000000009ab6a6a5352006a52ab21ffc2740314feb005000000000000000000000000029e8a5c965af746669a8ccbb6f3bbdd8e40ab1af9179561e3838f9f2c53ed21dac86625278efb2ba834daa4bf65e582d08f460a7d219b7e400a3312a9ea3bb74320eaa8994b5653c64962589501723bf04435da5e3b569f79cc7822e871091400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dcebc4a949ceb144bc2207f3129a5d4f653f70d3046a6679acd23f3d22083b8d220104f89e30b846e234bbfab12bc536885a127697c62809199daff840dcfafce10e14d91b7b041d9828e11d92af5fcc1707d6b3729b8742bfde7801336f8ccdfd78a76ec0c57c517eb8aa63c4eea5ff5eeeb24f8232e1acae2891084d881f2f0206ea7464776b73e57e5ef5669582cb2ecd8bd7d50294c4a6bbc28e2115f3df8f03014920783adc89037448d61f8131a9cfa40d0442fba7b1bd2bd3e9fde1dfe2d60b0ee96380ce132ed43bbf3b2dc81cf69d456b26032d2e1a1e3ed125f49bb5cf6f0c17b3ab4fff901c0efc9b88cd3472a28eb8ce90cce69ec080147f02c5e04ae90202f1b836ca39cbe1acafaac80d4ad6b33c3287c698750dc5f0bb47e1d14ea2aa02052237546eba5bd7bd93c24ee5da333f5731341f57392d176146f0a91474dc93032c750477324d7b650a5bfc6a5d96962c3674c08fccda87b7ae235618762c52c4031341f0c5f7c1186faaf87bdd2237d84db109f943a4dcc55c4c9d8c66e3ded25d0324353ee08093bcf78fc2b8929c7d61337c853cccef3def5cbc3dee5891154fbe2c9dd96a4639561a9a944bd4f4824af457f37b1d97fdf7c869e71eaff1d41d09305b2795af276b05ca3ae7974a0ac9ad82d88886468d978b44950b0e8ab006716887513287ecc8447693a3f3c71935a845e53623efddbff39e1d8d0ed3b387b6f2dbb65ea6d83d9e60200ac574417f38aac38fc1d7ac1bf87dec292a0f70397bd1284d0d1913ccc2a23438a6d86f5c558c5d443254a0905a76cc6fb393bf0415238b511e0b5bd70cbca0c6d2ee2711a23665b0e626ef9614cf2e20cc2e1201f0347085fde046d428062c5a1225966e570c449cbc70219ecd258cc1e798c712b4bdb81309bcfe52596139838d33e7cfdb42386e9e5c5b2218a8bd9eaf168437cd732a54c2eaf0878d66f790b6e7b1564777b035a4962c939e1137d62bfbef10f6ee24a3cec39437d46eb52152af986afe8ed3b2bbc463306c1fd97fdd9f897a4559bd3f98d46272276baa85ae1df9411b79b9472bc9e4db84e56f0f042c6c131af37039c01f71b3b8aa47b0789ee89d9ceecf8d183d249ee29c46b9e44743c3811885c93b23bb1454e380b58788f46431dd5032a7b14d801348c2aca81ac7f6171733841cb6e9b139025056492472577014b8a0b79378b82bd09b430088dd863c6ab71169b58f72259a42e61bdf8bc08188fe8bedab20f60f639f43ad62313e3499122689b54886425a343726cd2590a5ee62d52e6cc066c949088a84a6cce6ec440e03ad06f0949e99986d228dd3ba39455d81ef3f7172d0f457dbe92c5083d5118417d781b120aa35a7f77033938308d1a7c199fc1487a9411e5b1d9e2ff1b78471ffd853f9be8d19a0b0ca2eaffa76e94d2db2eaade5cc9f3f5480cc4fb0bd9f80023a6a9949e0bd8d242f2052c9cab05b784e73242d7ac9e88ff15430c7290bf2dcc15ac09f58f6830035e6d11b8a6531ec97ebde2eb4034e57a5f4cc3f9ef1112260494750920e1a6ff1495da21b9a47516463bf84525e104c78420825037e143638a0b2031dde026b5a92904d0c59c0cf61439e3c808992fd9c6afd1190cd4a7621807929b2c555cb0f63713a4fd1bbacfab1a9a4a210d668a92a461dd6b22d7a285c34918f837d9a5b6248ed2c4d938e456d1a8f47e1b6a9a10ecb50d67d51e4f86e5119dd18c08ec34234b60fe848dca1aa200beba03418a2a7d9e23dd5626a0ce33180562cb9a872e3ecd39cf21bec34167385826a911027d914d11a53a26b90dc18e0ac014f52091d436436fe012e2f34b0e937c04c962c80cdc1ed88b183d876e10e35e5500a3043a59fda68e5394330908dbef26515b80ae711146994df8f6b33836ba44829279ccb3ef8ae3c5199e971340302aa67a76964d6c3899fa09ab179a032b8cc4a9ce4b135d1dbde1f6380afa4f5be7f949fde638eb48f680438333eb04f8b55ea69eb575e6819adb84328d3bb3757e4e94dae3516d8b9ce457bcbbbd28265117a7a79bea82e268c651df22d7744fe76e7da14d49c1f2477652724166b3db451f52554aec1245f6dad06b5320b9af287207e4a2d503857b7c0fe62541790a3664790e1826e2fbadf0a88d7c41aef347fc96248c924e4482ecdd64b2dba3015e166d183050c79aebaf7a43add5daed42473e69d675d07f644895b63260619db4dea7003a2c12f38827b181f30628254b94b59887756de03e3f188a6e3b5db249d90df5fa7b03106ff5e308d0500000000000000000000000081ac3cb29b94dde9452e1a25caa9de17c04e9fc751d8ced86c601bbfc4ffb44570c22a6179ea7baa3bb47e7be898b5858245025e58e081d956c9c661875bddbba2abeadb4e57d93163c3567a0de7114487e146ecf262b896b998fcf0d523693a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007adc2ecca302d8f52ae021cbed6f0470bcd68c29a79e1f20642b5ffa7040448fc7e8e63cea84df67a417451d3cfb1a025aa877d47e7fba661f8cb70aa67cf1716da3f65d35fda909d2e09c018a5eda4f0094820a6c29fa1399ae50827b11aa2e142a68184b38416447aab7c8eb2048c615bf4390e390efc05bf15a95b545f66c021de4bb81034cf0b4ab292fbdcdad6444b6b7cf2278d3f07b6e2a88d0555bfa210207e675dc610b680ffb911b7be7e48c7e4bbc1596759d5f831aca4a040caaa65f0a1ac24cd7806fc9290d99553deeadcc21753555c644b63b682cc36fda5bbc5f690a2403b8c0df8835b2f8df6768ce46b90c43876fdf3871dbc51878f72cae608b02124b75b93b197e9ad8c22b637a222878fe9c85333b2fd0847cb8806d9ca86b9802167e173ce84eb215a713001a7d04743e3bef23c0fc8a5429e77daedd5ff83f99020050a8327bab6fab26b03de06f5c9f7a9c489f6b925b1060db630916f524dd2e02000654fe4791265026d045fc91cfb3a796ad27876c6d031185edc1365921d4bf022e8ad88212b92d213ffa58d6443020e55090efdf95a88b7a2657521d8c8bc50e3b6874c9bdfbaba962f85965913c7a4da9a004056f6458bd80457c1fe00918c94a307805770df8b0c503a3d65683ae0c8b8ca0d37a54ab8e26f98f76c137878879ac7179621a84354d4a950c6bb08ade766779919e2e16e11c28c71d714450c9c93d886b19ae7fad9775351a6d173af242b87c0b587def8766e82b96ce16a72ae88a2b783306a740ca6d58b9cfc1a46df6eca672cf545a4ec8dc0cb4be5bff91c3e5bf61c485e1d4241095cc74f1baaaa41cde9adf10a7957b9f27362d8f1df6430a478c65fa8a5f61ed34bad83c7028305e40e5d208d379186c6fe90fcae9d497829b5c078893ddd49619f2fb8eaaa48c1bade05fbafc050a00b678a802197e43eca6c4980b900d72da697006ff34289e79e2b771cd24488e9e74935acf4f3451b43c84f5efc35292cd5fc8eab83693d09a1bf2590b2979fee8bb006a17f1a209f4fe99ba2d953389118d262bc7fddefbc6736a8843718f362b5cb4bb733ca1fea2a56d5df376c80403995ee2b3593682c566e3084ebffa8649d68b33254faeb7d979c902b91bec3008ed3563d6b5b4ba2197d7c1f39f94a54a2b7ad34833ef0b85894472e1da3c13f5802c1321c5da69a63deca16bf0bd625f66c9a935412c6af313c9a7442e70052f260e038a0d9650567acf3cafd2a226abfaf76ba49585d8aa189c380ae3dee67d72dd43d62441b26d08f444b0a4445887a0f4c3e6360483cdfbd9a3d4cc2001c81d74ea396103ec22c7f16657d889c156c5bed4bf844b3eef6f2a843072c758f64be72e9b9c9180dae87883c79290a5d26fd271d69c1d4b133d5f39bb754b2af4741aafe09e999a7ae2c6c206ab5447b196dda8cadb9b7d2f7eb06e476d7622c8accd43be28d33a20fa77dab28bc9403cba376c6f7fcddc4bb656563bdc3bf64934935ed7d925a7725661293865f7df7bd09c7346654c2714e2468d89d4510a5a7d7cacdec445880e0c03a83917a64d6adc766f4144d1ec47e95534693e9d50b9ce302975cc6a54fbd28a026e3a355cfc3c0cb626d57ed5244fe3434b71f08d3475ed295bace0b8ce73e8f1f47ba6ae7a545d4f16c0ec289597618a096207d63b3adb68728ffc7e503c7bb785a0fc7ced68aa1d34a58ee495ba9be655b5dea6cce045a26caf2a7cec703ccd5ca170e9fb736ff4b4811c03877d90fecb44dfc7d68d5a6e989a1c0c03caeccfc80afda46a25ce6252bfc7f0827b275d7d3b45b53116c2fc9e93ff898c77323731ed0c6e2543de1473175a501e658fda7c801e6306cb5b7856845453bd43b698c217e4e2150be3e1de7335609d11c1477591c37b3b2fe80f5b41123a5500bc7f183fe96e150d04e8654b7b6fdc4e7904156d4fd12f61e54ebef5f1fbb61e58ac2c89a4f55f9f743b3337fc43dedbab699ba418346769d60b3ad9e5989d23aa92d40870ff781d0bba2c868599f1a2079f3ae2dfd761bca7fef65effc6a780d624eb25db3936c4f740bfd6d27e2ef5aa2d4a3eaa59a5c7fe24cfdcf5d8993b689445dff86db9e7a6d3cfffc363411a867ffd57adc776e42cac589932757f6d4db90f1428557437fd9697a5d0b4fefbcbb98820b242a689e5adaae10ad99e0f6dc27659b82e2ad69dd10a844bb6a34a3e32c49b9a336674725ff9385983668e6d59ea34469ac8e5c3bc33378d20042580e022a50a7b3082fbbe43e17eaded73701e02000000000000000000000000ca8a2ffa00f5098fb1f1351f3977ad2ff3dd2c2ad32785f6d829eeeadef4d936112dc2a3c6ca6cae07bd206c54b82c79c4b3ccc6193a245dd060a389198dec1f2ea6db1b475db8a9248c313d1d964f79d1d29afbc2e8eb013b4cc8fe0fe496cf000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fcac7fa49fc34c7eb7275e7bac7d7c74064de784826e413ffbb6c5c045b28a992fc252dcd14f242c3dec56c16addff19e0db8d8942ac8fa70f390f44568282067a5e6269a227f61c0fd9ad185a8ea537dfbfe7949251be78bbfd92fbed9a9c0514142d8224a491afcdf3e931b8f3bdb1d02529076e96ec08dab6354def0b2080222756d600f13baccb593d57079fd241bb1bc6cee4005075da70eae357fa3be1e030ce583b1a76376d0ffc1c656b56f582af9cc7c9a75554e27fd458589fdf4be030b09ed4cc4cf97fc84517f40f735485896cd41ae89cd61a5fee3123574f835185e0dfd2ebf9a0aabf440e85493ec64666903c09461933c78137816918b6d133220022b2bb35f8dca3fa547c70a39abedd1a22977b2a4448011efbba04276e63d57280308547f72a8e4ca8ef2843c05514507e982eea7565e80991978b60d3df57392a30300e87b0d74619605f03f62743ea723a9397fd28c043fb798f541599b92a9153f0314f39027db745e3fb7063da8904a74d24e561e882e464b0dd20f990912365eee0212016bd95f7352fcf2c6e117e2742c992f07879b63e8cd5d579aecdf4d004e8306170ef47950e0394df0b4f77e16970275dfcb3f78e53f63e3315611163ed874a6dfd3ef688ab985a53d569cea26b89a62774dd8c5c52e2c2c2fbddd04b683dd30091e7c3f0560ba66de10d8780e35230c9560b32343a111dfd6c52c74fa9e70c007ba0d3d7d76960428748105aad11a5c18c376383ec7148a3e70b12cfa50a6c960c89fb7000e6f22d11083a47427c23eda5f7098b4bb041a9c7ca4ec63897270d8804ac27803fc584ad1ea535192f925dae90ecd97b21465f92dd482576492222c63bd47402a7dca1313a6aa3579a4cad1079b0000e2489e1ede1fbbf1a1294600836b60ca080d28964f0c857c8973e9625a40eef2063d2f0621b1cbc385c0aab8fb6ad780d0afd01e61b603163778fe75e15eac12d0805f9fcdfdccae2d641f3e65def4a8a9f5309d5bf10c4ab9580599a524590dfae5cc396a1556ba2835727541b083b06c6523faa3aa57528b81bbfbd0ef588cbc502123f396accce66a4a6ad86886eb9fe2cef12cf7853c668e1befb46e9514bd0205d49b6a248fac6755b51a320beef815797eb4927ca03df907eaf7b4534e3184792092ad18a1de61a99dc912ae442c19ce062688edf4d006dd03518f0cd13d8b898537ca78b2a6ef3599c166c6e49e639f2c684430a599f28edc096fdd1018c694558b2e5db12266f47c3882dcbe8712b1ee9a5d4680fbcf102dea1dbfba912a55c1da85aec6b9cdda321a293e01b7f7db858370f979e96cafd6fc4a9444b11c3b3cfeab8e30b9d8de06fb3b2b9865c1358c93c82070d29bd1dee1e13ea70300728b7bf739f82693877de75ee6893ef170cda65c88618678de985a9d15af7e2a31d2974dca46a0c6fa36c0a5579efbf2e514d9713e5c46e6635f3134f6ecd5c3bca9cc230c1caaae02e7d1536287887b546c1522d51c16f5645a6dd74a73b3b5b8fed47cb4dbbd182f1fc20a5fcd342a299779dde5c82fd2a4c33a6ad78917f2e1e9772233735cb27773568adea50b1caaee0b34327d08fa61cd5f90fc9792a1fb0fb7a827573c21fa674343b32f281c9067750479b58a80e1d878448883009b33b723a7930031bda87d680ed3c7ad49726cb0bb47e597ba358f99ac87fb4f231c8587ffe7c2a0f014aa0b7074b99bbf8dd8ccddebd3c352891515215e6c7b97e4720f98369d7371959dd23f25eb4cad950091f6f2bb1fa71041a2947d661bbf2f7024ca16c056c6f2f297f8eaadcae3fa7e2f0ba3c1211aa6476ca09f609464566784624f1a9848ec20ed32bd107eb87872c9dcf96a8778bb65649794bd795ef5dca040a46df7ad98663db173735369cd55dd9f3d6418b4598cdeafc07581db4160bea033af55b7d33231c3b8146917f8d6ac2124652dea405ec3e75cca5341d9d99b9f49ce28efda6bfc3e071a78b73a11e9a0a33cfb7e6d7e02e86dc72a104fb398ec4d6588f57138987b73d5f1727acb5d906a8eebc24bd0be39f5262ea8be0c646625b706b8be502f6543937ec816c152e260405b3deaefeac746a631f4db4e25a7ebf0216f0b4474bb16d83494f723876b2c417fc71804ca3ca2b1c7f1333322aeca4456c59738fa057855d7a2ea5ed2fec33f9c3e989f2ad5d68ab3536f9d154682e776d5c6c916da0601588fc681c37a848b126883572d15d7c4c9bfb44a9ad43951c0c01620286eb89716e75dfa3ba878f72efb2a42d376f61906bd9ef23a48353da5c611f6fb6dd885a5c8239381bacaee8afe555227fedee62cf336d42620a291e74aecc0ed4f2afca1ffc54e5c6a109a835fb7f59b5d93fa6092e2e95fdd8e11f404f8efdf76646095e1b83ef5d35f1ee80d120c", "635263515152", 2, 1521598844, "63f17a162b529a91227a7d5495fecebc3d0ab437462eb4a8bb8820797220c8af"], - ["1426f2f30135d87812317afe89df6eddfd56795680dce37192b4df7460001a5822a11903aa0300000000ffffffff02192681050000000000e1bad90500000000056aab516551b005ed06", "acacac6a65516a65", 0, 176944165, "ce3be5f02499dccd44fa367ae3ccb075c2e958351f61ad9f7cc6d86e8b2d3d21"], - ["b01ab05c038ab68e6776c73e9de8ad9cc51cf6ef93ef47fcec2098f202676d2c96b71970e7030000000700655200acabab02e25fc6add75d2b7f7c2430db84a53e5d3121d805c9a13e7037a5e42c63fecb2d6634b101000000016affffffff9bb6ff47d0b5c7b2c370ff5dcab03328a225eceb3589e1ac6aa55e506e7683320100000004516551abffffffff0492c4db000000000005655353636af49470000000000007656a520051006a82436e05000000000653ac63ac635315387f01000000000351515285beb87e00", "635353526a52ac", 1, 1746047635, "cc3c7ef736f9ccc3fc6c6a2376217e75621d68c7e5f8a5f0ae7ecd2db692a291"], - ["0baba3b00365ea480b3e62d6fb80fc9ceec16020a907f0bda1974f10d6efa2dc4f6c2a6a2302000000080065525100ac5253ffffffff59ea0f0dde3aeb915e95cedde060f3a080daa4ed45f52638527a9d6eb02523df0100000000ffffffff3ddd9d57929e43ad0bdf8dc4c110144bc9e61be071314de4390f88f8fafc7ef50000000008655100ac65510000ffffffff04cd1e5e000000000006ac6a656a526563b0e702000000000865630000ab6500512ff1a00100000000086a6a516a52acac001ae4e6010000000007656352ab53ab6500000000", "ac5153ac52535200", 2, 805302827, "3e0d21e1c255fb5827183b669946fd786aa206a66101f8e86f2ec762ea9c833d"], - ["a3f0c44d032974b876a5484ffec7a600089c32847471911008e7a2a092138300f9f701afac0300000002ac65ffffffffd7cd6252f3424b9221e1b7daf14e153301460b3ae0b6da5f2b5410b596eb919e0300000004650063abffffffff67bdbebb64d2dae02fa1837342d3c7daaa20b8bcdcdd64a0fa5117785196b19603000000085100536a5251656343ec53e1049dc23e00000000000100020ce6020000000005636352526a10a6f6040000000009ab00516363abac63abd76dd0000000000006526a005200ac0000000003000000000000000006eae60500000000c35d50a1dbf8d0ea78b3002a39164408de2c40782a41b4be06d67eef33f401a750ecbb38a349c9f3bf890fdc661deb496be431b015a0e43276a9487976403c1d4e60cf1c29001b4d53fcf110f11fd1f575aeb20388d66710bbd5548b4a230fb3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005d5c7a3686214b61536a735e3ae319f732d5756dc9330b594bd0d4ca05e852490bd79ee8dd6a02edaa642e894eb8fed920520e9d62544c8e59c15184b14794b1cfffdd9cd356f8f510c4a0d06361758932c160e2365fb7d300819bc31fc857d8bc5f2e7942c8b01d4ee869acb81cff8b5c1c03648e89655265c706bd038d1226022285ac367597d10941b3c6cc75a2204a8ccef09eabc4ed848a061595ab580d1d03176c83a1738ab7acc47cda819dea0bcda82d677505ebe0d35852fdf201db1e3e0a25c7b609efc7db6b6fb7e6d70b228ba974fb5ecd0ae1d2aff38fc0475846d34c11d2f540622258bed6180843fde407db07aea2292713df0bd155b050cf1300f2030a9f20fda0889b4aeb8ab60654be996a5de769e59a72033c40890d1c012437430206266258510d889da1cf7e37534f6281aacbec1677040061551471eaffdf80f60201c698f9fa9dd6cbfec167c60bb54f3a4ef9f02a822d68c9dcfa78a66320d53003110ea432c9c269d4f316f027d9fede1aa1da9e9e78a6bda4a7466b9b84044fab0212b5dcc0e8b1632900d334d8a91b0539a7b4b9d2415c4c30cf37417f423753a34a61116bbd2d2c505f83d725525ee5e1d9c61935329074505579d5093ae241b34bc49fc9defb02f079e184d9d9b27e53fc08dffda79bb089b8d9ebae88a068d287209e8667e4e5d5c112e9bdd2b53d00ca7bf22493e89f2023d46897f66b88a03d7bfd6cb47f4a88e2d6e3fba93c52d92158c6a736adcde1f5dc851ee1c43784309fac45771ab715f771688f7ed5baf93e1e7137f51490ba61c90b713d5e09b0db92ec02375660f06c921cb3d0bbeb67ec3f63c659f2fa278adfd874d3db7b21f800235af687e8e64890baab73c09be3891c71c12efeb1fb9cc7f2c470cf245df4db2c3eed7d8c1c4d0a8baa84916eebc21f1b27c439507d96e45f9591071737418f3b25fa2648286426fb1dd3645f7ca15c3c38170ba6ef26e4b0686c55b7b964fb02958c58abb07e895be6ac7b0d33dd208180066bbd4d4518a2a097c781578c1a6bb0f1b818312e07f4c8ffb077ce3a608c476a1b8a8945ad61bf5e76b9fe54eb09d5b701afd32eccbcc7e2fd32d744de21b8d8cbe3e34c4950df85b1a1567c170297b5f2210255d21c4a892dfb123eb565635870894781376a19e097f18069e24356eac6bd7519c3115bc72ab6e37e67f431d1796e9aec2db1a7a0db1e207804ceb204259f488c23b1c6e759d6773de99a73660828a41b8ab661c7e4dcaaf14909050c51e53e6d9264803fe1bf5dcb571cc318b6d1b8e5ab4756da60810977cb3d773407d5230c710257c390f89b53d42d3509fc23eb6889c03f09883d84b2582c364b46eee43d275c5dc2cd28fdea71b1bb9499633b9cb288cede902402cc82be41942c799517a52d061242d9cb6cd94cd472458fada2f7d7a6893d508fa90030155d9385f97d5654cdf093ddd43011e6296136cc27443002371d1a623c0c6ea471bfd72502ea4a58bca895c52a1547495ba9591f9329b1feffb89f7d7cb2bfaea47c38a27b03b888f5ac4fb3999fe1fba8e193d4dda49a94c601af0f68c7a54ccb9a3fd555b97fb7e5f8e9933363a353069bf89ce0beecdd467d00988038466f904aa50536a05a9124e3a4f2b522d8e5dbd0ae8e17a9a96ec55acdc9dbaf8d26ec19d25446fb8426737a4546c0783f074b0a5081d00e11d41b36fb456092ffebc90dfcafe530ea2b45a7cf3f3fa02a73b92bb9ef547c8f473554fb72ff3af43ca010eff18a3103aff9071e93b6f1f8d57c7fcb43a972a22e4ffc39c7524c29bcb6aba0e58693eb647098bcf5089fed1eb68256f5d6e99aa056597fff3e34211d1069990080da81b82e83a8fa65751bf62be909afe2df4a2ca1b9c7790e8c6174433b290f6ed87d242592992b14e154e1f00f70b2ee542c0b729d71f96e59f1c291f2e200d6659f484159f2375b4b8d5c0da767ebbaebbbd4480616e9128e9bf78b3cf32c4b089f1f58466b48487c6ba3fbd50db344535ff5579e543a01b85eaef12541959b0f0884569739ea0b5fe2e8969144155e74228c99c94dac086415e816bcb447b9ff90e78877470714ebfaab1b33aeb1eb0e4c219d3ae310fe05e7aa845573cf9e9789129418369ba8fb0abf93f9b1a809b3723c1f16f2dc9120e97a79c1446930afbe35ec61c78094ce08ddc6ae026e02936ae8fcecc65ed67008f1bf15f94153ac48f97f0939c74b1a4863a20f64f6f68f1b1b3761b6fe9abec5a3fe3981d7071b5b283f8496c29ade56000000000000000090f59b00000000006f912d02e809f35b6374eed8179e7107cd0db095e1221634eebcd0636f2b57b2f65936a4cfafa6b48f31d6a37eac6af49e147d51825fa541457caa9962f3c587e31872bca14d0a8354be6017725878e28392b950d90789389b8de1948374f4670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000062b5776ed34909d6e222f3b45053d6fcfea2dc32b01028e578164a11127d20fb546883c4b77a13e454ec3d6970ab9bc004ffda47446aaba8ed1fa86890d80e7baba6dd368e6afa10cd0e90e310c0905c6c6279ec938725d753d704066d542fc7af9e1c0057c0782c89da6ac1a6f869056569fc77f666e1fa343be5255389c00c0310d4277f937b1be2cc8e3a865d03ba8da36a9fdabda51b9f8e168d4a9ffd2fb403152e0499e58ca8ca7b02c323a87baceff9c28cc98402ad6d16dbccc7c31344810b2366e9695b3104e70a92ba579caef31ec084bda848198a8d2033967f9d81857e18899e00b8d154abedfeebad0d1e8b524fd26822ad55ea285495756da1942625032221f4f9587e8bb182d861781215d55d4e2bf7504e8d76fffd258a6bad39abc30312d8101f01484477f9163d8f2cec0c871f7b124cf32aec1bfdf738a9f9be8b5003114d614f04bc0695f0ab265f2433a49d939aa28eacd936b0e50db7a4d7f96c81021630e10f5bd75520625b27ea1f1410b1d62a5182846839525d0443a2128048b903219e058f22321f63e63874a332f8f411ad2a5fb75d14a03311f94041a8d1c9cbfe83fa04134b03cc5920f8a0914dca283cb9959f68c56526a7794a877d5d4438fe6c543d1a507b3fd19029fc683bf1ba61b6fde978ef18726f760368ddb454487fa4e8539ce7c6b6013657112d70a05675039310d7de249f9bb6ffadfcaee24452dc068f8b8615ee5440e625c2989892c2ea467066a6c9edfc6e0a94a66ecca3938fddc97555a23ab342f4a593ed295e1c3f71386eda66c95653b92fa780f9b7af4c5f5b189e2f9830815b045a11199a6961a39806cf71a409a7bf65b2223318cc76383a9fad0d171b21bef099b2d6aa78af24ad33e83d0cc47307abfc91a235e79161da3062977f61415ddd7b8d560f3c39c5cfa4b59f40b2b842f2d3ce7ad9532e4016293ca6d9524bf3fd04eedc02eb953aabc8224c6b7e41ce05a05683f19fb7b6cf912c3ae20e2a2d268607f43c69db9fe3ca3be731a97f558db80b973a787c9b575372bd8e42772367c72e6c587443cf22e7979f8c16ba31e27befcb2bdf3165f12fd227297805d4353de12f2fb8203cfe248718ae15a2b10e62b30c433ed9e2b88feba77fab0bc1ce548eb10326def1b6874340caa51a887e4b35e8c1937e8e9e12d3fb9c98804827927b4c0b22a801ae22c84048a2974cdabd9f886b798d34d1a2d6aef6ec4eaecff15374b936e9c42615ed6aa247f09835b891d3eea838deaced79a7a94453891c0262956842d5731edc9f1b44d5000ed5834cf3dfa7f8608f4cf897fc96361bcf3eb2b8b16f6d52ea0ee5b3a2bf7e34e58ad658f7f5967d710e8dffcb86476bec4cd9a9eb7c5d6427396d3f2274cdc4b55bd52fb22b26cdd5e8f711a8413c68a1dcd526c350b8b87427dcf8220760322ec42661ebf5be74a5d3a8cea81d351c4200b49de1ddc4dcc0437961f489b48ad9dba5ffb23330b9f5cc52a514b1fcf567a4eebc386f5fc6c112ec8d4abcf7121d20da07c047226dd0c96c07a613e11d9f000dcee6f45b59679c33c3efb21edc85e00ad0316e3666e6b47f75b16bc3066721327f707992133c692ab7ee0d75e28ff8488b22d11817d943bc8aec59333efd71dfbef3f47580b2086dc7c15cde8750d2227582f865b059c51c9b497e1dea6611fa676074d2f24eae523069e19c76408225dbd4f845a80ee408daafebdd8fdb74a9bd71f5d6895f9b30daa45492b536618b6d35ffcb3fc8dcb58451dedd04e755f7a77daf458f33fbd6895a253bab7d1adf4c34ff04cc5f288ff46e80711bb3a693411064bab61db7ce06c868728ad07828293de5bf54971cb45b14a8feee5c3e8730257a477d1b72dfcd9f19b9e1fd6910209a7dcfb27526f013c7e24d05a13db8e457dbc34f8f69b5989a59e0fed4625ad2ca396b7b17b574989b3c2831e7fcba1c8416f3d70fbd443e4ed784bae456de96894833e19fd7ebb7645d08ded5ea0b890667ca365ac0d6dc2fa077552669b73ba32d857fb31c3aaf572361de207ad288dc47890184d9d9177faa6e78b565437363148b4c24b921f0d261b51502028361899d2ef38d38348420b38d2a9a43ea10b06211b4c9ec3bfa8a850198216ff1fbb164625770a5499e10e414f0b129efd30f31852fd17f394f14572a9e16a597a03266333dcb5f6b65998ab941d5c5bee3c42f08cda26a6cd18370bcefe0a2eb88d9301f3207cd164e647f4999649a15c1e1b615978ea3d0f0ebb5e544d3165e99a98f3829e2d20200000000000000000000000034df100919f61700b17b228533e590ca9767096d169d10817a230ac12907622ca544af6b980d60d0359d611673d3f1ba9529ce89f540896140b6bcffd6d245a96a0aa99ca3eed3b76d57852190e9ddd32ed2fcc03525e7d06d5b73f57c297fe4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004b48f1deba0511c13112e53974a0c2aeb74034a25d942bc94d7f50d2f1e1976f432a12b65490fff3a549a1d6bafb49a44b3fb139b53c736a83ad044b31d66edab01304158f444a878395422fc45ffaf9f1d7eeb2f9032207ed2fa356dee646706d062c31c56dde517b9ae4c545b9a489c53d6260dbdc519e20283933777ae9090317de306b8e4fdb06d5a0f7ed16e61863346849023f930ae242ee4dd3e4ef1ec80309afbf0c6a70fdf8772c7bd5c282805cc519a6d7d02d024186d8abeb2a93bab20a11a9721b9470c0065367af9bc8421f9814035d1842bc8471842cf47d645021570a18a57ada7dde903a172649a707c391487fdc93676f24f1b339c02fd7fc5fe40221ea0deda6470a48f25c9da023033d06e571ec77bdc1622306eb96df4cf9a884022db81739af9ef821d2e45466a4e1577e398378195f0fc9d7593daf3a1db77772030b29215d6675d626075371b00d0732b6563309390cc0724b569ccd2bd54a5e54022385e9f4af202d08c361534ee20e39c215614e2dd28bbd7aaa8ce832a1691f1803129e478707f72e107a6dbdbca3b9941175d0b1d0c0687170630257bb59c080fe7983859d360627be9ff00279606c5c1e553d487ad35f6823e8838df88f72ede470db03cdbd3825b8777a94fe1c7ecaef15f8ad2c9c8e6735fb43d5f783a6b362b11388a05373bab035b02a458809ab42859756ce1af212d2081e328204971191374f121f9e668ed1d9094a6462c5d3cf864d9b1a223f9c6d696c8f0c37246d670605a7b1683494a02213c9515d80b7f78da883b1ee27e041e7c1c95a8d69fd3d0d1a9f1c603f3f454cfe8cc4cd1b1a26049c8a3ed98b418c968a4777005c7c5ec50c533951d5651bb0e412b4bc7b34adfcc7a854c041e9c2515dec8b13bd6e2ee2a5de2f4df9213aa64683c86c3e931163402856b54a5f3c560e522d466fa798d735fb80489f2ef9c6191ab54423d06b2b905b0b7e3b13af9b83265a9a23e844484059af55716f24588191376c664634e718c4b7f8ead80216243e09783907e35b7b27b7ddefeef3b10ce309896665ed0e7669069bbc5cee661ef0ee60eee9545396e5ce27b87c942c9a01d028d684bc492289af6d1f0a7252ca96731a15ffbc3f1825bc864eb37123044e3cd1bf4f91dba9583f53d5d2d3d586b45c884c35a40edc855d58331882aa11812e0420868ef86e607f1aeba180342c30d38b136b69eae915612279cc22f8e48ffd81d57c013e35dfec5b39c7a0d2730d77b4593aebb21bffaf53313b93663da7cdf12a52f586bf489df4c2e1fe2c178b035a25017f4b85c998c57849aaa2b05f5f299f1ea6693769b255eb1a433e3eeacc6374c0279dbef68ef28ba6af70f32e2d6c8bcbff7991dee116393dc4aeaffbfaac02091ff659bd24bdb129c86d3cb14ee4c263ad585b6c0b670dca0a1e3e40c560161f9571cc136d2a94e77b432f4f8e0cbb8dc1fd2cca0b26b970a56d30dfc6852d03d5472e75bd718d346fe3b30d6b85066ebc8de3b77077687f9cc81a211fd11b27095f8e0627fd6143ce1fb8c8595cc1bdbe217a2969b0c3601152dc6213c0108fb8ebdfd9dec2d8803cbe70ee78f59d7c8b9aea03b98e86f0fd630e348589f151f9e08e1151a8be249e01dcf4094fe5c5e6d81ce3b643f9a79661c1873ec81830273f37e1ab5ac2e521987385e529861ceb74bee5da581cab9215edaeb920c6515dbb048052f087794b229fe664e657af336bb948c006329bb78ef4da135d9f353cd3df1a6a02b5ff906197cc3f548bbeaecb3fc6d81744cd0a0ea096434461d8cb248677ce3c5981a4fcc3ed75ff2bd411607d099844b397776924d1c8419f7fc4307c19fc5154db427deee3c636b6ba69a71fd3e222a6a04c0e44b10ba2ad165c6c6e9171aa80e08537714ccb9b1f4af1522d0d5307e52470c828dc3eba9897ffd1b8250b9dbd4d4c69a9a1cf4e9cfc9ec606e187e6ed680724da200ceacd323d37e317bfaa0dad7809977a3e07a7adeeeca73bc70efab8f7965612b398082ab17c5b675e4453fa23f31766a1c41ba064c47b9d57a38aac4927f44493659057559acf6ae501619bd6b3a41ccdf7d5c7b6b2603b2802e85d42344531033ab7cb3935638708d960770164fa35f8c5c42191ef94a9ae70a8c63cd3c2579712590a733956f6abd357b6d05571bd2c50ff4b509996d979ec36cf21f7990798ef26c1ff2bf837114bd33137bae17300ae6880e19041ca60b0c7076ee62511b718705b9e3e15bef39da9f698e43ebeaae708196930827557fe9a708a31ba1fef54d43fa96079a4331fc36855b7c577e2db2a06681dd7fd54fbc1b945faef66956ecfa47872663795732ea5ddedb87bcab47030f2ff8972c89f8ef491f0f72f0d5237924a631bc9f9b1d16554938b88d543130f3c4f04", "5353656365526300", 1, 388989590, "de7c96f96bd2b012b875e94b591e4954b2513a5b487f239593309ad900f46f6f"], - ["b470bf9a02cdc44a4087d4fb99faeb75d218ac4da2b25fe640cefc49425f9c6c2c2d1f1720020000000352ab65ffffffff91b544692c82af03892e3676815a0452460fcf7c9377375d77124a06240044db0300000003006a52253b6ece0487cd8700000000000451535152be99e9050000000001ac61821e0200000000065251636351ab60068f0500000000096365ac5365526553ab00000000", "6a", 1, 478489352, "da1fcd43785e9af672c9b2e8ecd128b83d55473f66e198c9e79fa4d438e29576"], - ["4b7e7e0403eec4433aee67a9a35307520b0de2c41721a05c1d2106256b8db7ece933defae80200000004526aac51bcec317615dc4c0f95d9ab6c3fefdeb4600f5ec5cead6fae24fd062c281dbbfb4167dbf0000000000165ffffffff78b4d346033a26442cd919da0b08b9eafd2a5c2aedb6dd7b2b1d1bf8b236727002000000045263ac52ffffffff018c1e98050000000006acac53ab6553adf707af00", "6351536a6a51ac", 0, 1014381787, "f114f8b79dee2a96f0aa0c6a0eef2807ef19fd88c8e0143e4e726e2fe6a8d994"], - ["c6cf2266020a8416e6bd13c25af9b14969b1aa8c1b0985ff3c16048a94f1e88bf2c8f2f483000000000151ffffffffd2bb5b02045b9d87f456df1def469522085684211ff9cd7a36f950560508f6fe0300000002acac0f47dc5504472fe20400000000085363005151ab6a653400f4010000000008636365ac5365536a11b2f001000000000853526a5252ab53652e0b7f010000000006acac535165537cd79fd20200000000000000005cc3280200000000637f77774918b8bcd58803e8e22005a9cf6fa77906a340f2902850425a7d4351f1d04c4ea5747f0fddaad45b0fdbf949e3116ffda98d0bbc2ce60ecd2ebf7687f3783bcecae1932de85e994802cf8989bf6571c4631441cd43fc5588f1d5adce000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005fd7f91b5fdf23ff40fd30ea55103b814b9e44a3adb3a9adefd56a811d2a18e17fb9ae15cdde51ecf25a12fb2d8c22d97e0b16891cfc8b52f26b1eabc7fd85935184d3e25bceafaf3623083606d6763dd9dd557a465fb8ecaf575307bb3b7b5468db62f8f9240b4a9255ae38da93b3c79744afe66c5533e46102fc37315afaba02214e589cadf80592b3a9004c30d7aa160ae4f23bacadff4f27e16fdfcee2564c021c6ddb35f7b11b0cb4464a02a0bd572095118c9c6eb5870a6f44c5d93d8fdd470b1f822eef97cb244b0638a3608337e0ad9ca7d9f627ca95cc1c3e01e7c9ad8f7413986667fdaac571ef0a383705becfdfb287e3740d5efd27955931143b99d8260228ec3409d2d9489faf3856cb78e8700c6d780397ad6ce65fe94b3fe386d60bbd032653b1a0e033c4e64b50ca9f50967d70d82c124d3e546a23083e538c59c595a302094656546a112c54e9462ad4930a538d5a359b5f524e68986c731e580970b859022ab4cfa78738c2a933025d0d656ee6a4d24742b9eae4cf83850ad670e1014c0b0330177b98641dc2724c9f4891c0f7b7f00d8ff497a762c2b50032e0694540d8c1cc8f9dc01536a896e0fbbffb0f8c1d997dbcd2455c806b373c5b4d229397ec11769a4a4c930859f89191df03753a53d59d9b800c53957d17ac2ecb4f8337ed1661f162b8275b9b54d0c3d787d4e015463952fdf7b649b68e7c47a8b935a815db0419237802089d305c750edd2874284f0be3f79ffe889b1e0968fab83f4fbff69478dc84108d56ba8e16e6116b6bfd574dd2ad918d88f46f143f09c27a0c192d1b92e017e72f14377ee658d55b0833895dd60c8169c083e51392bd1a4a8189361e808cda7fdeaff113ddd94ec33e238517ace52bb6655737b0f0294795256803a22246c2972c9bcec900c79e0ed34531a1d8835b5c2059f040ca19099a1b9e3f4689ccd0a225535c613c8207cf02032412d15632d17c06cc6b9adcc41e18433f2ca5e1266e2fa0cb0d6a20d8252b27a1aa51068b34fec21e62748b279dd5a300c5dfe9e6ec97c02d55e09b0020417a3d43b0fd23d2608b51b33e70206b43dd016a028fba18c79d26122fbee446bc3b924f4d1a2d4a984b918542f48de741f8f3a80a3abd875a8bf5d287ef9141be173225a0facdf2449a97e30a78ae9b6cb49d74cd19d69f0ffa10a5d5a928c0d6c0715b7f8ac04f88d4dab819db50d4b587c0a39360a8bcea36c5dc001f0f7bc787fdcb7a52f7c8a729d478690a42183932a40306b7fce3149a9704bd0bf776289c2f8b60b4816b0c542b2b919101024a5f92da9fec1bc25bfa0dc34796175c0edb4e0f188ffd3e20d68cc5bcae2508faabb3a490a529706ad967b9cf02dd479e6865b09bcf49e6ead14d8b064363be2b71d2dda851d999343028f212053fac2455821043de97f9240639e51ee6d404bfe308cbc9241ed3eb908b970b0d47921b6a9ea3c544e7e39977004ad2f663b3a4853e84df4ecf1a2e73efb775556d3721702e519f5c9326c1dbf68018d92ca6b26cd672e982a99e0b990e02e041a8c62ee7c421f060a5aa71fb116ac49ec8ecfacd476d791fdf7f32545641ab83765fb75fea5db3652d63fe64d8f95ae49275aa48055844e8178352a221a3fa90778145452bc19abe36f6f7293f9d7263238836f0dab7beca701b0efcd798c3ef49dd70e07a7e17d449b9a262caeecc4d6738fb3527037ec2961097fb1091ad2d8382b4c0fabe4a9aa87d95b27c31f1cb696e1b5ed21bf8f117928a1ba46caebe35d7dc302dbd568c4e614bfb317902dcc5d6cee2ff817dba29b8f632c6cb943873b4f6fbf75c3e445a90370aa901c7de6a3da62a55b887af6f98ba6d2f039ea5d0e7cda1761b993e2a3abbf7f6f0ddc0eefc5e49ca914019b0cae20b8e9b73902bd9647f8a33905d194c7d818d31673ed0fab398be476f6fa71517d1aae9ff8bb1b078077d3dbe54a9109915a6678b69e100fff54ff6980f4dd6e518d5b908c52b58eafa7bd22714a7f38570c5c641e009a2c5f116c94cb24cc6f66ac992fd21aef1a0ddcade3202dbbc07c785142389c17ccb21862034962800a7434f4488cb44ffa336422f288bcd62bb3aab7586ac246138e6cc1c2d9968de1dae8c3d80489fed80cd61856377703d0fb39bf6f8876e1a916fcf668ec70a5e67886c8abb1deab76985963abaf498a9e6797fc3c0b60b53b0b6f06e7ea4f0033aaa9b76133b5d518bd5c977908cca7a059cfe5da7176289eb139d338f853c237a349e55617600b4a71af7b121b5204203000000000000000000000000e5cf696d928ed228b95808fa65b3988be984b093b984ba0743ef59b5bbf02f589bebfbc40814819bc9a0615d8e0bc69a377a7b1b0940fa09f3a9b8433be4543f23d458d8dbe87dbafd6726b7d25188cb763b07d3ad9ad7ea7fd71a0b85e78b9b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a8528e586bed68356c34ff05e8af80f6460082e409428c8f2c9207c323592ba6f0143ecb63aeb9709643e0e387fd0f922da5c4dce8253099ce3f1075f52ac52cb8280659c12cd2f051e760a3c11029e8d5c42af3f39bfc5534e98b69ec91c7ae7fb2e74dda6860aa9a7647f5992ab88ac225f9564a873b765266886eff3e65280229973ee4c6c89f431b027af20762d9c54a1e9101da0a8b86851c6c01bb89f46a032de466f057aa4101c0d170a6711ddd719e39de7e8e11c45105fea3993a1344ef0b13f5c45d9a4cf62c48f1da79b4bf36c0b2addc3f93be0cb027a0a2135167dc3205d2956b724d9c86b22a3968afc0a9b50a9b10c08ec216d0dc3de8c48c36228d02086ee92b0b627394e29d296d1686d1f0d96c6d1d9a17e8ac89990fee4c9d8e140304c061e86d0d1a3650ca741664e287650586d46c4a3d3167ef070240644172bc031f30cccbca8ff97dabb86cc42a13bf4be516f63649a22dac88e5749109391c0d021e7a30e8d8974945aaa58c10bfb5bdb444e1387ff51665d43e191e7ce7eee25003242db9a9abf980dc0bb6f6b7a76c620a791af9c53448618cc6be26f442e201fd2096506332854f3c595b929d2cda4c59f23c15785a81c007637f30aad500c6ffe9b43cb59d26c46e3316f503f27463e3fdfbadc138f0385154ac1deac6b611d2a695058f0a3339eaf6a007de92ff3121092747f4b47ec66240909a575954c7caed05d4242e21846a90e67af9f0b21235a41e443a2d51ae9714536835effa65ef92010fdf4fbdd4a48868a4cd7d2ef9afa1ce10e9c0836de516d640b88910a715243fbb94e5a4f46b9e3a20585d453263f043688d1d0c46341ad46cc4b457b558888d2e1cf9a5abba7794fa03566318d3e15c6b451cd63c6d4c0d47f12ba86d4fdfae12d8bbe260494ed5ad8af9b8223a84d118173924037d5d3437c47be8c1afac52cb841197960f2e5ae2987e087d1f88b6fc67c452727f0b17d91dac07b47f01c4ae7f5ed739336673c34ed28c4f554a3c608e9716e6ad7a21bd516549ab9c3ab90d2a1db253862e666406647f8421aa7d1d28f02b49f0cc703a737ab55413a9aceec8f4e2ce4dfc3ec915ffe5255b9a1d0bb03f07f12953d3be8c001c2fe687da54474d6e490c7d0ebc1c05d7d60fb4a79b3a48556bfd7af2aa5e9527ba8125f8c555f16d67fe2b5e4195629835ee3afc1b6906e29f983542e5744333882af2dc5a524c0a99e03b6e42e72cd18d70b0be554070ad937fb773b31a64297b96bbf214df82117954a9b70225f2328e99a9d2ebc621d40808ba18cd12689e1800803cb3efb6d79fd66a5b3c9fbafbb42487a0b4183af79d72380b1f93db3be9867f03fd66b68e330ccc457274dc19157de01d8b1317072c5a832a8d02474f68639ef9c50eb5d8e1bea5805f7bcf301688e420ca97b8c8882bbc6b26bdc9f541534f87cfdfad6f76abf8956437b331bc0e9c86b631c415e68c121a33a3d5c0e54a2af0d5306143af1992ab90b9e5c1b41fdd79128d477873f60da3cef2a4f6649bfa73e986359e60b762824b57048b8feb2ea272a3d79916d41a831aca2bb4ac1acd043e21d49c4e89ec15d155540a7344267aba2e4af51e134db84b8003cb8fcf79dc16ece17b7d06f72d9db6f200ef39fae7db80bc0c1a99904cbca0914a7d7353c0babe6c4d32acf2cd986302faec1943df323843b94b4efbd6daa71762606048a68fbd57d39387d96a0e252a7aa8901ba84343b6792486929a7effaf9efe79890b4309d6494dae7ef81bdc5e5d266b617af6b2b828fe6fef2fe285374fdb271639ccdfa7b83fb06997289cc186ea9b79e7919776d83c3e13a4b14498cb3eb1b007dd99c0c33cdba050c4ca93847595b01081a50fd4e1dfc59012e30815a9755be9ebad98717a54cb80cdce48441f15a3551618381d28cfdec30f6ff803ef1d0c5b4b77faaab615ab58176f65088581ae664ab1a7fe34a46f4488dc4b80774463ed9c32dab8fa9da77a3f02dacc873af6a5c15b58d8bf7e2a7e73f60f2d2c873fb94a5ed08869eff465b0f0eac1eff7f52189f25f11bb3223d6b5b5f7e9d83238048a0b2f71b32674f011e4cec9dd63573426abb6b84c1f245b2a1967abe32b380a5fa384c9fd751a16438ee978edf509a155b3cd1fbf93f3aa10bbf1716d35ea78c3dd5cde3197b6971b043e5d79d9facff902b012cbcf6817d71b873c445ee8e129beb88b06a6fa2346ee08bd2956b1abfaef6a3194dc7bf8769d768be4238201f4be52f1222c84d4d302c880711e91a9fc78c4cf306e0a8b6ed80076b57d7a23830cea2c94eb468babbda699562a4485a21c706f93dda8d4f1047c28b39714d42952473d3799e207e634e7a79ba5ef9261910d9697ad11d5a2c2543c7d5158e0840b9f25d9698835a69db5a3eb237f08", "", 1, -1362049862, "72b91ecb9bd1d56a9031b010e0a23af5d8a49f4ae7e399b67e6a9794bc587386"], - ["4462e9ec03e0a7539e52b2144ea6e3d3060d45dfc26eb8b293762cae8b004d9dc5b11907c102000000060051650063635e0f739cee38d30dff7621bdcb5f588efad654f5d017cdb5820c56514ee41adfccbaf466000000000551ac006a52ffffffff4cad5a0fdb7b5f9f7ef74b76e268f5622e0808ea9df3fb4ac87d1763b4f4aaef0000000009656a536a53006a006affffffff034ac3c605000000000765536aacab51ab43c17f0400000000016a297ff10000000000066aab5200006a6bd52fa7", "51526353", 2, -1213420752, "8622a4dc91adb87d8a7bbb05bb76bb3a564ee7fb7a87c188ad151dbfdeea12cc"], - ["ed3e3839022ce200d160483d7e017a68f7c535e326f0f4665a3bce592a6a0530fd349eda90020000000963526aac00ac535200198cd0542e39bd37725ad435c11ae41b21f110c88f1e7878d658b3027a50315a2bec76d100000000076a6a6a6aacab6affffffff0400023e04000000000351536ad84acd050000000007006563ab526500a9bd6c05000000000852636a526a6363658479bc030000000005ac6552ac520000000003943118010000000000000000000000008a18627f0db1da9640d85c34c29355b4d992aa45e0ed917a4927d36c598da5d2297a6bcf4d7da359a5f16463c02bf2de0f18f600b55509538536a4274732b9287d19be75dbf106babb8971d2524184b79685b71c5d17d5ad188d7477240f041a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004f903ffb6d9f3a6c91ba681c63eec47136fb984aaf27a03876fb0f64bcfa51aaabfc36d173536e9b5f32001ea46c881a957d24c7d0747759b5eb7ded760e58035159dc35d0eaa00bdccf896df6192516f1d2d80844c5f4957c7c94427678c229ab5a43dfed1639eb2acb5991c37385eaf392ee31155e98720055641146f6a664022bddf8fd661d434ca94998d138e041e46f69b809e99740e70976d2710e0f27310211749d1c9549e042a37f102b4b3aec78ce1a87ae4aee6dcea595fde29ec3f2df0a13fbfd26060f7630479190c69a66e69fe5141927ee9d3fa1952e625329776f3420fa183e685e6d1c077a8929b9093a9472dae8da2e972b45341079cbd7deb6b5031846a3cb79ba3bcc7ec880d838ff1848f2308554778e71f403c2724e620de472032d7beeddf4313462f077b08492c8e51e54961224b29e4e69a51213db890319320319019b268d0dc022153db8135b56a5d13e5738c9a7ae1709f7581e0c8d45a55e02204c7cf6b6fbceca04ea0e336b797f2c7ae86e73a910f1f2c07fa0cf6b89eecb02098b422ae351deb49491220ac3317dee8e635c02558b76101cad97bd4ab1c1adece00c6d7ba016456cebe4ee5a07fb920dbb4a280c17cf9c2f5b054d6a48ce2d7d990c9ec2c58d1a0ad0c76b779dcfb8de6c36de6d507d5eb2f03cfe288c0b394820cfef56908eecdc1ada017f3937d02268ec6c815860ae42b51aa04eafe6b088b70bba5bdf6d56db0c19c48a10f1187558c7dc59f2099c01c6ce5d2b354a7664d8ea0b8d3b486f8b927bc6a157d9c5cfb08d9f82c39af3d5c10f852d51d26cb13211c8312eecbfb1082dd1fa232188192c665230eda6ed27fb2489f8dd414971ff879dac94c91141a9f88922138838e0ade54f028a880d32e70479fa09c5a366541c5abc1a3f243b7161fbedebd6844761eafdde59312fc750a818c773830118f0c36267e35cb67ef617bd84c013b00ab48593cfdaf14bc508957dda1da7d322378dd89e8675b68878bcfc860e17bb9c8281bca644d30875501e9b13e2de087de74c2afc7a145e080bf41a4fab5db64555837c10ed0b1034f0c10d2de95797acef73ce6ec1b40a44ad717dce3a6fa2e3bd17281f11e73cd301805b84b6f91fa5247b4d61b89c8056be911ef6774033d2716708ea79b5a6924401328bc71ba2579905c11f44b4531f1dbceaaee168c63c27539779ceeded0ef1c3ad664a6660b69c688ed6e670c3ad24d04e11e845b8ea1bf37f8eb31f6ae5e1e25512d56ec6f3a0b5cc7ab424ed69151e41dee915142b62c6f5fae55b0de7aaa9dc6af8a09753436987ec3217f6e402aeaac7a97c062cde4fa174b2d6f62af77773b125eb7d23bdecc2b6ae9df817957dc3765c230361221e8240b2d9bc384277acf559c57ac6873d0ff7376b8755d835757ed4aacb2712bb7ab4f30a02917479205f8eca9104c5da8c08021173c3e221cb4e2b8e16d803ac96a289b2690b2a0b797c7bbbfc1b4388f7662e1c34a4ebda376fced8105c14b39d3460ce56a0850f23e189d659b3c8191e64078a8ceb75c331e4c375c81af38d7138b6ee7d64aef9053981a625e29c7740dbd0e01b57943931d69404c4ab68948ca111372b404ef05db38c10b96bcbb56482e803f1459d597ad3ce156d8d9dc326590e48847155ae0c70159b1815985b99d16280391c65ddde8da1fe3c9221622b154330751efd3ff3fa1d0417eb91781bdedba44b496b3d9cc2fa65a512f4fe7d28aaa8ca45cfe0369cd649abe4ce43aaa6afd3ba0e702ac6252eca9c7095215ca8ceef073fed7a4edd66d5eb752b55805c49b70aed9b279f21a9cba245e972cd8cdc6454dbd481ec9481d10727a6c2bbd7830e589eebc1bad331f626be53a05f7cf30adb83328f0466b66eb385548e89d2fe741df76450c03138255059e252a8342cef33799ac78f2f001427780fe5cbfb6713489cef62063707c80cd1f9c802dc16b463ddca19b389b820aa6d87ab8003ffc0c877c3b24940330d4d094ca0369f03b23d9734f4a45e9f92aed9ab964596464b748353f65960bf19f3c4cd5585d7760c2da5b5f0ce8fd6aa5bae8f17d76b5c138bdf2a39d70f2a248371e0ae5223605c131f82c3532ca2abd57286e69fcfbfc8b9485705708857b02f230a0156d0af5bc1a98cef402443026e75115cf131edc144f907172e7ceec315a328af567ac1dd70c4a4525c828dc1d0c0cbcac9dab336ff2a8c49496d12652ca2e1c18a7053f2de6b922ddcf5d4493d2cf582e3a14e64b3b92b8b4638951dcc443bf79df300000000000000000000000000cfce473bcc522f54649240a4ebce568605f95153a2e598b369020c0c285bfc619d01813b9d4e92c1b0f42c8cb0d7323f9851fd2c8621f239002a5e786a7734605e7e48be4e197d5d7c8312db61000e89fdaf4b31a3035d32152ce4d62681a95a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000094d50ee657a313fa6a8b2b6047ad5274116e1752055beabb65365ba6d513a4b7f5c266d486fa374bfe6bd9bb7082b51668858d4e46b84f4ff38a9ccca113209e96d485d3f7af795a6705237862ad7a5892b8b470f09c95998143e6387c8a098c21533a4a2672f6fefccc0af5e0c12214c4a4a5ece60446416d7bdcb27edccce0223d6156ab521342dab9e9afe89745671648704148c5eabac88a8cac5f0beb0c2021ae39dfbde19f160311e2ffc6991caff6210771ae8aa271e7e9c2f2ddc964b360a28768a97f854d925c49cd40e0ed70092b1ff222bb92497db4e555c6a137a144e1fd0bf5f2ca896d78b9232773346f87c9195c6c434da1c9344fd4bf9e182f369021e76ad05310724a4792e4b5683091cf6f7863324e02cce5c14a6c7c8b99882ef032b8b4dabef43b21c6ed17f1dd60193ef238c8ce40e2f09b7c340c778721565c7031ca1506fa5b111748f655dd89ecdad1910605e0a32bf1e66544b73a312354184022c7bba48039f008fc91341aa1f86b7e1ac39903c91fe478f8e2749ef386e23940316101736edd44426495a0524dcf6ea02c616b51365c1e1d92a3d4d2fb56d748e3857d7d1e0887965a4457cd9d3bef49fc7204945e3ed88c95d70c678744f62ec68bf425bda66a2162eba7f209c0520f5a0bd5d5708dd341fc3d0eca59e63c73e5fcc660c58303d9984305379d52ca480f64dbb25639dfb5bf004be8b6aaa520c6fac999e6784828c0f9702c7a686145f0a0be66e04d6686764c60ddb40b38cd315f3b7072bbe02f69ea97bcd2183d9bd510ad6bfd192701a110745fab53970b28cfc0735d6adf30c117bfc1166d250082d11429fbb26dcca0f0e177779e0589f7d9edbe2532249b7254454c48794e1460a9d483a862470f0ee921b0cf57b4a8e21d1c3359b4d478155417605e8f18d0f9cc96aedc5bf8973f94a3e8d30cc0ac80a6b4055e84837fa0c251df38ac1f678ecbebdd1d9e50b91eeae6f2c9b86d1293efba5644b9dcdaa280a627c29617a2447ba4a7ddaae46bbb246e7783aa7eca5bebed536431fb6d47bc66b07d9d4be24c5eadc50f8e514150c28b093b79b0e5177d83d50ce839db12eda9670355aa9021db6d8f8823c45a7c93cb7def5610bd7e81fc2ab988ff4018c979e22f60a884b514274b2053960f9b0f4570282dd714fdb4e2258b867e813a5c5de65cb3ab898db2cf655673176e20444d57af30f735abb007cf597bf8e3b128d1d619c320271fcabbbc866e345266c06312e05b78a073f75bdc4805798853b5e6c0ffdfe4d1c88f78d6441a16c0d8bf409bcea2d5926bc055aec8233c654b19ad144ed1bfa9c73ace10d45c33cb7979dbbde519e73e8b8b265ee02de76acb914c8f984efa524f23ec3baa824e2505df76d39ea0facda88376f74bdc5632426081534603280ffb692e350416f421549a8fd19197e47740adf59aeab1d6f0ef650f2aaf2cec23d169b8446dcf02ed27a11a79f2030d87910d9098ce601f75754f2d69f048dd0edb931b86965899403734c1035ae34bbddf1b223dbd5183cacf2500f46fd2d88c3b5db50f184475a06dc14d38db4ceaf8360e8d5e8802d6953c4ba9c6ff0ef06ad5c8a16b80249b7a1f3dd900f7f580f07407f456a7e8b678796a6951731d4a764547a980c8f63d7da2992d7e7af653e8f1b916090724d99f7aa03f45faabc22c2a0858a936e5b84b0da32ac33dc9d37b9006e7e03cd14c026593b2e525484a0547ad4f7585506f3b13d95c08e5d6dd8bd7313bfc5ba0d046476eab5e3698947ed1f6d22c0ba11d3dfd0fb95104e24c52e8cad727468146045a52adf6fd52c2b9bedd035a7dfe2ca5b16401fea533facf932d256d96711a70106531b768cb12841d2587a154a923c841ba0cc9d46d9d8f831ebf259d43dded3b9a52bcd69daaa157394cbe19bfa674da928c6c7a95e9f127dfd2522b59f89f8322cf4193f64475501e7dcc3c547e6a617fcb31e7daf86eebb7d2499d182bba328f04b8ec2f32c5c1c7953f03e830a472b552fa043b01a424d912a35abf94b140f514a6e3771f5f509bc4f274806d1b5f6248f23e5f101958ecefddad0e16862cf49cd280d56762e101a84bf500edf71abcbc6205fb3c13a4da47b4596db61855833f387334c3bc78de0007e085396cd470fa3bceffe37e0c73b389a4723720e67691228e142483bb1ab51c0245b98b13e71b7e8d136b39c0cd569117230e800adf2e3c25e5752d97048f68996cceed5a5404b8bd035a069cb43aaef6483d47c650bf5cb435b50c3e0ae0000000000000000159acd0400000000b056dd456589859ebfbed8562ea46f3bbcf196d2f2239c4335b74c11a5f972c694b5de666c6be61e25cbf32bc5f84accd970a533b4e6f68508509abbf84dc1a5c0f63ea2e7b9e070f2851de671675f3989d83f4de9e08456cd020bff1ebd4b35000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006ee30d22a23f69fab96f58dce0e5431d46fea6bbf7df7d37661b9b2e0a12bae7baacc79c9210f7cc69666cc1789a835096a72a878c83525826299006eb57775be122175ce74cbdbc12b3610a21a78c6f8cfb3dbf5921395fd22e88f5c9b9b43dab5f52055c5982a6db0a9ff66092cce3f54f10975353f6587034ac24237ec146020289236fd24f053a7c65895f28b3360a3264f1d1253efa8372a593748fc455cb0215110b472648fe0283f087f55c68f1155c70a14aa855a1a6fc573e1e47da54cc0b247d6055393c2e87947c2295fc05b5c7fde6d028a6d5bb7cf67841d0f98e860a08dc8120ebaad4bc8bd0884291fae288626198a1acff8f8e4e0982c579616f360216d95d2bb6180121fe924b090a428edc630e6affdb9d428345969163a62188a90315ff565e7eee4acc3cf7d834671387c96f79f7dcdfc4430a95779878f06882440308840c15ab5f3c03571925858d3542aa76cbca34579ede934b78522586322b41032602ad9ff2b38283a16e173c02132249fc6a171ed6945c1f225153e33cac89e1021a4a07875e4d55f0b4c1d42a9619f340121c474389ec61a105fd01c810e502e76d9d15e12a95ca578544329427ea2cb849ec7627d4120e95b658a846e3d26ad2f1d38b1d0a8431e7e19860de5886ee9f9b24137f4e1ca53f515d6e809a4dfd0320a4b4a5cf0f4bcfc8113b2f52fb9fb261b663ca84af6c183830c2b17d23eb748645f27e9efcdb8a301a24575378c6a4d5598ed56d91b06b1e6eccad3a14b170675fc2d17afedd334ccfc56cd8acb58d35cac8d1a0048214b4d48421fc1f954b7987261b5f2f85a291a69f7902a03fb9acd97a9c5cd628e960ea0d3d0d8413d811f517892dcc683e672866bb55a36241b736bf847af46f949814508ca7beb4f2ed3d00043d7c845e7f56d66fbdfd041dd1dd2587a4c3396c77f14c96359b26e8127163325f9bf04f4cc670377a240cfaaeca86226f7967719b231d44f48057e38c2ca914824d560da50d433a1e1e7312e87572366e46f176ca6df30ca8d8153b3ac0657beeb8c4440ffe68ea90588087271cbfcb2427fac9ce481296af3cbdaa98b3d2d511c65ca16ef450b88c0d5e23950a6aafa1ad84f5db210290819c699fd22454aca701f56407cf439278d0cc478091672bef6136fb5496de49a5dd9332cd5fff7c99cc658345abb8575fef4cdebb394c49b47f9ae85d6a30631fcce43476eec725ba14a5b8a024a52e646f4d9518493d0353d1e0f4c66ea6e7f04374bc606b090f20f05e0d92dc39c70a2e2de3ebdc653e3c2f506d6ea006232892c7466a3c79743fe021201078e656e967bcd03e398b20e3541a52bba60ee08c0c48b68a61217a281677314757f7b0437b8790bc93e3081d31823a0626fafeef703fd33254d561973a7f64714e63bf44775a4e15b23f56017d3e8d1726402f679a3b6fa08b6d6bd18cc2026ddef1d1f17484781f2e645c057fe0709043b7fb6a46040fcfea7dbe76ad949d153fd62d9c26fcd8d304d4e6764f87cc6f962877205c1275c5b2096cce9dff9589d07afacaa0266093f6f9681f891bbaeb5477168fd5784b8c8124675a5e79997b5d7f1c2172a704fff9fe7fa7fa7106d58a1a3425540946e8549caec2bdd9b0f6f921b1fd10c1365a9c6eecd4a27efcccc1056e634509a9182f3882016b4d003e8654ff3dcfd2ab87dca1c843b4560e9682aa6ba2d0aa99ac7fdb9930161cd994e9a66e11a390a5c414f3ff1454293ab42c6f5330c0f17ad1c5cdeb2496b6bf559940c1b10a6164d42439028d7216f69202a2b7b3aab303fb9a40ae8ae5de518cf3ab5347898eaaf54755909199e0361abd7a364960b8c146f78e6d7e618ad579f53279e6ef2858405cd9c68a1355e721c0171a2f43687e24330e3c5d818faaca4467a35e1eb5a9d6b6fe34933cd5a69c358800f07697fd46db214f466a74484a6461c34688c30d700d82317fffa537dbb386f8dc6498b40c9ed6a33fd972e1709dfeb62ae644a75d9a7d3be17083449bdd91af96cf172d65f3b71551a47c45f06ac7be03fe1243d60c0c57a2b4aa4aea0f3066f4850bee8f890fce9709848bb36c712c62ca1d83aeddccf9134cd5bd95e0710719221a6af290699400132ada6dd090eefba93e3ecee6576f6627006551f11fdd4d3c74154afd675766f577412ea25870dc470ac64635bd12286b71dc339cb15929b0b406b48053782dfbfa7549275695aa7b3021df74945027435311f7e4cc07a859c7a42573176ece6dcfbf80b0806329010ed0ed8a0fab0d76dd2632c8c6dab33eff3264c954c1c5f700c7a3209a874e0da931cfc2ff19bd97f062b2036d1cdc6e3e8cfaab36a81c1b2bd533965125bf51a12daa0c0420cc5472310599fd5daad5821170f1990a518ac063b6e9b5091b18ba79dc06", "ac536aac00656551", 0, -1704031587, "dea3549522cc05e4d4b1c6a274cd6c6204e3b5820ba15eb5b086c3eace4e9cd3"], - ["13a3080902ed4c698577175b8470c98a5663dabc35ba8591f7a4e0d4107b4d7d4395c752df00000000096552656551656a516a86b404550a7b2df8bb9f5b48874a1f28031b8973d415086b8b8da04b08ea1a94c7adb8ba0000000007ab65005253ac65ffffffff03aee7a005000000000965525165ac63ab6351acb5ab0100000000086aac0051acac00abf6290202000000000000000000010000000000000000499f530400000000cc925d05c139c1af551f1593edc4cd554decd9d9a0c538d562aeb3e07cd42d0ad1f5e8dfc0f8c4cf26f125577ee5f46f3713978b8d07f742185003af6d9ac2b534b5d41e69a49e013c32c89534da2f5609e14f4b27699bf515c5d8f9d93bfdd500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000978cf53bece05211814af6ee0f7a88e75ea98dfe97d46a548c02ed327b8456080d92dcd03940e848cd84120a77804564dfe80b3a5c29e8d0ce557824c73d3856dc29d61340094fd6b644e1ca2097fddbcc8381d1250c98fefe5b1547be8d7363ea651697e0ff25be66a05445472d543e71c86afbf620c81e5ae8585a66b2e7b403257ba01c58a0c498a1c02bf66b717486673d458359d953a1412ea66dd89aea950218c4e5d4ab9b34ce267f8ab1c40809332406fbf0acda1a94260a98d58df816730b1520e8cb11de9a4ba961c3fa2facd5c12a74d9c98c514a55ae91f5f7a0d419150d87a1156c4765e2e89318b7483812ff2b97ca9e9214b27d67f10fc9da399e2b032afeb469948e91cc17f59d1092e4b9dbf96e181eb9c9ff5e21e8712c1581725302265eb50f20d4052b0e4ef59a9c64c35f53315780b8110af045eeb747f80e22300309029775eb3d46a294d54ae55195bb8a68658370ffd0fe43722871b3caa95a130223745d2dd20e9f277e54ea4d6213d36624c08eadd75b365703f911147db8c1eb022b30780dd1ffcfcd227e1a4a60eba9a16a1598977da8d70c65112abef84da4372166a1cbf2598d4a8edc6c76cdd20756898617994cf7d95e39997d985f40ee7e2dbae5fd8f31a8a79d93fef02108c5da457ebc2da18a4b620f268e0a7bdd78b6d62f08808ede3c1b6006f29cc9a4fc111bfe6555c0c8c7313ed8d4d7ebeebab7d54f0e51cc3891edd45cc401e20d5e28930b4533616993830b8225171be3f5e6fc32887f4257b669331aa882f5fa6ab221eb8db6e98b80f518d4a205cf8f02f3a9c89cd2f69e852cd7d8de85dc8089d04195a9e430288b963f7bbcd4e4ede5bd31446d2cd6b0a8dea3478d5212843d1a7d8c9030a695ceb29170947e8ea111fe474b0b4148be35a4035b363792722d24d37b124747044cd050f4bc0db4a02b4023c7f51dfa507d811fe428c30b4fe42654e907b7d6b3d62d89f4d1a11e15d134e588d446878641173dd093ee39f266c2712b4df34b698a33c63b077d3017603c3fc9f837d5d754acfdf6982831e29a81701889c3ed337f67d12a7cf70c587fb5cddd2476b4a5f79b67b0d5498957b14b903408472eff941fcc249ec10181fead130623958f65939f1b3f92d1f8724be6010fc7e2c6d66cb092338fc41fd4099942eaa45babb0905111e8c91665b69bd507534f22b8e015f80b795eac032f631914cbbe9246d3e98e3824e88dee6694d43c3b03029f524d9b62a87bbded88728d2e11511d808b003522b58b73874d0214e6f528c2e100bd70ce1416507f486605cd183288e562a3a6763de413427e40638879e81efc2fefe7bbe282e0d360ff71745e0623351e84a0177130ff262c89b9f016640efc0811d2d11fb0342d1dd34b2d41ba363e021d202cbbe0d9a2d769518f08786d635ac2430bbea671ea9d2e12615ba8955368d8ea3d3ac6b3ce485ec9677a2435e910481c546dffc601941502f5c2650fbe5e821ee69f3f86c2f1cc17feaa9a15a0a9efacf8ae8273a580b7c5c3dfce88d33ddbc05ecd6f9feb424ac40acda33a8d1ca4ab0f9b476c6ce5e033ecb9b794e485c886bf8239895ad81cec006b0b716daca46c433af40f6d384fa9b13ca9349c5cf6431d6c719e5d208765b1bd9b57a5a19c1aa3d988bf1b88ff47447993fb00a987356f251151a39661cbdfce3db12ad109cd1fd2aec81bed703ce35c03360d1dd3a7c278b920ea9435be414f8ac6c5fdbcdafc2272fdc7cc9314598f0e4b6ee3926a886d7b6f48d55fc4fe8cb8cfbaa999b148440abede58ded0abee83f41a4bfb2d29f5b7e657316bd520353298f14cc86687b3a30874faa9fa9e44683d01f8e010848e3855ff5e9c9f412e2f8e4c732556204649f894da39d58f89f7bb6801c09f20769d1093b3868b5f1d1b429ade217c90caa7097d108ad70e01994c0cf094bd04494919a4a4b0f30e1a0329657a1647a71851c61e38f18ae61a66d70017b9c00302ce634cbe34b60000a8e9fc2025f2cbc1c680aaf8b139979a24c67af4a6c11afa17461b7e489ab4661a5c3546e1ac6e7c7db4cd87db7533b39c43f3c18d36d5008713aa9ccb4b22f8bc46ea7470f97c795a17ea228a8a9623306497a770d6bb42afdda9ca860acdfbd3a0ba56396851ab13b5bf4e6d8521893b0b8ce5ecd7fd3cf26601a9a4e105c8ed2a2ec97bc6c3d798fccd0f4f56b7dd6b14c1a9829148ad67c826e0fb557f17b15771d1434a026565ee23e5ed2240dbf9eb6f0f8e6063b8744cf7fab0a9948435f2e3f7f0076c6ab2b42691ba335726318c577125132ccbc481aa3854788acacf2abf52cac47768ea4c06e063c3ccf08b3526c3a2aedee68baab482ea84c7c259f3483f3df95340d8b36d03707764ed80c3249f3a84aaeb78ec8dad93c9c207", "6351ac", 0, 1194704766, "63f4499d8c52ce7c9a1fd79aedec16c277759e3b56335bfb11034afea76911b7"], - ["35bc927104167f685dce655b018ce23382d4c8c0d4e5bda2364d6cdaf31ad36feeb5f89f8f0000000003526352a2d417ba209aeebf3f1ec844cc8f9556dc1eab0aaecb547680d56361f4cb5d099569372b010000000163a5178e9286ec0f0f1c3b1e9e1c5dca6030148442ae27682f0033d45e9e7fd4a88461713b0100000000fa6419adce6eceb6fb74d590ee43d94887a4d4badd66e09f3e6fed487e7e703bad2e59020300000007525265005300abffffffff04e01e2e0100000000036551526dab760100000000050053636aac14d0a703000000000500ac536a6ad5a7340200000000016a0000000000", "635253526a", 1, 2094929526, "619ca4509f95c361f56309ca4a06e29147e49338dbdfbae9d8e6ed1755456e77"], - ["8037793e04ee98e2d5cc63ef92decc6091f0d15ce01c83a6f0bbf3f0427a0f2672c2093c380300000000ed503ec1d6c2ec97bd5f7e5e253f8a9d39c2b4c16d59012c0471dad1999674ea3349f8670000000002ababfdcac76d2dbb1abf37b4feb6c4636ed698971fb3d87bcc0dd031b9aa786f7376d98747540100000006520053650065b9b5f65b84e29621ca8e85db887a6afc887973983c0f48a9a590c36b834889d24c0e5043010000000163ffffffff034db24b0000000000055351655152bf78be01000000000951acac536a51526552b9a02e0000000000046aab6a6aba4b720502d5201405000000000000000000000000cdda55c7a13129748c7ac36097d2d3034a240629bd0dc2ba1f923c2a813550f6aa8f211a1aebb0eca3bd6e4bfc7483fd2b0473d727562ddd0e6dd2d681b23cd90c8e09391f88fbfb09a1da6b02d017fb0c32cf64d676bd0f19ba07c2983371a5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007747f160ae45b36e77b1c7e13dda1f8cdb7f2ffd7d871f2a1d5fc17edcbbef1520ca17af9a8e9520fedab341133746265ee6af4771119509469c72fb21d90ec89aeb77dbbe024d85a5a4882531e201c359301d9d04486dd4e4bbc13c6fff0984600dfe0af4c116c296b005330fd903217b3a9681223702fc44dec0beede894e7020e6d9dbbef43255a246f433614ab9da70c9af2a6133101b284757704cd846b6102050bf694993ab78a67dbcb09481aee09460586da258e99c65357566347cf174c0b0717e0104f26cff6ebfabffe7ba5c3b59f5c6dd95317bfc1694e7c73e0fb93c72431c4f42fbfd4212600aabfb63b67422190427601d812d581484bd64dbb3e3e032cdb63442718b5f4498c3914cdca10ddcf05b068d8cc505b893c45ac64afccb30314e68a809928d3d9457ef66d41576736bd9768d7814f7cfdc0010d76f1ab6ba40302cc170ab83cb76f77612cb80fed80822ce6e284e3df48fafc3193672d38df3d030f60ea11cef80fd27aca78e4f32c0ca3925085d73c63e79cdf2fcf480bfabba80316a7e0f12cd5e1564a46229cd48c0fc09cfcfd9444ef8529a866065c67f40414f0105f458efb976cb03a58973f01009c2a97c9734dcbccf28deeeae52d373087a1156874cc3b5b909b063b9154b6fb29d482334d920c25de83d8ec19cc2f3da831647b39eb172a2cf37e719181eac366c43cc6ddd2b6d7de26afcb44d78653278a5320c96547db2ee31e549e8916b4bc9be9960d8dff4c0d31c75c57667c3c4ed4018dbb974e21c0a8e94ac7486a395f50bc0c445d7c5f8e7e295a070e0dc7031baf867481a043a3d7bbac7c106a8c44f25b7078f56bb5263c8b42a1cac373dc5c717475431ec7153897bb3586e458b47bbab361997e09a4f05d2b5d4924a84dda24b9d0bf1692dd51a6f0912f0ee11a5125928a76392f4ca86a20522de03067a8f1d0fae5933d0d84a3d2356f16e3c0da02a870672bbc2bc426983935134b36998067994e432751aee9f507e7aca03d1dfacf0142677bc7df16ea3fba5e981dabbc8f81a83b8413d2aa3aaf6f7be8fd0b1672bf057f6de28f224264c037dd445da83d46cd89ad1c9fd87d54da65688d70503e7bd642b27772e8aeb4db608167ae1e06feb6f0349c5131a243ff76ebfb5cc8d75378b6e7e5ce8eecd0e03f9cc93e22992c14ece31b647bb4ea49efb8cd37afc212d6628657058ef7d5d3a4aafee2c015be2cdf46d9e13054768d247cc110e29675972a7fa2043c1c08e1c26ef76a66fb23b560f9c5e852a8e9d26d44c751c1078ffe60ff19b2f3753a51d6bd020b87a663952c307a02b578cc6eb70ceb96410c1d18e613d6932fe37f12b4487df0e28f5a633a4a22921ac5ef25f7cf41f3939c7c79d9ed8c6d94332eceb683b8e50b1aae74bf5e6b1f4a952a89fafacf8f10b9a3a4c23e810b9cba06044254b6ca16114e1c224eabcb650a26f6e11ea43cb7aa411abc8916b9281c5fdad4a52ec2d5484d3f9944544a4581e212732966bd4089cb5d32f8e636bf14faee0985b5beac8eff8f640a1874c0bcc2ad1fd61cbcd45f876f4eb074491b678d7662a7f2e15693bd3a27389c112180ccaf47dcf49306a57e89035b51098167c56db6673a946d7ac72a84792c4d4a11279b05478dbca2c94b00e111ce2607195166ba3f214b4ec816adc0654182a810a328a6e9e0a32b4cba1d486fca3261fb5d2a4c81172febb682ae3ff959e69bb6f2c8e318a9af682a60b3dc307b2ef4968b4aec1d0faf8f617aec9cb88df34bebb4b91b104cfc44ff02b4e234fb7e982a2793f93149aa265fc773a94302a6cd69b73632478319927a0a828b3411514d091a13405506def1c28b2543f6a2e4f3473b5913299a3694bc5f769a42561ed954f0b49df8a641ed33fba51c90e528bd27bdfd42b2c638b5b371bd10e203696239f95a249670c89f6fb9fee3eaadface720a6c4f79f6eabff56965eabc17dbb6203f4e2d9980124a3b60227d1b255e5693575eb1bbe1d2445d1cc99891c6e56e841d821cc17de013d9cea99535c6422cf99b8aea81ed521332bb3d10bcf0aaeb873e8bb2213b8171a0966dbc0ab89ed0e2b2d3d253fe350c911bff67e5666ba1e9e207a81f0d986c4ca2d6545051fb089c4e1903105d5285e1e264a7d047048eb6648c58e998ccec3d26e145a447422d16adbe78b14aded2cc47b8a79d85c15ffebf62d3d0344588799d2123cf15f3e997993f7329f302da91eee0b2457b30923effe5650f45a9a81b53dec8bd6b05ea490689d5e2b5397c3c5a5b040000000000000000000000006bcb5a821c9cd7b0384ed348b7eb3d5b1dfedc750c1de50146f83edea585afe46921897962ae74ac006cdf8931c2d91cdba0d5cb6cfb7ea3985673545c2b0ec04724d1b35530211e98905df96fafac655c6f504bef09d0a5d7b280b64b26ecb300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a6bbe7aa78f815f1fde921ba200f2ce11444855b980f726facb7772d9c394ea23beb97df4b7d034110afed49626baa65579ac0365f6564acbfb7eceef1a5becb41d8a795a629c6c55b050e5c883df2072658dcc537103486b5779159b81365436547149612fe73bc034d94136f729c3b522ff377d8095846b1b25470300cd19d020fb586f9251fe31948bb268ac5a2b97c088ce6e28868b5be8a40c65d95db0ce00227374043ccbbb74ed85d46bf92111e9bf00e79f0725fada43228a72c985e5a350b13ffd714657016facbac4c11e057c251afdf789ae97d7c671e5a51099df0f7450dc0eb4fdc6ac6306d107d0b7b7901fdd171908a220e537f067e9cad13eb087f0302710f0a7cb5867a240d92eb5c9c4c28414596417442aa86f68eea34f2b3b7d50303cb2e0b5580ea42627c45e1138cdb66bb11fb50b9a41b96295fae11497b458d021ff49435e5b263e4159718e8681c2cb3d6d597c8359bede90795eaf42b69b7c60227158036f13fe64df5f7b1a5fc71aed84193cadee41791f4985509ef90482aec022b332f9e21210a5e1fb088ea8ae6c7b5d33cdbd92c085b6c554da67a5d507fdace28b422b07fabadef7be93078d746063e33e2170c6bfe583873e862ed0bd3cb7d7ea3718ab45a25b718b2bc4577e5c42fdc3306e0bffc78fbd381662f527694d9f1d0445ad5bd0343c310e7af80a3b93556d85b0a8e42b6ce94ad8ae09fced3ec6f3b83caac56ac3b8bb0bd9f6ffc8bfe6a7a0dd4a9633c85405e3749ac599fe780171e1ded0f80ccf0f27f78600e17a9f3fcb48e9a426e9c0161c9a1769eb37482fcf7dd2874b2ab4eae6bfefd3a62a046f9e6fd7e38f8cd4d319fe9b99d7ff891a81791592e0fb6f6429261a9440bfb37204c86e475dcde47f135c002b3bc1109ce62e1a41e5082b9fbd598b0102737e6889b204d29afa31d0bea4a0f8caf28069c567a207230eb721993c9cf145e2566eaffe388fcfac533d98acbeb884bce8b6ca2a8468531370da6c2e3879dd3ddde89aadcf47ba562474f9ac3f03df391687844165ee854b237c6018eac607d3e7e6c85b7a04a7625cc63364bfec44cbeac0bc2abffb3a1b85621b88b6100cd4c292ecdf9f85a4c7749c14bca3a616f4107c8562dfeb372531de09c8f0b1ca4bf1b8076c3720386b06f8c9ab80d8bf108d0b38e44f412584d8316670c8ae3bd5ebf3cc14025de49da66377c277e39756b01a5b424e9a5940651aa186625a1311e2e6669c0cd387103fbd77e28be66c82a00f9363a08db6f05dc65eb57d53019e715e5cef88ecdf0a09984dd0245a0ff42e931c0134557234876c9fd234c4ae09590726350a0eafdf4108194efd36d7ecf7b34b8e04b49f930acffd574ee8a17de286d70f70f940032c837e29c08a8988bfecb716b932c651899978c88ecefb530d3a7912d7655f19b06b98f0ff581ca65cbd6049c0d1d02737a0d832e06c8be2782b10c9b914b7d198a79926d7b8685af7bdd53b41364b219b793431766e924c66116fe3e16491d7f8047eb3d19370142bc4db7ae54a6b60628165a3037ada3835937d8ca7a570ac0c4dbddcfd576324ce59bce98dd153e6a3b818645f3ef9af1dd4b5ea8aa1243dae784a0ec55fbf6dfbf874343d5e9eaba18870c113f3a9124d605584ca8765bbf30226729a0baf0bd27d62f5671f238c28c00d4688e591ed52bab98690dd09221b6fd035d2f3a25e446adafb074237dd966e926b2141bbae24d0525e896dacdaa429ed7d378030c2f0737aef0751087245129814f55be7b824aba0d4cd7c4a2add214b9f72e1e1d681aa0383041ad821c8c087c9b27f407c4d6505067f58c722704b972242c545bcaa97d4c489e440ea1d1f5e2316e28c4d7d4303cdaa3fe76cce4edc98938c1e249ffb92c164fff29668dac77918ced416b39ad02f2aff3379501636925a71b7aeec111d81cda1d756ae2eb42d3b281eeacff97963c2f0b322458a93fe24d7470e454597699f3c992e8cf047756226f1792614745701153a86ee974053e2dbb34cd68a02bd2f2c224f6739e1ee09e9730666bf030938d62affb9348b66e5bdf42743a50b52741b692a0bce3ab9228cec7a675a94e0eb2e189e870b324b04c52cd781b738f97e04b94869ff6fb1cf9e487461df836d3474971d8eb84a1f43b99f2fb81f1fbcba9f6e6235641d2bdbee313db4b1dc7e2189803e751276990ae63e20c2b9e05786d6ac9ae6e6701e5f6672fe367978088ab657c461105c07b40e094977417e5b3884f2171fc5544a625dd7157a416361791611133591dece679a896cdaf5a19f71270337b18a2b97e1207b4a915ba4e6216e1fc8d4b74886a80ebcb7cfa17fce9849e068b3e73a807e52c68e2eeca7fd720e02e934d75c3d8c6e7b063ab0389ffb5073bf767b3835f5935e23d07", "ac510053", 3, -1097935595, "ce5e4af1d6d5f548149b05c30a2f6b520758270c59517b20ef1f8a75596afe0d"], - ["325f455501afa59b34d56207bc6936c8b91dfa27746c78b748ac95053c2e4e25dec67af7610200000008acab0065006563acb2f012be016f97740500000000026a522a33bdd5030000000000000000de7c7f000000000079413817901c6f4760190513cd9528c741a417b83ecbccf7d51c6b8ed17c44ef34e192e1dd705ea104af272ade298ea752ca10bcee9c4fe424fc9e2e6c5868eadb254d7acb15b6f1fbada7e8cb0c06511d66e4df2f6a4475bc252010d824616800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b9b534db00c6fa16c159fbc224480f0b7f9ea366811c358e309fe538de911221c6e31f582e7b1434469869810f548ec92e7ac8066c669c464c8228a0c7031a036c48d9166a053e0d8903938737b7a068286b5e10b3a0ba972e4e8349cbc12fb510b2049e43b2765093a977b1b8bdfaf639a10663615a1134a8b0ee6d1d99652c02228cf8d4741b87f55d65a1fa06666c5a402b98398351666c287db34653ce20e302271d8b2f470eab8f0bf98549d31bcd556324db69aaf4af9c9a036acf7105435a0a266e3b2d70f14292b0a9b998d251d588ce58a8a2b9ba1476fd24a2f21d5cc8df063eb2e6a658e0aefee7fdd4953f8c2c544e236b61d8ca0bcce02965120342a503231f6823fe73769837e65347fb171b08c9643071851f88de95c508a7db1363dc0204baa492e2bdec14dacb66873fc058f48ba205b8ce6cfa83f4cdfeb202f0ed39031563c4daf3f9fd2b554c070241dae65235178bcb38686f9a8849ca72a1594ed3022f4e40d64509db2349f7910a2c516b2c6fb8b6846fefee23d7e2dbf442c6404e0317ccf7f36dec469df1b0caa1dbf17a44ac8b2f8ccc43225b683a407eaa7c9c62a2d0f95f54701b51f082fda924a9d36bef863001eb5196646d21870df253ec86f882410ed81356b758d52b26abeea40d0d4ae93c07e8ab060cb42444335b7f90793e7803d455c8c8b8fff130ef91f6cbc03268dc2fd19a598a0899801c44445ad5462630c555abdf8e3ba4bf6383097f73af5f1ced2ed1fa472a14c66fb49e9ac0c478c0df36e3d44eded36fdfaa8f875d57cff576e96945421dad4ac3841e9d843d7db74b2e13906677d55a83dc40aff089a77c27657930dc3582e39c933d8f6204a1af41690f702981be2edb12b89aa55551c279cc3ade224c8705093c2b14f1974dd463d79156f9d8a4ac77d3104df7833c5d72716d984ef5ae5f8790fcd154a359c3fc87605c5acbad1861ad21b84466f6b5e26433743a7955df2dffc174a4a8a15fae8ff9e3c671414c5f1dbc0a95dcb1d3b7807e2a6abb0b66dc24cdda8d038c3215425076f05f3dd2c0dc9e612038eaa6126ccc2efb8b1bd5b7fd09356b7c77ca97d46842a2389568ff49585765c9aeb15bab1bbf43dd4bb8abda9904a3ea4ccab9c680961830a427e5688fce8d0e17fc20a50fe378c0c9ea288410026a9acb51a2dacf0ac457d02ed2c7d0564962515fd379854f445c7b709ec22c8b16d5fea02460398ee3414da48fb3d12c0f3bbd6ccadd97c2729648e726cb1988e568ca340ca6547475b74885bf806e5967a8d0c5b7eae01304b2d405c48544c07a369bb4d4ab944f9a1e39e4521c0867745b58df1c39422f603af5e9d180940c64965ad772d43e8398a55d06ae421783e736fb2f2afcfbf2cd1049231ccdbed41ea570e4ea85880cc8163b9a8f90f0a260b7d94fe8091fb4e455bd9e8977dd9028f31326ba30a37eae125c1f60e1b06523ccdbe5f4ebb39246570db177a1e87e4a27b7ff11f2f6bcf8e91b0ac71969b1a6ced4e77baef7916989fe78ae0ca8aa5ada088a262dc1201c6d44363823e9ae4f2267191bef565852e4dc613fd1b0f30747ec83705732b16b126b0bd6c5d2455414dc032efed97973546a896c3c2c97bc61b6ed24593d157d42d467e612a328f7cddc7184127dc07d9fe5c109901a9317fca78a675af0c52a51915429a1a1b3f0b082f44142fb50ee24d1e152596c90670674610765bd797e1f14bdf8f9a83552a789359eec463b8fb7910fb2d8f2d33e839bd2c69f8513d288909e3d73ffa55d64ab52fd18396b4880da89ac27d6fb9edcaf7da1687508641253f1762f6397a916c4c7737fbb276d1c1716608423a477d14676e182e1d84d68463b35448d45f2026627a725e3e66d23f12a6c2f470a03feaeffba0c901687ea43a38478cd5bf2b96869aac7961b3efb1750f3e307c2ff8df3f22bbbcdc2c4563754302d24bddcd60325d750a0121c8cb09ea2701ca334b4a58052fad67d9f66b0d0bb543d1640d14a5bc1cbf14ad9cb57971111913d3db7634cf5078c0877c4278369da2e52030aafa2bdfd5af95d1b758d6d1014d59da30afb32330325eef1a9eb74406863e1aae3d83b76deb59cda3019b124aa67650dc20a40cddedee8d3572070b8be660edf4f51f021ec083b0a3cfefc9142a04410e2da74fa8613fc4ab8eb633b5170ea04d4bf4892cc05c854d859ef1636977e7bf1d3b09e8fe1dcaae139743e134b65bfe877c45457e08bd1d4854fb1339ff3d311748ed94a25a4cf161322a8f99cab192d556a040000000000000000000000003b2f4ff6a7e35e8ea2a630b034a71ccadedc74485a1a000df438d7e47356149e2689e22e5fcbdd0757ecedfc1a4daf9b41c99a48d52ad7ab55d20b28cdb0ebad2c101e9af25dedbe12b463322f25f1b6e342bc214b8002ab82216b56ca71d7c6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008014bd7fbf8e741d22c25db2c1e884e15399f7e28a6e3a02f8d1d53bd830f49f21ce413afa307d1fc8446862694fbca3360010ef4348f1eb258ab002e2f6ed88a2b0b56a606c3f2e486a38634503c9dcc3c766e8a77b3eff29326ab81e4268e4329a1a70374521f9e0e94e80585e3d6254474e8707e61ba94e64ff9af265539b032cff3b11e3f7666302bb1fd4b66b49e1c9fa03ca0dbf9268fd22a40efd9da652030b684dd0ce95e825fa826cbcacb12f63a2871634b14618bdba38676ba3d5a6a80a1a5db30b205f30ef7b82773bc18bb5b0901481809a91f2b18bf5d566d3f7fb920a7f082349bdaabb0f6f90caa9b63f9a66a161af42e255b2a6070198a3e910f0030162fb8793f3733aea3f2a0452a84d61f4a83de6ede7297b9fc6c50a792dea1802204cc606085f5a4e7ae3a9f36145f0f04f0e54021fd6ebda0093d6b48a4e8feb021d211c50e5d07577bc7358f7dc27776647d882e7152d6d05e424e082d2518558030800250b8c5b0765672e7b5409c5d401ec8c9a31ed15634b6f8255ef015707dc032d1c7d8dfd61a64410251fc6b4d4b8463659336317c97446b08d4edccb0f9546fa7202ce447bed2f1e77b7264232615cbd3de2955a044f4dd518ad27d30762c61330cd1cc3773616f239bf2415a0f6dedd10163585d1ec4eb17bdd5740eeccb3f190a4e533a093c755572c0c8447c44a374258d0461b88fbf5400770eae8a6355337fb9e3079eb7c0e4851a9408d628a6a87d503c6e6ef9bdaa6c81edf99e177e7e3b7f123a8e7799443dd067c09bdabf36b8023b0fc0104e52cf4b7289cc13e4a70df9046a662dfa8532a914f4419d430c356b4b95fc820d363d4454ae227197dcccc8063438423cc9cac54a94a05316f156360f61ec28fc72783e3416e4f6dd857199d1b037b17a7be3a6c70dfe761a68077ef2a2134a014bf6cadd061d8c65119a54103e5bdf3d7b7aede6b02a37eff136bc00022971cc394fd1a6444ff6054e4a611ac868503ba9d33fe2caffe865d3c20af2c5a8a5d3b864b6b9dcc561e189262f36191c99db53efd9af6b9ad6156f90b697d244205fe147495dc32d7b511a12a593f45742dc0cd1e6f92c767ce0b1019056ffc406fbccf40d6cc5406993e4008d1e5576a7e59c3a8840680068754571a30c9fa59b15aad6b1902b17ac4f12ec448cd4978aa9dd50b67519642426f556e3fc4cd2ddd20159280a9c539f4f174a7278a7a248382da6396b2ece28793c0b38cab7494d43ee971ff85551ef87d3100228cb7bb0e4a34c3d1cc3683654ac41e13a0ed78ec5549f167cc3e75e99e0c32b4615dbd8cc79f64a624135b21611208f9d3b4ab67945b3dbd99013e1c9639cfc984320fcb10da2a6d24820bbea6af1ff278410bbedd898af0f81342f7f2ce2cff92e373877a759366683145a76025c3ada5bb1469c1a186b524952ea1c6ac5e8f2f763a791767e909689d3d21649bb4c16d576770fd936eaa92241bc1bdbd0f7056454a979a07434e1374c74bf207319d6347c6656b867d58849a9a94ae0ec2a8ed20b9833b483e8def0aeb294bd1f382ca8dc85bff75ba4c49773b214bec82018a58ece0dff61bdea148190dc2573d62b64be373c3b7f36a61cd9fd1788477b01261c5f4903de8e8da2b3c3ba1796773f9e2ff8036991094297a0e7b664fc44617390457f2acda1ed5580c090c0db2a0f645a552fd263c29466a1b84b0395786a3ce5ce9e13824c99b7e1ad99fab7e7a5a31f24747eae7f3daf3ec0d84b25598733ea1a51f8b1e2658f0325a8da94184b839a64cc453a28886d4325271f34e94bc8bba225a76ee3d46a64db623edd204e15a6ce8306b095dfaf684ea9d2ea520e077a77e1752c5ab00fea777ca628d598bda4f02a9d439eae4ce05269b05893a073848fae57f20cad8b11042c232c24427838935055c988cc2b4ed47dc0a8d9a9dd1ce861b93beeb535a026fe04d15ac6f8ac955655f3d8bd1f051e293b6e6b31c5251ffca76a2fd2a6ae607d2f7054f96f76136cb98a115fed52e51bda7e54114b0eb98d95596b86576327ce651237bbe6e9ef2fa570d75c5cb39b5ad5f6aeada66d533c7eae90f964fb71cb04d225572b95e44b1efe1562e5613b17d6e09e316b5dee0b127ccdf6b146b47767d03a02474dab43429ecf15a393fc87b0def431d61f5ce1366e7da79df973c24bc53e51006d8cc73f43f8613c2442b01ec264ec74cfc96fb19a1f0cadda0769279b754cec6b3f918063a8b9e5ef372393572f37d80ee5f42b355bc950f8d6bae6e000000000000000003cac1050000000025031e7ca99afa09bab8e4036302f545032bf2b0112d7c8ad4b0222231e94185229e7c6999531e958aeda711f4ed84707311c2cca1c694d937097ef323f0af07412c611abbf178abf2b56186f073187570bc61e9d7345955bd681d300a40d34800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bdf254108fae82f8f56ae500f60a52b19ad4d862cc01229f9b280ecc0a588a263e993b0840256fc57dfbeafc297095d5f9beb71d847ddb824db064ef719bc4a50993b813dd492d0c03eb9ba07944f2799ef630dcbd5d427ab15765ebc76b22e44b24104d6cfcca7a30884f4a1e2aa0b2d15409ffe6559f8c5e5e90068d89be31022687a5c53b8492f81b5517484046e1626908cac5f4c6ff571285878907c77002021a425604910a417d0c0bd315d5739f56c39412a21adbc6593a834184d0ac93130a1c0831fcf22f83d8e2fe12bbf5b30dfdca4660cd325fc7240f3f97a6f3600952168a48b9e2ca4b4b2cb2f0ecff61e965bf3a7efe99ea4704ea3aab9d2588c76b03003d8df27ed4e3e125219ffc8bb54637479776bd56d45f98eff1dfc9c50b2f3b0221e73c28f0f46dd89f8fd2cca9979b4142af7cebd54a93dcf8e0e3c6d0625290022797979639041ecf9773888d733229b116fad24d70f6d714d6fb183b86bb5c9a022f85238a1885437f0c9f4e4b969a77d034b7b5b81da76b40ba188a7992fc724602302775f0d7bd63e70d9b4f18d02856e10b611840433929c0e38e02c407008f1c82744b320c466d268851b93392b88fffeaae42e3e40a039e094faac1db76a437cd136050a8cc9bc017d79fbd7f4e5df406f764059b0c8be9ccdf4410e9da30d5073ddaea386b0a8375b86049f8d8e717aace13102cd8c1c21eaae6c5f3bea72ebd5d0cfc1892d6a01d5c966e26f8483fc5cbb79c0c8fdc7849ca99c563b331c420ecb5bbdbf88003acbbd33c7d657982f77d6b57672ad735f658027b931207da8176bca3704e5edd183d4ba9770a597aec8424263097a2d6abd3d1073c473bccb1dd26330f7c3c7413f6f43daf63d74baca3c20d51f25c81fcc8bb2cdb1102df73d8ec13a049f1a063366c46fa3ecac55889e7fdf3c830fe21d314ba5cf79ad35043951b613506becb34ccd854384a976abe008fe5561eae6b081aeec8a536e642a858a320efbab8fd3b3764df12d732be2d7b9dec300571b44dc34cbcbb1691dae597f0e9f4e3c019987f3e04d41024d325d5b3eb0d7fa7aea2d0760b140e3f21dcf9b44ed21012fdb16420edb9573685681fab94d5e2792fc2f4b260693fda7ad5f054f3a6dd24eff0eff16b22b2571a1bb1eaf321fb01808937537efc7b8d3b439f84b58abde01dc69ce3da19a25f894396a0dd8dbc9865bbfd72cb52adaffb6b3b7ae4c25ebff24efb0ef9bd65ce1e407cb280ba03a29e2062b8d796bef73621aeb1c0ee7cfb77d5b1df64f3d9e7a438b8ac0cd02491e4da4abdcd990d9b0002a63d9c24c1450f85ef96d1a61faca03b0fdfebfa4fffc7d2e084c7b070c9ecf0f499fa937f6af9e54dc8f9f9612e579185e14609a6ef7376917d60bcdbe7daecebd7c175675e8c839d672b1feb7f623815af7005616576eee4392d1f31f9d3760d8b079acde637d66d858cf7c9596880e0a82c8d2351206d7f71e4ccef070ea14365a81a2888e8ad5482335770d262c8ba3c8b44fc486e5ed1a5de63802415b621b82184be24213c72992330c93a7959a93705f7f855d13d5967395ea99cf6974c8c13d60f95d1c8d5688085832e9e388ff3aebc4897275c98c8c50974167a5285459256beca0b32da4456cc15d4a16be6571f135d440709f43b03c94a5f2932d9307a5800da7fc5bc2bceb77a95c5cb8cf06847e5eed64535ccf7cc896214fba46f8fd10e3cb00955a9b40074831b5ab77b3c05f1824eb80641e3362af59b800960ffb5e5b44de302b3154d3f7d11185c2a62c83de59952b140bd73d57724f6258b781fab216b0439f983a89da4f8882f6c8c850a42e002321fce93bccd4037bbfa9e5ed538fac4ec0dd3e025ca37a9ecf231ae88434fe31163c2707b98b776f6fedc7bb3e501e450209edb31494041721bc3838079e718f9be043ada2619ce63988960b84d68734e6aa945aaa1237b9d7e037b4ae65cf1012d88cb073e307fa10b550d6a5c1c43b65026f4c02b80439a6a32d3f61325b79f72a61111a1c4f158540164050b36a5bea1ffab666cf086643220ea57c69c9600b4c75d1e30e764e9bb0314ff32feb2c253381eef1d3c554cd908218782f66c9ba53416729779e73d60e88900dc698ec386f61a537eff9d3444e1cf2422e11ba5a835e48c61bbee69213ac9b232a793bed092f5858a43af8300e47cdd4c0e58cb705529317b491171685508a3c67cf0325bf70907616377f090100478445efcd9cd795c5b07945a907d75ed84a97c66a4a257cd91649456fb8f9d04d26bb10f5de8cf45b48f8c40a741f89d99ac18a268fa3ff2727a3d9e579dce195b0238d00a82ec332250aced67c502d2b8fa72ddebe4067fe0d7cb227250d3b760836d66ce43165c126c6fc50153c254ca3e0358e23d36e1c829a10b", "", 0, 903307813, "7faca3188962cb36c245ed8ac22175264ff732d0cfa840883a0fd76f3c30489f"], - ["16168e5604f3f18f9f95944c2c7f614796cf454e347ec2413807fae3302c568c7df0644f860300000008635353000063536affffffffd1fb1f0fac0af43355c0e21ecd764966b45b116e86c159e6013781570781c45b0100000003515363ffffffff56bd2ebe1470fe7efa49774e289eb7fc8e16e7003cd2e0dc127928454c3b3e190000000001528ab954cf834925d15108feea22fc117dcba7aac68cbb8bcaab77ee50112b1cbe69a855390300000003000063179bd79d0274c23f000000000002abacff550500000000000365ac5166020687010000000000000000d0c55702000000006fa58d797cf9323c1cee9b09fd09aeacafeb894815465f0ae225ca3caf671dfeb7795945088b4b481949814e44a667782cf7c56e2420b24eeafb044d10d8e81948f915c4c6a317706ae3a49e149a257a183dfee59b205a668d8cc660ad38a11b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f447d0db0071712b4b0c0fbba0761d048949472956b9e0a69bbf3cc330d101c1f6109abc82c22ad03bec0b29c48d685c6584637368d2bab93ca9c7e5847166837683688d8dd7af5a93a061920d3b78c6a8e796f31d35e7949b5ef197de73355b601627a866a267eabb812a8412f41692e1bbd62075cb5b5289630c254c101078031a408e366c3d1f0f12293231486970808863a52032eb889d2a308916ea78f14b02112ab1b4026e6966634362cdbcbba7b3726a875add9cf68ccbc9486f7a8ff28e0b2042ab9dbb7b7497c2083f759ebc0236c7c2ee83562b0af122535f1970661fc009ff7605119db3bb1b8d7d2c151af28f42f6aa9e25d12162bdabd60e744257df0219235a7786c9c0fde28469e438b1c64e843ae876866b4c9c55a14eba4fd6f3db032238faadff841170fc09d67763bb5eca136be76aada088c102b8e2bcc327a9c402107f96a8a0bf0799b4c4790909b01c4495412d004450939a7d6334d2b9d3b2870225d6f2cc7e578473083c1d34cb9aab03f56bd791771c81fd30e7de16d7b7937602200c3790131a2a32829893baa53080c3101f3883cbc4d1bd922f99d603ad741cbf347eb32e6050f16f7898c969faf0b10229c8cf17e1dc47fefd3af0212cc3bbc89364fb21a2e951a75f75948bf4359386f23aa5bea2797034545d9bc6729ac8224ab37fc06caea3dec0221d95d9af61b69c66b9b8b04784563c85b46b531472244c867e66ba44b274161fdefd5703fde7650689be7889a830eae605dca322460a297373b3a4db53a4c7b23c7533c044cf7adab1387683be9ad9806f5d6aaa9cc4f44e66c4b905689c627021012c173a03b2703578c634c491ee6e21d2da498f0b2d62b0eba9c6184d3fc1bb75c2e2041e68511b7cbf2daa78d2c7e5d0365382de9238347cdbccc0520755e685ea10bd415d764e89df7be5b03a56c16f1c23baee2a923e67ad359d2e6eaef397145acf8dac59198bcbd5e1aebb675dcb6b72db959597bdd61a8414d1abeede9205f7b86529986a6574fe91d35d1900ed7a0bc9c32789d20bb61ba5090132bdc673bdb28cfbbc9b8213f1f7c96bd2ae2c8b4b217ea9d2f5601616b8a7651808832a32bb0a0c766c1179fce3b440a7ec7649149ecad0d4eb92444ac9b63d2d288868589656b6cea5638dc0f1e427cf512b1bc7a55f6f41b54b672ba924f622d3c894dbadca33da0439ec5240a13d5953c659e6d39f4cde70035ad19a0b291058d5750e5252a29500192bf456be9f83dde561009ad17915220c1c68eacbdf8d71be17d4beeb0f5b6a143d46ef2f1665f36bba1e9534a0df4e3847119bdeb5be0b3f1d5d0dea8358774f09579aae17e74d81f2fb5a2113b5f1fceefc027cfbc7e4e4e6b4b1623593863efac5369261361fb23c63d48e6fbc7e7919477b7ee71561d447ee2a1cd65d608c383bb45de7fe70298dc77e30fa99850dae100e4a883b77c4589455f45e6e6ada287da8d5aca850902a040cfa368e28db6420ba7bd81124e695fc334b6d665b704ad48264208ffd358d5f9d0c5bcec817c63f581d8c444a1e55721b4fd90aef943e326f56a88301b9d5b1c9b68efba26ac3e6ab9c52b6f7efe3f40f5f13548eb063a2c2cd05cae1b5d2932d8c7ce9ebccd82a0596edface486cab83a5625e362e97e13df03e0e5a7c13ee769eeaadd601aef1f4440e332642c0107dd934337693433deb41915c401eb99fa361ff8f18cac6086207797e8ef5492ee44472c72fb31e46c563cb6b57b44e4eb5818fe4aeff67d8e588360d83de5d8a45f53226a3b813323d96222eccda338f489d4e40a48aff3815f48ad96b533a89d43ecf017392bfe7f1fbac2f7c3bee788fbf43594739f4761fdd4afac507318fc0a87d948de3e8b966a4015477959264572e15c89d955c99df13bb03943c27908a2ff994f74d1bb6cc0ae3036f46ba2769ef72f79ba2d328a62a45257ec768b28c5053bc068b8327f6d253cfe1cb5d24cf216cf393535aee68b6807b3b5edb4fd81774f1ab590a91372acd1407f39840626b5fb45e758916ea19b53c62eb03472c139a76947c75e0a8b622ca72feb4b1972691dfc763cc0f3192698cabd52bdde09b8f9671a93846126305fd153f06e41d8ec0e4702188f2be3a13e529ff0e6238710ef71eef94e6ea45c062357cb3d65dbc6c78bffec437bb39a9afe75215f9e950afce606df5370a65c7c49bdb3b9f84921aef36e246b80a99ea7f83ca4c5f01af231cb06b4e5e25db5f96b61de6de62159192e45cf44df620430946557ef900a9231fc2ceddadb79e7b788865b770fc3112cece0dcc21c8bd6dc81992d596cbc45392ee95945973cbfaedfa7deea247561868f869defc20b80486494b5ca5b11598ca816959eee32fb67c7d5dc13a61984db654ef4029719a70d0677ffe79db8b0c", "00635352", 2, 1001794760, "4fc778e0da9b4b3ce1649e83df9855385b44b188627f0e28c980d19ab211b05a"], - ["2970338402093b71c16b0a23c41a448cf766380d046ff060566a35097cf7c1290e0f4715310000000005ac656351abffffffff5d23358c08924835ec3a876fabb14fadfc31f0f687f1f879ad6b7836eb4070b002000000046aab6363ffffffff04cb4c0003000000000852ac0051ab5152ac1673b8030000000006ac636a535365ed1da003000000000852ab636365635300555a970200000000086300525200abab6300000000", "6565", 1, -2075150975, "d4647527fc5dc9a6019cb37f0020554551657189a61fd84387723294970b8a5a"], - ["9ffd779002b4cfcbf880e28a60d877abd9550fc87a1b25a257b8ee92960fbee9a572c911b4030000000200abffffffff15cef9a7bcda6885de240e9120561b01d7d391307b6eaa8879636dbd8db99582000000000700655100ab53519c6b238703c810920200000000066300abac536abbe4cd0200000000036552ab1585f10100000000090000ab525163ac520000000000", "63", 0, 340739621, "ce554b552f70d3a699557cc23e36aae4463c01eb6096d97f1659393369bd07c5"], - ["ec624ef304eebbabcd8f4110e06c12baba3b09a13e6824ed3fd3e750c2154d01b88dddba14030000000653ac0051ab52ffffffffdca2010a69c6e499d0a3084b9587c17afe85b272f3b7f240a41aab4ff928caa40300000003655151ffffffff7a8bdc820666bacbbc7257049e79a425b2f2d1015a21f8e95586aabdcf671be301000000076a526aacabab51f3bc1f829ed337063ebcb09ff7894a07098d7a6e9f9e8c885421074177d514e181e219410300000001acffffffff03862ff3000000000008abac6a65ac6a5163be13d803000000000853ab0051ab536a51d31f9303000000000953635163516a5300abe6e5de7f", "65515363", 1, -259701290, "4c1c0c3452b6b1ec6008d3701b68724ad8b9621247094c31beb9d54e0ed7c252"], - ["4588794202b8043ba4a721e03bbbf9691e14698317bc37ed49459ab67d93293a7a476a557d020000000963515300ac65ab516affffffff8cf0734111c5d05d571941265f588cf9e0a8c395573e0a1cf96f5eb4ac08b9cd020000000563ab516563c9aacdcd0326548e000000000001ab0e7f97040000000008515251006a6aac00e9855303000000000463ac65630000000000", "00516365526a", 1, 1014952118, "10d26763e7f0c573fddaa0af979a6487893381e3f877e25beb6e2788210569b2"], - ["f3d8d8820122aead1599f08730bb84e4615c58e9d8d19b27d4744779de21d8848ebcf426590300000009656363006a5100006affffffff01b0b9a00500000000016a00000000", "6565", 0, 1627452599, "df9dfbe0523c14792369b8db42c62b8b51889f7cf358144f5ea192a7d28c840b"], - ["730b96f002cc3f4c89ed24cbfeecbc1cde91428ddea1d437eccb01401a1d75e26da34ea4e30200000008ac526a5353536551ffffffffda6e50d6b6bcf7b8b227fdf21e9228fe65522fe58780a356d7f65dce0561cc290000000004005352527e8a1e0f0295e6c1000000000009ab65ab53ac00ac6a6a5a7dd400000000000000000000", "535352525265516a", 1, -794700861, "a472621c06baa06f032391d1c287e1dffc355ddffeed064aff289e558b156249"], - ["602aa4d9047d93599fb919cc55cb861bdea2afa0cf0a3113d080c9e0b31a714ff233f877110100000005ab65510051ffffffff7166c368d4c5b31a34f34861be95a14de71932e9dbc229c79cbf17eb1cc5c4690200000006006a63ab5365eb19f32f09a02b7d2f776e13f77ae5a7ca7362ce39eb242382365eb198228a8a06232e8b0100000000ceaf4bd385c841b35f0eaa68c897a6fc77ae8da7fb0953cc88387bba1822a41adb6142770200000006ab006553636affffffff03314e3901000000000263ac180f380200000000046a6a5351726045030000000008650051656300ab6a00000000", "006aacac00ac", 0, -1388646795, "950ad71d30c89ea92a671cf75b8648a19301baa0585123633601ebc7a297fd60"], - ["e3f17c6003fbdb73ee4e785a840c95bdbb671dd3ee491eb733e4fed535d6c300698f1255540200000009526351abab63656a51ffffffffcbe2eb737bed4af394463993247bc9f403ad827524192c92d97078580791cf9000000000066353655363538dc6ed03fde75133bc0adb813790232650f1e03a0b2059de9342dea919b5bc3a7729c1650000000008630065526563abacffffffff01ce9bd80200000000086a6a6552ac5200abb20f4d0d00", "63516a5151635100", 2, 1131340203, "1e88ce1b19580f1d8a285df3b620945ebcb64d75dc025c25f0ddf97e14281b00"], - ["4fb51b23047d4236e833d3cf70d65c3df208f45afd36c581e3d832b4c100cab5a57b875cf0020000000965ab535200ab52abac4d3727475cd781d1ee4269d999a0a54ca1641ff57e70f6e42be284fbd2942eba18181db100000000003c6627b513f340029cab17cce9910fb485145d9e18c1da80a53163b00056bf5b5e5166a60300000002516a8f68ac934f9500d71ce1a0b55b5e4f838222326324be16f253a81af14da3c2c5ef50478003000000085200acabab52536335b88c7e032f6f4a040000000006ab53005265ab953bd7010000000005ab51635351b11d5f02000000000253ab0000000000", "", 1, 259841327, "c462ef448b54e7ff807cc0c7d4bf4eaa28b4ef03dffdfe18d864ec0379607d53"], - ["054fd8220111440577303defac7dfe3f2a44153e6eb2df8a8643e5d64c30e8e06e67928dea030000000200acffffffff03a22a790200000000076a63636365526ac2d84a0000000000000d345702000000000152e000e11b0197ea200100000000000000000000000049dd97c238725120df6ffe847d8b475b2bf83b235ca1326b052f77e9fe1f5362af98baa95c9795304432a728f450fdf29e673afa67e82b7f4c4af85717d59f247e969fcbd30a4fe5237a0110a8c178951c58dd0fe9e6d763bc3cfb01d99f135800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000206012236dc9b4157aa6af4c9dca2e2ebe6dea25497e44e9692a4b9c3aad507181352da129995dd75dde219d2d3ae883e81496ca9f850acd26a803fe01b0bbd10d74ab025c414f2de67abb48ba71c0d82d0fd2c00d2ba5e8b571266071872e5261a09ca530326da5a94c428d42fa16cf4d75b86849a1f6dcc86e30e04ad3672d03096baa8f270ce36d9974216a51ee1d5638e25e0d98e083fa7278a234fc1220e5030b3dcdab2e0ae7954868638a399e8b99f84f67ba1a48ef6977b5f8a8054baa4b0b2122f049b2a10ff1ab0a5f80fad703f68d01ca8a7b21dbe0ff90a34a10b64d12007e66db28fcfef476d264cfd81a3826ac1b032d2585a3b4e23852e7508dff9b02278b50de5aeeea5e78d090fa1b131aa558ef438c615425a9314292b07d21da1802134f99b58a9af5a2b296a76d89660e024a219ff9fe18cbc89cc040a42d6eb2d8030062e7573fa5a2aa0bdcac77d9f65b8dd06851edd834cb2b57510dfa55f8f2d70300e90d5b44fa86b18bfc742c35650a6c9e7cd41e80ba46721e18800d4c5b0843020de7d615f3448c0eec49f38fcad618e05ef3e691d3f1913f7c4fc34f4ac1f0ff20cdb8a26353a4b12915a58610995dbdf34f9c193297aa3b5a0f5b01f05cb6bf67ed5f41460c23ccda05a428404df5e48a55a24f7a54a74f78a668093caeff6dcc312c9c36ce6cff929ef98de8290994981d276ec23f91a614814c06f35faa292f527906379b0bc07e918d403dc4adb43b654387cd087406d66cedb1472e3c78803fe1618d2e73799d85143b85fba3961544c51a996cbc4391509094be6d3d141b38d15442cd2710fa5635ce07ca3149f99740bc1670e00e542ebd26d95de5b774658b1f22588ec91675ebd86bd47fb631d8669fabfbc67b7951118c538e03c50d96b26bb118697f43895f44ac00a29226aeac0c0279fd1be88432bf1a2a372c128a7c59b8f4cb3639670430e6640ec97b3b4b70e9fa981e8f985b5d93ba98e00a3a9e9aa0796e16926b9200530735ee76fd191295d6380cf778ea2243d9fc8a8b65b2f3c0d3ffd34dc64032296f15a1d61459bd518095f01a0e0c6f49fbf3e88b4a3806575bc1ed52fed382a0ab7dfbb5d98021bfafd9804dedca928d4c9db13a51127570eef4e2854f1148be70b0491883e9f451799bcf7f9c2ffcfd54fd579937aacf4a8924b039d8703398e0f94d3ea31710ce5f5495defb867b076a958e3ad42685a9bdeb83babf553b31649a40c8cef34f8296ca497ee03172ae68549b727c2d0cdf6960d57074ab507d3916c305a3e68c23ac6673019edff5551cc75596d5c94f9736476c524e3283231bdf36b3e1a2b08b494b8a87f1e31e2dd80650e5573baa8db3bc6895c8662260e73380a9b5fdf13f19900fb9e3ff5849b3d0998e62d6080598d23ae8b38d9884be12336b5167f12e5c6f3cf735aea9c2bbe5fb54327be237608b992a7828bf37dba32899108665adb4aa8864d64133d22d79077adf87a9f8a350693bfe26d68cce1ec344241797486739f5e05cd95543fefc8428d8f7b1919c8eebe9d19a8e920ab6b263a9c6f856d1331877889f6f5f479b3e34f6ebe03942592e6d4273c485370db2634b9941fc000b8664d3703bf131c8ea461dc94bc8639c9c4dde4ce040139baa2e65035be1df642f0be20b845bf333ac29d420b3beb50df165e25b3f019a44f7d6704b2b776ee9602ea24f9e475b708c0a842dbd5fe575054123d9642b903e1b562b95b127de8139e215df60ec83eba1f95a26a9f89f771a787f7cf6221a08d8fd23d566e20a2010ab6235b10d705e5e0557427dab664fc519d8d25e06d2542700906304c6f6948aa55289b1a029454e8cd3a7523a140b014d4973a0656fbceff833b6c8495a62808f1b54c18a72dc5f5bd82e7ef4288b7cdf66cb33ed12f7a3299c5bf03b10dd13b2121285df8401cf5e0df1eb3d8b4f76d3e05ce9c3f295f95bd7135c796986e6c645b04b3546249ae38fbb85c29426fa2a4cc54cd0b77a202d03babd74aa60d507078b84d0ea8eb5d85a86968953e663268627c01998bf1f7421452a88c364f976396ce5eaf520d7f7d7f1f58fe26923906668c8e0eeea24bf7e9a9339abf8bd56441d3999ef7a2b5e0babdc7a319eca64dcc197493fb047daa9d0e0f231e9f006dee4815fb15a6f4eb2b63732afa9e1cb4203e2b2d98220aeb0fd17c54af33350958561d612553a283b099329029b80162bab367eae5f97ef3353062521f294edf5f7994686faf08130a878c1f66698e1f99eaa1f89d19defd7f8217a5b7fececf8202b970c49e286c566342d0dac611f82a208a3fccd6a3b3c2c45cb402f11eb5ea5c12705d987fa4c3d2e18bb660ebf3b00589e289c080c29a249c9d7e5cedb9e50c4a02a91dd7e56a59421c35207431c78135dffd2e6ce06", "", 0, 2070529321, "f16c97aeea9638f40efed010227179bcd6befd1430a2d5f566d8db475b87259e"], - ["9815ba3b0144295ce0a97a2e8b341b49478f8486ce4dc50d24c1977a9bd4de84b245fbcba3030000000151e20505f5030eb86a0500000000075351ac6a535263172dec040000000007656a5251ac65ac15426203000000000055ab1e740288a1ed0000000000000000000000000022075df33922e4a0d6ac332eb382222b8f9cca2cca91f406d64e6f996e751882caf34eb93672baa57279930dc252f233a127b02af5934bac8eeddc9ac17c39257dbe36c7e631aec1671aa2d9795e9ede40d34bc5c769f588e7ebfddf228be3c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050ead43b1b21955c190ee223074020543005cfa16237b69f2c7d2008bf56007bf90c46b0218f3150866762750073a306c3875f156a2ac23f2b9af89cb1d95fa9603392f24136b8e7f3372c24be1bec7b26a4d0adbd25c7f4b991c8982b57eb82efa20821614d9b0165c42f01524d661c3e2ae52c249ee0d1811b7b95753c41a503275f6cdc22415e297ed98c7a6514fc96ac22ffffe76847138594426f41d317640300d5ce107e1093ee69e704e249aae9a03070e6de6dba62ea023a734d5ddbe30d0a04b351db11a1284d86ea4e886842d10717e45af633c4131b5a4029a1b7c31bde0742653e22c27c63ec2832f44d6405c450254d3e5c8aadce942bc8ac9bece7d6021ec545e3ac9d0fdce35fee9dbd83a4b02012b7dd0c11b4a87c3b92ab94deaefd021229ddd27216fe70d2f990e71348680302ceab8573bd6c08c3a731d30518b8a2022cb1ba8b3e3a8ea2f192562db1acee61c2d4ab7a26654a54fd77c4a26a7e2bcf021404e30ed0eaefe30e3e037b133c37f616eb9eff48cb044714c67b4f5b5d79130327b7a585a124f9db529e30cf17accda54a7f6493eb5bf455a64b179558592550e7a2ec04fa8847b950d8daed968fb5cb634e24182ced22d4a9e73f88eeeb2b8d9b93dc6d85b2f7ef0e5afed3500978d3c9c554d9fe16c21372fd38d98dddc2a2d9981db39715d9043e5e9cc257c878f1263b844e54f31fbc8ed448436e4804a34c63de188f26551d1c41826ccf23f76ad6a53364fd8dd9b463090ab45b1c971e8a5d097e46ade38075ec9edf81ff463e3113f12a875827abb364b08e92de2f226aa6afc6bef186593f92b6c86d06c86896dd87379b5d098a3f1115b712ba888fa99cce5d37bd11c17ad6786d9cb543991a65adf88417d1a6abf441bb33185cfb9d1a477577d203da4beae0b2f239165e6eed7f8c4c0a6fc1ba4274f99e22be26c1ba0d4377b9fae4f86434bc8ae8eb4e45258f4cd22ac8f1cac2f35d0b21fc3f4dd7de661ee8de4fce713b914ae83119e24107558582795f1410c9c6da94b77f741da0c57fa457356e54c5653607b26566a087e66a87fd0242732e8d496b4bb990190872112d7ca8c3b3995e05953438d64032315ffde74d195dde3f33efc684d11357b70b30d9815379fc2a29eec88d6313d8211d55634b1b4512d40b917eca82056f3b90a3c9a76b773a61bc962d62c83d415922e01a82acc81bc8151710910523e543af91b5fcce9730952ae25a3c199ff60a47964fdcb283414d7c748781bad7c07cce0768234743d7e80cc9e8044cf5ef78f2b0d0cbbc644a40dede16d5d370825e8c489af06a507276448f0685fe4e3dba277fb95fb55f61506590e39c4329f84b00cb30d1630777b6fd080304f6375aae081f591d42603adf7c67391c62eb8d2f5a6d8d634c4551b42bd884ae7d92aac1a7b08889a254943103ff067f8f60f5314cf07b8936d0f60f8595ad88f73e17ffa10431131e81e5ae81687873f95b54a416c253778fc3b11dcab99eec2acec7fc9f4d039662e8835def6124266c54f2cec434098ce9b08608d24d43980646c294a83d217bce444b866a5b942be465f42e17776358f8e432ea91417415e7f13a58fe688b917b5151a30eceaddbd0a28685d11ab753ca308705eeb2674aa6e6fb62219225197079ccc45a78b223a46a411946e047d0c6f7e3d2bc2503e74d1680363d9d52c47de6e7e5bd892b807cb1f93e84a50c5834524e947dde8ed6166f281a359baa10484122d3afe76c42063a48eb296e061be4ecc368959b7f863f0178cd242f4a81e48dd01d41ad76852235375774ef6aa606cd4fc5f9024a57658b8d5ad54740ee603322ea5edb546e81bd94c2baaaa08ec096af21535b52e09157160142a01136c71a9190ce0005d0b3969bff42d15a24533dadf1e510ddfac8e957a89ea449ee652e20588c8dd11bb6042e4a142d79b4a5628fa3f49b661c613069d56a9656bba77d280b8bee2e8ba5e349f370861be3f9d6d488706eb7c753972f1d6a13058faf90c4e37bb69a248ea465f707654444173a6f7a2a3c7dbe4349898ecd19b6c0b3717b6b416dd694ec9781c38e9649fa857ac163f5689c2092135190d991fa1b6cc52d716a7712767c925edb765eeffd1646832c6749bc2697cd019f64afd472c7c7bab5305669250ff6ea085655086689aad3378315b98425a58341da93d9019500c1e9651cd78f5668ddcf4dbf10ad9cffd9c39f3ef28b89ff3f4941bcd64d182b74847d0c77cc64159e518feef7582710a6b5f8c7fe038a0500000000000000003e0bab0400000000a4bcbb1db2ac10f645bf09f7eedea8fb85e7cebf9719edb6791c1b264f1351806f8d4956d8732ee81ba73d09480b402ab245c10c4bc0f396ea47c0d09e82ec9c4fc93dffae8cd4870bc22ff0d3fdbe22bff109dcf0f0b31f58ab26734039dead00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c31b5bfeab70a89e9f1a2a58992a0660d2e72e373b1cb6c3f0f6ec8077dc36f22af39526c5f3b6307d8246f9d9d6c8d80a36f40ffa5903c819f88ed3a44fb349f5b552ae2f19e294b36066db4dd95b9bbf6ae08d501426c8a984fd645ee83226c9087a9449470369fd6c26bb924a1f312e0e5a181219322d7c623bf0740e554c03234b1e29112a734ced9d078a18f79f1aeb1ddcae8f6b7cc9cdefc41f97308414032aab2aeba9576ef162968379750cb096fe5fb507dcfcc6acd8e72cea7544907f0a287cb271f0776d2393dcb1dad076c4262cb7f4789c58f497b6fb959181f027e808af5752a9e9578f5c7fe174d41cda6d4b29bc3d4edce73ca9fdfdcb9a30af91020dea01afcadb2823713553deb64f478958593e8fbb19b3cd6789482dd775f3f2020883185705d8e50707506aa9eab8071ecb55a0829f5332f6959e546c61ab0a2e0221d2daa49e3dece8298214c3419d8f6050970e6e76e4f192bf6ff7c0240ef529030b8a8d0b7a6cf9635453f47e306663c2abf62e51f9e04e8bbe7c6134f9704751030a202799583d9205055c081e9cd9376af0b1dbe5fd53cca403b00db4f19398057ce89f4f0ac016c90dd76e4792027a314514890332d26a7097f65d57b1fc295934ba676a2d634c25d5897acff070e1cb9f9d0355e63c66872ddd2f29cfa70c29a1295ed0f769cf0da02345defb206c3f56d8aca4cb45e2bb2a81d6fc6317dc5d7566988667609bcf1110600a19474c7b837a0a5b611fcb7bd4d4f448db020b938d512eb91e8b428aeb9c21bbc27c3b826c57fe7bfec9728b9c907b6f81e1c6cd37ea6463d86e80ea57af2b18605d9f9b56d9ad5dd7708ef12a3bb3773422329e470403c39ca62e6544c143d1baa7624cd481910bd40058c2ca219e72f7422a21893a726f00b817516c7150bc7975ebacffbc816c8690940b3b2179da72422b45867651a547ca4be25f43157a95cabb2b2cf5c9d13369fbd79fb8e40ac2c998cd0cc90037a43522244a449b5e366b94acbdfb314bc5295f7155db597ed59c1c1ae442aeafc1110954947759c2b4921062b1e7d84c4fb2f886240767aa3f858bf145b33998281b417cabc66b42f0fa27a4dc877ff9448257fe848905307b566b46220cdf6afac309821e063962f9f41a618d0b217fa39971d63066a45ab43f5818956603b44ebded0ee883a39a46451b6c7588593e7517848ffdca0b4b341c1857d732ab3f67004dbb30c7de8474e03c0df346f97ba5c6244f0b55a637ddec3fc22538c4d810e595b96de3ba11b2a68db889abc6655c297aedc75723116244c6deba23a57aa6c172004bea8562689578edb50308d2879762931ec43aa7984ef7f900e8663f64ba863dfde5c6aa57f0461a8e77901b1eddfbfdd12f6edb783232b1807f6d856ffa3513ef04765f44009b79d64d49ce42fe4fa7380e00704aa0832baa2808539ed6ce68e7e528cbccca6030e8c06e84c17e40c4ffa4d07b34151a9b3e9c13e3aa051827d830fdd774cdb323ed8c24138fe75c85522f600814352ac6e0b9282c0bc531b375feb94e3e6afa82f63799463d3639e57763d24561b163bdc32eff774c8794f9a4ed53426f130131aa6ccf921fa874a93569b866fbfa0e4c7942d8451ff27904883ed569cdb6070329429cfbde0a7977660b6d911004a3a830dda189bbbe703e264f19338c4e919db148e3a05403b8a803edf45199b505d50c360d34558c2c2c9dc2ae60838dffb3b9e67f03972f27082f62777035fe4e311a1b35a364beb30459f86934efdfe42c237d6343a46065fd68858eae73f060f88421cc214ce723c3419ab468ebb2524838626857a0a2f20f33876bac4c444d707da115cfe83374a3fbe421c8619826b9b6e513e3201c0f2e9e7ccecd637531a17a1db1979b3167d8a37f50da5b0f7bf5ffadd85923b6b61739ed19dc7b239e0d6dde28488016d7bfb7f8e7a8ec60ca9cb45ffe958b1d0de14d6cefc31d2298966d008bd6b8a8aa5c8db97792cfcb1d156da3d60fb03e3b49e49fd5616d262b70fa04c1f4f28b112de223d88de5af7710ae643d11d709701acbfa25405321fcebb72c8cc21f5923dfbe7cd6243afdd0f963212f955eb716e55a88708c7ac39aa98f3bbd80762a2f7751df7751e4b3ab1896ad9975c9c73e4254a66935b2602923bd4029d6a6bf1f78739992c7d8b0a8634ab50df74d42457ee996dd8c05e8a2c8ac41761350a7ce29229065d6ab60a989d3b1224099db41c14013da7c3f0bd589e8cb84438c1e77571261226c3b236e25d6fa51d4e99c873860ec13aea4ec7089aedf711b21efc1d1f58fba49057f0f6a88814b2a6c5c999efc5003554d66879c2ab2feb13128ca77fb996b162d58113deebbe02616992956da5b436ceb199c2b4121249467def4abe446cfb38592a32ceb07", "516a5252acac53", 0, -1253002160, "06a11dd91b01b190705f37634f66466ea26b18dd95ac3e515375c64aaabef459"], - ["2b5c6719040c3d261fb8f6cce1a8f216d78058d63a581b3bea34b19030d15dd74e892302d9010000000852abac53006a51acffffffff02ad7ac26fb207e1cd9161588e364f0e77154f5a9581b087f544b99b19e4309f030000000200acafce2aaff20e1f4ec89f4d7688242000abf9c4843034235edeb2565f8089a11011ccd70c00000000076a63abac6500acffffffff94bfd1d3b3ef1a7334e19b1f7b7a9d37c8f4da2c4ae881c3b9b1dd7482dd0d330300000008516aac6365ac6353ffffffff0308c3bd0500000000075353ab53630065d832d9030000000002acacd91e9503000000000351ab52b759d631030000000000000000ac69220100000000bd69db426809d6840a4469cd58757583fd0803452fab79046c96beee219c38f42e643266597bbbc2c838054beed103c400d36734c295b61348827e8805c9172efead005763ebadbc24ac1ade27c62985ca752c3e6bbe26979e0dce9b1d4ef07d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a3d6fee38affddc2c8d8f99607ed97a564c041e2c30f3ee203868ea86f4b5639b5c6ff4c62a31c931926f19fe434e844bf2d8b71a8cb16aacccfb36d440bc599809063eddde75b91b223a2fdbaabeabf6960db1743765a577a281f4c74bc186d82385f56a514c2119e523f307a11d280364e6c56a53408a8f29045d69c68d4a0321a702999605b1c810f3b750d12a82601e305b22329adc232615e0bb7eef306f020a5f10538b02841d02106508c28b9f27d58eee4ab0d2b13b4c11cc53668fe40b0a16e8e8c1c934de22c26e66aaffe74f98f8289f1c61e015321c739327c0b6d0450cf636e51bca3cc513114aab0afba049af60e2c6ddb69244959dc15f9b388203030b1105860293ff7b61563793fdec62f1d596a57bca347c92dc0576deef96284d0325a78fb1b96e898b4df7e331565ed41ae79f917e949dc0b532f32b8fa77a95a0030208b2b36ec9267d438bfb304cbc76de42e31b1283f21dfd2daf9438f73897940212ac7d3738e41e16e7bea844399aa7912d68022e18a3a8aff91db47a7b70300e0216fa509b4870e7286a67a010ada141b42e3267cd328b8c8109c9e64dd841aa94ea69dc5365c143fa53084ee32155a74b2a6bcbf4533758905949c10b23ecea30fa300d9c5fc5de42a7ca850dbc5d1ee9cab7e94eb391725e5b79549796a13ceb4abe4bdac5d3ca79ca8fbac370a41e27670f21595b5ab5fa79ada67299097240a19a8441c5345b7dc9c9b5cfe10114f38fc6cbd341273ffe208fdfddaf37914570afb052930940d4d70ff3d3947a24b36dddd57676647572a3c506542fcc8ec060fb5d4bc858fe26db921d94f51c9eaaf6ba2f270cf62819cc22823b8f4e939db77b494261fedac5a76041b9a20c427ddc09b154a13053c006714e6a782a87b36659e955c30ec82747f45a1ba082071cc3c4500f7dc4f4a74b52b017fdb6628e669186abb4a0ca9af1970d54b05eea2a5e95c2b78c38e519544ce9ad67211e189789dc57317bc8e74753ed858f295b4b4a1e164ae6307872092a88b53d00dd2552156ee8b8d2cbf6cb8ecfd8dd42e1570c578b996cf90303c1265186984f85905a8e9e76902e6a0f1d389c0d6366ec1aba2e73019c50ff6a4ff316bc9875082fcf6c1e4702eab1d9ce95e52e2223f3baeaa413d6affa098285b91683eecd9dbf38e65449867f330ac4470c392fdd8ffa23c951c769c5a8fbbd48b575bfb87b2408bf924d0352626f68be0a0a330aa4d366acd869db9a66208a08265bd4baf1007e4be5acc8039a87f52b69e8d3ff074d93500298449ec605ba28a1123b749a82e35309ca3a16679cd06055df7cb982094ab2198e19cd4f36631ff14634e4d87045a88edd1d983551d3febeb507b528163206085ecf3161e2dbb2197cf0d6829be357679c6344832f4792593676c4b5f7c815ee1b74c6affa2784f527f301a04a0b0c0a51ea9f74c9fc5ce0fbe695b3d72767a3cbcfae29f145944eb54048de68e29485d9298c7c9073669867178b136355384953abbb9d8d51d15b77cb765af51be0d6688e3d2942abbb0e2c1c91d85ef4c8f876139a12aedc18bb67607054574cc393caf42dcc36adc2850e83812b5cf2cda77d2302d5927e141480680f5b8f03facd8e4202c78472856811b4c9e02b0b2e2724e7f99eaa7211edf9ba17ff5d2ea3e157a64c1dc1c40c8c464a91ec8a663b32e5161ec155423dc67bbb3602a2dacd6237e12eeda84d4785aecb4d07611e5ccaed133d3cb43758350a8d1dc043c13c4b608898f37a8ce7eca3f31f0102596528b9db9f049c7c073f4316e99f769db91d9f9441de7d5b8b1ec785c1d8c05858e6dc3b6ce8f46564e5feb0b43130e3f65721115a111ee37a78213cc632a7973c43758542bbdfead279acc352a3aa323674c466774d19a6446d36a29ca912765991222cd02800e1b012ba7b4ef6f65163cd2f182572b65609fc579356244585e7526ca6649e347190b794dc78f336a8bae58ad0ec9bc3d6cb85cc5edb8d0e5b115584738f67837c4071c320f78a521a5d1c92228fc401cff04b3e7c93a8a566d07d926a992add5b6ea01fa06723fc4993b1b6a22297811610ff56d42bb7be060ade8da1a4c2b292529124770842c971518a46ebb39dfda523e51822e1d18e25baba3a8d5e5436f2bf1fe06c4c88262e2a1ce54867d32cb5ca63e6b00f69741d95d64076c4687be84a6017a198e328915526078ec15f326403732de205aa7fa453b3ffeabc907be55e3cd6c495f9cb388ee2d642f7d39f19052af30060e7fb03c4d4ac0e010000000000000000000000007a074e7ba3baae44f374df2ee2d03c7d4025a385bdb1f1427dacca9951d39a777ef81272a75f7867f774d58d27a2b284b70bd0099176e450ef6c1a73c5cc51710a2b3309cf7f676074724969bab811beb7b04c66f5612cb0bbfb7cada35ec64d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c1f8b76133055eb1aad330bb0a87bfd64d8857bf7730080840710e06b5077cf8e5a5006996fa34635c2e7cb1ab1d4dde078f19ddd97226316f3a2bbfce68fdb48c35b97fb15579736e9b692dd196e9a14569fd5f65f9e71a44312517dfeade9e7610c0d4768538c46876c777aa0b8bcc576e204eff567a9357fe1ad97246a5ea021a348ae91c1cd2373ec150b7cc904f2ca37e6f29f93b386cbeb60eedcfe41cec030510cfeb3803087fe859451c6b68ee66d9dd4df6c9c198e14cc4e40cf4c378c50b126ea934cf801053b70c16dd4e7fae1e030e0584b432df369459fca2b4b87aab26446136753cfb479b73a509a7b11c14bc5c46d4b52b235f3dd8c72d349c3567022c9e678c45d7255d0a1c777f1fc8a47ed8c7fab87c93cef7fd3ed0530e4b489c0306fa6b297de2c4a5f0187ae95b1e7a27d31880f433420946252ba9a5e811ee2302192096dc2c61f5b0f81e0603e286523f87e59e9b9b0627bdf1253d6c06c85d7b031506530e411ea44afa43684342ebcc70a2f5e066dc903736135b6c8c612e037502131dd81b80485c59048a22037d1d9ed35354f8439930a0c33ca86059fe71732037ab7d2218827a10355a75cd3606159660b5e6c67d003e1184f18396c979d343d872d461daaf7938bca00ad85a2db697c2ffbd090eac9eba03fc8b87935b4b2cf0eae9ae9370d7d903df7c7a5a0e1382b49d192b0746bf5bdc3e7741964dfb2d6cc8fe701306c6168b7585e26fe9938e7d8620380cb78ca2f860d2a643a515dc070beb5003951fbfea8c1e457b39ed5f030fac051155b1a793cf2717466741abaf51be2170bf7e8d910f01f7769974cb959d1ebd7fa76a96676482ebb6e643330afed1edf01975912bc9a55800d76eb82f61cccc34aa6b2c39cb418c7137c48d4c9b37e3bb6bc265bb40056c5c5bb681640e0f8d52b6cf9299e819dcbda97d5910829718c8b25695c7f0705a6c8884100f695f0ae2a518c7c054c29171399db9c52f755bd2a46af939f09d4155bf8364e6f3e26e375dd9b33328d09c27e195360fcdb4baa1deb84d5cfe9bb53f4580b518da98824d43da74e5cfd1fa6a6f5e652c41f8d6c190ec4953bc9f30228b988c3aa09acc003b12fd9c240bb0d28e4169d02131132068a91b08b4042f6402be1d4acaa0bdfa113428e986dfc1dfc332a03cc2f3137bd60824e2994c887232e89b6112189022fda0969fbfa6ad19b303e9fa119d846123634249fd42818bd98962ca794faef9fae3a248fe1956a5d7b9e9eae84b853ae041be5bef75df9ac7f5274fa8cca39f3f1eddbe07d798fcbf8d51ac7c31d26e90a0cf8155a3226deb5280ef6de1bb09c2334d94583ca28e3e908b7677b369012521b76765fde828a229a64978b5c3d9e8e7abb8bfafc4439d766c17c2ed44c95eb5c3d03a9af1bc29156c77294ef242f84520b5d8cda089cd69540c9715f4c2d6aaa9084f40c24d3e554e3f0ba087a58be7ec7b82f1c593c11ea4f63f338ff5edd80e40116a4af95a8ad056b95618b49ab039bd64fbdfe376d1239db08dc3d56812db4be255a5fad9567583bf97571a8677e762a31f8060c50a55c04d22791b0605ac48bcfd8afce3e344cb2437bf92589eb07c093d94275a1da1056b90b5af8751f86c23c4c86f7e7d98cb5564b912eeffa45a5211d4d5db29f04073447fa3063603c096675ae48821805f6c4f628342676d9cf790d31e6bb57917a4146c6382801d35c25fb5e1656b3e476068e9f0232ffc6d686a0022a3a6437972e49b31d2be70fe800c408219b17d0ef19c60cf365afd2eb3897f34d218de48943c978bcb972ab69dfc67e2783c636d67f13c48ae8d4fa38143707e8be6136cf14cc9950f712773f833559b53d3722205bfdddc3ef0744cb40bdaa8a85caa3a9cd77f881e3061fcb10711904fc50efb3a6835ccb3bd24148f2039d55cef0687e6a73a2a18bcf232815b6a0bf71deb552273fc28eab3457cfa66ba60c6f106860b9192047dcce68ecfa7a3db2281ab0ee6d3bd899a6dad21b230c6afef051a6b7c0b151bd4f6445b12b43fda38fd3c06378ff28f79ded84bb59e6592c3e5e2930e461b903e8ee099b52c8bbdd05f0a3fb72a36df17946f4093b4f9c283f44111dc319a300c57f3d1a30adbad08937f36b07a860b38be90823ed5c2e07ce609093109f660813cfd6aa04b5f37017d8eb6fc9963b6ce6c5c2cd6101a226dc60da376cf527fb00e456110b2c7cc2723905811165b3a13bfae475c654a35f24a346177c38db83c9dbda1866df5a469dcac7d220000000000000000433104000000000053efed4eefb0a598846e472749f06003636a80ce324de48b839d4373a4f0e255012b69f56a6428659d243ccd4327b87f39abd5f9497aec7a67f84badd808471ecde22bdf02702ea7a7992c123e8992ce9241a1fd3aa5a735231ed9744a1516c800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ddc63bb2ed4b93c602040b4520a85e011ad727c4b6c699843a1bd686c96118dc9bc21cd1efa36ebfea40847c72d7723a0a3167606b3d771d97090030fb6dd029ff2d38e9970c1a4c7632115848d850c42eb5afe18521d484693bb098ff6f3c985481b2bf6223818dc89d9bcead6fb5db7b2161413ebf3c0a7419063ea8ef492d0223f68daab7e2d28e3f02c6090ef9eaf0fc73ec01c81646cf9360d9bd09da1222032520a200f33429a4bfb9f11583998ca3e4dd7f510747875a7eb7f02393d61d7d0b0989ba2eb80f0c8a32aa0c19e990b521c610012f3426bcb93e275ecb323d44c123d31ce1b420b42d148870feafb50b2d6143fd281e50c672653de42a90d3fc30022e8d98f0e0ebd45a3b72f0daf6c145c90950a2415edffa7d3f2346d59001d6850224b5e490eae88b9b413ec045e8023d536260804e467d5d5327c3dd1e4a81df46031b256506106584bd1361d4691f500ec9a866ee9d94e52597f05c1e10578db1ed020bf9e5c4fb1168f85099f0eeddb5dd91a1dd322972a3d3257893095a58236abe021bf55bcc2e87b2228f954af0fc50c3d67be95dcf94e1deb1f9d2c81848c269b92244a966ac21354a07bee36e124582dc413f094fa9c84d635eb20e8e98dcb7345faa736e5303048b1a9f78043f3d64712918bb28d8440c5325dfecc79d85448c5e0b997648c5edb729e1813fe06e0f073b5a5cc0ae800c164856d48fddd2fd34b8e778b116ae9cfa44477935ca411c241595aa9bec686d896d6e7e4b270ec08c236a644193146d2eadb546c856aa9a18b22ed2a421411db92dd67aaac4a762b489f9c3c9e1ac096affb529212dada0112004c6bb2002ea95dd924e3d53043606ad55df63e33f1c1783aa4c526ac133b9534b4507138084d0e729684b3ed361453d690adb7ea1e598297b5326d35a9a18c98897a07db6c17d391727df1d6a74275c909376965fb8400a3d2aa54c7bac82f8c7401c0ccf0dd36c5a5f4ed070e91e0f420717696a65156c834f28fee937ed299df5528645607114cca2338cc2acd6c759cd0b268b982824d59e0620afac162ffa94503891e792ed7a6d8fb4d6a9cd12b5a2dfde80d3af9b818df89ef09a9d75999e6208ad3c688a6bb8c4d23b19412eae58ae7bdfeecf86f026c3e637e30fc88889a253c066b00889f0287ba4e9c7c5929ddb53ac337013a7f82fd888f99c8c5986025aa38370cf23003c9ed05e9a99d0e3dddf4fd6fe99354ce38875751af10f24601f7e6639acb8f8cae41d58c69af922630c38cdc0b17765a8d9060e4d174fa2ef150319167fc243a059c288044e1e9058268578acf98ac95c0e2ad998fd2cd8b9aa45c3ff108bd4ac66ca7f74382be2badd922a2795ff28c3a27554d82a9d9e6e8b43f327760e19c93b0f5c5be4d830ee2dd7727b3f17869006ba8abc98867fdd91c19b1e883b33ccca87d625cbd873ada1ed4adbb61f562d7348c6679aa465b7cabb1ab9e1430f57d4249e24e1843714c118b3fef6609bd05eac0846382669a8d1832fd26a16cb0ff322bea38e42735e98575b7edbea053bd512605baab813e23905e19cdf07037e35c61c5d6fdeb7829630f44e1a0c51cb774bb1a45c349329f04fff348ff7c01ad8b12e6df26848c59f8a8598bce8c3806a486cdcf468291d9abc868a3266a9b0ae35b1d5c13d593fc53dd5e62db07bedc89a55901d599595170126916db2bfa7e9564fc6516c9697862aea18da9b47749f2c965d65f0db65454a7b3bf1a202e56b00c19a40723b5684667997ae403bd99c61a28256b030628104e75ab9bd0aa9a60919127d71a4644da83a1ea15f15334c6a064180da3e73d42321214b95812dfa68453759216bea2cfdb7d58dfd4975f298b5c0a2a50b5ea616f7336cd7c58a28f7e2b1c7c99bd7248c8f42e0cbc439f83fe60721a75882551b1e0539750d3c56df15ee46245b320e10e24277a975e19cf05358c759bcba7c5f3e61efda9492b6ca7c4ac69bdbd5a35e2cb11f860636cb533f34e6af203285a1c1f34f75db8c2670178b1d74383ae3e6ec26c5eedb6565969830b92b5c4bee568b6b49b92ae6b308882e02fa83489351077dee85de54e7e0660a7e58f5e38da6fc40fd973e89b557c9d2fbefd4b65abd5f7c8243e75b6c50b9b16b957866bcedc785209317a297ad3ef08d284db0d4ab8dcbbb6b652e2667fe7fba7884e7ecef3afa8cc4a15830b2f92ea5a63cbe2b090452119ccd5941dff957300973d0c1af6d04e2e39bec34bbfbfff7c08de0d78f6c48977f48110f3b847ed047d137c7a0d2a99a55b4fc7f5cceb58d4fda7ae8ca68b6f2260e811921b88321d960e6a8a1c1f33a9d4db68e0ce13b5cac05b6e526c33e45e2d62d48da80cbf7cd683d4346304f166a8b4560f1cd66bc6990877886fff782759b93f12e69792e08", "5365ac", 2, -525502696, "f3237a1b1312899033d4d41803ea786a781fbee414039f25de6806ba03a529a4"], - ["4bf9daae0312216e594c3771012d1aa3964fafbf02b8e22736ad1fd5cb5d78ca754d25c8fb0100000003ab6a53ffffffff2904229cd87f61b7147c8bff248ff32b7489710a8da50743cf800794ef7b7f8c020000000165fccd0e45e0bb272cb7caceed967f3959511aaa682891385669fc7c0c080df15206b9fe4403000000076a636300ac6552ffffffff017cbef6010000000006ab5263656aac00000000", "6a6a51515163", 1, 1899844270, "b7ee1c0fb02ef3ce7600951fa07c312aae1a2259b9dbd0d89a68a97fefd0e176"], - ["7d14241d0393e10e03c7b894a46efb7a06b4dc8f0226f90ad94c828cc194c351e90bf9bb3b030000000663630065ab00ffffffffbb4e10313914611196153207d6a9156e85472ba44d8cf62fa8a8b6fe6a06c23f01000000036553521796d2402694b6ae0f98791df13d5ba5265a2b0ed1c0a594b8b34080b435aab386f68c4a000000000253ac4b8332240263cd4c040000000005005353ab52e2b98a020000000005ab6a655251dd1826300311e65405000000000000000000000000f15d9fe5ca7fb7042c655088301c70d8cae12550a425b97fb1ccf2c95cdadb30edabd22e45641ce4f6e18c9cff4c02198720c2cb8198e0e0b98063ddfbb1eaa46c66c4835844272141441da20a5e98bf2559878ec1b970f04a5d7b8cabea6364000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000be91e565bd97f409cd9ac49ad7e05c855515faf2dbfb2fb9386729026959f4fbb995d1419a34df6ae0e90105b5e2504e63f0ac0cbb02fd1e4e4c10b5259412264ab8276a3aacee08abe21dd72dfa31f6cfb08a8c03a34485c711ab713f3b2e18a9da96b7b715f1e2a87b9b91e15458200d0c3e60830f4ff3725677de7d3044f0200e5bac08ea07e2ef1890812eaae0fac8235a890d018dc8ab124580b41d57daf020d1599f8539697d9086270ca727a086528a7262801466987b3e6b4cb105b5bcf0a1abcbf05444e99d0d33ed85eb7972d58bace201d327d3378a5213bef38b79bd91ecd11435eb94ae69bd129e064c271f426b44efa8ba7fdc3e99b303d29d2b53803156fb1de28dd2e056b0c1ffa102c1c3ce35763509fa7b8c2255408e131e13fd5032548943df06ff10424f85756f5230960b99d484c3a528ea106a83025e03269d0022527b68c58b89e3b5a84ffee3e926b4d8b39a96a868e1fe451b77ddd8e4b576e02260c9b72548d6fc2bd5e11e142fbd6519ffa9ee21657486ac02199d5df88f57e021244bea69ff539aefcb4df731c5613fdbdef545895ebb05d6c814524baeae52ba35c48995f5fe4a949b2496c7684ea2829e1bd119da71108ccb9478c502ca96315552c991b8f6dd351a27225ea26eec27dc34f7b7dee0b81a7418b5adbd64e8331021c1ede4704f824bf0c775b84ae82c52c04aee792ab9e920180a24e9e2f5b1503384e2d966f191acd045335989bd134e33fc4d11c2764e2acc3999ba5acf1db685ceb1ce526637b7cb99c3fb79580c8c35b646fa3e8b90d37b4622faf95d16514d6315b0b75fcc993224be1571f1d3d91c4086d3654a508804eac38b28797b9740a717e0eaaa3c4ce815bd97817c066f97ccf78a746f85a835b56b2fedb8c59b7d7d6a8158cf8a4986246cfe5777f5049215baab7cb06b4c20140f1f8a8a05977a508ff11f39bc57cf2fd04344c44bec4ce052dcd2ca2f84fc7fc21d11b6be6c139ea6a78f515b719f841b27bea7d875f6e6a97e4a49b9e0e933b1d57263fd9e6905e05b800ebb1e350086113a537fed611bbe0e19bc531f71da7b68e54305c93dcfe5f21658882160a3baac26ce5f1b02d65a063f4fe7e7d99bda297ca32555a0bc3f27e18488a782d5660ddade727219c189d7879817615c1652a51660e7d74a3ba8a38963aa707c47c51e736f561fc7e99a7e2392ec5fd6a311cf25e8b8bd990a71a60bd9ff8d163178f08adcc62ce67be1a5c629f595151511f36fba49e7775007ff714fdb3174e51c86b7480cd226353d00f99ca20930669b1b757a7dbd47b104cb34f24cafe61343b82040618ba6d2860dbd658a8a01ef3d3fffda6628638ddf9b25c8e19f1ddd8becde8b4d8615e2cda0a498e018591f29372b6f2ad7a8cb34cd19564622bb76b4ea65873985654c76570bb97bdb4583f2c934380bf02c33f66c171df240d520f91e6ab40e5fb6f9c4befba6bd5aa5fbd56cf2add90ad1db0ac0da9715a27a0aa97984e4bd449d85566f1237d75a3cf5089a387c37a21342059a608deb33fe1115733f0630a653619836e773175ace293dc59ba6a06072ce8e58c1ba95f7e10c7941e15be27f5d95ff03848315f51dafdf8dbe359f501a1bfc193e1cfb7d635d58ca5503d4a3cc8344486e5ddbf67ffd69b064516ff08ef5cd375c94a6ac7b9e07fbe1ce2d478c8628a2e0718a68be26c1d38fb2ce969fb3e5970b0c1633befa9dd1cb06191b407aca93179e86e31ab70f99590a8cdbf3348bf6f500273a6b8abe17cab543f89ec4bd7ae91262b0abf44f06f2ad374a5dd85cf54d3e086b178565034ef94f767b20af39eef2b223de9a1fe2296689a6d2a35d37ec6a68f964537c351a0a504cbc25ea6c1e576d16c926c100c6593e4980b48457a614d05341433c1a6c7bfb0a89fc9029c9e440f786d152d52094a19fb2d9dfd1060a3df2e340fd3fcac7a07fdbc3f3229ce9dc2e4980d04cb98fc1708bfdcfc42f7962c195248f9f59e0b2bd94a3a9fe4684c3d64bcd65fbca6ee6975a101ea7f093ee42794bbdad8b925d327ebfb8e23a1b9bc0b402b2ce7f58be79af09b73c36abad698c683122a0ac2e899c1b80adfd9c82de62e24de5b7931616c983527a2f586a622fcbcea819afbcd411dbd22f4af86046b04e788a6ba31e192cd79a843301f6a1903f0c6f166ebcdf34fd2e5d2d7f506b27c1bd273f1d83a85ecbb2ee1769fe89641277aaa75967ddfbb339b63a292f63eb5b96cb0d7bbea7978a358333219a0d8b2c3987d6a3953000000000000000000753b010000000000282dff56fa52e3dc4acf1608cce2f5424b01f804b4042ef898e4d253cc9ceab229c97bd6bfdd3a5f7491599c88d66b446540ff991f801f5510e6cf7e4a946c7afd4158cae7161347986a14e58ad5ec193c66d1a2b8f1441fd360e01a5495a07700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f66417c5bf70e7aa6469e79d29c238383b60b2c2856efc6d07d0a44bcd0edfa8c6789df398cb2ced361a97ad86688b204eb2d46fbd2daef39550e24e9091b75d7c60a9ce1678e6ad341275d366b23e3cb31ff876ace4052b509d993ddc552d7e9fecb113f061b5f8c6ce21072acb83de2e60780089b36cb5514833a89d9d1fd202167f22610b5b82a4d872dc89115a9944a052dd7a2147c8406b6385f94618aae30217bc1858e7c72e867e0e4adad14e3c75c0e503184a362090e3d795aa74c4535f0b1c3303ba2335f7c6fb248878384734011d05fe69f96ea75645dfdc5bfb15bdbf2293f4054c817eab8a1c07dae92b3bb78e88f53ab4e9a92728592e20f34fd96d022b272b01adf24f3792d1f7c415bbf429416b001aed4d8b0ca9af2fd6549c3d060210bd60ce0cb903487472cecde9cc29a94427159babbd13a19b4d7361ec111e0b021f101172eb3b6933c2066d55c5e5baa9a38793bd022565657ff8db2abd16d4e402157fcd1d0f68d12b94e09b03dc7ba5498ad9b627ad58ba5306544c1dfaf1ed490220365e3e2ffdfadc6b83fbb4d8dddc4d0bd397197106d3b03336cc7e58239728b9735a711d724ff23381927891b72e871ff3531f1a41c3fe19207fb505432dac4b3f1857f8c6d0225383c400a2fafb1671a830af8b8ebd2aa208f475f3238ff77a1dcbd6a2dd5e02fb92496ca8cb3631b369d866ede077ba8e9d64837f1454a0dfc3784acb2623826efa631195a129cca18c7cbd9ccde12232b67ca99459bca206e5498ed69083f623aeb161ae9b2d80cdf43076ae6498205ab8b5b98f165fd2c5352c6b85f2d466a05b508d267f1eb868ce9e9f6bc9878f589037d3206897b9a17c8248d1870fe20e9f492ccddbb66cb19ba148d565af0df42bb6fc65a84f91f8b34077e91c627753215529eea884030c85c9e95c9ddd3199bdef7c1ae41f9b952d382bc58fb0b01ad3e518ba2389981718d969064c9f7b1a7d6c3d4bfe580d7f74c8cdb5bc75e57361e54696ca9c74a30f1ffb06fdf60a5a440bf160f38c823e7c4959f57d36a6f7b41988ea1da8c7d3689eb8d17a2db7f0499197299596f2716ac4adaed00c11275d6d273c7d145e74dfe7636c08f9b6d492a40f7eb0c26953701534b9ee57a3c3db3ac2e8f3182112c73a9a767d326359113b277f44dbd08833085738af5cf31a1001fe241bd487d9617c369c8b0083672ec593ecc4356186f8c4ee714d8fd00abdceec5ee9fb226ac2119dfcf4061216be27640ff90cbd25be695c6775b50dce13b81f96a704e5bbfbefd8da11f5f7328e7b408ae423b82f731e75643dfa50e1f57cdacb2c1a2a8aa344b9ac7eddca1468fb4296d100fa0837d3b70e29ac67e30bf74435ab868444d5776f671df430189d81f06d5addcae4364706537ed828bf5add2ec14adf0a00ada75e45b7cf041ea3d8fd259ae2141938a3b8e9e8965530bd6cdf36d1efb9172314c9aaa90a5c68b7df2817ba6e2d3b4982e12e8acd68a5ab43cebf4a727fd7d2c5ed736b9aa96d24e18a8f2b0020ef73fe557c0608491186a5e426e06fef0a574bb38c0fb0417c42e4bb5021cd6c01fc2cb4b3fead025d1c345b7f0063715e8551c825022bebf51b8771b2ad74a3e86986d2f456deb9a93ef36751d860ae296cc511e2dfe85c925661073ec6b60fb1bf02ccb6e91a0d31ef87f36bb122e8869b335db38b6c0ca072161c5256c800e9f37f099dc37e6f662eef82e1b41d58a05ac39b4634999243c2774dd0508db85e2b824aac3d851a6785499e7009efe5eba4edf49c7fe04bd1bbc77d4a0d9edaf55c7b7401de21f4a907c582cd4938a3d70d0f9593a71259ae7c3aef4dd43fb4a7e1b5b12641cd931404561b293ef265836bf938317e2ec412cb5311d04e883b36e6852337804f03fce6bdf2b7322438052217a4a89ac4d9e69eaf99c6de22848df19d472a4a559ee34cdd4581a5ae62182e172b2c96dd1776f57d0508e0e4093ba3aff1a71b5c793844a0a8953abbd2f293c373ba5517f2be423c8a7fe406c73a6fb28e588c1ad624724455ef2d181980ab29e7015f630076ef55d68df2ee381923e0bc4e0eccbc52573bc7187ed978a05890c6d94f1affa82314802b45c67ffa952764e4510785ef83ddf79a53e12086ec7a35691aef6112afee30ebde960c8160a1e28d48a21d4f215a886e145d7baf2758f806d207fa7c5be21cf1d446018fe856d88a7da08547ee043f6091282d5968b8a5966ea067ca8c10ff22804cadd56affd1f2d90c0af9ba2eaeeea59af0f4e40000000000000000bf9abe0000000000e4b95bb3b1c757af25af9e544edb503d0f8e4e0cec8c1642bdede78f9f88f61b981ed056608b936dde696ccee684aa862edfa66cead9a80e78c7c9a2cdc8ffba11f8a46c93806b4a58a02b95c5d44bbad5f3f042d94db660fc12d345a3167c2700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b53e3722ffa874e6f321cba2b2128b32a04aa481ca8d7ad18d7d10e5b829bcfd8dd61022f154b0a8ea674914c839129d908aea63a9d474c05a8af09afaee67d3db6c6505abf8a62526baac57d325fe41a6e7680808aa52739e32f8960ddd0f1b913c0b3f010edef9d4f190fd71b4cb765bf639ddf2f3e35bc2b527ba705c07a8031b1168cce6ed4dd268c595c05a7e4a1c827b4d0d86aa2b603ccc20e250427b17022fed553fbb0bfbaf81531653b847678cfb9553b626c2c1ed9c39ceed56c5cfe20b045fef8fd6494ed7a2e86a41e03daba5013af73a7e8a64f4e15f91e14394fbc40f73aa745365eff58ebe82ee0e2331171ac20d434425c67f192641efbb016eeb03105941f0494e7ea4175ed745b3b590034e20ebb7d4b5925e75deb0bb6bf0e3b90217356f6a79694784c4417a0df6694f5258ef615dacca61f8c78453b59d1386eb031742b71f38f010f887824efd7cb2d90ddbe96e1720ce265f3022530aa3f893b002292e224c6117ee77e658d2fe8eef85cab2c5dfde0c78cf8bc643b239d12543f30314cd8e51ed2ac29818ff2c7b91c5fb098734a7ac86b3a1615a7d38d1ec212fe9f46a7a8d4e6319bb3c65ac64dd05258393551a29406c9c15c9ffc19eba16740659cb9c1cedcf034519aefd5a88167d2ec952248220304b30bce62a5c15fd7d22a06b5aa59299fb2425f42a06f7c290ab8a006e37822c05c84b542f0b48055fa8c63261b200bdd412f62ff35189cb58290b5f1d25adafc7935081a45171fa30460ff424687ae8a4db6feab0b776559efb7206d75f293d274258061d7926dc4f6a3ee7087f817d9ac88d616576421d98146cdf296ef00482370435cc19e4dc2870ffa070f02b69e9a5c0a22abee26dd2964c97def45db6a96a68a6426664d6d8a3ff14bdc433741210f7baac64397e07e1b4268c3a65d2629cfc35f1caf8368fcf97006c00274cd677252ff97267758ef9311c3e928dede0295fab8b39e00cc8a66ff9f7f251a25b3b90f77d4c4b3bb1ef0d099f0d77176a4c951897cfdc435ee73f8eda8ec9494e195468f5a29db780c74c42c8ba4bd59d0e31f089805ea6c7d769b84a21801e543106ddb9655e03bc3a18c5368f817b08607125d4b57e3c667239ae42c0c5abf961b3b6ecb126f272777cbaddbe08523b8785fdddc5531ede6254b6d631d958b3851cb5f7c92c6a992a2f841896e36556c346a010f5eb5aa32e6d5f10c5ee2157e0f4d8dd1436dbee3ae6c3a03d4d85bdc900257c490ceeb09cdff7818594f43a230b30d556a1f06c7bdd59e32efed9e3bafb1ee5d67724b05bfe58f0c20b079d15505af55957fa62d7df5351ce51d4a348bc949ca7c597faaf5f9dd21dabeae54c4cbdae380ad2fd24e1f2a9e661be7689ef34d55f561aec43eb1341671480931a8ef90999719f2adcc04dd9530be8c6149b39cd7751cedeb5443282820f8b8b78574e856b932c62f470209725b6fff609eb8ecb671fb1591991398cdd863ef0a7b056abf4ce3c05143715232a7fd1e793efb10d863832e3f2db88a016f4719c15c1d99499ecd51ff6bb1c6e20000e71d2e5a571d133de56a4c9aa86af138ce8828e7ebfa4b6481194a20dca1dd78822c6fa92c0223153d468413c8513b9a31c5c7856c63aaa04e4cb38b7deff043a0b8af91b9885c8d94cf994e62c41b55b0f85606fdc04a5e455e45748230da2c6f728c5213c3909ac37175e1da1b9ac4ddf026ebacc10a0f0990963a2163a99103b180ab401d7f1cc08f45efcd840cfe354080eb1b0d639c6cc94b1ed48c6b0e5d24a4ea3f509c66796c7271728fe3f638d8781306bf0d2a7b8c5dd38408eb3ae003097d0458c9938b52bf0cf6d54c4c0a006beb62a4f6fe2a75103d665c7796fbf2ceed26afc329bf88a697ad7624bc49a5df5d82d909a6afb2a4425e0350acba55027ed4560b813eb2d789992213d144010baef7c7b26e10832120831533613be50d18be6be2ab26fae345570a4524ff48c58c6dd18b0c11197c6a8ee113bf590b6b6b5f9f062d06f26c61827b292a973ab27b6b492f0ef46f4c9fd5805048b7deea6548dea5f82d6d4e9808a117fe524e1245327ea24b5dc487ca3514c1c1781f4b0958f5333a144a3b1fdc9e0bc07e22ae533da9d629114972b96da9341def59261d0312db1778053721698d302b7281af7f23e94f0445f5938968527a4bab524e4b92fadb39224b06cddb96cd489a3791d67c3060f9d75854afe409e0a3e6de2b29280d919d9e6c54c806ea9809796d7b2772dddb925a3a614f568dd8a4facf43733c3be541b5cf0b87aa0bf2967f92b38bd96f0dcae3456189c701260d9640af6e297e8d878d97850479a88e7ad62aecff00e2ad586718276be1d447bdbde06dfb092755ff453657960cda5c6f3318e0761f6b961cabe497705", "6551", 1, 1528788085, "cead04bbe4cfba9da21c4fe6ace13e4748f33eb45e707e5a1e656e44e211d3d5"], - ["b368de2f0283795a2488e6e246ccd210da2535c96bf56ae0c46b348bdeba068d0f408f328803000000046352525314948fa64c56a922ee4842c770d7cb7fe268b283f0b72d63899bfad5124b6f138095125b030000000700ab6a6a63ac51efd22cb3042f2851030000000008510052ab5163ab527e428903000000000151dff7d80100000000015178abd00300000000046a535300b270c4f900", "63acac51ac", 1, -95135240, "751069630daa51b3d37feb5855b631fbd835adbce2b2e301b6b364b7dc553f9c"], - ["0e23c912032ad233bc74749dec650794db5b06113d515fcd8e46fc33628ff3f1d57de939590300000004516a6352119cc15ca366193a4ccc38c2fe72fa8a9ad1ab0cf99814beec5b7aad59a042c63dfa8b0d000000000653ac636a6563a6d2da8781bb04a15d6f00e1cb9ef6fbd22e301f953944dee29b55ec93b3e1c28fc268f5020000000552abac6351a6a6562e037dd10f010000000006acab6aac00ac7b891d0500000000050063ab536586f86d0400000000086aab5253ac63536585a8777a01b03ccd04000000000000000000000000b3526b8f50a3e27f6b27f2ae16fba54196161c51c3d0ce270a782c8cd3dc83ef55379f80432ddd3e5c689e0446f30711038c1d5ba5f74092d37fec7a885b6d7a3f80d2ec66621498f65754bb1b36b2720dc2cfe7a465908da82b215bb5655f25000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc74f98a094fa387a0111f9ddc8b1a9035073de746048059f2883fbdcf3c63d39d890f72db99867852ca3bc61d0a7df60577cc248382f2bf230fb1652a6203007a86a4f18b8891172359e048416b463289abe1469956ed1b2006c5fb40926f5920d3f1c264622c846e097bd089d7a0c013dd2e38fecce153a9abddde08d180f02257c7daeb8ee3253300ed438e6e73c8fe5dc1eb6c9d63f0eb28660a833a5a6c503113457ffc43f900a350ea32aba65ba8aff45c3484f992759866f60608d4d16670b20e2e92ddb7b2262f3e05bd661197ed7d1c9bf62c2d90bafbe7c62f39c3c695229e6f426c5ccf8cef5d29ca8541a827a1a948312bf88bef18757a52fc58a5cb20214f9528cfa9c0c44cbfe7c37da300b4e14ef926e6d80eb8ec413416ad0ad3f15031cca46d91d5d5fca39f5304d234c6fdeda51105833430e1d03bc52b9a7b07518021d89c8e7d4935cbb5d5dba2e956f0698b08b1929b058cf18c10b1091ffe637be02239f179df22c9515e60d5655d56c4b0f0e9c8d2652830afca122594d06324f740306ec787e1c47d94d9186efca90d06e19716e12f03d14d36e91b5793ee8aed30f070ffc8619654e095f90db6243166c521830b52da247b077da0bd3f27d66645e45097cfa506be44bc92c2b769feb68d56bf067e316254be11154e54062132f4ffeebf7a825b3fd98021f1312d80a1cf4598856996e5e1b32fa5cd9c38a28dc3003aee049c368cdce81ebb328bb62c1e99883618591923d8f929b7e8cd0b9c518ae4a1ddd91a11fe798223a5c39c5e500585a2961838cb525b4ad1e33fa74a14c02cd0a1427bbfce79446cea22cf1ccf4709eccf18dce07cde96f88e9141f5cc8d69c088bc641a4add34b1adecc67e2d4fb40a5d17961707b746608a24121470e464bb90a5775b5d15f99bf181e8fbb603645f57f2377ad79791eb67f2250622d4f96c56d45de3cc1e42719c07f81c35bc7d8104701daea32934d0d0d4fe1eace4e90b83268ee2d38c4ed40510ac6d5461a88bc0ed8e9aa5474f7bc29236cc41f4616ab4f586228e0eae89bc2adb0e41b5047379ea157e9a2a90e41a0ffd1e53c70019f4760975aa07d0c661bf092359c38ca9d727e5abd3d196b0ab6e866e1b206240788d0514f25fe375fc4d8a7854ccd674f2350d4d02598c7f2c056d34d921f18e0d38ca906a781771e844dd7dabcb7c5af2167e180c3787f9e13ebf7c5def02f19b23b1c719d7d6d78f35dcb1b160ceaaefdfcda93142b9efaa5cbc082fecdfba000e965e4f240e450c2215d72284b0027abf7a221da9657bba8bfa036d296316103d9842e62b01d6e80181a951b3fff5a9a662e5d1649e518f72216967bf93f74892ca984ba7ddf11c0f6907fbcde4c2e38e8fd2e4571ccd1944606e8941fc918dae9d3c0135b67075d5ac2b56881f94f7bf76d4a6cff738a491df5d6149aebddc85f12bb2b680e3007c6a501fb3f7582ed6389815495dd9255755eda2a3e1bc75bc0ad3de5dfff6744a10fd0c132474eb92dece565baa05849cc10d94e878c69998f1f31d12cd0e02c56168fe4d71d3830dc97e4c1ac6b7c7cc901b5fb091a1d5a0ef5e6e7053894f2c26f5389c3a493761e26ee07ddc7b5fec335aeeda027fa03c15f0817a4b1447744fca62b98f7d3ec9096709a33ff83b002d3ea8c00ced8a58b5db6dfc287d9d71f8dd59f74815cd1c0d60261fd7084d8e8d81acaa266f18774a68d2d01916854190ed20597b5c2364add50b8ec839574c9c6998e8a1cdb26f9bda0577d424d514f05c2a28f33bd2e02929c1ef0b4b88eb8360b45bd3d179b7e9075e6ac5ad4106289dfd0dcde5b1a25e2f50b619fe39079c3e5ca57c665b090d840bf4fcf555c6f9f2f1043ede2184748ce9d05550206185b49536db8c4d4a3be93b03612f0446e0c49385aeb788c26b270d06ab5406d86d2392915f7047677ca5cbbe8272fa00300c966b0f81b7599bf3e94b4f34323e896e86694ac40842e716c14646189c8de8db41830dc3ffffeb387b46791ad7d497f0737e8de90e7e1453158d560a06ca7183112692517464b5747802272c9fd68bfbf7fe92a406270272476fe7c1fb3a7ca57df31b93b4dec46f9cee9251fc3db389d82f94e4f426edcd3555a56078c766a232269d36a381c09658ba4e103bd9d69123a1cfd34f3bdf461479a04c1da070967dd431460e8c319527a121101f563af5da348b23352e2a66d4796d596419c7e4eab203fcbc5b57a387f53f9db792513ac59c3d2bdf70ee69101cf7d20b3890a8a2ef1fe7b627b66f98be23540d8c2352dfca9c01bebcdaedd584d274b63ff24b9d55591e7c439053b198e1c52a828b9758cc64a1fdfe202e646942ad720f6c6371857f60f0527d49e112906a7227de36aa46038920c40de01d24a1c74b16fcaf2d1d30b", "63ac5352", 2, 1302013768, "575c31c8f3e45a2d2921bc8cb11f99e1225751c4d11bb5d0ee532efdb2435cec"], - ["52dd860f049dfea4677980ab584d201164556321b73b8d7718f7b93188ebd9aeb9806719d90100000006abab635351acffffffff42f10c0b0bbe572ff550b6db6691360b5532ba7c469c631b6f4ab904caadd1c0030000000353acacffffffffb29bb232cf7893688a582c636f8df2cdb6abe5ac42ef2bf98a699a0fa029bebb01000000036a5300c07cbb036bbff20ee14ee3e168da3e7bf8bdfa9f912562d507efb0fa9baa7ac7db583c2f02000000095353520052526a6553ffffffff013d2f8300000000000252650000000000", "005163", 1, 1501550457, "2ec2bc433ca1641e1d4825da76739748b1f72ac9fd2601f73382ce641321b62e"], - ["729eb7d50332cda816be68e70d39e33cd1b45e31ab4ddc88c02996156f177afd4344248c81030000000953ac63525163635153f463dee30e55a0e38ba1a2bb4fe98403d4ff08e6cdb64a2f3a7126d4d2d82b087aabf3120100000005006563ac6513557dd287f02f761551581fb7177e72e835dbe3fd751cd7e203d532d3248286036a69270300000006515353530000ffffffff0302f458020000000001538050b103000000000951516a650000ab6a6322d264020000000000ca99fd1c", "006300006363530051", 0, -338681313, "6e51288425b657680ca63ed9823bbf52e1c01d0d7d9ab50409beb0964943cf14"], - ["580c944802cb24e10e70285f891eec869f7c747e452ccf46d85591f2a36d6d1e407af3c5d0030000000152ffffffff3ce3ec30253a9374193db045d32d027e6b389520d4cb7e9d49d53aaabd34425d0100000005ab6300ac51ffffffff026ae8d005000000000963ac636a6a63ab6a007f8e840200000000096353526aacac5200637d28910a00", "636a6a51ac0053", 1, -2052287101, "c145fdc2b12fadadab616b8996e6f7a71f3c70c2d15776df3d09267781592fea"], - ["cb2f69e103ee061481ff3b1826757f1777b3a0ec54f0f1aa5981973239ec89a45c3ed457a003000000036a0063ffffffffd8b9f9d9ac2b4334ccf44ea4e14d3c218a4d66000b7a98141a666759151b91d1000000000765ab65ab6a5353a97e95a49f81b533b7a75d3ae4518080bd332c42259020d42624a74dca6888c8d7d9335b03000000060063635253638605a6b9036a00db01000000000363536a33e8a60100000000066aab636552abb615660300000000066aabac00655300000000", "5353536565", 1, 853468471, "2100f4d6c0750a42e448db061e6c7f664bfcdca7a0d25026d642d423cb5e0581"], - ["dbbe5c97047ca2f3baa9096dd3e4f085004fb0c336041dc070e49ad9ca1a5b5911c5b721f002000000025265ffffffff8de1e2d398e875bbcda1c64fa7b2852dfbebee0925186650b8fbf23ba1efc69201000000066351ab535153ef59f8c074a066bc8d17c19ccdbcce0e9f866c938e6544175831c3ec4df67216e1e35b400000000003ac63632928f1b721a3e1ba8fbc61ec0be74a282927404fc52f89e6e09bbc0fa38a50e42f50499501000000025265bde14d75035ff2b704000000000663636563515384e37800000000000665acab6a63ab0639a9020000000002ac0000000000", "6a6a", 1, 1016383838, "7f9f3a137eadae7ca4ca352aee5c027c2260652b1e9c1c55b33acb67904b157c"], - ["c3fa3c76043928d19402b8b8599ae4b2951ce33fd09a016af373796a44baf355e1f584807d0000000005636a006a65b6138f62be0624d0358928c2e6267ab7f9ab29fdd8de810339b2aa8a1a5d6a72feb7284e0300000009ab65ac51ab6a536351ffffffff53e1520ea981923cf7b4b65a6e999b4f628c218a4534ed6110e22e3eb61270b701000000055163535300ffffffff499814d66599d08c0f7e634f590956b30fd12074941e10d34c2531a335084468010000000152ffffffff0431288a050000000002515347bbb3010000000002006a9a33e9030000000004526a63aba3fe2c0300000000002b62876002c80aa9050000000000000000000000006ba828fc06665ab5bd6b077ba2cd1256bbcdb9a3b87a2f7d7a3d9d7ae2063f68a2b8fc82041649a086edb0d3b327111790682b94d4c3fac9810911a37254f11352ecd933933516cbb67f4d55dd41d279f0cf4260ec461b9eb1b1d91f6e7286b4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001500b5c8651e6a30ea18467fb8f066d4845db6d8c425ec706e1443b003fd19388f41167999d02ec6749f02ffe5d6f2bbd2b3afb3ae39b0753955fba5d9a367e72080e0f9b8ddcb38504e39fcdcd9f3182093e527444ef88683f57dcbf5d7e0ea6392460d5ee938fc74790c8a1775a2e50a11afb5af0e54ff0abdb010a6bf3c1b02021a7cd079c38d511c13427a03c36eca7cfbd55faa65ed4d0396b6f0212ceab8022d1cf0b865a42d567fa83ce060e20d9f6ccb582bc47cf068814af9de241720120b0ebe4b0add4e9608981ab0f085f846dbc88a65745ab0cfe6dceb8210e7d3cea817688ec3cc84d310becfd7be1414cdcea01e4d9089f4439153ab4e2400287dee0224685d25ec97c360c1d4242e41965f7a253a580a15a976d9bbb81d0ea5294846032452d2e81a711d1110610cb117b9ee1bda425bc240f885bc320f7ce6080481310302c6c63262cf77430e4ae2dcb12ff6c05c882a0929c90cefc50f5969a35cc5040308ab1a0f3ff23b12f392ed5941d70c2662e32f7a5bd3cab5c0e2705bf8b107490326df7d3cf4b628a733258652182be74e1544fc9d99778f7b642b839daf94d212fa4b4c96ce47e76c135f650d1c5ec808fa1f1a455159115b92d4c011108641859ee3536532cd1b7bba67e9b7b0fb8fd5f0cd6f878031fbbf4e5721d09d819068b74c9cba91f59d24bc52eb42c35db85c9764b160ff193d8ce67789c4e3247626a523a4c2df4b846fda60d32269b5f68b6b1af779f05fcbbc5e89e2eb88907ba56532ba49e2843362b951abd7ceac11a0484c07f5c33dac23df65d8aee694e7d7de4e906ed9595924949fc49817ae85a3cc2d4ce56636582baceb3e9b2d32a9ec435295b2fb62068082740768c62e9d82429cdbfc599415b3cc515d61786f02aa5577a57a843475af3ee921a9a5b3eaa10dcbc8e65fd19bb4218392f962c41f745edfbedda425e47e49fe69e45568804001bf1ab041a2328a2c33a2f5cba2d87c9e0e9ed1d4a01e5eb585cd4112aad2f902aab86226f7ddf5ee9b5a442eb3c14806ae328e92f3fe8d111cd3f8d428a4fec6072add7ebf7e29a32eab0d51799683cf7fde102eb6d55cf52e923d1bae5d8c07c21c3098926a6daf6daf3e2d1a53ae61d3c45c0a5a837d7c4d6be64256bc9e0568adfe545e28b1588d149382f384b56e5871a490898456cc43de49677089453c65d929f2da7622d1db394e4ac08577e27106875ffbb2b40dafaaf8eb7df13c62aabd8fa57b55bf33ba84b5a19a2e086e609f590c08236e681eac98f1f43add0caad986315a47d774d7930c3feca199bfcbf74454e85a03be697b4217c02dded3504c170dc4f568795a46583d774aa6727fc6eca91b0ef4b6191ebac415f0f159c1879f9860847c3c17c2d40ea1009b5baf9276f1b5c2c01d9fc4039ba425ce2a61ba02933930859a29888e1071eee1c3029b53b512daac7185844b5122ab88577bee750af5791708c68a38375359cf5b2617c5872fb0191ada62159635bee4a990e3a8112ca12d94f390b91f87e54da1ae45e1fea954808b41d22c1677b8b3b0e99b376dbfbb882cb39967a4be3c13c8b51ca027e390c1be51cb3c476bcb907041d7c5682cdccb3fc937400470c4969446890e9ce0cd432d89e45967eec36431d5c3d6ed6604f946f80ce704136913e6ccfc104d01a21ffbddfb16913695e8ef32856a9a73aa7d4a08ba917919d861a3eadd08702d21f8f18dfdc0879ed6adeb47e26140ac7ece55862d73d3c998642ea7db77f2f05bc72d00510a11d5e4c3cc7da5c4006aa0a0af3fde1bd731c6f445e3587d6f69f205d67f1895435573b4c7ccc7e2e079c6f0f04ad6ea7ece399911f833894e5a1769c781261966cd3bd6f41d822b6de3490171bb4809c663959a053df4d50da1f5476be79cb7c11af63db5db6bae5c1a41fa912479bf8099c822dd72625a81c7c3f1f3be0beba8c0730ca441e3abafe55de36d80b31e3c23e5f9b728073286fcd0c4baa1f605cf922e5371bf504cf888986680abb8566eaf3b987206e46ff2d8aa5ed775d4756e16a95a3efedb963f3422150626fa29885556d37aed5be80336adb749fa8bae657d40e5dfdd932074d548979a8f61911b4a1019146ce953b38fd64635e3f76366937750fbaebea104303ef9c5c524d46b6af56d20434ef18ceb441aa89257aeb91efbbb7717a3d53bfa925746decec5a6aa016ded5a90e1aa1b544c735cf6ac58d4f996bad5e10e3ac1d6acd886ec249ce4ff2c304c0cee61590499ddfc2473d97ae698d2665f0cbf050000000000000000000000006ee1c940030a7aa69ea3c7e8a3bab44aebcc1652688847f47b196d6bcdbdca30b5effcc019cddc822c0cd3b1510e84ddeb86822f30040356f85be316125eb8f98518ced77e415da904b416e6021ccbe431c30cb0bd47d6a218257be227662a68000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e2bf22ac619cfafa590d2cbaaebdef4cc2be878d5e7d4e4b48b392ed9a26637501ecd10771c96765899883820950dcf0dc8087507cf6950c0183c118b4a5c77f2bb27099bddaea39a51d73662468c33c2265acbe680a946f3bc6bb7d0d8a969c9922df957820c3d8e0a8004ff3a5e7e4a2733f1ad61ffe307fa51d5b602354b020d9b17385719631b52a819115e076b8a3f6e9356172e39659b9aed70c6b35e42022b5de6e90ee3cf301d42e8885ff479c8a563eb03982139e78747c5cacb5cc61d0a09a284595c7e7489752927445519981c374a811245471a508b2d41f33e804461046f7d5287b88139d2be9dac2fa8e7f563d00201d7e967accdb557a14d0be6cc032e2eb5b132f72308ee66a929423247f0f8cf6fdaff314a4f424c2d05e9c72deb032a11ff9731461ba0fe3faf06586cf48b3f91b89160682bc5d8dec3c746a5cbcf0311f9c7a98bceb117785fa7d31601c5ef4d5b41107e4551dfd9d97293beefb2320321dd63fdd6bc970032bc5232e5d04f626ad6e7b9af237b8c35bc668366f4efe40204cc1d37d437b240e5f329d69ae6719e8bcc95e6c9b7c88ae00115a8d70d37663de5237f2e1d05911062d66e0fd88b84ae4f8d16b9474d0db3c86b2981f21d7772a3d649b3986b86967abf799ff835f1197c497c60c0f2e9e0dbf1f39f89679986599c4c0296b32822d487762dab7a4ae3fb37a2f0bff369ee27a01fb4b6f0d9c0c6d0a2f320a9d2f61a196eb701c9831432f671cd7a1d5fde5e85c007d1e0a8f7d8fce0f2f058c516c5e6bd77f0c2dea49de02239867070a544fa7aadb4ba242753776f00a6d8f29a335766d76c7ee80e9d220d9e7656bdc801abc3c7061aab432446b90b846a76051f1c870c8188b92840dcb182c191c004d6f03344ffdcf9a9a97df0891f12113f4ee2b37ed2642fd2915c7f859a713145b5e5d0508c63a88162300b0fa285b0fd52d98ce8c62edb528bf41402cb89e5a0802fd5af4a9a77419182d5a529895a7f5a3d2772e68f032ac0a6f5506fd256ac94056f2f75fd7b8e70628473eae7c3880c207e6747ba574fa7b73c8b779b216c2e7d1c8d67708c91ef50201a06bf5765f386ce30b2cb849eed20f1e8e6375230c899db3f23bfa8d1f3d4ad7ba7d085605328d2e54e249e0acb05ce92aff474ca5e4bd9c3ad42fbf56037346dd3125dab55fadaeb5918f4c83e51e1c67b58f104830f375443d40080bed7b2ab9cc106f066ee054ffcaff74d01252d3071d316ce6477aa1e3454bfcde7598fd551ce7c89744815716bcea8e59eb34cef529d936fe1edb13122bf2d2928115b1bbef5bf2705ea4fe0c1caf9feb3c7a0a9aa9671753ec8be91d74d625a15f317dfdb09fac2ba7bd76b95220ab1c8e611664060d49f1ef7e6e782a53bc2721df0799061b069b50c2d153cd7cf5f4b0150018ddbef10c77d16d3a4555d6523eda2244e2805bf7608b2328b60eec3fe61acecbf63499f93d278944591b7185e04164f3730cd76b263d035060a967d5e52576b95e6d9c1503106439a52e4404511715a0c54ddfdcd172c4c330020ea678a50d4ef5f4ef02a513347555a4ddab3c83b0c403ce03434aaf0ddf1f28d3d1e9182142e8e7de69e41f4076802e7846677f23f11247f6a5eeb7f483f36a6206a7d8d8fd65e0d1ddcb433ec0dc61d950a3594c8b365a65091111678c9827b4d1c954282bc7eaac9f861a4b69b0d8097d48fa47e4b76cdf7474203a13d155f3df50d1ff54e86b6bc6a230e602a5eb2565304bdbf30c36e9b917c32c4a0a8905ab5f691398dee0f509f39b25abf3c823cfd7443ca11cc67f07c1dffab9b71b1136944a5dd17f0448e9076f081fdee4c6194f7faa4b323cb306ca59c0718556f14563dcaf0720a777f6349f735eaada87e11788ac7eb97cfbbba744ea9d3c4a916e9d2c14a8c4b94a8657b5d96496498e75f41ef430a44e5cfb3c34a1aa52ac006b396d058b7db16b1af0f742822e7c654b7ac84cad4892719c09dc4a271c72202f8266d6795397c9013bfb9f43d44b40a4ea814b6b8fdfd23fe4b574bd6f8a53aa9f1b1668fe3e5a24a2c40168ea7b57a78f590d1d1879509b3d8cc182e83b5b9fc6307ee7b31e018d015b50214a34a37260c26bcc8d9cc4d9d5e8e42e55bd6914a7672a44ee90842932b5a97c1346499afc4003aa355bfddf713d5acfb01354eb3dd17fd5745294f65f525c846eff1bc241db78f44ffa2f3a71a68d976903bd389a52f94a8433fc0db1e3e00e926b69ed03d468888cd016ee6815601efb23cca221a745f1bc8e756c4941cec55fdfd6f4c8bae0b28b54a1f50793bd1b0d9b9a03a1e55ca4da2b5b57152954fdf50d24b7e5b326b742591085a394709f39f8b7fdc0d79b02330a21a2b8a63023aee4e8e810b007890e87852660c5eed16790f2805", "51ac005200ac65", 2, -796780269, "e5b7a021c9598970a47c9d942a5ce92a49863369c76f9c386ee16e621c1767c9"], - ["f216e61704d65e467307e8476f294cd674cf219459323c29cc9b46fa4aa062d72d5bf60793030000000200533b7846a58d38a726d44f2c358dc5e0bcfe9c3597dd26b5668192142e9bc223c240dae3cb0100000001ab25a927140b2154a15bbdf25db42d787786b20634740ace9dc34d9b8dc046dbdebe78f126000000000865ac65516353526361b71e5dfb0c20811070a8b9cbb33b1dafd94d53dabbad638937b631781676137148195f0200000005635300655348fa2ab903f5a803030000000008acab6553516300ab1a70e10200000000076a5165006a6352a853c9030000000002006328c54ed50293a53f01000000000000000000000000aed6bf4cc1bea27da7b2caff87d3d9d6f8677c778276fde626cdd38468fadaba8b262c6170161ae0b572426fbe76d2c233860a526ef589d02865940bd80fdfcbf26b3480e8c255a351457bbcf1a39bf3d706c2e563f80ecdd26c4af6da3e2d50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e42b577596a32e07ac293a0c4de4c32c4c61fdd393837deba8cd65a55a968c03724e4af3cf4151c71ca8879d483eec2b076ca0b7c2e3d266ff80e2b228c1f58b46ce87bfbcad64c7b05d367b52214508b252bed2a54eb4c68d4ac74b7087803420d4d1b958932f420b238b312696b75453fffcc45301b2fa3e55750babfed13021cab5e34459df3a78ef1d45e52569a51541a136a09cd70c144864e5698354cf80303c79f1be5609118c2c7690243df5fe381433724e532d93778bc261c4ca347280a237e8977614c955099d0eb54b91bb8d1aaa8f91f7a3ec2ecc2f1b640b913ea26170e2dc90e34facdfd62e50451231cddf72832f5cfc0333f6f7d38853be6795c0313cf638c3e884d78765713c4623735f3de32bfabb75e7e77197f43d554f3dd1c0217de7bf41d2946f384c4ee6ee1ae16ef435abaf59822f3a472d5caa96d4ae905022430f80b9c121bf53ed337bd3c4e539e165abfdb6edac06dc52555cb09fafcaa030e317f16fbd06f48be127700e46c47d6a327c3d9ec5ad616dad5da55db1486e3022b38ef64f23b4af60da245ef90bdda41447032a56dcc71479994e23d35436d29938c120f2b649cd4bce186abb690b96e3e02c711e88b6e6fca80325513a025d745da15693cb531e9ae28d4071f3f455e1a7527cd11695b30e43420c5ef2e362431b28463138ac17d910f6df493087e661216b5fd514caae249d779fccfa2ab8c5e9827dce325a1d90167fd5f119f92a5c939fcfbc43d8718e5426eb033cf5f4a1d4d8c166ede5f5db4edd625aef60f4e3c37374485d195d43ae0b909966b2c7bb7fcf88796a8f8fe8da06afb1b6b2c956eeb25b7ca6c6a4a609d15d4b9a2350b6e31ae861ba5ab3e41535258ff0401726f9a522a2afe05b636099a312a32fd997cde02f0f92ae6c1bada211d5d9f4fe90e6f07d3e15213c0bb75fb2e1aadecb84331b24680fb7f0c05a310392a947eb1c11091572f35e76edfabc491d978e3c34c3ffdcf8c1633dd7f75324bceaffc5971a9e27076a755217092c7358adb662d65624b1be99d0fad11281ffd35ac3db2539e4bbcb0a06565e80cf0da5c47ca60b8b07fdf696317555df555e0eedd88c86ec1aee8d13d2a97a679edd2dd06dba4d6170aa6f8e18564a146bd18a16a0f23a2dd69dbeb624de55af7c56565c87b07269f4c2842615a63240c20263c9a17c0d15df515fdaa7fe2e13d0329d78e75bd3db5750186445d6ca91e6703f2b190eaf7c3676973a062840fc69bf05deaef025c885398040b6781492957349bbe911729c3d371c334929aac564867e208e7690f433bb0078d85b4d37726bb2309499bab7c723db7dbd20ab491180b57c73475ebed106b5ee6cb50b17f3280b6253d09b467ff66a4a27f5d28e8a115fc42c3ceeac3d1c64ea469072f2e8c749cde284f56d7c3ef682fb2cfe574bc197366072fdf3598a41022c9772a7bf3fcdae65027738bb223eacc222976e4a96ac9184310e715aa0665860a3f97bf89b0d5473b1fececa8ce580863edcd08e1f423bcf832ab1ed1268ed005fe26e6c796db4dce9b226fe8d4adb903a7f919986946520673a896fc490a42d5a7e8c39089189f022ed49bedbc11eb33b800daeaa07a9347e013cacccdea50083cbb3d17f69d8ca9089fb5db92b53025735a4705ea1db9ea82eb1e153f8c6b85c8329d8f3caa588999913de11951c71d26249f48074cc206779883451d052f6d7bc4ed3b80cfa49d692242043e8969e576f6b438f4668a1c28cc56aacba3cdb6d48e8bf73a29817efe3f38216c0d6031d5bb4d278869cc1903d399358b4662b9c3fc9dbbfb2d8a554a4b675219ff015dac30a2e3af2d3adee91a980b37cb661a55a99a58feefb768f28fc2fa554f7a3ad1d5c0b0b76598fc4b8c4d667016b3b98ce7aee2ae2481f60a9a1deb207d922f5fa0fb2a55960becc26056fd7fc256e023c35dc7aaff9c762d49bd81d3228cd7be18329b026d019ff8d2b5340f42518b6fa6521a3d6f5b525a459fbe7c1f9c51bbc9b866126fb84eb84b88221434f29a856075072a73de27e13b29833d78b62ba58cf972cb79c82f5b2601b2c6a674d634b8e56d4309fe3a6b9dd9abe872fa53dd1592238031006046e3501a9f19775ecb3ef826acfa3d77c0249c3c78dd7f0e5b7055268b71f75b802a2b97529322d3e41e31dabfcbe84212923f6e45f6eb46e58d168f8fad893be2b975bf88afc0de01c0b70b520546363df9564abf9655b626eb8e3b68561cf76804d8a82684cf7336c3aa6345ec83a64eddf20000000000000000b8410d01000000001701cfa32c9c237c32a5f9933fa6126a59fefdef036abbbf1b85d4329be1b0edeb79dc15eb5ff70a5a7f365ed25e724cd2c20495fd01574928df651fbb00031d829e88aba4d5d076860963a39b761d0bb1a4e9342f0001131906c8fd4a52a0ab00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc291a9603a767ed94db1eea13a0ccd816218fcf3552f7085c1df87ca8f0e3cdbab33f9ada42c74abe93aba7362c10b96444a6a95d4aa6a3a53469500275a6862d8ae4b93a79dbb1acdd589c0ac57ada45742c118da952c07ac46ee63bea1720433920cc5a9305c73b286d1d8ece0204c59f1da92b8f557db3ad37d94134d986030f99afac4ba99e123c219e4261e566b26f56af8b2156ef115f4721c8bc976b60032659fd0c280f155cd630cbc8b078d27642f791895db79145c7d8db747ff9d6730a04ae2ce8a93e1a04c6aa8bbb37afa8ab0f5adbb157932da28c3e2aae463a245829b81308d12feba1617739dccc5d4e9a2d336d3e4afcc4740a38caf2ff3a2a62031460fae20424384fc9750d716f8897a5a948e844356bd3c14743c597ed3388de0229df1a16e7d50a127573d849db033c76d356dc6ed3a35dfdda50b8b066b7ccdf032272a3c0fbe6c89f67c3c363f0a7928803307f21c8f888f0cbe2165451575dd802200862354450c5db01e60634c8781dff3692cd650142425184c3136e3656f511031f453e133334b80d68e117e7c6d3f48468e756e0973b63d3435c79028aa1457212affba6b99873b0ef6f56d7b9991397107c6e6362019ddd99a30152719073a262c663e0b5087fd4f6772d1b0329222b006b2ae34187d96522c03646f12c04f6c912372497633ee93777730405caf13d172e5216d513aef52b0551d16fdc14a4e708ef9260bc0aed26a3f30f3d46f6cc224ad20de20bbbff5a6f294c6a694ae17d72b0b0565ee4b14d4c6721a720741d1b1faaecfe58072f74a4c3bc7e2d18f7453126e973bb7eff63a6d7c85559f0d6a1ccaa4e2b29c3cf0c126da466bc48a705290fa00b56ca96845a56961a124970db49c2b7fd78df11446558fa6952c8c9e0327b4ca99f59fb47a094550700dd76b992b819ca292a6e42afecb34d239f0b83ae10f85f20adf09ca74cc8b5d37c930c32be797849c604f56f9ab308f81f001f8e7a470f643a44f2f30a350f236db528781eea72189e9eac4e68465a40e77afd2050a0be8d45c5df477398a8c3cf4e53ae046b6d37abf6cca379d64245f6c56de308d461e0b6725148c4a19fb03e3276f98b789f78e0f858c1530b8853df87a2e7df29bc14d4ddf5386a7a521284113170343ba51c74a0a8fa4525b5358a9ad15d3e9d82608d80d422a25ea9ac2af85185558054fd643404d8829b2a7bb6a46651b448da4ee0534c6b2cf8c99e475cee0c4d4e00714f94a9425b9ed2a4d70376159ad0cdaf9472f881f801b65031c448d54d1fbadb04a71471db9021898eb3ff2a01479808902b7c1330e19bfc5f07e9f2c8612470742de7657d73d0c9ff911459369db420a2b127eb1791a63045870f2c24c80401572215ec1099711405c49efa0844df2ca4125c11e1b2c55fb608c9df413fd20bfb75ec66893f71935794d6b3c16764ef7d4f5539214a8037cc4ae13e099fe85a7b6dfcda53aeda990f9a8f318fba1cb60232ade9696bade933c1bc6067c2b926b2b0602d866ff90a7a71aa22dde3e06db66935e87c7653b2a49e62f65c7199e584ee43849ef127ce8a17bce81cc294e9d6310c7e8686463e42d03489e62818eb4a93bfba71f6f1e2dd72249a6c2caafe7ebd543dedd34875ac11066c2ed40b96834c843255daf8a502536b29e6548316f5160df65c3c7df77a9bf8939f3baa111c6aef3186c1231d2f0d320766ded3f688ad26b7397270390544cb8d65c8b712f558fe74ad681e7020a37bf85486fe91a2f996f5fb1811aaefed655f1f61af569df701652756fbd25397c7e86aa4f1476181b341834712328ee7a493999ae10a4a73029b109ea42a38527511195b9c5b9ee22f725cd06cebafad81a5cb1ba3f275165b32a21192dd331684ddd87b35c5985f7c42f82123dd2b1a3ef773738817468a268ddcedfe09dc25e89ded6195ba7034512c6b432a816901de65ee40492d51aa6503788e897269179e0b661543695cea35fe814e2480abe7395161bad78190e09438fe5785476a0959b292cdb02475831edaa8da10a49aa79129b4f13e77746e9e28ead440262dadfeaf98724f02f7e0e5f16d7ec51c992678b20d8b4930564d06fb897cb2295bc8ec53926fe51032d193f6a1d0e9fb499b7fb9855cab6195dc1e570df56b6ae9ded852a563377326c8596f306c444a4c09f4eeea3d97a0fb1ff68b7ed779973b94194e0f621a5b817337fa6e297f92405c95c6bc1dc0169f98e28cc717764a9371348b34d215a455735b54972bdebdec74bc75063ad2f02e549c53daefc6c7b9c9aaf8811e4fa12ea6cc05176260807f3ed4f45c3d7ad3fcd1877f55b2971a7d460125850abcc4d2ed6bdf9e2ce6e15c10be47f9376116e8d668b6774bcb30358b0d49af968d7d0c725ea4c73ad1b1b8210c02", "65536a5153006a52", 0, 1172551030, "74eba569d5cd0a1608f8169b4a29d0a8da39341b1f0f64a2c496dfbee6300e57"], - ["931d5b050408de43cba439c12faa3ced38a819c29164087ce6822f415a15410d1721e9f3700200000007ab510065acac6533acfbcb4c6ce4ab222af7e2af45a7dca1eafe3daa9fce19fddc02dc1003232f91004ac9000000000552635200acffffffff0e5ba896a60f23586bd56cf8dfd8900af1914d74cc362824c230a38d6a9ca679020000000165b29e9a19a4c3ecce5667f53ad2841162bcf032843bc72da9dd203dd275bdbcc0950a68740300000002ab6affffffff042021e80100000000007b3b6f0500000000026aab84c90b0400000000040000acabb3ebaa010000000009636aacac6a005100530000000003af57ce03000000000000000000000000ae0a73100a2b25147eefd536f40ba0df8f84f13005308d04bed1855d81f6f1209e358590b84121019a1fb68a8664b0d45ac960be200ab2357fd2313e6208ff5d3858e454d5bede4389ed5e829f824a3920c7497868e2c46e1d592af1fc78fa4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d62f6e9fd2a447bce8653e7c4bc361d7ea82ae5f335c154a55c01ed1e4c00dfea83c23ecb3992232575eff5c89fefc0947d7d2dcdd9c0f5b548881bb8cd318ff9ec59da0128d6d855353d6133bcdf2f594789acf172e234d4487938658196d7032c032c8f6fe9b9e42bb3c24a1cefe5c34f672410537b468fe20120f81fec6e40209811dd4a9b19f73e2ed5a78d3fa8b5dde834b36774ed80fa7e100b96c268c7003161ca16f7c0a512dd04113e5677d5f40ef568d9dc21123b01010724113e261190b2f777b5c1d8d806af8ed67761fb50be7d0e81d73a8ec395bfbba2ba269a3ed8c11f69d1f8b93e761fe8b30f4e88a84f8a5d1ab39d31f02349ef68906e4d0538903016a7d3d38fe7bb70c69a922bfa329689142552f162eab4847c24dce60d28bdd022f84006cf5053fbf6968972a6bb52d6f26c04ceeac1fee7caae28b4d9e9a5a4c02053ac080cc2cfe73d143da493da2bccf2a235cb4fbee1a53acca722aef5721ec032518b6ac7bb653a95b2709d73169a783bb49493b7ccb5273c1ebaafbbf354ca7030523c301a4bd7e2bbb453605a44b18e334b8e3d16752824c882835715633b2babea3126db377418c39d6e97a79eb3a951321c02d101564d090036b3e7cad606bbbf0d20e47a8141d6e7f8e3325266d83e6d3e920babf35913bb29622ff4fd134e3d467e2d72ce62c39781a4758094ff35c1290eb35ea469baff4782cff32fa5559f9215ae6033812f3035883213f92ff0893773e1b0614e70645ff43948e542e0baca5bad99bf8cdf17f11c4073d9f4aaa40ca71e3f44929e56e5a4a4e64c070e34766039fe43305dbf30e46b7cc39c0933b28f99e68464475af9ffd37962e5c134300b8d70b0aad68b854360493490fea41866b38489764422d2d36ef5e6677bb88aba572638439fbd816a6cda1a746c5117506d821cd70f69aa84bfccb0c94e6c5a0d880dae1933580d806a9af3d1db779550a7c0345480e361d0fc5d491999924707938009d3055faafcd8b1d9d7e92befb4371c4837b87307493ad96b87ff67467297e71bbd950ba8dfcd5d35c967189079bf59438b77b8a0660b5b6675fe1154341d594a71a2fd5a9a0fef37199c457437db4ea3c39e471df0e888d42af61fd6c8109738525521ed62162d9679682c0a1ee96d8e0116e9961ae9f6645366a8c9877a03237f0711e5de6abfd0fe614d27fd0b89866d305c2847af6418937d45e30f21533a92598f94c8706b608489c2d8c35ce591030aab55e963ba770b2edea0ce68124be57119c2330eefae4f34599e99670a5db5002d7589cde10b85a61190a554fb5ac1facf6673ae35fd671dd57371a7f6ac412d79ad1fd994b339070e87ed5a53c40d25392a351525cdf4de5f95bb571eda0985743c63bf7d5471e1b506408afc925bbe28c4c183d4e5e19e677bd72c9f677f2f21de3c7c56270e92f20b8e7c95e536aa7a4b11298ffb7db290b6dfe72e9d84ddcc7a1e329eda112d4c4d691c699d373dec7dad7b872cc502a5ae24e6f27ce32c87b7491a0dea89e21ef34753006e389907dbe32183c3ae5e9c62ae954163e7513e18198fe51f4862b92b8c3950515a088318815cf68d0f0e7fd38ba15bf4e07e89c4c8c7c344844bb47d759498ed6333c084e2b523a305a4a9e60f52e4a29b497822e43ca38bef1718a482c04a2b62e76268556cd342dcb5076d92c7b5c7199bc689461f351f18518e0b614d624c3672f8fe5a7bf3bef829c833fb78fb0a7698b42f9f66d6f89a09693c1c1e12214790f2e8ed667520d7b536a670bc5ca365db06404bde3c20724a322d48856465143351f5a58ccad6807d129ff531877d0bae09153c852b9ef6094de0eb7ce4c40c46b77caef173af9aa1a49966b4fb0b4a153cc7db2cef4929b7b241a55b609f912facc9296adea2ca20c797010722dd1b7b0e55aba7b4686804433ff86f70be599b025b34259187826e965d08b14a6327a9899900e8d1ee6e4d0d9c3701ed72048dfc94495e9edfb581e172d2648450f3ca76bb2e135976d19b4ed0763c61896069e4e9c58d6530f4d729656ec1e701eb4c90936c498209b44934b02f20e41aeeb521ed8053aa5c601037172b357dce2a54b84eee334253cc0bf170dd8964065160c54ec3a51f8180dbe9f6f1b07f1cfc8a97359ee6dd305199de387c10029d4110a583afc76bc4e3ea491c4795255b2c3bbaab1f4ad20bb27571f31fed97d0655261257013c71634b7e02dd08c5cd1f4f37a3e38ed876950fbe4e31f2d2a9dbe6c742f3b8efd90a679b25c912c405000000000000000000000000193bc408e118712b2eed44a69f5a5dc531c8c764edfb344314f4c0e213965695b95da5255e7f9bfc0701decef5765e32f334b7897ee45f9385b3422ed83447d4bc841eaaae792375eb45c84d6d8cbfa6aea4ba6cd27e65fc486af56fa27da9c800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000724246e81d38abe3e042f5ecf000807aa2a671aa04a2fab468938ff1e782b14712eed261a89a9b2bd3f1afa26f8983ee42ef3e623c261f591040752682b4d1934f21f12cc3e6647f4af571cd27532ca33c677951ec0843993df002600d1c83366d53a3826c7e7fd33c465e3a8adccae90d16778e93c0ecf1f175cbcf7b966a9c030ee6c048a29e8307fdffc6db09830478a408002ea1fc95d4a66d5120fa1d0278032c77ec6a4f9ee0c4255a55824395631993c8bd871bfa899bacc347e9fdae1e9c0b058f8fa34dc1f0a160310120ee0ee7d71874b3019ad4e706cfd8e4858efb1cf6121ef4c8bdb82927f688fbd673780de0a8b1db2cc25de59f9a8c05c42a9da7ae0308bce83dd4e9cd08cf0820aa9b2f3f0bf03115efa8aa4816f19f8262229cb7540225a3dbde7e8030b607f64b879e9162c6368e2477c8bd9a36583c9d1f8f2a96a203213148e78d9ee9eca7b831438236e2697ba6cbae0228b391aa9bdecf8d764a9e020c4d19b1fde72ccb102478c7496b71e03daf52df30dceeef245972955c35e9d9020c66f9f93dc00b934bfb10c28badd77f1ced57608c403069b703d2db7d7c3f3ae5d7dbbba392acb49432feffbe2264f009c018ee5474da2d9a566b98df2c5abfe22c108b5b4ba4b6ad876b3d98b2bab6445fb5b3905fa527d486690f0bb2295b0fbc2b183b4a12ff6e3591bc5fb71add0f150e4b30d4ba253527c65aedec85fac7ac26474d4ac7b0cf239083361e6d8b04e2e0c89407f120514b034c373323b06f101ca0d61e9d5b0a6082ce68ba1fe42e4f343deaa1e274b11249efc342bccb9d3c282e7cfb7f54aaf66d7d1e546e9c18f30a20da243a3fd90b7cee8688df9e9ac60f1b647a6c7f0255a676397995156062c012e2896768665f47d161b59344bba4a60dbb827797b29a6ded9277201e40222c12a65e1d6829cb4c7b2d23c8458491e994e6cedf0a186c4829e6dbdcb321565c3318ce0cd0b24ae3924cc4e42fbbb5541d21e16e11598e9faac19f2d2c6f021ea577d52f435712a44b777997986d29fc5671d50fee1eec7e4c4c577cca811cd5220ae71006408f0b60ad135a93b789dbbf9cba3cea3f4046e6209a882105c3942f8fd2a811114db6caf011ef06aea69cf8af708d9e705a5ea8b0782f54d1a65824233c443cb0beddf16439b754c225881b59b95380187ced39baea643edd1f3d23406a4b670a55f3a8475cd0cf5f73cb5942f4ca5f688d8df13ae211cf7335cd388074c9da9bacb06dd2be075612739be2cfbfa23a387df90b1301bac8ef50c8b2220456b1d403615bbd85fd47a05ee65bc918d2373505ad3f01dcfe433bf73ff8631c993465ce233f75a8212672b487b67319aa7edf8f630b502e7074afc789077369da17d303a51cea4a99297f166ea46093f82a44402a222a4db398ce572659deda2be6cc72d3e4b29f473ed60bcf1eb283096f6fc43e3357ba96ab041a2f7a2071c2080f329fa8079fb55d669d08b4611cc7a80927fdf7432d4c1c09a0176eb2a72ac7b053542e5fff9dbb8dffad6ef6a0241a46bb6b5f1452059e76a36efc749b4c7e1a3a748beee0bce09474a42db205a9593d54bb966d8efa404356f38644e50c47272940cacfda7231c407027b0a954fd4544144cb882870b08135feea6357b9d37b77743d1fb18f9ae1ca2211c2c0da9e3616f60eb9e7c1d93653abf2a81fd3a10bb05b9dab202116c640dd9dace9d631cfc273df39f657e8ac3b340484f66e741aed3e35ffb90128ff155e6a3c45d6914138cd164b2c797176a16f1bf9c9bd19b7ffa6a866e15dbf060ab3d102ca82b519410e2dd7595ac622505a6fd59e9b6b0003c05f6fbfc114947498015c6118876fa1997545160afe14383623111c8324cb3762a314ab55d0399523bb788029b62bb4da24668e3a61ea3a1279c27a304b5950f84916133dc52e1ef5cd4a34ee418135544ffd2dff893fd4a1f7fa5e837e3cf1f12f587697205052b619b645ef7dd82d4725782e73805245b8400f0eca7422d0647f1b86bf0fb896326100b9e4bc30dbb882e7d1f1a814861d6735f3fb239952f569acf3b9a13264d951bee3b5b5d1ef448c37f0eedb07a0fa3bcc299d2239c5774cbb46a0891e27b277fb6af272f9238b2602e891b8b983858ad53d5142b726e3e71353ccd1c82c6b27774c773e903a2a93393d40c153145d21dc8e3ceff5cb3cc1a16669025777a33d9c8b986e6e42658c60531b4f2c3248794c70b41e50339daaf3a1dbe35fb84047da5ffe845e2dde760c1b66b508550000000000000000f886890100000000e80353b9eb784d8aa119847b20c4b1f23c0a177a0a84fda3401a2b61fe199aa1c026edead450f8218fb949380f7853eda4d3b44f950559d97d3798f775ea779678320253be6566c23561f326d2ef04ebe404bfd21ba930cdf0cf65c58b4df991000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000edbd8cb502695c951b40f1a346a6813cd263906b849ae5a85127579083b9d67b8e3290cf95025b805709a93e360f8206a91d6391876e2f8fc9f8ec4e83b1e63bd4915507671d4a3ab9e51e0ccf3f2e3445f2bece157d7b00d84a780ad488c3627a6d9fda774d0278260ee1da87a844e10e32377f67bf6421170caae74e4375c031b30e9c2826ce2a3cd33901fc511e461cacc2817333d949dca4e4cc7672e9cda0207d8c595a91417a4b78b6c80514a6d903c1e953b50ec8b4d40213b03c7f612430b1947b4c46b74c75ad94d63ff18a44423cc231cb725bd797e90e0b2881592fc1a222c68be31e49031291edf40f292f87639a833d7d6e9db868b4a9c4ebd242ce60211c89df481b5a5e5cd9a973bdcfd5dd41d4d943ad9c4675a90dfe6377c9af42d031aa5e3094a555c87e8ffcf073903f028b840df7413f6f3f3ceb78e4b78075b95032873cc2982d03b8f3d22f64b7e820e837b3a868c1579f63c61418706ba5b0de3022ce8a9f1d8d28891ede031128dfb2b7ef839743fa19ce4497525e120b834c6df02173d9e3c5abe12aedb636af9c618f61a23809a02da28cc2d5a9886a5f043af31d018584098e54f7ac26a8277ea44d0cd5e2289eae3dc8b95d2af6023995380213df8ab0090e7d8dd7c013fdfc9790f7adb4444315b3c0268705dc3801195ade88b4bf9eb258ebfee4f7c3f685322ec3c394bbca7fb0befddd30dd58f32d30bff2f6f2119740cfaf019577ccff979f9ec7c09b949ec1ef7238e6906de2a9074981dfdf56b709958e84b34ac2354aa68ea21574b6b4a32d18407ac244e9eedf85e82697abdffc2277c44684406499c6e5b00e053f9c47d6bef49f316cb3c96ec5629606f963789d71e6a2d0597178ac9339f37473e6d133ab0de2e5a8012dbbda90cb2dbd4a95db6b5dec9fadb4ef0295475b10807303349c85f9a4bcd098ea975acfb0e7a9c2b83fa12eb73c7aacb428cec60f90e256dc3c3e1f36710980860cd514c2099a99c667f8c4e738cac705b7614d94054ce44a035ec2b31090f5db17bd2bc0c0293f4d6c1cbd0de8d1a74982b09867b564696f5994bf45bb5b54cafc52034ef2ffe41b44fdd2c2ff7a87cdb3d76f119acbfd80bac40cd9c8620c9945c36371b373af221333b10836e81ad329f86543818bbee6ed104fc836d771c33719006abf74c3eb7f452e85bc8f866d2dc185b7df154c5cfe3d3854c40c3c0ef498a4a43bbc761b8f026a1df7353435a2d9024fd09f65b205e4e9e413015cf336c20485cd75ff3e11360e46e6a0e16578001d06188fb48a7d82792da6e1dea911893dfd1159d1e9f27017e039d608de8cc7eef0690ae1ed6e9cbcaef44e9f571c19b09be1bf2fedd3e20a3df484740ed08026b2b2aa315b57230303511671184e7e23de98d87cea42e3b8c62bf8f07ef4c5ec54ff38deedfc56388985f2ce4f4005a5ff5cb62d47f39f5f6b8d2bdf9506393ce500d1bde2941c46ca63da9691e1f50634261897375eb3a9dec25b78d47d9ab278fb0355408b78823e24d647c0d184a5eb972ba34a39975201d2f5708b2c5206c7dada38cd57938b0e5213d3e89b1525d4e9fd084fe229bf9a6ace4cf8eee86990c2b36bf7f1b3c6a49e016f8a6f7b31dfc606e3d3d6f21167b4e7e352ab7a484bcfb6db8f026689d2b05c88c3e66d124d3432779bdcfc99e03c3158d9e79d84267bcd050ec567c03984600f867f2b7468421d933a9a8d78eeef69d6784834baa6f66de04839f93c1913f84acf524c8a8631241879e2b7c79b87cd052b13f98432a3312ab6134bfda66e98fff1da7e697cf5375b4a1974f68542693e2adab4b77b63657f83dd759d4e70d822c78b72dd24f2e951cb80c3c026df36c716e4572bbf4e58e12827209a8f730980f04d560479e13a6c5af887f197dfcfab15c6aa36135a5a9a15058d76681e8560c0b12cccfc322e998b8759039dde9048b09fa898f2474e61c1a960d85784aa77df6b9525743746e22fbcab65e3fcaf2161eae56fae6b212d4aad0e426d04bdf96fdc36f14bffca7ed7bc260b41bb12f62bad51cfb9b27a87b3484fe940fac88817c61225849e625f0f196b351bb484116005e53b03cbcfc40dd8bf382a1b340f1779ba4d1ba009007c871a06477ff365a86df0622ec74d75877eacfc2c283a2e23047f6cda9f591d666e03c54ab9957b6c43e71eeeee101dc02773fa203dcba609a6973ace33b0235c4d5870b2489d4eab921e957b2e7d4630b8c21a5720cacb1e344720d9a9c050e7b4fce4820b37f9b38a1512e8aaa34f23fc12f0ebd1f1b356231ca301b7e863d7464a4cde87386bad92d6a0b995542145d41d908cd3ff6201dc5308a5c8a730bb66302edc9620ee073a683caee52ebb67446d9ead88a009f0e9576a4ce6d87090168480cdb53f8a8e9e53409", "5200636aac656365", 2, -956787194, "d9b03fe192df1b358701dbcd5ec5bb390ac63d16c87b7f5cb61791d44d7ace68"], - ["f5132c83018ceaaa7d17e35feb0973b073563a6353d9c9483f4af61bbe15180648e0ae429b01000000016ac3f5ab4c015bbc0c0200000000095351ab63005365000000000000", "6aac5265536a", 0, 1055742825, "3f902efea4b298c0ddaac3ed4d1ceccb47e991c63d3eb88c33830461b93fbbaf"], - ["aa0e5a7d037123cb16e271e11204fa1e33668b144fc86bed1b77dbd3a09c1ee856a7588c440000000007516a52abab6a51ffffffffe46fe0cd80ae316cfb6d0a85e718da2ec1483e8da759925cd15fbff224cbbb1603000000066a005265ab519de29f4d8ce7d836bf804fd9b22677211bcc5c394aeb9ba2149481cb2f6f6554e0d6c21f0100000000ffffffff02a4cfc9040000000004ac630052a3e91a01000000000451ab53ac0000000001b9e06d0100000000000000000000000071ddb17d1247af8b1a5aa8443a498c50a4c846a09e77a694a86fca9b70b311efca5f0182688a18aad51a8565547d9138de9fd8df4d642fb5d260fba28b3511551ea0a0e4214f257fd45319ed36467bff2e93ba8a7395bdf5e14e61ada4025c2d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b46930594f661ea98fe2c0fd602ab26a5d8076757706a8d30a2f2fabcef2f4e34958971911f5a5dce0f145682d71976dc76f790adf3d8cdc63b2416ad7227448f46274d696e591419323357ae47815946413ff488adcb4fa138ee19e948f2925b708ddfab265ee975c76bb4196e28e81ba3a7f43d75cad1de3b601f02b20aba0022b2fa59fcf83bb7e09171150a654c65a0e77650b953d0bcba88b9c782a31beb70330407ae9a5a1b0c1b07a765e02412816eef12d703e2b9098992a1c7e05efa3c00b20da63b5d5acf40d758baf57c804d0054bea986690c2d498867959198d3f98051d59ff2c4a3be5f87cf148d31107afa58204a85c4fe22ada9c10e60244fc49450229a9faff414bc1c844e978549eab2da1db610cb809777016df2eeafb16d45676030c1fc2f3629e8b00f96076c8f35ff2613113a52a7e76f850365032d55dccdfac022b95f7b9003c4bea064cea87de599eafad89a1b5b39a579afd48fa2c18afa43a031a5071433e0d8444c106440538543663d3a4ab6915409d20509473fe9593d4ee030ee23acb632387f4308977757f777b5bce0b7ccf53cce7e9c7f850f475fe938960837596da9c40eda2da85a4eeef7c3a0a4f5c31734a4be2a6cecb0af5052dc856d9c02d500bd0245adc5c22d25e3d8d2be09e3dcb0206e8360c9cf18f9f6b381ae4e22622ff75eac9dfdb182fad1704f45508287ff306d576e1ec93db5801b7fc585ebaa239edc2cfde99b76b18e4509f7464baae60e5a3d8efcada522a7d797380aac50901b57e7a0aa55cb2c92f72841baa72b2552d230a5219d0af0252811c218866aa68728ec521d229cd4558e580654c7e8b8072946c792d59c22cc4dc46effef480c35e306ea6813b6266cb4b43e2a2d723c90b3c5e9a92cb87bbf7ac3ff3ceeaefadb2aafb5b73ba9fae5924502cee6a0fed3634cc031df4053fb3062a862f4891fe296435a07cf3a48ab8fdd9c3c9e065bcc507a0c57a1ef522b798f47cdfa7d07ef5321b37d6f0ae4a9c52906c7e5c881e1c1f4a21af0a3f7a87dce9a1408771e0526ae3643b9d7568e3940eeed695c2336e828ad3ad71377313fbaf431c525a4dbf862537f087045dc6403a067622c42cf4caffc38de7cfc2012a9ae1da75a734f3bddba97688c0f67f269acb058482cc86adcb2f0221bcbca8d966ab9adf26a184656594b8d613c913cf8da9f021a76300bab89d910c6e3aaaba1d61c1766b3451187d752c78ede87617c37d9164286633ff9f8917d9cc1d7ddd472e4b87722c1b40c76788c21e1dcb4eb645df9c94a05681164adf58572809d8a6f4166d406e3afc8d92778b735c210d5d863c06ec8444fbb3da06787e007efa6e5d8c7d9abfb8bc3465964d72cd7ad28aaf0e6174c9f4d33290faa49f3147d98c80548b9db82d29c5231f292f4acd2e190875872023ece9d1578e5e5b444be75644ae010be4defbe125353c33d5affe8322423119a5cb33724bfea961772c2a53501d0260a41c177f87e1ffa4a1351a7f240793ce5b567354d3e54d78a41e64e157ea3aa06c0e86b0e66c9a4567b2c2cfb708c4cf5ca3ed891c08fb099fb73c6e846e68bb72488826a5436969076203675b043cd3b8409194127c7ec1f9fd52579843a9ef9b2336b638aea2177bba267324b100d321324388da888be92513d504c10ca61cedaf518081d939d3b87fee9e6b697996ffac594775821aa88c7c6e92db68f8cd5ad198e8d1f04743ac275d9b25a0680748346cafc36d1100465f3ae83ed63190001a94cce0feca6173be30732e6578c57753e7b104379b91433b752f8c109dac12488289ce7bdd1d80bc2d71add9ea5c77f08ac9c025f4227245a74d809775862b1a9ac759f40648a6807b75a93c84be9e07bf108efcf3ce4b9003d817cb684a999584238e1c5dc3ff857092122292e93256a61fa96a5691cd181e0ee106d9f655e1264f0cecfd26c246eab9fbe299a2a8eccc7c83d5f2cc7837ba1020c0baf1d87d44aabb279ea552ec2660cc887592090df6e010f784aaaaab737a66f21d85bcc2c3da17934bb05e5ce65f91cf21f07c6f10386a962df32d13126cf77dc48c4fcc727eda7586cd2fc245f9f09324631c6709ffc2e4d38402700e805fe05a3c016d2ed4ae9cb042b3407e1fa1d895cc64a1986e38521afe213179d8db8dd8b45ddaa740effe292101337086829766c1d2d4b4de8bfeeec6cf6c851097ae0b0f4ba93fe25dc9c3ead7801d22e8baf4bcaad41963da202ac7742ca5ca7d93e8fabbbfc1f1155a2f183428b25c7e6e33cd016cf1dad235cb82848e339fa49a838262b315f3d3c8786a74b4e7053e48bb7170704ca10c0d6998238ab788d85d50bbec037db506967d1afcfe4e581455f737c300e9cb30894f8c1a995d284f147a147af86a3a0fcaae45aaf1463d0d", "515353ac6300", 1, 2865390, "279250d8f887d29f299f2ea5baf28985957c5f294cb8e6dec842e735b57ea033"], - ["4d97089a02ab0ed372ec190025feb89731c0e52e74e1aff6227ddb53a108c53ebe378e2ca402000000086a6a526aac006351ffffffff4092f1af0be76c780e8358dec6d4d745e679945bba9669c7260d55a0c9c260030100000005526a6a5100ffffffff03c41e3303000000000365acaba132df050000000000ccd88c040000000004ab656a6500000000", "51acac006300", 0, -1925098182, "eb395b6a5f87e75aaf9ceda494314f305d60610da4aeb82b2fb35afa557157b2"], - ["32b5e5f101e7e90f162c2016d38b774608f568c917fccfaaddf88f0207fdca9e888785fdab0200000009abac63636a53ab5265ffffffff027349d20200000000046565516aaf1c1e020000000009516300ab6a6aacab6a0d341760", "ac526a636a51", 0, 1050565107, "d9938c3c6300f51c233e2b094d330416d0639ace5d8fc5382e73d2ce78af0a71"], - ["4ac8d6a1021c51414a92c123c75c1da57edd4a22e99d9a8fad0b55f33b652ae1d164c317070200000004ab5100530a0f32e67d37611bb244217f59de40d20ac4fcc6a53279b0889e29fe64737747c6d8532a010000000551656a00535baab3d404b9c9cf020000000004535263ab308a3f050000000006ac65ab0065ac218fc2040000000006006aab65abac16aacd0300000000066a6a5265526a00000000", "52", 1, -173005906, "78d369b9852c64abb24b5d7348b140f28135ea248da53356349d1499872e9d0b"], - ["c5a8bbfa030668d48f9645858e5adda4cc05a03d598f24f2caefcec2ab3f145696e07fae930100000000ffffffff46cc05252837d9bee8f9335a04c69b4641a5ce767608379cb246a543386b435801000000002cafee77cc2b67d631dae9ad42b2d47a57c80b0365c75249af6e34111b8c5f97c6f090c90100000003ac6a52ffffffff017a42c703000000000352ab5200000000", "52", 2, -622472365, "df6cc514ce1c44f7f302f6e15b27ccf8504c8ef6aa0e5c02f63bb95c4e2d18a7"], - ["ac2648e90208a6997bbb57dd2289bdb7482c1b7565603c18e09ccea7d82a79efb84638c2e2000000000153ffffffff711fae6be6588c24bff48b87367184cc7ef37d4b2b7b9ce19bb5e4d61288a201020000000800635351ac650052ffffffff028eef2e040000000008536a51ab51ab53ac74fcf3030000000005535253ac511236d78e", "ac5352005265650065", 1, 1223022458, "8fbe583841e6c94d2c19be54a1d813bdcd28c95a7e87f7d2a372b703e7f1cd50"], - ["fde6dd3403cf0b3ced21eeadae43508b5c30eddd5439a4996311a70c5ba016c0648dfd445e0000000002510050c4d481141e56f551fb59712ef671620173ece9631ac02c1e639307c0b40a9e7bc411d90300000002516a621814daf93c1870506872ec0cf294a03535b73cc88d385dd089037855afd0b8c29b6a0100000000096a53ab5352535151abcee4e2a2041e1700000000000000ee7228030000000004525263514ec02500000000000565ab006365d7d8bb000000000006ac6352ab5365fe71331503f2e6c8030000000000000000000000004bd7c0009fcb08248eb4ffe1d21aeaccbb95cea1dfde26fe8e469bcd2c3ccfe429b0241eb3192c5796df13066096120293b098fc562ed5b60c4fa8616e4ff1db1d6e2aa9127b8bafacde3cd3c6a3b68f4706bbbd94c6007194c264f9abb27aec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000650db20c3fb2e279cb71e3db42f9ed81c785b4bb1a7b95552392e39ce5d066b7af05db776a6aefb33f83d71dab9ff578ad05df55b4e4729569ee4ed65f8f8a24164351b764179ae3f89fd552ac6807921c8ed4728444fa4b7de8ed9b1d1f6c91aa78fb299494e7027d89c68163ec93431ac558b35485fdf01aca6b2ecf6d520c03260572c0f425c3ac0f5ddf2e565827c0c9c7de4598a25467d1521eeb13f81fac02108a7e710d17507d964231a2f059acb864b2dba29372113b54cb4ce7c88cc5a00b16484fcde4a56f06558a82b387b6069324f73cb9b2c1b46be7e5948997cf6eb72d656388b58517b83d6de0c6821b5607e83d6aaf23d8259f513c0fe1622bb80202129f3abd7ab0663f247aee86cecb49633e538c7a0d73f292df8508d0ab59c10b022ee4f993cdb2dfd0bbea7249ed14f7099fa8516ed40f22f0c522b063d1e3935b0325fdea70ec76589300680aa49891b4f0be839512ee7f7a1b04f1d1d452d385ec032fc28caad9b27c51f26ef1af38d6cfdb51c25a0122fac70617dac808d3cbd73e030c690624e87319da02e77d91fcf619cd60f56cf73b13999a50ac4c85e67f809c106278a2e09203b7a2ef70492937828fa1db01d8f1b07ce92660eeedc98fac7013dd4c4a2c6c9228dbb01331fbffd7f8f2015a3ea632ea082344445ff5ff973d75bb7b8cf999a4f2342b61289b67a13850b8923ffc85fdec34c40dbcfdb56be8cb323b0c773677b55bf300a90cb9354dc6b5f747a1a6ad4767b59d9662d1a5795b24d14ac19847f6728fe382f6d6e0daadd0322215a65301ded56efa46534546e4c0167249aea40b8d77e10968367ff69dd3a673981e8dfe5993d2efc398338d8c630776a5fdeb262a0b50f93096ccb540e6f4f187da393037c30a98aa1344e46046cfa11d8a4e9643859c4d512fc93015e71db14655520188379f56003836c9fbe151d9055a4b55959f6ce3828cb279f3d9622d7235cd46136279d2e39d93471ea8a0e53a050829eca78e356f7d945798673e60faf638f2cc8a4035f865a45c7b0d6cdb2f4edc04b050d191707586e091b8890cc91235b00f84d84cdef81cede10989cd269209fef5195ca3eb32ee4a915c5746b7ea4dde6f356a17a775ec8330736a9557c7184281a4153b8ec037ce222eb7af6917d95d22081fb743e2f62dfb83e37c3643b4133fe629349d40ad10c3a1c8ea13435a83415950fafa375fecc153e84b81719d3e3a605c4c46e87ba59582d1aa39988338281a542bb37efc2d1bf6d5a717d527696e3684a711be9ea626c48b2535d05d383c28084940d92f5adce72882b294a7fa23dca2cb1f853f671443b151add8512faeffe10d6a0516c2592a8c877302c807c222736d52b5d3df2b551bc1aa851dba67ca867f1892d084643b4acfa48622163cd25d8316a5e804db495275f4c95401bf0c9f86bbee8da28775a4c1fecc8482eb1664d6f9b654e368f31bb0e0ee57ecb2ac878122eb98047853e52f802f6abfb5858b3fb726e9ab8805e740513d7f1665ad7211db79b42947b8fe320aa8a87988535b4eaca1bcafd10104a8fb133b2de796a89d4ffe057f58ce874733792769c3174e39e7857150c53ff5d30c293bdd4ef601f0c8505bd7bc3801adbd790542ca2c81853a12f82867a24768bce00771de693f06d4dde4c7e2fc53c81fbd9a53cdead1cc520a25576d54b248bd3fcfefee384e10ac2e4778070ae21eff11619859ae1371a0f5ec32d78a46f657e877f2cd73fc29e08e64a7938756c4d22ed18407238c6e21d615fac8bbb1fb525422dc8614785b8bf4bddf8d9ea043073123ad37a972f4c41b128c57ac13f6fa94c34509605d78b1fbd8e2cced5e9efe9219f9d89bf6e4586ed6b184eab88c1ce9505ad0921df38fad8501cc1a79f6bbdfe29edd3caaf24428a61976b5ce3b82e22d330000df2eee0bf7d6d41f87b6cb018b0e70948e58d359214b7762ab7ac694da0deb7c94a37ed5db93ae57f020bc3c69d26e46dfd32f9da04f443e9c61631466a109c98e99f3367bb8bfc197fc6a4b657b4873423a235cdb5c156fe2c6411906138320ec114cc117002db45017d77071cfacd2738e2ba3745a432560c5076dd72016f6fe36f43a866325d00c3133052ece931338cf435015e9081d9cf6f0540cb6048a90cd925ff044b0f03421804514ce8b30665caf07b41897a5e1290fae5fec219016026f35fc122c924c6b91f2895be1fc18417555b2ef6fee1421d4b973b9ad4e946d86a9d3259e38c59e296f7325f4eb54cfcfd9617c26840000000000000000ffee15010000000031ad32008a6cd0b89b21d0801ccd0afe69588112f09d7c2a59c268bb59464fcbcf4ffd5d584500dd6645adf96a965dbdc7a9a355125669527c98e1f9f4c33f37620d3ef47867fd5bba163f37527a3cd14ad9859d74b71ad9d4e6571e352a49ee00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c1354cad0881a4ccaf56ae491dc64f371aa845cc06ac1f9c6da10f85743a5608327aea011288cb8966e3978905fcda1e4898677fca4934eeafb3c00913794ef37b3df73bff38324ebfa5637978d5c6af78a847c7fa8d97a744d48d955d399183e55d008fb44004c8308927db01400e8dda91b93503f3051e451441c0110abb3b0226a360069e64ac40e66f925ff63f0e824dbf9b811a10ca4a3a260856a9aead92020950609669da8fbf11e5d8ac6ce42648769944ef99d5a76d301a81357e7852180b194d5016337e52c4a473e41b9511bb1e09a1550103abb69bda3c1e373b8f59611225a6986b5fdba99f956bf7a089a6dba207b9a488ea57a0a3d63a6526adcfb902109868d402b350e5bf7f071d6ffb1743caf64a8ec81d12044378b6421b4b9cf5020802c586b7f764300ae609092616c4bd9b7cc3c33b222c643fcdf6cf5bc23212030f40ad181dd4f66b41274be43937781320ef750581f2e660614788fadc36f85a021b7faae78ede3c8ccb3cb77e74fea40d2999908a468824a8082f9e9be1eeb08e03103ea2ad4d214579f5696716716cb90563fbc6faba1238b8bd0c11e27d1ab1c530107f42736b7f5c0bd99c58adbe39ee8bcaff270f49d0598455a008a7019fa31a31da138335aeac858cd9b151e2dafa1d8de37510a943834ca5818c28bb359f29904c7c57f22619936918bbade8312a46d121a5c042dfc911a4c2b75d477ca506691b4142eb93043d6ec4d69428027492f5cfb6dc8a0248b636985b4ebef8f93b2487632cdd45bf307d8d56e9b0490630f5eccb213683f2217165ec6884ce9a97ea9116abfb84dcf79d412e24ec465133a6efc1b77e6251a16526982cab2b701c112d91db4f53776fef36c29cda68fd5d6073e8d0321ed9b599a9fddf06cc6140d3debcab799436daeca6b302b5aba2ded1eda8fe21a6d7e8d92a7db1eb5ece85adf0a73ad7efea8f6a9641a6912e403dd97a9e22d1df579010f268275d30104b8925fa09a6166c19a4f72374c06097751520803b40250732758b33a8dac8b287f84fb7033be0bbc279b3fc3eeab7d2db77002bfe7d278e4a533f5cfc3330183187579813434444a248262e9cc59e22f0a1e8eb30cb8b6f7725658cddd703aac16cc9f150279239c04413ccf3fe10b48b291c6055dff824c2e46e076b7dec3fb1a1ac516a546f44b8aae19ab964138ef913b1c4cd18e0a907a80adff15b03a4410ee2d50533750c1ab279c093a9c869e2ca963e0b397c5b2dd339e67e29c2ebc4c9ce133e7cc5161d89600de6651ac44ffb264aa75f5d040d5c9c89605538006b0777a3bd42701cc5e461297dd2c9bb971427b7e45ab80b997c3185c9bbcbb100ae29275f1db4d76a5f1a42ba1a0f75e430828daf09f99888ea4db80b67ff394e278d36d51a0b3e74ffe035ea2c33a27494c4e30668bb32a9dc286e2a8a17262e841bc9978bda141063161a2795306eb9dab1b87cfdb61fa960285bb381cc1f5f371da48a45008278c626c6fad470fb124cec49e50f24c96f6dba516d060088d83af6ab83827460517d16158d3afdd52af4efa73ae0502419ccdb112fa94a5371e232ce09c949995fe7b2544e7b53984b4142770b4e9f60935327e7585cfc05ad6533a3c87cd245f211af065674143603a23362dcf8573a76e18485f0cb38f80f9d4a2032decb328ccb527fb6bbdc187ddc5d9ce0ebba0ec0c3338da2f88f709e52faf693837165542410921f184f5e81c57b921f0a5520d22fee7843587825d8dd734417f1752b4d17104baa579be70cfcec8f49fd968822fb05870787e50b8a0bd0dded3065de3cb810ef02bf4239e725a8609e6dad3c3fd10f4df7e82e74ef66d909b9af2e315bced62d32aff959c7492aaf1d5edb615f19ed8978c9ec60912dbe964dfcf35b2010558e9c1777ffc6fff9c28bd285a607bab11c1a2344d753fa0d3aac1de14d99a288422fae796cb9bbfba90e6388b76a1f2e88607e3c7aebe15cdf5ec1cc47cde6473df99d50047a74a74e26a591b747d4108831aaaf3f863392d228db88179f13b3ac8028f7d6c46d896936911849384952556b7f55e368cb0a2f0338eacbf3908124de2a3095ad31fb6151a2a3b19fa2ae86a4e2c02cee152c885ebceabd6e7a15d83fae288336c257b49faba5dbe91b0ee90c6e3646bee2de3a72f8e7c2380712137c93c9ddbdfa7967d3c760c0652a3f0493179c58d0216647650f40e025328cb8bc8ec9e4c2d8054db5573ad42bac7049940e4de6775189a66a279171d0ea41c20df87f6f46b2b715590000000000000000000000000068ecf9b4e09568e5b178d1312b1b031a0820254e147839a2acb4eeb1c9b52b6bbf13578d786b92273d5ecb84a392114f38e9bfd59fda15773f610111aa9617a3e2a634d9ae4cd988841f081a7dda7b79ceee1995ab7965e5cec32ee3197fb13c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e2ae215a006d9f695994dd0ab2a0a1705350f06f29276a7c252e123212fe038de8f70e468e3df5ef423367be5d4d97c7fd837b00357d7aef21b84eb87afe8a2995194708229bfea15707bb0415d021d29102e89dab1c84519ec8cff052c37af0e4681daf99be39e8874d41ff46b9b7e662504c8ebdfe5e2b78f227c5cb82481e02243ac641a0325daf2cc15978a90932ac51e3d3af1f41d97d10e4a8fa1fd8cda5022d255bc44af3203d255822450a759c92d62aee7f425dfd4811bfa043eab15f960a170b0e5c91f8e64ad95c788b8d4f7735b2409836ed7d0c5404ec8cb0cbacfe17029aa18136a9fd5a25d6d441ec4bdddf6898aa4bfaaaf27657ab725823a78e34021cb225abd557178bd33dd2387c9bd1492e04fd54d67bfd598557b5ba6584344303104c1bd1d45b84ffca94c29db67c85a004a25252b63dcae0a25c7d78fc2e1296020a8dcf36550a86e41df1dd8242f75213636033201eedcc8d8da07ba122d09917020ca03ce5c3dcefeabfde7d23354fc0bef148601b5f9045fbb436fd02f11c317c021ca8d0e6a6dc79733275ec0a96cf567357bc6ae4508732b85d2a384e4a1012125c70d27b87617b265a5319414d6787941c4a4d7dc7047271605746f2cee2a6a160014abb3754d209d4b023a728c4063c685b1d244adfc5a4f07737e2771b2bdb6c85ccc98f476c2c4b2859c4fe0547c19c1fbdfbb750e0f4d7c0f853ba72cac4324c59edb227c10bf96d4c1dd5cfb0be24b3416b9b3f427a5ce115c67b10a63e3c0afa884aea52f0a71a228e02030246f59f74a69272d12ea1365b3409643ca28a9f8db99d9c46cb704515cccc69ed4bb508de1b4f0743a0cbe57f8a6f0b54aa4f8ae045d4d764e21e2f13a51ecf79c9571865bef8f8c12a9e2119126b91ac3dd6e106630c2bff8ba8b8d758755de174332880537bcbad8bccc238fd869ca20338f5d5fa012fcdadf8dfbcd88a2ef4f5888cfd73c82a97a10c7ea033793b2b6be67338c74f5a9b0f6e9e4db2ea7a4fb650b6ca6035536c02e9190d85f205ea8430d3d2fc6b48361c148717dc3037218220684c60e8d1405f221f7533934bee0e122a066e4540ead8a34a953131ea08e9d207e2b98956098da7f8b0115e872da7e0f6e0c1df549fa18a78b11045b4b1e59774c71b67e9aebe082180c8019fa443d80074f3b8ba1cf2a78db506cfe2b9c9a14313d236d1000c999789c38ea07f1430dca9909e220b9fc9efc07240b756e406077a789642e77b2c1fc3c2a533e04434a75d4ea3a37556268d5fcde72df387739b0f05ee42443e463480ed3763e1d5097bbbab7b6e18bc88a9296875eeb4daa7b0b3a16c96eed3d35663997e15b970691543c9551b7fcfc3d9903c07e0ea51290d6be138b48b92543e4b4f89fcf128e167b152b1e7ad39bb2abdac3c1d1cb230c6cdff4fe66e952f24a383fc2a662efc279753aba545e4a4879dcc6fbbda2f794240fefd0b1baff8b1fee6728b55847d19b4ddb62ce5acd6fd2c6ede2002ab54028f328ed184497af1448d5e3a09c4f8e371f3eb98a3ca533b7cb33607b9d0b033aca1f6250a82e9285e9c3c501daa4b1a2127724ec53807f60464af88643dffbab50bf3c398086ccc19ca73318953cbc12611b32b3efcac13952f7c563d52ea6d7effcbe336a748bdd49779018b5b0be8f214997a7fa4e39d65e1317a8be75509cbdbbd62ec5d7afda0047eac4fc8cfa7236b01a35fee9c04778460569087b337ea19aa49cc7a01076f3d6f1ccbf74b731fc82c916220bc2526357a0004dc0534ab0b88208d53f199cfb17681f32ff180e9779b5c364cf68e8244d7403e64091ec2680924ad90cf23a131dead43ddd7383db038641f660e36a59811f5d5b4c1d25c7b16194f7ac1ca0eecf4ff4631bc9f5c4c889148f29330a2dfd0bbeff3b9da88d6814994254e7a1eebdbe2bffe4a40f9a7584a9c856a3b9982aab5de1afc20371a907526b629fc526af52fea9b17171cab295e9ac19d3f3741a9cf8837a7d8d695acdf746548eb508dfe4a21a9da805790ab557804b25e18db3b2b80ec9cb7013bacea707c5aa7a6500cc8620071aee800e0fca754d3cdc7a69e1992f3b228e491fc898a122aaf4f80eb7842248cd8c9c71d4e82c758bf7a2a61a718d8391f0243f09555200200456272d13bf10b7857dbeaf124d703975914daa66ace52c16c5590db45fcbb7d80dd3b743ee7fa329d071a99b6436d4b5f2c3eb4e776afe0e2c34978248be67249c1d0e481ebeb649a3a522f7eb2d43aa55ba696a4d19b84741828c8eeed03a9aab98de0a32f820e61097f228d9b0a37b20001adc6e57dfcf259215b10442b4ed0fe061be631e1ff6a632372e7863cc0150b335d9b5b5383a062aa6dd1af0586c2be4da4bcc5f8d89b6a1c3848b14870e0b01a90c572b907", "6352acacac6563ac65", 1, 1985332090, "42da1ecedb570d11e3a3631d197ed6ed72395345c75a8e0df4ff5d98826e39dd"], - ["c6424d5402fa820442851b9cb718852cf4d6b01f406448bbae5bb7631080e412484996cfa80300000001006a01b4dc4b83e09339712339e90c87340f4bc8070465aacfc88d19cfbbadfb7212a3640b01000000036aab654156692503ca49f20200000000026a630aac1e050000000009ab00abac636353006a8123a9050000000009515165acabab6a6a659ac8665f010000000000000000def3200000000000ac74b6e2d713345b5dba1357cedad10ef147dfc73a1c030978811b2a87f86786cf476b156c038df54aa0db175394168076016c29c09fb353e2667ccb19c690ecee5768bf6e99b93ec3fc321e126fde110ee3c9d1c70010cfeb7627b46b7954ed000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007323b93bad9047160b72ea8914dbe247dce5cd42c161dc03810494306bcdaf6d10e630c390e3750e1c345b2a9f86655d9550f62a194c41c3c64b688abb27b6abd5cc83e31b6e055b2f1e74dfc618b45c25f5c9aab7045572e6cc70d90ca5b1e3408056caf908c0fb1a026c65c5931a10496b6299eddf6db7b72fd05a5edb62480212394ba83ea4550e60fdd51835bf9466941cfa8a8c651274a900481458e98039020e13dd32cb0e8347f098c224d1b50d4bd70bfa8c8d9b2b04d7d5302fde60958a0b1227d7fd5627782ac65eb0217b06d065bc57696e6eb7aade0e60b43836a1753a066891fbc085e171d0e8dc19e89de53ef7d6a6509ab88bee70c69cd17a4611f0030d62dc9ff6dd77628281aa3dbdbb1de939addb1784761ded282a72ed5cb848ad0300b970da264be954f3d8b7917acacbb06c65d72af7633ecd48d99c26d091d95f022172f7037f3c1770fe284906bba72d600c7685f79201c33310dc6c26d49ea5f202245e85cd6adc8e3cd169d31729ea1a7c71a66ccb9ac391155d7260f951b4866003292b3e986dd4691d6a8ff78b3ea436734d7840c0175a03f2aa4547db76ab37f102acf9639161a8c54401e34dbe9227ca68f92071c743c806eb69436a7523b7b62c1b1e8a0c51fed144b0813c1756226a91bacf397fc3b76ca0354cdb3892033023cc37b0a47d995f88284013def2486498f158330c39a0a6e172617982a2c50fbda7b749eddf9859a73af255b3c1d16f4de1a16e566ee2e7231441104f5a0dbadb422cf7bd0a556ceaa5ebc0954f72796b212c0a2cbacfb6bddb0c45ad081c9937db3c8f759609a563f3eda122b0849a89ce5927bb907d553a36814a4a81c81eb78c0fc65332e1b175ddcd642db8aff3eb247e4397320fed15ecd4600b37a07e95b6ef062f83780c68b5612a31beb39924cef8cd3e0c0c63590158060b20bc731f1652963b4046059c0d54f66ccf271706661e302a8d01559c0fa824e4b3075093ae57141c502d106d49ea5dc62a6096f77cc35e220d3dff9a4e1811cbdf638af3da35df83669455709064a5b392e22c15ce117c58a7ad6e00bc959407e5b09e01936df052f362be20e07292418e4bcd39ac54fbce9f611f17d5a7a9057381788dfc201dcbb93d3bbeb48440b6c11bd7e1bee3675c7b3f88f8e2f3ad9c547beceafa9bc3f0b2354a4a946e956e06a0ef530a5b6e0c1a37ee757763547d47abfcb05201fd0403793e7ca3966497c6ef2fc64f3cea0f46a1aaf59cfe8f626fa314adf5960cf9f9b6f1a77ccb6a8a12b22bd70d56be7ed74e36a5371cdf6c8e1f844a365c5ba9f966f557e16eeb8316b6ddd571d5eb6ff83e60db3f190e760aae835c1e2ae595a707de222b70c9972f8a8b89a468fc15a2bd7baeadab2dcefce2e457b9ff9e948dead779f73a003c70f61f8f5cefce8590de726ea3daf414d851a51d91d90d40200e0b7e28d4f39a113282f3dea0e5c77f22f2d0b1943bdc6cdcc262603f6d5941901badf5f5f677d69bec3db9e619aab9a5ea96112229086b09aafc5395e4092e0c259e1832d231c6cf6cbe544bfef536b098facedfe2ba0ff8e1493204eedaba929de0b7454af2cc228d40469384b956873631d42f4c4e4e34ef763e5c05cdfe176fcb9bd9a3bdce48d2dbe3420552a2237bd005fde30624a65c7efa3ad79e101748ab0e12aef1ec3f3475285846102346ddee256411cf3c598d165e0082c82dea183dbddb63a9f1def13f29e21eb614c535fb05b02ccb15a1b088ac2540f4b50675dd3afd828474ef51bfdb4d5d6444574907ac65ca8d0de26cbba54d12c6c6e6d42b6b79772e9cd9442c2b80a5920ef3b8f0765c64a356d9b67af75f9fab5736f75b21570387de908f48b8a50e86831f823120307598a72c646cc1656c6bad481e3b871aae19038d9d77cc589cd6feb822485068e68957511887fef4a3000012c06f938163c7883e430fcffec3d2bc90b251153f62cc66732cc6e26ab57f223349530f818105e7cc7417db01e858be0768a50c99caa50778e333c36cd0efc2994533bff6554905355b38a89c960f569b6c5de66b1040de0121e17044f1f44978eb1fbad22edb2722569ab0ae6f71ed29293084d4758b9b4cb0faf7362faecc8e07cfcb767ef6cad448cdb694a20c815a22cbad41a8f5772de9e7cb0e228ae1902e539f32589c660e83d1849de8c4109b6045323038334f4ea9fc012dbc39eec324254bd2045e41c7d30ff310ae52878ed9110c0bd9d0a846572572d9b3c4568901137303e4fae6ba4db2890eb88e6fa3e460115d564174e2ba35b059da43b97b718e013410ca7d5ec5b0cc8c2fecf84b7dd55b1435381991156fc729906fc10302d853079be9835aff1d9ac72893d6c6b9fd12f5b8fd3ef3b615b13abde4bab127da829190243eaae7cf03", "5265", 1, -2013807467, "ea37f526b1296e31f83bb93bddfd8ddcd0da6d32b7e7404a103f722b00df88ee"], - ["a461d6fa03532ccedd77480e4a5676cb33b44156aad4257d6eea83a11e9ef2c2eaa7b317d7000000000551ab6552acd150a7faf60a083dae3b6b4a12df830e2a5273743f22a7d3e71639b9f1a81748ebf73bf902000000020052a70c7c5f4a7c56aba56d3babced3e31d950a670a3b098377475cf3f611341820f96c71150100000002515171c5f35704770eb7030000000009525153636351ab6a6a69762e030000000009516aab6a005251656aca016e0200000000056a6a0051512df915010000000003ac510000000000", "65516a00525163", 2, -1277086031, "3334e51e0ffc08170e348f3a92d327b1d53a16667f387c374c0454d54c1c66ff"], - ["750ebe96025479f3183c8f37cf3eeff39ca26e82622b7692fce90db6b921458915e76ca94902000000095252000065535253abffffffff0f312b40b5c2ed63640e8820ba8059c87e2db66a7037bd183e11ccc114318763010000000463ab5353a522a09003f1c05a020000000000ee5eb0050000000008005363005153536a29d6de020000000009ab516a63ab5252515100000000", "516a536a6563", 0, 1687881631, "8e1576c098e32b30f0f093023c4a1eb08642a0f96d3c6129ca6e41a65a0855e7"], - ["83d9db93029058c3b3575835c286861021bdfa188813bbbce11c4a446fdd8a25f3a1ef605c020000000600636aab6a634f15624dbcdf4f3021dd065b6b78ac493de0d3da50c0d7ea47d0512acd3ddf183ce4de300100000000ea9c05bb037cf759020000000000920d8704000000000463636a636ce68f0400000000085351510053ac005200000000", "53", 1, -1600632552, "eabe5b2d77912c095e58ec68c93a2b3a042188fbfc894cffd713e817580745f9"], - ["d2a5c86c0318cb8d489722d94cec7962c0ea611034094a59c99a60eba79f83914377d3a0470000000007ab6552ab5352abffffffffefad732ee75c2f8eb77fabab3c0f96dbc35554dcee43869897baee0f1355fc1202000000008604a48f70434b7a0d95af4671822f0ef8f703e9f7d52c60591ad9ea9e0877f332fcc790020000000551526a526abf6baf0d046f694c0300000000026a52ccfb1b010000000000e6f51c0100000000086500abab5352acace66128030000000008abac51536363526a1139d1a202d6592e05000000000000000000000000268b72ff830834d97f76c0fe96af7d1958377d74c856c5d80114b3696c498da6d4c076581f8bf4c5252befa64406611eea2f04459e94666e3ff7b1c5d7123446d3fee985123e0e2cf2db4c006bb1d879c8b7c4e3d3267feb682d9593778788b900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e21dc571b39258c67827fa2d30ff1891f73bc0b069e590fae06285c27fe3dd7c0d41de3daad4b4e2b0de95d8016b367eb2c0d6205dfdeb86e0bfc73b3fc63859a65f403802168eec53212c7fe15a061565abd4cc866d2128163bc161bec8fa098fecc809a3f8fe2ef57623af3461a86110789a56bcf4d08d8495c126308fbda7031bc86e5e8b7f1dd8f3bc87486e7d0225a07e5040156ccf06d9f01a8e3f36bafa031576abb4ca27252b8fb81adc417a55677af12b0062ce24218b98a21fd68ab8ab0b00dc06a64c76b0fd4cbc7af2e725e725a3441be8ee139eaa90b1d153adb182b50cf717d459b4be4f7ae1caa187cf8f8f7f6ca08f430b6e4fa5f1714b304f6633032a41d99d0bc3ebe0d3b22a3dc65d980b9331e049b62a27dcf63437aad557d6de032622adbdfee7e4991265d68ba634f407fdd835f263b98301e2d7bd12cdb739ba031eebfa7eace374df03a0b8f36aaecaa550ce7923c045acca0bfb6333917956b8031be0c6bab9a43b7c8dccdc1439d9b7308df4b8d69963360e1ba9a7db3a454630021214c2cd4beb774ed345a6a2358a09df6f6ef1f14800eabb939bd5807411b238024c861222944e0c02b46b55b02d95e31c48c19e1356bc6ac5fd5da986f4c828c9eb7026b1cb34d36734c6c14b49f4870ee3ab8fca04efb6400211c3e67e80d2bc5fcc623a75275036669dd84e22c5ab89122e16350eebeef0592b5e6f11ee07e9288a0069d0410deac1b6a9453e18ba4a30e2b1408025e24441319acf9e94f859ec29e416ad4f8f2d805c17c7cea78bc1b4dfe9d719acdfc9231a658e0da25cb5dd9c25d2e226fa92c40cf4d5edb2d66879420d3881fd3659fa3291a5766ab9804daa6ccee27bce29fe7477b3d0a272df473e0f6c7bd68571f14fe0780b1d8621d2ff675831f959ee4ddfd705ed584e1e6ba7833a47f9f23dbf0847acb919375cc5f290ceca05fd499c13548dd6ae973c4c00b68f0da5097e058445f08511f4e32c943d0880c5af98270258efab00889c9a0f29e73df80ce404277c4fd3c9069ef6c136cd65f1faa477b41df5d0165cde39c90d5188688d91de49543ab4134e3570f9ffcd16f1ab63186520e5d6a2cdc9a7cd1c18b5cefdc47d5ee8c9aaeeae8d7a406da1c17e16b0409035b0f099a7ceb651711ef8491373ce2c22cc2b180ba6332dda0603699477621af7bc761710f24f7fb7394dc52298c7847b9be2771f1ae6e49741ac0599b90a9a3c128fad4ff1f7059d553ec7b46e566bfddd6736fd9fd7078425f20fc6fbe7e27138cc6193d2eb82c3290ee8a9380ac54c30bd9b262e328f354337de85170889a1f7e8404d168f9328182a2aa127d1e36a00d853d8a8fa955a8f34e44dabcd701d04870f8bfaf3763af52a950763a63486457f6ca9bcef1995945702181e64a2cadf601f3f1648abf94eeab3eff752b506f81a13e868da1b97b979380ad1f09a25299b09af64e2e0b227fd44c5ff0b75a4717e910746b486702ec616412316004e0112e85b76dca8b4e875019286ee930cb51ae4694e3821acd81c61d56c2f02542057478b9204b53aac1bee0db81ddb0b620f8e1fe5a1869805a7443af4ea38804edc3674f2dbf687c28f265f99b9930723354417af21374b3c275115c8fee6b3e43a8541ad78a70386376a94c657e37e9c230146ed2bf204b7dfe53b794fcb397e06f0c221f6d42930511e9e4246e97f2c7a0e8e36c3cf8cc11771f58db257b7af66879065acaf8683b60f739c0c4151f48c1a608ba9f95d58df1d678dfa9e3f857ec09fbd7c23c6ecf809a70b2ed8398fa1df1ecbec4e167d1ba9e48f677a09f27a532649f47fb75ff2859a6b78b87730905770d8ae8af219180b7bd7c282d38a01958568f9e20884c2900560a01d0940214693b523b5899c04a8f9bf6ee693f69535fa08aa894440f085c0adfe11686284735050336af60da82324f3cc52805cefc72fd084c69c284344951ff6542f7eced4561b919fa780acdf11f621567ce5cd7d9bb0dd710a06cf044ea0986fea3fd14f6764bbcd38eb2c6ce20ba3333b4bd9c9fe60d41e567a13eb4476be3555df39527ce7deb78cb818982aae14e7e500e15bd972034961621ec0c6550d47c83dd1d0b62c4726ea1b38f1a54ac5677bb9ba3311656edf5a6f442a7c539aac6cdc59c029e109de58f39e8095b66782156763057f63cff17a6bf3d3837aa513e1375939d6b6659e8a0ef562aa6e47564c8fbfc68a1705422d5f778dfbdb7ffb81c5282de62239bbe82513273b1ea1595c03c68e45725bf52d6801000000000000000000000000ea42b8a70d852a41a8761bbacfc1a74d723206d902640c7e6735ff3913293905aedd89bb6822b79a7f5e1d7bcad9839579091e4f1fa2b6c1ad4ffeb172db94ec292faf6ad3bf041314e15082df994046ff664ff2a145fffcdcb05ac098c422d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c62fcf62079ce0b3f2f198411f6af1c84eb1d04f686b66e769386e7d7f58c0115081b5233944e9e90a0f7fc9fc491e44ff123a80632ff95077dc6c81a466b6300459993e494d83db5f7ef44a76eaae5c8d714193289af816e377d749245be5101b007ac38894494f8dcea0502787ed0932c76bf933c96c70c4504254c8e689bf0201d6acc787c9366da0bc7d72c73314bcc7eb526367cfe0718ad0db5c7747bf19032d4d4b2dfcc89a33c231f550a64cba645f160ab523e26f5052f4b81ab45797090b04ace123ddf58aba6fbbb0ba24a938b00e006e390c0e9407a4fa62fe4b8fd3d62c510811fa074bba5d5d5c13ef3cf1d087278d28ef04eb58493d00caec5f5369021f8b492e740fd53e7cf22cab72ceca2f3d20ca0e538f26fe955896663f3f9d9002301a01a29585f3eacdc02510246a50e4c5f6bfbd567ed8311796f821fb5a1f0503216004955e3c8202ffa24028ea17326b8702b59c8221a327998d3faa283c8c39020deb9b87748b0dbb19cfd281d921a7a19a7eef768865578557d89d209edb761b0224571714bdba146bfd016af1b78985127b532f26ea234032d4a8b68bc4f76d5dffb5dffa152f99845a4e76784758861cd61a9f94c76792f39fdd45ab359664a33cce0e24bfceb26193de8580c10fecdce66cb7562c66a5fb8ca5ec71963199e1cc4b8ae8cc50b10ffeffb67813f85f39486ec4863401d8415feae95012e02cdce5503ca24d2791eaf81e7ed72df8669c82776fb13e97cd0eeb3f3126cdbb1cbed3926d32660d2c8b41eb9d9f132ab30b3b9d1fbb9e08bb2962e9623c7d27b4eab11b60f2d4f6c6da4b89dbde6fe02ce758ad6f2e4dc2fdd17152548a948fdea57329a26f91857710d796cd647f767b367b7f55937a91a88989203b20ce493cc4da665c5149240161c1e6038e463478cf286890b727a0a8d85d62d5725225d7b064dbc1a4c84e10b74409402b19e3c7c2054ccbba294bfff4a2640ac16ca2a4a813f3feb4d1d535b0de12003a984435a024acba5071e22e4758fea855400e7872fbd438579b9d29471efc0cba22a8680fc1bdc8746f4e1b5fcd278c64c938861ecfab64433ded85f99dd54028ea79c53b8e960d4827f69d80924b550af17f4996a819d7eeda3eca414fcb89142916eae38899f3c299d34956a2d234229b7867ae67c4614ea3e4bb99c319ca762d984d86f28ce8d9b5fd1c26a437c842baef5fe2ba682154db963bc3c24d0533811d5179c1b2f1105e111cf98d1578eb3ad90056a7dcf958c1b0ed7b3482fc305eb958abb797c54e52c00881fe8526c3eeeb06e2e54fb586535733de1be7db1694f8e5fae5105935b3645b079cc081aedf25a41b714ad249da5788e9b4021cf080f3eef9af9af324d65a253867fed418eb205c2e231ffa50109a0be9e361c054d25597786b47317c740fc57a5014b42505493ca998faa2f932f9632d21ed270dea44c636a64c604d058fc811edb0076d1ebdf75a18ca15e6063ae1d0b8a2c1cda6c29722b929b4f63b730141893654d0b5611ed11b386516900c1041868b9a5386156211a5858a40272c0726d76310ec507daffadf6c5c05a06dbdbdbfa46f87609302e5f5b709888014aceb1f8d279e33f89476eefc2fdf9f715b680de4859f8846b917c2fe290a0c1219648a2034d934f307a9043b4195a5d0918b7a154cb555a031a15bcc3a799290342cf3145f61b87cc407fbdf5269dc424dd4cd037e4d1b70d1dd0038500e385996afa9dbb059ae55f9c8622c9023cd1f445346ea61742b65bf71bc62e29317181a9b4e38690f8929c32e3e39b5b2c030e43cf9501c1fd7bcbd3681b8f8f7c86de5ebe549dbbaa098f620e3843ed428309d2f07e6025d60c0b38b5fd5f6675203e4ae10bbe8a42549ab14f136f9a3f20a04c607d72cceb554398e23c85535230ae9e87aabbb6c6dc929132f22a82beaa6524526c9db052fac38fd0318d8bbd9e591e78930ff070ec32ea081b142131c2bd144f3f8f24fa2799c7753944b9f8ee950a4308972f13ef0fef9182f455ad0993904064941a43f3c53ad45fb9c1c9b68bd81f3e96bcc6bf1177c6a0249da1ba979919612e9511760a6dbbdcbb488c024c81bce5fa8a8a7985bc198c4841f7d0a3cbdef848d903dbdf909e805b0816ba2a72c1ad75411cd58b927fdebd0648bcc451070435c0dc06c68a1395fa8ef6e2c022dea6c66f3d82b1977b5fcaf724c3864715023347343869ced02ca96df3e78a4a82de8bbf545a0f04cfb28a252178cfb4053503b2a79eef2b6ab235f8492379758ecbc0a3c90c90742d59e017e483d61c05811c98494bd830562c73e8dca55e0fb70a4d90ef99e74cf8f45cc4e79acd648f883bb5e70e6a3325538e75f04190cba15a8dbb45925855129b1a3b9e56d127f133a3604da8d7a4a3604", "53", 0, 1749204536, "bc4a201b4f0b7c643c61fb80adf0f54f4887c70751097b305eac5d2218fa5fa3"], - ["785a8333019dc51a6fce400cabb92ed5a02e98749fef8f8e55c0ed73b675923043b3489c91010000000800ac525353ac52ac8f4835e5046cd4b90200000000040063ac0067c8670400000000066a52acac535371019a050000000001abccdf5b040000000008acab6a656a5352009e66c9c900", "655352ac", 0, 1391260418, "afd80705814b26dc1cc896282c759cf3be490c585efc04ee0e079ec8b4ad720b"], - ["6c71e512046ec85212ac8c1871af8e5a02365cc3a43d5caea726b3de8ecd64e081d0b91da600000000086a0065510065ac53ffffffff72afbb57e6ca32c04d22f6b58339589fd10c837688c5efc26bf9768a2fc06271000000000251638b33abe4778445651032e7a7f3cd745c86c8f53e156b549d8eda290a76e17a4397a1c5fc0200000003516a65ffffffff39803b5e9ed34df487eeca63a6a9abc0cde5a288b96d8a5a382a8117a17741d102000000090000656352ab5300532103dc33017fdd07020000000006536a5351ac00bcad61330100000000000000009e7aae01000000001e315e86b332cd50716d81029e4c48a2c932ecb2d8b5a26584302d2e8ab4496bb3a2ab26960e5e09911d99c4ce5dac5075156a629ae637f1dd02f3b8407fee8247304753200d0ce3ef47ef143dd0e18ca3198a7c4116bc20872d13105c4075d2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a9fa4990aeae69e92f52a206c32e80f9ba9568ddf45bdeccf3d8183df5f1d38a6a5ed824932ec42b18a23869f79fae137f69232061f89eef510f0b2b8765c8cecf69360954893349d1f8f0604cc810fbe309161fece7c34d498774b70d536b55797726a314709786c3da4a09b3a87da69a96404eb1d2e91296eaa58b7c473e80328deb064afdb201c43d69b3f6564e2640e5830ecfa8c8188ab705624e65f44460323762a4ec3edc6244c9898e5eb053c635cf7c5ba72d3006c69e3d95b952ed9380a2fd9345be1653a50d52c523e999a87e93f8ba613821ce299ba566c2024de198b23467a661dc72e4c4b3c5176a1fa01e532f3aa67322d69c95042b0fd5180befc032876633f96e6e94d2a53d1a3e4e3aaf2d48c85ab844742e7117b0f2cb14463b4032f86dc9250e19a747ecda56a8bee0750dccf751540b51cef1906bd7daad496de020ea9ba9c02fdcbbaf15b0498805f469cc5bd2ce6e8add6b068f77c5c85b4238b021575acf1fed83b6973cdcac419e0392cf84d92f0cb8ebc76ce10ff97cc1101f902294e901ae30fe0cece85ac0ebebebd798cdafdecf781772af35f1bfa1c1b3c48edc2e2c414b849be6fbbcc35a04f428a293c94bc428e5e8a325fdbc477efc8e95e747cc574e3240f34bfa71233ffe72472cb2106d691f842bc31e8761704558b2d5068129df322274888dba1cbaa7d0eff0e20b34f2bbc1f925c3090d1b74296dc77d3a0997dba873eca43ee4aadd679cf21811545a4f3d64b704f3b1e33c9f02de692b5010f5f8720950d01d46f1ecf843a976d8d303a2c5f5fcacb33176681fe58d30d53a793ed33fc278c1778a91a68b66fc9e4fb4ddd84bcceca3e26ae66bae599bb5e43fbf6595a3f8fb3cb9c90c700de0d29b6cffc885fbedee256fa2548793429aac944aa1e20e6503231733d8de265461546a23cf8a20bc8967ed83c7a5497af34087baeccf5bad4767a3f999e94553f6c8651b9888e49ab39995bfdbeb2d08743176f732c6342bd567c10506affa9c4c1518582c696711e52c60cf2c7a6eabfbd3688017ba462953f44a7face6ce05f871982536f786f7eb74509953198cbdd7efdfe571134d80b3f378afe6dfc393f0c29acb7f1cdc0e790b5ca40c9c4c04b42058adca6ac898b09924e17b19306b35216b032441d5824fc8d57f88639ceb6b5be3d117c92a4d9ed54372eb077dd42db87436ab540f005f7a65b15c6462ccc8e936551f559d4fb8ca780d67241dc395b34ac53933c114e78358ce37b191b4bc2b20c7a9c2a7387c447d9104e857c75607301b190ae212ad51d0261c9bb1a5bd50479c75ad6cea5b84102289fbc3cdffd89109d0133e907b14cc5a2c19e6620eb56e648103d671e5b000bf93cee41c1685eeb3dc342fef53de8a6e51586244950172824757f91d596c97e31286fda509154d56f9b05b18e817e957569c1b577ac678540f68189c4eaed80fb5ef288712eb12fada91e72eb9827f41ea5051acb5220740c7fcf78765213a541929f8473b6eec097259f2fe262d5bbf3123cb1685b4114185f6faa1ca9370b1ba8a9b0cf3148052c37d2f87d1014b49c4032e42694aac21488d2d15ad8fb545dde50db5c3d058c01f054cbd0934b4f033a196411ac309102f39df7e1026e7664998718e31f4222c890e2ef9549c9512af4b573eacae1c100bf8a8ddd20d7734e0c07ca35da3198bf7b6d496f885de7e5a5240dc399e4875da8798253eb5410496a77d490958c9b36ff690c528632b81c194c40db6493aadb6ecf26c7b352c446bade5cbe6f09ec103c59b0c30036c1cba285518064ae668f244dac240ac01e70bef70677a8bc06f63e0afe9c577986335452caa24101743aab6d71586b7d26c272fdfc0c82b9df5c120b3f590ca94f257a03615df480c98f90b9af1efbd63a0e99169cf884b4ea4cef9492e4db8026ab3d7de160926fe2d1410f7ada2436a37c83aefdc4ee9e60d5a40f1711e2fe62aacca0acbabd1a7d2901cc22c9dfe97df7d223a84b0c85df1c0919619aaacd159fe53002436a8fe2157145f3bc9f51bb66986cbb95280b2d017fd092484da4cf3aca47dcc0a16f532b2666d8e272e9cd97712cce063948249d59f76a48dcfffebd7e0767fb8524361d49b288a1c51641034a8747b1fce87bf5283ca0416c6f98ef3099e3b0c2db0aa2a8edbc1b93381cec8bdbc88a1558bc6847222dd18c6f0bb6af62a4b90b9ea96f0897d1afc47ae4e2a0fffc37995abeb442a55c113d291fa8eb370d9a5f46679ac2e223df79096b6256840b0764d56f8dcbbe7e91347b7abe1f5d4fc072971c63fec0197776776d859e3412359188a11c1f6c6324710f574a79a937914d1e71daf3f19ff5bdf2373d2482b1790260cdd45cce50afe069491e9dc527c82fda529430dcdc04722064068204", "51636300", 2, 1321403412, "f5bffa690843838e128b8f5ba611fa1a3391a5ff2c966b5275710eec092da6cc"], - ["665e13680298312a0555feeefc3372316536da4b6238d28ed12963551fbb06ecba622cbed8020000000040e1e6b9c9fccea6e7bcdaa9aa97526d00324744a0c427a591b69dd4f1fad31df901c0b60000000000a7fe0e1a018a73a103000000000251538fb522a5028f389305000000000000000000000000cd804e7d69634a5afb456ae15179a229355cea576505290b4aca8ce93f829409d6f59d080261d236e238e8214efa8da10e8ac4376f80a4964080d905fa1b83e55ca03540752e5a44f133e7ea14defee523afa89f974b7322acc61c93016ec96600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c53a759d9ba2632e55853f1499ab228d1bd669429c7eb403b14a834a89c2b470e4a52cb4b128a315a7a1cf580d0470b7cc36b557838202f5f3ae87c04251be1502c389036f5e9e51cf4a3d87c08a76ace209f021f88cea6f07466164d9e65295f2c41b7e544fc8f76570c69487e0eeec12573617b31eefdea4dfda6af61a18ff022a81a53cf2670d74efb2902e2b7f1e807486f7024c2c37321955ba3a9674392c031a375fe3499df25f5b20af4fdc1a004ddc8ed9bf5a7b089a1328e81d194492070b1461f4ee87a2fc18df2a617289a59f56bf01b3663adfebb66e4ecd9fa2c847f32d99e7f768e295df9cdee9611e0f393cbe7289d4b8e12c12cda9d3cc40e79ff10320bf2d4b8d334f195aebd62dbaffc0df4d36c0617f6764088a5d56e7b5fcd18b02124637b8cbeb18b23a196780a5ec2e188f845ce7834b0ca7a81b6730e9a3f8cd03053a31e0f45e967264ab4b0635d1b7c94c2b03911dd6714f0ac7a2494355c947021d2817905c1b013401a8edc40942330eea344c58d3396a6e94abe6ea046bfc2a03116d9d56d8b980652c88bf9731f761b54428901d27dc4247ca033fcf2219470bd8cd3b596f6e7e8ea46af077836cf90deabefedc12c8b9323278091760e46d04f86aa54fe77c55864cc7857b473a8861fdf4bcd99e31a5446c511a6d5a47c1a455f6de2223b0e35caab824fa45d7748c6a7c46937f5383b2d6d73a2076d25fac8940cf52265e4f93324354133d3d8de46b2b531434c92bc09d559f9a6fc512a303daa12d694ebf17b1eae49f4673ae89b8cb4725edec2a2399ebc0c602a28a484fb2c937aa7361e8fca1d788333148573a6587390a44ff34f65de6cab195315d1fa153ec6504599a1d60c8403b665cebdb9e5f71ec6eb7413ed9eed30d58ec0ce2f4ab20c591c3359a3c1f2462d7c78484481b610c3c7cffa9a8638ae00b0d4433a9b111993d1467fd96a3a1de6146fc4ac7e512c4aa61bb1ce7985df894f53be854149fe0891deb5738032f2398b5110da946a3e745933e12b5862b2fe08a3c5472a24b579802128c6a22031ab3d7c657038ffa82bec9c5c0eaf912465ebbcaa9b906f2b035133563afa2a55588cb7cb480a19a2fd73ee2d068df9f8f46c1ea10ccacf655d97a65008ab5159b03e458ec440c88895ba2c605783a2b0dc9296e94ae7c6e2d7c524a4ee37af7ee0e8c6b6f331ecf371fb346a1eec58cb96aa28cc3f644286140b43894842f2361e9c24c73edcc15717c6ebbe7df72169247266b8a4c44ce3118cfa012580d26892c23c64e6f910127be5901a508fcc8bf54aeccea32a596f6cfea5e700e381458e9dad3562dfe5056cdf0f14eeed40b5841c4ccda442a9621a260fde99b32bf4377986c0b5bed71dee6ce91d282b76c2e3476a298b5669493e25a7bc05ea922f8fbb64703d87101fa8b4a533beb6e83e865a51e351e7e29fa81957b81387962f251bad050cadae694d4ae68828dd5751d89bb00aca77af63427968cbff8a2af921cd960ccf27c0128c2a8d83aefa05e3e8db0c578c2b303a5047e5e361c33f28a085fe1a03bfc6b808f506c298a901b2e99733e4908162a068c79f27838069360984c0022eacd9f245fe3cd82110eb3156f2e4afae73b2d9f6b57a22f8ea25126e9981b18f4833c18fb1e34d894c90621715c8ac356f7d05fe541c54e191aabcced39cc5f35ef3357babbd3605c062b08dc836aa7c6bad543fa3eef4a8922c2d31ffe676fa375a5f204bf11623fb82b3c140ebe01ea1ebd2ca9f167cb14bda6fe0a8a547ed208959130bb04f7e81a1d28b505dcaad0ef181f0b64b78f5b9af2b1bda4ae17d5e1b94db680cc8461338f73c651091a32b4e5cdd36bf7c02728e7d00e53964894b97ef29309eb6e2a701bea4580c7c9a898e261de04e4db43f51d527b5dd92a98011eda8b4bffe0b6cd64c57210044d9924b82c467bcf556213b384a2573dd0a17b416ab27f201457ee709d12e6beec0386007df0afd948d8e6f7ebb8b308f4dc23f59c5198e84fb2442ec97daf7c1113226eb274dc487a11665b2b3562e262be2c65462d3f7357792d52b6a86cdd56992c63ec45c86eb293455381e5f406fef7bd4c3d6961f7acd84a9b02659ba288a408ff8ae9b0abf0d2a88102930e7f5793791f8b8e92ec562c0aa8ae7b389067a0cf2b8a58cc34c14213c617f5d27d15181a3df654bc07e68b8c81884c8f0f9f72d369b31f608bde43943e434433041c2f5724fa1c4f1af29b7f1efb15505eb77a4dd182982aa3fad145946473af9c6c3b0aaa8905000000000000000000000000b8507a9f012908f1c2896efbee3d9885a65386429e41413a3928a88f7333bd2270d8b529a0c6a9947c5f3a95c60d8c735dcc622d4bb6a11493ebd9d1fca898736f86c17937468e5678464e967b7479a39f5ced95b0b6e080f4ef6f985488e81800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a8134e9382ac4c15ff1432eecba700f5473bb644a552fac62fcd8ca77584b336eb079505d148742fe5a134740f69bd229f352b096add38aca4fb83db94cf87715eb83e0f168cfe48600b2f898514e04ce9157788021d49c3b0a8f6613e5e505b35a1dd9dff53dcde1115ff1f2f5aad978eba6138a075f7d0678126e2eb79477b02007067b0bf36b49428adbcfc4ae167818d9f5b86725f73c5b4e5f28d6d3e97ff021639c5ab252e85bddef737419c4cdbc507f9be862e19aec448ac581fa1944e780b132f6327cc370ee9bf6efccb567f5e7d5312cb4be8b8d3166b2e605ad775701b1fedce0a4bdee6b04320122107408207e4af37938112d886563e184d67efb4b2030f079431cab83b5b3f7ff4076aaa873240deec73b7aff16a9b807ff5defef69f030cf29052ab9259ffddf19d1c6f63c28438cbe131b33cc53d3772de25ccf4bf2c021ec3d2c51193c1d736773d35765c255a47d233d86108f2d37ce6bb59fd7237c30206d0b5643ff5ef67e4b6523b781644b32771dca972c7df61e182a1f5230d14d4031d18eb13111bbb3e4751354623d9f565ede90cc4fa386fdf5acc42a64177551977d4e761d3b8369e4db5334847ac9c0623fcfb4a4d95e92d592ee12c2307a899727fb9c05b3199f0ad6da181e96557f0137acc7908834f9eef48cdef0040b9d43396d8dcbe3ed6e07e86a9fe6a08b924ee1e0cda10e768e5b6a84e86af0e3651ebc980dd14292465aaea13a52687987a8257ad2084bc3fba6c44fa9208c2db6e124d79a678951f49c3177ec75abe3df212b55f67aaae5ecbb11af87558c787b71c57b7703db98f1a5916a7e5484fb284f77513e6128fb686175d1c82e3e2de2e906bd68557d3ff2090cb27419ad297293740626a99e3083657399e6adc8c17e9b82ca713c63b2626db8f6f7832106170a2a72dae2ed4aa6cd1db987dad4a0dd9a7467ca3f026857095617c1e28d09a2e8618d9feda0e5feb930e64a68335efc1ee587073e37a11066230b2bc2f9b2fc7e2debe6b95e4ef3903bfa2edc9be7db16b8c59da612fed13f17633b0ebe29e36e16520e6d76678f03ec1aaff3882af456b6b3fef4901673f41c0987d8340417a793bb58ccddddee699cfecacd6ceb7d21b310ca989faaddb5e76302f8dfd961b2a557cb26c495bca15a6a1d38ae26ca29a0f552083262fff7dddc9a66e0470364ed74787f620979fab2535922262b583ee00554e14c3391fb6234693998e6a6a92a81731f44ce09edbcb9fa82f08b12281f282e69f51ab36379f693e1ef2ab5ebadec764cc6fd5f3287535a8371095861c8cf2db16b33647cb5d38730268c4aa7f9225db9d27e2432e9e2e6702e90410828ccfcc7198c9bf30af6dcdf2df37f3c01022ac79419448a7abfe6eb2b9a004f92cea0b348003545fa08d521a0d1e1a4c0512a6f123fa11e683c150de9feacac27692f9cab8a2d34146653d9b98266b331a1d1515726627fe98e19af851664fcfd1a2d05f23e7b5df9f636e1ec443fac0155b71571259a5f9a895b10af6c9c161e45ca2142c8c6f1c1792a42464c4eab015569568f04e7139af1df38938dd2c6c4066daef4141f2c7f13fc372cc94d7db8855c1995b627aca9950970163c1a283869731b616285aa9f4b1e1a88e3822add16d5e3414aef87284922ef3a15217cfea4365d2666edf25bccb5edd88ed038ba72398817dbe4c5f601ff9dbe567927b970a3f3826543602c88e60234cc7da886d7ed57a93ba20f7ceff870c5bd9858272f122bc144f5a22269b851595ac0572ad5c5eff293c7f0680858c30203578c671e1988bfd5efaf5658beeb579ea7c421826b49d1b9d23ec8c2b1ecb270ab42b7d2b981aee460d692c87bf8ac6f66e72af6523973b1c1535750ca21cc1f9dd98813c94ad53acf11a118c752e4d9946006f157087f5cb04aa778d36fc4bf8fc36382b859edaeeced7c7f12ed04bab647b6aca33bf2307a742191ea6b4d686fe02502ab92cf138ee0cb2e24e2560d3b8f8159b8d6c5a387710b4d5a4013bfba681970c5527a8032ecf0b59391a59789c7cdd146ecb04f4576a5f1096c6dd5d3ac7498b2dbdd9045c92ba2553c3c30587de7fb1a978bb0d7de62d650ef8cdc80935147844f720472356721e905e6bf29ba37569bb3d3bff6431f2bc1eb89c0b4072f066eb9759e5f4241e51247de398d09c3f3c1eb65455f01fac9eb3651b0ded2ee0aa70d3a795de3e668a448e85d72ed03b69acd622cf1dd94abc1d2d76d5404f71e58e5e59c2a23f78c19653307cb991f634391f51093adfa673fb0d1bd4da99c6273c059cee2e0472b0c6c07040c8be46b11703673591179608d4944dbc8cfc85dcfe208b3c377580e1a452828894ef52a2d76faa1d11c73a77d6901bb46662ecce4c2479caa962c7d8545daccad6ba0d", "6551ac5353", 1, 6529874, "ed8d34bed38c23551dc44b9ac45d8d06e9b638be3da34db702f3433d7c5e36c6"], - ["7c60923704f1ed3a407bf3366889aa1256015f1757c58c351b1f5047d8b97717c94a9ff222020000000965535100536553ab00ffffffffdf0abb9dd92f46363b2ff8c40eb9a42ac9cfc2526ff3246beb8212389e1137dc02000000080063ac6500ab6a5183959f3a019c18c321b6ddb231d9899e5ac7a44b8e032fdf1164a007a270f086f5f24278010000000465526a00326e5c7852f912ebcc0947e1e906955e88843a61fa14c0854d3613a7391b9f699f9ca92c0200000003abac6a2621853b01005b6705000000000953ab636500abac536a42fc2b0a0100000000000000005210aa010000000051b5dac1508d8b8ee853449477fd73d96f46faec14ac5186172b3a142a3165bd8757b6ac2deaad84e59f7a4ccc203d1dc6a917653ceeddc28c0a1f96b3e95317fc2aa5fd4c2f9ec58859c2efff063d2ee9f6f641a120052f93d27667d88c40150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000026dd6110f02216ef5e1f17aa074f48ccdba5ca2b4b0b09032a45d047efe870b03cf584350aeed8e5976b344ba365763c852d4ab6d2f01b36f0e1dfb6e5077f94099957f2059b47a05a9163255439997d42339412ff408565c144fbe06566702e2b49e6e1eb784c4307309ee4fa198b37da626b97d5b7e8f8c5b5ac63e88e9f85021a7177432b214346ec7e4a91819e01cec08cc08d024d95449dcbba7995cb84aa0302eea3906ee5a78c59b6c223ba2165a56821b92e1f123149356f69240612c13f0b1c4dde516b92e7bacc7dcc8215a85106edc1106d1cc6c1171630ffc09a5a158b1f9bdaad86722d1e06d4d575146a38b070e8645644ac3aee511b3d6a89c297f30214dc538d50b9da1d04375076cb4ae2a0f1dc0eb76e2d47129401ee6faf29ce2e0322f033963b6028f36509475125f4fbe6e62118594ec0d1a1741d82cecaf6773b0211b520eb2f072aa74c6b30d7a104577d03106705ad59a64a6ec84eaa78b7a354022358e09b0dae06a80db5537ffd51006fff196fa519489ab02e9ef3e4dc74a787021be2006712054b3db7bf0ecade45ef80919c72c7f4fc045180c8fa59796a9d5ccb5163e841594f24ffd0d8ef21fadf97c420047562c982a23df0f96ee2e3d1f07a9a4c60a0300a4595a2ddc7faa7be17831d90dcf33fa2b95ea0f612288c01cfcf542ebe2007fc245ceba9b2aeedb95ceca6aacc45d0544a47bdaf2993e82eb4eb6f3cc3fc742812232cf67abf265e377050fe8fd2c03dc768fa5ecb654518316ccb6e2690ab22a0b60b96ee34ea91e214df7604ad4077cd752b7c6fe3e91d2f6c3534cbde537ee6873a199ac1179de8e792028c391c1ddbe4933fb298c6acc6a0af9d0efed11bcbbfeac8562ce63155607d33c2f10bf2dd3555e130cd22b642e8afcf19d7ccbda8a536cc77e8dd03e8fef83a8b2c4109438b6aca246a396dd449ad3af37c227182809fa047c6e08dd6f0d72958927bfecd5a2ef1ed43fcbb94ab956fb1d84187cd7fa741190499152286660173318bf19cc3b05e24328e02a92e2a85159e4afa868a26c8892111a7a2e5ee9befa29f661274292a6870f8c93b1f37e235d6d7ef5c9c8348a27f97e073c1eebc4a1389ee26127cbf60d4873bea4a8dcb3043cdece4c56e6a0bc7b54de203109ce4549cfee4d4bf6195169d46f84e4cf1d884bb257d34bd250e28474c85cd3c7eeb36fc7d26915d7633a154861b4da5adae1fdea80b00e5327123e9b1633ac31492e5fb1fc36cdb902f164bec86cf3f0790cd03ccd9e304b4091fba6cd16e2232dc9b68816deefeabeaaed19586e52c898149bd02ce4c1c84d2e54c7e64ff8e4ed68dd3742a66d7a9f0900b1e54c6aca4a62eadd62002b2ad8317030e46ed7f0d68ff8401551b8e32260368ee0b49c5c7180b057d9fe4b381136ba350859efcc09e43073781dd67b72320349522470fd74d4409754fc25f14d1525a7aed3b05d6ccb3251c92312e8eb1e5530d94f73d96de9ba0e344c936d039061e6cb86202f1c08688622b1800f03ca899a5d6f1c0c539874c8df1d6fdca7b640dff2a19e28a84e93d9ae62c94d2b11e2224243ebb143c5f7664aa218f9cbf6a662c1f5eebdfd8dbdd8bef5a989b5e0dc0464e9d174a3340d2688e9560d5b32ddaac8ffd6e5fd50c5f09c184ca30541bafbfd57f5219ca0e8964c952711b94b8b67297ed2a8e3ffe3a24a174bc5540a871b756d78608a9065b910043a69fba68f63c22465b65d2b7b897b961879e3fdca9a964400093867022757e0f00ad16e220b0452c919c24dc7b60a7913763e88892637bff95c07e848db5bfc3c4c73416771bfdc3c94e7ea663388677e3c1a9be812023da495dc2abc9633d8e2784293c62ff83166be08b016ce8cd9f1c841f971e71f3d621ef07f07eed74045f97d646f05f56079274fb19212808fa3ad0bb7b17bd439c04c46d35e49854ce31dc29e18861bc07d189f79b642f7df77c34807642d1cf0bbdc712856242e5ff486c38297a9c32b855a4dd70f3a6562ac9e2288b79c97731113f1cdd736826ead24f0c03ee0aa8d080badec29f3cc83c54df4cd0cbf62c5b7e02af8b7e443c591f4da5cceb5f6fc5babf86e1608e93731b48bcd47ead5e70a153934e28eab7bf0beb5ec5cba676ca439fd921bdf7cbabc3f09d27661621fef4dca4fc84ffbf647152114cbe1a1bb24033257a499fa2b19e22765b5c99a478f3bc2f3fc531ff488b419656036778b2d91ed3175cd935c2e56297117540ca81c1770bdcea38ec96acbcdb32ae4c61069774399a7152f3162876d9c087f8e5a91afb8fc820c5638ec71ac9dfcae4c37bd57c30f0f1dd3964fb5547e0898d056acf9db596d8d7d20a45681b9d2f93485056f7d616a562c4e52ac81d62e8f7c867da09989d58cd2819e0d5d4b34900148006", "00", 2, 278787095, "595479af589ef54cc63e14af96f50d41f051358e067f29a2c348f58e0fe3d147"], - ["c8f86daf03cb6c51f19814577be2098519dbadd8eac3b158f12f4c06e35eb6865bb2bb0a59000000000200ac99d9189a40f7e6f356762f7c30732490ca771d2e919864a07f3ae644c26fb40d0f678b46010000000064644dea2f10c8600910fd8d229986ce85dea5dab185588c45bdee74a14d137e6330b9c1020000000365ac6352249a3f02bf46c60400000000005fa87c000000000009ab65ac51acab00530000000000", "6a6553520051", 2, 595599069, "8babd12203c4bb5fc88699d916570510937b095a98a7d64372ed4584ff28f3ed"], - ["cc3ae5cd03a9267fcb743ef444bec78a182f1a4f52c12da4cf95dc4ce01e69e09c4db3c5850300000008ac53535352ab0063ffffffffef3d19cdcb900b282453b4832b46de1ec2491a88d10cbfd1e1ab5b000c7e36c703000000075163ac6a53ab65c0d49b37a8e61cc5be3dcd60f5ce0377dc0d0009904500c91d03643181f1763b92c0387501000000066363ab00ab52ffffffff0490fe28040000000008ab5363656a5353abe9fe300500000000085165536a65515200fa74680500000000026565b8e9c0040000000002526ac88966a7", "53ac65ac63510052", 1, 1559386461, "9d87903db5856020793dc1be5b0eb458e6d16ef6ec46d9c0fb83a47c87e8305c"], - ["f0a3010303b86865f43feb5c3ad5055aeef2f33c0c4c7ba363706d6be8295ec2f4e484828a010000000351636affffffff7c735f8f3c215d1d14a02ba981de30e402f1d1ddac4f046b9a9d966614f029f1030000000951abac515265006aacffffffff0f21cec56a4594a011559b9b9ac491618e7c25c15463c3c84cbf7d6c4505e21d030000000463acac52d0d5a28d02ba13e10500000000045365ac51b49b810500000000026a638952a30e010000000000000000771d6803000000006975c99722381b64be93d59f0279f3755490d03dba4fb97862736d9e48764bf1417ee6d46012449460460bd4f808740444bbd403018a730ee407455d4ca86ac02492ae39579d07f11c4b1ea43563970f45df03f152aedba32fe1562d3c7b15390000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007cd44e17513299f2ecc2079a464d83816a71c62f0f5642e430f1376a9c98a3afa5296191c15d68a0a2019847d12dce35e151a0e2c0d5d7aa55e404c697c084aff089a4d373ede3454d657cb915a278b39178a74cdb537ba803243644e8377aefff2b316e84faeb0f1f956d7809a367ff19395b22c0bf72da3bae232a3d1d668030ce36625adbf69ba488f6e60753bdd00fbbc61fa1537de6443fcfd698d6a976a020ab797ff6fc059be0bfafb6bc3712f80e9a08cee7bbfc1f969ad6d2a4d4e6b400a19c8b92c150e97b3dfa5b7a1322edeca50ab66857b4825ebaeea870f17bf72fa25eea50cdc6dd9f100b9be47f8e979999d958f096d14867bc991090013efa9a802049cae8083c1872634b7337eb89ddaf337d4ad6a4bb2ac02c95a0bda7f73ebfc032310463e0116b6fad19b6de10768dda2ba84c43a03041d6039832065bdcbffc703043eb908d95925eae74dfa5a2d69bcbb90c1fc34a553ee82e87372c94f5a9328032b2b5ba8d60dee77508b23ebba980c4869f703bef1309fde4f52f8f3dcdb450303124aacc30c15a87397f1ee3acca7c98c014913ceb7ad4d6f9da165d1a9e7e7963d0c43e6909600640490e871e9b0f4234a713278836139234752f93021aaf6441062301014f2979c4bf6aead6baa37cd462f7322ed6e333b6ebe93e74eece28276421be508c6d5007c490b575db18107c8ac49cda3d985d8ede323ce74bd0e2fd9d7e5eb1300059b6c752123ce3c809bef3ea089c9bca82c8e926944797ad7db3ad83b62c5028dbbb443d3e524286ba686383124b7851e717599eb17020c432d54c2b8dc7095edde114c6408495f342e9590132e8e2880e35eb10e575fb8624678a49b2b12b291efa38472683e2e551d8c06d0602ee573269792cfbc59d0a08553286fe870bcc1f51ee1697f451a7b7436f92da5f5d4e07649013da86ec0ff779a0e1d11dac7f72dfd7b3d1f33a89408447b98b79dd3ed678c9d8496ced7ceffb505230b605ed92978c16ad57b03d9be5361ea106792c3d6826f6d8e8b36e69ab191207a3eed2a6e2de3a5413d54e721569e0a394b51b9e0abc7b84fa66544cb7e67615bf92ecbca6ca50fc2789ad1f7878ffeca0d85f924e6073baa6f9c7ddb8f96f2a30e937d04c864a8fad3192e439f3d9edce4e9802e9ea0020867274ea576194a1d8efc29ba7b0856e0f30da12b0fbdb6ae412e72a115a8791a46da48504644978c5ecaf51d604291d45612ef705819c66e0b3fd11ce659f42aa636817565550637fcccf78ebf82d85cba17a63be2e8be7929ca25021f3e97a7af7ff613a407e45b885f96fb5d95808e179546a7ae264029516f3bc717b4d6d4358a5f5192e104b6812d880526432193644e2d9c9e40b6f371b9505b96e70a062d486714aaadb29d2abb10e4c37450abdf438ca95d188612ef7503061e906e166d4bc7f366eb791b8fe2aa9f5ecb04642a258c618cdb803ef8fbf4f89f85c886f10c2a153d09f70c3349efb604a75dd7a73fc3e5449a8089718122e972a858cd8c69e141fb29391695a06b5379ea21a05c7de8608d04ac8b1247de934eded9d4502cdba145f4306fc5501df25bae612d2b2ca1ed203437cfedeeee96f6a5fb6b724b9585b4361dc295bda3905bf7ad65d8df778338dfe6c3def5353d431a6df12a7330a0189519c59652817940033d90cbfc296da442b12d011ad9b4beb59dc974f9bcaae2352da382302408d0994565d64aead81a578dd3adb44ca1b4b58cbedaf2c2855f8c0203314c0da48afff87d2aed9dc966d4003de4b0adde327118067b826db4babf7fb7049a4c6be746cb697ba465074da69bd91ad68aa84d097a4f2d0db8fadd8a089857f185976e98b20e3c9e85804f9aaf4cdc9a87bb0c5d91cf8eb0cc926c0e8fc298b645c07fc0472c84e7ca0eee5b4576ec7835f9d78512cd00361900b9a813efda32a03a242ad8ae2b90778a4d5f7eb910295deedee5bc4409b81797d9751a24a1f549b9d2735c8460059d0d7f433e8229a63c49535002780351a415ae2b77ae0980c313f9f0be22c625a623baab69f273264b2079709b4b3e9c7daff40fb71f3c1d4c9ae4a4f129c3860b23af8071295063ef21444761515fa1f5b1e9e03c402a2a42cc56e98dfcc35fdef4bf97e530a0f6e8ef798228442bf2a5b63633ba10637c5fbd9436a5ca94b5235760343a976731567cc840fa165fd3b181d7e8068098db4bce2aab6eff27ed3cef98a6b967e7940fbaebd67d16fe83640dd06ef6869a577006dbde001ebcc474753da64619d7f0e0b4369196bc1f1e77583f312dcabff0c3967c63be646a2830185b721f83127c89761735f80d1a8ac05ab30a50a15a545c044182ff9510768c882603e4877e5045d4a854a6ccab0ae92d9c80e4477c6eb4a12dd5474198fb0cc6190f", "53006500ac0063", 2, -1000506218, "f9d258fbf9fbc4521d5a3f854e64f9bf0d6a4295360172711718c24d0eaa98e9"], - ["ed3abb9001316b8865d11773b05612e79d715eeeaea4c86446cec5ccd8947ad3a2c84751c203000000086552526a63536a51ffffffff03abb71b030000000006abac635165001552b0020000000004ab525265d5c3d80100000000046a51abab44454df5", "006aac", 0, -2059530830, "176d14038f5ecdcd58200bc5eceb1a082db6eeb2994b2fa1bc7e477affcfad53"], - ["46ffeb4001e9365eb1ef05f45f65a9c5672080b857258836dbc186749657ee4dd2ea62b5780000000004525253522daed4d401bdfd5402000000000365526585e41dda0100000000000000003d815b0000000000b365a23ec123cff9b76ced883132c502473aed28babc4a64523c2f23f7aaf121b4da223fdd057cc3aa62d4c07aea086f456b093a8726a596bad3df032170a0045be1ca558ffe35aa60b6314f9be4cfa63f0cf81b10aa589ed278f093eb93d0150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084a5660fb132fb4813fa8c738622dea51c93e8d58cd81ddb72883141343c39c615ae602797cd52e964ecc40315c0ae85b0e94ae0b579799a6c18e8f0b0628c1f5a57b4b2c6cac5ee968d802f54e26a255fed9016e2f6656f7ca3e9598c6077d55284a5fc36507156b1c157614f0a6194ef2ce3341b5bab5eba7e185c704418f6031a8668d574420b6fcadb8e625a9f20f7b97439cee77390c0dac2386f8ee7236d0319ef1846e45d8afed7f2ba96a677302e18329a581c568265daab6ad3a7d1f74d0a2ba57a0100eeca9301dbad48a1121aed03ea15bb49aa02dd68c144970e48b87f1705879347f2f36cc503bc38d48d481990833dfef26117ed35baa7fd20e8ffbe022db1027c5aad3faed076c5531c9b645dbcd3a24a75c92f18f1222f983aed3ede02174ba91a42af82f86ca3510989abbf7879a9e44d6d7e17d79d8948d57179b1b20208e83c8db4a926412f69cc59cc419ce5113cc6709b2f59ce59475bcfa5fa3672032710bb7883ab14080e82e887e337fe471210f098c1316f2fd395ffee7125165b021823e74f3167343b6ead1097f95cd0a1897ef90c6c28a563d56bf3e09caf60e9acd0834f42503124d429d89f6337d31da32ed041ab4d07ab1117ea7e842951374416dbba4040d7d58f685e2c0e883473729d396b5bd74f7ffc67aaf7e7475d45750b5664a47cebbf5b16ed3b671cb8080bcbcf015e4b4ee62105df635ea499e444e4ca46d7f9ee85c7af2a0868360a93295062c9c7fe3f1554631b094b194b5ac03976b742cf8bddd8f1705c8476784c760f249c9163a22bae28ec86262fad9224c9325af5a736a8f65c139e24e332176e15382a454e52c92e216ee42de5421294c3b537daf3a0bf6bddef25701edc454409979489cc8b481de34f2dc87bcfc9dd6fae38f1d0e2a3cb3cce2998b5a4969a8198766c220436cfba39e82e89de1dc9c2de49cf66468f4f95de8d555fa24a31028910bc4c9a3407e89e3abe9761f6ad73972b8afe9ad0fece24a72d164e39251f6da4637fba9707ac891b7ed8e03cda16f88abeafb74a4f38032602bf2d89240dd6f9fadfd5846d26825c43914936d45fe8465c000e24debe8b8432755d7df574192918f7f4ae5e8c6bb9e670c558bc44006678c7428d1d423b53ccae6962c13559c43b13630f107d4ad838309cf1a0dc4c789a62ec43c524f4f7c07634f33671f67cb233cc4597e57684284656601cdc14755cf2dfed3faa9a57e573b3515edbd23918c5d470ccc0946b40f7ff507f383d26b8c3c2e532e1fe12adc1ad0386159b1707f0463f20a91b32d38f8f0a4402349704c4628438268a44c134a061102b26edeeee3b26a0fd4f194c6b931a4dfaf1184b83e9cd411c07f28514c2480744d0764c5af78f567691b833dfdb5031299e0b2bac752300b87a23e1870831cd6fb511450c8085bbda2fa20940ab284e41ba070ddc44d4169ad7d07e0139c6331ae9c8dd4f5b4ad787ac84787673445400b9a2ebd645a42cf2dbea82b88c47bf3b37c93cbc1e835b87242a464e45864be2a478e28437b17c76159775480e290b9fd01619d24d0cd357dfdadc1c6861988c40eadb8346a68ade7dbe812459729ddf22463be5144a19799bff76ff01f489f349f0b54b900a1e134c6cb115bef0e6f811199053452271b89a65169f561eecdafa00e75df625f88bd0b394ea1e995e18d1987124c6c93a44c0b6bdc71073a76991ba91c950567623fd8456b5ec2f435d68e8ec36517e8818f1c8710a64b65a95e8233852c4ba74f311693e10226be8c2b5bba5366b562b729f9a539c71930a1de676a8a7e466642afa67e230f684395b06214e20c049cbe55f34b57bbd45ab03411eb8fdcf4683aec744e71e55dde436eeb6ab7e51ce63febd17fb0ec3a4a406292fecd235a93f0a7bc22e4062fbeca438cb9d1cad46e6f65d28fbd9069695dfc4e244919ddff3f7ac8c283085b6f55c84b3f34b16ab834c17742ae2fcedf1ce55dbd5dabb6aece3786b0c95059d5bf146683875bb1cb320479920b3563fea86d7ac6f8101d4edd33679b27f1ce72e3792e8f7147bf8201851178201e89ece75e2cc53e773519bbe927f6151684b571aefc994d2fe36914559945d4b637510174c5687cfff3107ca230ab963dd4392b2e7a10eb7f685550f345f3ae844be51ecc8af24c824ce63ed1d5a714dd8711d0880ee0fc5135b1c1131c803efabbe1391eb9ba853c09a9c414812fed821091fcc482c209d18f9c4f8f94f9afa51b9ad8ce47a17996eb54b230955896c26f72ce50e5f8bb93649be349aa765bfda58db4163dd4335cd06a5b215fb2f2dae2319ca6ef6343892d78ca293a7930dc796cf93e6970d97c90e0125ad31633cbe39017b24ca21ead318d42187a5f43efde50eadbc432a6f201b60588041460f0d5fa20b", "530051ac", 0, -852224685, "016cbb95af8633735892055162236b783185aceadaf3fb515b2991a969db12ec"], - ["7760c4ea03450f9733d2d752f87b1ea87688902529adfda7496d5ab6febeedcc4af19b52f30200000002ac65b0a9aeb6b1ff1036747e5decc4fcd0eb9112e7ae0302c5538d82d0a8f40105177a4bf8690100000005ab63516552a1268da2ae6d8a2c9895fc0daa1f4bcb60da7406880ff3f17331a7599b9ee1a6c00148630100000007006563536a6553ffffffff04e70177050000000002636a84b8d70300000000085365510065516565ba3c8e040000000009ab6353abab0063ac5225a4c7000000000003655353115fa50d", "63636a00", 2, 388073941, "241efde580b050e7e35b13d26067bde5c6143ca692dab5690f9ce64dc45cdd50"], - ["c67c4c750343fcdd6f776ff00a1dfb7f3dc5e943b7e2fc0a18a1c212a6380acdf6c2f8ef83010000000852acac000053536a48bf476abd5e562913aca2198d9caf781a110040b179d65c008c880dad3e116bd0d61f6402000000096a0051ab52acac636a3793f5355cf8594427293af4d7ad33a7d5675a9cc44cfb3c1a00032e1c52686e34fa36380100000008ab5252516a656a6affffffff038dfbfb020000000009acac6a6552ac53ab6afa545a00000000000152e7583b030000000008acac53ac636a536a29205bad01a8b5dc04000000000000000000000000cc0bce2fe8ede95052ed924e1c142a0462481e16ee6131a1d1c27a829e4ec1eab446afe9ae716f3f0c5fd20d6079a840a3e70461e6bc52065562b1abfb7622b45367e39405ec33e169f28ebad94311fb7bd1c6eb8e04c8d31a356a5442d0f95700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000def7372281b97a6058b3a16590f4e95fa603bd2430484a97860be09997171cd4aa9d5b5eeb8c8b66de681dc70b23cdd8e953cfddedd1c5be7d9825805ed27435207e15e5d1e503217b982d264ea94fa95964c4f9bedf804bb718e0ac7f1f42201bc6dd7de939172c4310288142703215a13f5aaef130e665d0f021e98a13686102085c094eaeff2bb7fd25f6429ff6ffbc5bf08ef48c3cd77090aa57e132ba3759022ba1efeaa1fe19b2c803040b56c43f5d8b5ac78c9be3cdc6e764ddd0e99936200a2c14470fc28984bbac0fc64844d2425c2d976386f53da7621279ccb722dadef927d55e8550832d4e1f86cc1b4771536e3f134055b681640d0692cbee8d2e696b0308cc87636eb24502c8935ad30e4c747d90368cb1399d7afb4b52df65c5e7c437021f5662aff67ef22f3f5a1e0122307fb9823adc8ff990b7436696a49f0aa52077032418de48bce41e989ffd045e2cff3d957ee13668b8c8b2d6bc2fd2d45b23be3c020dc449fe0bc9db9142c09d23ed14c434df0ea4d850954787691698e22be996a8031a9a45c841657762f1a261a9cf8a9e58bbf405c9d1013b9b857b04de3aec00704ae554aaf9905e0fb28568dd8c8e6218eed2f300977e45ceb3ad40fcd3c762d1332628fc2557f1e8ae15766907d0447d04f7951b985ef399a2ff349b24a8c3c2549eae4bdc27daea74e2e7c2b885b646c9925ae4ed3a2ef4ae4a5df4ce6f6abceac59511028b23d81bffdbea3726368c84daed87f2762cea1e583fb72bf31b99d07840d622dcc376b7e5990d3462c1dad8fae60f4112c2e56b1005e6f7f2e714f32e47471a2126abbe604e45469a81b1ad19a591c25e7ea81dff5f43b201a5b030f0e00c16a5ab5f600353efaafdf833b4c9e121aed6bbe7afa3e8690b4607ec5bfc946e8e726c854d9cbf5611a84277d3ba77d41e4ca3c0dc148622338b6ba1a051c944a0250f3129c31b26a1b8aa519fe0c13dc6ea39c42269afe97e6362de05694a7f45fc7593a13a77babc14f9c093261126ea7264df367e22a0a4affa90612c2073a1c66dfaec0464a2dd02d3c91057f8864c314acff7279e992273e6ddf28b2811cf4c3c9d109510ac5fc599e80d784c967501eff22cf721e8bd20028be2708fba67acc741c798086bba94546beadd5ae2664a95ecb65a384b86a962242b1e5afe29fd09d2dab35a99c95d4226a9245fe09efc45b3f65f17827062ea5c0d174eb0ecacb7e5baacde82e1a5e110800336b57564d1fdeed769fb9cabd6ed94717380fc94eebfc710bb59afb18ed05b20c5f9005fb0c1efb5cad95cdd9a5988098b03ed76aea2fcb05fdc835c5020f99d381ed55824da0ca01c91550cd3d4d429510544e96d45407f2ff5ef74f94b82c93733ab4574e637ac6a621bc63b72dadc95f70260c31e22cbfee0cb1b4119a3a662699e5b7c8bb7b2fa8f5663b4952bcf46d2a94fdc3ad65950a79d17ce20d4d9e53d923cbbb0684156b2b9572a86a71a5729649dd1d4b822742728382e6dd92c959def266295e41c46e7be1d0aeb88f771ff159864830df2075fbb12497de623d449383f67e78dd31dd21267e57f1aadd5d1b740dc5728f4dbc0d5e0f0ffe4e83d8f77cbceb3ec3abdf0cd48baec4735dcc211fd9df802b9593f7958c3df7f085b3fbe5f05189cb5c5b9d76459a8781de0d168caacaef20a5e6a561179cf5f426461f890fda742bbea8f57e5150d1032c09c025991bfbd8294b4f66c0b3cc6fe4ab1344d837ece1587b393a52c71d659ab9720f0ea17f1fbe55042a8bb077180a9e4a86781140ece78bf1cef46b1dcb1a852b14078a91782f761dd4af6d701de61cc62a16789738761fe064cd811734d082edc9bb8eb4be5b935fa57a6cc0fc1da0fef311460085cb877b3417d216254102ed8822bd9a7894f4afdb9973cf6984bf99c7c7c2c20a90f9a3c15950d93e18d69f49b22a963b69b3ac3a7b6baf4c65d8313cefeeee0a535f5afca64ab8a7fb0339ce8371c9b62fed2da06cf14beca4e02d60def4d89f34e82ca7060176abb8cb026295d7a4dfbb1f074f55e9c64cda7d504ef0cff39b1a78444c022f996d92eb117fcab5c59d7f2a3e8c879ee1d256eea5548bf5ca246dd7639fec29d8dc22b1d8b1d8fbbfaca88166a8c0271de1b1b0ec3cc5b6d3fe22157579a8afebc34593705f69d5d0c73cf84bcab0740c3d2c21ca675adcb64b8575419167b190fd486bbd87d8d01e1f7f20ab1e17e87246bbcf9f98cec7fdf2616e264d7e94fb8a74cb22a7ddc2ae3c4a3c23106c0ee226f87b25e5927df2ec2c9df6edfebfeb18c699a7fe7f83bedf81b4c5aa2723e315f30f7c6f9cbe99e06a8f3ebf84a40ad92d3ccef17f79d02b5e850c95fd0bac754e939eeb2ccd2b9a1d084469c192e272c1dea9f8c26303c0f8fae666ce31b8b09", "ac", 1, -63997144, "00aad930114b9698ae668aeb8a7c623f4bcf0ae67a8b7d7cda6d942517115ffd"], - ["dd360ae204e8f56adc69dfe4c3ce1d2e573298e94b7c47138dcf5ed1849ca94a8fe263682e000000000863515152ab515152df057c78bb57270276cd9a6c64ea36758619d6fa8659ffa6f56f08877a55610cdeda17460100000004ac5352651cb4bd7b0d2d7fb2c597b8e325c7cc8240bb17c08e37541c91477751afb50556220c91a600000000095153ac516352636a6affffffff11fa5b2637e7da18f32806cad41bd5fd0c4d5424aaff874a91c03593044ee7e9030000000851535200ac516a6ab568d46901e18afe0300000000036a515300000000", "6a52ac65636351", 2, -840047507, "bd786974c78f019d9840a8ed947e95e2b83261af67f5c36206859cbe116845a0"], - ["088270b804e2447082287bf1b751bbf4b7d4539e3392d91ce769b1cd77fd7d86f0121295c3010000000263638071292937548f74605849363c3f22466ea15382273b4701d1d2924f78aa501691ecf65c020000000852536a51ab6363653f58564f38622a2bc25dc1dd982301af418a2d61276f99b1c83c9695a4a8021b3096ecd600000000006e18197b10f4acb757a52de07d094def4c1039399227ca4f6c6daf29e0f137c51315756a0100000002515379d61db2042b2c77040000000001abdf93c9030000000002ac6aa2a86b040000000005526a520051c0a64f03000000000852656a6565656a63ae712300", "536565536552", 1, 2086079035, "5b770f9a5e5aea286e5d3ce0c51b0a7dbb450b6dc864873d5f89d38f64ae3d46"], - ["7f72a64c03e68fd8bd2e1c79e074d9a4e07fe0b0b42ffe6ec275ea8799b1616ed15d8d7ecf0100000008656365ac6aac6563ffffffff370a68c2656bbec9f516f84538217a2936f189fab6dd726eacaae6490746c90a020000000500ababac6aa99d02c92acbeba4aad94d248c7440bc84bde7f04f471c2efcb2e11ade71f5cce7212dfb0200000001515354a9a403c251af040000000001ab427853000000000009ab536565ababac65528f189e03000000000000000000030000000000000000763cc50000000000d2a7db38046036ad5dfa68ec606a49eaf80c6c7022139828bcb26c65e96d951c4ad3710b20957e86c33d24a84433ded5be9719c3cf9b09f427ba53db0d33b7976737f876de36584ae434326ed61a595b0022fd14ba78fda7590964f1e5cb8d9300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000902c111fd203768a569df41494f3e0245e2d701e6a53f27f2ccd3cdabd05bb97296266cdba217ec3f4f0eca3b7009a0b6edf4ac8d424db0850948b4372a5a1f4aa57e6f8d039a66c0d539fc802affdbf25a92350a564733bc8477219bb9f9690e0e4304dd4947f585ce3124ec58021dbd662a8c697356cf7dbb8343326f4c1d1020d6a1f8189c68c1479d3b06b366b2757ac9cafa1f9599f6ccf6fc5caf3823bea0211d87f45747f359c3b885412a0c80d955eb3f2cddd9d05daf4980240b5ad0b7d0b2b9968c1cdcc872e79382da2c98b2e946019c8ff7dffa5d7c397a8ca0fd23df0271e3efb4003f8ac542eaa7af124e02786cd4d0e53f9aea50c0f06cd11fc3098030e33c8b4850d8b6fe9def18f101d9acad43ad1bc6c9876770722fd8bce287c390210439518a019a994857aa8fd9b4e2b09a8c6549e6fe90874c13738b02d7d279b021dc6490d125c4d2617f9765170ee5e4b06d46d0d42fe32ea752fb3b5b4dd28bf0309f4d721f0c6bc52886c5acb8d5dfd2c88b9c6de3a9a0b92d47f997364ac89cb021c6e0e0fb9ea6aaffcfce275e2acf1064cdebcc59cb5cd91003091646219a3ee44b6e45fa34b4f8e68b8871ba31c24d15e45d21f9f66aadb6f7046432fa868bda8c46f9266712d5e05b67ddd07bf42b7c3ea2fc03c31216eede8f2dcc9c52959b241c144b668104bf4aa8ce89450c25af906084d25aa63ed88da8ee52252e9685fcd1f613f88e144afbe5f5ffb12b185f7426ee4da5d44b12f25b80195f6c88862469b9a225f297964d1b56f35d7c204f7c9294fc9c11f17e7002ee42354fafe8506b1ea04a9a7ec0e3adddde7555f58661fa1fc38dfcd72413289752313ed994957b4c3b211c408699daeeeb4a680a0dd153a7453d2fcc6d1a0f9cebb24d22815137a779f6530b9d8915ab3bd12b96af012e309e5061ba4cd624367859c332746330c6758867dc5609941f82624e7eecc14d5fcb6124f023e9ce25045f69ebe04416c3d71e7eff0ddc55455d9b2d5ee4e13194baa800e538ef4a2a1666738694e2a2150a2b344604b9a5fbc99a5d29b2f2fdc2a427149c0a6580de805891be8f5912ebb717759ef25bf5684950b47a432c1ed420ccfc560c9ca1305748d48bd42f1529e28d73cbdd193de7a52d8b6296084467b8a6392591f72c4e6f2935d126aa5ce9e20cb247f2bebc681085015ad204ff6f11ff0472468a03e24b7af1bd133e7a71ed0157f9cfa0c4e2a922aecb09f054c1c6c96aa7393798b72166ebd96a3928a9fd6ce1a464dde4da6d9d163d1d6a270d2c52aeadcab7464f8d36f44e4612c06314171a119f6762b65f7228841f80093c7c7633d0824d7a97d6381f5a2dc70f595aabe79e096f6be7a80858e18672641de300ff28ca609997204ab2f397516f3bd0cee7621173ec370a3f45b34ea752694a89bc87fd5f8d696bbe0840f31dd91e2ca6e3b20c4dccc7ff6c9ad2b94bde7df72757d91ad18f23527d4353c5f13eb167324e1f9ca44c050031f6929a700b2942b1b012510cb61f6a54495093a9ca1900367a95d57230adbf02afd6ed3baac157585816face13debe91580c776a69d23782fd83c5d2b58e2cd586c9ab671734ba147d2290f6c91d13ea742bad4e977735c4d15432d30bbd85e6548c530608275dd635665262150164b62f2fcb142ef0297c1b5d24e1f989b523262ae3936bccb6e00b24b326b7efd578354198a20fa4f199445b4958fb9b68a4a071fa95e86447d7da6aeb5909172cd5c7531f563c38f8d6eb6aeab13ba94c3d7d956e3fbcce98c2bb018e72ecec248c0f73accf842d2d0da326bf22d2a7c648cba2efdedc0aef5e6c4d8c72ca6eb7a828079be80f6d5692e0eb34a330caa834601734c1f745f450da69399626939edd9dd1833d663bdf00c01ecb77ad84f82d6cc75c5aabb3c51192e6f67c861ab7d5a5c560b08d949f4d94a0bfffd9189d382e0174b6a2bf58316c43b6f9354683c1fd7895c6e429d2eaa4df4afef7b7b9a139dd094a8ffa16c549fdcd976c7308c22c43af30507bd9b7e3c1eb825e141a8c90f951c5292a94c131e70bb115054f94d8a395a6101af9b11f96d204249af89e619115ff2ac9965c9bf7c4bd438b47d75fa6b34fa46ad575f83d3fed27d104f862bc67182c062e98ea334485c9dabf575f7a64193e2f678aad02d7d85888532c0e294e7d14b8b36818fdc586905e2af22832ea968553b6db0934ac7f213a61e2e418c76a5c4ab8c5272825d0ca9c561b38df55f88a4e084c598d65c590c7121c233f34ce70000000000000000b679b3000000000054ee3bf8cc1e7fa163c850188925514df90c8ce36fabca10ab798307ad627d41b9b4936dc73b56ae2e9f7ba5fb64be01db30cd1c858814d332fd5a2d2ec41e52b85c8b57f846b377e20691e85a5a1a8709253b7e3d4026a5af55e9f09ca1037400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000289cc8d6387e4890e9b4ab2184f965461f1e4684b97a5fe033aad2d13a5d23b515a2b0a70b45a381f3c7e637bb208907abf2e0863cd6b5fbee519459c127e7ee5da0b8caaf84a7fc468ddb986c75993dbad10bd0827607fdfc86df6161aaa81258232861dea0f80b6cf3f788cac55d79a6b1267d4a2048dff42a88154d7bbc77031c4bbe0d3350b9667d5a676c433a18e9f63916914be487de113172cd4171efb7031806baf72ef9c888d79458b96a5b65e45c6cbe9e950389a7d5ff25b4f25741540b2d3a278382bfa9588d4a69daffbd8b6fe1b51babc6ab45977ccf9425ed0acb451b25293916cdf41dcc5d79a80c61043dc7d96d4b6faf330bb4b9d262303f6d0f022b4ba0cab7b4990ea4053be10e9d00079ac39df0b89dad012c2017112bbf21810313632498f20daff064453fe895fea253211b8efcca7450182359b9f26ed03a36022c0b5a6c64b31bfe18f52b39d2bb18db2781a63c59067d928d751714a71e3f53021c6e64b631b68b7e605d3eadd4398b6196f9f08909be03b8d6e6cd4edfa3393703265a3c4f516488b74c0a606d7fb227179810a0c55bf44ca68f8216ee42edd9759d60fae6081c3b107ce13d9d7922ed754ef716064130db0151e59a43c270ae0be740643f2f290fd5232c9979866ce38f7ae6b0bbe9a5e2cf8215fd35a7bae3e252bd9c49a8cfbe2d9c43bd15f4b639996e11c07ce3a07249878cd2a5342dcc689094a88c43fbc98dd2c4ae86e7833fa5f933b3440c6c48955e5bb40d11905b645b19a3a33cf7e42cb8d494b44f8da4305a9719076edef9e8410b9b52e6f814937ad5e4aac5b57f996734cf59998c1ecf5f1cc897073f76ffbb3668deee919eb5f5b7b952aa92c213ce62533028117ca2a80897e45bfe9d33b11f9e67885417a8e2a688de3645a1eb9a5266d3c36509cb01b4bcaedb5b9b8f90085a74c8f7172f0a5c3406db69672728e706fed150625f5381327e6bd6e5532b6ee6900ed5634c3fd2d7669b9cdad3644f36b55025113caf1955d666511abe7f3e1c58799511e249d9c2cac060c0979b732c4586ec38c07a6d587b4545d9a88639f887342bffb429220b09d09101b4536be08e54a1d8a7aa1d8e334a0e4c5a44fb1011b4da05d17179f513810c35fa41fef1ce6c834acbb5a508321cebe5f6d27f4f2ece3dd55b05e987c45173d3a474dd02e818a7805543c4384e0ddfaa6acc5697ca8124e06d12ee13030be5fe58f05bc872914f51a247c7f2714e1024e00f1814165dac6cd162db8b06c6a599393cd870f9d2229fa2824a86270944dac044afe229970afe6c3d73d09d697e72c7fa5393e72a22448743a5527a2f8c7e6cc1771cc6ce3b55b0303bbb48e62a6f77f870f8421c70e41a4b3ec354fd612724dffc3bdcc79a9b4f52d35f6c41ac87fdbe2db36078d193af6ac9ca9c8aed0d5aa40642d2137e89ce4ad80963d48abc40c7dbf7f156bb5c95e5d00d3496f66d10e2c2f2798422a2af43da859f443c7147d63ceb072a0bdc7721e17581069d643cddae4cafe781cd2e66d7ba27e01c74c407b2838fcad022324441418e3127c1128de0535891c626f435762221ce5c1f92b07cf3aeb07f44a886f85d88578e43da3a71832be36668bf80945e296e965263408f88cc711a096d47239aa1a4c65fee4c1968c17514b7676fe61df11062b711d1b93b5445ec44ecc426663b3ad9a9e17b105866f0fc06fbdd69ea25767a85ab57e2b70ec48dc6fa4c83f4b4955fa6f5f31aeb6ef0c8fb1832803b8f3ec3c18eb7293f6903863533cf635651a9eb737fcfd20c77b888dcc8b09f6c8e16e688a0e9a4919a356755142b96f51b0d3664fa0dfd88bfc2ce29d4b09603acb942ee1596fdbf087564641d669b59cca3724c8e57b67874befb996c10a660ae331b496ce0e0762b7dfd837dbc3b92876cf9efb34cf384bca505e71dcc5625684d04ce1d36d656f722faa27cdb32e4f16cd24c1e466823d704db4ff1ef8398e8d5f80cccb44fb2a9decc2f724423651d568df05d8de0d0cf6c20440c7d69114706ecf9c03f027aad95fceb372cb7df3037f2a0e042f366db8354d326d40d9e561e7cf6e2f9fbfdfb6dbfb7bc8f30a0d996070ba6d664aaca7d3ddb0b6dcd5a4689c6115e902d8b648c90f2dd521072fb1ecdf9d429de54b3d26d7cd79978ec4483ab6a6925469080c1551ceb476cbabee6795cb177ce70c4da6c3aa16e559d6b4b6864375969785724a100f8e18b6521bdf2483a342f88731ef7c2d9c40c317879b2873d3d325e5fe5cd5fe8d5686dfb5a02000000000000000000000000ecad6f06421e083a41438eb8084d0b37e7a5fa698ebc286819b154c2792f8e8701652ac612d0bc583221166c61a411c6348482955e77f7a8cc24a4faba8570c4ee8977b6c1213c0400c8345685d69d340755bdfbde2367cf6959377a5e07901900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a85a4a909b0f30ceb8a6c5a119663161507f19fced62b87d379759f0cbdb690ee9e60e70830c73a6ad0037b2b789dd44775eee3114793541eb98d8821d6b0e04325859ba77b55fac30971ebaa66013f5f78436119a8a17273546f6ee3bcab7dda20dec4cc53c990d396b6f7d6fffe84d24e65dc4dc5b6b834cc4477d11b23a4d0214e44d3690825687d4c6fea30204f5760bab846f672436149d3db19f1742de120218b912a0b82392122cee384d16eac481e03fb7252053454ecaca2c455d780c970a2a08eb6ad25385e34738066ed00d0a4053a81af46146ac8d7ebedbf8250bc8f00e3a3360e1e88265d0aced96161b7435dd467e1c06e8c16a4234bd2b908018f602085b3ff1e055f8aafb1173b9f93f95ed8b75dd51a512e2e245c39fe0dc2946fd0322e033a86a4c34d70b4ef90c85185bc80ea7c0576773712c1be7454553a30ceb032415a4bb1dd00d48f12992b43013199d68b60c38b6493a0159c06c4b1bbf4d0e02215842c6fbb1436f6ee7b5d452485cfc0a900ae9e77412bea9a8ed12b6adf263020a6201b379060eb46602821bd2272786cf4dcc3b59c1eadfae9cc9c54a5559af41621f839d698fa447873af03faea8d6203d6b33d8b4caad1b888721afc8d212bd0c67ca67f631ee787774fc22e5d8b51d2586e2490f6f250e3e39636cd75b2f63159b773b50327a70b298c5147c793f47f09e89e8e7b48009fd95fb9da18deb8bc2d39954dff6f8c3900c187e0f9e83a6a9ccf5cc2336ebc8a79813667573a1cdb87b5dc5393207aa7e80020e8c9496e0a70aa929c51780acde14f2ac8fd51e83aae55b25ed24aaf414d48fea191795abdf1011ac50205b01dd4f0a348bd8ed0593af4eb647c5b5512619ecd73b82b59fada178d0e12b083b3c731e6620fb4d7081f32c4ec77c7f8cb2058d765cbd7c7cc3ed4064e1da8fa82b00d73088af4b34b6ad735493a002cea4431d41424017c49bc21a7314e837195c454c1ed19d67b7fa785e57e0aa76382cd18c600f27aa7e34aa6caf8dfe001972af26e980bad619797e4380de44fc3e165fabf293a80bf92de93ddf8560f2ed572e8e34c9c76b37147a74c4b093e9b0791fa6dafb18e1fb061827574ebc4e0329d141634342f80492604c661f8b7a88ebbaf7124da733667326b72a4bc83dc850e7b5e3b8146dd9b3dc40a5a85bb9995efbc23b8087e5b1d8a6894dfa6b03f1250c3f386ee5bc29298acd18d7ccf3e50cf8bf3cff611b269a341d6b16c7dcbaa517ff866586a9ea18f06d0c288e7ec6fd5ee097e09a0d0cb5bdc17bd218da1a8a8d0a9bb56df2bf22720437ac0d1443a853d19e66c674f803a881634498c934373075944bb137afbddc770ad169003af1ab53e1780949863ffcc0bc7bf5cad2c278420c5ce34879b2e47dd200a04879ba553841349c1bbae3482c654d459cfefef9196e05d21d305ac44d52d8373cf55c6efec7c7143aadd846e7a58bc066ae918b170d0e0943c4031dafe4e968eb4c1356180847d290b454edd8b31614548b9dd679d6a867f86d2ce954bddaab9060b3ed6a13929bb1f3d19e97026f50ce8f554543daf68ee3fa2673ea01765fb92cdfb11a8a54b8882fabfa345c026f13a083e62673882b6384e17607acaa05763605112e030829e63d1fafc42e9380ec3c755d552ab9ae5d4508758a2edde5adf0b036e1d43687deb7405969952d2f8caed09564c830192f7db2e3e4bc7b9d8052a6b0eb8cf54ef85f0839a4b0b1ea9ee215c702b2346d8cd227dca38a4a4f27e3e293f0428094b495a7bd2f0d844806746840de2961ef7101cb12de7260bbd0993d0c4f5e2ef564d4fc22bbf176a718c7ea90426932ba904cc462b75f98a9936c90bd7639afbbbc14f03b2be72e6614930a72c9bd3d46188d5a1c71a57b6b91195bde54afab89294be448e2171be68d541f1f6b65889e67b4842cb29b2200da3182fabfe961aadde0bf3ba16cd5fa54a09011ddb21bf158d5be70d6ca3c9e50cd3019c90f0fd9ed92a15b1bf802d8754c7f2fbe86935e91980dbe6de8c433ee10ff882a93ff813814566da92e1ca6ec57b76a22536dd04b49de1401ce4b65992221cefea048871fb49988d45deb6bb6943f9c47dcc2d1e189cd007ba52d9378cb0e9141ab716df06912ae84d6df890fafe6392581cfe9c581bb76d5f62731b308b3a36609874b322bfd806e27d8185260a091a67c73f5bca98948b077d3cec6ee1b876aaf237bd10826cc17a76ff82ef5b8b94aaada6949b1fd861850829028e3d12aaed62f6464b1cc449753ba15e4ef4dad8195a44ef93a3ab7ef141cc0724d903f42fa407ea4386fa855b2630bc6ad898d5408af5fe6fe9bb94438b2f5e27f703df4355f1f4a7c69b1b41ec211aff789fc9c37cd23b3a4f628f13f66edb19031e83485a91675e10d", "00", 1, -1803528140, "e48796b19bbac4e6b38cd39ed7c866900842f3794071ddf1a597da64d44ea2e5"], - ["95f189830293f601ce888ea34da0f6e1052835224fc1a58eef280ae80abe38b2fef32b7e1a03000000025352457719e872c9d650d0a7ce1241c7f48586b4d4bd3f18e096c495e88812eeb6cf98640e2c0100000003656a52ffffffff037325c9040000000002abacef9c65040000000007ab006aabacab52018f2104000000000463005163e0e85e6d", "6365536a", 0, -2071810366, "16c0a7bd6381e1709e3dc88ebe989d720becae8dd58ae547144f6387ca58859b"], - ["fb73677a01231964c0bd7e4d1735c8c4f13cddd088441d3701ef527bc0b8b4bc769e6217bd0000000009656a635352516a5251180d22e703729e88020000000001abb5403b0200000000065263655163ab980f39000000000009ac636a6a006a656a00000000000200000000000000008d276f040000000022f5b722db159bd649b5544bc2385a901d7f83b3fc5747e5eef017bc7737bd1ec30d42a55902c24a28b5907e34f295c81869a8dcd0fe7ade5d06da32db1459b46995db517f7c5b2946e356c7f15fdd7a827b7529abd1c73f78df50feec88c11400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000421cae3b9a9318fd6be61421f1d0b0b720d90dd267c15fc5e99f2e2086e9ee3812d73be8252523e188ee33aee6dc1ad11227d227915a9a5610dcbf13b067023b51761aabe1355f4c078469ddd01a2644cd732ab29f552abd767afbcf46ab646ff59c585662e3654f188c18e2b5d331918b0a7551dcb9705052158236c5cd385802056575c9cf79b4108726efd79a74e18d7ddfed15f263262b253d74816a6201c9021f39f7e748095796c628f7aabee0af3ad440742bf5de3258b9679398dadcd0b30a1577fdfc354250fe96df24c0f2be1cf777e7c1137936c4c9534c3e67628f01e9153d499ae248fa115c0df47c20a466f22dc93a12567d4c51f8946a14b6ffe7610306e63786512d108489b9593caff16722a2b152d7a0e25683e49baf16a8c8ec21032e13e16bb9d7f591b2ca9fcda22205223908612b67caed5b94d591f8f3c230d10200760c6da05bdb2dcbcf7d64e7906e56a01e7cc97d2c6873f72c0fa94002002a030e8fe46d8cdb5e3656f75bc3d027ebc4ddc0fb6aedadd21f1e4bdadc2642562c022f670d1448d773976339fb09c66336661ae16f28c206b0c717e0c364aedac242b9ed865c37586925813aac8d4974b8a18df747a958265e316c2bf57c6261897f04888d23ed7d91d30dd8a8982fa5d13e481699bd0291e223e333a9df95214336a3e21292ea7e1cef975727192d1c726504c137e8fb5e53a514bd932bbd2d9171a1052f91a57c6937c24068c4edd57959a33929dbf7939666be3f97fc751de6eb23e473af354576c9e5a9c28277ec078df9f8be0128cd74a5e822816bd80555db569d28fa304528b5ef532488e46a3681e3336122f29b3ea22ca80422f5d60fbb9ea1f502c0ff77a93c9c5d096c8bd17562be53f22fb9f5071e2edb76ffd064c7c0fb9631bd5c2acf664eb31fc61a81358cfb5f4f0afd7d0aaa598761c94b54e229974435d62fd8ebeecae77eca50cc6f07189a98241f9b3d4962b22233de909e76fd43b47b57d72eb73b0222820b1d061532028d5b3e80536c002f55aea802bbe260fd2c8848cb93cddaf57017953f2a54bf33e99be0d3356f1a448d57223ec1b017a4556fbc8132fe3d879032f422e6cddfe1df3e0ff3c735fd19f17f23dfcbac06ec32563dc4198dfbf1cb731fae2e5e323606376dec2b1fca68542293d6ae2613faa336e1566485b48e49f935f21eb4f4070425cefaedef8b9a57f2b95fd145b1bca30af810d34ddd9bd7269dbbb070a4edc131e6fc8b3bacd6aba9d0ccab56d92364895e110a6f33af4c1c69fc2ca46908e6b75698cc9b13ea73a8815778fd7b0c41dc687783722f85d25e4d0cddaf87be795304847ef7a1580eec9613f210366cc007e41a70b5575e0576d3421485a8a7713b4c96b8663ce5f0d5837e2952a71c7dbece3dad54d30acd597cfd89df912c77b8b7135cf91ec0a656206dde11d598e7e47f6b42020f4b9e5f6c9cf337bbf5019946429a414a9aa025c57aa83def9daed2dd73c0b6c5456fd520e12b8791624d7d3fb685ab5a1b9b32e19d2915e5cbd1831745dab4aa498a1b9947cfd1ca5707b566e1204af78b73273b9712a1e292acf7f15b706e9fa8dc3ef2b67b038728df06c249848a78777bc9a2a7e6a7454f08f1a1cd60e6bbd7ea7a4a4b75478d3994fbef7140d2038fa7e295819a7513c0397faa01958e80ae427ac9ce3fbd91796d7afd7c8f29e061519b1e446117e722c05be4dbf0fe5f6ec5ad65939332baed73c1f883dcd1c74a1217d041ade6ef83d718b152026335a2866a4b520785e5d593d2cdb6c513839bcc35adf185c11f213e8fa6311d6715be26cfef3588d4d5c90783c83c6f1c50eff1a502d7cdbd23f6c2a4b2aa9bb2b78cd30537a05372c96f6e69dd63a3ea7283cb79df29e5947f3687e0c9fb81debd3efb7d226fcfca873fc777f7b588376d8eede8774cdc8efa8d3aa3a32eccb8535d521cfc6f516055340681c49a7fc17e3af82884fbfceee4ed3b9e7857294f638029312c403f6a037ba40335df5c44904639c5e2e7b4a21e3040bea55ccecb1b9d8ba9460c70599149ff2fb2a57efa9a615d1f7a8a64f21f91a4a2a4ceddf4e66038db4c32141262ca761d0f60aa23befff1f7341ba8738b298b94a490b4f91f0107d4ad849ad8a991130caf058bc3c983899e0c6d09c1738d76e8c974c94325ff0038039e5660ca8b6c4d675fd151edc07fde2281434c9b6d14201922110b50c724d84d364aed3ef002f9c0b46d57643387f068f803dcb5433145d1580976e3248e8998f66498c40000000000000000c85ed405000000007253ab1b99506848f337fd94841f254066d22d38cb1022a32504cae7dd8f8678519b36037d1356543fa23b75ed364c3f6600ab1b08d33c9000f49e3da0176eded209f54b48fe7b8567f57dc9dfdb0bec0ad96c7fd97edb530b2b4ef7d573edfc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000033aa38d77fd2b5d9cbc82460e0cdb80f566c60814d76c387ac7ad67a4ca03b2792981ddf3d9287a273c7a248832f1bc90886d1498c80ce7ca4a2f6fd4f9cc513d365d2e1a775796a2c989caa9d31c25334acaf8c87683ebd14d7710984ef0ff1140d42680695b8ee75689cc9ad9be78cd1a6da589094e0d656378d998dc32b7d02019ed4c4a18c2b6d3d2701c552868a98c9a493e324b493b4703faf19baf687790306cf6a7de7449a61b486734fc713e2518b59562b301aed779228f19fcf2f92060b0d3d42257b8fbf89377ae635a4e20d3868045a46190d3f6b87551d63abda439022624cfd46e1e2c4a42ccb491fc70ffaf556f3ad027849f2fb925a4db0b2914a0220ec64b3871301d2891baa9dad047b2360099fb42f2ce4197d75afa42d7e1f8b031c42a7b4de3bca64ea66a3cca11e43094e908e581c4e4a21f1cf7ee6a15dc3d602252647957d0560a274e5d25ff7b24c50883ef8b052e6c662a262c3a316d614250321c8802883f7a13371c9ed99b948671558329cf97c7a0b39b3d8c387d13126520216843941c5f69b764d48fa356902ef5d672d08217dd7f153e23b9a49c926b984e035baa58f7dd2337106f17726f6e0818c6c46b66545b2b6bb3806921a4f494d8f61070e8d211a9e8e402ebd1798b43a7c3990b0a76f7c5edf6bae148b741cc7ae0f5ff2cbf281e101f753bed8828e6861483382ddaa5fe1ade6d58db8915c540ed603c39c7be85fcf985a42aedf879295de9ce56f1dc0898a05663de8e750c3d750299d48305ea8d2329325a88da195157b585f6b188047decac6788fa02f40551e0a1dc5598e624e0a096e20a0e3af5c963793f85171140821d548e57c6f98e4e18ca8f83c90158d969c3ad178b3304c8528079145911202a6d603df4f31092ccb3bbe6180127d0688e4d998fe5aaf6ea5ad11fb6eaba675915bcb3cc7554f97191891148b0d74f30ca3501dad345eb04a6777255c5cfa8394d66634299e717bf81e0b0270f3b95e0b6f444bf5f084467517d379a942ef7d92df02e4af1270b76c96396fa2206690b17b352ca9357787d7662fd008ee3113d2fe67d9d09e091ace67ee1e0ffb69ba4556b635eb8935ba30a70c32dcb82134a97d268aba5aaab03340482d38b914d8490a22dd5a57e2f8c5399f863f0e29718e26744197c452ba108acfbbe6e4d796e2320710c5acc2ed9035a8c3f0f804a3367d17ad233a0de2960d1703e1eb1600c16a0c41f35ee9b285300e709f2ab4a5efbccc53e391dd8d2e78085b44d01f443045709d56258e26a0ba7462d3d1ec581e864ab6547e3a4e50ef89732a4f69b9374d1878ba59cc2bead32b5d274843d6ad2967cbbb1743e3f49c55c38496c44005a5193ef78e1901260ba5bd315e896bae1c812bea84549e8b4a7ba0ee0e00a4b01bdf57cf2f6cacb67e92f9af26972c87493d6ade4b0b3a687f900b59effabcb83e6da051b3fac5bbc378f9146cc91853067da3f59118a805f6fd8417830f36eee3033cd95cb2ea53f76d242488f09376a022022387ef141803777195e7682fa7f0d86548099514a74199e95527156fe26067cec7df01173cfc32ccb2b5517af529b386c588aac31c02787a37236ec74a8b9964cf327cad5197a4ac38dfd1979a4a1bf8e88ca40c34d1b237920339a9249922f90e67a7f693f9436dfa438d4b2e23ae6fc89ad9d08dcc9465d3be9886286ce208d4a64ce50173a8633c56e91f9538a5271b7da986a9de609ea2c9d527bb6a7878cfb19bfaf0798e6c6e89970687de13aa12a23380f8cf3322dd56521ada908c45c8837acf3102ae720c3d19a815f1082958aa2a7d4005c49ca73f14c9346d6ae9247964736392d38ad6d352fad01942141c85d20b402d961c0fe7e9ece8373034a835c6b2fe5dd39f03ab1407da1aec5b3ca4ec05d9948a6c14b7dda98f484a21ab87ac34649608d0b85f7d09daa437392fd38c1f1a7f2bd36fb73bbcb0e5d8b7599f3a29c1ad3e9f4a7e266938e1d7c3bb53daae95dbc37596a3db8114cb09e9236daa313f91f2efd39d2c5c2e0d3d9c82f5193b2442a2e338d2425704af10513d2687bad97e678ec201781a62d76a478ba11c9a59951a8c43190a31a20dc2e05e0f10f214e23ffff4821114ba4a17531e91bbccb5c056f1adf526226bbff802f24124a31e9fcc5a3f2b87667d6997cd9ec19653d35b5511c3b53e9012b3543b381a916d8668075bc854ded3b480057881dae93024a03889b3fc6fe9db242460081c9e28d05d15b65a7de846e3b81eecc13cbe0b67a6594f9e23c9e97e1b94a0011937c90b6e8bfac2d50e093b28c26ee35d5932b3fd802936cf6d4cd0a9f9ec55896a766eaeb7a1ff09a358c29b3a235675be2106ccb3b2159827b8165c412e7bf97dbfbc0c35b2bd075a18673b6a587d0539c402", "6a6352ac5353", 0, -82554638, "e264cbc7af2aeed77741e52925b9583406bbd280b48b8f37c794833449792d02"], - ["6444e0ba01f122dcebc01a378548f5ff3612feb286831f5b7df7ed5989577b6fcafcab8933030000000753516553ac5265df5af14901fb1983050000000005536565635100000000", "6565516a6a0065656a", 0, 1013967395, "f708753ba3ff589f03bde341fdc47a34a1463d8bb3bbd24a7b4d752ac860f06b"], - ["f83342dd0111eb06331f754c74c48c9a5b3de873fc514c14e18b2926b19203b2978111816d0200000000c7d35b3203b95729040000000004656aac51d924890400000000003ccd7b0200000000026a53feb8ef2a", "52", 0, 1860359320, "c4ec322d92b3198201cbb43e7e1e142d2528b3fa8bdb5301a3d77210de22f906"], - ["fd42002604dda956fef1d0af0229229dcad217d84cf180dd18fb1dc0cc1c5cf6104f03eb2f030000000865005363636365659cf191bd8c0ac6d1f5901cee8b2dff9403ff39c5c8ea238e60be80326ca8e7a44d5a41da0100000001ab159937cd2cbc8b5fd7b627e0d4377e7e32d36c9298ff452ff15570bd40fc7f66c8dc1d0c000000000751abab6353ab63ffffffff9dbe63265c9c65e23b10e88178c2c9dfc5ad967a6d3b83a5474f910589d305df01000000025363b03fce610325090e020000000000d2e6e6000000000003656351c215ad02000000000852656a6300ab536a118e2ca402bc0f050200000000000000000000000080a07f3b988af3e367374ea12f68ae62b23da30f73798892eaf7c129da41a091bb0cc03c03853a42accdc206629e5ba63c66f6f045d3357575352e1d23fe4728fbd0b8def631c375bc1048301b3c407e5c4d4d94a0797b9ab2a9d890298063220000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030aa7371ffa104f819fd501a5d3825ab39614d5bcec450c91c7d27992266d1dd48e60f2b5bce1c6199fc293bcc337f270d7f057d5d0317900cb85136ed241cabd181be70b7da56f944ecc4a779c8913d01cd80db3fd9167799119a5af10f02671aed549473f50bb09eef56efa5e32b8c42d94e066360d00f750dca491b657673022ba16dabb677e78efaabf3424b6489a5cde91226a6b82a3586c5976a32559737022a338ae7b7959ab10bc410ecd8bc13e65b8fcd28ab853bcdbbbad024ff34e8b30a047214eae7ce2e3362cfcb98f6f4a4ccd3d826957f44aab43163ff04761817850a684f42a97a19702622150572eded3468dd0a64b8bd06859794008b20ae17910210ed44e0b1e990b9b9afbb1a02a3064a28d4743b740579e96278d57a3a5bb2a8021378c6892ea292d6576c489e8084deb603cb8d70be5047c2142504f4ef230e5d0223a6e90dee2f051d4432f8332a52eccc8bf12e180e3dd29e4ae20a31165986df0300b58a571d445ad5268969857b03a222867470312fbfb3f78bec03b0fc7ba68d020fe482de6396bb2b63db9dadfa419b26d1acdb0d5079ff6996da0c512d3545e32711c4906b5dfda13fd10dc91fe6bf4434bf555e01b37761ebb11ef52cb644622ad33ab49adfee748542aa8d6e34a9c14d43f7526e0a734b1bc987a5c7b1192c661150a7cb48f47092f11396065e8e857f2e53f877d55ef7ba6ae97f91aac3802e2afac32b476e20935a0ee2e73f4413c34be60b3aac231a4e692931a2c1dfaf3c96de1284842a0bb418fa79c8d5721212983a072f942e3a8e4aa49246c5ecdfee7832f508a8cd3fefb3e529a9db9e73d8fcddeeef9cf773462fd6613de196e6e2b4f61b98ee08b52b59a54bf448ac9f4042afd2194dfbfcbe462786e852c2f411c32a7e4ad13454e273d28f83994886a48965a1750cf5fef3c5dc2565dcf2f04aee67451b2c9552ffb446853d1764269b3769646acd475bd17b79b81df9632a79eaabc1c299b1711b4aa0e9f8054b28fbd9cd63a1befe5604a0351c79abb0cfb3af58d0e5f84d08c0af7aed45e2b7bf22fdb82c98f381510be4476119306e8f6bd7493c25dbfc8adb7ea7cc7f5b89620c72548ceb2d58dfcf4b4001b5b50d8dfa777e3d055da1ed323dfb41bcaa7a9c1af1ff38f3958954cbb87b60f7c2a4834f9aebb9f2353fad7ba21b2c451b53e1536237a07348cce6c78331e66741cfdae40bf101825d340730de698516cbdced78dfb45cde15b89d3da6ccace0393e517c7bcfff802fad1ac588bc402aaf02e6184f31480dec42e70fdac455becf014c365418b33f2c776c4b88681fb16e167889b61ca3256b710b2acb864113cc8a0fc0af73cec944b6d5a01562e1cd3a59c421d8054b5209aaedab7287a4ea2b233e83cfb71014a6d8e8345bf8b9b3fb355eb3a4a2ae0bd8e350d0f28b79c96e9c3f4f688208ff724a221d7137328658a3d398d9ef2e591eddeed28eaf2c80afa59ce12a106780d3b40d0e534481e03c1d49575b2b29c5b3c9424cb9f9655925cbaa8739417e1fec647bb279684566937348e1a91d0dd6b15884018dd2108dd1e2cb4734536e56c86ab90ef79cbc7a7930028a027f5a43b08f00767bf8e5bf96ded88d30613099f7a6b8039f8b8837affc64b878f302d2e01e7bc3edadc04b3395855d50402c10f73a04db2232daa2b04b770cdaab138c1999ad4ba4cb56429f1c4c81b5d65328b463832c345a5c0c49f3ab8bd4544de21505fc1f4f4cb368b4cb0e805766441264af46d2e45378fab422efc916be317009d75a87735638c164ade7714b51812006801d8f4e8e06a6eaa7d38e8429136beb570cfd91586b7cd5c69ee9ee6edec9a76144da653d4729e32d28c38071aaad62ebfea88be295dbcd77b9d844b9f8b6a2bf82aa1f3479d5ceb468f2d9a399311453c29d397649dfc9f7627bbb0c6cef7bee3c20dbd4ba3fed36805da5f0424d8bb908c4e31f5af93cbaf20860a3d1eed4d0635261eb88c6cf1b6e5940b0e29f122893f7658da82c1e989d29430e22b4f306bf88b00723aa18dd7999bfab94d307c7f0dbf5427acf46ae14fb20721c7e8313a86083fd5880dc239a5055ea7d95f82b4a252c7c297e4f7fd56f29af5a209c0e28f9546299834cda791c0152ad6ad2178daa21d0ad10f14603646f26b814f32ebbd8bc8df12310dbc7a99d156ddb15edb60cdf9f1d8bd6769a129d9822e99b37e643889fbd32adaba74cc7010726024c97e8545303dba084143d27bcd600f216ac03bd2cf327830f4ec423cb942e00000000000000000000000000947c5ee9041afb5cb791e3df2921b4e7cf4ac0a0a849d40f2ddf7d0cb0fae9be0ae2dfe457c5c98e982aa301ae047d93e1127e2359dfd608cc9aa53dbf658bfa1e948adba4cccd02873363889f0093c896ccbbe4262d84dc513b4221632d141d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a95ef668963b501414c00fee6c900df027ee6c72e8680fc4d5d0d50754d1438d39ea515f07eecc31f36f815507161df435dc8d7dc52438ac1b099c27a99a4565c3b63798f1d590fbc8aff70184607950607254ab38b0515f256bd1e3993fec3abef4edc60aef2f7d2ba777864406e6b46a9c328798bfa3a044dcb6f291aefc0301c616d2493b9b028b6b6eccba59fd313cf25f6463b68dd34432f8ce0d4cca29030d12c56ab92c8386114b3936fd87eeacb30f078a4edc303a26a8b8cdee6056400a013471c9f9ceb83bb91d1b6cdbaf4261330dcf8f2ccfffb6de9fee16018662df086e2c6651c44700ad1f6c8458cca448ec7ed431d194d47200d604a0fb06feb9030dda8d7aa81d6cfd1e08d9724c71b0aed4988552d4ffab4749ebf1972fbce259022a4820e678f9d34ba54b0bd3d583295286c8fb7d76ec7eb85669375085d25c8803225bbae7724ddd6cd9343f016bedf0254e1694e1c4278cb577d5beba2db9a432030961bb367a0edaacc01090d34acfb468828538c20910d6b20dfb45196bc1fe990216eae1c2164a44b05a536703c3d8bee97556163df40f95e1dd6a42abb8089a10acdfc64b629f5a0812b9bac61ef70db4a1d14081e4ec2e332e620ac0fc3a0a54ca4d290ffd71bbf5125392ae16fb0f94f4f09a9d88d0432ef98cc528c77c77ce100007afd314882a0b8de95e135141c887e9471c97acc98bd7b44ed4aa2dda18c2df75ed751f4c5f51dec7286f8e39d009746559d570a9674f6c21794cc04140bdb37fee30d77a6eace1938fc0947666fbf84c02117b010df99f0e61296b4ed2ec86d6c81e38fdffbb97115e399fb366ca68b4b2f719e5e96c6f60933a5bf6aff9f5447780ce5a14d471ab62d7750108ed5403bdc499bb6537c31a4531f10bfa1af197be3fde59fe1e924cd376c512f8643c840c805f62e1857946d8097dc831a96e8adb40c8db8f54e1a03757bd051e074b1f43ee703bc5a2978a2050d7fc6f78abe240cb04d6f5220cd7bab6778abe8d606c6bdd46556dbb71166f43bd3a81bf4e2865abf8f4cad102f56c20b2248d1fbfa0c47fe935c6956101a6d523cbc0e389a715fb2463e9cbc1cc6936cf5f41815c8809d76eb3cd298b7491f542860eda711996c4bc0d1720c15f6e653abb4ee94ed4c9fd4166ff4c5a9b08e343421b2982183510cd242befb546c807e6aae93423aef3484c82be28bbfea47ed2fc7b32f9cc8e61f359c6b5015e74437dac1b7c011de7508a62e81a3c24fbfdc4f65911771c61c54f5c27f243f4d700cee016d8ca1aab22b9db1fab29eefa6025e6342725e2ba94aa2e40d8bc84d9db974c9b2ac4f314ed554c1d612b46bde4f85a81bb104232c60730e8f3646e70b29131d155500b74585b978fdc9c9ae1b951bbfd0655104a6db230bd4a85259279098b435d4c91777cd748e31504bdda69f38f0ada1620f2f10322e704d94dd33fd61101e80e9c0a137e95ad5d5552cce521001978a85cf2c0f781a476ef8a311aa54484099b34e47a37f62018fd41fc6f5b46c1b7a479c12e86ea440ec1424401db7442e0c98fba7baa5059b98d9b7dcab2dc84944d0e719ccecb1e2d5e1d61598755446de261ca3909b2145c7fc09d51a0b8c8603c3d828d19d5e78ca3e9a5127dfc0d94f06a5f2ddeb52a7e25798bd6d89c78f11e1caa3a5df8a27757a5d26a4ae6073b041cd6c9db3a7e094bdd6302310208650000712bf1dac3688c8656e336195e16e17f7b51f4a729e8e0b654416a3273dd4e1c6806b031cf77e2acf374a0e04ad7941146dc409e02839a5cff26e1a29863e27c7a55526aa16cc6893866a7d4642baec1c1d3aefba0405e831523317c2bf76f9d98712eba715159e0fc0a8768a46f1fc9caaef1184f3d512762ee120522593276f0fa062461bc41079e58b6261cfab21e766727b8075b3dea8dcace79dc3ef3676e3322691c9784024278b548bf5aa82b67146ba9cef42048ea92f4964c8f463e2780df53eec5e74cce4c72e60e6a137b37dbc49a7da493e3bc02e689b4cf3f5ebb1eb5839b0f33870ed7c94724dda5dced0e866f491850325b8ea84a7d7a0780fb2493619b5e6116c917896d5af87f0236dde936011b85541d1d107595f93ef8c1946c587323b7f985c09f2dba0f4f0ed0cc58dd9b8e50972134a1a749fe6f65d902092860d916ca0c2e6026ea3658fbef095529186a5b471f0c4a78e8b0c24705d8b0ff1965dd33ad9e0db7fb9dc45930311f29726f6aaf1b223200a6009aae084524f1cd7353badac0c15c43ea57c966acfaa440ca710f4d45207a318b9bf734425227f437652ae3733c07aab849257afd82fcaf3a50f90cec9c86f7f64ecdb9df7e95e05a1bd9faba4f4ccff3dd3cc628cad4ee07ac7a547e0e5885ab51c131d76b6bc9c5d6216b7ada9be82402", "63", 3, 1199084511, "edf9af713d143d096a702b08d9832b287f183fb4a2d90d014d60eba7f1474aa1"], - ["6463ce6e02e9d996b8fac84aea4a44af3c64ca46a2c4c5f4f05836af09380f46236abf0d2003000000026300ffffffff0849e30ed29afff540a7f504e4bf0e83cdcc32fde550d05474dea58d474d046900000000000d48dcc9042378300200000000015321a2f800000000000365ac63d7eb380500000000065251ab5100514258120000000000056a006a5151000000000300000000000000002c8e3901000000006b17a6f8eb67175d7e28215fdfad7946e921c6ee3779d67ed30934cb318d229010808385b0f7fd503371b9b7e4f526e30ac7d621c6034e5a62cda83bf8e2a2fa4adfcb593346bd72a90a48998e4b5a3d5c4b85f9dbd3578d53ca2a16d6e3a84400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000862994269f9c33efd088e6f52f6c2fbd6f304ebec24622cb389031e2f443d2ecceb1a1ccecf10bd83a9e5bb04fa608cfa0b1f2c9ad8c337f0ac5fa2498be75ebea203bdf586ad13804a9324794a0842a2d1b7022fa30d741c4b27864f7a384a531f4245a6ea0d8331a2417de612d42c155bebc6b6f4c3fee28b835cff61cfd11032494da34cf098fc0ac4945d711e0a12c16c81cb78882a7f5ffedfd0e68fd945b020d90a0f6cd365343bbab6359c7c9b170029743d84418881a0ef6336f4b49768a0a243d6c0058d6603d9f5cadf6fd348a10b5e6afd4b0265209d4f2a8f917a1ee81137c30f8943b83e43ecb04484e690222c2e8fde4f14d67ee1e6b1cd0eabc49b5032fdbd1c440a12b59b88fc9b3bc1448a6bc36aa090eb2c45d89b7040e9f94f82203279556cf4a800aa2e70a2a531feb52f6d8700093eb58130cb01d1ccad73ab277032362c9143938888833621e23f03fd3e691ccbaa95e899ee41ed0b6c34344d377020132625a577d446102fe06b7466a451cff3a86ca890b7d4fdf07e56c990f2429022c4947b4a9a32fb8ab88f2340a3d7d55b3ede79966c6ce40ecbba2b05910443d7f8a09b075ee1f4ec351b55090bd93018e5238fcb6d787db62ef167eb2f01e5ae3d68ab66568b1e929558c05a5449694cf083ffa59c52d57dc83349ac33756689b6f65cda5ab9f5fdc3b0221ddda6cf42e95f8a4abc0998f13bbe8de74f74cd08d4e57fad1d65b3ab9f719add259945d4c3be7162796db05f4f7b086a393f06778a65b8e2ef5cc7c945419cbd6013e16910f4c856bb97eeef3bf1fc18dbd36183d8b321305cee17270cd068962a4e22ef81487dfc22e87d0653bca3c2a0ef1d6f0db9d008a9fcfac0e4a062e2db95cca0cfa49fcb58dbfcf766c4719079d69792d7e5ea4580de13328fa0482759659046891b873ab469df34023079ba5b827382100736b8de5555ea9cd556ca8d2ebca48a45d6d3244e47616849d39cecdfe292fc4aacd2165ba3f62a3dd3d29adf251fa6f41195734d3fc7e76575d2e26de2a1084fde7d9a20a40891b647342ada9bad2749901850e5e224be4bc55662f407df90eabae544e7e7fca3731164426376f2c06a8a966dba8b668c5d01e6759da1d1dbc159cbf02b2a94db94b30eead2c32ec7c4f75756747f69d265dd86c743e9680f987970900c97fdb2001ea0dca2ae10b3b19c546144d3de8cd64dafe286adb7c4ce4d44259bbb100b09b2a223456d935f00b0f2beb2a356233078c5e28eb601907bbde3f22bd17dd58eb434bc7b6a2de0992d2e11a47ab803fd65f99f2b7c171c1a3f1c84b37af921ed9267d92f4242f8442aaff0909379b5287077c9bc91f214ab9c7fd7f4fa4e415a3a08253fa409559c46eaf09b7fd1a069e3e6c3f07c65a00a2d539c7f435ab1e94a994ff6829c6bba1e30fc863629faaab608a6d8b7587cf76ede132fc19221dc3487381e5086cb36467e75989b7c99f59b546f20c62ab278801f0fcfc6bbf1f9431bdbbdfc768c37703d7e654d4d114fd661a6864e943f78593b558d32ba543704ad0636203a37514da0a01076febdc9615e0c3709cd8b7de9a10cdfc5a455f66644aee1e999de094a578f037e063c2c528b23ecc8b7d9f6a81e46f1d0822f077509228ca4cf42729d404f123f194ac7180834c866eae38076599ae0c14563a32d70687bec65089f652384cc101e5ae10ff932b60c0d238034bcc0dbda4e4814f001328f6e8fa7cec0159b21870cbb6d74a07b86a00262621910ca5fa23b16edf201a15658b74dc7377aba3df38460b426078177c805f1fce884c805599b45f3c9d29e5f57f05a0db29dad53f99d513f578113097297b10cbc9a2a66d3b0cee80d94e7157629fbd319481c90e0a69833dd211d6cb47426cfd729e9f1095bf469082ff910a873e7d0f3adbb6ea875823bd5dc5c7dc1d23706f14f26bae2f332e46449c449997b04852d1b6b83c5e7794fe51ed4ef066bd8d344b507587a9d25c91eaaf5b098983c93ada9c5c3055d894d119750c7dff25ff4fc4aa33c85f4cdf9c79be3f747228a19ef13d257ab4404e44be2ee8a8fcc095951aa0b584ed600749d7a6c5efd96b4d24b7767308c41c0e9d1751ec04fc5b28830aec646d0b982c5c225e8fe705d1d5c66976f17f25adcb1e31c9cd0985eb4bc16d7e6ae19d73aeb908f803b46d7de054771d24264345306202b1e2f6b72202de734bc843db81b6d98fddfe46ed0a116e653dda5ff988b1d7796658c635ede48e223733c55cd07d7c666bb20617bcd70000000000000000c1d8060400000000b2c6c385a7ac1b3e811877fdf9b7445bacba0b0762f26b60861753cbb428d6db58ef529d1168f8542b89959da828591044213a16a21dab68fea0e2f6fb0fd76b4d05a6b3fbf1d593864441377b377aaad7263b03adb4504363e0a50f74981a1200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b26a1e2b09c03a6fcbf9754ba07e909a648c34fb78938740f006e96e2e08065ceaf7409083dbb5b6993dc2e1a7af47258bf826a23599002b4eca2557412edf7f719cea45964e6e23a7b5effe12d014795012846c599b19fb7f6608314e2e16c5b4d7ac7cce33c9785957f80e0eb3cb8460599351eee124d69f6a23816f46c7630225d325d89c2cf3ca718b5e4f48235f279c02041f4deb9d1e845b5798a657549702087cf0121e8915a2840434f8ce1ce44381d186d2437976bac45629a7d8b326ff0a2f69386ef4ce4c9d668af30f99a6b123faf0be5bb554e81dcfd3bf203a06710823f8d0646554132d866836b2a893f974db7ab00b78a35194796b2d0558c8ef7f021817ef778393dcc3be8d4d47265995b7ada64a95073eb659b657f50c6a86b01303083f1767ca1462acadccf1d1f58ee24efca5c6e2630fc47fc1ee92d76d4c99340320aed09739bb70affed825ee5dd07698961ccc961791e0dd7b50d2d3bc9ad38c022fc04201bc7df3309e87b8ed8f4e90b5553e791b8fa3c9ab49429b201d498e5702301ec466e455334d826c7edfc4db7ab8c33ab00ed9f8c8d7106db78275e0b683b81e176578da6d6a632256cb047081d00c4b35c3dffd62f044a58d91d8edbabed9fc263c4727b6ff49c21263165fe7e27e0ba42bfda1c0af4d91d2969bb01fa1cd72ea5dc37fc0c55fb632a8ba893ccba9e4062fca606c0b910934f1da70a24f0ffeb9e540eee2000608bac086b8372304e663bc924d4ec1f599b72a1307458c78ef85c0e14e69d0371110bc6a1093fbce3f8b166993058b9f2d50f384f4a13f2318f59e0c341a3ff03905b8cdf694193f792ec941e6e22e0b737e8fe08d4335bfbe5a3f4c550a3aa3a8acb78c54c17728670490b1ea7a72faa7a6168393268aaad198dabf3707db3fdb5a1e2f32886a1afae666b5a7faa67ffcb862b6f64141b384dcda7e4e778c89aebab97c849ec5f04eb6e59cae1b5ed37b803310363f3f50e0b40d4e7a94ce54c95da0fbb402975cf7a534cc36cf2c94c7a4eae2f932bfe856e036b4a6900ce55b9ae048a0b7809c7bedf5c9df474b83b2193d752600dee3877697be154a29d2a52ab5bc16595c942c59fca757b88fb0c797591048ea0beec29b5bbe23885261a0556e1c5301a32dc5bacbc6cdc7db0b6eedba4ec2ab862ae91a8d2655d99f484e790cdd3ddba55f5f41f049c0bf797ed58732d999b68f26ba52d2d3e1be7e8667506c936db25b90a9cbfc260e0120bb6266f7d465dc7648b3ad0caffa7cddb83cfc6b082a75b1284c9ad646e80a21c572cfbd98a5ed60e93736c29fac0a8b6d99fb150b2f1d0785fec0f83282b38426d90b6903172d2441e01f41127aae4610083e4c923ded35f51342252acaef99caf8f586c9861f62e9105416bd0f2747b4d1411130b8eeaa5a33ad4440667fbf0886d26b5e56aff9e04ecef7ba37d0af283eba511caaab56cf2180def5f4f12796dd348dd5f4d62dd2a685238d4712fa04efce7c8afd79b4b48d83e4828713142f83edc7f8bbd6f98ba6c14a8a9edf2db27edf99f807276cb54dc614b90f93ae4783b8c346ff17e53dff8cbd648b4b6e922b55ad73e0618cd3bbf9329db18896461baecc459452cd521fb797faf45b3f0f697373b0c6a64e1a97b4c70bed99713491edcbedc85b37e7bfeb236c30438ff49e3cd1871ef462ea11e389913c9ba4fd9f5c8e2b9299657cfeaee068ad32d83adbcec71724d99d8834c9ad47db84d595c304de25f1223233519c184ebf92f3286bb16e8b13d55946a84789643d4c456e478f5f19b241b6168c9b028a5e06c65337319d27c3771b88bd2de323d1966b176e832cea71c74c566f3cb22b5c7a4a511c0f31f4592633db52fb0e2ee65b51c43903d834c4ea7497273bce80a3861e561162a6b43787305b45d257bc9df91d0f1dad4b3ca42248424d081f76a9d3e543ccfa27c201b58242a4db6bf0720fdd5a88c9dad9d5bb491abdb1c2fdcffd2363188f29ecee10b29e96523331eb2cc330f9f70b6877ca8175995daf96396e74f7582905f036c0f10d2ab3a2bd9d5a777725aaeb5605834f9b7db7101dc290619573ac4d9c171491bc5dc791bf42ec9afd3bea0e5287124b87da542b26dc1a3c286b41dbc72a72e48197b2629da1878db13059c67cf9369704a945d740598fc5cad0ba3bd853ee03ad76c50d5ddee59b03b71ca02aa7119b37524db7c76732be5f76ce87984c91312e6d481e84ad64017c7764d3b7e68c10b2a67cd7b67df383bc3f64c13e59c10f76cb0000000000000000a4328a02000000001fe13c5c19f8629bc40bd568d81c38edc54cf042963c76f2259c31badc69a83c1ca147bf62c13d8bf896cd744184c5a0008f9502b8a07b23212aa694fe237894b6365c94673bd7c91cde5d3802a7a585c261efba18e2940388d9a95f4e09395e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f15c05bac924950a36bdbcf6aaee63837231da5896622b1d342867685bedb20af666cb4e4fe650b1691b2de893955bb998eeab33e004b756e143116deac6db3b9fe6e3a31c521e74535c400d15419c1644a12fa671b1df2f0c3c010bbb0d3f52497980372447fdad3311db2e06eca61cd7169b0f7a38ce13e4d8fc00a90efbf031cc360246c6525962ccf039ab93a45a1c54316056e090efe6cb27817806fdc5b032bb6bf02fc786f7264c994948f095aa63da37768562f6b7c4edc245bad98bc960a19b792f0e2715a2b6ff9c5d145967deb3b3ffd6c62a848465bf132df505bb991198e6dc0b27bb71d7a3ff9623f662e90264b77504b05e25cd3d79098b90c9a90032494346bf833eeb0e78cbe131e8b1fbdae381eb76862c4e9cd48ce1c4282f5ed031dacf0958eb5524d2a7b57e5b64de98bbbe3be7425b166eb6d8b3d9324d7152403253e9d8ec80855938ca33dcb17a189d9d44c50399e5a8cc1e6ca2ec4f603f44f03073d0e4e1e9c665dc4304265bc61ef7f2b2e500e43edfbcb1ca877050d920a4f03161f2c70b9488951b624ec4d6c3b656f04d95627503e7d23858bb022771a6597bdb0663f62eb7c051b396293ab06f43689560a86d4c4b87697796436feb962ff4864023d2436a9069865fb66f79dbc2a0a5cd3ee03077fd63eeeee01d1ea7af61535b2ed322a6368f654a9bb41357397c429bb3d3b9444cd34b2d3c2a250c07ac19cc01dee5fbb76b28d871f0d6da2b6d574264f25a323cc00611c10f2e90301e92f33ac2a4d4bcc28ee8776fcff992e02e6e92f0edb27e7defa69ca8462f29af4a633fb767c3d59b53053ac743f24ab5deb3162776df65598201cc7920c8de2759bd40e80e6e579c67015fd29bede45c5501405c3d935e311eb8fb7841f7dd06bae4c89582522a3628d071769f7977686dca0b63c78700922e0e0c092d92e2dc56d7420528fe38eee4b663fa03f80d2eedb2537098d1ab9a57c1bbf8bdc8e2a10b5d1865b7a443d189f8aaa9f74a8d6fef16bb277c1c8cd063a272b509a6fc76e3c48cf68adbde03bfec75900cb3db44bc9dbd6e620c8605e3e0046b14e767066739b29f37d6b644b0345b2733e2960874ef8ce615d76cec178426960ffcd194303f4071023616a4ae45cc97033dacd23c87a8c12ee1b897e57f2add27e73448c3c6d4e571b5e89d2317beba1d1c9c5ee1ccd44ce0afb6c7915654d86048144173e40253d82fb6455c482bb6e20264c8496afb0ff7055b7bfd5e945148146553b73c640274b01f5c8bae40894c005935de9a661c9b3be353f2be49eeefcc76dd2916d0ece479fb7fe93fcb287b0b213d015a9d3673612335a4f241bdf1fa2ba8034b91b838fe5533747138bfc53e359f98ac51edf7145bce9ce50dd8ae3edd205579d55ddced275643819c0edb5a3ba939b186f790731835070d11b1fe61c368d0613b2899f8dc1db320a62739471841f9bd7ed5af5238fa7241e30f4cbcac2fb916fb57c3c5d696f6a92df0b8653146e43510d92495f9bb8a705510a6b865cc629cdb3596b90f8246ec77e8fb81bab6b5b1a7ecc376d3cfed3bf2f603543c72c874485a41d8e4b046448daf3182256f24e82a3636a0c5dae41a346808e7426d5365ee171e4511a3470e6bd7b66ba2c0c38e20bd7d17614dd336413fc427f3defa47198afcde98891376d46c44453b3682f357196ac80d93ef02e46f844a312ba05094e32b405fad857b3d35e5b1a75673cd55d40683b0382310fb067a46bd0bf5a5f5e4a8bdef26579a99ba9719fd73a362c7e3ce3397b4c2b06119330cd05e65154491d33445d8f44652bd14d643a42a924cc0366777094f29ee2c40737d760d323b9f75d391712fb71d1e4637be0c59841086d0b131f9275482424398638a71ed7d8b2a0e0fdf596a50045a507a1d56ddf909a73b27bdf4b7ded57c898e4e0667a85fb926b0b87eda2d5ae260f4d09bc2bb0c15b3237327a1f1e15da97f4af0529a102f995b7abdca3f5d1d8ff64b673c7ebc6280a7ee034b47f105c934afa52d540bd9463a65e824b3e6962f956f5c0091b9af9c054d1ff395569f2f3958ef6639cb716d247cf97f73bd74fd96abfb8f85b7197104be081c5455bb94839e990a829097e96ec95e315b24f7a915f338c974c958572f1b146a7c16322322b5ef1780cd8bf8a5bc5bdb155fa3a5bfaf3b540dc18f90e39d94c088aa0483deae8f247f10416dcb495d69e2e4ef476db4adcee99e67f9072d2eb168191942cb00f7f26a8ac8854ecddee2298cf7c4d4f468baf484246f4dcfbbd885b865b1911017a07498dda21914a3b4ff49cbfb56434d28216d7b53872619763aa6b7e600afea67850d729c759f11a828f54bb3d5aa3c9d4b452dd96b94fca6843e5f2b027922252d218cebca5ddf50adf3e82f1be1b05", "ac5151", 1, -1726989944, "080dc8a9eb240b13bbcb4b6517ca75db04b4b125acb09fe1f6b6021789f392d9"], - ["542e0b5601e10e4d571ca155db82510b56bef90f9218470115a710a3bf9b737f851983ccc20200000002ac009b3f3313027eab0d000000000001ac8905ca0300000000046a5151510000000000", "", 0, -290389119, "eb65ee9dc7f73e05bcd9724c7054046c65c7690436853e784d9ea8cb7f86f15f"], - ["7ef28558030f4816a8cf2ce28776246dcb4d2841f8ebd15e95a859655f34bc76306d14349200000000066a535200536578485d14488dd71c97cbde9f60f923d9ca7600b5f7dc361c37b0434e32593d6da4a1b39b030000000452656300dcea2a44894cacaee08bb793b9f85259b682caed4d3bcd084d7ded66bd17bc090a5b815503000000076a526a6365006affffffff03e8d8b1000000000001650791fb040000000006acac636563006ea5bf04000000000351ab520000000002bec5db0500000000000000000000000073081868e00ec06098788390e1698139c275da042cca454de1b4d0e2f3b4ebbdab8883416162dbaa50a838127dc99604a9e2718844bdbe8e9f76e763b273ab58d20a3c6dc8452d5af014cb26741eeda65c652f05a1ed3d91c378133a2506de41000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009192ed1edfcc855b669938cf7d43d81e3a3b700a3b96c44b57040f6b0d044f23f50ae1ce6fd97b0be29ed545ad9814430dbca5ce23b63143453ea99b99cd90f9e9e980a513d559ddc36c699cb1315df185ae810359473bf6aad1d1a162a4309862eeca9c65d5180ab790dd18e3dc9c02b48b3149bf972e96b007b2d331077dd00228cfae77f5844603c0082d7589def8be11293514b481f561b82491939c5befd10318bb215bfd3aeb8d5b6e687f6cdcbf88c9c53b87954f404e8762aeb4a2ef87480a28886b43fcdeb8505fef386ac02d07c2da9ba04180f5b3532f494dc9ad78aa3c2831b03ce4b2afb3070004a012cad889547853e4eb5f5a98ae24b1d714d54d43021d10c3a71ce98453323f0d79775a96368fdb11827fbd9e697c5946176a7f847c0317731cbf069632814f785b6366b357ea45c54493e3b2aeca197d36a326ac42fd022cbb4c49983ee9b9729efa7879c0ebf380b9da12a7afa70e53bc28e95b0a247903213d4281eae1c87ef489a842452567acb09dcbea7ba46d054d134387ba3be6900206adf24fcea71bb8505244d87a65a6b7202d0c28b8a6c0f5c1f2f5e41303a7850851266cf2e6caea457f4a58879076c7fe28f36e4da7c2e65cd6b7a5056fef9ce11df95828836bc8460c46dcd8a3ef3d4f0138b2502323f466b2a2323a8d1cbae9535353264fca9e34e9ee1f8a47ddcb951eb9f378c1c2033dac4f40bcfd53c08e711a0ee39ef57957167e49b83449b95babaa88b0cf64ce8d9b843aa652bf2260cdc6550d9fb93e5eaeb902a6c05f962cd19741d822f204ddd1837a4f7d01a7afaede06c9251f36cc5bdcc00e4e2c00379f89c1ed01e1f3a93e4498706d1eb99ee5c9e515d7b9f8bc4d6fedc65b8e2ffda20a86dfb8dbdfa8b1cf7803950d1fd7209c04822aa4df65822bd8b1a66d7b14830922a76ba9f957b3032d3eae14bcf3995598a48be5a315bb099eac11a34c7e0a451224591cdb8536b778ed61ed81f2933b89e8aeb54fadd0aae255511f83f2102dc0dac5eacde259f38ec7545d0af566a8441b0074f7800903ab678abe4e7819f4da413d9c5c660dcac7e4cc86f40e21ca1513def9108cc7b60a82ede9624f84b3b0bb07c4f8fd19f777cecaef7ca0941ff97f5e443f02c906cd1eafd6353426e0cff3f5925ad9e2ed54825242756660f1acf26cb05c800d9eefb9113ac5d72b6e12a98c676f227345caf33ece14b7ee682d5e92e0c5b73df78a619d8fee6da4e3bb2a96b6d81c260429ac7f56fa76c833fbc3af4473858219030d479eb84b0840f4a790b1988258430604425e1f6b12e3532b81fd7800a25f00a2941e349649c1434c16276bd57fc86a6ed7ea7c1f1d678bbebc07ffce92d9280e546a12d5f9016600a641143efbb30cd8607c2f8c442e8353866b21ad19f3356f4448d0b103bc1bf39f8826aa4d3e61615afafd0e2ae0ad390919be30315807525746e31b3f0a1bbe45bca1b84ea07179ad0fbe217a1b99adee144d7291bcde4971952a4870fa13a485817ca7f1ce14606d91ffbae20b71befbda1fc2ef313e056595c0b073c345a7b5e65cca1776882c3289db92c387576f82b448582fb686efeb9162eddf2b92e85381b91fb95ba99d11fd3ab0496dded7eea7a2e33adde399113f7ac3938b276af51559b5fe33cc9f4f17a166b3e3651a83cd811036e5310023bbd2fd96ab803ee615b510957926afd529556ed6623479daea580a852bef7f6d1ee220417895ad815aebcfea0a39ba6c3b8859857b60822610082e76dffe19e189718c4575662647eda8831b136429ad046be6f94f99b3c9763ab40b4a9876ddc44ea1daae923ae69bfb6dcd1b4e097301d60a16464368d9790b5a38c960911086a2343dfd3eaae0d61989c10bce24e992f1bcf595376a0fa0dc0699fd56511f7dff3b21c60678037346d57b3c936021fdc5b906420aec9fa9bbee98edd896a25b4f12615ff7e0a832ef74b5cdb822d01896c84010fcb99bc7c7ea8a420459254460c97793962f7883344b9b11d7377d91d5956c5cece3c6be38d44f8bf179dac1678f51bf1177fbc8d5d209980fe29381fef36b52f896cbcf0248a5690572a752bf3b4c6d3a612721278ec3a0c9c277c16287d531c73095c94ba3e5652fc01544ad04a223585485dbaf9ee018f588067f9bc15926dfbcf31dedbd353425f1f0f72717c3714e402602a6535957866463f50bcad84b10f916bb28999da557a2ffa6eb668eee2b0f8593c5ac612847e6821c71fa946768340e09c793b503a2f753af0fb7260000000000000000be90b30500000000e32e2a9783b710b8c1bfac22fafa21ead92229ebef219c6ea411655d4251bea8cd9fa786711b8d61713c88d7167a1e41ae24e4250285c7d46ac41da9ddefb12466bb1b1a2bf008b186f6492f0119e9fd1a4e4f380de456489dbf4f35c5579b950000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008b81023016e864b05a03819d04198b70bba6e2dd1dcfdacae24200034cf82613f66468c5a563b2b92b4e447e74614cefe3213a68d587d2ce3bf84beb4b68c291e9e02e75ad67fb38e7a0551c527e0be4e1ab9e6d0c2e798d8ba00a0409fe7534bb6c2e006a13cb7d62f9012b25bfe34344f54926617b226f0236cf21ef39093020611622cbc8162d19e2e674195e1619441494fbcf802ef01b49050c9c20e82060222b106751799db84268a7548bf640e44c5f47c23de92618cc6805658f7524f3d0a0f3fbd7387269c7309a7ebbec0a98f1f7dd99747d72339ef8b9d1f304942341408a901641b2fa58ece65ebd80c316839b1b8307908f4d00544f9c46884a2b95c02285bd2bf2a7a3b975f5b3b8d74a2903d285f2e8ca8982444d98dd0310a73e51e0315cdfd95c23960bc472e77f1a6b62c8a8a9ee5db3f368ca452c9f67c546323fb021cd7b937bb62be123176311f08ef5422fef56be596a3f935f5d7c71ce70a45680305853fe0da697f79956a51510ee985126d1479be99798194174fa6b6193909c7030629c1de6bdf2a1e71d5cc5385d3002e9aedb77777ac4f6fe087a14dc3e277c0236e764e3d137828adc248611348e271801167434c30b2a083dcc87c143e6de3d00b27f3aae418fa0fc70bf6dbdb0c617b9c278c46f92daa329b79aeca4a2a497ca29b644d51b1a8d552036e286ab80043710cb4ed91aff667718b50b2d2a1760b826931ba4a9c09b25af19d79e62a3befd96d4ca96f8531819fba4466d1b61b3e5eb0e9441792ff7d7db57b64eaa8eb77a73e8c5527b8c28483dffbe97c14d9cdb10a63e7218f43efce2fc8f10e5cf11628f87619ae55fe464716fdefb52d3f7d65369307dbae93cb37d571a80536a9fe07d4184d44c7167cb27fe9f40503ceb0a248959c0c760dc326dc8a18124de4993ae316d6730d2240f43aea503f9780622e1bef4bdad926226f8d8abf8e4b925083d5d9a9113c960a7ac880b2035be514f42d68be4a8c442da7d2573ca7f52b122b15596ffbd123f2b0136d0e172c260466db6b54e58d5c5dc8e720c9326408d0bdd3551ccd92e454c26110b335915189d826642a0fb872de4c05c882affb3954367526cc7c5f92c53bd6ae8779dccc92b42215b6fd2a75f3eb2b42f074dce9f015fd119657bb307d47c9d7156841127b59cda59a41b9aa0c7a021e692b630094e5d5781a73fd8702e9fab49616b79cec0a87d0c317fee94e43120f02379e912d2f6446eb764b38920550d0503a9f28df3f597f33b7ebb92d988f66ef222320408ca1d54fa098093984ba5e00f46bdd806012247bec3f7f7d61f23a0c98a3c728a34ae79e09fe9d712c45fddc61b9fc66ded86d3f64aa692516c87cf7cbd90c924d75ada478b8777fa8e8d60c5ec227fdd130a92f62f7e4322a6f48d1b41411736b9e807f8da0b64a85d28968739a88c234cba392a739976fca6117cade2782c330b3724938f43f9ab7416642527dc037d21456f937811acf9a934af77daa5fda782ca4a6a72e60cc7ade7dcd8de154658afbe9e0a706648c96b33f50577df1b8106d9bc8bfc13d2a3a8dcb750d592ba52415ccbc778c99bce559494e221ea14905a9b91143c146edf22e8c0693d932947d55d8e465ce2374ada079dc0955385bf7d4f293cddcd0fb10024b52626ea64d31b33b6372d76c55690d080f876ff1db0662645435a6d987bdecf39bde6584a90f0ea61f0ebc2a2bbb5c891a0b02fd760b01989996641841c0a1653885ea15e16a13eee741527050da93f242bb474397a0ded294a1baa74609bdf8f4c97790c0f3aec9f0ecf5719263e22dea22fe90008520cf165c27d782a776d31cfbecd486433ca9c3e7acf505e1e6efb158ab796df653f6827151117a002dfc6388d928c28f883d7739a7a007f32d73e7c9fca0de3125d2816556c426198c73458eb76b5a7433a48e89d6e5751192eb1be2d4cfbec85c4fa3ebdad2ef181690117d994cab9e220e18fe288c8aa713b2e0659bd9db0a33a0efa94255bdaa7d892888b211878cdcf6597dac02bfd9fcf7a474bdde6890bfbfa1a7926ae6ac487a1eaca7ea9a916d8380aa3b1ee770c9e459c73945731d0b3741db63ec170deae322cbe26c65bc2261854a7e860345d1a5d96767bf84f7d8b3d1b50dfabc87dd47448fe681f91b958725d8c2e7719233ff04362b9c2f13a31e1588f7fa46feaab93ccdd4dc765a105ad50e574c09dc52f657903100326e1372ee08a703db22d262286c9f7f22bed25fa56fe729fa54afb1f45656d3a6c254d92c427b74c9f7fe91c757d8b71be355f871456fb59c8fa5d424b8072a998d0678946318c510cfa08ccca387a51aff5be1e23516d208618e65dfa28b8a37c9c3aa40b611ce5ff0ada5aee50356af6bf65a38369e73005665485e1183f81005", "6a636a51515251", 2, -1238398950, "433b51e68779b2a9de9047eb2e4eff77a2c9390026b6c7cb6e2971403edea6dc"], - ["564e568902c4558bf19b5bcc39584a99e63ea792933b27db00751745ded3dd2325f0a77a69020000000165ffffffffc38e69e6195ce85311996eb0fbcfbe598c79bfd9571310315a8cfacd1c37e240020000000465526a6ac4745686014ad955000000000004535352aba1ebaa88", "ac526a", 1, 1983747537, "4ee8275452e77cfee2478e0eaca526877d5ee02c5fdb4ca47c0d893e76d3b41e"], - ["f09ce524010fb52565da3c21d4ef247b662da4ac4af9ada04dd9166a2da1db07cf6f79c0ce020000000365ac52ffffffff038dd9ca01000000000253ab3acd8d0400000000035352533cd43105000000000553ac006a526aa2df67030000000000000000b405a700000000002301dc8ec30f708b1dbc94533c333df59c7ee67de17554160ada87a7c81cc18b12bec116ed4ed40b3fdffa4328bd9a335a9c144192fdee8fc71c6c4c2205d5768cb03036c6d106f545e0d58fbc446e27e21e23e53f8b9cb86badeb3296ad3932000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002da3e3c1ccca7b4436555d780b11bcc1c2329a2415d266e6a5e76dd73ac7298620205976fee47ec88f57cc7376827467455872bad4f7fa98b05e731f4624d3fa031f8a4a5bd6f879af1418d83cc1cb0470cd9b4376cec24c9dcf76f5763f5e2f7308cf9d3056ad1bbadf4c10cdf03a3b57bda1cfb019aa57e1a6d27796c72067020cac9cae53a90a1c5337407f9de718ec62052df2c50faf224f3aa7ed081412c50311b6bac2512314f5a23705387f032923352ee99c4f2113cdc22068504fbebe1e0a18d97803177f3c6e1c737eec952f1491f21c139d6e14922ac6c3f5eef2b87f600bcc1abc2891fce5a45555e4528aead595024e214a11a3b96abee6e7f09c30ac03302a6d4c1ff0c4c441276c83a582e9a86692375849fdb8c22e7b5bacc9c53c960310ffc5c944ef78f21b3cb0bbf8d80a3aea9531add3debdc91cef4eebf25272f80227f0e03ac052d35bce293a75da7a6b9c62d31bb16e8704b41fe90017e045f911022a622bf2ff5a49742439078d6bf2d1e8754b0a1d21546b38d4a753012089415b0217babfbbcfc09dd1aa8019012818d675cada7cd321fc49c258a344b03ccafc9beadfc62b3b55f23b0f04d424b06c1c8c7ff97f727ae24ae869d132a1d81663c8a4132b6fdf01179ba59632e31b7d594d8d5d45ab0bdde2baaa4de035ea438029e0d0266e5c86a89f260ad05146b1463cfa53d42624fc76cb7e45a12a452cd9c591ca2b2ed8b670187664680cfb0a4b7407743f219f8301c6c1fe43ad654b61019bdfbda7b1cd6c39b65c759c2bab8a65f691904eb80f2607a493523dc1c34ffb2245b0245372e18ea4fd9da2ffb7dcd97f4ab4d278f4ecab3878cf118e078039ee517b3a6bc85e8fd88aa064b8da3733c56984d1255235856b368ed2fc44353ea60f389459fcb46e5eda3519a3f080d8d9b7c1af1e643d9e53065744f4fe53553cc9fdf02c0c98023ad9a7d026d275dd780f9fca2bd4aa87b30f1013a103502eb26235816947f3b375f2d2dbc7ce0963f377fd6aa4ac937cb9d3ca64e0d7df6cad739081c7a14669ec1bb18eae9cabf6582385725e49b6d46c06e347f84db195e7227b5617cdacca540eeca6d17206311b0dbb6ab796aec09fa054ac51c49bfee56c5fd82f1858a144619b50c6604a521a1d5ff1392f3d6b017795f377f87fb1b579ddee9011f14f52c1868e8c0800ec141caa8389550d4525914f37d5b91e9c86e193ecb4754def1f36a53ca421da8eb22f78b38fd60300fa7ddabd0e89219ec40b77677304a4afc3d1c345669b6b4793a7a32a1ec8074c47fff97c659ca17b9dce7a65999d48a64030d07f5c6e28b09cc4275bd2612279f48fb41f0d9ff3d3b3c1f83203b5d40727633491e437df369cdd101b213b038b19a35ac51a4974e471129b207108d1e5e1987c80adecba5773d34be5e6a18c73dfcb5dcd9ff3248624d6399cc542141f0796c1881413771571665ecea78264848c1e6756fc899626f9d2a71121a751a7fe56e13bc0314e04050f36d90bef059fb943a13e252cfd1f81708db2e103ee48c5bec45fa06e3c90441cca303a8e808d9a1cbffbe0ff5429652eb01187bc0683c636a4a764e69e34c68d4724c4854d3b5e03f7f3f7e2e8e634397a26831a3664f9c7f5bfa9bb6df2c37b8868c2a680e060f272543937408525d09bef9344bde486e3287571ea69d8b0cca81d41605ed7a651bd9419abc9ccc27717616041ed76f186fe9b5f8c5b90c2b0064a8f9e88ff6f4b2adf82b9f519c0d49288bb0312e75dfa233ead8361fb7607d4c9b4d381342017dd6c1ace2cf8d13aa8561ecfb7458b640b84feab05680028ae5b12066d554885999aac901bbe222a72fec0c30218f041929685ec2463fa43d7c2919a806bd68de2afee88f8b1f7210c61ca07c6ac58e177503520e5ce0409e4c469a878884e5e84c16d91adefe9975c943fac86641572ffb13d160a80df4d9595b06060065c45fb6e05ddaf165f5f47454ed1f0a43b58528f6fcfb631284c2287fa0c296bb2104667c0748331dd90858d7ab609889e19a7a75b74b530b1450b4b362463a7843eda96f94b817be7bd7b3d73e39cca1b4a68d101dc1820395090105cc2ff56a4143b15d26cd78c3e9d9c3c130efc028b7fc8204567082d39e8fbb13504d636cd2c87470003174a8754da3359ccac2fdd7e0bd8abb9253b4d9f7c30d928806a6c465763173e700b40ef109008dbac2fdabd2972bf89c453dbdf90fa0c3e4fcec34818e2a80e9a5e3edc91702891be75741910c18ecfe9069e86000000000000000050709c030000000078a9601bc1b69420f26e20d0c7811f7656226f88f951d894cbce91952dc5dc2d1eaf2d7803591d1b1e5f6105dd20bdddbd13341425eb2d1ee017c58988978ce38f28b426efee129f1ce8839a924f595050e379aca932a185bdbc894ac178db2800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000596f022757748b241679b22ff7f2beceb578804171f7aa1010de0ba663701f9c3ab348d7b67629bf10cfad0ea2174f1c51a19e172e9e79b9f668854eb593d578aac1e86cc707aad6c1d244b577fb5868943bdded553fda72a8d42448757c21f05ebe7abf58a171450e563702f61ff6176d021dab6a24b49a7c11af15f99118e10328e2160c477434065079ef4aaabb293e216cdded92c8dd3c41da23a85ee8abb7032f584afb7d6a5dbd3729da7fdf64253a6e0093b116e50a9c41548b995a3dfe440b1d04e2981becd2797a79ef9cead7335933d135fa8ac6abb389ce5e3ecfcf78e122bee486c659e8714942a04d70f672ebe2f268d8db8f4868007e451fc674879903184186491d51488b04cbed0a3c4007efb9088b5ba29ee30b969b4ea2cf945c2e030291fb27c8542391f96d12a7ec2a2e8f6dcf014deaa4f09e37e28857a2bac0ac030b69e6cd449fabe7c6757d461f16f52a5e98c277f6bed82abc95f6066a38ff8803257238306ca51c0854af4f8a16d3c3405398d47b55f22be850c928623c40d418030777357dac57d1c285594af87043f732d465445176359e3c1ebc30584a0bb12f995fafde1fbddf341eb2a854fd07ca9d9398c546c610fae3b57b5eb2e6ceb9298ab6af14a6b58a828ac825bd81c7fa6ab09b9ac360e2496d481e84b0c1e63690ce66dd4e21e396b07f555d5b0e02dd1d849c84d460c0ec80b147b03278352e275756096a1e52808dbac3a77240b8259cb7861157e3263c6e8fe339d0894616b138d238593c75ee7d543019e21c6669548f6ddfb2511a523da46a3c81de91fe539a73293d80c0f146f2c30bb32bb4164da5cab5cd0c1e86b95adbb3666292a1647bc99fd0305b62255e00c484e3ee506f56d68ffdbe6800051b5454b813d6e53ad1949a8a55edefc5e0028cc2e823cc0afdf8456f3ec24f588d8c764bed023f081f5ccc5ba5e58ba9d60ca3ed97b9e4e3b20046f6cbcd766037a855ae94ccd0d809d8faeb272e36de845ce91f141bec99146d5240d3270aed4074c9d853fa07b0e67d86a9bdbe2979e79a379c3eeefc9777487aa47caaf92213361e7f5d39e9d43a17fcacdd8274840adef4cb74ca55d05446e1e85242ea8c9628abd059e28e96cbb32e12b1908430d4387f3508024f0eec57218c735c5428a4f2eca59f1f140816bd97fdd518c5df706aa8366dffe73d6f6d23ca93627fac8448028ed0cb5daa8e751cdf644e18cb8c6fe498ea67b5032bb22aa271055ae158b2c3449f3811e3ec597d73e9ba88149a02102b72a8e0f4e9025b8930c798ed3004687fa0967a402214b1804eb105faba46ff6d038b7d8b8d3464f8fd43460a62ca912fdfdfac787f595200094307dbd2927750fdd26fa88e2a17dedfc8ac690836a6243d4e90488e2f1531ebe00693337e4f9d079567563ae449872e6ab98920f28e67e6ecd371a25e3def9833d770e347e45b8ab616cb3ce398be53611abbdd7bb1b95da7da53c0b900b7ea6d2471d3051407f0c5121814fbcd4d6794ee08b3d9753681cb743be1fca9112e7d31a7bb140487ea705133dd66d5f8dda83b87cc8d5c57d640bb2fa563e57411628bba3e2614af0405cb67f6995963b3742ad268e4b45829101b6e82833f5edc40a4cd82bdf56b952346f6b5d756988342829a0bb9a5bad19207e3c9a6efd4c610f0b68bf20d6df6f4c3b991673f809991c334ad7c40f93722c93ca16b2758ffe032eabdc18775962d5453a948a0cedb79573544b5f4b18f61129461f106fb29a29dca52632858b167214859f7fc1d99cab6f7169519b346fc83cf228344410752e34ee4800ecbbecfa7db316a1733bb9ca1240728fa72a9b855b6641900d6879dc2b7bf2d13590b40a63bda34c2cd82ec506a474c0fcc61d68a425302f2d07c74ff09ba635569090e4419a02a47018c5eec7f990f6d6bc93ef167ccce988f4fa83a2de5a171767ec2ec72217aee22fcfc6e0417c5a3433fa27e727418da5696e0bb089b60a038bd4ec76e605ae29f6e3b4a0c71b4ce644bc855a281af5847e01b0da92f6dbe9fad7571b4c9cafe43016c107c699b60c51d852185ecd7175a7de8ccb327bb2abdcde1172a3f368a00b9981a88f4b0a503e5f8527028dbde3457d0bd3b2fce639cfded4bdd5713cb0a0daf274d01e8faabb34eea546b09efbe20a12b477193c79fa761f7e75f289d43c98cb4a84625ba1d71e84158766b30c66c6ac88af0e29bffe3dcba84be3d70bae17aa7d9bf211b9fb49ece73aab4691d854560444b026183adffd4adcaa40000000000000000656659000000000001dacee507b685648a0bfa5c9143377e68f50fc53b7b5da9467a383b02100eb12ba5da98ec2fcb72115cb8992f3061637c2b315853b8787442aa40103e970f36e1334d3a908b4687bf1b02ef25159ed2b3f9fceb771a96c3b6ae3eb2675f80bf0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000074f9281e26388ebd67b7fc1adc45dab0be1cfadb225a22b626f79e7195222f286a93114e9d445d86a45f630d7839e44c5e12fb50095098bd12c5eb5db813207977c69c1ce9f506b2882449ef561b8129fbda46692ba0eee65ed5142dcd972a9638fad0ff4aa8c503068b6c7313b82e12df3561a727a126da00c291046bf1a3c0022da9bd78528337bba3158de197a727847b884afb73a7c5de6b1bf91146a1a8e80223f2f7d51310d5aeeab8d184be20390e82ef208f8101a8c977cd68958aec3fd30b2b79fe514d98d2d439b1c432c6d5b31b2fa2a90f88f0e82da4d3d7f21a67c8b41abb97170147fd4ed5a51ad77e113d3771822ffa5a08c1f8be95cc02a570cdf0020d2658c7c5822e4d1a8746bf83aa4d63fdb731da9262d5faf9f2f3723ead39eb0205bffb8b39560f11dc22b4bdee459eaaf4a60314e4bc0c86918875ca3c6cc0d402174c9ea3f0e9a3df6ce57cf37825fcef57f44edced96e7942e5e1a0080280a1902004789dbd4b5abc9f457da42af0fa9e768ba0eb556cc6a81997b7cf3273270380329ed320877c77faf82f1679fde0304b2218747c9eb3c09a7972d5b680c74c8f47dbdaec057fbcb5b3766d5d0100809c89120415b86f2a33ff08435ee80f0f61e3d2b7275818729faed9e18f476eb6bbf512400863c1c41927de066e8f82435f3040994d8a0f215f83de1929d6e52e3e05eac767dcedee3a84963746ee055cb34b18ceeddcf0331b8d8ae2a0061413b3e3bf8dd4b3fa1d51655f5acf1465637f9b1328998d42657631f8dadff600b1f0ffd633abb12960466b3f76850a6fcea705a71acbef608609acd159a18e260768a568700d732665b8cdf786ba19d3f2eb9e7d94e264755adf688e7be1019d65658b97cc88f41cc084b3ad39901e4cf48eb9170f54ef14a8dfd427ee58bc92415326f264073d45ad6c16384de06ae6b87b1aeccf5223756433da267e158b3b0935df9db3ca4bcef8d259c69148cf93c25ce8cd09d431ec8c8eba51ca8581edff9194d823b5d7b765da0716ff9b61ab9d2957e5a1344b74b912a46f88a12fe80b79f5987cd83537d36a040c3908fb3a29cfef4860b94b3630037388f418fc8f9da9deccf07b80d4f0c671ab651b4283cae35d1cfcac90e225a524fc754ac5c98abfdf1f0257c213c79c08eebb75122a315994dd5493c15e9be03b6e7129d3ebf7d021d60be2f2e5dd6d11f0a7669190d35f39e8881390c0cd70b1635f255ff005f4d0fdb300a6880566a87f809c76bc8d715bd47c8ed83d91b3e257447f45cbb0b3773b40b10a460601396840ac210f5d1db7d80df5e6853c70690cbe305b47a9cfc1f0119c68cfe232e15de169aa7a652635871da67284e1e2783ccc21847e128b308355bf175a1c68d4049e365d52a19b06e21ffd329afd7549c88c3b6877e770a51d2e57c9f0372ee5ad7893e0b076a6f6a38cb56e66f2ddf2c66e33792ceae5c4de5f998c95acccba97fdc949dd18317d01161e9ec0b3ec162edc144f587835fdfcf953ef03873163077f229d22307559563ecc592b5c7e9d32b389576aec190de1256cae7932a705a9633c29de94424ba04bf140f2000da2b07d6212344910eb9fd45a507ecc90a4779a03b201a0bf333b8bdc80a4e922007ce1c679094dac31f828613d4c30b1e565dd650782fa14216946e9bec6be35083a1c3289a26ae669b27c9dd934589f227d169d7be550c2cc2bd0e63235a1a49317efa704e4b631ca34fda21043f85eb166403bee6ac6b01a8f51bc492e18d7b8bf10ed4dffd1a80928457adc9c96882baeac545009891f94f0433169bdff321f3d4af1e9c940c665b21b14210fd7215e339205197a403d79f2123c531a9f952bae70bc10b6cce8c8468454471a22417b022d4f1c3120585986784e0ea9ea7ba05ba224f61f73c0309c805031da4b50f19f6383b89ad5a64ddba00d98f46964a4754c10bcf40a86bb5f69f5abcf98f5c5a123ec494ea60b3ba2c8246212637d7556b156effe113d54d74dccc91a3ab573c64c5c4efea94b615326f64b2d8a8d99c5ac2c228cb89107671fb2d7fefe0796205e8e840021e73de6b04dec02b32489f4e6abd47947c9784ce364db9c592e8c5ccc1c48e5f2de56012a1c498968d4c3734a9ac46022f92f274fbba044417f932c7be8f1ac34eb327af743c24b92100c67b4aa5f92a27263226403e8c2bcb5a93f5d930ee5ab4912b98c96bbd76514b4b4d0e8ae42a01791674bd63e3cd0066c3703785e1be31bc09bc72b6904da5871c541a4f5eee591dc8cb87b8f2805d093a17ce7914f3c528bed447809913c8a5f6b3acf47eca867ec7024112bb88badd7090e6680251bbf56246a5d6e21e3b2274111d7a6cd97d4f6fa37459f40950ec06b5ea77a8c7db6531893b345f59fc05db07672c384478ff580e", "ac63526365", 0, 1568516646, "2397c973c5490211be7a4f81ab50cc02aaabb65eae14b749ea1664ece05eb747"], - ["00f10ccc013722ba86ce5fafc7a7ca8acf30808bc2217f5dc3be2e32c47b122b3d756a5e1b0000000007536365ab5252532939c340042f507e0300000000056a65ac006a0a28aa03000000000300ab635b60cc020000000003536a519114d7000000000008ab516365636300659cfbda60", "0065636a51", 0, 1234170252, "b12fe4d831c509670e41035a935b6e4ced174fa9713e133d811a1eda403f1ead"], - ["407b037501a54a7da2ca4c3de1b2c08f8f34a504525a931f4d1d3480486b23104bc8e5f257020000000963006a5265ac5265aba14215eb01a556b0040000000005636553636a00000000030000000000000000d2043205000000008ec924cc3296f1c3443ed9f5b67b1299d50b0710e394ed20e4589f48baf97c69386973110ac533589663e74e0eed3f2c133d3b9d27ccf270dc36f12a10a1ff96a71184888ad3c4edb083b017282e02d9615c0b0a7b72ae72f330bfa034b4cda80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000098c6defe469e21c92087b182cb4868d70faefe30dfaf1d8693ab97a7aa480477784ec6fb02d3dbcda213f26d1dae517480aef7569254141bf5b590678af7bd0dd972a3c1c63c6607e99fa816aa5f6fe615a7e65389ef76e112dade76b0fc0040df85f8a97d4f45e06b43ae6ecabd9ae472d4788615eec01a2e60119c43d344a403270eaaa3fddec50954cf990342504383927fcf2fd9f0677f4cda6987fffc2719032a6f1d8450c1b161dddcd69e6bb17ae398e24a63a308e031f6fbb8d42a4278180a11ef696ca22ed0bab6193fb74e698642265d411882c2b39e69ee7e4b1f5d65b72b1e7641a9b9720a0054327e126288bfb5b84c80631b4897a8ec4f537631ab4e03202333f73832d4343cb7c536ad85ce6cf988e6f5cd3c8e9e9681baa692e63b90030cb03690c792584d4da93c5389446d4ad6af3dbd4a5e6c8ddd06d75f89842fbd022ca11a7545971547a9ae18c7718733a334a1f1aca711771276f8e60bd11256cd021e116ada2a7fe777f8743058887c7a2db81931a84aba28e653f72ec0520d81be0326abe2d48fd5cb859544253293a4fc7f128662addd50076a87b17ebe7a3182799231c5d892a47018ff5b7af5909f3dc892adfd752fa26c8dca6390b263b9be0b6d8f2fb50ac50f58f8ea736c8559e274ffdff0dc41be72ef6fa0a3ee3f68794867659e3b20b775cc738c0bba5bc9ae4d5e581d7c4e84d568acb011f628a96997a14fca5458d5baf9a50cb2ada4c3fa8cdf5a6be086813f492ba0ca6f72f95956bf2e4743b057c8520b6cfdcc491153471aff9997805a286ab0b62b8493a69bc30529d846cc2656339289770b7c7dcbbc29275b5d91f191a4559260e0d1371ff815249c9d419c9c5dfc22c63ab24f66fe31fbc3c5a70586a869b23c8178ffe9133d1dc749873b089dab1fdf327852a865d4527613981e8460660023dc38edf7409cd1bc8b947977ef50cf6bd65667097655dc5ab81ea7a3e0a00f1729417706c4e6f51079dd53ea1926327a243c812511f318004f199b9540807da1df79fc2b0fe2585be589295abda753705db84ea2889712c679ad2ce367fbe96f9b7f06665b4fb4a068495addb02a6682247b15d57fb9a5aa7cbda90fa6014408a14515bd41c1503bf0ce46624d5f3ca5ae2c05ad0048b4204c1e541d63c0f7e4efee5976f17eb188414d9bd701d054ca88133b09e4f5ba217782de41525353d447ce84be7b8ec2f4e940a7f27852377ad3b048554423274f7728c2445971acaabd61900b044d76ef48a1bc76fdad5780a66600969b17170126c3c9ff6bc940983392d34a653fbd372d3752f3ca093c1f056b9d8bf56e1631a44e32913eb68f4ecb281dcb8d56a8236854337e95e6b8dce348e6f8f5aebb6f44267ececc0e65c5c619f8f4747bd22dfa2c12485d12d55eae8d59b5b31a070c43502bd404696381a1cb4cad6d9c3c14fc38bb903423e8fd633423003d4ac7e8cf3ef3d9533e122b138f7bb3ea522ec1cd7dce12b804dc5126f7c51733fdd1136b575fd92397990d2ddd61e3b021cc9160e04cb3a2f6f36d399e568900067cd3b9cb42521a6868be930414a5b6f5230f380bc71a49ac0f0f5bb99a2af2090bfadc26a367cbda542f7f76545a737f570e7a66a97f0f61f7e7fbfa3a070ef391737e4889a97f2e5083742c187ebf0bb5cd4d764caf1bac8dc159f5672530ea6b408efb7a37698136473247b28a8d19dbced435d7480953acaebd9dc42a37d3fd5e61f3a193b237139bcb7c23a1e7b754d993f6a49ce1e46c4d3ef6b13bb177ba4f1e4b780e712926f558a6da6f92cd5798350e5cfa30b4d90bce23671f1c1d44f2f2b8dfa45b54ebeab7603c51450c5582e1fd9788d38cc32367bb4d89279c8048675862ed77c9e88e327c60cd6d93932da554528e24a619b1608bd44274e120bba2b69e8aa191cf4f48b033ffa12cb022ecc16831f73ee06bcc2baf9cb4a18acabce311005ed4806e82e534181a696b76e50f4941e81e5b2dc1b44bb9a46ceba2a085a9ec5aa2f8e696943ad70844e256d078a0fd6c2c5551a6770ea8fec48846221ed7be01103b1864f027c8a334bfafff261e06f26c50a1af0f49140372843910911d8e9a09c7dc10e0f9d4516c1448ade32b8d251e5d96db405b801d2adc6607ee6b0da1855121bf1185a5e51367af1eed1b9a776f0755e7fbfcb71fdf76816202dd18670b94df76767dda342929908e2f9fdfa9a39db87b29eeecae966fb41a20067f839a2a8eac3851004fa4def2accb6c8694dbad1143695fc9138bc600000000000000004c4e430300000000947c44d496deac05f014fffd06cf8348183bd0f9c78ce5c6ca538fdd444810679cfdba47c2dfa2bb8cccdbe3d20993cb4d128ef5754675b0aea0eb43c0a50ac228bd62e6012de4b79598f1869177580f37bbdc97131102ac7d116c72f35005e400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ac05b3c602fee4895837600b196631d6b0fd45ff04e27a05a1c0f3c302ca7bf067efc33b7665777cd35106d7b8346c91be5577d1e287ddd740e1278acc28aadcbb7edb0ef8423f481ff676412df05641c17e5b621ab08a46e0cf5c36edf7099f23236ee69df4a222877e7d49db0659b539f119a6565861f4b2601e774a468aa7020b7cbd67c20c79707f9522b72705dbdb1a2e17daf146876b4dfb4016eb1e5f0d022748bbc054576bb99a59ecbb386e8b8c7c7b6881bf8e6a01e2a4443d0b1b30520a02a9db909591f5db3426f9f490eba9d61f3f7da53c841fc62f6c8760b8e273f8220fdd6488ff6e892732fe497dd8f12d548c47b7b2fe456a5bffddfe9decd03b0208e685569b1c6ae89423c42ca7be294b46d67228561c9730819c89b3d38ac97e03025d56612bb891caaa071146080593e94b7b3c40671911ac4d922608b986180a032ae183afa7b8f9076837a3d5ab22b479bdb518f7c07bb6495a6e378b3a41b8e303194a5b84bc85f7f7b74f84d23f152731175544b22a5ce0876061ab3dc3b983730301ba21c8f9dfa12ea3c6c4e8ef598d790aae7fd448f6bc7ea2cb4580c5f0dc0a759a8c6f35b4f45e549ccbbb10e1535ac2a8828cba8c637ddb391ed759f7864e97d108bafee90b5acbb5ccbe842400933f2080ac443e4e24cbbc9d88a28d3a192184137151115ae4bea75ee7a4a246ce5c47540ca54ff91b73a5118f805ad1df2a50f9fec65430783bb0f3c107eb2421d41d3b7f8879fa237c8348e6d448c549cf0ef2f38594f387bb79d828569c27440d601e43b3736bc1b0b9b44ae5ab121aecd7e3d808b695260a175cb75ec5190902bd1e34514d37d3f3de7181d4bdbad00ad7423a5477810fe0d05e5f7616501d1689b09f288c4f76d2659cd9e034c98324193fe73934c69313acdfdafda28b795a02b206c34c98c075b089ce592a3194614af7443d39ce33f9955a848658cc8ce02d8cf03534f50e0c37237cb7c6095ec1806b5c1f1b46414c21ac7e631dea32425e290133383514d42dc2c6b6ce1ac636889d9d560254888697438e041b26b5fcb109c022b2f02ff43867f8eca6cc57a77033e38e700608beebe5cb3446df44e44d949c0a37384688938f0afad67c90b00c54b412f6d8169b4fa902dcb6967a3ee6ac51810cf9adb5ee93ef199332c9b666e9ed70dd889c8aa7c8592c9c62daf6a6665455d721520de5f75785e2ca6643fc6bf33ffaa9fafa0df03ac640b10b35979d829e0ad369a98bb85794087030f6c2c7a4757bae894f375070190b416dac0e5dfd92e14095730cebe980aa608b0f975be4f73f3cde8b84e51e9305c0eb9488a2c491abbaa4f8a871e4033ab3c8f1dbc7c1f83ed668f1ae985683eee4c14fc5a4b4bd293c676b532b7c9c1044235d8cf1354904b2646dd810ad8fb18d63c5a87938ebd6781f81e32ce57db38a777298a6b1485014d4c70581c764fde45039a0fde628b1551f98dacf5175c8ba647be7007246949c0b5c8b24ff28482620b801aa52cdbfecdfcea82bb7152ca43288e2918d530e4119157ac0a37517cd7c951082b0accfec225d00c3707f6176347cb0dd9d52a4ebfdb33ba68f61b929746bc7b586a72d7715b5c7550c109dca82ec3c3b86bacddc588f56aa629c1d7d463453a282521f8c2a2a06b4da368fe325045365c5cf05502c4f4702bdc4f7f0024df3b5e8a1c57c7f4fadbf2b7fbdf8d75e024a2c52ec1fad0809defd2545491d412969728a43fa4092d2b2bce8d87439241763ca9ddeb5096e8d93157e05f3d19cd55c0ec956463a8c8b32183a974041d7ad6a08e60d1464e84c3ad1c9809ac6999ef48c130966a5057e7304599c9e1695dcf657bc77540bb0e1f530f8f4eed6004e689cd4c5aabfacaed76c60a2b074e90805cb5046bee53013e5a15b3052b82666233a18f8e1a659741865436f36f4b2c8a608dc325c5ddf391ecabbd1bc7fb0b2636f3a06980096a2bc2456c1e2dd43f94383e04d2654db924e35ea4c534bf96d9e87693ace6683ab997aadaa62fe611a0ed8b7a1161eeb79e560872721b77b8569f01cc2afd2edc573004a8189e5cfdab8f150efd1e89e5c899d5710ac70469fae24a7b6a34bbf660e46d8ff3b36b492ce35b03819a822422cb9f996cf32ba44733e4d4a6367a0fd9279e9b1c2e5af8619968f662e5da79b4631c65dca262aa21e9684c086e048b4536d46b586654452437d164ff4c52efc3982d45a9cc591800df3b1fb8a0fcd92143e199311eac54236a3bf3b496c1f10137020cdae13dbc6a91b8004000000000000000000000000ad931bbe068cfe525dce323cb6f624dfc5a576d34c67b23ab2c489936308fb7d1eb4e29b837a382096343046a199fc996c4481d98e480352b8ae2798e983b27a7e47d01f1978b649ade08ff8f32c804217d0d25135c2b9405e58f134348be57d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c9978c95e8ec2a2852965c7dfde8868f6dcb0ae3948c605c60de93aa30b4475abfbff315cbf6934908ab224e5b3995ea6fdea2e024e4bff0da55a5f6f6a24b014378e47418468f474f39f048d5628595f3ece5b2b5d086ebaebb563c94770155b1a657cf52ba9236e4125c52c263654ca4a853e9ca88e54c36f628757d16a1680214458e4c899201ed230ce87f3ff329e67a605e25ae84352be4f4767cbf2e9a80032bd347bff92554b187e3059fa191467da126d9e51cb17617bd82e61f819f06990b2ebab0a858d70e0832cbc9fca3c42e50488cea5b1ff845f8f64bac8b15a08bbe1c6e26b08c98db7fd454b3406ccd10725acd157c893f9be9a4cf49c5faf02b12021cb076b5fad8671a7310fca4927015a954de4a083ef121a5225a35d5f5a529080222a3438ef979c93d169b00b0fd427ab70dcc2010f9a08b9117ca24821af2beb8020656f4e02d790d482a44054dcd8c7958a190039eb8bb19fa2a48a09cba3ee1c30306d50c752afe3b87f23228448041e2f79273c253b722b77205a1a1e184958dfb030e80ef44f321bde3b6c56ae9d2be585d7ed90645732b2401f3d7c0f158566bc4f4b091274def31084ef1cba001cdf66422821be316ef29206c10c2a5d5dd9391a454a7f15243a20d0c267ceda8a0e78694377c03f9567014d1abce797d3dfc7108c539696910c4fc65fdb437d2b08a0683afa56b2acc22d3d2ec54ae1d5c7ca08abad1cb865264edcd7a9d7a7e7f280df22ddb76d1ebc1ca3d5c68b0b370793bf42b90e031844f5d917602dd07b6a22c1d89f6aece065af39ff88a4c78939f174fd743da87c8b8e09e425fa6dda1eb5fcee5b0b50f336ab2100e8a027e180c5d4c605510f80dd0660956edb22d7791ffcfd637cf3337fdb7266bee9b0c70e43f67c8b0eb8af672859b6290c06937f2ea4b9164c59a5fe29139c12958c2293167c22aebe59574b0693d1905c120a8b7c08cdfb9b063ece139ac1af95c79ac7c7cf0c25a8f824990b90d93b4a078796accbccac97a5c4454a4846720437d686e7ac4b6bdd9d69ed1d117324b848f1816d712c7940dfc84a1ed309c77571844532613082e6748463245215ecef9da47303e1725023b85673ebcfd72243f5282eb3ed01f2328270ef518a7fd234818cefcf5960d216018a001432ba3ad7801e2eb70e86f1ac4169d4e618a1a9f3a25a607f3038aebff05fe367d7a567e86409f75bc4b491ab10886ddd81705f923ce0998913d02d80e1cd7f145f5c252d61c2539bdaee4bbb1333ce357d51d24a53cfea437d94317784b7cb08164f2ad0af9af9ea630807b54ab54c38dc4cbdbbd1b604c2e85f00a18a696496615ae75893cfc484f3b18b7468900e8392dca1f70fa44f500cb0491007f7b084863b6b4e68f3fbef543945663053c19a845fad5a8a7d7b8b3a5edadaabaa8e79c2d53260f4a7c6f6c09f9137e3ef3731b2b2f40709fe0a43f1470ba551e16c6337fa58605dd48430d03d2e16468407b1704e9d4f2055aa30acd5daacf6c6ea9f75206abd8ad825035e8ac37fc6bf26439d544988996a91ad40a46bf35c9659bf7401347e96821a27333c8631c51bcba000dd0ba888a7be66871ac5c7ce72b5049ed36768b69b043343099922851692173c211b6ac62ec94653ee9c5c914c08d9e4cd9c2ff206ec9fbc7b74fb419c637bdf9ad30a2bce91da56d1a7cbbcb894d3396a23857361c3c1ca74f5afe7908d7f3272bcc0223c572039368c9e51c0c96e2503154dd0eb1b520af47faeccbb478a0dbf35077ecb35d57f7a4a63d70a028c2a717cd1d90e7e8238d005b5bde3cc07b1b887b0cdf67cc9815696c3665a16b076cef1e8260fdc748ee290aa941ff54a8ce1880caa129c8123baf6ddf24450849f93b2d3e259957d3ef6ca681753db995dc125cf996ead4cd1da4b59675d8899568c7c8ccff89b08c13cac2470baf943ab70df19c7e4f27f5384409aae0ebc7b71fa0682165e0218674c4fcb87c8ea60e42505ddfae5a15dbde26a5b8255501cec32ee25fe01e639db4d7ff24ca83bd687d85ae32849cd6992aaabe7857e69e451dba91d509ea27e206a7de1d631aeccfaa88f04d4527e777a19059e744729985f4ee1ab1538aae9ad49fcaf1275cd611cc70e4272a0f97ba1961cd98cecdf94f0bf16f392e82016ea074b2349b7863f48174482486261a1b19671ea139024d42bc576a63a5d584a05551919ec6bfbde014a8cc9b4457ba13ccce4393aeadeef0db4aff71a6b2d38ce20412a392a4c9f2d0a9f7dbb415f4271be2a9c71bb7c1d34c2c243b3a87d3374d824bddf0d1282c95cb29c6b56b9387d02a0c96eb874735450ee7ebb241ca53019b55ac046e6d48c1889e94d0b2f8cb76ce661ed9b6c5283b321e5e9157bb761cdb0f286e63ee6f43f6cd6231d299b6530c", "52", 0, 1120056016, "96a8e6affe8ced96ac5c76eeb0500abe81de5d8d6aa7fb584ca27ca1862d11a1"], - ["9957d8570126b2d4845c788abe9f76099d202af0050057dba9c149e678fad3f11ba7c9b53e0000000009ab655163656a6a6563ffffffff03b9f2d5010000000007abacac526aac00c1b860040000000004006a52004191c903000000000452abacacc328955f00", "6565656a525165", 0, -666266533, "378563438a3b73498f92232c92e8d334c99cbb709049a236cc29287dc295163a"], - ["73e470bb04664e75279872c17279b6de080d1aad0e1eb7676d1096a3908d047f300106dde300000000046a6a6a52ffffffff9bdd47a3883f87b360006f9fb62219bd7dcc6d4b94f532bdbe5dd5bf6b133f920200000004ab6a53539734bd375ae620578600b745bc6f975beab43bffeeaa726c7fd6f02a741287047dbe27280200000000ffffffffed2bfe7e04da2e0a33aad81f50823094ebd804cc7fe48eb5b293601881c0247803000000090051636a65acab525215098bcb026ff47b00000000000200ab52f4630200000000060063ac0063530fda4314", "51", 1, 317459275, "65637ea11f3a75a8b64a561858a5b574cf09e3956f612c29a47a07208c04f68f"], - ["a813c34701afb5a96b8f603d651d23feda87151d1ab3069d2976997d96658e7505e15de2f0030000000365ac52ffffffff0204b4f4020000000002526595bedb030000000005ab5251655300000000030000000000000000b765df0300000000a22d51f43e58f37b248b3d12f4c37ad79d5a985da1bcf428b864e74a2c16c04739b6f3b56e458020ee48715319cbf93e4a256676d01a42278e6b22a987de1d43525fe677c84fe788b46e95afcdce49d2e8bf5dc804eefbc71059c12ec2b9d3c100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bddb050a7ffe28f80640f944c54526ce43ad7dd852647e64c24ca7a14daa705b47f89b6c61069f5ac45f4a15329d9a5ade3daea6af54257d3dc806a9e41dd879df09585e8b8e0af0ecfc214a9458e1c0e4fb4c30f88cc36dc74b6e9998709ad7633eeda3530c9718ba7d2906fbf7e83bc0a606061b1afe99adac24bc276a1887032a332fe7ee82ffda0c56e64f970d3b0a54e560b465782e303d67dbad8dd41c2a032050007ea046ed989afb9c093c39a1896c9b54ca0bb9593f140a6a8b18f1ab7c0b023b7e9d51d122ceec4eae2925a2965129e1e4723bc0a3a7fc87ae954bd543b9289b53c29c8408c89997d3dc0282341c0b9d8618f6747e6794a38bfb75371f7f031938bf4b4966d668eda88a421864c781facdad75fb5e90807854cbae17d4ea910207338586be6e533f8967b6a78d538b0bbf2ac2368df9f8b1963638a67a14e315021e6e82bff0fe29ee58a5ea7dd9a3fd9e4c38994b9dbdccf8783e65a81d2c1d6c0229dfa447fa9f22b969726f871dec6d10dd3756b801daf88764a39946e24b945f020f2faccea46935ef4dd81a74b0d915674cdf2346bbbb68d100dced97b6fe06dc228cc9bf206b791f8177238ccd910072b34142c533ba56af7de2a240ad17fe98bc824c983e07c1729344815c19dedf4e0360d1d194af550d558ec4fc4eb896a1cf57c9381b8a79fd7aacd17df802019ae566b79e8775ac7ac7a9c059aa315525377e90e2f6df95ef79ba625d50fdca02a4ffae62c432db6abe77224ba4c6531a671e82cbb03405d54f50d9f1cf841988fb340aca6f89932813304d4444c87d9de48f2ff1e09000c5ceb81a92c22490f36331b18a0eee86f3b77e7e766187928db00fa355fb524a5c4da0e9266438851bf8c6f3822a24980d4064a8568c0414ab3ed11780167dfce83856287908fcba3b51814444e84e721936c304d9afb4bf1608aca1428110cfc9849717f900d5795e48dd3903e07bf7d2b8643db90624c4a3e4443f9dee82ee55a45e4c29e98e85923672c1a57c375e35ff1870bb344b60f6908c20be7621207e47b40c80f4642011e56d684a1f9f8ec3bbd50ebeb3e704660dc93af18ad825c4439239634213366cea0eec93f98315e057582f3d3f235f1b7d63baf8dd216db06c54f3cd64c2b254a816d9a6879fa0f3a7164d40c486124e1615034b33cdce528fb080bca11c2d8d232efd4335b9b0a7277811127ae59f6800c40f997a3da2b17a879fbb791d9a78a4f357d52da5afea25133086ab394069ba19d555b71ec3fc1f67be3249d2be8860597aa685bb46f8fd5b6a8a1df779a622fbbf4b4cfc0d901625025001e5f7228ac87451937b5dd25ebdb689132453a7886e5057dab39168c44c5e96eae3d65f59675c09d82f7126f00f13e2e2f9b5d51394ef7d206f112680b4afcbd90b9117961bf8925193985925130a0619076e3fc889066b6604312c72f3c761c241f231bba1367de3b1cc95c0158ed37e10ee63cf850e0be1fec07e66ad478d515be0399513f90f36cced3bb9ad9d17614beb09a941cd2304d9f301d635cc43587180563567ffb27cd9d105f8e3b25710a2504aa9f3311c9e2b4a0d78b35f967749e1982598aff9ebd6d03f1895f818b61d511427eee8b0a73ba88c158d5c778327ba0b11fec7e9468975363b49c8440efb16685abef74ea1537b5cd539e502865a653cfc8e14f86c3c0806b2119ee40677be499b73603046bec6769c732231688e81595c634c2c7ba73c7e9a1cfb67bb1ea1d9827eda64b76612a29ab3ef53757f39f792bfbb40296917d60b8f6b2e606b9ba822018aa631e572e6e7e7d3c273746420865a736a544637b7783766b9e2936d55312a01fd7c4674ad07799e7b1d7f8317e19047ecf406f75903874b8948509cae095e872b4a81744fe9853fb5c665f36fc0c9b5866278868342cd169332499973e48e8a5ad57317014a54c717f9dab5485ae720c6b0be239e00ddfd110b8e59798b027f7b2699ecd3abc928d8ba8eb31e66cf00c87c98a33b330bae417e9d8d92823a071d3323b6ced7db4bea5a2989d27c7695c5972b05cafe2cd75a5eb69b5a782a9573a667b420d838cab531c39956d1bd7c5db4401eb40401f69504d38fcc3f59e9427d79069c6f9b25a18f2ce726c7df7bd0457449ce4c9cff9c67a5b095f47a2c766fd9c0ecf2db07ae506f3221daf0a4e3e9a62cbe694b76f96a8a5db092c47aa7efdd6eaa0ca4c90d85b88d8f695bce6690cb431aaab8c24057df583beb0a89cab683a19a0aed39cede793c66342a0000000000000000966a63040000000051855d73a6d806ebc13eac6e32bae4d7b7a99e23be0ed37b69c4d0f01cc09426cb5523b9b3b188a429cfd897b42e97678cad8f9940dd79d6904584436768b802fbc3a3dd1dad3efe331323ccba8c30fe5a2a94cd5b45709a770aacf2cc6c30c4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005678a51c3c44b2453dfc25cf71739c05aa3311fa63dfddc7e7056b447d7a32739e98c5b1377dc80d237e9a981d8b1937461d608cdf3e62fdcaf74b068d6c3053446ecb350b3f4b14d8b3e1121f0880333f478f520d34042fa9503115b333f8e3f63ba150b5e789e764d642b3fd5552bbfc1ab925c3e5274f93c4382c198b6dad020d21c36ceed4852a00ce23b25627083fb4c1facd68ee60dce1ac01c6b1073f0c0206ccfff08e3c7393ccc78967df8f3a1cb253678605be9c99b1955d9fa570a98a0b1eed64dad7c5817b51054bc9503326edc8bf77336afa178a788a4ebb81105e3119eac8aa5da834964bd23ef8f569b6c2cf2daf4dfe93e51ec82f28c386ca749b021c4c08349c26354101288e9a17640b31d7e25fc69c8e19deb865898d1cd227970320610a19585b690052b3cfa40ea04b03b24062ae52c4327c6ded4633def16d28032535bc77257a19c55727ff5cb64e5750bb71caf865e73028ecb70b10d376da1e03143311fba756ca03f670df87d313468911a204439a5a806d4a252412a3e6674d022dbbc6263e824711b48011a3a5d1a951d33d9871d7d5b68b2fa4e82500ffe815d5fb31c7c5bf45adabc4917db9e744583e61dd9f500f0ccd85c49778362e5394f8fa79caa2a5c97d0a7dc78aba6ad52a584430185524b818360fc9d05ed6ce23e6271f56e11d60d9358692aa1baed8949adaea1773de530e734f768fd20d4d61eb46aa7704a9b9250ae857ae73f4f22b144ec546208bba7f96266d08141598c834a9378920159bd411cc49c4a630c4eef8c5d46cc4d02675baffc4141bc2775a0520ed7055d8b52c2ad5e65de46096dc1aa3f586b173af57471297fbc0656a278c3fde39b487c41dc365e606255a0b23497ded20680bc1f464f02b9816413768e7af4c2db2d16eb27d9b6c2e8f5acaa6423ad2687ea8de009d19a65b6b39c2ef9f92a0c5d8f79fdceafa9426ae44e4f5f82e8306b2ff95db1b03441c6b67ffa98f6201e159c3ea7053e8c361b125b4f0ca7c530055aa1aaddf90cfc1065028a6f160d72e059d924a6e9eb00615f97c77a9beadeaa7a325a9db1c5c7b4c3978ffd62550526fac570fba3576bf931d3cd6025c6d127df70dcefe0b011221f5f1c062c1f9d7b6dcb6e617c23dec8d36d905bee8b75314ecf34b3f5e402c02cd53c85dd328fe24bb75caad4ac0076df90121366d9df6d2bb3c22de62d6cb6853169965f57716dd304cae7837694973d26b3f79201e5099a34da054f0d420dc2b106fc7ed6ce1754500537fceb673d9c7bcdd327c5499fa9616b9516a2751c32d5125c03d5f2bf9ffb408eaf705be12abec7f9bc706a43fcb437cd54ee70ac8000debef2ea8d70e6636e1a084efbfe6612e23c9191d21d2d5d1388193290641a279556695cbd8eba9d66ca0d23e9e9094769e09a93818934aa2bb635d90e4ea313c3a264ad125910553288a02808bc47408aa57a55016ba8fcf3ab32648cf2cfc3f375706729be3a93f3a5f48f1edf5e94e8d2ba29a3debe201e70208bd96b18259071fe38141c51fa5aae516f97c62b120b636d2f258744fd2f12eeb6fe0bc48216b81c794137f198fbc9adaf57498bc96f2a5b3069b5bd054cef0e1be7a40f0fa0ed26d5a4be654cc64fe3b073e4099b4cf198c6ad51ed33209d464f6e79468fc6f047f7e2e996a5574866e605a529a3859a15c9b77230f2daafbf584343f3052ae4ba2e8ff7f87e335e6c6e53e7bc8074bbd9d7105babec8536df9f0f8f7b357dc8c21dd0647ede5095795a3e2d872e594dc13a39b5ece9aaa7e6cb75f7c4c880894b99e525a13e8d606d4c0e1008b7c790ef57f956a78be7ca1a3f45f2212194f3411bba6bf6d47852c2d3377deb8178a361c611c26012eeadd72f7029a5a10caab0e9edc9ea1165d524283a8b11a0018b11f31cb271ec0e1a480423c2fa0953c25661ed3d65226f682160c1a128868b9063653fe9b829693700a7236a26905e93fa11a8cb0c8d73734758a1dbfe01eae3c92716ce512868b7eb2005206d42854743e5ebd4b1f03b7fa926b1248c7a4b2f009307b78a1bb5887d6e686a92378fd5317882f7162991c7dc6bdaae3814b4b26b49a8a330972efa25de68728c4e9ecbe09039a064c2823c2d8ce22b08326e8d1f19a8f52bdb898efb78701478dd509d91f9205ddc663bcc8e67e8e316879d621d87f42f2e8eadbe040c4da03edcbe4d9f80bb25f8673568c00849b428d15d6a8a55ea3311f86a0af232ab5857fdb9b8de938e97068399547f4c64c0fa0ef2b69d80000000000000000b217b2030000000070c738b43074546ce187ca5b001501cffc19ad2ad5979a8f3beeab47b79beecf038356f7ce22b7484c4cc4dc584a5783ccaf073bc60b1941e1e24fe4e964c1504a230c47315e79b9d5ae4b28ad27dc5ce60bbf2e8ed68c2fd4f5fa2252cdaecf000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007895bae04c44e7046cf345510323df921892abe40a9bd9f359825a0010532ce8a5e9d225acee2aea419cbdb74ec3efa1d2d28039925a8a0ad96fc1a850c85ec3bd8d84ce9ba36ab236b2631574fd918d677219b987513bb9cc84be3738d7a147e2a010a133352bc8156b201192084fb6afe3f429681c2ad171044d4db32b2089020b89dc84d67eea8013a621a5a08d8703b204da86b337a70341eda09593be6ba2022a8b3d221168054e48b0fc04e1918b9b7557794182c4e383cce3e8f1187329260b148325b13075b82674e8739f7440e36207a718ca1e56360d89f9cd20d8f4b25a29f426f09c89767fea07711f7162e769e39d654763d1026beaf9617ca0f693520312e8d7ccdb418c846d72a432247bc4e05d3577c8af732c6f47c759e2e643e75a021d2ddec3f8daea0cce16adbbfe2698e6d2d4d42b3ed7bfda06b87200c420fbaf0224f4868e59b07815e33b94a5ec854b4d46729f0488225d3bafab738fc1818811022b0b852559c5e837a7ba135cd0c0cfd78ce8d5377909040e8e9eb6cd6bc4dfa803259b03517e3c0fd2647618d2d0022d83157033d7d09820bd438ec2d63a57c27c7ec6eab3b8bd36ab9bb573640ab418f3f8e9532ab37d8d47deaf11652ec529b091cea1cdc9699c40bbad2e11cff6ae6a5dbf81e12c216b31121ef31f73a9bf43653fc75582a80c5cc42f0a65992eb15380dd57c6bd54ac931ccf869878c0b0d152563e21024f8ceb4a1aef3664461eca97a533a4846ac62ad9aa6ad6bea44df47cac0b978232e52a595caa4fd617ca4ea6a32f77ff6e771839813650232395d10750641d38f47d0c84b7578d946ccfea2251bdedd9fa44bc41394a2fce81bd37cfead9c831e84ad488384f42ca1012248c920c72267339250669432398879353b17d96405c06fa6f49bd6ff06fbe142ef8d444b3bfb159a2a6cc8fe4a3f401103409aaecc9f9eeedc52917d8a22c6721bb43192b146fbc3041b5a5d2470ecc97e3e7df20bfaecaa254d8d788731742cb4ebc200f68fd41d36b23eac50254e9f33e5ca7d514a96acad35eb8e52f580c7973974a72a23c74467355e7a5dbeac60fcbf5bd3a5e73adc469067de1c2c28845227bca7bcc0b5bb0935662f3b89440159608a8ad455c6cd0c8e68334e5625bc2848fa562b08bf566b79f87f738c1d373c08bb4402c379dfe6cb025937d85e12baec8b0d89fedf79fcef14c47ffa04f97a9320f6d4267747a87fb63105724736a436e5e9e9e8c80e0b981f704fffd36906832efd63406e7f85c46855960bf10780083f20de391d1a9f098d723dd6e020c09f84c2e4494961e727beda34ace46d08f98469e67628255e37d012a8b41c7262451c1ab67c4632f98742afdb6b47155dd17f8b4d3a62be849476832e372144ea5bb15af6ba48eb0852b06c5624b4d9738bd7594ad63f67dfa71e7d0fcc905278634a03734da472e8508b16ac50df57df46f9d0b46886fc08ddb9aa7652dec757885ec4b48ef8b1b26591547381b6f22cc30dbcbef6c54b0c09f29516c9c6d810fb0b4cb2f3f07f47372d34eabfaedd5e3a03c68821d1569f6c6791a27cbfd69b728bf3716db902b3a3a872916edd4a95f84cb94415c505d5a0dfd8c652d411ee1a1c6b53e63f3f74c26870bdd46e63d37c01344de86880ca6cac33599f6302877f4fc9547e7a7df42664b9dc852f5d2c3f9cc21fb6a412fea571a7d49c34c7c51e1fa275f95a18e67449c24c0d2468b0595f724fa077d61841c9d67ad8ed0be7c27d42682632b4b721a1d9b8f1a7c845936f0a71b4f16098563f483408651f52b816c3cb14ff5cf3aaee388b428097acd1296c6ced5cac03336ed28e9bb418736fb45ce69194dd94e2b2db685dca7c727884066e7c10967d2992382b2ac9846287aef634d1603731789cfd91edc37aaff44c14d01352d978ab2113856d155c6ba592f8a71f90270962e5cdb5a333ff6f6becdb80be66316d4970c8846b06861cb3aae3ea84f5147b4173e521ce91f67a89772c55222ee45aaf3438dbdc1e955b9799aae63a80d613715f062f7b9b1c8d7ecea0289927c2998b7b1f4bd41bf171b6937f0fc1b5b6963478858bd15677b6ce0122d5c4777e3d61faaa7f622ec317229c6c69a7895846ed3fffff89224268c821ddb17a89a2f7f893a7189857574dfd5756677e0bcb39be5c73a99bef38143a13d888d3ed2c63c1f9eb7f1d7e2c76eff28df4ce3948c2fdb782a67f31b263fb82a32922ae74cde2e6518de084ccaccd56ea229aa43dab0e5f0c59bd40c4db179921aa63af8c49c7c4ed1798ab7a7433af73ee7d767581eea9817465e45540bba97b0adcde1573c13629ca3a95249bd01ea7a05d1c7095329a37d2b7276b30cb41e6211c4aa8f519c197b49c5a243a97150cbbcea2b62db2c5cbbc519211b8303", "63", 0, 871174497, "ed8b7e5bb9b41806932990b2c2351c3d163bfecacc42c634f9cc943703b86d77"], - ["5dc1bce602ff337b0dd41e91d0692bbea9a75553535622f1afde0d5287bf97195c6af7ab41020000000100db57ddfad53018ddae9754ef216e6fa8edaa36f6450fce3d45674eac08439b51c4253b0102000000056a63ab6551c0e1cea10156b6320500000000005d10af86", "5153", 0, 1602741303, "17109bd3260621922b1d8e8a96ba46c04f35f4da2fb1d41931db84dc1ddf30af"], - ["da83c45b0192ec44098cb8d436a7724fe8de8a50114b0fa28b92efedf1b5d9a70b0e96ddc10000000007535163ab65ab52faeacc1a04d8af6200000000000165f1257301000000000553ac63ababad2b64040000000003ab6aacbb79ee04000000000352526a00000000020000000000000000491a4b0400000000bd297fcdf40d2ff9d89b50a866ee61b5a416ba840cf3fa95a466fc160117fbe4ed55a04d5d2bd6e1fab8a8ad7d63d12b926a1339c29520b66906ed1d8d18e2b75c39c7d23ccd787826789171e5d3fcd8767dd7249cd9cc8d1d55b14ec6163b9500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000513628920ba7a926d496a78a711ffcece462a99dc730b6a5619d2f45fb64c3b81a81ee4a95a5909570b86798bc59366604c97d4dee7a13ad598f68186662ff65ebc7ad96cb5c49a5552850c0b840c800014e1f8759bd99becefab841d885cf2f7a4ad45c934f5b95e4da497755426c50f19494ce83f573b1655f6232af70c51e0208fbd545ddd6f35337166fc3aa12c6a090fc4b7f1c1068562423d8eadb58bb590300307510a71a23e29f8b782b239732a62644c2583bbab77bf1a66cca3010f0070b2861fd04789159a198e85c158217238a42af088d88bf5988ea8722e50fa747251026865b6406ed95f0fd24207c96f0e2b69958c87ae66e46698f7e7f0bf32ff60214e2e8fb394d80f704d66d713bdbe23896a6a334aa45b7a14ec238f91f1f6c7e0227abc039dfc4af8a8075cfc3af5513175e1cb6f159d1915be38dc7f74b0f5a4e022f662f1a087158afe29f36578a497251f90f37e20109a40b470fdb22d9e7ce96020db3d77bc75234327941b89e1ac9adce4839ce8f19d3dad78be8bebd74ed0b480326b28cccfce1ab3d451296b49abceb35937dda55b238d05ead511939a84084ba2741fc01c7e87f0a5ea869a8a0a99e1552fbebd62e136d4d48a89d9d15d75fe7318f2a647603b85e49eadbd979f1a1eff1957f592af8b830b6bd6615a0c2654699a50883145c6510499eae99299501be7beb546e9e4155547896986158096b522fbc88cda061dea11abd06a60c667eed28d4f3d907d0c3a6a94f70c3d116cfc7f1d9b39e8e1ea81ab2328ed8a0140c9821663e066330783ac0b9f5d151d62d5f9b60a7568949c789cb8130cc039426c746aa17f2084ee1444a11c1197ab38f56de69f3957b3830fc0ea1ea6583d5f0a3c0e65c0aae923d2d2712bebb349ffe33c53e371e94d0894c5ff4574e5a67b5dd73de63aee952995d86c313dbd6c654b2bbcbef9cd1131cd3221b8dd70581faf1c8ac8fada4634172573a821bacd86547f5d2615d0d12963c5b8cdc96b274cacf2fa0e9a0a6e1a04834783f7c0e48335549d96226a1b162bd46c6e9c6cf04c3cad15d6879e51c8d1e17c97f811b5de1572468ad1c14a4e959a654a2cb55cb94da2986488af7793a8c46ebafce2a15b49225de868a2e0f19f42f7fc19243a29825d920c6df910541811ca09d0d902819f94e03958808bed53b3751005b05b4de9216d88ce966e38a14a2e304d446ae414ae4d4f1dbd678cbb057c65de78ef1f8ac689abe98b1f3e9122500783005a8bc30625230d97bddb31d00998d847110dfb53b0833a1925ad6eb0b77e599505fba891995e2b2ac60f5ebb229a17dc24c6ffba2e60898b49611f90b515b07563a2097526319e37c21f26901ddeb9c395492eec4e485d5195d510cc81e6d8758e916da7a24d1604c8d6b325b1036bab09fdfc88d63dea7d32885eca8972bae597da054e03434a35298e00ee171307d87881a464340bbafb8fc2125093d5e1293b02a2585f6c12ad59e9492ced7801cfe89854c27308b9eec698101ddb76f4166a3c38ee6903fdfc1d0675515d3301c1ce3e5755d9001b0ca1939e455b1067fb5a3fbed89c26f0cf5592d4658468ed1d83305e5d396a037ceed9dc88792b3c8cc98d379d4b625008845681d00f0a6f93e85264fb0d65973f84a7e93479b7582cebc2223494a0eae482b29bc8b9ac5b471ecb994b69ff4137b278b1f3ec38416906a058ffa686772033e0f1ba807b1208b5c6cd405699ae7d553457262d1a0680cabcf6144feafacb8e575b30c9676741685b017af171d0d2b08ea3a58347f3006fe132db2723413a050f845a0fda80a1e5d7710a2f116801b0a1b91a46bb7afcb2b2b62fc00d659a35681de8d24d9cbb375ef6065170e79af3eac6c2fe2463011c9988b9f1355e82bc81a7fba3fad13d5082387061b52e184ea53d8a71f3d45cf4a12968b95a3c0895cd862ff45c3c1b380f450a62a4b49dafc304585edb70b36ff5c619b93e7f7dd7c8e9b2cabd5fddf60e9154608edf291eeb9ac45026879bbfee46f8aa4e846cebbb04a83896ed1c38004d0c354766b872ec31bcd6f20fe3bae1a4136922ffbf95f956f025d759fe585afb463c78e3c03ebd708c1d18dba0d011596680e4bc5e91a527b058b0a8ce53eafd45a69886f8292e56da0603a6fc8e71c7af2fa94c5bf21aef85ccc405ee66f2762a8d2a129bf8eceda2d32147a09d081be78a8a2828cc94c8298939efc35aacedda42e859635378227d1769b187483b9c6724462851bf3942fb98bcc7d2204e69cc26504859604000000000000000000000000d60c99cdd5613343518dc852c4d0b84d053347498d4a02629eb00e961dbb007846692f4871c4a2805b3ba857041940639298af31be2b16c16529e18182991b98596fab62e5191f8c9ad2e57508fb68b2d63b4fdd94681e178df50d2e1319cfb800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000af196f8cdf210500ad20af7286d29cc11ad51c51e39d3e4b6d340b10b360a2887dbd81a867ce0e9ad6e15e3409cdecbf251416da19e8cb643fcc6ba80c0e3a0ac6298dd67b43f42eaabef609eff2c195e068cd59fd0c352e28b1b29f3c89de14c41ae3d8918110ba5efd502e12e51fdbc82ce9470c9ba26975f22dfa1f6a61da031442b239c2580f13adfee0d70f5e296441828783339c618f59f2b5803c9a0b9a0306e2b45d2070e8947dc3e53c6ebf5cbb46f13ea43645eb5a37c0a1a66303a7ff0a2888e85e55b766e45b97f474af5c938a29d04fd1370d683e3cb3058c896b09a721aa1ddb40f841115d4fe92abe26616c4ef82eb52e20d7f22f0c4a089106357b022d7abd065c8c92ba3efd9f109d64fb0f5256d18fa5588a251b4b97057d2b92710315dd34757e9906195eb9fad396acef5c18934c5a3a00e72729c583c3837c25e80321cbbd0af3ba52a8fb7947104f002b683ae4c2a58d3a94966047ac939b7acfee032b5be2067c5e5b9b396568347c041741f8d18d1a6935fea7aec808c3d2d66003031dd091e19e694dd343f84e26629ba32fa8264d82e3845c1c09a90bfbce4e94337b4295697ca9b02be6ca667e4f8884c224c18242ea779d0729e80089f82b2eac7a7e1b2f09d87b8c7ebff6bb21a6d12c1907e65743ab7cf9936c348158a658ba10d4006b0be0ee81d60f9f00486415ba87ef6199ff4b7b599dfc17b04386f5247957b8c5e757bc341c13d950c49409bfc885d1c3416e3949414707e138aff0cf58f8c31d1038fcd58fe3c33abd36c75ffcca60686861749fe9c5a772debd9feafb658189b8e025169333fe7ea3e630d4af29572bc58aa2ddd9a0ced08f4761ad0608229019d88296c7a5a2cff49d41c48fba0c91f48c35e7b6e7badc9d42a63189504ede7bda0cad84a25311d1dfa5fd806d8d69cc5d18d56e0af7589ee3140a81d8953894a177b02ad80afd0ac07a28eaad30447a094ac69569bd77ddaf79f675bfe2cd2bb80379dd1e15014406d12f0fea2a8c8aa3523d1f178345601b3279b557170c7d73ef0e8504afda34b61f9e497bbfac1c55a20c113db78a3289f8bce18b1155d1534c3a7ec3728c723148822d061a32cda38febd5fa5119efa1d4e501f7550c850783a721b6d650b42f2414bfae05eee6759c74868f6cef8b096370bf5ed1205976ecff4436a2e0f07c6e104755bc6f9aea92b261c61e68fc57fa54687591b942f7997861b25082cc1ab3841296472a82a2fdf2fb8998f382ef4d0873bccb4211f494863023faf85b0b704e2ed2aafa8e3f8ca3ac00258c59a75bd302836ac4a9bf466295553982747c477dc0ad9368beff67a7bee975ba136eba6c6461ab8a1f8cc164720f59691795456cb90136dd88e38026be7785a856950720a635abb649ae99bba35823b6e2db0c3dfddfd9cf9789ecc5609e94c86e4f9d9b7c9edf03adb29031136be34672d608f3781ba29cea52716d4ab219d2df8cd0dd23ea7fa1d62ab3ac258f57d3fc0b4e7c0c4c233276a09bed6a5259e1de242f7f775af369f64464a988e84db7338b7b16b52b443cf5cec951e6c38a56ee8da8d19f3cc27d54c56d60ec85fdbfe672a7adc0a14737603d5e32bcff5d438ab150ee7c1bba3889634b6fb217ff1fe1add8428267ec6ea785dba625e42c5be4da8e41b6c190922f13bc912d6a78b2b71279be215a4be06301c31f770a42a9af0add1e3d3b3c6696ca9a0693bea4e699cc4be930f367ac3c425c247de3fb235e5d1b05555ae919dee430bb9bdbcc95dea12568799048a508f8a27b835ff10757dd23b01e9bf76afa07aa251a7bce568cf8a7058b012c0ea6bfb4c0e6739f026830a475b0589f7a89f0c3a0590bbd062769749b007a72c66edd910b85c90f1249612e6fb3223b9e819d1303b06c040568bd5ae386fd439f05f21584969bd632410413dc528acca9c51cc9eb0fdccea39154719a73da892d9af011fc0fbbc99223df592bd15866447b245f39264d4d71034310f07fb602292d8828adcf0e4a08e2a329fc1023d754aec146572b258340b8286a1b0905fd13a7531b8c7abe0743d48efcdea40427a3e4a9c0c189bbd365cdf4d4bc90ade1bba1d9c4cf75e62fe91a23ff8bc9281953d7baabe70245f9bf7b0b7121e7a197c9eec8ec8874da375f57349b46335490beba22768626cc151b03d98a15a3bd27660665652ff908d6550c6810b6ba3f1070737cbae4fd9878ec66901621ea54fc2851a0061535acf88b43cceab4f3cfc53b78b130fc1b810d9d374e08c1e4ddbaef77147ce826d2e2f6d2fa41a42f103c9dc314d5b73739fe1d8bb3532eb0ba1ea0f4375cfd9f43f5a12de5484d299d1f7f30b6f9ed37a7932c3a71cfb5e94706de825e0abbfafac97ee1fc0cf3908aeab138f776720cb917b047a1bb84a604", "", 0, -1961460397, "060539e926e9099dd4ec5305c0feb128e24bbc752f545aaffae68bd45a5d06d9"], - ["bad8eba901bc0eb3f1baf1fb3862a6d4094a2ff543e3b369c0646a42148b8af42efd8aaae50200000006535165525253ffffffff0471e5b500000000000865ac005153636a6366c5740400000000007ee37804000000000451526563629d070500000000016afa8ec8f7", "acacac", 0, 632984595, "1035beaf256c346ebc296386ed481d64c80a25740cc9f408b8e62b6a8a33f889"], - ["0e2052e601f5490cceb69177e69d6c4742a0330287224a9d0f8b572c8e01fa8b72857a44ef0100000005acacac5265ffffffff010154d00200000000086a0051ab65006a53c89e9a19", "6552635351", 0, 1541011043, "a6a9556ba5131ab9dbb4dcb81aaf7def7cc3bb1e96304fa149342c8af35cb62a"], - ["2b92af9d048afb8c498377650832d2ad1173e10a8896994ff38d1858ceca11c1573a2fe6e50100000007ac6a006aab5265ffffffffdd67724ac13168ef2f98568f6f2a1e1d9f8c5c43467eb1ffc1c961d2efd4a8c40000000006abac5351ababc1ff34a725bf51ebfb702fb716c6b9d432cc44a2bc29c58d53ea9d27ec73fb19b99c653d0200000009656a6551ab536a6aab17322e450927876ddafe25c84a5c5e7274bf44ab40fa652ac521d8e65ee450e1e419437b03000000025163ffffffff028a095501000000000153801d040200000000016a13fcb519", "6a", 2, 909642013, "548c905d6a92f83eb47cbd46e783e6a9a11221afa203b77c59d83f1115cfaf66"], - ["0c555c11043775c30d8716886cb161bef009d1449ec9bef2526f240779ed86db07ac751589000000000300535201c893885c393e709bd6c52fcef75c1c13c84072d8231b3b02d4965d8f4bfb833c3fd0ed00000000060065655152acdd819d2b7ddb96e668d044f3bfc8710b3eafae6f9c38532aeeb61251221a85c145a96e5e000000000365acab6b22e07c8af29d79eb0410094dd83fe358a665fb69ea52e3896ca57a0a3354052de4ba4100000000009c9f747b02d396b003000000000465abac53168f650400000000025100fa3d64d4030000000000000000c99a3b0200000000d9618bbe4ef50ac728af3d263787b69326243c1b65b9e481324fa922b41f7a394044fd8807936d7c94bad4c41e8153ec351b720b74018255ba9bc126fc379f3ed10bec0ec6b78ab8d330c71d7fe71adffbaccc8f9cb7ddc6e9368d74d60eccd70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064c32e455b29db0832993755777084a60e9cfcfda7333f9acb9d6606087306ae3b7779c5ae6f889b59545365fbcd9ee047ec23930677459c911fc847e6e7d454543615e1b23b4280af86e8b1207c4e9e363e4a59283c88c0197d448fb08bc4acd6505935ee128c8268c4399850734fe0f042ecbd3b467564353624e8f61ff454020a6121ded076e2a2b2e46ccc6be7ab5d365d3d223148018050c76385965db8cb0300ef41787060914e7beab9e3557a15d5e7b89bf564d08cde2ba1110b013e0ac60a19218b773f86e7fcf577c8c5f44c5e522d467644c559dfa7bba43f63cc1cc6cf1314e802eac1248f1fc3413f3ecc72cfca31d407b33aca2871890ae6ad4deb9d021df5d48101b451ac6a9c6c10f9aa9ea6fff0e46e80fa86ce22f704fd2a8713080324c1764a859586d5122f97b12513fe694fae72804f0ed0ffdf9fddf32285731402206d1adc4e420aeeea2b314f1cedb362f588df86fbd74eb9684b5df5183929680318df61e33842761d1a69c967ea95d17bd102cd4cdc1e97820e4150c568b636bf0210d341240f10c09b68b90fea140a32fee8293a086bd8fe4f2525ac30846de1f372e129f77340b307f3c740b6efc599bdec4a973ee5c03ed03450dd0bb0d9f18a6b1ddf4842411910da8a49bf92447c771d77497ab8daf01f9a100453cd6edeb9384e241e65c41022f40d5f982b97e69daf710c9ad9900b9b316181ef1a5379527ffb4d86d6d39648d8d19f9584abcba71d97cb16b3b761794db272eb4b5715424bc71593c6f914d6b41a1a28643ac76d6eb5042f47bce688471c51da394bbb4ba5700591ca83e89bbd760c77bb9b8c53dbaf5362c189d257916af805e8f5e33153763d13f31766a85e3ac9d29dc6d329adf5e21f72d48c913fad2e2ee1b898a6853447148642f44126aec083e74f3937beec374c6309f928497255e561c305ce83074316b164779701eea5efb3480a36821157533db62ca1028393d33e3139daed91cac68217a8e9c639c9a1005ba40b7c848e102531d97ba832aeb6bf926b831a6c1e7de8e14d08b26f40fb86ad085bc6aca16c167e6d912adde0d44d82befc60b4f925b303f99a9b7b265144ea09bb539169f798d93c418ccb2fc601dd9a513550b532a052e24ddef357dd2b18db7428991288176b11a7eb249f561a2866e8a592677a9892b98faffba728c27de2215a1a9730475d74dd153208edc6bfc4e39a831e814fe3675bf795fa89091a983aefcede555fb0d3a89e571baf490415e648d37863011f1a7cca73e11bfe70ad44f9f1923af2a0dbfffd6841d4adedb58b54eafe66f703ae665ac606b69a75969e46fc41f291d88fafa754930696d7aba987da15248636df50839988f2beae734041c0127e133ac5956351205893520ec804796576edcff239aa4fb65185a182c3ce79cac85938f36dfa18650812e0158cd0b3fe997f8f6e9cb4223f3306175679ba33c6e8b8d9894760a267fc23778573acaae3ad14d1c7bc0b5122838cdb4d6c9a746f304e6bb85874607e953d83e9f3b4ac75150a3ef5ad905ba909a802226c9e94242a3db7d3fef355ef9e77144cd1d5a4bfca7e7742ba3e575d02efa7474a00b55c4b3cd176cbd3bd945e642f2322587f3199fa445da0039adee4c9864c22224d056bfd001d3573bd6375fa4e1ee4884d087887c3cad008c8e1dcf256a966de29b52d4111372e384b11d3f7324c38e22db1ab82a83b614d8784ce7933ba3e5560c1036b92af758f24083c5375020d8df709f884bb46989f6951a0179766abaaf0c3a4877234ed13b05263cb9ca2186f77f14ea4aea4c72d8c3e0fefbb81f08d2b2d7dfae1e317e2a47111bff33c947bb493060e178dbd00d9cc7e23290fe4b09bc5f6cf3d897fafdd79adaa7a8880c2e97e39a84947143647fb331357fd14f377190d853916b554899531391bb1f1ecf1aabf1fa3b71a2f41d78cf154bc3b5b7650794156b266323368d5b49db28f0360603500267a2e7100a40e499ff6b61cd5d85a03a8e3e887f913d3f309e37f3efc08cc013aa354cb18c4385d79197047676f2056f87ab7331d64b8e3849efe0bfb4590ac676a1744bd2101796a49ad4a0f6e900f84cfb48e7a1263d5a8f4d498127944604e966b424a65f7e682c633a23632b75e7eb610cb6b2048af68b1f74f40c0d708e2f1756da9699f6faf5627d4c38cac7f0528b4c6d854518056c159f85784b4d5b791ffb5d1c5cf54427a40f0810cfe24f405c6dcba0f5975106670e104a0f1b6ff6428cffa25cb2d9f8713dbb3000000000000000061060f0100000000c49c8ddc38cfe53986fd487fa5736405fb59828b5037381f64740522681465e44a8abf3c7fb18cefce9d02ef7429773b73bc898c7cdef533a1bf2539d14f5daaa1b5ce7747148b95ed860a8304656190d941d0db7cf83d7abd575450370ded3700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fb4f187af42a78125f4c5fb204ef37534d17b2f66441a4bf2be9273646ac4857d82a8efd43c78d778f5faaca01b54eba5a36d4c3844b4bc08e06a178bc37875e54114b2f18f910a2c7a57c1c515efd720d5b98ffa3d8ee4c5c6fb72351cffb4abeb2ca31a5944896c5d522d33928211c98430ef2e4ee42b9ced4c40a52e9e88d0207e21dbd6348ac17c7a15adcfa6feec9fbdfa14349ba91d2ea14e983902853b303193fb9e76ae3d9d015dc5223f0fe0fa91b5cec0503259cbe0ebedb73e373ac0d0a031f08999d957544e7caaf3339dcda8ccb4d2073ad756397ba55901aa78167502ba7fc454380c14c771273e9dba7ee26ff3081a4a935ac50830ccf394658ea03030e9c94905c05da0baf68333f9a889572dea012e5feac6de63f9d24cc1226266f020ecc5c6efd2294d9eb2bb6addacd5f0b2d699b393a8abccb639c81a03ade844b0311987cf26ac24c26d852ba3101e68de0c55645c98e44d2fa041bdc95e7e6d55b032046b532797e7aaa8d0412810afd1688b6918ad4f7532a58ee891c87d897226302284ce026423885d88c4e7e7b559a6a285b5d746be2b5a48b11e6e1a17816f2560a2f83aa54703738fb598197e29ce82e93942d83733859a7fc595e9e1ccf540b4ebf264006a489dbf2f1aca99267f4790e357349c6d2992475b016673a6c1b006f158b086b6d3d612eff30ca6f5af00a49e884e5792972bba19f0262bf859a4800d65898ed6ba2597e2f88f72f5a1f3d07dba6cd3bfc71822166a45f14133cd9b6dd1e9e853f94d5bb132ba09c6ec60b7defacf6251c1a6cd6022c431dc7bd7dbaedb732ef39b37916dd609f24dda32d21b5dfe67790537c086406966b181b2ff2aa861ccd8232c52d55e739a6b6a988fd629468a00c08811cb0b5bbf1bf3e14d72bef5af12915d1354e818d902664f60842ad808e2816a5201feb695790c1d21c2ab4dac678cad48f946e0a160faad4ae1e7fa86f2449f4086d3866a34a282d31f4dad680fe5bb088b41790e6757e0e3b4cf00117da6cb4fcd3551954a7459ca2d61b1097abaca512c33fff400ab0bbf987ad35c1bf8dd9039dc060520e3b4483b22d4722f5516f84a806f15160c88537e8aae3afcd637cc58e8882a033797452d2e0441a15fe3d2f3ac76f0f32ff63708f1e68a1869addb2344df940376fcc0b1a95c37a27583d2a84080a185c00a81ef7d726e0fbf135e8331f09484f4a9d823c50babf33beec2985a71d9006b78c60cc44761092f82c93be7e200dd48b28865f20586bf5c382bc04d1e460ad29f49825e02c9182585c2bc670e21d623a4451902ff03c06e75e9de8385174b799376f34e07cb2933053c14dda6936c9dc7d292be56e178270a3d52c988e9a6a84926d0daa1838069e26e378d03311403bcd2cc4327df86c42d71ea3c8849e2f40521e76c8e3ba22991e8ec724a62801ea0da8f542863c33734edc5b043f0deee43fb7e503841850e21e1afda0fb03f534fa664ab7bc0d967cae323e17068ff06a1c4fe2db3fe05e18c2f63fecdaf216341d6ef9e25c85b7236b63a8f142ec67405f4942f4b572eec98149416dbc5a52a20dd4828d8d1080c4193b2e68c733d28a44edc08a3ec3cdbd99e0827ec9a7ce3b769e0e879dffd8575320ac43c6fc922781918575d02580af9044d408503402a5523575e64fbace3ff418a331db7fe21ab98da44811e2a66765fa2c04c4cea9d138c4a539ca73b6f0e224eac33d9bbd35a3d3227b3435b0e08a57b9be9212380d78186a58db7b411e83b51e5f58895e4f0bdf3ec3cf71b1357d53454fa0058f87140832d35d81065b3da0a6a8ee603592be15fda1763a7b6738576aba93978042a4b6fb0f0140ec4e95fecf8d806d398c0f3e4ec841b8ee98df022b5243e634c3e07179aaec1d7ca4b9c98870ece30d126178313ebce3c1592a2ce1660295f3f84ee0414f1a66bccab470b40bcb1dcb1741d3baf9bf3d3472f7863eada3dee0c1631c2a9b1e7b745a65ce291f07b054ff8ac3e5b3f56a74dd41596fa4db99835c5f952e367b6e964bb93b8837b6d8b456b38fa89cb3020c13cf32735d19afe0e3adfb8ad79c934ee51a43b6eaf14a344fff0265122768b891fe3b76be9128dcddfacefd2538c631bb063207d36e834fd6fd7087722b38252191f9c59172839ef5c13d230514bd7d25014d0238cbee91aacbdd22edb78cf68a8961e66dfd26c13c7820434ceaaf6c57a2d7db8b43d3271ba38ab2a2a09cc9887f342c977dee0049fa2b8897a7f0872dc879e0f5939f610cc51bc30000000000000000703a560400000000b4cd80fa7e467b58ebd8a8b1184177b31f500d2cda4a6b5f90f28f2b218c0e96b2ca02d3348f942b70cae0e7cbd538f35cb97a7f11624be1b21c123395543d83d83f8f48cc650fea4da3833dc8b9cab2d4d6c72f185a49aab1e7d2714d723bb1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005979b39518ba30c4c13b8644add18ac10377a50aeca0a0a66e060b07f35442d7747bf5f0f6d0c6ca9690a37c50fb964c3c419dc6e8c5edb2224d4eb3b8e5a7382bc6353cdfaef82427d9b477712e41ad07649c1e56cc7a92654a5608b55dc13ce34d8c8363def8021c401e87bce2798cdc44cc84f58a8d22218863d7d0cc54110202b43d6fcc748374bf0cdbfa6c03b54bd24fe8823163e2e55d45c3b0265e7dc9020683a6dc6406747bde1c1ff77bd739a27236af0c57f90cc843d9a4377c841c480b0b8e86600b2e08f634a11ad3feb4b2364300f5b78cf2b283d2330be9bd08584f2e3d24dab1aa036feab67703336e7120e10f5918d882ec60e1eff8a386d04487022efa4b50a4e208a4124998510505dbd8f29eabed8c2a63b8b90af7485121a9a903103b4f1d4bfb4f3314fa8b43bc865d45ad21d20bc7e3ab94199f61eaa1b73aa0032ddef1286be8f1fcf5721a0b1536fcbc6866c41623ba5840a46ad7ef63481a57021bc9a89c3a6fd3172b9bef3878b1f81e48736b68142b853383e84e5fcc8cc7fd030da93cf23a50e2e8a4ff32cb0b1ec6a13263e20c87257f9e1eb05535a13d969e120d5072baf4f6f66e0c0e19e94fc63dce9b4653f9c7badd4c5e3f0b454f014b4302b318501f5c7056508208299715f40b31b98cc7a262c408aa1b4c6dcb4c8189d93ddd26e0306b4bce8c59789d8bd624d43952b836bc97be6dc88bd05efd714b0d199ce064e61aa33d4762b788d57127ff2d929aa4917c3d948ca5f955cca46b1b563f6e878ea1c3b83dde60762060dee8057de517b756aacbd2060ce9d5e227ed587e3d52e861c91ce6e25fc14682a9f9d63ac465286c2476e25904a3e6e46ee567583c4a95821d620d6a1db247ed9c171804facbb6c7d8ced34f5f7f29fe7aa9bb0783b237a0e6cf2fabd0b33a3229e0647ac23877232bfac1c227486eb5e8555dcd140aa8749c83dd851245c8b9efde686a820d19d58b24dad8c6cb92b31b53e3d8d3458e42b8a3a53799bb783e6f9c5cfe10a172c11125c36d0ba84f655595348f51089a09392cb6929d70b7ac4adaff62f1d6a0f0902785a08420242e05616ae428de339e78c4c6b46102831fa00b571301615996d1fe47e1bd46409046f792152bc409c3e134b531ed9903f81227b6bbe8e19110cf2787f1e69029f3b647f78e74f085cb5e1afba2beb3dc8d4ea6a352c0aa9b73e51dbed6c92241d8588032e7c3f1483dd2680a336ac95ef743c49108233cbd4954fe5aa497aa4426f94bdab9d1990c55665561f7ef8efbfbc184af4dc9b0ef3a676217117f19e0b5f6c0961912983c46c5eac07af673f324d036c3eeeecbe8e4a42c0e6d1225e994d20603959e0670898622241ef3758eb25ecc485344ca5d3fae029a39d6e0e4a0a09926f97813698564fd333eaed5f9eb6878c14bb468de53677874f986d81a9ac0f73b3dff5aa37769ff66aeea7278eb27aa5f5c1873d0280082c989a889eec582a21e3145604ad867959b000bb05759554f3f81c6af10961e0a1098247df616a87d010080e84dd5a424d2dc15778b593ce8707d8bc5e6e9244df8d7485f0431ea009dcd62a9bb9be26824d5de12bb83778d86e4e20d5c2c9ac38bd8f7f8235c6ae10c4bc532cf934568cd8deda6b85e96032cbb3bd5a6adf7039db402c30783b8cfe8c48867120e9323c19657c6af61b371a5e82b4031ef2749483accb6b5546f58470e2aabe2e1b6ed07af89dc76eedf15d467a5f3219127904b19d993046250a1b1a6d6b59a9f301b639d403969abe1f3c671856120695643a1fc46110679dd7d013afb0a2df1c290cdbad8780318151bddd048a1a2b3622af5fff5b1a9a1e1446df823e67d4e625c2a22411989866f9b14729184fe8e29bd6382554daf9822e0331dad7fb0bf5db45696bea0f0de86d7f87b965d324b27716b6bb402eb3152ee26a5195f8da741e38064e812d8d9ea3da5a3324b2b3c15f3b7e93ab3f00224e3616d31c65aae1449ab79d934b7897e77312676023686549071d24162558542202a6c933e729581f6b0d7ef447a22d6d185dc194e6c2c5cad389350539b0088a6fe9d53d63f4aa3a2768774446de7d6e6f5edf346ab65bc5385dd46b0407680c062baa157ab9962bd17299cd42e6742f2c929c1a06783a3287822020e78e0a1921eed5be2b897050dc38d7a7a0edeb95527d7cdfc56b95866fe52c14416c1a1595d72cf6c0ad9df910eb6923231b1ac2fd48e621613bbd137ca33d24ccb06eeac1fd993c231760fba44b9d46b7f704a61f51f0d72a018de4c26e1f2ba57654b4222ac62a263e649d0fed5ad5eaa875da6ff6265e89de92cbcc85f0a150e9837c7177d7b6024e8fcb062172c02511e0ac5d3923942ca6196bccce532c4eab076055b15e01a21e65501df902714aabfb808", "", 1, -813602244, "7083eece9646196e0b4f6f8db28e41d176ecbb03322f4c9f0b04a3ff89eac319"], - ["5ea4c489016714f3a65e6f273cf22e01357b0fa681aeab47414fa1744037b338ac7924022d0200000002536affffffff01a95b080200000000096a5253acab6aab6aac95fd3187", "63", 0, 1986878410, "53759258d0dc155db40aaccc7b34fe5b4f49d9fd57ba2f463ba04ef96873b019"], - ["d7f958360358ce3260c199ebba36c293fc660200577f629d6074149812495b0e994358c4fd0100000008636563526563acabec1e706f68b3d2b8de3ed03c6aa6d294dc3f02e8dca7f8c7df58d8f77020507374838cbb0200000003655352f89a0cd208c36ab6dfa02fc53f11a5576643bb28e60a08e0c5f59f18fd38fb983f9e23160100000008ac51526aac53656affffffff02ded5f800000000000552acac656ae3c93d010000000008ab65acac5163abacf6e2847600", "", 0, 1781783393, "84ff06f6522caddc6514b0844d8cb269f43c2634f6a0e3faa4dbb4594cf5c151"], - ["a72d7d1e017d68dbcf34506b073ad6ca2eaad4a629b6d975b7493b549c04510d6457bbd7b70200000003acababd8e95ba00400ca2002000000000752ab5165000051c93bed05000000000019b7ca0400000000086a6aab516a00005239e2a60200000000095253006a006a635363000000000200000000000000009c085c03000000003a4f091eb383e9aa0ab3580ea6eea5dc24f4de0015105c9f80322a1374f23125a67b2cddf10507284234d07199011529aac869d3c2a124560eea8509fc7273d1cb565ddd2d406d287dd03795ce3fa64fff1b5af2c5bf71c036e966d1fc86f062000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008aa1483da628d8e68eba1e2326b5363149371d7672c907dd5577b4885f69719562ef7f59c16131141517084f9ab75be263d929cef077244fb63c71557e08d96a7a1f8a48f016ffd838d858ee08487409d389dc1c511233852a1213bdeb8c93abfc32904998378c8aa1b2447c7213557f113d9ffe6efb64ca80a436b466ceb2500323460bc1f9550cce564fbc5135600a5f37f654ed797e61d85b8a6c919c807232032810ae34212019e50a5fe9d667685ea7b849a69137b2f8b0d806f09d19aea6ea0a19e256d5bdf94a3f1fe69710778b53338dda20d60cbd7eb92c411a35544d093e1ff4ef2e2b66c07bb05e7e48272587d1b331ab41c32f2963ae6986686027acbd0315db3710f5aca07c82027347527b3951642d3e42fd3f05bf568429af2806e12a02299553e3b3fc00de0ae51918f623386cb348622b2cafcdf4995983ce14da2d14032355ec8a9977a439174b340c929973a64a6dc93f84225fc911265d671e72342d0301b373cee741bd55f8bd1ef8faa441549b423109bf972a44be1d06c4eefa7945020c1403592f95409e5ff957dec44ae6d107db6a59f3bf9c08aa46b1ff86f4d15238a4cc5b3f7b545abf88011e7bb324d8e082100646676c0f23576ff8c476fac2970f5cd9cae2c9f5d287452cb4319e848b3e0dc643372e5daea23a91b10d83aa7ffe5ba5b326a0f0d39462e082cacad0905b34efbca28c942fb2201f278892076b1b6a60348bc8c821835f7415f393baacecbb48b261da5dc92485986a14882e5ca9fe6d75982b05337fcb192c957738d5c434a7587c634e6283151aef4d00787ae813f427d751256abb2aa16c7c866e38dda635d869b8cd4c99716d0e6a78a294771015140224cd5cfd756154a7f1c921de433a477a21356beae799b2a04299b80e02231708a71a0c90b7f7261ef78ead100a07168a1cd622df77ef3256f9d28dd4aa07998bc651485a0e54c45cdbf7cfe6b93112db02f9fd5c84668cc289bcb4b47b8056b63113470b8a71499bfa80f981f41da79e00868b4e8e04e401e185606d965ada7d6c533c8f49dbbec14ab95be859c3634e0ac13f4d2db6cd2d00d55aed9eb95c62ed27e7ccf7038eeedbdedba67e20962419d54e392d1fbc796c52722888c9cf66ddf5fc6b00808e871daef29e2357fba08e480789d0590e759a00b622f08d5387267e84c3c57e95dad2dc4dc69931bc15fb64b6e8f96ac22851dfce7dc0c7f573bd486dd3630eac00391d11f49ae7fdf59b0c0f8cf6550a866b27cef66f64751df72407b2299f10cca1bf9f846c78ec64f8ea802b24bf3d13ddd0f4070d3024d345494b914f8d58695d4b1ba88261a4a7811a7c992e9eb35b82e84be4da9519defe94fbdc66c73a9d4207752cb8ea1f6fe946469d656f7283b759f3f3d83adf11c941858d0977785111acb8570a4e8877e41e0cafe038cd6f7b89dfe62299a6328b7af50e53a7f8d43c986e5ab53dd3048fa487504fbb96129e8a3ce45a0230584e6a2a83a05e4ee38d6ee957354fcd937b9937bbb284b5f8a3677714774ec9971e29adcfa4f494e50bf7d883b8bc87e348281d1e5e5da66482603369060786acd740b3a644fc95117f9a86098462a135c5be52b96954dd93676894ac129c4b8c97c4c794d8d3c6dde2ab0322bfa551f41f13176d3e1d0691601b4de53fe2d205dbd7fe978509d918b0b9f0d7d9e7aa8c60d867501481fbe1d2baa3f0140be5413aed26c70e9b883f120ecafcc1aa7ed4cdfd7aecdca734027576623ed46a5f16d8abc8f575d3143a2e16c6803db1e8ba8bbe68688026884934bf69fd58431857542e55fed9ea9fb6d8fc8d6f9adcf0dd09f674e6e976131fbc563220f6cb0dee7cf5350844b6d2b9f361961c3159aceeede01c26bed60ff252a090067d002db499af03af3e9b76accc1542e3f09dd609e94424b37aa8d5f783fcca1ca1763719d1a71180d95089811c58d0e2448605df87ec17780e39dc92933d8a63260a694a3b213433b5838880fd7e5b39441dccb350434c12e2e5a14cc08a1a5837634fa61b980bd9065b413b22c9e044e6718ffb3f7d8c746ba57dd12d5aa8661ea20dcde200e47bed161459fed4053e7df1288cbdde736986f66cba2d2af0c872d88c6ac2352fa099c4515c15185eb6060dd4cd5711cc3e55760409cdc5f58615afa6f6e1c4a043f6d8f377048be2e8d4818ebc92568ca676120099352572175f1ce77cc3bae1982bfdc4d92ff79d9aa35f2a1806c69f425a8afe458188a7e3edeb11936aeef5aedfad2b873f7a9e03bbabc50200000000000000000000000012950f3400ed62d552683039e6f379df179d4a8fb56bb3a13c3531e751d9c4b3cb4257f8456db00c91e2bf0e851705f8bb01a74f42c6d823451662809dd6829455387197053e0aee493ae26a6678f0e7e6d132215073f5d9cb0c353113067dca00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000987bcd528e07c326be002b865eb2804727b514cb5cb3141d076d065c7a093b6dbe6395aea08301caa9243633feec82acd4b5aa5708e2a44ee2c9a68fed7671e2d85267c43fbf43029ebb2c4c10de089eeec3905526188355970e991066557cc73801e380484d2eb8312d8570f6c4807da7f771c35a866fb4cdd902010d0399840210b48b6cc8bf1e997b569a656d0c314055609f0b0cb20ce62c1f56778e9b4a60021321ec2f98473d6ee0e68a028ed759f99fe813db98b667cdf00e47d679c0dcef0b2684eda8557b63ea26ebce009ea71bb96508b625bf67a7969167bc3d10db0a7307f53c5709bd9d9406a8618a12eb51fbb7aaad86d9f5183d8282690ec490f1790322093591e37d1f059a1efc56e059ac54c31087d5937eacd4134305b00e617955022c69a876a424f352b7cc2db8c4a115ce8f41c9ea8ba41a8614c4c791cd8a54ed0322ea65fb1aafa8fdf1edda7e276c23afdde4892b189ec57d72673543fbe1320103005304dd3bd493b4e9ba08949bb231c4f5b2732e224c331874f1bebbc67cc13603043f371b5ecb48f712f7ddca4158e2e3ccd43f7050d34a7fc02bfd6671511f2c83a8114732c9a484c2b529b82b13ee5687e85708d4b4e4a8383d0f46f5f587b3108a70e198957e05bea648fd3acf3daa6e42b58827bfc2c7e6c919ffbf1edafdf1597268b16f3f9332c3816c9807add4901fdada55aad9803960c402062bac44f54bec6a97fa98cd755eee8c87c3528e25f8fae54eaec83c52770f3c2b5200b7d0abef7387ffe001e6f0f2c4e6ff9c98cd3fd457dc39b9a3bfe094749727ae9dc7f703834b1e0b5321f8d248b38452d7aa022b9d2de3f8bd2f9b0144a38d95e4ea7b5a273fb8971a2f46b9751f20bbfbccfc5a3fc530c2dd8fba50733817bc730fe83e61afc8f81ad2058538c4a7a4d7beebd0b12220ee98ae8e2a05165b328e38a80052560dd9410c353de3ba6a8f4d88e3dfb88e0d3c1eabb502b494514d46557320882271c3fe14a2f2f537b9de01b49eeb622cd16805258cba3b72691385f1126b9389c43df51cbdf7a7cbb6ffdd7821172a043ffd3a7541a24ad856ec5a22de1a7754b446239830fd58ca8be672d8cb4f45309a88ce1c422bb48941184c547918d949f4505d57e44239105ae74f205eddf4e41d2ed9e4e7318970e78541c7b41638333027e019242fe57a1cb85a6dcc8661a3b5e4d40e24e7a8d95a2611206ba63b1689aedac3490e53351fc105efc2cb57183db9b69ed310963fbd0f1169e3b606f5396bbc4eb1e7a821fc0b4c21865c9e93b8557d91a4480bb9f1a40c406d7e49526b2efc7aef71f4d5bd7032194eb4fd5d2232c171eeb65a304cb5897cc3d2dda35c1f57931773445ee7e5b9647f832c1eec30b7934e5de2103dbecb14bc31cd58f25946c3904fe56f11ef8a980fc76374905e988139ce41739f65b1317be26291f787e3926da63550132186dc997c12d8e42c5c575cf6b193d8562ee7f3f2d2251c1759b25f7fbbb4c814fe4a68de5372526c4a46a03d632bcf2ccd633bd7c7637339b774c8af5174d045b5a299a72748fc1446513014d8324c1c29b448eac36094023cafc30bc35a436acf2a6e941d80c9be18cd55cc532b2c7da79fbb52fd1f8bb94fd935210ed6bf7471f89e4b6bd1ca1b963b73099f3bb0d20e0ca571b0d6f6dc65a088cb555e8f4cbe7f1b6ec27778cc07dbd2cb38cd7fd3e6f15bc8c255a6ff3140250f9618946b5cde62eaf250120cfc74e096c504707399c96c93fb822683f1b57cac53899db4a7c00c5b36884c8f24c58b7b5062eea2e066752c3d4bf715b03cfa93da8ec1786bba2fe43fefa9c8b4c95e26b48da190beb3808062bcbab8f2e936da191184baaab34d84a2318903c78b9a432cd9512ac0796f0ebce937410331a01651a2976407d2b1661161733ad46c8338beb54e0e1fa3dea001877fb608bfedfb7107af4cc3096d8f86f86a10e94db4076b06eb0dfa1fee2228de863d9e6170fa93d626c65f18521db940a4afc4fd76c1fac543bdb95b39dbdaa8d74ea8710d25078ee431d0aa66b28b7b37f63c349bec05332f02639c952fc289f2c352e5ece2eadf2ff9d3494989e65af2147560b747621854846dce9bbfea2249f3cf41067150f0974a4461724213b00494aff114824538efdb72916bd41de188bb6f82c70a1d132ba496f651bdc980a8e662545942b0ff5f61a00daaf6e7dc1f8919825e6051ddb216829c33a93218b4626dfd5d642c30fbbacc7274d9a32a3702f8f4f46c42d9f2de5fb46334919cee98a87a70049baf4ed43fd47267bffef5830c14764ea7c347adb4e41fa61f815c95c05e9291269427186b983ae1caa852a25580f2059f139a9ba1a00774e89e8ccdf10022cf9b7f9dc028bebeee447692e49d4802b8fced18589adf0a", "51655265", 0, -525441219, "cfbe0a09eec11a0f693f276faa37f1cf8b6e7c764b1e825a7e695daec47e9666"], - ["f65015b3017dc69b6743e79bb838aef89bc7ef4ae0a26b050253f844e257830e4c3702978e0300000001abffffffff01f45f96000000000003acac5200000000", "005353535152006a53", 0, -302394150, "f1f1b20d8c6ce70d862e2e40b2778a130216ab8f4546d237fcf44641366e946f"], - ["398c7a17043b6ee9d7ec2641c492333e49aff0cca591c4f9ba82ce5d50981df5635bc19eae0200000000ffffffffed78927ab828aaace41ba45f264cbb946c4d19a7c2fc5cd69a342fdb13ddf4d00200000009ab525163acab6a5300ffffffff282c153cb9134c26fd83e5712e92f0810e2a6ea59674ebe5f29e31cece75f89b0200000006ac6a6aab6565ffffffffc65a583b29e9407779bc2b7fa4c59c2dc5cf3c51c259f2f4f7b24ec5b7c0695d0200000003ac5200ffffffff0137904a040000000007ab65656563630000000000010000000000000000cce64c0100000000368ab9891329b85b00547a9e90f619f1f6b1eb24bc4114dabf18743789017a19a8d556f55b18d786102e4c9c9d8f4a0982675d0132add9327aa295228af0ebc02235f0cb8315979f6f3e26504020ee4936800ab136c040596ff8c870ba74e23c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000890402268b5e02f469c884cc5b4488ced011ec28e8ad6926a3cf77e3d1c3e6cd8d5ff90c44b31338ba582ef376208a942375c5bf5687c0f93640588041cd99adccfd17cc63083e2b53b728c6fc6e3cb9a8be67f8ea323cf7a5aa99defa8eca66363f1c21e5ccd0607a63e991f56fa027be55f2422ec7a842a2d7e92afbfa4a490208d24fef37c53965e627ce2b031cebf47db3e1ec2fc47a7888f1572278a5d84f031cfaee7b72c3c99a34054feffdf105d79766633859b8bb171f80a4d9a29bd7630b154b6c53f9c497b19a7b474f1d705e9a23c29f074c9900c237da4a027e6548281ac8e1ca1ad388dd6c6584d6b71c05d2cd2c042858e7927335f8eccc54f4669002236d6c6515c22e5862d7187af7847fe7f07fa0a5057bbf8f5f75b57d05f4bbf703130fa40e100ebdc35786acd3f1d1c0980365fd8a8bda71bf318b40d6266a21ed0329767505ac9b9d9ee9d3433ace7c5790058926bba22366d0544adf7382bb343f0215d22823823284aba47fd34617bddf28f545241845a14d069b3475c78afb16490314971f36789b594717e5a7a8f1649c99c2369d23bb4b413ad422fb9c658d5146551280de397464163342b9256584e4cec002c5ed1fc641bd9af268b53e07911b1a44f63f7dfddf803b79dc3a319f1b5beea49b400bfdf4d7b552e735747322cc7c26cff7d64a14d29b465bf4056890cbb7c9c81464d4d82582d6d805568e132055d9143d1c068b13ca7375446cb46180d921d7b5c4f4b3c036142ad699555e46ca1c6c32b8309bdb33d531fefb59327d0a49bdad5e1f6396eee9792818c0852343870b3b386156d7087a50f11ebbbc185700c2a767f41d8a1a96ad80502cb8f7a45e8dbe7c4c5eec6ab7e746407a12ebe18735d358f2e9c55954dc2551ad141e751dbbdca86586f7e901dcbad341d3ca074f2e974aec8b634c332595e4c91f6261332decf7079e5a440cdb3e04e889e8bd0b8efe2648248e038b79bf73229c4a487ba0e07efb74f9aea35070e8cac550b6a5702e37b9335d72ab0673df8b7418d5d8e27ec9ae02b5619179d7b3080bddd88ef251abd6e34b1006e4d566ca1db304578544d8257d852edce03e3b60a6c1922930f09a868bf88fd3d444a20fa2a6d64ef7d16499acd94dac42f765a9b924534cc3d89adb09dbe82e3db435ccbb1398e2c7830536f16d8049eabb635720c27b29669a8b8072749d836a5526a9f13f53f227bf64f9236fe30cbfb9833c060a052e3bdee391d6753b219675651509a1d22484f70159a4a0756db0aa3dd69bd303c7009b69725918da4531dd021d46faefccbd34bf1611061f1763bad082d101b06ed31e4bbaccffccac2c3808adedf8d6313342e11816e8913dd1cc159baea4c701dc69b14e77dddfd149d2368d10a06057613cb7df8578afcf8f524cedd6c71959b9fb36983c01df2a06441dc8eafd4c905fa7e27831e63118e0b54ebeb8dd5dda99b59b22afe2221d27e5c606b659d116d65ca39825d8a65a417a44acad6cf9fce8a9d37e51c435087255f21b6f539cc04d8d790337e4c5702e648bb9b3204bf0b0fec16e2abd6774186bd15eb5e9bb75e6ce54d14d4ef6694ae305ed9cfdfdd03536958ccedb7c2cdfb9b698b88c43cc3665dd52ee7ad5e47651574d3cd91a7648a5ebe1830e1c22fa0711aaaf5c0466afc4afef98fb181da81bd88b3a9afcca084335cbb3242d9f4d7a1b7ea50b7c1c87cc4374175dc53c26b2d24ffd8558a8ea997f54b68c0b4f96dd55d27fc468508f4021f5cd7c1f3d9dce7e1581aea4058c50a3cabd6685ab20eb5a2c9a675a3c1f7f8bd5c799309befc898be9ac82bfb5b50bbea527481e5e590222ec335d156a6669028bdc98c1b4bb436867258cbb76001d2869f47e555303eca51c8b86ac22f6a08afda538b934745e00e6f141971c4475f840c3e935a6feae15f1ac0cb656b895e6146c35c85843bcce40f2ad6d44637cd6fb0e05e582420b046176b8d3a2f656a58bf2aab13ad109308683b8252ff9f29838fafb3fc59914bd6952526c2db233f9667b6e9d1cf1309356eea129b2aa6f856dd20e23ff741a213fe38357f842c4bdb1649444c5c6cae52ecafd3d0515a40bc25d7c4e788ddbc80919c31e7a7f7819eb2375d627e861946ad4edd5b0dc2def9b2d1991827318004dc6c47e1b25d739a78101d015aaba99cafe7530205c40562ab00ad4e5b0bc7ea2cbdb5cdb1da4e3514ed153b1fe17ac80405880980aed98b13c7beee8a0ca654817803397fb61e29bb80961b311a7bc3ab9a6a7489498e9b378947d3a56b898e743ea6bbe86cfa1457bfed40d2b0259fe106346c8151d8113cee2c653d8217fb08b17cfcc0ee478d5ce72600cb2128f931356fc7de13722381fc9356b9e7b3b74250b427bb01b8bd8cb8a79bc7acb687c81cda08", "5300ac", 3, 1173390327, "affa86ec3013bbec718a0dba9289cd7deca7f86e7248b6a7adbfa17eca735d97"], - ["fb3cd34b038db8d5f5486b48406a158fe90f385ebb36fe43b2d1ebe60d069e72adfb88b7de0300000008656563005152656a2db27e801657175d7c2780d1bb43fb99b88a80bd9e3ee580e829a715fc11777f205e28080300000009536363655200526351fffffffff4de5a3ce1aeb91ccd9840c799b57a837894f8c231bdb8e922bed6a6db23f64b00000000065252ab530063ffffffff0148ae02000000000005526aac535100000000024acb5b0100000000000000000000000062e9b57efd06e7adcdc5dca9b5d669ebbf3ad383f2dd9794b1d38d52b91a8f0e3ae4fd15b22494a043c1a326dc6d1aae103e2fe5e8e1c32899e7d3e24f0257024e0a4f9d3ced9578910ea62781616535d69de5bd83af55dbb6047b821a4eaa460000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012edbcbbd9f3e70287d7db7467c25d42f7a398d24ee97bcf0e5b79c51c7a84cf055fe123641f912788291e6549b1241e9b305cfd5fc97a256bd953229e3b627a79ffb084dddc135c5197649040838c49ad6261cd0020ff85fb4497bb6bca801879d97b5ae044c3338e26402528f5c162617f4aa1333779ef635458316d472660032aa3b4ac31bf1a9ac64924cee0d23a932d84f0fc168013db443754069df3a99e032ab3853086683261620f8128fdfe84695e44890d045d583b955c16aad3e1e5820a02e7685bdf3c95c058ee796e35ac38b26bf1b2b21bff20266145f7b3ebd0d7b71108d36b3f1691e3c755bc1fb989fbb13a5b0691e36098d94b028966afc241970208456f2e6dbdd48470ca8b5d2478967cd2f0d120ecc3ee129aa285df40f0f33b0315137d1b5c75452f60a7feb1e824cb5317229003fe9ba3bf85d5a7f598aa83b403245121107a4c06bab76b55d2e03ac9c0cca49f20b9fd3ff7b60f7ffe6c87756b02034e5bfb3407b2a1b436c899ff5432cb22d525c680b2cce874a632fd27bdc0b1031728d5eeaa7ac535a1f656c4f5a3822ecfe3c5bd4c65a35f422d20a55fe00bb85a9ed2c53a5e1db94b89440b1297985a8faa8291b0befeadb17681fe2c8c0aa387535b79491c8e51f9c19b8b66ba9f9e1c362251c4378b762c15a7f3b9df101572933d939d3baf0497e2b0b3fc1403161cdda7ebfb5fe84865f9128ed177b39a54d83071184e059de0ef19f65ffb462add38442734cbf261340c4e4008ca259a08c6333a7a562498b5a57dada72e894926a40ffbba99e8b95d91bcdce5482764f353dbb3fb8724096d4a1dc6367f95ff617eacb2ec1a520674584ae708035c25ab6dd78aee9e8d3ddc26af42582df2c6a0855f71827b41040970ec1e37e1cf516107f10fa70cb7f8c25f93e812def78bac643bef7079876e6e63701823656100b753aeaab1b1791fb336326f33ae0af46d18a5bb23d520cd93d677584df271aad8ee2361873623e2e2963e99472ab4d6394de87add71de2822efa2a232b5aa6a03739d57514598d4ceb711978a3e3f39ddca115deee210e03abb53e3e53942d887bd8baf3b152bb7dbe2913132e875c6acd5d5d9386f403ea32132ef57fea4abf2a539859b6eaff30acd352ed8ecacea8fa5c28bc133f7e99a6ad248108ac052292c788ba963abaf41a484f74c81aa8a2cfc17184cb4049f3ebdb3aa1f1af2fe27fcb48b42f1c367851ce2a4817d619f8e2a0b767abb150723b6045d8750245d4d495364a27eaa20a464ce75a0ef8631a662e8924e86b206ec1f93d58d412a8b1a6e60b0ccf0e9b88162fe7b0e53c5c9982ccefcb9ae5889f9a2f20cf0e8aa9aa9160fb21753030529615c7a04c718e3906d297dde6c3b0506889b19a00d2eb7b8c0bf24eb46ab88486c5ab6f7545e085e763cf75b62493fec611dfe3302d93b8b332c8b42f481d0fbc38db996af57fb619d2d7266bac7e007228e6d2d1e931f400c02e885a047f8765cc70f6a2f05c05a2e39a428c3058224f1addaa92a3eb5b74b84b311c124e95782ed7f51990d94b7eb4fedd7c816ec71b3822285fbd397d911f232432c2b106c451b77e03abc8b850b41de42caebf19def9bd2528ba174abbc6cafd0289a9c9112c2f541f312d67b8980ae7ce3d6cce0de61dd5b1781d9cb5e7d3e4ce67c2fd3fcfc77548cb08b32e98058789801a49f59a32b44995372ee6aaf8e8d1db1e00418fb71beb8e18d481c34c4e7d2f801a53ca119f698fb7964b86e8a5cde6c13b49afcac9bec19bed2b291639cde42b507256b68bab99cb6101cac9b8a49590c7bc51303b4bb1895647156ea2362947ee992b357ab617b755a823a2fc4d1a4dd8fd2e81c4761717969ad4c74a6a01c969e12eabef05519dfc70a64b8aeba5474b9c541ab1868dd71e62c9f45c30f5219a6411d0de0ca03ea65a91b72e755fe3d4dd3973fa6fe48b143b878f52668b39502fdc128209bc02added2682b52e9bf5e1c20353c0ed8e1098186587a394c877d5f64a47e5305da666ee4aefb387a30c3a1c9d4afb5573145a78aea8e22a611ef3e3a752944d15a89a494494f611f8e0d7f501028e333a4055423368e7178e983b72ad1936257b85685d1e21f8f24227f256aaccb2d2bf41f3a3b621849ddbc0b5b65d95e3f15efb59135c80beac4c25546473700586b8873b15d0e2940999167725c89eaf9a5146eaf6d893378e2a1468dd195a536388a42436243e6f44ace743b7f9e1ca0270db31ef0def8e3a95dcfaf23071246c07582efe0000000000000000d9e0fe030000000018f9c49112db256448a397df0c055157a0aead4515c4609e17ce6e5b881c9ebeb8247c17c4e9d56517af9a83057b93c9015bf3f02b7ee3df4228cc138d6d3d3f9fe7d8f7db55153fa5eca89a1aa5fa51a3095817e0e2fb2a6aa093d5d3eb494100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eaa44fb89fef41ea55ee82583c38302b338b0992cd7ef32c5e3107f131d9e490c996d995a48e152c06d757d572f9a87159d6c8dbd16c731bdcd0837e8a620cbf4590a022b67d05097f07c2a4fcd155baee2441e43b90c914e43071a4c5226347d22492c352d17efdcdbb95d673cf2c980eac16e56cdc2a41bc6448178f6bef060207a1334247cec9e557e7625c200aea1e88d49dcdb1c371438195ea2de3f63c550313d999111c62d0161c2722ab859b95475b71c5a85e9858cc909c72d99ab4b5ff0a17926b5825846dd414d9d32a734314e2f92df3b29ef20cc2857194e165c81fb60ee5df53e01830d389629e7296bddf1246cf77619f2a568faca11cea4013d1240311b41815d3cae9041a1869001ca09236886e44325a8329ac7d964aa5161121980226b111d5a1fab1f016b5109920bc60fdd977c1ba2970038ea21ad51129b0bab803085c358d17a2b026835d3cefe8fd7db5dd931a099fd6eee3f4ac152668a412050315745d6d6f07328bb425bd731dcb40d27097ec26b0fc498b8df9a4be283f559f0327574a248befcf9268291b386f634fb589c607ec4f235786eca43058625bece36a98848b357c8c0e485a7190bb5abe79cd918e0a20bc7951018714df227d3a376b5f0977d5beedc3243f07de065991b583ef46c5a322b62bb1a2dd2af345548b416a4aae20fbc200978817cd6ee4a715fe2fe909d528bc8e3e48185e75baeb7ecc0b50a09e7ddaa8b0ff027316f0f0a9e1dbeedb4639afe334f2025c9cfbdeda46e176ba5f63d49d6cc7b2184bbd05d94e7761c136406ba636673bfb886bb60690d352a8e49425d7979d8babf78c4dbfa4dc105518879400a913825b8a65b77ecb4a8620d154795118ffd82201734e52662ffd818973ff98865de5e6a0653f7d2b1ef333b250a6a166dc86b488413ba782e5c465de887fca598238aceca40fb2c522ce23fba46fb4b57644fd489207f11b37aa1ba24c9d01cee69534df6b5a1980c0fe46edf1d457ff8a81023c8c72a105ee501b191639a371692a92e7e820c31dd1778f90d538c58d78b79f6c38d647eec2b7468855962f614ee7f760f6f288f015ecf221f9b8b8ff0e5c6cc124c153771474c82e36129055304902a8a80a22c683f587f1c97743e1757bb9eaeea948c0a58a0a8569aaf124c0fd42e286c5d7a8a1e8095fcc9378d272b8df159f477e790592a38dedbbb8376d8f8ab56689867e822223d50ff907456a59aa127cb9df3745958eaed4f1d5a42d7b4b0b5b730b322e22519bdf3add4884f2f3a096b15bf32706c3165d6fd4b6eff60e84b23fc6b9e130d39ed24345c3ea59f83cb9331a697425a2d760a1483f3602a96ea44c21bd93a65b6f4cc7d4d0727596869868273f58c8750cf180d8451b8576a87035c860d7b43caef4249637a7b17da5207f7d6ea5db839a7e1facbc1c93bdb425a11715f7387caf4e1062230cf2631eaa24987dee0dd1d1b3988c40ee8cb1d62b4e44aec140cbc87b00b344594a3f7467705939c613050ce22b879f43356c89b0c6bd5d906333e3e1d1dc62b654c187619196a56f55543d069a38ecb50a9144c8fcc64d93161344ddc78c4c822f72f069ec8074d5e1277597fc010d5e133cc1e53991cc220de915ec53bf2937d02746642bbf91a353a90ec0a9236492bf8b521c627d2f2c9b20dd37d772e0314eb0f5c9b803b42b04bac1f8f190ba6ce451516137ccfd51c1af3a3bcc0b1df44c17f2a9e02b7dcbfd5c991d719e66bf484e9d28479be16ca2c67b28aeaf95aa6d322666a694d1f23a6bea05a4302ed367bc96170216976f2eaff74368b18283770b4888313adfdb08e7d7fa3658b0c724d56a107cc3b76ae80d98ace8d43cb48853d14b2050131f4f86e173c26e2e3a31c6c2903f86402bbe31d83d6c33b1cd9a280f787ef13899f8e80eb928956ba1ac998331e77328665cb2d34691ef7380d8ea038a7ae61b940b7f78160cdb853e21a91e70620299202bb3d1f20bbcc25d5e86bac30afd849e113f8205eb185c53b728014286c74c3fb3f7519029ecae5416d6ca673457630a8b9d5e4bcf247a9fa80cb8bc9102757efb8e1701d079e6df0095fb2ef2a74e879e1f3bd6439e343704845968ead749289700dafcbd75593b6a6ae06b8b3251cabadc68758c46cd44065463ea00e82416b1ac0261117eeaeea6b726946e58d17d5d9dce329d18d8a7f5e123238634feffa2654aa881823c9a4926d70be4fc0a91fb75a74e537098b8bd4a1f17058f3aaa364d90c13b8811a79fa18e6852fbf03b068d415f66ab0e9bd5b90aa77160257e1aa2f01ae604d123938ecfa89b7ee68f78ea7aa1b3c5aff4780f19585bbc18d2a5d3846661aa7f498bd5705b5a0f0e32a25c6db81de8f3afd48dc2822d61673861d9e4736a1702630f06f01c9d0e1c0e", "65", 0, -418714248, "591cecfafbca68740e991124da123d95f95e313da1f957c63f6540e3ab6e1a54"], - ["4833a89902352faeb4132ce88d89b3d894f20f2a92605cd136418c8ac7f0f87c7161c6e5d50000000004ac00ac52f1c7304e1c39400e7215abf70008a6aa6e7ad2d5aa5634ef6db67d057b36ffc8c801ca9e03000000066551006a6a6339aaccc30130114a0200000000030000ac895efa01", "636a5152", 1, -169617858, "d0f2e4714914fd6735301bb06b4240e8684c47bb6c170a1462167c0364995c24"], - ["c7e0db4e0130d96173e798b12f0b200eb5e9223fee3aac944f53ef7e417cb62885e847fa3c000000000700005365005200ffffffff045b89aa040000000001ab0b8dee03000000000753636300526a6aa14d3b00000000000100f4d48900000000000353516a00000000030000000000000000fe50eb0500000000c325257fc10dc9e14a15138bcafdad9b77a801dd48f62fd33a98b3965e68fa60879fcc9e5aff295a0702bea32b690b866d93a7c4386acc4f9c0cb4def6938404605a18f59c87007ca91fe74960b6b77135c94ce5b30a44baf451cabe11472dba000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f3ef785006d864bd99722056f7091d25fcdd11a7fb744162dabbf7a9b90388c90aa7bcaf6b8c298f0f6e6c61174e7a23513248bbdb22d4f15fd92f3abc5f05e8dccc75bbd15e359f6bf12b3097cfe8bd2cea74d6266542df9338986b8b322ceb8dcd81d10c4b103c9a733ff6b0a6136fcbc84871bdf917e2be6b3fac039cbc60313af798c98a1ec0c7e9c9ef905177403d86898cc7e43cca15079a2fb426b713f030f26fb6d687eb3db4d3c5dc3686b3d0aaff32d61c8605cf7cc20406bf129205f0a1ef49d4c1713aba42ffb4dc88c845ca94f0a8c97cd2cb92700ac9b0cb244e096248929eb57f50b05dd01f63a77dd9afe054908d33a104a9a94bbc4ac6636dae70216bfe9b6fd30004f1dce37eb47cf7e771035eec0aa5e2a4f0eef5119557a7c850305854cc4e09289082bc14f69b1db86e8406ad72306649b24113eabe0ec13b019021adcf24b30310958e0db01d1964d7ae79e11163ee79dddb503455b43712848450305e2b49ac4621016ad56478f9046a4d8c162e3668ec88a0d8f15520870226e2d03156f3d8a7b0437abfdc31850c9306c010495385cf1bb6c709769d8a1620e67fddbec5ccb9aa78db04469abe5358434e05b291baa788c38f7f56ae5e28a116948f793f4dc031bae4a604d03f245f1962b5e1cfceba557c690d35fdf1d2c1007bb5511e8350c1c8822c9bf920b74564a9c51c43a0e5cc80e8b0bd248bf51b2f1551268e5ddbc44b43ab744501f55be9bb2b6e81415b441191c8f62142a06911f0a0362ebfb41d1c09532778e2d735acbeb3839691aaac63c48518dd28bc5eb04b12799c8e97fe144e263a0203d297a70dd4f1c4825258d601068fb8e74bf12e4a32920e0ef4418c1eefb9e0dbd6dff02f24257eb12ec1d8f5978cc915637599fc1565bc81c063eaee6f357b6cb471bfe31425dc01fefbd93dfaa25f517c1354537e3d4f36959853f7073090c51964a5c7a0faab24a829abbaa8879d311efdf8690acca4ef414bf9cde07ae5e9f118e9c96f112556b7cd0d1fa1c3e33d67919565c2f01fa051b22c4d323550be73ce2db8b26c20c7cce9ba3df65f8a87cdf941a2ae8f561d8903e01d2761b75dcbd7ee3f52e6445853990d18d9a77c7ce1d80055dcb3ec080c5e8784f7200a2503e290f35ef54c25596b7ef329d82e12a401f8f53a1cd323ba2b3250bc8223920185b65b48d7e0b800706f5212e6891f8e9f4c2912acfa538c51e53e9f331663353ab3755f0a17f3d9227e0d68382aa0051933a4f7ad2d3138194fa2de30bdab1de7c7db80e0fa4ac4069160e733a54169baa61eac7e34b288c3d1ecb6ee52220493c1ad0a6c14f48a960da0122b66b4c7e0a1b34e2e7b4efe25cd6c6ea9ccb7d757efad4ac1174dd2226930ab511e4390e149d2bd2593cfd69f7e017d12a972217b7d555a19b9c07becc057cdb4649ff6c490a06e59fc95ac7349446aca7f8de2a29bc4fa90cf372c2a31e1b14a06f36dab2d75a2c66bfba8ac8aa80fbb403ad340731e1e4a9b2c05bf6a1099847de84e56b5e3e1f242bb0c2dcb35d344a40765ef8f49b4e4d033bd486c006017ed390c11eb7a321f7a19bbc71439ad8cd1cab24608897174c4918732285ddbbb43bdbc6f445d5b8dcc3f6ad6086750baa75e6810aa51b61982a059e38d1e664b910e3df7308609fd3347e21ff5cf841431d74f26debecccec025574c819399a5b467bedfe4f982095bf5a8842b8cc7fe2b6da3bc60020981052bc598a6b01931199bab6c2974ed5a5801a0c034cb9199b4eeb4a2afa5ca3fe6ce42260331ce199426183cce03f276ae5229de948d115b9e918928a4b21f91390d7127bd68cd58c4b7cc5fb395b5ca9f5875a68110b88bd5cd86b80277987b43b47f9f3f34c4026329c6925f1afb03898e44194b5dd838cc12724e0607b17dca50e0b825591dc0154b90797973486513b9974f2ed156a7fb64b7f19eecd0786ff525434c3725cc56102d67dd22bd2996dcc7fe3bb3e54642f3bf02e4841dbf481453b75f2fcac72ed66fedc527091ac1ec2b21ecad048826f5384a9f6ef1e5ef110363b059f0042eb1d312a526db93782468ad3167e5b8d66e5bdcf75558bb09337c5663c23e302985d13546a412f4e7eafc7ea88192d521c4c49e989d3de94f39f0d2998c1a28a6d627f6232023a1a8b2e6a77b4e77fe4299da8a3e9e7e5912e161ffb171aadc1533e34ba6c77b5f29e1e37b1610cd4df8cdd5bbe41c6072ee0199b3047c4f06c70a2312047ebc6c6daf46521e217ee8a166f8e6a578156d6f285e700000000000000000000000000612094a44dbdfc0d7a2bf73917d6a881931a053b30f4fbd33f912ec497adb02342fa332ed9569fb9e4cef811a35b29550d78ca82fdff5f43edc5467e7624e61c3e53992ae17f604e7db622e9ed908f686889bfcb64f6f033d1b9c697b1b6809400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fc4c2ac088ba5ed3091f14782203e72f77864a5b49b84ad3a4a11a4e5d86af07df944207c2e7f6c5fd18c31e59e6dcb657e3dec5756f29a6d4846d0796fc3e0572e6a22f8874f05329ea9f92f004c0efd4914ee68fb4e3765553d4eba81d7caa126aca8a84126624d089cd31d8b70d7139deb0fcd782bcc569d06639546778870305213819538fa97e3ad501a6acddf2ecb383cd494f218d9e3c16f7026d117e75032ab6b29602c63cd817917a87fac49603b3b7a5cd9aa5e779fbfde96006a841ad0a0eaff69b1b12cd5496d3a0a6f393f34ba8fa58f9ee9e3ee4144ee3113ed71b87169458153d3cf2ab8f4970a0ab5b5d115ecda04f0857ca34930cfee1e44ad9bb030d849f05f36a84356cfd4321e0be9369481be65167f74cba25c5dd7566e5ecc402070ac916563db9d7c3f4a38aa9192982aaec37779ea3efda9d59dd9ac706a2c6022260a91a6e3696bb85b3494209765f37032e9810a7ae0bd114c7d40d7abee3560312e15911e2572b9518b3d002f613f357f0f3fa355041741da59d72082d74dbd0020c6b868a38621f9365693b3eedc460292e5b7068503a449c258fef17ac999d8d1f44a70ff09f96ec8f8d8fd4412f1d0f633b20c773fa4da9696a7e1e71c856f9a85e06bb5f9e87b39fe75a160b21e318ae0268d82628b1ebb827aa5bfd33f0f915c6ad625fe81328964a28412646c75bdf88cae238a1604f5b8972aae1c25a83d58d6bdd53ec67078f3619cc4361d8555c644a85848fac472a5eb63fb3eedb216cbbec34cb137f625fcf9cfb71b26d5e40ae8c26cade09af6a1d0d3f8c0526aac59cb62b659a9e0f70d0572f4fb0430c7e40fb0dbb739d7a4538a8c8d6d112d58b41b3d7b39dd9a28202c2679d4d516d8a133bf6caa57d7c4db0f43885a9fda6c940855fdaf27cbc42563a6f8096fcc726c0b6eae5432f25b986095750acb8e005a68e13151aa67d9ccf1099d29ad3fee2745e315ce512a09f756462e974afe11a9efc62462fdd388ab2ea9c90b02bb03ca652cf9073284c5c97e31b404a653832d82d882b29395762ac1e0e4925869ead00d7dd5ee312ee071e59fdc4c1ab15dd1989f25d8428a3747a5cbfdf07077ac4bb1c499dcc52c4b1b89656e1e4ad07a5026af7d7ad95af820f4dd3c84578ea5016230ac7ac027111ff89824d2542994125caa7cf494ba696bf7ae5df4fed2f41aad648e099c8527a2d6610513c82b0da58056a17acd16da52ee2b638b4c0645bab67aaa9b0e63787455be599164d29e53e64aeee25076985a708fc5d12504d4a8db81017a5e5ca1f8dba711d7d74bafdbaec74ea6a0e26b89fb39116d8f1303d2f1a3dc1837ec5d68d3598913473a4903677423278a62dae77cfe2564b68ac3bff81e0523e181db6c83a51b6930bec49b444a86de27b0d3237ea3d90299cb39a55ed68c1fd6b5d65a7d8da72e12ed9761e9fd780aaeb6a1f6e402811f82bf57c0b360b4771baa484129f7055a2b3a5e05eef80f3a15d8f92d5ad537c4a931c6b32353f113df2a6fd2264a89187d676d60b32b18652d613ef951699faa07be5b0108cbc3b41ab538d4b0a900be10b65386a8c8e0338d3757c95e0aa20d7320ca6f251bc4136316725cd2d0e553b797e648777d11ca8ac8547bd9b98dc6b68189cd2b86bf310dec10471a10f0ca4a3a3f90657c7bc1aebbb1f747546d83baa4f9ce1b24170fbe1caed047baf8935b569eb0617640de32033d45cd18f500f425e30f589d9da06c03c26aac7751a8f63afc1cd9fa4efd9a48bab24252b248ebce7b33b3203f4eca8ae94f032206412488d0525dbb81aec713fd83a9c71ab3b3059d9e47e75c203c14ec2b65c611c5926175fdb5cfceafed16feb98df760666c893bff2beb71442a90964685805d9d0e68bb3861ea7aabf54beabe4f04684ec49e11d15057610dce3890d47f5150b16f837ac4daab7454282adce51e8e557b9e9a5ca8f55c159693a7e52e97db2c298ae3337677baa21fb2633227272a7d4a6b7bb659ec9c0e4f1180701ef02585a1d237aaac82429cd1ee8bbd03521bf2b6118daa65a4d72fce98a4e6d2ae89ab591141d8d6541dd87c7c2557e72069c0dd144f473d2785ace846ea1f0dcc65cc1fb7be0675481fb10d8cd25787adda2513ca8691d2a25f75db67033aa5c9616e49a8f7c6f0c60517f31d3fffd4dad0be9072350b19c4dd1577ca369f9f39e4592b59850403968a46062499960d79647894b85e1e09a8ad9a843ab210549898098b602797b9557aae160bbdefce2f074028b0dca52690000000000000000cd6cc5010000000043adb713a05b301f2c1ed393d1aeb9e4e7b8ad2aa7ca00bd1ef3f056f7cf132ac78a8b6db314fdc64772ec3f3a8782595e4bcc1780614dfdb035b9f496fad9241e5ebac521cfcdce6d7af3f97e03d2b6fc69766111432797c2b555fd5a3b324d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ca8b6ec469ffe1f69b4c7df490dda03b681645c4fc7c3d3c29a0709d575b7c4ed064c5560b9b46a42f003ddeccfdc5c7a507d637c3052943ea9564acc1bf9776d7228cc53d036b272b2bee1257e47507171a4ef71530e474e99bbc7e911c77c8c881264a42c8e4bf8d53633ca94defc78c3df56800156306a778d80a8955d47e021dddc3e7fc9dce1c5cff57e92f8d9472e02d12b54e6e899bec68a47597fb3a57022132aa23a589c6a4fc60cb9c48bcf8134bc6085e06c0691d9d10e60f028014d90b08b9f91177cf686675c607a20ef5e6010313cfe365aeb4e6981821cf2ba594941c662c60edd1bee7a8d151506ec5570a43488cde56cd1d1798ba5bf0aa1469f202144ad5aa9f0d463755cc3ea19749c4f6018023644974cdbc4d96a6edcbffa86f0202128fd6371d58180ffa6de2005bd22e2cc96f4d12f37edab0093244e9af08630213787f36096747f72f56f665ec2bf14c5207bded787a93da0945d2ae12a0e1aa021132cbdb47f561fa0c920b1f2cf6343348efb2721a538f928ebfe2ad44ba481a031e909610d9c8fd704acd9455dbca9a9d54c5c1cbe738727c1d28941ac7c9c3c2395b413a9f1a9ff90b5f1bea2a780f19d79677008923a3168f6863346743cb32b7a80e28b547b16ecf7a524485c1b98618878381e5d7a8f8ebfb3f9ac375915fee4280365b8d1895bc3b4543172b312c4c04c086207ab8d4af55a2e6ec1354c83751eea6e29e7fa5e53c5dfad1347b4d5e2877d35745c7df446beee9e71c260c274066284b93fca95414efcef121bfd3e07758f7cfb39304a3bee32e95f043de0e89476a15ad37bbfe03db557ceb7ddd316ea0fe280990eca896f88e668647a23c8ad072596934f243043409d7f8ff76eb47a808d6dc4d96e47b62eb8a2c0926f412b00335180559a75455fe989f66e8b6f586abbefcf70009938af805c57c0846a150e4e7c70a40c5a474c2d8424ad93760c14c471ce479d1aca73f0bab8fb81faf0504e0e9f86a36fff97d661ba443a8a2191742380e7373389d97eb76d6599e580d697aa205665c699674b6adfe627c2d1493ece151bce065510187311d82e050a857d31b8c62e929b30f9d83017b9e2a406bc6f7a99f78866dc1b3ff0cdf883d18e0f1fa7fbcb0c9a3a0bbdfa3d48730cd61cb5bb91afd0b11bbf60ef579a3db0d03e9594e640f67cdbcf664b714e039b361514d5760a7096d341c216039dbcc8e28e58a497ad83bca3d495c60a8cec99d57a47e88cad9789216be262002af2245d04d53cc2b615064ac5c57d3b13b2979bc8ec39dea4bf9798c3819dfaef397989f11e50f8d76b92c798975c3d7eb16ca7e3235750812e04c579cca246be72ff87d6cb6d588b27280bc005994d03e0700db36514ff699fa588d3be74681996f546714d01a7fc5da1e6df8275f5f5bc929010010a7a05dab95add0ffe755b28f710c6d06ee14023257a6b6599d0a828b3e9b3dc98b634c0cf5ea5398dbafe3bed541514dfa8867db9f7ac88fbb4a2052930d3ca483380a8acb8842464b128503934f82ccc2bed49bdc09356eea52151094d21306c0108986545e35c99f7270cfaf38dba1bce76b79011f305e3cc71e50b7746697908093458cabab7719c6804d31f001468435c4f209233df30f72b4e8ad664128178a6c3fe0c9e3fb5dee618e841c01b02a86916522348085621d3cc5ec904e1ca52f35920c66957a56832cc99654cca222a2f0f40809afa95baf87bb09127ec180bfa72c12b21686b8004d204b52aa0f0871f9ef2c8c80095540adecc83c10b1eed3bfc927f77a65ab0f65e83b01f5252327805b53820d9a74ef8de95ee6dd763bc2643c216903c6678145fd76227dd9dcf6f2eff73790615a7952474b510a0b4ba8c223e0e8d76d3fc8b2c315b7628791b80b900ffe499c7b2883e7ddd2883027ba9504d569ea1f32b8e4aac33d6ccc52280b37e9d84b82e2c62bbc35a8982edc25e2259e389fa822d8053ba5e4e5abfffa92581858dbbc44dc31d7eeb42a3d3681d5ff3ff2b89d7a2888cf9ea0e60b1660290f234afbcc89fb22e1a23aa43700de41dd43eacd253ef1f00366644c991ec54124a3e73d2b2f1db75ec0e3c77fd3a50ce8f5ecec9092567ac78974410e88d5fb54104f98e99cb2a155384d22ed87748340b6f8e87456ad9ea42a163503f7a1df363733bf1d9f149fecd206d2528759f74dd12bba50e01e2df3d1ab9add569b0fd653870d8fa47faa5ca3eb592258f2d8b854d650d6c308b1923981f3e2cba6be5828621d93453175d4b2ba7f761ac75515a01995af05a6b9dcdeeb79caf224e721e5c0a6b914e1c8a3b8d25c4d4d88b0c4b76a81d15cdc1f0d7362ad9c13421bc3b90a9ad2d00fcae8c30bb569e6920ddfdf1841e5bf002c9f250dc2b8bc68fcff046a6d5d26a8f405", "6a", 0, 1898689840, "d3901dcdcca3c29910f29318864c39b2b6a5b09de414341c214ffff2ec285f23"], - ["6271506c011e74ff7a17e249207d4e91daf6c1825e0a7b018d2aa4b968636fd398300dcca70000000005655365006affffffff04bd0fc104000000000900ac65ac63536aab6311004b000000000008516a51536552656a7e4246000000000009ab53ab000063ab636ae2afb2050000000000e94596cf030000000000000000d2fe750400000000a13291c71bb1c0ab1a400d230722b8c0d8099a2c920147729ef75e54c2b82f48c838ed9eb179480e0bb4744548bbaf54a6fd52720adc91d3c5090636bd6fc1f9853de9be170a06e8fd0533300f8a5d0d354da8f6c25122843419d077e5b0e7fb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003276e09df6f95109ac57f6bbf46d9ef323032fa17cd1745c77e7b99c922e85f3288af6d53c6822790f0415f4fd6ca2c6f04c33dfa888993c1d31fb87b190034a677bf8f21beecb4f8608869a3c0900333f8580a92651bd7b9009d953154967166b04a9b3635d1b29f706a3e48126d59b5d57a0cce8ef9c3f127db87deb2e9934021a48d0e68911f9f7b7510a86a1399e8ac99df67aec46915e423fce7cb14b671603247c949a6e39c309dc46253062da43c4fe0981ed7b0b516330c2c3ca505da7c60a26ee971b92759f695a3198f57ca56473d07733f5681e1624d9cac6a61d1a933b0729b1dd1d6828f0b4acbbb7820be39aca6202b357e087776cc64074f40cb89b032f3cf19b007180b262c07065f8b3a5857fa4cffd24714b06b8fb28730d9a4c100302d31993eed17be46a7003968c114b101e1dde2bbd15597939f7b07aabeb985f0220a29a31a40ef91b42742434d1cfded026068ed7a5fd6b69110a7f490f04064c030391d2c655bbd7337399188aeaf1ef602fc4d4d314de437e6496e86b323444f50229e7902f9af808074ed57360a512565891d1dc354e1cc4259c19559f26c559cd6edb33f35146392412d25881fcf97e8d27ae704ac872aacd4db166e25bd913f1605d14492c0d80efe8e671fb14f126efcc232fd32699b6e059488201da1038bf2bcd07109260cfd11d2b78ecc3ade82e96c13594c11932d417ed7e0cc30af7970edd2e2d87a8e812d9cc3de536766ab1e2c218801ac4127ce51ef75801c9b221c6b225fb340c7d6401c5cd771bdc6d1cd58665e6235a6ee374ebb7202f60d9f1390dcec0fb841f080794591aa056a7ad9da7c2256e7699cbcdce7807e6bd8e42deab71847032a58bd056f1bcf32cc1de805282c4a9e648d12b10b531cb94b541c33449d054f5aa9502bd2853fb2f80f61cc8ad4c9ffb981021b5136c233513e40ae239d58fd96050ff9bd3cb8db80b2a12c0bc3f0d139659a63feb07487c4994f903ce6d7783cd65b2822c647f45c8945ab7086686448b4621d93ca9a2284e2f58e8aa0509bc8705c184c402fedc4be1841952dc6f717e3212de995a6d98daaefe42b3f2d0064de2d0ce53d63f80af2d5180e6b0987026eb3ad34039ddab5307d54aecc394988f1aab4cac29faf102e8085adc399ecb6af314b1e43fe2cd405342df8ae5b21553b6b419f2cdfb82d748c96fc4e347c8d3f0070f029a65b7d771e14f0c83ddfdf15606e405bb5f6addee1645ec27a56b901447006e020324ab7d6e1504af8976b4f36c9bb12c799aa3952a9809de82186115a0f1f3651db275c066ba822035b1ee3b55fcadcbd76468603a1575577d0a308ab481c4941a9d507f2d7c9015867d4d4db101e49ddc3ff5b5e502e631c30fdff983227c5db671c69c086ab63947853356d901cad6fc7c90b1a3b22988cedc869f2787af819d467e134b71c920479368fc15df430109c1eab475bfc3837701bb0eecfc5e449b130819e578f87524ec667fab2b8f84808281d1065a0233968e7e0a9fd1b4552904ab4f8aadc1455cfee17f78a3fc16ab4b9b94af9ea71d0d084ab6e2c021d5b71f49c47542ff9ea19d2ac11cd71d5609721442de6eb1ef976784c87b3ac1f9e97b3566dd85be83c859b5aeaeb2a04f9daa6f488ace09a3e5dc0dfdf81b796354e01131962e478268520c542f28f6c2854124de98502aaeabe97536e064cd668555d9fb9abb5c0a6018221e4e7792620c219bb1fac4557b7b4d568c38a2b265763f55f8d073ae47337d5270f475125eefeb24f752656c0d66d6274c044c6131861905a4e6de5b716bd6072f2b4ed6253ca604367ea39f809c15c1ec7d78799e954c57f4199b904cd7d46059e3db9aab86672f092f823f23e65038a680e013119fa1fc7b719a5a3a9742a3c61541919acad7210319627665d91e000d699b1319de42aba733468a77bb3e29bb3aa3c2199d42dbaf228650645dd23352545ebae28180c199357a34d049f468ef47d52108b2f5b53e51de8d104170d33d7333bdc024a945f2a40a0cc1b4c1b09c8db948741c750a699d0686694ca4515b3bc80a869f55ed3f15148278545a9c842e046c3ce0d23765338e9aa874ca9c954425cde2fc988ea17a950df395b44263b74e94bc776ce83a45fb5f092d79bd9dc8a612d1419b3487fb9e54c39dc7f8d6494d36fcca8ad177699c043fe6c2b292f2d85bcfea5ec59074996a0a12308ddbdf1a81d806b7b115b7f9b8a8b9a11e22027f6d9bdf13366b38a5c0b29004c4ba7d595ddeaa821daee05f0000000000000000b0dc510500000000b190f0cfccf7e6e49d2e3c93e181d17fb20b752df74af6090a29141458fa976d9691d917bae2f2c2f1018ee76b777d89945e687e64aa7a398037558a0901ea5b628531b9effbc34775b749035dd58a17758ade93ae2c13791d65ceab2604c53f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000031c8ce001c3faa3be73ef97aef44cb876ea5bad8104e5238271c5229b8fa33d658fdc43fb9a27b65fabcd316b0776338cbe5c177b246d7694947ea32f2b7ef5e76453b6e35d710c2956d00c03116b492c8deed9a89d0632cf16b145922e9551dfb269bb842cf4cdaffa01d39b8c176230b04100124171188ce191ad47951df42020819015e21e6bf1b4d32219cca8ae8c465a6309094201ede9b9a047846fa45c8030ae4bd8d03661b803cf87b3691cc3013506931755878652d0149aa6b5fcf04220b26aafaf52bdf88c5769dd320f52c27a3f5b61cdfb5939ec5e46d4268c7a526d70db76998a515e38c37f8aeb12f497c09d26b242285173f1afe5980c2dc2c09850300b5910d5824664a3a717019db7a185385410f0b19fb62600aa65d1fae7ea49b032737950916288acf4d8972107753d2c1d34a50b4967b3bd5b965a0c951c0846a02165ae3a030cec5d1efc479989f437ad46c6ed201522107eecdbeea84b77514020321692f9fa83090bf28a23fba43fc1f92fa1464cdc69a835842189ed532cd302a031724ce12409b3eaad73fc85e2f728d58a5d05c66b8179c7f85b2beb06c0a3b4d4d95dbc2ce03cc9761407feef28fad087301bc4cc3d2542f57468861f141c88e3dfe34c2ae39710f2c92bd6d0f4f46b3481a23b2202f5c1fa0519b9b297362079481ab42eb4a60e9dbd79e31efb905c015f3098256837d55a71c6ff3bd10b7b11a8ada0c24584dbdcdc5c9bd9bd9df25b5a03da5bbf500b53e54563b0f3e9c9064baa1ade2ea688ec2980790442771d1518c289df73d89aa6fffbe064fd9f8fe03bb76ca2f74a195f9cd590c9080bd8aa0fedc0266f54afe04ac746fd78f9a051d05ed96d7637cc7fe8d306e8cf41a8220a117d2120f985f0b8e6b99ba2fe7b7cae7c54ad16e2954c071b0ae922528b15e78e0a0a790b7304d5bc29ecb947a7bfa774c017da3e6757c14094251723ada8008ff607b67663842f704b33e58108ed7514c9521641f51646db809fdfdbb3ea9c55add903948cbb967a6607e1c0b8e6ab5b2b23522ea94defce4274b4423bc0300fc039e44cff871aba968a84d3bdf2f3b58e0ccc83b8cd655d8b3994af67abdf7a7016e432d1b3cf6692db7bec5cd56396caf195c5482450ef50d343883b27d023f8b0c1934c1bf0152465ac3592438510f5eea79a9e3129007c536f8b39d7dc989f721e225c8518bdf619bfa8b3d435d42d3b992280ccd527830068451874cc869f95052b8112a9413ea49a632451967c7d5239156d9d026dd7ae6126defb39c7e67979388c25141dcac57a6818b4642274c49cb432baac36547a9f5db9d0c4916465ac71c1fbe8333d0dae7116801744974804952d0a73513b92261c1e58a848a58b00a6e1f70a7d36f1d714c4739712edc91b7e840096585a01771e3a4c551181a16032c3cfa15fbb0e4de07f96987855ba910d14af8ef4a63f34ea72efd90628d3ba6d0d173924de735640cd18db5a0f1b304c6176efb708c36fb13ae6638d988bb9d631c478cb2f99c604ea4ef62b6734f8c3bf304cca9d057252ac7023d5abada1f40b695a92b38a604b47d494ca39adb8b623ef250e1506a1a92a9d96fa135369c9c4350dd843c2031287787ea385ae07e5d5df22147471fc87fa9cb2f0a0aba2ab52fb04556091b3a9dc147dd0213619d15774f2bdfe12b610d423a709b835225ba0c944e864c9cac123053eab9a6a8aefa7c0f22b733b257a748e4f945a28affd0d21d15ab29eeccb1cd4e38d997b18782f6cb35ddbe99280b52485d1d227df4345d5876bbd024524077242fd975ce61ae3f7a7f557c57775d96cb2a60a271a2cebd1577674f496a0f0fa31b4b416bedb38476ccf836c9de883745ba2adcd697a88f2ba966ab8351e42cdc056bfb7d62320f240af68b8a248abd2b4ce8ffda5b09f20446f2a36b946ef07b6be937a5b33d714f05b3702440883ce98e104f4bd236333f5d2ffff7d74a4c56a4faea7471382de9b99d66ca3075fc85fa247a2c333c8e8da2289acc119b2e2942b752be60920c8e9f4ddd6edd46a150bf20ee885ef899177f74811966c2bfceca2c04f28e3b7c74d854b1eb0f8784b7d41910499cb34c7e724c8162fc956ed446d2c3c41a93e577b9148eed42b91da353ccd98c49ac67709373eef9c692161019b8b18e10b04eae8b9e5928511551da3cb4a3c08b6e2a3377e1a371af8bf1619055bbabb3e4c7a1e245a5ba09c3187194da28e7ced19296518e179399430551692126e414ef3914dd306f29acb94345ff00000000000000009d639b0200000000c5692db1f406c09ba4a5e2b6e8c98dcf6610baa549649045dae3d6cdef0376fa0c775e74913ace2fee5d3102ca48b75d10d833035be27f955de5c7472af9bda7a0d27cbfd014946408a69da609415cb917c4122bfc2b01a00caad7eff4e4a8ca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009854f1e1c7fae5bd041c362acc9d0e3eb0d7d709caa6e4c8b9be509d024dc48ed0c7613cea2cd166afa12dddb6593638577908dd74afc2e2de61eba2cce63b2c6144d8029ffd33aba8bdabfe51882d24978a456aa7ab82eb886d93adbab505fc91348cc4fd5cd1f19f246d25f2df21fd80aa01c05b09b9589261559be16697c90312f30a2d5c3392930f02366489e36c7598a9a6459c36eb2da9c433d30d14df35032aad753c29a150112f25725866d9390571e24ad2b882bc44a60f1b23505c7d890a2d316c1ae6092df636c384173ed4b16a173b32ca9ae43506bb16a452987aaa3100367225df1464c70739978208295c9dd567e97a37277978cc89b6bed234defa0218ff5cc8d0b84f891e89e87c2a095a436efa11a7fb67045cbe6d36cf8c03d298030c16cb367767d81e663f1e1be5fbf32a7114ce92e717aa2c6eb3791452da7234022d7b9b5eb253fb4803685448f7563918b22cf98663999d54089060ee247e1675030a14ad95a540519d2b79604c7bec35357d096ebcaabee2b495d0b3ecf353464303090f96bfdb5f90a7ae4dee215b6fa967a4a31548d228b7d8c8439556832ee5c029ad05b4956027f80a8729466222c1bd1bc3fcae63c0acb03869c61b8ff792e76c0e7dcd6db9458f1d33756efbde774836aef8e5128c7239cacae2d2b804446fdafc639f03ac75da6098dc23e101d6f705cba4e29c8a30531595f91aa7c0dcc629ada76359c0a085fba5e0866045855ec0fc69450cfa403bbadac7f3ac7d0bb6bb26278576c59ecba0d6ccd338d011aeba5db5ee7b7d453853fc871cd4512ff5d76ab0285b2b3bab5c2167a7f11fc0ce883787b7be6359e6c488794c2f0489bddeb87586a17e77f28471e4a694152aa0d4576dcb3f98f4600a4e6b640fae15687170327afa98c0399ad1b7d53e8536cfd1647dbca5adb940aa4ff81084df580106220b1b37b7a8b9c42ccec0676f0070c148b686cc838c679ca724586b4e448871288469e9c90fa2cdea509db5552574421dd64de39e164a9111f71bf8adf7984c94f35dfa931327b681a2f78a91e6ac78d4b39aa21e589603b2eb39f7bb1446e20042420eb1c5ada46f1d745259c5f2d03738e09cf1e2ecda4d0a33601b19cafdec51123d585fb69c15cba7e992f2f5f588b58b47de5f7e377129c6ea9e309c09d8e967a58824794ece5fd55109a200fef86e93a2ece84738a003df2d41c9cd41b93d5b78ae2aa551bc15f209678f3de22efb856a14ee123e3fc4eb2c9eadd39b966b1770cd205f9d6ae9bbf97f92eb2397ebc4da5dfa58a830b3874d46fcbb1db1fca2be33c909059bc412a84afff0b23e8493ab7fe9996ee2cedf4a04e27d797693a5fd79d8d085704960f2711a383a3e6290cf80fd7e201f17ed86c003e58c46bc0079d858de92acb3c19a20b1fc9f5f7579347f7180755a40e6c4b4f909fa1a746de222f9021794b9cf5416b2dc45e13b85a6d303c7ec8a80a43c5df1cd25be45409958c45982baba5bbfde0feeed94c50e4e7a4243e7023b7f2e4f23442fe5ff644dc94ad69119559117dad875f11de55c56324593178d553ab90b79bdc867c85ac2cda1dd29fb0645ac29addf69a29e7331157f253ebe2219e4d072914ad82702474f8e1fc2be00762b5106b989cb82414613e265501510ae083ca42b4a33c4f1fb1750452504f2ac2f2371c0e5e3ce676b6fe4debf5f2b6966965083145728c44eacde47185f7313ce185bc6745d778b9538a3b1b1ac852cd0ddabd35cf26721acd0011b3b7323d28794367f7d20fcf69b1c3787c4ddde563d32d037bb1c3efac7a27cf664203a441dbf26c824404127db4b4360aa0f5d83a191535bf60d8ca235c92169f8457fc6588ae7bcea61bb3974463474cb2d8bacea2458080a0fe1bf5e5deda46bfdbc53b42867816cc8d3f4e43e7ec017d3c169266ba8a85f8d235c107a9983ce65fd994f251691e6052f89640688c923aa95fee44e90cb6b9b26417dc5432f58830bd8810d2b65c77a69347d7ab51d24aadd081da0104293e1a43eae9d47ba1afb293e4da2388f395762a1d5c5a84fd7aacfdd80e386401ff09d9d99406b9706b30b8b52f322ea425a393c934b799dc7c9dcaef277599ce1426c00698e3c10c9c40f9656f478bcf272e6a3fab767a4d260bfcae6fb9d4da6e9f6f1a72206fff72dd2cfa0607e0dcb45fccef2c6f81bc18e838042cb1632f9b17b4f3b7b4474b15ccef364479c5ee2ed20652c262bf9a65cc1e7da994ae27fe6306222224e98045208bfd60652e11f607024198a4875fe6a558609b0118f261ac49efa69482d5efffc25f8a9dd4e03209d87ff93388a2a08c489f7d9a522c9879a272e3e175fd903c53359af3c9c56b3726c3a540b53974e9a5fa3e577413adc3c80da03477cd3ef4c6418876015a20e", "5363", 0, 82629267, "e5c96a29c8741adf85b3580713fbe4499a14049ccc07512fef94343ae37599e9"], - ["bf8a3b0804fa894753aca690320370387b6744f47b07957cb3618ab0fe293237c0e027d454030000000263527ae8824bab8fe5ff45a2b744c4920af7b4aa6ecfedcea45b9d3c727a20f57e3834e693860100000001655a47359092aa7b0a17edd287f314e73f87b136cfdcaa525df24176b879c4464aa9f72e640200000006525165520051ffffffff1339bf2d0e822564db4e6cd7a666588d03d844d89b1e6ed1563adf031d9ff707010000000963ab52ac6aabab52ac41a571ac011d777303000000000163917deb820199d3a504000000000000000000000000333442fbad01ca6a4998ee2b643010b8b60fd5a759cffb001c33168216f1eee6585b47e2b2eca4ea8c101d795467e8424599711c98e6f3a36aae85c580d6a74a8cb6347ab09f46e85ebfb45096efaa0cb20d80d78901a795b4bb3b6131d4024f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cffab9caa58f02b71ab422108027f57bf2872e902671438345c42fa75a357ea4ba2b1f09b3a254a94fd282d53dff281bb1b7b981c63fb0c80faa1c932183f2e7077edd64e8554d5f83862f97141294a8b6924d160119ed3dafb1a48d5b2269de94c42499eda9757add067e192bc93f3f760bf39c41eb54b7d393ba432baa4fdb0203d3e65db776171ff5338b973299b9df98ddf1743b4c942884075935a2f5ce9a021ce11bdbb6f7fc8f1b9a884efbc18e8cd9521576f4f272462433066919c0b5330b1167f732a871fcf8edc35006e6953bfbe2b044016766af1aa332a22315952429048697dad471c41319e42bd542b69f7948e63082f8ec4bceee5c4a770227dfcc0302454f927ac341a79823b46494b113afb2e53f1d59d0077f49e03e5571a7cf580310f667eed011852114cbf9d829b5b1f40d71ed9f9b4927c90977142df2f1481e032ccb3af40da681114c42e9995c33975b06383b2f586088d24ebf085453716387022ec5ef05bf07a5e190b0c3fa530ec5e30bdb96bdd82ad55acd25e6d80f7e6f64030cc1496cfb3d8051e70ebee344ff571e23a84685aa51edb28a36e5c4e58efa293b7e7de09cc264a952abbc669031b6da68646ad55ead97dc0ace0d0ef9c2857bc1f1c09bef4daaab5195a2503edc4c9a1ee5d3a413dcf8d5c00e0ebc1a5374bfe3e1981b6888db803ba0996e4775727c502e649e3a466fb6c71d9799472bac0fc49a4064eb6f9c009fb121f6a937fb5e2a653743ae20a9ea78e2a7fabc92c467523414d2ec8ded07b8c72b732f541113763d6a6b25f66c023e3f03c486aae8904aab3777d1d879920edae186206c82cbe44cd3103acf3186ad029af902bdcfc0b767c4a080ddb4b477d1fda7084b72ce45e37ec53159747a14d66eb44e8a15c2610ab3a32f6b28714b25d3b7fbcc516145ae29fe6b6735ad85477eef5b3228b5571eef93e887f9ecabe65c4324ae32ff1e9d432c683389533e00383ab70bfc019e033c74f64779c4ca46d548b2b46225300af3651c51faa495999f303e4060e66d21d51df7693ed08f5be1497409cc91fde9866d9e107008f6d983293f7ce96538eb808cfb76f990ceaca99476f2f31f27e1096ad37040ad3b83d15aa1bd50c0c026593ca09b5f041870d5eda29d21fb838704a6a32be1b6e0f97237038472207c3882c4ba96792189205cbebe5182d42a84a7ee153ba0066ba4e6b695190748017032ff91f7fd4570c8ffdae02c6d36ee0a9b522a935bcb5a603986f82ad72d0eb511eb698f85a708e4b6e23c163e93df74b2e18646420ac54cef1a05c237c7a53d362c3df11fd251ac40829280fa428d35bddd211ac5e0a9d9cf7a07bfe672e6600cd49bae0f61e1b3bc13b776d7ba8ff69d46ca1ca463caec80f5b4fcb88570172a0feae3b84da0c8d3a71db129551e16f736a2223b09fc3e5b02c857e97e5579f38cdc7104132d224059f9572145b5590bb8b0082c05076f983097829a5ad903adb2ee486b1a1cd2dcc25b4b5ddc7a635f93a2d3e6a358e7f1bd02681326eebf4141322f2bdab3ded4fecbd876a39267d1e3b638b1142696c31b0e26b3b327e49791dfe70e870e32da84b9c1d99cd5a42252a0e2ed3d92c09be596f8905e71878c94c5b4504e75df732f475356e335874aed20512508c08bd22ee1c3c50dceb0799f236780ecca9686475738b45d54c0951e40397e93c55a3f05e4d73e99d4a1c5669661bb662ca648b1ca778355e0b48df65e3203b6b88f0ec4ea47f51afd2846e23bf77b0cae4ce30f7f5f110dbda3ec289c851efa2c4d3f488778ac4a9b38cb6bb4d8a5080765aea1477a0af160ea770635ff3784c7dfef6f182370474d16d8ea4ba436a60d9b3cfd79f7d00b3ac7cc056a4a8bb364f0a310a1aa2285101ca9db5c0f1dbe758ef870f0c2953e8b1b664165a5bda567499399eb289ac688397726a318cb4c46e16b6135bcaf2cd64e160841288969fac690bdf3f26ca2352581e1468ccc98f31c8afda1e47cbe3244bc239f85b6c70661bdc6035ae31bc978f1fe2575fa19c5a1d6945b86bedbc5759a4f0c7c7347ffe76a4a9bb23c110bf82b39f71e0a161da1e247b102416df614ef4919a6f5eea6e2d865d1166dce80516940011c01ef76d7037a36ef03aa71904649054d2f577aab2913f2a3d97ca3c558a4864f27f5759af0b71eb66d6d5db6b3efbe8f1655c70fdfbd784f15b29ba850f6ca30bb961e4dbf47288a784d550d8a6d63649b409caa75d1da2a765ad7e6f01b1ff3b9464f4b88389358718f5dc09a74d39a95388f500e9c42e955b5e023d121fa9e5839b2ef78aaaa9dcdfdfdb332730bfd92983313a7be753a7c0af40c4fac78c82b7b36bdb73fc5045c768582f6e2143f9aab97e809cf5177884a64e79e8e579d921a93a84cfd752382acef07", "525353005165535365", 3, -604189762, "396e8d6b6673b8135c8ac29ff2dd9cee6827defbb16cb6cec54dcb0ec32a0af6"], - ["2ef30d77049f64dfdd155f1d5ff83ecec61fff15118ce1b075ecd26780c78a490b4ee22d470000000006515152ab6353ffffffff1c50142f3b7c13dd3344ab02b7bf0328dbf891df31e53340b725496950e0f42b0300000006536a006aabacffffffffe9ec38313be666250e9628a038282c74a03b5f04b478ed28c74d2c614309e5db0000000005536a515365ffffffff168e58b35ff97e828c478d3be2ab8b16e58a3a9b9f1ec5bbe7f1b0769415f0390200000006655200abab52ffffffff015658f0050000000009ac526365636a65ab530000000000", "655263526353", 2, 1787584635, "16712d42b278b33e1115b51b44b02f0c80cae74fea95b35a7f4a4ae8a7114d29"], - ["2948947004821f488a000d39c646da1c42cbbd76544b69a0f9e7c44ecbfcd43f46864f2ae70000000008ab516500ac51ab53a6b78ba7bf8c7e2a80c2f633bfcd15b3d1cad988ab1cdd103d2c009c6b08fbcbd12ffda801000000003def159327e776adfb7fd6742a98e65bbadf8a66cb1082db611bffa173b9c5af3777a206010000000153ffffffffd64cea1b805fb34081405f1a251e558452c4964213602d8c81ef0cfa60abda7301000000015267a3571f0394db530300000000096563ab65630052ac534dafa205000000000865ac52635365636584fb74050000000001acc3169f460100000000000000009ce1e7020000000002570c9d70b63953e8bd4b581c5ce59c6313f06e0db0be5ffdf408f292e4f92cebfe7c81c0ec26d956d1e69e819f8ec74cc18bbf1e3e0a3b2c611e5c3feb40fc73e4ea9d76b929262f268d7b9df0d9f06b32b7c539c24221dfa4df9d433c11e500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000449159acb66cff3c21fc9b8babfe56024b822478f9acb6c2b7f68c42695a390b9908406572a5115e05319b34c9db51087e53b26e3a63abc0280c24259bad9da2543480cfda49dd3a1b0d2a357710991bc0cac8e901e7c735be08aa7480a561b1806acf6ee9d5a76e0a08c4f3f5307bc681bff606fc38fbe6167fd16570efdf8602166dd4aec39e89a521489f6b4f3cf7934d7cd03e299b5b71b8e60c7e31f0da7c0315d7bbb7e128fc05e8355665a6d1354a735f90e76c8528b6c97cf294e8d0a6520a277cdae56eea7919907e6d835d6bf605aa25c5041a923913cf8433626f961d260aeaa0611f56c9c7f2f0ee2abc331a63c9653e4065d77356e49f11d42495683a031cbd1d7075167f4d6836c905247c88a59b5e4b7010112159c67ead399c2dab8803250401f6b3f58489885ebeb28c6c8c27e104bc1f3672a40ae08ae4b32a13a55803133e1ce170e923232e47274fe0d4ab2c7728fd43d8cce04f71292a17831763ff03137989a654d67392bfb5329930acffcb81f96311b2c3466bb50b407569daf7a20323a0d3241ed03b0cfa97d611ddccc049c78e89ab8976a51d5c89d5762a0e6001766cba493cc05b6d50afc41b87379134ea7f9a857fb434171a895ffc58150681c21dd30c12ba7b1914a1657011d475c2a905c0ce09c8d9c863310ce041e00d68ce006a7e61a557e04a34bade65bba55d0272370c2fcd2b9aac3dca58a9fc734a92d81104d5f7b242dd24664510fcd6a12b58faf6b11761b6f9022403116c68892d621c65a4b6b5be5413bbba3b4b2ac62c3a205d044051f4f56dd083cedc06c3273b2afd42913085b1000b243277c53358d9d3fb3a94ef35751b3bda28577cfb624cca38708a29d1c4bed0805524b650e624ef49fe2b202ca20dd496824a8ba7ada0e16b1db735cd4cd48b0bbb4725cec0fda50dd033084b108d66d8ce28259c3b14564155f6d2c2db0e182db1e6a0e27d5aaafb2dd0b28b7bef2778d37109b06cc609e33ecb31601c952d4d79a27c0297913c78e4b5eac02ded3dd8bb7e99983ee069d319d1a0f58e26475c8b88b4f31c531d8d36e70b294302e322889c23ccb08426ee4a1fbc0fc9ba389ef370f8af284a214578d87adcf2de351633cf1f0d7bcc87a7baacb002bc672d9be2d970d2d018a5b42a3124795d753cc76204f06f8b249c8e167ed0de2888196243175f55b1aa4cedb2f8c2b0dda188c9650d8c616b299897fa882b6269143159c7871721b516384d95bb1d3d7cafb4acc0fb3b2509f419fffe89b2e63a81fac41a2a2d09cb43497718e2eddbcd0013a4d35c776ddbbabfdb69acb7c26342ffc3d8d1b5ca6edeab70ce11e1fc196e9c3103a87f26a3bf10ef517bc6bdd9b5ece920b7b64ddecc4be792a4182b79dc671dcf2af8c16c92416101d22a73f253a18a4667b2d02fa207db1c6d5a7472c56cecfda669d06725c6bd763279704ce92833f1c0129bee695d6db09efad962b7bba711a8db8c1db2137d90feccc6a2e1737d1e3346ff62f94de9e4e112d45743a26e171ff00ef92d7805e44138b1fd698adbaf2480573845a7110df69f16760bd870be8ef66e91b3b613367318aa28fa5effceb08180a9eefabe52b9b359df472ccaa007370c3ace9ed816a5b4ea2451ab4b1075a290aeb01f7ab753c21bdeaced13cfab7c8d557f2d0dc2af92db62e3cfb8ef2980b26c4a9c087dc6124e73097965cda4bb619ead2b3cb329175ab3cbb8d2c30a0339928f216f91bbd45b3be6444ddf1840aefad1567d3d9081bc986b4d0497e47ca05cb25b7d76e9b40ed913f87ac977cf813826bbe816dcfc7d9f31fc36833882dc1076698a8414e3e501e93201d6b151b7007e7f2b91989a64b396d7f5e871cbcb6543084c68b60a5fcae59a4d57a8b4b0a9eff01b5577a56756ad2b4297d62265a5e438477e59a4f4b72c9f02a20823dd5f87eea905d11ef4eb8b45840a388c04c49c012b69a2cacf23d7e7c985a251103e8cd8de4f4639fc9bacf92809e9535a256deda0d47b803360ec0454e498c7458afc3d63e52510b687f4169c0fe893dc40485255b3bc02b60e5b6fa0504839b84ab677bee57c43fe65b0987a9673fd66d3734365e8fbfe5a8d8bc7357511c47b3259d06adacc817fa24a2f2bca8d8d7ab646777b11a7b4b5f1d787558a5f61af0d0b3ea82a088a1ca3fc59440043cdc5d32defe8ec3735193b04d03d8bf8f1ac0a2dfb18a2b4bc2df9adabfcfcaf7591367b6464f04c417c0d0a2833c9978bd49ad047a9772da068f4d65e9916409504506188baae2259d22f8bc9ca5b4716a7314aa02c1fc82e7f53704c3f8357546c5fb088ef055a82eb0eb22ca12caa62151970ffc6fb1e6dca0dd589d6c8801ee8c9cdfb07fefb0e47e63edd85d597be2b08848ccb6c0ba6c83890cf11733820eb990d", "52ac", 2, 356458880, "581a4440aa66df8d50b7b1b9d184acc94271db485fb9b4d8f478c9d7ae8513c2"], - ["ad27122304affacfd168901af107231f7cc7c5d08517a6f732512aa49178dc6b1021dffe4a0100000007530000525263abffffffffa67c63d62fb55e2c64399e7d0bd7db6ba3d571b1e30cc6e97e2340701e57e0d9030000000700ac00ac535352a8f456b9c4c4e7a54e3afdffdc54c5e0a0b92fc8b0256b5efccfaad2264df35add70862d0000000001abffffffffa731a85912c7319c654f5508f08cbc3fc0cc6a3cb1f1882f84f4c700f68fe9c103000000086aab65515351ac006b89ae3a04466a74040000000009abab006351655151656317df000000000005ac52005151ceeccc000000000003516aab15a539050000000002ab63b115cfe1010000000000000000420be00400000000034d6101e9b89785531c01906e9ff0e910836abaad764de1393d3a36d481902ffe9b5b8031967693791a532baf56b7451a925beb4e04481291589a84421ef5c5b47f347d25d351063280fd0438b7bd3fd788008090484791cf91a2ffb64c8d80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005aae5871f61295c68560c384d84d20ebb6216b36cc0b2054d64250da548542bd0c6b6e8aad8aedcc606700e0c6cd944685d4a601c0a18e3fc018ec8f68c03f70ce317fb90c12755fba476fb4ff7d8a38c80c1d5b208d621342774dae9475b63b9ffe10329ec59cfd77da4ffe57088e1f3424a3c2ac36a836b7344fd01d88fb9f0222bed57c79bdc04afcdca64c47d3604a5f8494b6b16082f8814ee91b97f3abb50213f731821b30f9fcdb6cb8388e423e8b9d1e1c8ba0fa53a07a1110f1fdaa65b00b207fc8a3fa7e84d1b46a7a7d3adecb1442172531d374401c3fe94271266f03e202d80e9b89bcbd6c906de20430f32927db88eb2a421bac4199b37a916c62a53602074a666fdaa5d33b82f8d4ddb2cb7ea1bfaf92853361fe3957ce437687c7c39402180ee87ced11e422e5aaf4c8c2879f887fc6c66d16b6b4f23cea92bc7d164928031aa2b2f7f5293076dbea1b1ef8eda9d37e3e40c29b48322bc6447badb473532103177b6a160aad8580784710b60dc7b9f9dae73bdd9e0f921da9f42e619c7618ac022cd77ceb4879f9c61b9ca9fd32af6c03eba6ee73b00f0d68c93be90ced455dbdbda1262d10d620cd5307d1e5debe21bdf9ff3dae0f3879057877c858b312ad2a38fa9ae855ba04268428305e9f724b6c4f94f5c04716d05a310ead935d13d1a61afc2b41e818544eb1d105c7f854157e151c7a1666bd7f00de453933aa57dcb0c45255ab048085f2f31abe4697a1f0a9377513bb590779555df94d7d0c23deb468b7d1aec6ae51d8c3e63d10fc63f62236ceedc6f16165222ff0d6e343d92771bf73434e97d85b23857f9f1d01697a0c822505d02ab04b750f3aeafeba483eecdf7e2de8506baccab5c48b387533dea608732b52e9501c34258fc88b2f2d648feecda678a95b30b9f80908a2bbd9ff90131e27d50a114a12ff7d460700efb44543cd9ced178c7cbbed232571508e704251c1384b06f62fd9191e3c40f980780f2de247c426d7ce7ea4557029825c8f43bcc64fc73dbf2602dc5236cf2f40c8b8e645a83c72d2ae4078636f0cf8e2836af0964117ca40752195174a0334573690becb0b7349828f64037e8a7953b6e5e285bf2874cd7918e5682f4f48b13b3ddf0ee478cf555db5cc6426c4e365e6cc11710ec93b18b4ef8b17099735a87bd9c200a2efb2565941d574aa43bb517bbd8d0e51c09a406efa9e1d0608e7620cc3fb7ac32b098936ccbb9cbbb9a9f19d005204c51e6979e67a188c8802ddb1a4258aff84b3968092ee2a4eeea29edbf1b6c67f72834fa1e295a0655c7462b65241ef5fe925a3e285a794d55259324c17352c116432aab28bcbb3702ca9fcd758cb465129baa659d2b1241b5460f01f8c427bd8ea7b27aa8a1977ea18e9de0049913046cfb8b5a4f87e98beb4b3c4f65efe1ce198f1f40638049f9f29cddde518e2ef2909ebb36c14be127272adc3b4a36e2515a02d85989c2bbc9ff97c581c80ae00fdb0ea8d5082edea7e77f3c9f7e2b8304df130ee7cd502c77ec8712660bfae430e10c7d73e998c10cd8effa3b0bc3141179ef60661e21004f979ba64ea1ef06f71abfb67774bd836bdcd2623866245c73f0d78bf4df88477c1b74bb34bdce98f9c50efef5e5081625c175802ea7ff91c1b6a2f45960404cc0997a0748d92d9494bcd6fc72e744a98256c53679140a7612e95bdc6e2c1cac19d188ec27a3b55c33675b5468ff8240cf6d8ede002987c1c5d5076c85c295b251c214d001be1beb3a0e7dd3d952e95c3aad40d39c6578e19574b62ee656a9100a6a49eb483d6f77fda183eb7e4786403fdd7b3a245c3c4e6d9ef0863bf66c355a216cb9c74946508aa784e205e03c1523e4a0d27aa899755d314f9f683a2f731fa672bcb572cf1c7b4567b7ee5e7eb837fb329a253f30c729e8e897bdad874f86a93b548868f5407b74a33674fa5d69335995e54362fa1b7144d946346c9b05c20c3f65a6cd3bd64fc7681df698c62e57962782d94fbffd2cc4a3e65150ac368210b7d2e1447adfa47fc95e078e687c7af2fbcd1343002adc2f4773a9612be5c8d9cb3ebcf2dd18dccfe96d090f1470ca47521f6548a111aa8255b53590ceac0aece91dbe5337bd26a802f8405f08fa97f0441fe0a0dda8b08492d4006f287624508b005b168d8c06688eceb63b9239916d73c238c12a65ed42dfb4932b5b8c775dc7ee6da13194aa7de1245f553ddfd57553ea4d524460f4865c90a3f206ac0f2ab9624bb396844cdc6e8c4fd5342e039bca5e8e3d08d12f85e1fc99905818c9399c56edfee3a53cdba456ba8c3aab90f424c27c04e1d9f4d32dada28a33d30b7f7714af1422ef3583a40bb96b3f6ae169143692d3f98d0157069ee2d3215948ebe7fb37537ac91146633984dce19e645bf2cffafc9b3b17c06", "51655353", 0, -1366103095, "52e61d542410198b92525b2d4350a354ea74bdf9c40ca81a0e6a2e2a1d60afdd"], - ["f730670b02b819203e4fcbd569e1efedd9b5fd983fdd0224a7db7ea049d9af9b59e63f94760300000003ab6a00ffffffff16276ebd682469a8bfb0093fec1e87a5c0df6591c4354b2e33d57697b9f12f0901000000015259e8812c02d609c504000000000153bc77d0020000000001520000000000", "51ac52", 0, -447471684, "33c3d2a3499b8f570c0c539cd47995c05203618a007918fb32fd194c444b2f41"], - ["dc791b14012d4167265bc134c400dafbcb9eaf6d05a25271a655db96b2626ca97928131f960300000009526563516a005153ab487e4fc604a13860000000000001636628e00200000000080051ac6a5263636a841caa02000000000014975f00000000000652ac65656aab51656aad023f796305000000000000000000000000c9bda62f29cad177ef7f20f3c08986605d0d9c6f67cc143ff9a2118c8033f66d3123b1c6aef8849bb4e967fb9304a33802f25bbacf57d1e025acfe6d88ce663eeecef1d2a6477dcbaa2aa77c3972424eb306dbdba7ce3990e85fa69bd56baa9d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008fff75bf9b1418bdd840b3ddeb7519b7642963c3a32b3700e1798dcebcdef7cd127fd1485b43c00ba9348e42fc33e27126bd34598c1e1109e906fc8c59ebf496a18ecc635c1930944a6f20bd58cc06b4776c6424ab3300d6935cc06dfe6b29ecff1a998b4854948e0a6e8e7313faa7c4516cf01b8232c3a7b19f9bd5c98ae09d032599cf6a74f95885cd36867e1c81e32a7de044450e9783f80dcfa0c3ce172fe7031851084faeef624498a5498681518d51ebb11a75e27bedd306336b6ba2f28c760b01d264c29d972295c6524cd97163634b115d73d77458d7af316496bc271194e10facb86e38b586b87957c0253daeac34282a6bfe47697035abb053e523252d20030a3fe347315d01e78e978fa95037fbaa94036abfdd737031214118240245a8f40216ff3d88e67151c97a3529ebab753a82f70e683a863f32f7f6c240a0d13427e70205756057ad179adc450ff7b0b7d34c8f156d2405641694705ba1d97ec92bc079032173b1ad7ea7d7fc11bb283d61e19dde28c31e8a2ac74112db4648b46f77fa7103148622ba96b563b97ba6a4ddbf196e17dbd17a037f2edf3ad78b6ba45ee6c54736c0fee85ca5df9d72b9e1c58c5ddd89e7a25a2367872def5f7f877cefe8d8ba06d81d4afa92f1cab45857eb7c08cf5ef7600427cc68e0c7126c12f92524e375a7713efbdf4b25ed2a48fb9b87512319853e5d4d0eb04b8a5a9dae4862677c4d4dd14daf614a188ec1d36953019df830d68510d7b09f25fbe407dc37fec591779ba08894bd8da29ad430aab9ffe47f3108d2cf6e9e5548ea751716577df44a7b42911cec852327378290f147458b9a0aaf1f2234cd6ae9ebbf53008c3f3872c8458d9e3e75eed791989fd29e0d4635df40df68543a6617983a2a6d8f483ec01656c56d92a46a49557b4933fe8e1d7e4086fac35f0a1faad653f1fea7c188117a41fff0f516e955ecf2756f4cc010aa587c633c7a2b1e9fe0cb281d8ba36136660d40037d504dc63260c45e6b1de3e34330dc1527665ccf17ee8dae264a7574439cd0906a703f779c7cab11d10a177fa0ff8cdf3c170283806b5288906519d91bbf8a7926c55f39d4f3dcae0c492e8f8b5e466d94a44ac9d23d111b458b1270d3e2c6f8ec60e8f06f57d61850cd5f351a45be6cbe18a9f4715f39b1e433e6608568b584f9a7c55321ed075edcbedd9eb2756b520fbbff31bbf3532b81d1627ea97f50cf216d6bbe19c550b97b8ebfd27f334b794cc9b8d50d559dc76899facf6deb2467fb59c6daee191e5aa0f92404ad64725d7a7824739fc2a1f44e84e6f094cdfcd98f689f116d628a53ae82e8872c4565cf1f736c8f4195426bb1938ebd5562f36578fb113c3695fe52906879f661e4af7cb21a864bf2019806f7474af08aa0c1057142ab7d003b5b3978950939a3aa528656f0771a3081e5dce91a5b26e72317b5eeeaa09976edb62fa0e2e89ce91b29bfb88f931037d7c0a5864be0e067f43fe906fce3ba356ebf5f6dd023630865a17bfee140b3ff0f7350184f32520c05222d48547999ea86b96e7912273853bcb37d84bb73fa45bea4eb63d6b44d2ffa75e34733d49cb30f44a71c2ec2a3fcfd849c77385efde6c1ee87081c568968f2a95e58820f9ee9d083aa0e05566eafe389f9c7ad6b32f9f2338a86163c0738306d9224dda0a8c653947ffb1be8c638bbde192b8b176369bc236560b2c82a206587d36348667d3cc80942dfa2e34233fc70cc0f8f75867a377426498ed998e276c3eccaa43e2426860ba835f32d8d529f9f6f98ede1a1323131900a7a7c3563cb0570b27727746595b179f445c4de71dc642e5f198bb29e1d9d488e8d9e7b6f2854479544b9d7f9c1cd67a241aad066d893d90348493c9816c83217a44506abe230e28e93096d2cdc6af453ffd1daa6ba55b86fd4957c094fa6b26257acb3aa6c2c700f6e519091304b62977e2fa103e0136bc0dadafbac8b8c66c6b82b542c33a5ad77e68ad097485cc46a3ac564f84aae97584f9e419459d93564d5a198ba69332fdd5f913b75728005082772cfc6a450268cfa293fc0944187adade23b15092631fa4c8b04abe5c2a1b0e43fe4835b9c0376094551960cdb8053dac3e7439f6d3bd69b489d99db19c3e0418ddc4ed099a8acfcf810bac80e2ea281e1f6d53b063e67946230a8202a88a5fbbc9943877b6d8fc1caa8f8653a2004b9b4b3f5f6d460d20b58227e03df6e990cad6d6734a73746eef4b05926a17d6b63532ed43caea6f5b048298e0d391ee9f49bea66c1d00000000000000000890e6f0500000000d0a71932922bc6eea5bb62f8e50ab0bed9a6d6a14ccc4abe537037cf27ddfc828ec2b93ff752dc0683422d94a6723d9a23d6fbd376821e4bb253a15845670905045c61a19aadd4279ecb6777ef851abdf0cb21d1b0f16b33adf7f638ea0a1c490000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029efeb77ce9f5f0793304e97cc610c2a761795ce333031ca7e4ccbbfa07884a7a657cb6bb2331de9bf1d8b1b936aceaa3a3f57748fd927d660674bd073765ef090e8ae3b2be239b3d6c602452f460c91057f4bdff0e591aecc740a0c4e45e1c20483fe45be94b809477f619eb78711bcb582e49017cb0f97ba11cab5ebb1de07030b2624adb44f195334b3e6242316f4265cc3f96b2ce5e105a390e558e03f745a031971b9c9a66ea5031037e761e618b757012bceb32502d86479fbfcc8b501837e0a0012c01d1eb31be25a3bc696c05bf74700e4122e2da8d6ad0ce4b6f0bd04fb582977bccbd2cbb48c446b1adaad1735bc3e2aa1489693f03931fcd0198980b4ed02148f2ebeaeb19c8aedd10ba4e123a87a5e5c396300b05b0ff4c912608e3230930305633fbd820be760a37ac10a7cf69b8c3db7a9141d268a51c79cee5b7e8ef6df0317f344d658790345d6e41e765a1a1d0d13f8277663e9a4561af95abd096f623c022f90731caf6e3120b248e10e5023115654ea5038c75be9bf11c03210aa844ed4030a0a6b754a7a54ed09099ea6734354fe1ca6aeb673692bcf27bcd8f9bdc47536dfc9c02c9052e9b43f350f28b88c2ce85548d1bbd18eccc40a275f446c5c9b71d0dc79c35b6587bd19e5a65c6a16ec6cbe4388cf63809605951acb17c9dfb95692784e3151c25e60d5c093dc7c852ac0f317e96b06e7ca72f1bfded3abfef87a1f4eed4f201259f64eacf7d45c9728f1341e2e84ec7befaefd117726972128cd97b9cb7bf6e962bbf15f4bd967fe56c94fc14e88792228a325d065c49418691ec14a27e92ca50acd92985ff0f53bb45bff773f81a477877be323a22bc813c1fadfb87e86918d9fac6d3b3b97bb83e2d3f83cf13bba3c4135af36c9d4f6dea79f83873e423dbed32fd6e52f857c5fa120d0beb13f25318539785ccc7ce7f10892d6fb78140654d6831bc3971a8c04d2e14f9228cfc3a5cd313b22b993aeaceeefcfa55bbd02046b76a9f9d7b5682b7dcc3f39ac51822653317be80cc843e62d9b24f5450925df6c82f8b7d17fd92b1fc639d9986792e6275042d528e936b84fb71955513e7e0cb61444a2c7c86fcad02e5ff4506039d98e7dcfde5a30827242a9fba4b64f3a8fd5780d0d93f847d806ffd03c19d4a8fc1f5eadd80eaed9cad9ce818529559f70d4844967fd3af536f5fb31cb9c7208791a8405d9da342605079ad914704d56e339cafce0955d0bad9be9801d78adbb081a55a25f080dce1c0e85c510ec90dec8d09fa0e14873cfa6d2e71fb1325b591fdfd88ce120bf3040a501b04124a1bc38c73ad7e0a970f853ac9c71dc646cbf3f18fe0a56816a7a3fda1e460646d7f4c7a09014837e1d12c16178d2f91762108e1dab5b37d73003b217b8b55ee7cbd7bed419a7ff5a30de8bbe8e0a7164ee521fbbceb38f3671e698363a13c51bfb29e1eb16e0d5df1c0317209f83c595cd76dd7ab38d72e3975be1129c8f3f24897035079f98944643e4513d05900e2a8aa39f8b5d3cff4b1fafbdededdd56e5de30db5380f830ba57967c2240f705c141b03f6049f3e431a412b05ba70546d2c0f12f9b869f35b3d79c6ce4b9712c05f1ad91e70571fa68e7493e11d78d509ebd594f28e64d09e017dc0c63ec2c1eb10250c267eb1c3532aa84440beae35e800751d4d5d8afd72deb9d3b390aa1a7e60df0da2810732f9c1f143b3ab64582ded931fa26e0a5fc8a31cae6e1feac32b044fa8520dbf00619604e5cfd06b296590c94cf5c0cdf2075e9b7072814fb1a764a2614c44aad1611547c4d8d82a2cf8a9f560366888b54a2b81d127c88be604448e1f42c6cea5e2a836f1a554fd07a93e64e9699667a993bcf3e35e2bd1e21a2265f995cda658c1c50b19c8faa2805c339c8f754c11691dcb847875e4dd3a51f4aa7cb9ae048a474b32e845ff6f68a6f9b3e13dc7939c4e200976d11c1acde74a654b96d2d6529d54051191f4b73ba7ecc0a888cd4d7850f4fbc5eeeed8a4cc40a1127fabc9f633e917e268187b69a0348705ddf0cad14adbb754850a89ea687cb33135fcef2dc0c533da8d6d5e363ec49a669aab2142773e12215a6fb38ca9b4bd871c6d2eaf8fb0e6f1ab0ba78643aad69a9a4f35cf8922ff71a29c633148c0d1518fe14ab027d8ed04299bcf32797a9232844f85c87af8c738c4c96402828c4e9fd523e8bfb378f4c66177ca166cfe80476629b642abafe5d19bcc46b878871cac6e46bedf608c814cc5d7d37d32a864dea654bee513f8d4e395b8e46d53730aa2b551c4a209044f6b1412e09b6d58605b13ae7b3d538a969baffa6c3bd3bacf530c28447777286e7553b7b627492197ec76e4bc6900066e0c313d7c814cc290dcf89af1f0b12f769d161acb1b9fdc15585eb9a07f6b4243c3905e71b0e", "", 0, -962950417, "adeec5cb231177795300024ea6057c3cd6edc1d970efe15d86110555fbd7dd68"], - ["b8a8cac104c2f1f1202b2b9a8c5464f103abfc76ec68dcde25a70f9c97c728404aee42d40e0200000009655165abab52ac0063ffffffffeedea1c908e8d6a0984e64c64ac8c4b77acfab8a65bbf1c83dd6efb5cdd79acf02000000055151ac51517c16a1fafefd0fab05cfc43bca301bf716d2a0f6ba030b3fa6cb47ce8c95ff209f32184e02000000076500526365ac6ab7e947d19c2f94b294f5e957d61c6d6fdfa023e08c48275754044fb96f74d14127e2a8000300000005655263ab52ffffffff0339d5fb02000000000253ac53bd5c050000000000023bad010000000008acacac65536351ac00000000", "", 1, -1190876437, "912a11a1d0a91d3b9872fcaa8ef970beedc1d813f7a0ed093a31ef546bc7ecaa"], - ["f2df72a803a849704a7119015f0a221405dbbdd49d37c7f45066ff3978aa7c26a5c00b23ce000000000951ab636a5300636a65ffffffffd63a76e3bcd9a3978bf765a4494b6cef8f4e3076419b1e83a397df7c579be0c00300000003ac6a522ef12d5e1ba7ed59ae4465127d6bfdd6e908d2b83124eac8f4ef96b96292cfc2a83220f20000000002ac00d0c91506017ae9e30500000000086a5265ac515363ab00000000", "6365656a526aac5100", 0, -1406636751, "b3f3fd143dd5df417028849aaa793fd03eb93f2e12413c664fe3919efedaba04"], - ["85e7c33e01ba66f9e807d6591eb01dfad925439101263a1603bfe81fc647bd23ccb781a9380000000008ac63630051acac5368db7384023ea0c0050000000002ac53f3ff27030000000001ac0000000001eacf2405000000000000000000000000e58cbc6e6f5e83dad8fafc48df52197fae268c571ea7badb90f5c90c42bb68381469f3dcf6467bcb19b205c098c8d1a307357187e4eac4e1b46b431945866fe3850db11bcdbee76b7b4d6f7fb1c68bc47e12ce034d20c28461cd7f23a8d08bba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000077d91fe7e9b6bf1ebaf5a113981b57665bc61c857fa7b2c4f2bc41d3a3ce7eb4ae453ebdde3f30b7dbc43eb360184676e8b100c6e7f6ea6d3bd2f1e18560e220b35d45fded4336a9f659559c597e7cdb3d84d897a73a2dba5391712a0b027ed87747b2757525bbc6341f6f922666b4865d1cf38fa998f40ab940269ea2834b3e02295ff66a347eb0e34dc24c482e0dff70a4fc9ba51436a2ea4a100e5f6df277e90208b12143cf806b1328d5dea3c24b90c6dd4713e23d5a6fdfde2a994b315dd6fa0a23eb70dbe88f23c8c245808ca9b5674dfea5d089bd99e1bfed9a48ab91e6d1e40b5fe7c764e9f8c7ac8a033fa95af9851d1add3fe86240679cd227d7041969a1021d5825068e3e8fa0c5a1b38491d8b2e5cab7d57709993156a73c36f02c9765220222547d81f931e3007322cef9379a81467e2d547f5997f84672b7ff028debdbb8022886c6fa0bdfd15ecc06ebf58f45f5b23d0c6ac1b10b61f0d2d3fa036a979f21032aa0cd94963593de76b71813d1f51641f2738bbd305fe134aecd71495684d191022cfd973db29858a06a3d35b533a1c90d2724abf6328301fc439700b89ed5166ada24f5d08418f4b385b2f690b9dec0d7a3b8136b00f6c7db8edb0691bda9601705364ace2d157939755b7d42b7fc3f4604976c38ac3df588dc1d6570f05aed8c697531981015eabc559b9eeac959e40f41d3f7f5d0b2b4f06cf37a0623f6e9d6bbaecf954ccd725fcf142b17bb7fbe3c89ad69876c702b9658f00b6637ed6c704304f7c2d234c5e4ed39c4816e83f63a30f6858b2c8f20569d4f81a5d9640f332e8bb5b68796a7dc9b5cb84c86aaa4b1b50717cd5b03d89ac59ba53a637ab3c9b9bfe701d105b474744337819b73a32f30072acc579818c860d90e9c29c207c850de8ceb24a75f519d8a7a5689d09701c71bc44cc00577bfdd451b1f0aa9d4dff2bafdb1c0c9154beb55d4e7f0e5fd52894ebf758de10d5fe8b2d6bfb9dff2812a04888e805eb59a1051636d8980f1b6332b22eb4be6c508e4d5d39f1a021d47b03fd7e2062fbb7b5b615ec02174e793a4199d6a9afcb9be63a9d0c628028a07466bf7f6078828932b6c45c9acc5500350486168d93b137e936d9a4e1ba115b0a08c6023a99771a815c56f17e2fa8c885c8fe2542902939057c1f4a545e440d65b7184715cd3d6879d83affd91064d60d00bb2c3b761a77733b2befa96ba41b10065bdf7e860e1808c7499111177a64e067be32a55f5c30dbe0a8b2431fc66b741d8b5747070c4f18b8b0da38ad378f6cfd6ec1a9fae81f5e863193ffeb8539525b2ea4db09615ae19c85fb0fc9852d9c968a8845399c2831c8eab30202057138a757b8d5ea0461ad2ab18a0e69b221c2d5544259b5ac4a2ef95b86eaebce4bb5a6b94041e06b941b4124a8448cf7a44a629a600a5f3eda14f5d0d576a80f11a44a2ad28f0f44866440f19dfe1147aabc0d41a05b41cfd768965730069763f02ec6b3e92064dd622e58c72135a3773235374de227f12406cea6297236286f396436c3362edb15e3fafdb78ee669b362b7ce9f689b9744e4956cb9416fdcd72c57815a6cacb3758e8b7fcc111bbb95e40e9ae16ebbff8ed7d5e4f3911b2ca7a0e1d435d51e061102bc424875bc44611e2f3ae59e64ac0c104dba74df7fdcc2bca3335ce69019dacd35d03ea7c37bbaa9ed443406e3e3df97ccefe6bb085fce791546784a5aa1c13408ea5c390a5189f6c3bc44beae656b91537ee4dc1fec087918467176831aa9d993270f6e2a13870ecd0d564e881bb5a19f142c84481db18360c59cefd90175559e587853a4b8f32ce8f2b40ca81cd2b5ea6197f66ed13fec0b34a0beb93e1ea57715649e2d53d02c3fa150999d5462ed4a0c2c6ac41d3cb78c2257b10ab4b43b38090976dc0a8a61a5463c2d3b5b44ff184d62c368cae8305ad525f95fd1f4d46bb6102e4a0c02ab083970445334a7c559c21840322625122c431abc3870284c5184c794cb5aa09eed21656a6bd032c7bf8426b680b6f88c5bb2dd4e93d2bdbb6cb15ef533f3b7bd3a8d62b8d1ed153958abf1911a6206e6a62f0476aa3e90f1072f39f64b4fa138a3e1cfcf307740747d2b2ec29dec7031ae338c4920419bedcd3a7a7ec776e992d9255fcae841a0aa3ee3e482d439cca50e055c3a5e73e405a179beac86be3cb274bf829b253bb294321a26a7f879691de0ae72a2c2c7371c03c96c7ad111f12c8aa6a251293314ddf865aa71a83bb4c247a0efdc9d5a281a6acc8807e805f3da60d428802a6cea3df86b81af73daf372bc267b05394dac8c7a152e3fd87be62a9913c462f68f21ae9cc76c87eb1f1ffe4e88c94d5bf5e9baf547b56e00e1d2bc62ac11041a0b4c34e113248b1db16a195fb31bf0ecb9396403164f5884654ff6c750b", "6300", 0, 2087165888, "add44d4812a1693bd7b9d4247b3d817064fbca9071a028bb033ecd92e21ade87"], - ["4713e12003aeafda525f98cac4eaec4b0635b922c814f7ccb472749ad1efa0cade708f32f2030000000400ab0065ffffffff97953c7affca64a02229cc52e4062bf5e46d2bdab2983c2bdc63ac189007b0da0200000003515100d278ece696fae8a6359dab5d73b63bc49b61f5f1337e6324210edc9e7dec7fe09f71dbdf01000000080000ac52636a516affffffff023957b7050000000005656565acac6255850500000000000000000000", "00005351526365", 0, -709014167, "3c000cd6b6b9eda3cb021a81e764692dd4ce478d65b9d8ad6092492d566002f9"], - ["76719c220252cad5f4fb8ffae5a54b762ca7bc99770ca1d7ea0662482ce27e839808d6b1960200000001acffffffff09b1ce3fe90f6b25b3c70705af6efb869db932fb5d6dad7a812b7eabda5269970200000006acac655165ab9034ca7e02e22f9a020000000006ab00636a636ae8c6940000000000056a51ab63532278433a00", "6352526a0053ac", 1, -495979550, "c44945fde90de88ae3cef44bcd0a39b1427ea57535644661a8f82227907d2ee5"], - ["1f87774104fd311ad25b33100a05f1d478c78bf20ec1ab1118d9d8e60e843ab1fc98f28529030000000151ffffffffd315730e61194b4e0e91159f37540add66307c1b7abb02403d54d5daca47d3380300000007ac5353636300653b50f16a9277bc40b985d7c51cffd7c92986f988a71cd6e50576c58953313974cd412a8000000000046351516550007b35de29477e7cfd9b11573351aa7d3dd9ba776841bb8654ab3d171febc54900baf60000000007635153ab53ac53387d31df03c0d5ba00000000000952526aac00acacac635bb9bd020000000007ac0052ac63ac65b4ba92010000000003acab6a00000000014cdef405000000000000000000000000c09b5e6f2cd01019e3d0507a9e3d15a1bda4345e28ec6b2f5f704a95d2689fc54ac05bfc5cd84a5d05ee9775eaa292b2f07de0279e6865c725c59f8cf85d91906e3cda1d8de648d8b06b9c774df6a47d3d6fe16c66e9e7e651b8e49873ccc04600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000851e7b02ffd413649e59ef6d4a7ee2d7145b340b8b95e7e923b2ca4c3715bad8545b64a3eb87ef5489dcc2887615676384eb8d3a7b12ffc395f0702f1b4917d5ef9109eb0052b7ae48109f515d3b3fe5b620c992f304bfa03e1db976e954838a90ee44c384233be3a21b979dad205eed425695bfbfc539411d9571a891448a2a032e678c6c495057c837858f53bef79eeec86079eada3fcd56beba7e8419da85e8031c82183f3f08bd4fb020ef5ed2db448c51b1a513939dd6ed590d54f8a56eef9b0a14e30e0bf5fba74309687fbd71b43909012c50db2e27a7caacbfc944d135a925189dac43cb9d92c91b396893d2d1bcd85ceb160f57a67827045a7d044f616942021bb612d6cfc429fdb8e88d069530db4e73c972c07f7da379a078e6c3aa95e1800203230678ed0effcfb4c8a2ad222406124172e3b6d63204269add610f63985e16032351e5e2214385d612f4e21b94b6bf6759828542bc3fb94076dfc202aa16cd81032e01af241c401e7023cb211a57894f128e652dee45f7e435cef65d151bf1efd702259399e6f540fd8b3af8df783c850c2a725c863fb592a759d45251e576ee2db958cb7c2ce30506e36a5699693b2316c4fda5e8ff16620e37afc91e7c223f2dbed02efaa2b7cbed8fe2bbd168109b17932eb4053e88e53e382bc3154c8cb681594d4f8bf647d23b044f3fe1cb9b745fa202aee553a7e1619bc8a81bb1ec1c57615e175a433a2acf5fef795d6e2e81ffbe6fdf07cba64c1018eefc1c96139e11f36de531ea98b4ce85e6b4a43bd97efc15b2a7bbc106be84aeae61df05f4acb27293cc716c110d73adad75123314d4aad0ee4d8521ea184e02afff0e622a3986efc62fabefb4cdeb44993673e6cfe98691e697bfad78a57a804433980a149945a58a065047c432cff46c5ee9680ebce0d22c58dc9a7cb0f9ef545492064063dd8379b93752a23700d7b8609ff402d2dd840db6b92febe91ec21c2cf9a92649611fe5b5f5acbf0145e98150b51a7d3234ba0e5139132d6660933882f5bb98bd989c9aeaaa87702f22de3a86d65698d58d8c6bd7d01bfa5081e988984562080fa403164d205c2c60d7436dca585139b1e2b23e32b90ce1f877cbb30b3184495568c0c1e70bd87f836b193342450ab9eaa582f077dc791642c4c0a7140d5f068ffc7489a932b89f118568fd9531ebe59f6a83022a82bc77a79b5e69f4be815b69b01adef0b8976e9af93188ca0ac834c9a73e06200d49b4665477dcbe8815c8db995f61ae7328ac70500309f05a9e69fe8b26ea3d1ecccace6255e54b1bc658ee44be9ca3ba1062895d77be054bc844cad3100ef1fa9d66bb96f02f5e21cb6b027d40a1640e479171fa4455b27f43126d0d539bff7252aef8e3f6260f2030e31a794753e7b1fab124c2857ab67587281eca6c2dd174082554f143c6b83ac12b6036f6936dc14c7d681a1a5483cf9743e185ec704936f006c9c938aa5c4882085886d169e4bdab42d50e3829f31725916629680a1553d4d0b30e1688a24447bce5d2066f7f91a19afb954a4e8bb54db745a3b37e4664ec250ac5f5554471f18b4994d50e8fac718d1ee6de5046846e5457376bfa6a0a487e33d60947e4a27037a2c0a8c4ae3a6f6275988f084251d01f5b204a8d943991b2f6aae92b501e6dac2a4e06ae3f8e17ffbe94eb1d7a344a0e96dbf501b309bc8f17023dde9c4e585e574e79399fee9435a14e7c1e0d06e85ceac768f297d2fb5a293d0c5a61847cdc9d40addf9fc32f6381a20ee6fb9f0909111272191de84d61e53c55573b44d1bcbaf57fc777042d2e4b06595cdf312f2af964d699218c8c29421eb059460425f7db3cda5792d812202d9d02970bb34924cba2198f4701cc054919d212132bcd02709a11261dc5eedc26ac4b26988727e667020d8289ececb1fae5fca9753ad450d8ee178c4e29865afbc4d92d844bc889e36849aad76858b3be78a2457d08a5b9b6e924e7c95b2fc56974be62435ea58f2c6c8623eea8e77d647120e734f8085ae8dce371ab36d7194f93522aaa06c644d3f655209a46b359fd854780f7bd5a684048e9456313730e80ea947d629b426a5b41b688e6213df7a8b5fabe5d86f491857db7a7d31d4df3658d6a4928d6da2d2cd39aee67c57d9a3c469c6c5076315973c59820e893d3b459a785526224ddc35704567b9778e9943585935f42179a9640efbbb69b191e62fe23605b89b2d8c08bc5926cb0f158c27818b54a255d269bed0ed32970000ae9c6156678cede674206e9380b85e120b54d8051e4ce1f7d01a75cd937bb0921f8e1ee2a4b1635ce39db3e0f3a08d70f463fe92322c8d6c62ee76ad7461754f588856a7a8d2c9d44a1d064da068c256c3c1c0c76d22003bd8f738947bf86fb73a9a0e6b7334581d042309a53b803", "5365006a", 1, -683488930, "450f6957449a8d0f8fa7f4b0e53aa284246b74e9f30e3e78f4c9c8900c61f152"], - ["63bed20e04416fd75d28e3eb741be144d42ec9d551ad78efee5d637aca0a8300a57e2c295c020000000663526aac0051ffffffff2528c63802e6287670636d7b4aadab8d24f2dc31da1c05df6b95cdb0d6092a750200000009ab6365ac0051ac636a1d620ce12fc79b1f9a594b7fa5759aeb934663f20387a5419659fa24f8094c26ae88dee502000000046a6352ac0778bda0d9418bbcf6ac3edb478b55cefbefbbb95ec3782cf7c55a6a5630555f4b933642020000000151f0653ecd04d2893504000000000352ab6ad5d8c90200000000046a516a6a377274030000000001ac1a688202000000000865ab00ac52515353000000000102739403000000000000000000000000fadf89db2bcf407f9b717ec9a852c1d9a6e231309d7305c56b69f6156a77e9fb0ee5762b01c4e30b4dff842fe343d614d8c68f3a2f31e157a9f426b06c1b1696ca13a19f1c591adee00d5b0805abef6c48faac6725ce0bd203fc15bb70dbb3d800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a19bca6eca0b478ad3ef6609469a5e34b28bac3b192dbdb3d8d12ca289c438a566f043023378c0cbe143d4fb3c1b252bbd12c68ad5072e1261a7cd4d909e58dfa65cea2d8c701dc84cd019a6edce795a8c8e1a1ebd2b5f15a1425a183fcbb537cc7e66a71120befac00c2e4f2a88d1552963711f994e7f85592ae369eedbee39022e318777fac9857961fe2bc8d971b0ba10489df20e717fa99953413f3c1d6812021700167f9a9c167ce31698ae72538523da79cd6c2e0fbc4d880e6792191edb950a0ee40f4035cabfbc80b0a5bd5ab2626c9706c14b044d02b5b50976ebf4ea362f0b1dc867786985dbf29810c789e06de40dae4fa1f9318981b636c694d017b7e302002ba8044be427adef8d51fffb64b35acb0f580d687bfe8bb7ed8ff42c359fe50221dc57c0a1db143509a273d5304b544562e7a86f910740675c1a11bad6ed8696031de82e58ad330d470545e6465c6271d40f367bf7cb7645a229e3554d522b4adf022da1c4bbd133cbca07a651edca904e87059d2f0fd7339423863aff3e4a5349e80214d01865e3d7cec5504f6decb1483cdee88d93c27e58880d825e64a8e1e191279da0d7f90103aa4192a4016242833af50d1903136262ce8156cb8b0c57a09ae4c79307d8f8b6c736ce05540712daae505772db5e1883aff9ec9d58ef3b126f9bfde8cc22c97db83449d0b78f139a6ba0c6895c957451901bde7ce7110e82c82d81d32f1d63d4efc5895db87bf317d164341c319baac0279b0c9f0cad6c393eaf9fe270c5f17c3c03177e76d6415f41df81ceaa3f228fb0f58c0b547f595fd63a04e70ebdeb488c9df9b9c2dcc90c218db6f05a2bcf5e13fa83a9b3c3b421148e11a84f8594c869bbb94aec09e1635285910878757076f500c7cd9132e356c2ba0b1dd976528c2c41cec5726ba0efddb45d8d23c5e93163fd3b5a5f80ded1b8ea05c095131a8a13dfaedd9bbd37ee23a03abc44b8cca04aa3090c88c76c53c5f8e87f6fe8460294297a9c0caed5e069aa6bf375a82c2c76f79392b20441d458c333482ee09fdfe2fdfa92335fb0877ce65eb7c92e72c05731d7994d7540dcdc1e379aac871f5c4c3edf16535f62f3144b726c32d5a4f130555bd6701c44509607a1e879e68544e32004f09bfdb5c8c8ac9356b98cb8c2237b43d2cfa5b0208daeb7e570fdcb352b6f6306c468d6c4f06fc9be94e2ad2a4e1e902274d4d1d92fdb534d56ef9454400453dac47af7113fe42b88325b66a6e36832cb0e512629826e1bed20241d05dd0ab6f02f85f5522d17b34f8bd3548409b18f1b33469f8f442b067283ee5a3bba1c64d69e860f5a70988266d199ca52bdef9dd910f69ba0daa866769e766e94f30f4ea84ccedcdcd346e3a3fc92b84e6a53202fc7f243bf414c58ddd324bdadeb86bdd7e090282ed395061d0dc6804795f3859bbc9aacd0f0af4ec7060b4b688febd9065954da6831b83e6cd107848359ab82f470e478b2d45f738d6ad1248e7606ef6727362c33c711ca25f10a2e44d1b125ccb90b122b37cf0982e8d069da3dcce5601dd00f3b56461a9723f0fd6beda083f10d159f0c1ed3ce862e68009d0c9dcf193a382ccee645006758bc1f8ff0282a0f60727bc35070b158acf741d9ba6df7edc68e56ff8de5045ed6dcb4c70de4425b101c9a88a1429d81b46f5046563a518cd63314fb3f1d5987feea0c8b8ebe776417fbfb7d02c03e706a8441ac5ab20dbbb05c8ed3ef7613f87e2b3c57c593c1fd391d588a285638fc146db8dd294346ef18b0eb80aedb0c5b9211969d03af94dda5de40fef146bedf2681e85916d14c2d56d7e4f9f54e960a85ff35e9bac2b521e1c5371922f5bba9c786d078f0d62767a30aacb00c841bd9de9b72aecb4ebb8527919c38413501d6fa42741cf767b89a96d03d77b372faf86d7bbd7a960317c867a9d0cfafc3fc7995074d094b2ec4859240c74809d7f45c7331f3945cc4cae9d8eba99431193cf935e0ee96640b65f35b8fcb0cbb7d77d5f5e18bfa005a11f8cfa4d7f85669851f2c1de188a0062162a98985d55b2c61ebb5cae48960f417cf6187dcefccfd8a92e7c16e5a1e24509d7fee2e68961ca2594527a35694cf179a9ff6e67565d79cee4fe2f06695fd7736749bb603c55b16a7436f9ab7020339b71ba76c8dad5d3515a360ccb5190c5509550f3d2a0c11358ae31122b01c124f5c2abb94abe890b7995e4017027ebbf2642973263e81e66bdee616b83b19199677f643ad5f502b1676aee3a07ce09c69f65dd1346385202077ce40f6159b0b008585fc5b39298ecb135dbe41e881ba5003512adb8ee9514598009204346dca7218de9a794e9758b353ab156c9c75528aef15eea817735fe11c18525c0484c6ecb50e50256724f5cf2debcbb5b2448560675b70dea4a7a1a205", "6a6352ac00525151", 2, -88450697, "af59dbcc6d7bc923432c631b8876ba7927da6010b56d117ed91dce8cbdabbfff"], - ["295546c60360d1287e9c4c67fe0e8ff101ffc5ce5f0566ae4d8f68c6c95a7b572146cfdc300100000005ab0063526a4d7bffbed0cf8a91c00e2d99346a7ace4eafbe5259966a7a2a37d42ca93b6bce4b23e8150000000009ac510051ac65536353ffffffffafe93b13ddc5da3b5ed17b98c2fe7261a169ef5d33979040fe9f8eee3056302100000000055251acab517707a8be02d77f7d0000000000036a00007e370d0000000000045152535200000000", "", 0, 1347590081, "2064e8aaf6cb16c46dde6d7b43656fe7f98496e300c756512e1327d51f8425ce"], - ["d469e4850209ae0e1b2f1fca793f467b5356a7d543e36915d936e2099eafae4dc97125d67a01000000096352ab635263636552ffffffff8ec67f49c7ef0fda6af855bae56d87a4e203ec94c183b9ffb2b4a820d4f9a07303000000016affffffff02ef159f010000000001ab5c9a4302000000000000000000", "6aac6a63656a00", 0, 1034546221, "3281c1afd32b47a8068cbefbf7f143da472998ae25fffe1a4bed7e878f5a6876"], - ["babbc4e50354fb024837d24a3a172ef8fb4bbbe0c908a28303903d47cd378fb7d867301078020000000500516552acffffffffa1f26d1e880e5a32c86e735bb59d6926eb5e53289c7e10df58936e5dc14162b701000000046352536aad818bc117e1696e5e82b6f98a7b4167565b2517e51a411a4b0ee43297cf3e4bd36721680200000004ab65526a9271c5af01a31ed5050000000009516a636352510052ab00000000", "52530053ac65acac", 2, -692393489, "09bf3703ad560e529f15cf30e6a9ebe10f53fd51034b6d65ba0211bfb3d3f3a3"], - ["9892616e01ceb38b1b47845894e4c6962bbfab6b66a601b51db09e438016f7426cc4da0c3c0000000006650065510053ffffffff02d579130000000000065151536aab512fa6ec00000000000253519731b2c902000000000000000098e73002000000001d03263d242c299aa10c36897e197284686e6761c7a3250fe8064c63c829d8d34b332f96302a02c43571acf3ff215db278c248891a0be6b131e2ae296d4ec6103359965d1b44f7f16dd64a3479dc7529bc3e5e19daa94709e707e0a5d8e19079000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005bc918de29c7ef6e081b2683fdedb12f59bfcebaa017f7b9d725c4cb259c66c130f346234d5130cdcdd92f5039d53234ccbd4b0bbe7bb3dc17452eeb1089addd2164f1c57a9f930c402e92469bddce4ae590f1fc45fa3e2f93862f214f3ce524d627c71140c897a768408aeae47457670e09b1f5c5eb23a210ed85c9ba51e198032810f187961ff8b34067eb82b714d1b7690afbdffa0b2fb8d7335bbb1d44b7540201d469033d3c85e3b4ec7727a1181bd5e2e745e073ce87236fa6b439ff88edf40a29cf2918768d8a9c18302bc354f75c2e9a2c7027981eecd3a5012581fe3d8ba1036ee198b3dd93612551ac551e0f6906b2775f08c4108e955e231d965e7e4c090227e0926453d8ff4f4e5a2fb52a2094e3e485c847e2c710e25d8d8b23c88b0dcb0217e62195f0aae90cc76600146047e566e21476cf408b5e4cb9cd098cfb4a1a90031c2a4cb5319de7a737f7d2370bbeb7c438f9575701ee5277b2ea1f9755e6f78b0212a8aa80bded47bf2081fd68fff9a36c82263a0c3d9a3f438b840cfbd16261c403229510647176c7ad841671bcb763467391147826b732382bc6a44789c0268ad9f05680af1345d3255a0419eacff6bd332626f6a04fb5fec00dd25f3c0b2812d703ce788690d1b66ae5bd5fb5ba907721490df47808aae1b30c427e3b13cd7fc50dde833d5f7ef35552b67efff8d571e19efa21f958777997ca60da7d3a8f085be2c2963ca59f08e640ba01d28415f80b90e7b320cae84f0af96b1f6eaceb2ecbe97bda4a817b4f3a077512f783e61e95464ae690881d950ebec2308c1d3a295993bcf2168aa5c46e8d3014467a22b8063143ebbdded3e338cedcd84f7fbf8b1853bd8a4a20a70191dd432f0ed82037b41015cbb593daffc59c83c625701400c5775fabe97df6335895a754af1cd918823770ae8d585b1822064c23d2bdb9b9bbebb0e7f0c925b58de9d1b22fae8f1675d6cd6e5d76c67dcac9ff10b3fd6f37320c92953ebdda7bbf84a8e87b9b2558af2feb6ded41221150006b0b0e1e444dc6c46ec76106baa206db1b94d8b5a7fb026ffd0de19797f5d4dfea521ff0790ed83ebe7c74ed5f238189f0259d2e62d6857031b6e7d333f48a84340876979ef59e1da82a4ee28ebc7ecea5058d64cdc1958f70c3a5c0a63134f5767c384f5af795a4ddf3fb434da677831c10588c55372170ab96652cdad4292f32550882c5f1ad750f398fc4432ac11ea7e3dec403e1ddc95eaaeaec2f38a30bf221ea50a5f6c5f929507c472c81b7bba43a729bb2ca143bf619f0b61883bfb1e41277ac91247032f5157b3eee4934f21a8d102cdfede05c5f914de84868adcb94b61654537dbc7fff3156aaf36e651447302e50d2a91788c762d977970a48372a10ba27b26b0069c8744bcc0a5dd1f16ffbc1ed9384e5f5f420090b974fe7a2febd99682e58ceec438ebd37ae06f7b9de342c7834fef164063479fef2abf2c9c24cdd71934df9a297be24dfe71d5c71f67d8326afe59e06913f98369b82de32c1076493da6bf826095d926cf2b92d5392d86fc518234fa4bb855fc8fd76a6775e8d08b6ef4785c3d6b5be60cce170773a3ece25bddc0370594978f3e4ee1325c7835ea8cb094e21356978fb79e59846dcf4eec91fda8ce7439ae4321a21f0151fc5714ca11c0f878582d78e065d380f808dec316066c085a9f2e6ae1b64939ee6eecd0c68f80dd1892f8620590fe9dba4b1f4952ef5e2e96afd477febfbbf47b2a47a7a1d2075f32ccd8b27537fbb2ce52325777967e2876373264fd55496a5dc8f1b3d89ef6054273657f237a3c4f71ea015d416a90d6a1145338c5e35d5c205785f9f69a899ff81f234dd076f36a9226607e1598395a5d814c68716bb4bd78a5ce86610fee789bca6df20e9f910ad602dff695b0398117a7417fd9207d9f1aedde5437d4d84e12d3b3794e580d7525e15f1d78abc5e33078148c3201d6d7f8372f205f1008047821107de0107de9103ca452c760d5961571de6e82996f514d4581fdb542a795f4e739cdbe2b27b52bceaa2b0f53b937713dbeefc250947879269a8b55c916e3676b7ed41082795969c58dd4d33e921b66cc9bbd7983908fc7e89bd5bc0db58feb567fe232bdab8b999085cd482e3dae9ab64b6534508763bdfa8ef79152d28c366cbe8aed8b5ff9993ff0ca8ddb41fd9042b5eb2e19383b01865081d4f8f0d57991bff0db2e0cc87e9bd982f69ea71d1cfd5713ff26ab037b30e72859a9b877a649b4dc33c5dd547fda850618531e4823ac3b0fd010000000000000000000000000a64b7eb59f259748906b02676d69ed3116b2dbd6d1afb0ec79809ff43334f9df803c1c4a7dd4a5d6ea2bac9aca0566abc13f3bffcf074974755e350cdc5f48047ada8fe442d0442f1cbd27dfb0549fdecf0af356235cebed2ddce29764e780500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000245caa44317a42e2f10c816132a6891d7245e6be929cbf2ff8adcdf0a9c4c186563813e45c9bde0b41a24d48093b1fff45dd6c80b91721f564f6ff9b05b89efd221486a93ceece49269aa10fb50c4ceddbb0db1baeeb6146daebd240e9b151de8e0459e45c1ed2c08ed392c4a53764a2444950df3ac507c70a709c8a10a79a0b0314dd15eb13042f2c735186af767794ea49b88af6d100782466ca44a0fee8d5ce02020f6580539c8ee63d601f38ab68b09bd7c112ca9be20ee9fb79ba83397018390b1254ed52763d7937a1164ef090cb6f6d6253089bdc1ab2820d380efe37cf94d50c4c8d1b396e5a6aa93432573446524f3717f093d096bf18c0071dcc532e2da9020d4540ff609b4c436f8f37b7cb0c78a92ea7856334e69fc277a5bbec848b4165032461875f4dac230fb526a9f43d74c3dfbcf7874146f7b1f3656e8cf525defd80031da12e74c5683b52731113f689f1fc7e8ff18e92c15259a0123d95a95bfc924e03214f41ca0f5f642c23d8d06cbcfeb857022d620456ed9721707c5afc08ee4f6d020c61fcc4346f6f50cbf71ba24f9cfeab167da86223d0b8507259bd16cc415e51ddfff674513723289e54ce2ed1f90a586275c27179ad671eb4ca3dbe9d03c599fe91d17b882d73ddc7868408b5c3b2f53c1dee0a827d3008ed298a555d83bb64b208c052be8cd8760526a7a693b95e676726ca39cd096239ec62e39720b108718e421dcf66ecb398292e1e2cee7cf36c1086cc0d826a7300fc57805c27d7a4ce1b6573b7bb5ab2febb1ac1730207031e028ab51d17e7e2a189ea788c2dc7dec41f2157cb825235aa843019b66832030c66bf572aaf2879aaaa004296c96261f018aa4d38c41e05ac12d0ec66af54d9e6af9e86bd7f401a61a80a89453b206e5203d86a1223c291987118572b178163a553e4063f7a1787f3b163980d77b1c506d04bb3f978f63ff60fb0164f80765f2ac51583a72ffcabfb428ea29900f266a5904a206bad6391b53d042e4623ce131445a6b2972361c42011495481b76b0795614f61515e22f51bc76646c8a603b10d7fefd72f05d472aa47919927f65f28d87a6c21a010d293c29b064636a3c6a536dbaba570ba6341ccad314ed71aba31442b9f41be83429ef5c3f87be284f99e917545e9a830d3efb3b0327ac8576046f020aa0b7d8fb52ffd9739cf5ae785908b6fa55a98244784df7e204094cab4583ee6fcbc7b90bf0ed7c73881eb1ea1c805062ef795dd6a406752d0a6bc4913daf4e32e6caa648463c855a7464e6a08789d173845339c99d8006f2322f7bc0137fe85bfcd4e55e86970ecbfb02d833299fa663dcbf4307107a0ffd255618833cd780e01fe93241117c4f29cb190586a564af170314cf365f629449678cce9e2adb31b5538fce4656db064aaa4d13387cf569cdf12458ad4f081961e53da52f564a691352d1269f6ff4501e271c8a6a70850fa5a9445af1003dd461718ded4f75bb0d3cf553622845f5519f47e763d096e8cd5aa29242f38da9fc65f83725368686c6fe969a4a90642c73a47c3a02a841e6455cfa534eeadf715d60d93f0c5855bc5fd5d055b5046e9aac35fd173dbdac8c075a68634ad0f6bfe107de3d7f39f28870fd948781f5fb7c65fe1c772efbc133e438b1864177384aaf02d3b346ce1a45676a40609d3357f794d272a87929ac4fc3a551c2e315b34e23c2f7f127234078aa32e7a6234108865fd3d7741d8126c0cb933adf529b1437f94c35821289bade5a210191bbbe8cdc90e9444f6bb4710602493e9d9dccd18ee58e27d997c4665d8e9e0c344404e8b42af169a548e2b09c940467532ed177d0601972304da4388d3803d73551b4a5f84128daf12cd467270296a6128631fbc0ea5f805262586ce176f5131dc8596d46302e1b2e7da0bd89d33994ab362d2c3e21638d135fc97fd9afff0e5c183f9a32d5babd815b90a84b1124387b165f8cc660ba3e74eee35ba48f080962e6078fb91804181012173e1418fdc4401b514e7d71cbccef05fd491ca41efb0374e1610925370f1e3a3eba9a9ef518c96c472fddf7a0ab1564bcc2873bc50f05a5cb0fe1f3e75c534cb818dc05bfb6d1e6022bca849ed0e2e10fb8ecc7b41a7028370840a04ad6b1b7e4e82cf5968dd2dfa8abf55635e56c985d97b89da0c3cb43f7a4cac7e608dfed8aea49baadaf149f756e90cf2e4455010c9430636b2f4fbcc681ee2ce7fbc2f34be1d4422c672b0aeb99e42122c597937b352de6b7ab979bf0a52dc703883387b6632286a2ca594af5614efd00608179d22938e4ef5aaab801e9c6e5f5f7ca6eaaf17cdee5101f097f9f9d497fa8988236bbd01ebd9c640ed3a896d4c89ef853daeb558eb0aa50c935a9a6d108f8030ade97afd35831d0c6ce495a1624928d906affc00b00f", "6552536a63", 0, 1892728135, "6bfaeea13cfadf49012587029f1d02d79f71d521e9c3fb692b7020dbd057e880"], - ["7689ad9d029f16c29b3e263193aab8db533cdb68718995b25f0b2c76475b6e3bafaf6ca0c5010000000165ffffffff0679064e307d1bdf157152468c7f4d9e71392aa1601066f8f5ec2826f83e58670300000009656a000065ab6a516affffffff0434f1be010000000004ac6551ab3c5e3300000000000465abab6574793d0000000000055152abab53c6eeb603000000000700ac53acab6a6a00000000", "6a65530065", 1, -1047218082, "e11166347a75746b579a16ec592be7149e66893b64e89afb1bedbc09d9c7d544"], - ["e4f4601901aa28e15581a3d2096702176f61c9b2f47be0429b35364df06b2512f221a8314f0000000006630065005252fda584f1027807b7040000000004acab516563a6fc0400000000075363acac5200520000000000", "", 0, 2114858216, "112c419c700b1f6df42facc85790691a56e57ed20c41f9e1a3095f38e261fc6f"], - ["846fe45403e7d8a4535472dd3e820db507fbcf46d473db1011926a70326f33fc012d0e76440200000006ab5351510063e9aead3e202c50dc4faa484bc497d6fe094cf4276292be894189ec40c6e1c5b1e04fce17020000000851526a536500ab51ffffffff1d016303bfdbde375f0c12d6bbc8fcccd44669f6c66f44e4b2bcab1930c4248e02000000085152515353516500a9d863b9028def11050000000007510053006a63abded0690400000000000000000002ffcb1f020000000000000000000000009270d9aeea91ce99c61f2ed3078235c76e2e540adee55fb75f017908fa55858bac11cbc0ebdb84f8f1f7ce9b06ab65ea4b9fb4788d9280efbd1b76414a4890c4e072bcbb74b81e95212a385281ce8d99cee9a45857f3d92b57fe42e20b993c78000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005ec3391758ea56a3ea48b9556dc1a621d085d56e13b6a6eafe50c6e62ea90a69793e78ca9063df6d8fab5b8fe2bc685dbb360ab5641e70dc6732c45907876e2c1410c6fefd141d917180a6f47094d0f64cde604e124325d43d8de1412b9f778927158bd61228d1cd90f28a4567d49ccb10a3abedae0bf9766f792c982c1f27a3031131e8bece564989792cd473632042c434fe6f394cdc7bd0fa98f407c9804aac032a442c80f93dd8e4a1daa3350086d0e2fecb6274a343f28fe24854b67acc182a0a2a81a38d5d9d0e806c1ce40216161022df29700f3f70fcc8ec9e9b7199ff75c405810a3ae051b958189aedc9a4636d4bf0299c698e1a461c634ccb482173ad940312af512454b6177189c1598b1ba2bde4fe80511473ae9867c854012cfaf4ab3b030484deb51892c33f7b32573f4a832a9fde7fbc68baed21c3f0b3cdbad536695003201f94617f43b108f65969f522cd12431b26b680e18371819499530a3ae9a03b0207107f091d3908a8938ea3eb7b23ae0e2859f22a80663c670f972d35317b6a88020b11df65c9d0413f3c2665cbc4c2facc4738ec9512d600a6aefff6ddfa6a318de3e08d407a4a2df216f0ad0b55b9aef3057e293fab46c8b5091374c1c9e5dc4802cfd162109b0d802005e3a117c0a7b7c7a65b2c96910fd9ce0b9ac2cf3622746fec09a76381978273fd52c59bde4595fcfc32b5096586669378e944ab2d5f8fe6d9d1a92565d982f532bd129a58dc205805bed06db72d44982e343537caff78e72484f6f379a020572cd391f7e2bdafe480b372eba2186ea849a296b9bec2c15d1f21cb1b0f217947a63389464b7e339cdccad24e9777611c3d8a11fb1d15fdeedb7b506bf6c3ebbc7c527549d7de56db6324f3fc333b279e49cf7a03bf82e11f543c5308699560696a8c3587ece47eae791f1b842a5a9cfd486b9fb4a4c6d1e8678b7492ca370d5750d90785d1c7138b4df7364a20c659768303720e692be4fe88d70d950f23d38120331bc2b5c6d81f228bd8e5098c5091f665f2868f654e943e8942181693b134e5b772239d2e7dc086456ff6cbd8c18fcb3c27785b45cef9eb52aaaec803cba110c0b0b73f542e64de53fc3d8410c9319674e87e0947ad4861d8a24096cea7a4e9e59c8c687396ea229fdb5d086ec8cf472d5052600fac86a37cf7ba210e63066b21a860a1e9bb6245ed51e7646c9bb9638d3a641987d89641d1914c1d55a8ee9ea0dfda5fee47cc68a3323ef04dc25d9a439fc6b6aa59f577e1b7f845ef073057c04506400b87747318b3b586c561f9ff0f9e0d2ef27788b6872715f9403926dd0ea89a52661e7ac04506bd692a574e350c74f9c0e81a498777362a14b676460286731d1c25ee135ae820106200f0c936e6ace7fea5249f0f36079a862492582ad6cb0f4124e28616b620cf5240cce4f7d1096f3c2ffbd401db388b3e8da05f5ec17f2903648d1d12c6df2ad464b41f0d3073629d7707b1e5ceb20dcd24c6de57a46f06cb45d664e7eff7e34e737944bda9f608cb9fa1dc4941c6bc4be06ecb9b214b50e7d906bc94b9befb5ff6443b80b4111b47dc4df011ba07f795686c11700baecec3d738aa9dc92903609300eaa8aff595a4dbde6c07c62e8e6f76ebe2b1251bf5b5abd8d24c11353844f7d435cb5a3b5a400efb7ae22bdaa5482f3aa8492e3f9f31c38caaff910f6693e488ee2d243a446b44a12c1da808ca84b108198307e5a1d26eff8c544018bef51de8baf6a37e6d1511befebb398d3fb50e87d9cbab880268903bda163f4ebe6532787d6cb9f5b16ee96bdc1a457d960e14fbd10195538f717c178938827d2a054fac9ad06fcc641f3dd51b7fb0cc51c6634b51bb764e7263a7b580f9058b5333576d3cfeb80b557c146f079eefbedf05c767791ce15df3e71426e16ceeb7bfcb0e1960bb73959e16a0bec2e1162e5ea28bf5ea52806e9591cb57747e30f4c3ce911ef544d930c4ba410e40f80598347e1efe61c0486e469325f4a27ae1da724dde390a4a7f1369caebf8cec717304300209c041a0a531232f959f61f4449ca48ea8d3dc31f55897270fa4e24621d5b7d8a411b859735a42cb2728adf6337de910b5d46844a0a10e75dfadf7ba69c4ca4ee7c503bcc4b8900d9f8bd69b744b5607977a5565fdfa690fbcb4c24ce208522ef0e517cd252d715b8cff6ad3d2bee69b2270a6ccf9f3647198cdfc29c2a9c4398f6b057bd51818714f8930dc078be83b3a8f5abdbcfd06ecfd41c703efd87a61e3abf35f71d9636f3f79f5600000000000000001ffa930100000000b64f67a66bcffb1c63ac0a6327e1b428f9eea8e6645d9b0acc2eb2f456da633b45ea36c52035e5b1cbda8d9971e2ac0f1168e1293e4c197c5c3983ca49004dede4fa6334c04e3e58d34f10b970ac40dafe137fb3934bc887d5028303579b2bb80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080dd3b72eede6224bbfefa4615fb3951c21ade7e728049edd9c4a47e949ee2ff19d8cad1220488ffa1211de15a42ca35c6f7cf3a9427b1ee8bf2b68506d4cd93f13683484372325084064bde1f82f8453198ab4555e2f18984f595ef145d82dd58863cef31e3ec176a3119a090d3fb76feae688dfab365c6080cf58e0890c0c502122b8eca7f7a03c96ed979d0bd7d3b57a1b635aa849df7b30647ed57af820017030726cb4fc23c1dff991c995fc8f7de98b4075ef27181e85c3b472f4dcc68bf3e0a04930b8a92b0d116f8fc61664f5259d52b8a7a3393ce5659053890c9723cf9c030295a0fdb18cef7c2f18d18cc4a27b410db639a2dfd6db323da01cf7c7c97870207b4ab144b656afb6bf1a5578c62cc33de42b8bfb4d28d318de1374cb3e23c930303330fd593e9cf7aa499a300a479169d100abf7dcbfa3466d3c082f02f077e6e032ad328df1598992103b68ddbc2cf92ab6f921bf5c70f8cb310ec56699b10aad0031f835da933bbd3f10d49659f27b4955b45efebbf7c57331de9c2338470e9c52702287fb0414847c7fb6d6aec40d626a33a52fa36d904ca02d61639c03db72f3142f0f7eebd249c7c7829792f395e91fce1d19fead1ca23adabf8c46bee535436bc72642c06438a344d8bc17d39fb400a73b17e5499a92468777c47eff62a342624668fffae4fb34991798776519abb2f6133edb1d08de1003f51d6a9e59fa0a2980b305ced2c91ac80fc6a11b3e93249894682986ee780e3647f7c294390afb46b562ae442ee15232594bc0b1f720e57a8fd25f028d4f3c5be66968b4a66d61303bc3708e2833c1cd8e4ae4b0e3231237e5d124ecc0dd6989d438c98b0414c482ba1c00e2da895e5167d00f2a6dc68ecf557bc945ae5caeb929f8eaae33189343adb74512cefe32e0e8afbfdfe67de127325cd6b0476ddd36ecc3204e8b66a5373ec43cd72fb7b5def7b77bcd1817828400b6dda4a2dd6de5cde9127197912ca8d5509713d8d4bce8a38516f49af621a4bdd37eecf6906411edb3105e81f20ee002ff22871dc4092b61c8623efce4a8bd61cd2678d1a1f28ad1f3b2eb0efe17d1c90fcb7fcf976fabe3a68cc563301d8d37b552749a018cf46dd0e674df1bca611a6f60e6a673c08c0b99c1800881f0dd59f09a8cea8e1e8fcdc7957df98294e4e2a2d177e879dbd8b32b0b8f792dbca4863324f202e2b8db6a954add0b48e2e3ef073bb1b26975f99ea9d35dbbcc0a0f05d6bf9ec98783d76f0675e9ad1d4241cb6a8189f1fc98ed5d63921a3ed58636dc6c47db66be370aa5585b7e00cc9552186feaeb36433bf8a5f34fa17972de7a2b1af7b86b4ea809b56adf7c10da88ca2b09a33c259006e68304b0043b8212a5db9a80ec41fc783096964f76afd8ccf1eff36d4f567e1583f1d374e6d43ced952eb51a93ecba2b0e778d37687e9607c60e4e7fd0dab394f6d9b64904008c4c3847d5c953bd69175100cbfa575b69231e11366f2a4430190ba809f65855422070f957d87ca29d1877a0f1ce5ff3ab24d999b01946a354d7eceba6dbfec78c86ddda721a88153fb5c2b641265f8a8692cf101f121841f61c3fa21202829867389144c0a99173e51dcf077ac42b97a981046e116cfd8f17d130c7c374effc099bdd7716d44fd2c64c46569b2171f4013b36a96ff7dda89069e6a00203db08d559facdf998c3790777fa9d6f5ee592b0316c6dc4d096078622e66c0d3772020ea220bf520732c17d0b1a8e5ff24fb6bf01f1cc1805c63fb7868c083ed74d17bcd4113876b82ab2ba377553eec003c89fcf7940e3ba7797da1b9311fc6fd3bfba1550db9b8dae11689c65aa074e41dc99f206572597b83f69a08e972898a4b50c29985528e66627d556c0b65ee4c4636488ddcca46f6fd8422689150e3d98de849b5f3c521f8132e16fc0a7eb2f06710ab2b140a0bc3736bc133695ed6b1daf504b8303f3c9ca61c6953c53c77f60723b3ce20eddd797573149b1a8eafc01ffa2d6200bfff9b7993aef0c260e1e28f0161607eaaca9676ae5ad8956fcc0035b9e08072bcd0a3dc4405958b52505e39ea2697a5b6999d0e4ac545b4ddb00800229aa966d3d9bf46e6813df3e9a77af6c342b696766de3a6ad60107b32b060febdea7e8cdaf6a548c51444aecbbe58acf2674cc8c2e367b01ffad453f65d9d173f885445e9bd54d67ca8eaa79b5b3621e525fe5b739b08d4de425d121636a5891c0f78e1f3d599898ae09a8370f1ac87bd84350635e7013047ad22b106860fbf9a88e1d3cb5e0249192b2daa5ff11e762a090cafd172bf57717e242d98a50e095ee50cfbd5bd2f2ef66297a21ecfdafef66e9bae8673ad66e2bfce58a6b407f54941ed66be7a913e56841783254b9894bae527813903b74e114f628628679034b7f6245f2d07", "ac00acac65", 1, 1641639284, "9d2a18f5a50f6816f54abcf12e64b04a0a086b3adae6b1c98b3bbc9e4b4c5447"], - ["5526363b02207539a5179f292c3c6b5b04deb0c210e76c55fbe05e38d92a29891d05e790820300000004ab006300ffffffffc99aa8fb5b415011e101e533443d030e520ef680931617c71c195e19b8a14dd50000000000ffffffff023a731702000000000100cc7c8f010000000004ab65ab510000000002000000000000000025acad0000000000c2f0ccb56d91f1d17893dc7982921ef048f05f2bcf6a5b91a5c2e42d1a351877a7f9266194ac9a461aab0f23d2137d1650dbb942c18d8b08a368dede4ef62fd8850a235b91411e030d000a60f041e98964960b1541a0be27f60b092bc3a53f35000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009736ecd469493aa81b274004f367e9cb9d0af0a475f5996d80359e5bead13b02d4ee67fe0c2241776d8d4be8a42cf897423590abb81c19f868bbff4f36efb0a28c8392d983951533fc7e64d273015896f53892fd3427149fca84b1029248912f53d07ec1193e1354a82462050f1bc3dcdf813c7a0f6f89e003dc69637141f9a502283ac7c874d38f1e90a794f99f743a0e9e073c2a7ea5714bbded7873d8685da80203c0cecc4ceffd291fb0feed6dd80f3c89a7f8c64fbec08ffeff6ae3f3fe6eda0b2874358b22f646dcf47584f1587e5819adfacf21fe0e791f8f83b8b54d22c0030dd07103201e0f9108637ca0410f6219c1016cc6aea1f050cdb9b30f4d2e53ee020fb37cd018135c47ef183d07087849de3db284dff4cf41676ce6d79b75896e68030a96aa0f9ae1105891341616edf8a977e758bbc4b970d6d18792a062dcfd6ee3030dd86f623bfcdf9683a202a635a19c21e43587b8e059922c8541e3eaa8f3d77f031b38740684cf2b05384221a93a2eb70548e311a207dc5cb73ed91b87029ab2bb020a27d97eec12c614d68198306154306210067e4f2dc55b26c1b6e98592eca4fb45d15a19b8402e9ad51e023316f1797c2eb4162789c4a91505bc1eff2dbd079933efe3544004c60ce661208336e632c3e3728b6efe467e6bbe95836820896f88c5b830c16fafac4241e9ac7075408c791442a64d4d5dde985de46c27ef3cc9e943e1fb7978b15f8a42d15e623e2be6a1c8d57f472f7292f7bf286da361aa4b0701a35c136eecb879a5de93b9c92210e7ac8e546d0a6a15e8985dff8d5051a5a15fcdd0f9f92ac72481be00de734e4825462928f81a0a520a03ece9bf22fbf5f0f118fa02f5192821810a2d5b5ae225c357dc9e6125c53dfe278dd318d7a4a9a1499782c07d7a396d9d6675f140646054b87b91bf38e45fe74f51690f273310514c7adc37b12769e65752a63b5a8c3885248f63f6f21cc375a3963efc213eb09184825c90010b652176fe0888e56be49698e65e5ae3120aa4668b65febae01d89d9a3aff26e0ffe1a68252341579144756920c248e7b4b91f3105b7ef016aef312e7009d738da266862e4ac10c8c003312aa1a9f6fa9571ef99db4d1b4ab3f61441454f85329410999fcd00bfcde0473fd8499d6f29d43e23cfb0cb40927c72ca29ed914b63d98fe3b0ec68d23262478061e6f85c540276275aef0042022251de62dd4426ce0de56d2490204a969076a3597ed5e451224b8205df17eccf51af80dbb411a29dbfd24eeeb5d27c11bfef47c3b651282ad65feff88fcd21ae46cfa046630ee85b1dd4bc79899b9e32fa2dffd009c9e87e24896e9b9ee7a3377c739e36686a496d8b4b6805758779ae61556254af4a741074332417dcc82a89b73209528e44d34108837640578641611a4323d14fd06a33db93e861a1bf88d72e50e53b719a89dc5ca036e058e2a2fe86a3ebbe8c421aedf3689d0de13efef148e821a1618d85cee02741c92ff64cef6c51010fe04e0bad10244ba4374b8c0499ad82dea48620b1a4daae78650466c424f6c56860c2235cc3e510b0e4209851c1246bc9fc453a44ef2803497cc2d1475da1b32fba7c7e25852b4f628c55004c27cb5cfc165d29def914afc0a2fe4e779a4b49d3387e73469c9d63a48f731d6cb655f60cb923aceb41f2ba9a414899f42109f3335273aedf7437fe5153da4a3d3fa9aafdbee9b263cd6ec6b77c6e95d9bf518bf3c5cdd672482083b72b7e6cd59e0f9e71f1019997a23be3c475eb146487a87998ed8186102c86dc1d72abdb3b50eac4371f86a3130848153c59bc2e5b1fa4d11c4c3a497be063167aaadfbe6b81e883765fe2f0dbd4784211de1ea7bbb7673a264e17317d2255803bc9513cbb63b846802a370e82b0313b3ba7358eb2d107697c70c990eac6e46fb77c9627b8aa4770b158b610f2a97892d8f5620c9b52dfce7e9de215845448acc8f86da8183601da4796a0b347b3942afbd01c08ab0cbce0b950a65e911ff7fdc6013a4cc06a565f89bce90a395ffe308fbde9b1760a4e26fd6a91c641f98360212c638f5f36692557d634ee86b8f6e144a2961fd74fa650553e58fbdbea376d5009f8a7b91779cd405f0a03e597f4bb44cc5896164c6c7ecdaa7969deccd47d85d0dceb358fc2aa87c871ecfbbee6a3267d0e07d8407a147e7147fad3983dec8e4bbb6cf2c95fa9fec33b7266450b03ef9a53e766ac0a0b696dbcb493226bc9b4cc28e3e3e3b07b2e32427f56d6fe02ec95f45dc9a3d519cf3e00000000000000004057a004000000006f8f0d2dd5ab8233e3deb9effa58757d69f0ec390426fa06a11de6098beee9ea912ef902827c191ca8324aaed4ec9948c76cdbd91fa1db72fee428a28ab32a20171bd13dbdd2a134785ed6e095cd53673fcb2fc5bc3f851f369f4fe7506d211b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d2ef009022d1d022a2f8dfe875499efef58ba3a039aa45b97dd973eb010cfd2f0bda6b7336e846482dd99dd2b9141aac08bcfaccfdfe12053af77522617eadb7316dc33b50a09d029cba7a2f2be2631df3926e6541102c1cf55199daecf3bfe38f975f1677cc6a2a7ade33e24b244ea3c63ec85d54935f3f196ecee691d5871e02093e0a09e91624a6077c4f6f7646b460500e8d906fae440ff2a9fad536544750021dc494f451c984306b4494fb39fcb801fc3034f630f2f4a28a3bcd082fe241760b1532bcd9a0508f98fded2a02bc0b2c8475c1efdd85a09f63288125fb6f1fa1d008fb20b56d2ad5f249075cbb2d0fea0d61635defa6490298e52d060589c4e7d0021c7bdbf03382a417b85e2d1be0e6b0fd7c4fb6cbd41cbd4659a3464740b133fe031e4181297fc899a9ab43c08bb9023e8e1057b1bdc0275dda41b020fe360942eb0222a0f03e2d97a5a1a7d4631d1a6f9456f7e08d9be1108b2b4f1e58109f6dcc93032d2c65cec6964626937896a1c1f7c15a0a036429e6cc97126c55f9550a0c6b4103024361e6bc8df921089c6631f65d1783ef621963d156be394f01df54c721bf3502d06adf8096f87134326c698b5c131ac2b87b5d80fce3bd8fcc74fcf77bee563bc75579dea6aee2fa4b25827bf14155211396cb7730e4a8dcae92980b5905899adf2a1e985d46522c33c3fcb19dd76e565b889694795fe342b325b37befa8a59c5226d212aac3f56dfee56499cb2c613a6af98a4d24aa791430606d0cd2e1f90004aa7571cae53daaa8b782c702813945581d536162b054da3b18be206ed7ffd8ff63aa38eb4a6032650aab892cba38db534e68f92f6ac44cecac1c4c00f6067cb114ff0f02b77b215446d62c2befbea645fb824ac18fdce97581f69c02050312861c09ecdf9fcf3eacb943963e19d4b10283fee919b7fc9b29f8e0191a90f2869cef0628f6af8ed5c3e3a38629bd12732a35234a14fa138181dd96dfc8a5868d4120564ef1cad8f18f6ebd308115422d4c8efa03c583880adb01bee8905e3c5d68bfe40f88c5b2c116530264b58c9c1af32691eb6dfa75aea7048b4313f529b05f5e05b80d67c544acdfa1316ef9ae51bf7bca9267803bc6d5879d0c507b583966d76ad9b3760db9e74907a203d08cfa96e9528ad036fed55ea5536479290361780821f30c0c29cb5d21715b07d15a10533f27eaaa5bbc517ab07b4b0a4abe785f5e1bb1bde454a826c626f3f0d7202b65be539b3920e5e438241f30015bb472be95d6488ee7e799942007b78e13d1d1917e255fa954b215f57524984e4accd12e354bfb0a1953b68732d7cd64d79706b378f535a25f8c2da0a21255ee4f1a6e4b6a30a55dd1fe2e9fd7a24aef9f40100cb930a62568ead6753118922aa9c127e93f333af8a7c74624ac303ceb215e76e0458e8c9facfbbf5e61614daa6aa143fa1d70d916b23407c032032490558558a20442714f2f09a79c02cb4acc9be76b312729fe12b1fd0b424175b81f5bc79ed67cff14a17d264b4d371b957ce22b246c427ed0ba5f32cac6a4d5bad7b6e201afc5e59e8a6895c28fdd1591770553d2b90515261c69ae018883922316a4dce211d794163d050c3869ebfa1b37b9b525768e5a01313f367f6c456d83995cfa23566ddcd78d110e9866b2ddeb3a0e26c7c560fa635894cba77dc422fdb636151361c723ae2ff9458bd583007e26a692175d6d522f7f52f4047579f5b2b2c970b7059d72a63c10f98c8d21f0bacc78c7cfd120af6c1748372f80bae8924430e79e4bc03ae32ac80ba0fabae9fad13afea0d587e630e7a6dd8b54a5b3f7ee6b10696b2f8e1d20e95569543dd1263d8b88e1c14e94bf8b675513c33f63a823a02ecd5e17d3df8863d61eeb37d2d21b7f7eb3afa2a49bb7d45150cc8ffbc081d20ad6f6da4be259abeae68ed3541148820af709b6f3163bcefbb4e3571caa8d13569ba751e4a0f04408a46cc6dac51544e6ef13c234ac33848e6aeb660630d540a525ed87251489698343e278daea6a4655567c33c245b6e8f2ae336859d59359fde81b77096896b7b2d3a152c0de84ad01d2d6cf0e021b28f5b7c5cd3c669f8ffe06d6aa224d5e43d7f49bf5d7306cda15cc401f554d0d2bc4733e9ef01c600542bb92e93bc3cc78ba09ece5f7d0dbe27d2dfdd8d170f5cef328f269b279c84b51f90226a0cf8118997de6a8c384c58484b36597330f38e2f69d019be136c4f8dd224b81004672a6d7bac35f62ad7c0c79dec02b2e69c6c6bfc883481150c5f4b4714dc7948569655a307328ee88597e0460ec97022ea828f9d722ffb304848e20e283a7213c1a191d15d08ccd97c8e205193215ca07d4c016bab8d6f6918b16915b8cdb4b3d0fc8202612e4787bb3272cdbcea247c16fb7f87aace742f7e409ed4e01", "63", 1, 960838316, "7f089987164ad3f5fbd9f1625ff59ff41182322e8c1f5270d38db54987ed5185"], - ["9bac7f9e030b2bbcd8cb7a5770477cc8b22267ca39e84c25bf41c765a5ef7981028d52fbd4000000000300ac00ffffffffefb24d6e766fc4738adf6b29e9da8611d5183aa8bc1c8bd233c4dff18d2488d10000000005636551536affffffff7b88da37333f75c6d25b003830f1d4985ee9fe66e68a18e2224baefa75ac818f020000000251009d1a0f4f04b04e39020000000005526551ab0044c081000000000003ab6565f2e4b300000000000963650051006365526ab375e30300000000046a51515200000000", "ac", 2, -1086449835, "9a7cb056228fd8a5c4649dce58ac942efc79961cab6834fbf4c1d17087ca311e"], - ["5358e1970428ef726cf86e705c4e163da8a74505b779f89607b1ff5220841a2766bd9b6f5b030000000152ffffffff29b333f03a03841b9e5a61c79a318c9f24978dc2fc812d075915ba63016e6c4f0100000007650052ac5151637f73db8f9f115c7b4d6954e5edc5b76e68b8e4205814a63db9efb92f657539b0ad0e4e830200000007acab52abac6500ffffffff96121f2a8ab243f94bf36cac375e5adb73f94d206d8c14db4ea1382dcfae1773020000000863ab65ac526351522742635003da1a25000000000007ac0051ac65ab63ebd74501000000000265ac1f3900040000000007525200635300ab04bb0250", "65005265", 0, 551506993, "5b2914174ec9ccc43daf416bfa1986d811c69a00c2e2ac831bea774c9090ac1f"], - ["364148b50232d66d22bb4c5bd1ff075a1c5b6858957dd028f444de125a7f75b6431dd5bfab0300000003656353ffffffffa652229ab35b609f08b0dc29ac15b600ba45c1b41ae0449b2d463bd97dc00b4d020000000451515200ffffffff02daa301050000000005ac000052ab696dfb000000000001ab00000000", "5352", 0, -1999539600, "c754a37ffd5465ab6b041f5c31941237b783f62c70ce09bd9513d6a8351b8090"], - ["148802ca01502802c7bae59f87102b3ab620636f8ad5ff45444ebd8114c11ba71acd1b9c9201000000036aac631c3bf57104d4f1f8040000000000aa86b603000000000500635200ab2527b80000000000076552ab63006a00d287d9020000000000ec2472f0", "6a5363", 0, 2111177408, "0f6a9b49933131fbf3a0c23bd9e21293af9a160d09d3ff26db1d3892e2dcede3"], - ["e48ddceb0100469ab989efe004280d671e42a12fceb7e40c4fa6c6d537f5e92afe61f55d8e030000000800acac65abac6363ffffffff045c10d503000000000352acac438ede050000000000645b8503000000000551abac6a659476c1040000000002526500000000", "5352", 0, -1542168280, "da74be9b9a2b04dfc172a01c3cb4e0d08a019f5cc722f9e6b2b018dcee8a9809"], - ["c6504740016fb606efcb4cf94898bf3829e76a1496d02794fddc4f6b3b45363432889b3cdf00000000026363969d56e10197f23e00000000000453650063f5e64e970352da55000000000000000000000000003d71e0209ab867ab1ae99403d8979e4da7092b44312ce4246500074c88e1be49c55ce0077ddf5525bcc482d9ed9b535d3396da364683f2b51d7a2e2f42d15140cfb5dc200011b31d62449e0f48a95102930f874d5fb5ef6c47226da8c63d780c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f5b934aae6a4cf6fc51c197d036dfe7935c8150f8641f89139e8c69994258cd6e9fa8c5c84a506045cc10d4bd5b79c490fe4dbf4b1b2e4a4f82a5313abc980437c10e9d2af4a8263d7980e024db244368cbf22ce38b9fa61e23248663f823ff7ebab3f9bdd701c0c3e211d480a3aaebef5ed07513f2d17d79b0fdd73b2108a9032ad7714af6cc8e5561cbb910e31840754524f4038e843d6b90e30dcb7fbac44803180bca320bfe88ac9af382bd4c43d3dbdca7dc69a83de56e1d21e19147c11c9d0a06cf297cfac0337c96386416f10a8908d3190f4654b6582ba79bd4669c8c8c5103099fa23861f4b99c342c66ece8cc58bd7523a313865f42a7efe7835166ab6e02006d3dd53710721539de0c3170cc4aa5e27462426b2622ccfaf629b08d6ce596021b228df19ce5ef9615c2de90e5284e85e9d67c5ec19d22f9a8000ff4b8cd60b20305f0e6a86cf22f2f823b9e583833f16d798e677590da0c6339ab00e042d2fd600321d5adaf0bea96ebdf75a81e98f41c5d9603ed832d13ef210922e373be56e3b7031af15eb5886cbda69745cbdc3b5e719262451391f6dbfee1ab4a5cd5d234ce1f9bd77f87416a2384a4001c5e9490e907a4de087d0a57cdb24f4e4320d88404eb06ff2e8dc9504232c108c228d9c1e43c1300778fda465842a11fff264e0b62a0e18b3d30a6e7c4e28e58c751f3c75c962946e63e1b52c08944a63cdc1a13e70c628038b0f251f959a9962d9470c1c032c980065f2aae719b086fc12f4927abec273c10d65d08562aa6727456f9a507240582fc11e041fbf7b1edcfc4f04c850d937e743882f7521ce3b912ad4785999d1ab901b00f11d9ea5ffa40ae6013fc67c910cdac4968645e05e5379998eb3be3c558a04dc8d5ea5d7d81a9040dbfac50a2f40120281f5b5d64a9a5be228e8389f8bd630d0ceafb67c3b11a391118871e5d2b9fbab5182e65959719d53d64696b4b46669ec81df24be2bfc418aa24f447997650a3a6d3a2781715d6fa445605db762ab865bf57cb10542eaddcdcb15fab27315259ae4ed97175bb9e28b2c59353b4d79ff2e33c35d4060f241223374b9c20c92b3aea0290d33577c81484b83956d7838d1938af7b5d4d97987c44fb1f7124ff36bc85634b7e4ee36d9242bc91da00134934ac40f4ea4ca4241e0bf3431f85beec3bbc612dd5948bfb5509318dce2f6430c65bb7e1f01919c3ea7b657b81cfa9d7167d2caff8a878622825bb99371c50eaafc87f492999e082afa64b51fd34c653f84c2b70722c1ae20d66da171d484e74cdad251606af9ab4a5c640363e2437b2fb5b3c8fcecffe6dac6481fb85a406620c351e6716ceb91ecf14af07951120563679f8c3bd418842738eb12a94c245100614fc7f4c4b044cdc983c8588a10d08c9f7525674a2e6253c00eac9cda450f9676b3be731f20deeeefa8792fa6b6938c036e0d283bcb965c3f2fdd30fa3a1dcae15db2d09ac56f99142f063efb765d374a4435c5b9433644410d4105bfe3742071d8f58d1d9739906f920fb817d4e65d2f85d2a6423fc693db4e456da597e3607b8960fe39623ecb67319a298adfdc51cac58726bac88995286e09abff241b888450d624fc2a098a3503920eaf85163152797a5aea0ecb824f2d01424e4ccc832a819c50b45347a6adeef44222995aa3f88e36408290929cb945071746dcb96cdb45a4532ab609054aabeb01097b17ffe1658302d7c12d3ca2dcfda6fcc92d6331c41720123481f0d8fe04ae69fb0e97b5153ac809475198a3a99ec4b7b99e099f2436ddcc596c414206748af51d60c792a37b0cc5acd6e79adf8b576952036c289e24046251f622c85fa20b22b2f25631c9fa6564348b2db192ea51ef562ba45320ad6d977f3ff3e6ea209954b2bb974b3303f36137470298246b223f5aa5c036aed4f57ac23e0a04f4acd7345f98de6cdbd74353723602f69186a505d3482b51eb2403e6a2a1d4ec606626c2b4cc2f86b044923264ef1f1548b3bae2a3ed59611eec0cfe1a8a77d5312dcebcd20a6edee208049d4d2d0dabea12bfc8ef9487bb3091edb9285c2683dbb4bdd8db21d397f94238f4909ee818fed43fd415cf0d2e3f88ef5a6e44c19c02dd7e0979e382c204bf10b2fe90a5f0a3c4a5dc1a23e42767228fa5d4d462de098421d826b4a451030954147e786940fff8344fe03e87e32deaaafb09b97ee3f1eac121aac557124ab9984b64db315a6e51b380ffb9403c834741b9d037da45195f93c7ae5f2d0454611394c8ee38ed2b89fbc6aadaf1438030000000000000000000000003a5a935336ebf09f809652d2c24ca5e9a8a1892ccce1890de760e8e474ca1e08f8399b1397fad7ee3581f4386b30431307b2db1170daab394017cbd0c2e949338d75092c1fdc6a7b457b0fe481e67e3ede9ccb419d3370d1a914886688ae7caf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e2bc005eb4038747aed27933d48c1207ac30f0603ef9c0800820de281438e39120061386f1197db87531f2472fa308da52f3c02f66fff25e7abccdd723a61c0793679e799c11713d1b185a8ab45b2df0a58a6846735ab4231f6f845e3abcbd4b0fa0cefa62837ea8e23439f9d04de6d46c79e7c0560d66f200ba4425ab7086d1032230e415b50f4012b3bccdd0ff87a62b9c742b18532f2860b9df641408d5900f02274387adfba5beb62b3e32e32a87dba1cb20ac4483a8707a223dc0d596ac52680b1c60980c9efc10a9231bfc223909c37a38b77d15036ac0d13093e96914b77bf91c07e01b1f33834844543b1821380ec210a42678b7a3b06cbf1dee2f03aac15f022aac205a0b77648911c7d8475e1834715c18249ea7a9eb9da564d7c3e3ebd460021085295d3ffb71dff34255ea4364248fcc0a05003cbce7aa0bceb0244872ac3903241fbbbf93d57b8f3ef6141a50e40ce15e26428036155a84fee5c3cdeb38f311022ebf16eb52a1ceafd1cbeaddeec868e88f3db7e6608dccb1859e24db323abe39022f50851fa756a0a75f0ba921454b9a717a4e249b9516bf6ad8173792d041ad50e5eb1b2d2cbe5525e4e1c7818be470916251a9e95202892e0351b4dd2c5dfa765c71310d8412fb9473cd939cd1c07605c53342d167c2aa49daa7c7c702e8641c7606273d5dcab10ca42253ad560ffc1e19a5cdd0388f144a65c9defc0fdaa8861f42933aeef6bef1726d7a8c171d1fc09d075356ba8064324700bfb1fd4b897d98cfbd585384c465bf690e6a5ccda7e64519c8791f96600db0428712c6e417364b7179114aac76159e49e0fe0c59365fbfde2eb8d447d302bb8947b70c9d5fe05a270175a898e88bdeca5c9192795f8e818cec7ab99cbd7f5a3785bb643105ad47b60037eb1f36c4fd29501362c29fd40e90f61879b7aa6f89bd45a84a7e0b57443370ae0cafce14bce5cf5b8c4bd9c0fd8c05a721904444cb7d6512f125369d2e3822da83a9964773a7f23cba5ecd6a63a08832e561fdb962dcf4ea7da80958751837765b2db2f43702da6f0eb43acb7e1ca678014f642f08282df0539a3bfafdd30596f5e65f3aeeba9d185f6e881c7ba2c6b5d82daaf6bfad6f844f58dd84f0b36dd231dd6972d5c5fb59c477a0a0c3298bb37bb007ffd6fbbb30202c6c401bec220fba023b5a6f0a4efa67d4672cfa30f2dfaa366b0cb159d60c919cfb527080471c03b3ad148596352b4878c3f175c3eb5316da85e2f6e39858b3495265d9fca8584bb08d698e5f3e66019f4356db609586970a3e4ff5db2411b8f687a3c5d20d78b075402bad7a1bccc3fd8c89fd3b261c8b98cdd94551ddd1a306b82197d05cfde1ef70494638fb0db768e6e5f8e974d5bda7b09a52b561c5f20a2d64b385f877abaaebe158e95cb4377bf53cd4f69339b0d5cc3d77c61d79616d55e89b85465555f4ee934055e6ad2c6e172f0b154a4c464c4072a2dc23a713b5de42a84511d956735c05dfaef5b833091500833d49f24632d651fe872ab426ce0da00ed6a633644812cdbc10de53c0c0fb89056eaad9e444c0814766b451c7c7062367a6dd440117c8a4a2edaaa2940f798cd631aa447d806d622f9ba38226ab8b826efc46fca361168c434c83f1e3e36b392e33700a0daf2636548a9fada33e8696cedce851c71ceec096f1862d296663814b39c0449cf6fe6a5edc6071066a5ba623a7de49d40eb508dda5e970ab482848dfbdd6ce32fe83df71e7d19aa4de1e941fd01bb59d62b3382121916f901a07a9ec285a601bf8ba5ca20cde8aa3b02f8dadd013a32a2fdeacec5285337c4d49f9ed3dcbaa311aea901f92ff5fa87e9b2ccc220c3bca897ebdef4ad02312bb1300e19e831f33552260d3090caf28493e6879dc1808bfb33c0b303b18d5e2fbb6c1bcb50977e53fd6cd7733696d686b565660947e1a73a85e2eb5ffb9393811e4d349f7672d05d24f086acbf651cd9976f9435b46e31bd64c5e83db61ebb9af3396a0c3e3c63f110764449203996f101a90b8e52f8f5ac8133e80e4bab964f9c8068f39b2a74e6bad5b23c431f7c4467f100808abe7541e9248b22057467902d7c3d7961437027b2a820e8f7f4399d8ba59a61f06d4b3c2063628df594cce22ef8f539068ce0fff67f6597b883c9174219875c3baceed2ae2b146251e2952a7902c655a4939b442af425b4ea370f57401db1a545e1cd761c6461d84b500599783c4b7f9331fca3a62c8c1360d855765a5a14084beef62687e14fb5c816963fde2e6ae530000000000000000e9057a0200000000df4c54642565d4e94dc6fd69c1e1fc86d09a8aa9b6a257c38b4be18fb3f65d70948826ef36b22352015912a8a1c1b6682e49f56c6afc7fdabbd2fb4e666218e3c6ea04d27616932dcfa6129601eed83a9ba7995952098e62e66adaaee1372d700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000052fde838098b434a8858a335614c14127330589d3805413929a4a555634938c145fb4b190fb6b1aaa5eb465bcbb6d958023c0ff32061e66d87d31ef4bdcafdbdf6b5ec5cb0e913d4080207d862cedb4d829d94f3829598b5a4cbaf5e11f764b5f10cd37aed2b0168cefcdf64a26a115232c9486b1ce2c4f927a13752b60ee9220311becb4ebaa1e190effaffa02b24cd1299ca52d2486af9640620be27768286db0303a5dcdd1dbbd30e2846f56edeec5615672aa35b62bd055e373c7c9c0b4604ff0b2071f12924f2d9c3c70085af0037c9495ed9b51e21832e6998dbe4f0fd56c5941eb36652c7499f1d897733ae156c429fb786d87ff8f5e0a0dd025a518e6ad237020d0bd4930c827891979ded4e7a1b42b0973edc223225c9646ed780b2dbc54c8f0315d338c6d62edb42001e98b47b5e31e0276272505a6571abd168f586e451725a02076eacd727a5f5f514ae0caa1819a7a0ea0a0ae6d5b3d7d4d044ca7e3b68be19022114b46e2680aba4753ebe3af1815d6d1e5707f11f9b4e32ef0ae6413ac58a15031f2c87dd2b5f9558ce8ca6255f0a3dfca3d86322ea4ff6964cd715201bfc7e2d12808373ae732ffcb11cb9fd120ed583d43a2719f510c2a01c41f308e4a70c572454431d1b090a0f4428b9c72cb4ed9f3c71bbb786b76e3f617f98316076fda943498d879a280930c7c8fe44dcdba36287e8444300d02ad78b76fafcd993947962ba713691b84bf756b3e132a508b93b3c4a014d455e81157c1775284d9ad0c1d7498c7e864e5699b97f58eb6cb054edc911538c7cb15c6fa80c8fea37c21f597cde8779d5da0c059d1ffe2e08e815c465710177f2b395fa0f82fdecd5765ab41b1d21a9eb0749f51a7c2bd50eea16825dd3d4fb59ee12f014cfe6ea220322c588eef92090355ecc56b02a1fa3878634cbdb84aad371521bd62a2e2b038eaeaef353eb5f4501b0c9e79e7f3487fd1ba508fad784055dd90e9d91fcb8dfc23c879fab9c582e1373aa6016d83a533946624dc67539f0388c8360334bc330904de3ed7073a105c3949ed6d346f242fdc600e229f889b809c7da88a7fbb4c0e88187c90b09bd212c3619c9a5141d20f1a16bdbff6358857ca7a6b55397f867b8502af97f29bf276ad489df2349d106786f55a9be04570b4b6828f6fc7d03bf526cf29456f1326b79478305bfab078f43ad71b1fc7a14b9255f4af29327d839ce2e876300096e3e9f84346c4e99b10b33bc633530897acd4ab532164e9a3cf9d8cc4efd564be46bd0b74d1a22760d5268f6af5bac2235412f0d6ce7aca0d1ff19590b476d2a87933c8a056e85bd17b5f3d78cf023e9e08c9a796ae0573e00bf6e0d547691ad249ea12cd85fc190cba9398fd021f2ddf28e221de8ff5002654cf3ea04f4a9009e05164f47326ac3754e8441639ca4dd5dd87c83cd70277bb2255cef936559c81c48e14424132b90e77f40d03eeac0efa5f3afaffc39393468c66a432a85c771660627e44376dbf5f06b49bc7813498443c8fbd75da3d4ccc73d60284709a423c5e8a789b11cffa3b0121566c4fcda4e87501a3b82f75f02eaae5fc055b5b65b625d2c494f2b1237a1e68c6b70c0e2d513d0f1dc009cd0d214042c1ccc2c816eb561cb6e29e5dbef60351db4b4a7c6154d8310263d950d4e2a30c1df4817d2686062e4b0399abcbdbdab32f73168d010827791cae05a4db9b41f17f0512bc7fdb3f763463597022179cf203b82592b1f86357fcae7ab294fd90a63d85515258e95acc80186ec5bbfe6f5bf0177161d92cf6670bd615c011b1b77b0820855420d448a6391ff158460d0b793e87e948bd62d8682ae3b4eef51e3f7a2a8fe41c7bf0f346f2736906d993c2027ad98edb70eaec609c1ebedec22fa149cae1159bc84dd0a0dbd8f44b3cf7478575ab0cfaf7e882ed8d314ab2e7a7dedc0fdc61e39fe4c08c7287ef98e1ddc5b22806990433e5273f47ca9b48324b97f8b0d4f927c36eb8410aac6c0d49de6614ee9f85d126246a8b665149dd295240d91cd5066594dfa951856dc7c213eb92237cbc899a179ce133d3261adb9d8c15afd94223bbbcb1107c7939849fc0ba9b55fcf71b9702990ab7a2233124f389f188edb19261dc57b25aad58faec5508d018eb000892880fcc8f5546357ac6517f4add7d5c9d37e090b59a48e1e89430c3a7317a0a5bbb974efe960203cdaa798d1808e54a0a3fceaee3bb89da21312c5732d54d58d26e353eb0a7968db30b6ff4d386c2b9e8bc1b6f8d9d55e771c96123cb03c355f6e4ee8757df2d886d3f6e11f83fb95b99c3b71caf49518fd10f8e0f4c743abd6c222b152e43fe4fff0d100144abda47313c2d3bc65e1c5b4bc93815ec3e89a82fc9e8ebeb803aef3b1384d549b25cf2b937cc83cd2a7c123022836106d600dfa0b", "", 0, -1841663445, "7a170d5edd5a205d4cc689d5dfe1f6bb014c4dc81548d2af72a8e27603560344"], - ["032a494f01ab7c151fe33ad7d81c789709bfe8fd863cf01299ec1cd014d154b917b93180520200000006516a6a630052ffffffff0118574c0300000000080053ab5263630063f8f5877c020000000000000000fcdf5d000000000026bc5f42248feefb4311e3419b69662fd18223bfdae341b751b4c560443129bd6cf6b22c3f3b23a2e06782adc9f73a7c4770b2557dc848d7d62e57e60364b9647705ff6b1537baf1d2334346cb2b781bac899ec2b4b5e08ae2ef207f70c28bc7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002fa8c402aa46ab269bf22756d10b3ce60cdab6a67bbc184ea5c9a8e55da4122c94b31d19d7f77915b75ab38121fafc1bfe4e797271eff7b61fd0db9abbceed99e6dd304e571403e39308123c43fe422d599106d3280b0cf3c9cbadcbc9ff69372c6a7206048c4bbd2cabab839d362b3c7026b65308036b5de835296bd4438087020196acca6050717a726506e98a48b850f9b507cac564111a9bf2f4fa025d49b40225cfaaf82522edbc99c564a5771890acd438fda508855cc82b8e1a8c13f00bb10a2a5ea60471279eb15e0cefe8af1de6b0a6bf6792efb3aa67e50c12c196324df01ffd5a072264705496a400c633f8353db74db221373fdd14358cd07a6b8e1bae02239891623e089f4d902f3df5e39e0d7f48cc2c2cda0804db4da96fea9cfcb7710323211c11255fba1ff08ef70ea00456722a3de78cf09838c32b74c425211a66c3022e4be9b305e08e59bff8f4e069a83f54a71d9f9f171cfe254c3bff103bf163a8022340b7bd5f23b51acb046edf3c8ec35468c3ffff0b55c139a39c8af13e44a8d502167fe7cee56a94f1f8ee5638a5f77e980848e69ce63eb9101b0ecc0b43ae70dc765059f96dccf8cfc842b5399af375c7002b8aeccf9c1834d00e0057385a3ca60cca51b92ea32fac8a8a960f42cc870b60b63f41f49a66655594922a8655314fd448eb7caf4e17a64f018ea1b8fb4cba9f8228d2767d9443ba88c3d8238f4e4376fbb2c4e0c2d4b49a1029ab72f8cbfaf3d5caf3baa8091c3ee391020fbd30a7e0a6dd44088db0dd90502a7b3afe63ae118d1213bd770fc6ad972f35123da2b6b38c2af3b78720bfd3641141afa68b0e07a96919897fb79b0967ace6c5e386b1c691c1239924133d01f3ddbda81bfe9fccea9c27b6f8b9716faa0752280300f18edc5dda145aeb6e7d3ca0922fba755abb452f297b8e8ccba8c4d57c30ead6c14e83f69cbbfa013b74ed1a861ae48067751670e01739e78ccced2ecaedf6e2ca49a2f158b0ce42c454c45c772a2cf2b16fd0dfa27a9e0a5a011124f87dbdc2dea54968bff316b82d173009fadcacc93114471632eea032e65df89fed1d3d6ab8db177df1dded3c67de8fbaec971a27c7040b38fd2f479ee93175c089e6db30ee203ac6eb847d9cf72a7590258c2ea8d3fd9fd903d57ac805ed539020986a090c073f980849e940030eb392da89c2945c2d2df1730755e79c864d478fa6d25020910843a80a6ba5eb8cb1fb3bfbaedc6ed960d0dc0771f1ffcd278d760ba672fdf0db1bd46683d0c7956e9bad819fdeecb2bb3d86f3d88c0199e80ddd2e78fc1edd23be405b35c2f3f41e0f0dc98c6f4c8fc996737529d8cd103c7db864d6826ad685bbf81a7aedcd653bde576654f241787ae82b1dbb5f01b0e41ce10e78a40dcff2a6eeb8681dc80701b9b6bc7cd45e60dc5b56a952f2fc22b6aff0e7ee0a96734303e6892b5b2804b611a7fd004fec8f30bb98075c833bbf44baa8e59234410e81bdc61c9e092d59a74a810d834df8b84308cd06829fb28d395bec56b4a6ffc5c84681d6623d047251df71bf719f8d22c64e924500995c1eee22eff3db50ec4a21b4edd9e79fb8ad0f182e3eddc1da1cf958e5570bfe01d723d4215e0456e59d00e9e0603f7cef2dc85f142161233d9022195e7a8e9e632f5d5cafdf4c5ee8cbeeac927bcc5bdf7ceccfc418db50bce4f80976a7b9d0d56d701236a3c76f8d1b1f56c9c73cf9c2010ac8a8835cddb212b93db76b035fa134ab04960ef493e6c63a504ceb782234e02115dde458a029c0f6fbe35a6c54f17b2a1b1fa744b56fa7672f80050ccb20c3324bf75b46ea43ae0057cf3a38ea7141477469c3d6842727a08e420e66ddfd61d72c5775b53201428f64263350200740f83b32f45ee18b22249eb02a9ca21d05c6e48ef45d973518cd296d2aa6d1e5e49b23b182397284c869da31582dba7d6ae724ac405bf0b9d61f10dde367d9f20c562d52ec76d210ffec6dc4cf5b01ca6f1b441bff23a488dc5a778c4cdbc83b9ee6b95050307696a8f738092c9ba005baa35347c9f906a7d06e3fd57afd776a785ef2e69e914d5d2672cbf188b74841daf611413e1a8a26e4b3e1a4ea11eb992e25ae444186deb9424178d9652d9a0fbe72dc730735b1ae4422bbbd272eafc1bddef2b75a6d4a555b25d9002a9863ce1a30c6b2ecd92decb6ebd07665a889e8ebddba0b90dde0f6988ebd229e2f56a8529cd8b902984528596523eda40bc9cfd5e19bf9b944f0114566414bd1687150f6964006e1535ec0782b25d85c020000000000000000000000008bb8bdb4ab1b5cfaa19a54f0656942168c38b149d2ed6114938a94751715e0330add409d89cbed4eebe4a66ec10bdfe30f1b33381f521bcb592a6b3a176642a05c68b9f61e72ad30b4686cae2c123da77fbb31a123ca75d10fc4d592267ef581000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003c80e01cc6f6003c88509e3eeb69739b3f791cec3df1c6d934c5ffc1c0474b84d50c72dcc122b410789eec2040066bd92de378c4d815fe5bb55772bebf0355aaf6f502733f0c34829c5dac11b72cc737087a85b52e9824e685e4c239288babbec41934b88398da8210907a6a22358e45080aa9745f36bab9f88e3753124ec1810227c4902e86b2d1e0decff4047997af566010ee4dae7f3482e204688c3080590f0327b24ba99dcc8fcffdd92865134a7aa47a8d99a8f851b467e78f90bd35de2a630b0323bbebefee0a6ba0c2f7bbef1818fcdb98832eaf48bc3789c93dd936b7901c218baa6f08870362b960aa034c07c084b347f2b5ae013e9f0e6899d9ebbe54e8032d519b65fe1b30759d7661eb8d7f8d9d082fcd5798125087608308398acc8f830213fe1fb091e6f363e47306823720c22629f7bed16c8c097ef093573027d2602802235157a2b12748bb299bb85ff6b6f0f509c402c07a163c4111e88f31aec1683b021cced7867dcdd63cb66ba2a3bd6ed58197ceba7f22c62c952c017036e81964a20214e05e06e69a585a6089419eaf7d52683e2bae8ba344879d0568a09f3532f5e7021ae0f9e931a220c68d5d7f6a8a4f121dae7954656a09be94a29fbe3ad71c0cda67ae70f9b5510f15f81ed6e18a770deb38f5f549fcc92aa731f42d522ea70f70842edfc686a68c03d84236564e014fa7205dbc343dd0576768704b408b875d26641c8d4b8683761f513a951528f6a583eb173424fffa4860167b370b5b23152c2b63b55e77d33692b6949d75cb5f101351ea25c23ca432bca7ff9788eafe85cfa15b5a3b7d176b3f076492ecc4b78506945b8cfe48ebd3ad092a764fbf6ba0d13375887e5b23fa9e412b7bf9b1a5fe65b8ec89d8caf6f61e2957f6644fd19c5ad75e9785faa1cf32a72ca1ab00d856bccaebee64022a61a9224e94940a2ab31203510db397d763144629e3369a1f3964d4dd1fde8b1ce04ceb6b23044677514258e6a2a30f53a323dbd7ded403cc059b895615f29551396b20abcf127110932aed675797b0234c1150151c6190d07b17f7c69d92fc310ac13382583a6cee845262094a6b292ba4443fdc16a2e5133c451cd650693cefd701199cbede8270d14689ae5ac4d50327943a58c933b5f75e39d0fdefb2f680a1131e7038d9b12130aa7cfb18bec358c097351b1eefc7a03d63e73f26795f33c13c066f2bbfca2c64511a64a489c3d84131d160ea51564f421ae48c3fa13eb16438b431da3523c6be7abd697ef582b2f175678fff221e76a1e0afc3fce3e004b8c0b657ae4eb3aa4fe171b90f1051ee64b29002246a0be39afbf6579b5085d0e792ac1d8414280d02cb960ca1ed8defd272a6c82d8bd3046706f09bdfa2fa0d2d5d78b842560bf4f7095b22b1b32d94bc2d25997c922e50784ae66ab4d287f32e867631a8ce2579c478deb494005e20e2dd6becdd0ed7720438f50fb6244adadf537be00e136561a0f36ce65cb8801910676be06b278bf752ccda3ae19515329e49e90823dedcc1b4be4b5d130f664b40adaaa8ce97b705016358b52aeca221a289c848e19f798cc54de3f981a69d6cb2ea41b22834ee34a6fee5c261e872e69b62fb0c222297cf78fd3c80d76cf5c024f06931bb4bd18910edd79595ed533a897e3ec8677641e7a7a4e63704e2b1bc39f404235c6aef9ee7fb3fa1b756c53be4b49a2ac3c82e4d1d7831aeb3e6b5b670a82a7beaf47dc65cd35a7f3b25166479cbfff60aea1222e589a1d1449ec19d97a272655651e0f58b8423d252f486637ccfab66e4db1ccf1d2ed4a4d5041f78b06890f370ed3d57a8232bc050b485cf8570f78eaa6229385cc13df2c9105227ca5825fedee8238be8151310b67d91db7f02400f501a4bc4a5fa186228037fde65363982d9ace513a41770525da7c5d6c4385e0004b662364bc5312440a2f2fe21125ebfbe6140905512bd8232ec3549fd166772fb390788e94607d93bf3f001116cac64de0008949daa981e67a657e24b73b5c1f0c10777210ab072b78e90bfd878be48fd4045ac219b6ef72ac016ba14db4479bf9d49c970aabea88fbeda70f002801284f6451fee852ce1b2d406c0db388ea448fa7ff67f41173b208237c5aa791a4f1c226671f1b387f598a45ba493cb65f672d86381aa1dc84b71573930817c1bbdfb4f32d08952afb04ab3f3490f068e4f4a5f0c071f319c906ca3dceac7d93908cb08d4c083f01a19fe809328fa4d4a30e4eb09a36052589ea109ce9a897bddd5c5653566a0d383312c8f5711d799a79032e38ba1ac578e03b3a62786f58e24798bfc43d5edf04b877d7603e297690c72a40974fdec71bc0313309d51f9f6109c4dcbc0f04018fd690c8e8e7491551f94b27b14d290bdd73ebd48a45896f64d6663b61ef78df00e", "000053", 0, 778527675, "60328390b953a2aec3361977a080191325a490521a11420bb49bf20f6f7e09b7"], - ["ea9d4b84015e476ac6d168f5bdf6ba3e1bea23bcb4f8433009cf7822ff59ecd44e51aea8f702000000045352636ac5c26a4702de0c9003000000000600656a63ab63619ec9000000000003ac655100000000", "65ac65535265ac6a", 0, -1916090835, "c4e635b1a3308524cd77ebdfb16f0232ea7c863015b9b80f578e5bc8472650cd"], - ["cc1aa1f101ece3501b2731333430c171cf329554ef5ed89d333f4bdeb8a655a22016ef2c160200000001acffffffff042f263e0400000000055100ac005113a9da020000000009ac6563ab526aac53ab2d19990100000000086a006a6500630052c7500804000000000365005100000000", "6363526365ac65", 0, -1093365599, "df8eca24d5e03174e1fab2471f8b371696d9501a624530201114d8ff83748a42"], - ["0cd4d5cc0288acaa581c03168c1dd38fe58708b5edc59490e21e4315079e3a25ea561c796f0000000007536aab6aac6a00501096b8beb09b6fc9c90b79f3dc3917d68d8d7e94e5d64a1f8424e30920d7a23796e563010000000651ac52ab516affffffff02f1b46b020000000009535251536a525151ac560921010000000003535263bb41cf51", "6a53ac53", 0, 1868094718, "06f9fec640896fd4739ddea761c9bdbe1a835ec167694db1edd02e8cf201dc2d"], - ["b03f84f103019281a3c59b8930c49a6459196c1ed600f1f3391704ec8c53d268c307a44b770200000000ffffffff060206570eda726b1fc475109288c9fc227b8cf02a797dfc4cc60eda083624c001000000026352a188563735eb3cf04fa784d6ba93245833f3d9b8fc6428c1cf61f0cc5f2c01659a9a99590300000007ac6552ab655100ffffffff031c398b010000000007510063006352008c24ef040000000006006a5365ac006bcc17010000000004516351abc8882918", "", 1, 1360849158, "768bcf818d9467a4db822835e9496a60f71cd7d2f6f0e29a79ac59014ef7dbe1"], - ["da1043600382c1b9692d16acdc9c7b3239d40ee697f1ba8cc5b4e717cf6370f66656a5d6c2010000000800525151635365acffffffff09d873d1334cee6aa222b6ae9caf30fec163a6b74d2c9d646a59e66bf278b9520100000002ab63ae54c974153ab76c594168520c37bc2949d11cf4c019d8e06b84ea5fbcf7518eafb8f9590100000004006a53acffffffff025fbf820100000000050000515200436f7305000000000165e06c32a903eaff0c030000000000000000000000004ec68ed1fa6ef0fec9fb119250f4c8857560c16035d651de01f549ba16d4684841402578d8ba1786df0027e2ab72b72ddc58b89b14a93355fb5263848c3415f21fe1bd3caa6e3859f51c1a5608b31ef92123fd63b8193147c32f8657a3a54362000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cc9e14d083d536282684a832de854bc372aca43cadde34765ea8fc2e33e93b6e5d2d11a9b502ce42bf804d7fda5cfd754acab64add39e462c346ea3857a4f56028d92a19d0a81365152280784de298c777eb0278a502e3968ebadeeff3bf66c8a41b66d0f56df029170e01b91a5e45e5578ba4824ec9e34f242816648a84850021289439267ff305dcca4e9031dd36a25fe58caec51066776db3895039c53e42f020a43cd8dc633bc201e6590a1a9b94f1180139ad437255f83fe86407dfde01f670a2427db711f22f07534f7257367813e108d0492363db315488a11202d1a54dc512e241176912ba21b9724b5e9946eca5c023b28733b3b9a9569d03579a5fa26430223c0a54d467ddbb8414401eaf64217bc60982a9ebfabf3e1552c4f1fc27dec430306b77a734498bddd1231cc05aaf78a60eec18810c8c74a07dde0962b0dc27c4d0328d9b7735364bc6ecd4b97369eb4019cac35c26d0f5863fb11b6ebbf56726256020a1741011748a09ab4483c68d70a0b7bf4fa59c39549fc6972169f8a87ae19b0032fd716d8ad5594dfe20149689f3dc8b21d03358bff4b95df9727eda64ada2744606000445d814fff91abf53c58485133cdb3c2f8ba085799bfde35341e1e3510b6ea6ac93c39eed1e8bd414b4d50723b0ddf648fee66b8b057479f7c669243f40071c4c6db6d3534ce7c925a23c73d0129cf97f23d2336959fe4c7e48f87c97ebf1545c5f957a7a83717a37b923a0c80daa413e2dd61046b099d4fdf653424a61c8bd7685c2fa3d2138a4195c9140c41ce8df81c0ec85b49506be49b628fddc26133f4b8ee29ec8e604332fba41a1dc723a1daa39dc28ff9d3a5963317709d8e5eb1907f77ef17528ac3b82925dc28f9bcf14777c87d168d336955a2d20337196e4656a4c6874eb570768d598869c6d15599a17518ffc876e63fb6e56ec5d8519715cef67c81bb6a1b67a4bc20f12a247dc12e6848f4848a7d82e4832813cd4b69bb5ade38b9e8bb0682beecd01a50a8aa44a2c3d5bffc1e6e69326f614f32b28e641076acdc34beb6a96943b5774ae3da2b6787ab9f197246991f4f1f631d62fd6c30f501689877e12e9084019631e1e27bb8847ab9aa3c5b159a72e98f050fbb869a9965986a8dd081244db7cef9f34282b30112e0a6a95ef136f2d310a2d3151cee71817521506793e9408139056a94f3c9657ac593fb55b026f458f5751677ce60527d4f47c19b7be45af84edb2f779b3b193935994720a041c3fc32e80dc5ec453206a97304ade085454f3dcd4218cdc8d8e4617793038c4e6ced4b50e2c6336b6a33c985f2717e44e39075f3b08a8f9f0a9627f81ae5731f82094a41becd2dbb1f29d03eb96e7f3935f137a2c83cf613761c89fb5b87cbeaa76c97e76228aaf09764a24bb0c995eb70c985af2a037bb0ec90f58114048c9b5d86d3438f2067c7cabb368f980ec29f2b931b53e974a375edfd83a1460ca270c373b94308676e761f441a82b5cfa13ff40ed3bc37d9807b0737863caed34aa535087afc5c63d0ec1d1151506485c51c33058c94de1e20cfeb8decbac6f6ebb17d41752b99c48e1be1bc8aa69c6aab3842b9c7896f540c7c26b03ff8a9d5fdcd59f963d4af19cf9d3405b9a49c8f1185ad2d3c6fe78fba679460aaada3f56ddc742a0701e08a7d3c6161c76d85e5f7374be28c53dccd47ca5a9f66541c8cc18e52654e5313e1f044e5a1d038d0ae9a21612ff9477f2f220c5f0f1b11be04f57111069cfccc489cc8b9e33ec8b182e531a625f68a1f670600bc8a7bf533582e420a56584d7bd4b6a950aa2fc2ec121f3a880429aae696dc0b153399d835b88e6550a6263ce6d8742047faec98b64dc7b7d6f1be0b762d0be8655e410742386b83ea8e4db1a958c4b763fcc382593ac9abe213f28e57976a855ef9822d963d042e03078f6c5eaf85208ccf6fa5705023759f6f394b5849008e4d0bd98368a738868a7a64e12132ce483d99e95caae6ca25e758d773d4dd8721c772dea9a5a37134f80b29d0100fcb44548f3a8ca5c3302f57775fe5cd7448a4c9b84dc531ba76e4942ea8a4c917b808c753686de1e3975cc874c6db4df5dd8d2c2631f9d8ee50987221702f5f835a7720852cf8085bc78ee970cd2660b61cebb660cf23e89bff547392c4c439d92c63db8272589b0cd35a261dd42e806105eeff27b991268f9466161128af5cef6f0e2c578e5bc3eb817bed5724152334dddd46c7276ffb0e11fbee88839c8b0e55bee1edc2819f1bb1cd82963fdff6c7aa38005f00000000000000000000000000a4c7ceb4995c9f8735a4e51511f68df15e3106a8f3106904334af7bb0ce649283373021425c85e64a54bd667c7bf79f14a607dee1987aee8972dd3dca4bae88f455fe698f2bb3a744c54af3d436a05231e29af5086b068f95d33f9567b5b6495000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e96d30c16e7d1f8793209cce5a3eb6dd6e7f5123d28a4ea48bef0469c2f72c6b2f4f637211b1924ce7cd3ecd85ea47f984f4abb54e1321441a9737b5f0ed5a483e8c714d0e255eb30ceb321d06b20dd254d48c21f9e654e097278d766be081eb3550b60d6b4aaf71c38ffc29cfe07d6fe49567dd1d14074db099d720870be240305219be6b66a8b7203ef7c4e002285b30e56f920f62160ef2b587b7cc8983d480209b476433e745fd0cbddc409f95d2494966129e3b55f766b07271b964f1e40570b2112b6e672b8a84b95e0d045d121b80b96484bf1f01c239099bfbd605f9d1d890cc6a508720d14fbec3b8b7184271f6b93b7f43f9d677506ac783bd667e1968a0317ac1a03596ccc206f0fff6880c357fb4a223973f9c414b699df5e4ceb6417b702178f6d0ba27c9da6328a3f402709795b98e5815de4bf4364c5a9f4bbd473b7f80324ee1c44f879e54f157f660d6a3e77651c36b2ce1b844a58494a3be2504e7da60310b09816cbf5324e475d0636e1c7d559c37f3a0fb5f5cdf8addea5041132be0b032f07e02978472c64436bf9cd48b94afde0fe900dfd2ccac40698c7ece106b248bd792f63aa2bac08f2634f4931cae554c7f92c0e029c0c2e3b1ac6069eda2570939c15a583b484d547170cc52c91c432949ee95a381142a304a74ff2a56fe8aae7b0b0fa247f6f97585575b27695bc7d5bd0b2a979692967d4ba25a7aaf96b44de4413312723791dfef255ce8ea1da652d02147c22d169b48c44fd88d69a5246eabf2150a88cea28989a2871600e74555ac79403d34db29498108c85e33496a740d41b35928367dd140a76ac7eaf1cd86dc24317c4f0d7f44632c5b1062fc3b42ba7e7d1e74d57f871b17bef4e53ef673c1fed2f3ca2f04c345ae0193bc2ce485ec788fb067e8d9ac4f66faa1f28aa5b7d2cbf54ae9a390dfd1de571251f84aaf283cd9806f431a39c16f637b6329aa79422a7c56aa07e151746966caa517681485c24562e166b41f1c1d9e494fdc5cfa92211f8c170fb9845295a9806b54fa6ef7b4b4a237059e44ad03d1c23023b45872d9ad86fa3b0bd1f053597e15e922c5d5518eddd5f4e114d63f85c9869de49117ca840245adc9f07882e6a1e0eda56fbe5c9fc42e4d2d8f02bcb1f8920fdfb1f984a11d89c449cb9ab75ebf0184e696855a8634e28e7196ecf692e836549208324f923258359d2dd15bec75ad576251b97e2c9a83b4570d1725f91c85dbebde7cda3a016b3cce5474d900eb297ce224eb152f47b011f79eeafd44e3f60c97f0c92fa1ac5b4b1347dc00629c73f601bfbf9b3ddaec675318ea0951b881928d56c82ac7b1c04371761b2665fa50557691a0857a68e7cb410f6ce3a39813c7e726b55e92e13f914ea960071029c98dd3bc289d51294be0a31ba747bd89c603d9be40ba392e757a3a6584146f608068b673b24a5a21d4495c10e6e6fa702d2db952acee5adef84d8c8437b995e75b13b8177ba59552cac113ae15594d599f3b06da31eba2c04dfb2eb3e0017a6790ffe6055b00ea5699245b69866ed99576c0b49cd4b8d73ca6ad9c5089afd64a7c31b8f10652a8e8d52c493af6cc9b0747a859cd187535eea5f958e4efb1c6327973e15fa7b133a3fb0bdcd9f65252f3d466dd75834b0bc99e748130e6718024ee3a0ade77e7ecf3591e9de42570db5bb1616132e9b51ddca06ad42761155bdc988810922ffd27c1abc8b4aa651d022762e5439153226ad4e1de693c207ae3496b76b9de38b762c92c29f1526693ff0449b217bb5b89d5487712a4c064f1a283e9a930520520783150ff0f8fc61be10e6ba183cdc9021f92f94ba64e3a4bb27e10f771eb109fb45af24a68b308af8fad9f6251a7941bcef81676ec0834bdbfbe2feb813d2d6ff69d4037551ec7d625c9a57e866cb98ba7c887a5f2670699fcf9b30969c0047d03046e17bc065bd61cf15d4c156c40622071641696798e77d5b1cd13ddbe4e7300615c6b434db9a187d34cb0f0d7c66f5f38261493235772f0219e8705f0fd3abd53a07dd5c10810580521f12f0e92d4ba19ca70d92dfb8d1280f111fbf2b87c7fd1a385f644d96d728aed5051ecd7b9a2876dab5df7f58384b34dc97300db2452b07f7cc7011afee8f31f7702f9f3651fa53fb7a87c4223174a14d161a021516d9e6b59efa1d125643890625ffdea45c311893a30c18aa893868e65dd2acc698245894e0ea7d494822d34a508ddacd7464c4077bd292e9e01ece3ba58068175678b482fea2a6e6f8f60254ebcac2fcd10195020000000000000000000000002624cd299eea61c3d34e037615146f006c6c5611e674bc7188e84ed925e18f5f3effa8c49846d531108c3bb162d98e83c2e698f814273e3e963a1891a65b3e646de12de72c2453c2f76accb2d6e068c8c512b6fac015f0a6dc7eff4dc82d084200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000127571f610c4803b43cf2a0855cad3ef03ef7e2b14b6a72a05ce49b96498012d00ebb6032130897e6c0fb97ae45ab1eb77cd7d9ca7ca5496cf852f193fb2fb8b5ff322324efc388f7957b81342aa3743d2d6d88834165ff521176141578c7e9cd9a0a7f182645311fe611ef32bc52075176cfc994e5369fbb02ff5d3a0d67bbf022de28d0f9029188e0769aa407371c950599b934f2d1f2926930b9c89540a7473031d4bc39225b8e7abccf80922c59f0a9df7f089a309e928bef0f9f6bb56e12b1f0a18eb5c2dd8fc2d2ceb8ecdb96c69cec535d85327c7d4ec32cbfe14345558fa001385f7b79a4aa4f1888f090417e28ca6e9d8963171477cd1b0fe45870d2ef7d6020586d484a60f0bd0483284adcfe1356434edccaae871ef881933f154cc80fe7602192dd05fa0c97ea410109f9abce0159b73a48e0d07622f69c4f8e74c844d5bb4020a45a5be898d6fbb741221af6dca840568a161e6320f74b88053ddec2d08e9b4030b3c6f9b5a82127ba086aaa5c44c6b3e542626e1fcf11898c9a98302e704319a02243a47207516c30d92955373cee28cf19e17b6ffbd88b9c155d8b793ec7eb02a651726ba0d66c225c42676884df0dc7ada7ae28710d5fb9caccbcc4ba6799c7cd451726899bf1c50d6beb290a897a941139f8de65113af10cfede8dfd90858ae077e740e074b055a8c78c0f154ab5216a0c53a60253bf0193f8a960804edbf1ee03bb3eb57753b03bca1b90adff3986e6424b552b270317cfe5910d451e3f1964dca1e1a592cf2bf4ce8c85279d30afc111fd58a0875831e6a06c807881edb322b7c060e15f44e61e22c689088c3d6faa2f30475e85fa8d84923bfd7c6333140b03a8892e979247769fe7610b865a7c0460d9eb1ca9c39cfbd713c12f09922d84ac8c018c0b413005e6cb22a4703dd6ebe0e2a9c125cb11b3d92fd689a13f571f7d52c286007ab4584ea5cacdaa058e7fa5567337b4fdbc88baec3194b75180285f9056cea9d6396c07f5b9af171b11d642317f47be9a770bab1ef39d2fda5c90bba6f33539c914334fbf0370d517cf7370e7290a267cce3253f99d0d551994090d1344f548ed05cb260ad5087e517e831448f3ef7c387e11b49173e460b3668ed843bd448c1e6b5e62b63c7fd6f33f7ef97d53eb270f92a01650a54f057e9ca55549be60097f2342d1f6411fd50150cecb61512cd872db0f262c0e21d1b4a3154f194f0f753e2f4de2ebab52c8de4fa24eae7d25d2159c8cd8acba1d34376270f148ea7684a725661ccef7feef7f81c3eeb8273e91cbc32df99090da49f91469c6d44e536bd198e8ba6a116d3c489ddd52479a95ef601b5041cdb19f3ca36dc5533be524db77cce4d1b9255e8d61a95c628a4cf4f02834e68e798766aaea1c77d15b6d67c1c6aff565dcc14b78fbd9a82408bdefbcf324b8312cdf29d761106ddce55bae250d8d31ba2be1f130b6cfbf85c59e6926df5a396cc347fe499288e0655e8429ae6eced23a4d84e94057f3e7fc86ff0ae73f257e10987596b3dc85aa2d02e8caa50794161177488ecefbefc3c9d9ee4a02e68093023c95cd5b6f6e37d5e1c56dcb53802ffbba91ad9e5ff3bde931c83b748cd2f9197ebd6e2b16d695c3eae122c3d1390880945cff673864e6c03c1229670c01fb2e9fd02c4adfe2b305f665ce5ff97f0e4e745964472d69fb9456188003bda15f8a5e845846c0c9ff59be982dc36c5f5d65bf0937933c6bd2acd83c3bb07ce22b9e057a7363e605ec6ec607d317023f35c9bdd62721a5623eb9f07378f1f11c0df7986419a36a76cf5c55a9aa980f26750021446f40719f802df2ead721652a9fa73450ef45f4794f99bb783efe246057b5526445bb41c8b0c5bfd50f2bd7b16dc1916d63a6111d82135dd63ceef902d3d828799f9d6e00fc1dbe8efe52b915900ad39f089539f217799d3654351e310fb847b0dff0496ec025d9a9fabd08ed88d16b8fe5dfeebff1c97f4145e25c1fdd90292df6e80b9e27df646e7535c783d6538f93da6e38f3baac5cb9a1547361bc1c6dafa0644d9c020f8591a5c5bb846584a1b57d1f09f226974bdee3ec0d8e69194cf31a5764cddea51ec5cd07c74e678b56d9a83b577f1fe4f98bffc9d3e616d3062ad98eeb6777a829f521e5e1a5313ca5c969d7b19648db4d2b95bfc1ec352bcfbaafa6c4a6dfcd04344c5bed83c1283c2a63737cedd7a000129b0ab0f840f2a98d545410c1561687a27040dc28ed8196d1fe46b6eb2965a35f35b8ecfd9d8901262a6fe0cbee646956fb0e431875e954cb5f4e030ebb0af98653150e050eb815e15a7008e976826ba839f2aa68368dcf76619431bde2e582515ab6fdb546b0593b10963570c3b019fafc873f5bda3bef238e11649d0bc5321c732e02ed2a9d88cc89dba24523207", "6a6a63006a", 1, -1240689834, "44f062e40d540884d4e663cbc4ae54a85e810ffea42ff921493c3805ac0545ce"], - ["69dcb5ef0453ab0a5c9b34222bc3e08a62ea9c6ccab5f8876d617029d210c30a70fc28775302000000050065acacac63ba6d8c534d7a8b0f0b5923282e7057520db56a95768e43cc037064037686e09c0926db00000000085165005300650051f5cf5fdd0b34d25e65fae2cf793e3efa74e300f85f7677d67f1ac9aa845098312704230200000000016a140a083bb5f2c2b862e9dd13f83066071054f756f42eaffa63931d176d7cfc23d798fd2201000000085253abab5152ac6394b1edde015a4667030000000000e607082d", "51650063516a5100", 0, 169180113, "f9c16e31842d706da21de0df806ee6d4fb18206a3c5d33526b417add435e2fbd"], - ["55be2d630131b94b7142abfd8c2f7269178be517f0941bb0a05d141561d646ec38c57e622501000000056a63005265ffffffff021291840400000000075365ac0065525377be4f04000000000900ab520053535152ab967f4cf20300000000000000005dae6f01000000009ec063775bd36fe71da37002d123b2651a3a40396d1b3eabe214c7f2086779d7fc1456649bcff29cae3a2ea015a41c99a900023083a2fa21b6a921c03764582fce2a18acaa9465fcc5cc8bb032b87609dee086e5ab9b237b0f41cdd34c670a7100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ad2fe553ad6889cf033e84b177648f7b6ad71f94dd83a658b6745992ccf9669a5f416f5129c8f0b88683753e201923f28a8155249892f0fc20e1ee1183dad5fe84f261e70717828cb0f5ddfa1489c0cd653d2f1739bdc255acb9225fd4ef30a8f91343f5f3b4c2b5bc9e4cf2651e2e4c45acdcd5a93ddaf4abd73219dfafa5c80218aebda6097fbc311133f4e0b6f3a8cace9d95fdcd7492bde181861a5cb0d6de0326704f24d47f9b5eaf2f42e5af4d07657c5e781c193916b78bf470bd783e62fc0b2f4d4bf71b15b7bd421f110e972d35554b417afe7ba43cd86210d1399b19559511110fe6b946311e9061cb9394a74dc37d43cc705463115d3ff183ab4649a4b3032fc0c6290c6860f19641c71479f11ce9c7f095e7a73600a397d00bf4f9ee85ec020ac185e3e386de9da606381246cf2874ceb6e7386ce5874089bb4fb4ab535565021bd2f461e4c37d3953102edfd3ca7277a388d82465af35d91bef8b24c0d8d19d030dd15d284979be9da1994afe399a4112013f20eeaa1b03cd26cf740a8a9278a4030e1e53bf171ce465e05985f59052d6c3a1e3898579da611c1eb92a241ec816478d711ded2b6c2308765e07619fd33490854dff5a2e7f69b5ba8ce847dbd5089731de149f8a3488761b794135392f776d080c2303e37c96534d27c2625383c63bbc3d6113787ce0de12b1e630d605664aee6cb216f2588b75daa1cdeaa23998a71bde339f4693aaa500a9fe86d8a61c2e2c31b8ad5b1617b8a49bf12406c3e96478e783d4ff6e1f3d8a411f26f7d0ffad597d89e3d79475ed123c1bb6b00676e7e3bd592ab1c72ba8e42dc5169e2909d41bb34ba976417c6f73aef87cc620bf59ff995b93ab60bb1e31103e8fddf91893809eae20b245b7899c5c763ea8205263d45f558df836439293c0d9b1c81e075ae807908f566c761f9310af6e0266b98c55a5ae8925e1ef103c0260359656a3270846a86e19bc257d1c8f3f0f3f3a714ddbcf01b6a99d07bb27975759a9e41704e393f04c5080291ae5793aa3b687032b73f2f3c3b20fcc0e9d301171d1074536f3a8da3921521be5a1efd1d1204cc47e60a14d44ba977b8b4782ec587d1e2e911eed09ca196b9ec4e2f9a6606d59e3507e31464425d638bf335b4133af2ee5ad4c7ab45aca6a0c17d6a8520a40b633e52e527dd891b9fd7ed8e286a213c5885e1909ad56ad2945c9d22f777f6332cdd25186e18f1b24f93b006d221a5179bdfa9c3ba23df3b519bb6baa56ca08f68bcde2c6b993e87857f88f8372b086918b6899e92c3de31ce204f1266261653a738b43ba27eddb2d6161afbd75f75c1d7fdd3367ea5e4ee5500424751e94fcb25af08f33280d9772eb4e72b6ae9693faf044808ad11928bf47545c6f7faa93c052f926e1d9f386c04f12819ace596e70b41c390afa5e70f27874725c9dfb887ae49faf7cab5d29e12a9935bea1b891594c36e74d52503fb219868754ec04bfb386e01257579f043abd6bc309daafc8cd95004707de1b041b4e4a5ae9b64d6b4d09bf8f75abfae0f261aaffd6994599721c74a0594710e6c3feb1b2f70c683610fabf74a77a10107e1957808d71c8407429026bc5fe4781fba99388f5032213b22c7e3ff5abdaa9333a776cd5705c3f62f5acce4c5e12782fd8e617e51eb1d2c9c9082c5d0153e471a2779bf41fe0dc066d2fd925c2ca3581c5ed5214254eeec60cd0299f59ffbe5aeac77fd0d86df46b0c7ad2ca7f04fd3453252d102774b4eb0b75da17c7b315a7953b34b759f365f8156bc38d9b66c4ff22ce4621b776bb00636a79da8feeedb57520d06396750e03de6fc00c3cd7871fe00d161e7f0a1b197bacc8bb06a8141b35a392b1540f238e95dde4e3ef10d2f6db907c8d52b533c9e1b2c2c52714edd7254209f273972dfeaeda537d2f0535f7aa72c4eb1bb07e024469b2df6fc8fc4bc8d163f46741c74df8964f763d8c55ba536455452ded8c067e37aa3ebe0230c61412fab99cfd2c9b0a3d8c28a6a7a53d2d27322263833c60dd94e61fd13d3bbd5e98bf7287e2ecce6d71a729dad3d38d27f1f1890f2f9b1b51a52b4a6b769c4386d23923a21c389f4a1a5c98d2da008f06f20855fc5e713dcf2028582b3f39252c06bbe4157013f6f87b3c77387e66c85dd47708e98feb929700d1c32003cd91864d796173b91abb46acb51fba30a2253c5b917bdbf98319723b766e8c72c3c056d930b7113563d4c6a16056f3cfd947c2433af51bbcd90c732f54defc0698c47fe6363499ae4a7329850e9b00000000000000007765610300000000ce03067158e407b1f83ee826a83cdcc0edf1603ede3e638768d07bb0cf4cd2cfacda96aa0b769b01b6a34a95b014637bb580291e9826a10522acd20b55639357f525c9428a80d4aaf3c9de389a45859f8f241a5e782d0f3621dd127908429c8d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de04c4d679375b8e553e4491e14ade38dfc0974d57c30d3ca5e7ab29313766af9e751868be3a83a5a1b5b3b75c251540cd0bcffcd6d210bec420c9b8b620b81de4ae384666c4b8ef5213cf7475ae261e83369a5da5eb25a3462ed9d133dd49fcc7ea951b12c704ad71b77d2467dcf82a48d6688224d5b9f80d7951e9abb7965a03033afd2435b41468cc8843253ffb8ad63e791bda27806a5d3358f3585efd53a5030d7604a82ed43c24c5f855516dcd5a2113ccb36b9978bccd71f7741cc7b129e90a1a07fdaf5ea097ec138a0e4e5f069e8296a1f7bc76fe06355dcf076e68a6064e182244238523357ee5c33d6da9fead28ba9a22b77f2f0fe13674ed5f9ca588c2020df18db8a757a846d887f130063278c25cf51b75d6c56537ad712bdbe59405be021ac7d72a14b4fd9c9926fadeeae914843109de702fef3c1257739e06963f1ae103102949c24583c159b6dedc05f2fab70e2e990dc5fc7178f58b9c4b105abb92f5031e009393929b6e84d191e712e65d360a30fe6db524991c707e19a4df244f70ef031aa4e545b70ec4d84220c31f6d3c7c8d20904cab9e3a6b2549cd63ed57da49dde8925cf96214123de40d177d815b22824816bfd730432f3a85ff7034a08ed9036020e0c066a6db13cb2883d798d5ff521aeb4b40286f85aa35ca402f4ce221136d90f7c9ddbcafc3d54c52ceb0c86eb6ed28eb9a9bbe9616992c0e6421afb004387933ca34a81904e4cdada099cb602a7e6c2f3fc53a77207ab0e21fc9a4eed6681c332ba775c17e84e30c563ba768c12c12a1abdf94b52af2b0eb2e63e645acf3cdbe3a54234e201025fb1cc9da68d0ce3a9cbbdb9554de3ab914ac0ceff4d0824686a4daccd97100428dc947281d35c53e341c918a6987795cbbf5b713a0bca511d89babd1b3eee16796493d30b330766bbaa167c7dc261cb22f9221eb3ffd50caeb61ec33599995f3cf4ddb2a8f06fe1671d36931841b3f942d0d57c5b2e0c714c7f0d8ba9653e9c6a1dce722894fb48f4bd2188792f67844498d442a417c7910c8b382b9553bcfcdf89f777948073dc2765aa8742018950685ca283d9d2e380b0254602cf56fec50513570e1d5d93b095b3f89243005e8c651e3d2525d36fdc15b92bd5961cfd574f89eeb6b0b50c7e3a96220286a628992280c8bb031304ca00067cebac035d216aa5cf9e143e412225c21c707572fe1874d5af8f8412f3a17a203e6484a878eb0f2a7e8b47d3ccdb08ab1a3b551128ba92589dfe6b71026f240d23faa18e28a1f456d5a4850d8b22921ea787812dca1e6ac07ca7d943726b0b0155e93ed514969a0e8067bac78f04a72025c89c77a2fe2832f642e12b7568959ea31258032dba31ef5e18ebd6009d6cf351bf33c0f4e28855e0a4fdbe9f3f0837bc2714814e6ebe7a901659134f060fdb88c1042420e7454012f97ee2b217858d35f377e151845fd41089effebd8d2f5f6533b446d807e64e355710613d5ab4b3e746705ff1729f6738de0cafbafff3618428a42c1d11117d072733be0347ad08b1c38988b32e5a4db3cf3ab87019527b735f1985b96cc2dfe175e8e48bfdd2a95bd1e157659282cae156cc4459fc29c054d23463f4a405e01e223b96d58448b222ee913777481b8bfee8352c676043f4588311e607a0fdb2c415e76f525fdd486c9fbcdcbf3b77d25ac10f9fb37e981bddcf9e92031d69dc8f5a0426a340f316945009e84b9546908cb3762a2ce504ccaa98611fd5fdf2c2567be137ee3a159762b479528abc97d7f46c444bb102111a2aba8f3ec567e6851c0b5883429ff40ead6ed1107452e90fb0024861a32b3616cb72c6bccc1384afc44f3a3ce1a6e1db30dac1a75ad409528161cf0974ffbf187895529c58f126dd647bcf956ebff9f572392f0bf27c76b53e2f2169d103dfa8422c7b34fb0d5e7e15362bcac42155a4931b9e6a8c55a85f8675835e9f2b98e30b6830959307c6bda6dd1da973032e9df57f4eb9f2b26e5a94c8d0958189cb923c6c584918520d72de2cde2713e014f94c7b027ec67d224f7b6494276989fe42d66c8f9e444806179564eb667912447919881cae586da93b9501e8bc380f869fbbacc98d8f9cbed198596abe825c53482ff699947d62a70f0a2db346b7791b174731c1adad1d3e90c654d5385a93e44dabe71a00298d11d5cce7723d1710bc78e802b340a20cde7dcf05d8bfafe2bf169c0727960b2dd838c1bbc5ae6be6f33c386e352ea91c3f80b4896964a67ba77d4c4ca1a9d2e0814d3111c2e4594830000000000000000bd2e840000000000f04b5c14d36978eb062ff1cd0365748491e23e8a1865b94c6b64331be18ea6bdb56116b1dd6e5e4afb140037bd2a2b017de07a51efcef46ed30ae065fc8b37d468db5283cb6de49e60e2d4c62388f741bc8230fb08bbd50d061acd01937179a400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2ad07216f4f93180a15afc1b6fa4a71401e1645f1600327c7fb8d4348c86954f449c0097e804141d3e1b31ba80f8f30f3400738fcb79f9324df4752ce34c36a38b09a0eb659a1aed13100936a2c669a68a3f35f3a87516e908807210b64e7d827c11425d997ee006b21cea3e09d68b6728aa1254b81e5c074aa3c3d3cb23c60032232c6348a384293bd2244cbbb1441f3bbf7c37c22ee7e1825e44850b2c405c2030992050825bac74891eb3176dd7407574a802ed8434cc9b909c3e0a5e22152c40a295c71b17f11483779c20bdee39729a1c2ffdd6198e0df23bd8228eefc6289e12f2448988efa31a33ab5a1e15aadfaa72ce41a51b6a97f7caa8883e6f246a5f6020cf9dbdc0bd1d6698a71b19dcc8df7988d8fef1227ee426f1f8af6e5c9bed9f9030ab773e9b95c32dee2bd5f8d744884ac4dc79c3b6940a1c327367ae40f466a6f03219b0ed7d1ea6fd8e0eadbe5e89ace092e328881366371b4e8192cda19df5bd5021ca14cae37f1a81ac57b5962fb31d302a551390dc3992ded2409fa8792a7ed53020948c2ac942e03573006b9ba346c98b17eeae1d42eebda0a59fee324ea685c0092e50cd4ca65211fce3c5869d640275e7bd8ea114ca6d23f361535fdcd05001150c6a7022929fec4232d4fbeb2286c78fcea7e376613137316037e6b6b1aa1c8a60905f670f9a8a204bb8d0117fa2efdd3b64e99995a99ad5255daaf5a6e25aeac2392e4f360bde1570419a0c6d79de7777d0c7442c1a2bebaeb335fafa8dc552228cde1e2e283d627e1a4c0e04e4b57c73cea2268dd5d44560a24bbf052ef7f1633a3714005e1e3a14d6256551fc1d758aa8d07531bbc4043aa2033bc01b5b67e9822dcad6a6eefb61447886456c1c02d1af4720668fb7286e76f079c90382534d3fc287fc10daedec21a32c236bc8e54e27c9c5ad9a667d9fc6f2389ff1b1ae5a28359469dba22e0c9661a12bb72dac1eb06e99208209c839ed7688f13fa1f9199a8b9279c294572da27c6eb71ec8429539b7de4dda4f42e7e7c43ef1997b5c1cab2b59aa73a746c703777438ab8c73b1e63c9990b2c763ac7627c07cd9798e33a906f16639922a0c35cfd763eacdc562cca7eb9d71610a0c77083ac8d5ec4d73d204452dfdc1636abe4bd3a359495a2f467f92601304da949e1728661880b2656cbabd29f7d0a79f9c7586c01fa7f7001a37603c95240ba19c07f8a1ce9af2bcd94448483033602a327adf97bc56872df5aa48c30fe05389b6b37714c8c83dfb2c440d13c8a982ee6aa5cfd13a141bc6c049d3ccdaa24fdb824e27e596700d2b2e350b02d56e97e75c4385fcc487103c4ab8aa913e83853ff9374d4c8e731eba568d51046d68704f3dbe882e2827a243379f6a9392bb04544ab75a8f66fa8f37638b1a9a2327c1c993590dda680d03f2c0aa77677b9f9b1cfcfae8ddb619ed308cf2a430d8797cbd683d1f42ec973457ccc3f1e9704ca3433a16f65900b1297fb9838c52de2c85c11bdd65341ea72c9ec3c65b0be1ad9c6f132b6d0fdfa3aadfca929c5830428ae008ea510887b3fe9a8f03ac5f21a3a3846e084b9022583919483d0a30a85250a290dcfb889f21e8d8d82d4e787d305e91029c87b51403a15a38e68785ef282a392a4d1bef09a6b5563620501b54f7454a77ca91bc66fc161846fc6edeb097ee6c855a5164774726b1cfe255625f08e1623e1ca91e25c12f25b2f9da08dd9d5629ac88ae98b57d663321ae81d3ee0d2ea809c422cf0cd0b5dc7594f67287b30af9bbbdf62d7c38970833dd9c761bb9d94e9261d39213263412890fa8d6bbbb0a63e532db6c301b3f38d5df57c2da1a657e0f5f8bb221360008a8a5d1b3f1dd4bd15a32d2a7eaaea7a18f7d0afb90683cb7ae84a1d5fc3c05f3ee6935b66da7efb8aa7e1c4088f7ca57d4612d0ad687a8ac524889cf515573f472c1ced70e55bc0c010510678abc95347487b1598bf1d1c07df1f50665d03c061a408e4c7ba38758be3a8c50b9658cfe7d4801e5688ee8b1f44e9386c9683f2f40cc1b2282a24c437a6ae6026dd13f724a3a68d42ffb8a2127ba74b1fab7cea59cffe9747dd0f944be4b285a57b724123a869a786cfdf6c17d32af099a764ed954526e8e5919cd5e379eb487b6b82674f64d8943e442ba1a18e9b89c44241a749dc767cc9f44b6c8e57a6caf6dbfcf255f0c0b130aa50d842bab44b64b5aa56559945b7f28d5c21dd4a4609970a321fc844c6ee723908c0fe3c5c7fe0f5f4ec30bf25d428d338991c8b38fb8ebea1b5cd8958dc51b7d1ee93afd2421f0d263103ad15094ca7d25706fa9442f2b8974765c42e4409458917f7024f4a6de8bfd3ff5751bb0c752673b6243ae3c13e1dc3c4f0858fe2e602c4447561a4a507b33a3df3a7fd21782c0b9219a525cabadddd0b", "ac6a", 0, -1298197879, "bdb945749e4d2091bfc08450ac38449e41e4d4275b6db0b41d12561cf9c49d8c"], - ["5aede9bf037ab9981f60acecc315bdb983dfcbbda67197db02c9ad64f43f629f6ae70c3f610300000002006395e1786fa2fe148f648042aba0c4205c22fd002af02ed5a4fe9559e2fb1c04b60c651fad00000000085352ab5151ab63520f80b5a289f5e62a89909c839a38958aaa5e8b0a0b216291d2fbfd02a391812a476c337901000000045263ac00ffffffff04b5efe501000000000752acac515253ab2e6ef8010000000008005151ac51636aab3f05a601000000000098263a03000000000952ac6300ac6363acab9bbe7300", "65", 2, -1506628071, "667dedc6b69f810fd7b6a4504aa083881480b25d4f7b247ec8b79d8338212c13"], - ["6d398dbb01eaeffa619e9f336f9f36c22777b17bd91cdecec28707141cf3b3ee56b19ce7de0300000006ac6565006353ffffffff01ede4ec00000000000000000000", "", 0, 1136078499, "ceb8da137e33912b22e543fd2a7d612f04fbbe148fd81762fc59ed7dc0a1a365"], - ["8ba8c3a904aeaee25db958c1e8cf0c2958c40d5930dc8ff626609d17fafd3a61ffd70fbbf203000000066552535165ab706d4653622c952bc1b954f5b3efd01bdc37e2e716f05e369af6c37678b2f3bfea1a916601000000055265535351cf0a94d5ce4ba512295d7339321975def47a6e77560301f18eb5ac787e5b69b3d635487902000000036a52abffffffff64e08af76c458284f6bf2d10a7bdcd7e6ced63c49409bf3e91b14e75413b62f70200000008536a5200ac00ab53ffffffff036d934f0100000000065300ac53abac0723c4040000000008ab53ab52ac65006507a5f7040000000000c6a46a24", "5151", 2, 735845796, "9584bc3d5bd175705cf4827b0ef53d09ffdd971a73d205b0aafe85783bdbe142"], - ["aab932b70250da6211953210115aae2c1067bd41897228399d83674f8cc84f6759498a40f6020000000002b052424e00da443d7ea5629e62f7de50a022991d6de059d8be6caee061006a6ab9261c020000000152ffffffff01a1a53101000000000863656a5152516500529e36d8", "6a51", 0, -1774314734, "0fe8c376968a59589ebdf18e5ef12055bca7cf00f4875a31b1ccf9755233b068"], - ["efdaa37b015a168d5d1f0ae8d3d2c2eed29b95f8ee9b2d2e16f902beed074a474df26de6620100000001ab2b95ad4b01458bd605000000000863ab526300636a008b8db0c703ce8cb602000000000000000000000000d85d40e47c2fee569cf64287417fdfe8315aee5b6975f4680703a3e660ab3d45cf441587f019f73d132f804f8c3df63f6c37a020161684a47707e01d6c3ed30a41862acc421e01c5b81846878f02bbd78e5d14a249e24e0598affdb1f61c8a310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000049098cc0d4f08fe09bbd42bc52611360f2d7c6e0ccf9b2d0b79cc632bc4304a1720753e58b7cbc0b56ea37fce7f87fd90edfeb98335f85407947453fa3107900310b18309f8c3eb4eac67e3295bfd38a09ea6077a43f87cdd4793f7191ddc5aaa26fe274037d0bad037b83676f71b86c3ee3252c2130a94b399487f965dc72d203305455d882704d877b6bf00703f9cc4934adec9a29946a2d6772fb06dfee31fe030db7e2a279d6e22e246e411b69d471effe74ece73570d267d6c844764b6585f00a01a9236a4c0c464161797b41b0e339a61eafc2b76e3b300fa68fd43f5742c5992951f0fa98d70731a1c48a97207bf602f9b84919aa0d814bbef6991f32280fb10313b62939208d5acb465bb829efe5b65296bd683011b337440018d961d72d2e3d021c00c8e1f462eea02c87b6c55bc5eca9e2e8e8cbe3564829cb4361648c2d5c870215106532731abe5f548725bd02da1ea5a6f653fadce0e420e57eb3728d22505302011352d607702022996f40d1ea3993389f6f3d6e8eb58e51d93ceb3a73962b1a021cf6a81176eb51c0c44c15aaaf9bf0726da6402127f2a58bf51e3d902a871a26333addcb43094e086aaf250b51ee74b1387338e3453aa95e3d5a171a09773fd1bb2e28c6df2300e6a7e620104dd3beaabcbd5ca0bd150061b4e8ba8afbf02a49999452d72e15c555af86be894ec71a3a767e9ae21723d784fe68374cd05061dd020df1d1a4f89afaa3c1c0aebfc4d91d9a0b2b289fe714edc4e909945dad1bc56cf9caa3f71a4f94919b653af69f45516d8e88beadf901fbd81f62d932ba4a18735a4a4527858e1d4665d4bbcd3c43ea01b01b539c4e7cfcc3502c9298e6e0197773f3fc91c8e051b4406cc948834fe07677d0fbb67c0e4ed67a810402f3cff1648567b4a27a28820af6c1e3a809fe23845c14dbb8739edc753a1a698b000e386ccdba98a8e73903da9dc74bcc3b7f14e261b7d643ed85796f555d89083aadeef7a344c0604f1c320adbc255b9b0ff0beec691dc04c077b5d2ca9be6c7868020f148c9583d75ccaef761764b6ee99832f6999ad45d885b51eb3af53100ab7c4258994a1ad7828729cf7ff219812f60d73d19b11af3aa102c387eaa3e3503d385c725e98d2eca62436e9bd9455573a696cc7ab778a2b6465160df8559a10ff38ffb230e531815858826c604320c530277f39bd0eb3d1ee812c24690ed478f97596fe703e9cf2bb875e73ed72c90cb3629d21be5e0e0bceaaafb36c1fb54ce76fa8799f164a7f218f9a893faaf0c416199752db12acb1e9ec31ec2f80fb9380545530b5043b841a73327e745c512c7033ef6762df6b15c2a0dd0949e08d93954c9fa3ecdbecb45ea475882b3a3c475cdf9747cc847652a4fb49a30159660540e350b0cb1fc92e68c97839a38dbbdf015770fbf726dbc901d8f00065ff018e9a34854cc767c211d01bf5ddd165a787d0b567a19a4b42c7b78d9e88646196f0bf7a9e96488c17772b4d74964430c65c8be815572429035ae363ff8dfcc03a78311731443e234298c78778f1002103a29b81d187a7747fcb3643c419dd0903094b8a20e0582f536cbeccb6f9075509b20b8a8abc4c31b010551ae1e50f294ec9d4dc96255f86decb6078a6de5eb249277b234ca6e196916af08ec0f2b18c4c5207af8815c1a20601598ab04b3e478f1eda3d3cd23ad2d65e2a13ba695625da7d64a0defa1b3355e81b67a236e3f0f1c2248b4208668986f577330dcffcf472014168654d0166e23fa340a5260a3d1983d29bd88cc55712b507c06bca6efcbadb339eeccb075784e12865fa6f9db24e2e8f7cc83ca1798d92b80277c8b18b993f5198d3643c73bc17fbec35159e15a63f3c7298f40ec3acecb0dfff22f2e7508ca0986179b9cc369a8cea8679cb327f5b9b959ad99f1d36abc85374fb6f39de6b7a7575d0714603ae2556a8f55d62c4d92fe21604d46697fb8cf675662e9696fb4900651ca246cc8437f442a63cfa54cfb698cd2310f417bacc181eefe636cf4d3fb45a3f5b942bd9fc490e42a10e45345bbc889b7c93596ed1b68d4a3ed169b0d68786e5dc83858589cc71fe09cb243f2452aa21d4f821a9aa0118672ce059e458882ea27119f9092021db22de911f97d36d32c50e041e725fb2d73eeb4adc8de26142fc14e827e103309d3cfdafe201548fbed60c621767413ce7dcdebb58101067afb19cec1bee0d87eef049ca87cc2fa55f0130f2140a1511db4248d1cd359d1909c8abf3ae940c457f2014b7541b78be4ca2236f8f9020000000000000000000000007d8706e8037e60a44f6e79808f45c4cdd2e77d81776007505b829617d5fde2e37f000a86e38dc38d318ea90aa8ef1f30ba3d8c4ff955d416dc42ab9aad0b099d0cc23967ad0461b0e067435e3e8e386b5ca0cc97eb70df89005fc2da2cdb44780000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017aed005a6c9bc85480eaa46d87886e582d79e38c89ecb980b43c4378323de8bec96a96b97256cf57050577183dbdcdc6c39d5fc49be7a8cf0c0e689481ad4a9f9013960fb34c17f812054c9ff32820f84756c7d6694141ecf63e03e53c847f20bfcad36383903d42f5d6935a78d1186c2253f57ddfc9437331dfe67c3a1204c031c1a28f244ca269c98d4ad49c86d02b321fa79561585a654160e3ea8730fbca1021779e5253e4a73c0dbcc0c1983f43ee69dea12d2515f6dbe7d68841d7d55cef90b0630cb3b4b01cf8a5137f3fb79d19033304e2f6580339a0b12a769b642d50ce911f177dc569cc3a03a6ef01c53453a141e55b71d8daf1fedbba0cf32c5cea3d9021ebe059cf244ea844615239e53b848d135bee164c95666f3f2d603ed0aae60fe030bf72b10fcaba18fe99c96a31a160f897ac69f10c770d3959ad60ce8ae9214620323430ea640689016106d3c59cd1d31345a14c7073048280a91695ed23ce48754030b7b7f4611240c8a1ede0bc6c91b69e3bc6dbfeaa1d8f1d048111e2389aff273032fdf832713d39288598a1227a56c27521aa23c316cf886bc024ac3734ba339dbb5a7a30aa1ade57ca845767e7a68fea803aa9e5a83db5d977b3d15241d0cc9d53f612d0e89af85325cf579f1dc9330a9b33dc21de537974115c51e46fc7dc818325ece5f3ffd96a063adf2509da718a606836bbb6fcf638f5d9a3a5843effa900c6addc21e579f498d0fdd640a27425b7c19d1bb2473038cbe45e7ab5d7862a4aa785f0bc865926b7cf1aa51ee5a516f776da6224e5de48f87456d426911481716bca4444cf01a2718c85cb6532d4fc85d96a4b4bed4b698496a2fb158e9a5a767d1ef8439c7e88ee97354f17201954943231f0bd7e54ecdf19962085a0dd9207462930ce49393946c565be9cc402580dce9017ff571ed848cbee1d82568fde147f3f8f67cc9e543d526a4d65b6f356eb43ab43fef557ce5f129feeca8533febc0eea9bc950184ae1b58c71459f64c1bb04675b959467d1706cc305581695d4818de6830d8492580149065e8699be0f60274accc15721188dbadc97f4a2881cf9fbdaf92bd1bc68e33d1021dc98e05bff82324bdd3bc88ffb4e9e6c54ff3203fa0346538ddadc9e571f6cf12d3657c41f586742a3282ff8363a2ce1d767eaf9c8dbbd121305f3ae4365b9739e381daa4a8001b579959adf1de70bc7356b688361fa0ac57d2d227d2e37b5c0dbd93de8310c4862d5bb76685e55567855940a75e1b501c26853e1b6ddb7245f52d389af73cdcc51b8c113804149d13e266e6226ef4aa9dab670335892b4dd511aa4e84a9ec2c5d5ef7d734cd70c42f431f7e411a29c7d0021dbf2a0722d291c132cbfb929841c2cf65ff2a4c9e2e0cbd83999dc731620334e20f8d91a7acaad1fb6ec774f33329737a94ee8fba4c48824501f73f1877fdbc47af1a16e2fa20160bb02575238efa3bc3f3d61976db9d06350434f23f4ebd74357f7ecd3c4ca1a510e8b3593211f56850b4267615d917663bb2912eb663fda9b161d1de6af1c48e025a39bf8533d95d7078d26bf373bfccd68338e7b7407a85b2203b7572ef7a81c9bdff7b6d9be5d1c494c902c0085bd3be992e7fbd11be14fcdc29d75e4f659d7a52bdaf784d297f65bff85f4287b9281a00585473d4af7f73f79e420e54371e0b22ed8c20a39f1717e56d10a4b9eb791ec40d782526b5015adc718cd2f8349e14f4d4a5dc69ca6357b87e3c906db1154896eee80e7a42cb95c5dbddab429fb7abfd74c2e1b37f4aea346a526234569b54a7a2df70f84ba3071e359a466d61a6eb3c7d97838ac060acaa00870740cc228b513d4f4924b47aaa768a681f55ddfffe45ddded494cd183e9512e0001e061c7c96134566156737d9d0c076c5af07e42190570e2817902cface024fc1401f0fcc3eec98c7352ff4b231a0a5209ea3eb8685dfe68494fc46edc368203cedc94f3b591cd2954ef03240f7ee4df4705916a9dabc06aba86c7b1253e9bddb4ad3d036c3919745443300563596baef84a410497092de530c36a3ec06fc5820c1870a007b655156b54e5f3e714534463d2b71be1a863d62615261029234fe0aed3c383af07f5e425a59cc77c38adec36f4a6ff03480dfbdd0b1d49a46bf1f0571b8319283847940ee52998b5064002ccaa770b095cfc8f00143b569ffef273a4ec8cdb7cd63b48da77c72fdae0496e3ff0372f5340ffb33f8d24fa3d023582a91e889e9acb1a45e64348b16465c688aae156cd48d3670c3f40000000000000000a2089501000000004c49c9064bfca36d780efc4c824c1f813f5c257a2476fc10d5072ef955d9d8c32cb321cf8dcb643db958d20d1b96309810354f79dcc75d2849be3b5d4a0a78c9e8b01c2f6322423a39c274646a1139356fedbed57e5fcc37d8de604de90651d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c050c851598e12027cdd9ccba1818930734605c38626068391575dc5efbc9024608ef5e960297b1b95f55205a0ed8480f43eff3efd807a38f5041b37b1afd986e027c0693dea31a5fbe7c279f5e673082e67ae068bca2b8c97cd5ba6321148122ddf3f60c8d12a975583d3b9439739285100a87479f31d4693f42dbe54aba62c0310adc338abc60b33ad19a5959db2ea59ee650ce3ffb09a68e4425db2de7d070b020372790f31660a270641741be96d444f339725ab3d12ccead101d8943ed62f850a25260fbc1f858ccb88daa79b1b9cdf7e036ccb943d9e4fe5de10f7f6220603112514d2b5f1dd58f6e73d997d313aaaf960e62ef7237782bf5244ab2e1a6eb6f902139c02fef1989923a435aa5fb3d0be38906c3e28ac0ea8dfea69762d9db05a90021a07ff1392c0f5a9f9a5a0af0e056fc1cfdf637c013ea4080b9a29dc2cec44be0311fb5648559828fb710bf6a34568f705ee8e143b8cc891f8c85135c07f7142a8021f10b5d6e163cd97db342e7e4fedc8bf3e660ebc763fd30834667050a4336c420211da5535d309f68061a32bd2f3c13207b4f842b84e3ce2f1d7cefc4e7fbdda12fa4758f296ebd70a5550f558d3e970c47670530e8b7c51aefe98fc48597de8741489aa49218560cce2ac0d01020f274c2d1074c6140764689b05b6cbb0451201e65dd83e6101a21342ed813202d9fae6d39ca4bc6981de4b99309157c07a520876cc22df7ccd7edbd350d57498374e9f009b4e63e47acd12c825fbe53bd8cbf8313016e307efd5535c5c70b551e3d0115e133f362aa19e50f6027a62de42cd0afe0fdeb258b468d3098a849e3d690332c2b9105a1c135a28394ba3d63b83ff417762dba3a0c9ac6e56da633cc65de23d7eff3a22653112e4c3c20f5304b091266d49ef23b889702b4fb5c245fa682e436a116682af0deef501e3d9746abf5bdcdb04b1f2d4372e985cb82169fc92e4a34486e5e0ffa7d99113899494dbb191b885ab85ec5fab48207e8453bc7bfb949a58675ddf25ac2d6458b295ede58bfd2e05842334a8037c8ccfc6e9fd7d410acc04f1dcc9dbd446f1b30384e0cdf83b9bfe66e132e000251034c1c37497cad54b8e2a26b750b0da3f462a40f822a13952112242e5b09513af01c49f37c6dd63616a83ef42897e95c84ed8d552b5c10e4826c66e9da1f7c491514a6242d2ace7150ad59a82a29ba60a000fcaaa2a735271a9cb8beb1ad44a09a17f5cbe74eb3100afdd1086337dffa1a5bddbb14badd8d33909cf15c107f863ebb8b81c5e8826ac18ff035e46cd3a9aa2f183a428ae428b1a4a0d50de410953a402cd34756ee94a77b2368808a9f09b50089a9de5f59ddf8043afd9d69f6a58a6c6eedf56d9aa667ee97d10e0a0856f2b8a606091923b83818b547e77d8134b879f50da87a04f70a95ee3d7b8035f42b679b89b596d6a54affe4e1b1c55551bed2184b6836fbfc52095a68ff3619dd91d74892ba671a144c24cc152a423a7c02f0350bcf40455c0b978d77d4e6caefe43d1e9d2ea79e332774e8561053a2f6f605b29591752b995270142c084fbda343e17584caad6beb885d06643e5f54beb73316cf135788f708befa5bf4e1641ff73b016618f4435db68869d872abfe4a6308db023f5a267c5a6a0330fc6de6eb9c270939cd071da688d69324aacd3c3d5735293721485f43519e59bda5e4e94d9e07cb1b347b16ebe9defe6a829ca4dbc023de08641997f9278aa9763ec73187b5d2f03bd80e68d0778e52cfef10e5268978181cd978952cb4a2f2a49921d956ef3f3b3f0f2df9d08e77998dd7f42deb50a7b5cb4848cc01d1960e8089a8513f7c6a2044092ac9a4354c1c31aacd657cd302426bc86e342306242aec06c1b5d4308282966bd1302f70c9edfffbd9cdfb7c4c105fd488698bc253cfeae5adf041a2cc79f422881a4f248bb4a2c61221946206ec081d79854565b423b34f168e978d8a691e41fbdda9e4fb3303292c5c6be382db12600ce38588d65fbae1ced80b61b4ce699dec39f1d277239cc4768acf25956dc6a7b0fab9f7b35e0dd758f516add3e1234c50c6237692fbf02efc65608a50d88952dbba5a246d05db859c7c1afa31c487c92a252470d3653b410f4fc5715a865708366e3294bb341c0cff2cd438bebaed434ce09cbb608d0b82025e6b1690f585a736c52ea183db58c4546706379d990c71f148abfa9b00df205272132a61816887261a5ac897563b876697080056d8c64a1cedfd0bd720f949460834862d54c446ab9ec0e9ec9e73f6834cf58fd877780c72d2fc3490579e7f7347a26d7a60db37962e02c045739145e45a23b372b7ab441fc516ddc96e7639eba7423b70cefbc8ef907cf96d1a099a42f1397aac6da1c201f13fb0fffe2022d05581aeb0a04a97c40df37ad06", "525152", 0, -1654978657, "c01782158b69fe9e15b3c94646f2be62866ac8b8676c23b54bb7a9771167558e"], - ["2591496e01f779ef4946e4c6a3306a1a67c09fed6ee9be8d2bb62eaad59b5be8054dbce08100000000020063c0510c9403438e2102000000000452ab51652d0ec5030000000008630065630053656aba5b8f0100000000007f2ded250201992f0500000000000000000000000029a62c583a9206af173f0a691321d1e7ec30d751aaf091d6e6cbb0ada5ae21b515fc97f19d8e867e838bbf9148e26d231be8402aa314bb618f0003f86b1470695b43895dd5239f4dca8cab37970e93c92525e0dc2f1fe636ccfeba904ddf02aa000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007017f3d8344537c7f99d73fb8c2317a7cd5c4842980a23899090b1f944f8082354296dc6e414ef556edba7d525b7b6813f87a58f4b206a34adeda14eeca48d7395c923a5b0f217eb765c7ce1d378793ad7243f32e9435348e86e105f9fcb1c32f8c3a572a5140b39b739110f443d3bc65838fa0aeeb7494e6cd99803f43da37c03114da1b96c817f1e906d61530ae9be3b454b5f81f79520fa3e7c041bb09d5623021a40565105a74381414eb67c19aa22441fc10c14eac75a40657116492feac2760b0f0e0997114fd39d73adaa8ac8e76b6ed40ded8a17c2382626087f03cdb1a6d20a444f7b482ab9983560558694f8c5b3f2787df6c5570701330d6c504fc7b3bb031ebb64187138a69bdb58a5f05d8452e17c410cb783ca37ffd43ec417880fc107020fb031dfa9377d6a7097f6edc54d6b796cebf30097964e45467e644892deb77c0226d62b289416f1b50257c94aa652364c925d4b30543602a65f37dd8cbb59126902178c03ecc6374b9fcccce97b4300b7c3cf32a89f4a4b8cf2630fd8846ab99200021dd63ac4eacda2789b9398496503004cec37489eaac9b8bc5c96c14ebeeaf12eb3c7453aa722c2954f7691e3ca5dfd8b5f7a1bfd57193e33d8327c04ab8fcc1f3a0c9ef93acc35875a9d345b9a6bb0920a78ce71a8d4b16bed8bc45e433cd4f553b8a5e2ecde5cbe5c6d577ae88bc7d31d6f6ad6c74b9bc9aadd539535ab7d65b6adef2b49a6721211a25535fc58f65bedbc163d11f765df4b19a7f81a6403192107e81a1263587be1dc1408606afa31f961029f10c2cc2ce0afe5e3c2e6632acf71ae44fa45bc52f119990395b7696bb0e139d984d08df2fe3ef723afe42dc5a8c5d64422caec4187247d64eac8c2c8bf57b51c73f539702cf4ca77353554d94d33e44f3d54592c797c88ec01ed00c8aee5aa280d818261fdccd24af355595acaec6d89845619edae744cf16544196191af37d2847c819045967741a6db8751c8155828384ba3f7d431cce075510e54c84d63863f88fab670b4fb2d53e1eefa01cb1e35b2ee12f9afb4fa24a0c4dffd1f8de5376c302ceaa2e287d41e23a0aca21e027be38ab268a8b584b951f890623e9089908e1eae5e61f757cf5de8e26a3aeac8e22ac6a746ac3d979d82e52ebfd9f41c182099059f3953fe168687c5f08a9e3649e475bceedcd855d74a8f0b111c55410def5d9c2408441abdd4be60af6d6fe2ead622d28943f73aca642f20a72796c0794d668c10a3a751af3e5a5077df3e4f26e145a5f110137323dda00ffae2af1a5993ba88e28ad1cc7ba30ad1d442e465678e1596f1832c74025e806ea88131f5a6cda2cb3de6d13f174430e6c025f7b12c4a0a9699beed3be11f4c81ff08c2bf27e3a340424b99377f586c5bda771127c40e3f88ac8162a65085e773e132a07f5e9337984403e10158cc8cdecd11d93393f7820b345821771fa652ba3ea9f0fa6970a0206dbc704ecd8bec7fb1c9850f52d273bd322d3057cddc21d647b4cb6941e96eba83091fd1543d415cee47ef967220b0eea5d5bb8d1cff5d6b83c1a5b79c93e6eab5738f9fc26d25abd1d4c124a6bebaa6e37243356c13b17058dffafbb4df2148214294ad2a30a56cfce5dd21637dad87ae04a64fba25ad93ba35e2c008180fcaa49f7c7c6bae9c539c81b8799f019801c39ecbac86a940ce68b071748cf5fcb88b584fa178fe04594a006fc4a8382343ad4e07635fff350ec09a839a89ab93b58c3bf8fc469c9163280f2a7909d088d17f71977ae81f16ebdab65c49753a69b396ca108f072eb629ca448f746b7065cf683638ac6a522f43a436d94c523154c3c20a2c866b3b4d50e6d4b23e0a2494a4f28509bbf042383d57c9d84db5fc3dc194fbda482e9d69279efab15aaa5fad3ac308f7f6438dbf57e0ff68dbd7461c5e460978860fe04f200e2025c154dbfbdd83cb6578cf3770bab63a42f1dd918dd2bf0ba22386376801f358f76a5dba88adec3f8346c1e885981db12f8c741199d216d7beddfb83ad6343a4d75a17d1f6da9465360ba2c7882c654d2d8448b423d0487128cc6456d901218b34265ecb41bd833ac01554247cf570fcbe3542091ffe458f38613e4e1fd7a5101640c3dd8445f0793a010d022d39c2eab0dd0618f1e8d4f057f6f66f4727ced018c754224c7ab2f064390a8c66dd1c462409162171d199a93cdd35379701c477ca6df65bed55f5ac07f1f295dccddc4cd596086d12e777834e6a51346c15a3efb9290b4ff0588195ee07f465fe3bc023836c8f2b010000000000000000000000009707920df44486d8ff13900463da8b0b3bbdb1e9adf72c568eea9dd4d3b404075e11e2f7d3e68e14acec33477d2199c1510ba54f1c81bcdf020c0360dff89dede24ad641c736ac2b6ec2a1545e4c72b510b1243bff3fc00133f6ee83e72174a2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e423a8003e7c7b75af165588f6046d9c102f637526b846414007d7261c09d36ac2ea496427ee0d86ab855d47ff09250e772579bcbd8cb3f35805026203c01cf6968b81f637c6ea2aa0cc07ad4648175b2c83d7713462e61fbbc63a57c430fe8bb6ec5e6d1673b12913e56d624a7bdb5e18d5d7f58c2d7e38cf08f7cae278e90030ef6ef782ddf871de024abd67c1fb2a0b179e984267ca7c9618d4c18ec6cd30603169f3405ac159a2629f3d3dea02e6a59c176c99d0cb4fe993e19340f261fb7eb0b274e21573c63e604e654a013aa0dc4372734a30a44dd99c0fe691a56e980bbbf2a50ff0002709d9fd1942890d74e9cae2553eb10b43e62c522a18446efd319a10329825fbfd9da260aa528a980451b7ac826ce6787c41b12285b4d5371a6d169a1020d030612e076ed388b74401217670b86a3636085a649aa86423c2c832a2938a3031a11fc1e335a5fc6e85e2079ade553d9af53e3f54f401feacbefdc4fda7359d80305bfd303c73c8e2aa2c8ecfa83d9cd14305d68880fd9a5e2c847731c0ad4c7e50206e3434f16529c1821cb792227691ed7e0cd022b5337d7a7f00c62202d35db2847770db41639f16e763926f2fd5e1602f1259e65164e53f69a0ffce0b067e0875bd8d7f11ef27813122c5b70ea7d831fc7b407a2a479b1ee742ea2d48edbbd92b2307435b01244237796e0a4db8ea1b87ccae2b0d47d61e87be51e3d3b273d7c4e4d1ed7ad3b88a686cf7e1100a8822290e14fade00a61e298c5d053eb3c24fbae3d3b8d62db6ab429214780688190f5e9e0ef618d83e8887fd3460705b2c45d51b25137e16eff997265358cfcb58a4541a825109f1842fb53c7a9a0345d0fe99b9f47c3022019e97f8e8ea9001cbd20b70d0bf1f05f4e2d06d3d9d2faffda86765c27a57d0a3894df7306bfd8e4286e498f17fea0ebde2a83cae20c397f00e7dd9a18cca626b2621956e52f1ea0cdd605a873bc50fcc839793462522c14b8eceab7cea075dabf78a1b7a7344dac18966d6a00889d5f8e7f60b6b29bd2447016e1655cde20a5a8dc9226e64f6f0b27956f87a637ca9802714afa945bb90010d8d00933d7bc2ceadf76116378fb866f77c83edac32853f4d2623d027c7aca05f0db88bb103896e96e5b601e77b2eef1e8970b7f76cffc7e9ea7f09a034349780ba6cf2260e5450cee36b3b148b23fd251f5ed8e0e7bcbef3b0b07ab945944a3cfc1d2ed73e87148fa185963422ca333bcf712b1604b70b8867fe11c874cef2df4ca50fee8fb7bb8c84c506281f1d8a4a16e9e12cc3b04b6ed3382e59c01086cdf023ce3ee6c1685761b3e56c849ba339d18fdf2a5fb6ee80fe309a76d4f5642688509771047ed7093c1be8df9a3f68f56ed05adeee575a9f733e7062b9c4ff04e3eedbfa8ff0629aa685436f49869ffc384df5b6b713baeba2f79867fd9df188f7324f8804e89cfef9621537682e81f6a7d4fc4139ed1ec0297b566f724aa6826f0f1a1b061ed200cd5875169a2f0ed3ada92c7733d896f1b39df8bbb72af2bf929fab7f8bfd3b79ab84af758011bd8d145d20a1669730aed3174df6227fabd7571264438b3ac18b8b0924a577c9f36f34b83bfc3fdb029a9fdd944346b69b456b75643c89e2d2f4c4cf7fb80ce97df71047cfb0b4f2541a97e45f1ad55fd7b3399cf3fffb07d5d9330ad42ddf2e3e2ca27b5c3a89401642d6902eb95ec99b72f0d2f4a5c2200cf48d8978e8d913496f2e67b9cb26683b5169dda0d98a37c39dd4a63910b621541966a885157677a671934840bc898932f0088a56b97b61cb9cb77f483bf71df80b41e25b580509395b83b172fd87a1b01466fc92346aecf5295da16c9be087ebecf6b63667cb42c3349eaae83fe846a16a291ebe4b950c00b6b5d8dfa1f917d1e58fc57b2f692af28aba4ba5f71448a3c5fe7a7947e73ba7a111b50c2ad5d8dea3f3c70c38fe20a25800c38d77a3ff75f44f19fd900a19db6f700380ff1b7049382eade90cfbe028ab0f78edd9ae77863b261748672d10c14103b55262ff8e9dab05a41a32fb042304acaa4d1655ee3df2d34ecf8a98bd2154c946c6905e8c023e2b6303bcc76ed96ca16e55a67bb4153fc4c97aec46e9abc4ce78a9836b07120f191b43e2824c7e015884b74799249e584fbe1b2a3a2af0840c4e7a0b3e434da7103c9f47ad8b46820b81865c1f92e659e98da0d48dd0258ee013c5bbdb7b0f924625904c26c22df8da0026c926d96eee94f7a485f0f4ca7e65ea3b19caed1f2b950416d9c8b78746e99502c85879013f19812a5d1c36784e2deebdcffa8f83898c8e123e1efcf9ddea754fa1b629cdc6155e045cf3ce12eed8f53f65be1494e5f881fbe049a03761018122335f1c7d38c345098326f5cd61b7eb1b45f5ed121860561c0ea62c35487f507", "", 0, -1021158988, "fdef9e51c882dc5534aab2baa2f5d45c3c925abc9ece4f08e6146050d8eb1690"], - ["372c3ba10215ddceca14fd896f9406cf7a9c56053f56e338516601c9c7e35cc07eebb97e620200000000ffffffff30d4baf013e73e095f7cfc068290649f0f19de1bc4771998fd0632689a1559d300000000025353ffffffff03e502cb0100000000086aacabac52ac5352cdc2480400000000010070ae9f0500000000046551ab5300000000", "636300", 0, -625391440, "d3859297efe9f455a8c4b8c34446aa29f9224eb2545da90266f47f75b57fd58b"], - ["e5b92041039aafd5c53b4a554797871a6cbab3f1823d583ab32e2bf21c3b40048cafaf48b6000000000265006c547f91605555f9f31fb886a4c36b5ec443b1245b6a2d13f166ee689a7558b30b5084d20000000005abacab536ac62b6fe998bb29a3ea2e7a233e8d678c087b9ac7a8321adaa9c9d81aec2f0ea2e2503e2a0200000005655253656a3500802d0222c94a0400000000001f1ea00100000000076551ac6563656ac28d18b902ae795003000000000000000000000000ff13a0594e2280dab0e6de7f8c1611a6a2726d1b5296629e3baf10cbe6c3c40a7eafd9a32d75248ca33cd581bd15083cccfd9cefdbe2024f06f4b58c6481e24eac11d01f7670aab707efbc6137f1d2a9713590e85bd32950a8d820d18515e85b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000309c804ed1a241ae1ecabe7e249be4664aa01338ab285c9df5446936c829d7e9e435b4070f0fa130263afd1c85f15754b6ea0363a4652ddaeeda1d2005c9daafe9a59ef44c5f0bbd795e669754a1725eada891cbeb62d53e216cec837bfb83f06501a1ff7e314f99de59f01ecb0ec90e82af65733ac37e55b27203c9d1f439f10204867c061be913cda0c9c82a03fa798e045892ccaa3257b9657152c39064c7da03219ee8984be7829880dc055055d30b209bb4b83de9dc000cf07297fff583eb2a0b20d25371c906a4bae61a0a57b2104d8d140b571b4cfba3a7dfbbaa91d1756eeb183431510792716187b3b718aa168e96aadaa777d16ce4e0f58bd6a3ea3803d00309cc2d5723221ba6e81de9a0d1644e9538bd833c5ab84b29522802b1e8c068d6022dc02e2a381c54e2186770d05cd49d81cb33051f3b406c77a17fe0a79b42b8bd031c39d75b70d14e006bb9442870fee16f75243a36fdf111c5b7084c0d58962aa80316a329b20cd5d574e7cc28dbcc2a090d45ea57be3088d7116f319135422b2ed4032530c9e7920fa7d9f44f85b622cf0f6436554892286b3e86919143366f7d5638c51c6bb7f16dd686dca8a77bf32524cb1155f022e405e7e08ad7572c7ae2deb3d1b881f3ef9e471685d96cd7d5aa17a6f7617c76f92c055e0ba71fbe2062a6e1a5223da6dccb358344cd876f04aeb2e480dfce94df3c0f968b8a319ae43c54c7f34e81d6cc99779beac27257e691488a83b1879c1aa2faa625743de21ebcb8ad222b73906266b6f8d132fc870879f71878c079ef2b0a130f2b4063f93b477d945c292ebb1c6b98c1059348f2e137b1c1a3c56501b992024af9d3d1b52483b98bc20ff155ca1b38bac95a19cac2ac325331487d91560bb262406f8507bbb1d7fd90dc1173463cacf8b90d07c2c93cfaf45f69d58482e0bfe1aec5255dec2882a32f879fe0d08cf9affb10639e3030967c7d271884bcbdf299ff702fe85a8f3e1e2fa20552921b24b6dcfead078e0784dd7d28bef9427ed791f318610179fbe43e3adb159bc0c69edfde3016ad593d2d6d93239ed904770213034585dd2fb9c9db651d88204c431cbd5c3369b36825afbbd2cfda2ccde0016b53b475b9ecc2b70ef89d4f6f9878a14977cbaa74594251d97b54b038b65801b6dc207dc95f52dc843148c787c9e652cc5f9514fd0ae2fad516d93020154e3f615c0902b89aeb7f6481b1f465fb01b0e4947a20fa9bbd6be5111c5f66e3c9cde8ed96c25166a9f37452043ad771129b78785db7f11b0b0c2634d24b25bf169e45236c7f68f6cbc343147b8f8f2fde6ba8b633eb9a6a209f148599e72804bfdd128df92452faf1ae3f83ba195a9605638228cc6702e523b09d94fe3c2ad9a4dbbe15e77e88ce009334831db2cdcb50b282e485254bb9fffeeda803e09cd8d627179ed0f40ac5e5a6be692aec7b5a33f70c331e133873564d41edc1f10d598ab4024b87e101cb8e6eb5f097567b1db07cd825d2c4d0174ee625bd9e695c26ee84b50e1d4c23a9f8ca6eed73588842dd72819398ad3485d8355d37b27a9fc43282bf0ee4127f3913c9f5344aa4c5c9b0dbd32b1b1fa27cb34a45bb538e488da0ecbbb37cc4053a0b0c476052e88ee1d71cd3aae8777dfebb09006888ac4bb4b390a784c333c8d9b17a31eb1c7cac60327bebb5a893dcd1fd32fc4c88d858499c9e9cd3f1c84cc15652d05f4b8ddfb7ccfc3ba4d5b137be1fb450d9005e5367b6e89bf7c2e92f23247a3566f8f5b98fe97bd2fb466025b020bd1c19fd32725ba52bb52f8c94d1107cdab5a63b4a469a923b28cf1f5eedad7318c288186c62e956adbf247d72d7d061d13ae257e61375d9f569a2a0db862d3df1ef1ff3864ad822baf132af4386cd55c8ffa634b1f4d749c992c1c2d4c04c07ea48a2363d5f537e1f3d3f7d50cb0cc221b97aad0fe62e0de11519588ccaf4d348ac9a26175f72644fa2fc57346373a92bd1fd5cd75982752ed690fdd8946e579aafd1b83bd62bc24ac31a963d09a286a3ef31c07c0a7d4ace5279591dfec8d82d02d5135432d86e6be38996f00c1c743b6c4337bd01787984520b2f0972e88f818a0c6a2a951c781afc6d6876c37877d525ee127545e5fbb1c3f1db87b84443fa27d66b17df9181ff4e5d59c206f2dff424449cf2a5012a43c9a6eb50f29eb10ca96a0d466c4c683c88e0a16be673d043d23d6b7d71665892a2f48743138bcc38689303aad868a2d99780d5347e9ba020c517fc74b758f550dd903b6c1380fb04d17aacc756b201000000000000000000000000a1aa1baa6f0f4d3b3accd85c65fdc9d911a68438a798b10c7c17a72550087d8206f1b419c51bc6c01c90441b0845fc92dbf5ffc91020eb8fb19ba3b73ae533f53e57505805befcacd4ff2915fcdf5f24cd963b9807b8b51d30954d54c8375240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002abe8e55ed9f73a1eeab13beb058fdba6d653b4c7dd41c467cbfba9ddc9e255571f9525aebcfcbf346b613fb145ae1039bc9751e1dc7757b43a9a1a60bce0ff17669a8bd6e33fed56010cb7bc80343f42c4480448215ee45b55a311f522631f8d8076d54073d5045b65c9da06a085151c16eab4f75bd49a7338e78cfeb7f770b02084815ad0198a950a85aa49f5359d4f2be5a7b5d04457d2373ad0e2079a7288f032b9957c4dfc04126788db8a11e0bd0801e5e6abdc9ff1f60413620d0657db7f00a1c5ec0a9d16b5ba013bc97531c90a01a4844ca5105a0ab64e8ead833dc5793980c6ea4df133f513d4083ee1b991d2dd725949fd57f4b6aa4e1f89af07ad21519032618162be804fbbde3ab07bf0e6079ad6b2c3aee3365ddef8424e276ddc333f803066d1aa254848b9fe197b009110e9f287ff71d97f6670c1f04a48c6fc0106940031113bd6ae0b713d4da08c70a2eba99e470df2bc790796e0777de8b4bb9df28e2021c92d8109dc018d281a9b8e6404f74b81341ce3737fe2374ce341b569b1ae56c0310248287daa807c727bad587caf9c56f187ef3f7354d6e716b39514ec0698978c29a03b309e13f9e476cf509faa1d4d9b0267d976fb77a76c163c4700fa539c7f15b4e69f36a39cd40446e71b39d17b005b012bdb4411bb618937ea8b82e7c61bff6481ee2194933411a56ef966775f7eb493fa00c8d822c27a6029145beb3c4a13bc22728513c55764b8118671c2ef5d932a7baee687ab49eff9dae9d17f893c536fa2323afe5c5fb6b2ce266bd8c02d95f8d878dbd2b44cf0fb94119d46c4b317217e607256f8ec4a2dc853615696c878e2730c7548cc379c77c4360301799c0959e8cda85444f1264c766b8e0008818cce935361b75aa78751d1fe235778bcff796b77ba53336022ab31b12c3852685f24e6dff8ba0883e372cecd0e7570451b9ef039f23f07b441e2e03b9019d7928868e87b518d250c861fc9c0b87b4313fc82e24990c463eacf75a2d33464debcd0c71f7785094395abfa1ee12bdd9e6d266b4a64801d4174f4839119dc235ce71d4eac13f2ad6ac7a88d47ef47328b8bfa9af8b008f76a8e3c00f35071d636ad82e2b10c96aa06cd159fa97f4a951013fd7a65c538b2983f12f6af76c25bf6423a34cc0f23192ea2aa684e5dd865f1765503d6b5bddd829a2ff43889519ef58ce6e9241fc20bc2ded1815a96e084da7be8edcd22e2ad566c4935fa282ad3038f80baba4fc60811fea9d581b02e489dd800380cd1644e3f3228e358697e6f118b01310382b6a0eccf471dc355f8d2fc1206abb2745fbd432ba0607854601800a039c3c7da2ad91a05dd7096ee47b31d033aa72d5d647291ddafc9606e188e3fc4b49e3aa9cde7f53149c91ba0b0bbc03b3104a39e5756c1a480227095327d4093a236f77ba14702ae1fe1da89daca648b9484e64e7baedf75b5425ce3fa2c8d13097b3d1b1ca27113e649adc944da0ad690e794f97edd4bf319181ce3017a490d5c740348242510a46ff7b67453815bef259c37b52aa8056c0c4a4f7ac103e8cb7ecc9e2a945392b541f3813a1df73d161c10691d9d86cd07d8b8616a54bfb63a919e158e9107ef5ba4a6061552445982c3986d91b41812077d84891be618640d3531afab61b769a92c4937281e1248e87a000f8f70f558687f38ada579c344861a8c86bead638ab8bf9070c3397f04dad50176fe1562b7954b67bd740e32631b885d8f6e8c99e5aa5e0c63bd5214c0a942ca7b8b2f130f75d06abd9f4fdd82ebad186c81c57e1e7df267af906ff2dd05b1460f4ae1ada79e1acb597fa787fd44284b63d8173c677b11d75875da65c3f3ee35b0a8703072b88a34e092b3ca255b8faf94af42370870199e9415bbf92012d51ecbd8b2e7120c6dbff4a3176b7f85a3c3b16de6efabcaa2cd37da666698008fc4cdaa4f9791c9b9877c89469bb285965625a59615500758bc0d77afafd552382fd039d20888b236070307d10dda352351b94825ced5f58d48f1339b459c2b835fe5c7ffa5890b7708eeb48b279331956c4f767b54efd8023d9e47577cd7f4ee7495b3115fa77656a6f48867cdc3d2d04ff9d1bd2a9b02b1e550ddbd4c057441277dea9977291e30c8d2b43bdd148a69b6b72d1c1464b4247195c0d4d410777cde6fb3e3ed03cdbbf4a4bae7ce16ad6f619a02402bb2d6ffe1e9a979ab131b152d7d4843e42cc1169f460b7a492ad2f856c3d2555037e185125456f2fdb8bae87502c7300571480a88e3859ab04a603de2c9ea1205ee1ea5daab2359f30654bdee9b3c6b613f74cd3b4181553cb73f4841a0737a4bb8f9b28d4e1a51813d809880fe958a4bbfe0b0561986c97216eecdec45f3e8bb12077d33b3f1be0b175e282606ce4904d9962d140d269b32dd7050c", "65636a", 0, 1974178227, "0090b1b2f97a6c181953e5ff474d10cac44c7c592647bca80937113871c722ec"], - ["9ae7653d02f383d092cff961707ae4f008539a8242ec4054de6d8c2eedd7a1b298d65d78e00200000000cf31fe50400513e89d4c95b63dccf2ec131ff00e222a2480bc97b3ec82f1fef827aeb7720000000003000065ffffffff048f39cb03000000000951ac5365636a536a6a86d510010000000000f9e385020000000008ac525263006aabacb88492040000000003ac5163000000000100000000000000007d012b05000000008fae83ea692172e2c36838673efc669d94c5705b64697f2100ffee0a36db3d19819086a60f876e6de94abf3fc326ffb91389f91712ca6f181a909510f78c1144978664f6e9aafb55365de91e6ac59912bafffa9f7e8056d3cd9860bd47f740f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a098a1be624af328e4e212f6b2c347e7f11600283e02008ee818949765d60bd749ffbf32dca121e6f8977899ca8870ed2945a0b63a7ef142751b4e6e0ed2e00ae625a53d0f2c6e0b896874765a73e89480d0147ffbf49c3d81cb3dab9b496a68f4ccaf0d5ab242f04eb55219d2a356b27c48f633dcb2252b16e43b505dc842950216e7aa43af82f0c07b524103cac64c5b3579c122618a001045ac39814a108d70021e1b415d68dbc7beda57fef2f3e5f13958ffff0dfb87bfa0bdae28d5dc5fbad20b00a8e0df526bb69aee4692231171acbbd2a07fe83ad122d5aecfd4dca63dd0282efd4a91c61be53c8ae361df75ece9e2db0b2ce98bc934a33490c59f52d7792c0224aea0ad39b54ea832ffc8b43755ee54b7745ce51a6e1a1cd83dccfe9b7c95d50315ce5c3aa94076a3d669091e8f17a32de8b27736675f7fb1ffdd52d4a28cf8ea022974b595096c3c3e1d71c95359c2dd4490a357a14e00f28bc2c852715f0ff6b9031b42ea6d6352a2859f535f7e1c760c966a7146d408e4df8847f8235b48426567031664b2e5f971141e5de5a167c45beb0f385ad6bd86a8abd35e3771edee30f536e48167a2d9ebcc5138b92c138b8c42ad30f029e20c60ec19471f2495a7a3e0df23e39bb13c213fb28ce12c7aaefdb9d5477d44f23ec11321629b8f18a6fe22ddb85642455c8500968eddb2957901170dfeda057bd925a993220846a11575396b75513079a4f0ce3291d6b135fd2edf727846e8d7a29665f0587641d6700886b31461436dc87566bea0e4b3245f9cb802259019f43400e0b80c57b9b9e8a3a3f688c02219275491c3487e8a517f5a96fb91e06b4c5cd3033f8f2b6288dded4b33dd735e1043909bde729a757e4a0366ae27e97ce066a32c399d0e8831aa0204a927b097a5e5a3589e598adc04226cd382389c6f50e9f831644e262ac90f5a0fc0b5a8ad381dc049935092adac3fe591e850bcf83c77d0cd9295d9a6df4d58a9cb5474726ffa5be89c895638d171e3ed86d9cfa337bea978f1c31cd8afd07139a2ec69decd2b22250533f42666c6f710cb1f0f2193aaee9a3467d0b06a912e16878d22a15f7049f24bfbaed3b17939a08ddc5f6926881e58113519cc08d277e8a52de283aea1d2191cb5d79a0312f76b86b64416acce4a414f129896feed05ec2da078bfb376648debf78a065f5b1b4e5849a85e4d594a61799897da1ca4443dade74ff45507474ca2a129c2aeedfdabc0c3e57bfd998c40c73268d33e7edc3baa2d78d62417922b580e9ab73649238a562a88258d1f7dcc606c7ae713a8d3ab1f1f25e7c3960eaa625c8e5ad4f997354397d4ba1c5193ae817b001615fb991667bc96c133c8b20919e27258b3b6f4b8ccf138aa756aec20da041099d985cbe8d09d231298a8278da623b72e7845d780ae5c39e2866c963b82c1bbe6dfe4740b60aea2231ee760fcb669f4c6c20493218859b478c4dcc031e2a13cf08c1fdf800ee77da5a6dfcc71e77a6aeced8da4c164cb3d1e561d829f095cab1564c6a033b62f7664877706f64cdaf5e3cc069fe718d452b9f4dfbbbd6120668da6c3994c73d7d501e5cbb1371f9979076d7f8cab4dfd0fa441b6b86a12381132c7cad0ea907ab7e603fc2626ffa8eb0d13cec932343453820211c14b91845deec991ddd052c4f7aee075a237b6261a17282da00735e7ba98269d9f090c8ebecec6b09ca28b8deae2be1a388fef96056493d8839f7e8d1cbe2d4b221bdee9ed87b215d12e828e08e3c38a70d4ee3754ffc1d98c90fa3a85da519396b12c4d35a049410ac3aaee2e1b240ec8bddaa6d2617dca488a69f8dc8dafa159f226f2408f4f85896702897f9db0c931b03342fc03690ced9629bde0969b653547b864983b29bdd2d6136c945dae9de66661a745ede4c189e09603d4f81fdd56ab3917997da8f5a9764101a1fc91aa573e8794dc61d52391ee8c0ab9e78efe214880ab5648219b755f46e4ac8e4e74d26d08ca478619cf487def0a0b60d0be4186f857042d98b2abff51fa9fbc92054bb69b3810f10f1ee2cabcc91b783b22ec0b985724b4d852c4abf762b50249770ffecb75c84025d19fad665878dfefdfefabab362c29bc94ad278c8790b48eaf9b487780257f395f0e95fc706fcfd76dd0ad8bebaeaefac253256b75af242844f50de36f999d4d2e12136340268070c0df0fd125006f08c34df9f40ef0b2bf3261809cca848aad6d8a7425cac660b41dd8dcd61425fe8b8d2104aa7a4ac3bd276ddbd89104f253b091b3dd2186ff6b6a951689cf718380eb6d33c6bbe7312b1065f3c5d424c8c46b5369d6dbf0ead5036e89b97296ee9b0d1f361c0a3a419b6da49a6fa334fbef3ba64cbbeda9643453c5f9eee5b44ae5f26bd635f6a9ec6f8025d0e89ce9c344a4600b64fb05", "", 1, 1180931806, "687bec462be15f9b68b63e325599248ba136c8aaed0682052ae3ba3ba484d01c"], - ["8b360cd5035ea33dd7505591825b8c1187e28d0b0471d8cf2c86ced75392fc10fc73ae953e00000000046552ac65ffffffff6ace879ebf9e3b4ffa24a1669b7729d1094fcc2a1c9e55cd2aeccc18ed35a726010000000352ac65ffffffff83f640fe69af18e18eadcbff5d021ed9fd577f6bce8d16c6a61a24c07cb9c8f80300000008ac65516a6a6a53aceb68b25303ca0d9502000000000552526500acc3eebd0000000000020053d1198e050000000002515200000000", "6a", 2, 6510966, "03a6f52c6f30c3a4174d00a69a3d68873bfcbf85bbf4d6cde82913d1861f6bb8"], - ["85b6c6bd0326c462ffb480400fc10122e10d97471033562cb13545151cf1352b47327dc52803000000066a00acac00632944e2f79c0a84524b37c693fa35db21b1e1c50da26b93634c9a8400d00d4268a11972490100000006525353656a6a062ed4446745bd9c642328e4cd1e6c69593da50876721e795e1f6e08f479c306e98647fe0100000005abac51ac6a007b9f6d021d87ed010000000005ab52ab5152a2ee21000000000006acac52636553059480b2", "00636a6565", 1, -1617374854, "14352697bd88ddb3cf213e8d11e36871146077d6a7d18e70a573d6a915510e30"], - ["3aca2f82025be8abfc7a26b083e945abd2f85354dd40864cafb74b9f190d3d27ec2ea90eec03000000026353e83a65df094ba7886ef61911260bf0e1fcf1b51abe904177107ad86e6195213724aae1400100000008ababab63656565abe193e2b803a1a1c205000000000665abab63ab63f0a680030000000009ab5200ab0052ab5153bc4769040000000009ac6aac5152516a536300000000", "526565656a51", 1, 886859900, "74feb3bb9f5ca1eec5eada7b163d915b7763e2813fd74c87b306df39128e45ac"], - ["3af8de1604b81bf64aaa19becacdafc83f1bce21616e422d4fe71e57021bc6d4d61caf147e0100000003ac6552ffffffff4ea19dee3486148855977b161c6d7b1ea3b86cdc0572339b52ea71aceb47e71d010000000965ab53abac6300ac522ccbf575575a6067dd9f081fcb21444879e44492572257eb86041b05677ce2b257044de5030000000900006551636565635129bf149fb201814cd135a7deca0c8579d1777cff1012e66c9795daf83241178d0d8f953c010000000665516551515358c530ff042ade3604000000000042fd700000000000025153d3d65a02000000000151ae421a040000000009ab53535353515251aced5383af02000000000000000035a9ed05000000008a9d754954f2ce82940f09d2931fa480dec084ed3d89321fcc4c0b65385dd37a6a6e1226becc83b07cf29b23cd97960f53437925236300b64881d303e2363877b6c34a42d7e2a9d2758cdea41108858f9081b78eafcc399116254b1c69c525d40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083bfa06c2ad83766e68fa47a519b6eb2ae633f9a3431d6b04695f4f832e655e06f4498241d155bf9d92a8a04fc11e1469c9a697ed68ee57c79b53c277c2fc432f50fe77464b4981c3efcfd3903588f787bba7d9d765d80da94a36efddb3aa4b90f28a8d923db4c23dc025d96cd2ec4681386a4539fc50387e3a345ea70e3b5e403018707f0994db3e5f115e3ef36f8b68860b5639458e049577d2f1206d6f46f2f032060750b10f6b456c281abb5734de953e502d8c55c8ff140a9dc1e38ab3bde350b283b9e4d561c9e94a0b71bf07fc0a2ac510bcdd370f4f938d6cd9f1cd2c30c111ce77ba708c283e1c940fd84c6a82e4d9ba372717ed98c1fd99e75e895b25ea6022269b07ee13bef3fb4a80ae6a976c6b20fe234c4554c4a91bdf4b80f9db788c203057127605b0cf4a0b8254db799bd85b7dc0a7225b6e2db78f5a5bf8aa87d35e40212c37c91e6d004205c76536221d7e2a144b5930b176aeb0db4dace15917b93bf020d3a1d7682bf5c8d90df733b83df4e41538313c6be8019625b87a91e781fe0bc02211050875a782770c483e11edf308d20fb6eee8c1f779c96cd5e279fa342dbf773a8f0a3a28f351fc3b3704911b65d7b160b3693c737cfd4f0eeacece751eb715a35eab54bbf8f86273fa3d63e1337ec68fe13f8e2bc1e8b89c4aa4c5bc5c976dcc51a6d5aec4f32f3b1ec24b25659a858b84f33dfbbcde6994cf1322c3a77cc5c3529d136a2b6156e792cb7746c8ecb24b40dee6fcf076265d7b5af774d374506a8af77e941e6fc72b4bf0ede5d4bb44cd29954371c380fdd60869c27eaa9decb626c85ba6cce8f000b3c67963b7081aa5460fb4898ff15b87ac322501900653e3c470b27c9300335d05d81cbed4b2ff9d0c6ebb15584b0badbbc76ae3a5eb0ad9362338f25a9ca4141c756034df72a404b240d32bc8fd66679cf3edcb2f8760ec26ce8e94ee4c54d069fc169c162d82f50059d1df7890cdd8d0ed8c8031f9e60ac453d0e341b8d269fc48c47ef92d4184f15f17018179a23f3b73464214bd28c73a8712914c08b4c157a6da8b810076ef8e0f9420eb6808c989dcb4b1eff0ebac89dc146f6bc298d3e05bc42ca7ab73c6b0a159674968e03e4c4047bba279a1bb3354009be544ab790d216cae361e28395f7c8bd91c683bfe6f675ef166b125ee68338d6c578c51f398a1352bee98bdd08e8621effd286dac852f668145143a851ad4bedc55061ba02b4f126a2046facabd42321070982de337b786c8a9fa071c82496ce16b70eeb54f55a1fbd3272ad6b3b974432bef4d0b31fc4b073279d2b720198a02e06cd9aa5541b730ae2ef319d59e0f35fff9b17952af447b5e4c7f24b34183d8ed19f8307920c8abbeffc932264d08cb40712e74f4580a76cbe1419927ebb504633c4224fe3cb4a26581226c5cdcc969dc6ab2faef350e765fbddb593fea29a1517f5d5f9e89eec982daede2533debd8bf1df7b481622a8aec5f069468233f5b67c757edd6812b06bafd81d40a7ac2c3e47762aff6f20ea6f2d9646a8b30713e06195ee0483286f7a64013a783967884ad1a35b84918aa1639ff20a56924d050d96f1a0c8171c2f07398b7d282474964ba90a4884b11300fccdfd5a0d5db70d93b5be8b4b54acd939cc0fff399291389e98b99368138a60149d28fe0337523d7095b74caca182a1dc0c698f3edacf277c0e2f653187e2dc5d368d214eeafb5c143815840a4d13f79c458b3fcea8023c1dfc58a4ae7112cd866d7c4418702105f7fd50f69773acc7fc59648a1a3b6c1d0254995649bc940c2c70a152f73ead7e01d7500de0fac705dc12be0b5a0285d7f1d8b2354af24bc1e197ad8f2559da41f1fc834c3e055ee28938d5b9d79e9ba88e9f5f72707e035bc3bebe24a67c7c15d9eaa5dfc4e446e3e9013dc47ee77a451182b45448ad6c813e4a87c9ec45240a423294c73e9f84af2308832dfafe8cf0cfbd8e538db15222a334b7ae2517229878c6b79153256f6da976e1685a182f5ee10b9319a9d85f94311ed31077d5038beca7d9b65ec790afaf8851ce0b8bfedcff596246012ac24ea083516322f34aaa4bcc30195e726763b5e26d58ace989e1c13382c8e87e42b976dc4903b3b0a1ba18b665e0087e878f4e20b72a015519e6096ef3b811595f7a584ba3d8467b5885f4b557b9d04d69cd4e7fc62634c64465d8bda858921bacda980137d2e80c9c266b57c2d9c1787cbc60462c4e31ecad90d18f3cc3f3f310dd4918923f29a35d9dc2fe54180ddf6ef765058be59c0d6584010000000000000000000000008ddc00f45d7eb4f360a5ec68b247c93b6e9644cb1d01fc50b65ee143bd07101d5e664c19eec1e864a317f05a950424a300d5f75dd5e88adc0ae73e94c175b15185771ea6c782286ce6ff8e05570f844e5cb3331c2cd3857ac1a1a40ccecba7c800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000906d9819253b03c08981a2f5ed41446a173d5aefc063f606fbf1548b5f5394ed9b5bc87817140c8064002170eb9dd2f1849a98021ed55b7303bf652b7760b8245da6225729209bcc6d08af7828674c5d367d152592a1f2169a510a44204962741bbd97d4f5bcce824011b58dc212a3782e94011523417f7de61c65a3288dd7ad032aa387bdb2cebad88ae8e91ca85a7875031c897c7066635edc3f1471f36c54cf0219240319ee89162a28fa5084680343c1df93548dddeb7107c9a34efdd8c5f6470b0cd5ded935bf4f18a9335a90e88281a92818dd9888432275dc021c2e38feb27c15c0a5cf4e22f75f467dccb489c769c41a78743118924fbdfe1f302208f0d4a5032a630a728af43dea94ac7e241bbb42d5beb2f0cca3cff8557fe1ee61064cab2a022b4af16cb3278a31e66e89c96a9edf03f2dbe697b5ea3d1e579f92889b6cec91032ffd26a47c3b1a07ec510680e77485a41518a62e91e665a88417c8562a63978d0314ded6c7d7f59fb6199a269e32f5e2bf42b499e737b4408d9eeb59697ea2bc390224b2dc1a2310bf2bcfa9e0a47d2a3e3db1b697ee44d30536bb44b6aaddb510243d54bb0d0f8e9d104d43148308be6669f72d8758f285bb8f01a13baa0992001b77bb5baa991fb15c2d5ed848b728d17ed60f554831049d649cb2e1b6d7d0a6e2f2e71aee47bf317f1e0397d0680e77741a219030b08da08df36953f164d087d78ffb5ea5ec7b93ef6f4d79823a3f58eb06d13d288e68c08acc6298990447559b223cf4142351e6274cc4abcb331fcfc24afdbe4a33638c4ec957a1c021a7295499d2fbd0860faf0f98986310b2bb857baad2f1c9ab69d5ad4f7a3d71b3bd9fc49a7761ba6e1faf23eb3c95ef83a072cbbcf96b0e50f24879b0d106bbfc91b2f188fe547204fa32f631d5be1b1e080123801fffc4106c571afa2ad7639b2f59d1c2de8b9a2911c007aefbabd9773e4158a4bbd77d1a24c69d3c8c9d44ec30d82794d9d2f2acbf527c0f20b54a0cf321cdee518995521e7a7cc2d9d373bc453c2f3be3f7793b941fbdaf0eaaa5aff96d8239ed1e20242a070b2bd1f290c2a5f853831d0b0b344b42b6fa05052988a7deb0eda52edc1ea288290dc05cfb50b4d9dcd7cc9682c3b9e74706ed3e016aea80564cdfbb707a76a5cc629a826e299af611e866cfba066db8762f3b52c58ce041610e4bef5b9a4a2b015d57c170742928407a0fb01211568e51c77fa3c12341c79114f6cf784e223d1005a5e2666909995029db468c9b81411ce547643fc93223f9ab4aa215f3500ba18d6d2ac561af9907e66d2097d7025055d64638fb166d9e55f3f57dbea96615989404b447c5dba4b704d3a4852cd905aa4125d6de12ab30089084887d4a78715c97204a86e31565dbab123dd5a15dd76ad22b7c73fde992da504a32a25852de3668b507a4a8b30364de227d8bb6f38a57f896b439dddd855eb1c89aacc6111281ce8ead5cd9099407a8c401b97dc13d91e5049f7ac29f50935338dda3df66ecfe06ee362f0fde8920cf39775f008b68b9301d605c35a11fd26f0114acc7f2c2cb74a76d4eca019a30cc4a4c8e3871fbff4a7f009924b8192576f87187eb7a302fd549ad077141f064a7a67f4812af4b1920e97e3df279104970592b5f7f2fc746f92dc8d1e01294a77e254f54b41545a7dc1c690669291e58667438a26060f5921823af7eb2dc9d35e2006d4ba202e2884ad439dcade7ea96d6d7ce06bf84d1d821d214635c023c2eea44520c74c06788a3b0b0a1acc9189ab9f6a8940afb1a3bf71bfe452e5a3161e3377a56a61113a24396cf410dfef16eb220e5f0a84b0cc5531d5155723c39f931f8fc82f05087fb6e5cfce2ecb709779619612a465853415c22b1a2ff07c0d08ccaa9eb2e9a371cc8ee4726331d2c744846ac535f2af7a526d9e8178743ce4f730ee9ee5a55e7fd381a58b44b78fd70f3f6dfac3da4523bf271844bd0fcf40497ebd23c91303a6f0717682361e844a05426d04443066c5c0e1696e1c73d63c56b2e5e0ab51ed1eb8598f72b477760f2564fb64ffd05696fac0acca72eb1a6d9a9f2cf3b7d54530fb6a651582b1fc0e2b474f3de7579e914293187e93f1045e22ce609d1e84afb535f50240ae71ec6741f7c88b536e1fa0af794cdd2145d23bf752cd12173362519e1a4c7f10011d67d2c4b83146e4f770b5207b7cd4f7e65b3bd8f3b608fd112a81a544c63a3cce146f0acff46551b54b27ce9d947a5c68d5d6dd0e20d17291325971f831093b2cc2a7f185e541ce8770a2c72bd689c62fb17e1a842ba5d655eda51a418aa181d6b3b37390e49678f1cf2a60a9050d0f1a7c3335d623bdb565310855e27201f43b1ac8829e7b06f704455cc7186bec1d5f4e06166336e5145fae4026e796cce9adcc9ee0d", "ac52636a", 2, 780349869, "deacf13c09dd487d97325f96db572b4d82904761147342a01c968f37d3c436e6"], - ["75c726d0022d14c59e3e668f2d75fb43bea5e125ac0cc3f436875a610fdb76b67b444c8218030000000500ac52ab63ffffffff61211151ea9e8839249471b25e25584f92de7963c5ace95bad7769a60838f71400000000030052acffffffff0429084d000000000004abab51656eed95000000000004acac6363c61b1a030000000002510041752d000000000004ac65006a00000000", "6353ac5252655365", 1, -1425864140, "9a0ee10c0eaf545b8da697765107986408ef9bcaded9ceb4e91ceea0d9c788bd"], - ["bacbc0b703de80d482c996a1cea6b9e85798da6e6879f0b7571102a82dd62173e13ce5b98c0200000000ffffffffcffe4a9f7d16f8dfe44aad3742c758c1fe8c0a6678e572b15a302408e6613d1e0100000000a2389cb4ee1931b5d4a868e47d4159fbdb379793cf58c4bd5d5ef50dbe3303aa8ceb52170000000002ab630fb922c10131284d0300000000035200ab00000000", "0052", 0, 1194006058, "3edc8e198076c84cf47c0dfd91bcc54ce75b425b3e6976d0b77ca32626840515"], - ["9748908903633a6c318997ac5fc0fbeadbba8c9883917a1d319483e32d3cf68a858f314e890100000003ab6365c92299044f6fec74a4b0409fa962d46e7110d1a3d70522076a7e48b5ca10ad6060d7cbf30300000002526afcfa059b3dac2df5b604f55b34b78f759ab467804a63463cd38b0eaf0cd7e949bae4176f010000000152ffffffff02cefa36000000000004ac6352516c976c0000000000046a52ac536d03ec27", "530051650063", 1, 1537933915, "80ff07ee25278f799946a9e4ca9b37b56bf3bc6d1b0222d9577197be4a3b64a7"], - ["05d98db603efefa517ff7d7a62164b194d6b00c5000cf079c8628d062428067d261a0f5ef60100000003656353ffffffff0c191f75ce8b3c961fc5a08592ef3bb7d68069aa1bde0a938ac96daa08f653cb010000000153be739905d56299f3c5084d37470ea0b9cf7d3808e754055ba28e5bc50907fdb51cb039d6020000000463655251a685233302a8553e050000000000014d6d050000000005006aababac00000000", "6aac6351656a6551", 2, -1084709125, "342d2432d08176182f60e15da6ffc903b137c7865c2b4dd4fdda3feb71bbd7d1"], - ["f06e13d801f99431fdddbaa1441166509e4138d9648811a9500eda786a98956a60107fcfda00000000085253005200ac53ab6613c5e6029baf9305000000000653acac53ab000c61690300000000056aab52636500000000", "525251ac", 0, -1508217707, "0471c81be230bbb98d78b497a0183803abe3d4c0fc0980667ca5e3d4bad20b46"], - ["af037f1902505a2a4a167973ab96d03b0ae562341a44d8cb641c92137ad353fe0002ad889e010000000552ab51006a91b7b3ecc6eb1d06f172ed9ee369c9de5ec312de5362edc8e9ccf517b416be6379ac34450200000003ab63512c3079ea02b590c90200000000096363636a65656aab52ce893d050000000002635376ee421200", "6565ac655263", 1, 1248727546, "330c54361d2d14326ea9c612baa1f9e6fecded397db2d59f20b95aee553290e2"], - ["bbb49e5d03f982e0fa32f4946e60f3db377a941a1ac45c1571429ca56966576bbd353ad5f10000000000ffffffffd32a7f0792039d9388b52258b73dc16c60c93aa4572753c9df240c36c31e9fb90100000003536365fe94c1e30619e25edf91a15b38ae733828b5ca3bc2778e0c96aa3010378a2464e0d6b4b200000000085300656a006a00acffffffff01e262d10000000000096a5165ab53ac6a51534097521800", "", 1, 1410944772, "c6d9687fbbf41cf2e946adb7d8bc1673a3a960956767733a300d1a992ad8bf68"], - ["cf2e43570299f19a5f9bce98cc2cc77a22bbfff6de5353a3fb8b1284ca8e7e066a31fb44f603000000036a656aa183a698d97013c0354399a2120b8dffdf8acafb48fad94a5afaaa2f51ceacad4eee5ba503000000050051656300ffffffff03d8c751020000000005ab5200ac51edc16f03000000000852636a6aabac00634b7239020000000003526552000000000300000000000000000d93b00300000000675a46a063fbe7544f1440889554deaa0ed281ed0ef61f5f35d256d8b3f78201e308a8873e7f92704076106222cb9a00f78c0d0571f6871b29b1b8cd82718ac5bb21c2a464c1f7f1d630e7aff94e4ad401156e8fe92229d45d3e91be046b97f100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000867bbd8d18980d2c7b7355b3a2f1b8ced57e25930aa11f58a3ab22d23df67b72789d13c9adbc4cd1673cd3cef15827060b3e3e173f98490a37c9384227c9ef9dd6c85ce0bea343f770dd013f279e53213221a2f36b9b1fa6709fdbe8a1707a74db4b526841d4f80f99cf9e97e35bb39356a88009d76f9af8809a2eb53ac41cbd0211706f7b5055a835c05f1f3ac8459249b0573d924fd1e0737bfa74e43ce5d1140321c70e88953501ff726c3f146a16c059b27698d951ecf5d827ae0980e0ea4d650b130ed349fa668d7852a983873358b7cab179a6e58d3348bcc445bd383ba29e092adfcef314439edb2c36706327afac821414c83b362cdadc85cf94785efe73a403279f9fdc6111c07921915698ce2061fad8e57893f098720e13ff00c5307532530202e67759d79c48ffdac5d5469f2c70bed60fc60c6f800d043c52d3bb3741427c022f7c44f1db0735a50cc17b6ba48bd81906982101c63a1830b182de56e4eab24f0214fbd81875c41d42f3eea60768404a5f1d5e56dac6ccd1d74a6822b56f358ead03203d056fffb1fb3d44358d7ce9aca780eef910aca3632f34cdfc90cc9772594b6cae24bb497cdd0eeca0e808f352dca9ead313081dc9585a36c2de9f4dac064fd3dde45c38f4fadcbcffdd4be1b41b62c5c55ac0b31fdc1b91b320ff8efa2225f7512081085db81fca7213e154d290e5df0321acb44381d9a859706c803c8602c21226b444d5b22506a7c35797e047b1748051cff732c08376fbca00a28d5a145da7036c0176e68fa82a1114c1cbc0dc2aa4e30ba1686e27356549f1ffb3ff7b447f9cfe9921cf806a8f2ff74c12ca7b93778867b6276443d53233be63f8f3a086f55fb873b30f26236fb0932aa9e04480a66271481c8a21b14dfb7bc6d24311fbe86ce6c2e87a58d111bcc485a27a3a4502a175e4b205c1e1d18d290c8f5c5faeda087829a6ae5ad2a776a8fa130c7f1c307a2aae76f99d952b416ae6c4abfed1b8033fac463334c387b25ce3cd87b25e392fe372d0fbda81e79fe857006a50b6c27fd62ad707283e462c03ebf3aa1a322024da05a426dbccb1d667a09d0295bed7c97d51d235257bfbca068d8942cc3e5348d7da456f09c6513c329de8f5a18d869b0a823d358fe56e1e9078d2d4afd97bc3c624eafd4c8d5d4f934944087926855c00633acfcdb72fcd1640f53cd4f82a0e91154db834232ade875b013b465eef7cd88603477981e21e42ccf781aa1808c3ace2095bed821921a5e8bc53cf5fef1af297a7febd52fb735d2ea31b60bd17d9773160812067a500cb8f63d153a05e82563db858cdc12d6541b85f837aaf53a61c1035a81a789b79f810351c96518fff14f0c36e4268a179c2bac4dc562cc1af6fbc0c8378b836a2ddac5cf520853f65099ad461fc203cb791af896474e6b9a0c70f773d1d1bf4304c2e094b6d7c458ae7e1f6c3a11f3ef6b5563b50e7f946e27e96e1b72c4a888c8adad03f5c585e643af4526bd45f8be515a8d0b15c2bbd21eebab7d6093fea59722072acb44f0efa9faeeb91cf101431e2977112100a3e7376dbfe4fb9ec9a41a741400446cbd883a27168c1698ae5cda69400550d5b0bd6e0d78319c20c913c7931b9c2571e0ed8f58f47da06c3b8894f51abf1f4aef2d8e5849f1b36b31d41d499f35cfed7dbbdd5894d69cfa419312379533ea15165e2229cc72d47abee19f944ffb0cff4cd4044575b41dc1efadc165cf2c2165026b50d639f534897ef8ec253284bb3f9790c3ba344c1b35015f9f2937ca616b51c4f18a989d2e8462601c7e26436cca3d3096dbf78c27be5d46c384840bb413cef273c9d84bc87141577f20c4f9efcdec65787c42d8769383543b91c4d882e795870cec989218a80216def7679088cf93b6844f9081a59c2214fcd9b7f068401412f4c5197f664f87dd60087e214507c3c0141bcb33a2e384f8b17ee350a1df091251084da2828bdec7d222bc582f30e176bc9d8fc5e278ce1ab40eea9c67f1800ec801b96cc20816533e15bf09fffb0431ff50128be4a976c47770e99201a3f9c7ece6c1e2019244e0e47ad5f22e13b3a3319bf98308c7350c95c709ea0575c149616b99e1b0f03084bea4b6c62e739d3b3072fef0dc15adb417e0bee7ec628faf2eb898f707d77bc0f750886ae8b1b02dc5f7ad1a0868ed4205ad729c9246561759ad40f7b0e660fcb5ae5036aa055d2aa9b19d39c6aacf594a31067a588626a3866ba41a508d9b35450e3024e767ed642b9281162aa2915ea09041f4d75a12c000000000000000011e70c0200000000324af9b0dd417a75c282f4842f088858da64e6118d684457df61b2d6ee472916331ae2d112ce18a8c70925447f96e9635e38863c5a9dd4c6bafd23de33892df0a961bf30864d52b592dc3641ba4fbaa789763ad2ffcd416af18681aa0def830e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d85a6c20a91ad69fa700ac3ebf5e5189f8bbe11150255f9787d4d4f9a688ad3ddef84f7b8320e833b3116509e5e29c4e5aea58e79988e12c99263db4c6bc595c4fa8d39f9dfa269ad49428ba4beb22c9e2ee3207a8b5b017a43a8abb7db2e0fb5cbbf14a6808af7caec6e31385f706129e357602717ea76df9623831c95ab9e20226b9d1fabac38e29205324016392934e642ba2452ba6626fa6c05e9af4eef0a2020a2088d8f1a757b172da156a9996a4503fe0278643fb2aa94e5e0b54b062962c0b2e83eee8ad8a3dfa787077d989b5b72909e4a03d61ab977e03b32a25bc60bf0d282a797016717735ce5d8c553c7241b9d144fd5a9e08df145e8aed9d047d3b1c020862acd25d7cb7470ba6896b5e6a4dc76c884d13941ffd6c86cda3ded54bcb150328335fd2f1ed3c09b38861eec106c56450746dbaaa46caf2b481f2ee47d4dfa1022a382ce342a699f9d8779fd9bcac97fee0da808e63c3af3b0ff4437751fd85d803287de5b7fecf5e4d4dde722195b8577600cff028cf3346c00b634e1260301e6b022e7330ca21769eabea32e645647a5af2b001a2551eae3239b0538522791690651cbec157904a423482e875cddea2d9b65ac14c49117a0ee4f7fa8d896307c26bb0fbb033a38015b56128b1ffe211e7545f38b69a44c40c3abfc195f02bc2bebf732870ad7b0e1add15f54ecf15476b7c80d342ef46d56ee8f63d1de4fc1daeacc5e913e223734dfbbe22fda356c31e9156aae3abfbc54aaccd37303f88b187f5d46b726696532a6761c55e95ed3e970836e897fc74ed3987b147ea8cc9b70cc374fb68435af4f264136b74e24a2dc5c686aab13c5cfce7d93e2d5e37e4305ef0309feb8953faa7b81aee775c45d1f7a4c080e93f7ad7572e86d5d15bfc204cbaf14c1adbf9e1af5224200037d45e78aa33407f97978a23f28f68c071b2ce536436204b0509690b158e6241d28a4b0c4091b48aa24694d3244d9ee40c9bd35e985b57aa1e684913e178a21b8ec56597c0e16c198395b95bb403226d77a815aa7a5c1ea003172c8b0e6c2dbbcf66de811e8570cd7a129e0789b65f7a671266f0b0eecb671363b13bfd9d9e2591dc76c3c0118cf129193796d17c11ba9c9aeb2043ec04b95b6beca0fbbb86ea587aced5d468c98a94de7cb7cefc16672cf12dd90b6e84941e436ed6b637b1a23f20939de03b0f6de22c5553dc72395d6f2234e487ab27d52a1b7c075534de2516ed25620e8b2792390b085eb17234367cad341a9869f61a75d32951244527d236f82d86709582919070afe462ff5bdfb4c9bd5259f6dbb0f8ffd6edbfb89351e9febc3f1a711a259fd28895962112804f18d81cef06f2ca1e81aae9b26238bb0866e8ba309a3465eb703808f8d8c855236fbfb18fbc236b68fd4e95cd752d16a8f953747c139eb7d798721f7018ef162e913f04eef807436cbcafe924ffd56ac2a61060a8fd60e902b6e27b62c3179f8f2818e3639bc91fd72ad0df1d08cf177fd598259ef6ab9216655e8f275e5c5b5a53930652f9039f7b357d162c4a8e2d6788e61d724bd234040cbe0054572b5564a4ce6e2e422123195b1207a5956f05a75bdd97a33941e4776bd1b964e5cf4c316b6375dd4db8d84499fe7a0c99420665bdb9571a263500224bc9e539e51bdea62e50a281951eaaf079721aad28ac579a6d6a7a6f00dbf97377596a776baa8cd6c742ce84df90859a88b250fa856b45ea898ca1934e9cc760c06650097786ba41940f1e383f27fd71aba0598d9fcc8ec35bbbe568fe80d9da93d348e8607286bba627d2b688ed39b825fa56479c9ef77e819ed60dd2f25085b5f1318f6293c3a683ed707966012a3443499edae329fc822cd9c8ca82e34788a6770434648e064900616ae00498da624c1bde12df7289de144692129f98292b6fb969a9f66da08067c0dcb46ee3a9bfd03244670f1c7ef1449f0d95de2daa44f98abe1f4fed43a3e02faf0b72ffdb9cd4e715f16822323914eb3958425266307cfc07f4c3def01426a22a46d5c47b288f81fb869353a4482e9893d8e1ca3fcc030c99dfbfe5395e8cf76ec691da81997c8c3712eaf0f217d7f2ea7d13957b78e56172f35c31c68253b7f4334e6a1ed2d699fd85eb78658f763237c2d8dae438dffb0ccde2a0fe4bde01b24551d7ff554ea46398701ff79829841a8276bf32e9d043dc18be3855be9403ca84a0ce7a51b851dd223893c87e0f306e51e29d9830f04a45873ddbd564261a907e0b4713afe836838adc62b5af8cf6193e7cae000000000000000051a3670200000000dd07a8becf8409c69d13cab022c4475e96650c7f33f08c56f609a0fd3d8f2840200913aa1cc8b09ded932194780f3576109da2360df1e8c3e0c2a35245ec2e7da25c6c2341dc833a62de4bc7c4a488c4e427a734467ac92f45047868278be5700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032f06286b9d226a7be816cbbdffdf16a105147aa89846c0df1963807956795ad370963eabc9ce5a923369ee15ba3e2fb74c0d9f465aaf20b61b419ef94a4b7029fe070984610ae58ae94f400c7389ada940c465a393049d5b1539949c8aef2201c451a3c5e417683d64ecbf67db9d8d1f7ee584acee730d998bfff2d0583072e022b343f4023b19e7dc41b444b2577fd1173a2895f03d368229fd8d8f1ba5824120315995e666f162af804799310f9ddf44b5b8af7be8b8fc4ae2496a4ff82912bd60a0360caed942992b91eb7495a94154fd738f60eea3f54cf7b87557d1b61d64d9a1390472617f16852600b7c92a17553602e31e369fd380099cc4514770af7d1210222795ca6d362151f1fe088abdc7ccb56f96c40811079aaaa221c34bf4c8a95d5031e282d2986ffd33e76638747cfc90e70217a2247ed18970a5df5dfda2a69f77102197f3669036b83105aa076b5d8362a0fd2e642f90334b57277bdaa29c688964903256cdf0bf90e386e2b6cc49d8f1c40f9a340994ef763522a60c127a3780eb5ec03215a33033edf8b89d83e6cc3c667a385f65e705932f7f47ee8c7364fa9bc6f1714a0da5ec7920dad2b3b53c7b425526cc0d4a95c58c5eb884b772c9fad1532ceddd203676cb12e2b2f2c5974411920a722f83e2731d40daaf5d19a326ecc09538503a673e5f40d0b4f10b1e98971ca485590315f7909f3dd0b184d5c2e6488a1a6ba384c72b1f9bf17341da50b434803554e6424c67d2daa0255e79dece7d7d173373338b3e2a6822f3d59735949c50da2108c6cad36d4bc16030bb59c5c57bca36fefc0fb274e9952d4519db01c76199700b622784f59c27f46dbca4c5df6882c0b67ff585b9f4f06ea1b299d9e709989472763ee1bfea63f5aec800653fe3eb035ef16a0450edf9ce3246818865ddf3b509d9d661d912cf2f8a5b0c475dd2f29b1441d3e31653c624980f9b1fd6b653ea2fea597e69bc93b676260292d45e0df8713455dbeabe33e7da5159003602c434ca4f078fce9ba0304631d7a53570f27c6881e5b3746e939b280688eb5640abc822a69a20a94683162a0416267398269fdb806941af28803110f432a7f49506e647bc8756d8a97925845073ffe619837fc5a57b036db326f77a4e2b5882c42d69f16dc68a0cd3f0ce0fb952b4951e771fdcd595b09d248b4969296f821d57a99882a3c9115a4911c7312fbfc4f50928de4689ce7e5dd2d54e6917ccabb68f28e2fc0700157e20c8062e3182503a6bc9fe9e3eb5281e1ecb92313ccca95f5ae2ce077d8bc45bb1a35b2f629aecb104cf666a7cce59b8f5315e3986f2ba50d650bacf9001315dd357215c6caf0212a331b058e6acfb7bc392339006fe0a7ddc4c54200acd784fa9d208afcd614c5c62f668e717d7f36d907f7bcbea0245a19d45c8b8b17706e95e9d9aed227cfe58ff5be56f66fd52353eca571d318aaa416cc6348d8b416927dbd81c025e13f8a3ea4a776d1319f02eb848d57f9d3e96b77077b7cfe2d25e01651240068efa05aa7390795f942347f5bfc6d9aa575e6d5701b6b750517544adda94f54b92ce0c4a7638fa614fda153871004119c5439947b9206d8d4a0ef99360d9be75976ac4a9ba3d82792c6732d108efdc1c1d16198b210b45fd17e9afae48c216f6ebefc74791826c7d8bfa9fdc28b1004bdf77b1b86890fa9699468d17be030a7f7d14328f014424feabe18a4e469754e68a460b291e34f770fec2c3d86e96b93f5b7ba008327bc7c05c08e300a89c59051fb8fb121694ec846bc82dcc3bb8a86ce4b7080bcc3f9caf5854ed4249ada4ae2d4fb5c9f4185ca5e31ecf9779ecfa03f11eb6322e0e29d1bbd31026cd82ef3df7776c93d728e244248da4a12b6c088e7eba17b1b521d4213bde8655b2da5e781c865aac05a954afa1a05f49423b4a2d24fdb67f386ed23b7622bf9d2f56c9e386a9847a214a4a916492d1ab28eed1d2cfef658d22bd0efe50f6c1c7c475ad5aaeeb4c05710e5d994599fde659b7f92588daacf6d85acb3a4b9b77f33fccf529254f616a61354d1f3e51d58c738e7b87b975695f7c99fb2ed5d64cdef93e36ca5b48619695af4e7a1cc15b32c7d3478681ae8c35a0ba6f97dcaaca7c4b0c9439bdd8dda451e19a6d2f4706c00a0f1627415f3457bd8736915851f5e9758dc7867e6fec3617fd975909f20240ee3157f0ee942acdbb4dea1988f451aa2788f2c3900b1a92b5e329b838b44d0ed1b9d4376c5606f4117431e95a5cc1fffe0751089fa6f996e197002483a0bc6fc92a88afd10378066b7ced791d988e65eb324a9094aa3972220752bd98aa04b2641a8736f3ba451401f71dd2fd5d2a99fd9cc5a40390bea686af8128cf74b9a8054e1d8cb2506cef56d3e6c0391e17478a3fd10a68954eebaa0490f", "5151536a516a516a", 0, -1779429524, "3880b0bcbc241503b202cd0d42aed05bb8f1f4104056c2be34a03f87144dd140"], - ["0670a5e0012633fcfd1f4504af54b52da9c924f3f663555306827a42c585663a37eb2dc4ae0100000001ab90d762e902909f4d0400000000036565aba1ff870000000000035365533ccbd562", "536aacac51", 0, -788489278, "593b582176d2f6dd7b89988b476830b2772644343481f5be706ac14eed4cab50"], - ["0bd7783c03b04476639ab5dfa8e6db347dd88c15783d239a15975a837060d21681b6bedddc0100000003acac00ffffffff02ced335acf28d22cec71035f9c445492b457cc0078a3e4384b1a764d5eacb300200000008ac65635363ab6552ffffffff23c8da0b98a77ddc4c44b673d39491235531dbd62c585778e34ae80140a40459010000000751656a516aac52ffffffff025f2e6e050000000003ababab3cb8a40100000000085153636353ac51ac000000000235330902000000000000000000000000176ee8dfe605a21f328deeeeba66111f5132dfd79099134594ac6f64b9ddc73c86cf470a003e0ac31e4743a33f7f4aa5ffa5a3d56372ae4b4354dbb7bb45c3ace523b12f0598dcfe12dc23febad51bec0555c3acf0e8f44ae526096c2f79fd1f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e4b580210e224f730e2592ae2cdf96bcc2d48f38a4ac9cc5a22db59eee47a82529867d859a391681388fa60e5ffc4a861515bc0df0cb410739db2a08c7a978cc8232f2a382be1215756af34dd3e740f79835617c5c41b986d7b13cf3665c05dcec504a2f56ef21f2a38fec50b29d5005c3acd35db519e186d10dd0f94fad82c02301167aa67f2d687d826e1a3a0c230fd7b42dca485f9e4355404bd0a02723f51022e1727caa7ad0c9efff7b94dd2ed1bd45c9ffbe3fdce9970761e56713df4cf7b0b1ebd4b15fe4205716932274f3e2e4ff95612eef415ed92cb3067a1ee46667db511bba9b253e736f2f105672ae7459dcaca6d04271e0e33a619da95081141011c022a40b48b8a4825223df59aff63da6a5521d51f924c70d126f136988c87ac396b021b005e70f6ec190456a0d9629139c42c719b3381547c7ac3891044c848b336d2031d17f4ab542c60c9e4211088d7173cf9effadf2c663eca3cae58eb9cfbc589ad03215f1005b780bf2f2cffb72fbff0b6ec5e75ebb9e54ccba8ff5eb4cf5c30bb20022a17d79768d5c4845112f895778549149cecde057f8da964cdfa82bbac1fbb9dc0cd905b65575008d1b4b5b8ca766a6a36ac077ac968d238fc950d7f38cbb4d0239de6dc2093dea3a2921589cbcd529781a8dc56bb6571bdb23e28f8a8e29aeac2c8b7380f20a8ee36b26585923fecb941a8fe3baaa0e83dcbead907ba214ee38df6cbc3d9cd1a394de733e229e54df0a1d4e74f1e2ffef9b01bff3e8762623175b63cd23c9e1c1b3af6dbebbb3a82c902f3e041333dd4ba7fdfd62a3983a84c430a895db4dda9cc8d28ccd9dc7a6e30f8d68cd1856771c6e21744cbee7b07bf96a8c049afadc2ff29173905b2433cee36f8aa6d480fa2f44302753de13bdb602fcece5210f3f16def20b9b357fbf50062a6dcedb99eb48ba6361caee8501d34055ac96162e35fb6e0dbc454bb7d18968205f24028510d25060c6f314dd0668f574dc60c54d5640930478e8f91775af5e8499b6a8a13ba73cb0e5a3d437a7e630c00dfbefd8421f9d05d38474b18b9883f0372133270c38c9bac9a0def8266c04acb6ddb94b629299f04df61aa48d119a91fb16692b21ef3b0bd22f041a133e394800b601530aa76d8f4d8b552ac307ee666b1b5f8637942cce9f3dbc3b50a0118a58667de98c69f9ebb46015588cbca533b6110b80bc09a7e9afb2b8dc055aa11a903e88bb3c4aa563c513a5a51991423c7e0a25d624f16f022d7e5f244d995a5d4abc2948761c4dbb0dff4f55f37b07c9714db365b73e960728aeff92d053bfa15933d08c7ebe19d70c4e9021eac15a3f726acbeaaf57bc05c04b60c1c01938d8d422681b90d1bf5a9c6de986ce2806749b4eb830af4f859b7934d886355b3cdbe8d8606a6471813f2dfe70358c173d687e009539a47bfccfbdc072f8255176c5aacd962fd9d75e1808b0420d6c9ec854c06fc9ddf5811d2ec726d43fb10db9ce9f7373db22da536734dc79a0107ce257d182ae2a153895ed23b4f1cf064f07738fd1e35f59c549d156dae80fab2cddee21b41dce2aefb7fc1048578a8b9e04a196b26082e199f541a23bb1085c0dbfdc873181e3581e1eb2f690505a68b81cfbef179eba48d3aff836c34a2405375ae66de3e02e4cbe5facfd6d87322bf3146aef3632531915b677f812bc0a795bf9c9c2df1577c2f7980322bee8637a5dff7ae3c4a6258c4da8139859e731ab29510109f7ab4da010f5524e75c7959dd4f979468c5686df482da95c7825ec493e4e6c45758ce8d27a68cf9b15e43f132d60a5c85008699241895d2124001cd183daab1d45a3a1e430e000edfb4bd228781b8806c43dd0744576af86f11902596ac0fff62677214de26d86d0db3ca0efa98d90150e4ac5b73f4e5dd38433105954928c746367b2eb2acd13b86b40b37da8fe7a3d5d3453caa02aa375215877d2335a372c2099e3e4d156803bf911a1ea0f96f43683a919fa259206fa0c4ef15d95f10653c246e97711d5c19b860d5efcfef52de00f99b93adf55bc5e79bb428f5a71d3f929ad87df5e208769fde1f563a4cdcb95f4bacdbd37ad9e2d541ab810cb995c166a5ebbeca41297ed20fae37be2a6ac4071edddd4f1913cf30567c11389a39db2d165e605acae683d02b6a167f8ede0d956b6d3d5c007d9961a8031064f85c09c9d884473c5a4068eebf6c779bbce1f2cf14d8071bcbb9fc809a6515a6cbc4feeaab6dab8ace9725cef8c59d1bcc4d9b8b7b6aa38ce9ff45455a0ee402e39b02af57b202000000000000000000000000ba2cb3a465a7aa4a90514f49d5b5cf6595916892ffd20f00f2794e32652cc29677a2cce8eba14eda52662c2eb44a78d48fcc614e718ecb658658f2b36bbfddc7206b5e057ac1b3db4e4dcbf7d098a6b65d569bd9aa2a7a82d1f0b1f081da9866000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004d970ed89a88f42ab0f71924ace1c755f35c7c288e728df9bd0429b0fdb73f8c46be8af6c0323f07a2ce0f7d505b51bfcc6969d66a9b11d9c4432d470aeb8445568088646d0e0fcbc8468f6f9066e96af76cc9f2683870249a6c6b8f3f1449d2c10c399d579b3d02e092af5990de82d5e6178e6e0638a5136189802e7043a4df02156ce2c279b9261927e61aee7f9575f394f9dd83e249f75d9b798f9c82e622d00327dcb5666923d2adbb4e681d8d786b0ab2bff0419b4142f4af3f24813f4564930b14dcc952aacec67843a9cad5fab8acd826464448e709af1316a13210c89a02df07e73c82d34f3dcab19fcfb0b23e62863acb6d90ce209ecbac50984f3d17e5990311260bd8b30aac83b7aed100cd271c84469c2ce6d4a7d49bfd8df19e9d6e67cb030748f54d13e6b1805652044a9e7d7cd92d61340c31dc80b95485168533077937031244b9e0242fb1ab302f0d87e33cad787af0556c34a91c6035de727d937b52f8022562ae3e15ace447fe03a0caf2ba8a9965642c69ff4860c841c4beb946f7f8710203f6f788bd0b60ae284d296c12e9dd6591dd7d739e44b371d9fd381b1283f6b1e5c1b32e74a782b82e4fc8f83280ea0c2e0de1073e31d5a0d3e6660742e931a65ae03f6b13a73ab02b25e3f141a6bdf644e3b59c527c64a515f4b39cb0119b3901a06c95d084cffc37c7c8e4363bef53411a31023eb9660afdf1eb0c3b22f6a17e08241f0f183aeef0013466834622dda84ea69fdbedde614ed516b54b96bdc7243264f6a96ff1b661dc4bd19b5c8c028571c27948edacf9b871d6f706017d80a122b39ec21ffd30e096fc3fa25f9894b36cbf068236504df17de9480eb3b0c3bee14b8578913a32ad04aece6b718d44be483aa6a62a83c4cbea4a65bfcf5e99d282e637683d1e2ee16663d13fcb976e8fb84ee63a1a52b6a35abb0feeee73dd9bbe9423afdd8a50d1d41fda8099e8c3c0adfdcb4eb65098919814acba2b365cbb311bc76d1c0f73b3c874d835981785968a3c594b66a6aa65920860d35b58257b951a392341b8e3f39112b954efc771fd36bebceeb78bd2874bb6dcc0bd53c735f40372dc8cdfe9da1b25ba19e80804a2733e1a5f925e4f16a08971ebc49e59f10d0056cf80c4db848cd122cd3768dca2f1c7c2ed5e71d5e19669823b87655a3baf0775e26057dd0fe0bfc7474b9be1a903432667a232bae233816cba6ebc59957b21e950866f8a0046af0a80ab6cb409353fd17020dec284af879b0f7c92961db178046fc2609a749634101ca405dd67104125f2946a8dc20f95e94a8e2c6a9044a0d464c96baa022d921a09fcc4875389779df99ba3632ee19f43a4d742d71d61096238c765d6945c7c92fee13e5962ad237a2a865c76e75ac45b07ec69d329dfc14bdf7705dddd0cfe3e38313ab0f2ac3f442a486a16ea620f6d8dfc7a3f79eef58c3c9c92f73d373f66cc73a5e32e3a4c5c0e93672515814d49225c0d633b7bf4676fbe5ad4e0ad03b836202c72874a5689dff0e309f07adc84764923fe0c94c66bfe7a5e4745dd2880976084954adbd77ea2d74e4d565706974e6879912d926eb496758c48e063c8b2adc9a80ed19a4bb3342f28d24a419c271b2cedc189edf82fec762c81b8e5bfc7b2e60e1465907d8f5ac03b2b1b7c89cd4e5e09d291844650f4f076e359a0e2455043c55a9c69dbdd8ecec809d87a02db24a69f8f92a597e832b88f7dccc2a0cc6e293da334a95b095eb69ef7e36b4fbea14bce9f82ba7ea1cf0dfdd446d67c95623753e5267945493756794f0a5e31de64f10b816fa34a566ed00c94760d5ed0d5bd0b7926cafde75168ab5de79fe7f636b1cd2428f8208fdc7126ef847a641a90aba0ea420353141bccbdfc4bd81b68d2960e7355a98eb3710d20b02c811c25ea8794a5234062771067d9befa54b7d56219dd314cfd450a089fd177c65f29d000defa37e1c4a7445f0f8f6bcfc2ace9c30e888ddd2508ae874ef60616ad50a4072d0c70688475b349c0ba2e464360199f8d80de93044b3f2712df0e48fab0a7c67445ef6c79d29a05d4409f917227f9ecafc5aadab485d6a85e33ee32cd9a6e0ffd73ad27684dbfa70cee64bbf51823db60226685a195386e7f9bf4cb541044069cf9cd05c4dc44d92d67a72b20e1fbb1f61859c55bd39a7b910e0cc23b79fa130b6c19127e1f79d42e59753223c5877ee4f119f7664b6b54b1440a618317599cb8d8fa09596c5dea7ebc5632c761bcc6ef5a90e8481a3a2141a83bf91d5f4de2b7b29a3ab3705e1aefe0865cec6a2898846dea10387076de9e64c59ff0c80468b31f97feadfafd225f8f55e1091984a630921f55073fdf9df5cd0dddd99821f940fcbb463fa828e1d778aef847eca34057173707b6c6278eb2937d07c200fbbf3244477205", "5365", 2, 913450060, "0c7ed87fb8931ad02e034fc0979111cc78a75335d0d5e0153c4df9748bc3f968"], - ["da3638a90413686eb1f4dd31df8f4d2225f01029c646f8779992117f42873310e7ef588565030000000151a79f1f897636da0aa20218fcda838a702bd4de2172215cde2a01eca49d6f1123df6ec35d03000000025251ffffffff38b405904f5c2bdb86acb16753ad20cd7792e063f0ba08ddf245adc267a54e5c0000000003006551ffffffff25f70dfa535a49e951392a8eaf5064bf668ff3b61b3b87814c8be7e19866b02e010000000663ab635252ab88e00214026a3a53050000000002655380491f0500000000056aac65635200000000", "ac53", 2, -965840814, "3903fe7ab41fb776aa221292fd81356c8ba9194da7ac2b35e1646a3d6b15368e"], - ["f83654e803e8616df0af37c926ec61ded73042144376ad74c99400e4b4d51fff8465c91a040100000007520065005163001610171b8d74536c5fce3d434b8a6e1c03b4a7616dc07f384da8b32e25a56257ad53a6d302000000035363516681d9af59261dbffb5de78483841195300babe0b2e7eefec57065468a53050109716d0d0000000002ab6583687d7a02cc656d0100000000086a530065006a00ac17a14201000000000652526563655200000000", "5253", 0, 1461359083, "f590bd1372ed7bd754e67ed727123bf631c58a0bde0584952cc0b23bc107135b"], - ["128048330410257a1dee50a1448a7f9cee3b9530193495ffeabf8d5c97df4801c4a77078ac03000000025365f6d2e04eed8b37c750b44d7dddb9b16a25793b79496282b4cdb46165ea3223cc4152cc880100000000014293b08fbcb0818fdb47e559bf717856d868279f5e4685c16d327944960ee7c8aa43010100000009536a655351656351acabe7a51d22b437161b4d9a2f8d3b744517e54913cca556a418b2127318b71159592ba0f803000000025252c66c26cf03f848c704000000000265633529780000000000015140e6da020000000003000000000000000261e73e02000000000000000000000000aaad652e16901fa3a93dedeae86a531926369d0912fa66e895350c0c7f605e54e5c325cb8d92afc01bd5fe3ed90eb0f7b730bd6e66db5a96b6fe7c520c36be3f0e6d22c50c4470d92915e2982d01f58516bc2ec18113ce83491d50281a6329e6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c6600504f8979bca86213dbe04aad9d991c61a3ba35148b09e0924eef49b2af47079ca0f7ba54f2aff6d125e921010122cf28aa0a84f983f26ad2a76ea7cdb363d337708925b26674546c323b952e1cc83992e594290aed5ec29ab8cb0c1fcbd864836bbf03b1ec3bbfad0073d5a0ef3f2096e1ddc861ea5a2ea298a8e6f4510317b11c7c1c2396b7fe440ce097002d5e6bfb204d0335e4264ed98fd499c2b2fa030c74d85df4b64fad5a13bee801910d245ef1dbbd201e4cb16fb9abbe22f785330b1ddb5e031b03c9253b19df7b221265648d19d02bcd97ff76a5f3b89977278592222d8be40656b2bc91a2d4cec14afc5a92eff440f087c31c06790cd7bef2f500031bdcd901340aa3d9791903f5b0c05cf6c1f14f3bc1282bc2b046d44ddc7cb8320227f058e9a7907f44548849292db144dc48ef9fba493cc39ee87a5e9139c6c2520314dbc47ef6e9f1ed072b3edd255728cf9d83ff83b62973a82755d58b26cc83fd02236b5cc149043a454ed8faf2abaa70ab60cab5ed389237808687da29a40acd97020af8a7a145435543ee659bdd3492b813123c533f49e39285c2a3fcac9a135ef08b8f483de98b85a5d256251a3170bd7ddbad015a1daa16c1f99e36ba99edb321a407e0fe7695796854fee1bf526de981b6a13e1ca16ff6784f11de862a55427637588ddfaa297a2cb57dba5c23085f56213e42af11d77f7390385db95b84cf07e7fdd7270b3f3e120b5abacde6204782d4ad8583f6f7b5338e25a2541a25334cc501c75f036d52b0c526b9bdb63789f85dc5b06c95d712f5b62e63621fbffe3e5044fc0655e2483fed5792fca197d1730ef7dd1d2f69f62430ddd9e7c3edf2752d74494ccb3bcc7bcb567e506a52a146c6e3a41ef53449019ffca1c854dfd895c6a41e98575ce40a00f5692d987e090db20696b059093f83a6a77ae536d38a5bda7c3113e7a37767474a5b53a684a0632095d698a080a8582e5067761604ab5b8732452f03a3fb9f983c120b21f02bc18c473a4414142b8e143731606615097303fbcacc1892d62d1eef55a3cd10b4e4a037f6013f1849b7f827d2a4fd7c015fed38bf71c85c4048e5a67e990cf24670ef3386fb996d241d61cc9d0d2cc0b18f25b34a6cc2451fc63a2950316984a10b741584fd7f0a4d94fbd8823e32d2a4c933574b5737d3949c83ecbb08dca1e4515a4df57d0b13ad37b640b9a0844c4d74b679822b2141ac9316387db0b5c1f81a7417e2b0522cabcb6d58f6ff0d7ba92cd58c76ec31b5f20d6bd3b04eca54b4e5541fcb566bde99ab42032da3875f42d7caebccb6c6b3cfae34c83c0856b7bffa5fe4c9b81658e2ab8d3cfbe741732df36931ea5b549a748913f61706edd506505e7e7e8277f6d9d519a5971b5c57d04912baf0331616bbb3dc3d7d270da6af69cd0959f16dfb1c78e7c8b17162653f2d07b572622cf445ae4626fea35125c89a0b1a2c4b59f8b43b62896ca0d7439b66d963f2af4eb3fd17c4bfd6cc0eab1b2ff7702446efbb9fafa19e3e5ad3a09be7210aeacfc0c6a819e4e0ee50a1b83f0f5204b316f09e869a6f76f76e17b772f8248e18f00320a560823300352426ce1d0eb94e168b3e348d71494f8c043e4288167f782785b902337ea5b47c8acccee9dd06ab295389d7907c5e17d995ac586abdd7a1aec1bd520afb793c9cf9dd297f96f72d46ba0117a6e0414440d80b6aea61d56c2a67d2c64f3b9071b89ff563cd2fdd08c5b484246ac31194e6618590c695ef7e47eca5ae1cbfeffc7ef57b32c6341a8da0839a90e5b75640ffcaba5d79570be3fa787ae782bf3bf832b03569b387ab861d693caf975bd6930558fb0334dd03e36fb63ec37d7a227878bfc792a74b8afce5e95d14262e35950252ef1d208ff5d57d96411e70c8b31111760839bfd499ea1db6dda42f2a02f953eb087b3b09550fb4a8dc3cd9e5353c8f80b8211e3cf38c4de2fa40bb4fbb6c153efeea7fdb64f521c49aa5687178dfc10b84e7c685fef3e2fc171a81546ed2f3a802fedbf5d2576c261363d0b3d0c8d797cb1cc9a975e1e3da14e17bb22e428cbfb3d5cd13dd8464e3b45d4a3220f40fcb4308bacffc8363c01023d558919f39b6ab1d4e8ef312a4ac863cd34165e8b9e3ff7a10b88e5481d434d31eab8b850b70069668ab60374dc40061ea20f8ec527152ecc821379ef8e178f0d37587ccc3b35388f40f7b6cae92576ec95b61ed7f28ca4c91a29226e17650373096e045ac49c2260fc42615be56b09859f306fbfb8f03c7f68068260a2a0000000000000000000000000033342ab98e1498eac22861dec9ebe951fe940909120d55771dd2f430e8da596dff22ee9316a4d178b3500b021a220debcdcda5d24167a0ed048714eb0c93d224854850cebf378367b7ee70a968cb9296ae5f887f85ea0c085caaedc76866907d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004b35e0eacd77a0d29c5d4c2d77284a770ff5f2467e798db0f8c67de40af40a7c59076eef715bb9eee0239eaa69818687d60d37ad3beaadced45403b692fe1e5fbe9460fa1c01cbdfba3237df03daed07968bf0e25a514b421598dee6d2535d4614981475191bcc582cd22e561054fc79ef430cd53c9c3f53d84c90e29df9c30032c6e310875b9f81c022fe855effed2a5512345ab99f4f9d8f6bd7e6fe6744dae0318c5bbfdc5624dc5d1431ab50f20e11b09051e590bb69964a1bf02b76f1b3a6a0a1904133eb832c5331ae117affd148ee33fdcb64833aff3a9afb46b18e32dbf1e1ffb542f8b8367a71721748ed1e2a9df1b7811c7d051873fa5c55bfd9901bb62032ee87438277c87dedf616970fbffbbf3fd2853df01a2a5f516e89e8741bd03af0209618b37289f766d0ad2d0cfa0514c0100967c3c672357840f090ea97e0de169022fc94aad774f17d283751256885087ec5a8b78013dce47416ed2b1d0296c335802189e37d590d213c5c75e646567d4c0cf8b81a8634523b1c3c84e71c3bb976d56020898fdbc2389e86e9d473ae87212a5745e0e47d123c51801b8279aa7eda5f7c3c1ea5a54d2067e4a55a19aff0aab0e8ed5a1645856d99456d896b571228b7ec48acae86e476b3a433b38210f3ddc09f3b0bd6351b9b7931d70d69202607a7a9127cac42647630138b7eda26e222835385d8920b984099fc44c70c57f876ede84b84662eef908558dfa960a8595db3e6d429288ab5925b99bb2dfe697beed2569a6a68b12453e554780c49d657e2a95e560869fe2ec5b522f0dfdccd68608fa8524c5009e6c524c89e94dd708809758c57ee2a753d12db1d53c76ecdbcdc39a7fad14f579610064d542e1ef64c7b7f4c54990d25e73f0b22c46afd2661b98f20d84f9c0e04d523ab23f47f4dcb9f4d25a11d109ff0b3f8cc5063f0680cbaeb223e2a6bcb0794d0d8e580158598d4df1cf143030fc0906295fab1a839f0b332e4665c548034dec99d3ea27ba98c0ce78c73d46bda0e4784c8bf1a5dec3dc0f9fce7f8e0ecc4b42fb476ca95bb79d9f2d4c593742ae34b8ea649bfa6d9c361aa32a6047eb46d4731d099ce6b343d4d3321791cd73b53b5d66d1a30c47c7eef7e0c1b4a8d5f8e02e9724f258a991176f0960fb1517028fbd3274877b70764d3c1757752dd047287f5cfb14c46cf07bb58adb3385f2ba5aed4f009aa03ae329d0bb801d1be6d03aada73b69ac20a175f813df7be043d6c7a59b8aa9f7b1fc29105ba8e7ebfc1547d7ac405a14f4d74749ef901a50efe1998895fa5e1e30ada16fe0427126b3b2531c5b1689a6df2064134b529edb780a570daa82fad042a7aee32f1870482dd542949e7df0978361198bb8a46b98a136e495834cf01144a7ae496a6541e47e79cfe02d98e8375b9707665cc7271274738591f0dab3b0834e562ef5b3ddb4c433fa7c6b3f05b06255b76a53f16735a781923579bac738ff3f51cdc4141dae22c093e0114aa69e37a235b632e144a4eca614b90e958201bc74d3df5f2eaf934d0bf09749b31b9ef3204d15ec8f8f1e9b11523277f68a4bd17fb9cae8f0ae659bd93a5708e84e0e417f19ed8721d4341940334e1d28c0030ec78add0ca9fec279894a3cfd3cafe3e62e4e3407adbaf07ad6cdd3420cc753ccdfdc05db9ea5692509e9a5cf804fd37f38f1a3f248cfc74979ee4671d659dc98473f329c32005d41861f11a949115ed83fedbea48be1ba50e1e5a330328f75ff738401158429340d6d750989b5a9f92e1cdebec236d2f54465482fc8c1c9e66a3efd53e41a34c8d4cc97ae6da13ad9d18d491adf231c7a09a1b6c6e5534075af8ed4605082ce5d167a314ef7d3351025ba400e074343313c2b63c0f4f0274fe2b344bc9b73a1b36472df190dd12e5dc7cfa6996c83fda388956269134698c02f0377b67cdc39c1b001759db968c590892023e3af969e896ac692425205a94c4499b451702a9a8d8743417cfab381a27a994da666eb0b29c739c2d93b3182e7568e04cacffb5ec168ca0ace6de66bc4ece2fd82783ace48c2abf94065fd49e218e113f27ecc86fdedf500dd8c722c1f10c1bb227003d50c04c3286d2a64432dc3230f3a57956cb7b45901fb33d785aade364cff2cc952cbdb4dda13ffd22d4073e4c733e2a1b3ecaf249a4e46a948c44d9dc89f382077603b097754ff03e237e8db5e4f49044f645268ef322c2ae0b2828fad701688b49a3173c6b33a4aee9d56e58afe2b7d597509c2101006fbdfb121386ec3da18c5c35a99165cd3911ccf365c5eb8b124ff37ec6b2fb7b5a18403fb32163f384dcd29825bea6bf093aefc5be3fd8e8e6bb8261ff0c4d5635e43c8c6e4939b9535de6c8efa74a9b1b409f7cf55b73e80e1dc81d42b32c070e44483678814792b7cab03", "51ac535352", 1, 1337258466, "8f21e9bdd1e74f426bea7890cbda23fd8d4050133e96c8d756513311527c5ab2"], - ["ebe33b240241c4711911f86dbdcfff42149a5fee383947a085a14ee30b11a8fa1bda88a40a00000000050051ab5163964755654c3e91cfe323df8b9713a5fbf706ce1c9733d284f25e9f92ce8c0f2ee8b6cb61010000000700635163ab6300ffffffff024b784c03000000000552515263001cb7650400000000008874683a02000000000000000030e6a402000000007a12d56d236224c43bd61a3a93c5372c2598dd34585272e45ce983cc6a7082717a75520de9a55b8a1ab9237bfa31694068a6b183749192fed5bc163964acdeac5b402656fdd986d237c932d241a3cb021a7befaed084d33bb39c5135cde1768d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c11058c36aa2c5e7e857d9b0c14e6da0c9a0c70ce472f9977e71089906240b3d1f1f9776b227c54d6870888cf3180a3ebcb228ed0bfddaa061c29d3b96581100420ee77b87a87f22312152c8a508946e1cf875cf03c11fc5ef66690a242031403c0463b03f4d764ac1d708a7ac650f77b393318c01bed1e43b6fa9966c1fceb20316ccb53c8b6eee54c3fad3542b8b086127e53fd31e51e8b75b49cced025d7ce80221a7be2840784c793e6631313d5843f20c752e7ea5db9a4cae1bd836c705bd290b0a35762284cc35986db70b5a80c9af34a86331d0d70ce3f2dde731c19ac7d28607b76a1878a52b012ff27b34cfeb266e18bcc6b37556298ee9d63eb94b62b6d6021021e4be741eeddf95f4b81c9fd6b5246cfe0c3f6e560b80dc17ca1ff7d90b9c0201d6a0309c4cc91f2d01d44fb486fdc85ba06ab73b299a1cd7bedaafbfa7b18003241804e2a39929b51cf13928fd625edd05db2be3f7897fa587253e67b21164960309e4c152b735b5f003fbdce49c23404ee7823ce995ecb3aac267b1d4a0918a1a03063771bab43e4829c7f42b54aba1087ce6ececf64ffe110d707d9448000d49f7f1feeabd5da6f373af2b31c6b4503b3f06c93462471976423dea86f74172272d24a271420e6d9e14a8260afcc3b6a477425c6dec27aca0ae801f57eae65d3f1353d2667dc860d02a016bfc1e564d9bdacc0d396ef651f9ee6c415bc3193e43261d00e4a87aed37448218a60355cc8c547e5b3973016ddfb7067960a7dfe156cd5f43f1d3b7fe002de8d272bd497721edcabd1cac671b1c095f3cda4aeb2013c0c914f0a5fb99afda0fb5ceadb82181806f506e674d88a872be88c77e2849d7a7a7e141004a5268ae14d3ae2b1b8a163e13df02f151ff211ff2ca723970631e5f9001a6aca5a98d0ff06f2a325319f6b9b648fd91a79cc0a785443632ce87889d16b05ce670c695e740daec591b044a677843d05a141cebf0ebb4cc80f44bcdf825d34dce8ad112970532ef56f81c95d3012ca0533e0c999b4149406dc3609a66895373120af2edf1219b255000c2c0861ddfac9ffdbeb422ba8aa42b6afedef04acde65173f99964f62df8a193615f3cda4bb56b2c62f5eb2b98ea5589ea239419ff52145ec942dc74a699e758011ab925d365dd302ff462a8a51e4fefbd7c33f2c954c2c5b6e47aad05df4fe5a8eeaea4fdef4710251d2319ae31baa540c8d577ecb87408c2b59a3974dd2c4e6981c6458ef48b2e58320831272b9419576a1b4b0a24880d3aba6b6bcc83cad5b60a1f60bd32c99a35ee339c12d603a7dccb5d135beb89d53a8c8686879a80608227721bbc4ce95eccd4794c0f4a39c6b4b2625b8df787e8614da2d2d959f8b79d0e698ef602d4b91384a173b6c275873b20823aff2673a504326da0dd96eba6c39f775ff8a703f09f2f5cfe54ce8ff47048ab1e4c766fc129e9465dff900b61a3da8efc53f24e31fc2d8f4764b2f34406cca7448a6a4894016612d9dc474e8e236f91e17aa52714ba253dd4b10d10967061d1d7c6ac1aa489a91efa62587fca5149362b612b7da2b3e5204867f4cba6df22e5f8ddb1b7e8e894a8469ee8badbf57aab7fcdfab7e10f302835b74c71159da070b92e8eadb9d60f5ca2627027b6cab1b08341389f6a95e33231e0f50ae9e4e2aeb199ba2d6f354ad9796d9ad3aba54d17e04813ca26dd0e2c27efce4dc3d5f7f900dbd502866f7c20a20dff319460742a3d1fa4cb91b743965f97ff4d150e061e0e4e93aa3292ce87133f4a2dbbe0a64c5b1f32b11c00dc3dbf4eafe9ee48ed8e38fcf2b8ce92ac2b487d824133b7c39d65fa1ebd689ee7e1058fe5075bb75152678069ce646a1ebb1e22360a9ae1ed387fab42fa7c082528c6be36bef91b60c0aa599a83d953fc64f0307b7f189554fbdcefd443d7f9971038860c881ac66bc41c0e7b4806b2ee4848cc4bf6d2801a3f68aa79765259ecc0ee6298ca40eb6674eb0f5d77ef69cc9f38c6efac3f870dbb1a60aba3b357587fd9ae9851270c5f0720e667e8f32c86e39b640c4cd5eadc4470cb95884b2a9ef82ce8df88a4576bf5e076f31299306c5ab72faacb1338e5c2d83bd8fb8e838d84638cbd000747ab5511a0802b298a3494914790237b90fc77dc3c605d49dc16142f1ac0ac9cadea6c2696e22e7b02dbfb8f07fa89f62745393b58299af22071fa4bce57d6c10f7f62030cde3e7b57ada3f67ee698334ba71a5368a76277d15b41b9af5d7f20d99503ca9562b24927b7cfb361a394cb73bf182ace6165c702000000000000000000000000617fba4152e1b65fee78317eee7cfbf67652f090b13b453bdd2b8b2c8ae7a747318a634a474f386bc402cd0867df36c3b946a0be75624fc641751674102d345f7b7243ed168e07cd7c7dd8a40ba76ed705d2319111a7e50bce081161eaa43654000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001fb88335891a20c9b1bc4c74ed63d7569ac2ecc0bd94731eb28170c969d6b8f44203af47ae4a4d27b31cf72644946feb3a5cb2080e779fb942747367a22d0c7e48df37878e38415707a09301de5651e0302006ad3e6102845ea53a1c653cf5c0f044cab99043cc94835f111b258b6d835517e5ec6536b74eab64308ee450bf4e0206a5279f4c44058c91be9dbc1bfd60dee16a3c2f99aafba78b7572a0720716040223d407a3cd3c4964cad39bfa510557c012747651c449e02ec84f34c2efd6b0850a12c317532177f17c02809e2ed9d8b5dc53948e68b12a6c69cd04913cb1d0ec26145911a5f040fa6f92cb4d426c71d9c9cc10f0cb4b13d1f0ea6c3c2e1e965983021091993134b70c644c4a1edfba27c6f973c2ae1b6d786863f3281e2e9dde8e14022fa4e96868a3a138323555cb758f6dd3aa37236058d29d07f3bf5634cd85deb4020738ba78037fbf67118c6f4af728382ee9ef026eb2fdff7626efa62610f37a8103039266f802be11a92c5b45b7cde9ad50e86a58ea011bd16b3546ab2173f669ba021740dc8914bf9c06b1b8acff8d8d9b4a14b74522a68e62f17f5655b0c78238a86a5262f6dfb601ecc59f7620770bf63942fbc0e96c8b37c5f95edf89bebc75ac8db044f180d71bb81f24af69bbb89abbee8e835f2365547cf47251a0b26ab9497146c424ba851523109797c1298524f79aef928cf0faad47242c6e391f98e613d8b62e300bd0ad3f73fda654d078fe82fcb2eef0d7c5bb3846009ab431d4b039d95a891d194f06258712a44c697b4bd5e9ed6b79cfca7c3c71a5bfdfab4236cb3b2a45e2ef2bfc314cd98879a3a6bbf77b796b8f78ec27d260dcd079ca9372ccb6205af88bf8a19c62645c459768d32e4a7d871d36c7b37af18c292adf64a4815913efbc2ceab01ba208afa118e94f06daff052bb25d61e89b3206075d4740888094e34e121e05415e2df7aeb7c3ed0c37d6b71d955eb32cc1d8215df4a9bb678a4395812ad18068f3c42a375c35abb85539700d70d6d78654a2f7ff0d32000ddb40a470365137c19cad9dfc96388287b3dab3f0343d7456b029c5a9bf9a70237353cbbacebdabd3673f8c6824b13007dbc857fdb88e212753ca1760afe380a8d2a6ded20d0ef0700eab0b4d6bac00ff36a42344902cd82ed4e79379ce32410b97621df0cfb0423ded8b3116d9c93cff6a17cc9104bef7c7f2ea1448c4b8c76228eb2fff8f3970daeee4bb91e35248d4b3249c6ef55e60b1d432de449dfd940b0a4d1e8793f14c12c7669064c5919d320da4dbc25e8b3dffebd240fe974bcaff8752fc08f377218144370cf2b89764f516b2e2ec9d1a750d3fa107e05d6477ce1a3ba882ce054ee3089d850ae2988a435612aa423f37ab77e7395672c4a45054deeaafb392ec53bcccea26a23f88fd9782a7ec3d44b5222c31ffcaca5cf3a91b691b40adc728c5e8dfde77d8d4578f7493af077a4230b2a549af21059f04dae40d61e1ac30b955d1a98f8010758c893f8bb4e540d9595f588a171eee754170d2fc40ef43cb56597fb45226d3629052200392c3bf3843d16cb605e6f41d8b9e9bcb55569afa5057cf1cc4abe47c12c2217342967f8bfc53d05fa253664a9ffc48fb230e245dd5d29b51b7b3158ed983274586e99750747893f3d6a49f4462a0106124f03a97a4a6adba016f7c3b9b697c4c5351932f8b39775e6a260c57c746245f390f824fa9c7231c357701170ceb7e410b537f18ada9b47d29635b2c57b59a7debd84039e28529cdd40508a7a5d67efeac0dfb0f27a7783b24995ab29eb04a2bf5b0ef62a764d8cde0b67621e8920ab680a4194b460b8c856ca9a1e0e0cbd8fad6edcd586ba7ebfec8afb508ce10a764563c25419b06345b97abb1a03221668ffbeef1248c526bf9b384d3d0b90d63ddaedcda5351fc25ef55e4a4e46ba9f2b10d36eed68f84280ce2569b24acf4d43c4608f444eec23540815b7b39303f4f625c0412c44c8191fcad1485f1356d14912b25643329468679f870903c3f65325d9d03a8ad292f6f5d3bf49f023ae1d82f29183d2c855d16b9412b0a00e5626266edbb188d17830fca7da47c0fe977dc8785f027f025153dd584da0ecd972b99937cc207f56e61badc146226e51417f8e330a183e3eb53bf3bddac89e8f5878a001d4b333e4eaf615202bb1b82001daa9e68ea377391a788d12d149f49f0dc9e34212a369c20d179353db8f675a57a0fd9e4751ae5ca8b2d40edef297a980f641d41f9d73b9ff1af3df2fd9325b58f0c08397ac919ce240d23cd567133110a58352e3a3474d6a47eb283b96c1557d6067e20e2fe48d88412cc6f81c83f0b60a6cbbc505056c3ea45e2685eabda2117ebfa1f334cb446a791b69a038415ee867dd7c325ed7a8e858d445b13630a3f546bfa0f", "", 0, -673625490, "340c7587b869a58c65ee7d23ec2cacf2cd76b19db46251cab29c315bddf5aa4b"], - ["5478007a0315d527fa3b36b69ec00d46573f3fdf6c1dca6b2c2d134473158736d930c484ed030000000851516a525363ab53ddd8d9446cbdcf75c89584472773a42c5c700e0424ce2ab21f31cbee423474d2e0976aef0300000002006385fe1bf377f38953ce0674a4aaefeb6b0c5307e6ad27d538126475e68e690ef4f8e95a5c0200000005ac526a6aab20c6aef602d777720000000000066a6551acabab0ef861000000000005656352ab537310f21d030000000000000000f4ee0d03000000007b3f7e726506a1441ea4802a84034025ce0ffad9080e5c11f271cf396a599d5d9fa6c8d43f3d84ffcd4ea06c27098e7cd8264b5508743f8e9d85ece916ea2aba5962213b6c544efbaade9ee75c6211e36e467f159afc8e50b122778d5840352a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d0bcc884de573d8ccd303de2a0c13eee8be987934cbd81916e79f2c399647a9a23a023b02202330c77ef7a60c5ac467f6bf10c9c70a6dd237f6a2fd0fbb784158d8bca50b99509358aea6cf9e12ca71fa0738b098b648c1771080f1962b51a71bbb43cad9fe6c3826cb6bd630864d9051c589fd25ecccfde6fcbf60999dd1456022ad8371e96dc0411a5490f58bdb5488b78ac2d2305a1c175245c6539bfb3034c0224aac7ac901f5ff034e81529350eb5ab66c7b577a42cf36cab0394f01fa3e3af0b267e7428486eccb168fab3cb425d0dbd58a7c887b793f1bfaa953476f273c3cb1baefee33c238572f3643a80d3957995c51135697010d7e0dd3be1284e8ec1cb030cfd2036f82dafb6d3bf3ed4554a9f6c611ac0d5f436f1d7650a23a18d36e96f02035bf3e47cb2f229143c3d92a587e77fbe5d2d6ee60c69804765d9c4c6cb3bd202300a159db4441a95561b262e7722e4df3d25fd1b3f4beddbef15c79ed18959c50222cbbdd7a483ff185fbea1e16067e7ddc0bd4786c11629f3ccc51735d7172e4202102ce0e77b6c3f90ca163dfa7d585b39a65c8416169814e778639405b88c94bd410ee00df26bb455080cffa1a8ca9fcab0463d6969a33525bf0bb222ec9a55a2d9b13562702e0568fb2b6f41f221b57c4870c02c0d4d8d66eaedd4c758fae32d3534f5be1b157eb248b96d65d56fc01e75fc1c0d8306eccbfe3c0c306e64646e6aa876cf0ca9542fddee2eb09fe454c520a1cf84776511b4aad48e2f6717c8b544e5a30837617a992beb43a04e8379bffe5c9d32ef63b08f4b6a94f6c1dc9c815355d5123bf93a09b8fdb0625db8c89bfaf08758aaf14e8894ecbd93bb7db64d42b05fa0a16134500599001b408ea4ae40193eea6c5cfa1f157a60d1dd44ec43b1a7fab5d973c8b615a38646d5eda7baf42743ca6bcccfe23e992962f4d7183a31cce8f0baa84e67eb3521755fba9d7429aa7bfb6f7ad3e3f5f264da6dcd59ee9e11bfe60084b9001183fa7f436f550be3a914fd73d2e825b1a4d8fdeefdd23eb6b94cdf3af4bfbefca32481aa743e794bd8cda54f357c0652ffe46e32a6260df2451ec4da1ad448f157b4f684fc554938720d70cc85f1ffa1039688bc9c5ca6d298179997c9a190ad64114efa825195e0526ec5e378698edac683d1fc2cc5b222cf721862ff3fc8a69b44436f6944e6f3884a47e16546f10c7438d3538bdba398fc689351568de15e430f2630ddc4e5eab869485228e242a0804454dee90152ed568eea7ce326188803e31ae79e6335eafef24250c0ea37bf0e5593e986ec13a36cbe8cc906343c2889735dc4914743a4d7c97ed983f7b806750337b03da763676f96058c111f14a56ab9b7dac6bf4d2e2a9dd3f435564fa95f75f69f60f3b4c509b4512f98037a294971f562ae37767e02951bad6a8991b0fd76965ff3c0fef3c37a7c59b25fb3338578e79f8d611d50fb074a0da25c660cd10e3f2124258c536f9301145a8144a73d8e5863f80279d2db833461e269992023494aa75ecda48fb1c3debc158fb059d2499c9a61691ead526565e34701f2e0a5de2485954f0d4021810745d4fbfecb03daf40957389c34bae51cfb5ea2825c55f4c784cf420bfc09603e4b75c2014a9d5d9224cfde48ed48fd67246b79f069dcc12161c68c4b1d0fcee0e14d086d78ecd32e5af0b3e7617062415ccd15293cb2e9760d7ee8817b82ecc128b46d88b2c901f0e184d72406a475051d6c109555beec0b1592518c476b646ecfb77d839ab938dc1f662d942811e7f93504fc4d9663fea3745ecdf142e20be1330918bf1f70ee332bf4d56cfa798dd5e058c906b2bcdf0bcdd95e7d5b907520d85d3de0eed71386e0c154403bc3e04abf8706eb3afbd91fd84ea49f8c6c663f1d6bf9dbc711b07fd4159f59e8b3d62c41c758ec1e3bb54520fbdf49fe23109a59204ff3904e096506e07484f725b2dbbed2e8615463cd8b3841d5f99d4f1e09833e1c9fac0dc4f96673224b154a066cb16b83ad141bd6aaf749e4f1fa4db796a9054e75f3d8f65db236e1b16ca7bd825401c37f94b24d288d1e3e50fc6345c9dc28550ec4ac5b3381ff34b403d7e9bb4c88ed981a3bd7516eabb770919deae38270b623c128352fdf4205d2bc4e7c26499e454983578d500499ef296b0cdab01ffcc734f77f4641eadfe2cefbba7b865c2024c00c92cf0432af2cd15b7cb8239f5b2b1c75278a2cc3346d59e32021650d0bb1293d7d2c2a91414321ae060c15a2251fd53efd5d5ff16ea63aafc6bd4c52040000000000000000000000009486eafb3e385d0862da7308ded5722c0d4a14b6e8744f04dfd12afc7b746541e73d087d197299cff88a007aa7033003922217e3cc0e38a360bb73eaa633329cf3edd0ddc5ad133a1ab69230cceb5469a4c25b3607530c20377e1c8df274bf8400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000242a2b443b8bf5cc438de2cc760618084293fa2d3fa6af259945d222c8b090e306039eea9e494d2c65dd1591363e7d3bc053d70b707be3d28be322bd5e5d0710d461eaceae3ff45289c26d6ce4a0f4248dcbbb4c41b70f80eaed0d3832ec6d27b094b826d942ef014aaedfe14033ff24039e8e29ae39e8e819d6e939f27bfa60032a219905c28cf6dab334779fe4ec7f4a2519c0c0358771fea44d2f0c7c951e62030816b857e6891e1540966387578eff869bb5d75316a86112777a5da6277f79b30a0bbcc9e69f569b7b8059e9a1b29e580054e2269a9f78151ee93f779e4e329a771acf6c4515a3e527d03f45a10e81362021547d281ca63e3351b9b6bc541b4c920215fb21f8ee21d5f7d9372941f5f907f82546141de9d6aee5980290fdd4179918022d97b71561941b9861250e2627d6e8c7ef7f39fed8855e3e4f6644f423ffcc45030113e9b447b7ba8a8237053ab15d856842cdb94d6660a7871bab478e3d7d977503002bee8582c02b879c8a6f38b4d6be1010a5a2ea3d952543773dd7d3c6b042e2021f8576e502585bff5595a63ad38cde0367d47473dda01dcff607fec940c0465bb3623a886430001a2f4fac6974994bebe973d352727d6c2ee587f62f65d1c8bf148541759e775ff8b97c8ea01d712814da1080b4b40fe637ad459d1aba817f88119c643bd308a26bdaccdb51f24761ad6c9a8ec949d9b2b5edea4aa86f372793b9975bcc73d6492fa748bb0fa39e88ceaabdfda0db9a1a83a8458b6a911535d746e4211e55eb524bd7027bbe9b58c112358d63b03b66af10910e23e990b8982af31688372b6c7fe03b0cbe9b01fec0830d474bd10988b4db2ae52f73fd172df3b143e17f2fad969a64d7e0e4ff4992c93b722b8144386e4f688b7b3ff6fce15583a9dca604b83a790c2fb9814694fe241b96dd4b2812f3d9111496e0757ab43b515a68ae5addb55aaf5fe37f32e17d7f02e68d887df6824accfe01ab1491436cf3747828063ab220f5d61311acd8992ac5792d6be5ad5e9433c050c3dee0746cce732f344bf6677f4234488f7e544b43a21cff112e5b654f88bad95fcb55f13520bcd8feb617709300beda28e6ae4f3a298855cca30c665e95f67918a9dd34b957f26db3caa514eb81845e973f54af3a049f9fde1de19ebe95f27c0f9f202a13b9747e43a2f29a3ef87db5d83758b58bf68b29f5ee9e5450c17caba6ae8e519667f0f4c6e72c8f4e958b17282c95790ed1c87e06a1a832b3f50d4db6b49740eb1d00b3c6a8e4d56f458226305fbf09400a2d2a62d0553921f31e1193f9c7dbdd1d399f39b121a5b24b010e3ecc854ca4bd5d598c7a17d1af637e17cf253966de1cf25ff5b7b218f4883513595d7baa87847d4561211ce4a874f697943258253bd17a8eaf61c62d9f5f84472d6c81aedb07104a1fc7de22c7fe888770ff730fb19b334fb3df35399322cf018d41d5e2a28e6371e166c6d46d12375cf351a9e24930daa63fb0482fd8dcf31d9480c6a3d06fe13efe6e3d163e2b91e9e19afe8e260a112d90e559115b8f6fcc41266ff7de2e951f8ea44629c7fa13a26164e11d6010b5187883e0cdcb3b987068c92c70f5528db99d7c9f0861ef7bc036c4eb5e734bea4f45c57ccbc0f0fc53d58324aabc297782d392c0d9e962f9e54ca4b261912d2c4b9bdc985e55f6fc16d4bf9995a7255f6dbcdca7d29b62566f92d977dd7b6ec9b9122f4583c456a058273900141dbc4f546373c1bc16a5514afa4d4d56261d897dc2b4e5bc32d66cd81058fad1ef0d5f55152fc7c1b74c0644f07a9ee28032308c562f2e4f5bbdbd1fc8f90bdfe2194f97e32e6a0f3b9ad10ceaa6081d24be56c806a186b2d2887fceaf83b481e41da0024cd08fff4e75b563f9be976010afc5f5b7fd534ab8fef458eeef24b9d04b6edc66a6ab9512377b5376bf8ef1f86a2135c6cfbd3bd7bc90a6a40abdb345b2eb83a426513a45893cd0d0659809aa291b8bdc20408c4e31bb2faa32c5474924742c0de822ec6e4c634657393c181cca94b47d7cb183c4441449d924a7641a3a8839145c35c47bec80cc4f541ab0dd04dbc714c23e7a4b1130505e7198a62605ea5ef7ac0951e1ee346104ca50aa9a51a3a15a83c3ea442045df125061ea9b727a5dc7154f0c3037cde5644318ddde1a84f9e8bc1c74aa8043f91baf94de156f05ab235dcf0151c24b0f694c5615dbb00ff6031e484cf443162fe7cc6df5fb784f54b4ddb5cd576e04faaa7266af92f2506815d0228abd0e7d9aa360a2b0da5dd826f99b030000000000000000000000008b501169b91d65161ae9c60696a15b777fee6f89242eac44f620c67e86f779d7a8d6f02361b1a682ffa53e499d7d375920e06ba00d51e6c5ac2cef2a8242a499abf41fc5abd1a289668fa699d400bc26355519d5e1b1b87c90754b5857d4af76000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003bf47bebc82a4967e66cca05b3665c214bcab0bb9dda46defa86d0d4a7072d610aef3a611a6b502d4bfd047dcec543afebd068c44a119397d4373c2e0245997ac705293c7afc0eda64a6da22453169b7f226ae05d55a423485a64cc79dc13ff8bbcce3b128706f3ace9b076d979c1edd76746fae5a9bc4171b21ec4930d08895031693bc5b1e2573618e45b823195ddc4cc32192a02b96162e9bb64f7c43f9dd9c0316d3c9b21525959501437b5f766b5a310098c8d0d920c6f23ab32172554a4c110a17a3824217970696e52a147a7cd9b3d5285d57e047918e21b06f1c4f3f87bcbf014bef3b96aac6519a1f5149552ca86078e5a33e724050fb16b53330188e77ad032b67393594c5dc7450c188cae702d6ecab94a52165e657cfc93aa641224af0c302258608b80ead03d65a4fc9210c4822a810a719bbb4fa1738c79775f5b439f4640307c24e6739428dba8ec9debdc42db40c1f08394c3bf3064c830fee2cec576fb10208d1803265ad6b50f4cbf605497b1e7410cd3a9234024f6af34d2f693b162e75031faf100697fd22626bd2e4ce74f18ff640796a3570a924a0bb00e7595f82f0d76653e654d9fd86c209e7e335c492446d806a1e7c570b751fcc60d5fa82d719317e00c9c8039dce25874c4eec32b052becc831e69a5016605a32445498c13a336b330710fe7e2308de4eedac17fedf7330d674c9e3519b5080ec47056de9016a2f496e3fca876d63f87cfa696abb05217bd5272d158c3809505d8f5e781614c7e1cf0a0e9427064fa6399db0216b6f614adcfc3766213b8ed04228c71d05189ca4b2a04f0c2eff45a6093132af82161f5dc78ff2bb1453ad8a8866ac1a0ff4022c1289b852d06932223453e65bc4d7efdcae9c3ef731a2b5c93053abf2fdb8d4a40764a9b712ccdde2f4518d8f75bd160a2cc0f02856db5e068368648e85ca60572aeb98b2e67226f783cb474af9360dc5ade319489635d6751e93ce2c9b36214051a6ee8d0899fda4b3a133d8cb1acb0d5ba9ed208e317f4cafc539c18589a359fdf00466263f85b5c4a94d06e73159117ad0b9a8ecb6375459733a8b35ad19369a8122f3b126d97dda7bf88238c939f41c1c64887a481bdeb58dc9ede917ff2a386e8dc56c4a8bc3ac3b44bc2d591270ebb36d923e9a02dda25f69ada33a905c71263d6584bebbfd1219d2af0c243de8b1bad8dcf53b3bf86d63c37698d0a9ad4229318324fcc9ec573b564f2d6e60b8eeb9eb4168fb47948b1391b45ec152a0c79ef621a21aa4b679ec744c6d5aeed91c46664f54d2c781bec0b121d154ae65794bddabfb30e114f9f49429e9cd4a391665d4d74c83e71151d8011daa64ade0cf9e31fdf391d2604c0baa1279ca5bce00a0fb79823af50b92383748555bea10d0e3ecc80152b4e495351d0ed3e39471795b7778ecb4fb9b3c0f340cb139e81187bfac16f74005ed3b26bd40c2c20ffe31cd26848ce940dda89e6c9782743e41391f0cf6fe5cc23b0b4ba3bb6c1413fa1cf71404511ff137200307810a31b47c3c2147e62735c222032182caef30c27a44ab9ab23d7a46c4a181f56de46c0ce46638fece6f2573d60af0764d60b94974556f5eebbb919a2202bc287543f36a766a3a267e43a266e343ce93ae5e3420464b33deaa480cc651068984f7a51bebbb2d22302244f71a163f9b7aa9493e1c0fc80b8c305fbb874105a0010886fca31760bce319127b8003075ec2b31caa4f65fe70fa42de541a2218b66301cbeb4e404ae5fa78c1dc0d576ab3be72f3a2bdce5c435da9d1b9d14e5263c4b113daeeff4837a8ecb90a609976668497dfb75ebbd64150aa4f2313be7e503c854dc41dec2295b91f7e44f664b8cdb482d42fa21d000750c6472279cff7b3032981090185885a8e8df6d1e98656fe2f452c9dee1a9973db527a84da2c503120d6de7b7a84aaef55a89f09344cfa4e873c7691a13402a9dcddf86127ec9e5c04ecc0def9d6aee0aad38cb9f2c648fa577587738a4b6aac7549a29d6978b26de1230d85c935be8658a7fdcaf601239d45f0b22891a691f6409c321a28fa56689d4a96b5a4f9d7e0af23db5ceabb9e0e050e6b4f6dead5236a3ad43ea1c6212f053a2a6f646eee48ca06f1bf33b0395abb97892825ff210cbe557753076c615319044d68ed948b426c3feea4673f21564b8c664d65e7be785f5658b1e588d3a57ffabeaae29c59e50de87f4e6b9f6f1c187196590f23e75876008064f76de9221be6d1fbf7a1e719319769e852828241ac7601dcd0ace66591a597fdf960832bb712f003ebc5b74317a1ec899b3ec20f19c57c91015476125f51f1c88b2c47e8796fb4349617feb41188e7bbeeaf496b3c272d03cc6b1621466cbfbc14f13c2d023bbc375d9bc00766f12ac13ac7f4801ba3292b9beb503", "536a53516a53526a", 1, 168192221, "e6ad0915ab74732849e8aeafb2bb0435306f732ce4964a9ba244ae7a551a7e26"], - ["f0762879041480af8233ba928398061057cbb88f7e8ec0686a4b60856847a1885d4e75f30400000000026551ffffffffd0bf60ad0eab1245b8267ba856f1d1a56a0477d6048334b270025811d6deda2d01000000026500ffffffff09af235c4290f9cc3e4d50485ed96b4fa75c530c7c14e7c215dc2d78698b7f6e0300000000ffffffff4b5c4b26cd78ed0f55fa058d59365e28f183906cf8e1b51e41e4fe741c24cbd70300000004ab656a52ffffffff012a216a050000000005acac51ababc5fa9b4d0300000000000000000272000400000000f7f95685c7139d969a33b9a5ddb06372bc0e46e0d787f600e64fc6f1a2a94b469ed563c92470322da0267900ee1926c82998a08042b3cf757ba28246789fd97d9eccecfe75be3a6a8b1e4ca1650b76d67ed78d101526ab0762ce78e9575cceed000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006ef13446b1e5abc6c883f24fec623ea098401100a22ccaff4c195fa2329ebde55193bb7459ad3484329fea24997e54749511361bd0895ca670e71052a7eda3c1529b422ca5586f80bb927076a56388794edb941c56acec2940b566364c4666aa7131d4697df9ed3cb22def09a70cc34648d209bbc862aae65e0809831615001e03038873ea0894e931af4965df88150fef62d38dcaa3aa7d103f8d8481cde4c9460229709d65b66f140103313b8566e4aa636b3672ac133706fc6898049e36eb53c60a1177f827febeffaada10a237217315daf42c4e62285de0ec85761caa6c46d9a9189380d4758fcc3efc7b982158761c7746ad883b186134f9e20031234ec291d0030f0825ab37bc4c1c89a9f659c6a2dd310ffdd9a4276245341946422ea4db7a62022bfe058105575bed3ab6c45a232e533160167b80905339b640c9d7b7aa172a30032de51e2b8297a1c5b080f7e3648e13cd43e6f007795f5d9e802ddc0d86bdc9ec021afe5cee70a56e9926980c095f5e231a67d972afd42f92de3e82c63c4ff45112022bccec3ed5efbf6966129c15ac4630d77ed31346f4d4cb19311cedd9449500399146b19e04fe4c86249cf04f2767c2767cb6e6599ddc76288a79a940a7b42802f6347b8d1def9e0a67f2d1b10dcd37abba50d10d23fdbfaae6d444888cdf03dc12a508cafd4ffc656755c7867aefee8393abbfb7400ee177de05ee22a3c3de42d24bbb6b198ed987e813acb601fbb90d51cb3d2c1a50cab7ed38656b85cad867440df34082cbf7bd4f74ced05d48488ddaa733772e824abeacc9db8baeead6b4ddaf3572c54fa4a89c01d27c2120fa0d694edf68a884feda901db81ac4038ee661ef3f7be8870329f5f343bf927cb26424a8e16592b64b6183e9bbabeed444eb3dba693f94e06be36cefea9a95cc22eafebdf48ab8d5b2f96684aba9cea546e697668a8d322c3b939509700c0a38eb19a8729e25526cb5a0046945ed771c257059b30c2eaf00025362f2454ce5cd8c55c12128df8082961c4ed894daea849f07c1b173f0be984fd71162ffdb830bcdcc9391f73cde6352c745a2caf67edb2b7f07aca68acc914d4d17f1341a4bc36bdf5d34c898757f5d59e286a473c28bbeff3af98e0004402570df1241fe71888b1cb0df69b1a2e7775e0e26f0c9f2a235aabcbd945f4ea0adb4333194cd0bafa8ace7e73d6f38ee4ca7264149cfeda385781930cd337b290476bb68fdf25af373f8205a1f183968cce5ab3bd31fcd932ca4d7616ff4fa2860a6a34b1c16fcdba5e6de4f7758795817361cddd973616d1fa24ebda0232b39be1479d8d85b7a8f13f444c4eab0b38c7ef0e833a7f4e8fab51e55639bac7ffe84ccebe22dcd3149cd4206f4e6448b0734088c75271085ad80ca9494247d4e11bc4c7234796e690477304a8cca4c16c7b2f3a44563242d5211556e2949b10fe83bd6181885d430ea5c918c369fec99a8247c2a0080810a5bf4f6b914daa67ecf0b961fb144a031c1d7e6e15e506c6ed5e4a009e2d65604f86e3c099ac3dd9999d47648bf44eb6c83fe2bf9d72d75fa59841d8cbb6cfc3b869a04a9828c75cf1e6cac244d1cedebb75db93ebd1d93222bfecb3ed0d07c522c87ebe4ad985f422e2877bbc751a934aa1efd3619ee6c632ffe6121fa7abc33720c006732cb45bbd796b985bd36eb8e324494c35457eeeaa38e77587ad920858fc80d55ff9940e5b5df00bcdd4a046298890524fcdabd5e2d03240ac8d3c879542cf0844755e744bf8a82adec1cc7ec0f1314116f62fa868d491163d835530d7ce48d6a327b11771581e6a42409defc7659b8b934012b7c8034a11dff3f5fba66825f136986da900d52a41f2bd2a596a2cff27fe92fff2417208998d78bba2d98f148dbf5b840b98003f7bbc44b90fb2554247ac9655048e1be08c5c9e34bdb402e5246007a7c197083f6e1928e9ba4257302790ba6b65ba9634e399759d8da1270e95504caab8277a0eb8e15f1405f6700ff635420c5076025f7dad5214647ae4860af630423c717ed21ffe60db802a3e9074a0733d2481e5dc046cd2a04ed68dff4a6f58c2caa2fb73ea5981c84df4868b5e916c8b6902e2e9b0b682eb09d61957e4137ce6fc7ed2660a42564b80907dffef5015900a97d04254a791b506602c8b50e409f37098e10e9ee7d338871d5524958361dd332d07d674601028b81e8c3b1d9722bc8bacc68c569220d141bbbe17fa662ba55decb756d4bc5f22311c6c3a3ca1ec99840464358111dfc2590a1bddde5e0d5d2bf0100000000000000000000000092dd64e1a13e65ca2913bd59007b80bbfb2e22e63d426c679e532f7585cd0adcb53c5261452a3609420b2797d73100d0fc63cead14a3cfe1c0e6b612d93a213e617e55d2f6b084d435c7b5baae9568e5f7a934f488440f0792f551134bb337b300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e5c5434fee8063559198d5ad12e01b02ab882c5c5aafad542f9e1b3bc311b8cc64c2645f4ed79b1ad29832e6c838f1c053c39b9a68ec80196eb91b0762c094ad5e7555650c2ddf3ac12eb5f435d21cee9bbadfcf76456288dd150f793551cdd88f725a7168b104ad6b07558a3f6de73d872330cdd1fbf30c7989c284da95456d0313bb8c2fde6c7163dc84d65cbb369bfdbbf6e3de9427cdb707b3f4cfe2ce15ae03239b5224e6c77eb3935d1e66557b197f238532fb63cd3b2d060da59649be42d90b0ca9a97258da539b5dedb30ede947226073a503593fc49541f240803f1478cf320743f90da9521fa3d945ded44c30552c2a0e915bec5fedddf6d2fd7f690b88e030cb7e7a4f195904432bc38cc672bf59393838ca5af4650633002ac4645258779031335f7cf12f428901d7b8f5a041722fc3e8b5486b8186688121cf715ce74c6bf030cf4b00f34e4ca2d2531e3cfe9a758a3599d385b954678df79ce996d0f816eb6021aac2bc7e933d0a3406a99b609255f75dfaf88da34eb3bf639212c8c9d409db3021b5a1cdfce10c26a0912644022fdd1927156a1e76652187db6351b66fee764dd4123dbc7840c5392bc5205c519c079b62c41a63297a1cfec0ec6f47059217d0ee5349dbe4b9a418c94b0cdc265616a709a04bec0b998d53b8dc822c97e151d58ae7519d7fefedefb320123514bbc59aab95214a28ffef2df57956a8bd01e38415ff9bab47316ffd2df4bb62debc82cca2cea4cc6e1e4208968670f074acc26ac1bd025b46bc886b831dee42b8f5bfc6b6c949d91701c19d1fd31068bcb4f74b4dccba9d0b10ae97e8d5b52492d5545001f4d4280be9c96db1efdc1640226acb006cd019d113e1eaa52d56e1420f5daecbcf8090d8062f2b89a06c16b1fd9532044eed9335390c4ffc73dcd73e6a61b5f7f4edfbd2fa64708055eeeba57c49436ffe300749fcc8dc88274c4c767c6cae5487fcc9cc60497f7e172ffd53eaa81d51be2173a3da66d850fd47edc7cd855bba965de0fbd0be15ac07132af39bccfdf2614d7d5f9f42feb6b27ad44c9d1990c704636055032ee1d9ceffcfd87e05e61c976e61271d9b3eb0134dca1318ebfa3f88a75476a3b7bf44e3ded74ad6713c99a3e6aeff6c0a1e2dd51140a5898013e8eb90e27883d00ad6fad9cb0faeb0569f457dc8a173a4af1d11df5ca893f7aacec548eaef6e100d7036216050bd8f04863c477f5dafb923cade71b0e49e83cb41fa2b3cafd210287b83f079729f736574962ca1ff8865a5d0b10c4db91169660441a57d2e8affc8d11d2eec9dd2d194105641f57e984b99681b98d2f5c36ce4a341da36ae0b4a6b9753bf463da4207c2f24b4d4b5ea55a70883491498d43ddfb8998dad80fa84162aa18958be297c2f259aa2c5cf5357880934bdff5691e519e8c3b40d2b34bc0768d946819a4b7ea27f8e9243b45ea0dac3ec27626e0ae15aa7b22bd7cb8d6787ad918fe1b008587a56e5a415ab6980341600c6d373550f2102b8532b7eb5a317ba9ed743aee3b3ea5b11c29e96c7d71359fe3fa35c8a0927d7db42cbcf8f328e490a748a5ea0e7b7ee028776b6639e5f6dd1b8306dab92bf4b7d29a4f1379a64f8d9eb8ea574ad66795bf738582de4541c0bffacd9a5ea25fc4ac4e6649c2c5ffe41d5affec41ae45f78d97a85fa47d84290723187049240f14aaff3c44f6f42902b31c5e67833493dca6925439b160ebc13f73cd5b7fd941be9403a1f2f208fad45b39e0e957ee555d0741891e03b37d089cba00985154cc499daa4b458d8e9fa69124ee65dbcccab3e9ee237f9cdb4cba06069d515d10104af676a411021113bc31d4d617d1946180f2c0f76fd627b5126b9f25829597bf838110fee364a4ac981f7ed3f6ffb334806add543ebc64625890e0b27e47273677721e21b37043851d59c7b37a7293c9c3d4a2d9699a933ce39133cd503769934f319a0ecc51919e7abb61753a9644af11f8e23f0e10b77b6f002c56d1721af44a7f7519e161ecefa1c2139639bbfe953269632e861b25aa6186c2c865ea19bf3f08d0c6e1e17f99ec1584a501361661af8fac7039f8191731732f77002fbcf390e00cf63726b2786be9b0de86a332a53c13c6c34a4facc298c9989e06b95b5637d2a4fd05a57f419aec978c3dbf6ee70c3d4be6e50ccb0d6dd17c6c4762bb7c40c222e5e6dba6fe3eb22b5a5a6dbe44c5015ec0d56fc75b56e5553667215049b9a417f4e82dfd5707a0bd4f792030b66379c2511696986b926715ed846dcb89d77d4130d7020000000000000000000000003d81386369992605dc2c9302c1f385a70823cfdd9ff4ddede7e60f55bb6d1a750645941f654478327490458eb3056907330b569a14fea55fd7358fbae14ed938f45d96beb4de933000dc6c7e94cb272b34dcb193a61b27a9c1a20fd8c652dfdd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009e1500680132e4936dfcf07c83ca45206831038abdcec1a8b6a30882d7a2de60281ad26822122d43a009cad7c3dcf4dec12c518f67b2c3a530c8c670e257d71e5b3ab656446433dd62474d9e328e9a5a094b4d393ac083f5101a4d00691d2c0ddad013baa96a8b44f99130e91c24009102e98bb620c21b369e7ee23c9d1c3024020650498e5cab2696a8ed14c1d78be61363d78f94aa3581e125e9b73fa56500710207d3b6bc4ead015290a07038c1bd22d407b0dc41fd0526c689b66067464dd7010a01dcebcf439de94d5d86a959355d7107ef481c5779c4dd75b33d17ab308905ca1421ea827814dbd88f05c188e5a6f33fa086c87d1f24d5c416f9d7f274750f56032f2f747969603d1bb44edd169fb620918ed56c6fc0aa620274f3a06b627578bd02043ec3b6e21c0f40bdff42ffe59e1962d32db72d5fb4a07bdeb49395311ebfbf0313ef3c079adf6f53989d49a792bec7c5ebf79a7e076637551ee1ef5b1a22bdae0300f2d58dba4caa079a43468c11c1374610c2c40142a7d7ab2d1bed3b9b33346e030bbbfc94530a9913afe26658912bfb0de275e8b912da2d714610608f7d2de776dfbd7ff9705fb3fc70ed866e2dcd04e287220e9f9ce7c048e528e59c40354865b47df164302761f7b067c0b256efafe41311aac2891cfc92bbf6e95d9e9c64cea44184cc4f8e88a3a6e90eba3d4e8f49d0e92a31b4315908c217945d4820a6887651ceba874a41d99c76ea3af724863f2b080162038bea7fac5af0156b8498af393a4ede625faae0634274dfffe3435035a8d2237d7392c18683dd8317d961380c7a827fd9520bd340583622023eb27fdebab8f89f1a0aa5ee8b4abb23643d9bfed89d1a1cafcc297868a439a1367eb1735c6a61beee0e6c8f2e35f97bb41fbe3ae60d823e2372c3912a680a9e79677d5f1fce7a7f4109450bfce6438e5afd22788f5b10e4b598e5123eae62b6edbafa8625f9d037697b9d94335aefbf47881f47bec4b3f2ddfe51911c82eb31df79b5915a7f6f1a66275f98e515d305d1f4cb27f222a692abb7e5c782e5970ca88be086d73505a3051c467d097cc21864a9070c1862ffc6bc6c3d415a3b4b7ed7b4a1ff8f75c42b1e833a6bbe5202474219c22d8696015b93ec89f091b8878cd072a7e05f87d1bbed535fdb46d913672ac8fb9d6f65e2c150252872e1d8d79077daa40367e3a2d111749df40474722202dd1c339f7207cea611425f36d90e81d9492b3e4d8e1f571dc076e736e9a2a1ce27fd09d552a06d8f2e81c6043f72e2fc1b4a2d85fbb9c98f6da6bedf167b7585e873536883adc57617718409fe24a43c21e62aaa3aec817d625df4af985259c05224c961ea490e66c0fcaf2236b0e6b397d8f0afbb5629ad9a19532fe68befe10c06de7db78f17c0c890f7975ff90bfcd76fc7b0848b9fba963396e8c38990539b38ecaabecccb74cd7731c8b009f4b833ce874f51ab5750bf7ca7607ab71f0cfd69113a985a780a2b736998413dac0d0b028d4c19ce70aefac2520d843a134e8a7acf4ad404fc6f1fe98c1c8461f291af2a570496135fb38685f4492fdf93284e786949b23fe62d5fc08d75f852161e1b219c8a09de4b8fe8685f4651f582c47e890e548a5c3b939009f9feb4294bed6373f28426221b05a6f93445c8233e943deae440ceb37a87ff45e9430c97dbfd54435d8c5ef3198b9fc55f300bc9156c64aa719c239e8ea8bbdc8bb80e178895798c2a4aa571d3dcaf0e923851eace0ad6c26181c21f92cce1d62aed61552da7b411e7fab1e88df177c397e7154e0dc5e78a0a1631293aed44071e9c005f017d448e46c97018d24edcd838a7252d37ef31b340898be408c98124bbb2ef73bb9be0795ddf205717d344bf5afe9e65e8bf03e41fe9d6e84a0d98d23364e943fb4788e6b880a9cba50964f1617ea22aa1762310cae88a49e8ba497f5ca45821668788e728348fe700d5055440b17a1370092ea802478ce1a6a86dca54185d031b9af9d8f6754fabc943aa3b1f53ca6769356fa8cdd5d590da451adb63f2355f0f6401f6a562ff16c300ca6ac3030aedcfef9d830501bdd4e77fe2a96176320d1ba9307dae8b4022123ed72d0b53867de48de835c12b2b2c4977b9a8349f5aa30b68f6d757586a389c8c3aaa4baf0e4fcf31a10ba34fe8259353195733d240cb8107f46dd78f2970171162baa30da2518ba6d7041e50a498dfbf881d22b7dea780c6b00735b5007eefa7008a6b6697d8f305b5cfd49177f403e3739faba34c042c12fa7f28b07688d886f62c72e258e360e4e690c26a86b749ef5e7b19d48489b7277f36c23fa0d2c1984f9f2ef22ef5f8a4676e90e3ce7b98bbf10a08d463815b2d138853dcd22dc2646d8c8d8d2877564533487a3634fbfe3a8667a8e0ddbf4fd4af6d3202", "526a", 2, -12162373, "ff18b2bd50f0c75ccea7e5b1dd670c4aca36cb09c195a5386c33030a3c2c3136"], - ["1fea829001f7a6e1e3ffd2bd37ecced7c8f716965c160614fec758db75498310dc47f8ee9b0100000009ac5363655300535300ffffffff02e89905050000000006ac51535365524afc7d0100000000065153525300001c8b2d54", "636aac5365535165", 0, -30289340, "b27322eddfec9985656d0733cc2704d8ca91f95a66847e714bd89b61fd78df79"], - ["bcd5a94f0223cbbaeaca4e7bcd136abc02fe6f2cc4d14f4310b7f55baab82f94ae6dbdafe10200000003516551f0af98d034792bafddb1156252df7e267a28e6a152013aca4392d56060c9b9c670c9f07103000000025265f2bf1c0304559f9402000000000865ab00abab5363656766f30400000000000bd4a201000000000163fbf06e05000000000253ab000000000200000000000000007b380d0000000000d9bdc7658885a7d261e8ef95d9561bbef309323bd3f073f9397b39c841dffdf7ae18b03201867dd70c79c27f8385a6528142a7bbf5da10a4c2a6b688b5a0d3fa9f0f16c75efcac5590075d5290085b7090dc629e20f495472acd822e51d690b100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cbc63d28964a0fa41739c114bd7be688b3099389e87951a985fe2bf3c18ead44b6e2972d54c2a8742882b6264f1c515ccb3a8b262b05f466d8d96d20aaa3e3c2afd156bc4f1b6a597d608d47c1eca3a4f8e82c3b2ed0a1855dc9220a5c5188a08a432db4e48dfe109fd7554692969797d3b54c5e3839cfe5a328cebc2d0fbbaf030bc8881639a3c3f49b93acd05f91d1911323c165127d5c00adb27edc73cca8f70300bc4f9e0815bfabb3626cd990eedda6a4020db6c57ac92025f0fdeadababe180b1f0a53623cfe66807346699ea30d1500447d77793fd33818568124b696df176c2618bbfa066003236464189fa56e73c376bf44e75ec02cc7d5b2ed2d0e9621080318ac014509f26d4a2794bf27db2ba9df6bdbc75ce7ecffc2d793ef47cb5224c70211fe8e6de811b7c6b57e32677fa901d965223a6c63b6f81d6cf4a94d28e8e5a40216c7739858243d19851e383ec7dc0d82ff6c14b9ce6a3d148ef261d27e08c2e603002ea5e1705287a8e879f4035ea472c6b4c69324f29b31deb547d173b5a7835403198a8625705d2626d1f5af905a4539efceced4341f4ae5d0debfb7d62c65e86287e35fe0bbf643e9b7bf141666d81f5c02f97f05bc3ba06810d465e94dadc1c3afa3285a2fba373a7fcb86e8b3709b230fe17fd0df4aeb0c564f3ca9f0619ce31d8ca60d57e3989d81d4990f6379ae6dc092e2602703b44a38d819ef427d3b44787fc6b239ce5fc80f8bb9aac3a6aa52a682081a621d3a49824849771c55fc15b463e864a892afa75ceb1388b81f1e941ae87135ea1059a6c99417aa667ddcf82806c83d2d89c9c3ea8c233be667e92ac1b6daa2886a31c874ab75f34f86282256047baf8281090bd1f43194e69c07555ff7bfe5246ca73429219ed8d99dbd6c3df5c0168cfb9af9cc213bd45b37171a897c6aad2cbd8a349429743e7ce1a49816d44dba1c847f071343c164cf8648e9f266e3f0259bb117338647c6f27862e44baa741ee59de68729b8c06f65efa662949266cc26fab90b714c3d3c61c5770065fe0b94ee47285ca6670c419105adce7f2d03492257d59cbf1f98ee502dceb274ad43ca8f37fd64b221e35901d24bf55db002a9093bf9a28c5ef0be2d7e8f0024a456221a0b86a0062c22eea54691f1e440e48f555f3e9b8979daeb17d2f6b75fc791e493a9664ef452759d8e87c6e24339f9b8a3d74fa8995b6aacbd0def385725946515a78ab6a53a9a9f49408f4ba24589fa42835e712d7043522537c7572e6613ea0d3e9c7a6671f40957e32cceda5e38cde42701bde4e4a1e4f7d88533d07d5592a4fbdb85725c830307007ebe5ec8579415f5c8c613a5efcc8e20d83f991a9952a9a97ecf0220de067a1e5ac382505d025d8a1304bc0e556b0683ca8204f86ec57d56a6dae13dec5f5c72bf6b4e1cb5cbc317af9d157ea57305ad7df9d2c44f0e2d1e99109e78ba39bab9234a7a1b1447718424d3c869230b5fea2202deff2d140aefd08b9b0f5cc1dbc197ea312f2533ecd29486a1c975a2a2828443687c94a7c19c702ac16f6a6d07f8ff9f62f0b8b4641b9f18b8e87f59dfaf232e305527367ee0d4b369f2fbaccfe8146c6057d4469fb81ec7064475531305f03d4b737c19ee037c0990f61c744751a2cf3c97ec73c4a71b44690d7dca9a0db461c64010a2ac6f41b71c3ee36e2f32bd6c21921ffac3e55ebcfb41f0829c99a3d97676fadc926ba84098e564cf2af7a30c775a2b1f022520b5cd60740d099c53129acb94a59fd15dde8341211ddd8f7d6b58bbb0fe2ed4f6c9cedda38d86a6b7ba1d0f736cc337e838502b2ee4115d566008193b894dc0a5e30d3aab525980b1d92451133849bc5760ed4759f90f1515c0db486abf8ad45d1b5dd4e34d621a3fbc091190599ae1c95d3019b21bbd659af35bb3617f223655ba34ebf41e8878fe1a969d0e7fa4928fa21feb28f002446f06a502e827a6360e3e53f9002f2ff8faf07d9f87a6cf2d06aaefcf36be306a911adcf8affa6bbc3e91f1bf3c3741674151ab9cc9fab26a25bb489c5f0c033ec8050834308e4329fb3ebf48479c508634f401844cdaef5e08f299946bbc4bf63b13898ff9d44551d222cfec0d52ec0ae81cef4e5eec22dbca13d5d48807d1833dbedf39e9c03e0e2c870ce3fd4128c1cb467ca8fdfdf1100b018bd18e8e4b4e4d41b857b775f89285c247f356507a0a5f6906773e8d26ba2544686a07d157d29d63489c6e5913f6e33f583f2ef911c9980a4f1b9566487350340a2e968058c834c0ecd68ee844050000000000000000000000004026676682bfd78494d5513fe415b480c9d529daa8bb73472bcc53872a6c34a6032cb2fd9a97bca9ef4006c2b030cbc6b59151c46c1240983f309a596704c2f7cbbfc39336c35fbc95261bbb7b52aa54dc26ac76cf7b3ff2ad4f22e63a9d5f63000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002aca253a9afcdbac5dc2e9bb91f0acc485fc8b25152aa7b0b09a42c790ede59a58524b6d07c0e171821e287f1c007078f671d0ec96e3bdc98759465b7d8b67cc1d4f747336d97f517b1491b0e196f804bf601fc26a2faa83c41bc85bac9704a52729205e03f8f15366cf8674cadefc9b003c885b66c994948a653fad3d66dc39020fe987f1dc302e0869e701c6ec747549ac32b9651ce51a38ad0f549d682c7b110317d23abf8bae69852b9651eae5e00507fcc53d733e51945689bc459fba13bfff0b0ce29885568f2738b8e14e6e12266f2319699c8b0155d2be1c3f83a5649b47f404e14896bf43f29165f01572b6285bb7103337a461578ed7f3b7fd77359a4720020635f074fefbbdb2cd8aa8cf8a211c0284d546932f0ce81bbfd530afb5effbdc031120b320f4148938154f4ad53f35a6989928b655efeb8b3028584c939c465f95030f200bf41da91a73f9b336e8b468f5f2e0f3c7ce63c6491104004a4250eb85c80312c28deef9163aa2d6e87e94d5588d3713ff7f042f8480ee17d686132aa86a2a03276d0461e06018b951f052bfd52da80217152de35b73835fac60c47ea306e77e14c456b8324708c81a337a5bb54a758f58d9088d057b50c02c00e185c700234df92a80964fc8b6f54f2806622237f8d16bc00dba9ba5edf91aebc808a003418ea1f34c57424db1d0e579fcb491df81c295f6c6b67397c0d99c0e991e95f3d0f26a8ee7edb45ba3e1d686dec5d572bb411f043d0edc072978a9df643b392dea62a0c3598fd8223eccc1564bef3c38bd82db43f27002a7b92c0af0912e6781d0fbe40760ae0c11b6340a52967d09b0b4a0c19a62536f76e41e17a6cab3f4abefc51ef41dd337df10298e0eed80594fc8c191f167d5a01a53a9ce9687d5dc8f11994557e978cffa07a3ec62e22ce4be99930114c1f5334fa011d2e83dab0b53fdab145a6acae1553fda4755380133e367d93a49e10eef8541f8f9fbb8df8c887ff87f4921859fa2f532fa339f8e11245cc3fdec6a2498a99fc6c43e73669ff7edcb38b404d8ee3552f82dc685b26c985c99c5d1d668b765c8c2210f6ab27b84b504d8a59a730f32f83a0a36d0bc1204dbf93da22762dd4fbfc75e6e56c2b96b8ce8b0a47cc8ddee310e40ad53d4a29451718813d20a4fc84ea84b80dc3a31adb89b247e905215bda5fb2d114213b09fde3933968fffe8a065d2ff7aab0de54cd1e2f518b3f0c0946960f1debf1e6972f1b8ba56e8bc5e765ec7a670db6f485e30bdccb3a53fbb5d0d037e9653f3b34c13181753027f94f39087b834a6cf75525f28a373f57c1df9d1624fd6da4fab3800ddebf05136f7a380f6952c8e6ed8a85ed7d9a18f19deaa872d260c0fb4669a23210aa7d34b88183c2d4e07488030185ee20ca99dccefd25868f9ff9e654d540b798ac909124f3e6b437d29be08b883a5500fa541bd8bddff13029a1189e34117956045e73261117cf02361cb855af7c7f5818c11d128943509ba501218f1a57fa590a4fdf31e93e650e2f455c50097cfa224207ce1837235234b400028b3cc7e848e17c889e099df0124473ae4d00acc027513a15954ff0b618a75f3f94572dda4eedcce4ba2e4552d0b727651ee06e087aaf1766044ccd82a39431f13af51f3dfafc341c0007f6e8666950557a1cd36d1762a438d37af45addb36fb8f851c3e3cd34ccbf21cb831da1a8124e6d81962178cccdbd3e720035293c2b2366ec0a7de334cbaeffc7b244fb50c7ca3e2d37ceeddd9144a7b295a013cbf0c1530dfd2362191b76eafc859ffc493dff0a150e9553b09a0b05df79611fab87326ea6f4f61338e941c177266f481627287933d861cc4ddca8b45d87bf702416e6f823e158dfb10bae152bc7ed1b9d6712f2cf20c46c4af4a62a739477958a0a3bb145257b6c9c768762046a6ad87e3b66187dd208e9da9aa25b5ff6656efc52708db0d0450ae0b2f1f14010be49f5a572f42b6b7de1c31c0025fe1abeb61ea7cf524c67b1dae24b6784416356f92b9b476f7031fbf8a4542c5e7807bf26310979e3b1b9149a304633160d8107598db08edf91b2295114c0e1ad92c6c6a5591fbbb40b10fd597ba5c0c55096f786e5b6a9183a78f52d6892b72803768a251b97aec7d04073fa93da6ba22630322d50af0bdc9c526646bdb485ef4473b822cded1f4c3e6d7442f342ae1f5b1508c9fde55c08bed22d4b15cc102674f5d1f7cd629e5205aa38734ab0eff553b5968630ecc5167360a590e8be2ac0f739b19445faff82b01e931be1f2c09ba87c17f4954de1df498feea6c829276748fe95268abff14c19e6ef9cc57dd960010af3c8c57674c73b8888ca4cfb40361b8cda758d3377143def8252942d4f6926566c778d71efbabdf6463a831740c0b82e083e6c23bf3c66a31a39308", "ac0052ac", 0, 107574252, "44cd030ea0b398afe12e7e709ece0e93e14045ce1dd715a9c8a19870a152edc3"], - ["82b8f44101da90f7080c7767b6238427917eb58b7d86ffd5380eae5cbe0044fe194bb816e3000000000500ab530065ffffffff022e2cb102000000000953ab65656a00ab635164d3ed0200000000056a52526351b75bf805033f2e3f01000000000000000000000000724be09c4cdb92bd4e494cc5d65120dfce62b608473dc91fd6b97fd82531cc08f30917e475ded1ea55e3a9aa1523b918d170bd9dcca7117cf112dc61f4c0c24c91186ba7ed2eb056b6aad78773ec40327058d763f92f29d96f20d0f8200e32a40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054a916d3b79c1fd63b9264c4864cf61a744239e9ce8e5cd8a8db76844e6cdb549f49b299383985803ddcb9e92bfb38679b3d579744e06d83865e058f8a8f0ac6e4468bde8a8274c441fd553c0c712da8ca8e1b0bb5ef04194d65adb2ac40281bf2076b97f4675c31790992ec5fd1203241cce6399c6f2be7638717b24d166ade031f69cdc897e543b26dbfa19c8dea80c65bf4913160285128e9ad0a6fafd14682021099785a8cd6e8ccd90c534b4500039c74bb36dbb50d24a3897269be30a362770b2c2da6baedd2f307dddbd3b321b9fc08fc50b85042c6ef1bc54c4dfd251a9c0825347a16308b554b85798a6baea5c0a5a0fbfaaab2881077953670021225b91403036a66de053cb953995cc389266c8a0bb226507543552a8d6b777315179759f3022ae345dc7fd13c881ff482e2a70f39676ee69649fdd5a6c4d11debbb48d2017a022544d415273607b6b84c209720c2c297007029971eab04589de2e50ed3adb9f20205163e138d09faa055c3a39b1a87fea1eedc84cd4b953f1744c3133ebf8a775d0304ba8e37861407f6f673f674b872a1b9202eba778035e725c917f4efab688616799cd77519b65ac863cb553ac297e1f8f701aebddc6918771add15acd328e237d6990ca0b5ad54561677e6d614f3c63307f918d0a071c83f613d58ea5a939d49891764c9013e7a8817baeb1cce3ef79f35290a9361cd47eedb905acd7ea011ae358441333f3ed5d10c8dd10d566a8d01f0f68947a54dd74e1a555000bb3e0f2db5ddd08deba47441759162c66455a24e22c59475a6861d50440185d233945b02a57413204fdf9c267fcb65ad5a94d771a893afd56c8c061e80f81e3acf00b560d87f769f67dcdc3225288cb6b605bfa93b37e8336964d402bd5982415a154176407b8e7b46f1f98e55abc4e9e0eff38ad7be4334693dd5ef96209cf91660e17be7bad5f8098da88dfb0c4045e74e094c30c8351b6d07a95f149bddd07f6b3c975155fd7ef2b627559c2dee286a7bb38cdb363522ae1cb4539d0291cab4c4be1cf6f0edcdc49f33905e1c5621cd4a81a82719e8e3e4867c5be1d932d3fe1cbfbb5d993b1d9da27b8f77227d5858dde217ef768fa0f1c05d7de314b24af7d66a319d5c8f260352ca0b58a8c0a14a136c5b99032b6994660bfaf271b3efb355c162707cd237b8f5a601eceaa493c03d9f8684bfdf04ba331225d341d86abd356cc6835a8f614a7c8dd93e5c69f1de8ba3cf04de16fabd6e5eedb9aea3236093376e75e5a1b20dc660bac661a41bfa33dc6a2e89437a15b9ba147c60122a7a1ded8d42ac38e4bb34cefd353fefacb6c911656e06e5c9e85898945cfd03542c1a537a75fd06a7ae40fd4831f6048972d745aed8c88f46393ab0e6c9da108942964b89bcc50d731bd02bd4f390ac35fe0501e89f86ca1ca79b849e611eec80cdebb67d24408e7f217e495d82981cb873bdc71187792423462b4e48858c0cf8e989eaa6df83981a744d979baa1d9b6fcb5fe4eab9386e8d65604036c64da39edee127e85be30b98ef731287f5ce955d6a234d49da6280113052ff455b1a59f0ce83a45403a4f92829fd8031f596a5347e8035ef52ebe28361c28f40948938ab9ee55f225f07c980737a0afc912e77a01a94f46babc19162e1ef3a52de94d0170a578f9f1d75b8f141a75100f07752b1f7d47a8f9828f7750e6a4bfaac9842164110a64c742f2c226715c7e51fb2e06f9729a6be160dd7d5cb9a2daa6cff94bd00e44f08d69786cce6b8cd88caac39e0c8ec629d866495662fe7b9078e3ea0bdc1a3c79fa292f5092ad0cc88101ceb5a329c068d0149d953ca4e95b340f607c5d35d86dfcc7b5a52364d6c3f6ca64f316e33eee945781cfc69effdc6f4fdb03c3babd90994c4fca93f0a07661ace346dd3ff21fdbccb726f42c85bb0fcf8690f0265c927295890637977770249c7b49b84ddacb0e51e8e83cc25145c8571bc38f817e264919e6a06e8d670668ee0dbf9200ce68cf48e2c79cad2de11fae98884484888e2be877a11b1bffeb6c03ecd0d0de2e789044e8c606f6c6a29d295da9e0a2595fd2008acc47fc66ff3e71a0bad89fa640f7692fd01cc372a10b0c1ed01cc1e80ec92c895ff03dc252f7acca5d029a1db31a5dd325a59a9a1482e91c09f43eb711832d585096b46ebf27c25fc3d1305cc77ae94f2e7935d3dcc34e4584307108d8548414bcd9c87e14f8e8bde1226042efe407c264f63828e54c72a20a1edffae22724ecb55375e76a917e8c6d86ad44c8cab53678eae040000000000000000000000000d00bb62667a002cb61cd0fde3c6dee524c7e4fd7b526856ce8342e88b45735bd5fbfc6dc9e036accca287b3fc41a16485e8460f3ea183826196bdb45f68babbce0cea5775f4dd25fbb82ced95745915b7972ffd83c2ee22235a9a8a176481b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e4b7af4ef33c819679b560eaeaa7825293859076ed47d82b6c28a7b69701fe36cafbe61c80c9480dda94cd10145985915c78e0b13a1d7a023bb426327f707eabe1ed4dc9352f0d041e02dcbff217af210ae7992a6522663dfc02067b2ae04826d573e28705a6b6a8097a77ea5beb74e8533875cf738a62c755b186ba26e979b021d6d7f56662cf581cfd2c296784ee88c2c14521acdebedb0c04a91a21219490d03115e78271b5c03d9802edc055abbbe9c744c94bd10118e04c228e0384d9f6e250a1cfda6ae6861523529e193d4e738c48731d97f92f33fb056111595c4c582e48d1c1780a39777720a6125e4a4f635bbb39f9291d704953665edc36e096ddcb2b1030f867681bfe57e6d64f139e80750e40e1c405025fe3442d754cedd2b7b9b595e0213da9b831f0ebe9d5ec7da25f057e00c9caf8fd2d75c8f50cddff84b0189ee25030aca94571336a048b91250b0f6514618cfbd2a1011673ee94cb7bc8aacdbe8f9030089be8504d09c4f6f13e95444eb9ee39fa88b03022b0b8f8855d1d70f6337fb0307c72e692bdd5a0b24c9173611acc7a8bedb6256bb8a7e040ccc452e3e228cd1e9842c797b2058bff2b656ed7d8434ea18e4bd07160b7e7f9c63111d4d6fc3aa55733e22e589125a129c04c78aad7c84d76f32d72d3ba2ac1f479b74ceba8be4384306b886359adeab9258938214bb439a6fd2a196f1cdcc737e79111acca47da60eafaa73e5777a798f66b5f52fd9161fa061735eb391290e50f7d00970df1d20d1818fc97d99f877b81f7d0419bec687c08a7b1155c93a96a1c720ba9136aba523bf61acde31d99895074f247f1168af8d9d9de55e909e1a1eff1f8fbda24a7f097dea701ba02ecb6a3629b28f15a731c62cda6d7edbe0e3b7e6b57d431eb60968a1491eb3574a3d81080bc2c315ed2d1aba04657cf590b6721c280948962cdfcd2189190648429bdc9ef31cdfb36e2b4b3981f5ddea620412abb1cc52eec1d31c5ca31e1ea2b236c6310af01ae5cfd11981ad6e3be3c45d96955acb15d7f95f4fb98d8d5b55d8da8a4d25c57a3b699637bb1edd12c36dbc73c9af72819d8ada0ebfd222b8b1bd99c1c36fc3ef994b389a5b6eadce9a83cbe48d12fd9249acff9cfce4970371cc534553c342b415ba2ca80156da9f195e1e963a007e377be989257bb7c8fb55bd6fbeb54094fcc565ae74eab4bf5880210e4286d98974ed439439a50165bb38f5d8884c7f80da209df20e6fadc927219774301490207baf9fe1bbb12bc4f00997539ef8880b808eca31a82efad8d5af990fccf05712616ecd763a46cc5795c71ba989963ffc43d60e20f07cbbcaa693ed35bf227b3c81770beac95e22bdf3bd04e723d230a3a1a763b5ec27e036d37aef61aa42470cf338810193e42ff5110093b1bc4a26cad07006757759ea3deaf1cfa064b7a5fb9e6c402b469c0408f80f51db952e22b50c52b4ab186ef76aa2e60a93da85e1aa2b9b4ad8262a32232d33f2bf1a459a72d298450746a724b0fa15fc555dafe6f58469663126f36bc7302e63b941ac33a2adc415b244ee98c0438f8e2d440f55a4b1529880677f1cb4e2a1b823d6e859daff89b0d274ab3cb7472d1143cae0b471fd1b1c5c01ab824e0594f91968de421b2c666a904e4cd676aac4d22baf503af8883b5fe6b1034f892e3032e9c20a38a03bb677b5c0ff9c5610e4163e2612076e2bca3e016b4df2f3be5e9eefd499a7ffad01fff939c56b5c6cbe7bb20e68ff1aba69c71b8ce38185f876e573546149cde481aef73ea6fe75dbb165931e21a49c2217b21146598a5c306913e7ab6cb82954adca71064828f042ec77ef3405261a376c1993278288c49fe50ffb9ebdbe0aefbeed7b52d0b2e6f442b5bc6ac65bf706daa8cd94a01da5d6dedc106eccb1ef32032bccf52bb291b7ba80b9f6f13093ed5b73057ef678aba8ef861f4203baec68e8d698aea88847efb15fd485bdcc96317cd66822aa075aae1ffda83cabf554daeefcb1f093823d7ba3866b723d18ce3b564618fc318d63c8b02fd016d2478cb1c91ec598bf22a396faad0546be55935ed1422bfbf9725bfefde535a0809b12d923f7c269bb1a7638ecd965ca205d5189752687a6d5e04de91528f673f95265a532a5133bdd585366c1c14146910cdf40044a6d80946d6739c16f96ebf59dc8f02d980962720d67ea100ee601de69f5cbad94ef3bf00d7489c202c3f050a2ec398da3ecf4ce0ab18e75ad1eda1ba2f0e14cda72e5bd8ea02b0648c90c78691715b646889b00000000000000005072870300000000fe8b82a1fb0ae1435d83e17e77105d2b2b6cbbf3a20d2e4add5c4b477b09da7e997466c0359f414fd7b462e1217f36899e82f9124f6e9000bf83a641b53a03257d28f7d650d19c024af4e6106d801f8a7295bc7ee7aac33e28cfd79424d955f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a01d7d614e4b01729430456d4d07007c9af47df247b71e18bb1646a7690ae17a1d052c13201ffaafe362fd9657996e4283393047d218187022830c79d9fe79287eaed55e7bab619981ea857947f5027e640980f818d4d8a1fa77d5af5f0d102a7704ffbe18e20aa2ce1dd8256081709efbabe7176736fd7cfe661cf89861cde0201b18cc330d4cc869320604c4800beea2ac9cc6b7bca593832118c2e2c4af465031fd8a1fa7d9d411bf2a7092441ee24f60b136a77edb972745131a48a5360e8bc0a169dc19450582a816271c87c378d05b5599fbba323aab1dd5a4796c71a55aa3614d721113d3f82e28b472a0a155c73f75c34898027cdb8e9ba150a3a9804de8f02254d11ec9e778a6c1272c4a42a02b7e6a0744504a2d0487fa75062893632bcf60318371b85cd7fa59b2e79af7efaaa26634dae94102a5209d566230c8d4e0bd6c4022a187d0e779920dc86426fb45decb58cc8a1a2ac70a1e2dd3f33175110b9c797032551189ea71f515d9f157c97e489dec9fdd1057ca7036cdca90b5303953a3aa90211d66f615bfe36a784e241bdb834b395672cc9664ce2dcd15e1b5492ef5384b2b1040af81d4e15b7b82fba6a96eb6e82e73a4a9b3154a28e1c1862919b990c10a017be6ff91177572a5b10025e47a819e051bdd7713e656b794bab7d34e254b2003897f39b23c9dd62a0469087dd324b71c016302a349e85f27ddc190dd984267717d22c2f53cb1ffac17176d5fe454c0a7fe485e2cca5f0c9d285e4ecb9cb6b7177b9e43d5d4df178f61dfd05f5da3201d05847e69965d9fbec2be34468d2fdddb52c5b2b1b4d91dd5890cbd6099a686931bae577292f37f82d455aa1341b5e6d36169a5a1283abdb00c46a120ed056c4110db1f1de8369d3f3add7cbe85f7760182723d7307604a48361e18d5b494ef9d0f939a0dfef4e8f21b145e9c30bbf829303034b40a3c18327a5794754a158c065568a909211ab4e13a64d6f1999d39448cb396d7b100edd0edb1b14cd4afaaf2fdee78b476d5b57d7ab9721b0aa454b791b990374851c5fd6d9d23eea2d545d7eb8b7356ef47d370ae753bc9ba4f9be277c4bbd84c252571343ec50fb11fe29536188055e9a3e7d996c3bd31f8603f438df2cfee4588baf79af4e9971f6a308213550f1710477ff16a7c2f9db26ccdc46568c96c201eae9f2d9d15fcf29cc8f964793bc10c2afffdf84182bb2d566e2af596ee9d149f7e351e1a875b57fce7ee780d4dbd8374052b0929e09c573968adf4d030c15023a4420b41042258c2f34e6e6a4cac97a630182861dff375e2a70a0e138fe9c03fbdd18df7ea774e85c7025343a69776968eb731f8cb2e52a9bef32a3599506076f8d5364f1162a15c5138fa14df5da541ffd6f556f967175e261e50d11ff82c2f205d5ed73a136b8c5e54774824cc93eb231de4fe0ae3d322c78ba47c48e87d3cbd0265e5a4788f41001181b3550eddbfe0f27fce664c6f6975fca9dc8fdb7c8bd312a1887be3d97ea2a8bdc8078cf12eb50c7d2814b521168861ded1703f4ebda6458f56c47ca9fb9d721e9f08153ed4431dc945540e445d281b8085b1da9cdb40c624550f596e70fbe74067d8b18436b23f26859ff0d2e38d310bb4619a4544d9e28b268d2366d98f1c282a66fd7cc680bc2a426e2a4fa1ab6f35f934238412ed7b9d46248c95e7628b3b4ce282077bf99c7fb6e3bfd7320e866c9cb37d2226a91c87ac3f84fc3b47b6cd8fff5a44231a36885770b3e4a926924f670b74aa723913aafbda305825eb86c213399f50d3371751f02296088075cbefc9110b7c07dde7ad1a6b78e622835271dc78cc3217be84fcd4a108af5871cc2b7d91bfbdbee12a254c241a9e4203a2494a11907705da9ab33cc5a78e88cdaf33eac188f8df9a45df5fec4143db19bbe77da5f9a0206d648da69307b1c56ad2a119f0245cb84e300af1adf36a3c88f5b9f03f6b8631a1585fc0e7768a6faa490961c1c4b8f1de6a296de97f52f43f7b7f2d060d1e4807a8de27dbbed473839e7aa0c8611fd2362b647a4b050acf04a13e1476af0241624ae74a997b1c49cc7a136025e9d3bf6f6cd8590ee08d99948b2dfd22709546ee679e41e7309c71c58b9e9a1be13daf56f96b837d2c2286851919cbd3944ad00960cd721daad9c25bcf43fbe749e770f8ed734d33ff34cdea1a3cb9965658a9bde4ee684a8551537f138d672e41f60e47be959d9ac20dfbb17993a491bb339f45c7573cedcc1c3c9f70084b0832ef6d7f35cacacb17ac4e2c7735219050611a4860779736135ffab2b8c0e925a28a68244081abe9abdec702d7444bf85ee430ec7c954d54085f1bfa2dd7ae64830731a650b4362076a5c28ec559ce3159403f1ec993209dedd0faa915556a84f3e6a78e87637edb1c68edbcd00", "ac656300635365", 0, 2062427382, "9fbf4d6cc85d27736a3ed295008c2bd2a0ac3526251b866e074aa4330f19fe71"], - ["897afa3202d395fc3e5777a38fed5a2ea36ab53ffb0ec25cca8f58ed23440d1791d53230fc0300000008635152ac00515253a0898f9217fa68db9b150a3ceee7ff97fdcb9264ee136e76988f22fac841d192aa1b91af0000000006ac6552acab52ffffffff02ffbcde02000000000163f3a33400000000000000000000010000000000000000d095960100000000ee41d784990f73dbaa548929269756b252e3df85d52098495fa0431525087a020a7bcddb962cd9247fa75f2dcb5d9fb633715d1c3c01ac0715114dd7ff755f71190f398b55582d49ae992dd09e93597f9e318396ad40471d0e9dc524f472408600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fc41cf1799399b0ecb79477f344f998fc4d52a955cbf91fc0d384e27f39abdde3c04bd6db0315a20f316e76116b0f55c9795036a1c9e7055e12e9e1c27d49ffbd9545c84e19a9441ccc2689ba7387e272e911fb9b9fff8cbb416b71d62922bf57746a1e00598c30cbe1028e7b21b5d749f2676f401473d8b05b1807ca4a41a95020c9e9aeebc9a3a2a63c457dbfcfcfa51bd3dee0b4d24c8625b14fbd882e95df8021f9c8913cb5a511515422949b897fcdb6c5813ddf51315e3e2c5a41c3e3c86fa0b26bc645c4b27eb1a8e3b7074a5de810fb029a7dada18a7d8c49ae4579d4064ad119278cba753bc8df82060e67a54ed7494d916b6fd6556034aa4037b02a96245031248e36f0865b0935dcc0eba7f81cf1bd6eeb75f0f517996e1f4b281a328e38b0226c69db42e07ed7fe44e0fd8f44ea07bcd0a0810f0fec9db78a0509edcd19f560205b8aadad25c1976be265c698918cf157137cf9d7c8551de21ca6301a52e7de202291e16062f93928213aaaf3215b9a9b63700eaaefa4b33dc9b25b49b19d632b8031b4ec7467a39bec293d0dacdab832d16a055cd94de5d761052224e2b900b3caa8bd363166e82de396e3b2e8f18832b591f4f4eb751daa913681b23a980ba1ee43a8baae540f97502c1f2576412c383bd7a79af4fcb0386f6fb0b47d0f04126e3801acbd30a88df65074732ffc77a07d7077134697789a0c043bd3aa7f877d1529297fc269740a5459ae0b103357bf0cb409697027e89150eb3b1d6ab61293eacabe67ecadd700fc6617219cc9df797a55d51d31b54a6c2d5955cb54f5fd28b300b3f049f95d7001f3be197a1437995986e840d0c1f4a71725ba1531a350ba5142b9049efb98fdac264de2643bc5ead18ae14507409628dadadce3c6a66457c59dad3beb3c6354161e2739929ba84f5a160740b8a16b3b3819f9d6fa26217b121b5974cd6bcee41918b2bcb1b10858856dc31182c5310cde8a4044864a636151cd435b08a3e61b49a18813f55392e03cd744cf4c67efc9f97d350b70b850c3ecbdbfd135c64049cba4ab8db8d9da72e9d5c07126b7aa4f2dfdf470d621bd94735223d41500ea8409139c9ffffee933bb15ff49c8f009b29aea83b9d86d0fb57841bd26b82866b7a6e7a4fe400487711f19fedb16aa59b346b780266767639a906d9792028a6b0cd9cf9c91be1eac6c8d99b80a611fd983f8e420acb9efed271e830dc6bdbb60d8b74c2125b73b492c694af496d38dcffbe6395fd50b208b0a4e7771aace630e6c82b156100097fc87d1a3a1fe739ef3a60ea48b1827b5de9ab8fa0464988c4398084d1ee5b426118f5cf8a7ce26034510fc905aa48794ac16bad73309391a50b3b4b66307f2f36a87a96e1a024d69b5e16a90da67023faf33e1daa3859e38a81e6cbd642d6939866f8f82a068f3ea407cf7d03376e14e3e539a30b3c155dd08e43e7e6d1ac82ca7dd5bf46f2818dde6d43a6f00576e25fd006d042481b57eb569def219d0c55c8f3e0c6ba23a9eaf5ce9a1008b06026e57a8529e56bc536d852739bf23153c5c55535e0cf7b30fcff40818a31b61bee6f7f72f390e9a2eb6a338d9d3305a88925b0766d66a707a533f1a550426a9a749c06322aeca698e861b63b68766279ade8488f8dcf87104426454cde486de2f4c33be4ac9718b41c8593148a2b127527487a919f21625eb37a41440506ae691f2e69281210f74e6d992e9e1f662a60c88b859ddb137767441eeee7245be30ee2bc12e9130b1847790d8e2c0bc7309a3ad02b365638779c283f641fbf23159f92de82b8137ac3103cba61a1841c345b06cd324c38e6b11d55f89c2e072b062adb91db35efe4bf01ee41cac22afbd2ebef77bca91a406ace35c693efc6b08ae7b8315466de6decae705a55b6c3ef03a356e57238d714e3177702cd5fe45ddf61d72229909ea6a363a3355cc10f290ba53f2f7ceb1ac71d0bc5abb0232685ab6c6a688a971be2a2a2e5b41467f8b2ccda1fd1c506dff75e31b73bcfc78997959097a148f826022147797c32db852527fd9106a7dd3a621d5d5cd7686b9d23f01b7dd750e8532ef3f21d4766602c12bd56164460e6405ff426a58e60845a0b9a7e32a53e0bdde67da3723f8804f256494e750e4ac29deb32cb2ead83fe81abff8532ee3f7c3c441aaabc4b325773cb4b4b3b59ecbf76f5f33b5a00a102250bf44cf67c492ddc0f38007183f1282ba1f812669c7481c09fa4ed7fd6444c9c8f21b1d6586b0fa999ed1463f577c2d35585e233451f6e1bc658e7cc15dabd9360a9cba3d641e6ce296a58d70770bc2db17d8d8b1394c28942b3b33c401a9e7502f2a41a24a09313d73bfef46c77e1b7a22199e29e24c41b3e789c4b0ec0cb7567e54376272fb981f4e46b3fc2249333915ded4e612ef4cee60c", "5352ac53", 0, 189443197, "c77c8f76f34597ba7339847c389f79e05c3cb6ca3d96929110a4f29a3cf43783"], - ["1391e5530159535a6a6a032194214f67bb93c70e68aa738276262cc4210556eac0be3d76d50300000003ac63abffffffff023917f9010000000008abab52ab6363ac002bbbd8010000000006ab65ac5100ac00000000020000000000000000947d4c0100000000804f4bec660523f2ee9377d5d3a9227bc5c0c3010f3b311433e1b4258d2ec37176380e6e46a26a5efe8a5dc99f42a8557dfa25903a4ced01cec005d5640758ecf5cef6d0c949177bedd9d5b8e8d52cbe55c2c09cb1d503cb938f150f6523ecb400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eda209429ede09e67f8fab9374d07fa4dde784012e61ad271d841f18e6b310af40e9a44ccbc77ebca3357bfe84fca49a9c8cd060cd8a6c5ce4743ef39859b44dcceae6613de0dbfe6e94c10626ee11345d8ec32858467913836e2b8bb5f43ef0e46e2fc1658c30815f6481ce39d57ddfa620c6b43f820dd011f5e73f6305a7d1022a39e218e417f2783d431011455957f315bfbaab18771d6d0bc7789d42d9bbe8031d84c0d497f647226f0fe6ae4d81348c1f2b2721a59b1a145019e06622a1a4610a1270c4757aff8836eac3836ad2b7e5bbb80ed796aad6cedeb1093bafceacb7810c11ea358f1ae2ae308a3722b46a72b375ebb59946343e8b1fbb6c8d7a6399ac02241e6c1b91ac88d845fb88f0012cf4596ceae0494cd630ad7c3917a777f91eae021b83b43cf79624dda5c6077618673de93d33f713d13c0ac6e7148eae2fdbd9400210f464089032184e74d48271e01ce1d809d8184222965a358ba58dd1871e94a70307ca646f770c5b96388db8196e8d58157a2411757dae462f8d742b4ac39f9ce40323184a26ef63847abad34ee84179561c3e4cee168e14a67e0dfd76c908b801db94f4d79c60119bd475371abaad7ad75fb2d512dcae6be06c71ce6c192bf41f20cd6ca2c4997786600953fff7a7893b60b8266060cded79e4f7b80c5a0f089d95c657738efeb9c4661bb02e0d2b8baf3524247f9cb6ec4e12742ef04d568e9905837e3db3e399b96d5f2c39cf472d01ea6df9aedde45153cdfc5e027e99cf49290a703f88de34a0316b457c21677adfba828a00e55d8f1defbd1fa6e602d2e8b511df7b87b928f49a5bd648559426aa044e8f1264422e5514abbacda808b99b4988b1a634d14daa154f9e99aed27111fe105dc9fbdf44ae99da0767bfd0ada9733e08fab9a64eeb8dd2f5ed8aeed9a93c2fbe3e3b6d330442c776271666154ffc21c57d3aa8af2a826d9ce5b9e7347b78db6081ca6990c6ce4cc877e15ea4f36e7833c4950e374c369e0d72f559b8e40bbb33c48fb7dff6dc71fcaf96f914a7bb90ea06a1440d63516dafebf6e53efdd82cbfcdaf11a4e0f451f5b247e6679ab61a6cb3bd5777c455428c80209db3ba770bace7fb79cb73ef68e30b20b46167be8030ad7120643d71e33b2e2059c1227c9e8518b0dcf1af56494ee2a0d11ccf5bb24ff0c0e7011060a41d10f70c0701896b74a4115e2d44488ec7799ad76be29afef12b27391fb30dfc2355ac3f91a98e093456be3fa9f4e1413b7d50448ba2e0c24471658dd3e6ce53df345ed1ed9c4d851d89faa4ec0693d024db5347bd8beac3a3f7243cad653a962c5b6c9991e0f89c5aecdaa12a045857149e927da259a8eb1949e0ad792b0899c24ef610979c2d9d927928951e3f5a491b2f648e9b49f782a2bed2441b71ea6708bb793edf2364168da195e44a883068738d075345d3d2d2846edafd8c846db226dfeb0febc15e6366c39dc6a438351ce803d12bd4478cc389e57178dac6908704604a5be6a49ef1707f762bf0d1aba2bcbb6110aeb232e2dd972e12eb7ed24d11f4e6198398c979a67208b6cee76ff24c889a7f02fab60f6b27540764adc1410ca088cd63c8483b6353faef4d6b51e37ab0a3821275154bdf2f6aaad5b6afa8d49a07f8e594954fa9f4b86fe783673e1ac03e1439c6d8feb1780b2fb21c9243c4f5130c173c3cb2c179655aabf169d441daf427aa1a191e3c889b8b8b0d470f3003471af6831a2d984ba4167f7df50c4b4c5e90cfd385748ff280a88e4336935b3676dfb38091c17e1a938ffc79bfb1151a443b9828f3050381de0337d995c1562a34168dcdcc61c9a4501c5459de34062812330884f23e1fab6c7bf42b05ac161d0659319e365e9c5363e48deded138ccda8b5c517106b6d60509b25fc17bd28c6f14aa62d52bb57dc0dd894fc92f22e6d3b3c4e047c47978c92220bdde8747473b09f0991d51fdfffb515eb3801239bd7e8b53a8d70c2fb43e1bd3c61f0f25c90304209a7fd070c1530cd7868a1b1542cb2beaeb8b4d5f6812e78a7b1ebea4d3e507d831200b5661ae87e44cda6fdbd0a1deb86abf1f915ad4fd8ed9419e95fa30fef11027205cfe982d9ec4ed8a1579e36527a1c20ae0796c8f3c0e087edfff6bd7b0e4b359bc3c7cb57f2b5b29b060096b49b97209502f7ed85c0c6f29ddfc1744ecc24030d30f959f532be9deba469afcb8278d6d81a541e02b3c3035cbf25b01fcf9100d94df47f31624483622b701bf53781ae92fbf9b70a06cf4e400c7e9ef11cab54c35400000000000000003474fe04000000001e30bb534d5e9b0c12a83ec827062f1da5576d1b62371b69d5090d0ffae837b3679ec4ed63e63013c7ad87e8e21125201df350e9eb9658ee8c94e2531ed06547b4e8222e05fa0bed13a9a151bda4708aadf0110438ccfc60b3fc6404ff26240b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c4c52724aa0baf0821422a0c7a2377dc58995a0b12f4ad6c4636d6cc255a49e971a64fbf52c881afa5e05457adaf498dbf5402181e99344fa303af6f6d75013879fd16e6c566dd5fdc773fd123fb899366341061fd3612c2301dde913495c7546a32858ad8c4e5af95ac5e822fe7b2aaa144e721c11b21b2c1ff27831b90983c0205b0c02744112ceb4fe5a9e70d22c4a96d0bc87a6c2a50ae39d81ed3afcbe4d30220fb807bd29a63fe52eed2e5c11a9c9be9db4f51e4dc966294004d75f59ad2190a165776345ab89b243aab8e89a56d24eb8933869ba1541d5a9cacf425657ec1001e163743a39e727c7d9887900b756a73546e285bbcb69219c22bf2c3cc8127990217d1a876951eb8f1bacdafe74a0e64c8dcb402c9832f20ee7ab7d3b3b59f9192030e1efee087562398f2012a6955d7450372eb06c2e23a64bfdaadcf9f67c5ff560314422e7a90568ced913df2051300d0d917d2bbf6168392b826f3ca68776a294a0320c9cba6af48c1ec86132a7acc3a3bd81f47e936165f240120d19eb40a8618f7032f4cfdf840c641b4f8ba13d20b384678e1107ea369872c5cc61ef5e939cc443f32136927af9597aa14a673d174915ec75e14ae73620432d12d9760af27b9632967213e54e0f4b4a406b18d3fd7fe9335cbaa120355a8e36748fe29d98e56f62bfc0a8757dfd0571cab549f948952481ef287b464ce5dfe02adfa6dabaf322d0e8951b8e877d6a265c6b572c5fc4a732fadf3271b79a04c32bedae84742e373d8d8536c63734d51da2fbdf2624e6ccc425e67d5c903f7df87be204a08610266b07fe27493703fe8abca77df062629a274b89d71a4164cb70abd6ce653002f92ffbefd87ae0be47f45b746408d35c2fa62d3569ece89b487ab99a42b5d0e277844fdd839df0b9f639dffd72dbe9d2cfac156b3d99b58abd70992899d624bc508eaa13f9b42b31ba6667c755e7428f67ab4d2a852174b961881b7adbae47a6cafb5393ba7da9ae8f791187f397dc7cefd1a55e4e6bd9425933a44281f882c23f994a5c494fa97918c8e50030362886e70eae0c032b61ce4f6fb129d67256c34599fbf798d1e87fb68c16b7adbcce095c372ead324af85dd093b183b7d160cd9fe93e5d93fc400a8e6a83630415672f7d82b12a8cc84cbf68f5e05fc0802c7dcd91016541293e20710bbcbf15380e641a2d9940bc7030ec6387b1ddaad64aac3d47bdaaf40bbeb0b74544932b82f01a35d89899dc247dab720b56c486cc036f8c1c67cac65494be3530cd30d2bc490c9b9b016566d7b56044b61caad1e616c86a050fa1c7e4f65bade82973031f4eb08de63209befae479c598d5ec57f542ef84d940a9508035c635a44520fbbd8d4d8264a48a88f9aa6f8a2127cc6dba5c3dac18ee8fa4ef4d2ae4b62f9b6859171412551f81f897a13f23e8791a21ec0437ed53ce8235b3d370704b197f19d6a8175ca2644aa955ce4d017fb54d4514b9ee20a7b6cabfb56c2df939efbdac872369a87e1af472c57aa2524a50196a1cb6561a36c451e37acb0e4fe1796c24f893af350b621170f83ce81fe8f63e25661d57849f0a0655dcc12deaf1a8e9e6e40996b4b4942f8b6d130d1ed0f1e3197f08faf3481aa6979ce839dd7a095f6d31eb7f68ac1ab1d84678952d192cabe5810ecb50133e2a90a631c5e680715c2c360286c4831c236ede55cc8782367feb887426ef0b852ec3d8c2cb3baa86e91bba7d8f58c313a2f40f70beeb4d8d8f464be150b49356508fac176798c3a1470edb9e762c64a92b9dfb151431b18d826c4b63a010b70c3a24dd408443d8116d7fcc8efcae8509c9379ae2f78f507bac507051332240dfcf045dd520fcc79b9af4cb13c86384e31b31282279223306e5c76f68685c2ae723bfbc0b5f9da27ed37d3d88e227da72b673c9f508a3e98dde620498ea540be6ee91a07980f898b5ce2c16d80c6b18187db220f9492f2f62f7541c287e2709951d9209b010ca1f5be87fd737b0254b56d19c218ae35146aee108fee97293a9060159672ee1b95c5f388c5ea09f36015944f62dacfb3bc92665f0712562b2c22549925903300c2f4184e7e8f5f53bf2842526b0212c5f4f9073b529b29e4b67f29c673b508777710af6982f4e35e71486880cde2efcfe41f3d3efeb91938cc92b5c4099e75096b0cb0d666b39f2b1fe6d6e3e05534eeb32138501138d1d172d423ec4e52b7f8a2f104b9270dbddc9c6d41597d57085300b0ea0f3516da7a3026e140f4e6dccae69514149a1861023564289733bc969d56e69718929d5323623c7ae8b97950066630757a320a0a67fd780fa424afc5fa4ad19673ccf8629e0e899fa96f3fae80498e36ddf53bb1e40cbc2d7e740f2ca494d7d41476558492935e9699ca4e0d25f8c29233f5a976eafc09090d", "6553ac63", 0, -1016846359, "cc30e5b1d01b5756264f78559c4252eb9abe2695c663ee02da5928822ecc2a68"], - ["0a33374a01adb1d24f422b83575818ddf34afc5b75b0170cf976f870c4705bc0df2f5812920200000004006a53acffffffff04b82c9003000000000252525eac890100000000001283e200000000000152ad6690030000000007526a65abab516a0000000001a4a8c204000000000000000000000000c35bf3894cdde5b7c1f26ea0cba11bd16dd6b0f1989a847cfc4dd204e2f218fbfcfc49fe5711475e970290c3a7c1fa90d5d26543934fcecbfe98cb033709d072c0b25a2de4d25bc6f8f34c48f87a559aa50eb09159d912503c887c566a846a5c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007ebd2e729056dce034482adaef351f1298e5f2247b5e253ea77946c1e7af678abdc64f9b92a94952355938f17781f964587eed64cd126ef0583d0cee3315e237544ca57677e3acfc1a11621c3f76bb3e61c03ba15c7d0ef3f611e5570b564a37770a6749e290abdd85ad9d0c714165581ac994b5c3af83d9dd253eb5664baed1030c5e4d9aa0f8c8e4e7b48e059b60244a395716e56cba7d2c3adea90ff9ad71fe031d23a7f8e70beb8c85e10aca0da72c696490ef1030e17a640c5e3b8ff53d75ea0a179f6742360163fb63003712001ee27399cb9342547adf78ebfad9a2081cc6592e0ac07f3d7a98e4b69326b00d17dd72bf1e4c5ce2c3ec2ae28601eeb43d3806031372186a4cc1b6b9d3bfaac0b33810e4b1ec1c4f6d0736c5f44c29e9f049685903145de7406f7718b2ab3265d1cd75f244acfc10d9786091136b6882874a8c09ee0214142ae42048d3f0f43df0dc2ea7a1a405066e9b60acc46330608cb4c07cfbcb022e5d237646774f6136bf30266285fcac485c14d7057516aa9e9fe82c2e5f2927021fd61c11ad28fc871a897dcea6cad291a0d8eb33df1b6f3276669ae5edfec80c66c6c97dd13b65dede61b66679f4cd677e824aeefd653832946f26ef2f74b358ed0425ce4030cef39fd9ee800f373b96b96cc8cec57d348c5890a2ab8fcbcfcd7d94315734a0d8ad8ce8a8ca6bda00f53db7751ae575f8f682b42b85bffb02060c4315026327953d6a82edb512b2205e89c3eaa0d641529a7fde6b8d732f2cba2e51c43ee7e417bb1c38fe3badf1889e572cd1620667894ae55c5917c89a23c2280ce8ada3242593961bebc644615def7b0d298a8993c9ddef51217c136c17d6c3b78fccfb373c385db6da2fb6ec58e0f640f0feca578d2ce75c26f6eae8daa5e83b4aa416609cff2d31cb1936dd9f042c7a8ed30fe4e60e575f766025498aff67755b17d56f35fb1cd7454eafa3e91d3f6272f0f85381647c6efd388c320498b48d3bd5959124639104a8d1019475f116080dc68e5c48db463c942ad3400bb95adabca079f77d6596eae2d56d964ced53e68c6b6a31e0ae437032988ba7e1097c5ec05a717eef5a6920fefe5c8138fafe9210e0f58852a66183e40df943a5b420340386e2b1ddef11ec1b3bf9e2a46265ef64afc24d52e25dc269e8d6ca2f00cc250dd7e8f5af488fe17bfb36c6c1ec0ab90147df4fdf700217a87b3bc1557de1e8719fd712693f4a695cda6f71ad2c4927f60f383b45e65456c424db799d96190cf7aa30e1b1d4a138b00fe88ab095099ef8ec7be0127094e404f0d2d135d1d06affa24af415b6711d3fef181877e0f0c0a1812bef9f59ffa460dbea205cedc21e7c859a332cf9f49042c8b428aa02c78e33e3f4387277af6cd19afb21d3154c66c36a564fa22aae27dafaa5c55dfbc989b0652de046cd2705a143e697df4d34a4782547f1364585d11516ba0ed8066b8b09393b5b33ddfda2a607b433bd118dc541c0c5e4460e922a2c34b5f2c1b33899dc59ee5202503d0024f7005b0fcf5c9a16db2ad447ff24297866d309428e991edc6b5fb96b1990c43df35b5b75b1fd85d48dc9be956dfd32b89956b7b2142049d641392298fc24cbcf9db257c48cba13565dc9a454bf00eeea69e47149bcac097a0356dbdf5db097c7ff2d59102dd5759f1ab1306b9b2cfce950d79d7a6f99c318c55010de3df76f90303c9f019c39687b42a85cee05f1c5420e6462d2b42fdc66fe1af968bba686c1aed4ef3da23368c26d2b6a249213737304c094605f344e0708e2f580e4042b33c576eeda0ad5aa53c4a01a77441ac3bda104c18e76166be0d33f7b727578147da07112754257303577d56a2b36e2c1fcde11ed51a35bf97a3c62cde227ac5472b13f4b7c1d88ec8ea35cf46cb415c26ba99c3f34a3ebc8ffbb7caf7fa4c51fd0f600c17021688bb562e01f1b0f7549a9dc423509e08bba239e25b5b72367b50241842f84f6fb28f5a10c2ae0a0d7d8d34c81a6a7dfd1daf7fec578e2e0ffa586904522747e650e61849784af19efd28bbfd6ba35644a02d75b19cd97362b64753361e6f72dcf3f039c2c677e941905a2d8b65e4f04ad40bc65f6fee43339adfb8b208028b5a480399e1286528eddb4b7c5868eecfc7ab2e06fa573035841a888b5d0652865b8c9cd2c199353fe86ca7d2524e97482109928e48ee2409acc0dc06d64a7308f04551ee8301388cc7a84fa63b888086ea6918c4c5c2724108a39a3d585e63430152d90f7c2a8a51479625c40da5997d235335ab5689503ae9569e3a97411bf1998cb6c5d0e08086f5186b1ba59feac3e7df227a449111c14b74e4ec54045b0f687cb2afc5fcb871f162bf6797d78982b2020f9ebd1c527de487e56093dff27422f866854bdc094edc07db75b589536b4530c", "6a00", 0, -358242065, "ce5397e8003ca2d2463ac14fb73bb584209eb9de035bb1624f81d3126a5f7a9d"], - ["b49a962504b3f04efd97e9eeca1ceee42990fb65fd9a5184ec2b1561c90a031300147b08100200000004ac6a6352ffffffff2b57800bd2ac5237522c76aa02f8216df3f6bdd21e35dafb3e3a41467052578800000000016afffffffffefb0c0521ba3b80fe3b894e08ccb32fc559753205b0d6470b66c6493b50c3580100000009ab52536565ac656365a8084cb6f7a2968c04a2fce11e8974916e09cbe1d8945c91a436c412b38e44344dd31f77000000000851abab6aacabac6affffffff02c698f8020000000004656a5365625bd100000000000163f64ea8e300", "63", 2, 1869610916, "e413d82152380a2268a27da125b9e416b55dfae7192f45860b57bf865fcea7be"], - ["7f42472a03c3641435ba5f5f902ea83ede33e571135629fd4bedb189a9fb59671b618e8fa900000000076565ab5265ac6a5c327bfd33d9daa303fbc17aa306ecd9a4b908c39df985f5bc5e3b1aaba68bf42ae277ea00000000085352515152ab52518152305df97bec17bfa2ffa86cbbfddbbf6449b5574f1715e2e559283c3b737cf3130bc00000000004536a63abffffffff03100dff0300000000020065d9d76e010000000002530018c6b3000000000008636a656a6a6a510097dd7a1b00", "65ac0000525252", 2, 2128441131, "dee489bf29b679e1d4c5b8877024ecf1c161a2027801e3f0e5a01603581046ad"], - ["4ccdacb3010c3b427a449fdad295f0a5ad5eec05acc8a555ae94416586c8023440429772530000000009acac5300ac6a65abacc26f1b73040fa22203000000000253ac0b4a670300000000025151e49b78030000000009006551515251516a6a61064c05000000000000000000", "53ac6a63ac", 0, -303626256, "a01255ee198af668547b3f0b23064d70add2685a683bd079824cdc8a6bd8b021"], - ["561a8ef7049ad310c6d702a64bdd612178d4a56b2c4bb958b05380b6aaa7fc941bf19e273c0200000003ac6a52ffffffff05b6c6112511efd1252aa060ca5cf786abff153197a13631340b6983c46e3cb40300000000ffffffffdb77a62027a58a3b1a714318675a41e1fa00637fee754ebfb956cc9069d90ddb000000000700abac510000510a2ba2308adcfbcaf3ad13ac29be03909d11743a1cdc3f77200d21df2266769ff7169368020000000165ffffffff02e3b0fe010000000002ac5177410a05000000000000000000", "00526351536353", 1, -1996700804, "e1a6ba8aa03bec5636455f2d49a78ba6306e4db88c8fe4b01a78b63339574cc0"], - ["c1237cc1042410a5e9f7426a0c5e1377ed9ab75d1d9701723091fea588e3d9f9e22634da6402000000075251526a510000fa51f3ec4ef0aa67fa778c64a8bce0bc507656f9c60f30bc9265828fc015e1630de16a21030000000300ac6af5d8dd23aad3ef7147bfb76b76e94fa74e8b7138e5b9a9954d5a6debe78a10106d008bc6020000000100cd39bb81e49eceb5a08c382847648565df6d8b4d134f5bb1bfeb2e6e09cd5a4d1835dad6020000000753520051636a65ffffffff03595abd000000000008636a51ac51006a65a1b0d40500000000086a51ac525153636ad55c780500000000056363acab5100000000", "63006a", 3, -647895388, "a838d324439cac781cc8ab9036b6f38f488a78bd1b814ed8a6fcb33db7c13f28"], - ["1501236b0198f6fcc414dd4c6ed0efa6c89b0841257bb2f81f96357657999926106a706ebe030000000451536353d4b739bb0249a24104000000000751536a51656aab88ab5901000000000853ac5300000063520000000001000000000000000000fada0500000000663915591a5ff35662e95ed4070177905fb57dbe16f74b6a5cf2d1b337797342f038024d3658c85e545b65db5fc0463be5ecfce3955e55c23063470dd972797437371589ac382aeb6ed940c95518ab3cfe31595ce90f44dbe8709a958f6829990000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066ebff329536b4cd2b7d2207cd3cb512e03f5cb89b25fa0db758c2f6b55b648c50390fe3d1f910ac16abf2d5c41e8cfe104da9f81d2746b3296065a16ff1920c450e36c46f73a49140592fe19f2cbd0e5744b91dfd6d20e25d32809d6888ac713d953933093279e13db19fa1f3cfcce534a8fbbba75b7f010bb1e19ddeb36557030c6464909fa77e1efe40a86fcbe922c021f0e6d708b29ff42709c4dcc2f05c3a022f5c161158393787d7bd0d178eac90bbe02081a8dca6113e94d0d16e632b28ac0b1917416c43fe8ff15d3bd7e09613244e4f502443e4f7fa9e89682b3b4ad45f2224894cd4c37f7ed0fa8036a166cdb30a3477263fe4cee8c3f5138fb06cc127670224d905d614e35cdb383d47a732443b06ab02ec105ffd91bc548e7eb0d55d8acf02138df9981f9ddf1b23cc651fc7ad4d8cee7538b6ba637a5cd1e646ba5038393f022c3f7ee1fd4ab373660940b4bce19b4289b2c646d66441d8fea8bc93a07f855403185aca24299aa363cd9b2d2558adfd633a8008161ccfc525d5403bd08e15200a02093e683f7427ca58897d99741966a4a93ce38a33f892c447170c399c70db6d16443a32d046385c57ba49d9073384016536e60b422e6480cbd4aa8a54d1029a33112f4ac1b1bb4b66b512409ee2ed3009a732224f3510c589df717a117327a88a628ed8879d626db1320c020dc69a6999193e4bdf5993d32c5ab0698f4f63acbdc26a126c9116529ae4247e4d363006a7164f360b6c75dfc120b71163b2566baa4d9daa35074d40929590771cfc95d0a53f7cb6679e73166c8a1721c04375b292f90d7e836a42194eb50dff760263fd32fc3dd91a190abe5c273f9686cd9ee3fafad1d514db5278785ab76239ce60dddd89650ce034c2f6a1aea65e9d6adccacf02f1b3b3cf1cfd1587c670a7e985bf55b553ae1b98114f846e9d48f81dffb3178601476fc7b34e875c629ed6e8126befe3f6196cefc13f41aa5aa4f04fffdb9157e5633d4f056b67222f85ca6afac8a8d6eea448f04655b17847a1ec0eaa23fdd581f2a9780d946105782e7d5b3abea042d3084d3fdd63869c4bdd156d3788e375881d7856108742f7c84c2789df9c102844f49c1c55fb13598b2ba6c887c8cf7fcdcfc2edc2695ba5edf6b3ea9131994b9c191b573fcc6e78eaf9b77459d54e0209ccb29e865277fcf0fba3d0a120e68d6a6ce856c67d3c7ddf6dc0b7a98a72d32af688fc46b3042c17d2b6ae54d70ab20ad3f250d8f95ee0e886fd06c5d0cfa9be5ff2e3b821b4664018806ffe9d392c947ad4dbbd7dfabf90fefc3b6537a82f5e4c9cf15906f5a6bc54c78878ac4039ac087a81a5866fbcea23766a974cdaedf3c70c3cb925dd0ebfddb6cd1515c8927c9e458730c80f1d4d5ef16b1f1f9a86eef8d18bc34e0ec48e524173d80ebe3a50d664d6d8063aa937ab071792cc2c04adab63718dd3d93a09c6568c3f64db6148f618003357b578d3373132203aaae440a4673302229bdf0ea0c5a856f7cb85ad79fbcdb79df06ae81034266cb65c21502724d9961f61f875bdc984e803fa458f501e041da6630880d07fbb380a511cf418e13269346282858bf176041c378252183c910cbaee1cffc2b90c3369e00c2a5ca82417e7bc8a3236bff00c79dcb66f7bd92c486d4cd63823eaeaac8b2bbef8ac5f3e243676d77bcd7665c09ffdd29a1ae529c13aeeca426719b69c4c8ca07a5c5e9ed74005e497317336c31ada464a4f30878036208c32fd85ddc2b19d4ebead5fb049e448dcb4f3b38b367a2b0f06add70c3b0475b3b3575eae4952065f60a841b691fa6b37bb662a6dd0c9fe56fe0b6fc177b26439f9c23bf7f3cd2eb9afa80f5f9ff089132797844d0597f2efe8d316b9a6b1d00fa922b2a199b7f274da9a128c7c06df9ca87acfb28d734d617a5b9fa0437e45e2a11f60b35ef2c143edf43a29a5c27037461b119aaafa72255e242185d0f966bdd7dff3b5898eaea77bd8f69777109ef4f7f2ac7da67d6a9e5af4c477273b73b0306b641368244a4ea8ccec4a7c7d18876e5d2af7ccf7fb7e560cfc9b82f64d8077d79149d016736119b5856daef149f2b78fbd5fac180b45ea25288ba932e1e7c15428b6ac4760f20aa2096ae09ace879729b2a2f02e14644fc09643f72a25a6018fda49ab82a7734c3e2d9ff233ada6648315c0be0a8de1b80eecc7ae9a92467cd19b66c38cda28c658b12c0a972dd4e6dc3835f18df0292031a928909ecde27f55f93f598d20b8ddd9510398c369ffd164da4fe5294e091a8716aa5710ea8394ca1756d4c6771826c8887f3559b25d0932c8cb8561959bbbfdec1b7883bb92465c7d58b083f04ccc66e769369f1b2c57e18e007639e92fd5b1abb4b607b893da82342330781c51977c259438b1c2ad333335b3a1e98ce608", "5153", 0, -653046097, "60266cc5be98b13e101c62b57a3c3b699d48bf2504e84a6e1db3c1356021c5c4"], - ["eda22f6801ea227c8db6a2ffe961e88b638f7738d59af2d3825aafdcc6494f2e4b2fc7942601000000055365656565ffffffff02cca919040000000009ababab52ac6a635165cda025000000000009516a5265ab5152ab5100000000028ff0c005000000000000000000000000c803773d250e8ed48246b3717a46abf0d088ab09407502058eb700ac4706564d39650df70e74937d1eac3a6be89a90914b79bee4933d9d93c1078dc03f0f23a82f0f83e66068fadcb4262ae4e1884e1bed3673e00b2181d9f0b2d8bcb25e17a5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008993ddb139eb08051d0b26f8bc2db7002abcb880595a82081decf6c68921f4bc6a66b9fcf89e190a83c2f13e7e64b43a7ca43f93f21e2f2ab2abfc6b186384f63b2a86ffbc08b07536a947502b05210490cb95ec8ae75333a4d0e468cd05d3424902e8f95271ad359413845e84e064608b13baacd9492e48c1bfc2154c7e5c8c0304e20a365f05d7b3c10417dc5ca17804dc7383b478435e12c40c69e1aeb8345903098f31fe9a933018bfad702131e7ef798396ba1f32a59f4860720ecfd9436e7c0b2c7f63367e1aabaa1130c8c8a2df6323589236c3066d0908cce076aad57ff562285c82ad71d1e1225cabbede8b2b27a3f0c7bc35b7c8aac7109052708d180e0f0217a07deefd2f46dc7c406618ad14e9e038d76f61543aa503ee385352634eb607030413facbe5ef19d1593a40ffcbd78a2f9d39a604ed5b297ead8a43eb93b5f7fe022bc71cda959e2168e93048cb7fe2d0dd03135d0c603cc835a4a8682d8d38a32e021fa63a4b677a866e3729091a4ee2196631006907dd3335c9ef2bc955f4ffba1a030838c1333b3fc8bcf614f8b2038cdb492e41e046cafb18637812d3a32d44d71daf715765047ee6552d0a61217efc1c8b1df5f98aa5073cc7681cceb4571766a84a8e7e14b1ab6026d1de69dcfb6016328ce5e47a355bcfd089151e04c80fab00ba6ef6eb6fc83c9e5aa0941a23085f2be0635495089c8d0a49674b848497b529bba58f8a42a1a107e26872bf2d714ce5e434f8c44b5e4825f57f2b4ff0b2160d873db7c945262da0c1dbbd40a88d525aa12211e26d6df4611a2072794b5ab02969025778dfdbbc777078f225681ccad8d473a2b529bf1b5ca47a1efcdc1376c7d3e07ba522db88d89ad60b477ab4374e4ea238c85df40d16304da6fe5e11d500d75cad1939ad1e6ccfbb15dec3239290ba9f3ba4b9646be33450842b8c5fbb28758f0038133bdbcfcf143c117b4b0fad474b633b497f1be2a1020b2747b3ec87e55911dc4f1b340526b6006eb25509aaf30919c3eaadea6c3b680ce77c31c68085564b047abd546f14a2ffb865eae4c52d6f199f6b08eaeeb0a2cc0308e99e1cbbc506eec3783a030b9dc7324466326e7cb70653e930f4366c5086919ad4207dc2e40f901ab9eb50806d5a5bd0d74348d4cfccdbab19f7e29afd3c73f50c0f3247f9f1a9edcd02aba59cddac1a90d7aa046deeba90c481ea9f4d882b7086af8885bb021bbae1fca89b7eaa0f6dc17517df07d2b58931a72b771ddf783009b803127aabb396311a3d977a9ce118130df77def598805e5680c6e7d3c9258d918cf534d5c6649ccbd309cd41c53ed45549db4c27f3261cc868a4406543ef9720d06818a45f8fde026271f219ecb609c962783ba732908c88db5c307c420646110b374aa3bdf53e556dbe0cc35b5e91845f79628fc756fd6b8de8ecb66883fd8f30af65aff876ebc2385ec81a0c14768ffdc7b4356cabe1723a7b18a6c1cbb4e65c256f9f6de14f2d00d6ae4239827c862d8c49d384332b79d775b602b1ac6b618006535b1ef7ef79b2b24ddd51c40115b6713a602df2d056e44da3cf75425c3e3a400c39f4721ca39fb160167a1b3fccd8413f56e3aaaaaf36b8f49a56e87a6563499109068ca7f4ddd561dcda3a14cdc7a1cd4bdef147a2b0315bb6ece1137762194ab331896e137d6413099ae2e966a9e020c8061e933890d8c48a1ad5a84fad4437c08bf649c39c8ff091b37d8450ac04cb22284732ae83321562cba0b0340f23d15cb835e81f7f13ec22fb7b4a383aa91207f75e5abb80868408e1bb4388d8dcdf7e1e36ed85e9609841f192a7f680d4406b6c3047a01d93f4fde945dcfb16e6610b293baee156730e5d3145b056c47b12ff31d910ee1a3b166943e9d30e989fbd932817183cb0b5d780bc43ea1a2fec8e3772688fe931f58b3ba5d33e8175948e346c09a8afc7a3ebeecf8faf9b4e291f481adbb11dc718b46a5ea734df72ec5dd07a724ea0a6549726900ce2433000bd0478700be499d97d82feedc89ae764afc306b513cdfffa2c481addd02da903cef4783fef35792a4a9cdf2b6c8d51678a0441b729f6d522bc3143f0366d8188f55e67ebc8d90dc7984a6311c4356caeb6f0b2194217385186cd2ab86cde24be4fa116132747365487621287577103a67ab19ab2d515ad7f94aeb5517cdfb5d4d356ec6e3d962d2b545eba6d7ff66a72a760625b1248bf9cf29a71edde3b5e9c5d3edb1d9297931b8477a65e14bb243908c8f8da79d394fb0e45357b4ec41c482aa0000000000000000c2b0f10300000000764000efcecfc93b6761b11012d00a94b69ab083f37ba68674d0f841b484136112fd6d38c642e6beff12d1e158dc94b75d1e4241c43c550a35262598ed38cf65dddddb7f05b7275bd0c0abd71ef705589a06df2c80287b04fbc87446f66223a80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013e825423e226a8a71a9e966ec02abcecb817bd930a4e41a350cce0e7e33da819744cd921121b6a5acade385f594783bc02c9f16c7d99491540634dfdee5ea334f58873b22c14bee48c8f96f3a796cfc31ba3ef1c9f9a7435fa43551aff6c0721969e549f905bf2b0d29f71119054b9bc07a28c406a8515098a2857127bb55ff0220a5c48362fa0fb4efbcad1e4ca896837708612b4714746a13c055dedd3befff02064401b08d2e9b58d4cc3ba00dcbd15eaf604bb81ca3189fb09101159954af5d0a13b86ca8180492673a430b9fc8fe08056620f9e7e38cb8a47251ca0b11722d952ed2eddee2c65cae4d8d63040a312a7b8bff4bc6644c20d5397f2c646988d34c02158890a5bb51e7eefa7aaca88f6496586f6478bba69511c31d135c62e70c4b4f022e6f560dac62859bacd984d935623979417ee879fc0e5ef1601fe097c9c80f89020f9a056b303e9b12af36b8c77301d63e173c60e8e872fed401f40105eba40a9b022a1cf403969a64a0d0006119a262fa52cf33b57ef17e078447f60f1dc7997a23021ec7c10baed9a7b67a49a9d401ef5de3022518dea8cbb91ff8ab7fe3ce70347bce764e0fca2e32977b7a39e3e034de8fbca1d1fd3c81acb8e335cb157fbe0a5531b0c755c79cc2c296208a51f2f8183af6d0c6d6c3eedc95f24a5ccf39cde0234502215ff3b19a47c2a64d479a2e8fe0b60754085671865686dd5bc67807ff850dc5207839438b0aec3f9e82ded4c11f0c37b86b647ee064274b0843e9ab8b2d151aa3fb9bc1b21895cbf2d327d175b9cd4cfb2b8aeb4acc79bc5d4ff7cd91f195f28b0f1bb36ca207e74cfb793218c8601f6878e1bca454cb27393857cd4a5c4fbf53ea09f88e52ba5de9be3c5ca84ddf16a30273fce14492dfc644940ff91ad00f1098750eb5980c6b9c73d5d26ab34393fafd559c600199af8aeb95ab133f290b87597cffb0ae33376eb1df90c6d09b16141b1289f1dfdbbe2d15a427a05cac07f931d98a8b62897a3cafb484d49269c25045951d233f5ebc4a9c439fc69c8535452e1e560fd67dd91ecd3f0431133c7c3d17317022e09935dbe22dd3ec7f5e660e67f886e65a467552d14a74d4aeca16fedebe7f7e88aa51473206979ec341b0ea4c5475ad73f153009ff1299af157c6ecfdcf9e704536c5f1e7b8dc6c9e5c6f6f79f233eece6bb82128e8e45fa7234fe221a5be192be78229569573c4d7b7226291b85f3155ffcbe92a4dc124d7a4f9fa478845bb8c17947fe12e074b284676ecce42d48c110c725cf478813b624fc937add5120e7946341e8eb0d0b895b3628e6566e7e68cc2b0bcd7211531e2535e34ee42bce43d26c7bdbaac960bee1b9f38232c9767c11bb555652295971003acb38eb6a3ff091a681aed7dcabe7cdd2347974f5bce3e9360a8fe35fd5406ea95d9d633c16469b854ea869be69ca23be0fd8846efb43c9204d996df6c3c01b67ca3989d54777a3ef0d73e37fde2f59164cceca596d7d36c1f5b589007bce1e4b23dd2e47b84351d801acf66412b35529c038fe9f35e65a8a4392aaad83dd75f820efdc5ef7bb2aea589920d59dc9a1c259c78de0a371d89f164e16ee33fe9ba303b91693140e3542b402e25b4c8027c98d8aa12e98c01345469234904af040cdc62fbfb9b279e26672bf08f84dae1e96810b52cb3c078e768fae07675d893d3c8eb412bc8c80a0786df3bb312b1c7e7f51443213d18b9c2275a311f1b581cf731498cd53a0d0719c1e43d7f9d52e4edf99c4e3b02ebd035b8ee2315982893cd45c928007ab6b2e2c0ef3625f27bd2448dcf0adf2c6320a5bd64f3aa8d2ea724322d0d75023a6691eb1d159db4494d8bab8a0cfd04aa3359dd128badf582c5b2b5e9aa24fa8aa60ed4ef6911cdbe2cb7e0591745fb92ebd655700a5d4abd06f180a747ec2ddc22b15a797e3a9d2cb796145642018c7ff59cddb6527c123573df0b8861b18da5f019707f7477bef102cca7e88a7d11624f8447050436721f79ca719e1baf2d92dabe751c3d6ba713ff8e3618a887856749ea77094a6f9adcbae82c32b481545b41898c8b838dfbcfcb494cd92616a755440b5e4bbd8cb8fa6741e1d9ac7402e34543c3cdb77ad5db528cb9f8a09bbc479bebda001f2bf7593bf023493a49b6649b00bc72cdf5a7a18ff1893d0c661de51e9b8d4ab59201352264873cb390245e8b5a1971ec664e5d06f49c6805bcb5b0c7c113b25e2fb506ed77772b7609d493e44f1363da4e851aa26814529117988aa1c682e0a6785f809bc22587c1fe508c24aa410c828fd90bceb94c14eb8e5f33d8eb354a0aa2df02f9d58969830ce12ca3ce81ceefff95cbab99a1b72d33d13de2f950577b28a20ddffbb0032e17d1dfc1041f08fa0134837ea77ea742545817b7503e41f642b721453d0a", "65526a63635365", 0, 591722917, "566fd1cf0adf04283d4a9d27bc0de05ff76ae71baf898672b6df219a85d9d74b"], - ["671d6330039b82198f810564b5fffe0714f34fc1a31cb6a24d7d8e487b5cbaa37208c1c2ec03000000006cb8565067e11731e52cf55cd7fdae0125dd70a41c685e229fade51b6a7e1b5b3461a69a03000000090000536a6565655151ffffffff5b99517bc4811347b1e6651bd4937531083f3a738a59921003f4b1b74a4292530100000006650063000052ffffffff03e1d0c104000000000600ab6a5200acda6e49000000000008ac6a53ac535352653736090300000000006bccbb6601b399c40400000000000000000000000035dfa222a5ab2b88d72a04335a07163ee95e72bcd71826fb0d2b20964c365b1f25dc616468d635aa21b0eecaf892360fdbe3237d9646d7c2798d4fff1100e6ebe4180ddb2dec97cfb9568a1bfc1040f1da10c89c004a4d57dea18bccfcbf1a17000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009e6173b825b40cfa28d6fc2ee684d2f66df9dd631adac4f6e1bf849d449e24a10076fb8a77540ba6f7f2e5f2331011cd75c51aad3617547c3745be61ea3a8642c0bbc1d04e58f4e2d1e422a8ec3565d100672c4357e80eda8f66d150cc4023de07dc4c0796672ea1a9870904c10a949a11bb3a416f757755546c15071f8b40c403262d7e3f778a1450db53a33bc1c77042eb62afc5d7e22358bf2a66f11114fb720305f501bc4d1ec3aa71af4b41b55d0fdfc7b45169c8c300fff9799fe6803503ef0b23a0a2c4dd1414e52dc3e3d284faaab796c0da38a37f9b51d37e49cc7001e1f806dbc88dbb067c22b64ea314b8d999f5bd503c62a709172bd3079130804da666032defb4f74c6a13e651193ea1e1f08f8afe40076e3663a3dd273571675e0ebdaf021f8b5723739da8042da59db3a23ec03a5131fe8d545344b4ea397393bcdc1654020c8eb7ab04c1d3bbb41f4b8f4a41cfdede126e4ba48c0d666559d8dd24fdd12803152a3a800bf6ef8614def489c6131ce52e00e60cad9f552889a5a2f0e276b5890215d64e4d8f5c965f1e1effe446c53a6665b1ce5242167f6ea60ec3dc8d8aedeed988352a3f0572ab97ad72d9c912e1d9293634eddfb52906850e6e3f833f4dc334d7f1e49cc38dafb7f563e513c377ff1c5d94207de23a64a737ccd6372de38ab2376e823ffc5b6fc3c3fe70ad82c9ca85b1ace045c9fb88c98583fa361aad1ecd61792b9c1c0c64753fb1961dadbc4ebde2287bb81da0a50c56785430222f6a9af73ee03fd27becf0d5710d07b1e77298bf6b2a81f42f0d537f019aa50b400b5b66dd8f1e6836ef43f3d8adf6efd8ce145d39dc6269b6224217e1ab80f81d498b8aa830bc140bb29bfe8897337d31ac712824d7cf29f8fe0c85f664e6115f92aab0bd865bc49ccb10372fda90c3f72606c2d1597cafe8a6034edbcf245aae94eadb218279930b830b7dafec78991f537c3aac7639d97480e352483be9a2f277c11720a355893df4cddb5932f7fcedd113c510947aa57b8f8a32ce85c62f17bbdba1b6328971b8e556031dd9140db72b263f542aad4cd9227b842722d15982fada8f20ecb81b0c92be1578c7137c95a0212f912e3ed255216d73e8fc49cb1d8fd36f681f6880cce066b9a2de48a4782d30b7e10b15f6019337d8a263811c94d297dd2bffd1705b001eebcc548c5255b79f35662a961a1efde7b125346fc72e4fe73897b2c3fd2ab6842106b62a7c1be585bed00ab691a3f452ade5827f646977134da370e49ce2540d2d03f49f3f33d4368ae391005587360a3c30c2b3fa1520c020152a42824b9191bf665a960c0fc78dca99e1642e019572495f8658c7732d254358bdf0e270acef11d2f03ddaae0c1ba7766a08ee11188951116d0f3524562761ae7acc5fb696ef9b4f052bfe19b6ff7505b49f414c5fc563af99884810834e54df964a3b74f04eecd330019c55d35dded2fccbed7951baea25fdf9cc95ea9295ca603e6878a4a3d44bf056e3ded4ec3fc20735bb5b1011aaa7e7de5c4ec37d7d5f9f18a32b9669aa85e3bf44cdd356ec14ee883ada6100a800ecd82d7404e79d060b138213b78e3384e18c3b655607ccbc965b51d0cdf38465c1193f69ccfe2e2edba1d4225f30cbe5fb1ccd0496c64426a1a14863069f9da5cd57ab28d48a2ecad1195dd4b511a4b8cee54dfc5a05d0d615ff7f783a06d56e9de7696b473756838cd630d3df7ea2b2de2f727aec6e1824fc45f634062180d69cc7c9d23ecefb1925b7cb4a92da35bed64b06675973100101cad20517c9f314527042c57020ccf0a60403d5d707b64bbca0b3abb3bd9b7e62d9ab12a1933fe8f05abddb9e58a1be6982493ca32cd1513d1d78b66f4079ea5c2c30c071d47285c7617265c3dde4b27bbdf050fe948f1bba3f4d243b4c5834aab2466b07abb28821159eb3ae0b28d6e53aec5847811184bc4de55b7580584b2aefeac0ffb3d375994419a0889950fb8e322e057d585e045df194030c35305a1d8bbcaf82a0d1db0c1578fd2a7a0fcc96e0e33d085932e4f200044841aa2cc6c6ad6e6f1132940404ded6734f2aed1c9cc16143f98b639ab15a73b3281141f22a19b3594c0f745df4aade6af014a26b83135350ae0ab8cdbcdf28806064f870cf7eb2b4a7397e02c8f165db2adfcc802c0f4bd42a84f000e295dd0e5e800fb36047c774c52e556e34fc867e4b8594c0f3b6997f76cb6a5983769f1e2790322ff8034ddc5d7d8a42124b943989d1abb3ef6f8332345605a912a71f4c144de616c1ca2d2830865eff881f4b22e07007da42450e5b87263de61f085b80aec68ad17908ffd20c054b812ebc788780d78609f456599ac1551cd40b1ec6c6226adfe9a929a671e968817a25197d8d4083be229ad19bc7e4527fd62e36ef1895f406", "63", 1, 224243722, "a11612b83405de660c692a3f620464ab55b171d03387234a298b3da872cecb55"], - ["128b99fd04f933ce98b5068548c64e6eb072df506c4c181bdeda74018fb07a2198416c4bed01000000003296409e7e63687e697ac11dd485bfdfa1d4bd2426636eff05bb0eb6ed49e3064c254bb90200000005ac63ab5365ffffffffe575f688fc85b5366eb6127f1196f91fbbd41783e5059f0d72882766f605db8b0000000000ffffffff21f6f83c639ec664761acc28c7cfe87a54a5069cd6d177952ed89492b2cbcdfc0200000007515353ac5352abffffffff02e837f80200000000095251ac5100acab635390c439030000000000708512aa", "65", 1, -951791733, "e9c15dc1952625e3dd73368df47625ddfa37c5979e1b29f8f06f74b50ad514eb"], - ["dd227cb004a68ef5b081345deff7d4aff4a2ebd82642f0de68becca9bd65a008a605aef92603000000006eaab95477b5234246274586ce9cfad13b2bf13e6f366dd64f12ce57f0013110374ee21601000000007144b8c771c49660f3714e49deb88071f600e5d46ad26cede55362584b2d816aa05d60a5020000000100ffffffff7a3946743d146890609ab5540b32727861f2e9f2d8ca4a28de239b009192986c030000000400ab00523ae8626a046cb52d0400000000085300abab53abac518642dd03000000000151f44e8d020000000002ac534ce0cc040000000000bf9d1e3e", "636552", 1, -614446, "4517dd7c13047de918e173ea6d382fa9d035bdbc2a4883154379657ee2555d09"], - ["2731335f03d813cba1fb1a109072e2f21cbd55b990579aec4653bbbf9ecfe24003a7bb6094010000000265abffffffff553b90af86abc340722e37e96c0de1be80bfbc8ec18933864d26dcf66c115d1e0000000005656a6a63ab2d7c463ee99ea2d5ae75c5c559be03aaed37a060fd171e6223bb721d2d22c57f32a8ba780200000002636573b111b1010963a00500000000046a65ab6300000000030000000000000000c87c020100000000f7be13515a34f64ff9a9f77e85f64f217e904f85c2769ebae9be7fa95dc9342bc61172430ed374afe3e605631f985dca23e6b1812313592b1844729cc2e505e7e2963282776cd5766bf2eaf17471f8b220bb4b62ef1f9cd3f9219d2b252f5d8b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d165a604824826ccea94691653c7faae4e40b3c2c15a8e4ae52f77191ff906e08da7be865964b6531ccaa5c4baf7df76701c23454de3011780c7645f982a5b56fab6beb1d06edec37771e7ff034f644d37302c2539bc90dddf4381349783f25543fbf7d552f7c21ad7382952df8cd05c0f9768e848f57dd7a2ccd041adf30e90030e4653f7daa2ff160b5b70223aa2d5dbfe441b392f714a4ed66467758343e690031956ecb5b318eddfab3a75c84f4a1a8fa90faeb19f35fcddd74ed6b6492d95520b256726b2dde131a7941d1481cf319b612a72aeea92afc0dd703e40116f12873906f2bf194219caf689c708a78bdc2d51a2709c53414c94397c36a1435b8231b8020126af80bb7d4c5f3649a60f12870b9229dded123102b0ed53fbf23c9455170503057cc05d0b8f124d805faec466247e5702113614d5152e0b8d6a1dee6a5b1a00030fecd96163438d269f63197313603b529895723f4cc362dfccf4541542d670100305c7d9a6e6229055452d01617a4f6e87b9472e77316febad25f9735be09fba80030e0b3806aff9302be0a549c690e5ea318fa27ba067602fd3ad38f2db77a430154982a8ebb6ce598ead66654b1f529dfa95847f60a808aae94d9fcc36d189d4552cba69ca2b0e418706d511450d6bd1d54ebb447cf90f424a5ef197cd7aba9907330b7e79eae2936cac8a780358252dbec3bb18356c477b23a2f2183ddb53c90bea781ba9186e8347403f0e12ba210b4eb7fecf91e265c6bc9135d0b7b3f06d714950b588aa8a2affb0966b3f5d16b37f5e6b5612c4fc46d6b875974b47d185f76219ad9d4d7b1bf8db3adad49002422f4c4d745d2086af52572f6edeace8067c58915309e0f11b4870d324298d079dc81690c732212231bdc3a8d33e41240973b3697e4f4f163523ee20875ba9ae2962aa636f64e5d65f9288840e199e290260044d93d12fda28c7939625bff3c34ee4cb5aaf5ab2d6b8479dbaf52f4f83dbe1a884abfbebdedcfdc42b23b397a05c3d61ea087100eec9c8e3257c3ab6e309680a78070263c34c7d4f3c5fa7da540ba178b5c0e6077ecc7eec1106f9c9f525fda3e89b98b5da9e8483c2c3cdff3fb2e32a8d9146d6942ab00dbe08adc2cc7241def9f7fb9d62eb0a09acf0956f63ccc955a5792732cdb2e84e75a0b0b4a69b7bec919c93777ecfcbfade1c0632f79745c4c666d08b0f8819b6ddc196b6c59747cb110c4fc07066d2530b90c8f49417309145f4f96abe7125bf8e448f49b45125b8634a0aac0bf2a7ab650227088c69ce46cb77157370229b539cfd430e70b35ca8d8ed1019a787c980025bf2cbda824113f38468d8f3a31c7d4bb0d7d735067659ab84e17c635ee019d36a758a3fb4dadff801a2a3af26955e568f37562ccbf011d7d8fdbae0b9bee6c5d034edbaa171d0fab9182895ed092c91b8c9dd92af24a522d42f1ceee346d070cada76c3bbaacec99b91f63a781aac2d0afeb82910c6d1b1ddb7cabb24cb70e5a74bf56b521b1bf16b15ee25c8f61397ac8bdd377a81f90aa2f3f6e31decd813ee3c761841668ebe844b7d59ca06e14c5b7ec0a8c4008679b86345078e0e01d23e022ee3c4f90145ddeebb2b693530e42ea6a48fea82873ebdf02d430a3af8be6eb1cdbb3be94f6c6a6bead8fb05c4a85d6d6fde8b15bde55394bf10bb891603c63bd3d8c1f660f3b601292adb9b81feac56f7cfff61c8c71a65731cc21e071e1dd906a1191f7ce52c07713c3591d1cc8267793e7e9b30fed3c1fdeba71077f0ad160ce6b1d666af13317c338bc1a84d9ab27e2c60a8ac628d5f678ca0608ffb90d66cb2034aaed347c24ca7dd11696b9ad14101f1e0a75facbf30115a851a40f6588ab1a3605be32923e0ce81cb78d2711722ae45be6ef04c0b7ca0263d1b8cf8ac9c31b8525fd00316d3a8e7dc554f42b40ea6d2d951f9427f54984c3a78293d4a01adda001fc1eaa26cf17c6b88bb63b11abb5a868b17881b8067b941ed787ffd6c0913b2d1056c3a5142148666df69512508636ea2f95accff258a766f1f6a17311d601e801345fa02f9766d58bd34a5c679eb781e51021d83fb0ca440a9fb62adeea83d2910b0f38db270cdbaea4021d6748fe445beb49bdebe58edba98219bf0a893ce8fdad1a41036d6411a2745d9984e41768d50f3a1e2f6b4497708ab524eed01dc8119928b202d3160c9b0c6e7ebc51bc03efd2404b64e304c81f6175f38a066c64d225606f27ab71b4ecbc4e9bc8e8bb551568d74f8ed54a4545b16c06e50c4ff582df7ff3f020000000000000000000000002fd30532b72fc65f1dac7c0e7784ee9c15600eedf45bb2f8772fb1c0f55919fbd049ff9e80194043857e0ba55af496654ea722a0c5118af2e2699b1d217497055b644a3750556d8c79b2e8c7f36e7d6924d0ff68357c5c3452e55db62e718f7200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fc71b7f3f33a1187b5523c5b0314f250c872948669204c7512797005aabd8d709594b1eb2c16de4aedbe06aca05f7728797acfdda8246c99754b5979c3b09032c862fefb4d4f477fa7b99fdcf6330f03b0ef69da2c5dcfb7b751f2f2f0bd0ff43d0d29fcc09489291740eeb13eadc5d37df6e0136099f998a286ee3a4d4218e703288f453bd23d9d9c31c2bd6b504943ff1e6d8e2b231e164dff2b2fc3149725cb0320f650fff360a8b67a7c387198ef0cad06a71329a79c4d39f7cdd0e2fb6f54180a2af45f27b6b2123f307362deeca7c3c6132d639ee0ad387848f1345b8d37b61913b43216355c782630178191e2dadd2b96eb9d94391a3274b410d8fc6af80da9021077f012ad1558e8af87338dd459cb3175585f9840c27dee8c4d6c70e6bfa7f30214810a2f7ae852c7dc296ad9cc47460769b74ed99e7514b2f8a39a73f8cc437b032120cac40dd7a49e41e67f159cfb9f3c3c85057dcb89cdb98f3d9a6fa3320314030be989e4b71b105291f3ca1b170a3437ca5ed4f81cee6ff127296baeecfcaa0f0327be33b43987da66187523c9dc3b7681e41a4cc0763c311e8a0a6ea84ef1d72069f0a1c0d0b8d876da5d44dc49673b64c3b522243a4d03db0dc8739dbafef1c6d7448b80f160337ebca7addd3af17ef4666a981daec563278c559062b15e179ccae6dd7ad2e8591b7deffc08e968a58989515410ca64b1121b617d5ba95663739c4042813a61d1837c0ae28f570bc1f3251c52ac9c8ec5b168ac1f4b721b7bda2b2144fbf92c3ae33e06aeee1982348c471671191efaec6e6f3d46f84d5d4a87bd422455ea1294cc031a76c86dd9c1e73999388d2703f074c8a1ce93b76554cef4d0e6ceb802b0d0e7b5f223b813aa5de5c3ca8e63d7835d51eb92fe2c79a816fa16bc5a4f6704dd1cac0b874d005f4151dcd99af83ac6896e585ad18fd4263bfc6f3294731627b99bee7a327f1416a78a8c44bd83f9f92c75de37e44910d8aa7572f588f1917d626c3105544a35fd2f2cea686aca25dc88351111acb67a0a2a2c1e3606c987e0e279c436aacf9b71b2c22c16e0861a83c10984ec6773536b74e44823f3b2774fef4f546d1d7bff318f9c5d05485b930d35c90c1b4819db06a88c9256a8a1f9cb5187acd0467dbd0f1c77e2ade02f7842ab1c2d867451255cda298b4c7cbd5431222269e3ce0892ac68b6cee01306d8eb51165066343354aabae3879a315336969badf60c3e0b71299c6f0ba5185aaf40cf5d845e82b4953e81c32068841ae30cdc0b732a0a1b39f3e370b795ed4ff3d392394fde9e49eb33660451f40dfde4a3d3ce52e691d2638bdc5326196eb072070dc8ba4179a72223cf9b0553d87d64a2217798c58725f5c29c555ff7233351c7384b0486940ad8b1f6caa5d572be5a8364d6d8c8239b2b996dde645cdc0c12270246f82ffd7702bf40375a2aedd237ae3bb665cf50915a47524e655febd7017802df4cb3d7736d23196f97694e016b06c7f81dc4a630a6f298a8548fe069bceccb316d42fb4cf291443799b42f136f610ef0f19a7d60227592ebdee6d96d761369d4a166cadbeaaf718e0171be8659230ea0048e8921f73757c277d5d79c13d55a424e772ddc6ae9943004ab2dd431f0869f6b73d52e890c744edc7e2296cb2ee2518f8ebe212688fb7c855944c876c7eb81b00beb59c6573d0b8cb524b5bac198f0eba4a6fd43613f8053f85d7e8a1c445e348967fc6baba5bdbafbf7b52b02ce7f2f7dc4dee6272430716d2441f1810879d229cf6652209d771446c405576708b72ef469dc7e3b5877c4e05cbd5f3037188d5b5a2c1e872fde5f3da876f17ace6fa3884fb371de2ab25ab18ebd7846189b00353e31c193a8b04c9cf4f949e7b6b91a13727f011d016464a8419484a1c55dadbb0300402dd0e595475ac3b931c078e4879bd6ce7a24bbaa1fed8c473b36c8d421b371a35cf0fa02cb491c75cc29aab44b444fdceb354169ea0c099fc09e3dc6392361f97b42fca2bf07724de164adc8d2cf832bf354d12322a020328a895893776905f9e63d6265a386bc65bf61757c38e995c1bac156eee2734845441ce062b4e2afaea3da85fe5bd95b66d99ecfbfdcb257b34bee147e471bd1084b979c56412f848a356e362afd99c068ee431580d19ac7ec84bfd43567411143e12f49a036486d6464da8009fe6d40963c5546dbd5cc342a691429f5f57a173b89998c193b22bdfd4ee50363907492f6bfbac7bb66a0bea010e2c12c8ff51f650d028151404f88ad27062a46294bb101000000000000000000000000f2b1cd346fa6e13c7576fd4021811e69c13efd7c4162b41b5f24c57697cb59f6ba8654222edcb5af8918c0288f61bcf0975734449a814f8d91d64b85f8a65a4276049c8220a5033ed2d85565a1e674db2e1ee9885a30d0fbfe3f574f3aeb44c100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ce050dcdbb071cc0fc3a09b21e8143a0cfad526056ce03d4d1d170f336c470c69b2a15a8bc9f183e2dd9f93c6fd2bda9a9b539c0b31e80605f1b3a9dc2d9c80e6c0726fb8923989cd386e0af6b7997fa847a4ad53d0edae6d02b936085aa6e12aa123e7888cc75ea1d8541285b3821e4a2ac1d8f56b2be74e08928e465e0ee3a0318c4d76d106209e7c29af3f5822c3765d97bdf39f5451954d6f355c00cbd1e270308ee39265bfc05ffea6bda1ce13f5348c131234c6bfe018e7733f74406e7d1450a125d684b1d32906835bcc08150684c60b951d1257942513324756fb76981814108f8817b80c6a820ac93e296367797dac5ac2a465fd6ec685461546b29fb269402250a78eda38b2a3da1eb1c7b97e23b3f45feb3dc721a654899f44493d62708250226b88a93d063edd15b4dbee6e0749692ddc7640bde9662d812aa7bbef52eeb440223b037273f0f3395e6ef935c91503ace8efcdd733dcbfd4f0353c5ebd8db7ec1032a01199edf7ec2ef55bcb938ccdeb5e32db40fbfc85f5f55cdcfb9834003900f032dc8df7c91416624ba191b8493e5ee8f360064fb25becd1eb8a9626d2a42b9b6bb2bd00bf581cc3ccb70ce9af62252a59f1a6c1f8b45869eeb2b98bd10face6e918a10818285ed1eecddfe78721a91e36dad19f3c2a667649ebbfc819f4684a8f31b42bad43a8846167735efe2f3fe168b17ff27e793b27d362eabf42a389f913088be7f9bb07b017ab67afc6482636cc6f5728acced37414df410924e38fa64bff0ddedf183db4c32ea57061a1b310a8124042fe6fab8c2bccc520c9c3a59d6d92ac58a4e53062c912638fef707d1e6b28e6b5ec277794b9a375aede8841bd05acdea522731c18d379466ea40ac8ddb722021c3a9a25a962ad555d4cd6b8c1947aac0b2c1115988c8646aa647c27ed66ade29861bb75f95033554be6414e8e4baf523e41ef2774808f0f6e851e7ff9c59ab7fbc0c103653eb0ba5b07469c787e1761c61dcc973478e9d332ef506ab8dd0ba638c01d44026411502790f6a44b6f2a9d3627760853462aa8650b194da7c335bac7c82c8419d75c957102b0f618a184653dfe1847702383929941dfabc2bf23e8c4ca58ad644382edd3338e7f35187d3e8191acd8d316859c7b7fb3bd95bc9e324cdd45ae0f602d8a6fb01639c17945722777dd5fe6fbdf51684c32fe3f91e9d064fb953170431c3151618d92c1ee88e316a21799d95950bb55157c6ed62bd3458897843512cf692e72d891f85ae73a672e1fa00937d2e37c6b6c344019aee3c4a0ecd276bec1123f43e02056c7b7ee180e9e061ed9b09457f40cafdc5e6f66a4d4ae57d4b810edbbd0588cc3e4c1a60dce455cf08fbee5ab523726be1b376a947ac9c43731409544074e54681de7620866b652edf0d66741eb0a855cee07baed52b974ce687c20e8c84b8faecdac79fd133865dc1e275f97a9b06fae17073c3ff59a5f4b50a5a1e8b1770bd9b0a29aa3a8b452fc25f4687a853291d852537f1e57ab64d386122368986f575462e9580cb929d99a893e53b543d1e7af9e01c694bf48e274d71270744444720a3b37012ecd2b988c8a2e49a1d8a3bdfa52019752aa2a46f9b91924e4fd8a817501ddb04cbddd7de25c55d88c6a053656273f0909b6a2cee36da7db93cd44fb4dee2a3071c3b635a6deb357fe17b77a6d67388190333362327ae0ea588834d57d3ec49d23e41db4602d9750dab0dcd6d3d94a42309bf3e05ad0e9a968162bb9154e990081d9530a36e8fdba6310d12e12a36d3445d9476dbb6280e4b82ca9751686981e1bd83386589db846060be88ad122bbcf3c02018fcc275298ca4088b8813f12d5a02de13d3401d85dc228fb34586ba440c4ebdf69a69f6684a16c2a66a8ca4eb822f0236caa3c64a48c375c21b0675d52c6a2f149f3e590356d26ab5a4768a51e4b35f3a91a54859ceb3dedeadefa219a30520b444635aeac6b5e2c3c56b95dc7fde327b77be41934ad149448da202224e7d095a94f10b5d11cad05e78f887b11c3f1cbaedd8fae46383584052ad8eb176d638615ee8f82157488f8a608f7cb21ebf5ff47846c87478e9b5cf1144a110a784f3d0932f920a5c1a345ef4f3845714ebdca5f2b5178a76e2dab47f16245ac6aea7e22eb81cfb47d4af334e771eb1ef4a2f39d01dff646a47f6172d03db54dac2bcb28246f73f6622d843f83ddbd6525880c97a546ca12791efb386aff201cdd14311bb42b2c71333427f160dd5bad9367526765c1e2ecdbbe82f40c4eeae999bb8e604f5846ba78e9701bbee2ad771ca13dc695399c0e64a73552dbbd833ee3a3bfba3f50a62f3b6103410eacc7fa636ecb361d7a77b67be2957d2139c0ca21ee4a65bd2356239d2b82caf72081382d1398970f75c8a6e3381e6de51411503", "536a00ac6565ac0051", 0, -720254056, "3757ef29cef296203f9a815b1cd8a7a23081465d2a7444dda1e95c1b8b79cd6b"], - ["4bb49efe036700b1a8841c20d2b8aab40acb662396092800e7a3f9bce23e2779b34920ee8d010000000152ffffffffdafb01f60b3106f56009298fcc1bc9837e44855c635f51a49516529faef7fdeb01000000086552abab6aac6a5199c9385f1e528118fe6b3a00b3f4517f68cd80a52a0f53397a9c7096f1de4c0b1deeec10030000000153ffffffff022bcce201000000000763ab536365ab635092a102000000000000000000", "52", 0, 659936838, "974a66ca5e3db186eaac4c80c5ab4c908147c9cad232cf1f5b97964291f44914"], - ["87a557ec018cc7b4bea3ee1c9bdaf2df147045d4fb60fd6541e2e74542cccd99abd68b33800100000003515200ffffffff04a6c9a005000000000263ac22d86f03000000000652ac6500ab00f9b95104000000000463525353da9d730200000000016a00000000", "526a", 0, -1721199352, "566888685633185eea0daf69a229287a46fb3706cc1d734ff027ff66b3f9a81a"], - ["4e7ecee203ac7138c2def93c0dcb33e6d92d723e030dca45d6ceeddca26c58171212420d73020000000600530063516a1a12487f731af39e060f363423c5d58d880e6915c82884182807fd11db3c41375f1816de000000000651ac5200ac65ffffffff0c292af49fcd1153894a5a5d7775b640806a1817118c07cf33ce558c33f0069401000000056a63005353ffffffff039460ab05000000000465ab63657106e301000000000563515352519c80c0040000000007acac6a6365ab0000000000", "5100006a6a63", 2, -961177221, "da044153645ffeefe053ce9b3e30fbbfd5c9bd1ff0e7d56436971b90c86ac33d"], - ["5cb4ced703eeacc541c7261dd573a333791de19c1e3b5d15e5e2f1f6766d147a95a610efa80000000000ffffffff42d364e05a41dcf1926520d60a8be4771326b81fc774a9bb51b149c1a6d9979a030000000852ac65ac52ac6352ffffffffa132bcea653564e0b317ab62459bd659a882d35fb1389974b3c894f59cba2c86000000000453515300f06b3cfb0146443505000000000363acab00000000", "00006365526a51", 1, -1941457561, "a09555de29f4ab0935a3e7b3a2716687a8e33fba574ff9b5abfce6b1d3068e3c"], - ["81f028d30232190c88962f99f29e5fc2e604dfb6a4b066d5ac526657805f4ac69fc22b8f52010000000152ffffffff68cf908441f72ee5ee413b591b30516555d0762c157c7ab06fe4ed916bc4ccc10200000005635263006357b1804b034c4456030000000006ab6a53ac526af11231050000000009515100656565656a53c89c1103000000000000000000", "ac636a6a52", 1, -728757991, "d82329442a821e0d79d5f1293e3cad34e94c8a6290e8994f4fa9b0818428e1a6"], - ["83b65b6902dff28a51a2351428ef2a8a8b61cb5a0a0f5de131d26bab4388d617881af8b2c8030000000863ac6551ab516500ffffffff32c074e7e67b60f3f44ee848a4f244209c7a6f0b7963eebb01b3c4823459c9ac00000000056a526351ab7160aa1e032a51a6040000000005635252ac636c15a8030000000003abab522d448b0000000000016ad5d8312601c12ac804000000000000000000000000465a7128be77190af81b465eeafa6edcc4a210116428b4bd12d8fdb8078001233c4dcb156a8fb8313c6f979a7efb2691300195d55b023420a69a2ac93f17db954630f509eccaff4c857b3366850fe305306dc5e0f75c27e0f059478dc9d50924000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd7788844f1c440b86cfd5b26261b18faba87e5d238f13d8dd77ae4302e99781a2fe8e5fbfeb5060c725e9f584e374359f4d07a03e50c58fc11fe06d96b9232e195255680d3384194cb1f9705c78e7a10b0cd05f32a25ead51e676d97c0509a349386ca06e6fdceb42b96e8e127ae043799603777e9dd73c8959dfe3cc5d7ee031a981cc889e31538814b9412e6d060ebde1f16694e81788c1b65713af9d4664802022ff243839553641be55d45929600a7bebf2eec640724d8771895c16f4e157c0b01a1f468f42a8ad2f16826cad1bea07b6ffaf39bdb8430cc8c7cab8b36b5501d1eef7a9d13a72637c74eb5683a7e76728bb53ea32c36c96ab9c9e7c71b12d73f02281a10a795b25a65bf9c569038e017af20cd5aecb7669ea1f9f01f7b56ced574030891d11f4c11e9e4d9dda7dad37874e53c1d6bdefc64295df0202d51e30919be032c22cd7bac85f9ea5c0c96062fb2147418291a25c2a83b0858704970e602e4a00211e03703bf0a02fa0cfbd8fa7cb52923bd41e706c41b3ab0946b8bfd7c875b46032541ebae7e1ed369f5cbef41d35611b1927817d0825662abce75425ef4916a3e9e0f99f3f9c66f792d20cb9e30de2694d75180dc41bab5ba4b27492ac191f8927fc15fcf2b6ca64a7a8aab8b5f3faf1e3a62618a3f1c8209cd540ed8a7aa56cc2e1f33de92bb81976ef3d3e7d86eb7a8b6a712727fb579a1b86f8c1742deb11d9ab99e5b2f31792c9620f0707d493f662fc635f8d7a266983336c0d078f97f4632aed3b738a4b6bd3e0149bef27227ac324477c65a07e68c139597a290e92fad1e4b82855e0bb48ade19cb0e080c49c97045757f5019ab867b26d576657ab952c92e5b128b967fbc673234bf857354ac639ba137108b2b516fb0684315d28b6c7ac3c5375950491527130e9de53b6390eafb8df9b14184b05265043a0ac3d4e7febf8b6440da7d2cfb8f7f23afb4b4c61caaeb90e03f5dc756879b7612562559a4891f3d29986d84e1a280b029aee71d059bbd68986177435b86706303d2c7182b271ce23a5a07213b9c213bc8ffcd412cbbd73b6922254d0105fab83a7d8452d20b2c99fb46272f49b8e7de18fa02f7041e338b5ca5ce905d549bcb169f404d49a4d780c338eb73bab77b1f0189893b6352e148bd98ecfd97a6f0b44eaa78b1e70aa572700d79cc7ae902d8bfc202be28f2bc78e114969f73246b0174e663e4ef220af1ec3e479048dac934105069a75afa13ec5b4ab2df9cf1049f9ebe524ce4e50b8899a9aeae4ba76693e726c44c6f10db52371ffce4017885fbc317889c1fcf08402aaa651ce143639e9201567fd43ae5c5478c2259ef75612062a57dffe96a8b2f16ecac4ef5a5c731f5ccdeb42efd3d43ba46ef0a47050a8586ea366ec8fddc3cb624ceec3f67250c2c966ccf8697b5ce17f824d91d7052cabeaa7befc111facf3d6c610db5cec03878700d924a58fbbea8825f0158643e36277a5e6682f2f1d395cbf3fd43bb6440c50ae76ccfc64b9f40f5fe53afb97f4565529920b1ce535ca392a18ac6b1793cc6a493758548e86228c7e042b4db59e01c776d580736870ea5705877ac15bc3c2ef40ee7dffb687a35bff18b0a08116f3164dfd2833c2f0f5515b0d23b38203b195fe37674346811a7670533d01b215e59045379c27be55c2e98c5c4bf82f847ff528c57b49f34669121c11245e2735dcc55f128aba77590cc1cc4ef047ad8fa73f1ce0b7502dcf06e267ee34adf1c1ba5adc6684de424495f255b8438f560b02660c394a5074dbc9da381623a938e1da16d15bc015d9ff3253217c0c590f714a60ea371b5c2fb6da520a9eb8e1c50fe764b1b6ca4ca9f67c6c5b164163e1953e86051782ddb5f674eb2c6f2139fb2a535aff8d24be58ed8df38a45e39d0fc6c5a209e47f8717946fb7387b70be0f65e2425b12f665aacd545743b487cd079cd1ec87eac1ed8fec6666aac3cdde990588e500fcf8f25677557495ccc235a5d925e6cd2aef3f9d6980a485479ba7d0985ba3076465a606b6260623f6771aa2e3a9f85fa6bc03ab501dc4b91cede3529d4481fc93204afd408aa470de2c9191db6dd05529c76d33a916a500ef4b957a49182a76dc9dc19bfb7db98aa407f1d18e914525cbeaf575af6d15b1e9431eac12848213250de301940b6dd54497328e72f91ebd7bc5c4c602b0be2b99b5c00099296a74e62cf408e6ee1845c6f0f2c9441e38fd99ca6e75b674a0259360358d69b41745408a23fda9de2874c88e92e403c374a87b2ad797e534890e6025960710f3491e81d8af25aee983ec2c08c51d57d633158e3e7ac948fd1e105999023c86e780e3b95ddbb3f836eb93b522adf4184ec150863ba703e65229c0431f3257d16413f9e0410be6be4d63a7a85a58d360f05f30d4a310d", "5263535252", 0, -1835847718, "faddb4bd04d3a6c918933e017926526e7590ef2203fae2e07d607a5b3629a85d"], - ["4d6172840202bed3ccf849ae7b0ac75ca1b696dff59c52ee191066cd8da69dd699fd3d452f0300000008ab0051acac0063520edcf5d41e5a709ade8502563461427b6a4bff740ac26910f044a047b89eac93bb25427c00000000086565ab525351536affffffff016c2020020000000009acac656a52005200539ad86e35", "51acac5253", 1, 469254342, "6e730ac3e8b31ef8dc44709817dca89dec6114c38be80a744fdbabd032128464"], - ["7d51ab3c0404e46657df49c00cb939cfb57fb109c828904b855bf63a6c3a5432dbe40972eb010000000965525253ab52635352ffffffff4ada2884c23158854715057c3fc43dc4e25df4ee697616d6c40770aed59affbc0200000000542f68b8dbd510db5a20dfd559037d4228a1c53c6d3546f731d295cd66ba3d9bd792e32f000000000465535351ffffffffa1fc2229849dc96dc2a5e2133841e9c6d49b070af6151206dfa643ec3e50947f020000000200511931486d017736ee0200000000056a516a636a0000000000", "5365", 1, -1114740331, "5a2772187fd2d998e3bf36d953a0fdc2a84cbeb45e6c44b6c3fcf3ea9ff35e06"], - ["1bd9dc9f03ab3705b0ed884d18bd53ff3ad3aeb6e30f0543449491f0ef22f18f34c36dfdd903000000008e81f17d414fef0c4d306f208b2c1c294afb0309be5f8160a67b5548e9f14e0981f1c87c000000000952acab65ac6a516a53ffffffff4280d64662567c8b50bda15a2777d6617ffd0ae5a8d6eae8eb3382c016257a8101000000066a51ab6563ac89a9b70d0170e40e040000000006006a51656a6515239679", "5265636a63", 2, 1182737273, "8af6bbe9885ab99e6759f0d4178d03bebace2d297b2443c5d090a99375cfb7af"], - ["42adf5c6039d52a50d27e50f71cbaf0cca5469272e15d09e9417bae721af94765104fef4da020000000365636563f479169fdc2d0e2a421284f07f97e8ee28389a65aa31ce3517727d9a4535887b33b8310100000001abffffffff841b9c5de0c748ecb4b5737cf82d460d655a33a83a12fa828ed5c1b1abf6aa04030000000700ab63535165acffffffff011404910200000000066a0065ab515200000000", "5200515353636a526a", 1, 471122597, "dc70c64fd8c8e34e88e727dfe44325a1913e1baccc2f02c20a7f717fd50b6fe1"], - ["ec81ecff030bf08520417c4fd07e657f885b3f32a71a3b7ecec3fefd9d4060a5350d4a48660200000007006aac51005152ffffffff211fa2d830dd8d6330aa2d0f763f448e68cfbdb3e960a916ca6b03b91dc037e001000000035352653bc6212b03104ca8391689911622581729ea638a0c7b62e6112af61155cb11967e39b7280100000005515363abacffffffff0437dfb504000000000096673c0500000000007816df03000000000765ac52635100abc2bda400000000000000000000", "5300", 0, 246438760, "4614ef21963de20bb70abc89de8448031bbd31b4fc35bab759abc065fbaa1b61"], - ["a44c0344026c3441ffaffe081b245cf3d4ef2cbd63a7c25b311fcb264fcd0bd3c3adf1e9eb0000000008ac636aab53525200aae90845262dad03d2c0939ea1567f798906dccd3cd155bdb031072e1b8bdf67243a2f910300000002516affffffff03a9c0830000000000066a6a6a5153655497cb030000000008ab636300000000acf11d500100000000095153535152ac636351000000000300000000000000006bba260300000000324bbab57eed08ca11e2e7bd846ebef0c81ab0223a900d21f72d2e8147dfc6d4e1b169d6ab61981b06faeb7c50f4c0cf12c0f6acb6a33112b9c0f37fbac411780a8384cf5a45e5a89408396aa9e3a2c5567a855ef8b20c7f298732108b931d3700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b8d393e2d2e637af2f397d751f0ef5776b6595ee001a8da90045d8c6e80b4d0590f454f8607dc26f327f9e6e4f8ca965cb48cafa9f59968327ee9378cc3e0158367c3aa73fc615c7aff617bb0890901cc1830423822e85e8afad002ed9bed85277bd2bd8056f9c61eb8bd1b77700afad188fe79550335bfc58918059027ca41a022b6880222ed63fe312bb38ba6a6644dfb904733bf23d2f0e56bf694be0fd1c1c031fe0f99fcd3588059210dff5ecc862b5243e37cf83334a354e0f30fce1f2983d0b2bd18b1788838842a97ce53eb4786756d4896103760e4a3338a177efb4c17308220409c368890bc3ecb284fb1ef9112075471059e4cdaec1d5efd937d4d807c902153969e80e5b1a53a03986db99802441ec78e1e8caa29037148cafeed3beddd3021ef1a26c62a31d9f73bb9e29c6f4c6932958aef1f91a248dc46bf1fbb1e3fb5f02126d8e51b0367492b79ee850d1654cac42b4d4e5ba686241e651fe7828085fdf0328ab33982e01c5eb95d992fabde89f334f8eccb38fb0b9e34112e8128ef37e920202fb9d4d1da47b2ec70619932109db6f78d5cd3ed3583c0351f6067f128e428fd8b592300e624845bbb66e79e7f30f96fffa1e8bd7556e5e520eff793de61bc6c71308c53ede90954c6bdc7e00b36034612a2c1dba5c6280b933110c45e48e1b7fae4b2cd6b4e704b60ebf11c564c9614387368d22d5a171b8b19d5ed80019e15d90d77aaef8d23477248e2eebba74ac62e9de946678bb0aa6afb4c38f5d1fba39c9504670f874195b8b80074f53b4945231e66f13cf671e71ad4f7843cbadd3cd9673ec070cce4481518fcdf91bbe6e7f80dbe0cadb1ed1ff1099752601ccd3cbc7d047688976d81c4147f3f10febddd5add060591413a7ed977b365a3013b4ae992f1337387a53ae26ab56d6ac4f058fe969218542d441b781b8d90fda34b0fad3d3e5d6767c7654326abdacca91e88851e9cdaee0d3e9029fe95b2e6985a1c1952807a9b09e9424bc6d6c125ced8ffe8ea8f280520b1ca4c3493a809751e8b49f74d89da09b66d7ca661f0416890569e83afb4ac72ac4062deeea3ddbdfb6728b402c8d5bee75f88f186ed42fab3260d87e14d6c18c357a667ff0ff71051ad65c654f8278a8b167751026d741ffec3815e9936bc019b790d65384e192e4156871d93d2e80716600ac8b98420d700c22b800fad509ebce3145e4a0768dc30c8932c7c9938fb464f6acded64153e2336bcb0d92d4db5f8053d3f870cbf9e78e0a8b77d842fba77f7769f625958abcbca716ee8607cb4131a466ab9f7daf05ca4ffeacdf9df5fb2d1d6123128584fce6850686802aca8a81ddaefbdf79223fd935a9bb06af2b90fe236c4c39dc65e78dc88074f3fef8d1e663cf40f52bcd23d1d9222af0ccb102347feb2b8a59a97c8aeb286f3d3b3d3fb108237574ad023e229bd758e0183a29defe6af4d8732ad953d6ffbfd85b628b9c95c3b3eae0459aa94619b4b8e2a615a67a27446d8771428a8989a6115db3d4860a9226d63a264aed54505b59187a2372e9535ca17f6781c25f44f9a30d59c52f997bca2fa1a71845ef6a8335cc257582e38d488e1530d318ceeb590d565c690897523485abb90c5564503253f7dab4380e8c79214af5b179718e02c687c4762afdc84e4bf20b2f2b1749f02d232a57d611ce3da8a5a114fbbe962f931cd2f4e504be72ae65130d2b889dd805ce41ed60f67489f92f9d911c4db5ed447d2e437ed2ac8589dae7a94f3da31e1b91cf98bbcdaf6146e02541dcdbb9fececa91c5020678e93cf0c8833d36c840ce61ed7fa0ee4c5e92d917e65f391ed1f1d3af7d59557e60cb09e71e03a5c4ce1b775900eca8e010bdbd17016d6e651486ed040bbddece4850e524e2ac5f4cb42c71c7be7ca1dd908bc16f21b880c2a1cdb1cbf6947070e1e7e0da74042517d8961b87c75c75db879f7f438b99639eef845c42dec8a40bd0af6ee455b793e7d0981daced5e2eaf5cee90952028225cb692a829c5a920ddc2dc7a4728c595e0510027f68b197a4fa29189576e7934af94052fba33d0b849d167eb346f55acbf038372c4b9c93ee078dc3ee78fd6e2b2f96b46e9f8545945aa480b93829c183fa7c65afbd7bde3cdaff19bfef3b9ff44a91951a097ef0d0668c1548c042e89d62c2aab21a84e8ab0dfdc68e1ab0608a3c3ba66a64efd62ea9258662a6d4d34e7ea34c4fa9879962c40011095ba62ecaa947e8eedff6c2879998e18c47135d97a608b04fe33852af028272b6b1f14ae050000000000000000a0ef0f0300000000f4f7dcc72d18f970743346808892170483155ff1e6ae6a67a5d97910e8eac4303b521671ff212560433ab1363088c28816424249443076a94b3ba0687dde539f96f89cecc10bd7e06f2ec3ec3b71198c8989e599ff919181a86c4a58f0a02c9200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bede59e25a46627a9d5beb9e8a6572a83f7c1cdb1c193b9d182c0a6033ace3215de28b8e9bf7dfa40156f839be296f5eb3cc6e3128ea9d304e2d2861127c82e9f87055ad065ba84be36b81f0d10f50fa65339d7cd6bdda99d9053d02717fea4622ae0246383623653ee00489179e6179d9396ef41208aca4d4cd8958190070430321d2e3aef8fbbafc757ceedbdb1067bdd19c37d5f345b22cda19d1285e7fa65d02212f8b22b2c1de5d9e4c4d09273256ba368bc53c119dcdc36e25fb65778ec7720b092b2b63fdbda002a9a1d219c3cd0944cad951943257295709f15b15b70faa5e0edfd6e8962c16e05b8c9ddbfb090cfaafedbaaa8d3ab6fc85f11ae6dc4016530323ff41a46a086e3416791012ed634182e44dce606a65e63f4d62f5e083619c510326f9b0310d85aa749ece0963751f33cae15a19c3c096dc31e605771487aeb9e00307976ecc2f023940373920204f80134e8b475ee9e668e2dc2a8f8eb5f435b83b02290978ea3ce7c609c80b89de68f8931f7fe4e812e797c822647eec62607707f00315d740da55f4a28e8f17bee736c2b0936c414db32e7abb6eedc2acd74f658ef572a80079f66342d0b81d89d16075b01c3c7f03b351cf5d193b079a6e76dceb40e63ebed706ac5b265a1bfa8ca8c7816a05646795e9e6f3a490b0b4919b15f1585443bb9eca4efac0917b0f2fa7e9fcc052e2ec802360d92476af448a2bfe17ef77ec080c65bd92dda95b18cda55a99aa7558817c6bd5800f4ba9ce4a8857a7ac48ed259793ed4aeddceb949c16923c537b05c7111fa8ccdb56be7c27b0bc2e0e21abd4f861025d569815e9670fadda882d09c37a14fffb8c21710550995a62fde76b06f4fd8b3d2a2789f2d2f0a3d435635a5bc061082294df901576980012a0ab04cc46db7e66aae8a0efb0496ffcb0fb9ebc60426de669e3bae7d0987b8608e825755621e686ea1ebe26f769497c20406d6e280284bccc9b098501afec2d912d9e38d8966738f4fc542f5a51415a7f38d486a62f6429eece0fb5d88ca7a6d11bdafc42556d06c316378223d52d2975df3102610763e4000d0bef3b0eaa227690bb6726a2785402ec2771886f78a462f2004db996ea3287847bcf2763750d34b8ff894891c5f3633c0ceac05ec319c11a99fb5b83f04a9c2e9fc4d9a5d496c30e95daed0f2d1e24b6d257a8e9aca957342885ccb1009f923d1bfdaa4c4dab1b4a70b045eec95bc21b1efc516746b6cf65b408d2757138cd7c296039f27b5b59d6964eeecfc998a6bf692d1f95818f87daa472af62b1963157622fa1655589979d1d92a5d0540355f4b78887d22b3363ab7a29f10c8f0681472f9b50cac4c786c23d3541af04c4cc86e816e124f115ad360d650cbab6e036287a4bbb45e64b1abf51dc44a5d6398ae9685b1d63815f9ca2817e2d8191afdc4d005a87c56c5d9fda4ebed80a9cbaf5ce048f9395467f90b1a51c26d38199766b3de29aac6a085eee30e2fdec47f9827c56c1bf8e5e968dce40680618d2f123f306b30344dbde9310bf0635a6e2768e00f6c4f5f11294588e85c5e6621a92f5eb449511fe288e1c7cacb72dd4766e3903458015f8f6228954c23dae2da53e1a5ae48d60ab705c358e27d7707221875b2ebd81541e4fe376622051f368c7a561db842021815a5f443b6c0aec01350481f75b7b23d85b74e0acf234e024c5032041666091a0eb9a8dbb87ffe9fef1a6c9fd8f9808536c8ea3e18bab5f361dfc655fc3bad97d9606af876f817e2e8bbdec8acb174d6996d61a17d5b3b70ec3f0ab077eb42e6b96aa68d4ce4598c9977ca24d8c40c7f1f3b32f6b0c6a885063fcb64af999b1b9dca758fda4595a5a2543de14a743be8df6c8fad46c479f84ca18d01f352a228983ad5ef96744145bbb178d2cbf459f1172274493954cecab38197962c4af24fb93d5a0af4e005fd507c4c8ccb47c950daf245b0f41d3bca38946fd9061046b3b47027bf2f248f5210a4dea5f910a600f0c87c4a21046c2177cd037542979b9741574b4f28dda1004c897cc80a78994317d59b1fb1dd7fa0ab1f73d4505a6d1af9527d910fa1c599017d3b6894d0ed6ca6505f91b9c6fd86802cb6cd2da4eba531f0aabed267a26be1508262b67977a766eedbb6ac0fecef35e6fb14d62f7e25bf99cfb6755a9675e4bcb5c11b6928a68f241acb4842aaf5108db874b15ce932f9575972664fd9e872c6c5e4e7a444dd3644a9814beb42f3fcde8c47ee09363bcada61aa89c9f58b51c71374dfecabde27dc1b69d8d208c4604000000000000000000000000184ecb82c5c24d87b6ba0b59e4b13178f53cf6e6b60e924c51410e72da78715cac675fba516ca207aea71eea8084d5cc9e22fe222e4c7ce246dac90714b149ba86ee370e27be2fdd02157869ff75d0a11f1b3247073a490cc9233a32bee0f993000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a9e33f41bef190b94446e96d35a9e6807d07f52b7ca606a4e21a2a379fe8529dbb6f897ee0c65f5b25b74219813be5b2408315d164fec0a7992b4b1c95bcf3827f461f3a7022146abde4d5e51cc7793fc4db45505e3e7b1bec88bd081385806b8faa06ecc9fd8202f67f93410883a229d5ab3f1537d3d4c168cc09d278875a5020c3cf8bf939439fc55ec1d70a18cc1b3881aaa340a33377318891f5ccf2a1ed403065e0c582db8d949c0ef6ba7bef45732786cd0d1928342917e9a73ec5ad23c470b1cd77395cd57cf4406bbb282acc57aa16457fdf8b294e06e1679f7d218a123e326e661d040168f15ee868e5a9840a6f2ea65e038d9c84b1ddc0375181b870c65031a987faffc59f1421653363c3d31e9aab6d3ef7cb0331d6342db8c598665b544031127dc523801efce7b322f7bb73cd4e3d36ba4ff45d47ad459fdd5f8724b9913020ec151a89be917c55b0b680d5d6032f2c76a047165df729bb6cb7369454b23f10219cbd3a05ad36dada98039c5e6b39791559375940ecb69eae23577bd1f8c129b021235ddff6660ab5a538d5e8650d40a88950de8330f9c3848a93eda7589fc2de318c79adea4bcdd83a610a1777cabfaa96c5dfceceab313d0ffdd45c91e9ec4260950acb22f2359f87d4393fdb7a6d449c8a1b548602f80305ffdcb45cbf79cd6b5fa73f78d6cf3a455ff072b4c4163f998b90bc4bb20a02848f2d1ea74809264c7eedd59bc8aa5a2cd04619f9e1fab7c3d6920c09cba91569ab1ba122ad65c9e9aad1d406d7fe32a7093af91cf9a95cd64889965b05f404a1ad7b724f588d878c36de1babcbe111d0dec31cf36d3b959869dcb85d82906b1b7e46382d1c85dc2d9c6f35e96ec6edf36924a71e5f2ccb57945c646f4c76856b1fae0878332a66dabff1447248480281347c89837216a3400bd9ddcc95f03494ddc66517fc5f439bb9320119b96258f40393d19b7d8294e5566a9a6afe1dada837b3da32d7c31395f6a10a331929ad801b48c2763462f9ffafcbc12ade324981573c4a47c1d18b240ff171b4431b5c8ca82aaef4d8b6709a368d5a3aa3a5e0d4d3fc8371cea74c20d227a7f9a72dadd4fcc359bb87024e3257f2d4067ca08b84d3f601d4559d2ea35f4fc0e1defe8b5a0dfe2dbb6be903f82521a5dc94584552832e67ef04b5514d9354e1f6bb0b891c4c52344dd35616031d4359da60fc930c7f137cc8df411ac5439d7d2b9a69dc9f092f215b7d84ac377fb23f0d34825e988ad236f53b29c09a4cc3cb0d6d22de39a4720fc4c595ab8780934ab3a5c4fc8b07db996281c11e8fb4b7b61075fd520d1b00ae5a7aa3a6d31c2664dd9d72a75348fe5422d5e24fb30d1a7cc7e9431f6db6c17a91e64747012a2ec340b20da7486855ee0d1f477b53ba6a92a101d5a0b75a3fbbb6450736e849dc40d9f07c1c49bee90855d0c0c3d1f4a3fd36cae03ea99bf720219935a330bf65f7265ce01c8b3f946bea454b4ebe580c1285dd1edf9bd5dfedd570d61514f3d99e05447b37444e90abf615dac664c9b565b97d50ba019e279ab7d775452daf1bba04f5549b10b7922b45e73cc39e5d4967a1d04195a0ba2f06bd5a6956e47eff0ea5e583824f8e63769ccdeecb17446eeb7ab136e6c98ee69b2f46dfcfcd37772298f181a9766abf919ba942c05aa2699da4c19d0014f93202c8a51f7338d5686f3739faf79fca0d584f0f39c87c0b1356d616df357d6e356fa07d33ab0ff51cf447fd4605cf157718d88a34a1c5231bb51de8f554fcca0605b8637e0e30283b27319ab3a194ce3182586c1037d5896d257d0872a314d7e56a34e203037954811094aea7aba04cf12b7fdfd67ee6c8fe0021d7b56afb4234efcb4f3951c410c84fc674b0d8589378bbf71a84dc572feb42319069fb93076c519a77f38468a9332d57c62251edda1d8813c526c06b8cf7f752a42738033740bcb8a53aeba1d4a6ed215557797eba357c981454915fc30325df67537b7429735b9ad749104a942f559a00dd2eef1bcf4088666077a78863b7ae9a8c7221d02189f81b5c684c6212400229432bc473e177c4a8f5d1ec710893a5305b6eea7cd5e3ad39703419d561daa55f5471970d6570727f2fa2b1c1545974986c246db3257007f12e66ba2f8d5c5c9626e6a36cf40d06dff6ebe069ace3c76e8f1c7ea89c0c3cee944748dae97c3405d071d7f2c2b6e0764966acf06a1450bd4f496752a5553e6c2fdbb450def3e31c1b648b7555809e66443273d0c9098c937a06ede63c81d4abeaf8ce53723ea98ae25fc878928a093f846d434d74d6ec3e732fa6770ea5119d6638a420ca14e3748eeef987a5c3e2b8abfb0471efb82ec964b3b498eeb7a0c1ef02436bde3535b66d570ed85395ad5defedb4cb73626fd1a1b7197110485df315d833b05", "", 1, -253004569, "b9792a61bc77b537792b3a5b59dbd1050556eb42fe4faa42399a32365026783a"], - ["a61ddaec0452307ce7bc47fac5f36bad25ac9410c16fd8ab2a9a936f368005e013b5d36fd90200000009655263006aab006552ffffffff278dd8fdb92f72423a1326278137298607ca8f4dea1193c75c0d620e51a7ca60020000000152f464e668c757d18dfed275b3fb19e6a725352006ade9ae60b4ca9f319ede2e5cf6bce3bc0200000000ffffffff304271c8189575bd49fedc5d1044fbd5046163b8322853fb891ef00e28724c4b0100000006acab52ac00ac51087d79024ab3630400000000076a52ac52abab53a3a96c010000000003ab6a0000000000", "51635363656a", 0, -477329926, "40bc4dbe0a8c8709157c33d09c9d06f8c41300b2afda5e516c631e8cbba074d0"], - ["84c78bb6040f31bcbbedd16e22bf9e1a11032654fc923151b501e99b713804cac000aa335c01000000075353ab006351abffffffff866475c4c109aa93697d0755df4820cd631a1489634893da3941eecca3feab40000000000265009200f193da4274a58026a78e28467f735951a3bfbb01bad79e91c323acf81a74e3cd29d6000000000600535353636a63bf2f1b48e956f61bc2d95522ec6c2a0e982c3d1d671245c1b53171f2a566709c585b560200000008ac63656a53006aacfdd54efb02edac16050000000005ab530065abe0ac270300000000056a65ab53521d6f5030", "00536365", 3, -2079048284, "cb8943842968398b86a36681e48058e9df41f6a216beed5b95ec2df9e7bc6d74"], - ["72736894018e424ea797e461de40294b30ec5302c72bfd931cd2e12b43e9ea684e1bf9eef302000000036300abffffffff02b29ec701000000000800ac51516a53656aab2cf903000000000465ab536a2e56904b", "6553ac635151", 0, -732131452, "85b36358deeb0d96c6c17c21627b6b1a6b61f065ab595607e66333a810680008"], - ["d24ef04801fc0c0214bbadb9cde4bfab39a7e1240c52ecd9eaf1e5fc7c60ff7a60f6230bca0300000003656a535698f227049b2005050000000008acab0053ab51516aff2626020000000002ac510b0768020000000006636a536a6500d26e040300000000036a656500000000020000000000000000c31cbd04000000002135aa48c1975a25e34edc1224a4339c01fa1b798de7eed24bb144c10e75d722dc2163b45baf73dd77ed12dcb778b649834c84eeb941d05a0dd7110e53b8bd033322126feaa306f14d772466503a70854b594f9797cd529e8a472ea72149356200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c28a133e6eb52e40e6bda336591210b1750a4ba7e9b7197edc298d12fa69524781a0f6673c550deab8aa6d39ba6eb8da99d215079e1589e85591b527b32aa17ac2db2c5e96835da5f6cbaee3f4fbba99a22fe772a69239f9b1faefe0c18271b6b68d738eb78efbfc023107439dc3bdd2f01cfdcd0c535aac097c401a6b37d2ea032cd063ef904abea9d1b9dfed2358219febb309fa80c2610645ae7458db109fd80217e9f9123fa803c6ec2956a211b6a867cc097f8b08fbea1a891f6e888883a5d70a2103f5fee0f7d98ddf739481a4127eba777168c3ec4a460261705d784a20792c077c51d1314b2df81171a4636c1af7dc6dd1bf1daa0be8dc8ce87dd71f9df20e032f8bd3451d5c293acc6cb0e8a9ee66555234318250221062311e70950bff8f82022c0f96f8439465962e3a5aa8e87fde380fbc07acdcde1cd26fc754a19050ab52030178724381937acb6f8dac03f7dabe096d31146ee9fbe75f0fd4cfb82a765d7203087e3a3fef9920a79ba530007098aa3d7398f152060a5e865fe2886d0ce12a2c0218fc07553ac4cdae6aa81aff3097ea85cbcdd4d922514ccd50f3be70cd68bb89b7cd0b389f5b77aa837411a2cf26a5481653045417d6c892f06076d4a40b6835e7f8e9df70d1dcbda182511148d2094dfd62cbeb8090494cdfcc9b9f4846a65d1bc3e72521d04f33af17b2a98aeac1a369569c26c9140c6ae526ada577b8861038f2557d440d61b4276207cc593f66f45111f2a5f4a194362f4ecd4e015257f5d6934810422463e7237101a5ea0d4d1dde8f51c13a798344a133fb72dda03634a83a319107cff70e92a0ce205764fec982832a9d1e08c6548b18763585388faea7aa5fc2e3dd91d82c14db270b593cf327e58a656e6da44af93d3c9ed3dacec6ff1a601c2fedbed9938fec1419b8b04300c591c893bb0a592c6194752fe79dc0abe97d07d96bb8ffaf72474ff532e1b9520bdf5ec60f3d91bf7da1661b7df7377be9beccff63ef214e8d52652401ba703f3ee5a4425889a7e9bc473744ee209ead12d8d65a873c4ae6949ed331a542b4d81e4e2572c292b2deda2e495e60dabfa440f1ba33a782067c6cdf372934d24767628feec4dce5bbd1a84d4d4f2cb6cd6bd8c01655bf09ac065f7c94ca84c285894e7aba924b422eed357f1347041ffcfaec496743fe61e12bceffa17b6fc5818f5d2594bb2be5e93f4aed1afab828dc6623a15965f1329f49840c247ad1b5a7247800dc14d923b8b694c354049b610a7380459e682a7ed936561f6613843e5deeec4a627e555a533a932d832fe136558be57c4dd8c5b313b8c05eeef087292302536614ced4481f57c7d55c8cb44e1ae629df76164c678c6fa62bc499c72957c5176b8ac4c5212016b6b4c5d84141b30def1b554d51b0d3a10eee980e91542f190dda43aeb05cae4ae87f800ee3c78951ec0fb1fbf502cdb2c21bf6f51eac4d096fa7dc9a8081cb94f0ab7a71c2169b4c9de82063ef9369c81269d44b8491ff4c577bbb47db47efe1a1e21e1f99a275d59e005d729a25849623b960ef26f291777bcbfd9dd1fa3c6f4fe881ccc6698dc54c4a63f6e5f4f9dc68e7369c03fc3f211db2fdda17ccdcffb714a9e6d1071dd602f9ea9975d7a81403b6340bae5ca2ef70c689110419ac33d571c84c8e3f69087e37c1cd7769c2ccdb4700e57df07e7615d2299a452734e753a6435d0049049bdc81f6096848d89e3eff329384b02fb78e90afa59aa046e60a8c77e75e963656e484cbf0b1a60d15987f84c38b286796af0a3dbec806eb4134974c54a7beda49756d041e3d233888769e247c674d0885a56deedcb4c8266903f1c61c7a0d885019d2fc855db6d6042bc89af78d4de2b08bb055a6054a5c9ea71a49e7a588a1be873eff0d2a3a8af15d29492e0ce81c9cf4f0f2fa4cddcaa361d0ee3098e131f60b8174630c112487d39699fd236a47744850f6edb6bfbc6e804ae3dba02893d377097ed6ea2fd7b6ff844e5aae3eed658b9da1a69ce8189ad083bd40ff5881f48f528ce1d9eb236922be4837b612a01838907d03e6ddd48e0e23c5363c8e10dad10ab79c0432834b288ee1bf9a05f7f8f0a253dfad33f2d61694868e452ff10a92069474021608b1eabf53c1efbba8870d27be59aca0228b313aac81350be8925a637953aea898a01effb1410c4aba05e305de31c325e090e014b655d0d3dcdfda5bfc2daa0781a53662ca74b60651284dc909b0edfef629078c34f96b76d611ceaf9260596394aabaa0f53c5c5d43e55029a74200000000000000000000000000a0704d44fa33d462d5b221848e8557ad251d1b2b0dbfc6249b5480ceccd05a508f2da60d5d366f24774ec25e306f0e7db776e4b7dc41939fdaae42a2b30229e5280af298c682334cdbeea3a84641af33982e52c8c5c99b8f118af4a755272eda0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060ebcc76330a4410295265c537bdce025b0eeff1cf248b167863de34cf83f401d95bce4181c4689252a0a364ecb80506d560ec1a75dbd4283f99f06c9043b463af9dfc35268729da571a61e9da4dc96ebd2a1d0271df7ef96a84047d5716a08638c42ab7781d28010bbf603457498c3f77c0a61ac96eaef21b93b469fdb7c436021be9a37bca61efa934a8196a0f8eec1de88afb0c4e3507be021130d0ea6f70e1022e0ccb66ac8fccba3885754b703c8c201df3c4721581ed377f9893dd18c839bc0a2dbdbeb146f4b66a00433f464173195ab4afefc18c3cbfcf2bf61fa38880f9db2a8d6b416ef90e3a6305f52849d9857520e8086d175793748eda8a6ea1eb566a02130dff87b6aea228239f91aaadc69076d05ec49dbdaa8d73b302babf655fa1fa0225977ddbfb48af8615a73ea631da8342429ce8d26397bb6bd81d59283640f1ed030d17225464450dd363b852031ca84b4d3da61775312c1d45ccc078278529e69002295748610380998c1203e1f7a8285363f4be967e07a1b94e6ea05eb85064726b031f0534efa16c26ae557c511aed9be77bdeef4dc36cd6db864ab4695f069870b06f9a4ab756b213cf088e9da937b082a7d3fe90af7b2be87465fef2f106ec9b8ed822b4b978de7afde737df4c101fea7365a7a8b00b92cbbbb03e466423cff3a2fa15aa8523b1a3c30b3651f7e38de3462320e134ea932c7cf990852d4514804d182bd047df7965322a1cd3a9c7a50cbd7a581fbd87d28e8e0090b64d0957037d95cc2e29c4bc696b87ee05b4a6eccf73c6526723bc39960ccbb5165fa40fa7bcef20822485c16b2c5811f02ce5a86376dc10fcee4f08db5ee6ce16ba86d0d5990d1bcfa98b8e51bb1444c77bbf83a5ed429822fc61e1289fc85a814597256626a4bc10d4baa21c9f893873a728a23697d6afa474944881ad51da38cf1a7d334486bd7864433b9b9d4a6aafbe8cbcde2f3f0a3cbb1c3ebb388b0613ac57516a468e943652082b2f1c884c08d4db838bf3bae086719287017c9ab5f01e25e8274bb86329f81cd0a4a965f8a1d1b76b1378366a02c1fd4b3b8ac02dcc114dd82ad2c6e7b2fa543a29488d3ef10845e7f04791ffd5a7b2adc2e340ccf8feb6afc0b76aa945a8d775f70aec7dbc23165ad4e1cfb585279f895a9ed8351592d3d65b531eaf8d502140ae1f13059d57b89dfe4860fab7f8843c167a56b14e8d318ca0d27c79dd5d274069f69305053fd58458c020c9521359a5e70cf0877a577d8e74f708b567c9c6ae26007b0e6df62cc3b862f009acbabc93e4b06721dad39b50ce119050a6528941667e080c1643138ae1c44ee88025f5ad1cd82cfe3cb041b0f8ca0e45208b1407fbb1f008d83e8d89dd6755806da7e144539cd83d69cf73a8aa00c5e04e5072b997f1cdd8e850d4acb28b28a1062e5664d2ddbec8618011b8eb5415883e58aed18a1c0f6b272f4d73dda6ea4c8240b24561ef661344c1d3e9049875cbf8688dee4188f6379fb5bdfe4c8f4308ad1f2de3b716ae6f7fcb1252b23ded44c9df712ae1b818307cbd883e580d5c9838882504c29510c2d1bf341db037927ae2dbe16a3812fe45f67078871b55b4e7eac09cdd761fcd2f308c8551542ad04b7db9ea386eb1148c00e9a12697b4e9015d7025dbc1653fd55ac71c9495fa09dcf1ca208e13655f1b73b43d4cb8da9314041dedb70a9252d1b51982bf8d271efefbca2bb78baca250aa925260cebcad6724402eea7b5902304db9857898b473e4ab80c0f233cfa28af3ae66a082426bbff86da24d04daf7d2897f3261ef0aa69b50923cf32c490952865ba564be70158f5c87ee9bf43c197bdac9b28bd5c6c0743616fbfff7352d525eb253668aaaff20e45cc0a83bc32c0f3654fe4994677147d6ff431f7fcb279d9ec6afb873ddb8556e5612f80fdbbdd97174f09bdf8931838b7faa99e42c240c831193d31a4e36e29070129dc98ec3cd45529845e3cb0426eeb4ad4d07bc3e74e74fc6813142caa20f059ed6030d82be6d3d111aa8463a2165875f5e6a6b35a1541afb2aceaea9bdfdb58259d1d14ca068e3ededc6a0d5b425214da6ceb99d240ed49e2e54ae913cad4e8f7915198ca032086101a3ad424340b9f15038f9865af10e600c9359999db0e1731fe82726fb320eaea658adb5ba808b4c6bf5fed917dd68a845bf5d31571a337fe0e84e1efaf425d15059a19557741d886e7c937379a8c9c6ff8dd68197810a706997cb24d5c24bbd8b54adae58e50cd1601d71b3b462b1bfaa3324da867f16f37b3bdb0db17304a9a40073419eed474f41bbeddc761f016ded91aa4c92511d8d6190cf7999d9c353322e709f7bc33f7a5dce45c8e6cb5228a9d83ed4b2fbc8051d0968c9e6b40b061c6d42e46eecb1f1f91aa0b141fd22e1d427171e04", "51526a6500", 0, 616562880, "550a3450c2b3ea42c462282df57e53d25c09e14e0e6fd1f12cb49ab9e26ad0ff"], - ["0f1c987d03d0cf72a62e2627e00211db45c7d042fa695f7420b295aa2a650e722ff4c7bb2d0300000009630052535253ab0065ffffffffe7b866040f01bbc674a00c7bca7d2a5e7d5aae6121826198633545aad50528e80300000009ac636500515253ac52ce7ae54dd7d99915bff6ada040ac740f266b4169639b418735167825cd8f5e77a5eedf410200000004ab65ab65ffffffff02098d99040000000002515238bb430300000000055300ab5153ef15ced303c8995f00000000000000000000000000bd903a9858f70d3974279028d28bb6bf0c83be93411a7d283e2a501cbb7313bee05b227bdd331625b7bbcaf0600416bd1d85fa5305b24a60f0cbadab26db665969b1d722fbf70ba2e12a2b19c724b5f035797d315114fd1795ce3af2da57ebe6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009eb1e57c77df09819b680914bcbeeed296153380d7fb6823ccf106c881bc6d6663a173a08aa00fc5956f50681e5e68fc5be829e5f94ce8c8239c6f37b0648dee648bcf0189a5ffdddfb421598a0703454929a9d551262e93c1edb467031b532ca419586b58ae6018fb0b98eefae22c7aeb7cbafa4c01abdb26da6c469d9104fb0203be28da2ae6c41a7b188795731100ce5f279d6958f6849cd918ac208a2918d3022f70932658bc1ec673bb471959e9f9abb7b282dc57c2afd62644bf0819bbb4b90b157680ed59ff29dacd16f1f50049e821fea4a93339bdd6df34588cdbb46381e71cfd88b0ebffd9ad85274efa3fe8e31565d6b57b8c44c71ea882a2ecd08cbe8e0226b151f83e79c58fa3a8a8622ec75cb3fad6590191c99042d06af05d1ea57db4021058e0184a1ca178754ef40bf8affe314ba344e6bc7bb080a38480bc3748a1780316bc512dff9059576487151e62a41a7cdff07ded7fcaddedfd388f56f680d2c8022f75f4d54a2b128bbe521f7b018f29eb02cabd44076cf7909c951cf2ade3fb26031abe2486594bf6c86a43b5a040d60cc77567a95b2aeeda017deeddbbb916110be47fb96303a9ecbd8f584995669194f7d4dd8c8c65903c314ec24391a8c85cc67a55f23a85d8b5af9d13e446c84e26ba9ce8042b21c36965347e5d02de5e26cf1a2f8ea4ce4374f108b50faf7a28c7ba54c2584faa87d92c2c08786b1283c52c65343b47a770a2d1b315a6f9bb49a417d42ca4a711484e1f830bbfd60e9706f8b141422449d8b0d9e3aba6ccad0046ed2acf96280e995a629df3013575e1fa43f1ef6ff43d35b34de2add1bd5859cb6308fb774c04183c184a2076524dc05e2b2372f1e1ff95e842721911279212ae46d243e54703d0ffc18b6459771e25f417e79d5ef85fa100141586a0764aa6f563172b52bc2071776c7f16e2ac85a80f28989068848a9aed74652e809e8d9cbb5ae40b13c98437d00840566e8bcdb367ad915d989c618f8a6d3715c8a5eae7510fc0fc58fafb2a469df99ce0eca583ad04bf2b235b94a8a4c11204305624782a4c6ee33c65dc897291992b097073642ae097c7abe33cd0cc5baba447d29decc864de0d2ce77d3c3afec4bacec885d9216131c80650df2cbd80a75c5c7483c0d7e0f5df3ecf371605b84318584c8348a661195254b7723d743e6737a19841138ffb45a5ae5ad748a5b7321ee1406fb66d0cad92de74f17ca191bb0de36b4a97d9fbcd1a0f390da202e91a1e7ed3fd899de132c6093038aef5f0d513dec679c3d7af92f819a797e8f7acea8dfcf333ad8317b44de245762a30870245f1375e3c6d88ed3ce40bf72e98cd9d1a02d24b7fb54aad8eaec237581499c42498a7d713ff3831607e90132b823bf78540147ba750941eb6d4136aa1c0217bd160362877432c814937f25a5e55f0a1a47388b4ce173da82d3c26246370ebb5efe4c43e5164f14a74fd2d24bc81055095ac2f07349040b67b9b97609f535f569065e412aa6dfdf1d85d16f901e2e53e285a9272f4b68a0a85de908499ae434c78f2f2ac7caf78423694f81b93976dd0e3c3b5ecf2879379dc5bbdd7aa3914fd8beac75adea5d91eea600542774f1ee3343fc0277f3373a1349221847b65183bd565f5b875ae3053fa6f56584391426bd304def28bf51609983c397c2697af3c4dd4d21e75e8a514aa94fd3b0659a009adda2c331b1da1136c3c6384e97c1c999277edf9acaabab60943adf9b8cc8a9e1c744cb1090d5718ea9bf74735d0ba573b017b54b52d7ffb81f99b7718f892d6f8295bed370e0dc2e06f5b48f5c5bfad74adb50d56eb9af86bdd3e1b28172e2a92174259f2154016495477f405dbbe6fa16b33e6f123a35c1f0a8c406cdb8bf8e800f2a7287eef02f33a4422b5128a753b3af5211b8a0ab40fd4f31dccaedd590fa902ecb932bbba3b58ae7a45eba3508b610c1912138ec71ecbac93d9aed8ea110c1b24786adc62e390752ce2282ab05795ac2502b5b1d2a8be75f32dfab93411da404800453e0159ec254742f12516a9ddca431ac53cdf656fca2dc9d2cc23f95ca0e144d87ab068b5dc67e9afdf0e422763eac1f25ee33cfa4a59e90ac389c3d2a4b862962bf566b3636e6d73840cf3db1ab1967c7e358d391b8b2c848354e82fa89190ea967e033297fff42d7cb8c2224ecd8889ff9605bd64f7122a9741791edcff11624fa851db3606ec9e936b86b56e926e4b136cd32f5210ae70aa89b42c7726a4d8110693f3c0c18cae286628d34e7d700e680a9a00000000000000008f659f01000000001427eb266f535c9064084b90fab607f2935fb0a78e49d2e4b252267e79512ae9b49210fb1e22d9fba7c6eb691f399f83edd2601204b9339cbff5b9fb5d2e85e59bcb58e6225b6e8475e76891bd15a919f0f3edaa8fb3667e76dae009820f4e9c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ec3fe26a08b8dc07028f72d26b9d184d27b1050d341ffd6575e62451210e9ed61e10286989f77c8ad295028a40ed4845dbfde53f20741d219af23f35f6098f385ee63aaa9a6a763d44f947f9e53174b4f61ebad091bc4cfe913274e81d791aecaf4c85bdee7f40d8fe632651654c1ccd5a6186c21342c1d1f84fea250c0efd8021151cd44449fa64baae5631e9028c5a1b81ddf29adb545d4a0acde012fbf7a9b020a7e12fe972df913ec64d23cf7bb6521718c57ca09fb4631a14493c0841e76c60a1acd6cb8eba15be15e154677155e9d384db76f8fd213566b771804a01aa96bf11ad730e17ace2a633cd0b46789ac1901f77a68034ff5f6a23b6974f79f6512dd020ae7cd70d7d877a428a7c9c55fe4a5b109cd61869b918c2a837423e5083585d6020e697b2a0a93b2e04c18c4ea4a230fa7cacdd226d36fe027e7dd5f1a51f92b83030bdaf4cbd37e90a7d977aeda12f2baa106e358a78271eac28d7ec913d7feb0e6032c2e142c28065e5b19368e8d0fe9b92a315be9f39f78a46c3da8e913bf2ff59d032e150ea73ae8d3b6ae9a94117a53b56dcbdc1f09408b9f87957540be6d6edc1cf2eac3fe04fcae81801ae814100a33c3c8927055f12f4687574e2d11fc9bd0250930736d084ab006ab062e1d7d231c85da5ab3d325ab5413362fa2e58d7fedab7c6dddd49e80d8397e92b2a799d67683251267d5f58c3e7e80678a61ade049a99152cac182c29b14c78604d78ccdf1cd92ca393e0bd759d64247719edbe7523f202b6410f8b054bc9411563a937613624f5439ee41548aa1fa0d6c2ea04b9c03bb9b15ff897d705c1dba1d5e52081a466a8b7018f6be4e2eccf1d4d7b276fa9af8b2f1b1d30316b63a4cab320f9e88a7e97c5ef0d3df4b5ac1d6788e8d43dc7bcbbd9dbd8cd3a983c1b1e2f0202d90178ad8e7086b60a27a5dba0b557a98d1c9bfe1c239a689cbc5e81e572df444ac3dbe2d0b5dbc1990e18b71254c12dcefc818e400c2112dca22f7c45061bdde1f918ba3a6a50c011c42d35ebe395b231afbd03d6040a0c795784b56d08106d5616a362bbf2b498ad25d3d4fb6f87fabe5118fbf6352ef91a3c1e881b33491a3e9cf287d5ce4c1c525d929fb6c0d802988a68efab3a060a08302655a4a9d78b118fd423a91ecb97fd1d49b09d2e14725dc5c328832e4e07cd112ad5fd0ef6616c1bf160b2f94aed7c05dc13fff096b82157c7c17f96109a40021a67d25d577916eaa2f43eddd352c1b225e4ab3da13acc0087bd361c5b2bfca32451b53c4cab83dc18aeeb3184ef0a4c230cb79136f57438395d766a7cadf989aa0e9e9af7930c704dd95201b320c5723b37e65091e554313dd757165d2f24928950c29cebd31083928f390cf537d791568555131e27daadd3ec1822176887edf4de0b28305cfa34158bdd7bd8ede800667140eea8f865b7ce83bd1c720d85d783695d30f51731fd81c65fca06b07d358666f938c6b96786df53a972bd53d1529b9c88142880472ffcf678e60582917eab6b94ec3d6c11677cb1247dc72936c65903b0d9f4d195f91cfc08f2057db18948489237503d67f756c23528ec43b1b27985a355f3fd4087289058ded33979381daa8395d6d37d2447fc99580abe3c637662c8608dde027cb1bd8575cf7c08a565e9b1cbacd6e31d111a80b67909644b78fa59ee7e7b4ad0bbecd76ad77ee485cbaaa2cfa9f19428292562f416b7e908a6ac9cb3fec0b6acd6ecb1805192fb8ba4531280f6b763ef64bba4ca0f7b6bf3a3bb493ad24704e52ea3232dc486b4f678092f5ce63aeea34cb66910ce91d6f0e1d64f7782b7f122dc115e63d86cac55465a7c46c045af5ac85e483d0928b478c03180659ab469c6d617d0c67ed7308a2948300dc02374533d55cd16421a39099b27a0b0c2a3879861a18ae63392c81c62df09520a0aa2b16dd95470c7f1afa1c7e0a560f718e332be26fbf97d1b5e074148f7a5780a7e92ad1bd753bc226495de02232f5c371cdef06582c699bac566455e2539f444d95c01bf88f9f3d1413116a7c16e521fa6c97f2fd6cb0998eedce5c55841050b2e8128bd6160202673cc0e95c6dcb83b1a2e45f03b888c1770a2cc9125438186b8020a9c0111ed25e3d71d3b7a4076f5bb56a9ac9aad3115f5a66554320ac1d35886845db5fd496f44802ed11b7afab8710999b1da0c83749de46f80c0e4418944a1fd6551143b99e53f23e85cdcfa8d12b3341f94111ff18d53c52139db9fe32ea028b7fc1bdbffa6be85dc302ca259bc6f6478897dd6c05000000000000000000000000b9b200e72bc4c09f1b517e261d2886b24012cc68cfc9e0d4fe819a1ac2771bbee43ab800797345d6a95ba486d5b2f8b855bb6f1b936bb6b5e89fd0407b5b3140297648f8c68ed2c820d6d1712179f961e7e1ad796cfb3d2fda30ddf59d0f6744000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a3e5ecbd404110fbf75824fe5dbaf9b57b6133fde01a77a7e30a6cd018842fda6f365a353c29f6d262e2cbfe20cd0a8c1c624e6a6e4fcf97856e532740b5f7f563a0c47fcbceaa4d1fa8429d00b2f5e737454b8b4b8c23b286a5e440f7ca3f35c81ba621cf898d3e64d22b32afb2cc5e209445b9fa8d415cafc5a64f28489690327bea66cccb349325912e1b0e038cd37806f856ab75e19cb2648a00a4282c9bf032f6f6fe0910d07b99ebee5ef0ea5ad34266ecf33de617fa4ca807c8702c671eb0a00c1f8a383bdac14827e4be0572aa8384d12b357a9a13ac831338ec93eb641810b4ce24dd1f52a2f84c289d4f0ff33692bc7aa68965745c7f6e6e0cc32c0089a0313bbf94763175d3643dae025de45bea2759115011cecbce3eb4c7ddaecda1d5c0214f5ba88684512410c7aa2a05baed28934b430a1738f13a57bffc7ff7d75e2020300c446a3096d56770505c677480918ee4e946c7561f306b7f1d7aaccd177c5e3022c7cd94f3ad32ddc6db9f0fba3e5075ae77e581a827440f0c263c800a3b0b7f1031f96e197f06a9d2c003305bc8d81b36b65159eb3c77e024911f080f2e08699155c4a8d7ed28aba5fffd234391e0baa129c4a98aa6c415b5cef50803ba6c282619c1258c07fe6677bfabfe2056f76d4907636e3ee58429d63f6b524d2d3a85436f1bfcecf943a6f69a2af29cd1e91b99217a8c355769349735c3350c837acccde4810aaa83fab2a777cefff7ace26f980589b70308e5d2bbbb60cf64cd5c09882342679506a919ecf6360aa5c3982c62b1dc7897757e7ce016b69c42e62419cbc210f6a5d4d06f57fc9bd59e3a1ded33e9483d09b6c128b0d68aa34c50f22cf38cf4af401f455409e1eb58099c5e6f1346ff35941aae4d0e1a864327ed5dde3d2db9e16b7037283290fdb128b6a1b23dab8af931ba26394fed7825ef7efa3f322a17589f3bf77baf8a93e74407ba90b3c377750a2f25b1ff8b56c2c72704635927dd8793095b394cae822414ecc3f39102fb69306393862fd6c81a977e3ef30b68309c92bd358d627265714add08dd5607a4fa0e959f8cdb2ef2fb4caf0ee9c20bd6861fb4c03bd132e0c8bc2d14d80b0e501f06bc01aee2edba55c743d7e71690735e8297857f5ecb3ae626f23288acb814cf6350a40ba1c311798522e3a0410a07e14006339a054536f63a73fe3464b801f201f11ba2615dd90ae63486ed67d57e90adbe109bbcf69dd35367675066d13fc1b77bc62899446021d1f7f0c2e5c532268e2acee12cd1f5ea07a1e57d5c9f6ab30a26c426a708c31acd9b6e39e55dfc81c8897a017468bc011c81b0aa3631725baed749eaca8ba4509d114259c3aa23045c98f847b4ba38383bcbce13038286999e35a202ee1ad57ef38bb9ea7b8ca9c9d7e301e20d9ba3755354efd81518af5a296c227ab188a07e46d9e8cce4277ac837d78c3d6c022d7d0d71dd84c0ff3bc6c11b24dde049ecc36d294538cd683287bf00c551330a5abdf40b26f4beedee1cde431370a3fd0ea4c72ab4ef3cf95c9bdadc15e0962b5bc0cc8288fd4fa660b337245ab0df6152d91f18bb2e959ed42e1632326664324211443f745600b2df2d89ee106c3b7adf7662437b8ced7872bd294fe73e8df44dcdf0142bf3d6f52f98fecd3e11c204cc2422cd5b8912cd174c1120ff1f800865a15ef774337e14359701cef17bc0d08af760b038e7b40b95ccdfc6875fd628b7a4221f2dccc96420da9325f75e149175d3a8250dd826f3bda65182854dc03e00b4fa06f73f5609527e3cb1496282613024d254fea3b65f20c757a4cdbf9ba99b41b4054a217590af69f59c4b28ea90cb606610cf2403b3cd0491e9f2897ad44d7c933dffb606adadafe709ea234474335bf037a02a256e30ff5f96b713e57eba6df62124b13964e45ce31e3aabcdf7e79bb67930980128bada151a612f1a79df7755fc73e8285525d9caabc181e8a10cb1ea1abc608c6103dc0bb2522fc5eb863d544012f948ade9118e5c16624d420f6f5287233d68e7889e7d8ff510f4a31cf611178a60cfa88a31e986d63f769f7c3ae523ee4a8a7d33f565b87a35950e8453173520c2684b9d74fe8a137a625c3f92755b13ecf73110c2bc5e09f84fefeafdb4b68efb234ad6e2377d00212309678ab32cb981347d04fe140ac5c0b14940d51ca95de5beb4d7a8aef66939e9b1d041d822b228f1c8af453e2a23a0b1ed29f148e3edeb35581d97979739f1972192037f0d454a48f70f0f75439807095bcb6ba205b496b857df6d0258a705f10c5382bad9ed936904208b48aa3ab696bc983c2fd4b88a162bb7490adb11d47a624441088b2f3448b885f3ba24f2799c1aebc84c17cdfe71a186971f5626ec1db695f979b606872a4e5cfd2c33953061c1c8587928d583c9f700e", "6a53", 1, 968570945, "db97a44659afa6a8b8dab437affd047b78b5e8a528a2e7aeef971e588bba9441"], - ["749d52af03d97dbce330b9dcddb01aba477ccf318f1e39521577578ffd6f715f1d28e476280300000000a29bb643fd85bad112e8ca8a541fddd34b00c8315c86cd6cc86138c4d08d8451b35889a30300000002ab6590662a8d7613e996fb1566a0d78f435301b4a6ce4d2f8323c8d3e305dc75fcd0ccdec52b00000000055351535200ffffffff014aa40c03000000000452006a5300000000", "5265535353", 2, 1722198015, "722cdba9d2cd57d981c90a50eac8d3a79c5f1e90b40390cf8809112021d1c517"], - ["b10018dd01b6598f0febe78aabf545f890ad67e0f869a56a864a6f81861fc6183b050344bc000000000951ac5251006a5165acffffffff035be8860500000000096352510051ab5300abe737fd0000000000025252b8cbc50300000000096300acabab6a006aac9795f0a7", "6a53", 0, 1198506131, "0ccfc41fd78f4185055b65b650a00c0eef0f1efeb281d273e02043118de0810a"], - ["92acf4d20342b456acd940e834770017fb19115c889d6fe2bed5f56a9677383dc807df05fd0300000001651ff8febb5a2da28ebb29598f70af8fcd85aadd928accb42c06721dddecf305067530e4fd02000000056a52acac53f11a73e22717203d756482483b9031244c50898e8d65a725e5bce9bca5df00bc14eff418000000000952530051ab53acac00ffffffff016e73fc010000000001abac784a53", "65005152", 1, 1544025466, "d9403b6a24a00f08e0887c6294e72f828f2f392c24fc70775a370431f71f7937"], - ["1465af2603916546bbbb49eab4cda6d14d83f361fc605a1536923169fa10cf74e0ab4e2a4201000000076aab52526a6a63196a5bc55c6f4733399b88e76264c9391788ff4cdc11731b26fbb9549be0340fcd40130a0000000009525151ab63525200abb07604c93f25ab7078b963732d27accafee671b5cc09d24d839dfa43ff5e2628280a034d03000000016affffffff030fc9a005000000000452ac6365d4a1ad050000000000f1d4940100000000076a6a53ac63ac6a0000000003593536000000000000000000000000007af3e76f4980b702387e0afa5303db62717f7fb2907f0de7806d1aa17b24b0c019b0754b34871fecdd2427258e8bae932bfaf0f5406940582859cad20dbbbfafefb6ed5440c6d5f8a92177f7f83720188dad65629b2ea0f46193add8637906d6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d8c6ba008fc8f02bd45886c2a5eb995271a34cea289fce7f70ee8c216de2837b42ac8801f1235723b400f78d52beabf5b1deaaa4ce8625866f019e024010d9051452cb487ba8455450ff1dd86a13403914377ba495f674f69f2ea58fd3ed7de831a21e28b0bf2cb070684c63fc3dd3ed247c492e863513a0f1849127a0881940211fc3fd78b78b35ea65f8736e09d5ee78ea9a73fd68452fbeeb9d8f478cf9f8b021892eb6841e3bf6003e57f9bb65b494bd249823f6d55d48247ed75db022839590a06ed7e56090ba2469d191f83663904a164957f5780a4cf57c3027b412210d94f2128606059996c1bf2f9899e5faf4924457cf064dff0b903ee3b3356b3a09976020b94e8fb5561b5fb7a59bad897e2ce84ab4eba6647c7a79559ebd73fe773de3f032ff07964ed66b14ed46c37af807840692e02605ea063796208a04642190375f003209c65102401b8d0c4396b4fc8a818c7a2c432d5be2601362504804782bd3c1202241cd923e81abee836c1440fafdf79be9a6e4db2f338213fecee8e4bc73aab0d020639530ecd245642b282ffd5b10ca8df118c1f210c5c0cc4321d6ebfe1a8132ecaee553e0e9abe00e8bf62b851831acb3ef0c565aa5014291204777b6bc7e33c404376e6a5aa7a2d37c9db4f80ee94b14f95705ecca2d04b58f03a9c27b0542f16846d08ca0d5b064539e0ba52808e032a021357318cb5026b6a25baab77213535ba80d20a293e02e8c4e9d9a23f48334262ac3519cefaa81dfff81fd3b919df768ff2be2c5722401c6a6d7470613f08dcfe08f3c1d2d7f595951dc656bc2255acb8a0cc0ccf903e1127a242e3f67a4272d870036d68d9f8ebc08957b1e42359217513f3478d79cedd996294d722d54d7b7f7a60562b574ba70c1143dae78e3e99ad7f27be1e3a99131e4c2c1c9f19e7e0604b81f19d8f1b44793d716ba54fdcb9d12cb781e83eebef9e15c1c0ee9059831c6af37ec797cc12bcd3a80c23429e91e3458d2ab0e908c54877cdab36b660368bef6d961c7664469112cc1ac2f121130c56a104ba1f4c3d410d91822b50617aa022b8e1e69b2c9fb0eb5071e164a238f2392d52447505acb850e1d30b009326619a821506596482c383492d5dc6c3d70df765bcc9badc9de0f3bfeae2ec4c3a7fb31742a9e3a2ad04d96af02ff6b3e7959fb8d86974b832ef72414963877b95dd8953b9acba05c80f9d6f64064d5feac82c5fbd46768b5cdffa48e6d00535b5437a71e55ce5140dc67ceec4cabd166fd927c4ec7f38954f69adc257db0fc902427fadf383c8802c36719a89a3188ad95754637270e99a914cae4bd469f4a4c2d1312088fd83a4449122bf3112a34cf5179577a73dd8b6bcf239bdf7d1fa5eeb9621225dc46b501247dfe94e8db0b0b68ebedfd54835ccafdcb5cc273de102453b5fa2944c3324f8a5bae189bd99f4624d95e541e5a612dea168bbd1e7e248a8f15067e99198070322b7d5a96a929b4ef601126fd9507410557f55f345b6d50b73606c6bce1296f87d4f8c5eb2cd9b023a7dcf400555ba591022442adbaaf74e0d169d37f3fd5a529c6379ec4ccd8548122c4fe6dd988f189b6a9c4373227f3b3b6abca212a04865e9acad5b841cb51fe1f704a6fa4a872a0280318f883d348b3dd38a88402fb935f4ae9d52427e90779184ae1893e3c71d622b003e0a794eeff9bd7351dc72da65e7c9fc2b3c792121a1266241019fcb4a9c22e20eaa58f8495a5c2e3947b9ea654a4a02727292d183f6f8ae5ca14c50de60760a38d81d468a6fa1a798a02539ce095e759a2085e0fa8c2c676c39a471f8dad064f24fd7fe510d23d455c49219b2d5c8e14899c29eb2d2e4d76065722b1231829f2f15f06757d344926620da40860308e237fc81f43c7cccaf103a7c5ea3c6a5cf466cc94aac8dac53a69007a0a10789facff54abaf945809fbe3281490bbeadc5cdc11ed01efc9331c2f8a7b58ede38a3261082db5a30697f22536e363aa4192d40e71f25d065d77147849218593a9418c7370135af511697424057b0df15b3d70978171f7ab543a0e6a77556c1d93fa496c42aeb1eafba05609ac362ced553855baf8ee73e6692ada2dccd123c33d3d86991d0391f2458ebc44e86d2422dd34df251277b78d4d61ff5086eb046ae92f6616f222e98703d89d944c04396647de5b20814c6d3497f3af1674f467837915fe6c98a04083535658ec5e3e473c35edab29208be96975a9ab48e63e7eebb2d0aac4c8493436353cf1255b6ff5557c3667d59288a172c00000000000000004515bc02000000001331979ac742c7f6bc036e49baa52f3ddd0cc0e6d094a45e9f9b3676fed9453cf7748abdc3683a263df4156144412d719821c46e971407d3312e770bd5f2181ed3b7266c648c9814b40e0a1a7cfbcb806a7c831f671669e54561cd9ea7d1547e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dab2749f7e301a4c204223d0ef4bbffe0c0a938310425bfba71fb3b374c5b335c6e75350d06313e7f0de0199ee6c6536ff1c3f3addc3664670fe8c0f2963b00330c28d89a26bc5cddac81b178fee3bdbbd78e9859a34a3582211fa626eba7c61a651877fad43af7fd7ffc445ea679760a737d83ff8eab78faa6342b5fa59061c032d10536c053db45361766b7971a90b6c8b1f4e7e67d3b2eb2b2cf1e3183ea17e032e34eaba6ba5ded39fd236e4e9f94f0a1a43c3fde91713193ec46ba3d948178c0b2ddb1bec3943458ebe2323d8d0e2fa471d1ea8ebfea3a30f78aff2c65d438ae814cba2df42741de15504a4a0828a20b4f932ce4a83be74c01f325f9e4d762f61031f08eefd750500000a15ffb8cfb044e79b677f4f5cbe2ecd45e6e5542828d0260220555caeb58b03b51c59d53a150a4fdf72b3e1bbe34a91bebac2f0c18b6b33940204a6aaf7f72f4a4aa86678b405db1b21d919458f72406b39889fc7a2b0336133022e46c7afe396956e85080148071da595e3cc1831c2b37f5b36e0179a26f430fa030571582d3282fa3dc0712813d5639060f2ac917778ac5eba962794fc2891da7a4d291c09b01a6d83a1407c89694e0a14b3ed5ab080d9f95af76390b86f8c8b1c4538231087e0d58dc0c8921e1c88e39f1a7b12af42b04714a7c2e7f23e0bbd8059d2f62533a50c215d3747e6338cab85d425b1491531d017fdc1d8ee6b927bff39fffe386491e26b82caa69b3f9251802f37c2a6a48f7d7c8b44ab3b0d201955bfb78b78df7feace037aaed675149f87e7bbff9689ab5348f5d50630d77b214b822c8677feebacd9be2f390a44d265f4b0376c77712c23df227ae2a1155a2763e23eea4df15716707cad440b3013ca6e40b4158b9f20e105eecd3c3057b155443a734d6e95e58ad4204f5835e73f1bd01bf042e8601001869af091f4c24863a86e80de02554e0f368a847a5178e6c8e8900f6b356271538c9bbd91513ae3fcab36bfb62aff0e9fef0bac93747d6930e3a432f1c623e66675361bc548bf8b83c667d60f3e0d16ff5ef85771241dcd41fe76ed833070c7105ce0ec4075c4a0982552db63ee002a8d47afeee97a59fa1e73e6d33f8f3205d5b05d0ad5ae1c43a0db2f0b430c033aa4727dd60633f7819f35ed00c433ba4f1135141ef623074c2e86e2565810bfef81443789842c41e3059c81b183c802d71154f54055beba6a47d47d18be90b93baeb80d272d31fade670d130e773ab0c04fbcb279f5a23cb164987f3793b48a6dcf95894fae43985c04df3c5cf76d1e57dce61e4ba3a384844c87602bde0bccb634c66f3b26da59d9753d4cd82fa8059dfc83ea504de12fbc80fade645c7d8342d4df15751bb2cd648a13ce39b9bb9ed3aa002dfb50bc7f8b0e0181df18e1c9ea8c323017218a6c9d19ea5a3010f127309dd4050f7a1fe9513f72b5cfb68aca5865dcde7b3db3f3a49037290418ff0d91912304805c400551e11e041feb6809e6b509f32758a205847b9331f7bae16526a9b57c49c6ef4e4ae73fb6f7b73f788665ca338b2de1f1e0266654589cfcf484777778eede800940a0fb0b021dfb507f2bb4c564c404777b02b50b4b3b3c4223aae2dbaf5dee0baa829881ebf81764d557fc10d11f502510e287f058d42990bd2734aa7006c7746359d412b3c575009905d9aa3473140a1962948d9184e2494df8509c00dc342ea8e509f7f4f3a68cffa6d602f182a92ffdb16188fffef1a752494f3d755dfa6facac8b574ddc79801dc820f39defbfbc9882c07bc92b129955ce4f918a3cf85641027e91233a94cbeb806aae1e0c15d5d5e3d9de2483e139f61c5f52c30653697ab5e093f7581e6d768410a233fd7b5a62bd6dfc3519e4dd3f5cbaf7152f625a13d0002f9e394a61e40ddc582ecc46bfc2400f71f5e8d6111803fb4c454f124531db111f498f4986d9da4b058caf954c6220dbcb82fa4d1490a30ec336dc8bff1fc1ba3cf6d5ae24ac863722dfa0a07f854768ce154df2343475f849048d9bebc767af64a5cf5a459185bbb602da315e0c619fdb5e6b828836d2580dae4e3236731e559c5e8fcc62d11634ed98ba63c5809cec1171bee9d2c37458cb4b719cdbf744701167deb8963b013f28049a03aaa76b9bb3254b9180ed09f4732d70ca26bc75cfa33e168dbdf07d89d7471e41e0486f4660c9893bc774a435ddc7e4a07aac5ea566dc49e09bdbc4d316899bdd72f3f4f8f99558266e39ce0ad95df0bf6d5b245e09bd42d4a5ad4e728b1bea572a7703a4bd5b445c750000000000000000000000000007fe571bbb2e812c4a70b78cd78ff90cff57f88adfbe2fdf7026bb8b33f1ae5a37dbbc82528f249fab10c471426a342ef9a39a2cd7d65a31056d9f62dd1ca55e8cc9a2bc0a567a0c9ee0ace60f32e58795fb683efb6b922ee16f333e60a5f26b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000229c561756b18475f936fa3ba432376f80260b3f19f3baf76026493954940b8944f3af0cea0142b384e239fc538c4154b606fdf0a311841fc5fc63ea5f35366afafb1708b232ab0362b81024531f267aea2061d146b92eb81cab77bc16af0dab1f353513e1b9b2408a1c1f2ebee866b39b67e25ce096455ff850cdbd5bd350ba0320ab37a3ac35b23d7ee63d98ec182bbf57b1094295d31fde44f875a3a13b3b21020f5414f4523e2bf72069730a357a3dcfa58ee848efc03463b322085dbb86dec80b280f8947b1b431bdf8cf9216f3e8f919fa2ecb085201f42235952127218264a122b9e7b2242333fa8a808cb04d9a95c04615b3dc639161a07b2a9119d68407cd022bb540099345376e89f9e029f38548a8698e467382e4900a000d8ec01d8fc807020cae80d43b8b84120f18fee4b178f396cbafeac83d52da433ea406484f17787603146d91352f4e312c955a3cd1c6f47ed7f5f7b1e55e4b3fda1ad7e90007898d6802062819a15ddc40ad189b2adb634aa1db873563614cb6d9bf4c8cf564ff28e27f022e7d21056188e7a3c1b57b5f44b7f4d5e330640f8646c7cb1927b29dc6e816523f9e98ec9bf2bc28efa1ca6969888a5aebc0ca6998e99db1f0a3a74e05f3f3b947bd8bec29fedd93e64e1213c6d019c6ab69677ec3af870cfa57b605b942f546490f94a89ec0487c3828454482b207601e283a710b796287eafb3a60d2330eeee0d1282d38fa43963de55bf1983ad3202f2688188d5635a6b83cef6fac52eb40522878ffa2f85cd9cf12efbacb7a6e95aa1e24fb9995cc3890ab5712011c0166ecbe04b15de6c135bc985ff2a86499c11b4418d74af0eaec419249976ab1a80f02b561edc659cced2d2b01a15c807f20230cac94cc14c608198f0160509f1f36f48da82451929263f08232e5dda037873550b2b6b8d7078ad1dcc8a1f4c36674cadc2a3ba2b0a406f97d1e105d537ae7393d611e96125af0d0a5d1dce1713340e17c1d3c75403019dc6c6e71ba19b467ca141a67de50f8c1c10c90d2bf01e7bc2d1e8215bf7c885de117e064dbb28b4561df8fef0c4168a95a1007940d48c6d5417900f6e0c9d26d728b480b8bef337ef383fb1f291a00c66e5047bc4f9eb1c4570673a867731d477116023fe03014aef0e7919cc30ea1349bbf4317f994a964d16852816525705634623cd97a8e82443b54cc74753465514489f1750ff5131f56d169922410173ef4fafbb53aa501a7de0b8840888ad33beab082d82260d3d1ddb8c9d6618a7d9ad69f5cd7c9feda347831f1f0baf0e28325f683c2d56c871c3600c8ef9ac2f4126ab5ca2d958c9ea57b9837fe950977023fee46834fc8a7a02b298a4dbc13711bdd0f483344d7ab20cfff38695b19d30987369480e7463cb4e96a3396d241a7f218f9b3b62715b4fbcac1e52a2b5c3cb3bda58270da624986e95a15f368d63f1c86abdc67d4739abb481b7bb8b0b506635fdf8b92d19cd3c06877ccba8f4f7dd73dcc9eec0bca263348d7228613254279bc8800f1a666bdcc78cfaa9a206180a17f8de1ae30d080635bb9811504a4762867f5ddc55d7730ce8636704db52ccda2e4f2bef847a82ef072f1475134b0f330fba9edd9c232f86725ec6a4059ea6f7630d6b4d42bfe50c56ef8d437b3128bd311f60e83b7e933743b4cbadcdb06d6d61cfd17499ecbbe15bbdfd3d7a859a3af798e107c11947271f7be5a6b5ed87ba5e072116af7dc78f71924f0b4508689e7b36d56c368fd3292ae54649996e298c65c84de6a01ca18f0a7fd3336676541ea311eed6984f1462610fd8b5ec3b719c37232c304c18e2bcfdf9ebff6dbd8c243ab5ae348ab94dab17276545d0432a942744851e13ad9c290f82af8692d1066b8f2e8516881db2b658f3ef1b5a66586a51e275b0262c9fcc124a722f0ce68f40ea5d8c88a86ea064f154194cb9a82f89f2e6bcb16af251b7fbfacb2c86a3def22aa08dc4729e17733134adf84e9309757389dfcc2522ee07c494373670818799fb8d7753504ef7b8fc187f0081c029b60acf247d93e1b3f25581655a76c2cf40fa7e3c649ab2018c5169415cbf90f05adeda85e0f4eb03966b8b3b709a83de22c7a699d3dea42fc4773e7c4d83e1810595670bb1285a93102514e9791f3f9e7f1f74ddd5674c9c6e1af9d8d774e1dc05c9451ef8ef0a83c7a730d8a1b870c016ad291af915a004434f77d6f747a24e51c7f8a3b52e7e363c4cbadba573ad4e429189541618bebd594bc28f3c85bf67bcc8f5e3a45ddf38c5a77f3922d4bc47e76b7311da5aeb2ba31be339a3ab87ffe9271cb61ecf6b94ca4a8cd9661c5c5591e6520c11d10166860deeedb8d9644a02d313e843108fb8997f967db29e96e476c94f7cc1f3cc9cf6568f5ac82f06de91f6b8982cc879575612c05", "516a6365655151", 1, -1609995268, "0c90fcffd7a7a1b35b849db654fff8f3b71dc9938fbb4740555346f4c03f65e2"], - ["432230f301f4ac25151ec09c420c6372b7409ec85a25a4d64a2b8a47048755d5b2939242e400000000056a5363ac6ab2f7e1d004ae2c15020000000000a330480200000000046a515100472be1000000000005630052526a2a815e0200000000046a0052632bdd6d19", "", 0, -441325616, "a2deddedb2e2ab7cb40ae5740ab8669f47bebc8ba6f3c1a1c85573465130645a"], - ["ac02aded03fdb1f89354358a1496207e2fb9e4210968aab55c65848c6be62c9bbca33c0c5100000000050000ac65533057faff1f15bc5f7e08fc57d59f630e7b611e530a63d1bfae1d0fe8c92afb2ddcbbba650100000007636aabab6363acffffffff508e7c465c0dcfdd3d3535434446ffaaf22ef803ffd4d5c9d92d1e9ce034fdf102000000055300ac51ab62aa389f04a77fcc0400000000008850990400000000008d838c0300000000008f115201000000000800536a6a63ab536500000000", "6a006a0063525252", 0, 1181079097, "0d001d8ad43abc4457a5d8b1e6c39810517d5459451a5a274c73231b1b090882"], - ["c301e7a201790268bf7f418ee32b3f0b84e7d5f9406b27aecd4ba69b7b619467556d4663270200000002ac53daa5eb7e0445e57702000000000665536352ab51e5f3eb010000000001ac55170f030000000003abac65a4182002000000000751ac6aab656565b745b34a", "", 0, -479447383, "96d09221331b6a08e224ba65003e62309f8304183a3f71373a0704535b13bf5e"], - ["c3d7619601e46ff07403d2a789e8ea3ab4c6295e51bdc751d4a361eb02618700fa46b028160000000009ab00536a636a6aababffffffff042856e00200000000086a636353006553ab3f4a72050000000009005200ac5365acab65340935030000000000436aab000000000002ab5300000000", "6553", 0, -817234043, "0e7eeae3d0ef319b636eae2e9d6beda237f6b0884528b0ac0cf8b4c0a243e92b"], - ["b3b490cc0437be797c194a2a6063273c2ddf18c31598800270d1b269fc9f17f8e1eedf2bda0100000003656565ffffffff72fbd6c465c18bca4ed28e8aafa231f765509f34fe6b29dc95016acfd2188d49010000000853636563ab000053c9e35e34ccea0039fc21f5d06ba00eb241a5315243a8609d2e621b5b5bedebdfd25187850200000000ffffffff9aa2d4fe25dbb060213943d456608bbe0ed4ff5228461fcd56498487fba75b8f00000000065352ac5351acffffffff01f54dad0000000000086365005353ab63006886b81e", "63515300516563", 3, 2112582072, "feeeee394d78315fab87fbc2a82d1bb119de3660dbd6a781bc45e052c09cd45f"], - ["7881b80b010723adf5f2184187b78357e4f786707ac4f7c6790726facb60f6ce0baca2cc7a010000000151ffffffff03d6e6be020000000006ab65005253ab850e8f010000000003ac52ac8328be050000000001abb38b24ab00", "53ac5165", 0, -1702400657, "944cfac48535d715d11bbfb90eab3ac85abce145004c5521923c7fc9220adbe5"], - ["0d3fdc3d02fad2dab63a3b20540ac11effdf753ba3974a5afeb813a713f531ff5e781f50ab0100000000450ec220476f4ed01e8600b3dc65fc2ad1cccaec5ef45463e61d28f93d1ed0e4d2d781580300000006636365acab53ffffffff021ad25104000000000073091e05000000000000000000029bbda604000000000000000000000000b967babd92e695b9384cbe8c9ef3306326f97095052267a469cb893fe601176433137542e55619acce4b6d1b5c50c952dce97e46544c8e34956c7f02de29ba7ecde8a3a908290033715b03a15a6d2d1335caadae339899ad941458230b24d2be00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d4092529c74f171e27d8b95538fab12a98448ca7e4a0a523f0617e9fffb9547d9df569b123f9c8f1b25eef44c040fa1772a6241542a12510ef1390e6257e2bcf2c1423d22f7cfb0560a5b1486081e26a24507aee52148152e3288268a7eade111c59cb017600ae8d499868630103839cdc837ee1e3006adc644fb1c97d123ab6030e567fa7782441c36c253e971d4ade298d37eeeedcec933d8904ffd226df79e9033062fb66afa0fe4a8fcfa732e7b530a00e80f487b5da5511d9a2107f617535de0b2bace505b1dfbaa2ea5a9d064b4dc6b8c739971dff6e6bf26d3b5925a4a3b2140b51d144f7d5cf1f98dd8ad05fdf160da8c63f84e77541725508d3d7c57279d402201cc4c39d7321903826a6cfbf312b861aef3997fe14ce0437b04d4c7b73922d020d96c96feb0f795837314b8870c41b9329040750733400ef08a664593b6759650303b944f477df31a5eb87f966df884ff0170f59debb42019c06ab8799b69738f4021c15df2d6753beccda8a1118ea1a14726ad0f83d03c5e09d3ed22ce53a379751031811fca622c07b4b39f8f4bc9aed8283480c8e191fdc38e78146019ec8163ace89ca2197e91bb59507064c77bd383ae4c309c9ffaa98bd6790de8459d46216ceafae59092c2a73fc9d6c725ea2609492673d91051cc177cf11b9588ace097b5ccb8126043b036b5aad053e44816ae193ca201ec6f4c9426baa55ba897983e46182fc577d9f05868428378f175fd6e49ed007d8c0061e5aa481f414aece9ef5c314d969bd928b0791c013f90ba843578e921cc969b2ac3045319af3dcae35a76fc0a13c6e830dcfd0f5a0c61d24301746f244186a2949c12e1041090f916eb8857868da6203d2707cb899891b480f2889ca6ea37c0d5f30b68a4281023e06244e4ee94af8338e1e2b39ac35a4730f5b77618503325e7c3c5f0ccfe47862083d9eabcde4315461cc19087cb59fc2aba7e4ccf130a630f126a98cbed04ee5c04da4772bbac7a8b1baed76c67cd89efd56e0f54b7b1c5f2b9924f26edb3792809d03097b075bc0b814ea31fc2f9fbeb7f413b6c95006fd518b3739cd71c798e420fb4c82a8c6440f5291c4ade64e2c2accadbb865b486f2674d5ee48c0b2a9b91025d25cb25f704586ca29bcdad33bb814053d9c47937ef91c5aec054430f13c3a060858838a4d329d06bec441e40fcec7d961f08ced6b6433b392be042cef1d22de662b50182a7ae8c89fe3c5dc2f18455c286f04c184aa1f947f1a7ef989ada707d43c148be212110488d456d2bf4439d7c6882d56fb00d5d0688502269c9579d1afb705a01efc4b1283fba0cb2085ded66e1e38d115ba7daf4c91b79b9efe4a4f6bb0ba89bd887d6f3f84382d9628dacf455c9b527f84188818d3b0af5cb35b5643b7df1b09389b83dc1dcd9d1ec9e3bffdea48edc25cfd80c2ac4aaacaa3855577d31155ed45662051d30827b6f603e99156c9771b7bca38bd4070b18dcfc443634d1aa68fd2940cc0e7a3a6d7a29e3351d1ed4aadd5cb3cd5967b2f171fca1da93c88ab350a784ea2d37ce86ccea403bf34f1863a749ec5a8a5c07b7f13160740210baf4f97388154517471b20e20b8c7f0c7bfb61e59ea90e287a9870adfcf1c4846f538f905890b5d0bd1df0689542913df76a549888acc7578317962d93df4f7d708a5be7f16cac48d3046330321beba670d59eceeba41c17c45f0a2cc4628b17ef1422d1c665fe7be19ce343e96a3bdf5ee2b8aa4c0e6ef5e307ebba40f5866ce81da7550ef83542d0d013ea377fa81e463f0990160b1f2c8e40941c67c31f2787d4fc255a8344d90a1aefd8b101909048922f11309f275a3ab52e8e1190dcc5767ebdd1cccff5ebbbc848d54ae7d7ee075b398bdf8d95c9c4f599dd8dbcac4d8704c4e48ac309edebabaccb3ec6b2889a58cd72023bbd504977c0994be6f5e768cbaa5b543056b01aa8ccaa47aceda7d39064c5d0b7794fd2836099f6637de2c63efcda05c2755ce752776e87d1dec4641d93011564329ef82af428f75fe829059035bfd26713eb433c751392023bdf08d8eb0f17ad9b84fbcaa6ced4ef93d1aa98209ea71bfe92dea32a49538fc1d660d2ca72f2e0030d107ba2751a667c06034fc4bc80276ccb2f32e8e739f1410749a82733e6c2c9e0c68a801af82f6fd38fbae3b90ee9e540aa6b986b9b26094c8d743daf9103ad272fb6cf405a9adc516583261f09adff7893332f38e99851d2ea7ccbf0098675a336f4586c0a9ede469ad68da365032ae799b8c21ddf771270000000000000000524b160100000000618a203257b8efeeeb6e595ac4dd1ad4b15c0293949347aa2885b5008a817147fea016c72d6fd119f26aca7802c10e58bac60d55857862e38a949b8303f66ad06cbab3d91a9067ede04432f58c15c38f3f70e3a05ab04445a81586d83dad231700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000103ae9ac43f6f78bcff81f59f06d516ff046ec5618dfcc4be5d7c6ab2a16a9da2fe1d61c61227b920a193baf5430691724d71a147ebe00a6e081702133200ee7e5e9a4bfc309623ae5c60dfface6f12f2c4fcf82bafa51f6b3fb1e982707697ffed09cdc9178183d35a7983aea2ce57040a1ee475f4e4fb9815d2bebdd06c7f70217d0faa77aa1fa537ef644bffe281d9662372e7db4b21eaf370f4fc08e37068e02280070c3d9f3244798af536ab7d269408cfd1789d12335a47f2c364f5522bcb40b28ea1ac2b56cd9258879fe48eac397ffdc898d78b5aa7ddabc8d81805d8f17b126f46ba25fe36169b02551bf8131a6df182c270ebab092dc546a530719a6b86f022fc652557b55b794b71a78800f7b4d1ee608adb0a245a3b0ed4db95f48e80acf0202abe5c4cd0514837133feececc0dcc180e123ebc66121b3d931f7ffa58ecd7f021bf87690822b2d406dfe9c90460ed947f9cea689d787eeaaa3f612e04d9e68fa03226553258175dc16f8ee81ca0696da749649f71c710afd574f1d6b5831d2e172022935c8f79ddd10bdffedf84797d03f087913048ff3d762f8d7a53fa7295b0e948a05074c5e0a626708998710dbd7c1ac15ea23b91ea9e85e266f389e0678fe0917d42ceea98fec564ad188800ababeed1f79e9b52c25704666361949c18900f5f738040ae42eb822947fcf5ffffd585fc7efd7b491c18b47851250a7faab4aca9c1355730c249390e0760a80913633d73f5e15db6c4808213521eddb1ac308b19725fa15a4d0b92604896fe1ff0a2e21081d0e955a71031b124f6e052b73ed52fa087c34557696651adf397aeb0e37c2ec8cbd0d13fe92ce08bbcd34c454459b15a88ae8fa62f3eb67f421c4a4c152b20dcabe18bc08684ac0d280d2a24fc3c847edd93230a4e1d6736a938c174cc1a43b46b54edcad8a532957f56d152134e5eaf4e1d064c137883883d99b68a2246dc435e7e226b77cbe6ae1d37c3031d1f405bfb99fea16883d7dc9d37aa81784c3d826b87e69b94ec76e3fe76ddd1bcd2b90da32991af5cd4d06d74e0ce41eaf8f4c300af227a5030e48b3601b3e793672ac134fa7b2a5538cfa1659e6e4062c6f0d18a23abd2711b7f0319e780f06dbcad2a83c92bc5e283b12b81f44d096c97f4442be19386cd1045efff045920b705a09eb5f7e16571a6104798b354888eb5af2c623bd99d7ebd8704ce7ce0f874915fdf4d86eb70e75acbf872a1a93c5a1a7f2ecbc986f939882c273d30c5eac1a1e37f9fab3f25b5a9a96ff4a805c13bd93dbb33b22b4674c47d99ef9f631fd98710d334b7c16a46d6e4a46317b4a6a781ca639113182b950aea6d97f847780f0035951e05865e4c17b3b744963f28e5d1a5d568620252757d8968e77489131f6ea7c3a513cf1b96b245894469991789789d5d80e1d7c5d6d1f74a844671830179a1c4e990f6c040b57cc0d45745823bb17aa827020131e5a02402c86a75ab2d1b483d481158af8ee9f82e0f9ad3acd5c8ce92fc2f50917c7c8577b4ed600b819e00593c657c21661cf3ee74f83e119a4d0ed19046bbb6ab28dc121db3548341af8e2c197d0f526a5413a264051cf7e27eefd56a7f3c1bdb0115fe837916bfbcaf23c7492efe00fae49fb55c058867df7705146e6d5a4810cfad56c41b6bda09ac8ff6063405da4f902dbc666e6160faece051a0ffe0b55a3fbaa11692f0722e0e12144e1a58c28c10fce7de95a960272cccb74eeb106503338b29dcfb6e22ca4bae8c6d2d58ecef29a8d59ae3e70e34aa2a99144b6fedc47558b6d566f03add2c937ad5bf1e152e45545c76b262c22e46aaa93250be9172d388e8f951a4893e7c0b7919a95b46d132c41e6049315da615e59dcc07c0f9d4dff5a9edfb9a9b0bdce52018668d3d01f3117ec1eb286ab9c1fb192bcb2786edc28416ad89d2b54418cce414e86f6aa996e5700422aba15f6a2bd44745523c4f9a0232996d309178d866aaa9bb7904ac5d841d1a642e5f747c4be10e90e924580ed140be9c5f19aa03eab0b19c1965c719b53b50a96f9efac911ebb8c6be0ae7a4ff38af1ecaecd938e77f15982c53552699555e9c2e7a10857d7898832f9a43bf9263dbdedb2918e91470b70908845192e05f453872f18788f9da67aaaee83f174316bd2b73c124aa4edb44edf3e4569ba42f33df5cee0e02b98d5a5daf430a37bfee6d091f73837adc8befebaf8eb219a659bd1906ad8c1a62c20ddf5b31086cf41e0b712efc3d2fc402c51f4b37b971081a51cb78e688609df83e348057668fff52fbb68944eaa66b04665b447a31eb8dd903b234b6026b22d590e8c393e4595cc3b942546da2c6e433c487c8ab3911f166af66ab47cd46d8f13337b240ee380dd03802cb7fd9596646bcc63af6655ad9bc2041b1925fb65530d", "5100536a5153006a", 1, 2128484212, "0c073667f8a072a52d55497381934a4e350c33c82c4705c7abae57b483f41d84"], - ["89634d970348a02d05bd377b79ec5c303d175e4c2c865cd6551671d3a1a7c8629b977faa110100000000ffffffffde45d190ade6253823d55ebb5ff720d1167b5dc0ed62552207dd2f9be10ef66a03000000045100526a423559a3be47061578a8f2103f300eb6412111530da6a2fffc547e6c0190b81fb59575fa0000000001aca0a6804f02864ac40500000000046a53ab008b2aad02000000000351656300000000", "", 2, -1183668709, "4e4ba43568605e895800309260cd06e9233767e6c57e0132ba703df3c311955c"], - ["bb4e996603078125edb44d4c80bf286c8d2b83dccf0f352f4469a99b981017b8c34a16152400000000076a5365ab5365abffffffff569c13493d32578cf6e0d143952e649922433ab1fe3edd2e21e8bf25d2fe6ea000000000066553abacab5140ed5b672e206b9c9ed00efdf30967d9b4b5771399e496a2bbd72aeca4a992360e6566b20200000009ab656553526353ababffffffff01c67f6f000000000002515316d1e16b020000000000000000ef4c9001000000008ef973fe53762692895a5b28b0fb2e55c560f3f41df626fd66586e6b822fe777aa8f9908453ca0fc05ac31e99ab75086dfc2e3a64890267769537db05845f5c37fb791e4aea27eff91978b35e8538ed90a0861ef8d1edb58d5f8a5643615bb85000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006a174541820080547604fdeb2186bf9f1727ee8a92d5bb2971b1ad8c614e0c5d50d6db7d450b3585fce05d1ea6765105aa69a2de1e644f1a26b0025cb0365b69c4a07ffd8f1d86bc0ef20da7639a1b6ffd0cce8beecdc4606ce1322abcc25e47e4240559ec0d66885395963ddd1ed091265b23aa42aaeec2b20baa8c8144effc0229fce2606bf7180da14f77d5de398deaca5e543728b4c9e6381567f18157249c022079316778e811b5d3214a89545d042130eb33f4b9f5e1574ab491fb544e90520a050edbbdd814ae40c02f43cff407061c68c4a4e17a337e35ac1eaa4e5e973cbb2bd5fe95882b7b28d2cf26a3ae80c470813de98ac204c1041e7b4c2310848cf803075a2855e9dad212a2d635bc8663dea374f313355b40118e5ec10635d2298d8f03136635a9015c5aa9962bdb3668b0d4400c51e59ba83b9d6de5908f8d470859880308c229c95b5afebdd48d496ce0afeb4b14a10aec76f414c5ab102946b8a9bf8f0229741b95182dce271cf6b6c18e6bf7ed462402186d877af52a1e173a0df7a95b021934ce30603a236e2529f11139ef04488733264fa4a8f9b15abb92265b8d76532b3db3d98e063ef67165eb0513dc9c303cf50b0d42e912baf04408aed41366ca77bc9185aa611598ee7eb8e4ec255fc4995afd2deef44f486880ff43023b4facbd4d56a7deb38dd22fdbbae24f73a227314043256bb8cbbd0badc228b4842745436c6fb0b5bdf3d0698a36188220b4204976f045a87fa1e41cacd2d90fe967039fc7f8c7536387b53cc3c64e955da105a536c952c79312712973ba8c464bd7c0a18876082b9f36e4e61114ed1b64e1018fe00385d01ed619bc60ccc9db4c626a7f742bea3e8db863543ea717bc72659c6346d4c3003017f052294fa1ca06727bd8f443f34b5e6117647298a3c1e5aadeb0a9f47c7af9ba9bbef24de6d1d76a28a13280ec8e156c9b82b6c1e9d235e62029a2eca899b603249b85ea4cd36669be3fc8879190849425356e10ef3993fbcd7be0be83c936c4e6fcffb63c9a8a5bd23812cf7b826f86109c1999f7e55477c4a4438f8cfee33ffd15908e823c266108f955508cdd6715c756be951abe4a9bdb436bea6e7b84e07f58f553c84529c5c4cc30f89978107e827babe5f6d8aaa7b9d081637ad78d5a323b8b3c3349ab21dfd3777821fd81e152345d116f4523ac4bc8c8fa58158b345574e5c597374f18e899d81f742a554e65658627ca245b9885c873efc153793334e51762ce6b5136cd31545f94f9feb9fc5df1fa819481b9846001a91d5f885da5feaa7e9e1cd2a9963055b6043267d177850db68d7ec57c968bd122426a12df8a2c528e4da611349da4120200db82b25359b9583cb7dac27f6c6338079432ba44ddc4f4be82d146336466e14af252411c466c3c2f7f7509d1953a90794fa10959d983be145d73e4b392a41bb72cc3084d0d663e622c2def6f3b526f32da7e227f12647da138b2bf3545f823d68fcf9f7cc07bf21ccd30aea972a4958e2e0c4dc63111671c556231ccade97a11173a97feefc169bd497d4f8a0d7b8229edc164335a7d68b10702a9d2e55b5889c43dae47818775eadc1ce22325affa13b095c63f8619f86ddc282e99da9ae8bc1910b8617f632789b4c2b14b18298fec213453d88a36a9e7565bd13c6b19ece9fa36d2fbcb3a2afecde525028350141ee94ede7c4148fe455109af07f215df20f070ec68d42969e883c10b255faec11b6d3d2e3a3d7aef4dcf5df715ebb60ec135ccfd61eabbedc0145c3172fdb84d939c4d9e4f50c59b894f1f271eed99988a34f929958f36a1cc2a0fea2441359e886e37e1be1ffb4dbf544cfe544286856b093463f5bac71f9b24f49a1bcf984e40018f5a52c69bbd7527f67490f11bb6f0c6842730ab25ef2d9fe8fad21c9bd8e1d4a9f512cc3e6bc49ef12d095bec7c78eea93ec7f7e516529cf8c23f2ea31def6e90ed4477999be12c84298aa65869ebdd50b7677ad7f42ce7833178f8da2cf21f307103c4095955b188a1b6711bde4591fef58227ca14d4ea6d4273d1d9dbbc9e88a21f1502d1b45564d8671c9b71bb6073922833763e8f315488d14c17ab2dc1c0d7de79ea00bcacfe62c12aee086f3cc0783bf97dc8c3d494845d1d85c2d7741e4f2aea5b3829751535674caf36543c8b76afe33f5a7061309176bf82a50b9c9ce03063c9583b90979069ac36326e2d4694bf4436e9eb2f47c8e54562b28e1a278398c850d6ba84d93f20e18aa1adb651340304d7ec0661a2641871a03366370500000000000000000000000095673fd59d4d44ae78dc9d1f8cd54941c617719d8433787da50afea7a32c443412fdb222a2403c24606061066b585794e62c679837d878bb3ca27f88f78ee097aa96017c12ed83be765fb3c38dabe13b41d83a2e08e20106dbc2f387a912ec29000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004eec79f38ea78fafe9f6768ef1edde8d63320007e98d0e51a9dc5258e1fca929f2d1c9fffa0a7563644908febd3212a18cc06cfff72bac86810e5863f12e19a03e5ee07af114636dd9a5c35b7d06bfe66511dd5a9c48e18aac4c18b6461a0814320499203ffcb47cf8b41efd20c979ceef5afd1a8619b6d2e00bbb43fbc798d10302fdb946fa1796b2fa5be961f965b38392c4536c9d89e0e2850bdb80b0b867ad031cfd43f24a4887c4aef3c82251e804fd99c266a44bfe3555234b200607f436730a02ff9165b47ab4c9e378858bf9fd151a7d4d3e511a9f8d857b8487a4837e97b61e1c6c7bd94e93b02ded2426e8ae9713749f1a080fcd1823b89250c648215c090217428cd2789cc4050d9654f049beda5eddbd398199231b250052c1e8dd0afb24020e9064d2fcdb9cb88b16ef32979612b3f1875789f5e1fbbf4cf56f3c7ef0c3c9030c3a22225f7421e93f1851e04c42acbad136462602667c7e83cee465e09bf3640212b25188350c6fd44d6d5233dcf718af262752ff6303b3e42a382c888ec3f33903260c6024e1339c5f910fec4b3adc63582ab574baa69c3ffdfaf96fbaa6e926a336b1b26b1066acf29a517309f5c13c17a54087b1d3754478a10c7ffe3e9dfafdabf06e4902047863da6d78eb36766f995a2c9753a9a745d0eaf427b0bfa01b932a4c785c7c5bc3e8903fbd58f1ee9353a69171a2a07d8389b4167df10917aa446e3cb59182c8583b6aa5a039dfa357bd498963fe82dec891efb8a9d14041e5d48e336d3f0a00f2d5faac67d9bc20187b7012249c86078a088b2488e69052f921ff6cb9bfe69e02594b100f4d2dfbc7a27777d26eee885e5b5ea1253d1e503d224b45675a282e5da5a2f2897c1330820f35d7273a36bb2138fb1be9cd8c2c8872f63a885f419b4fa9517b050724e1829f7ded707bac4f4237735f743cd55960a1b75ecff53dad1be864b9304465a9a033287f8bc0056a37926799d4cb889b7592b5c1dbffae18f41d104bacd64a2edce91e7adf8cfa38912ffca52294a841fb49a5ad1e30682080071a57c58999dc7fcdb8442f918354e15d2854394def1b5187ae5752c15bcf9ff930a8be1a0b7950a21c135af0551acbaf482eb990b391bccf89c97f427e802bbda3eb2d087aefbaad4534b7ca75858d6a11ba903974dabcea521afa80c995012afbd78ad8ee6de1939e13942e8fd2b3a4904a46fe827c3b209e330b7c37b8dc48e208b78612f3188302cdac6457a5ea44b757f99627cc7e39ab785f13d46c04ed388d6cc83016bdb4c5383a098f461fa39b33537b5427141cfea2afe5720fabe35839a2737f1ce6348f1209ccd9740928b911d32368c2d91c1411e91b72aa876ffca25c83504a8af8a9b7b6d8366e87557cfcceda90ad231a64b52f06a1fd3f59b7675764799cac94360b0106e5a8ce4e8792a6a8c07589918d7cfd9f290cecc370649224b271b1eaa118f4740b15993330a83c7a9cabb7bcb5adbfee12b5f21b759f0a309146448781c17653559ae4490f5f1601fea151485aa19ca9afc84617071e9e9a103c4e31e1c0bdf108bc2753e4eca718c4651eeb4839682157e8e6e19029f08e5312acea3ecdcb44adaad5939f0f68227080f164daa72ffd6965cbb5372527a1ea1bae2597376b2797701bab637ec29673511b362e5222716b6ace3a3983da48042baf113f7a92ea96752966a816eb0fe0fe7768b4a57f9f0853d495e8a3810a97114e56dacf4d8d5fd452f58030eeb257f5359163d4d66d988376b95973141dcd27f2b42bc7c6c28d5df66d82bdb3769b8ca1dc679d0505d8c29eb3d0253b62b6406d908707ff206fa4084034b74dec7e56423ea2e4688d568eefabb188fe5fa2f5bd3afe104e5ede7527b2df85ca48f5f145511bdf05c73926ff9d53aead8142f334c5396399598e7d71217678ab053a89782bdf1d4c417abef23cfddf874c1f71b08fcc6a635b3b3215994b6bb422be696cd0ee67d42b47417fda348ab7f7c0d96c1e31f9232f9b7b6be06ce8a6ac733ea42cabab7f8d72c0b1bb8f72aeb074dec504b7fff08af8991a1908384162da12fe2e2edc0be596772ebd7e54a857e227e6b20071b34ce6c5de412e7289d6ba76454ad8baa56e9dc0aca42235b00e5f82c335fa37dce0941551689d3a7dd1ab78f0ab0a81ebe77986c80abe6ee8ce4a0a07b70ab64648cc7c79f87a007bbd9303d98764ca3589563fd72c497c9cf64016c75e602dc2a6ae6195eee93fa00e7a96a1ca0a035036e1a862abf084010834e105e088b3da8dd3371a42d162341cac2d32fbc52bc79e9dd07319fc3d27cc36fd8c669a4f7008c47920e52eb083c1b65edcf398947d27027494bfb75610991d7698a624508f3a528632f2c820af6036e918beff68734c1d05c7cc00364725c91cd9569d0b", "6a63536aacac", 1, -2126396243, "7f6bf6a09ec6e56563d0b47e82e0fe82eb4a038c737f5a1b39c90fab74a0290a"], - ["f940d172020e6ce30407d65e13b5be3c043cb0e23941bd5eae046476682ff35e122c83eb6c01000000075251ac6552ac00ffffffffbc79826d613f48d49e3e557ab308bdaff79c9b2bcc9f5251f9227e912a69cd74030000000853656552656aab6affffffff04e18c230400000000040053006adf8dd50200000000066a6a51ab6aabd06dd4020000000008630063acab635151356ee101000000000098bb7d5e03f7c69301000000000000000000000000ab01731d5fe01b221c4175f7eabf53fdfb9957c5e3961de7211c1c99e2c7d9e58b8114f7e20a651997bae3fb317b7e452186d3333236df2a222d05cf3177df6d61f83544a023f04e9f0ff8f337712fc4b9f7bec6d09fc432fac9b2dea13c1dcf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f37283587292e6b50a3038d1aace7102ff00446789816265528d0f1fb6de5ba452865c724d1e5535b4560c62eb647002a5cd17927e8471c926d7d8c58f10b62ff263ee458fa8eb2122b247df0878700fe2aee424ed194dbc55f82f29896986049528bcda3f35f081bafc280906d6a57fe9cfec13da52f10e72a3562c581ee77a0225536a25191c1ced1073402cd9bf2ed01bf374a0c76e7cadf5510d409e978db50227f992d756734419688e76b5d2addd7081c2edbf1672277648b954eb1678302d0b0c9909dadab876f72bc47374495a4f112b0d29f5a96b446b8a23ef52b062af212930f9a4d8c073dadc6524386e8530ba140cd47bd868ab8eb26898c5666f1102032d078ee96da32cada0efdc4dce5098a63fc3aa69c104a07692fe4d57892d4aee032d1878d93c450e3c73fae0ee71f777b4bafd40f11c1e67f49a6f4f4d8d57d38f031f8e44699a26a9fa03ff09d96f8ef4c8285623b1dc50ae84443c93f5e3495cae032f6a8b297796ebd9f6490df07c984452ddd767b2cee94f004c60cc0ac6e85ca402183b2c2950245367af8548b416f28ab77ee92b183df1128977857193dc6c07b65c0651a3eb2447a610bdae079ddede9f7f0e3b913703c3f819acb11513f16804fe3923c0dac9c5c3825c4a9406f867dfe1d089197ccba0d76beaaba0084d277b8baecf63b892269b7033b60f718abd992172fcf87f209596c0feadc5319d3f2bd7c3f6066a5c9c84b180f16e88069c6b317c680fc085c4d8d8643a44663e8bc2aafdfb0bd84ac5980de5508bd2adcc26615fa3083d96257f35db583b3b729b5e0fe22fdbcb06575db814fd3a06faf33ad1f75d46a5ed99181f2c0b70a8540df1dec0a50c24cc3968e342b8fe6221a4f29d5bf6d19b4d61e5af57e6ad5a87c4021e73a5672910f368964c9ffd25d46d3719d818bf6dd21cf0f5c921faef2e94594e0c5bdefc6369d7d24fc974ea8457e9733278c09f932b3896d0244e96ed1dae50deb803bf82b37f1defd71c67df95be1add3eea9d8d595f531c8bf5adabfd437e4663cbbc89187c99b9ed8d63ce2eacedbcf77686e31c2ddf9dab65cc879620d1a46aa5365377fbd63bc5e343e2bce140b3f6927151b450dac48b6c694e2a11619e01978bcf1b13fecde40834623792b40954e2fd784317be9d6b53cdd902e16d5a3f843043463c5f5bae553095bb94b194c9f999ae718a69ca634c13a7721f3ad2d9aed1026867defb26ffce0e652f379c2669e56f5cc0618a88e3decf3aa81b076872c0143ed790ac33f5613d19629190e4091463a158af6f403d22c8f10c61660314df26ed320283f218171adf0eaeeaa01be6980fff565989b84cf36da2f5d08e31141669f475baff566ccc1a97895c1e93abc0f6488bf907eaab654710e5d479304a77f42f6ac802c9f7c16b9f14b2712e2d636557a5540cbabace0257e560956064f4e7e1b2f83a46bab5aca5f9efe63e4d9dc94977db7a6ea2adabc6d59a56f53a8211bd77bfb0732062cc466e39c30b3ede76c6c828cd727f0b6546929ac418345db068f7e47b33fb6b3168672f2324ea206c84f149656bc9a60e0e887c8f178645634b808d0ac8de8edbf90082ac2f5c147706c9deb525366a5e5c92f1240ce65db0e98583a4ada6905accdae579e5e8fee5770eeea3f7f50a15bbb739a4c555d0b0625f4bd4c3e673313bf2ed91f2bb1f3f3700add15cf44588314ea3945476aaf4acfb88292fcfce1616bd658f2540b7d6f77daa74da205d8cbf67dc19dd93c8ebe06ff28b27d1b5550d6cc399fc94dae8e7fdfb421f9ec51fa9b2a642fe7954ee6bd4e15a98e05c52d8ecdabc103bf5d9b2153c4203c49d7bba65b43563803d222639af9fa84e3e74a32074e6980b862c77027cdd1a1fa5817015cb7bb43b237aeb2789cbf0a5f1fe619698a626c625cc91bad52dc651d76e3e1615c32498dd48a71ba92cf3872018effdfa6d1cb2aec293a2e564ceb9d0e6fda32d2f32b4a57ac5c751bbe999edb16949f66f217402dc3b3a086a811805d454d64a77138137b65145fb747d6add79ddeafbeba5b222421ea71b48bb313b0e85848c59ded0a69e5f3941d7884517375cf831ac6496281a008cefa8cb3cf9591c822deee6c5e5adf2916c1998bdd522c51236ab02b3cd56c68501122d0f85aecf8edc72bc91fc44927e365ebb001593baf3e12725a06664aa345e3ddf992e3f46881904a59a8f54d45a826367e2ff8c033bbac052206d9c54c7162b919af3c09da0ae0bae2414870a5a1f3b956d5f62d0a2d9000000000000000056b95805000000009bbc0294528f2050c510db4c90af0fd8035fcb4ba7932350df20b8d27a2bad48b17fc2eea127b0508a88920e99551e3e34634f1d460d08b33836a626922377bfedcaf22ddbf66393b14b1683f06c61166e8b972d725ac0d4f90f40a00fe7f52600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fd24cb07a154d9255404d74d87c083cf90707d20cabbeb54d144f9722643acacf9c593e6f8347da755a766ea4d5c1aef772fbc0da9441ec3fbdf2f384c5721b122c81530759571ccc4faea8605950c8a7f277af2e22a902005cf5389d0ac1cc07563fd46cba0f3afac7e96d2ada05e381d12f34fb8da0c8d6fc15b50e3b0d05503046795ace7092af55b415b74bbad8a19bfa8c657940d7beb705e88350ec1b82d020ac30abfe7de4bfd395b4c6c044d9c0fa24d9aaa6fb5f53a3154f8316c0cefd20b2af69df37fd4c161ea608e3e3e507439faed3185ae4a4182a02f18e81a8ea49224487ce053e20489deb2919aa2deb9d47beef1adb189672c5b35492608ad073703021f20898fb876643d4a4d5878df085270ada308c6f0bd4f4e2e5072034dc6190301e1f26a1412dc6d01843787d032c1a44a7b2ef16d40f63ca34dc654273bd7f003252cdb5489cf65f385c7ad676d39121ef836c050c5b93f73e8acedad2d49fba00306724be3af062c4f6328731bfa511763058de4e557bcd859937823d52cac59a00212292dcdc0ae3785286a109a4c1fdf297e7f244d94ac0e01cdc6a074ccf4e81bdfd3387fcfb7fec1b34df291bbf4d3a4c32d278f335497cf1699e00a393c0371d5c00df1ac9388eacd76918099a011f5736a79ad0604abeed8b5f030d07afc3400ea84d46fd1a325ba504495d0304f9bb2ff08e371b046783589c0ed516e9a1d832a89df3cfebd7b0bc89732f5d6d560f57335563aead411720f418b3685416611e6f55bf409732698f899b70a1aea18858bc69cfd39301cd472fecc26fbfb95a7fd1ae7abdcc1bac6b949f0044eb4a1152811878f42f4354d7909b18dac3d20154e80aa213677c0b9ee014e58472ac7934ca56e41f4a5c9c7bc7eef20c4cd6e8564368ba9cf35cd4d624790b45f2c5752d8419d8853d52cc92427c01bbc32201125b09648a704c3c70fe1c9ebc7bff171915292b2f2da94d3229dea579affb4158d2a764e5f6ad17c57f17ce287484a7d231dcdb4bf54e678d944fb969381e519773324f0b06b710d30c34be8c2580db85e2c094dc398618c38514110318dc55e3ce49f04d743f034c9699ba4fcf474fef9f273af563a569dee158f65216ec88e3f22fe17e37f5890cbee307d8bfe358b470e8cae95569daf502ce41e15a0ea941506740ae274e2d440ad7fdd5c04a177e3e59dd9740b9b16b20f33e59a2e4cd5449f4bdadcc52c05b7076425d37a56d4d5fe2bfc86468b33b05f33b8de1065e8b763acdde296d9860ea8be57bef211f972917c0cd612e444481a3b09c21903430f1c761d2b6d6894486f87df4498400d5a824874b4556f8dc569d381b34500392f144c5e3801cae195b3f8a0f184943725d6abb77fdf598af9bfee305940696d1f2bdeec12225e3f148a3844154a59986d39be1328368bebbf434d8d63d5335c62f2f42d25afa756ed24f7c75b9bf32ec5a6ed441d9bd302ced84aa05712d80ae6c0d84836283f8b6a33e3b35cf934e936a4885e28160f0169b20a4e3bb451b22d1b74b9ccaa2aadfdc8fa77768f01a59ecaaaaef9a768825ec7f85003c81a0002ac51c13644579a9253150bef29f4355db646b065b3e36f276d59ea74ece4a2df0da54fa225cbf13b745cbe51cd8d2d135c66a06254d9b01cc6e8dacfcce30ff6e31b0eb149e6f9564d5c9ef4f755e5cc9d8ae44ab7bbbf0da7c0bc890c79a1be3b1495cd46b09540bb0a2f58f761570f016b6d3969b91f1140b9377b47d1ac7399add9610ae76e9ce6185ada417f6d22d66788c387cb419d7b485dd54bef00901322c4bb42f70b6d59ed5098ae7ecbe1a37ede2ac393b8a2f98671da7af667fa724ef2283d3ee3c55df2a2724f94b32bf9c8ef90e5d509d03b641777b8d7ebad9ce7309cd33b04f9b5f49be5a4ad65154ec045a00e7caf70d4b459c15684fdf235972ff34f6061b8f893c388d1da3996cb40b8a69c4899ef80f2dc3dc8b71518b6d71ca68805fb467c11a0a3a57c20ce35bf0a885431d40ac29800ce881a544c0f9512bc9f1ac9a0cce0fc3a78aaf364f87eee4f137316b7370ce585b95d60b9fb6a91ffa2c7eecd4fa563073aea51b212a2d540f9dedeb082fd6e4e4a793e62faf4e14dd0829e46df88da05e58615d687b21f6a25b3654c6a71535fa3bd6f74b26f504f461b1485e7d0d140be8afc970157fd69b34a253aa1197f2ab8e70c0307c33ed369b519c36de4863be54b690c8539f7768b17052b55afc22c3a83e3cca210ef216ebb86f1ba057864747cf0ac0000000000000000714e640200000000a6c3f86361684c3fd9825f7d19b9734713140f25a15453121315837776c70696415331698658e322cfa843170d9e7f831e8d4d149f9c8128d7a34fedfbe6905400c8b0b81591245aaf08b613f1b388ac383b77e11ad749de1cbdc184645dcfc5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009b23794da8db34c9dfe349c18d95ecc7cb66b9854302fd3ffde82be948a7012d16ac720ba10247ed95d2c3de3b1f45c096231696ce46616daf4f019905b25eb9f9e576ce77e01fe6e14bfc0e03d178483715153a0b4a604e2720390c298a628311b764f0edc20418b8c7dc094bc6b60896bdab280739544a7451004de4ea3bbd0213218f9758ea127469cf5a13280566076e0aefedf64dea2041bcefe09dc6430d0215f3ba49720bed55a826860581d9c50e3c4097d9f5491ba8a14eb31c11a5c0420b08b8132226292d7b56316a5ac14c670b9c9fe4d71ca9588e393d79739bf3aee6179d8dd08465e4f3bfbe45a4cf20e82e3b289e8705e96c3468b45766d55e0096021c036df5e48217b03389b94e5e5047026a7f3af6fca415893987456c4e4fdb25021099276ce71fe17db677fa08074061e66d54689497091e60209b7f13b4a73a760320acad174e0235bb08edfa7c5be99d0fbbb2dba2ed3fadf7479e0b3d018543e00202d6aeab86af5cbbbad6c1ea8d2cf71a28b8af83de16673d01a2e34eed9f342403247a8e9ce8fa2cea5953880736cbb28fff62bfd1084df1f9a9ae6fbf54e75814353c3a45fded83a646a93223e796648ec547aae2d1fc548708c57613fe1ba4c186e298aad7943a48d0cf451b272218635978013f1be6a6a7731214c83c15d38dde2a6170d08315a853f0e029ff50a1526d556a141dae11c804364266111d1c0dbd32b2234c45e610ef2328bad202f8f1f470244b3e4f81a892253e6ec7e33652a419b276bd065e14589ef8903a6ea1a4902d5d84444131f098797c356c3affce420d3bba883a334348ffd38f91d198868319f3231dbb1b186594cb3e53f734c05f54aabcf6a5e1079a7da3afa85c467b1e4a9c17907107f92f757e904ac1115b6f3cb8ac695cfadfb6c721c04dd8ebae9560594fe891ba045f62f5de4e1294c3f7fea463a8db901e757ef6aff7ad9bcea177b5adec237a4b26135d125ebe91215b50c3363020c9ef120349dd1af164fb1523ec6908bcc446880102e7574fe605ca5310e17fe21ea365b6a82df2a8e7bcfe9eb2ad745a22fa39447ed3d5ed84d919d8c3b9874b0cc4ab230bb618725c8bad085235941ab95f9cce6da608326a1c840f4e290d65c64c342d5e63c14ff09752f5814c665ac594cce0c219c70fa943ef0e4d0f14b6e10be6a319b7b57d45a6b44c3cfb02c4281ab6e3089b91bfa1c60f283b33931e2bc2a10204f7e68edfddccc1d2f9c8a53563ea2b6f49594716abea23ccef2d3d363d03b7b4f2d3192485a732b74b1693e1b42a50b5ace9ed6244dcc5a29ad20a01d1c24c92d24ba42f6cf3e6cb18451e416af5b4fe6f54dbf5733d7d01d81c97a37da041437d238265340cc178bd37255ae7b7bc9e19b57b37044eec48a85edd1b38de4ace5722ed91940044c50446e7324fbcf8db1a9510d96ceaf56bbd91c301583de243d8ca27e4bc63256ad39eab717fecd5dba1974b9547dc22adce9308f32f1c1883736b608d6b6a8667916179ea3b90ff79fa4b0fd946c15f070af3613465f6e2bffb4a794716d832e38cb0e9a9569fe2b84c2b79125b3712b3c36ddaa3a18737d10d84a04365aae98f4a6090b7fffd689400653575c3f5b5c24521bb4a78eacc80beed4c51de2de73b17d0a4138ab5b8ddfdfedaccfbc3394766fb03b66e9e52e972af28b9ea8562a4188ac8b456fe03ed30fcb525ed5bf379d6fe4db0682e857f8dc27d70c7e050788a1cde02e6b33f6e549b39efbd72b84ead17cc2b184fb41f0b9a4524aa5523a3a81ad911cf4f92038114a3d062e9f87b6cf929a5012b8d22591fcf709bb5cbe79a44bb4b7a8d992e69db192914b75d8d65da62d463c9930535bd915aac7dea64e15cb5c4d5402268f926ae985bedb250e43849a5184009d8e3b61c41abcde3ef0cfb391ff59dc0485265aaf5236a75b06c0889e0737e6c29d3463e00bef157628a49cc04ae2d9f9765ed5d87fbdc9768ff984a3680e9eb3851bf5bd7dec0c15950a539438b0151382006c3532525c716f112859c5bacaaee02ae7b4d7a1ba4f6b199b9c084c426bcc1d94ba8d6d6d802fac0844f1f817530dc19b47fc1250f4a5a37c4777ac971804f24a4a293b318839a93dd8a071f93d8518e23008152a2ed722dcae901ebb82b2d0787b61ed696f64fdd893db844fbe78739e713e120aebc1bdc376916d005bd2ebb495dcfdcb91725cfc9713b54751e25ef9dae7828567899afb57dbe6bac3c325d91a973789300e0c2a7ae0a8e4e8da76210cfa5270c4bf73add72dbc19696faed6f81bc08d25948c372e672eff153f1b255978e6fe681fd5f3cd91c3cc49e8f817ad0ffbd92256ee7b827fc60bb31d246fb242fe8b85da3fbfebd4734aa86ecaed31b2b1a9d09c918f5dea6a86c5d845680ff93ac09", "6a65ac5152", 1, -1523284677, "a652bba3d14b4a700388c133727a872a2437481c4139a4d1794280ffd15b95af"], - ["94e756b601fd976058da034883dd970499e98483ca928387e379d81882933e27e7c6ac90090100000004ac51636affffffff021c73c7040000000007006565abab6500b24069020000000004ab00ac655b797bb9", "535252ac535263", 0, -717663808, "3a890b896816074a0bcf66112bddce4798c178f2fc893cf5d00453542908fe75"], - ["610e9f1302f8aac74170da6488397674fbcbc884ba16022194b51be977b2e42f32adde1ba40000000005ab655353630db007e0f4b0496a738deb2af3961ecf13b5171121ea1a7fb04ccb0557223678586d0d280000000009526aac5251ac52acac42533ddd02591b73010000000006acabac6a0051bc3e71040000000000bdc689180200000000000000007dd262050000000081b108b9ad8f35aa8ab2e8be05d20c680201ef8790e9f748afb1cb84b3aa3e4632e09cbbe65ab3d9f75e82d59bd9faf9b8c521265515c115e028634e72286adcb9527a7b21e0c78b3b200768e07bfc1005a4d140eb34f88c2fb8b1c0a793527600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fee1fddf1bf38445fd330d1356f88fd8840bbed58842f801160e9966fcf52d73538ac9a68009672a1de6153625f13db32e5b632eb93d90aca07bb77d446535195044558873aff21d7b176bd3d738dcce39f14f05c43a48d4977365905a3ea1a05ed35693d39f93edaf2204bfee017f1c6ee77a27cc50de9f668d092cd092c31003177b17ee383214acb0767c8b2ab77e5eb532cefc4f0fa1d0f794f071579998450319302605c8afffa27cfb16427b6a78845f666c8cae3e6fe7e4fc17c8275d71a60a1ebc8f7f7a40f0121366065c66de76c8a21555aa4f25c3f22df9b5096494b44b0ad3847545aa55875b0ce1f6f0571f43d1251924ac55d2f213e24a5f58fb23e2020f121c4c0c5b8435fb923ae46c3911e1e1dbfc60d2631190e767c45fbd9df33e03072548abf98acae12564f344a146167134788a14da5bad64e111fd4b8379a70f03268bf09d62318b5180a510614704593840f83bb7a1f7f72c08c0cae9e7ddb5580309005b85972c4439b2ba805df54c11ed503ae362242a6782eb7152e0e7645975022889dc0fdb484c8a769d2a42ad9c0c5d35453f8b9a0837bcdd8887253c6fe1433e619750dd553296efe8fd1bb4c1bf94599eadf524f6dcb237e1ae36a82d526f4f9e447cef7721d1a409c3eaf8c24f05701ce2912ee144bf98b92a215d451bbe1c7d9efa24993d9969848ca851cabd04400d27b19351655a134c7db4ff7cdadbcf5235663b0cae58fb6046a7f97b2f7b71839e9dc10b029e98a4a0e6c83afc43217964e09c2fd225d46fdf5d0b5fc628c542fedae0ab6978c059e644eefba12496614d24f45004cb89ee39f47fdca31f267f55e691d408d4b9dc425aab10e298d8617a64ef14936c9dbf019f03e68b36ca1271ee451f360d595543456d4489f26b4afe39d4e97e5e0f47468ea7d4a5aae8e757e19f95fda233ddbe1f2d245ccb1245a42ee8bf8756d06ccf29573ad703545970f7d5e457ddf8b42b24582028813e4a83a28309dba75dc14f1a24e2b1b05a058be0fd0ad2002a7dee7d93645065a95c153c984aa9ae1e303447d29a25a159f497c40784cb254b61be7b66754950e5b0aa30fbc6ea22bbb725be4bfb8c3d2cf20c010d4ef197b6c508e0da15087c9e1bd65d6d18432c81fda253b53ae425cad642fb3209e5ed57b4ba058340c56239608942b464c5f81bc9d2bc8a9ecc58ddafe198e3486214c8937fd58cceab8085c7957ba7258d7b196fef88f519f8ea78d933d04f798345d898a43520f60f9849cbe5ea234d3134cd760b2ebeacf36c9d8beb07730df1d6c28073b62e6f20d7fa8da48604384c6000c937324af3691bd632c701055ff727d49adf955623017f1efca0e8e34268062c32cf101d1c220b60ddd942561e2ba644fea9d13be350fd13ba779fcb31377c7783b2511e8c5983054d6610084bb616ad56d1a518685cc21cc27f133ef2b3013e496fdded2cbf2e125ac3d38368337f67ed7f955ff1ed7686f03bdbc33e7713f22c8919639d051ebb00900b42357b5ce830c26770a0939d197e8bb72fc1d552dba15eb75cfcd3595517b65eeeb08edba1dd49837485baab75008fdea4362d5c9b8bbee7f4edddf358349f8a9d20145bdad2c4c16e1de5da68fd9eeb8366ab6c98476b0f3c02258ede713ba8d2ac8a5cb0a14c080c7d2c3e2311569a29e4b9c452111897c9349dfa2e220fe3314500496f96992ce4e5ed0b7b23084015db74281608366d5b031f80403391f70518fd2a893164b6c89cb2686ff77b26d3578b1f50cb03a7f14014f4fb258416acba7b0f4616b1757b15be7711de9d76bda8a6823d6c6ac3d1858552482080d6b01d792fbb2d7e90f7d4c0d6b9966feda57c45e2e8588e57ed91188b85f982dd16399ef8a564c3ff8c5597c76820ab7de4931ad94f84ca38db7296c2df2d6c515d519d07dc1ad932a40f0a1d9e61ec4c0a53feada12cfbf6c1c38538ea7fabbfdd8d23a799086b474cba3145fb3fff56c0ead51cea3299b6a92c280e1348fed887c254a48f282026cd508da2ce0abb297ff4df443e8f7ee5a3ce6c5f6588b6cc9c6a4d5bc9b1a630526a9e97a87964828c288f8a9f50f3aa44dac43684f1b09370716e7f786b7265863702afe58f862042958590e304c6ac79e488581492a3ec9803bcfc498540114d5e3ce138e24fa0b67b17aad7be1d6f4760555f620680842cdaec49882fc5994c0a9f4e341c1a7cbc76d47c78013e81cefb3ce13260b09ef88285ac82bae0daafcfed50d1f08020edeadbc001771ba4cfde547c055c98b7d705000000000000000000000000e8b281a9860f7273cdd15f4675b20f13b7f9ca24a8b76e17e1c1e4b1155920240e83e49727f061e6b70a844d3c9c4f6df5aa7e8aba61e17603c89857fed92deb87452a2ad50337577f8663cf9a72ee9331923e7fb607e6dcfab8b90175719c9700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cbb440f81d4549e0309e46509ce4c3024c9c176663ba428483c13f284b116d6860cacac5032afda3e7d3c86bd15e653dcc5a038f78e65b0b55c7a5ad4bc57873203b3f1c4b4ee2c489666f67c8451c98ecf0dc042cda16bd9ed7d0031b035166cda77758665578410fa9d4786f3f774e0dcb8a7bc23182d190fe7c54a637eb3a031334fe8379a5dfcfe68ddd7dad9cb2e04f74387a45dd72f8946ceae0401b39e8030e907abbefc8a07b456342cf656d8fe949a74d148d081ead68370763cadf8e270a20761e02a061667e96b374904e023c5e70ae2d8219e176f5fb2b713f88e595c00cc3810a4c0d0cdf8b833f9f1a3ef6e027c629e8de3d3009eb38a26f1cc27724022cb1e36cb17190940c56f37f0e2a760b4c2b6e268ce11da77b8783436912b0f403179f4e5769e57a8333645f1d99c30ae8cfb563867fe762393da20f3433a56c2d030fb51df639562712157a165aeb7a3a77211ef263d70e7a116cfcb4dd9d2a87c40314bd6c07ba4590209398ea3f28e076005123d1277b86ba189bff76808bc3c540030af451ee78570489fdd72c39c4127252dcead075f284093776f5908ccdbab1d2d103fb217bf1f3511db971a6b153fe02275bb76ae0d1674f136c95badd05728651eb0b201236c8723d029356d3d56bf8d08543c47998726f1b716450813d18e0940455dfcacad2dcbf18302cb0e8efbaa7217dd0e923b411297541d8d4eea8a298e2e6021dcd46d2679b0fe7e38db81188b4242e1655c6db14335c95e18a90b05b5254aaebef462f58438811b6c11d291c038a853db7dc6ff6599d3770073c286bb9e3b7bf03d4412c2753072033723fb5dcbc2a2a3e20eebdf0c42b8b4bd7fad076f06c9b1ab1a3a909a787767ebe7747f15f55084ad87d7d7a12df98ec00fb450be3fd52959a92c915d4137475d28fb616724d48b688773712695164d1cae531fa831bcf7fcffa90f3732ef8b5164042dbde286ec9db0bc34537e91d5e1d11fcc3a5fee6b2b63c75c4a6459c367f06d4934bc9c850349ca0c68c2d967115c607e6de97cc8324dff65ad2151e0a9234c082c27f2debc4f0595f7ac38b4100e36ed6f7e5c1d4a337c0a2fc92976bf27d26dedc5175f07031331402bd25d8d24cc94415d3281b9e29648aeb2ee701e3574792f3ac51db3e550f3d1024d79d54569abaadcd33810cb56985d451261c1c061dd1352e5038b897e79c130a31f9bb494623becb727ac62e584b0991eba72a1595971fc2af17b6e882dfe833f15f8ffbe623dc99890137418614951529c6f93e02500f33fbde5706731dd540aaecb930cb8dd3b241656d31def4e57413f4c78f336d0b8e405b02f870681c37d35d5ef751891f814d00b11c871f925c1aa510e93c2256580462fad9e1cd42aa4db790d111368ba77627196310c7c5823b63136c141f64856c0fe178f6268d7c405f58784a151fce5083218a9ee53c7e4b5a60fc21c1f55ab5d27c7f1b5075f9bceab87185333b31008ecae651cd9e368781262fbabd0436fe1ac2d68daa603c125bd9cad5bb8f427cde404d62645915793d6138861148500770e618ab3212a6091436dcc2d4c549c7c06d279c310b1531328ed95b15bc3ca3e7f036203866fb51b7c0dee9a8980feaab5800b90a86dd11aef0d9ee3138315e6de38b2856f9ba76ff10288bf278ce72c8d0d6dc2868c3f3e53648303279ef4741a66bd881b2edddb4eae8eee5545fff832249b53008ac4126fa3873c615670080f76af6707c0819b3e832b4b957fb2e7499cd55ca291686068ae61c8b4808e4ab84552abe29d795870f8a61214934c71090e612a419b721c65256d2f1f54f7f91616342e6400ab70b1d39541b1707a07ef938cf3aa4cb04a564e910175982c9796b457bda71c2a012526626609e207cef453ccecc9e58e9a48face985aa2598b381e29bed84294da84d68eee0949546a7f9f088dde48439ddb1dd041c30156ac859afa6ad02c4fd42800ee3c82a058cdb6799a0d58a6c4caacb00f05f461827754144ce2f721497ee98815103d8f3d09807161b98281ebf371295863f51fb6424b4f0d6bf96d0d862d379606ea0266c47069d9f30ba5034236dc094d66d4faa7da5c17767967e4349dbd472ced5d7fa0d8dcc9bdcc07a23bbeab004ea8d7922f47474eb4333cd76c0a7dd38e739e88d584d8aa2590c44802628105a93decc243d44a3901cc3ed18bcc73101bd30732945be50e9f61cdea13f4f50953286f53e10f58359fe8a6b00a2982cb71b1676600f74f1c8e28acde2cd509661f7f2c66b10e39998f50ac9ba40b5cc35adc9de9ceee847303ccff3cba3fed252e4c78979ddf63d537a1a24a257cda66fe0d6b3ba75730236d59e69803d3305021cac9247d6c9e8086790e4f3021569a03ae7066704d1ba8b4607ead5b6c01e8500", "526aac516563", 0, 690020955, "1c972b28ed17bc256167bb0eb67ceba813075121fa935a981421bbae0495c4c2"], - ["8c245238023b85dd56547c8b04045672be3fe4867c299e2894597b962796089f0375a243f50300000005ac63ac5363ffffffffccd4f49704e65c38f39c6cdef108912032f201a5ee0f643af66a6ed53a3aa64e0100000006006a655253658eea69b8019694190100000000065152ac52636500000000020000000000000000b5b2de05000000004b70f3b2a3489a26b7c3e35c71b41227d16cba9a59b611fd3330bde0616e976d766ec0a35179b80c445677178104a8c80b92dbb72edba771490a2c18f21df0d6d3f572031ce191ba44e034a197e129cabacec5d0353323f4da89c82ef71c24240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036aefd592f85b834e8d380350ea8ffb44d6a2dacb72707b053fb53d583e1a7c9dcb24baaf64000135692fa454aa24a866654f47e5c657fd8475b1d688d1137e04ee0da95a5cffe3c60fa24407d6bd2ca6c6d86aadce126ce3dbacde9997e543feba7054606609714a9a263f6120243f45adba67b94a9277fb188273dc9bf4d3a022277d2e531f51596325c66813afa2fb533c35574b9f1143036edd32b3166ee00022555e6c179707665cda11ccb3591442e6c21a1d995c610b311a58a0a8f82c4ad0b0640fd3538fa9540516f88e8c1b0d72af5a6b1d4c57b100dd44c536df729416008e1efda1fa80d44e53a8b0aa4e7e6711071063e61da92626efc565b26fdac44031c4c2d4bf1617e214da09eabb869ae266c02965a160049dfd370495057aaac020309f6ed0a3127c1c3682dce976dea4ffede0865502fefe61db618b60647949200022f895785803f68f464a8a01cbc82804bb99fc0c162f4e79556de659c17205bad032792f62f5c8f55e2acb915a81c287114951b274b22a4b2225a5bc7d1442bc3c1022b96cf7623c2c914ef94cff2c02cd3a5b421ccd17fd2f7f35386a3ee869538413faf62d5e6fc51c0d867fba42f6dfdc2131e649f0c6b01cd363cdc35f56370f039f5f1832c1653002ab7ced29a4b699a5363af66f23a00f681597bef04141992f14e7f41e2b6fdebdda8dc8b9e8aa7fcbcb973128a25102c7bfd0123f5b8a5c8c239ad6f59c5f51febd81a838576f6f11aea9c0ab3d7b155d36f8ef0c403760809d650438f760a37e55b1cf22de8ef6ae43f2affbbccfd7fe6febfa08f5944f864570b703dd10950bf801376a0cbe1933d30ad03f9f3758e47e948b4269e5ac153f90e28d5f80fd36e05fd88a20609ef751cdad4fc44f401b9660fd7dee8c51e1c43420507dce9c70e9378afdd99afe0d7aadfc36d964751b4cb1708289bf7c1f336452c8f74d8a2d9e5e22db0229febc819357f38c94a3472a2048ccb805bf89c27f8cac214e2feae4a8e817ba188df08eeac94980faa3a27f57d15759379f07fd98ac0832cf44214e26c061843730531767417b8605848e228f9e00df17c25aee32acc88e5c9af5f0d43a8e91424290d50c50b521565f0f4b1c5195cc951b9f3a295251c66c181d78df2114efc7a94c5489da353992e44db5237c29dc8740a10487c44bfc69ca4bacc991890dc4928841b660a1060e1007e0083e7124da5b2972bdf1df082385b0796013ac3cc85ccc8b57a86dccd6d3ba900b8e9b8d18e597477a0282747579f0d71a5a6524dc142a11e4e81f18f38788956c52e2eb584e02d01b13a142de5c09b2074cae574f5692db25aa43a3cb4a8b66603ea943c0cd53093a329cd4bf39f117de3c46bb74f867c097e91cc0d1aec7df6b1bd56ffad831e9c25d2ded963d9e36e6399973f024a760b847b65a9dd8eb50bcdd739181f0351ebbbdc53698fa7bb26b4b117d7b6d34a5b0ddb14ce51cbcca0e923005bfbaf11d96a08be0bed36ca31fbc1d52f7fe2f3285344f587134caf8cf3b92cd3996393f17f0d112770a36727824ae5c571402dcc9b8c1fbc5dfb57ae639e766b5d1b2f8e219b390793cacd15eef105decc539c00289150b5276991b2349deee2efed440a82cf8e1de3c8f895a1043f899c20132cea9adb1bd92f3e43222c8acd881ab92ec3a0290659478dfeeb81acfb48c855fe9282029103a592d0944efcb93f01af71f4b925feefe9c289ef92e1fc8a939d83405dd06424f1c6e076980319a3f9021c8cb4aef01cc3cb155f523328f4d262f175256fc8ed0846ef87f8256b1f155eaf02bf257e967f3ea94f36ef3abd91b4f076323bb812de6295b0eb3c3b20c5b490e7ad12465fedcbcaa0308475719a86e4fbb4a53bf9073020d05e5c060ec862bc3cccd2405c6b4f4cc78a78b76fb1a7b281aaf164fd6d535ba5e7deefc121929e4525ae0b9f226d16d57741df0ec0ab3840473fc01acc252c8ad55c2f572c36c7832e06987ab27d70e68fcb174a807fae6c577ff6190f0f8096f46b5a6e47b160a7af14c1466556a6fb4a2950e52d695ce3ba67cf4857421d17eac44dbfabd259694fe2313164d2b801b9dd583219670284ba7162c8d11e16e9d2b391cf567a7f4a6eb8a581e7df63041eb0a0c8e48585d615f543e6f6623a8b675a99a6716c0124775b7377fdb497bbc5ccc82225ea390457d5f5d665264a2294747c4e9ce2972b68b6bbdd339792b47b2ba660d3488b3399dc566859e40754a964d8407f76ee63b7e98b1c37ac11f347cd4c4bc19b0ad1f874020000000000000000000000001e9a5f950d18ef7eada2e9a5113f26a2d0346a7592a902d9e41d0075b8e300b82a218c7ca863781d82fda0da040c036e53eb26835858e5e5d2563542ceb67ee806410ddf0a57c7e9d8bd5171a797111119c28a7bcfda276ec26992bbcd4c617700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b665aa6d5a0ea7ec8831fbaef6d68067caa7f37f721f701226c84b061b78859a020234cbf3226c17bf663861197c979fbd8db9a17059bd397d1f2ae01b02c772620e3ca8790c5172ca75f90b70f3df7b7c79929071fc16c878228da167f5b3b4dbe8bb3dc4fae068d45a954cd4206a8cd44818b3e41c4e83785632d42cff3655021f809eddcce8fb102a2432fcfe021d104bf94aa68143058fb0b6ed52dc1224b9020d248df5acb65923947b02fb94056c8b500d42339d802c85d233659a5b646c070b158931b4d000e5ae2a83e34d47485572fc558a9fa0e80cd0d34441d867fdfc9b29580725b84583e469ba08409e6d6aa57eb42c7b1826fe3044657527cf03bb6c021f9bfba19e7808021fa0fc831a04ac0d71b9a81b916bcf784a437b3b99aaa1130227f3999117a4b5ff21eaa946c1773996c15907f9a76784f4cbaaeaf21b40cf08022148249301aa7792a3deb4e1af2d7d06c51a89fcad3c307f46710b1587e35509022df8d139f9b016c72c3e0c1d274d458671432c74e3b9f72c4c0027df722aa8e1020a9133435be80ee6f26dbbd2759838316f1fd8591368372cb079d6da6dde3dbfa0bcf4753d64a2035c3370ff87cf6eca46838cfc93d916c5a5bedec9545602e7bdc96700e467abbddc34b29418a75f31aec09eab56ed1ce3f635002c3a0aff418d7749f9d2aeb90836e5d7616dc38aa0ffe615a618d02fa4fb3cc0c0fc827fc2104026bcf25b0eddf8c204285bfd524076bc9549e96efa3e010eee57a211455a99d1798aac35e52acab0c11d9929afd052ca30f612eac9a73f3349d0326971e45830dfcfdb5145c78cb4998589456121779524ac609643f3039e182aee32f074ea24e98d26c49d1740e875f10bf291965cd51102600e3b4893a6aaddb802956b862f40754121796e4bba8d35b48c38935dabb975082fd11d6732dfdab2ac0b43901bc7a3f389f6d2cb0f0cb8fedeb1be55d82906e863025b3119a2c0eca44d54a2638ffef8a4a5358f6bc27ccca946f77c49807e4245dfc442b8cb336881b4f11980578396d0ea6b5b8b08b3e4b88ea57a26be679d83617db5c3de4acbd034dc32ed5746ff77cf6b1c8bb1b6721b47f6b32d41157835f7b033a7bf6ba17d3286b2e444c8b4d938aa3710c45fc5c32642fd0c5ccd73edaf8cdadfcdebdb52912a9c5d6d7f44a720fe9179b4908f6f44f319f1a9695816cfc6bffd838fc2f67aa52e84eb6f86fd21956c1bc943219b18aee8eb21999554573fc97cea18cf507b6a8aab8d193d3540c486babd264d144c3924e2e6523756466a418c19de381e368f5429ed35f6498559cfd3ac730304fc63935f0c507540bb72bd9628fe10afb258230f329ee8880db1e3ccbc45458e846cae164f80ab536d60ee19343c7c79a0bcf2bf47d57a12918d3777c698b49ed438331db2eb0d1c7d0b5957cd802e7aab227d68a81bec0b6ec31dd31d217db9275257afa0c954f8d913e0c81a3c4b9aa83cec551be8fba4ce797324b3cd2437d54dd0820b62eac1eb03421ad667657d018ac29680970494551b154e054fe757dd256396df15de2c3ce46e123c48c8c42ed57fca565c8b862bc0239b27519a54c78dedb6b64bc262afe62b1adbbc87211922fb25948d26521307b5ae0b6fac1b227d63cebd5edb66cd9fb232f3ca4b57ca72925377fe3b8f8a869d8cff703c646504f37d03ab3793690308c1b2c8f59fe79b84b975b243d2cd62dcdae3a90c19ea33eda7f6e1f27701dc27d5367646e6e3dc6cd4154ccbd0af28b21279d667002b8f6b2d35d9c4f92e658d17617c3c595c9765d208f0a183cd238c3f54e8e5630eb337c9f8e563b489f353064cb6a2bb3aecb1988e86b63099fdbf2dbb84599f6efd8918928eacb8e5edc3e79beb82aee9176733e8e72298afb29ddedc2a518e409ef3d7f8ae04459052eae14c7f0b6e8dcb18d62aa1b63be51f8845a90fb49e4607006ddfc0ab551dfd08797f168cf3be664c183429400fcf44cbf32696c69654815a18dcfe8f950f179f408d1e3c6f89bedb6067dcf0513aa65bab314b790b6a223e2ef8f141ca9e3ab6842c42cc77e3008f1776a8bf4d88e92006ccdaead15f1a4be23f538dffed693199bbf32eec21a03d7ff67612589713808d245e42d010236efdeb03b8d04724d135a121964511c076a7db2bb69245023e05ddfd92d4bfa3fb3cb82cc49273e7a78c17e7d31221600e1b093aa9a8dcb12f8f796726d3da2c72d0bab8b3d07164ac70b44d99440c285c20159d902b6e2bceb676b1e0b26b3b84030fecbf1ba9c70ce04058a63bbed23dd4b8b8a29ed019b8f07982b257691bf7e118894a9e2555cd430c73d29077c49a4cd94d4f67e3c1a9f6c2a90e4879658a5aca1532ce9fd779d2e522e17db9318d404fa0f3bb21c45f3234bb86dddda4f505", "0000", 0, -926080502, "e8d36d92f19253e9351bb522511852c54c190fd186da53729378b2f678515e7c"], - ["858fc233033ca627c2c1d40767e385baddf648c30f49d536bf213606dd63240004991b50ee03000000025351ffffffff78fe020d977077e998fc9b98770b059c905e914d4e4d7610726a16b9d447ac8f0300000004ac52ab52ffffffff14e4d519c04e8e4b31d426884e39a9d172971c271eb6ffed86b19604758feb630200000004ab53ab63ffffffff016e9757020000000001acb3229a0303176a2500000000000000000000000000941e0fb5560a19d6ec8114be286264d9bc1d6ff29bfccaba358e3d1212faa4e3cea01013c0472e09d2addaf30e24d32554d425191d0cbcf9cdff319532bfb0435855af96e3b7de651d423bd1cf669894c8cdb6ac475ffc3893dd3d930d67719f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a562b4f8e5d15cc40946f958c765d0b7787e07a1b9f295d70d0da0b07d1d6e68e43aee7b5161cfcac87f10619602a6128189bbea897c9e36081302395679c601a556754feca5047e2f042b0b9dc867f4f4c10d1407f0c30e4ac6b4696c099c2427e08970f77f8580b2f2a0e3480711977bc94f6a0a08f9a123cbae819bb9870003107bba9dc852eb77c71a164a35c13ad7b99ec630e2d90712f89e727759fa5e35031c77cc37865f4c2e4e819170044ffcd69e320f51a00f042fd813fc0d52c54bda0a28616ac3f7b0375711035069cb289adddcc6396a6f9d424806ea62f32797530104e2e85ca57fe91d8e2f0cc2a606c7567010e8edebc68c7d5fc40a380512a19903280f66c915e38a130dd921f333a937ad47cdd45150e69f40813079e23c56c59003118423a55f4f01396dc29d5210d01a6861206e334cbe01506aa0e8de35f24be2030933903afaceeb82a9362dac1394f7a40b49a4bd55e8e45636089943bb933aff03148b958a1322729538b4778c65281c895d7af5cdf9a31f2a927fb76fc103a3af021ecb51e1b8d9cde1719fd6c1ffd62fb298c08586420b94b4689e6651e733332d406499067112411cb1945389522e12743a8c802abf0a1786fc3ef470d6de2800667b199f424ebecffdea25dd3af7dafea8223ea8d7250b39751cb1ac7eba026f0e0cf07e7ec9a5fde28206d8ef9f099bc16a8dc872a90d08460acb795402b93898cd87a033c4e5311b2de65e471b442a71f18fadda378b0408ec7011f227a639e9d0b4125f46bc125a5e8ba3de1e922642348bc8f9328125699623c9fb7b8102340d18f95970be9a092c24a71ce43250478166cc2ac14da96893d1d86db66af75cbc2803aab5a7797e033512709d1563a0b668ccfdbb0bf73c2db2ac3d6b8d6224aa2a8a70f4439bd31054ab0c32cdb17a499bbc259b8676904e17d4d8824be979594a9dd779ac4dac090d6b3ab79da67f54401efe3ad726e021bd32b063a36dba0e8e3e017d4a83b852d558e3c6f2825da3392afab26b60bcfaa044ecaad1d584993f31c192b7cb7679e77dadcfa1589118e17b2dd5f63e95f9eed91d9d9ef94dbbbafc8ec3701fbdf8cc5f1e5941a393199aac7ab10c981561131aaa228f4347bcebbcfb90f651c5af1cf6ee047c19e34381c149b2268148448f791ab650f097941ac02e723394761b9d03108953486740e5863353ac78a951f3704767bc385b4cfb82245ecef374807f18994a636bdcba4761c0bae54c22b943b24c06e5fc8e98d5b6f9ccce18132110e4934213f54a5cbe5e9c368dfdd825bfcab4785d9fec7350d0d092303e611325973aec8d2f0f27b82855854e8b51f410c016d18104d4d9539e9fb086a8138d15fa008adb1dea32f202058228884737c8025bb4e0db9377fb62bf61c8f138b086784d0dc77ae6a55c921d56cc3c2eb192b0fa23fb5cf7dafb944f8f5c203463c4020543f0e39d832ab986f1b7a8b9144e0d788e5eb3a309e5280fe3b113f10230e30e06624f9a52a0725ff94c0946cc870215b12d577b666bacdcc9be4ca227c116ee140283fc1a85b43f40685295e5f8343ef12b0cbfd321d831640ad0ad3b6cf34fd47cdf8ff1851525c44c140b37185268961c04f4fed619963bf958059ffb28aa633cd08ba0fa5f4f97f90e15bf8d73b22192a7ed78e58d5809208c60b036b59226339505f942fb7b2f18e0d319bb2ba7eaa4191948a3de01d55dde215a9354122d26df1371edbc5a8c5d3fc14f3fc51a89c855826e1651e61708a263f6474819e769c9f7ca29c94aedfd501e4c77f80808e05ff934dfc04cecb3ab769db271df429fed088061a8355c376c492571886820af42abc3d5743be2d4168bf5bf2a1ab62228402761f4a5ce0d41c5e171579197aefd92cfeebf81ef288f2f2e789d2c4b106c6ce584fa3474f12b0256203841729c82d6fac5e61921f7a1b2270e4ed5674b4ce7de0d428772f99c688551af2c7bad00d0e348ed2cb0de9929957b250b11c2e78a678cbefac57b17966f48043aef3f5bd3df29e34b24306fc85d45904024d1644e5b8556560324161e123bd3c9ddda7375e43cec6e435ec1b9aedfa52b0d7e3ce0f2f41c49854662f42e924e168d89a74fef004b67f2c6de92ef928fb7256aa5f03894837dc15d4d1843021de68882041b05a6879a09489250b118b05e0d8e3bb42c92c78c9ef40b16085f114efea59d470fbe805aede9a2a08ac876f1c5cf257b914c0dd1c6df4c291b187acb5c8da34d6642887e9553b5a11efc31769fb1d700ec08def500000000000000000000000000f611f27cc1f995e631538f345d547847884c9aac383e26c8f258e6ce9c64405a376c8bef9df1960addc28b62dad3c4b3ea4c2f01896412893b8671b85d86c41aaa45e8731686488029ae040ca41dc7155e0935b6a04128a8b70f62875980815500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f21ce9f73a7bfe2719cba1bdde65c74c864ea758a201bdb68748fca3b8d290943e6e7a39c8a06e9b778937534a19d63fa1d9c705b38e4cba057d018580d753b0b909658db44bbe0839d5a1bd6e8aed920fe65eedd4eda212eeeda1c0a95cdbacee7063797c5683d0432941ad49a5681ab3b27936c135ffe47b6315b7b8ce7d7603146a4a6ad3c22507af59282a172e0eddc5c5f210e304d6e12c515553e7ddf7e903030cfb94843b1495492ea8a6a4779c5c2179b92601e90faf24d644f9ba16078e0a15df6c5d2db6a538759c47852e41285a6f81404568e51941438c4acad45e0d161a6783da1dacb6df99c60c407a9ff0481f4266ee6efa9cbbf0422fd6f1f53926032261f17ab892f149e787af9528bb548a96f02a9ca59ccc69d025d92f7feca0ef0225aaae370e6cff2f0b5f40d5dd569a553e0db719cf20bbcee19d31a5e864236a0211bd2f6b9bab5cb423766b5218017966b7411c2645d0d70ea6c4bafcf1338a80021bfae25d4dcaec161d8921ecb33e1dcc249ec9eed2fb7c4d93a4ada2fd697acc020dcdb0fb1d84cf299fed68499924f593d26d1e78e395c0ad1840d7949b84ffbdc15fa0430a81141df3e7402d9f739343f4d8d4a70c277a73acd954e707dc2221b522c2caafdbb25c188be063d9ed600aab5f35bcc29983bb8b76b736f04705cdce3aa51bf28f89134dacad03b565ac903fc4aed77c8f7f0f08d2cd221039a606a5f823ab498293b33c5fd28da7b98195c29d34da7cafc3eb3a49d6e04586bf3e921b7747d57b49dc0c9e39e180f879504b4bffaaabbbca8930dd36d56674551d9f53cfbbdc95c18eb5013b936135353547f21c7691fdc2ad89edd8d8d62424802f4b539d099e77e822c1d2bed7a45e6ec4c3c5bc644111f555039348cce363ca83fd0552f00a0d574870c50756e57e69bc6facd8fca6e74c3d50cb539a8b0577e480df2284a0264529830936f9d6fce34ac0043b2961d366ff0d5b72ad9dd60625ffd619e1b5cc255165e6f3593b555770b2a37702a3ba1d810b75288de65d069dff4e0581408edf2666a9ef7199e4efc77c17b2c5616de0e4dddb3fc36c06bc7407c858ba4812e1ef83ba1a22d95f4bc626c435783ab2f90f2377f94d3f7f2aab6e46af21829e5202dad5d4f6e3b0332ecce7cc43902522a0e799af48aab08693c50938501bcd427c13830b102e1c572f16494774368d22ad83591eef8fd568203ba5197c3bf5dc8c3869da8006d2e77cad6d782e55320ff842160cfa62e993955fb6f68c4eaca3575a5105a89772b641244abb958e5d1536733f2e5709820794ecc4578db9a390d98f60bdc1a4477392cabf65a9f54610f54c0810656104f963612771096886f0f651006e539b9eea9c86f5a7157cee195058ba3ac0b6e51975448701af0e0aef0ec08ee74a4d292735c4983e0023f282faac0b77541d23918abf1c628214bdc33fe01d5c975f21fe42c62aeed80af365d508c79417ec103b3c5abe24a5ac1d5c5186c631af3e237f96e2bfe1e92060d92ebb4b6d54df964e786b272f2d6b54b1a2fb306bf768be11c4158c8b0152ce37a8d45db5c75afc569f95844dc895dfe74f54b9b0bb813634b779dc972d83c46d6bee822284c017a8cf8180ff39ac4dfeea24c3242adca0a4ce8a0765d879f6f41e217e8ce3eef386a89c0b82023ca3f1e936efd737a0ccb1f44f69f0e8bd43c7e26f41b10f9d90a1fe6d57da8214dfe598812900f69a506bd90509fb935e8369e2bfbe2f06753ca01e92b45001101ba151de4683b40bc4f73e1c2adbdee8818604a19222f5f5e55589e8b5088d4fdb26d636792d915f5f927ccb2a6c3ef4d7e9cde4c9f4af9c69b547d86857c8590411783be87674759e5642805ae9b34e3e1a61b397f71330cba0be8e267f76f170476f99aaa6e493ccb6bdeff4f67047ae1199099b5f53e0d21b5553800c0e67f2b92713fc94a6033a37bfe400264176ab072426877b1d447dfcd50484321179e93722be552a6b89562a5cafc81b6906d64187f8718bfedf32c064263f8aafb13ed0d71bb3030aa92a124230ce0c2936114d75811197318f3f8d31ad7bc7578f91afc5a5d2443bf6ed6c25e4f5ed0e8d8ec0f5f27d1b381c25d50d548b61d967074109aee13a216da149753be684318580644add363386a158f93e3231db540a6cf18af66a32baf17dc0a3acbf38725f2778728fad9d3c51eb8f8d30075f1fc2f4928207babd9326214474ad433c17819c48531d34f818fbe8ac0b4fecd467d6a1c8a42550954754427bcf46e87fa8010000000000000000000000009e85966989d8af5abd41862b4f560e42d57ccff7550f5ec3c43bad0cf9cc5c04f40734cf9949348016c97fd129cee02614bb23eead99ebaefe96bc781bbab5be9c4651c387a5322006bb24dbaed0552ed0a74a9cc6979a1e9655c5b672c97c41000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004c42388b54195117ba1fe35b004594ac0b54478e56b125d00eb48fbe3f1d045d381f4b2a7ff1f8b9623bdb7cb8cadd6c193182811d717b520ddc4cc5e4d403a6bbf3d8306d131e279ac6378ce115397a742aaf19ff8b68edc8f384902638e00443d4023df9e00473bbd4347856e46cba09595eb0cdc5176dbfc81c1396d298dd02154daaf02db6951a58545845d91240e1c0f309d53db32ab614fa9e34ec9c83e2021bae843bb89a1a36d195a027308c68dedb3bea00a0c9430c5f3e03fbc6ed34fd0a04314ed0a7da2917e12a35c3ef047c3ee8da013f44e5158d44d2c92e80f8d38716330167ffaf7de2330a5f057aa98044c18fb717ebc6c526ae0cab8f95e768c703214cdd90b7dbdf8351c5a28ac04eceeee27d3d2458aff34edb8dbe47a23685950323d6ca23eaacea4799ce8900ac0fb98e97ba3d45a48d2b3d6368f78814574c9a03052d1835446ba7259e8c11633322b5eecfe78f3f375737dc1b50e2a0c5f90d94031accbcaa430200e94a0cf88b5d15a193393946bb050b0629985163ce4ae3b78d032bb1953bfdbe280bbf4774649e7a4d556ae328a38070dd960de0b0151e6540bc2b0f29d0715f724828083b3fd6f35d0596ef83df24a7ad63c2f6ba42223523e4ede248d34144e4636f9ed7b31d753c9daa351a38b32a3ff63cd27f04dfe9955de67901b339b5d3a4a3dee363b4b79102e4455ee53df8da4ba623ce64f3db8a57c74f1f1370a6e1d2d3724e386aee947cd4588ed9a75db25e0f14943a77604e531fd3826ca4d4f85e71ea6b02f357d19291ddf72584f0598530d2e9a95fda4cf9361461072a26dd4257bfde27276b59e6cf4059377f386357b8f61a90d0c240307bfb40b3cd96c2e0cfa2a5285f22b5c6c3b814c83bae1a4faf522902b32bea7d8d77b2decec50fc4d468019c1fc681c885aaf47d13c86c0fc7ddbcb34b6b58206e172691b70b67e64b7d2906eb8f80d47888dfa69a1678d56098eae1a7d429d73fbc865753801ff3cea7ab399df84ecd13d833215f4608cccd4160b1b4e8328b429419c6b69346c52715066fe9914647fd04b72d89f9ca00f22fa365e9ab01499eddf50fe807a61a71ee3744582f6628ea3b537fa640a1d79b8b076dd9cfe13c55e115c34e6f261579c19d1c639670d1e84fe64026b01c183fa6c8264a76e40d722ec0cc7d2c1f847963404a4debc6eabdc23d0c03c5eb3e896e72156056e0401d9f99c1a977a52b39afd0fb00eca6f6651099afd71656602bae24a9d74e8e25d59cedd3d1d87d094836bdb98e8a26cc6ba9f0a8f8054863d92cbdb7cc2c4ca5db089ab0a511d6e3bf10b7c30a7fe021d6c04d1193b85fe1ed7dae25eb7d89dbafb2e25c29da42259f431eb667b642306d806b72712084ffcb87d5f0c71dac0b13a958dd371f401ee8d9f8021bfe6e321ac4b5d74f64470eee82264b836cf5672dfcd4f861d19c3a64b5128cadc480e95690d6f5aaa09fdaa372a423250adde7ba48e36b5b8ec6650512b3282fe5284942e597ce755e44babe743bdb8063c1b4028cc600df559e1e7c48d4a4aaed58522e1d17921ff59186657beaed7583751822e70ea66f89f5b83cb2119f81c3bbf0a07101959841d1114c228adbc82700a52c08ae16a8c6a847b87b2dfe8519697b92731ef2f3dbec12a15bda47d4008930b38e8cca015c84890c9220caf581d682836e54ec4333b56fa26c3004759e8eb1ec1fb677f7966eaf7cea57d8eefa77e2f71b3a00c282ee747d14d69e5994ec5772c40efa46727e01369b664a79104c4071c06796e00b8def3f54b605fba239631806d5e71b2027d74c0dff81b98bfb67c798c098685cf2de82cc2a66ca392373b31c466472ae98a8f3b946706ab5a1a93f11162a587a02643a109dddcb4ddef19f65f9783b8fc9237ae8e6dc4bda72a7c03f5a31aff7175cc39cf640c7ef1e8b772bcaf23542ab345b4e144d7a9d77286053d2a4fa372ea12137c4e91611611f66da2d3d82cf74b0e817e78bc99c01f1e627b1e96aa7a329c6306d4fb1576733145abab2bafca9557fdc7d54063d8d57476d5cc71c4d621a79f76c924693ff39c801ed66a586bc257471a7ec6da6fa92bfde12e81633762e17f29d896781f2a1cc026343ec56c9e4ce4f1655ae9e834989f301dc55f010c79b72b80d6caabe576cf8b0f7b77160a8f43f4969b926107cfbf0d47904a72e2b03400b16937e32d0603d9263c3210ec4a1054b06c846903e46ed4a987a95356cfe3debe4cb05645dcb9d33f91030e566cc42b4abcbb86fb30c58b90fe3a7e2b3f82f8ac13964be9d9fe9764c42bafd0e6b2bb7dc7ca8df14ee90e37ad5990bf8a8f67840f228a8ab01f3c5acea796e84106432b31f8db84452edf2309971d858347f41987aa2a8ac80008110884bc9e515cb89956c9c4aeec701", "006a63ac", 2, -1633900595, "5716eb5829a40cb433fc003a1bbc356ecec53c64c7684148b5138030a2beaf5f"], - ["83f79225020fc35fb09fa9e5207ee49f96fd6fb3b8d282bf0ae47e36278e99949639497ac200000000096a536a6300ab6a00520d030e5e1aae0a9a2249ae04f34844fa5512060503d4ae4236598810a59418a32ee8a66b030000000253539abc1bfc0429a5a8050000000003abac65ce118204000000000552536563514e1ed40100000000065263515363abb0f279020000000007ababab5263006a00000000030000000000000000720f0d0300000000f77130710f3c9dbe060339377d73de73974d0812e49d54115bcf667020349cae54e4add91a6409d85d20ac45d96bfe0d6a41f3525794d0d24e918ea59e23307702b062a622c6f038fb2697ceaeb4890927149bacaf6d3e222510d68f6c5eaa94000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005ac1847538483736211e62c6b52e24bdb8d417d4cb417d631588b97f03736e12152afe5978900f33556e72d46543a76bdccaed95d907fd028bf3793b875d2122c3c552d12fb95b097d3893465383bd682a4bb64891d9d0b8a1f11b63f4f62ecd7d8e13da2399d8872bb52297645200204f4d0a1a607fb544e0657da0f0b81e1c0212eb49fc4ce0672b07fd72503f665eee3c55effeff095473e59683a4b42adca203060931f337f6bd4b31e56f48f3757e25d0f71c55262ba0ecff8443ab879563570a0b8ebc0467456f7d642255c1e246a6178b2f34635232baafe56ebdde0e73c99b2450a51ec14e3c2afb29a9132efc39dc2ab409f7d965bf86e0a30b978e02b34d02184de582992b6dc644e93aa3fbbd76203c1dc03dc4c62dba03daf2d9adfd896f02002ec89c77ce02f8150a378a9cfa1c810440c634642d6807fcb23d5729633492022a5c5235b7f15e46775d8ec229f7b6c82a27cdc69543b903c757a46fdc2a3dbc021be619a59be2a177bf5c58ac8526e4b86eba0c643186fbddc56458ec83b936b0030fa816f9ff4d3dd471f256a7a730ca0745938c1afd5036f41ecb0dd10b121ddf078dccfc430b895254852a3396abe0229616cffd12bcfe8146af55fa60ac34443384486e7216b07adeaff17a8dc20b194033cb34511d22b238694ed086d0b6135dcb456d30ff02cece2a4e1341c7b72388d1c4e99726af2e5c4e9e0efa744e387b10df25b607bc8fabd4a72b12ca8edecada3d308319d0ee2ee58bf9eaf4554fc5346f640d3526b21088050c02a8ffc70a1a0d7afa2df92a33af1e4d9e2802c8b1a4439014dec9854a4d9f95fdd0f3aeb416cdb1f2a169a45203e3cdb631675bbc03fb1f4b9dad23114c31cdc136475902ac1113b775c0ffd0e96e210d9cc2ab813212fbefacc0eecde0cee3609cbc4a33268934ee7cf8436e3e1ff27d67cc8ced49a8b711953ac2e07bd6abb832d180616e87f40958aa9aa5f81162e8083c5c27abf4b3464fef75fcdaa447c1da8aa291a50e46405e8d2b5eeca8e8cdfafe21b1be50e47d2eb692bb981e0af2218e484e54f56ede23254053e819db5bed2e653b2896d9599e55d409bae1b539ddc3f84e0e1937161d74135f17b23a3d474f087b57137b7dcdcd5862a6effcf9bf813a805be52ba9e9e006807e51127876ced82c4d68ee4a7b8144fb1e5cf8889bc68ffcca5e208c91d4217c15735fbb67cbda1a7a330327d6b13b92f853de95d5445b799beca4a62e6c327df4cd1da052323456dbbc999918805c2dde203a9f29f8e561d5d9313195655b1cb7a24136a55c05164cfd6ac7bb45c1a04da857098c9a88429271d3c8c220e83da60683764aac91412ef9109111b24b7fafdd2ae74ee785f2cd4435e2ce8937d3d780ec19f3755d43e16a160fe88458edeaa401fb5cefe4d2857056e5e223a128909126b156f849772a2c8cbf2146e5deef9a142d49d175a9c98cc9d5c411474a0f8bb91970c57a1664a3e88e46f8e0ef5541c3994b021c2dcb02c1ef15bacde40209125287406578f4609083d34c4c4a98d9ebbbd97b8981846ebfa7dcff709e30725ee1f9c6fb3184930c0bf3125bf94a0ef4ae3c6869dc19a2be19878543bd56ff81348579862598a30b727b04375511642a65a486b08c54555043ffb3f3f65b0bebec601e4b6e7778f264bc062e7d2ae73284a243d85b0520b0a0765b917e771ba8dd1d6e91bb5326bb1ded294dfb7cc37e99b3414893ef0116ffc9d48ee70e1ffa306bc94621f3f909fa22e7d2b0247f5826ae9a37de001bb0dc8ea8f063b95994758a34a25125908a468801abe4221b0c7ce8d4627b375a068662ee4ee1343a73b540d378066dd2d10ca39a98c6bf7c1a534fc3b64a317fab3ae459057ce0cabd2d9258afb61e59e944c4c5468fa55768222b45604dfb42133d0c76e13865c8938a807c421cbbb3fccf9edf42995e16e000f403917b95d0f29d9c3e1cd8ed68644bd2f7655f68c01d5a178e021d5063c62decf51c99ea57aa443f14c2d53aaa73b88d0c8266d018b36f2f523c41db59fb1b342ca8b1fdc983e24701b2ebf41ffa76b96a3974dc1d0a0c70b1eba4c3a8ff01a78699ca165ab10503bc9f3c52d3cbe68d6c13b13cef27160104d910dfde91ba338a894a05842bc9027e4c842ec027581f680cdfc7e0511b0c151270539d58d1f592ea616db32a80c65d9baef8074bc8c2c6b815dfa589414c13d242842b9aad7ef0f8a4279342154be3eea29d0708cee66149b9e64c51f81d93393ae8e92ad420c531f4f407f7a00400000000000000000000000050fa76c248ad9b8fc6fe70e3635acc80ef665c6f38bf7aab39719b76aef79bcd098db99af66815ab876a3e611c2b4324273e21a6854d73cc13932232dc4ce3988252cf3a8712c36dd27774f596b89c470fec4ee85a0fba8182878ddb6f7d86ba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007cd402f152cc62cdc8abcb4d220de4cbe4175736469182f4ef817162f8b0ecb40f7d88a022052c8d1377c49a0ca675607e62476eda4620af03b2076ed2f6c6b9e5a7edf2b577250538919440f6a5130060be7db00912682f87202ecb2f8055fce09f06331567f0f00c83fcfc5cb68378e5c8702fa7d8b07cb441442e309bd2d0316e7c8a2014eda1e4abb3c977ae953d3b8df62c6fb3ce2612a4ccb6aada8688e031699aea6a3a2e9a236c85ed1dfbcfda87726f68a60cac560ba476b2d2c36871e0a0132f9c768a71b3a5638fd89f5b067ef68f8aede9f6771adb89eeab325b1d4411e5608ae17c49193ebc4c3d71842d5be8bad806b93db422b73a4e97ba2ac78a9021c604220f0dd1ff65def79a71555875fde5bfa1075d004bdc072e187637d7fcd0218c6609d6e220d1f9585e2f484deb61b118cf675a6089d30dec75aee5cf2fb48021e4a2116012b2007c7b9a830cb5a3629c77a1d6ba275a410fd1826adf5046277022d3b89ebc6d0872576d56b4bceffed0df1ba9bef29bd7fda02b5ae2cf51ed76a030ecc6c08ce3222cfff83940e6bdc8af59ba1d867a760aa384a20a03b546a315a1f786b37408de0782a068096088d43d7573b9112c6788e7d51e49a3a0dac903c26148fecc5bf5cd9358bb6f01593aaf8326f7f2c965b85bc1b3d234209505292d7d526f8d7717ad28897491ab8a2e192fb0ecbc492a57f96e67a732ef3977f6eee3f3f218c33430c85ebed98e46f0761d89c66c0128b7b3af2ed70c19203b276f8c8db0a7cb30fc3757550966378262e0864e5998bfc360b373c8c9786fd54e3b9e18660a024b34495fb35d075aec850a8a8d5e58e7088beab2050a3d9cce0ae1ea65575be7fb15573916dcfbf1def0f020fcd07dcbd38062c35184e29be301ea9b591a6caaffc5cd7e10c4eaae8596667ddb45acfd85e82648c876baf40ecfc221c130cf61d5f69e61d5dcd99768cccc23fdae1bbd078cddcdb077954aded2dedb30d41bd3e9d00fb53cb8633d52d88671f5de62b061100861ce1115132b51fa0bedfb00ee2d48b59984161fdcd4adb913a797ed58c1802f04c4bf08553f8d8a2d1cd1bd0091614e7214996cd452a25134f1619aecf0a9b5e619af3de5c6f49428c2a727a6b2711ce24e6002912986145e4fdeba81eefeb879db1c822e6db12c05c27aa7e5ec281c2f9971eadf4ba74b8228074bc19af2e17a3e779e10997faca340d8269fffb8b77d6e455e72f1eef323ae6f38d3d775e5e6e1f951a5679cd4fd6b6ff5c261d174d643466e6064d6ced7c223cabbf4cdd1664eeeaf2e1666e09b5a959389ed14aae60afe2b4f8454d88930552b03ea0d313b19b3f125e635dd6eda0e8c31ab29d4d2711a336f4758bb8220cac5fdbce62aeb28fc6e08f4398a79a5140b04034a5b5e9ffc32dd88da9cffb42d72547939738e609e19e169b0265e581a575947c9947e1fcc86bc9dfac54c4d9ae08f4a9e120d69214471349063155b41cf33225b87a1b8ca749a9de9a8a312405f655bbee105b661f2f5ce494b4967e0901d41ac2d117c44f86c32f6f586676f9f912e32c63cf57d42dee7102a238251b145c1f0a51618e18e3d382c06e1b557d62caed665a3e0cae8b278aea47d6b43046933579a3a67390cec68c4bad4457e0ec424e61afa064b5b5dfc740a6e1f6dce431eeeef7dbd65ab51f809ff5b1033b5aba7bf6a9626aa493747f3e588e479115d48fdd99fa6d4d275b845b12f4b0cc5277324a2c340a948303d1fe782320deaf47cbee1f2bd215167ce621871104f366a7f4f11a26ac524e666fe1efd1d8a6532a3e39b2a29b077d3ec2a599b5be0c607e7230ae2eb6ca7a2cd2768b61300ff22624f6edf6f8bf84ebe78e3d1b11ad3dd833ecf080f4e42e417cefa3350dcbdce5e78301cd5b0c8aa9d1cce9817b5327ec68ae4faec08d6e88bedcb523bb774773bd980657190d4d65a46ccb4b0e6de8513e7698ccbf7c1a2923183b704720abebca8eba5aae39e3018e204aad58ae7cd643b0694cdf2fce89f8e8bc1db01ac9da31c5ddefdbb6717c2423106b84f4d40b718cfe269ce85b93f6447e98e189771cb88ac8152ab8bcb43caf74487a2ce3ec330dfdb539a48df4181a0c27f0dc2666614024fdc04c660ae3c03b384858c7b2fb955d632dba29fc1a1ee589c22f6e4a50357925923ac8889ae2381a49088ab0e40aec31b85c644494f306bd7afbef2f4d4e72eb4893fd829741ffeb53788f456b613e932e30fe0f78b494d8d427cfc564afa82c22b6cf1699e5f6a2000000000000000054d6f30000000000b51bbea6afd8649f1b6da89520b2695c94681e22b64ca0398b90cad9470dbea9e2eeec914f4cb3cb38888d980d5ea3fa24fafdc73fe9f35466587f24397d1cc137533d61de3f61977c579efcec43a059fb23265dd77c17f1ab0833c1e15571850000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000019002b16552e219df7a3f588a1e35211b5aafdc0a2732f2de978b59ffc1eb50bee0d5de5cb4725e4bf64fe9533164bc3acc345995302768cb3347469be3b582586c53cc5e586a91a7fab937c417c1375a17bf3af3d00da0eff4b3e6010ad8b2e26a3643cf81484bdf0cb5134fbf0b57aa53313aee64e97caa9eb6b1e1e40250f032985a0b7980ad15a4d69ff2f3abf12049227e741c38719cdefb64f2e2d84b9af02005a090b0aaa30cdbbd32b15d50b17178f9e694d28c3bedd0ba79f09906021bb0a140f5548c626dcc9ef9c389aade96407a0713747a6df5a1e1eb3583a07ca956b297402a5d19199d11334c9d55892729eeb9b6d1e2fc3e3ba124a620ae33cc3a0020267facbf5eeaef3ae869501bb6154ac65eb2e94be83171e4d4652188683bbca0202fb9b6f43ec05763b6ff81815a8a90d28c94d659f403aeae3ac5dfe798147730222cf8b4522ff3d354a6653c90b6349dd0b1ce4b6074851e177c694f92264714702174457eacec109f0e3a2f9d6ab2d03a2ce2248a50d0c72550ace3afd0c3f646e031217ab6576043fa0c3651ead0fc8585e7d6ab538385d3bea46bbe29797fa4edf199631b56e257268224ce82b37e9fa08d8900b834f2d0d850907ba26ce1daf31a2cc50ee5974ac572868293714aef0de27d07a12ef4a4ffa8129b6782b996d699d1231a5d11784e67f24e51d23cb893a2cb8745a6e2eb821777381162fe138f40f2e8458bb0b267da8300f630a1331c20dffc750ce7d24e5c0319cbb6b0761b8b10a333c2ed05bbfe80e424f9f9688ed19f2380c4b7654b91975d32198c088a13830ef4b4bcc2a24ca988a118c4943b0e739e0fd13e199a56392fe5dd3fd78e2c0c3fd855eccea482b89f0f8bceb22432f661e8b5d438bf03c97b07e9457b7e27fc3948a6c153dc7f8cf5c0d4739243e254959c16d7d6abe73202f8bb6020cf06e37ce17914e5a7f04aed8dd021bac4a93712ef30de273cd0cb263b95218eabe7366643c9bfed5e4b2f6f15d4611c1272c8119e5edd7121fe687d711366131c77448991c5c8d8751457b78bc8c454b1fd3dfe3f69a644ac4a57f697337c3ad969e996eee6081ef1424beb1dd50185100318524b1649d8b9dee1f8ae43874e828727145f5f9196c844c6957da845baa5418ccaab3f940b0117f3df0ad1809eef3f6a57a64bb1e3b3d9578759b1194588258df5d1d8b5143c2beb3b032873b357ae57043b466cb05b7c272478f9e49c1d21bdd047ecd88bc92ab8eab24f15f715b3837694f295400857f4340222d9fbb997dbdbb49b17d725100dde3bc2de80040f0fe49a98a02e10bb439b3e48d681d972318a06545fe31d9cd5a935d26e5328c9e1850e1cd11af398b02e38931ada28786367ce4e9da793d6c49f93882ed730c884e40a2ee7399f3eb47586f9fe3defc81491d1ae5c2b7e70355e455df7142ee7d35ac1fe8cf8da02ae278397a9f351caa11b923c9b9ac68bfaaa0f9caca7d0c18184a2a611bb4adb6a98cbf30e7bb9f3442fef3346a6a37c186c3519f43a63a810b87bb5713f60cca0eed4d47d6c8fc0c4a0181230b04a5be5e97db49d72a39d57bf6a369f8df829241d202c80d96d97949de4272ec4f94840f94e6b5f77ce5f9fed10b26f8988bc12ef89482867c190a932d593db1e5bc182b985190d1f7bb741ca8479e9a927566d5e95d941eab9d54cb55af3b175d2d3fd012b8127243582f22888d679e0d4619883fe8807425700a1624c97eb313ce27adec83f5c4dc07c87757f1771d78d90eda3a93ec8c4ee0bf293b81e2d990a42e2a1205356860c98e1ceff2fa49d7f16fa16bb76c859c05826a525e1acff240c5ab4a2582e11b0888b58a9c3b46753d31ddd7d6c3c422cbaf2ba9debc51bcd26988d51c1387e8289b10c88b734c0d4637c596bac28ab5a836d15a0c36c69cf18191d2310e0e9f21b75456e3c702efffea5cdb0a22a982ddfd88e35409ddcd9955d4f00e251784097271b403ee9064752ca359d0efe4e2cc11f958642f678e73ca7d07877fb6143b1563f218ab73f2b3be14a2acb13d249dbd31dd8a0a84a07149f50ccf3c0791975a52ad1c75d9c8ebeb6b7b60ad111c87927b45a5242ca8aa4e6d9c679944ea9a30552eab935f55dc2edf1457a34532f5c7791bcc566b1dac4aca258b0e98bf06003191815e08ae2eb9357d0adfd1f9e01cdbbcb48635d2afad4027cb3e94ea4f178f5acc6238aa871dd609dcc155f54ea8621e672be78d9735441b703e78b5d2825f737b1becdd54811c3bb0b5d350098a2328cb91d1f1beb1307bde18ff6f8dcbcc3fdc18519d51e8815797489d445638460af48a5f105725e90969e3dbf5ca41fc07ec47a398c1431bf52abe1df39761ad426ef738465c78d44e7939f3a2aa4a0b98099834f7ff49519d0e432d3750ea0c", "63", 1, -645320676, "a8c5132e7bb19250f74c4e1a1f40c9ababad51df8a9624d59268cac9e8af9bc7"], - ["c137b74f03c654f9773c1e2b2c3cc1dfcdee992b9ef59fd91e7f931c207d6b1b2cbfd3b7dc0200000007636a6a53515163ffffffffee6ddd63193b6ef3c2922b7302e4a0e9ad37a6362d225b44c421e8742712e91f02000000086351ab52ac6a65658f3ece963f6aee77ee413c848788ca41bf021379f2a328dbd9edc98166624fa06906eccc000000000865ac5352515351513400126204990f5a00000000000800ab635200525351bbe831040000000005ab51520053d6a79a050000000008ab6a6353656500ac3fad2d0300000000036a5163c2430f2c00", "6363516551", 0, 200675681, "ff929a188efa75c2dea4369b26f03325cc3424889b88806ca7ca96494c0286bb"], - ["375cf69c035d9eaaaf9319b3828790371b66b32c87dc5d552785763eb636f6ee7377bdc91503000000086a53ab6a6a6aab6affffffff012a7e43f342508cbb049d45a7464a4fc6f38c753de1977c59e04de1a34cadb80200000000ffffffff00368517c490a0b80c4bdc84caad3646823ba109b0af8a8164cbd70b60246836030000000800006a5153656aacbce599960408de59010000000006ab51ababac5263d8b2010000000007526a52ababab6a9897e204000000000500acab6a65383ce10000000000010000000000", "00ac0000", 2, -1351539974, "ebc543ce646e52c153f106160e0864344195b5a6e5e53826334cf71870e0d218"], - ["d0c482aa0408a9da36d79a7590b110464bc309b1ce492fa915a40422d62f72b136d2d0ca89000000000753ab00ac5252521e9dd1fcb2d94aa944b565ba6aa77ce1137d7834fb7ed99c3893052f809a0c01b5d2d42701000000025165799ddd7dd27b42043c4c6d41d5b6113a471712f98c6d817860e3565fea814216362539140300000000ffffffff85b27f4ec728de8081554b3ca6f4b94d76c4b749c6cfc1ae39c933d48a78a09102000000026a532ac3e62901f9dcda04000000000000000000", "63", 3, -1506398402, "39fc0281befc99b730774e7a32d41025fa46f24015a833f5cd0cf37cd910372b"], - ["7c6b0c00013689d3e55feb133bc0b7117346475668431f444f6f2425aa50cc1201741c1f220300000006006aac635353ffffffff030723fa04000000000600ab52525265e93c53040000000002535220c66a020000000005ab6a656a5172eeacc301d8218701000000000000000000000000444b0e625e8f32625a61a2cf67e8df6b11138229228c77155a73da0349e4ff9ade2d976b0ff75dd6d95133379b7fd8378f582eb390de278314769aff032fda037ccf490c3532fb9658466f2b916afbafe938bace9b0210d90216031a9e13eecc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa9e41668af0cb147507a6c874e40b32d4ac2e770b71ac1dc605c5dfce4debe08a22a95cb07782f0cb9a8c9c2decf4ce456ce1e1d88b94fde6bc3db4d2a2dab9e0b3b778d0f900bf9a5d0b7e99ca797b3aa711b5d2e2f3e22c592696ed77e63d303076aba7f3ec753f00741294e911644fb730315e6c1db97d1148effd547e7003053434d3e3e5d007b07298d254c2a0285914dd8bf65d152289a11735b97deeab02219f61546a79ae96193639e7da7519b1fcf278185d5b7188d07f0f9fba5177df0a256e1eb58aef20e185a91b95b478507926e53ab039f1ba647248a661b08d3c6f06099fc0adc1a8e99a73e2efbeded2f2a798d34784a0de5cf98057d313994d160213764b19d74dfbc100e3b078ad14b2fa37917736914ff5f8c1012cf0472d7c9a0207229c4c9b27ff8bdd7d6748089241628ffb454753e9425102c41ea15001d79e031cb6f9b7320feff3a5a61c79ada587aec18d7bf89ecfc161bc2327542f2d58ba020af00d1d1e1b071c31cc802a9aef3b3f3a972c8a781745b3e711c435dea9b10a0211891a070170000bf0f705c37e72aaf4a16dea74cbe8dcdeaabedc9d03ab826b2125e139447d741320c14d7fdd9c3097fb1ebb8d5e7b8be0de094eb6650452319f5b831152f6f3f52fad061c024585b309e5ee4f2df34f1a09c0bf280eebad41b68b8a33da5556179c02908c18153517176ceb91c728242dc3bf413f90dcbaa302ee438c00f2a8834097bd20b2bcf0e7b43c7bf52ce7d83c281dcd6957b2e783a8ec0f8f14db4d13d85db3d37ba569d202a79986d470e58f788edbde68719e3bb006216f114ae2b7f4b98ea0260397d6322a732df57ad032072cd73d04d210f74aa06da14f68a1eb0d06aaa8a94f20dbf27787d71ad517bf3a3e6fb356fde8ecfcb2bddd9f146f6b2e89efeddaee7a1ade1f2590a8eb782170446a329ce33bc0071d43e953c84679ac8b371e1de43e8898fc061dfd8d98ce2b10166655f94f44a110feae99765f252c8c09695742ef3c905a291d992ca9c179e50177105cb69084d0f0098f93d8c059d9d84d8b39326c823c0f1a1365bce78ab2b8cebbab59d2eabbc9f2228d06f3d06a1b63b1448c1c0dd3b1fdc823c07ef426812b9fddf0f9de39ccc6a133d0c64d9a5d7c2f613baf03b7c693ecfa72208023bd8463bc8256f0b8f99d5314d0655b29a71ae2fb2a8ad8d1d9c9c76289bdbe8bf56343bce99d8ccdc75435dfe13d7f67615552609ff6bd3c9915f7c1ec999174410d3fd3b65ddb0defd028a20ac47e7d27c5c010abd3849a38bfc809ccf0beca45ca9852053d7cf138fc2c87609786cba1ec837abc92e8d04c7a7e2fdf15a00768ef32f7e9b80a5e27a18847eb6b09c7c217998951abb65a68f2f1ffa96381afe4ecc6c20a511d97053377bc145c09af39c66ebc74bdfe97a89b7ba64fa7a066db0c6b37a8f510236f29a10e0774f6c3b90217f32bdd985df928c5dcbda5f1ac19ad6d63b9dbc2b991e0e619d02ea3f3548debb6e75848fa0e00cc46eed946ce8dc8bf82f0ff676aa8384f6bc3f39551e7834bcb973f98e2bb6cccdc1744c2f1df9b11e2a1ae45566caab33b3627104d1b6fd281b1fbb5aebeae14639303cf1d40a782c4686d3920322bfa51147d450b27611cdd9f27d8a9acf5145fefc2881646858623dc827128a8978c2e543f4f309c090de65ec91c6f25ae92ed258cb0752ccb8256176704b3d482b216d7ec43b0a8776a7549feed2931909b9089f2998629729238bd68af80e3c2c88c0f323af56bf3c4d2b1efa1f0e6b3ef8617503a8f159d4f917d956024731f9b4677d0d5eeb9e8b7fd8e9e6143ce3567b47b79370027b74b237d264927d58b82ebad37207ca02ece242756e55b25b0d83be831a0ae06daac936ee4fcca56afddb9f5230d5b21e91e4d2d9ecb91b19f592d2411cb6c08c14738c3283f1c3872ef4d24f92ca33664f7ece4445895b41e785ea77b6607bbcd8fbedd021265541c717c97920bda17af39436c5b4b380029faf60db66f5ab37a08e76625c22824fb35f87caf91ee9166cf2e734100e217044ddace2290947ef53f9ff84ccff89b0e238131b13e0628ce96bf6a21b9fc7304d7ff739d867cb10b61f9fcee77373b610502abae21a6c6607629dd6cbd1258b42614b3b89bd6628b429538993fe5f9398f2f370e8ed27832af8dc4ba84ca216c59b4b348c185da7094ce7a5ccd6d8745d2b4a2b200e33a081b6cf049726bc9c4b3cabce0b3ef28901919f8ea537a7ad4323da2165648325b6658739cab548a8386c592eaa485a6bef28b9815ab13a885acedaf8b473c9d11439c735c2219e616fe812617adbd9e3e82781f31430cb8689b6ddbe85c2bd3931201c0a225548dc4216b02e7a389111cda5f6d2fc984a6fe3fcccc67070fae0c23a005737001", "5152516553", 0, -1490017737, "704e94177f5a311c53590bce2d1bd3df848b1200e471f3cf8eef58f0c3615f22"], - ["3dea1afa015b498fa64bfe13a54f42947c295dabf75fe7ffbba2d78984760726661d6ddb4c030000000851acabab63516a51ffffffff01a4641402000000000663636553acac00000000", "5165526a51ac6a6a", 0, -1612566191, "906390cfcc1cf43a915fb597ffbe08a3d441d33b21a33b157cc5998d5127a21f"], - ["1c28162f044291ac71b3b7adf788f47b432a373154aa8238be8d73fab579f0e9c4c2af2c4d00000000095153ac655151ab5253ffffffff27bbb1278809761ec719969e01fa07fe0f34e5dbd57e2e7975d5f744912dc0a70000000003ab52acffffffffd4aa0f1824a21fb5f60b91c4f83c03dd6eb3b008c62d3bd097e5fde2b637311f0300000001acffffffff63336a920801f811f0ba287f17948b89995f8454af67c1a5bd994694094093d60100000005ac00516553bbc9751603be1141000000000007ac0065ac656aac2c2acb0500000000006d423a010000000005636551636aaba3252700", "ac", 1, -287142519, "03c12df20878e11e510b07eab2b62b7a73e220c8e0dab6b78c2ab8bc6c87720c"], - ["b1a534fa03a0472f5c0f1b021024090ffc1c54a7ed838c7127307d7da0de8fd6c391b9d4040100000008636a52ac63ab52abffffffff79c37553df16a1688b5ac9a2b59e0bc28377f6a42ec97bce0453f4c6b831b16c0300000004636aacacdfb4673a847e55fce4ba1396b64aed279017e07a357722015cce29bd7e4c562ec7334ea2020000000100d66c3426024f8fed0500000000025352e46d570000000000046a5363006617eeca", "6500ac6551635363", 2, 158221600, "50f6487c617267d11ecbc6831b52ac3e8fba68e72abc7c9f9b00233eaa584ace"], - ["372fb39f036c7a8e6b1bf477f2baa7dfaeb9f0a71dad045ee1030c132dbbbaac3951b00c9f0300000004526aac63ffffffff65d2473bf2f55f1937e0517c03e197140f315f3739cffe188407655b5e7ee9090300000002526a43018420be5d38c961889373da283a5f74b3e5b3c29aa556936dcd83b5725b641b6eecd20000000004516a0052ffffffff03a1b593010000000005ac6300ac5243e0cc010000000008ab65abac52656a65de3936050000000005516a65acac00000000", "5200ac515251536a", 1, -1807372379, "06951c75f703be46fa836b70c623ec7024719b3d72426c4bdfd9bab034f09fa1"], - ["d0c7d2a8015c34558957fde6643c7c2df5efdca813a8d49ce42127b52ce07ad430ab1ec81d030000000251abffffffff0337fe8d000000000003ab656322133704000000000365ac531ca5c6010000000000787d924a", "ac", 0, 1119628954, "4188e3514f32a682890d71498123498d76a2d9c9fe460513c760b0f844413f30"], - ["92785a37034f9d20008dbc283df80b42d43357306bb1f0b185a5a638b3dfbf88fc8e126b6000000000076552ac53515300ffffffffba609f4bec2e35809fda050d770364477a37cee6a37a26c34633f842406d193f0100000005656352acacffffffff215d9e7d4f1a8801d33a43605dd03ba2ba95965555bf113bb7649996f4fde8660000000007acac5363abac53ffffffff0252845800000000000763ac0063520063b228590100000000006f64bdc502000000000000000044bd1f00000000006eea51c33062ec244064cdb0a03ed4fbd2cf05ea617b55e2eef84888dc6829fe542d995fd6359b47541e4f76eec037e79a2f63eeb66993f8e35e6e66d1f57e449590e413b75163e42c89f497d5d8fa4f91d65a7d903a3925205b1ae8c3d460b90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000089050010f4d7c5b1d2bb4703903b0ac4b2761e0c2dcb1f1679aa1dc7995c3ef75eceadc98b4b0fb2ff30290abfb88b73d33bdd7e8874f8145e29f28c683b906ae5ed5d588e64e612dc44b2cbb3f795040d05dd881b9a074094f7d95105c745ce0a3bc184d9554a71bf881aaa106fc6166536922f4c78e501cab4e24560ad9be4032448370d101d40d1d541d853cb6c3958d34cc688535850386dfba3a7f840645a022e90252f86437c7f8173e319a78d9dcd9bc51db6e4bb83ba68ab419c371dea260b1639eaa6457c9da6dc7d68c06bd9b77c01d34e5b8c7284f566e6687347c69ee4279be083a7fe57276284342bc13c986599be3ebed89d8316ec9eb37cba2c21c90328684526385bcf6eab24c91bca30f38a2732b33623a0d051fdbcb0b940fc45ac030fe9e6927964a2f13837ce562a4b4c46ca683fe4b08262c8144e75e32d242946031c5f137d6109488236e21cd3e8ae93078393de186681c62408be2b3dd0f2d83f030b868822a44e51972f773ebeadc9b1e6194bb48575fef6223db6dc7edf60ba0c03143bd923714650762517bef19fc194b43a37c9b70bb0ff93f8a825d3005e2c339d6925cd71da9887202131b20a8bc5b5ecc9deacfec9126ddd338d6f2a3841359c3481d5205962bdc767c7c5c45b27429598695be1124059f3e7e7bfb0f1dc3c88c0780d5e20453b4fabc83012e093195976b90e84226ff0afe1085c35f62a8790fdfc2241dc1d8bd2ecb3453fb99bd8d86065457d4f6e2d90cca23cd171e6092ed5664e89a98c7933738eebe64fe023d33fd5f0675b9ba98e76a5876c816fd91075065d4b390e93524f9595c628ac9e63f28271894b4a0c84e88a5b41c93f38100f274dd722da6344123ca41fba55ade43ce0fcccf3babd17fb2c86b53c38aba85a84a72826cf41a620e72864e2805bc7d0a2b83e99bc16bb088121e83ed016692d36ae5565879a0dfdf852df04a8b35c5261420110113fd65d47c264c252da2911d58a7ed4598a40d94d1e0d385fdb289206986cf76b5ceb4481c5a98863a5a349196dba37b2b091850ffb225cc2bfe744daffff1ed1d6b947c6391561716e05736da86cbac5240176fc4d3600bf78e2e177a69aa45823c80b2d5fa0ffa7227c989820c06e122caba43e2be39db55228d6f8bccf32692419cfb649b42f56ae241a0dac1f7237b2ee3812245fb57bf568b4305871777d1724e5bfa5e5d041006840e55ff22d326f39697639a030e77dec40cb69903230dad10bb291d7af1e0f191c0dedb09b20bef3bdac66463c1d5cffc417b8c7f5784c0f9a08f1816bb2d9130e4ccd9bfa222b647a938ec15ea6e72e3c3125852a80f0b70e6eef18fc6b498cd90c7d321136875c3c06aea6b2adb8da26ed79f91654dc9c3c6d3f537c4a06dc4085b4db6cd5595dab9f2878c3916eb6709b8f3f238b4a0df1a8728189d276eecb6e4093eb1bd876e3d581e7e8d4790c2ba9ecfab3a76a036b54201cc77547c975298a598aa2a3bcface420dacbb76aa935d8a7472d2594ffaa475ad686f0750fcfc466a31450dc39d0fba72215b80b188df34f761f37ed6adda8fe185b1293218b3f20d87253c24dd91e99359351e68f78bb25106646340d1b75a1d86e8dd56349486643b38836bf05c7361a8b849d836a830bf5b5f1b9c0177c5e873908cbd9f5724cb10846e13556f13a2b4a75b57b2d1f7518cbb1a212bf3ee2e0a9d7bb3dc7ca978ffc42101cb4479ee8c1c42038b7273a12c6e940a648a080de59308ed3cc65222bee82ce6ed29115d0db76ffcb95f13dee8fd185a8dc6f31f522f04414008defe094e1020fc9053a1ec32068d22e75916ebba036af64267792ec7cb3bc1d1b121fc57d97ecb7b8dc53a12be1f93df73bf62210e213c759190b9f5ef15f80ffec7f53258c328890d42b5f3968addc2814d66a8c87b56b234f20103a533063b3bdc1688944dbfc3caf1171a53292e977e82e7fc7e9c98f5bbc86b952d9ef59e587725a5241b754b7d037b814c49bad63c110420a5c80bd115785115ad65e0ccdf5243f5e311ab7fbc106e1d70a64814e5fe9902ff44d37803626ad080f57c5e4af51472e26d8f62500cd8e3497ecc34a50be4116ba9d935b50b8b8630b9e767724ac15008e80ca35aa506d1672c75310dfebad4e0deacd070bb508671cfb31ca700e5a4b0e9661d600f4c15838b1ec5d1be0b43aca652e3066341a31c1dab53a4963fdffeb35e5a8cfda780fbb59e8d02b0a65d9dcfc5b5740efe99bf0298d4bef84b9252ae6860adb950ce13a9d200000000000000008a60b302000000009fb24bc204c521668af4eb34b1a91ea2aa04a6c32c075fd717120a860fd7d513b84ff3df8dcd9f205674bfc9f0a460936fcaaa8cbd5490a7b1ec05224bb4432972142d398bd78ac0dadfbd87369d11d4458f831110285e6fcb2c6ad350472f4f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007880f49ae7cab9979238883a39204ed99d4ec88fd0df9b41938cc4ab8042b932b288f054293cff8e58f6b23fcfbfef87c2e830eec2e8b2af333a8105184bc08b9e8c501a9570865f23991e56cae8d484e007bbe28a2c19c078676ce13f5947750bd7f94c59ea1956bbb26d1f02dadc5fd933c76e16133fa9277a6b94a3760deb020081cc59c034c4cd8d72c67c4ea8c50c64cb812a0424bf10edb6ab376b04b1c1021bb93b6bbfb929ad2df1d6de9a9f27aeb8d9ee024493473d588e9bed28a16cd40b02ad297932e653f9480362e1f0bf85f0e76c2b0895f2b89fb4160eb27640c1971c84b00ac569500fdf28ac7206faa7393f0b02701ab1b451e4db1b9aaabe42b10202c3ec4cbe6d7743948f7a5f4fe9390b59e12c783e6e53a3af4c60c99b2dc521021a34a3586a145a1a3cf577c7637bc9d416b162ede2e6f113dd8c4c5d8f85409d02057b803742d91353fd868eeb04244d2745c7f2ade0f719c008434719022a580f0220fb3afc6dfb044c114e90e47d0ac4a90644bed3fdf96d098a2fa3f96509387f0216cbab6ab9abe3afeb6c5733f654ba644900013f6e09b0db74bbec659b3915427e924b97c4161db3342efe54359dfdf4c5f7589ffee5fff9f2f4b31cf52f2628ee534730ed379c63ea1863edde61a6a12248986b950dcbd3dd77083b6bd70319b376e69125643445b9b93a07489675315694658b6f704664d88a19f6cf3392b1fccb1b2bb836dc9baac19225ead4437c53e81f7632df8e9cf1a018f8be57b05b9abc427a1f7494a9edaca3747e691d57adb9d53d36e84a455ea8d92a42d331dd8223f8e2f6abec84c86f79c1c935f234b6234594e838bf6a889533533082039c25d807341701aabef6deb869bea6102c8849da2cf11bc186cadad3b63c2c8a5d0fbe3a4b8777d41b6165083bb0bc75674ac45ae91a2ab54a4a544445ea5f73cbce7df28f0431c369bc7c7f9428aff441b79325fcdda0b633e58247bedfde51ae8cf6693af7df1a8995da85ddb8daafd83e4e03759a06a114bb72757ebcce0e6ff940d71ee4af585a78d667fde9783f307e33c82819c4a8e55c30b6c182081d6340eb2994a33f0aed31251036461bf2a7241b2443ef83f1651e7896e536e433bde0e666a472b458193f974aa9bd691a0b2a333294c71e38a2d352edff3a47c1c2837da1cb5e9d12a261ff63019df0be184dc888314bdcc18f07c842c0dd5ef0e2ef3529343cc09496af6400592224ba6f794a82965e5edbd76d82fe0d12166b28516aa7ef6e02ba2e68f960acb2deae00152fdf9e1d9fdd4134e9fa303489ceda760e8ab75f3f8e2307d4047c273f12cce3f06ad3a4ffa6c5530f46bdf93b3ebedddf79430358475a59cd2312c50570593f81e4dd3f4467dcad26739f3f87dba6234bfc0256478f1737d6f90583e8269e69583e77dcd776ccad439ab4de7c584792506c541ae7aa0432ab99e6fc0218fb49a46049b42a686678618269796754c94ea80050ecebd84c2b71bb9632c3a0b744129afe77ea3d6d917541f55d2c61738ad542bebf012064561977cfdf124ed404c87b6657f23c1d4137f260ebf999989a5c266b08f827b645b9766bd7cfdcbc53389fdd04dd024675ab25333e9676f64966085eb35a78bd925bd9a1209453c9d55803a63739639bf4b8bb67100a6a7522bc472d94db5a518927af92d9a3ed6092493b43385ec511d3fc97386d767e16d395369f034a58c861ca86702b0633d9d757ca2f4c5d972cdfb6f88d691f9182de11caee24c7f78427aee09173a690a5bb6f381999a4ff65988a1604e1bc60d13649205f6d17da9fe87745cb2ec815d22729051f21a7ba09190cb4a0340d6a8ea163efc6899e7e0ec45a88d1d7d4ec58d520d05fbd653a68431433692527177f0557b4f63c5fe4c85c13c8ee22e9e05cc1c7fa126733a9de0eb50c6be1c7a121c03c2bef66b64cb38f4ae93b6acc5bec7ffb036a85f54ba0932ac2614044783d325cfcc99e709dcfaba1913061453334b6f5b245d9033c2903a886ff24cdc103943f05c6fbcfcf55dba347de03574886477702138e65d353071684aac0f9d24f5fd115d6164ad602b51b33dfc6488236623681f53e57a6ed011789f9b3d713ffb656f24d5dbc119b1debf6fda83849446705ecc105b6606b95c581782563f6686027ed41f626c96cb8c54e3ccdfd4fb2ff9047f052025de6612f573adc54fc06288f2e5b2be67d3f8a5dd2d2fd533daf356d0985307a91a8a0cea625243d884d4ed16f8308c7f9e404257baf5b6e100c11c24e3e2a6c0d840506b36c8d3d10fe9c06844f80ad6cec1f3a3a0c4837042c7dd3e00c1275eefee93d92baba41996807d489b6de57cb6aeffb71fe54099682c49ea9cc84bc6ecd83663af952c722afcaa067f9e12b6a66540a6c7e2a364b86b10f", "ac52ac", 1, -2006459521, "49d4df96f9c3cec0889d4ab64086607857846f090daabedc61213e4e85698c73"], - ["21410e0603148b7eab5fa234d5c72e381a47e7e165348d3b2e91cd059b1c461c2066cbe0180000000007536353ac65ab65ffffffffd3bb1945642fd76ccd2cafdeb3f7531526945377996b75c9117802e873920e5701000000066a5100655100ffffffff900370fd304d531d55579cc7e61ed7dea5344643fbed641f01bc4717ec0695000200000006ac6363abac52066b11ac037b55a8050000000006650053636553d5d67d04000000000853515353526563ab3965d201000000000653536565ac65f454cd6001d11006010000000000000000000000003fc3c7c70d6f0c2261a91e1ff5ca5ae8a4e0d57c3f71f6ca957e65cd6f0545369d8b8e47a91b43b9c920c5ab218988630586230644ebfb1f4f7da7014b0df377165b5b989be955bb1da1291c398f3ff78bef19b5f16827c23cc0e8cba81e787200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000654e225a282b89f56e7d6737e88b74101bfa04786e3ff42ddde5bd4f8ce52e5e2a114125ac19ab708bf7d9fa6bc52549943c4e1888f41ffc6d10f842fe2ca9478ffb7ff5dcd423b065f87c857651fd49e7cdc9b167fcd6cba484936de507eafa0157909eaab849a3333b3484b2244aac0b4fead589f7d70257b665f431a5fac6032550bcc66f9986366102c42e40af642bafba6bad31b1722f0e4c84c469799be7032eb1209b0a10181a69130ee4104516bfe08e6baff91d4adac673fa11bcafbea60b13c0e88c9871afaeac120c17c2ac2057698116098894e901d6f9825fa15546e3113d6b3b6499d553801fcebae84dd48eaac386eba3e7a387304ea36ababc1a06031b4a59e7ec79e146b982d61db2b28223f242e55ccf8aa91f06fe0caef80d6c3803191549e4100d0eaca3445d8991c7194bd6e1b7f66bb1726288d95b8272dc4a45030f99d6a0740e2e7e4f9df4f84f04777bc0d5a428c0d98cadd0eca3f10a1a02d60217e11dc9d6155f671acc2714ee97adcc1ded409c41d8e4749bd6148b9bd4ded6031ea58547713cce136570414434f8cfae877b20cbfd8285c38ded546ae552756b15e3ce7de4ecdb5ef7edeffcf3e09ce62512b686ea5115f1992caa0d1d530bda8a2f82cd7bf0f3f1dba93234b8d22d451c9ae114c31f4a8200d4c630a56aedade3b328f2af776d2e2abdcbbe1f288db7fb9eb568b067b559f27b83bc5c569a8d357afb6afcbcfb6c2d8c253079f227d8c9933ec9e1db51af97c13de5f683ed85893152c8c606aaf6eda638c1ea2430c7e56e2874706bb50d94b67204b2b3bc82812616ac15981eddcc6cdc9e05b14b916e236ec0ace63fc1fea090ec120ffd338dcc894fd3379f4d528f0a945e8b49768107407c46fd2066aa6ee1575ebff40e928792a78bbf303c41948251660abfa9a90a8d900df14ac3c877dc419505af147a5068c6a6d2fc94c2bcbae88e07a7987588e564bfaddf9f7e8beabd56cad76306aeefa048fc3498ad02eaded6011775d40cdd41b5771b32be578bd11a5675eb22133dfed19ecfa3fa1faf0b918183f83a9bf3852ebdde2a183fb088f911e22efbdfb4d2fb74363f7422169d4ee812a0cab5b17c851c70461ed701d30d08be4067a1a75f04958155ddb91424c7ae99a54e4d85cc30d86f1b57f88232d95769e926605769c6546cf5ac8286b7c2c715076a7e973c1fd57a7dfc3fa843267000af221144f783828e97b1312c7782746cd25a75de1213e31402b9ac692284de1d103b3d4bf3183de7cff35a388d4b6e69da79809a00c03dc182b9408a0cea19e3c67dfbc66b5e8cb8a503b7808c5d25ddf3ea88b7a6b687f1234f76751dad2277969f29994455a7951a81a715bade8f8c47be0d2bb52261763fc869d6dd3bf0e0382150f6645ab5e145b29c0bb561c23657d351da835401cbc2e91d09a932312fdc118a53bc3596ee3b2725e814bceea8c549a664afa4db89f3ca467f8f9b94a543f6bf7b68b9cd39c97ec4ac07a7c3dd56561dba35866b6fdf56ad2d70e8fc17ab6c950ba70eb30dac277bb1ba7413239c4bb8af0ae0f5d21febcdbbf1ebb0839062fa393b1ee7cfe3b78dc478f5f45c8529d0f21eaee2c7b2fc7a288baa0d31cac1f1846a954abce93e29502b5b0dda98cc2506b479f3245d0bdcd0145614dad63f22079de6f6e173bffe5cd61ec3047608643f27a4fcc5a3294c0279954e247f7e3ab8a1824a1184a0c4c400d6d2ecd37529f11b5630db65e63e0a76936f7a6e0c895eb27c526c48a5f2006f79d59bbeb5a7c212c30ea1c934608a8ffcd536f38cc548eb996b356591f3741d578cf9d8fd280820985d89fb6faeb2cb68eb21d2d7496edfdaf3380c2804a18cd36d8627491d789daea21193c701103b73a81e034033fce65162f89d9b9486bff015c3e87b2f15db76826bf67b3e62410a1242dc2a13c1474052bc2ca5e4b059d19b0c12f0ecba94d657c2578135405fca4761b86ca8d0e0c94a54c686b6a3a1d4db45baac949546137aa4cac1baeb9e7778e20abd876dc25f9d42d6c4a0f3b23c30b016ca4fe153d1c227cd9517bbbd964d9e28dc131cbe679eab6f9564e3f0050c353b103f8fc9172318d261d9276ff9eef2d20616e3300d1591fb0960f46143289bf1d9c26483c9e3c5ffc98fa10a23cc211d1b9fe84dd5f032f8cc16cfa743d5f081349370eadd29ad2efce20990e62a5385a9a7d92928fa52d9b5ad7abb590302c4b16e2f95ee7eff96167be894ed8367764d251eade35659821d81e565af5427a5f07800fa0875de34debc3fd96a5ff158b275878a64b729fa00bc6f194107c3f9e4d3c601126402debabeccd8f63f7567ad90ad95868e9dcd8afec27aa8075c75f71d5dd7d244a7e4d5721a69e0582639ad08235a6c40e287def5b045f9b7b41ed506", "ac005153", 1, -1261392869, "821e787a1fd1d2a63b0b83b192cd1e7d20bc2b14296c7ab1616782b01f15d72a"], - ["db4813b3023983d2eebfb3aab865746c655736ee7682cbeb671b110d3f04ac573a9ca5e1ff030000000100ffffffff0e7211b4289294c4f8785429d343ea57b178870a6869cf8675dec1a9edf45cae030000000951516551ab52ac0063ffffffff0160dbcb0500000000036353ab00000000", "5265526551", 0, 1228609669, "75edabe179544a7055878c4aa824862185d5b7a7c8c4006f8001bbbdaa96abd9"], - ["686ee92e03e5ff970fb5acfb484fbd2b971af67cb5f827bb2819c2e740edf9b2956035bf2402000000076a005363ab6363ffffffff7b7b949dde6f992989297d075d51f791e90faf5ff80a1dd02fe8a283afe8944401000000009e66457ecb9b459529b16614bc9f64506537c29242de08a2bc05dcadd291cdb161f9e20e02000000016368e874900189f8eb02000000000551515153acd01ebab0020d879f01000000000000000000000000a3f6adb5ae818b1826f115ebe60e9e477657e30e6eb29e46506740cb27be5140547eff1ee0e7bb65e10dd4f8904a1bb20b65608f980e39c4471397de4fb9cdc87e3aa9f055a8e9d3d1fcf208a9c6bc828c4cbc4344cc3019702d391727b933e1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007cd7d2243d138b0094636ea573ba728af48fec4ec464cf2d8aec2c14866362f74b709cbc2129da5bf0d53d9eb1ace831be480f6d73d0388e79296b578fcc59e7dc04d0a81cd2bcb12d47f093950aaea063460e090e47a8d8f9d8553d2de9701e933060bda424be647b5aa8474b0716bb46d5f1a1dccfed37b162c74fb8b824b10301dba0e998d6546aeebc8f58ec92320d3f116f19cf9b570488919555c1a55c990220bd6b64af38214573afb827f59dc06a9bab938b47bbb87163f1d5e8fdf1e2ec0a26a75016fc0fb3cf72801d3ba047d74e505e0f0c4f99c917a58fc4174f2ae2d81c605bdda62dee15744a17d2322f46b80995dd71aa472bc7b17264d5786842e70229076d8e168f04e68b2982919aa4f1f9de45858c0d8607e18ff31aaebdcaf7da0305339e2c0945f9297322a47fdaa13f097a93477fd130ee4698ba952243746da003073ea4914219d282ffd261b84d4fc1052fe6f711adc2f5fbb1b96a952b9a8e0a02134cfb16a580051bc38b3f4e6feab16545173ecd2a0ae05fc997ffb21a6e3809021452efe114b895e5dc098e86333fe493e2d294923312114d3ccb942b87e9432c3abe2d55194dfc41915c6955edc2e575ce786205b8b51d500b6a1d729139f8262b77f30fb4d1b0df357f71b72638496149d8c74f59a23df8ecc124df808f33de49e309b1435e3db5c32f7ef60776f04318c7c7ee5fe04a022983e6fc5e177fc98d3f0bfc31508c8f2ad847cf298bbe697505c241604e2f3c078b92a2770c9aa362cb8b8091d93bb4d1ae1963d61f3970f2cb948fd6db65bf52ecb6f0ad585b100f83b3aeb7a4f8942407cb5013e0ed2a7d1dcfefbae72ba79fad269032a4f0f88d380b2395d8c3093fd1ca1f735253bc412dad4446c85a43770542cb7c260b7efe6929afa4c21a43432c9c0e1b7c377c7540adee9bb2893d323afd3ea882a7b29ea8c69391f36d628291c6cb9660dd7b39e8e813f9b4bc5724ec94f3b713ef8f764c3360299408e820ce2e7163ae47f6ffde18e082625a06c8051e2d292c62530b9b225d1d384aadc27ac268791737a05db08f70bdf343bb71844b94c061dd4e76247db6ad81dac24c56e371c22a61a8c9285d2a91ba350514a9bb9a478698257bff1eac039cb427afb413c4e4f7b8b1572bcd4f0ec6d2dcb2a3549f191d92ae2a1a9430733efb2afed70414c7f4863a16745ff7b637d0ccf4d23452deb0b5833778076662ff75b31bdaa1b8eed4f1415f76ad18a3750bd3c6492e467f659ea8b862b7afa4a7838545d02344d88eb487418c403248218eafc3aa2f627f7aec9263ed5ef51a00bfa2a80b4d921ebc3276a9e77dd9f15cc8af06ef5c5ce1949acbee87d0e9f6381a396984f21e05f23b5bca81b1d05305cac375b6fd58937424b2bd325987dbf5deb8fe6e7f54a93b70320adc7e099c93f0e26eb7fa6062e692fc2be3998c40b97a46fa5f3de3f52f5684763d42f919b04ab84aaeec0bc49481449a8864e1d3e83cf45bb830d066ae924c51e947149944c170ae66a2cd4ef2b05815f0db685e043e08e6514ab23a9ade3a855644332cd3653866c00e2a704d415d2b6ceae003f0540066246e762cadb125db8e1cee6a2b94301e454b45a191e8d70ac7368d50439ea43aaa0e1ec3374cfcf7e771bfb0af727fb59b316600f448adeaf2c2e08965b755c0308ac71002f89f9f9b9abfa28018acb0020b82efc49d83966d2bc613eb2143e6f42b85c3ea600b89b2c4452a9f7ad4307812666f01458865e93fed999dffbe2bff13b860713a06487a230687c33a1b30fde9e0b8c2510a59a1d66ca9303bc3b2206c142bc0806007f5d31fcec6f78deac9d957e4c92c4c9cebf22318c5b4c1100e61ffd19ecb22e0cc60fe2985af55c62a9712a08fedfb2da7938cec0499191e76bd90d8df9c6cd4a0a5d0aae91a393a6a2c5759919ab96da5a1a8afbbe3112c2f711a6226af91b1e369ebb746c11dce48f22fc5053285c42321ba6cbb32bd5dc1e5da4ee9bb108553162b12bcd772dc7c6272f91db945fb374337cec2d475fcf8756da6a83feba68a42acd75070c4be0f82eab462c3679e4836f5fb80f8776dae7f3cfcc94978c8adeadd18f5624def202010092e854d250ea780c8f4576405450551e7da3d8e1b22490a85b55bc648b8ae97b0fc85a8183b7689bbef3b7d6e846544f5cd9fdc9d5a3c2a66e1b2d42f55b629544492e23de5b89f6cc0bdcc086c9913586f7a21b5b77304ef74dafc440a5af563236b6ffcd9a853d3b996821270ea7cbef2b84d108bd8466501000000000000000000000000920dbb0b8206ae6b022769960dbd126af68f628342255b9101bf98e0e4b05df9c31779c6275a63538f756ee08f2acd445d2f73a6b506c4f286472fbd25ff69e7ee0ad4d4cbf126ef1a1d237854e5d797de17c551725908f42b22857d5df0182100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e2fa06c55dd6a34764846f7812ad38c259402bfb5d2b8f05f4dd8f384167d7d39deacfa0064e25e74276254144f71fd94d75bb4971c05a2ad31fde4487bd8ad50fdace4cc741ab42a9b6acb3025181bbec0386a1203042282c322c1cdd39b32be2cd9f4f589b68fbf61ff4a60ae87e03921158d9bf6b6f190ef30187578ef88f030e14df6b75c3ca02c7c00f42751349497a81a5d8bfbe426806bfd6097be201a4022151897ea041ec6f203cc02e28003130a77f03cad330d0522ffc87493d91c1110b0333a4bc91324c4eedfda78f9661808fe0c164c6a009597e8d31e851fdf00a4c2e210207f399d9392f75a09b86bef5a064acde6ac73240c6ef81816f54726380021c739cd182c3cca87bc6f1eb6b9a9c91154f7903e6609c75f8d871f99781bfdf02166368c1996d7d136e1daf6cee4591a14e976a25c1ba3fa942cbebfff4ace9bf0319adf8bee8d7a09ae7d6581e2ad27b5e39ca663397238fb8b04ec8d7aafc58d00305db1b6e108175a99cd4121b5c0876090b0285aedd05b4d4508aa6938df365d5030a4f131cf47877714eb4c393f1a5d6ae2a0c6687e676c5995bced1102c3d4e6561522026d60d0e89e6da1d55d392a564c69934e52392ba18087788372ddc42a1f624b20b646c02c74d202330370f0e30bd8b7c49948dea71205afa2b3ee4b289719f90e796756cdad86bba9fedd6e4fa832e1ff2a93e0c90f864a63a7f5b1a9ba0aedb7db72ed0ed935e9dadc2dc20c4683742a860225a73132e0db60611d1d4d85cfda8593d10b188a083caf62813c13c7ca606ae40c18a73108b00a65b2bc1973f5bd58ca816873c5484b5461b9376af9355eee0af0268e104e136ad4f85ef2681cb0ad4a2b2caba11f19db17dcb954f05da239fd38a5bb3293d4c7cf2ffeb5e77e3fe7ae3ebebb93bda26000ba3f0601b00212fa97f03257095930d52accc250b67b6d703bfa200c1dc68f10cdcd8b9e9d02f52c06d9627849cf6cf76e4c2fb2f0238b3c21c9d2ed9636ec112b2d0dbe3a3d6de122155f2bce6bbdbd891ebf312a09614cb6e37a4faf12fcfb6e6f6158774351ac9560eb2df0802094b7b6eb6c24884b3b762c150ab043dc340cc68958aeef70c6c2555125c620e4d1532a07e04306539811cc1bf896ee637b371ad9fb0657157331b07673921d9f860141bf71c1d817853c25cf0c0ff23ae83ce42f29e54b106b2badf0a1bfc815981c15b6c1818db20e4f0b6de165c5256e110fb7c98766da9112cfa5301062ea272e6687d19386a2ea020a66359e5dff5745f59ec28658e4e11f5327b488ef1c1e595164700a555928da6401409b91ac7ae8d1623cec2fddc653fbc4ea7b8085a699d241fc4367656a9d9a5462276909c3d61d21789e3c3712b3633702a40de4385a8e152ab1002b9820db80cb6b2c9cc365ea3fe2da3ac752bafcfffac318638779ba4f80f0a88f807cc2f28a2f29ade17457342f4877ae9a28aa7bd1cd6a17b477e7992b5a433ffb74eb72fad0505871d592eb02c30459f78a6dbbe5bee333a150682855e98a7a417af0cf2ec8951de3b329944dafed9c8ab3374942070c63f5193291f0d188b97282ab8681838f8bb62724a04c1367bd72560ea15162d528ac79bd15e3561825f1ce26b9fc47b6eec0365c706de3768f99d554ed914c16eb0a7c7bbe1f0fa9960b69fa1900020354e6d1be7c471c99f2f38a68c9e19b516c628657a24b3fc1974dbf05c2f3d193289cf446a5524873dd9522c72254f27e5263a0d71da467eea1618c1b843e83e2b344e90d23306da90f3828d195bbd4801455a7b3f8c0dc18a2085267005c12741a45a1af3fb2a989a18f1d374c8f7f7e9fc20bb5c9fe56a3de1b28dc96680fbcc6363711133c896e7c05a947981c3a20079e3797a587b756be3ed629a0f0cae22c2167aaa7fac0f4b2d473a1922ed11d51775bc170239ff16185ea170b2504fcf2d3970427c206f8904dc83f10077770dbbb2922fc85f14f0d86dda2ba1d23576431ab83f37d7c90046bda028d712d95bae1d94e4e8d098f92c4bab87979e3c10d2794f446da706ddd57723b4018542c8529393862e7a1c6e450b5722c67debbb57787551a0e7c151256659183e735098e342879ed6858e41e0c97f9245d01cca17e97f1dddb814ecab83db28c6f64c4eff569d25375d205de500f7379ed26df771b2646afb5e4abe04bd18c1b7113d553949fd3018c484cdf63130fa88f6fcdf629338e0787bd35ca71ec4923f594cb2d8f853b88f673028195f04bedef619b88e3f3cd44e29946d90d105307080afd566e75c0f078d8e0ccd4b9d593b250834c45e82b3ccc093acdd2bc014183c4ce318efdb475190e8e6fd70d35b8a4bb90952b68de8dfcf5cd9c214cc6f9b1395cf29d1b4addc05d6a17e4bfe9a7dd6d124656b72de6908", "5351530000acac", 1, 2055472226, "271189653df5d8387cb9b64d996c46ac37da99906be9705099b547a8607c9a05"], - ["266a8348024815decda64aad93ea273c28bc3bcc154e11b54fe98381c204d51cc41f4359060100000006006565ab006547b5f89c93caf4e0f2f9203aa2447591fb6619d2758ec36866a1f71f1a939d9f31d6ef5600000000008621b5dd042dad4d0500000000065100006363ab4fa4dd0200000000095263635152515152514f88f50000000000010005adce01000000000200ac00000000010000000000000000144f6c02000000007d70a8244c98d73389fffd7dad3d377a27e0b5ed0884f4b9ffd23bfc25d7a28b5e17aaeedfd6ace3cd60772e46c86787ec65b722fb54f996a2e08e5652dc4e155bc842beccc2d048d87393d0074d40f8e864986b3427131909082b9268b5e8e100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f75cced1a9ad7639a4551ff313f04412f54b349df98359c3bf7df72252cf09e5246aaaa7242788993afcb9e6842d16e388bb14023003ba5213b6f8992f68d93529a5fdbdd89a1e825973999819237b87bb2f085524fc85736d9e34b8ced8193247ded76d3941b34904a2642dcb1209ff284c26876c6ec649ccd2a3199b9f521e02282fa07473be2524d70bbf11fe24c23d5b92c99f7b94ca7a48021e0a4975e1e002087ba8082aafdf48e6502e9664f5fe96b23f7ebbb221df0c6ef2f4a43a7be2b90b16032239b42956e382f9e2d4098b02d425fa7a358ca17b442e92c16d83e949d40b8fc04732d62c1664631c8568adc865a61a2295e85c00c929ca96965303724d0313a366f72b0718a1cdd0da27ced3ee7f578a5b8d7b9e08a2721db726dc0dc02f0319fac83eefe9c673da1c200f58012fd1114c02fb82e34a1bd4edceccd6a3e53603252a963940dc23d8d58c95af380aa80be20fe772951d51522cf48601baba1ace020e9f118befd29a1afc0891d4fa8ed68e3b2e81c2239e2c059d37debfe88793b0021e03ce4579d6b2942f349b735a202568ab370001b4fb2425029b930b71bd07cf486dc092fd17e6b51707435be531f89ff648ee176cab080f4ecc5476005d2937af6fe2150653e31f7453b4d3a319ec987040bc24f7009a60208b25b74be3f9c58938c86e650ad7e884f79d72342217e757e49f9962bb464140cf77ca0844eaedaac9cf2a02d4290dd16df18769d8d15994f86702224d30cb79ed3d6f4e2d9010c8abe13b875e78b1e71c586299cf14d136542d32711b2e063f63ec1dd75c787969994ae883399ceffb178c467d2bcdbc8f7b3e6424edf4d681cd0746def8413eb18557070350e7a8abf2a60320b7c81fae25a14d0f7f17b959821874cc94cd462f826f069a34e63ebcac9abfb4fc4d13c88d00eed65854a2ac47ebabd6c1c0ff85d5e52fbf37a6824e69d71ae6e7e114aacf7a0ba24024f85abee18cac2de013dc457c38704e23986e0a67a04f25e36a5ea5ec8eb02529361f7afa86f688245e9529a15ce80e84dea83cf0515ffa88455b895d1b30f550a63bc087d31a4cc96a97bcdfb4bd21728919c8673c888e73b9457b7166a026360a8118a76ce1e652876df8c897f66890c87853f6c3bda0ec966a4e8e93a3fb1b8042d06d8445be507944c64bc41fed3a1ab99d044f4df6e614db41d54ad606dcf0838d7c27256511d4959e8ed2cbd32833d58250ec08ca7bb4423d0d1cc22a8b8482b5f9993ee23c8e50a489a4f558b16257a61fa378dc022ac74c2f74cc2ce26c6d5c377cad8dcd6900113df41c455f50211466b0a32fc5aefad3ed6392941858002987d13ee79c619cac44b7ba8acc23c80a8581af57e223b40396a54d6aa0951246e86bfd53a28793ab30d4f16b0ae4bf451526fa7a13fa0ef01f149845941933f10a448712c1556dda458c31f227a3f72312e7872200a47d8b9864247fb2fa7e511ffea17d9dc71f7492ca4a7efb6045508a3f4d03e4befb57192889136a54d267f52f64462c15b5e9f2102650d2a6e87d82ace89ac74d9ee9c12bab752370993ffef820c74eb524ebd3bf7d99ce9a8efdc711106165e6ddfbd600008c278a8d4a045d1e644c23ba09f413b16c30f3e5b9fb8107656ed4d5910470acac74c91bdef91449369b4d84a2ec99d077128f2439ba03813dc9d134bf087a66b53fa12a4c860ae19cecc04740f63e8e624c2e28d7aa0f17bef6bd69c575b61346711cb4287fb0822e6512bfd95fb26484de2b9432d99e919377480464c9018609afce1ee5b1addc7e37361675f8810f5ca320da87dbb7a04e08790cfa50d603ccd07ff87796b4d66e2ca6a4e12666a055b66776cb05111b720fb3cee1a64bdfb5203e16af95fb0b9835a7cbc0bb07a9a6fefaa8e89984c1dbad493b9e0b57cb166e69545ba4727764cbbd3b104297676c1bc4ba8d45f56213d5de048ea8b934043b9107411b3ccc3dd7ad639fe82e92a6dea8e41542220bc82158fb6c542fa61c49846ac3e2b601621fadafea7712fb30b8c9e7e5508763c26a6bc564dd6e1bd72838848bdd7b782eb6e7b9833dff5d1dbc0dbe913a1d795877956fbdd5f820ccb090b009e1932205083de1b9538c919c09b846ffc527a3f8860958b7b24ff257c96f458e22239b4b29cda74ef55e26b61b3d74a798459862b3e573171f8b375d619b4fc6340b9446e652632169a0bd41b3f4989e480affed216dd72de5fa01fa7f449d91f17ecf29ee5648bccc55fdedbe78f5f96cd1b3c329b27be5e6291d478d0622dccca998a67b3b2fb37aacfcc9c789df044c0884cf36d4210c13f0097cbf5596b6a3a12ff0b8537e206d6a17afaf7c93845c9f9dd7fdc782385825e4ad6ce23bc319adf1f2ccf100d05077707c095693ed76c2d3d7ff946104", "52656a63", 0, 980983185, "4feb010621bd08ce9d64707081fc66b3e0d321b4a83636f04931648439d850fe"], - ["9551c938022164dc49b221004651eacb2fe596c509c3f506d3582d6ef1c1e27c07011eaa5502000000055263ac51652ad1ce10f7eb67817fbee116eb0cbc12753abf6954bf85fbdc5238e53c20beb2850dd087020000000752ab655152ababffffffff0280a051050000000006635252ac65514132bf010000000008535200ac520053ac0000000000", "ac5352ac6565", 0, -1733984556, "52e51eabd9edb4c335ba6c68fe33695da0891f341acb48a38a8d7e53c0fecbcf"], - ["ed4b022001f2264a0ff2f451fb63eda871017bcdbae48d51921fbdd01e9b90a1c6b4fbc67f000000000163ffffffff037c6fe30300000000095200ac5353526365ab3a7c0f0200000000066a53abab6365696d6a03000000000351536a0000000001cffcaa01000000000000000000000000a07f832c8d6b558a38214227e31a3ebe8ef3787ff824fb72f31318c973e7f01b1093fd18c0ca7afbfa82ae44fe9774d61b4f5c83ed5bfbcf22cf648a63111deb663794e62bf30dc39be7a7c9a74eed9ef61b7c60b41d9cdf23839a2225b9938200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b4e84e0a09c4388a6a80a10360436ec58e4f2ed1783ae94f0ce62d895c5289abfc3569e224fe1a4da2a86a1168514f8755711dd5c1437ae629662a17e0b3cff7c883b0bbc4cd53a0be8059d6dcf843d34ee145bad50eacc7ef3dae6771b1f100d2f854adca6e2e8788e937831e82298f80b852fb36ceee2e81e0e483b8ae56b9021e8d323d5c42f5b73c43e0627a6bd5919c201d4d3fae7fc205f9784c63b61f2f021f5583425e662463543e716b01de80eecadd19b6a772226c1d1737cd6db1b3c10a093958358a828240b0ff5ce6c928337ba4006c706d95dba59d72a12541e2350a182c4944e5876f82ed9e8c9c8660a883858255c8c75da8cba20224aaf21db82e0201202656d814584ef18018a2d8e6d5a062c5bb8c8056d96c708b441e7eb1e5db0310553659f00077d06eaf5e213dbb0a67f15a0970e72dee13ca27a118171ce7ed03061e085e8cb429c1b3d54348f87aca0a08321e6d66aa6b83364eabb32e99d8df022b1a5ad0abc5349302f75305992c9ce2ecd487a290772f5c642e50e655cbabd003283637d19fc0578f91b08e60ec18e02a581775be4207f7c1ae2197dbf45183c84005c2d924e447e9949d4b1131887d23e57d720d79bff400d07fee27877ddf26d0843fa10a2deeee71510e1bc0e72dbf5eaa108d0a2a3c2a4a3c75f5795b3ff8d4746bba92088b4313a7bdc555ec975793fa0706efa998c4d2c912db3508a3e5900a0d29bbe3302ad6a1c5f41b08d61f692cdf01de2897d06d5440ea4617a143936f5f6e34e857fbedc008f85ecb6c5152a9d21407c6855547d9ecd7d2560a5a5211eb1fcb3757a65aa35addd4677d02f9966a16a6afa4715009e6cfb0f444c3d84cb2979dc46c94e3c4e3495ff6a0ad64e7bd7d28ee860c95f529312d0bbc3b34f839ea61c7c989b5f75443b47eafd58b55cb945ed7d0f8e0f8757004a048f7ae5a77092fd3cbf925358863eab4883a47e8a38ffcdf8f06e1454a2bc25ed302870b71521b1d7db6620f573485ac37ad4904fc091023c4af9ed705498dde889f30fcf78501bba180f09171c7cb86eab15a0cfd321449e81e481f22a9989f07bae0f73081f07f260bb8a7cc557bb6f6b9718bb4539ae1848c26a94592297f25fe0beb84b2485fe771afbd83af02340b6ebee08bd3324e560d606e564b197b740a1971c8e43c105ec15ead7b5bf07d7ffedfaeee16b692c794f9a6fb31fb253650bfcf12d95ee210b624e791c9ca38b7f0557284c4741e9ee01bd69ed9aa38fa18d970c1a96fd370a543fe1f79a9a05747f5ba49fe1160f36f0dbdd1b448fca921a5126cb082c13163ec314ff39853550364ff34a10ceffe4f5c8ca77f83d44ba7846bf90a67d9fc7aeec1f8498ebaa1c8f878d3e704fb8ead5995bfb434ed94d8a6ab12d17abbb697406fb8e9321874fc6c285f1939c15c17ac493e739a2af68a2f21623a93ad59f779bf70c148c2b6ddcb1343076efe85e1dd10fa0d997adbb39f6f06eb48cc3c8f181aaa1f7ff1561b04c3c9095335097780bce454f8761325bdbdf52cc35bea93c248c3055d1f71a237d7a17b07cba7db42ca8a4a2e09901fc3748b0565d07d647204a42ce04a3debf7869b1eece82f6d3bb3de3c59c84f36878b36f32c9a46d676ea590bfa24b22159841311388064dee76032bf4fbea970ca005c44610caa7d063e576ebb1c301390e9b963bfdb6c1d6df08931d3c91098f795d08472120f43ceeb90f97b09ba93dea0768afc07c7fc92813b02c73b0cde3f161312f08ef2643639870b239f9ef57ea0fd5eddbe0be93d9a2f342023a13145fdad08eb197d07148efbdbb46f8987bc7eb1b4e25f2bb0056e36400cb0f730af9a427149899e82e9bd4ad5a52cfe44507a35d29ac93544046416e0e24c62d2659ea598c0fb4245337a3672adc72cca6c5b965c939a523ba439559c120dbe2b86d269eb310787649827c1ae8fe78f741be7ea308845406ddff993ec8e6b24acfdf0920cc011c962f993b0fa615ce75ae813aa3375c12d2b4061ed82d1b28c96a3b8f0847bd9e8908c73e4e2bece05a97197878e8d4898a5d90788ae2bc93bb3f0b118f80b9ba95709b6769059b2585b653f31a9e461ba6e010ddc7e734a1b088ef5c9fc976143ab9d2a482f8027039f0d5d5bf3928435cf7c2ccef380523337c8b4c79748a7f44688641043433caad4996c1617626c571bfe379f73c1359b040631b00c53afc09dc3c590578f642d268eb13dbdd5e3ec5e71d721358d439863a63f6f3a1315a19e7e383e9a14af8da9c9af86275e29011230358a52a054562fa4c8ee33379737b0167c06ef4419998f79b1c1618fc726fff2de7b225702b7a579e90ef11b0f1c6456829640180268c487e066cf89e969cb369ea1e8a3fc1b1d88822eff5e686510cea9c155e42ac57b4404", "ac51", 0, -399663988, "65112ea9d2c6d5c5e64dac15c79bd6b8de093a0c76715afcab1110e41c0bdb6b"], - ["62d90c8f022dc4b84ccace2b3835fea403b965fb6f38dc73131ce204734d13cf07d7f94b3503000000095365006a6a6352ab53ffffffff2c24eef8cd1259685154560fce4f7766da8d30a22114d9ffe0867e191fa3e38c000000000752526500ab5252ffffffff03b69d790200000000056a0063526a2c9f8104000000000651655100ac634baf4c020000000002656ac10191c8", "516aac53656a", 1, 1457994268, "cd77a3b431a124cb98f825db6c7d2d8652fe5a39df6be90e9418365f438f9bc5"], - ["74d2fd49046187da7cc7a83262f2c6d15d4b3de9df25a97774b375249bbdbbeb7eaa81948900000000035252527188dd8ffb2eaa610c37db244b8f271db433bb0604940c7352f4b487c3c7fa23d1a3ba820000000009ab51ac6a0000ab52acffffffff8c8d5051cef9baefd8afb67add44533093e33bfdbf7facd12a6f9522d8d5f54b0300000006515352ababacc3bcb3145d7c3584ed6e8fc8d5cf061b5bdcf5857b772ca0cc8d004c0bf8806b1fb4e1f60200000001abffffffff040a4c770200000000066363636552ac5ef4e7040000000003ababab030bc3030000000000f89db201000000000000000000030000000000000000d140c301000000001d41e0a75f718352daca18103fce57f8b45faa8752b60f781e4863b12f8a248780ad6774f7dcadb0b1b9b7146ed11324422d73cfbf3eafc4faafdd94d0bcfad39e09982d977cff4e92ff6c0c4ee9ea3d76a406ec9ee1e6aa8f2685bdbe4727b900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000992fd456920ba7d294812e2d5d9ff670519bc4794b75dbdd0307c7297baa3aa52b3eb09956418f9ea93500dedfd3c7a21cb7ba9b4ba4efb62a2f995cb60ef25282a5bf92805d7c60b92b00eb9d0b71acf601ea74e66aed0087203d53d2e2749e5c34657c988a4f2af213dfac269ccd5bdd0730d5c0f17d260949042c4310d1c30212ccadb1bb3a24a961b7d4216183ea3b59b54c85059b08baffcf23383dd92c4903209d4fd13cc026f3a12fc856915fc2792f0b7457a39543a2962ba9c2bb5dc39a0b20d093d6d9a3dfdc8f56989375c4e4b53802e932b77e3f674e6b7c22e225b1fb012b48e8985ecf55d725e7436a3f5b90e69306021aa769f933347e346652cafe03237a4478a72e50a7cab013c3635bb312ab989bbb398e3f9d6e2f674a8e58ab6e02209d653fd5c8d8c8515cac110fab2c759f16bc23aee748e58803fba5ffdd4bb10221977518ac5725ebee0f910742c83bb59128aed4a47b2ace1880efa3c02314ad020e12cb3b9dd54c664228cba80ec9265744e389d98ed421852f12e61157dc8da103046dfe0556b6709b6ed8109abf98765ec8a0ae292047fd12a324364264c0df5b7d3cb20bda2cfa0165b2a7b3258caa28c1448c0a1c251b652fe7d89acf65511aba4f8fdec0ad794002c4a61fc6a6f58ae99046bb4577e21d306a6ed339d7144a8009b87027a2b835d3f3c41bbd4c245084c9a51c3af31e89588d18161f2d43f2f9836f1a0338fd53d2f5cffb32ac4dd877b1ceec9f488dc84bed43b29c1cf7c1ce3845f6a8d006e9cbbf40e3d4066a10cc6a17d1e965e877ae278a649fdefc4e0f1c40b1923eb1107a9623911a92243c1b696eb43ac73eab935277a6f01a892e33fa22aa87eefa3214fcf4679beb34b5e8b4c9bb2673c556524f628c78f2c8c913fbe0860a29eceaa18ac42346192cb85c2025e374971758b1b75dd3ae4cb0b15b2e47596cc2dfb94124d88e73d39a7f55cd1485a8be512cd072e765dc4b4d898c5ea43a7e20c31649bd15ec1fae111f4301dbb2fdd0e925658e0f7e73a02312c7ccb009b0bc03d3da4842aeffdd9a188716f7be7e1a9cf029504ea85c458152673cacb0264ee85b708f0a864b0a63097c47e1b1f5b2ad73ecb5c10d8e6fa532e902d11b6e0ccd42d38d2907aae35a974df799cd4ef4679e7dc8cb4a41a0290b69d7b1458901de6ef2f119addb0ca795c3fac0e13fdf9350ae7ca5a81737f00bd4a7ee632dfcf609abf9be25ad3cb7c8ca626fb279c6fc34550c7c30a7227cc28b69783ef01feabf88061587099e8f2ad71012b2d6d818282cae95dd5323eec72e39606e79b7c3f42d1c312b9b67ce7484bc97b29277d705ba4ac1f5942383325ae69d2a7cdc35e87fbdebd9eacea2e8563df4c5b4ba48678e0bee6a6700c4c8305be022270089aa944d3903e7f9d7b07e60ed53ccac5e8ad76dfb8923b49b1b9faecbc934408dfd7468d7545f51dbdcea67604157c1bd4f0f181559833eb61e65e186eb206642ec7a632b71805636bdeab23f01369fc4fbc0864c2b826fa4a4c48ea9c9aac18bc7af602a9783b84621d4ae9b4a5cf3285ed91658bf2e7d3f0ee72154c0cb0f058d4f7ebebfe33044332dcd4c6741ada9edb34dbbe1ff754b8bf21acf2263bbf4802c9f172250c3323524aea50beaa8d8f2dbc195716c42963304367e92bd9a288dd7e83cd5d390928ed057e572c0926a781f1654d503c73be951cbf70a2c552b3be8bc0fb576754abc76bcf829ea7383c87e25286aeb4cc41eb78d20236a3a88c50e983eb48d31ddef3f4d031fd3f9526a69871ebf2da4b3a65d1c9781402626f24c8159d59174a82d0e70760d60dcffff08b525016a506ba835c9e4f18b3359b6c4a9d68aea4bcae1835ae1aca703c6b36269659b24d7625246f93b975f9edbf3bf8b6949e217fb082852ee9d8ede1c376626d2c10f7e22384aa5308e78b13fb167827a4d32bffcb349ad4e3b6d027ecd2c9a0d901f6b850b947cc40b5ae058cb28b885a2f5a9df69cb6b96c6ad4b16a9e3cccc0c77a2cadfc1af0fd528baa4c693e4cd3d36f9474ff6a33cbaeef23927568f47aed1f6388d42768c875cbb1c8cf44a02ede35720865ec347f268f0373fec19a4941d6db6f47f23e4a1154c16dc791bb6a7bed4cb6848c71cd5cb34aeeae999a1a608404724d1ebd1c04b543a66e32f2f0a1edb15dae03c627b1c457b36f157e445e157db5abb3eb6deca5f4c8a80f8cc527fb0b3da3676a8cbbf2e5be574fe05dda7ebf697f0551e056dc58c529365918b41e176c479bb0000000000000000a7b90d01000000005a24985f27d452daa9b15c339f279cc9b5902ee949aec313865e26fbb4c1dd5ed650b16b0b94f07e3315eeb54c95735b233971abf6ee0ce6639d7fa7ec2a5fafa359e3f8c74dd3f3242128f69f35a7d12579962b4e4d1be8ae32a9bf9e035e6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000714daa5dc3a07359050201a1e5825bf539d0e92863b65d043b96629837a1d8e8792334bb7a15b026f27e4d7fd0bea95af76a6eaf922301ba2d055407014d24b57689f4a58dc667cdfcacb87b337fd0589c02ed2780a7e37e85325f88b37b4d6b49a70aeaa9466de8131f67c9b349fb42ecc46159f3a0a78b2e419a0c5c3b4a2a030287157f7e9c6edb947320bd3c2ae88975e979093cd14857d309e54d5aefbb6503167c90efc8c1fb63423a714d822b2b8af61931097d4b6e804501af4b9243bd5d0a1dcab20a530e7333db5c8ac46d482ca4231a5ac2001bad3287784885ebb48d5f27a13905a24ce46a3f4d061f09168d07923993bcac08f06743a99fb70ef9daba0323c26063c309d87dd469202e99e7fd11eef565432b2d1c0fa34cc267e40693ed0209c2ab673eb1e458f212bba29149f11dc80fb72f8f9be4d5c1b5d30575122e820305ef73ef7ddba35180cab24692f95f5c3290a648a06c37b2a085613693824468032ed757799c5af04c133f87e4797b5fb89a7aa675f2761b179252ea2519451e9f031497ad3cbe40d37517216fa3b5f71954058a246d97a5dec823a29957db2eb5ec56e1ccf5a9c1263ee356e95775ab36e7dc68a4dc00f90cfcf906ffff99ce91c2b2d79661db716077885359b53384620fe4a9582507defbe9d2d273c1992445ffdf3c567f8dec65e16a44aeeb316b705eb95d8a9493fedb4906c5cc414f9a4f288f140108ecbb45d4590df4f3dd3087f35d2c7fe274aaf710e60637c5e3182be37a1f94160e79158759aec5016aaa3d74fd321debd78fcf475bc0500d3af746f916d58fcb9951662adbe073264b471b28d6be5242de33c1c73dc93509bbbf977be4595a87cadd8d789d74a40baaa1ba428884114682932e8b1bea9ca0e5c372286c3b4c5c8c9bcad257b37ddbfc57046c8888da30afc0f6277896b971847ba7e52f44cbd340807559dcb17b0939bacf097959fdf4bc6be436f6b1d7e2ae8ed19f098a86eb42787d1c92bc146acb8479488d57fdaaee643af24b765238afa5a94aea11b6568caf2406e29b804f27981b6216e5cbb37b23671dc9274d48c62e2ae31ba4b2646d17f63f9331690d493d45de6792e8cfe68c634768b4f2ca0329dab550b3a4a3eefaa692f60057f5b85d368a167ff0d678779708444f2677f96cf4a6001ce6fbbfd322d6e3507834db56decb4bd8ebc87a6cc72891f11a5713687d483ce56750568a19aef71551bd7c628f5bf0b1ebebdc74f788b88d7c8f94716dee05e4f937f92bd2db90153a93540da6993610336e6112a03f93be64518aba71ce5434cd1db82d0341b9c73346ae43940354e66fbe6bb9db6c3efa024c08ceb6a1639552cc755fdf6094499e61fd0e832774f00bb14346bc08a540ac0b3ed8912bf3fd96816045abfd0fb08b9e3e8238795f4b1c9161c08f40c5be51ebdc107af271ea090c0f4746fd82782e08756f89d774919b6a3c81d2d97a644147f2e16a4d57c948f977a277abfc733b9bf6838fd6f343a5fee471b2f4fb07427e76e05a4f1bc6796d54dc15295efc0894cfeb71a239e8264b02da8becbfdc97b84a77eb79adfe92cefeff50cd5ba0ec235803fdf641336a94dacaa16d064eaba1985d959471680f507e0b0c37832a2bbcea202714691db7cf9d30f7560a2c62f9e124c136048797c146b0e6b71f249d774e440cd562e18882e738129d4aa1857e8900ed2f3ed029bab8837df043666f69551273d34b2ff2278df971664359abc60c5bc29ac0753fb1b012ae2d9d3d45276d3adb040f4113a6eed1d663784cd35c92ff77fc00c9dfd1a09abff13bb93bff175090192ee02cabbc7bdaf28093f7ba832a00b5e46bf50a9c5f651bb39dd1847b6ecdc0ede368f1127dc5326112ef54f6b481250636e3ca23ae96aa7b9af27a2855ba7a7a1234707299d2c475c1ccaafeeafc75fe1eb431ec87dd69129d0fa3a512843ad55ff8c628c936179fcdce567c84eabca5687f367e99beb1520d11c5affda93776e8e3037407ab12292392ac015e5eb13144eb364b3bcda57670960ce023fcb1b89508eea2f466774893df45a74474d46f20bff52fd9d53dfc3478e88e9416fa645f82c91464434528b35eef16aa48fb8c9d43ea0825e65639defbadc2a9cf8209b9749676973b35109c859f9832bb47a98b303682ecdb3c6eaebb666d8b772b0119267feb97de5921b4326c2b6e1f8b7569218b8ec4e816f9ae8722c2f8a369c41a84c16d2c0caaad2bfe53bf6620ea3083703d478ee9971d4d11fdb00fd9e95b86f7e4aa01000000000000000000000000654d07910ad834e954083a529bc6c7166ba6fa5bb090348ef800e104bd9dc7920ee65cd3cb0dc17b5c2ba3540d26fd5fd58151edc92702832f6d0140d58a74a40f04c8816630b61ea81e858bfe841a5c4ff74d0906d4a23ad9153f5630904f9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ad71c2fe579a9186acbf525024f80e7f5d65bdce07b71cfb6a0875291036da76b5d3603f382c66ca642e466bdd9804a15700ed8b7bbf1b94afdad959a048531907457f1b862ec562cf0c6c45037e80b2a376b392648bff2ffbe411e1b052f7de1a51f3a496aa3970d4b9784f72295d0985b4b68f7ec7e787c3c67db22be2905e022d64d858d268d2beac923796a4287e6d29c7ae43253c3c5182a73daf6cc67b5e0221939eea4d7a70f6e7a63f85a5a23cd8fccf48ecc6f25ea1fc7bba2cee9cf25a0b251aeac7fea1b21014d64c2975aa73934e8fd10a03a4f79633788a149b547b800f4adbc8ede2725e05e6ee4a7593fc34d8c8de4b019561dda4889ed4472a5066031a96a0a242655c0fbb84a20990f333666672806108f41b549fa866c0acc393ce020bde6a60422023923296426f9bc42cc16e650b76e51a767ed63674307785c39a0220206c26186effde9af5a0f48ef47e6bff7c3e6fc9921561ced573c9636e0f39031bc20ee7ea11eb0d40a66082fda8878a69178572dcc0b3a0bfe24078755586f40327c5cbddaa93afc495c4af4762f7d57fc37ba2d608e3bdbb848b72d25d03ec080e78d23ccb485e0dfee6964d6e157eb0b267fc601d1656571a5c9a09b3ea1bf1a1021c04bf3b71a5f7a9448613a7c5d878501a1da85395ad054b24ca3c24f883c9c1fc703a1229f60c2b1e22cace53e4a78a79d2677f6ba2decb9e294e24790928a458adea555c8d03f6dd33ef9907d7ec50f664ad8902c86922cd13d87b0ba8f34fe8487b59a2b319de48ddc94a6fe116ef4ccc1f677f0538ea2b1e954f71768fb5a3bd53ae482acbef153ba12c725d00251c2609de0a44964aa92c43ef32e6babd8b679e5f43d78c67110f15ea60bccfef41de11e1aec4abeb1d36dec6067ff4e66c157a459606a3ba3b15b9dacb45d35c8f5d3a443767ffcc5aa2fbda17e6f79681c3d1faea7adea12b63150e3217189c21ceafdedf5a3a0f1cea0224da6bf63a77dbc77324c55f8e8be346e5acc2044700d45729d71899f98507f3cabc18fbf103369b88e48376b450269726e2fbe6fa99ab2d5cca2fd6107cdf8a032e97a88378a864ba6dc5f294a867e05a5d98d810bbe0d85537ec1aa9262a892c64d256397058b4e28fef54c40edd6eb3466983599694dfe4eb62bf01e841640fdf7731872118df4edc580bbc55a42644e0ab0f536acbdb05d21b319994320d3ffed50a5080a02d2cd8cd8824f12ae196eee95fffafbd22e095e40da731a11ce0ae3b15d293cbd414a9cb2bbb2febe91204b313f263d54edfa56facc5da1314ad61d4708fdcc601d2a337770810a2860dde14de0715505c3ac1cc7f4fb68e1ba81406f4d0661720e7c1c8b2158e7b7365b231b06a75347d4080159359b8faf481f689caab193911d4f698391087f13d35a47502f6bdd9943f438515f3112a769a1a79b0c68d6d855ffc79494300ad0dc1c09ef557c3d0f1334967e5e77180341f38d4e4ba615260a772a313ff2adb4e0f5a17b434f70f94244e3cd135328dd1d508e40c8f21b5c51aca77d577097e9ed52889c384e1dcde762f962ee8c8ceffa2ebe18c0210ea1cce95fa51811c826a7fe13da347dc031195f2f55397b07eecd8343a91727f8c06589018841207620801902548caee8add568e7c4873638adda391c18a2141da93dbc36def038cafa5c2b537ad90cc0706d28ba4d49fc3b12bacb5f3f04257e4796c56beda0961c507b19f555148bd64b2145504741fa8eed47f6a27f87b496e37326b726c7d7b29024941119581ca1df49d207b0b2648cd922c53c1aec79fec5dea56ac234662faf5ac8801e9ca6fe5eb6d6133b2b3683116bc7cee48212e10d2ff92d5ffe0cdf2b801448644a2426f9d547de9e3707fb7fd619b98a0b1751145c80b03b0bb7226954214a9edf704c55f7acb408775164f06ccbb253de9b3a3ce83d4d1039f9457990188a3e3fb2ce3734e2ba69f9d73bc1c3ebed15c48d3de128751b9d7762a7e89a1179642476f3c88311ddfbf951f568bb663db5cd48671afb0312ddda3e9c15cdf888d9ea199e875f4d9cc6ad86c9c4def9a1f8383ed9f06ab166202869f3ff878ab05b1acc7943a54595541d17f3f8c597b5cef97b5d1c840da57009a65bc0502542c3da3b428ee5aff55f404549346da59737d8b1d520917bf4efdbd6d91c5686456eab2b29d4289dcb9a54f8f036ddd9daa1c634bd959b79a6c6195cbaebed4f12b430868977d3d011cc07055f9f47882f96ff222dfddfbd7daa12758b1653540c0e29c821fa3cf83368f11eeb7e35bc9ddc4cf7574a6b483a9a192e5b9ee7a6422706f1d9a8352564f87f14a48f8b7431de41c296c1699f7586e649feb62b350288f1dd4ad3c4584e84723380863a7e4a3c463b56a49965b0db0a5a550efb9c648a30d", "ac", 1, -941057562, "26871f674a555fec36c2b4641b631133a3f016de0fef311e7086bf5e838fb2f8"], - ["4384790001f5562fdb01eb3fff3ab35a1e98837b88146545bc3da58a442aefaa027c19061d0300000004006a5300dd28d60204ef375905000000000465516a51d216650200000000066565ac00006aea486b020000000003525265984eed02000000000763ab536565ac520000000003000000000000000031e60f0100000000f406dfc80ad580c7cd4dfa983829922b88807b3435e09f421b3057635cb584d565e49fb52295486c559fb19bb90d445d5ac12a031722a239712f90b03015ad38780f3afc0883415f24ea4ccb8224d99153441d99935e68db810b26fb8077dcbc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023338c787dd0d0e06120cce25b6a7fc3180a447d42075ac02850700c889d34f18fc363b88c464a0a958d5172dff796085e4799aae411277c5377a720571db3f8d435ce5c1d89d6ceb6ccfd07382a1a0d01adc3b44b876a6a6e66064507e096ba220f1842dffcfb35defc83727d30c24373f6f0540513c55e2e26e03cc229245b0319bfc59dfff2a2e89792b6db83675bd9197f4cbc353470c454d3c4c5371d8351021dd87004aaf5fb94d2bfd5aa18e61c6e9243ded34f21a6302d278dcf363133780b0cde4b6a2ad5e3e25c5b7772cda63634937bb493715820a59be21bd7068079200206818ba1e36537ced6f29c9cac7484d11f77c3ca37edc7b62347f04b9d7f000226fdbb9b0394af9cdf77f737f71cfb40121ea388fcf6b9dd7d751757e6a34968021dc3e74356dd0d749b3f6c319c3cfea46fd093935546053653237408097b2daf030d9ab64172fc5bf54a69b4cf67b0b8f5d1b0601f3bf7ed205f6fc475ca03f2f30326bacc0fb7516268028b4a8cc931da97c8c23b4b918c1dffd22b0893bd673c22021371109fe735d3efdfcd6fa6a2886b78dd067a288c16278bac39ffc5fbbdfd02e3bd8d8e475d37eab11f876b1748bec1fd3667eafaf7acb64a8bd2e155781a3af9f6c0a75deaf5c1871a44c857db0b7a07b0282dc33857ab0fc2559fc09574e2429a2f0ebee66b46ddb1956cce56bc9a1307dc6936383774e0ff8f80acb4ff4473284af79b1605bc0394e8b1bcc65baf584208c40ddfe5e700c508bc2c77a727ced4204f047f68dc339b7185c60d1fefc6030a83882958b628f8e61b6a78d8406ab3ede152a8d1a16a1a0a6819c9cefcbc5932f74ba2ec5bebcdf54276567b4c7870e6c261bf32834e753356bcfdfc5fcb9e080915c04a10983ed0f047bc89f635d6418fa645c5b8f1dc5a6e4dcef00372e0e2c3ad627f2fae0643f049bee8bb0f2e0eb1d9e87f70c4db1e6fefc55a9faa4e9e9746c4c4a7c986b29f43397466103da0cdd307faa82607d64d0289b08443e115a919190e66b518a7c110cef1f9684841befa3d2cb24d04a5b1987991ea1907de0f616c3312ba3674f2568144a3735dbdf451abd885cf5331803e1020f8ec7bbec524ed1b70625cd87ceb5cbec9a04b58d157e57c32eafaa413aefd3ee45e28cd519caf091e38910d3bc08e47b43bb22e0823fe32c3045281c88da961d8e8ab1a1c7513c80795ac484dbb872ab0fa43e572b9013d1adc81416d957897b4a428cc0b553df149742214e03261de276103d9496950e4a5a3103a7624d2aff9c8c8fa27efde90c869e4df2ebcaaf490d2ac3054f1ec0056e8012da7a0d915c70f5af1a430d3421b94c4fe6bccb26d1b89ba32e989c696cb69980eaa90bbe3ed4ee6b2b5d39037b6d452268ee504bb28648fa9fd55abc6c9814b9a3c54aab67563f66b253000ce1b82ef1ad9f085dde2dae8af653cb2db87ee351cf88d78824fa668e590286f55c93b50bb03d0e87e6a384a33154801472b1afa9d86643c1a36cbc028d5a5ee391266bc29408120eb30ce51bf193d727bf9f12f1e7955fadffb2baf5c197c35be52b2a179288f20fae433356625123750ed15c08315df70b31a22eb1743909d8759d97a944e02651aea642b0682efbf14703c7df0aead58678f5e1bcf1d24582cbbde8e462ac2aa8c4806f6286be744c69d16fd718b0268c9acdb5ec5faeb273c03186dd8e272e35a6bfe377b184339a09c84a0dc44f81b77dd32c40a2789c034e01e2b0b2cebf66a02741515f2a2f323b6d1258119337cb44e26d93ed6d38c4c4094e57e37cd57ac138ff3dfb85dd180a317a107333e6454b07d2b2de26d32e5c18ed2dae6393fcdee7d17a18f7dbcfef317be650bbd3fee34c53ba827f6f7c5ebe1a14edbe41b968ae16dc60f4d12961facee91f100876b4a690fd64f250d633b8af8d62e38a8875965c04e31de21d9c40fda23da4b859cc90e0f56d54441743ab4e0ccef7915df569af50306e3bf6637d505618b513b289dcfb2ce13cc28f7a2b024f0a093608f2c64c09066424fd7780745aef4e84a7cdcee263cf27d77ad7a153a004915bddc6596a1a024a1132557718fef9cd72bd92e6e4cde2187c357e906c649f032b218a7a0cfe4ba27d908835829461b8fc23c75b7c2508802494dd5192f88005a76958ee675d758a444779f70e2f6310ad51dc79f2897a430002df37a6a2281e2c5af79035d7f1a6c596e1c49b604deda44ce70a81be2e73fe4e56640762143ca011cd042257aa6e5a08392505c742d972f0686bc943b7fe404000000000000000000000000786ac66066bf1e01e71fc83bb1b2117d5dda142e509ca517b631f9e15d5c97d9b83e45b12e6cd1c3e97de311be40d8810311e5c2cea6481fd9923e15996940138b6100cad0757a6b2b7a44e6f31a8c0f31418f2ba3e12e2bb7f860d78bf122a700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2459fe73efba355eac24fe9a393d5224641799ecfedde15c8b8db6db5ff44b1ea52fa150d73dfb1b5587cfae4ca644087998fc42b2733cae776c9c44ba3a267b1b0e5d7ea3a91d7a0c1984018f3479ec5fee96a9fdb3cf5a5ef7eeda754d3e491adf2f24b5f0c38c8a8ae4100da27156984f12141365366a2242556177ccae202112576da80054eb52560d837dd2869f5da6f224420991d1dc402ae522fec95b9022d75568cc9b1e7f762cec4c173cc2456f8b45b1b08c2ace058b2c9c7188194e40b0104f0353c8133253d21b51a8dfae97b1f27a7bbe1132b2731103683d204a7172ad9f04cad8cb6b25311a428597f39144687b7e202fa6ede5ae968772588e75d022ab725479d6c46961f28ebf7cb48c5e3adb7761670ee711672cef214eefe4d1f03163438c334e5d014c606f49f740c12e0d2b631bf745e12b48cc324f3a9cc4e4102129275a0a9fafbdb223667aa7bdb49825051ab0018a3e09b3421a620b9982212022de7d9b239189990cad2ec85ffb244dd42d0c7d96eb0ff57265ad7fd27ccca2702163e1352d486eee55ccc18187d8dc819cb4f1512d6c4802ddea01cc600d43e94c6c6d7d68b577c77cbf17537f040541e517559ca79f82699c755e24cc965a2029275ba27d76a9ba588bfd9f00026cea0eb955314c38d24a6d583acc30518bb2c7ee74b8d762f6c6508bf163a628a4bda8fa947b3413a8b4775c9e5ec9f5658074d9f9f8e1b96f15084e172a71f558684bf3f97e3e5b6ad029a03ca25f76497624a77f372c23de89915773be639389b2a688a36e59438f9a6bbd4eb15f6ea7480e7cb26a0c3733c8324b7ac89b39ca58be9a5a2d09b89e063df97419ddb42f01dd13422d4fbbe6f93dab73907120c3f84c1adbca36ef54e2e511a0d3087bdb3b1b856727281da6480fa40fe2096e9a2c8c61dc2c674d90ac3eae798ae670dce45f5cb9d6aa8a63224c02d8a36cec56e9bdb122fbf5ddeb988b4023f1e0c84791f13736f1a566d146fcf7bcc367ab662c58d9b713b0de35c05fa939d84f17446133dbff922ab7602b927a7d4b721ab5d893f2e720a582a26e597173c0ec3e9f023f8f7396caeb35053ac15ade6c753d7554652f20f515015d3a8b9e70244aa2ce7d901155893a0fcdfbbc78bd38488fe30987ed6ab880f993e85679d81716426172d38e2659b422338510485cec37373a03f0fda92788fab9fc32ee20f5ffc17c53ea19b8f9c6634c5890689e2c93dc9cb8c2df73c27e34f45e0bd6ccad43deaebfb8727f0eebadb837b625ba092dcb1f69316686964235e52463a43bfab116f5b8476e8c826dbb4edc02673dfd97f7b89a1d1abee42c9fc9ef1117a7e9ec22049616a3bae6ae06d28086f174ad10fae70d99787bb503703dbab0e1cf93beecf99897a70e41bcb26354bb6f6bdb56f9e4ce55639ad961c13cfeefe9a5dc253722122e89eb85e57b75ff8f5a9e9aedc7b43b039239a42915fb9873040676289a90deb9524bcffb3e0ece801e937fbd1703a24c22b69f43095a1bf06978228bb613951f6d3461b5ba63c984bbdf3487c8dd430f39e6fb849e13d1153c090c58471482c5e57ef844024fcd0e932e1c6d3a559c829faab36d590551383def8a98083cdefc65bdde0d492e60f9bd88564ad7619f79a7faea7045a340c17404a864eec44a69dfa693e9189e0e09ea071de355f24e5c6c989de46215aa9c7adf1c5f008f43de8232194df2ff7f31c19cdffbe3d4e15fd40ada7b88703f1c9371271d780f4965c05215996f5aa23adf5212350d6b30aac35cded0ab7907efbacd2a6adb30dfe4ba44b331dd767365b654980929c48ff57d7d27b3ad93c8e4201e29e2531ca9aa22eb173fd5ffbf80638e32749e30f074eace7d51dcf3d9f8b6f50c4cf1c057fbc816b70932ea097064a998e9ef09a36d7a1614ec722c51b727e55bf1bc0c072b5132e9c0b5b14a080aff97a25115fef9f46cb2f1cf6ae6ed6d156acc961f8708879e475f6c915d842aef14e9799f5d0c4227ecb3363e039108342023e235c65225f4909a35ff29600888d3ca3aa74d6df9d3f0a2e026c428bcfa8e087405d91fadd5f08bd5228066153d47f6afb91582fbe75785541e93ea1c9374778c781f741acc0a76c906ee2b5e615c0520175a67302116747033b98e66c4cacd98aef3954abcc55d628bdfca5b43b317e447f599fae07dcf76608c8948f3a8762f3dbe5777ff65df025a486dca66bf8ffa6d7bee405fbcec684c15813cd483b51988ea4d2e1e919199c441964a8bfc47320d14a3d3d87130500000000000000000000000053dad622af7576d00907ad969b63b1bb5040c14d4dfeedb2ba37b7c39ce458fbf1dbfa37df21a50e953871f07224ed37fe03251354c31d8f07d2110783427c7755cbb62212bad8ba6f121f5f94302050137b23cb9aa7fca9826ad473ad99e82d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a1e2411bd703b1b6a31dd4dee9d41c18a5a8d5b329f4e88fca9bb03af65ffdf3444e3f97f9e67c35d0709b3761ca822f3c60f97cca74025370cde2a28f57737a9f12774cfa8fe3bd5d2aea4920feaa646c202a69e265becb260979a5e718cc1b14c6cd8a7bfe522586ddd0acb60497903d81f8e716d30d64b61dac84b99426f022a40a892a21dcbff64bcf4f8c3b50c5f59aafe70999e926e5052a4ef2bc73277032479eca488d960b1bd127cf9cabf59b3ab5a3d18b77f4ebd8101de1e0e43661a0a0ce45a518c846d7f20dae93f938cae4e7eac9385873511c265a16ee49dee614c06444d4bcb2a3ef186e690a70ec1b226321673954ad629583a9374c6fda2347103128f7d6bf6347b2ac12ba70caa9c73bf05768f50d379f89fd9877f45cc81e90702291f0c3ba75c9801cdec05dbfc0c322a4b090650eb4e7555d4f416744a70c0a7020ce6062e161ae075e62d5edfcde4f3ea9ea0a59ef9ea0f01a046b78c333a1ba1022fdc73329259a4877172d8a246808938911c98937c2ce17e5718170e29625a7e02186fd9391ba0a1cfcc5cc3f9082a0a12f22813cd316ff0a7b3bde27dbe5c652b98857d05b65adce84900e292e62388542f1136fb9b11c6a3948d7019a95513012f688c2e4757d6b906689ab14a125b6473fd64fd5088fa57e12ecac8cfa14aa7b6745569df3272c812fbd2dd12c3acfe9b9c42177132087613ce8441c9bbe804afc25744f12d30857e2bb26da15f944151fac959e863ebd4c9a16698aa0f0718fc00b50c589eb56b65c028de6ffe7e393e705e9a248fd954863e3bf4550bc99d157e805d2e1d54a5b177fe6b430d2abb32c662013a10f1ce799ffc963b7dbe38a993cdce4b49edcbaf975df0ea755ca6134724900749802033f9179504effecf5342b421ae85e2b4376b1d41ca011de5190b27b79099aa3e3c2c9a799a01e9edbefd1145b3d5d0cd2e0390d9e6d4863346d6f66b04465f7fa1c9bf414d4d9a0df780c801aa1a6560714eadf342a7f08db210cfff5467cb2fc0f0df3393550068f537ff5447eae7988067aee1c4c98a35eeafc9bcc6b0d5d7243d74f61509974b34ba4ed47ebaf53a8ebe4015d70634f65afd75aec95192a2ff1f3d68987c86659b91e937efff58f40badbf2cd293e75ba21085fca99acd78785c456789e9ae37bfa888d4cf5707b292c2d453e5c0152ff9c0973f4ad3306a963142be64ef0554e4a447ed9ce19ebf849eae2c1d1db4fdf7387e74541ab61759610995e1dde6e59f4d8815456deb5a4a56dd226867de4666a9d9e37e944c50cae995c4ffd31ab9ebf6d7a86d4850c19596a7773a88463c1d43424e54b3e57bcabffece95b4938cf33fb06f8f5a1a3556d2536d2c526ba3a407170fe5e2df44193365722884263a7a3a90e3d557378a16d1cb5bf4edc4b09657e41030827c96cd518cc0ebd5ad13a4419a15baabfc21e2b764cff1552bddd36fbf61bd8033dfd70073326bcf2a1e49060403f9aadf77b3bd59a041ebf9662fa5e1bba70b3cdae08ab97895d0a23bcb11163bd2beff8ead5cfcc4f05ac0c221efc8be662ad16f907cf896c64caca1ed265d6ec09dda36034e38ccffd1d29785b42c707fd1b10b31abd888a1c042542ecaf4e139bf38eb869295725bf3a7c30aa7d90211c3686ff3fe06bbbc7d05532da3e2bb55cd7926c06ae6cb8e6a3a5d92ec0e70121964b3c418efc25e681ec5ea0fe7b5bd02acff3316aa109b7d92dabbaba05cece217b71e0fd576723927fa3fac59bc68c8adf4b817a96578fa083e61d1ada3db9bf93ac25b5486d2ba24a8f6d16dca059ad18f68c2ad05e48d3d74749fd458f02f2af8feffc7e73ef6c04a74379b3c2867497e21466c8ef08697d9c5b4f1c59647d310cadd8d6b22598e8f313a3e1c78dbc55590b0fa33500fb4ca7c06614a615a34c18a1d6eb76b534947cfdce82a73b10a4480fba1bccded4324f191a9cbbaac2f2df877685cb39a7aa86add7a5ec451f3a973988b6755ac580c642ca38cb0424ff2187e3b00b3836a12529496468e36f30c5f44e0d4e42a84f67317025138e9cdc40f1a9a1bec50d20d8b304bfb024e375752a22a0c51d208034a4b541b289c2015298147c96ebbd34354851e6a2e2a812747b3ca0d4789690d2ad13487c6c4277de65d872fcba58646d4b75640b8940aa642f9d77a80e1d60e7020af6de68329b8f6599bd1a71ba518996d5e036a79fe3d609eade5b85444c53a3cacfa2cfde1772ddc5aae035375d26a459e1d74697855055840965672fcaf126f8462dfbc2b2d32a76649f0219a8e6613117cab1931131bb76ab517a55255b66207c60554017b7626315d28e4ad843f8914e1d9afb1e3bdd69381949975f3c3461e7abf28104bc26b423c9dddeb32b9bc808071dec590449db4b877eeacbf4204", "6553535363", 0, -1507154841, "0d7d9d163b83010bd7e82cb154af0aaa63ae35b472e6567086dd9ae49d608860"], - ["1cb311e5047a5625e2da9147245b52634d20d0b8d21bab797289b8aaef5d3c0ba2a628af1502000000070051ac65636aabffffffff7b513e1e92edf8b8e5f776b845aa1a5745f113f87d7c4acf2e1342e6150493a203000000026a5201115cb0088bd443beeca6931df2c269a87679b5054daff72e3aebb8605adda306aa0a2a03000000026552f10ca9ebcb16268af101bb686875452127fbca63bf625e93c4f37875bb479efdf66c78d00100000008536a51526363ababe96d24c8047080eb01000000000253636c723c0200000000009f9c0f04000000000951515351ab00abab52098a5a01000000000651ac00656aac00000000", "0065ac656a5353", 0, -1533137217, "274c7ce6beaf089bebe36c0fe30e8811220355eec7da101d2e56b42a7c65fc78"], - ["a76f7d83022402bda653adc5c38985e8094e0aa9346a741cb421043c4e49689ec019e9e3530300000006520051516563dbe0f3c21616e05c0d56139c9211f045e180a1132bcd3d39ee70b806489654c537e3181900000000036a5365ffffffff021583fc010000000007535351ac650053713ec7010000000004005253ab0d657db8", "5163", 0, 962319507, "d0b4e917270c5b7dfe356dd7475733ca31bc07631c48b55a117755e57b162fb8"], - ["d56f8d3601774412e534f06fa24b8b9ffd01c6290d3c2b3407ed051f8f9c9e815d8276e3b9010000000165ffffffff01e92652040000000008ac5363536a52006383d49a8102240a090100000000000000000000000066ca4d44de2ee1bdc95b335420997333912d01856459818d9650b06231f4de4173dce3beca5445d4425a28e5b7698b1cc93d9238b700fed61b173ba6cdedff3a2c988826c9edfb33e4c6ea5d375ea149808fac6e17f9686656ba2d75b0adc61100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000895c353d7313751376f368cc743abf1936c29010e740848bf37d3931e22237a7f544f15ed547f7c5b0185f33c589c9ac9cd7c3f1a44fbcfcc68ac96e02bfc17244c624b0a33de554564d86e04fa56757117f21cd65b28109bf0b8923b7d94e56fd170cd3bad114a7ff844b3aa9b23a9dfde7d8a03a753d3d6ce598b528d67267031da884755dd064c78bc6cc96d6e8e4c60c28bdffc2a915fbef42ec6579c474a0022c047fb49144eee159a87f4b8fac2140cd1eb96910140e588505441253387a440a23c56081df7f470bbfaa5f6e038cd2d3cd0e20cd8a0b336e7d6d375c0ab5f7f627377e6bda8e106156c37925817d4030f8bdd0cef87e3c97d2b4c52c07565e94022ab91cb24adefb3adf90bd2ea25d66fccd32b895f641cfb0423045e23d40c23903248aa50fd29e1baad51961d32168e72b5fa94ce002ac367c7aefb82c23a8d93403230c95c440f850f5b3f58c3e083073cf54992cf90641e27f75c2e72f7236cf94031e3ee062b6a81a64f8a49ed23502d20a980fe081b8fa8fb96c532e762bd5d857030c6acb5d6ccb16938d1830e2902fa44584bddd1a75c18534948bc87d9e6ae23fb477c4ec55ef21317aa8dc6ad1d70c98f14b00a91317b8a7a6c6b50ed8130ccb6e2127d0a45dab00815454fe11fd9d0adf06c924ac229eb4fd41765f03028133a18a5578eecf1dbe1c50b7de021b3f8285af17738995ba3a3461f44e220f1cd09aef2997b810845f76b5de54b00fa48e4a98f02a0eb5625030775bae352d7d8e456acaf7b8dc829df5beff8a31c1921a2396a288b3536ab265f781948a63147a5f26be895602e4f5f225cc980ce1194c5a02512106b51c8cca0e84ba63caaa7238a2fa0d9077196c7f1a46e34de0cdf680280ef126c01e2a729212695613b95270ad52f2bf902b32a75cce2b1a94d5f5f604a230a1a0ee384df54c35a4a2c8754dacc5b12d1e884997c87c38e10db2c8e72cc0e90e618b6b749e3dbe592a4412153cadf179171ead24dce08e9b5e4195b5f05f411ac8f3db98c60a07e41de2604e61c846811510c8d0587baccfc0334603587a0f61b2bed50ee23477d20b1dc285ce35f49ace3513af2a184636435bf81468e17f6866717feb085a42e840e67e5279087faf344def3aa82f32080dfdda8fc777b2b36aaf5e81949bbe1108dfaf2ed2c4965445cc5df8814aecac8fecc35c2d3a1f70bb9ff31ede8aa2f12cd38504b24b23ea4e0472be6697f8d0b768a1b53a5dad220292bb2bcef0174965825f27eeeea8238533e92cd03d19e8d4f888cc6f445eea839f5672c6ac76b7958eee4f3877ccc9f4a67717aeabb18ca3e7a650fa8aa6480a8cb861922f77f1b9ed8ea940d1fd68d49d9c0064fd75771a0ead8fa1be4c1db13abd501f07ae0565fa65c95905d7edca9bd101ada53d855818cb37d7b415aafa4531d5f8305ec1ebb47c50c86f700704c2d90a8444f3b6d240949cc8dd4f2b97836b4a82e2504c9d71562f01cb75529c3dc7f6c248689bbbf9f4f24ccae87f45b39034bad4de0ea3047382649870d8e0f915d9f5b4e1ab6baeca7187707afa6adb04420a3e3478889f1bec57ebbe8a6ad860a6ede2aa942b4f5df1129b73c81e96b17f484c93835ed3d4a488b13d3b27a65d5f187b983f7490431a6199c5e8abfdbb7c14048d28ce63a3d9c1cee3e625a08b8bff9b1d5df6f7c3e2c60d6d5a2757eee6caf05c96870ee1b609388f2160894facf165ca65af2523a9128e6d1d42d6822b1ca0128ef10c513848d84df10515074e932a7cb9059bacc91ddc66c84b9c84aecb7863bc01ab86699127e72d615d52fe13b26142295e024e5a93be2fc847d5d6f92b24dfcc96af2c3fc92cd01f3379325e06f7cae0bfcf8086473353fcf657d0c071a9aee9e9e3b9a131645e1c439d0d6e9a6c5648c097145888f2b92e9f2ccaff420e6370e8815a7f78dc8fd6720d8550a938faa954eac2712a0760abcae6a3add93af1e1f44b5f2fb54962afb39e2a9b65aed7af11487815b16d8638c9e10bc42a8fd8dbe98cfe86ffbecb959c57615281f6d67bbe2f8680f0c504fe5c26d2c064cb0b5590ba75ac8f710dbd7d7c6efb87ceb5d9a3e8daca4eea117003c5bedb6867bcca2d06e170adaaeaf2df7c744fa8c17d707cada10da9eca1d79d0fd90bdd32b89952ee7992fa245a553658add8f462c69d7819d3e98a1541408c2305f1a28c613acce628e57e9ddd079a5638d40f034272b401baff84579e4db6f42f43c0093a91dd3ee3a3709395d87e571817b8eb725de39ace2a0000000000000000c280da0200000000462d935c1b7032a04f09cece0fdf082523ec095050108e936398ca77e62267a9d1f02b83d2912ee2335796ba509b0ac34d434316a351d59e72b2b2d223366b0da4d301095db6629fbd4c6593a54310237fc93210d3f7fc5061efae7657205bf500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bd38c10a581a2d916ffc2623ae6e36f96c80590ce3859edef89702154bdbf877897685240cb018c8784b7f221aa7e25825d1a0427ce25e47558212a436b9a57ebeafc2fae3f4ee520ade8e2630d334ecb0dc9ce20b29136d813122fb91bcc9a1eebed83a48626c863f87ea23c117c64f4c8c0b4d7eaa3015bf8b3b7dce0addb20327446d624f686869bbc7ec3f61106c884861d3b84082860d23cace9af9ece24c03064d014044cbf62a8e44daf60f0f9a71e275dd580a7fd04dfe3e4cb516bdac4a0b27e74dfc66f211c09ed7a7acb79b67dd12dfc929ccf56556a0f0ef5dbc11e287049a0e635565af59cc9b66834bdb6094ace75c172c2216a32a74df40c2e9b55e032fd842b756da797a4839428f0d2b77eee6d37841e0b20a612348e6bb0f7ede2b02073910b7a4615297beaae6fe7408f72085b40d5c3a227ba60f4acd490c842030020080066d76a0c29d5b3bac1dfbc468667deef0558f02547a88fcc624ae7c11ce0201c90971ee0f3cec8a59fdf619d252e83ed0b98d91ec6a0ba0097d36425121cf03100c99a14847ae04059909aa08efeef66118df68a90b3099fb6e6d3caed1e43c21908a48859ed018d2bc5fe1666d00cc0d6430e4661525fd6437308171f2b7b85635bc7fd0ad186e4cfa034d2de329d41ee8a8349dd04deb34b76bfdaca035b8d173efd4e4add5d1fa3069a21fd7bc3b589f949cd436be6addb3c23019e6d831ec45638e2292d5bea41395f7e3f0de4937b27b8b6f8c6b670afb13e2dbb05eaa12fd93bd161bb6296c5d7194a83f3ac3ca76cae229caf7765a767c6fc6f09d3518840ec5be739f4b17a1d2345cf927fb67d23865f00975c9868b24c1877bdc3e9bd371c8191090a9e0433ec0712a8815863ba4b1518bfa196a436ea8d164b887384cc7784db7229aabf18111066e6f680fd83a74b69c853a0e2c20552870130093b0e06730614a9e9397c9cac16e36daf7b3107e34520981d01bf197cdf26b2056711c4d5b56724d3db98b9c7138c2481b134239cd54d42ae5094e25174e224dfb2cc4355581770316f4a9db417683b7f4f31cd5f49dba7f93bf0a3fe62b85de23de21f439ac7769248cd9929cadf6b705f36eb4d537230869728cfc109f5df7e839e36682c1a4cae9472657dbf900038279a2bf616cdd93fbb03c15b29f44e806b4427e2bd35d2e4202ef2b1339dcef0a369e05f3f5dc89d110181319ae54b4b4798554452bfd6bc3ac2b91f4de92efbf535f5ca223e8940008c8b35376b8a02e18799cb6b00964cda27e5c699f2642b6cfd588df943de99abefdb702d2c44a2fd0df2f093cfe89c30ad77a4b4508ca794207949068bb96a79bfb8100372e26e16fa6b397e0cb8c91974734a9646b515b892d5fe725032b4ea614563e59d8b1abcb97650feed6b41b55765cabad4397f8f3ff51f5e2f0456c430e324e1697d4d287d1b2656480e0f404e0512f18449b170925f5fa95fd5f51dc8a88f2b598e6d46cf12c74050ae942c2a2c0f831590c458ff30807d497ece2ed0eb4ce2d68b2947f1029e692089aee44024711b51378cc2635f1929dfa41a1b8dcbc8406fb559af1acd2a52eee96fac53a6a32a25e327a0b32b49577ffa08b5a1715bd2ff126b4c8c9f2e6763883a6c541e200af43593d486c646d21d67a0836b72a24ba402d9d4f7a5285005b7dd9228ff5f333545eb65f23caca641bea1fde914477c4c602f21b9aad9e9dbb0f8ff4ba3e1e2ca0667fff5f706b508a3e6a7fd5540baddcd7dcf904b744d87de60fa4f56fddb702fdbfb45ffee6090bbb23ae6da4b2d518b4c1888d5f42ce13484184e31f79414e3d232ef429d52615a0f50bab4252c97a5248b3761b9cf5e8f2dd16d732166d168d8cac11d3b5634b2ce52f7b1278d24713770953e2988dda6435c1bd3913cfdaea8404176738ed111b8ff41c12afd2fe7172759948b1b67782c837763b81eebe297d4e74db72603af0cbde50984f4aac26df214119f964dd8599d9e75b9b56dce2077eaf0b9d63355d4ac24bca1f2b6899da15266eafff492ee0247e06876a729550ddf1dff7c39adad548add69da80d6c584fa8a8918348f5030b56bf31c6ce88f555392e327e8b1c1d847f57388c1fc9de02170447ded4b1ed6d21f07c18ff38045dff82b426c7ee894629dc2365f9a8dba37d4341225c67a750c746615497650f048de89c07b1422910607d3aa9599c354a1ed35a40af07d8829a87e3ab254af724dbdcf49bd00d579fb22a5a28478883c3c5e62075ddc6fec307f8f1ffefc4354282894dffc73c1b47265dab7a860a296a4849cc6bcd21ce02cd6822e8495cef7241e3731744c7e8605d53d387d0a91e3e1baf99ff3fc6de8f045983011dad0fd28a87587c71646d024d3c76fc1e3521ec6f6572d674819e30e29f1c804c85ab0c", "00", 0, -189858871, "31302b0b5bf82d67fa0bc55dfba2b0212d1653f1b770d2af757be6fbb320012e"], - ["ba854ed102e09b9dd6135120c4c23c1445579c344491c16c375368da910f4fa936ac795d1b0300000000af79ff6b2b18f0f81a764666eb9a36b178d1176b5b1a72cdb9c5d15f57ef37e52a2283e60000000008ab00ac6300636a531f433c1c03dfdd04020000000004006563519310df010000000004ac51526351a9140200000000075200ac516a6a5200000000", "53ac53", 0, -246097963, "432f48bd7288c5fcf8084474c909ec7d03c870adc68fa215d82cd8e15d01bcc7"], - ["1844eb77044a356885d20df7d397b1daf22acb88612db507ce0874c5a7a646e243ca96e52300000000086551006a51ac5252ffffffff7799b64c6b10cc6a44ccde733895b8ab90ea25f4ad7de40cb327739e245567790100000007656a6a525300abd883acbd8b944e4717076fe8f584f310b7c56cae467d4daf345a6472d755069f8f36d2e2030000000253abfc40b8223a41eb2100bdd108d11723cf1481f95812c5401c75d975d8191b2c639abc980400000000086552ab00ab65ac53ffffffff02b5f299000000000003005353f79c320300000000086a00656a63abac5300000000016b15490100000000000000000000000064e674a2b26ed87cde7569a56aad8dde4ff4bcc6bc9676664b4abbb582408f66ffd8f64bc0ac82afeb8066b22601be915f045925c9b41445561667385c2841edb2bf690a57552739d7b92901e5f1cb5fa4b66c8e9a862611a30dcedb2bc85eb100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000623583f9c245ae79a99bf38086d1d9d2545530b76a2ad2243cbe39a8f41bc291a985b3faae604523cb14c67546c16f9f470bdec10c8ac17828ddadeaf1fb05f79001e43a4b2ff36260e8232170015cb748e7d93789d94cffc1ebc0df57f0428fc9cf3a7362e2ac2fd7a245d09182fa5a2762397e4811e1ccf85f16b0ff983e70022110c45da1334755c6845fba465c8a42465377edceb3363e8e39f74c3ff60f240325d939b881de83c9fbf96444e845b20ac31e8dd74f87bbcc1337ba3b77a500560a2d5ae4abdfcc5390878e72545e94541a10d86098d9c76b77cb390730762c65a90c8d55e70ab3aec5738e5861f15186b9bb98062762f8b5433886da56390b4b4802272e64f893630cec45b6b46e74591571b21e67471aed2c3c86642fda855c0338032204cc747984319cd6e61c2a84747709ea862c7dc71c7b9d7cc937ed6c223cdd032719742cf7892d372b33a2e541e1bc581102c863050326d8be8628fad5ea3d5b0328a7cfe1e1d7d23388d34d88984f8cc16eb410a2df2a65edcf0306ffa0266f5b0212146c3eb1ca53077c28d7ac23ec1413eb1b5c39496a17d6c217d0bf467bcfafb84e3052fb2f41b09def5c06c5d313aa9db9facfed8bb76e4c9c9b7bbf71584709f2220ae1f8d4edbd1f046ef2d05d586a2a795d9586ebea37013542dcb6ea077a6291c5c3e02d64782bc5b2d6c88ee21f23aeb197409f86eac81209e65ad00066974534b23b8da8f8ba3277c4821cc6df2eb769b449bbad6a1d8c67edac6cce8927518fe6f4b2494c4a8deebb744992af16947a086b1cd03520485effe4bbe5d30bc114e841740bd92118ed98a419c050b1e6911030911e1200fb277d76c283f032070f5d205cae1f96ef0cedea8124b95288d13c71e65852374c9098c3154086f0ced0fc72b30914ac849f171a20201e65bbfda64bdcd7f5e3a4bc4d061002ec716458b49643a4ad9161acefff0e4c9ecb8da70bd80ede51259c70da3a290e4b74795ac609913ac8766ca40aa7344375e288eb270793d0a544fd1817aa5c3e0f89d2ff558e5251a2c3f83d6a3bddf64023be8969de0d23441fa4e3a10a4f34947abc5ddb4c22d22dafee4eaf59620b3d3d2b892e20318a4729ec6e9904b5d7c0f7a6a10950643fcde9dea4e790f585d6c8143f56401468418fef78af88b60a3a13d1288b0c7214e2ff04f136df3d5452010d2af16e32156f41b7b1f48f719d4992db8f2c18c7d34c6c828da2dc0c2dfe08957a931005741b2b95c5bbfaf1d0100b2e735b96c9fc33d5d878b6a26fcfc621a62a73bca43b1352480e611e26813ba6b1fb074b07d8fdf8c17d9445b226625d19fbf8d2ee348c43cd58f782fb6a02d3f87ab816db6ffd5dd0d687c14c3307b6a9b792386be0e7f9cbad14f1bfda7e97394861d523caca4f08b85650dbffafc2178a517ba27ab599f77defb07a21858aeb0143c169abe80cdf24fece0b917c55185048fe83f1cc8e5312fbbd0633589340123d3901dec87400c88d118c01414c2098f4be8c47a1a1b01656f768e26a6a144f07b107c9b2f33480053991c675a95475077c0d26f2cd602c84bf23cb86f05538b6cd3af77a706bd9c680a289978297e4322e9ceb76388a0a37fe2cfc250e3ca667fbf91208254ff60f6c5a6ec718521eadb74361dd4b9d9585cc02ea5670fa80aebd94d3d9aea06429d8301cb70ca89d5a828c9c9cb194e24b6e270d8f5b61c5e6206680f6044c13f1ce81018aaefdb08e2ba2cee4f233c2c6652e9b1894930c06693c45fec9a5543b1a702ff37df7436a03cd592f8ee1bd98d5a8e9e51c071b195ae308b73ac377fcc85b91cef3ae84b6b958b005e76bc8616fc9d53d0a9c3be697a09208b9653c72120c2faeb3624799c8576eeed1593d9ffeccf248448f1f26fa696ab3051a0c49ecf5399f38fb30e04ebedcfb4e0e4f022696d8a53e1d742b9cc36f9070d71c4ea28c5cfe45229491c0bf138c3fe5c605d9d1e7b6e7c29b45103e87375f0c5c11a3168c9fba3db9c291c7d47132f1d3349fbb4762aba067df20b04c4f89f4abe0ea09420dd256cbe36c8c2c59d5c3bc77f287f4c38756b1dcab58c06a607b46ccdcfc2d3482d94c3110dcddc295411a04d75cfe0c583a6d17381d4431693379f71ffa574ab2da15d9852a861ab6731813c49fd06dd62a9fb23034554553708761136f2ca7c3a42c55d8dfe56f1633a7df1d62a4969cb16ab6d8596c03f540eca9e09837a80bbb598e8f08616b2889ef026fd8f1a5abf11cf7509647d8a79d70cde076f1a5bc7d89f4c17c6cc4ef6b79fbaa4439d8adf6d3bd31c71e13f91fac87179e0a35114d12b13dd1936bc3010d7c3d245c2cdc22315fd891f4d5672a73cf34369180d0a02d697cc3bce87d521e822e631bf92c02e11548aafef2ff6a758fa76496970f", "52ac51516a536a", 3, 939494798, "0cc1c75c55ed572b62d93d95faa69b82971f71f69f3fef35b033872ad0093594"], - ["d0c7fdd802806f7925c45fb9d42a514532fe7a16e1fd1d6b0c2ab1081a9e8f081afeafee9c01000000046a516a6370310e30d06c88af702aef9ae0b9711ed9e91d5630bdb657bf541ebb5a70662e658e10f10100000000ffffffff044fce5404000000000652635151ab6551884703000000000100daf1e404000000000800516563ac526500928407000000000008525363656aab6553d833e4fd", "5151526a5251", 0, -1112764220, "f1e168d185e3ed217c2cd57f729a5ecc427bbd7973ada07c00a65e7612159106"], - ["38fbda0203d874f265c6ee03c28a4fe8b71dfd8fb00a8f4a920541ba605063b8fda1c8f9d50300000008ac5365ac0051005338ea37825fd9ccf602aab90700a02388682124da8140039f82745a9e1eee038a5ba4b8230000000007ab6565516553ac57cac3617241d072e65f2f391c1008887739288119b104726c0b447246b4d9dde040526c0200000006536a6a00ac0088acb7f703046491050000000009536351ab52526a51aba6ded7050000000002526a88ec640400000000075300ab63516352db0aa67f020000000000000000c1c2810000000000ac55ef555246a722913c5058e92b86a23e518b6c47a9d68e422da0295c2010d7dc73370a65398c3585531fb23c940b2b41248484d5f652b7f5f2f09bc766e6773cbf48f800118d186f52a48dfa3ef8dd9f21a9565f0ad7ca82dac19eaa81025200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b9f221c2289df639cb4b7b1fc9981d35d83925aa8a0459ea766c053d8a1f6492024e6d34dea6dd6178319dc51e573869b90332201d5841c53c3f896c48b4827a3b22c14e2c2982858b1e341b1a705c74c1cb867b1dad111c5e1fdef8a003cf15f2630628b710faeba0a62defc021f649ed15281fe72c2768f77ddc0f0a5b8ade031c995a7081c1f3e819d642e81da79d972e071eaec43f19f204db62568394304e0202d14751fbe990780c63e02f837e50cd8fae7393f51a70e6df2787b091297f6a0b1ebf55acd038e56fe23c88376af938972e3aa9d83defdf6df2c9f7445663e1b40d2d8df02e5349a27c0b65b4d449955ac4c261b80735f4379e38cf1cca4919d4021fbf6929c6fda8170bc5de4f6ab529aaf4a8012ab8c01e01239d413d9463c52e0209d38e27467d469677e87459bbf42524d98ccde5389dd8d57974efad6a5cae9d0306c98690d930d1adfb4f62b812acaff6de2bfe083f394d87b157a8fb792f82260304d3a9763767e98d9a3c2464a8bba418fd2c2c884631f2f495a7c80b72ef0f080322aa40b4b7ffdde46758c4c3c1813c3049ea2863fe6ba0595a77de02ec36196d19671070010d30a0211535c70a67b525ff4f334998186a99c04f9fa9d50978d573d005d475dba359efe64831e93bb57cf6a63765f78f93d356d352761041525de032979f9aa42f2805440b1cc503bebbddd70f24017bdb49c11298effacb06d8b7c2171f6b79593102415e98ba8eae37d6db7a3a2535d4bed80107affabc694d9417b683cacca2c11dd50c08d16cfd494d420ba2df51f59e29c6380725e7e67cce117ac0527af53f9c0e85e56db0649ac7d798bfc40bc89bd034f19ae7e42af712aa1bf65469a346b1de39d3dcb58fbe7aa83afcb4e8046154b12dd5061eabcf4943d8806700557282d0bfadc49f59e92312e030b65ae9f5604d410322cd5006df7975d8166f98c36128b86d9d168e0014ec1e90d80892e88463b54710b5080a7751253c37e8cf58f77a84f24d89247d5adb3ac698ba92edd352f223c99c055f032edaac846a941ed130f51bbdd6f2249b189cc634e7379bf87076b7a95c2580b99dd13a72fde3d1691d178b0769930d948a5c9e70f977c79ecaabae7743da6a80d1a7450787871f021a7d5eac4610534ecc9883c0ed56f2581ba541a89db58bade7043338b39ad8303350d7a5bf773cf9f899c4a78a2ef4d350e4814059944867974ec64e07c2ffc5c1fdb49c5261824f6fa356b28a3f2a712a7f4d0fff047ebb4071a5fdafaf1a79cec03a3b0f9c36b2c62a82730b09b184850cb09a71ea47d4ca06dc0c6394030395e224113ed32581c253708bdc173c9a4efe26e9f8a13e397f159fd2c0c4646b77ec9bcc302f579b1db83692f6923200d8194c00233b2da4581a144b4e695f69461205941ad81f7e31b2ced2209e489f60e19c97f5e0ac01915e58a140812750265ad40ec10276cc9d8be4b50fb0334c5dcd51b81750568685748c1df8480994ba8362e40a48db34367682519e430b58fca1450142ddad64f38c1f57c4e85384b80e4a2a28d12c83534e3259953e288ed7431482262199bee33654d290c8263b7864fb5e7ac892e3bece78b07b34b4065e33233a7935668698915393e884473ac4a47628019925949118e5da91914b730f811d4c92b91e001a2e66cae7d6ce4f06e9168f400b7d4ab39b8fd2770d946ac4dfbef190aff3b59f0c5d29efaacb0371bd137c21fb4860a6ce0ab03fe003186592b17ea1c347e013ff3394e73698054a4799997822241d9bf315a18d46429a4b5ede907f3dca089a4cab5088f660e7343e41260928050656103a48ae086e1242a95907c69dc89f35cd006a4943792397df9e0d7f36298e73e7ec33564934283672946878456765142d857c5eef63918a064fb3c47df65f9f8f6c47d1b353034e7232a08f7ad6d8e2c1adfeacf1234aad85a6d501c36895c8c5c896e4b35daf59b2f5ba7d2221a3a2faad7900aee9b0795e5afcaf1146541424988a2a7bad3ad61f40050b4ee2118f8dd770a3a7f9e8e3bfe97c3841e2b1ba28aaf61c7ab0568314343fa6538ba8293d55e0482d1ed0560905d42dba69f4d834de6fdba285fcba9f521673b5e9471f093c0418813b7a95001266974147ce3e58c5210b87a75963029276a66315f59963edec6b73d79a55669d0ab9405825d8ae3ea03f47229e6adb1be51155d7ef28d565626852deb33b64b4e20ca7548b41683756136617597056c1767e652204e125d2d4a1c5e1cc46d80a086f3deb7adb0000000000000000b08b5505000000001b18d552365a7a80633bba4221a24a40084d9300e3ff1da15424fed2b0397a11cf54bd578ea86d7277f0c4b6fc17c221939610546e70986d03aefa4f9798f513d4f9fb3d837c63f6f51fea32b75a0d9100fc5bc5b8413d14aac3a9f389c9769100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cc89746a8a26bf931f023fc62be7b9dd6b23aa273ea38c85f80670b5b087a3e3e34ab0ae3b308868a7563b778e732c1e3c0401ffba29f94942b751a9bc810c667791267e85dabe8cacd9d38f74f6259dd6007233197c2f0c04910977754ead549bab2be1931c99bbc6905c50da60b615dfaad3ab6e63dfbfce8f320e84ec2778032b30f9bf009bb8b1c8416f490d2116e11211d4bb15a4044f4f249cf5db0a8dbf030339841f3785ce307b52c425c48b080000e08ac10bf25e0f3f34c1689061d87a0b002f5c482a576e5b44bb04bc4d9d9c55245c9da067b0b9c3bd762ad8c44e3eba05bbbb9db0a2b31643ef9d9c760c6a659f17455209ad839aad02005fd7d88c9f0305d21726e0748be8acbf675b3f9ee264163f41a3dee23b2562275db06442d4b7020d798832bdb98d3ba34dc39e443b1f9f50b14a33323d57be2b5764aba4af93ec030cb42d993f3fc3ca67c51cd280bb13456894730ba61437207bbe3e3c62485954020b79e548853b32b53faa92972082bad0bfe39ee6e161cf292df35e67f2cdd309021f1791a87d5f16aa19754f407e0e8c779b24d64d67f9e5c97b1ce99701aacd602b744d7d573bc4fdcd334d63b19377d2b5259766dd597bf74c37b459f7fef0b916d0114cf592626a40d7ec926625903e764e0ffc4ea91b7723c1ab58d81f4c4f2420d00e3cbd3729bda824c140d5c095050cd16963fd00f7bcd33f367192d4064def812a0deb8a26bf7e71007ea6542781ff70133fa8eec8377cc88af562ca0938455319b310c92224a7b6f8e0fb137a589a4ec0bab6580ce72b18eafbbf0f24d59db5baa462772a48d0aa62778230d6b54270f778f8968775cfb69136f8eebee62042ab2d3238cb07ada37cdd9f8c428eeb9e016e244338d7ce8ed47a461e4ba30a725609bf8234d584a7cb527e8fd0b81ea56fe8424052e77ccbb79587835c7af93be9493ae15920292b129fd086d9e0a4a73a5f8f7849a109d9f11af380c60386f434ecf40780ad3200a3b328fcec397bb9b87aa7d27eb68da7f53a83370a60dbaea07dc86e6ae6647a1f53bb93607f8ff42760b0710035d6272036cba7e789bcf7546d5d6a2781abfd95e9e6f25db92dc0dd22d3dbf0778ce4c9da294d714c268317616b2614bb297b5069eb6d9f7b1aed36cad5857a0977d7b6e1eb3209c02a257f3010db385cc698cd32e56c7b662c45528f7c2990217cee41025da8d7334e986cc4203488255e1357195375c7aae7d2a5f48db7142594dc625aa409eef34fd1b2a63fa3e57a0c6bc8e8f01717a162955be986342d1d3ed1d3ebc9a12dcbc22cb9db122b11e73c22620745dbbe1b770a40e1047b9faf8b2328b3996bd620039fc00def8711d138a6a7c45dd04a5f4ff09c80897be8bf492d0c7c67180590d333409118203a7e030c028484dff0b69b91de8eaceecc2a936aad26b2871da17b11ea670837e256169287495078e7c93c943e94b5858a564ed7af789fd3fdc6676129bb9916f2db92e9221948faedbfc547cbc9331558ee73dbdaf5f665b4262ce3257e305869288d6b394d59c95195f3aa9ab0f115d491a2aa151785aab2b039fd5147da7d765e55f594edb1293086cfa52901e7b87bd9d3f27a17e506f38ff9c8ef03e811bae5a0223d5cd818ebb99c024cb438e0d239d667da10b3841c61b2986a1359df1453caad5492f4af8f1e12fd9f15e06288fb7f5bbc0d42dbe12a07d60c0f55cfe4585496367349152a957f4e2d92b473963aac3a10e4809b1c60dc6beb64fc08eecf9cb3d5a17fa88417e34b2b58dc0df819710d410e4c329998b9d0e31b43743a730f1b381efb51dd182dc66e4f367671c4a065e0c8414a8bbd8e1050dfba44c9fcd7c3c9b52719231f35d19936c4105d60573af6c76db8e7b3f0dc58405071618a6342df9d1479a75b6b53521cf2f1f01603b63020e8018666b08e180cec60e5e88b3bc9753695cd801197e815805d9f4328b59cf03fb6089b0531104d158afeb09028fcf2c8a47bf65f08d4875df175e6c39da1c1257b8f481d439b5d6d27c997ffe969d6e921301af3d3ceb5b8221c3cbebf55ee3f48a634a108d49cf2c6255c807faf2cca1f88633539e1675a211cc171bea643e56a60a76683ae118cbe4e444ad84dca519d401793807216d4211efc9337a0a1be7012a1f52adc956a5f22a0b30f5678d75473ffcafea2ad68433bc2d0b831c2622f5252f6dbc4ccab9d9931b6d61e20769b12d4c5132749d93e4e67e0bd409f5d72b4c03c34802934fc7af23946e3f2acfbbe87edd2559c6bad08b89859a384fc2e9ecd7ba99271f83f324779aeee6b94d18477b23a9c82a25e0f92eadad30ce161d4467189e414e88bda9a1b9638d1ec168d06911781226dd41bc4babd5407c7d45a73543fd76895f85b5673b04b4dea7f2c7e0f", "00", 1, 1254601664, "2058c6775d1d75642ad4c832027972d0e8431ece85aeffb5df1c1ed8f1da08b9"], - ["ab0494810124db8b073039e4f4234282f0b6106e9272b71c204380eeadb6f14386705b0f13000000000163ffffffff04693ce60400000000016affdfb804000000000965526a516552656a53d6f85d0500000000085265515363655263cbc1460400000000076553ab52ab515100000000", "ac65", 0, -760664371, "326df314a309ce6c1282fb59fd3708ee9ee8bd4d882050cec69935b4741a7aab"], - ["f8db671e019d148a0c6150f9b948e5662ea334688b526a6d4aa502a849c10160655afc6bac0200000001ac20b7634103c605d70200000000016a8c682801000000000078e3b9040000000009636a6aac630053536500000000020000000000000000090e7f0500000000845a6132db739b9cb963d669dba367c3a3bf338e974161967c07a87b356a647bf26283f3514acff9fe5477b88266c37e116f09c9183f1a41cc2980567aac7f35130b5bc7ab0f3c93d5df13622c0ad2fde0b6cdfd484a4958fea372b8d35409dc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b042efebdaea17572004c4946c1cfa1e6aba58fa2fd4bce0edec9ebc661f8ad56c2fd27e6e299f7e7992c1a4119f9ea422f2eab35e47144cba85b550c71e169a323b51c56a2ec1384b781c8b9c66815995ed3a56eaa8e19b69c1a76b97bf311f25df43a2443e9770bae3439422439ad5adeac555fcea33010347a7471714d3140305d47483d376e47f9ed03b0e7092471f29f8421bcdc4d3f9b1b639aa23033b7403121578305221550f0c7c58194f9d5173cdf50e34fe860df036afda2e9b31afed0a26418a43b414ad303ba5d0d4c3522c856bd06aa40dd7c3646642064d398f1a390745c940ad32b72d9ab8dc795e55412b1a843845c08117999ba671dbfd372a94031dd5a0fbdfae997ea3930a172983c8bfb79404d35e8d95f37e9c3feb604abf5002169df91308666f5fb042bc0bf6af6c5d0a1ce0d812837924e709afb6497ade1f032953bf4f1a180ca87af39241a6467293ccc2a11104bc57596facf11f22dddcf202059d32a5f4bd8e6568dd9418bd1e19816906728525457a60b4768d8b74c048e4030ed2307429164774b69b77389784b1511e80b1d5d727e2e1f0deed2e1b616f5d41422d31b7f0bd9759301abe0ec77b99d630250017acb509aa7b10ad7edc0e592adaa58e744bb60d1d35e41b7f3536ad1fdf99f2751976f1a30570264ee87be39b088f908a20c0983c2c7a32297ae5721c16f7cf25c28b94ab266dce59cea66ff71ae229bdcb38b9d86597d2b6e648c2e479087bf0a78a34fe0e3e318b9ff834e8243b0294648c8c1b8e4882b58b92a784968b655c94ebe4e0101e95ac8fd1d8dabaedc7508b64e4fc4de32444f0bad0bc80408c8b102fd6fb109e8becd5bc74d7cb93e1b91f5470c2723bdf7c8331e47360b07178af3fb21ed43808a7cbb3a17c7c7b53d0172b813f2675f8a14ec7eac517928bd649ab194726c867e7fa4d87398e925ac669731209444748bbbca85c1835ff427ce6081c1c22d6aa0ceb108937205059d24a85c4a7bba5b1bd45fd9b00870f3c4bbce71ad95b42ca711cf68c21205dbc79a2093cff6de00edb3eaed80f19dff7c7e633fb3bacd4400fbe6f350ba877a44b2ade8f7cd7f6ccc67a6f43256aca7da94e8b199b6538223134ec97f6620b59c3aa22a4c8ceb052906200250f22e11b4072bc33bf64588f870fcd64f7952b0a2612ea68e31c4f6fdea23105eb05629a2eac11c4344d9deca1db87853dc76e326713f8a4cdfc726526477c4b5efdb986359376a614c383bfef93e2858acf618211406e40c65cec79c61d8b0583df795d4ae8848b353525e964431ed94eef17b91f87d18b4c2c8edb61ad427c8af243fbb15f7232c628412df7a964abe6a6609529017c7a6e68907b250268360bf4639f3ea48cd3fb46fe658b9f3a44306ea0bb4ca0002cc223d73c08913e36bcc518bc9485a474e80fd3278e53d70f6b40a7218a527996af8abb5cd9879d37d05b69f3d5b1c3a42109ae04a4982b509b0e84cdd07c03f5b264ee819d932f8d8279a15b7c7c1e770c05e269138a5b1489682c5285a860110eb4923733f831298b8eced48d3c79df6d946690d7a74a0e9d57a171beb0d7a3ac29bd830aa533770eea1d393b5d90a2810bc4f103b273dd31589746ced2c1524f554b84fea86a154b17b37cff1529381cfa872cfebb070556181ca10c157099c8f464cee195fb7b45cad27d33677d92ed97c9f1aaa30abbf2b704d44e90e7acbb6eba4482a4c49ea6cd32a246da18c2476b45b3df3bb6f9506e5c4ae985f56bbb0a9898239c3f106577f2e67a80c4115ad4afe2e267510cb7dd77c2ac61ed04a2dc86a5e423145599ab8cfed0097cf46a11a41e2eafe04bd6c3859e9a6eb6c6c91a69c1b61349b4b1d5663b799cb0a280b53f3a17ff6ea628a3227a16c33dc1eac5f7df4d4f87a0e6abbee58126a4ac37324b4d11730379d31e8e043d42f8cc32f9283f66cf27733f885fb9f996b9475e45ceb0eb56b18b246620c52a63d0505a3bfcba09e65e3f3496657f56034c69cf68fed8e97dcdc6c9df5b2705151bcd5c70dbabd16dddb9de1c1055c6bf902d71f87455a79e2dc37247fe966c426b47ce6816e3fe1ad2b6ce3434a1a915221f8682978cdc166c16c2c6b8a6ed21ae5ce393a88b06583945b6eb17c51c05d1c2545f785f3975639d66d3bd4b2f916be0d961df3da089bab23d4a7dd6808d7668b648936049ac151ccc1df197ab2d4961200901cdabc861b65ca0f8af754fe094ea0e39bacd4e2ec41518507c171c409f7da4be41816a4f917fc780ec0c00000000000000000000000000f65af88ada05ab7f4da0c7af39b00caf10af201fcfb8199e0704d9df89f998ed37e87c5d54e268c1d229cb85f006871197307b8c442ed0c37c9d26802553581b60d1e9bccfb4aabb5491b9c4cdf12db7974b62806841c477ff14877125bf1508000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d9034cef9ce65b5111f37fc2a5e4a5cafe8f5fe1b076bd8ecb06a369bc9f0cd66abaa5b565a041f8368d8a3ab5a43a6c1b5ceb132c492cab4e0b79e46f93168d83c2cea7a05a379b6e99321e0c7e4161cd74258460e164fcf8d0d15e76ae3889033e25dc2515203b9f23b583e2fdcfecdf89afda1b2665eb443ca3c6c49e4fb030fab8af4638ba46aed386aeca90affcabd6fc2b562c09bdcbe0c1c2b0a6d0b43030080b36341cddc0df0272fffee738bbec77ba4f2c70b2457682fdd76fca8b7600b04929ff48947e6a96964ca373b9840aaaa0ee165d00c68a0bb0137a9e8de65fd2a041a513a4a151e7f2e5831442228de40ed3b098982a310653811ec5c58689b02252736df22a1d9b6f322129e126ebb2cf4f4245ea4c8978cb099df57d039b891022842aa9ca489ced4b4ade94baff804037edba7b286e64aea102977b8749688cc0323a7b2c5bcd1468e77b27117c7573fe5fd276e4a58c7a983b26cdf3c8837499602084a00b52fe6b03f26983379527db9f26c43c41230967c18f1f3b9e6aaa3e7090200c63130f829b0f8a5aa4d1e241bd0c5a516a3faa4fba146c7281a1235e3a72914fb7fc4a7b09ffa4bad97d04d93d7e214a52873634f921c8fad0a6f9b99f03eb9a0f64d6a27a807c725ec179b8f50709da8b8b2cecba100a5d5b30e05c1a715fe10d865076f186b6f0dc254ded9476b2b9c3dcf8548062b8c221bf24138aad31dbb116a92d07b416c026e9832f8ca214b7a36d91f87436d8cb92ec6658d17a624fb6278f65519527bed24ea0fda6e07f99edc462de3c1bac9c7f7f0cafb03beddaf804d15b3d15e10755bb8b1c4ad2c363eaab53a86b609265e3d3d9c7c9c239c6581197144e45e8af8936197538c7187dc76ae03e68e9a37b5a65b718f920593829fe3b11c290e43bfa706f0a7f28d03edcaaa47918db7242effb71e9dfb1b72d5b3412bd0a5c8d37806491b447fa86ad8bd716f166aaa4df0680c303eb6686fca09371d00bbf56934d8744b9852b4f9c7b73cce89a62c922299b8648bab48ea4e555290d521e4c548b6d1af968edb745b2b86e66b14e80316c67da718ffe1a440a15d889e148cf62caa673ffab6236eee1b73dbf3ec02706c55cd72261fec699ffb2b2abefb01eb6e240ad94c9e79abefc8e2dd3bde4bf5330b82cb03d476b3b535ddf727bdc6eb3f59102ad55bacd5ab259f089145c5ba6481af5045b37f608556cf08f22efb576f07fa7dfed01242aec9acc8e2cb2f7eda4d3f1fdff96844a02c5e54bdddb7f30356c27ec8287c3a99f14fd844ce0450f8f4ea42e33e44bffc35a128e4658cec355c512f4254d471ac4a733af62de211660c678cc40718c25e4ce0383486e82a0d5cfa3f666af5a6a61ecacdf0d78efd0b807cd54b7c9605aebda2596170320866736d52c71a2fc71fc5ac51272347f252e14e0131d464ef5efc982c0269c4b8ab3dfabc42457219226ac8a57b0816b11ae88258a6e85fb383a286d59839ac6af14f6e85df6c22d5f2bbf38013fe15e484900a07cf14126eec915db36b319193f8338312c3c956f275e891d7fb7239b2bbfa8fdf24f0a30dc877c52f153db2bea04c0c29493bfc1271e31f9ceabc27ac8f04bb9c16b808e6ef6c581c4c403dd7ab1cab8ad6e541c956e54975758e07af349a59719fb3eb12279cfbf8bf70497f796de0f6f74d19b7ade33faad935dc0df69869a9baaf8ee81ebac550bb934d679ac002c5edfc56b5609303752272a979fa6c9390a6217a6420f93ff5bdeb9781eeb8a1abf000a72fb8c273cdf693e5780f2129994637429746443175a2f0c80a0794fcd55c54bcf106536fb21a68e39ebbadc76e51d7c7a453c4ba4cb294444471b42dd8d5a35e2697afe252298c5a01f2448039921f9ace9adce8372502e4d8dc551a0aaab2d2a096b831ad410e601239e435efb257c99cd91012a0ed45e29c14a152e2d05bc573d9b58918d0845e94bf6a9558a5fc0858f845d84be5ee45c3cb6f1333d8b4b20d6099f24525c1b6992c5635d521bb81ecf575d83aa78cf54e8bd60bcea0240f6ac3e25f81d7c3f534b9c3300bdcada0ed71df9f99b990a5307b375774016c628dc2cf096c5e0e00acc222d48b1592f0242c3f833c919bce31d4ce00066b67f13bdb2f668253846801b0ad1fd95f6cc1bc9b40c22c8a3fca6f3612d58c223c9e3730753f171de70ec6e36c5b2505997c6538f70a4ff575aff026a375660842a842f789df728ab5ac8e6ed9f7c5aef35f7db8aaca2643f40e2679b90837888164159c7fa682b4c478b575952b4106aeeee15770b97517fa5dcfc342cddcab3bbd0d9460981d7a5cf8af49062158e1d21082327c040205b4f5b6dd615b31d3c83a188e8dda1718c3f91374ad4dcd8ec89039afbe5cf9ccf4aebe2cf6b4c3783538c009", "51006a", 0, 999335528, "ee0239e5c9750c50f71933f310435390cbdc95504832d69b595ca0dd0d71823e"], - ["84024a0302613627dbfd7a2111b8282ce01c56eeda85cf10787c420f6f47ff149491ba22100100000005ac6a63ac6ac08eb05b2ca12680d05a71a6cdb90eab2e96b3aa6596fbb0010ae9a274cb48769f05de2e0000000006abac65acab00ffffffff0350da22020000000007ab52656aab51009ef6100300000000006b67ea0200000000055263526565d0fc73c802cac53d02000000000000000000000000641a722676a9cddeb37a19886945971e05263dae7b987cdc1fc871e3951bba034f9cab588692a665013edb0ebf303ecc9efb0eb6aa5297ed804c81f0465af8b1bcbddc84ece0fbb4bdb428713cbc1cff39c074db71c952ac2b207d650912568400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ee6bb6aa3c5ee6d417edb7dcc0dbf7417b308b4953714bfc1b8d46e6a7ac1362edbb2616e1770939e1783c9901638b4b1ab564cd8d62a678f62481200035f4d488138a70a0d6c05d8e9793d1929a73e6e925c526e73b6a69cf9bc7a22e4bfc937d07501ac69101fcebd3e17b523b2cbd12215d590e15cc0cf52d07d0ca0ef22c021ee427e9223629bbb10db41361b88b304b309fcdd5065b3e04d0d0aaabbd3b9603114f06b2fb2516fb863d9df537cbfff629877cb148245982d26f34fbcafc27230a3056911f5ae37df8e59a1be69c176c5ad2db117a3939ce5c1f50e825e405aebd0c94ae094843b68f96e624431119ff5c784ed92265186b58bf1a52988ee633dc031117a15cf0824e54b7c470caa643ea8f9f1908db603d848f3c173a632c1f1398030345e3171c32e591f197bacedc83aa254d0e63e7da2c8d249f77b641acabe5230316158ff6622521a30c368e04d2fe311a8fcf10d1d4cf13e51c57efb4a7cdefea02148211996a347f30b3ccc6da4e93b9394e11136a0dd3c4cbabc4c7217765b3ed022ba217c21e1cdb672b5535b942acf8f9ef56db50070ed431e37a81b9118fe11203f5be2345408304b7499ba19ea9c8a75b3f7bd2b9b1899c8fe7794ff99e16ceb564d5a550ebc924e0bfdf7e65ab913fd8305e3020289beb61deebf41bd58d23cc5c3e933c069b3072b993b4a926a5197163222b262a5921aafbf5420ebb0365ef9a692aefdace19e9009a753e5475d2578487a374cabd0ac79c7866c7ff2522ca1bb523f0a2ef6e07f1a73621b1793901271e799774306da19c5c87d5dc5275c15261b8393490ccfc169ff144b3a369e7944ec3286cec95adab4b6b3eb43f7cc9da79a0f3fd7fba3ba1073dc12256460bdcc0e1ca6760cab4eb200e41d441e34c153d1ac8ef2a728316e92a4970a4bafa74b75589eea6788454187376691f47da33f431d6117e9df78070239a521d478877d627da4edff168ec6d352d45002df3307db2f04ef7376c93235c5d0a2fc1def4f3153e0f97d810f318a8330cb50115a16e03373514913da93810321cf664587d582775131bdae096babbc435510f72aa5f3b4b73ed83d949979b4a2d5aaae78f8fcb4f603c330c9a3cd3d52237181daa2bbe45b711f56f9710b7884517f77482918d2bb2a2bc952e64ece504e425824fd86135a56a225d25997371571126cea94b27fcc15ce8f5b30ab9c1a63201189187ef5ecfefcec21940a4cb59db00da6487356d992b6b9a6dc84500256846bdedad57ef128e485cd323110685b9a7ab92c164558047d54332e1e9d12086424b0f04cc8536598546ae80b5a5d2290e8b7984fbfd83f31ad2e9cddd8eef303261d85cdf8f3efd7ee436a87ae51f7dd3e936f70c11c01c7af3c7b5dc8aaec9f41a5014de23f8e4c1ee272f5f9a0c70b902e2d980ea74d434a34097cdd40ca7b904d6d1275105c4ec41867678e2415736ef21dd81fe6547069c99b241463521bf9c29d89151047cea8cf3a4605d444e44b398cc742827e84d7cd0073ed73808e945beaeb54cf40af55a5d4c5b2c29463c18cbe42248bfca3582727772fa096be69d18b3abafc05bb2a42ae884c945584642c4dceec30c47b82bf9bc58783c2c75c0f44f848456fef540f4584ff1915c4269781e78c54bc4879c4c6b84671e3eeb1ccb3b0acf877fed6e9c98274c95c31eaf9a7467311badcdc78912a80d1d609274f10c5c3d5312060c9a3bd9a177d39f5cebede1e70933a09e1ad415f14e26589b20334cbe7e1aef51c381b75cc2b13981d4355ff52814a606c2ccf44051ca75b4d33e90c4c3956c32fd13316284421efba00f0899632a66b6e05e76d9b63c066964525d24d472e127d9c045b642269ee2742ccc58b3b4c08cece3c25e011d1e05ad3ee36f9269a7b26095446b6d108749ac2e78a9dfb2358a4b61465701371fca37d27e447b92b268c767f8af0c9a0aa684b1a065f810bb3804bb57c0edf98b4cd46bd1036258c444d512958d7613317de5913c947fcfa46f351f0410638b60dd796def760249ab65c63018d8cf14882df240911c341c636e3da6edcf3560a02d7c9fc376c1ef4ca536a0cda22295316eaa5ff09361b62d3f9a69c2bef1f0391f77872b38d30939633d14eaf93bf0d18ec5fd067d7e1409ffc576e864820426f20d0b149afeaeeee05a0e73c251a0fd1f4d641a7ffd179a7840554be247e881a85c22dd0d546b7babf4b78d963dc4216f44946a67b2b34cc871ee56d4c8a0589e941de062cb7d75d614ba90b7219345ca9e00000000000000005f05080500000000f0bb3f7ba71435c8df809d18c163d56796a850e4daaa06ac3ffce690ff8a72f4d568df140aa01a6582d4f05e47e92f610595cc7b4b41277231f05c38a49bb32b02053d8de28f3ff3e1251b01333ebf7fa030d2b1b0178fbc4450a379cd306ff700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ddb18cb6062ac5556bb9c7ecae7fa6c027922ee0918285841f26dd60e79dbadc3e420bdd80623314845e85243c13e8554b279bcbfd577b3d88d69f7bb36ed51a7d8407d0a17b1ccb39b66d253ba556e03516c170e48c1aa0e05a57842bc9e35d5e4980c27ba68899bb53e3a0a95efab1d6669398a355a4fde8cdc69811e54902179f31d6748cc8989b69564eb39d4fae9ae1f6bfab87b5a53d46db2956823e3b02223a624bc9a0beba20fcbe12bd8535a19d37c9b79f56d38a1b9fed0828aab42d0b20669ce1cb9dcd5f5d1416cfbd587a7e25e20930e127527adeb285682d27c47b108ee05ec40e6e45eb337dd022fcf3800cd8b8378ab4f5c5405df89e908ed73b020843f298708e8dd70294d7f1058982097b592a9c42ded73fc36028732292738a032f5366fe0f91696897b7c157e5b827b54fab9090f99bf13a93d30aec4c75bc8903093b5bd3200d012934903599e8aee68935ad80a18d549fe0b0407c9f12792154020728c6d0faa1a01682cd22775dba4de0df1344abaf2f1a26873ebcabd408410d0316064c1340df92c334ed7ebf610b270ad161bc45db7ebc790a29edaf21b1ecaf6267c69676fdad49f95402013e0e5009c24884f1f6f005d51fadbdbf510263f1c10156e4439f93b9e90a3414277c03b2e3ecc8e9a54a5a1358f52138e67af1e718842d340e158f35d0f24b42281415e4bcc27629466cd042b2b4774c0ba7422f2703386b9f2510e98d43df5057d16eaa1c44f6c5e27930b6fb764592dd683e98820db189e6f33f1444b4e947c8062c4d25fd22c6efdb8f86371116470525f2eaba653b9d88c53228cacebc0ae649880185c4ac32667f5354534c572202f7a56d2045410a8551eeafa07d1c71599e382d1984c06b4b8d62eda49c57691d283566176f19e9003f68c717fff61037524d8fb5702b25d20ac36936912b8147a98d1af1dbc87acba9a73bca81571e7e2c38910a0396939ad13d07cb64f8b5782bc0b8e20bf235e1313f820081465ad110c6c314649ee670dbbc7a8ec37aa7a95f30a74d395611ae897a6f973fa9dc559e8a4b3d226a6a5cfb6e46712f74ac58c1a3e16bed926a837c65d104fed90a9ba0111c9efccc9240ab3f2128fde759e309f1ba5f2b5a61289b9887ff31bbd3734ab6932dbb3d01836fc2234cc9afcfe35c4f47f089fa1b3722799765fb2249185b28cfadd4d455cc0a499a1e9ddb5f8c5a80425b05997381870b8d7fb2a6b0fd43933720b376d3d231ef1d7886da2bcb687c0856607b75362c370566365998903c3af0b7cbadc1e6cc418e9bd933d8060dcc6218176d8d670062e4e2d8fdb58b66ca3736f82986c76bafdb03c547cb8e50181499989d6ab1a4051e392341b339f6f7bc7fcc761c9120a31f96748b903f81e3c4820f78bc9fb675c3585bf2e3b9f67d8a4fe06b481111f5d26ef2c26d109d651c00405e19af517237ca2b4bc352ac5fdfd15aac7ffdeb135783336be74f89472f7890f6a62a1c2c330d02873fa21f9c6985f1cc5f48ca8e3fc70736b597cae297d5a3f20ef10c76aa5097660d5ef1db4aac69796fdae893d20eb7339cf812031b7ef3b242dc14156dbde7f07ea8344f56cc2c52fc17a8a46ba71c16eb56363bc036e3cac3e17c9b88dd0cc4f1565b1067974873461d97c60e589e44fb35838f74891aa50b02c2ab6e6fbab875058a4a5c4fbbf8343640a6a6bc1f3df388cc08bd48efe1e563f76b150440bffa00709843d40fb22c1b21d93be41a12e2ae7d239bfd096bffb732d66311e5deb194f04c57d5193aeb117904713d7d820974831fa8245aecb7f7285b7b65ec9815f8e9ed8bf4deb0f2a3e5d50b446518e0769dba83255efb0dfd6c2d3c485bf7c7ab8324cf57d4209af4339b475acbab528739defe4e1cc29c9b4645c2b9f57308bdb0143937f0384cdee7e18cb1b15ece7ec510e3caf492e07bd388ffc877e62b4c9411c367e85b25d555c6881609990c50d10ef701157b2a04c3ff83339862f2b9561a06e9d0cbc7c4cdcbb4ab158193f864893c8a6d4a647ed181697fff55fc5ff79806c5ec08da0e6ec842e4b353bac6eb0eea15a5f415cc03b891e2f85b699e837f51af379168a3df4b91bea9b3e2ec42cdeb68b83a1b99d7e90d8977afeb1ec13011f250a7e93c3382ea3c73c11b7de3957cb867d73a3121b9c482b09a2b412edca246f6ee922fd74cc39716ebdccc416fbd72705b4f19244328961bdf964371582a4263383659a76c99213902b8b68988d96342b05a3a40929d982d260b6bc8cdacbbf5940c17ec2b3252cf54bed6b00627773aff0ebc0143b6884ec5152381bd2c85cd8afa329ec99a105c1234069a6072ad82b254375c103667e5b179d8cbdd8498b9092753497658cb890bde2b97ec074b723874759eafb4a397b1d21e2af2723e06", "5163ac", 1, 905166469, "c8e54310ea27bb773446d007dc49377f04eec8094b83cbde39765c025b7f7018"], - ["981dc2e9042e344695dc6f651cc32ad0b4249feccff073f5cd0ae473a547f4f8e59f302ac103000000010082a6611959e82ebff6a8a0c5a8e14162038dc67a3be96ffdc9ab1feaf657b2feb382a0830100000000ffffffff050cce30507d6f5e7529ec49445cdd917c1dbc5c9c87e4f8a678f43c192350c10000000000ffffffffda1d27bc69b4e065109ab3632ddfa2bcdcf9f12efbf4ad659bd6b4c8653de29d0100000000ffffffff03e4f1a502000000000463ab6552f2eed905000000000451ac65ac27d7c20400000000004cb02484", "ac6a6a65", 3, -1968106151, "91b1be815e98f8d67c5b88a2d23b1ab0907271c867e450c174795a9e6c7f87eb"], - ["01e595fe01d3c0a2c4877033db04ab6bae6d8e291a54e15ffed7fe7390852e7458585e9e60010000000551ac006565e3b79d6901c6879a0100000000016500000000", "", 0, -1003443996, "85a22598c21a0bb31e50726f7802bc542efe3adde91761eb6f0cb748ad85ee76"], - ["eb4f6d8a0442ebbab38afbea3fbb81094ba5b531e7a5c70a011295a7f74f46c35edf88614f02000000025252ffffffff19dbe563eae7ee749df597f559f62ac5d1083cf415d5289a273e29b332d1fe7b03000000016553b0e9fdeaabd5aa93f83bb2c77bec29bb65648976d80e553ac77bdec9451a189598706a030000000463006aacffffffffef525986aa8177e6ac03f9517f733c647be7ac54a22cffe3e2a2b8e11d80faa7000000000951ab65ab52516a5253ffffffff029b4e5a020000000001537e0417050000000002536300000000", "51ac63ac6a5100", 1, -1773057003, "dc4a423be4bb076455dc739dfbb64d576708494cdc13a8316b6e94017ef76c63"], - ["56f490200390740fcffa10a3f5d12d47eae473bdddc98564d2925b44cb3f22153a15c6ac41000000000453ab006a333bdf387bd4ca2ecfb818ea8594acaba1c6512068c191784afa3d8fd2c0c7ff37e1b32f0000000009ab6aab6a526552525223abe04e5343a3f0e200eb332ede0d80b9de2112b40e14c00da6d2943d84772086340932030000000165ffffffff01f8b4c602000000000463ac51530cf2e4d0030000000000000000becfff0200000000fde7fe5059457dc35fca53db46f1fb2993fea794c478b26b2a5270bfd41d6f85bc0e16d98b26154712602f9da6c2be4069fd319d5c15a0464a731a9b5df451e7deb4901f2d645e0b8013e13af87b5b3e9fb23709d768a90ef8f025ba78c104a1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e8d8dd6964d0827961ecf8ef7a268c322b867c70654c4d2bdb0c3ad5040cca329c7cde5eab6f1e40642c50d09cea3075c4fc68d5f7a21d9a66886a6973fdae680902b334ef47f1deab203f35b84edab809d4f5c9718ab83dfce79e468e62a3d8176bd89a56dc25783eea78544bc20231a2da97c1d2c55c58edd4231a3a21931021dee03fc5525b26a648114b542950cd14571b55d21bc9b0043422c17c5f6dfc8020a821d7730dfbcf62bf57a3f4b41ba65847e91efddb2db1f1b773c198e4b3d830b1365474ecc286f1efb2be142c43e1ac5787f966369dd4c0d789132690340beb90302002de830ed0a9cf00b110bd2849dd2d5c8cf78ebfcde5eeebd0562c82874030c94ee904c834b15118229c90167ca19bce875c672a200b3e67ccea7113cfb1f020a203bdea9df97fd8faa01198973cfb93607a4ccbdbcfa444e30bfa19af168ef0228c8906b5f1d9287c9e5a93f22b0644a25e005b5fce587c2554370eb6ae70f00031ba01aa00b7790167ee8243dfe419cb4831c23d909fd881c7b08d502854e8fd70209a8769f5e2a411b12726243aa961a5c27cee49408b75a2edeb7e3447b36f5cd515beb78b71126e69f7795d7efa4bc1a9086d61f568f971b891bc780cc82ae87c48ce245456af0c3cdbe9fa45d6f809bb96a65f86c28857ec8a8412e83fd726162ac773c61b1801be55eb7d680cf5ab285d054cc8fa4a53a72e7a0c90d0879ec52667b4de9847b9f647e3722126572e681f8d5bf4d6823bfb980250f940195a10620953baa5ee5830237d4fd66216cced32d614954725dc844475010b6652d339d84b4186a92956b2c03fac21c3778480e721a43628ff3f40a85df5829a80bc25896ee4d13599fc6f6a6b874ea1dfd037d485b9983f6f67db2bad8ffb173ba0b72e759a4f7c5dc7b6d9ffdfeb36d0b1bae3864e5a9b236fd2d6ebe042a9096f2fabe7171d94eee5db582f6695e56224e9bcd96653def8a40caac2af0818c700c3f81812568cc70338823893d7f8d47213ced64228f3370fda512b29020d6562245e034ee248e96443a14ebc49b5305a24785ae78ed1af52abc5318a20c87bf85168f2dd962e8bf9c153cf808c9aa126f0878aed8c4b2fe28a94923a77772e74a2606e56276109aa55a396cd3aceeace59e347c95e19d9169b5d7dca67ce5a14e6726905313a19eaecadd1e3110234bbd74a6ef548ea55c1244e14e5d5cb6ca0db9b5e3a53c5b39e53e14cb0629a4bb046576ffc802e2cb0e92c2d3d16c0f8b262283888d02e89bbe1f7e822bd3dce02f7046b6c846739b20473cf5786e6cfe314f2a36bb9bc31f2217479420844ab1a8c26b8db2c3b62c4fc389f30cde2c2d4dcaf99ff18dd9b5a481c003380b26213cdef27e6983b3016e417cb72b1669a367f299d2818866805268bd17c0c013846d684bae2494cc2b942641de48fd2899c3023b836311da831a1e2804beabd2555c537eddb14c2f695c40ff2ad2599329fe45779ccdb51b9ac344d43ffb0ddc356d4b697e88d6142335f77b8bccedf6e184ccd4007bb35c6eb0980fae29aad201b8031e0fefba1cdf91c66e09056dec76abce6a54eb120899903c09e2e29f0e6cf19cf4ee839ab184dbe6219adb5773923ad39177ddacad5b6c4e4a25a396395188df87f20668e407fab6176cfc055a43dfd29da56151631eb4427969a55db06f6b391933aed02728b947e0e52d17311bc5eaaa32ee53483e050e074e5f56561514260d095f224ecd1872565244bd47712d147b402b69ab9d1c377aa65b358ecdead83c3b9b497df88b2cfa9eec0c15f96d8b0c573d0df03a1621fb616355e06fb75644acfb3812b11e1c04fa7d68b5c3a7b71c3a4a2bb6bdaa074841a115531b893119e03144a57c5cce0ac1184f525e8000aebf9b254366b32fcc170c210d4df0f6b876a75e9dedd54a24fb393b0405e0523983bd62e88bd243605805153aee46a26a7fb5203e2ce904ef2091e3de1d2a9458f1bcde1d49dae99d053a912e7bc5144d17d435eae3e98074727b41df60958e46c8456040cfb90538c50182efb4db807eb0e1d1f90372ed40a69f45f577a4a483a23f433c9dcb5dc573644857dda00f014240d3700445eed2738d26a6cc0fd93e7384e1e61e6358d1d6d922797f65161b9b52ac8e038fd6755acd6b0ca1fa6cf20dad3651e8597b3b2b4dd3bd0474c89b9fcb0e189185ef867bb4282b65546265a33698b50a54ea8d8151d1ecd4a58b028c6c4ea8b8242cd1fee6cc376d7b6e2faa233293c2efa476133577d58b4ef5d2000000000000000011346c030000000014908dc91193ca1a41b7232496889d6460abb1fc390117c26acf36441970113a411bf70b8f7375db9ad8eec64fb7c8f4b51f754940b0065a589e9b8ffe98aa156ebd82ee8c68ef42e486793bc3c744cb910204e655c54d102e59f806dba14db500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f56c22207dee24b72cf6060cda33d4b2ccf6f6a3bd4b0587d123a74d524cbb80d12f5474b5ba38b1e2d95feb4423f72c12949bdf5c72798498c142d2375d74755cf8af12ac8a0461a697305e8d5b133103414cae99a3762b1381cc74044fa6c6068066bd374543936b9f98724decd11fb7cecc622636853cd78cd1b1d5f79c540229e6a25a5f0530e9a61da93c3e3b2254f0962a236e939b3dc03d0898c2d71c81030f0f512ffb857b5e2e939250704b870f6df576f2215463c2737f58109b8f66f70a0568c2e6538b8e9310b62ddf628a242a9e59ada0b3d827d677af9d74ebed1f152bde2d9b70ba864541b28dc39a910a62ac933fa08bcaea3f36f65ae12239444a0307ada2b940131824e1fa980b37e5abe76f5e021369ac971a49c5a96541cc8047031cd760b3ab8d1dfad91b1d3e385761ca16850f8766a6ef49ab081d86b5ac33d002158ba59cf101342223b6c79787786586ee9c237e2fc49bab20276706c4a2fe100317f45668626854856a5bb72c09167def7aa1a2ffec5f21c1c98de1070bc14a86021708eed123648afb661130db518db66eee67b07e8fbae8d74c390a86d0cdcc2a58fda7f35f2d67632cdca4f8074bc7d0813a4d2a0bb131646e1c664a2d5a4ce7ede62cb15180e89ba7d9cf58d5ab261fcf9ddc40a5d08bb23e32a244b62ed9065cd3a276568dc1a9d9dba5135d58ca6d99474130a5e9b3245b1a8c91801b84419800a654a666b2d1660c23afa65a87fc8f7d01db89c063d3cc980541ce66938e7986c25ba4b9c3f8e70fae1c84dcc1a9b47ed5dec411ed2eada62410c6cfbe3c90316726c1e69ee42df49e6a27bea9725b7dc1f4fe45ce58d605f32418c9d31f936fde48d7671e9a343649ff2a7ffdcd1c76b21eae1deb2584b0ade921f8085693732c6af97aeedb2f9e42d08cb8a7f011b7a0ea46682dedad3a286da4264648af4b55a0b9ac8eb9e8330506eec79bf5439d2e70cd2dc5d69ad5eec033eb80e307f845786c7d8add06c58edde06e2bee21bf29917495ba86be989614f5a993386aa28318edd6d7f9d22dda749aecb30cbacb9a23116cee9f80c6ab33e6bf5d89c41c040627a2865e87f38e1a130a2c2ec9c2c9595aaa1d9caa227290acaf7cecaf991eb09ba79d20290b524c6f0115e95420471bbb0502feb0eb8109b59ed8c9a4bb808d16a6ec85afee6f3c664f48e88990ff45e1ae3971165c2f25103184dad0f5afb32d2c4960605a274416d01eca4a0f7e152ded086ae5da135f4fa73f5a606e1d01587c826f1ee17b2cd9da99d0a97b2f769c29d2dab589e73c64126eb01766734287150e4afd3cf1e93134f73a76ae7469963f31bbe556db9f5bc3fe5878e10e8b929ca46b3274e73d1a9dee1935e0c7264e631d213fce4caf27a2b3a951b19102f205319a5b5372429c307d222a7e98e50f41d16cd0dc20c42442c77b170e4a17b70bc765697565a7e4a1ec0724d2a2706905241f6387d21a45fa0f0f8c8bb6888c94c58cf8d64580b773aa465416c191235a4a3d018acc476c7de545b2a1ba161a81dfa619fcf256aa26482a923b2a4de3d287949b083f51d477b675e860a05951817dc3ae5c69466d2699d8dca407e02edc5594b72476d1c5ddcde8172a2d81845aaa885cce38fe02ca819440c92008ca401cd4815a33f60c781e6b393e978020c45242e286ac7ce098ccf453dfc96f415422cc55b8a1a07c90d7a1785d8142b5034ef65b3483a6a685d5b9a6795a9ac818f4e096764690e5a72549d017c85aefd8145a5c913da464646e94d968df0346beb8a5673f0f65e8c7e4fff8e6b4a82105fc8a116b0bd5ef2d8fa6e52a36624dd2b660b38fa3e4a643567c712f54a8d54cc7bc3f9091d2ed80d26a6008b925c83714298807bd005f734ac70e7799554a4537b14b0bbeab7f315b6a987904268b9f8e49029a49159f962f224787f7d0b2b55068632cab87c5608be4d30833a4aa4e9d97bba01b0f5bd71921411da55e624ff63b584ce1288be09a133c11e19c850bb86eccc40d8d7827c770ebd085f846f2736a988e0bb73ee61b8eaccff6d75aeccd95604d0892977e48c979df3e8521ef75d6dda7eede990b226141a652ca671324c89ac7f849482c2cfd5390443cb1d97e673e2eadbfb4ef13f2b52dc1fe8461b8108f6984847136f3745e6f892bcd8f8202ab356f646f96857ab99bb58a2c7587f7c29d5cfac430db5e14e9dfc91f116d8ec0c4d8ee4c9f37cb5cebddd99fec19c94c3e30f718818cc088344c5ac155c6504c2c25242ec9ffa9f0fd0000000000000000c5003d00000000004c2016a7f51853d1b86445a20c969d2f58927b8adedb8a1ce08bb6af9765876869dfb7eed75e4f713a2ff7db77ebc7db333f20f10220a38817b043ef2a20f54c1f2949db43d58800326fd49618c51409e711c57848e4ed48d281788bee51d85d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004aea369f9d3ee43c36b1b7c19ab5ed33f7786197816d965fe3f765bcb167d5a341014c6e5e08bd1a8fd117eafacb47e9127a817fbdf2c61fbcbb043daa1ea15e64d9c4137f5fdb3e71e9c04b9703b832d5bdc9dcc62fa7b490862410fb1fdf2a56422bc2c5ccf959177e61a63559c681651852a9da432230e9075e23036bedbd030b1fc4d3cbff010b09a83168ee0c2f0cc3f5acb6cc47c4ffca63894e5a0565f7030003eb2cab583295258aaee7f18f3d5a77e2d6e28b04243791999abfd93366060a0d38426eaf1206da109726f2711b0ef4325f06c5ffa522c4ad425b8a57da593d1e9d5875b605b073b6a91f1e2ec23cef6992d0f1295e30f8c882b0a9afa9cf84022ccf8a305c7b1f1c34474eed90aedfb4e0af27c959cef2b3b3aff2313890477e03115146e9ed3bfbeb1d3d2c2adeac5748360c8809f932d814343de8eae45d797002021108e7e305cf8379c68f97e302fa67fccd7f60810d2cf2a0cffe352d71e987022d3357c9c310e4267a5916720fe88143a77eb2253a6a2e3e9519aae58ebc3a82030afc897012656adb51b82973ef40d72d0c6e44ce062614bd99e0da13f30512319fe2d01ab12b5e3071f0eaa495cb0eeb1a76016ad6f0c20731f88f20fa7ec39c5329d2b94d353a05bfde4a1c5de3c2ccf1f69ee9fb6ab13221a8e61f476f2c8fb88f8c382abea56956d3450ef907bb15e33d3df72c924c64701d534fa4c0f80d70a513b15c48f8c8b2ebe07038f499390b21e792b8d417b5e35d42755972bab028a37f97d591f791a50addfe32f87aa2e4d61ce494528efa917b2f1e8bde697ac42744614a3fd70e262a19d3391f7cc7fab8bb20d7aa179e5c7db9962313ec1a0bcd06a5976f7ba7716d89c2411d4e1203aae3365db161d0cb815800ab71d289998317a41ea3ff398d21eeeb692bc1cb7d33358c879522de429aefa7654949b494ffed282c9bc39d51611d22f7534365a8dd35561abbb527dad82157e2a059f22a82cdecca60b13a96b1700e94f7d4cd6a61770bdccf027a35876d56f8a8b19dd1caf702be5d720ef81b4fde82faa8e99e1a08000ce09e3a7425592f7743584f7fc9ae1838124c74cf93c91cedf1a6b002fd9cdc1e1dc2afb6c54ec27528f76ef7696834ba89f8de2c7e212babe3a8c54f1f3c6b9b8b2f8715a3713868addf6d8d1b5dc0c0b268aa79745df51fde9f94e0e37651377d056714097e206f483791bab60c06359124c9338be136bcd2912b2b89457bd0d07780ba8b81ed7165f398811a1fe891468f5a3e76224250739efd6f26c390a1329b3aad806f7a21d829d34172cac31ceec198f249792779a5c69bf23f4a9bb8895fe6a8237d03905142938ab0f33c9fbb926db4850c4a96274ac49a79aeb42d35f9267aa96120519ce985252656ea5b1a2ef46677b2de59822f982810d31a8a01f3e66e422979a126443e88c0afa36aaff9ab065cd1c23505e4d5ce0cc4fbcf8eafc3d959ec0f4d6cebd47d3e662f5b17eb4c769844fe76441885ed3087caaa768a8e6ba4506b0446417dbebbc6ee1bec161ba819488921c47fc77042644968d6d1c9713061920c092cddf88c357e5571d5720cfac10c5ca921b68d596e8edb6ed07454ea1aeed8495eadc87a1e294265217df1321396b4147f4c521ebc4a5b887d9a7e7c71d57b284662c6d2c2c4c0aac96da4dc0de2d90cc88940be2f648210551f201b57e5bff8f9e41c82bdf5175b2a39beb6c0ca9aaca6735df9dd28b75a1bdf8b4e49d38b35858a5b165810ccacbf4344f6316f52442f5f1f7fb913c7683723e75929edb1cc72b496a6e29aaa0f998005714222e31ffee0da57715097d15b4f0be78d7e92e39c9dbeb908b3599d39c0c71692ed03915503d507a16c7ed8ff6dd7f4c1fe5b6b9929f99862e6bc1861d5ffda10fae7f4abaff08efd89f60cd7d26e317adbfb218556124b129281a39e097b3413471d669ff6198c7e3922502dd9b894b2b4571a8abb3387994204f3f231f333dd25e254d8f0f4ed09399b56519ecdc4d3489c5b61352b6dc2dd52bac7f8784521b398a2dacd0905d47eb80d632216123d259a8266cfabf07f84806c66ae2506b842620f437374a4d3207ab37912ab1a451758e04431ad96b1c0003e658e303b3aac11a30eb32e4052591f61c01487c2ec9f0e9099f1cb33d685b37cf89115eef563e7719e4c7245fbe50055fadeb1a6cdc4475f865c0a6b45137e39c05e82c24d2762e78164f401d39cbeabac1c0d0d14fdb1960dbb10602abe70d6ed1ed0f4adad1705a469f0a448132cc9b01a40f02154abf7b96bd017da845a12b910ef7d47ee496cb99958b384a46c257fff70337caa110b547e6b17ae133559cb3347c774f392b0565a8aa2be662ade6e3b869a248d0a6f2a5f6c4f170188d3bc762618392f012b3aca350c", "", 1, 1987178934, "f7add204e6f9230a4d1e720c8d3b98c58c543e501a1f72f2639ec9ca4e5d3031"], - ["962e1312036d1d23047fac3265917059a56ff70cb37c60070a520ef5ca0b2bc57b401118b7030000000851ab52005363000000c5bd8c7b98762ce634ab24822598a9563a5b93d2dac75860153694baee4eeaf0da7ae40000000004515265acebedd6a5fd38430c916b7794791751d0de2b0f6389bb8873c799d7dc02e2bfb44dc52de70100000003ab53abffffffff026e31c2000000000009ab6a6a5252516551ac75024404000000000093d812a00334dbb001000000000000000000000000ade09504489b7965f6c5fb81ae87a155b76e3c025cd0fec1f063741fe6536be8f824e747d0998f9af99246dd7814ccbd7df6ea6767c516f31b1d6e6f2149f1f34d542ba609c5e0d4c8da3b4e4c1262e6086b764d3a86989bec7877f646de007000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000606ec1c2dba4804b42e1178ed374a20168afd5ce9ce90de306302326d899a0a509416d9138e619a6da46bf3e8c564898da492124aa84cf60f25036296e7db55c097499acd982325bd60a80aabcbf85407979a404e066adec57ee8cb226eb2af98a088caea941a64d57ac9d0ab962060e39b3c4138f2baf35f8f3496a822ba8c1031973df3dfa9c70f91d5a9e82f67bc907ecfd3757714062a8345a5878307347770210de995d4bc91aef8efb2e35a8f348dc3417712c7d333ceefa70659eacdde7e10a2e6e95781aba2b6d6a33c2bb6da0a9d1662d58dd54e0cda3f73a53a58223655622859948ae0138982e5d74e4dc2705b05a165eb1e548ecbcb973893b1085423a031b6b838249a84edf7f12627ef9654b920e00ae8fe6bf035607696dec14f88fc8022b8f72c842322914cf4752c8f5cef86609fa1488a862e857b719321d197411200219078e526261e75eb176c54720afe86aaed49f548faeba6029dadcf3f7dc5224032ca7f4272a18c7ec282e1b62c6063e8cb2f0407173f7460e769c18842c2974c9032281b388bbbf812ce7bc239132e21f80410b67d86cf5c381eb90443ab0ce631b78ee61c21f9cc1e86bf341602bf76127fc62d32bff3a111d38cd156806f25aaa7e2d6ed2330364469fc2dae0793b88ed3d774d0c413e2de455333f9997e2316441413e734f870b8f44460161c33b7e5c2e32d3fd7adea0e0a896167f9fe5cc94acc5d180c05e6e98742f3682b27819d042d7e2b6c1b11d85ab437e419e860aa7672e7728ff84eed0e9b66d2de6f33d575e8e2f406ffa6a1bdc4342da13e2b3ab092d96d26489116d50f86ccf6152315a1b32f23281ffb0185c676e6c6be6d241d3b8d4d9957184c9f1c145eb03b3f283e7471328c39755f5e00bbbd032d8f1cccfd1e90cd794421a95aa1d95813ead916d31c4aedb67c073ca488ff7d02a5d91adb185649de39ebd2a63658255d7308a0c2ba1e707d94a2c5aef7efe1e150be9c54d5e00510b53dd9ff70c0fe07c524f3b3a3b56ac3bcc8873ac21da1cd830c725917e21f71191335131552656765afaf7bfc82d818496853de7909752e8f17a33f133fd8b1cb1666830c3aa46bb22630efdb84a2b41f09ebed1f721578177274d92703bcfc2950364025e535c8c7b083e98276a05e0f482270b439be3dd44f0e51abd28162ced2f612ce130ac22896ae415e37aa32ef7bc4877efa05dfe87529d8495f987b66cc6f7e37de30a303c42fb524ef02940e244e1b055b6e73d5abd993baa92166f0a1a67e07c4b78ff4a483c73b8657c2b08480d1177dbbb48762f57612e7a5b18ff12a678f90b2abe1348d365f8fde1efcf2154885043d4a01fbe4dc1e6041801d6479b00efd711b64aba178cb8255009a54329ffcb4e14f0ad7a4f5431e4737bc3f86c6001b2dbce0bca671cc04b2c57ecc835bc8ba373afc2c88f6b7f69fe601c2262d2e6a269ca4caf1dfc77a5c470d4307ebe0bda45ce8c5f0ed69910122ac37b3fdefa48de9511518ec9bc09f872ca313810aa68d576e524deac20108911cd1b21bed01081a39953356639c8d9523472d678b51239614950f6ad1c6e87da3ebef9ff030cd35827ba59486123823063df4840171d0c6535f956f2a54600346619dd9bea3da2e1a449e262b64da4178a53537ffd0e46e29bb86d9d25e5913f1d4203f321d0338f824a07135ae44970f7dbb61083ca3dc02524e05b1909a8a2b7e3b687f5d931884e25b63254c411e38beed2fbc344a037e2823450dfe2d5da596b68094114f695372e3a0635b29276d8b0bf5bee3d90ab76de1fc31327ab0982fdf5aeee341759fd1ee14de5ffe814c103a5a202583da56fcc07ea1e5c295e75dc9bf2c87d0ddd8ae24ed046bcd1ac0219a650f2753cc394569ea61967bafeba3980ca5cf72d11ca2927f69ad708d5edb093522bd57d17e0e6a8574082b94afc818e11c8bbbd72579c1700c1f25a5924e05e12dbc9e5f6cb37c6814190c113ac668af2bc7d9a0a28de5682f0c9831ec0cfd2238577f30d77c528e354242a1e4ba91334db0be9e0e1c562331feffbb0fbfddd298b976180c6df77f854260a87ea88a92e9dc860bd8440e0f92221167a9d18801d16c4451de7c2f78b0c546abbd8e8fa632f16bb0f5dd7c4abaf296c9398a90de8a5a9f67ee45e4f97cdb760ca7f5c90334f5171830e0bbd952bc37c7bd7f3228d02f6e5c922c44910bf97c72b050043f60f762f62da1a7c65210b95f5ca4bd90667de10ac9c5e0848a1f5afa094df1abbeef9a5254b348d95000000000000000051e0ce01000000002cb97ba23882ea8144d87fed19eeb2feb7bf61628c7091a03b7e3fe01c4c4ab97d94326dced2d938c54483e8b9670df6e9fe47adc6ca46b6cd2a5ba9d0f13e53a8c309319bf6c8aed7e4bc30cd2f18ef5de971579ca12eea854385425d59491c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e0ec680f9783080a4949c33aebb683f4ad4b2084ccf5d68cca49d7742288a925601bb948e72fb87c0a7500faf07f0b88591cce7ec0df7ef55bb0608713506847cf26a51b3d3ff4eab4a4f6966de2686b6f17041f46503ebb41dd7ff13e6475fa09fdf06241926253e29ae01e7f8916e638804080c1d87be644b0d88c48fd036030d04877befb470ace2ef0a477eac5abc62f3ff6c409cdb570e84e9b9940b73440313aca4c8c181c43f98cedcbf6651158305cd6be12633bb3a58c116b150fbc3410b136aac9b79d40bd305cae0057cfabf45deb793649909929b11a719caf617fe99302948d2688bfcf2d21d41f800ab43b9b70798707756b9441f2853ea203ff64b03119258a2243d7b0823c22110230f3997c5be50b52dfb7d33a6cfeaf8a172f89202179bbb16bc1d531601456687e939e1f254eb31621a28606e1b8a4494f551953202292e616d69fc91e4f60419df665f30090bcd8d5ceeb75d6ececa143f5696298f032ae6b91f062471bea9c0255abb0a4a3300f5d53961c9f4954940096bc0cb060d0305236b877b74e5dd944d82771af95d45d8f5792620614bf9991589e7dae69f440cfdd126c16e60d840ed2c75ee6d406df9e3868d83a61ba33d2bbc88533c33edd1df47941fdfac19d775e10cdf692744b19a312fe73323cf27f961b21432d727ea9ebd4e6a6b858bc380aed293e930b3942832200b68b0a08c1abcf8ddeee816380b6d24e5cafda1999669a8617bafc48eb155d1dd3c8104be7ebdd87099ac529ebb7667f3cff6e824908463eb59c01fc3212340fcda28789e40cd6c78a366b257c1a605edc4b65cfc158ccb7426f0d7954485b10ba3acb5ed3bdcccb201723bc173ed5faa1a4f09f18fd17a37ab230e152eaa7b6e40f0fad889a72f0817ee7376c24f820f9c0a7c779a2ff138cab82f2d6bc04b037dd2c45f5f22349689a74d8710f68bd90ee42734f082dedb8bb6391c85eebd4df9bd8904e85e64735e435d54e693d3e16940abb8c45e942308edeb4e623ab38e4e328ffb9d211028c461e3354ce4a13f2d77b87a786b54ca3166ed868e00aac36146e9d04344ee17f5de048d7467da3b6e223f57e5b0b7be3e64fe412cbc36df59c7189403dffd39027260a5287df6b1313492a504531ddde22b5f009308f802774a6182d767e5cc275b74ed607c7e37a8d1976ee4ff780af2ab0d93c9fac6d0a3fea13b5026ad18f23b6f3fafb39d120c6dcb005eb598fa369e20dabe5a892666352c4a184cf36618a41882eef4752cd73a148fd7858a3a5e6829b563e2271b75dec2df4e3d16eb6bed5280dffc9dd0d67301588ea2424d60fbc1850413a276d09cca0ab32da2ee542bbbb2a6ec71a5770ff80d05a4221d5ace9a41af9affb255e791db4fa0719d08aed5da1301a32070d1bdac2ef4ec9af291d0166df97f60bb2f0a569dcd4e7c907c3087723c49c2c82d74140a445e35aece76d396866a3d6a24f6a63efa0295107e5d212e5cbea78dd1124ab0fa6b15084edb2dc8274d7548a6f7ebe238cf3468b7a5be57e35b96101c9502143879b737cfe19d511cd50706388936a6da60f83de534dab033ce31a42f4d35b9261197ea16fad312c7ae26e4aa2617ea31845a9c00d99a3822d616ad3d14c7d82b2a30d7ec0c4366e88333c348e832335609516db080303c886765f10eb35862e70545d66b57256e432d8255b1d80bdde73420372f1746e2567384374235cf17a33babddaea4e2754b5f440d16b8a87e35f09e88c77e20ad8415e317d5c7dfb16f1c90c4d26ed31e2f57ef57351c966fd7046360b79a54fa028d16ad51135d1c6bf49f815e03779e64fd4476a747183e68e46a4ece5115295727882b18524e9bb6e0cffd1ff7a859b5a069c60c155aac7f35deb636c1fb7890e897859c3d41c0430f680857d33f28072f5ce87fa3bf9e852cf4aef5d589e293f913d960b0deecc6d163dd3159e809d60b723f94c1db2ffe23171fd78da5c74a55b192d89cbf23ef543732c1ce8f9f7fe4d4a787b86beeae55e418fc4bcf886ababaa5529372c6d96980a3c59fa0494c83362f9ed5e84db38b65ad49fd4d867eb372bfc039dd43789947be540287f42d5ddbf002543afbe985bbf721e13a4d107eebae67267f443dcf47883f02e3a2b8f7189e303ad8046b8a13e9cca3402d96ae414a88aec97f3b9bd5b153a3e133cbdfbb9b80d7e1c38f76a18d6c1dea754d31f34e81b4b290fb610e475378a384c7f4194cb20c76364346cbd2d7346609faa44d4283a172474de2921a4ca28dd9878506000000000000000000000000003d3931e72c3bfa1c10b1be6db6b35e708e8317247c8e0e2148986010e6f1ba14acf7a86d684630f34a2cbe7e88b9b96fccd22e26f913db02368d89efd85fed4be26c188dbf485c62866fb1873424ba217682ec30b88c8fe2af29d2c2cffc77cc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000067f1231f071b8c71ebc6887501d749e3d16f20ad33a980676f80ac33a582e9d0cee51e50b61e5df5ddd6231a93b1c47932fb79e80f727150b269dc36b4004b068587ed9ed7e3d649a009988fc707abd32754b32b25ae4592fb58706c78e1ca444d66e37c67e31dd486827044bac821a9fb23d1da4ee09581977f71fe67d398f9030908fc017fe5a8ff5e5fda0cbf7ece450051bdafcb8f1181e57080500cd6aac1020c748e5e2e8e1b7415f0a5e3c2f08a11442fb89848479bfca0704d72274cf0d30a08b6fe5914020bfe9b79a578ba517846a121eafb93322dbfc0f01dabe765e9261ce497e22f50e1095d64fbfe9b388709c530ae5f5f236a94cb35ddfb510ad1e20300a64c286577ba911b5f57af8844ea8606f03bb5f54a6c2dd02021d227a224030321ff811b329136ea47beb76c6d7e1d3370aa9ef2ce34e2cbe57f8b29d3c6b21c02202b3d69170a73bf81b5182692087a453b8fa9284ef3d45d13559b19f38eeb730303bf248b818c6cc2c0b213a7722cc1fdae51ab1e5c4f40f8eaff83641765cbfb03220cd2381724d691080355dd5bab5b44f8d92ec8c2dee3b2cd08ef1a1efaca730ffbcddb2fa617f6b8a70ca880c043f8bc201036e62aaf8cae2a4585e642c94858b2489c0c00f130ca3e9722426673dd6585904147b5e01c9232f09ddbd30b03f8ce8e62d67e704ef38dc1765965062d20bec4bd275f114495cb56be34dcfcb2b4b88912c48a75cec809a80e193216ca55474e7366ad164bd10316083db06a31c122b761136fba55088564c4370cdc942950f00c15fcff92ee161f2d7ed124a6d0da9b8bee8967f4c154f97888d8169ddc8fd83e724dfcd43256048bffec189e6a91bc13d70ca3da0caba5a17dc1aafd85f562e295da6c576ec0563c412321459e1b011a4e712dea677deb4e1174ee701508de5bb5a913d1384f0cc03dba444f9b628d30616c55e2360a17bbb67d10dac320d5a21c76803371f5ccb8adafd6df2174ffc2ba3027d5bf61df44290eba87aae491f4e342ca598190dfba55440fe4b0dc9bdb298f7e8c2ac815d542b6b8a632dd4c6c76a3a90637acd95274e0d73db0faee820a97c14eb9e37b5fb13328fdf7309c9a5c33380f1c9bffa1d77334c5001421a20a5775d92ac5db531dcb3ed22dc2e2b7b0607d40295b7177e95cdc50694cb0cade2d31397e3680f3ab765454a01a073050c550a0a8083686d531535839ef88a5e4191b34f3af3bc48502ef6c6a5fe76bc3fb99e322d2f09e9d57d35064f075be1f74463f5ec230d8f6149044310d1544039ec0fd6666f36a48a0f6fabeb8fa3a4eac7d950446a1516c64137d81c8a1b10d478d2f555b3946d4dbe5032d940ddf57df8c0e335d9466ad68f639b7178b3f1748f6c6a0837b1a68d80a41820440cf552d59d0ad757014fc5e43f142f7bced1e0239cc70436a6424b49c1ede32971872c326a661d27c44ada164b4dae44b3ee6dc2ded2b1f2c843c8e2bab510f9eb8e050f4811f9be1e8fc8685f2d7f6cbf83ecf1a4db9414ff614f584f36a6742e3a16651b45fb711af84441dd76c308467502846eb1a4e313ca1c97dd3761e434ebb8c899b98baab83fd14c4fc45b134f2daf5aee3344c32b94f4ce6a8c21b1d1dc86ab03b13fcf08cf70d64241b0a94fbb3aace31f67f4f937427afbd18967f04065a938bd5b6ba8caa8dd73977d0c9a308a46234f3ca51877621525a6fffb44c510a3f4ccfe51d9a09e3f1573ef5b09ff98689100d6b9741ee82bb4c7da3f5db3734c23f884f9f3522cffd9dd490abd86f8386066bf3aca91541422e81a8937e6b2ed0c1d389e24a433e44c130f125f22c44366f78a9eacb51690ddd566b4621cee8b3509dd8344b80601f507cc7831da803d7e06d40353104dd50b65f7b9a85ced4e4cb9f01323af9054e26d4494ad828e2f3aa2d588229e9c1704fdc8e3da12bc28bb0a5715ee17342e21ba63eb0dad83b9d61644f985bbd7df578eb5906e6a32f02bbe081b8d4800b3bbe3544fe0558d076a90efba46f15f45199f2a091cbf03bd57dd49c69a21476652b6ea20a54974d64f46633dbec9d406b9fc2b13460deb4d30ee8e426efe663bb21c76ed2439c54b115476889febc3be297d6ae6ce526585964945cc223d8b421625b499f995b4b218f696e7a72c2b97b561f70e94e60a2c802680ed56180aad956d89dd95cf93934a55a4a7fc56261d4eddefd7c46e061bedadad576146171ec50040d770be04a8733c98659ed6b57166ec0823cab31838cc77708b97646d3a5916550b46a6326961336ce48c489842a254f2a4375a69d22ccd53fe9ff7ff887fd9762ec6b208b8c8487ae84730a48c6b906aed971416fb40960698b1b3f618592b598c56ed353fa92092a627ce7f8eb43bd3ba8dae41af682cae4611ebd9c3b074307", "635153526a", 2, -1612124312, "c59cda5d0c13d7c2d43709c6ca3d8e559fd2622396317781441eddf4d7c7fc28"], - ["1f6475c9049b26acc3676816dd3aaa647ff904c54533a8103ff528ff1dca017865f64295f50300000007ab636353526352ffffffffab544ad4326bb45f2cda1afd6900492b0fdc1f8c5e28196fafe5e652f5c424bf03000000008767a3d33fb3dfecf70860c0a1c060a78ec22047f85043a23df2b9f85a294d0e12b42a480300000007ababab65006a6affffffffad3ad4a67e9da5cf09c9f6277a79c5beb9c994dca91c6d11a2ee5ac537a27aad0000000008636a6aab6a53536ac212057603377e3c0300000000065251006563ac705bc603000000000365ab63467131020000000008515153516300655100000000", "53ac5100ac", 2, 1025636861, "0ce4ad3390de1442c6b75dca298a3a5999093629a22198065694e913ae7102ac"], - ["c94f421b01cd130cbc590d508a01840f938d221e8a206232201671bbeb2322d95e86329a370000000009516565515100abab51dfb9d9f00328f9f90000000000026aacf5b41f030000000005acab005363f9ac260100000000036a5253acecf299014974bf010000000000000000000000002ac31ebe3e9886310fcc1b3b34441cac1f4a1cea46fc3fd996c6b63d4096a15f087dc6b200b94d46310d8e3663075c73f7f3d40a2cb9bb74fa68031299d919d6c04f659902443e17bb18f1157ade0349960fa91383609ef8a61a36770cf865ac00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b42769baf0fe750e1e74527ca0b7349d81cb9521b3aee1942f32b4a0c763f7512115e32ef377d0d421fd6d0026ae9f568e9a27e6da38f079baf1f59f0ef84f95f3d35f40013c94602873ede859e324a856273d4501e5481fd9f11906e26fde7e43763508c32bbb6d08a1a4f703059f0f68e8faed927cf5eebe485065aef1e511032f3395523cac35d43b1b0e869a026d972b68d6cdbaef1a5433c4c7fe5af83f580312fb4a868af8efcc4a1bd55c3bc071a2ba6c40d81d8572c48e9209b94494bee60a0f44837fe10167e746773393218e6795103b1bac7d774849ff4f3455e9fa13b603c942e3e2028388052c08f7dcab7901c8e22f11c1e729646b34d4a7cc36d67f02002c4a7902198dd796621c1712f7ad1b718533fd9a55b540f90f19783e9b9fe602128ac1d87e2985589f060fcfe31548db979ee1fa4e0c7673e1bfc7a11b152559032552cdf68e5391e91e467b35b07ddfbd88d72b3442e56eeb7be23fd7be3aa6ca0330639194317c3f9e628820185593ef004f49359b4ec0e1b8fe54fbdd5ddc6ab50223da9185f89eadf8935bd830adedd7b9436163c6d8999f385a0f831c10ed61850aa1dc91fba1c3a7279f09523ea4f28406b21ee7243bb2f7f4141f09bdc639d18aa472f4251ab4893e89dfd9e265a9ac29a751f15fee540f4702f558c151493aed1e8b7f82e47c808978b67ace721b73a4a23291dcdb8e45072d8c7c747b797c4661928324de4ed972a63fcce893b367fdcfb2179b861e5d79330fd57ae13071d57b0d8de7499a234f5af8a453ae4082cd32b6faef6ca7e8683e936fa39fdf34b37d06a775c5f1fbd5c9638d4bbfeeebb586fb5edb73aeb1c2fe51e53424c2f28e089294cee1dfba9c201dcf08874a97bd5085898bf341bf94c479a7e45a8ae86a2b982809e23e4abf96ca5be7776da77126905a9fcae7ffeb0fef10d6a071c245c2c6f83095d5f9af76aebfa339954a32fb3caca1bf45f4b9b7414c85771ed7501c0b7eb94cf9d86f27d5560be3486f17b6fa38c1447f286319a9ade7b294ca744859fa8220a7419f72fef564f52f09012d932934e1a7af66fc1d93ca2ae692c4ef6ae83d1ff0c5e567177f823822391df780ebe024f822e09d749fa3b6107d4922e32399822f0663a684a78c22f75efe0c0bceec6a1429fc134f166ea605338a3f71170b8e5949a5b8269a0381b7b76c9b96e2d4e328089b619f43ae2ebafddc43905677a11c75f80416aa81f4a934ce69f9e680d62122434417b487b3122eac284885e38c07ac6b806a68f817d4a0bd5e9171ab0097f18e7353cdf7722da22f9bf55f50bc9aaacc9b82d21cdb58b1fc5a37a9a448d569639dc33b9645aeb435c74e89bcc11a845c7be147dd48f24ea99a9ad8a521969e6e76663f35325b01a38c79d074597974f91347b4b0758781f68095150be89f30ded30d6eeca4ca61e473e557eab5e9449a3114a1a73034bc6db374be65110d07fc63cbbf83a1fb88b686c67ca6d450b41073b252307cbc88c84654fe7d501a727339d1785bf631dc4452b2ed5b0d40f18b146b5e1126babc811e4b41b7c5440795985d9bc8f9f87d5b4f3d80fdee38bbb45c5c06886600bd2699d5315d7525c77ae10ea28351d5895e9a1653d3b09eda70a9b1f25ba7ca0b948c90c75e996b7ffa13b8d6b4c3324adba89b81191a50ae045352e5f5a1a18cfe3e7e1ff5767b327c5f69f60a19b5f0fac21c89641f06c0f0a02e035ba77ff3e809f09dc00be454be86ca265fe6d6d8fee72ae731e9dcc5a58f45f90cd2af88593df2dbcec6dd652fc526cee2b1babda8848aa102ae0ed1db1019451dabbf825e6b5674d84fb9094bef2a5ce201866f54cfa038675d85efd1c3ea689cbfcddf2fc317dffdbdc31edeaeec2d628e585447590d2092305ee0c9a4a6869eeffb950ecdd636d2f518d6ca5af6b182a0625b4769d9a1bb98456a907fa966694f5eae28a478d5de8eccbad07439a4a16bd7e78ef36a9e19a1d1fb64564fecf2baef045bc67c8f1886ebc6d5d3c41ec331b701eeae7e1b9c8adb67ea50e54cfd19e1959f4c7c866920805d9bfe87c58e65b9303ddc34a5104a3ae49bbc923db7e9ed9024eac561f95e635e4ef5e98ea8ea71eec9085368bba488fb127631d6185147abbce28cea705958e36db3373af693d0365866fdec9ef546a3c47e21ff3bda99b695b84273768aa846e7d9c9d662d95ea8a2f5a55c0ec11a1b6dcd0a4539fc588a350a26d15c0e6109d7da3b197c1c0a3141e8ac9d71b0d6753edb7842aa718c035b68efb6c8480dc3a6431b72f936f9ee867255a2c60837d4c83e8fe1b888b99c9b9e220b9d56ba7cfde69e4d9c3f47e396fb1151f3714c0ca05f74f4a1ea0664c11ca9e255f67ee310b2ebcc48e3d7dd7ed2ab7d088810b141ea453408febfe7930b", "656a53", 0, 107611704, "a2deb721b45673d45ed38da59a17a1e839b2ba6a243a8064616bef2edb1618a5"], - ["a9f3a5510467fb857674a3f5c1f832394f6b14ecb0f33e13fb47e1a295a66bca126730740f03000000075300656300656affffffff17166f3f106590a22577002fab710751357ec61fe2e75433255670162f1b5a6b02000000095300ab5353ab525153654c913e7d8bf21efab8dd424396ebfeb1004865d445b0ae1d836efceecb90497a81330600000000026a53739d27878853a44a4bbbd07ed256907d959f8e19a89972dc03075d2ba3507c5510bfd38c0200000001630b2f8a1e03ec5e2d0500000000096a0053006a6565ac516978c000000000000263ab9035ae010000000009acab53ababab6a52520000000003000000000000000076dcfa02000000000e884a785d7268409dd4d48b9152a462c7c4d9824e3a7c6d972ef541467c24372f3cf5e5d8df9473b660fe0dc22ab438559d3c200c8980ae018ea2b893ed5f90855469624e8936ff5a35eb302dee30f28cf2ed2cbd17bf8cc20c55a78904b9520000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020becf193bfb20b6b4fcaaad1dae0847e1e76d134fb6d8c28f3115f25d01b03437ddafbe526a8ca9f02f97ea11b3cc85e7cd8c4aed6fd1ed04c6efc890b77c5c4f1ebb6a22f224715c56e7fa1a976388ce0bb16fb86c649f0ae174982087a7447e0b97bc6dbdd88e42f8c364cbe0c1e4b8ada265d186ac0ab956cefc290d3a63031254ac12a47714bd2b8ca2484833858a5ec60007344a09e86591970d20f57060030539e3442d7f742c706d368bb1b1e3b566c0dae1d490e15dc04444359503d0d40a2fc8c23821c5a65bc5a230a1fab25b43e57fae7db16cadc82bd7661dc820dc711de1d4b0371f2fd92dc7a7723b6873c6e5964de2d0a20c43ad8844772ee460880201092a8de45afa52317bb9ed27d63e3634d86f1b3c6dfb79c4621dfd2f31f2fb0200efa867a6ebc27cd972f2bcf19431332df0c5fb4251783e7088e57966f2379303059c7eba743fd6effa0a1cd658265b26eb18c658f357511c4df30e0cccd8f97f020426e9f7a2e02825cac9fa771e253bc1481ea6cee0f9e4ca3ab4b75dfa1d6bc8022346a5660025363af68801db5caf06550c58aac6ad180d66ab0d32891478e8d01acf3593530cfb26579267afff040b0534f655c00d150e8073c7d6c46acfab84fc77a7a640a75ab680d769974d12c854f1e9440d01e31e537334817c64657da90b4a1d8f9a1707d4e1a6e7c54b2cf5505f48a7b10f27552c41b2b865cb0a362fe3fada94f8a037bde6e12f9e91b7ada4ea1616b612d6a76bd402300362677d011465ad63569e66350458d822ea0105546de26817bb7b5fc5f9104c23affae4b40209a5ff986f2892d138abbe9b2138e5edd4937609cc084d7628258a7d042a182fcba317abf91f562dcb1286abdbd851b435b40d3dfaefc055ea0bad69add1c4d8f37bd70cd1cc02c3122e2c49f487f1e8f4800d7ea677c2afaca05a3caba6686dd857be2f2d410836afb01ad7e65b51cf372cedb320cb1f3fbe06f4ddb154393b98d2fa5b6e5d7d7b5c6ed432c2aa3629b85a0dec5c849b580a984df9120af3f94bf3ee6077875d5c64cf6ced1d40458a329b6e929bded890418a2ba12b0144a6af86b967181ce0f5f468032f9b3bae4ec37673792cebfc2dc7b2e4ae364b5055f9b331468951cc2dcca5bc0cbfdcbc9c373abec2c2a92d81c902a15b8c397b91d5dd12bf2a003239fd00b393f929da0f431314cf654ed589b3cba7d1228b5a9b62d7c3170b7da62d45f86191885969fd6356f890690c8e9747c7e7c79873197934f7bdd699dd27c35aefeae27f717f222c6d1d7e3cdadbcd7e380334d60ca55a185a108e9105a61e154506a3be44c16e57d8719e04aa4fd01ea58e7135e87ed08efeaa48f64c7e2044674578d149cf7f36926e06ed4455d8d4df61a5c718b0d3b1c4eef491e29463ac145aed6319899ae3944b923376cab70a570eed036df82235181e187afaacfac08596b47d4c21bf7d0dacaece79a3adc628287302befc9a5d51b4e27c66675e9f0b79f9077d262535fa3d4d902a36bc69c9670955061a1a4cc280f2546930e310f2b899abfd020d1fab369290babe262d8cdb4cf233e20c988a251440ceb1fc3e9e6caf20517295c736ab8d43722b17b3f8c6705d907daa47f4e4dd95c574d235789b45da9563d4d541a3274f8046ad0f318a1f25d184d5386a4d31475b649d4fabc743105388fdfabf53a0cbf702776d1f16334fbc0cce2714c48f93e9682eedee13e0aea17130d999cdf9699a3399ca9a4311b243d49a8f11a93f76f882a1260ee6709546290d3d1f48a531e2936a3564b86cbbeb81ca76cefb3432e2ccd0c16f0e82a31da4e8d3d76b473797efc1a13dcaa116b9d732874d18b1a09737d6f15c272b80fe3ae6399a741d1355b24f7724b87323a37551c2710f5b99c8259075ebbdc3c1cb0dd8db2a49fd732503e75ab2fd66bc8339d67002fea8b40c6ac43580c1d6e95508c87449ccdfeb7b91b66e343d0114a563fe8b58b9897a54046dfa8cbf4f43db40557658d2ea9f5322384fe81cac5f2cabd8f2d4bb14955851b0115270f54369bc887d2c4644447bd78b648f28bf0e646339ebad7cd9c7bba4b23aa130bfc274ac9e44757be19388c7f8604f9e89710a71dbd863420c69f5a55e404c49fa675abb0aedece428b69306efc5dacfcfce07873fd47a47fadfb307250bdf2397534e97b57093ae3399ae0a79970f37ccf574b435b72ebe15b69bd970a9337102fcc999169cff84c7c265373156c89761ee939011c368e9b13840c6ee8b6351714c3a958a610a47d9000000000000000000000000005aa19a0831ceb3f3464d046dce7a09f8100c59520153963f91e71d84adf7e176af18532330d56206fab71d33f6edc9067a929aa52a84c7abd8b37db08c5eb2c93d8623285e27bf87c16e121de204281b74b66a6745ed82ba0a75c660790c723f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c43d1877f2a46ecd981b319e689a3d2bdfe1c5d0356ddd30909971c46e3feb809b5087cabcfc53f062167c74a08f1960fecb417187a93b8e2a2da5a12c4386b69fb7a1791ea1fbfd3ee807fe1d4de10b0ffbc4104769f21f1980f85f479a4181fa185a6061c78b764bbc088e010865ff198f15a29c0a50ebcda0ec8ad8ecab903046b58a71a9bf4f4c44ff4d7a8127cac3382f04e4d2a4b8867379abec5fe76630221992b8f041ad268ba595b1c4577cfae51ceae93d5ba142cbc61a7375c94cc530a094b1bff5a081b9c20cd03964d248ca5e8ba9d7cb00fc96cb4ec396b16cb14272eb198f06003d0724e5800490786139f61cc88c8c083a7dc0ce778c75f8250a102029dc8a9e6d58f27dd1f4d160ae7269eead42d710e4bd0260dcdc4c2cc4fc092031624573ed98417c56cd11212343b57cbcbe831b47fab301e7776be83b2b8c68103075227c95b6b56657d715ea01cb18c0f97d8be78360d24b080c23cd6fb61b403020ad49256ac29079227a9c256d6a5a444532292e6629bcab35e2e0f7795b6d8710225813af57ee6d595aac92405f1416dedb44e3bed61e2802c0f73051d38f597b4498fbefe4f30756962b7eb0147bc795887c366a7fd3be30e8c78cf18919893a6768d1f7f0f4f9ffc3f08a9841e95321644249e78658731e8905be0df875dbe2ad27ddecfd9af2ba578af5ef5c17ae088cede16bf05bfd888065da4fffcf903458e8181157a55a6433e2c062a5edaad0533b9a80d096feb731b430f943e8cc36580ae9fc41cc1d8ab9b143f0e10abcb62bece9462e49c907d70da7cd54e9d7f31b439394cdcd9be06a4bf678f5e0cf3c8ff181d1bd3f3dd8e0659ebca0dc92ffad1ac06d372918ed68028cee77bb7db1b195bd0ecf7dfbb1a4a1d10c7bbd91de1a9fc3bd41468703528eba34bde454b3584a51b3304cfc78bc945e0b18247690d104dcdc96294d8603d674a8497bd19919b7d86fb2c90d1e9c9ad709663ebe602184834ed5751c17af5c2f6aa705e713f1543fbeddda82ec429a7ed8fb216a3c744baeeb698d8521bc34985fca48e07288245252f8f19a3f0bba8dc08e599d2f459230f6f8acef52ffa41b14d74f84addf92216af570d89be887f1fb459709100abb47abe5043fb70b01ce8bec7deccfb7e20886c2208a2e288dfb806b0a958648ab0df13480fa7bbcab8cc878e0f28dc6e85233bb177aa081baa067f4ce1da17591187d32c6a6401de1a79b361af6332e6a32e62b01a36c8cc18e19a529c3649e7c7206262f616cacb57bf94303bce4f6b851289108265a65c0282c7484c7c5c1bb04d5477e931415823478733644e3d8aab559a9202a70c9e1f902fad053289c085b22cec380c4f4c66e4a3059ba1056a80c4f2bab360b7235763beecc55504b794cc7fd4e4bc1e8dd6fb4e0a441b192d0465127679b547d3b2d37012badc74cf6fcaf93756732e68b5efa19bdcc5f023e947cb650ed9d98c34b50b019d87be6c194dcc6a3ff834f73c4bb61a96b928733473a782d688f019eb123deb5cdc558855fd0c1ec6ad3f8aa0f55078e171bf4f2b1e2326e4acfb5657933d37152b7ea98d2ef48026afbcdb29e513ab3c7470a0031f6e2d0133fb017b2fc328db1138876323cd34013a5627b43005ce3575654eb3ec4df78ae86d372e1c46adce40aca34552eb2737a38424e003e7b5d7284a6bd9bdc924ccb31015b5cbf2767c8d7a7ec6026053b01175cb9e026d005f40db3ed028ccfa8431c3bb3c62842476a43169ed48bd2942e5f2d80f338968e9114353d6116bc9484a501b01b693e05282be7ca23f8384395f58ec007d2d8497fc02926f899632b0a9e070313194a48d4472775160bf80b88d928041446c9180a0ddb3791c557dea165576d6740de2d0a4cdccad8eb176a011d64632079d6d96280eb0f37c0bf35ad8572d969f8bd9848703b85be758dfe4e1135be346a14e89b1d12b7a18a5a8e16fa1b1b331e31ebb0d70f00ea3e0bbcdab7dbc73eedd6dbe529fa9ed2fb4e2441733694b9c79e53d5a0534e69d3efb958196e301239e686bb4e486188c264314f0bf7a87ce3ac4f65bc7139106335b43cb53f8a36d05b459a56fe58e751ceabc26a882877a3d66b5135b3d11a5fe7711e1ed2a0f57f34d7ad403d3c356256015a48b3dff6441fa585b780ecd00f2ea1e6385675ed5ccf83e519f22e5e5e3d8bae7d5f518eb9f8cfe520694d1eb967093cd5ac1d3477ccf44e8e1cc39a216b5c70de7f7d5e80c3fd9876bd02219947b5d7e430422a684bfcc69fdac5de49b3103000000000000000000000000d2af3bac5ad4e758d9380c8befff8372d6a3651b935a015b451a7fec50da9b98cbc0290fac16822f7c9e095b4fb5ae69757af271dea9926b47ba5f42056d428610cf9d9b8a94ba7fcf491e3b6a37e37fefd403cdcef2b22a5374e626bef2e91c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d32cf40b382a245d821e0f5197425b8e50cd02ef27da641bc6e51d68c3dc712e5979835e06cac53943175c79c43804c886ccea09c129cd429acd10d8fdab61f2f10802ee88f1babdb8ae8902f8a8f73c78bcdc68da7512ce6902baf9b8ec424dcce37122a1991af34bb5174f7665fae46fa67e436606e0b4d310e40b49ea1561020e05eea90b9bcc517affb8ba97785d224da332bdb1753e290aeb8302bfe75d68031666d94be4e3507ad6bf911040831f9fa3daf5a44866189ef43448a1bcd70a9d0b21e3b5a22b9b4472918de6b7fae452db4910ebee92e1fd1fde10bb846456a7b023e7cab89ec400ba904777cbf0275c0e24f656a423af7f64d1c84a7fd046119c0322496f937364eef01d7ff28e78923af80e867d644ac46a4c4cc2309a2883a1ba022ce21869651a5c9ed5ab8a59c631fc0abb5713cb90afb6a3703bfa3a34680d24020c3d23012b46fcaa1f3b0c4f4f68835980fe9dd20b07e2972725b8c21953933003141b31f69c651f131acb40a27a8ec96d004126d3e80f274948f5d1feac5e55f80217edde51b5821e25a57f74ee832c5d6b97e7b2b37f94da00f88667227a5e895971483e4a38c50e96e97a8e1c5f7bd1e411f27b52cd09f5e547177820afe2f8085c6e514616b9a852cc334a5735551afd81462b41b6e16413ac4384df417c666f281fe50d18521a006133b05a51d75a6d18decfef80f5d8bb4047c094680c9948d754d76019a9ff70e1fcdf34586e706b02d751c802676a4cc3b976534de54cc4a86c15aa42a0d05ea64924949477856947be20809c3f7ccd37a326526197216f0aa636e3c8d931f03ae7ebc314d9f98faa1c2647b9395a811ac716ecc2aff9342fc72e93c0820c078b33206dc7201cfcf465ee5b39e8f7ed8aa2feae319afdbf201e7f2dbb6307e52332577c1c5a222ffbf7e7c151dff4a9014a607972fc2e0b0e5f20bea147c68dd7c24657cb47ab428a35a4264736e589fc4019aad1cf7279ce9914a322aa7ec261c673e810bffb0544e4ac5078528b969ded45e2ddacf3c3980d1091d6cee3fe63278ea2ebd928d3cbe6019c08c51f960bfbae01266c98b98dbca04086dc497ed9fbdb05f93298d57e2b709bf8032b3bc5cf4e6ded70ff3b64056e3c53c283924c2a31b4f3734f6a3f660fcd5a5526e31348b2a8fc1cbf1125afc99c35053c390aa74b863a900d998eeb6ad46967f4ca3454f4041bb3593c513e17033e69edd0bb1b5ca531b0a6ea2247d701b85ec07c7e2e74ed68666cf4b2b0d205aa3cfa04c7b8a1648f0b820bb280ab3b25e2c452bf06f3c03db2f44f94c442ef6b7d1583389c9e4aa76abede6ae01c22f992ee3ffc0667ac733e11d33d5fc3ce2365f345e67d57fbc933ea626958a9686c8047e52561c0fa8d0ad7637dc9e5a446ffecf86e1109e829789f6cddb139dd916e7f483a27d7bf61a0b8aa07751c8261f5098b672fcd061139e02467f4e6d550094173bf6720d014238e66f39ae7a14a72b440d571d30420c216bea74b7ee323739cb9b3c7ae7ad4008a7766cf9b2d7d1b4f1fe36618c01c11df4711e8d6293edf0e7accf1c2620a83c8bc2a426d19b1dfce3293029616f758bd6f4fd51c9fefdfe4e15735ded92c552621bdeaa7a51a80ce6c229bd42d4df72d34f13d15de9d8cd3562cc910a7e54a593bae1c02b5928335083bfd2134fba464d73e3b6847b520429206edacfa1e5cdf88fea5245a5c2c394fa82950330c4843292db137be2e11ffdd5c7a1b128789b6d465dff69dd48e0a802a2e9090a6e428dd75665e944908ebb8d8ffd40901c3bb586d6982245fe15285bc1ada4e6b91f9c164bd998df7dc3656992bd274266ab9f2ee40de28e930e2d073f3b76d8d6dae5efd4ee43f2070ea2f706e448a9c0d4f5268e5226d043614fe06a975efa71f63dc92de5a97546dfbc003a70849ef4de3e93cee20277c067d72ecf3864815cb439c55e8ccc409acb1888a1060952dc0b9b66a8d115dc23c40288ade6059a84fe852ee362602dacb64eb3a20f2a376812f6cf4ac6eb058a6e2c77d5cfa66e4f429221cd8a32f242a1f6f2569aed7d5d2e5497bf391d0f16ba462a5d18f600485111cbb7f3edb818f60ac910e39c1178838527e879ee913d39e87abfdfa9ee3a3b6cff04ef04022753b9ee68b36086ad3816c722be81b38525453f12df9588dcf032e23b0eebb2a424a61e3b37a85c4b0d1dd78689fc90ba5021e6b21875a9c6bca6ecf452c57d3f870a8832a31a3210d9377f476fb7cd810086f687ac29fb1ea9823cb46efde653faff3a2ece3e3e1c82a579e01988f1768c2cf991aef55150b47b351f9e916d56a9430cf171935edf964568e27f6e1c10ef24a14809f125869d8741f28cbd35592af334594a0d313775b976999a913ed77d042d88069549f348b6c490a", "006a5163535153", 1, -1278062566, "646573a74f95625ad6292b67eb2e7b3a3e47c5c9a7a039cb077cb23d02a9ffe7"], - ["fe0d47e0038da737989be3e12a926e1f43a0c27e01d34d3ee8b358b9ab0e4de6692fffe8ad00000000035351ab2948a60516a7e96ed259feb50cd1ff62e3858900b1c1c9067bd32177b004e1c82ca45e9501000000016a096080e72436612083a7c2e66e3add1c1521353ad6ab9a7d029e6cb12fac6e3864ccbef00000000006536a65006a63c41634ae04d709c2010000000004acac00ab56945f050000000000405ff2000000000002ac0046814705000000000553515151525bdcae77", "636551ac65636500", 2, -3340207, "95314e3d1b84e28648f0eca4a1f9337f92d5fe652c5b3b178bd926a077d89362"], - ["c0acde4704aa909f5d532fde665a0d52c3904d86395f1dcd9d12617bffea8352451ab30480010000000252abffffffffb524d9765785215589ea2c6c4ebfa9b9e37e5301b5f97a0a47c08c9033bb25fa0200000005006a53ab51ffffffff2cd213338b767181caa2c669ce8cf8456870aece35e5d85545f66f4259bd589f0000000001525e77af7dd0b88b8a9f793ebb044647e537cf63e6e921fdeff4e49338f6465e8274492ae80000000004ac6565530baee77b039abcac020000000005ab5365005374252604000000000500526553ac0418c90500000000055351ab5251dc994cdc019e061303000000000000000000000000475d9464113d39e4862d69ce3f0f3381ce2623b70864ff47fbb3e3e9fded64cf310b76c45286f08fe7ee837272384d22c3d395081f43dc3984a3c5962048e9453e56707d3005c4dad15b0f4501109adfbc5f3fe61b42e0ca2d833290e133fc7b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000057c07ad16c27fd386d7d15d9aa317b3d4547172d0e61eee9fc6d8829055f741d265e335c90b2b963ad313898d0aa5d568ce834d59b67e4b36a84dbae7b8fd2914f2283c44f6b472b60241c31ab84018c8c9efcd8a68ec0c81a35483d0bfbf82fa42f90c8258f3521b64d7761824a07069a19ec25fd27016b7ea27ec3899a7d2b030a23c1cf3a58dd0808514cb700c71628a36fd3f3eb8b8a0c599768eebc98a0ba021d5e1bb84081f938a86ddf00a1170b617d33360d48618e037e6423516126945e0a0f248f185279c77a49316c6ce8ceee84845346bfc165bfc42517f83150da8d050806478d4a164ca53aff027fd43b97f448d4bce5eeeb4b46bb73a40247c077f40203693f7bff18bdb1032baf95df80850b72411082a0cb36cee3e6e2e2636e318403213c0de3239a08b3b76c041291ef62cc233fb9b8d3346389addf7b882d07ae89022b2c195a6d73ab415c92d9a716b326e334de20c1839714def1d0aaf7929eb8e9032faa074e42b52ffeba6f0c654d5345c1ad31ac0afcebb75ba7fa6ccf0602622503288613fc12e58431bc529657995dd3f40199cf37705120a9ec231bb21c746f993a54e66a04bab7cc9f81bbfc40c1f008a349f656a1cbbacb290d0ac33c67579ea313df804e9c1dfffef1e980a93ac1de485f0f16127eea137b1e866c6c2fe47dd24bc8f2c6522f9d4ae0ca685c5b2c5094cca623866b3aa619dad4b1c01f5c2b4f893079a5a56d43216eb199f2cbb1f0549f296cf850cca330d584c987c13b7e1e0ca2441ffec6c5d7a2727ea417f87bfb479a691609e0a8525cac51350c3672b50abe0c2f984e28ac28cd8d8d79fdb1d240ecdf8cb8c8b7e2159737b1607b2c3d8b29bd61e80f7c59529a2327b3e0926b50df31417d4e66d58363c531ace5d007100e5e97335b212c81700db0dabdc75787a29c848f81e81231d6b24c93144e85e8f8182b32fa96b25f0281790ae86fd9e7af7e528d6b81e9d52a803d3ecd63d167740c70a0c5aa3b1768133e843b890d9f15162a50592f1c05105faba435e765dc8357fd0fdd2ee9841f4c8604fa55e46cec8c56258a7356a2af0e8577dabf1b7351294d7236d62e7e3cb71684cce60b4462df8bbd7c54ef2f5a977df4f4d8a88cbda04a232b2d48ef67056e85a8e2400899ea26ba0395c3e775d0499dd6ac1d9acb5219f56651c414c24e28cc150bcc48866b21064a8757018942adf6fa3e2e9dfa80c69758b19a3197008035b43808ff69b34b396b5acacf164b58f44eb101c2c92035f7f0f6a34be0641cfd57c0e4c3bb5b36a4f8871968cd61d87deb00f34ed049db2f40deaad873acb6ac928b49da68a2941a1bdbbf81570722b6ab3cd6a2ae8c62fdc1c0b80b800566c09c253a991430fa404eb35fe9dd7dc08ad6a17c8c71c1468711a238ea6585c1840ab916f90638fa403f529016adfc3533b508f3b7eff073011ce4957bab46570925e68c24b7a1d87b11651dc3e6c4b7bc2624d82bb96d32630c94b87415338453e172c0a2d1693b87b122397ceed81f84269c4d57797816a30d216a4e593884f571988cda75d2cd64b3ae7ccfd8145846d9dd052a9d9baa688ed310963f74c0b1a5936d382508ea8bcc01d80458dd740f252ee90d1a7e7614625ebad3403f3451e22bf026d1d6b4a80621fc7ccbc61ba879d76f3aef58cf89494d91f57bb57bfc9020dcda8a5dabbcbbaad986ecc475aab897975b30902cd00a362a1a27dcfecbda3c497a3a4d143b6c65984ae36cb44e7ee5f542bcc87a619c1e7a8d9af650f8316c90ec013fa5b1f0136e88bf4231d10f364fff3a02e1f066bc1546e62bab4317aa40e3d1b661d1c30efc30c55315f26bae4416092e91bbd403b3c288061d0651de3b411484c1a033542db560fa34b9b2f2b105e2fd47a7d62824d23aa8d9142075f5a5b9c9be6f838cfdfd02cb25f922f8cb7d4973c4cee88bbd69ac0d8f85d00d67281a4206c4b4942d072a2007e3a6a82a706633f839250497673adfd0384140fd7351644a4f5ce320ee0d465de7607241ba5f9572105d71479468936ad41d2a7eafaaa05fac6c3f8e41bce13955d32d43b7e0fa478a4e32bdc8573d9111810fc737e81b1126352e219ce5b91b03e3211f2d7cdcafc15965905427bdb49e42a5f71eec00d74c192bc2fac12879aed36043faacc3901a027118bd13b4ec1372536b5ca916d2623e5414f5fa0aa1bb766ed31050c5513813d98e82dd0bde22599a51ca13284452cabb604ccb92cee0ee0665c0c8b64a28efa51f7306c4c8ed4f02537f4bcc8eecc3d670cd007c10816fe4229420fa5abbba567579788eb985bc728db4c7f0a82b15ce5b096d46d5f2aaccae53f45f3c77ba407fe394cb26b720d8f6b61a8d5e61e8295be1c41ce67c0624bef1b02f580fd925f5002bffd180ce4a1108", "00ac52ac5251", 3, 582941288, "4ed0e37f515e78a8084bb281e5a6fe3cf07ad142b706ccf0fad56a82f12f6255"], - ["0256d2c6035ee62983e4a76440fc9c4236c33ebb1956e2662d3ff6ca4b091593907d210fba020000000400515265ffffffff56fe1c3dc85c08614c5ed393d6f14dba7c883baa25446c16de0d314a441fc0380100000008526a63526aabababffffffff4ab4361603447a992c8279b1a04fc2bf0eeea8efaa8236835742bf0e2185cf83020000000152ffffffff017af9c60500000000076a520051525351a5f75b64", "ac5163ac", 0, 124548875, "262e7b1ece99e0a5c8cc08f57f047a3332a7d6adc45de3a786a7f74be016bfc0"], - ["471f412a04f7368ebce021a19c89f9f36eed12817cfd20a8cf593daaf46e2dcabd9838069c0100000000ffffffff11afe59e938ab1c93cc8e93cbdd5c1029b2b81611149d19551d3aaee02cf5729010000000565520063abfffffffff28067031f7c31f71006cd2f6771b14f0f9ab07514c7414c0bf6ddbbf43083660100000009630053536a63ac53651905f68bef104cc28f7e5fbedb18a9f9931a1e3211a820c95193f4fb9d8341099d342dc70100000003ab5200ffffffff04e4c80705000000000865656aac6aab650079bbbd03000000000551ab0053510474210200000000026363c5ca690000000000045100636386a87cf900", "516a", 1, -1234865985, "972925a4e858bf237b225c30f94805ab76c437e5cc4e5808fc505756d816075f"], - ["2b26c277034dae93473cebd38ea1f5ed085c1265c61f1fc61882926311cedba8dbe56211220000000002ac00ffffffffc873a1081b98749d2b8a2cc874cad76a1281b8a3b28ee718c135a2b9c35a4b7b01000000036a6300ffffffff69525d5f8fa7817ad5b70eb847a1a12a9f1e5e1dea492fa8d5857d63aca0b39e0200000008535365526500abab322d659d04c85b2a040000000000c245c100000000000165df9b47050000000009ac6563ab525352ac0082d6c802000000000765ab65516aabab00000000020000000000000000d3a48103000000009137d52223eb9618dfd21efb5758ef35fe95b0f3e68f3dbdefd8c5f811cacae023c277ebc8e40b566b93d1a665bc8ed309b2215880a8211af34ea7fbff6775799d0d51b7416c021e49b98a76891423a0d6ddf44566a0f90a95926909f8ba1ae7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002bdd4d10c3cf8bd0464223935e6eaa72cc4265d90b19ca04d0f6f9199e2a4ba34b6052ccdb08ec1bd294fc5b0f4294f056f111c2a6f1830ea5deb3c6fd8c30b38685ca779cc16dbdfd4231c2dc70aecba9a6412be1038631a37e1cfb44bde160342e7fdf100cf93164f05fcf0d9e4708a388dc8b63008248448f664bbced73f7023013d2d3e5a152f034c236933146c7e8eea5835aca43dcf8285bf45f15e2cb6e0222c13c77bcdbd12b35b028a2fbd48ff850603c7ee917234ec80a5f15e6ebcc420a05cfaa3d9a76e6b6fc99694658483be11aa64d34c2b914fc15f533635f6ac77e27baedf8c52a49f4f2cdbab3d7d6706215e77b7995d03416b21c51eb3e56f4c903190aff636953d7a6dc286fc53dd8c47aa59a447eb6ff06429526356234e936ab0314e1ea0d8b4f9a6e304f478711ff2a8591258bcac14acc9eff24d9740fc4aa8b032929c97b7944f97f361b87982cfab1b4c7d17a3d246aeaf183e58bca2f782fe203238921e4cebdadb4015f59772ece53fdd33184907d663f644a6c3eded4cd1b1c02063a837bb1326037d8e0f2cfb2e07fdd0a94343dcec72bfdb3e31c2c7ac6f6d0ec091ea73a8221c8228c4c06c5673819ef3e33d648ef0a3bb4568b9debd9841dd38b772cb59b8ed3c3475f1ce392e9092ec8469033e326abff655f9d4cea5f98460e4efe1eb29d8b9e593f4222a6d4887c5ea55ff0335af6e9e6f40514a268a8ae1bc88fba3c3a2592692d7b4af427ad2c1d5ab32689b1485a66751711bdb045b84eb09e46321b88ed662ac9996d6cfce9c56418c52ec8830fbb5a03deb087535b6f84d0ef82b936398d3c96a0487870d7bf451f7a261e29d3bbbc8ae624475b5335ac5dafacd132adc3d7778d6a865c33cf154cf14d0925fd97c18584be79e31ce284d6168f45fa083c68dfc49b731d324a22e86e29e97b5a0c4d76e14f08772f94d228f2565f5576c111e8b0a3f076bd2eaa3277e63b28b9885f8558bfa251b6b3a99831c6fc35be9e249ac84a08a277a21250e2c9ec59211f0a59b6cf76f68180e0ba03b08e48988b1e3b54885550ce478ab325d231d7505700a23a24f356b74319afbc1ad24a19dcdf89adcded1c540cb622fffc1c5e2121f5c1f6ca34a7c9ede9418b78bdf837281be6a9862c2043ac76430f98374b23251684c551953078db475014a11fb3211b507104beaf0970db1f0e480d32b9b295a61bc1368644cf054f8c3a88f5beed17cffce4229e2136c6debbe79030e29cd4c0ce53a5bf9a85498659b66e2e75db386287c24b1e85cc91f890a00a9fd78d3ad8781c71a8b01201cc1dde3791514d7a29a12bede3bf4968d41c30bf93b3a9afa095d4e70afe19501a14bc13f1b53c58c991976cbe5255705e974d4df367b2f869bd41f2ca0fc7393a8c918dc57321c6d1acab8c06809ba3252a99bb2f653a58e2bba7752ae41d12ffd7f04fe0f0565e0e41c4cbb9c5ac1fce0be2808beebbe52a2f5a4cb4142e8d1ea48f17c4b62881dfd1972feb93e21aea362f00b839c183ce86c3276cd85e8ec1c54fb6f3471ef79ac6241ae875282ff872585330e35ef504034f60ce84abd1170c6eba9e33a811aec5789ee564ff86ac958aeace6c01600ea57d35d95cfb2bd84342fd0472b6d001dee47cf5910ef2c61b82cb59cab5db7d0eef8f0a4a6fb88fdc63b1d1e8d52d3742aa9e68837029e2d727dd49da035ef88d276fec86c6d331650c85fe10cff3c9d01302d6f6e89f3e050bf1f1d87f6500a3d21abe9449072b51ef814a9d2fd110f0cac538bb55f2ac2fba00029b0f9678d04ba7d7658f3824669b7ff54f2ea98900276a604cf60343e786cc9390a06b2088522effd5e743ac6c16b15b33b5bc0b0ac3c17e3436acb7984fea6b82af2829ede04b74e0b3a1680f0957603229fb7b0ff09d5de03d8cead6d18b96a40aaafec65d62bd1d9222ccde1e4b0349045e10190ad0b350c89cfc4e463971adf9326638f1bb7f0c207d4f873ac3a92db58d31dd29eb24162d1c27b3f13a908e3b4487290fcd4f2f9662dafe6355ec2e8d8d242156c62199ec65787c6d94f4aba9332b1c90522db67ec68e35b193d8e01aeda90abddabe9cac765e47ccbe6a205a767e7023851565fa4da45c9652098715d17a4211765948d00c92c3c14135de645354c5bce9f43f55c7a336eb724726959cb73eedd085d70a28cee19bbac87cbcb5e563af0f8dfafc30c0083d75fec9f45cc47ee3fd64999ec6136dfe5935dc8907fe5ea271c2eefd84a27a1d499596243d682be2ac607f9f2300000000000000008c4b2e0000000000983fa7fef86a3b01f612d7d17f30fb78b910d86bd2b3e81a8d95354b689d4b87ec5f25c7ed6336921836adcf92053509d015c44665af7eea4399464d5f7302705d3b5237316df2c717681eb63276b31f9ea941e62a542661764ca2e0a895b51f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004de5401343433e0f4395bf0a792077cddd495e78957d5340f8ffbc9bd8f80c929eedb509521edc69c080f336faede9aa03e6e816dad9d387f70d2de28f07bef882ca59ff639b9355596204d709cd9c38edfc2d61292cba3740af91e8112ee0f87b8f2e52a107fc7d62c27b3e5701bad70ce99f1e425a98926c1f908098e546be0229e2ca399fdbb2fb53e4082b85a5bbb13e1f5e6c9185fd530d4e7d42d941f3ed030944aa06fcec643fe660c7de5669e5dc14ec8ac8f81f75006dc703754345b09d0b203049589c78b1ce3673a44a611723d3c0bc87a2c21bbd0c01276c457743b7f616af9cf8e17d4f2bec0ef71eab373ccb015efa23fe2e2bc304b3c09161f06ffe021533dde12fda2934bbef35431617b78a8d096531ffd1d6dc825e0bd9c312208c0307c9d0cd7c3a427225946876285e2435ca55936ab244d0fa820402db2212067b021ab8fc644ea41adfbb8a4fa138355b74fdbe060d921a64f738657c3c5c2dd836022ef2af93511688cd8f848be9838294e76abc7514bf056681b6b19b4a6bc81d6f022199f876314e5ad8712b6eec29603344f1b2cf5c158274d0c36bc1851835466839887675c92e21cd3daeb78e902298233a827e8197b32a845c3af7f7bbdad50203c90df34c9becb8166e14ff76dc6843dbaca4e77a0dee4f1598f02da3ce63f0ef72869a8cc348b4848f8ded91131eff3949e2eb1eef94a0ea74664687b2a2b088cf77f9d2c62fdea5aa41a13379a702ea1afd79d4e987e104f51fe82802931837d57b18a754d822381bee5c1050e578733120783a180072b05f070b02fce4f5a0889d862a9d132c26a76c64631fb3ad8ba8956e844e4e6fe6c53e114d0de272dc6a5ea5400fb1e8bc4b410b0de7f1757a442e59644d2149a34d8ebcd32f13684c1af261169dde8aadd6d8e897182a40352f9e66111cfaa2a0821c5327bc588a9831804c82c796221b426918181fde162680151581532367dfbb5318e16d9af81c2606c0ff678285414bbdd069a9145988b405b3b898ad1491b8d8a6501df16b678c7de54ef984ef266ec6195b5533bbe3a23ffdaf76d302f050d119686c10d46fe6dab67a51a5f98d98ce06c156c8ebe87d671a9abe2e4d135dd4ea6e9c007bbae4318e1b7c9772e6fce2e585a4c374b4410b5fa8af69d2ae6842dae95aacfcb7b30dcd9535b53f94938619c4d50b377764c87505cd6031912df1dca66fa892358687a2f034acd83c1d545fa6b7ef41b2b60f5b64971e19edef278ea5a5a8cc1149f637a13ac7aa710dd1cb15cc0283648380708444d8b1c6a6fd12fb0386f8c4ece7e681748289189b617fcae279136f7073a47a3f6d163e203ce4c2c8ac058c6dfcde3c508d96f586a1a0214da7f10e947fd085913ce46aad953ddcb7a2ff445599f71b90aa3152939a64ee99249a5a65d19ec530ad8e607af6ffa452988277af7b0bab822eade9606f29cebf72e0fdb2886866210157095fa45dec7d1a179052a862b9aff8ee1dc2c5a0eabcdf7c43593ac46939cbe8de90782d1f55f61753ef9290692baabb0f1dd2cbb8a3e91e3083c942ae3278249c697195b6b544cb9e09699bfc72d7b336b37938c3310eee4134f265b6e2154dd861917f9d3082b447fdc433a795524858b427685948a5a96196995b078ddfbe7961773393fcfb9864cbf851affc35efefed7a714e2bf1d2348e5d5117171e42ad151158e767c57cff9a36553ddb8908c4cf66c899a657db7b491c9eb09cea3846a51946508ae78e722755f335a7152be98b02837b41da186f0277da19e5ab4e0f714362b4125a8bb911249af4b9c3fc7348764da80c236be08239eb9ac34c8396748c545450f4d998d5fc0c3ce5dea4755d82b2d2ca72959a3190f1ff1adcf69b07dc42ba00439837cd817e9ea5009c1e897d222034773460d3f109d1a11a9ed16127c5b207551d143c4ae2bd1876f58080ecf502e59279a7bb41db9a26f05cba15ef44e616971a712cbafcaa595475bce899e7386841ad47e7e31b46c06ca6ef04193fdd96a48067f8ab87cfd04ca64c191e6c059f362e2fe8fadda43f464b02456a69e695899a02bc30886423895db9276602d95204717d0c0757f6c1fb50cbb10f35b2a92d3773cf30112568cc0f31fe394dd394f52c2f0d354e3d4faa2be0e1f6664c165e7a767a35d621902c09b108403c25ba381d159fd8815eb5d6aabf573f79e45e34139b1b6ee3dd070bc08747f81dc6d51dc5a9061fb6f602a2ddc19940330ba582a8a837a96ad1c64362b549eb35f9993cae579c1ea50f11cb3240330da6df383adf548bc9c75b1cc3bd4b2d288ce95d278d15f7b99cc8ac090154963bbc3093e9a7e6598ea597a7309cf0188db74bd41f33cdff09165a12b17ebcb59979f992895d7b46d5acd014aed1df5502e9c442654eee07", "6a6a51ac5200", 0, -267341217, "d2033ca30e40bd2cb89ea9195c732a5d065ccceeaf266467f78565f66a82b95c"], - ["eba18af004bf8cfcef337a5b833472f7744773ba90555e97b42e4b8bfdf6e1e02d0c26ebf3000000000752ab5300acacabbf69caeb0777cf70b40d31163fc265d338b388e04294714076c6e2582a3287eed224b4be0000000003526300ad36472653341effaf59fe698a70180e9ff1f24f468cfa2455dde2f8b3cc17ba47a1ee470200000003516352ffffffffc05bfe2a3da09c398f7487b0513b9f45efe969052f962314f422a4046cc186dc0000000004ab00ab00ffffffff0164f2af05000000000800006a63006a5163958a358b", "006a000065acac", 0, -2090116042, "5932ed1e27740a1810c1dd0cd1f235ff8bcf64d2579e40cf502e594d386e4ad3"], - ["fc4d72240382b4e8eb87f70aa50773203c5643f6993837fb8634119afbb7e96553d9d9c4020100000000ffffffffb6467961659e63b8441e96cd5eec4c5ef8bbb442826d3a419650dc055ce468cf0100000008ac00520051ab5352ffffffff65a1a0b0bcc1f353499143455259ba6007a289eec9160e84f6fe6848286631ca010000000565536565ab9d1d5f0401beecc3000000000000b88f6419012a583c020000000000000000000000006b887428d35b9f57beaa10a3cad2e233c57ec29e44ca4531b271ff4f52b2d9fdf066663253eef81ffac867472775291f235d913677731090e40cef467365a6c90c59f39e1a13e15257b477098568a2d57cdc08191f2122975a4cd4fe3d3efc7200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f901f1685cce76ec9650e94e8983e29eb0195a5eb5ed56c8cb8d314cfa309a6e564cac880a0a312d46ff7beea6c916eb77a927a22ecee1e03bb029a7ee2cd3e3c9d873410007dc76788fd27826e87660ed8989f5a3d0bce7150a4b209435a43dae7d7245f71a83b3661b917e211fd4735dfda69bf29241c048bf90e823c6cf7202103edc6ba683be3d97fae0de5430a5f8e87c4879337978a82c289e8f40d0bf1f0318b80d4039530ad723003b0527496c9f3c4322814934384ad27015d45151f9af0b240ada9efb3d0a35ec117899020ee65ab1b59d2295deeb7c563bf98f965d61d02c696d6ecb078a57bb130ff92724b954c924b0194f6ff36a07163b64ba42cb6c0308e17a4da1a157e78d82d135dc9c74e701741e56dea8ea6ad80ae6f110fff9ac0328616b2f31829bc7e506cfe3556ce2d7e549ccf9495f7418a4ded8cb35a11b8e0222204ca9d155f3c78db8e4ef6247ede93bb92925796f5a2b325f9e55314b06d50224bed1563db2c1552e6910e37bebc5ff30cdb249b09e085b1041ac51941b5be003202d7a22165fbdab3fa7ac27bffb5a3844a44e552b5f8c3330d7646d6c91b6603a2d717922bdb52244f1abd956928cf98ff8cf19a7e32c75afe90cbd028159cdbd0ecd06184adbffea2777da3b5fcbce80f7d7445afc9f91e2b0c1a9b6390436d7d7d5d4dcfc18b207fe0d9faf38155364af122c015bd32fcab3b7ce2451ece32531a8e792a9e8cd55a317c38e737b7b8bc6d942c2c7bcea43fac046362dc11ea9539efc2068ab4c561eb14525e456f0b4f63275d217ee3d0553485f00781ad7578a668e3bf93e586b5e7c53919d27c15cdb705bdfcdf2a6115d651c78576e78a84648a298c91796d57aefa36e9f97106ef532415201d70b49620c8ea4d9660837b7820fb3cd4184ae37303343f528ad846457adb486925301a4e0783146831ce860116f2707a9047f54c2adca7b2b5886cec503d9213bdc637b99d6b88f1f3c2926295c4ecc00a2098e8932f9c08930539fcfeaa86ced926935267c599556cb44fc44f8320832545f1e5cf9065dc7c5d6785fb650e38a1e49b8a25654a9e538cab841eb861ee6e1c26b5fb181d00812b6c83de5d48c110bdb646c9ea3025cfa45f4e1b6d4ece4273887dffa460378b2c606534a3340c37c62b738fcab1f0d3c9c694033fff0fe34eae94375ccbcc617cb2dea2d4845201637d352416fd0e2c76f8a5855a1da9e4598803f3a890428b94347dcb6876bca976e4050e083681306713cf62780f691c44b6291f98c21867e19d70ab2a2c55b40a1e882be0cda1d767b6938a70354a2b55f01e6ccba19efed93f049d865804f91cf87f8ad708f7a9525340d1dd549fd207a459be175a4b88951f75c84e225a8587a8048171ae6697f7f6eab71a4331dea250a04c94091a73e68e58f2d2cbc17600f2df3d488854f0bd1bb52022c62412c1022b4916f68559e8ce4b6a954087edb85c346702c4c704c10bdb8e3284baba107c095f069c3378791dcd5ac25a051771468004e349ed0740c3d83ce5269dc3e41a0445565eb69ab127e8f74fb6de22e73457f5bd5650f11db82ee0b4406d36376767bb57c5691544d1421dbab22027779db272bd9e29aaaa571c14599f0a5c01aeed2265434a9826e14adb8785e7bfd352af93953c6238cc092eb48ae2dd9b78f55a4e98858a84bb7175dcb3d380513067b8e95fa768bfda4692349baa8bf9572c3056ce3d702bec634162f377cab605ec1bb9a540478b623e486c229116d3d83afd483878671af63d1b19cf74ef864350e61bf151825d47f14cc9e0247d51e4e8203934ce33f3d3614b2db40fa349983a18a3a77121415c241f743b068f0ba36a14f1d577fb3d49572b1efc13d920587a8c7159169e895e50503509c478c83dc49d2d39dd5bddcf5ac04672fb76db6708af0751aa3f605ab775293a730ef3cffdd7c1cdd7be99cb09e657474542544902f515eb6e2b4e38b329f38ce602d7c083b12347694e6f4c9bebe3014b3281f68528a5c3cf139d9d3c91d883c600a0d56bd3b9c3cea32d1802364e3a5f1b8f17b1028d673caab6a3c6562a57e39845432ee79d45b81ab764cc436eaaae773cd44acb61a604f0d77cdf44e3ae44cc5c1f85d619ea99fdf0a4e92a80daf472cb6aacb0589c690d734f1585b934438e96e69ef675336abc4c1da01676441e307237355c331361f93b7d1b5dc57ff79def4817ecd27e2e803d017fa97f431492a55a60136f8ea88ea0628e5eafec19fda43abb8c7cf166088027d8673c618f4fd7d6a8accaa72fb5155dc9204f44a8768b504d4717533370ee79015f13b2c0d6b9bc21c5144520208c6da4aef1c90895048da5169a8c06b4894eb49bbbd4dba5c2084f751416f2d92630d555e2d17ef15a6f1d7c9cf0fa5c759c100", "52", 0, -1488314322, "63f5ac3840448cbd4a66fb2be6e822a7db7f18cf13b7dd3d96114e687c3790e3"], - ["372b960002945027e2e089396934348f923890b4ea27e086fc9072df4bc2e82f2b327ffe75020000000400536565ffffffff73aed46dbf129934ea2332df8c4215c754060ca5baafd83a295f3f396fa3156303000000010018c008820294d17b030000000004ab526a65f4a1fb040000000008516a655152005251e1a2e7cd02000000000000000085e2990200000000e8f046329edddc72c97f3d1cb7aa717ccb79731fbafedc182c62daf227cf32f71ef691a0d7fa6367123df1df8dbb3acc23f3be25a62f747adaf70c6505363f97df0acd2927471abe78a75053034231166d5cf6e6db98022446b0ee74deade55c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e7005cc04ea334d37d2b90bbe536dd35e3c7776ee5605c9d04e4b5f7ee606cb2281a5c02d5efd19cdec31b0c6e1f997df1880adce06ff99ed743efd4f2eaf1042179b009fc7b7aa00d44c59f995a0783535624f216cb5211acdc9e7381aecc34bb7aeed9aabfe26417d6cd194866a7f212504ce07c4d99eedf370eee18dc3a89030401ed1bfcfc32bd0c57cb271f81385a7dd85725d9a7fee7e64177df098b3cac0304cd93b58c085f36cca709dd8e0d139ac29640ffa72aaab2791c063f7c73ec6d0b2fe27e352df4b584dcb4764ab2464e84a9c25cbd6b06dc1b868ec9d723f183bf1d96b972ae79c943d27a15caf399fb286769fcdd73d0ab2431a19ff46d89f3310329adc4e42216895976b341375f9e2f77b7ab7f6d235a5e1c53354aff4f1e7a51030d1220fb573252d9db2fc95e2e672f70c34c2cc7644029d90433a15ef47df74e030764415b82de00258c9b30a3a9a294503cf365c9b4a9e1b26a50649c4be6547702125b3e3ce89bd004b507c041556e8ff7e4cc28502483adf79ff32bab360aefc4021d5998c80412a1bb0eae381aa209dbdb6ea1e4da1a52cefd13d9328a24868a4642077e14e0b9148d54d5425003fee1739e15027c9613d13d1d9fdb4592a7685603fc573c5ee8d78a44f20c3dd8ca6502737e83c1c491124a60c1bf01467734e387119df6a2e642a8c473d96a8eb386681551b350d7a3d088206befe2bddbaf04948c13e53b453b1a931768a46120d002f568eee2f9e847016d9ed776fbc4914db7e2875eb6ac8c5f05b505c38939b0389233ecbf12fde1f489f67f1f790bfa7c68b9e347ea0311612917d6c27cf8bc5989d2e47f55ba2f28f508c3f7b220e6c2b97d99e49c990780ce37b47ec991759f17e14fde543e0df04dba16afe54db0c2a6bf8b2a911f6aa2010635356de93933a9afbfd7d0d02d1b3beb431e3be893f669e7f9f12cd5e141e1c3f434903cf10fff09226bad66c3dbe13d17238cdc69a43c53559546de2d168f099c9ca84df21ec0daab96dc22c9a7de792f888640d1c8083a7581acc46c5358e188e203f580029b52ee3bd954db4a3f03c5434db1e8a24f18f393a01ed48efd5819c2d37e51e34292c0c2126bedd3b9b18b9008f517ac6d57cb23c4bfa2a8e7ef81b04de94ef6d161fa2a29e7df0ed74d8ca34b44f0a6dcef58ed30fdb223826551f056aa8d4565536c5a58070067dca102d0c11c5bcfe3db4b9140e0eba87de2cee18816ee2443490e518b90351ffa9ad3516217f3db675f253ef988968410ac81878cdd01f206dd9d76c5a42cf800b7e7ba920fbde2dd7dde4628b3e4000873ae132714dad125546d1d8c4fd64fda02ec9a5fdee87bf5a5c785978f38cb1a3ab34819ba29cdd5a4dc3ec5ed0f3bcd6d88b7fdf0eb3e7a565c1792c72348866abfbd1cbe460f055077e35a313aaf06505da5dee699680190a41dc64802b2e366e5eae8bd71149bd46d010445d234e8b16495caacad0dd90631daf9af5aae20f61fc67ad58ce4c2e32970c790755600dfa48679fea299dd4b38ec7ddac5adcdebaefc83fa0768ea695991a5e0ae786f752b307fb4468c231b08d58fc23c16f2032bb683c94251b6645e13482bb291d65e8252ae72c0d2bb2aff32156d67a04c1e6c14deb1b75a3b81fc15d67ae5e598b90dc69da4bd247adec8dbe39010de775c1fd887595bb2fcee3a6cdf74e4d31923ab865a35d79b378cc30a047e8457b108e39b189b25a65ad630f6c1199e84aa707b270d34ed10904fd5b014c70ab5733af09c0e49003f6a6fce81e70eb07ca7f536299ca0034a7448ce1d922b9531fcb2678a2ec74a7d4c287d5011016162a6fff2be04615a5f0ca6c03b0448247b96ef4bb0486baca9cbeb7c3d03342b4aaf94991db77cba2f53fa10622684e3a7ad697bb30709dcd147746e5e40a8f930f3415786886bc70ec89e1473701ece4dc8f65038215058646a68db613b22cce5480c96644fb46ac3a1a73e8992020a387bf397b848bbe6614101b44fbf46c3eb501363d2ee816c979ade40620f70720e3807b9ef2a22e296e121138950f00f3ab918cf5c775e9973ce5120c49ea74db15b4c4fa2743c41ac69a703f1c78a958b2a1bc6bfb704c0a94d2e79b7d10557e03ed847869cee9b4ea10b4400dd3f37b0a6ec07642fb7bb69dd041ab2aa5460dee54772c193f25fe4a493ab4ef7b0570be2f3563b926a120ef17417e415e11985aa693bb78f15e5a997e4b0deee6d34f209add737660a4ee3af277cc8ae6364c6977d785648b7c3819ccce01f4600000000000000000000000000da50d9bb6a6441e3ad5a83db49f29b487ff32e3f72179651793257b1f0df2f0cbf285d18dce0bb70d0fc634f5af99f4370ee834e36eae10216a0fff7f0d98afaecc34e2a30cd566328fd3889bf19d0dd66bb17fca14b600b6cca54e8f77e84430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000069e09b3d3383bb896acaef245ef4481b8cedf3c31f6c2245687af062846433bf788e3eb23f55fe9b1215e1c933a0583a2f2bb178542b5fcecd0a7f30266eadc64e54f643a107e3450c58642164acbe6fe693f1d36e214c9f3a8f7b19305ae7647e138e11f84889f0234bac9af7e9eaad0f20ae7913c5aff9672fdfb329982501031057e9ecc1808efb4a9993f322cdf093631c5230ce53b85072c65e97a1fb2ba70203454731381b05ed3e0dd13d4c01ed32abca86c83d4640500d2280da6238cb170a2d96c6d7fb35250bbacc20edf287ed61797358a2b5721186f6c6ad90ae7671be1cd412dffa25f857ee3f28fd2159e6a149de47f251ff2533e90a523ac2b2e63a020a30e7527bd5c3cc10b192c4aa0fb83f958547fc2c9dfd67c5b5f2734ad91d8c0202b88ce0fb768c3c15010445335fd2cccb725743af5e66efed6f77487433e6b502116c69c649579f77bfcce709a673288567bd058c6d140a03d2983ea3dfac89b9032a8baf4564ae856e6965a6c79a95fc8db27f30152eb3e8f3f1eba4a1a6517c40021c53c6e8fa76bbfd5f19f0fcdd1e1276e5125afbb232d3bc4452bf0a905690b214691056b91d7c9d99ef93d9e4a1cec109a505c9d58763fd57e76883b54a299873634245050f86c02006478ff92496ae68f21639b5ae842422159edaf2db7c8890b8d67c03316b5a9f87082c02ef988f8d508e0b104a334f8ab3f211e6f0346c717ac163bda0b2e29c13dd5cc6e7c139718850746f30613a17665a947a76b42bbb34c7bbc003f4d4d95e1d0089bca9033b7df0e3189a79fca3f3cbba5526a6ff73a7ba29818b05fb1ff26fa9c844f54febc0e5930726d8a9fc8410ee40869ba0f202414973932988fb007e4bb6b4ddb3a888f72cdee3e1354c0ff1d9e5602070ded6a71b6e78e72125c22b26b16c1b0e505a77b2c4c867465fb25254e8aaecfbe59956566c0cc695b016cede6f8b35a0ff85a127f5b595f778fe0471060a1bc78bc2b1a11bbd32d21a758f6c1e93397f4a584e71f569efd5c0cb8bff1ef334a53acc3ceb37da82a206dc144692fa79cb54fe45d347131ac6c5e0257aa1ba90a5b85a6f460a1b39ec872b9da66d077095b8f72dc83f4b2dbad4e55a296917ae910e41fec2bfe4f623110aff2af8d510b1c45a900e6e209cf577a5e9a39d6e998ee6d7e9453ca04de7d5589189bcc1cd0364f1803bb7650682fdd35eb59568682a24e09d534a9ea4b79614bd7a78372ce6f152d7780a2a20fa2736a121c44ca61ca6446bcbd2b246c4ca8e773304c0b77734bb74e90da62dca42fda7ddb20d326a31caa5ac52a9ef4806b42e5a52bbea41fd405a5b83d99c32f6d1788bbb37d69567a12b5c9431150755babf35182b897c6c7063e17b8c4064c7c102c5d11c42d36f79d29e20f48240e8d3f9626e01aecd76f71166854b1e163dca591f02b012a42324865a5cfd6b9cb2461d670a46cf2a829c59a9a60c9f3248e18057a52da38b35cfeb169e2c9573d1c6011e8aa394322b253ab88cef5d1ace2ba7407ece52534813dc49fdf7e1e380f079f494f666275d503c3f4b907e3fe986607fedaaa63e83fc1d52f8cbfa932e1d5f95bb3ef63dfd834f1fa5a16dd4e8b68fc5927522385eee26dd0b546a4eecd3f8083e28a3405712e30b86933fa87dcfb7a90ae8a9be285e1acb53dcd2b214bfd6ea741b461fa473ac7bc199a0acc331aa02487e67f0802b1957aa0b4103b7354d23df199fb64b0ec1e4a6e384f6c0284e47cb5fd86edd4f2904a8411a07a7e957209d80fe9d27510039c4da15790f53a0b83ef70613f575afc53701d708e3689cb6ba4992e30189c837ccfd33de44d0a42fb1dbdc94ddd553857bb14ad077553c9d08b4cd303f7b181d41e4c8cc1afa99f7298eeb1fcd40fa4e31443e883bf51b9fc24248e37a36a21469d64299463164aeb7333025646a287bae8139ffb38976b1d2c592421c19cc7d5ba97de8c22d2de902d9a8e279ee013e1addb877a28d8cf02db24cacec82f805a4897b77d70295136b03e712edae441f68a29da3f6c9db2f6d569bb8940c7f4f044c699f6650c733777f706c690f4bb6408692ad0509949c457a5f397a7ec374b997d3f42d779f839ca634dd82191ab4f7abf3f9110db87619090eab285e82af1b843ebad66bd68a882ccdccf4426be0335a1654ba58972cd4df85034a2289bf46ea9b79ba6f86abf46d6667e59052ae55db52cfbd96e66bf1afc189df588a53325b33c34b9069144c2fbe9230e68b356b363f0052355a8646707bb95ccbb47de96978f80a49a43849db3381f66d4e8d1be49a1c356e8fb842206e5ec5ec204a83d57cc4d7dcc1b4313c48f3a940c05fc20c3ca5f5ecd5f7747771905b4574d0299d72aaf6b696d1dcf581a4b940f957bd3f01bb2eddee00833c75fea909", "53ac526a65", 0, 240217027, "e628cc1a2e8af55a8f84b8d6fd9cf7377fc071401b342e32841a0f9f2a9c6862"], - ["eacdfa310328cc19d2837431469ddd858f0aad9a05db01815e6ddbcb4689eda8bba9926a5c0100000008006500acac6300acfffffffffbaf50bd9d1150b80ab7628a9e5d404b9506677bca9701d29f886c26d84bfbb2020000000152ffffffff3290b89a59fabcddd03bc83ec49e61efdb51c3f9366ca530f78397a4f29032750200000004ab516500ffffffff01f9fecf0400000000055365ab525311035cf300", "ac530000535100", 0, -388087160, "cb3af6e0c9b4c5bad39e608da939196f5902710f9b75e8cad500b07fedc7c024"], - ["1b8ec97b04edb8a0683d0378519d4562df6e8fb803a5e8c001a24864825b59d9b754a3dc2a01000000026a6affffffffab66c4b59ddb0c5ad9bdafc5acba38f2c74760c020b99b1846a3dc5b9a8c58b90200000008ac51535100ac0000ffffffffa874f87fe21dd2ae81f42f09c32f8cb0cb4c32e7b41326813356919b66fe282b00000000046a51ab65ffffffff29c38af16dbebd4dc48ca087614e28306be0a0b4a522eced4e10be5f57231189020000000800535265ab51ac51ffffffff014db8ad0200000000016300000000024fd448040000000000000000000000008d2ac5312d2f1e03e8d1999df1f028e1e0e86b5c89903e3cddda4d347834b6f4f6d3df3982bbacd1fd9aac9dc34f00e7251c2fd0f206c4bf52fc65a3d56ad60aba0f89e8d2accf05276bbb6c9c28ce665db4d4ff456c87b55185c54f50e8e2b400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cebf1bb3c04a91ecf43544ea54e0cb8116a8ba2620e2f600bb6449c31571d10c396bdd9c493539e9f4c45db1c5df74d3b27fe4c8b67d5bd17598cfe6a35e7d93ceecfa887de1890ca34fe8bdb542b6a84571a45912deb7e430600b956113841b6d5ebc1cd44eed86df20507902f1d6430e26ce0ad8ac724ed68cba25ff0c356f022f63e8efb8cae3bf3393fb95c26e872341d04da0a8eabab94b22ec25bb62608902025ae4bf0d96f99796ed0e924e846b6cc0a1c158d5234f43cc8f6eed6290e93c0a1b31f32f3c319ea8ecb599285010523355966669994d6f29c5275103167a8b292bd3fb20c40d97d093ce04472be01b1205b93d42b36a9dc78c9af5179aeade9c0214ec8c8eb9a14c395c04c6de8d901ae7080e4d3600b28cedc5e6d4a3b2abb931030da6849d63a4781fe6324f400ac496445326eee46e4505fc69466c6a88d4d450030ed4882f557ffc7bcec5fc47af7343222df10d722ecd9ce6026b3c936145e749030d340257db18176a87a93d2ee462f474a34d39dc20a1e23c6d9912323c33fc14022cf63905a4304fbe489dfc5f759cda91b4e7744fb5811f03dc4c11f9796dfd1ce3d5bb4bc6eb3b879c1caf7626780a2fe85756aff1071cb9a1d1458a8b82cae9709a5c8c43a550dffda73b2222aa11c2682ea7c5c98785fb04157e161c5b83f08039adc2b5bef62f4d3ee68929e616b22a582933c276a48baea6e1ce053142e45e6c8007012d3ce36be2c2c70ca1c0f2fd772ac421a79cd28c937a0773669fbe3b5d902419ff68b56901dad5f908f272f56d096593a67fe7bc9f0ff68798e100e2c3c6732e96f27c730aff0d6ca9da6e3d1c98f7b1b2865d0ae653369a6e7440dbfe571e73aeadd2a22fd6dbb297de83021abf8a113c261f8873275853275f94e5442a04809474e44c708e976fdae6e9d949e43fc538523360eed0d032ed1d6d299163c62fbfa122c5838343dbdc8a36344dfe45dad113694fc9733fb71c743833a7052f70f2a8ff861694a264bb14b1f15a9b79cacc03ac3c517a9495cbb00fbfd17ce25f36aca1c87dc65799e3ec3fc72cec85eeff060d6d3eeb512514c39a5b9b71975d500bfe17d38ad49f647008dfda3e7c751857e85225282f8cea98308ccc45a6ddc8da9d368863c030904c3a6d5b0beb0c6de49e8fbdbeb81c151fa324eda781d4ff5eca7fc1bfd7bb0a8ca2b4628afc76a67ec5806f8a7afd45f9447d2993802cac9dab0cfa99a16c0a5637d4a49c03fb84c7a81006f4d60147bc32253a6a0524ff176c772fd2ed9e0110e9c409c1de854f134e32869517315ca38f0349e5367f3e5856ad6765b5a0dce300c1dbb3cf1c756aed64d0ad1d134593b6e4120c07871fcf15a58f6fdf5e0210cc63e05f8640de77678171fdd87e88c9b921c4b6ee2505d682e3813ee48a6bc0029365a9d1756c405a90c2cfdd42fc9ad48742cc0a43196977bd4cd900dff7a2f3b2e289cdc4fd77b7e36760dd54a0a4ff22dc8f16990171465977dcef8af925cb2626044b8a1889bf1f06242f752402312c80bcc61c8e3637e9bd301449d491a22ceaf83a4e96b060dde6b78f361ace3393a5d9e8db7617d435403da768c0742d6bfab85d1efd3afa3ee7407f5bc20a7cd77d71ac420b9dd08c308aeeb725fbc119075a9299088431431cc535c128be983ed6e7e2286f347d1283987c4802e61be54d02f2d8833a463f451144c8f4ca505f84c43d3eecf796585ba08680f60417ce143ab6eaf3c357dd89fa3d66dc4217d74cdbeb8443b7aec0cdf4669fad309ea08c91a5bba7f0bb1f0a136cfda382b27df85a33de94e29ab39c531f7567ef3d9440fe32d64c2d4d4f5b9b514d7cfc3c84fbd0d99d0e6a3e9d4813fee24bdb86dd93203223a673d294a1a78110347789f72ff2239495c3860f8dd596f13530d07357d11058a5b7afc22ba9211017e207e5707cd93c209119ed6f95bd733104bb905f10580897e08566b4f97aeee9fd32e235792fe3bed7a3101fad5f7491f68a1addd0362ebdc5b319d463b38460c6b529c09e9d8b63b672457c1921e641bb0d9dfe8a83dabe66607f1526b182a81c8b144cd5650690f553ab3609cffd256c22388e7e3667794b406a3038dd91f43e21882f4908102fc71be42c87ebe3f3c316ab7646c240c3a9d850303a4fdb5a63f669e6d9521777b92ad30d42ef42d7e86b2c306eb943bc45265aca6b56e85060e2fb470050c34fa02eccfd1e852628a4fd0a2a51f8806a57e1613fbdb7239ae87f86434c19842914bc5676e8c998acd7b22a31e68b07020000000000000000000000001e4e75eaa68f232a43bf34fea1065a5f22ad5d2cdf0904378de2658cd0061046a2497622d7724ef6422ce1d0eb2cbdeeb7de4d5cc9bf916bf53affec92cd5c928587dee1956bd12f8b67d0a5bfeb3162db3fdb28feec1b3f183b20f20379c26b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e553c818d3e2fad4e2c4b39f7fc6e94e6ced344af37664e1c12ea8ce4dbaa9786b2e263c5f37506a46c1dc640e66454fae0478594c4fecd29b2bd5845464af0855203b6b85e1f59d1e1ec2cd2ea79e1e07634b596348333a7ef2cfb340f2e4be300539c760885d3d5d1d5e06cdea1f7ce6dcb690f3325aee5717db03a7ffa2f4022236aa1abf77b53bfc2770f2849d26ade3681debfccba8d147115232e33437660317aa7a1ea4baea378d5fa09af68a92ea0d13e41b1feac2f20b4fba68ed8a42af0b17a19b732aa9ba8cb7a0943aa5d4071deeab401423e864191b2c39edb034b94c1ac0fd6c321932b13d170c90ae4d6c621478ca8f771a8ba3f362d72d13c2739c021570f66bd9ac2c8fd3499b0c082b95e2e95a2f8b880394c893adb022aa636d44032f1f7d436369c052037e50f1e28fc499874cca2fbe88e1befa59ca9f1d13b2c6031f2cbac5500f8b436a421f5e270569734a90c31cecc6e06954514cbcc3258874031318143003735bf810dee7ef12bca2e96b1d0a5f4811a902642091400adc9cc9020de26e61ce1b6620a273e0ddb2f47e9709af5fea1f590b954b64b45dfc973c1d6ca869287deadc35588e4482ae981aa421c7dd97e48c57e12c84d9abbb541731fff8868703c1d4831b7932d53014b120ca0dc094d3d2b37f7822c27a1ccddcf9136a77c0e4b4721fae959ded2d07bb096d107826f8e7a0b037f51fd41aab8aae5cde66e1e5a25cc9f3611e833deb18196d7a289ed6953e7a582212678a5a139e7eefadee46a67ad6f15391b811be9f175531e44e2775c5a684226a44dade8f4b71e5ab79a493479d0154466ab052f38ac04763bb9386e07ad026802097cc6913844587e74e5ca4283616a4379f2e4b265c880bc13387596dfb37da8fdbb58a20887139edbc8d96ee28bcc5dbeefa95c0291275b81a381e153069d92930b5f3a089ee8263efb81d2aaa4de15898e71d7ef98f27ff5b6107a5cac7faa6c368511e1d4483f8feb9e9ca81981af4125b48d56ccae3b0e93ef8e8cfb7abd34ca32a91a56a770fcf4e04c62993407457a73e9aaf1cec49fcd8bd28786c73b17af38f685b95c765c3830fba88f2f6dad192ea5c38e76f35baa130e982ca161534e53e54608e117358bb75e8dbda97ab36af10f1d4138fc226af0e1750f18188909807e352393beb15e482d7d2358162249ad0415549e00752e02f62ec14ba2c70a20abadf55299051fe11d57d0ddd2b0be10c4154fa3743487f58d1275ff3261ecf701015160cdeb483f9e2d619734765f94a84b0e1c94aae781a5125828706dfb620c4e36d1aeea9a30908016073ac242594bf63afe9a155bc412ace91d790ac3020d8724597137a8b19ecb4b0ab9c903d5b4abffa613abaf4470e2af39bcd85129e970413d94f3d34cf548c1a1009021832a1097d67fb3821af2c9ec7b0d08c15e9226aa1e9a6227258358e9e24d7b30b4a9abf265a579e2e907983e869041655e78f74078ad23bb46d9519a1680f94c95008c0bc28cd507b5bcf2d4a7ee03bd365dafb2c211a41db475fa6bf04d44240d0915722c06c16e68239b7a30a8d227ffa00b63595aae14e3fd65f32030ae0bf41265a172242804f3edae4587e77359aeb4001394d2ae7ffff9cb20a070cad9fbbc347abbb0512f861326d1d67d990bf5313dc96b31e4688b01d079d07fd44532f3abfb7d67358378e29de78ddec2434aa637a449af881e8a6ba259f96afff6dc984b132aa180eb28ed7cd502fc1bf83d1799ce1dad5afb6acf3baac859b783cb4da30e97f74561ff978be0913464170188aee3882f696c55929add4a0f3f835d0a2a026b23f963bd07ec5fef6202e858c7376c2efb013db759a4a09048d1f5a7a5c64b0867299509bb1de8ca98c129689f923d7a28cc123a260c58cf6440a5ba4fc4d56ad4ed80a862a630491632399feb2700172ee5b47481bad36075fa1abef7a70763a72f34cc5defd8f2bba95778a6c054b0cad8d6daf636853fe5f8aa5b716217498ba2b660b55c84020a236c1dfb33320c599ee107b136e1584f53821809d362b3c307d41c5aa3b14c8188231c94f5d3d02a9e300db71a1c17efc34820a8c86cd53c9b42e24d0f4ab1e5f9878a94c7957b5fb799ae1f1d8faa3b55657fba6898b382789fcadf5dc2bd08e6e7ae47f2e36dbd77e8774da97bd1b892727a288118bba4009b7de816b708fe8a64ffd705b8cec0e089e9c0782a4bb9a087e9bfd5805e40d6387eb956b2978a3398a6a5bbf11aa43feeb66751890fd312e27ab1511b7bfaeaf2fc3aa9a9362ab058c753fe8a59a93870dd1b99ecc5b4ae8995b6497384755d4a030943a0702972c874245e443036ca512c5f5ff1bca4074b06e5a2a42f82ca486903405ef23d406dccbaac8e9ad390f08fc22e134ead6419c6d86c204", "00ac5353", 2, 1904815369, "07014e106a35cede24e90201532009d94effaf6e40494079af5df1c1a8562a61"], - ["18118b2303fea54ced8c8bbbfc43887d1caef7195c69288ddcf19c0d43af2cc973db549bdf0100000001517f5f2fbf1ae9751c89a98e7bd8026d96c867a1cb79111c73f665f7c11c57a28e8cf8dac101000000090051ab65636a6a5252ffffffff4267ec3067b077544458022b1c4db9629c20d32b369d1b81bf308a4fa3956f7f000000000763526553630053ffffffff03e3b76b000000000000909c570000000000050051acab65e1848204000000000465000000000000000200000000000000008fc41502000000003765cd32f84bf4e1c978c168bcce6a58f63f90885de19a5efbd27379059cc9062d50fe63177047f544f6eda54238ee44f0b4c9973de1c40e743d34fbdeb169e58c134f00463458516dfad656477faecbb6e7acf38fe5e75f42d8965b870bd34300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c4c452cf9b43d0a076682545b8c5c61fd49b70b803e64a88128f6ce150c85631e925949ec68f99ed1a0c21d5b62c059e7dbe7b0f67584926af19e761181d997a9b8f1e133b5bed02a033824480693d58c6999e33dcb4cd8f4225c2178d30df9a831e4fdc61a4a1be958eb1c52f0b3757374a23a968cc3ad19b69c0cb737ae3650211d92b99198a4bb2def9b73d3d204c511b2b68642f65a39488088087b97af61c031941b8f75b8f703d8f0e5528807e1b058f6ceeba3fe9b9f42b5b48f4f0370dad0a26241587fd2ec36ec831d4e814b240883a50cd2c3637273a865712a4498d50771f0e6ca9e8dbfcb20d6b9e9caf95e5f7f1a7fd0e31b50e684deb6b0e24b97475020f425a0d46787d40969a35ae6f9a530c2e7fafb440e293dea634383f90ea915f022c221c628fad7d1ffa5616a7006176a924ee203ee5bcc6d655c6dcc00661312e02157278f5408f46a6db1193eeb3c5993231188323db902345162a6ea5f597172902234c8fcd0468567b2402a18f122b5b6133d4f606177e22ba7a18398e74c05025030e6dbadda52b8ce3e81e77355c55b4da65f9020a90130cd2767e497ce4c5104a9d8b766367657829004fbc7dd344ef3116ffd9e66f9cd30c5f4facc6f0b9252a82120330a26a6c060df5a876a65a241dc0fef59d3d42b6c83bafbb73456f22a6414b289ea9853bd8938960aebdd0b3839bf27e4610dea7bc529fac977e81550f2126b47416b4fb485260a2b6bf41fd14b9035d7ab893fcf7a76d82ee7dcf3f1c9927ce81199f63e91c40c89ab1b1c51c4ba7197ff12a3de6061eaa79f89f247f5095f8224d56eb6edd9b378599629be3bdd616464cd0766579de89c47e4f22b2c48115da5720ebe75ba9282e4dfbbe57530c870507a5bd969ee9f9fd6137970e5ec78fe96078ecd3fb046460685a8b22382f5b404d65ed13b4a1db761d846ab3a2d9cc7f9ebb125f617a36120924166ae86a800e2bd84ff81b624a168c07eb83fc142b5b3a0aa4ba666d20c1ba50cf4061528729dbda7bf2b80dd5b713c7367f68902ca653f495ace37bd45a9c9809b7720c398aad699c41a5591c3e6741589b6cd6926d1c6cd25e0bdd6c38f517b70009dfdd1c49d563a20734075cbad234c7cf1816c74c40c47ae935ab6096db1fd42cf90fa61d63780efe780fda02ca27001b5e4414283fe48125c68a54e82e24fabb870b9d54aeb7a0b4007a7de15b35ca6e8338c842f818c657fb812928cb312274fe3ae83f051392ada2fd1bddcc637bcf7cf75eb0253b202f67a6271d830476f6074d9225b86617826e329140c4eecaac8e6dd608380bd2e010c3238c503283d16185da94fe4c8c10d8b75321cd7751bb3c5d133cc7e132c6783f4a05795cb9ea9b23b84ba236b6f97a89a4315f3c95337a24afd9955833658131cb9141ee8b2a8c43832a13b60e968c8d088aa938d11590b036220025d2960b7655d9d792624e289ddec33b8bed291879c3e3e7ba7fbfccab8471b977a0b3cbaef409204395ff59b3758a71ae5b30469afa9290592ef36bf9aa0131bb32bc5b0fad945cb4db6be26cc55a632a4ff45aec769c244a7c0d6804bd021086a8e5a6bacaca69ff04fb18d4a53777c45882bfc186848080f93b58e39f2e6bc36497779f01442474e4fe5678e13a516afe8b75e023b73688f4fb2a11830e7d33f69abc12b755af166e2b10e145283fb3e51e42cf1ab76cbfeb3a58bee5f85028d371900c36277a9ce65b36d9a483b3d2e8805f8d7212e350073e53f2e0f3960d0ed79eb356f82222633a17a894eadc4ebc36a3d1d2ce63c445ee485d73afc2727b135bfa1083c23565f3d27e61738e53bec97f9c0872c0f93be3086aae9b1c0c173613481ead1d33c8e92de90ff00f8adad49747331a7a979cf1fd30a5e611e0ee4d088d710ac38c8e60e2c305afaf3714e45f773eb6cb408fe511ca970e5824482a092d04387434b1f10c76ae543435296c02c55637a75ca6d25d2e56d778582eb5e127cb19513f20dbca118446d4264b00375fe2148b6b088086b389fbdc0a456c98265bd57a0c8dbf906f5a30416489a29d9673a6e43b2a9981e271662c5b1907aa24d951bcfb6a86cc4d4f5f6de2cfa3d05bfba14417e3e1eb608d2f3e6da8aa2511545d74fdc107e9cd4f0b9190a4714822526256d29c7e0dab63bbcb5298f2af4949e9fdb33f18b50235c481d1a3fde7b40dddda391d337bb7a6774d148a6065ce0612bc9f09fd91a428603161541f7409547a585ab674f9fd3403378811d3b6cf15bfdb4480a3d661bae50500000000000000000000000095cc8edf7c026b70b660f7945b47569e2bbf37edd2fe3fbc5c03280d9cc348dc0ccc366a7bec5d764ee2cb1ff330811b7d3d3bd0ff8953d689ef3e8b11494efb5335e689f90a1b4591b5b739c218aebddeedb82a9fb15af1c3e3e9bb9e6fe02d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e624ea378a29751f68cd38f7fd128b3d641768e3a7f424f2302ae38fef4858e81b0e36658d93cbcf1a6be54f1705cd83008883c77499bdf4bb576e79c92ad6c1dffdd8bf1e1895ccae2d9c68cc7b7de05705cf959ff8494ecd953cb12424a5a91271fdb099a428c577faa5a4fdc62e7c87198dae11c5060c535c32921917ff0a02018bf15b93267ce9655da6aa9dbd812045b936a6bb1cb006f391783427ce64930209c0651acfff916447961db7d220fc1029e952e7fb268814f666fe67650f59890b2e7e883f29308324ef26fa25c16362c9d14ccef9a9a6588b1c7358afc3f2f606200f0e7034795f224d9e1938d175d0f38f8e3563cbc50db677de8553ec091cc30320fd2d0794c56253ea8b7d41f95c3f1cd79f2340d3f7ae4145b1ca28c50b87e8020403855aa882150b737e4689f25df841cd53d98dc29945538f5051b406bf9005021ad98cdcddf78dc3aa18f377ece7a7119c8771c9d6726ba47297ed70a23ecb2d020c44dfa5df6a34bb65f4fb74d602eb0e33f5272e9d6eed3e34970e8f45268d7802106192a050de4348730de0c056b626762c0b13b2eba91e683a0a170cf90b63fc9eb9b34dcc3be05bdaf2aac35c59a346a8c08caa3397362bf62cbeea8223e7e3cb3251bce400df6d944c86819ad47dd21fe156ffe01d288a314449238ec18e28b4e76dfff8c92b44b599675d8c34cbff48314f9dbf9e1d315bf812250e02993aced8a12f2fd6e983800a64e9080e79cba63562952ed64c0cf59ade27ad076058af2efe19282e2a449011f1e12b861893c71bbb33dbd2d0875f32c0ec4813d865a2374b5a30ff6b5e7565b1d8c6a12e1c6274e1b8984eefa6f56792aeedcce37f4c2968fd0905cab4adf50504af4f5e9dbb33fb18d3efbb9da218e5f4a0a815185d5cab2f0d93866906feea2d690301908f5ed9e6a869d1278b6d6181af3eccd51a395cbbed754bcbc5f2813bafbae7bd5e9afd20d826ab682bbe8c0a5db9d53524915600a9710ab0a27470ac46510caae9d510586bd9b1761efde8e37c0f48793ffe56df175d2e7ab7984f29fc1651326fa4b96b00e61363ccbcc0d7cf4f45f736cc200f649ffb29781f6d811570d2237bfa47548e32afabe45d1bab65b0e2fae44fea81bb8a094953d480dbcd5d20084a0895a094076d3a848d77046de67caf24ae81fc1e6d53a00bac31a5a3b462b3e2327980e8f8bf4c89f806b02b8185cc4f2205fd9d79bad2d8cdb3828ce52ae7d11ee1e06cc17b7556c804136da4537386b96a948b3892bf60fd751ee0d630c1055d39a44482c568a20999caa9d618627c9297232dbc36cddabcbf5aa816aa08df16184aac40a2a3e13677667780d68bf1952b16ac29b70abd3f48c176f45f428625064c47f137138b9b1ed4b967b164a41a2372ad5dacdd3aab11b250121f07ac2845e2a0ebb3ddb94f3c059d5b1fc534a9d3767c057192014905f29ffdf70bff450105f88a1c95bdcd107966670559f2487880ec5b9c47b450b82527b33632411794e38ecf37e6e0fdba85e17e47aba82d6f2772e4e2816c9091d2f91c75d5fcc031b7dd99d4cce29d0466e7f3e468b30e8aa218d9f41ba6a3bf0c7b583e7b3ab37650e5f77cb66947ad4fea733f32f88621c925c887bb27cbeedde9283d543bde731a933d6f9a6aa659c98b07591a082b4274db41b4c437a15eb598c01e5a48b71512c3a79ae288bbc8351d2f65dc40430cfb354d79721c6a4e38de1a65cc0d7fa5b0c12749066604dd7ba02aed20937c5796e6ae6a651837feec5003c7bf46f52d5f1e73b8980c34907cee57b3e9c5fee3c59a3d88a2e695279ea3083bbbea64cdf5e562e48ba12ce28e7c6594bcbb2c8ca30643b5f314ab2e677718964118ccf38231b4a588d4371e6242d299dfffd9f8c043e6c94855feeaab393e9e8c3607ba764c422b5105029b5e54f6f48cd15c63cd96854c7147269a80fbba22ebd7aaee15cf5177bd01bb9f65a7284981261462228d9b1126a4bd3111b24eef7c13c29189e6a0e82021ed090046ad04b3a0752b4650fd435ed356a0aa96d1f44e79269f578678e07284aa5e2ca39c39c55f457c25ccf78a44e84b70789619d9e8bd91a35d074a38006e0feffa290b8de1659f495c72b800d0791d6f9914fc6168793488e3a557cb5e7c246d1b2d97c6c7925b170c480fd83fa6a0be3a56799221770d71cbd0cb319a9fcff6e7ebfcc08ca65f03906c21f8b2360a0acbf0c0de996044ff05e6d1496b0223d665724b9ba0303280a58f01010a17f1fbcc7c3e5190b5367f5b47c1c6be359a89e11f54d243b4df53175c8cac3a1e92f4b9c0d88725229523085e818e3f67a72162c821e25bd48d87ff6385bc2ed18bbe76dd8bdae85621ab23fec91002e48779a0a3bb3a73843222823ae158cf12fccfaf0946d1419804", "ac6352526500ac6a52", 1, 98842182, "581ad9ea6b077080c32b1d6e218c700b82af807b454cf63a45625a5c16609acf"], - ["20aa2c0003e4ce50af6b557b7ca2752b85222d751132028f9bb64d13723013033a9098fa4c0100000001abffffffff28e81e0d68abada0e6ed36ee6403c541b8704fe672b6a7c894bb567e4f25ac38030000000765ab6352ac6a63ffffffff00adfc964a6fb053ac9f89381c3bcfb5aef9124c29dcf21f45ff966d8760aeee0100000003ab00acffffffff0481dbae05000000000152a08e72000000000003ab5263cdb10202000000000152acc0f8030000000006655200abac630000000003000000000000000012e74a050000000094e6f7319ee360653a391c70aa79053852206575be9e9da21a7a4a0e824c9b5457ac443b5ca28e5fc28ed4be8a2d2f707144019ff2efcfd6ff21f6d62de4a058dff82f6777768edd404a98b8ca04d3addccb29946fa777ab0e49cdb903627aa20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021866e74d23970289dec464616b765ba5bd4ff9936d0c2e43184da8d85f31543410190313ecaedec6a4ac255c309576fb4ce8867cf42fb064a7c723057d9f565ec2e47c620b0bf85832fb8f0947507297c2f9e21593f8ca512783c72d45dbaa51227d8de85044c338307960f3ea3644a63f6b3afa15041aa1662bad6399efe27030fd22b30db0e2d39e88df0a7300f1ee556daf6016c04dc1b3612b471ebf0d5eb020caee1404d81bc52bea65ec4fd60685a58816e0841b0cf6be8257ee05cf5cc1a0a1906c9b4633e74371a7a86be16df3533cede4fbc4b38de869c9374ccbc3c09a32662cee2d9e5dfec7ce3c6abed33b1e963408fadda299cd160bb085a5fcd59930227c85fd26af5db4bfe81dc89163dbe3e515d62ede33675640c5b0af6a2c609300215f42fd9d4c021ffb3b87a272db0d371bcbde64c6c483cb35f4683bbd1e66215020e9400ff10513683bd1909786d3db7f3685f1797ed83e0f6204342ecb743ac6e02179357461940322a63c7b2ee325c8b2304feb40f3d0a62bc547fc2185742924902066faf0465440325727a1a7eb2656e0394fd8b7927f44644b94b9584063cf4dc82599afc03f48834804333658e2764bf6b4350a2183c5b7cfce4a3f4511546f4b030c5db3cbb9194acfc09abbf7666647b0bd578627f9b877be6fa38825ae845e3270c70c27d7b2ff452dfd791cbd266f11a500fee056b2079b71ffd1f7613461531b9a4bbc6760496505fb1fa8b0dfc80572849d00c176e0fbbf69cb709c12ef2230d4ed586a6a90967120650dbdbd43912cb686b6e6a917c3cd06aa5a328db6eca47819a5c6b47c8643a055e7a2188b7582a800eb0205bbb21fc7bea7a45c1e2320ac0861c3b5f082529798f54abc70aabd5d3f9562910e2f0e8e2e3c4d342a97b3202b1400a1828ddd43c73025996730fd9614feb864179d41880e88173023b27e9c35a46c9cbd3552ec6d0775b9fcbd42dc7cef799deb74c8f16695a2aece9f4b0c30497ff3b51c894735d7f75ad54ad29dec49f0accb24f8587ce64417dc3f65cc84d15dbd1ab6815ea4f83032f9fdc8a1ea10220d0fc8aded10f5bb9cab10c8ff47ab1d41bae0ced760f45f672d745cfefcf05a193545be4232df1e099d61c93a43f288fb10f7f2e7bee3074c4f6320fcbf703f86dd8001e3355edb84a62cbcc4e22a1f4d58c12a2200aa455883b483677985fb0683928eeeff5a378fd43fd1f9daa6f3c461938e0cdf9c54f63fefe1f8220b8242289cace97165e7e67a7ead3e7e96e905748e82249d63c36ec7a8b1338b73eebab80121922041c438544d9524b2e219535616bbf6efb49f61ad0809893e7ecad1a235980e593c3964a7f3c611762dce9563282d7711438b1f647741f081948eac8d3b25259aa089b55df3c8938f65a5de94d494a102e41ec3ebdb50a112d28f7ea8285d5544d3f6e351a5f85fa39865a73a05d815637975f1004b642d43b354bd58bf17d3bd9098962b0531257b6621008582a660a5d48b237db74345c3f4cad7a83020b7c3ea271553f6c880789cadb73f046927c1f81730b8757779be3ebedd6c4925fff1bb7b1b9a2c24913dab5eafdb4bccdc1680d5b8b8a1e1ee233cad1e365832cc6fd3acaa5eba52ee8c9796643eeead58c95b07808b26597bb92af695c5766a7f2a2ff7d82b79a75ea19a31b75b53e754b8e48623d64366e2498900e1f4de0f412de94a439cbb8d535a2ee0567dbc88f4d5407b33ed3d9e6cd0260e579aca7d0a2ab6f693ce58c5557a5cd916e8fae824467dd8372b227c2d3cdee7315d0e165db98c920c9063c35222973ca2abcee17ec3cb439b0bc5f469369e516d63a47056df3da38e758d75a1bb200bf540db0f4e3007d56fd7c8af9b8b38a6cbe5afc4f48402279a2ac3507e1fc7a20d4c4b74bfaf8307493a44feb927eee938a46a8c9f162a336bff52e1980da3d657500397ae387481c2319a5e526827788e68f3992d9325f0cdc464733a963480fbb671eda239e06fa8fad59a426b62c935de587e98a68d75d3402e7da5b9918fcc0f5ee7c43ccdbe0d508d99a58264e0f2fe7c4cac6b5295cbe55ab927c42b66985a313fef8130fc9e78d30321365fce2ec070841e1624bbfeaf1c603582a3af3331c163e601a5e32243132e7e2d6d3dfc1e286f6b7f5c04f8c13fe14e43561009664aaabc4c507f2cd14c8950d16a49d4ba83b8e04f7f48b5365e8edf41e32c234d43d188ad5e0c9e66a638d783098807ea23a8dfd2cdafdcf20a7485a4ffaf855844dad089c9e5d2ca83e0000000000000000cf8dbe0400000000204788050782feef909a10217cbdda364e7635d7032966a3780d99fb693d78bc570a446eccbff38d8af11a942a3a47c7b86b6ec29f168e082d4d94bfddcca896dd34195ae2259891cb16ec7d05029c74b78331716de3591477b04464a29be5290000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000091b9b6ba8536b7a39f45552c9fe27e72299880c2975871e8cc73f959c454f23e2d0c798161850216b323126e7e4fafcd2cfadceb029c5146b8837aaf0ebc3ff18d5755727d134ac8b130b4f240ad62e966419ed4fe8f2333a01492a9e0efa6a04879700c43e2792682aa5550916b0c1a1c01d9e3f297e5558fc756c6cfd499cd020fe005d7c20f38e9913f534d8400f9a089da58bbc4c6525b1241d49ab967873803279de6f3afeb97437af3a9f10edd1a0e8b462ce294fa3c18f13d194174103a0b0b20463ca3338c3a9facf5910340214daad9e283b2ac974233b8852e99a36057f012a2e13abbb65d1db6d440b3f0bd1efc9f264cd3bafd88530032b39d339437240207eb509744e50c0ec0acada3e42259da58d6579feedb68601e13a66e070799bf020257847ef166a143ca1ea38de422a7690991732884ab89e82f2144212d95ba720322e2e8dedfcdcd336dc2a97cec6886da0af85f4ae1c746121278657be8a08ec4021a390939fa5d7d4c88ce4227a4b3666acdf2f175abe3b5820b2731cb6ac76b24022880ecc6c586125470de4f29cbaaaf1f5c2c1638a8d6824a52971cbecdd4ffbe2f5ed205d216557c89dfab4165bbe485631fc21f818b7b29d72e54f4e3b3bbf0d4b391290850d5f1461b8b796edae50b40144b33338cea50462ae5fff4c519ec0b631cd51f9ebbdd04eddc442e8fb6432c4ae1e6dc576b44033a46e4db87a85bb4ab63d144fb837ae6261ef551a2c2e8c85922ee04e6d09e83e14e3f47dd0962c277004301b4bbc9ac7e9aefdbe1e94808a0007be5bb05c1950d901639a08068fefcb5a783c6910a30ad4d6a7a0951105b5c6436e774df310a40a6120eaa36989d0b52239e6ddef11d69f2e13516c0ee0fcbce5851fea083e1d6a7f9f6cbe1b473dc3559a510699a75a7ad1185c696ff6c343cf0b5f6929082dc5feeb70cd34f509ed38de5bae6b1b309d3f012e5119c3b132fef0bf00b9d6c7d6fbcae6449fdbf36bda76fd394a7c6defa80ee8747b8825485a336e2c12db9322b05ae751de5b1cba45c0f2515ef88702adb95633ff0f3163dfc747445cb01675152ef72b98bc80da5874a916df3270805a6094ebf08ac160f137e2884d4c0821622f299232ab412c6728b9769c5bf3774f461a800c6a63d3577099ff315fcee7b08a23de0df66e129d78ddcfaa68ce2c6feb6aaff74fe63a649c953fa6147f5390b48aee818f49b362cff46f6bccf556dcc509df1ca5a1a5393abc9b08fa78476698e8fea1105d810bbbe9ec9b0783ce1f0c2bd74c1192f3d3314b6eb17c60573eab3675279aafc6685e7eacb03a90e80d7f747af0f7fa731547d3a4df71628dc81a1319f320460318d62225017ebec32d3146aafd0560a38fc8dff559bae920669767753469e4457b0a753ac51846dfcde9baadfea8311e2653299c1665fff67e8e145e77571e75b849c2d1f5d7edb7c7265f61a5f57ca2d591a082bdc10e86ab8ecae36153b0d1677aebe43fa0e374580a924c1582ce3f1555f284d9a015125e7467b274994c1c7350a9d0178f482c7f983c686ec65bacdffcba6b12eb3ffe8bf06eb47d167b2f51f5d2113e2f5a8c84c0867ce4942079a70277ac4cd7062ac9fdfec5b10637630ec2488a79b014aad77ba787ef89062f2a9c72e66c3b92da3d89cb00726049ae8b80aef6035249a48432b3d208cca4d3d07d29478ce035f9f07da278ddb5ac5af1ca87fdc0e407d6e7fdd2bf14bf82e20488bd09f781f5e55ecc78b142c89fb02198a91f172b77cdd2075e1c0b6bcceea43a7c39f1c1afc8f0ff7ef559db82d18b2cbda57fae883d36c50349a65ca80fb62d9436c28d33c538ad02b5eb60b7f0aecaee51a540696242712234eb1251bff3bc871c1ecfe1b0adfcb92c5a5bd25e2119e94d838c43fd5cfd8a6d3cedd15152373eb531b487485d140460e3c504b71e8eeac9aaed9848bb3844c625831b5f2a76541b277f4e9bc52c76294e0486b4e15f63e08957a32586a16e8d72ef773e1ce919f0a0c1c1118d5a1bb89721c2f90ace007c0414a7534aa146c9c1411a1759d8e883ec6f0a9fe9c3ceb8be986013b5063ee51008132166c0fbe2740b31bc98aa9b73b1d62ad48beff8b3ca2cbbb1f1bd7df8955eaa9bce4ecc7c52f7676e8c6aafdd6eb81ff05441372805692d232c41bc8268700ae9d1d138c6f7cfc61ec0b38a15a3ffadc2d5c82c4d1103ac632e4decb1ee4d489feb3ca042145cd9bbe2ed34b4ee060e01aaf8ac879bec08b470342915245477373dea2fa3a39ba4300000000000000009f5f8505000000009f842ba0255512379ea8478eed494f94570afb8973f87ea4a7e91b2887b5c61728ec20f1df88e81a5617841e4bebb037c22790184d95db097bb5e42f2121bed1a3de2a4ed83467f6584af18f46c5ed9264c51dd66a0525e410b3d0835bea68ca00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a3a2414af1ef0035bc0663503e4830b3b3fce3b7f7562ddaf5365108b94b37d2c824dca061c7509a43d1ff17e7a1375bf341452c2f0521dfdea97158a1e472b89f1b9d4ac852a82e15117d40aed9c9ea3ac70d0481b45cac131a94a71dba668375af0158da84a816507538c5759dffad9cd9bd1e243420d554b7460724d97d41032df5ea050489f570507723e32f856f78040e284f3616c5f16f790e6de0fd292402030b846dd4dc4647bceae6deabfd501ac9109bee08e3694cb988321c59e608420a06561e0d6efcc68489ab818d58aa2cc5419c807363aaa00a1bfa66c366ebc1da1919e3b0d0555eeed9f934c02074d9a80c9e8e21f8b9efd3fb417102960a38db032d733d49277532bc2d0953aee95d484a22c30b09756d0e6177bd8b617a86cb9a0302c1ef8981933846d8a36e38443746e4ca327a3267945d6d5057d63bbb0650150228c3e64373aba70916f751522e456e3dcc4225d97f9d37a4d6fa93101f9d2f130307b65582539ce38ae8c9aab5e76009695b9582e6b622231d242629dbde2883a80309e031e771b5c59247191332efe6b9c60fd93d800cc3d993149c5dc7216a031a98c880d9cb068f9a64a8d8b011a05d0d3105b49e4f2f96c6b889c448491af31f740cf8a39375d5c0f1ae45f5ea65ab6e3de87639f76fb6877cc8d2268512cf178200c24db6a7d4f9bd0df019601ef362fcc20ed395bc0fcfc4048caf5247385e71b4139e3990769bff266e556acd870745abdaff6bb19c200847f2fcc55ef089e7473f46e0457c9becddd9ab056bde83b8b233cee8f13baf60114f5deb99424ab7e6acc2a8becbd4c8255d8e828a2da1402e2e051c75456fec95612f8048fd3a17ff11ff3ca8ae780888293f68d10666c9a849fd6b3b5d960e271e7d26bb8b8b7ab38ebf836cbd22eccf5e9e2f6d18d9525abd182513a3bb09e719ed28ee7c2be7292018850166c9ac870605006ba951b29cfbe95d3962d55ccf5dd117ae58cc20a5ac3fc87f0b0cd51a3cab0d906ac0ca3eb3feedcff2089f7487677b13e6fe453d74166155903f3a63dba03599ab90e9e653c65c38e44210835b899d7246433541130e1cc7d5d7f8d66ed19c282b04a91b5af890edbe28b8fbeb0b47b423d414f447f06e76e144348c18b2e2119bf5ef1dd35693d0231f42acd8dc9f7fe74bfd5e696abf64e991b2a1098a640ad0e5b44edc4422c9827e267765a1350d72e665d2d3fa1e6e85232dfa11bacc2aabc355eb58287fa9f95d8980ce726120265a412428f858ca7eb16b64b764c687f4c23e7e312104e28670c504f85176a2eac32a0097837dce1a7fbcba882e54eb8c0f3880ee27919307a54d93ebd27e898cdd2cdfc0a484083c900fc8603df05d22ce7e12742173e012a3a9b5bff8d2192fc21b74be0e29e1fc1c051404b01acbd2fb6ab8b037c826842f487aa63a4fe09429c3e08f2891e57cfd0fdc8e06ee3ecf6300041d704aacedef452026a0bceea1e5ebbd8c5e89e23d70fc8c9f239fc1812304745df0c7fb0b9e19abe3c84e31e85750b27a562d4dbc1dc7decd4968a30a218542a5b2601c05c3dc199c40ec835b42fab2890906a91ab5a5a4a76c931d2918048d94ab8b6517b921b3fc8b3e8400787e8d87210c0ba7a096b5981545bc83f0bec8ccd4dd05a3b83be5dac8cb7b51040f58540c6c3e5c5fb467ac4af6cf6a3f32ba4f6536cbaf34c9cbfa2e2c1c078b3ada7d2fb3d1186f8d4989a00f9a019ac24cedd9c0dc043ff2da833078c74a259d706eae3fcc5899c454d6dd88663700c611a8e523a75bd7b2709d1731eec7167637c6dea13fbe8a197a784aa9c4b39292c5e549c8d3765c81edb09c40e1dc8964c4c2f42999b216e2ad738cd5729001bf95ae7ddf6e905fc119b0c5baff276bc74450dddc51fb33a44140ed88cc8e94ad60b324d49a468fa52980eae83350da444e1af6c0ef60b18266476fa06c79fa7997960a450389f4ddc1ef369f0202d7992223c1927fb104ff450ca4c653fa435995dca4f9ef6b9013e4e4af41ac6859541eecd5810212847206749dde0c1314559ce75eebe421054b330dfe3ee4855c31a5ee127081629e343b8acf580fb5a4da7706811309ee7fa8324be45f0dd7295e21e36a124c147aed8101ed4f0ae5425dacbc1e33c50a63388706cf7befbd8c390c8f3a5d27ef1b98bfbefd7d385524e04c078956860e1780bdafcb9fef85c9a91fa5a81c2febdeb255527d2eb0dca64f91a06de8c9f05155f286cb442dbd9c5a9f61937f6d2153cde1aa39da9ed47ed01b9b69c93b490ea2f36218a1925e940601912b133f39c1fbec782b034e07d01f7f72affb5de1a748cdeb6683c1876c64a5480570b36f20aee910432ce614b6efc4f06363f8103885e386aa8cc1c1f07e919035897b10863d972d06ae34bb0da90c", "ac", 2, -1372283481, "59b364a42de9c1341712329772482c71b301b4966396934bac9859967bb24b65"], - ["71050a7501a58c8f0924234484267a2bc5161d45ec76278acc7cd1f7f2d6794db4a62fc61c000000000963ab5252ac65ac6551300da93f031640f104000000000853516551ac65526a0f7c840300000000046a6500aba06b2e00000000000200ab229adde500", "6351656a00", 0, -1864018183, "61a699765d2084b8b954c3bf9c3cb915018702c7195420a63f262be60647483b"], - ["915c7bd201374b15277767f331337804b05b5d40b3d326ef1a7cdb89b20b4e741787aa89c103000000096500636aabacac5365ffffffff03e50183020000000001acf1246e01000000000553656aabab40c7e00300000000035351534c57863c", "6a53ac5163536a", 0, -1095091889, "071d3fbe623395be3550c456ddc164cad4d35636760730b5924b736e8ea2ffc4"], - ["242bbcb902e8e939bf1091494df520fd862f4e41dfd6de90f4f735d91aa62d32f0719e0f5402000000086a00516553ab00aba27790c24d562d764a2433a3323205556f91187c82cabd28453dd1c2cce5ef579c36638c0000000002536a0f169275015a9909000000000005526552ac53d5569a9a", "5252ac", 0, 99349162, "fbf374afd532389fad9edab612fe371be40c255062c75599ff83470ee5ef4a63"], - ["65c9a2e0043a3e47be6986ab25b007001ce121a7192a6a8783a141234381b9b59bc88463320100000002ac00ffffffff308ea3d3728cf8256155f6f88f96277f1ee319319857218cb4f924fcb8051eeb0200000009525263526300ac630079bb3bf3babd3001364109f96409989d380f0dafbcebce43b6085b8b1d97bfc66c61dfd200000000076a5252530063abffffffff9a29519905c81595e98ecf37b3fcc217ac7fb5142824810df839be90898bb7690000000008ab53536363ac5165ffffffff02b7fd8b01000000000653abab636300c1921b03000000000000000000", "63656551ac", 1, 596457409, "71b022c1cb29faa7620aca470a6db85d6c83d063db3184c5bc559e3edd00add4"], - ["26b33cce026f7bba1ab3f419b07cde25c0f1f7bd7a474e31586397e91f211c22b0782ff9050300000006abac5351526361d7fff952ebdbc7d77b9107222711495609e1e1ae371ef9c7130c777ef6ad984e98cd69020000000265ac76dcfd6501eb67b1020000000009005265536aac65526a00000000", "526351", 1, -1070540843, "a95658e37fc00a99045667b3deb458e478a8cd11956fa18226ba404ed06cf9bf"], - ["7947ba71042806084182ee486d3ff89fdb674d85c91cea0b4fa73e7a4be5965340debb786002000000085200acac6a000065382981eb6a108216df4da7079bec0baaf2050abd8b72778a2fdc000d307eaaf261f2cba50200000002ab63ffffffff86bceb91eb764b4c388a546a8e877b3f3b16066be429925a60b18c3becc5376900000000066551630063654958fe3869b2b4626d8b6d0ac28893e648893c35aa612e821c71f9466169e4e00df7f01601000000086aac526a00ac636affffffff01f4084c0000000000035300ab00000000024cb7a903000000000000000000000000c2786419a62aaf7ac7c1788a1316e2a392b5be2d76f362a7987979e8176199130a690fdc5e876895cef176a021aaff601266b00f6c7b27d582e17b2054840a70a6c466ff5b40a76ea1972f90fd922b4b1b0b07ce02c1c89c3104d9aad715801e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000077e27f7a419744c162532649be1c8098b6bb9b704aed0e05464afbf8a6bc443faeb029e8436cf57671320295d5c09c89b943b6aa7edd3c8957649fabe128a8a9c516e95eda16c5372a61b003983fe1e5f1d42e03d79af2c7f7f94d106066e5c1eafbaf201f4db8439654abad5fd74ef099c98ee519184a54e06af6d2c43d27840219846815268b58ee6a384d1ad940630c511bca67ef94fa3dd57bc0daf3d6f0b20201511c15417a563e21b3f46ac08f210fef4a468afcef494562c44e104a0ec4df0b16cc79003838493611720c11e942b7c6e081728ac0d150017a7a955ab4cb5c1e29e2c19a4aa96ad9ba614ee96dfdae308c390335f08e18d56da742fb52b58afa03055d34d4db672d43e5e95d7265e1a8a7382232db42dfe1031f13e3d49bb8ca1d030c343634c53022cea4f16f7ac759dc47f6744e77482dab5ec4fd1cc998b2212b02252c52807cdd86dccafd41e1468fceb924a7eccaa7b45106887cc912038ba1ef031fd2502b662622cb6cd015cae8b953b1916b0ee8c8c9cf55bd7c28b87b14a2d4032b07550a5d1ee3e756ebaad5dfa356cfad6d1425312e98917d0170d28ed15184ea539e90ef7a497bf3b33a09a0b92d368024e92a7f959692638e8e1fcb6bd801c58aa3ad73586fce6440452b79c1b56a14cbc1b2eeed6276acb9609332311fffd13f84d8b6d8e16864e6b26b8a21620f95cc0ea56370b961be167a67e7f45c0b41b2d620d17752e7b47a3f79bbf66297e76a0327912c5f8a1b9229e77edf1469816f9f11135c3926649355735a0dcc49169827c57e264e93f0c61cc4b0786b9f7d6ac6fb252f9f5a1dff30fe36463a6f3b122adf2034c6920ec008fadca34696d3668c20387f1eb1027443c2c4672ceb94cf03cf376d8cf21d9eeebbeeea54ffbad474a59384f5da187b84881704a2701d721c95c55d4e03a66e6a25ab1824cbee4fe693cc739b23d585f9d1fb369dbf38f4e2026e11ae54880878f66f3cfb571776b49babde1e6d70178eef8903433b6c377ee8410f2f30a76501d3b5c6476051d3f859e08964004827be21491a2481a507a6dad4f6be6ca30621c98e0677012f97284a148f8f911789d3ab2b36681db7e56ad1188b7648a144f58bf865f5a578d9633180cbddd393c33793a6664a76c72c5ea23ab50f83538cc0915fca91c6cac55b0a9248bb9dd3383e3979f685b58536d9361fdd99baddc7371e84b891e0cf4ea56e38467e8cb50979518e44935850e162ded64cfa533205cb367e638dc1362571dcd91e4ff606c20999f4880fd1075f3c7891b69aa0e521ad163cdd69969f119b2ff2383e896502406515594b4e0496e997c272eab30881404df602daf5d8f37aae41f8d4ceaf8d15320d5d41e0176f4922650b64026a1cdf91b71c004c5f58bfa0bb1c4275716bcdc7c59858d5ab509688f71ed732d1090a87040355f50f06a12e79c37fc6593d2ffef7df45c3c46451ca02ba42c32e7c93b1e330f5e4f1afa9139dc77a9dccbf5ccda74699dd1c63d6b751b64fd10b556f1be930f0617e21d5abfdfc9c883edad56af0a3e99677725d3bc53baf64a73f5c9866c8226fa157030dad27bf2c1cdb3d9a3eeaf4e141890be563de92b47626690ade55f782ecd88cb01543d4e10c57e10aa8252dc4d6a0f602de56041d7208904480542bd220ef7febb1a82013cca9bda3ce7b891bf996f14e74efb2a8404a499d75e256ce75d9f61ab6470da9c5827b41dbc23dd275daa1e6659c790b20a8c331a8bfd2e333999132aad613ffd923877b21dc8d27b8ae9b33e4838ea6ae9cbfd6dbc21e1d80d5ba3d04cba02b838c660c5d16cf6934735f4f49a91043580fa876b4693386c1355472dd21178c3b3fce9018cd9083917dacafdb9f6a242eabbbbda3f96a069626d50b9e863d1498e3a74c02b31e7b4312fc8b10349c2d527c6c3faa68f1efbd438e68a209e4b36c9535c97a7781e40230dda11325aacd43cdedfe49f737fbe56f985258e9195f5621a160fa9611ec9e59bc56049bb446afa7f0346f253d2e91d5f76b9e869202e53d8a1077d49cef1682b9f4cf2020f479386a3b8380ce878d9bc4dd5e456f8284362259d8067a57f9f7caaaebc15471fa8d546c6f24551bc0c13c36785d79efde73f82f247cd5f738a9271af701f646e928ac28399ff03998502873a39e516508b4c76b40e24dc21c16e916c550827ba7961bba74af1431bfea1c2725f1a53a36aa77080290712b8824d2a791ad528b3fc49e90d2220f67c67dc7ef4f9d89b276be72130b025e40fbc00000000000000000022f890200000000b60f439c1bfd63ecba78779b261ae6d9daffec0f4122fbf206a14607a7134fca27f5cb81975589f2a8931729e88f322d82b649f453675798fc9b86848f6c1038aba87bfa06794b0886accdd81460e77c39833067d650b51c2846e9c5187fc09b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070439d464b4a06ec1c12bd6414e9210ee34ca5fa40aec3953cd6b70c6353f4c7a8d659ecb74138e73acebc6274a17e79efaca9ad833e16bfeb156b0808bfc604f07b58352fc7a7cad0f7213448ee842dadb6352320f696ba433eadf11873fcd128b04fcdd9fa0b24287ce2e2e0ed6181bc03fb6cfa27645bb4b9c684c876ee8d02017075cc4a151ea3849b6cdd482eba2e2aee9f076d6a63bbfc1cd3e39526fd3d0316e81f1210eaaf529af05d18be446668d5c0cddf8f99f85e71a00a84b18f46fe0b22cb46821583c7eb03d8ef7b41d07858afed30f7c274abb10e614e988abcfd4d11a0644e4e9fd40b883b05ffd26588f2b98aee192d70248b93c89bbfdabd6dcf022ddcf5beb477f1d1dbc57c9ea89d83fa18549b159d8e34179d2b06b49e57996d021a36dd5e47f4a490600b5335bff98bf8b935d3c232c31fdaf8149a0c66f9542c021187330d31014600e3d4873581148d36a2e4e8576944b2f3d9977fbe725d296f03104be871a018ea55267cb5c46f1e683f2d18b0eb383e6932d1fe285b2de84f82022ecfa4cbcf2f6b76d5e75bef16c1ea994355bcac1d007a16d49fca5f6ec30e0979c2875b8695fbb6c072363c9ff124ba87a4e0eb476d942cf8f50d1ceb6b93f2a4aed7a204c1f59a345e60bd45e84c2c83c813ef15f42870ee4dd20f9ee0ed6d265a4a350e4398c5855045b4c7b343fde80d9be202889962882982f4e1fbf0bd613b2d79a8784de917eac794feb85f6e0618fd427740adfaf2b9ae96c24a8ca873ebce199db9b0b15ce25c5d773eb4c8ed9da3180eaebecbffdc241de78b7e57de20ebad5e5b8bb378accccea235083951c29ab33f3e6f6ad1845983b3a18c5f6514fc96d1d4ea379067e73bed25e01d609d38b70c0a0f4399b95601518f8b56ceda71f46433c5da864b4ea14cb3e94acdfbbb425af383ffe9c83a20617dd7ac610a5c85d3a7f468a22f63a1240c015b093b9d1888ede22cf1ecd8a9968adb9136f789e0edb45b55fbd5fdd7a894df09a14265a8a01da4fa466b845126821418adaa31ff87e18fb6ffdf30b2963e04879955868cd03e96028ad01f6d2aaf9c6df7c5697f79b3758fb377f014664425ed8573b6da5aeeb5a9d65d063e2992ff9169394442ec1249c0504e4c0d4e5dca67a8b49c976a8a7f4f06022adeb40f47caeffd21f607f30f916999faa514c56c1f064f96b5c61b358a78f01b9a0b5a9762ca5e6ec6e2d6988083185260d2f8fa89f6bd67cffd3eb0855e04c58d00ecb982a5f46a87eee48af58ca341ff7c6442a46e8f0039a89542cd234ff5bc4fdc700de0db25bcb45f02546a9d0ac80328bb0f065ce484982d46fb8223d849699de551bb08d07ae8e616431199a7f735d02a78da12843cf6ac482308b2003ea605f4ca0b16076f9ec84895b67d910f2e2372bd111fd65415593992cee426fb6642fdcfbdb7a3eeeacb8c7809b2bfb34b9439c78dda8e51846cf05667e4d1e780dc845708c59e8baf5093267daf79cf888511220386c0be053e56258a90d99abd57b2209959223c9ea2bfd4c0e39b021a27e077ed97afef87eeae4d72f9657d47a07c55ed8aaa0c8af5843cc4a97fa7460189d87abade83471da35656b6a2b1178a4d4ba001acfba54e70f77eab4441d7e8f93ec750dc8a4f67d5d5d56068a7a20c3e282b20c0dd09fb2f55a8a243e9ce3af97bb6612f4ab266ffa738051fb8fa680971586b9578c4bc2cb7f3b510b9c57f2158cd41d1dbfc3deb43bc10ee32f5ce1f001cf8395839769a2f0b66a3225c6165721eb6cce174441661229b1281b2d49bc39e9e3c55da7700cb6fcc0ad5ad5e73c059f9f24e6c5c67dfcabbb45d21dba81663f471bda95b5772db483832787b626ad8fc02107c3c6f4c5c99d8b9459ae9ea83875e1e2d27a63f08f8ba90b42298d7eda032c035a9068e2f23e65bba9a78e8fb1ed20c3253ab8543965f28b7bbab033f1d209a569c51bc13d1f9b8c31d990af73d4df03f6cff72a4eb760b1886db3802226fedb0c4700f47bc7e61b9904070aeca79a68387e5c8c2e0cea572986ba6ee1dbdcd9180be185b136240cdb02056585e82a1956cf9232d3d138e29c3deb1662889b2728af2f85fd025359c62e8c41fb26fe1f287eb22b47e26cd4db564176a91aeaa36d8d3e8688c6ba62c4b35ac79b5408b639dbb6dc8b4dbf020f5f9046f22419ba884dab5ed8350ddf4412f747c7428cf58309e4450b7b4636b73e5ffc20ff224172eac07e5287edaf770731f713b5efe81bc924e3667fbb19f0e2335986f0b067e43ddcdc6462eada5734783802d33507463fe1ba2c1611c95b50cdcd426451cf2c8001b7bcfcc87894d4da613d448c3ed260d073144fc0c196853c1d289ee97d8e9893f211af0941d5daad2c99b7623e6beddcf36150d5c3e1c4f420e0e", "53", 0, -1676254180, "96f55243108d11a5d44638165ceef6600e21d320462b7873aff4b6d60d0d1254"], - ["878e93bf03da8ab76e0290eb35a10717485cec2d64997d405d23b63b98d56800a8ac123cb003000000095253535253ac6565abffffffffded8f49b6c83c8ecd821d4d29c563e9827be40e0b3754b556edf3acb18286917020000000163bb81ad97ce483f2fa49ec39505a6c162aab7eb87f9d803fe0e6046f1a632c39d8d0e31680000000004ab650051ffffffff024c3c150200000000026aac435bab030000000002530000000000", "516a52656a656a", 1, 654153698, "22177338e483537aaec208b340d798531b3dc71441e0a511985b12a5ca131c40"], - ["a439457a0180964b4016102711a923ed926d4227c875186b2bc24313f1de550a49bdff90f1020000000253006f90d107016ce172050000000005636a65ab0000000000033db9d4050000000000000000000000009a8ff768b7841bf62dcb5f6d42569290591f12be16385780ba1ed26a9a1d39447847697c118a9b8eedede49a0560c92983fb7c7079bcbf8b04aed3d638bdf75f3f2bf8b986c8848594ecafe28ab46716d5da43f2430ad9f26eca0c8077d40bf60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000089032c5f850d5e98515dc1a12b0e7587f2e7f8e08aa6f89c87ab7895f7a3b4f884c1be66358b54f171107ca193dcffc5d17fcb1a9af954d8f3272bf0f4dc0f95fd8ae138c9af028df7931f6252921a6e87383a281c686a0b8ec32a552f54d0cc60ac4dc7a050ede354da7339e1826b8e505ba69f1a77831ea141eabc34737251021e50a34d3bd2880bf4b290503ec9b3bded96129823a97efad8ffa83d83754b080324f76f42d94e445f3eebbc01612cc60bbc5a061a3150d1028f48e6f3ab36ac1d0a294e2e94a71918c922db2bebf00d284acdd35fad95cc14e64d48e594e3b3dba32ed990fd3df8c4619a6732cf6166746c72d5237d84f57985f3cc25902a33db28032fb0a46ec99f53a680d165f0a2045157066fb3a27bcd71a752f1c0e96ca55751022f69678e44d3b55c6638cc1973adf184cadc95d2aa95c0da84a5ae769cbaaef10319d07e1be12f3572cf8b530b4fe324c3c9a06d5a22b916690a8a27776598c7b30312279d9eb42e73ff789cf3dcda44f5f0afde88bcd8ef9807045a8aa1b82b7a06032309228d8e3777b20b0e6fd90dd4044dae5de0cb1d27b9ec35f2e91601bd22ed5a6630abc9a9cfc9d38c1d36f3d67daff53768dae8d0de0e84210ef34b5c80574b9e706db08a87224206c72eb5ca0b901b7dbe662061b7ea83a4b2bdd0aed85680094fc2edba5a2dfe0e305328fd27e1be92df94d2858cf90e4d869dd79965b6eb73305bbfd8cd477f5a06fba9655e8627f5efb833aa70576e90f5990a01bb4ae86686dfe2cce5a9295b0203c3e6142a4e2bbfe86230d3e21b7bc5206274a3e1ca0a8e87df7ff88f9f74b91edf2a6e94affbfe844f485f9c758ed6e527b5ff7a7752abc355db172b08a84c373d2e03acbf39ebf6ce321a4112863af4ae09616205268544ecf11f546acb4976388cabb74e528e8a648610e39d6e3b9ddba99694d596e15189ce2a9036b9f0bf4789aa94737e88f26809ba6f97be31da0cf0667fa69b9ae92d9e44f3a192016ac576b11eba5542c38d365b340de2241c2ebe55d0906c3c2d0ea9048cfd22aa290cc2ed21a48a774e6f9b1d011e93128e1b4602f18e0544ff4b77a7f82cb1a80e5c76fc62366ee754f7973232515562564de833cfb6ba360406a4f753c5e39986acc1c8061264389f69375f179229dbffc576a47d290480cfbc780125c2ad135a8ecb4627a26f404b7f8a8c9a09b2a69a7c960504ea5566a87cfc0ec235051008d19a71244ef72fbb39ab54359b1b97a210591edf8bf5c97d488a54393bb605bfe66c5a34d0b4f3427aa1bc29f170bec4286aa56d678ef916a361ede7740f47e53bf0066f6d23c3779c2603ab9a4c0f033f335444f0bbdc22c60edf79cb90ad9e62024f12f0a5ac6dcc079eea6666d7e364b73d231e25a63129449a0924794685744935d19c2870f66a9fb5620fac95045809aaa7dbf347c2e7796306c584ee61f438c1ec843654d67e25b103a12c1fc8ba8a49fff4351c9f2724ea49cc4717d80dfededc59b522c98b9d72058958910f69379ef6a36d341942873b7d4755da87c599eca42bdc3bc73fc16680715260ac0ca5227a5e89d49bed3facf7d37210abe0f83778fb9f850dbae508766e4adb879bbfee180d951b201d24d3a2a9054eb93f2e46836b6be7e61db100a2f8af52eed87d1afebee0ec27f95ffbc5a403d433e04931e15df496dab0e02084c05322c77e82b796961ec9ab1b47b8e3c5bc6c52fec28fb0c380712cb5f226d785bf6c744dff89d0853bdb4da71ec6d551b9f6e40bc76fdda04732d124132a5b050bd8553dcfa37231e4b426c7805130e7b6617f1f65285b867692927b5d324506919df0bb66ffb39bc332f7ede8d2ecc4aee87252e19b821ec9b3ab1fbe91d1e8bdd9c0b0059e09183688b175ae143365d93470b95e068e8621714354aaa6debef4961fd9f2ed54901e216fcf04a73ca606fc7731c0b4244d081fbdc6b2ab17872cac740e2d103586aa3738e7c7f727330d780ee26c74f20394cf144f2f3083a24b5e7746b881762af99dad2b7ad5de057d9233ca5d127f48049d0d2b18e2cd43abe48c38ff7230eb6571e7f3a5c8fef38e3a9d1ff883e60cbe6ca7c525c849e42f6400ff8988a9c394992b1ad50510870de615c9222ef5df28e69ca0e02f1ab5e1026453772c4b31658b78d41bfb13438cb9e123d4d213835eaae761c17e2f1dc7967993261ed721e7f1185cdd6f34877fb678f5c5ccae4ec3cbee8e2e4de9ba28d5fa200c08ef376c3cdebc09c7b5672fb1105cdf6c2a83f0633fa003000000000000000000000000c1f8762bbd24396b1cd7c42123d9b58c9b3bb43cb2c1b9529cc4f40fcbda2b3fcdbbdab78d9cff090f2e0b8951230c99f62d12b11ed3973adae4a1418bf9b50fdac7cf402b0fd7f9d4f0940054ff4f7aaa0118b04949917d4b68ef458ba89c07000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006218e1f3d49084d853570ad68f43d6212c45833d5056f9958ba794100f730d9bffa98a38b06155b63b1ec514ad6cd044f2a679ed46ccc6b6770a876c0b4402950ad36272ec9c6714deb6798caa1f8be56caaf91df415ade30aefd49ca2d0202bf6327d1e8d1d33d048a7e71698d4d9040b794cdec46d42bbdd51308e9d8a5797021ff4f128f5ff1f674d9e3ab8ad0b636d09a31b82fdec2cda9e4889c4e96c413f021c83fac2a6188edef19e19bcd53de4ceb4f5b97377593c3a502ce99d4a9ba1f40b1146941dce85d04995518d9d9736842b023212f8194df567d99c78c6d3b8976f032f49f3e302b94075c087961d4b66ff7fb5832773e92a26615031d859ef652c0321212ce6a1790388e28bb7e60afdb6c8283db7de1c25d9b8c407e111845afad0031a6c5c60ecec70ae3fe8316f2b643cc1d0639bed325258527e4a6cdc0158ef21021fb2367223908666979ad39a86fedeed3a9ca3b06bc7e2414ace79b022c7d5d1030bae5deb43e9e26ac1140a094a5c2cd00aab64e99e2bf40dd6609a4a5bff8c79021eded15b99f507a417db2113bded7b726407c917fde30b63a53c3928e1a6593ed1eb64a3f02aee3c172b98dad2807e19691e526493f42603e437e8f2e30f3a4f1a379814683dbd2a5deff7cc69ad158e18707f339370b00bf010b5c5c4679934c03c6408de0fa5dab2e4ed40f96be80c3c4fad7ce1c4d365e421788010f2538957d5f774cb5cac8a386002de4c370dbab19cf9369daa5e7318f5ee07281a844e5337a19a2eb98c160c5931f0b6eec2a79fdc5f176c17e93eb26bfab8eda86b8a6d584cd271f41f18d9046247d4160805a8f2fd7048218d3f219a4adc939c971024bb026a309057f938a1878166a029ea0e5f66fe17bac953ae89536d3a2f9051cda00c9e282cff4347bfdd56b783e75b7ca8e838bfdf9f72c1b84133dab0461b53ef13a8ce6ba262c613771d1a7e39a2b6815fe6155945f4a95ec8581ecd2ab60c7894cdc4b1a58a5e4473fb77081ed1f3533bd06cef78bd866825335fdc5e3d180039380ace265e4538a304e930d4180c31452174408ee40a06b2ad8496799b1159dfbd6e8ee9981b6cdf3b400cd8d32578784946311da4bfe81c50747248068d7a7fc0e5998c6c46dc4143a000ece438c20917ab43ae0a33e7e7840db9b9b13372048716df912409efd95cae2d545b9bfb165aa3da500a019fa25398081e8900798db73a144efc8e9ec96a990c77438fc1980812a96c1164d9b49326c124f0ed1328c23ca897aa47d1ddbddbb0c9489bf51e6f6e44e991f59c72c473aa53c7ddf0350df96858906a395349bbf255efaa9e067db63554a6c9126e9d9119bd27c140a6b15e89c10c4d65a3e46938eec62c7c29284e6bffd8a68bf89fe81543dddf3323e1818efd2afce48a3f5ebe7b44a8603e43127beb5175ead4eb048aad9b1920d28142377ada59d98e9755fee48ef1ca9694654779c611a6ddb3efb1062e4ac3b34b4c8e5d71888dd1848fc43e9bb660c24a354a3433de4c639259fd8664fded42422ee270f2624282a28251257d490bbf699f8cdce1f8f2b29bd27f3ae8345b7627b74e2dbf7000e8afe4d26be49ff0510dfdbeff4ce10ea59a526458db2b9083d5de0255b9374e1d0aa39f2d0b37670bb683a4bb061a058687c84965f2c527b8e2e19f1a110dbc2778b5d4942ebdd8773b5bbe0dbfb4b24c5082153a6b0cf5fa7523716d2196b82949ad6f7a8ce04ec1cec561199148b56b5548a7b2b4ec8b9c780e94a7cbfc52ffaa082b15be437edb7c181cccd0376bebfe0907881079828c9dd328efffd28421f810e39a2e35339de2c4a8b24fd4f591fa2bd79fccda951dc3f98c7098ffd2a9ad2118514e6857977e535eeb94579a91d15619bfc9239613ddb2a960d8423b0d2e12814a2fed1e1e0d1d90dd1c717055675cfb18d5dad56e5d14b1c07be2ae76da9384bb6a0893876a78438c7cf34eabc03a7e981f74687f5c86bc8cc85d6965fda9b1cf37cf8e69e89447a2b611609bb1f7ebac7404a03d4e40a1d86db91b59439ced38386ac024eba3bc5c8c8a914d34f97ca6e31760afa9ef62f72fc29df0cf0da7d58426204267e0558f2b57dcbeae63f0d43438caec44deae52d48ca503f799cc0dad26c8055bab98f88b70738b5c50902eec3b0010f5489ce2a40a002102bb36f12dc0e0a59b2a48da217f3a2074426c79466fcff8ef11f01e685effe675ceda137975f6ef0c3a8b5908f80a058fc301d73c5ec771de0437da7c89189f3efa3e2fb4e24200000000000000005d72e20200000000d34b5fd577b8cc90700416b58b4f4d456e7ef0034ac81205aa7c9fff577931194755ace1232b67dfbafa74025b597615baaffb87ff792ae2b4a0c69d492ca64461a60b9840855c2610fc8f69c9934a6ce3005d15a568a53115946e3227a7991000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000acfff3e09420cbfd207fbf54e225f7e74e42c57ff1ed40a2f7f286f1b3c3ca004b64bbaf98a93c8a2786e27d5c2b57b4fe9200a962550f1d028f75decccd15e482aa5cd2e148b0ca4e7c8ef1018d25b33680952bbb372cc383e567eccbf00cad4724dbe34f70da58b6f7a84ddc263ba011aac5aafa11bd7827077a2de381314402104cf46c26d3c693c1bbe7568379c257adf2e65eb60e38ce9720a4846b336bcc0225d91a68f04f55f9fa7a96155ade1c234e2c3627c2702ec96390f961aef9d0220b2dc488967af75020adcb3d90d0e6be3a0ebc7dbd028fc1145e81dc8709920ff91aaead93df02b98e5e0df7d9395a4579ea217c1b1916ab1bbc51bf5dcca3036b030408e2dc5b837d512c4df658c414af4feafd360541aa53e61ee21ea5266a10db022f48e34f765592b1575f276a55b9559251b585de585e2b05fe2575b48098192b031c9b0096ae75eec0ea27b87d527f51325f21e3b7905798363854aa0793a1c45f0229584d9fee2c020d5dce4fd25c729a86f9647313c2c58899123b273eb7d945c10317fc08e5f225d5ea0f5d6f3136d249ca162c06b32cdb65de3f6b9560ad413643c0add073b2a3d1c74445c561a666ad7347ed5d5d44ef084a7b4238df1e9e8c1621ccf26d622ef87587278ba19d7081660ce616936bec2786d4b03286746ad1ca3e656d8506ab1c396d01a7b9f3dd7d72564b5b3dd87f88a228e4a1136dd67a30666f8a861d685edc1e6d0c51d0fb7e68b2593387fdbdde724e1350f130510d0553d7d3a180c61395720fa8c6eccfd33fda460fa19611eca46976197ef61e0739c891f85ebfdd6a00ea38d192f98778b3601f2a289d36f8d04f011816761f7edbeadfdfc07a0b19b0c805c02df94cbb56e45e666b97b2a8114346186a01b51b600ae535980f316237db9d85fc34d8800938d3d4db495c42567272368a332b9dbcd8ebf0f4fdf9ab4b5a62784a739932e8ef3ad5cb91936110a165657d1949930ea9fa211fcd0280397a3e1925955b9448eca4b38d1ff19268fcf886316af987fb7c48313be16df9f795ca348d9b3f34350c790f1ed377c86703d8194a50aa4b0fd3dbb74cd836ff2d26028d8d8a97bf99fa98dbd4de6d844440a8a31c0948453245e755b4b5882152a6f631b43ba92575bdaadc0782ef602049c5630282bddbbce61e2dcf89635b3aad8ef826f5729122a4377f92a4f7332f6d79891ceaabc78f23e8a01f000b7e09e7ac0079d007b2268916bfad25948d12a38f092310f29d75ce45a689a7dbc437d3fa699b4369cd1861adf6c9f019a40265cca1c8e07394cc62928dfad1d766a55fecd3e87606d3a0fc6ec0b53cc70370b34e9ffee67da868f34a308f7f23e8a1c0f9053f2b9881ef1f261e1dc22fa8826d61b259e7634c23a1323f8266d42322a5a34b8a53991c9f85a9e003ea82703aa0c34098dab089acf5691c3340d06363d95e49cc7b6093dca9d07e95cc34e79e09d9815733cfbcb5706eddcd8324836a1c2286b92cb0c663a4f2e13c382c81372a16414958f155194149e59d79b8864db508956e0da4ae6e5a035197e38cf32a9018b82164ee8c4f955c9326611b0394d668bf3529c829ae3582ed2675396182a48412bd67bbfc87ab8cb809fcbdc9ad62f84c0718a2ccedcb77e655c6c9ca4d98367a7a8844f8573558afb6cfd7d88fb07291f9b6be3176853cbbf745b92384dee2969719f17fe8fabac2194c43dd4d3080378e382f8b8aecb4f624f98c57b61fb3f64ef822dd96313d7541526aa9375680f3f9a47fd0cfe43449049575cea45a644661680a63aaf8830419023da60df47d68433c55455bafa27a4b2f2af5949ea5a8fd2d0ace54d88ef0072c48d36c497938c9c7745908acc7d2fb16c016f9e8c0c2b7a0b5c57497c6785eddefebdf109780a342b0f6230531850a22f27507b16fb900c1233ca69cb0e0e54fb134ec050aea0a074e6a1db65498fc0b5b4923a63f2ad2264443d53196b4e75d4aef91bf77b4e701cc941eaa1fa6d2abde8ee01bf52dfc88392a6333d007ab3fdaf24cb2284ba59bd0db9e7fc1bf1924fa56eb08e493dfde21422d95a573ad883aa4e193c9259727871bb3dcc34bb30d5cd63df39bc15af3ff0de8c6de5bae0d4fe844735e2ca7375e22c4911ba8c2081bd37c8f3fd0f4ef4f95d6de830a0f05776f8e8ae25bbdae34218a360f93b9f8254ba785e43de6fdf2555230732dcb20f3b3ca9e267bf92f9c698f6bd254b5d429c8f53b8a776af1ac4e28546531d4153331030dc29a88118f233be3c57a3e4bea25c00e18c49007019304f1956ca2fb84a8b7df68b354fe96cf45b610c75c8fa35e34121dbab78e33cb15ec32b21339529ec077eb19d13110321c942a5187591f9b08d2e33f195b352de92fb2972bd1d16971c4bc05f4e209ed25fa07", "", 0, 699689189, "c15ae46c62646892a265071051de2cef71842bb33ae9294cbcf8896c34398630"], - ["82ac9c5a017068bba7ecf380b207d33e93612c7db339df73f593a145dda9fd32aae22108f5020000000153ffffffff0165591a03000000000263650000000001c3c7c40200000000000000000000000026fb1f241ca9c504f221e8436e535bbb714e2d4bc57ce7c5009dcb82ec3c02954d31a185dfe73a923b357af54834a1171c8ea5a359a0e77b917e3a3d46705825903cc7d4e024e9f6e84033e23010e202cdd01a1be1216672f59b85449a4b611a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044d687d4424377733d25b3ee860123d78a939a4c03b488327aa8e3b3020bfda0d481ccff08915404e7793c93d2cb0ead38f96c28f1d6178e3b612e8f93ad800626ff05cca34f7e7c75bf150496748bfba43c0ed79eae70499d5bf09bca383b09cf2a63c014a7a5a5dbfaad28c85e012416c7051a32e23eebe9097efe2a5894d8032c924a07295c6166d4a5cc60021db741b2e41147f7526e8cb7a58512e2652a8d032ae6e6f3192878bbb40851570d21261cf5c2c6d054ed123c8ab376ef588a28dc0a132aee74de075677ddfaf7ebae085dae65419c5e9d55ac27f7c59528482eec0524872bb3ad544592e1b9cb90203fdb6da9e961b30ad0ededeafb3b9b0f0c78cf021976dcf9b9c3c7d9d807934e1d353d9f7b12224f0c8a857bd0e971ef0cd44cc3022b13bd143df5b72718122efae6bec6168bc0db7668074ad6aec24818341c27c1022e17b8c25289e3239f19da3eda2e53c5562653a8c7499b92a153e92e0eae0cca02083858b18d149d69c585dcb299012768d1cbacb89c80d28c3681a18e60729dea0221c7186d061e9ce05954ec24a56045a0095ca7b447dc95abcb413980568f6c2440d985c9002a6c16471bd2c7c3f9aedf68aeed8af18be62cacb5b4bd1c1c4d0ce44380a27d3233bbf425582aab10b8c52c048e48c486c1978cb40be8ae1c8427ecaf9f035b595335b30beaa14660f544fb181c25ca0f425a4ee61d7feafd67dc947fa8603fb3e3210f163208f1efb889adbabaf109329c61f00498d59926ca93e3da3fe151388ccc2e0be10e6bdd301c877593410ef1aa2b9fad84a9556ad41c2af0c18771d2632879b655bb9cd718399461ac95e49baa71a8feabe5eb8d3de9447c357639db284dd4dc4d18494be8614e9c52a68c25b2d86ecb17124699945bdfd8707c5159ac3a2142c7220d279a386a344a0af02ff32d7f97d5cc5bb1ed900ddc6d37d19dd49b79ebbd6a7bf7d90245324cc52e6ca0e549e21918243d1daa461026e7232d30f4514f2d423f6627a6a20a67df3b3bc5fc73581f071c3b25d1249971df2f8f32889520d84a1a07bdd09070f0c7eb126aa7195f3238fb6cafa22a402d8a9edb10b562cb21d6e87d49a670f9f9062a08ca1b93deb17700361c8ab6172b0d9a5d13c62831cfb2a2d887f3ff1f78ec2a1ffdd7c2b7b6977788220d3996f1b6393bd1a801f3c8e7a6f91f71e72f1cc890129216fe2c616aaddc58ca30e2ad1553c9c1066eabd614a52bc5b75e2183ea2515417490eefabd0ea8c68b18b1695b494008042d7eb14f3eef8109440fc6b77d3d0d15de5751973e4c50afe1dd2c2adfda3296843dde41f432916410c21af9d0b83baccd3b978d8fe054eb649bf9c8de50043841dfcc7de2299daf362d5e350cbd95f4c43112a805ea94f0ab24404b8c40a3396fdeb46551ab50a73dffac2a478def97188820e0ed75ea89f9c923e1469f61e10521f5a313d94bc02671a885284b763de627c9dedd863503dc911e73c5dccf7e649bc722b489b845a390ad21755a6e6ed4f1b076e86f2a05bdf1bdc55bba7bf10395e4463cd84d33c3739fa2e23de4baace991b1e549dea4adf736ceaef206031df8ffd301d579b51b4ba7329319fbdd389e79dd8e0583ea2e26878901d126bfc060401d1d3edce03215ffdd2996ce1d8652aae4960b767c089f3eec9dc3b77dd23c6de4266ea712144d25e0deedda3b24039c32d26ddc3a293ef6301b9e33ecef62315c14242955cf9477f96fdaf189b59c823566703bfc449c02e7b4774dbe996a01078aad6afa8e9ca0d8781facc2f4c601912742e263df5653d1f2fbee72cfbe215d21a7388ea2a9df9785f65de0d971df18d50ef06706bb3719ef2aa6b9f817228db71a1afc62a96499629238e7c81c4d5b421963138f8cf455a9ff9d8eb9e040781b92bd6dd9db4df02440f5979d4d968600ef3d8b8fe391a13d030f8864f5c517c9e2fcf5ce29aadbb1d76119f7622111b2228767b86cb7cbbb7b4ae8702a0e316cca9728006ad9ff3a096c297dde9e29c976bcef839e9b8748b4abbe1db1ff2d78b6b9275433e5eb479016f7673636ee9f03406b928f3ae6181de100be9f1948b0f9e9fcd5c1a9ac67f917e35f82cbc3c99ad1a739e8a1f609579973c82c6fe77f65bf05bf3e0d24f0599047cb13515c1f32b16a3c29c5c3f37cb3a43a88178518f4c6e5990536d386e93d691e323cfcce6a7aab3399241db9aee4987dcc3fc39077899d6ec9e4e09a7a6a40277b02ed725b2c66278b5de89f0160cfc56fea8d0d9b6bc2e2f6656fa6f5afcfe3905362f45cfc2f153215126c84b6867d2626170a32ffd2b15d113f1bbf4624052df7ceb599915a10396c0dfe82864bbf92cfd52882425f7d76fe9c999a366e36e19a68e551ae22e2daf171eec29161e18fca032b3b3f9e6e00", "", 0, -1856072120, "c13ccecd01c3a55cf6be70b12a9b8abf5156c76bb4bb1dbc3517d6d5479e9a28"], - ["94080c8304bd8cbc25dd597d5f6f90c45738d968269ceda7e0eafe4ae40303ba87ab255f0a00000000045153ac63ffffffff0d1025e21795004b21b9ffafde52bda8bf00960bf80d8f50063613b52a5d640801000000026363ffffffffca2f7580d22e2505d888d6218612e0cd8da6be34de926533051152cb156dad8c020000000453ac65ac5c6163500efdb5083517d6b9d481f457460c95f373cbb0546744124a14e518fd2b8528b800000000086551ac5365536a6affffffff01244a1e0300000000036353ab0c8e4bee", "53516a51ac53", 3, -623078067, "1c52caf592af9810c424958d6b4ec412db104e244dfb08a408504322ca9c5888"], - ["55b531ec0121b8a708f5612bec69987a38adad2e368415887a0da8061a6085113adfcd74b7000000000965526a526a65535252f69352e503bfd6490200000000066351abac5152385053050000000006655251006300d81b770200000000016a00000000", "6a5263", 0, 7550576, "83dd52f7402ff4b10f8022e0014cc13c1cce863bd224089eefeab5b8bff79047"], - ["abbfdbc002b7476778aca611d376d4fb6c18222656258f6cf646cdee1405a4332e98af406e03000000056563516500a4e902a0aec3db82928332decea4b6c1709d7b6125c4288fd98e176ba05fb0c819182f0900000000025152ffffffff012fbdaa01000000000500526a636500000000", "6a000063ac52", 1, -744043499, "00ae05cbff709f588ce4d2479a499dec122beea0ddacca0f6c2fed90aebe58de"], - ["782b043804cd2719dc75c897c26ae980542a641a328c5b5ea0880c7a355f224eedb24b0c4d0100000008656a6a5353650052acb011bc64e9877a8683e00f425b81d6d412585509de13c2a14316509eff5ccf44f34c340200000007656a515253ab6ab90aed62df88fb3ee5f4c1e08131926c740f8b875814cd0e9e19ade78bbd63855adf545f0200000008abac00ab536553637c56a0c60756330c0ebc3521f6e62229787a2537613ece765d2087bd98c579ded6c229a50200000007ac63006553ac65f98d1a78024014e3020000000000b2fbe8040000000006516553006a530000000002ed634300000000000000000000000000a12884ae0b2adc5b56d9899e4f9289099d00b37b0df765de03901eada8a42a3dc9f4f35375022fa3d30cb5281782e4d5b287916878a79db72c8f0eb8dd8fe31135ee1eb41a7ba742d0940d64284c62fbffc64f1399e504a1e82386561f1cda57000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002c96f5f2fc62759842b8811deb269d470af478b41424b71b57a63ef84995063e1b7c73fb96025c195131e8a4871bfd4c75f7f9a7b0fb405004e45dc3be552507f27e84ce35f560f49d7268d6632289c95069286f68a36de0c2bc8f8fa502cf532fbbee671ebec40b84e1a485300d2d5333bde69278cc5a31721038d1571a5284021a41a0fb85f614849b87237420bec97da85cec214c3b022409c469eccc193c48030c514904e3eb1fba250583b5177a4c2209c15cde1b53b4ad5d50322484deeb850b0bda7d206e791d0c9a3dbd844c47d3b3dfbd9d83e42a3f34e563c29d4af18f4b2ecb8fbdc094565c6697b7b0ccb2042068f8dd25d9c25b509dc9c1b8f583cf6502058c14687338356e76bdde17d7723144e73ee99ad127ad0c63e07a5dcbfa47e5021ee4b83ed808722df2eaaa30e5dfef12cea21296db26f8f3443a4937d668790102233d467a209d43f956ed927cb2ae881dca203a3ec6b7e5396f3649a889c7a84b0312aacf708be4a59670965186f19c552536764aa70d3109bd634511a751b3f0ac0203bfaff9af00b0d9816044c66a5f14dcf10228db14d611c91e7b3ad9038f0e6aa2a41b7a70fb5e62261180c33d9c7a0103df99ea4a868e430cc7afd805c2220f7b7edb867426fe060da4a6a61429d6763c54ab40296752a667911c465cc6d9eaf2292ba973715795ec0757b41b43e5a7b471eadb4d4a59c909d92936348b33c12b68964309db2b4c0654e68570bfb55e42b349a461123bfdfebea511b4e323a429376a24d37d61130370000d0b4b763efd8e7b3118cbd7817ffded8913cf77c7fa23bcfe4aaa80ddd347fdc9340ec162442b45bbf3448136c926debc81e3865065e87c11acf8c17c1b82396a1f75f0e2e216b5669777713a6f96c4509ee4fe46af19111f0e29787c5e5c8f37a92e4a878ce30d0daebef31ecb9fb01836dc9b3866ecaad0e8c1bb1c8f947254cb0a5498d2544f88de8c13c46f54465db8d5ea1796b83ee5434d412cb024ee562fe2a90e84377e4f34aa94ad7a98b2f5a698ea4a88c09ce68be2624a2ac9de9c09cea50356d9d270360c4cb291120f708ea1f2079c2732ab60ac36adcb97febd8d4378543336f0f2027eb0a8c34c274a01c996fc0d782abe95627d4e5ae6e61f3d56e50f3d18bf8ffa083a67a3ef188ae02a473bb5cc5ba679a7f9833f9b9793512fd0172f5db65a10d420a5c0bcd1f7dedf5178dcf6d84fba0214c1d5a4377617d98e31a18eaade04ddb868f9b58f1bd6a9bddb804c7d913ad4d0e70fbed68722d9250c1a12ba6c88540337165de435eebcb3b5a3866dd5cbe44b638702fe547d20ef2ad95239c85e829b0754f71a6722ee17422de2173425ca33dd0c1d2efaf9f59075e35148bfd05938c86f89b1193b35348078d97384789a5718b23aa4129e69f1ec86a76bc59d512f37fa16382971c7a3ecea4dcbd5a926e00565c7bd73c256985212c748bf33fd7f104a675909e43e99e4a485442d56d5f00b7a5b5e387a8960573b33b74e520d84ba641cd2f2dcf82dd39605d7a8ebbf01ec0e7dc4e28723e846aab9c65e3b3ac5452f303eae1549b65b6a99710b2f7ca64532f137eb631bc8f695c37341489200e189fd3af02db94d7b746bef89a3df23b9d22607daba3420d957814bdb632a1017a9612a143ed8abcdd41c0339aa60cefbcfb88fcc7f2e0aa4f56d0cda9683c0027ff110645d432444ebc3fc034efb9574422a226fe113ed1c2807a445e1ef55e4138ba12db432ecea02d5ce7b622c907989e7073b592e22a75b00181478f9a4c96469e0d610b89c6e515d86ba62d70e9ca9b66958e722fc56e1ac1217559d65f79418f5c07cb036c7ecce8ee09a987fc8132c2c06e64d34089cf3a5beaf506b42dd964be1239258f52d684fc6a4c04f42c3c94988e0b86cf35d9de7b0dbf5e17e9512102ba1137c8302f9cf8031c57084976293e29556e1a39ded12f08e54fb713619c5a6169f8f6302adbd4e22566b0fb26d191730cb33940112f4b5e33fbe2aa84a32ca1e7207cca876a8e3a70a0ec340edf49211523cb05d65a4c54ed544315d265690565ccb9e701659183d090df3ec02fa126b3b7fcf54266f2ecf0df1342b9fef554221bfb3b96d94b71d7e07a799a96f793500f91c1cdfdcba02842208312f1b07435b280f3d4c20bac4e9be6bf6a37f9c9758254ab895a36a7dc37d799ccdbc14c29981b6dc405bf94f8322c1d646ce75a74707ea7bba9282415e51e153db997da721fe57f835d40a498ec2e860d6a71f79026b25dfdb00000000000000008d652a01000000002cd5f6a9483d18ac91e2053553dc2e4d7c62df545572523db3bb327f572fee4cabd07ffdf9596c6abb558a163ab8b5706af308eb390dc9c69fd3f2ae1a88d355518f58db9b7b0db9e6c5aa57184e6d3c7a6abbd077f0a8fb01f25502db15d71500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000234133c8fd12dd7095c23850bca3afac86e077af8eac230ebce11e547895eb513c56d1aac5eb2318f950daa32665f0e5d9373117c8153da23e08494814cc6d3f740bd5ad166d769386932df4ac11b38eda6eb6c2f87a679e2429b9fa088602dddc7f28f793242cbef41ac0a4bd3028eba809b5d6737591e9133c35f4190a95cd022bfef8965c734c9d08b8100c4fb396501e0ab083b603a18f4752de095b0dec4d020a2de9f382564421045661b7ab331ce292228f2f20ce72225699a45bf9536e440b0ac87e20a676bf9fdd3c7801fc87a8649993f98b452914dd1de7f9dffd7b069b0393170dccb833e680751039fdb230b8625a369663ff19e542aa3937307a51ad022c1062ef9c76ee29394fb84cc46ff068148ae545281e99c4e91f390df8b7eb440300ae56b3ea848d2302b968ff02c79be75eb79a9771ab6c105e2a96fddaef15f9021d28fa6d69d2601e23c3488ebf6c9cad61e8d99a387ebb81efc5d8f6c45cdf2c02015c5d5f4d6ba2a9e86f9ff4614aec9f55c8e59e250f0f7a560eb35930437f73032617e43788014ac1e19ceab5ec8f1e749300ff73b3cf19f583d3c3ec9f0e5e419dc1944e6eb6670beaa4c4707c9a4fc912215fac4016ed8c9c5b6eb66259a12fc8b4c745c084b247431b4ec1c342d5d900c2ede82d7eaa68a659ec7a21c1737a95b641f2a1dcbe4f3d119854350f9870b4efa801db52e3c72a24272dc91b6978d58739374c5ae0ac66bd4d9e15c9b66853fb706dd016cab802dc6baa379ae717cb397d53e9b5dc20401b785fd362e9675558fa5eb48b4543978acf8f6e3152e3b1f55342b6a66a3b4480777a72a5f69332d306b8d016315112412564065b06264c951e8f36f796d4b76092f7cd6411e2851fc57c6e919484adb824ad47168a2436ac9c801740b4a8674c375cf6035cf9e8164a4b187a1c52422cbfa051ccb28350137bb9ce0d685c42e06be256dc3d302b7d61013ad03a71d77c43c1050d6062630905d261a5f2b199755ffd5df03770fc2ea38e7cc3e8bf2116ff72688e699b65189f61a44827216ceef998f9732413ce09f132648f3a338e7ce7ccb630ebc69bfaf0a18a4cbec2c0a84832569177414f6b1515c824b7ffefceba28dcdf9efb5e4d99f43b09131fd1a3a7d838f32656c294b3282750ecce1bd51451d588f641825f68df25642558aaab76b577ffb63bdefa4aea9537636fec9c0bed441cb61de618670c27764e52594d7e832eaa7d04b6c5ec57d72f7dd81eafa2feea78cbeec0886fc85a2f94dd4eb947728f585b726247f92313a6811b6e5cb21d4ca96cfc752bf3259bee98e97cf2404eebaccad53024fe2fc01eb4d844614d213e019a8d1a980ef69a30438d1ad1ac57b2a230085cc901c5fe1cdc1688819594af5c96b0f1745f1599a341bfcdae3f2a703f9ee4d033b8691b05a1a5f271aabbf53ea07c2e0407af579b0dd8d46f2f1d7092281bc19e235283e4b438dbbd398dd2c118c1bcd045cb698203aab95faf1315715bc084dd7ed0b773afe404bb222cdec8dd52f3be63a0a0cf20a95602b964601da576f3b65e4e61a50cbf6fa3c75447e971b13a1c79f028c713fb59197cc98fe559d3f72ef5aa19498e3a9f88bbb7eaac67c6a37017300d8d8b4e251828aa9e6aea80f0b5bc30a4eab370083e9b8cdab23b79cf9a25d030a48f31c20eb574139c0d10cb867a89ea171993c17389a4bb9ae1b151efdb4f0e67113505708848d99d6a2265af17e07dcc9452cb16061c74d8a573810675f820f6c94c2f9825b25f62e862ccca8004af7f1df776788f12c6e5fa9a5398551d5561ec8684962f3ef3ec8042ffdc5b50bba84b823959e4dcf952cb2115a387414dc3412af87a42e6e9fba9c92545c6b1d7bf83ccc3e8786945f62fb1210924fa2198172a12e1dc782cf0140e4c0d4d2db65c821fa4a29e035424981ac489fe7a7bbbdb6fc3374027ff1cc073499c824eff7f8e0eb29d680f6024b1e0169e87742e895396d703fdd01e046f4e9d7c0b4865b57a1b1377b0c1f1aa373bf0602e623d744bae475d893a5d603db64453d15ddbe2abd9ca27b4fa5a95ae87a4e70a7724894555f2145c8c83afa5a53ee750eb458ba06ce67e2a52db9137ce231f8ad7f3f907ca936d7c052fd0a3fbe95acdc57ef2fdf42641a5d73ab9aeeb1b713b96be38d73e5ab81bca2897b4a2ecd1af7fe15cb692194aa665d11859445decb63aae2a8b00567f27ab0b653eb6cff08f9ade49b8c98cd2fbb0f014299f179fb62ef7e9a0fc45af2450a24504807d5cc32bcde02d82c2527b5ce094e5e821744549bbb9fcd8c0bfb7b45031332146b8afb927a3ecc742b3cfafb437b6df47e1658efa614352ee24f8f4b5dfc5cff29abdae18be451ec81d266030eb7c62542362fe576c2a442b2c954283ae1b51d907", "00ac", 3, 186612117, "f0e8d52bd7e6330a2923e97ce8e33c815d90acf03358a2d7bd7a092a3a1c1d56"], - ["6c88ee1402d257547566ad4ee4310b85d079b4b6f1da8e7efdf65764b10e2eee1ce0250e9b020000000952636aac6a51635352ffffffffa1a53cdadc18e2ad71a1e81417f55eaa12ca18bbb93365a313ce989bff7aac3d010000000765630063006aac3808196d04c1333e03000000000265003e9abe050000000002525285f31a010000000005ac00535163dc08f10300000000046553535200000000020000000000000000586f5a0200000000e1e5f9e35e83a773e7d4a47228d3d02c228e85aba23c39062f256274e60bd501981134bee2128d405f53c871e2ef1884821757819f8d21693e46f0a9a8bd70099ed000ace9c34250e05d127086c2da126300cb4aeb691726a651451ba02b1054000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007a4fd2a20e2070c47d513c98c107db81e0618e84e35842fd87712abc1039aa21cd11b0c10e04013ead56614cede560a67c53f2160dcf10a5090516354ef1d5470afd55f45de81db4a44ed2a6a4016a521e85e88344c59e996556ef243366d151f1d63f03f31742d83e784e9abf503bf04549390b65c7ed7b83a1626298eb3d1302082619ce99e9871b0377eca8bbb84575ef3320cf76ff4514146b480a9cac2740022274e6297e3b3644dc967279fdd30a19890e09f5d83b4ba9ce67f184cf1e15d90b29b77b31bafefbb9ec6e2ef9a3aedf866beef71eae4d510cdaaea5551a6590741517f44a70f21d6622625e81ed82345e2e521e9cf3d580a5811cc2edd86b9b2e0329280769f0fcb87df6f41d34b1877b75176dc40303e9d0b1e05f17fc595f7cce030691411d28bc444b47c7bc8ef037096c2583f431f1472e85a7a06947ffd955a50206e5036152efb2f527fa2ab53461f25b9ecb0d120154d0d0a59529e44b2cf3e70306ea533218a1b7bac534926dca28c82e4599c5931929cbf0faa5da65b4049bc902168aa686921c1182287c619aec53dde29eec03f93c86a468c62d219df6d5f49b561aa29968412b92f35216355afd38ebbd531035347015edc34cb35cc61520b41d1c90090e6555ddc40394b2acbca49453467507a63b12108349dc0ed62df1b0f301c37424dc8a81296d48dc1aa898b0660d3575350588e25ffa0279f3379715a3202faf7d7e3d1fdbf858a380f46d815604293918d6994ce998057dc1bd7ed1e6fc8351944669bd34a6f33a921239fa5fccb0a279e3294499f30493a75ec58e6e74bce78ea9710f2fb841d88305643596d00d08c4b85834c68a1653c57f94e8fb8c8b538356cdd8c311b44d184a5273caff90991b1d5a218da1db9cd8d534f64d037a29ad0776a6350bf16637130808f328806b01860f997176c22e7918a459b59efe6b79577fe3e2d09ad63f423da38db8e1f09ee2cb2dcfd20b35c2fe2e40200aa05a615ba043cc57a41ecc74a088b85d2bbed097d362cc0b4a19fdf55af2d00055e275139b6f60c5aafb95d0ef571b786c73ea653bc261e7842ff618dc6483991c193e2bbe1fa5c06d198e6ea7c799ebc0e8ed34d3395b7e88e24bc0a5d28662bc9b257113b0595ca91edec0e15f2071cd77bb66cfb5e743681bdaf3a8892ada7c1c31805e830618b9d8abc07609922fb10cc2e01f4a62a95b8a982b9dc1c288a4dd5e93b5e927acb67d0b742ff5ed1fa8050328204d5930037d5a9234d964646d0fe37687643e40b5c41e02ca03580262e31e6ff02c0adad8b858f65705b393d2871f559d7a941ae1b072250b8f8b20ee5b22f067aa6f371e0fd8a6da440ffd1345656ba08b0822bf04b46fc397c770fdf8ae4ced8ee7e664127a9a6ec6b57cf8dcb74ae94cb3d957a65e09442aa7a87cfdd65e119a49622dfc4f7d8a79885cf7f66e9c3e9fb613e0ab1dc9ee9b59675e40d0d7fff097cb83c5d83a175e256ce8664c56309f5d4ee0d97418869654e57a19005abb0853907da89e9da83c6f90480fb85ec87ca44844527cc74a8f9d43f98f4c9df2f35ee2a63f18eebcb906655cf3af689d013c5354a2c6550df358d4fd8b6b1a3da1da8bdc4f430f709e8bc6034c62a8f55fe10d98ddc148a142c374e702b52517794c20027a35b6b83e8a96edd56989feef5c7073fd48ead31d7110b61ecbaacb7089d4cdfd05436ce1c162118bd8cda145b742fc0e85a59c26f4674ff35be2ea100e6c5d6328bd86371a166accbd16d339d05936037f59714132d2183e2ffc0ce2c56b13d118772f269a3a14887a64a743f71556a273afa582472b681c2262b9ab58cbef99dd55392c7a29001fd49c9eb936c75519166d6c9fd0b53586fb9bf1e6543bd4e11293e64fdbefc58519089b3b8636ac8726d7bcc8bdf523761df37568af19a67f2c46135ae54bd4468be46c84b3b4f66894e9c010646291bbeaefa909756f744a8bfc01496ae6da05aacea52ccbdc384bc7f7cf8ce8347b178b838888a200e378f758570ab156fbeff226e1de615d7e395dbdb64df31e4829bc4a3e1161ebcd78034b20f735481d46f40440ad6e9437334b8da6681ab46773636f5e0ae6f32674cf89b26b2283ca0b65399f06e1557bbe796d3c4269bfea420bd1de629ff28341bdc46f9866df813c6a3b9fcf7454973a967010797b91024b3ed9e1ece7aae233d599d245b95354410cb5b7a17027e1c6b9f018965faf5e12e6ff946c4d1121294a092b036f708841e2b781bc50d3d093442f980ecaca000000000000000043a2310400000000b8565e3af17e83f3eafc1821e71e5016f3c28313b5432ab47ab1f253fa3ae24fb5103348bc92b987166bbdedd83849617ad0993beca9aaf91fe5f6b1842af2097996149b4b05880f10cb1e0a5a7d9f4107ae240aacf4da4d456a576402991c3a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000077141519abc615aa49841f0997f1e759f6f4ad9de39fc60c42f4b6683fc084823b9b7774b098f13736331b658b547833b81a9b0841b18deeff453aa159da76c6171f74abccc0cb0182efa55468776a7ba7613a90be0cfe72313d051b0bf1c3a8a7832050e12baaf823c39321acdab268d7b648f5ef1c7c958a04b6ac6a89a36a030055d998748eb6cae46cf10019058ec0935725ffbc8c5f8921aa41bbc7a987d00321fa2419823e3b6ba17ef475181e927d57b8cadbb84c82d2145fbf86f4c5bdee0a17ea9d21fca1e8188d2c937e1f3bdc00926da3affdc525195b3286c29da62f03101d0fcfd8b612b88abaf813ff206f9b1cb0a8f915ef57c507376e58007edd2f022042790357c2a9017612776124580cfe5a9f21ba886edea1851eb4cea422f90c031f5ed76d265851de53f467e806b965b1bcfd3d06f03fc035c38e017a1cf3ac2d032bfcbd81c35d56d7c1d25fbee6e9088d8b2dc44573e8f49b746018580892bf50031866355570d950b90f35107493b70405ed35c24f323d5c58abdf263474d7d23b021e662bcfd93feb218b13528e7c6cb492947da095e8884535c4385f8708986922b1820bf2861c1ab2d4e278db70a6d2a443909730d7e373363896b1c66a8032b60544a35ac953b0d87cfc2314fd5c55411eb611499875056ea42e59db48d7eb9c0740a021197c36272d807fed6932aec18befa48feef2963ac7b0b14b3eeacabdf2e9addf191c8b92ba5f3211b8161cf387586ce1b82b58a8e394768ceeaa5306c4a787202d18f26ea8f853288c6c3b61615a656bf5cfac0423886ed6859ed01b045d512eeadb868d1daa880e36fe42ea03b61265c25c2f15f0be5ecb21f5b90162e54c52049f3953cf0eafed28ed069f722606953177ea938d483cbd122a456654521a0c9245eb144496db685ed31d897099bfeab3098900f890cd637dad15edc5a533be9b3560a2ef9e877954a3e7f5bb32511d68ab01dd4d97d08126c81ed9da1f2e6dec61a780f715fb806ef5b86c3940ea77b1da8b09f2448682ce986b8a8677a07b5d358871561b36d89b24d789e3290b9aa9e87e4aa231e8596fd31136cf09480fbb3c5cd73956e35a1c9b06095068a42ef8c7e370a4715297c615c32d73893b6b383a6556cbe9f9d9a4062c5b8172e9d57616fcb956cb424e8a2161f07648e413bfd2413142d5ce78493746c8f90eada36890657c5b9035c00460288320931664a63cc99f658d32fe6079618f6b8ce7af484d29677480d9d54857da5b67e02236d51f2cfba1588a24ed04d8363d9196baff8ae4555c346aea7fb0900844c0bbb9b54278f125c491c2f95ececa9ab455308942104e001b4fdd8b972adf83b200791dd74fa2c50039ce109f4a11a3f515781c8c88f5f0418ec078306198a857a89096b3fbb6aceb08eedbe60da75a888e9c67967e42d4fb1d3d4291156ad09540c9e4a971d6dd2a913bd6dfd43e1973f3371e194d77e69fe1e32ca9a53564bc498c1e6fef4a8ab3646b287601d3a3496866110499fabdc965944e0d5de9163bc37d53a2fb244879b1b842e8f298ad463ccd7dabecf9aa59e75ad8faa4ee73e219a9aad514302488b3dc433aefb07797b85ad1bff5e606778497c97e85f6a6dc04fd592a5d233809baf199827717e62b03a9689eb53df77ad9ec3817392ecc5cffb416450f0f994c31333e0bd11364eae447a4cd4454c2d850a54e804fb4e4b3379b56eed814025b2945cdabb382e901a007966326231ba882e880cf2a5db8ae3c33a12c0c702f724f3ceb9118b1a9dfe323181c87d28b69b929665353980fd70f765896f1729dfa5dd84bf3ddf142c85db1e5faa44bf664ab4b8e125e0d8a5409a362d631b8c9f77678df757fb2f89a4e2c86ff47dd20015e6b6bff99b2bc950d15e4b49a1c9960761b55dd6fac0e4730bf8588508724a719825c08be9b40b2dd690d55a91cd214e18e1b94f9916585ebeb4ac4c199318dbfc1c02503b316dd49c7c78735c4bcc2671f5c6a1e312e3d0ddc3ee0d182d50d0d934e9c649d4d9dd9b9f6ccb6629dbd26432f36bb23e0a0caea401c943a265f177f5e4f1043f9ed943f06de6b1820b0e9cfb651dde4b68b775c42d3195dfc3aada9b274103590c9a6995af0f5b2aa912fd85f04c06e7a7ed508dcd849b971da83568366d37ab10f44b3995d732bb36a8c8f8ea5ceddc824322db147a077923c809f794c0ce4bc315ce486243be5a4d3c0cf7c1746b65ff05f2adfdf65883a296464a4457812a4f1c1a930dce10c6103b496bbd316d541ef9bc90182fc36ad19c7b21bc39815737000f4ed21e94a75597ded0cf8e088aa3161d1b5eb90cbeb3f1c71a65bbd78791d0f0b06439c843fa8a48f3784a04be50589750d39d991e2ec1639dd5b321c3c1c35af21a6710b0c6ba8ad7e43c6065b0f", "526a", 0, -1414862612, "3af793667dcadf98fa1f2e2b8d94f60e0411aea57ccb03cedd41fe26b480985d"], - ["3ca95a6d02f8432a95bf07ee5ab11fda7c17981744eedba824264c8c944989ebbab728cd7e0000000004656a0000ffffffff33086fd4a3a13b8a9d2de07b4c0752e908a058a081e8fc15ad3d215fad5b01fd0200000000d76f082a0437d19b040000000004ac5163657fe2dc0000000000020052faa25f050000000008526aabac636a6363425a11000000000001ac0000000001e94d540500000000000000000000000057a9f4f35e0446d3bc60b6e02c4a9aa051f0e6d99f589f74fcd235d8664591a31880f03320fae5de86ae9f99643a04b8b62df6e8be3c49959d3a717af3790a33dd7a9291b708f8a2ebca0fb9e700988bb809c268fe20e44cd8b5b831a2c88c6a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c5e66f61b399612cddc698973e12016d28df01c87876c360f71acfc398ecf17d12ae1b889287bb42aecca1c5392aad32c885b41f30e4331c8e8ac2babda17f90d74fcca7992448f6e9ae1663ccca2833ecb02a15f80f72a5a9250f25d8660ec2af6e7e73da86682fe16a34370488fc6b780d139946c4691495be93d7dfa26b21022c5668399a0cd94bba355cdf8cacc6c9da6313cecb5821dff3d97d2f9a7d144d02191a29406437f2f0eb87f55de667673da2c9dd7e9c766a3e5414f4c9f5fcd4ce0a2b2abda53502b32201a5dcdd4095ebbe7614a051b1126882b26fe46f635afee510396a591b7220278d427ad806e0d8d99d9438c74aadc73db4b7d933647e9b060307bac21bacfb1eb49e184e373a23789b2f5bf6da0a2636e0b068a1b5f9e5439b032c96226acae45e454b931815873e1f5f56cf6c11b6797da68538f542a893b26c02229148076b2177b7edd66e0d6e58d50207081d673cdaff6c9032bb13d93750b302254b2c25f11bca5bdc421cbd937b68888c3fe84da0fa16e889ad47c75c0b82950328a59ee0567dd438555a5224eadc2cde172891bea5f40aad2aa1f5602c7916e50dcc683bfd5754a01338b53f2d37e41f2f0391c1bfd511cb4d97e09822f30d9f5dc613b9012895e054cb67c8ca37ecea4c5b1801662cf497626f7c0d1e8e743ede21b911ecb8415e61938629cf2f2a302a75cdcf78214d0640c25038994d2f0eb02884b6518530b117144f0c6ae3973dc29e06e983956467f11fe1d248a48a6e720081e6b9517432e9e93c456499ee84930c52a8a9506858eda9dc40529d39e65517b631c861092cc8b31cca716f1e6ac3c86def98704515f6297c3e1318ed2b62b91bc8bf1c8c2c2b794c0a6a8f57e7d2a0c011e35116f05625af5446dd5ff56dcdc5e509e0b6f249352b48e771d7e3f0b006c003f2394139386cbf7087e44f92dfce6365f301e1aa3b3b7e7c2e221f0947a24fe962f8b0e0c23a1e28c16841535ff2247a6cdccb3e698ceaab98c0ce3fbe137faad2c34bf1b26b3c32f0a1b20f3074f4fb287c9de6916cc91d23e8df10f05b2a710579fb660f2700c9eb159e1b82a1e44fb24f7db459c3034890d9b3625b8543fe313d71475b88bdebc2c0f774b7ff9c206c0a1251c57d811b9e31e2c0b0288336e0fdefe3095eea9bf319348f1374a2dc5bbbc1c837ad9d6885fda8c17720821be30ee7259c050deefe6feca0256820c1bafd17479e6e7a5de07bca4c7da7a6a105b7fe1527527dac3274afe93447c54dacfe6c64e50464d792aad788c255ad005ddd81f1524c4986bf92bdbcfb9f8c7bcdc579d5938909e3bf3b40ecc47a88264cca4cd63c25f93f7e61a5129ba525e2582c74b524c49d63820b4f4c23e08059c41f40c5491f25a00ef60082c27a2f19d2bb6a18539345a137a1cae8841ab1d5e58d2201f752df12a9b41dec3feba80e2fefd9da99bd4921f3d244dafc437083146a6ea140439c594216e5f7ad30fe52b43b6b4c63c591344ed8efab9066362196299726ac47814114808673d8a0ed9b21aeaa967dd1cdc95573a6ebde90ebcec60ee97b2de0d7e24fd1da4a799756a49828c4f078e56f0fe1125b9da9ec419c05b4a87f616e0edf8e30920540fdbc9d71dbe0edbb3f9f78b005f2acd683760f0abefc9f70c4af0483d85b66fa8e6382d829aad4a09fee0c4e92dc5134ebac4057ba227e436114f4df8241939670421ce8eb3511188cb39ae1a93ac3b148da17d2a625df8684856b005bf70baedecf34f9501c56af94b99378b4ddcc73da0dad4a551cfd003012de8671182f487478e9c5b67c735ade31749e2a32d8fa6345e0a890a0ab2bc84cff11090a8e63207b752027016b31e919fd86bf78654175183e2a8a95efb59f28de33677d293d975c7918d6a7b97b31d1fa0d15183c0287926e574313b8aead31cb5cf8392b771c9aa9c83a281a0b6848f362282dc9d9fbce30887b1f6d6b8733f41900e8627f0ffd53a15859eb573fa333b862ba5df86dda49660351e952a8f6b4d92248df5a90affe25865250a1d6edacad0d0eb29ba86f4f319283cb711b1c765326e15c0be98b395a452c1c05d77f41a1a052dabcea18c784cbaf77395410eaa585e08af7b99e941c2d9b33bb993f98f53e86c6314d2ff45594ef683111431295594be7a33f7e527b9516cdbb069f53f2d911542c176ec7aa2ed4aa1bdc4316f9dfc5eae944cbfe8d172d0d7c89c61b15c75d997a46469d0ed3c858db3240662b29a1673dcca33dc561d42de5c2763ddf0644f0d99eaa3eae4e9f82df7c5256fd416e0ee40b0c1c007bf04ff9e3edb3685157daee4feeb8730e6e3fab274d478a4cb428d896912189c37f9b402f866c92ac382ec9a4e92aaf89fd788ccdc0fbafe1039b468c8c39e4dbc19537f1e19c1b5fc39104", "5251", 0, -996058480, "3a24fb41654cb80953f14d345fe32477d61cf219d3f1a6978a6d72cf73b66b31"], - ["1a8d7e1401448ba3d245cd61a697dfbdd0306a6b6e3dc6c915447ed083a5f55e16a9b0ad8000000000055365536552d1e43fca0394aebe0500000000015290352b0300000000007a098b010000000006ab65636a51ac4b93ce39020000000000000000469b300300000000376656b641009e8e60cb7863eaf1bbde4923cdb5a8f9d34f175f72f9d351cd1b2866a64c3fbebc85c50805e9ac25f114d627d3feee33be4f919905cea6cadecb8992b5d7182f74d218d2d95d56647c5a52f2f0014156870e6a03bebfc576023f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000055a58d728b85ece19cb4d43ae9a1727b4da487b08fd769597fa334f102ba6bef52aad86f0c95d9da7a545327b6bcac456e1e6d9cd513daa65b50ec78a9004470fde11e4181c84763c14550c1b44e1ed038b67a74387ee112547981054855595586f7cb40f0c2fed50f2b3e4a5bd8e16f077b72126dd92fefe698322253e98cb40309432debc6d3f919f7d59ee4e411bdd12f21d5f93d146a08693f06378792f459032f2ff67cfe0ffe13fd6a03344a5465292fb6ce7dd5a9dca2776ced87d844b04b0b14fb67db880d1cef19981ebee87680109e72d7cd763f45ffa851e46c992b065f0870d705ca87987bc8eb446634ae563d4463d47200742e33644d3f8d2f28d1770204efa9c3bff0a93b6272ace3dcd5e6676593678f65a253c8e97020e4f64ff97602060eab75dc1ebde481b7058037022f27630a94e76e255be80b194f0c32469ce8030cd60897e6741528462ed56395fc188855dbd03462735b8506bdd556be367eeb0309d0a52fb719f0530bfea9de08cc95766c28630a04f5143d5613a59d583b101e032022b939ed71bc0e61a7480d14336fb6613c1afea725ec5dbc7cd61b01ef8abd56ad04fe51cf39c39392c5a2f41f99bfabf27776cb19e8b9230828653692a9fabea3a63a71f7baba16bfefb13c0f54730c6f8625acc87758dfabd3310bf28c7c57a98edda826259f27bac449899873c014faba24a3a8471f39985a6dbc7e76b20d60e3c6767dfca6d28f07e9e1e6db7968cf59d747e389df7c2c0ec16a56e186fb6d94c4cb0dcac917562f9beb83f0a4a0ef2dd7d5da4f48331bd21bfe9cfcf989de0c9185da9ea6abbd6765bf6809f91d970d3b6ccd764abd851dd87663322988a0782ae918f7b990d5da4935d8b8c4cd39398696b62c70b957ede530f45bb633eca4f2f7d815d5bf6eda269b5dc354dd8535d3d290f9c07d2787e74930c91f0c7c2238ec82e9aed4be68909ee30678daacdcf21ac5accb7b525831109a168e3b42e0aebe5d1823fcebc495707bd9fabf7d56580e29b67abcea2476576e5d161a7402059865c816775d06f88a92f4aea076f3af12ace29e6b96d68d746e938c2a65c894b903d7c5df5b923a8d620a4405238d4b3c86e6eeb0abdfb7519de2d9aa7c872c99e56f0275e04786eb916de5ee4261d72b68bfe61e3fc80fbf519927f4ac59828f1625df73b0ea92645c852822cebc781888b86ef28950ef1adeeaf29f7dde0489f8fa0d7de9c91cc9d3ae1971754e59e639f1c27d13810b5fa365de562e8e915da05f3be417f8ede21d642941f1644d47e0fe308c00044d4c9585d93e52083c83ea53c6aa56e47ec8149f14bed3ef97a645077626bd34e922b3f18698bb6e78122bc89591535729d59c54a17140fe5691bc2c292d31b607c053bd40f6f496e95d0c9a3c147945eca827d067e8d0052e13431d9f63527c15a200379e42eba647166e69c096877eb9d1b2c98b3f84be6c2da70b0a040ff1c77855fb952e8bae6b946c6ac580473586833236d0665ee3e2fa49aea44356cffeea34319c68368e2dbd802761d521e3115b225e4655be4f95ba4e4b368f6068ebcac7bec92868bd3e4e64738707d90ac3ba359e91e2644e87035b158fbc67373b79391962e1be8b68c29ec4965af29da47a28cdfcb66f50f0ad3c244e7c4a57f689b4e440c162a2f2643360af631e2cbe304660be47cd73e08a2c7b82fff4a4163f18c1d86896fcb86806f6466de2f3f71ff0be76b96d10fdbad395421354836bb057fdc5228f9c4d89c7bd955f942b2a4f581430264d1d03f10790a296eae5461e7f79f5f7acc930e6ed9824933c64ddb98cb7c9c16bd7f1c0d329fd511d0cef0292f351e87fc74788f62e4f44e5f2c2e5497f7ea10dd5d5673fa04bcc45e452d3c5994b6a373a59dc83f069138470adb82534496c4701affb381b4611f43039b9be6980005950546a635775a75d5d7e77eccfce284a9acac02aa4ad7ba21287311fc9c6966c270a2823e7a6179d47780284cbd179b7362b4250cc7024a32e0eabc143232dac75c39b9d86acd47370aeb56fd43f33ac4a4c5d28a795ca854d99ec540f568b7e965c8f799715725a26fb0767f8585535f93179a5aea3bbd9de54a7d76e08d929b796456420b3ced3d119186fadfd9a1b68e8f789b48edcf659e341b2d5f619a67d70aa44bcc3719ebaa454b581c24ce8500ed46b4670d1a2775ec078235464d86c949d8bc174b25036b7c8e8d4c4cf03bb7280b0260b350ca017153a1cfa544e3b0893e84795d79c91ad2ddc9a230454f46daa0100000000000000000000000092284c2684eec78f7744b520078494438d73381f0a36668d84cb4cdf595b01d259cf0a91455309dd0c9139076bfc775e2880aabe70defa0317e3a1f4170cb22548352555e47bbf0c1824df8e8dbb43aa38a5a65e9ce3f8cef4fc5f423d98d7c300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c657d8d763b1894fe063e16b91edf90c6987b03e7c08e68286f842dc4fa7c8dd84c093d3d8def9abd5390210427b2431d0ca2ef6c0fa454ed0269e0ffb21987472efc2c6779d4cdb8d82e17307abdbca8c1b7c1c05af1fee40058a6361dfd76c9d64b786643aaa6c3a705f29eb6a051ffc48026725eb929d0c3164bb565ff7ec032bcb9db05179f1b3c894a47e4b6f1d20e132e93c19f0e9c278e23ca91ead475c0319b321103458a599c122c6373dbff98f64a58a17ab307311408fa68e64813a370b15dc71945172f6c80baa335bf73db5486109956f1e691ac1f5a685bd9bf9a4570045f70bba3f7b33173c585bfd19ea133e8e54c76924e783538ee6d7440e48cc022613ebca87cbbe509598f4911c754ddbf33ef96f8725f2d3aecc0177a955bf5c0304d820842e10584654a70a91873914a5ebdd70c8d574ebef61365ada71a8ae05032e3194b9c19d1862079d8630bd820289041c792fd04ce9a068913009cc67306b020793d32cbd048dd544c1aa9ee2147bbca6eb920dab578ba57d95d90706c6a38e032357c66bc0bae220c19a95f4a9ff8c01d5d559d1ef6a53cf96063c346118816527ddcbef53d34abf71324677c519c2c798851f965167917a2558d6f4af4bd900d05870702c9adc41e24ec4ec2a8d63e2f9ffc6d3cf350e4bde10d03868fe29de3fb402656040a839637dfa8205b996543fbb7038d1d37209f89844b2dd98bf8f67a37a29f35ee9711aa006142578d6444d19616f2b741392bd6397c94d2265de44bbfcb6308d3ef04057ada134e701bbb49c67032cacf2d33b32f1ec94c11997c3e1a57b935705a466053e0f71793da2eba9121b10300759c66c239a77af2c8ce46ad4cca19c44058d7f130dc6dc9612b3a4dbf91d01b0a95d2ff8befdeab4d0be4ec2d7ab90b4999093675cf7651d5bff5fdc9c7b04b33a23a72669f30ef2a278622f811f84b131f8b4141b77272bd7995bd3772dcf2c5d7667658cd74786ba63286bf6ada3161f58830066bf04fe75327c81a94a22c05a6b5b5a72ca4cdfc98e382db937489fb2308a4131d5d79a6ab4aaa0c0840be5fc0b8e936177caab45dcee29ee81b67f11f1c35dc5f3d6da566b14b32968c4b8ab0c49725e5f6f995869fbf3c5fa7823a0f55a144834f4c5973f859edaf2539eca68f107111e181902d121c7621ad21bc1c515033d62204b66b9718f2d39588ca92cddd72d628d007ac0eb63a9856f1d71322bbc6135c9146c56a86b93371da8af58ea6bb5867271ac5e2371c084ebb2cbf629112340a7e172d054cdc06c862ac1ef78ecc5bfa128c8d3714d4b4973a1768179915c5c76b08accf2cac83058aaee4fcbfe8351a51f03b0d2b6a2051f125b59a138c4d59bb3432f815d85ae5e86ff47d758c0aa4cb2cd1bacf1ad0443ad76a2c446705468a077eaa90f1457f7d5568408d954ea995345d553a10f78a5877bd33713ad4fa065825a5af6c4dd7578a54cc5a1a8d52f163105eb9deaef9f45c521659d63a00e7982a57fdfd46e98a7a33d2a4a846238000950f0c2c2335389dd6461438fbd18cce85164e6668e5281d1fb5d704999aa4b895dc1b6b1e15f7113ebde33c77230671ab3e9ccefbb4665fe6f75396f304104e5573f975dee888c94cb3aa66ea7400cece9b2e4ccb5bdc9acc5e7099f6dab63c624727cc245f77bf115fda2389628e887b556d0ccc82db76f57eb9646831c9d883642031b9a9b1366700db8bdeda9857f9f44fc4ed66c75626f41552629c4f78b839df719b32a3b318766c4dd4e8dbfeb5f90b2260d9d208865711261511528ae3112bf8a33f937d4168ef066e6e00f1b37f03037244205f4281d4e47fe7cf64f83325f057fa9396185fb5f4254f790569de20d8eb2264696723c199e1909be5bc2088001cc73ed7846263561c6b707cc8a61810921b6605990c145d63f6299eb8dacd28c6a668ab910c186c7b05ccbcfbdeea220b39f758d0688acf94b4c186514bf2e47ed95d4e28200743b6e5e0a96919454ca6da032136771c9ff77cf78cbbef5c3ce8f881747d0cf0840678af94cce317f25d1c94177536476179908783ce9ced6aa5370ea6e9146ab8f96f77241558cba57ee25d21a7723b2b9f0a59c1a99cf9a3797673eea2151d9bb5e8e1d60cf9960f1971e831ca840cbe19868cfe7557d8ee692b1f93b0664af0c04fbbe8423f63f434b4a858b09468867cedaf56cdd5fcff0d03278140fa384e530a9d6c813889652872df5e42c11777df7793ceec8db5443df1e6c6c0b0f9bdf3374cbc054a4d5fd400daa1c030eb105a93ee365e3c31d0f86c16172716788e88a786e8efc4bdf66ccadc40c444405b448646b8709606078a507866632a43f19d2cbcb1828ba7bc224010f56832c96f9193257d46a2cd5e088a5dfabff73ad5688153a2beb04", "6a", 0, -511879882, "e5202e5638f254d1b7ceec49f4f1c6d57f53a49460e93ffda5b003f3359320b5"], - ["938d360f047bf0fc9a8c43de489a76647b8ea4b6330e60765bed346f2299ecf76e9e4bbb0b0100000000ffffffff9756a836d5dc2148e71fe4f668f6c99e9b9661c48d5d3ec91202dbf33cc4db990300000005ab5265636affffffff9042ddb7980d7d647392cddad9ab0dcf554a830724fcfb5974cf65c3a46a2cf70100000008635263630053526affffffff8765ac57fa4390b0a0782feac8bc6ab9fcbd7c8f19853b146e6e6ef806eb88bf02000000056353acac65ffffffff04f95375040000000005630000ac6a50c47c0300000000066a000000ab53552f45020000000005abab53ababd63aac0400000000046552006a5f22fb9e00", "525351ac", 0, -1493545131, "1716e3c8c5a7054c89ecf1075bb253315812e6961c49eead1097094ae874b4e9"], - ["5a4a0491012189274b61ea652441ed43ac2ce6a30e931c24da5e35a4d1f9511282d649985e0000000006515251ac6351d8fb126d018ac7a5030000000007006a635365536a9291e645", "510000ac6a006a", 0, 553396675, "5916d3804ccf20ac520d3b330801dc095ed3f39f4748cfb715b78fc3771e3262"], - ["e75601be04c08fd0453d9425ae549e6d5a3c1ea51bd0899bbe61b9553f98baeb28fa3ead290100000000fffffffff11f1ec1e4e673218323ed12824a0bd1bdadee2f01e663cd5ef1fbf9ed0ef8060100000002ab51ffffffff5115df8edd11b50348abf556d3d5b90da50d4ab51fc8a5eedfad91392c74ce450200000000ffffffff16f2d298eae8f67161cfe0ab898e75814d54f9ad953b0c136d1e0c419b8d0a2d0300000009ac52535253006a53acffffffff040fc3410300000000066a6a00ac65ac6236e800000000000551535100526dd0c10100000000056352006353c8a9e90400000000036551ac00000000", "63", 2, -1316286187, "5be63e47764a59805ac39f0718a82477bf8b620f4b2bbb5ef3913436f4577da6"], - ["8355e829039dc8e259febf68b85cff8431155baa7b8c9d91382e9da6e5d5d56b218914599e0000000008ac51ab6353ab51acffffffff4504add04343e67ab5891c2fcbe2fd2725eeb42a591b95e72ecb4af63a2af14b0300000000ffffffff6a3ab9c8686705023b123d95b44562bc961e0ab02ea91d92f54819186edec8fd000000000251abffffffff03036242030000000000638ceb05000000000863ac5365635351ac5874f50200000000000000000001000000000000000031f70404000000007d109a6753a11f0108c8dc11c584ef784bc0d2ebe4bb5fb5d3f71e841280e976eceb8bdc6327c201eefaa6f2ec2a2740e493af0eafc47afbacec7490921aa3e39bd6e34b74ef25349c857fa4876be8aa698998ee2dad3ee7131c474beeda394500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ebb0a23ef75aaaad7030105452d7c98c3d62b98c79fb328cb69fa52caff6dd0dd1243ea2b35340620da3010c93a1fa1a5cefce4db0ec1925bff3e9e2f0a6ba7d92d335821ddece12c2a9a7a09187549432761c80142e18df00577d1fdfdb32134447f98587339932ac78dfe85ad0d0b8d8c378a2ec9e01d10fc0362036e96ab1021da595cef654480e06ed63dc67b2b821245c2ecd371fddbe945997e8d2bb3168022774c4555a2d7a6c08625206668903dd898c4b951c3c07ddec7d20b4f29de2ee0a22393c1971ee0ce1656253a771a52b54afe61d73b451cb7675728ecc9c2f8cf31779a6dd6d966e64a57756b1ddc187417ae55f45b68a80fe20b8877471177b7b03104c1ae3f56dedbf316bea7b5dc56d242a5fc242b28d421ccf90b9d595beaf52031a4f6b41f8d1986afce5dd772419fefc15a60314fc3489b1970dfddaa13802400220ec12b2af487b5fb7b5712eb8450c46864dc0bbac563cf5d254aec41fb67730020d0ca24178edcf28b39678da30540d8c612be2e649a78147b03f267ea2139a73020d35caaead85181bf128c92e481fb025999f7f1617cb320a9db02f9071f423f8bbc54cef3e9f6ee9a169a1c22c265a9f46b685b8ee1300ab5f49623b967a8be8c683d751113e13ed2a51ff8c72b1360f4c6958fc51c1dc540d9ade01c1f9f925af277e1bea6e6acacae4bec6b08064ee79e615ac0306c9ab83b2728fd493d571e250a230ec749e1283911a00aada50cc57227ad4d1897d37b0baf573ac1919e322a28bfd5213f8d5812ab489d5bf17e1651eaff77444afd4fc2e0fa369a3346c7dc7cc26be1ffbcc0c87f5a48c264f422f07d07c1a3ee678f9c3cae3df58fb2ea5b5b68414a91e24afdf28fb7d73440faa20c7d83b41ae205cebd7fd87faa83cfa21942de3af9c54914459039de81b9ec230fe7deb4d097ef6c66ce590a724352a67822d81067a129aebeb3ef903a6cd5aab9998c379a9fcaf43f09360393dd37ee4e38bf0fb747ebc15239702219df33107e8ccf35c8be857d5b1b45166b0e43a4f39eb3dc9f79d25679ef12f4856a51eaf18f78bab3b427c2cd9da2ff37050db49108c42b6398e671aba41e78f1555b837bbef5a4f681ce293228cbd1aa62bcb2da6eca52477443d72369c7e80c28e4c52d96410adb73c9b7b40949c02be378aa25e0934d91f2ec7c90a856cf17b2871972a51ff862cb79a88c5a5ed5b03fd662da5e169ef63f002c5831504c7ae8a39e10adad3ebed176651ceb0b0f9cf3aee24d1a82db733a20e81b1a24f4938e6cba0e3c2205b4dcd20761b299e69e659b7697edb1bd911509d49985476665b05f888ac991dafeda025aad0707627be0387a7acebe99d8d3eb749d5cb6271dd6652290cd66683c96e2fd5365b78d4ce005b604fff3fd833986f603eebf2f0a2ecc94d26aa1300292773a2d9b20c50a7ee9d9ab4e6e4bf51e31009a6136581dfeb739e511c010485c387bb2fb34b5696b7f16953c238c066ff1c5f262ae70ca6d85578e71524f0fe4875ee4bab1e0cd0fd662c8914a503994f3a8f20ee20770fb76f2137b1eee60daa62ea71c31f438cbb1fa03c42d0eb213c9b5e7af6fa88559170ee921c5bf88c1d1aa1d2083c48d15980f54983591f6c74cc70368f412782989b5a1fc3a289a87f4df0e88f3c53994bd6a77096b84c455c2a4676fa0546073947983bb3ca48859b8845b98c580111dca06ee47a466651433634a3b5ab94ec55aef721db6d49680776dd001b4f685f14feb1280eafad571e0e986bc5fc8f69b562b7bc5e1b5488f9c7e2bbf1d2831f42cd80b6a91d4c36696135562ad445c2b188c22a7f850946ca66a8d737854fffd84bdce4e98939b7c8cf06e8a128c864b1d41b34b5fb5b5323d7384a5c8f738c4cffb42ad3c7bf7db04fa6d741097f483e7d7a65909c1fb18802a8169f684bf5e8ef2c546f82d4361e254d31a5d7ac3e4ca5860451ba19e2557c1bbc2f4a347467b32eef24253aced18bd4ae6ba55581470636c805cdf74824f6f2ac115fccb0d780f8242415470099a6c4bf9f9a4a099a77aad77f8ca1d97e1dfbb943cea6886253e5440f2702a52aa7e0936391cb467916fb03d3fea0dadd2bb93dde7397b6d3d30f93c72505bb4b58d503dab49e7b3efc3026c6ffc76a135647912d872b1ad70c5eee63fc0669515a8d69e873a110e98874f99bcd63e40c14ca0193fcc3b243541cdad8bc3d48aee6c5a67b854d417690a966c45f8a2b45fe19becdcca2a2c403fffb7cea41d30d0a2c546d8bce1854aa6fc10e81a9e7dad8e1d3f2dd749f93c5f97b2edc2e3a0f7709d18d85d86383c3452f8d10deb0b97784f15722341f9627202b1c92cf7644bc9be98404f52fbc91372f14d4778fae28d17aaea6ae52d61df7e5e1f846f84df6363bc076dc101a5b0c", "6a52ac", 2, -430810237, "997a13abe0950ec34d4542cb9e15f9f01aa6dbad3939c9970fa4271ae6bfacce"], - ["ec901df603b4902b579da5f6581082be2dbfc2df8ccab4d24109e30e81fb7b5da395ef1ec202000000026a53ffffffffd8ca09bd6124b57a122484eee81504d1a76cf293b7fbe0800df0a82dd5ea81ea0200000005abac516363a3832b3e8aaed34ddbf9809985c2cb15b345ee02a7e95f7f2d9c2eabf3295df38d0f3d960000000007636365ab5200abffffffff0234328e02000000000024ac8b0100000000036a000000000000", "6552acac", 0, 450715887, "489dfdd063ceae8705b612d8964571944472a3b3cb630fff143f77438534f978"], - ["3e643690034ce1d533333f2088999104d45d2bc2f2c6aab1a4439fb647e47bb54d219fbd71000000000252abe7466df6603cca03345785de77913161b7fd49f2f575e4137dbee94029ff7ff3744b222303000000026aab1946b07ac511e0ba02cb348c05c51583e9ddd4953bd8a16f1f6701392375d8804f8bf72e000000000100ffffffff02d06ce303000000000265ab52c53d0000000000096363535253526500ac00000000", "656a515153", 0, -2059040994, "a6c9f7c8ecfe45101c6d5d07c4eedfce8ae6c0e0ab33c22c0a192d6887be6142"], - ["1ba2d5b901cabc5b109893102862294f5c1a5c913e46cb090dde06c393a71b4d8eaa1e58250200000003536aacffffffff0112e232020000000009ab535263ab636aab5200000000", "536353ac650065", 0, -1595492331, "6b24b6f05c114b3fe35f5743dc8eac4c391c5aefca24f550ddbc709c240cb21c"], - ["8b705149024141121cf42a3d68f078e91b7e5e30ab50bbcb5a3311e634aa15175526994abc03000000025151ffffffffed920c4bb906909481762dd168027e3ecd94d1a0cc2cfa09b472e23beef43fb70300000001511746e4b801ce40d40400000000070065006a516563e3569dae00", "006351", 0, -1842010233, "2922fc423915af0985b5e6a0da7f85d84aaf9d5ffa82301bd9af2ff126839b7d"], - ["c4834c2c01ad2bbf6a65c9d4c008fa64ab8045f99f84b89af7497ff89072047b6de7a50a9f02000000020000f3dc7018025139c50500000000026553a243c20200000000015100000000017b5323000000000000000000000000003b3815bb8b45066a14a74817063db54cd47226faabc9dd5235603cbc8bb804ee1dfb8159415b38c770ff0ad6525dde3f4c1332e60ea481be1b232d9c17affd5271d6f028aadf58b5c8dec4f6496cd28d69c66ffd9b87bc2c35bd20cd6c91fe9e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002aa7cd7982ac2b396279650fa04b9db535ea845a24a9c95e65a95fa877bccaef04b73b72a71fcb213df953b3a491e38b7559837e9a94e5ba55ee7c45c41118350eca2c1e9527322dca8535de51804ccd2b8055f542267e8761824540b829002803d96e48c26f1c568139fa5c0ef1e64cd2fc9376fd641e2319c3f8343bd3758e022cb5b268a5172265fb3ba28cf565ecff8e3eaee37c28e8662f7bcd2b5ccb065c0227e9ad4c6bf6612fe57bfa147d6835f905ae8d083308004b74d51b3f2775b5700b16a06793b4ec39d5824501f0cb3ddb51063698ec41b34c83eb282cdb65c625b922dae72851987c91aecce2dd893428b3f790ea04b464c1305a216363222c1af3021c239ad75d4c64b8f8fbb5498186f2f6ffeec91c173bea69dcf4916d4a8c67f3022f9162557ae4b17562c4bf9e0b68273612d8680e0eebd6d4a0e002793078aaae0308fe52a9b549dd9f31021cb6b6ae4e200f2ea0f133fcf229395a313dffd4ef950213d29ba0407b2d1e4a268474fc7513bcaaf2ce3a3caeb9e142ee2943a109234e0314b49891035b19d135e0879f91862c656a92f8d8b5ff4830228ec80181dafb0a085963dca5fc3aa171f9145188cf104404683e09fe7cdc6544c4e00f16c7c48a8f4012a690c438357ee9d5eed6fa6410fcf01eae9fd0e420dfc5e5c094f802e6ffe016fcb32c805d48b1059de10e6493ad49acc1f6ca1f090179e5f9045039a8b7ebe57bdf32d33c3edebe6048de8b34aae8848dbce3f7f1bdbd9353d87e7aefe1105a392d7f0f18684cbe744902d0a79f81dcbef86d557b7b268809db11f6f5b3b8431d50e6d75743df49703d807f6a98e92b3ed28955b7c288c3adf8fd008f5365c912f1274d314bf299041e84282768b165138bd833445a1136fc9a550ed84c95bc624070115cf347e9c50bfda6c685c5eff59296c575dc28d1d7033fbf9a738661b73794348b6684cff4bd953b17f7dd460b91a348bdb456063f0332e00d226759183b681169fee93be59a51feec7b569b0fdd523cca26bbec795a5a31e5af46285fde56ad88b5b0b2c1d3e63e0a63b3d0421660219bb5d0252f54f1f5d3ce858817e19c7721a971db19ef599711e7d95e35765fcd628c92e69210fce79eea7bc036506bef0b75f5e25a135df859519a4482313e5e5cea83d34fc007e003a3deaa9eaf353f4a55dd41d662e3c6a8d152610db0be23ed6578dc748e28bbec24c8f315af39d7b7dabe982b072f664607cb1bdf8c18d935681c11a568c8bfc03c172378279c47b6f933306cde3ac9fb4eada0886f413c7dce3ef6e2fe0d93eb20fc0d16712c37365a9090b6eeee3a6e3f7b251734df997ebc9567725691d59f20c0481b88077e26290199d82da9e6cfd558a064894f73bff9f00247139aaf86587783ba7176b716a28f1dbc0f224ad7ff0c01a5cb957f4ea60dcf184d9b612981028b9dcb79c901fec9d46a3d940ededd89fa3275ac13ce174860eef8d0fcfdd14f3af8f9b3d7043107e10022818fa97709241a92dc2844dbdbaecfd28d1ca8521821d8196c87d58f1a43177fd35e59d58347fe628cbfb87a1a90d49e3ef721683bd01a8382bbf8a0d44bcb132970bc49976ba78f58d659793b7b8bb124fb4e451f963052acf62b1beea278c1d84f86c66dc4f39c6b5136055d86ad96ef06a1da94c51a904e316d2077b4d509cdf433456284c2496aa664734413026e4c5fc5e773d738912230d106c9865bfd4ae001c8af37a614c13813c25355d5470356355af37f5c8202564f21582c480b07065d0d6ee0c1c2a737bd577c97634573d31b0c910c0f23385f1997361d40a1ef4536e38469416323f17d8a33da5480a35ccaa172bbd515f1c4e3cedb1dfa56c5786da6ed52b772bb0bfa56fc06cae527d098751bc8d2bdbea3cfb94e8f169270227d67177e7fb72918904bb293dacabb021c90d86a1199f6992fdc1d9d240d303e56746eef689024fcec83347d620aac00626eec3a089f4e8ee075dfacb589e9a8e1e28a5684094ac479a83019719af1d2677ccef2c1dc3181dcbe5d868405423b302e426924b8d44e0cfcf931f5feb34fe7d5a17f9b724b3371cab13c0c9f62b65ca7b97ba79a170b378786bd1b191879564ccfac5429a521b5f0f9769e504e32c30735632ffa083f60208e855556d53529ca13316e21ab78443f57898adfc312f87d8965a95c13e4b7221124d3eeff5a0f44f7426520f683b6de4d641cbbf83af509ee9da5c1444791e856a976910ee88141cd8427ccd401da827086bef2017cfe35dd9c9991419a101648caaeb8ea88a5e661c021de8cac68abde864d96814f500c178363f09daee01a5b6c05c6f997d87faa75097a49dd693f8d6209cea90f7af2dbaf348a9292b1a5e01a5dc8bea9c4602eecebaeca7f9dab6946aa941da77db304", "53516551", 0, 206226581, "886fcc8864af8803d33994cde84834d6569275963bd029e0edd76ec273896296"], - ["684d93ea01a1659b9f7d0f59b07238d5374ead4a5494f8763464060b22141e72d5a6b0d2130100000007ab5363abac5263ab5703470116a9d305000000000251537f8b57cc", "ac63", 0, 2017936627, "2bae93d9b47803022976492f9f014aa0b572129d0e40c02e14ce8549cf617138"], - ["3612d41f0285c82ef5184bc6b07b0e85e29792b5d262df80f38fda77b204c53d7dab31ec2d0100000005516a65515229f019e1c28538443daf968295914d51fcede7f92b08875a3a4bffd72cbe8d850f04b2200000000005ac6a516300ffffffff048c64d90200000000016a1cafb503000000000763006a52ac6a51c7552a0500000000066aac5151ab6522e16d02000000000452ab52630000000003b0383b05000000000000000000000000d96c24349e5626e4ab9ccfb0923d4483e52a6f220e5197ff22271089977789a5add151fd34dacee6685b1dc04e7da1d5edfbe5c933729b72aae63f8d961a5fea88253a7d8fa485b5743cffce98de6128def93c2c2fcb3f2e766f09d50101f1ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000376799c9583cb23ff64fc6a6f25cb6d468375da59e032597f665240d9e80c666bbd3edca08045f7b19a68faf322d2c0a8a5546e21724d252de1f1ad026096c372bfa23919d443ab04622f97ce3fb27bb20f88bc5b3fc23dde7c982a8da2f6a670033b3cd0afc2e3228175d4961e15ec7972123b69c0c2c3193aaaa193eba4ff20316350897eeb070975cd13fff3ddd3a59abaa40983bf778aab196b643ce4ccd70020b25784c0a843af1ad38952ff3ef96fee58a705b58a160b8885953943563a5c60a21f67a434b87042151a6eb9f62930510ccd4e8155fb24ef299f6825816f3a83c152fb4b8e0296f37b6237ac72c9c1e91a870c25c804a753d3786308280c9beb103289c7f6f8b93203ed5c4f83e06a566515548f2c3ebe473b589837666a322d9b903058580fd6284e4d1f3de06fafd7df6b00cf5c20cfcdc25ea9958e6f4a037a53d0221f6227212b930548b0099edec210a12a84789830543c3392a6cfdf144027f93021a61d301bfbb1d63650c60f6a955dd37aa1a41738f7fa91d62e9a8811cc4b1a803131e9e8437669fce089f8e952e1ea4ee6429e294cba3555ddac8b25bc0921a15dc5da86bcf048d87133a053213bd5c164f6ce0fc5b5f05085ab30fe8fa6ba686850ad85c0599ff58cd025af20407b558097144ae4a17ee497947ed0251ce4fa6e2872d9b69cadd241e8180b910a0a78a46fe19fabbb147e1b5c57e98705141b0aff785871e1bb7d7d7dd7df7294a43771686374e669101c1583950259bbbb300552f270d8061e24adc64d2422ecedc04f57c7856a87f27550d22aab5c3131e0c782df51b0b296c309ccb9a771dbb23a36d8b6894cd74c9c2c4fc0a0fc43a66011746515c6f0ffbe2d908111d1296ffd2b908a9686914bb58b4f25aecaf9257148d47dc2791daab91c9999861ea2391104448920d5e053bdda0bad13b658b47388212d719a2a8c96a46c1e5d1fb40fb5707b255b1c3c2a4c1bf43946bc091a9dfee37ec429b7242557096643f7b23966e9ea2e76e45825c9cbf1d8be6a93df225ab919c3be57b6d7dd82d3076211c9d2efdc3786cc72ad57fc603b938c5ee8ab363ee156a9ea53785a10fca7086d8438e0068a35a3e3bc5b60e14875038224932967fa0c9e8293647d6a23a9cc0f007162b81c0fecac7a9d469f96c539e1d2a21129a438b9d9ddaa5026117c2e2bf3971357f48e684ba382113498d6138e6b0887d88b992383c212ab1ade4328eb61e5ad4df9c5c57c6c52051c5165e2f2210f8be59f01b50f542428ed348f1f476427012bcfa67ead8b5a19c258edd9484173c4e9a7580107967a261d2f98bf6d27d80af5c18313297b4cdcf7d93762269f109da9b1e3588b59ef72d8b54557a836c5746794b58ea92e014217ccd9298e9a8c946728d67b25e9de798c474a29bb17a04d9a63bae82ba09380fcdcb3e35f5959b232668802ee9042c37107bc306afc84dcb2d548140b01cc9da34c658cb1131e6711f0a004aaf4fbbf04f4c8e6b80ed2938426450494d37b1455bdb2963cf2aae78cf5858d825cb6668c2f5f17e3c01ce16d47f9893b57482e6b5f744dc16fa97e3c27381e3d77cebf838330fac2594574fb7ee2575bde11e14748eac71f12bfcd45f9cd86218f9dd6e5b86c3f18b6e38a3c6e16205f1493f6c19274d173dff18d243668c6418f4d842b50dcb4621a95d35407598d4877185e24e8802cb12e14ede93dea567fc9e307c54dc9d84db5c368727d76b0fdf8c9e4a5bfed139899289d45ca174114e0a5781661d3af15902d3f680fa0637be97a28f9481ca77f5971e58cef8f05c6f807c0005f6b14f2d2d2a93cb43a8f9ec8bd51e5ce0b1d9a478f55051caaccad892f6571e2c97a28a02ca8ee0b091d2f74dd7517f5b04035275b163202f5b465295536730c07f23e88088691443263493c748a04e587effde15584d7e7771af5b071fcdc0f10702c661d2e0ae75b11eabd284af786bfab4d0b6501b377a094ee65ba371682497a64bbed7a6e780256623f56c064431f9053bec944d3e7b488b72c35281e7b25f1a41c6f005931b4e09cff327f2dd6c622bc89c52ffa3767457eeb15f12f349ef1e59f89d4e5f876528017229a0d4d29dfea75343725ebf2ed2f6b1202a5ec47f20a4f5c4aed5748480ff42a0fda67307aabbbf689aff8eec315fc636aad84c1257ba7f87fe5cc5f1c51458dfb35322a0dfc5de474e49e956bb9ce53495b7309c2a30b66f28b1f31fc06a8f0fd668c9058b79771fd4604ccbda2a606ffc50829b031ac9e0d2fcd839cd0400000000000000000000000006ef216b1d8f58986ea347c3e17ad660d480b80f0730d99570352e034728dec79f3aa5be3d5a5ad2a5cf77b708f70ec242845bfa1a370e30509804d6a3d75b0afc5a6b26162df3f9c5fe8702bea6b7fb80d1a2b553c682167892adedb788435e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eabf3e6174d6a5e8c0357e41d891efee65aa6deea7f5e6999e7011b8a71a71468811e1279100bf1c8f04ee4e0852da6f7f1e62485d2b687ee48f7ae1566b3ea4c04530211d8f7ef8e2cadf551b98b49c20055aa85ce35c9ff3cbc52c15a1c036f475dacaa715b8cf0b3e8ec2e68c264c7ac6de795d8c34f41fc55874f3c39093031c6e9b1a0738433694177c7f47482798610599a54c20cf0d61cc23dfcf279a4c020b6b8c4b1c0a427ad585b888cf0e2ad8f490d75e4f818d3497e0169e7d17ab510b031fd77dd0f510e440fe0d53d36f85b252f3908c009a8a651a4dd15618b8c45f186abc4493518f5aa0e8aed9bb72ae12dfce479891324bc1718bb53d0dc2860903036c74f42f744528bab76e0ec68263ccc836aeed1317d26628cc4feabdc5d2fe032a5aecce136537c52551a8b92c466281e513bfd741dd023f42dfafbc5f8633570322e5a47b5aaabadf15c5f48f85f78b7a750a39e4856328eb406c7a09d206d461031d09aae96fd76af32568921273f8bc7691234c3cf4a91d7678fd482954a274dd0217694ba8925f5e687cdb6f5f7ad814a6bf48d43d27612aa99163189d291d4a61366000d294d6dce16af640c162f0ec0b9dc406b7bf2415ed2e48f1bdecf5fac8265c9b8055e10434b847a3c8c10a99826e9051d1880caa73401dc652fb80b6666f77a525a2f91845ac4993e3fb02e85df0815996f941ff908a7ad8278986351533429509b24a1a56507884a95466f7e66a61acf5d63d6e75b2406e1c115cbebac990dd9b6845507b5adec0565499fafbe43d84e1a345bd69270d54c9aa08ac2efb0222f7ad8f7b375ad3b79275e29c14bc076e3ebd41ae42909aaf37fcc5ed95ff0755a1d470b6c29be5d96e8339a6296844d1dd53a776cafcc9b2926a63742d91bcf18ef9a4912ad926a4f00b950c119b84e5eaf4e0003beeb38ced6714409da85a53ae505078560958a57b9a0f8269c615a905c72c0ee16f9766d9668944956103dba8883051d3c47052cb201c0374336c3ff7edb8c19558c6a216febe9a1c4a5088da068b3b8fe095960ce34d846a8b931dcb3dda2712e7395660fbd651aa22d3e7d072d850a8031c8beed1d14aa879899eb0359fab7eaa9fbf1f34ba28993fb347f04dccd98dab5fff00fdf84392a962a9d1d43031a4c1e436da7dfd8b05630c844f2199067dc511e93fa145b66472ea20613f5ee400e500b60cec21d235bacf6ad50470efd5fa10c4cbd23e39c50bf1e2655980425c9b9b1883d661bb3a1ef514bc57d1acc30322fddcb98f70004924ca1ac3f6f7c80ab96da12cd53425e37e402fcff340f8261c62d9045df4afbd81ee4b11d00d21101ac20c4958e518bb1ffbeb2e8ec31ebc82f5f6fef921460938e69652a268843ae49362e4f87e7a1612b2ffe5d2f71732db8660903499376512adbdd207cc78958d66bc96659617fc623fefaa765a9d11a6ed40c221a014bdd371c58f089a5254e30a2497c62c7be4ec7e015abc49162d4bd9a4e7b863fbceca6bb8e14e06456c335d2f3b4e4952a0357cf93350137eab108d39c51e910a4cfb28060f4c134ee78a5c79d8de4276670466e44bc4e523d6f6f0d1a207aaae81c3b52f04fd914e8d357ab7ef2b107cda96e198470f1799fc6cb258d4b9ed2bac9451300afc6ee573d7d6f7c1ab2256ebe8db586a5822f3f70db2fdda352d3ffb03bf94606ca35bc25f58976e5f125e36fa67a5ed42d49c74b1e121fcd59b19b4659b8a47db13474ad29e0e593b6fba91402da9079825a829c52cdf6a3e9769067c4102f6b835865b93a82f9dfa409488c30399d4a4bede539b0ed8601498e9cf7fcc196aa8f99174fe06838205e3a53a4147b0ce9bb0cee98d822a22ad5d89323b8b8e94e9750624f44d66897983a7917c8b865f189d92cd4e583d5876e1e284a200efb93c43c15009aae2010b4ddacfbb9265563e5757f451931049d9256f637e607e7880b63d5546719bfe0edd5676690688dd86f06eaa0968f16eeb6aebd831b1faa19377233794b84832d75bae6e4a99ce88beb21aad573e09a85f561d7976d68d8a1ac605f65f1e218a07fb09f86124dae26f1d9ace65ae09d6dabc4d65d1c764cbf80d856497be0c823b25d96b434d8ea2ff0f570e0bfeb965ff9fd6fb063bf6dc0ab95e32a83675d21f3df6f660814a796baafe376f2646d26519fb0b896917ce77d867e999ed0becb9390c39a62ca8f2d7a4499cbe808929416dd990f338f87218e8d554b2e95ac725ccf4c2fb8b9f5661bbc6704d58a160b33b65aaf80000000000000000df49f40200000000a2790a6770039771a6986dc5346581ed23c91a2488f8fc57abd56d3c05ea807625577a70569f0789e6b02bfb8c3a4c3a47d20d364d6cf03c3ea71d389af67f32a39ca6406dbc60f2e091358528996bb150c88804da4bdb84437f81364c9ff20700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dcd38d0256fd07f7b4aa2263c0c2e195d921f6713bfc938a5fa7e8611228a55fe88eda097ae6a76b0312244d3653c1de3390875d2d4ba4a271d18debd66c8d87154577467c1d820f4fff7a96ae0f8d9c99e5932c90db83ff59ffa46d4abade94025f4833ebcf04979e96f20e1e6f0d7beb2374147b295528e27ab114b21a091b032f298b131b0bc7ac4f8b0db9827dca0cf536ddfd56fe7d97947c8cd5cf6fb03b030053842ab1e1d9c1bc2cdfebfc7b9784fe7b4292b516d96b59ed4099851a925a0b226e57fede229dc05389fd5070e0ce0d0ac8e70acf4bc486b25505d390aee354094e3613ca04ecf29de5461b07a6dd162a0990c830931bc82412eecfb211d875021686423da6fe1fcd4d1a2fc983be16616c09788c5b42e807e148d1f5c134609b0326c46b10b748659ca9c2a0fed60da50eeddf97c06248217972a392b2a5f4beba020a09e65fd9f830445cd1f0f5814cfd001231006d0c15db5d75b6536a7c23c3890201949e371590d05fc0ed7529dd00759ca41453f8a1623bb5c3373ce1052924e8021984d524238c55ce8a981d1fa1b2f0989c8bd2511fe95e6feaf1eaa7cec5b499263db03dd9a6d87f6dfda68bbfec4eac42b01877c36ccb59d4776a2e3b8906094575e6d9fb4027822177b9e31dbb6692b0a3d6f09f3d87cbc313c1b919bf9e3d303f336c6864434bdabe7be97e215ff6d89630433090101e7ffcf34b68d12277b1410bd98c45ae33f8359afad34515bd74bcc7e5289a37b0cf86d2b29460b84476824c5717bccb2754fa8854606f2f735d8093de341af991b702fe30919655c10d96ae9174dd6a3bfbc3d3862fabb2066027b93f45ccebe8387407ba361136c292ed701dbdf06e7b948b5ef9f658caa2a0caaa93fbf3b1f4cf2caa28b1efe39136273a8cbef15e817e23ab612617ae4d59452a08987840a32d8f2e5c72c764bb1ed5d1fd466d7ce8472e6b9383a4614b80cdad450dd1694eeb1047285c422f4e95ec3f822ba4f6eab351607d9869963eadf2a5fc5b37e65323e60dfca852b4365f695807e2c0cd62fdd4a8b14aec15002df2a9a13bab395ab60c358747d620df39f6dfbca8899dc5b34ba840175f35d4980d9e5247ff277cc0da8da323ea666fceb379747214b21a2335d96fa9ce331536e4c5ecb30bded3609f0d50ff3beb8231a87fb3f7a7bb7a77dbe81cc0facdbc84175821cda388aee08b014c22e84b4b4a3f3262bf87fa516ac15635c7f5039f4d17150a32a7363e5fbd85955528c25471322917f05d6ef09762bc290d3cef7c6dbb6ddc86c8cf5983d5c54353fdcf55dd66b0c2ba23b03f583b4c69b08e0f859e4c142086e367eba70acd4239d502499795bd8b36dedbef41874e593a4f13357f722e775f026530513356c60074dfaa2623eb691e84e30475f5c58e0b3b1e1036bf9f787ffe67f72e2beb08d9bed687d920aa12e96482c7d01f412d195c832e847a9b1e79bd3385b1fe7f4eece4ca232a1af870a57a7fde59d46a4eedcfcb9b9da8262dea53546564721d7da6af5abed8889a969effbc3437470a7ddcd855feb4e9d476d47957d5d8690b95720e3089a4395461e782a84b2bbc10b5534473a71aa4f92b4f4ba83282e00b7703f84bc8e4da5ef4a20b30448178ed25cc3ea49db556fbdf55c088235dafaa001b5743a23ccb04f565fd22c8e7f41e7b1012e6917abda884193f1c61ad611842348941204ae4098f2b02475ba779d82a7c0b893fb21e9a5ecf03d4883a1c08ef3156b378d06c1295b66ada5137e26a287bfd986385180927ee893d281fec541014cc5e4e2a3a8713d5d8b8a63bf88425ae1eec8623b1e796acff1da6768671771a070b899d189c1fe471d5eff2a4f69de78f60c9ec6d64325d7a907338f31a40b814aaeba0a49f970b7d58454fd7af7601cfb72cb4f396f5af2b290be5d29dfc2382e1035b4e88fc2f719fbd77bec5f343b0bf0ffed94101bde6281541a0f5f5c2274129367d55995494eb5318e72a41723466a34eed1a297dde4ce161490f689fd5960798e73fb0e9ea993b55f0519f6d108bbbc3d59e5b1d9c64f664c74d7015116c235c6cc8a812cf263789d2b8632e3cbd99a0c64fbd325b4cc03b8406eaf4fc48d564a808f4844524b7b88fe013b834f4db0cd2c7bd60fe8969c04e989a7d173ababf3f5b34a6f75ec0f07ea193822fb4f41ce57db9bdf625c025a9d3a86e0b954be1575992a8a32d4c6a1dad7dc5a128cefe8d6580cb88a366170263e065a5a2c467e043770f89026d3f7864af54525203f8528e3c5334e2fac7e22b74f17c1c639d4b41a00162ca6bf76ef073aad5675e752b476c180045c647af021c4fbf3c37084b05eadcb1e05fb5a41503cbfe44f19cb42dff5772f73ae2d6f623ae01bc99612ede7a5dee373b6956e59c56e90d98720a", "6a", 1, 1293760694, "ac0cb63a22e7e6cb72df175f6c5a81d63aa55ec87a54658742bc8328fe7c5a44"], - ["eece262e02eec80ed40445027c14808eba8ed26e499c4d6d00ca539c2f2b5f52d8f48278fd0300000000ffffffff91a0024a1caee7f299f3269a27c4874bea468b411616f9a0e8a48dbdd618eb65030000000852516a6aac636a63ebb95448046ce61f01000000000851ac63ac51525152522c5a020000000007656a516a52acac6e0b1e000000000007acab005251650098776905000000000253ac000000000100000000000000001a0811030000000066b99442557cd6012ec4003f2de2abb4e0c0adfaa3d14c9fa98c2338142cdd69ffdf37cd25d80bda98f620a1c3e879966a6dfc035feebb5c9bed73b026c56f2cf687463310b8f41d5c20f8530a17482c5e59c7e04c3010e3e29b366285eded3300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bd51d7ffaf70ffe9ba7557b3ea1b43c5f585137559617e7a65514bdd216946707b4fb4d109f87ea7c8f2d38578d5be63b6db3b1705c9522c8084b9190bff39eba0fd86c062f9c0e09740c51414cb59eea57acedc3f0fcae07b888c4aaa11f58b4cf00e29dd32bdb5fe7bad124d9c3286ec70e438b1df7b2f2c5916bd450d8f87030d0f02d5a96a34986c164d272720eaf8fa58ffcac999f7e0a546e1acbd336ecf02186c8dda946fe4e0e5e85c476474a72b2390d4b84ac568979f432760c3474fd70b1d2cc4b1fd3c93a42ab84d8fe5464553b517c040214b8e98e2c2cfef6d36832c27f34cb81872886e91325b1a5e5f2fd081c9dd809e75445146078a13816d204d022b3830433f68556fcd471920b19aefc02f05e55e3e1fd68905c64ebdde2cf9140228af8a5801bd56292546cd703994280f7e5b91a51cd1477ed167081771d7ba15020ac1bba9080686b5e1c01dc6c5405b67a54afcb3347728956cc6eea1d8f4ec560323a533da2456c39ba92f825600b1ece71de3398bca6e0a77c0736fba74032b74032aaa06b4b791dca9e1183435cf8c7ff4b5e07afa9d50a87df552cb41e945f7845075c6840dbb2247ba536a1ebcf377bdb6ec37e5414ea20b930da8ac8b0e9b2e43f589d4af81726cbb6aa31c6f45f99492b07c2c0f5d22d64828f3442907f68c14f9c35c271b863c2617273aabe672e487369778471792ec2b03121607e0dc0ce56b51b403e4d62b336637dff08951f1ed48eb9d281634ee5a51ac17f1cfbc074ecc79b8e714fb3d85a7e2c814ad78d88fcc798fb645a5a16d6bd7006ed2cecf826a8257dec7535cc93b2c54debf8d759c2fb41504afda271a0f9fa4ae35f7909a89708c210d4792f344f1c346e9e777a6bfe60a62446066491881ddaa15c2203bd0ddcf338f6bb0d1b4182c067a7fb6e7b72f157ca2eef65365bcef676f4503edfd3b6523dd348865610f6179605236d5673903b48790c1256f5af44cf0c1146b03529b47ac68203e68e78a2114b85b43ebcfc28ba5c0049457bf48e76ad14bb2a3aba5b9b5dff8a64ae346c3b897346b9b64e76ce3e102053b243ef92c2d105eedee4a2a0571bdc9fb3a9a3c353b0991ade7c5951cab70d49ea28a07ec8fa7002e8d3960ab5ed06a201db5c28bb2394de44bb34e3e74b61ab183c7fb1e1b9760ed362474e94042fa0bdac3d7e95104e038f7ddcb09daf662ba0d291cd5f8fba431af54b2e5bd043089a1e618ec396ca39a587479dab9c55473f266bb0314cd544bc4a08f3fca80c8bde5e54acf91b941aba837369e15e223e358cab15c6216c052beba164e9752ac149db8e0a6debc114706720385a248042fd5689405b4e4ee95725f0ec4b3f53dcc8c121e337ffccce9934b9b5d05424abeb97bdc1cfc620e42902bc2c482ae70921d4cfebb7dd7c96d27de089982cdbc64f6b2a9cd92db26c9824f9c7b6c9b36d84b642ed9164ae1314bf805c8b9aa7d7cd7e370020a290c578414fa7b2060455f00ed26392f89a2b6453c059414855962c0422c490ce0f99544534d3a1c5746039f725f4380a335af4bd819e66809a6a48008209b5804ca7f8c51a6cdd57c8fee0a2bf032b615ac402f782980825284817d0ef2f34d80a34259e2b33fe8c075b8c3f7e81d2daec5e49c2ef2afd42f3c94ecc565cf75eae94ff6b56a84a8f1bb92a63109561456b0636bf471a6b637fa30db8092ff3de38dbdbf4f899eddfaf68ee7da56f46e25d36b50a6b7c505369790f38c311a4f9b4d9327dd26c61733e7a08baaa169d5990fb8d06600c33e04dee82d4a7584cd9885ff66f6e973a151a3750ff05c5ed6a75c352e9a277161019a335e4848782efcbbe2e9a6722dacec116039b7ff10dba5ac1814158ecb9f4c0579c2953d141a1d4dd71dd5c6a0caf47f9c5ef748b3f38bded3c19193c0eaa6d037a529c9a4fc03abf9cfedbdeff11633fa44257152725585a76639536a9a216e4085a2ab7c1f322276a3cd01aeda71eb8096e739447314190bcb1d5a475a89b3cf841576ea7d5bfa2ed5af2fff3e5d33217ec2ad386583aaedb518f58c2467b80470f06a84ddb4d43d3670b49bf48a553898c5475483492414dae64b05b6f6dc5296e3453e248793c8bad4bf32ffc974d35141a256b9bcb9c07beaa8e37d14813fb8c9255d7462b59b62accc189e796c1e4aa4d3c0d20b123d3d73902be459e4657e6a9f0d30f3fdf1f92420d4e48bbf14b89bbabff953c13188fcbd31501d2d94e7581639ba1ff7b6a769593e20b359e3ba4aa2f25feb1c24fe61778dfd06318c587e96cb626698990bcd8fe9eab263a095a714e89ff18a69c991da4b29cdadec00f19e5a85cbd367955c4a48c698bb52b411d2316616d14cb4e00a1fd608ac33cb612783c463d6160fde6dc765af9d8ea4f65427d825180c", "52", 1, -1324892752, "d1fddb093cab147e9495581c89f66a95e4cc353926734dfaed61eb4ed7089e96"], - ["7bf1f59d031a4c7611005d0e7eb54122bf9fa05f6e47fcb54173be032acc8b34a59ae2e4890300000003ac00acffffffff151de5fd6852fa2e97ee6faf21ad823c587dae6dcfc378ad50c1f7f95c7b8fc70100000001ac5dae4c0fe37af888e621ac1862077511dca4fe3449cd622960305f86b9b4047a2fbdef65010000000765516300515365ffffffff01ec4bc0010000000005636aac6aab9f893a6c", "51655351", 1, 425065340, "ec8523774b91c4331182e83838838700ffdf795e34ca6f37c979400e9d2fd60e"], - ["47372d9903e4155ed05d00f6ad6290e2a46ad97a3b36bca39a5c49c50a1cefda89a3ef18b30100000003ab6552fffffffff7fefdd9b5592af6760040cf4484c09b7f66a34b9242489e6563d20e35363838030000000052c662f4a8075826482dfec36ed04cdfe01f6c18c873d27dcfaa13591fddd5e1dceac664020000000351ab53c068b60402ca7232010000000003656563aa6d19040000000005526a51ab5100000000", "52630053ac5252", 0, -1051898593, "8b45c956be084c427cee98f23d8283a39c313829438f6a6c32390b16e9282c39"], - ["7d810b2701a38f4afb08e340533e60458fc57a4b05bfec7010e79fa5e61b3d163cb6a15ed301000000026aabffffffff0491bdcf010000000002ac6a8e5bd80100000000080052abac51516553aea09402000000000652516a6565acf850ae030000000002ac6a9e7a919d00", "0051", 0, -222753988, "7b88d845a3e71b5ca87536c6e11662fa20482d3026a3f1c181896455485c1619"], - ["90c5e6e703e52f954758fa5c3e2c98f11a8058a87288bee1f2f0152d4ebbef913af563fb4301000000056363ab6553ca88d176c00b6e462a85b3724eac5e06f76cb503df0939ea83f08aebaa1c80c6340b0d2d0100000000ffffffff62e9c5fa461911dfc8d8af64b95e1cddcddacb578fdb208fc37e5cdedc8ee25f02000000095165ac6553ab52636affffffff02a4d86401000000000551ab6aac65dbeb640400000000096565520063650053ac00000000", "63", 1, 1870370869, "b2a524dd96b919cd782ca3eefc1d6b7751d69382306e961ac9ad341226dc9433"], - ["6a25676602bad87485b576e5d70433f94b111ed3246fbdca46bb03813938ba49a1965802b50000000000b339876a073c755458d446ce903d699999f2ac43ce29a02dbf5ef7070ab6ec75d633080b0000000006006aab5151533f11197704b6b95c01000000000600636aab636ac209c10100000000066a51acabab63edd11402000000000553630065633f61960200000000065263526a510000000000027ee6790400000000000000000000000086e0ef62e638e5f3db785ce056d62c02e35fd946f8c4b002ce723ec67be0a44273f51167a2290e35fae54db3bd06b28d2f9e431b7ace13179387a14d41e1258dc1a9572a09b408aa34126f3b92b737823c02eba06ba8e739107a43073cecbe8900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e9fa51c57f034645c403dd4219a63d8174b7c157e1ec4af809525160a238197ece7fc713c569711ca407634aa4e5056bf5a15249da619cc4c568a445b4003ea24e67c8d2aa7e6574e57b347fe4ebdaa43f3f52235e0913c9960bbd93c95cf2d3dbf1b625509db0fb75ed6599f2d2ce845e5d8bf7730c9fa3e7a38fba14472e93020926d9c2f62ca9d7d200f666a89ceafedbe3ef7510810a45bf246c5b1acc2226020f864cf6984f0fc0cc99b6e0d617e12ce556758578ed672f71b5ebab1fce39b40b0c09873e890144db3f951fb2ad1abddcb98ff80de81609c191b32debcf159b7718dffcc86442522b5ef2716b5c0124e838da085a4fd11d6d520ffa7923e422da032d8bd2c0f52ffe9fe420b5c6675f9b9cd011a82b7ea2929942574bfab4118e52031c036dcc33f5770df1e820293cf26000df88ea4a9e357060a4ec04ae0aaf6ab9030ac960d5fea4488f8bb54336e928a932afd8e9abdcf8a6690e551da322665b8b022afc0ca590eb98d837ba03c8a22000b98c6055c21b0bf5cb5457d59a588654a902136044e7b571aa68d6dd4def809157c35406e87c9c80a1188d73ffc67bd611ddac9bdbeae79af3cf182f0482e99f2fc97ba2a33b6e895538756a1936361a2f49225cbc63829a6e742001b31167863fbc6bfcb2fb9d530a2e8692762b88af8740b363efadd69917dbf70ac963a6a36fe820715d46131a65dd00152399c01ae806876dfcc18f6982c4979526ad68575f202d2581cd0e99e915540e572d222ea730ba8afcb7c2e07f34cde1a61cc3f00a64056fc0ced38d44a2a7638fd54e57877682b07e1012fd9464c25aa41dfe3beff35bce6c54fb4a4b3d451bbdd288114c7e263c7b4346838e5b62bd7b8054b6e2680ba633924c97f7722b855f0cd3204a6e570dd6b5d31edb8692c31675aa9841a81d2c728dca229e87de0a3fbe9307e3ab47f0601741a7929802a91c727dccc5dba62090331fd1bb39550b96e4790495d52aba93bb43b5dbc557a0e7721dbd6df75dd22db7e2520c5dec62700ddc87e92d7846ea7db1cfe167c6e5c60e7a3b5bf065f66083317ea4d7054ab03303717bdcbfcff5e7e7583281aadcc9432318b465d8333ec8b4439a81184db3ea0111b0ac42fd70ee5c63c37476607663b48e59db124752f07d17058e7e144be4b34dafa9a1268d93badafb342690a7517a952c24b065d0afbe49a638c52146b27dd25a0b3500fb57a11247fc253fa6eed727cd6324f23c898d1cae95a6b4bbbd4763e2c1353e6e2c5a32b35df37ed9f248e230925ee236f2ee2d3d66ccd4fec61b32e10dc990bbe7fa9d371fd7e6409d8485367f12833954fb4bfe584b3161de5b70fdfe999aa704f48d3a71b72580745588879042748c81f7624149e8ceb3d0d4b1fd85768194423162ed48693de3051b93a5e13be2cbf96dfb683f9904b277d2f10810f0dae57ce849e1bc5cf123005b0b8463c70f1e2a8161c296ca4d52c04a2ef150b7c895ff4c3f6b0774b83522c325e5a43015e37e4790c938e18c04c4a47105db69ffe691165434f42b741e32c697c013fea99fb872536310affa3cd25d8ab9634c3461fdc6d9a7461473cccbea6bba5bcd1ebce5b1e07b24ac4e1dd26840517d964dbdbb7ed310ea1b1d93abeb1e9a842a0fe10875376b854d5d7d0b03bb28f1aaa8c7f09281748069a75267f14963c88a3b8122ce616622bc727269e5ea318e174400a828de584b5d2a8309db4818e11142fe5d80935e0a464bceab1f824d2e5552472e85925d187b79b223b45e587c7aeef09afbf0a832a9596054ac9cbfd4cd0d25138d948b33ab93be9897cd2b7bc13dbf273e4a7807531c51d33c472da461a1bfcca3b35aa36d29cc54240c395e18785b25900ed62fb6d2e645f19cab084057e749ab8fe3510eca710c1c7a7a4fe3c02618c2e2a79f9991a9d84ddb02cd517afeef9b7aba405126d6c98e750e6fdae4f6e107f67674ff568a85f83237cb1820375761505b143e4c2d7d87b8a65a71970bc45f4ea8f3f7e3352f1d3e4c27a48802a9430309ace9d29fa27e1ff0b12142268cfc48e463feb93f6bddc87aa391fb8390411e18773e3a177a9720a1efdb79ecb04f7714f3521ea12b36d6df60be9b131c9c714d4e6eb74bf867003771fbb0ece57eae671819906e48198f05544e3044b9f134eef2bc237f541278e824aced225a68d149055c114ca66a0bb77c566b96a90e9b7f7ce2868baddcbb04477575ea9b3f971386e363f3c450f2e3b205c5cd9dc0841643ffa97c00f936bfae622600000000000000001d973502000000004f2e7264dfc63686b303330a312c4cb0c43f416b57bd4251de6d879b0a68520c1d551a7b98192e697cddd80399e599d0b5513f9b94956c8f30f6040d8f9ac0452294986f87ce9a42a3ca7b67136abe7297ebbeaac06c07728ead32a30981e47800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d30ffa8f60985f835f55b9dabf9a9566a033692f5a156edd533203ac97d548200450330a5a279c81ee2c9516e2d32de7659c487532dfd765e16f13b0b5253f8ec64a6f8d033db8afbd3e407df012ece299188a8e2454daa74c2517bcc2d4eea3f4ce450d99397ac634aa5e16459524ac2a229982b5b5edc4a2059063ce50a36802244f9da19db48ebb22c1dcb3b4733b13309d66b7da871f5582141652aee515a0032b27e05061b66c7f9a1146abab440f86f9715322eda1f83d8e28f63769d6b5410a28ac369eeb78ca7d7380cad4e9822b5abbba6af3ec78d84971c1385f16429b0a247bbf35576cceb96bc39006ff97aaa52a7cc61fda7cbfe0f790b3773d9b3fb90314683e687d70932be8a847959144a07178409ad248f8a1ffa47cdbe02cd5ae26032e901cc7d2dacb81b975b9201c55838b11ef473c679af8b3c90f13f81851ca0902054a89651c935fb6488a0d9321c6ff631fd8b1843b55c1a488e1eafe811fd7240200f5e5a0fa040a4e788248f2cd69241cb9eb83f684929f2e4cf3df4b33f8689803299ac7ad22dac4ccde1249ff807d4d1370d8163ee6bde392998bcde91f5a7ecf729126f4867446ffe1bc4e1c02379088ee395f3ffbebd1688bf86bebb16e0cfd02b3020af3adda9948ecdaccae48a182fc2c031d259ce498593fa962f36b8e51d60b700f932d83c7ef15ababe32ff859df0faf4ff1142d3b30fd900f70543c1ed21a421b87643353132a014a4b8f7409964460d3341fe116d7611926f1b89d017219a2c5b2dfc8fc5daf1891a2a88489f3ecd201e80dad62ea0a264663f06580f322abba0c91ee84703961cffad0b2b45c9765352befc7b2a9a6ba7a00eb6858342136181f25af6103a17f47ea8eeb351e487e5464524e42c6dc63f7011d06e603f052f072133f90caac696be70cf278a5768ba164ac1f7fc16b684d4feaa3302886818e6790ff2564ebe28880d63ec157149d919a0e15d46e876ce9b2876c378ea0da1e5befdc6908cc877baac4af3cc28563f5eed3ba01726ae71756611a1429852aa8254133dcbd42d0384ada1b80d909d34833a413d47e406f64b5e414cee37908e950c0e1ed19267f09d5268fcb48f4cacc367e12316c4f741811ee294f2a748bf45b79e5f2f98977427f003e5295ecda97455ac138036cc8f774a0a3cae81e5642bb0bb2fa0bbc0abbb2f2dc3274b80420bc553d327456a120a108f4868368ee13a872dfb286090e5686abd4dc9ccde9f3c330fde39fac30b1a1f36ec4cb73f1c1d7357c2cde7539805edab0f260cd4bd860e11ff08eacb5ca3e5940e02c3daa4b24809a2c2ec26773c85f0587e584f115c4e5d17913a5ab6145fb0711fbb074b013562e597bec9ec9b41e248fb2f9fa2c5a009256246a67529356b8e66d267217f6efaa6fe0cc8aecc450a78df22ab6318fd9ce36af9c7891772886b18874395ad591bbe9ab0c0c957d8d78f9d9483dd7d19b089cb633b9b3a4a757dd44c124e88038b18899c27084a9a3f0b689ab7fd7c0f4ebf0eb5e15d4f5913963b2fb79e4d1d76d0516b9a70a141baf65a250ab7bab9a23780ce9fcd3fe89b11c0d1ca61a7d620530217d39bc5b131d167778ad8eadd3bb29fb06c31c0ca941c41339a715396d88fd8d83f50ce54f4374c4cb586e9e65e76cc6bc6c56f5feca88bb75d9263b90d561de22137e29fec6ee93ff45d04f3a4dd7e0379d5f12bdfaf033292a16c100226cf0721a83e12d4de5321ad0d1be4d3676aae782c941f14a80072328c04516ddb89ccda584c483a98511403d610ddd05ba7da5605026937d8b1641b29041f59dc4cb9efca10a7031f76e0a79732625da2008356b6d76ef74a8950de7cfe166f85fc548a3bac17856ad276fea2f1f26cc7ba3fa50c7d18c9932e8e52466d437b8abbefee2dfe27b17311239b4efab037f406ef4a84bfda8916bd0ecbd25017e77e5b2c35c29faf59471dc655c2f0b4a21d781353783b61ff520eff4429c308c8da0f8cada1b206b51aa16f6a88388738a53a3d828b123dad55d0fd20772fa72f3ff8595ca8635603455b3c02922ec3bf06119637bf4d21131885f24c33ff63066a2c98b36af64c883386be47b4e10dfe28e1457c8771686442444a42786731e6c786efc04e2a4401dfaf7b1dc26e7da5174317d994feb05e428a6e14e3efa27b05a43db513209c5712a433aa568dea8ef87ef6a47fc31fd37e98038aef67331d29f03beeb7d1a5fc54134dd531ba4d0c588ddb130b61b5cc406affe54d4a8a0b156d73b502792769b96e6dd5b2b71354e5c4baf97215697d9f27520bd55662694ee16c8e3275c8785a603be64bb4fddbb5ea59f95ba1d3b7883b81d1387a2cc754101ae743b3dea20b868af11e1d464cf2a40cee2a0f96ca9843c5b8c0f0e64c43ef0206b69d802ca883c0a", "6351656a", 1, -1755215588, "8e5d14df724d9255629742d57866d0345966942eb175ba1f6a65786a78c15a43"], - ["56f61416049e5e325c552b4d5f809950b1be84cece2ce171c9d305d59187bd8a2f722cb7470200000008ac525163526365ac98e29be1f2e0ddb98e685e8c4160f10bb114d761720eea9cdbb3fdea49a9f7bcf82ae45200000000045352ac65a0cb9d68aabd402754d73e1d06cb6c30f64576463da9478765aa307754c45bf74a33a3c703000000066a5353636353d7b5fbe333a990c0add81ad79fd49d05f943ba0c9c6fd0c0b8fc09f5ab6e8e3cb2fa865a000000000363630078b1146104598e5d00000000000100b31389020000000001acd31f7902000000000700abac510000abb8e2b0030000000008ab516a006351ac51a83e661c02ae7971030000000000000000000000004ba03020c4369724b56d0ea9600d75a275b843e81304d622e2a81a65ba3d1917cf2fcf8d6d0bb328cd49a5cb6fa9367b37c9a85124c824340cf4a9d2ebafb611166c5c16c8179fa9a6104af44efce9d3dc172b23e417dc10c8cec0ef3a80da2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000302b31e098a355845b9fe39c00b7fa17cd39feb8828adec95158973733f4f7aba32ad02e277b7ca23879514df2f7e6bfe74868424759b81bae105657163aac843b4b3bf9d0ae8826af2a922e8dd5758d500d4ffaaaf57fa45d310e4fa0ace17b7fbb1845aa1da3dea4ec5d28836a22b41f750516ce34cad3b754dfb697f87a1e030cbc038353ba2f304734426b1b9cc04ad7395e6c63d1e7eba43be0bded5c6e70022523aebd7663d5e4b64c514f121c545bc28b6a03c4b5f257bd214639bab038d40b13024c9994634ab05c2c7117dce16aa7d28e68a40453863f3c331a07864f9da60b48dbde01a16576604fac9369750272d513d6ab38e7995fff4c809481e56a1a030a03e5d75a8237b32b348428f0c13a8a84e8b22f6f2e1db3e89393b30d49dcd3030c52debee305924ab755b4dcbf2d6d29b927e6efcdb6b3d36e74a231e0cfe7760321790cd5739bf88b00f0df464d44883dea63a014cd4dcffaa2c0f2664cc69aa90311c334e2f2a3fd4a7e1d55f1d6fd2f6b47be998e8acb458c122d7bd8778447a403276904bad1a3e8a37b995f1c07198e67abaa0de6a6b38678f9daa501b150896736e31cf9e28fce742ef9fc65f0e88822d81e7f6a758c3d53810e3652c510fb254c9dcd191f819961db4f6de0e85b37c0a18340b52892297bc625e15f5b39e5c216f2b10c4a6e20385b1f406f0da7470610939273bfe28cf0634a78e602bfa7036f9575d44f6f610c1bd27567d82cb07d6dec67eb22f1950ee93efe603615692adbd1b2b4ad0475e11b7e7ecb556acbd52aa9513d085020e2947926d7d9048f6c2b0f554b95e241d87203e1aeef7b7522c64704f6fbedab38990da57bfcbfc1221d1302a78d5b07a78e2ea382a026793618f75f891ec28fac6257082e52b0558a14e5c2a313123def81363f28647156a82897c3687f181fcf8521935ec8d9fbb3a8822ea541ee7078747390e3ffb95e6c07f3f9c95a0a22eafd8d700983fd04bb2a502f2d07c01a17794ab79ab017cb41599aad4eac5c59551693825252b394064fe821edff395e1ba4e1e309ee98629578d29062921384cac55a0f446d657dbd7d662e50d309a818ac98bb02fcc38de6c5672ace54bf163c726bcad506f014d8bbd8dc21553296313328dd7fd27a815f0c5503ea2678f44208e1b3ea3133d0d9d31c5b9765ca145bf0e4b232f6daa81bb6586f815e1df02261505e4e9a372978c76f938eb69098a76807bb7a444e4bbab39543b2a20b3c711fa7f321d0d2bff290822eddf94f4a6c4c806bd694242d9cce3b7be95bc5f7d3b9945baa8b913923ef7c2d03c78f22279c188aad3aa8cfda5913741395a022e1881099265c3ae70058477e3e2af8f8a4e763c3586318b56e7b0e8630b341689272b9249c74e482fbf4c9442abe80fe700815032bf3d492004766bdd5ba8887ac452035a4861ff24af7bada58543d57a3bf925eb65a4318f36967a77dc1f4ad37374903ea9b5e1b8dc87dd6c334265eabd9b082e9a6c962c622bf77ed93b63d6c904c543e0b9a709c2501869d2755489c53a42ff547970f76b24fd0a4c294cf7f0bfcffb19f5f879482355e87efad759a7d5ec929a365e2d76ca10efbb03f05c207f7951376f86205bbe5e320f52d692aabf8208a54ccec33ac9c58b2c32a17ff4b60c1af88e1c4668dbc30731bdd1ab59dfc77a454426affc3d7d35f272f626132c6ed44d4f509f4ea05b2cedd2d0a811c3c36481dad4214f239800d8a5a1e6b9225d5105186ebc4fe633ad07e2384d603975901e99361b22e070ca47a559ba3f48bf490b11aed1064394dfb093a5e61795e15b9acd73c05c844d435be3ab7764410096313ea14cc54e891a7e888e26d1682d7c99a4b44fcb7ab9e33449ee82d0f49ca85041bf6c2440fd8bf5c8c4bfe714cf39f9d1ce8f3dffa285de1744be11748fb061811a69efd6ac29bcdeb07e14daffe8b55a37574239aedf9b767cad3c64e37ecf24f9e128d3b3dc788b9a8eee2ccccc62b83307b52673d235051445cab2f52deb69adc2e4c99386b1c02296ac24be3e3ab0e761a79706edd81f1e72c269cbc593e3ef3d92b30095feebcdc4f29a5fb4a3dc1a4a180bce812017b1a4b0d3ce615ef64109b80be3ca842c2d4e7780ea30e7cd45914e0fab33e8591caccc161fe2867ce092194a1a245616a114e35dc4bfae41eeffe1ecb543bb30e9257b1d83c75453b72e79a940e1710d3db079817b46a36249c8d79d8c4135cc9a46781ec42358fd4c72c8c56dbc273bb73d3a6bd61f4c30dfeb673013ce6c8040000000000000000000000009b466357f4ea93f486bdaaed388e31d7c367eb1ab67ca479c4cc7cd840a4b5e23cf06e5478d8991c7b0c53f82fc3d402f58d93533726397c64e0440a0043d942fb2121727d5e9f999067a8ebddfb29fd2b416ab3496c9182310c43f7a53f386300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c71e5a979f5ab7ee272ccb09c2e729eb8b9086b073ac9f81fbfe0e5720bfd250903eb4a183d45a4c816ac293c097a8046f889fa98b3a24f65433d71e2c849024258730bf403286cdcf9362315e6a500cea1d86e54586114f81f86379e4ac6e919f8df2a8f67eacf88710dd18fccca5ecb664276237da99c3622d581cfced3f87022dd944332edf84a87075756849dd74725ca194dd1131d84aa4c1a79ddec904c4031cc061e3108c235a88e4cf002364aabebed1fb3675ddbc7a7b22f318e9c8de070b2908793a214f021c26d5860b7a4adb4521cb546d0ba2bddd9955d63d3cd538fd0316967c1eae61696b74bfc79e06e33beb89787bf26c8629553b247373ffae5b020d7c97081c50cadea9f53015f70333713dda75061f3e6eb8968696472ebcdf4902024f4dd3db6da8817a2fe095bd94766b1bb06325557160440ca4d33317d1d6bc0302110889542b73cc0dde373769e716f4c2d04034aed1d46cd6ebe19de84006a0031e79d9739c4b011decbb61aa78193deb2518d6dba7c85a0d003cfad8d51539ec021bd906a4eae53d21a33b99077991f68091958715a4bd8ba9b4730a9ef54f67e5d2ec7e4e8a4d1fc16d7ae4f13dbd75ab9e9850f1353eec17b4ea9e4f3ca662df427a8ba832670d0fb02b310e933c80d381296b854678053df71ac3d10cfa781fc39f94e3b98d41e96d8dcd42e0d797d54c8b528ce681b020a17954997b0023e9c05b5b37456b50a981664c885db0179ba91f60e8ffdaa4f9b9b9967cb6986f891d234d6051726f8bde5a7ff8837c37060997effe0ffe066b61085400bef8ab2295c780f9ed4f4aada2efc9c071f6f48b249a3d88c75df3209cb50ebcf0e20100012f6396c49cbfbac9b9979dfc55a3c42bb586436f0ee008e611656ae167507ac2a8e26f83d87806dd205b3e4ed1e200dd2bd50691812ef7908480aaf2734ef89a44b19542ab21f0de4dc69f0a0f2c44b0014a89d2f8cb48429a438e92951567b839a5ece3776cdd697fd92715824e2b5d03262ba3ba5bf03f60ab1ef177c0f6e957d0edfe2d0110952168805adf022e16462c3463c771ffb75a13eba23e0f7ffe73729cfafecba603e79476e1820e0c10bcf964e96ab398f309e478693b02cb2c8cd0b712fe32903ccb3a06855b69150aabf93d8f6be400c3e1722e266416ee9f6a231ee8cae54f5462e9e5e579ebb8e10ada1d9d26072478f2ec289118ee39f9e7ce81de296f61224a4c1e17be395b7e3c7842fd7760456cf2fe9553d32ab83e74442f2a23925278416310ee5b600cd8df2ba1faa3cc5f88c90d389ca492098f0b0eeeb4240d1395fbafdba43e089ee54384ddc0bfc81ce26312a9b7c44a4483f250ce15026ef7bd9a32250577dc44d760ac1296c3f7c7469662feec83ae7d69afe991ead1551bad3d564eaf0db7c6cd24d2a51dcef6acfabcdec64a57af741694cf3b3fe93c59d020bb25ba8208f4a482d7cc8dc0b54ce7a80a9fbf459403fc57812d2a20b14069ba30b99111beaf9589bc7478fef9784aeb5ac5da00a104a2a3bfb6843cff8109f7ff036cb1bcdd268e95bdbd1ea4560fe7ee1f2c1d689e47104b5bedfbe2f1359f38addbb24b84f045047ee9752a302d1c26e75f1939639d7d9ef9649ca68cafc9a090cd27cc5f2fc397e13426bcda935aa7f6401d7887f432f52348fc62cdd2b813e8b198aa300a07488b485e7989b9ecf9e30de8d11791247c06336c649afd894c56502963837923a1d2a4113e72eed675c68adce6d897479372c2481d4b86ddb63f45521a51a0ba31d29fd5a586c1e5b5ddf258afccae8615bf5ad1b455c2ccab0d77d497424e3dd9692efbaa7172631d76956d38cfa80c00db401107b210e0dc911c95882ac1a539568315a5575bd9147e6a155cadeb17b6809f026d328160f0cfba0120be6236a821d7d4f47f76ad19ff0bb6d006ddb6a02808d2b8b84484c2dc27f575e0767cfee2a073d8a162ee2993571402df60df9e6a11772dbf5573aab751f8032e5cd2bd60a8ccd700bfb1f26d5f7fab7caa72f44fb59d80412f517c04659897a9cd907caa092702121621fbc4693792cb8c4cd96da4ce57995c04b2dc64fb0da181c5991c2286cff2ec0f79ac4dc094973bb53ed58dd7cd311ec9a07de54c4fd557f802e84cbdfd408f883396a4f7adf19af38934e0adc2f1d26bc47e1f89a0c927204f3b6c9e421b6d0955adcc0904e950447b7b39e3314e2f16359b918880c3d40486f0b34ae95986a1612b866b75577a65ece63c78f7119210b0f7b6d68d53316d67cf0333d678ec66f96275f17deab246cdba4c4fc105cbd68f2d9eb2f1b7b14383473595a5cfa84195b5d66ea4ef76a6829fe8a6ca1ff3c43c605c1127bdbb9d21fc0b942bc1142a34e8863b1004cf4ca3834ddce99c5f34cbc23ee3f3d19609", "", 2, -387096437, "0c594f9104cbe763cfc0c0b01856c19236d204e1aff05f1c67ef7bdec54026fd"], - ["7d59287903ead37eb1114f34c3c84ba0855e377b6339aed82c1e8bf58bcc32f3b23f40d4350300000005530052636affffffff7a21b5cdab674d88b4b625cd8d4903ff6c5f39c46fcf7e8a04e603e70f49a2be0200000003535152ffffffff70d2f99594aa6f0dd888296a2fb9872bb6da1547994fea73a38188b3021926ad0000000003acab52ffffffff030245e20100000000008ba4c802000000000763acab656500653f7429010000000000fce6f85f00", "656a535152", 2, 1730337781, "d10363f7d78870c1c6fa9345ee058e635db2cebbab570431c817cab2c87553ac"], - ["7a947e0e0407485fafd7a03e8ac64e6ac6637c084ac2481fb50c9f05a374ba1e3844c4beb50200000008ab52ac655165ac53ead13a51424626955569b4bda62af6f3055a9106fc9a6f1afd846aab2bffea63e142388f03000000025152ffffffff15941f33a9a9f1ca9a3d4c97abe99753fcde99dba8646647028e97d492f97627020000000951ac51655265526aabfffffffff098cfdb908fe5e09f0da59eab436578ba9e88ba113c383723c2e631fc7d6695030000000300ababcb9fdaeb04f1e12f040000000003535152354aa2030000000000f91a50010000000009526563ab635351ac00911ec8010000000007abacabacab6a520988139800", "516a650063ac63", 3, -1020794756, "048aad30e475f2bd0cbb166d16bf3682b1851dfb1cbd7e3bb8845ce446ead925"], - ["f6577da801949a2cb73406a60d1601244381ebdc07504f4463c3d007e94c81f0410ebe643c0300000001acffffffff01e76a4c030000000009ac63ac6a00abac530000000000", "acac0052", 0, -1242433645, "c311b0bab09ffae0b660ff006f3a990f0c1a0ecbb4f87b9e18f7b44e1c5d3528"], - ["0fabcba203876ec086455014fc07c1b585e012625d37a342ea975b47963e274979d82ba1b40300000003abab00ffffffff62c17bf0ccc1b6150af39f4a0f87923ec0455ae84c25f8561dcbe486bdad36bc0100000008006a6352acac5365ef9bc61e7e34fff6e5294657883854df1bd62255452b8ced8b918d0f30a691929441eccd0000000000ffffffff029a74d403000000000963ac000063536a6552ec9dcb030000000004abac655200000000", "5152", 2, -231833641, "507d8af51deb25658ea60a1f380090f5865a6a4f8b695a8aeeaba90b3b613779"], - ["ecf12beb03025193af2ce2e17243076f2a88fd32c37b6a47bafc56e716bf81114a8106698502000000086553515165ab5363ffffffff25b637d477cd53241036d89588e36d56c1e1faf0ba307221f04fc1e520517c6e0100000009ab6353abab51635265c93c729ad88df5e763b935e1dd9cc8e8d1d64b6374206333af277b9e3696bfbe0da733fa01000000056a65ab63acffffffff016590d103000000000165fd30e034", "6552", 0, 1706991474, "8efa29c7ad17453fddf17d7e52ee315cdaa44e0998eab09872305de9653e13b0"], - ["8c2f48c6022fc922383992ddbf84c4c6eb98e60d4b7263ba3859a5009480f0b89a4c3c982e030000000553abab6a5311ffafdaf14091da0ec920ef211f766ca8a0df8c9095c3f898b2574d646ca40a61b39a96020000000253655ed1397d04a152a505000000000563635353650ca5fe02000000000351525150e4c4030000000005006563ac651c1808020000000002526a6fe89fd1", "", 0, -1230454624, "24a4fa6c9f2644cf171a6bc02249c8601f25b98fdf224df36da6573d1f22f8e7"], - ["8f98e62304ed74ee854d7fc769dcc20aa8ee29a92b737aa8a5a61b512e01d16a024c8d7f410300000009526a6565530053ac00ffffffff1fda9c62280138264fab7e52bad0a35399ef1c845a70bab03762ae11f4248a3b0200000004636353acffffffffaa88b4856def669ccda1f24880069f05bab490ae1e54656be926b8d23279997c02000000002ee52173f8a8a0101d4b46b68c68855bcdb08692d11778ed5bb038c7a77e4fd482ac14600200000002ab001bf871a704ac876a020000000009ac5200526a520051ace7a1d10100000000002d7e1a0100000000035263523182d9010000000009535352ab5163536363140cd17f020000000000000000ae453c0300000000a09cdf53aab2b36ead5af9194385d45ee6f22ea4d34491c499ff7ed6c46dbddc01c1ee344cf9f2ae6b7aa10728e374cd0210a923710e89f4294fcf03ca698bf9148a425167c704916fb5256d3355dba02cb913c93cb70b977bfca059b01c7998000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009346b5fe610bb7be8adb1268f7467db021f42dfad98ed4c63ffb5b98c507cd8b98e7dd404d9d7571d826a74f555bf49d3d40581ab548b36814409c61cf5d1f84d8442bc7ef453f044f3eb5d99aac3643f03a081f1646f2233623e3748d5f4c048af44b1933851636540ad93f074529ed002c17d1b7515fd452a9b53a289f4362032a05dcf3336c7c2649df837abd95d8607d2d9355c14a1f818b7a7ed2a88ef665030d9b0d7aa8b68b070ad129bded178332872a3abec762ba5b8fc1d2e12bd500c00a00eb2b31d281980be20f46a3216e746252cecc059dc0c095e0127417f6b3e3ad0e84b47e655e790fae6a57756217dbe11f86192a1a02658d72fe7191420abb4f031e747734b2de1d299c54de5f6d367683d9e1603816d11c96f69b04147c7c1efe021cc2942a07b80eeb9db65290f3b82ed454ff3c549d3eacffbd04df0ef1c67347020741fb2356b47410695daa809fd20f3a5b50e3a3b4b11ad953e3a3c5a751f33f03190b7c89742ef19c04c9fb9363e51c514486a2b935020e342a0c5ffa786249e503253c5da0befad18f8e1bfe8fa17b7fa5b56da7397095bc63b286f067ef5c36cc1b6fb5c40680e6ade0842bf529f403cd1f0e0689547dbf929b4c9579956b91fe5f53e8d6f2deddfac52154f87023463c43f282a7ece27629ee7c0f654dbc6a26b12d0f8c61309dd67d38fc8f7db738d9dbc266b6480abc1d8b88c4dfa24c74ffb5303b2fd5d5956f3c96e8f02d68b13995b5424dfeee259b9261891983633ee02b4747d4175701270bcd67b8b8e96322ec45ff7089f1c93fd4cf5c78c547736cae0615c1b051e8bb1f9d62868034f6d446c70243d14be9b78d5edb309b75c7d48f712b954e1a90b8b8f6a45570088db42a697d52c3daffb1092427f58b2a0921e29b30a625247a8b84bbe576905a062749ef83f0d0978f681e355413a9525f14f299b49ebea103eebac836b56fa9b55e9a6ea15474d50c430b5dd6ab4b41c0a7c3e57430037f13ac40b2bed95c31dbab1083dbab79c0f7b36a684f31f3d8a9dcab8629ce300950f01074d1b86871a3104640fab784451b90c487743a8f91101bc049c4fa480fe0115833c2b4b53026ed12247fd6adc4b1d4b3c5de2e6afcea620bfe12e4c8f99c5a7fe9911f820914075ccaebd0115488da6f65d35b8cd77ef54dcf0ccd453c2857423b0e576cba6725587e3cd34bcbaefe5edb73628c5282a89e15b8c45bbbbf355b39d5fd28994bed8c3bb944f9075ff61249ab9696dcb1342e89f7fd32cd418addf586e85996bb7d6eaa26aa102793807550567f45ad3605f4b77f8e560534f7880ecbe1890e43361f53c455a080c71f031409755cf48367e3397db72da3b1b3069d1886cae24c2be1392a42b7b7cab0bf5e083f85c08fc4e26fe6e8e0422b18591719df56e403497005f1b821027bf80eaafb96953d2736ac18aa7a8b703bc83e2ada1be4dbf9558f83b313a36cdf1c1e0fb1f78645bd4f4fd277218ce82049bfc161b5273c128054d13c7cf0f53bf490d825926a53f349a564376c4d689967290a53277ad3b585a1084c262051cd384c3339f7a25d76448390f488245a1f49e37432d75302f3aaf877d96fcb66b57e01c136eb8fbe4d47a786aecc17e9e8f349c8ee59472679c0f728087deb9d487dfea7cd52dc4114148860d9d7d2baf478357da9bfded61dc9b1bc42baa0dffed114f605228e21eb0cf0b0d5616f9f0b4f4e10c4ade900eaf18f25d675b8a5dd78710fbf426c72adb5e9aba47145ed089db8cc0c30044f8409dd44076cf414b2efb159627d5a04adec5756cc86458acd2ec89c9d037ba825d7816bfd4a59424aa6adf53bf1047555ad4a4f93f56695d286fd1ad534b9bab2f003e9bb1b916796d9b2b8026a6f9e67f4ace7cdf4f7668f5a3df7568a8bc7b4793b10afe67db2863d946ff142fec6415570d83808c41849d45b33abf393cb1b20f4da892ed494ac4f73b47cf128607d16c40c63e0e5590351703007a6e7ce189745e313da1a22dab4437d45805a42552892a6670f9489bd97809fdc74af3919465006983dc6b07cd93eb56e01d72b039d556d5107902241b59615e539b2fbe1af39874688712fc84ec7060945489107b1fdde52e934c999d6487c543ec4e6bb17d9ebbeaa2533721e2631406f3bf3ce55859e3e4388d5eb14c3e57ba802208e2aa6d7c20e45ff44891cc73912230fd97398924b419de325a30532befdef5e81eebd77e5fb80bfb5d6945bc3abb03735d46028cc59196cc626b97d4ad40f24f536177a1a8045050000000000000000000000004a11e9c70d3bec0dee63977897269a1ad97492e9a43052ce1253e1554f96b6b2efe482a52bdf363845b36aef62f89f96f74981191295969341064dbdbbb9ec000949c9dca8912a7721ea25f27965cc5abedbb1961c55512e83cf96a934ff5431000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008596e1099e37715c16b714432150a5de70b5a7c811d152def3593c1ea30d07d7ecb83366d4e380af65e3a3b987c8037a9aa91d022584cde4146f82a5af5d0e03b9e50e0fdd8808b28ab253ad2ec4c0112954a537d8af7ce42f68200e8f4dc1ff0ea56d882b0bb219f4c93eb84de9b0026dc89494d74b97cc33ec3521e1be8fc0030782dc9df794364d62961538c597c3f8246595edc231d5281efdd794a0494149021f07334552a608cc8e99873eb21b7d0ac3a5500c186d39e44da42c9d409791940a2e4b83fe57bb7ceb89cc38d178684888f31dbd18d3b3cdfc542cc9332b2616652b832d9429e1b47b1d8bf147adb32bd6f4cc7faf635dd86fefdf8a6ad3a8a1820329f8cb5e934fa681e0e2d542f0ee8c179b9deb9bbb89d1bbb16788fa3e9962ea032ca24b85d007df6f32c8291bb0d172b508c5611d6508c5be98397926747b8ed80221fd678dedc7cd5cfafd910236c69dcd7566dc3556920606432c86109563a2590208ff401d054fb526a1b39de79511d31c8d46392e6587565188e61b17a862917a022c12274f152b186bdd29b2722b92ecf101b7b3677cc37dc024dff6e0b182efba7ff5edfa87ec22850481334cddce03fd794c626fffbda760ed63edb43202795ea672650f07f7ac9c498ad2d7529d021f92bf07677e87351ac2e1fb5ce7ffa26b65d8ab67cda166e18f6579dfa686cfb814b9d6bb07a20af435e2bf1ddc7b81d7620634a91c6245e103ef3e62cb50ab4e60292409a877b01653f5079d12d2f296c8e186b7dc28ae6245bd31f72db24334f81270542ac2b33581c4dfc84518888c3fcca56f3def017a30bc7a8c0335b6103c632caa490bbb8eabe34d707332dd1b19e300766f59169248dc59f2cd1fb82c318f116b737924866f47fd9bf092c193db010cda003f23c7c8b4948eb90bd67885dac978f37da63ae9feb63c4e89bfc79be6c119643309db8a5eeeeb3b44abf064f6b92d1e96e8678b4ea1b17fe9117d5e13e35a0912e2df0236e4737bf0e139b20806cf2a8f8b6a629d45a26be86bd1086c6bbb7dc6af4b9103ba4bb9f76fc91e080c13d949d2aa9bbcf0a81fd2c9e6e82130887e43d670ce9ffb660c9733b5ec0e1f9ece1d270029ef560340cba9e70b61a589c350a07a49482a42688cb60bb821f8e92c716cdba5e68bd9a43f22c0ab8681adac5d3273a07a2325926c3293647b77e332cfe0988c6fc72b076a50ecec396f4336214d1ea8315fd4cfe8b159a1059aecedc9c1b9755f154e70463c81de9f772548b6a86538578361050d293677325a86009b53ca55e5ef9bda13f148c5b6558e9a1509f4d1eec51b4925cfc6aaf64923744babe1139fef3b84bd670c3cc626a57654d52fe3fa2b66392d1dd53b282c836f2ad783131e08c6719fa997c1aeb62a2db02e2a81f50573076ecb1d95b521fc7dbb99f98861c30fa5b9b4b072c3347ddef71dd9cbb37d56f6edaef7e88799a41e74a76b123a069409ce4526ea1c0de032dba705ccc88612bfd0d0466fc86131fd627e1225dfcb3ffb048354841181e53bcc56a4b9cbb8d34471c5b54d0bd6ca9d47c4c47bb1d9feb2e82dbe351e0ff3de674614887effbe140d4cf25cbe3c87c31e41b8b5c737b9026bba770ab9f9300b053b3e3079bfdfe3f075989bd303011d1017e98c4455add242fdb601838f1a8119b9372bb6e7f86abb7a160d4b0ea41a1d2cdf1966c9d99eaa90a17afebab42ba11a4d7b92271809de7fdc090a8bbad16e323f194d61f5175f45639f5fd54b0b72ea931e613b1e4ab69a01550f26ce05e074f161188163aa78ed106272eda7ef60383334ae2404819f86e14610841c0c4873795ddf1c646374eef2d5e5395e28f251540128aca0b4ae1a0d7e95e2a13ad5c008938eb8dbc2e1c99b6c3326173623874d0f5643da268f8668fb6e62c82cdf3c1758c8a4fe718a174721b366c5476cc71edf4e7dc155f29627a314178c8abf6c05af86eb41bcc93b2ec3d26510b2f408dab3f6f54c6da28ce8ae677276831f5ac8c3cab5d61488ff2ccdc632854c6fb2c80a819da7279325fcb57604504f74ad86e219c0b27e4a13c6f35e7b2b368fd5163e88c261134c7e64fb067004948ea0c9d4908351bc0de2331c65b2a999787094b57deb89f9ce72ad6a601a934689faf691dc1986ab4a544634f0508554f3d966a35ce5d319cbc15e014dd6c35861f22b55e8c20790068957788ab2c9f9406da18b5ccd974c9b57c905bd85592b8c2e6d59cccb1d267f9ec94ad89db04b40f29b62446893ef537f838a2abc835e3f0571a14587e2f8456b03bdae757556e6ecc22477e64432a499c816f1bb0686bb692a6f0bb596616c9bf83614479ea04314a218d0f1bea617554d065f60fd168f9d912361cdd56d54bb8b512202a1ff4350decf38928f897cbd370b09", "52", 3, -677808630, "084b6c756e78d03e5a3b17bf372962f737c29cbc54fea86ccc20f109efcf3bc6"], - ["6b791808011b103bc1d2c94fb7ae97b441de1079669e5c78edbd6f60d4d85c29373a3466020300000008656a51ac6563636324eb1fe20205914b020000000001525e76090300000000055265ab63abc73d02290200000000000000007ac2ac0200000000c294010d4a3768e2c416c9488d17c44bc7320e1d6aee5f25a312e7a3d4420d1f166ef704dc4db28149ab1c1285e08451bcdf35c6dc672ac351c7a1bb7d83ae0e374da923c0123e52cc9a2f7c90077aae0d7977628bf7bfe4d76b706aeb2773e7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004f0791a86e318910f88216a120c6cf4c93fb128ce1b206560efa5c1896be6d97d7bce8a9821de90e6983b3698b6540ef7aba9995f782d024c1401fd37919d7dd4e17fdc88e2b4928681b05b277c53e3e1da255cde979930449c9f280c3c29e633c3f30d7dbd836b39e7c9d19b90d75f706ed8554cbeb0fa2a22f9d0a3a39c1620322c20bc4742bb8847373e91c26e5a7b615dca2cd270659eff85997898f61ef4c0212a519cec850480ca01d50492d42830e86593c469b77bf24e5108e5ae2e0b0000b08bc1237acc34890987121dd67f7406fed90276cb5aff6140d2856c7775cf4560669d993fcd0b47192c689b129556e4ea8f649ccc493100409417437350f1929032fe7dc6de416f4bd1fdaa7b74d5f9be988f829eaa8db3f88cda768f6a5e6a455020ac6052f09d25d8fac5eee79d036470ea6379ade7f2bc516d6adb913757d5de8021e6f29a403411d254209adf2b750bdbf082c26de3b98a1b0bb171dcae903fb9f020c8cf2aa06e2eb34fb69531778645bc53db2848f652d144642053aa27f850e430202e554102367d42c5a175a4d6f16a685df273f8a2dac980ca26f496debfb515bab30aeeff3bb21bd781e5fdedbf9808d764368de3607b8a4061b083d25fc17fd7426973a4fbc30314adcdf20cb7c97427eb34397bbfcf6258f74dd4d1493d77e5761569527ac7eeeba6151ad5794d0c5a4d9e8b7b7fcba4f9ce4c9faed6886b13cde1c7e618475bc069688e36c3388744032ca50d9f9d498ff20ac6b30975a9d7551bcf90c08cda4b0d3bff7142c31b6f7ecce16454b69c3814ab0016f9215242d326e8f25e9c9f8c45f7cd488fc66fb59df728dafdb3382c69ad390cdc6747d0e0da0a5a7fb5eb417e0bbce8d168ba538b3905e3cf8ddcee430e331f62b7011b61bb443504cd7238becdfdb1ffeaf82c67fec211bbf52ac94b3d70ba164065ddc732896c301337d0a419b91c730c03e1ca13267a6c203c990ec35f4480558b20de2d1575538d8c729149cc3e27ed7eff1176f76b06a561e4d406448a93ce461bc375d33a4f8878fd79d2a6e5bd07c32f3849195fe2481d1d7d3b5b215b97bc0b4dd9262281cb98c2233292612b0daca1dc068f879b8797678e66cbf7064134e6d2c144cc1961b3fc144d42b8d52e26895b6d565fa37f3018830a59098be18464839e6c5c8790917681618f03201a69dcabd91d7e9934a78aac63b67d630b4521b76c4765b29a1a2c42f75e44dcaa81975e16972c5aa1844aadfb38ef87965cfc0d083e80888d92a6f84eed23c19f291f4d4e6181b5e76daf2a9cb326bb84567f8a994681fa1e59053e33cd9175c35699ccba45a8ce792d74bdcd003f770c39843b709f95ba9e3ebe27b30fbadf99a1ebfaccf18054d43766c09d894dd7bf5e533778305f28a42c96d7c4f1c6b9e0b82ea60664fd6b18fe06ce1cfb6259ae9573f9bd24be1c4037170a88018255b1c4861461eedb95dde67be569cd0255c7e2901f8e79f7b83aa90e2e011356cca96476993897ccb8f90412105b3a2c78b0930fcbc3b7d22e861cd50ace4aa38c7bc326a5c101cf612e083dcbab92e4fe834aa2d4ac88c9e064f75dcc3af21d19396d24c589a56ca96c0e51cf9d04128b13cd81bccbfee08464121acb4b851330ee1c180da2e3dc24228083df688ea496bfc9173221fbd7803fc326ddc756fa5c5843f92dcdb594d34bc3ee65d46162caf05115c7e2679e2a82b9cbe450f884cd4cf44941957ee931194d8050c4069dca67ce8f166717019dddfdd8064c8595604e6de341d5427a881544f820263ecb9fcd4261f4d8e8d632de9d2b6926e11cb8c205798f1a03d4957e8c1cd0e23a20e27b51d7a880be11d3391fc12882bef83f912bda3c76936b7e98615e8fdd279cc249b75d37c11f6e3fdf802c1c1e4b6c5b07f6506aa3c7dfb3e0b3aaac2d5dfc07a05f431d9b7d1e85c9b569e4a604220647b30ab8e0e9f323be5eded6afa7f6f5e8ff3fa1dc531a126c0715320e2e9426c78996dc160149ae99dafc3e03a31d98c0acd8dcd06e07c06a3ce3a2342ec3e745798e8aae39fdb41476665dc0c9b82a48ae08780a26647d89fa76e29bbff605d27f4bc08bc55d563ef0bfaa8e7bc642899f9daa8f3c15f18c7392b78a0904d4d26892125e2d256299c61e8c3e48412921da54f57c6f6b512f27e4a3d2d580b3f4445228bd10992879f9799d38e5cd44366db154830d2bb96ea62cb223a79e70be3971d1fa535730c3e4d6d2089e878fcea40d7b420cdffe22fb2719153dfa39b5a9e67c49b09e701000000000000000000000000bc6d1ed50e741067aa02fbd3ff51e7e363940ed582023831bc5300afcf0e16d509a8f08f224aa67e317a1425f88ade3f9263b969588b1dc0b685915b3e52c509052f3c18d250c1f6ca71c864ae729c078e763fdbf44b56e211444d96f21009bb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009529634b4cbb1684ce396021455fdbcbf4d6e61a0c3cdef287404600e586370229aacaa84423dc1770c0dc8b447eed1e8499adb172fcbc6685a40e72b2b82d502932b75a7cbb0a260d1466567115270cb53d77ef4aa728f84f454af2805b0515eb424ab82387994dc445cd83919b9f0e8087db37181871529ef121dbbc7ed1ca0315f68963bca0ba9964f892c7b402f7ee9b9f9dc045494e6e35c7fb74b2f7bab50306e0c239f18d11c276552261b681ae2ca0f1d73b78750adfa351ef2e8bca48630b1e8401cba05a8dc6b326fd66f3009c05687a0fa601c86a8f774a8171d00d056c158a54f589b6d1c2b102983e64088f205749258f6627b91f050eb26a66f542ec022962192c58d47cdc491f1d2ae6c2799086217353dee79221ee8a3164d108cf79031f8ad5defa8761668bbdea74ace95f698b88704f68b6e2bd19d9cddf0263c2670306b697f6037b80d59cb371cb5221fc3ce7ca3c58212547983f164c7fecef9bf1030c02ded10afccbdd4677320f70799eadd66c284ae4daf339821d642c6e338d8f0210e9908cf15dc28d3ebbefadab1b3553144938506512d247400de3cafb7e5ac9dc9f34795a7fa2e46f304105756bdfef422ffea26c18fed1980335660e7b8ad54d102012a860b1d6c376da2f0bde0376320507876e6a2860f1d5e374411ab3279e72a1084bee1b50c04b8c81b3c2605937fb37b9d7aaeb0bc4cdd93215fe88e126f6874e009ba721b63c361ebb9eebc002b4744939215ecb5aaed0239ce26cfa2db0df98f775c8592741c13437bd929daee9b6323c233901472b748e018c681ca75d634054afa3447b8544b0b4dfa8a3d60464d9ae3d508c42ffaad2faad66d4f95d90d771f8363d24758b0763395ca7e8a4108e32aeb225991c4529896358ce8612d29d43c75818610c5224c41441340a586ca790e2d05815e03a5144f4b42e93bf7bbf8cfa952765e9348f6d1f744895bcad0baf7b220dbfa259478ca3c1ee32613dcf1f8201ad5c377afd5a71aadaae981b7d3084da68af0667c51170f95b2f096771c48b91d400cb0763cca8e8f182b426c16cf21ee241b86a38106c07c0c4ae7ef191eba885201bb8958435b3dc90bb1936a74043bf0a5a8b3ae7677ac2dc4afca239df7653d09cfd264deafd8ab66f4b02338942c1900721eef45cf5b5b751ec43d6bb93818bce83a58b601202adca9cf845188ead8d78b8123d30aefe933bde822ab425004013c1427f12b81bd15fa45ec791db504f156fcccb14477c1a9437eb14d0746becff253dd8c897611596d7fe0ad056e86771b29c03e78a912aa94602f77c9a9ba4359ca227d46a831a57e438870762c7c0c989da1ec6b0de4d3db028389d140da81043f63e6951593b23b29fbc4cde84aad7f893ef47820230d14d73e55eb005796f6c9d4f5f0ac9df94745729171ee86c258d16fb29a6b7922d99b1770349b89b6e988d8d61186a8fa2e93a81a15f2c6a651168d0a2ca2af3d493c7f3520a40c6546d7f739f2bec9587de489d2861872ce02bdeaec32d8f1a858a019bae413260fe65a7dcd1de9f8e572d28a906f4c6077331d7ed3f7f87aa7761d88ae76621edcba95b3c575cb030716c53037c376ad149d2ed32b02d1aa83d927a8482619c63b6b39ed062f17cfc10182739738d8fb5b8d0e7ba39fe60ff47cc617bd4952b0b6d6066ebdd4f99994d0b27d0bc797d7e57b6619f92b3238f735dc98c522d539a99d6c781cd4c783bcab48673c842e8085b48432a269e5435a2f5902ca880388724a1c1022096f96344291cea34c0c4abacc6b7da60f6f6d7d5a5016e168a10ce731b78f65dfd20b3430ada860afe257aa979ef359b7dce4fad0d2c3514962c8c97798c774a82ed86a8b9b9003d0422cc0751e5270394a916094de63b08f95ce027200a19ead2b93299bcea2675f1234f14bd92b5bf6129745d68a884a7d0afbaedfff9fc4d4999a7db2e767e3777b7faa5d5dcd5d71872a81c4618731c34d21baa22eae8f5a81598283c480f8515397ebde7793531e4b3ad4b4b4cdeb594328549187e01fef56c8637f863ffd0e678add35049576566e2b98f5de0f80aebfdba903feb7c52c4230861065927068245f45d9496d8bd46d724c5baddcc8dd9c5f94e23d8d51519aca82706aebc9da149c1c84cbf9a6f3cfa1e3bc2f34904eb8ef7b344ad3ba0547f0fa10f3b938fb1b2b4834075caeba3ca538031b490f1a3747a429d35da89afa3f97bbb2f66e321f68074999ceb2971d2f1fa2cc97e4fa2b4cbfb7ff16c972f49c3df4d8d8687e5bd67f457507a994a5e4b3c8d9672b244aec81d10fd36b20528aa1fb5eb6d5bf8cd515cb3f908d1e487685e05c60215f2d168eef5d5e64e94fe4d2d88afbc38af7a15deb31ada74571b9f3598953ae55553db81f0c7bfb67bcdb803", "65ac", 0, 1298665457, "fb640021de7cd35157f1de86bd3a555051eb85a8dbee2e7b453ef02cab3f057f"], - ["583fc53a0479012a9e0330440f130e23a126cf83924b571f2ed3d3c5b4fad568580191e30502000000001ab528b2e9246ecf458413dea34b6c6939c4b781b3728d507b0573e7d31de428ac2d22820200000001abffffffffeec3752b1e5891dfada7ec63aab8675fb35d926a3d32120449b2e16fc907c483030000000165998e8d07166d49703cd04aef2cd3bb1678e1771fce1aa2005b17f4aecef5779c66beb6560200000004ac51abac3e47dee5030d1faa04000000000151d13466040000000004ab525165971be00200000000000000000000", "5265ac6300ac6500", 2, 1123439229, "55fd1cb17c2ec68f1d7fca1f13760a1a9c2d633c32db3d8b1f59ce501364ef0d"], - ["c25e076703e122a89ddb750f09f942d0cf25cb532bf2d2f08482ff79104c57a96600634da70200000003520065835b0d9bcc2c00a4d15e8efd69471df7f08d83e6d51d9e72f59b531307100ac1f99cf7250000000003525152ffffffff91bc687f6e2e517a924bf67136ba12bf03e1205f75dfa66d1c6da534dfd491e0010000000765ac515251656aa42e68ed0495fbd30200000000065252ab656aacba314c0500000000003affc4010000000007ac0052510052511461ac0100000000046a656aac000000000296519f01000000000000000000000000c970770b4ff56aba303e943d42e2a64cb8723f194f87f755a7b82e241e57d9b62bbc71d341add7773512502f562a7e509f6e7f35fe2a0e7c8f9d43ba4c27f0b3e7060b8d7f84ec0063f6b53bde73f0061184579f634a16210d34dc54cfb99faf0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046860a5debc701ca1e6023129f844865ec0d969d2a7810397ad2f03daf1ea3760d2aa10820dc4478b8c8336c192d209021414b7833821e18229f6508df4ec07e8f11b0633cb3974a0668d2473ea4321ec71e138c369165239937cc6f19969a289f5ac077174b4966e7cf26ce5f17e5469d950967a492a4831b1b0f194f1e3f2202091901d1c34cec1a995c7da50c3cdae30439557a286f3bd63fe66b374661d9a10321c3cc68185483d8ca991751365a5fa1d31aece3ee1e7dbcb19b09a9945a786e0a2451e8f2c7ccb560ede72ee0c9aa99352449c4c13d9d49005726c253eb30bb5a2c06d3eddca104de42d54732e50aba7c4996851834b2981c5b6375f49d2b9ee90300e3ba06049ae326a0dd40e3597d1edac9b6d96bfd63b8bbf59e130dd855ab7c021a5ffa317cdcd3d9caa4b729446fdebafb36044fd7e60ef646e8435d1aa47e7e0328e1b0eeba02fc296e2e56a8845ab9c599e40f66f1a8380bf98e29d3576c999a0319162b2f5b3b3f0ef1316778a7c0b5327f1734625647ef8197680b8f2275ee4c030e0ec6dab45b9d745075d6eb3ac2e7c73523cf84fe122de634661c8384f81bc87a484fff2d249b3edd8a9f06b15183bfbcd60a39b1e129135c789adcd0688f88701b4cc1ba453d02f87842d95dd483e5d900343c75bd5b882d704e3513a2c5f921a32feedf4b61effc114ab81dba9179f9cd14a9bb1fc1138106f2a41cf406a2e69192ae77d6db1fb93990152af10e40ecc8c5433cb26debacc10d92f4df403a4352ce1d4b66911dfd86a5b68f0994576e6ccdc689e153913f50ae752d78aec8a187aa8cb29d44dd9838bdb5508cf8bca229126c8828241353f69b0768cfcdd6395faf90881f5ccf5da41f05e25f4c6a3dfac24f6a3c8dab513cc3d69fb9009a8594cf9bcd5cc273e2e07c402505070f3d5370196a7a46f5b64a844310761336abf49a9a32ad5ec887fae6c4b2712088d9412c0dc2b02ddad0802a34fe6d13487518bfb81137c3fe0c7bc923d4a96e120a311f71a725e58a452fca0fca71e330099de64f8e9ba86102ea082ebbcf188056f97e78cff9c1c361fa52e3f42e612ead5c0b9c33fedec3b311c0044d08b2b0199dbf53245a1e5a0f64560729a92a73d96b7c6c890fbdc19dff5fa2464cad15d5632b034aef13e9dfe217ba21e5e7c8e5f2ac9261dee61c894be8d1cac778e8f1f4440b85d3b548351e326ae9d398cbcb74f13a10d66023d461b5e3ba9ca1615014968ada2ed60ba00e8f1552fb00a49e2c7815b06398c93af1f593c5e0a35d3ca3f0d54c501335da1159a11cebce519bd5c412ac3c2fdc1e34cfc76c8dadfedcff86f8eac554c9650ee65eef07d57b61adf259126171e89847923fc8a50eac31c70abbdfc136f53d12706ffdcc22c80107e146b2900b2160a4fd01a06c68cbc3264eb6dbee2d404a9fa6f8d717a703471cae02ad1c1582a60ae6f3f623056de10f62d49c34d0a6cf23050ad583615b5a19d0d56e959453dd7f2d2e3fab92619c3866b52c718d9b17528373ce139a084955bf156eb0dc294d2a22001c8f4fbb7a869c831712aa89e243c976e0df44bc213308f4ec4e27ba62f4f69f132ca931f08666e91e2f3c686cbc3c77e4fe107e7c584cd39e95e80db715e24d30ce11d3bd843babee178ffa3e0bbc936ff82e2b3f0dcdd8bf36d8f12fc7299d5eb01963cf872c816dfe7ecceab228321ab4a9145ff81eee37c09c248e4a5e2738f6e39166a4cf2cd639b5c451c8a1860f72b4e1c8beece360409fdd6b0b88dfb29417128247e14fd0c93f636059af0a668aadb77ffd78d311722fe86dc4d50f7924974d15d409e51f3266895586cb773a95abb19fec022c35aaf1a15fb248ab03c5fee7292b6efc43f50aa9f1bdbcce901d6a1fdb960091dbd5ed33ce2e013bc97a622e0af9e2274e2d35f7a7edf5d3504e994ac466e6c05b54963454c3816326a73b7538d991573ee1648c9cb84b1b59a351c671e08c3f5cbb98c9feb62fb3d3e29abffe7fcb8a2c27fd8316c907a2d3588679e80d59e7771dcc9aa1cb38e4ec6ba06b72255a750793d18da4e16bf12f38f425630ec11ff33d52fd30be143bbe795de920cbf0d0764ac25c35c211e79244ef846f64abfec2f1e9d26d798bca6726f4bd89ad97390dcf61c6fa0fe334e9d6b518273812a97b0332a3f8ed9e09e74010111829a5fa99a27c212793f18cc749809c8d091f7902078f46754cfffe4274db100d1a5593e9a7c3b304f9a5522193cbe1f199537ba2b3ea0229009a0c2dc3ab8daf1400000000000000000bd8f80300000000f0543b0ed18a378b59cb7cab73c3f9615c3e51869ec61b551b4f419a8d6d55da404b80eafa701e5e6c8fb68fa6a72aaaa2d6b8a856fb3570a80533966c1920c2ec82ebe76d5fde7ce0c0a4ef3a2d9e722bee12a10cdf774f24c3375ea776262a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004b399f600f7a36e83e4bf3f5d14637f9f42fbccb4fc28b40b9a298964ea950bfc2babfde36adaf9b47add9be6ea932bd01233f63d29d1ebf373d15376a385ef638bcf382eabd50ed872c659447a6d12f1808cf32986be62686bc3006cf5fb1789961a3c157ba25be02dfa7e886bf68a96aae2d40fb7018141c1fbd66bbc824bc020575de58cdb15f6b0d4c7d18301b0f363936740db405153a2dedbff4bef101c4021d6686b2848419017f1baef950574335f893f48923350c9509f2c9313cd4c8940a26415c8eb38f34e29264252a1b56eb96996079fa1e0594efbf1f5b29b6763c7c21bb9f6d68fcfdb78447cdee785fc1e901673da5318b31a02f2e9d02193e01cd032f8992ac20809e45fc3d2102a170b7b62e1c9a646fb907d7ae6583b02d8101ef02064511eb7c4502533e51185a61e8e28776f5fd4fc0828d53a5a6ec6d36c7c14b030d8b9ce7d13088f053f9742a299ccb7085d1e153bedc2d029bb18c19cc7c8e5b0319a78e90039ec1a443db987d9cb2491f0aa5649f403996f41685a5a88ff3c7ff031e39fc968346ee68f34e9246493b3dd001e33485501c23dde16ef4ba239535195bab95beb1f972fde6689b5cb0c43c0e896128b3282a4c0a0e56ee0a733a8e48be596b6801a9bac3dab41399996017e658a06a90247b0e01a1246bdd421e36ecef69b501aac24c76c7cdcf77a5ed6bdde93f46d4f37d7f85d117ce03115a43c8ac16f62c093b6ac797b2e416ba5459561892369b62bdec5e6181b813c4a513fd056b0988182a6055f18f2f41fb10b3b3dec7790cf5d9407fb527786407ea182bc7634ea133b31b3278f8f39674f4841560a21017c8dd20f9a955c1baa9e6bd2532652410c30b7056043cb97d53a2889c44e74e1e9fa28fe00cfdb724cfc280865f18087623fd4916e7772bb4c3e9dd5bdaeecaa424511d9863d7ccbc5665c27975a67a4e32a38b3a4b4f794f34616ef7c99b50990929ac1bfe83288b39c5aea6b02328e217e6c1f0988e9cfbf354a9b2cfdc8e27cf0ed6ef3e489dfb07de66869ea2bc7706346418f4882f53d3a5b87c5c88e73fb09e2b776d44d84fba165620633b67d7c53c3f6929a11b3e590280f0c5b499360bd5cc95bd5c991586be10482efb14e518bf6654c183bc5b2a3955d24024131bc001bb63774c391578488bbfbf91795dbd67c1273d61442e380e536e11b82ad04fb0d39f821a7e7d2b5c5a3089544c448177b21780299a0da8ce188ffe95acc3ae332b07edb63bafa1a931920edf73c3131133f9683e111777b4ef19ef6b1b50df742a2fccb09c631b9097d1f518cdb78e299354feecdae71442c01d020c00b2a6979bd7c031badd90a8710aa4912bac081819ec8098d185592a77950644db0d170a3ad5fc74d0e35c42d881422b02a724578c547599259395ddb6767e6f2ea29e2df1fe57c5fbce458bbe5850ddc27ec33cae574d3cbd8bcaa18ea23b7f12c2a2a28ddd04a6b46b31c484e87e5aacd1d0b8ca8460159d3a5c76dff3bc05924ff9f3e48abaeb2a9f3b98711b0c47cad069897576f6464a5dc9868e4cfe92e51d275c847c4abcffc6b9bf2de8327f3b2f0b5fc9cfb66d40835fb4403ab2604a9b1f3158e19c8e9eb72cbd9d4dbf7e05cd7aeee48e03f4d1a5fb736b5bd45d469ca918c0192a2adf34ff328c82cef96332b9311f2a1c45edd24df93a5da0c35f1409265a9a224e0360d97bfa07868e6ee3b159cb9d829b8f98e5717c92cc7c0fb16de2b99221664797724bb3cd7bdeb674dceefb978e3825986e950d805eb0c92bfe8898b4192967ac728a3f8028fb0fa9e4d87babf48172d536bc8946ca29e79d06e71810193c1a0e367578f925bf07ccb54e63905521e3ec38e7ca71fe32f53d6e0a15774c331ee799357d5bae9501fe6baf3321c8761ea2d397d70b7fd498afd69684c0f82ed71ea047d065f3795966033f274dab1fa00adeb478d4c47d6036d8ef82c6c625fbf7791a57b6a340566eede2366a3fbf8d85e33248bffd6251f3c2584f714676bee7456f0ce558a073dc7952bce9c9403cfa0ff81ddab9ad989ed9c0ed5779d4006b66d214ef708cf47de9f0b34ffaec3ab9bc4f7371d839aa7204cbdebd1802b80aee71db38b72f13051ff954de122a406bca28ecd97ad2a7511da52992009769a2e801b3c5c5262d7cb18e4f9e1786704905548b0ceee18955f2ac2583d8ccda391ce8d59f53e9dc9620b865393211593028b4e3e4af39dbff7fedb02e42b2ef48a87713405909fdbfec597a5f545273a38da06d6545619aeabd2303cf065fe4f4e13c7ddb6a4fd563cada3d9c7a0c6f81f30db8c29dbcdafd7e8d2834fa3ee4a14aaa79fb6054e65a368a9ea780763faa52cf7ad9a4249439e0b2bf3ed1bd7d701948d3fc99f67ad52004f9630c72406924cef9271802", "65006563", 1, -1138087830, "0dfdd17d9153ebd22e8142ea8baf4fb86a30de395c335032865ad81d36c0347b"], - ["c3cf14eb04668efab16b4575c7745340009a2d72c9a724b69a959fefd8d6d3c9e1f2e5928e02000000086552656a0052656a68783a111ce036b399b6111b1ff8f1270b3944826a1efa4639edeab73f8af1e24f3438d80000000007656aac53ac6363ffffffff149e779af191ea7928be6dd8a37b6f4ac42e5c10488ffb3ef4147bb3d48226fc02000000066a6563ac5263ffffffffbe78852b579c2d15a20c3c2c467e9c78757c9103cbb3f5382f4e298efa2094cc03000000030053acffffffff0144aa0b0200000000066a6aac5165ab00000000", "", 2, 1379973953, "387e127df56d79965bdbcefbdcc16532159c46f2f0496f97e275dc11d08506cc"], - ["32c7454701d837c1a6c63e0ad8feef52254baf18567720724baca6117cae2ab7fe51e2ca890000000003536551ffffffff02d4ca900200000000056563ab6aabcb0d75010000000007ab00ac5363ab51a74dddba010000000000000000394a2a0400000000f434862b65ff451ec5ebd2df1c1fab15115af597806fe2d0fa2f5fc1c0cbf2edb1fd0ee00396db936a9d14e097127fc9177e1e02b2aef3a08e79b3efd0afd9ba04e4a26cb7296b96b9eeb81e3897882f351be120873313733dd512b309b5a9f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000073ccf4e7f61c0cfcaaff4c6c01cbfe229a72b2fe594a274de0e1b681ae44fe0d9bd49f1e23c6a9ca38c216b7258b0f72920ec9413ff0b54c0324cc95d023f0052ff6b30f37fa2fa4743dcf08081f24e91f432d0e1d2497b32a18e724f3026b071e08a847f8c2ff8a96fb414462c3081eca0304e9aa78bf751c0a853025026313032484a229dd2d072e8405d4565920785005c3dd9676addba869d18a420bb8c243030db83871b9820ed44da9385fe65acd3e2a22c0f169e0b7e752a814b4cbca78d60a1965689df7ecdf5ce1d131d2ab11bc2579e68c8df9efad7436b3b88fff42192c116893333d6143c5d31f123445b756d9b0a87eb5b8e3b52a348c03ec8d5d9bdd0316aeaf60f49c357a0aa92d184c337947f41f05a3b5c8ea7de28dd816dacaec260317702dd31e518b99da81d9be364d55b4f8b8ea91a7fc06c428714b0b302056db0327a72b5fccc760214866738b03d63f889b0efe7099f0d7ca39b8fee69cfa901c0210c61cfa4e295a2f429ad08c7eb4b86964080e9bec2366e8df334c2dad225dc3031bb6c582ee8c7307b47bf59fc472ee1068bcea63fd87fb36500faaecdfd3615fb3844656aeee665dd11d833663ff4c70ba0c9496f271a2e7a71a1213983e18eaf95dec93d09c3915baca3092118151a1d22bcfd3675c2e8d561aaf3a019ddeb60ae55d2126f29f04587dbd0d6490dd300191cf17b4875d1bc2d73e2e4345c710c49cb16408d5935cc92c92002f6d3a14363727e140a86e6f2afc1ad9b81e354b2ac4c6f6b96d69d0a0cabade730e1cbb3371162d2cf750c404edcf7e3581acf9929c8a61327e2465997bdf88f5af34d6f1d403427c18e05764fef335faac04896502999b5da8466e3a88e659733ac38919381ab05b03854b24e7b7089a459cff4856f4b9d2a9d59fd07131f3c2b49c65f0b9555b33afc7e2898556f7fbba8a72736ed8494ccff5a3695b2f68dec7fb5e0abc7886464b20f54a3884bb99ded51c16031fcbf40a92eb2f8c6595b941837ff1a36dd672dadb5745f872f56c27ece6abe53f2520385a9220cb0fef89e995c48ada29be1cb01e64dab8d05d0c1fda028073fc031205e391324b020bb032723ef19aa4a3620401bbc447b6b93d19cabf566fb53e41b4880c0964a668dba10cb2902564a629a91099a20896754273465c879c1c9f3669069f006401088de690891f970ff1085edc3cecae41eafd154f237c1749a164e7cbacf4b44642697583a87978c186d73cbbc351915f58ab63254daca88932c0bd66835005e6cadda0f8907051e64ee77c7856c42e73e6fd9861d6a2e07342c22992bdace74669f75f22cc0393f00d947f304033034817a7dddd6d8c326fe0bfc853c9dcf229d93b64f856eaf54c694a5f07743a90b580ea35b8272bd01a8058dbff97646428abddeb0ab1061309bbded1c3a95a2e021fa1dc2cf926c3d414cc64c2a652d69936727ec46ce8131d83c34e8223fb46ef0eeae4e4f8676cb7508c2dedc9d8d1fd286a9b124c81a4311eb890cc7ed84f7a5de5fd6c5625b86adc556e4c077991c9a8c868d9e003b3c45e0531f9819455b93ae5b55cbebc7fe4204e1e5483c921fb15ca2b33e0700db4f9b4702f347b539bb4144a89353a9dbbe290e5562ca001af505e6393d24b5f3deb5c257af3179591d078ba9cd474a5ffabd5daff7946926ede528fa6b1ff38e72e8b812e370976b00ae85ae3584274dca57970b56482f3e30998d62e2af96aa4f0b3562b0b66df6293dfa9e16150582f97e534fe9f9e5ba4721bd36a52918219aa2627387827594cf50423acdb12dcf090cc69d2f686953eda253683ef5d38d9660739227a22bc3e1c2fa223d192557be4b56cd1075369cd40817d1389a28e12e7423ea75fc5d2466422690454d592cd931ec29bb90368348e0072e92d28de2f0f8251bb4582424a0a921727e2e966e45410b955b91a2a4ce25185c2c224568e45370228a3daafdbc11848e6dd55fc82b69282af68e375a5f84eb92b4f97b5d6a567653079f9e29679cf862b5ec3152363fd41a513dda25075def85f623b1f78121f074967b72ee32b325904d53c3dd998bf64dec2f5fc2a3d4bb0a23a65c9b0eb93106c57f05c4dc53af52bb7d37aa2a9d96eb8a90fba4932d56daefcfc3ca127087586d6a15c0cb07031cd5c9ec6e7eef24869fb1c839728565603236d5a00c7426f3fbe14c354333629ce3b597a0d29c2c26a3015c787020c5532deff20fae05e02d839e946a63c23d2daa039d7ee6f587c822775e0ab78f9928f70b71583007b1026aba5a53524413bae6be80a6d2cfc96b0870b0406b6cbd46805482770368063c8bcabb4348f3012ef2efd83cebc8b300bb1b64125c99d69755a43273f1376e7e6c60a096f7be23f065b98e8a3fcaa0e2e4a3cf5d70b1dc3e2abeb0e", "636552ac655153", 0, -1236282920, "1cf4363e83d55e79b5e658cdfbbbfdce529a2bf93b19f95c3b4d2c7f4798da10"], - ["2a58d205022ccd0bfef60e16566246832551140f251a2c5e2c949611977c03d37f2721adf90000000009ac6352ab65ac6553514c074bfd8af262f6385f56231e9d00f14f1758db062d0b2dbcb46f31b1e6bfa90ef36af2010000000700ac6aab636a6a1ad6370c0116b4bf0100000000002ddc08cf020000000000000000bc5d3d040000000037db541e54fa4df50ff294027fb9359706839ef98dbca16f407228d609cd07f276cb333b7c41202695de61f9f1bba46ff1996795d3a98a7398b4dd5de6641fdad982ad68a5bcf6b2b22cb0566d25c6dba0b0f8848a5913e6b7feda2957ee450f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e12f6dc377b17bd29a34aaceb0ef695becd9b7d65779658b0f19e7a2e87a8590aaa7906ad03af5bc415648cf0aa71724982ee6d3d7a155887c761bbf03f58de8893cb3833b3955b10532c54480bbefa844e21fecb5d81d1890ee950ff5b920e92855beb609f39421f84943125b7f23ba5af7956fdfa49e4e0da8c78d0eafa2b030149820e7e51a55adc1b82c7ff54b66e19616a67b5e1536ad84b5eaae8ba9711030dbc89d79dd1c817e0ab85f0ecb5cb7288e4d33a4a27b3b1b0d3eb355eb2464a0a2e00da61200911e11af147774beb757f6955f3951c074b96595719c53463cbd827c9f0937174fae935445fb538309b1cf232f9d92b3d37d9dfd3c39356be4b8d031cbd6d82844795af9d2be68546a1ac4c1f5a12750d27de3f2502c530520935a602225a1b9f074440b12207f35f7e89402e06441bc6e48e00f2878a5e6e99390693031537028fa08e10f02436fbbb7f0230ac265f04a7ff8e6c61f4fef1b9b5dd17ec021b6d9c635fc289fd8dd437fba8e1e99ae594c996b6cd3601eceb72e0adb61710022eccf05ec66e7cc81f6d40100df805cecc2768cfb35d179bbf8043caf254b429ab278d5d51fc2cd68bb134a5bf8c4794c8e8c0362c2c0beaa7d9e3e6ceb7cf0c6474ed7843b11eaac34569108e80a18bb7c1a85a31febe630f40b4d0c0692678472c0b49420adbd1d8254086ba864efbe88a7a55d0263fc7c2778a22ab914a5e974e282fc038850d5d805d6350748e6846df44d717e2d2d6e04920568e460058ead6e78f8ffc0cb7e68dfaef2653416cdfe2629d1ee63636bdf599ba2eb22a6962e71411d5f17ab3bc326b7dca72e74e3e8d4682bd220648562fbd002e923c0b29db569386e106f1486d8a71fbbc860bfda9bad5518f97dd40d54eab7599e1e1adc10aa14ae43ac11537ec866b2b0295aa8e9182421da1f603786ec9670aa9b08b1f741d8b5affb23fce2dbb5da25b118345a39e1b8bbca75fc2ee7d06e4bc5380d0aa7f0c8cb4665f74ed376c6dca841c06fb8792f916629c67d56e45ef2b428a1c61a8fadafa7ae4884cc40c6f0de4d59b630c53632217ead6fba39cf3712cba38a0062e54b404dea8c5587368b52b4a3a51297434429c09870a08c204419bb03daddb1e84bb4f4131f0bde10608ee3f7792e6e7bdcac6acb1bb533736e1bf87c80e9460f07074ef1a8b018f4e95f9b110527ebfc0c8e761d158cac83c92e08cbf677d5d094e9ad8ecc0f460d7fa6a5cae8ba6c9866c2d2bc2513a792a991efe75d1f06dd05c106d53d6c02f96fe8c49c129ecd26ffeb91b6ebf2065acd9ba7e2c9ad822d6246f3b383354efaa89c90c921a00881d8acffb61a387a85067fa2fde090530f1cec6159196a27109da8e1b0aa7f6ffa75f3b2bbf1b927449e0f67b34f8fae6b2a560ba396c673ea43a9dbf78f5d7085b23e92d8b69c73cf0f77abd98336f1a9954dac238d34e5ce9373e33201a19739c32f41e0f53015321857e7477ad3afef2aba2f84b6bc9cf3329f6de11e67fb21411f9512b776e5dc4b583e712825404ab23d1173477099d18c117603d9d1d6b27288aba26aedb31f5695c50244d85e95dd5e3d398737309184b7200383bf761cb2282f6257dfa5422f425d79ebbeff73606b9de82224c3235b19487b75cc5da3a9d23bf8294b9320da1d2032c9a6e58e1a86d1a170bf02211e27c306718832145efc474feae34deae62da5f0eac5bf9534da0515208b60d6759d6820e1646408f0baeae18f502dae7e08f1d6e17e1ddc03c4f84192c3cf2bb4d8d8f43f336066510693edbcf4573a83f3b0f09dbed7a68c896b246a6ca118dd32459ce55a15020f909dc0f417172487a7712eaac82aba6d8bf72ad85f0e201b6ce79286ef98fedb67b310598b31ef0bb76b4b66e8b5d7dfaf0155c219aebae498dcad7d059c2acce77213329f1fc8d913fec474ea6bacbb6e2243cbdcc78b8c35492a3aa7f12c01edc810e98af0374f330d08148f0d749821160854dfa55a249049196dd3a25e22a552fbdd3a20c20b6471f3bf89c426880a50cda88a567456e19597415c873552f84caa9b4467ca6902e281255337b6944f6dc47d297ddf439abae203937bd45667b082de32c0add1577eb8e96a6db8758b12fdc0c05b0d3f0367adbeb893806516688eed3d439222fe7d9eaacaa84c279e8401a37feabcfd2f312607329cf50ea36e8c5af060f68bec415b65544caa378e29f55f9d5031f0f73c863d608d44f396aee4ed9b11fcfd8d10d790afbf55da03135ff63b65e8f3d2d757200000000000000004e32cd0400000000c391545e7e9a44e2c6f59aaf88b956fca4513b4343ee16f5ad056604fb5000cefb228edaedb153a10c522d65e446e14771b0a9ef4545bbd5b2e8e8c7ab6ee5f4b97d6ebf239d71a3f6f6d1bd16629d424bfda2253c71540e7e0afdbb4f20467200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c33b2117ee1bbc87fcadf898ea510ddff4f63351ee78857fae8df175cd96bc8836c6175578e482e4934c656848b882b90a1cd86fd6452bb546e56481bdf2527ea7aee6debbe0861a21f27e1384c83d0da8b81a8aaea7fcd040566d2f8b5d3795e386e5410913147461d877b6ef68e605befaa9a2dcbae2664dd8901f224bdce6022c8d497e6f3864f86c3b38a39be343c5dbdaa5f85fb089ebaf08b2b3bca866b302281434982b1be726fcc689e549ebf3665022092db6f7e921b54df82c8f3d2b1f0b111b78b0a3c824a82ecf8e7e82e2c8a11da231cbd341de5f2a5f331f6923c0d000c9b684bc72d4aedc0e3fd5b7e19aff01427cfc002658797ea2922bc335c9ca031920bbe0092361e790cd303cadf2306bdfcb66b8f1c58d33907d5808f27563460329867b41dc304d528d6e6e45c9adcaebaac7405be5bb4ee33181d4046bacf0eb0303dfddb43b48db9fc9da56386419a08e8f47bfb7588f188d536671942104108103197008c83146e1a84da8935ee5df0ce105f8c8b43009a1600aa48decf24c2a410319e84d6dcdd91be57f97ee40d07d359a63bad1179593927876ce7176db6ebc1bb258db352869ecf9c41ebf589be2ca2c130d14d66b33f1b6e1ce39b5db2d7d37b907be600ffbd1b0ee336cc823593dc6640e1465b5cd2ed962d5041755e4c300a5d9f2d0f433d6f08dfa7ffa959c12c8358adabf4d2df16299a564f8f33bba6b2b5884be7e03dabc7d8fda0dec17f95ba208e8e4ef9455e5b58f4c4e02fceac0327581ebd8cc1e2efa639f2178924dd3983d03b255beb82641af32bfde8c558f299ee0bdd15af49e887b9bcaa754345e683098efa54ed11d6e08606b4a41056dff329c7849c079fc49db8a1c3b63054a5efe0b3699119c26510a92fd2e7be8ec7a8f2cb1221c2228c62bb4cb98cc9997f95ab551dd05d016ecf280db5882a3a587888da4303587dc0ced74a91afe1b80d401686092a26f4ea0ba39d05b9668f1db9df20f2d345e85d4490492efe11bb874bcbfbf7c194b9aed4635aa24d548e763a57febab6631d525403129354fd5f0793beda34af5d50a4c0fe3736eaadaca3fade8e0c773cd39d6cb7c78eef625f6338420c47ddd413c05cb57b44328ed7619ca30ce55c2527173a9dea8f9836cbba691c956b40126a2249952ad42724c24597e48ec6d0cbe785381f74bdaa403da97b0d0373d9c850c38262324238871bf5c673e3b43f5a42724e98cd2245814d3bf9c0fe595ccb7fa5bdf7cd811267d2120e9e890c971bc7c92f69bd901164c641bf62a34a7bce3cc5406e640f1090503efd11e223fbe44ebbffb5bfa5c9a66f39d618e9ef3eebeab35a3266418d1cb9835cc52038f069a48f9f8c89ce46554439782321b4b7dc5bc89e98c3bdb81220f799fbeea41676647f06d8732d22adc0156396b3c4fdc37fe77ad1c5be2ece40a619b705d513e78eeb0b74ee905b30d8152758d7a00153c4de1170da68f3b26707170ab8062c917e8191a42929a19a8b266f4099adcd15834814ade27918c989f8882b9b1d14c694805ea92376ff54c0d334a88edae2943cefa97b6ad185ffcfc07691b1136dc278a9edbb4d07ee2ac51ed4163a264a6ac0d3fd041b85becabde95618b0fb5d40ad38e60da1746e103c8f736c8315096ae6d11230196152f773d1d131804992dc8c42537360c505869a6bfd4e7cf005e70abb7ca10605fa1a2ee481813f7b47e9607c47f29ddd22627fc051e0ca0c15d6e11bf63f61d52e5a9cb43d7d246206f8be0ca03ff82b6d1c79cf85a7d808d532dbe91ba0c5ddb36296c191cf5023c47de93235831ff190459d61dd93c435eb5e7e5f4011e84a64d8c78412970a281478752c4eb094ebb076d6d8ee415756c41f448d2a2e119ae7a746e97d7355fe772a84738ec667dd17cf4519ef99741b5a4b46d019cf4a2de126d9866f2d13a235660efc265ee40dff14e111e43798ceb4117d5bd49f8ef2864a2970446ede1829f4421b4d95a271115e77fff567b760bd6712e74b46387ab5249f591c617e729b4615d8d61b650efc48e186e98b110a5da0bac5d2cef830b91ca41ac25c827f85252619e251e2823308fde296c31ab7a036f89087a6880a6a1339772747f781af23c74721b4fc245f203bd47cd764aec4395427debd7d9fa8545939aef6d912b330dfbd6ee2975774d16364cc0a1e94183b280cd9bf39d04728aa6523a1805448514dc11e927de5c53383bf0f9afced39102aeb50d35661817c55b4f746deb9d5927e3268bd288d9fb497fede4cafe5919eb5ef3b69952ba359369602c8151e78b68031f752ab05a265188271005843e9759b666de37e571d0d9c5937e31a02f6ff174c5bccee10e7c10b8c4482d26899aaf8757dbdfe72583940f858730bfbdc99f39b446ba4f3e72bfcf4c0c", "52006553655300ac", 0, 809922732, "35420a3c2aea4dcbd8b85fd458fac620a1d2fefc956bec8ccf613750ad155545"], - ["985643d304fd3f7b5148143b11e38306cb3afe70b71b420dbdad8ef92214c67cbf18ff8bb1010000000765ab65516a5200ffffffff0d82a105f6fdc3ae9a6e4846563e5e956eed60b6c27ca6230ca0222a4b8c631d0300000000ffffffffcc46eddbf01c71a777436c1eaa281f7d2c4c7f2c7e5fff5b01e3f5f9bbf3dc5c030000000663ac655200abe9af7e1550734e46ca4e69dc3b3fd482a13e8b32bd009244a92345138bbda17c18946ba00000000005ac53635351ffffffff03063481010000000002ac53ccd7a10400000000090063ab636a6500650002d5b703000000000400ac63ab00000000", "6563", 3, -369456481, "5a322b00c2ccd5e9f8586be959731bdfcb2d57045ec30b3e919ccac2f65c22d1"], - ["20be07d40265da8d5c54f55a7bea15a675fed58a9dcb2bbdc43778c37ceaa64c6bf4e0e0f8000000000865536a51ab5265acffffffff5c45c5ed5adf2cc99e4bf9d3af0ed2ac9e589e175c165c056ec051fc06ef20c203000000026500ffffffff02b92eca000000000005acac6a006a91667d03000000000400636a6581e02dae", "6352ac", 0, -1051900195, "724c4f8f12074a37c786776e4acf21eca4e9af89145268421c69934cee759be0"], - ["e9995ee90148e2a5f994c608a99fad2d3c7f025c355dcbfb13abee7046cd22f4e04433e1e10300000005536a63ab51ffffffff018775a6020000000003ac526a00000000", "65636a5163", 0, -2014976053, "8c986ba7d59f348ddfd0777587a409b09953e0e8eb57a83266c129b52da1603e"], - ["a01b61b202e199a6303e713d52d83ed7e411dea967b5816f87ae860a021e06ef72f1d3ef740300000005005300ab635b1a4311d651dd2abaea78cd7c1e9373d385e150b5ed655a92b7b26cc9838b2c8867a9600000000006000051ac53acffffffff03c224c20100000000006696b20100000000096500acac53526a530070230c00000000000352ac5176c8881e", "5352ac65", 0, -949295909, "dc920ccfd882a8f18cfe4388ee7d4b01938fafc468825f51d7c68ba1e06d71de"], - ["5f2ab240014a46f9c266c131fec2eb5baf6105c9f0b2a2d30be9446f4e274108c14cb3b788010000000953656551ab53acac51ffffffff043aaf29010000000004ac005165c5151b020000000008ac00636aac65ac52cd198f04000000000253529bab8d00000000000853ab53ab655151520000000003f0b9d103000000000000000000000000076b4d8692631b9f23466ffbeccb5cee7fb6da895960fa7d80fbc2bf203001fad1eba6aa79a0c878bc91144a0d51152655475ed417de7db7c569b452d6c5a5f2c05dbd2fa0ff5251e0391a153599035854e376acd65b12f4cd5ee2cf789a0e49000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c5dac04033fdc341b52326fe52edca136204bd075c95e8ab44ccf33f502a590d3f5c56bbdc27b0c76428e5c5ceade78388a43749abdce9f2402252b554a05fe3d991a2eddbc0d9b0ea615541a612312c289e0ea296e582b0eb244ba83f9b9ce5dbb239a960eb3e75e237f459130c6373285e85227d8a8d1e5c08e6d0170ac8802220063ff18428ee19e55ed7c216424ee13099d0109edabe7c2779aa8390a5fdf02091119539d35ac8444e2dcb0a9e0722416f9bea1b538ec94e9ec226489fc79f60b039f08c6b2a2ea20c1cb15e51909c67cf68c6b6df1e20f157c068b5ad4db944402dbb280361d5be624fe0c11f615fb98b017986dd19919b66b559edafa6149a40315fc9b700c51ad5547ef5e3e4079f7cdc11578b6ba0f29c36f626a5313042d930202c7a1aff43a00f5621da4814399989e47cbb62672e1e8c89e915f7983ec893003066795da7c3714f2ac8d2be9d0c858407d7d077160826a90753ca1c29e39767f0311304e038089c589e13c66d73508ac49e87fa202136075970a05985ba25503c4032a005254957f7c59aaebd9560f5a18e0e169181f20c9b3f7d5fba0998e687f11f00ebf573e083b436d4c96ce25d0c0a0309f3d57ae9770bdc0e911f948040a79d919d7f9c19f136bdfa6196a1f06cc0c3357bbce41bc46ba2e4fbdad74d5f1c8886ba09fd7fb3d331ce57c2ada02aaa1310e63194f38fa6a2cceab990f2955ce45f4c822bcfffedabb5b6413d0c6c84507a61c0dd1c759acb50784a39ac1f38fe0e638d73c12005f129e63c8e78512d1e5ed3c4dddab07984c16463d205d359b50611d75b1b30921a7d0358d925cb2be25e2d1c1d2a1f7fa8143527a6458a1ca33dd77c05c1f4f24b3679c4761137719fe36cfdcb103dbe192b4a4e269b08b2b6b0d88c336c68c7241318be80307d57ca4c4f7cfeeb540931083eb59925eaad52d4c2aab85039ee8d53e751487007deb00bceabebb5539a4c83d4aa9c5eb4a0ca2c81decfda933d8c265dd993ea2e4b7449fd55b820ae84980fff6d73002993386016192ec321db34cd1ef4931c7be085069170e68563721b7900b6cb8c1e68d1c162f8ba627e73c151ea88bb5a5ebafba8b5c3e1f9c0719ddfa55f602779df78bd15b6b56453c0fff6fd9a603e479f3817b637102c4490f6a77732a3f8c76ebbe90403bb25913c813ebea65178e55dc842a8bfaca0143f29a7fdcc233b20899a3112ebcc849c299ce6045d9a835b68069294f5e79457eccd4b4067f59d4debba7824f265133a8953670972603fb3e58dd0e88a4f5dc5d05be39820203551c25ca2c2f0ac4be60ba8794f86d13b83456f1c794e2ccd1f03dc005e37f6f3244231a11c835664661aef56c215672046646e9070859ce9d7f0234a6278f0dbe33c68704b20b742fffff975601f2f32713b1775ea1f766ebe7af6ba98446cbe5aa589c56a74c159467fc2a768f14d6e509edcd1a427f0923251495560fa00197877a8d78a47cbe711837ed6e2eee937965857cbf9b0999c9c6f8fcd3d5f892284c9125bcc80aeb91b5ef2fadfa3345e16dc9001b729ba6dbbc795a6d67f4a6e3baa92ed44a4fc9e474646629ef37cf0006d976262596572a58adc22d1386738b930234d830951005127db6b9a99697852b5674c82c0eb7427e923ff63ef6a558e4e3849f18c5130d25076bd17461abfa10a824def5a34cb3418296b01e5cb32d42fa4bf97f684b60ce9be74b4a30fcd377b71158730c083b094e4e2b5141f76dd21fb94b33359bf3f651f81279116d76006c3f0afd46dbe4cb0692624287ed8457b613d00e7c6696630a985f4df9d47a79dc22754aebdf46da8a63a21d93d35ed56aca76b3e95faf077982d10076b72ed46f8aba30de93bd42726bf0b1ae801ebf224b062ade3ca617f033eb062af41fece7a77ea6870a3fe8ee67eb174e2ffc5e8d56226d754821764eab45126e42cb118f8651ccf3e006f10b74c08993028148cc0c14e5bfe1e2144c1ad0b1a0f2038724adb45742cf28b36f336758c5b8430e206ba18de9c303319a78aee477ad5916918e43b75be626b8b62c665602be16d928e0b4c5b05f5c5783155f730b32c27cadc684e61ef4723621544dfc488802be396452bb9bfe1941baca8a71ed94fc85846ad3e60465f5bff550de6b8282cff1ba247e2e90a64d25d082d7ec87db6a73a76a7b5031e2e2d515c31d22739b27a521af0230f68bbf5ffc8ed07a95282436bbaa92c4792b2e4b1c3d08d74ac35a7325957300dcd5f5b3758e662f085cedb87c5f470000000000000000b4c2000000000000ec93c8c10a9a394a862445b544e257acbe3efbebd08747a00e96f99449974e296b6ef8c9ba349f8a7a24904dce2f34dad01360c0edc8123d8e7860cf21ddc3c64a7a6c14e53daddb55106ef330032e526d1260429ff2410be1c41475b82dbf1800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034dc4cebcfb3d80b241491307a4d4048d1ea9d216d0aa42c79706f21caf1a9448811874bfc0ec151d57c47a9423740786c520545512fcbd19b1b8971e4fe43e69ea196ce059cc6b4930de85717a22559a9de51ccb0827706b5c395bda3d1e35ee8c1f32a0344918cd847eaf1f69f2e5f5b899d425832ace74d3931c0db9b80d0229d132de73304a29ba1798b6f0fef80a42ece9875b2d64baf3d96e4339552be003088dc053a05bfd9f3eb380bc5d89b70a2667b5f841c27996bb036d48a0b787060b0adce2d984d0e7915d6cd3e9d3b4459029abe618abfe325a8102763cb9029b7c1ae6dc047c3c91488db32d81ea5fb26af415493e1ac96fd050f70ca6cfc7b38103212e1d5088b09c7c9a47338781a737da576ba5784126908e3860c42d1f7d52a90204d40534f1191c58f6c2e553d3a1ccd5e1caf33084db1fbddeb8d967f2204b560310dd2e6df6c1aefa8c5c8cf38380438b17bb2f0580881cc6cb584dbb097338450327526811e6fd0bf6ceea5f09256bc1effd53da8eaffd072a1850ba8ce2fedeec0225aa5061df874d16f589ff31534892d1eb4484820b9f500e6ca89c5338e03814fdea9f8167d30ac5f21cb5c61f2422bf10313fecc8719eaafb81c221c7e1a2c9c99ea8e3bae44370dc6411e32ff0946879357b6bbc5b8c8d87b81197990e1c8cc7f3cbb22056e2ddec2ba6aebed61d9e9fbca27be6b7b8a34c07fd751299bb64f3e9a4a9f503b096cd8c323cf3b2150af1400d2225f9724c55cdcabb8f4c46eb17814795b070916567f94e1dc5e42f6fc0c0e2075b4ea248f144d8e3f625ee95e45334f01e9365a8949529cc670bae6703c94a6eef3ea5dfa9a950de190d970acfab90726a2b3510436e4c1ac4a701e9ddeea5804db00d481a71ed0ad284baca1f533f63550ca88fd9818500258f62f0b7377022e1965c04aaadd7ecc46489e9e369e6b07604bb14525967297835b944b7cd1c1eb7726ec4f14c6501a1472aca4972a970b65cf80d70b608d3930940152ca233d2453413cd9207c480ab5ee848cdb299354fecaeb16e548c1597727e34224b2a73b4c5e63f714e64b4a6d053731d9574bb1bc2b24d8e12d9a385a4c7358e3cc0230dbcf133aae5c578c40335d4e4ea25a5ab33365edb8702ae7516081b3ebdfc260c7bc29695a5be0cc05fcb3dc749cc2b3bcb5d07ad9d49eeec852cd52c029a513b65730d20f63eef18e0b9a7838512c15955b40a29f20e57c5375846abf15e5b0b78060de5ee03347301b0a6cabbfda63120ec0ec9c76ec11a9ea9cc653230ce9abb35d93ce3714bef5b0fd5595e17a9a7a1e3f7445014070555a930bdcf298810714db6f7897f4dc089061208f8fd1f86de0fa1e0cb9845e8dddb2e79e42f9aa4ca0bfe217758d97b7f026c648cb4675f029b0e7286b7a0bb873dff0439bb4e3a9fd28162ab6d2aee43eb08df39a6a4b38feac42c8ddbb7dad5efe12273b1bb88ecf03cec56319b3430da26d0d83e71dbd61666e9b90e22684800ef865d4285f570d5db216eb5c7fe9ccdb52e6ea2d179ce086da7fb772a15423b04fc6df56385654b6f7b26d3dc21c4c88efe5ad581ec6cf9021d90114235eaed8c9888aa5272ada85efed934d01775d4605d2d51f6040d4ccc608e545758b285cc68c106d2477c7b68d875408150d746c5dc7366b2b206ef8b81be992531a662179b916af6f58140fc6bfc5d5d14e6691b047944a2da3f3ca6b11d1a53cce315dd6a33d7ecea2a8d6eae2d101a252dcd140f64e6ef3df693ceae9c7adfb13af5eb7b9945e91b1273f4ba9dfd5721a6334af4b31fa4729c839c42bd5ced5eaa8c708d8d0d659839e184fca0dd8f4281ae598d8f8c1dd9da2f9ab9043a71bfb11c3d97f87828113129a6d43507d25fba8cf2ce354914655aca9165a18d574aee1e3872f6b604dbf89940fbde84152268cf150b8c95913b94b4f979f85b0aac8ec2294d6579dfcedd429171191cf189a27d37de48bb35f3f4fb395d90dbe75ab6be3e652967d5c60a0c125248a29e7cbd18efbcdf8a874a144501054cdf3bfd71bb16620eba7e56bc20e25e0884f5aa7fbc612e4b570f17ad24c4f0e16bea1ec7502acba19c7db9b548588ae4a0e67d8a76290c37c719d1d4b97c547d091160c0ab23f3a95db9dfe8dfcca5486b9b574f0a701b368b1cb8e3becf519187cc84014f67d8f25326082d8c0ceca6e48d18d87d685e0d4e4ddbf86a0fbffe1b1c965bb7f82cc49bc37c41fcfc945e312d548a624aa01e5558fd7817e7787af5e7105134c249010000000000000000a379fe04000000003cd42dfdbcbb84317bccda3a0111ed98eb96f3b8b3357af6bb4cd199f2390830d907a2ab4b969dc2c97e6514ebc59e7b9e8168731f397885a24e347b8c026ceab1c4dbfefbe0b9570c44485f147b74c7d4f5146807ef4a2072940af0cccac7210000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000087d53e2fb7575922c0dddb73d96b5ac6f8a3744700f6ca2fba2f037a42fd8a8de8628ff25d25c9a6c4ba6fd005c6444dd08b03f45f271d3de14fc924d9660437ebdfd742a972b7fee14ca5cc20b938a4ccebb5b65d30599e95e045553a369077a89fe1c9922af781ee79319e2669525fd10831fa9741cf2c78d22c36b05d28f3020789ed52d26b70218b1c1fd233d095f2e96544dd7774529a169a8327e9d8bc6d03124549075ac9118192901ad76a86eda668329cb31036bb751aa85fc266724cf30b1da73f18597cd96ea3734b7be0a47bb784d2f6ccd5912864c7bee025f04548f42962c51f064a8cfbf3eef4a94c1ea0b410132da71ab0eaf4632fb6e2932202790202642238103af65349232011ef1bb8ca7d615c25308351d7ff17c6a56139b9ee0200168992f6cf4a333c357a80662b9d1e92f7005248bd33f7110e435261b3dd76030f933b6120171d058fe73f02063afec502a2246326e3a5b96d2053d9d3c1aebe0229459a6c87f2f7a515f32f24c08f067eb53acb82299e75e97061246dc5931f490227b77faa8cb5991e399ca4983b77395ad377af1c535d58a91c72bfb2caa1cf24e81dd5afaecdd727300b9cdf102678feeb72fb22826fe74c2ba5738272a5bcf6da3b36d51f720fea702e3f787618b67ce3fd57ea3308c1b3a8980648acc3b8c9f4961e26dc53e2027c2ddb7260a832b306ce5547a798135df660b3fdcd4774d96b97c435af2005cb7b57632f01c05e6e5c83a89192baba5e87210d819efe5e5d9a6d1f2f86818b3c861ea8df8fe668ba938076df09767bcc02f727382f59131b0d28cc76ef9f1b2ec928820a70bba08d6abad05c17bb02a1cac4e53ca8397297f259e97c0b563dd0d74329db4b46a112130592f71cb53e3982a4afd6ab6a5f22d06a044d7297ab58f05d510ac446c7d68838ac71b2239b9dd8a1221801b5c6e09382c2d56e2ba6688f9c51eeb9c025a10ddd37ba4b75a96b668d73f982c1a772c287778cec1796bfd5f7935331a3b902c7d25ef683441e9699c2fe6ecf4f4b8053c3eec344fe92de3d0a17ffda2b5d4138c08e85f04d50859cb111efdd0cf7345c078f1acc169ad66b8150ff7264ac54ec6150ade992dadb7bdf5cde037edecbef952eb6da264d15a7e035781207e86f296b803dc124a8121812b8dc8cd5c44503d3d451ea241657a943d42726e42cf656d9d3d4565b2f714393128c000da662d0e3f9d172733a3560a00a4f5aa513384c047407c844a9319dc0f6f69de3d0b37222e0e36b754b5fd7b3bc71c4fad8c0895a445910bddde0a949d4769797b24b1b06862d8eaaaf1b7339f59f3617dec9f5dba8efe9a87ce883421a731e2b56500a762243557928087f4df0f9ffc49b964b5a8e040327ebe78074fd7d95fa5f5e03f2bc16681b0704ad7c019e1898a97d24d1d548cafae3fb28c6a4880001a73e7a07e1be2787b74226f96842110694c4973aba8c3052a160b674904655f646dce230e1a9a0fd6700a5e88f191aaa406f7f0bd8989fa984ac1e4a6ba7101405270e84eee07a9a034aa5a59d71ac8b406af478e9da4eed9b6913cf9264a4fae29afa221611118209dee641622ebfdbd6c68575cdbe560762a4a7fd96db910a25a9dc9c063c400eed5aa9283028f398ed7d09a0ed9e51350abc1a535ff598b0a8356cff87d0a738c821cbf46a2ca8d81421a01e1bcc97a3cba52b118391c59adb4298d60bb48bf06eb1c1e988ca679feeaecd0e74ee9670a41e89449994c7ea45a427b3b1735d641ecc22ff6268759f488ad97bbfd258ab3d36bf7b7fab3448b71b31bd4dcb39892b27462652c9dcfb3e4f2ebec00cfd3072706079f07e60eed857d1c2e4f11fda62244133a171139f15605853d3f5e269c9a496b667ce750fe48c48bc25eca9845a4329d7b1e914a897ca39336bd715dc9ecf714e0947ab0e4adbf9ec09e68ade53d668f6ce5fc6ce3ba54c8e6be6949551c31bb3f9ab7b0e2d9599ce772d2549f891a1ffa4696c1eceee420cea36ea58e9c5cd3462bc9566f6c4a62c5591e698c0fa2749e6c61cfdfe5072c7062b1a16798c454f2b132bbc464becdc5910a0f5f12f999aca68329912611851758d470a7614fd6a050f5053b3458d44f197458e3eb5e98620f8d94b9cf593560b921a3bdb3ce5e2640e19e42224de0ec2d7c7820804170c63c470df57e6e8e74f7775c4019131197c5fcff140ae1ca53de98522c6efe089a2b8847b72e2e3d7d3022cd26bb0c0d3b8e05ab55e89d12d75a1780974daae8a58fda3c38a68d623bd0e08fa02221dd66dbe9014adc93798b0bdce4e1420b70197e5317a70b2294c6c4a3c934b88fdd1037a0147e976f88ff2bb158553fa74fbcb6c2e3d582d15d5ab5720652a9695df97069b697eb9ce63fa49a7d232736918cfb6a9073ca8c104", "ac6352", 0, 990198618, "51c43fec7b1b86df20aa709eefbe288b262356d08665e7243c5525235e4a30c3"], - ["b068d6c904eb4388577d82bd380448bba2d15e912b1bb098487a56eaa4bc49a71cbd5c21780300000003ab6352471a76344d4e192cdc549309ef38aeec83d7395897fb8d1546f0eada72fa40abb6854e0c0000000005ac635100acffffffff53202ef478d919fc114adac3614099882689b055f346c61ef84ad8e4bc1442de000000000963636a53ac516aac51ee96579ccefe3b10dd3e687bb153de7557000681b01e93575c397fb052d26da6dbc7f79f03000000086500abac535265004495c76f0120a47c010000000009535100510065ac53633904d6f1", "5100006500510052", 1, 615824480, "6c893f0465c9f2e782494bbaea7cdeaf83ed6a40faf590e0e0285448b3453146"], - ["25c769d804e5b5fb2ab4352bb144582bcc1e158027e41369eda9cedbb59f7d979dab70b760000000000851ab52650000ab53ffffffffbd7fd3d989c5a9c9b69e1d7a968e41621f8ec374fdae031fe32c937589e780d600000000035163ac2bee71c8f8e363109b8462fbbb354ae753fa8ba9a138ea174caee5aa2cbab4b1d1b5020202000000086500536a536a63abffffffffe76b4dc755115a40ecc465714a3b848ca932f31d815fbb5a1ceb6f61591458f400000000046a6a6a53dde536cd01f8c641050000000003526351416c1c50", "65", 2, 605436556, "17c248b5e5d846d5ef1a34a0d877334e0d2c5b17ffcd214f22e6a2c1bf4a489b"] + ["raw_transaction, script, input_index, hashType, branchId, signature_hash (result)"], + ["dca3384204639716f2dd16258075981e85fcdcb404216090dfd490dff1803fb5e005f2274b010000000965515163ac526351536ecf8738fd6d32dfb19ca38c1493d00c4e85d3fa70828c3ff922f41e93cc6a61145267f9010000000200513149f7fa803e6e2d31eac862c2916a2650e3762d34bdd1480a00d0d54f69423689df54b50100000000ffffffffda59507626bee042ecd1b53bc1f94c8302b861d397c30bd3e50c6ded6ef077a30000000000ffffffff036a6334000000000008ac53536aac65656370e85105000000000100ab294b010000000007635352005353530000000000", "ac525265636a63", 0, 817108351, 1537743641, "dd12055e149394bda8b82386ac21cb1f786d5b6e9ee17b81521a3243718cbc1e"], + ["1859bb70037d5be6d9066d34ecd372791bb2eb3a828d0fa2c6bc538816d38824b55dae4c1f01000000056a0052516aff955429ca8ff9394bbd0e758ac6848c245b083a94de4641ae6e9c466c28d27cd8888a5002000000076aacacacac636a048924740ccc5932636f99ea4a9261fb747d1f41416f150aa6cf685810403ea76db2addf01000000050063656a52dc8ae957015e464f010000000003ac655300000000030000000000000000a869180300000000bc75ed964aa683b01738bdc42a63feaa96a292e46835b04bd6b3321cdc205947f6f65309097dc64c893d3314a94c87ef7df29063b36a3d7d1b5ef9252dfac8d3c0e4b89ca6baba25e598efcfbe5e09d5634b1324f220cdd7afcd1ab5b2e92da4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008d7a0addca891a28c66cd327babe2320a8f438a4a96df47bbdef18b5b30aca4b8e11ec38473656b2c3f3ce1cd485be138bb2c4a2b363d9db43736811e5b4637278394d352981daf0b9320fe4834429167ec04ca8d1cbd3311ad387c94d7f9a35b1d59fa4199ab4a80c8019b4d4a412f24bdac0890863769b90a53333d86eaed9032e12c3ea4984b9a4de6d3aff2d429d386f14f73b564a3b90d3e53c4d6a0f15e3032262b1bd4e538b2dcf14a7d6b67e4126d52801fdc6cca92b49e056daf29eaacd0a02054ecc38c4013789424bcab28eb1e6a5f112ef48bd41ecfd0edc03dec94cd590c12f83acb663f9ac055f482041a37a4690bcca5d5c68d73abc1d1074aa4c7d020ddb814cee7a98e8a46304d64e2cd30cede08a30f143ecd2acdb9c19a101092f031dec114269139b85b1964773862608ea5e35d5e43b5d4622a65c03318cfc59060212212a236b93ef202c90417764a4315782b3ea6d5aad89eda1ead1895a572c16022fd660a2a04d2af4fe8570f916092389e3b4c5e73805a5b1a4fdb8a607d5ae68031d99e96bdd239655c6e3b7c5f30aeb2d5447733455a631c0d9ba1ce358d69dff77b958f933503c08af4f4baf88fb9ab16233ae533f24d331505c2e5c40c603615fcabcacbdca306cbd59c7f11d68b3e63b6a3c8dc091fd66625fdc55a80cb8a5a5d0998bfe5fae29d088af5e340e92d41744c73aeda4eade78c2caae8297c06f192ce4a5ee924d202e2b454b6e17cf56108c18da0f32ce141989b95eee9d63e1b67078f8a541cca6ff23c5ebae626cde0bc6a19a55ddff0f291aef0ec1cc34c1b45af11d29fbbe1f2be43c5c40f3bfd7eabee751287dcfa92d6af552d61074b901a758c3ec4fc291dc9dd64d78c12f1c9e406cae10c1dee2bb21c891ce9ec845aa1747d9d28b669fe44360028139cea1e77c159d99cfd21e68f82f41d53c98de4f9b9acfde1ab370e5fe4255a300545b506305b71269550799485968c7f91bf6164262679bda67ed2adaadd8ac7377d5f6a190f449e30bb28431bb5e9839560736821eab25d24d9127e95bb691afed4dd74aa1b07a90b41cb1751a6a3a1da402cd270e6d4621567a8595c58d597e14d0e4b86919457f028dc50f566ff7ed9b7a87abc8bb2a08e2a1332d8f6a055060c6bba6d7ffc334a6ad59860af54fddc1f6df7dabdaf63498d524d0762d90864bdc819bf90926d82fdd120a558cb51b1ba2ecd5dc273efc98cddc362a60de49e036a1ddea152e816433e551f45a13a517139d1eda2b524b27726049ec633975ab99f67b3b3282a8ce4d916691fe485acfa79ff8705bdd514f42a9f53002bd9ce462a274eba868a6ceda928a5dcb59753af9bb12c7fbcab61d0c08b67d320b238985b339af637d310f657d9094302b2f3f784e336549ecb764ddfafa8a5ca03bd3af3016f82aae32450830a4fa63c22c0ef7935b928f5aadec9e5c73b81adc43ab53568ee514b8eae8c443155f520218b621f46ca367a681e36f6fc7ea9ca89b83ad51be628c0c57adb1b76b8ede667776ad409c12d2802d04f26b3bdd0b175eed84f5fead7262a314907e0c9fd0522369004f79ce0f4afab40e5a8717fd09e849f519515845d8fffd219a2a191939b7cd891ca6efd3c7ebbac6c9aadfbc026174ce241d9a428f5bfd92533b1fe5d95d876ea940210bd6f81e563b1f9b3d602674024cc40ff459bb1c178a4c098481b4cf130e1946604bd875f1c976bdaf2d50c6a45b0d89e62753fead3ec2741fb2d8ee9f64d8a36a0d7a0bef4ad3e2e404e59208211a85cd48b2cbe6e08c7387ace7ccb9ac0926dc4fd4b1d4217f84977cbcbe9151e781afc8ad6e6693a26442a97f598815ae23ddbc7a734e0d94b8eaccfa49d7e5fd79df353451c51d4cf88041e2c3af543535ccd807a72f03e741646a06143e5353edbf74838279eec7295b9c2fe2789118d5a5c64569e67ea8f4e65e62fb15e2cbf5ff58424a7a518f062e6cd0886ee2a4cc62c18f28a4cec954fe9051764fadd766499802114f2e94047b912b181acb2ac433e7dad86867f57f42a203ecc1fc43f8d616cffeb8ef839e69093a458aa2786c6db389b965217984633b5cb3346df7fd0834351a02cfa682d9688713f4ecf9c93a4f6467d10771af4736af7e6aeee5875fccffad3eeeb5d31062f1b286feff7bc2e2afa384fb7d28fd2858466a56764e646b16c212e1737888b0dfe626b63ed7fa300888787ef766d9b2ee483e53139e3b4b93c0aaaca53a6f9d07aa88ecea30fa5bc27b48d3feceb2d3c32718d4fe64602f04000000000000000000000000778010feed260d8f1494cacd6db5eacd1ff3c452d2b016f69a20d074793ec0152b41c52b97045db5c6d587d008419824d25f7d1989a1931cc79bbb38fb02625741bd9536c851ad55a1f82e0c893fe145bb56f9b3f9515703fba2284e4471d5610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000068a17eb955b828e084430e226a8a39dcacad98690ef845ddec93b69183341a8b0a4256d3d88cd8d9327ba7f250b277bf36ece52c624cec56d4461a9a6968570d18a4c66fee3a0888aeadf36255e6c4d65c5775b4641a5568ec68006d77d503463cf202d48f67b5b8cb0692e3f42118efe080ba3f80d8f3cb1bb2722326f1d594022cff8ece8c377a455ffc05e9bbcaefc113bc2dc7a6f4d3c15ac3b2656ba596bc0300d8101954b7a7b4aacf3e9da13dffebebe54d60b6164cd401546571c662abb00a065fdc11af05d29ab71d3832f359f47becf46a4830d4ae9120b65502216541f1096e7ffc1a65c254995879aa5fd37e6860aa3a611df9992f6e74b74523d903dc02026d21cbf63c7318cad17ad40bcb90f60d34125bd45ad8d6c8462419cf03f8a003298d9ec50b7a2d8f456e72f7fbf5a350693b98fdc7fae7c690e1a7e687ecc339020d8964bd8526bed550c423a01c15b111e98c426e3be7ed04e5295662c778b6f302145b819cba89246d4b84e267d7d5a9e178a41075d37ec963595b93cfabde0228031a9bfb988fd70c4b50a0da69e4088db24780b4702a4dcbc298e454915b1ad2330460b9ad744b12b0abd7eb180aca3d6bd58409dc50c5898d27b982a9cc2b5ce228065f101c08b9a8da1b2579d0ec274f886c6a1144a50b300d5178cf67081c9589ab2c11a63028898c1760fb5196dd78d6538142efb3e8c180e8d0443e2b679e349677832b36a7688f23ddd5ccf0b330294bf76119207126d1b2db4f1ae4965a05d6cd9c3bf93ee2c5cfc12030849639b5ea3073f38dccdc1814393814852e7abb52d3d364f441a24aee1c31f43c36282c0a5de397666125bd68b727b46e7e82c898e00d23f45c0fa019ab2e3d0e96e7b975db6f5df602677c91a0b52bf52ff76dc1bdfd4c77c3e29bb273a6ac75cb4f35c3d5861cc2a155cd7f6cdaf4a30d2c959fce83d06dfa03006e3997c9f5b5758aa7165176eee96babfa96db207e84d277b66976204fad33141e646fb5d0e41ff165eee9cd663bdb93d025480ef2c483638fff8665a79873112187af8a653bc86e379f736d4f67a2b2371dc75bfb48d52a4bff0868265d8c46e0ed6a507f56c3506e769a7f3f9c019d0216c4e1773c749a3be84478f0041876a9829704d89c92afd96929abe0ec67cb882fd55209a4ef8673f9a077f9468f33d79ae33a99eb1151ea52a48711fc1735654fd8b25f74e6507b2feda57722ac17e574e4d4c286da7374acdca6cef5f79afb6164e8f4aee18db5c11a0d783df9304c13448c4abdeb5e54127372e9b289ed8ea3625f55b3afcf0f358dcc76943426dfd937733a0480cd62f88a0dcaca939ebf53bef7416b0048d6c292f5858b5a71ffa915802584c0595e8a29097759c77fdbeb6118e59f196be19d7e1ff04e42ef38d0b237ded5884be3ca3b91d980e0b9fa623b9b24289544b1d8ef1e9cd2a530b1621c2763d48638838728aea7bedb5f960051180355ab6118cefee312c130b4553870d81539054e54921c890835c7713c833ccb7a583067b5d135acc93e0843f76f72f20dc27b52efcc53b594b82a1c67c616e649982cd89b777efc38c07805f2afe8ecc09929e9087e913f6a7b27ada5c9b492e7ac7ade46d4d14c520f7a4962908a8991884cde3665e20ef9dab75d36804eb03bc3167810cc733305d1d6e758a43a99e186a7588a1b2110508e8122151beb2dc68edf46699d52e8d070133085c3214b4900e2eb84701aa19a4daaa288435bfcaedbc973a998a24fc15cb64e30c08e4ee96f96bad61d938ca2427ca27d7941821b959707b1212edc2db3d59432715751916eb8c12c511470d971b1e8d15817aa9852dc3b1a8b13cf35902955bf87b2152233cf9cf9ca75e3ff1da45dbbafb63ebd4deede79a07b069046072b914414ae4231bf6821b7a7913e4ca628ebdc8d1e687a4e6b04678293bddf426df027be35e4d027b4ac19cb9b637b0145c01af91850421f774decb5755763304cf9bc3f0b0827d9dcd11d864e350de39cfae008139451ad8619a9c2618483db77e3efe54d943e98655ce884a26c1295edd5b85f2b5101f4fd6740694f4ab151265bf19821c1dbabb4d307c38a6b487cc854c3e8b900375df4bff6cbca14e15b2ada6c4b1b48959d31d43d366a92d76300a4263c9fae1f32b613a26be1a88032b12addfbfe2431a1c1812171c4674255c8f84b3860b8c661a36ce8babb3d3f32b7c2a320917161c3267007602858b8fbeae1fd4328bf5ee049c6981dbe51f3a929a16e7186a3cd7365696682b90500000000000000000000000052ef3c03a00e45cc39473d105065421c659c27e4540817472a2dc9aa2afc75db67061a25cef45d92c65b2e786829f94cc36443c7ab52cd3bafd8ab4ae41f20b991769adb81e3f225e1127bc7ac625b92a27217efb98ef0ea7060e0d443e7e623000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a602c28fa48d410fa29a56cf653dd3589148cad41ec852ff76cf3c81600756742b486bc8fef08472bfb65cb9dd56f2d66b779672a27856cf1422312fd26f0e506ff5379317e22cf56946059207713c8b1b3768d80aa856b9760d43446b9571d3a70ba39ab4d32bfda4a2ee917aa36a02f1293c9abc658bd6bea2175cfbc6f9e03301ec6b9a49ea8d9861e72d4c5f285feef8458272a5952c63c80fed320cc39e4022f61cda6d211bf60377e9885499ac3304f8e9633209686fbe2bb145836e16f610a02e37171baeaa1962d2ce89e6c29b7d52e7eed9b4a1be7c14c790204ee895dd625e72e16d729f150b86fcbf91360c33f6f49ffbd530390952e795f510da1c68102061a5e7b3be7d6be960d8844116dd6fde3eb2b887ac9b1daec5ed2784edcf77f020fd6a88e3df9ac217cc612c7a1217303534f7f98e7f793727ebb22c6087aa8b1020739f727be16bbdb5f6fffafd94313d26e3a1596689f6e13817d7c0d142af425020180830ddb9ff6e766c964ea8296deb77ee95608fcabac4c2483f33fd2eff2e70313668414d3d133cc0fdeff8db3dfd088e0a689e1fe3e7f2d689660c0ebf9410d0dcbc68a16fea2db3cc91b79d9a5f79eae450d270016c3e830acedbb0415b4912c60e9ea125022221a6f76b2b8de281ce9f1d4579b8019dd8db06591a65818a3510d481806a6cf522d7e7b07906fd9e0150752bf99fed94201a001f662b26bb1d5c5e28c55bc30a5aa6e55be0d1341af6ee825424b31cc115ada73fd863dda1b5c9a9e8b568be846aff77cd39f59858528c67c9f0d071d7d1133905141c4e4ff80c3b8d2dacd7c324e78ea38d3d55db070cf8facfec23ec8b26e7fe963f08898bc181c6c4e8497770ad31dab96cb04f57db458c28c20a40fc134a279c8019db2fe27aa7678571557a52166ff2dd910c8edf0398f80bb0e7651f057ef3c378f672e6cf6c3d9924f7a72d4e052c0beebc27241f4271d41734cca074a46d53ef5f548ceff7d611870ecb14be93382969b8b9c135adb9a9e03a0b09141dff270ea6f9bc66f409c18ecc764f27ef35988fbcfe876506b75d66238e4737ea538008cdce2420ae2f9333749f73581f2c6385e9451bcb8edb7c369e0d2e7304b279e03bf4d6304bdf4e3c2cb4cf1e26c95a66c7e41091c13c329cf782af29320fd650a74cc1bbf583b4ab47c6bff64fa635b51d5ca8907425321c046a4a62336ff158ace90b965c21889f2458face0c8663b34c2b4c00c8f41e0a679c918052a05d39f84fd9b6de79e72019c6a6ce82961da6f5693bea88506800f7776124fae37cf0dfc354695db07164a23b7e6d4e69fcac9923dc248edf8a535a8a92401b49d9bd8eeb81f2df73abc7ef016e5605de815894cb7910963c5292c865e53d6730fc8f45f63592c7238b73c79779f1715176b2018c01dab4184ba4fda4a98c7670208f6c327ef3bdb9a982b39da43493064ebe92d4140c84b38dbdcf8d67072b395cc0b3dd63a8dde74275a50023e41240ab7219997912e5761e6ec796c6b3376c2f6a6e74bc46e04b06c136ce3c8e32396568405778e8fbc2aace1dc22a5dc393fdb2b7e58cdf392fe32225bbcef6ab0559ea8c038b86abd6903571a61d6ea871b5845fd2cc7fd9f809930add4ef802c4c83afafae7a11d607d68227dcc82d94c847c0d506062cad0bde6051034ff87637f450ac413a4a5ff3ec7b1c6c872a531828e35e302621923d8ecad3a15d23a3fed0cec1976cf0757737a6a7c465ba7a6e95f88f104a7981e1b69272011e6c92029b6fad850e7149676c20616f8cde9a4b59e927a23a59d600e4b9c998146472bcc275e10b8165e59f616255709a68471967b5673a994ff463f85213615100523353e85bf5e29166eac27004c8d92ed1142b44561d34d8008fa03ff9eeec0b44f3f304dc6f6f5c971df83b3e4e91760f1f1b44a60b10d02bb6dcfb3946eec602165af7162ec23f9c36b0bf3a0f22abf92ae8214798845aeef5e70c1945381ef30a5c191ec1cbc85ac4bb291709c97ece83456dafb958b050f65220accc7b08707d39c75ef04e936da5f2fbd491ec47fd30da4f825c3f6c6ee213b6034e1b90657929422fadd0b5fd8e97dc5551baa00ee00f5e1c6f3eb20bae18b10fcd582b6777be509dcac66ea133cb188edc4032b2012d3e634c7dcdc495439635dc9814140fd457e43a5634d1d0a028287a88e8fb8621964659670e996faa12d0a1425d4ac60cc2e851e3bda13849679b2eab73c8b0ef9b50f9ee30f2d2e23c8e16d87e51785809adb65b424dd278034c3eafe611340740382dff8350d0cc3bd1e979954c3701f0ca5e21bf3ca1ab7202a96a060033ec0e2f9a85adb3d2a1f17ad32bf31c83c4f96580fd1d085215c60333b6f26c757d5f2ac7172a98c30037d2aa85b98bc4c2e025c001", "53ac52", 1, -1333470626, 1537743641, "e35d4a3244951fae20589f51ef73d77f119cf8e4d9122606fa39db0692b9b96f"], + ["90c53b53041cb3fabbc7c88f2a2aa38d473444b37e6027e4b9e63596663603f146f0207baa0100000002006a275fb1455284162a4c361614b3038f649039a8b97dd49b9988f13c8aeb5b43f533d2a6ac000000000963526a5151656a6a52ffffffff4dee1ad730042440d831e0ad15f0b647d367ef80dc6e9817effc1b9476952eea020000000552005100ac1f0b79a3188123120eae4c050169cccd3414a766ef605ca3af2915a52c333e65acbe03e40100000003525363a065ee71027080bf0100000000076552526a0052ac161ddd03000000000951ac650063636353acc91f52e30100000000000000000aee520500000000a8d3ca3fbd4f54e5843caaee354c8d4f994897f65c1392c1c4b62eb2cbec411cd3fdd3bb0ac9fa526579e6b231a96ec81ce8a8779af54ef7fe3af07a52174acdc653103c592c464a337d53874113dcdb5c671297a4b15b801e6bac7c34bab12500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c103f48c4155a46c706b5f8a4c8a91c38734cdcc87d1fab9507f782569d6d0cba302c21bbcc27c28095614cdad8390953404daab750fbf6cc95c4cd6f64e0a26887ebfedd35e125e5eb8ecbd95037d285a4e130b0f17d42946dff13d4afe229109669b490415f9a31d665bbb13288069f0056625ba4ec2fcb57b56cf2d5a35550205445ee821990625e33935f930fa4c2d697b2baac299c9ce5472030151e9b561020cf9b690e544981d8836c3c83f8be354bcc5c82052cbfb7c65272bf1b929158c0b00140b2200f63827050fc5dad9866847522372e75c8be94716bf8596659ded8a576f47a1917e5dbd6564d00a9d672d6e1e6fb8ca8dc3db6f3e186f32b6bc94a8032398a954b10d6d715cbf44db9c7ddba0922b7e9e5cc1c017a62e1115bd0548ec0206fc9d04260d90e9841878a77fcb7328b7a754e7ef7ffd55409baa1b8e55c026021a2e1f97a8ca871c90667007bd6e71a0757fe759aa7b812e9bea7cfc9a49571a022c28d46f346aade2d9c98526283d98a90708f48060f7d4888716f6695e2801d6030c2557ad6e198e1762806fb8f211079ff89f1d468f0eab7f6ba2f1f2be9e82ef14125d2c5edc5434a6eed6c2789ed1a9573819b67c99f936058facadb9c2f46550710b627d19e7a5df91ec3dfd4e113c7d1399f73e10027721531f7283af25d54c11a48b55093a81a88358b4a12fe5893f3d11b36513483cfa9a24c0b20f4bb725a0e53c3d9a20ea4d387934b87501bf171019a252c84d66a62e43511ed96c33768a979de269a6d911f34c90489d2bf35035b15ac0b94a40fc84eb9514251230cb38b4043c2672e357d9d760fe6a761d83d41bec15012391d2c2b7623bb35a9e688a4d018348a21e04f81117123326801799a482685b0f9ea46b4881f8f7af3705d8c0be78a75ae09e2151185e18da8c12f34e7379722dbe51727864f576dcad041569c375fb976b514be32a5fa5aeb4da2f674742101622594ec2a797d8bd443dd062d676624df606d6d5a546059c85aa352658185b8a6eaef55a3d7e5a852af4701fc30404bfe2158fc9eb1117d92b4969c7a470351f80f1ca98f0f97fdc286e2812236e4a6d6b43c78054ba1b0fa8c60683bed51cb0cba8646b583b88ea041e2f6046460da9bb1d1708299dffca56ec3a032fce45b81e9450669fadad8ac04899afe9bf47f21b17f2edfae000e13f1d0aba317a4e9aeefbc62afd0a1adee76fea6a5336eee2925f3eba004b3dc6aa81896bde60616029343b73abcd227313d0e786a9d75b644591b1db2a3965b78742bcc6f90db45a3b8809350a4432383edd9aaa56075d35134b89486bbc753762d242cc97905d9af478fe2545811ec97f790a63cf60fb1cabd400d5c4f66b3a29d30678920e047637c898f40d00de61c3698f258cf6a8eb6ee8609554c6a3bd4ab15bd6d6a276bbe10e86f8c505748e5cdedce3eb10c8ee8f5aa58f1ba49f5451d232cf750c257ba9c1c6c585dcafc7e4adf0d0aee6ebe758bbd0274e5df308671558475033b0e14a0fe416aff196f882856a25d7b447d2a3cfe22846906b121e67152a4810918092a709f39bcacb579f84e85bcb79cc424fbc4f616ec9141a72945d589593b89ecf7dc970f5de83003ed7ab58a79e4b36a699e177ab49abb2d41126f7f66b20e2812b3264a2e99db15fff220d49d4404af007abfc9c9a5abc6040c83f50fef292354da0072ab427582b3ad0e2ae6374ffb775fae55e0c1832a67c5b7a9d467c823633a2207434e16dc03ac386fb7eb2dee11c5a65d272b151422f91ef580e963c185ad74793fa00a840ad9ec4a370bf7a63e5b8fb917296c8c782e820e081792105b256ef4793a93c8a3e104645c94966e337c31ce7b08f6a525b41604af1c7d88e959f5d7f703a1b1a24f52b539d4e3b4b5e6752e3b188de243154843d2d23f1496d056142754caa200647a700ef00fdf83abf2bc8589b8e653e3a4d4c51dd739cc46bf1448623b059f44b15faa7ead0da646cb0af67576c1461962910f2c6e2dba7415440b50a5a975e8f1da4e80b2978b6b26c286b37355173e23c16b007c8ce4f14b46eb8d9e6afd5c6738db553c46f28472968300735ae4771fc4f6e2cbe31920929a1cc99d1acdf6d3b88cc4fc75a765defa562de7e4315440575b101da3fde16601d122ca9061b1a18c96234e2b6cc8b51fc3b06b074cd45cb66d959c5fe4c3c137c579a564dd6a88c7e1b67973e9aac4a6706804cb905af84888a0e996ee57d47af3b938d044d506b62130725b6e4a5889ab1f9b330ac38e71c014361c1e321d9e40a4ba612ba3d1053244f1caf63b9ecc433a0c90b31fbe2abbbc0196f69fdd5dcdea4c7a3c9994f66151f2a21dc3f7ec50326e8f541fa6b5aeb8d62b929959ed745a3f77ea26bb671b941bb4640a6d7d6ec2b7d7a1e09", "5353516a", 2, 1546077898, 1537743641, "6af8ec836267323adc3df4ca66f58ecdaf5017e5f7f6477a9b17ac84a1f7693c"], + ["17072d580320cd8651c607848651fb05338e5e6ac88d6ce4011a9cc4872d1e3b1fe76accfd010000000165267bb2d6c475029eb01f1edbaa1d8aaac50bda5f8e97f90143b57b562e9df3ec3c53b4d503000000036351aca0dc343560abf9ef2d820d64c4a27179ad76287cf385106e7a4fdff56c64b52bb355e54203000000096300635100636a52519b786f8603c0dca80300000000026a6a0cffdd050000000004525163538dd5d30300000000046a515353c130466503e13dc2020000000000000000000000009a2db7a78b0ec4690c409871aeb90ed0050aacc5f472411b653ea9110e2dd14d91fbe85411ef33f96efe5a801a024c5a02c8bdedd95349ae08d83a87ec60c572770d449f5b374e28f3d755b3b163f941989c5d16c3babbd30510b9722b7b439300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000270ea6b2430630ca87bed9d38adc84c8275862e8f5e82da412fafe230e417b39fda0c00fb933765e19db4b8bc3b50c7daa28dba82f8ea3f3717bc5e473267cadc1f36c77cc75ce91c3950f617294a1d6d8bf935baeeaf2051334422faf1535cb771402cdbd07233dfcb389279e5abee20351124dd25f1e3b05b8ae1e9994a90c032af6bfe2e6cb991d29fb63c450aac6fd2300574206314fa6f2bebda37dda9e57020c48f245ba03cfdec5934eae31c5dedc41b63454cf0218d0773ed283730311a50a08247fb9c2f6e98b85c7f47759726fb216f1eb242c087fb505208b0948ead9026056bb54d16171f57f59c6982936f27de3782bc08104a0121e21f8e16cb84246031bc4c2ed68b7074f1b40d6839bf3a11c40346c67739bb0e27aaac132c5f8b0f1030881919956284a0aac482b9ed863d9b39169fef9774805cdea784248f5b0d0a20211a6a5177ef3878db7f6493d8a2ecf02ee35d290c971b8a31fa983013335fa65022e13e863735249cd5bb8edb3f2e9ecff5f24fa415fa8f3f89080eb9cc5659a65031a6a6e742ca418fd28a8fa6025a4fd9da39746a616457a7c1bff0a2174aca5a098be83dadb23f28ed1bd7fb4fed755b9a285ccc4d3b002215ce1b4629c95ce1c1a823dfeb8c6057fa2f97a8ec895debd1b53884820ef9e28cac6bde7dafc2789d252bacf8818344c7d5dcd442bcad89bb7b8a756967bdfd1a3e24b9625a09641ee11e19e5ebf93356a95ae02504343420dbc5a83e6372171f520e199d06bee03b448a5165621d0e1feaea4300cc712f6090b30c27b20dafd2e846f166d0a003ba15c0fcdf00c16dfbcdc4ff918fbc95c1ff946fb9b437cfbf82340c54a3341da8117aeea9d88122916c32a3c16881a84fb56e38081bff09605b08cf11d74dbd3a047a5d99bab8cb10fdc3644c5e282d6e0f34b4b7020305e33939c9ed67d1b7ced6f7d8f736ac3ebf40cae4b294afc89b1f9caa20433007d180630103a6a7b5c51b817f195f4cc59c52db91bdb69051c88882ad0c030b80e91109767dcea9fcf564eb0d4bc6aaad7615b32e52b182ca9b94bbb58b9991ef51f648a71646e7dac6d85abe53d688cf15f2c83095cb122bdc112e581240a391aecf9ee96ecbd20ecfb19b6c4974234cb395408da490769cdf899933cebb4a153e1103a1b6afe2bbde8ab1a43cf23ca2ee688de3a1edaebca0ecf36fcdc522e27b05250d24c8e92efbaae9b2174393a8948e0526434288a73deb20a70501335f4b8a9394a2caf6cb1aa679190599c11eed98916b3cb426ce3d0a90815a54ac2dc202978659927401533c7ba652f5034bcebe58b96e5078bbe713c33ff8db8f73b3134646dbbf9529ab1f4f5c7361cf62ed31cce225e660febfda6da10fe70fa0dd1c158fd481414deb3a2725248ee89636b9d3f11058ccdee70f5eae75b43fde9191942bfa15d9de7fb591c23325f36c908ead1bd9592d2886ea633cea9afe9f0c20e2c97046bcf640e50aad9bbd2c7372f1f7614884db794ab9d61cf379e475efc5b5680c7d3005e918dc0d61ff322266a90ac2a07bfdbc1b1bd65c3f2d6ca71280b577e8e22784a63d3e8e5f3253dbe98f1271045c903ddb9c3b58b6ba7730c6c08b6139d2e796431a1dcbd97876ea91a1f05e025ba38c5a93b7c4b6f9f77d27b9ad405147ddb97e0ab8a4443cf9ce2099bd774a11432c379c23015a30fb6593b192f9a52ecf26ec6aa90cd7abfe906974714c55e933f1d7bbde81d7bb7d6e128fcc1984ca66d110e3b15bea9a09417fa0c63701e0a157e0376e1837a119181d114608c6cb0525745cf357c45c21787f7e6db3ae2f7beaff44c5c549bb0577e3d6c4c92844e6ad4507ff81d6e37e1c9aa3fa10355827595cc86454fe6caac5a3374651ece45c4be24f51ad952590d4fb1a12dfc4499cab8cce74e8406589ad53ace6d784a5d3239dd07cd5985176090a806bff60333da772bf7aa91c08911eb67b19124e0bfd7c294dee95b050868977edf5fb806c90f7115b8191d961b8bef387c9985eeb2d421c9b8acad2dc4c66161428c7327b41cf3407b6b9092585c0e04d8a79e61c6f73f75ab9e409c362ab206f76cccb1c89526a0872e16ff1f5ba1ab30cf71463bc40e827703ff87074612fa97436a119605d597d1e8c7d57e4ff671fffb78d2d814a27d905ee63e40678e2215834b04efa677cfbbf33c34b12cee71b84b1a6f25dd7d804a1498b9a3c6b686690074dac6265d2c83c4b36a043ef41fa71168c9e0c8a8469ebf6f4d76158fd24982ea2b9d9bbe3e0a9f6717040000000000000000000000000106392021d690fdc6cfb1e2240b4d68f7f76247d8b0f1f5f12fd758f1a7c8a964b72e7663795a88990d51c2284f805a7e94b9ffd00ac7ff8540c9820d082c09cc7f2682dafa817d5577613be7eb9be73abbc277fb3e49c9734af4cf288f31f100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000524d3bfd7a0d5adf30223570a34a7b69af20f6a9694c108bd7ebb0033351245be3ab44fae8c332724789474196d83da0cfb700db2b7f023c96b65281b6abe0b7cabf8452d20a900935111ea95c482dfc80fc02989123db705002bee58e9d1d9f0168e531c8c28127d78cba2b52d8d6e015808724f912d164b6bb6deef293c6720308ef6f4ac93a13e0ed163d6e59cba84f5596faa99fbb9758993d59800bbda625032a4d688690dfe8aa62aa37f5c57ad6b038bc0c2c817ef0ecdd73788ebdf7a26d0b0326ffc51db8c7e10ffeceb1843e3ea58d667d4366223b308d29037836c64ff31e8629bcd877dfdbc82fd6db9233c7470bac6c29e2dbecb7c0156ee2da211b0e021c526c02c8bef517ea4c1f18935a7702fc7b2ad20f03ea7637162f370028f0b4030a4920c7173696a949119e897c2b3060df747540358ae6da97a3f72463710ec8030af5bab214c31c3ec71905c18aae1d5a0eb86546978519ff873921462d54d50e031d8d4dfec191ec9cd4643ce5f9c025048723dd3a4599ae86e59f46dd258a9dec02020f23d99938ab5c4cd70f134e01bcf538b17ea0be405b20e817e90f31609da89cc7dd3c95eb737bafeeec9146d6f78aa6201165957b23c3ba50e62f9dc3886d39232d07e5c0621d55b216a4cbd3f918fe5aecd6b82ed1963bb2c3a7c02a2634d42a88cfc98ac5c867386e6e559cba7fd89b58bfea945dd9679441c17954db96d81a2c83c84478a7b81499feacd066b761c7fc4d30735cb1347b1126455381cfdd08e48b09a1de33b220fd05433e4085bb001f9e2a21994193772223880b5fd2fcdfb125e84acba12ebff7ef28267773e579aa828e7d7be9a4e427ba6711bc5f4b4a7dc855566437577832fc7c240df897dc59a029d4ba15f21d57a71f2bc5b658d1d40ec7382852a6be1813b486a12589323b97e9da884b42f2d361dfd93c133f16bd7021d530796b86a273dfdf1d4da95c84208bb68702dd1ea1e741fd757cf543260e3aaf94f0a22be73492fe2f5a417f330d10cc09d2b403c73e893499c5a236e93f42169acfd0ac67fce29ea5a8327b4843ff1bc790a4d4959819751c2b0b13dc2cf0bcd82292365de3d776ead9b77edfadf324680d12d4bc3f539f64faa3dd9f5a18e7d4f1611ad3b1621f58923220b33684298811f7ca426383ed325afdf277a9a8a1764364ef174f53a08fc078bebfd321c2b9d81bc1e4cc4be628365804e2f8b3e09cbf344cd4b9407f6c5d442bb4242f0387f6cb99b76b2f1b64efe6c06e36bf4a66d22aa81d577421585b6f1bfa3990f027daf86dec17501332bbdd0e3b249202599263309b63d7e56dc5b2c505ad934994275a25fe6b0d5ccc770a53735b28de489b09270a5a1ec04098ce5b0afe13f0918c5897ce320d231e72ae7cbb405425a298bc0e07efe6c536429e1a4533fca0a4d3e0588f3577eea0d314679912a560b442edb36f6171f249c4491ed6235a293eaa0f536220fa2ada6ffbc95c84d5a5e1f79bf5b34bbeea8d630f84c2863c173813734e7a6778fea9a3bc3ccdce67eff88108fdfd4c8629921a79dfa6f763d9822c42d5bcaf927764e32769779c659965bdc6a5c3208bd753e05c6ebb494232e74cd4d09ab2ffe9dda1110477207bc96a9df9635b031ae11ddf2e51a4ed0092d4f425b83a966db2e966ce2daf23aedf14b6154a39e01b09200ec5ff6a3b4052e8eb8cda8947a63fab1278d2ee71073294d47e149e6199b6f641299dda690d179367b79b04cbae6683c8cbab1309d9045e57d7c0b4048a867cf5690b70d09c7672178a4de6e30efe5341c8a4c187fab9548510fb6e0600cf329e70b19b82ca966360bfe9599e0d1eb3b1cafb992f24fe6793c4de9c586755913cf3de4a66c66eed5f4d26696100218af709bb58c5fc7dd76759abc2a767b790f44ecf6be851e59395bb6f0f86e991ba831e676ca697dce487085e863e74175ffa00b36756a4b9f07e6f5970238a35d6022eab479267267d8806b30350488f767c2ad710f8b388704267f328b78ec7562acb23a7f010b76c03ec122246b2252458638001ca306194fbbdff0a17e5e49d0765e9d92f85e866a01dc4c5354328534865792d0ab289256c6da23024f9ab555fc3a8c542fc3a2dadd559835d23adffc9e775b65550a93928d9c05b653a8557f70b25196001c355c69c4e30ed93363deeb773e6be3f694dcc4605e8abcd6e869c0b9bd6b64d5a243af7f5014574dc9bb9c2e5f2cce19b6073631b3ca4f296db8cdef978a49dc03f1ea4f1d8093969b90b71db000000000000000062dcc705000000006456c0f624bde7618a677a4d9eded2cc605c208d925dd0b429501da946894dda9f4f59f808b7529196a9a0de646753138ba2c1523d65c2e61b924a116f3b93aa38316074eac1a82a469d8f40613994f9d4b16cebd9dab6a67c9d971512e73728000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a50da206fcec4f8c8a89a6e28c7e7e7c4395b5715c4c4de4c87f0c7cf9f62d2bf175dc05e1892867f0cfc0915d8738f9598810aaf45445cbcd12fee715eb638bb1c504835cd69cc8b8545b77dd285efb0797bc65233f8505f633a83483b1885182235ab6e1ec35f05f1932ffa6b962790add5505347f39c6e0141d4f0ab1b12030079955b062af44f51243234d895574fd584842039d2e7e2208ea2f61e344e0c032c63973b6df88e3723e5c2f4d7f2c626e66fa580597acc5667e4f80611e4b1c80b00b849e9538f4542ae2430ed5885410ffeff3324155dbfe04a2b04bbd93c26214c3ed8fe99935ad64e027e218834218bcd0c3c785f118b5988f9d76cd057a07d03044b5bfe6b03469adf1135662f41144b3e53df3d32ba420f03cbde18644d2651030095afeec6ee1d057650f28aaa1616d41bff61d424050ec9fcbd34009b16ebdf020ff24c585dd4980fefc0ff67a30b4a9b0a3c8c69119ba37c56b04c68c03993ac031d41fcd19bad18476419029887cff6afbfc66a090e4180243a5025b8044ea9ef0207e678bfdee2450575a611802b16d30884e2f152260f2fd65ac867100d659ed7c42d9243986757233405ba227e064561c6b4df740eddc93020499cf83400f4221b8d9f30cd2332466da7be3a439348901a218e9c8ce1fc54c6a74dc2f30b55b60cf84c11f9e4c132788da0bb86ff1135d206b6797d4f42a2ff67f65b7d0815196e1e5f9bcd920c48b0d577ac36d47d4c2ab1fe6faf16c7882b91b28972c63c64cba88328fc48ec029b3d0d69396f0ecbcf2bbc3c4c78329678787ff3f540a656003530a7516c8609cb6e12f0304c76b84b796df18363ce7069340e31a64588f789df6ca14a38057b82c3da6b20823e65df8aa33cf52e614a15bdb2cf713ee8a9e174d5940780fba2e7d619fe1edb0e06afaec362d61fe9d13734ce1c936cba2a30b1741981a7d2361a74667fee1ab06d7ae6f0ebbb37f482575b9aef66c2ac213b22b84ce2428441ab842d10cb5d47ac591ebc27d7cb2c8904a4e9491e24c3c916ca5717c4d435891d99ce1efa24dd9a83991971cb096a11f692690a236b7382c8e84aba32f7fd14c449ffaa21b951760796c28134d2b2870b32e31708ac7c6227a89c39ff6358f062e8cb87baa2ee8967baf7e8158f1893ad666dcd200f9f54f3e0d40bd97b0b020efa54e7b47b9298c03810b8e9f1bb0b48b10881e563649debc2abdb8ebd49310f6edd2c1e216dd9496a52161fb59f99c61a57aadab60001b349845649346bff38134ee3fe3364f185979d3eef073aec8e0290042cc8236ff46db5b21dd97fec5e8765b3370e694b846b03eccf9b139e6a32a45c2536526963f4f32cae41a952456befad2ab03aaa7669964bc4c3bde4cceab0b12780d5066de1cbaf0aa4c610bf3555faee2c6a72866b6d5c84c5432f807d2fde5b29c0c67817db5c6b821e97ce372bcb10127252fa029446e8b29bc13171a1fd0df5f8f0e948d2893edebaac8fc55e0d6b482a47fb145b6e64f6a5f79990e2ad4b93824ec13e1fdebba33b7e3198dceb0899e91b3f7ffafb6ba7308c9d2ef722a0bc19fc3a1b265077fea222af5fee996b3d47616ecefc7385c86700d24a663ef43c37407399694d72cd46e75453924812b7f560e03a2cdc54152181dd37116a8eab15735cf321ebf1447ce527d9fe3b3f2fb7ddbb740ffe7149d15eb2025e78b75a598337accd1cc41e670222e51d2b141b9bc5ffc4189cd5a03632c6fa929c04be1e7ca882de58ba7ff8c2a4da42c7bffd9b7d8c5cb17355a3e8603242c76c6d2fc43aba369cb2709b86df7239fcaee4b52d8fd242c1470a36918b81a62e951eb0deb098298ed05138a59a0288e1c6699b2928e3c6b37e75f5b7b356447bc7a7a5ed8a976970ec758b3cd72a0ec26344f8cb7f2fe92dfe6aaa2e5d1554cf88c602ee5f0ec7bba718233a602e9879b6c36a31023953906369d05d3a4217a899f758beeecf75947958f59c3932b19e1025e50b19b034ca03e4d4f01b2444d15349108bb42deea7e8972a02644ed42458bb1de656b7d6c839a5262daa5c4c5303302bad3e0cb6757d11146e74b9a7c237f6cd36342d8d4274f99fc73811d0e032cd522c2f7a0ab4b85c8c5d56e0d9289c28e8cc5c53ba4aab0d26a942b82b935e9f8570a0877e43dd5fff71175162340602a6e43b4505e2b65cbf06bcf866a2ccbac80380de5d303e1d0ab9abdb7cd4d2e34a50785a675f65fc6d5335a5f345d0c11607d7867a6d48c73bd54474169f6945f7f21796f852602c469321e4d4b49e29148895bd4517258db02fa44124f215dbfe3c9333facd1d871f09f6b36580ef4d5aa859cacd657d522e5c22005bb6ee8207fef61fa93ddfbcc26494d13e96eaaf24271bb453c852cd95f1bce1bf7f048d62dde54e00", "5100", 0, 970543965, 1537743641, "a6058f151aebe5b53b57287c02e74ee55cf464f19408d656668d4111c36619a9"], + ["83e2f965039b5f55bbbe32ed9f0333b922a0a23995d46f5fae602cbe1eed6fb75f9637afd400000000050051516aacffffffff09014a2d743eca497fada29ef727934fd68d50a9178af8779cf0829f77dc7bd201000000090051acacac6553ac52d2f0b4a88d446916fddf4c8bba1f80e2dbd8412110fb4cbe7f1cb3edbc34072fd429cd0c020000000800655351536a5100ffffffff01a25e8d03000000000452ac636373332ec800", "", 2, -2057996197, 1537743641, "3ab7df21a533ae3533479871924bc0916a1d6bbe9d23a07531927f363d31152e"], + ["29dbb6020252a291d26f565e3582f385faf7e1a853b9ef7911a9a184b54a3b00c67b2a043403000000066363635153006cf1ab756131268f80dcaf16268265f2a6a5cd504c57cc16dc40eefc38c5808949950e360100000004ac525151ffffffff03d550bf0100000000086a52ac656a65ac5375d29402000000000263632f962b030000000002656a38db086700", "", 0, -442102597, 1537743641, "4d600511cc4e68ed2c5174717803cdda42712089c1e6c99f37291772d18111a2"], + ["030000807082c40304e086bf8400dd6ac5fa6bc135c1c5c8d84152158cac8cb066d3e32b37204eb0d20300000009630052526551006300cf36ecffe92fcd2edf044f16a85dc525d86231b583629495833ef82697ab45d6fafae97b02000000056aac6a516a122a24c0981469bffd2c240fdd0c8f8ca5752ff15f6322c4baad867e0ab1c493586ebc8103000000045353006a2e40ed70ab8cc8cb95b2500e4f1e4056a390a36c46c1c785ab49592236dd17098da4dbd802000000065300520063522b18a83b048e2dd90200000000001cd392020000000007515163510053acaeb7df020000000005ac6365510048001b04000000000151cc5bd106966c9a6001be6a26020000000000000000000000005772b9b5a4ee5ddff75c5ed710d45325d7dc354a126c270540cbaf86bb36c7af77d873bb93e7240fa3f963e5c9d8ec955d3216054ebbbfefa0fbed4dcd75bbe5ce7bd80c08d11a81305bb19d21ebb325a52474083d81141a31ec332f58c02f9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120bed51946b9c476bf8e5cdff84980de93629f6ae9cfc43a72e2b5d74eab9101db023c5bea1543c0d70868bd282bb88c235f647cc26a832d61d4b1faee63041ae64c638479d7d2e71f90260dd7a3991c56dc7a61245cd3ad226168f65a5c36412eb0b571ac8f16963be4aa64038312017b7ca2e80950e8dedf84bdaf366fcc2031036ee603535ea325862defd163f7309d1b0ed6fe114ec0426d037d3986b80e60206f7ee4156b62bb13e677e25d391fe29a1c4fc86a632b453c2872fe0cac8a60b0b0467305c018943384a1eb9b2c914166e03355cee4c5de68022e600ff01de4836fd9ff197f37fad5eef4df9ffb06646e01f6534fc6385af604e458ff7f8c4c5d7030c1d004c3dc45408265276bfb4f063ccc3d511c3e1389ae1e10a3212d1c9176f0329993af8bec40f0c590f3fb319a72ee232f8d38057efd40e9996a48436e5303f022d013d68a3ec677769e07fe89eaf1d9790dbe6bacb4062a54eebb929c131d2e403087602812312e5ded235ffe6b1f4bb3d466414dc85c9956ca8bf592e3635bfef0316ab0757fb2e87941db44327ba327d346c13fb92250dc7f21dd488229672488409aa13350fe4b0726831ace84637800c51c313566388d53eae116c0a951b3761340b801572896cb3231241e63e790629f080f18c90d136e4d096f592ee288b7ff0a5a85b9b8a32a7d321aa012a8ba3a970f0661cd2643610fbfea4df5ededc7277cc441643c03331e27043fb6ac3482a6adc0fda0a8a48c716365415a1c5fe166563c809f911fd6db38510d5b4874cae7a45efe87533b006f4e760ded0cf028b8dbe60bebcd87e368cd8af63ea032310a7c4f46c171b111fec1ecea00ca687eb18bc13a23b5208514e73759220e1cf553139235d1d52b71a37ce863857f5c7a5d80a718d12ac0cbecff7c6be974630291056e2d17e0d8e593e02edce73af0e125f9f70adafd62cbfb28579880070510ae028462252885837a48a19f0b454e8f0e03944e2ad7286886d02e4a654f0dec1767cf021c84c70b1d0a08df12d78425f3c1ed3eff52f182053676b7370443d5b40b03452fb9cc30f778a67f4eb63cd68401aadb4ca1b983b2e4e2b59512ae09380448cabaee8cd6cf8a5f53994a70e91c1095b37ba9d4edcf5c28c556519cbae8ed3773a92ecc10c8c67c85a49ab5a65896ff0e864e10414db4515257bb102a89152cc12974cc41f489f528d72f65354f3248555b5cae5ba676959eb61dadefb926baabb7b84545fa7b60f395d7b3e33c9cf12fe4dd262502a21b23aedfb30c9ba8d6e980271cd6294baadfe8b6df27c081c1c756a959b707fb57ce2bfdb02a0ae354d5c315a711321a8320fa7690a1fcde8f1a3a68d31a84805d0cbd1121420f4a37b3d7dafa1e10d1cfc7b53e6154c0c5f692e722dcb84713a584810551d427d6aed66b61a041160b784e5751ca3dea8cc519f3cec8d435b75523d904c50c5ef04947321305f477c00c3c9c16614bd55b885b64e731e2dfe2d0a1dc727dfcf0d1bee0e097c4c2a85da99bf69dc2949f928087ac936b5e6f11eb354fa85ea792781327e03acf53b55d363ac9c7daac054575785dbeeee327c8b3f9c74e2ada82b78224bd29b06b26923a72b5aff1c9dac69be9ed715bb9d7c8b55707a8c3f42e960a562db72658724d4565cb0fcd7e65f17c6a450a4a49bd42780ec5e9dfca59b9b44a7e83d668a25f161dbe297ce1ef0ba15e96b6dd0227a3b30006c754834164c27771fbb857bb00a72b3fc39ba77925f5422fe8024a0d6425b993a3cfbf9a568186b4c54c44e31d82a84cd981f757f445be52935956e880b5a5dbea9410ebf57c8898972645c84cedc3c0f733b234ba975c5b1171b3607c7f2bbbe323b9bb4645ec5b044068ef66c85efa3c3546cb214bf1a6b329a475e8083ccf5a6d64a25c89219a2bd92a22376e32cd8c66edc8f76adcb9fdf569cc50d1837dc1fd76714e66cbd854317eb577fdd17b66c8cbecf4b8ba9c1998eaf666072f233f5d1b5fdc1cfe8752402bf7bc50d07038502abf0bdddba26a3b00eb8dcc380d027e1d53cb4d29d096bb69950906e8f6115e239002298065f38312be6329800f52a76706b5705bd09cb900f25671876d43f767319e96f96b89287cb4c46c41c77a380765482ac4e1aa15e54f4a789c98ccca37220a1ade4150095925de747c734192b1204c3fcbc611963b39ce605675afbcbe4fa46e7cbc64230fc7eccbd390fe012e96506c88cd6729deee306f1ea18022e94e1de4e78509822eb53dfdbc2a5ecbc6db3ffa7f35165d9c61d9d5e384f516f6916fa31f37f5e563055a65a3248c7332326e8a115c240a73297da816c345fc1a12e3fc979ee9e22f48eb64b52fe0987369f111a21097ecb1c7d01944a742579da4103fdbe9e2310851ce5e667f65a9a21bb0e", "65ac5152", 2, 257261756, 0, "5afb6c16590ae338a8d1ce24be89b8414532caf87ce3fe0cdf117de5039e1726"], + ["9b66f93a0218806da33bbc3b184f77821eadfabcc458ed5adab55c4a8287df23ae5bc52fae01000000066a000053536affffffffd2b3a1d7dc201dee5270bc7cc4b991d6c3d0d7c2159a16cea4e61c00f77b7771000000000163256cb973025b7ba60500000000066a6552536aac2b76400100000000095100636a5252515265000000000100000000000000004d4cef03000000000583210a6fa19db3139288b5a8bff1b4531d7d7624e9075b78bf6fe5674061ec31e9fdefb29099891c6f7cb63505a36f95ea4ff243830295af4430d9d85ff37f0b633b32e76e8a1e3259dcd71c3c1db19ee02225c52fd7d1d6c29127a44d6a2e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051175fcf93fb2e7287392567269fae8e61f04a3932571ba7fc74c6f7bace0c376f4e283b8d4af1c577d60465e667da3b10e7352d12cc4350dc66ce84385eb06be0a61f6ddc977617733c6172ab3137a30e29f5f2cc21e3d746ff615078245515922adc63105e65c153b9358508173bbc81e482a6117626002f896aad2793990032e0e722b64ca38755477a172d01dce2887284cc60ab1d28157dbee901975c9b702271c30608e9d0ca559f9b76c1f4c8d413ca426d6650588371d383240a89f5efd0b03f332397a248f596aa6187ac10f3d0e0dfad6c9b571367fe804d67a480dded8aad21961e43e1c70df96be5d8901080298d9b8e473ecd3d90573750534a22962032e8b63499dacaefdac1208c82258b9d81b82fb6a8808403e0466073246ad9fd60200544461ee2a59a9cd8a30856d202da66dc4e2e68fa65ed22b9c65b4c94ce4d2020242d4614f1b996367c37914d46bc7dc51e7cb2644f26c630f847a6bed624f5b032a51f417dba790a6137406fc9290816b2701c5f4f272e3d1427f228132394267022dc71e9c608976013c8b0df3f5e64fcc2a8d0fb28bc3288686b4c1f1d5fb8e415e6123fec49232c47283b793ce4e8bcd6ba5614b2bd24ce0d6160ce2ca3f11304b9077d9e9a7c82c7fc6a2fe436da441d23e4ded8c24b12767a5a102a3bec8d9f2e9ce308926477baf5c18762b70687f5590b98fd72a8ee5398c6280dd557f75c4672c60c8f23dede4bae0ebae771cb1fd469d50831d4fe5c7a6ccdc2e643d86617f46e7fe807e0722c632dcf994b0235cf1ab768f44cd56892a38e455b09cf6c02683a3f5b954485783f14a3b71452f0d9d5b4c8a54ce0637a0e7c60f8ebefad7abc17a2533afb76281fe95d5217c8934dca8dbd711595074126393caeb3a5980033d6af333b4fc06bd1b2991be34c5b5181378581b6843ccb0bba815c46569c5935ada8fe6a176dbc8a1305c2c869a2b1dce7da8a715b5a3629c05384fda1793d98b568c94b6f65a9e2aa040571597b23b495d2ef445a9c5eedb14aa3e38b84234d055893a7e197eb4d02142b77495c0f662e4f37231b45119e95cde422844cf933589810d253452c42a84bd19a5084d6d4f844efee146fa09a8c588963bad111935c29b3393f9f868ea5e1af6d6f6ceec9ed3ee72a95d3fe1665a75ae3679143abd3dbf5a9b908f0a1849c9ee5266973bb25798255196fc72609e6058fcae35c4410fd399be39786f7f335ec84bbd251ec9014cd920c39740daa51ba6585920775c1e3cb023c5ba2df90316a753cde65d6f69329a7988742924934b847c86266f537fd76436a0e4db131cf7d70ae7196bebe7d7a9c011231ab743eb24656aac1a15ab5182e90a4ff4ed20ffeea733bd201e7721bfb45b409ee889ad131700fbb050f1fc23f56fb82d30b59e38454b0f431ddb5ec18a87a0fcd78880d8a5ce1582555982737058e6fdf1144b2aa3fdf9d2e4cac6341de6660f73b5f9742d94847bf41e90c3d3d04c057297ea6e2f050df4e0f5c18fb321a66ef350c9a577af0b22c923c8d67657a866237980c17abb0bf4cd23bc6c8fade5b83f922ced20852ccc0f6fa2fcbb74e1d4416ba09ce1f9993708692ea0256b3823abcd10a82b06589a3fe6442a6c5e34811b273e3bdbea734c33530af94941ade9daaa9d94dac07875759e8ba4385189bc1b598b0b494d0ae60b2abdad9e892f500a2100662918d7ead3dc3aa68dafee49ff490136b8063310b1a941eb5e8a5f234397370bd8cdf579879faca5c72d5c11cff7314b051e277197d1225c30e0d8da1564e7d5edb5ba4afecf1d8908184a08d8fe8a1580cd7ebb9c19ba696bd1298b9ef05e47559df2ada2a07de90d8ea90abdc76c4ccea1b393a35b8902830a926f4e0b42d363c77b3b5a02586c2ac7da55776b4b14600ae0f7d0d88153145a438bfe3f386593f0d7cd9e628318dc1c2ddeafe552b7a64a424571c828079cd1b8fe350fb22c75f148ad69c767aac1c59efe9636ecfd1b3fc0ddeef1c00177eed424e8cd52163d2f8d84492ffb1ad7fdb2461f80425f35305baab32cb02421be561514bebef85b2273b12a74dafe04c52bbe694110b5ed9fdb80df0678fdd09fe1f71041e0e3a18a1b5a499b0c6938809dd12ba6d39a0fb3a8a4668a60fb5f4ba5eaa504e8be525d9b205109776d59e50ac51e1efa72b27bfadb32b86479cbb21633534cd2d446d8c0ce0b7062ceff0ba7d3710260b80a6d8b0d828d8f690fe3a60c95ab6c13c8ba891def12660d4f55b68fcc765cfc754b19f4606e3e11074079fefd4e2489e5c9f51b45955d63e2633742a0a18c22ea262a03c88ced0e6c47f64930cd390d48466da477d55cef858e519babb18fb42f5e72776543ec1546b997a42483400c529aebb7e0539986925158e221ed10057e2b760e", "", 1, -515577429, 1537743641, "e8dac40a791db463d3937d323342a073ef3cf8709c8da9aa58ae709cd06d70b2"], + ["94ba7d3e04fccc8d447527d871d88ce9c3b4881eb085508b41c0d4140504b5c269b8f7d044010000000200518c4d895a2a5807dea1848a158be0d2210e2bc29cc3fa09c89b250f2d9e5a11cec7be00960100000009005363ac536a51516affffffff7d3736661367cc111e4cb8191669700fd1b281fb116db85fedd91fcb9337a347010000000863635152656a6365f38f95e2892b4edaca13c39a7b6bcc5d504eb0a1c05a9ff409a9f396d1d02958f652d24e000000000851636a006a006a65ffffffff02dec9630200000000005f4eed030000000003515365984f64c301463951020000000000000000000000007e76125e8fa2f4ab1529bd0b6de977d92771601cce89ac1fbc3c9415c68c7f76ba5c825828baabd862e682de7f5acbc50422f39e30a773cfcbea5f0f4a29fd00751b05535e21590da1d44ca6a42c18ffc606af8dbe0a829f0aadb3555e9c647c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf074f6eac949d639a99bb1cc5d3ea59bb502f936080a44a189447672d6bd627d54e2795148916e2f9c1210a99b200642b0a8ef48b0876a323083eb444e01dbd949b5840a49ee32a80d66d395448cc072a5570ea36c7a76853940a9a7626492ed73fad93603925f3505c7d2c199d952c19f22e779f173bef8bc39a9a4a518ae6032b0781deeae1c61edfe232414af6955cad4cf0dfabeb9a0bf86f444f0c4ed745021fc3cf047d74c7d72bca0733cccab0accb1545ae3dbb9aeb84af5ffbf41eebd00a0389a8934ba8a046707513d8de3ac5d394d28c7646f81540ab2477b2731b1fd205bccd7b71f35fdfe222076cf8c1e93cb45ff20eec4fb2d7ba74141f95e9691c03257096368984d7d6d0cf3fb752b65df17484f5bb3403846150670bd20be737250307cc76bc14b192cb34d5cc42c6f7897ec7ca2c78cdec03d84fd0bd5a52265a180301151186df167b6e3df27026d96402b4712937ecaea3c05f887aefabbb298616022eaf9e661c2eeb3fae0c3ead42cb5cba2673e1a12fe30a3453c349ef9726683d031ddde49d859deb954be368266b88ea3dcbfad257b28204be6d388ff7dd1e67b278241fb1dbda0e7ea0e916667338497e654c57b7c529a163dfa032fa18d8cd66a30411534f03de906c52903e579a11f5653b00b93401d5ecfca96db830e67f83ca88c9e5ee9ebe43c98a5eca2476fc6e20f8750c1472bc8ad55e346d1645a14441fbf1084f7bfa723610ca0592ee260c272b3d309696fe86e6710aa2527cb74b2864f0daeefa96f6338d072e37278ac3070f98573a29991a797b275e821ae5b99368239f7f2387c20148056e5cc42d4046943112c05d61158a48283bf34c9edeafdff88f45712be946a5935283e6718fdf17448e8d62dfd771d35fc3ce2661ce04ac33cd9b6f299227d9fc97d8d7d0638e3cf67498118397de5a53d3640e54e14586c68fb87f0707f15f8b3618b5f85f6cf5d27a93c8712a4dcdbc5199372666af56ce3c09f917c54f210427cb01ebff775d9e2e3f2cd9132df44ecd5b621cb61624af68b07b04e9fa5d9f06ab716c48d14f6831c5cdf668996135929d27e7c52648463db30b0c9efd15f5542ac441e3c288103ddb991a0533c74bc31f65a71723ac4b33757adb4db6d9835f4dcd2d18dc58463b112abe6bdb0dde17b57db08dfd73a470371e0efe7a2aeed148639791fc903ae5f2933d2eadbb17926c8e6f9b8fbdcdc3dbdd5cc97157d622724ea414ecbc6932a2be8e15e41d56b06c4099bdc3d2396b571040351c3589c6fe4706c96937e211c8f7c704b9e7c7e7b412b708bfef648b0a54013caa53ed5dea54220dd3c37b61c55ee5e09e3724896dbc9b4fdd0fb6c037c3c0746af52c26195e947ddb16f6d2df21b12ac6e368edd380cdcb905fddc067a59f2d796a10bca89810daa6271812b7b2cc3e67aa2280d0bc00a2c1a1f3180fac954fa0461fefe153eda11cd68e23905c1328dc202eb74c13ea57d252244dae6deaaee8fbe884a62e28f5c71237b1dc0b23736b8d2969f1952a2030da700116e2845afbc44c064f71cd021e491941ece675df5161e98913dfbff1ab493afccde48218af312df2916fcae41fecc52348c81361dcf33c1bd54138c6f9a65621fc8b101100faa9b1bc77bd62ec6fcc4fc0193baa92fdd13aa9fd6b7d7d51f41a45c2bcb3109644f627d5771d31998a218577a52fa0935a90bad8939183ea722387afbca66f4d092d7ed1bbb578caf0fd5eceda9128497e6fc4d6583baf9d3cd389dc86764a546ddeb818270a1d5c865beca38192bd3c5d9b90a7422f93cd2715985e6867051088ba1f951dd57e9d99189f17a4c3f44de1d3c103c2c81f6d9f6f7e8d1765126b0f7fb95bfd2fe0eeda6b6f6b16165a221dfd03b6a794b8bed5b623d889f74192642ed6597f426d9938a1ff584a1ee1db7fb95b5891ffdefa4cb1dd14ffa9d8a8e06629c9be9df73b8dbb3b75b017f8285b0a9596888de8fb924661016c6c8758c244d999715d1fe3d991fae51a4b14a9db8ae06eb19165adef144b42493b432a7d772bf5a195e5b708b21251382d075fe6be38cdff6bc0dff40f2fcc2d0d8a4a686d1fb3e482f4a8534f5998343589ca233042aa73c885dbdaa02ea4315de4a5e3ebc15e156442653b1d2ab00104f697aff72c39341bc9e63179eb0bcaaecab5550c26da3bfbf2be6ce9f37b8de758648ae3990c4c0cce2202616ec426614edfac3bfbb0066b01c1441b098617446498832ec5ed15cd93b47487fa35e19e969426485d860b9be2ce0e5393ca136fdeab6f620956fa1f77879f5a203d376664787b0c066a78422c5764ff0f470c1b8c1dfe7755e60902d12d7d08ae4ec612e2191948fc5d33f8d18187b9b6008c355ed0571ea1a2345d22278ec7532adf07a747f17bf7d1fd6e9601", "525352", 0, 23847538, 0, "50a3e5653c1cceb744124a27b3b4fac9f88f96f1eae543033ec0888ffd80b8ba"], + ["030000807082c40301c049c55b9382d7d22eb2e8cd353918ea4cd32453c250c8564edee90eeb724a610100000009516aacac5152ac530093b14f3a04c8e03104000000000563516a006a1022340400000000066565ac635263c11a4001000000000251005dac150000000000026a6573b57f109e0c7d6d00", "63656352636aac6565", 0, 66388475, 0, "504f05b47a82f3e7aa6e81060a5bc54a101c2148b2bd6c75059bb8c09f0892ca"], + ["030000807082c40301503b9dd029ab29c99ba5fa74c1a3e22dad1810be3d208f8d2f5ea4c3d54c10a80300000003655200fb8f4510025d27ed0400000000055251515365100b9502000000000081c33dbe1ef791420238a96e000000000000000000000000004331e795dfe815d0e7afa230aa9e167abf86b7bf378d8b4b8a8b179951e92b462c9a4955538b05379c66ea6a31737bb7421f6d59b78af3b95260ee8e83c0d9e82cb2b24137b3f47f756392864988f90f52e2c4f70b6a4984ab3d68ec17ba88eb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8798052ee906082ede514b4690f8608804ab7bd6bd899917a285f0a69668c60a9659354c693dc0d7a77127ea47e410385fb02faa20c783cc5075ac725797acf61731b7a998d759610bffa277f1b52d93a656498ddde651ddecb1a724274e0fca987f95b50b1329b33e10a02959ae08742bc415febdcff7d7d6ec7e463bece2e0329e3098b5f0a7fcc4604dec12dfb3390833ae01da1c3580055b7725cf189099d03111ba7989037a91df946d0d45024c183dc7b8e1f752d488c59231fe7a56651790a06b220157026b1afc6067ca11fdf1fb99cf2be71c948f5988fe9cba79667956599535fdc37a3411ac1f16d9cdbd7fb82d16e55fddb0e7f721d4a8137b7ecc514030c547d7d4144e24b7a22604c413974567380b31ec4d167e37d51dd5f30e4e8120222ee3ab88d82d1f8b8bb73d34803d8786134afb5fd6c536959037f7d39f27d020220e401022c222fb39380a5234ee2bd900f22d762ff068b6116b0a16c3b1c88a9020521bee3f22ad2c35e9c0910484e1c777e0aca54d1854cb74e9da1b05f5f05bb032ce10b1d8b5bba9c27d4540affe549c8b7d4819002072244a4ce1bac021a93d778707bdd70cae61988c22848c2b89eff38fd21f048163e163c29bc0fe4783d83066c13d97a1647d82bc0bce24a87fecf8be2a800b6dee085a5616eec1c678dbf95c097f204e276d16d485bf6e62b23b79f54beb17b0b2202f07c306872b8d8115c7cd18700beadd7f71cd7ab1d3a7f154d58a38448c4e175ddd10aba34e62cb7009b87466b1165c86a30deca06fb158457ff6efb0b10a073450e81bf3d720d354a9b752956470fd36a63d67f7b7a89ac7fab39cd9a80b18c3877d04d36d31b1795c9fe6030fb201530ca4c06e17fbef5ce7f68c07e5917e5d8f6615cfdcb8f01e73e3574831acc7879e81d36314a0fbb0a178c4500cbbf1274688cda428486939961b6adc52af7d490c50d5bf484d6e637f5ddb4cdc31739712ac08e4d8f3c5c25aca7927c1a4b4aadf48f6dc9c6f4fdec94fc82aced9fcae4794f403c2a84a029c20fd0a492a2d85d66ee6e78048087b7c700d75922ac199846faaf911c3f0d1de1b39c700ce61ea3d856cf5bbae3bfacc1da0668d0c7a0524d5a2c2c75f16f0d070cda32a296e15d2c22508aa61a3c30b69b423d2cfc052d63ca49f7d4f63ac1ce6c6a1286c1f80cc1318444172ed744a039066e6bbc971d1be0c84c7a1149ec44dbcd8f597815893331eee4b61e99d6752e6ef9e0d69df8f1f681089445e87313eb7d3ec4c4534440c21100c2ade3b058babf16f56881b45037d198aedd613b4835491e64ddfbe3c966e500cbd7b2c8149b265ce156637a5c47c7b919961a2564d2cc9602e5fb7148af75d4966d4bdaf7ab2f2299d26f4cd5566e1fd221692af9e99ed4a2a1ead554b12a8fb264601fa879e657ec49232b4a0c5ca6f28a9f7a4f5b333b388ec425e8269063c8bd85acd8caef655ad0388dbd0566cb2436fc59ad3cfbd450b8f0112a8e28207367f5627c564bae84a9e5017116ea4cce2755bf454fdbe7424f13c2a6b2b7703cd27c2c12db4a42def06047442d28c30c75d68728dd536c5619ba5488704c573aa24afe33430993f915fd7d3f56cf7c22cb92fa705e67460a8bf178cc77f0191435cf1463558bc318d8ef8079c5146d10a4c822689023cba135a8ff7b20d70ce2a37796bc410be2c5df17f0269c202d5ee1e5e5aca76834a5788955c0f034d570f75c4ccd8b91fa88729ef0eca3240ccec9a0267fa340d2853d8d2ce328f0af6b3ae74eb79b8f42b8b2abb1cd6be89072986b2ba37f3256f8636cc38bdf3a970e6d58f1d7b3c0271879d44c990004a3a423510c0d132839414fc71ec82e6d6731c291c048188211b9bda8f3481f199bcad9b0884d325ad9fcf052ebf25979949d1d0272dbd161f2fadf709e995b031869e5bfdf2dfcba54d3473ddba9c627c613533a455c579cafae1f23c46a5daec395aabdce80bf12fe7370efa02f71f2c3d4d679aaf5578e37d567f0fd613f42b6235ad2968a29c321048fd8dd4fcdb1a76735fcd62a0df950a404c66104d5028967e91de68b5521068f660b6b594cac916147c8ae45dca8d37cb733f0397bbea7916e35d55b430652e3494c9771b9b30ce21b323e98f2d2cc69fd05312e9516aee3652bc255ee6641e8cef940377f9b7572a0c11ae425e4d12cf0a23fb7161429259a17f539c95f9626d02e8f971da507f0a027ee483e7d3d8c3beeac12a859d0c84202a87025a7433dba18d4e0cd055f2d0e9011c200000000000000009ab7a101000000002ee0cbe81024386a6bd4feaa5170e8925c2e08345ee83cb88ae815aba131b7870336e0a521acb13077476a5b087df6a66a6cde994153da5205db29203f6ae9c3a53ee5cd9bd0f93dbc34a9130c409e958532c07ec6a140677218b8d52abc342600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000097cbe29a649959cb7d296e66bbba5375712b6532304212fef044c9957ac8488fa3ae95cd76f8609493fcb08aa615036e0fee8941b721662d085a082318e68e23180e0c338204d3525732789a1783f8b1fe34dcbe73eda96107c05a04713a0799bbf2ae4d9a39fb0ee994dbee89e55b623104665f1a7a14b41db0919e16dc1d5022eb31e69adf5b8f6bcda7bcfb182a2385bc9c7851a0694566ddd021ed0b88aea0311ae13ceb74f32f6c582884a87ad41e8ad9d79a281d2ffb3b13c4f03e26e1ef60a08792ace9c17a7fc5af0784e074835cd43ff225c76069e13e6cff9335b4e3372871145be37622c35554ccbc9717e68a646d05640b6bf44eba2d46d120e0fa3d3030ae1f3e3591106723ab5dfd382834128007d7df049a59006e907e8f9371bdc41020fb8845067f37e0e777e179564cb6004ff77c838e0bbad3730d2505b7ca18a1a0305b08b1f7f151ea32732ac92e69e39eef2a9d0923bc3c6d9856f271467699e910314e381bef95b03674427c214b0e83c172d9f37db5f56c119cff58eb3bdbd1b9e0329b981fd064d56ca045ee695f25d198c69f7ce5fdf604f5b165fd6e54a7ef941393588191065be6ec043b1032d0323bced5c65d4aed7a26d31013626f296cdd026b82c958350e9927ba5603be8ef6311b490c4aa755f0fc94b46b4235d051ce5651317f62e3a6831a5b3e820f241cc3168c69a3f5ba964afa9dbfd65e93bc269a53b6355f47fbbe48b003d80591cbab5fe1f8aaa5f895d85a0f9712ec3415a3f572aa1bf34381472dd8eb478afaac09b84829147f5b67e5602aa9c676dbe06775be5ecf97650c779b63ed1e18bf1a6506a87998820cd5b451b4f6c532a218c630c991d9643d4f91e64555263d97e702f68e0f136c54fde78211c5cdb5fa5007b1953a8dcf5f57d165b34eb651e24f67a987023b890356237b0464ab930e29faba350a49c8dea32eb895f534821bf42aa69b66dd12e076afcfbb9ab4734399f982fb827ad8e4fd1a7545b012f62ba6218330e6f6ff333b0ee232314c76473b14a601034464b0f87bc04335de00f6dc7cb89a975b2b552b02b4f9a01bc0a6157fc167592e4edd5099dceb695a604f35280c79e848a76b486a142ef4dc0f0c95ae78f6d3b4d4eb0c6f5270bbf738fc45f0bfb065574e66ca2a9955922b6944613699c146cd6adbd3737ab02d0a2c0dcae05af60edc10e417098bb6d72151d90dbf2a49b61babfce3c256a12bc4f7d9b7f4180ad886ce3c81d549ed7dbf33398494b56692611d116aa654b15d213ac049ac13e64f8736ee3890c1123b248a593119c3cabc0d2d0d91748a7475632367b20026411e98f8dae7e740c73e3bb51d6c3fbd1c4514baed6398a07c588d9b0024d005646e7313b99a1fe27717edf1e5bbec8f0d4af6c3d734156f2edb4a977ab7fb84ced94c72def3291df7a02ef2ea835488f2beb7e1e722fcd620b4408a499648d451c871a755383d712312e0bbfb07d6c062ee97ae7df9a824cab7e132e309f39d0b20aba5f357f264d06dff165218e22ba3f9d419ccba666649f5b2dff544ad80e660eb43f06b78c63820a05f270b7de7bbcc6c29d9255b2f5d84b8003b1f602b8f830f1c4f50c08b37e782712dd5e0b46335f0177339a1e3e3b1fbf8c21299319a42ffeb33de27bac087dedad6031a9bbcd7f9f08099867d65faaaad4d1f24651b15b526ac589a024b81d848ea6a988985d6791ba5f764acf1637eac749d84880c781095e5312012dfc988518b4910cc0489110d881fa79d877eb10467d97e199aecfa972de285e77a923d9eca02290ea21076fac4389513db183d915aa305c29e0d94825c0c7d6861b521d72b0cf1bd9f077085b39e49d5ac3e75bd96a46564d23332cb8c2d0703e9bea0364466234b5764b61efbfb668888a09e5a55db34b5294ba198f964d07fde9d4abe014b7c56df48842fcc5fb808fb6bf144e25e4b6da306a8c1bd2d7b9b464a2a3be0d48f413e381608ba21d4b8f23538d17cda975e124815bc1c1116b0f9e19d64e606ea25bacde46f68bc89cd403e381cd562d84583b0dea378138df43850dd7e4a11db48a445f132a9b742def8ba0a17606801a2282657c4cd8b8e2dfdb9ed07754d0cc64b2e94051c867b62e6b7871c621cafd4cbd283bd3681709dcb8771c76db42117c7bad43d18a7e63b3bbe4bfee514f5d3600aa4835a93a439b1431f16dd8b2c203ea9c0538bf278ac7ef7f037e7a41435a22ed6a9d05c9cbea39611a0a5c514df2593a3c337e8cea70e3b3b679722c33b3c0ffc42b04f4779afe1acfdab8f067036d7cf8699477623857d372035f28f32523e1964d6ed35d7c93e9874f61eebac3406c934b57777f75ca76a8fb6581ff21848e22b7bd32d0140da0114fd8a48e3af9b02285e8f74de79edd3b203a6d75c708", "0063525353006565", 0, 1157052904, 0, "d7917325a9908858e96bc5d374625c16da1038d81b7fb105cf35da5848e5aee0"], + ["8be71d300135e51740ceca5abd5dab38e1b68ef95971b9288ecf28230908d75d0606a17beb02000000086565636563635353ffffffff02cd4e010300000000085253ac6a00525363921a8f0300000000065265636a6563ee4cc3030252c5cf010000000000000000000000003bfeaee61790b4470a2a0c0a34a221c35c7827f72a4ca90a3af3ab545c030bd5bfe3e01415032e85b488667566bd1e15032c570b9c9ee4bf774e1cfd2fdb414030d60862212860b15ec4ef3ecbf83832cd6fe8a792edddf40bf9478d4743a73c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006dc4cb4499dd9532db6c67df2b3b8d4171ac9b842e94bb0280670c1b696c64d65d7ed7228c24eb876d0af70d12679d7fabc2ee67d0812670464661257a235050e7181f48d4abae590c959c1a0682b2bfb807b7c2a67a5e4249e145f5ceca54f677169e2a9e2a8b62fcf67d45d1906f81700f5da9ca6a96dae64fdfa0ceb85663032d0bac670e98c291fd18d4f01d2eeccac43599d3637dac1b0b8e71fa24a044df0212b79e577046c158bc099251ca887f51bf2aba2f7024824ae1acd4886b1a46270a054b23cb9758e6b64f296ce22425d96b6a02db684b9d1cda5d5718edd56428b2668873010ce60a65b28dd64cb97901742f4c4b94e6c39c68fd74bc99339525720208b395cd87f2e8239a602a8e0a357881ca7d64639d4d86916f3c696752fc3e8b032530577c8447cb5d0a431d0896b046fc1a5bbdd575eabdaf2143c3131dec477802286c40f4b288c767ba8d40e687088875ac03607202794aabddbe277539c875c50213ff658f2b85dede1a2c772b9d059c91925d6f19c740e54c9b8a0b947e3cd1f90203a7285035c3447a3e55af908b9abaa6378d6591fc374a2cb4bff2e67e61af2ad6b7b797b262af081765ddce06af275990824682a3d9c6d80cb7bfa13021d64faf99e219758b4700d040d4b1fd1fcad726dbba99b6b7419e014b83533f9353b9930a42b573472ecf1f1ab5ef0fca7804d44cdb8e0baf744b10f6d8f7a87349b137c9c30cf6b51ca9a7025d21bccc74477a0318ac221a308d7a6ae7204f232684df2b43ab3c22c517c616a45cd326b260678ff9597e4f93e4500060d687cc5f88bd259686ebe513d1433344b86315414e29bf781e66760ba41ce5ec45b53e2d16f563af8f21337660b5a59d85de7fb7f4fcafdf627dbf2b99b702ae1612cb53834a50e114a8410dab9891e9e9848ca29d143ec0039ef09f80290486f4bf2a457a00ee30ae82399b07829c5bbfd113051ecc237b07d75b55f47915ab5710c10c355bf02bf420999f1d7a3d28017a9e39e066f6dc653d868b6ba617d44bf31bf978c27cd9fcd5c777d8dff1ce5b8948dfa86e17b65cedb4d24e6a287938b3e0ff0332922df427aeae42d25711d5ca6a84367c10c97cc16a6057ea452d1bf411d868fdaa5cc1a55570611fa1a59f9b0f773e0ef4d66dea2ceed33955588335a7383991114a621e3e3c31a39544ad574467a7c0c1de7df20b893c8c28ea1938fdbe5f6ac3fb6dcd66f30bdafc9952ae9fc9d9b94c3c699f80ae9564349182d1657d3f730001a645b8b82bac262daa2afee3c54017afcc9d0886b38c055b93e9189401a358133de8a0e4d3039171c046dcf157c1edeaa0027e41a66b5a007091a579b81bf76eb3ed5e567f23549d0f6c12290236a6d872d9724a14724b3f681531271fb651a2ab12d33c3c4533e7085712868b67cdec682758f2dfdc4888b564dfe17ef76f748f24c44ae926477d877b531bf5194877f57fb265cf0298b0b564190d8f4c070d6c2a27d2c9426f0d717f8ac59ae4f7a69e7662b35054d190a786e703706ff3955bb857edbf8991b657bc6f1407af3cdc2652060a62f30877d75f838f529e2ad273aa17dafe9241794f9e7145a9633e9faad5f94689ebdbbc84c10dc70797b4983b821efb2023559fdef121cbfcc398dfdf8234dbc0c2c84a0aeeadf2925a2a99cbbeae6cd66c1e06d33abe7caacf52f3cd9b0dd2ddd21db1628b92e79dd8ac709f902f8493371ffdad344999a560ef4028a7e696dee3bee4b6a213d643c819a008ff7155bbd785f473d2fa9316019fbf435ba8c2c7c7d5aa43a3366345a8d14e183897f75feb347ea36946e1b203b84e64f981e1fd5ff4671b5ce18ef2412b58bf15f2b272ea59ae34cb4c3f74c634819fc539c6e91129e27bbaae11062139c4f20ec1c178f8a90a3a80e629b3603d600ec1c093cd5cf40d35a09fef0fd2307a0df39085ce10d7b9c5febe5bb830f2043fab1cea31b2f5e35e1977dbe9289001c3b24a64a9b0d23b6b6defb43c824dc389524d4b9a6408cd1b4bc66e23a578e9a93ce0113a41a3f7434f89f0ae74d42f657824c1321c8901dad104ffa890cb4c84476337863c289987ba6e8d7a1f2368c16b24d91ee924d97dc7f3ef46e2f38b9f9016f6e14ed8e88c58309fbc053320e366ad0ab2da19605319d4299ec53d761fefd609f0181be1f639a48580d1fa4e1f95aacd5a9d4a9a6864446fa8a6b28447b93c0c0830ee20b7e3da3cc853cd6fe49584321755be8c829d847170ab2e3fbbe537453465a3f9b936e14cd21ff1c8561e01000000000000000000000000f7cc88490d987817c46e88744fd75653c43b3cbd5f37e5079e9a55a1ad00d7dfa1dd8d57f97b0f54cea3e128aeef6cd3ccd711430f22387e6b51fe5a5886595e5e13e8b99bba7570076c725bea290ed0e7be011e32f23774a58544dc6a203f4600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a61389626b8b19e6d86bb099b6b802a856ba603efb29ae2e4c4b66e438c6151652e557eaa7c6cfda78fe7fd46e057483359c42c49b7cb67d95b233af26475e0d81ad87b99fe9114b4239818538af1d2a16afbbd3f2636ad28f8a9b4d91d8cd11d613ea5c54bf5f163fa0cfc7d6b7eea2527dbaa1c08ae4ae13d69ad5e80b6ebd0306b32940a32dd03043904b9ac0b3dc5d69a58e8ad06b73011f44ca5a4bc7c7040319cdd9a1a9b887c58313d3301f02b237057bf69f44531236823ea44df058c2390b01c01016464f8d9ae36dca21283bfd1a6a08bcae6ce25c9e730fb5a216a96cc827c4c903b3643ef0b63cce5b103e45cbeffc026249fdca06c735122bb38ad9ee03159c28b6976886e8778a2f18beb73b239ebf8bc824c1cb24e736336dabe4d162032fd9c3a772d8e35fc2f0db01ad6ef3d134e0f5f769e8bd9dee373e39b87423b40227fe865605512174220e258f82b7f3fd49853db26febf7af0ba82af7c013e3eb032944d4d1dfa91ac4fb6fb53ceab0ffc8d60d86819606bda45ed4e31542a305ad0319ce13cfffb48f88cf852e00fb0aea704ff6f3ad571072ae29fe7f45938bb91fe1f2d888e0d6363de53d840f5ac0e9d373bdfcfba91da9263f2ceaf66d556d071d4503c5131974cab98c30d8cbdd67466a59b8d4fb7fc2dab0ce5766ae1afaedd0dfaeb8c99ad97c3dda258959d707d160d77a7f201024c98112d8abf872c0c8ee59d926687527e22abf63ffbc20c9b0dfc3952e3edca83a4ea20644866217a716484eaa3e754c9df6d8e7331e7ee69d247ca4a877cd4dae857f35e52771a915d473b6e6a03c42e25e046939116012c92cd678e9081f112775dc231d0a9e9e597cb8727b337ea6d32af30448221d68c2ea2b8ea1fd2592ef28407203d32f4c20861a86efeba20b83a0550dfa667790fa8cbb2397930dfda79511d6e998c636002dc6940b22686fd4f209348ffbc57f1ee59d5d1a0f808ea4832749b3f350fd0a09f1e423e14a6f7a1d43e4c9273fdfb794a14f56bdcb963aa13777e31d8fabaa1f8e2d7c858b361cbe08a98320707e8674f68b5edec26b96339619c8d855aba9f7ee2d99440b42afdea5bdcbdab1dc75ffc8879e0195c7df07584524574be216d89d08faf4e75ff5e0225fd03d28ec2867742ab9bc4615a55aca0c6507958145850efb461cb7f90571c98b4f9ad954263cba88c5b71ca4f9390431ce617a2e5821742b8e53af0da27b7ffb5c47883ca6df44925a1f0a499486d06f13bd078074955fbb12e4076008ea9cdf78f8fce7245bc9f1e03f754f0fb9637e4a7ce3f4f7a07c5a8f2c985dba069d6dab5ecbf9d273f8bb52ae526a2208aa034aa35f10c779feaa46b4e507e25f1ca3c70b514d64d39edd5e8f215522ca1125f77cddefd2236715876c6a6959ab472afb8417262726384c03035953f296e500909502c08fc4c4d8f3dab94d55479d5888baa35a8c695fade4638d1f7e8b706d5bf5a83716c2afcfeaf2e3465741cfaf14c8113807cd7657c5287a30bdc7690cd229b6e9d980072e5f7272a8a73743b815b41b9de3d943b78deae8db5f0e0f53f16e643e7b2d09c6fcd4724ac2c41d85c2c9e7f3af8db9230e2dd6fe69766418a25cb49772d3348972b9fd19dd870c3b7714e991cdbf37ebd66d85d9407f8e448de158e63fab66da5a448ceb982e1d5475d8ece42fbbf819f2ceae5be343af398d58d3640da731eba0ebc05b4ee8b62c9cee41d800f3098123b5dac8438519cf4922e09f138511f3da560bc1751c62ebd21daea1b4b79ad651e4bdb96abbf84338b90784f6efbbd7410a22ebe5c2ee4367b35fef814fc7216c9506f6bd7d440c7580755a95af8931910d347005d5c407c03d9cad3b639a44c88c23ab996112f47ca605220e522f049aa6640065787aac6c537b6f6cf9e1e4d2d3ea7d926d08d5ffbb3ee75806fad33c949567535dbc0e741424e20aad8bf917201e3d57a638c596768f68693ce7c1978015673240efcad7fe49b9402a8ce06b71cff975fabfe4ae9ac84711fd4afb6d3f72cbf6425defb64ae47f5969f18857fa18798a18c8dec1b4b1005c771329df5369cc1807ef1f30d89fd8fb68f9f50f8c58b3a6637f2e1698f484c68d1e9a796417d69410e3ba7f88cdbae1900c37b4969e4ed62bf15a41408e447f67eb4f1e097843ea54ce73b36e727036179018883a8842bd6c8cf1e92fc86ae1dcfcd5fb8211c08b21d796486865f3ddb22d1c49a183587a3c7db2084b3d1319f8f2a6a6c83e1f0284c98f5a206897422640657a02acc4bbdc2f54b432f3aaddcdda6cb370954445df3c06ac2cb57c4c9c0fc9e4524e0084000acca4799bfbf20b910cde84830403cbe7fee84765aa015772f9fdd25fff03fed4bb4a1e58634c65becee01eb95c0c28625c54572602f81706", "515352520063", 0, 651253559, 1537743641, "8cf6439f29fd7546efe07bcb95b1d5450b93280efe8cba6d4ed90eb585d1f2d5"], + ["030000807082c403035b0ee7cbfa73b4a7221dc145d8d691b1b70c2b101f51706ec33259884b14659f030000000563ac520065a6102611af9401352b6a17c34e9259d53131b40c80d57824a566168edf487c0f8044a1e40200000000ffffffff34a106e8f24ad22ea45fbcea2109f377cc0022e983a9402d35c6466ea65cc14d01000000055353005153547871bb01f347f303000000000552635352539b699a3af22ca2c300", "6352", 2, -467320790, 0, "013d164ab23c56453f9b1dafa2e17dab82570b483f12cea8b6a338ac3f5f47c4"], + ["916d733701a0b971fabc2464c13fc261cb3b41c592b10f8e8292403133d1322ab33f05a28b0300000004ac53515230177917037f9e8b040000000003536aacb51a0e000000000005516a00005145abc8000000000006005352ac536a000000000100000000000000009607af0100000000fd84613ba7f705a5037368da6b04cf7f2fbb27aa0815ddbcdce883d99d90e24451b9d1767244abe36a6192154d1b7406c3de9617589d08abca78b9bb424aee06544f5e3fbd9c6b232f03a0950ae32af07bca66843973df261f40306cba13c8da000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000157bf345583551f1b7002d4edb8b3075d756417b6e6ec9b1aab384fd92848a0a53ce2174dc0814a6894e679ba5b395c9d598aec0121141e57d22b28b6aa622abd346a22492766e0de13549938d7baf5522f5c7f1179efa103a41dfe9aa0ce42acca31a725973a363a06317042efdf2eea4c72d2127f7b2912e1d8f4e9738f230207e175e2fc18f1aae1b930c40867ffc3f8971ec87248968b554abe254c0c07fd032a07a7cb2750573df30388b230e9332d961d845fc5aa07b63671f5e6e42c705b0b03ad0819a901b0abdc40ec4583a23fdb631af17e2e41b8e91585eea3197711f738e7cc0ec6bc8d8ee9ce9c7c319e966899c949e58917158efc2505c755a75e4002293a535d0e490dfb1be3eab7ddde00b6e023940133bba22e114057606a1a92240319f25215ea58c972b17adca8e011b5d39d38aeca753bfd3ac9dbb80b8146272e0322a5e70b6d3c91a1161c1e251a7a3ef294a5ba97e44307247ebc72145684c113020a6085d7533b3422bae9c2c07357858b2d67d5415dcedbd4cd628cf60536d069033038e4fde7148e35f01f808d62867041fcc4bacb138e01cea69e58d82a1a1d193e753ac5ac236400ec23baefbd00ab5371c8abcd85902af93cb408e7037f734e7aa9ec86e7d90fd5c3aca11c8dae714d6cc7c6a9f6651800ebce0e949aa139b1bd140d68a4872994bece3e78584f3d4fab4a3b82f68e27c6fca3300da6dbe667c9d7c958b3c4bab6c875a422ea2a20f3a861f15b67624bbf1cfb63faa359a88c5ab123b7477ec99a5c282f78e250f23e1ee6f7b37d9d7b9cb78127b391965731ff6f957b7c9f4b4f2a561ce52a3f54c39663a2e4d633111bef22c5d5f38e605cf556a0534f96e2e677c1183691a851b0c9bee3b1a3acdec0802211da8989207969d1d44dceccf06afcb074afd35ffdb0ff32701619dd0c81097fa90c0884dfc9e6477cfe0110ff2afa2062ce4f65a2912febb454e2bf890550f58275cd78d6272db7fa75b7eaad49766137b2a0f95975567443c5b822fa71ebed76a9037049954438ad5ddda327af284ca619132f9401169c8b6186c171a37fabf395d48dfa819caa47e7262695fbad16d7b4d0040cca1b7c98c3ae4062f5bce132cc1915ad45ab7bdf41b0587692cebd8edaf43f2e0b2de8277df79f7bdebf10f87172831e723fff04847099060d7653a11045b7611c17fae613114807405263be77bc6466864cb21f7afedd1366266a7ca47bd7b6f00eaa956a08fa10772242ed061abc0551fad26c367fc0a0fa38a2a2a9efd4aa9b36dec6061879ffee3afcee3c0cf4da212e9919a7d9d610a5b72290b2d68951eaccb7db572d069d4bcd927080bc0587d685ac5342cd29791f4dd142a4846632c42983e91656a9cf6f78362642e614adc1ad479728321884a195f552336da52c870bc94117e0bc135cddd31d36cb34511c6dc7dbc76da09f285b793c91f0aca017a6b50d9f887c5a97db1ca246e0d3a8357f78f6bb6862090afbf16fdd3757a41c7915e00f76d7373975d10eaa0e32fddd433f0abfed3ad84d5cb444c6bc8c6097249e2432d9a8d3c733b5f8cedcf8c678b9d9b7611f302ea7f55adaf02426c56e78acc848171da483040df4223cbc77fa0f288f19c95178e667358c4ce46839d03a29cd92c4e7f4c8a000680abc32d73941d969cc6618ee26da5185a9d688d77517fe4d199bf93ce087f931bac65ae44d998efbd0b5259d392682ba39e71d86f036872098fff5a3704a4e173f9d3bb9c12acca6ff2ffe21f1eaf0a897502e03c5ad010e9cb0da9722ee15ef87e1c17b0dd833bbfe9fe60fe18c3790dbada2d11a8bc0c38ef1e9e18d1451f7373d99ec52a5379dbd9becd5dbd60c2d303f40a83d83dda01717157eb6707e6ec1fde8ef6624a388851ee8b708512b1ae4b6b3383547f4d1c9d094e37a263e69faa216e8dbd740fbe5197ddba1612bdd1b34dacdb6e660e57b8454e9f9c1210829ded00c2c9aa43e18be17733937a86e4f184014d23fb8640fb8623585f5d74b544da76c7dfc3bec9201eba10d8dcb68badc82256aa87be050bb97238522ef5592742937d99b4ca8f6392f5b9a02788c51c2366bf555858cf24e34334c4ce6f4c627c517a65f322e4b7f113731f7f723701f85ed5386d2d3499cb4b50afccd5aea2b4d9fd2b82d69cacdc85c4628bdafd0d93685df0bfac66ae9a001bc1bef1fecada2467f9c53783d650cfa74ee7a7b7b0c4b4f5f5492277590f1de97600bdeaf2f0896882378b5bd210da8a46b5f216a114a5b6ba3aa3575cd769fe5cd02ff39f3b5f17087680e2c759e84aa18f77f2bb27db59e172a3f8263da24fec234f8ba0d32405c43d61fbe3f8d8506c72b619fa8efe8e85f2aa3c9d0cd8290d2e7dd20574b725a6348c4758770257a611e2856a9cdbe0e350b", "ac5352ac6a6551516a", 0, -83645321, 0, "2a2fc2b80f49fb0eade3e5c65150a114d967c3b11f53d347120487a72d57b427"], + ["030000807082c403027afb1649e7da510190abc55b553819c561eb68178c4319aa66615f1922560daf0300000009ac52635265ac63006affffffff528c78ebaba1bcf26207a4098b200c62fc9207b20b8da1a5fa18075fb0bc14fa0200000003536565f4c3a7f1027258210500000000076551ac65536552543e8705000000000653ac006a510000000000deb4a361010000000000000000a6183d0000000000e7ab3e106e3598a892591db109a0f41c22b9dab5aa76df3ea75c59ab8feb36009b2e834d861295321b99e77af91a3d44edb62326685ba5ceea7f03176031fddbe9a7d978674472c29f14c9c4aa7a417712ad3788d78402a5aed657854081b707000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009451601f70c9e3601525014aa3ea7c8e6d18b165c1c383c16796baf0dd36c39ed100a878f3f1ee9ea5d780c38995c8b392eeb14916e8f2fdbd5868132bb793fd132239999932dd16af9729b14399f58619f2b52643095707ff73d8a2d1804a6d56a08a3aa7203ec876dcfcd284584873dd0e9e04a4cb936d482e5d3f4f821d2a03147a473bb064a0ed93121c083aec70bf2b36e3e9975996085c5b2a7332345e5102062c1d87769890a918c2b782f78b5ee6849f9ea3806cae8a8f8b677c8787f3510b044e5bda129fab05abd1da5ea92515034171af20a5cb9f41e14affbf87f7cee52c036fcc9e234e40dc86b46afd88c46a5057a4f81f74d101fa880b24ae806265020f9d07ae18feb60dea33486b873507fc90f30f11d53ef381915d3eaf0f71afd2031b25a00daa7132d33ab74ccae753c8b2999fa1d55539ed471be405f32b041909021238063a391d5d592f292612b1398bbe54acf4fd1e5ea9bbecd9327a06cbf4700303ee627fe709f5dfb387e7271631d03d7ade614cc4b78a21a8a74e4cffcaf8750309f019dd2aa4d7819faf6db8e1eb42b698fa2c8b9b902274d82dcbbc5a21e5f5952debcdbe24ac027ac8aad93eb52b99ceabf7d7178118eb018be0bb3c638bec89ebc7ee739d29c599da0257ec196af4f4f70b3f800d4b1f80f384694d6a5eb6fcc7fbd4ac6d7a14360ce63c1890667eca613fce3e16c12935ea61fb66418bb994b1c9fbec50278ef19281efaa02802dd07e335e24c9ea6dbfca7bcb7dcc6b8ec524274de779691df33a51607db2175616e762fae79ee2672b64e3e084f6f3df3b7928f410125366c6e2a4527f9f7df5e38fab1f76897a3f23d961e110b0ca3ae91266af9f34028c01ce1a1a18623df195c7f3aa95ca08812ec906f459b3ca3a2569d56830ee380d52f4caad1b05e18f79b20727ddce2bd6057a961f57b1414ac6085ec02f6b66809628afa8b2b82075299d3c12db2494cc3dd6aa84cd63ba6e654da0e27ab7a35f32d59a2da0247cf011a19fe44101c79829567bbb31faf0a702a7e487f177d3186ffc265fbe9d067ceb44b311928d4afc66dc83a08d20e9e2f29f87b976fc42d092a6f6552103ffb11d6abd64f54f3781bf6b8b5e6916d9f98fd4e816c82ee9f92766350e05ce631c3727d1f68853a704443d724b2a279650ab7a400911ab8ad418652dbaf0e3fd857283b6531e99b3242fe7459982067eee484dde4891882e4ee0a680833ce8af9c287dae3026c2567fe549dd7832324aab3a9ce61ab089bc0f2c91f6cd894c708714916470d593c54de6d5476293c681cbeb21ee7b4e9241bf2161fb342f15d3f106ae0cb2917eb13275c6d5e583f8eb893130bdff3d756c733c0157bb558800adda7f2bb504871ec099e9c365788a4e6026d7b06ebc41ae989752aea68d3dffaac8eb7a32096e4ae38e528f6e3b152b26f04fa3ffc5b58ef17f78bc687e863fa15361a2e4e71420a1aa2509629fc81e178ef13417d774ce99d79a5d8bced1ef02005fc66c3f58883ddedda04743e78c28b6c3f4d49f51c3bbc8fa2137db5ace5b60903e1913b8e0ae2344983ec051dd1e2c2f57862844956246472044f09c921917ef6c3ce7442298b8dd36c736687dee6592c80148441f724bf83ba40989fa00bfd0633647a592cb8ad5a754d01a32337933ad2c7ca8fb4e468f8991c633ef1fab32e4abea798261d5cc6caff382b172ff041e7b382ec02490e438dfc279b3c8df6fd4792f383eb4aea32c5c8155e410c39885a18eb1e7da6a8de83aac21b7868c85efdf867def47980eaf17a2d2cdb6a6cd9d38ef89f87a9f4f347de41399b1a949fbb91970be9c211a294ce838ec8793ad477046ba8b809a69a8b77967692a44a9dd47093a2efeddcf5709129d3ce587a1d9ab9d5edb0d369154285b7eebf2ec04835e0ca3dd9fceba05781bb0c79a990bc83903e921f68d5ce250df955b54af70a58c9fb0365fa5dbf70bf8cf2fa0c9ef70363c7d4c8852af63fdb03ee1598e259fc40a814dec22fe24ceb46577b2f820f7df9552e63704f04ca512f374494ab4e983dbd7fc4e70ff2c679c2722fffbcb23f7333c632a1469d2ef379e9b38ffebbf8cf7d0490dc2ce02a8fd681735755240adf81c83c3c914b3b6c1fecc59040d6e0dc30e72767170cd9cefd3223afb1db21973d578bb4440984f53b283c9ce4cb5f9d58c0153f6410b736a321211da6e3bb112b4ffac1a5c6370c78d0617958497045a6f8cbbf4151ed5004caa93701b6d611d26e4eda82c39e9412d4ee8c2168fd18bac43bde409d084b54d822414cfffb4adf11e068643224688c3a5c93a5c42cf5ebc6ff8e967cc71c7d4f1b1f28390ee8312c24aad3b71109fcba1ff71a98a90bd4a3fdad938df7b2097ee867a31e46729c353868899275a3fb060258e00", "", 1, -2141711051, 0, "44e5013d0bf41a1f872777c78dbfcd51881b656996b148b4f6d704d7c08e3f02"], + ["c019f33004ee8b83bfb96870c2112f6a9633167abac3c3c930f6153a9e7d2605a43cc354050200000009536a536363535265536156f27ff2180ad35d9f3ba768224e60b6238e059791bceac6dae6e26e9ff946ea3cbbab030000000152ffffffffe5d6f63639e95b77d815c832e142a9fa40241df42c5397b43a7905483fc951d303000000025152ffffffff942ec8070e30e12cb114755cac080bba441e34e4c09e1f1d1857381cc35071e703000000009f30af6e041edc6f05000000000765536a65005151f8cd380000000000008fcc4d05000000000800006352635363ac6c547e040000000004acac52529365920703982dbd00000000000000000000000000c56d4ccbd64d4f43e16a6399fa81bd54d4f39b3b3e3c4711094b61ed7d393684b5dd80df4333faa87b65d4c9a545c90c44e2b9e2ae7943b409cafd1aa09c2481d4a1324083444d6929847d897044e8164ea4f243da07d66a866d8e7df15d68420000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c2c3f333d4fa407a78906cc9fc8860a3c7734200c78521a9f05c7ea1ee083a9f68e759f2a7c865fec0c87204ea1505a6d4b266c1b626d73ead1cf77823fa1dd030a25087807f74649e4535ea896e97106e608929562af0178c87bb9cfa1168f305f1bfd483de808a9ad99c406dd4d4f7480cd061a85a511017a587a852b7270320230720744d86f6a04b10db011da288f6697ed46f81704fcde6ce0a3d0f5c3202216e0efb5880c740afe4f7a10d4f661793a3639f0921f35a5461c4a0b93bf23a0b00f0f218f0ee9094f348f55ee9d674be88888076671386d516bdc47edda234d9726700649600d7a4120da86e09cee022aaa9803e23423426df00b7913b87eaee020679ca2c8dade15d18089461c51c5dcb1042b4f71087d93ad037dfb6f1ea7e030216864a1328fd2c159edfe27cdaf03ed98d5a97111232e7dcfcd2dabcdf58f8fa02146e86abd3d18362a7d7e37f29757769a55f68a34fff7f15d7aa2da30a7c57c00229ff0e24b87261c4e495d95661450933c4494621deeb9595a6645f50152adc08022cda1645c327f9407567709b9b047ba9dbc0289552692ef57c500825ab0ba09d1b1200ad27d14ee8801387267ccc8501a2b657c301e2c982a6f0e53a20a94c66095cd5496a9b35a6677ebb054bf357fc91c6001e19d5c88652b9cea7d6cce557ca90d55e4d05871f3ad4061e1b8378791397fe428c67de2f4b1075bf55c08e3b3fd674f185779481b48e60bbe6947e2e017be0b126c98f232580d7640f1b0e1980be533dc17442217a602f86207c3a036f99cd7433d18fbca3df0036d9faa02ed80246d1e7e51ba1bf5909b160d08badd2a0a5eec96505c451923f16b4d74e898b757cec36f7b20cff068898f529cf3db828021a5094c0da387d953a74e2ebed784c902c1d3a91fa1c88adba72abccd0f8aea5c22be0ecef2dc30057bed34024765c229a1ecef49601d1101f859d92516ca1e4e634bf41a57cf8043a449318aaf59611458123e06abad14ce077a2bafd3bd54128f3fc13cfd6001c04be0ea371f0825e4805de4cef93a3328e10132f9901c388864ee52c59a53920cc51b105ba953eac77e6f4b73b3e106df5a53e36197e87777a848bdd2b49b70519d4cd79ffbba1c7c1e4c37ab79fb4edf108e2b7f7ec05243aabad3597239bc3c02246b43ed718ca3e44105a87903a0a7ed9885161e854f0da68af34d0eadcf5de126d16fd25dbe1c8b6fb4b6b233b315523753fd0d71f376c61a004c764217e2fcac1cc74c46e17840e854c507fec728e8747898434d3f9fc69084187a5d88606fb495afa817297498f5849f0185669a24095b7cfc11e2134c4928d4c0023f5111773a5f33261fbd43dfe865f784727822906ebaa8dc962b8d9d82cfcffb3812c9dcdfdbe47087ee31483fd005226f799bd0c4a57374dd17db5524417b27df671bb6c01cbe41516e4a3caa21fb892f64b334cf9c0467f60c7dd78527b7137ea8dfcd9024275683bb3c38fef90856eb8414322ff135a04bcf5491417de490c7a44872a696753819eac045b0e68d9dd780049afed4eb95d4e5705ad8f2918889c1f88a179e05c0921b2dfd52655fa34758d0d1b939616ca7233660409f5958fdbc18e2b97c424d88837f70adb40a2ad018294301d5f26b650bdc6a0dc9904c7d852acf1fbf8a4b5e23ffc155cec9255c1261e2fe66166828dea8f955c4d4970834ef63569bfb7c451dfede501a66af2dde4ab88c8fc9b7503df1f9aba3a492c7742ef37f74d705b7af2ac9478706f9a448084bcdab5217f58db3a16e191084b1e7741ea775820bb6d110c10380dbf738bd875c6290fd42e776c5237819f333eff90cfeb799da7705515bb792ce3a862a0408e1ba86f95bc120c39bfd41d6a63d579680980ebeb046a831ecfa25c9f4cccd6f76fc4c1450c04e655fa9524fff29a60b50b5a483c97b11dace933df6ad040b77a43514abf35e1c5de0d60bf61cbed115dca2e0213d58927f6bd2fc0c0c1752d158744b54a351f9da327fdef239c23a21f471218e6c432014d4f234221869cb2efe834a9fde2fb87d3c015863bb08e69d1858d3f8fcff69026b492c5076837d81decbe08e37044e988f9bc083268c59b261c42ec338ec54949a46ef8df97c975122194c4af620c147b84e525da8f7672c103f719b868d303b468dd9712f9e0228575beea97417eacc47771537d32bd7fff91ed0c1f78f698b3915e540c381dd113e4f1ee00ab88b87fc4216da339706dd63fd12b13c8771018300959e3cc3be7c564873c16740000000000000000bfe4c205000000006c5969ba6b0b90eee463cf247c29dc25d7ea476d2313a9518d6461bcaad4f24620713dbb1965fab753fdea279237733a947015cb2f88fdd17f835f4f0d2170f5539620f4372511413f88311753c86d3925ce96aef166e5407b8a04d80c8cd58900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000462f754f74832955e16b57bba7cfd84d89c1e9e988aa0fc9714713df58bb82b03cb6f3fb90180c8d4bb7388603de19a928bd6784c5a1c0728915fde821d73244974796bcecca5c860e51d982e082c5fb9e0d0e3b23b497040649bbac0fdd75e87ae07ef32c9f196b4182ef02ad8cad2f4cd9385dd0a6200478b35c89ba7012ff03184d9ced2a9643805d3e4dd8d2437975c67758490ed64b0e5c09c6691606db34020ac42a10abc2bc32d3e9b858f1d07c421c4a67d191f8813e3b7309a1343771f10a03efdf1dd37f9a2f59b36a73e5a41b00a1896a643983d14c587c4169e9cd4effdcc5831e41cf145c9a1d33a4b67e8e8b3d0632234482d5bbbffdd78678e4e771030a6ce42d5f9c1771b5213c8ee0cce59905f5b9f7ee290d8733ceabbb7d2aaeb00217aea5ce9b76fb324a30448357dd1fb2dc79bff8b1a0cda9af1d6ea96d1656400302af6552652a582b2bf1082c3521e2cbbb401ebd82359896e80e753387a605ae021f7510a81137f492b69f6da3371c5a67ce468f4f47c5b16861f4e04c4f6ec950032a95578ab7eb582187dbe142f7b522a6f5a4dba52a3591bd2d998a8e065ff8bf5cd530f6063f58c968bf5e58153d9659c896b93b054f0d59c6d16d90e46be7579a5751433fcd8da9411edf6d819f780627c8d504a273abf15651671a9ec1358070222fb11a86fdd709f51db59c29c991bafae8beca17e779d9240033e8c7e90652ad4be255d697d4236d1f9a3b32a640d7b024e2303d62e4583eb4beb996d9b8a39c01f1448821f8bbdda8b27dd8ea3cb6bfc0968defc1f7857d888e09057c92ac7fa93361e7d2cf8c0cae4f560a0e30be87574d12d912ae0385f1dcc6f0376f6dd7b0c88c19429b4dccee467ad79119bc1ed66980748fa50c3b7d92ca022e99c9a36e95c8ddd5d837c08a2f668a1822469ccdadc9c477f790b7b5cd950effa54db3156289494805f3dad6572cfd0dcc258ab02381be72c9e3802f331956c762cafd364314e8894f5e211a698dd25ffb44ee5f2c84a9f41d735ba05a6fad7c2dd5d3fc1f131cf1f7e29a3f7bf435e0200b3152545d7b8e0708c166c171391fe6b3c411a1cd0981c2883535abcb09d811bc2f842d6dccc9861051bad30c061922593aab1571c39e160faa2203089f97d43614ea572008a3bb2c560198f3c4a0bdc970922ec367b6ebbb84c8ec880090c2bb8cfe0edf898a240e40a6a6b3782c17ef0d01b5d563e01474e6168d216e9d66c2cd52a4d5b952045332010079f43b0c2327617a297408ac96247f5cdb0068e85e8b62963dd76f9fe92c747c2b565acd0a1cfeb31554dec2e30e96df947ad4bfa7e6baede6bac0d568d7df1c2c506d8d6f9737f81a2a2d6725f7375df9c25f698f9a71b18553546aeb0898d74b9077ed5d295e5217a95171c49ff787e423e6708694e01a650aa3f2b895542fcd09056128b309335b50925af6cca46be40a48f92947a26bf97b2264d7e06cb8e5b48d97985520690717d736c1c9257c1ca59a247612ff035d209de74814128e16f9078e50f1bf4c5230e66a61da9a832b64b6d3213367a8bee2b443c4bf30cd939fb3938c86886b09faa1a0fe67d7743c269b4a7715b82cdd39274813395cd0cb7f691b93cb404deb3d2c3cbd306ebc8cfbeb4284f3003f925efbde8601012bbf3f08c11378c2c45b46eb019b5a854f14849959ec39f087e619b84f4d2696f72e605090eba47be4ee5c9e21655048aeb099e36933bdf95f4c71d4d726bd5ea187e782bf3fdd261a30fcb11e77dcccd2ee29900b9e8018b29ef79763b1509f3011d04e87969860d550f65230aec326d776e500d764795284b5e371d627884c7ad26d9ad997b8f9dfa7c23dcdb3ce386ad290e4ded7ee5f415a9df016fdb598432722f84aee9054e31784d143e363ef9d2b55c1cd6daff78104e08ecf6d908395013266bf6cfecb233cfec05b0f109e6005d1ed7d7154f001533d3c4f3255b0547a6651787ab4e0b8b8009aec3869bafd86c231dfc7d14d5cc4b8e5aa1a4377d2fc74dc670b30735bc9fffa98d595ea49754c36b59a6e2756d8f858f3f85e8087c69f91820716db4a68ff8f2f2b7e23b1dac4ff68eff76199e938316b5db0b919f03d56b1177560fafdd3e8a553177b8defb82eeb0af3744d8299d0dc841aa05ca2253a1bc95b2d715eae88fa67cea04c586235b525a589450cd8b4aa26e4e45020dec46d47473fe2545a9dab0e3407d96414ab6a978acf399517561b7772b0ee5713d7b5f9a06b4e93efd8b331243099c079488519949d2f16050000000000000000000000003669cc45d60e1488ae94b2bc59eacfa1c8c7536e173420ea06b16e0222c8d1a888258a19445cdef3a9ad073e79525f653a98a049334f500c0edc09d71fdff64e069bc9cdbf5b4438d99975bb001a72f9a0463f8d714ab671f163af3554a73ac800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e16ba8fc883ce38d91d995dc0121d5aee910ebd577a6246ce6986820591b235a6c47fac0d652ff5c45ec03eb46078778a1cec82b712b4d305b1fe4a7b3cddac092d0debbb40d6ba901cc11fb55e16520b864af64c97d70dca988593d496fd0c47019e51d0b4438f448663ecb9bafe29ed0e9ca9d7d6135e9dba7402761b34cb10223ddc6a2f3371795db914d9340096f8f415b69887435959595afd038c44c37810302ba3556b2632a90e0bda8a3924c8a856acbbf64bae2a130a2e7449c59b14e6c0a05e57cff272427a097edb50d34ceab55f30af008db96435e62a4cc45b877200038212e694480a53e632cf01c78bbb750d19825bc2df308313f0d6a74659632e7030c521956effaccddfeda6c01e57e76381cfb9d91513d40e97d66353d884cb14a02092a7962d0564e7c561e4b706c04e7b0f606b0ee9f04ef4fd3ef67ceea33f2f0032497a7d90c18f36f8ccd623b913b0cef0077165af7a9608ee34b07a9532ece55022f880017d72111016b03e1cecb2c942064c7686b944cb669d9ae0da40c76a8e4022ba78b8b948a1cc51ebbf9021941c5ab27d6c113dc72b435ebeb3216cff4ffd73492ac7de7563c124282668cb264b1954574191adbab3b6f19a266dd64525d3a91da9994c699c1c05f16bf6ca68ebcdbd8cd53972f50b265299d5e693a9fea46dfd83c3772ffb63309629550d06958ee1bf61ffd29b738ebba58a0f561f90e669854615196484e870e6f67ce625bb26f29233b7ea23054963d31142ff11aab3bf7a5950aeb002b8c9b878b86f7b5f14916c5d5d43238683dcc8c44bf29570b3ba33e72d8bda66072796424a676f9125bd1c17f7585a627448aecdf4f78124f1b979b18a6ac29dc1dd876b10541005dc69fa46cf7f90c88e27b6a93111f0ef2234855c5602b28f5ac3c95d9863c8103648fdb78f718ac555b26bf5fd725ec8bf6e73881a0969c1e2a3854efdb99bf8095c85b5974f602b7048b4864658672e54577d4702691be3ed049e459c288f33abe27a9616e91ef2c79aa21338e240cc8653a76feeadf6ea1ebd6fbbd696b40105c7771b605aa78cfa66f1fe265133fdeb9e130544db586ba2e9ef21e43cecb962eb1eb6a24da7288cf1486407350dd7e43abb0d97eef5c5f7aae9e493e482be759dbe006ad8a2c8a75cd744a8da97b54818a0c926b658dc9fd20f0df2a91bc1c533b4acca3aaa0f280709d6530c67ae56630b9ee152e3b22886cf6ddb16ee72e7b1a0a3ff52bcdd9fcf3c85eeb93f02a59f80089c6c503679712ee673382ef37e62aafac57fa149f8748190fce722e3226477f4c5d8cbb6a23d23116b1cc5f58fe4eb78f3a5b3d2dd59516f3b112305ceedbfa80bbfab69dfb386d0a2ef9e03d9386effb08c75f2af3a33336bed4cb262b24ea56eb3970e940847d62ff1422d212ace06c41feaf9d1e9db73bea1af5c622af8028232b30dd15e75394e8ce03fef321c1f1e99b276ee42f1b2731d21aeeae1877afa1c7b407943ba2f6df243d14b211ae383e87b661ea4f630198609c5aff51953ec423547e380c8db22653659c63761402074018754001dc04623abf2a72edaa87589e1fcb41e8007e4caab89939aaa405845dc4117aa37b6d4a3f895dfc8bf91ccc4fdddaf1bcb3b0f6b3a94484ce02b755b933f0cf78df5d968395c577bbdc52becf86a21a8d45475e84f8caef9769427b5e730014af072ab430953d4097bcf0f29c7d91cf76aa37490c2033618c32ab7f4e33c238f2837f9d1672e9f68a14a86cfd8ac4f0bd32e4052b43db416f15f84234d6118f342e64d61fee54fdc479c0c06f86097b03ea62ae1eb65091c963d1b8350ec033f6682265469684b07ef1fa1af084e90848f3bc305169480d036c8a7cf2a8e5ce88f850a692e4cff300f845565ab3e3f8d86022c954b08cd2f5a7da9c898009e4a788f90eb86376a2fffab6ff714d6fe3556883555b8657b51d9a5e89864ed6790e53d86caf873effe60a573f517b1555472c031acaec6fa3e3d733736cc0d1ac69fea5464a33a52373f61a5020cc5804ed9ef0e85c21798dfc7f02f10e9b06894367fe33f7d21f83488fa851a0ae06710b6ed4b8055db1ac9e8aa15671dc956a3f83703d46e012ffa8977cbe35169429c04cc5a8f0d460dc273bec4e8084f289a37ac1a4e34e5dbff7f2fa36d809b89285a350d0bb18c459d4b1fd0c6d823aeac33dbdda057b54a41ce778fa8c368d1bd1a1c74c7222eb3fef16109e55ddca482b9e1734c2c9c25fd7e100adfee0cb74450489365e051e47887126754947e3c8b779cc95984e3c63c1810fd1d2c0f3feb29d78cd18a914000126a054260e03918c9e5a8a21c35ea7eb0247a466074173e5948593b65e45f025c14180f895c2f677e3643bf653dc225edbfc0cbfb4c858f8eb0e520ad4e7ec0f0d", "ac52acac6aac525200", 2, -718278866, 1537743641, "619c12ba2914099cc228aa7c5db51b91f81e114147a7666e36ab75c82f307085"], + ["a1b9a05f03e4ce71b428782acab93adc4c751cc1d3c85cb6d731b116e511fb449256af13d203000000056a6352006afffffffffe2f9f0d3505f6275c34b994fd418188b7350c718d1c87a606ec87b06be3aac803000000055365635165ffffffff2038bc3713c89dbeef6953902407d03ba6e3ad3821e293d054ec98834a85ddf8030000000070a0aff602de1b330400000000016599f6470300000000026551e97563e500", "ac636a65536a6565", 0, 1121641278, 1537743641, "4c25f637e1a128bd010c07c0fbb40420d12f606d3e7e891273efc83f7c47d985"], + ["2d63410304a3ece37d4f56152487b2963404e2091be0bfd9c7ea7d4c792a620b622d6c621701000000086300515153526351ffffffff8d1c7ee59ff25d19e7f51588bd80459024e120c0afc22593d5b137e45322663b010000000400515300ffffffff84e2c25ab25724221121b88cc967e31c8957a6f65a888c78a592c0c46c32aff103000000056353ac5353ffffffffe3b523f811dec65ce76e767de6c00fb469ad5b16c13a166a3ca3b1617142d6910200000000ffffffff016fd7070100000000090065acac006a0052acbd1b679800", "53656a5200", 3, 1908452564, 1537743641, "d015de78c2f562b7f39ef2f1915a58534c8a17e430d146cda06d7054c0d06f4a"], + ["b8a9224b04ba938e83147c2273d9e92d88d52119d3bb367b15f7d2419a1a7cd9a1074b874b00000000096a525151006365526abf5b5bbd485953e47085ca5e513557dc0755b57759dab5418dce31f875f9426a8fd6ae7a020000000352ac51d6058cb9474aa6697bf3710d612477d0e871a3bfb2ec8bf3c378a75c7cd096782a6c131a0100000000325aa67f2b73e381a91daa44341b5f4999c1105d3dd3b43e7846df317075bf4f7ea495510200000003ac656a65bea7c204cf876804000000000252acbc21f20300000000075252636a636365576ae3000000000005525165ac63baa2a803000000000163000000000200000000000000002d0bd90500000000e27d6a9f76900a5b737ebd949225e27d2d0487b87a86e1adaf2e7356bcf29537896b7082d4ee63c1322208f970e89db6149cd85f4431a0b1e6d87c8dc0e427c80e45f8b3c1e6ea30b53f310857018a2f54dd322728c9138d8f8d90e10fda9b430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032c3ebdd9adc2299ae005fcc6c630f04ae6934b25f5f0a0be27c6d1cfa06f52de5017989e52761da5f1b70168490bee094f517a38223d8e0a2c7c26782614f54217ab16b99cd1e71b7b75ea9bccf054c0a734d3c9b45be42028f4b3b40926c78ab6a1b968b473b97320fcb927bb2a7427937e4d80115f8bfa87b96238d51131b0324addab649dcb401c1439ad03b2d97a1de0c94f8ece3712d42af26f44a05415c030e2e703b55a76be6e6647ed7883d4f91fcb9c0a9a7d5fffea95baea5751654c60b0146c8d2c4a6364302bfd6de263d49a14263bb0ff3ba38555b7b00b22832a7b2cfaf3921d12dfced2f8fbee123f9524dd355e5798f55b47ed00bfecef400443f0321dfe89460737d72d25f7d0a2cf6f7ecca78c06a0f4a67341c324a850012fdab0303e060cde9178ab87fca883a8f30d752f37f1e3706cc82390e11394de9ba4116031c47d0afc2c6e267ed16a7401bbc90785ad171bca62b693f2b15b4ab4010e9d403206ca6e59483be5c0e89a61a7addf549aeca7d887d7e650d95ffa89724fbdb0c0201d482e1d8380dc32cac78e489c2976fb6f2bc7b9370fa77caa40e0bc5d7376a07c6717fae391060962910a6cbfe7d1078c1b3db287615d74aecb4b226812637f42bd12d8dd24f2eae939f1078a564933cff6b11b85f65d672534b3e853629204c64a9310de680f0e3031e9903a162c9b2a23e686421566a33320f590104a1a7d4e482262d2d69039d6e0700eee95afb2463f135101c9670b86f815aa16dfd88ff2f389c3d57d42f3f827b29f6eb12327ed157b1a077cddbd7a4e95f150a41c3deaf6eaceefd769db2083d564a5c9418c7ad00c3cead8418454281de8c9b842944a6f18eb3cd9c3d6e54a24ff3a6ca75589d2c6b03664607e37f7220ebcfed860af6807bbe73d3c824cfecb09054f126c942a92b22d886b7dbc687add53f5d33d8ce98be65cda86cfcd8b706001fb35ee3cc07289a2e8561eb89bd4dac355ee95cea0a86bcaca9259180b9cd0f25ae1dd26c76a0eccaa5ad1971d56de758141e0ccbcee00148826e218eb54563824ee063000e23af07d6da45181c3348ae9ef535e8ef9915bc6258b3d56596c265a7e67b72b66f0ed30f0e90e6d873f1f566b7c9849cf3d38d709cd332960ccf33c3fa51eda315004844097e93856d01926664dbb98c0638bbc915dabac0f589341897998c9eeb09997576592c170a5ef671a77331bb1c53ca2d83480b27ae13793f564f4bd2be7dff0aa4fd6af4b5182f11afd092b225424d3c573c9f6cec48e8bfa64f4391ea3585f837a3474ac1b0dcffb69567e565a995f9914865da8f4419420ff92abdc6dcf2ba60dd3b6477d6a735e12d42abe979a437e6903b7c158286e070a8677a423a8089c37d6a186b1916c56f02b03390c474ba3d481828c121042dac38aba4c7480d5da8c3c87d2876e196623ebfea3262c452f964a3be50d67d324c567e62fa6d8b5ea66e807835a5b2199f76a9ced70cc96d34f5f513db56a517d610701891f896f46b867fde41e29d3a7c87557416648614ef948625323af375677f8a819d6326a418752b666ca2bf50fdd91e8b49df33dc56665b2576d4d5f955c9e6159680c1d7b2f4bca1bdf7ca78d8a10ff8fc7d5fd51705ed5187a052f96b841dd466720e3b5adfcc5dcb74f6a38f5495df125a13e2598d926298c5062ec69086d0848a19701a54bfdf46970e8b1ee6af4665d80936fece7f6d63ab0373910fc573fb34b609653d6aa66f10b879ce549d5dba9c442817139ab5085dc290532fc689002823445d352a903fa75b13d5cd70fd2739c42c784d9f21c802c71119a867f5487ae0729ce60f9ada1aa52fa12baa13dcd13a364f8b729a47b56ad06ffae86594232a52edb6e608c9496bb83d9801fc87a51410326e4f703b1acc34f1a6f004a347787cc1f3d663dabd682612e0139bf28beb0bcdb4ef10c02be935a3cdf88e3b333b2293bb8c49227b3834a918cc20fa4ba59ce9339753dd70b228e34e296b89157b465e6b4561cb584ef66d0365f7a61c2873bd5a487144a03eb63c0c406806a2dba8921d159f8623055aabdefea6ba87aab5a3d67255f259ba1d54c3ed65841013882b4e9cc991e333d0dc55d731890f02a3052fdc0efea4d16d26d2e5770e18d7b06f3b8395cdd4befa529ac0febbb2bcc29189a327a4249610f40aec9bcef235f849a22b10831c8a76fcb34e6077273aac90e84e2399d0fb56aeea9604eaf60d49c8bdf2f03b32af3d05a80842c1ced561c32387ce7d49e0a160ec59718fd0000000000000000000000000001c9fb9d0ca9ccbbca0a1a99b5e04765dd27c34a70693cc01b15118e22eccadc5358c2ad868b492ecd552431144079cbd595d7a8454c1428409d1e525ae19345c5267ed07c7db3491fb9266433165040faa6eee7243dfce796e495d05a825f651000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a77b67a15cf7f3262feb78d64487d79c6c99f1e5aef71a55ff0d864380f7589fb1694335e82d799a4ea1bb5c2049a3103d1183fbb38b1fac6ba365027a348a447887d8d4d64d6a3a67e31b5df6bf874b03bf251f8a3d24c325778e321fc56cade71dd3da6458e9da2206fb3dc981c6b478c174ed53c89522211d7fa86dbeb31022e956cc225acd85b38faad131050f8d8cc5cdfb54c913ccf2c99379ff33d46e9020c1ba6bd9ce747a15792051ca67b15356cba85e2e15d97768bde60879ef2fcad0b07d3e3538f958dd0d0135ddaf050a813c10ddf1b55f499eb30a793abba4b83e4ef8f8cba43c06ff0cc11581fe83eedf85d63c106bf21774c27a245d5a03a3f0603023731169142d26a22332b18e4533b64681591eea325f64b143e1aa184b4fd0103095b6f51704badce16706169b2cca1fd6caaa99b588bc57b1ac4df3750d4ea9c031ddece06dd33c45d8a749c587c314de8cde625afa1d8b5f04fc6bc7cf69cadba020b9ab24a5a6a422300764d5dcf553f276a507964b5c3a918c2dcf9596da3fff9022fee396e5441d9b13ef358d1fd635d5c371790de69dbc62e6a0c96fef54c8853b58f8617a08adb4db31ff2dbbc37f6fabea7c7b8aa403c8b74491b62e1abe5224faccb8fddd410424f823972b56cdc658869985d58045a0e17c36df516e8fc27250f5bb0f73af5db41d57d6051d25a3b774728be9fb1733a49bcd598209614fca62ba988e9c39e8ea8632e17e4a2a895a4b2f6e0002739e347cf0c9abf4d83c51e363fcf883b0377724039082fef2bb0b163f7b1b97430f3e97f7a96fdcc7b579cd5cf5a2b8bc68324449aff8c11ad766785f20bc55f6c0601e9db8af9b42b3010b226eb0607d1687f78541c906708166e27b507225ebd0ce8ef138fe18391e81b7909518f9cfc01636869102156d0172b7a9db08cf098463bcc704d3dc285b67b7b4cc4b0311d3559dfeab204ee57eff422d55930a512415eb15d2960ee02b2af18d5a715c901c2bae3c67bdd2e4939ceca1829108b79b812dbb7214af25575bcfdd59932c69d5cf70c7a1f7182e75ffc9fc98cbf5001d88c4f2aa6905fa28695e7aac4256c386eed1b1320d1246a7c625977d9d84c5b3d33f4b39f6992745e6fb79d3f602b31534cde23f6b22b3d581ae167426ed168836739bab83170f1d7519ead888ad83c9de19d35234cd87a440a993d6797603670c2b8a3ba652de7b97e2b36f57ab59395d92364fbb927d14f9732fdc90d616fdd7f1a876b7c8e85941f0108d499469cae6f2d4430c76f648942c12303c7efe77e9e8a991a2f2c9b455e5f063a149c40bbc9757cc75914b25a9358533548b5338c0eeda6069d5d614e62e86f2e4f4d2d760191aefd59d27eec55204075953b1d33d2b231dc77cc1ae9bebb6db846e3654b2fa81f8120a51f8e8805f7fb502ae25112ba02a1c3a439e2fd4fc2c1a1ac4b516bab18592f1c166cf00c68164c09cb611779285829124216bb418150e61d1acc08e72558e9adca899a00ebf7af01e160f50d431261f25d099ee88068dfc5d6feca3a8ccc0cceec4e28f69943ecd359eb46c1623a688f8e2756c4e59827033f2889d698d923ce23a92768274b5228ea640ee6f4e1fd6329b648af3c7030911bce9a0aa8fec59b6c98a2adb6037b7a755a6ff07e9a24b61178985dcce71122b798900e59cdedbc186faa205ebd4f5b7f379ce63a6d9780e9648731df0bfb2e8d43b7549531506a047deb649a90370b88c0f0c1abb4cc0cf861c85faad10983646520cbbfa15015e1f98eaf6a5891d93d268cceb51ab4e510a241c4fd9efcd016d5f03e2555ebb4dba179b033ef41583b8c4b89a5aa8dd6762034963f87d5070ac3fb0524aa087770b81ad4c816c7410c80d3185c1660a63282876ea6fb0168d3a70037bf61a4375470a879707bd49745b9b3102547e07d859fbe225ce7a8b6e8b76a31b136a9c4c9fb343f09abd430eab05cc5f789af5c93baf39a05ea6e5e26edf69c4293cbeb536d2d020c2c2e073ccaec849d2000228097d2431b9a787519dffe7509c6db23bd42bb77c25900aea430fb5cc91465bde14ba60fbc6cf3905cfcc270b8fb9b35775987894a7475c0701848287ff46fccc984fe68d29a8620f14b8753bb4323bb97bc3b5d9cc127ccf732e71df2452de3f92584c7ef00859fbe4ce0f08d3c0d14450afc6dc08e24d02453ddb738b8c2bbe76a36de895ed1520f67a8491b37978df0f8547fd725acd156e75df43608fb6295a2d4f12edd7b8451ef9cad908d94e4aac020d11bbbbf694631a920cc8ccc6b890f1afb6be453f93fa63e3ee1b415a131da263a8c66ba94da3c97d7ff1b787cd9baf3cebc7a32e11c2d080c70e51b6814738a72f7a06276e093899972bd6e588734642a29e9cf4c2f9fb4922d8a69bbab77a10b", "53516a5363", 0, -2079140540, 0, "03d7b925122aae2f32118ecf94da64dbe3d3b6bdc11094fab790c2c1f529df73"], + ["030000807082c40304113367e7682e7c3cbf20edfbe1f137827fdb7072f7bdda6ebbcd0a78d78723e2010000000463526a001223d576d163ed81a13bbb620cb64bc85eeb3a349c911a1140c38fa86418267c94a41ef102000000066563536563635312e0d925e2b3b6b882fabfd38e6fa4474b4ee196b05424588770653f332dcbb14a7f940100000005515252656a78db2c7a1e3a6fdc9a095b5479dac44f26f7dd3b6edd970b59c6b8c13a48e2bf8a9ba3e000000000055200520000ffffffff03f257fe040000000001533bc184000000000004526a6a51830ece04000000000863ac53005163536ad37f1167197655fe0300000000000000008c10770300000000c90ccd65d7e7f8032ecc311aaf5251e1dbdbecc142071f8977cfaf04ff06aea5cbb892bb6485c5af5c249b0b94aabe8fe453897dd3882b9a4d2cec27e61cc292562a1166f73cb5be503714c85e6c466937a14bfdbdd6cbffb0e5e9a5b466b28c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a67cca4e27824f5d7497fc1233f9dc19d948052374ceebe9b66b97a04fb3a42f70a18132568e01aad8dfaed9cf43cf5e7a43b237bebebc1094c5582241b8743d59278dc020c5832f6293c8bc85b6015bada2d84374b71626b837fd8597616de617ea48326750338d9636f0bdae894c27ea5707c822fe342e7d20be379286f62021189a452d9e76913d8e223222dc2154df5b6eecda76b28eaa8ae0cd075db1170031cc814266860efaab00af1542d43c1c218ec3c25f866510c90bebacc563b57ef0b0417c38f471314ee590d89ea902cb436328d3571f3e3610961b998116bcf11becf6a66260c0f8d57185032d0d47e8fe4626427a0cd1597074eae81fcf28184f9020552a1dcfc04906bed5e8a372c2b494d2bebd1146b5d1c1c7da8e8413a0280bc03198b434b7edd8c320c64a9558041d6f9706f2191092a97fdd98b3e2606f4c67b021babdc27359c5f0126c0ee5cda867478af3ba1e3dbfde2839d4ec3d505ba45f4021105ae219726bc392d2787fbdeb8bba7e49d9eff8ba498e284d7580cf79b6a01030173c47d778d528a79b690cabde1169c5ac611637989372e7ed703efd453661786e877874d163fed64efd3cf15cb5dbfde729eb1949f3447ba643116d0a7ce81bb96c338f01a9faad6f3316edbc4013aa67c2965a688dea87f3133177234fc43611358d87d44403a80c35b4932ec9712e92b1fdb2f5f2a81f4a6b0f38a3b241ac7b26ce1a10c28a3546f9b07d96c04e9a86c6674ab4bc491b0c8bd75c9a4b7dd7c33c9b65f995d7511ab5cf9a9224495e56a5f55b108df9ad466e182ea8062bbe2efce8154a8b1dcdc395b1159563466dbf1b0d52acdc95f163502b3946d28eb33e0d128523d2110bf70f3ecb6c178fca3ba5a8a3ac515cb5010f640123cd9321e38acbe80d8f9197c0dbaab02339d291628da9d8fbdd0b4746c161ff3bcc7e577fdcd3a0c67dac7cb142c9d92864d2b1c6e3ec31ebecc045e5f0efc633ac12f1c1bc5e45bfc41330e629cd27adde32795e2d33263069e191e536f179661ee5ba2bc47698f6bf5b63ff0d47a8bf79d8fc308a5ec6e9756d590a3fafb9c63a6e9ae1c372c8ec0b185a6cf5cec756134214b712658815fe8b16d0163b5301aebd0d68c0137c21bf3a81471f64ffbe2eb5753e3f7968df2bdc40a43663c0759d146cee661e276e20b7dbe9916f250615a3bcef10afae3704f988bf065aacd155c4f2b84ed32b1f7e61d896162664cbce94516c205e4873291f48e8d5366e423e76c15003d843adfc1cf50fd187ffff4c4837ce3f631afa3b3f92d635191326b5b1a3f3c473e9b7535de4fda8a20e67c6f5625a7841a8f11970825a8914741af0529cafdfe8edd7e41ee237ab79bbd0c37aef7a0233b3b690f23b9d3acd0b9f5432305e0674629db8b3a464b5d5ebf4432332ca66b1946e264bc4052ff6338feeb3897a98b77577321c20632008701f10a9d471184c1c92677ce7a1448d3b74a298a38b1fc8fc6ed3b3a7240d71a3c10850e13aab75f2dda69628072b0ecdb192e8318f24a88d34bc97a01f603dcc085bedafeffa34ff3d1ed1599e71d0e1020b98020d4f4fccd21fa3eb76315aaad5c0d319df0e605350891b63504a93d19f4333cd6217e7f7029a9f576469d799d3ba5546f9e2809790c9c76d60e68ec80483558dee798e90266ba9a9d6e98f86cf7c9f5d51501aa119900ccfe34e5a6503ad6be5f08c7fe7492c994f6910b967d9691029ddc2c2b44619810e86a57e149313cd9f12c6a5db661bf4c378e8783f93ac5d3ab535950dbeed76c4cf947652061bf08334a48bb4a2402ee7546dbb7a0cbf232347d293f2027d63dada139dbd733fe1d7ca967ace50e53c6aba9b5d4fd8d1c3ea0c8d8d1c62079d785be7ee2444b4f6f80a4bbfa7fcfd14d875538443abef986a2b298f668c5f2f444df545b1c6a3acd3db31f2a8977ecc9089a96226faafee261644f38721f9daee91fa0056d9eea8b33f6a120e9db9a23ed40695ee5ba65954d7efb8dd83e00202052f681a3e9cba2f59922611cb011597d616b646fe6cd4332e93659ea8b3194dc16259c20cc4aa1a9cb0c002107858ba04e468dbb62df4063620e3f128b422660b118ccfbcc1005be55e514dc81625549e520d114ec3c34d2513380253d7cc3fcbe11e4ce72e8585375d1cbdbf81b28d066648d726f4afa241358aa83ac9808b6c26dd895bf513a75b5a9cb401f83ac18e072aaca4dbcc446a09133da623685f96ba8e8f3834a8c3a89b3e001c755a698688475db8b805ac46200000000000000001356b1030000000016672b16cb7226897669c6d260b77c7c9f7d6232064e8654fda8774812f9b070dc81ebec958a619922efc1090d54364937ef1b6b27f2b3ea64ed81aeb679da7915dfeb59c535934bd7f1b583f6f55821c9808e1c467a60fba20b507aa608e0f8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008b05bbcac4eccb94cf6bc15b312967fb205157e97299734bf74a39a851ba513bcad634e391f8ba521e6b3d16e5e35c6ad0c3ea663597f16d12aa4628fe26b7e841e329d137d722d4af8b382f055b335b1eb7d2154c9985a113d3da09e16d38c56c99e6053014ee42abe3b036c31bd1ecfbe6a479474b1d802a2e35895945b31a021bb111e63ff198f93fe4d672e54d1312cfc5e6feaf402feb61627743f54f8f6402012f9e6331311d3f0c83de8ca8eb5c2fc8890868c9e51de4d2932b7dc673ed8a0b0100d5d68dd47390e52240d0a64f647b21bc118dc008faa0ac1efc6721f7737af32a188e056128835b5ef1ef101746db335827347e91c92138e81ba2f18d4ce8022792e59c3d90b30e6493b28edab1aa53a1a8f60e7b15478b606da365f0801bb1030402033d5794e3353be274186bff151095f13d608453af0761521be5793dbd5b0323c210f8a4d10887a9046350ec86ac8c89e4d7541cc9d86b68a5b6b31cb6f4b1031117cca964c2b60ca6bf631ce26b594c84ae040540e33a397063918078e37de50315fbcf8e8c7b20b54e349835cf872e08b944c5ad857f7503377035f4d1dd3d0081d216ea9fc25f79a45e814ab3b8eb8b0b2fd0951bc66e113222723f8505bd57d751b99afaa65a328aa11d4af271421a4c6eb525e5dc4986f2b9ae6316f61363bdae1007874884fc2e56715d3a85256089b17489aa98f1e7c949b45065d6934f10c0f1ade87451d7f869ccfa93c43e971c1bdbb9ddec77f6ae0f5d7b0981a3c3c33d5e8d9bbcb86721d4898988ac31a54b432c20cd11039f29946fdeca9f348a2f7eabd04847f9705aace7adb9b9d3ffd5121abe2bc245952eb2385e98a4d32192b27ab42c59c55c61db4a7ef78f14116f50155c56adc92c05909c64af8d10211a54b785e03120e24eb4be48650b4d4e7fca8ef160cd3e8bdd1c0297989b9c0221313abad84b3b5750530883bcc11c79701c2888a0a630010c91ff9db499895ea41a70d39cbd8353405d7e070032d4eddcf0e1d69320e69e5e2d5e09d81380a4a732f8e39ddbad29ef9989b8dda20e24068e23fac2c0ed12547ebb5ef7521dd061ae6929890d1492131c5a9a32bcbe5a98cdb95204518a7b487e76c22d2d0daa13e7e985b6d74ee9412be87db69bc4ac70b4ba785464a9b4bad8054a21f5b757c638a7d48d05d755cd48a00cfd18d88c9486ce1d17eab6cbca72eb277c2e8c87fe35db5881cb113cb741a77c9f4a01e0196c55e13289c24bbfbcfa6102e543d5c65feb66bb83f94dfa4404ab2f4340db0de3b92f47b0ec52e249adb45be5c91c1ad8b2e21e9c5fe96163cc88e21f235ab1e96d9c8dce5a84ec174ca7e4e548de14c9b0867fc5a0e5f8ef7317a5077b9fb6eec8d6f533899c5c535a9ef2a921fc2f3321ce5a7e0455b638aa9c98eb622fbc111ec36724407da0221eca550168118e2318d4c9eba238a80e05d0da60be72ff605614f2228d8437ed8e0f30df79204e3feb30081ce9448056d3b9e8af572dd912ec7a7a4c4e73f323a84d95ba34ad2c9a65f334ffd1f18bde9e9659b7a7177fce15ac688ecf3c3ccb361869c7ed77e5d745cc1d8ea0efe84fe2f75665da596c0e0522ea44b3e6931f98eeeafba921b60ae983e33347465d24ff9bec4c86ef558a93a6d1809d17881383ac0fdd558051022d12b1b58d27b27ded5abcb86b1dc23741da7f7bf6bd2b6aac926350a579ca860bab4c469ec38f4e9f485d651036c208926f5ef71e87ced9e662a5348a42ddb3851768a95640e7eb05ef82b8ec7ce3f881ff9faa4fed1d76cba4b07d99c8903cd5c8a24932c70560e9efb1535e12b2316787d01f7fcedeb68c4a7a60a9fea1ea0d71c1c8acf58d2dcd0ed45bdb481eeb7b34d742e0f2add84f9a6d68d53d2a96c187026eb6a22246dfa77ed2b9e8544b2738cc1aa87a8e80cd34d634c78f5c36e8af6ceb3f595761024fb760f107c6dbe8cf8a6aaac9189f5e3867f85462879bc22404837f1ef3a01a5fd4831f4f14383635994a016fc53f5b08947f0449f111d872e78a9826a35cb98f459a55976e926ef64bdc8615ec9831530107f09f01c80323c1e8128eb6b5b5dc05da7ed234cfa971cc286112c21a49c98bc47f3e184953e8d8f236c2f63863b631d91451130eaca82b86ca51c520a88810b9fa643cb6621295c52bc9b5ff84ac32d8506ac867e2e584079852d060e4f8d4625db7e98a47b2c22fef6bc026c54d2c8d5b9fadc96641733ceaf8c0e82fae3cf14c0494961cb9dade540ccf01297e32f430c60d120000000000000000eae6150500000000045f06790a046c0d63cb8160972d1e1e70706970b5a9089970c9cac9f6dcc5e1cf9f49341c57559bff4878df169c4c25c15a3824696e91a87d49d877dd3227579f08b02d9b13bdea0cd233fb0295c94788f615d5cfb1c6f74345cb4ed9fba94e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a971809db8634dfdb0ed91988cb11d40bd0bd15a327d6fae6e3cdc1618234653ce1af95d9e8d88b555ea91429757f2f034eef6697dbd7a74c9f9727a955be860345a7b888a84b8b112ba13e2e3f47a3092b2b451d8008356362da3efb9e6247b70f6026e23ae5a220adf3908924ce6b0a7facb1fbcc04aa6d17c7b95974888bf03304322e770e60717efd9aa81654c0bf4e9218e3170510633b4cb31309485ee94020ee0240a6770a10f0980eec9d32fa120701e880275c8cb0fd6288f7cbe4e0b3d0a04142eded507ddfb8b2e7311288fcba32330d08d797468e6811eda57d45eb5de96b1b188496962e6c444c30d7b5f05375441d72f067390d620ab1171aa4fbfd0032d9724b823729bf5e4e52464acb085b2a550fd9f42865ed8c1e0439c403dee9002281f19d780c961abeb468eb0c6815f06254ff700c20a1d68563a5d8a837f86c7030aef528e42856d5712a12f40e331359a13863c685e8b72555accbe157ddcdcaf0218ad05caac42c00328d7001846186fa5b1b541c92fcd247aae908fc0256f4be6030492b1af935a51bf3fc62aef2d6e73da56243fb723637d5b8f18f723ba0a7196d53e86cf731a9671a42c44a96abb57e25b5fdf2f255f450763b641bc76114c84dc55d8db503695100af2646af29218865c9b4e686c079e492d7552831073b52df6b9b536eb675770d7735f96b999bc1de7ce8ab19aa5252f5e50b3857edd42e04126034ac741006188216aa2d8d486553b114bcbac7dd5ad8843fb76194d12d973b657d7d96af4d86116b163a9108dbc2f785c112cc485cc52b433c5a147d1ce1ddfdd924ab0b0e0e8e28c3c2080de3aef93dc54d9b08505e4125aa95eb09ef4610bd6ed8d3b8b052be368db225b0e0fdf53a90965c9a365ce2d4adbc848738dab213d6b4795ac4f567ab28ab51fa73c921f18b1db90bc87a27c9fec1c1b2d82fcf5c5077fafa9718efc7ae830ce5dc57edad6e9f0d875055c16dd8efa2db7a110e50d760a9e28279fa4c6276391459686f648674ad93e8a36fa0f184c1ffaf02d8e04932a69d83ae563c3180dc374ceb446b482c1a6b05bcc5c9278d5aaed70a8e8df8c5052d8b968dec819d6c959a61829a478ec367783aeb36a696d429d135e87133de5f7b84dee3fcb85febe3769d2b5f33d735082283a7a7d3b037cef5cd7cb5c85fcef7d7a3469e61c2ff6563d640a426f9af9a5c59964595a29e4f793ab0daa6cac57871bd2d5c3099a53a362eea417733775a68bd5844afa1d6e319e428749f7b80b41181806abd7aa38c7dc14f27d8404960a8cedefbe47dd8f6a01b74f965ba354135f9f95cd875935366828cf00ebcab1c7525e0b8bc6fd8c3b19f45ebbcdc9c1e84f516055529f7a8de2f0178c948adcf90d99b99a48c6abdf2b69d3edeb3208524076461c624846e7802f5d3b12b98ff28268a21f46d2313aff06e111cd009d4efb22f6e9c30fee4c48d48a5ac6215d968d3f1bc5b61ea17313d8bd498924e5865972286abe2717280d2ae3901437d0970f782e79d00dbd3c125e50bb99edcc9324837b0a17211ed002d8307b542066ba81da031a389164f04d1a95826855cd0b70374919bf519e68516014e54a94d8b756ea4c8f1bc295da11e04e469dae5ccfe0c1757f6f38326d47a0f49760ae2419d4d1fcbd2a6cb03cb990ae4e9ddfb389a714aa2df9d4ff983fd27640fe1867e0a12bdc2e0a1d984424ef82d725a3837c0fdc0475378d04b417a8ac147ece0f6a33543e1f769f19be418083f9320474bcc99ae1ecfb8b705a50fc71bde3695d45ffd5c82fc6b7a08ab3134db0bc5b07156c362831f8b2ef6fd82df680f855ccc98ca289f7a7f8c38a236a63457ccadc33b46913f4346846bfbc937c5e94cf428e23c0e0fcce5c90e952543a3ce7f507e8ab2bcfc8ad3f3158006f46ecf02bb3b4bb75e5f3a861878f7e8e81d2d75922fc6fba28dd30b2726b315fc102a55cacb639973411eefc0bd2a7422a7bb6f91e77e3bf42f955bb7a2c7b020a0bb24738f59edc72dd82b813419885848f06bcd8dd4ced5c2407491974de2faf0bc7ad0d31e1ae42245f4d6b96b6209544eb1c7c44bf9a660500a4041763a021491df575e673e76b14a8c6735d5a2935af5f7a560ae54674971e17f9fa1cd0e1fed532ca1797098f747146eef72308d6c4feefd76151dacfc9d9a0203b7b4d2ee1d40bfb73e353970d8c1f63a5cd0f1bd1721d400f11fabcea8cb6e3075fbe99871672b9ba9fddfddd24a71b286e7746ce5c6e198efc9c54be8f11175775d70e0b03eeb833eca83771cfa885c3fa72ea2a733da9b2213b258ca7a0e0d7749485df74f02327c8ee76c47f8094814ce03c36f7496f26a758908d4a92a61cf3896a001b48d53d9569941b48d33f774af53302a08e79b2b80321702912a9ffb6ca00", "6a00", 0, -1835469658, 0, "7cb5aed9c4a5143dfe582ea79ab19ab09744f291f165b413bb2b8d48c4d81f1e"], + ["b8052d2601191757e15d2d48831da44dd44c9a7d6aebf477322462cf3d49368e17e8fb01880100000003656aac250c10a10302dd7c030000000000ce0993020000000009510053ac6a525100528b692003000000000700ac6a52ac63650000000000", "6a65", 0, -1140675683, 0, "3cc23fac8bc5a958d606aa28990eb82d84cb2a761dfae7cd02135d6d2bdf0069"], + ["030000807082c4030128a3c3f781d9915b83eda823889f2c652368891fe1b0501426645f901513466c030000000153ffffffff04bf5b7e05000000000563006aac6a52a5f101000000000365ac52db4be1010000000001655c644c0500000000076a636a51ac51ac9098a6090000000003e5502800000000000000000000000000006c7b352c5a00440b1b78452d41307a21e6c9c1bb6e6b3e3a8a133be6038d9d709da0cdccea3c4974983f486f439e880a42d9dfcd5c3476fa41a2a201de9a4702cd5b457cea8172e0682ce9a8c5edf367f00fd4367a4b853576f3d3231cdd550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054dbb2a2aa898aa04c9eec3cff86e4164c721c34a4cdb5d82f40e411a08f8e837e6363b5f9c27406e553bac96703619ba5f686da518b9159a57038bb57f22fda49bd24dd2283419e5112ae9120f78cc35163ca9c2c28ee11cd81af9aa01fff2ff8515cb2e0b026afd9ce0a171ecc98977f3a8998f02c368c636141ccd1f5cd9a020dba7e9d88cc6f1434c3ce55ccf6c598086bd9bbe890934b75d76cf9735cc68d020a97f1ca7e56cfeb788851497c05d8a75a4276b1306eeceba5200be887ef34d70b0783766ff3a3fed27c570307414e8d2ab314b4bf1a7fe7a5f78668681344f20d267db3170e4b4bbc3422a70517c32a761bc1698437ad6ea014a3916fbfc216a2030f6ed3e1a8c63744ac14e01bbf6feaed4f0881d5138face4a5b3f17b1c178d1602279f9f6639870267fb2b4a405fa26c3016cf36cf0076437979316b846de948010215884d62cf80d0359b2145dbf16d934d6054fcd132111c3617f27394cacc9e2603018d601f08613bb6823f3dc0f6dd5564939e3584dc8af5ef486eeb115a46aad7031a8c723223c008e2661001596bb53ff367932c9fd058200f3424c1abefd713880d6ef3dfda9f124c7248dea1aa9483fea901446451c5e6e94accee7e9c51e875896ac526037649f9d852970c53dc5baf141603db5f0ff4d3d0a9d7b05bc21c9eb0ed74a349dec5e5e7cf249f134109719f1ebef7805ad76349717e988b67242e24503e2c61554f64204a44a807d872bd45170f68c5e2ddb9fae5ee373d0714cc57c761f5261c279411c4a5788ef4e774c1122dc9d8023e889de452476497719ecb808c0b2628061a7022fc96cd7728bf96c7f159cf6c858355db914245224440861afeef4fe0d5fe2c27aae79c79e06510c21894e97fd056de9a6a3db7092530414573ba40230f9b812fbe36dc681756586288d5274989a1a70725fe677bde10f0f4f0841a1ccd9c1dacc13e4e99b16cb30960f438eab648282628827763f1c6ac0ae44af4f9b1e12043f7731621d8fde37de0e8a5fe610ce8b3d793480bc37c7451283006150ecdcb400fcc52866c2ef1e63e1a1f63506d5251426ff607f49cc495c51a28fccbf74c0e339791142cd8cebecf8c377eb4afe37bae25d2363026b1347b0ed9e6251191ff338d0488b9f393902d6a50df856919077c01df51214f77700e39264f06cc1cbfc9ea50ddd996f603052d3bb75cf24637bc0d58040efb369d07b87e0941d0563447b036dc053e1c4381989a527960215bef8331adc600bdd5d4a2eb064faa0c73093575f52f69a505e60f108b128f0f725f80f9e4455956b5d890fe8f74f6ffc00ac240c9b96a02fbff6d710a45108578f1bfc8c599bdc0e20bcbde831b5fda3e9188ae78a76d56e482dc9a535215624bfccd7787cadd56780496e15f4deadb09433259ff117a2e6ee1a041c5f2f5f6f72cbf612337aebb2a2adef15e3cea8cdfec81d1278de4890d3538544106adc3d11b386876315e874228233e198789d974ea20da56feff4c855b97e66b440109163b60320ffb876ad7820d2b1d7c0c23efb7be7439e2fd45300e9a12f6eaa6833bf2c3bdf849f6eb3b75f272c7855bd40596ca4fd37a2425a38e4ebf50c6cb5ce7244bf2d8ae1778fdaa48d897a1b357edb803c10489c0a1f8632ff77ae576eba4941c77bcde0e20ea31e47cd1521db5a59c66e3f30ee5cb68eb34cb6e8519346e5de127081d1a18c32035abaa2a410e4e5b3ef416d84b97e2f76659da399eb5a828f6ea031e3b92a2a3bc364d2853c3cee3c50baf1d84bda0156120ea17f14a583eec10a04ad87a647e5d22446760eee8ac64fdfb24ab75d7bff140034b3ea2d50e9bce9e27ee81bea2c4e43be268fe95a7dc39457b47e5371fb4653c73c76b66526ccae33ea08c5d76046b9ba6f9287b186027e5634eb386991893ef9c93d78a86e4162a06c0b8629dc01fbee9f3b2fe74676ed58cda3fd7e45b7923ec3e67ccdd95fd3407254b3a4c559e47819863a2f7b50539dd06dcf307e8a1a8021dbdec13bfa8b5bdae61644626cea597202258c6fa8727c39bdf5806314f6bda884a027df9207c8471706b22a26c099ea889c97b430c338698bd6e04cf2d039d6b83ad227c2a94254b3747c2b160544fe52d691fd4a4ec24893b79f40386289e960820b26573d5cfcea320a96b0d609a48c7a00ea1359e76d06b6925329dbcc1097d1a300ea8ad1d5fbdb978ae01e130ddf1eb9dd14488e06b2c69e76680c840feefa902cd55fa746288beeeee197d167998c72b3eec9c2e58569344590002000000000000000000000000fd6ec2ca6b6bdddb3c3f6b4d507582d34d2a01c6844630ba7b27c07afc384e4ac85c01f7009ad6666c8e2fde15ad2b350fc1fffc8770ca610137fa4f3d31e42179f5e0f455b7b54f4ffcd966bf3f656004562113d079587b13658b0f3fdb1154000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001269ed6c67d4901db194bee77dc15134a971c8aef001b8ef5948bb433fa6dcaa18fdd4b24a9f95a433845b48bec38f33d0dde70759b709564021aa3061005d56e534404e2ca2f96921dec871b568757c885edbd498941b1a834e602b6fa742bef8bb1141415409f78f7d24af96157a5b8f5343a68527433576031c741ceeb077022ac601ef9fab471b34b8f225492e3cdec117c1d2ae831c77fd029c6e5ff7ac1703011783a80e7ddf2f18dc4ba91737c4c71bb529e2fbd950ccd92ef326dc6acbfe0b067f82ddec8f6e505dbbb1344b68889ed4b2333fe1a5e1ee911390431c15166695b22336a2e996a3f4dcd4f8046f312f9c05f3fcb49c48e2c5f6b6b8bdcbe616031026029556369c24fffe299f3fd0ac4cae2599e17ce252c1c3b4b171b427c2f5031cecc71975a70df121ef983d3f480873b3b61dea73fb29692417b13c42bd7841031c0a5fbbd46680ba183d2068f961c4d0d3e875af75a7f6b64593e969e7fd92b80304dcca7921256c9beddef6538f6d652a04ed5d4799a2c9d61d6d9ddd3cf8197a032b3e9170cacf9deb224431a7c150bf8d836fecfbef2d0b6612489efb0145819e2c03f6c02b2ea766743952739249a9418e0514ea3ebe847c20d7592c81fefcbf571bfb326d102fc1b730249ba9512bdd4310c1761dd42fa6b141c306b8ddee69c785439e598a9d9e917c6047fcea1bf69fe3c73d6bf27c524d018009223fc19ce789ef175bf3deb1cd21093593ca3b4fe9c559760efe5b6b4add002c65ed2846531dadcab129e3ce2d96cf2380169f9fa64680e6e582a10aa35ba4429fa6b17f4792e9b24e61b243963f390edd038cca65c0acbc80853a780f8d355aa587cefac25b55385a71cd5ba02b947c18ce5645cfd85ad63f309c6cea8219c13dabd8f25b7b66ff766898dd5d506a1cbf8c04c8d2708833fcb2f5f029f876404eaee4d74447839079f60b0f17a7994fd96726e5ad24229a1c4521d16d0d72c9d32fbbe98a8f7877aacf4a0e9bc32dc8fbde38fa31be98c12ab901b97a592dd0f828a40bed703f194cc615fbc94087a32e3095f14226a4c5aaa999cfbd0ba7af1d22fd99d94991c4b2dc0d07d0771701d2e0046e2246ce98411f8ee4268f30e29e295de37284de622d516696b64ecc623bfff68b9b1431aa805cb2438ed45053abed3335900f0510a17b8584b6ac68db771a9718ae9c08ca4bbce57466cf60ebd5096370d5095f63d859001b2f217ce0a2348e3e772b4c2d6a6660344fff49c3333fc948de3149571a40ba54394179beed4c889c95f5046a0f47c9a32c1e7f4fb5bcdac6091a89136169e9fc9aac469af7da5af5d4538bcc05f12805517f30865eedf6dfe5bee192221670bacad9299cc4236087fcfdcc9e45d3ccc6821026f1add3ad18af28016f75772541cb74b175bd7c1c959f35183328fd72339737d76dd041d245da4b7c3b1b6b8adeb2996e462422dec652b4f51134158fd2192dd2fa0df17db2a4c2690ab4a1c5bcb4e1f8cffe571c472b23de06ea3dbc855b66e2d6114f0a32727f2f6109f0f5c33c4061f84196f73ea2ad5105d7baeadd3d4526be3c269c7f18b16f206d48b470eb427fa57bc92691d6a9fe8467c3519aafe918f207bd286b6817dab74852c1fce5e51bcfee0b3f7614fa4fc095b22a954450641977bde3e36e3f5b22a469a0c532b18ea30b2d13db4f7b23856477222d1507f11c75467ffb06b3597022abbcdf8c695e2d2d517ba2178a3e3ef0644d91d06d8362cd4ca143e0ecd0f58d0971044448d475c42f398ebd7ff1a1979372bd8d81feb3650b5b251db9b17479ee936762bb0a85bf308f368cbd4132e37fde2593fda683cbac92c6c2db7a5004842787d03fbe8a3fcc3d15559fad76cd76749006572044d84f41970432644527e5123cfb657ccb573bf7071db3d8f091b805b25d81519c32b104d035aed2de91fcfc155fedfbb0dcc064630b4f7e962a1e4695b23160a749e64d657778f0ab3839f92353190b6b57879b96cc49f043989f01e3e0ed78527ff08cc3605b90b228fc1b06562ad9bac49da60b93fbea3e526498fe31692c38faea57d08c66edf29915162232c917d2d049360a0e54f992a190eff6b89a1e49b82e7f913dd5ba6fe8e4ab2e33f466ebeb4f53c35f61ba2414200b43a6ed6d8b256630bcc3ae43e8a281be87632fac6adbe463784b5ee5e47c05fd732e3031645b5f976e96df99d18834cd0f0901893b88c38fc6340d49906574164fa1f8afa45ffc0fe291d644ec076e9a5894861ca63f9d7c69cbdd7f7b55030000000000000000000000008071b81bf40c5aad1f88e98b3cd2e4f9e5f16df425c6b343153339b289fc7112f83cff6317564a7df841e19dc24802895ea37f089d4d1237a2457dda4dec2ea8cf471115d1f5fa43fef16a8de42424f8e61dfc395ce16bf3c4f0218f9b49ff630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006aabf91e55930afe425fb46d187d32ed24880e081141dc7e17b29afab234b69726a3f2b7e13c022a826cd33bba37cb3fb3dc1a3841d16528dd2d5b8f22924bce61b030b6e4725ea4e03f74c9fc95ec27fd38de3333a4da01acc2d647d2e62445721807fdfd9b65bc4714ff94a97da897c92e016066e4a5ad02704ea92e6369e0226d432436f9563e528bb1df666320e8f9e59377553b26aef79e13aa4d7687dac030c2a6720d5ea636dc8c09615bf75797019a0005ae4b4dda93f16f63af669b63d0b00a057a6046c21b92985c97d7201c4fc5e1b57586a39f5a079264085dd221618c9f5393117aa419984e53475c159c57810c9e20eff84b4413d9f1c71c467b48b02043c05ef06af537e3081f609a332611f8d48fe7552f5a93317eeaf04f7f8f6a50207fb63773d0927cce08943c786407dc0bff5b3003d9cea4c2aba21de8bad34210322e264c9756c62c2b9a8d83d8b593c07a557b3cfabc21ea7fa25fff58ad20463022fd565c60dae252244bf8f87a137bcaf74d4323f87e9efced84c3cb3654b9fdf020bb4339e014ae5a8966489fcedfd0970e67522577fe3209d86fffb0b1fc9a3e93bad1fd3ce3411275ff8e9c94fe00e54a006ce8ab5f6f8a21235ead2544e15b9b53667313f66571c7803a10fefc6c16fced786500f7e77b3d769f3d39c14ddb7aafa7b48749833aeb3de47e376122d0e2aa58ad10ef819fede38962dddabcd983d4e64cd766db17b8c16d965cb79122f6bd18332e7cf613ebf3ef39ca639ca2cc7f7f482c55407cadc05a2b268ae3592b5ba040ab015fc1b299cff5ba784243dd254b67e63269a8ab2a2dc3c9c824a5a05df5a0b03368a25e6bec29f2536781babb56c039e313aef4db9d84e37331ca7591bcacc6a3b65cd3e4150bc3cf3971e0f1bb2fdf2654792021597213d1ad7f05e1bc13a3347583d85cac87455d14eb0f0dec10967a7176a2b348e7f02f96d785926e9b5dcf8150c9ef3f6012639ef8825355684553eb699ba4cb51c0642444cf6135c5f37665ada07e66dec0da050d5d908cec0d5d9a9e4d46a3119d4fc43bf29beb8dedd3b3d862459aa6fcf0ac5c0cd47bf0790da0d72aca8375e1748942364e5122de3063bf0782afa3d7e5cd2c9cf36eb64873381eb9f2d2447aabac7bf3323a96ecafc708c86cb10b36a2bd4ed697cc48096a43a79c80fab1fed82c6bb50a364859a6f7dd51fd64a4132233c87055e378147bc983c4b993d90fd1798da113a3282a453208276ace388161b6e6a1ec374881f226b685045fd7759bdd5e023ccc9e23a47ae0aeb5f3cbf8c4d9c5b072448874bb10b3466c721373785e6470541882bd9694b21252fc360d96f759178acc534e1d462b4d5ebaa4afbc0ee9718c9d12d9dce525bed0f77ccf5e9bbc9fe097455db53c095349cc92cae3daced0f06f27d42bc89263673de10054b98d4a4cec7734e365123a6986ff74630b8d4dc81c9043faf82141fba04b1ec90f8563b156781ff0dd867e14a6c775f183621262d99b27a922c36f8e082159fe0598b2c12423772baa9e168404e6463e53f34eea497aad910b5b97b7966558ee481ab0d84f827f40f857cba038dad15492cf994a02809932d0761b67158fef208958fe16e1cbfd5fc8a78b57d1b0735f74f57528f1663411e0cbe497d393af917a14c283ea873eacc26e3edcf4f49273df59343a615269c52373333beabfc935b697929d202b5640c1f10454b2f77c53304644182f3fa3fecc4898cc3e3a965dbd55ddc3d928e796049f9a75eb51f078307ff4cb41a3c52633fd6b5ab9fb5947c886e7d8083bac708424f557c804666d6f86cc43c495106200012ac118751463523a7cb3a93d8fc62c7bf1276a477533e245db0d1697e678ced319614eb366f2aade2fd810f12076517916a13d17867bab2a85d2b6015a87daad8270d37b5c8ffd4a7fd4b537ad0bc2892d6e06d0ed40f63232ee8ca08719ec6321fe8a6480c75ea037ab2cfac6ce3ed339f77df8d24a17a9cbe0276961bf21e6a274c5f15d6368d70c8c34f805a4e07dd276c54c714a0f3ad40987e5cfef746e51dcb19967b4c2261e211df59584b69bf9e1bb48c87a4c3cbb4beb3cb8eb613c1a60082ea577bf5ff09a74370620462d48afa182a0481a5b25ea5cfdd6107c4892059bf298aca7e22de711fa05efc4d3484e3876774d0e527c298f6cff05409f330ea89a9f0a9d04cb97bc1bbb553a0bd237110ffd840d6aecbea02df4cb028f0d4851411b7c5f9b15324f2415b716bc19ee81299c9717ee5ae973d9105386201e7a583926c0565ade153251d74c57287ac44f990dc88623e20ed29d0de103577f8aa753bde5b7ba2dcfa61e8a4e263fb83615abbfc23340e19cc64e9458bc46a0f276d12b0441cc1104ca2da521e768d50148125b8207c32d802", "63636a", 0, 2071402406, 1537743641, "72f2cb02ef471988281185b6175de918e7c7c876cee818dbe1925dc45cd5860f"], + ["553d522b046cc2f75dedeb8cdfe8653e77e7a5a2c061cfb71eb51ee4be2f5d662903c0db1501000000055365006a51ffffffffab11751d4c3f70bc785f7f8c7223a560603f534afe60c63c8b293f17437a84f70000000000ffffffff9b61b322d7370c6049743b6770fb2d88d56fa41f97954ddd8379ce1642bc20ff000000000663ac5300516357422cda104ce580e1586b02600453043324bf13a1fe7941fea1266512f058e3155ea3ee03000000016aa8f696320296dd5c0400000000016a15e7fc01000000000465526aac000000000100000000000000000c1f9a050000000094d2b7dcb3cb25b55ed1933a82a7e75ee7302f1aebe90373ae294732483ecd9eeb7e0ba5d7e16033af34a364515b6875cfd4bac1c7fb30b4c75e80bab8850509f1c45c9f38a6f5f7a3505bcae9ea020aeb95c614cc6bdf3edaf4555d09c9882a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002ecbdbc6e6fc11031a4a2008fa9be7d61d382e135eeb739ec0bec1cafd407ad1f2f74c19b09089d108b474d11161d952b55629e44c8c6ebde0cf4e05e5c19273474447ea5df8226805bee2fd3a18802cc3c5f41f68cc4c70fb625c61a26fa039278542688555c46affb1ec91d4a5334948593a57c6e3f7fc866308e23724244031beae445b05b297c2339aa66862bcf7781b23ad1b312a1c0da57173a50d8e332022cfbf275484d7a83f5457c2f7de79a1713d88cc7536acd7d5152dd93ef4d09e40b00e560a05f9212ecc8aff66be967eb2deb16c74bd944e1926364b2cbd5aab43e5756e2bcdb7dc6e160ff7b574d44a3eef300dd8cf5a07f28022bf07ff89e27070320111ee8d9c7eb1e761164cd1a5087e4b2a5d186e267db58bdb3c0bdaf46b381032c7939ff16261c2940fc64bec0fef123d1800398452c435f59a7b55afd6f3504020480515893bc113cbd201fca55cbe5244689b252c1339665a94fc841ff1c638803248afa7cdf9337ecf5ca65422ca0da3a359ea17d49030c4fedee719d33fe5d9e03274f5e95bb889a5f5db6fa64b066e9bfa93bd3ca76b8b0b93538392252e8ae120872f1ae502f9734b4747c6a5141b3bc07cdc83e07347fdda1237dfe28c86117053306f8b068c006568d15d424d54587152da2d3fa80e4711728e63340dfb785304d62b77837f930c3d2f5054fc6138560eff3477e2dc1e0c29ad40eca6e178010d8a7f0bed71ab6b0d8eb629aaa613c929080a42fbab43904b8b51333845e73f078ff1c318e76792560e361e67861feed9a1394f8702cba913d371f88619ba894d48849ca92aa24c5c647a53f6d9af80783dddad0969905815d7bef3e619ab0f3203edf8d576fce44181d76a3f3ab0706961b92a3bb508c020e4d4fc434448bd3600cfe0bc9c35c0b2e9165eb1d95b1acc18c50319cff6c88368d7c8114bdfca9247206e76973e0aaf506344b8983188463a45235e400f8efff1dfc05e9e80080be68777151281f559cf5f4dc033ab4290260ba5c2d6d13dff151e27bfd9d3453a9dde8b808193373b6e276378d76b9651a217d381a93cd280adf53f7cc9c02b83ed4d46b6cc8d4ef7c792e64326b5534f4b3e6c7551aeadfd571989844841b312ac427186c69cee9b6c515e075f8fb71fa8be9f9544f11ee6fc0ef7e5d062e4477cb1ff81b17e97b7f366d8934eb3565277cea36a9ce9c9849d03b1a92bebd6a10c09e26762498e55f964cb51a0a83fcd78a9e8b64a80ed6b702368df51d9f6c9a9ecf553594ba273b64bc7351c2621afb70f4a09579d45df6d1231c0459b97b979ac574c1d10f4d318b1fc1375a8d0e7afafd6c435c35c49f4fdfc12ca80e5540b26080e1cf31de79933f004e77d3c9ec84b7254164ea67bc3877cdc0d3e344de54617f8557996cacf0c2c0ffea17bbf82e01457a41a8d41b40e0326f9582970919fbc8d0a4e0fc68e4666ae61f564d758eff3f1d3f6e48a53d9f1ff2d25521141e6fad786d411d28bfaca3cde6469b93a2bd834133d63ea9bb8d1f39f230174233527e14deb799be62d602e12f7578ddcefdb9da588d39998c60453a6981a004f0f3412d18d5436591cd96514fdaef014e6bad4c853e9e39ba392c1e82bc5ffb79f0848e0ff0f3295f6e510af5cb9caa9cdf00059339ca8ec4f6fcbddded1ed246cd20c177e399fa1a272d62a6fc0c7af3d8075393ee5f15b3a84c5e657f37613ac86da040f191dd8f6d5bd4c70779371b5f8241fdca3091ec4cdbbe5ed3edd2fdc767a212949cbe64926adc78795ba43f7138eb3b4b6025937105f0d4d70f859287395f79bff6dfb680ade0f72bd4c45caa2f343f5ce5797a3fc7f3cc98f0c0385ffddd6caadada18cb4033307c722add8ed984dafca0367daa489973f81d50afe74744f67f8eb158ac52a2cca4b68c465225063825ffe324fe9c8bba14a4833793e44b2b35ace837ce06e4bdcae0cf85e2655c8ac703f4e44609d4d46919342ec0a233e923659db5f47e4b49c268a3ed818f6d7adac2ca000dfe595e08da98385e12bb7621ab1bc02ddd46b01a8821c53341f3dfae8e181269f67ee3482cd4b57cc3a2213d0456c5baa7e11a1f8db1c6e9d67057f8353dc4c014fcabd85cc56bfa3e7f32b422dc618ba95351ef1821415545281cfa8f9732c185c2252222760e01161129db59b0fd55bd38ede71e6ce3ebbfe50ca27adbc17359617d49d216eac32519c6b8a0c979fee73c85450f3c24acaf86c9c293a963fb74d28bdfc4b4971331fa6647ea1d2448453304a6cacb98c2aee3983f88c5af99381f3260840633549e6e58db6b365ecb9bcee7a81f449445e2e903412e2a176fcdc40ee2444c208cde996b4f59d54e09066f40d904e1ad996c15fd51ffe6236f561a889516574787a2977759e1cf1193d4ac87191109", "52", 0, 389627320, 1537743641, "9cf4f22e6829300703f006d20c4100504fbce151444240fa504565777233447d"], + ["555e006e03348e6465bd736f1cc04d7abd6c0bd9a9945dd17e1bc02a5fae0b48c7f9b8c8fb030000000965536a526351ac51acffffffff9552b35092ab3294f3ca07629ecf6a9dfa4dffd9a005dcd2ed682b5660f033b50000000006536565655300ffffffff483f4c0de123c4582b60d26494bb0d5c02713072891e0be0ddb469c868ad836303000000095263516563636a52639fd62ad30106e6aa01000000000751630051656a000000000002791b01000000000000000000000000001b82b1e8e19a5fcc1441dd9bf65808a7d24ddec9c53a2e53f5e9be4cbc24c07726c662e6b0d7b71b77ba39bbb333a53d25e2e8f93bfc1bbc70ed43798ef50730a33751b467044f8b0a6b728d52fbcb8470ed4f73d72f041b9e889e60c366b47000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c7d8752945dd6c301249a0a2a7068c0e0782fd44215e0f4138addff052032012c929f5be6f3289c3f52b94609e3c6d825bc11827e59a35246a784e5344dc712ea63fe328ec949a61e88c6e706ebe591f300d0129c7d20f16330ca116d3cf426bc05ec46998d329a1b20c9096d5ed4e11e4a2c385fc6bfc873eafcb05cd5174550208b54e2aa26ed1375bd0662eeb3f468790e67e5be7bd690d8ba008c20e9559b102208dbd9c6cb31b15e3f0957d2871d47b40e7151698787873ef834ad3ceae03310a083fca660f0344e6e696a1961cd859711de934b6f19b21faad145a506b93f4a3f6a9bd2c7be043b2418f89f2b36813accc4d571b646437250f283a27f5fc63a3030c1f23bd1ecf9c58ad7bd903c4dc2787e1f76430a63ec1d8da26cc0fd9f6de240222e2a00d03059ad65d7c35a4720c474793a8cc0ca53a645928c70f3df65a351e0314a00bbb4411d8a31c1b192a160532c1b9dc09f7adab40084e661f292e3a7705030d8e32a6a2d579beb74ea66be41ed4845567cc798eda64066192bafd5732aa5d03071e7ef36521558901b69d379e19323840e28eadddfb9e5485c588ba26cd1a32cafc1b91adbe8df95de5e16361be5a4a146f4d02f282811eab4a682ba9d2432a20b224e1ca03f7d1c24d544e0cd8a237173ad03fe8d38235605777a1ce9faeb68a9b1521b9d48d8fbdd3e73280b3b4fdc73f50b9a72a1100f161d96cfc34da24c29f65776b1c4aee48081a863d10545b1c63ba87b9524496744cd58bafc28f2bc1f772c75773b56c5d100e3b1c3ceb6ebf4d0337e9493a9f973b5fc74bf35e99152bfe7076220d0204970958e726cc80227571f5c8e45944ddd9de33bfb7119699c32395926ae4d303b0b9b2a53ea3473403558ad8320b34ce82d1d61bb4aee64ed71fb4af669bdce71939758c8a9ecd4c0cc4bc745cc01bea598c06d62abac982a4b146e442b686eb028d621532d1d50431b67bd7e1b12ba4636e7ac37111301de3afa4496d777c3c120d3d523619173c232de0b420a4990c3cb5b66c57281710ed160bb33c53758a550e38b809742cf8c76e060b8b7d66f7896eac4df0113554b18ad420e5a9c5f9b1d0a0387291febb25b863da46d612bfce7e419dcfe1143ac0b7bca99543bf016ba05ff0cd591c3606afe51268fa9eb38ccaf6b79d7d096db67bbb283d6b2f7da45c4d497b09300ebdd08c44070a09107992032e27e5ea0527438022c97c131f77d7abc41e6eb85396c260954b67bed60493a9a75276110335161887a117206cfd7c96e76fc115a0e27885311b538f3e354df772039d14058fcccdb1de8c1a38253ecb527370b9b0dd36b4669f1e3c1a517a821ce73b63342c0f529df7cca6bfd53bcfe16d3598775809eaf08dc2c632f4f2a3dda88f8d2343bf330c0032ae51c0aaea7099976cfdd040b4647547e53e021ffe6fcb5dd7c32263247642c3d1028f3339438205877ddc6816e29e6f780efeca868ad04de883869bdc07996e136a0695c029b75cd73a0d1e75d8c7caf4ab0995041cc46c95aaa6db7119f0da30b3c5eb2d527caf754120255d33708d344877f73ead498de4759cbf3cfb655de36869dc864cc901970e0b36c4b6fd8d7b6f643836e515839f3cb09b235de9001bdea57e1ee442b7f02d7ebf0dec891a945b2cd710ede483ee3e6484df49677b4641fefc3ca53a9be3cfab3d4c43e020c1d6cdf3a314178f6b6c5f279d6649b253ee0eae6e5f0593c2790307f6417a56b127bd2c207fc2443e0481e5ed15a6676b0e0bc26281032d64eb3ddcaee120e9094f38cc1368fc108b0aa7f8f8151da43b238261dfdcd560c0a9248bad461c0766b75eff8d8dd8e1852e25419fbb5405a4842e4f1f0eceb3d630593466c0a606930e540c9501d1184b0e2aeeffe4bc7abcf31281f140f88bf4869f299439e3e628fb77ce1a53bf397fa551fe9e7fa626946ff608e9c1a2227da5530a16420bf901ecfa51736d9b71860b45985e8830f333b8f937948a738088b3bd0e4f8e158588be71891a3b502cd095dbb79e2ad06fb0f0b57a7ed00855116bca259766a66e68a2255e56257ccba0a080ff000411ae0d6e8207e9b0807b6c37a905de4d626be4201be124e40e405438139a9a1f5083b2b359501fa3436f2335e5d7e0a77b47263b6aee5788e15ab2510df2e442b2c5b5eec93a707c5ba8e34503b596da236e4e632d8d0006e8b29441dc775118aeac5613961f3b78c944a09170b748af111e2362cf1e154fe3f3d980d8862f8543ba7a2965b941f2f47280844f168f7a010000000000000000000000008bea9da1e5eebc8eab56f6cb089beaea072d6089198fa63d18c3d10c5cca38e7d1b3b0aea00b7a261815f80d5002a88f2cd950a36143e95fda1aa7b36eb3d5186e3097cb40f13d7461cd400ae66cdb3ad50fa5442531b866c4afcfd398b56b6c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000087d1753752845fab5810f37590162cbe81517af33f18c01776b033ca7fb4f1b801ca2135550636b347e76ac8e08d50ef5159a65e4551f8f941d49bf364214b802328dc019ff0c46b8c8805367d93c38906576950ccdee8ae7a457fe44a20dde1e8b41c712e0dc5ce94ffb44d804e159e75949121a9c31df4968c79cc0af3270c0227047c493d35a8526a8c04a76a3f420739104b4b53803fd63e07d474cedcec660220a604677a89008c469d860df4bd1acb639ff0224b18ba6965fc5e5230ddbf410a06d5e13d7d64b170d83ab7df3cafbc8c19b75528aa6b975b618a84eae377f9e67c804f5677f74206d2257fa1124e0270c69b9288545b8eeaee1d49c74dc06d2d032cd912a059d539eafa646b267dbe4ed9565ceeb1bafb7b697ffb1db3cfc0897903099c54d0bcb8e1ea2d83eccf4d2fb16cc286ec39a093cb0006a58e6de0526ff50317ba8cfa09c7d408df9ca14eef70678964c1b3d74a29dbb5715da73addf9cab3030627d8c6bacfa5b0b20fe7af5356daf3d7235f2d2af1ff5c4bb1f0a376873c1e020e3f478494cdf567d1665ed1588a8a607d7925fc3eea0504fb928e1f107560dd6be4a864b58246cd4f7b228d8eefc15b22df4bfa8e0a7141a1647e75681432cfc47e3e47c2aedce66b41d78ff2e97a38f46e9b71b60f16679ff6feb77707ca2cd1da75181a63db09f09d6eca5c96dd7a1aff9757c018dd39b6e04944b800dbfbd46d677c4ded1c0965ec071248a1b6c669f7bdab04ce575422088009c26a4dfb0fa861a9b87c19a78356c43230138e7d9ba28436a539fba66a33cd3236dc42c4d3218aa1588c8e1f04eee336c3f8157d6229cf271fed56fdd3405af9530bc8a825e28b8d8a0db06b160202c66b2d578e66144777f087ee0dbb2a95b4b861822f1fe9dc41a05c717ca16a1ad3e1a3e137934bc70a53102788863a3d371491ccf28aee1851bbb31baef89a785a2752b712418c1f995b42bbeb6fa9b1ba09ff4aa3729a0bb3995747a6d09c0f6cc300a143f1088761a81caf0772b921222c8d65f90f67d2430754a68ecbf527d011dd6770387f980dc3c7dacd28e3847fdbb35fbbf2106cca48fea63115a54aa64da4c5852ff859e9433ed177d4ad9b39a6008b02ec85979b122554d0449ba9da9ee9083e97990b20c19818fb64bdd62f01c072d7314d4f53bc232b1ba7131701985c9e759f13141a4863bfa3eb92201b9d40ca49b6c09862f0f7fd258dfd5047af517b76df4d7dda05a514f7490de5aab487f7de463eda1a971e3c2028db35ea9933175cebae6014838493d7d11f792b2acdfd899fbbd57a8eadf21d19cc6cda3aa90c12766024d818294366edd9e937407719352294ecab3d521b04c2098c14eb910019691a5df22e6b10bbd6dc471bd1d75debfbd673a12752fc212bb64fcd5c8a4a92e8f54f5c4cc5dc3ceec38ed007e77e9a528b7227f81756dd98babaafb625534e33f09c5084e77ffe961206d4d6bc415ade2d78b7b263ac0ce52927116aecc535beee79111adb37d4934f04a11dab07cad7b8492b9bef68089ea2c9da8360fa82837cba2b102d1dae3f44b726a7012b52679cecffef5ea22ca268a27305109a9d86baf4174da6d87bb4db5910eb07dcba00d606fa5165493d06af93725c5e1d4869a33ea22bc2b55c751353b8044566c16abc5e57137b2b2823e3d98ae4824069b03f5e5c71886f22f7440649874cc29aeae0526e71608bbabbe081d274144c8f25a29474791f7abc614af7e3b62c448e73e1b24a9e703010f2b27070885ac7fc1f8ad8feb2f4bce8ed015d06bdfd8072f553c47ac1de11d4f62d2c4fed8f2c570876c5145444b2f3240f285cbf342fb4a1fc2f2071d12ef1a7e8d9b3f356cf54be4339b7756afc54016aec6dcde7e472999a7f7ef35163ac9961bce128b83e9aa0e9b05b73bdb60adc4576e7a3282ed31fd5c3cebdc438164987afd327350d526cb1faab15bcea7571dc7bf7e6c6f7b222aa80ade021d6967f3147e3406dea7a734b265d13e070d0da0bb61fc42f1081da1174a2dfc088e29372bec587bc308d9fc7331be6d0226f398f6713aa9459ae56ec88c4955dfec06d42bf59ec219fb25c3af9747d563f56d257946d1950b51ed5043aa86523a70947b1ba0e0e0e3cfc9f0bcafc54382048e292688777dddd155fda5786a6c433a6a0117f6199e41afc190729a4722f0268f50399ef2642ae0da958b07dfdbf6db51c9710afaa30abd3bd59ebc85a915f79c52fc054ebcb498e0fd7ada6120a423d0338c90f96cb7565d7872529990ea035f513910e584d2a9825602471b674bab87e4fa8acb1aa0d14569fb0897fe20ed0a54e2434848807bfae161842f01e4743b3236d354f09f81bba947c7be740f937bd2699d0ff3e73161e3859bb6d1dcfdb0e353f1c3d2fbf663d05", "ac", 1, -1005560772, 1537743641, "0d5e0b02a9c23e1aa5cc09570ff5c0b8dcc8df80ac73660359e85bb334720842"], + ["030000807082c4030408402072a9d2ebf6ca8e704e795c435b70d1f79d51a7446fa7571b8c95eb038301000000040052005187267db2d3e487efa704640d84aa788f2019781276fc50b0c6a2bf41b28a617ade7c2dc8030000000165cca66f22ddc665d32a7c37309aa77ad47f40b189a521b33acc88977007c5f898efbe32a90000000005635253ac00e8563e42f16e76b700c1f5220e2603f64b7d0c46941db778420378dbc0c3f992301045a50000000000b07dafeb012d4d0d030000000004ac6a535200000000000000000228ae1700000000000000000000000000ae5917d4e76db29cd980495c34f7c311277b3e01be985dae56bf89f33b98de5c509e3a4dd386e56ba2f2aca95504b11cac3b2183beadc53ec688bb7860d7c561403fab5813c56341de020dcfd4a5ed9dd82a926ebe477d88e3e3bc1db553a3060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037acb81ade6100bb29c389cc0be68673f191405374eb676f86212bc4384a053d69db6173ae4bd44484e1669d638089341778acb05eed0f79a08635acf17703b77d663ad9be017fe04a2865c9722d6e562384e564d90092499593d9a0200000216b9cedef3b0b49f66b0576b9920b2537d3a3546dd3b913764ac8ef1acdf6a6bb032bb09dbe4ae43921bcc74f872709f26ea43d862e61f2ed7f7718f4e5bfec31f5032e2118a1ec0db4c8453412d47efc3141e23db3102a3da672c289061ffca1f7720b0294ee9e1e854c418d94e36f182b729af30e566756a575206fd3d0f2f420cff30e2ba3c604169ee172ef8acbf53f37fd87cc84d745fe48aa3662820fd372bbe10228f8529bcf876d925a1bee24738b493d24b3943f6337e19497173e575385f92b03002ffef2e3464bff65f89534d91bc52ac4cb2ef535661faf66e02442af227438021bdd2df508f4979c84919bad90b465a6c56913913bcf2490f695ed041aef44a602119cb7feb2710e50ed12b3a3033e4507ecc3e757f692a83690f9a322849aebaf032002678947be4e322638e627832badea199502319a780ae7b043f5c7d92602201b70864571c04bb7d82698ed9e71d5b5559ece47d0367ebfff34a14b55dfd12e8c4ea51b6ab4cefbe5f7869e835d6651cad497c0a4cc7f0e9d10c8c1a71e6ef35a2da12645004dae32f0d059e3dad3951d9fea9ec22539afe4a44d08e700d17e3f13dc98af6fc32243a8a967a3d86fc23ca8e7cd5b240a6309efa72816c57e99412b470a33826280dbe18836cd3cf425d786835be1e3a877c88e5179c94d28675f8caa9656aa0e584dcb3ffd7e0cbeaade8c261371f36b7a2c34d4135d36905eeb9cd48da2306319e6d0c13715b08533b64021c89b1a5f0d49141aefd15a4867d04fcbba798844f0755ad9420bdf378eadfdd206c8824fd94f5081fcdc6664edf426f548da7c89e6ef0221e621b5c008486e47cd9db44df5d8d6975958e60cf3db0f898aee7a2b3d4656dcb0f16832fbdb2d789ab8e9ac9f577793c12c1ad64b9bd29cabe182a83440dab1923e40adf11dc636dba605489d50c1030b238b0d6315e56c7312515161f2eff86d76b86c5bd43ae8b991ef01ad9405c0cd135dcc0ea3fcb7ab011f8c82918b160e37d25ed8976e6c56f810d236153aba2d67b33937e4cc767524ef036405ae8814708af44c1f2b0a95e326342456aae18730fa4da64353e734ee12c3e127e9732665cff88abdef4d507d96373c35673837451de27c63fce82304b3633679f8b2a2bb4c7b894a0cf3bee38e8231b56fabafadbc8b750dd18b9c75fc1b7322c15c9b20519e8db606f601c0c08d0eff58f92a046534ee495e9607eeae1ae3a3423667d5c5138e0735c91e6054c87a63a224c9f86240dcb1b58c8e2b8e5da4184d94e1edbb5fb6b4900d914729cc58f974b9fa50c45f81d9ad3366f6702130144b4829436a71b1db1a81a41eb7e8b45296c7d0ddcf6aff65cb5729f28d49c60e07158366c472acdda457a024070bbe9bd23499d6b839bcfbbc59bb27a76a839691f130768295fec23d6dee106b122e97194e02a3ec2dd1fa74df62de8220fac6df39f494bb8c1b902d4437c6fc95b62611d66e33dcf1413ab4e85b73a65b36328f2580f971bac1f2af60a35a87378863549dd21925583a8a487b14c71f4288d56d8b5d384384b818dde26d36b3ec77bce32082403781f93116336b8fb0415cbef474711c927388a55acb3c693fd12b540314aabd485a1cba97370db4a8cedbfcca3a7fac99bf36019e2420fdd5399bb1e9b5012fb748256b3ad5b80c3cb1a7b169c29d0d0178a2dfe4726de4689f55feb89e0622d5d19239e8af25b0665171f6d97b3542be1509ad410615824fd244a988f68989d8f073094608b232e387f2674248652b5c650d24a8caca91131f27f2e0eba6658051533f71cd11870f3c84d863808cf4a6e631f73c6d1367a9119ba17e3fefc1bcfb2c31b10ff2dd431b0a780245d27e2fa650ef2bfe1673f0b88319cbbcd11f5f5b1c044b719de96e012ca5e590aac1b3c9111f242414176e9a0a38837047714668133f272eec12f42923c72753892ee28f54b89a5bce7019084218168efee298b37e25c587617e10d6a506c8d0bf5e18c587e26d7f716e2f087f98233abbb70bcb11fd60a773510d2837ad6b7c497e93f02ba2c2b599961a0c7d9c5241e197515467d67e73ba44923da43907ae600e538e7e8185fc94ac38ebcbd60991cd20c95743722f9f9d8b6bd6765c859e16bd83dc719f91e78b3d02000000000000000000000000cb0c85a8afaa9844b8f9b62db4dc8b971aeed12e03026afbe0a5c4801821ac502cdc95e7bed6a8f7e02e7eb744c3a800cdf4fcb2e63ab5e74941335d026b2d86bceceb77fce18d7c643fcb6039ae02654ff78cd8aabecae54426ea48cc032dd900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eba640e740704bd0bc8b1f3169262934e11253590055ae0ce796ea5b879b49bed8b0a52a006b3a829e39b60d0175007b49818c9b0be1aa748074747da296e7fc36b68667c8718ba7fb8100299f9b126b46c30a6861e59d3e820a95e423cbc52bab3346c15f171e2868aef8d0b1fcaf391e3ea8bd26976898a6781534fd5b8e2d031536e0431a94c018e79d891e510cbd35c1641dc16da57b02e088742ae8da84290206348ba6941ae9e4e3878c71129e441553ba0722dd60527df9abe1713639f6d70b08aef0f513deac16d436cccced8f44d58e6c9448508abb1a269230f25e997e74ec2090c9555ffd51697afcf5b83b2e691182945e9e4e00d481129114a137bcda0226f177d29421c583471da3b4fb93df0ef1821d543bbae6c3bad6e03b341677c8031c41e3979502a475ce79aacc2c858e2fbdfb3aebf67e2b838677004334a5a5050328892176a42a1c71f48a3fdf5492070be9beded8315a0df76c91ee067a915138021749461bb7e4f1019b61899dfa524b4dc0343a799381bb40342ed34486b0ad7c0308ef4423b7ea636a306e36ca31ddb167ff315121171bef2790b0c7f6767b8eaf24d7998e8220605fccec7acfb2ecb28b91fb1dad75c18e83b666c264b4ac0557f7e82a2a18412100fb11aa6868fe969a54d251b8b6d571126a64fafd8a0b5c78901f0827d0a3fc55fdc23136967f233c1d366939a1676c9a64d4c9c76e5283837e797d018da54f1b2f599f8b4be9b75cc134bc0e6ca22c596d324bfc3d7fa316e38980e712cac8859aefec82de471f29e28ef707266d2b5407e3507c8d5b1a982ee7ae000a8f90fedd4926fc2dce6e06100eec4304c192e124332eb89594e8306382eee4423d345b89e65b304daac21c224e777fa392809a8d86d8798b7e7a8baab63436c32a381ceef6ead6b978e3d412d134064e76f8a6b530d252514651dda077f1822f14f7d7a0deef27ea7a30ef819feaaf3e614e1ed7b6b67f9f5e70f16840028a9a235a61de82d669210b6685bdbe6fce056f25790bcdbe8ef9634bec73568653af778858f0e180798709fae241bf2a442b3181a52d0dde6ddd49bfe0bb40f99034f965d1a08b112b663295f8e38d8c3022012eeb3309dc52d278950e2ad967fc1237f045258d164ea576900d7ba3dec328fffb1e5f9d35acfd6a74504d63fd69e7aad5e72899c7b19066bfd4c1141b81bf9e6b50be4eb20d794480097d7c30664117e42f79520f7171ecaa80572b56287ae5f88f5a48ad9521056d3e63271eeb91246a2d3ede7dc5076f1c886cc3968c6da6f56ad86909eca239652ee6f06fe09b5a77cf9f1e50d67266121faef9160be213a949989185fb9cd0822a0312d45f9e95040f4aaa75ac90e6049d68117f4e3ed71e56e889de84aa16032db09607ba0c8099cffca2727f1e9455b95c176178310c8aea578c658514a2ce5e88cb54e528231b1593caae8f5ec5431ede25e7a290c98522f03aebaf37da4dd1e075c62248c128cf5c75ec56a5e4fefdc196d0a37609f9eaa6d628ac9b930e8131132f9e51ef33c6afd2a267554ba42ebf478c2ef66206017b4b3d93d2ccf17e628bc79f59721586113f3abbf2f28d3856235324014c71c347ac366cfc58c053bc5d1d4871310b452414b90fb24a24281c678142fb2214b9e0a06818ae98e88b7ae519ef7a06cd256e360bd2a406855013f561e4cc31aa313a6a41bff81920473d4a7145e946b1f26eb9849ff6dd835f985c9b9ce88c243c6eb5604707cb4d2b46e9e7c36f9552c87de54ba9c6eb8bc382f6c1da32d06f2a5819ee3bd1293fa294409099b10f305abffe9caf9d5d9436ed9c27a45fa4b6e5cd61c2978abc243a3e496f1c449ef54abeee420d57d101f897d794057687e1724025b92a5bf9949096a05913040d1d9d837a3541b89092b23e84a3618c297ad8c81f106de833671de9905037e8bb25c086e4f54824c88525e3160c7241692c8042047bf3e71265b341c74da14493cce0aa39a494e97b43d46c16247c56ee46bba52ef154f00de20a80023f12f5d58ef27a0288c9e0e2c47faf066e3684aea861306e87444e5a6838d35cbd50d246a35bd43fa90baf20f09a549efc56379d41050e048fde3b491adee61e28405b8b31cd1e35b7eead93552647521d94db2996e78679bdbed3dcfe33140320b980d9bc6c89d994e9558954d23fa763337f7d79556b62d13f738dffb3636a57e941a001491339c854e600e9bb42db9342e8b5dd8dd1d7f80f0d6b0858375f91cea06d3fc8dc49a61563356d9cab30b9620d611729c51bd5cb38038e43c91ae619854516f028c053a9050fed567eb1a8803b093c115838a1d5630d403fc90dde5b0136cfc1b21635d27294418e77106ba4e84f9c0cb4771bb4ace34d6ac53c1f95f72fc66f6a6db9815ede92cef60a", "ac0053", 0, -1903954703, 0, "8c2127b3ee43738181ef4c4cff3e2521427f60f176f8ac8255165dfa88f0f665"], + ["030000807082c40301c4991b102288778bdbdbedf423274f8d47203962842681fc87f5f1a181df64c50000000006ac65acac526affffffff02839779010000000005516a53ac51d71bf40100000000066a5365636365a23571beb53934f6010000000000000000756d5b0100000000f62938816000aa14a3e2d975b5640b5ddd47321b3de2bf83a9a4d6dd2243b0e852a116f1982acc41efbcb8a7552723f5c56be6ac22d3f026e6869212eda73f0ce4314fc9d14a91a346da81678b30c68b81964e79b7a0dd6265a974d713a3e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009800d48f83cc7d95b8f4f8757e7aa6f0a9059fc7314d5ed81f29bfb77e15c9929a572e1b5305fcf007fa76226c4c99febadfef244f336921294c2ab46869ef868a1a837b3078947e34746670abe6792ceea2021dc8ccca4b2f207d8daa45e70bd81341cd029a66cd84bf7a965039fc6d60015abc7b7ba5711104d20de51a090032a334fb532ca72eb4e35e769e757b1e0cdaff571d8ab7b9a06ae4ffbbf1cb97403274bbf79d5546ea1806396eb3c7c8d99757c5662fb2befd5fbe8a33d6d665a0f0a07ed55ca233641be19a5ab5de69f0033850c3ba7c2fa2c460e48af287e66245cbd335cab7f2d984aac5068d557e890554f4f98a57221dd919f85f445725e49c4020366deb89d3d5792b76c99bfac32c3e96f7436ab66ed31ec9105f9b4085a1bdf032d9f45ba9c65b8272c6687cc3d179de60d6619814c0931b5b3a91bda32c4b7d8032bff49a62f6427b2c3743bf04c6768ff1e25d27a16510c93503ff732dfda1e3a031c7844a542a688b049e006e8fd93e6faf025b0f34aaec76bb264a2afd4837291020ab13480c75ca27ed9b7e80e6389bf9478f70fb6c81ed014fb3e7114419f374c39bce8133b38b50c241300751f72faeb87826ce701fce5e214df9fc725922580f8f2701329f4d9e54bf86409ea6b65e1f8d71433c493b6c5fd4fb3a8e5e6cc4bb7fff9ad6e09d2080ff8b22d25faab774a35a7484535c87288d35e823d963b29e4502ccc3509c62af988604a9551ffb49ab5d1f9e9206635792ac41d275a790eac95c16d3126620ab66a7f85190dd3073a09488cebab30ae451d917462b138a2df1ef575da0dee4418ae6767e1de47d612e3c92b48be0514169f0e17b55d553bd35ef55f0ed8b70816f80d108b48622572978f438ab39d6a8ec7406a3b9ec25f870f56d9d488e7c335b5bcccd5477d1dd57163443dea611f8e330d592999f5ba184888f4d5291871790558354053410ee7ee43ed72fb44ee5c45a9decc6c67aa284eab5cb9924ac822baced87dcc20cd1d5d8f01931a529744b1ddac26683b05825b193750572833f46c178ad74cab6b72aef7dd46e7df4786ae8a409a0c34292cb177f4fde172b46fcb01dd174103367840c9c194d0635650f15c05c90085f5e016bd822c51b168403573371f25bee6495daee6aebe1f90b42b28302bccb081c3efa7cd815ae8fa42db63a0d24b3f2b1e441078dc916ca00141ae3795d975bcaa2e66ee9670d728f33aabe4587a918d94fe9dacad320f1d619c33e2dcf9369fa3ef6ad4edb25bc797d888d91339f9d5af1258045289030cdfe1e33b3708a3fb435ce115d909fd515701320529ccfd35610562fc521999046c7e3061870f815f968b53d071ea1d013d6ead7890179e658cebc26cbd056cd61c8ff51b38667362430c77bedc14f2381fde331243cb529c54c92a4aacd304e08a349ca5dce6b184793305e523210079bbf842df1588df66dfb6b3adf496da4ca6d8292978611ce8aa8915ecdceb78a58fbedc82e3540d042fb091e88a7be0d30ebcfdb54cdc3e869bac325de86238faee08185a2936d6e6620ad93203f4d8c855536ac0a8a343bb662233d24bf4defeb16bfe80ad0285610edf72bfd294d5dcd5814293e656eba7c5c52e0882b0ca59422021a5daa039d2017f303f04b80438923fe1bedfc1166dcf5491fe9b733aaf0311147a49da324e2c4bb6bc8c7f8258186400a835fd458ff690b34a2a953aba8f42f5c6aa2824983bd42398a13180a9a2ed508c8e43bfee6ecaad4077937ac5e18830a404efba66443e270e686b50625a0a978f8f594d1f8c89dbebcae8f6f2617e40755d9df0d1d17ef2bac7728fd5b8c1e8f93555591d9afb868fc8d586c667e6d4204eea3eed0b783277767d08c6df89f64a07fb78af7cb47c4eb4b877df5f92a1597ddd0188d2815b3c2c2a00bc8b8c8c17450901d52f2c3dbbf8ac09500d0535f7eb5d9479df1f60974b5e8ea9dde860646a43e0fa1f5e4080c159eaa1f9b702d6024342e79e39eeb9cf519e5f861ce651b35cb4be187a81d0147157f2a2562f6dadfa36aa775dfde5cece343807ecc591e8a6f25a1b17839b02c702f3f036e3e292c3ec5818e4655078044955ae9378f5b358c693dc25387bbe21c5bcef59c3b1dd4b2915817c820a7efc12d4804f4060dad5ca91460a6795377ed2c20fd216008755e5b96965190110080756812297582b27cee6f93d0baeb4447ff3bcad75df29b397f51717e625a196dd41b48c461dcfd18170274a020471ea9908a4d6c62f5547ccd97453b654fbf70628708ddb2e98371f5c853b299b7b7d504ac43853db8518dad6628cc333faaaef697d5b9786dba7b1f0c01ba39c8781b7051e1327a8d69ae1c1e810d32cca59f3e65c1497b15ae18577184066bbbca20ff02986a8805f92aa258107", "", 0, 709561226, 0, "fac2dec401fdcbaa67aea35f97bf575195e0445648537b935a226d941e9c766e"], + ["82dabd07048304632d14f3ed51557c59b4ef3f89a5178ba793b667ebd27fc189d301d73b2603000000056500ac6a51ffffffff3f2932ff5f3a296fabc21f7ded3f912be1cd5404c136a389001c45808e735e3f0200000005526aac5165fffffffffede4032921eebc8239eab11817cd0dc2e2318e8e6d466df19f488a940315cd90300000000ffffffff3b545f52b8b0cef665228ed23e8b9e926e75960710295750ef372d4ecd64aaba03000000002b01f3f30412f8630200000000015182ee760500000000056a525252aca5087900000000000353516a1b1b97000000000006ac5263536aac0000000003000000000000000007eca40100000000db488ed07c2a70d3cdc5462d9e5cd2a6750dd25ac5edff13078a961b97a241771a8f585422c843dc307806e7b844f68cb62bb867c92e2c6785539614080dd32d494244b6ed92ff5a17fa48d171b411135cd95014582ebb5da4d59c0ecce29ea3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001dd593806f0248201a3d301241f43497f8b1debfe6d08a1959ddd31082d1265f34df90b50af872ab26e3cc7f0eeefe4c98fb1cf15bdc46a8555aef533f3437dd6fcb4e6eb71dced25ece07eda51304a2584a2b97abe547f4545f0d955279fad85d5fdfd04608569b51792b71b92eb267ae5ed0ec4b582e3ba73e0769465d7340020a20ada02341b21ad7235b7acee3b25224ecfb80e751b1b08355b179330e7989022c0af29a310cf531736b2505254f7d7ba8c0c3433f811932324a6c0a283268460a02f8df21c2ce500b08ed247ea3b5349e6bf2d97d4733677e232846bd06b5c055b432a6ccb3d87bcd75375c62e00b92b3482b4abbbe395851d546264c2043841f0207aa16edbe0f000b86a18c308ad0b5c5b0d3310764f9f1f8845b2580d08314440229a8bcd67e780067649ba72515625bcded0190743142f427e1dc7bd81d9b24b9031aed4c18c01ee8376aa80afcb4337a33291a0e29ea22bcbe4f0d1d37a94be11a0210571fbd54d1cdaad5ead36fa9bc025a587f95980d3db6a12210a3563a088c9503285acd281adc97e3565ae6dcc557843f4312bc8740f201ccdeabc9b3a785bdbcedff4308da59f3428f6715842e08da5c3ac75d46cffb1886435e804270c4de26afaba93d5b8d76737db0fb29373c92b11e972e4a3ce6a38ec60da5da9ef45f523d04a788df4ea7beaad3ab6318bc1cc9ca39dbc8449d3747d3460ebc9d4e11df21ecf94039e6af28539e47e45e2c994e933efbc01b8ec214879c485cd3d679ad06f4d03964beb0ac1eed1105e8a0f4dbdfe5d9a09a589f585ddaa3bd4ee442bb81afbbf2625fd7d4adc38b7755f98e80abd8ce0ced62c124a8a6c5d9d22faeaed0839adf715bee06022193c9e87ac6a9772588097857a88528ec67bff36d3701d08e7013b0992d9e9abd33b8ada5140887ef2e154df63f174de41ecb87eb506f07d1d8dda76f1c5f0657c5bb4320112cb08ed88c170e92d66bef3ddac8f54df1e725c434f72db4f6ba958d130dfafc1f09b4873174e8f084ca94a9ec76c382aefa05b274e31c5ee09ebc593d01f7838b89a5632adb673fe52c59faf75043045dd12eeb80861c3f1895ba3c50e2d91416707708acb6e1c940005eb2e5e972f975c54528804f1cde02472a564e8392d9f2955cbf7394ea4d0136809e322df2caba928dd8f6cba0c956c7fb3cb46f507efd0b89c38248379b1d117345e3dde3b5c41198e794d60ad6fff2a51923ed52c77270f9418a9e2b7c5acee424047a4b3185a52049e354ed30110b1024f335fd4a6abbca648832afa43d52a9f9e45eccbca46c2644c8743345073ece35a32a5229c94dab069aa73226bcd3af72e149e23be23d720f7469f0d6ce21a97cf7378e0d019f1f30c14ac940e631682bfa6712f428cdacd80ca33af18bd791d02c0e29e0b7ba55e0522ec79217a7a48284dbc94284f271a917dda6e75faeae7d3873814a0a89734c5c842aee24f7619b87cda64ae3560abbf59c776bffa309a09492fc12cd197e992c13a1eb6cd5d14e6a4bd8b1581b8615d7df4227ece762fc1cdcb3fc96efc9552fefd6584d469e290c8184bc49d2d2e4e4ee13792af953720f0ae75702ecc77d937bdc50a7dd050f0072c30b611c69a7a8a2152dd74dfd1ea90afbc6df837461fef6f2f05d31dfee7f252fe46f403222bfacec56f9a66420f101d1b57d337e4f8f60b40454a93d664dbae9c213cbe1da5f23919e5e2f9082acda466392cb6fc1f4bfeba8bf988792e3902a0049abcea01435a083c6257899458875b2d0ce44932c7d59f0c7f23dbd21ec61c4225c3a7e7c3bfaf1a5a934dfb6853e077adae71bebfb37d25ed13db9e2a7fc0c25abe4c20775f68b1e30e7869f047bf54ff3abb19d330f7f775c8e2d1c93b60f4cdff178018f9c989278b15b0eca061a45ebb81075cca761cfc6abcb11c1aee63b3a449ccfc27c5bb99533d70b42d836a728dddf69cef9b27465d2c28d0b749999333d81de4b10dec597bcd3525239917a692e6ba8431a8796d64d1504b0172d0b71bf21ed50bb902b0abd4ab76364e1bb818c368553a62083ef18a2b170a681d69a64c4d3c9e54796b811306d932065a27eda3d14f80b4eadaa859c4178265781e26357fddab1f6ce3376f497b06c939395f1a40e18d291a380522a3c461a955ad462d30b0cc22a9d121e48fe1c83286eb1aa36ced9e55b00cb85f986a6b1f7192aa5920c51d4c2eb23d4a736a9fc14ce1de5412f25d7fabcda835f99720f48d8150841ee089c68c18c2f7427957c6cac10be00000000000000001f27470400000000113df2714b44c0f07bcc7ccda87638af7d7768f4e93d38b7d86d24f9c21e26da3916ce1f6d15ab75c34afdf51f832bfcde63cae7197970f70f1176ccc42070c342a2959c894a6ef89b028ebf450a93800aaab56642330e5b9e5c9e588f54764100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab2afabf5a0746a81cd446bdcbd7c0beaf8ceafd87448ea683569d5f239a7a6c07dd4f6318d4972b4b0e17cd2578159b0023adc0beab847fc393bf3fae53c0e4e3f20cbbef60712bd29ebf22fdc031334d4f3b58ac5bba17ef4319693ce8ddb6041073ea1b9cd63e1c96420b35bdb5a1dd88ab51b7308c4e724d6ac49fc0f6c9030c5ce87ea0cb7534bc8929da31904fce03d151349ad0edf65f85dfe060ec3a47031a635f58defd5fecd29d220238418a7c1605da76dc9a2064878599262058ae200a06d49215a82648c14b789572bc9b4430b3df3f06ac3b116246812f91d9628358532d4ed0e0d905415fec29b60216430169e6fa2a8bd9b654a4aca0d2b094a54f0207a09f96ed59abd7c5506e2540600ef16d562c8d9bcd91e5f919ffff84117f43021e2cacb817bd6f7415af49288aadaa076af9c547eabe6573e40520214fb4abdd0319c539674127239f46885cd10a7764eeb7b8dfd75d15946dbfc7a4c18a16e8aa031bf01fe1a1189c700032c31da11f30395e4bea6f52e59e8a5f3b120796430dfc0202f8c56e75d1cbacc8fca186c78e0f40a13ee061374b42a02666a34c858d40a9e59df991b8feef51fbaa1846ccacc0bab958e1aa03a1e63e7b8d4fdc6efd2a53f0078e4c39c9f81beff0bfe9b73af6edf1520981701c1b9e7b8911fb6ac78c96c22a0f041d846944dc39f4a2868dee175393a2613dc57abbe912beb8b22aa279c32f9e75dfb09670c12a84e97d4a9ba0c8e8a1d9ea5785bb31038350f44826a1441794b3d81afe8f8cc1c473327450aff1e930eceab8de2cbcdf4e6ad40516ced3401b7110d3f57534607c50fd6055a386b2a2e5e67429881d6540f3abcf783074ba678eb90983fff841c25e132a355c7d283da3c5e86ab0e6a5d43c43600c57f37a43f4bc7b70798a080a18075ad7ec9be80574d8540a3316767009bf39f73162044ce129324e2063abcf1dd9b05d3318071fad667aef5f8429c6e3fc5975b3513840f2d2811bb285e5916fc81809a7b93be5fbe7e7acbe4933cd7f09df627e25004a2b002c65b33329103ac898fa654be7f08ad9b35130c923648a4d16605c01e2e0965a9ff05cde9b01cf45412df85369571a1030f18bb7b3c495178f230b83d3dba0ad1d28f993bf6acd86542ddf88a9ce1e158d0f0cecea420a2fe823c856401fe906ff3abe805b7b12e4e247d5af679ac63b9dd519c398946944aba606821ba76e6d935286f979c56e66f8ce157f974edd843f3cc6d40f6fe9f9d93dceabda975a64665906f1a543a0be0c244825701c3527b6532dfe19911373b765f0faa835304bcd256eefd8a47afa7f1cb36fa4fbf163fe720e7da079a86996b6661c461ea3dbc4fceed45e91033cda4b9338b07291fa7534cc75fb75f7e955b43a562d860df1da4de9c0753005b467917a3d712c2f511b49a89993188a6f9f141237b4a9eaae5ef905845f83a1ed7452b902481d4ab614c132c29a93c73dfca9798e916069f0d3ac2978a97cdbf742394319f42bf9b873c8482bfbfc0c3a085e6796b4046e6615049fa453fd5b7cf17f33a92c3e894645f71fb970f7bbed648c94aa46bccb91405136b5597427942c8df318d0df2f36a1753f8942cd19cbd5c64457b85c216eb2219770a98c6ef45eacab5d2560a4be2f0b778329a2a66debe965a19e758c17c4713cfbbf8527ca26cd4ca71bcfab944d228928454a515cd06c7a7e07ea096696a8620c6756483755cda3a0cd028f37b73ade403a1a594f4af102aa718f2a84abf6df48e8c379abd66c17b5cf2fed257a797ccc67ba7e3278d7997afec71cd16a3788da72ae222688c54e50ec468b43d7f46125e7c99f3b836724f532cdc97ac87c75327b532bd01bab5c26728a909d6c62c77575baa5ff792b05a851357bda1abaf04ae2743b1b81bbc3ec6a44be4f4f6cbbc9b1d212e1941450fb83c4568b6bcafbb874502b15821a7bc262a99ae4fcf4b8036d525433aa3492fa898f0f9b540fbe438f9cafdb99a0ea040ae44bd37066a6d707c0a4eac5df94e1b04ebf24d26821dbeee6c8dcaf2a69441cfa95315dfcfdeffd88e3cfa0a882a7af0adca89320e856e039e456509c6e75205ecbcb2e7d00d983b7f10db85655c7df377d8e7960339ba2da39020233604670aa4d1665bf9484290ccc1519d480db07a8aced82bfe586ea859511356f65ef1f234f0cc0a74d0ae8f5371b7b30450726cd4661c69ac44eec577e21e5960150a051408171884739d8ec6b99f80ccc7167960c16fcab9447d529f2971e724d519095654f00000000000000000000000000f68be2218d48d1681711bb7e42841b973aff65895ceb3048ce161c929e042a1adfd1edc4da4b6a1e12f93f289058762e54914ae464e38755967f2ea5b9e9a38509fe8e4d71d357a21f3efd7e9ac8dcb3abc9b3ac0522def9b78cad05e2791dd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000087f6c2397292f6135b17bad2525cc61ac7144b368eba85ecea3cde8eccce73914175e33841cc305222ad1e57029454c5cfbab322ad7c4dec080598bd7a802b4badb8d07c18227e7ec6fc0ad4e583487079bdd0aacc31568dca1d15f8f1dac33c690f0047827d997491fd6f644c177105cfd1f263784461388ebbb69b21e3aabc0200e360dcab9937b1336e4bdf21d44bbc05c5220e85d26d03a3c6b6dbcc56f617020e442b4c9ab6232fed1a345954a8194d0273975e4db8259091c3ba7ddbecbad80a03fa4421ebac94beb8bbff071bfc5e3272f746cfdc8ebdb5196b0db0333faf2889842b95246443cd18215e0ea4760553e17d05f5a21da36d3d0368199f5f83ad022d9e838d695f4d6ef5e8f17cd57cb0d53ca2eaeadb171b09f499a704099b599b03298d9226d49ad675a6c05312c55f6ebaadaa27cf72768df9538bf3d596abf1030314309b3ebb8e12218aaa7af49e30d9079d8ea56c58e21a5eab5699102331b4c003154d3e2f707f7bd0df381593068c74b9d85ac90bb46b96055cc86d0c638bf1bf03243a90b9d4253532ec1e41e3b3a0a50eb6c34dc9f0f303310c3e99a72be88120dfb8325a5d0a369345bfc318e6a1e8294dd9b5b9b8e177347e39b6c9849b1471f737f0292385c6a2695dcd6ed165f189e064cbe99090c4a53942923588e48670edf813b69a34a2f3186a03641dccd4402b6f29436ccc172433784e7e67bf1a6b7c1b301d64c0f034802f44208f993309cc262d933737bdeaea4202a5634a2f87f570d981b3440bdcafabd3a14e126cfcbe6b2b3fd8c8fdd361f90c9f9d381949e4b320928932f20d7878ac355b97fcb5a326d7e36393f95b6efe7f98ba1569b6919daf6f3c644e16e44e327d14ee2756a0417b34f35d51843f55123088d4f5c9b50b9aa42a2b8b319dbcb3d803ca4ff10d4f094aa268b995e8808726dc64f86a30d61e2b886f14ca53482073a9359c555c82f188a3c0c30f198a6657f1165029679f83d4212dba8247bec87ee77341b819d59dd51f457d30d4f3637fb890aafa66bdb1d359ce7a9ca13ad5ff35058f899266bb7950dd903d648e96c4ab012a871f94407add72dfbaad9e1253d4044f44197cfda8780e36ced7a0466df3772594e511b16884f902c2ae9c0a6e384c81c6ffe8bb3dc3c62bd8a4b19f3db6a041dfb45abab611119021c8e27d0f2bc8c2008d8f75c2bc2ed5d28bd54acf84f8b2dde0be4276807cc6d61cce60fd51498c5f443836d7afe180bd19cffc4f2b852be017a3910cd37c59b58231f67c86f84a3f6dd87da5c515e1f41616badb595468468a29e31afb65a4b19fffc75f99177f0a4ef7abf069e095a122c7adf26dce1f5ae07d4425a49958f48c264a96052100842035338de217c870a31ad2e880ac22b24204f824c0ad99543f34b68a0774162d0a9d132d2b897aa6c0ceea87715c4733e71a8a8df79547ff3158a865e6f5ab5e0c9d893a0c603dba1a35788056e4877e4d5af22d442cfc9d3320332dae01d4bb5af459d530813781f5c3f57dc7681854942bf0986fdc2c9b584ae208abe990c0e2c08bee79a3d2dc125b93fc5a55e2f6a4d3c44d3be3c796919692a3b0907b0aefacafe9348cc90e46db0ff70d234c9a183a47fe1df509a3d91ae5b42e11b98e9efb151be7ba670252f4eebcdc8b341e16125094180263291e78d4b0974a0a6504d4303db9a31c52fa70e9631e10b3d182dedf03277591d26b14497e12f91c20da495035011ab50cf54d0dae365111ab7e2b5ccd2bda488f33db5a7607bb954087371bacf9e300c0a23c074358c86fd186fde37bc9b82b9523286e86a2dd89f90f396948becc375f0fcdaaea029929e782baf5acec61e5a8319d5c12506ac860d2dd57ac2325c8b92b1b5735814d45105dbcfb200b5899a804186b2dffc010632da16ebde4e067f6008d18d60e9cb659967e3166220a6bc447e224b86c8bad525c7bc77c07a4065faf7acc50ab208520dc1196d1b230897b2fcdaaa8dbc572b612502a374ed52ebb31e08f5591b65b9a77f7d8c248c56338f45d15daf9fc2186f1e7169e26a5d7d0b78248602c331e5c8280c7ef8c6819b465c155c96e8c8244416238f5b76573c3a898ba37857161f40a271ed89a0af25323ed7b01661f89f03119ff401bffb23f4a13d180c6872142ade903724450865925ab6c280103399678cfe9e53d034c2fe54e9c08023735d4200fd0455c42816d33f134564104b4120961330d07068d117292a61050f63f88e031009eae29a01df39e9d39238fcb84df4df23ae4d39aca3912d8b1eeee20eec49a8e4725bbd733425e78f53119784a6557bcc4c0af4843ed833853b01a093c74b2f3e76cde3dd83c2626fe8014bff4fa23b9fd962dc951b39b267696384494da045618182408bfeffd12b28719bddf5c0d", "ac5263ac", 2, 1903998239, 1537743641, "e1a1325329c16cd46ae975558246efebf2060ba29f27e4133d15258d67ffa71f"], + ["030000807082c40302f204a7b43084c2a4a905181f1199e583b88bfb0bf319c21a3b5a5887a813b5d7010000000963526a52535365ac655e210157a2774ae3310d237b3ac181b7e58a20427ad748560060d4be846a788ee2a75153000000000463526351ffffffff046bf26405000000000600acac516a0005934705000000000165bbdac0040000000002636ad0a8ee0200000000000000000000000000038796df01000000000000000000000000192a95a07c9cf9f737c1457cc762ba7ad36578a1cfc36f8538c9726ddbc589e921f471952bb44913ca105cb53b9957157366dd94948a2c939a572d869056aeac52a1e70f8074a634186cedd3218323bdbad6c1a79df1f31421ac1e0d597eb77300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000859e45933945f2973ab875c616cc4fe27dd0f10e6ecf6554e28a3fe1257548554a02e40649e46a12dd71b799ff603150f8b6dc74ece84ca9ac2e29848e9d9e090fea6e9c8b9102d09d8123b6120c7a30537a6c459b0cdf5b094cf4706871d00b92cf5f004333de995a60c877c015252a62a56c656baa92eeb741c6ff4d29bebb0317be14ae7b3dcce7775cdd39a5d33cfb4abfe7925f472d8b0c51057ef9736ba003136f7f37bd8d1cf15039f1b3c42d7dbb14bbdf3fdb899d12fbb710bad5e4fdbf0b019c17521f2884cd67c0c1bf57690bca154c6ddf7d6a50fb83e61cd1343757e89326a18632e0643fa1fc535ab263846dbf7433284c533047d930a299c397a8030207027db708fd7bfb6741dfce68181dabe08523c0efa9d4eba9d367629163700c02270d1533495fb4e7affca80fee38def77547f1d2400a59b4cbac490802458f5c030af54400ec390ebf760e296b3cb4d7977bc9bd3426378352864c27b18df446a403212f0ab2c7d2f867e120f6b77c83e3c2a7c3631a0242f1b166f13abb8ad0b7540326bc896690de9dd1c169b709d3b4649a1588d526610898d0f1833ffc3d4e877806ec5882fe9f6197ede1f2e64bbdfcf36f4bce05114a5ee483325c666bc668e24ef351a4ecfd32d7d879728b396968d620026f394167fab500feb75022af4254c09e45a70d1879521aa86a7824e56a41a4d0a76ea9f10e07d0dedfdf027a76cceca9b628863d171f4b9895f0aae0ea8b64e35bff8a435346d9162a76ca2cc63bb5ac43bc3e8aa73e00cc6d98f80451ed52828c0e771e4e46519f0a60d144a7c3d77ad5ff906b9253514fd869b8ae2fd06796b01843c699d402466c64261dd982a7a87ceaedee68cf92308f2e2a1fd4db21d937906870ec392dc302bc1527ed47904436c54e94578357fa6265730c02daf8d2f979c37606647baf0e58dab173a53d7ddd4460a8424c242a5229be55e3a37d372f88f10520d015bc0481741596a5a3864f9642603fe9f8782a9a3fba948c184603fea0e9615ed954dfa4143318388e508fe39cefaf2d9b63874b0040a05c1cb92265102cf7aa6d4d7f0760f9ad6647027963a1641bb1611b4b088adeeaced0634905385913a2ab33a846de715908047545cb3292f2ac826fe8107658d347683ccc131605358e501521fcad4ede87cabb2d8fb94fa199b0776a3a67de581d222d5bdc49cb62c0291d62e63393b570c5f2e474007bce5123a165691a05020e7c81183ce4d47d5d057a08660ee0afed91dddf19cfe733d8d21c609451e91af772fdab8bf72748d3d3819ec5b2ecfa604fce96564aee84d36b9e44745d7728f31456f9ff7de9a123ea95edd927b78acd191b8d23fd8b05253213c4cc0b8b9de95d76b6e31b873283b8c6eb56951bcbf9b6985cbd8e4a0d356b4864d469d573bd3eb24b0bd94b0cf044aedc42b6c4c4a194cb9350726ef7fc4c24fc40de1537823770ce2d05e793a7e33f67f79ef8322ed41cfaabefbf69df4f4fb2003b4d4be6aa9fa56f6f27bf564fe6c12c83c597cc41efd83f94086fcbafa026c099dbb1317c5523136797953728cb6ee6d9204b257531cf76e84369a115e83016c7d8c0585237b666880a3053a52e2e54a4819b1037574d6c180a966dcdd8adf5271e836e681887f9c297d8f61db972d5ec218c2a7c9b6f9f7adaaa3b78d44dfc4a9980926b4ef102c84a30e80023727a04feaaf99f3ce4670f1a47cbdc02eba3dd18ef71c8b48a425439b7b827b786e3acb392cdf0c3d67d871ecc3d431a56b97689ecc21c72228aa3f425e41867c1210d7ba9a90bc267be706f98ed6574552a0fc14cd1eeca8f42fbe20ddabee3d01e979a037f484fc1c0c2d537328733397585f926f8f214afc3b250a7f83b078147d67e66267876d703b934f33c1d4110740f0795628d0d75748b9ca2c37886575edb58713f409a1db0d196284525d71e5afba2513ec81ca929cb765445c7af3526fbb1697404d1117e4a07322c1f483180d06dd274a84cd19ad6e43762977e40e7961fbf8823b402be5a51cbb467ebb285b9542b6f20f89547662304f719e6932c3c06e2bbdf0159d9f8636eb58430e78069a39f8942bbeba2531ded5fc39d5e50c0342283dffcf5d750e656831cc76152ac9ef967e03b35bbaef0d46ff38d29803a84233ba75684b19bdbf2717b05b23d096d6454759fb1dc2baa29faf758f62cdbc1c544166de1efacc6c1b7202da5ac0f5646ec5ec21e69b9e746809d6e5ab64aae16459347c5235b37e91ea8cde02f8ae646badb2100000000000000001faf05030000000077409d1050aa834041bc92e5d1fa287921ec507b0470d9044a2a9c649106bced5be416faef72ec0d9ee7c5c72b6e0f825f5dca3e453dc25f00d4e28870803acac0b18afa1f31997439e79be85c09deb4127b07b29e441983dfdcfabdf4b9c38400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d33ec3c27a11af88fc71c55f040b02216ed3b54975d1c239dff3023a6ab6905c3ece9485edd6c4db0bbdedf309c7ca04e10ea82aef5c59690f6e653a71378ea115b1856dea065c624fc7fb48a662457824487c18ab06f121d35ee85924acd61a14d8db6e9baa0ac40f1484a9d6431c3458e13beebeff173e8f96f9a1c8d694d503094aea6d05784db6e1b8a6938805ab97cb51652db68a1de73ce543e7362a1a17020ce82d72d4028b79d2fdf1f0d2bd275ea4d10f48d5eb6cae28c0a2ec756c270b0b028b658c12361e7650b878bdccb8f5fbb072d60cf43db3cf56c0ceb2a52c499a55879917919da04da30af9d63c8a61f504e470aceec1e1501a5ee3615d6ed88b021e67197f097f416a976fc1c1d79b91d5653b6869c4df4832c0bcd217fd232dcd021aa0e3d5cdb7fdfe47693f7a29f07d32eae6217ad47a9e9e816a2306782d30b3030a76c99479d5130c0ebce88c850081bcc31364c7936b9b9cce2b9c402c36dde20203b3552372de857b6632e8efe0d0de6cf0efd2282ec14626238f8adb58a4f211032a033207b3e7a573d7096a93b8483872012f9e27038bb496f23e0af8e6a552e43d55bd1a9fa44701b665b5e4575f5b280a6ccbcc49bbf95a6bcbbbbcf8c7776a4c3e6a2cfb1b25bf47804492e85e0c4b9212f5ee598ffc0b4c00af5712cfadb1176e25ee008011ca9b4fdfedd773d5792a08529faad245a72da92b9adca63d64e6e1d7a331209da52b0e23a65133cff03d79f719fe7e54ad7f6b5aa295b4a99ed5928d1726c9f2af0a6f58229b6081dec3ddc69b8651b964fa217ca777c88dc51adc51ff8fb9f62c5876e81e8535cb2a892700a7f41fbad38c294488cf2ef1141f5582ef8b951cb1251481a5f2d4755df1ebe29ab38bb4ac117648d5fc812e812ec6abaf424f5be60cd4a398d3c74e1db80d5f6cc0837d1bb1a67c5c7962adf9e714dbdc08e7d29fbdf44ac49244575e01f7703017623007ea7d142e36d7c6d5353138b03389963fec964b1d7e33d64aa94cf7df3c15067b8191c2a4277bc7e6c3384ffc6ecde5246c09dac1f748ab7b15a581e64abd487010f96e22aa7a13d23681f71fdc1610e4905f3d235a4ac6146e4d35c015de26e484e53b081974772aba416634117d2775f795e51c5c539487aa5dd53c5e93bb94b9873beac81d29d82028c829750a9bc7e19b267a53c0d92dee9c540c9a6e2c92c12895eed43062cc98e7388d7d1bd700f88c9a39f89c2397b54c1ebe2ec5a82820fa03373df0ae1cb203892b108ccfc9df9f81e1ef8dbde5112caed80ef0c8295fc63c553f4995c1c6bdce5eff122397bedbc74e5cdf8341e17b14c1ab6af595283340deae4ff00ff1e6971d99939b6b667825506fd93ca5541ac45379eae50cfe898140c97e4daa1d930ca375f50694e8096dd3993d5424cf14166ba430e6989a4b8c37131374973852d89b6091c0a3a94bfcd019822657f00bb2f5b2fb9c9b23e9b724003e98f073c1a9c1decec8b4e3467ac1501bb2551d095795a09c03831639f6a610dd9ef3a76db3f49807189723d894c48643b98a8537c9f3910906b085d43352f4636655cd9decc4e776b5e3a4469401c625a90af6cb5098c54db3cc1cd30a340ad03f75472e3f83ba99ff05a9e9b4269f00953afd6feb073ce2f5b66905bc9736d27f2dd4a29ca6f7ba0784924b009f19b150b5ed5e4af964b7be4d4cb3189beaebabcc2811b3cf1d390985d815153d820ca29e1e66228333e45cbb2e3fdd5aae771b17dbebcdb5984e7c6e4b062f96194d934272f19a7059199fa39341063cfff76a23c17dd0e79694ab610a7fb7cc75f8b854ceea20333fa5200540c4af29afd15b2807532f9b37ca93fc5f5332303d88dee8c5ecee20bdb73eb23a9eeaa113c7098f59181572b9fb533ed3097bacd9fececaba0611be113172ec0f4b7596e8f3e0e97dbf9c185a53c057ba79739faf3ed11b08d352bf0dadfb56839a4eeb325f6912f4636317e4b001bd9be1fb69591efed9aa2257331ce00d85f3cbccff121a2bd7f2d8dbf6fca11abbcf8e6f756feb5913e69a916e5bffa754af948baaef11d8199091f688a29c150a841cff281d5286fbcad7a1bc36b1b906a823a10fb138b4ead42e73b44dbc0d28bbacabb29732f8a8c52eac508921fc84bc38dd7fa5a2275d6d987ea6c41847b0d7a4342d4187477270ce4c56d3aed82ea1621a9cb32f8630af876c837415a77a54396842e5fb0baeed315b5a50f211f096c4ab332ff56b25679fdc337c0dd7ff82e2014a9eb1f9ef924a00000000000000008a9eee02000000000a2ede6003d3f0b1dea5de85d9e8b600041da0683165439e2ab7c05f778d30f9a369bbd64f2478f88cb04f5c11289739d9432f50eda87256e96d0edaad1d4398b097cfd2af180b48672529a4c588dcb2f69507261cad61d80e42c6379de643b800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ee78ecadf8e3deda93e5ff8d3d0f61c90cc0ab7f091c3bead3dab456881e3ac0ab2d617cd536291724cdda4d51191b0cc51882bbca5bad7bded420257eb4727bab7d503f0ce3ebfed811d0beae61938368d574cd006dd29553f97e0a9d839348677e11b1f10ae82592f250826440411c70c0624bd6a9677fdd0c3edea2bfe39b0319c5fd73074eb924fa02105eeef35e8fcd7a29b1bb32a3ff883eada72240a91b020ea834e86501a13511b59bf960cdb1af0ab73125eea229e7c0d8c3b3d2508b530b079f595c356b3b3d51c30fd7bc226134bcf83cfddb064699a943809057d3939652479a9f5592c461b5e90150680115f64d940aed5a8a559ba1824b6fef886297030cc8928fdaf7b7017b46e2318f80979bafda51dce88f01c5105698199055c436030f324215808be96ac0f1f50791ecb36c52289917d11354a320a2069a76ed8856031f63eb2e7853f35eaeb65bb4d59304a8756bc1bd85a5b11ed1b496a525ac07dd0315a89e3c3915923c937d3ffe3aa12e2fdc59f4d55b46260ed5f82ddb743ec111032fd0ed1c0f46aeb69113f4dff45c528d47261e95376b9439efc5a85534384ac432c2131852cd9535956bdb27322bf1417da538987c7d7977b946ddcf636612a94b80bb5075dab661d84dd8c70d32a753ec1daccd88504d2d09986a858d0dad2c962738f3775d916ddaf99db26d8ab66de9be0ffa90409498e1a3cdc348c1f44a67c4640a915dbdc7aab593793b66344b4eadd515e9a64be2a3553c93e6b3074112eed923364ab9740a1de745d5019356be8eb0cb6146046e1aa9294e83ef0fa1d9b49fd9de871f88bac4cc7413192bd8c70443f83f1b57d8b3c5cd8f3a4a4fb04577b85e44cc69daddd60b4a110acfb3fea0822d3f2d73254982d61e37ed96c0677304ef20bcb969e26f3e53d6a9391db13d1f0aa76d1e042a2d4c448fa1ddf2448615467b658273823f65db363f74bb5415b83f550cef7f55e4cd3a16f96d10e0fce0bd87e8af4e1b78c0c097af2951da58a78e2b6d30be3488a8b6e08c915dc419d4f8c202cae63ad5cde5ce822d80fc061e6c97f3e5f60b92ffeea8b400d091e7fe2431e91df0badd8a7810ab39218d5343a8c5754a7adf42d859a1b20d956d85fc7bd0a2b2235442314019701569565a39478cf022d3611d36c04eb0b5fed0f5a842648dc1ab3771d87a5969581cc00171ab78bd166c7fe4fe1bc3cf00a938a0c4ea8e32b987db80f182098354c5c9542af58f57f91beac0c2476b7d8c12c92ba0bac4103753b22e73ccb99a5d30fb78677261f63493d8be69497d7b5fe8dbc3962ad9bc0172b63d838e018690f5c6914a9357466d2e6b4ffe4db3a5a92827442f5a26f8b9311592e356a49f6948f1c093d6b536eb143530f1b0868986a5ed86282d923d949903bc96759ac1066b016eca98485e3f66559de9f650d763d4be76304b6ba90953cf641f451a3332fb16afb1d96a9aadd0805d8838d2b774721b5688a1897cef08d2e79376b4c11fd6d665e24fa73c6f6c706fae6d48802bc3f3ae4f95324da1968d9742ca46a9f6fd2513c896e80c0ffaf91484526d9a74fed7944775173ae65df22246bc5b949df8de5fa9008575090193be77ba6b290bf5d9ffc913db5e6984065d3b5e8b35656b95da1dd97881a9635e29c15f60de66e8c3bc755658da163491a1e6b4309465cb73e12d38c04913c1a30b90a76caaed0902018fb26d03aa7711dfd141b7e3f24f4e9939f3d757b30448d929f8f23a0a17813c0595b02c967020e8b44e52244dcb2b10eb916902a673a93b262fbf1d456b1496af4635b4766c2d2fcb010ee9d9a4fdb3952043803235be91fe5570f34c372026a2a3789b7a8ebf43e10301e9b7fd14fd6f827a60fbd641e285c023192a9046232b61f6a5eadfb69fbf7800dafd98d766dc9b2e3f17c47af1b90cd38330bf1ceb833f0fdb0c4d8732a4916c9f496b5c63ee64975915c531b55ed1ef3c916dce1b882d4640ee8b71128eec3c83642ac2b654476c90ab7ca871270bc80336ca487c83f650bdbbd62d5a71197558f26e317d1e3f281cee878918ad1c839840cb0e2ee230082f9b5f77d9ae4cbf941ed95f3667b59d9243a737241323e63569419af5ef08e9e226a8fc84790a8fe01c6c1b5176fcca8283cddf2afb2195ae7fe68a81396a7bcb10fc1e130fa5b19a6c8e0903c467c2f66a3508e71e13e2e74dbbe8d2d7ac9c7729b691a77c06df414b87f5ec33132dbf47b45be2a27ec2804d4119b3f2be9e351969b051a00ac72ab4a6f01a9532b5e742a0d175346cb13b0c954a1ba0bffe7018eb395dae11858605de22f7e22e0002371a118f9f191e3555c771ab3c7868e1caf9f1f38c01ca03d975d10150644af2a857bbb3d25926bae680d7957587237615f96207326e2e9d0bf53b0d", "006552536a", 1, -1289411036, 0, "c9f083740eaac8444117d4f56919da4348bfdd3b5472fc6671f21383d357bc85"], + ["030000807082c4030465ded46609a8709980a645daec8d18c186abdb8e845ca6b63f8651b3f924b7af010000000951ac53ac63526aac53950a60539c70b4da7a3474b9dc78da7497d696f288e49799ea91b5f654cd9b6935dc5f9f0100000005ac6351516affffffff73232c414fb34e55ae08a8a67951f6991e8c7bc1efe481838c090b8e0bb77ff900000000076a6a656a65ac538e387c1957d3819748b32dbded1af9ff82afbac2af8bfb0ba9388bc9a0eb788296755cb5020000000551acacac65ffffffff0255e5c200000000000400ac0052b2e26700000000000551516aac5300000000c5d6bf58013b69fc0300000000000000000000000078a944c76ba3984ac9fc138960bb654b6f707772229603b977b051d67b73bde398565723edf88c47f43649eb5db7ec3cc51ced9591f7a84081e19a00bcd75c2dea506c8e8b8666c56a16925631f21f2316be37e93ac1d2542e6c83c1b5c7add200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000afdf7036dcaaebc1289621d94ed38ea280829561f5e7d36bddf2097d674e90956a49c7c306b27d0d9d9441413e82dde38cbfaa96b2071742553de6bd5df7b4a91c08c35ca3fbf8a889d87c8265f33f91d57042d7021f938b42bc0c43a5f9e7fcd4f3561f737fb48295c53ce4beca7ffdd0ed8af0072099145eb6c818f7d3db2e022a5e9257c706128c4310f4b9986cfa00cd71e195b063102f15f27f40fa57a1ad020644e5c39d8600bb6976205b4e04990cd64959499790a31aa63955b18b034d810b0918937d47be67ca929b067d0534a011d5fcf61641154f48d2fc90278e9d23458c4d5d451cf9278461939b6c0ef5eabeb10ab51595b6731d5aecb19057c6f204032cb1ea5c2b8dc402848f20c8fd98d8f84506ae3d7e2a30d013b4d693805ff9bc02108c3ec6ea9fa100fc4f8b711d82fa81217e19e59944182afd94c8ff1bd991aa0216ee84bc76193c364c5a086fff6489436c991e186744c73ca3c1ff2eb44c35490313a46c16c01286a767daf02207840d37e7500c153d8b1486671c5ef6586f180c021a05910ad5a84f16933018966f88054457481467203a97e6ae3c94070668212b3eba1eef44f44166481687ca75702ceca0cc626ea8fd7affb9fb958b4df09593ba82e21243b8b6d110777e375c5d29079f153d2daad0ff2ac5a34f5e24b65b0c9b7c5ee70cc6f58a036cf2fc22e7eb7c2df7dd73da0a0a02b6e3a1891a579ec501b62cf7aa858bae97533e7403ca756e2db8ce968986917aaff8978c4e6b9e05a453a225194a9610c9a1aeffbcfe8454c04135d2615aca485bfabe0480634045c50a1a019362e6af401a9f3f5ea7b9948065bbfdd77ed0aaf851629000947b4fd9cf8cb1f75db01cfb7dcd0797c09a5f97d2752fc39adff64a879d8b3326f4c9ca15bab482dfc182c483c914342cf1b6a72764f6e5a152c535e3a5f2cdb11cb65bc5fb530dfd799ddf554404a6a88d2a629fe7aaf4eb701cd3d868fe3e7f93ce4929c0147ddb0d019a0281b4f2bc4c276c7afd7eed51df761af152f4334947610d4986f6d0cf052f79da1b2997799e9a11f66c210d4ba7b160d4963747d75ab247bc3c1da2679dce017fd0c030bacf89b0479cd2c84dfe71516e8ef1b5bc0bd153bf6cd30e6809b920fd4a76669739df3a8d36515acc1dbfe79f3a244adf2027120b9314e1d828b4a828e3ca7139de80a2ed1a0582cdb2ed2320ff7d050c04a7972b735414b8cb933d16247a8298f0c93c4907b561db31470db7dc2871e4409640b93f8252a0e0ebfa8a818406fac1c2184f7d7e3c720698208cef7c262d944222a57be0b480cafc3cf6810a2a65ec88650acceb388310dd05619eb852cc1fb838d29416103cebd2bb99e12d4cb4de5d8b3705be8b3476feb082f81bdf1ef4ded18070f71a14d29d067955b58186eef7239fbd7cc208e60c829952991d550d03d17010924f646d0e7424d98621a14a03ef4bc8eb431663e4a2667556ad9121dd59206692f5176f0574789f2ad7c97ef2f372c709a2cdac773aa7287d9e62ed42c5814f009111fe6b80ef59cad14297e6ccf3a4b0f716e5e7da901c9f7dc26fd32242cd782cc74752dc076c19c6036054e13fac8c542d86e46b277ac87dcfb9ce431679caf97191697813b1517ba8e2968f1f90e280f563fdb488495aece7195c0163531dd5b6acab2b7b3a9b6ebfbad82f0073a5c072afd6fa396edae4587896e7ac4106b8c632e01da608e718ca922d844f38ce8a1b2b4c8b9a3d1abda16dd75b56ead37934bcaa5ec9e8b1e4bc71e69c36fdf34d0cd2fb3d078e941484772299489f4069ed7680048b989da3d8f9a105e74fe09a046612aaffa851ceb0c1bd9c6c6da8d62fa73cc32b8e327aa88a007d6183699eada939708f4809534f9221bfd0d8d0294df45eb766c327bb616391b27169cf668832d7bda22019bdef2c74c4e790c7a8f19a1ea0fb8aac8bd0f37a69e024abcdf566653955e212cfcf05c0377cc2b1e9bae4ea01cca3097d26dbdb8ba10b3b6b62c2d6ee4978ae8c71cea2f96b2594409ba1fc3730ae3841b3727dc53b9e201593c83d9c35554ca6d4c08e64f3b408e225418ffe7c6e49bc837074c9b4e4c26a77e76fdbd5509e9e7888f671abf6d3a6e2e69027341dc26170fa18c8dafe463a257e8b97ec95838bf83182cac644c1fe83000d259ba32b44969228b99ddc7ca04adf6294d1b43cf2b4840a442da6f3d66e82f53a7e68986e68bff1fef8fac18e4d2ea111f97bbaf7f720914580f70bd8c5fb48e1ade46a072da9fc54116487a258a09557ed54ef90c9da7858b29fd4681314c0fdd4ec21d9cfc40804dd01b6c7bec7d10186a3d0c8c7f0ce36bdfd5df9c762ac6c3adef377a9cabdde7a1079fd7baefc99c374761eef5de4bc36ed3813e97af4c990ed6d61e1c8ed730f", "63", 3, -298200912, 0, "19fc7275b14f8775fde4ad94d3d8a9259787378e91aa5402cc15288b5b4c4f0c"], + ["030000807082c4030395b9bb86810e95b2a21c2f7b95cc19b0e8cba3fa09c33712c7c0e9061bbf804a0200000001653baf9f24434dba1597374eb1103c3621814a344bc3f57d9e3bb3f9d68e8f5070b48959380300000002650015d1c34c7ea6989bbca9abbd10e7e00292707a0ddf3087a9139df51e720812051fdf05f50200000003656553809cd9ad029c3baa00000000000091f2f7040000000007656aac655353acdf81f6dc785aa311034916fb02000000000000000000000000bbbb26602590fe5708d258bbf95d20291b955c90202fe8877905c9cb0548c4fe3d04a39967d1353e3b12238cf4c896f9be7428a3ff0da87fb828a011f7852c253edbda331bb41c913d3c2839aa1cd6821a0fbf6fe46da99874fdccad0834217d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b90361b03ee29ec3f7c0081f5d6adac6228bb2fd689bb5267f1828361d13329b6a005b9f124d79a6d4ce759857b382919b3e97858309bcad4c6153c587dcf8417fb5bc0ca410fe52fbbc0d33188ce71d5f5ee40ae1a6baf8e11b3fc0df6dd490bbb018f6c8b3e7a3508e74d07d9826500c65b2526ffcbb21019ea469787f5b3032f5d28f44868898304876687ede8462a2f2cda970ab4bedbc2a63e35421ab78c0307f75ae0c695835898d383c254dc59e0f6c40315e96e072684439ca3ed9a37c50b04eb962f24bb4356a8bd19d80c647228ac5a6a3460bf038449deafe39088d2479114f26da96492659df0334b9793ed42cf2fe73c6e55c140244699b5bbc33f41030f2b1e18398965edac831e6b460be8d60e54de1ca755429471e0978848d9a268032638c39fbcf23b9e24fcd9e5d025d4acd8c94274123df87d8abc3a8096482b9b0313e96527d3f3d73149472af58d7c5b365efc90b7b675a0eb805cb1665a2818ae02118317ded582f7441c0aea09888df7dce4eeab9a299ecffb20d150df213036c80208c551878acf2ff110db0c9f1ddaa13821a0083897a827d9cb49bbc256d8af2aa889aff5da41e161b1e6aa864b95f334fd66a1285dc566d4dac729ee7784e407b618338b1def3a431d86f19fc2dccccfd44d0f19b355b97c22cc8deed66357a2d40b175aa6df02a945456398f8b9dd0d18712693eafd5020af63da25564fa807b6816c02c683760267d6205b96f7502015c85ba4f39d63e7f016ebf66f412d4b050517dd01fb26edc8b51902ef8b9f9a7c71a2065ba91b749e69c63408a3bf5acf3241bae02cebeda975283207ce2001ccbff24479d1980ca08918848cb42cfcf69eee86496e75d7702a7f6206bb26c7b65542042e8a7bb8cead8a604c9907838a42d96212772628cef4ebf2abc378994f87e1d8ced5494ae436d92e29d632e3e19e063dfdcd7a92985417f6785f957b7850fc21757472609f80005757c702ccc3e36530503d4aab25d87f59ca03703f091683bb1b315710e85176902dc837fce30486b29bf14e7faf30b2fa6900dffae5ce179dc486b5cc4ba3e5bdca1925b2d809eda336a1848398d03670564fb142176aff2fc187d1acd86759401529a8c0aee1e0e24e8aa5ad73ccda748dffe7661febee64e9416a732f53ed6a5db3263f203c8d9d86c6d33dc6ffce222aa1e138d25051ca0431aa9e4b98350eb053f2f901d0bcd9ffc60ddddd8a09ee3a86567b904d0617b8af6c1741efb18907abbf573581e92c55885265d97a6e5f9f70670972cd2ccc338404a20d8313cf9cd1ced4acaf0e9b50b0c63e690166fc393e52bb71419523839251a0416b67cdff78e847f3236d4186a28578f40054b38a803704855eba34ef3001dbb4055682e555f6734a1ed72b696ea1c1fdf5788291f0cf0e34e5c05c62630ebc3b3609a66172bba19d34dff4c195916a48596a547f60e80720973cadefc4f4206d97b32a824204db7eff53a31faac73de1c4e0793ea9c8503472c7100ff80efd3626cc50c8753a7966bcd09e6f2c59eae77a8fe1fa50e4f12c0fdf257cdc1106f1261a693630c9c7050ead7fc9ccc67745dae84e5c3b91a83ace14982ec3f60dd8c6b6bf5d42a1212cb893cbeac146fcbb34bb7d51f63481b62c3c7041ac2f2bb035366dae9ac0d2c16afebb132ec7ae799e3062099d4373fb699f1377b76350b23845c8cf26d4aa760ecc2dfe98d3e2e90437115700f27b8fe3666fef76b8cec3ade9d7efaf5d437c3dc453ce8adf4695e3a2e11c1b9447e8247ebf46f1cca2d43575e6a87952172ed09301a959af7c0621719aea399ee73a3018fc72c9513cb64f899d9cc881af2f717e32db266cac67644c1dd3040ed08677a8b9a8ff32a9c163984ee54389e02ee0776a2df9aacbacd49d9547734bee18210b1a5bce0266d81b78ff201d4a4c4e5fd01027d36b098cbb4a1d19efdda6ec7c94011d55ceff1ff9b440c3d48f6403b325a693333b9ab2d8d865471bacaeb08b385e2ae0836f99a4e7f5229d0e55f660564c96b75cbcef03168f04d2bc15833f1e7def1f5341fe1af75acf4258ceb49104954579fc651faee6fc75b0d48f9a8373cd7f3d16725bd2a4f17557c192bd48125fcac5107b0afedc47062e686ce2b5a19bc815e436a1fa28657d36a15edd5303ea27b2ae7d0daafe16c03eebe103982264a180b997eba701d070c52b0d0c956909ed14b71cfcf79d971810c84f8850a27036a849ecbfb569378caecf5b0a5577388fdc63c9aadae55d5e4b5086f7e955ea6d040000000000000000000000003e045b53d7c3c238deb67464ef80740e927e295946d36710b6bac299779d636e0dcd10350dd05e0232a09c39bb6017dcc26306c3da117ac09870bee1323ae9b0bcb310e4216250fb2f3e945fb69c00307625bc54aed49582acf5c74b79603ab10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083c728af444d83eac5a637b47fdb90738f5e9746c314018b0d0613ee9c12cf12b6bce0625ba22b1fb42d7f5f6ffb9fda87c2476f821a9774fa02ac1a29a1e211abecc45901444cabdf037189d30f662d8334b207290e4af5893cef386f0b145b5a42c4218fc834b47112a0b166a6dff98c08d5eaf7e83f60e590fcc2b23efcf10304e5fd4735150db68a409939b207b878bae5ec901da720ab8f9b651397e30a920326e266ca209d9ecdf132e5406fd64e5d1b87296380b4361cd9c795e2b6517e120b06ad529c803d01cb1e3c749a0fc5dc1203f9bebe3185469cb87aaa32b012f26480401b3d781409e1c3aef3bc173e27bec9700f642f7d5467aea340233bd89801030e52b806d6a5dd8ee461640139508d3cd3a6c94713cd6452af7f947ca734aa36021c7f644641a8c807677c3210163cbcdfb7e8fd97bd74cc9674150070237b2efb022b7905cef222ca63bac2ad6632605d3df6d2cb438d73a243889bbc30d5cb4a16022e4f617427c185cf9e82c9a0d4dba42561d5ee7b12bc4048216661ff4a2128650220d0a6b7dc0496f8bd2d0d3700551730ecd6fd1d2dd10478aaad941d9c26528066ad2628e329912e0162cc157f468ccdcf41032476ae5c06d5cfd00159493b2e5e7a2f018515b34b1618aa64f2730243106945965a715b7b3b3844dd702281282350309b73c6efe70812b9609402122525546721e30793ca5b6fbac9c7b6bd237971b363f70d1f4cf08c57c9e194001bc04019dc9e49256c41404ce8a110d057866da28354ff7c8a380f32ab0555566dbd89a11db52c03ee46e0ab445ba61f3ac322182e9eab5d71dfde338e43c1721ee1ca849345e80244d15fd0a57c13bdae2332c77704069f3078ba05def37717b4563d25434074465e6905aae7e5326ca1a4ee06f8c10b424914ba7569da952afb17145561527e209e8e3d30e15924a9cdf93e6fc39a0ac516bd724dc7ddd7fedccdf639e8acbf4d4eaee55e311c554c272ed14340b9b5eb6f8196edf597bc41134f879e5747df95a2b81775ff2a3757a299e925c4c4f23d0bfaef5b994e7eeef78dcf14908f4b1b53eab8aee06e46808c88d09a2a15d3d1330522e9c87bdc358b953c267dac08b2459438137db7046269e21b29ee096730e62b74a86d02286c1e9b076ee303e98dc8a2227bf7743177ecd04fe7eeeada2d858b221a8b83f8cae1ce683dd9dc5633f1360c392198dcebc9361f9eca9f9ef2ae2ae2f833d8ac80457515734883a00af829486f1a500bbc1ae28a355dca9347a5076a1706bf0b5bb86fad59c407e6be57d4d54aadfbddfcf2fd64a35038b6f209e68730c982b108cbecdfe5ce1aef21cf8d7e95ec7516464258fcea7c8aabb2e775194e2aff515fcd404c54b6a4a284142b4b32d1aa0ae2efead9339e8c5a98e28a64cc5e2192527f1787123c6ec9ffcfb080999a5ad47075e4696a26f86485aba1f18bdc97bbbfa8a85ef413e0bf6c19a4d3a692c49f6a6651b6b6ddf4225dab5acd2a78a602836928f7897a5e742a1d50ce73eb2c839024adbe0b76b4c320dc586ac43e001c738e0bbc53cd4419f2841a2211a5f9fcfa3f6a26a5849dc99439cf9c225c09e19f19f4f5614ecc4c42eeb5a207526287f6d942bd32b7e0166b5aa2beda37fdf016bac5b0cc2b8f02a66431a4a7817e60d873218784d36545ddd5dc88f16039d0724cb6134bb26789e3161d66e84b5f4dd0c5f4d4788f536e90b568d5ba5869e81c67ebfe975a1ed7753745af32c6467aae083b34e94d1506ebfc859f6b6e42b8e2e30f561d4158e23dbb79d7b181634922e11defcc56926d4d09680a86dbb5df20c5181a54be111498b1ede465c3c1adbd45dfc660a03fc789123cdff2419f16dcec78ceaf0a521b7a93eb201e196197fdc86d47025ef6204a18f4b6bbf32d875796135465b8e42d93153a3fbbbc89564b288faa894b4035ab7861e11b5272800fa7697e6cdc70a71c5c4dd1451ae8f28d47ba9f44ffc04694971c99f85766327a09d9b0d263bd39af7ae59bd585106859744aeee94384b09400cc9e63aa98394a0ea6ba82bf8cfabd2257424f5a01521aa911d6da3bfb64a321ae7d732ec0d881c4a721eb8353f18dec4dd8fa9e4f2465090a89c50e321b903148096fb9df66c288654bb1b89a9a0c0280508a4f0b439b3e14a73b962211e476aeeca9f79fd8fc82a54fed32e966904f6a963e4c1f79ff68109ebe2b17b51f6951e4b68aff16fca8afbc4f755db688b991312bde2d9b5189b170d3ab50d4f28f1d5482879b13f1a815fb0000000000000000b926a60200000000430d1baa3ec5f110cf0066e3f59c99893ae1dae6da3797c196e7eb1b2c1190e65149a7d75a362e6c0f24b4689adc5b3fc9cb9c004c592029ec22a2d0039d5b2f977570e07b9471e18528a08cefbff69338d0b6e4de04c10ec81b638ec48c107300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000411cd6cd1486323069d3db7a559dd99faeb3a2a23e9b24c4debcf1c343619a9a30f92d5807a5c356c6bcf11aca46405789cccd0f787af0967152d98006be760c5da9fbd0e2e4a9371e66870c5367071773e90b31c051b66c6d8aa88a6e9659b1012b47f7ab0e137b0a3a5d7f72bd07ecddd4bc9d0112f9926262d0ee82d5ed93031ae9ddd35e697871cad0d80c6fe219a68d0754d53e2035808e35738062d59a09021521b4aa1974422f306a67a5b65e8935d3ae72559369b92930fc1fd67039b1a50b0831e4f1c0e273965faf5767b3ca6c0c33ffaa46555033006237c65062d8841569f03004b1176460a18dc045270d4eb87456591f707dcde63458a038a7f4d0020222dafaa2358cf58d88c6859ee2c235e18231396c9e5f34ff61a6b360578f90be0203352648c6714dedc8e0080f122c6e0e9d161a78838e9b411f2d68d4e898074f0321a50d5ca8322e6843d4ba5ba6e12590214c25e0ca42f56a93508ab1e24874b3030f7bbc0b89f2be396ce528343123aadbd4a4705e9c9a7029dd9d38256bfd80d50225f7b3bd79ef583163257732dfedf33410972a73da27ee308866037b1efbfd81da84b1e886fa2d91536dab7dea51377ff929d5224c2d9daa081881c3cdbe3d0888bc7b22b64c48fa75c450a9de20f89f57ba02e7186aabfdfac4acdf9dc8647e0e7fbede67983368c0b23e43e3501deb75cf5ef9ca06e9559e2b8d0c09012b95687d5df5a161d22f61768a0b87c038aa3e482219eae44113bc879d40e8df20b4e781bfccde3716f206ba96b71947f31ed5f8dbfc631d05c26c7292da5d9025b85c49d33b818d04e4740b142a7e94b35f4747603e96efaa0d48b92c5dea6e692ef3bff3405ed2bcfd22814e6bac8f6c5f7d311ebff9e7d88ffd2ece9ba509572e561f003c88fb9df61f02fb8b99af8f42b909f794f9195265e09fca85dbb9afbc61c11e9a6eef2dd793a40c102b2d4bd87fbcb2201ec43b5e0f1aa47b8c1f976f5992447daa7ebfa63fd1567068eb169292ce82ff687ebc3f943c644f41798f5d1242296f2fc9e2fc47f8a713a907a10a922c4473a52ab09c54a173f2d05b99b6ecb6eafc9221deee3025bc9dc0220063596168bfc1a7fd890dd73300b1ef3df3eaf805d0b6084cef7191516a1dba8ade1c3878e622a9e314264aeb40bbb07be18d902078ab2bdc0d94e5c5fba96c2a19b5d47612e3ef8acfc677977dc6572c25300b58c4a9966bdf8c47f5dafe3201586e498a64046b6c37ec688108777b1191c0e384bf57b594bf9b14e739b3cc655b1b636a465d6d0b42d3974a9b5454e748b766f65ba13e09bf3ac2cdfa12cb797b852a2812774794224da158e4ba222ebfb4eaf7858a1973ffb72deb98bacbdafb336d360c09883cc8c268a21a8c35ae42cb37debc43875ccd6e3da73ee0a6bea61b30585b8cd4826463a7e36e23fa79f8dab5a7cda419cfdf1136e82417f9d2904db6badda614f56fbf7f5d1591962b20a11c64fa43c942b4c1dcb6c66989fccfb10b2e94d631c9d9df4853b2c724ae50f20fcaaf44b4cf417f1946906e370794a090cfe0aa68abb173721101fc71bd38914cbb278ab05aeb2d4c86df7b1378941749a3550dbe8e5c96660e15a5a98b66f04af9b906840caa8ec673d5525b86e0bb72072e8a8b6621007ea28043a72b5e32b980833c4a4492c08e39f3be8cba303d6bafc4e49374f2868216df8ea444f22ab2f6ac5872b01f9e997d14a3fceee0d62318d9ff3e35daa4e9a6323675409096fcf62c8d2c8d1b45492ecbcd134fd159a79caaa6d1b563200f796190b1f2e9ab846adb40d33d5ea31533990eef576d61aa39311f391883d583a5b939f2a1e64192c0fe5e7c953c26e6248ed92d5f3f9440158a1a98ec091247954105929db3373f69189d8eee9f55790762333d8076c1b1d69bb1c4dc2a428dfcdac1b59cc4595d7b543dba01adb519e30bb8dfaa1d019d0b04401c6bd269c96215529b642be34061c5a57273b3aeee580cce5d279533e945b03cbd43f955ef45cf56d04610bece4a188b8b1d416f65b085b58407e500cb2bda5df6824c16b50c04ed212cb2341f4a9180204b20783c2e01d597fc1b6c5c71d35e81c97690599d6cdc349b6ccf6bbce0c01e6b3964110c25b85f4168820c5b21ceb17fa22ee341821bc147785c9a577929acc757d53f5ae3f7dfe78ffe0b439d74b0ebf4928aae0e9c025cb70a887319b169ad545fb1ddd779e010692a78c10b2e172faca0a774d4efbb4b659173fdb671d42dc7b7788f9148de62a58e43814e0b8485788f7f1507bf11e58631786b647e719049e4bdf6c00a9b6a646b6edc86abf9e73b8b63fb90cf25c67cfe56a54a525b794dc46417c3e561b392fb9aa06d64554c90836d64964a5c812c647dad7d2ae3a24d7a9cff83965713424c0e", "6a52526a00", 1, -1462065816, 0, "8f88b1cf561f5bdeb05408f7b54e45ce91283b9525aba919d35c899d5f43d3b2"], + ["e40a517f0132334c42f3dbb977527ee0f2189e2236ee3550fe5e2b35ad39cd4c996cc8f0670000000007636551ac636300738819b304f28ebd0500000000076352516a5351008ddc7702000000000763ac63005153ac93d62f04000000000963006a00ac00535365f4fb1d000000000006536aac00ac6a0000000000", "6553636aacac6a", 0, -332937932, 1537743641, "772445e3be2b6e48648881b5739570031601c99f21362aff73699071e59d0228"], + ["030000807082c40302de406aecb97242cd75ae0d0408b13b351c83a5784b50f6e06c679668fd3c2b27010000000152ffffffff5b3a845603acba7f1db429079229b2ee2ac9f03fe3f1c826ead70da99c2e0285010000000653ac515351524bcd471904acdbb701000000000400005263f8955c05000000000365acac4803570300000000036a00ac5c6348030000000002005300000000a8403d78010000000000000000c88a5a040000000001fadbe2aa01c8a8597177136833d3c4df62ade77352104491bdea594195d238f4227a1630e87c39d07328dbe69ddc77d51d51e9cb2c04efac7cb425aa8a2a7495f62431fbb78a37c548ac7cb27a369060126e6f03cc25c04a7d54f0515f8d05000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e4dd9207f76d3adc4113caf9c39d314332a63de9bbba625aca0a445b4dd2feeeafa7dbb6c6457c606cf20f31dc37612b0f08283d19646ed25fed4fa967a4a36302c79b90a84090671a808244884522b6ff2ba088945bbd2fa833170f15b47ee03d41ea2732febd65ed8a1161f3ba480cd5480e60499b094a5f6aa435c214e1f033014c6bc6c67c72b827b1bf437d8f8d50f9206425903a94574288d5bb3c722d4032118fc2f89848de615c03a41dd3cb70626fe8c7e1f5a375b178c0ba8927928040a077892221e2442d0403201aa6da53a8cafed1377d4db4f049ba6f7a5fae9404799f3eac3757bea064c3b4b3312a1bde7b8c24651e4280596a357e09a2b35620b0228d51947d4f402955c2223bb45c112e31f9bc4ccdf9d67eb0cb915cfa4b2c9ce032a26cae565b672b026df772375cae842f56850be63063505bb491e81d6d85a49032972e7f8ca18f58ff4230104e9b7eab82545324eb4ea3640775774789621f2f80323d41b95b484caf2efdacbbe3655cc49791e0d140cf3d7adc3adf2182ff9562d020917d4d1a20453010321b27383190f8316ea39f6480035df29ebb3a30484534fb579ceace1169ca6aac283c3a509d952d53db6d502c938455b8800a4fb8bb6976032c82243e259ca59774fa3c6f014ac6c6b9207d79dfb7bfc7fe0ced17ee65ba882580168590fa42662c52e0220bb9a519c7caa39765d2133015087e0964320f3e943d09b44a66e5b86002774d5a97d966a2a11301ca4e05b39c74c1897a3d49970a58a7e1d7bbf33d499a7f21709fa84615b8db9a4b916dad7d17e29613f4b0bebff5187655957761319575e39cadc91c95f4585d3135a627467f80949d50431e47ad887af139363ffe2ac384e44f28619e7c285655cf5d7cab044d308b05a37cec05e7fa264c0df1ad12fb084e4ecfac829a3689764cf363567b8ae3a7cd37ad19e6f715f9617a2b9778d817ceaaa1f80b77ac9ce1061f7c9d62d811f6887419a8aba6d253a1269c7ab489d8ab3a962e747cd219cb7b8ee7d3c56d631bfdb3b238eaae81f90d38d34befb7547e7e0992c4a40e9799188aa18d5c2959a26016dce75109140c0b6649d3e896d627e698b4821b677a6eb77107b508f009a9465667b03140d2fa35aa4d6ff50a2a9ce4949c82f3b5c67d698bd05a06166444f81342ceddeb32cb84fc040e97b8321d4d12faec6cc47c9bff12fdad264da7c908df172d166bc450d86204af3fdef8ae1c59b6988621170b1da2d2f863f5f4ed9e71934cb7d233afcaa3dd028b82c6aa9c8d5f0e471f9813a7a971ae1d2ac583f104d358afde48620314cd8c346e0f5863a13ccc766eb0e16435c13f466facfc2dd702db6b8edec6bc52f1a317c6aa7c4578c2cfa204c61ae0101ecf670cb2ccf052da660a5ddbc1b600961b5738c0c0d99ffd3d4f37fb0584c3f2c2e165d41ae077abd2262e19eaea4fbc45b1c4441fe63186f1b1616d5e73399b1699b7dd56061cdd59501116b20dbf62f5654e9246be8040bcbb8e5fa109c6090140b8d76fd2f0355104dae5406d8875e1e8833a2a0cd960c554867e72afb03f51a3a76fd35bb2759086223b8b74b1061cd310706da02605ba8dfb69e04a29e898a736769c6f41e9c6ca6daf2e7dd9603b520d0498c8a17a593495da897ef6eaa06514f588a484033681450e7da9d5e0ed7b2a0cca609776073272e72cbf9e19ba9b7495a3d313c23b81204faa7b41ccf31a9305768ff58acf5cbdecfda5a03518d5e804d9c3ac5087ee18c35dcd5eaae254e10cabc3ed1a5e901ba1ab82df97a7b3f6060ad3e8fa4ae849af597a7e482d14ada168ffea7a8344254acfc81b071863661ff5fc3d5fafdb0c9f25213484e85beebaea0cff6f2789f20e72f1ce944a68e869014367bf2d13b739cd9322cb85d245efd3a6a2121fc7e1963562fe32d0d86c9d9371be025ca424212c0ff565e7d24f5af45f0c2a706197fb9b7ef0b5a4b9df1ba74e956b4b61748bedfbb50547a0840a61801183eb81324cda3091a18e39e2c96f71b33451c23ecc14a471e5098d0ded298b51eca79709e974958d774dcada634a7196ee2d8eb4ed1167da02dab9b25697c322efb6af455e84f3df0ed161f700b4a99a8d1f9fef1f81c4092ea85971a288b100dd37a7e49cbb7eb95d20db23342488666841b3c6a4f0772ae6a7afea38e33aee6c7fa545deb34f0be46fc268818d481a2a4ddf5763bf1b2804479f8074954c54fb5ed63c4014ca995890110b23b93c9b706d6567cffd72b512713dc6940778a2773bf7704ce365f73274e81f8b03ddc2f53e0812f64ad82eeec7cfbe30a5bbd7349ef420dd0d89c5a28f181b7731d19336c7967c094df77dff58cd6b6b2a70147334e6fb9adc44fc9af40ae5ad21ad5887d98786ae5facf48033dd99c6b5599cf01", "53", 1, 764638258, 0, "69bb8f7dcbe3a0d5ea01b24a01254eb62557e8d706d52ee3bb7b17551959a0f0"], + ["f697f05d017fa612f56f7c8c0eead46e6db8e511d4e97cb69a174f55e3fd7df1feb253c7e000000000085200636563635351ffffffff017044d70100000000060000006a6a520000000000", "00520051510000", 0, 1837266794, 1537743641, "6d159f9a8942ff230207ed3718a60c554ddf96aa57e6e5e2b5cd4170f944169d"], + ["030000807082c4030250affbdab0ff6aa3c3b0264461e24af180891feb7a4fc4a2f167cba8493a3f68000000000752006a52516a63ffffffff3a9b1108be3d0eb0a1a9f0b3e1912b8af1d2d6855107953a77243e3a97ef834a010000000663535153ac00a178f841046a17fd02000000000038eb620300000000040052006309daaa05000000000651525353636af7cb900000000000050052ac650000000000000000000352244a0500000000000000000000000011ededac36134169d441d0ba40972e465ddb1961d9dd4e1c4d85446c3bba1f3f532120ff40409ab616be6327cd7927758db06f55e3449e54a3604f361be62821c44dee4370bdf479c6b6a397ce9fa8b37b7042b239fbebe9a0d7adfd8bd667840000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000055057d602729421d781ac5bf5cdfae41b30c78012233c9f7c95f7a21405c2f227258de3608cae9cf3644740806b5d78b3bbedc589103f40c896d12cecf0d9a299e055e22ec7993ee7aef75b5a762bad287ba6789b8e5b663a2d3d9a5eace13fb1bbfbc70ddeb77c7ce3eca9fba989597aad6b33c217dbbc39a1b6697923747970207b442ee433bfd13cc484bffea80d771e906d7f04db45d18d217dde16fa228150220c05170707e5827c608e95bfb3d3afe93c4f58891dc82b999136d0ebbf2093e0a022fd07745881491bb29125b0550fccb8f740c6c87b2570f3099172c18134f4053d3f29d021d12e078986afd930712308b2d887fc9753d07f83fca88e9a0047e021b30f3041ccb4a36ed7cca652c5c9a0cf1748f49caae3b3b8d851e8200ac70da020336190b6247806d4a822b46a6ceb86d3ad672e043dc2f02f8a89acac0ed515b032b4b1d2b82a24f1ccac5118925e41862def539cb2bc78fe1016d2f0c1cecf6550222744d7c16bfa573345a06bfcad2bff3131d14293e6e24f92bf7dbf8b0bdbeac031a28c76bfc0b682db3cf865d034b47cab641b00814f99f8c31a8eebb9ef9485b03b1c47cc7f65491b97991b28dd1722d20a64c0a48f30cc3ab3362ee1158a0d19bfb3cfadf386bfbee94131b46f86cdd96130c86c1293cdd7c3a5e2cb7086a333c7b23bd50533b2ef654d4f4388a37b2dd8278f7a8694b3ff086a9070b0c019e84ecb67d8dcb2ca07c06e1f199656770a63023eed30de054f7a09b88176f5bc485b0ee9f141cfd476717fda41cf20e5bba3d7f80fe8741df4ee6dbae9c225ababe02f8f543dac6eb3e7a1708064120f01ac0dec4a0b74ce2fc5ce868c7796ca5fe60418ef170e8f755dda9f14391e7bdf1d37da0c00af94c9b3eab75dee0d5b45d080d2a02995bc4c14e2298a523175e573bb103b8461e1183f7003feea0cc154a243e43aecdaaa1a1623101f5d49fd2a91d2ccff031ecc625e687ae1c39d1d04145fb4987feb058ca3f92dc5f4811ddb86fc32f7c4dfd59935ad42b890dd9037aaf2aee1fa4c018af84877103ca106dc63d0ecba240edb291030dbfd1a7ecb4176b038e83ac9d194aebab8c34add0adc8fb0c780d697d9d286df701b7a13162222e0f36b83b0cbc6a100648092af63879ee95847cd8b35dab9869cb7b6bde244a07f567ae1437595b03344d2b6f1aa0592358a7d4a3d2e2d6a56465ed030291a7bbd27cb935c5c46490c1b2b4e728bfd9b9bf3949e3db269897960e5e62e50d1eaad1bcffe1dee5c6dbb9b7dad8adb1a79d16a8d7951b14fcab20b4f149f7e8bd5f21cabcd876f52777ede1ef1e4cafa3f5b911900117100fe8f342f9d5bd77050f2cd4d2a561c01aefb0f33c402ff329e9debb647877af1691ed27695a2dd3857321fb822a8019c6fa1de0cbe2c66b7677e06c01beaebcae5296cc35014d6de183f21c1979f358b7931ec9a229ae5617876e48af31c6bfcc8dac57e1783e2e3b36fdaad98b0341fad00c6e0a2772f095c155ef9c5d01e1e7696e2306511468f84de3a0745dec7bbb8597cd6ae898f98b383c46eaa2c4c561796212bd3a23e5ee761cbd19a6068051bf250f7049e7560e2ac98fb4b2d57ae0e8e77b08ddfb421c93845fe79652345c5e02b940dcdd6fc76139286370a334d36659834f44a3e26c7eff1b9aa95c496194731fbf17ae6cfe1fcb0d574eb5849da115ce7e543495b985c0b3b987e67f4242fa90a24ed666fea79e5404befa449fb79f301d0581afc15f44bec7359b832a87fffbcd74985e833d00e41adb3b379d87e9e8cbfdfd5742038b7a07403c45756ffe2dbb2347079c81a11e1e0e9a20672597f333e4f9ea91687a17012d27960d885e73904c7705b06e32d75fcb7c89290823d9a3a8adf1c5f08cb5eff789db222a5af0558cdc505a3e4711bc2f267724a2166367af7b8a7ef71a500e9bf5a22808adaf51a5a47e4b751f0d7d1f7ab5cee89e9eb455d46123675cfe036c16088fe36dc8393ef809998b5bed5d50a9c2848241bb6df49793a60c2fbae1e592cbe79bf847f47fd913f41bead3c9985580d932c23e0fe8965037f98a55b63c228f8bcd0df01ebfcfc61937b19dd6608c682ba0c81137268204427499c5ca607bbcf9e122a334284aed708a3c494aa9d856177cdb2b6c6e64467e9b88f39a2df84e2ee8804bca2038c4907ffa9f4c858e44ec119b38cf46f9c153d27484391d18774877213dc0dfbfc3ee4662984b6688239404973bf7140fc7faa493498fb9b446815706a4705117329e520000000000000000601a6a04000000008c7bbceedc88e4b5c0654f15d093c6c323cd77fd60b06daf08253051903bf37f139dbaea51f85c8d3626fc78773775e60a7d6b2875f914abded43d003183671528ec3f8fe2564a662260dda0641b43df44f81b5a834fced4e19d5e704f0de0820000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000072cb6c8f0253efb0064c8c3c9d899c306f89df1e53523ab4851645a9da85fec1d5e9e98b8e8e50bd31ebfbccf0befb39983a007c335b912a383747b34b13b20b0e6f1d3d91c93533c9bac8775d9eee9fb5685308df7c2a8ddecafb98f9d295d7de0a9e23bcf27c47510962f27e094ea75cc07d9c2eac85c0da064586fc40a76703112ac74ef99cc958f5efa4a5a6ee7e2faba58d856b88dfcdbd8fb6dd6246ab5d020417ca46cacf89aa285d02c6eff7a21da4b4491878af5ee7a3a851643be06c530b04732588fbeeb15cce4b97a9886b7c6ae81bae302d557fbd4857eea374a53f371f46fa03f4ffeaf9c3489156505b513b19dfbb72a277990cbc3d58e49e67d49602274ee941701c61a31ae84f3804e549f076a1729e19abe50e85cf19173539e297021a8648b314393f13b2e64e2e6db459b11f63f1e03920725252339da851e53375030c18d588148254d423ea37a61792375e0b811fa00c47e53155439032efe909f4031381d22f0c27fe71c1abce82d400bb5f39b4812bdd82410c48f445a037f4c2c70210e32196bc2368cbf4ba5ec4abb0fccb276af1d13be67c417086cc4868d5157bfa0e9e1b06ab2fe5b0bb5108567b2fc91f407f2847814910d2a86a2ee23f2ccd9a49310d6481f1a977bbcb9e46394b836c1f6505aaab3b67535a4f2264467ff4bd99ea6a04452a34757b99a6de500be61a880099361fac1de25cc5a991329aab754e25a9165c956d1c2b76d2b7155ce50f42143f0380ab4dc9668a2428909a73d71770542fdbcbd9f094bfc18d259d41855cc45ce4498e4eef54873dce2de21bab8b54b4721197dcd97fdbee5f17e82fb9efcd5c32c24915fab80d2d908b57624ca05aea863ab0bc2b980884a40a427429b4aed5392b28147c1c1fa0c54c1e13a24a72e35cc5089711b3b1403923f24c80b1c832b6052c9058474dc22906bb24888fe461a1152150d1ca998cb9d3228c885e6e00bf57a740bf86d52abf68ab0e80ab9791c1b98da269d04ddb1ed127f24cc619235551f9a14c5515ac3073324cfd2152f615ef0501d45fcfd2fa3edb39787a83818fa5bbc5bd4938f2b23370ca73fd18079df5d2c8c0e96f1781c6bd53adc1632985fc98efa8457ac431ed696e7a568faf8795cd881da4f962198e53edbb60333cfc4a02462171e49c6df3da4df875f8b42e520034e8cf7aad8d385648b4b5a195988cab733efd76e6e3ba5f067e1f8e39185aea1ecd1b0f6a56b74469772b1023932125c983b84c8ce48c1003c75cf59594647947192a91c45ba951b7c0e66ce875629b02b8e1316c3f7369edf61f6b397f87e1a219d65d711df0b7047a2829fb257860ed2b4ffc9e3c00545f9a23d772efed2cfbf1810abfba1e4c3e3c896586ae5be095dff70b1ed9432ec5997c3da344d837bdc31e361bdbc7623f425435ba297fc86d000418aaecaca3cb73d30fec1504cd1029197ef122407cc508bab6105c302ead193b49c05bc9f6ace6b5debdfe1f25e5d2056230dbcc63f41450ad687e0470df023c6b0280b0ae7b6ad05cb39bec244a25b5ef70eb1d694ee95525208ed0dccc4b3287aa4596b06d650068bdd4108e1c3625ac3412d6d691dfcd16563fa4fa0359952ba10b0e709da037729d8fd53cf1b9ba8b166ac64da6ec8fef1c5e94b243fa55b8e23e5cca5bd618fc90022290bae5da3acfff06ceccbd8000dee28b09c83e9489796042231a38421a7964464ba7c77a47c078021d04c25f0ed737ca019230a7095c0e1dc94a9bf13f508631703d249064c3b181b614b76b136b9731384605bf1422dde58d77e4c3ad0bd1b3cff168d1995671b4effcb6f344c0fb467d052fe232b44c169875d3677d7fd1ae2195ef65c26d3d70e3475154307398966760909436a9443a61179e44ff6f37fbe421fe152d446a51e61ed4a10add50ddbd3262afbdceeef742291a1da48790dd9a882829b08dd6c7762fff7a72d38ad6095234ab7eb3c7f96ad95bdc0dda253cd922f86aded741818764df2674fd7d4c0908e505360d61238b17d081304d6acaf3ac14160328fa7dd24291473922d35b258ebe44ccc6c43448e59d099a49ba0642f7f7c2526b1542424b0f1b2fa8eb45a26ec8b83a30ddbdc62851ffb9b3415bc30fef276d10d7b381ba1284147d0afbda40f46b575c5def525586d27cf0df30a9c4e50f577ff6ab753cc929ac96ff4b84ea46f7caead6ce33e26618c7934aae33242768b757eca62136a3b86df8510d9cb0d22466ef44347898ae90a31fda12a721b55c93d7c7298045470a7fcf6603000000000000000000000000096c41e992b37ac44cb8b1cb34f3ef6e0ed7c6ab61082d1223e07a8dbc939df5cf81cac775fcce57996ea326e5b7ca0f00402cda14ae6816dd17c0e601ad0150ece14c57db685213615854954d8bc281c632523def7793e74cff54708cff912f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b06bc33ac33214896d8e018c1b4db3f4d64e7254c33f7e1cb757c979473f388b5a472017a2bcab904ceb297cd232baa7ae2b93710d07c87c9022842845f8f6dc3ba2ff054967d1e914395be79107ab1f95d4545a3650e249fb564f399dffd36ee236c9d54dd714945ddc5e7b61fe80c28c28ff442d4b6329e36793e6bfa3991032d90549a1e8d058929876ab059cb6c4f78f1d691481c84ca11798b44daacd1e80301b07a4f3c83c8db75a4bc435a0a79a0297c81a440ada3c3e1e748b518f485350b03f46cac43ac48c15a407c3827cd8460a37d1da29d7fad5fdd2a68ec800b3ce3a375ebecad18db81a913e02500dbeb41e99b5e3cac5ef45a1607ecb926dfc476030450ee643c93791b252acc87071f19244f287de724d2ff8fef21db88efa02e78021aa4c0d8d9de18c6728f0b6c06ba65230039efd890c6c74d7df4cabe3ed28769022ed50ef1ff06d662bfd1bba56925b716b4baa77a66593114645c0c0322d9170f022f77bc7ca3a31c38a827226dbc731db0aeb28d76ce1b9d4b0e1ae093481bfb54032aaa4f4d85cbadd1dfb166c67f6e2d1a59fa1ffdd4bd08a60fa0d27cd784c4f84cd486a076a0f0908eb86966f76f628b8c63fbe82aac86239a71fb870c40e53f8d8e632a351810832347e1c702347429ab36e3f9fd90ee237d0d7dd96f5ba0be2fbfe587655fb8700203be328c9833e07c1d437992d760ac6e8a89123f818b0db9cd28894f5b506f914a32153cfef90c5e3598e54006629de39386b4c8e3963b1516e44dbe405bd3790f00d1dd96d3b84f5571872925bcad59846f472b40522e6ff38287e15a4e5617dba37380f1727f2e251221b780c7d65ef30d38ed0ac5dc6d4860e7774d884106e8c33b957c8436b350c75008bb7a672fe021ea86481f290b07e3ec0213baa3fd36d8fcb7291de7e837d6bad5a79e9159eb89bc03c1a2eca04411b8eb794c123c63fd063c7bb48c7b9fcbf8ec13e7d20c47f270ca0f478040dbfb0160afd9d1bc01c1114fcf85f0a07438d002ecdbccc4d50afdc46fd91bc68b3b00535e81fc6dba6ab2af96927c424c5839e1975584c87962955fb1ac096dc7c59ec1b924578e8c25ceddcb59fa138447693cc8b21643630fef37751120e0caf920c689342df4a4e8071ab92bfe9fff4cd934dbc718cf66f21e4af68d6fd7af1ed5e513696301805de107111d87a0acfe2b6ab9b130a1b942bffaac7a2f7073d201ace379930d623c8560443ddb327c6af3e9f3741a10eba345019fe14ddbd9774bd58249eef57e6e42013b5b5eab92167b0d39062e33127e6464d733926d1c0538fd4061a6376e2433d8e8e705d2dcf9a00f77702e100dbda91dbab136b5eff542a6d41c457569923f164f60ab24bde4dcbba2155cce2d2b56af6a4ad8f9c59c4b451b319e5539859baa65c6d870d8acce0bc73358aaafdccbe27ba29dbe833abb840d33d87e344939a68c47137ee88a7b155a7a8bb8917e5005fef444a5c278768e39e3758e30a9c815ea1f26d0b67f662f004b199a31621b3ad25d4b1d1c77cd0fec9ac0bd47d992989da0b0c64690fda765f9dc7c64848dbc36fd091eb825c2480149e5c575c882704bede12ba40bd89984e47fb060c5440b863c6c3eae34ce07e3b464c3df8f4d9eb5c502b091b1fdafe0c6c1f15e5cfd6008bb0bdb7807882ea0cb3cf6b037c7597f6eef22f00bc2337dee366cdf1a6e56c44927279a3edf30429e389d27eff4a148ede963a421ee1707f522b56d003280ef3e5a11857fda140842679b53936ec7143533626c83a1cbd07e3953b7e62fbcffbe7ef66dc8d0c9ab8c75c559f9715da66c6df515fc0efb64c9b6c7d293bbe78f51e2c2b746116be43658fb4d5aa5ef03af1c978d8c0603479cf087bc1371fb013472b3993e157a9d80c807dfa4dbe0957bb7b33c04040a8e54014750be7883b2c054b271d2c12c2b6c3ad4919e051f182dafa84efd1c7f44345934d3295d627d516d3fc972c2f52b4fea11238a1c47a1ae6271fce47998ad178d87d327dbfa73f95aed92ba881328e4eb464dc4fadf6ea6726dd5e1c7a608c55e5b0bbcb6632c9a92bfbfd78f86d95a8d6cad249134ce7caeaccdfb7a4f722cd1e8bbc92bbe8e19b595ef3632f33d0dc99d969902f2b1792a5e45332f87a55d38865a2c9585a8f55321bbd07e098918df4e4152ff811595b7d5b0af0e736509936ecfe42e74494d205798ad7b76f2d09b79b3910b9681d584dbab1cbe88736a2511eff25ec2da97c7705bf9859babc5b93a7aa235cc5060ce29c8457abeee3028d10de5efca909ef3be0ed05dbd1e2c426f7006676fea06b2b0fd5e731db8097d22139e8cf3fb79b22380d94374034a223147fd999669cd5d6558451c3d9ea437f4d8e9ac70d0774792dc667b43c00663093f28523545670cfd08", "5363acac65006a", 0, 1134508940, 1537743641, "c86a565e4ec458487c8114a21c28ba00473ba7a7591d5b876e58c8b4fdbc55a6"], + ["585dbf0f02b374e1c5c41dd906f54007ec01d33db536475c46b98634a9080a3c1a13943545000000000953656563ac00006365ffffffff3d62446244e31f1e636b5f2ca9be27f3a5cc646ee17a014c9d369984a73033070300000008510051ac00516353ffffffff015ad95e0000000000090063635100ac00006a1ab9c26902000000000000000029c64e05000000005f2ae5ba47c1fb6df5ccba630f40c579d9613fd4e03878014dc89b5bbee621983d6d9e831fb05c00e5722d71f7eeed1b4520dfed3aa8836140fae812bc258302e4ac521ccf2b91fa7cb3a6c9b720cce682a1e708da0f167835dd373ef5f276a100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c678051ca646d7a19a8327666a5ef31ed76e08c54b4ac21622e4e35f4bd90b31328b925eb164b9007b0bc55be8325973d6ae836f7eeb90ac6fc4a0769750a73e88ca6e46ca13be18e20d46716507edcbbd10220772f18780686777f8de9d8b24e5d39b9d6ac416fae70bd44bcdf78e5b38436052b410d90b349e32a3773187dc03032adb222af0ac4d6986d46fcbdb25a1252cb0c090919fd2c64b88aad70292e5031002963e4c308497406eebe295b84be28a30208ac7dae548677bf733a0bd7e640b0082871815d13d5fa0863c6e3e27986334f4a156f2f3c29f216fe0151ae01d3bc414931170af6c4b284e7fbf5ba599dcbd11b9307993af9d82085d2f094726e303115e6c330eb983cd83d21e469db49752867bfcd2b2e49a9390d8f6d4bbc7ab58032460d4590e4d277a13bf8d4b28a5a969338ab1ab893b48e946c9f06517b82fda030d09a5f918b160d75924facfa37ffbcedede141dc41c1648e160687ff3c0658b02117bee42321cacfbb1df7d3f8902ca81bdf2f3651299302afc127ddf1c653e71021efcddef5c5ad1f1168912a65676211fc70cd0f2de0d542885f23a3cde5838310d5a78c5162f3d9579c7aec3b02146931f343cf1a51efbe5d7175bd4e878fccb7228ae8ef7debae995e6e39946efcd1698ab83194c61fa0dd26afc3a5cd21971c79dd8da92a3da7dea7ac601f60bd60136ab31645f2ecd9fcfaad5b60b1c83c13e3abbacf48a40d36474624d4d3140a3f976afa53e676e4bc839f2108a693f268193bf648d416634eb5e021d85dff2f3ba9d6f007abd9d6cb2239ee701aeefe0a1d7c509685bdff6c01a79e3a0a396c215949d6b7e01780170372f82cc4a46f5aec2c6480358a5512c18f4ad05f73938b877eaa03f02d389e9206a233e1a52480ab53eccd5f2fa1056fb1ebc711bdceb18ac58cddbae9f5e22a073339c2d2d334c8b5b027a28355f5c63080a0c938a1603e1bae50da03fcdeb15277e4343769694e6866e54bcbb6f43da10c1baee96d3cb8b084bf7a7c2d1163a72294595a1c86c541ea5df6808707ca8457ed9754fa3202d863736a2a8f13d43f95c97fc900725b47abc2db47e054853c4ede25d0455553e6325c7fc3f62a53a35289dba589a5abcbbf52e83cf60bb0d7ce4f6c4f1ad29e49f3bbe5c7d9bec90f5196030da0ae7d2f53012ee3da57a1e6211ba97ff628c99bdb970f4e4eef5e5d9876c7fc745eba80eaaa0ef0ebec9ad7d5843107d9010ce3b16ac6c4f7ec3bcfba69e837c1969684d11bf8a84e73f6daeebc04172a59774981e063a4d1f96c034f10e5a2530932e316dbabb094ef385d366adc6181af7796c968a5fa4219ad1973f85ce055a5ee61041a41cb6820df114a97376ddd674cddc2d16f58afc6180d297d57b1fa732b99e5a0287925417d1e0f5294a734aa18bc916a96bbbe546c9bbe396bc544df511f759199dd5eb35a6d142a9e6c020b5cc18742dc7de148a7e256b514187bcee7fda2b3f1f72a1f76bc781e8baa6c967a288fcd0dbfaf31b6cbdef85154d524f11a8f71f72d5324a03ee73a2e40addf8a1c2d0736b758892ebd85550c6c435c778683b8185d4b9679ca088feaede12cc420d333de5b496716d9757d39ec7766b32e075cae6c28d22a39775529dea89df549fdc4acb9024450bb0363568f886aeb0e0ada8d0bee4ec6778c09c11a41a37b4b7f679587b088ee60d9fcb3f6721f2101398f3a24642dce0d6eb644dcfacfd6acdf4b4649963a1b1c7d3305ea96f87f5d91a40492ab20306e8464eea669abaf954229cb9acd6a203ca1726a41ffd2006f64a8a6f82dbde57528e6213e4d367d9903e7d65832dcd5150d2e24d430dd4b03cf35f430309fddabef7223223825fce4d7070108cde61fb9cdb0fb03bdf5e5cd1194b668406a9a2042ac09f72215ebd7f534f350de1711e5a488c916e9587c8918e4402c7b6de7f877c6d5d328c73b9fbed972698118b15c8acd7a9ec126f09c4ea8c23929d415285cc61ebdf9f639777ec05e71de9b27f4e84e06f1cde546dacd142301508ebe4a72aa9efff4b6ee9992eb75b043855c37f2a6e636c4141572e8b00e71f26321a2d635f97ddcfe257f12020f9ebca4f0d61fe7f249c81c9c9e9b024344adffa6188d9e9499c94ade384a05c1644e0bc26f4840b5d599f7ed68211af6eb8852cd35c5be439925e2262b5f02df66bdfcf228c5707ef9d1d32bae49785b77379070a547887620f46551beb07219cc0adf1150ddc5d2f6c52559763906b6807ea7e34cdb62d1d2ae58aac37f11c03000000000000000000000000644769f7f3af64674efe9aa3f90d38b046179a06cb1b94a2e6c49b2fbb84712f1345687bff3a42b9505dc8565928fb47930bb5fed23b3f9cdf9b395f08777d739cf1940e57ed3e91b9ffa5d588336972fc7ad5765c7c7d88c61ba2695d8856c900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7866fe20718db6626767dcfd2677e797dd94b100b44b04d7338f52f90d21e6e40ec42d2618b89c75faa9fbce829da3c734003c4a59f21ddb749f64172ebeabf42a4e6e8e948315445114aa8c06b020154a1bc8cfd4921dd047036f56c409d6024c14aac6d1e17d0c5e7a49a3979529b42386e3c7b09238b8f9c88838c7a3a050306da82057474f31301cb61f3837751eb91988ec5998d48998bb1c9829d911e1c02159f03ad724602f6647dddc13db23d796402d9408804b61d5692d99268b6ff500b0890c98594d2fe6dfd03fbc0c036f23d639277d8ac59822db65692a3c30284b8cb54e9bd9b13e348a0b96d8f54e2c70e5b6b7649725c15cd8b868454d5a2dcc5022fc1c131e0265fafd765a7d9f52c5f7e5bdd2fd1a97da826c25d2c86f61da03a031920dcc33ca92b1a38084f5eea21c0355b7ab913fe649dc9f93998197012e16a022c28ec88250f4e6a0ffe3d7b0dd157177e04e6c3bd577d63c942bab4bc20c192022b007a8462eff8956021ec77bcda2f7e1d04dcacc55f30d28da72b2b0902700f021e8eb87b521da1c70a4e0941ac8860c85c80c99d02a96538d1d1b96ab076ecf070edcc288bd453d58fbc7954546452e30bc2604fd10e84c028639279f2f691dca5ce208c722f723b9fa3b9437597762888189dff10ba1ea2ec83544e4ca23502ada4bf4bff776e3d92ee529db9776cdc1fbe24f59ab11ad3193774dd72c30469825dcae5ca751059dd9f1480843c1c66d5c4159226d6eb559de8e0c92f17c1e65b1111bc72edfab48c9b24e3195a807f80d46336a1e895df7e2229ecfe213cb143a22d0b536414c7887ac5252d20f80389beb31490268b2f781b55433456fc15f3be908213a1852c82810dbf399e67da2857b25d29f715c41d85411e6855be5a47163882d0e13c4508abc8e5a92cde8e128dc6c1c4a056387b8a951f245970bb287a8dc55617be08c86f4c42ad42a10375f9ea68151d457b108e7bc548157d9934107ff9381c0f609d164cc4ba6c4cea99eb2cd6b7368daf377b743527e67f05321b14bfe4dbcad301f1eccc8d963402aa4829a5e4986d7a5d6f9a5149820c8119ce365c1f7129146c19001789d5bd83f58a51533ff7b7721b2a172c59796ad953b5a36d394b5a7bdb697b30b22527f14a22f25639d21e6b10987906778fb4dc59f441147abbf99574bab02b5c4ffe6c91610be127faa0d025f9b4fde4b1f20cb9fb51222eda38f318af5899c18240f0bc1cf1e4a33da5c705fbf3c224f70f8507b3cd5367718d05c9e019f465120e0e82a214fd43b7e3aa2340a2ec9b668462f97d6bf5007410bc56a52c122f3158120519d79a2ec0e5cbf70b71d480944e7365772ef839b9f834c7ca96d1a0fc3a1573d283cb078b8eefe042bf852b2d646d52e27667ee6df8d125e032752edf9cda8549266249e6473937a89295ec7fee41cdc940af2f8c395c362580ebe38f4c6d8c9ea0b5e5a1d6ab3a76e91ef55f5eef779473d18047c44a4c8f31b9cd3984be11d7b02f35e5c443d9f230134cc45a04a189aa4c415eb29593f9c43b81d0d990ccc11624ce3a12a897f2c071704ca1a9321e1e718ef22451be33cdb6446aba264c59504888f07db9a85e52780f9a35f932ca99c26f2e21f877f9e490a93e99f6e4404810b1660c0317abc54c3abfe8c3bdfa3cd68847aabae9dafdad395292fbf60bd67ee6e2e0b0b21930925f612bc18020d17bb6fcc88965eb1ed459622d1a692023c9426ff7d48131b1f30bb74fe42333f16a311c911e900c0de4a3ef681a667b0a592b0b96e9512ce028f12a322c6f2598c28da97cc3d1434f828077a616226a9ab71395597a94b37b967c51dc10faae95197f65d1d40bf41102cfc5a6c6b1039e23435bbe1afb8fb1c859e08dce57bf5aedcb01de99d873df07721d27777af19d11f8ee18c8e2a317d863c25d23e5e41b8a050ba7ed07d0fc1618a158df7899fb2cc4e0894a40f54e2d6abe8a24c3be25ec18020b108d97040ff8e45675e91c36d29961b2b36baf06181949b3c441e1e6036208deff50688de72e6656533a148ef94f5b3f6f66815f1c9bd532733553560da2dff5954b7557fac88afa217926c9215653697819a5c9e3b5101baf989ada1bed0b483ae4d00d9849675a72d99b15ecb9492b71d0bf9e52ab88aaebc57fbf2ee3c6bbbd1b16b8b3912fca00509aec8b5fbc343741e63528993052011cf587a3f3ad4c7ac264b569efc4068eb35dcb357773d5079b77f21d7859dc0005d210baadcc9c4f504cec51b202ce39ea5b2bc5e0a94fae6f018c4fa3bd52f788566f4b99e527d717bff7d412930612fd12738db8144268e96122330b67bd6cd8ac18264d1efcb03fda5c0280036cfed2c68648e62cd8490201d74fa6269525b2e622921d5f250ee61bac66026a0a5f3a0d", "ac5153", 0, -1139268744, 1537743641, "cad766ae30a5100ca056876bd4c5920709c4ffef8a2fb74a0338b03b07ec14a7"], + ["a8b4e7090216a3342a6083093513ca87d71a894f17df1475975f1de165c5e58f257a18bf6c0000000008526551636a635163ffffffff6fbfcc4c231d34b57ae621de6a87d5554ae9ead8be6d5543ad0db55abe065a6300000000065251ac6552ac1ddbc63301b1b2d001000000000563536aac65cd386b1d020000000000000000e8b05b0000000000d80926812c8acdc480494dcaefa9dea9767758b7f87100486f053d9e885875b55c7d4efc60f28c99aec626c2979ba1d22e77cb57cf12166a85f1c17fe25e67aac61930a9edac6690f6c71f516235af473ef77da32a236118fb20ed029243f0cf0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063304edf84a36fe4d8d4c4a2b167b61b0ba08f16b633107559ebbc47c065e4035cf613be2f4bcbdafdbfe76dd913788eefb4b90f8a19fd835682a214e9c4914e56c79f6f79768e8e1c072cb1e6986199ec4b60dc98365cf19d3dc9f736262718e848e6544be6300ae1cd14a32064361a9b147c2132c486e766e21e4c35b9adef0300de851556ab453c928377a1f6bc97f426fe18b091d17500c12207e0eefb41cd032f79a8b224a135b70d603b2ffed2514f0839703a71bcec6ecf9d133ff17403b00b062d8988599b496cbce2311d75c0464d1047a551e51c84a54f090ef3b6f11d22dd28329100ec7a2c8c64774e57fae0bf49b79e0d2abfbd78dbffd237324fd7380313535bf1399f5aedfde9bb6421dff896ec5df603b90d8981b60a3da9222d340d03240fd7bf2a4d246b667517bce0078f7e474edef0ea9879154a20ccc97e28f290031e2a76d279f887b1bb1d9bd5b760edb00d5293ab51c0f89540d8a051e92ecf740326d1ce9f06e91ab0eceba92fe21d35daf1c4aef6cb559e69458d47db9e57c5150209415b54ad5cc81da3869503b63be7468ca7f0524a3d406aa545bda352280452836acfcc38bd7940f8400685823d216a8c2e8e6e51a1134e95dae09b781950f930e34a7c46b968511f8eedc755734c60d0802dd01203df54eaa8856444bc9617e774358c87ccf7e493dddb1475ab455bf68535e3fb870c74314e70b83d8d471e5d3759aa8b4622065b7f4586c5ccf37946891cf8dc2399d9c3de80eacef6346cfb1d5527a49061145a8ad5048c4d893a0e8868c51fb782371cac94a4b01b8eaeed356250e0de21a5633285b870e9e8a8320dcc05d65a5915ddee279a8dc6f5f7816640f2df69601713828126c980a0741dfd4b4089a75d59083b63f16c125b3bf1cf92d38c4e3a996511a281a7da5b63ec41b1013d8fcdc5b8dd567a05d730e5e51506e25eba9c3101e800907f222a42c691efe84820ae62dda9f59b9d442f2e17f02efbe78bf7025714036bf5f717e7630d6681fc6fb4db2c4fb091dec2fb1da424dbf9537254684d8c0519e1b29ba6b82e010213afea1d0ed55293e85fa5a55990d184d7e1ae7820827c1ac1a3b0da941a370f4a381e46ea28692577d561d4d42df3f9f0dc5d12dc0f8a316ce84608d1eaec9a146800fd7a0a650eb819f00785e29462cee087fb5eebf8bb2315681906c3d3a96e1e9ca554d67c85eaac9278149e39ebd503a1b8aa33a9507f4fd69083495140c23ef5e51a5c16488054044206d9b5008da73cedc32af0a4b8cc4638cdeeff2b0774b87244d0955188a45e9bde2b2bc05579a5441d5571cf2284b74883c27f366c96ac058d0c8537617be443d4d68adcfbb622bd1e64f2fa807895ccb4d4591ae42849b98c9e88154b9eb54180aacf5bf5abef46e870dec2e827f808d69343e69e1500c9d6b6119b72defd22f42c83b2a23b78e3c77c4c6271541669c8ecea5b0cbb64d914515b5bbc69a2a8f56a6aeae7c9337191a03cb1206c5f62db041effd50872bfd12da0841ca41273d3d7438b10bb479788f9316e2a34db48307594e96e5e71a5179eed52293460d2b1db085525abff0398865570c47fa62def6e011f1960a1e1738b5ef7b50c2ca2a874a12b2726773f8aca04c18e9f95f5204146326888316ea5cd2f4c5f05568aec7f8722e286ec585fda2a80f19d0368cb3c9eaa6c9f4712a62ae127969071b886f18911a1a17f4162cb6885c6fe08e4d8acb66dd20d346df62fc6435c1c12728ad0de287ff5307ba76fbfec6dd9e59005665fb9958647ee91becb3f8ff890c44fead96d8e36e9af7a348a652acd17786040a1a4f2d70293bf513eb15fdbaa24ab5bcc85aa2a4b8c20bd847b8b1474589a81e90de25cb2d8a543cf86b0dda0b60f7f9b55bb43e40221b086db2f472f67fb8860219134b62691a3e82b6233095f671354f346ef855fb3614315f68529970ccbed0b35096f18655ed4bf855d4f197cf498faadbd4ba313fe5cdfdae3350a48277d4d1c4261afa20179e8e0aa1d8bdd958384254465de5176ff49fe4092b1847227244d6cb9571b62091423440f8050fe0ac0acebe20e02c2ec4190d9c67ecf4fb9b170a3aa40cb77729c9467c86e382cc113bb85735cd6a6e42c9433aa423396f3fbd9d7c56207f471f94a71622a4942ff0f7efbe9fadc35f683a9ea3e836df5ff68e41947f622e851061a28d41103b57e2ba2c12af716864936f2ebfeb09e1b14456c86c658bfee05f3484788d831bbb06f89fcc1e4626b71d0da963a40895b0000000000000000ad2d5104000000005295c5b78251599c221178ba565c51c64e4391ab83fc72166755243737367e6d3fc5ee9a80865abe1106458db464beaf17435b889d306b8bcbc9228821209f0176bf9f00152f85148a1e1ce1d7eb1c83070d0c9246964b5d1a4fd5a61a5cbda200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b7c9053d4f79e46c23efb031a1781732241d0728178ea28815e1adae178e00c0a8d53efcdc5c7e6d89c159b93b91e77772d072046f848dac84b43a6d257a15e11041e886781550de7aebf9df3218c0e8155b040ab79b18d8e72cff15f1392e220d5b447937a911729f8cb93299fae5b714a92b365b42d5f4edcd8a2e45610c6c030cda499a83c126818c82ecfc0af93bb04da72334679a229d763fb463ceea410203066b05a7ede0fded6d29e3f157acfd508d350317f40bde7a32ba81f43abb142c0a0289d0227c89f45cc73d83a66df4b686f927330134194e45e860693ff2d7fc89e6ad8c6e69af3869b8b0171201e3beae799b659dbd806bd07aee1ca441c630bf020915d245f65ee64895dab13c92af2b5d6e58ec9c4cc420bc982b6429e7b1cb1a0312afa518d101164e7d900d3519b57ce96d8a97679e593e7d1743194d3ad44a22020fcf72c05e8187aaf39f6d0819b6687efd48ef65425d37a1a6be0225b3d5332f021081539814925a8816daa2c2be229dc746e35a5ac55205d60672462e66149d730318e44908b4313c935d86c608314c36ef9b78576055d9c0c9b2586ad8335d846362069769cf23018ef3008a0ad908140bee2009c535ccfd9a53d1735eb0defb796063f46fc0fb86704e54fa76a324c0f33b8c1eeaaacf116cf589e2f5dee23ef705cc75d2438ea7a137eac222289d7df99c291adbd370ec904619b75a07ac576268e9f21f2b25a4aeff7995be7fe3ede53c892a661558bbb2b44daf836fabe4bc61bf907c43eeda2556a2827382da73863f78be822215cdfc234a310d37d49515331c2283b8351713a5eca2e2f138d4745fde0c33d352b7c96943c3164b28377f78f46e303048514099116b09c385cd4e8b4f4d44927e491fb47503461496215359cbb742b6d06ca64ebdcc379e8d7227b361f141783698eedbc6589cd90bb60b151952c5d5b4658e03023b6c701042dc84ad836a3440f8b3b89b16812bac8c1f9f9cfcfffe709ef4b8a99a3cd065f37390bc4a63a0febc0fb5a0041a05b3ff64822a15f86396c5eff0d89b1c108604fd841a6bde1f027462beec88d4658dff9cdfbc61ce3e5b348592fc97536e5a1dddfe58905006525f648eff17fef3b24805163c21d7be58ac7c95d463675f5d02480ddefbe3f5df26af6076978149d12f614292e608ad8d9706a4e973dcb5e90fe0d026180f72fbc577f4a4ee6a39ebf3c9bbeb8f9f60b9464fcba99c7fe0991df3af66957559895b7ff42412fa0ecdebb3ade5659b3b303bcdbc7963a28e5d421e2538c13317277d0618c5a01c8d56b95299975a5c53934966f03838be6c80b4e86db65428e9ab90ea2099c65445322ca135443f6a06c8399584f716670a4e9dd980cfcab10629c2c0b6478ad36f3a2ce63dfb2cbbd229ae11e0cbe843bcb3ed0ecd5d39c47df9b48d056c5065abf0a13a05c934c3c5517b7780cfabf39521b6be25700915948646ec14d15b0eae44c7236f572591203019dda5b3bf572e0a08bf2fa262e7e08cd3fbab5da71a65f5da0c2083a5868e16183429d1ec078ed6644cfe075c7f9bea5b1ea0f25777a5d979db6047963dba1be6f0943605fb2addc95dab5169b58dd6dffcff8b4d4ac30deb0ba43ffbfd6bb5b21df174b5a185f994f46174ad378718d4ff07cf229b617544c3f24e1dd1a6a8bbcdb0ea54569d130c1318195005c63eb5ae02c33483e1ef7ac438cdabc0cc13eb9baa22db09f19c3951ec9f5d577be3719d7863a2ff121912654e042d00ad91dbc48d9188f4c704ca7a5dd6884660735a1d5d041d0b283a551ddacf0bcfcf1144c18c369204571e43e0b093adf24ef5b3e1f869acfe4e2d29fd7b2bffb0f75812d2ee7725da1ab63fd09eb52b1dcf9a39b0de25960ae49912725c3bff7efa854f31417fc225d7f93cebb87ce66b5049cf967948bbc64201548faa7854d79d67795380959021ed2bd0f43d59a7a43690e53deba4493065e71e61ab5055599dfc9498825f6607c18597c1fe1298b0426e6c402bde3a9868b30cec8f89a2c0507a08c0f4bc09cae0868947f1f7a8770d52afe4b6fa36f4e221863948f57ba32ee2e2f932f63cc50136b04f344f29ebd585aa609eabf1e1d2b6f292b257cea051ad14603de45e70b875b4e6c5b656214fe75c831d84dc7e7436abff27ecb6c124e3e10efad2c79eb9be3caf1440a1a4476e8064ca912aa2fc333dfaaaa748dac491b2361f8a101a0241e6e51abfb4809d839b302759621fb02116d6cb0e7dee93e51696636af76d10013a35aedefcbd45d02cf843081cfa335f74abec950f4af0d12e367a9a878cf7105f021624144e243fdc818be5e68ab52885adccb866c9788583f06d938972d1f7f26ec3ab4e5eb6646738c37381f4dd706139a314c7876219fc044ef999a3048a53d13c01", "ac000000ac5352", 1, 1854065456, 1537743641, "20677cf0ea37e1ebac9cd7e46e88a1c92e9340e9fbbecb89b5febf5de7704d01"], + ["030000807082c40304293d4f1826d4c0fcf647f50f9ed5b1b7a5c129b2db2e05dce12010daae3d28ec030000000163ffffffffdf3ddde0245569a885166a102171e7c97c9adaba0bdfef6ec7d1911619c0fb16020000000665ac5351630011739205120621faa7f0e925e9be0e5ff412133865cca7979e8a81c69d2e89bd2a1ad9b103000000065252516a6a51ffffffff1c1a21a09430c8e73ab26ad910d4c7bf84ada735a3cf47a5d05e0e936fb5c6ca00000000085151005265ac5251ffffffff0331e51b00000000000500ac535100e14ec5020000000007525251ac516a521e4b27010000000004ac65ac5100000000f2a7da490100000000000000000c4c79040000000000f09a583c940ccb899dedf34793607996fe06c393e9916c221e76a22c4dcb3ba538a60184e2ab2c0c308218f516a4221a43288725dccf1ce75145bce0628bd67a3b968ed339848a82cec728db80600423370f9629f84c1c145e34bea8f3c5b10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014321a3f0608e46cd812f5167732d778f819c362f7f51f45b200ca4d8cea6ce3b7528232d0476f09ac4e9a2d08c392252d35ce10562efadc4132bc841b0ded148a016ccbb0d2b31cc6c2663e7b41b07848114852111ea5edb39253112f23cfb724642f0ecb71fa9e6d4326e7a310c07a070a8d53480be259c2af15f43e27e0bf02087f3936adbf4202cbe862db9515180193e502f4a6750dd42f7ac625b31cdc04022d14a51985080b1ed2731f99c3edd24235326ca2f395abdb58cb9eb76ab9715c0b03fe01d404ea4574b4b51a063b70a93272e9746efed351cac75452fdf9aaab972bbe07e3c6de3cb38fbf63545c7b8712c0eb5e8a67e233e2e5ee0f99b2ff1b08020a52526ca8cf97b90b1f1a3e2c91bf9636f74ab3af223bb068df16eb394eeeba0210af534953e3713636a3a5e69ee92a10a9889a2fc8ed080522ed02b72a2d1fd3021c675b5fd8f504bc2637cbda9e7ec8e36d6d3c4690971f2128cbfd5fd86a1bf7022d44b5bcbf70753c3707060892247fe05e560f0662df1e7d45310b768f0704c3030df476ebb6733383c3c2d6162ce680f718ad0f0aac4ff7a20ce1fddb4f7dd1474b6396d99c839e8611e329c3856c2fb51648340aa61e6d6c02aa6faa9e1844b9a6d6a8b3d094b19b8d27bc7763a26ba541db7b50104b0c32c08a7e0b903d0a2e26a6dcb893a92dca61af9e480ac929e045e43c27a2b9d64dea43561a7dfe15f12d27d78400724ae3bc085bbf6b5ff054bcce0f673adea9494cea398afbaf38fa2676ffd433cc2af78318f763e4d136a224ce931d995e30d6ebaf690b9a7ccc1e0a7ae9c31b6b7b9384708191deedcd125d85de21122e2f09c4b23a8f10037a784910c4b92315a9ef4af46807ce2329937c5f6790fbf38431ae5b3dd3e73d09d2968674cc66c963995b2cbbdfee4409a46024a07080e63cc1d71866c15fdae4d18d620c2de40cc5cfc42905a1be7bec7b294a93b27e27adcfb4b66e557b524e013ae07a1f3969ac02b133c6bf90c3481540fa4d42b8b83bd88a138021689ebd8c2683f01af2c29d15b419e8ecdff230a3f9e6120e3e3e38b46d5a43e96433e02f8c20ce14e94b1a58ea69b0f3a7b2b71be77415f4868cffe89326c15b38611ae121b4153631945f4b1bf89b649b54ff258386fe085dd8d2c7e43a26d5894d7e175d00fc66bb259a2bcc0dfa26f81c39b07be96d391dee1590f19df52ec06333c6ee921237d5cd6e5f9fa705789420a33c139a7adac49d9e586b8f6cbf6c75229b09fb8ff7537e2bd74fd16a913caaeddf53ca357532ea3ba7426214984b394448f809acbd6298aaa934697fde7c9a9cfcdb6e4a6ccef3acfadcd750da8b7bdf324839e58622a2f2c0edf9796131f93f1e53d8413aa6568c1e6228a672a6a37dc8fe8eb39ae21eb25445ef21914ab27807fd1dc0d392ac2949e0e98602462771f387c1c47824d618d7d851bd798f0e18a758754a5e471713224317630754c2bc4ffbb10d93bde5b756e091ae1c45ca233b48fc1da98d9276a5fa04628d1bf38a56bddcdec1fdb9283ca75f398fbe789b387c7d6bb8ea8f4e99d58093cd4feba919ba4db43ae3a748cff1fa733b86bb297a15147f23c201bbad003393c3e48ff00c46d38c878f3083bbd7cc8a47de425697e6ce75c99bd168bc44076a5d21cf2af3a61ad193cf1c6873bbbf644f00f628f2048548a1150a28a5faee49e1963bbc54ee60ef41d808ffd79cfe57d28993159a1cb61b71780f011c8d149b3c21b548ca128324bee0a8ab8337f10993efcb7b177cf8527931718e29f01a9a62314a38f95c6c847fa703d935cdee5e2ec54f0b47fc15ac55c4d0a68ac4c0f2da65d465603e71d248a9f0eac9c5da0afe986f538778626ac869af8bff63cd641856e8e6e8f4560f19b60f92a9ce76e7a62b5e02a7fb307c8d162d26016037377121f9eb22e7aef27ce6706f6da63185fc02d95ee349e3fcceb4dab5ae7f3b14f9fbbbedf4f461e034f54e4f2f5fccb1ebb95b0c7412cf63172a82aa1ef4550fc0bc8bce2f0cf6b048e96f4b86572fb93d66de31bc347ded49d57c3aa7dc40c9dbb787cca7c17dfd10aaa633d5eeb3549719bca32436e1f285d480e42c5b28e7c28b9395272b4d742a86b92b81e4f066bd7edf76f27a5a8c83d99ff88b5acb4cfec0f2551ec220f26fd273858d607fa5ffdcaa50ed147a228be7171dbeb7a27d259d8143475b4fc867256b6cefe586a5b251b66cf65fede5b160c3f37dd1ba0726a0f1573184693f045c276ce18238c045dd71404d7e7078dac6525d533fbd9e82a7d92ac078872587973577d7e83370e1703c52e41b6877307a82865842ebcbb7dfd446e78243df14643225a98eaf554207670797c98adec683caa42ed6c84313c0ad6bfbcfd7773b203d3f6385a432152853acebdb05", "6a63", 0, 1286360618, 1537743641, "eaddbaa8e3eb8b047e124dab59982cde3aefaa11775fbcd86144b578670a8d42"], + ["030000807082c403031632d64a1a4a9b83ab8d0411692ce09513c51fa5ca9984222507549cd5be307b03000000096353535352006a6565ffffffffc021216fba5cea403bc8cca3b6734dc5a9273be1bb12fabe59aaf18bc00a01800200000003ac5163ffffffff232d349f157158eb27ab6db512577b7a62df71934ee1d16c4f090f94aa33ab2d020000000163ffffffff04b39f5a0200000000046a6a00537109d80100000000076a65526aacac53e2b69701000000000553000051ac2564a9050000000001003ae2777a0df4022f030000000000000000d5671501000000006f2b38eabaeda60038a28ba81bb8a09831cf0880bc83204459711003922686bca66e798b0d8b2a4a8592d359dfab729b7a823879735973ac0530eaefc3057284f6a1ea61a037e6c5740504edb1511e424441ae38c29506c325266e59892bec9d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000791e1aecf6ec3cb7a3dad8bb60827d4f5b17f5c95b0e50d52509c55357d06cd22dbfa359acdd11de9fe1af8a84305c563bf333ab6a2d167f53faa4e3e67cde6f605acbfbe21049edff393da3f1cf0d76185db402803086374f193f0eca5e43972312a9835c60e1fd21e55063d6ff3b261c654b2d4619ca34ff81e837e035cd8e0322aa6880cf4e64ff397e2305e8910d73258af16addb359ee791e0ce9c4c3aea30300b63027ea634ae826d82d852bd70ea64c23f61010a4b5aade8b2091da583a0e0a0043a32a01ce630cd4c719ea95ecdf7e566f60f2ece13dee5b0407203ba6bafd9800ff945d35a3841f4f3cee48e12ac397687ab6db920aa1d92228b0f88c34bd022881f9394184e8a94590356d02c0137ea6d26f8989a36cf067e5de33923c9f5703205224e27ac33c1875516191b0ee53f011a7f5f85f02746d5585fa707a8c7b03020f40489dcca6e3fcbe1923488435a402e306e9e70dc19b770891bb112736c8ff0204f001e2513c2db8b2c0e9e851623d196bb941ee778e98fd18cef94d8f90880d0213c86eb18ec65d8a18a057e37d5a886795bb32d57eb3317490cadd13968a3d4ed2bfc93884166f30b65497428ae54078742cf280e5726dddabda75aba9e45a3eb07498962b15628167010ef35f6f74ba13659f5a81507c36e5126b4c98e0b3890fd1860efe5f95a9f765d6bf4eb9f9bca03b7231b1870423ca5211fc0c63da3a4559ef209ce4df215626ba5c8b28d9d75599dd9026fa18988824aa4c3cfd54ca694eb383f01c867f7bf1330e61ed222ab3d6df39dbe91a38cb133fa9e47cb2cdc65251509bf14cc31ea2c17327f091e9f14c346023fb4b2eefd5d8c4251615c1643217042490bc31610232657ded1e858b45f075349fe071229ebd918ada28517a9df56095f08c75a9b83415c4ee44039128fce982c7cd3cd701a68a19569ff1bf59770046451f8fdd13231c8a481b5fd3c32ac9138d58afcf99542ceff959e57214a71858b57bbd47e59d32c4e70da1c2aa3efa8b881353e0a515e16cddb8ed1d2a4a3770fefefe727e96f95aab4632f32c7f0c8c055b2805b8d345e8d66d64af682df6ee769cbaff54999fd95845b2c340a1f76839bbbdd0dcf0ab793457175e0243c7695820eff7d36ef779f1a3a07378a8f6f8d32a7473b217586cfd7b934e31904384b3e1bc720b0865822c3ada77517f165435cac5e07e2ba27d955da56028e4ed9b5df2171e3e71d61b6619b8f921f5588a8857a35087fb522dfa56e9aed001b78ceac9813719edbac574e29dd1b26e3cd5abd7443307cf3e17923408780dcfb6646f7e3110fb0e10c6a2b3af9ca61623b3b5cccfa371656a0f444182f4cfc9c84ce34bd4d3156dc6939962d1e4c6cf2c3046beaf8af2b62144428527973591d0c0889733c0df4b946924cac8a3da7abe6aeec4942d5a49eb5f837af46a3abaf31ee47f02f849146a5ac185475213279b764aa0986c98e340fbf12372da982dd775748c23c893f5ac704ab3250418e04959997f0c96d2a6ebbe77434fab2f8aa5feb080cd995bc61e7914e5b5c222ab3202ef334598dbebe5010c7874cf9fff58af6dea498f5b8d1c680903aa84fe885c2112dcd1a4e7112abc4ce3be2404b5fced6a5ca48494fe332785056a831967b0f04c8203b099fa8683cd50a42d6d497fb0537e647e6b62e745e517f7cd575340eec46bccf6d40937f87eaebfdb2f71f85ffc60df986f62a6d0478adfefb57f0862a22a5caf1c70d5b08817e818a40740ac2b2d55bf6831532432765b7340eaf3ad8b5a1e9a2c47ac4c859c0f28627afadf47b7e40b21a9551f078019deb15e5f1f0388a05e88c65e2610f850f9494182c2b7a0ecfd55449b242d65425fe9cbea89e61a3e0999fdf08e1e399412ce6e4a2f7074b3a5a2e6291be96a33c11d49e2ce09c6a34df15fed9db657c256c51e0a949d9fcaf02fc4a24db31a795323c5e4c0fd53bb16110a3ebd85329845950849b5cc53c5f50a21d3b591b6f528175508582f42bd12dcb78d58d70ab01520487f218d2c3a9c25b48af9172736b0c4a62e6f9169f43dc9c39a312b9ffaf95a8b1237f2b0c13fa3beca126760ef411835dfc9dcfadeef6d5daeb46f2972956a52178f3f08481fdbf8aeffe3f05b8704c99c1d78b2abb3ad8cea4e84313b4a92bc5425d363a2315192a25cb9c98605089cce0df02882249406f068982298a249f51b9be1aee377fa1b7bb48a1f0bd140837b633d469f78825055116cc6b349f8e499ed7210d901bc96f80f3ed46dec4a0000000000000000980e740500000000375c1a08b2ec44c163567d9265d99f347c69302cddc2c55d6dd4c03a576881377c6c1a6bbda7d47f6cd86ce67eebd96bd1fc91bb38797cfd994485099c39044df86ac6231142e54639363b853a21bbb31be6139daad3f01342f760d1ed3b143200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d2f45aa5c70006186ba9ac82686683d11bb71c1bebab08a430c55a0c723a4759674c5cba6895dc8d01a8636a6a902f58957f9dd8e72e9c77832b8b85282da3382824f83c1acd6d3d7e66ef881a78d2b8d6b1f983d3c0554fb2b0ea4509f9cd2a803ffe7ecfd1660f8b2fc5e7557c875e5ea736dd7b297e24544d640df99131c5022f62ed6fb7d80fc81d6f90939f54b3659aea7892efe48d5dc229ecc4463c0d64021c149c1a87834cdfe14d2b8fd927e9548e829e754f64666df92abf4df97036390b01eaf0b85f85bfb2ebb023541aaa50f96018c6686550ed2b313158ba6676235fd495066928f624da6f3b97baead1f344c077294c67df69cd255002eb9c0227d30329a088b6fc40c72ff448e98994bec7a20b016f5e736c15674ab75a2c739c142903035873fe706dbe2b254a5b91965f75884c4bf28a8e464867ab66e3903bcb5dd903117c99e1368c27dc3ca9382d1432390ca778b38d7af72bcc4ebafadcaf3f7323030bf582008292b798413380de9f90d9faa59155203cfa19c5a2b0babb7545fd5303255b9a44488aec5119bee76f5bdecc8dcbbe42b1536fddee5f0fe391000f44ea455081ccc9df7f15015bfa14efb3aac74c5b15fa272e88e3109edd75b5ab4f9a8621638f9c14946267e9cfd2770bfbb451fea7e6ff85183c8d83c9f2a1b37eddccd2ceeaa808aa9a57c8651afbcd094e14bc9330683090f4b96c5a0cadd38b5c5ad9ef6297441c75547d58d545dda56d4c043a865188d3910984e2a82b79911c9f008bcd68c1169b68111397156a18c42eba5079a0a67d32271fd744177b5968c71246ac79d6a6c35cc25872e27d69b320e7161a0495774815fd4ae70698b5af863d0a2dcf197acce4b784fcee71a17e631acdc281496c065af2d4862bf8615a55f0438f4777d6a7cc3754dce443a4de1682fcae594bea333b5ba19e032cc8ef8e09c76fdd3167bdfbbba18702f402e183bddf53e0a41cf2dd2bed3d60c7d3234b4f277ed44e4e273b7ffec5be086881eedeca5acaf5e4605e7e03f72cda26ee648386fa21e2263d356a92bd7cc4d107aa3c5b906ce102af91e8f1488b21c382a01c8c78d97476fa953dcd081645769c0ffd0f9121f0b40045c10b34963846031e2a1f3af8f8307e032803a4e42f973399f7053f8094d97c7633815bca8978e787904a1debb9ec6760b5334cc5fdb6bd1f12a20f85c7e097abac8bc3e7aceee829f77764e03dddcfd2cd22bdfea22b94125f966a4ed162753558129b52991334c4e43ee9dca4f00744e9379a1d4e71dfc9736c13cef5be4ccb6f240f9fd8896ee1c92855cff87b4a0235b034a5f70345dd76c52255a43dc631ac33e7a82e27a919db5a4b35b6cd4e619ea77b8885db9d1f06954bb90320af6d6954788b4c1e5572f837a90b2816c0e3b267103c392765951effdc151ef81489f19a64fdf8648aff88f67c1b8e190abcb8d1719c55d07b1b4ee511abf0a2dcbc310468e5263f5f604b73694e129a88a7291e68d34a29b371819db8152dfee6b050e8956c5379148bdbcfd7823f9d981a91db82cb1f6369705b456dc6adcd34182ff32d21f13f97790a9bc9a7c89c47b8b3ba0cf51cd1a6e8525c8f577fa55cbcb1ed32496c88da153b4850f521e71be71eb2d6c025f6c676e93363ddf523b572f1d5f48a356175549d30bae932f71c10479390341df3283cde762b56c4784790be6677c04acb465dc864f275284fd82060f89f4c7c0c5dfde4b84ddc37fcc9c9738ece825306c22538c1ae31969ea34a965bb55c235b15a7dca16d37a6fc8a4db13944656dfb64f17defe5e8c67b52b6673bd6aaf21032887c893b5c6087c0194232b7aa1a97a2d5d897272362943510e6d529912f28297789c64c1ea3c258185b2e9d76774bf26c5d3abeb182ba622785c765b1c201ae8a7b7837f91d4656fb9f1a1b5f0b589caff8ff75838e16e93403fd5080a31e675f495b65d7aacc36ab2a43dd5dfe268d4554130ef9f5b78f668c4e94c787b5c5d7dea7608a6a0284de08f010f75ca2d70235fa03965718aa257c6cbfa0007f404201f29b2ef1c942fa8c52b74064d2bcb2848b6604f8c8d19e4ae89ede97b55f5a6f1dc75324a920ae696513f502d7b25401fc155b9c7e980b212dbc34fc306d851be51be144bf7267f2692bb32bf22f1d29af2a496a4da8aabcbd39c2f618ab38ede643b2ebd9e663ddb0827a009a096b0bcdd6f6c892690b113706ea6aaa394708fd0163477b86024c828efd2e6f34fc10ea536843be06c24e4b83ff5b112db32a82756301000000000000000000000000eeb221cbec58acf6b6e685a4c8475cb6be534bef4f5edd3c71681386104e7413a3b8dcce1333771520173fe00fe4029b0632c90c40f528c78fd5796cb0dc23b4b7713b3f195b6a00ddfb1af6cc3f19c915755c6d95b6f693d5f3f42d29b9c6420000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016f5d65995809389d8556ae6d45bb196b2003bcee9bcf87efdbe014ecf2c24dbb712a025619b67eb8ca071ac1b29356ca1e2e800f2c1a5a4bd9a8c6bf9672988c2a57064aec9b0fa0cc863c18da24293ac4127cc426b5c5b5b66c9a80e4a55c4472c19f12d06685c0a41232dca5972aba1db67fe65f41501e2134a9bf80b6181020ef179894dcb5c9837b159a4dbef1bb882cc245917fd0a26043707c44a99847c0303733548607d9eedc9d2bb0bfeea688bb9d44f2904935c9ffe46d31d112b0eb30b008e13b40f943fde66c126abb040a9c0e2cdc115b5eb646876d21d64461c09f4227678d659a0e3dafa919fd4ad4416c7bb6209f2b13695ef348bda2c473d8961030f436c68a9c835bd626e73d89407ef1c6eae77c75e2e5811806f7daf39d3b148031de846bc8b4b27d81da6ac8c42cd668092f38584507ccfa3e189b4f64b2ee4c70228102c47c301f942412f4cd7427ab61e0423cf7630e3c70401c83b3a04df680a0306cebbc5fc37b0570983a8f0a6b7315793c39f5a8976aab3bb0438fe6f0cd60d020241a76e925c87069150a58a5bffa71333d577c38b2cfcd5d20543d12b5587cdf8ea44eb7e36b8ec27d8307c7db3b59f96a9eaa4d9edf3d561b92f3d07eca5bb0383da71c9b80d37f9f6aa823a603feb192dca8a2b0fc5867e6dace2320173f19ae91fd598c8854206563cfba8890a626fffbb892d2cdd5e77a026bdf8d7bf129d19a50bc9026c3bd9b9008cdfdcca8446a7fa4e322a5239de4d573f6330ed6e79b34b0627586f35f066afe9c32bb0b676dcef9aa09368e2dd5633c3fb65d616f8081fbaf402d4940d0b2c40398c6da5f4f575e18c167760fe6e0411259a859ecbe3a3e1305a1b2c2ca6e573320eaaf8155c3139488f5ccd132db435bda913c8d54130e9c20297c72ca42ede9d19871667e7c77b806a9df92138338d960759498c599e6d0476c3f6b5cbc671c2a883a44ca5b9490063864436a03e75e6995e051b18a0e8a52a4a1a3bb9471153a30f373ddaeab57a732a7e90e086f20289f29a56043e625ad4570cbd16b2854cb8230b8786c7cf74387fd10d68407737008ae662e448c95d09f510755c5eb5399f0aba321be4cd6f8ebba316af52369fc84d072a5cd41789d823becb0a1e8e4d0fc8e84e3c4244601b2a9cb2b2b4d8977ccc36599f17ca6022e312643339eb7068f527073ccb0230c1b94d5771949c74b4b0e28c4348775c4fec467e97e2d027d72f8829b7261089a872dbf5b61593bb7158c13d7f2baef406faa990a144249cf4ac35f2f8ed3da00d971acbed7186aadc2b77f76a5f10bbefefc07fb12f6f784a00cfd1ebd6ae5e03c6826dcf4679a08dd3b3a8be6be79af787fb63dd023d9ffcdda5def0ea312709909d6d9fbb447e5eb049e8cbd0de36fb1a4fb15ed082ec89c7df30854de6a579e35fafd881c4b35af14bf9a68b01948b1ea6fb5e6ee162d4a370ec06c3ba0ca42bb574d77670d81ff2af9c0aaf45bd1cd0e37ee5fc9623299c7f475363eff3f379eba90ee2dca5a8793979b8e35743d1c5bd486ebc3f173ac41b2793e719e58ffaab53b29b6445cc692f8838c9bf783e8a1b6eab5ef70b5149b06001df97c1956997d71c4fdc322bf6c386a10d9a4aa2d1ba34f7374acd8d707428cdc454487edd60695fd511ef7694d2470836edee4d0b2e31fba0d9568f79a1159102ab73099935d832f6b4a4555a55b71b7ac33575a738ecb50f53ccebc06d922de315b8373af2841c6849c63c4e3c8a671d94304cbf88d4017c44f485a133156fc366878073c5379ca19464ac57e21c4941a56a91a279cf4d28933729b15947024c6602db2bc2e2f6bed0a700541c11233034525e860075da9e79f06673159bce2e388b20ceeff0702cbc3ea2722131f150ce9322e811387ea66f1ef60f6b57bef12c7ba4aca9b8bcd61d00e89e8af250f105e3b9d10b397575a249dc571b88fe9d39f8d7da032d4e26b005a6eba5f738bed3aa037580838c45e21021837e88b0e9a6995e1918b3c9a0bc007fbf6f85215ba02b8654eb9d9132a52277211310c3e5e1e2841a191e03057f0bf86d09cc21cf2fb500f5695708c0c3f8c402575bbf79ba5541df1c52d8b68f6e826b716a963deb310dbbf979d55e4166c3ab6361442e9af7b52a5a486c710f17d4cce642c6d5388d7596ab8136eac45323655f90f4be5373b1b995df77c73b96b8218aeee26e258b7331e88e9106714394d70c27524f0fc232e629a966e89286fd4fa106ad04df515686924159523499177bd174ba3fc72eee193cde8d41ea357a17ecfab2c3ee89ab3fcbb3be3c03953d39eb3b0751fdb0e637e895108d35c3ded67d6863188fd9355b7eeac5dda1bd3b312e86cb8c7366534a2fd4ba3100626a7b5d7015c91417e1cb96ce5abb6f8694788c3f0d", "6a", 0, 429089975, 0, "194a7086fbb7b0a377ddfa403cfb20e502545089ea1e5a38a3f8f4c1beee076b"], + ["32580e17037a65622458d6a31e425a8a4d9709f87bcdcc65c7666904901f6b031491a1593801000000025163000d214d778cdbfab0ba415a6f686265e0c6b74111c92fb7a95fb1a39b2c2a8dc4195ba3000000000751650063006300d4330582d4bd655236690ff6ca3089e5d8acbdc8488704cce73495a11a8970d428d4e05f020000000200523f2e657a03a98c0c0100000000050065ac6351eda652020000000000dfc9ae0000000000030063650000000001a9b8b1020000000000000000000000007721e68b2ad6f5d0b0e558c2ccfd5201938e26df5cdeb45950d9e3909d53cae6e53ef60873b1aa63309557c9245a0370f43f483ee22fb6c186876f6468ef22c2c49145ddb33efa737257d1ed3faa6b21d6cf15762ffc075ffa3f755ddc786cf6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005964d979437c76fab906b2349a82b7511c7a6fbf38629b34ae9ab4be84aa808839783b07c1487362680ca160b5262f9d7db69a6c42414934721d3c69c497a48496c2894c96ae86f6cf3dab79be25704b78cf4edb1e618e40f08fa0e679bd6851ac48c766bc1491a8a191c08c2fa2774fc12315e846ad9b49bd4cc735a8699b2b02299b6c0179f8f503bbe0ede3bc0080435b31908a4d1a86cd004f924deda458d8030a153fc4db41874d67172b9a0245f93ad12843644ac27d47077c953bf803616f0b04d328e44acba87dea46b2f09de9e14afde9202cb9a9539efce78a1efdaa6e45de234f1f160d8697139abd7c81c10bcb8db07ef4af0e16d40087e6d5db0c67f7022f739643273e91ca52bb5b68a859436f34d279fca1c4aeb5354f18b59056a4d3030a53ed837d39e270b90ed92fa056ea28e7f4dbccf1437e7d35f29861b421c3610211b1b3d8294de90af5428d218b01558d3cd9892378e36a9fe09984c0b4c730a2030f9fef57345567b07520a90c08b00fd36a16ec7b6ff1f855db45f12e2d4ebf540219792bc7c1f48fd20c0f28ab3eeab417bb9293c6ed0a2c1c90c0bdaeb68cc69ae3a3da8f26328fa4324ebccd1a9adbe5804cd175d3dadac14be2cd3746328b33e114ccffe40091ebc8f4a81a484c5686af2428640b4dc8eeafec5fcf8418cf398bfa208efcc74ff3ab88e53537a5fd7048931415b6634f373cd13e430198bd3a1d0c1fbe2c4211aba4e6efed990f70ed313da3237d036bafa4b4095818d78d8af3ea6283b8aa9fd4a42b6b196333f0de66bae5857ad4306d376697cd54582480606e6be54972f2bfb6d220ca77ce0a89dac5a06fcd0039b78ec479586a6dbaa0a7f4335a932977f27112f3e931c9cbb61bc1a46e0c4fbf1ff1a67d346039582a836929b2369bab9c615dd43e4e4d1030e443e1dab6af0f53120c0622b97f54a89aff538f5af0c75c0de660e9d4e2cb8a666d54f60eff9a838cdd53d53552e1f31e745185a88e1cd149ff1be15d1eada86eee97b74a2a2ce99e85c919e515c0f4eb81606fe378e23d7f3fc2985418e879fbf9500dcc5c03d57b2787351d8fe07263ec54ae557a8bd057ab0f7a3934fd4c8ba164a43029c9cfbb295b77e27ec7643f6439cc1369eef35bdbe339908ab7ab15de964110805d35013ace3bdedb4b2cbc9d67ae1e13a5bc69622c88def254d7fee8d73d97a905181afbbc01b0d39de9c0b598dbccf302ef264fa74d4e901bd612cebab0c9f821998978f50eed23a32b0179593dbc8e3fb5aea3b3c5cfe9a5d630b699274d7b3121afa73d001d0ff3e48f2fc5612863cdcef5f5028e067d8ee72e77455fd7b78792187d5f5a9c41c52d86f96cadb7f468cfe8900aecece2bbf3db4a91d9920f18853765a33df0200a71dfe448beadebcdd1f7d6a9c726d0f93fece5dfd220a5b8f8e02c40d7193fe9ad4d41763848464d2d05741f0262c57b9d31abb26c596cc79b1bad3fa12484da1c85ceb654b8a1e1fd757398c056a3d7787394b45a99974f290672d7f5d86d8c91551c9274718d81f275da2e7dd6cd3065811b25a0b45d0c6aeee16b369aa52c0edffb1a6e53c50ebd58c195381675cefaaff8601017d4e9097b5b7b5a39d70cd75f529a219e52abd84889079f8d9756b3513f0cd8553d1edb597b14963b60db6080389f9a003497035545c44cc0e4cbb3f460862195c7081f68f580108494329ca18909dc2d00fd5076f12f071661b3f5f763291b47a4c7fa68ca60dbb9a1ed3fd03156844c536af471fdbb7fe407c0135d2798be602f0feea4f424f6081e315892bd12222d626dcec7c79fc82cfe9441615f4c3d77f38cbbc25571e1f58728fc67f13a07705aa8ce4886c82a00644ad3fd95c3739450d77d51d38843d2a5d9e354e80576a94f180b9a80490c5511d409b81c4ac442a2bcbcfd6e041df4612feed9248dbaa43e68998ddbf5564001aff573857c19947a45f31ddd84ed5a750abe5343efe6389c7f91b4f30a2522f8b304e2e95b7ee0e53d3ad1a52c9532da78d89731ca89d80a04de60ca91a2b1ef2a17cdbf61f035046f7fc414503e5b745d61b4e532e673d31d4f05d7e3cd5b829231abcb424f96ecdf85e4a2548d9156e816b41614ae39cca69036bff4592ec536fe42085ff6c0e23a8830a939ecb3c4236f6e4c3b58ccafec95a2012a345524d1ec11855f4f72d482eaf0ff54e9791316073880d488cc7a6a1c6ec0b365337811a4fbe31bc919e1128c1003c250f282ed8372b708c54e657260cf60d36ee606f9e1544e41f9b3776d82552856374f145fcd1916440a541afaedbdfb32e5960027e393b319c036c80cbbbe5d8e2ef2ddc5640626a83f2b55b3bd6778bfa17b318af668b11d80a3137de34bcac58be037bea68adbb37cbd3077c524a35ef4460897d6790a", "65656352", 0, 1061033422, 0, "ceae4b1823649e6718d4d38c245ca7ccc57b6cf3226f5444e4b94a6754782daa"], + ["030000807082c4030378fec15c8e75ec91cd861b49cdc83b25a0ed3c6f9138395a268541554d86f615020000000251acffffffff784e02658944a10d4a3608c1d598db3072f24f9d2a894cd0a3188abf2c8a41930100000007525363ac650063ffffffffff177ddefd0a15a93592206ad8f96a6951c4e57dd8e4cdeeaa14eef4d3fcdbda0100000007acac0065006500d5cd3d380146a308000000000008535263ac515165ac00000000b48ed1b300", "006a6aac", 1, 1254699060, 1537743641, "2bfecb8a24721cbce09085a6427585f8e758da64e8952e689471e7346f8859d0"], + ["c058414604bd2cde2cc35bb29f2f1ef59cfa4045b3505b5704b4d90886a1820391bcfdb9250000000000fffffffffbeb78716102a8a3c044b4394ee13eb97a2263f020352ccd75055b78d6c1d0bd000000000083c5db2464302abc4262ab5c02a59145439d3705a143378abfb0745ccc19a136ba2d7c530200000003516a65ffffffff24a947846c8ca82d92a1732d3525591f2116a2ad900e80f2348c88eefc7155a403000000096a52ac52005253ac51ffffffff023239890100000000001d1425050000000004acac656a0f056bb40100000000000000006df7d4040000000046a1900395ca086ad8fc2460fdc18dfdbc3def907e408cd5999d34addf42f4605be7fdc06a0fe898f370583430812a64e48ca4b67b468a283ea577df16ca0f20f3e2af08fee914b7cb2c6c67a2f245cd8ce1ee260e77b9dc0085f69da6c34ac300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e5a96655661413bd4a59ac30675f8b10c597060a5cedb09ba4da58561710e009ebf29825bf76a1598f7acb42787d8f5bc8fe2c4e027c7c7c6f932fc93cf3fbd0614ffe70f4a6eac00fc584b98302f467c5bf5c4ec19ac27c434a69feb38805486277f121bffb50b4a15a430464fb1bc3514aadb76eef6151976528d0e76524fd021267bf4b544dc4cf3165f5b3db1d0d87648b26335e27710f89c3a14a499afe400322e7c483c513e4070c3419b97c74da9bc74070fe7183003afb2d1bbcef296a070b0029d15d66bbeb8d47ae02b632f31583fb01e609acad89b236b96afcceda3f9f6cf365020014aa98c52dba1513c6f3bdf561336b6b01d039a2caac5c70de696b0306ccd31f1bd7c3b0ea00230e826aba16dfff46cff644a12c707543e3384ee0610304ccbb8d897e401aa65f4618e00548f9cc1e922758fec1e19ef3402036fecbe30224ca76485a58c018a77165341d4d3741d4eb4979b837e2e9cd1ae9f0e3f67856032d6c7cdb7d04409ff687b707729bc9b57e5419ef73365e378be3443ec1ed7f8203042739bbfce95117363d730d7cdf2818446d4b17cc32b7b88b2e0c3bdb02d6ed7faff6a82c3e675a0dcdee1cd22cea299688413fefe563d6ac370cbb9c7bc89471a1dcccd02e219b1384288867cce9d6bc5179fece5216bab7f9bc2d7313abb5fcf2caa30f8b52b682f179dd31b74d22a5cabe2dc024f52d4fe3b98b4a8cb84652e4c2d042e5214f757efc91c284098124e5478a7c8565b9ec951c20508df95f740cd7e4cdf8ac4268376468e240a00b0fdd2f01c3416e64f49e555b8ca645f66798497eb497b9fb785465a879fb92eb3d9fa24e598e6552306ffed4c197425b6c1da0117fa42df18570d3c2c61bfc2fdb5f994c697a6118683af07d6e94382a1215b0688fed0635c9ec64f26146fc097b4fd6129fb6c2b9cb1c7467f16eaef51c4e8b55b467186578f588ab0559155bb0a769954927506acc7e0dfdde573ab2d192b2a00b162a01a9b3cad080f97b1f0e435aff4b34b64303dc7d1f54f422e80c58839bcef9e50416f971e1fa0fef43085c8303a3a20967387320a8c268d8c8bc778bb90e2fd16c3d7847a70f47c9394f8bd5ee35a9f29f2172201876ffc6e6681edbf96c809baca4002d76b43056f17c9c97a3a454b8775bd50be7e545bd39a06f999b3bae3445e6667e0fb94fe98c873e4ea842dd22145f0e6eef75a058846f2619e1a7a7490813c92e6e20d0bb2c19ab870a731ef7592f781a7020d2826e639e99d05ef76cd10a23c8094bb08b344f47fba48b6756f8ddbc6b77b2afdaaa6e8841be665eb18bd270f007f047fe8a6011a66e4fd9c73e7a873455ba58ad85c2b6a9848b72b0543423c5b8d2c85e243a22375c50460e358ab8400e15f46dbe26ae69197a2ac2b81bfb3399059d9fa634c5eb46c8b1cdae4531e7fa2ea6c18f78cd557fef5e5b1e2d376b4867d7ec44d3fa18821d57c6119a44ae73c3878b58ff3fb9a10bf435e4b8d09d5d6ff8a822e88286ace6770c922e4c02263347ba1a7367edbf144cdc9cbacedaab6a582642cd23a0522f5c5a0952761c5c2cd85f8b2189dbe250a9146fb6c517484f4f1dcdf472534cae538474103bfaa7806652728c5200effbe4164bead0fed245e9e7d26d8ca43dd5906d9fa35d7d9c5104838349caee8c89be941b4b1a9e516dd7724c5043137646185a4da6b297006bd56f79666fb5d2f4e1156369b4f2d6e966dcbe0fc5508698a6abd4eafac1c9fe026e3f19605863698a5dd896fb140aa5ec91694b759a45492092ef7eb0cb09e4e102e106b6b21039b45c4cb296f5cf2e6d6b823a9967d41cb30c6fb3a32b854abba724002045ba602daf3b37716b36611fcbc47498e9ac10dfacd86a95d8b087c1868c45007e9cfd060708f96972a9b489ea9af43a21874429c9ee987351e7311d999f3b73a2cfde57ece920167a5e8ec2bde88b565f637f048085c9b7a3879d3479db9f07bd955affa3270630c2a4a7bd983fb8deef487bab855f1103519272fd6b3555c28c4132a23cd945aa4f9369b616a0224e4b32452343e3b0a1df02f46e6b353796c0e6d8499a54455bbb54646687ec7bc212916d8baa30aa0a403d31a362a17de9c464a648b2b1c5126c7524bcef17d9d8b59315d9f203ea3696d0c56da3846d505f5b985adcae75b2d668055995e4d7268b0a4c47ebf77fd8bc26a9700b41be3ac48295df93e1ff8733ff7fd091e882c26baee6eca3a5767ea3a68496bafa0aaddbb9e4cd4ccb1dce4acb17cb2939f89abdf23b0c738341ab0f0a1cd9ae66b9d8291ced3691f8dbd9106a44a8c3d56361079d4aa2f683f3d5615a98bc0c798a322c38d1d1c5a46b0412a99afb88acd1524a2ef2c00a55e758b1e6196eb5f6607d4153a6c9926a858f9cde2018fda47e0c", "65515200536a0051ac", 2, -1243808973, 1537743641, "dc1567e9659bfd002fadab9a5c950316b3ee1c4889e3282f5bd062b25d708fe0"], + ["1b71ae410168ac71f33785ab3b67842d368082cc0d3be7fb9ca4c6b4a50362b5f926e51270020000000351ac654c398b0e02f9a9ec00000000000900ac63656a51536a537b6d8d0000000000000000000003fd458b04000000000000000000000000e7c465ac97838f61aa86accb9fcbf4f1980c6950c00f73220118d07092000a0c72e55a42b840ded10a2f05558ddab57f4548c22ca451280bb46d0831354572e75a997a0e3787be217c5b2df5dc1344576b48cb8b8333fb06729bf0e28d2a7cf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036ef93e8836b43403f5f2ce383af4f04d82597971c67348d99a4bea564358cd0b06f98214287da510ce444d803fb4c00b2be379baf7a2f89aab8e4ac165ae002ae5a3b6f5c993fe7b4d1018cc2279bfd8daf58b4acd8a05b9422c2b0df1c4a0eba7028b6d050d0f019f3a4aea40e8881a8042f661a7d1d03d6c86bb8e5fed8d7031746b58887f4886fd64b25565c7c3aa1f3653c333784785f08e6c18cc3e1d206032315e1983c1de45cbc9f404e900e451d9fdd474f2b19853ce6772e5b147ab3ae0b076ecf20c5a44d124bb35f8a9d04978691ecd5d8917f47eadbbcba6db6b81037bcb4cf9450d2c5dad14bfff414120e58d7a3e799d5f7cc9c09ef9ddea267b1f5031bdd741d846eab5f47d7535a2649ae537e64ece3c4ac766f61bf8c81e6a62dde02017db405b0373dccfabb3da533763e4f784064cf8f40907845e70159359827bc030d1d9b5968e68800f3c990b0f3f7efab93135b341e77f0d1d87112e2f4ee65dd020aa9969ea39c2527ba9cf7e997236c67ab5aadbd282a854090824e66d1b689610207d4b44e7e1d3788ae5e0765682d898b175e19700ef3bbe85ed6d68205f4127687f5bf3d7d6292a13cab0d19eca9a03496665b9a892a88574f2638386d5220c6106fb3ab6a55e664d9d4630a48fcf2663ba38b3e391ae344176f611151da205a90fc1185335c5f60d5c81e00bccfff90bb9fe8c52a771ea575ba236d2db7f381e820474317cc45ed9a2488e1040b7c69261c8ea80ea4ab2a7a923001d135e07bdf55cec6c1323d5eb431609edfdd05f58381711b0a322bd578f2fc13c6de94c246460ac3117d4f913761e2b80133def481fe094f3499249239610451d56244c7990021b4f7aff4afec0150c9d1adb78e671d9f6dedd8e4f4033afc994c42cb8b2ac264d7c90eb118053083b45627f7a775ed185e676b6ca4c799ba9f38ccdfd20ad9d580f57fd5820d1e47630bdcd8e8be4a1ddb202ced7fb6e60380d9e92f2d7f95a18a4fa80d02fb9e97bd89dfa142d2664f88f0a569f8db40903ff5bd9ddcbca5f55ed2fc2ab7d5803951c879125fe5d84aa06d4c2b954faefbbce5a0777a5ab1614065d69f69bd2cd708a8ae2ff5f3a63ca9a92486c047cc0dc3f9c44c446f5784b3f090814c5ae7dbe005b7056a84f3deaec31e88ec3c4910a5fd58e13f16ab9eeb0607bb11001b6a16013fc7aa84328533cbde283ae360c0a16be16d41398b1d5bcfff43b69225461d598fff8c07f25d51853ac10a69db0f0482243e78d7817ea215742d3d8242f457993559923ec085b3c3a30fec7697d64bc28c48777112e93e41c2680aa3bf75a1ac9883dd81318622278ced67df2df0ed2b18c65309a2c9e664752cf65fd619665de472c74cc09168e54923f62ae3621a851abdd6682802653da1373ea26f4ac42a38600c00c016dcff85b10f9248c3ee5ef400ae64dd4336bb1e374e3df01d0814c4f2c7e144d595d671afeea7bc2dd7320859b82e8156ea024db4a33090799e30ab8b1c98fbf8f68fd09cc8a1c95365d20804edaae606a69b518559774b36e00578bebd9fac30bfd220e0120d0208d542c407e70c7f2955223b78ad6fc95f1652dea1c3ef3c33cc0c543b2641bf0798c88036b24494ebdddcd8a25a3de91a53ad62e78aedf02447f8fc77978ee206abb3224ab66a4d7355cca5841f9496c6359422d6e47b0eaca3f90bca18564e521e63c8db0b6c6060a55d04888147c95bd1d033d450242d989c84b27561d674670a73d19f2024bb3253586bc693d6821156e314efb666d60cea978dcb3c0b02ccc879f01a7eb7968f7b5a1ce0c94945fb729b77bed2f74efe20320812e7bdd9b576583fcf964b9a9a626b8960a392992d614276fa3110bfbf9945e0f636f3a4ddf2f41c0215c6755937690efc120b55688d4b903111c83af2d302b7ee5f6b83c7431db83151fdeb913476d20b339ff1bed54b7a8bfc9c52ff684d8ceb907eac915d5e6915ddc3911c3facbded1ea6e4b0fef138446108ca4ed5a888559e376b7f25457d95a538c281bd1f0ef1aa8cab6c8cd78b7555cd8287da80b9f8165957535af131d2ac579cce9e650635efbfe85101c35e8c48a47da15b376fa2a0cc3a0d2c4ce151729180fd7ac019e3bdce3b97190a07204307701bbf591ad106efdcc9b30ecba7de906eb56ed3668291988ad4a9c3a659b9a5736b1c6bc0395e5884a5fb35e1cd6e2f88cc5aefbd415448b87195cb2b341a2a291db07b44dcdb73b081a65873847950563b896aa46729fae4dd6a7ff228535389b469150300000000000000000000000008eabd0da69d26e0c898d9c0da14af0e60653329d9c1f7ea981ca7a32b8719a28e7290d441bb32573181da2c8143c5769b8e03934a4f7414f3b0bf7e307470191a62cb58856a676acf348a9f37ddc621f9718bf977de494aab8a7bd7f931128b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2021e3147611e279b6a1f88a88a3418d8e76fc3084994f6e694a5e70d1602b9af6343e8e80fd472c14726f31b4597f4d444480859a77badfa577d81da8c8fa580f78e56b072503dea39cbd24b7d90c50ff53c6b453cece8b956c805b1776fd9915faff0ddf1c9d83f03529c35851dfbf71a70cc84e371511954691460c3fce4020088b767c35352d13ae7db8a112b579407d5d54bb0a9d4a34ac5855429b53cd6032a1f05db82313c5a7d773b0c537f68a376a4075e9d2f7e2304311ef13d8341650a02d16cf182070377706a5d1846fe001ba14caeb3c18711d58a65cf8926b6cdc4a7b6e5104e8aa168d77dde20b31d3e5df6ad20e2e8531f6f5f595a55a914e5fb03140e6a708c4c1e3ade55aa14114c1cdd70d47f8cada726c2a85c0ce5a5ea945402148bd4c986b54d4414e5866252fdbca3965e883fc9d75a8c57b3f59e1760a6a20211c3313c25048da31759e0d09ad8361258fc82605441307f3b0418a2365cef930201d93497c00cdddcb8df6125bb98cb61acfcaec8bea988a173f9af0555bc385b03231392fe5cc800d0a4fe032dc833f850d30d8d6d5fedb2eaa245e52cd74c93271b63cd4ebe5590ee45f20198d623a5b77b829acd6789f79f376932a4c9347d3b1f5d875269a950518bd4783f0a1dd5df11e345ebf8095d038d6ec1546fad8489cdbf0d4dbfa8f5323b6baf83c9a35ac8eb70a2911b9242491964f5d5da52e364cc7df5057c0ac7778c3f14bcde1bae70a0e46a1200c47b609c9158c202f497fffc5cdff860c2ae2de42e6859d2dc782dfdbe7873287166c7c7b0e4f226bca13d83725996651d78195d4d3b0ce55bf5fbed2ba4c6bebe5399bca22fcf7a6767ce66038b563477086918194e8ee04bd7bc478bde43fdf915fff1dd6942bdca226b8d8777c71fdf14edcde63801e75a21121798fd26f44bb7514ec6fde2fc783979f58218d63c81e20df5e2f7f7095f3f01a19527081a99acd428ea167e13c3fd665076a8236d5e32df7c8aa0be21f225c812d3de67204379b1ded38206b963a89235dfd0e2ad24b6cc5255dbba6b1e5d08c9c7334711ae94f2b7dc0bd122e73b2011f9c1c52d83bec2751557aa2b92272ada981ebba4e9f2d080d2be252c1680f271b5b5132b4b60405c5b640fe1fca585837219e356b712bcfea39e740a8721b8dcab7fd2decd4081326b5a5cfee3f4072b595bca4d07f3c276ccc7bc8383f50e972e1d89d60250f2de9179bc6a1358301c2d48cbd76b01b9fe03e55f55a6b44c233bcb49980df04f61294fbd979ecf2636f958e1301cb19a69a49f6b6530c4483b5218a04a367db099887dea1e9b91b1642dc1781fe94e9f30c73396c481da61b8d979aee09c438d9edff5bc9ff8e185c34fae08e89857c6afe724ae06f28cecb9467c22668521db620840938e396158f478893046f06cf9bf3ae90344beaa5ca60cc438d683e64a6d7eae4ad27b0a76cd7c48353b3f3f8735dce997b3759769f700f218a7a6a55342f119699f71af10b9595eb50ed466f0321bea398139dc412450a47f5427ab3548a03e27d3a7c6e5648d6fa7f3d01e71a588b5329abc2a7391ff368cbea955be5b0d7c880d67d3f578f6725e61ff13c02d0ace2125568925c027a3aff207f2397fa78fbeeee0080fd51da4d6876a0dbd82d6e6362fc53aa5deaf817b28471153c900bfd5b48943b37e708fb7878ead98cd4d1f7258a246dd8a21d0dc5f289e61342db02ddbfefc43a07715d217b49314e7f1b20ec484aaa72db00702bca10f6bf29690d5061def6221713d5e4d3b23c2d295a1910e0abb100768d4f4db586915c015cca0f42ad83416bc81144e96e38336e035712199b87218620e046f9b51dd3206d021e2be8b85c42c64487fb0969ae5789099c34587a80337dab1fd3c7783a17471ff1f97b2fb0fe317b67408de889eb17ab2a70f29d7baa6f5255df07b85c221375e42571c61a77d0e42e571865f0deabcb88c55a508046cfbe7c91abe40edad757ea1eca483d9640cd2e8484325e0e8cbf014cc4d530cc396b063b160decd01825c499014037fb4201854ef1eb75c82b7c4ae791a85cd586cf7aca1f1cf96264d2a738e5762e9f39052c39d6c176a02beab22c4dcfe40d1f94155bed151f2b7f79f40543609166c6702862e4f2557ee912f5a5c2f50534644956e77d244b0e7b7c275223d23d9c9d85f527f1b941e255d71eeb931bc334d95690bc253ba2132625d94ecdd217738c6e3e3f0301af892d1dea294644b26c293a0c752934df8150998b96809aed11800000000000000005b048a01000000003e504dc0c84b698cd1be7cdb91e879e07d0cb6ae39f2170d9d640433e8b0ec5c4bb316278fe836e5116c96d4d82c9fa36dce611437660688c8f058ca9c49a61d5bf484f07057066473168645091986fbb4fec89a2eb7ce8e4d01b604017cca6800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ec21809f5193f748813f62e4dc4cff446a8f709088f20fb721635672bc8064570e0245ec05e2f3756efbac86846bffb282ba90ab2c78887eadfd792cb001cfcc53545f20b63de2af682eb389a7c230fc8469295301efce7b8d68ff9552ee5ca3cea3983ccabebbf495642f1287201b6d2f08f9efdfd026febc21ba23a07b5c83021292ee3b51723a3634e0c2f3760285f8841bcbfa741802adf22c0384e43fa64d0215517f8eebf001b770a61c4bb40609a5c168ba6d71f398a4ab2e9a6258f1f5920a04006505160c46433d0c0b64d687e1367176c900953a890609ac36f8ca2dffafd90e8abdee7ca8cc0ebeca35ed74834a31dc4d08b9ce5c2c3beab097c39cf3a3031b0001560e0da5d29fb91e1887618a4a4411e43260977332c64b6bd998d51d25022c354f8ddcde315a947f1901f830bde97313f2aa2a782e108811a2ebd2d5a79c032f267def8b7ecc539ed0e9a1846ceeb3f78ed99a40838c0678804ee4a1158cf4021f44172d9a038358874e43ebd7da8c38ce179a4c1c81d2990981d462240d46be0204218d27f75cdac577c68b72ab8f16460e4827ee137293772bf6d17e620a9f19787b4aaf68cb4d4e3bb10a64a186c360e1e2020e67d8c71077651ffcd3bb4e8e018ed8c7d12699cd46dffcaf88826fe73c36ac499a573dc437564525bebb4336dfd18dd6e7fe50b4b1f333f179685a21f5b43f105565eb180ea3eea30feb3e3dc8858347205da0c32d4706b28bd7d10425c727e1d75f748efc3e98c38aeab25ff41af5b8ba0f2d0ea14b2fe4a73dbb12fde6655868156db21cb5f7021f79de42938f2bff6bd09e3eb82c6a9bb5bf9ebdfa6346a93ee64b135cd9b810986f6fc61baac316fa0103bfb3886e41c411327fed02d2c3c28575c1f9c58fa40d286e9e4a220d2170b606c888a3ed5c0f64f1b79982e5f7fe55d728ecb0986ca298c167c3c365f83e7e7368bd62dc1e3cdbca03a82e248b19b84b2c46f06e0c9453e176210535b40933c52991ec529e71008ad186bdf9f96a8f13c78fdfff6849e67baeb86bcefff061522df27a6407158ec0581403b335e3fd5ad37757edd75fdb19543e31909186fd28e9044e2dde7222097c046129a4c5b08cc2fc26dcf311b3ade9a0ba0f6e4750bf181d5b830003933dbd94c7392cff1c967fd16540c21efbd0e445b2730d0ebaff8e31d10e82dc6979ae7d05bfe3f6cd188f6212cf926f3b1adbe039a3b77bc43af6ebce84720b51ec943641448f254e2fd99dd139d87cc3d1818602ff87aa4beaaa7dbef30007ef2020a54d2cfd4bc486cd9606108ec7b355ee626c4539c023d15e124017a124e945852d2bf79a3cc715610c92cb09c4386c96e5c5821dbad360d2a7d4890977107ec9296c378bb4ee25438ba6f37a2cac9b2779b074d67c6eb73067e93e25ed72b96229b19e94b552debd32d15a032bc89fe7d9b972305d41c88b83308fad5b6cb4fbf42d2265fa1cb36a02bfcd194b84b3e79c7c42f13b8eebb233a4fb1f12403ee1ed15cab65d6daf9c3cdee285b9c29d820af5d169031ba6b9b0b6d1714e1769b69d1d509d2a75c9b3532c92784102c0d32375468d6c6d82f9a6bca26e8cce6466d0932ab4c8ffe8d02efc7d35510b470424a1a855156c2ebd93137d03a1960087c12c14202df73ca112a6bd876850b170b3db6e348cc5113255bf2f5e5f4c352967d8af04d375a8140763ca7ea2f96f1936f547fde74bc1f39976912ae97566e6337af85f1ea20d7f03d7734b4de682878cfcc12fe445cb3342391d9dc14d9fc681ea81db3ab3a7f3afd0ef2f9888c783ac6595d81f264fdc1df577bda2e9fb52ac33f0f4a69731e8f741be1ad091704b2447e3b475ff6b6225f9e0df1070cb968b1a101680f6c765a07dc5720f8d9557d0e7b73d8072aeb3451c395ce4df029484b932da696f9f6d6ff029a7dab952b1cde3eecb43f531bcfdb2ab0011bdcdfc0f748e9fbf4674b1e036cac0bbdc13b1fb8164e693d34db40c15c09f628d90da2149bc9455c8e1928db5e0e2a03c7b829bce539655d37caee979f0a49e5097e98fd7e0716f02aa125b031609929c7b97c7db13148d5da1402f77b26a2152204e7fbd20848c5cb27f474f3e2afa64a0dd5f80aea6aa49b2688c3643ab9ff92c01420a2299d915c4bd9915ecb4cffe16aa2907afba977b7d09c0b5f81c72fd451cb06521588230f0f342f74f5ade4ca305ef61b74e31a15a022f487fcd35c000bbd617746c685264d6d9c735fc4f6e9e02cafea05af81df380b40a0b00e01ce6ee20cafca0a9898a1acc08bf74243e5df79e6158dea1cdaf5081c9a42dce4cc2debda0f54749ecde6b6602f9b94f376967c56081c1016eb3094ed27ae1ddb91b9822de46293eaeee3efc700a1fef3896d7622c417f7d8b2988eb6e83487b54b8045e0c", "005253", 0, -1359006412, 0, "e718a01910c2b7c019fbedc158c4316907cf7460152bcba78d794485b70a5b0e"], + ["030000807082c4030199397d9b6c72c1bec0171aff492a5d2a5c2e33914a62e0a928199cf9ad524743000000000039fe028901d48e7e0400000000065153526553630000000004b551aa0167a742040000000000000000000000009aea8b9230339b631644cdb3e413bafc400bf781bae83e5067548f48cc5e00b6d15d0a76b3bd726c70be9750cdfd3e24dce4cf4002b6a922051ed2e02e179449d3f5d08806a601389ab398d325b4be0deee126d65fa96a41cffdcf050634a8ca00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ce5b58875c2e8458d628d68655f3ba1fed1bde21c27a37b63d9aa9a7fabe3b7ea654495679bca48eddb00e458ca6e8881e89d78923d4db41322b75507919f84d111d814c4471ac69aa9b959a129b0e24068523c9255cf3b89f0e5a1c4504233fb4ccef51977eb2b1057ceb12574e04adf38256257eebfa23cea68a13ff51de19021117c4c569b35ae92ab3b8f4fc1a7cb0192116f3c1f628cc9b3aafda48b462a30200ed7688a1fe3280f75dfb522be9c5096e79449885cf040189b93cb0aa4a2fc00a087910a25d82490a8091ba57768931c8b64d33c807208b96f3918cd371685ca8740e3895bcf0b4655fcb31be25eb87bdc34ffbb9da52c7461b104e788ac45c59030dde433919fe49c8f185fd417f7a6d4f89c0da9eaf9b0de97d87aa1cbf876f3a0312ace8f5e02299620461a95f57c4efe2f5ab687e0c66b5551bc4f30d6821a7320204032049572cf65c9c09b999e9c1eb160622ba19848d5b9cfb928a4b925f76710219c676b64e0dba792eb2e1b42838ea99957d6386a672a1505986c794a13f4e6b030d08f7f02df2f2ef62677f758f1718a629114a29ac82ba01c35dc810f9722ab0180e46b33edf82994964e5178e358bbd660df2e5d26940266e37da172e961ff069134e9c15b67a2f31800a7bc913e384eb6016212a894ad43d0bf621b883af2afda43c26b68e196f3a10ec7352d285065c232797689f76f31a6f482628e5ceef5de055a569d29b4411f73773134c6f286dfe11f9fc1ae4255fb2fa6b5766c254a83e1b5e736d945d22a433dbd03064b2d6b417966a8e8d97d8cb5618b67c289102139bf7dc97056693155db5d8ab7e41940127f9f07dcec2cdd145b8ae757be3cf3430abe1d714b2b6ec3ad4d582dbee90a49de5586b4ff338e702483068b44801b47e324164d9aa342abe1d68f7c2069ae3c08a994cda4740e70fa23e27ad5ec1775e8cc0f6ae6d0a584f4e415ae7c7f2640bd03b38acebd91e0b30cd47044d1a80ebd575a9eb0b97247af2057805fc64a1450488c3dd739b34b42c55237ec4c8b8ec538d47f108f6ffc2638c8ba3ffe4950835a9ef4d4ebf4c5f9ebd398b28884959e0b5309df4151b32248f044c73508484d6d160347f0f4163cec4924492c6c9bfe4809cc5576d9b42bb18e222d8c3a47ef03c17c490f7266593514d0f1184b65511e30e882557f6f3af500824f2194ddae330249a35685fbc1437ad85687f608621ab2b6a50d6cf66692d0162443b5c74d6113b53f1050454fa7a1b5add53f702567a2b3842fb7383e2b93c05567f335c1d7a5dcf8b88c41b01ee648e88dcb64c127d6cbf32e0bc103b8abf8d23e15559d21d9d03ace23bcaf7d16f1b54f43306329eb27ca7c9d3849849778041f41e5e6747c365d1851bcc7e404b4f6e95370b7f4261e5ecf271a898129ef86fa04b1464d7e5bb9776758c0a566ce0081bae35644ea860063b46735d41e28ff210a05f912a2027e7aae23d1335c4be7db3e31fae720c3a28671d40b6f80938b86585fa58e7ef377e0fa4545ece1afbee9a0a4418006872c0504ab25203e944903d36e4ddd1df167ec31c1ed71789ac2d03be18b1410c705d647b69f7fecc112b06ad719e9b0acb8a3accd0bd87055230ca41620daf9052afc8b8f4f9d050cadec1ecd2505bb431a761b6792cb057cf6c1f834ee67e0fabc463e262e9e1e248cde04fa9bb481d6b86d5586f5c0551ea752ade038d4473aaa747cd505c4c080559b951ac5743131eeda08557d0712cc0ebb086b440bf5a3e8727c1870671a442bc9e4112e1494dd058fd8814ab28c45c20d7c1206c98cc593c4de082ba32d0655ba4a0799bf8f4b3e3e5ca0b59f27f63beacaf50ac4b697605b72cf5f189ef1f17ce33bf6933321051a3a6ff8e2a9fd155f22551d0d9063dfafa368dbb7c09cbba78f5dbd3a35c62cde7ad287c202545d6b867c745855791043f372c94291494e6e6ade60f9e28e6ce44af2c702f2457bbd9001ab2dcb2e5899ac359874f956e3620b8ccc8b3e42b49532a21e69b035980157d8566c383c2674519e2011b9fa7df5d7e69a8761556a002de4e6bc2f1c008c90414823c9599b719e1be9e56758d9965e0f5413579fe9fa736fe2218fbee42c93699c63e519642f83981c8d30bfb2bf48edbd6f844c870c0d43b08d3a87d5ea4d070785b7fcdacd119c7d97e6106c52359b4a51d1cfe1ac909bfa2f62e6625b80bae854909b02229d971bde2a0339982c45e6168913df79f2e7d208d127d0b6d06e934777c2fb76f6fa715166ae4117ff957c0b6c94ac6e8f8bb0943315cc301d22528af3754ccb4e04afc46a55d0dded5b9fd95c66288c013ccaa19e358d3375de9874cdd0505e9bbc08d86ffff1ce61ff9ed05308f3f937ca6af828cff1ab14871053a79d80797667db6420eb1db7002", "ac65", 0, 900788221, 1537743641, "5bc440fadb31f26b0b07e5cab8cf4ecfbf184b066b983f6a9855a2a61bfa087f"], + ["030000807082c4030204c1b65454948a280b6989dbb6c5fff6fb9d06f67cf6b8d32b110ff159868d64030000000365ac527d9f3f7ba560c6573d3b9524b60a7a2b72f30affd983287b25c889c53142d1b35753ed130000000001ac2760677e0216f3560100000000035353535071210400000000046a5252ac0000000056ff2bea00", "510063ac", 0, 885361454, 1537743641, "611779e165b9286d21b283a549d15193e3a7c3a75859a938723cc38432b9fc3d"], + ["0e68a01b04e6d91a97818e3140ef31e44061e75173776027cb6eeafb72b1cdeef4f01d45c20200000004535153009fcc422fe2ff3e4d32fdb5e8159d718737ffd04e5795d6520523c96756457b624c1ef92c03000000066351acac5253912d24184d5ea52d8ccd6a8dc3577c9a5e0295ff6494c9ac09326e4c0e1b0b72897c9c9400000000000f21345b84533d80c45ef19a57c9d235583617b955e07cec6c93afbb3faeb996770579120000000008526a65ac5251535300b27ff104919928000000000008536a536a6a51006a8082cb000000000005acac6553006ede8c0500000000085200520063535151f53ba80500000000016ac82619fc0300000000000000009790a4000000000058a7984b35ccb5a3e4f18e2f5c66938ba21961340fa8a95ec673fd6802050d1eca47e41e188a9fdf95d722b763baed2c5ab1fe491ee11af80f730552c6008dc36c23b4691f59bef51e4ce24fa81ffd26529e3e9425b1b0b998af02f9e641b3250000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d9ebe83cc462d29c3ddc9263935e51e18f9181e307466a0f2b0cc20de08627e6110e1c798f0e730551ba95554fd923f06a596165157c597028feda01b83a8b78427689385835346c3693949fabf03c9ab4e7c6cbf065d09ef4965a93f44ebc0dc5865b2ecbf550bb899adf78022da53b6de7fe23df0f503053a6ef147210b2031cd42071c89a34ed2de351876c9060ddece8b3708988d1f06cd62f29ad9f378c031ae69db41692c043af4908b9f488a7ea1879cbe7c6d03074584fc5ceb81c64ae0b03ef0b4087536e64e8cafe2d5e1f0b4e12348a61d9e8b079d925d3a01a729580ad813aa2e39e2a6ae3ba49c70274bc5bcd2f94decdeb569a94dbf11caebea5b7030be221cdae8a8fc623cf3cc2a3fde096cc44128ee6d468aa50dc914bd56949380322846a6025888e0ac97b16f3ab19c361e0115af25b2fbf5030aadc28a5db05e7030fc69477165a15abd8d83f2fe674737ac9f69f2bee33b3377d0f22228e5ba099032737654295a0643573b63dda545f2692516d5897e52d77d46cf4a62e8156566d032db67dd0e5be61f5ac73f78cbcf0a31ee6f943a496b5a3aa5c766719778fa453800ac8bdce05f61ef1e485f65d92236f7dfac979a17b09afe203bc11c7919f8ca593e38da2e74ca74c7d24a2f6defa16963bf20c180ba5068c59fe3eca8bf53a9adcc85c4247c59c3df0d95d398037985fa065cf4751847373d0255e171a5b13b808e29ddf0d9facb645eb245672a46bcc165657388d1e26f122b5e6b1d06e95f66dfa4dbac45793df9775341069e0e008ca143faa223d19f0030159c6148574b9cc49ec2e0c0c12f4d3f6971b9ed91e17d43174be2b3ec7ce8c3eff3db28b51ad2059d90c35d7799b56b942bff1538f03239270b89fc8cbcb4f8034f742b5ecb1625737664c73069d57558621e119672c52024c2c42c85760d4ed9226238d3f3d10ccad965a20d32317cc0977a2af8d5fab89fb2d15c0d03239cde33d8ca11339ca4acd0db2abcf2f26428cfdb9814020f88587df10ab5b1291ff263a589f60eb8e65ed0e4ac6b324ae162bd6eedd5606759b24f0e1e719cd1a042c0414bed9c62d01ada2e57f20006fbbaf7a7551934f079117519b11ed7602effc4d5d248ffcd01d9d1f7f08707d57bb300c5cb1f636251a2aedfda4974be07dda44d9b63d31565ff56feb6428fde26e99c54ac435f01aa22c7dc0ba9b5bc7479e2784cd598f4f9626ccfcb995a37a85b0aabc1f9b507d15832935815e51bfcdef2c924175d4430875ce00e283ca46808c003e87d342777f0a59b51ea0903482e8cba2fc782beda9912363a997b526d754fbb32efd4d385694d9d8c4383969ba77d749b1617c133dfc7b9108b05629294f6c3606ef6026b51e15d31cd6373238a72dd43e5a0ca46e68a4a4ba27499abe68ddc0d322761908c06dda01ad9f7f1269dd44947e34a4773b4ef0285c24c0b17bec5692b7fca04c3258f812ad8dc38a06df4efb41a6f44c92ffaea444f2a573db16ff68dd86c2812edae23964c3b7219d0f36a3e2afb5f426d6b8321103dd934395df11934ce40ff25bdc57ecc4d204fa18279eb70510c43d4eb61521f7bb166adb915f8afde1536a6654e291b0dc63cbbec99555ddc9a1facf8998b73f03832a73d940d7d6329e9757cb3abf0f80c209a573ce3d05c626b5a494368fcded1cf6a14d7146b72b3023e2733367f62b38d0d2fdfce5aba672382ad0f51b36e9e66153721a93c47055486bf06d394e9515cf6291650e10a24e1a224d9af829b94752bb467b6a11d86e6363e84c3628bafa6bd9743c52bed7fda69f27758c243f3a4abc7912d4ab384efbaf33f659cdeef80b6bc8477da7006e5737c09c62293a86c2a552ede86e01b1c7cd3669a3a229da3b28cdf15d99b4707d66ce71dc4abc1741e508d5ea1f8d59e545004fff5f183e967fce133e46d5e84029a61b66d70e6728a57463a10e2fa0829e610c7c52109de6d01db359b904ab5f0993247e1bf056690b3fc8733b9cdbf12e154005df64510a9499de2e9b251a6be414d28e0aa2545ebf46ea3ae0acef81c2f23338879ee272a139b5460f3f648d72c32dd9840155092f2f2c08be6c8ec08ac415ee27d787719e1e372589a3b8991c1a973ffb3892bb105096f0db96b2f29e19b41a878ae3108be056e3bc146bcfb8776bfd76a8f825b2c3302e6f1f482c5cc03947bc2ea80b86b2d58026afb1b922491e61b2213fe6548ad95dc2801aa624affc3943712f8b7116277e78400f147a884fae69ce57d98c094d20942ea69d8a02000000000000000000000000163ae9979e83e4d9779219979e8bbecbb8cf824c05ad5a9ea913c2a42cc37ba11c44bbb248bcd0058b235e67eeb2405219570f0b0ecb11b9950e9308f57dac8fe89d271c0de597da1924d4d329a3ec552337f111ca3a06a47d3bfd2591ef3719000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000645189f5c0fbe198edfb047b8f722998f756cc462dc623832c2af42c3902193a860ff0cf758c8b49d6430335ddc60df686faee56a124af77a7acc667eea03bb9b4e0e9cc0156313e0f62d9f034adf2357e8e2007a7c37257be90f8e7fa4262f1fde92cffd8f819a18839988a9a63a242db23f71586300d622eb26826eac86100303137f4c3c94300fbc7e6d3fe4c19a7056f3b1f051fab059d6492c92ab3da9a4022203a24b5e064608495531a5242e6f4d1fce0d559aaf5b81457ccd0edbf321280a0055a2db4d487413d58348f0986d3219e32eb453689fbd95f7c141522f19f9f9a75c58d9343dc8322624bfbcb3aea5168fb97cd0bf817cbda87c2f81f9200479031cba0802f3693055e129c48178187ab43a0f4889e9f01be3c08ad73ddb7d2938031579339445cf15cf0b1918fdf62116ca5ef7d044a0f9ed2e244c4d30dc35a99b0307f7d9c8a4d93adaffec7fe1afa4043a8dc9b4cf4d06ec9683dce4d816dcc6d6031d4dab83966684677d88853eafe6d1ca563a36296d9c15cf15042d1d062ffb82031eb2b54ab42a17331ee0daed28133d71c053979daf2e29ce2b00a5d9ae367caa51206a29bda790e55d6a1e77175c9b7baec4709e4bada7fe80b2705479c5c1e26bef389b803e402a882981c29188a1b024caa781c8a1fbc48e15ab2ddb9ae9d3cd6af739be2aee8363eda87c72d11650850a272102b67995337407457280205d27416cbdb4135dabe0a02536e4315649997d6681e2cbf71df388ca3fac3d0a3e587027c48eb6ceb8becb933377c9e9cc0cefeb837288010bec066810a7d1b544ed8998e1ef159a37e6996a083bd1011f10bcdcf30507979fecd2700cf25c0510e445161d33bf21030eb25cf168402c58de96c32aee903ed3b404092f438b58f8b7f2ead0ff13ba90f5983f5e518fd9ffe31fac29c5643e835f2d06077601ff9ba391b033fd148958de42d4892e8aa31b2b44ae92b48918a138fbb8d01ab261fb0d421d80fe002f0c76c89e5261c707153fc360a52b90f60f0dbb33d16d91b038f1d2901c872d07e6cacb96ea1a8dab67749e31f2cf3cc056b50ba894041ff8787c2c76792a95d781454899ecdb6cc69868d8d34d08017ddf168247363ca889b3ee6a60fab584dcc8da0e10ff449c3a4ec50ece9c970e07df600321970d70657406e37f72c6a71a1377b3b4cac7dd52617feb61180d9bc1bd22f53c7cec677312ea718888c9e72f215a2ee26e6f6cb9914f110f43859e93399d147c384685502d0a5a8c5f2019950f915659ee75b0ab9a73b2420e67e9ca8e5a3dcd4bfa5e3e40107358092aa0e6c10ebb2f986123374c055d83468a70f6a3beae2a33e9449d9e1c99b5356ad3dbe246bbce163e999357755c0c900dddf8f4cf7e2b4ec9ff1cf91b27889750db96842c5aa78c4c09a1cc40aa0cce9ec3be422783a25d8a37d4c43778ed1a3905e96734d08ecb805937d456945b6f5d374e33d1715bc902122c163926279ff55d054c7276b4dfafdb20d7519cb4f6bc5e19ede1a6fddf554f769dd30fb2a7569740b80eb31140cce1206bf046475c955c63167ff0e91ea751565ef1243489d7f5e6761469b68093f033fa508b4d17320c8fad2bb7e22855d4ff39abe797edab5dd2a2dd85445a75ebb2e07070cb70e5f9b5b0365a2ccc182c100330390fc209a1304be8ad68aebe1a0bf60a8b3ac382dcae74f338fb060f4a14d0bcb46258f7bc8b796ee6bb27f9ed4d5c35580c129441a3f96b5c7be8e9c533afcf1dd30795e2d6efaffc3b7d5a3dfb26d9e97c4dd78bcf8d9810a79b855d501ce22dc0512842ba58411ad8dd60ab5b9721e5b85dfe04869916953ba8cde5008f08932be1e7c755339f02270d49d196e7909f17567dec7baea4c59b3cfeb1805adf149afd3dee327c11785cfbc3ba156abf3cc53c3fb0e6294a46258ace64c3c3667d48266911663a2dc4fd99ce4b48c804914124144659c09f87077e08ebf5b6a8a341f3681889d3b154ed867d5c865b40ce29966885d39b8d4ebfd139a65691e11a1970212068ffad1f4e86a99e1549a41ab7c0cd0ca3ef74c2605d8977baf7b67a7a008c6ddea94124a22c917cd4e11a1a248648800a1d0b2a4414f21ece0ddc1e1becbadddd9d8abcd1f34f0e2979cfd5b70d3cc4b69bc111ef8ce2ad28014f2431ffcbac4a0c0824f2986807030f52d320ac3f2ff9dde907f57d2572b995fcb16d9e97bc91374adb62fef894da3de8d4cc54ff5c124f590ffc34c1d01dacb4d08608e53b72b15eb1bf4d04b49e7b5ba9d84662010000000000000000000000000a48490e2ab25988fe482316e16836ba1db25782c79ccf6f9dd70c8094e118cd7de9598ee5ca6de13bb3705220bdec4320168c0f0c1caac447afdb4a0e7eb2531b2a4d5e8ca7caa85dec9dff4ceab8e558a4f68bfc87048ce7a6530d7341be510000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000074fca3bb82ce116a3defb42a9888a11cd9e9bac061c2adba2c908f399dfff98b25c8b95396da262de97f94a40169545cef5d3c843b27ed8468a2a9736e5556b3fab479a053965d7d322941f0430189b867f99bf5eafcfd41cc867838591a47968c59763dcc35cc71804886ce8c28799048335a65d813b08a04c88ea9946648df032e1aad8f80c4e0093af5df96df4035f7fbc33645ac23f4b7d8cc5d640959fc00020879b4588734f7b6d04f7896ed0a43ce122aa29119267df34d9039d4e8fc05520a0047efd0065b7f1acae831e6e6015bcee9998dd8754223c597d16f1c378defb2246820a234d507ef637227bebc463d349521234f192d46cd82e2d6efd67981cb0201095fd9ec3737fa52ab118406de89e851612cafa27f9e48e2b6931c453dcdf70325ac829706dff986ff18ff6e35dab2fcce5842bca358427c88399fbdf6a267370328f14de78835fcf9e0addd5b1513de77c94f659b15ade4e6d8cbe58d34266dda03052b9bec56dc5e1af7d6469e0277e7d65da862deb2a1d0f4cb275ba01c5021f5020210f951a07d329a3363e037965b24870f7d922812feda26ad5a376341a337dbc052d34828a06f9d1dbdc8907ea203ef3fcf189effd3d2b37c536421f2e20deb554e0687958cd8b7777876c09eb18d71e9a0db1bc72f258d7e01226a2cc42a2c2812805d37f32a4008bc5d605f8a004e82351f745e7679f46a18a281f4da1dfca1e0528136ad9ee4e1823cce8003769febd6d5055002842c0809ec65351352dcca755e1516df3898c5846f16d4d6c201a4760b93a45c5c67d77f8c11862d5b6eb56ca99dfbb342a1fa38735ce32f69e45c59673f0e5f2270eac65775a2bfe6617ad236861116a70724b2a7da3fda4610140a2f0a0fc6b229a32bb7b8dbf48561dd3ba42c4d50c0a837c686ea2a7f169ad3a3b6f2348c178d1804b6b5698c49daefe10fcc4641e8cfe3e46bd5a30cff6c18fb388cbeff8f0e261ddb3b92cc40fec219684f4f97429979cd703b1b201effd99e2d512db62dce2e767cc646b2942a72c8bb78ab446b708332d5a46eed59de97fbe48f7b788961d0d68bf46f11641dc9764e075db7bebd97cd565c5b417e1878349d66696fe9896c8817949731be7efdd7c972d5edf90c9f787fa72d1761bbfb18135f60b927a8162a9a7eecc788665ebc290437c8deee22d4dd98f8106f2b6070b5600475fbc055345e96e4b61f06c2c89a0b7a89e305200f213e8b8fc09c2f8a50d017b182c883c7d286c2c8b451230e01d984fafa0cb513f91351686700604448aa55c7d3d8e06413ef57418e3ccde262c57d8a2ab2bdfd98b5dfd4301c890741f89311e811a3a385569f32ec0917a29afa5fc48d643ae0d4659551022c82f6bf6ba3e5dc75b655afa4266564c0c5d25bea46e98da52c262da628680ecf15ca13c0a0f954662da498e56a94ac792c33de785a4e57260c68c69ab98c8c12bc2282d39e7449c0d6537e62b8a19552de7a778abe90b98a82a40deccebe36c2da5f78bf8d0da7358e8a2c6aa3690565f86fec332421ecc506e272ae02844408da847f0d6fcdc5e8bb595423a5d6556c825d8308b815933d990e8644ec3dd20ca29918afbf94814c420935db0efe6ec3677ee1381012464bc93259ec911f4cf6b2878f54a68c2e1c59e7b042d2a6540670ad962886f554f52707e050d9cd0e00721192693aa1f96a2caa5a2da9b63a57becb33cec8fc8dfae7e540e9ddd48bcb5610dcf76b63d4640cf6cf9a0dc493854bc54249d30e21c7024a4cf1442fed6dc4d446552e49ab1c551df91aa32389610c8906594b4dbba8b9fa6a1e47b258c616dcd5b565abbf838cff34c939f113f81597a855befb89109e282e2a87a89335e357e368131b86cc0e1375f07080efe7a5f124f848531a0331f4c5dd1b51f3147d628d6216ac3df943ab55481104839ed1773bd59155ae4e16919c3efe22afce443fba216d3744507ef958b9c6c655bc2d1423a737272d02cf6cd451c74b87250e560cb802ff263d2792502e5cda17c71e8db9fcedc0c7ce973ac28831a7e6bfa9a55018fd3249ad0274f3d745d718aa2e19b5dca47ef89ef0ed136439abd34e574a5eeb35f4b98c93ccf0087a2c44d7680601bb9e58049221da31a095e53e8816b5403ac4de747ae7758babe3367ce08f8859ff78e53181ca384f35642207470748fdacdf33101e746279ad28943b4e85f976df7487e967355dfade7d9c35e5cf424008aa3df179589ff19576567a3c3cff26728223a9374c243172015120c4711e4b49330939c9d90100e97f9e35f15becf5d08bec18523886cd0a5b958a6f024cff1b525d28d490b48f8383ae0e3a102b6183385ff7d345a9f2a5c46b82cf00164a74e4c125b12dcc8b367d371d41b1691c22c8ff9c001ec6d33b278e63b9070b", "6a65526a6365ac63", 2, -1493272850, 1537743641, "77a369865a7220fe5242db03bb7b6c539181943e0fae84df56cf54c6e28d1bcc"], + ["030000807082c40303f0c5b727572b954107b2a47e5d2b6fbfa8d8c9cbbc8dd37b5a1ceaea6c99cfe401000000026565ffffffff1f530bad7c338eab7e5274b74c8ec140f6d545df60d4bf0f35accf5a1a294a2d03000000070052515365acacffffffffe6ad17c58a9a9a308e245521ec4158f0384d36ba852d3c09ef1fc3dffdd6c7ff020000000765ac63656552ac576fdfe40381b4880400000000056553ac5351ed96a70500000000036a5153bbf2f4000000000001512f21c2fe000000000291b5f305000000000000000000000000879aca3bbafb25edb46688654abc9d9b1c497383acb95f3e75bcf4e8c319d6d88eda997028edc91692e452a11b81474235eaf2fc037a11c9249dc54af313527fee56a397f305a89b175069d165ce88c61c594aba04c013e777041ee3820d543d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7355b05de4799fa561fbde6fe7d7a3eeca7ad0952580d38cab916768b17b2cfab3fcba33bd9d8da2ba08b05b20e587825363a108d27312adbf8506962464ce4ed7f0bf8b3b0b36d32e3549f027e0d761070bb90cff1bae88bf11418bc918c1040bd2cdbe82c10474b3ab8f7ec5e6bef6910e46d3e62b4e80ec8d9191bef07bb022a3795023befd2e0756df4a072e6358eea6f485187bef8efd590fb17fdb2e93e030b1e6498fedd869841384186a212cacb4044932fb2b00c5d1e5868babb091d970b05cea399ef6fa79d68d244506f2c521a6c734c8c2d325f62e3580726186438dc71265b2f6e245bbd0b0654242fd7f40c2bc793a09503bd2c713dcd27c6226a890315951d2a54a65112b34fff8b0cfd71fa38cfab8bba18f1d62abb0daf7138250d032a1b88b22e8abbdb455ada2fe9cceeed7421cd63926254d2ebe8df4493715bad022ab61b88c6311d0b1230c274e565d7a3fd0263cbb5c14ebc116dce260209cd0703209f287359a220cb01d04dacafc3d81cdbea19313ccb1bc48ec9b3d64093fd0e03108b6d44f51ed58532c4d0cb08dcfc999f0f025312d0e1de9ba9a696e30919087638d2d2b2de7c90cd8acc475ebb8bcbfecf37eaa58b65255014f43c46b3aa85c20b4399baaa4f8f1251faf0db8691a04d8d1f6b55ca9c5f11a1b31bb02f7052f8bf2c1eb3608e19bef4c266eb1282e11eabbc331100ec19504658496498bfde93889c66f7e5562cfe736845552c1cc5919f64ba7eea2b233902599d67a0c1db201b0d59f8e7c341eab037f75d3ff580f57254b3a9a8c1528bb344e0cbda9e9590a73c8d7335b3ccd676be188ddb8f00c94fd0859ab2f9e3cffbcc4ab1fb65569345e376e3f0b1e13ef71c73c634b3852b0ed4eb99768fd2191e3675402a7b4f62d0b96051ce30df89bb7ac5310235270bfdd0ec514dea92685471623eb85ca1765596917461dbb48c06a1382bddccf0732da40236f0dc60f7015acb200b6aed4c877955426e33d69fb2e5ee44beb3002824b2748d6f9bfbca70816ee51a92618fe021774ea7ad6643bdb3d5f98fc15650833e6696ef9a63d4547e02fb24f48f4b66cef7cdb0b392a0ed1ed28a2a44865887602c5bc46ef0dc3e79ea706bc46eee83535cb842848056f892d387e85b89564fa54dd37b17ca36f522861ed92febaaf229bc61c3fc114c6da97dcd02fda15a2d2a84238e67d5c262d5f677c27d4b57fb8908aaa3b2ee5cc5294c24bd127cebaa9babc30b2f343cdca6db97a6cc2bac52d566c8c35942a147f193616e96af2266b6e182eae85b993f84cbcf82f8d559691549881a8a53f1bb73b97efe26e789b538870e362f89f53e3322bad099c39ece53c1bf9a9fb60cc619a25ee9696a812fb5725eadfefc6447124fa472424f1f16bddbf1dc5e4338fa849d69f8bcdb25eda32f03547191ac4e1411a8682823fba96ce82dc1f78bc257b253c62f962a4c89b3f36fbefcacd726110f4c63b8d6c3dd7d0c7dd9ba09bf8e05bfdef4b74d3a02e5504719a87d5eb58377cb27fb65124dc589ecaea0565e764923cf282e6c1babe17ad9d5d88e06debfcb7c82263ad9a1c608dbae635b292930bb9ce012e58899c138cabd952e89ddd166ea851077125faa14cb5e9bae6a9fa00e8c497c969d083cc31669f04a11b467c386108c4bdc29a38fc53f4fc9d23f8a14ffad6e0bcfe7df6e85d44a42fb25704044c2c8cb0f325c5540a08d6f4c3237f93a403b4e04e516a3dec5bfb05b74224326f6a4d07228a524aec8d5d7fea3024deb56a9d5fc6849f224c53d781f24f561974696111180e5078c25346de70e2ffc07f6b9623035313348b5a0ada264344099c696da92584da10ff4e63d55cdeb3d166b0f46e003b621afb9cb35efd1b45e08dc87df7e5a6f3c36c5bcfdce70af93501c183d01e68bd4ecffde506cedbe30c218774d5ac7e89aa20f8ca34d4c2f4ff3d8a9f9f534de88aeac2ec54a88615b8ef0ab6864ce77a3359239c6f262c8f2b9f00c4d3c05a03f6948cf2d54fdb3e7e7ca79681a12ec4c4446de0ac50b7624d9e3b6fda28825e60547229efb5e78de8ba712017d210b4bdfb6cbb5d75b863cf62240f683cd554278be27ccde65538e90132e590be3c368537023c5ecb3c5d4a6a60997c4ed7060715d2d0689ac89ee4e2bfb90d0d9a154df3d4c5fcb75357c66339ec391792f78f5df84b365941d835f42ed0160c90d3eee490b7548362e614adf98b5d65d7c920e210677e682664c88f163661cd0ff712a14bfca6276aa8ad45f5944b2040000000000000000140b01020000000056bc8110e084dbc7619fb769cf933bfcf09e37fe31d1cf4d625c1c501bf32f0e63f0acb2592c8c6fb6745f74db3f6e0c1432fd57077f1ebf97d98a26c124a30838609dd5ac27de061266456fdd0d5ccccdc2b10c7e8ea16deee6de4d4c5f7aae0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005590090b738cad52e2c0deb7062ff00af541c1ea492a3824ddc7c69ddc71ddf7751b873071b36946806c6c8beaf0cbaa6c47f71857edc9dcd52e7b253abe3b4153ad64a0be1a848fd3a67a06fc703e7f3c3658453037863a27f5a06eb2baeb329d13ef8987a3d0972e12a0531bd39c1b1ccd21a127c04c9ed064af7e00968e6031aec46acf74b4b4c3cd714e63f9779922052784f081cfd1486d9f3b6e12a57dc031c866297120e368a05c4b4e7fae0964e21cddc02aa2c71ca4b51a266a445aed00b06d0ab34d3325c65b555233130283657100c0519e613883bee669815722b3efefdaec7c061c0899e2a85112278575965980638d1b7cd9df514ce753ca599535d03026104f3781d5029d5af17568c196bd8af645489da9c6062347697db856fd18803161697a1f72d91aa4923ea8e534e6f24a3abbe043eefc81d4085d5503a9acaf9020f3fcc7a63dad16108a5706a8dae6791bbf9cdb125d6aa633d7695c34e7243bc03178db95b595f5fe22a0e2ab6badec02fd48b82e3f62198558aa344923d83460b03182dc634b8779a3e5c5d407e4e2f890be97c73b198e195fc12f9ea6fa029be72d5b788caef59ce95d3996f9a0ec4a3587e47803dd03bd59760daa3231fa41f12d92e772448061cd9e38594996ae6948380efdf26713934b19ba23182244ecba6cff671eabe4bba985d3ef5f5c69c65051218d97612fe42c6a0179f9c1b3d37ee8a0b36fe60d93110680c3cb681890a89b60c907cab813996fd5a833db48743539462c0fd9812af9d5ca1eef3bc2dc4f36bf8e583dfcb35487f8bea322bfdb879475afd3230f6c12e963095e4928aa2bcd599405a17a06e3aec2239680acf8000a6bf4f17689a7cac79dcce85f7d29a7009e4c93375a3706237ce40f18aef60b6a20bc50bb03f312fee5f60556fa7b94c47d5d41e272b32994fe7e08ee3bcabadd9cb721a8768674fb97d4f047ab9521ceedd5ec48f9f7a46862f6ff12fad6f424549ada8e605aa162860e18926beb22740e5c51e088a1350392b015bd993381a04be90e04e10ef243a1a1b492af10aa07e4764d722c593da62498745aad4ee93de156d5dadb29dd1ceab94fb0ee0852c5a252c45eb037647d9cbc515235f4cfd66f2371504493c325089bc166964fd2a98962330688df5200897d8efd1d6f56be1e74f0d753e393461b19d6087b5cdff497856373c115123dea12e2e044c78f343c7850359357654bb253298695cfe4fb21f7033036dbf83e25d99168dd813c57074be14579f907055051aea172285f9c1bcf7c6a3faaa5fa5d3baf89d49e13c4f28876e41e9b11687082e7e28df17cdb6815e2be75d9845ec3cc491e54f61efe6efbf679d9ba48441d1e20a10057ef21d9da9504c7ea13997ad8fcdf82fa1f8efa1de988b8d06a75cf6ff8c7b9aba109d964fb8ef7a5dcaf74905d2c7c8aa1c016afc6cdf5ae8cc638400e74b83fa8046ffb13607c1267e777c12a8c9dc1554570f350ac0c9ccf4d368dcc230ef668320cd81fe04f2610127c7f6a53517d7eb64f357b8d858bd9c90050080f38174b7dd82a6dc37907c456d5e6b29ad01847bdaddc0feda06db3ade1b3312b6e11696fdb3f9dcf6bf683ee2daeab53e451fbd610a47907c49bcaab5f3e4740ddd24e793a215a9e41855f1fd266c40f37d5fdeeff036cc81cb8a66c55332ce47022d3e52935dcf064db392fdef0ca05808e764a33345a7705be0ab4c196970a60c7ebe056cc4ae8d9b8fa9dfa9c38d239c4ec99583662cc4d67c4994126b040346a5a340d164a40f21e093b10db0689fb291cc0c737eaf19ad24e2a3b0260794bf0b170d51e0a389968f6e201223098f84334e9388f0264d502ba10a184ac250993892419ae9365af344cfaffb3b21da6e39a545d58746a67245ddb1f088a92817efd501e0e37a9d457900036a11033b57b53a8a1bd19d3b20b4022dd8e754701badc64f25ed267de7c700deb19b509cc5d4a72a4a02415d696aa90710f42f65fd0154c463f0dd900dc59647dc215cd6aaf48d00313ae7b35368eb2a88b2fa580698f72abf4b3b2080ea10a3d595b54c954d7c906df4f24fbf687037305dc0aa5159d622938e90ed20dff7a566a496b7d4634b10ed0f48bec4b1904c41f30e36e3874083b69539c8754a5bc487ab54a241be5a9c70212e29aa0bd0bdc5db9412f128a17b313b7afd432796170c4e8cc0e29f28469c5d7ee9c87754f19eec80450eb93e864cf3e30e4ae4ad8ccb8241c92a03ef9e659605a692dae500a11f854616ce0d582ed5d999313b4b35fcac0f4bfb22680a95f2cc55e68b05b61287fd8052a5f33e4bc6330c0fefd5a2975c95327a0cfd955e9c086a7db5a6f07d2a967b52f2cf54a6737560d1e9b40f61eebae29da53b56e24cc688f71f0ae483a1da832d739ece0e", "", 0, -1220965910, 0, "b81e17209896bce8269392e7b415042da0f86ea6f9cf08e1d8444118ab0efdb3"], + ["030000807082c40304ec0b0dfdf8561f67bce42e11568d27b34fe72b04ba9eb7cb64820e4b0dc3d9e5000000000951ac6a63525200655313ce5d3734e975855fa75f6231c5ed5ce96b6e0bc40d635cb002341d655800469b1853c000000000036a51ac3bc8cf03f718ea605160797e6faf9e33930c9493416bfff094ea5d9cbd3e85a8179983af0000000001acfffffffff95c7d91ea63893f77f13d1c81a3d70cb95bcccc4bdb1d76bf0b8ac3ee039aac01000000060065656a65002a6df8be04511a55030000000003636300089f7f010000000003006a651778eb000000000003006351753d4f050000000002ac6300000000691474e70300000000000000009ef46a0200000000317f0b68537a41f31f4ca1e4d86e74f3f92020410697080e9362a660f63be18f1a76ecbd0ad9652b93f81e16cdace9c5b7d02750739d55984df5bf6175061fedebceb9a5987213cec332bf9311ab8fca463ab85361129b7c181233f438ecd54500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000da87f8452366fca46ab3c971c82fc82d5020b9859e49598c38f286a9001881c0f24d5280d967b54ce105269f60c60c170bb8b3d7e1c5e5949797eb090f3601d82df0ff64214b9830578d890d378093d2674f05f4aa80ff93ef25e9b83788f6c294829ca74f97f9a82e3042acc16b2cabc299196983c2f628ce2a3fb864d38634021d1f753e38c32a651c754d87dae4ff4f4cfffb937cf3f1fda261bc9cbac947ae022e7cc98a65fd172d027225a149e1b904dfa089abb659e9715cf58d11840ecfbf0a000f801e45c61ecdfe302292137dce524cf9069cf0c10ad07dfad94d1fe3580b8cb30a358c6d0768c2ee1b78cbb4022f7b0a1df7cbd8d1a18a6ee12c15a175bc0224975ee67243fac5b8675c3b8263dc8bbb0c2af6dc3286d1d75c6da88438bacb031de10d068a870f66fbb9c1994a59c0619bd078cdeef53af43068159a6830419d031923142317da6334e174b2556c5affaf9ced816ec9561be526b95c7ae81dc508031286c32d3f54faa24a8dca95c195765cf64e0940013a821af8829ac089176d30020faaa9ae4073f7caf9ab53185b71aac6b9aae1171a57b4b5aa399983ea06474466a25123ea650b7baca13a361eea018e5c6dc5e4dd943f0520f336be28ce5606f80afa18f08df3a904c03447ed50ff81ffa85cc01fd4116355811fb4ad19f10db56cad17110fa170de742b1e7e85aa52f2a245b51610b5790d1d38f101e84f1a0bc05bcb81071c5587b44f8fc8f07ef6ef9c372a2313668d70a459b8e19f4ebb92c7c7144a48cec17051ed947cdab4f3684abe0037261eac3da8ec33ce24070fac599756eee3bbf93556fb3fceb39df9f2c986700fb680aaa40db21ea6e372aca19b0ad0b58e1803f1ac9f2c96504dc3872d03137889b9096f609bfa1a6a671ea6382da5d84a9a1fa36e8abd2546870e6d1f146053be18bf682e5db2a39f26bc9ef3f040a1a9d5e3fb5d99c466a08c64fe1f64f2cfc0dd39d13334f12d0770b814c73ee45e2d0f15ecc8d42c084002ec559f483595ceff89207deda43a345a2f1155f8ca147e7b6597400f5ccb3999d98bd2db8e576d01ca49b5f5a13eabe9b6e7bb520e204cd5057761825ddc44fb5c08e0b02b8d16984b7dac0db4a5fc4d077ee22d5ff8fc36594f4347faec4caf0b4390432480e92db8b670e044c8634fd8b0473560cb4cd58e3ce5fb58d106dbb2fe28de52bc3664f67c4870ed602a5f4b39ce8c715ae8025ecc9589f80d949a0e8a0452844dfe06ad3aaed28750fac2e6cec84617fce95714e500ff8975a91ac99f113f87506618c2535a3a086d1979b46b25c8f96f111ca0ebe090da613eb1786ab630414ca12511e738c1f3da20d4e332db8d60822ca39d0425f30aa04565788a17db3c6ab474a2c587e32782dd5cd309cd97d563d98332362c75eedce7e4f6cdea0986f95527445364e885f11f89dd5a71a586401d0dcc8d6b3466c5ce2d019e57384c037ef6ffd2bf8d6aa939d997b8febc1e817d0f0e61807240d27cabe46e3461caabb7509681479d1df1ae413d53c78dee290cd75254f19e70cff23172c8281e4244286f51f43717057240f5930bc5c306f6a7812b93f081ef65167fbdbddef91ea9d58c7e602027d25ba5b2be4e57761e82cbf8bc341ec0c3cb558fef045156adbf2fcc441529043a9e52580b4c50af80c54369a15b30bd04e5f7e782775326f86f1c571eb40dcfd48d2223463ee06afa2e8aa1d99d2804c2c6e11c5aa12e7abd4778f8a8164600ee883a383f0d0b3801752299f2fb7e361e2d09bbb0771d76ef28787927998b4dc8c781fa40d251ca5b4cd3130e2004ec6a11a0f1430526f76142f66f25342e521e5164194b3321306406e4f2be53bcaf2d004b06808c88752fccd13d7eca2d86365c64790f1f6dfe7e69407a7a08a38eafcb3850c0c72188a25d2163cb979e43526339a92076f5043207036b878abfa6cb4990b7488cb3ad8ce74aad433a15db42ddd5e423a6bdb3fcc45829e6807169a251e66c43c74ad379d817f5a78ac8cf7c6fe6e69b2b311d694e7eee80aaf1ef1df24d2ad5d62c26aea73446f4e40b160e31fed2b9e784c1644f3533fdd95c998deff28d0089e9d5ff95c25c3446fd8a75a8124b63f99f8ced06baa4bb14ab42e10f159cf63b9918039e5456d73ee8c7c0cb0ab6a746221c2b6fde36ba4265aae97c9733557817cf041730237b0e8911d3ddf4722bb61cb3301a365c61cd073377acb776153b86332d4f351abe350c43505bb619b3fc0ffa7df1f0922e0ba5958f995679abb3800000000000000000cbf6e30200000000211b3305cd3aeb9802702c7a31ca8599b7c433d8dd5d8da786ee456c8d162913713ff010e134f5197af65caeb23dc94470a2465fa59fb83e762e126f5a1ebe2134cba145e419d9ef37dd93e9b65ae5a883e42ba414c894213a8e5d6900c0e58800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000605124ec34ef74469c7d42a4c4307ee943c0ad67898b656d19028048dd8130c12c83bc61eeb9256c1372572e188808bfbd759a05abd4c8a42c245d415a11dd7ae8bcd886fabbdc748a67d15ba30834ceaedcdc4807410792214a0938f59b5736bee9024b6b91e3dffe2b56280fd9857f41f2de4a8140029deb779eba0f1e3aa6030716b62a831453b646b35d567ddef603f5288aea108d1fcbb658e046125cf1d1030f3b14d65bcf360fa15326d2735bd40673c2fb6fcd5091812bab6e3f7a5ff46a0a07f3cb97c452224e9639660a8caee0d11519563f4d8591433a437e6b5dc23d2ecdd3f8b5abcc157a71cc0ed8dcce58071dd678c0c45c7015961db6b3d759779d032d05ca4490f770c75d4b957532d5a456eea8eb60409ebeb807180bbca6af9511030a4041cf7c2e2464d8258bfd3771b08a30220eef372c6ad1536be6420408f2d102095c27c54e32169ed375e5ff8803c33892b2ecd6f486905d5045878178e09eef032d12ce3f9d4b0454ea896200a1879ecd153e69ffa5ffac44d24116eb456dcb5e020858c06027cf93236f6e083adea1a003b88c9827293620b784f255315a5b7d8a1d948057bd0f2994114eefd16a6a2bf0493c53f1b0cd2a7aedc05e8fb2d9e70f9dc2a265a527d1ae2bc2c189526199abe724d58048e70b82e8442cb7cd0bd9fb7e1323045df625df693481dcc30f709aa3ffe5322b018badb8aed3e8c07809d55aabeb1f52a0e1c526cef0e5751e67c97af590dbe589b10a6fc03859709119be2016bc3adf64c45cf092e547f967e55634365a2a9dc9de6310e8c4de170a5cba3ae14e6f7b727acb0ccd63a726b0f8e45650261a8ec1d74fe93c069f3502f822b9cc95414f418d47505add8a00fafa9fa2a0c492af253beb92b534c12b035ef525ba0e13e9f8f3b63c574d5b8190cd6d6bd607e1f0c3adf48e049afda9a020118e814d6736b498cfae41bd723c10b35b511ae3f4bbf7572ba01875584547c0f96145e94c9b3304e2a8a6af7ff30751983e0900e82ea248f19e481e2d18cab8a0427c6ec0f760675fe2a8f1cb3451d91d6404d4e0321c3b648ad73f05594160a9ff31015014d27f001c3b74b16b6c5c7471ba817272ed86a34120e6648f61522a47800cb6a2f5c211d2b408d866d189f69e4c6f7ab07bd76674d813e019b4c674be827f612cab1ff49a35f3e95235eb864951b7deffbfb71c16d4a4f5ac6e90b2a23c517a8484029942e70e37e00aefbd55febf8866b78dda500b842817922da6348603cd2da21019043a0cca8ca0e4293b276c6d59a3306924053b585839874434dc3ebce997814468df54738addb9630c2e1c09e8b2401294368b7b3e827c640813e5145f82cedfec241f68edc360e463e9becf5312e12c671232f946f59b5f6ccf5437652c26ee37bc9a18aabb13363ad68d4bd5b6a8a26aac048221bc61e65a686ca36df2c8ace01b8838ec3d55ce8bd67da374ba797cd7e1ef3cecfd45de36fc9b8ee0dff7c04193990d86d5c3f20297d0bfbe6d0ee688f42bf9c473a48b8ab1be0972eb6b63a5930d5b0f41d7dc2e7d6386923a3962871797f4b43e0cf50efda8509a243c278854ea87f6a4b8c2092a64648d721bce072fc6153ce2b3f75d9bb9b6d4753dbf8ee909a32d3b21f3a4b85cb5290d6dcb2db85a2faac2e98569fee5e3d992cfa1a1436feca5582477b5c0dae6c56aa11d36ae512d6e9a1f28ab047fca72d361abc3900b024d5a8b398295dbcc369c763e4ee061d8c554b1e28092d14c7fda7081104c957a40ab75b4de8616f658de921fa9c34fb84bab2c375fefb089329355ca67682e44a34db465b609346c16fd7fe3d73def56c28abe856d723a2328d4eceed643214a189ea632616e1d0b7aff30ddd0780c5fce514b9da97c909934bcd0b8a42fed7d726dc050fee5d53a4cf492f30b3536a9be028094576c2cc5fd2017d9ef25cbc711cf506ada0be9387b103b992d8362e3e4606dc0f18161d1e83fdc490101b1836e5be76aa1db00cc193cde698fd81a800fbd143d8493a7041aa717133ee0c142a472733cc86726e6e68f01086cc99bac57a1329ec35947ab67bb122d5339ed15eeb578de6466e99a3f2377c315be7ed09c23c8781797aa69fd7a3aaa307d1762e38758a68af7d67419679c9759b49c193aad38446c33195a46091cf896e9cfb31db1ecf18bbb4d753f7cd1539084533d24aa1bcd24ca71c4e8e1cbc2476839f758b7a16dd31a264bb0fd75d7d93db9b131c11c0490a10e87db26d481b4c8c196420eeb4bebdabc6fff01000000000000000000000000ed4603d80f9e9628045e9b4564e8fb2a3cb13e0d9e054f3732790beb5dad4a6798230155be0e27721fa033ccdefd6d4e7bfe996371fd3cdfcd46cebf635eee8c02ed46f9f063b227092afa88da8ae680c8a8dbf55adfb52330048b647965425c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000125ff4cf9f5a8534ba8f72c9995b016992c40fc411a1178c53c2b11fec3c1c429a0d64e7cd1d508543b0bb3e21d5a95a78d0e8f2b704e7055704c6dd6163720e06ebbd3d06e71b4e31c2725402729a42d0d00d250d31d2c62930aa2ac6d4a8dc169776a493d0b0439456feea36512d38058814fea2a421ab6c1fcae390fb565c022bf08d001ae41d41a68d45915a31c3db84d1f52e36f8051d4d134a89ec338f900308039f6048e656c13d5daad3988911ee68b2105f9ea76b208035b1ab6754db810b0704274a564d08370554d170549208e96fe4c41288b7c3d0c0b2d19a4dcb6869555e8d4e8335a43f89023dc6d3947b8ba93d3bf6f2dfbfed1e2b22312789776503209fc09e79051fce1de77658cd55c2761bc2e7d9bbff95b80c36c7bd7dc56447030b5d5d5dcbf9065a0d141b46d0f7ba91760cb029e43a0fcf58380ad6220be26d0219c94ad19f92fee099c58c3e2d9005981cb847089c9dee3f59ccf563d2548d48031d168a44f1783a10f1090d40f4d64175063d91378964b2ca96a3f8836c8f6380032151da995d33b38ba9f66f81eef40a9894d5fb63e9c0f81645439989f59c88326ea8c253c0c8a30e1e4262a6a6a19cd219648e4521e684b1b9a7663cc3e1f242f508ffe8c89816fd92459d396248f46572f98c5bda493868131efd58efb55b90927e54dba35cb75e7cd08ad08c6b87b4f08d2990cf081aa8adaec6cfc2e352b1de61937c7a330a9a8f184a615924ea84617ff359b4cbb73ba6d5d8b944a31d0c4cfcc358e9305d37dcd2c5ac6fd7a73bb7ab2ac2a72166def7a5ad8d7935cc152aa6f1eb63293e09aae3e6fde0baa57553c18aa1aeaaf936e42d5af7ba9d5199889b6983863a772859a3267dd2d8a268b1d2a5038e41768aeffa4dcf55a189eff8209ce4d80c3754d87eb26e888fe5e9c3f34559ee32fba4da4cbad62520b1305aedf16f81620dda5bf0f71df9018ba71f2a0aa07a1a48dc68ef208b98e45cb7c8b14cfe57cbfd6bba187f36baf0b5b6ca930f91bd7b79f648d12e41c8803aa1749a94a7df8ef305d0e3fa23f41eca39a8586968cf938b685343c0e79c244044d2fbafc44ae1e99f0d9b59112b69f43a1c0c29593060b0c63e043a730f3420a2b17eb6df4e045cde42b85c1ca1c9e8630d7f11a469067ce2d478c3c8a495b20ddbc9396bc2dce02bd0dc18ba183a259df5637633034c9caa13c021b612a55d57c73ac65580a8484a967aff1e5b3bc7a8c79ad53276801f3a538fdb7a390ce953d0040eea1e20ddcb977c3e4393fba2d6176f5b696f862c8d58505d696bf68d706313dbd7c0d99f2d0d613f5d5189f45747d40e94d11afa6bcd67d28f62c2f36c6f3053b68a8ef7bdff8ba5537a223716a201f05f58a026b09113cba1f1d3059af69aba6a732d2ee0d72f0c3dee58a3ea199648ec83afe65d25c785da5a2c911c165dd2fc690d5884d76671d48abf8de575b11203bf2c0eb518132169102fdd0c9b1d1c3cf56ffee314cea986d2a335be85a1b237790a7d7b62d05f8d8186c1aabd4b3bfa03e6decb326d78042d9b4344e10a25b0f60ac370bb179c6031fb45c873b1ba480dd5fe1cc7c6b4828805d00317b739f3a32a0a403671c73e0da58f1a07acdbb7b96b5d5c68551f4ae6b5fc2d7ffa07e47b40535432902d88f5e41180ee7b07c3e98d6c334ac997539ec15c84c95281b85efdd81bb6e4a89d78536088f6c1abdbaf77e235353fb7de45fb91273148c6b212330a31153535eb32040f4c11098c0c7aa08f2d5b8928e2bb3b759c847e0be816f4a2ccdac898a37facf8f1bb974223b3f4665c81d31a68bfee9ea3fc7e7c643a30ef00125638490ef314fdb3b13fc165e642c9dce1d5556c48ff20823af1bf6a15ce3eb1806bd3a92e4a16b75eed4729f2843e00b0d099a83857b4a2370745308e2d2026f07a2aab6bf7a5c4b834712d4a061e9965efd9fb9403e522789c92a53590f381a1d407a1598a0166d7e2b2e137cb1f9657cf2ef075af67d1ba528fb7b131840bd8e3dfe7c6b64052482b769bc3239b873852a777bfee8f559483f1fffce943acfff836bd4d6f71cf0942ad1cf788cb60867d5874601e222e4a661a271e8780c7080418afb4fb09dbbfd809657fd866ec87a46c8ef9c618dd60bd38c553626f293733f49dc937670d1924fd034aeeb353a29e0071bbe6202895090c3c31f043e42cac69c396e6e29cd516e76ee18501501932d8f539ee23407622a4efe4128c1fbcf1638db13511b60fc7bece2ade8baad64ff73daf95d4beda37c0d24f15b04a5029dc75a7ebb67a44e95c0392f98ff97bdaa2d726f025189918bb58b80efa0cc64e485c5a80cfd8ce060d219953d2c4666afe913d467f08c2c37d239aeeaa4731e2f0de6ea53a40bb2fb867a9ff189a0bc63e857dcaa05b0c", "5163ac00", 2, 1533195145, 0, "83f24879bc1b7b87f271eec21b233314b416f4b5f8308cace50352611717580a"], + ["66caf51402bc668a943b00b8cd0fc2eadd34c8799dbc532aedfb9c8ac098c5cfab3e51354901000000036a6353ffffffffe92116922fdf814202a638057b264fd025ccdc6f6bf569ece40ac0a4df862fd401000000046aac6363ffffffff01e8a377000000000000e1ff705003000000000000000052ff6e020000000003d7f3e152d7c9f64a02a41f4ccea972057b997bdb95676211d374401ae2cc48370ff9752ed04723285b757bc36f5cf5c0f0d7b6fd895ff6ab3fe63e77c5a0376168bb5d048cc7b932c3a0b8a26d390e418416c1fe3053df88671a07f5d6f80b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009383323cb1b69b76e45c09007d2d3fab8a209a856f7e19d2df38f880ff6ae77da47c78fd17ac11ee280b6afcda5b2c36075519f86402e208030ac91edaac38f3c46efb6ee7e911427f376bbfc7c5beb672197dd633d7c1afff15fbfe2659b1d23cd5047413048ebd480d0d01d8b085cb1e254c7e86f792ab4e746b486fc614ed032a69828afcfb502f2f2ac8d53b3b7a1b2e3b3ba5e3f34836eea12d28afbddb82021d4834f178b24a9b3638dadb947a49b9ed20e693a7d549b89bf5fa7c904657b70a0809a791c1869e85483f1826070db20bdace2706bfbaf3fd8565f367974984ae2c233764d9ed17bb09e369f597d51e91ee928efe31396a0bfa26d23e4441cb7d03025c6b91310a630438eba58d387cf4af0210fccede4dda97f57e91bffd91221202151598bdc703105cb442d89fc5f06f72cd9eebc23df9b0cedb82886f358420a3022125bf040b150fcfe43f120aebb22d923a438cdd820823c6aa14f9d7114247b60219b06d251646dbd3065403701928fd3350473a1420016cd180843dc64fd2a230032e3dc9ae25a51eeb9cd55e33f4f2dc0edffa6e7997aa690bb9967ca9c94f6a52687561a59c69569aef8dfc080a02f965a05233b203fd9bbbbae5ea1154e14f1f913528a53976d64e44e93f5c6883109cba6d4daf8d55a73ade88a2fe127738af6cce526d4aac8ed5a4d03b81a2ade37ef423d0018772134bd59120f34d367a0510d3b34320b65d41c76432687697ee251ec9b35c55992a48e472efc18b0a66b10dd974a2438d3ef2618cfce8680220edbd80ca43a15977e360200c080af5b1e923698d3420ed53bc3a4e71508fe84f811c12f498a4ec3bd8268851cfa93d33d1ba0ece32de6d4cd97214f95767f4a9447b97aa0b450c205581730e045ec86591497aa27495da978e8371141e6ec48bf3577521499d27e8520790c2107854b5b08302b39a800c7d35e48bf4d50c7b9807de7604724cabefe58200a983526d15e62fde36bd45b535b62ad14abb4d772cc401f327512ef027bed96d4782375a7426f65b52cd45dc46a67f1b6b401397a35e67bd6e6ec2e7120e3f622f19a6fbc610e1fc6db1351c54848fde7023601e4ff9d795702fb035813a605130c78572d869c9141245c8e3fa15ccf7a40ecd0219701f4bb4c7ccbe63fb1fd1a5bc4a63ebebdcb9b87604965a4c0458bdd152a7e76f7edce1c4981ac3f5626f53ae1fc90f6137595b7f06abc0e330bf623babf8b6c5291c1fd10d30b04643f2b2f7eb0dc99f724294aa4bd53ec6cd3c265b7f357c9754575d755556ce0bebb1bb455f46ccbca0878dcd393f52c3cdb2e520b16da53241bad8ac6b4fee0e42dabd388f9484fa98ac1141a033a3ddacba2d75f36ef1ba7c0a90ec32d9b3e6f9c2c4229a6a89766fe84130f55633d5fbc5f60fae84b5e2f92f1e4f502c9459e1b8b3cca58f1c920068320140fcb77663b9fcd4f3d7a7ef5d9b029192ea447b901157b9e433d2431b457bf807abd12451e6e72188116407aec692176e04f5db2e7d8af9e99accb52a58e0a3d6b4436af0940607e3928517dc125b947b19938c8feeeae1b01f794d0753bd85d949680ecd84bc653fe87914650d8fe4b98cfc5c874c40b24f6d5837340f8ee9934e599008f3ac293474f0a880dd4b07ddda47f5198a0e587f2a96b127ec675659408ec5e6a6350feb5c1ccfa816d8be842d17c8dd50fc1079a585fdfbe65796895da6335296ee87f5b17648052833b7a00fa28e74a1b39a66d10f8606d52705decf4b6c7966e49c9fe1d179cb6d3dc8f8083fb113e0437418c8166ddcf1413baf50e0e7647c75d9a900f00eb8f5dd2892b180cc75b04e6977e2c4515aa4a8446eb1034d248e9bac3dac7efa537c24658a2858bb4df4f453261bed4faca63dea8a8a62a4ae3b314e6dbc0523cc1ef033a9d3447ee262aca0b2b429005ef83abe9257f8780feb0665d7ce935dad17d3bf606d18588b5e0ace420e3e907077514b7e1c6218ccd4d9eaf7cbb7fdfa459e4f568ffbbb8e875f4a0ff82ab03519f4390cb6ba621a8e579cca48033afb5fc739b1519249c6e4def3453b6dcf929f84400e15b37d6c89cd8353274327ab4361d0806c536e5594e9105df197f1dcbfe079b6dc62331ffb9a80b2ab51e02b76ca789606f9963830af4663a9409cc6336799f39bd2d50e0dee181bbcfb9da1e82ae54f84ecc1c730cc920f2bca5aaaa392d3f17952650b4fe25adc7ce5882d5fd07187951845d7448c61f2b1a167e02c374f693ac937ab12c729124a0d379a28b148b6010000000000000000000000007a59f617d956c05c5178804f5ecc64cae54e5aee988976761df592bc98fa69f22c414f43c020cfc3a510f5694510bcbf902ea00ed1f98e0ffcb722cb61bc39a95abc9230de56e830cfe8fe832c312cc2b2a064a2ecea2924273b83bfd5693d85000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001277f24025ed92754600d7cf6171360bae2c0eeddab63c32e90cd8f4d8b80636ab876522cff30cff268fe26ec1a424808134ca62f1423d3d1b070b59425dfeb566512d20f0b4d215b39708b545be53e5e756132818399891f8d3a5ad81129ae8ff174743b8e60d71d18eb51d834a8d96e16b2a68f4f6f8ca7327a96e78a30f4a02154306cea74671cf390f6e0bd5877302832cc1bf534fd61243bb1a941279506b030d3108b49f6b337a84bfcada2d0cf143fa81d5f726c028769d2e02af784e5c600b076176b17c9c10854c5b63579cd9aa6d494b3a083e7c6893fa2f80cfcc1795abc221737c113855fad4b5e1750346c9cebf443fa42d841b837dcc1ac3d121b58e032295edff309fe502c61c14d720a9be8f22013648539478ca9304d92fe0f993d102253cb04a7d73b9001ce4f2f39a63a06ac2f8b5968aa2b59f20ef7f3d2046c4f20215b388d48cdbd107f58d5cfae67c43ea5713425d59e476b75c4303a865d2eb68021ba2146814d816fcf9b5b29e9035678f410213c522ab0a8148bbe55354631bb80214e0e371ed3deb76dcb9bdb41640950f9496ae4087808f05261f72a3b354536a03515a9d8d1417feaa6d182ea82076d3ad319976d31194e2ff94fe50628aced86dfa921fbbfb9675e19ad0a1372479d9f8890d203ca1a469fe6ac432c564c8fdc8c3607ae40c540f8c6202f27c6b0f527511bff5d4bcff1155dac04ddfb4da1bb786832992f54625e65633e956cf743f251679eb3f72122d3fcdc38df84ae2c372747de6d0f0a72c83bcef43af57097399a57eadc695cfec14c758cb903471cf5028dfa42c07bc10ef23952c1ca2a259f5485853d1c14e4687804361bee18e492581b8cdc5f1884769c76e1ae89d53ea3a7901a4f3805e55206ebb13d9022135ce24868f9c77d18a84136f17dda1b5537c93a9df7c82570c5388af463cdf634582669c4a77772a1e3997276b10db1cd1cd859e180d870722d0d776e81d67010afadd5b01a5a1fd036d75ef2cde498886d87a7bc5cb332efc78620cec0002c357d8460a7eb9f1c8962616f365b2bd53dc1d952fd28ad7b52839f56fec6a13b82b4aea69159d2882f2c7dd6c875aef4c4e6847bb597682db2908a748d0331b9102e968f096b13b013df04b29b6190f0f7b40bff3d3c49464da2b58a08465a1212a2e5e2a71e3339df128b97a6c9e39b984fe99dc07c99ba4c6623894d08b23fb85d2a353f6d3a88e129f7ebdd8fddcdb64d0821ea8824cc5281841ea53e0cbbe90656c590c15d0c46a4ae95569cfbf2fe941fc643d3de9ec2e16ae16730d00952723d58e38f8a7bfd89ebc2a4823052382f14c7c35489de99c6af43d33943582ee42062de97837d9b29ed59dc94241e5dcaf4a3487a52f8e4d4818ae789602cd1c4f2293214d930a65812c9aaece05dcf0cf6c83cd58c7818cbad9557e7fadda702b478452e46228b1170b50f59bcb82c00315b7067b0d81838203606ffbb18fd521c810afdfc9f3a3286829523c3f7b46bcfb5433a5e89b4d1708356f2fc8379ea665711ba53f1aeeb535ef354f7bdbff1be5bb5e42abd9ceabf1716392e918c4570f7d6f1844fb45af1111726b0c212a324e20d54148bb6eeb7af1c4801bb4dda9f8659ab2dd2d7ebe93160266107768d07429b89a37a08a31a885b166bea5d1772343bcee9b3bfaae2e73eecab3136c5f71c24539871b20319e704be25e4609ea3b00a068695e90e0df07934f1bccc86743da77a94676dc0e4a7793f75045487ef867f43d3b9d6d37b8b28b85616e429a362f8bfefcb7e2c21e00d820ba2b9e3b8a38f073376f84037c3bb8febcaa61e1c858968f346556d5f6b76cdced7708acca684dd22182a43133750a392c4b71c03898a88e342d75bd9c552e61e4bde119318a5a1ba97ddc6f67c0deefc6d5a7016048eef0a7977b96ab848b1261b636f3952a68516d02b866f0fde7557f23ad3b0218d82bcea9ec0a017ab9bfd88f8244686363483c8f28eef4273709dfb5c623d079ed57c54132cb19004d667552fb6ee3def4189a28901039759eda55d576578a6cbb029603e076c48feb2ff6eeac1760004aad54947ee9ecdecf550d6fa291e1f6d9d523e89d791984bb120b83d4965cf574938be4000d5f15aea8cbedc058a6b4c1404e5832e0324f972f77876f64f60190ff189face8c86b5084ff38f1f2ea51c62ea9064d2975912da6834d93f101ffa810d1208bc05b173aceb35a0d7fe37f339a1dd0bcf3fd95c61d273979de8d0b2957b2ea464b27be290f643bffcd2fd91e4505000000000000000000000000f97f387f73cf3c0316be5000a1d688321576d114db135229ecc513fd5f040dbd911b5b2ca9979ff6c30fccb113f10663b911f78382911cb2427a58ddbcf4d62b8c4225aa3a3b9a128e073048f355877c6ac57a377242f0cd64bcb4a0ab6c12280000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023c1c121d1e5882b2d03302d45c84ce52d27ecfbf331014a7cdead9355b1473938a0b5e314a7eab19227f2e8cff1a2a5479243b6d4bd0de8bde094e1403679115afd042cac388d71c100095cfc74e33f887b304aacf7f60e80beaed94e23a828ad15997ff3e7150810b0498be76f524c6b8593752c0a00f2991bc4b68b1a740b0302cb7c61f56b86b63e6b2adbf2f617476115adbe713c1ebe1132d09333b604e8021a49e3586f92c71da70c581f3918a2ef01b95a6ed40b4021a904ac280edbdd9a0b06bba89ee11f0c73f3ea607696fc46ac2dabccc23fe720a5cfb510db739494cd477400a3497d7650118de9225d4bf8dfdf7336614dcdb16092706e66b5b71da103259b631cd56e298a7fac4864da73d76c8826fbb09c050f3888d776aaf86176160319e8aa7e085b4c7f1560a7aa5c4c0ed73aa6aae08cb675140c6b494d72eef3de0300c48c8d66f2cb4a2e6c12218e8b99cb43307cfab69449f166068f32a4076ada032ab8083f4f25a1712dbf906e8d33d33527200cfe1fee90c61e8150ee26c37ec203100c55386c8fac531e9c8241ffb9af49b0e6a93624cea0242da6cc49c9dd0914157c571004f153b7b7e9d0bb630ee2c8c3710be2d0a35886c01e553c036e69922bd181d0a814c4d50f9e7c99f91b63bfaee523853b282c755f6f78687e60973e23b823e439025bb26a05adc1b445010559c1d176fd4fa1e03ec1bd19b1c41cf286918961d1f231f1058ad9d3b79a7790d6bb0265d7e51280a68ba8c36ce98b2659638b10567941fbc3aa3e34af081ed071712f86ee7224c4c83c41d4cd985d3151d7aa847041d343968d28b7fd8bd2cf278544a70957bf89a47855e84e3bdb30fdb3aa5b4da67dd1fb6bf1c4d2abc3e65b585b3538123429d38712fbe5cb0a8802f0793edb24544b3c923d408743abb2113704705b177d0cfca4a653b04f56e87cfd7ff6c454bd379303a297657cad9641b0d0e9fb51da67ee76cf6e473cac75d8f2b6678be09b6147d5b7cc550708d1d82115c9f8b0d25f612abc8dc007a02578ad220c490af64e4a0b945ed84507c39e0f9f01cd0420d4e5c00ec92d2d7b9247170412bda3465e5d61e74c04aa25376f8a164f5309cfe3c27ede8a780ad8abeec31d2057e2a258cea754fc0a3352c0ce3f1ae28d0f3fd634cd3897cb4f97a75d6647d7d11395bd0c463bea2d3961d5c6732c78ff4786ebb0178e595d8a78cb4e8a1c48c7468cb258354ba90a14b2c82ca5f130976e7a7650364cbe54c789665d16334637ac03d6e2f7a9c2c606ee8d6e5469f7ace775eed53b904a50201db33d932430fb140d8ef9e767f61f4a11cb775db8fbbe1fb7fbd81b4cf1819741dce9ebcfb31977e7f1774df5ba8e1842add5a9c61e3a03749f1c0ffaf03c4c8d5c23b71eb584ba81a57176dcacbdda1c9f6837d99aa8569092c7e683991971a3fbd1a168c8309621d26068a77ed88f8fee61670b2eb076e7d38ecf2fc73d80591a9446d4651651d247a9a3c6de2b281000e2ca92d87e07ba74d7a6255369cacb87308318ffacbc20d8a0f080cbdf7cfc2f598a9b6c33c3f6d944eb3068dc07ffcd29ad4ead8302b6cde0ea9cbf2c6926422c0aa48a1e542d97ed17288cba1282a08fe9f016763754988f968b5250124a38cd58c668b79307ed991255f5319516e79baa77dfcb361d3187ad86d7ebb6057047387cee9322c9287399c8b745f9f36bb1a010ef11c4966899f63a5935d0a3da1ff61ea0efc817ce276997282237ed768e0695e4fb7fe6c4915ab376fda365b278cac9a9e1d1c854d7616926ac931bb8e6042b10b3c4b072b053d4219fce7e5e2bc1933264212eeb7b4a2ab8fb8e8672c21f43d249ed164176e8f2ca2ae47b42946e74ccd9d8ed206bddef567305baf525b4d811c84b8d2afdca042d6c0ce9eb856f2048e4f470b761215a7a2b59d7afe4a2be15155cef7619df22da8be898889025d632425f7774bb81a384f03373279d1cdc2e7d2a815e5a1584f6f993a5be01d6f0f4f5848ad0c6a77ef15772ea8f8471da5003f91759bd8dd131e8ca9f06335d2440fbb5c8ecb99f67ae8cc9a512d60a99cea9cfd4e0850b9be4db40daf7e8847e5c00b3ab001b9ad4a022aac6c55864acac669b7a45a658fbe21fc73a198192179cb3d423ee75be126ee875808a25faa3860631bfa5578c7d67599f9337d4efb5d0deca9c7575b8a039e42d598dee1568faaceb3db62158ca6a7fd01479eb498252baafb82f68dc213f50ae611587f50fa3acb33c1c85942aad5e19cb52e83a64f2964871704b25bd4c326d248f416b93078c6ed352bf484f1085dca22ace064ca414c04d03c758bfa72054dcb75bd682d521f725b928adf0def913575d57e20f90c341201e98623866a3fd039475c52139a55cfd8381759977099e54ec1307", "63ac53", 1, 390593626, 1537743641, "0bb17d5650a3c35aaa2e163ce508980879c681d70cfd41964002da4f4e881cd9"], + ["030000807082c40304b3ca7695683f07e6b5030f750d39e07cc12e9f63fb9a5e9d307a4d64cad430240000000002526affffffff66f882eed6fe190588a6355c12e227812b7a53f494f8e8f658cca7f12cb282450100000007515351ac655253ffffffff892d22220edf1e4a067a91d2088106ba0e1b058fb48d4142b6d4ba08a3d287ee0100000005536a0053005c951c097d09cd2a12e723c82d8250594b990b0aef27012c0fc84f994bf26b8c8183c3780100000000ba7a73db02502d3b050000000005525165ac009e654b050000000001ac00000000d802cca303000000000000000002e19503000000001ff3103206e72a5926d1bbbde6f8c8ae89360244bdd7834dfd07381f74956d9602496d93d462b09bca017a1d836d74a8ca4c9e37bf71e03dfbe857194015ee4a946cb4e040a4a6a1ce13b0920475fcdc8ffd522a4dd5a51e0d648b65487c484a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6426671fbdc0cc7e7574024ccf2a0190304af8f0a0f07f28d51e2c76a05943f3085af78f9410bcdb0a6bf7a2a4d685fc59fbe705873c699824ff055b477fce367d4d1911bbcd753c60f2a69ac015ed5c63d371b8fa9a185b376df7e1c8e231870cc05ac244825516e7a2a152e006e7ebb0e8f1d701b8d43c1a5dc45fe487a5f020392c7547c08687acdba70dcfc345af383607727c94425abcb181ac08c41dcb903175c44ed7faa01508a5537ae2989eefdc328e98edb73c29675a1f3341888d7610b02ccd5db79d2cf505187ff1d2121ed619e267b887eca1ab2f98f90054b16453ca6fefc166990f0e48e7b6f877647bd2f4aa2fa573d310051a33476313d07d898020bc6a88b010a263fc954262e88f3cc4f339d14ad0b9eabae62e6142f139c426402053b0809a4089cb9cbbb0b25180406d0ac66ddf708988d36323d48dcdcad64d80214b0d8f9526c4f5cc720953857b4386793d4be6b6999ed19592ef2cf2d1014fd022bd9d1666d9869958c587c210d7425dd408c7b2f1e01fed06926dcb136bf35df032441cb61303184bc29909b87670114b59584bd9326991a6217c0b4fcd50039fe7df799942b05677b334280512134b8793f5a7b06029502f605866a05272da4140d82e35d8cda0f15318f8411bc03e9149db623ce3f3de24d57ea09368f78c04d032bd4e97b7ee48c5dac74d4b520513c5f4846cb972eaba50b347b045781cd260e3d8cab6641cbd97f36e77c07c624c6edc35d3ca7dda0ee644d74129a34e8ee6af8dd0952b523f16f22129a9b23e399a5f5abd10ec5fecaabf9cbc43b4f6bd9e4fb58b5be9f83b712c2c0b164376ec6b621d83e151c2f8aad1e41a838e7f0c300c003f04aec6f997a44fe25c7b09d40162e1801f52a1e39c39eb4398a1afd9e0408ef1043a637b4e93dfbb62cf04c3ecedc3ffae87cdfd0a51f4b9ff08580e083648b6bf8e5b5f87a83d3d42a2d182e761c840693009b69f365fe618838a8244c30bf879a48b70d74d562981b41732454875134165a42b1c9717416c0728b3a4023912b49e689ea15c99192e26a6c9a8190b58ce6b65da16956780685552a6e4de4377ea651dc2b15d8a94534b8a4ff104db85e8b9918c38dfe4bca2ee5d0fb27de2de393b6dd41b2c98419e260fe86a24ee645a6cae331e9c361ad15e650b06b77ede7ccb1bae3b26ae483cb9b73acbb7b5a8542d3fbbc49c85936ff655abc5aff1e571df1f4463feb066aad406d18cb4b8f3027418cd1f66d0ff86a8414bb1478ad0a932a43e6a79f2e626d049275b7f048503f831ac9c91dfc929304cbb0c42836e0d5f6b601cb03a16d2523e089d0c3bbf6c79564acf7e7c3879296f6a31ae14c7ac4ccb1f6dd4448587479c25c5e519df1ac0a7283502da032ec657920518e15c5ca74359d730c21acd05380325978ecf9be4763c0755e917a994f37206c321c7b5e7fd26dc5a2a299b338cc21cae30dbdf2f6fb63ec4c77bdc13eee091609d6ef1bd5452a56d219607b4b6cc791523124008a49f485f4f010ebe6d0eed8df7f64afac38e562bf4a43e56497e8d94875a4b75c85e0a17570f9b82fafd24e84800b9b1be41f9641b9de445a24be4f3d115c9f7ef951b89e055af9966b60b584b03415c8bd498ee2b8b6c0efca3f00d1dca764a5f4abd707cadcf088eac6890f00a72480038834164acc3311157b4dd593e17c9d3f899183d33fe3bf4693f41b31f2cbbebdce2446eb83aad31ee6d1425ef681f21b1d69b96bae61b2965d049996efe5dbda9f803053265a948f8d6619b802ef4609b9738af3153e641c55f095d209afc445f201daaf6c21d5623fd88b00800ef87e96ac60bacf8a98c08d25452e2b88d35e2e3fb8259ff72eccd1a26854f3df264f61261da3cb190888c8e2e29bf198bfed65c799167ff8f6f34a888cc62376a33b22641d93d0bfed5a91a219e47e22ddc1e77526f3d2d1db179436b60e85449619072220fc3a428ac4e6c5e63f371fe75f52eda574382adcb48a2d1443300dc77680e680ac74d497c86ba44f0ffb8a83de8b7a72a0aa785c45de8b14460ddb218bb1db40fd2fd59b62eaa3c27f767132aece33afffe00c4e80721530bdc3db7f3441eb11388e7a43c004e91ddf5898cfefdc3375afd943790aae86c6b9de7e16baf15772760a80ebc97ed71c7837333da10ef9d2a336f79ad2838ed1b9a3479ae0de214a3ea3ea05e43245589e3d947fd31c072a8c21e01bd2cf7dd0ac84151a9d48a2d1039ffcd90659f348424cf94ab50f902e933d4985e73cb9e800000000000000006a39fc01000000002dd906d212b4afc4a86773d5a7f08a37042bbea43c11cb6916bfe703692183a17341268dca74cd1254fb5b8fea7832dd26d68885a25f48cac04474782d7058fd637b12d963e80f7ca137a6c5a78d14d620dc583dc02e9e5d8a59aa05eb3f4498000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009f5bfccb93a65bd348cef41740781f9f7c88593c3f9b252a4604446af913c8c12af99ba238b902cc764cf8d8bba40403f3260325646c900714503cfc668e99b059e454f8bd3421b29a23af7f3c57e205bff6417fb967d53ab154e637e8c2343ed856ca3e6e88f446b20b525597baa25cbbf50fffa00d620ae243a6de7716e5b502138c6e33ca5b093aa2c967d9e8092c827f80273f9a6d7b1dc612577fe17b238003171b8a842b5fdca1fce0a2cfdfed09d54f47945896f03db8c0df9e67bc5a925d0a0798f710a1b581dd884f3c66cd221f513d1d9688ae87d2310d7774301b55a292b89581a9a8e0411f20eb115ef4cf4e40e2c46d615cf49deb32c36b2c53fd431e0322c69441c78de594f1aaf4ce3c5ae20e863702867b8bd0da0ba33cb4fc4fac610206d175541dba7edf730db99cb6b1e9097e429d8748b2de53e89efb6d1e3f140d031e0392c076c5bb7508322028e8e2000e10ae0b855fb730fdf87e53168b2a85a5020a35cc2fbdac21df5f6485d4148666e7d1cdf74885e7d0a0af8c8d229e580ec703155ace6a9e0a36bb0e456c971c169c073d3dfaf8833a04aaba57507af64a25d519ee1cb72e4c0148088569b6cb91abf22d5beea3df961c7e5b792dede5201d25b0e01eaf1ddbfc8c5a6094b5e056d8bafed906a33369497fbdfcc4687c15ad9498b203f742fbe86edd07804b353f7c665076367e4416c8d7f2f91a77ef524925ce59cca151198b20adbd08e99a94eedaa617ec3cd39b1e6463a16fe078405b535b06034cc97f2c4a36f144d9e268249a9cf41028ccfc8a9e71ab207104923ea5411bcb51cb143ff3626da14a56fa053072cefd5fbd23962f205f13b353b0a2a1e02618b588ac0ee388df86d923b8a0cb405a3b6042952d0c92e43bb121a5b1e2d23a0feee97167ca5684e3d1066100308eaae217dbeb96be49f274543f3a4e80c11cdef64997c4554826266fbac3a241f007621733c004496251e670406261ad8ee2950bcc9471901c09dbf4212a0ca2196a66eec83cf348d168ca1853f6096d50d31f02b3fc17b5d86a81585bee5e5b6e5eef9b3d98e6c3914b0f4b7b4217d0d9539075dbaa12c69e70141a64b49650aa139ad73e85cb9074ab09cb5a72b4487e707cecc7362a956ca867664ef7937d2fa51f0dba204da6a40cf697e46cd7c93bc096e1f771ec188f75cccf81d8954bb9ae281245cab7a5055edfe258acc103f11c166fd2fe4d2e64022b7960a3c946475a6463b9b2f0a9d7eb64e6b0ff49366ca6f57d3d6a15bf1c86ae10598bd3ed680fce4a4ae025e3f0c3d8ebb69b7bbae3e7dc2fad2c60ba33db74200f162a3a59aec77b0d999ae5957a1c6e9e2ca812b7463040d9966ab6f1c673bcfd71c0536c32705cc6483543a3cc8bc84cc7fcdbbfbbcf17f0f8dc79634be7735609b1f4b2804c5444af57f46132f2ec1a2986aa2d8e440ae909941f883f9f6d97dae0db0fbed1c19d953d427f7c85a91c6e28d4f6a6bc65be4ae14cbe066df66aaf22a399b2823986532850ee1c5f90ff6144c79ce9476fef5a90e05ff57f2b24d94d5ac0bce3ebeed15295053ce0b2506f59e9bfea5e3020bde8a0c45c252e7676b5e28761d8c1a2037f24250f729727d261afdcc3531f49d3958d22af19dffcf76ecad9eef9edb0b3918622e8f0a28651bbb7fc493c0d932f7161df7ea9cb800ac7daa78f10cf046e1184ae39b4d6fab545d34b4950b5a73c52bf2317846c2252d755a36ad3c1eb7c4f31cdd40e0cc5600fa67c72467009bc8445250a49892e73f364f1fa3fe40470548b3930db788495ae615288ff5680ed7aae5e71f1a5528134ad026ce44b6d4e40d56c02fe56c83a7e5c5cc679b9d7561e19fead06dd16cc0ea5c780a58cb5ce0677a86f4fb8312f9a78160cb9d287bf55994dbd8a889ac10708345adf1e6219815a17ad5eac1681322ba8df92af323567212830a5e673170c8ff832880cb896b2867f11f5a32c56899c01f603fd1093b784f2f1234005fb71b41a09ea1530db0477d5fe1b6f2f6279b36e23543a4ceacf369ab8fa92381c66c379bd9d030dc9bd5255b6f1482fca2030b4d2b2131e66775014d4ad49625ab9e6cbe71f1df06e863361176faa9d4f438b224739537a97c949f61a609ed180855becb7e1595947e57d6b1cb259cc1ad2e06df00ab615217938d00a54f2ee34d0590f81f2cb823eacccc43fb20d5587ede7bf55f18be4ee9026d480028150a7c3aaccc333bcb8209832b719d939d907f95f00d6ac0e19865e0342cd25420de94e86ddc90000000000000000724378050000000022c6639c9e02435b2b88c5036d31f6fe61b3403be737bc7b98373e84d31bda38126f20b76986ab6f36a7a92de172f61e50e16ff7efaa87c252f5837c46ed7e8355d5422a21c6aefa826903059c8564e3ef4f65180ed49b85b0a699c4eeefd3d400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f156242ef060d01d40d397348d4cd7229ae0287f670aa38a648e264bacdf39b1eae11aa51e918bf061827b042763edf6394f9d6b862db8bafd98cc31011def0a40e7e0243d53a796022e8882698d6f3c28035551c844a5fe2cabf96c090d36de89395a3ef9357376014602915a7a52cd0288d05f0803741aad63fd84fb9b6b8d0217e8ba28b053210c6b586d015eb495fa9ad6ffd6f4adfc599b10b3e28547e89a02234368a2330d46c4df89ebdf340ca2c94e2bf9201375b17092e2c343cd53d2240b013f71edd8b502136c04de4b1106a6badd4ba561a8914d4ecc5091e4ed6aa9f0843d162c02ccd3cd57d0daf276e487439b433e51955fd9b0590366f09aaacfdd02294c980b55379fd5abec3b3615fd9b552643c16ae28c23a61017f77791526061031baa25eb7aa9b551e4be4adcdd16083920d2abd9a86226260a494a4e1dd4041d031ddea1888c08f31003bcd9aa174cfc0ba20a4a8abbd7b5d86b69070ee4d132b20300e44d7a99f2e5fa6f3989cac39cb365548f9a1ed653c9a525c7b29ee0623774022efe3960e8e791df35e46a2542c76a3f196e2154061151fb4dd19f759759275076be619aa304a89feb93c7bbf0004e1bd387820edfdd779f51ad399e039c63daef654e57db196a105d730b6028a264e1a7bff2c91d00e13fd57c94daaed94251acd0a169b4ade5ff89dd58bc2609d17d94be82d3299165a85cc23c82d5e5bcd285eff0bf3f5cf47a84c804b87df7fccf1dca280d48c5f29964bc6c7c3ed7a0c75adc545bf99af39f10eb68c1486e910a54e2a10a704ac0021d5925e6b8127bdd444e02dfacb77cd2ea24e1a8e8fd8ac4d845af1f3f88c1cefe8e800fcf0d5da7f07da3d241c36668b07aecdce4c6c6b3532f2216124fc6531251a656fcc8b7bd5f1edb93722e9ecc85c3c7fbcebdfda0f013e2a541863c173116e5cf70d81cd5463e448d41167f859bbfe1cbfa592fbff260e364f9180d48b4aaf02d77ed13447c89c89edc8432a63bf1b78877fac5ce4bcfc9db674bddefe3cb3a88f5ff3113bd4ad8f57f3002d406ec4d96512d747a3d9cda127b338aa4354ef7ab4090ce37b074208138d81f70f96acb3702132daa016eef96526bc01ef4d22349275e2b41bc6a4826aa3e4778aa5cf11291dbf8dc593514dd6a016d9c7571e43ef9faeee1eff6c5a516a003e8e72cf55320d93f784335735bfd5d13c2d23e440714169c684f86a1d40628301d40ffe7fd0936384c2bd23d80b59b880dcda3639ff29694ef480fc8559d8af735177f41eafa0836b92bd122a49363f45632c9d2dd8c809f263cb8eefadeb8a99c69fe22912b70205dd341092b81a17083dfab257d42963655172de0ca380e79aa5c1acc2ae9ab0eb4eb126efbd31da6fd01f4a0520ccadda09ff7802057870b0642d037a2888f60d98c2aa5cbd9d758f15ba2f96e72ad3e417edf8ad3f675ef06adaf2db90297c74ff7b33625dd3d3896b3ab8aebc77626fcef2a41874211596a795c21295bb8af2d2860f1145f27133edc86a1352ef53a0aa1ec0a9743a19ce9ea5a16540bb98ef9f466d19dfa00b07f17fd01664030f633103c294684c15681563f2c50cdd035df838aee80709740c1e81fe32846a50885aa88945c738b5886aefb1dbd6021cdcac6f6bcbdd8e3ece8286a9971e7f4f115f5288f086247e408470191c01d4aa14ab2d7658ff2651132572b06c19c7460bc5379db2b144ccae40d8d610e3aa32dbc30359d46465426e007b7214663ef8311e6fce64f38284e357b250c3253b72e6460879b836c01b6308ad88c0b28fc9517403f3520765a7ae202a388f07f69e7eb926fbb6c217069a04d1a8a80230de0efc8f39678e2a6bc578ebc40fc4fe17b1804757abccd75ba2c24668c5e7dc4379c6209e3d35cd3a171fbca87e50270857346ddc236cd65b50a2db0b2d458c2524e67521f97ed215f5efd8d41f5834ff709466d8b7a05e8be3f5e5a2c0c94a6e957b887e0cceda8e4b4ec1bd94a5c9fd806e6a1dde5c9e2db822b4a03cb1da8a080f23dd55ac72f1e76e414e7c4171797187ddd252640c430723a2654b2bfe1e82d529283fac038bb26c8002f089fc1ef52e6299f385023b6fd066a4645caafc9e46cf343619c434115d20759ad5885357a550279d72a191ca49a993c8f390ee1f7b74bb9f47ecd9532ebaba1320f6da12ffd1d2cbe5823f8e4db2d76cf5cea220e555758d75d49e2d73a73664f2cb359ef9c9731515224fbccbaed10f91e4ea57249482a0e09a99479a1f6e83887e525d6c9d0cd7bc9bf6a4e1e0aa4772196366b98c81f6affa2442935076a7a44def4706173c0ebc75d51fee0202c860afb25e51d7aa291fe8e374b19df9d4b41a3a62b03e079593746c79779cee8f86665c87c5c30c736a77a57daeeb02e641bfcb165f001", "6a5152", 1, -46403995, 0, "45fcd405fc2c872663e0b5c7d2ac5b5ef472d6fafb309f36650a01b7f565019e"], + ["e53ab34a03456d762d7225bcb529167e2dc8f76c4968146907364e7e7a8930abf9198257820200000003636a639c6d45139c4538811e4a40e46eeed756e59539c3fa3b662f135398026334c4d47d91d63b0000000008655353525365516ac9fb6986fa59a157096c988afa052624fcff585a2dcd3980635d9846b1e5797a791b17380300000007ac00ac5265ac6a48fce04802f0a9d605000000000452535363203b03040000000009000052ac65650000650000000000", "535351535152ac0000", 1, 1344936635, 1537743641, "58c4bf4148b33f1516604c625dedaff54eebb087e236bb8d3de05244a68d8119"], + ["030000807082c403037ccfedf0dd7f3fd5b346443ccde2d27fd51760f98f2e7f7b74e6bea7314dae620000000000ffffffff2e0a532e9870d210aac6026d34a340f70f2e59c4e663d1e5482f54b01519216f000000000042a9bee852e4b9650540c812d5ce3ec6678432c313f1f14991afcfd293bc1c9d81c0fca60200000009516363006363525253ffffffff014335fd030000000008656a63516a655251000000000000000003c75d26030000000000000000000000001179becba7097ff91cc2966df45b52b3bb73810c459fbc96a6f63741a3b92113549b9d685c6845656803897243afe9921e4e56fd795a6477d2c1338fd10f3d5b872ba20978770edff1d7adb26055b3d8e38e6cebab69bd8011f43180e2de17fa0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017cab231ddfef76bc1320cb9122abb7ee14aaa968b507503a2b07dbd5e04ce6895017530d8e8ede9aa8eb58939f21189678013f8713d7844eb8fb7c329565f00306bf0f9e3da3bcb484a040db7517e4fd96db322d06ac0956883f2faa532b87a4cacf5a759b5a8b8cbe5812b864adb23342895788b0c7115fe8552b280ceea81020713c5254c453343a147052457e44cdb2ee0cc9c5aaf9cf2f43bd6f7cb8ab7c30226d70c43a5479b899e580b59b34092782aeb6fa8976abbba93de4d998975c1a10a016403f3981ee50595b695cc8005383a26363baedd8dc06e7cf2eea8898e2d0c7588674f070b690cb89f3e21afe3eeb5f0113cb736767c28cf85f10ce9425c16031dc240021d72e54abaef8acb6a15cefd1539fdb281cd04c7d9a822e5baca0989032d2d3e74c1061de728d1794ee1e7c18dbdf3142edbbad18d0b15269a1a57df1c031479749b669785889aad0e01c1eb987cacd1cb8a346a7545b363f143d84ee84e021bc1b659e70e2799cd16399013ca7eb952ee422d431c104ee9dda258b39fc9180210bb13c449339bc95f72f6c18a527c97190b4aad38381774f73d425b9e469d7793cedc862ee15b7e67281d6ac9fdd8a7d25a0f21ff4e7e703ecf0d31bbfd0827c5efcddffe049f5c2d7c8049819687805448540214008eccf59ab653de30e9388712ba2f42f747b4791067b499a460424fe4c61518212313c642cd6c5e401b24e545a71d66fc14f058f0f2b4ad165cb7dd3c5209e42dba34a4a4e09e1be20b5cbd29be4b47e7759275c637e08b072bf3c58996e14770f515b3cbee3cab9677a7e1c7d233ff757203161bcb8b0a27a7628925bebfe6abb19514a56900908d32ebf966d74780409bda50342ed08bb9cc2a0dcbb53bcda1991a276d9ae472a7fa0b319efd7781d7b869d6f51fb996c42519ae6904411971189e2bc92f3436a7d915822f0bf85032442dc9fe96bc229d8017ba90419084c662cec99777a032bf097236126a28b6a77dbc629277eeba814946c6767c98d3080b02b3181efc3337ac38b9839393566720ca2ebd32a7f252f73f6301af65cdc19001922c6fc0192b61c598f6b867320f3b1abb8f36dd73d1264c9e2b0eaeed94533c7b2e243ca8dffa30d8632cc9cce88f45644f2c8f17e2719d2ea0299177857599fe57bcb0ef963ea1c0c6c2f4e138cf0a1bfab75d41d4c98b4411bf25de34013384880e1d19401fd64fb41b229c14fbb15125b0548dc611307e4794924b29f995aff765da09ab3dc43f0346a95eeaa4ca7f219fc48877c85c69f53739109daf2cb0c584ea1f8973bad2c1a73ee5b54dbe92495974b6a1514af367ffe1815d65894262912e090ce092e9a6bbe5b4e3356dd4298f04c01c99ca759b0d0755efa2bf9bd9a01c360f04b6b54863e7eefef3bddda6baad1cd9d28c097fe282c750e46c50602226cbc7c3d21a459d824ac4a8f0ce07af09abe03bd6f21da0e02a84626300d2da13792523209855e0248ad47d9f80c198c3bc74b26510c0b138f9fc3ae81212356a962b23d45c31aa064aea0986699ae038250500077d930e67917e480d2038be5d0da19fb57698d5f28b144a24342f2990d2be26d970caa51fa7a7df7ee15681bd8e9fce7889762e8eed338b178ea739c4b731ed7a9fd43f61626e8cd63cd0a02a29ac3c930a0355efc7c02e0170068556593b44fbfc80f1ae8dde80b155c73e65e5d601ffc61f95f2960c9aa1e4201b7305f6f4f951382ee4f302e21da647f4d0a7f531682917e39e9bc4ac1b194504660bc3ecdb57a03b19bf8e58287540a01f1cc954291fd7231b5b8b071e4ca7dbf124d2a7f52a0f0e9266780f9e45be5c28fc2e9b09bb6001cf9d498a4b3e739663f8741e1c5363dcb947a60527daa290f169479132467fd52169bf172656aa2d0737a1df5c6a78924bd8a662c37610b7b3cf299136fa782e27ba81da9b5a10aec4112cd7e492d8056fe76195cd39237bc424f25ccb7de8cb9085838fd51e73386e2439e1560cbd1d818f84c23375da504b314638bca55e59ef04e516ae9dc23e406836d80a1b63edb30c991f3f54378e4105dd88a458db6c35a4b0372dd8ad1715799acf807f8339c0835c18d476749b2bb1eb94ad8559a97ea82e029a09d33695c5272ae699c1957986a43b2d2aff69ebab4488bfe7c8dd67012ff7ef4f825309c9fe267697041f192b86de3ab2b7d52bcf4c2aef72c91a4100bfa1f7a57b67059edc2c7be17808aea10e3eee4bb536e199b1abe8725a5cf25a788793fe02543ddf959ab7c7530000000000000000ec5c6904000000008dc9ce6a7c7209476dc3660584b50416826c96ed54a6322babd4ae2f34bd3f141601b1735d6329ed6dc2d27abe5d1b3c587a7759c92d8bb2eb3d52eec88774a48094d4b999e1b6020994464b3f0e9d00481f7fa356759d3fec1a082fba8fb3ab00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000155c701a4f301735f4278a43028c1037d15ea391fb2fa6978dafe8c87e0660a5863616f000987cbf4614c72da25575a560e4aaaef195fd1c35dfb9ada85f5231b1a35914cff8e515a591c994dce883096b0ae225db3f00080555a534dd3b257d042105258d29f40cf9c86ead7a6f7559f4550dc4adb65d7857bbafd1debf46bb020a2ac1a2671f2987cb9ca4de927d6a8b03ee51229aa81df4ecceac1862fc6309032ec79325abba864e1cc95ee2931a93c962f03debde9942dcc64f66a5527be4f70a04da76ae073484e5175372d71b75ed99489ea64ff7244c2957de33f5a948935bcc6dac197fd10ae2d1fb2473b4768b7f62bb922ac0465b7dabb56ab727b37ab5022ba2b6c5686e52bdf79d562cd9449abae8dcb5790941205cc11cc41f2feb815d021447f662ca024da127bb41a175a8f14551cf881c3f84fa90888a079d923066ab021e1460ed4e3c619bd6676a2bfa1de078416c0572bf0ec53283c5c0afa02710e6022ec46d5aeeaf96b3233874801515244fcac47837d531daa9a2e146e9a7075ee80220ed17f0c0b828839169668740fc0a11dd057ff5e1c5b98b384eee0a5cf385b2edba4e59b19ec88e1b2b627fe79a4d7e08127881008aea1d84baf8b8b024a0fe51cbc91fc8a22b50f393f46a55b032e94975f9c2adb00fa45403a6807dd60f76e4b20f5303dc19d7254340ee7f27df19b953663b6aba901862572a23893f639e0df3a2b5309c39ef4f3d74cf5a6e43681214f1e2daf3b7cc99aad453b5a9bc3e351f3e05939f0e5c1b818f5d1522a1b4995c6e973243d545b83f046c8a98306200780ab55f2e5a096436cc1bac86bb247cdafba83f5c2cf715cab3eb6c75d827e015225bc9e3516e59b1f474591e29dd8f363b2a5110b61d61ac5fc8d0d8d0e0aa303ed6405bd7e87d649f58622fa2661ff8a12266d38d7ffb4ae86a6bc8c88fa19be09d82eb532008d9fbfe71e4ab6b4d13dee47c9cd66c1c530bb2ebcba0dbb5955cec08945a8b2cf2dd2ef6cdca563fad1f479b9c2ecfb0323bf8eba211302c96af8cc2b9132e08e45a36fafa0463cb75430303b2a12a046165af395c2bbce26e377867c8a8910c823ae085b166f1bcaa5054b9792b510e7fda995ca18739bdb6e2b273653abb1abf1114c9526f400287482fee8350865b6fdac1cce54d2da5fd2019af6d9734568ede1d06927083e1282f90c2abaa3d3047bd1cbec583da15ac40ae6552be6fd8ffee7062a95fe39913d62e1459979fbc41a253930950629e0598417dd8ae47cecccd4feae4cf4035c97656f6e52c9003d3ba8d612056e13cad0a87673683889601d4f5f8c90a1282d13b69252f48402ce0fa21cc16271c1f5a7de6a4cf3098de11a68b85a7327be93ba8591f8f02feb2bd8be9bdd3b3afe26798f57abf5d8521d9fa4d79b67d6f74c1af28d00ef28efa0d8cb39e0f6eb8e774d1a1b28284130e6c244f65946accfe60edf19b057ac4f0588465c2f3bd9eee4706a59bbfaba62a1eec0bb033a4b2099409fb5763c6d3b4e8038334413bed4453dd27f8cb59b603a95621bd14b319f1405e22514f36a1a46310ac7b71d3c227e243634748248c657469be8745d920ea8d3a9e527a51ac94c718216af02686360dbd5254f5b4f309069f3723e4b264dfabb548ecc14b865846f180950e09aae7e5c332965d3ccba65d7a060e8e1e782242a7554d0a443bedc746647cdbdc0cd4113dfa4d9d07da310e117097f4366eedb48694a7d128004c4cf643265c7625f6726c61aca65447577e5c261d6d04257d5edd42652dfcd1c642b69abdb16b338e69779b16f7872273ae2f4005807529ae819cc8486f68ebd05e8116ddbc385f5917c28cec34d2b024950b08189a290f8afecc33c9724c1bc845f6077252aa0d6208b5657a1b2a2e4f54aa41401aca67e126de307214d27837eac7d0e8db33279d7b8049b50e877d66462cc1022c1c65ab9e8efb700eabddbc2ff2c2d9429137972f4258195db4f1f01db4eeb871be4cdbb2f0333975f8c2172db3e5026904516dd65d1f4262493e35cd0a775a853cfbae085fa81b1445908753ea94a62b9e4d86ebe19edde9c82d9f82a2bb93d1e70ab88a1622ddd3dde66fc4306bae557e6c5a5356890ff823ea5d5c2a22b497daf941d66fe21a79b26a4d660708a0c66c6a1dcdcec3c6b1a8d17e408e53bbcf84a1b77ed8a6f021e074924fcb69ad4af7654cb77d2aa6b26f096e11c74c867bfc9638509c413f02c3d02288e9a5b5b2999bb539db5651f4265c6d7b994bdbafb7d34b910000000000000000392cb204000000006526d522dcc14e8e725178939efd6e65684a71f144b63c2dde1b12cfe8716dcccaea06d18939bdc123e822aa9a56f905f3011b7154f123e41a9271644b914574f78597045b2d6f5c521f8000abc2c153d68dddc8501cb684ef372951c16e778f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6253d2fa882bdd23477f3040107ad029d0dab5f3009e9591876239bc83191e1cf72ddcd27826b3dc438b3478ce60d05e7551fdaec8e290d02d87f0611e2d5811f7deb7cf58e5e2981710af2d8fc8cf58dfa13ff1821783e2c0345900dc58c550978476209bdc3022cd3e45943d27c2af28d613efd4a6700653bb82f8023242c0314e9b6ce2940b732c1b0e6ae83d4db5207a14cb4d743e7ed10d19c92f909c175022ea3331ce17cbe8a7ffbf702c9672752fefbe24248b9cd44588e4997e713f9920a072fbc818fd3b8b290a8de7c65a23f9979023f3d7cb6f478540d54c9f709112d853f2d876a04a0931bb54d8c94b7a6d5367434c05ef0057ace5a77e39f3b63a80223e63ef35e8b81dc3cc1915d9ebae03bd16d84a559aaa91a33f30e370414ae2e031ecb084192c5128d527ac39831d895d5f336d07c34e1f0ba5243b94aa2cdea52020283a545daeeba9ac0acf612008604bd986183c2b04ebe87501354220a118b23032631d377d0e5bf229e654ebc8bca099d7209f9b87c98ab5895486f25144fd6a60303480e9f3cf71d9c02566e6f1634793ae6a1ff615089845192356d202a0b5bfa85b16550c54b42bcc56606e53e72bfbc763eea536120132402a649375c46869ec5cb849810afffcea3a457a5227b3082e9b4dbc6c933e89dede4c8833cff188f9f9690417670361a93172801f0289a11be6eab3b9454e1f7e73a290c0c111caaece8dd47e95dfb81a4699a895e737af0f72e466c1f4481c48e4ccf3d2e28ad454ee77193fa9ccfd3fd2260d00113316974af607f01072c6428e1255c1a8ded3a51e40eccbce7b28e10de8202cfa5c32fd4875df2e9bb56fe8ca39d4cf3a4e0ab64cda4a1c6b604256f38ed300e978f0e4889819b606b8925e3975a6278046d3720f93ee6e5762ea44ede50ead00931e64d5cc9922245cb082b827821406fe4f9cbad2c7fb1f2f30ea69907ff7748b990dee5ed0b23b8b40b0055c9122da1ee3d1aed1493d7ded08115a31dc4469b0ad1731547e4a92cc071535e07855f5f0fcad1430efde31f64aa7d30cb2e16846f6633ffdfc7266157bdaab56ef198f2138b4234c6cdc855646a4933b51a16f17555598b1122184ff59f3c190dcd2fcb870c314b632dd766ac40526037907bd1fb720b5b96bb1e6c91cd7543d22246f529cbdef72708ee80633111d858cacf83c2133f60602f290e9c3dd5530c3e4ab116e9281dbe98370af9b65f2af1b14011707c823179f6990b3a4f0b5a755d86c9d78c5ec8ddb31f7489872b4f234968fcc514d97d4cf4ec6cd917495d7869ce9fab98bcc6a831af66b1354869e83663a0bdfcb69a1a4a054cb2e7d86fd042ff54e33e53c806617f74bef4f201c01fdc2fd29afcf35cb7c7171fac742f41e3285403a59171e091f2d28f95b76bb90ccc1681c3cbe982e7ae90f5a3034cac082a59073927288efccff18be379b7fc8a711897e5b317f3883e80214075c2ebbcf755fd359a90a4e667ef03a7aa58a1533d3ac95444b747ef376cac42434940634c135d6eb390b54b5dfb0c70ebefd06396c43279aa775a412b974c236b56fcea7809be372f3c817b7587eb0e2c9e5f2af4e51548169916e418d24882904b2d3a71b12ad30eff89b2404269a3076693e3f8889357bbd7877b5619dd7d1027e9d4bbd9f643431d7a419322ed179f786a38af1afd755a220e4cd197fe9e91ca716f9061414c4cdef91620b8e48e7ddfeaefd3abbe27abefe41fe17e0464d7d35b9f219cd0ccab70fc26e4e60df95b8d2d16b09d4d7bbac812092b39343fc89c8707105631f12a6f48ef731d61062895329567a973a07606cc42b7b8ca4ef4038a33c3f43468734f7ed55980183b36cc9e8c4dec09e3ab32da497b661c7c2d6ea9cc860adb4e5c5b41cdb6dd11835766211e7d5fc06643c5a64668ca088bc4ca55eb25f87160ee8bc246d7593931a9e81d961de915a4cb5fcd7de1562fc7df26597a3b6028c101e522b190d6fa5af055f81d17900e634c7e268808fcc8ec7700fe6f87f6abdc99da0f193839e8a48b06f57940e314e5d8945d48122c188d446e61602e161843225a9ebf662af7a27357a8cc4b836d6cfddb35444b57f4aa739f07ac9e949e42f61b0d0d29ef20cfa2f9c5931b29cbd00d7ee3e3aa55fb8e6d8a979a1c87961bacdb83d7fde24a40e89f7f5a4aad8f5aeaa43a599718c5cd2047d0d96f1a781f9906d59fe2fc7363fc5607f89cc795c33cdf50da0dbbbad6e19346c1898825e86265b5d9c6e68f628c139932fa0f7fcab9e820e9ee297faa8d2eba204caa512d0f2d18e322e5273526bc8beed249dc539d4b70a0b9b72e5154848c5609025faa63c1529e13fd5bb6cdd6bfc227cc077bd3a762f9632dfaa4c12e6a856faeaff5027336b0be9669d9558d0479105362c03e0c", "00006351ac005363", 2, -823594623, 1537743641, "b846743c2666e949ae83e46706e87fa287de0f7d6ece56c2ebdf3e516e35ed70"], + ["030000807082c4030195db4efcac8258532c868926590224f6b8defbeee6a2ef60ebf751e4aa8789c902000000036563acf4b3754c042342750200000000006354820000000000036aac63e82d1f03000000000400ac5363dd7340050000000006ac51536351ac5e21c4dbce73ef300300000000000000008e25b90300000000166640231cb668153ff6e4285ca4fd045c1cc5edfb694a83a650e6dd734163a4fa163fdcf171daa0a9dc0af3ee4877ca20aa2de1563c799e318ff740847dca83b096f1d95f1b6274dfe1f0abf26d5cee063b220edce05c6f747a99a54612665f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000119cf472dac6ef911052dc7234e0e3b5aeeda4289aa458a61fde596caf7cfb644ec87ae2cd7434c511b23dc789dde6ff5b410a7ce58caee0e8bab5c5ff574a69da0e8570cab5dce749882a507528919807a1a62282b753500fadf7c7271e856421b66a8d2727c7c17389a4c9c41731deb7f005ebb1079d68b73e7562afc8a99603243baf8873ec44dee694bb21e6b9dc092b7e22ed077e483484bf4f7aae10d2c202265c309bc36e7f6fa462dc3dd11dec025f1c97ef445468b62edcf8f1d25e7f700a0205bd857b28e9fb8873ea053b9353da37991b2871d9b7adf798076a98e60f75639fbbad5ca9e4a9b843bd5776ad19d05ff32119d2dae63f5eff5181651b3ec50212db8301ef0673c652b44f92ccc299191f828fd8c64c4e6874db5feebc13bde303231dfd6aa6990791f7eeb8e0c5daa0160639d27945345ff1dec2b6f49a2bbe5f0323053576887dbc2e78e0fe472f9f8797a114920a644567546ab29f05e683153c031c49d8bbea3e436af5cbcaab3e864ad78568166e0384419aebcd6a0e3ac537540328ac9e23031cf729172bc104eb410fab665a6f36cb89c038c2ac9f42d47da9d5bef55a2461caec56027393c4f292c903c8dc902732e10253175fead36cc74a20e172a936c203955eff13028ebcbf55e78ce7122f6fa07f03c303983e576bd99dcc91aab5411219bb50d7547dd30e00b28d74dc51e9b6f3dcac70d01a80e36f5c231c84cfe45a121baa048163ada69c03cdead62e3d58cc3b571fb20c84e293e7bf2b0060b691eb4800752623ddcd4bdd54ace47b0a8727b458d4eab42bd61078fba9538e80338a0a33c189be13f3be47fbeb1d5e6cc7b56e4137e93d041b317a022fe8215eddfba11e2014f134490b5a1437b92c91d2f605890e356b5c3b50c4cdf5a43aacbf833fed008302cf5a025c3774c963244261edbc60d7c46c806a248904e218e3be00165596c71c29d73f47b84038cb9ec47cbbf8c7b30a5d45062ef15905b07d4535d0726bab4717c4036551d286960455650cbcfdded70d406c3d77007273e75f8dcb5961ba941885da851c911be95b3b1b34fb247bcb7ef5349c924bbf2e1e16eca7ec198ecfbbe7373bd71167b4b5c4010a2414cecd46e36c228af47b587e793b4a9e32749ff94cc36995d5eb4a34e2e64177ac7e39bbeb9ec9a5f4c2bf9572dd476996bc8a9fc050349881bbaf1dd1c16c8093f88fc30e2abe8e24a95302b69cfe2ef246b2e0029ff71f240611b6bce0ee91385bc29acbbce7d3b6556e6c200f97cd95ff87a6ee6c6c70f5191f9f4156d4559f7f32128d233eefb0e21088eb4a7b3ce1761e05af58287ad67e1753fe7f2222d77c040cb8167bf5abfc58e1d4f6858f9653a0193dcec4b30553cebc636fc90757e4d5d8853456bb494fcb7308aa0a535aaa2e2bf5ed8ee6cb380f2fb3211f979fd25f5ab62225f793219d3c0cccb0d72b821a66bb289fddd2028f2bfbb461966c2064cb1050116b46396cbca668b21288c5c3c8e02c3fa8b5455ebedcb884367e8a759f0cffbae4022fe7a5ea32f486fd94f5098b849952afcfbd0b72d86e0cc3add5a344bfc39f7ec0a551f9d2af8f4a3108fa6d6d00746a62b4dbf23930f376098f381dbb739d059033d34ee714ef6940cf8c2ad17052d80d37083c950d59d9480e59debb734d2314112bbcac64b6c88281ec82d7b706abf72dec4c64bc443566b19ecc65814d9153d10fb8e22d14b7b2c2d39d96ffc54a836c9d85d5783ea2a2d4bd8f9bf641f70905170533a06f865db065b4f565786d80b8410497c1e671e5325e7c6272cc1bc50656196b375f458669755d3b4221b49dbf943545f33566deb998691a9b3551cce845fb34c6a6a422c4b17fba273c9b5d0a79cf941dcf45928194d987d03cf64dd7f7c4e6ba79ad77329ff0cc89bf7cd29cd42b666b4106bb4de28b4a235f4fbc6f249f2a95b17f156accfe76f601e5dd72d63d288fb546f8cf9f65b6307c21e48b010ff70e3003e7ef6226fd3bb357c60c72bca1a03593f2828f5827af74cd656db01347b939217f58ebf4c2045470a1e7d07efcb0924996c359b7086262a33dabd43f7728fd1dffeb6ae2d504c91c4ba56cec1e7cb32b83df8d9a6d90619978456a4961af12c82648aee127542b5b22511906e9eeaee7dd38220ed31a9135569acf7fe8cca949c09bb0ef5cd1408e1a4010d73a959ed2ae757df531d17df6fe7c829be6cf63a236f2217f38e6d95d7be759538dcd4cf2a0e1cdeb996784fd6c0771cc2d08b596c09f20ab646d06009acd110300000000000000000000000080df0912779f830a655773c3db093073b6380cb045db9ada941399873dcc1a938011bf997bfff4e08600dd347bee60df377ed55fa8813a17fb57de0bfd1b021fb866a69e19213da85f1956d6c2e094bf4bcfc0faca5a94f5e8c774f3c7e39d5c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ed63398ed48dd2de12869ecbcd6533cadabeba66c06dc71ea4d0771b8242206ddf70d6ef9b3b44fdefe814a83f37578d2a92fe9c2b508ee77ded68ad132245d3b08665280be5577354fbf91cfe6201aa38c8f196924947ff79b97de273498bab6b0e65117f1f44636c5616baefdfc459176ef7f6c2a55547e304e450f4c71172032acc87e99612eba02f713f7d951a739d90b08168ce08c0153b4e78375da675a40326d990fb635a2eaad2ec9c1429df7c84d443909949cb9188699b86b92ecc65930a030d378bcd8daf871679211bff39cfc7d671f43809ca61b91ab08799d24b582a107f8ddc0b90753b2e23336be502c662579aab46f34df3b05f4933cab2439a490213002109fa84afaa4a5445cbaee9e05e093efff3c748968eee9bdd8c87b88725031d7653bc14ae09ee29c63c451cd23c6bd9583d487df5ad5c250d747515ea78b30312c736fe5319227c25533f45434d0206050ec18a2275939ebc06e518f7c8888102293ba4060ce7104273195cfcbdad0c32f08e9dccfc70f111440df0de9afaa3bc02170d708c7a9e0a96513ea38bcc8942acc7903523245c75d6f3db413cd6d8d35fe09cb47820f39e2c78f9dec91c18fc00a7195c81e8459fcca97efd7b4d51fe95236e0132410f7004ad5a865d27f21834839c5ef2d37704826ed3d7c14347b122be8df44a1898e763c693a8ea171a884229a22b0639fe0752f6faa553c2264ae2f63a914264ed6fda13404de0dd6b871ac9fc44411d90a536c01e04189ef44967c243b2954017faa3ad6a003d87c41475463977631eb9886996976302d2273e61d273414c9b0aa3aab1b176ad8e59bf2d1373deafba6c50ff3ba7043d8fcf9aedee395ce0c6c1e3b43467027de86605f140d692ce8b500d00edf9548c505d91593ebed199054977f2dc047490e07b4af56fe1fcf0e16c3abe56fd858902318380b61e63a6cc144270c8e0383c87d1c9e847ba4c8b00e169d25c18c6721b976bf5ea905521700b3fe454e4660908d4069dad07e1af1ba3ef3e74d0fc567bf3c808d96c4f0b8325b13f9b7a9faf2aff5c108f2755a6dd5bff28a9298f0098a17f824f536127cb8879ebc9ac7197c1767b24d5a4e10134664538d0f9d5da89cc895e4518f0f84526e33ed098b310f9a9d28383c1c7ec9b804a42bafae5163ca4e426debb10e7044933bf95680318bf5f87d77ddeb9b8b4a34455d738023e5e326b112b29b0b96e7684e21ffb7ee7941e276dfafcfdcfc5ea515cb1e814ca86bcda12f7d1b42b7285ef82ca761329e8d6ca3bd838fcfd01ec7be18a54ba98550e3a901235892791f85d5e146362e7b18c341d43374a035be283c670127d8e42b87713efbf0870a4030101aa8757c0fe03732e7a38c7125c7ad5d1ff780a946dec27119d4c7355714e8b493a21eb58d2155b5036e8e4de879caeff434219fb32cb69e6d64bb3534bd5a290de4f0ab5fc73231e56166725d44d301dbc61bbb066d708ae1075af74b3cd7f36a3f38b8a9a33cb5f487b6d8466ef60d9473924e4d280b0662ab0835684f2ede5e3be3bb7abd2b7c284db3b09d68e8d484b503c59705a0c5d2b7983dbcd51138016c5ad53b971c54f5b7092de4edcb483bbdf5dc974240c2474d8cb12e75fcbf3a85e237add37ec9230b5a56223581e41e6c4ff6c09d0d2b285edaafccb9bc28c76fbb3247872a86f60de2116af2c19291d851181f793971297f0a2217c8e8f93414c043fa85e34d6ebea6ba9d9e0c3f2682dd855643fff55ade89a2f58d43ecec06119c1ae5de0c1c8672c1f3db7d49a42a172bae9626f3cd1e07259cc92fd895d63951fa55aac6395fb5217192876ccabf84ce5d430c509fbcfd7308d653623a7e41ced0af262c5dd42cca2376d9e966503ae64191f09d26583fffefcb460a83e2fcfd384c2ad39a53bf0f37f6e95e00c97cc76f853251fa323972ffc4434cebdd2318407e65607470bd2d63040af5a6ab62a9ff5904a2523e9afe266f97ed7a7020cbf9560988882162a8c5ce5e1cb92c1bf286ff86142388845a5713697f6c86e97390ad53c57d684210468cdaa5fde842470f437f0ebf09c0436c460398d7ad17e330994562601d968ba41bbf413a49eabe43fb343694a5ccb05a999c39e529e4b6896d112e640eea0bb5673cd2ea95fcfca0c96b9621b50d4b8dbc334d815c4aac39434033f4d412c8e41ca1b70088c371c7185a1dec9d8500e1ac37d3d648a675b52b60bb73885117d109df53e943898d86b1c16f0426fb7de4f08c01de52ed0ba2ae6aeba19f900000000000000003887130400000000db48be28a5e70fe46067576c3ec777f377062ce036687ff8c5a43b7588b1a3d84ac9e294ad7a64aef1c7d9a0ea6ce0548de93f88651fa1233cb3894c0503155eb4d466a04134e653491c6c06dcd1cdbe8a433192525376986900b9c99179f81d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000057db0e45503feb2d75c2c6c4cdf2e5759a5e32bb54f74fbdc0b98753394b4d5f49a6c3bfa0b53b0f2130d2f7c729383998ae4b90d1360a6cb66a77e4b7d614fd589fe99f79c9f2e5ebc4c222b069f3cb32be799998ebfa2f24753a16a869b679b9f253b71f25cc57a5e18c27c7e37586966dc0c54f7dee1075e66235a901eec032533cd5249e4f511621ed41e4cefc7b91ec9315449cc238c48c5756418b804a0032e445b643ed65614eda0a0822e1aa3fa453dc4531209ef5294e89974c845d1130b00a054eabb9a4079c0cbb56aca0061a92bd2f918b968893fbdea52263d5f638594b0e7e7de851ac7f13e412560593b1bcd737eb0b4053112959ad40ccdfcfd460223d5f2069f8b54b75cf73c6a162aabebaa0b3e02696bd4bdb751a96eec375ec00203eeb71f2d54cb4b73fd0056545adcaec904e1cb570adf31e7b3a20f1679edd4031d431b7a5ac11b8b53b74c50695459a7c7b0af17211c3a7f23c1a547f78169af03130009ca1c3d86b846496d00de3a688442038330b5ac30a19a9e6697c58e8c1c021ab8f83366cc4ba576afbdbdcfd339e669086e58e6d92b7d438a3514a876c1811e2ca7694f6fc07c0319ec9370aed1c926111a969bf1243efccf60c6a3c5a1f4ca5095f389c50054b56e096799926da771eefba8e56757a11335436dffa8261225340cac12e6082a1be2bf1ba6df0109ed77423e803e9d73a79e94eb3bec49e0583efbe46cee12103bbd7c01fbe21b601f02e5844c42f9bb7459c15f00b1e3ea9b7b612753e45657889029c852184a302f842039289f6e2b273bd14e45cd5547d3d342b5db3ce05555706484652f9bcebe3e26f7e6e0792701fb057b123fee49cefd384b09c2be68dd9b3d93b267479b544d6910bf02ed09199632bd5bb4035d60e8cbd3f9e8698f06dce380af0fa4dc3869d8a8c0bbcecde4cd6b6df2b49dceb147dd4209fcbf2719a6aa29256ac966d91f1e37f92b1647ff44aecc022a951c2363f26cb326c0c38b1b7ad78945908caa708571285cca908e57b5c4d3b640f479e0376a29032f55e6564235bc68040ee28f450a6545b9d1969a86ffc40bf615137e31fc9a8f9fde9c1224b24f044f404811fd521b7b7f7a6a886a205139ea03c46864b45583504e9e749c6e2d09a2749068e957bf8ec92417789a6bef16b98a571587d3ea442d28f3ee098eb39de46d21fd572acb30c69374bd272cab66279698f8ff69e6e7dbb3e268a995c34eead10f2ec3e72706a94c9f6d9ebff485c26370c16d16a31c8939f415dda9c8f7b25fabdf9f4a38fec9509b3960fa55276dc374b4a238e0f41e3c8965a002d5d28edb5abd99473d6b44019a2c2f4f6300708e7c2af9f72bddfc259e0f30bac6b50267eb0e4b82529ba2dbfeae90937040b71c65c6a835c5ea0e5b0cd0b2a522109f53036fcc4df00da151bde631c1cd44c4072b5d139c5523ab00361d9aab093956936668cb3032ad36cbf1531c1b6e20211d75709dc0058747a04e1cb1a9563dcc325df6f32b2a9f56999dda86548e8f7f4ccf5f22ed8b4372d6d8a553aef91700a580f34238094905dd853c235a3c5b9d92a9bf569f80b9ea5441b625845eb0da39a44b8283f7343e47dbee056ab318115c423d0a4e8a39c643653a0ca4a423339a6eaccb683c667e709be6d70761ec22ffadb525f92ebb121affc837d5bc004b83c3e820d032e3b7925e58c52c7fdd1d9ec87b9202c96d81d9810e5a1452335a70cb7d2bf10c8dcd14f9888b6b5439e4d97b16d9f93d7b19424bc732f7327cff05b55e46364a33628199a513f6c84f9134ffebd0bec1edfa173589cbdd5176c1bc775f31314f2e860efb853f0aede7f0d91f96ec5a34a22feac15a1be3f877d4e63dfcf951e2e7119438b38acb1b06036526c8774de7729bf66d44b206c4c2e6b84c6748b2ac742651fb793f67502eb95c6336a0d893563ee679727e4646b2c3d1ea269b3e877b5b53f1a3edee391f1cbb943cab0c94ea1c8bbac0cd26d3b2dd2257beb97a2206c664c20446ca91ba796cf3df31af52c096a6bc2f996fd040f149cbca1451018d6eb99ffd5c3d79a83fe77a47b296c57835212b1eecf3febd6cb06dddaa267fdfe74dce74a0b61597c4b55c6c18466810b23a7d26b44037a219ef758369213d71d20bd8af7848b6a290acc5c9d9aff05680ecff4e749e7afd4fe1638d61433549cefa7e45e09f454e8b6a2f3f46c56c927527ca712bcbc28ee61ff0f151e3084c97d1ebe3e03fc03f4818e62b141c7807ef5c48bf78f2eaebad844fbae688c48460bb50bfb9f8dfaef921bc1f981941bded180b2e9e1374bb5f28a7ee8c2f01fdd083cca9a254f8ee70c5bb3906154175aae78d21920a2c749ce1d53185798c2ad7f66da65db44d14f9b2e6a47d6c590d8b4bd0e1893af06dba4e770a", "52ac006352", 0, -416817208, 0, "8348e6ff714f148d8ee444a2f59a20c340e9f55efcf0f53bb557dd074aec32d8"], + ["017d3a0f02950dc516904ef21d59d27c6b0fadb0aa89db6917e6950294fd4086ee75a602c3020000000151f12201b41706808cd82eafa4fb0f1cc60309560b337e496f279fb0bbdf6f257732f5c834000000000263acffffffff0200eed60400000000036a6365610834040000000007005353ac51acac0000000000", "6563", 0, -1523851633, 0, "10043308829674390bee165d00a39f331f9d59576026fa5c6be719a4467c2248"], + ["b2e7365d021ea84c408fd30967cbf7563fe9210b5f51734c6b44c77b4314396977d219c9890300000008acac536a635200ac67043709b63660bb059929cec649c2f65f2c6fb096bf56dae7a7a3646d906794f6c90f6b010000000952656300525263636395e172fc0418ac4a03000000000253537b824c020000000000f8de950100000000055200ac51ac850c4c050000000003ac63002c2b7be700", "63516a", 0, 1871467588, 1537743641, "e527abb1d4e309b8521af0d8d9845e9844336f47f370e5a08172a27300006ce0"], + ["36c3a33903e80a2dcda5c81da86c6c212207ef5d1025a3df94f7286a9b9d10ba6ff50921de0100000003ac65004fe8288e6dcb9257e1cd2a9e71f6f40bdf3a7856128ff151bdd7c308461f8d63f9811904000000000352ac65ffffffffb0ca6f0a196faa9c8be2268876e410840ad45de2b7404ec4e434a3e2e14ad9c50200000002ac53ffffffff01720c760000000000076aac63516563650000000000", "", 0, 1507027457, 1537743641, "003461b1e66d65040039b7dbe9115e889cac5704a12e19e8daa013ec231b7d29"], + ["99000f1501cbcbada130c15afefec025f50a98ffb7801d9aae48f51cce7649e8ce84b6d2f4030000000365ac65ffffffff04905d80050000000000311f1c030000000009ac63525265ac53ac632278a80400000000056a63630000255b74010000000000d77a9f7b010000000000000000384db901000000002dfacb286219c724f70e1cc6db1daaaed15be68be6108fec3995a39cfcb904d678ff11113c4981590d959ed682a39fc5b8b57a2d8f9f940cc590c0ad984fababed4949d7ae2b44086059aeb069a1009edb9ba2d3e1f052f7f3febebcd3bda33300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d5abe50f59197c4f11262e3975d22af4819ccf2cee1ccee80d5ced1a23d69d1b9ab6dbbf3f427323ec785b4cea28f8fb16a7d2ae66308854126ab32d054601517a2a2f2606410891cdd994c084364785a6a618390955cf9927ebc7fc50143864e0234905581a60cdf52eda2476f88f19aba22ef8f25a8bcbb98b665991c9b3890215d78ca218b90d5339ce7944208f1670f4f873b17dfb11eb6add4a03956e39590321636802d824e0dfd072911593f651adce9f50286a66cd943b44a8cebab42ace0a0156f7de650e10a83508a7f22d5f65fc15013fbaee558c6e28708d935193f78170d03803ee9b0ff250b1c369fbc31f66134b01c779c0570e5bc146daaba4b00c02018a8d3acbb25e73e8c1e9aa5bbdf400972001ba25aba7657d471c57b89144320207713975a3d75253df9295dfd2b0b4144f80b431c2c7de80360ee719428b140e032a9e23d4eb5c3743d1f8d4587e6f5a1157f788a5b97eb986e71583582287489d03000d6992abdd8011799e86a6e71a2c23fe3af8b4646f23cf20942a4e835a486f0305668f797e63a6a52b21f402741c2bed1fc3d6ae6de9c93c06a8d4d10f771b46f0ff9f6fd164b620c2f6f7fc9261bf3bcb77672043ec960745c50c0bed888d24c7c4326e008f2405097a500d8263f78347a081086b95e6c4b91da48c8a24a74daf0f1c981961f3b94eae735121d3add17d389a456c6b2cc3369f5c4cd0aacb7bb0b377f7491147924c08211b30ff36488cbea84f308f30dbe1e60ee663439396a9ab1edbb2513b9e1b8051b46d7a76edc0186aee39710d05a40fdddd38e47103a95f941e79d6b3ae7458c8b4b4c621a74701ece0e386eb9cae67cbc40c8b2988fcedf987acc3142b1cee83d4bb24e9dc3020f9a5da8a9ff8783d1fa5d869ce8a3f3b9a23e08d33581e6119107078662c3a9943e6fc5e1973a6f5b86c75c2259a175e6e4d8394cc911f1e15e75984ad6e29c71d8a2ed5bb00d19fad93c1820aad67fec07451d250be470f651c4b57d680a250e70e3a5bfba28572c0e73a16be07941919af17601475c1b5e433f1c4c45cbb009aafbe317ba40d36c74715f972e104c20708e9f408289948fe20e4cfd9f654246cf4ff567a74c7ea0c71665b45ca1d6f8bad6129efe6678e6010f74171ea1483718f8ccb95ef51f65dea1f24ae1e7bcb9762e056a6e63e4735c7f9db5f0eca8539bd5077b9b4c75dc3bf93df91c19e33583fb0d4e170ebcd632cb803de1ae0ba62e73045f6f8c85150797a81fdfcce40be382bc09269f6f790de4a4557dc6e10f59f1e3e2283e7d0303b5e39e6719653f21fab4646d87f837f17d51fed54d5ac4c655b5ed365a34529c8e050a931863fee9f5615066f52f5d66d839150e020f427d7636df71ec120d17b2fb3122f510dc58c793bd68d1ae73f86abd8ae22070337602fcf46a2f06d3ffdb53ee355a00edcb6318b376d89739361274e1d89fc1f75a994ae5f5cc7a6d7dfc297e2ac098346f1c9b866dc3ccfc3d08309563792029264b7fb06139c9860d9a4e125952a717e7a56e428e8176685bad5aa458a2374cd3e5956c4b39975dcd1120842dbb3cc7ef2811d269902ebb527880a3fdd94a9defa17997647baf2366091ceaeb2d2b143dcbfd474b3fbdcbbfab7b0b170b2c9a197e0cecb9fe0cb9be071f20e798ff38b8f2527a7c62a1ebc889110d4d7096deec30518f9c33466730f7344a5495868e330b34957d8be81e402d44823b2fdea724139c91c03e6f8ea578fc1d720c99ef750e1faadd7f684dcf8e6644ba417a0e7c75724547922ac7f0e6fb60d4864ab1bfca8da169ec0d73d38a073f592bf4c232beb18ae56437408e95d867b6b6b25a5fd7a0d5dd634860655c4f14453f67b2de61976c66c45d7514f7735ec7a73cb2815efb18e858d9d78af0462ff8f2a2674da1b90830a191322845a4d9924936cb58f81964f3616642354a864d2760e8c91d2d75cf787ae59f7f34f1eb6846cffd0218ad79f003d6ec2ecbb7164615e2d7c52f5a6ad71ab8d9da47bd10ff239675ed4017efdbd73fb648269086096ff4133cf9c70d1cce006b6e60ca3c68192721ebdc3b6976d6c72774783b5e0931193f061aff5a38c3e74be63f0cf78cf496fd15feee4fc3e99e41ea2b1f30c1b8be7d14c037be2fd771b5700cc894c14ea6cff27da196bf22373ce350e46d512ec394821450f7f3151fe3b4d2f07585a0c97d28ccc69a96d0c9a1bd869817a17eb106d5fd0a8e715e2402a14b3d9720cce9c51ee4cb7c92bd3ac3beb51785eb79bfbf20d0347e16e841c51f79dbe0e18eecdbb94a8997acda8e1e5c06d53667e69ad3f885759a18dc44ba1f3aba817744535b516edb7c3787fcaf28fdcd1dd45574884375b649f24f264c2e522f77ea45d62ca6ef81b546290c923ff99162a9d8a0b", "ac6565005163", 0, -690841074, 0, "c31c8e70ae41f95054bd21fcbe4380a0385f765da437efae96e0fa3324ca3448"], + ["030000807082c40302b12611fa3f6441f2b8e2e55f6754507fae7b71a3dd331ff4de9eaf2b2b14c1b00100000000815fc6bf16a38358edea41b729ee3d56cb1f20cb34c0b7f3478976f2c783af33b9c96abd0100000008515252ac5351ac6a623a880c01e0677b0000000000076a0051ac51525200000000b0674de500", "5152", 0, -1069958644, 1537743641, "ef5904e0429421046449e37d92f7e43dc7793430c1dcb32a1630ec1c3c8348e7"], + ["82b9603c049ffa02dea415bee2f6c695c5a6138e31dcadf4c26fa48015d9f13e5445536e2e00000000026353b52fd7676d63fa5807757de64e03f8448ccde1189ee6743b66cebf5f626aab5d33567a43020000000965ac52656365535353695350973895a3be4bffd61daa4e9ea22ef3ef12029992b368c936211c5a55504963f82b01000000025351ffffffff21e0dc5908ec89a0cd93c845e9772ee0c13ac9167c9507f93fc3630ae50d4eb0000000000952ac5363ac53515165e5eb72a30381e7be000000000006005351525153d7df4201000000000700655163516a65ca17a30500000000016300000000030000000000000000d124970400000000f44cc7f723e1faec63b85c7b218406e4bd6240a959da7ffd38c085728159ed63fd475073bd569da6bba2a42016454f775d4fc19ed5b633c416f62635a1abf318a351c7c05dccff6fb2a604409b0308b9c61c3b40f2934ab4dfba09a0df49297900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f61458ecfcf8312f7934082d9acc48dd2b2b95d69d43b3e38cc836972e438afa116f2f5c19eea0644df892e8586a81fa12f3150b7cdef7d8d939fa9b118ec57f8cd0cf078388551c24d49ae193408848b17f2882b0a68bde6936880eaa9225728e3ab6e3df6010f0b15ca06e38e5f40230cd0168464894d6d93f3d5f4ba1c7af0212b69edcd7c34dba6956a0bb5e0f751ff4a126a26b65607b3f4a2c62d5cb1b81020d3c85b098246962bfb5324d43d9733d1c2ab2f7989ac78f7c40d6a53e2b38000a0199f043123e9bfd7550c854e2a09aa4a58bbe7122de210558e15d62812c5a09b9e852fb95cb272337da2a4548c04f1c3434e37a59af12993f531be323b41a5302253b09e3c7937c47165064c475ccf449980a52992718497061a066e2779d43c003015cde4fe0776873d90d7f0db3cc95daced053c604b9e8b8d135efd9a39833cb0311f01729ab1c8b3146bb14c5492f798e0183fb77ebbaaae9298e55f2ea91eb450228a3dd5c88e0746d8db750bf8e3d34f5f7c7dd8cbfa07dbc89bf8284897d3f340301f052a8e087b840b0e4981e076193d11cda7a54f3e8a455010393fe3ce42c6910ab3a48969b0bf76c7256396f6498375b1d9f2704ce1d911bcb15b71bf53998cc3f8c81825002372958b539019420872ba23285a4a6c6251bfd8dfdca41bff6e9fb0377373eedd2a7bc24fffa9260206ce9ee60a600ff0e87e4021843e086a6c10e70426ce22e3097b2cd921c498b6102dd13604541498355f504f911dcf35d16e0cce3be265842128bbf12927e94e375e5266d3f90281b1705decc52d3002e9415d8009790ffc7ee187530823bf020bed1a27d0e656001d5e259f8d005d082c15c4d097df2b76352f149cf6c88eb2c9e41491803865186f780bf14393ae804507970362ed6bc27de13e6d85c0b11e146f5e1af7b36e6eb37c756195df8464efba1feca8e889eacaa89dac7fe360c691b0aefaefed59d15365ad2eccfd48ba251631e01f39587524cb9a59b669481512e76198e372c34d0b6c9bfb5fb78be83cf14349db7de554775a26dcedf5f2b3694fad61159d61674627acf948113f14c5b900a2017d6d7f1317818d780e405c5fbab525a3a923dfd9d0f084675f7ca2ba3e66aeb87139900f97b79f0402a5548f96bfcfb249a24f4e33930189a8ff4c7c49178ce512d3523894ac7a7df2421a9fd46b37ceae47adce98c93c8e7988a0f78e9694851c2df1a95e25e70446ee7e74f0b2c7f4bd1aa144db90784cd3547b6437c88296439693ce514b2252397931d2773d18eb2cc70dce1591e2f589459e860dd9f72956927d421269e7d383be05e85a535499e5d6baf4ec6e7e4a9e7e08abe1adea6c9dff58bb23887cade29c3d11c950b349b917abb5e59c6dad7bdc9090451b7166063bfd3a7f4ad285c0151c7a204283f2fbd65e0b9452e74e5290c505576f16323c322691aad108cabe0c28cf38d074b5aa427f15b9838810a75e7950e0b95df7a426fe372d42cac0166e5860782c27ee523679e79980d2bb577075afc22b561f7f04f40e074d006c709b2fdc519135d93adabb331abdfd2912b21ac320b08f30abadd217976eaa8e60a3d6907c82bbeddb43ef56e8ed689ad432c428a894e96c0f1a4ab50e913960b3ec75d28ad9fbba6dec8895291435b98d94d80a4155155573b0d359fd0f0eeac82d661074689b9468c5ea00260a55f8384e0c04e1de441a991d7cab7a52c2e1029878de8f17ab00034de046cf2762875454f18a5c75b720b6615249312eb6d8014cae38f2a141646eb0fa7340b5a23173b11d7d3a2ea252e9dabd67cb175749e4eb483a4d6a42bbee74d9dd8ff75be2c2fdd7008019d4ec61a75e83a838cb1a487d388b901721c451034a2ce97c8cfe8565245a05548d614ee689d64cb32909a570199e7842f5c4e1c3d73bad89b0338bce8939bbf2833f58a23c297f5fca59afa500e767cb3ef050a18c441e3aa2418154f7097114c8245ecf13d645a67dfe5df26ad0edbf6293a02b6d000366a28f7d53aec14e12bdd674c9946cc51cbd12b55e4837c8d9751cc47339fcc3a97a081c6b084655d669b9703db6bd111f347b335eb53f9173e4d45bde26301306df82a9d9082640dc9ea14027c14bb6e6d57eb865f21cd2fc74959e4c0ad737cba2fb974975aed8d5a281477b62aefd330e45412013c45b03bf5140c296be934fb446e900963b806106d5875c8a89f9405ab37fb62a97336f826a0e7cfe2acef541e259919232faa734864bd2a1cfe34c7a88c61cd08e24a0000000000000000c1cd390000000000058f0c115f82ec83c6f7728b4a8d1f70dc7f58c680f1db57ead7e7ce3e9a15a69d2ecdb682ea2396030729fc590e1cd939d2b94515cc9e04891b95a7f21940ebd672228028555d9cd633c5287e03dca2de1ca29e156d94460702cb843787f23f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000685ed553242cb58caa77bbcccbd8eb61c18ddb55b5b30f2a56654f00fc7eef2b87e94e38b8ebf8052c149b2bb54a62021f319d3aba960dc45d0526f9eceb10fb413b5f0a4396f7ca497e0eb3d457d57874e5cc2d3bf0c778ad30316064281c1dc4758ba08522c097c82b7c616242afea1949c0dbc6d10ef7ed607ede632d01c302201972f8fa79161c47ea0f85b8e83e4672cda31bcf7d672a03f46e4fb1a3a3910325ffa41565385d16b22dec2ffdda0189ba9200ed9647ff52d51a27c98038a73f0b05bc00b386226892276b055a1d44ceeb642afe74271cc05cf1de617195422ff224129edd1680d018fb8749af95a8b703c3c92b12287c448440d88bbe8f0b2a78030557c441a2ecf0aaca224a2590ffa3dbf994ea5cf98180af4b02d12745b2ef86022951c805af428904bc39ceac98de705db5dfe0f37282e25f63251606f92063260318299356d54d813105698e419e10439b3bf35d1f10248b8876ebdfe752e36022022531b1ec9aa865a768ac7059a9493101ce15ccbcaa57c10fd0d43f89624f72ea021b9e19cfaa8f53022e45cb054e10e5ad4fa0b48295db52a506a2527adb40c5f7e5d997df5a99cdbdf1cb2440c685d81f7caa165526e209ac591ff2044d084692b8076439db36358cc200d408b9a705f3aab88a4233c29cbb04d6abfbc2411a345b6270886c6d4d2f72a752d541600fa16ef48b4161e461fe72dbb314542cac5299ca22331ab97eea0a2857aab6d466df08d2bda1b878ab94156a973fae7a0c253afdd72330e9d2b2348503fb19b61fbe6ef88da44f5651f10b8010358e2c39c9e44da7288ea1f4cb0bf8df74a4978edc5eaac059357e4b3892f745f9d212a5c7bcbc9a7f66b7a61221c5b381a9c49301654ade52244d0e7bd16c97095b1f5529668f428c4595ca57b08f554f1908f1ec0fd092ce950220030b08056c45e2275192919105b30bb4d670550840c735a747434c8f5bd3c236309195e2a548a0fd5681595ffda2c8da53a1a553ea5c2207e7857bcf12564fabc594e2ac44f0208baa51fb274d2c845f29d9af25a49da84b4c62a8b819628b89b44596b09ff3352f07cb4d22ed55cc1e9bfbd8cc74b5631428286bf3e346e1ba312e0fa5c892f992e4192e0d1658ce9f07436847f5ea8ba496056b0d5436a3d453cb7ba3d20f670c0d2cb70b7b3a0d1fae659ffc375807cb51c68a9959600dcce0cc3da49c36bbf23809e46d8698542a496316164596d8b8e32a9e2d47471548cd9fceed856840327d07d646972f5469a88414754a82f43e1e856d2459a4930209ce85e026294e2beaf18e2da4e42894df0d46f42d3587a25fba207b84e6df1c2bf894b128c6e4ff17142f9fe7e746dd2153fb199fa63b8d10d709b51edfa99073f2fe4af47d7a76022421e0d85fddd185c6edd54070f739377c4d5eb72eed23e7ae7fd305774cbb1f40bf465b8f51a729245abf20d809a47390fd0326c2217e389c072ab43647152928366834dcc71098c65e862e6db8f68b78a4750cbef10a53452118527dce3a38d3d339cb9a9ca0bbcae52fea76d0bc99ec0d174c8b8fbb083712ca31506aea46d51d0973bb87bb483dbb4d2471611640cacd2274d93653c86b8258950ea86559a5c9e5bad7242ce8ae40e1ab907ccf3fcc42d95aa5659235d067b7c9b5160ffaae7f63af2f39ffd82b3a349edc6858176e8e052778e5b0bd929cfe518df302a093caa9fb27853da9763dcb5a4a558c601b690bd0c82cb1964d738d3c2739e5e1e7f7728b6d6fbdf2a33b5f62800aaf29970c4dd17def5da1fa0b09c9edaff7039e9f601141624a135323092b3803cf9b90132aa5f996096928f1ed5af32e537d55dc4795c86ce5b13ea5a532ad1134a38d7a21a8a89427d565f133cac0623c6cc9d02a38a05655c42e60a0dada8c955cce7b3fe3025bde86a354d42b20dfc4c2a0655b8df79bcee8d1bc5b3c75899869224f80e4884891a08bc7da5681dec34a1811702d5dc83d08a1263e8a963ed20cdfd5b74b9e5093c0d39092310edfb8b4177ce3f377dae0f21a1c05def9c9fc9c9db69997a975e97c753cccc08308dbfc47b3ec4612a7cb906643b931a1f1fefc5b075748474cc3a877df94232ff25e0d076e77d082667ed614d7e29c3133ee742437a5b165fdc3234f79638dae2e602991f9371f00e46779af2d2d1f592c03244365d3263f1c6d78941a8b157b8d762c2519221e96c96716de1b7cc5a1f8727993ab0cc459a4491faa1aeb8af855c56db4ff0ff3735b9ee0f19b05bb0048fd12e14700000000000000000acd6704000000008bb3c16f983e8c33e58ccfee8953783bfb8e52f420031fdde77461611614be9117907371a1e06970057b0f5249acf9279b8f97c4fc2b9e92646d2efe580d74658de24d1394decbc3420c652573f3008815b8839f0b82a0302a47818446fc1026000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003c1b5526e1a4c8032017cf269e634480fec4eca6e973835d970a0fbb4f6bc9febdd31397ff34dca77546f7c85d31c4590f02da199803b797b9a42c8dbcfc3be669637b2b3bc87a3b2bbccefde4b24451f7e4be377fa6c6c024119b7545135c25eb356581f91de69031aa4baa916ce2488031e3232ed25d1499a2c9ed75c8cf46022a32b6d5b896a5df308cc92099d3749dfe86e94f5abda1e4c6c6386d271d0ab2030ffb2e6b6b19abcbc97241ad0aec7f14699c62dcba012e151768746a823b79ce0b067d569c0d50445cc32c5558c21ea07cd99b387ba79b98f1c86fea57d6efc3d81dc659d370e37fbe858304ab038ea2bebb2c6154ceb521a5547c3f4372da2f7e0225646e850efcb1c6142326e25733ec59b89b930a9ca35e57290fa2887c49743f0230387a319aed4ec1a52a4c45143ab43f4daf1f845dff99a01f24cf0d6c49d0ba0303b339ce8bf2c6bbbe83facfb115b3ac46a0878bc07f81792f7151e2fbf276af021fb5f77f688f9fe7357182bcf9b924c9178ab9230ee403c3133911f5d678d048021df2a56ecb0d846470cbddb9d42ba0cf2c14082056d4d0b66ee6f436a336f09f64226160188e0b5d19cbf06a241d9fc42ef1bba619685cd0f961bf86a28c27e66261b1fca52f0dbf2e3aebfb364a828b1ccdc81c54edc867c58ad52ed8b13b77446d80deb1737b6585fd4748adb007e4998945f6973e79896080653f86d9d4e5a30a08dfe7deffbef1db5be0a4a84d6c65d2f5c8e4c46ce162f86bbcaa2a3ba97b083e1ab65deb88f3c1e30bfe68adbcb076fd7d8c591dc6bb1e9765e47d7f65925797664ecaf91af8a56ff2a1f6baadf9d34ea24b93581b8ef45809fadb6907d16a604d7df39874ab688f807993608fc91f95dda927236b047fc7443eb5808eb7d2cc4b444c312f05eb150e9ee351000166b15d56948eeafa1f6d9de8387c936d4f18f6a33e80ecf780c9b25ae0af2c9a89bafd37e8c6b930d6381f25e3a80551cc3211d21d3bf816c2611a7c55901bfed51a2f764ac7551c8cbfe4973cd65e86fcf4bc79ad8fd7adda6e55f4790a770a7b5fc74740a07dd342ed7c412cc98fb64ba6de4c0f7e66d840a8b75d1b023676aa06bf1c1108a5e4e192cf80c230b08363d569a5291d4cf6b52b87d2981a6bf9b2755cf7c90e04984bedc262be1db11469c6ddc28470f0db97d0d01ff933a404388ea5043f3187cd54e74e84b57d51a5f60e1c35ecaa79d7b4e98a2123415caaaec4fe359686e04197aba2b00e4105acd54406a580b69ffbad95f7760f53f36f65c1e31487640fa84e679664a316296acb372152950678ef941249b6a2e26a0b1ff58791eeb6f005b113b741887fb58b3b9e92c783cb8f5da2d50a537572ee20460f7961471b62583ca35544e499a3cb430542a7d68540a0a282581b202f84145d65f0816242a2fcdba5b6f2814d38a5bc009ec46bdbcf1b6b0ab0f6c5bd26b19a93f6ad45c17dc19329893c82084d18a06dcda812b8f19db4b8b01688a5672830c23775570ed2306413f1fe3c0d3e2d844e7d3a96c366ce98eadc5801b282ccf72743924730a9700dfad5a60ec12d21978162dd8b155be32cd7cf1f0e57c82a97b5ee8a8151914d81f02d8af23b83136daa29b0eb904b6424637b35813368de54d0151cf8ae92afd74b7991a8652762bb4b2bd4332b74c30ac6fce1d5c0ac6f96576b863d95cc06165a21164c08fae622918f1ccbfa3d570f305e5eabee9a55a9b6ec4c360d3e11c4256e1d28747d1e7b0088fac7109f4d8305c53ba82d18f3d4800f4e6189c01097e335ffa3e0f12ed7d823392e5f2cd548fc14a466a6d42a02e1bae1193b3710e2f4fe1de3cddd37ed8e6e5d6df8078333467da3aad9784d72b79e81523273fecf6dddc74bc24b406ef1ed83d342000fb06f99a03c96906213a79079b74f1cda993474eede678579a00fb3b6a7d5417acaa9320f3bebabfed3d18aac5d1997c6a5fd9d729fe93cb46e66b0c2caf779fdb481775fbe57cd4a5bfa19610f8ae3fc6cbbe5e6f04b37275a5b04db0a98e2dd0c730f4232f13bf9ed7341012ce8cdecd6dc27fd272431f77bf7b3a0bd613352a91d48fd4857f38a9d74f807db8c17de2b2482daa92383633796deeed2243b644fec1702e81d9693d8da39b4dad72f6aa3857f367af258e9ee2e2dfea59cf1056c15fbe366bf3df3e6f98b6402ea82710ee7626c7901a2e1097c3053d12d53cf9a9077fefaa0fe12a3e0565f879cbcd58705ae8b27e3f94628f6ca999837e97d0b99d213ab196aa812373c4cf706e097fd2004a786fb77f4525df1fedf7674adf51e2954cf81053bf538206a93d4d160cdc2051c91095c15ecd867ab0ae440d6d26ff21e791fc72d96553a0f6fb0aef1f05ce8c550dd0466dc183e8fd5473070eba9e87dc2e1a5c204", "52006a5163650000", 0, -1166786770, 0, "4e6d139c53ea6154d27e844b5d8d314ca6f55700781198ebc165385608e401df"], + ["ad96cf35021496c73457409ce5cf45fb16fc170e5f69d5d75748702c8ccf75e8105daed78a0300000000ffffffff0065051793bbe836d47d9cf15eda20da736cca8ec54428bc19fb3f37742b5a9f0200000006ac53536a636affffffff02836b4a020000000005655351ac65369f2e020000000009006352ac655200acac0000000000", "51ac635352", 1, 404611999, 1537743641, "5c9a0a45434f121b30def013e1bc3bd1a9a5094781d773dc10879b9a4dbca3b1"], + ["575b4f7002283e8ad486c6e0415646cd13355c7db3137bbd15ec1f3538e8da80a4eeb43fa503000000075252656563006afffffffffe128d774e38ebac0c0f8bd7a2b2e01a81b631dd0e570f26a2f52a7ae748391e01000000086a00650053635352ffffffff04cffe99030000000007ac5163ac516552c9a6680100000000015363209404000000000800005200636a53acf61b9f030000000002acac6155765e02554105020000000000000000000000009047b5a3602fb7c0ae0982a6e1bc7a6fc8d7efd69a7d078950c321ab6a414b0c01ee8461f8e1033afd561005f41705f1f8df8d07c9fddd49e4015d44a824e66eb1c280f640015c50563ced044bb4d314bcc147e4387b22e8071ad769f72e3ed200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000566d6b5b2941cf28846b0a02499b50e9f9338f90e8d4d3fa5354a689800618df26d0100c403978db3846741e3992c31fc603f764407a0c1f6ea38c0643274814dc11ddfffe51ca12fb91d8d84ed2b6930ffe46ef89a8686a103ea55b9e21543b093f109d0c23bfc6c0a5eb8eeebf7c329a23b4bc9cb6a0cdb97e43c5c3a69375022001fdeab032d4d8d7e27df5ee04cbd7ba883098c5caeff22329417455b58aa2020b93382cd85a49ab5f2b3d1b808d9a65c1d0c378abd03d851efe6c07b5cbaddb0a0782b23e729d04e861360a46634295ae19c344a7e730a2d58bc2dd56fd93c33dc6c85adc4bb76efcdefc83f1e90b68f83ffaf7575461e59ea0d3c45838d1bf0a0306924c87e4414108b87eb6f223d62d54ebdf90c256ca39962f2121ccc07aec530216ee1b2f234161173eb7a241a5d340594fc24607da84a0b0ac6117f2f9bececd0223b6b3858454d50d30fbd8fb122afa71d79d7ad4cedc36bca28f365074b7a56e030a3609aba875aa610f41b9e1636a7caaac4582a3f45e18d4589f4d9473863d4a022f06693e5ff27eef7d316dedd3e471bfd8ed2bb68a7cfb380fa3ec17c25da053f85dc8153a06dba636bbe92725ae88c603eb4b6573fda1d4c7ff73f77b6a2b652ae86eb96aebe1ae57383fd569db9f1ab09e396d6818b1e0edfdf5fe9f5d69755653822ea813dcbe56805ec1a8369f37a15289825748c92592859a8fc54cbcc1b665e65ea6c1c62179636e46f3b33527f210ca9e7ead4b43d845563bab637c99b518104fc5e9c9355cd6343c47f7b0e51095f31275ff54cb9f6a644fb396340fbc8d3e01292d48090f4724982d89160421518334fe6da730a13477dbd848c79eae433d9b19f94a51b66ecb87c3bfd4ccca759d909a3e2a49beca75a4059ce65922e8bc6da7e847fb2ee9442da73033b27cfbcb94b3009d98601e81ec77d5ec70487f59a2d7cd87f1ab7ad3d51086223a19f5b84d7941e226602ca2aee170eca034f1c2ca059ddaffb26f67902c04c3fa815d969ac5c1a9c6f3d8742b265d6b9d8b33ed71f4d96847aa7bc998ca4559b25404113dfe61bca86d15bfca7e8a9ccca755ceeb9e1336e471aad86914f7bb50ff83c2869fede378b3d218e8dcce4e125d1668b299e15163a7af966fd82097cda45efeb5534f14170859ebf65d03b04ab0e817697d72e4add2b2c0b9d9c3d6c5b816f6d6dd895456d81cf795d5ae56ee8fd5ed8775beeeaf47ac81fdd3b5b9c60523cbdf399ca2456a7410981073a7eab4d79baf17b0f0416ba5ca0487023e34e1851db156283cbed75c4a62271cb9e6121b527ad78bcd9307fd53686fd3bcbf933758edcb112c2b3b5fa09cefffecadae675176d98eafc7e022fca26b911bedf56cdf9580341e78b9d4e843228b3b28cc06046b2621a3bcad6452b2a9e91f7d26ac3677d99f5f7b886bff9755b10e31e7792a1e19fc33d2785053e4facfcb520622245c11ae7d33064f9a86b53c108c62927976a8dc5ade5f1153bb8c7af2da6a775335f6e08fe749e4ac7e8a80a2562bc96c1129cc113584bc98033e8d3628d3cf71ca63c2ee47579944c17212c35e11c39a15348e2389cd7f312fb9f6805210eaacd1bf6cdbb31e81fcedb0ca53da775f1e0b7b68044cd043850fa33272401b424973799b1407f83b7b5cca20e7e8fbae5b50805b867e640bb9089151a4826cdc2aa79a1378f146d779664fcf42d942dc3fa4c2509b31a11d97b26e3e7ccb36d0a16c48bef10636d6b5766ad47344b40df1997f7847a207bf2e75ddc6932c2aeea24ec2a6acdb31315143f987d6c008eef44188951ae88ca358ab91dfc6979f98b383cd950b3135f9b0b4e1fc2c2a1d81a48fec46a48fa1a7a903c30321e7304d4c97fcdee1a52471e49820e9b08be8d5023e24257bc5c72cd30aa6f8fccf25872c714b2801818bb65fe0f06c8a27a8c615162d284e5c7d4c29f0b50de25d3077be79fd0a1bd122790e85f8f7989b135320d108d5d8fe62f50fb4bbeeb90945c8d4f9ed99efb3efa996296eb7ffc4fbfb6896997d4982516bcb5f2ca3db35dfd478cda2e955647e39568e6fbf843f55b32dae1bdf0b6415b6e1468d8c6e131a88cfbe7fc6d5ddd8777140ea2333a57b628d93b1337bc574e4c7fb88c8e05ce679814c6b306863cc262de4498ecfa1c393082df65e56e83a5743587e37cea3b22cfb52f01cefabcb4de6cf135a08ebfc714a22c905e5b584a6f0db6c07fc7d11654c030c7cf4297ed5799418717872ac8667886b65f485de64f9990dc8ebe5af30166980000000000000000000000000006d2da2d0b224353f4a0435d6c3514c2674786e7725078c16767385b1b366f6f881ddf7c382d76c2a154608e0a8fccdd117898df94db440a372d2f5315df4a28bd4e43d74bd0bf90aebdff9d25e25dc44f3aa922c0696fee2dd9e33aea4c0b99f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060e00909b077b3cba15f1663e8cd5b87c629b824cdc2e1687e67afdcea8c80fd390d9386a3500d038f0a54fc20acc4e672468780ce61b2354417f1e55aa199b0c6636791a4b5e1f8f045c70fa7ac431486e55d8cdc229010347555b2a30ec5b3a14870f222a6efa5cb1eda7a4c6a440a63837599d938e356b5b6fc94d3b2ab0703004e7b7367378718175a41c0f60bea1d0d772c74c93b2435b11f2ad47d4d65ed0210df9c84d1a356dc5404d500d8eda502dfcf235e155554983b52a22ced867b650a08f2f3a92ab9e8d3849593c33c75042010d109864aa454e70816b9aea0ee1fadef948f9843cc1971fdb2dd625e3d579cee26646c985cceb629a4057bec08c3a903200410d82eb42c3c4a735bd62fc42290bc403326518e144e67d351061ec459c3031f73b073f3f8b34faedb439c94a2ae7cdb73376b348c58bcc1dc7362630c255103087ce5ba7a9dd9320c0dba496340b8fd378817224d05a40169893f9d48c3bbec021ad0049754fe85b74355074ff379005bbf2c13566c15a42f20e5229d86e735bf03045de1f4e516c5c5d55e4874b0fd9df70cf9524fd952edeab634152b60956234c152a23829a91c2781e5f50d567b343d4676d25f23a7a1374f42cf21b473f29df641af5a1d83f346c8b09e667aa0bd843884f54929892745ebeefcbd52c6381dd84bbbaaa588c9c94b4c41ec027c9a082a093857d9ce726f095982ae8fdd010c0a6f510f64c7dadf993a43be38736963e58c4e85be6c41351145c309360c29f6d48516145fb91a7800f12c9acef85d36f566a089939074706489f71e581d8dc11bd1d2dc8a4c20d756ff5e2b4bbda45ea3ebb3f104a259b69cda1b0bc6adccb2c0f27603b6227b1ac30cf5d21bccc7de6b9bbf2cd1c01190bd7201ab75f455731da42f574964f2ad5e6078d6bc00cfd1c3a1f17b93b055e8ec3fb891e9849ddddc80335687d97b7b4e2211899819ce618be43e4000bf487872ca6b950a319dfec1fe3847c74e23ebd5934417a904d9d74ebb7e5096a7d269b8f56ce1eea6745e87379c09a2bf56cbde8519f8a8827f218606634fd68beb66a48b8f5387084d734b4df2733e35c34a11ac2e083df5e0258ee02ad70465bb56c4d438dfac79be0c7413e0823bdc89fc30b9bd63b7c809ca8176d146c44fc3d0aee5dd53eb4fbbc222c61fc5e13e1c479cb69b33e168dfca7a2a08e8fd64fdc8922ed89cd0b607741cae8537d4edc9969f7089409bf6d84e0aa3f3bb91eba327ed5c60a0b1b2c9ab89c2610883d2a270c8fda168708eb7b97fa8d5acc463d6240f1ad7fb3a1f1c1ebd844408dd1221d6fd911e88d6d9969a749864727e460a43d80b8a8f5d1882975ac71265aa4a7013e10e025a0f1fb1893f58ea9b158bf9b02d2e70c594a641443c20fd93f465b907c762cd5f82e72cfe7928c7d7338909afec0aa59327ac73c75170b6601ce89954a939e34f1c406cae0b37bc11e0de50d5642a2c57db0cbef410487947cb2b3eb0c199c230ad42cae0f3792814c1911daf19e2ca9ba109c9d356dc827555b8c747a55a8912c94cc519659579c48b0818e126866dd3e039066446363565ddebf219aa1f36503469314a2b714478483bbc830bbdcbdd0ec10481e28c17811ea9087694f8bfdbe23e50ed43b7e98cc3d8af08d8d7a9d4ae7fe8945e198a5d284c619ac7ec98d5a942a05755f0fd8a84168b2e110c5f7801ed8e1b46d70fa0ff36ca75a266408d07aa74962ec9c85188f80998261846a92a8613f2ff9bf614fe2fdf674bb2931c17720b2878d60a0e9e718e8c58f022ef21e93bc0646f1a6ebffb939c1026250e3a62def65e1e2bc75bccf07a579f9d2e0fc8cac534257b64930f650ed986a2e8bbf05192f72e7a126ffbd1086959c3f335507df58572005d6efaeea2e861f98006d41b968198aada4378a13bf892645b1504f97c6bb96653cbd35e5210d7c3795b44dcfc12832809bd206637c8685bbde3eaea25465be4e10d943fa06802d65546dc37bc0b4694ddcdea91fc6b3a4110f9a52ac0d2a970f4d2d3691680d1bccb41d79316d9ce560f9d84762aafedcfd54434c6f8774885ccbaeb9c1820a96fa9d4e0be0582935b757eeb8708a0357b381cd1d06c1e342e380f68cc671aa43d253089480c3eadeac3bb211a6a5e283a24f15ea00b18e3916d2b2b3cde399141c9e37f36218670ef6128232ff83170a27abe7c7b20a00ef46dd37e91bec589b94dc1edf3de41010111a777449ceaaa2cbc356234a35bccec032b58f6f92517e49cbe883495b6ccb1bf1aa6125d1fd58c08a275492cbc81a36aaa65031107afb277b3deb6457287d417bcf693f17d5ae0b06fddfcd6b8a73c8eafdc51c69e164efcbc242313b149a5d7a07516d62e9fb0bf4d7fa8e0d291d8319dea280fb85bd074056c2352810d", "5152", 1, 306691663, 1537743641, "3d157217f4eb578051c61548f6bbe8e26dcedad993f68b4ab898ff812c77f111"], + ["040a3d610407e439c56ff8afe4328fed74e274de9c8a10e054e295eb0721cf273e30dde5b500000000026a6affffffffe45fcdfa5400fa873391de764f4c513adf5ff225e16640628fd17d1e5d3aeb660100000009526a6a53ac536a6a6a7594e9916ec68b6c971ca18d867136e0c45f1549b78248717b52ed698e87b692c2d6baa902000000025363fd28fe1d9e7231c33f5c11a93242f9931bfe9ea8dd6195a99159231f75e13b9fe5a2d6b001000000065251ac6553656a18c82c047ca1410000000000076a006a656300633553320300000000036a52525e132c03000000000851536aac526a6a0036e259030000000007ac51510052656501b93d1500", "6a63", 1, -770469476, 0, "d2e7ac79fffac6a8fe5adc3999ccc0ce83849fc8469223869d40e754545e8495"], + ["8bc5717502e3090d7af50f3c4afbf585225da07988ba035b903da797704d0ea561dcc7a16401000000046a526a6aa61b378d70132ed3bd88ed190b1f68ef5ef16371d7e8880caa82e37eccb9a5a5806fa4ae010000000751636a53656a65ffffffff03e481af040000000000f4341e0500000000076aacac52515200a59ad305000000000653005365ac5100000000010000000000000000c8958200000000004f161e077aa6eeea39eec383375aff5f782a0efb6a89769af2c617a05c2d1715eb66cf773dc5aba053bdb1c59a5f2d90a607a00bd4074b85916817076069fdc81ab7f6878dabb1ae46b2b0fbddb2f66001ee983fe481ad54ffecd15f8a65d4d8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e855dc33d6bbeb7734f1ed0ed9a6cab18e529c23ad6cf06961ec8c7a085d9357f44de0f1741fef7d1bcc19cbcb1f9544e0bd72d129126e2fd5d318c57fe95738faec54d3ea549c9521329b1f5f9e75289ee2dfb05c06fd44e8bf598a2a14ef08972951f3049569cf50215c7b673c16a1b542fa869991a5e3e9e262372b58f99022004f375ca25be218b5f484c68bd33aa4d9a327903723e3d58b4c61548c12b2f0329ff0a29ae2f2f302f30c4d043b7ebe4cd953a3ce58f86ae3f9f8f8da3ba0c240a0154d8d36e1a36d74cac8b47e1d236b57af7281ed06c38cc8f2e4e3dfc4661824c39d6bf846a36afed1f7888702c3a86e8931a90b7ca28f245fc0ba0f21aff720317d4e1a2de148fc75d6207c6871126790bd8739a14a65dacf8b04f557e8665f00224aac7ff496eb987f6fb711baec801c82ae64ef6402eee3a3bd2856d9310076602171d110cdf6b20847e9ab10f3f09dbb13c3f5d638d556d52d01d7d4a6d614f13021024f87dffe73619da1b96bc6834b2705542bfe459786d379e862c890f2d99340217c4f7d938f00beb0f2471e705a720ead64f93dcfb48199b017e3d014ce76052f8849e1b762642a641b4854a1fe0a5c3171ed0affdcc4709083a4a3b2c8edd18e4edabe9ca068017f4d1825b782f59fa0a32ca00dc26f3ac61406545de685d51c212c044da6151cfd079b95360aeed00d0e04385a8440d4adea759b71d245f08a05e70085710f6ed3c1a4b209d42c4dee6902f02fc0012e26abfa64246a0637fafceccc7f87a7f38f0bda7378284f745f3ed888ac3c305a8ab53950e9a0a5dfee583b61e38d2d902640a4b385eff94ba33ca2718d725b7993de2ad51dd5db1e0af360b1f46e45783e078607d36b099d7de1bb65605e2602f8d59634210c8629515ea2c50b9c5bebcb36bc3bb4d3939faa0396118357716e5d6e668d53d9808c4294dcb2ca8723fc7079d426165720a82c7bc72b340ae7625ece87c4bcfb242f7dd9d53266ea5718ac770502ddada9105ab36a77be64e471fe522ce01dc2d6e4723df18a05e2ef897898c33ff67e0919b6c2bc7a58a493db8c90a65f4b8662d2399cb9e2886b0d3133842887f9c63c2c9c37db706e8a13b2607d1c075e31db67aa7466dade541d018269b8aa69d91d4eee8bd50728e18b4616ac8652746e3194addd3535cc2204e9e2a9f246be62d318257e1d619168a772c565c49ba65b92a04a38f7acf467e755bd8dcd61aac6e8d71bc1365b31750c2872a13053722b1afc176b242a0b9d91092545b6c56d7af50e1c36c99391822fde4afe3b700b99ef19b6f8eae471e700905bc5c3e8d4de515a15107668525e60e950e4a3dc353fbc47bdc2fc83d2ecbc16c55a68ada476ad331188bc02b3490b4bc26671ee3444a89395c1871157dff6671fdf6f22b08f51b274a7dafe29ae8db0c0995f0af2763901ba2fcb5680a1be276e11ff146435fa1383b499b41d545a8e74496fb02f5edf3b7fbbcfabe91f91cf895c9b8470a01aecd305417f5f28f5c8ecc4c2db49ceb5e033ef363e36f775af9b35592d23ab1eb7adb4a65607773a6db2e5d07a9e8713848f4ee737aee5049cd7dd69a4006a9248c29aa1c93f7fc6a648547316a47a9edcf2da777519bc797a2517d3a62598bba9e881fd53190fda172bb823259dd47601a948b712450ed1d66a48190360030a9b2235286bcccfb684cd61a0799978da27277176785228bb855e9166e41bd9c6b22ef9fe07d669a9cd27868d69ed984cf24aa2c421a26bcd18ad6ee5c0b0f1484378071fd995c89b59efe37d0c6a19f6d88c67dcc555478831032dd8dcaee768cea016fed5cdb0709473c5897a6388fc22ebbd635431db29a700e112c2f8897643bf52345b9498f7d065636508b5d57b6a1b0758934ea53336e4692d4bec7ab66219b1a7c40a236b964595ad91589374906d8a0307e980c402f420be7b2a865a7a283e8fc1a8b5635a37915879ab345327bb106098175f674a8c8bb4e0e5ad86b84d88654d1861b175845a234f5dcb4f78e9af24fdda1e0f794cbaa5b51e60980f8e398b94e2e2bd9e9eb61d1e44e4f00b6b3e4c814b49072ae246a3e045fd77ed398e4bc1f079f660510fcf02f97936919a4347f8f126b376e2e06db65de1a9c350180dca886bb161d078c46e26ccb17714590fdf3142a67523a055f1e0b0580a4849bd661282e74b80b740667ad2d0514c61aaf3f2a177677c234b8c14c1e88f3f92115e439636765f7e153be71e2f2d16af3894b63d21c5c9d5b4bf57fac75c48bd7cf230ea87412c93165c42b248edbd8c540c77b6edcd358a6f56b00dae595c3a9c0f224004114c77e7fcce8b24cdfd1ad8e3c53516bfa63cd4d15f7cd510625c2716d26432e2151fb9a21aff73be6767f886a209faa9116fdc217b0545d8d2502", "", 1, -144406872, 0, "d7e7097a8396d488cdf8a37ddd02021a80a710ff9662fca37422299a00843837"], + ["9eaee13201c6b8c59dd059c98ee5cb1ad200330de6052c8e445bf32a51118df2770dd8df83000000000465636553ffffffff023055b302000000000853526a516a656300825b4c020000000007656a63acac00ac0000000000", "65", 0, 899587373, 1537743641, "7ec0231b0f240f9474e858d063498f2c84ae0f51ea7d0321afc55c99190fdeee"], + ["030000807082c40303c8b05b7dd8bcf5a632a2fe7955de06ea2911a19989f101dc550654982ad2be670100000009ac5163536aac655300ffffffffa42211b4aa46eed2ad9ee2210c4b390a86bcf0a52ec687b1475fe7f665a149ea01000000085251ac5265656a0064e9c3057692cf3d447b808416090748a50d34d563b2fa69defaa74fa7a475fc919672710000000006630053515251ffffffff011b0a6d000000000004535300ac3ecb70280000000000", "53", 2, -366120767, 1537743641, "388c6dfc57291cd4eee072f7ce4c291c7033e5aaaa89f8a7d68121d89d54b443"], + ["030000807082c403041e5a839f58657dd854841b917f0c691e6f8b5c4d098a184b4de94db75af58092020000000953ac006a6352536500aebc3ab7fe6631f2006f2ccf992f93ceea7267003298a182a7765087e599be25a557ac230300000000413eca586a0c036f480a80e4e0e421ae5f006d0497db648e3287d183bbd3b3ed2c8a5e9701000000016a9c38b0f631b5972d16f1c56f85a09fdbe51b75d5af2742d3f1de269114ec7f4b9d7b8c88020000000100ffffffff0340a8c9010000000002005278a3cf0500000000015326b0b600000000000500535153530000000072b2b0a500", "6551516552", 1, -337421895, 1537743641, "a09a52f8baddc3ce1660228552678d98ee546c7583c3fc1456d732f79714eab0"], + ["1e2b94600160e71a0ab14ccc3ff8ab2e8cb7224c0235378da40faa6a6d92e519adeff13a4e01000000056552005251d72cd4b501667bbf05000000000765520053635152000000000367332605000000000000000000000000b34b95093b31108d6191a2319453cbadd86beb2d544e24be291d2283037a9407f4b2d123b97598c42d8dfe2d834b086d852a70f508ca76a562b8a79aba3fc4cb68e198d0604014d9ade129072967c16214a216145d456b419eb8b665d1a9e8d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ca73a5141d878f62a9253034588d9985f9c9429aaad3833d071715c4e5d0c9b3e2cec440cae521c5681a3f623ec6372007c5b9bfc405af89fe6a9191ab6727ad6ec873e9a5f7be980d7587b5c972e3276dd995e3b59e02d0255997dc722ea7c74038297eefb3303f290844ec7b9160128ab3d7808be1280510acb65e0488a6080309d559ab65b80864100bc019bbae50be2a255a72143297f3a055e2240cd6aa04030f9b387844fb8c2d4402c6caa3411b19b63b48e766df08c0a156f96fe688854e0b009595bcce4a4bf66e4c5021571eb43b6973437f1688e7623a729086854e3025cd382a8868ea8a5a384c93bf488124a967e6cda4569469d7fc71571ac649c54e0228ba16a940650273b995b657651f85eda63bc8bfb594e5abacc1863eb3f6e09b020f1d43fc6ca29deeab51648ab9403f63c9978dc506c874ae914d536ae7a6dd9b03294ab01306aa0103dd28685d2ed08db86d0993d8aca8fe578a7fe4cb148ada8c020809434b6a964774d53f1af28bae2da490f26f7229da0fdbde9550a89f1df15c0227ede5ace93ce3a600465bdbba514f017f112e37c6dda852ff1e1fa365ed725070c52d44751e1f2a39e696491c4a17217b3ddc5b4adaf80e5c682f31e47376e938050b66040c1cf9249262c05b8a982a18efe08d29abc1c085063c587aea61c3ce2fd1748e2a637b7f11510f5e6e9be0f05b7285d34124c45b7939cb660ec91ae3dd5f32768850914d44eb097505745946b55dc433b06051ae6cb782dda42e215b76536f0e11b7c1bfda69f7993db41e71cc112642b89382166be2872ea914117b05110f37b1fefa1533041144437eb11fba93a2861117522363d343c02abd6307b489a0707a256e464f0d6d84fc30142f313ce58a5c4198084cf29b64f9862267f7779ed909683c80370aae01a2b3e4b0924d49c91ae92667c7c3aae147597345e0a941278d1f2cf3ddb57d44c33308c5b668bd058bd96ac0e7875f02ecf74744b8e216a759f5021b3ce71aead6f03091295c648b7db739f29e94796d9e45bbe25c2aa59ff0152bb53f976642b1b6b4c43f4d2d5aa56ada9e0ab30fbd117cfca9d8555c9dc7ad476f8becf80e3b3d9ff1a8b101c9ff1fb4a8eb96ef39a9cecdcdcbb62d06dd015526157e53841b84c6f344a04995c40ccf35689fbd3ffd572c3864243e16fa257ce491f3d1de08cde227f782a96e5e1a29278394b54f488c2852c48752cca149f5b9809ba86fb31fdcb340cb80018c9db855a90e967ee4a8c275891039a38e21bbfd9ab10f911cff3d0fb19c50b456b09d7ff33bde831f87296c5497a1cfb26a255b88f774a06de05cf32eee62bd0f7671a325a41e294a60e4c87d463b0d213991238df751f81634b961345f356615f8d841668acb50c6063a68c8dd509fd915a778f9a164a7189ca06a7ad5dbba31cbcf84355691fc7d2d8c5074a5f1edb2f9bb3119f235c0daead9e8dff1a4c96b4ca198441b95dd0414e1895304b8a33123349becc6dfc3aa7368fe633216bd83578a382bbcb5ce89e45f0681b341936cece49895c6b1e0942b5f885778880d62f2629261406c4f50e0d8cc4de5d3919f29120f727e33b966929f6bcc472e78b1f96387480a3363208d8d51b7ee0a0350237a56425e2888e9f73ddaf06d3f21579140e9eba47415388c24a51e58afcb0b82f1076ec69663394d1ddac646b9a73ca8cf98f0f1b1958d7e882232da8a8b1e859c8c1ac30d5cbcff86dc6c2bf2e933000c85f5dd2ab9553e04997c5b59e69e614c09c1192b6f8526b0fede4bf68b2312ac4cd4cea6baa229c5d7ac76e4f4040d084ff687b993348ace79307be36844bee131a64f7ec2709971ae528ccb43712fb82203ef86e76f5afd48133e137d3575b6b4a7d6273878d2eb9f21695837aa4c616ec68120f8dc404946c6b45941cfb54696e6ad0f8edef1e661661669d937489aea574050810ce3d2ba8a8998c7b55c04a9b12ef63d5c7569798d9913e97e50a46d06c25abcdd7b755fc0250b0005a378d9f2f8fa58b6d8716ebd939d46fc2683bf13b037d532fcd5904e64e75e99213bf5edafcff7c93cdbbe618ab588df43f9483bb420fd158fde7276a4c68650bcebc5c51b12d1cb00a1f42ad9511b32087f3b6d0130c8c498336c13f38a789958204d26a3dd5ae08d694d2ce02c4919beca140cea5d5498ea8d0812e0284962995fc4cf783b8875b528813c24ddde4e2b9a8cc73240cb1b82f6f67d1fcb29ca4f35eae494f2f52a72d3a813c4df398b1f05ad017908a7b5c217529ece3fd404000000000000000000000000ddd8aede857de69dff271a70ffb356700b534359bf3b927f14e6c48fc48a7695ec00a1aeef1d0023fd397eebfde0f4a9b186c116eab20231da729c9a7b466fb793163d51b53e0c3fcb7c4ead1dd56e7880a35ead458d4a82d0c68f954399273300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e936ae4b72164ddbf1efca88e762f1aad4a2277c351470c5ef1981aec62ed13743f3911aab835a89035534f8e32b88ae0da8a96a6bc36371fbd8d9945c7f2ab6613138e74689dc56cd78a529d4ae7a9d76bae32bc406f6b147dc9644f5bc74a566c3950a667213eaf63f11ae77109ba58172afdc5d71a7d177978a607f5c4fa70306a04a23fba4d86702e317a7719af5e981f0668206cc0740bcc18bcad7aa6df40227c42d68b2ccf41773c6e23c4653668d70c35ab92ca9f84ba7c15ca731895def0b05ee68ea3eec8a3083ccce1bcf631d750cf10385323d67ddf0b87619c96d2f7e8b15ac686523061d1b96281d132b70720e1895764a1ebad228af85756e7b2b0c03087fb9104e62b1729e269e18a263462d33a222827612d59ad47a4cae1636940703002e5c984051ccbe3892b6159c140e01f97a0fac4aec461081fae699ebb2b8c3031378ccb9e79a622093fbfb275abd07ef6eeb4df7795f9065c4969f185997891c03297e13454e2bd36b907cc82055a252b0a51313d65aab8238e2c2938be314af030316c30908fcb24c055428ba9cb0b068cab21ced68e0d4f8d6ad0c4c2f9a88e18376fa0e625758c9ceeae410939641626daa840be32102ce2b0b4b61618c9c833788d4d2c70e09884850ec46d0056595dd9ec6d1da8dc6a8f8ef0dd57715334df3014061b364edfd3966b2e5afe4b88ad78e61c5896ad46ab0fe927620f2b0331871a2c1cf38813c01e946432746d5bd76dcef2523cabfb16fe053c1ad351c82defa1782201bd4a874a399f0dd35bf8d691210c39020d9b2b86decb5809c0820d634035dd181e65601aa82b6c3c9f5c31176c74423d4cec5f9803a6cf02713fde3d83c0f17ea0984f0b68c8a62ee31cee983a30bba020851c3384cefc56ed387eaad1bb3975aacd7cbff8ab53661c01dd57cbf46c4b5fa14bbfc3d6ad2172ee056726a12f9c61cc7f6c3e0c4172e18649ad9bc0c03133a7f95850829726c57fc94d1bf5191ce2e04b757828482228af653f436814f3c1acc8b9bb5e7ebdaa284d2d287fede0376d8e93152512cb6ddf5e39299fc7b11c17355281579a3654df30c40ef0c21ea5f02ffd49b094758b5744ae6447ce81e76539db0ed9dff5369b7dde15a6bb99c7f0f6757d428115da3f837d09ed248e24de19ab4079838474b47fcdf3947eebd1af8c8ea005a97730eed63d70e6a24bedb84178d9f26acbf194b454c73416b4b6de846b3b4f0d695b418fe4fc8463b70728f8a6c3bcefdf804a7df7ea6ea6a9d3adc74b07613b71a2b7543382c2ac31d858f7991740422a575e5eb4c84e30a5ae6cce1267ba4e5e2f9e50a2019cf154abf741dd541b57ebe0b4c586dbcd2c730e8d7791ff606250af184d40af45d2ad134a1d6feffbccff5e951211a930b98e87b833cebb4e189576b85482079979827a7024aba435a6452140dd1bc0c8c5ccc8c34485d60cff44050d67bb91799177ee476d7b22ac4565e27c6d124a8c719f481414e59dfb536ffd1e3c8735c70a1f955a9b22545856d1309912c97dc7cc0048d19ef16b67d539dbb2dbbd607c8f4b743ca4ac795e8761bff64b4393f526a2947b6cad370dd36c641421a77fb02b6a05cee163e7413af871f17a44c6d090b4d180a923ce8fe7425f1594f75da318db9151eb85b5a5b775863e03c0d76ed5bddffb5b33c6ec2c99327641aa77de35bc4c51187ea89348b7d2557ae305c4b75621b8949cf2fa890b34695fd1c834c7c50f307829e5b767eb541925b760c5477a05d57a052d16c0a372901d01f4d9ac9c28a6e54ca37371f4b10407d2571f2cf306294aeffad54211bafc4aa2fdfbdb0fb8fbc5a5d47c81890eda85f71cf0149d9400bf807a37baaa11bd73e3ce8321cf6422573e967ce5c73b5dce91a652dac5fecb04025d1c39ec555c13bb8c645b719a301b39836df6be04d54241a9cee8101650a7692e4d8078de745731e3d9daf00515cfee731d4c1fb07a9947e2c48ca0d75e49730464c6872c27b16b34a8f05300c2c3c3454a4300348dccb0a7ea57b444ec1a75f57089544ce5bf3871839dc7bb859a6692bdaedfd994da91d14df4dfcb7d56d10267513d40b287d67d0f921edea511d3dd94dd76333ab0d732f8f6cd03328aec9410dfecc926fdf585817e5d4b7cafc6f0bf46cf5e7ce23bf1f3ec14b5c3607356288cf8e864380e034ac31f008d4c71f2ea5f3a44b7dd133f3b1f3b050dfe6f70267ee3197e2b7154707e043593b0d2c817d8b132293e588e136741b9999573c74ec50319da303574900000000000000006ba5670200000000fa23276100ee17e970847d30e9ef8a2af36b581b5ba0cd44899eacc1c861ca87e620c5b19dd6519f31213994e3598f14deca0ac7c0f4fff640801c698f5543ea7ae02956aedf0699aaa94672f4f2b108c65621476e7c8148e79955d5a9065ccb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e6ebe1b8fe5701520555f9534c2c6c1f4d1f92a835c974b0298175916f54fa716859c315078635f415d6138bc308cf6887bffc1e9473f959d91d8759fc439af02b7d3d1f8ee5ecfd22d7a06735f981ee8f815b7b02b67cf4494b12480db928b3160bd0a1a180d50d4f76ca78a5c4093a133c9a49fcf3e8ca5bfe535a4e160930325e98e09c61dcdb83e25c93651c7731fe73dd71a50e3cb440a65128a6884a0f40229521e875e600624287a80c2c363f4a5e00d3c0d64ae26e277515b1a1531086f0b059fd46a61d9ad2cbb08d63da04d1501739ff2b1c109a8d224e939513d552a703d4f11bea27aed45f502a01db244213b408f9b0324f1c0dd992b90729b4165f00320834bd1637cfc91a5b8dcf261f2eadfbeefe28df8ad5314b7a23cbcfecb7575030fa08e96bdec8c1d2903acb0f17ff63aadd8f260c01faa4f90d318ab30dbc2a102021714e7b8d5aeceb560661666567d2124931ae489be5dfc20a51ca5d63f7cc502205fb55d9691ec5e892c7e15cdf5b1520030061c107d3447425a4b25eb38f49f02149395bf4aabd9ee1c2df0add4e79db9966cafef2efe8b6726886710eef698fb6b5be5e0c7b5ee2c4672b7798d183713dd21a1dc03e035e9cf41d0e1806638d0c8444c02e749920f458f97b2abed507c8732895ddd24a1b35f7fd6d1ee0b4c9d3666369047956e08327048dd75bbf962356a5acc0c851101dfd8c279520441ad8f8fca0ba9faebfc6ac7931f269adb813c85138a89de6fbc9a17febdac2697bb3ff0b67c2530c0a373ba3022b439cb79321fd6e44cca29e3e0d1ae38eba05a676bf2252fdb53ff24ff28cff11198f2dac13c9cbb2cfc8c6d4951c5c7a617cc5d6294a1204b79af5be5cdd805d89bc0ec48384aa52d4bbf7e7a975a697c1f03aa2fb24dfff429ff40802677039e85932cb5a17f2ca38b65daf921032b9892914784bc42bd83ca5bbe71017c434ae232e04efbd6df827cde6f1dbac59edfd84727405c844c7d88911bd485628390561ebee4df8b3661f44df571b9a46e7b26399b288f73ebbb63cb5961b665c6a9329f28307de416858f953adb730fc05c92d3f7ea6da5e287fff3df8cf015a95a1c2410c8534db87267a4f96c9adcdbc1d8fd23582b8d2b8b0972deb06615c3fe328485007fc6320de9616f9ac9093a14524612a1088fe20ad9793d54944bde966dafe8c269c7f1cbb7f9d5c0fab767fba39dcfa7d9b6f77d1fad2d3f55456da3ec6103de5535997ed1206a1f31e3be00aba0bb6cc43e9e71bfaac2d387ac5ff37f8abbe55438ddcb5544b7082fb7f18b5121abe5ed9a6d5488b1efdb7afbae776d6213642a459498ee6c576c164582b28f12044e99fc9ce5aab8c482da1df7323469a64e2f6eb7301a197d652594d6e865632ddf29d5408a292cd9599f1975fa031525cccdae1affaf9b4610c6ebc7c55e27c598078db308ed55e4473bcd04e79f81ac7d1e291712c42ab71fc67366bbe51042e2741560315f8435fddb87fe478742099b928fdea0e54b1f447ab6429740b47515f8763367016e61bbc24d7028606537454e543d7fd22092be8190d03fa4d3e1a1fa95cd127ebabb199905dbfbde1044b7988c7b10ede629c8a6e7902c0ddcc3044d0c3b452bd1f3c777dace76d0c25853846d27affca244a88fdfb93339dede4ca563cf9697425abc9f620e17795cc9a38a9f7d002cae5b048caa055fca89ef4dec950c145c80059f893c3704aa30730208f3237cbc4de194c8182ecbf18d68347d148e6c5f697bfd0053711d61fb606dccfa4c873f20980ccac40a28bb21e26918dc1f21409dd29478edc6f6f7a4f01a1bca506b941fd79146aa9a6a9c66940dc8787de07153e94276a63f0c38aa5d973ff451d59adfcfac3eebf509ae777657281fe4abec2832affdfc951c331ff7b0cb1eca6d7df3587ba07bd74c273aae8308a132628c55a63fb1b1037f7f8d61f6544f86e0b1fa58eb8b34882d004af7c547a234a61382cc44974cebedc5e321b534f3ed205fdbec6ffbd117f08ae3222e9cc1382aea3ad30564867e7c35e7a60176be733ef3083ad7f8668dae63fd07f29ca8ee459a2e4724582726b27986bd50b33329e92a16a01847017f3e4a433355d2f9025511bd62be2ccafc02e83a8943c5ed16b90449fa027b9485450783732e60c7188a8abbdc6e064db91505df3cb02ac2f2849807d85c64cc7ea35037e7f76c8b74cbcf557e6bb3449ff9d810215ba8c02f126fc5da03ff4271e67b64a258fc8955e2b28d39ec6d54ad56cf7c739a7cb8cd1967d258d5cf4e5d99a2df8a28f2bb82dda8a0145ebf901f64587fb3c37e65cfd6831ca3fdb8dc1856bec90c335b079c6b13a2f1551b9695f8f3217017a8770279e173a00044818f9f99d832e3a63732995a8b145bd273c0905f0a197105", "516563530051525163", 0, 1989934335, 0, "165f611ecf21c9dbd4bc07fc526c7abf55893367982d3c1903ce4d5ee7d99c5d"], + ["b4dd23530332fe76dfe9ba0a6d90d104d38b035e3baa01dfa187e7d46fe5730c0e9c6bec9a030000000653ac6a525365ffffffff607f485fe6b8c898e2a91f0c0825c75f4c0c27f431ce577f20f8c94906c100190100000009526552656500516300bd241ea7fc3ac43b1e85360f222b189ee49dffea369aaf96b3492e216d0f17b2c5e7aa800100000002655311937d400222970304000000000265ac0380e503000000000765ac0053ac656a0000000000", "515353", 1, 423769690, 0, "997dabc362750faa48304d97cbd2975103cae785213f6f702f9317a59438688e"], + ["0bb1213304674222a1dcf78be33c7b7d00723abfd7660ab20b3667b1f4308cdf02ee671cdb01000000056a005163acffffffffc56d800b713a5cda35cb3261eb187be8bab3daa83c079d192498f88ca68114e8030000000400ac5352e0e2353f8eae579406edd13378539c9675058ca44c68881efb4dafe02da6d1907feec7de03000000000701247b75e90f5449c067311297547d3094eb5e954ad2774bf58adc9491c0b406311e620300000003005163ffffffff02e9a05f01000000000163074892020000000008acac53ac63656553000000000393fb0a000000000000000000000000006948bb6594ac0c03b693cdcb4f4335e1ae44c8113fa50de602ed69dbd13daef05d928034fa5da178afafc2d6e43737856a2a49a142d390cb4ca37b546f8283159c49ac223251877e268d1a178dcaa13e5f86cb8f0363174cb3429917db2494ad0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006779b029ae5474ab4eefadd082543aaa653f6c673764350e1411a8d844c37599ead2ee62747bf591c54823fadd61f512933d1c2cfd8d2ac44ded4d299dd6123752bb33763d53927550663e126163938d3a32721988bdc909dffb566bcb055d60adcecdd7db8716dc5d9048f44aa42fce4ee76661d64aeceb8d8fc5d2463e5db0313e9c33b5f8dc2b92d06024c4ca13e41fa7103ccfd1c0e72373364f1d74c24e30206ebcb1957123e0b8817b84c4bf81e5adc58e39dfa3bced6380b5b5a719c5a5a0a02d6b68c41f9e5001b395e7357a0d9980c4300d4b691cc9229e9ded2d83e3e7d011a8c472d7e167053b4b2c2458fda8ac58e6c7cb0d2a04315cc1060f44a95ab020e04d07f63f5afb8e02b945ac055696556e853cedaf13e758c447eab08e76a3903208f5e3fc2fec09681a79704d55551404c0f1c4060a11dfa4119c7f909c5e6eb030420b8cf59e3e3015e99d535b78e7c826b2204187278be7519dfda3bced5d9c302295b1061f96a9df93c954724dd1b385e2afea2adcc03f9f66c17af807fba0098032ced15d3e9ce2423c22b9d5e4bae1b0f5328e6af67ea452a7d4936b92f3b350e7f0797b9eae777e2ac7b24beeab1390c739e46fe0c0b3e8e6fc2f8c7f1b527c50ba1282df9723955f564d09e19f03772935823766178aa31a77e4af8e7e64b13fb4cb2a86761ecca16556d1a03610287eb79a2dc1cbc53a76837457542629cea69029a9af516b84e0f013ea22cf2ebfc40569ca1108816758c54ee17c4e3d9ccb560878b4b03132c43b382aa53c757f82fba2d1d67675cad73f5c8f85b63237e9e3af8772e739db6ea832818b1303167e8e58c93a0c1b82ce7a95591499b35bb8020cbd638494895f1eae229ac3c1c2939b625ced37b793e71084936db94ee779125caac330b3f72d5e50c24205ec365e10fd097447cd610075ec8d3340616ee5bedd6600a419c173f4cd4a7d89257f90d684c7deafba52b658b49a39637da6da0d92f6b0d636692245b24ca3f20d32018a09ffe3fa302fc65b384f97d24389807f88ac77de8f354a1a56379f667cd3dbcee11362a0e30ab219c5fb5a78f829d4a167751fd0d35a3e286c90a1e18f97809f1167794c67d73b9a189919a44854d43dd26f6b1aac13a2aaa5684619c9c75f3dd5264fd976062c8420c79f92b9a84b5f88480393bb567526a3a78f6e83cd74e6d9a5e95cc32d2693c56d9c47858efb7ff6053eff67ab8bcf0d8dc447a4e6e91e7bb82f290bace29a50343460ea44c27720daea92e74d4e946761b57dcd1068160faddb96dacae23eddedf4e41c8efbe7a1af68defee9947113890800f76d714fd6b57d4b2c2fa5ed054bc8d87baf2d077893e17d9543a28de0475901d5524b93078456710b84dcb7b39be3a17a117a4936ee0199e9421abd7e3dc7efe12ff7a6ca3bdd04c7272d3dee4932e83bb1051fa3b416e599d44dd422f0e2a9cd2170ca8c960ad719f68bec14da0eb574d1dc76c014996fc85c5bf6ff379ea034c6abfb4483b8a35808e68b33af50b41fe6b79a3e6d2e279eed46a06fda89c0a0e306eb5f2ea6b73e94ac6fbe1f4656d560f029a4e5e4a30c4d6af4a6a695dd91619b24c0f239b73122f44868d33bbacdfa748fb47aecbf1796f7dec38ba9089821c4df326ba4518fe955a82e2f2af8ae92f02f936736e96473f511b3fcc86dbc6a458c952c384269f3b7444cb96277726a204bb1e4e3e8052699488fc9500f4f257d3f43e95cc3bdb6efb5af689be200ddaf8357b80416cf5c66467a326bf3564face99492174e06dd4a53510a7cfd3e9d0ca10dd0975806b2e6b2240081d63303654075bc84ff70dec00cb4fe0a5f7141853189316c042206cb3523f93dd7683747bd5be42fabf922f4b4949443e2de77e4268f7d1b75015dae58401ea20638f701ee52e42a7ee06d0b1516d0244a9add4f6d581643e9de4b58375d13835063574003d3908b82dd61b98dccf0dc5f9b683da5356a83c99225925de16bd04e94a88408402a6f030e29df399d0515f69efafdf8db5c66b261b5f31cc4d7736e31a097dfacfead19f9ecbf53d522ab75c65eeadfb5f9741b6e200b6052cb006cb12ca3f6beaaf43f9353a399c6c4d90227350693873f190afde040ed2bc8e9be7fe8992b344452612be6b4b816ce44393866d89e6520796262efd45b1915c0070755bda8176c36d0282e518cb5f2a3f69fb9444c15e53b0e773451d0f6d9197b518e17d27fa7a5e1a68d5a3fa29e92048269d5f84dcb52c91be6e87392151b2b0ae5342a1000000000000000063f6a10300000000c9062bd623acdb0e821651aaa9714cf6afaed8d8b5a16cd67f209059d6e27592543d22b7809c680fe7c2d201b4d94770bfda7f3af6e289236cf383500b3472742216d940900d09263c1229d8f65fe1de656edadbc7e7f289f38899cd3039e07f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000af42b0586b435cded5b945072f667be1f75cea6e6c7989c348441420e48d1f3c8e1909c55fc6c31a9435f547aefafe9495bb2b04fa665f8e39784867e03193dd3ec3b77548b29024d73d2dd63deb1239196d30e94335ba286970132a422e544b29cccabe162f1d57013288096c1d38f71ac7cc4c7fab271878cc4b4f91a0b2fb03030f4801308e627476761530da86339bdf9708ef1572fe6017dc42d573f8df3c020af071a0786b673334bc04268730fb76a2581c3a257761c36b78c32bb776e5d40b05e3093f7124cf37dff11d9083609c44e075f4af1dcac95e3d5b5ef67a42e375f76b0722d40422d7ff82eaffb0f2fdcc8cd4b59e09389232bfe0f286e02e216a0208744ae1a602c6287ad11dc5e307b5eb90df277df5115a40fd5e9e9cda7618350214c5acb50df817266634ef4b8047fe3b1f940501c12613aa70d9f8a2f6331b8a03113e443245324c8205fa4285093da6ff071798ee2888c5a545e5d719e4b8f5e3030261aea7d7ed39cf0cc06320ed78f86cfd037674a7216971d0998dc8e1c1afad0304a027503e3ba0e2a7c43913807c12de435052985ce7b8a9b4e78d0962fcd1107f4a6b70d76a4ca8944ff98e3dc2aa3211ea7788a3a85c7f9eead0bbdb864bc895c4ac7e93ff9fbc7c520854e09a2faa8324e556724c1d2f6425313f0785a069fd7d17b1ff976cae26411ccac48c286917e7d7ab4744145386b2c8a04e649c81dac40b2d8b5a7d4dcfd3811fae9fc4efb6c9e85dd0d615ba5b91b1b221f397e3001e2917ec3a1f92de743a49c4bc561efd1fef9fa71669d8f60aef28f425fe908f4818e0d1ae2f890025c879acb4fd65405b4356be438e3627d897d5937bb2f2aca61184418a2f8ec9a26228fb84a659ced8bd8d82b75139852196e58af2679ee82a567b9993fd9df26b7833e40a7acfe390c6336f433b939c1d7aa3a397309f85f1717fa070f4a7985dae31a6ae08a4979b675b09f1083ffae8d61e838f4a7caeb51c5b5ae659fa07443eb05b3452182579c5a8990b9091f0abaec7ac06bccdfa6e2892d4200b5582c069f287ea126ea1e10f3316a8a9e4e05f119db85ad110e161b152a3bcb656718f4fd0a641b8953f8e5403bfe333c2b1ba7f5a4a762f0551cce3262026734cfaa7087e39766e4fd3a2b25979f02d48e46c8465f624e563fc28d959dc7c74738cc41e397950e3db8b7acdf13485052fc41baf11137377a20758a67ea1b9da7bbe09f365c9c470b7bdb33460b4d100d1e805d0bb70402270eeb4ea9fd655b8e038fafd995017a504a5882ea5b0f85c195189f10962cc1f5fa5462c2486bcbd445ba2acf381f106a431eb2051a397253174cb0adf0e0147be1a3eeacee59c9443efdccfe311636da9df25d193e4637380cb284fb6716efc1eba2d66b4d54af1006569bf31caf97da67c8e029807e1ab5edf058d5d10b8c495734bd94ef581d4864a7d436613067dfcfacb9712a3485006bf3aa1aece95cf93a898758b71d5e1a5947c08ce8f388d3df487225ad7037bc89cdf176af9f8e283efac7c3ec2074a028ffa1419fc496a12f7cb3638ccee18c50bf1065dade437e79c5d57de787795d14ed65bc43ad9e03515712fe93f89c7584127bd09d022da1f865a83659de30a45f2e7e5a6ea57fe7d4be20fa0731aadd5ae1c12dbc34cec25730f6f7036f6947c42b4142a7b5c87257c3878650db5efee096e7c95522d07ba2fe270a86d31c867e77aea6b845e5e8a1987eba0018f513a773d702317883644553fc6c09a52b55a6124cb852e6921ef54a799d8deae64be104794e59c224eb1b50568daa4d910697a86bc1ad34bfc6efa11a587d6b377100117acc6b5ff99815bfef97c1029eef52a8e3638940dfe64b09c6d55db64e7aa700ea784fe7b8e8e4338a9ace574c3fb29db97e6362ef86b44d60a29cc98ad5e6a16b0e8efdaa48b5c8b14451140a47952fc74e9a21149487fa27e62e324a715f25e0cca5753b115327615d3cb6d2b88ebb1a274d7d6a8d037e5469573ba038447fecb6a69e2969ef4e16358fc14cd9309902eb8c7ef48bfc7b583606f375bf0ef520ebdaf18443d1c15af88386a5007d727be106d96ba98e886f74109624b4ae58628973ead3813af24480b07719887b543115e75d92f1028bdb7eeedfd218b79a311a1a4dbda55f60b38f42d673bc2f73bba96862c1ddd20aaa5d6a67ca7a010a5ff417e58b9d597137a86a6ec08dfa37465a68abba8b4daf74c5b5b267287f4c1acd591e3d17abb4efc7cb5e933399539d96084da696ddf9b0000000000000000c2328e02000000009f7829c226e829379977237df58d98d89cefcb7b6eeacc6861e3c3aa99c6bd00ca33da0051956994301a22ca056ba4bf4a656070b590cca2f408ec68f3294a91b18cca05b49f7a49092cd6991bc7be664ad4eb42c64ae3535147d995d59403c900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ec825b5dbba8b32a8fc3689fdb8ea3f2445b752ba7e4997a89c810b4cf8cc2d908e34805717b2fe12fb58d9180b64a1441bbfa0023c8d8af70615d197a85e9b39c05c0ebaf60887e7940f0ec6a7c571cc45020dc700fcf8bc4d413620c81b75edfb4a002cdf157323951910aa21f4483f2c9b46738658b93885cdad76c97945702143954d90dc853f53b7e536caee0ba1f7232700a05277d26ce962f117d022fda021952e4a55ff7e4441ef6399f03b947e5107c53402d8e8e31a50cd4082182bce10a02aa028d0c45b69989acb77544b8c041faca3154286568a57dc4c1a18844f5c3075d5da740097303ff4b9d9f53ab616c1deeaa1218fa446cd95cb9c2e37b96dd022cc0e2426a2f624211aeb385748876a6dbf6b1dce2c4767117e0e1b461ad45e50308db31c2d44799ce6a0d78e5b72c3600e44123da6ed17e99508ff64cfa77c7890226b936ff1018a3eb545e65e3ac22c555beb4e984784ad585556f2afca3119511031130c84b2cbe686de21f68fc3d6eed85f4d90fb1eafd7dad942ea16cfc2f44fb032ab8a627b477952a3c473e71e593911828c45a48f26aa80403fdd48da182ef1d98e7d080d81453a60ee1de05e4739e457a308e8321c83db7d2092434b8d398a02bbde0d94bed578790a2741b7bf97f531b4294890a20db4661ecaf99e7aac7ed2cd52474de4457d99aa057a6ba3bcdb4fd2fc553d1398f336227a4e9aa3620943c644f21570f259cf91dc26b95f820aa2528d37e58613a22ffa2ed665dde97929ea97d143690f2c3a5562aad4bb765770f635305720830e68b31df6f4b1ae8b14a30e191ae7ff5f23cd0ac0eb394c993882e0391e1a934dea878f5cf93cf02fa14ad0cf01015262e2143d39da34d7612dc0066dac459e0cca1c9d6ca757af19088d3b33e32f9fb75164c11b18919a6783ac50786212cea717681cbde88f0c38e2f0aa367bec21e5d96d48a60eadf9acec0939f35d7965cfbebcb95aec3521e67cda731d13e0bf5a2aacbb1975876f038e2dd70970d9ea7de565d80b3b7e3e74fb866f0c838f901d17ddd64be0f112e951905ba1bb22b4140756572ae2ffb6ed32f33f865058e640b52060661ace03bbc9177874531e87addaff1aaba6aa47b69d0a2034c425ba9ed125fc223b42438af37fdba3ee5fc871cf06b3f2fdd55651813651ab3d5eab8ff310322292defe7daf01b0f3f6369127ac9b0c22b0f9f793ca3137c7b319d450792dfa4275a03ad74269821d4c3b24c5aa4697c3ff49393fd52fbfb651aa77782e79c5608af9d69aa8d9c84ae03aa90403185cf221ef3ee0f1b8a04682186a09e260a7c9cb402ceab0004c0a6cba544b106309f86531c90db9d1cf5b319a39a36f068f9f0059ce225af2eebb743dc1bc33f884e05b3b633297394572619c13a17da1a3c72952da891c56a9d78db5855ef06521d5a0a6af9619c27afa4054538f9c31cda00886f3e10bd642df628f2857a4bb581a96ce5a4b1d0a4593423638b7637ac88bf948e711364e0bae32ff11f13c20db1904a240766b0900fd470df9244ddff9eb781c328d36828af66912961faeb3c9be1dc91ca09d4924a50d6d6c1edb75c2a617759b83ffd706c2b0cb35d81861e2416aea48e61714415dacabb8282290848e998dc9ceedd1e0d08308d2badbe44ad287bb16df460826a8c7ae83c6fe99f8a5ef521f81d30625877698a93edc01476b41008e3cc59c37b5dbcfed19f52260e5a979ed6e95b35449a14dfc7086ff2df6aeb3758749ae0e377d87ce977ddc93544055ff499541d268618abb7b8994326bdee934c9046b56a8ff16acfb3122cecbb31df9f27a72ec016f63f7dcfaf46a42245052780687b668a0ca90915c07b78cba345a91351ff3e6ab6d5cd20274f2cd062c0a641bc894d6c6980f18f6daa302f5860e12a4edec5bb0f9c8185b5884a7b64f83b60e4171f36a121d85444ec2f6be4d7f0f2ebe7b0d765a703653d7fe82819b562cc4f344cd3ed3a33a2d7d6df624afe22928156152824920e3dbc3db6d6d5e734dbc7abdc729c51745b1f19056f06dccfe5dabfaa45f6a581237f2fa8cd9c4762c9f3028e597c586e0f5f44607b269611f4b21c570850be61eadd76a1e43bbeb8a4c031d9688fedbfb160f5b33b0f81209bc6de188dc7b2d5991bda2005c1f52b223ee650d6e18b77d0e9025000afcfdad9502c8055afc690113d2d049302f5c87f587882076bbdb22d138f0359995c91a0dfbc73a93983520fb50c8f4f598f56f91465162ee89e6a30185da931206cb3a8bc0f7eb6446e58fce5e04316f5eb77a4dc4b4a7906eeaa2b3ba948c3883dab4220127ce018b08e4b341f6132e305427552d97362b7b9ac331f14ebec61c65cb980dfc36f5fb2e9ab18cab00c43edcdf76f61b3e6e03b0784578bb22698e04253b80d", "", 3, -1282780702, 0, "29534f6e4cfd75e2ac1cfc5236336fdd3efce9f621a651a3c4e22feb350bef98"], + ["030000807082c40303cb7f7f0424bb532696a9e08b08db91f83e94d6aa801b8ba43e3d83daaff8d3e0010000000465ac51ac63df1c170d501fc8715ee5aadaff334b972d049af826733c07f7db20a9e6b2f25d2c267b0000000007acac6a65ac516affffffff294ec8b48e77507be6a762f7bc51c3bd9015fed36bcc14f8ccd159155a7ea8110200000009636aac536a63536a53ffffffff03faeb32010000000003636aac1f505c01000000000552005253655ef5db0100000000056551515352000000001ab9e645027f3603050000000000000000000000003476bce1006fc53fe8b757e2f3fcf06793cb28ef4e08e0f011d66ac981694db7abd515e2a2f00642f8c25bc9024bcb46eb6fe751e97935428d5c610f4de52a97f045923635223b1345ff5faec43e588839bcf702c67ebcb8826aad4dfc59b9a90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034ee4d0d34b4d79e3e777321a5159eb13c1eb93e7f87a821998dd4644ad1f64fd51a1f27f937613a36474816074e77ecd45667b2e0c8518602efb2eacd3543698754b4fc542c7149e070603f4081b8a9b3286258f7b183e1d391beb877b76179dabc45fa7594e9f87f62fbfcdcbc61116cc004f280936bd2947475f259d1bfe903088810463ab0152b7defe7fb25cbe10b38645225670c585610750529704eef9b030ede89175b4ac1af5f61ace40e25181399ad5ec20e960d1de0b6ea51d99e09b50b06ed38b452f07c3dcf88cb91bf300be4219dde4bb8e3ee7513270fed8b56c366c1c287bacc94929873e8aa1bba818cd20c9c4c107cb13fcf7f83d36b277a3c4c02060c317d4bfe7acf899932f0608fcf413bc551b38689d199e1376fb9cefe827b021c3414450a93f07865d99229cbbb81a22ab627ee7e4386f35789265a50be5e86030da9ed6fb8aa7e8cb28b9fde8dfd2da9780b3904e58be0a67b5c72b7aaee58d50219899a553abca9bcba2cb41635d4c9c61f97e4231bdc02f4fd19515675306405020eb6a6fc423b32576876a34bde39534cbc30d4a280cb84b933ea98f598574f0dae36ca1043b0c46b483594f779b87864dafd09783a7eadd57a7ea86d7ab068734fd66b0a175351df6dfb9382d67412584e3fdf99f4b4fdca2428d502613e33c1afabd1de92ffb4329e70c26370d382241253858abfc41a5c4b3de9512734501cbd13ef6ae25b7effa8b7c7222b9ed4d39672bbe79ed44722543139a8875c2a340b7ec700d29eb8130e1db44809e31b914031c97457dcc70976c488f8de82b840e3f25c1fb02380ad12b23f8c878e1d88ed04b58d68cc4e370ad80f7a7db2d6de48cbad80ca552725c7917bcf36aeb9630b15cb030b1cf3edb58977d3826ff734350aee9dc3fc4a066d13ae123454c6599702f2ee12e3f8d4ff3768cfa62b619f7a232e8e84257351bdc38887f64a763a73e5f1330ca38abc0e875c31bb4f9ad678befbf0ff639633d5e1e4eed6bf770be952bb7a8ccb48db9f26d98aaac05d6392e293d29fcd6f61bf8403cb1ec75553f7b6106756d5501fa3e6c2a19d347a0beacc782073da141d3dd1adba19baf38a8601be9b7289b2b057a729a4726728f221c8eafd27ae368956e09a4ac7ffd0fd21fda87454002d6efd6e81a0de81375048ac9544944494f4da0320acc0445d6a8f2c2a30cef4794f1a85f47e0ed05f03bb311839745af065406229a830883a1a9f6151cfad3908eed75bf8c6d147a6cfc057d8da56a114be147f494c68d2506bd283d468014a877d1c4f8f0ef1e85b72866bb1df58376724b3b3a303fcbc8bbc8a4bd2096056fd8faf5c33f4c1b7bc378c860d22407c6a006579618b72ea06c808202ef47930f415b90be86f26b9cbb8b7d755cd590bf78cf491baacab39f5e45abae9e6aebb354f8b16253d7ef14ab310a8c5f231fc61d906b9f6abc752f947a9fef803a13af4388a25ce7472cc064f7882211294103bfa907a967bc1403ac4005b1c393e00d733a9d017d999e23a246301d750ebce3a0f7d6506799d52384a8729a50fb788a0ffd5db224ad92f6f128267082574d9fa61590f2ff233ffc4bcb8554dbf126f810735cee520b5ea94e2cf7fbe39a1ad28ae879b11c233c34effc5efb085272691dce730ab116005f90df34582adc7313d04e3e737cb1366fdc4a88740b3927f795db604836824297a02dce55102ba8366d3223cbf18b82152a718268892faaa20fe6ea4c74e65b91150e01003fb377955ef5425997e266f4a271bfcbe76624a5a0ab650cff5f7c098a474f4727429d75de6d134f0f2186baba75be0f0f1f9745920e73bbffb03b554490903039c64303113f2f59e619dad6a6a5dd8ccf60d8e2effcfa350cb01b93e05b5bbd17c1301e7602c187468ed81967f3889f09536141cd0554e67bdc5ede667ecc60b30675d02f8eb873f7eae06394369b7325a5ebc618b360ee35193fca317f9be66a3f0668141dac9774dfcf1b798d50706d223a758fe12d54e838c1546e950e5c4b7ca1beb7f26741a2884660f9b4f6bca2198945f0343e8f65247542a6438869e54c9ff907d8976f5d5c0b0b9848ee62990b4600fffae72e69ad98f67309a62186591a3aeb8595056c8f855505b5d7d90a1e560f4abe37692d4f601726d5ea58fcdc513eda2f7d6189c08ead9c15f7531c67fbb8b14e9ce5dfeb411aef14444158f579bb2668b8ad1de519d65e0e813aaf8c4b05d763bcc1bd00d4a9100a06544171a82a7496e4eb60a6ab6ff5f74d8420483030000000000000000000000000b4284f3e624a3eb35034ad24828f92b65025c7b43dcca8c76ad779299e2639e54c69dd12addf41c2e5d44c1cd6bf358df8c9927d465465a63ba00d1c008c539f98193e56eb94f5afb80e1632da73f097cd6557d127b87f4bd1e50f207f19a4e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bdfab69aa3b3f967f68421584195cbd388c0eea53b344a97f346707ea3755c4c7c5a49c3bc87df0114d376c3da6ddb375b8b4c211a97fbe19ff7e694304b9d07dc97c06e8810ad47c79d54d94cd05beacf7e10b9ca10bad260955151324f49df1daae261954febac55772fcec483f648283234f786e1c1238b93922487be8c110316f2e01697a6d63d8c5d39516ee461496b7fea072da5281ede34b9c4d17f63210326ed426db6413753d66631d4e8b762d929c8ae0034e637199d6a1c1715b658970a012d7c7a64a9908b7fba3359eacc6eb878645901eb2e56e7c01e28a91827712f3b15c80fe94cea8a2db9b7e504f59cf1f9f94a45c6e4ba09682cbe166cfdb724022c2fc650501d3537dcc9435bd4aca73079111bfc02986e3e935aef51316bd263031c23416faaf4482c4078b772d474bf6705a20e69a072aab0b4ee30d0489ef9f3021c5e8d9944cd5c284302b9f85b32557520ef8f1a88c90b20e2f5915a58efc1e8022a86ac7e942935eefcd54e50995a26a98012673a74cb9fdde899450e55149fe60313b2f588a00d75990c18c92506444ef52d9db52094fc313e8a7a0b04b9c980f0cc39a5c13e126185661f8114fb8a906e748528ac37da897d31943a794f527716667eb25399f12ee1e6611414d50d6750c494c8f804cc228010be5cb8ae8f7bbca1c9b9e78335c987e5fe17f3040ee82e3b8cf0994d7ce778c0cd2ed9d941563df5cbe1fc765d4ad3be58b39bcca2f46b2f6e9508a0d9d26cc1f394d5490d7aa967870af100af7b158f58c0c73bfc3d380cb83d928642af28eb03ff6620fd0b5a8cd2841b33a4be03e1dab52cd5cbb01bb01528dda9c279d1e4c00d0d8ceef7b9203daff9862cc9cb5cafdac988c1a9b4490e928b6a25f575d0950e9ac75407b106c4e1fd7b8b257581e301c422407e612c43be1794872cc82f02bbbd1ae839520f2116a3b85ca09a8b2774a7483f767048ead89263b51fd4455b538db02e2d2c36ce61adce8d33ed54d259968911c2a6e0ab53606eb18b768fd9b752b77929c5fb7b3e11f2821d24d39d944213e8afa3e9b0ae1a2cc97156da2bd1a526801e15637ca583cf6b4c597abaa7ab9805b7dab30274524a95d491c6a780761587dfc57a6b248ab0dfc6c0194fa2c7ffb526ce1138dad609ce19d8e5574bb0e37b6c78ae4ec3d856d88f706a84dea479d10883dd6ffa76b040c7d9d21c59ab0651314fe6755bd32a8fd597b74d91046b7e930a02b63259177ba633634b7fe0588471a5eea2458ef1494efd74d145598299b366bfb29f67bc9ac77753ed6ba54e7b1d300d3c6e084064caec4a8c241fa6d318c28b4838a98586688407b5dcf69eb1ad8ef055c46312b28bb59826aaf3020c0728ec737171643c82e29953ac6f5d0209fb7ade56b0206106b73eaa282543e398242bd62ebe9416de3071258bbfc76652abb9f22e6e92cc5800aba539b0d6a474563956ab9a1924bfe19bc15df4ab050273ba75668a93bfa30c725949ffad84f42c201eda5f1ad2b7a8bd0e7cf7fc1793dedbdf2bf0eabe03775e5214a1917ed8a20339cb70134e81b93cc6ab14f6c55783ff26d0f66aa0045030c547e55cdcd7df4ac70b1a1fd7d30aa39e0788fd6c6841adb85ab504e1442cc995365283034c97c856ffe64009700087f0a3f40298e83c6181c3c6af59d91e949a026cdb2de0f095a7410f8fc5891983c9fe9916393e3891e0ee2ed1d06bf0ecb5918ec6702e7d63242acbca7861b0e45eb9cdd1fdaca016a8ca3c96f4754501f14e752f37fe14c2cc749dbc0a25b4ff9b5a0630adfeb354d28f5d8a6324256f78724acf0d4004e5e6da20f45b8f6ddce00ae3504ddbde17a47270a7a613b4facafee221737f4323c7343d2757441f1ba1af440a697a361c5391bfc3bfad415bbc1deb095451d93aca4c33995900ea362e116ca40fa5836720d56bf91d63b633a40de2eb64a4507ba12274a81b1346ddc2b7a7a643f91b43f4f2bf10002434df4e906d5b539393c587644b186bfee5cd05b7d3aba6165c49f4b194f0bfbe8db9a52fce3a19cc50de40337591ee5707518dadbe4ac5301e270f400e1ee6a9e153194f28f934ffba236bccbe57927cf49d1312462b27c403f538bdccc77fe910d0ebd4ce795ea1182d6a0a4baf8da8e7ed57649d2335578921bd7b2897d37d9dbcaa22693bf5a222b86e97bb3adbef524e337b49a046e33b826256940d13a0fba69469940329516aa3a5af4c592e7db685bb38a4c38b55130b5ed58c06a70c19f05d1e00dceca0f9029fdeb9830a68ad5c45ecdf1d7f0fced594942b033dd28b8c626412f86c070239a19ebb85c91981053ff58f155ee265d766e16f61f720fde61c2c852a9c4e675752bc91a8d64241afdf440b19589aa36bb5bf8ba884b56a73659c4e0208cd22600b", "00ac63ac52", 0, 1260068403, 1537743641, "4e227597f48b170274c41a1a20f4195bfafd24531fb400eedf4ce09cdaed9f4f"], + ["030000807082c40304eb11fce7088f604ed9851fb6c9d519e130fdd647c8849fa8b619d1088d8e412401000000026500ffffffff893cd3b1270063f0aaab44d3545a304ca208b78d2b35deedcfa0cf6d4aa87eec0200000002ac652cfda3b1c76c08255cbc4f89796c58cfa6398cd34830c4b9e324b84e2f007b42b14865980000000002636ab3868e1dab477a69d8b0d09737beeb0a293a6621161dc34956ca4e9e535487908d361ebe0000000008ac52525151acac52ffffffff038aebf8040000000004530065005932910500000000095300636a51636352ac8269a203000000000353520000000000c97ed666010000000000000000b880ca01000000002048f2e88d7071d211045beb4f9851927373c9762534e6ccaa627605aa6d5cf03adad49cc6026722c4d93cb6fcc4f18ac5f29092037d028a366341cb734adfa03fa043982edd846217f46c9e72471c1cd7bd8f7abe81c716102d40bc0e4e61b100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000430d9438239318cca110cbd5d91142b8cd4b5bb4141670c9a303ca9320e38305a382bb8cfb25cf5579a8952dbc222d8408514d2ca989f7a92c81e8b6e88d585e841b0d7a544172190c3ea681452ad96dd437fe396f1b7e9097d577376ddd60aa268c9a4193b4098c4ce10bb3c973048505143aa224f01ca983e4ee5b190a3af4030145702a5d36a081eabaa24d09e2a8d1b38151faeaf448ae47e9d47f20468cd202275fb5eafc6226a9ff7f89ae75ff8a3889b6ad6332b11c3e8aa70dffc914186d0b07f1c1fe057fec1d401b25264400521f3cafab7973124d2f37ffa51a10d4cb630db057dfc4be46b9661bd46b58ba73e06b08b4301ad45d54044284d43f0c30750203ab89eed50f7ff137d1dd6edef9cab545463f672a024a5694eca610235be8d202264208ab38eed22f8c6e720c3d81184eb059d9eb2a51c5f445a7108814411f510228f90e14729f28fd7c6dfae647eb6e7ba39cf41c8cc1df69c9740159ebf3b354030e25bd7b4dc4f2f789fd8bfe30c5f988fc3ab9b29e34cdb148872533edfdf2580225378f04ad4130b64605c647f1eda41467c4cae51a94989c861a82b21f8b334bf1735bfa383bc46b7db6552be261b4d16449ea9d4c37952f96e56faa77fa66612fd0f96bb9523b8a7dee197fb6a35cb228ed79cce0d0e5fcc36a3184644887bc75d33f76dcfb77805b51c77edafba5996b4e98ff43f5fd2924253eca3b4ba111f714e3012f20ed3e2e33ffea98a2f6aadf3d5edf3c637569e4645166f2344b6ec5f104d379a738683ff9b9b9b60ed899e57f503979dc6a5cae863f2ac59ab5a806f03d17c242bd8201b1abc8d688ed1240be08c91fd50b7a286ca44b97992b82f15636db85bfb9c84c43c9f5f7a6f95970ecfae7755b07115955b2fcd1479425c9e109d7de525b071dcdd5b30c706ff3b494b9105c4d7a062e770e670b2278a12d3024d5592016ad7fb76197a9227b131bbdcb2dc8d25422283f975e7e465ec950f828d52b8f52815364fbc531daca42268b61b84d656fc76856805fa3cdb5ad339f8a45f03a3cc07885f4bef7ca9a6a76ce6fbba01de6789c2c45f27f9908bf2def0a8476f359944f3e109b30569f29d87f12de4cc6337a580b64ff4720e58785f06586cd75a08ba6419c6588ebdf7696a099a94c2fb9bb27481c3e15e7b57579444dce9027fdaecc3ae12b5615b03c9c3e9e9ac04d0c09fa812cb523996c7cc991df0c5907d932ce410f5ab1331986661cb413867b398d3aad720296c970b96b983759cad28025275ae1641ad158c486aee4c50f3873c2a72e46cba3a95035522b4acc877860c185985258c4cc003256c6681379f792a2d603b84ce12df85a89541c80de9805c9d43fc05b11c4edfe41fce314bd2406686d4ae6b305cbd83f96120f6ddeb3f367ea3a8c0e337a250a59248d23bb565dadb8f736277afb060a459b6215e48c1b75cdfc5a8c7a16aeaa6841b10f326b0b9015aa6aed851494f3b0733a5bec4ecb39b69e7a34c5955b2e90966a8925e5c9c6585a7f475f2eb11b6b8128808c7ad8304b93e7bc7a51a337a0c0a1c518dbfacd2cf3811e807b1fe041af54af2a4fa2d2e61515411456e4d3399c9b246ddaf8db590b58efb24bf9ca443573c41e1b9770bc877b381fa00052221c78ff525ca53082fce737b1e1b42678e1a78ba58e41631f44b69e8b878f3068da10ff4024fa8471337b0c0131a6392d10fa1ab562008cb190c00c5da7d327beeb3a6e778c348f66dd9a5e6932630149b5da6dce78905b7efde5191b8e2984efd54fa6485a8995b47cf98a11fc08b89901333fd5ef3b4ef4f10f8af619d71c54394be9c459763db32f1b9e145e4b94032e0f68ce8ab3c2bd8b8b66c8f6484059afad0559aa2e0c3dab99331b011816eaf6c4e78cd8618812497bd0b87fd9f85c2ad6371bae7a5cf3d352e2f4e53100104f99651c3c539b93ee1675edaba1be432598545b6503316a245c751d3398448fd2c25f0c64263d81b0457561979e3e76788f7a49d0a9375db8955a71c642ddb2a4960b6eadf9cdc35dab9955bbdf08409ab29e2301efe091e59b7d81d98b56ce0d5eec9e2dc468ced2225a88de6ca753b57c257a1ed04df5d77b8feb19d4b54ae6146703ad3f30f3d876f0eee82b298f6ea260fcabd231d035d086cc7679a69679d9b0e6f0b3f362e6e05e78f59d2e4c3009ddf5e78cf4b7a736eec2ef3bb456026cb669eef97c7b6bbe8298235a262c412aa0a85e414f4a3875239e92873cf408a3083a4f4fea13a5d2cc1e670d374d963c85270397646570d966ff8ce64249908d7097d126687cf8bc6e2c188e09397ab2322be0ebd83cb729e6a362c75242537834748ef04fbd4c2936a3b837a408579f5fcae5596f038b56b609a911ab42d89f8f77b66dc75c5ee36ce7f14370d600", "5200535253", 3, 53398968, 0, "4185196f4086ebfb114b56ee364570361438af19baa7b6cbafb886bb41d979d6"], + ["030000807082c403049e5588c7ecc09d34bf0562cb4542cf2a9e196498a6e6fcde94de7aeaa515254a020000000865516563535300acffffffff8e8f39171b5ffa2b4116bb87683ad34bc8c40a5d9fe709ff2b81e2480d7861e1010000000300acac32df8622ae8948d79a3c5d12a3b129185d451759370ab03ea188760ed296c9a089c0a1780000000006655252ac0052ffffffff487587f92787b5661bba83339a48ea93a5c1d1c77c8bae388ac157146eabb62d01000000056565656aacea09609f01f251a8050000000003ac51533753cfb8a556886e03c70c8f05000000000000000000000000cb3590f7f10d77db2566921c6631471bb63a47e65a23d2fb44b3e3cbdbdc8b5a441677685b645b76073f5f2af5ae4e2834c175c0c6cc038f43c4e4c33251e5d7b43c2f5c1c8c0211e246f89df694129243bef733f33e39c2cd95011a6b0b7cf000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c855a474c1b46f50ec509c8d065071d228c7d73ae1f48fd30af60ed542d4ab608987ec60f3462d37e4b9a0cd1f420279b0754164f0462ef90f3386a388463677b08396c5415fe27b14707704f860625cc25c274c669304a0726da9a7da1a7f5947ae6b5713c8e474a34e725b915c67fcf84aa9f0958063f988dae8dbdb73d34c031e4f97073ba052961d695b3ce5985065ec6166356d4d56b5b1d367a7eda144f90216d78c4b10984c74bd46dc5ca0dd571ade381e508fa46427981d5f4a4263ea340a01e0e72dbeb3b51284d6f4a911ea53b7ee0cc6a72d72653ff1203f862e12f17b1c369cc3705671099a7a3625de4ab22d2e60e5baee763dc058fee359f143c03102156b2aafb779e6f59191b9f04552d427531e46a8419e931a1bb4c67817b3334e022111e01e1245fa621272c8c72ee73b29daa3d2dc32f3837f41ca2a52a0a0cbe30226ccb6777e743dcc2f681b8e1570daf0629ce67e6b0df642a2558ae890620534031d6c86b806bbaabb51914f87a54b0a8b5d94f124609065298890351ff0751fa2032abb5d9bf4ae139de11648b3a1e5fa7f14380180da87cbb0c6d715e9fb526998d7e4adaa3b1692ba2a7b866edc16ad8ecf62c449bfc62285c8c83893e102f8ec869a49c623373d379f61f3ce53ad6f9fc92987d93aa6422da9bf803b675cfa64e7c7bf87d661d07163a417cd597ab206d3410d42ebc82b3d019d676db318d3e1eabd7c21adbd89c62c69eb19fc6b21fccf1a326af175546cea6942231fc469c6b11b3e47ec58bf44f20d8efb69e6c18b177a633e0dae0c4d9b4e568100705c76a1e05b17e415f16494fbc3a8505bd64a0a5c1ebe98d7882d2f53347e8e0598e79fcdb0e8dec2f1b15f36243208c4d2a3d8fee01e73dfe7d6394109663b40c58e1e387621b5ffc48fee473eac6e6b86a01631714347c0d4fea535744104a6a45be26f4efb4d09cb3931fba2d3b3af5ae87c53470b0f4972cfcce75af3b1df785a3037ed8fa83737dff4923a51b955b811ab6cc2670fba929fcb3378b89b3d66f2d82169dbac7bb0b1ae52e72b8d89e31df552ce98ddf52bf748ed0ce6b52ae201ab7923ce6f51de69b961e4c6a1d0c24b732d8e958d674486b969703ab9d25e2233b78ba46811e733510d38fbe6997da27467b44565813530b2522f91a384bfff9adb686ed8ecdc6a1e35f714996a0a143f991b4e2e5a542156dbe4a50df3875d5f86724efceaef3e027f35f02530683d020d653e88bb26c2210df0634c648ad11657f99677475901bb4e2dde211a7dfa93525d189ac14e8817e27d5b4de966a0292168773b2a84f9d3b43f034303482594a58483e4e7ebbe2827cd981d693b60f222ffc53f2193656f784c65f0255783bc664bcae82c50241c091c24efd97de09160c8662972330bcdb6d2b202e0d31a50b4ced953eeec1e6af16d7e804afa91637db33de76359ea23624ef1628e0f57930a49c9b8f83bbf039ba8e84ecf1f6a4b4087258aaf1196e8041fb8abd7768c474e3ee9d8ad542fe411a671ddbd7971c745b99880839598273ad9f1116e058b0e06557901db41667d60b9e016716122fe5cd2705a3751b050bc125e0c444395d47ea682b88a7389cae9d94350e4e5d7404c1119ae346ef5c6055c1ccaa1f84c08765652a347096d8a5210c9be9f5816261a3e1b1b2b873e8b7eca013cc8c8174441709517ceef2b674ea1ee2ba1b1923903926a14bb2377bf28fae936962a5289fe3cf5942f7df828f9987d89b32e72f1fbc03c908a4b284a3b2e3e50c9ea2c0599e175150c607df1ff80c99c7e5a5e9f7bfd3ae9d803a13c48bc8eca22fd9d6798697c3fa3ce6b4d9d82c5026e810a100bd80ae3979d34f31408e2f4a9b28196aa3bd800c221bef798fe4f593d3e6b5dbefde230e3247647623596b5aafaa4108115ab5af892872f49af5eecf5acdfe8f5e8b6ab8f0beb318b07ab64b297ff2ad642862283af81bb07a54263e49e01dab42947eb4e5666f138d6ef4dc1b63ba413b5d85dc6b1ae73c2e684718f1d61e7a65002eba3b5d446753934124e117dcb08560d60cbe7cb2cba0503a7875723c13ffff6458655df110a2963254811b32029580225868805f63b93aaf9383827225053ea786965687321f472543ee6066e41152265ba92924402fabe46adf428d618f28609dba3ac4f7abd6240ba467c4c7a60c37854c1031545b85e34c8e6aa19e410b7df054932c346e777f37cfd87ef8ff90e45db38fab4399185ba8e303fa16fa8abed24cb004221b996fa925759e99326fb3b00000000000000000000000000e414dbbf62a0461b8467768a169e8ee35b2dc1876d87e43b49bac187abf0c0eff23671337b787cdea6ca360f68839947fa69cd925b320cd129074536bf857f63b4c942dab8ee3c7b6e3399ec96134e452c91998a33b5fd58926381d2bde1cb210000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000059c72892797e28e1948a9076d49718b4c17e0ed99d550b3d7f8289e4e6efe67c8b0693b8f20db16fe28a50a57275686c234a8d1f55dcee5f78e836f6ee4521ac9fbaf82776bb98df9814f5bdd9d9cbaefb3a189491d15cac70a75bfc0bb1a9d55815688f404b62660c4f86b489429eee990f22f1845da4f7c88abdb8b92b0bfd021460deeeafbf7547bb9dd260a5822bdbdbfebeed40d0cc3309a924b4671fe0ee031e78becaa180c233d91d04efe33029f28290b54cfd23cdc0b56cb830c453c5b40a0652fb167a26a5a06d082674126512244df0aee5d2a42c3dc893136520af0469a1acff89f208033d68546dcc29124945e627a34dbafef3abe98bf2f26075a3a8020db71b99d4f3479a8f1f46338cbfc268e341e740bc24c99eaa643431898354270205992995ee6f7815671753324dfe5c6142ca8dee8d28459d20928ddf1d25a413031fdae646103b6bc61c75dfaccf47d73a057b5fab965248ca5c8093fbaf6d18de032eee89ba74add508bbffe3ed6f76f1022f8affd33f2d90f89943cee420f9871c030cab86878c6f6fd9b0f5526af9938702bd3f776dc07f084ffc3d7eda8125170828e0428d50277323c7529d740a92e76cfbb3da1bd88503d5259f58bd14a160cc1ed2406617ba23cb0f9c0185344360ca280ee2e29ceb9aa025091cfa704b44a49b7a1bb3ab4067eb58761b99ba915dc9f23084ea760b63a6b677f5148985c10d6ac120124d423b06537f7c5495122c6904ca5d71006b9e0ee831f639d11c13befce0fa3713cf63a42afa8d8e5e4ee70a63321344572e0d4f38969ef40b22dbcb005ec55f15ba77aa9885be699f658a781a559f4ea4d610c4af05a4686b0d3c4603f62b8a4f8b68a56818314cae3bcf4cbc3a3cf0750fa77ca714d897039e5340545f6eda826a8d9e5c825817568aa65158003eb4583a63db46e7ec566e2a62955c073d5061e7c44ab8e12e04c47860d906e49f0faa62dbafe8ffa151a166a93bf6ad3f53eefc13d0c429bbd11895c80f2ddfded62d5a3460914ea9569b796498cad3daeb67af1e3f6e6764ffbc03f9a3a20acecbffcdf3ac2553a8a51d1d7536ea3e9c2b6ff9a01cdef17ce06bb57414b08506b1c68c30e0e9e412df564f299df9dc37cc6b0d6470ab08d329a4b24342bb4f9f2371605039145407fa057bf541c2e06a0f76ca52dcd20ec127f06355e1e9a15a406ba618a9c09f9ad0c2efc20582840723ecf6f7636034f768bb747e8007fabf1f117de91079541d10fcd0a0cea93b41244122298e064c09f13d5cec4fcadb503f94d0b7f69ebc585ee9627a755c1fc6bcc5fa986a05f298a62764a6d881158b30ef59150db2d5a674bf923701bd5055ca60fa138388e256d5432d275e97f906e91789d708fd8d58acb3e9c1eddda081979e8d321ace095d0a49f58d10b1fa61a74452dc3a96024d37b5db5012101617f15bc1c664f1ca9d2854fe9fafdd27a1d7159fbea558ffc9b01a6640783af54cb23c0a022ca8b8d56caa73aede85445bc620474a7a00fac7179a08ed6ea16112ca1d925c3b4000e33b5873d9b25edc352245bba99cc799a38fd1f0b510219605aa3740f70dd088eb0386548429f2005bf7c74bd4c51ed3b512eb459e54735557662bad28c7c5234fd85c98da2b51282686b68a999f33e96a9fc6f94ffa0c19e544e111d01efb44c89061a5184d8e0132e1810c70f73f86e00021e6f7ac09daeecb1b7e73aec848e0e88db80724007e6e422d58e08e0bd0a4f7f323e4be48c27304d1404534b2c135b4962028d2ad783c30765e3ee464ef611780384b497f61a281c176681149c738df5c376333f016ce6b6df84655a8deab788d92ab8cbb5f090e3d591286e21f4ff93c374ee9f76d384e4c7d8b4f432c92a4ee77c88fe0e1752dc790dfda3cd84b6bddeb7fa96e363bd856446612ad64c743df8e9f046cbcb874323ec8e184495ca59086718d00bf4ef5fe78e60a2927b53dda80a0b4f641a1b8aeab6df02fe79c99d23a8557308be02910ddeb2f5abf35cdf57d0f230eb15d0f91af2b1fed10e00acfc604ffb069fa7b1f88b0dd8851e8c992430867ac7d63ae639cc599aa5891e7144e4da7bcd307ee907560b251eb2bf8f5a949cae961e6d0ad656739b67af9890e410cc5f523c4f40461430671d40916263bee990bd863db9b5929d367c8a902a12e90adf5fbff44ef022331c4b79e5b3077bd7f6e33d505928bdf8840992045f879597907fbad62acf9f03272dd5b3f95033430dc565a8343c5336ebee77d491ac44896fe378112bb02000000000000000000000000db7980ab21d73019cefd4c7e43461e2fae9a356b3fd9d4042b715002046388c751203170354722ff271f315ebc5626f6e30dcc390ca9a863ec9d99e75b0de89535fce20c80ff463b64617da568612ff993fbfe0f2581499a0c6bccaf4a6e4e8a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006c937a52b66913e7a66a0519fb02650aa45ed5ce5c6a3aab3722ca7e0b5658c7ddb4633172dc40289b4845233262a14d686e67fc0a7805278723c8a22ef6c803920da18e1e3c1016c78dc4a7c7f62917c3d26aa483287e8f3d5bbb339de04fdcb4eebe9246ecb92f1dcffe6977e476f2797e635acb0659c6d45a9f41902bb6730307f383079729d7c5ce9838bcc65f397ce509a77885753d0d45b6a260cb152c47021fc46f3adefa4eba24b814da8f5af6fc1e2735aa50f42e3169806a94472310cb0b078605aa88cbd5b2818b8fc9caef0d9a4e09bace648802b689c15643deccba8325a50bf29e8688d69f68513ad982365ce14e7893f1d8cdf970f9dbd65ee5ea13021ff7cdddc8e954d022260f73b9a6ed41b987adcf177d9b9b3d11683f370a163b03082e342dc23d8df77e5b7a03f5792b68e39dc4c6ff5529de5e05ca8c3f4402de0301be2029b0d0a223c931fa5bb17d2ec9cd2f8fdb4ba74d4f62e0559385373091022a1757f74900b0793d80b2e22d92f1245cb4784a78f89f3c51a85bf514467a9b030b4169460b1a3491280bb568e775e67c1bc37bd7bde8e62c60a174abbb57bf07a819e5a3f57931ed97256b5f84881ea3453dfe5b00596d4e16a72adc394393370fbb9c663363a096eccba7dd3989f149cc154633f248e7eb6b561d0fdad6cd732e3ce4e9eb57d8265b7782472ca83e8651d06a616a569067b5326765cd8c8cb184e4ab202d8b2cfe1b0fcb80f493b8ba0b08fc488eaf5e5245d1d858be0d80138eb3a524733725fb234b15fce5177a616c63defae5d5717bc6213079dff4cbaa4ae03f5e55e4f5aa4072fb045951977dc28555f70f5a4916444100c2d5286a62946e0e6324b59f6f3b3943258310141c1bab9acdf4137428266e4ba5eb5b89301b01ef50e05f8fcbc2f7858a4475232d81777e5c242ea44a46f4eb864ba425d655d4fde2257ee93784c07cba203b670d51b2d72dca72af0bf372be3d4e8c56bddf0233c783fe773287744781750b17c600bfc601eea9f3155b63b0d838fb32d4b963f00405cfa6ef7b48450257bea8fa0d560f9e79a5d535b333f0e0ed9096ef9d3e6ad95c73154c4c1c41f457f7e22e03219c9ce7e98b2bf2660935f7b11ef1a35b286251aec580d492da5899db1370a224f450d9127022f415f19d17c78a4bfef1ebab11ede436a30fbd7db0580e62c716deb156b32c3fa3303e04c37bfa747ec7098638aaf11add5dea835ef416d5160c4546760ce959c8f7a1057915a3248efd49589898bf0f45a4013c31981130554e013d69181e046e47be56d800ebdc553ab51d9141150907eb59514bbf4e94b1b0521ecf9b0d641c57de0016844dceff13f137c980f6084262cae2779311f107b830734de3dbc08386af1a3dcec5e42443cfddddf55e466fbce6b60ea5e85b72f68f79c49d1d3d1bcbabaf2226ec7a9a575bb182de97b5af9c94767759cb8fa40a9878e4cdb43bb26b769e2df13a4e78e5e6fd3b6448fae32ee8c339c6e53f92763740f39b6077238733d78c0af97dd5419232a98a89fe1a6ca3b47b7c8917751cab16763797d498eec0def42bdb4c14d0e6c4f53ba237da34ee8dad9913197463295d68a1728f46edaee1fd4ddab99eb389079f038ccb55af97ce0ec176fb3be58246f9926b4732f9b703fca26f7d44a9fa8b98cda902302dfb2dfbef0b773198a2b08a26aaee13cac42ec3322d029d018e39f3516d2e4a2100aef4f9e01308704896dffae8f9c98a0c6cac8be7cc9db33acc1e145f8de17af39970923f27d9fbafe7e719a0f4b59d12507f76b324f58f341f38c9e73af528e6ed7f40666f1986978aed26bc56d64974870f26213e93ca4d54492ca25946fbb0c284ede8c4e6f86a32536fd833d3a7b117ae708971960a32a7deb8dbab024c0e1f8bed43698cebc6706ac3175e2d5344e8f33f30ee2c9bb1d3d90874762c6f037a383cf240a7faa293235cdf41dcb072184fb6bd786d8e81a209a81421d7aad6fc70c4d1d98df8864ca485689e4455b0c80a74f3ad312ece23149bb7d4676cfaafa4fcce3330bc3218a29b4ec022e0e02a37b4aef26a519a72a82c9753a546e17d306acb1d8e77e8d9883150b884474a8e4b68c5e29697a86846b9fd3c1fa685344c60b98a40048153274a326a0169bbf28edabf049c17a2fb5a9441865ff79c6eed2fd5d1c766b61918580ced537c2c6dc2d30dbd068876a3f0222e066ad1d43e2e1e35868ff6a7537aabc2b6c165a0ad2eaaee983d182c429c2adf5a270b1525195f8aae6ee9a40ee86d40f1a1ca89932ea6b829d9055409c75e62b519cddadd30a6fa976228b1bbb30585489998b258ba0b1341648b8638114e26d0904444d62f5da9afd0d666628bf556cd326f6e2e5e7291e0293cb6a2b31f01582dec4a624d743ccc1f02", "ac52535153ac51", 3, 7037365, 1537743641, "13c623ea1771d30176d20f4c18ed449ac6fb3600b777228fc80c7962f63fd480"], + ["12d6ae63012449c2bebb6bb3bf82502a0eb6ccc9225bbb25ca744cafce4f403e8a585c728301000000070052525300ac63ffffffff030e79c60300000000096a6a6a6a6a5300516a7a8221030000000009516aac516351525253974b8100000000000553636353ace5b208f002c8d7a401000000000000000000000000c44bf5a1cf240428bc6a9a3a91d01669e3d28495a7b61ff26db6f95722f403942ae1c0512c110abf1c5032fd0ad36fe16c69cf152f94e6a440f03fdab2d3565af66d7185d016cb0d8e4a174381c0fa998f0048eb72f2717b57e7030a3ec6aeef0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013eed741f5745d6cf35ecbf3bc6114108cd4cfd95e022dd0011c6d02d32ebf556174398d1174b981563f60222d74869d0fcfed128e87910ceaccbf33fd4566c28b380e7fb78047b0bbb0986ff3a9b4a965698ac12c12efa5425665a73d658d1fdf580d20d95084c199328a8d0dfd0610f7c75c68fc8b0bf706dcb964a9eb80a4031bc68964476e1388aeecb9000f9a3ed0d586aaf970e5ad1cebce33b2fae3b53602262e228cdc5af9c23c976e65e1e22f44214d5aa1cef71af34418a968646dac1b0a07cf4185dc4d07c4f01e20f944c6d9b2154fe89b982301bcb1453a1ad173ef394739cb9ef6da4dae15ca9a55b40f75991e35970c4f0bf358e31de7ea5e9b179303290a01e3acae9f8e56f644b83d0479ddc5b44f6c194df382b8917b82eabd0724022feb9a3c8396539cc8fa5581d3d2ac03b8bc107a5b04fb9c24ee545d8d63a38d0312cf5edead3ad3b0ae2d10c814b21d38d2d18951d288a42424df892237efbb320201cd5924a7d478738af4eb788f9cad11e829c940f1f8d879730b713eab0ec1d9031623c35b6ad752eb10054967ffb86df9e7dd20917874d6804e5384a480fad33d01cdcbbc7cff3db3111e8af3645c5bac7d8c296c612a0ba4eaac9517a61753d4170f49e002d773b712e93f6c56db2c708f72124d5f789edfef501de296455033a4dfc7e7073690ac146c563c6c8c16690c0ccf14e3a28476665f44b8230d82579f9a7d85ff64c3a8af3d2e9ec78c139adb27e5ea4b0e347d679ecf31c9644d3f42613aae261fc080b87326bf15649032251f4b2875530053ead60e393a3050b1a69375cb73b608357ec46301e59ab8a34c0710bb0d989733b96a74446a3f5021bab8c00f57f2f768cdd940d6e685eee8f134f42534b63c02d59b213d92ea38ce47d327e268b29cfe52484ef1a07c7bd69187356c100bdc01404b40f92eb6e42e05f87be2e383de2087f33ff8d4a351abd195cae7e9924811c02540622ea08835220a4739d1468f32ec0064c8e232b79b38151bbcfb12c355beab7a4dfe98ce3cd38f5a159bf3bd2dd104b5c9c7251fa4afcad85edf25c99f26c11e4f762c7d3b8f4761ebe3dd498dda7a0328894f77d7fc81330579469b5eea9dd303f0ee711e96b7b8fd7b620590707a2759d354a00ed38d8c30d8159bc7b529129781c9bf9dffeaa092889b1dde2b0ad9d47d96b560a779465cbd4ba7fedfb0d9164844ad6c6a442acb3516a83d3aee9c197abebdf6be3c6a33621fee8d6dccaa0a9119fdffd7b3791fb1dc457955f51b5fd80024711afc5e0d97af8277938d00e6b5049a90e3ebac241136cbef0871ec87f6f0c26f1a4b51c549ebe9f4903ac3f6c4d108098f85ede90e19f47c1969294c0314326c61708cd446b827efdb22a6421ee6d9c72be1e6bbad57b9d75ec056e64c8b0781b0b39087c6f6d68c88b0e148c40c9030966fe48311dbef81e0bb1eec2e59be68ea37b1381b9d810efedb029bc5dc5df80165465c249252a8531a9ad9bd33342bb7822457ec9e62f985020f355ac4b8911d761b68484b51f5d0ff2cbb59723f112634684a471bc9e72b2ef50ddd8a4e2e0283336093e1e14378e44de6bf6c9ff25be35abc46bef789e057ba89dc5cc134b26a99805c0a79b6d0f6d0c07d3b025087d8e8ac60f4055fbbd7d9832d493ac0e3340986ec07aee807cd0175a7ba0f0d5d5d0115a9a25b5814021100fad1ac2db11f71a751ec979163c45a8368483377aa6331f8283c229ee036911a4e1ec8af5ed0090ab6c5d8a5b694e860460d480e1d1091f117d3e566401cd445341cb262eb77969b4d15a7b8fdafc87beb43750d1140a863fd5414fe101eb97af20a78e9e66598a4740bcebe5c42049fa9f929294dcad4bcc3f0d68880daa3c14ab916b070452425d7d12e77ca1906d27dc6a8c7aaaa90e22ea743ac2fc19a9154b033420deadc68efbc685d70a61a1fe51881f7e4382e34cb8248117f74445669195a58e0bee83874568a4cbfbccc5f268f15c41a89b98ef68ef75fd2ae913ae605ab338b9df864bb5309f0dc1121d43fb994a5431ad4844e7dc989b7b6f876d533bae63ef5ce096929e52bf3d70e6b839e3b87db0cea7fcaccf5b63a6b36282277746112585c9c9decdb3e6f647cf839ed1a121181ebcaa8025c26513b86daa16c6ded49f3f64bef9345bf3a5df42e0c7028ca033bbd68c97930559508cba589c95dd8b56d856b7bdafcba351d5a2c0417af60c9fc8744539e38b47a07f32591c180bd8f7270577fd86c04084847a38a306a5579c471b5010000000000000000000000000052c5fef9f9f557dad6e8b102e3542b6fe700e9e5ce1f3d9665babaaf3ab34a1b858b25cff06698b4f496c17439cd2aeb34c4ca96cff0878ee6a0ed61613b3f06285823282b77621deea7c9797dac0c49496408f793a57aad8f912e7be9ebd61e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b5ce2d0b5ee13f8542c21d47ed6b5f12e3afb91794a83e306f9d16df0edce92626ece9903816dd5b4314e0c42589d7ca4256c19743ad98eb72e644b06c21e247a9b0f30ad9cbd2eefc7ffd7532fc30a644d127847707ddb21a7b4f0780460a8470d132a4d93f931d3b3d6566ed44ce84693e17c43bdfe8b786f84cb6bbbf450a0316c6ebd2e793ee3ee3317b3058631730495c10e767085d74e3c02c6ce400b9e60218ecb269cb835d02cb392e8351db87d733a529c92bc0d6cc12a470b49e407a520b077b9c181f7974f1f918d72aebd424101a9854d6c643b0fe7ab5dbec80a9ec0cc94bec0378d8d3e593ac3e1c159c7f4700c1beeb7ec3bc6e54b3482be1796b9e032a558122fa35cb2c6e94214f6ae66a90e75365c7c56c7d90daa4f3c2c43ac352032a5f33a86171e5010234db267a5ad32dc58690135166a66496165a9a2ff45fce022b75e5add021a810b2c8d78eef58b26726453c2445af62acf77850b85fb4f4b3022399976e458e5268295f506994bb829b3c1c73646224a5d313f756d442020740030e293c07c1d55d4fe4fd972a2b7dc3e4f323df0ca6a73a660385cb149d43ae385840cde8e2f93f9dd57c59222ba4032464779e00fc8ac9f0ac1e825b7ed87b3c7f6cfd18e13d18297768595f671ee46780ddfff4f73405c8cd751e938d3f6d3c227f34c1d02ba486ea77f112b44d0858c3c72c59aee5a1db5c9fd16414d0d9f505c6a0b816301169585d2e4b0ff0c56ef6dcc882748b9881d42d67d6a12da73c9fd408602ca7ee4b3fbf64cc1912290d2b6dbafc91aa920150a1b6d6128f60d1dd2a2346fe171f81da397d61d6358fb2d3f373d1f6443d0f201ef2c1e75d63839134b0a26ce52a34cc26d72b8d59ba3ddf12f75c01125e16a4bdf848e89f7d5d5c000ad3238ef7e14f17167c6f22cad62d3289c06babb696276f992fbbda775ce4d78c1c011275cc263d52ab76c2b57b1f0dc6fc75e165778a3ee79ac1bf8b30db6feec5cc44b005b669175497a80e213fc7c19f7a1be1c59e962bac888f4912c0918d06c24cd9e3b2582177bb944df440e1b8f188a8bc742b50045d5556cea70e4ebdb26123e6b04403cc5d4964f1cfdb9e9b72b6bb04ead82efe16e07be8a454c7b0b375e0840debc5292355a4fb94da0b12e0873a4f319ad5656de6b7528192e45f7fe904f1010c9c6e49fec62f502bacceb64dfebff46151548378dabc20bb82558c48e0ea0a4756049b4f94db703d193e08ef358b8ce805b13ea9b6f0c18f587fd12af59fcfbf725619f3ba45129b135e20a1ca9035e7fa1ff7e61b2cb02124ba73d961ba19af652d32ecaafbf5c4f4686176cffbc6b8ff8bce07aa0bd5d9ace0ffc5fce06624542920d5d1d7f178f11dd8cc3d5756b6ffb10d2668a892deb25a48c6d455489edc3af6cb6fab3c3b87414e391e2fc2a799e621fa9b8b6e45a329169967ed5407348eba46754bb53daf51fc8ed841246c2417d53c7386c533c8be85cbc9ac00935c63b3cb821c382eda3d7269f22f02d7f952ca6ce24a1169f876b115c6367b0fbae4f08bd27ee9fcc75b939c4d3c758f119ae19768936e44075c274513518c6721c158d8581cdf5dff534d39c72a6c707c439bfcafd3d3e0fd46911f0e2168af4159c922b7cd8bc53030b3aa1221f9318462ab92ee8530d2617026d22027de476138ed5c82c3332e5356ec36f20847b38dca90dad34bdf22d5bc12e7b6d1bcef25436f349f3281c9408161295710dcf03e8dfd76018fdbbe14d2a62301f25fb79635f547bd315e191cccaa65f34c28f40bbe4726fd9429f92f6277ae93710d0c31f6414ff1ccb331e76bac53badeae7e62bae648f5362fcd47b0733cfb8e776d4a965a64973b2288e34b2446f46ace967e857b53e7f1602636bea3db77568fb7102634d188806b4a35dd3db71d05bd4a99f78ec19f3a9092a3b16b58facd0f71b706e5466cba8e04848558e1594401da1193b2635b682404bd2d2efb1a555a3e086ff1327c46d4901ff69ef4b5d138a5526cc840ac4db61b80d9b0c7dfeffda718f761a1ea5b1691a746145b5c97f65af49e4eb90c8fb2f7409b1766f897b43509ee4a6ae7409bf540a5bd278efa9ff14289e4243de1856efb699823abfc67042847a31b70bfe342b0810dd28122fcfc857212906c2559302bebc05e78b1f91bfb2a2c66a065d6db85757013f7399966174d11963a0e1bc3fab856955d608006117256b1b2801690ec6af2c222831d241aa8e1bea0e9f620530761a067fbb2c7ff620f6ffc8097be661e3d43b581ec52010629456522ebf19dbea55d6750ba88b7d77be8b3ba297147719bb19e88c1d95c805e622ec615c492922b01b6955460974c346f7d15d363209cede1a69c61fd0294e255a24fe9eeba0920f2159b169c8fa5a0fde8976fc908", "53", 0, -392275141, 1537743641, "93d61de9f2370403b88565fb1c691b8c3b481b210f89654301a2259bbddebd8d"], + ["030000807082c40304d7c8b42d1177cf2cd95b2418a0998814e111dc760cadcf7d821d614a8d3055a00200000000f7c69212e9a8a99733bf464caf228ffaa0987873bf7a895cb7bf05ef585aaa531ec2c7f70100000001acffffffff639e49d1831c27d6a88a95b861ff1b8c41f00584adfa189a257ccbeb26d40a440300000000ffffffff42885f5f08cc96a90766c915ec4181d8856953602e40e814ce9a7cc52282a60401000000086a6a6a0063655353d8838d880188590a02000000000900526553ac51636a52000000006efd20c6025b15d40500000000000000000000000006acf58fc4ccdfe6af0adedb7abb30984d1de291a945540508a35d410c11601ebf43f49533758156e6c1417023d8e14d064bf22166d9d0481e17e7d0d9b7025688427a3d5d2ac82530daa66db203058f02d7f67f5050982708384495d0550b4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a5b72048d64ce5db8ef86c2fc901101a0dc7f3a7a0cee4295d1ef808b02e31c0f27ea72fde68b7c30eb5fd1c12bd2d98bceaf02f71b00f725dc0ca7e414273b608902a5ff10191a195cb255d0c9e535110a22b4deaa97c3c40ff16d5ec4cf52f823d6698e6f2e4500865a624c5bd6198780a57b73cb96059d89075ab623d46bc030e707cb56788cc214bed666d956e242a08e51f20bc91eeb85493a4f2ba54d98c0320754552cf7911016a5b3c912f0126098094e62dd941c198ddc4efbb78f39a370a08496b437bdbbcc6129696721fb1ac65c2fae0608cd5138de4a50c1d1fe062bbfbdb0d99fe5e0e4581b29b04a9cb897d585294e059c2d42860c6d6b6c3a0a223032a307d65d196a8622fb37a01b4e1577c30670d1b4f3b3b2f84e13d5bd0d0cab803285f9bc8ea5267ab517393cdd21c12a503391b60f4eef6cdecbb1577c16aad180317a45b6c2d9986b19c2b1c3392a69f7e4161a6111b5163891d864de09ffea3ee030893bfe532788abde465bc2d3f80cdd6b76398a84a9874ea1f03f3ce3eb4bfc9022d500d28a58dbb3fa8ae463687899cca4746576803e9b937b1e889c2ca20194442bb009744c46173c38882d2355be44f5012a2fe0b370f50142a618599ddb2bf74b3f44d85b782fc724596f88835c78185cb1838d46330e746794bf7d0049a17bc9dafe546e0900be1815842672fc862a3cde03d41fd82f2faf10bc65d5bbbe757dba8c1763f23b16298eb6436941130d23e61fa5647988bc4ef94c0366ff5dbaee1820e999ba62ae605c7cebee21d94de143ffb80bfcf5a2a9d45fa1762b6bbac014e6797a5451a863309545cadb7077b334a7a7ad1c80506aa297505cf97e031acd08c82b29a919b82efb662d7f53bada9352b5a2855d62efe1eee77fc55780df017579ef2d9abece5eda96782f63e3fdbb22670714179437585b1b871be5048f18a2f225587014ce1a0f61832a0acc18122c677de592e01c6ef692297ca3d0ec8353e3350b970c82618c20425833bb9a641f8e9082340135238e1a5504615ea223d88d894a70ce0b9ee763298b19ae120335401760e651b7bc0dc5d1072b0d75bb4f22620ff862e87c38060b790db969bd60f735585c07a288e73e6674e40708e6f13555e7d7c96cec1acb3280d8424d5b48627fb3205c733cfca3828f0ffb6a27844afa0ca614aa318a90fec171a0fa4e8f80a041f3c60147950665efc02bd90719ca6ce4382223a2b21fd69f1e8087f26b4329938ea01918ff75cebf6e7393fe4753259ca286dd503bb806edd6eacc00a30fb8b18919af73e81d54561f8af0f38bf21e342f530813316f0838f96c4938bf8d0dd30d0dd032c0f1be317cbab495df12f164e24e178949f607d80002169dc36e0aaf3ad0db685f60850d7c1444791848a95c8f64bd693a4afcf149216b0ba0cf2b490c61caf3d14f5a71b468ef58e171b9d9eac2214c51cf75c897b99560cb38d879af3453101092a1c361c30a50c60974d3b6ed7676cb1160337578353b2f1c144f45e41ea11c98ea441d4c27374e4f3ba0039a2776bab45f07b32029d225d24022702fb9f9ef4be43da5626fa8b4200c6881b472811127be7ff2f2f5dbf93ed6e7c042779c59606a47751c73c5a313727913d19f20b7c4d9431c37ed9a0873bd7cfdfdd90442d0a5e8bfbe709c96a7ddf0e08d9cbda530295d43858028e13a70134cb7c828c7db7bb36e6600d51d8f938ff7dfeb2571beaa891412f89471d070f7b66868e4e90ccccf8552ec1f1060d5542d55f0db0b8b9a8e3110a73ee5690269f0267bbeca76f729a07c49e5efdbde9224f6761d3b7044b7e7d3c78cccef1061fdb6447e50d78e7b3f49585cce9568438a963cbed182e2801e7cea366fb0084a2cc0b7f49c84d25afc621abaaaa271a432e08ba2ca4aa5f4b416f89efcdeca65d53ea7ec5568e4a0a88fee5a7a8679d1379267d4e94da127307e191934f76d276cd999edbf47180316ba1600ec0df27bde160b21b2712957c01c6ef53442a181817b8ad4973bc69498970474c579f50bc1858408344a85254499af11b548e920bd02e1234584bdab472d93160f329e6acef203cd2ea0c27da73ec47f024e0edb5054285893dd8abe80f5a7b844e1061a164ba2f2920d653374e9d86e038a8d107e7eaf1d89b449cb1a7b43dfca4176ee02d0280038e366e35542b50bcab7586e3f07b17f5732865d00e5be4c38b5119bbd069e89be21ced9e87bd731cafd9c8240e9fcd99019249cfdab8c9c6f843ebb6558957a80f69146b6a108d00000000000000004b690503000000009d9b2df488d8bf20fa846aead1c8f18839b36b87d59973455d290d8d821898799f9560cd72fe98b2680ca04d94ea9383b270175b28210453957b792ef27036167d44a1ec53c2b4a52273b5c959a6f8941ef01b0a2aa526ad88c42df4efb705c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009adc52cb07726ed5d80ce636377bbded2e70480f4f8548ab2024abe6f2340fddec0f4cda9b5d82d3419243fc74f58b3415f53a5e3dc0329691e7c61f0c74bfa302c9b6ac1fce9bbe8b51cf9b747eb5002f74695c9186a8c52727dd9160b7ccc56ab05fcdfd00aaa49889919d1c872f0a1d941718ab33cc2a8c3a6607cb0932190204930355349106b3220a54b7791779d93f07f15d6ac08de877ef8853acdefedd020dea58138080de24c85301a104e29ba2c6278f9fd5130ef1b1882f36549aa1060b0388d333c50a8d8b70b84ca30c160ba4ae04aa7b759ada61a6575e781bf57d113e1ad5df82a98a63c01917b15013f9780dbba7409946cd84fcaa26d44e5192bd03281be7df1661e6039de716c13bcf52d5d2bee7560c7e4258ddd6bfc865a36b6b022cc542a562cf93051c2e6ef0a5b084f80002fbc8f690907142a3153b866bbe43021817b85cd15f34096b95d49301b431ba6c11507768d52c21034bd8ea511f67f002275af86a765c20141a8e2714a563201a40e9336dd635231ffcf60a8f687ce9cb020816d5ba852e5b960d2dfc220d38bc0bf6c4a81b70ec9809df93248d4bc521bc434a823a792184a284fd7507ab302966b581a6204ff6e52b0b9b6ea54a771714a35a1239be49e869d49346ce8d66eb5752215ccadaf151cc9f7371d7d5c2b2d494d974f18022ba96e123e15f6fb7591415520fff40922afbab5452390b4d8e1a877426904856afb9d3eecce1186c378a65b36dee58729d0418e4aae08dfc6401ff72b02492c4ca061f3511e31575641225582e4c6bcbd9e34e4a1185ec06aae0930a198965cc7dd868cf8bb00c98916b6c1e7a2c7d625531acde061908b88da6c4619efba41b03878ec3b86541bc7eac2a85166acfe8d91ed9f09469ba7bf2b655629724b339d59fe816a125e379f43d78745b24b907b9b039c8f81b0150c77a34e6ce05e6f88f7a122fe7ff579c4d3b5a9e6f08c51a63237b11044429e47bf631eb755eb5055cbd81b5466afae284cd12ec4486627454a09e3a7f1dea51543e90f9ce269768662d6a1eceb80fcc31351b47968fffa30a2e6343fe62241f939ab0024d3290a36c6160ba8b2d9133738bd88e965d90dddde576a3f598c03a97dd9b95fdbce4c5b6e487cdf418d396893784a1e0f7a81060c7df5a694ba54edf7de0cd4ad50372f9ec3715af28a37bf3b5974b6b8d08fe1a292d2a53dcd6b25dd6438a209131587a36067acd5756ee5785d9cd966b0ab363773f2791c174abab0875694bcd69f9ea362d3369d3c547049fc363f90fcaaf6abcc45767b29d9e884e5cceefe4c6f62a431bbe527ac1ffaf237cc111ca8758ac2ada0ea62f6616c7c044d6d3cb417c8724cda8d23719d2f0865c846a41c94d9240532f647af286ef68386231803a1176df654fa980c15c033be00602aae72f9f48e95a408d996b1a4977034f0d4147844d8d268fc07f948cf43c3ae5a33a941652dcc67f4ff6e90700796726d792aba1974e7a342376e95f5e11f6b0d003c224c007682176c32eb0cbdc0be06efb91d9b9722c3e3c88a271a5df97154b3ae098d80a292386aa0354a07dd9eeaafbde374f3e2b2fafcc8128df337f000a868da53ac2c53f78f3c7ccc545f2cb3aea9b30e3c7bde18d0720885423476085be7718c19a67b24e104c7ba8a7b502ae54083dad51867d7b258a35e8c83048933d7aa663604cb7b2b95bdd97d52aacd250707f27248e36b5e3b81244a02fed0f2e53fa8041df8a10ab29da7cff0d56beac59d42910003d9c827ecf794126069ccffddc2f95c8437af666822c667bee68bd45d97e858293174de3d5dcf597365d1e0d046910c3a1742c0caefcb31be7e37d7c6fe0512b9b51fb7de71db8834e8da24dbe6be2aab05dc510b081cecdb9fec17980e4330e43af54554523c3c4bd4ccdd1f9faff47d79d330c8501fce5bb69ed5c7a8a9578389b586a814f610c8ffd1963f4652ba8c3ad722f63738ee7c9e47cd7d63415bf3f4680fee8df627de813bdbc4f99f5eda93ef0c8e1155f580f77db85e02c4a2b4d77180ad321d557f1681533f6e6480ac1af12a68d070a62d3ae9026d4c730da9768019940c8b59373699153803bd31e93a6f73f54b5f2bd51dc00c758278ffc33a9432c0a5c5be9c52425a354cd92a7b55842f66b40e27c8b46b0398514dda12556015c6eff2cff5c34a27092204ddeef8649682f2d865bf792d97e174211ca26c32d960e4b81fb75e20df08502a2ca0bed5fd5c1ce84b4d9373b8eab354371fc5a1d718dcd1b4ffa7bf94f43cfe07756c3754d284f4aedb5c7e0597418b23115efd51effb6afec13794b477c27237bf21153bcdc19f7cbcd1f6c089c1d7a8e468ef5a82cfbac2dd2bf3326b46286a572eb02f92caa0af67ddc52cc3b6134ed6da741eca4681906", "51ac526565", 3, 849022614, 0, "fc7f06c81adeab84768b418c005fb1a4320a61a9e116a5afcee70d5e21309b4e"], + ["0b750f4d0161b66a98e90509cdde9b9becfdc76de4b1d7fa1ead0f3482db4f66a6b15e60c503000000045265536adf7837cf04825393050000000009635363006a63ac0063195c8d0200000000056553525352c474a80200000000096a5163ac6a526a6a5318618c040000000008ac000051525163655bd5d0d5013051c102000000000000000000000000529d16cc97952076ad96f709592fce340ebf5f27564285a3b279bf26312f859a49cb7cbb48cd1e9a2234e336b4c1419afae5cf08ede5e9943f132d172cd35cef5a0b8509c694002624549fb22bdc6f34bf55dec8206aaf32b670b3222eda839d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f695866daa5ce58f375f0e42ac35ba8c3cfcfa4a64dfedbd6835ea11e80e9d841762889ef2616c83c136623e28673ca78eac7e4900056bf767fbc58586305d08aa3e11fc2b33556e72b21870e1803a115eb9da1998c262192d24fafa10ef57691bfbdbadbec70251c75d244256222fc22fd8951e783e079df45153610e2c64031e4c34230bf0f931eef7f57e00d0c2323e3ff9cc43ee6aa92fba5ce818a1a1fc02010635a77e5888d0c9ab396512ac3ac428407ed4aff42900c4240469a371927a0a04bfabd45117daf4037537519616f2a6996afad37174774b4f1718f985d05d35fc18e305332168f3dee5b118a6382f1a43bebd55d7d71bb2300e0c549014d4be030d42cb88a1f0597b3b9c2c7cb61d42d4720b61ef915ba32adcddcc7bca1d494c0226df7b9cc33aeb1e41fbc6f2d7f510f6c4d69cdb35de5fdb9223018b426d0250030dc24845087ff32ef44c73c2bd96582305144bd892107f21aa2018295a0ec68a032c6bceb096a57451aec28fd846c40b42bfbd6b2acef7d6477caab1bb77283077032823680024f4289e827ba5f4c9af050a15c6ef977c052469fe287cc828a3959dd7d9e1f6b654a1694c95bc1fd283393f886098abb8c121e2ad81b40de7af47d1fb8fdc2266dad70c9f1b68b78465d7940cd13d0d3b5b1efa901bdef612692a49240187013391d2dc178c741bf2128985b5ab3dca924a6859fc1e62a5698b8f23afe05ba95f69830fc317d66822ec493252346eca0643d0e0303ac05ff7cc56a422d0e4003265990c3497b5e12a690a91598327203c617b9c34268cb5291c5bc2beb1da26c4eed383bbee10626116ec57482e7ad61ca220527b99a2012d078ca6262dea743bdaf2eb1106bb514b94699dd5829264d7590b056d4e0c1f001cfedeca6aae76a519adc3e9bb21ea13e42abc51aa580af74f6b07f9e963a48529687917e0045035bfd45d6821421e0c4dfaca97564b957f750de2608e44733927c6937c06241f77efc40dd89eff4e9336bfdb8f13580ea9f340c1ec3be4e0ad3405e44ea4e3be933e6aa967dd6205466ea1f402b48ad6fc3b1706349b9927572caa20e6338de8df78c3e989bafb310334dd3990212ab9f85d2c9b6823ba826f45931a3ff84180b1ff59e408abb7ebbf063181e8367ea59750adeabaec62e829c20768b9fcf0b24fdbc1fc6487d43c6c7b888acb61cac4679f50190e5b692809fbf82a646f67660ce1fceed6b46a9330819aa7ccb8eb3ed85e2bfea18b0405085855759d0a722e9436c27d2295dba0089fbe5acb11bec85c2e23f97fba05bfee8188789858244b9689f9cd0ee8f767150251ab1de8d27c027a15bffedcb2cc476f8ba076b67851f7388b48233508ed7b14be70582339d7630b04b22dfbc99e0b2e69db516f145c59ab03bb55c2c6e12331537de527e1ce66ea04f92b07220ab1cf422f7bca78356f3f1d6b375d3363b781e0886a490a617fbeb1b6cfc242f112f805602f8ad2bc61084fa75c4e31499980b5a49853a2d7ff9f0f02afb8e1791eee3f37f70145a37482d4d3133ccbe88d29de5acb312287780170b37fa35051edc803b7c36f11fdf855b2f81cefc428758399ec1ee442c572ba34dabc819b14eeda33b0e470117fa0e4b463e6139237003cb175d23a67b662ddcffb2cf2e5fcd94c711b2f82e07b183b2877549f0eb7afefcf1ed66eae16ea9ff7eef7dec4c27a1dafdd33e1455b00a6720bfbd28bcbd32e0c2c5f5340e5736635d6363795d19a7d1ae04c06933a4e6e8c91d32133b98517862e4bf85ed4fdaceab9b8e13db98f67903eebb31ecfa56a7537e573e362400f26dfaea7131cb047e4663748caecd709fb0961ec743d65bcdc0886f191f166e687a36292253a1c75807da91cede779124b8615947ce1b8db009607004f982a68ddfaa752b2c596d100c3857c41e4970b0429c5e48e60be5ca3ffe3fde03a363dd8fc7d47339221aaa311da4c39df8f57ebf56f29a2128e666cdc2dbe448ac788b11b3a5a6147e0612eebc7d08b624aa809bc8bd7c981447ccca767c10589f9e23fc81ee1fea7d41b71c82774536e4bdc172b51bae52c0447813329d9796482147cdadd3f943bbf6988a15c8fe264c71a3a2401fd3d46b1db889d9891a2caec54a40ff028e828a3339b31af97bbad040055315a1ffda3282697f57dd28d381cd513f5889cb64b49a4d8baefcff8bfe40451ee8daf47cc2393a4b8c941b4c593c6fc2eab6d9504ae68d3add49f2537a1a81bb7510efc26f93a311627042973855af13a7a0f3270138b09f2ccc60729fbef75c49b9015d6a94aaa3dbc92dc9e96d6e8370db55cc03f07857083949ac9b782beac9c0937beffc2678803a6769d0ffbf96e9e86a3856bcca2a918b4ea5d6f75ee3437cae0372fdeacf960c12914cb0b08a39509", "ac6352510051", 0, 1271430395, 1537743641, "ed1c370150f46c27f1d1f7f2afb77452ea4d134a84f8ad7f59f66a7c4aa91947"], + ["bad8031e013a06aa8aef741ff8dd0a98c573f49b8c9b4c429a06620eca23c641fa057deab20000000005536a51516affffffff04422ba7050000000002635308b126000000000004526a5353ccead80200000000095263635352acac5300a2d12202000000000452ac5263000000000200000000000000003ec4b2000000000001d3f6d27570dca7005f312f0aacafb3c08bea5abc97452a4f37ee41fefda31a9b1dd7086eb0a10cf86fe77198a7c61c8634ab1274a6fa217b54ff924afbdf28bff40850128f4c5ed953b92ee8031a72e981287d1ada5ee6a3109d8ccff2b2f800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000330db90af3f6809e44a6baaefcfa05fde81fe32300fbdc1284a415dfe77abbc0b0722b3d2c32d262d81c64378e8bc685f13e20691312d4ef76eec0415db4c390d4f9ccf95ce82e2f43459053fc8520b3adaba6bb0a1fddaafb2e8607ec000b4e39f10600978ac8e2155f7a109af9a9da51adeaf35a51fb8f3e060260dd1ca06e032a38fb58b0a7bae01a2564f2ea5d3621ee8f7b37c606a47c5b780665ca641dbc021fed5744032a9c2c098de9444096dbcc5d8546028275d1244a653998d2aa0b370b03bd71a7a150dea22247973f00cf1b1cebf4c7790a8bbfefb71fd9c00ddd6f1dc97d6583980fceb25f037760ebc6d57fa26c09f655683aa36da55c593eebd41c031bf2dd329592d570428c3fce448fe29cb37ade519a132f744643e994f91dc705030e3fddc702eadca33768aeb760fed3c17aacf69605645afc6cc013924c8084230319bc899cfc6042aed784b42850aa739f239ebe53719f19adfed3d4b8c16a3bb902005af9ffbc8833bac5d70543ab2064a427f3d4388ef7a0452f86ad8a8c8cae8e030a1983e5142189ea434003db4823b9a63ce5f56ca72e45db4b4cf177cd04823d5fb1aae8194cec8edae3cd837313b94f69132e5114c55d88488229bba5cd52cb2bc24bb385685abd67c95262f2c95aa4b02f353f50fcbc5678cbbd7be7e4873c3664313271920befa38d02cea7d87ef554316c65f6a93e5411495e7a824beb671a3f1553250cff46f5e57b2bf5f5ba4f537b03878b4b2edd068c18a3a57401d58db51009a5b64bafc8565d2b3b176dfb3e25483ddd8c76d30df32f5881c25fa08e31b698f11b36ba808788a9ffcd66e99d1c3f448f889cd9ea3f8579698af95c916d27362787aae65efaaa9fb584eb7bb257daf474c2422eb22ea2bd21e7d5e666904d91325c46102921422dcdedd42992aef315bf24e18e2d0f92a299a7b0dad317054671431a811cf83073e44f3d4bb0252e47d17424d34d10de9c35b92722e9fcb1fb52acb84b9abe4f7127c319a232afa8bdc9f07dcd5a23a163c7a950764a2145bb564face6ae8d36b8ba5f56aa5d6a2ba02c4372f1470831c2305fa867b44cf94a3bd773c140b5eb74654f899bd0282ad4708d08891f0bc42229febeeea2bbfddac335b7d47c36103493da529e81576a57a54abfa37fa1f99f8e5c947d420706b42801b9d290a1ed2eec7f7dd28ff4149216cd45821e34b82382044b3133fd3532f4ee22f58abdf8c80eb1f1ddbf9ef1c716c9c2f23d79a6c548f2f194af5bb5c29330ef02a1bbcd296e9bfb7bb5e8c3d50395f5b280ddeb1a78dcc4217a1f4454ff3f1740d051b47e4252965e354947d435ff21c29373cba567bcdf431a3601d310259fd9837733dd08a4259965a566aa6626228506d18182824beace94f87e4a9dfcd3a7cc926501d534beefb3154df941c823d55f48df1f224f39a31ef2e1ef1f5e9cacef5da5f34a4ba25d0d46ca210444e2b60ffd1be845d27ddadddadfb9d6e8fb5042fdb677eb9c9b8fdb9f9e108fc51e074d4fa5991186132a271d946cda2044dc629c2f07ec6abf6d4db15e85353bda23b9598dad0819a81351f2715c350da8d4a9681eb70b6bdc72959c4d4e129838bd392a4c5f3aca1d7513d0e29cb4db2138c6846bd374bd8e53e8857eb9634f192db8c5fa1a9e031f872b294a2234df14756245251fd647692d4e613b7f220b5ab747ccc3eac9ab3c8c920f01353f147b9c239a0fe969a113b6b9e4afc35fe99f08108603d380632f1f538e703bcdce6758718137d40a5b7a2d2035fb7e4666082e9b5c63f3f2fbe0fbba496bd96bffd2126d10a7efced2184a99949d45ffbb347245aeddda080d6c66010e8935db2fdc014add583d60cf2a3ede28e0ae9c00d8311577ebf33c07b40ccc4462593cd6ebc1b3969f35059ae13bd3cb14ac99c70db455798e92e843a36ad3bd6edced494d4ed7990091d881e497e7cf70ddd499e4d0713d4e3889534d3056762d1f9da6718979b88ecdb420491fdb46c460dc0f790ad4689cd64f12efdb12b4843d36b20d6069d4b8ca6dcd27618ad173ac0831ae36931ce4ad2c3aeccc4e48da4c3a05bb0ae30a56b545f17f9199f0443262dbfc4c3cc542f7d3db1d72b26dd1675fc6d287d4ccf3b09bfbcbf11ea0610d15a7ca80de13cd6aadf762b80c1891a0cb5621e573578e113a29cdef32e4981818519e4cbbccbc5e336694a05669c8ec9d569c242ec315843b5241f835e4d83fcbd5adb13dfe2a4fe0cfebc68dfc2ca3483256519242ad3aeb5d8a76a40d00000000000000006c2c7d02000000002c576033f690ea1522656ee762d05c8d14a04dcb85b958258e1e819364de9998d358bb3cb3475ad8cc8b345640ee7065b4333c33648a0cbc0d4216685bb75e3d4876cf2c0aebfb95dca508884239a79413856139fe34eb3fbba0e0f77321761100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a6f23163c139a7999180a53e91db67ebbbe030b85913fddb2152fe45d8daae84bd75c98b443e4362a8914690fa7e8a08c3f441757a8e52593cadcae21fe5ed48c3b061004a00c1a3d9c2d0ad48e088c1ee3f5d17df5d2612c7bf6da5d8a9a8ee6986fc72d64084db4806f849410c71588d6073aed0f15d23f48fcee6ac61ae49032d11108f48202d0b0b89c5ababd61d97fa4922294edaf59d9fa4bb080d6598130325a2b514fef83447f4e3943d4d3b503b4e3bd4c256e55a7d5745a6b4e4222ba00b01ae0d7a05b2994223e5cef22329e54336e16fd256c299f775560177c93cb8296401c599ff5e6a79dfc6e452158bbb5aeda3d624566bfa9bc99bd0dbd8a4d363032ffd523f4a2b049a0b0faa7fbe133c8a7c35d6911f2751bfb5bce0ac3cdbc7d00222a84520828d01d6ed2df7a42363770b179665e650cee5afc69c35137af7b14502234bc47844206e603193aba8a0cfad3b7c7124f862ec7899119cb9a2aeacb634031ccb143b3c7b74ef394c372bef938a06e883fa9a0595d4699e706b191fc6cc0c02301102fb46bb15b7d09ed2948b7ab66c62f12bc23694730cf20e68795f187d9b1bde503b95074082946360a281592d5f05a9c9c5044df6cc76ff73baf6edb1bab696c3f23c92e21995eb4aa7d965dfc761ade7ebdfad47044a03259f8a936689eef8d9bced3910c236cb6e1e9cb43130c57e89f8eec05fa88e37964af058e927f5f556b6ce587558f7ad0924e85ee74018c83c459c6d554ab345cf93719eaac75b89175b65047e77abde7bc47e779411ba23beb38dffcaf5f3d94595aad358cbee18e7d0c3c7f0096c9f399707c5cf10d1493e97aad3551e5444b989d70268fc3850ed5b3d1b00ce273237e42a84e59666528e4b865262371590ca25f98b0c7a94f2a35f04f63ac79b210c73ae0f8ec6bfe21098aad8b90e7f880f20eee17d6010a3035ca216ca8e99166bce77657d5a114ac1a6a794d38a55e0fd1c8018bd2b49b31dc683287473ffb9987ed31fdd86ca304eead7cebaf8cb42a5a9dd20dd9ad6ac4301adc660599933a854e68813fe46ee7dc091666c8576e199852e0b2a9d506e080495abcdc153315dd5c2c6c40e40c5bc200de9e810cb2fb038f37ce9fdf20f98abf7676ca584b7a50f08152982d8dfe451d6140364ef7729f6610d55348210dc75eca3f8a28169cbda74f86bac9e6933c549e69d403b71ea96a2768e34969877c780687b11ce3cc31450483e0d28b7dd5e54620004466acc99e0644ca2e5e9ac24a7d5df90245f940686cf7bfb82a0b5d4ccb4124a27c43068674bcc770b9eebd447543c8b5f807c902019179f911f73e1f0223a37ae42e1a2fcc1b56c90850d2b48fc8fdabde05c5380a89d78e8f2bb4a8af6b59ac30ef53cd5a654bbf5a9fe49069d51ab5d3f7fad898cd2586b16d815f2e3efe4726386db8bcf89656085c0bbc722e3414201c457b0d5e8e823328d1b7e358b394bc984b2491f57db9f82c96dd9fb540cbc9fa63e528c7fc52fd0bfb7cddaf296035e041334bea306cdcf6b9b276ccf504b63bd876e5d6e9098acd7a5766b1baa7e7fc586638d7e35be197c500df8c1c386fd30efa4ba37ac3dcfbd77642903254ec2c6179399622dd97ef4de2a5a9744a861952ae647fd301254bccad54a597923204b0cbf6c064fd1278938246ab5278d8714a72313d02b7a4277c8f48ad0a069e711d37342aa4d19ec0721875f923b464086763c09fed6552f6491e5d4c00d640bdba020eb42c12dce53ac92dce5a345fb6d0f371ba6582dbad66e024febf0d320e30321bf7e73889e0e5f106e0a7997ac271154f97e1911a961502233c3c6502b586a46184723676f255ffeb266cc0b798bfbbb782cba4d327cd6ee0ac4bdba733075c14e138a0a46b6fe2edb8acb0cb38ef2e92fa3586ad3c6348480d86d263b8208f12ce3736349b3ae08d9f091516eb46a7fde98d8bd74afd77399558d478ec6afd787d3c6a2e93249579dc62cca347434aebdbf4a426cbcb196094028e0f9265a77ec8c4d06836ee3e5abc82c700de58d976f5d2c6aee0d7db57c8002988672da38272bf204200977f49311cfe9f5ec4064a09805275e21e763392636d219e0221d0959bfc006372bb8738fdbad57c378ca3971be7060007a55de15e8dbabd69dd3f3d5fcdacc9b13676c5205adfa78a1f4428d9f9d48ac21ffef3682d252ae2ca53cdf3a0eb3dff4ce0922740f78774aa8f71a3ce54e754bd319e97fd64d61e6e597128bf2e567b9591a26970b3bed19801fc0ab96d1e7fa6af882945c5894af64fe13a7f8a48771240b3f2b5a5ab3ad2e423611cd7415c6769eebf3cab0f63fcc49f9ef771e281783f22ec025170dfaf2c098ea5721b49517230ed8cfa7f14fce5fa5de6d9676b60e3fc1fff0197804df96586dca0d", "63ac656551510063", 0, -423159274, 0, "9187470b8dc2fae865a4b8c376bd89ff7a605624717530b4489ceade401bdd09"], + ["fd61516401bd94b3cfef12744c05147526df75345334b048dd2231ab950b50d75f3a05ffeb010000000451535300ffffffff031dd485050000000006656565ac63526ff6ac0400000000055353525151b8c309050000000004ac5263530000000002910d2e0100000000000000000000000036aeec530ff4708cdf8096a04a87fd0c8d03687d7eb15d444e2177319b61a4bf66422eadcb2a016906a9f2e5b6ff9beb6176bd8da6ab861bf230d3152d40b2147a1308afc4650d6511161e50b532e09af057bdffc723b8f675edec44466e35960000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000039afa1289e36654268ece429afe31ecfcf88309011e223673dd48d98394fb7b77ead572e4549e67d05a7769a5029e40d4ff1cc914f42adb76f322d44e57683cf6510e9cefb5d3cf98e3a1272a2a6a54cd91f102f84660479b14a0839599da954cebcb938fb6907a48041575d824e9de124d6c31fa937b6b1761e194a797fc2a7020e5d451a65b3b10bbc28278a57f837a865796d022a0443e894d2ed2fb986b10102200e9f88778241d4d2261a9e250381c38aab6d9443b140aebee72ae63e61d8e90a053ade69ff9ad010cf4d93f1a0133457803173b2f830e0d50a03e372edd56c8e2f679cace3b642110406395b42391784632fef09816060852963110a1b881c8f022d97d41cca57dfd9e52555eef5f3698d3717655c074ea13b54e7e74e7229c972022487c75bddfd1a0c13a956c05ed82390563de1fee8e50d3997046bfacf2e4b20032719d30826f6cd5a403cc6ff665688387873a53e3074daf493909ef10a740fec0304354d301edd9081c1fdfa603f69d2e1323bd510798a3be41771ac8975d4d72903258c2e5bb88ae1a4dacac268fc5023508e393294bcf4951c86db24ac765a72e5962e9d3cf22fecdaa032f2a01c951cb32751e13e7aec8c52382ed5de7febb1f62ccf8bc0f7218923700c71605761d0af89c676375b31b13d12ac96d33c588ce853e1556305c5203a8923ce662f2ec91207c221df36d4752c7852f0eaf98aa07e4bf94ff139c48d921b12b0a5c59ab17a526409826a99081698d7ec518526d03a72c1271b14a374c07e7b22ee75ebc184b1e9aa2a82b3859f600e854f7cc80868905f005bab0d47593d412231a5db8900576eaf8a600cfaf6749bbbb70ce5d2c6c7f34e33d3321468c34b0ba0d35e017edb7320f334007850f1245b864d1ac5185f0a1cfdef42c18817c1a60eaa9e29fd2f9d0ec73ea74834efe22c3853744300bbd559cc6cc669fa5bbccf91b3ecf6d10de2f35184c31af9326711e862dd02f897a1c927caf1ca19c74c590b28b8052556b9e3e506e3f9c86088fa60e190b1244d27d71e6d5f1b116f9dbfc9d588ae5723ffb06bb7e1de033bd3838c09e185a158e93e1060b3526b14d1b5047ef4de11d6dfbea29a5a58b6400e844da8de2e88986945f64b3d449f828293d59068fdab961afd83bae6f4c01b65ed8393808d84df7c77c61b02226e98ecd409da3439fd72b4ab7c317e9c6147af6ae07ac0611e0b18651bf620444784830d004812fa389f7c2f56b0321d430bce865725b7d36a45a8676cbd81ca07b1bf5c4090c375418aa90066d3a010867ade9b5261a354e257ac5dae53010ef443dc72458bd5fa46753e5a29c34125f9a9319fd3459ba01264b0eeef069f4a534160618f39db1c0288380b7a5953d7c39978ec3924eccc24632cd7fa76b1454961956d34b7ac529baec260f688742f13a93934d879ddc3d98f97aa071817d2b4ac6aef430711c28a7540b03ec66e866a6e73208a59a54271d4fa7989d896b6c011b44121c66b0df4e769b23e9d6a0d5df6721f7b62068e2b3f48c198f5d42e8b19843b77e1516889d9fb90f81f1ad3e4170617dff9116a364a4a0049a31f1bdab07970eec8e6a5fcd6fc04ee50e4af0858be388b79e04d1d789c33163216e755b13ed848576407a12ade28852bd7947daf5483531a2a42edf94d02aa46eb6f76e9ca8423a3723bef360fea29bccd0d9f80e3c141da81245336d0bc81d2bee182e46d73822ade1f56573887e3fa37b90aa8692599c5e32b9e38bcfe87cd44f9485cceba500a2417393b00cde9fcdc0d53a6a27917191b484f6809b0983e490bc81800537739f4037261d10673809a4286aca51314e6062504ac72318a896b384c2f050515cac415d820646e588d44a2901596b1af6124ba98ccf0185c6679f835823c7688db1fb9f5fd8c2dd213f5054c6f23243e0b0d50198318c9d213f4de84794e911f4d86e4c12d735e2f3e8679e6e3d1f3c7217e75688a7a4e160db3a3e3d131a4a57e5682cfe53ba10c76a5b76ab9d8b49da14dc45611a68d5f1218d2e9c1bc7fd14c023ce979d4452b21d191bef5a9229d8d9233c1be0758b8de60e0e689a3fbb61de91c691f68d4b37abb012747b9c0ae14aa590f3faf1985cb7b4fb1e502f2414714f490b87ea00cca7afe3682af845f47f4df687d1c3d6a9cb4bb324f37605e9babbc25e73ae35a88e3f51957c4b93f7c2fee7df0244659cba92bc741110945ac617f91b69a0dd5fe8416293fe852154ad4e691e3132979217bcb15b7fb3bbd166c9d9a420d0000000000000000c978c503000000003febcdadb52ae74ba0b564528523b9e8a3b43ec5d03124b3e478e64270123c62b4853f3f274e466e400c7ff22c5abf1b32060a1dfb63e4cb1d7f6fe629b6cd1927528f7b35fa4ced26d7f1df03a974ff73328c4b256040af5930764f33ca8a5900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000845da597e305ac9f0cd6a894179b4737ed32d6a5121533382c639dec5a5a308261ddb3b4e11531051d0e6c307e3132a77c32915530c01b8a397c5ae801eadf039c432184519ca20e3917f3b841c8e90cf5ddd11f08f9d619ebb09ea2387973d10a8bc9ede727913133224ace1c5077dfb32962487fd6aec3164dffdd264dedaa0205c098b61f3011398f0496da56e08c18e5b00c236d662728d58a1e7a4830f4dd0306f6c52d35b5e3761f5f7e9d4d7c2fe204dc66180faf92fb6d03b86bf32481640a05e3c931792a0951998d33125d341e70d444f2e0b92f276eaf6605c312b380605b4b9cd999eeea208e4e0fe0a3850a8de5bee079b41a9d7d7ba386005d8e377303270945fd8fda1cd1d93213c5e475a9ff2341bacf045427e7367c74c0bb41e877022b4c4ab86f1be3cdcb0a4a7b5d334db2bd48e8b5995eecfe6f7a38c9bc51af5d021f959e36fa1d9446e719b523356143c59c39370a29337234f6f7d857e2655e69032f5922ad5a94a55198359120521cc88b27de8306c0c63a1104a65e639f3a37f3021bf248d41ebf9072cc44ac2c05cd3769ce4659ce1748f79b3581d4c80b4c7cb5bd35ec7c30237387d8eb91b1186c2e634547e3bb53e56189e8b505f51993861e46f010184163fc6ae9ca0be5f70bf1da57e29992b1d7d81f42f153060553a0bb812288be43f560ca3ecb20973486755ff45454d22686dbea8495d5aeec78d695e291f75b99865cb3923546d4deb52187b1002d118881513daa8bb25ca0cb36e3da1c401add0fa381f0ad987f86479f7b2a744794379361581ad0b8926102956fcd9d007ff3c7206f63af5e74984bda7a9b394736a400c58eee001921e3a595b5448bf1844143aae70abd3527a7577a5bb8a8789c95812630b636f9a1df8825eca8140e13bcec3e29ba0829c95e22179652f4ddd0d23d3bd03ba78ae52516282d38e2d1e4682732b9b844af92d065d2d5b259cb41bd877f98399f991153d35dd6fd9a8cd89d647f1c48f10ca42b69bb164ffe69e21131229a435f1c6b656a8379090d1f1a5cd2a9c174ba7903f130976aa0ea31cc225222d454f8e1727b4f4dac7987f488afec851115d060a27046585c4fc9ebd9fb4376579cc1bcb937034752563063d74d0463882dda0a3bfa319169327dc3baea5f64e55d300d05281e1b79c2511b81053e8aaf602ff50306b33cc91e46d7442af2683ad6bcd7c9499124d103c8e469121a40a8c2e99987d3697b89b5e1c9cd4a45729e79feac294d32cedb07e170c5d2fe7e4d563b2d19010246a82c38618610ff3737ed1e089ce633a1d19f43abcd591db46f20008e8633508700776932f422e744d0c96af3cafdab4ea3470408dc392d423144162326b08b3d3d23e0a116c0eca7a9b4a9085fafe86f0b0a3424cda51baafe18276c1b307d0c60618ec5013a16b8a69b26e990e986606b7997b92345a7696e52db7c33d21e90eeda8a18c05ebe1a6a4db82704a0d246a72513fefbcb699452f1c98c539321e5f4b865ceb4667c4481f928fadea299a4fb4ade66128144c2dab526a7419a90bd4d48af723a6db0e6ad1f6e137e22961cab6a12803992ea34db492500eb2d2bce464a06a72f41f7cdd2d66df03ba52fd4fd1754c5370a5e13b04cb1203a5f773efee5a0ecdb4a158c8fc4c6697e35c0d34b9761cd447285d7178c92cda202845485d632f0d517821d4a83c4152f22ab4d8ddcec42bac4f5ea5ed251fe47f17c28f412cda80f13ec1de08be70f4331fc528a835a82be061681343488f3ad59362a97f04a2af0cf32ed638aa5374f83173947fde11c0e46a888953c22f4adadd3876c24a84fa269eda3599cbf04ae317f0cd39e88dc622c200c065ec05aaac039715abaf1367df8e5c10473516d5c8f1f6a88846f6d08f13b249cd927e285570a66fe35a40ed8c1d718ce080fe9281b2fb0be2a8635ee87d056a320bbec5b8099f6d57284501e73c500856e34f385ed31e3a53a941fda9281b7c82e9dc511c7a4e1fdddb29e6ad6f91c0676b52a456e5adaea473abdd1a06b2c2bbf916e4c4fab9889fb5a5d5e2c8c8094b84fe30777279b9672498005207d057314c616e4bba808c2291f84a49bffaca23d8669db51a6362c5707d406a3b3c80bcd8e08379651c4a52eda717967203a56024779f501ec7e8e9db221e6468638fb5187929dba4a50edc051141c743775a8bb3dc886199c2bd2e3b0b06bb40b9dd4f971cb45682d57ef6f72d278c812001382578b1dbca4d06f7ceb27b4ac87600c384228bc21d835eacaf7fe05486134de0777c925d45c5684b5816bbde60c60ad06fd941a4e93d7ae158b01c49223c14bcbe0d1a7ca7a0e58a06786d23af93f09518ab671bbcedf7161ad019789e13597ea0ecc19188d9455ef126560d03a7971403b217f891c02048804", "636363ac", 0, -397200488, 1537743641, "dbeb9f92d9318eb5c7e3af6b0cce138fcfa43cf7f3ffef3c4a2e7ba5ae943f0a"], + ["2c08f256010dbcb68cb8daeb1a9678d23df67e7cd695976107604d30d49235fc9525b2aae901000000076351636a52ac63ffffffff01cdab58050000000008516a656552630051917022a60155d9fa03000000000000000000000000fd37b62a6d6fa4a3387c7b4679bd0f1ce364e029be858566bd57109c971811bc65a89bf33f01c21af480beb94f4c81bc360e7093228aebefcbd8a192497cb3c15ef06656ad4975250a1158015bac7e6a7f15c82d0a94dbb5361a33dab8c7d34f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004816b8f6f599ee7946ae5688cc70179716df457c0ccb81ce97cc60fdbdb7f829b1711c51a99d225630bd8b02dfcf34c3fc3757b50da46bce660641fbb4153b6917462c940d68330f95570386d4f038725662e7c9444a90f06632683ad7c184e6c4217a95c773994de565e0dd77d28b623b58a88a587be69f7c382e662646ac73021f552e1b93420c0368ac58af5a51893c68a0d4a430e0dc65fb6b2027b69f34b903163ca5417dd836d2b2fd6b11bf2233f4b4198a132727ce74b0527ef829e7d2e80b0166afdfc9027b80a5f60ef8812634d76bc58e0bfb362b662847c1315068ffe110163e8d9b22e2ae52694030b027494c265ef62f34d5190d851befd468773821022634d12c6900972a535768be192395d51350ec3ba75db072326a8ce124413bf10225c38ffa8a245c7e5b075ac5346eec7da0f7f38d42a47fcedb7ef29f8ddabc4e0201efda6dda9a47be254d4284d9dfd230fa4041f5dbe925f67824df37ef2adb13032b3317f5110a5f22870b72f6d65aae374514e3f0672b453c826bb4c73933403c03240baeb9d16ae70a9a63e6f43c006491b24d291cd84dfdac6815f7218b0cc80357616b6905bccb8f03bd72d0e84e5482fd0a0016a21a779f37d7de65cadc243b30ccd0534ce0a31f48f6e50a4813bf3ed696d72df2359bcc872909362eb6a3d3f6064bc6cdb5fbdcce701d25be0c524d92e15f365ebcc0a28217236fb33faa24dd24e9db4c58ac9d0f654ce987d3d76aac390ee37373c04f69bd0c7901ae4e82f860f349b17e5ecc4070b5770b1bfcb03f6b387f2f1e623f81785e0675feaba2eca7865d7f994f77522f1286762ea82038c019901ec245d53e1a8c759462af2858ce0be9ccd5b689dc323b6967b9728cabcb660b135e8d17e4f3761966a75026a25c6548993e63a0ea6bb1119a6cf478cec704593815fbccd7065d83b1d1eadb5505419237c82da1201dcccf6d0bf1a2271f32db8925bcfd8476e4505d1c74da4dc700290a116b771d77189bfe45f55d56d39c039c59795f5007f17a4e60b5f0e00dec6124a23039764b60f7d0fedf7a70e48769ee127378eb3b5dfbc1ecb70b13e39650c22ffc4f44d2f51c08872e7e75fe62d0b3b428c6d9e99ee68f242c1ac794fa6df6a9399a721b62e35772f2f92a52542485a884b42759d27aa88162c19eed72bc8b2e7007d7429e4e831eadb96faf85336564b33ad5e04e07d9a4e7453116f0fbcc1a780901903475a3abc6c59ef33d7f7f966c3b39e6a3bd729a4255e315a424199f80713daa684df0ec97fa9f28c5275d90e981626a4af49b2f1e6bda731ca924d28ce98eb8548140279873ac75848f0ece6251f12656d74b20a1ccc4fc9d832f58c6b88958024cb0b31a4f259e2e40b3373706d3c45a8ad7f3f3d81873293956536c7f59a9883d2bb7575a48d690ac99ebc295c8316c7aa507d316dd1719230d1554f38ee1a6a90b3df95246b6652b7c6aa12e58e266cbbbf61308cdc09b5103fea416389e8f855eecf3ca1d054a268f8e8b37ef27999acd3619972d5ab1168f65bdeae7009f7851a958212c315a58785acd1f45177fcf65828e4d09a32bd911690da069a33481429927daa2105111d753fa245ccc1a99771c51a6cc8b23103d4887a6951169bdd0ce946a9dab256cfe3a1b727bd3b7a6e04352248b685ba2a6436b981305e2970c9184cbc36298772cc40a9a75f4440dc5de5d5d2a686c0702aeb205237b401b48d00475bc977fb16824448f654fa336e45b38d5dcd7c5b6e6a9b8f2871b42995efde1dd2103e1068f3519fffed441c07ae8b16ed8a0e1af6ed58cf08a766d64c71ac57d1b637a7d7226b482f3148e2889d920c7090e4d1d8efbd7c69ca97189f0cf7d89f9ca68f82dab05b6454cf7d1f797e3ebd99221c98a7fea046eb1a5152e1ae7ae0797b2522c5ebbb1b02a6b32533fab678d682094101eb67051758f5098de54a236d8402e85bc6039ae3fffff3b18ee21662aa58754b6b6e36b0639316eb2043de720f32dfb5537a8cb11b45c45ea7dfa64ad62faa57989bb62c10d4fb6b68f7de67552b23665c60f78229a81a0310149168a9943737c52e39b300837980a450c9106c819e31e7d827d046f8d4ebd63fbd56a5eea53e4c9b567e7dfc7a5724c8efc552b041eb7035b6fd3f74825f2952e04d457c668e31935d5483fa185c8b06f5d8eca4d74222eed74058c3a4cd7f694a4239396b5aa1949217b37f13cd60a60b6ad91cf17abfaa7c25eb984b9ea45d8ca1a8b16efa7376cc6bc19c0b3bb4e26f295303c0ed21776d88d0ecef0dbb9634d7a7b0eba0e7aca9114df55bf317d5538ef67a1375e57ebbe71325ddb70d9c7ecd2e405477d21bf089f63c3c7e4b09e67f8df361cd8196f72d80738149f723bcd86ccd0aecbddea713973686d07c543fd01", "6a53005252", 0, 1890921588, 0, "7e44b3c01f6087b30efb179bb3bd2464820c885cd33dd53fa6916d599e6f6fe8"], + ["e355081701eefb986e9c5f371b74380809d69a64f097a3eb02773fbb3809ee0894ce6ee0d201000000085252006aac520000ffffffff0243af27010000000002006529e4c8000000000004520063519ecd5eef00", "6a5365516300", 0, 1535195325, 1537743641, "a751134010fab2d732cea859817145e4c05b3447681302fd8abcf853a8f51ef3"], + ["5f553349037a9220911b90d97daa797b3787a9d411db0c1be552b40d4997f51b38ca82a3070200000005636a5252514c45d1d3728f027422b94814fedf55bea646c203bc48ca851de1247b4321e5604152a9e0020000000552650000acffffffffddcfc84651747f37b53285685ae5ec972e77dd38194afd3bdf5adb34676b41a5020000000852acac006a535251ffffffff01cc338f0300000000080063510053ac655100000000020000000000000000cef3ac0000000000a4e975ed1a2667538d3a5aefe6e8a7a05dd69df79ffb519bb777e1e66fd234c15a1f77a5a98becdf0ac40fcf3ef9cffba284298b10b7b1e50872c2578f5c725e4acd80242acf84c3dbbbeb05ee0def65d2f65b1a822a5575b1bccd27b0ee4e8d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000896a282a0fc98caca92c1a3aafb0cf6d43428d65376282e2b615cf05fb0bfb242ea9a40c6a525135b81ead5226729f6f7de4fa90efbdc1a1f10ebdd98ccf5de124a9266c6324c13de10770ca7041732cc609985bacb933d373b60c4dcca69e8bea3860cea8ebf80138622f473120cd4bc367acbebb193dd21f21421d3e587c86030fee404e9bbee2227fb7224fb9526e9d39b9aa1cdadd9666d0a781c99ecddb670219427fd173805ae905063c633378200c524752140e30515bb33c1f1f415cfea50a02c7524d20bae3ddff3fc38e3fce5bc8232fc2d114858588641cb35ac58da851404e85a5cae90d6ac78290031cb83133c141557c00031f543f69b7e3e735d754031b5fa80d7d5c1d42ac13181c688b0e784d0754abbd39d1173e2b007c7c749dec032c25b0687eada83b831f89d937ebc5fa0d3d7636bb1e519966022fd03d4c399002019b4105d87d6697ce224fe74721ae737576c7fcdbcd55a71d65775d17bff87b022d9cc4a923949bab631209a5303085f432468419ff57f002f01393e5f9ced335032e03866b25e27460c77e326590133e89beb3a12f29b921f3f2267591c2902d8df6b7acedf51fcfa1b6731dae6176fa928f982d9b55f83a80d30a2c92c2bdb889515e188b72cb5b9834b2b4564bfee515c9012d29af16443d562720ed355ef024e80984e7869afbf3c9a190ee5761ea66c706e087ff7dda7d1b3b426a46731f26c0a4eaac1d74f7ddf31eae55ff587572340c64a0be415a5db6566fc4ca9a9f53d6f732b7dd633a949fcaab5fb14f976ac874ee89837a0ec9eb66652aa65b0906b01d5f220ede79eea37e13079f8f586fedd0c33630c25eee5b1770d02803b04834490e9b7b4a4de50ae4f04feb71205b5432fb32ec95d9a4b47a9e6b0ca10257751d244100e925104f849d24ae4846823ee3f572e8aabb912aae0bd64fccf73b8802d4d98eb33cd4b7b4228b64807ca0d57da41d03adf38470fe5d47a2c694e1479e9d77c0a2f55aceefc37a6c04a70125c57896e9b544ee4d94dea655e3f7ce1f473b89fa3595e15a26613f2bdc3981367deff59ff36a17b92b9f13ae9d07b352d463336c8023c0536b0e2fb11157c81d798f216d5fd750a6301646c7f39f282ce2f14902ad0b7df259f261666085cb9519dc5f2c8f826daf268d009bcb460fdfac8f969eee01491d5786022f73d05213b4aeba4a912afb99d467d8be631e5d4d28d25357358d3360a39818f8b153a33b464ff50ac8874e0b8bd8882893811862b1dd1ebeae0ff7b5b75292b66bbf65d528c9b122c5289395c1cbe10d16d2c4316543df3be8e886505718aef206e6e836dd658ce4d16b89dc190db699d12ea6da2d045f4866af33937377abd60cf1b1b3add394f011ee775b568d6f16ca9d18b3e6cda5caa594318335ee7bc3107aa337ad5a0aaf22d5320a607c66f7af2ee332bb916dc465f2e49d9f9a06ba50fc72572652f385d9c7b61f4ae6faa364cb34359275516765d791fbdcd952599cdad4784078fa9da616e108b90429dde3e11c331899db319fcf75f9417420d78e7b3e8dddabec29efa9b6343d2a8649dfdc26f1658803b28e254a5482f4ecfc813f0f281b92dc97a60694ea48b35ccb98c5e4122e86e2caf775dbf616bb9ab1fb417936d8898b71d468a2e64947c096114828886591206bec92b4840f6e62bfae44b7aa11311b6f50619a39c7aad05658d0bd22e7f04e3d36521e7f5ec42b618566288e4436ba5c3936136b876b107deb4593dbf4992eac731e385416d33a70f49851f28fb6908b904a37930a9296f55d192b2dbdc5780f782f73e731f14842a226c549d14bcd9333a32c2c6ac044b58ef771fe04d5a79c220b93350980a1e00051751d1b1b2101e6e4543450b124666c83fd5ebf641a6396a43a5507a7435a15be78122f69e44813a9fd307b954027c51ae1cf1c3bf4746d7ec7405bb7e0e0ffeac1a8652d0771c22f07700b190b3485828cea64bb2fea8969029fbca12549831485466c821e5c2d11a30fcd98890496b14400559777c2594220bc73531122cace4c9a1108fdc0b3abf79bdb700b29136da923e959ec4908264cbdc83c5bbfcc29a3526665eb8585919718d7e2c576659c33f7c041cc94d5038c696f428736c62a5a43064531ea5b7d1859a29989e352c2deaec9412973eb3c38c53097a0fbb79511bf7e5535b067592c1dc36493f4c933c374eb8574931569d8939ad490601c9a3297ec556e1606759278690b95ca3560e44088d69458081693d6ee8a782075d662ae950000000000000000e0e28e03000000004ed1f9d90ec1aea9691d06d8a150e78fcf4966d4f1676914f1c1e912a9f94ff27c4d63385372e12b9388f63a1ac3106b79bb81ad050f08e9a7e0051ea84df826d837ab16b210bff7e6585c798949bc163199976d254c00df3c2511e763cbd54100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b6e6a04ebea5e2f93f659d03d8a82d709736e1027c9fcc650e815da43ab88c71e8cc511d641e10ef77175fee4c3c7733c81805c876f75ba9f3bc190b6a2901348eb7f7594e4301e12d77502ca7889eb3426f027dd4c56e71a9ed05c071f7d1637f8f89d9e5e46a602de0614632971d3d0748133dc5f1a50e514260f94f2732e20212058cd1da6b8c27bc06f48354bc5ded0dc3a51755db916bb07d1adc7b213e9b032a3d510665be7550e7781b3c29775dcb3976d77d4dc20ee0a8d7dc3ec55d5cf00b0833c6a2ec070eb2db3a566092bfe4791c47cd7606b16a986d82f5038438fd3208ae32e871dd259b9195560f7c5aa09c6de668ba78283dd55c83fe2650cdf7dc031b55577c7eabb3e09a8d472e1891d1c10c6ee4349593297bbfe09164cefe90800218a4ef583f08f274565026e134d70a1a08fc6262eb17e8b3490c3bec1b43ea86030ab38b76cd57461ca63c5f88b2c961dfc4f636c1b69f83fbab2a5df03440e906031c2b454fa80bc10a15d94bca585dce14ac260ee0c05d16e7556c5ee9d0d06ec702129c77867d51d7d866bba69b8ff113abd873fc2eae0f547bddc5852f4bc9cd22a7af2baff325bedce2a49628ddc76a7488d2e1d43669c5eba452f6b0a9d5a6f8f0698bb2c77f6fae1f9a48266fbb0e0ccb5df62b7357926cad4250c71869d29d504358bd3ebbc5439793268337ef61e4d66892d6b4accb987b690dc09ebb9053a9bffc1c238fb45df4ca4a97c12ec3dc302d87fe8b02ea17bf0e6063756ce0b422965f8db7765acdff62a788313179025cd74a8e29787309f7f5b8976b9e26f23660a2a78fe89546d5bbdcc2ed03c526e23d460d6d52cec96ee02f33f61a942262143b6bec63faf137c346ce4941620637ab5a4177f9523aea33768f475c18fa1f306b3b924cec6ec339f399f0c5f1a962bb395db6a54058e59ff115f5ce1c38b3b31a41fae98ebfb0c742ca78f958e9190002a105fde37c7026b6f35f57f6b5c4ad2e650e6085a0ddb8fa551231c4d66aac235331e0b0a0f686b2f0ff3a841e9b7351871fe48275b901bea31294334d4753ac66510265a42e44cedd5fc941299483e0d77b874c23ed6c802f4e15a88ae1b56f3f2b6901e3ed1a7f4809ca526db3fd93a19bb51c7d7533a187cb3c1c2fb94744f2e56569e6b79634c48880da50e8718209af856403efa6a74696ac0dfd3030386257a30eb9ce67ae7a702f896fea4613dc4fbd34983ed17c918bf12c8f3389ec0e7baa36d40f631aa127ff2f34c1a17174a964125907ed4e6f58f43b8e24979ac50a052af67b5c5701156365f7e14a25351c90496f87c886d0646d67791de47f6a3ab9a4af72f74a45572348bbc34cefeba96904c6b7e8b03780222e4f4cf4898ac7d6a053ff84906c50071f1c0dfb6ca5641f0815c20d35afc22505836b6b02d6bc188c587c7755e3b31183bbabf707d1f68f968bf1c311c51b59381be485eaf81fe0bd2c9ef6e015bb4e76ead089d73b55968cef3ccaf939c75cb3a983452333332bf90d18cdc885b9196d94cef6d06252d9459a2cffab4818433a67fbfa740eeac21f15212c5b2adbd041f96711aee8a01f6ee894808443c43cf368d10d70e8cf4121bc11be13ffaf381c254157f358bbbac5243512287dd5bbdf8ef053caf9f69d4d99db8839a57883cd3b5265499b05d1f0198479d2fd5d8245c3033c44e0127c88ba8722e28423be63e07244acd3c5421a58684d57a8883e089228abf5e70e8a2183fed9b75c4b54eedb40517d479661db15ae361b617ee2df097f55b8ce0e2e7a5b9e5ee5c218f7e17a24bfff38ad8d575798162d906bc0ada32e56ed22b99bbbf446ddf4bfb3c83e865c6604b0614fecb00f97307f9a0ac0881e37cbfecd396e1dc186d65cfb15f520188aa401033e23cf4c86bcd21afc0455c324f690aa0fcbd9f844304f663cf019775b5b05de68f2543f91b313f6475faee888b3c97858ec9c338422f51e9ca7a3d18268f377a01072077c0fcf6dc70953f09135dc1dba0e210989e9c89a5a3e94d0cf8e74bab1a71ae25add885b377e90cf3543f1b612f3881ed32fca5b2d99a6b6dfbb46276d1ee857a0a83a9700b23882061146a5958c4a8aa29eeb237691ef185479bed6013252aacdb32591d312189a57b9df5555d0252101c208145198e24f5dc9416baced220ccd10c0036e0964cbdf9a2b2d4cf9869130e470193acc0049e58aa957233553a8d0e84eb70334b654e078a9773668a95ed3b6fc4d31bb5af2b7192f9fdab770c7e15c1dedfe838579df8d568edebbc4261f7671d170e78470394aea40196d51d5efd5043aa3b5e1cf9ed4e61588f267514b58748936b2195aabaa7544d7d0ae63a6d7aaa88f35f0ca8c8f71018dce93af6c6474069ba6d6488d7b629a1f33a0c86267f7f68d2e90e409", "65ac53656a005351", 2, 1504723169, 0, "9df43f7e8e22f3d969f7237a8055b29018e42a8a0a9cf8c9d443a9a9d4360b8a"], + ["030000807082c403049a69a1eaac22bc1a94c392d0af9533612015d81663efd277e53a4f8ad10cd21c03000000090065635200530053acffffffffa5b072bebea55984e573d85494b60923ec07eb8b14bcfb1a9ba2559e1e4e29a9010000000400536551f789f8c620942bb006af5ab29200698047937928adb08b3a76adc12bc25d504c306f179400000000016380620b643a3383c6a747e78b9d7aa5a4d9b058cc7cd59d625f3ab1cdc501efc732ae57e20100000007525263530051639671d8a902371b8a030000000000cca805020000000007635251636a63650000000005b6534500", "5165005153006353", 0, 447425695, 0, "0cce7c743238c2c9d98db252b1f1c4259839d04428ed01d15448d37986898a56"], + ["030000807082c403049c0037174806b92cfd2dfea8ca7b117413eac6d48e6c8f79e25dde592f3feb5d02000000046a51ac00db39331c7443fbb76418fe33b8530e8ad80a375bdab8f0a73ace73a210efa47a7ded17f7030000000453acac65ffffffff3fc4d78a47e4e4e23e9e49a1fee6fa6bba3ec110550dec4061972a485d8dbb89030000000465ac0000ffffffffa097422d0895b9bf2d217e484aa2960f28aeb9c468cd7ced9ff2fbfa7e29d5e60300000000ffffffff0101450d0300000000000000000013495c010316ecb9000000000000000000000000002adf1ed6187f6453b43c7c0c70a85a4ebd935a582c89f63a11ceb14dd89eaa9f87332cf539cde006f1d16ff53e3912f7424f7422d947c9af90ebe03759dd33dfdc992c7eb3d780421b4ba27a5d7116adebb3ee2f305327036e58e93ffcee215400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000187c97fffe101db629ac06ee105e0508a656e29a1bbd479e6b87cf7eae81bc78089668b748845e77715668b98d549f2d6a200193aa24f10895edc02dc46006172c0dfe64f569311a80fe2dc5a4cd779b7cd1309446686af59ba009b07f2982d638438702bd84476c9889893a914da00a0b5bc775d886e3b540ce668b2b3ecc560213f3b5455ee3b11ad4c2fdacd97c7b7de934c189eae24ef9f56a46069a96242e021ce5ef2835495b62e25ce3ff9ebf8903c4bf1c806699239ba131c873b482df560b00c9b3334efc9ce5b8ca80d282154d36884024cf63840284709b7e1e9108d32e84e8b09de1934e2070234b6f21912ca1d7a9d11119ee8176f84edbe5732edffe0205e76f22c336b12d42ad25fec1a8404a3f1e8d56c3e4b65ec640a948f6189a4802157282e31578eda44c86bc9dd11721e084bd4841a4d99d82ccd5536f07f0dd9f0313d9c207c39386604c30f2235437cd567242d5c76409e235f93b3cdc3d5eea06032b5391c103f0657224ac3760836790bec6de9a38aaa268e518f6f6b1e1aafe2a0307898c9b865f25728a754caef3ef73fd8bd10747c36974a15048158f3550ac6ffd89edc4893a52586e43cf564d148d258cbf00901b0420e2a5bb64db0ccdfb8afa4faa0d7748800c0a7a262bc36b5fcf9a6a97e237112ec0dc58269423686d2ba1b372711eeac4097dcc5968af7f5b37e07adfec77eb289b5fd440ec0e7c226d3b9237760dcc9865a7a70f2913ac9d2e411fa2b21bc4490405184896c477b3d5bfdcd8c78a3e221397d0752672df64854061ed98a44032aab22bf6bf5744668641329683508445635c18da22d1d824b6682ddd9c206633b16bcb3f3c2c44951226b43ae1bb9307e465851519688e0218ed78c5e390cc49dba7ef88c33d89f5b8b65636b07137a2a7c3dc0dcc990ab1c956d58c9375176433ce581a9b20992b782e7f8f157ca0e62b12b6ce95f14c4e0be3149b9a47a7c11a2b4b66f8f97c9ce2fe5d670f2b087b676d43c3c1e7d0113d745d177038a4a3262bbe9df110939d662f169467e894fc18a364bce66d84d81b0e6301ea1f20cd5f787c275158ff41c89147893ded8be240f999e74493fd5dc3b2759183e57b749bf9cb42232b8be9b8323c62d47f8b99ef88770c4323ec1b3de57d16679ec73eb364d50a8919b4ebb72c2b1c0318b8401366943ce4a8ba9cb302590820b67941741bc4781c05113b5235c49b2fa7aefc8b21b0ca30224e25724fe59d1de51e6905b1fa0943c176b5285dc8fbd36db616cac659afa88d505a524c44808b69ab04e2f38fd761e95ddf58318d3dbce1186bbda9eb87c7317c7ea7e00a22d86a9944657fe3322ecdb6dc048958bdb4bf36f612c5fdc25c65c451ef576b62ba9857af16c58ced6f9b391cb1a3d95322b5bf72c5c68dd13e770f2866bcac747453ab0c33bfe304946b146c26f6c1b0d20f00db8e5af0e6ef2212982d188685a4e346abcdc66ceed109a58f9e5f40696286fe529ae09fd05b2df832789476c6b5fbcf8890b9f8d7caa00c626d3d3dbd4064788b1670caff9d31068384d7efdf3fd4fd7197ae4781a6ade4c8965fac11b8528f2213aab72e2d596572017c42c14af83a55fac85806c8f3fe9990ab55700c88e003daecc772c3a76859147c6d25aea84b1dd6d5366f044c95b9db1946d270b5605ad8d89c9ab190e187290adcb4d0fe200a1fa3a9bf8f2c0f40e1a29ab359a0d8c7eab300761ce64e2df051411ed7e19beb6e60d05f7919f6bc5d876c073b8fa3944fff2f86771527d18c09068d8acb281769ded8121858b79fafafe5b3e47df1214b8fa442e5b12da6b97e0c59c84c7a9d7932cdd85e0fab63aec9627664119a68d6259c304a6abaa81dd5bf882e890ac01ce9dee8cdd3ef946b0fde7dbcada7cf63cf0a50fdb409393d3ddc652b5575b8ce71826cd916327d5be7208d1c7f8aaff8540b7e6687a37ef4891843c55d4b12db669ee548105125b24815071a246a48624c2cf4eb32c48647861cf5cbfc92fd07942cba48974c1ea8235045469d39adb9cf970620c0fe76fbe5e0253a72975f0187d2e001b6b92632a342749d51bed85e02291f710560ef05b30eefbb5e084e2464d4fe292147471a1d25c11c5c0df1e544c10db4d944002a6afbba7576ac4bff75b9dc6038dc5b88c6d20f55ed8754b22c47078d307cd417ffa32f080db43131de18b75d5231dd48a117d0a0de3d7840cc5bd13fa2c23e9aa88911c1c367e53011a281074a899e85cdd87575590e8a50afc4173a594304e32c1257a6a504000000000000000000000000b1a12e3d8e210f41a24f3ca11976d57e9a904a4118c45e937d82ee7181e4840fdbfffb0a75dc9a7f8858e0a95fb2517de17496542e4478c2ba67f006a76d5aca83c1f14fdf7beefa49968b61070a67286aaa24974679d250972fda878eca725a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c95d442760828e54c6e7fdb96677e584e1e119c6fa9468e4684e6492c7bb9a9b590d7ca38d543ac28220e66be6bd655b70ca6625dbb95cb707c5f1fff488faf7a62cdf62de3447bc89cb4fa1980649c82fbc4bc1dbdf15e2c8a87849fae98c6075925b7fc87fa300de6f9d8e14a9f8c72122c8661c30f76e8ef19b7206b538a0327f6576785edf08c451ac78ec311e206978ac6aca0ae0e1abbae8e9cf61cdc6d031debe0eb1379573989460c741b119e88af7cf059a5b03baa6170ba716ea9e8ed0a089b47410e62a8f36ba7fe47d2f1274cf96ea11ee3a551ac94eb5b061b69c7a3f3b3be14426025e1da6daecc162e6e3431eee55a1aec0312842c2e24a8e3f723031d8d64760e3a94ab9e121d369cbd6a7bd1314e2bf0a5edad7ea570c85fb6aaac0314853e1e4b74ad301ab79c5f6fadee61b01e625244d442e0bd50e5b6c577df04030b8098d0af83b166797b26e3a02733b8137785fe34536c16d0742a7da9026e0c0224a0b38e4e05a672349a1689a888e878eba3b5e04fbe8bd0223de8b68fd1213b021d8a8f7f90fa9e4e13f038d2dc34d8892c33d97cce858811d2db8428dc29d9bff532e56b139e104033db66c5bf4e4fe8e980cacf9c70463edee4dfd0aa19cf9b9fb0f1ee1497a8b1a9cd49759f093d20dbbdd8cee7c964a1cb5050b69dbcce9d258d3948f42ae785c91be7ae5392ff10257dea34b9d49f101179d6ce90a27dc7138da419400faa07b4dd8caf43de715c8b9f60672e9ef8617e23c8522af96fd241c1ed824b4268b61076996d872706498a8abde90f211341ab21728d53ec805534cec88c2d8fb50d9e07f7b495d33ee3a3fd8ca676cd49acba6d327c86365e779f7e342ae19c1d50320a828b2963bfc982a988b533923187b7675bca0c3f3a5156906635aa74d593d4c15c25424981db85616e7599d90756062e9a3edae33095a1d0c99f30c439cadc70d2ebe3cccfd97bed51f304c5e6304af2e8fa4a8e448654e1174960115094d15bbf0acc5274427e763b5362b66af2b13e992c52b2c78fdd3573a0d4efdd9d0b91885cc3fca3809b4b6e40865f2d2494734924f38ff0e5cd3a3201c1257ecb8b39fab2647335df2bef13f619d9e9931d4a99dd692ace21216763acbf0774358c871647ca598588b37b469669be353a4afa9b78c86e697f3b68fbd9ec443ea0a6babb13e23f43d0016de577329c1726caaa2657023c502913e421aad5c39f19065a6cc8f5a32425ed099357ee125dd87b70ab65d3b6a858ff0fa015f968486e49b9ae5ea22949fea8f8d65226e633f5a71c312b1b8d364daeb806071f2af345b82787b1dc7fae53c7553870b72cf18e24b90ea3f77415819732c41ef66472997477a44fc89c74ee54bc8b03b17362e7eacf77ad22f71e8984631c1b8cfd7168e56465c5099e5a7f43f6738fe9e2fa15c4b93f435072c53528ab255c44b29be1c40317173d027b0a2323f640f7b63247f9c4494d474531d7769b74811212a21c6d211cda2060b744741f45294aceb32d2bd563f5fd8a78ae5fbb3a543fda3c7dc86f622943751d9c4e30b83640f25f6c53a83a1ddedd228658c30b7c149dd059b5008b441967156a342255b699e46a73a9ac02e77dbdc951609b43e83f2c0ef5e11d84c2c982a977fd342bc07d2b7b14ed193bb5a13745c21a933dcb4a34d4e77adfd4833b6f2a111a3e201296d79d0aba2a5f9528661814fb0f6de7fed12494fca36315104b7dc5f0de2be0645cf71a84b958c3300cc26b4a5b76ba63d5a863b139525354d0fb5640c976c6b49a9c75ec93081aaf63d4da2a5e62c1698df6a6605592831cb6735c47411d33c3e7c6db13bbe21dc39d0882e7124f1bea9d6c2535d0011d42aba16e0415cb5c452880d58ae5791a61d52928ded062a84aab9b1ad2cc62307ccf866ba76d09536bc8804dc043478e860f920ac300cd844b6dae576f4b56ef2ae59527013d0648b9325ef6c7cd497043f0a5eab6ad07bcf2746086cb4cfe01d53478b58c716ddd9afb0a9b4401f3bcf65e387c47bc5e12189d754ca226860680f278ec1176f61495a6e37cf7820e8595d0876f9fa957f16b158345ef8ab5e0a64ad2982bfff3733848a6b31fbb830f9b55bc8cd2c66f0745f0c77b5c905e9d5984d938d5f300abad59c265654d2ed30a84e3bf15ff102bd1b979207ac6d85c45ce8e05a1cf170531175288842b46d01cba853a701d3425e0cb4933dac9b8ddced61c5b1f7aa89acd3a822951e66f6bc9e268dabcd2984fe30d51f69fc9b8e584ed2a38eba30000000000000000b7544104000000004daf62f4e875c34c76761ae013ec727b84d0737445f5ba02de460fb61b539036cfae2253b44f9c8df09dcbaca65c76b3c89e8f0aef17863a4ec4b6c567bef5ecc429846bde72de3d65d6b88e8b6c8948dd47de49916bdddf23cadae74cae32e300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000141294a1697a0ea79bc806500f9a352c6f791992c5706a9987f9fad468dcf08d9739c1bf750c6af86bfc0cc38839297b8266c7d551fe6ba3a9c1493c2a8be721f73ea5b94e3602782c9e202bf4062ec4376cd75a6e91cdbd6fbbb43d030998530434d422d76a7e672ef3f2aa020c054d113fe78a36a264ccc1e6754a0207dcda0311ac1d0a2cc685ae8950b91025896756e391a3dcba21aba1fcee4092baeb1c5a0303e82065f7d8c63f4598b2d9ce44967ecfbc206e777a7677c49daaa8567f3a7b0a0910766f418147562d5c241382c23abf8a8a046e165d012d88feee80073dc3aeb9ad9920315636152f3be418901eb624fbaef0f83215ea83f0cf1ea6703bac0c020d7a3e373873e29fdf0020e3bd4716977a487c4ed40b1ecc3d115bcb402292ef022388ec547caa786a164664329eeecafb467a5b39857c143ea947b4f1fa0e49d2030ed41488736e798e35b8984862cd7a494ee260da60a4c2631fe0e8282e09ccb00214304252256bc88cf09271f87f8d2dffc87238514907165fab307d6efa51d4940225cab71e9659feb4623d32e7484bec20d070bbae6f64276a43b5c40b31051838f292add8546514977fc70be5bb3d15441dd7e466f9258b746b0b460abe3e9bf5fb96911ad03d4ec733daa654a847c6840167bdb9ba3a59b3f5ac1edfbfe23f93b712486454c904ed4d858437b1eff2e5412533cbdd3d521753ef4ec15b2e60cd62436ee6c55d420481f4f51f715ccb0f350b41b24af3e01b2639cba84e3d6925ffc61616ac3cfc4e034b02e3ef17fc5d74baadfb971365cd6893580b2c2cf306e2229084ea4b6923b7e35e2bff021f4411f1251820e90823ed7f05f93772d2c24d3649259245227126a8a72aa411fff3c6dc66dc8613609e1421dbd6e068746509a58e48221a34dc852b090f854ed4ba3edd2d50641f41be4ad8e2bbd29e93a9dc3e17375bd8e8bf77a5b2a58037cd8250cd4a6ce73a12ee6fd98a85fec232c50f23a8e2abeb4be71f77b173da871fc921fce59649fc89ab3e3181d6898bc69de2b1960d0810cd0043cf610a9f3bb0b724ebcc57c7645341aee8fb2b77202b119fc5555bbccba06ac6d20d15edb7cbf90de6993ee5c2b51f1541fdad5299e1ecb1b4628822df08e16b5c8aefdeb13ca5c3b284ceca10ba8591b6c0c759efcbba3c149fdb32043b5ccdaecb0b44f0a976b0db56813c54f9094e21bf0186993523706facd70efb8b848a2f70cfc33ca29bb54575e3d7dca16a436115b86b6264af78418ea698183db96eb343670a460eb8e6708c41787872b4bf662e643e467ebb15f12ce04b95b29805fe413ad3c80b307219b0a5838d0ebbd3044fec6932836505a7fc39dbbe990e363417f528751cd824523389bce6bb7019c76c3ea20da3aa0ce43086361226da772828b96033a90d70710136929d9c9442fc4c9baaf16b7af2988111abb3b42bab5be66d7045f5c962ce6072a7890b99cc40ac0e665f687eb5ab8da15b45d4eeda6003412efe13ec8691ff3885d7bf44a5bafb8409a8d0f71364b67fe10a1f2fd9a174df773761b2420673399f2d8f69757583d23b31d64fc2ed927a5611afe9e7a5afbb5722fd99fbed18024625008558f41d5b3666e9d3b26ec86f5a8cd9cfbeda2e2a1133c63db9e69a6e5bccbf964b40f6c12384f66aa264428ee355b3928b7d69beacca96bd361449612a4e9bd85e69049d2655477118e11756830182318ab98f598fb930031815756c5037f73d37656c9323b97c584ead1e6d3bbfcb8903e64dc411f2e3eb7eb7293989ddcc2e2723268d62c32fc8c97527912fe8a410aae9149914e3f48b745333a07808d108f8a1a7581e7ffa498a29c77d7c75bdbd393e6c79c8edfb183033afdbcfd1864f16b42385f6457fd8ecb9c5c86a8fd138809800e1cb185c451e99ef0d4068920541225ea85d518bcc477f113f7b06aa6bb82c813cd518fd889e300473d7d5eaa800786c92909fae16d4ad832af022602b3e9c6200da889c6d4ddd27fabe5d9a3bc056ef008fdf7b919f5312f72f8d8906dd2ce30cf55ea01091ecfb919ec1ac6a636fc75e754813a4db05f90e70939b2b85d474faf99020747fd76f8c7089a5ec89b79a4c103c1d6aa7d22eb60a38cee9b22a6b76091c8ad0b51f517407865f78f9a26d81bfb6f000785248dca8f388846336fa9bdc92fc5208fb449d71633b9f7b796cd5e42f7e12fb444a7083a9650d0dc80aece36f426f99b366bd59d2f71144ef5ff90983465460cb055cfc9e93edfe86c9235fdc1fda901690fda733473e982a260573fb3551b00bb64e883cdb255b29ef5730301c918daae8fe6d7c2aa6bb8f65d62503b7bc63181957bffad54520bbda3543749956b5159f6f701695b0cf89a92d993936352056864577135ef7d3beb976296da22c8a0f", "525252", 3, 1327450234, 0, "aae29618ab79cced8ef5ea1d3cece2d9929fc785ae40b6a180718da9f8e6910d"], + ["030000807082c4030313563d567dc428474cc4573d5bdc469f471fc852041ed26431708266764fad1e01000000015190d3938115e8b929bfed012cb9a0f12e736a19798c7c2e3d094701f94bfb26b48117f84e0200000000ffffffff2edfd5d9a6ed5a3b342bbabdb97afe591d631d83fba1a1b5c4fdec81fa8f7c8c030000000100da7e70d202c75e1200000000000600ac00005300c509e70400000000090051005263ac5363ac000000006c73c09f010000000000000000dfb8d20000000000aa3109a2ae0e9990db0ebc9372a8bc00675cc5b5ab8bb980cdc12caba58b55f30d0f92c8c7efb57080af134df7ddfe9e0ee76317c54c749587fa0ea287dca090314676757d0bca5bcff25fe2c954ad02d3d2b835cd7c9fa2c363127ec99a624e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008712b37312f94c93871c1cda196490e9f82d642bc639766a7b965b8560c287eeaf9020b80790a23762f1cbf0c05610349884f1da8171aea7c696e80bce35ea9f56c53ba8a3f79fa462dce8b70bebfe31ec29818e7cff42f7e057a6227eee8f485df4d29b81e115e118cd4ba63b3e9be2d30338bd59663cef6a146de6d8f642fe030bb4d8bd4db5c69ad96235d45073cd026bbae40b0300e0b59c3608a83dfafa6a030e72dc28a231eb1846e83a1db554777032cad758516f604c6e7ea7579d6bf26e0a0214e559c96b2bddcd8aca1e028b36ef0e7647dded52067cc6b25ae4cf83c41550ed71a9b09605fce6f6c270b9dd603c6171ca708ca814357e6d098766b9bb28030a9408efee7630b0f439821ca5611e90a41c1aef2bbac4371a080656de8e18fd0304511a817322aec59ce28426258c21c28677561c563a7e8e11ae97f00c39093c0227bae9a774fa5c82edbe22b8d000b516d0fad26030a19b237f81e6487a07b54d0202e765038655a080338bdd946ac0dde64097e69bce645d2d15522062772fcde10216505b847db39795bced52ee21ea803ccf6fa60d7fb2e749b6c99f66ca45f660a501fb5c6d99d0d71dc5493d8096cce71ecf3d86e0edc83f53da977e1d8712a68fde0cb79e0aef8106f30d83a9cfe313e23dfd072ccbc4f55c92ceb4721e7a62266cb55783cf4ca50d2913808700bb74eac7e4a9dd58324693cc0d54011854223341c20b6d85e64bc724629b16dd0d19e96cb67527a684e036a5f7fd65c96b0df1dcf5676c9f2010e5bc260de045fdd804084e4810e1f5b1bf5273fe33f132528f27355ef41d5c88f512b546f9e2c77da879b371636a41e0d211b9fa5c22a99c81c8a99567bb221c645b447f2a93efb987732bb626b0cc43ce0c85cc1ba779d91ed0eb67769d4278bcc9984150e74d854ebcbe461386a66da7138548114260c6a24b0db26bb1a650dd2b53b1a912dcbb30b18f18a1b02984c90dd314e10bed7411d6e2dc38070ecff44048fe0f3b2766edfeb65dd609e62fcad9a900d3547f0a956ccaa098947b08142c452b9e72b100906425e32bad478c3c50ba5d9ac555cf6a003e01b105003023fac70fc02cbe484647ef31590216bd6ec7ef28c309fceceee5c3963977323146149f3234eecde361c79d14d8b2739d155ad5eb0c17263631b011c6b942ef048876d3e7ad8b9432bd1a0c6d90f80a0354f57aa78fa15f8249d1b0132d8af8b2c931e518899025747f6e53f56ce8a6a7fbc814c8924a7b38841f6bef1247030280535e6a6826961f76b2a9e49fd05c257b04b5549b7c1c4519b2e1b76856b37b195ba417a988d22a021b18456be1cfad221031368af11ad6c1ebba6c2f7611455117c571f094bb8d6ae9f7b9b7501f673dfdf2763a3cb006946766bdb86767d5bbf02c0ea859bbf543425977fe35bcda7ff9d45d13608ab341e139d43b9038440ae249e677640ea5ebda8fa660474931ec07b480bc0219565081d6a91b41880b130923d9b0c48d668d613d8c38d62a5b4c28e2d49a03761fe0d8841ae7f5949030df338338d75e48c3d8f9d7ccc42642f66076b5a741122b61aa1b20fd8d1b696590ea40421adb631843892a443d8fc432fad00b9e65302c47caf69c096b9d81f93658db016f6daa0e2a74f01bacbd944eb2ff67ae1f198556ea09bde5f6af04d104e8622c97dd7c0173408f7d94e63e060311eca6537135d2d456c98fec0ac90bb0ecbcac2b7bb0a80734fb82d362b7713fe8b743e5cf550ca3d3712ca28e5fb5f273073dc67d55291d729ff62558bef65c932fb58db686f02485b443200d6d6e2b0c3eceda6009bf92a774290e6f70e7244cec48c78a3c9a0ba69fcb21060e37648f60591782a3a452b77096183dfe9b202cb547eab885b2b31d5a739d0cf6e54fe71b0de3c9054fde1104f8bb1223df57644dc85c767671963729b579fc9e6d591298c26df758f8c009368ee9b16fec4ab71d6368c109d19b2efefb0121ea4eea97be39dfae5914a9429f7c6dfa188523a737583a33c85d91c42040dd1dc0e3cae775a3b9b85e13ed65747666523aaf78ecfe7d69f5a7556ed11ba23be6982d5d1bc58a5aadf1095ae2cff2d3573ddd0ffb0cb043bd58fab87ff0e112936d3f710dd769a0fdebe74fa8e3c7c6d31376655bc72a2daba17ca94ddcea6babf4c4b1e3c2138d4535cc1a45f174870a01b253495ab86ff82eea6578f6a3fce36517a39e68e03acc263c8bced20e4501b8cbb4343e0b996ca01d6720a9db655e424a2539ee521749a04e6d35893fe769dec10f19c91c02406b5c40e78a69da5e402e0b009485f89c59b092cad27dd036b264d48eab47828ed2e9532f1baf6f1802ed550cf4056bc304b5c9e2cf80293e9bdd36c6b0cae5695bb724ab28ec6b8734c1c9a2c189cb6e25da0b", "ac656551", 1, -1180502046, 0, "5db3f2065b522ce97ac4f83b4ce825034d77a3f0587a742f98dc3401b610d237"], + ["030000807082c40302e7298875be1a830d27656070e045ce9ef526c838ff456de8944fe6b88d00b144030000000953530052656363536affffffffae6141e030915b95b03113dda7f4bc904bf63d0df8eb11526fc85488ded01d93000000000165ffffffff0383614f000000000003635152707e720400000000026300d195540100000000026a0000a0fc65000000000100000000000000007e88d203000000008967469374b61751dc159afb25dc81218566818a2b78becf3fcd43849587c9772d5fa09f36f83d4e761e17bb49f52a6a7f1f9847f18307ce384e7ad64172ae9c6035448c1adb21b096d78b655ae1ab35f2f01d177986b07704bc345b9b8030e800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e78e71512c2b59f450c710a19b7d543e70ccf6e5a16347dd9baf30ee740c26803a60578fa03b753c4faf232aefb87fbb2cf178ccdd7af2644bd5ad9f372047394bcfd2950d1397a1bf4722832d46880ce227e457041c6cb8b5c0267573d8812d7130dd1ad0c3bde03e0b2ac924f831c020e2dc1ef5147fd921e50459c67f6094030d1367f373ec94e2bfe4d63d257cf9e309afa9ca78a8813cb333a5e487e3a10403148ec8e37b6e4dd28051fbfd80bbc10f63a5c18157843bcc4087153ff20813e40a01b63f8a2e8531807d2eb57ffa7a54d7d71632870325d149a3c89068571549801905c74f75567f73f4c276caee0612e1d43e570be60fd08afce6d6f35dc9beb20217ae1b797f71ab8fdb069085e22198ab402a636dba289dd496568e3275a1009e03057908a31d14cc13d9b49679a638fb70f1328b15a9e78af46650f49995f2ffc1020e198717e795ab018f0a1b477a4b94675fecbdb80a6ad700d6214817dd8189fa032f4bda07da8ec7c36fc627ed9aafb1404f22945f74cc460606914659c79358a6021219f92eda9711910533de25834cb15c4d026499b31ecaba3d77ef3da654aec0a3e332847bbc28161a47f81d48cd2f10a0edc6ae6b7fbe197b532304ef2a03ac7ee86eeeca51b2e38906d3e5d3e0af188f02e83cec3f5d8cf585b404eb1abb696d4c3d2bbd17449388b7f568be10eb8ef632e5a66999b62a7650c6115dcc1c747966a86e332b887886bca4c9f9681029c3ca22b31c39afa083b2f586e55399bd0a10814cc7707bdd1f5da2b214b7486efe4602e0c0a5924beeba0e28b578f195539e7da27cf8a370544c7c1d5f6cda6724984cffa32c0f12d8dcf566316e61fdb28d324cc23a7669edbaa857b1bb319af06700f30b1a80de8889d15f9e2d47e21791075db8b58514782e5acf1008d52d8c0d0e5f02687590b1d27e23487a4e9fa4d581c0a4977efc17ac01e5265f934599b3f1c5ceec12ae23e8b6bc48e5a2a1764fc79f51fe87dc3dd0c89f705bbf59402a04b65bb506536f747a8669d44e60ed35b0698c0f3f96e618bf9703ae07e04d09e0f844979810a44978253107fae3b9b971563b15fc2af1947ebc10dd3742188f977a1a36cfee4f720aaa113fbbbc5fd08c8cc079b72a3b32b4ff273d1fc2ad24caf49b83115385f8698b7d69bca2e69230a617b4a17b9193afbe87b3fe233942caea25e9dddebaf701b8fcb3a5e92571ebd874899f4493484d07db0769815d30b62db8d61a8291f65db74ad89d6cea7a744a2140f236c36589ba4155120951ae5ca71f0ec55183f04e89f06a9ba09925794b75021f12a1245df54b3fe5bd6aa7d9b5972da44065c4f4c6aafc2751266018f9356d3d5a6fa48bf0dd40ab70b177aa6325e6234850b347795b1f9efd5b1e59722dc75d3ead50fce5b5cefb04e423d083bac168204e6f8d128d765a53c89e18a082b0e792e044a9dec72d4b4577fd94346e91892a8ef10fa48f0ade8f7057146be39ad1eb0213fe052b473a199b76e840c312837b197b142eebd6b6d4eda7841098fe54637cf4a871a3cc7b93dd27e1cb22833fe11bc9c4d7bccfd4b66faae1b043a7cfe79bbd350c6dfd5d67352dc086cf6a1a97dec9e84f81b1481b59367a17c25ccfe29a25d0f0b00d0ef23358f2001d37e151797143eacb5b375b93d8cde1b5c874085184cee5b29a6b5a5b987aea6a365ab43c171ae7cda89179221d38bff5ac18bacad1bf69ac05f5c3f7d93bcb6fabb254e436fcae7d4062a839c8378ded1d2e6c961cfdee003ac0798d8970d6203ecb38cca3328d140d54a4e6ba8f2b4d22e5f2bcf6f4072cf85d8636beca94fe4452bf8e87924a5d9276cea266c4739f72836a14b5b6e31a3e7eb9491e80547ba990107041a5307068a58304ff3e13379ad5f10df9440180dd5de4711a290ffe1c81124b44f8aa3fb24cab216f1fa506ee7d46bae17aad459a9a88e747f9acd324553d3e878804e94af1680d28c90f5bf42b8a63ffd615b427c9c131c195d7a965eb3a19d09bd16174f01b50d7764db283919bdc51ab5b6200cf95036bbc3a32d80a17bdab06d11391675be87b4322bda1ca530c7a559df985e60be841d03e6c71fa9c3665e42e332d1dfb9d717dda3bbc0f0d4eb653e667a827a2dfc4e870af7eaf7cd3e28e1f7afb2c2879445affa90f2839c0aadb3bfa3608e5c94a8ea70cbf43da40b98bbfeefc7dd90388ec69e81cf7d99bfbf25befae5fe2a1e4e640b567eff9b6f4a7992a4b6b733f73e27edcef57d4d94c38171ef842c9d52026418f7f52c69669c82f97d6d55499044906c92fcd806b1952de09a4b51606d10ee0041a3c175c5ea4f31c1d95bcfa247426d0f8a9a225decd5d5d10dd823304572989a4b1f1137c07dbfb61251751746d387451a82031623601a9430050a404", "536551635253535365", 1, 1364084365, 0, "8fd0dc44840b2b4c50f886ec9a25b1c334845a0eda85908b896124dea9d6a097"], + ["58fd6b1602a5dd117a872a24dad344d7473a348b03f67e51eafaf4d944e669e4e986a805f90000000005516a6a6a63ffffffffb0a5357f718ffcea3919dd4aa8a7b9af22eb45d86971cfaa40b3ffd72866c03c000000000752525300536a528abb575104d09e6704000000000452526a6a0f667e040000000006ac535253525321788702000000000953ac635153656a5100ad28be040000000000fede8ce600", "6a6553516aac5363ac", 1, -819439571, 0, "241b86ad733fe63511e956e4f2f515f3351d4b81b3e9e87bc59c7305cc662d67"], + ["40758331015c6866d80311d7a3691ed26f37910dc92d3157cec923e6247d85f53e785c447101000000016a96b0d4d90376cced050000000000289ac7030000000000ada7560000000000075252535151655127d72b7d00", "", 0, -93875733, 1537743641, "9caf02f36c0eac4e95dbff0f9def401fdc4f7fb663e73f5ee21e3103ea5c7799"], + ["4f40cc5d0329e0cfe4ab44bb51bb68736da005cdccb3fbe87d62286a09ba5a5015851653e50200000005656563ac63ffffffff11c8cf2d8202601d8105a47d95433bb376f0863b0828c2ec4032221a3a583b2402000000086553ac5263656365ffffffff92ac7ae4849bc2cf7c1fb621ead48dd8005f8545b13ab0ff17d3aedce19cc5b50200000002acacffffffff04b6a2d10400000000076a5151ac5163ac4d355b050000000007536a65ac53ac5270ab52050000000001acfc02e703000000000200650000000002000000000000000001835d020000000082e5ccf352ac15385bbe97f1efca7fe3a7a15e289d9eae7e04c2805afc30d82afed44a925bfa2f42dbcd7ba7df6f04c4684910be53edcbe116a4769b5f1caac3d70e30384ca481d228ca2bb55a13de8bec5c3539c95d83d3cecae83b7fc0d52c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000363067eb424d0b3669317c9189180d360520fe5d17fe0ad64d1a7c2f685e23f70cc209eb4fbaaac68b40256490d0ce3459f98678f2792185981eafb05ac257cd039ba7b4460400eab97875ddf0694f7bd1258062de12b0bb64a0a1b6f0da4ee27faeceae3b7e326789b368cf3bd6f2d6014cc44438de75a4d0ec49d85bf610b50315268610bebc7a1c9e122b83e6cf07def15d771fcfd783ac9d2b4deb8ba6f022031ae4f87acd8500e3780d11b9d47d65521e6840c3a9484e9dead618d330d35eef0b052248b4aafb96d2b5adc2c4834b7fa580c55ac31cbb97de1152fc5edb3a8ad7eb2a1e38d85e1f1479ef965c7a34803bb0cfe03e16ae6785c9eb28ffba7d76a4022caa3a5e2ce3469e6cbe7051bf84cc6e1da6b10ce7efe7adfc519f3a89a3c9af0203083b6aca32d44a691b66f154c81f278f57eefc3a6b9f7052532f09c0840b930324edd26a97c6d71d59a3f333054ec970f0de499d9ea69a27213554a3e981385d03212241f1ebe4c5e4b05779b56cc948d3140f1af4afbda6556c80b7d504dd3ee5032a26f3bb38286c62065c4579a4adac38a17b312630e9f5201051c75f031092e167b5adfece8132dd574cb4ca2ffa4088876f7070a25c4013795a57a1a2a2642a9343dcb5437820b9d1094fd71b33803e73691bb5bc7fdab7e38f18681c691b1ba0f0b1e1880f793305c28c1e9e79969b9f2057007bdb96d26583d0fe94c60fec5b21b95d8b58fd42192bd49d9423c3b69a530a8409844f26ad6db2cd638964b93ed387c618fed709ed46ad05fa2e87ce05dd73cf473203e131761dc5e177ad23b002c6bcf0e61894c85c174e8bcd1964f65b99a9c68dad4df8a79e0a754075e544c4bb4e23c7336d168c091896e1e0eb077d0495284adc118ae5aaf55a17e8e835c977cab3225802d7216ea4db74b5d6655403032f0daf83c62c96e05dcb6c2db0bb01c8d4f3817787d94ecb6156e45556a3e322c2799efa92a5901f448cf3db2335649ce88f9545993c17e77a932b47b393826ccc82cf5eceea64ab426f22bc8c1cbb4c414bd70db19728c2994fe42b442e846bd58a4ceca586d010f948a96319705811b56df8dd61db7ab4fc6004cd3c73557d3e290a7d6592f649fa46d9504a1183b7d034ff8c28649f0aa36431cdfadf18fd3f133af7a3dbe228c87376188bb1f6f095feda1ac7a8142739e0891f98c3b0cf90df6daaebffd7f8c248453bfcc19417320e08141ff2b197a40fb99ab5ff23fdae8608567d2b2b9eed6397360e1ae23fb2549c7778a996b9abc44fcc7c79ab888d9ae8b70d3a0b2c095d7661a8fa3727d0e4992cac3604873a4bb782b130c6e18460dbb3d404b8b746e571477b7833b424ae7c2263e678d2feae6f409a2ba679fbaf441e745a2fda7c0381213922012acfd9186ddc3d428908b25548e87b7d9f5544738fb5e6e6167169b5af3d78135de6da25a7454b89486f21cca1adcbff14175724366151f63e6f11bb9b61a701666f69ffa0bbfd93783aec394533e2cfda3ae89af6abf3ef05d4955eb0d95be0263420a98987c5d188f23460518c272622c52e1e9878bf9276c4ca9948c37bb19574e96ee8381825f8bab9738d5840022a6bd3cc9b5bbfc2a86d74380ebca2d6be714b18999f951ea7e525a1c31e88e744efe7516456b6ca4b161a97639a7537c1dc82ee1033e4a9ecc4f7323f4456091c61a916694b772b8eb513abc24858bb5c9df29a58fafc1f8f384752733875040b074cb515d9f611f0a2595b1e614c0535dc38f8f6ec7d984c4f6c071e7b86c47bb88d13c292bc6bc66986f40f45f2b0f79bc319ab64d18fd032c14c31e832ace7b110e5e74095d681012ea17fb263b77679bfae06a8b412373f4820a94561ffd5ac66b7547da29a0b48b95fa2c0b44cadae134ce441173a1108ea21bf6e5b4a3f11bc1d883e3e7dc4b6d643688c39f2d1a6ed1fdf481dc120ff51dde19249e2910520a19a2a7d12b6c735d245763a65b8285ab069ff5dc160f6908b06535b1c1953d43a4058316890a945da3341b20dace153fbac41d5b67429dc95c070b15f4aa0c7e4aac18882b3ff56362df36338f6575ebe1fa1c6ec9703056ee04fa1f78ab0c23f90a793ffacb74e86d8fd0bf61fe0d8eca7ff4ec76a65057a81017e3c343bb878e732fe1b09d69727773b27f1b0d2017c49fa5884743c58aeb5034ae4b7b7ff4c2f87a39daff06260c419921e7e42bc3c2f8ed583ebb06ad3046cb9e12ca73d47a7cc6f9dbbec337e61692b37a3407f98dfe4cf266de8f30217b438000000000000000050d19b03000000005d7593425ee04864eba8626ca3716d7463328e32632efddc5fd583f5042750869b7b1042e56a06c0376033fa497daf88a74fa0b306ee28ceb588219cf8337e2da33f9b702c2970ec4eee3810cfa3334965e820214a6c0effc05e6faf5e012bd2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a63698e09e26507838ebea0459745f09f42fbcca96778d02bee7292aaf2dffc00a626be92531c59a99c4824b71669edf2eb6007f2eab802463cd1353df0f1fdad4955c515768085dd0d2897f1f21f52ea4745f7269ea26c8e2543d4eed8b4cbcdb8b12f14b88bd15f8e5b3b92876af3e3c0c9ae0d51c30d6a3c56e139bd23a3032204ccc9882c1b4d8134bdf41677733e496932f6b66641f9704579f3e0ea2816021bd53720c05e8a7c97766b5fa043614c76a5c79ec63acb7040239c493bc117fd0a07e0cd7ed4501f1b705aade9c1f43bda6e8b789a2615eb68cf1694ada0c8122d97604c7ebfdcd27938ac473215fd6ea0286c9150084ada92c6ad56f6098942c80215078eab432182bb6c4d5097e6114add4383c7676a047c92adce008b47f2286e020ad5cb62dae867477b7357f3b5e392683f92e7706bc983c885a6ac01edb353c0032a647f4ce44bfd8cc57755813409fb1a76cc310f616b6d37b17823e5826b7c6703303afd347b3753a7360307cc1a5a40f16595eaeb588800a17436602dbd432eef032818de664f7b987c807efe2f17ff3b63828edd8e2ad83012bf14d1bb0f670480482bee432f2e7338ccc09dc182df3a6f0c7e6e19744e51f8aa9093ecf033adc2434aee56465ae7c0d483a7a6160c11925114a2602ab849130a9aa3a9ea782534d9631f8a991fe7b8695f09ef6fef30152fabcbeb88503c38285395771fb949bbf3ce798b09d32f870659a7621dfab9d80faa7bda447fb86500352a366b58ff06464d42473fb0c0d27f2a57a37903aadaab5b62738f6974ed716d221dbc2e7a2af7595030f916a3763584d585bda7b5345118aae3af92b06737266085c50bb68bd8a587205d50cddf12c263d44bc9dad603c292d53252b8d20ac27de131fda70bd6c9e138cf19aee28c11b8a1e1d653b624277111473b36ebabee404ffa5ca5fd89a04d9177ae443ecaba6f7153c7e722131a75a7fb31bcfc4726f31ba1f03a13fa1e47d7af9de1e553657f393c58ad3652e93a2ba641fd8115231f02d9444f19c6387726ced89caaa0b1ca31311311057d050248d9e433f59bf6f859b56a18672ecda718c4bf6eccf2bb31982edb65812be1ad6329ee9159f65ab7d93945f490e696745e16991efd11b61dc25403516e7e39a4c63457a11e291fbc58c90f7d6b26b92e4f2eb50f67af53f89023d3556788f7bd5b844fb037d12f19e26445bcee47dace863da143ba4e2f1e51200b7d680fcbcf62216aec3817aa4782df2428785b89658aaf28fd0ca9b8676e0889c6556354bde7714ae9e57208af7e14dcb329f91788814014a01a3be6531cea888b9bd4476a4c8673dc83549f574cae5e397438fad7b9114f1e2cd1c40c5c624917ae4d777d6d3318550b7b92ce849acf9d67a9ced843f807da7b8b68907e35cab7db0330c043dbe084e58468b29aaa2f9a0a777b4cb23ae0c9d0c5708885b255ea80edf8a789f8376f83da23786c9673ff9f3027e77449bc8509755de38c36e88b4eacae748145ed07b6ca3c96b9aa59878b46fc15e32dd1d7a986c3671b9e788a0103fd35d74cff92cd111bec33a0c988269fd9fe5406dfe6fbb24395366fa03eb6fdd38d0c53c6552817b226eb3f739468cb78a8782e83fbc602583cf1eab509a4263503d2291735131c92798e578aee55b362f0308d221095c3d2f251e166a5d24237133a3e14cc3bea168c5893e1d874e6c417ed9245d48ecf23810414d846553c4407b308c0fca487f7d87aeab2412dbbd4e2cc9f5e40b4382401052845efa1681bc1d08efd97a557c09fdeb6b83e0b42185131bf6c3c008bd724f77a5677847719f217ca11cabb4914fc09e563b64c9a598ef36fcf48e8881f11e5819c23688355146629827f64fd1fca93bb34106eea9101c92526927f32d986c834e0ac1fbb324fe3fb21b7334dfa804d193eaa807b92cbb2fd5e4ccb14a5cf36aa02677ca9fd05e4f661e702f15bf80d22568f60ad784303c24d9a1f1aa4dc3cb9af203deacf393400fc953b945e7fd5ec45853b90322e8de8e3066a7a905c193e368c92ca3c08eb844ce85b89aeb5d873a508eb37ac13161e67f6b40910ea7d7f3f81367df9c31a8fe090ab6877a1e163cae3660e63d8f749f3766aafed9dba90b42a611554a809b34ddccd9be84ec6ff84f8c0103033f7fd6aabb8012b5080611332a5472317f701553f29b6422e7d357fee4c4c1f718d563830ee0c1fc0a177e6bdc7e500364ee15484ec7471a4a083db143bc232984d51908be51f40bdcecf855112176c6983c3cfd0d881dcecfff0d2281545c99db89161bdb1e97450d07721475595e388e2782a44acfeb2fc5221600abe26a2135ddaae3f1c2bbb91bb75964cbd2af6acd16ca21148f93e703f7d5d2deaf1e65088dc61bf6c5bc90b3869def2ab3103", "6a65536353", 2, 454520553, 0, "1c7bef69bb7beae814ca0ce56d531ed715bce5c50c685bd3cd692aa08faace6c"], + ["030000807082c40302a3c06a7459aa3717febf09a5de19e61ab99dd29ddaa2d0c0c135941a5be7531b02000000096300006551000063653517205bf0d32f29f433650d5d414df7173976fd39f71910e70f422daef073b6ea7b4cae0300000000ffffffff01f6dba10400000000056a52656a530000000000000000030000000000000000716a3c0000000000eeaac65cfacbac1ea8b63b3aecce84488caff719003fbb8201ab0291182feb9781710b2f172bdc59edb8150f25e2fc56b4dea01ecf766d7c303cba13c7f050c201c735723a2ab56e9850f18dc7f4a95aab5abc07a0174dfe8fc4f02e783d2ae900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000643109701f53593cfbff8f497cf881a5662cbe43564e9066346159637b1c5b142c20dd9385fc7840072b326860f9288a0b8fea26f703974234fd777189dca89a0bec8b691f2c7c2b379359b1c67f0e06cbac14ad1a5bacc4fd2e5d7e2943a093ad0cc67f3e0321c39028e29c50bf2183a4badf21ed5693aeb29e2070dd8dc1db0317896564c650f9b78b17a85cc67e8056b4bae08cb0a7030b1aeea8dbc69277cc030751a6117ff9fe324916152406f20a5690d1e584d24084edfd6c2edef7b16f9e0a0245b5e20335e68a47c54a07004ea5d2de7d66b4cf1179750c089b24aa718ea0e7bf23d77bb8768216f89b5064a6f000e91b9954de2bc3468a6eb91010642bb0032496a9e088a8f2c5422044c765a4e2ddc7b7bd3984434475c24426bedf6977e3022606d54bad80bdd44cab03776134ff546c53a097011fed79e744a3c9d0910b06021f99d90de96d0c6f3fb697d3bbee49dd2991f354495c917c7d107e1c51cb69220309cbada843f5ff7b7a0fbaded17356aa6f8493763ae6fcb5dee84908836885030200b4fe943cd9954b3f4417564da69fc519449d50b9d931df11abe6027722ef262ee892318c925c73a6c4eb3ca572e3d0a607f9a33d010cdb9a0e2e28fea0d11e3baecc7ae6129128adbcefcc742cc515db855f66e9d8399e324d4ac2fda282e8c9f96d4d98f94352d13e546c4b622ba8f34b8f611fb095a344ab87f2f7121b74d97886e0f80311f7b422cd9a1421a2559749695a289bd9353150e89410bb38250a42bac265230d7ab12aa6ba213dc3f8dee3f2485062d6280bee5869065400ebad466c607abe5b535847551d43bf95a5f0a35454f1759dbfb056f34c87b73e0744ff50b32d035757aab5f78b8721ff0eb627d1dfeb1253e4aa09c91c0ef0fd45d16a7727b9ee901224a72213967ca349162537ea810e487ef43f135a04cba1be7bcaa09899605007d0f51b9fa05e5b43bfdda92cd9c43ffea8caa470143b6804b25f0213f603061cb17eefb955fb50784288d7d5e3cf7830b8bb452bb3d23efa592f2a04ecc77444defad0cfcb281dbd33b2db8226027709a1f4c5a743052f7c9406f3b15a0e85aceacf83de01ca009d86f2b18b8f5f7b8f3a7e1cb11b2d87e3f157ccdb24fd25ead46b5e40195277c02d6078fd745c7ed084472f78eb2dbae0993f5dd9bb208750eae4a1d339395bb0bb29693cdf3f5f41e053132e7addf3e82b726e1606994998b20f0220305e76d7ee29e6d5c74164030d219899bd8833aabe18250a277c35aaf1c98f436ce25ecd08255b3117d3391d67d86e3db935c5f0f2c76720264b51ee77fb203d688da36e06aafbf9006c745b61a5179268a4e53122b20a73afe5be244e7d6f5b6d8ee605160c739826d3a6cced49598e91322d2e42dfb5dbdc5dd8f2bc4107395c26b0e698a74339f698b2d8090bd9e048b27a7f647c67228f94fb0aef939c2aed5a0a7c89bd0331b9d6a017be56076f5c17c2a070c2e99db4466ab21d3b1c7f13da3852a52d1f59c3d9195b820f17080d97d24160188a55ff8693600b2ecf751ead8f994a61fa3bd41b9b0a3edb3371203b93f14700ca25b202dd985d18b1844c76fee71392938016bb2724bae89f29137b7caa4066a07f9548db00fafb4dcd9207c8f8b7bdb38a804ca84c5024253385976c4e938210aa5fd540fd81812c1c5aee269ee8526e041390933ad3641990a6b7533284519a34f5069e4e1f4f34546a9a6b237da0dabf1761fa4604a7b05806b2fea7cf743be00ba1b0abbd5e0f7f31c3edc6052db409635fd30abbc20c9369a7ec7f8fbad4bee3b8648fefb15a4107fb1498738e40324cdaad8a1996dccf26c2357179c243a2998bd37cbc316c35da20870511c18fc43be1fd51cce94b534ae8a97225eb2890189d16cd33b557dd00ecb4b2b3acf8b953ee5139dd490d69eb98fa74e94d567f52127085adef4373b4335808ebfb7b637a1c825f228f2ab21240fc39a23fb26b1f4b5e07ed22cef18999e6a9a52e7bfd5caa6e7e525d9edaaf8235e3492c47965623c9df386649ade415fd2565145661d55138f651d2b62545d41efe8f2fb910881a0027f735ee5707de2edd7d2622940a34773022a8b1a5f155c2a717d8b8a5487aa999c2ed9d8ee6919ec8fef95aaba2b5d2a315728c90430539013b3f534115e42a54be001321c2d846ab09b001e2f3f7d35efbae5164d275429a3359562724429c456d421afc5c34bc29c380df1ffc730a7d8ed34d277b021bae54f772582c1bc664a47a9b183c7618b568fd63e171020000000000000000000000005ca9cb8b44eae2b5e29f050000d2e8b8948edbcbfc6edf52b85059996956ae513535913dbf01168f945674e270b7a52155ebe23c928088d9802dde7c3fc4a56790ce2112afd4a0e07e00a4f42b9b7b1b75972e8d2d65bb6733fa375cfff38abd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000abb897459574b344db21f1137a2b180272fe826dd260919b7c872c2aafc007028ececa5e60a03079952abd745284e8f18531454e662120f6e962ccdf6ef64072478b86d80c300b1df6dcfb90b28bccc73b5702e77cb1841ccd833184f540fd75504a81b344dd7a7ca6e111ff7f3410511c70c838703056e213b6e9661647720022aa4d7f71bf483ff46e37570b9bcafc3e62645141a9859579fe2b7b90287fb0303283b2f54e736235b3ad6940faeab153aee11cb8d3e1ed7a9a998fb831260e7a70a01a8c80fb23a0682e5f452238107ea87affabcaec7e20778c267668ffbdd3404d30e8ff512320b6911ba43de5711af24defe24ac9708de62a532c7cc961e6806020749c499bdea63f30444d57d411326671c29d405d4dd7263208b0401654c8a6e021812a63e0a208a64f5a8f331d74df9cc40f6b2b1fd3c2c565855d4657927c99c0308819ccbbac57cfbb161b2ce7fb3f17b4df80a147e1488e185898fe3fa55105b031083d268f0adc13f2f8b6dbc03efcbc8ea57b89dbf33e4619d0611bc01325fd603184d07ac1a329e7d941b3a7198f4ca272ba10b322cc1c26eed9148ebfb8e8218c321067a1e436df304366f91156249e0e5f0fad0d9dfd5716070def6c89012bf40478c2ed318e976e4d20880074302ab6e75f4e62200859c49b4707697bd0e3a7674170e3400d30f8a9f33a9cd3d932520ad6afa6eb873a8f18906a848462a56df2609c5de14641457a51c1ea0a58c758198312e62ffa0c1b4d9cfdc2f4ea5a3d6fe812a91b1242944863816cce8f781b50e96154a17678469c268c2026bef04becaa142139e20ec3338731415d22344b04486b7428dde9ce498bdca0f8f6743af94569ccc2e3a1b2d1af6e817a67829667515ca82529075f493f834515528d0f7f55240fa69ad97551f7f3e1e880d38f7d05c7fe1f2ec710c7f5653dbc7ba4d02f4580472664b52cb23510f2ee0c026c8ebcb171e141dc0bde79f06455949215d45a344d11279970d9922aaa3385f854916ad96d140b737a99c44e84df56fa4b8c69cc3964a6cf709316a9a7b10b5031562e473b78130be5adeb30f483ab9f15f2cda2bfa58238e813e4c95dec20011e0bd5aaed63a61ccb4bee1ceb0c487f6b3e44af536b3ca18edaeb1f5a7ed943d2c0173935eb965797886122f7c1cf094a107d68fb9391e7314881ee29e4ea74b01e9e944a9cd07822dec6068280ccfab88de6320c848ecc818e7df24dc413f3cfebd276033a70e8b45d6b4cc89b0b3bd9799efec46af4177bbab5779e108fb49e4d9ba3c1f9b940079d426e0d6f9c96cc4285ad7c311876edf8ab1eb52412337308375f94516102c210b6e92f8b45a81e08acd7f763e77474049ff9d13ed3dd453dd4651d89c4e55c11bfcc9ca8bc79988b402d83c793431344975114f937bb42eae7e6e468a00ee399fe3509fc3e140070f0cf38dfc89388ff5bde540b4d9fd896f124f2ef0c9f2446e343468224287848ea78fb23ee2e8164faef2d67a7f82c1ea18d8d3ca9aae9c5b9794166aa333ca2ad39e868909b2ca406663197ad9dc4e42604c2c86ed6b9ff36f4ee8909b3fc0bfdc9a61539b719482dea07bdf8f078fe5d00b7cb814b843969ecfba1147c68cb09c7e111314db1b761c345a30aad2585f05f03762b930bd09b0140ec7c209f61794f0bfb1ed7f23c886dd26b27cd424e7ddffadc9c57985005452547bc2a9df6ae09636100d4f0cfdda2d19a4cf44f142a30325b4f5e304a52ab35cc181fb580aedea16a12e4c2d2de8ae368adaddbb06c8b91727cebaa3daf5fb881ea169999dae417d37f58f906e72b673e75eb55633ce5fa5c42319b93fa4e5d0344a746feda2df891222a073674509633a22007a555e57b3d007f0b682b1bc39b5e0d61937d0de89f4dc3c4d9340da650ce9da8e098673f7770547e5c67ffe53f900ba82397d27414eb0b0014495f3942579f512aaefb4ae6b503cea36dba31fcc4f21b418cbfb6b62e467619c95519e58d7575ad7e7dc73edafdce7043ed86d35a52ae448017b67494838dfb9c9bcabf79b38a98174e3c1ff7c628dfc912e91036224bde034da892200b174ccc6d7b09d5e83963f3d5f8e8d6ff46cb0db98f7f46835de0dc27383b4296fa8f4b1db3bfa218078c64790a333c5dd5fc49fb92928b9c60a1f3dc822b069d2cefd26fc7972d83a5b73f53bdf737e9ed03cbbcbc73243b441aa0313dca7908abd152a80578f81ca1920a05b3ef54f16cfb48566768be4961e4cf76d122177db97679808fcb6b7447a55db45ab030000000000000000000000004b143e7e83c36ceb4dd05557dda837587742e83e4c2c29daa92161bd2d465604447aa656271b72878eaddeb45f48ad0935e3015eaad62533922467c0e7c241e09bd804c1fa63dfdcc1922a88198e71911fcbaaeb3f75a180f4922ffee380c25000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000baaddf5ef2ba42e80d59b7bbf63f675d665da8ec92805a3f89fa25237f439554552309f32f725bf02356520410c833d5b653d27c5082f762c97297a391106a8d03fb2b1296e944e88246c6cd7278f74e7b638bb54acd6dee9663fe7b33a4b295a1a45258ba2fa18059a9d575c76ea57dcc175b73346fcf1cfbea68f75660b03a0213935186fd960e15197bf9f3f4a19801ce1d52d16756a06912ecdb0f99289d76022c502e6784d1231271c693f0950e5e9c5c742180596f4d56075f726e1cff31360b03a6268f525cd1d5eb4f97d9efc619d8b8eed6f0572ce6480376f47a1dbfeb3d92f87c6eb2342e91447e0e7fa224d85d1e1ba2bce61a3c862daa877e2874cb3f030f9b3143af7bbeafb9e78410077e72037312d347f1f3418b80b289db6c9dc212021e85200eb94e1e75161ca77519f33d9904e0bb566263d657288ab50b1068ec1b0201d067576067b6290175150f9346639be6e9bf131e6e8da1c3d12abea2af85fa020d7a6d1bf95afb83bb0b042194c08142fbe96a1eb071fac71528a49580bb8fd70200326ff21560fd6afa1708598bb1c1f30c40ca69ea77a36c3b7385e29ac6364b5aad66c66f2b22bb906dd3f379b55461eb7ac4bec83175312283bc0e0f464b281b25a145d84e1ca6ee23ae64d7042d2148dcbd7c0da89024b2d9274560e1569ce90b64ff29c205ed9951b575b50fd17a9ed42f42c069f8b3eb8e64e033bbc83b6f22b44d6472c2797421f5bcc8e5f457552f649fb4eb03d0cb6d5894e388a2211a465a24a2bc685908e00036b4b912aa74bb8a14a9f3a25946a73edb7d4e10f6f18f0d3bfedfe4a80bb72e9718e93fccde1f3df30d5f7efeeb2add242016ce4a388aded775f504f82aa93911b1e7b966c7540360aa53a14a268b7e2961928aafb5580370217d4ce8dfa8df81c1eb29007aa2531fbd4332092573366e6a93cf4c43d5efd12121ae791a514c3291904baea8560d868f3b28a2c2411e5baf699da52e93b31ffe46c3c22a7778307ed94364ca1be2e003fb1d29d8cc0ba7d55c05eeb917652f66364e385c7cc2033ea2dbdc311ca59845bcf2c0a197568f764b9560fa9368c3e1363a61f2c37e47ff6e9d3ac55548c880594876257695d06d16330e43049f536eda02b449e907ef381cf8b3c302c5cf0697dcedadf7b29c5b998a1db62bf8287274b88ce60a6f23bf348991460b3201fef8bca2ac3e80b9205f84b8845fe26d4370424b2d73d3bed16640b3f1d50dcbd8b485021e213342b07a6d77c030b8a8815c7b0755096e313ff8c24ec99e4cd45357f7d588f9544dac8c686490203b644f61b737651712db17d00b2fff53a5991038e70d6f0d34d3735595648aa8f230390fb98441724363645dbe251123ff7f0c4e2140f7610c9201c2cab7cc9e9e38eb543c8818747612c46541649e0c26127c5f8014413fb422fd120ebcf5b0d42ac3836f4a948288e1aaff74f1d4464063c385e0c3808196e5d6ea6249fc194dcb821ccbadf9dda53791476b77ddf527dcf523648c452a605d2f5acba49d4421f32c0853216c190d7ffc54f46031278155eee586f863ecd74cc05e8772cc3850a5b3f7b7190c897b67d8272e39b015e8b04b471666e99f4e5dd1ebb088fe071a7f92519c3fb91151fbec59e7854c53362c5bf612c37599f751d816a1363b1cd33f533ce16012a97c78b567de16991a414e9d23bb718463940fb12f8c9e5ef08f8e2ec758ac2c9bccdf1ad3f17bc00f647e7db6350f3b580fe0eb5fe33633bafec6afe7bf35f9ba4fe528f23df014702e3ded233c3d93f80e6aca5f4d8790b24790003926854954d0fe26d2bb40442dda5be9ff4c7152943e4cd1561c163c2f4b0ae99748a4f9cbbbf794cb7b0988b8c1e257e749505e54585748444a5565e35abaee4ee723e4ad1eb5b07b06d6f19f09da93978417ec37a7beec0c786527ab255079f1ecdfc045a9ca18bd315d321df8eb07b6cc8c3a1be7e381102c83b2882562a196ae98f7799a60228bead6e0d4c089db2b6ba8f694ba9e468200e4582485d35953b766f15b6e542c66905e94b6bc39795b90aa591759cf3e3323f20d70351bcc84a1853146f894879193ef6c76dc54f60a67718c2df50f8a2aa7cdaaaefb47e05bb9764b4d9f6aeb8ac1dbb69e89a823cdfe538123ed82632f6bffbaba80ef056d624d15fcb6dc7e82d85ffeaa43189fdebf0950725f51564a1d332c0bab122299ea335d625a344a4579c8da3626d6e6a0ea9b26ef88b464f3840b2a1cf069e90a892c7052d8b63ae296fb56321fb7a0138f1ad3ececa81be26cd0f6b72fe18654c9bdc2fd5faa790f9fe69e99a78f95cf8b24fb9bb7e09159e537f870c58d26e56cee27e63bbb49f65abf5a9c540cd857ff35fc80067bc6c4c918a6992c46252c05b9a7b6e235b6c8484d5d0f", "656a5265006a6a5352", 1, 163497046, 1537743641, "6b7c8aa1a2240867e404c60191eaeb1b0eb9aadb0202fb58152ff8c9e768441f"], + ["ddb7a80d01b0af6bbf9879ff248f6bb203da4d406979e6b1a1024141aee90af0d922b91417010000000465630051be75a3d7014e70be010000000009535253526553635252000000000100000000000000003ab1680200000000d447a55412e915b403bce456581550e32a110178e29c564cac9c8b9de87bb4a6328113fb8146167485f82d06e83905350348578c871206f662cf4926c0686c4c1ed1fd8a15ec74d2d6c260edcb7d7be132535c1995d055bd1b752baf5025f05300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d92e281822a9293628ef0c7004da309976a26673072a3180fcd47992f4a492a984005846828b2264f732a440c6d7ccd70fce9a66d3e33a8030630c861707581176119e8a6853e3c2e0af41927e7b0f63aa4ea3e806eec13d07fbc751fce726fa27ee33aeb4c292f3bfe2346eb3d8807d1e106b684db8f5f89be6bfdb0430880c03209f67c7bafa5aa00e2d40870e81f1c0ec641caed66330e7a1b37aa8abe697fb02253f38ee75be92fcd301220da770d2e4f2d17bf0dfd8ffe09b2408a07310149b0b0300ec77563b14c54270b7bc18667e0789b692bbe3bc86016a31c0af09643163b7787c69b1bb6fed04e6e0f30e34dc53938b3bade3ba1afe4dd7ef2e25281df2022a9066c9422b03af44dd4efa22946f9a36ae126ed82654ad6e5f383e8c947a5002023f61f1bb485b440e18a466877b19aacb225bbc4a864e3cd2cc1aa35090402c030c97a1013bbebe90470527b9a207585f1832a7a70fb02b07a53df154e4a49f4d0301eab468980da8f4951e342ef4ca3a6cabb6ac4645342fa96d7b74472df13ccb0221474251f886415781c39a59514a84add1467336cc4717dde828f5b181e1cd642fbf5d9842795e5a4a71b71ddb69f130737b841545e525f3c8941c053e0a7b5ad5c00a80c2f44191fcc73e57c8b6492b4336e1d870008fb00507d3e4dfbd6d81c23b40929a982cbb4499860332f4435322b0197ab0ba6a863baa02cd68be20875f18a9bdea384f47b848668c2f4ab32ed0853afea74eaaa0dd860ddcc60060ba3f3ce4be3f24e865bd80251860601522e35f41f5e7a28696112db53306ceaedd739de019df47a147f9ff3e045bbf87510715279b38827347696e589c357e374217698412a676e9779078f8dabddaab2b903bbeeb7181cbe36d7f20369ac1913b1ceecfba004b9919437e65ab8bb4dd6f3d3d9b1929b072723a658729718725eeb7aaab8011f989ddf5ee2267169bcbd279985b42c29cfab53941fd5e1b317d1f1b1c8e6bfc4bd55ded6a566be8b277abe001faaad35b37d1ac9e3c7a342f26eb81eea7008a28773150e081e0f1440141cfe97b94f4f2f56bf57ceea30bd29806fc51839f1b55e3000590978bfcd4fc076f403bb0e3761de54b9e731329dedb711c4af5852f22d338f2e6d170b4a2d008a51e411aad1fe1e93da12c9abf02faff2798bcb0b8827b2cd191876b557a90c65b5a27e31cc754f791605ac9ee278a7cdcb27d928a1e4cea3bef83e4f81cbe38d46c787c3f29c93f39e5085056f6a475bca2c43539497c4705bd6d63cf54c766ff568775703f5db52173866d34ca2e2a124e78b591cf8817af2cb949c5e501264d13d2fbe8b3ea30a3aa7cf01f94af0cce840b7c226c1b7b635936dd2473f7afa4b41661e34d90a5b5603e5bff8d265e41b8fff8f82aad4762603fa092b042d5cc205696eca7465c5c7e10ca5ffc669885ef295fcaf3a6acb90d9a1fda9ce78f8eb0877c127761619615e6f325cad160353d84ac77ec815165654be6773991b7e6395d5ce87af03ea02ed0038f190d09acfb467f4e2febc58888595170df3e710507071b2fea7c44780732668e1744b9da45c9d2d36584a2fa7cdfaaa3126a497311a451f65cae4a6833f5a5dbda8dddd28946e75e7bc387ec1e184db8b1c57146efc292f4826a7fc875a6b64d4327b9da4749582408fb73e70bc6ac07f7431db1f6253d5c826ce0f868b9412829ea218d5c5d63c8c8c18d948d336f8c26a4b3b5020c85d0afa799195fb94d94e7780b6fc2a7bd5e39757cdf38bbed630474276bb657acdc435560d813b5fd6146b02329fc0a85f70e6fbb2e80a21fbae1baba07bba558376ca5866b8c4af7bd34caa186e6379c781036a085707a287f7030b3d45f5ace7d4362cd30e91e70ab3401326413e8e437b94df674c6562067eb6d2eb93d618239d16b08d6a52c40f9df8874c7ad2ae179179ac91ee524d75e5832a35182109ec156d8b115f49bc5e83a0776d4dc8b774f6e1c3c56091fc96df6454e4bc6b217e3e75364de42af7f50c0b825c1bfabbcb96c196cdc9ee5ab869052d29c083d09bbe3c99aa2eff618a901eddc7eeb4845ab29015b72d6707e8c0b40b5529195a6e401fadcd324e6fcc48cdb9926e60e698136b2c0294824dd8db55242e6de40c36758d8c2ee44aa9d75368a84f85f4cb612a8479907720fcb031b94da6bdfd18c468cedccb8251f6381af143bfa1310163f0db046b0f0b5e3a7879f6703b8312f2d1dfd1f1794b97d5210d296125564baa33844f6377583be690abcb36dd78551e6733381c1c0b488da6fde368714a6eb913aaacf326a121f682423be49eeb6581d31eea41db6ad34faffce83a3d03abfe788dedba6aa952c442f2add5aecc0d674e79a062f1cd969ac5b4f3960f8ae46a1915a210be4079719dba048ac06", "536351", 0, -158921530, 1537743641, "3620c8ded84de25d229172ab9f56331a0ac9e6fea65bd8a42abd8c1ab0489c9e"], + ["4d54744503251645f026dd4a2ab6b80fe4d0f38fc4a535ff0dd67971ff8a6854d0f4bea2a600000000050063006a53ffffffffc75befd492f359191ee380cac3e3a7067d3ed523ed56a06a1910e83ecfaaa00f0200000008510051656351526ac25d92294f556b4482de21f8eb8471acc05e5db39cc1265a25b2e8eb34cb306e998a1869020000000163ffffffff04cdfa270300000000016a50fb8d01000000000365ac008f0a95030000000008536a6a65516a6a008c0f630500000000096563acac6363535253000000000100000000000000000a48e201000000009da54701bbbf8f51eef7a6a078b0154bbbe175a93b109c3cec7544d6080da499ad35ed45b2ab6c5428ec485e4081619c6a34c70f4781bd946d67d75538676152edc7cb69ae7981211de405605c395d7ca0b6e5d74c4cfea74f85f6f1cb2b51cb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c81b40e0b8d9162651317fda2102e4fde2b6d983eedc83609baeaedee56e768650f6133dcd2ba62bdbdc9a150af343e4e03dfebf6a0c0ef1211dd0a9b89d27542ae7c6e68f6a1f66e4b345a14405afb64b3da93f6a1e2c5d89d036edf91428b0069e4d2c2b6fe2662d68d15ba0d9c136db91ca6799765f006c77ccfb224d9c0031108965bb07a085a5472af2fe982b8cb956da1e62b9f53bfd5fd567c152c9753030274faec23ed40e895b02a2bdba9bd4996718a379f106147f5c609896d1cc2000a05797040ebf481d228c123385808451a71e7ab515faaa0d45db906c23b1174a2df7b2da8c02d2b84e891b41a4e34cd97cc5ba0d3c3b8e306a2133c326b5f802a020a62b4ac86e6bf664b8d9cd74e9d6f4f9b705a3705a757fd90f8892136799a36020e81ad92d2ba60739e9917ca119a68cee1020cafbd4ff199ce25bd528cb7ea3e0227531092f825f864e9f4a6c51b5d8567145b05d254b52c2ccec4c831b445dc54032b9ac56ab151768286301589329a49ea3c1626b41ed01c887fe8f193c3b6eee9022275542212ae69049186c1d594baa511b36feae3ff5254da1c55dc10300eaf5805b2e0c85ae8e840a71cae5a973335d26b77b95648c73fa1987fbeadefff49fb75c733fc2b529ff9068ffadd91d858c42df583a9a504f0f4506f8efaa7bcb6414a753b3fce0db43766ca40b93a14dc81412ba3b4df24bcae4245a6538e81a4ce8b01e20b07a1daa7e1e7fdce139058fc38d69f10902779ae9cf8250dee4bdc44be4f1f03c42a31a2b98e6951aea158dee11ac15ce7b8a5a251b80129cbe92796426e6f8a75ec29456b6bbc7658241acacb296f0c0bfd48e1870f16dfe73323c9bed35725d2a4377420d4ee80cdfcddf1e2d708e0231d811c09829ef1049755dd31419aa0aa23229d620ac7553dc5783387c682a486e70799d47c4a816f340cf4fd303433d06816c7b56ec333cfd1ac030dcfc93cf354782591570c20c4211b200b8eb078fc4235e16e7802b02da0fbab0ea33d51da8651f9c243fcb4639372882b4a736ca35d3727f968ac8c8c841fcd4a4b4d402a830044f71d2e34d834ce73b157a52cd5c5ad94cfb11fa91d343387ad43816ecd3e8100b1083b01ec5e3963dc665799ce2b82804d17c74b38f4ee79e23ffc55bde11643970900a2bebf0263ef2b1a131552908fa88ba0dcc65fb978660100dd03b6901b239200e96e39e73c14d98da9d128272f8c8c0838be0c1977b202a316162c1f1cfa6cab3964732bda1b8c9299f93ad5f10755fd220feaf9821218a77e4915afdfec541c0cc74689bad07ab6034ea1b3f5decd3ad4aaab03ed47a3ab12707eefdfb05b7791d7ed1aa676cdb16780adc9b0eeaac56e91fbaa24ff4fd229010292ad5ae65cf23472ab8ff58f1f76e320727235b0b5b5af37c71888ab45bd24c176528734cc901269341a1700963285e7ffdf6bcc991fc28cb51b249737a75d30fdc7a5a59c7db885c77cb1a64c6a351123b67ddc8840cdd7a3a1d5aa62265e91fbf65322832d8532ac7a60fb45c07461c2062e2953782adb8520495e970858f055dd181b2aaa2cac3a436c0f35007c34851c4cfe0476f9a267c69d936acceb7aab138a0ef12e0fbb34c9c0f7fe1eff67da230700863c0f27a9c9740cef45a55e2eabfbcf055856f9afb5360e8d22fe4f099fc06a058d0b307228a40b85754dedfc1fa54fbf55d3c0b2c45b03e6b14a2a55533624af1e0437706a7066a1e8d9480826a2f1c0bf80ed0531d2665fdd74e662c5cf3ea499404b3b0f011e665d76ff029edca7e390c0edac6d2897fad365e70008c4a07dcdc1414c4335d9c532a92edc75f96d8b61473a4f4c24695ff2ef5dda0d7f4ea7fa3e4503df5c7d27b121750839e54abfea21ea9288994ac7fdb6cc0d713ffd0d86375ae93cf4de6ab6a82029a56e959f6ba5828c698f2c3161b97ebf7cc19341c62b962ecd6d87ebb065461112a8f5698965396b1afe4b4c2ff5c5cb9fab0b27f75b2c73ee76adc01adaf455e2b6d2a9c1d1b1a56bcd6cd23ea69c73c23c682011b4d3c46007e68d16de892c0843d2cc9c98e5b35a86ec1eb3ecc0cec678c50149c892fcfbc984d093a3c9535ce27dc3b0ed24d9076e0aeaed5803f3e4e9ebd58f79c94b84661e4893bc6f5dc8c50c813c90612abc98cfee558fd8dea2b18618a9c85d1a8aded1b0c83fb0cc344efa83525753c99ec5ff3e07958a66ccca8b601a7a08917b6273a5a1c34df40ccba27d159d1c4a01a8af82ba766967c7a401b3f43262f62f10c8391c383ebb7d46ac6811b46b313e53a3eb40e9167e064ee835738b98b1fb679738b327b4149252f733ce46acef219569db070f137d4d12ec4e63b3ff7e3a2ce9272f5bd250480c94bd90ee6554c96f92ec0a465dce8e8b8e6b02c2ed8de3d80b", "536351", 1, -671021817, 1537743641, "963540cfcb0d91bf34f5107cb6d476627bd182bf6ec5012dd0687d489faaa224"], + ["2e6b696f03df66e8cc85e37c7589774955bc77f3ddd3916890bdf1d77ab2bbac69b0648f600100000004516a636a3d2e795602c2c534a19b9537d76897027339fc538aea9687f886168058df1946b211759d01000000025165fffffffff97909b06a0536357af6772040eca4513c22dd6f0f7ef5cc6977b7a02fb64cc30300000002006a7369ac8e037f08490100000000076a526a6a6aac00ed5c30010000000009ac63656a515263ac51d34ee302000000000251650000000000", "53636500525251", 2, -1560523293, 1537743641, "5eb40539467fb8b917bf8dcc84eedd75f5b861ce8a9dd4556048d0043a6ea2d3"], + ["724f167302b11544fa822156dbf879491f786670ec9570ae95b63f93c70c0e154c472e310c00000000060000536553516504d63a38b6b350e46e083c95015bf13da719db12771473f9a266287a37e34362aa0a9c0200000002ac53be41f51e043e51f90100000000086a5352ac53acac6a1894750000000000090063ac51ac65635252c6cda2030000000001533989b80500000000066a5163ac656a00000000017310f505000000000000000000000000f4073840fbae733578f5c5f02c33764dea19aad4c53549533b96f2e81a88357585a99a5d82a8545c18b504fc5ec308cbf8af257a6a8ccd1ba4ef1cc2f2927880f04554fb62acdaf6c6ea1156f7ccc0de8fe5bcdef65f44488bc978157c2c8a2b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ba29392c9afca96582f84c96b58e90a31f7428849bcadac55073c4b02d7d154e238d36287a7f9507c4ef93833e6fe2f55eea5e1c23b9f6a782b882b9122cac398a00f8718f0e6f080270bdb2b05f5e0b64b5f860b3ac28eea5569ecaa605c26ca112dcc42820ffc47741a76e9fb8aa08e74546fcdddc1df9b884cfa6a0dbb15f021f2cac4cace83dc5b3ea347f03e09c5f90d42fd8a47f58fa3e7c697cdf0e0d49020bc130a9fdf6a3597aee7f321d88869e242a875ef48a9a64cadce94aece512d30b00293e8b43a1ae150de7e373097b7654fb46d36c3a0054dbf518d2c8afd992cfef24e2b8bbd31b7f2f5344c147ed46a8943175603eeefd7fbf2c18ea3ed796200312801eb5ff785220af955db44eb8f67aadfe704b42992187140b7e9fc2bcace9021a15ae89ea02a833fb92b109bf6975ac56bab793e5962a97c41bcf41bca12209020cbf8e502fdfe5486f7471456e5922f6039df8b64a7732891649b8fa0e11ba1602032df895dd38fd6ac567b20c9047d45542968b2906a48a559f86801ac50d34d20229b06b18ea35db8c8ac5117136430830cb3710a8b2a335bfd2cae360d8990da1c13cb887e9758127c39b0e68e895261dbae10a10d10008d71076ca1622cd301a1cce6f3cacec9283b413b79d00f379c68c6765507806d380fee3a0dadead4f67e409b6c1290a70bd356396f97f718356826c82d8268a82855fb09162816c3b76ee5bffca2ff169e9671b8ee07abee6292ae0599941280725278ea0d4a1d0a7eb265e36e1009ba9e2fc4006b362db8d95a4589619459bb40e744dc5c48bb17616c3c6cf24b8bb6ae0146c6f99f5605c57b0f23738f17f4861dcb846ee14f255e47dfd1cf3de213ce0c2a5f2b68993bd4788c3bd499261b9fc3b203c95dba37a4e5ec32922593acd791aed9d03abe008a8f9cc6cbe95d05fc0996eccd9643ff8df7da9b9f249dc7d690dba98ee85bdcc52675d7f1f3b15580e02b084e6da2694e766150fea4961519bbe0bd02c90af12544b6e59abc6281b895336c4095f2679028aafcd198adcf2b652a1a1cbf951b1af6b9c4040426c04c91b53eb033303e7d6955f74a1e562a6335f5dab0defa8922f7e657a8737298d433c09a4b8c8fe7f89c4aaca310291436dcae7bc4e01a631015001e589742c3ee1fab397256ace24e64ef68d9478706e9bafa2e46349671d4762cd4a11fbcdbb433cebdf29d61af86bd82cfb2e6cde9eeb8c0977b6b40d2804f142f86578fe3605a6a4a2a13427e10539e8306f2d3e8ac08f8b129dcba6a58537975f7eb46c2c62821760e104fec47b5d4f43fb98ffb68c5e2e223d4a3ccd99f2761682e5cea46a0b4b472278083ecf8cde23de70aca88b505cd8e1572b66b89880523e15d9b5119161211a5e9e9eeb4c1590d8c86e8281d2b05117579381c24f3cc613c1e38a3d26a3282a4901ef38acaaeefcb4241f8173b2ed7a312282e8f4a448168b08a69797fd670bbc44bad7eb15378fa8c36d0d613669d0c9ab950f5d8e5d5ea56f00ed2bc8a229cdab2e5d50e842308c972284b8aa7c682241550c8841a41470abe6be00489fe487658534989d64869378bb1b8d0ad3cc89545b2409e881a69e2e8cec6350515cf02178a1ffcf6a5b76e177b33fe8b01831b27cb68d3edc4b6cb8dd979084bbb1c17e68a51d81656e31d92cae0b63d2b9ff1a48a14e90b6234803aaea084d3b1518c3bdf05a952e12566570120812ed8b533efcc34668e2e561a5d6d6b87ab59fa058eac8576b9c571c11c38ecf469273ae6bdff72e1dac3d1da04982aa31d91100ad28d9fd8870042df3b15aaf3929df4c24ddfb52edc63d3128f977604c88948e8ebefc3f3c4f9e8529f274cc238862dce7154cc2a9c742d5a13b3c58c39e22d939d13a8e3b55ab6a8cda1de20a9f683c10df6c36ebdffdd5a3504cef7ce8e30422d0dadade60ec8e645df1e88a5826cc0f90e0e31477b2388baa78fcb64e795f99c212cd5635470b87fe7e75196ec578f05074fcb3ff16f3a17be54dde526da6819895633dfb5b832b7ba106e7a90182640bab7fdd325ef2612dbe1e9626671a063db96449a7bb7314ce9ae4930797408477d4f483f0d843dde6700af1862ef037e8db1e754d4dee946eb58222efe1b4dcc2a7e6add065c429211dde38d83bcf7ca54e50618cae0a19fc4cdaedca06640c1fa1cd3c4a5996e3bac620ffba6191bbad1c06bc054b7d7eb62aab8dc436bdec585f7d671365d3b186da7741a6466708c0e5e4f67b1b8aa942142e852920593db677b06ebc97952935206eeb8d35e2a2f031e8dc7fec28cc66df203dba9793eac63d6bde5a91a8700f74cdbe2edbe62b5e7e512aec3f69892c034b594e70f618ac6dd7103cad45a2de74fb85dc65f8f47615658b90675baf1a3be72fe17cc49d8c21450c", "00535251635251", 1, 2039130554, 1537743641, "ab3e859b67f65f7615dc8ddab9be3f13fe262c6314e88458b164161808c226ec"], + ["030000807082c40302a8b52e5143881e37f6b1fbda6792701bddf013e4e1a8f7f2f50b5810ca490c490300000002acacffffffff0bf45e0df179afd04ff6c08bd38c3ef35a96be18be0863e01db3487fc4008102030000000763ac636365acacd23d35dd03a6fa85050000000007630051636a6a003480a80400000000016a46e03d03000000000453516500000000000000000001f4c18605000000000000000000000000159f35b0a09f29de1838d09ab6463eeb2777251b49033899bbf851d863d91debe6c03a442849c9f2398ab8f82eb0f5617f6bed867a0de5cadac66534832a08757705e511817c9fd5c5bc436ba13aa55ee008202f688f57033968b80ea1575fcd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008d777caac514e6041a1643cc795a1c59a3551f64c1edd557f192a7a11d102a8aaf10027f6871d441be126c380ed71e635f29cd15280bf89ee92d84e9eea1bf06d78ef5c50f72a433c42fd5ef4a3d2299cd72fe54ff59cd22aae20f22ecd374e6ac6f10efa33c29bb2bdca1a8fee236e9a773a48b6416621dc95e4c7646438a95031f41a6181a95f29934e5bd94cb43a31dab65d96b11e730d53e58bc1ffebcffbf020c953a10f374a283d0852bde39d687c24cc81fe57994a08a01c66f696108ee1b0a010f765283a9387cdb28e5cceeed21482cab15f4070420859447b20398918e8faf9de1a4b97fec5d31c8d994533c1c31b8026ad1115ac202eebead031f7d8d95020585191f80455ca57f7b276539ebe955f8b23ab0742e772d514888fcc027b7ac03078bfe941bb277d3d6a2776736d0bd816313cf2c2eb9a9420327ff36144221c103028a2d16ab6fe92c9862730fd38baa78327613122143b67e00ef7fb73b7f0f1e0213b975aaeec9b25adaea4c1a13cb0b3d05f2485f7add4f438d3c29f898d3881b02109e71784899fa2c7a839244e4c0585795155e85f61d28c8b4b314bb28ac7c37e67ac3b581db95d4f840b5023ca1938af72a13c2ac5274cbd9afd384c59f9bde131395258ac90325794f1a8d44af52a281c3d1d564cc10465dff5d1d1d4778a15e5519df465a4135acd38055234296f3428e9ab12cd0e8140ff7d6e85831c185af0e748dd3313c6d246669afc1f33f457fd30472a471b0185f462a2ea5a2bca920bc529249f52e950187e9aec3fe9d3cbec4ce602f72bc511cad05765b7022181497520e9a563173260a4c2d44f5d2bcfe2da105cf3669c0680aad23ca17ee99311b2cffcfae67e594af57793d0574f40d02bb39164a6861eddb70ae52af21062a8a22e1cb8331fcfba185eb1691cf49f7b930024f445a6739a3d7925ec742f7b718aaba529e2468a78ff0c6da0bb817320f756cc89bc1dbd9ab0bdaccf6ca136e09e9953e65212c2c1a5691438d26e1d5ac662e74ca7795f69d6c69b1664a3bead40b5a1d3722d6ed4a65d1d90f0a126adc1929704120bd43cd2ae8c6bad51394f69212ff9f3e4e9847be1cedbc8eab716f00577056c23da364d6bdbfe1ef0cd134e69f8a35083a9f4e3f8fb851182b1c28d025f902e36067984e4f9fbe780eb0629164e822a60fc44528dbca6374f1dfc1e4b9a2561dd1bc7576068736ed0bed415a0c35d7d96e2efbf6f10927f38079ff37736a3c74674b39408acb5496697088e70596b0e734f010f9855dd012b31c2d46d9eec6e1963a0f58e8bb36ce525658b891cea0f15708099869fbe6f605cd6d18bfd96e2e78b94f4c7cccb513dbb50f78078e0cb6466231b51ba1676413ff48622bdb5bf82b82c8885dd7098439c1b70d55238c844f7c0ff2a23a58cff827141df13add7a277e160414a594a3dbc332b5aeda5837c2130f3d2be088350d9fb7b8cb04ef93a85ebfd8cf3cadf2a78345e75c6bc7a7ae6834ea76da304d3ac110a9e0f1bdccf087a8d531dba568e8b8fa9c720195b96a35df695abb5c39a45fe70279ccc19529c50afc26a77a430b8bb488f43dd3c1d6f95b0df91031edeabbde319fd48494a52bd50f42e3c4be0bf6094893406111bb812bcb36bb709600ce62c1515b842edcbb46ee1f0bc960382a4e48b19d9a87071078d157a7e0872f1edcd8c9aeeb46e555cddc2014c8081b511bd87c3db815680891f72c7f7a2fc9f0892aa4c908f6c7c45475409360ce0ad91ff7433be7d9c65866898418502be43c65906bc978bb7b94dbafb0e96e61220cb384f7f02785a3ea0c6bc9ccdeba888337a1f320cf33fa30858ce7884fa58d1d3ab47df4242eeeea2ed90dc8e4dd47d2ecf8d83e1b40fdfea24881a658fd14e2df64ba8f1a3a93c5db2b75bcfe171b233e6358d732402b90c49fa2ba23d4fb585c20ed7e3ace680c79db90cd2cd34dddafdfc918476e803bcd75552a43a4e89702f1cfacd679c9d457e3437a00d1f268f0808d62bb803f8904bec59f2d507073f4aedb81d2705aaf69d4290f571acac862d5a942d3446b6eed08bc4b044fbc60137e6cd7a7bedac09ddbe9c9fb4d6b35180f75d088906204a32453b2666a90272f767588d29c365e802ed388eebc301c09dbc3d41f8cbd12c3f68c014f93e240cf08166612ebf33d247213ebf6a6981901b3a960c97a5a1798edb1da33b856ef4cc876c4d8b5b71ff75d631d1b4c46d7723572b82056b3fb9e3f915134da3f6cb69c3a27f0a1d9de1aa8a7d4ed63f1fb651dfc8c95c39686bb44b160d4bf8fd41ac5ff306a865ffdc4e2388862c0f26bdb0ba40d3ef41e147de947c4ec7a223c1003043767cda4c86572685be88ec33e4a24a115b5cb43d5cd2594e2699223fd5e56d265439f34c533d9c7584e6a11cd02", "5365525300ac", 1, -886154570, 1537743641, "bfaab46a5dac8d8d64e4de897be74e6547604dd713a7d1168aa6c535c967e30b"], + ["05690b1d03c0312cb53c2a3d5ff7366ec3e84e02ce607577cc267bb3f648963d92b42d64ca03000000025353aafe420dd112a2c947ef700dfc236fac680947a813e8b4e8c0f6a989c24a4a3a17e571f7030000000100c96a05730150768fa818dc53e1b69ef441f94310a928367233b331256dc3d6eef3629f8701000000026a002d2939ab02527a480200000000085152536a51ac5353e4dd5f0100000000016a00000000014fbe6c0500000000000000000000000031d3caacf9325bbb576034b8787a15bfcad99e433651aebbca47d70c63eb27c8dcde5c7516c3acc1007b65266c97d0f44d224da447abf34ef361edd3662557bc375a30cc7d109590afd8d988beb33a7303c960fdbd17f0bf58fab06babb0b008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007a31e081259cbea16fe5f670774d79bd76c932bed961efda23598968d5faa68ea93f5f52f63fd8de216431dc73769b73d30b2800104e5467f137f445a541ee177f8d5e58a0df0150c5199b488456686e044549161a482149621289cdb5ec96556b7dbfb8f604ba90dcd8697aa89a029c4cced26ffe005d655742186a276eae5103255e8f54927e87099a5e6b0e811d3d06bbeac2f55810dba125314aa31ed496d5022b00a2fac567c0570f666a6efdcc03ce59310d027da5746722f24a4928e38f6f0a037f8acf99d1ea002b4a1f6dd486d26798efc5d349b66a579c95d629370a20a1df2192d7c1f553ac486477ce4fcd336f8a7c2b6da8b6d56bab5ef889b0ea123f032af1dd5c155ec2a6158107003db1f2239c40319d70d2a7a0126ebca429ecca2e0300c708dea67fd74c58b359869e49e30fb126648086f76f40d88b0ccc3f4d6566032a0176dfe7b907e9c67e3b5cb1b9b5a17fc827805641b6db8bcd0070e617b02703271b5ffba20ed313f92dae47dac3c94a693f50b0fc6a15080e0fd0403819984e022612acbd93d39d94c994a602de6334903af2ef8d05bdd19cd42facf566d05a4f0d9cd6c7bb6161b5060f27c5694d030c2245d58770f4a5168ac5b1754884f3836bdb96089b38efa56c2036e004a6c55ab26eae95f61be493708fe3fb6de7997abf70aa779849f201130d4faf241e1937cc2d03b909c72b30744b1dc997fba26abcb4b61e2e3353ca6a8a60af1b028597374e109f950b161fe6f665f3823d2cdc9c440ae5776fe21f4e400afa3c7a1f58a7bd3723b48fdde44334a27e9fc7ee1db5c533597c26bfc94473d3066968d0d8c49f5dcac6489ee61bb447898bc1d7db8955c2d342dc7c361f388692cf9060afe5e745fcf89e859e5fb4a9434c35751d544472d30d9f47ca4054697bd42c1ae5b320b7146d55df32f2dbe2698a5edb34e416e89910c7ade913ca7a0b049a8d55134f40d18c0c73f82ad8745f0b9765b68741d9d4cb03de377e9bafb14a901b8279ac29affa2b0bf260caa1f48ba6ae154e714921957a345e387989c300828415c305f998e4c93269934fb830d23335bebe30808df6c6e464d48e5a702bdd664de99f88dcbbb7cb4ed269611539963d450209cd9da684825f2541bb09675f6d18b9daa8888b51f9cc1931e77c3b2699afef8d2e2fcad23f2dab432b6b9e32fa8e313a7a0cb20fd556ab7edf4b5ba69bb8aaeca65099c6b2cb658592d66d44122fc903a013e8c48a268f8bd012b854cfd5dba69e0e1bc993ffafc2bfc097ece81993fe712c705a958f7f7dec5f26ede6ac812f5e89179eff23494c3a1618797f4cb6571b8fc31d7c3e0a88fddccf07144e21ab6289856f0d3c89d1c7b949b2981457bb3b70663147fa992518422c0a0c86d3db017a07406456e83153084738475cf7c27e984592a6a59ed163e5d526a3615d734cbcaa5936fec76494987af50519058035a3c0356a2f3be6d487e19e4e96c7760e26ee5deb0d8fd0d7815ea229ac7321e20b485f01562c2ef026d3d690413f649103f554723cfb132e067747c4f190a53f93d6287784a3011cc5ef6199ad14c5287e94eadd293953d26f600a6a003dbbf4f09780d0e9534f16ddde35dd66fcd49ad8ae097a5f0ab2457f14323cc4b901926bf987e67fe057195ef147ca1087e4821193bb4130cf3f98f89f679a2a037e5565f269b5035388be8cb3445501ac0f0579d5ce1993d94d5ce3eaa7149a15bbad441c58633805b25c3f839f942bdbfc2f4c7fd367a111b48ae9c13f80ed3f21294d1b859eaa9e9ed065d40665f39a15fb32952d9f3fb690e69bdc4500a30a671b0c75a74b9dbbdba1b954dcdca12677b6ed540ddbb32bb4c1db07e0d80ad0d9afffbfb96b74ba6811270100a7f093b027886b0a61e9fe091175a35186dd1b822be341385a000d651567f5f1eb92d9cdc74b6ded19213b44998c8f0bc05a083529adda83cb1ef68dc7d3d1d778e33f0fd7a13db7b7d5f2ca59fc310003fc977eb0353d0fff81193a10f13a8b6489cdf5c146a2d5397cff49317c6fcb29eb3f327a82244f0e6c4c5f0ba33a9e16eaf6f462173496ddc00bd0bc7a3fcace9f070ae6f1bf4f2b6baef282ac5f5fca0810ae22c08ff90d0630a3b32d0c89b0992a8cd451b2090b6a3be56264e49b0d9e728c3219ae8b7b8e411cb402581a5284ac05deea700bb54468f5949e73a076bc855543f8f8e62f26f2558ebd265f337fdf3ec001ff898662925861464b4d9903d86c3e4b7658cb91668558509f980908469b2f6a01d952f7c57407b777df8e9840ae0ebe711b4a50d09bf98a9329a590a13545c1cfc9da7790448aa5394f765cb3054b8cb1ea51187bb941b17774319e01501c48f806af64068fd6266c548a6a0ad6bb7c1f6a357a72ec280e8152a8c58f0d", "525263635352ac65", 1, 566654117, 1537743641, "37b459ff427884cefce0e4bdc084c53773dbf1bcec78ebe69606fdfd24b3ea02"], + ["4a3fa20e04eb9a5790b97bb7edd9ce1224dcda7a48a4ade7175e365b53586113bc4cebf13e01000000096552536a006a656a007254b67499a5e2a03b6aab39f7792fb175b1db5c1312914ae927579d7ff818d839195b290300000000ffffffff25a33ef5ca3c68da40fba3c3b47db2307a8aaf0e8693a59a632d585dd4d5a4ff020000000552516a53ac39a7b8014d26f5a7ce68668ebae14d30b127ab06c8ca2ff9ca71a15ab7657458f3e553c500000000066a6352006300cd99f42803add254030000000005515252006524bd58050000000005525100635210e6e0040000000005526a6300650000000002c301980100000000000000000000000051ca70eddcfd9ff945df8dff4403aaa49a6df4045d31820a5085432dd10472bf3d558d272d3e85d5810dc3938e4f152d1cf30a5d38b281d450c4fb64020a5a43544ef63bdbc10825e1a6b71dd87abaa6ad8d804f6fa553539b37b8890b84095900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000599749e97bae7997d00d4042fb8f7f98c160d9839695a5c4b0cf139eb43074b31eac7a02b7be4fb3e6a4acd8afe6379aeb26691eafce9352966110c380c90e9dbe120a8ff355a5ac9d8dbe7b66868e6f99079676ae442ebb3867e87222bab1bc4c069245f4e5314df1d372a9883bacf4ee375b58d0159e78c1da6b4eb50b0432032b0133db8313492d32152049be84d2d3b36933e5c2a746da21cb34d66340959b032f17dd12d1ffbb1d87679c1deeab7241e73d23ac22752758a9ac040cf107a0910a0753668f5797539d4a9c214f170b34f9f252ea6630d7af933a98229367edf35cb7384402b9b5dacef2c4c1b8556eb8a9dc21a2b6d98859e1c7d8c9ff95273fd8021c668371067010a449ab6f3494706017ed80ef7b79612d4a946fe796d6acfcb8020badae97c3b167953bc154e94de9ccee67921e667d89d59720031810f7772a4c0308abd1763c5e62098ae0da3e20ef7bd58958e6e82ba9d69175bb2e0dbaa70d220309c667eba8dbc6202afc22f0baa47d14b5b7aebc297d4a5151bc9b592c191659022fe5de79dffefee22f9c714ae50e9debae06ab02c81f3203592b684df9693b37e476a2cfc9d91daeda5e42a034697ba8b74840bad34135bd93618cff2d1380943797d5351b37ff4d94937e25f2794a1634e3c6b0e14c2d0027d51dc4d35f9df95efc2fd7bfdd6c085ab83dca5ee4d808a08ce63a0d6391dbf74bb2ea154cc2e3a0f518855e7386c9d6d390bc77d47d16cb375231569c452c8f942f690c868e678366646c2ea64ece6d3436c9f8cd3e71dc505643ed896b6518efb0bdb7e41c79ebb845c54a4ccf8067828cea16bef4b29ea57bf08674a92071cbd40bd77ae1b7d654a6cdecdabdcba77abddc6aa479a580c70c2dd11f7632063259f5212047623afafdce0abe31353a846e0d0521a98df7f01339008ae6fb16639a6a1902db8e9eff0af692e20418277730e01331ac22d3109dd7220991cdf50fee52dcb82c50fc5c9d2d7125761c23e61e006ad38c15078dd82fac776b4c1cd9ef9110094cb7ae71baf0cffa205ddb6a2a44a617dea7a27cf2af400c604cb21dc40f1de0267bfb2a7045ba8c5f2bb5cd66184209aa0dfb4efc4a52a128925fdda6ec5b501649ce16a83d9f1a1cf7a2918d6b1b21e9cd1baa7284c273598af84994b273ebde68d470c78c31f73f17717bf2ca480c9992e0d01c004d3ec1b803e7261f3baaa1618d7621eb07a38ad7b3212626b0fbd14278c2e2500e5217f9592e2bcefc4691db207db9e9ef43fa13841d8a9a647c2987433eec62cabe40bdd1bbae65545c398c330151de891949268ef486fa122c4c6a6ee8c02b1596f2c431665cd1f509b4bf691cb80e8d26e0575553a809385323fd142117a1fa6496c817f65dc903a23302d96d04ba16b03340d8c2b9178f28de258e9195ab68ea980508114f6e0640fb6d199ddea9fc435695ea5f61af9c850302ce5eb401747f42a2170058231d87da960afcdd678606e72962eb7d6fe5ed1445d37cf05fa76e26818a8cf0a9ec0151f6c67794c3150d6fdc61b113952b79d3062e648e755587e738cef0d838f779a6c55e7f9b971431f1ce533112893e533d6342754b01c64e2224c1a805114ef8f36debce30536e0331bfbe9ff77f8f149694e86a3f5e7d3fdf1627b12cdab42f41f4bd853b02615163e042af2cb5fd9f7ce29946e6d526de4fc02198935ad9016d5086642ef0c1b6deb59a1a1d1c131e55e48351d9024bfa723114b8a4c97ee106c991f406206d19d898bbc75ef63affe43b06e4df625781ab9bf7d15c8b0b4d0beaf746bab46c9babdd7cab4a26e7894e46f2bfa84a2af4d0feed367dccc7d3b6937974655bf6b8d5d87782f2fe64a03f5448bc971c0dacf0a8031230bec13296eb857f43c51d39b094fc9c0917388392633ef6ec3aa323fe63afaddecb5241b103ce2ac94db87c3e70886e12370a48a6eb06074d5d507ade5965c9b615000d9f760f415f4b9ac51b45430366884a2febb16a099267e92dad5fbcf767ec798a2d4d124405d0bce8b3e8176b798749e315218bffe61b4d901e5e6610ad90777e229b46737354765e34aafdca869f7a68627562a59f4c8c704aef56285e239de6bccd64105bbad930080e7781e53eac434bda050b7d637b4b20c5c26ca7f4da36d0f7f768a4f3acec572066aa3111397cb0dd9660b7f31a37fd3dd2f3e546a064b1df5b44324aa3f313d86b9348831dd28eefbd970ecb2fcc3ab2dbfa5104a6513a3fe21d6bd0b3576da611d8790285af05fbf726000000000000000098a6b90200000000b6e725ef7e856c6958b486162716967cba860a01479833abeaad6daf147e2669215c77a01022104effcf4a8375a121a27b1db288c40bf0f7bf3873b84e088d618484c66ca86e213f90d5715c32e8729b6111b5a2594f11c08e38c2689369bd2a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa7a243061019724a92b2dcc7f9adcbcb0bb2ea8e0cdf03056b6dda2e3be7d5cf1e6abf030048ff4fcae6e294c41f557539486180786a652659d475d2ab679ce3ffb0747f611342aa00ccafb0f64737856adea302d33d0eb312dc10826f58a9b2999d9b2b69745073ef039803e1be0210f8c061d39603c79f30e8786e1829f53022247873c840dfab6f9f8bade3130f151dcba4d57efc68a041621ae7d28be15b0022bdb41b24724defef3726e6893f3a4c0e5684bd755d8726f9fba5833ac0542820a02c16e3a42af8e8ff2d7c69f08b92553740dec860273a5879edffd2ceaf156a2dcd4f95dc8953f137b6e7e71c73f716e0b0437cafe1acd5403d798b488eb57a3032451b70b387cf3a0481cfed35eb31f79f0da068cd092c9851db943155cb0b17b0225802ad3d19bb74c446fb86c388d42470ee3f3022d4a4d7c8ccbb33c7cec6d770211780e9e80815a4e0e37464dd03a313659f6287f0c7aa746fd50f649bbe09fd502181c1e80b18332ddd4ee99b389cf0b3100d4340acf597f3f1d9e5bf07362be8703186e5128631c040fbd7218a3d2f7221e4d0fdf2744d74c386e58a7fc0b90bfeb08a3e743b7592c38a347a2b9374d5c987cdf4252bf3efe1718962d25fba8dc3b966dfef089d2ff78423e42c7d7825f3ad1f66258c7076730ddd0b3c0814dd4af1b33c625b38c42b2679eea477105784ff3f5837cb268f54f0a2949bd6e56d1ef3bf7f4d82cf63bb424f75494b757a658de30836fc7db0b23c52c7ccf6022fd6a65ad254964e5fd412de9636bd574c67f5b12e2e3604f9e3f74e6c889508c5b345a92207345715481847507cc618965e0aecc032d2616e0060f3a607dd883350b65901576efbdba3776b7d668ff5c79e0f1c1ae6bb451e803de13bc451e1bdcd00cdb94162a84622811d8258b162b05b9173eb3838da36fc3e26e1e6d05b5e90a170a99fe966b5928f56e5124bbadfa1b01ff33d134783fd173e50f149a06edf51efad298cc718c622869dfab919a048e5b0be479107bbd82ba7179fb0dc8f9a79c28afacb0fe86ffc11fcdede7bff8ee40fa488dc6064deb4184cb0cfa7424380d6c04b1c12b13bd0c456facaa0dda8b8bd3870efe45d4950a4d69041d31cda257eff84221872b9ed2f25f3f1666722b68e5a270c9f939ac3753cb36cfbbd91a5a5c42e8fe172bbb5ce528ba3af8035d70dd003bbd7868407ae2aa883664ff9e147b0c6989ab1ed164cbaf320ae5890bca14006eabc39e8d37563c245e33b283539690fea44f894be2ae7762a109aafa5330011191fd722961a85ca1063b9c440eb8e21941e932f8bb94fa7c93e3c2475a11d8edfd4dee1a6e83ef2060a43ca5adb7187f2065080f5fde07d14c3f12c3f07749fc6ea7e14d6a8dc72cf97aba24db97e9d77b1bd290d6bb8856ff4e5e8703f7fbde2f802ae84348d2321e9c600eb9006adb6e0bd41d4a94ee458c60d3dae2f7382cbabf69d7a84ef0c5e4b9e932bd4c1ac18b0d60153dfc0ff40845ff2064d9c0addc0e67a7297641ceea49c76da7ab5326db78c9b98c5572f8ed6c97c29d791c23e4217f49a192dbb3cd3b8e9aade3a1c99ecdebd7f0648298a1877d32aea17e0a9c85ee1d891dbe2116b2995d764c2524e7be5fdc6fc96da305c14aa025cc5af2192e3a24f0c0696739173ee0e306a0912e38109f4ae220736567a454f4a3a2bfa0ca45c1b18b3f0e8ff263afabcadefe21ea79e6d8d9c689177362a45f9ac91bb054b8cb15071daefeea9e382a9ef58a52d454e0c2138feba14f681065d59d042379bc8112b19321b1125979a4c8c7fac80668acbbcb726688f4ff0fa9338c4d8195d78f3a3760a37c6145bdeef5a2cd9cab6717e884e72291b9ecd350a97999fcdb21907b68b171aa8aeee11755645e14c6167abd530b1fea9354f4a8523f4b6130a79a9e09ffeade00121b75839f348f82e384367da85f2b0bba47807132a9c4e21a13d98010baf01ff6cdca1225a573f7947c273c27e6421f0a333f4f5ccbc596d909bcf9931b78080927e5cade380614912870a254640de54b16c2cd2e8dd084658509e92de70f77c02d748c257a6ce79bda6f4335b247c01863f980ac904bbee3a915007efb942b2559b0ddcff9915bb969933001b12d03e9a0f83e27a5fde20157484f25794b1143053e3465e6779109cb08bb0eb1319a56065a0e29042b1eb48422e17e9e1ca34c8a16875517bc097f603910799fd65e96ddbd1a8a5201351b60e2581e1a89d49ee02f00109bdecc00bc500daadea8a4168682c175009d6c206b82ecbc3c63abbf00c752760ce75e6829a45ffdc720b01cb41c82ff20e5b12841bffb141a96f4287822784796d979bd1ea4d523ce3e2d5925020e334249756c1909a52f0463b19fd7870eca1d1061d57125d369cd5e989f7a0807", "535300656a5200", 3, -881347552, 0, "e5305ccf2365cf6bf9bdcf0ed944763dc8a3de5ae6eeb03bd54bea51a92c5d82"], + ["0e05400801e255e9a0d1f8f499c6510f1392e7919c4e3a0c9049741e4c6300460013c2cd9e010000000965516aac6a52ac6551e154397d033e27c1000000000003635352064dca030000000002530053a9040400000000045352ac520000000001da5d78040000000000000000000000004ffaea1b4578c918d6b5fa106505959fc7f31a323e396e18e072acb127999bbf7db000f411a2f34bb35a0a596064e0c7649ec786b8e734fec144bd1c45f01ce81cb1471231fd2f3e2e3771b15f7b2ac1afa6a1d3dc29dc07b81cb3812f92aa84000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006bb0e0be89c11191d38467fb5c2101968915ddae8e525f9376619ba7aad21a2d077a4f03c62e69da9202a67a18c205b75f303af53fa98be4c3b58722e427adddae5b973c36993d35e814de8680f69222313dd55ad1885d1abdaf5b48649a970eaae1e271d949bb6bf9cf7a3ad8a7b3ce16f8a19656962dada5d73508318881a5030b576e9eab2d4b9c3cb89fdb1df5f6ad46163aed3b6720cfea524aaf585ef0d00312a3d45dea26466c46514967382afd77a4fabcf2bd899d25f0b1fc96a1fca6230b046d4c309f8077a7b3305f654b88704df2925662324963dbd15f6f777aaad08801bda7907e4ed63efb2a9d3e3fbd739e62724cdf04bbd417c9f346147c5cb84f022d5fcdca72187009fa58221b29a8c4d1dcdfc91c015228358d892566b7665e07032b00bf42ee92fa5bdf6c848977a4b95707b35c559a071da515605267c75397fb0203095761edfc4bd9402ee1e173b283b23cbc6b9cb49d262dbbae69b6382c39ff02152e6809d1aff00f65efb90dc7c5b93976f641a04302a37d2b9f9a1cd944956803091c08caad3c141a03196427278cfc3a45e22f419acacd731f17e4bf4eef15e0f2e45fc6f8f04ae79374d690c81d507eb9b8facae99bc66af925697ad0cb5e3ac2c63077a598f1004a5d4a454c0dbc4271a89f8000e9fda75f5ec8440b7aa330f99fc1b2619006dbbe027299bc37f41e76c82abcd29080d86ed7b1ed9e3c40c80fab6f4d22bd172b50e72089cacc3bce787ba032a78fd3370e5d8f35bfdcbe296c266f0935d3ca4b7b0477e9506879437074a06d7a0d39844e693edaa89037ba1a4f2a507dc00784565468e33121652175e0ed0a2443a216c80c5d011581e64172d914fa3fcbf1df2dab36f3943f4e5e8cbf5e02dcffb5148da255ea2b200cb8c6b362ab774aea4329ae33029c7978f584f9cd54f44305599fc5d6121a15bddf19865f7f0e48fd7139378359b581cbc14b9fe123e3d7148ca06a3d14cf386720f2d401ae8615e1b6b97c3ab763f8d2b04ffdc2680312d934ded4f4502e9d5ca394c9bdc55247dfcc819926cd37575cee4868c86f03d1ea2d8c26cf8a469956d0e4ddc31f64055d7669d91818b5ec8ee675dc93da9e85a1eec9a7a7e3e3a270b84744ef932b4c7ac4265253f091224d0880a21fc95c815c93f3936e48bc8557372d02fd59885dcd1c2330f751b51c58f21ea5b6060d229478a7d0297b2ae645e83e8e347f919ade1383e95b55b92433c8013cc095fa9629526e37ea2045d3914e3ea2b8acf4a9a58c77a8e93689ea1c46329984c143deb7fe9d0a4c81aa9e23f4b0bc36dddfdd84884f499470c48cd23aa46fdea7995224bad7fd0366d3dd958cb3c221918af0735e69c740df164dd56a439bc30d5b48b57e857d0ccb9e5892b55e8dd73290df9c255f761d2a94124290b53c32ce9cff3aadf8c41ec1a4331e5b4933b8a65ff6b6db39c6c0d61fcbb09dcc84e537590537eba0255b5c18a9398daa74fcf9c324b3299ae7b6a5124e688b632e7fc28bf51a5cde3f59af81c335429ce3b2b362f5b2fbf2ff5b1817327b896207bda926b3541feb7f70e81e6787a3b7c39afb91a47fcf194fb4aacec1d40a7326efa222de9423a32e0b9a40f544c32e82bb247b0482de2dcf7eccc1eeeba111b380e055fe691974057703b3b475bbe426cdb0214b0f18ee079462a252df53020d6cf1f44ab17f03b2077bee18cbd2a6d6a0b3142a961ef1450f16effd8f8b0ce90b9d4be3620ca757bc950d096728472183b207550d6cfc99afc5dbbd0314c13e3980618def545fbde5cd5382d4c2a26c3d076e2b4ba2536ef665956bbe9ad36e63b678ae2b017b0c74782cefc1d199a9857a567c57e836088b9e534a8db17730e0ec7685111b958a75a397e0731830bd7b67a85df28fbc715dc6a0532b602a30ca69b3e645083e18b5c4a4dd4ad4221fae71055de24348e39c8cf14d8eaceb483f5316d4b6e84afd19ca6fc6e6275a4a5643c7f0ee7ea75588b934375f8f7620f0c74131e6cbfadd67296a7ede83848d6f7533c58940b6c3d61ce0fbb65d223d49318fa7d81959677f5d85cc2820bc26237a3f0d8b0d2778f4a3b0237f82722bb655576c49c2eb6178819e3bc301898dc977f7de8b96bd43f502793aa31d0dabb37cb10a64497a25c144947dd24e4aad989eb7216936210989f59d5d06ff579602b173b35edebf8c766c0dc620c96db179a03df62055a16ca13b3b0b4e3b094a33da84f61d5f2e376cb10e3cca008748fb258b32a3e713810cb1800b1a9c7ea6b1c3c634b59eefc612c018ed0d0b42d2fdddf1ae460c3dcc7cae10563ab2c071f73ff94fe446ec3b6138fbc8d8cfb0d3cba5ce901b65c712bc257b17644ab8f4b513c8b233cd2c9e0d2c96c51ced77eabf00fd39de29fe801fda7095c19fee0e", "51630063656aac65", 0, -246848956, 1537743641, "6b304a56710eb78c13b8656f3902862cbca27efcc14aee61a12f8d43f6fc62c5"], + ["030000807082c403034ca865b3501945ce0d53c7aa53940a944a94fb5ef440a42cd4e44e61e277cb530300000003005152ffffffff43a60ee0d50aa3ce074005f54bae981e9996c5fa1f4f4d3fd515675e4478900703000000026a65ffffffff10cfcb3e2dc29776f58dd01993d4a355ebf1b000ae297d924ae21faf63a801c603000000055165005300ffffffff0135e865000000000009005353655153526a52767e221e00000000030000000000000000c13fa701000000004fd98763a0d6b6cdb304011e2a5e693b63d81ddc05478fbed670eb86dec868910069e309176713e914a806916a81d596f8f7e9be98921c510f095db4674810146aa55bbb05ad6a112dc82c1cbe60a836ddd341f883c452ced12d7a731829dd9800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000939c909707ebc266c8624dab2af3dc245319ed3cc62260490adfe6758a49e7e77d874f0ad8743f647029387ded100b14e5cc3c210c0ffb121ae242e1930b2877f9481e3cae0b5b76b864a4c55eb68bc5fa4767180f32f59701c5249eaa51682a00f68858aff3e380ad060cfb503a694c8e06fbafb90fbe41dc07d50fb579cec1021efe85c8b41607fc0518e811575a2055977131b8eb7cbb0ae270adec91d8136003023e91e99b64bf7036ec55afbd589b95aedd3ece499b66e9409b4f9043ddf0940b07143ed981d2f0f616aa79e2642d583dc4eae2879b49c3ce3374e68a6add286c3ce4fa3e5caa0fb1387816458018fc4106b23b08dd55e1f9e554efdaf73df3e8031a1559c067327241220c6d67b6a94176eb9586abd99ee3cacb43caca8c0bfe34021dba2371f7517145090167fc3f277320f642530790d8e638fe4da61735e337cf0229ee33a4d56e79018738b1c514e91b54e72d2d838b3a6aa33552b93acac570c1031ca27c110e6717c9f7481e9fe40ca0744b74e5af3638eecc77317377d88b7b0502172bbadb4dda03d4eb0e308260d152f5df3dfe291fde0f2f52ed04bea7597b653f34bbe89815170fb1e8540b12d7efa7b4a840adfd2ecfea5d9f945b62532f37be67e3a1e7e8abb7d137308eb374a99ee10993a3fbe71e78d7fd5928d174f345737312aaa1761e80299d5f031d14b5946bb0edac5b61cebfddc185ec28dce44cd67d4a414b3a0db4cfbf9ae3d7ea164e2b0508662a9252334a944e8e15ec03d0ad91a2f8bdacfc93bff76ee0de8d504c0241818ad0371796f72766bbb283674234d4f5f831f06cf6e199473a86a9421218333547a164bb32902a08595ce1205e709a4c981d2e8bbaeaebfce505373daf494dfb559c4c7af09f0663ca527c3d31e4e0aa68eff130f94138576b7239b3c9a7c5ebeb9c554d0eb12f9a356f2f13e0eb89bad7209e157849ad53e118f8158bd23aa2eefa7fe8581712c85cb4a2e2609130df8476a5b4749a30f16500a6f55381510d0226d4d41b5300fec7b49c1e845b8db9f54f192055514d14f521b90106d7506d84d00c2930c9c3e6a3838a680f2ad1a1a78ba7053dc9aa6cb8e422bc558cb6162cc6417430a25bbb53ca5782c78bf6ad6f26954d6301dc002acd993037f4f6ab461a0b3a2457178c778850667652172b30226d147d700197deab4668e388ab0c8d444e07af2010eabad680631536a99283463dafdede21e904b2a9535c67df827a2b8f1ef82c8c9db95dc0bd30c2102c323edc3b8a436c1a29ac66b7fbc0116b9941f5579424ef90387daff402bd96aceb8ec8b435689d9726e226aee063053bbef37ca2af886575922ac499284f1b2266c0e3f160771332936f3d5954efd69b49e3a4bc01f4e82c465365a0de8495bf2f6846036620a02ffcb286df82bc153c5c5bb76e232e88f45936493149945bc00c508cb58db86c3ff26d39e5330f42b7a40d3fddb75df95883a7f976c6cf068749228f58260e474fa014e3a81ab75fbce9dedf827052b76bb6e98f2ce02a875b9827c0060b4bc46a5ac9fc11652dde450a642d5ed151727613b62e9db9e389c640501842182f6941cb3fecdff464fbf4880a1daa6d7adbc63090be3e1539636b332dcb7e495e9fc81d76179e35092a4dee0581417ecd351599e2cc05a36ab4c99e5bb85fb4884fb3c411d1b04b4f523b798b886a8d55e7a99f6f37f8d5533adcdb720d8d815e3ca7ce35a1ad381e4fd4816d41c1155f22c3b602d69e52204965b440778dcf0de8c81fff9501fcddd1a4b7349680d0980bbdb107b3464aa46a306f94197a5ec3d65ee3121b7c0c56722e50fe25e6e3a551e14c23305cda750e1cd41171184b0e5952b380f5a4df706c5a8fb8c83d83463417e1ac44c7f1fe499188d007db914e20cfd0b4bb06e158703837c572001da6a65d61fdd17efacbf331e7e10b8691ea11f977b32504b3f5d94bdb80e586db98fb463fc10e3dba7a1653ad818867a7fa8e02c3b34567394b4dabb41bd983db3ca3c02cd3b500499e92404a3850313b4daeaf3ff458e972a313b94cdb4258b0ec590af414f53ec5f2573d0ced91b150bd90199b79bf4dfb79d94a8cfedc02a16bfed3bde11bccbdb401bbb1f07fd6d2da99be02629e129023311d1e0f023ff1ff03016482a3515288ea5125a1ea8b79858d977ae025d003589d6149668a804d853e6f8cad0d7d880829a78e5e070b18ffe8e3e4daca779d889165e59c5b0b03a30de393d8acdd3af753207eef60f433a7912b31e2d9e75ad34304cc56010000000000000000000000008c7cbae74e85123fcb5ec59691fbc78a6876dc00a548f14af8f27c5b6be85fc2f5ecbab9a32cc49d19b05ece11e0d2becea09d6554842e9b83edf764a2c1eef1f9e97a9c137ae380dcf6b6d20bc5cf863486138539af290b32dc86cbeaafc758000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b2092aa0060051d17a82c8e2f5fb65f95b03b94837557f692bc918f767d96651224da972ce08b23bb22c86585f18b23edcd2001908ae2d4997b9c21df4a6203e463008a28603b35aae1f7e437289cf7b0b6579de87b1742595cad9edf72ad660cd42bfa142eb738ff72cccf3af85a865a090984ecb9791bfc5ff9b25623a95f0306140203b0d100548d2a97934ad69c93b03a88b84089d8532466af86e6526329020157c9b91d357f9727fae89e952ad007f6c93f5a4ef86e66d28b7cdfa035a43f0a0451afd0511a5dfd17be5f75a9bc2fc49fd3eab752188f856d51eb26d23eebf23ea12064412a2255466d2205dbd51b09f5d7d04308d6503c92c851a9e9b6defd031a08b27d1b3940af684fe445f43c7acfe8f2802ec5f097a73638a3ed5a2d4745022b1a0d00a6ad99c41f92fc43638cc54a78aa2f8cd42f66598b7f26fd72b4026303174d57b3ff2dcdee06a39f7bf6fbb7b6c973bb88e7ddb4fc5db71c32f854418a021e7bd813496ed628b6792d75b3c52374840dd9104818cb768c8b7e6cbd7fcdd20203b2a9ce184d292654eba4e66f88e9b7a7d4958bbc1094b0dad2d274784bd7f049fb09877db1b51b83ce6ffc9b7a2bf48249f26d4c053d319c224752c50e422f6c3ed659ed57e4831f7aa9b1465f7369475f81651f59f79290098fb458050dfc96297ad3634832a8b3e60806a76ac0c4e4d9933671a09a8aeb8bcbf13b0197ff4b379f89841ea77581ffd8ee7b9adb3ba63cb1ab6ecfd2317abe42a30f3bc2e28c92ea7b6005f6a05b5ae457302f492529593db41926e9c5375feb78bdf1ea03f45b69e3ad876f4d7f6ab2a10ccffe61c91ab2de221a087e7945fe29b4498eed048fcdb9d2179c17f7aab22d526a23a0871f5140732cbe2f357afa51a13dd0d06652d2114bdc1239f9eecbd61d47dfe78a33bd45c6737e1b354971fc655baacc3742cf72dc731084ef4b813a8840f611d28ae73e59ec51d9fa66dc19596e2829ee554599fced207f01921cff69b9ad65d26e8b423362d8aa62be97f613fbd0b4d2fd9b08a486dc2d5752c4c1c4a89463edc590210de9bc895a32c8a288836b4cb2d22832d78eb9f1e35f699169ae9fa72776aa2630b5dc334d081aaf4c921f6a5aedbbd24cea52af215ae256583e4db7f9d580ef8ac3cc786664f2fd7712e9725b9e02d8a8c0aa592de2cbb7e82cdccf45017a79a512e97a8f750a76df77958294b77e3af4185d5818825062e4b0964a247035e72913eceb73f89dedb5fa097e8851162a9ef3a1e8ad34652c11ed7a52b997bbfd13ab68a518023fd3fa296cf806b848485d9efd55a6f2ee818225f54f1fe87e9e9415f9091ef66c0b48558dbc8d413bf198844d2fb205f94e177e6be3c65e23f9eac6d92c9e3b8dc90404258c1c5424b4de2d35c2c9384d59f1bdf8e1540a70fa95ea06bf16582c0a1b068042545a34863e7c5de7f16ff5ff831b2dc6f339254ce9b32c70004c1f6e7163a41adadb9a38235966d18cabe7bcce48711ed747f35eca36eeea775bf9e73f4322dcde4b9840890ece9f183f3fd4ea4d06372ee43cdf25e757467679c2a5b4dbf0e44044097e5331b9d03199d75c93db86f8115fe540f3bc05a62494037990c46ffaa37c6162116144cd5ebc972594df5bca9700c0d073b1c9fcdd3eb642d8c31e7ff6b975bd364c5cdede4e79b64159dc21b7c3ad008c7e0e9d4f8b22072d6faa65396b2cafb7f3abd17086a39299ecbe0eea43c1e3163ea21e6a62cf99b817a485a7c53205ec3d861c0f9b6e4d69e04525714fedb1e5b68fe93a15a7cdd9d5bd4b6575aae3ac26c2214c55e626b4732a26b8a4b24a83a27d155cc0a4a456e12b328f322a85c06fae087bfb773043814f33cfc699a8d35cd40f9e85f0a9a830c72b76e29f0aba1e282c47d85906436dbff4edfca3dd9e5fdcd33d057daf761ee407e7fca390b436ab9ace8e3b5fbbb04e6da50eab153f022c984a8be7d26ea8a863ffafc46de9f984f186707ba80362fa645b24d94febd3157d0a69e2430f16ae5bd49860a1ecac9bc8ec2208f557065ba866718a72e3e2f09f8c09d90b763b595f1627aba32e92ab1e601633f8e983dfc55d70d6774bb2113b84cc5c411b487a654165f4952ae0dc9c65d5f2b0addc9af0440b25804ca0664578f51cc858c296a994cdf7c7f04f5f3ef7a3e664d49a1e8e265567149f3f8fb9db511f007c9e40f531f9825b58ae5a163beff524b3e6a45f7a10cee2c656490e98f616793cc1127031ef1900b0caa61c6cae5a55ff21dbd79ec1dededf020000000000000000000000000485316fdbdfbef38179e14ed235387e5e5562f38f08fd205c2aaa135f2c2f7ccde5a0dac877ed76553ff850d260391a0e8ab541e69760ea3b151901d6869097c7a0bcfff040fecb7926c364fffd4d906cb384ff135bbad9cfa88a63edfac2a2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007044972f383f08ad8a449df1bdb6b0f1ebd472fd3b5f0e3bbb886896747983622b3382a5a9a384acd546a0872ca9e6054ec405c4b4e2a74aab3f47ca1315007f711dff4c72eeedad03b25b9bdd89bcef4c9ab2ff82a6b2987d97da93a70b3db25f92ed9ce033ff8bd08a018264fdd4df03a9d40f12246dd907d421baac838952020305ca35183e6c3f6055b146a4a6ed8f7be977852b6d70d276ed64348db2ecdf020cba3bbaea73d9095fba8bcb0979419e209756c80a23530307322c40247aa55a0a0552ace7d23da034a435a25bf36c437fed992f7866c058ff1d79af09fecb89ecd1926deab61889fe9ee19bbf929d4d54881f58810db87a91ed536a8baae9abb4022b19e0156e617bbddf1c17468edf1b8a1449f59a88dd4becb24fa990c55b734e020139f06248c2c16c98f46e5b52b1ee655c5abe6c7b88eeb990867a0fa7978c5e0205657e7bf8d790c9f8d56cd58d061e1dc4b7016d82d0454eed89eb507183cc3b032ad70e80ad48d95d30cf64d79d05139cf1850e10a9dca26f7f97d88b9168e80a0218a918cb398b53ba3df932b0042201370febc61ff12fbd2d692e6339e3a2228eefc2eaaf9e94df53aa839c80a091338157081d357f28f6ea6e597c4db697d56f0eae228702c82ecdb800ed86c184a32f8f4068692d43d940bcf7885eb5169ea3260f9a0a8c521c3489d72427d6b83a527ae496763daa8a314d2a91d30d3832e375751ededf144c7f646bac07f9bb9e16075d8261b17086384386092a14a8f587cea75f175d9b22a9127b2f86ffc9d1a7cf9a6b6442c29540ab6ca646dfc90d174542fe94355d64534d0a0534296de7a5e146c4d1485dd8b19ee7a91dfe1517a69e824338cc965d9e999d86fc5e33d7a2fb28789e4fed46c397a285b1f598156ccac4fe5dcda5ed7bf29a27fced7890098097bca2ea45085534a79383869c30a25d1654a360a159a06bcc4d217bddb00b61959b14c01d8f619572f6e50fec68aa3243ce7f057b0d42965721803ce5042e6d8872ac0de4c4ce96e3babeaa67e41b1d77071107ea0488bd40409cd8d43f802452e64f92e4d05ded375c5a26e41f8f2e0037d26615d30124ce664967a86901b19aaec7d947b74dcc000b49258e7a6b3a4469097a2b3068af38896208fa44c97c6132b1c43d4fa46704d0a6d53a7aa555d7d04ffe45f660f159f9cc0e82a5c134aeb8dbf62d60ab2ed80a6b0125685205b2955e5c500f83db5085252211cda34920f970c5eb2da8919e66a30bf2cb79b9db349703693afa4305f8e06a2cf3309f1f130705324e1544b846c91a575185dc6a5db0267e8fc9eaac245f202e6a3e8417c64fbf56f54e1629c716cc2698e850a4bc1a5c1771defd532ef3ff633a9b5dae8ac2bf199f19c9f6f4f09f1b3861a9402322f0d725aedf090ee9c5079cd42e87c27ce5aa0a2af2cd0ec4515a0ce58aef26bdafa224c54bed1dc103aca91946e77b51190b4446c390d0312112c280de6b6d9f723edb2fcc1fa95b35f02292f87d6154a7eefb55046f636de5c9e08edbbb17a451fe33e6413464bd512ea8cad96b13cd5f9a66a4c5bf37d9e5d86ba2ea2d1d958f243b655736cd5857e2e552076d3db374a457278bb8240d240f37411d40b558fdfac7caff9b1149a6375b3663d7b721f85935f54fc3607e0472ea547bce98c9b1552386fa7e7cac860507c7cee40b1643a770a3a1695d79da58f2638115862eb63d29229e7e41a86b0f858d55085c486c4a70ba8970728e3ec22c81d9c063b56e58db84352e4479f5e4f1963a7c3562e27b4905b433c9495537be42565f670cbc9528844c40e75043e543889a30d6d30c0c24a127eb08f06c2287de8b9996532c273b9d375e92ca9155030be0bfa7c0151540233454eb04a62d6ebf38877c8530428f5d6730aa47c29d99b9c7541138578a0fc3cf7cd8fd6fa82ebe823dc497967e3682b41173987d63f47732e0161656c9d5977c4059c5389a959e22a7ad39dad183ad6941661e7462dcd868b306e8da4901bf7bc8255130b071f2168ebd3565821ce4bcdb1a24f98f346701ae89bda0b4b58f76ce63f63ddeb1384cf79164b32cc7184385f49b717e02b28ab2373e9cdfaaedddf5c178b4e000dc154dce428f04d97783e5e8fee232a735f9fd91415103cd026381624ffdc25dd15d6348a6f77da861a1b3a05c94e51aa3fad612e1ff7d21a335245bf98974932ee106f42bc9105191cba8e7c08325c04fe9395b767f311a1a010ff407a9e61093fec9e243d4c8aebb9abd047af50c30adbf3e29d58748f1cffaea05fed285bd19d98f4ca8b03225b4c8cc13ca42b09a2041687d609fe11ac533e01e7611a535d55eae4ce8c6b06ccbbeb2b067c605edaf87111f70e02d576fc823f7bede0877d716a7a6c3985fd7957d763754951c9b055d09", "", 0, 2118459688, 0, "3b0706ab7d70416047ad839bed798295f2a3a39405005ec5a4bda32b9ec1bd04"], + ["d7d3ba3101c64c4944fa0a1087756a1a796831f6d47357ac10caa7cddb91ee5dd8695d3000000000000553ac63516a0b52c0ea01d8b473010000000000117d340700", "516351ac", 0, 77313838, 0, "098e7657a2bb07ffc05849bfc7a59f02b3f4024cbf853588ea48eabb3e5789fa"], + ["030000807082c403025d6c6637acdcf641e82d3b9d6fe507776e75a8ab4ab959a6be5e9c057c56695401000000085165535152ac63acf199203a491f76a78d2c76d09befc994413e1a8d03144d687e660024b36c302dd6c4e94b010000000652520052516affffffff022eb1ae040000000001ac869b000300000000060053ac65635356924ecb0000000000", "65530065", 1, -562238084, 1537743641, "f94476822df5e209cb08a6791e6d0260041219cc0d62ba20fc9602017a1a880f"], + ["030000807082c4030389f0625f94d65b62a03f664bfbebf5dbd80c7c3c5b99b4a5d5d641bbfa1abd8f0000000006635153655265ffffffff9bf5d723fa799a599e2ab500fe0368e1cf0f0b0121e7d066e330b76ea9f0d13302000000096a0065acacac63acacffffffff0470f81403c90aeb342bb91c17d4e83b3ec35af1fca63bc377a04e35a760322002000000003dd6657f036d7cc2030000000003535163c51b7c0500000000046aac52ac1e46aa01000000000553526363656631c618f31af8a500", "6a51655265655152", 2, 594957091, 0, "c01132901397b50209aada1dba4f2f398383a2d039c446681566a6c95d548f1d"], + ["d8c6484504ac1d91be3242e30d7f60179dbc2ae35d0f9bfe4c83a6c98aea568412f619e6670000000007ac51515200516a9813dd76c50be0ae170ce155cc84935a92c1d7eb606930f6dd80dd6ef87e1b25e08dccdc0000000007006500516aacacffffffff240400aef0bbbce38f80596ab22402b8cf919dfcf2172656ff2db01af7bbd3d70100000009ac6a52526a656300acffffffffeee1d46431624459ef5fba8afeff7ecc030ef394931d104c75ab4cdb1ba19c33000000000552ac006365ffffffff04a7649a0500000000075265006a65ac63985b9f050000000007ac655365535352678e73030000000005526a00ac6a6103a5030000000001633a67d7de025dece804000000000000000000000000c03f61eb17e859bf6cad227f2b9c17050fd90ba57d173a8557bc08539f97111a6ced2f0f18e6e82d15c20a7d229307ec7bee78b24af83b5b6781ead029ee8dfe57aa82600cb1c58e08884911c005f2689b31178b6895be6d42609a7a89d8a3af00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2780ebba592d930cf791443f7990618983ecbbda04202fae95d6ccb1f27e540bbf5b883e10fb3624ab1ef369cd3d3afe3cae5d5c52d54ce66262e2afb16017acae4b7d5917f093b257954cc0c1218c5697796e2711baa45a504e5c5a1d7478893ec716e9da24d0c8a07e7db82147daf1f717d59394005e90ced3b1168d4c6e80303844219d0ad4cb8c87f482b07eea1ccfaf3c4129aabf689db5b2dd159e1d47c0305be1bc2a07b5af851c1b88e071d0eb55103ffc7483c552359853381a1d484f20a08f0794ef3abf358752c6a933e3f7bd56fe979b462d09aaf8aadf9f7c5260d33cb2c983d8ab82693d2596a5a9f3c5962c3c4692e7f63660f7b3e9f13744d81720223ef1c98325c9d4462b561f6e82af8abc1c84ea9a753cf8895a6ce6e8059a3cf0223ba1dc5af61e6848b0435e65152136161ad3ef0c538b28b9b0c26912f57cfb1032ce7dc3b9b3e9822e689b3119135864c7d6b97ddd920323af862685e1cb9abbb030b025b7188b9a44c5573744a3ac88d3310e09472fe4031c27ee9e034038c354c032babfddad6c32aa487a421ce592e3590511ec95872ea7924ecfa3bfa347613309a97f364eb6d91e25b51db8f325ac6c660fdc53aa05e65de18f8701ba9bf7540e25cfc6b73d809ef1114485390fb9fb032c1c3f6a34069fd75267b16555093233605a55f8fa91bea4b75b23941c301aecf4a4eddad55e251ab1cca1f11877057c6b7655141c1234868464bc405131d81dd96c9292d166120a8798c7a0217d3dc65837a181eef90608f053826701a5ca09f73f7eaf66c1714f3cad1bd87f0121997872ebe2a3a9c5ceb750e4930c983154d3bdeda1155143bab5c02fc6c3a4c20bfe9d7a771357007f404c3a4eb4efa9f926d1aa6d9bcf4b26a88dbc4a48c3f2932998763fca82c08bbcdd41644b421d81845db4c1515468806f4abf436efd4e9ce15fbcaf0be899f95b6bb23dc5ed2767784d5dcb3645ec411c61efa0d47723d3952d3d5cd072fb8cf449fc71f39a2166b833024bb60fb58a13bee90139c51cf97506094ab960accacb92186590b71cb19ab88e2bcb0200e79c86a6fd2324787d27f5e01ecac1a490d5e265fa4f9ed8a48f34c2104ba84fe46207822b912c7e65a20fe57c02f991aacf947f80beea10010ca4f9e9b11cc050d0c3b5c616db22b948525a52eff4750021e2b5d0601eb0ede3ec4c8ff3bedc4ee2fb76fad1bb82a7969380841192de76e393c810361b002fb70f2a8534a27a5612b4a1d8c1193feeb5b87623373997e5ff0e50e54c9999bbfd6cb4061ac74d4fbcb7e49df7ab190ab566d9d93b84305e5b267c26b139108a4ff852151e4bb2a9891030692cae64bc320ecb0c3044a71f0eb738c5237969185148a70cdd1c51f6cd5a80c4e12a7d19eab24db3e4d9442ec52fd5124a536d9e64b2ba6e8506a97525747d467f2525d403980e5a280f8c9f6f3083b4640564253ab5bcf8926a7d711ddb9bc106f0d86061ee58c075cf5388bae4f6b4fd2da43c9f3fe5ff92d53b2edea729a5efdc64378a0205d690d6662ad1b7c579551897bc2d58f9e8f1cd20e4828a7908d12f2d66b7925011bd6f577387a9f8c6a061d81e1b935482de0e0ad042acd206df8c3cc3d9cbcefd78b6513d3945ecf9958df5ecf69e2eb2de9c75e511548a98bc9329f995fe663dce2c5d753c69428a0fed38f0d6aab06e11815fb0cfa622bc6832f83483151b74e2dcc8b397e785a32e1ea09e6554806ae22b86849dbe300df0ddcc001f4d419f518590f72ff38de6764283ccac68cd2b3a8da4ce193f8ce61ccca376828fc294797ee553f037039b93e172ebfcb50c9a67ae84639ff6f45389afc2acaba69fa0ba03e94e1dece0d508efae0fc73cf2919346783f0c2a922d511d5023b93a4bab1ac11b4d253e3185d01c852d048b0dbe8e9a03e46ab9dcd5e84c36ff9bca884ccb296e11e3a6d86bc935ed8c915f0366de72a301b68f02c924d14e73cc2632212df2b7c175f8b0e00ff6fb0b37f8a7148d95931e78ae136d84b104b05b9f419ca289104101ca0306622f5fab3c5d230906acca1359180cfddb0bec4c482866c7068e711b2272e56aa153a67be80b4e29fbdf9346e3d9fdc9ffe030b60242e9fcd3cfd1357aa8dff5c92131c1c4e24c91b58d4933d1d2f2f015b40f3561118134bf8d1593682f080e77badf4b0baf97effbc7259c39abab0b0d47b9939c500a62ca0c6d2693523cc2cc2adf5ed825e390db6620cebab02e39e3c7287ce30edb63cbb2e642e7ba876f96acc2394603fa103000000000000000000000000003d453d88549b0f794d5b9becd1877ed3ee97d196bfd61dc4e13f739ab22bc2328cf1b97b29027faba41af2b0ef4833064bbb7c79f0482d7fb45be886417b341435e081397201b1fba867b25efd97e28112676bb81ac8b55a4abe132d779588d400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a55e747eb67ed2ce0065113fc35ce10e72215eadc7e59bc716971d16b1062943d6d8fcc5ae724377cb07565cd66fbfe34934f51ce6093d69c948b59840abf512edd14d70af1a774b09dfdb25182969a692b4efc4ca376a9c253e9557acecb05f1bc1c2ea41c89b20e307bc0fc58df62b0fcd93c035d12f77993fc23eb295e6c503246542b3e8332cda22ddd228d3894b524936cca6afb33169c2153c2882d59d4502271f08159fa1d3721e0379c716ca9d7a4facb8ff37649cfdc1ad113d12490cea0a031fb3b561da875ce25a0a7f89279d809ddfef5ce838b2814b4f89cc27f5ddce2203d35e5a495bbecddc595e1f14f29f7e66cfabc454dd42051baf4d007f4ba00214d0d0f2f9033e4964a07cea6ef6ff9872c2ef3c30b9dd25dd0a0e95752561bc032acadc67bf1b1927609b9730e28b1962156ad9a17a34b272d6b1c1f836d349b80200b4c60abbb47e7edb25e15310ff7bf0a344603e79f07cc269a3ead1a879b5ad032eca24e26e306b1b239dcd865d7ca6ea0e2e056c0d3fda158d4559154f013e6203105ee94a59eaa1ec48da051d53d5e9798a0ff76eff6e739e88fc21f2ce0586ea014333bbfd45e0361b74daea9f3be1b7cb57be9ad22b78322ec6f23868829e139b55ba04ec4f809512509b9fa29875879ea3d083fa4c5c8d66673ea58feecc26fd57f9d213a4a35c24533c16a6de88fc812624e2950dd2d56c2296513a4d326eca4546e158971d267795cbfdc3056258494e85264c1d5aef37da4cf2ae0e3be3f719960b200f2a7a9a8ead1e594118df34d07838771a92160ce037de7f9effc87ec0ae6c0bc101bf7cac524b11c81164b443a2334c4488821b9a8868e6497bf8256c33d0fa59421b1e5a507cc9e5b330269c0582b3ddac14dde8479550a3ad2606240fb8f0f6d6a88131fc981f64ba29503d9f307bb8fc5beaf3be40cc772a722c4000364f3de5378da81e60ab3ae5dbc28a52056c009e384f20cbe3f024c3475521ad443038d1c85ec8d3acad930e0d1554aaa29a50574c0541bbf311f5004097dcd53885d6b7c8fd27f589e9fc1e86367c74750e75f1b48508debdf273c31b49832de7dcc87744c442f44de1bd099c86c48041ebcae8cb2be7bfd91d753ee7783546aebc1a7b097c025bfa02c12d90427777a0704570ddffea27072da526c27e10d7ccba05a91b8310fe2c3f9643008f561db3d3d9fbde728dde4c39673d33826f516bb671e2172576d0f010fc858de1b6ec299a0153a483e1fe62b14f518671519b4d9d9fc40ae6b668a00cd907cb0ee3a05795df662a52782c5a2b96e2038e753c15aec81b6c2dd303cec79f45662088c1094408b5808556409abc53f51819d4eafb0dc4a9dc477da89a0916f29bb7c705282e9770f7ec41c5a71e7cd6aa800a1131a0c13f0c3538a1db7b65f8b27242e17f8fcbd7b4508d72785bddddda2abc52814f305cd3305d4b49c3622fae088edeb683961a2f150290346805211c03a549e0b3591bd6a31e463830d5544fe2e57f0d71f2476ab1a501033b28bd97451797f5e8a71b7d1f64bb150680e31d06d994ec18189a4ead572f35aabb630a4c9b0741c6b38b97a40cf94f86c251ef0bbf0d32df28bbfcebc047cc7de7277ee179ecab65dae472b73f9ee1a2b37408912104231b782381ebeb0fbbedca1b4c842a546197c5dd52f502c6e7c638b4df8577b8c671d40166817aa66fcc0cca6485b30ac3b8be14b8e7bf456baa0462ebf5fa40ec09a4a85d04c25e7a0da627cb6817f5a1259cf4239fa3cf1f714461f4e78d322e2c7fe0993c0052bbca7b332f0bb7ac73d2881910c8c55b98c021aa5570703ad10ef19a2c96590a15c78933f519a47f679c9917bfbe3c561c510f4f6b2e2af09ab855bc1430c967cf5e86aa51f6737c1121e7cd73e717363de5404a5c76f69a03f2504c0391d47819d32bf5acc9da00e2c536965f8ae07ef3d7dd05282ce511d0c59966fd21ce0c86cfeb8202bb977d7bddef3869aeb959b0c1e6989541ec7ebb569d5c0290f56f158a3188c329f20152c1b50a7e9afffa77a00044a46ff0e8834b82bc5ccdc36122d05ef3760d914ea5baf881630c47944e6840cb0abe663bf4b20374117d886a353b61a0613b708b04da1f4bca8ad8b9fce8e7a0585d343c83755add8ce306b1992bd9c3224a50954adba0e68bc2f55b38967b31da5181d485ad99311b45820ea85d018e2802fda76416aa1261e6125abbf9916af4020bad3176e03653c8336c9fd92cc7bc23100c1600dd7a1700a00ff1b4e54df00bbdfdbbd4bf774ce346d19cbaad456277403d1861e0f5c655dcaf1fb0ec5b572c031d5369a06d905dfe81d427e2ef428c88aee0c2aee635c95368658aa338479c8076eac86eda941cafcda39646dc27d0d6ad34c9bc949fa35893306aedc5489a0e", "63ac005263", 1, -1316560829, 1537743641, "fdebc6b24f11d32798f34f3dee8494a3183cdeaeb37af0f4ac923427256eca42"], + ["030000807082c40303ae87d28d87bd798d3c4b995fc73d0da92973d1b264859a86a1bc6140aeb14b8f0300000001acffffffff23ebf4247a01429173d15284961825c8a5d2670d8710d3db0538e0dfab5d633103000000096aac52000000006a5294460cc2975b2b8c32c51b3ccc309c8d8739bb6996198ba173536a9e99a5109f0b1435b903000000096365acac5363635251ffffffff030aad330200000000096a65530000630065acf7b166010000000001006791420500000000016a0000000049292e5f0228b05f0000000000000000000000000056929e595a1b5e81ac6569bc16adf853ecefa11f646bac4c928289e0e426734dc74056fc0f5d9b7c0e85b3570330d4e0d29dac8fabbc8826e0a8d013d3057026c5bead8032e31ef31d388e83138d00dc63dc8541cb7d9d88d43869c9ca9f432400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0419c7bb0e86cd7b441ac6a2177c27b50cfee1873f23aa85e2451a17a511cf865d76d3fb9ffd3cd8a3ffdbc9442055411e19a0cb25c51505623700babe4f41db8dcd4955edf958046ddfac9939babad6b357f51128c0cf942254e1b9ef2dccefe737e6f52462f652af5b30fbe5a857ea8e8b4ebde753f87278c860c6d8c6399021de7d040101a5e52b9c596d45e3e3c94557b2c8c006b4242ddf0199a7d458c9d020a3ff06bf6541b11b7104245fd3d12fb135252381e79154291d4764f716c0db20a0743fa518c8746f65e25adb738680728e98d9e753029a5c1d858a3a57d8ec0e1a61fec43d48c2649935143c83c188b53aca02e3d1ebb8654cff587b6bb5474ed020dae634b6340a577a88c25e7dc79c9b8b69fdd17b7b87aa870dffc9adf5d7e310219e1a67d8d4df97a9f54a4f7764e6ae3caa4d86b4eb56100a901ce5dfda415d303295363bfc2473b9f409fdb39bec09d940886a7948e8ed049cd7d53425c28866f030216ba779083d4d752cdc202540e252ea19511c07ccbe4fde5f6481d2cd9d5bd0310a22878e4e5fed3762c847fdde57ccf8ad882006aee96525678149f022a27afce3532907828e2900f0ba912d6909ded4a639e78f8f54a0c7277fcf2ffafcfb1074d57d9d847ae7090afb5e6fc2bc474e7b084a4da9d8bcbf57c14fc7ac8e284a9f4fe916a1ff74281c80bf8fe0001fb1c976463bf8c71d303b564fc48c22521afb4bfe5eb45200bc304630573c8b6f52f50bd0f550826e807d25e04e2a42c657ab656d4176c08ae2aaadea3cc3b4b1c73d45ba1acd010c34d0809337dd3223fbf9a2f43873aa4381da19f4e743add5163022263c19d290603da7b05068565737977a14d2cff3939e459c39b60ec89ed578bc49c72608ce74d8c42999475ea9e1bfe4675ea6c52499bc4a744d554ef7dc80729bdffda66035f16f6849e4d2a29eef50fa90a07daeaa3b2dfefd3561d8dd5a8a2e81bd49f0774217745e865c7707993edc406b2ce8aaeeaddb820ca74d9b4492b0d222cc6219f86efdb33f86fef2aada18ce297e59f9ce9e9c81e709727adf38b71bcffdc29f2bff483623a871205c5bd54e5999c060cdaa31a86e4c0481a633ab396a57ba3f4d1d893c3c2f7c3e73352c52815141c165d15911c5ae2b8f5a7cbbff08a44c0e9d7d08555a728ee5827ec711a5d60979b251b8cc314487c767bd28d4bbeecd75793573bbd02a5b1faf8ac86a6c2b68c8e70017be1bfbd47fb482d588e61be29a4f6147602d551df02ba7690a70fda966dd787908a74c15029a76c8ebf2cf5d4d9349f26c36568a8a5e1b8877faf0f24afff5e885539d126ac9365fed1d0ceb09965f6370a40f93ebc3dc0f7f8b59e80d862f3291f323e661946200971c7af77dbad492f39989348f9896055fe40d77004919d6bc8ad33c27a10ee6536b808c71c1c50fa68450d060e6ba369003fff2d8d78404d342b67e0b5aeba679ea4633fe7344b6030e0e7471b4d6a27e0377462b7622bffd52616f660d1e52b7a4ff2b6dc48afee9e7af4a9282db352c661993d1c5970a9f182f40fe05075aea599e008c46e481a47a19a36b7d87046342420524c36743868825a1c90b62e2320d0cfb1686b12ca46badfd5a0f007b8b3e6d1ef980afb295579b6aec7c3f3160904f21e9eaf19fc671eb3f6c919695405bb3ce7d772a50c992db27eaa90371121445e1e3c61d3ba510a9b1161c185d4ba35600b152f0aa3425feaf18574313b53111d4b682836291d36870e39b638e814757365338717f81ae30909fbbf0ebbb8e4278f20b7dbc59f4f25adcd064795712f58a3774c6c5bbb8706b6be91afcf8a731d3271c1302335a2e6b11f0c3be4f76fed641533e13c2bc3c65d0e940e25e013de7035fbfcaff61f94ccc416e37997588920d7693c610a2c083a9e84609291d953223b7f6d87d24debed75f0acd1e532e3c36e4c6840553bbb04295a189d31822c70144ce70bf0cfaab75c9bd02f501d34daf5e4983923b50b227fef856e14d89914a8fd2ea83f8378776f87e8fe68e50446b6c6c968a4fda7fdb463d04abb7501a3628d94e0bae3bd2cdb9bfa786b307d4cf34addd083c2c50de45a6283ca09361444cb0606dec2e35ceec6f5885112817811429081f78ad06102599e8b3458ce9dda75035a4483559481246114339a052a454af971ea90e07f53bcdf6eb3b9c2843582db24f18e499bbb08fa473c01097ee733f7eaf4d67d8e0ca5c0b83d8194a1188f06b6efbb12f155f3f040d539996051d5c2475a816e94b8be1ce14a010000000000000000000000009a4fef09e4870386bf84e38da9d9cd820b982d6f62f07f5d38c7b33441ea68cca7382029aaf31ef254ab9ebf8952376ebe3a747d532342b61d6962762dc93b03e8b641b25c96675c6aa978923496501b5eda1d8ef7bfff76890f777c358c3fdc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000baa97cff807a1f8526775870d0c118b7995f37456f1bd66ed4365e334fab57a08bc3c9f9d6d5366463ea676496f8927297b12d49f23ae4493d4b67bef93783a555967c0d5b8d6fa197ff48631c7c4be57674e078a9f14ccee8e440940cad1533c902514f537badbc1a660bcbcff5ed0784cace2373475544e36896981fd94a4a031c347915789c2dc872c629adea110d24771f6a878bace84b1f75993db3fbe656032a06ddfb3732ebcabc68dfdd337330ac92e72b93e55b8c4168c6892c1cafb3630a0157a745164ab365166c34791551038bebe0900d186d099a0e7821b45ec32212fbdda2717d7674397a12313cc7789e317d8d9cc7879c0a6d8e59f1d375fcf850031d8f9fac930bd84a61b125ea54a99e47a4788db6b26b257da1c1c2fee453c01f030621a5a5af6362f8a9381f4781919138be214b9c3490f36e871802167f6616d60329e38197f61f8952467610bbc72ef94a2da03b9a0190d89abd2d9afaf15c1f3a0327d776405fdb9f549b3bd63bab545f4ade0e52dfff13e40471beda3cd8eaadcf032159bb40d66b16f267a64b87aec8041ecbf565aa28af5cedc02d8e32b7f86ab9030baa6fe6e2a0f9270c42fca4477f554019aa6e7e4fd75a33f6eb2be3b3d680fd6d2fdf7e121a2015a1c51b8f93dc4915b370c84e07e3ed3aef7f279bbc66def9a0562838ba5e44ec05513853e6c68a165853c0719ac7d8756aa7382b63d1aa7fe61ac90e245b3d1a225cb96125c36ec6ac85137f5246b4752616ca05f5d3db8aabe0578601560e448c8d8d492de2ad165eb091f4e2db48ee333424c1be8ab456d23b7a798e16ebed6216c28b76f63865cb51d059f82ac436301636c189e20eb19bf65ff4bd9f4d0ff536a47dc30529b41e2247849de7ef75ef4cf972c23a85e5777171b143be5fa879ebcf6d2457cb1ae5d8e5914daf3fea458dbc354a18cec7afb29aef8447210b9ba40b66ec7be3071b8c3e162feff556c3a0155e86acc07dcd4c53d0476fc2e309f6761609ecd1c8dd8b4d6d4e93fafd9b10cf0ac1559dcfa3a26d25592286459c0dcb08766197ef67fd3e7288844fc2719116cf57fdc407f7cab16ca095c05ee208840a18d9f2da3598376ee83041359ce3d9394dff409bd20b8b85b77406cb85408a4d77c8914f025c917fd40467003000e5b507ffb91f3f06cdf54bcb9be2daa597f26a4205a8f0a010cd2e19fd4439ec6e07a7f55f04414ea4cacfe27acc2191d2c730619c1d11729a1d0679ed56c137f7b87777394559dc7bcae9869d36222da768f6e7303867b90646dbdc861d0023e1921bd02fa35ba02279f0c0c975634e85ef65e4c601e2af42bbe8dd0ae85a7779b126b118ff96c4de3f54ebb4097419b0c9dde2673c949e73d87e0811aa2597a03af93e05c5fb06bfaa39dd5fc1c799db09b4ea25cd90394655ba43ef0e7ceb491a998b4fc0069f35912777d017e322c210f8d515bb238fcc402de15e9110a2511e6a1665604a23f4083daadcfcaa1602c8b41b50d9da0b8fb3c6c1d02111762ac12166753edc07cc7e43c360b6df9de6bc2e6d89681345192be9de5488e2ba556706f527cde41984f3f60837714e6d75f1f7ca5b0a2ae5cfa08aefdfb27b145744f8187c4eb918b29924c58fbadea874bf41664a5040fa0f299b89e5ee5a0186a50937a71c382e40b6516ae9ef4a9a5c6a8c384d1504a886c3e7c5aa68326e5d760e08d6c5233023a896a18ebee48218e5f0369e1b2a8236825f0ecaca47478c7f0091345d09ad38471f5799b4dcff0d1d19b5d337554fd4caa4a74a1baa9eb5862c9870fc0804da9eaa4f4426deac8c4c15be489e6e106f77a6abda759181486f2e51184881b07cfe0a69cafcf1bf2873011a4093c89c4b55408613150dfa756b0c00971a5df62b0e5f98d5ce868f01389792af40d33f400021e1365aa88103ae1a86f34ca7f79316a8fe4f8ba04317c2170adc9f80f2dd3fe352c85df077949301bab88adfb7a771755c09064fd527651a7144b8e11aec272070cd80a9f54a1c3b14ddcf6beeb5a8b81c3e29967ddb8969de4c8fd2a477e8dd29417e9949a134fafae8d7d83c9eaebb5d7b0568ba7fb6fb31b98ffae248375ca54f62cf085e71e43179429f6a0873d0faf23457456c8979dd04d07229631559f91566c661796e49fb04776a7f2bba859a98b4b3771e3ff27c7ee3bb73352605dca5eced1e44b9fefa51ba718c1cc4ac38e17b10afa8b2cd8d7ce67b00f91a9e8c345b95104ba0ea3ab5e36726bc2aaaf4492a7edf771d72d18ad0cb813be2a129fa81889088d9825515b56a8ff5d6c53e514a1d981933e2e9c9783d36b0fcb6f6331bc93d826c4f64e134e0491f902f534efb6bb092609d5198dbc818baf3d28a7aaf0e64dd72c430597c45ad07352e80923a3906d02dbe7266c303", "525365526a", 2, -1437686251, 0, "5e8b40e4a78ca6b071a19eea9a75c73c94dc881d4f79885a2e21932ecec3be39"], + ["7a2a4d60015c20644dc228e31fab4b4506e0a0aecb45c1b0a0d3cdbe7ecda5723774b4658d0000000001631c49b96e03e4d964040000000002ac515fd7d6040000000004536551658bfa2003000000000463acac657db894ee010000000000000000550eb700000000000d66870f4ff17af083cb8bd3bc4c7117e6887704a86847588ff145a2a7c4c4d206b1c75b7d644ac43458ce9cc7c6f6e14c647ff4774b86fe1b59c8c9db2ba88a3bb62a74fb1c486eeb2fc077aacf17ad0e1461ad8ace4b20ea8933066010fccd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002be84616d125721edc0ef8d300e2441c91e372dc45f699bd2177baae044f6128dd9f9a5ed3ad9004e9f818329626dc9b9df6a2e4ab2c183d567db1c3fa0ba1cffd1c21ebdb62eb49d142938ab42187ce190f6806d58c6805ef34870216806c9b8a1931f827ad5a63e2a2be6c1fe1d6403cdeac82276ba61c0e93d7c531888798022e28a19eea147a5c9e1ef4532354f81aeb4ce4aaa20174f3ab0e57e6c35ace9d03094e3f5fbbe28b48cf299947367b4f1e7269714c8f729be2a535091388b8134b0a00799e6a02557aca81303301a865945b131035c34a947ccc128a1181959947c6e044cf5f6783a6ca902b6213f5e4f0d017e68d08b49db3b0b91a3d4e09b300fc032f7e59d7ac4f9f6deb532e3c311f40c6b302f125306bff25175051360c67bc4f02010ded6765807334a55dd159f0da28e4dd6f6e99431be08abc6e555c29ee68690221ca87eae0a5cac0b80aa353ee1a4aba1b3e4c211ff4467366b8a4fe0b40f69e0202190a3043c67a7d41f4bd7e7f2d1c4dc4c8c7e785f8e081541ca49172a9ae63021469ac58e53143e38682d130f49e47d80a7d1a38bfa0bf3de83f21fd1f82c75f1f3a5157a49d6403f856e5d06bea04fea067a547a7d9077f6fce3543c950e4e9a08833eecd2903c3005f99f4d64bb9a7d42c4adeb48138de727d3e84ef6163ccf05f2150e3a1b7b325c0b0d19e8e05e6c76bc1a18a2755d78f7cc2ed2002567a8045d445432ed10e799be1f56c87c447a61185ced3c8a42177dc6e9d0f2dd6d3b9222f2ab454d2790b6e16ec4a0faba45f71968d13d555566c0dbcac1870bf631a1d1dcab9b890bc4db236e6d3e76775e20391cfa34bba031fd16aa952bd2b1f525241d15f1a0266029b047b86d8b06db48d17665717a088736a5e0aac0207e6a4e56a51e300153afb2a7bd4b3e5d8e13143f48c1312efc86745f11bb45038f7e2d26dfff3f380761f97919d6ac38c3ddc033a8c00c0948c6000ab829c5d7633e9c8131241a0ca7ffd6aa55e4ce8ec8b5c71a8c0bc919b7fc6b02931bc80726b22669a1b9527c43695dfba4d270266a301bdd3a6ebae09e191185a077cb3435391102c11acafb3ff117654a70506b07f567c0c25d99c22a7aa677ffe09a00661bd6c7539fa14c67ce398151d711cb65e44b2064fd86beba9fdc6a9cddd3249abd7a249bb2ac4a6e64f8915cf728f190b0f591795b33dd5554b3fe6b35905f9af3f94cc71ab7001f848d88190617c27944c4031550558d2603b847dbe61d720515ab73e9df75ae7ca98fe5d4f8da4362abac6850fc65951ce88634972ea28481888cb8e3b6f939352871683f03e920eb865c043d79e603019d0604fda43e71cbbe503a84ede22d0419c061749086f412ba45cea01af20c91ef77fc82063c6652679fa1a8fa6c42498b1049d58dae0ad07ced2cbece6cc93c854198724d368af97249ecad20fc2dcf66e3767d73ba7cbd84f1fa886858ff94bb0c05defd5291ab285906c9c6f9e6f53c7e9bd01eb372db1064757c35dc3db90c9371033d5ad5e2efc1e3eedeccc7195b48ea6a826489890f79aa1fd0e1df1f32d4763620d35349064a4e547bbd15307ddc97c2b6940ab3e4b06ac62ccbd4718c77dd41589f2adc5129c736f1f3cab319306ad311e5a2e6bed0ae926d461fa11a64cd1e16a58a0e804d73f94f4dd1deb14cada8124727640ddc2f1f54b62be1642e95a63c7834ab488224514f22304cfb8d71816f9afbb472b6e798e9720d4da7b4c963718e49e54f570ec9ef146ba4ad49b8bd8767f107edfe59ea657a210a9e722eae2d416461e5311962fd0380724895b34733b5f18de9984bc8fde02f1bfacdb5728c39f3b97c9274e16411010304a66cd1911c7c7553b014cce6b173808065f7cc22d2e8de3a5ff1a88edf93fcbfa73507a9499c4a64d589327e0cbbe8e8c5927a2597f4d033582b759277ef43b012de1b7505ed9b2c943f6053504f5e1cb518abac4b2e15b78b96cd6369ba97aab8387c9560081e9cdbc02ef56da579ffee9ad6f749ce4a41091488f4d3f1e5cad5902c0f78dfe85df778b0ddd7656aa3623296c5571ec89105a82c5ba161b9ad9cd0ac9bfb4de167ced10b811fc7b3bbaa4ce36c2d957bdcb94930d6ebe4975a2ddf6c602806c04afe4600c70e99822a7939880b4305b859b10d3b9534745574a15a23ea85fd0adf5c8f4a631ca488a5ab67df8d06d7ea1ec5ff4f86e548c5228ec9b0e80d66277e9d5414b882919bcd39a184d448f66b62f18d6a33fa1982fbdaa72a9dcf7f078207f8dfb6fee4915ebfce496185f1562ed1f185bf13e18442872c1912b08352578d89b710cbc439d83f89f88c72c071a0aa8ba94d18c3cb443770bfd672d314d704f82c381a6d3cc8d9bbf3755f2a059e65fb9fc75acf04075d7f64c1641bb9d7703", "51acac", 0, -1552337250, 0, "ff37ebcbb4ab9db4d00fd8e571bc2771ac064a185eb9e9ebef1ff86730e5a57a"], + ["030000807082c4030153306eac9efa5977214abaa5267851f2ac1adc336937121fa481cb01ba2cb18803000000035251634f47747b03624ed80500000000096a52ac6aac51006a65e4995b02000000000700536a51ac5200da081a050000000004ac51526a000000000000000002a36209050000000000000000000000007622b125023373c93daf719d15aa8e78b79d5812036a10ed11eda032a693ec6e3b66fa22018371fefa807c2db3fe2140d594a28cb5211b61de6f8f496daf64670ca4d4d2ad856700788f970b63ceb5daba1c92b71ba4ef71147f2b78bebf01a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036e3e3ba257a974cf871d9920965500d1306665ffcb58c88284e68c85f422d6a6d4c0e2734dd20a1a68a9eda07c950b3a794d2f44566e02187de1ca16dd8e6eb3c617a7099bc8470fc358f4b0eeaf87599c603a3debbebc893704d2402e4f83a1022b08cd1016869820f101891e15547d6638476cfa8847393fc683fb8e93c0a031324981dd61c6db3dd15c3428e67f615762a04a52eaf57abac72c933555e6b85032f930d988617c44e10fd51b5a07a79d1ea554e48cc51dde5fb5990d94abbecb90a03ae22cbd8ab4132f49b089d0648da65af55b1b011acc6faf1e47c15bf4942d6ca64a807152db93b99ce1b263f5d95c2bd099280a174e670e699455aaaeb541403208935aa66c296fb3fd97c8ebffa9ae9e0f108ac70b31079df93d962c9ee7b03020e5d6a24e99e57a73a55bc49f1505713f4b7d5ff4044f88b9511ae7900914eff0226290136111b9e5ccb6d6376cbffdf3f815c5c6a477dd1e1bba7d750032039230304b0f317f822ccd0fb68a378f4a59150c218b2b371d52aeeb2171f6847396ba4031658e2f12eeeb1715180378b12207fab528b2325aa132632e5499500b16e0462739b348bf4faa65ce0527717def1fa9f2f84bc9acec7e942c12abde345e0668e5bebafd37ae66c8c10a766f9092e8d7f08209639c7cb20634e8ef14689dd13e31b444db5def1a88ff9ffdf4b6ccc981490b7a97497158a90f9b18f3f4d30ae16459846b49ec55ee0af06436ce438aa78f7f0e47bad079143fb24ea5a86a2c47ac6893b03cd0b2315c32f553d956b6c8580b30b7e14cb25f148d1bd07481958ffddbcd6946bf2209a48d156526c4ea22f25b6f962b70b206e6921bb7e8bcfa29d4880931775ce09fc850c9711b2ff9cd5881fcae453a35b060fbb86650650f971b2e058387b74a59740f3738eb8499e168ad11ce61267e97f580a522fabeba68e2c97eca95bcb806acb7316404f085c29d3edbfea2eb9f74e0c892a20d9f6f24af9882cffd45b5cb67319e7491d397bf41a64877e60e23d2ab011ec85399e98a8c96d10d095002a466e69ea7a6f8155f0cd89b5b1c853318de181df95840f65f163d6971b79ff02e060e4437148d066520f213145abb369e438f2681c642bbb81ffe3829c8b1c12f443349a8d2214cedf75d14fa4796618b623510a67d314b5e8c6d3069199b2ca0b8198da01a1d502cbe5349be7bcee31b4917fb29098d22e7e1fec1643a008ebca922c692fe81a12d70786da1ae19181d6c5f49a1277ffc748f523a3508d59274473e4502e14a02293f743792c916919c825084ca9508711a32ec99243b187ebbcd4d028a242180fd05118675e038d2eb0b19b1dd76c68db8c30f15309383cd7f2a88c28cb30473014f6c2f181f13871b688fdd7422352ce33e56b6bc2f6ab206dedfa63989ad0f75e24899937cfca392c0a0011b128730f9388f866a76d562dff45b9b94cad22ea49a1ee5927f28692d715d24299028607a51e080956f3ff73eca7a343bfb35cf0da29b64100b7dfca5a9003353dcbbb4837064171b0d91d697422fecfb675f49da66743ff3b2e30a35dfa2b5098a7a9738383d965e5d3542419ad56f7e3728e30fd7bc32d799679edfd0ec0702ec9923373cfd94e9fac40cf1b604851864505b52e595d96e99d87ec93003b0b62a203b256d13aa94336cda1a001bf9645ed3e9dab12668d2632b6887b2c8942f7d84c0ea1c59f216374b42206c9af616a5b2a8064fad8e69e99f7755b18f3758d363a2b87998a2e82f7d87ce68ce7b3eb5cc36b96517d79d761b2fb60a15f3ee23bb645611cec251e578497265eddeb8295398f135d92089b79d8dbf1409070cd386f8248b534919fb5091913c144ab46d6015f5b092efd872224acce4860cc43b722c4636cc899ca66174c9d9f3f37f0cb1d7cb9ef3bf9713da27e02376fbd19276351caecd12d98dd08b8b5368b94c350757d4c41c712deaafd7e56c68ee5c53304472096904b2884e9a9eabac29b431a795e3d1baae201d41623d6b44c1ee914212059c0c93384c460629274aff5f45d373e79ecf420f1abb173432672b6766dbc52d4863a518c15dfa5788c623f187fb30907252418707af6a9b1b50b07e5f100bf8873e558a9448c5907a148b9c5531217bc8d6e8d5294ce5868d25734155f088d8aeb5d58735a34fc1cf28dc4aeb6a58ac31d4a54877101b7f8521d5371e985f8dfc18ec3f7142146f44103e4095ad30fe6f556a712c89de1ffc22caab231c976132e2ff19cc129eac5b3feddef7aae936dd2b33637d4040000000000000000000000003153260fdf06bd816c30856afdecc0be98fa194afe985d724315dd5bcc262e350eddbe735c402ca81f225fcd63532e94bbf74f64c286284301953027a9a04d6b85deb61090d39dbc689703683a380c25333b4915d9e0213966982d5cb2f151a500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000685fc13d559845f61c6824b3d61b08a8d911dc176c143ab857b05710a3416c7704bd4b33b2d5765bc51dbf73a9532fd96b5e34bcd9c7ed5b550b626758e1ccb6a1dcfc0bf271b637f62ff895924da2e96c5603981644aff58af47f27e6998ba4a3f34e5b3dd7a9e327aed70b02e5a956f88d76d5d17f6a176943dad5f6b0f48e0329d9282f09065a3b9ca1594023576de2cdb6e6acff84d43b9d1827f63e8e40df0217a44c79bbb0f4ceda587b48e8e27474439bf34ada853a8c3ba87ef60c0febe20a0549f2af1ac0bb7bf1b6a4b7bc8a131e4bd42ccf22805357b04c117b0527a75b23fef93e9d225653919e9eb6cd1cdb3aa9886bb73fcb02b1f05c83b3331688c9031682c8f23b3448e16590bf4b316a6a99b18e34108dc97cd38073d70a5ae597d5031730131b63dae7c0802e04902852f44e239c410b94cbc77d3408974fbd3f99230207b1b680427a40a50351a974433f62d6adddee5e2b3c281ae07d92e20b5ee277030975e24828bcb3bbeec4fc746b1c55b7422bb5a6feb6424c48ff6f72726ecc21031588f25aa155f753e209e109c4de0130e4a4f0d1779a8fbd2f32754018cdd7e87d2b665c5fdbcbe9a6f9886ddad9b064fe2e305f4c0d7777d0febca1df67ae2841bed999df874fdf70e331c7adf33f756c02f0501b35e4cc33d55718182587c1b6abfb9fef39833635eceb024514dfe7536669ede03f0d654e0000ad33f52345f9d9dfe7d2b8d5d142421aa83d75d07779e372140635b527f4a98c3fa8214a17b3365be5cf7c4662e7d752f2f1468ac93d25957536ce7be4f41abb59d25c50f92880c23194d1c588a69049784bdb53cb320d0466c0f01f8751458afcfebf1e99f2fa8a3d5e2cf465bfb3c2f5044ccff094a383fb8df91666582814de4e528d98e1b95e491df6455ccefb32aa1b14c562836979e340bfeddbd2e7f08d8643de9cd896ee83816ebb0931aa06adfb9e352c454a18e935ce0d02f2c3a540513fa3fac82f5a3d93b43024625d5fd3426b0e223bdf44f7d9b2bcbbab3e2e3c2b5c8d74bec90df2a408b455d389365d2793914e61f63cb5c207eea1099bbd08464e938ca98a029618a1f0c621f2dceac7ee6dec4ad54fe9b2128c79846d32dbbc3adc713af8f3a93b60ed2ca13c02064ad456c8724125e9a3de5c1d6b7e5d92db31f6e8db4dba34868b2267e8ca3a0c19409875e2473a918ffc9ee571c8a5f0fa7dedca3a0c084fa49303253380ae8381d869eed9534e61cc69eb91a8fa5877f687a9a24a7c9fd51b78b9ea287ce2079fe22f7cb24adbeea03bc4808ba516c47b62e08813bf8b0487307ddd5425370e9f91a8f49bb411eceae5cc52710e9ceddd73add4cb24a505b4553a7ff2090a89181e3e200718bdfa35215538130eb7c475f125645f6d3ca96f1b4a127400e7f9cee00654bd93a19c39bd8f58e542528be439bd4b0dfd4100fe072fc78da9999381be3ab82d9592ed98c755858a23d969c635598f279217606de5a63dbd2f32dd7e33903560016ec6c10368139b54037b39a49595c2b47f0e0748aaa5347db89a2410fa89e3fc4100eba796068a13949ddc9cecc2c9ed4bc4db9370ec48124390e6bbfb1388e8c7b28d4b1dad520968f3b71c5e531943f0d9653ec488320500c9c1f2b6fca1321179f131c1bbbe522ec11fcb239b255b9a0d76b4cd4700356ab903c633096d73e0ae66cf93643c3a28ccf5de3996171737077f6895606e9280658e5a66767532e9554c3e9161b5d4066b5ca434b3ae648a0012705ac426a92f464fde558205ae46fc02abfb62fa3b6ea50cc4ef01af1f127680c62732f2aa3d4fc8163ee4d80c028e74478a237434d8787233dd150eb4f49c2b6568fe3d99383dfc5663c6e18e1171f0c70fcee01d041a3453410e827c31ee88d990e4777e2ef3ad4b272e3ab4ce4e1d435e56585360ed20e363feac505ca249018d8668879208c1ad47684fb18a7edb67ea3e12f583ba4211b18c7848d392af9410ccf6aa895a5dfeadd66882cc866e05802b74fc4fc953f86e7f68bafb67d782552ecaab8ecd825a27d5bcb418b5953331226d0b61aa203c52092cf2341837b7ac84aa8f29d3d0f6855872d46f7c30518967b9dae053361f43b1f0b25da68b71c82cf44d2a29180cffbd9fc922a6e261f8554b0db52e3d1a95ad9e9e8a41eff98335e071df7dee27cc75485f3d065f1b2e7cf1d4662db4c642987f387908a071a5c723b6ec172f9d5383e83b747db1c5c43b66067ce85cced6d2c41b7d2b567afd27289e35744070b71e5ecfaf4da45d6bb344ede4ef9b0ffd6da5d4b405e1d7a332595a951da1c3a161d253ff6b09ee041f0ff554a133a0fd1d53db6f80c30831b28947aea2f66b9d92493855f87d429db3b888eca92e30cf0b9a753211bcfdfc1e299440e7ece142d87501", "53525163", 0, 182308742, 0, "2064f5a29e47adcfa9d12f6afe91414508b1937781723ac21cd7662268449a28"], + ["030000807082c40301560cf8270314fbb298d506d603b0816ed4c5fffc5121b2a8b74e0e8054d0fdd003000000095100006363536a515230ec62ab014609ba020000000006655100536a5200000000491663be010000000000000000939d1b01000000009e5cb9ef3d4adeaaded3a1a48418735968a9f8568a9345cfe4c952f9ccf4da041359c9749efab5a3b10c4113a8af1a0f2654a81fc5fe4da295615d1cfb0d53d31ffd64e9840863b7806862702e245b9f5260bcfccdafcbc2e21f8d1ad4b38f7600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c70558f2659a25500fe4a42fec1e72b8aeaa3995b01dc1c74b1f4233a79a4436611df037d8c4e35be79e81aa3ef650e67898011a64f4e4e649edc49eef476b4cd3da15c4367db47fad008ed5be7dafc5b132700f1ac545d33583348282b429c4c5e40b922f1e922856c3cfcc061a7aecf72652ed4e456f0a29fe2ad3f190eb4c0212b6a0b2273b15de2a59895cd56cbf329468b6e373d30a812e717ad51079bfa50325da8ac25e4411078c4fc4a72fbdadab556f46d5e6a18735d83135b465e15bac0a000f6aee396062e38e0358459ba111ae3d1bc4ac3327811efbc223ef035a1cdb30e8ee10b88e7ca1fc2e89a288589b65841d61269bb5c31a7223a1dbfc9d34ba031b3abe400d4ba757f4086c1c46da22e4fd1011bb35362e41ba6a7afb98053d4602141a05f8013a5179401f36fc85d7bec57b707e6d41934e77c7b20d50032208d903004aeb48435fd801867b034bdfddfd1d94c09743b28e5f2191f240f839e274d402240fd6d223b88c6df088bbea02ea9129e2c7abce548fcb4b9f5ffc7719bb456e021292f9ca3005c99a96f665a3868f45c683ec214b085dd1c211310042665293a7c6ede646ac3ccbb16bb6af003d6cb8e6b5c3ff95c0ad7796aa11fe4c8057b40874d4d404a3829bdafc305fccc3a94c964d1fb5b7518e4bc92cd76215f95539e707ff25e76a2bf14b67dab10469775877f7f95e577aa66c6ee05ad9df4b885bb244daa5b619f86cca5354c0a52a9ba821e3628f0d5c61c2ca855216bd03dc66fef5c3cd743d8fe36f0471741be4b20e2b2d3dd4b941e5ae9cbd9aff715b4d6e64a46d1480a5efef60726ac7cbb8808d9b7f7003461edc629ee536a974a0618f7ee9fba13e57e129944f47cd0a76e11fe88e14f601d450dd59bf62915c088d156b2109446c75b4d4b3896e763cbb7d4b8137f19789188d555ec6baecd99010f004bb1ac7fa1ac35dfb8104966fc54600e04ae9d427940eea48b3d952c9f06b2b39a8c78553d9e4ed37144f25484cefaccf1c3ee6dce7a352b5e9857a3686dbd7e01bf6ee3389ce9c62d3a65698d69770f8459689afa9e42922f9ffd8aa42d1bd7c4d185faab20ec344a2c63b9474a639607151390257c35443a9d824be42f15df158eed97507911362a3cccf68aeb4a308f40306586fd770642d06b6098e9e0aef35ce1afdbd67231a413f95280054be2469dd6db430af3f63ab68c2d1a5c4cbc2bb564e30d84294699dddd2de956ba88888deaa7ddd0f3759e879c12dcaac5700c35b6d72cadab34675ae78c7194a673a353f88a30dab9c1d2eeba7a144b41c9fc112b7b763d8fd86dd8e6764d859d92d5db3dcc081e500f9246bf1d034bb433f4a89aff128d70a7c26b078e5110d76d65a7bfacab7fe607dac0e392c5caf91655f159b7307e162c165a056d87c56596f782f0f7787799fa91dc8c7282c0cbf882aa3917402d879368d4c10c3e9dcaadbba2ddce675bf5e7ff001e943d761940a8e7ede1380b82d7c9b43f8fa261169f53224c534a306285074fa7bb059d0d25861b7c64960c3f6632a20448c415e020a87911a3ded54308102b82822a45b0694a7d3b42aab6f35d07b27ceb4bab39283182cf17d28be5fac55faa53e409c67869c25de70f22d7522e7d74875622a91352ca4ef4029be184272c0948167d6ae64b09b80ad4cf652d85cfa31f148bae5e62b243a874b7cd9c0957d91ad0467c1d79839c971a12f0d4c3ef34116d29363e962c4f91920608301b2a26739a86c7d32077fdb0b21c974bba98fac995ad48692d72cd0bbeb3797674d581e7c7355d8d6fc77ab7d4c04655eee37891af6fb8fae672c3d5b5173a0a46449ec8e1305539a50cba1ed34ea4e568e96fd1846bab4ba470136958583f96ebd6b40613258cd60127653838f63676f5075feb59af5a28739ffe1d11fed008a0a2e63b19d44ef21611bc4e5fde7e917ed7cf96e349d2a9db35433c90afb5237326f2875fa39b4cdec9cb4d2c5835eb2f54a6100570fe76116698617c380c905633929c93debb2dfd9c36ab0b524f75eeff15d5293f2d84f5379a117d518919061f06a8543e64d96cd6bce77d5169c2a4ed446df2d2d7aa0158aca48ace0ef6308cfc2d4c22b2be576c633c298509d06efbc7e2b00ac9ea45b35a77932aa3ad0f88785dd85662f784a8ffe443058c47566e7cdda17a6f8c80b34b061c0adb46269be2e3e63085b81d92bada4597ff1f845957f1401e420dbadf15e6393b8051b0ecec066bc774010b8e508aa38db1eb17f6d5649fbc660362e2302f204ef01d97cc78049f9380a223c788190e6b22b3be9175241c88c2acc90ee0b5ac6ee0f5254f532ec65274054e52ba301fe513236a3db23d2d240ffec176f1aec0e4ab5b0a2417df8a6743c76f779da947a8b1c209e0781e8d2a69850ad0e", "63", 0, 2145458821, 1537743641, "acbf3d2002709797ac22c6c4fef2ef9ee98f30f1c5b4c8ad49944c9a2217f7f2"], + ["f124d0560356a500ba8633610d2e48e4b7de0e41c0801b4f021fecd816179136a31d4c927b020000000265acffffffff19b7ff3d853287e877130b3ee3fb46c34988c5d03f7090fc53c61792fe3716880000000000ffffffff0c1b5a05262b7d3ce6094ea6439662bf0402681839728ab2da7fba2e17c50e3a0100000006635300ac6a652d2798b50382831700000000000165045b99050000000003526aac572b7d0300000000075300655251656a33e723e1025bd273050000000000000000000000000bb268bfc72c8cd4b0309846716453397c484be4d928e2c756ef6003a70b6986d01a6c2d8977ec699b9bf8a38d1333f4921528b4a742276758c319d3c99d062cfcf05c8bb288db6b1279ff115067b013f96b90d733b0b72af309306fb97f8ffd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000081994af0f4816a4f7232840d0d6782a4a5c05703c4550319524e2108d69482614e77d2360066a7b46155b8ecc7c5b23ef8808df9034dd1cec062ad85db2985e99da5a4b208349519ded3981772be1c2f5cb3f3b37bdab6a283a95f50d42779ef1e9800445f971a15b6a850b863279369f8f976d93d87236310f23cb477d93aff02204d876f213fc2c27f72123232c9380f3c71fc7be80b3f7bcd604be0c375ec210201d986c23d28451c615bf20826c74f905b86b635f00154b392bd548d2bb41b2d0a02158c1f4d769c5886f2425e908e381ded8b303e7ed354db388bbad36a594a124e4cc7abc45b71f2938e7b9eac7d7e4f98a7ad74ddee4b77b097105886db9b3b021e49e58a608e0e305a23ccf655870e0dde4bec450c47e14a3301fb5dcf0364de0318cd04a232feb840d180246d450e308ef533eeb7cb15282d4d463be5708dbb3d0207f60f0019119c45aaa940adb782ea425edecbed4c4de990baa2be795394756702061a2b4b9e86d5865327268e5aaba95e3e530cbb06889ea728dd4f7c2f31edac03058f9a3edce0957c1505fa1c85ef9532b4e25311437e30e51b9802a6168d78a46d944b77218b4f7cf9e7cb68ed0c409bbc31f7152071b1805d3e65781a710fbc52e9b2fc7c0f67ba2c47a4c569a95f95c6b8b2901d15728f76d5ed678e78fef6bebdd9819b4f104ff72f42e6787565aff180dfef42e5e9a113e65864054fbb82cc67c3582749f93f99d951f50cf6b424849fe5c4dbf67a485e6965a93e81d1add8f366d2edd77fc8a63a138fca16886b2e7d971b2b2d83d269c66935a1a6fe0ca5f65b72b1f6a452e93950f584e64d6d094f44dc08307cb57d1159a0d2df1f01f6d2e012e21ddcceaa08d4832df99f17d6d1681b9d0fb7f46ac28e8ee8e9170234d363bf7c074574e837b654b74c5200879ac9a81c543d67b96c6f5e4b2cc76cd1c38a379a3f68f84dc737d2d7706b29f89a0711261c9dfd28d83da5c98ecea3d53e8ad1f777c716e9700cf61c91256ae103daeaaae6470fb63ae6400c9fe963624d0235320cc4411d73a8aa755dc01663f7be166f732f2f94fbbb5ec6d84cfd9711723416bb3c80791dbff009d5b5f7bd3ec0b307c72e11defeebbe6bdb23a05342128c8c5ad9f6e06036f9a01a14a28c6b26a75396fc31aed9a2be7902a30a631c50b6578d2ba379afddc01b14a9e7bd0056ed6edf5437db7c5f2cbda61dab4124219790ed13c2c7d4d8b76be34250b4f3611d0d522876b793214ad4aaf71857349d0def15e344b7cc26f8703050a9bac9920e00508aafe369209b922c05b081f27852e56ce1937428bb38da685c4679a5c6a12aa3454ec1f0503b34f1617b765c50288ac144d4e994db4466b230eb098bc56710efbcc19b95a80660617e9c3e1ed490d111bf171dcfce1dc89d271a35799e3df9aa21f5f9e266a2dae36928d4e13d2b554daa4ce3b61c4aa45542fcfe8822f32c0ec0ee50f1084d4ed9f71ee6d5468252214c8ee6dcf3d511df911eb89bb048ee1a433e3556a71398be57a64d142892f720e1f10a9d468d1bf8fab201c18dad72c927da19c506bd8ed91d9c689db149a89300d12dc2d2cf3451befad4deefd12972c49b36f35accbd390e180e7f849f8254a97eaa6d74c18867971007fd8b8f0d3551f7854168b34fd367de54507eb3ac31f10310738f29e608a7c8f622196922e94fc85ee5e1afca7819ecd5a14188c85b1dbce94df6a3bb5b0ac1e26dc71894174159d4b8e92f4e4b3927351b5186863ecd5c1c054cd6a3e7e1c5548163bc6b5d642d2142805c60797f5c64f45d99847416329d734b349756cbf029b6ed7a55d320a373776cca2da65e64a5b6312104aa72840729f31a5f5cae9a88e46c6744c763be1eb2c58df5c8c40369450c0643f7d1faa2e88935eeac3abfcc16a51d0995693d0f80c7d5418423102e09617f9f44049cac106767fd4a214531d1f6e5292734477436cabae98cbf2ba2b222e4207d6ece0649e72dd6beef99e99bc2f58b1d5211b0eb26855437080d551e61dca4e8cb808015f967b73aa3ec083e31ebe931c5b75bb60793718622f90bb0e0dd0f7230cfe53f1ddaaf676d1f70c9ca9c648f87747943b788999db0c247e58ebb8b8cf95c77e3839850f6940cd1b3a1991cc4bd25a578d58933f179992f4992b0af00cdf78b71497dcc6ce6ebda464b1712f44e214dda7d97b4b97818eebb2a43e451cb87d20aa3b7a5b376e6d1d133066fcaa27c602def2904ef4611ac67029422e5b3f3687c8d12cc22296512520000000000000000520cda0300000000f87e9ee26a2318d7da1a943b61139a3f6349008fc2d82cdc4d70fef5f6e768a7f34143ab8ee01ed31577b2662740ab922aa0b47ecfcbe6be3dad9475ed7477593e0b0064cd54eb0ca6eef01ddf62f17999abff5e706355405f8389f13850c818000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ebdeeaf5ec8afeabc4098020511fd46c48fbaafd64716760159329cd083d7516c58e18a2fc6fcbd39146ebdbde2526f7f317a79075590f8ac6c07ffeefd9c5d076494cc47f6f90e7edd430354cbe45efa9324cb0068737f5ffcef3d242e1a156ccfdc69bda84fb4fae251ffa26e90121c03df0bfa2dcd31b05728121aa97aaa0225e531c3513e36e00816fcc838aacee339fd621e4d2662a548951ca01ab9605b0322454fec6d410e1a11c9893b5bd719dc6417e1c56ba9021928cc095812d643700a07cfc1f0cc4b97f7a1ffa372355b235e8ab146373d5ba15e49f550dfa569dd4c0c761343ba2455bf0587d57395fa8e9510224cf39efeab2bd6fe204ae9e47ac70223562df4319f1610ce8862aa0810e5315ec9cf37d5e0cb75b03b6ce5f1708518021aec55d05523babd0140b620d6bcbc46452b2698fdbf62dcfb7c30d9c83f98b80215baef080cf91573f27bad6887c4a1e2ae09f24f1d6006a9f19c359b9189c364020a349fe14704811c0bd1c6189b586d9ed14560f104b21abdcd3761faf224c512031d79f29ae951d777bfe3a9328f113e0b9811d5488bd03025d756faeb567d4b80e1af90e4e7035f4dabed67299eb313ae5ec3297555743ff433c1e08bbcfe0d81f6d527232a2ed33997117863149210f19238b5b210f2fe1a26486275fa914ba0a1c56de9874ab504d5e038d7ea84a69bfd4da5b9ad56b7013b5862d3a8b0a4d6a6182463b37ed037074b379d76d58faa39cc0d104b6b8b3d698853352e38e18cdd963919c5a810f5afef99773d0f4ce6618067bf5bcffd262485c1ad5784f63b09a637beda153b8457a0f5e120ed0a43a60cf1529663b9573f4ee00c7a0d05ea664d8034343c55b872a72f49d1dd795aac01082bd4ae9024c929102d4fb0f687457d64a0139bbfefe04788ae1166c2ab7cca93d5687b91d276315f000d886b0bb408808427bbb59c123782039cc4c166ac785190135bd5d3edb0016074696b1fd1e224a456a9c9fa4aa51f3f13abe8a94f710c4762deb074cc6221a381bba1b87e52da570df2d8f9d48221f19aa0bd089643cd170742c01107fff43c06ff82dbf44c6971b11b663aaf15858ab27ba3c4ef540f9bbd8077289fb616c699df02ffddf8615ffecd7da1ca8d983733dc5f296d041ca619e6510d2eca1a54242875d9a06f306456b24be2a579d3138e087f95d57dd962324ce8d72922f9252207d20ff2946c1fe34940e7b6e3899b3197023fc8188ee470ce2eedb94387b06b3b016c75d7bf8bccb985db059e155246a1d346823b03f902ad621275662c14181da1ed462f0574d97e35dccfe16a369c3ae7d3a9f6e0de87317b2f11dde42c6b2849f2e898b60b09f2ee912d32eba3240eb66a90d8914be12075e9e744e5fe3e667b0381711cc769ffa6dd375cf2ac840d02541832f851e05a03a136f625b3747301679b5e34dd2bc07224bc01a7f6c185ecdb8b0621c52c12aceb81fce67a9c7c6a657b27f1bb2785dbe4d7c553ef5f1567780a21d91d826536a66c32c2162b749a1dc19ce982e9cbd61abc2806ede645d33f432bc71a23ee09ad0e2484396a2b83e3eaf460627c424679df026c112456ecb3c1dce0aa9178c607ef7a534849a4f5ed1fa38902f3ff35bbb4c8e4ac7afc90f71294dda36bc44880bf5921e9df4c181885a15457222e3e224e1127d11e49fe82ad22c67ef4ad65b572ceb69a2d9aa7b964cccfb3903d2a2b698044bf29f69716ebbc807ba36a0c1110de66e6bb5f0efb16d06e9704c2f19084d07d5e949dbd84be33e9a0ba160f208e08f671c5bb49187e905dbb333164d27c20b2ec50525b560601a9d87b2d9b6a5432216ed8eef4bd9e5925233874bb83f7a8804bfcf4f82411f1be764ee94b1343ea07dd9d1da4c9c73410158f0e0b795dfdf71f7129993c97f729b52f8daa831c81d8d98deab3523a7db2ddc7d8a18c3a7a7fca94eac4b85e66db4c2f43e809b569190c5ca6a7409fbfbc0ffe59556e7d66cb74623a09ded8191fa98a1bdb54414d2be0f05293730abeb4f13cb34a76b4dd7f8f8a0072543bcc84477fc94a99e02ef76de0834786587d88ecc1e10949cc1b6e20486c4e9857e87e1cbfcb951985857596a124e63223658ca0203194a08b59e5c2fa03c4a8f297e88571b2128baa8e4b5466693a141b42e4ab7424fb6d41910fafc98e6ec568682ba3d7a0e15017e57718c4f24af85f2b4c018388228a15adf0485bf6b21b2fab7b7ab16994db5d401b2d73d472af503be946c1fda3bb6fea253d1f757ef8578f741548e1e5b9d6637e614e0987936fcd45d34fa108a8d472681a8f053332359f47a9293d29cd65bbe7eaa8b4442dfea3979503b46014a3111ae17bcae4728052269e8211ba935717c9efca0f9214ddc9f09159f7afebae8dd14f48370e99b10f", "65", 2, 1881656414, 0, "a1c13e151639c6b1871780b62f5d951f9480d785cca855f81c97536409210a5b"], + ["030000807082c403049add25b41d0769ae70e8b500fc4c0719fc6060c5ee85df8b8ce56e990bbbb43003000000086363536563535200ffffffff05791ddeb372c9e0eb0f86337d3e77ecbac88a45b50eebdaba633957b8cc122a01000000035200acffffffff6341d6adc609f1c9e2105320ae077d1fc224b4bfe326c81fe1e917ea23bdac7900000000005a2f44292025411691000874d3b2222bca6a5905c2f6c73cdae147dc1c61fa40de3230a6030000000563ac6a6a52ffffffff0189d3cb0100000000025365687df5662b19e4d800", "6a51006565", 3, -1823706795, 0, "ab1e14fde2fbc95af20358b6c0bb769a37e8f74edb0c2cba21b7f7485e893aac"], + ["56a913730161de8f060898900955a54b4932789a8a734f02b6734d6d87a3d62fc54618b00b010000000465ac6353ffffffff03fe3e3e04000000000565526a6a526f4fa003000000000300ac63ea66d10400000000025365adce68cf021758ca0000000000000000000000000051a91780b999ea9e881465853c95a5d24e1cf0312562730cfc696c50ae224310e28dafc6c11be449d0847232a582c119d935fd4e127e6e0bb54dba7e47c97cea2b5b1bd682b45b409e3d6453753c531110e09a5814f74b9360419f8855e69ecf0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000067bd35a1c1a8302831eb466a4fb6969c25cf55e246f7de897958d9bcc248e2ac71377969caac9f7ce1d22378dd62a0169ed590b0b110550c8cc454b4f45533a8ccebb2c30918573044a7e1fd82463c75e2ea3211ddfcd88fd5579347cbc515e67d0219e2c466b1f25935768793df269c898b58d37900b0cf29ad41eda89245b3020723335f0d565efdb7eb2d129f34824471817da66cb85ba780e51b318a42840602269e35b867bc8aaa8a2b7ca1c5a1154876433c1ce2d23414e91f41e0ad691aa70a009556a5cc9a7a3c3818d61df2cba1d7a742f10525e597e2c6c3c4456e46f93bc7d0d2182f03d28b6c4a8a117a28a0782d868119362e4be4a7ac90ed2ca2bb74020dc53f11692aaadfcdb08d58018b71c9f89f6f5a0f7ffe012152d094fb7aba20030237f92c03d7e498b47378341cd0617b1ee7ca730669379b850ee18cf64b880b0218bc0805e975ee49a82c15d681c3467fc092213acdb41c5dcc3bdc76ab046f2b020dacaf254356e6fb1118cac893776581ea22cd37dec33479efb230dd654c849c022859253bcb25da7964cf7d328e54b42b4505bd15fb96b6e5685080f30e6c821a5849467a880de181d8ab8429b59967b4a377b8b7da4c27c5a82181664416e30645ce6c15914f6f80d8853fd5b229710e0d267c8e2786d0600ed80e0171d867b1c16b54489aa35b9c5527cb20d4b42d34415887c2281af8da76f9c8a8463ce2ccbdaf4a719b10d56a35eb670a0a3ab15215dc78bfdcc78479530b24f23f71e83ac697a9e8f5a192fb62f60854332095b276ed3ee346d2c4d5971512411f851a013d08d9c9c749c557c5bb4c785a32c62088979b3bcde2648a076646adb9ee19b14910d334dfde7240128e0167eda2df36e937e0b73148063392909c26051448465c8620edda4c19260aeb8977c7a2828e7f31789b1be8a0329a4149754050ad0c3a4e80f6b5b28e253ff072183f89c651d202d349583f5dc96ace8cd4adf81f68d44e375c258652b7695d538e3cdcbebcfb988d78301e5ed540c4c1c670977b8a29433d87afa97d6b64a9f6f374a040ad904245324cacfae1322039573e41c6af5382ed8bf942cdda4cf2a3f113cf3ed72c222daf2674be969725a2030ee21196b97e91542a43bb4122109d0b62c4df18cabc844424b224c077e88b17a243bb5c726c15a2f2401fe4d28c2b0173fc5a9c4acc439ba00286ed875cb5f55e917dc9a5564e6b6b1af05a88fa79e02f9210bf3eba7ebcbe4ab80d660936965ef8a473bd96e4ab5385ddd0dd560399928427cc1951ed30c8af1a824d3feddfd2d70d910677edd411411e1a5d006b4f6daf701ff3410de116238741c862e9a112e46c55fd4f2085d0601ab76f04a188e67c92febf6e00f66b231c890f0eadc4d6cbf9f5ae1f635d0352d204212fb436a140fda358c83f586aab6eb7c48c15ef0817248b8104c91a8b4396aae5e42109bcda24179ad66f85611aca9c1fb86877f0f296e0556162d4b4be423298878cb38b2610535636d005065e78591ea8aa0f07b19bc79e91352034c7c5237dbb66a057ba45e88b84708cafb385e5116e2ba676f2c2cccb47c1cb5efae7a0c703058814e0b9b02d41e8d65c086999d1d3e7df108cfaa81a87bf674b743ffbd65adbda442ed7d42cf5222bef71ced5def6164f468fa2444978eca122dfd8cb6d6bd69ec2c3bbfdaeec436999e2424f317f158140de8f78393bfb61d6ab95f6373bbf548a718bbe4eb8f8becee71de470e500867d2b285c257266307a6e752794c3c7f5ff14559d7b127726ed01152ac1fc6681bb6299dc2cb51e22685b9ff31545ed1f374dc11f4556963b743ec67aa69094c67a2a839182c107faaf22743ff0a54fdfe6981c887ad844071c278b112d1aec575595ae856b4c3537a5fd62ad5106d42190cdd0df471fd71bb24b3846780896b4d268d94b6bfca78e2ca6e46eb68a872c85c68d24ca60e2af56ae74baf1efafdbc7892def6ca8d19de7708ff8c5e0ed5866c2ce2d1994432fb98f39276c1d06af57b8193ba5414040ef601e0b71820aa54f337ebba2de2b52e4ae6383b3fd44c7899fb64ce331a689251b12623975070e4ad78087991f704cf4075f9a16822083591ee671c234f648aebd17cb781417b640f7b05abaf26fceed9df9194a13dd054f7798fa1e388a33e47e0624cbdd1daa887fedd261bf4863c8c7c82ac164aef7557330f7fa13b8b9e256a9626ffb97bb8a09c9fb56de0052c018858bc86cfa2e484ed4943bee5b2157bd8ffa5f77c67f2e8cbeeb4804000000000000000062dfb8000000000074ec66939bc2b63c58b4b2fd6bb9e234d58f3321867f6eeeed315cce5f68b22ef8e09ec5fec3c71087d207d54c928118314e1af17ef4edef58cd7b7a58bbf1ddfdf4f1cca7767d46cc9d55a9cf7e2ea1b12095859e9577a8d536ff7416299776000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008fd387249db5d6697c5cd0195ecd370424fc50ddf4b831b3a8dc68de86f5576852920b6f41cadcae265c5da7978947883a21db36cf1cfa00057148ae6a5548dc4a6b5c51fa6a9edf4311b95a1e74035b692e330b2a4005003c8fb9e3809baf3a550f002f3f25ef28165243a0b48d31f30a832fbb0ba30dd631d85ef648b983c8022132d020713d1efc9ff442c40f1bf4eff1edbefffbfccfd15c57e183b007468502272aada235cf4c299fe961ecdd5259eb93d777a39d9f23a624516503f0c31fb80b01560f523ed0cd7ff552e4dcba12deffbbebf18a5010367a8dde62a02f0b818864194a634030ff4ed3326dfcbdc04a21e3c3b70d00f095f7aef6b811f2f5169e031a9e7bb731d0432963726f0a44f8b5f6f9bd869ffc28641d62573e06276f85c0032bd8db51f6937b83e4912a9cb097a887eafe7acd537c2af41e2707b831244ac5021714643437fd33363dfa90a38f6b11257d161ef5d649db238bf0a7fac0030f4d03280bbfc84b62447390d8539728e007092ff5a39abdb01e409fe80c9bdfbcbcda032ad87b0934ed2fb2b40933dc31e03ba88825a4d52ba24349979a253cb5a7e3f09ab130d046c5987301903440ef655fc24cf30e5091dfb56e351b778a501244a4d4d5b8b865e88326d5d68d15a98b211639cd7953cd5b3e8d03b795da1a874dee8f5f4b694fb248b6de75d02169ba5e04de5cf6c9fa6d2adb5d13ace1b84c6297a57a724acd787ca30ec10980b2e1dee0979eb2d5dbf34d67894781d11753c7ae6a909f831bf48efae27dc5adee9680959b561c75b4c733c74aa716c34507447635063cbf123ad445b47e9795a1847ffcdc0c922f4388168fdb74c091f14ecc6bbadd938fdaa6097157c0ba9477491040321de2d8df1538d8ecf6c590bfcb5f047ec05ae8080e0ab19267109fd21e21f000632722131392720f6cb2356c6b59879653af5f1db5d127f3f5369c900e792112b6b2cab9a27feeb049b585af43497e54cb22def39475eed38b9f2d6d14e372a3b41b661851812e63c1dd6b0684a9e54f6b07c0210dca7334e1cd6adc864a0d71a796f2fcbf218d6448c3af39f1fb97be3afe59086b52589ae365a1f5aae95abcc9ef4998c79c7d0a4e2857a7d150c5a5ea4bd430fd73f9ee4d18977a49eff427f73cc1e368eec6b2bedd9794872fac1532bec118ae9e4adef95b54331a80da4e8dcc83a2c90c31cc25485e6250bc7164dcc0dd73709ff63e6edda38d61e1a95362420225489726ba69b8ef59959dec5f2143e058ac8a6aff1fa63ecf3cf349886eefcc5ac90228b82034d3fe0dbd38c0a643f83f859fa0695418684845478d111e8d8bc1273c750f0e5c96863dea07b5f4b4ab9218a79f1b90cab3f83178d89f9a30dea553a490540e9362b89c5b2b3a6e03c67b52874ea8dbb972ed96d432ae80135421446ca6dea73ec19009bce8f5c225b48a1ce65da6dbcc216a1b126ad620f946c692dd280d859b92dd2bc173f362cb367ea662b3e4212a629584ec8963528ffe6120fcf3db5447fff1a282192cf9e65bace5e935939fb599e52d9c80aa834a8bff59989aea78aaeeb915d5f1a0bb371460d60e96210d3507d5412648f163ffe42406ee3d36fa088508a6053fd96e57b90d2f1c9c3c7e970f1395a7f0a5c9114403ad1fb9fe84eee8e46ea957290747ac509b348e952035490489cf02d8f16b9b717808a028dd5f611b7e7fd6ad0d18f0b2f2085089503517d4eb592491f5f41773fe45e1252b79eb472d9d4e261a10d5920314919de4f460252abfa612cecccd52afcbe7e029b4e70c1fe89e3eb8c875696d39002a0a8f25a5d9b98e7063b7ed445b67bbe5812c5b8111bb139ef43076ded10c201b126e642241b33036e9614baa38049ae3f7abfed2879affcb8931b3b9132708ad7d25f46d7d0a9a115085b1092134ce84ec208ce05d9822f3aa87edf2810ac56b188f558f97065191afe533bf331c66d802bd8f97ad26163c45225c2c06a75bcbd8e54020c402c2827255b9a335d1f0bdd2776f8e732b87eab85caf9e8d0681f81ff0b37408284ef898f2a0f22751dd998e8e60c14f62655de74dc6077b8e97e4970e11734d3db8261bc07cc23856b717fa99dd3f18f6d05c30caa395078db6cfeae8ff2d79600102c09a127b5fdc128cde6d0b979eb48fd7102dc538be64ce28d33863339b6f1ed97acc876c9e4d5083412ca68843082b3ff5821085f14c8ee297c24244db23a29892bdc80a72ce0710c5dd1d77ad42840341f65c105e744ba39ab86eed6f84f3edc3cfc4ca4b4b41f63407f5d8160724aba9dbde4f718c164da404d4a6c82d12c3a1f289836f16f66e2c4ebb76dc312de8d6a92e949e675cde2a8b0e2e4c3efebf691d873446df6433afb3060bea5bc58cac9a53d39057cdcca341395ebb711e1500", "6a00526363", 0, -19753095, 1537743641, "e9345b013c00465362a8395223779212b2e2688869c43e80af20c9cfac156d10"], + ["fc9a062f023ce41e360a245308db283bc49b381bfa73c8cb26f9bedf26dba01a80591e177803000000085151525265656552fffffffffe1985637bb5d9c4150754f496c0f327a3498ade44d54ecaf0e13148d38b46b501000000055165535351ffffffff02e9cd6d0400000000076500ac52656300af1d230300000000016300000000030000000000000000ab082401000000001d6e1f578e911d972f70b8c1a787ac5ca6e85e4a81e279772d387ce2b185bf02a82c2005f08e07a547e5afc77339fc1513ebeb3e67d396826091d19bc9b6cd4d796311351ea41b71738666079544893d29cbd1e2486e02a2d4d421baeb033c0d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076ac90d3e4927e69cf3711dc8226f62b2f68515b62daa08b97b4c3de9c287be239f15da0b3b9e6d07d5fe8b741435177e9a6656754b569a33f3b35d4ab5d292706920ef4e51526d0d7d785da417a1a01b1fc08fe77dbb5e702d3b2f0f1ee356526c62257993ce8717c35cda6d9f1b53e9c5c433c12f1e726f5e5c7f0ea0cd20e0203bf18289c312f986c66217eb143c60e43bbf54c98c21406ef273f85b801d9b7031928855e53c6a65dd2427c98f29c9ba931375338f43f535fe16c91114a7812b90a00eb311341135cf70417d99b10f6bcc10f58c3147fc8f0f4c708905bfd69a5166e9cb8ea54ca745cf1852e64477d221d47736d3a206c3983d435626c7c1fa055030c0abf3e4219fcd72de754ed4f2297aa27936e89f69fd2c38dce99a2fd62e974020b70ab2be37bbfc9905caab927a51c4d7f12a1a7843fedb9c8fd35259afd1dd00324c210f10107e5e99875e0337c2f15c1111f1c980b656dcc2d7c3a5a3c477b5c021ec7c9dc9f7da6857cf228a3f47fc8114f75a4956c34818daa5c9311e91c870d030dfe16ded4c85f0fad81a243780b6c5bc46c89852df7df3b614d1a749fa44880069652750a75aacd0a1eaf52ef2e4d83ec69416886c1e120d92df904677c1cebc05450ac50c4b72c7e6c86e6b2663e14d1a255758c3487ef7dfc22791add3e3c9593c86646e07f7dcfc035f7850a9e53eb76e9f9b14e5299ae5acded5750a40bbeaf5aced7f27e9a4b9de3bf3aa6e49adff9163f8c5dda23bac0e39709fbabd757e4b85feb7d6709eb3214be98cb07cbfa5f0881d6509ac740fc38c98a5df46f14353b205fbd449f13b00892083087be073bc589ed5d47ca2056793c13fa1b96d9d607c3d5fab2f4742870431568363f36a5c19a3cfcc3998543250cc37c284e8111928fbc7a6be359534298fdc99957d8774e306efa019ba0c07ac34df1aeea46e8b38eedbd2849b68badd55beba57aebbf64958b12b1241fbfe4bee090a44687a75f4f81b7156e16f33805c25003769066b87e05078af49e1863e773cab22a596436c822f92d631dc082157988d3efa308a1730b673912354a8e63df6bafec1e459ac773fe293fa40a70ad392790230ed96ffe25ae68205eb5f691c40d56cffeee18e8ddf3a668920933a44af73776568eb954dfa6142bdff9f63e6eb9b8ed2ff4427fced22fa8537dc370e840d10734a0a6f6a47e6607f3f272f6d6f714227ef3ff2d76d679ae96a83ecdce90b6819fa21cc1c203bec2a01118f82673d0f14bcc15c8516264ef1270aa8c72988483aecdb1aec388e29dce6fbf4e93c588a782f3c1a64b49ed326c9b1c548049124212185f865212540a7e4c867885bf1e95147d3585f57644e8e94844f597d349ecf217542ad45b8045434b2c11d1e3eeb965427abac6efd756a2eb58cc45b8e4de557de383d41ca1c0c01d82f400e36d29e17bb7019b3ceb4863003eb9948d85513f634abbeb735d60c757d15105ab6d8785efea2a56015eeee5df8b63c0b3d9dccfa895f5e7464632daafa6addb44874c968f4c26e705f22bb98ac6038770e324b72ee644e34341729b871e76b38d86b1c6642c44a95a44d907e15c7df1743efed81d8858dbbd717c546d07c4bcc825b32906e5cd3cb690bc38d3399df163fe6cdf8ce2ae6200afe614b6dc00856d817f843899d987f592100b65d18640e993b714d3a462b571437810daaf3c89713f4046718ceb86aaacc487d3791548b91e9e44d84c59eadbb289b3894e8badcc976884ac756c8fbf146d45e8162edeac0ed37476035ef611a237cd256a2c2bb8750b90c04ec3971750b09caaf62384c823fdf2e267039222de16be87a6b77076ee653db432b27a70b1f97851d58cb97549b735f67d38750948e9ffe9c2b5dbfb9dac20877756ddcde91bcbe86f6085ac2e80b92da05bc917f3d47f80756f84787f707ae8870efbfdea0e194f02593e1f11120d7132df76d67487b533361f898251bfdec92b9f7c514ef7bed7b92e6ca755d573b10c60fd4e3929375cc3e64cc7ec5cceb092d5e9208a0355cf895bd2fe70f24a82864ee5ec1cab816744edcc4ab1bc1baecd3bbd66d00094c2c578e8d666f1bf76ddbc89da9a137d1c6ec52c956a36281954dd40a6da783ff2489e6b2e933d1fde844427ed850e663cd8669e8ff0e097571c32a1edf7804b0fb483a5a0cd1575665a5369d5d06dcfc7ee3836056e1221c53620c228748eb49b8183516f34f735b8d20d4ffde3037d1e5eece5b56b2f736118753cac3402bc2028e14ac3ebe3dfa90000000000000000d8de8c04000000007437519bf188dffb94f47ef16d2d0a12921ca6a427422d51654f9417c179fedd1395dbd1f2cc3b2cef9521cf855e826ef2a260fd47d4b4b7224153fcbceea57e9b2008d612d21dcdb07dc348e4c3997d3a4bd0eb63619aa93bbe38b7a391d25600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000340ec0633466241331a1bc6e83e175f6e0c7d4e8e09433a3100c31f264983f937436d578ffc651375198acd0d0089a81e6369ddf892693e44c0bcbac95ae739be712c764f5ba67ee4ddaee7fac5f952d5f015aedbf2a103f43f95f833470d1bcedd1ea1cb0a56480fe6b87772d1a6f9dd56f3eecceda9fffde32ec07aed798f4021f29752a25958f0eb4bff3b48daa3dfb4f6c803c43b6bc222d842f793985262b0306bc988fa77825108cf91e482f93ae34ca1931f5a0c2af661580ef6a421de5ae0b05d1c25ed87dd1466499f5969696bc2648ab2395227aa1fd5a6e97f1c0b24cfeecc8ca629b6c76425ddd42c03584d27ce4ca0d6abad995d366e6650d54ddefcf020f4ce51ef992d15f78d1d97ac0096b28bfac60edee075de3121d67fdb8adc9d9031dab93ef2c9dd38dd2a347587e9490c5f53d3154e746a82c1128b43a054333a3031cf7f02de38eafe8fcddbbd6b61f65b8d1c82ca8318fa0564795ad4290c46833031c567ea21e8042658897a6f07a3f0271d77351e5e1d6be0a02db1684ddf1c8e0020e17a5d78a4153f5d0d2f5df4281850f538a580d687dad5666ddbd02ac139268bf801fbb194247d13f43337bd69eb89f04d9e5551cc92e8fb0fd04636af0358534a4f5f6605bbcb6c9925b4fd3387f0fbacd071d258b41db4133d876452356553768125e42f0751e56f2774a9a37b7931c03b289d678551f6226fceac8c22e09973ccdf9b21dea6a19613a89f7f63377dab9bbb9f0c9e0719206daf2d9285d6d9a0f272c59ad5f123a8c4361911d670301c64d0b96f3f01d753bd2ee70dc359bd4bf34f85dbe3e9eaf1c6c7b5e0bb88adf6b8d13802d3307ba0d5015c9f1ab6158aa70d77d214dd959356762dafdf8bca3878100d0b9308759dbc1ad1894f23400cd48d3de4c7d68ff9360deca2d01a6712f22c2f7a963526b8a2e5f2bc004615ac474abe0568363247c35c6cdefe2775124565276c1ce748ffd819c1c3d5a3cc839eab3fe5d517cd39199604a4fd3b0a35c6c6023000a8c50d0af4c57aee282ef8368d711437a0989ab3ccbb607625494b0951ab14420fa5c31a7629077fea89349bf589b10b068d6ac72e3cd6e0110eb70463b4a3da6194de6d803fcb1f7f74d13c4660c0e77f3136140b8358ebd586f9c159b590c776594ddc03be0a819a46874d229f8dcf8346ceff3560f5697c8d92cebc974aaa45d6c1c51b14a5d723b82a3ff35322c29d32220cb549fb0efba2f821dee36973c5a2d0119afbc934004b943dd1b265fb9676f279e4b28b1875a71efe9da7695fd343ba06d4a5e6ef088721c72d3312920da4d6b77e340a833d957c5143afa92949f317b399b8eee260de59e83fd4b1c53c79a28a23c10557fa84693c95a2d3ce4c1ca69c5652e32104b696a843c9a7b4b9cbabd1498dd6fcc16b9cc18c3e23d6ce398e8e87afb6a8dc3efa1a60f5e7dffd1bd289419ea2fe66229e0ac41b7d77278ebcbe193c0d763be93bddd5928c23e3292c172d595edb8bdd4a2fcd13dc887f19d18b3b6177c6c9d82d3d5a4214e393f6085a7f3bf808c144973c1388881951b982dcbf9c7c043bb7d3cb04c701bf5bc3761a1cfb32fedf99b17f851d03d77ccf1bb9f5e66ce25e435f3532f86c241cb0c363330213acbd132b9312a340bccb0351fd1edca5ac5cdd6851548802c287a421189eb482da16edce94e812227dd81cf6632c39a0b2fbc8380bc39a9cccae1e4b9199543761490b02d7850cd08ef72c56127000b34628f02616ad95cca771355d9c733c7c9c98d2bf2b96278ac6aca19fdde8ce0832f9437f1e3cc0185626488607ce150776e2a9354b82633fceefa211dedcf2d668632a025bd739525baa4506b24330a864f196e99724076507587e5a35f2b8103dcbc958fa69fd4da0781db63d245483feb7353e31b85918e548f41c6610d1edf3ff136629f1a6a0abb243b2e2de926a43267e8a7f3b0ebf41fa08cce5613d8ed168783f00b1614f40f7a6647c2ab7537a4b95ac1c65163ceda2e9c7e847df628758e8a1a270280806d81c60dcfbb78556ea2b583f9c1569df77a91298d30620c48b1f83fd87c5e7480e47c0a691d427dc8f9126d5388ab135128330ba4807d64716e98dca0b661681b3aca1ec069cdebbde58f09d6f77c5d21095240018063425d053347f10b0de0ae1fc6b053f0ad6a14dc2550386d65f66bf83cf396c2424bb723d42c1fb74307d63114d44a81f576a31a7e0e3fdd97cebf7f388c346181d3e0b61d71759ba544829229a3b3610ed5788317210000000000000000eedda40000000000b64af53cd3cbea06dbd30034c1a45d06fc4ddd1855bc253f39b5119e8e6a4a7d55cd80bbfccfadc53754b65573366b986c3e0d02fcf82c241372cffe29dba36c665d2560b86b2b73fd872afbfd9365db8bb152b5cf4e1aa802fcf18aa549984b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008945b32867f86fdba8ad457cd0c1f061c1eed1dd52d6cce440024fa70220ab87c2dd97e9ac79e8ababa1fabeaaf74c6d3868747a1d3279c3b656832f31da2666b117807378d66ba830f398924a47531f4bdc7815685b597065d0344d41eb1b9b87ec06da982854ba2797a6e79d47b910e2e8679eaccf83d79af35ea42af075b6032f9a408f882cc11f39803e8b9426c3074819c7f61f906a6b9f517ceffb9a004203248c0a3e1f1ae3fd4b4a522394e2136df2b73b14b1ff56e92960e9cb6cf89db80b06a018d4e7f46c77e5d0576a5418b1db9ef815e9b13c304514423bc0d7e0574adef50c44fe8a3d36c352f4010a346979688f10c31ad5cc1917364c53aab4374c030cbcfefbbd59269619fcd0465a19d1297f4dbfd5f4f8d4d0fe0b72b3c1db3a650211512e4883767432b2152b6fa524a4057e2b569d40f3a51508b5be4d8f6f265f0228882e255b2f72e1d0f012c654778a97cb8c513b48f21cfee15c41ebf1bab1010205b1e68f907ea67662cc17f5408a46dd2c23c1f76b3533e715e72acbf574ea850313b18c5dce2f332fac022a9ee7b4bf57e4913f424e3d9f02fb6bae4d56af3fe56f57f0a461b1b51aa7363861765cfa1bc8415e4f9ecc6bc9bce0ccac3c41fa4d5490138aa2450095dfe0ff05d80a5c2a03a232bd4682e5132700d9a0aed6b15bfd15485dcebf077ad69941872a39c107520dedfe64a66a12fe08aeae44a1d9538a930496df9078fbd8788f82337026cf6c48aaa6c1321dafff73702022bb10ce2fe3670ea989a148f22d9dddfbea0faa6768e113988da527ca1608359bd09e957ff346dc9d0a97945b2a1b280db2b69f28e91b6dbf92d919bf08a3095014853a6e451ddc4340bb5b79c78759b9e74289192890efcfb08eef8eea1af4c2752fc3e6a3d3ba7e587d64a79bc643d33ff8e2a192aced2465800cee6b84ef92364f0a303261d059b9bc0de0c9b7a7bbdc0dbadbb395e7b5adac447c88a01844cfdf9a52b98cb3755493faaca012948a940ac581569a2aaf24899c88f2ce9faf375155c77e72c26badfe3d2a07820a670e3402450404586312f0866bba5e1c87dd33a4cb16125d0ed60aa105cc1f97277d31d98c839bf55e4fb412c055d9829be112679274507b83aca6eb7bf5967afbd5ae935c2196775050440c7d1c93873e31c4dc3f2a961cc2335cf23047163dfa2118b7e1d0b436e0353bc62bd70092536c6fc70ae9e4ff4f254b712109127d1fc7be78231913070ea8c1eaac8eb8bd6710f6f0aad32913f2b80a526a1b0054668356ea439f920287755fa3fe1e9481383cecdfb8a1b5040937b5cca65577b4a3722fe6dc96e74e9990d07f1ff60b4a097487436e730ad710794d38551a630ffa0b8369b21530b39e7e95fc3b1becf99f15ce10c43476fb12286fac0ba27721a4ded4c47d8c81eee4a4a50ff3bb7562067d0024dd8f59c942d328bb5dcb2a9e0251542ddadaa5c647aac0f212403a17264c8c5d5551f27222e232c1972be4a057ce583601cc506ee00d32899ba6b3dc147ebb34b4417857934fddf472544d2283e2924e574f04b46957175c7fd54771067b7c7b1e5cbb363ac3841dbf83a9d79a5b9d566b7143c6dae495bdd821cfc27bd357730e7b020fe095fed52281ec84d12b5e058ec16824e2c8e397667868f1a49384ffae8cae75596c687dc80a9617c974b88f1953234b2530cec459bcef8b1aae31ab8fc9c25ff6bd7645dfd19444a7ea4c8fc385d1f04d28d90b86be86e466e69304aee36c4a30e9161e1175fb771772c2fb864c847771a7d6948a08edadcae6751662eeb92b863ea11036122bea3e81160ff54d96323d92e47d63e88c2263ec26e9ca9774df845ed1913868a94991034c83bcc35ac2231855ecc4bfd23e77d1216ccaae6294e3b8c490decbd9ac1279fe0b4f9729a2a08e8de017ddf9bec5469849a21abe4e031d6f22cb69ea26b14e97a1c2b370e93dc9e7fbb0bfaaf7c619efa41cfc25189ff40dcf3dfde04f80b57ceeeae2f2b3ba6c71c7e636301033fb49e86ba4cf92da2f1f33d6522d26bc9fb7a159ecebc135e1c3826f4586208bbf83408a3b2f28bfad37f4dc04505e7990f3b4382d773610232bae73a694bcf9a257b70b99cfa91fb5e3569cfca1041c3762275404a72bd8c80ebc19fd8cf8c8c38b207bb53611da1e68a5e59bee953b4a2aaedde41de89a0b1314ed8e14de31ea735d9cdd754f6a9307742ce284ca3cff616f7223c0a6dc8b1a8bba68202fba2a6db5f2bd5780fd96818095996cbd9268a7e213efc2b4e6c950e6e8ec972f3570db227995f2ce65db57574ba35b7f273e45f5b6103b11fd1b8bb3ca427bbe75931064d79b92312060a29823dd45523134f50cb1c6df2b507e302ed9ea48115e36f8ef6b61989fac147ac5626de90c5d25d0d93803", "635251535253", 1, 79634240, 1537743641, "64e805ee7db6678ff53d0ec444e1cc1555cc2ee5f4ac5bc753edebc014078bf4"], + ["3379ec270157cc12778d39967f82698e53a65af9481a1e28bd29b15af8a7da81b2ed79dfca030000000163ffffffff0201bca2020000000003536a00b17ecb0200000000075352656a65520061c9d76402fa59de000000000000000000000000009c516710862f930ffebc267542a9b159358f2e46f392bb65ea6d7978ecd5dd150acb09d37f0b6960f64dde216cc548912f2a1e769d22850b2410fc8f12b50f622fd913495e618d77fd4025b99da1cb919fb819d4c6618d6d00d79c8d2d896a9500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c992cc2b235e32e4c89e4de42c4665c65bf8f9e46d837d1ebc1a65b93a507dbeaa3111e0984170c99943328cf772b8a23b2ea1e1daae746704702e7e674441280be9f188710be0c408fd94ef2a26d8f3f5c223b0265bbe7507e1f8c5f1c9226fa41121828d8506b1242f25a50832318b3bf98fbdd4d200e70adf1c5202d7c12302219616aabfee9c5ec8d0522b5db87d07fd1f59b83e6741d579c5f0573a417a9f0206035e9ea0848d8d26320bb19eae9a347d71bebbcd2902a28ded5f38ad265f170a063b95003dba0f8d7fe526bb6d64ac45717ddf0453f9898bfcc99eccefd6f362b5719a1c4aee111ecced26df8d2430d28fa85fa02be3f6c34d990c3c87243496021eb6b7d94127347df2991c3fa58c3526e961f06c97f9d778eec10f238008c3f60322103a2426ab147684870472ce9e385176cb0955332d71556a74948244001412020690d500231219dc98ebc020f248001a463decdda420aad66b1b9a861f84adee021d4e2f49f4c3ab5ed2abab9234a75e3e067d7dfaaeeabade38da6bc635cb38e60209d3c1bdc1ca101a68f322bf08f634d428f8cb582395bbe5e9077ef542e015b1e82b20048415f4aed89dc1e9a3b330b3c1acb1e13f078a137fbd8261c6fec434a050001d7fc906c6af880ee8ed551c1d2f8290cf849407ee7c44f3e5cc09a92c2bc1f4dacaa7a5969133529cbf716da49a7755c8636116a36b7343b1cde239b099f48afd8308649ca45fadf155e6dff1cd4d792fdfea888b4ea981f229fc95cab23fb286d276ae1940322e67bac6d5f4e147df2228c532290024b7970dd077dd7af87f4bd41982cde99941e18c57e08227053c9b4ad8db7d0e9ad382903b8bad3b68c642de6b0e19e02d0507ccf5649c7892cf3d59991530017e8196f49524d9e61cce4efe698eb17508689f4b807d093f72fd2ea29cb58a9136a592f6b78a8e4f8c3fe96269b5db31abaa4c574c84925a53eee7d2b44e0ae978542f3af1c39408dc2cfd8c6ea2d392f847f0aae1b5d848b5e62701f55a0135150ebf9ba106a608436e7ffb4fab981a9f4e384628fa782f939d69ca0a821ad985ffb11fa5b8ec0e7c9f5feb8cfae3dc8c6d661d5e9978155d9e6871fb0e834e57294fe702e7edc5e72af05f7d05e7b4fd1ee5b1862963a0474cef93d6d8f549b0f2fa185de716f26e9fab13faf4be6ef9538c02b7c84ca9fa96b63364079e98249a911c5d59803a64e1ed2ac592aec9ded3f7d3e899d95bd09b7a2b09c5bdce44d56f68d12ddf1debb39f8b0be07550cc29c55b1334d51538a5099491817293a36196de92574d8dc472b0cd517ed7cab52eef2301ec173e601bc5f292b2d4839a43e1c8dacc264c9eab5c0eeae7df491bfda904213747ae823640a4622d8c9cd70a9ecb8b29426b96c8222ceb6a0943afc3436e61c3d63d75b85c325b6f05809f7c06542d782affa3fd0ea6ba7e90ef0d9cc5a54362ca6fcdc651600b6f9f2b53c02a2ea98a94492033d0cff8e3bba8cd3adf0df8621f2b3f45ac18441b00a7e43648ad3a3dbc2d6ef3fb39e81409e8b77019cb5ad24b270d9d60195df6c184c909956e027889dd8947472a820b1ab7c79accdc1682ef6d647e9839d51c989c5180308cc38ee1f4c45b17212c599e8b11f9b2eb926975bc5444aff364d514a867b2aaaf42cee57151d14e0f4227763ee3d250ad9de690dfd2bb66e40d3a87bf896779db9661cb727cd044108c7536d8632c15ed106b7bc8ccceac5155bba64e1085e1c4584786fed5124450bd390e4e49d55a5ac3ba047f8be03b933e6cae632841ba095115ee14a51d1e7ca698655d09e40d6ed6ccfa3f35eb7411db46e214bde351565fd883896ff4ba096c8cba25dc8ff5925021046069dcb513e6628704eaf203966184793acfa81ea9659a8b0d82a4ad64196770ad79cb6cd88b338e800c951de33348eded31d9a090b71834e29c098112895ad24445e4f6ac174833ce690e38ef931b9600b1f89fbdbebaffeeed72b88c72662bc6adf1f85f2f0599064000a8164d7ad85f64733c530b923d3342fa13555207843902a977b55bd0e3181e2757942f1e6cca23d97c05b43e056e0e4538a75e650f5e0625463b803f1cd8da18c27b1e4e7629c6ab5b3631dc9ef3d8514ecfeb99f534e4f4b1e8dcc1506a3d81906f90bdfeb5caacf39f929025360545a68844dd700a7e0df6b210e95a59e5e17dae80927670ede3edccd6f152acad9d46dfb56a079f7fb08677cfcca6e879ddaf1900f272b923955586d11a6e6922c623e2bcc2d2a07c0000000000000000fd22800300000000c0039416b34adf2b514329c66a8095debe583f1cbccd1fc20c849791f3359f6724aef36bb0f5283c558c3fe961e461d89d5e9df6479aee3c19cd26eb73fdd2e040e65cbf9bde5be859c97fcd1e7a3f26fcaf4282dd5471b1c9a1f1ee98463c8e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000098ee27df6a54e4591f440b3b95c06e8a87efa7d095d930a8a2d96ecf42f2dae03f6675f04f247fb76414461ece5638ac897ce74354722e50e2bf845d860bb902af600d3f4063be4b163409319bf7faf0eb733e9bb9bc9900b404e694668517a369ce0e321007452efcb30f5dc926af959625db41b1176804c3d35f34dbcd708003290b71f4c6eeec67147c001b63546edac5d8e40b21c0d3101de97f72a4d6b7da03278661673a867a00b66c661c3d0b8770ec9afb93973870fb16a25d86aef332ed0b00505de9491440ccc6bf8ea77eaf69fe21dd0f237502be6d2ee0f40866e9fc9dd7237d1925898bb7c7b8389c263b9ed56bf828f7c07d1017a60ea0676dc4f61e032a2889eaacbbe20a8cc17a14cdbd4e980d7c87b9e1a39f43f69fb546eb45ca410220de0493a2f57aa7891a04ad3ce7657917862203ac0ec02aaf291b71a063f20403165a6b688762c6b42d9aad0d93030cd8564b4ad24f39f2a835730006301be82c0210aaab6fb99f0e0ae7a8f469fa0630d09845b09a6607f6a15347406df1694893022d6a7a69e0f3987b7568eab424f431484508bcb427de1d08d3998614380a7c14c44db67384fe74431a1e39c50f89dd7427ae022e783166fd1efce5778250e6952b06a30b6285cc0730299adaa4933d12e6d0ab589c3ea80e7ecaaaeda82a98843e9aac3ed45598979aab1e7b6627540482010d2378dd6856bd34969344faeda375782d6660651121c64c9c0587aa1dfafef00b600b2acdc4ddaea336a1998a79af12db928ad1741fe7738b00de56cf687013b179d4ede82591f1e52bb5e35d1965c21e78c75fb584101e2eee3a20f34816014b0437da009858b808a798fe77ed904ef0536dc2eb21a56090b882aca1cfcf7e89716baee463396653ddccd1bd28e96c720072c81ae7ea2b6ea6868c739aa6059541683d28d178a575fb08bf8bf1a8f638017f64baf0a1557d3356369037106ddde92bdb9374094cd9f15e4d5d6d2079fa12644b55bac1f67ea0b097664894c486ddf7965462d0b03cc9a6cbd49ad2881c4fa438f7867f347e11c81bdcf22efa4e41171c3106c28aec0e53c66ea6c21f7cb2373b8b06d2706f51e4c11b5256a9d6a80baa924c655cc6c8ecbc39f01ef9976802f5d9b7bdf5d4e1230df58f7cf5ba14e2f15a744ffb39441b6a2a8bc25c57caf9506866b66a2e0a061addd12b6a696ba50cc6c1a4d9e3fa4f05317508717c796a2857892f0738ab88afdef4dff1705ca87121c309aa340a10aa6052d728b738486cbffc89ce962093363e1fcf2fa912777a1c63d34049a18615517e0aa23fcbc0560d0971641b9e5514f81933a7443e44e6dbb2034803ec62d43a840a7b9ec4df1ee3a94102664d970deac2fdf16402a4327d2c63cc2cb3547cfb60d3e4f145ab3b6981e25b024eaf95697d6aca23ad53e4fc07f3c4708259cd39cd110997bb60fe17e72aebeaa16fbb8d41cf9758bf4ad7da98a9ebc8ec26fada19180452134d3a02e928fb9c535cbf94df606422773eef01f96af7b39ada6e8f446db73325c3b9831df2c442c17b656243a85972af6af9b1bd61621049c00af3984a3ccdd858c92e6f49e6e6a40932c83457f95850e6c5d3ffc6f33e6796ce7a05181f88bf196b5dc34e064fb3dad2969c5a94e047a6981284f48879dad1d4e0888573a2631cf79528a1658ffb52fc87c8aeb90d73557440ae9dcd05f36376448a6ceb36fe64f4e5fee7271e07fd99a0f23ca5b52fb7b47c7155ae3756f6088a2851b40f263b531a413f5ba8456f0017faba2a4363108e754c956efc314c6c61d231164879cce469fa3728df935c364c2ff0c0fd77a4c0e21d4d000af31e88a0c50cd46ea67ed716609bdc8d060866a0673c6362ce46c6596a655c681b3d3adf5236ab2a58b3df8ec52a1cf291372c8fcc65ac003fd0a667376936d541290abae24ab996db0ae07dcf63f105713a02c6ae926284be2907f669e4e299bc0d006ecb69db2df3301135fa60c6c8d0615ce72605287542a81d6a24fd58463712d9d2930e237059b7e95c896425f7b70efa059f0046e0e3502727de63d3766007e06545ad7727026e77d0bc2385bb229ffb15b5ae264d406fb3971e32d7e73792d4eb5653f2f2949d3229976ff39cead98702b16dda27d3fde13452fd78e31dbdd9f47645c140819aa5912fe42d15196e54cb7b5a0a6180dc778a384fc19a58b088c2a9407d291431597766ef9d5e8d99c47b926d90545591b4fca3a50b9f7711934133653597e764e158ae2a4dc50aaf735e6cd14e2e29db932314becda4805796cfea1bcdaaa1f0a0d130a8b121f5e967f4ba61ffc334463db3f5e1000424793e033422f6f3f620404495c3fcd3a8ee896f9bfa3c8b4cda4c19f4ca4a6ab0a49f64cbc46a981b3acdebb678788b9eacf337277803", "515253520052ac", 0, -333550652, 1537743641, "928b94c4edb9482569c911012e307554755e398b0bc5d772b0f8a21f13c696d9"], + ["139dc5020260caf5d8ea2ec7a469de3da44455c03fd9cd21d730ba8038a22b8063419e52de0200000000ffffffff5ed76cabe76d825d11e48288ae33d2049686bcd6fd8d35eb533fbf00c136280302000000076a656a0051656aa4bddc150312c1cb03000000000251524c91b7000000000004ac5300526a0ff7020000000003635352000000000100000000000000005ff14401000000003ce1e7bf175ee60c8add5796d1849833343452e9da1b7909d6071a0b1bb06c1b7b33e2de2a6aa1b40fc542aca7edec4b8ab6f30fe7aca91db87fc1889643c3c65ad60ce06479359740e8c7b1a1afc8b6fea097a746d4272afc504c58834e6b9900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004b1ac537f54d1d1219dfff89308b0a50c501a53ab88610fe3b522f12a614716dbd4b01cfd621dd5756b5b81c3fa6cdb1db817ff49903277002c9f4e2e5f0e7e54b65734df110db5dc2e6df0be47f9b9129994458e6915c80d704853e203bffff3133975efbb49ab5709de7f19625f7343cc0ca6e36b1049477bc8ef6347227031517b75c28fb09d02f5cf81a30f7a16dd44aa81b63071495bae6c326b52bd4a203084e5362d09811f5e97baca2808df414c28589eff7c54169f683860cd74bf7130b082ecf71d5ff9a7a6489522e1d51ff0a9851875648b67dfc5f7a611f5732489410335778be86b568c7306cb453d284c6c50c3effebed5f0954567246c0f0b093032c2476f18df17c71a5b8f479da59d6ad8c1297f87f169f75e497524be769181b032af5e9cdceaad203a77a61da84e7142649251c9825bdec6a7eb379f4babbf663031f737a2d2240aae08f36097f1aad09103b7ac76d1e6278a2561210fb8377c2dd032468e1a1ae1c9f0f7688f065dc7492beb385c326cdd21b91e841037a370968e1031f10f158801a527b7ac0e8e69e6a40cc7fe6054799239595fe1bc4c73e36c172cec0d1003ac9f4d93f81349639e36f5df808f4f12a0d5d00f98b698ac2b2b4c5791e6db4e4f18be656fbc13c7f28759697f747668b1628bacbf15ca4bbe84af34bdeba0c6c48327742c49f67adcee3d5107914a48d86bf392f7b32c8073f60557daa872d9b6a170b3c5457425e5719b2cc78586f6a012dd043b07f7232732fef84e8de907901ad17d226ae709ecfa2f81b34ed1aa481697b5459df4c4a6a3616290c35ca73ca2811e55e1726e62c273f954a9b3ad71114d7943a80230bfcfc739512c51082b99d03e50e8e7d80b4d06cf1d185ebd4212850debaf77a6c967665d09f6ada20c43a4569b86c5ad6c23cbdcb6886fb113e9d7093f13d7b23e13edde1e15928bc5086ad3e5dd2413a721a989dcde97d41b4e801ddb867af46a6a46075ba14002a9e22dc77701712ff69e17da831577f539bf9e209ae6acf0986c845e6085459f0c187dc7dcb9d670f6a5c5a9347840e6f9ee87cd0be64ef749cf1bd2c181f25a27361f56e20928371f319c344d006f27e4f7b2be0b48c7d3a1b578579668d7252380866587cbf5548dc2d2c5a284f90cd48511cf18acf4519627e3a4a04ef612cb18181d4018eae847f6bd5398b1debaa1ec300e20d4370e1075d3f43b8b608fb7e9c09b4316fcc4374b2ecaa05f5eea3b1b02db2042f28cb239729422a1998e9fe4e064930259a5bdc785d9170f554a73a056b34e2f920fead872b0d16349ceeaafa486ec443377e6c0966e24e47f5527dcb771d86b1abdc6facffad4e189c43d5ff891421e0e07ccd58e37899a12de818cc0b4a9a73cfa81a88b57b0b16c5c3c71fb79afcbfa8afa36b5b3947d98973dadfa99ef2b93cfafc456aa06ecce88766bd2e293e24b9c2741299bc36bcf60e8df362ddc235359ce978937ad2ced5629cff9de4d07c4973db06a19fa17264b72a0c54f132ff30c7b96476119ad25cdf5863a5a6728971dd9c8569e6c09db280a7345bc219c2e6dce2da1748994a4486507e88fe348aa1d09552605030ed496f82ac6523127381294a77a70b740d14f37d33a65b7aed253322426328f432c16977080f63d60b71c809d03aa8516dc1cf03f6d3bd2e004882f1cc9cc4baa6342ea07bf86c1923862e054fe792be45c74872049ed1e61821b7046ad5bc12ef6238097972df4bfeea5949be5cc8bd4a80ee5276ab376e7ff3bae32a95dc19b3d81eb83ee327d5f3cad1c0a369831e2224542fdc3caf12c6bafaf970fb8e8a5161ad23155c4655d62e154f8636e480bf9f3b6c355d1b2137825739c365aee2c7767c03a666b189fad3c98fc155481877c02117fdd46e790ece9cdb950cbb8fb82de93b52be005de35feb4a898ee46b096c684541cd122bb74599761143de3ba84e806cf132cbb397402d0a87fbcdcedd714af058c9d7363b35214831349a72e85045e4195859c77448c5acd01414f444c31507262edd09d1981a3755b7d2a029760443d74a9b0a3e42b790633b195e7b5badcc4b7720743de30b58e26cd496bceb85dcc52872c43dd1725337e34e99b8079654365238fc35dc4104c55cebef30ee9977ae75174131c51dc6482c3bba9142ffb989d3898a68b9bcefda72b67ff100cacf2223c5df784cf41a6ae24fa739b524bced27da6d645f271256f45562c028c54c405ca4d0192473b814e031d6fe8c7429b9aac46c2e8711548a18d2c5a6f168150f9cece6b7b2b7a7fbe88aa4d6a82fbd31e1133be292a04c58c264d75de505e35ff6aacf4d7f0bd1d69fe14adb46a3d780a12858559bfd430d13d78c345609cecdd1e939dd7c0941102d68b240d1366196011397b71aaa749020af0c", "", 1, 1951606850, 0, "fe29a02c4fec29ddffadee7a78920f0c90ac573c78112f4fc1b1f59d15d5a95f"], + ["030000807082c4030109e00f361f49ec2e61b77ed6226155d97dcdbda912c001b1d04d3247a21629840000000006000053ac5100ffffffff04f41f81040000000004536a5151996a650200000000076a53526a530052e2c1d105000000000600655100ac00b73ec20500000000076a00006a6aac650000000077d4610f0300000000000000007ab08b020000000011bb0b1aeba6d30e9f085f3046180ebf68291b243e0c94ca5605e53b72a2d541438e43374d0052b2e1a215efa80e802ee712314057ad383abceff77fec998b894f99e242b1e718f030ea42bb7c58bd062d6c57eb45c0f96f7ef0bcd62e3308e5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004c04345ed9442cfafef8d63cc060510c9c43541c8fa929140e926b66673f471d9f7421e88f0b18cc5f7b7d8cd4fda3bd6503b68a2ab075309359d5775efe30d9e49df128e3c3851ddbfc1f0960cf52bc3feb52c3fa452cfd8055dcfc846b52ef649aa2082096f4871b7d095d60a0d314ac737a83d5b8cec223102db3ccdf6148030f8f861a0b1c34d8eefd6b94903f4908dae15d583619519cce84f9c421030f8303070e79433ecd1a2672658eafe2dbdc7f3663f3581ff2f4259d119a36f42147400b0441b1ea9db26b52e59321899471783480a55f80111c2cd5af59da7c0e21566cc878064ff25c556fa6ac03581ac76e8436af0acd6385a291f1c97e5025c0eb790310fee3f16a51cf6753c9b0beb9f716dd78cb24e7fab6b81337a2aea4dca9afe8032a635e6e593f5c1f0d90fd69319937ea2436c604c03e0f202b05a347fbb8d2e202211496e0a9a08ca2e5c31ee9ec7230d28255fa85a3f40decbc2b2d9bd48807780209c87cbea4d8cf2db13c045d78207eb50875cc65b2e87fe27a239f107a4ee77803051a27c07427f531d18c92f54d1c55e1bc2c417406d9b2953e8653b48f90c50cf1877e304bfb2e14b7cece58d63dc0ca967b2fbc3f4a604ffd6aeacae08fdb5f3cd0c9e30f874034b039ed775e54bdb68065519011372034d085a5258ec572e7a4452670c8275c04b715968f28a8d0941c99c89b7d64c536532dc474257a88aeb670efb8eb228b9bdfbd830a2c61e188378811a5802c920c221a7a6b4762250e564fb766166759f7205dd08c843cca33e687e7f5c623d2f123479ca96454de3beb977fb0e631a38c81219dbf03d53177c5bd9de18ef0552be538216aef86052adcdcf75111fe1353835a746d3cdbcb0e65271dacc5023fa526da948f78b2cbe2e00f758e2df76a5bc589e69f1a0373c98bbdcfe1437d24cd1de665a25b4580806745436ad4b403c8d991694725392cb4d6e54f11e1f527015194fc05fbb169860959ea6f238915c044d822fe9fc7a199bd8564cc443c9d66416f08d7a87473ba07ea663c46b91dabee295cf22ad20e360855b5b576757fd68caa63b5e11126c56d35696a845d2f5aa9b3e368553c26b0e70b98490dec7d9632b31c773a408580faf6fad6b97283f6e171f16b0d4b20ac88a15fc041df732261aeb0672c90acd306bc42241dd19a3bcd995d3406679cb16b8c28ce0a88fef182e713ca32adeaf63cb186b46e6b6c1a7d533a724383167583ab94a756bcd7f73c3e1f7617898bf873ce8269f45edd5f5051d2f95f2eabd5130eccfa71d804b8b979cf9ce363521b8d950f4a13d98239a32d67d2660b0cf83a1cbed9f49641aa07de1aa121ddd0f7f81eb3cfe38a8414c6a7759a50c8651d83a09a8f4a997e30811e499aac67276dfa3445704122a1db113ef0f0a3c70a318995631fdafeb2869629af6784855a19e330f76fe2bbdd4a8f309550a1d373e3e92a4fd80063d1b007789d582ee4b763e03db85e387cc15a91fbcd9631a61ffacd893e778d0d9744bdfaa35ecafe1d8ca8460badb5693329adc0000bff226f737d4c98a3cd5da1268b91e1f11e7600610e847d937877553e6aee5570914e08a711f8fd0608b62cbe5bf851cb96e05d701d9a008369afc4c88e3b6d095690696462166566af2a6ac0da9188bc511c13de618397a57d3bc574d1cde7c550499c6b3b04bc7e52ce7131b5e9b9be3309a6e33c804417bc1947431243af0f2e9465f9e7f8dcb5f88f1c5a12ba24ba95af169570e568133da994800846509df90241f160603f273192c9e2d058a736432b7c3011fcdb9d989b7f495012db299a54bde8377448cd06f2f890265f4b7cdb2294efe11165c9fe1451e0d6488f490f81da93c07005dfac6ffca8fd2ee3e228eb038d02935f0de79506e9f17ba220e21e2d8ae1cac98bead1dfd6931d27cb06f59f889439302d06ebd6f909989b45768d0fc57eb0146a634e44230ff39ca85df6b22af0a2fab726bb6d4ff37c008b04be8dd740ec4e0da2c35f711cebba2cad1c8b2fa9e62d9fff8ee41f54032b96705274002a846c10e60ad75fc07b2340f7b51542c37dbf83d0b022bd8afc4791675e1bbd6af675e7ec02075488779029c52c55750c13536f9715d975fe85516c0719530ee8c2591f6fd5badd38eb61cb1a130be05e0e213ab55741ea08e0cae9c059d8be759ef5fd4d82aa68b23afb583e1285cf3a56b1169761b917987c5113f2703b0b8a0c777d0a619864c80e5504a79cdd26df7d4cfdd5e2c517d02ccdabf82e3313ae230000000000000000b3e62d020000000056a0e208f064b16729561fdb85ce36122f46a1a7adc3c8853cf713b84f8fe94cbef9cc43d00ddaca32ee9ffcf7b7ee05533a3ebcbf96c7664f1cdc2e06ff4c0f1beb1661ce3b211e2ffa07372fd803b1790cd4c405c4fd9332c1ba3434e4d98300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000823c97d24db76572ae4849cec1f92bd4ee1ad9644419b42b06d178a1ca55124d7090d2c500972186237048a5394a828f0ee45fe42f71b05cbefdb37daf603b95e9062b934697ef8888f1a2affc4e09a1b8fb76f25bff215cab4cc4c266fd096444725ce06927b9ff950656b200fb1dadcd2efb41f653a23ff1264c34c6001c8c03280589e3f6ea22f6f6172bd2e88335cea2b5268815ede9b0de48315658ed360d0316e2572f450b7f124944b7546c31ed6392ea7fc20a96025ad311c015ebf612440b08149e4808eadcb4ee55995e390b39e8e6fd4edfc0923fe6f32f8559fb241e28baf780317f9729861d4c53060558499cc88b614a306845b20588117045aaf9ab0311a3f70f7fa5626818dc8356e66566c446aea5106c7285bbf2355b94bf49252a02117d28a73662cde57f5547b7610cd11a6fa365a06ca5597fc6d5758e2a54bb850223589d62d4516c7ff69542b1e7d07daef07dc205157805fb86989bdcf122ced0030773f3258abf53f7638233b3d9a1cc1c57d97875c46999530a4e91fd1a89b3e00229de41c804a072dee4002388d238c0873b0f90a948569d4c14294cffd462bda5f7ae6bd08d88b14097c8cd0003f5d08cc35fab73102011c559828207f3a65b39c38ce776565bbf543c6635073553aaed8d95b557e645693e028fc61c790a8cc50168b133367e589a8df3b9a3ac9975cece5f2ff16266ac314d9dd30df727702b321fbb7071ec0bbc1063004cabb0480abfd88119a9fd1b075ec0157bf4d406ca71587c68ff257054dd4e6b0ff14b21789defddc9f34ea03460e08b6045569a44b8d884e1d53882dd98fa43fcdd28149322294a887207e975aea380c283249837299f864089b1669ee12228ed1fa44daa1f67acf8828fd9064e366176ccdbe7a9e5f30eb1f08e65e18886818e7670345ed77676738f334789b8da352d783171ea404209cc88b4670f60274df6c89968f0be4fa3c09dfb8c1dfb2bc12d5eb7b04c6e0bc87713dcba530d42ff4be6c9e6c9d9a079473cc9a0f945bf41836fc182749c510f9d228d0219def6d2c9de3b12d33986f32b159e632db19eb160d3a62592255156adc7609dd5e53e43f56c6e3735351eb0296dee865b6ac51328c4ee6198582bee857718633c44eecd64933281213307b1f064b6435c0492270b4fea729376ff4fc30e2e33c48d62018d4b067ce1bf22a205c8aef031a5936ddccc6f1e26ecdfdb5c4b52dce662ecedd874e4ce0ddd532029a71da11ba00c8ce3b41b6fdf85e76f471d2225c92313f7ea804c6e9d3992f309ea3bc64c3c0046658cf702f5882322f5f465ffaec078c0e9f05dcb3d8c275a4e707e617bb5720b3b12cab8fa45d26261d44e39aad2f4a0a4930c218d43db7422b74376ac0bc729b9f18e22c417c525b66a543bc97fe84adf881a0970e6d3449604ae5b9566eda66465a4e100bccf3bba89e70e487df1d55d8229f52e2b4c867ed6458ee1f1dbc42a51145f8f1fdcd5b3828899c55c528a09653584ab9074f1160eefe0f5f90f89454bfa55c4f068e65e142f62722e5d13680421f5704b21d3bfdf8bb56e3ab074095bd7c3f1710ae38bd3313eee6f89dc1a61b5154a11abce90da880d6dcb2631e78c47bc15bd9ac9b7f93b141507a6636f99f1a52557cf9ebc25b507191a2a0440cdc6fd94ff0bb113187cd256574b3d47ddeef899d0563a8f45942f17424499f52efaa17bacd7315c360bab191a67107eb1cf417945e3ce56b7a142ad3fcd9ab6f0fb89f14337c5580106981e687bb02bd2fb6c6b68aa60853485757acc300b5e1607be94992b5f1f5721b30cbc2b072d23810edc3649f0f054adc8477e96bbcd3a4a488c0aa99cb9708f201bed48fed88998f33906498a4441fa339e10f6f229c42be5910e0d09b80d15ba31c182b3b3b0710f5eef120e2259f8e7ddd2eb73cfafd15415be59e8ac8cd26e9f981329aead2319a77951177c6164bcb6c0989a0bff7f2c53e9cd502c24a90cedabae574ddb346c274b0d27d3e66cce57effad8f4df23dcc9588f460ac942713f49477efb61a92e34a29327e3b0c25ce733a47fe0a1f2f7659d2286df3a5145f05e2d74d912f91c1e0caa78fa3b69b1161cf6a4d1498500cce798a36a535cf06c709319cc27c334330530cfa538446e2ba6666015c4d8766732f224a6f21bdd254f9f771bb4c642f2d9bc8b554a6f5c48d50d7550a32ccb3eae86665ef7156bcb5c053b17f66f4cc032de54e4d99043ca29854bdc44bd79e24c2259ecfed916b3e97c29ce0a97f483404699ea0b04000000000000000000000000dceed4e1f8ae53f427f71fe0057b8801c9d643287616b7f2bf0e3bcb98591726cf0d35a1cb1973a0ae3f363a5b31e3f0baaa657364e784e1a087fddff82cb5b73244b18da28d0f66734ddce700710ed4f03a425f8c7344da6df185db3133c93600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d65accb1f5ce4e4ffd2025a6a7f2e248ff9fe304ce3306b5aab66e7bfbd2317700ca18d2b3409c5744acc1448aa14f871d932b4a58ce6a4f45d5594a6455b6196dc2323db7fc6fb8a7c7a032a1a252a0c1a0623c4eea50f3694f1f81f8921ea6120900cf1d9dfd3d431a097b28175df6cc5153c5691618680815c8350a7565810215b71e92292735944ef6aef9573600384dc2ec9edcb6311004ea98320484753f030a3ac47430dd812663e09d64cbaac6a7a37df0fa2c3ace51e81dc3e0b9f036000b08dc5cc80956b670013122329b8929fff41a10bc8d35a43003ff9e18bf09f2148d3f53ac2ad8ac633436b9acaca6bea25068f6ef151ba53f8e8eb641946f951e020d444e14cf945c1e601d7be3106c41122f82f3ade594f6997df0a138eac394940204493d57c6ceeaa1a4fc55965d1af83eeed7d6099c3dd6d8267ebc1aaa9e334f03140a1a0859749fb182c5f0458b9f88fa248e0d85c4f77cb5b98c3e58de969a9a0220e6201431ec75054557be48e6b08f3a4656d54fb35458f9375a3ecc8e8c58e7032149c2165bba160704e1e767b6c11def2ab2d7916d58a20d25870e97069eef5eb33240cedf722cd4b5c4625c5bf48241f03942ad0f7a413e567e627d81fa811b0f460070e54e167fec1439d7f89213278d5fbc24712cae965648076331d0ee8d43d2e0bbf709953ab0c14595f65dac997948045f724636d0cf373495fc1dff31ed969ab5817a7741fc3fea630e6e5215ad5200012daf86cf6fefcac63db0f6bef4771dec20d2df85c4a7696b6c0dde34990eec3236aa3bff794e244764ffdf31c007a67c02d6e11c48ce4e0e1a56b1a580bf50631373536ac38e863f87aa8d9be274e7aaeb58cf304356ac44b672ae5aeaf545f0555b34eaf9818ceae868d47bdb34b9bd881a01436b752ab53ff8af10566fb507f33c898d655a4377edffe28570e1de2cdc6a0cfc5f4b4a9cd9cc83f7fe6f04b530cf79d793d74e552ddf7a4262552e070ce413395eba34458af648d5b19dfbfdc20dea8ede9cfa8562b505fe16e5bf478fe3396a6ca468ba1412dbb04eef164acee29433542c48a5c9c1596f509d2b407df82f2cfc62c397f5bd35e3b309e0b596cfd4845575417111143cb94cbbbb97000e5d7c9eff4b4cdeaf4bde0137e20672ddb192fc4b8c3e4a9710b803a377368e08b5582dbe3528c1c8ab7fb7f1755ee6eafa1a026419407e6029bf874be8d6d508f5e16c52698b8909b070fa60f2b1edcfcc0a4ac3129b221d99fc52748948ed273980c424730b5e5b2eb20cd850cd79b0f77c996477e27781ae7353d3c0e5981cc23101d4ef7ce975b6f85b31c83cc62ec49602bba54c45e02781d6f7d12350b1509773ffcb838ae155a1c35925f2bd03b2df9ce16930e9f4193f1a0275e01c1465d84dc0715a8ba1b221f353a34a82c0d6723fc1e8909c8c702f18b6488eb5ad4c1d562a11082bfa37c30b958f9f6b5f720a5b74385f09d59b71e2edf133c3a3813ce58e0450ca044d978ec18fa2f2c76ad15b9080bf01e516d7cac595fff98f8f3fe2e897c9ad871de1d81082107cce7f8612ca5cd05896418020f44e33d6101c9de9e4ef04517ba2695d22cb3e16ab5b468e556360d8308236ac4bfd5d70532054757f17b648db6fb08033c60fa60f1e638d7a50e0e3b9599ec276d0c3dea3c80344f411b07eb03df14b72d231bda5ae063d1aef7be2d571bdab370d4a0e3db80a3c96874143b6836afd3432ea0de4815273b2bd97b4170d6f03f596f61b5cc6196f913b4c08c45fa1f56080abcbe7e34086f9bf2be66116c7abc9e2f14309ac693747c8ba35bb92a98dadb76b54d82eeaa23fc89c21856eddc680837062a62555d792869e17ad69d5fe724e0f78870d6cca11ec9f7390931dfc3c7e1eb3b5b56f81700a4b2f1343052e7d3a4fb4077a057dfffb3d0eb640e857a3b6a10061c39264cdaad54fe1a4215337516aaaea05d27ced7569ae4ba36090a223fc1867d28b749588c84e07254fdf59a58d5b86ae1f310172845a396db5c66e77b08b457cccdfd9b8356d5c03a7faf9f288f8787c948e9a723caef124df9178723a3f867adb44c740847ed1a9a88536c95ecf7cfb3f0d88339d231c34d9d040815fea6a39202d115d6ac6e3485e772b2af5dce985394107eae2f6913f7bdc6e7c2233a2f21af09220c04f2fcd9eb78c71af8cd9694217b7091c93f68df3ed6283e23fbfd5224787f3670de09c6c85989b402202d625d66d3805810d0664b8e3a51e0a7573d988fd51147a56aa4dc1080e11c7ba3f077e2454c1488f6f4ecb2bf7dd716b8349ab47eb3fda4519e20f114191d32e635fd0d17a67d9165279ca31d179595e6c1c67fc258c29dd166bb8a344eaeb173100fd6014d4eb3b49cc1f476ea95ea0b205b40a2f2b92c65fb9d90d", "006a52ac6365515252", 0, 580907261, 1537743641, "94230f15cf0ecb445c9cf68d0a765fbeb35370b8b1095255ae03f77f80e755fc"], + ["f79ecb7403132376d5d70aa8abc678307477ff301dea58110d326c33ce13c55157600643b20100000008536500acac0065ac0d8961694e83abbc03e2a99eb54ef505326fa46b7ffec68678f65fdd3615bdcd95fc747000000000055251636353e82e0d439ea6e6c37e3f6c2f6d915bc50e1fbb636cbc953d12fbf6d172a6a6e92dd0556a02000000086a5363530053655178419a8b031dec6f04000000000952ac525265636a6a651ede0e040000000005516a535263857af003000000000000000000010000000000000000c9086d0100000000409e3fd3b013b6fb50410665269e1ad8cb3421a87a0db67181c75e7b9603e8ae6c30f1a18320f95f59f799f0c3509da85a62401fba352041d7f29ef2d83198808485608de0d878b0adbe5954639cd873c9962d7ddd807a76f985090504ae9119000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008ef28ef0582f62153b64b62515027971722583d27efd5a0fca56c50cb242c99291712031af7466db618353420f8b5b7d46111a81953f4f8dbe328bca954f09414f322caf195cdd6a87704a8fb8c5c58397118b8c478318b2988b5fa2fd2141b480445ba782a13414f96bdbfa991db87b25a58075cd5735a4b38834a48cfafdb4021809c572e8fa75d3060508e402f6d285d851dc0aa2eebb196c682d1593a57d81031924a8e028521f681853fb73ee33debbabbb3e0c5a1c4e9babbc57dbd1f1cbd10b079c2351a0ca2e339e3ca7f5b6857595810d4d44a0e76a853638e4cc5bec46607644643c0ee3fab195db5699bd6797fe507a62ed290b898adca23c4a8e76c8f10322bcf5f6fb351c9100f2eaf403ff69e0e112e27f6a3630fb8bc96f72abd708ba0220e554d2354b98863e68a21234bbdff1046f11ae572fdce6688dbd3e0dc344e5030ee54ac45a079d22cbfe46bded8c53e2d2ef4d1d7b8407e2200833da18a5d9780309f327e48df85ffcc26858c0244e019cbe3d23ee00644cc661283af5d427c2240213483ea903946b66aed267c28663344f69b47055118c7c8602eb6a5bf7645ed6220bd3f3cee7906586ad445c5498d3bde9e50573f9d42a1ec0b25677e3b0e910b10eea1e64ce957d4f220969aa7325277da9dfdb0609a3b4b98c65e332c357f72e7ecc1b754a0e75fe877c06a23a6c845760fcaa82b8676cf309ce05bc6b3e96cbe3f99a86c1fe835e0b592319e829a6bf64f72cc331d5718306e4353523a82940356abf81d2fba427bf09edec9f44548b6abdb2749368b892c51300924258071366c7bfee1c99fd03fe8828f3877a3bca22ec7e6123c072d54557afde0375a429a049c5990e4f3311647975f1c74c255faa2ffa02f5c3d1560b7825d350934b6545e7aed971b9c7cf4c8ff22ab07d63a7ef41b012e848f2da55fd3e95a87d84b8618fe812fc657fd68eb68bcce6fd8fa677d4b36c5ffa113470f0bad78801a9d0f808dcea511d2f0210d4d6ad0efec3fdc8e711fa8ffb8f13ba7ce19f5cd52efcd5218f886c6f645afee5eba6f1b1155beb2b35afaccb1630af6e333038a36f41cdc0c52db43f8cd48266edad6af243be59848fc61c836bd460d1c713c9bb69269111eeccf79ece5a0a340507d4f6c015e29c834d46d9445d0b6f7f9b05ef451030f4f23f416c6466ddb2eba587cd3d7a6249852a0229dea83a8476e060cca5ed976fc45dadc77bbcff42c73eca40ba6a3c7e46e664dcd637e4118e6aa09f77acc646f98c708c102550e0764587fc21825b9913dbbc4d13b292a9ba698b1c46637831ab296e47bf97bff6e269be83735e112d021bd3120bd4310a29ad2ed0caf0e7630ce86650ec2e6a0edc557c76abe4a7b881ec886f09645cb2dbcd651c31ec3ea88a21b58a57d598c534b0550c9ba7895f4974835afeec3cefee0bc8b6966dc2fde08667f1b5b3b696cac8a318fa3143d5a918f0ef3f29398d87172dfab95192e70bb342b636b807f4887ec43e74b9db2d7efce6a596be409413750fe108368926960a21f475a213cfecc09287d6b866b11458c608b5d35855454aa5bdd61b114d2eaa17a086d0ac4f9683d2d39e0be79ad20d331948ebe6dfeffdd2124196b1af6cefcb097cc3d171c82a999d6849c06186aaafbe03084d9ac2597f92f86712667d7db84eb676b53565385c2a99f15967678fc213c0b3ad19a5bb2009fe3cbbcf7f8823f94dab3803132d2b8abb64c9bfb2a873130c594c6443dce9d06b5338c27f09c16e0d37729353d5adc1ac488960ece0d8d6a3285514e6f1fc137c5db8e204dd0aebd6c183bca475426fe167c4157e4ad02f3d74e96abc34372be965da57d58f91acd244357e005e7c9487b38edf901741c2a63325bd5a4fee26ce7c75831b3fbaae15af62563050123747f86a3903f002c628bfceab922714da4a6dca84df32bd96de54eb76c29aae585993af35278e2797f403a75e56483f19c8c8503103b182d8b19ed9947d71c4f94e281a90d2078417b280aee865764237c1264ea12e63ab317de52acdee13235691d803acd446ce3eb67db7fa1062e6a8225eeb2a242b505b34b42609291b378a9f1d569ae517913dac76eff075ac82cb8210a2795b414f5b8f516e04d4749ad158b6dae868c29734471265e092bb529f747cb9f14a7211588a08456d2e506d28e3eb3ceda5fff6c50406a76b6751db4472bb8ef51dcfb6a8dc851d46d139d293ba83207d022f1655d8bae5c79ea921717d56c3aab4f03534b335f5a0db02e2819c73b58965a126eefce9dd94f5e02674e8abd494365b7592fd2cae3fc47b187ee6d40a1946619a986e180ee622e3e238ceec07dc26a57f12e1e653ad0a75e9108db03abde2aa56cfb614d4cda73b1f4f73d82208fedfa855dd10f037b59d645b78bf0c", "6363", 1, -1165762510, 1537743641, "ef66437f30b4eaff53db531155babd7aee6c5f2fe8c48141a5f6f2f7f35d0d62"], + ["030000807082c40302b871b03dde4d889378a1614aacca208d92ecb19a2dc7e29c7ba316105cce21a3030000000963006a5351006a5263ffffffff8d7095fc1c21a17f2b46c2f1749909264afa5d71e92330f62d79ec014434186800000000085152006a516353acffffffff03f8de4102000000000252528fb8dd010000000005526a65ac6a96c14d05000000000353516a5d0bc9a00f05adf70200000000000000008bf63005000000008d6a57249ec322e5170847a8240392214e8ff0f3adeb8b8d1020c031cbed8103eeb52757f76b7ba26b5c7eca9fb2a149aaacb673169d024f3a60b1912aa5a2c1fb7ae1c2b32b76114a652df1d80062305f56eb6f3fee9c41438f7e182988d2860000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015cc218c5a2db4f440182ad4f2a47063caf58c18f315c860a8dafefdfab397e7c62011308bc402aa3a476b2762b0a5b9666b0b48b0078fbc8968526dd73ffe1f3aa92fb5cfe2b754a8f3b3bd886b8e8246e9a8fd29145d5f36ad82053c41b7736a879da833c59c843ac4b0f80b18a2dd5e5473ac4f025487e2c68979d214a37203152c5671617e779d4305a8f4aef983a8348523c65025e12c7c581b5663e6bf81030e36a55f47e9b1cb27c1990349701b92a9cb3cc5cd78a054c9cc9d3262eec2140a06c005e080d804ecf21099457090db3908077c5a028f0495b9a0716c758b569db5dbb6f603a00bc99daf0a942f3cbc05c0b2cedd9741e2469f913656606da9380315223801ce7860c5b450df89bb9150439083016f77b90243cad7c72c4e2ce6010321eabe578a7fb72886aef8350918ef85e9308f2a28006f8ed389a80e7c55d4fb030272c1ce359d73f5b44dc87f914856edf6b77d28fd8cc1e10c5399003cd63cb30316f0d17a6cdd519df47f831a38e02eec571475ae36a2ecc645046347780213bb021a8a4c1c4457b1d04f51a1b9f8ba5e926e11009af396699ed41123ae90fb528497427c791a14e109d7587df985e99fefeec6c7b874e366bc105a06a14debd24998526b5876f13dc78f6a8cd65f821c07b8a7f5734076df51aee58e8701772247b6e4e2835bafe100d374ef664f4af991ef55c7d7385e454d8a34b1d484433c51185c720b70064b61fcc92fdd76823381679d2012d4811738d27b22d85f747519afa246c6a21338a6314972c4af561995cae8858ea3b2dc456848cfd897262d83156e08f8cafbf3cb60cd1e2c0f7f7a03bc4fa8f6b446bd3182f63129815417f5eb5bd817bbf767ab542e69de931e1826f93bc75550049969ddf7da631802bebea82a72e355b6a010b9036a36401862b03764551a36a48a8794b271d33c1aa6580f862a74908ec51a13448de1bc812ee40946c8f85ce49b0387dc213a4ccae71cf1b50105f095bf0ce3bb3fed0373b8602cac6f23af892b71833b74bc907e40eb23e5190c886ce0b132e1c6b102bf36e910b8b83ed365030f3cd584e9d2ab65427cf3d2cdd9a0c5f7441001ad8c6f38c7f815d2460281950f28c5a751da9c20448a4345ff326d8e0535d5f963d1fdbb4d2d040e3c7bf1214ec9799668f787f6c5e5e675c2cca231ddde2332ab34b358bf00020b46f48e8cfd7a409522dea34cd85300f3dffab9d4940e615d8cdd55514a8e7557467a5f798856535bf487ca2893fa54afca01e0be369a5ab8109e5cc0efa190eb08254285fb30ff83b3bf3f0238fdbbc7cf0dce8577ccb79cb05e3b5e3d47ebba8547d9708373d3418b7ab1bbc52ecafca542deee1bcc9cf723b08771378bfce389edfe51d453197baa90193c12814da995434da7b6471ce87f224173a0df1ffef204e87d83345d9ffd9883a30a42163c542bd56cfb571200a622fdbaf104135983e8751c3b8b429a42f1a431901b817a2f9e58d28b541b241422a6aae18965103fbf7ecf0d38990d17c30fc25eb9eb7e604f49784d606786e0c4f5c3d2542e27ce829f60e948acf67d272ecf1c08535888d74218d882d6ccaf3b13026214bfc6f443a86647cd81a106f81a501168bdfaca52068bb1ef9127a362b3da8b38af5e9a246c536624306208dc86ab89049e9d5cbbce5720053c9a94d0b51a39e31bca1c84208a1829480bdb4bdf99796a781a9489d26ed142c560109139dbe9da8fd6dab03d06da9a4672ddb1aa79cf7cd1dad671145433c44101f9935d57a785a19ba0992ea1b21f99d0f399491ab56b22c980d653382d599d8e0fb916a83cb9db4dac50ce389e9a8b2a3c6aa88cc206920328ba3215950420f014262e5ede3d6c0f1a4d873c5fe7c08c75ed38ee118f3bfc68ce243c1156092c895791a43dcc22ec3fbc4061b18b2bbde87f7419e26873d9dab6b92e5f17607d5f20222cae55f3b8143fe96d194b66445ca5988763c5d0f79aef28e164ea3f2d1c8df49662681276a3e5ed24a9aec9d9b64a5689770d5afdf216e6eff280fe0c80e1d28d7152704e4df9c06040645bdcde54847533e7acfb738ce2e7f1341f06acd451cf7b2e6b910c2382c6501d8ee46fe57e359d76ef5a2a94d4268a73563acd2feaaab22e6677434de8bc77fd49408ea4cfce47d45569a7a79a00e62adddd2d37285026f0dfd41fc81cfe6fac9326a531f5baf6deb4b05181d75fd1629f53d8358925a23c0d94b50d2e2b67af0b6e843172f1d602115b7f5d7f1101e9339a20e6a11e640a160000000000000000199bb3050000000098d17014e561ba1694d1988c5dfa619bed5b4e2a88b4cb4dbc289f37b979139070afb5190b12450079abbf0a33b8df46504bd4f2747920f00c56d27a74d0442c790e189e5af6945299c939e478c28772108f3939b88676241a3fc8a08e40895a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009254858330484f6f45083f241b8e616c3f60cd8c323869f7767812d555096437fe2dc6aad326d0dc2e0c0dcfd1ee449222c4fb4a794904260a64a6fc8e2d900f1373a4ced9bfdd7699bd30c627f827aca7d962b14aef4a23b5512f3af247679083c9bfe3031a00c4d2af2cccf4cf2d7b9ff1409bdc3f5123338fa96347a885e6030623168067eb38976a0ed843594ed36b24bfb7b8e91fe989965b9c21a629b2e6032627699a3fe85873316476389f18c683edd1172d45b0ac95f4af502455bcb5270a0411590e877e77346fcf0fa7feb4dce4c057ab97e0cca619dbbf1b10715052c7cebdf3499de109013ca5b3af620a2b90b4e1aa9ccb41a3d87e23794ba12ad618021329ad7d3f84a98f5eca79053373aec327b2e394722da1ca2c261872dfade1a70213f8c0f4c916b49783e5c772ae7370e23614dd48e3187a9c68a9919ed053f2b9032a8b5d5550a0242a64afaec594355e504b5529c1761fb4ad45dfedadd055cafc0204775eacb4959454327b4549a337a286aa8dc59ccf7c89f16940c6a1cca7be9a0217ac999e7586dd4145d4dad98724a53b71bb5978f51789f629dabfbe8eb17a7a9865ee90a2162062472ddb7f7c38559be4f39ec38c04c712ae4209f58e6b4c1791f9d6145cf269225f60d24725dbf7e15e5993c46a845feb5f29f87d52d09b352d6574c4df90beebf6470e777849bd1bee190c5b3a5591a2d53cd836558a0ff05a585cec7f3ef7cb025e09c7c0dfa5b0217a5c0cda7e31230cf780c3678d3dcbfd29ec3b941741f251a402a078db6c144f6beb75a5272fa67cf3cf83dfce7ea1f69aef046c55f96471f949e6b7186bf45e479d3cd6a135f9a8ad85415acdbe107fe36c52c3a69d5b5bdc1eb2120aaed9e2b778fcdc0622d7c312b85b22a1cd20ed5047b4ddb389669e6f4c888baa0cdc51fd09cd5a6d764ad9de33aaf735b9b04858b0427c91a2291a5d69fd19f27bda3ba60e22e3f30afb68b578f0274c172ef200b998888f9ea2f951ae08d7b147113940ca676c903f29292efe7105c3404c12313b0b8a1e4973e71cff46ab2275f14b277e7c840437f29e0f93c31a8ffb158d23347086aa53e2b39a8d4a4fdd997f7f3e9790d37caeab13abe783eb8677fe42b634c4ce7ac9ee293a35cc83bb838de3f8998a6921348268727bb215b5f4955ffd2a15f7a52d4ebffdeeeca17b62991b7231a9edabe8bdeb5b0d2dec1717066717b3e0ad9a4e52b2c88e5812659150c70a1dd2e9fd325538d1826f99d0ddeeb1269ee848c5bf224f322aab0de200998cf1ab307913a8366e2f89b4dce33cc4762c0dee08146c2e0f5965deb6d33a845188888e20c6e04d215be6825eeefd1f02fc8c5bce8b63592490c2e6100d3948fa220bd8be7ea1cc8a8502d1508a4fcfaac68328a81c8fdfa2e7d42d8be579aab55e7cc5c58dd23b37ce14eb4ef6526ea194defc967f670db60117a783e7f139162602fc43b2d89f18528842d6f18a98af893d6692c7cd2538b562ae94b77444ad344fe74a7fc8dca0f3e487c617b11a8b677f2d68cf6f3e55110b77d95a18e3e34192eeadba38639289d4ee29a8b047582d59ecb8780de0484d11339c002607f1b39b538bf5b37e5d06beefe0b0a3eecce9c3cd5aef5819cd85ba7b22f596dac7514dbdb744277f443ef3b0623a62f1e3f13fa6752a0de1600441ee234a250d16ad9db33ae1863e1c55c7076fde7a5cf73f23f909145d9618fba8c9a4bb8dbdced427fe9927d4898cadd46feb15c12c23578926aba368736155313ed26554c54a7631a6a0b7cfb25c9387db4ada090b44a740cfbbfb59671ba0e480de51ca727a20202718e3fe00bdfca9dc10cf207ccb2dc4339f6724fd036905150053b5f9e2c6fbf5ab35cef0ae0f9fc491eec2d130f3a5ec01ae4fff71bffae0ba9eea5a899d6b0682223720c5827c88920b1bb70d40ecdc29b0c6f84d54fac30c2603a0a07f4cee100f5799a4fd6810133b91512c3f240116f7196d8087a3eabb036ae0a78fcbe8d580cbcb8384863e8b0681b1fd6f987dacb1e25fa1dd9964f4bcf7a09dd9003b8b50df46e8db025f896eb7fc2cd8e0136d897d4025e4b0d5d03e2adef94bd76bb09a5f6b47ff4b310cab73ceef8a115fb046011c2c5172748a04d585ab6fd2c29a700bbbeee88f0337e318fe0e9e9e510bc2cc96515f33bb1895a566faa2a92b5c39bb15b4b2205f8ae12f248eb64691146ac19a7e0b49300b175c7d89d900de62a368aba7449aae9ad95ed0092f47ed5fe160f0eaba1998fc778842a75fd4bdb4c3761326f38d2d0f89c82ead2b768eb61f05c874c36f808b2d77e74e575883be91ca05cfb6ce92ff3ca9a05cd80f81bcfc0dfcb41aa5e38f0762d4203cb68e9c417c6e56b8a1ae63ac2e62adc5c595d06c1f66e5611759232b358b5601", "63", 0, -243327454, 1537743641, "54073895d4b34713104970080fb259bb291e531b56e810ae9d4ff15e50b6cb2f"], + ["030000807082c40302df7ca526bbcdacf36bc4d8bf77f235fa11eb6d9e96322d3d78f26aea9c17188700000000005145f80f873f3f5cb4e2fd854090c86ff55f7c8e171ccde0fae4be61ed19d4fdf210254700000000004067d1e50460ee260400000000026a51f050690500000000036565652b31c402000000000700ac636a00ac6a9176bd0000000000096351515100ac005353e30e1c2b0000000000", "52", 0, 1575417702, 1537743641, "f5bbf119ee1b62e12f8713d1edd786507e1c4ce0f5dcb19625ed4fd79bc71b75"], + ["ce57a95a0391c9df0c6657060e73d5bad973cea76ac07cf9d4207b1289b2f6504c636e696c01000000035265520c59a2990752383f04d3e407ea8d0468b5cfd584be20d2c5c9d30bafedccb56d70796f7d010000000653acacac5251ffffffff112b25f372441f96001724ee70351fe304c719c1897ed12f76587557bd6fc49e020000000365526afc356649013bc62a0500000000016a9d5d646d0100fde5040000000000000000000000006686d74c6246b50d7ff3d06d1f3f7e38ea63d39617aa60503414bc924acafe55f14e2d0c78cb13bcffab75de9ea9cff4e2010fc195c3d055a1bcc2d8072c5a08c371525d715f5d9a3081eb355a4baee7646d219032637ae0358edec2de5b173200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d37f0f9b76930d59fb637886048b06a6ef0c12fbb480e1c5a7315fc9b034b4b7a46a812bd779de2434f31a3e2ac875da36c0e8d447d356d966b9d79946736e7267f42ca3a7fa4541575ade9b81889ca18fcc7ddb5c2591fe24bae146abb793892747847bcb370fe8eeb3cd9e7316b4763cad68a2844122564065be728ff5db9b03118bc12c353af96b23581cb1b12674129eae3b1dcc7ea2dcc880e09f08c6a77d0227f5cf4deb88492a3aa57034c52224cf9f54d579e3f114a58ef9e20329d619030b076a0e819137939b026911a9969fcbf1a84ce09c7f137768a0bd878732984a25f226c6bce4e9d3149a9c3189a45cbec98576dceb284b2e1ac54a437f5845db08031e5ab20a487b970b770fc4fc39eb908dffa1351640f075900812328df308f56b021cf34088c3c675eeeed4727ad34c4e1d4e693af1437eaa8e75174cc0d8800f8203011aa504994bcf5dc036152321514f542051d09b1422b1aec323e6c52aba01b3022d1663a0c5c28358205099f7b4835602267d7bfb89eeca74c39067c7d8696a6a0222a20d24e80bee1ba4871cdd3e1dd1f9e1e7e88d5c4f13e5f003401a901f97ceb26f6a643d63921d62bb0e4778ee62261edcc7c9c0bf491f62bc9d906dad13df319a044ac7bab8084fd23c99e3611cdbd4fd7ed6a97d844db2ddcdf09aff82075af3bfe6f12e06696d5ae6e4c026e0b4cce68d0f6a1b36e7e11d2f253681eeea8a405854e89a42d50010f732b98862bad341ab6271975f1d667950f6c00b0fde8dac0cc08f16da39c3b84529ae8bbf8eeddaf42aecbf0bea6a951e2ee495d2fcefa22b1cf8f53ceb616d3cd0a5bcdd069f8fe45abe295c9e9d1861addd4e9362c876d1f0f3edef9bf3df40bc5c2f4a5bdf4205db8bcb6cd78383ecec4aa48623de4ba996abc4dbef19d69c7853dcd3667bda45816dbd94858626107b8dcd86265fa35e35e00c9348bf3f660f47b09ddd2991e2dfe2ba4e2673bb5fff5fc3224c1a5e68bf44deb5de80ed0de9c0c265d9fd552e6fb98aca15583c91d07ad7ba4e813b67bb6671da18d9143231058405470e894fec35d7c54bdfd7f6ece510c787ce06bce2a2f375de2c3dc77a3015005047016c46d93c8531bb1652bbf195d488e091db6f9957fc2ed640b42db0a08be2dcba07bacbcfae12593412d08b0a0bbab1f62d7886d80e39780367c762fba27df37d9252679593ff5c0ca7ccf65a880d0d640fbc7b9f4aa7b459a7dc47cdc6f998f58b664c21835da54879500b83fdb1812d02522043a355b94610695c8f6601b56b104e2fc9f4c06f6e29157136e9dbdb7983dd06330fb7a00607e15691e4979b0eaa04fd6b928a7c25975ff8ac643e02cffb93bf001b9b547135e6ea11548c92cc6adbc8dcf367b52b9a99faa1faab3d358db80c50af521d52ef4c3b9af42cf1734f1345e7326601cfd6e4830c5578558e2ac580c6fc22b098c7cc94cd4012b06853cc38b30be872f8a57935959dc006516731043867940ffb8f40a0baabd3e6931cc1b8156b4386b1d073a55242bbb421dd1e30277c8d0d585bcb22208fa07ccf9894a012888e7c12cc0bf80d977f49cd5b9567238e54fdd15a1499da4a2916d7df73c93b98d881c21791867c13e9e4eace818f9b0d6fcc959fb2da3e18d623bbf52aaba71c15d9ea9f13df667f4056bd8227da7d5d353e6afcaf58a5547dda0b4759971dd0879c07e570fa2d9710cb6faa9cece7979afb767db43d7b1e873b2c1b6ea5d3031b51b302140990d19f0a31eb8caa28f51a29b33dc0c0321447c69f37d5396b5f40bcb8a0ed598eaffc26b6419aee39fc13cecb6e9f4b08011a21c503b020bf439f469a315fb79326f71b460ec2c0cf633c3ac444d590018fbbb98fb66fc48e320e4092a09a51a42da4927da7a91e036d2cb1865391917d0e43fb19fa9b2530fa5dc4ea831e0411e0ed6c9c1712ba88fcb32524791fbc81a0c92e1d9bb05c432c607e5f2686764f539e0bb9dd55721c9e8c65553536fab9120df9dcd28a452cc713ff9b5f3319e84dab5bceb1f8e7e5c4ccccfb018de0645215ac15aab0cf0ae5efbc7340978c8d84f5753d29a7da28f66601bf775ca3fe81524d54dd70e433a99abcb713102c4419655baf1da2b157ec1d6a506d1b6b70e62f7606114fc115c44269f4be390a37f40578cf2bc1693038a7ec82e3db942ee67d93be8efdd17f16ace7ba2f61b2172ee19fa5a1d8c5bc96a8b5085c1b465cb52bbf893295b0a1779c0f9a72cca1daba61e70e8b7023e3263f79c232524e57da77f254995c83f6e4d3383bcf539a42a47d6e410041cc377eedbff85a24bc12ad6a5c8f264368aa4d5848aa314ab716e2ea450e9378d628570d6000165f253e7b2ad97e82336df06969453cb69a8ae1a4853ccc73f8af5921051b03f7fbceae17befb03", "6aac006565", 2, 1508316050, 1537743641, "ee694798863654a15dfe1e91b38ee7a15e4815725a3ba83a8aa6dfe8c78b9d11"], + ["030000807082c4030109a8e5aa8a755abeea81f38273d72c383055c202ddbdaae373eb4fdc304fcc7b0300000008ac52516300636551ffffffff015f6f2e040000000000fb4bef1e4938106700", "53ac51536565535352", 0, 2145876141, 0, "10b287949f2f42275bb8bfa6c08da04cd06fd29edffcd5f52e0945dc6119a7a1"], + ["c53f5241049d207bc2188fed33f4652344052ae04183735bfe9894b1ccd1179c6a7f0da7a600000000025100ffffffff8e0df3986056f1aab7091aaca7e235ca11415a3343872af15d17dd553c4fb8a302000000095151000051536aac00ffffffff320b6f784ca74a4272ffae7fd4bd2140a42726284d869facd6027bf36985b0ae0100000007ac53ac65635300a3a2aba7bc4151410d90dac57aad0350fede1923b2cf457551ac29c61f04184d0b155dc10000000008536a535252000065ffffffff0200572f05000000000452530051509d62030000000000d200e3dc00", "ac51", 3, 2129491232, 1537743641, "25059bc63d1540213c9387dfa4a6eebf5cd64cdc270494347efb4c99f93162a6"], + ["030000807082c40303ab8c4d535eb21ab7810de9847c7fe97f95ee72969fb35948cf76f8404b07535800000000026a65e6ee9917f9276cd6f1e2b886061dcfb3afb2d91506ea32f5aa9eecfacdf7248f724136f3030000000265ac26a996557d175a2e146fa4ca296fe48b54e90d0a8546e0b165cfd3f308d91d0780649d6f0100000006516565520065ffffffff0145cd000400000000046365006a000000000000000002d0555d030000000000000000000000003a0b8126fe42f7690fbb39a63f528340fe473c9fc129b85478812e72ec9b48fb45a027f134c6c6a2e2ff5c8cda88621a5ecbc1c9938006037e1fbc5b926abc6781af7ef37c0372da2280ec1f1d3f0ba1d01c84d7581ddabef4ffe4a248562b71000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008c35945384ec2134f1603488722eab532c0a9da97a4409f5ec3c43fbd3f61be2e9f0d2f0b7a0a88e85c40b4f5a015a5da6503b44a7d21d3b235e851eb503c03daa226f3ae3efb49dee7f81fce1439d2e1532b02a6e501193f73578c134aea9a67e407a649b434fca2d7c453e82d70bc3f4c49289cbefbef0909110a39b74be220203285869ec3a1e19bb5a041cd6ae62815b6f68db0af80ded905054cfde276544021d2f7a3074df50b2d94eafe4e72de801f604bed672eeb2e5c6d4c25b56a5dab70b076213c07530fedcc8847c3331479473b03a2d98752289c4b2adcbc681765b7bdab8889a2580abf489d03dabc4c647c4fb7abe007f46502348ad43fbab1d06d0030ee49cf42fdff8c930ebefbef4f7dbc51d880fc9ff3e60cbf24751fe2d07aef603176e2cd71b1e8e59fe551f8daec64adaa3e12dba7ac8517739e482be3160d9bf030b77852acdf7bf24b13e6cc7a430cde236125d001bc28f884082dc9f1874d43b021b0b1869089557578fd8600f8be39d0c1621bb076e5eab7d37c464fdf6345bd70215867d053863be47d010cafb13d177291286308cf84437568d62427c67a1a81958b5ebee51f25fcbd8606f1c3c4f22ca041e931f920a2704f32ecd75cb1414d6efd8211ff899159736b437a021003fcb69136073fc2d9a54753d1c41b830a20759e3953e29631d9cdd194f29fab3a841dd7e3ff5acbb4cba644c118cacc8e8c277164d9a1cef37abdab007bedbfebe744728e0cd738431889c44000e93567673c686ff9855c0864063dfbce9dec34ac7cfc8a8ec3c556b4e75a53d0f7d873010b44b680330a8eed7dae98487ca9451cd879dc59239dee513df0e0ba8edc5250626a1b34fe933537b602ed8fb0b9c61b999635931fc1120a874e8f035ab2ad8ef83e3e1dd35288badc117c8a578b9a1094bce6583d25f59fb32f37b09e8b890ce4585dd0f091762ff8395a826bd0984b4deba26212abde9f9030bdb6d48c660414279991e085374cdf6afe06b60df21d2a22b747ed6ed330c9b082c496bb51af9539389b810156e5429d51d42108703df862abf86e9e396186667f282b33a8c0cd9d9395e4598a0bab539a3f7cb83a979db618e24e4cc1a2f71a1ee97fd2642c6ff85b17b307380c93cdf7acda30a77ded6782fc7aa819960d9fb6abe138f400949a36ac273f1d5f4f1ab598faa1d9ff12e5a882bbb339828fcbc27bbaaf0d5893b02117c23361c42556c6b55faaafac2b73b8e2a7a74eed02204f03bde1feb94941254ccc5368744078a0545d94cf6d907df5614ba68c30aa2299ffc9d00a5ba8eaf4653c98af677dbba71567fc0d812e8115283b10a487e0c22441b17c7b68cbc7c2ad5d0b34b6c15417f49977ba2be9c91febed47199d6269d630e93d2aa9adfb59e6524aaf8acbaf7c4ad29b2dcd3caafab56b517b012a7e21adca298c8fd830e75eca3415673d5b76bcd7436d1614e0b495a3423ba90a82136ce4534e0b015ac4973663eb6a73b5ba387903bcb3cc8f76a218181ee936b512d648aabb2a0d23af2f45c6a2e4e4643ebfa07ca6aa17415360d85440476f64dc33ce07fc1c1ca5ef1d3e7ea4bc48414204337642a0ebecbe90d7cce2adea52a8fde387df0937b8fb58048855fb9228e4e7ec88e4d4e8a9618cb6f1ce4691a4b108aee2b3b6a2b97cc308fa3d49750777a094acc7604ffb87fbf5c90c8370d5a01cfb221693a565ed6f9f585125a8a024ca2323f7eeb4729f0975dccf8d3af5bdc622bbad9f59ca6bad82b4be6e5c79b90f49895618f0eeebabdcc6c743da7c976b7c31535c5b4b3dd9ebbf8dfe54af95efc3d1561899cba43a2ae3049ade19954e33847f13d0d2c9a891dbfaf82ecda64e152cb6778221f0da1d846c39075d8ec2e2d64d517c5456656362e9267e7e58f8b9ee4eefcd9003e3ed6e5f39ea4c60b6afc1c835f474bda407696759bc91a4f0c0019e28f4dddf7d14a2f83848afbf2f342b3cdd5874a534dc2b87f9fcc09a3081bb0a071493225e8f492a5c1da8499ae6e401888451ddc8353f89050aa12b367986ed49f34e73872e566d23cfd1a5c2cd4b26bf24578c404e723894bc4ad85056a66dd006725929a596e39315cbac19da3fc73a8be29d4800845ddcf37df9684fc50091c0c079c6dfc3ddbc5359af9727ee59cf7fa6e3d19c2e7d35243da009f4369d65032bf08542c530463e1e6f7acacdee4a84ee86e508c4591962122cafaff8ab0dea0bb20d6758153905dccbe7a62270654a24f6c4fe09868388a64bd0f0a4d8df42a22232ba402000000000000000000000000da719927db953dff47392464724e31c7ce34ed57d069b0ad228767720dee4294133b92ac12f90cd8ce35638afe2e7d2b8d98c0a62b85524752f0b3502ab5a817d69db11f18aa6937ed37a53755e8ec243877b5e3e1f37431acf16a3c37af0800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006d6d7fb93027476c2ed5b1cc2282d3ff494b5bd4b3822d5b1c1ec57f75836d0d0c88365fff388b523eb9e4875d15b77b3e5a16c47c3c7b7a59c6f354aab5c669f35aadfbc39fac9722fd2c02a1304f359b2cf0e1740ce184db42f40b13fb15ad70e1594c64804472b800b29e1b20a26d4f218706ee83b110ff87e1bdef3f4064021c3392129bf70de3a696c845f36ea39e45503b97de937acc32a085039cdbb0bb030846ec59b933ce92af2548773170dbbe68b4b5f76b771d3b113507886a6fb9ef0a0234e49acd9fac3255866d3cd4a2384439c817a4f9483e4ceb9ee435e47a52ac35de575e2d25cfedb5cd0107aa4c0a52b66d339b6b84730a3f3ceb9eea969a4f03023ba6d4ee56471802d4d13fd5e62886b6287635f0e8f9e0891026ed78adce9b02183b6b60fa7dcc69462c36f3b3fbdedf897fa99c9f53cb5564c81a6336176f660324a90dc8e8bd56ab51f86fc58a12d6e2ba74fbd530e7903795416edaa758e8c50310c8cbcd51f9bb781c2409e405f899a6628148feafa4a0aec88341a275ddd6e6031cbc4613450894e240c87de4ff102ee77127545980365eec75cfa65c0223a0fd84e27611ba23d2f17c6ed4cf73f8c8b45e5fc37ab565be9e354ad394fbc2d72c45cf659ad3ab06b6482820cb6935bbbb80ee4108b050405c666b0dcb8df569889d23067d441c9786c71aefbe485c89c0ac1e75228b09e1ab0722acaa751d1b6fee9422e82322c81fbfe443911f42511f6dfca0ae86c5fd6bc3b8a9dc055ef0c370c238712ee6aac6f33598074612eddc80f8c46334a54945e147a2a23eee448da3f5326c2e62a7dd6e3792120621e4e8c47213e83df1230f7030da0f74291d384ecb390eaa0eb9c06fc64e91de971f0736d91ce8e7835d19f6840a4a222288e162bd411523f90b2e3884e2211ac9794baf3ba91b032ab0070d1ae2291705bda9f5faf87d47a64289bd6af908c4fe9dfb74bcdd21eb5d3963882db285178a28bb4c1112a63dbeedbf862bca871831812e727f0e0fe588e7a3ff605061c3033ac73fa9a20bc1e9669faf1678a43cd5dc2e18da0d3c2482b62501c394baa89bbd2027ab25a56c855f4791d56c541221ae7a8ff7eddcdf239caeb004190cedff8d62407e1f46a5567dd029b4943320961647c8b0e11ba9cc7b7e02c912ad6e25161d516067bfba196baa50248ef85738b4a5ac5bfa027fde72cfbad965de69851efec5d4c9b4cb0b0e0dc516568c1a81ac45b242ed447da3b93d7a856615a05644d11f128d7bdde63d1f1ff129496539d2fdb2fd221202502c65c160b125c069d06731b00a4283f11cdb0a47adecd6bcffd8172d7cc6d5cbce3eee087810ecb7b0bc214c87b33ffb79a657da042350a2013531ac883c0af5c3b2a930089168eda3190ba6d75456d8a0edbf4ef5ef1b3c33e422b7b7807db61f5fcf3d51d26fc735e6a1ad8a20998a87b459375f7a1ee9a5c1667be66bbbae80a58a51422c2d6a66487e65c51e8740e23b4eae042bb84677a615051ee98699e13f0a0a9abca64d58409d460ee1b15236ce3b956dc0f730ade30815e90765ff4bc42aff7c02529435e7b335fbdb330418a2d0f031f0b3554b921d6cb552c5281d1ca3a881b7e3ec68a56b142914c4d643419b105836513744d7f78bf809e96586f2eefaa0c3a923229d8e3c3904389ff61d320e3ced1f146e4b687fa29a320595816c74bd5981bb1b2797720420596cf9abd67d907361ef6a6d67f3e0212a149755a7e8dcd964f960fcf4c2b43d26d232ea3ec2e1478d19715d0e2793170aa9cd7a7cb39a76dd8ed340cbd3da7922ab6054795aad3d5513e18691a591c35284213d29bc7bac78e71d7b5d4b1effc096f4115d325e46900ef80c3a30f30d71100a2f5c3e2d3eed71667eb50fc2e3286880f2429012039b4f3f8acfb2d9f7e038a333569bc4af34b838a28972e6e0711800d68071eeec86cfc216f2fc794fe93b3ba4a6db8a7c5c57461ca7f69d3289c35ee3beaff5f8a59c2b2c383682bc33063f21a6de970fff9ce56299a207e623221f6cba341f17427105cd3c5746ad2cad29ea96b62a4c90edd0b8dd91ace4be17f4ed00fa024183e42aa3e2c083538ab77abaed1abe266777dfd352517c64958dff2194cc201ebc5542caa3a24b23b91b0334708866f5c110ff8905aa2e5aa55d2247146ab3c14951e25005a314de8f1a928aa92a97ad64c1c65d4038c88c69a04b70ea55a1817dbef7fc2be0c26ad5d023a90e36d34acd4ea1d1990b9689ebee672f973417c709291d95e901a57a664ed5753620f89d0d5a00632325163a51c0ec70ef504750a33066cc5803464221df3e8f62a5e519faeb34882ffd16d192433133d787a970adad5a3c9e39a98a3d2a66fd1009f28ffc4db49ddc524be875225ae84649218c2ccb97697401", "6300526a6a63ac52ac", 0, -358475696, 1537743641, "af4e3bcd8eccbd4a101021782f9e1f1613a39f005294ebfccdd279dab7df2930"], + ["030000807082c40302c69fb5cf9bdfe5b50afb453b3e765fc194232ebf31ced7c0515960802b76e56301000000095165526aac0065006a23e7cba906cdd9346d9ace4cd264e4550b4865a2d2a69a0b40518666b9b34a6ef27da04603000000076a006a6a535252ffffffff02d775be0500000000036353514068c70000000000065251516aac51000000007092ab2300", "", 1, 1739178220, 0, "99102056daf1a690451520a238d7cc9ad24431c6dc7eaa5739e53cf951b6625a"], + ["030000807082c403049718a1173d63149aa99c0feac271784fb4aaecbf3535be5ad06683271570eea3020000000151ffffffff0e2cf5ad5168cccb4964d1ff9fa59bf90a4e645a669357d90ae3124d3e7e4fd00200000008acac5365650051ac7f31b137122c8739d01ad8a34200cf7258a8f48d91c9d105393d8b71f3b13328a99b9c410200000004635252ac01ae21cd30d8094d8527cefa6193944dcc9e10c35d01cbadd4de8026ecc82306070c6d23010000000563656a656a1385f6d903a8af460200000000056500006353d60efc03000000000151db4ca10500000000056352000063000000008fdd4ad200", "6300ac51", 0, -83611747, 0, "46f80b51efc033f88a236307b497a4ea28ffa20ece936e0d3c223120e91e556d"], + ["f8de3657026e42afad213d84fa90a041bf6c9194afa72100e5b80b71d0c4e3bf17ce8841f103000000090063ac0052516a51ac68d2557d57230a46336781b13de1f425dc609ac17667dc34b245958a8c0e33b1056e38710200000003655200ffffffff0499f1b50500000000002d8b2903000000000084442305000000000165749aaa050000000002526300000000030b37fd02000000000000000000000000d91d56afcf6d9270295a25421eda2126af36bd6c19ce6a7b245a517e63a5d9b02a5b21b41c9d0595fb35eaf7e3df3d3424d396fa9b1d85506f5a218ed0c8129716748c6cc33313dcb1aac89332d738dc70044b620279f15704431988c86c765b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005fcbf1d91f34bbd9019c2023095ad191ec63e2ebc81d835ee410961a2eaf03035dde33e173078c287d1e8a04831c7da3b1bcd3be7ca860571088223af156ffdc8cf60e70c441b38edc59b852131ff5a95105fafe85ec084796f530d2dbcb92b3cdaf6adc85579999105c4e94b34326b819541bd7d4af021a0ec0611af63d3f55031c8819971be6de083b6ade4575ef5a9884734c080d0d7c8388ee02c946172cbe030855f5ddda17d5b00da6a10bb6e9269817da7eac91087faae964db69d3fc34480b044baa84d80f842afca858a79131242e09b6f4868935e58f7135ebaa42562981e9562104d292e4ee6d10a383bf96bfccd62a32c24afc33be2af9aa48a08d4292031cd70f85c8f73effb9527a5549e4adf200ee5c7aebbb2d5f34f09ece75219bbf02163d13003a96ff52c53222b5fd64fc7e52c2108bc995a23f539ca857626f25c5030deafbc2b2ec0b456d12b1b2a61c6fde90758ca7e25735335d50980f827c9fcc0327f1304bdc5bf458473e3bc0b983c98b762668a36edffd7fe12623e4cf597785030b20429295522587a518c69a3886788d0dc80005cf099f8ff774d7c3b9ad5dece12011794cae31c1496bac9926e95b7e90a4ec7b88ae75970465e427881baa8286e24122f0a440afbe51b72fffc342a05b461062f980b7853040e7846a47e8f3eb8bd753a1319779964c338239c2a26fceb38938ed3266013363c5dfb737110e02fbae76da4574b313d91a8663a00db5988446305fdca3cb634e49b02d45252be73e3ce04746576dc97ba846a99110ad2628ff0cf16dd174d8caae7163baee9e8df2d4ab86a94127d4a6c944f8f1dba2856d844e6e0b1172fe54f448cdadbe4c6bf5249885add180f77f4098ebe154c77faf3f776ff031e7af463376dccd3d2ed2deeb68fae79ee3bceaf4cf9bccc0834b460d8bc7b147a3ea97852d17680c1b34710246078682ba4ba9e7b9bfb7c7ea21d5341eb3f9cafbfa8c14d11dbb9df932735875d415f954c4cff523191e4d961280c1e8304c06e45164d38d460b70c1c7aed1ba72b4a33a37c6e5e2742901add3bf6678376af3ba692bf8fb90441fc3ecb76a5e1d380bec99e419cfaafc035efe704d97d5147677cbab82f9cb5f989c244bff9445b0879bbcb4a03539a3d76095c924cbade2d9e0553ba696d3d3921d81e658914eb89abddfa39263a23e4dad42290ead4d75090943eb3aca8c1c5e0c08ebccd44dda2143cd39bb92192d2f1e70f6f945ddc3301d3f09251c283af7b09a165830011226bd4f326ad03648353cde0a9f5228baa29c2b0df0bd7413bc07e2bbd739eab54a45ad81901a9f231deee4332f6f0d4d46e5433f8e709ec6fe7b4130a051c804e1a6ad7e3b534dda024c7fd9dd758362025050575efa64f6c925c33979a04f9b2a8acab7351984de4a1df837df738026f2237da5ec59cf74b490bd3c5ef10507e5484a0b2fcc97dd58a1f9af32d9b0c4d6682cf3cd332326d1f8e2b4dd7fae145f2659cdc7bc8c5e19aaaf718a34e15830c7fd3a268a1445a90be34f9b3d686836a8f9067fb832ec54875908ee6657725a024f2add9e9abb2b62b75d42bcc38678ef064547a5029ae28207b043673185f4d62a8e5e9c23d471ef3d6f950cf7332f8aba15a0b02da2cc28d3350c07c3fced466de5b86be612c2662b0e1305a2dd3bd5ad933bebc4d248cb9c2af3c20fbb1b729b28e8742ac627e764edc22bd29b26ec594b88c5b66e2f46cc3857abbd7c1b66d3183890df1f5e0598f99cb9098781b52e53141338df619a305f039b9fd7eb361e01246ae38f39247ed6d49db677b9365b2d2473d0c232d1192f22d645423bd24acce12b266782f05010eb5f7a096da96f72542228a2126655d4f0e7b856089ad934286dd27d4b316cabc38685170c64cffbb5c643fbd5f4ca48d4e27534deeef326cc26f56a13bc04327139dc84f3ceeb1b0a1f503edaa5aaa08e3e3f5f75e10070d1b979d8f3f6f5882dca0161ca5e83c5eea93e1b9cf8b59bca8502fab4f92b36cf9847fe43a672a137ad228ade40127f0cfb1eea60bb6846bb2232a8d757c605939711b04107f631c940eeba68fc36e3c682284e26cb35fa5dfb5aae1c07cb8663cc176b994c755272dd42b1a533438d9287422cbf0d3258902e3286553bb0c38c6b1e0c3c8c9719730cc545d10f430864b85d8f007c138b5c873c84023ac52542696f7472e7a7a35a353710b70a1ceb9434accbff98b393b38cf835b0f779f924acfdded8129de7fe8e26c49503629f0f759ef449bef3fbd66aab01000000000000000000000000e203b135204122f249806eadb8229864b926b7e3e8babda2eeb5327d8f00e3122822c90cf262a2b76e83c312fd2953d85e19ae282fcdb6f89098b63205410ab097e94873624fc2db7a4d161afd4e86f25a896584cf2fd15021ec9c6a260e363f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002163b670a099e1b2138bfa34d5248e4c859169fb9a43f54cfe8320b258d79a20c9b6c10bae386823feacea371dbe00df5a8cad332e3d36ade9aeb2bbb217acc6255fc2921315544b5f2c3c2e8663d22beeb0d4bb430c1110be9f3f002e71220ce62a1b6df7cf4f999449d64aa95ddd92a1bbca39d306616f44bdd394e29eb9c40210046610b0c48a29e5baa3330fb0b9da9f8848f87e3e90ad48bc78b4b83a2d530318d5170ac5e9ba4c430debc6155a78ab00805fd53faef7816b2999034aaba66d0a00fa099019f2dfc10329e3a82b76b5ec745b07ef9a145b54425f99e94c7a042c079eadd47fce96c707678c065b53599ac58eaf16710eee51d17eee2a6c7a4726031026d14029baeff01f20458dee16a86c04da823d81eca3fb39a1fbab49af78ac0315687ceb31913975290a696a834bd13810365cf6281ccbcd3e310665369ee6d70212e3565c6188e5d5be641462364f3293c95b6f6b78ea189342e6e03f8e74cb1103103372340eef50623d525661b5ea723532fbecf027b8dba0f4660df61f167d910317785a27bd5e6db8cb0dcb04429859f68757d97cbc5d7ccccfed80b2e9f0619fcf69938d339c7f3e1ed53d889efb5e79d3c4a7e0a9167a187097485969e12d8b328424e71f4aca62ea2bc04b53a71a60ea59ae2538de4b22ef7c1107f01d40c08fbaac75b1cc023f3f8538a72624aaca7f44b5ec9c7186497d2c3b818695265f3ec5db59dfd9bffed2964e5d805f92ebdd42c2d8f552a42eefa63848a6167c24be738679c5c1f2055a6e4acc5dd51d8b85d19d14a6102a80f4f54305ae9976bd2a46dfe9490257e726d20b9970e260739f018b330c05fc39c4c4c85e0ec306ed914d1e324f16386407b340fb6e25a76f037c49b7bcf4745855545cfd02e0f2ddd56427e8890308050040ac505017a534aba0af6711b2d25e0781f260a9d6ea1bb50f7da0eccda959a609ffb40002047ba8e584fa4e47857928825ab9bac89c1216b6beefa527243cd3b735b69cc2481fceed9cb43e772656cd5459c37dd68eb355385ee455d4c34ebe09d322c0bd6564c63eee5431438ea10eb188164ab47a21bd3a8491a813a44c91bc43c86b366983e5ea7eb544ade8b169e932246fbd4d3b8ec162ea5709454b27625521f5100ff4110473e8fc60fa9210e6f3c54ac579c95c82aafc446396eac4dd7f9fdc95ca224477be270984d3ab15806777c56d483d840c883d1a2ee61bec47f340464dbc8c4ae571d1b268b1dabd17421617d8d980f258e8b375428cb12c6ef4e864556c3a7c7823d454ba4cdfb138b51cde14cef00330ca07a64ece912ead94e3c9e3e15a93e4f90010ad8ad12c335bd66e1d0fd86dfaa65bcbf3c1591505b777e250e9543ffa9288f3366d0287573adcca5815ee1f636a5b0fa448261914729cbb55f0b45772cae652e691fff024d3a37d898f8ae1c5a6b1fca1b27664eec1586b40c60cdc80b7c4b10fd626857116e4e533e56bc0b4af891ee3d8c6827c985cc446bed92aff80e9ad9b56e002cf14be6074c3944e7d59394895f09b01c7d995e17feb5fe05834d76b07036f06e8f773ff07b1ae227500c50a1ef8458966db0eb10b1eb69b4d83d97eeb970579ba6778dfb0081cda6b389e1cea841996239ed00326a8869dd1899fd5aac56c7fbb48ccfc8858916c5cb4b6854ac1733ab2a3b973bab630502c23b0ab6472b6d2394203f330507d32f3f82bd66197ea0c3f3f675578f59fa6739e9654a34647222659a75cdc282fb2c869bddf50a35e6796c478f7ee87855379a374d0c9febb8d6fb15880100664fcdd6412bfad2908690711db4c8e3dd317d2da4837b48cd31ede39087749b0427fbb6a1b6ad5a36b9b01f06fefadb01c7580d17a42ca87f561d17eab8845a4a90e2d85aed9e6055124a2f348486b418ce3541c073c89b40c140f51f06ef27ffc2966d2620652115b068fc4c0bb41167ef698a5f8da720e0f661a284c1365db7535f46899d74551138a9b8b6424920134e18a401d7d69f739ce898405c10e43b6c84d4565580695e6af3e2d2262fc09f2c870b3bdad621a79f6b4c6b62539bb5efe0f59454d6061c37bc282183b962642306ba5df1a6d50f1c9118fab286538b199c137b38e247de4db11979dcf6e7126255a4b5cfa2c1ee08eb70a3f6a18da80f52be530d91291dd2d22efa6f2c7a6cd5cc1e250e0fde674039c9a2bf88bcfa1b5b2888612246bd96e813a8ea6977a14fc16df88a554b9c66ae0fa6763b8e6084ee4a59f7bf102e6098b7c2569919264692a0000000000000000125f4101000000004de3e5963a70786b3cc58f9d7b1441b1b24d33b36c6fd533f4246533692b251bcb448b446ddb6f4c3e2da4f127b3fcf559781f94d413a308092219adafc917c7bbd0c88b55cd64c679940ff3dd165c70e486185c93947822f12ef6d8ee1ba85d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f333e9622be256b57756a1596f397cd3bc67774202e3b98bb098b541fc6c7486e74c2f98f5e7e13819d4afb5d8cd5d813090dfcbb51a15fb649aa6d125115ddf5d64e56200b9407f2ce8f7e18f05ca5c9257ca1e7384b8b43392789cb63c2a849578ab4d3188ab5626f08638e806dc16e8cf5b3687ca6de491e6ded40b2dba8e031136a0370ff786764e49f6e4a1ef8e40b0dc24e98deee74761ac86fede14096b0223b1f62e78e852141b5dbf8ab6fd2f6e9ed06bdb104d049343229fb4b2a01b7e0b02742954c9902266f954e2802354811a7d1b9bef7f8873957f1bb2b7d42d1118e17607b1bbdfa4763afd637e82a00dcc102cf2196c2a21f947d5b00309a23db00200ca295a0264939306c7f5b7aa62ae864d6811f19ba4330ee99ddecea221e4ef020d625efcedab0d938072222d8b28e48e8bb42629f8079cd635e3866fe9caf1bb03075cdfa3fd1e20c2c7ab23cece80d0ec743af405162bc149dc3e8a483c0fe19d031add98eada81849727435faa1326688536432ec7095a7c913dddba16f63e84f903167e79123cabcdd16f21cf4be6e5e47a67f0be0ab58c1f76bfde7868931364285ee6919c2c7c1dcdc91ec0e78f204e2df8cf09ae1418438b731a1cdbe0bc4176a31531598f16abbbba12ec2b6b6f92f5ed772b72d3a26835b0d84f4e941920635151cd88c24044c00e777ec756e24ee2e5bc5d3559bb9b9cd3280ba2d04e7655b36add1f277f108c799e201be0c1949fa424d52526b200d65a591eab969f1a4f66f90082f37a0bf8f93ba53d43e1958ac70a908e531a0d975592df8bda03a336908629b465eab149c229e357493867a887e0b3214cf3516eb1979f407ae29fc2ca7c27f6d8e5d39fa0d49752f5c475945b44818913ab2b44644d0714fb7845530de27c0fca8e46d95c950a1138c8a2f1f3e1d344919f4640397104f9cd7f73c445fc8ffbfbb93f1d24b4faf3286ad7d972230d725ae1bc36e450ae634d1d660951e3e198b3c9f19e11692a69741887eda6c0415eb25c750fb29473a1aeb2b8f704051e1a9540c9d46d0f80d7832228cf08346b083e0dcd5931785d13442c8cd9f3cbca61bd7289331de6caf4101103943a77cddd5983acbd6ad8231df51a37e8e0d3113116516db8ccf5d9626c96305da6740bcadebd463b8f03ca1fb61f43a514e7453872384e35aba599155d68e51c60aa7d9eb11679b38033dfea2b65c853ace5e9cc0b5507abc9ae59273dabb0dccedea8be539ca5b3eb57970ed921c541711268d170ab345318ee9cb5c4ba2c2df207fab367e7e5afe6bd982fc34b5f415b4b9eeb9190c666ef2f2cf72c30a56c1113979ed499cc32ad60b8a843f85649eac23dc7e4582d18a529303f532470ad833731f2fc958d1bf9b31244270f84d058f358fc2aa77a93211b66b347419178908ba411b313fe3984559713c51a1a618cff134c6d3334775bd5a20f0f1bdd63f31574dc5325c50db0ec61d1a8d6f76930d01709729d10171699096a926447b41b8b31669407cfb679a6bda47854eded6759853f39fbe9fe8704cf4c803862a6c475198d9f49512c7e3134e0a1880752754d59019b79406c42baf50ddda07a403148646cef526f14fc26adc195e0e2772bfddf4dd4a751fbc4bfe4eeb7017343e0ef1fbceefc5f5d9bb171dbb8220a38309c4b977b68e1231b9aca9dde6709c87416ae2bd93f1a9ee5c33bbc85c5106818b54179fe67f6609c915728a511da634ce0a113dc84aeec5690effc08beaef8cf8500eda3967fac400b27a33830fabde35a12f1c43f441cccbef245a7a33cceca9874bcc5e0cff2ca5a4595fe0b5aac80b7f10dfac7cfe048a4f04359bb376ab8b44621490f6aa15c4773177e54f5c8a79ada2fe1b882ce4359ef0744f332aabe68de8a0cd706931744504c64b4dc5c6e6e62149a42d06bdc941a3d72b5f86f8dd23034efee81cabd253b2e2ad70f080d1a01f13a89a58f7a349ccdcaedcfa0738f3847b6513adf42c36eabeab92ee9059f12bd4f261a18c221cc0cb0e79822bc60fa80f0e0f03f683762247a69b7e0d8839634fa9046730d38d3adcac7353ed5214daa5e8f28461bacbb507535afbcbdfb4d948ca9e11c6e3e71ff2cc319dda1ae96579223b4c22d9924713e0f860b1980ae9084708f7500d5c28dff2a862e4ac083dc0722125513addc136bb593b9396303ad92d83d0fe2da462d82af37cf97e28e4254f0806d77abeae2b921f96299ec19f99b5f8624f5b5a55a9ba27633b4cfcb06147d96c075172c8e324e517caeda87ebb9f036a74dec00abbb111d965599e929c3c24c0b1400e31300eb6695d0a428c08a251fd260eff0db11ac1bbc7b6f6cc882e8da0da701925e9130f3a533813886230a943179b33c10b07b4e28534c6da774e8a653e30aefe5f79ef935930f", "63", 0, -1652976456, 1537743641, "471fa18ffb2ffa9378ad8d29fce897b96506ffd1f680ee46c3ee49ce71c2aa43"], + ["030000807082c403033a1c1e29e320a88bab4ed7d1cd852628f37c5aa31a71b38c5fb0c6516cc1526e0200000000aa0734030595c8332c3ed0f1d999a7cfddaddef59b16ffae8443cfcf4b56b3f654149e6e0100000005536a6a6a53fffffffff5f9e0695f70e3a42c8db46cf682efc08b80d93ee6d216b9544b61eb6e98e136010000000153ffffffff036259350300000000009b634d000000000007ac5163006a006370645104000000000551515252ac3b869186a1303eae02417a8f04000000000000000000000000487d8d0a803cc27b3f5c8842c164d57aaa4d3eb9ef64cc618ffd26e0a5ca58ebab166b05f9a4e4c4d9bdf075d8b2c8bd5e5429ee847e112e6d1ec5e920616e65201384aef6fe018cae3965ab7b8a5a53022ca31f217249a9f87bb4f9aac57e4b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c33764136aa5f90a85ed50a61169931ebdf0bc527b8c6c1983357051b4e3711ed2759bd0e5655abf8bcfdcaf1c1343f5d263d1ec16c525c70247f91d0ff924de034b35319d08af1fa21384eb6de0396000aed27d3e11830aba1e349852df3f4ae0fb73cc4f47d3fbe84af3115cf90b88b6142c93edbd680973cd5bc0f290c72c032bac399a918ceeb2e7023b91aa85595c7a2b2d34acc942025776051e5c04dfe00304ab444e23bffd8e8cd64715500e848dabf98597910cb8acfdd9a0405775876e0a000680f158694ce9f700374e50c4c6cdef7fe5701c8db1f3fc78a7c83664e5cac7cdf900879f443b624f4e3ff960a89e22c29e2d0cf7e479f2aed6616f8039890203184835c515fef98115c9926d6ee547f904101ce137bbc04ca3fb20aeb75998030050c85b06984623daefa2288897fe10a2deabf1f2e63c07c73d429d0c7ef8ab0323e36774ae58b72886b56828da872a3d5916109c63961a80b088138a528710ec03272035c33b229e6a9fc7611492390b5c7bb6fb10df426499a83c45637b4d2ef202218233ab37ecdfeb4de394e151481e9052fe49fa3e52888e849ead02e14c1dd9e004bd1d4d62cb8515438a387010aca469d349bb994b1f3c651c51140b265d5bbebf49e30a3bd34ebac0db57509909b824a177c8ec04ac52cd85d127c26ffba82587a19f5f250e511b5093ec5a9de171c037149459d74a4424de260bc1b3ccf4465a4f40446290dba88a922e25a7ab838d28c3c49e6bbb8b80f479853e8617fd12a7622d6214886fde03205d4df8517c9af8c7de7b126d73ac65cc0775e3a9741880be8fb6943097907c3ca4ffab4784972ac52afc6734d0699ff652c13621130296ad7af4baaf97e693e2720f65912a704889e5ceb34432027c5a6065e8be9cb96846df07db10a318c3443e69fb784e02f22c1b498b094c03bc9b950604826860407e54fc805f78783e4666aaec10abf3340e740657d47e848d5893bda9a78e0ae461ea789d0f4a56d884b8b7e8cf4c7458fede3d9e7feb5844762ae0b738ea67909d2314fd5d9063874ee0fd2991c51f640580bdc6ed1c57b29333755022ca493d65819c44e5ccc970a64ffdc8da208082e90f76ea7e6fbec4edd101e4f139245d5f20510d50789900db9be15193aea10dcc8a51db249066a0f1fa10b82e42538b960d5213193c0afe3ee4050fe2f3fb382410b929648aa6581d81e2570eeee088f186e1baa69ad2c296d3adbfdcd295dcc94ad3f52f9441f7bcc173dd4f0074cbb2715dc5ea25f2def03dc1a9c133ed49df4669255a3a104020b0834c473a393bdf9c3a95ce1442ccadbefdfc49db0c44c711c7e1b4f59cb7d1c1703f5a42364ed242b7ef73b144dbf160374970360a922e7dabf891b211ad222a489e33ee648fd02432da9b24b66541b452f4fd32cdb25cac70745838c3bead95f4ef7d43dd3451f5c10fa9b1e3cddef4d289ebbc1232add8624c97e8acc3a87a5c15cf23aaed474ce97cc07fda8d88f99b6353d1158afba9b36ac3cea470787d6d8ae39a21914336ccadf958494f28dd92253fa7c42780d90faaad846adbbbb9c96f872f404edc2954f437d134051c492d2ca61250365a1ec16c279c3b90dc5e4cd47d16ce1e65f16eca07b0998d780dc5dbce80992e7178da120358eb272baa57120c9ed53aac3688ebb1dca7e8e594a427a0e582bbf8506fefcb162cbd537d095ea1c80c678db194df0756c535049c64154602032241e7297bf52988e264b747bc7dffa04c36be348c350ccd3e5877214283b703052517cc22178d2f52c5d99a83801a2698244b284fd8797ce19363c666dac1bd091cc276dfad7f775e7b8da636751eaa043d18b4f9a7adfee66bf5a53ea9acd217e1ef3c1f259edde206ad52e4fe1e83bb1a2643fc196188277fae3c3b813b1938c6abcb3b0655720c3bf7f2ee2bf171aafb0ceb105e0f3b434f77ad911977a819fd62f31a6f867f2d76c53d9dfabb1b546a8fd53eec6c5b71f1642ce38a8a780c495985961a09541483dd00a81277aa665b7b1531c3e7868718db491b95f5d923c3219bdfcf4b1b32bf64aa70d4d99f5e30530c694b1a484583d54a4658592ee86f3944cb1e75e24aa1ebffa11484930f01b2b2962dbf421804152199a82d566d6355079298f3ddb31d42247869d28dfd028eeae6d3eaab2aa9fc97a81976e8aa7f505e1c87a7638be6e3a83541342e6bd16d7c9823fc38ad13e25432ec47eddf3c1d4452baf622aca7539868b35efde93a7c829f9168f78e3466637d5f9ac2700000000000000000f8f90405000000001e88bbc7dcf0cc85e4084bafc5fe8096cdbf7b84450cb8e9d35861710cd150fe0087d1ecd7ecf67d45abd3592f27f7203332db44c3173e3f909aedf2df7c4351e2c5fcebaaea31fffc26a303d29b5ec198ffd29947bee9a6163aedff5b34a29600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000515c2c402d39cc553bb5746114352bbe0a3134d8b99565e3f086499d76756c8770ac9f0c756dec7bebb93e7501ab7f768a8c40dddae982f4f62114802a1f914b01f79912a3e7093a40594dbae53e72a2f687e89e5a281b909e2a04ad2b4d4e897086805c81cb1d4d95c6489049802c8f8d115dc87d5726a1ebc94baa96ae6f2f022c132c0f497ac9221ad1e99909dcad87d113882084fc0e4297a57fa4542d6836022884af159dd432917ce207ba5ad824dd2155292fd5aa31943aceb97d444986220b0061b37731e01cd7e97e279415cecc09861adb9025d4c665bbb6f4a181e0828411bfc13c495b42179f4b22893aba9eb8d3d94d0e1a86dd8e512d7b2ca47fdf8f0323a26690204856571cef35e02c683f9155be306e60d8abde09a3c65cfb81623403182ce631d0b813458503365e008bf2b68840eb471b181ac7da21572622a840cc030f90525c2451a5af084a11a54b477f787adebd0bf5c0226a2be834eb0b55501802113fae9de14a283f1a6d5529a49238da9fe29d087c51af1cb18e5649e237648c0328634c56b57fdf34b13b52d8f9fcfd9535824ddb1ccb3309f895c573a73873941b66451f35944be5b370f2d01c5981b1f64c5a2ea88981dfa79ad2d3633233f098569157202976ac197b49bdd366501d7fbc947bd231ee9784888d74fc0937cf8597a228b16033270439a272e9b6a596c614e7194382c5ba23db01544d4232f1baa538d7b506775752668d17ae1fcaa34052b4a5b6184fa66c01ccf6b94bd09a3bfe7d71038c9c0c9df2b4fffaeb779b7e7c54ebcb5b3ce0c66d4637c29d66e9790862c0215800c587354c88065b42fe8b56fca7ae3fbe0396fb64b3d8780188a5a6fbccb26106deb6fbd132cf1a8801f74b01d8fff9fe8aed315ddc7defa64fc7f5ce9f1c42b096ec96c4de8cfd3f560436f66006de09ffc7f8176b0f4fa102b3a0a128e93c3e0581e58c5019a0e9eb02cb06d5a3aad36ac68ed5da1196f5e0902cbec753fafdb51ea88affa7e02229285c3d41c30f0b727c19c28b84dd50327e76b810d78b46c8337ef3c96dce57091a87ac7549fb5de223acb07d0e347baee519c28d66e77a88f34c4afac82be83cec1d5b6df646829775a824dc8894a92c9854d702d467c408a2e1ca59b34ab49395408e70f40b01b0e493ec9b8e8ef1355845ac9f08a0842992aa269fadb35251bb9066dcc67006bafbb4028cf600b3f6e084bb67aac98e42f4fc631f33b08c40b42afa15101aec69eaadf02c0e8c78df8baeddde1f8058a04798cdd938efc0d5f887bc48dc7902445b16bcfdbbb4fe3d9771f9ce31accf2244fc6908602da416bb10c0a7c9a2f5de00911e58a0fef771935cfdfbc5a814833d771455a52b78f2c8bccaa0ec57ebe74145beefa6b0fe32c40c9c0386843043fa49ab9b40fc56421c4684631d682d3cd301eeffd05dcc23fc8764cb277ccec21df4e86b015df35fe83a9a3555f44368fb8da3ff2e2cf8d1f400fd05dce69cc7ffcd7e76a66a50c3c366bf10406cd49c30d995c152ad4f268f1cad090bfe08b5ed55086c44414dfe957f039a0120deb659e8b83caeb0378b062677d89e8c6a0b03d61b13757d906fd674e8448c901cd4a0db1c5731b29c11712032dfb80ee62ebf5a990d4097053f2b9d1ec47f94b697426c155b2ab6fde1123f40285a605f3222862e6e6d963cf461c151e447c057a6d26443e7efe23510dd82fae63ea72d966093db9d95f055a47bec75aa177e1a0b415339764b834146c17411b6a3d3705592a935e619a692f7254fe703ef174f980a5909df90ab6fdc253e9d1a7eb9d528fce80c189c816727e3e24b3670801c1bfd9784108408e0c8581e61bfecd2e2aab0d66b5c26edf202bb68eb23dd1c34b3b47c464eadaebd85f261fa25f1e547174368e6998e4471cc6eb6054e222cf33b8fc255a144cb067f7b52d4dc09a19c440273b4aa60c9dba34e70ee3234319a2d8d6612c864076b44a11a42aa9f96f4ad8ed930167da8d047827b4053d3f91973aff04eb9743fba4ef3c8094f6479f4f84a9179a643a7a3e761b24c58ddda50324406c289df67081ef6710165be7ad6dcf9e11b1545ec449892a2c9a5df96a2472f6d64cedc98701201e7ce941b313720b6f9b814ac0e6b1fbf2b23196b63f4fb8d241daf9eac48a531978e245fe4930fac7e665f50982cb23337bbf5e8d37e1bc0fb42c761ae69bca5266b3a3d860ab7ee2dfab3d318095f9ddfbdf8bd17bb5493f80f8764fedb95fefa985ede1e7d402a9208c03986817463e666e5c7aea9dc29f90869aa5c56140895e0dc418bc3cfe6fd1aa194db7e599602bdc0b4c2094151029e97b1e248c88f78e46d074ff196aaef599a6eeaecbae363200fc9efe8ee0cc0b06c7b2cb77ec33b33676e0e5ed589c58010492ebab79f04", "00", 2, 1911798050, 1537743641, "7557868315272bbfe5b895285d771b1e12c60923d27b1bed7a4c90e9615d3521"], + ["a68aa32501c0bf5d6bfb4b106bd041cc10eae4306c93c1daacd5040b3bc0dad3bde00aa36801000000086a530063516aac5231342fe00291c164010000000004005165ac0e4d510500000000025153000000000117929301000000000000000000000000341b2ecb21c14f42cd14ba351ee1bb79f1cfdd4c346dc04216c5739da4706b1d90d9298fdd948ac5b7477653e1253a019405616eb4d4b16d62f6b7d8865c74764eed2f8f85fcfe672a019fcfcd9f9294fe4e519e79532cfec1ae66cdc6b7e73600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000efa71e6dfb73903970c00cf3044c098d1449e39b92b5acd6e27cec406a6c3474dc9a99ae18891d8b224e7c6946d9e4d169e7a1f2cb2ce322a3547ad9c0d021144ba8ad4be65b199a84e68b92ad2f214cda8edcb37bbaefb8493da6b16dd9ec3bc3e16e38fbe8179d7591906b17e0b66449c65e18793783c41f0078412d8ef160032e01cbe82116b72fedac1fc98a06c44f812043c7b8834960b3e19f88953f959f02284532a6f62cfc55b362b133559475b87f1254fc4c3dba5de3ef3391310689a90a07162f699fbcf169d1d1964c868ce887feec9408eae609cbdcecffc781330e1474a4fef37280f56ceeacf25db6ce7ac4101cd6d5a07d7d491bc6d0dbec3751090228a46efb69346e6ff4c332cf3cb38518648951a14561c4e05f978fa46cb2f5860315551aab847623fd4007046a16a434278582fa3b7c1b3ba2e0b8b05b211bef090309fd3106f84941b6830bfb5d0a4f761fbe30ff4b2171329f941975d8269fb015032ce5429a3d1dcefc81feba75f8fdd95e1b8814fadb733145bd9e7acbda2073aa030afb45d4887ea4c377833e39b2b6b55db909c2b381ecf1078a26001208d76678f335ea3b14ad99b42935f43f5ce5b23af525f597249f8f345cbd643a6c9a4ccc5f4b48f635765b6e9f74d0c2434bf25a6e1bff0f21a74344878fc610d5d5aabf4472565a117925e61dc6e2ae5b3e397576201a203a3b6001094c80adbbafe93bd00d0029e8f445cdb51f1e156e8586326bf5cfaca59bcdb6a8bce04d0d118236f78d5628daec0479220e6cb1ad8795bb102a2a6571c1ea034dca7a388d16a401b1c3ba6992ae14fee57f7387621fbfb59cec628d325324ee37a6218bc037efdfb2140d70d92dd18dafcf126892921c4558b336228bfe28b7a11acee3cc1b971d16e80af5620b59fb49582e982a453b86e788183ac0a4d72e19b82c386a42f7ec988ebb15d1555c86bc880c25b17a32b4b7c1a643220602056703068843f7bc8b561649853494e19bca7bc65250b294404233c0217e45afe4b7d32ba38a057df6502f5ffd2a63666ead64bc090d8d1f93e3df41a3760e5c8ae17948d810cdab3ceaa19b9624560751e323d648632d1e5268159de5ec015b73a450ce343651bc9529959f7f6c5593dbba2e59369550d808151ba18400db53dc658c73faa5ba3feb2da8b770db9598850a40e76c77c655a3ccdfbf149edcdc00924e2e87ab4992924ed9dbde5f3e3d533b3993b14d2b94924f84ceee52e355247c4b7502cf12b8bff64e9c2741b0913bcf748a06135a461c4a24c8cdbb67c496b99101e834c9154938afb82dd6e35e58cc8e3b74f09ec14f5458c5602072bb73fa8a50d1b4f364f9d1a831ea68f726685278a7bb265204c28614f86e25cf17e61ed1a856e8aa6476b67b89c175ac7c43f4d680c7ad89cad1ad83efa2d702a629f8389e7992af8b4cc9235314ff911f32cfd3d1d5a069f01e7052a8e66e99343d5774ff5c98ea887451bccf4ceec26ea97cb57675480504c4a7cdf7771f306a5edb2de25143b07e1daf0b88bc599a3632826b39cb714f0e8ceeed9fd99b1171a7524b904f795d7cc1cbcb5482ccb4cc171d559e28c101ace3cccbea0c21aa0122b356db1c183caeb17e3ab3b696609edf3f16e7da3dc33b5864cd8c1e91e0b969bdd5c0a9c2c79f0b2fcbe89121af8d4226232b2a9ebe043f15b8b646459586a8feb8ae51097fe8b1a69c2f9b496b7103da7aff98889fdfb2a811cab77aaeedf010c8cca09b0daf0faefea96ea1fac0d28319924336fabac38c8d59f77d633690f4f9c1096a2bfde8ef087b8f9bf525c8a9bc615093394ad0b6cee4c700de5f9fa218b7e81b96c6870770a81bf5a720f805d32512c096f67394ce4ede900293fbc2f24db7cf6651c6a613e8c3c7554126643c907c59b6e51a3bfc42ad008332e57ec005ecb7bd297eb008c26fb4b335a743a46b32f402a6c028d79bbb2c22b4fccb88cf1d526d044821f8f5e3909f48a8d1c63dc7d8e4fb412c671d7382df5136fe4bdf34638df6ed215b37f746204ef05aaf38230e0c00fd4549d48d284483d25de25c9c759edaed7900c5d6696b293c7b715fc75112f84e8d7b9f517b87fdff9e5c2e3deb6b22d2b2fe8cd398e44c8f262155c528a807840c1f5ed96804faa53d2e2227cd2286471e72759cc7862e274a364b4e2cc79e1a1bb269f5d5c4a1347c7559d511280b058a75d18f5110d8cf7db68c80b75f83d540bc050adc1ef9c903a795fcf0c125d83a209f37d8521b140952978aa11d8df3d5ef205cadd3ad6f0a8b23cfee7a407a87c70d67bb52b4d4e07caf93bf97da77716f268f66a87aeaeb3c1da2db44654959d2be72cfa38a629f2f81b7a66ed7a23d004ad7c2564b0ae4dcb646fafb072d8f263e4089d5fdc7a6e70b927d285fe65909", "63", 0, 1410329287, 1537743641, "66eee6a7bf6dd65e8941f840537fa1e203c53f148d150724cec273c6f96f0e52"], + ["030000807082c40302635927d1259c4430639078341f620b9a723e8a76b9b04be66e58ab3d35793e0f02000000096a6a65005200525353593b86a340a794b79016edabdf6167c5361c5ef2e10ef723489fec0163e020c857e4c3a9010000000151ffffffff04dfdb590200000000045251ac521e11bc0300000000025351fee387020000000006ac00656aacac0b7757030000000004516a526a000000000000000001c4435605000000000000000000000000f5d3b729e3c5d92c30b22f4222025d5db5741b6e6ed106b7d595c0c450b156c0a5c84c36a3f352299eb1d16c2d1fe7c216982aac67f768353ea3bade4b91b91537b0d5fc1e302cfa85a59f9588b9e79ca3c2b795db765574c96f4cde2b391ced00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000db7465e8ca59957d7cbf64a0a2474ada1e12dc4737ca1554e551c6ab1486fda818d12c4a0a255f4f1406d088eba105e27c040dfd6026a668c81f969551838ea3d7d57258bb604801573c20b13f3ecd2e302ee0b2180ea5c0d66343c15d8acd456e2c906911ddb21c43a1f1ff3ed8e41472445c510df8b2b9505e83146fad114e03180852dde921e49d2a6382e7281419b664fb866c6799b48f977496cd8ded6c5f0319e2438afbfeaae214283c4b4148c383d86bdf1eeafc624c53e59b4557e3c33b0a0290822299817f70e998605c83b87ef45bc4eb6f960d41f7b177a1d29e748925f30cb1ed38e1bf6d525c604a688ed03fb65b0aae474d6e2ff151bea7b73c284c030104d33a85e92d57337eb3aa811dcc526058374bd9ddfb822e8c57b014fb8f8a032fecbaac50eed6a7c0f9d4df4d3951ef85dbc9f7f37bde89d56edbdbbaa67bc7021eacc608b039f8f99760f8e7a4b5b3662a687f0a13000a64fcbdb3a15fdbd93e031065acb2686137cb9bb4bc82ae9dd6afbcee69bc5be25dab597d4b66935c1a3e022f36349940e87b4de6af61eeb8a28f5ef32ce297194d79a05a6f8ca11cc4716ef206005786b82be3cf5635c439da911952c67efc1528b3e97775660bd3fb012250055f36d8ac6bb4a20b6ce1d3d49db5d3394265d3ef7fa66114bfd7f869de60849ffe6d5879d1bd33616a1f2e3eb2773a3cb52d3e215dfb5c89454ff9e9ebcee469fea047ec84ece279934b53c99bc69927a80d1bc2f00b7002fc391b4cc5069ccca46eeb4507314b6de82960bcfe0350a3127073eb10e2251052222d156a2f5013465052a7ec5d1b766257b703551a35f127b12f045b45f96c91f12f658baf92dc514364c9715a071d6aa5ef98aa006aa2b432fd4b63e001ca784c6633eb6455df0e0a017c60d53954ceaf609534b498cc21dd05b74cd3bd5a51c1e14705a7b11bee8405dd0fd93a29f8d54aedd629bca42b33442e438fb184f0b89c76940fb8c57fa7310daf3299f3378a5236e5c1611d93a9545272c479e69ca37943bb39bdfc15995ccaa21ef11fc772c3b841cd6d53d7b5ef3dd1ff3949c8a7f4d5427db9a58446f18b7f27eda2bc661806f9c742506f549056b5f46d3514b94c78795096ab7ea1bc7f362d42b4721ae6901fbb4237f75cd50d0a25effc1782f40759ec4557eb13af7e61fc543e5739dc384ad8542908859b19bdd8282807635c20e3cde3d3cb9a807aa82dfa6f1b7541e3d5ff9c31e019cb047b7642d3d92f69a702ba449e87cfdca76c8a3ecaba9dc563927f96f7257457043cd24abe39a3578bb064f2bb17ed25cfcfd4b394610c23284de21a70e901088e22230347dac0c69dc65d4250bb80b46699e17cdea435118895a0169e854fe4aceb66985bd3fddc76d4758138e0bd190568ce35ab96da84fec244dcc51ad648c64ce2d3b0312869e12b25ea2cd76c92da8d47d6bfba517ed9eb623344774e82f3c2db14f392d460d2c6787a58f6e9156a658540da86fbd08782306742b5545f1c96609081546bb02531d6aa5b06e1430b4b7eb98bebf85700344f11604060dc4912bb28d43b6ca1c6af71d76c4585558636a883487617411ed3b0c31c803d433e079e3c743cd4ffe64e0683ff9a18a2026fd1d555e569bf313919d2f6b5b89d1651d16d34fa1d4f98aa5e1d57522bc23e6af0534f63657b77723fd63570866a9cab44fa034ff52a0cea8ecda51e64e1bfa775526b44d93d132b10f5ff88267fe8f0da9a56c3900ae70f65eabab2669a77f53fdd4f8c1b67ecfe5dc7a46bb436882eb868f4bbd56a7cca3b146837a675dbcf6f38e9419a2a7fdb020ef3cbad5ee245e0c7e5581ea72ed12ded067c13c00af7b2336865ca92361ef0c4c924d6c9e306eeac09e0c12c4fff582661eab8c24ccf571480dbcb15892bce4a230283b9a05e1321e807c5bdf374f9de7cd0ed2ae94619d1f6e0d40bf685f0f64d37c4504ca683fe94cc8ffda355532349b3a9ff6a9bad189842a7f53662fd2bf776f172ff8db8e7fb17b0ca59bb93b2ccb0c7c0e6217463fcf6d241a23d529a49ac991187474cccc87719dec54586818b1e373e2bc2ff33c990ac6a0d629a83bf5fa51226f513dfb2e16b2b014e3998dd0f54f2c02d069c6aa8cdf19bd3758ca3830fc02e501d7461e2991fe09225b137f275ff975ae5bdd239cb07bd2db6ca68beb1e4b26bd7aea57c417d488ac9355af12b1187e18490951d350b97800f99b85376d88d22fd81b71d7ff811037bc6f5ee2ab497df5abd4b9e69f8242d6a1bb3d91fede585507a8ac399a83678a9f20af894ffb04a146262b2908596151cc51192ee71ffac6be04a7274f6cff0c74735931d5ff4c5865e0279ab05b7c4631de46c00ba147f61561dae651f7e4813a2950ccfa206e6e070e4f0e76da590c1b808", "00", 0, 1761313659, 1537743641, "18c911f9af55f4ab9bc9494bbeb9136e97f0cf3bf237277cc241d19cfb666a79"], + ["d5406b4901da41cf539403460aec01553c086a02e92519fa871c9e75eaa441467cf4ba3d4b0300000003520053ffffffff03202e2204000000000463516a51554cd200000000000665006552acac4b23490300000000016a000000000300000000000000000118dc04000000001a32c546ea7af7ed28083bafebf53160bd4a28e86be79a0dbcdbbe3102b02b5047ac8d2f4dc874ad35c748e94195ee773b833300e79dd808670494a1cf21fd128f7b81550cfe82fe8b14a96038a9e5e7e8263f51b194c036e5708afba291208e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002630b1f98fe73909be3a210ed69ab293f5f19a85fa692d3690b5b02ca24ad20846e13c1d1702736116c92fd456082a9ddb13fb2f2e9555a84101e037c1ddfaebf02acf28c385c2a7b30d4bfdc44ebc988aea04abacebd63aa5dde619301b858ac56cb3004fcda0fb527078c5c50144545913ddc34feaf3dc62d46d127d58b7c1032ae964af60d7f872a91d75bc2b714edaaf29a54b9693ad25885f55dfb34dc00c02201a9eaaa589077df5a6a592a08985c237a7929ce57854bd33d4f928704295280a0283254cacdcdcd13aef8dfa1d741f0d73b7e5d467fd14e1fed39216cf24c7df995bf266fae9322f162b413a0bcfb846701d84f8f83c859878c52a75a61eb0090216cc6f2ecfab4e353baa7344db1cf92778cd251240b0dcef5b7d062977e2643f020cbdf54e28d249e196ba2f35ca410d7547dcc5cb12991667a69681c52f7f793003117bc5adbb6a1ae95ee98dfd0fc964de9be577390026d3e5c353d4b4333e1837031ad0e665774c813f601ed6807e09db19020a5687c0c485db3247f1abd59ea9a1021b98aed4872d0c7c4b14cd5c26ddcf0628216f82a757347fbb03c47b9831d9d21891bbee9002d6b869e9fc211067a762df9db1907ebb3654a155f73b3ac09608eb3a3f819a2caab0b351cbc66bebf62ba607327f28e7425443b272d9056953180a83acf43cde159ae975b9c6ca1c2316fa178674ddfad24c66f08f87cc5de613fd251c2d01e7eaf7065f06c10c44b041433949f6f58652e356fff109ed125d3fc34558eeb4b9e7d16f544652a62329147ca8f2f8b1e5ab3b1ef33ef238c85b9e40b2fcc1b8c65981544272aafca6ab5b454f43ae9848babed7bcf5acfecce3969c87200755211f67242803d1265fd79c2432739c36dadb0a3aeb38a5e9fe65a367eabe833c8c53edf9b6b1ed5c5d48d5b46ec3a6a49985561e01372b84668f53e7a72f0c6aa791b7ed0008d04ba0e34fdd4bb7238919b0388186d449ab53ea11982c54f6a4648b32e609fd8b39bf974c639053ac035f0d69570757d59519cb4a9cc39df7c4a2fffba3b66e443a911642b1c156c3ca04412e27907a47609300d9f92316472a888e51691342b565011093f63bfd107f030368aa062973738c48c2473d0fa7c885aad4548193a8794a54a6e0d1fd437a393657a19d10dd436a56bbdc2083664e73afbfd5311cfab807c8c99ebf8b487754d5ed1d9f0cb883610784d020e4800cc1892b75c867a14b970872f268757730c3073ca73e11bb0a23822e8ced725f21d932f3633ef6f0c839f6c53f865f26a31f279d7a3be5a6cb22829493b88b1401f2bae033b24440caf58e18b4cca03024bb0d12049db7cf7016cbeb14980440156c65f8502e222caf4770904caea4d79abc5d42ce075a19b22397cb080118b27a7b7a2f2f39badd6132421cc95db38f113f70aabd03f984fdb583e6d7f3d301c5300d3e87b207e621081af69c4298f2adfee2177f8fe141ca5a270614eb95f72f747d6ce3f0c5737b20668fdb1fecaf8e7e69817106195bb80bc47fc29cef7aba783cf921716ad68201758372c533cdc9c7072e5572af6013f0ba445c13e4778eafc44d9fe149daa770831d9e282dc94c8c0be8648d5b4b921f6d24ff78a281585fc5fee57ae07cf5de6e390e686ab82e6a7fbc1933456809d3bc7660cbf491dea7c0f1ec76dc9a0e5294bd33af8d68a2d01808f7772bca3fcd9e57b1ca7c56f6bddf318e638026958e498b23fcb148d0db4f53ad86fa49dbfe2d6eb9d62c57675b21d65a3c346b820bf71759962eb5d2039067e0a019619fddbcfbdd88168d3637afaf6f102fcf496ca14681f22885a12aa1a331f66add75fb992f35239583eef7add1bbf1b540e6bbfd4d463bf7b3ab1423d690e01cb0b451204e33d7b12e3e496418337a4f66220ecd0c1763158d69088477ea59d617a4be920dd127685ae4e5eb822ff12c561975d4f17e7d861a65b847b70bd9c249ca5d0ca956cb7313fc9f3a32858a947868fe89a56641657d0a9e67ec1ebe1109f1b260c2c9c183279adfb97c477feea88de23e3b50d5ce49acb9599b917a75c6e94afa96aa4cebda9b4a10b7640dd19b5e3113ab480f33ae30f5b4d3ac0097bd15f8436c7ac42faac4021f4deaca3739fc90942abb319861da0e20ec7163a12ec05f8c8bdf35d1fb253cf428b2d2ca56c955b4a740a0aa7f37cb60166edd138a7bbeb038101471a21077ef3fa26d67039c7caf4b7deede53950e9506bea9e8188f94ebaa9a9ba85c8846c79908df91eddae11960adacb7147f020000000000000000000000003ee7b8d9fbca4e23176b5d6d71e2867a1da7552860f1ee11e77840f6704d224a04fcbb505d2bb9c249ae08b3e665e2f5a688e4b09bc4f105c7222636c5543f11e02513df439dd3d915640e529e972b582ca98a873e6daf06061931a5b1caf37500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d431dee32a6ce46085a87d338b2066cbed93d5a5f952ada82c5226d2a5afec47b0fd09bbd7148443613b55785561517a1cb15581aca2f455860349ddd7eae8fec710b4a9ab812dfb10b9911f0637369e2f7faf60732e0e8c38a0209af8a6437a6a895022366d53d84b9707dc7035834077d44f94b3666b9ce70ef417cbab55cc021aa841e2ebca89a1b6a1607c8a65d86bd2161eb550cdf07103ab8dff2a4eeaca0225f1ddc9fe9e2046d021fa4c51ee4d6eb816de8c2fb84b452adf738f302e0fc50b02c2c9ee9f658430bab196a63f38a174ccbf0b30e28a51ff02ab4a8fc5e40d352bda61106118d7c8ce01323db544fa55b9e4023cb5f9371eacfabf8b1760bc0d022b21accb5a8491b0f608d54a717ff05741294e4ed3f0dfe097b152c7fd58e6b0020d5aef03e4ec45bccd9166bb12c92683d26d729e5c94dec38f657ece7619d558030c715e346be63f696aa00982c66510d5b0970bcd911cb2ba06561782eafd4735030138378fb267b2f9d18b33592ea3a24cec62d5b58ed9ce18aa89bf974c84c414021abd38e6caf8d1050c6844c3b1ee60f3b27144a5e406bf44455f88037cfd4da43294bfb0e09e0e4b441688642d70fe3bbac53ee77b9b53ef6097c2f7ece50eecd3c0a598fb067540c2aade8b48879892b29ebbd89259b8b94c577d0b3750b0e2d07ceb9a3f96b0742867d7aa8cae4c6ca868b69433fd085b34cf5d7527738a7a02d27509c4dd4a72f98350aec863f5ad52754f8e0a8c6b1ff6c7a65fe9980397ebf27994d70ae9ece2ab3d717cf9f598e2e724ac340547286ce78b7732a3d9070a22112f5c96e095479d536bdb09b0039f6609a177416c612660ba858cb7000452664c6fc8ca2e3b900740da3b52a72c43213f5708c05dea084b6823222ec42a4d132bdbb604ea7ac5350d6b524a9d10bb6094b9d4d4bef8c6a98317bb14f7678315f21a8e030a90fa721a23fc9c73908dda934b92ccdcd2accb398fd360bc66fb125075170d38e54432ec9b01444224e5075c4bc8faa8d637e51ac8dfc6732e3ca0a20ee98039dc171a8d91fac968e795658b1423295426ede1fb21d4ba8357036aa5f73a380553fdc4793e4da04e5b5365e6359c4aef50ca22335651541a19f2038d2518c780cd18ef004881792ab3d2fca3d4c79a7e769afb83033f906f182083046b2b27c1e5f6b599f3b1975935b6fe35756b49c7a4a3258430a4ae89041af62ca838a22e1e6b0a19f75529c41672f6c5a5e06d051a8c944b583a7af61cebaa7f1c68427ec9dae4331d5d4a6be70d06b065353358c6894bd85a432c90e9e59c136b036ece01e4088b7b75b41e2e27a3d895e6204addbacd471e53312bf0bbe3b3b4bdf56a4bf390aca23e69980aa905f01d7ae74af54e39cabd15648e602674511fa3e771441664c48a0419478f57e0f5b0c0f5713dc260b3340f83873ba5a0d2d6d695b910c69f049b2a82e1ea2ab2fcc1b5b7e120c01693629bac77864d3560f5a62163e0ac539944b74890a2d60857739a60e251ea23a70e6d79b45d7eef26782f4e03b69222d13d0641d83daec9fc6b64a0151be8f0168ce24fbd10089e0621f7c6698f05c7e992da0a9729079034be8d63d75a3f041aca3e9e2ff27dd7cdb9d1fac6eadd8bd12750cff31c566567d9f62554de13fa6a97958b03ffebc7011b31184d7b1df1e32e9aad0ebea43541ddaddac340e0ee6c8fb7e3ee3995de1c4e3bdad373039f45a2b1f0b2ceef1e5f00431073771edaa6a7040549c1af19f86c70c3b89bdbac1aeef9efb7b942ccd072e1fad3161764f99e4ec763be04b9b9cf76ffbf679e1755cce3679a3f72d60f513de64e565bc68bb3a0c4f5875fb118060cb0e4925bd810e79184e9b7004eff9d6cbc7f8b6499b55c0a34108492fa42022c8373d5745d88ef0e343101d0aadfdd33096c491aaf5f8cae84aca4ba587101e1478b2aa79663ac9077a6ec0826b925b2f5e2e53c51a377aa4024d2284ce00e166c44306feb160cdbcfd61d5656828a6be2fee8888c3ef95e2efcdd554dabb4c3230f3d6272eda33a7db943ed94554ca7dd65a5d0a0c7a21834fcb6749d151be2f2d372ba617a056f6444789539c9069260a83111146fc0aff3cfec648007b378c019aa5fe170b97b6df5f3ff84595986574ff88efdf7b14220c54fa8eec161e644cce744c9a9d3d9913445de21c1f8bf0eb96a9168dff0d9f0da3d8711a76415af4b76b4edf6f151f1e995d0f98dec2d0b6f4529d6922f43cdbcd9b573b5bf45abc6b5ad54f892925a44f21559f966a001000000000000000000000000350623a9218ab5969108188f01c7c4c2668e49bf9a86d80b1de519a7928fd8caadf7cf5c914bc8b598aed0c320f91bba52abc18e106ddfa18452580071d67045a58bb855b2c496bea31a9b09a4948c2017c1b13484dfc480f79e0fc01ae64e3f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000125d2a7663c369b7ebfb78bae9fcdbf89631d718a10dea16284ede564e7cff47a8de64df79d287bc450fef9bf098cf2d9e42332dc8d2154607de7f0bcac6e0b5e1625a2827c9ca196d296654e56f45050158f773d95d02221814236bd4960ed013106ee466226a988aa89a2e491677170810b453e28ad423567fb38c19595134032b610026f04b536e88b015f35a96e4921615a5c59762b8b81793d31fb200e5d50212da5c887ff81895574505c1b34d148eba7d6e0f1f16c95f440e4ebdd154a19b0b01500eecc46d26e621b5016d396b62e9e3d7375255105f614139117555b1063301b918ab6b63f62d1f06dc00fa610f56f9f2ae18ac94f83e228ea03faa70d2f6032af583e8b42d0dccf1871b4e393a9e766efa10cbf0f77555ffe912339c8c9644022ec78fa9d86aebafc8b6f9421c431c7aa50c67efc7e6f9cd4bffb1470bbf527203032a59a735b6723d17585f1efc159a5edc3c6717ef53a52035b872622c1fc6f8032750d205d1785354dfb2a4d017b076530548b555c754640235264fa795512d81022b0a1c8947e5352c2c47866413fdc2c6da9df68da8a560bbdfd91775d490fcdbb483dffbedfb5d28a4c789836f5172ced89190637e1bb7c8a6ff677f8e7a304d32d930a10e1d80a6df13bb4a9f65cd6c409943161f1b6461a174208356f418b78bb9ead7d549cd21ca3e7e413d9e73726173e24887dcfd69936ea8b3b891f5bc3f445bee037eb2adf9481e829641e7e4d8e188b006db9376d00f1df99ae470110f9a9918b4df05a77a1ddf085d3c66ce8414403ae2ff8633c750cc259430fa74a912ecc4396f58f02b3b7a0b1925038577f29ef923f299313044bd45f8bd53992c597e678cb274ce8d1eabeea3431039e299342ad5a990f9eac5f511a5fffb67488a66378e60075e64631cf6a08fa68f9f34b2a2ba84825dda32d6cd6a3fe92779aa7674b15555533b33b415559d9efca55be92b44d08b559b41cb9fc2a085e03bbf12daaadcb285603a1cac94169a846fb71cb2b40ea6c8d998bd9949e03aa19cf584edca475dc07d3a6a5db79173bd7c7d52d8b3415cdc61ddcf2e5728b11fc6e294c04bc16e2c72ccb689140374a7fec69436db9da997b6830fda8d43bc5023bc323b7ae2819c5fb4f4bacfed9b7b758ca134280889ad3beee7bfa651b1a8dd3dc9cd34b4b73eaf03a9478ced9b348d2b99dd1e8ce1d7fa1538cbad9aa8c61de487b46386661b310074f080dd107bed9b4c677b7bef303e964811531b9289c77954f4bd07ecf3d309e2571667b3816a940cdbe1878a1901dde939bd8a8c0e6f1fb252a9230235bcbd91589349b376de24f3809fdab11123aeddf3b2ecf9859da3d7cc5db59f4fa68f6be59a3546b7a8fb90d249a9853a9e67c59e4465da2b3e3883cd9a951b6a244258ef5a1205327299106f7996c1d268d8d5916ceff33f5b8aee2b3b41953554df4267396e5c91c68ea9900d8e2c701be05e2434c8c66d20ece95e03713834c2a1d6d2223bbeaca0ab926403ab2c3336786d973d3e18a115be2c2de321be7d2a4b293459e6ac336ca3304cfcd11e53e401912fb747c52e145bf053e055e2e0be50860ff6f5c37e052d1100ec05d6cbaa111dd528838e3b50af86ae5c195f2c997c8cfa00a46e6eb8ee1c0097b44002ce6e309a10038ca29d24badfd94ada15433b447ce8c5ff11110e08fadb156329cf61a1470fd920ecafd65dccc2646431c9c12cd6c3be755deb669d66eb55ea5beac19b74ee9bce51203a84f630632316d2feb52875a04cc975c5db5a969cc64cbae9e621a89f44d004397736169a0b9fafe5dc72057dd0ea69ba4ded9e1bc209b970fb43c2f1741aedfcf0bf21f0ea7d249744c6d974374080ba1b4c9095815c453821edaf4b77ba75e37dcbb939cb1988b581c49f433185b5231ebc7c1ac83b5a768cb06106d84d8a8e99ecca4296676d3823123746c6021d81a9077246fa566aa488345f7b2c3c382dca435354178292fd6b2e2aa0faaa5d7373ab41f338bd8f18a8cfcbb7bd108a339099e1b592deae075c18e4d4619eecaec3d16f53a6e6668b9e54a5c3e071ceea3273fe8f6f55c82a2bcf275a420cf2b8530dd69358ea32059deb451a8b3a2b617954e77dc05de3099ed2d7d11eb2ce757f19307885abd9ead2b6174723b788437074aaddec0f5ec1e342dda78678addb1c193d4608553e19d2e366ba6bab6f314a70cda22d450e025faf09a0933b772091a9f0c7f756dd6c86153398423029946f9eb1703f91636d6cf85cab0a55b105ac449cec0cc23214ac87844122296b44012dcd96550276f39939b139b24b3301dd99e4d855be3e6e903ae61109248df6a7790ea27620f06755b3cfbbc0feb54e9e3afe16f7d011126c07e11ab88ffb73d2f68197d8aacb477b1f8e625a588a07", "5251635363", 0, 2118051687, 0, "a30ba7de7b669a3ec76575d3b1ffc2f21c9468a64ee1750e3fcaef2ebde44f84"], + ["030000807082c40301207ccfdc44517eb59f67da923ce209ec570f7e4ef08b7bc9ddcd53cd394a2f3d01000000086a6a65acac6352acc8ee4383028847a203000000000463636aacbcb618020000000000000000000000000000", "acac", 0, 242773124, 1537743641, "bb20450698879e86147a37155bda8c90fba5438d9964f0db306b156e9f06baca"], + ["030000807082c4030380c561b076f927c99b0d5d8614e0ef3303a5b45d6a099b56b52ff7b0b1a0239d02000000055100636a650235873eb9742246322efb7470315de96e2e2e8c63b00bfb0b1690452ed8016cac81247c020000000651ac51656552ffffffff7497451167af1c35b68c17fe1a7ccfd663e077c8076b9424670b10fe9b6da19e0300000003ac636affffffff045ad0dc00000000000265523680ac000000000004536a635318cb000400000000060000ac006a5342b22f020000000006535363520053481ee18d0000000002c0e2ee030000000000000000000000001d2a4acf7e5eb93cfbd9dca60578269365b4eb99d1330fcc1aad169ca8764404539418a20c1a6e1ebcb9cea85dff8a538308ec6f4a6ba5e2317d884b74aa07b0400a14b5b73aa9f7ffa04560474cf8d0f17fcfea5f48a33a45ec68642ed99e9100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bb88505215b67e07d4d1f35c025db507632c7d8e0be22229701d0584953d2b540e170fa1646e1ed4ac81a384b15cc609be1e0be7b76d82e5f6e74b87d8ebb37b4975b4bcf5937a623517117224d6c0c801def1d121a1b9dd070ea1ae1d54548e747343c8805c3d959429949df1051ce4b8c1ad880cbce2b169ca8a3b729d0ed8032c67d89271015b7663b2f5f389f57d507257667a07454cd99c59be591f1666a5022871bb6e5987929ee58fd88cfd5d1cee99d823d91eaddc5f62354ab18230c7a90a03929e5d1002ce8308cda70c5cebab14b9494c759465417dd989697a6c5fb3a3d19a55fbdafee55ccddb8578b4f41a746a7de9b6b8862cc000c0c6db608145280227ea04edab0350863dbe8c1fc7f8018b34ca93cd30c7dab3ebb0a954cfc55c0c031e2c75bd8028799f7d7b98d09c871cecc06946889c4a1f2f4776f0d503d9a1c8032b62d51beb9143e239df4b7191a89c26a2500a90ad75119df7de98b74af891d203195988b8349335f4a9927dc05659d7971bd4f5bba5dad294cf926e10c255796b03096352a2f4ded1959fc4ec83c461e59513deda8f1b0e87ba22bf64093330e7df8947abeeb5a769cf256c23c07e7aa99217238a6b35ec6ad56943fb32d5fe2ed998c2c8dc89f54d170e97d915d7e2547988bb97b52aee47f627778c432d978e1cdb2a2bf0115f970bd7a67d0300b291a96e50b8bbb42fd9ab368cea3c04e0124ff874900ca66f88823570f5ebeda9f728b50c7c6765942ef78e229d25f462ba299d2830918c45922a817def26f1f3c67268c98058aff72f8714a2978c720d2386ed1386e7c74cd02c009e1079d5f97e23c3ad76d1b279ddec9de44e6c672e99101114db9d2d7862e3f5cd55d3af7fa9ed5cf2edfedfe74798d4922142ea1a60e0d6a8557d5fa458f57cdd65d6b108a1e7bf628602020c9ce9bc0a345b98ed5fc7c63247a057b8d705d1be8290beebc10fef63462a5d0b08d7c46d3367db3ed78e81c38b3951dc8fc9d61c8b6948e5db8af9ad9c4fb4388926fcdcb0e0d5f87ee8b3a2330b1e812a9ba8515635efc7764079cdaba1e3b61708cdde61b99ce8df7c7ddd89d743e4e8aa1c2530b5835aca6bb07f98412437ae53803d0a6bdeef7d4616020c180a5062dae4c3e026de49a409c414295c380f5d7de6d233c66864075352a95752267281e7f7c570fdc3e67e86e281ccf4dc316123210493ea75e726e11e327630f9dc62b47367c7fc5a0b5b0601bd70dea6fff61fe49535704ba2debc1671ff3ffff9fa9369b24fd12db38d39123ef6576cd246dd23d587377d0693a65b31eb6f53b95b909b32546d45a7f4971ba44d91c6298297c1ac953d8fc336ba7241b9249219139793cc49f79dbc579b5624d4c44cd9ef4d96a6c62553ef191c5257732741974ea3d02b34196a13a28e6b27cc851f959cfddda304e15b0a428c721078f484821bfd86a30ff5eac1af4e597b099720e3aa1d328876ec23519643798cf600c6d1e0e93f945743bcbc63158d339e27118726f592f3b72e39fbb1e3d49584949ef795a323945255ed1cb69fb66914724f66a51b3cd9012a6a6df436316fdc4cccf415eb64769cadaaf54d905b58a069f40300a9301d9b5c4e9e14af0632e0f6c00f49c70ecb92ca2b87b1cffeb91d572083180c699166dc5003efc4684dd01705e175140b4e69d6873b0aefcee732268474f59e04192218ad24a45830cca8f7d2260a03ce51a274426229f8fab454a10998a5ead3a0be583ca8f2b49401c9c2df4378997dbf1eab94e25d4d0c31984cca5e56edee363b1a0a29be08de240747d6a2ce83da9a31fd1731bd9950f050e6f46b93ec23d8dc8c7e5559b616f78104f2ac941c8a29cba2d9f299fa90603294ab7165920f85fd3ba398ca1410e517991a19878b4af4c11736fe6e2b3d960074428a0fd422ec47d230217f6c090a7f5fe84f742225168bacc2dd3688bc70f596f745068264dd040daeabc01e9ab7fcbdb552be5784c8ac86d56eb6d3510f838028aa06ecfefa23c729968a3514b10718a4ea4be26c01a534d7f0e67c0d96949e4fe1aca698de351cda0a3d707475cbba7266cb6b28e159171ec99f2bbf86a5ccd2e882a727340df93aa4f84a2169f15a4c7bbc49d87c571b4029a6fa404c1e7f0048b4c0eddac99ac7068192a559b6700fb121b1faf0f06d4a48f90f6e4b900d2322f3653bb9db7385956c6df9597728ce8f210c05229371cb229e4c0ec61aa14f92b74802a052220e7c023146748f2831851a8f292118ab2301af0482e100000000000000004e213603000000007964de940611fc75642454204ab1c75e2af9846c80991ab6da07fecffaf3155d88e08c096dce73bbe19363b3ebadbc4c3f8adeb3590bac20f173f7eff772d19abb8b1df8bb33fe56042b5a4fc15281801d07a47696fbfda06e4895e68df11ead00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7a44af55481a0cff19e57f2282739552fed296521b4ccc340e822c19f6502d2971f9bd1e16857d6c304cf331903caf080760b166579d4bbc48246436bd8d5352aa5c340a45726b79b3cd11043b3d53d09390244999b782289e3d7dcfe6a6967d1d7cde3e6023a520dfe7fe6e256f6fac5c614cdccbfe98ebfcb755c8161db1602025c586fd36f435c2bfd144bfc7051a92d7df5e9b792fa2c1e98424fb07e872c021b09e6531c65bf5d8c0d25740cb6b051ee3c3ca3d04521229cb0dcc0e034ab520b0897deebe790616976dec61d445469b619906ce7effcfac6fc0ff13dca07d49e541a22be0f07655ff642d99694acbbea7702f1c30f6c969bbd876c20b086cd290311f858ef12d626fead052013094323c1ea2cecd821dd4e907e1aa314586986e802297e027527ddcdb5c044cb61bc5cf9e4dd10c2b2e2984620316c3a04f5f782d00328c1ee4503541da4a568a93dfbe4721a838b14e7710b2e6c145d1a7782f4d12c032a50ef88f3a2c293ca7bc7af4b2e8043f7805ba57de3b9d03db8d38a919ee69802282874db021c1b7444a2da96c6cee38b029b9f02699cf23c9666f5f692b4ee1010d55524ef379c7a65530c748d4bc505ff4fda693a574e788227dd326ae4f99af7d10430d60ac6738f79349517bb69fb1469fa681e7f438188a8fd9b6f2587bfc644afbe74145c36801b503b14c0d1d7cf636aa09bd38f9857ecf3460e3179151a2a5aa2470b740246e2d712857bde6c83cc32d80543a178b06474ae0369a7ea0a852547ee9d145e373ee4fd21b7dfa6e50632954687c04cfdac4efd91045bc0d344a857a9ecd5d6898673d6dc97d3b0cb40d2fdf20497d9f6bbc31ff5a6b85e1a09684e6818de533fbb9037015abd3cc713b3b0ffcad00a0795d530f1c413ee635d94ddd7a184721c2827aab3fb70631bc0f670cf48d1f1393a902df4d2708b004d465f9ada6401c836261deb8df4384e08738fac81493ed6513430427038bbbe309b36c3d41c30447dd59796e0b45066fc60936602346699c9bdae4d30ebfbcfd9f8c594c41bb9d6a001e7955f52f4b6750b73fd6652e756edd594a75efa253d63ffa8ec68505e224a5c8d04adb7403b8dae0439c40ea75af7cc2e66ffaec6aeea064993b0a9f7ffe984084c8e7092ef9b9f06e97ab07ac3f4a77b86fa41372eb1f78a67a27c87f97db75597eca6a6901b2ae9374f9ab49b608c3aec94072e99e7ca34268b9adfb345f98914de2d0762b67918fd5a6dc3d79b464c12c144fcaa63c9d5d4515a825af36124c669e3a5e9b5d350544e3232d9ac6cd523e87285c702ab50fc37ccc64e5654e2d2efac9315e3d051c34f66d3667481ac800c09b475d06e8ed4bb644645c58ba5706732fd0c94d59926af4be0f68be2d401f4d6e094cd7af29c5a3581608874733883e3fd96a788c6ad7adc8d6f071ffcbce1c1ccf81ccd820f0557da956d87aba693b6c5ba37dea9a779463b20fc54392c96b2541f122e518a40005e848f155f95780a1904aa9b917774bbb96ca090cb8603109aba1603282a8696bda4d3296fc51ae8fe9fa4a027d4bf9bc5d359320b4780c72e4a656cf23d6ff47530529f127afee6cc67753ef7a62baefcd37fec6322dd5d22ff945f7a9cbbd38e1eec5c1adaf42d07026e484b2d9cd49cd911bb83d3005440af7b9e1a1c87734dfac29e1538fe3e9aee377dbff8485aef1fc7c88cbbb7da821c5c3cc2b535b3c558bbe81ea19cf27fc0076c9130ead5fd8106ee5be00365c96ff8b881b4538ae0258c21cbe4eb084b3e7b9739e1a2894e0a3884bab1eedd5d3fb954da6588c54a59c4a2f92ade0c6d8a64c1d4077fb93ddbf16de30795603c77b0fc608627ab0d0cd477f251936feefbc8d9aa62fbe7c65858fff109d69f4f910fbf97b9f242a23f57295e78928b8664c2c23cac0f976345f5c834fbe60be705a1fff7aba4193c62fd7023d6bc4bc5f036c8a0be7614b044d9b5a767b5a07e3aad88cf833a6466986705efa6ef890b2fbb79d4896a5abd29a0b70637ac5fcc6050c5953044018b623de9b00c55dd36bce8f5d178c4e54be4096d721862ff4dd4678e624dc36e6be5fae491d6ec1f77da47eb6b50f3c327314a6a55395e7949cf7631ac45b2cb9836e4c540d134a9f3ed6a0179e2e7318458ca863447a2514665d649af84727e2a4aa9367a6ad34068d176aa11636e0025aa9390891dec9a4a31b2eedc8ab99a9d468f3a50295986cc41e32d879c21468a275465b1cc8093f5a8b1af2c157c61420b8132a8ede9fdcb7b9e70b8377872ad1b625da24495988975786dacd7e4b6385df89b7d071f4537ec31da746dc2ab3038c76c39a07142a90743cb49ccb79fbba82df05e263d98eb737d1bbf623ac0f3e035e7f94cdbe4cc33afbf92b51120895cad521ff298a900c703", "5351", 1, -845608365, 1537743641, "c9931c176ef77b9ad2c709533b4c2b187a65bb6e6a1592d9378d3e7c53f6463f"], + ["030000807082c403043ffbf61c7f046d55a0ef44ff294f71499f14403b530a9ca0a622ee49759cdd3e0100000004005252652fdb2f7a891207a2f79ef243c5cd312208737d541a81e548d97198940ea3c9debb289f3c020000000600656a5253528c381d1c0c0a74a992dcef5d915fabc5039e1a1d29d623b58f190f59ab9ce7d6cf68f2bc000000000663ac51636a5148bea6a47dd61e35fe5d873243681e9dadd5dc91ac78b53be71ce2a3bf403399fafc83000300000003636351ffffffff03eb70c70400000000056552536a5289f7cf050000000001527ccd79050000000003536365000000000000000002d299e102000000000000000000000000cc259c128e83ab2e624023d64f85be68c460c03a93f67efcd49ff83d114fc9a506f4ff4b851ab9093af8ca9c1202af3e13406eadb23d131ed6544d98a276ad1cfe929ad28aefe697923ba01e13dbf08e8fe334a70c4d176271a24f511512e284000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009ae11fbb6952f576a62b26b05736e58910fa7716c05325499afe3d62d35996630c95b7435886b0583c06b4c22aa135db1d913e334f15680e294d0d6e503ca52bcb8052e48b0b22a86040dd9e98475f314653422989204c312fdffd3aa0a7273b4bb37074b23a9b3a93c421fa3cf2d637e72d71d3a903a11c50edfbbe603b5129021052c166c55a106a45fc5353c839e4d395dc6ffd4e8ad865b711df0c18060393020345c3f5217598013de69ea581c91fed0a5392cc34064b38eac28268327590070b07a759714543e17bea895c06888e5a76cb7b5093066586cfe0f8f23f7573e4e6140658b6a9e844a72cb75da494c1b98ee4e842a9af529ea81365ab6eb8710b3e0211544fce9b3076d68417c172a2bac7b3af0faa11538a269bf6eff5aae0eb0253032ffb9b38b58b6bb23855d1057f337c7f32ec2ed3dd360c95fdf828900d0e240b030c01d3c91e2e8eacdbabc3a94b555eb7553ffb52b45b873ff61b0a12322f1a04030332e3fedba358b2022ef4618df63e862f8a0e2ca7e597afdda6e07bb07e833a03182793634d26044ac69de1be5a74e86984fb20f7a120429032d438cc853be5b0708b1ae79d852b40472fc72d13d70eb4d3658e601b1956fbd44afbcff773256ae61c812cc3b9e05ba2d25f5db0ef62bd019944da87909987b9924b3a5ca44a1412b30495d6bdd106ac63361293e1005953f75c5d094b9f907ce6a94535a9b5a895ae0f0768031f5cfca6b9d7837d05073f6b474912ce7584f9593f2874154d9e1454e4b654f9d2f565f5dd6fca63aeac9f904608294e55175ca2c75ac2859a42b1d84a29dde727325907a9faf8f69ca7970e19a9ac062611b0974cb8c291d184bad7e7ab981e19e4abf8598f6a19b36cc602102f951596ac39b34be0018b6cfb0a31fda38634e6ea31557b096662e0d7d170e843acd48db1503fcd6fcc64a8f48ae3a2bc7b509ea1d3cacb2e217b527c96af9421d5e3491497481144f196307b6fc2061d6364b641e8a09a878cb52b953736078883718751f06b2a8493e119f75e720230821dda70b19c9d8208cc298cf736ed5c98760064ca6b64eb2453901f8581169023a1ff396e8a6a049d36e0467505a3e2134f1d9507b8991ece65a7014c3482f83ad1214efcdf37d7040357bfe6c5883aa4ab7760e6dec827e54ef91471826cf78ae594a2da52c3049a6d1dfa53cf5106463f41ebd651449eb8e3df359ad87f9265b3009e6a354320108e4e86150678961be45388ee47bf0ad5ecee63a498fba1a1b0e9f2ca7c1cd5593d85a14576dec13df9f705b3b88158cfac56762ff82434dfc041e6f2920da766f54e6833daab56ea13880f534b418c13b97b62056f3ea94f1fb8cd902cfd6bc77cf1937e96da3d9890ef62a2879d42c606e7a092eb57712d27fa9d0b192226651049d2be1c1368a70acbcb61a41a1f93e0d6e9f644c1e27984ffb1f6f0b42e656d428a5adfef0146819d14abfde0ae89f03e9b649e170c462199e6898cb118d7e56620d1dfc3235ddb6c01f0008e6b875678f68d82913763e3090d4d92d92bcefb4d001aabab80e047677bee45332b7a3eaa68f1551e0ca022b95b059cef07b9d4d462b5767444d00c096d096f4b947ca2be60562483fcf5846bde864ffce009339815a4eef07310b0f9666e232fe2dc4d0fefd85a34ebed00bf51f6260b3984709c7ba1f8fefe5706deb87b846ad56095e7f86544ce4dc57682bdc7b8277352dfafdde27b277708e82d44b452a96e8dc60650ad71a1c59b5aa664a02d9affd8b99cb082d404c6637b45a13dc16b9800726ae2bb424517dd849d5cb49086c8c6c56149d2c0e5e2ef11279fd7cf030a67ba087240a9ecbfb3e375ac3df682e18db70e4783658a70d27775443b7ec15ec8e4c19a1ac7407251132b002578d6c1d37729935e2101a0522b5effe49cc96bde875b64c0b9bd16fefc91095e03569ead4f089799e930db503a37997b44a5087f446448b2cd9304293650ec78a301154d6d2bfd222136dcb3d9e2936cf1612a114550ca333d3baeb5680d3b9be5d119ed4912cb9f1e2b85eeaba646c076076865a705e1d10f5cb782fa3caa9085d8c6654d2bc731a4dbabf5822249972a32cb009b8419cd21930b17fb0a461e79e9e28992598f637bdc5c36d7836b40539587416d1b9bfa22b7bfb7060c2d2ae1d607e45f2a2bf3608273fd6946b16b9af3fc93a3c79237c540852bafe6d6f0ff07c88e3849d2c01d0c78208ae7eab46eacdbd1eabf55c4b51eb69d242a79db1ce12f4d6eb929f659ba5c03010000000000000000000000000c41c2a3e88890078613bedbab34b4c35d389d48828b8fbf962ac0329c4466bc1117b257b7835f46b8d74bbc6ce5a663036a22041a0e99abb84a6aaf213c5367b1a553f0f9fd07cfea62d43b02a96a8cb45baabf6fb8a175370e8f61c1923f0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e071edafc72b583b3e3c13ab412992df59b1cae1863b0120edf6a1d5c11260b8153dc790721463b6a6dbbf64a4f81ee3d42d3cf9748034f837f0d5de23a089420cde7795fe06fab96124f8ed81ed18c01391d1a793884989522b18fdae0e005015c6fc01e2a05f70c6a3f6668ca7f1f542ef653a6667b79bb2c3de09744b90a2032075387f02b409f43911695d6407c2fd02599ea3356f33db2a1452a218cc38410301652874e846c20b4c923b73bf589c5943bfd7ba2564069b9c16696f343ffa020a055fef7d2a387ffceae278820fef5a97a7fa8138349f03f87182a49a59899388f497629a1add9555f5e20cb8463f1b87efde100670283f13fdb6f74abbc93317022331917b5ac6b6983dbd6a881aefb11e596983f504ebc159d8e0ae2bf7ef5adf030a04b3f764a5cbbff3da1f52304074050104fb3d7a0ffc3d1024d346ad97d80602135d46593ab0ad13fb0bf9f30d910069a5bcf0863bc4b5213743f715ff1ee762022ca93f5e7a7b0a560f5592980b67599037be723503fb32220aa9ee32105f25050313c0ad39a6efd5da68b5eca945eee2430de1c753cc3020b1fdfecc652a32be3c2264dd9e78d4675ec7b80019e91e52c5022bd743f09cb086fb68a27d2f7b394d5c3bd600f0f6d90a91cff565f2b31d66141eadba69544bc04e1e841a9eeb7a4fc01683dc7cb230047f218d7e70db90718f0ea76ca35a483d295f84cea70c937436286711abbbc8178698f36b361c9ea988cae88e3492f456c66da62210355857e7bd78fe7bf74d2c3e575ff6c639975e2042bb6e24cb38a104528039c7ddf69ffa48839545a69f9e02737d6557b659f0949ed3a99b3a88f88f52b9e20e8ae7b1509aee44b189ce0d3866c3718a89f71ea6f43e19f8d06782ee4c05dddadaf82d6972e8b29b892c808aa374d016253bd3edf1a93740ea3aa9b955b9d072c3f5f53551988e31eb0a9275f4cc8b31b3cdff713fe01a3d6baf355a8970e945459ced5248ff1da2d0340ae6f694399fd6f358cba713e74ba5401e7820165bf67037f54a66e6768d0ce8dba2189421ea04dc9b1d1b2246d45b0e8465d73bff0777fa851bdcbef536e42dc644e3f141b0cf6201ccdd39681e4c3a1fca9ab6dbddd95b58cc4b04dcebdeaa342717ef677e422de92619a163cfaa8bb332e28ce4b746f58d4046d2b94023073c48c2bc7e5481c9a83bf68464200d6c1d3b8adc403a49183d35bff351461febde6f844cbbecfe225a56c864e6275c63f0b7b15e4b33aeb08c03302d465fb86ebb674789624210eecf606a24ae50a4d037c1a73860a048d7ffb352dd00f3b3c459c70fefe347413830a3e028b9e7d9eadbd89e26edda1c78a004f0df6fc56317933316e3343a77a02b2524bac21acd57a4fb731e20baab5ee3663460af57f626071ffcc12a66bdfe70b9004a490a80bd8c93308dfbc2fc1a2a48c05c470c21d8569ec0380a9cae043bbb3f2d6a43fd3b8c8695c35b003cd3f75ac25c62bef6574435c0180a47870a1b1823c8525942f5192221d9118b8b837fc7013bd099dd9cd91e258753cc2086a373b745537427b00115fbb51fcab09a2d97e46259a3deaf09447cf19f7c840386f40c559e82b57e8ddf217d92b6a27b7b083b635464f0f69b87643561b83082de8cfcb9f82d6f2da7d551bb01af03b1d3bd33bce0cc20de7229d9dfe327f3923b127baab2a6a8acb2a213442ef48f4042f16904457910b54e227100000d46738ac06694491edd0549dc7939a116f97d861b0b7b7190a2b5ed039bd253ed3921d93f242047584fccbad54c07c2005bfab6b85ee33841a2e4d840caec72450f3121b0814fc9930ab48a0714f86471e0b4fa1b18f9c4254551f515812d4d60ff9da90c5aad123df7b7aaa6d81268394bd15d37ed718131f7f68af2895227c64422e3807653f7368f060cf02904e33d2c1ec0c335bdb3874bc5fd8b56a0b63963a11de5a54a108ad2516030325a6a68e025314c37385feedd17b424d4f489281b0614a58f0ffd95a3dc656905db1b6aea9018151478ef75a04a44ce77b13af9a0640a07b27e33b38a4ce227cd82b6d31febcaa166711bb38d76ce81ca59e0012933cbe72c3bf477be0e36d783fa41d3614a94ae070cf9f2a009395f1dcbd2385706e0f9f49993eecc0596b52f70a76f2e9f412a1f929417e50a63197e9b7b43f72546eb5c47e040cd969b98dcd0c5b8aca0e38270185883390478bd3a9ffa5496691b6ea73c1367245cb0f3c946caabc5f3f519c17048829aaa10249e6f8974fca56805365442dcabe75315a7275f7180c70032f94a90536279739186b36333c839e87bd36a36d27be38a35af65992a0aaefc336d4a14a5fd0f74373fe7682d342b63d8a42d30f07913117b545273a4f11398e0bdffdd768111cdd8b6bcb63c0dc8754407", "63", 1, -537474924, 0, "4d9cbeccbe61f6cd29609d37327f7855a4f7d0afe48db438bc5e8c7e97b6ef1f"], + ["5604b67f01963e10e1aa94d4f83afedc9bc19e49b591f387cfebb25dbdab42c076d3179665030000000353acac0c5203be03aa2fb301000000000663526a6a6351ff90ef000000000005515265006a27089a0100000000066a0065ac53000000000000", "5252006a65", 0, 1630946853, 1537743641, "438cc62a94d6df1a707d25d2b925bc37aee5a8ea2e97dd6a836b8c5bfe4b7fb0"], + ["110eb45b0214e1632a8526b3e71905c5174225e94edd0e11b555ef39d4a7683dd6e85908ff0100000001524c310f209acdb29aa49c04c6160984757c900a752b4618bb4931164c8ab900b2ffb6e3d100000000015306d7385e04a8bced03000000000663516a5365516b804904000000000652636a53ac6321bba001000000000553536a526a388be103000000000068305fe300", "53", 0, -46500627, 0, "b64b4cace09576e00cf0a271388c076e77c22320b44d933484ffe5a32ca6a39e"], + ["030000807082c4030216d394586799fe53412f565ba2d669779cd901e38313f4d77559ee70e5bdb4450000000006ac6a52526a5107ccd4ba666a1a777d47e8fecffb904760b63f350d04a3ee73bee3e419fe9b7c7cb8d4940100000001ac35fb0d7902d2186f00000000000451acac002242e202000000000963515200636565516500000000a015ff32010000000000000000e9aaf30500000000ef7f3602d0510e09be29b32afcac981b8336bc2bcc6a7dda89ab8142b28de2637a1b5f7bae7bb36a3821e6694506cfd0f0d027b93e62995a91e7506c4eca50be2caa647581683d17373dff173619063655b65d741ffb44a4ee6ba83bef3a645d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003b59889b130c30173b2c26f999f868ed1abfed77ea8c6f5372d7e880edf1dcf25cc610456e6a979b805ebe2857c74c63d9612dc92c64ad503e773c44f2dd92530da046648797a947f6ea94042fbdefe7a070530b7c2fa4e43d5f06b54338e12695802d239f856e44f36cdd908404cccd7aea293b19791d31e11990055aba232f021f0ed27637193eb4535e39e64d4875b3fedec7882e2a6d54d8c30aa0477c9bfe02107f6f84506f42d0f9acef5e5f375078128589ae1ae839dac8c39762d27059890a00ca7424f97a2b347bc4b2b156856a102be439f917ff6b5fa6fb2f8219f035ea39ab61188a9ab6268ab5fdb4a08cebbe525cb953e2d039367df3f61c955a2c1a032a4ad98e52805d25e11d285fa80a492a11bb95320fab092df2936976d13350250215766318c0ebf040b6402772360ce1034f61af259c82f85d9dba579f3eb8bd9f0229a7597b90f6141e80a977df4cc9f708018e4a93ad04643fc6b9bf38d043e9b003286229bbdc2dee7815dc57d5f77a408255482de05bf617199af25261bb9260b8022c0004448b36b5d9b794304d1a71e1480fe02f43b54357f21baa704fb8769ecc40282653e702e55d4167cb64b56dcca9f046b6b73f33449567ef0e56bafd76dc7d5e874a141158ed25e847c54b1937f1fe78acb3e9a15164ac46615080aa93c1bc938835b98fb1b842c34be1fdb9564c17a12d0635356de221aea0e12334128a2ee8bb24412ce94be144229be0fc3c2c03823b1d84dc3279dacd2e5db725fafb7eeb1399bf19b550a49fad6a49b105f58577a18fe3eb5c7973d6a0c87e6048d13680e9b51fa85715b36d6bdd0b845038d9fdd689dcf05c263350b3a468497f7fd3fb2bf890ea35f56bb5ce6a1d9d95a24852b4a2aa15b623aa5b166a643cf5256e6eb744c6d7e19b0e4e5a9477694243139056518f4b337c76a20bd5ea62c4450ef38d5820664bed0b601429cfc56815df97e71158c3ca8e51f163df33f988781e5afac0b44130c894ab255b6d5a223fa28c05efc463336a9cfdba1b6b4c16130ae5d26c9bac78aa0dd1287677005c3162ae1c92f1344aac419409a8f280915dc720a805ee5962425886a6b89cd4ef400bace6efd0bdbc5e910e068d0e8ad8363fc48f15fc0e68c47ef261d6a90c8bc8d30adee61712c362e92cec0175212fdf8bc7e8f210ae527a750ea665251425b57f04e6fbe693abc8d709d283505eba707ae7661265b418a06d7dd91f069ee5d2827d7165f9ad158472aa47b5664ff3d238ca6b16dd519df7460a640af892aea0a5f5d1928cf30f7de445559238fb6728059a94b109a1e38f8ec026373c515d8e7fe92fcc5bb51fa3aae7adf361f3b97b48859fadfc0d9197d17a99308d1128161a7247540c8a80fd49e3b587355cfc7ee5a87756468e0590a2d181bc704d76e7b913457b3a46067db75497f3964e368fb24cc4fad7bc422519f7181f43c1b93104e4010dc2ddc2367fe19c2b4a480ad3ce79834cd2b4e456b49fa56fe4610755830b35db04fb90c22480e66908f791bd44fc5c89e3adbad51e6a527074a87ab50ba0653eede9b01c92bf8be30ee3970db714763f4f7bd54250f284796069824db51b21330122636429b17693e917deb0097a683889986a274915e75b16c395e55fe0d5161e64420a1cbb11bdb4b80fd72c5354b4a72c3b960acacd6635ebf557f3c85d37ed88216391dc6c8db6c038a27c223ed92518d6c1b9d7db944af989050e3305b7c12e4e0285d0bf5fba2b35f782c2e2958cbd0c6ced57b3142941aa965c9bc4ab402c41b5ca2dceb144d5c7b007355e19d5fad606086a461232f94a112471c9ff1b0e72519160dc3ff5c4524e40b5a41e633fed9a4db945b1b56895d28dc4380385e128db170f2e27afe4c4e8ec319b9752c84eae1520c1ac34ffa196425d40d69b7dd519c7945120ec12fac7d2d014bd7d45f1c64813b40f254038b27b655f49f39fa3721910c630e9be40c7858f3cdb1a204ad9475d0e560f5f60a2d8a9b9245c31a8bb61527765ed294cc643a01e4f0fe66271411cde1461683cae2f11ef2bd178726871f5ec841c38d87d95a0b32081107d6c6a989d3bf01641f9f650834f41f6e4d1fe29e846e897094d3bcd2f8b053501af8ea886b46e6dfd5a10c0f83b3e3c88a7e2383d95dca6eff11e811ad5a32488e5555c1706e9a86d5bb77f9e1c45da465d529d4562160f9ba246672c3e1f17a13d6a17129f165e2b16b7d5b82f7f4a0bc3703bcf4fe961ab638086949d35f75621050f8c2a06274c070c12c6c428e1232c5d8f00a9925f390f76829f0a473ed6760c52a933842a21608b82e4688c62544424e3503b3ad1f0708b83049e2f26c490924a1d123f412a333e2c1c64a09d045e654400a69479a1266741be5c58f9017015a8d6a098fc7e01f008", "65636a5253ac655251", 0, 1869852744, 1537743641, "3e62909079e494eb875c3e99804abf4427aef6bacbd5fe9b46cea5774affd3a9"], + ["030000807082c403026003076c9166e07cda969dcdefdb683448b4a2a6addadc012bc01fd746a58af0000000000952ac63ac636a51526affffffff24825a1a5e2a9ad86e545a09ac6925a47515ed4e9a566923c798babb103d9329030000000152c04572860161067001000000000363536300000000bfd9378b03a392e201000000000000000000000000973fac2a0ddced06115d04dc4766ad4f14de5b8e1f7dba522ced81b0bec94c20fc74f1ee0044ecd0562d5ab2066791981666bc7abad7d4e21ef24e9bca26bd14562a7bff2586d21542375a7a325e3692947ab5b0f47e8e0436d62fa04724eb0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000479536e59e6023f53f6a4c0e23becbc0273d3dafd6a13b67c05782bad08ebaee2272cd33b08a616bd3f22acc1f8be6060883b58825166cc92cd8176a26f397bb17dc8d23e32db6903c3bc06d9513df339bcf6243a84322af2ca215df39ef2d5a5fddf9d7dc292322da0c8754a94d38687040e0e551df7e75a3628e798535873002087d000f03d9e0a13ddc01f5821cffc013dfbdb9a69ad49aaf49b2b00328978803222fa22d0f2908184342644602dbb2d062fdd1e0c736373407987a2f8517ceb70a041cac230f4f6a330c4af3b576b32ebbf072a6b533e3bf42223d3e4050f6beacfd3ed96aaa4469a9a571e85b8c5430ee63318a02e638da40abce1157a966ff0a031f0154f694d9cb750feedf8aa37ef7b98f397dc471348126ac75b5e4d60ac3b60210451599142db3eab9410ffc6aa486cd510f74821b3be7d5c45b70b89abda2bb0223b319d71c28f98e3374eb4d8297199b049a10ad223eedbcbe8d0c86e17cb5fc021beac4b14b5fdf4a5d5cca29bdcf21fb4dd9b47dab2bb55d7129a76de264fc4702029168d24c54fde9670f4b76d9fae00423aa64ad87d38bffc00b7c90abdc98c1c71d0d1270d336f177bb4afcfe03f2ab71983ba7c8416b19af5a4cb0a497d8003888406149433cc9940873135dd4153b6a9560e3523593bcb8c0b698a9a88664db1aab844b022bd307cc0d33c1c6f1c3fbb63136cd5efc7cc799cb1bd552a21a0cd401f8ff562f73b7821a517789f11af6b66197415054e525feff5e46b554514c46073461df7e192fb48f416d61afade1cdc97dddb74f4a7ce73d457a2398faddec68e957766845b78028bb5d329336ace1ac4d07b5b22e9d1a506cfe424c80eb5accd18bc1c4172cb203fb456a9f46c857feda272e2377867174b8fcd1429852d26f4822859e7601d45cf966e9e98b5ab93a7e87897e04816184c44fbab97d4d8bd283eae39f03fbc862ff4593e8da5b97dece87ccbc3c247c73bbea74c5e6fe19dc5a65191c61036c4aff9b4f6cd99f76554d4542f8d6c38a154cc0e8ece1fe309b5f8451e820d14a845de0d0644fa425212be4012d70bdb1117019f03ad7974afa71e872d7be36bb81c39ef0763e773a70ae3df0a9b1a74dce97a92594173fb07e79848a930a5bffa333c8a7100313353c71a3e2023b6e3569090710decdbaa5b7508985790f1e70357566ead6379ac270c003609c057a00892320f24cee38d90db246e4d9461289b1d0e58dbccc87c1be70ba70158de8769d8539dea00ba26b3692f1717ff2dc99f65955288d7e01570586533f6a915686af74e6c730df8fc98ba27b257654bed1c9fda660349e4eda1cb6d11800b793ea5a898220a5c4c02e5ae78d154363e1afd357c5991ad3a4dd665eae5d9cb223613b7c9ebfbf640f249ec7f78b0f1348f1c931c50d820aa4ba04960ae076d7926071985e447b67e9c6d29c95dc39df4df1acb23e56f59cc4eeea26d7449e802a36ec5d0f9fcfdf2e49754e110ab84f6cc0f292c11f38d4594dd63f6356e84ca0397fba74c4af1464ad6ef26dc1467e869e9f5d42a016f420d5219cc840a692985bb04e472bad65bdaceeffdccb453f6022a354fa3c5c1a0922f321f7cc0fbf9a7eff1d2ec566ad9393c7f144141b90b6208b1dd214a677a222cc2b3a3624f4abddee1df256f619e5083d282dd84ab41cbd0998d84b1d4b3e561a100b1cb32b054f3aa702ee50be5401e98392bb3b7114b195a04cbadacc123aa8b5295c59b7bc886531410d6853f10827192d07ab1d672611b56fb9353ff3803491d3eec166db5df1deb4f245526de9967379f3d856eaba556924f4b0ba9fb6d0b86097828f166d8a7448bd260a9aca706af00e5ecbf449a09763048fcf0be0a114e646d92a2632fc7a5bfa3c6714a274657cf4e1f34b108bf56083a0cfec188462f26ffbeffad6110ef56dc82cd963a6cfce1f4637ef621357257036b0b802f8859cb56ad53a8cc5643e7d58b3dfd05a80540db61700d5941d2658f754e61966644b3a85a64b7c8a937ff1018ec6e8433226cea62075d73a3b22224734a51c6356806f081358511da3e2edcae00b960a85ac02cd7adf2b94fed588cf342a787f9b8b488620293315542b3c628981eef749ebd9a301deb52b4e34beb35b878aa1748fa7fd186a33829475bfceda2a94c5435e0971b69e68035c01a68d3cbccb367fad7cd757cade529dfbc3aa7976f26ddbe84e6ebaf46a9bfa55cb1de082792720b063f00e038fba534e0fecf87cc841a0919335f6a19d41bd71af331934d70000000000000000ce12b30200000000478cdec4f39dcc18c296aba2626232c95db672e2498942333589b606ef3f3caf6147364cd5c60f18b48405366e23287eb8e4c4456dc248b00e6837196ad504fc7fe1f747278fd3df2c3ad631495b3ee5889b062954a7b11317f9b3aa5568dc030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008d9b7a077a931390d4c48b1c19d30d28637c0a5e749d6edbbfcb433ff406f54a8bc79ba747212d3df68456a3347d6af12f41eca521a54e2506e170046f745de724ee3681a735d8820944d0a6a9e46ee9d0b3fb0b9e8c3356f9857196597bde95b3db518360b9645ea0d7eb1a254bec164d7a531ba362a3617af30721822b2280220acf2e3f360beffbedd966d6acfeb04a845d7f0c07da5c4125528fb119113e6032b30c2e36fbc6ddec0970bf2f0de00cfb71beed548695d47f0c652ce647ea4d70a01fa4371e332c3ffad4db86fbe3caad9792b17740cc5f915b4bb38a579b472a9f3a8b8adc7bb46d33ecdf23f6c9f6c82a46fddd7cc2437138717508bfb716441020d3e1420ee22d421e8cf1063aea7e911b20c6fe33b2c54673496144c0ed43299020cd3d197419ac501e94fa26735e467b301b1ef0c5aba360c5dc5e2abdc6c0952032378abb2540369c7e190264d94c32574075177f4563e5fc3b9c31d77a5d4ad35020c5f70f121883854754f5e6220cc90cf35fc6c99d822e840ba6fdf9fc9663f3d022d16581fc6d683f2ef33d873eaa830e2a5007bdbbdc78cf108293d4728f08f0b1f9c79c35074040e4fca01502cd8a8b63c4a2f5d02057551cdcbf2504caeb4fe790a8b28016b688fef028d62e4150e729fb33cd6a854cbac4d8d7cec8cf12d42b62cc3de0a6b17be5285c2737c804936a862f2fa114b2f568f22be88016ffa526efa3ccc271098bc736616bababca52146d385bbb8ae2113291803dc793a84147bfed73a3cdaca94ef77e935cd0a5bbbcb2ffb8dc822f1fcb8e2bb1ff7c1d2dda5776a89246cb204a4020c29a78268ce2e7ce7468d55c4d2398df99c7cc8ebe84d54ade88f4ea7009ad4c4e7c421185a5f84a1210146f1d0fc056aab88e1303717702e2fdf1135ff8c683563dcdd552471de1ae06a28456caf0acfb8721e59f3678282d5ca39d561914b1e078656464828f040b046dbdbb550f67a03a975e2034e455f47609083889fd0e7b144cbdd0431479566aba7fa7533a8b48ff610827f98199071b3223a2d176b44d4ef38b7451362bda80817abcf53535142b935d7d3b541f7668a64360de4dac66109e7ae510fdb1abbf2d8c57bcd729f8d33e4d947a8ff35a8108c9e0d74da806b842cdf00369c5f038ccadcebe5269c77b962d8f5a4797f20537eee2fcae397eb38e4a7fd964adb80d31faa151e074778b0ad29826c1cc5769346d1191b8bda02403d4a1bd4ae950a0c638d80ea88f5bf0ae239ba35bc29b431456790450b29031adb5460d7183d1e5f5af9cf1da4040e31c1144482180e411b84fa1061a3a31253f64717bd391ef542478ce7c1c6f69cd13b11611ba37f984a7a04c7b2f1f8a961f1c94b72161671376d7a3446f9de3b4a3b1e3c9b615601fd4d95b502a3c37bafa4ccae1b01f46c688464f644d074832f03f5b20d853e8357e68a707f8b8119db316d458c37ed3cdbff6e974d643f20fe5c04b9941f1fb16af48964f32edab83c1b2c4e6f0cd5335db7f2ed052b7ae6e864b2b9ecb69a2b5257ad388fdf50b338baa84fe0737bac9c637f1cde4c4994d3ac73f44190ae98637a3f1324e4114b164a7f5696c2fbe804622efbb84819d16f88c936a62ca85818afaddd7b465a316914425e40cb130895900bd7f025fc448becac2c7d15ead3606d08e3c9ab2956426d505265f1b11e04ce5ffe70a850576e06d54a65ff0dc9943f3b6ae93e10f7d349aa177c7b7480b0a8668f6678f07507e7ad61b2342e94f348622b645a43d4552d8272f910965fcffc9fbb820ef5b68464c1cb3c055b46a0430883d3263b02264409f154ad594198ed8c730077a63ab7f3a4135ca50bca6d2dd8fcab893f0055856c0a54333ac924d959b0ac97b5a954bfc39b91757199676be83f650e5210f88137e6552e937b7bed90e734b43a69a0cffc4d1675f379ae4828dccc5951853d059ea170c0f9aae24da73d57c8ce2e9159e553e69345b71c4e6a7113e1e640dca43cbf850c6ad079f2fff7362630ec294de0357547728b195b09fb7f482e8a6a6789510afe2dcaacc44f5476cf06f6242d7ce32283b9728ba5074815aa67ae11b85cb645dfb28bb58f479a083794786880a18e0041d070947a65cc0b523ac7564d7954fda9c0237f903e51e92b7a2d7f8e8c7c0c152aab49c970a65b1afeadf64992e84f16cdf979055d4bb64b5ca2f43f1da3c3130fbef4d8b072901fcc9524f6e7eb294116b74fd0e7fbcb307c97643c5b5d4b8400b5d6beb09fcdb3dd08faae0fd5f3be000000000000000028324e0100000000a024fc0b36713ab82554bcbf1c84d9607a951c4bd542d24226e2e07f720db34b3a0307e778847cfbb8f0d1119695a14553ead5f8a8d9a6c8c6397f1860652cea29254e9044cd20bd9b0b4bf24571603d2c416cbfe1005f38170ec6eef74b5fd1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003abae0f7736200f36b576c70890c329d687a8e78df451690db9319b58c844ebe6022e1b05bd89b909da0c43d00f6a87647420a4b84049290508b005d8348e24cac9ad91e6fe9e3ff8c63d65515895c038e758552bfcf7ec1aa1aa3b62f10e577fbac8d6c1d0c18c198d93bda5b6ede248b68fad32ef398ba2dba867e805cdf07030a3e95ee372bcd1ae86477a35a80ea62d0a73b2d062aa3d1780976c4c674b83402041b3bd2287416336eeefbdec979c82799b824f19efdd77793a2727822c905300b0423164b0b685d00ec779ef4d4cd3744a5c6afe57f5fb75f0abe6f8d0279ee7519a862bbc2cd751eebcefc4878280f10b241fbddddabf956745f9c4d9f44fdb002280a94e18020ee3e243707e6856e9417a56287c9858bea90a825f6c0fd2c6ff7020f8f85387291b7864879e6094ac64bf4df282975b81a61f16cd6e837813194230224ffbb45a8d3356528df66510ff96341212afdc1f26df445f79ced06276a602403075b085dc77d99a5fd1feeb17668e4d2c67cdfa87206fd6bc2de1c22a8670ac6020f546759363da912cf585964b4a1079aee23c4a58d2db35154cf3cb779ed667a617b6484a58113127cb5cc625f2fb163799a7ab40765ef8440fa689bd19b4a606d2047cd5705b922b77a26aa32e4fd10b381b5d36bd11d9545bd991bd6635c3a3678f80c9154a5e9082748aa0f54b83185dc5ff3aa8176ce3c8f30f5ad691397d48777162cd9847ab62e7ff2085dac59082f9271f8374246aebd2ba8ac2349a0e12258aa8e9e7cdf8a03174d60a1ce4869af385f7f6321580747dc6d8ec7be4cde3f77e15d62ecf94e365b2e7b910deae5b5a9d6eb07d7d856324bd20c5b9ae27d2d783534a04765688546b9b4ba05ab3342f0007f9e88cdaa23cc1b7ecbdb88c471c4106cb05bef2f48e47329a82d728133a51599f4c306edf15291ba17710b908116eb033cd7655a9511a3f1b529dd01776321496d00f25b6e6d8e76ddfa2eb2c50f8525984683c3b6cc7b9d4d07c46d6882d25ce60607264a490d33027a3a8739531dc0af36601c13fcbe56ca246ef1dbf086dd4bb17ea87aa6fe1c6237a0642cea422393ccc4792f19d70ffada1b380a9bbf42c56461f7902a502e577caf1a8ecca1f2eecce1fff1e48c3e4e9958606e39a5448ad1124f210af3dabadb0b260830e5962857ae0c05cf67f0d6c0b972fd69e28cd79014032c50454fa4320153a2486c8360c15ebba67687f7220ce73b640fa9d3aa9d2ed40a04ebe5bcaf55a64a7be103c50a7bf6b638a027d446a0b9e7f9a96ebefcf2f4d33a2e8e3783d417ea4a95ff79eafc78cafc5776126803eb8c3d2d158d37f768100db7e8712707cf12ecd2a8808bb1dc857e3991c9961126cae9056493b3bc246306399e7b36a56e6f9eab30a5c93ffdf88d0b0002e2cef6d35c17a85b8f9c0209f8c0f2d25e7d07262228feeae99e193a9c27eb012889fd81f9b3ef36110cfb121fe8bd6e6ff69c5e3a348c2e44b03d1d97b14f2ed7cd5559ce9289e68d90b118748eeb4a43e2dbc1976f0110c3fc2f16b600017d708ba8f53cb6274112fc1d5b91034e4934c6d90e028e6d2f56f79886e83bde14a63a83cae7d3aed98e9fa5453963f441b19ebc280095b0cb7c9620a15f74f6ee75e0dc19449ada25b273212d87f80a7432a07adb659d99ea6927bc0c112f93a831d7c9a17cb4eb02c5fdbaa700d7fbb18c28edd5f571895efa07da151f875d4d696f53776ed9e7635990a6256024c6c26b4bbf039e850aadf2e957996aff04b9235e5ea331ae01357dfa4c2728ee3a3560d64643bac2697fa468c5053b864378ff75ae3169ad35e90f6ff1489aba2cd15de2908380f583b96f522a4ca9ff84b97451be5654ebb245c2bf2c50d9726fe62f35d1ce36860aec2d125db20cec676055a1948b0b8888af9579f480c3858b4453eec1719113a7238621487d4f0c4dfe4025be9e370be79789e7c492613ff22f53eba2678d6483b404ee98a48c6ff7d88df44ece9044e377faa7ce1109113235fcc71b4b7d1a72ef5860eb5178b8efb637c1f7fe38d781681811acbfb3a9fb300b60b5412c0d965f550fb809d59c4e9c8121d78727d6cb08d5b6f0ea43f25b15d5056464e0fdd41524efb6448c8b95b6241642329e6c9c70fb8f5804cff1348c7949e83b0a3057b01bd6c9de4b86067e916d4019388aed6c60817cd795ee16124c0de9939e66b86d750aa4f4fd074c471a2c8f08a7451ae5ea3311b567a3a609525ebce409621242175d1b05564f47254d60a02f998154c8411a57904ac687863b50530e82173211b407fe0753b7db1768b462f90ef5d827ec68ebc800c97ee1637361fd3921b19cbf01386fedb2978caed9fcc4e116c2d37282bc297e0ff115ef94bda76a48e9b346c9302f80a7cd6a3274120e", "6a00ac6a", 1, -1411250569, 0, "9538506af9d91b067afb99da7f8823da1c579575b0ad97341b62067a724a1251"], + ["030000807082c4030136985f956a3c7df012c58d703fcd7e94c29bba802df5aac5043cad93f4e37941020000000165ffffffff016732e201000000000000000000e4bc5e56030000000000000000fc75fd000000000079d407e6cbcd16148fa4743281a27314f9cf9f99e2ad74a579aa931cad7c112042a91ed55fd170bef3a83409ea6e513c84298285cefd202d5c3b9cfc1a9b1c2d3976e0372bb472f8f784e990bec574d7c2fa03995c848a78da7267e2ca0937e500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d34e5e196cf531e5db97ba72292555216879aae08c57245ab4cdff8072d168d01839c0b4c229920d44a68e5958759d1cf8ed7d67244f8c0f69dd18a1fe79c65f4424f6df67cf162f1802d3911577f6ce85f7cf752d1141da9cf43e087c4dc988c0a269b6c11732ca656b3407b82c0de0eed72bf33f5dc80024524af519e0afaa023056c0e252d6c8e3856dce442e4b458063647fe5c9682b4cbd4f4439fabf310e021d45d03530f8ecaf34d1d9401ef516a0c53a6f477c1b611b8cd678a37de54acc0b001d95339917870b4e4199c3238aa80bd0803346894cc1e41a84ed58d6f18343e8105e445b3e22154dd66f2ee8e3136c5e745c6872c280007c020f1d97ba998c0226f2e1c7db19b330ba94a6c975ef54418a0b3e5b0a58d2eab4b0b0a3a57e6d2e021af22d0ad44fc11b222a33f78cc153052a1ac2aaf0e2bac5a05dd327e9c04ce50304ad0ed8e2056afc24287a7a5bef806e6cb50f27e4f4405da6e0d1548a4763ce02222b88cf670b44bd073da6d0cddab7b16cefb2a2425b10ecd7d140361143fed0020816d2afd7875bdf1492bf8a41e18f8bf3c4a993ebc5223c66f067c197e04a71d9fb958c40a4fc704357465c7cb0a03cbf4ca2681fa0c127b1154b630d2e9ba13568aee789182899acc82f1363aa1a995e55e8b50ea1ff5b50f0081a750b410055899e2dd7a188f3b535e47aed4b5e1e964c4807c1bf9ab545aff6785a7d7758159db6781b3995a9e940193538e1b6734d276799bd752dda37a748548d9072aea50e8c8e948d65e2a41dc8a2761bfb54a37b5f2b25b8044177ac462764ebed1056b757866ed6903cb4389ad6d4a0193275b96c63d042d9880e835ac7c269975e550ce540c98c3ab95ac91c479d2ad9d27582ab56d3e973fcad6d785c1edd0c6480a98e6f1f40f03f2edfaabe83c51a03e073a301e6e5d7730e3aff4e88c7aa7128a1d6b0aaef82b80867c9a7a293a7c9b9513960db730123a782aa916dee1ecb31ab7456f9640c4d75fa5636fdab5a53a1551f1d5d7e1af43c0473bd6ccf5fed6b7db8338ff3d6462f5d1812cade7efb7e02cb3bc17085392f47e7ea1f9031d12dbdf464ebf860b71a348feb1642cd5107174988016ead7edb8f0c6bd974c89a662cb0240bda7ba71a9b78e8f15390f31ff357916e03d5109d1b14f16aec8da14012d10dd3f31518fc26d755c447385ecf125d061bbdbba7669d7ecdee083daa03f4842c91d95ede53aee9a1a86036bc717d1fdcff6cba893641aa141c8b3090823faf9268c4af076abea7e83f235505daf31376986b37d2f54a04edbc0fad1569841b68a7c62d12f32978f88cebe52522bfff2b9a651a6b340355a745ecc37ce89acec089a8b7b87145edd495984bc3cc38747c8e029180705f5c7761f75bdcfc602443e99dfcc3c64532e655bae8bc04f3e199d91a9d067cbe21c4ca36f0c324087026aced085fbb9afd662375239a42d75c3a80b68a31d4b4555fad58cbe9c5573796d424e09dc0b00010cc49701476cfdb4bef86877cca6576573115a883bc11a592e8c246e5ac51c289b5592e0938ba4ec72494d841b42f09b02ff62c4c17c8f5e1307a3040fcae4549fe796ca3f8f0bb1db5554a8810755e397abd3da1d0fa7ff8bfa8e127eaa019c2ab7a8ea678688bef3f0775d1014203de9fbc3f3d82b695f5cd26cf427d8467e62f9a4af332c1f5d3a35d0382ac0c180f2d221a5d6757a8a88f425e1c046bdd5ff402087576ba36bfe9f9ce7c78ac514883f88b5cf571289ae536ff33227fed342573c2e0fbf124764e2092bae9e0fc8651cdbc7ff99bb6400573f8775b26785a4985e0ca847e9f9d31dde4c2ea2caa4a65b8963a5defe5d147bd929408fbfd571dd687977975d706a99f2b63ca7a37a5c04fb9024de2d385aa61f414e95a28bdfa4ca993a1274fd12db6a7eadf9cc7e971be8febc0940f137dfc67bc8c94a016690d91b25a4b6c91483074e9a11cfa37fcb409bc59e61e04c360704e0767cd67247c9f59e052dfae796cda01d80595c626435172105eeae6d856842ab9a0f6f2de6e79a192ed339be290b0ac6e41de366d7ebbf0899435b0f0a216cfe5dda8b7569b3c42dec3ddb6b0294def713ad6a4967c08f4a243c8d80a689a149f67e42f8341b037cdd2ea48b1b77f8705ea51a5e034ab71261d02c47353ad8bdd3f9fb6fafaf746aaad674b1bf87f805902872b8f3bba474064ee9dd95b93e86c4e587b05d762bc2385a473917a5c0e70b750d94ea9a438ad3cbc7077a1a2796106061f59eca140794940744d040000000000000000000000009953e7aa2a7c038528fe023c5dec7a28e2bd3c70c151dd29aa1bfa7a9db9142dcb8c44cae5d8e4217459eecf42eab75b68eb407353ad69bd74686f2899c5515c826a18b16d4dc376e5e6df21692b0421cc2315b0a2869cc76f2dfe71073655b500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f150a6de09372439b9f4d0e3686200b584669deac1462d97c12c12576bc726e4bb655c56ad294293288782ce8571a8ceb69edbf5b895aa9dca164c42c9431a1325a2fb1011f5bd99020fac4a3783a259c294077eb2ce2d6fcf401a20552d32b4fffe6d0e99e3463831014ab332345914b81928bf39ad07f32d90ae280c10be440225a2f8f1463ca251f37509d22f496c3f4c7be717ce343433fc9dc00223026abe020b14d9a77708774f21db51882857dbe0035f0b9cf38a8ca8878ac82d36a35da70a026df115e31c9bec24e87ce114315d2b7ce9e90c507075c300a0e1da16e350d27120d0f430e676a2be45c4b0a4f8e10116c272f13f270b0657d0841aeb2f42bb0304c805a4566068a9c8bb9b0f89c43df54f41a2a6a1cc5c94d9bf5297cb6f8df70201bc5be1b483fc791943693782ecb6704b611a7e9e2dec0da25ff87b86148eb502263dbd567ffa01d4156cf1b642371e11234f6b17a958c67d1ada63c23537c2e10325113d7afe6a1d67c09805f9ae43ff36ff798704c32f33e1216868b2d0bd43670318b65878d9268dd10a6204d29ba25d20823b7e030dd6d495b9b7c1bef9f5dd17c38bfd227b8bb4ad46e302ee054f4b81e0b13349efb6131e93e0c37fadf048cedac0e0d7a7ff504ae307d69fb9510eb47dfdbbe65f0e0359044be83e8d56f4f9243284e5c158b9e5b945ee69b4612cccc133c316918a7094fd4b87516ca57f1d83852b64ad0f04eb0d69624667a1fb4796bffbf2d1b81379d69d76dc0ab82482bb3d20a60866b064872ea4b908306109296b9555421e8e9643f59ede6c5fc8ecda39d94831e59a33132e0eb9c4a7664bb0a77605d311d93ccfeacd0b6de4a9e5d3943b4ed0f85651a182630aebbc8ce72fbe53f643476939b934429f496fc897e3ca9644de2f0733f9eab383bd625a988a0cd2c8ad571a2a03422a392954fc560cbc1122a84c5db3dc6a03d9a4233c5ca0c7146a4b9a5baf7830c34745685564dcc40307fb8d24e27b641b12f02c44ca9a60b9df7a8485271fdfb8b95f54adb2dc4d35d82d7ed5f6a63434773235529b3530950b6e0493441281c4e56fcb009229dae0bba966b465a1eb3345174a7946f2e290df93cdbdde940d014af99c77cfb0c6fbf215eff76c39b2007f183bb02ec909c970e7ad966b06f7954e4e273664e8e7b711386c93ccf243c41c3e3495cab2e4438b78eb44f05e5a657f6436184f08819e89a26b9fbe2fa221607b13999c7c18f902317de385bdc5a1035eeca546f8fb3272e55411a7ed30651332252b9d4d324df220e058983f225b556e5dc543c3f0e7ea0cac9069b0aaf11b2b35a45ef641e54a7e5fb1a2ade08a2194b7de4588c82834ee83422ed1bf0d869071ee47fc2912dba534ffac688785f12041d308d1006192da434b96b99aa4caa46940803863c2fef9963f61b90dcc59d8ee5f5436dbc6e1ed98007dec7b42343f37267322ed8b5d69cb43aaca5aa470d7c81089623bd2b043523aa46abbc24d84e3e17fd5504fcd10cda1a7088a57e7bd2e08d65d0453e467ee29a9471941a08fb17a9ff747288ff25698b1740499d653903655dc763b152e22d11302f15d615377d77ac795c46184097334586a429da2afc4dbea328f3477768a97c1a64cb62c0da3d07c0ac9e5a2a0773a8a0e0d14529c2554fa9eb172c3133bf7be321c820ab1441ed5ab1d0ea71c58391ab1ae3a8aab5d5e7acc018531039c78654ef50313bf17931374b58f0f3860fd57161375c15e95f55a8352f9f7e60efbfab5b9f57f37fbb8da19519f25ce627b92c63d72872a3b8bf43fc4a11cddfed17a2b67b77334ca9dc84bdef7c54fabdcd8aea28be78b981f73c08290b265d65fce6c85dbdc363397d88007c93d984a6bab86460c706b8fe50422456f9282b88f3117d335bda6b7e9c214d85d2926736cb01e72062586e8cd099057bf57feaa40d017ea707b1a73e0e427b03bdef2582924ad0b51690f7fdb9d4fff0ec4aa4b501a3ac4702bf03c3e9afc2568c43a2934ad2b5cffbe3a77b33494cbfd7837313664ebe40b904402e203a344300e9a252154910cd7018f07302d01a3e2488b3fe4c23750b5792106b1b814b1bce1e27012fac32756fb83f9b89fb8acc37572bef70c0c9f61e78d50159a9369b43ae2991d1fc1c49f457e2429e2709444e5e869a9f3cff7d6254a6a7dc749d9a0b1a137760d4b1705a233300a0c3b931634c41b685b4b6cd2fedff59fef1145e33d00d78b9a908713919a2335773d7c02bf2c1469240e725683fb8c7da6d300000000000000003428b603000000002d4d4ae6e079f27ca70eb3b6e706ca4967716b3d3b1ebda305930fd6af4574d30972205fed172b2069084698e36432187df084e3ad02b04f4855b8a54075b5c9003e10255aec31f46acff7847dcd2b14d39e2026e1c84f229c2863e6b4d5778a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014afd7b6b082a0e291f8dc490b2ce26baced924b486f3766a0b77683847f000e202712505dc794fcf36602385f28a9d57dacc620199ad408dc59501016e75c9c1c238061451986fe3231e0adc214e25e8767ecbebd79356f10670b2a35fb14bf0507b0e7f5734a81555dd02f2bcac8d89ae49b10be4b633cccf7aee924578378020cbb4f097efc6c6b3d938f50b875dcacd6440f2577ac166ceee26e17fcabfa8203108a90041fc14605e0f3f2667b6be06e830836f2bd6f8ff0472ad2681e8a22a80b02c0fddcc183ea2e54783fb83dd7ccf7eef8445ec5581bfa0562f5e29142a3b6177983aefd2c6f130ad7728b021bd68d9f1395a8cddcc921bc0c570d45d49c6a0212920c80728a5dfeb5bce19b3b0101cf22e0380cca509d275eabc27c78549d46020318b9e4a47fc605fc934b0b214d9a98a8d993b130fabd8f4fb1401059fba3e9021c7dc59dbfcd046326bcdb549ec69f0c4ecc9587242271c7a46d764199f0c0be032074e85b80b778620ba346a359d15c45e66d02ccf68d1c368932abfad4e38412031ff281dbb560468fc61546f8408374ab564d7548424da7c256e4716ec654e5e0ae78dd40eea73e86d1240346c600425401de71f72a4904a8191ff06a3a387a7cb07096e00c7e85e86690804a4ce304caaabf2f81a446ef33556b6c730cc6a3cb58686759b10f31392424721a40153ca9a98770326baaa1d37c63472940b4621ee3b817eae7642e0a79ac47a1337c393456bdb36b83a3fdf29248f26fab95eb04e20d69f8113c70cec3853086fd47c0d1e88908c6799f53a7ca86b2867a9253ff5d6931c5a6954527ee93fd2aee15a9748fb87a17a44c8e8c941f45be87971482b3ba05a6e4d61d1d754517603d4a6a5f01a60d49b6f26d70e17f0602aa2dec5d3975db10d1665bd61853a7a368dcbb4b6392c77e1aa23b06bbc38ce7a09101849a368aeefa3cd4aab101f67bf6e9144d103e5ccf8f4453c3086badfba7b27d0e8518330fbf3f0a28c2b0d9f50459808055f55c3f93329edc2760b5e8fbc778133c6d39afeda955ea894dc31ea64472fd08f826b040d9adb452e47ee60d0e67908bb8bba569f2254e4ec43d17e2329d265702f825b251ca464357dce64b6714fec1db3937150727eb9ed8a335226a8a3b49bd01557926c756a39cb3c274d18971e8e49300bdb8f69f4f497294ae7c4a4abac85ec2ed59e243b11b63eb022f8e89732697a063e78715aa97b5532bb1572f648ee7f683fc0e3d6c97a5edca0c3539b2d7bbc8325660c0fc1bf39fbe2f97101a7e46c05e7f4aca5a8f826881d59bd1cbafd58249db73add4cfbeb10a92ddfe0391134c837301b8bc143b7d4e3a8e0f69eb6c1db1b31c406f39376f47d505e9236452eb1d5f7713a330395b7e36a45962ba0a7fd412545cee9e7e4831ef09447936e03b0f54e182eeee21b0975cb41c25387ba77483d860b7953f4d8fe194dab3fcc069a77a09f9288cb52adc917bdc1ba092f045ed976db1be70b350495df4278395cca0fcbb9449c286a5f58b104e450549180c2cc70b058a56fa0f9e2ca556677bae23738eff22e3b755e64545c08013a93e1efca77831dad713a8224264a6d4da4d7faf1db5c8ca4143911ca76f6d519554ecfb802b3a5343335cc45fcc7d9056d21330446e7b31cf0d674c4def8d887b650fe77c67666d7205fe0014087f25660626e08876d09ca0f855ab0923d02e3652affcc9b36f99f3ddfe3145e239ad5ceb3b7e31fa0644bde59c86db8c7234bfc8abb8c5c4b8bbec554c7c9795e6ba7889e3592a2957c047b65c3180e3dadb2614759b791148e6fc8db5e3a44445c33614b5b1cf49dc93fe204f32cf19fe15db0930d45d14bacaf85a3a6b035f66df97c1ddbfe8fde9cad5ddde2182218335f0ab041c0c2708d4a608f6742e2e6f1d104030d7b6f2651ec27efc6017695c8c7fac04fb59eb721cdc58ad114b29556cf85a6b10c54a925b1af2453e0ff50d14978e6265c8f1e1ef79ad54977c6791504f87b03ffe0e79c9744ddf53aeaec7bbb81cf20b098f0929a86c06ecec49089b2e0ae0be363477ae9f5a0adbcb6f625f1ae9072b7bc43fa44a019fcb2cbfcc4596ca39b3632ade9570f309937b69c052178ae7f984a96a99e3b51b82876a897dc0ed1a47b540e248a992fa1b0aacf54399c87b19583b28a3fba3f578ebd041e8c87793804cadcd4be0e13262285c6cf6c9770a65462a61344268cb031e1179a67ad6fa6d35d5f5b2a06763f75e811a44d68d037c99595f773d54f90e03b180fea90679670f5b14f9ea3c506a711da5ba44bf161aecb06b8e7f11c7b9ab15db7708fe2a04c716dcf7164ce30ff9fc45f6269ccfd0731a41d521717711fc4b847e1c20cc299d1b8677dda2ecab835f7b24260cf195d6c1a24272e19a34b9e06904", "526a", 0, -1598401905, 0, "47e2e9d7aab4e65db4301ca078948d41cbfbf8e6e1d66293649e81c245192d0b"], + ["1c10441c01c260bfc49f41cf0929a714c85ed756b12905de777c003b63907dc74b356f6834010000000452526565a8a48674023654390000000000026a537e535a00000000000000000000020000000000000000aa389605000000008a701c8c19cd76ca74b0f4608828bc7023dfacee8542351ef6ed827e7d9c8fab159d3458b198794a9dee6b948eff984c69099e1da6613fc7905037fadf2226d70c5621b1c672340c308720c15591d64dbb30df093713b4adb8edd7af2eadbae400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8a66894742d700bf16f855346f7fa1c7c3b126dd2e1418e64d09be565f54fed45f7133733b6a2ff913357164802405b981e8a2e7f35a23c8ddf48cef3d9fc8e5c090f60232f8799e5bc32f93f85784fd842c7ba776af900932d602a0154e8bf415790cd197de03ae1f642548fd200fe6136a962a80b9a47b7830f114e597b610325bc04ae1a82a6df1c67647ea5f4377f966af3a9e915c1c7b33e01c6c63ca4bd0303c0183f3d9746d2434d33dd2f735f98c1a3358d2143bb1edd4cba5c3e5ccc620b00ee3b5a82404a08b9f91e296de3f2fa607dcd7eb0fd4661232abcf282e175ea17c946cfe3f4d9d152262218f044d1405900cb8cc0049d4c2d2be4dd178a046a0222d98a0f862fb3274c179dca6b2bc53f7ae7a6472308545b08d45c03ec1b3b1002043850c755d7bb4391dcc11f8ec2040fe963421f7401596f8491d71cc4f6d445032dfc6a24931705787946af47d97b3506c29621ecb136af24847ad71ed54f306002231f38709a78df6df05e23e7050f201f62c4cbbbe5b2521f83b7b20dc505d3f8020f60d29fa08cfcf43c879eb50aab6aacb5863fcc26afeb3c56c0d89d1a39dd556b85f09532c22b1f26d25989e4345d8977fa0e9625fd4bf515f668cdc3e1a3d0686b303f9d07ab33310981769e1e93186cacffc3b0ec492bafb14573127894a4cbd9a5273e48f2598c4e5d8a3e2edcc360f84e6f2e2bc1ed99c03f7ef2114228d98e1bd7bee10505b5388f38d04a0cd102f1584d81938d7cf578bd2d938e90f69553cfe316d73a823c6022f2f8421a162243ba7baac8c893041c73020dbbee4b58ce0cc8388e220661b077967aa67fde2bae6fbb5857fe2ff777086996c8a5cadc6e944d5e071dcd8beb51455c7aaa88c7cda1ada97269e439872a955b3c80b1c408925e2f421820c334600f9420c00bcf0c7aee6ebf8b15c09a73f2f755ef99f838cc80a1c1e5f2f7a001503e09f59bf929d30af53f20c0a34b8b542bfcc7f26712200689372c1ba2f1d2648b04be31ca5af5a8382ea772c6bf108a8d728a15a280b6457795a5b4d7884aa2c3ecb23abe6122587a091b173f19c4ccd32e940e52c21bd0d15c36b88e1d6a12cc0d0b20494f737af0cf8e7afa7fe34feeb2abd0f8106635c41d4c07d3f821222be15e86d2ee0c9bd4ade583d60d12c99a222383fe16f3db1941e7fd222ebb496c11009d0a7cdfb14b20393e31ae4199590c4e057c62c8bbd472701657e871d7556ed789fee90cbebd60294729a7041483cc887b639bcf9a9fe868f81a2df242f3814aa3fc94f3dd0988e90f908b158fb92659fdc59499f4c15b12c3fbd3e4da18adff1a63c1e2906a52a5e1022202ff734c38e21a52ad03badc4d8515f09e4d9fbc6ec24433afab979b08a8d5d9e17db1d737fec71b39bc5c565960715f1402f9b0b90ccc7cec0cd74623af4f9a838f317cc1a3a0700d3f22c0e9d635149e69f80d5f1144d181fa7d2191d7a12d7d1af69b904d3801f87aaa9cf8d9a88d76476141bb7fe7c1c838fca9fa7a7fb349dd7a7067f7328610215b9695655c5d4214cdadc34c66198b9ba9dd29fdb795bbbc593f7e6bda0bff9a1c616b9e6787e7417c7fa7c0471afcf3f239fa42e8c236a19eb2f52c2881a555d3ca0f28c218b1502d4042c56c83fbd514834632824e07d5dff201fdb4f966d6660badb86d8cfc4d5b6259159bb32bc7553d40493a2f9a741303a8d4c1edfc73b8d08d1c5163d4841a261dc200b9f38cacf4c3cc594efd418558ba12cbf818f6e5891702d7558497019ef10cd87be27372f9cb92aac294830251876d058a01d55c735c3f28cf46d6e442aa31c8b29aa6a385d46ba2b16a3c0abc19832a682149f56e084791d51803c1ba6f642c35f71da6320c703ceb08dc50e1885a88af4bfd06e503b332eb038b93d2efc554e8191d2b0a36eed06bc45c5ac2f5b6d7d1e6806d4306fa835bb753ebcc19486dafcfaaf26ade1d0cc1f1c7c8bff53d77d36a79a0e194ba897c459e8df876a986e0968c9082b33a43e144c856b8f30734ab1bfc52547111ae20d647f49151ad672a6446ec2a10a0f170e6644f50cf8a6e3feac1e43f43d73aaa86c809144e1b588393786ae75926e79dd564743a1deb9a879ccdd3df65cf3a5b1098e876aec3e74bde35b5a892d4fd099fc297c788d89db212615821899c35cf127baa607283b3bcd8a82a8186e110aca38ed9e891f21dc6bc8207a0b82e4a764722bc80bcc6925b79fd07bf07a1e779a2038a7e8d8b4c1297d2d2f9bd69dd443b01601eb23cf64300000000000000009d55e00400000000099165f1e28d01c39ef9635abd036078d77c66c432a275133d9fc6ddac2c1adabd861cb2bb2196ac128a4550d3b4e54b561d82e88f63d6d139d1d3e161920735943f182fd9ee22b68035c4909139c4e97bce7b54f84f72d8040b16609cf687fb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000047b0b57b87d14a782a398f14469658153a05ba40133e43dde16200865b4beb66bcdfa8c8f1616edaba196faa67a71e58bb0538ffd715364ff4ec7b3ed19df4f6fea69782b671a296d8c094f4048741e291094c0b4a0022b01f9b583279e65ee20e2b1a47381a2cd63c0d65d937ae65f9c30d1ebeea3231692d9fa91c434e556b0328135bfbb468a8b9bdd8fb634b38f5f9b4d345d085003d431f49baffb2ffac72030f6d960538c2fe0bc529baf09b414c3026b0aa9ce35e50d6aeb008646a7760c00b022a87eb16a9b24f377169c99feacbc2f6672cd802107a5e60e8410f14f66d0afb1c13049bb73a3f293b7be8be540ad074e01580bbec660d8219de7ee33add860215e88a806de0851f5c2a78a8768bc54c85d724253a9f40d30bde3b0cd99bdb4002043210836ec617fc90fe9920455956f1f92a5f3b5688d671fadee7a528f1ce7d02107a2949de46e18fbb229fd331a33653bf67f36302f9f8029c46ced4edd7508a02070721394669267c915c46f129be0115fc7afd9cc2385e5765560e8c82fe6e24022c4a03b67b85a5e2024f2465c3ccdb85249ba380303ee340cf99bdd66211cf1dcb33f5809046274c08a0ac73ecdbee029544834ecbd6c4a933bd77ea37d69a1439c610352f46ed90702a03e3ad823064f59406fcb2f902dd278c2c329c745ced815ab3f3ee0313372402098f84b5b37706aa5e3c22eeeaabd90ae0720a2b4833be96c411af22f1b409dc932a64eb00c2123af86153faa4f8fcdab525157dc8a634700baadeb5b26a5a44e2ca300c624ddb3eb3d04b001ec8da32522b580833da7199429451a996bb61ba3f8e405fa1166b51791681e856c1b70fa97ba23817cd5a3576532ec6b5282cf02fbcf368959be9e553bb0f6b6cb13e6edcb8f7927179e1c25aee01023f81c1deb13f72d337a326cc2399205b3f8e71c379e43041487fe9f70561f1c5af6aae7bd4002b561f1aae7968e02a6c0f44f8ce9870c038f52a6b9dbf7f1a50a16d94581c842b29d115858b3ce4b628d900024f603cb44d67d6eb731ec49955ea69d1312f6f9da81edd74d52ff4aa3b744155ffd04eb6478ef492f62d9a99441fc8e13481f1a0085d708ad105dee1f96f1e03c777ce19f86dc0b8bc4c7fc2514f62c4c89ad2c31f1572e8b5d510251cef820ab8211f66b965fa6a1243033bf9a7c137c586710f55ad93af20f2037c4ec3e056cf10d6b6a2a84179fef6395b03f444658d4a4ad75ffafcfec855fd0ca7f25ee59c44208cabbc072fd3afa6fd0e034e3d87c485e99a89b5c111de0f3849183dc69d843cec81aa6bec4577db5b9d2183d78572aa0227a3ffb39caed3b044f7dffad5713911dd2a85815a882082e4f117d82fd9a894ac7bad8a6776260e5ea466ce523ba04ded9a1e9a1b4a8d49ad04dca874ab1a684616be39f022fe453b824e54af08d733ef528d1a3dd6e3f0b54253dbbf6d774d4a2958623093444fc4467c9f9721dc4324cc6909b6147963607b53a9dcdc88546435c9684d4b91d52f2ace61674acb72ff2caf6fc8aa673a6336a752acbd989f67270668627d256282780d4773735fe0d8e095ff5bad30071a382e1514fe576167d6e547adef028693b8fa921aecb499b460e5f9e2ebbcbb3f26ff1481d81ae45cd23c0ef5f27dbe4aa04a303cefbd37ab1c1527986720bb875ee9b37d1fbb374480b87d584609a89dd8a7193325eebc603cb516d7325bd007f235869c0af7dcbd0e991f5fd00217cf9fa04e685e47ac546370110042bd5a14db9867e37611682b5fbe81432daa378d165e5a31711557cfa24177e3efdfe3a8a903d95c56154b835fd348fd2f6affd1bb2cdafd1ed261629c40e988f182faf714c6202215ecdd86e1175651439ea607df627f334eb6d8d36100a4c3bf1a0b02f20903a6c200cf11990de1355c696377cda2f2be3c0184ead222f3e5f0e015c45737d20ad429739b96755a1314db07e32c23778bd383461b99ea2f4f79ff7ac7b15c350abb23a9beeb14f394307abdb435b4648f81c7db743a03d7d1da73353dda45dc915b3889d18646f36fec84a45e2cb430b34a238982c3fa866b7545c11515346e4df183508ef7e8ff193a62b882e46a80c6dd29856d80f0161e7ca8873e0b01f4e983d27749f30c11a46ba83a6c9a09ccf6162681a4e883e0927f2b37f343e3899f2738fad90d5d58d9959d755df0611b363c0b6fb64bba0306134b68c13a5fff96320b0bea00a38eca733c96e56e3f5f8ec914dd57349fefccdd6efbff8d4c4a627f68fe6577e97dc831071b12d6e606f1f79ebc1429a1e1638bc396c6de549785c4af5e24bdfb2bdee34d1bbb3a93074c1eb5acac86d7a094e75629e57526fb2669c49049561f7c8c15803de4876034353a94a7e571204f9a3e83f4df5793d3176379c237fd72fe00", "63", 0, -1581468389, 0, "4058dee5938982e299c457238effd57f96c472c86b1ec48702a2128830902046"], + ["030000807082c40302e0e26a213ffb0cd23b2ad34ed7da5bd4c642bc05be23454a75071b7713c2bc4f030000000100cfb67e1a878e203e9922a54df6a4675f37b10187cbbffc80fa4d7bf65f825f4e0e2ae44a0100000000ffffffff03f402ed0100000000096a5263ac6a535365651bcd910000000000035365515a8e6b05000000000300ac520000000000000000030000000000000000ad019d030000000061bc32b4f67ebf917718188043b2a3c285f4e861e8ebc8653d1097514333f306f79eb32657129f2a4551819c5a9646e1e824ffb5d5e3c8db312916f81335ed10da8297462d73fcc2d31cb685e90280d828087ca7cc80fdde914f3d3114ddb2eb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008f1982dcb8d26dee453cd7a9c4214d2e0ff41cd9f24ba6599d33d5df91b1f62041cb7b708af9c41e86ce99ffb0fbe78f94c2c8aac49589e74afdd98a9a78de0ef19ba997a8d45bc9c793b91ee55ac3a7d4c55e872c4ef2a78d926251b75bf329a35c6d4f55dfa5cc17b6962703f99d3b164d51bb5ebf3eefb350d0a663975e4a020166b41371d608bcdb2305d395e919e18db715d0e0b76e4dc1e946ba0035fd7b022b0588ee3493cde583c15b04f14c068d54783cfe828d0872b68cfcec259782690b07c6b708286fe28892afe5091706ce9081465a387652927a9e6dcad24726eaf2241dc99c8672fc231950ba33eee86486330e2e063c6aa71295a082d808adccab0324e15e268b8359d3799adf605ffe83932eca7346566e6db5e569c1bc0fc973d602298dbc1fe70efc8c971f019b7f5f738aedd2d85e1beee6db95d6ee625e2d7a4b031e0c5e78e3429998758ad564e16e4591cb7b7799f9e54a8e9c06dab3f3b39e030230574a8d70d19903221e085ac3d8baa019a72f915103666d00b91572304b1317020969989c10d587399606b6c589e3b84c4f491d009b7f84b8f906600b531553555ae6d897d42d60ac01d8bcbdba241c58a6b1568131becaa1ccf4d3778901b9a4ab19c8d7ca7aa07908128ee5dccdf9484fee52214b8a6bd2467e21c82d8969606aa3a84e8b0ef62e5772147440f7f4b2d62c631d46f2fc810026453fbae3591f819be27c0d82a93b5a578689f764fcbf5b5ff71baa53e97ac5cb09e1de35910962f36b947137d20a84c67d720a91f89d182d661a22dc6f33cce96e9136c4416725961d69a8abda1b286dbe9ee9bb4d207d8e2edd4f73551bf06da6e28ec0db090ac97707d23eb1738397a6dc45ebd2591d0acfc8e33a2958b66cf5a64fc4046a145b3dc5b949837a572a052ecb1098334a074ac0b4c960bea22ba1f995078d7038c4d610b0d4084a3960522ac0b772c4202a3e38f6c830543208c4cd1a4a8bbcc62492c0ef6b6af2e6b18228a574337d7847b3afe507d13fbd2cb9b66637b396ea04224cd38a12b9041f44c28bcf2af01dc4729bda771cdd820b0ee664acf760d5a7f9aab9d39a671ed03e7dd0ca8991786d6e80b669a4977b90f1f7ded029fc26261613144885a2022bdd9011eb653bbc2189cfeedabad20c910d1badd80fa928d7a44838ccb6d8a309d84ad8f66f7d667712613ba90ae47a785608f8a571cce09d0ddeaaaa17f835246501cdcc0b651885ff01325e1d96ec6ee39ffa9479b71df443546aff066508acd9933dd6fb2e0f5e82aaa46f5b6e6fb8283e8f20394b8136eed07cf545453a48e26826fd5b24e600707552e064523c19f2c6f4cf4ef47aca9a8335f2b5f0d20a66049a6b1d125651532dc357fb90886e73588352575d326444d69d5a2a9f0246494665840212e22944a19d2918a27cba9ae29d1e5970211cb0342092d9b47e1b3823f0f04aadbfe7746deab245f906fa9a6e15cdb772d84843c84a0f23274b080216c0e197a51d42c4308096fd409394f72f01f140789fe9b37a56b2ea7a4714f78e0124bd68c480c3d93a781254aeb5f5298694c3082d886f854c460e61c13b45a587ff058ba309edb801777ae577ba0ed5fd2ea88311e45417a651dfacbc1be29778dd23272a02c3c38fe7f078523baad27d8c17e05c1c07c065c9ff7221b620de442646a0930b124634ce9333748b268c4db6e5da45bac2faf041086e0873d0f10355aa0a9dd3b7106a35f874b03fb45e78fec3329bdf68819c7b4abeabb112ed673aecf0a80d64677c4a4098eb9af8b28146174f5654297726deae94d3ba31ef507ef447f12e7462be7cffe156fdf8fec6535eead59d4f9d1d8b27e5c7f86d7e94bddd50e1f34c48359ac53356c5835c58959912416f03ab0d1517a3e5cc73b1f14805c7dd842421cf3810e5eb8f5f477992c940dea508d0a439d9d60899c08280ca7272b9cbb2f215ed908a6ddbfecec4b38eb2c738f71cb0721498f7532d562c23f07fa1d20f6720c8c3619be3f1fb8c5c23db16829a4a415704326b9deef940163a2af85cf3ec5f9f3c6e3d8c54eb1e4f436c2565ddd98580960d75ff65591ea55b079603a820d8d857ae3cc3f2c0a29eccf0fa35b9183b4c89c1fa36abba170edb15188161ea39253bd7687a48021501db61bb192787e847f9e62498050a0db225f7c34688ae504c7c2cc0ff3cf7c0a4d50525ef642654d6dfb9073e2cb7300e3484c7fd7a72dcf62a2d463fa7ab524f6a3436fb9ea13233e3df44f204a478250f0565395af58d01000000000000000000000000aaf62a26f153009657a025ae9fbe972c0ac6365c960d27e7a3ba331d54d1995d1660da6f0e7c824d4ca589d01b69dbb8af3ad2e3eb3d813b9a4a1d01e0607cb293c7bc288faf3e35e43e486aab9acfd8b2a1379921ee0201add0ed4c25ba39610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084a4d0dd822870d41f1c5f6b68e96eca35d8d689f6bda531a3fba058a5f28d1bfd031414cddcb95c04299c25e4fa6088309d591bd89dc6a04fe71e9cc72d27f3578672bcdd6517e244d4902c4a426cc2fca980efb853090f7c4668182338099270daa41ca087b0e0673b7570f9ae13ae0ff3a867b0396d7590e612278e1359270215bbc32eba139d65e397decf6082b9f307b4f16c5b75c155f3bdc8f3445140ad03008da7b75b40f01d73861c8ea713db633701a87a979660ae010570cbcf35df320b00f161ee653a0191d4bf961d7783a8f84257e910aed41354eb2f6088144dfb2921d72d99c012861c6369ab8d532d99218ff5fd57cd2a3f2629b3039dde233cdb02210df005d3c83c9b4d1869dc886517491f5660dcc0d0497955bad60e760b25f7031f50f72f550c03d137bc4974dfd5629c8994cde1164d6a178f03b5d8705042f10200a06693e004209a91919c63c9a246034a6daebd7d7f1826f9de9388effd4c6f021607ddc9c8a0ec33b7348c6133a9c55e8190bb141cfa29dc8d0cf7b54fc3f378022eeec72659a80d6e7ee49512e4a8003396b8f855334974345d3c72cf5eeca5f6765350bbe6194c14587239e5467e0910db4e8f9d21b0c2ab101d06e77202ea8cc28b8bee420687c97995393c925fa87d0b8afd7ed28ebf518ab5c2a751a0ee2d0f85cc972991b3686fcf4a7e6c209669f25fede74ac8f2f0774d1e568c4cea5bcd69e83456fabec87ea861d4727e0635ea34118d61194ed2709ad37b071f3903a5a435fe882dd6264f74f595aa8d0ca34b2497b3c451a0e5bbc04ef1798f9c20075f96d5bceab4c0740475f3523e81b0883b5777be43ccdad3a9e956c23438d59b41970a542f94804067978365b389074706729b61204dd22ff7525fbfd31336342468074fb169a417f8a8e3a3926a3693ea70224c2b3a75349921e50cd15836d94a3ec40c10a3ce86c7c0927b63faa3786aa4d29b7c1dcced3054d6c62ec8d055638ecabc136d3f24197ede81c279832c2b1b8110e601ee751a3dc25f909a2e003fd86710e90f0a6d3160e0341455ac7299cd4566f5d7f0ef0a3733e1184aaf5040b015efb41863ba178a42e05bff8775c611f2607a8407d6457c8b2849cdb5d43d0c1e2af303e356165de9f6ae4aedefe0ad078631de2042f140c2a4bc3a90bc5188f89a93b7f566682a8a1ae61d067777290ece2979104683c1fd7ed585d9efa8c3c6ff25b22975bc704470007e323602a091bcb2e5f4dc68c5f20f92ba18435eb046acbebd9508a36ba7e4facb4379d5283479aad0140d69f5dd0cfba017ae2ab21268e8924b185ffea66666eabaae7ecde70ae3de85a956914ffe4e7a3538fc992126d6822170049f22539f009b020eeb7438070db0d100c3b8071caf0acfd3e2175695af945d25dd4a625b1a2b21cb401e24b25d8936d20b84daedf28a49f68858a97a1f3bbd6a2848af886d6f0c6f38d4bfa5f69fd2f8a07ef7a4dff299b3dbba84115c779a9ad37f6f3d69fff6a53858dea3e936347c1a1df0d045274e3451d9476eaff6f1c38f625c49ac67235378dcfef7d40a826be5524ed52edf0143c16b45ac70d2826f1466cfac9b71b848238f49276e03fe35f9acb2fbcd63b582aa20d5b3c50db677265ad8acf0162edadd2609b47ab1cd903479a4bdbe767e622f5479b548854cf07ce0b0af54097f3a5ef03e3e6ed074a833ea5f1a636c4de62510d7c987ece78aa88457e010c7e3461300cfd98c241b60eeffeaa937a08601f8224a0c5602aeff4fab63ce4ecea65735ad7871f599c4da63e8a2b3250c1550546a886bf94c0637d1f0b7818a9faf6062fa71b95a2f655ba3db2c6571db10bb8105d37f33a7b66ad0da6c0d13b68d3853f9bcb011e14e881528a3086cceeac70fff79baa2b09ba72368b6fc284a0b63bed2fa005f5023927def6d9aeb46bb91c20b3380d178e0fa6cd95e791394a2e24409a197ff2c74cee4f97a1c87b2cb6b4189b21e9aa8b24b147c0d38e696a2b71b909930cb471f2e63aed127a06ee81233db4679b917f48122f8d0d063e196676aa9fee634ee6fd28eed644cc73081c9363b7b8fa214a03a0425133ea038820358a8041d9b1111c6d898d76ed548a3d950df70f3252af3f800607d7fe05a63048338d74aad8ce85ed2e3d178dc883d33e17f654a6b9223612c44efdfef242f6004b66aabc7e392b330623b2023929441c61ed334a9d086216848aa3fb9d3dd887ba483bc116e130a0a0917077ec7896b5b7c3f58b35341191b69cf0f3f380ef49d8d22050000000000000000000000007a6f327ce4f7f519698ac5889eee9aedd2417db12ed8e8dacbe4070ea7bf31827c803ee90a43a901eb0e5141afa53106a1f7812675af21f31a7c3db0d2124b04d7d65f636064ca6c574f135eda2e1e11dd66427cba1c4aa08de1619e446992e900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ca2bc8ebe7d844edf4027bc35efddbbe7371c2351ba5b99b6731edc204e64e9eb17501c63957246c63393dce8c802858d2eb76dc91377b0ed72cb31fba4009a40393d96eabd02ca2e15f5b93d6cb01f79a6ab3d2c6328a4cbc65a0b2fbde29b66402ee78439ce8128bf3534203b2d9898697505735a8b6f6b8110134e710b1a8032b2cf4e8a381991b0f9cac30d212912a5ddb26097c96d2a88cb1fd14b659f80803236a0bb7ad01d1fd823b5cda3efe547372fca712927263c4b6ff3d44f246195b0b0028ad8e33950cc750be2d0a7889da28935184e587caea996bf4d1baa14b58360db2f6bcaa06c254b6898c41d722646c915339ca1041d4b9fce7a56a75ddb4d703119da5d63a688e6652f741b798ed01b6458a7049e84d59723cbd7d3548d48818030209312387061ce698769544b9e815a971926721a38e25c7c31def8a3a8052d9031cce70797112b8f17b5ec08ac73ac44c466f2a49ee24e1e9d442638d4d0cddd0030a62a1ef997a06f218361b1af398833f4fa4820afd0bdc8b1a4c75d28d09e7b803146249480c15391b78160127c1d75c1a7ca21f690980fab2e866f8f0af6ae12e664d1cf43bd59531586128a630aeb59cc3f74c3e8ff1a35697e725dbebfb1f957f4ca1cfc1bfa4d82799d528495c5f2523b719869241d4184f530da51c3c9dd00f6958d74dbf1b00f13c11a136d63dc42ebd0b0151111540e8cb3af7284d702eefcbd6a0b019eb066bcd513f017447db11d8d423cee7507df2f950dfc3cffa59d944b3ec74140a38782b3ee99843b3ce1f352ddc6628f2af5165fa534c116855f7ab121b61f0723ab1198da782b1fbbb15c1111b3511ed6042879313afd1f38a317cc05893ec21796f44120350ae9cbbd73db2b3928732d97dd68b236f030719db7fdd3711ddaa3eb34c84e43daa44f9fb4aa375b96561fb010df297420f5b22e302c4b6d286eaffbf012cdf63a2f3768d4db4e0908d9b17aa18457a760798c7cd3e94cb550375b1fe72b1f9763be97c0dc4dd41c4ffc9d3bd36450db13dbd7ba64ec95e86118ad5a81cb403eeedbb72aff90f0fb8a0aa709844b1fdd4138b7d3f76753f09288ab0ec3124cd78ce4243e47f129d5e689b26d68f7e026960c9711e1a0490dd0c66a6549c94d1cdc27634977ecec270307eee9e07b1032834b9e76e951a1bf30bb83e193dfe64550c53b6af2ec2f381ab37416ab7ce8c8634bb7735144f27019fb030b915583418c21cb77572cede35565d4ed925a491f743cef7f14e765cb57da6ad85b7f3e51a203e837d32dcae20717dc8eedf8b8a2b784d89fbcad1b25d147a0fecd3ae18a5f3a56e4873844c97b1edcbc44b28ec7c84e8b95afd28445f80f5718d4d97c4d6ae89d42abf3198e1238f04e81ad4ccf4954a698ae2f06d4ce767bceac51d7a985d4b27b548576f567eed04e79690f4f5d1d3052904574ca38f34e47442bd8f5616b5245baf6a4bc07036b396e020f668f81e543f1616a462505cdc0fd3c362bbb912b93934f5e75d9fc6df7805c4c62ecf029253cf9ce837719bd6d0d336ac56a654a1196da3ad0ed0e041d42f0954a34c567a63a023c7b62ccdb82d7ffa3481dccfb251c77299d957f7328b1f365a2930a656607a539f8ca2a6112a4d80f1f260f88843cf2e79823a1a9ae5d9c9396ad054805c9f98d8160031f0963b16857e6de5d3476415dddb66bb2bd496b615f4a949f1c350576fae04c663981336958052eea35154461e0fbabfe468bfe3c3d64244171d55998ad1fdf98d59696d44d142432679a9ab771428d1fe1ff0fb5e934e9d99b0ad46003511781ca1041aa30fae42e36a817079f30eabdfb77393e630f86a812a468fe0aaaa66d1de979192138cacdfbd86f189f3e630ddd1381f259ebe0a08e0dd6142e33d282a0a5b346e2b421b44da31b647d7ae910e5ac841c52069414cd9a4950348fe45eaa662d7adddb6106774fe20c7f65bffafc4a788da4925abc4d336719935d4e01972b836d3629220a9b8dea25eb97211ba13600e84819358242bf37fcec1488a149cf385931d530d6b2ee11096a803ab0694fa52532342d59e3973907fc68c1bfea9235722efa325ea589b0324abfbcc29394d99626baa832e6cdffc7f3721d2a4c03eb3f125bf3f6150ef9911c83922bbc450037ebbd2a3ac21838cf75ef4bb8ac90a862a1478f6f74380f66065591b1cfc98ab2189badcd85fc8f2148d1034c56dc8360b02ac6685e828333bcc5d9d07e73aca58e272fcebcd845f8103c3827ba3235cc5b4934277b788cf4d8927c20e62ac3d6e8e0e046a0a9c69d3bb3590b4ad66efe33e424533f6cd93a6afa2f6100b6f50ec2ffef5a67426a1d4057d1d54f6a52f4d58b38713c6aac779155c30e225e7748d80d1d9b3ed4b3dc119ab69c6d320bbb39aea287aa50a", "5151526553535153", 0, 1919079238, 0, "913964915651ff7c48d5f03017431af8c54ef932d9454c870b183ee3d289e333"], + ["030000807082c4030195dcddfa5d27f1f8c219b10bab3dcc32ea93092fdcf7f99f118ad92c6fb97290020000000163ffffffff030a3558010000000000cf44dc0400000000003b74200500000000025165cbba46c0446e74160300000000000000009002590500000000163c564f89f2b65fc37626f2c00f382c631f8af4d279af29edc0932422191db8a0bb6df940e4cd20c45ee91fbf4edfc4061a8f60297e45d77b7e808cf5e27c413dda4cc46503cc8039b6c14c0c23c8fff512e8422971a11b71198eb1d25695fa000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f697f39a47de45fc1225a3aee64ed2d422b03f512dd6a2b4f74addffa1f4db0661350b62010825cab08cfb296e6b35b24df5f1577bea258397405bb67f582a7760e2824f19fc319b676d7eaca1ddf6b963e580a99115ab03bb536b60aa96489ae8cdbecaf70d8b004f3da938b446ea994e48b507b9b7a1a471ae22e682bc35f030c28859de1535be46ba62e455477a4636d1929c10f28d40a43daa9c024ccf3320300ba0773afe160ac6661bc09772608dcf1871e07c11ef212bc55d9b5cba10dd00b0657cfb5290535109731af26377969d32d6b7615719cf8d0022e966f7d4db1a8b840ebeeb35706880ec7c8dfea7d8695f0b438aac25be857365dc47129bed0fc021561dfe9c233444158b0e3474483d873e5b4c325d8bbc9151087cc8fdcc229000218741de04d88a46e0c69d2f340d0d1f6425c82ca09f137aa47a5ab8f8e887985030609d31ef3a60face7ca57be255db042ff12d1f1eafe6ee0b9016f4a94497208021c21406e9b9ac064f1d031bb4df321d47f96fc582753a39ffeb3bd032c0f2e0f020453a61efab2a718da92f5b7d1f8cc89a7992897e776a9630a5aa80e8bef8233fa335893b3fd6912507cb5a0d240c3baedbd44ba0759c39f396f20ff28031c87b83321aedf244a66f8eebee7025d218c06b52328bbbdeee297da592529c50ca1ad19b7dc5a94c98c551a3483d9db39ac04ad6fd8b1fe544f5b0757127ceb478ec415ee08ab9b88f0466d478a310a6e2644ba2afca672e8d8eae9d83de9ffd77dffb23cd9ccc554500cb0832571d4cd25df85fa49808149f07606d0a399adc7ff723067d7ae42c5841389c18c8c36f37743c3ffdbd8ec0c5062add31612a69ac2122d097e3a19f7e431fa97048348c2da652de53de83fc8dadcfe5003993938c1ca7aec3a812d79c4b79910cca2a4a12712496a5d65ce48abac878d25fed58a09899f3e9f60f0490dcfdc880c2bf4f2a1a550abd8cf8d8064503b8ebfd0a41a9a77b897b336722ae064d23255bf5762d6bdd183edb091621c5be60821ba00af1cda9e0f9226f8fefe00135e643f2c7a9b824cf98af0de406c9bca87e9b6d004cac60c2f6a6e38df3c790115b7e9d3753a09d549a9858b01b8d81c556110604753c66c21808b6cfc3928538ec5bfe276e3e2ae9585932cf0288ba5f8916261f705a8db853091be65ba77479973556d04c42416f03c136c8937673c43bd28ec038776741024f63fbb83c89d46d00dca3c2eb1bc7f5c5b91136d550e541fb8210044f2304d3c05de5e5572f8a66f489d58f9913cf623d296c9048abeacd5bf3b9e1870bec7ec93b8f8ae09ec3c34a5c26d74397bdc03039ca36e882b9d55227003a4cdcdf504fa95751f3ccb0f57819ed27c5af02786fb793c0395dffee7397cef5c90abacb6b0c6ae64f5e6495f53cdbefbaee346be0197d06d68d4c9f59cef60d1fe1da8943ca99167ea9bebb3d3dda9f3000c2e19316b6ca127976aea20666ae6e668f565ba7324a79038d3f8f969910b487731cb3b4d119f4f0c74af26d4bcde06b3352ab05ad9818087bf4197b1d36dbdb9972bc644133330925449a115dded7cd1e663c5062c0b99a43fe499bbc265aefa6cf55696403586bc3c58746046eda7183bef2ee684ab20c8b2e8567ee3ffdfd25225263fc280822161f16647f012fa418f4ddb5bd467bb6dc3ea5194869d2da04a6a02b509c83a4f325bc198207c3f9d9f1304373825a337e885637c80c871bf597f145cd373c11a0b8c9b6decb30cc9255fd54d2979b546660f61cd5463c73c32840fd18d483a3a3615a6a233c1026eeece4fbf5c6388184c73a9dde937126142db42bdec733d42ee721807263cbcab9961d633e070f7f6732fa3595a06437d162dc8238749318e34fdb1232f4ff90d41cf040c6339e7485e727c3d84c9891b88e860bcdcac2298160edf0c8589d1f5d3903e584e799cad0d220661f4ca77ca233a59044d3dd0308c6eb50667cbb0ad5e99a45dbaaa386b4f64c23dfe9306a9cf4fab3589f52e67761880263731928986af39eff920f8bd786d714bc8b3d048dd96c1bf4768db24d16c13f68b43c5db2fd57625866b5244fb0f3755525b348282545895d5403d090139c90d1e2b27c063f02c2e4f84692d8a85cc70c8185a284b6fbf3bc029422d1fd045ab729b9a424bfcc438873c94d717990066f267def5bb8f33e7a720379e4a0e93c7045975e2f1afe6d5fb27b20da776dbde9e1cb926a18e6dc230adfc7ac5332a6577313e07cd8fb80baa99333c238349c4972eaac2402dc4010000000000000000000000004eee78a6b0cace20564dc3edb4c69ff9ad2d2b128074e4ae68b909808f394dc5f43c8aa1d8c8c1835e0e97a963b5416de907aaa8ee7cac121b02d11fb2c33bdd2588d9bcf74c6cc51b2e5613aa41a42458be37269301613a5066775bbefae3f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a726be2fe05362ad73f5281c34d98d7487c4b1bd9c7891c6c564bf57e32cfb754c85701629ff1562125c7c8bd7e8247990162c16b116002b8b69f64dbb2181ded38a483741fad64e77c481fabc1ca3f855e2e480f56f4934bdeb98a6438eb96c40df571097437e01dce5da4b5b5a255ecfecdc1f0c0084f03b55ed7b527726480222f0bb2308bf927e72d6662553e6394a3a31f64f34496b23cb9f203c27fcbf3d0203f6d572157beee9a85b5e5d07d91da1ec3db737c3a1f8f148e3773cd6d3e20c0a062806855eb3ce4eee64b2b08d70e614472db024e271639da70eab6e11f766e98e5584d040ed7598eb5afb49be3f506e706bf93c6b9c33189bd0097111f737ed032f3b3f69b6eec686e2a550ac8e823007178d4fad131adbc7a3354719ba82afc903225101a47aa2ddb5d0ad54b97e4a04393bffd6515098c619a6afbd76be38f2cd030ecfa2987e29d95fb88049f55daf24f0f2fe6b6a97107cbb90a9356c95000252031c22c563083cd4b39dd3a6f04c52b819a51aed7ac9c5bd27c54be40be4671118020d9f80f0383f0d18ae927047c4f43dc36b5cbcb5ac9c2434787fbd7ce84a957cc7d44da19ab1ef3ad1107bdde113b168aa74beb42e73b598bda9bdc62f8565d5015d4dc8ad1a8cdf65119c6204edb2f31c6f195f1da418bf9bd2d365dc158c5dd6a9f7617802a31d42597b8e7ffec0e94f0af90d460b1f369e718c1a2b60dbbbaa96256bbbe0bb7ecd33e7c0caf769b85d3de622ad6d6e239c878e98aa894b2be961f5a29460c5b6a3e3adacb0c4a8eaf12e661177393d4e86306fa6c0314d2965411e6b391211995d6d1070622e9f6039acc8c396e17676978ff0812c2e5db0e5cfa30883911c5281e964c7354859325bded4e234e7352e9ebc8eaa14a6cfe6d47cf44be55fe71007e197bb1a709d8d69270c1a3a8813beb47bd1366d1f97845b7736ad8126eb25c7f52dbacdef1ffff9c73ac948b722901bb24ca5004cdaa9cd4377a723a92004c8cba9e441004573c92cbd1462dd5f9f8e306c897d783a1e2997e462b5c817558c82b4a4964fc589a383160eaf2fe70de958e74c72283b41607d67f9405a65d7eae95a6411195d7fd2cc98196dc3c8738b970f14e194bb9f9f70fe0d602ac70db4d54d6a75239c561f1f5132cab3e24165dde4fd995347baafc7989daf891fe9717eabd73a4b4d36a3864a99bccc1e0ee4251a2420559129d487e31db62d91536eda37d8a3804c24648c2b87b26364a4d96ddf8cb92adf9499433e4903c237a4be5713534ee388cc05254c18299f68edf9803d1a69ded06bdc60a23a7f943cecd87d64c61f15a1c0a4dd016afc76a78a0ff3d2832d5348bab169ac0635b7aef1451cf641ee5c6cbc38f1f9f8840d939e4f6a28bd69b7ea05d5d2b9c4029a464331a1172e666d09d21e046cbaca3f7ea9ecd86412db24fbbdcc6305462e5090c77d3b53fc11ddad38f74954904bee2bb694e19a00a127c7e0688d6d017882c964e896c80e909cdcd125d4c5769415aea4a40a15ecd1647369fc443f01ff32db069a9e248a41eb4cf680f6c6fa7335aa031673e0d4f55581ad41413c8d7fb50738cc9719051c9896328f7dd7d4f5acacf2f499a05df1bec9bf05497dcadb186ba3f0e70ade9e4a22b246d051e6678ee7252c9226af5cf2704fe794c3f9e7edc41270ef4c116b7c1f0d78426ec186ea909e221cfd511bcdae8c9432f5e6b6fd2a6277cc287d537e110eae0686b82921e937d612fa3570de22b23034e67b442c29fa9bd3e3c8159fa8c56019f0209e879f93bc8d793c2c0b6126e6b0d3db4902d631b042a99039145c3e0009c92219a8001f65f5d8fec1b7bf920700e759f25d4870432ff486fbcae055865b7da95c71375a45106782c6146309467b77efa9cc74eef4918f1224968e054f7503be8998e66d8fd1432a48c5f3fc10de8d54807be2aa01107bf4df1e6a50140e0c6c6a2f2b803f332b0891573b9f587745d198bae4a4ef476ec17181b53168f99dda3ad95c522746f7759ad5fe7871e03e1cf804013ee9187ed8a833f0a5c6d568c43db130e5d1c5c01961830d30f1a4cccd751240e71a314e176f9cc7f2d5b836e788384e39b0f2d8d8930435d03a239cb2637aa2f01eac34b4409e6402d6c01a7ec32fc02df4219a11a3a3d045ea679073f7ac1c6f3ddcb6637bd5c5921cd5d32f646bdbe17ddb90cf19fb091b211179c451275bdcff319062c491c0141ed3d5f95ed49101a8cdb94294a4a5a3a5d05001245788bea25872d87fea81464b6d327e7e03000000000000000000000000ceb8c70edda1cf151797be4dc8d4ff480d510b614ad058f0eb58d1c773135d5dba3c2526a206b98be1b3bfeb7c04b99a5b048bdd44e4983ca09d870fabe002bf84af03cf31c2c0b8bdf592dcb21439a9ba9e140cfe7a326eca299589fcd8a89b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cb2eb7b387ad1d2fa1f9157340c2bba41e9510c6788fe9b31eaa4f688062448c45a62ddaf3a5747bb1c46a9052ea7fdcfbb7b6f1c52b828e6b42cc33a2db5d209b49310d9f4b55f1c02cc824e1eafe300e2afae94b5572af8d35e34a5deeda9b19a404838f4ba8d915500426b8bb6f97bf67cbc4be7a682d959e89175429f833021e17c158debce01c9cd54f41f6372d75702a76bd18debf67b73a983c9dab3935021d934550bcb13d9beef7da826e3a70b348dcc1346276ebab9a08aa0eb34976260a069e19044480104d1c94b2f328f802c7ec620f71b9246c1013d899e3e40aa5bb7df961de86994a40666137f4822a67619690c30b054e12d4adacbe01812059ad03117cfb541d8a02f2de25b09ae74d062cd0bf54c738e1ccf83988e36a174925c10328685ef56516265f1a462376055500cf8aa481a92cde1f219ce59d3180017a1a030ccb1585ac9e30d4c387d077b9f88a5b919dfd8790b68b6aa3deb3d6688dbd3a0312ae3f3536f74118c6c9488504a50b43d52eedbf7c8df71d453fc16f653da29a020881bc530f8ea198307ef98d1cb875eff7b9f335ebde2575eebfa83872263f6ee862ef934a2ffb2020faf22df7b00f7cb8e475b92e9fd82574eb963f4609c364d803c58648a2ec73cb6edfa1b52aca6df4975008d33f7a565267f7686e27d86e6e5d4c44778ca98c11fc24a1c8621f1b298d3b70072506cd08da4dd9c252fbcdd0ff3d499ea9763aa510ea098757ff3de046d3fefb0a3e52ebef06de943e135705ccef0bc6aec7f332cebd72a04fa298895b94c5b68401454c9e9d15c199d0e5a468f0b47903221f223ee7d9b8cbdd4e1ce37c860bfb12ae1e2377baf054225d52ba7ceae7ce95dcba99e4e029a56dc2fe2bb81477c700afbaa43acdfeb4457851c16fb6238083a016635598c5017e38b118d494011bb3751c3298a9e6a7a3556a6303a2f4ade37f56d4f573f217b6b0a58257ae3ecbb37a36eeaa764fa672d59c8ebd0a9764488d346b439e56b083c4bbabbea230d226351508a9e7e68084e985ca4d180147973e519d72553443e14ff4d36ec89ba61bc9505a0682d56caca5877c58c33e8f93a75827b316dfc484f30500e8eaf6eaaf2645571462750bc6028e4b1eedee114f91a60a193675c456ded24adb6fea2fd522f790380516c0fdae6cab073b4b1a82ff3fce7dc8d8ba98fad2db2d91cf69a7c4eabc378981be85374c493b4b34d402ef1295e1e236d98a6caa5d62c8d41f4ac5f1cdf45f02f9ef0320b7e9693bcbd6f04fa35d4da76bf8953da23685570c427f1138f71b30c1257d3e86770a500d11e5f4ede39fd34a02a9cc7ee57c2e55445d8255218fe2fe0be4e4c9d929670d2b121cf5f0aba50f9d2fd3ed09263e371a958d0034016963bdf396d768e92c4797517f569ff3162263b53617b750fa2744925ea77f114b5ba551c3bceb4c0aa1d5c0317f1a9057d132f30efaec6cedca7b7df3d3cd999cae446320c1f5676078bd7ef71c8a723dd6e887f4f096b12da4d7fb025810ff3a43f19afc81bf90943a1b2c5ae2273e63674a2714eef7a75f7ab5bbd61221dedeb2bdc559e63e0ca70dfc64ffd3a96fc510f179c836254b3837beca1e52536fc592a6ca3d114a34157f1221ce9b1e4f652d698a874103164fca580575d246b6664f80250abe9de98e913a366333bb37169acc2e730da0d16b9cfba777ffcc912d1bc7ca06fa4e8984096152ef838bb999b59ca13712ec903fb7b9a5162f553461ae15f87e8f7b673fa57c760ce8b95c869d9165db2732a74967d3af855b2a90f94c3880c68ff71258660d6ed678ed00af0a240a088d8cf85d122e47957ecab89a5c9891583a0073760b558fc4a483a3a4d9ec529339d3aed2c64cfd7dabf0228e7bda08dacb98176f2ddcf76e6d46ca6e640d06f2fd00347c2916cadd690aea33f17b4e7d87f233f7cb3bc854e56805f9f34219988e0697210d394f8b78f571332cff709da60ca6ee55485b057d3f4df8fafd71bcf855e1951def13d98bebb5b3f165ddcc4aa8c6c4c9a2e828433d80fa6e073ca36746878de89c10e3cdcd5096b571cb39ba17ecd7723660b31ff697915de4749af587b7e890e9bff1e049d91734fe1819c78dc2414cd6af3e5140ae57022697a93171be164a94da95fc7d2f24d4ffc7e44e28de5e20149dc6dfb1140c6e10d0feba5e87a827723319dbb22fbd38f394adeb064cbe0759cf3818131deb3d97d75130938a7cadedaa8ebb36e22417b023269508c63a49f663821a336326c433d0463160069468f44255f7ce2c688f764854421fe00d998d3813603d5863445a0aace85b738efb62b912e4651692ee584ff176afe6908c93b084ae3a404117acfa3c6c45b2f895c5dde7d1852fd08aabc02c6698da9f5775e3f0d40890d11187f96a04", "63656553ac", 0, -874386906, 1537743641, "de3a02d0be1467862168d8e0d5fb5a6bca9d7f68ed326f699f362f17c357a28a"], + ["030000807082c403029fff46d7af8fbbe250c1196bd9353d9f88cb714194b9e57509b0eddaaf45e8eb00000000066a6553ac65632e7d3e836630c41827647b04196a69abcb11b8e4d3801f04eae89b7d17672fcc7ae14bf00100000000ffffffff043d67d105000000000852ac6365516a6a5178ce7504000000000452655152aa1e6d0300000000080000ac0065520051c884000200000000026363000000002a6e110b00", "52", 0, -1183916109, 0, "57b50eac61a80809f8a06d855fdc9581633e4cf86ea6a3589e45999a98bc8a1f"], + ["030000807082c403027608d7e0cfe502aeac644b07eae2c5d90885195967c419f6bd59f6af076d742d02000000026352ffffffff44192d67b4a06d7aa66d7c07ed266f9eb6228235cf6b839cb4e69f9e2e9a119d0300000000ffffffff0464459c03000000000852acac6565ac51652bfd400200000000070051536a510000daf4f50000000000055200516a51d56e4b0000000000056a6a52ac52000000000000000002ecd99c05000000000000000000000000547f3129690e9db4e3f62001b238d1f9a04bc14c7aa70dd3de31e45ba7ba78ef46f60d3d9f279c18f111817a85da5a952d20158151c0d32b581c0c15b1386393bf74f4ec4c51a87913e6e99a7f94e00b9e75c7636179b0caf3274d791ecfec5c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a677172bc81bf498c4d916c9bcaa6ec3a57864d2bf8ad6c351c6a81318aa40881ce8159be35bdae6a372867ea34b312e461876c5940e0c9518e7c89529a1a4dcc422c68815534547fcc0e5c0b244bd9d00cfed2c5f28c5439d07ea682858942379b1d307a854bbe20a7f4a0214755efbf02501e2c4c9a17548de0b1c05d7be360316c462c0c088028bc5a6c972d7dade1632b66f61efff9a7413ccf6604868616f031d5b9b133a1c861252e1454e370a17a6226c1fe64842acb785c9d9eb59a4897d0a051b16ed3e34838759b8408a8c66584ed736daf4601b16ae1f1eee1c387c47a526d24516f41c0d98f70cad434a01f1c181f10571a66968aaf55dc40aa7c6170b02020e493aadb178b96896d0012474b1d49a2ab9b4697884098ffe2b156db8a503021c0644fb9b93f6d2731b772c58a347d63c383253196a24222abf0189620ad22f0313152c5cbccee68d4d282247ac07edb868b985b4771119cf702ee31d9964a2e40201e0545ec644bb708800649a4d9a6311b7ad0950cf00ccd58b5431c6cf4c8e4a021795270e097d4789b7a3d132991759263092f5b2848dec63ccdfd481773770c349460704eda4bf29869c22110afa5bafd434642eebf707fbea8d2dbe57c9377df0682bc72d8e43c9353c498fdf2410c1e01c53675340dc8a461adddf66d16f1b7a0c9df4658a0f9fb66769b896009582f0d1b99784735f5e6176646fe144154b87a05e6d8fd53ab773e794af10e347c3e7f48710be17227505b8b38b5abf154ba60ea588522107584dc371c6e1aa0b7ee0ab3ae43f01eb63d350063870648ef99d8ba3e5e1d008c77ea93dcebdadcc740ce72339ebfe313e8a587a28eb0fe35ef7120f2c73c98ac5bbb16b44fe813c4eaac0a16910ceb806fb7da3772e95dba2fdda168538258cd00c5bfca742695bbb310fd74918fff7920cc116dd91b01edda50e619300c99d63db66f791d800bdb4dad0eb7bd7348f9baffea3386d132e158e24354ddabba685c808dc7716b11f79a0c00836280450d7d4c838b50e296a309cf79123885d2b91865f5d40325e0341402245c0a577346c5d8f51e6b54c6f0eef67ca026ef35952e5c07038e1705462bb230bc124e19d6bfa75630361355b9f088111cc2df309a53dbc8958e8ee1b3cc4d662061b552d7e1d4525dd39c3595c92d5a9e923a6d919463d7fa018ef8062a1d0e9341019796a3efec36e4fefc7cb0c1dca5632d887a81136c6d24d2c0ca0e2a1df61798a3428ebf2976c0a30c16c1bbe9b048432061743c1271eb4935ee73c5c0d21d358b096d9cc05516ae865b20c8cf66adebe9627121dd74c3d5c9af6d046d83708458d580077371710ae406d4f790af7aaf853133f205317266affa92f403c6ab35af50a4330499ffa784d62ef46b35912261bfe5e9b6e56a390c6317e5e5df2d388eefefc2d89e7ea31e70f6f3e1baac68a4bf72c8a7e64d88c2ac36881108155127fced50790816a2ff3fe41a26ca6fddabba991bbb426b8b67e17a6c7660f91569b126acfbf61ccb24647a5a1e0fe1442774f90a9069aef1e3fc647ccf1a86619da9b3dd27a052b8053f5dd8e297878008a301c6fe9088557413181de18574d8fe159f9be4a3fbbcfee638243ca57784f5b16a9e4874afd582ea10379e202eccd73ae168cf87a640b70c3d94ddd2d3f648369010dcb7ba0e1a81d5dc0652dcda05b8f3b52114775f3ecaed7f5d5139cc5431b41a3205400e875c8315b10ceba7193f86ac4cdcd3531a59fbc1f48dfafa4b0f0d1d6c1244ccb48f48d9a9af9bd3d3433d2758165d68353ac76c090a757888ab3a61af37a295664070c34a01a1121ffda3685e581b3a8693ab754bfd165d20bab2f4e4007bc4215dba8dfbcc6d4e75a828c3c724172d655facd9bc6401ce603262c8631defaa06e44cf9e61d072c9ae3eb81f1ab637198c8ba20da5dc4ceffc925d200fc57ec1aa3036b31b7047c58091c5f58ec60f86795b951b840fca294908cd954df049c6c295ef476fe8d9a71bc8825b92bb23d1d7090b2453d3354928dfb8d9c007b20cf07fdaad79ee7e22bd0822348f4956db6c7ad872a6bfa6008a7814a99f5606709556cde879ef57f1e5badd5925c44dcda879f177f310381fd0e19a91c620aed6f15015924162f742691bbe48648c7da014acf2eae548599987435858ee50dd04834412724d5a7a5473d812dde4fc380c9722d1ab4f9b8e729b668213efd9feb5812077954ca98f0082d7d5f180bcd2879e02ce9053331e863462d738c305b65bb8b7464a0000000000000000484c61000000000064e62aa5d9915ec031271c92770216c3cb9d8e91f0b0389d3c015d88800023f3bbc6a3d6ab36c5c51f27e5dc5cf19127bc9ecd32e4bfca4492c0c5c98044822e96db2cd749990c452b47f768a404c11874450f5f1a57eb6d6bb05d9d39a825b3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a899cfaf63edc1cb7eec126aa9aab2cd57ab86dee06ed31f67b059e076e10925486173913a488d8c076061a11031feffd89016938f8268a09869bc24671aecab2264c4e8d5c1663717b250737965fcb7ed2cfd0547f6595d054fd66119cd88fa0f81cfccd216abf11f0a528784ba14ebe9085f512d1a91632737c27745c8247031ebfe8d2495ae1815357a887ade78d80c64e84395b55597f6fa05fed1e1f32f4020042f53d3d3a52770ae1861ffa06c8f6f2e22407ce5d954969a6c8c6b5b825b90b06ea395a444ff7b6984ae4a396e33ee86b63761d67eda0e43f945c5c65cd8371778d84ac0a91517e38cab043155043f0c27d3a3a8b6a666bd8a8881cf7dcb06b021ef7b03fa1b83af17d94688df8bf8c42ce611c0fc62f0b214541c9c6bc4540290315ae9a91c5087680e56051e43928a2ab7a0dbb62ea5a0349725ba1f170fafef5031776a5fe344ee52622902c4969daf12c89316412f1254dac88c99c8315bbb8b10214a721b5fe93615ae6e1126fa108a28bc77edf065414abb5e3a3fc00ad7fd32a032a11819f35a39afc853ada688c76acd091514720361e10a79244064b1ce8af20691d3ea21fa58a82b75503d9f2d6d639bf94a932d13ce7c548bb34e330aa714d6199aaf9d7cfd81767366e16693f46ad09aa60b2c3692cf3b361a119a55e41b075f5e64aa888a729d72bf47cead33fdf802ec75bf59ea986bd81118ad5409f6d4023b3526ece30ce5cc97ea128c2ab7819aef5c548439c9a3e1e7dd7fe4ba116cdeb44b2d1e0500289117f30c40be6eb9e3443e4fd3e201ace0a7be9a2345ccf6494782374fb374b52f78172bb927bcc152a19e6377964de13ad876e12f5eb64071bf059dbadca56315b32755622b701843889bece6611252d179c401c6fbbd9d1110a2e582ea3e5af2f34a35f8373e3645ac80cc736fa06171ef2e3e5f2afbbf1e565aeaf66e72816ca529fe0ecaaecff53b20e806e9cc3e6f265ba2814fbe5a4b9c14d3afbaa886c3387c35e77a2cff5263eee087b547a5ea6610cc6e7cb346be3b30f8fee7e63de8b7d7e49fb32c5b9df7eeec57e215116101110e0c6b522bfb38ca699b25b310728419edff9a475c226f595933b5cf1a17ec21957a9c6b99d538da2f65dba24a98c0436b4b687802211a8cfc3c4525727637a5f590bc65e3909c80f0135b00d0f4ab4c7fb9d66f35e18af91637568b0956bdf511dc4104920489cf4fc425998f5dec1eda0ecb533a042f524976661adb4def84cc0b5b84f5d732886b3342f230bb8cb6074c56002691c0aec3d992f331b4ce193ebdc799437bdd6ddf033579f01a10aa9015ee9d14dc84a1e15b76feb040c980eb27a0e7a8385719c65fc591493cf03064c3a6e188649daf9890497fb7d3d620e601441b7ad8cc25ed8cd3ec1fa466e08421fdc86af6312f9ca1626489ee99ca3d120a1eaaa864d94eb2c7f5c2c22b46aa8497d552ee73356ad2a663576606f01ee0002a979a5132a266058e95f961f8a0bfbfbf718dc9db7b9823ade5d97b6a55f9f79499c40ce0e75121e8c22f2899623079aa8febbf346b35dde3112ce15a2ec2aaeae04bd39ca6e2c685932494d91d694bf4104810183ba3b8ecb706aa12f8bee109f8d7a48111ba8f3d86bc289d4b5dd5e7ed69a1216ad48ce759ddfc538c12ceeae8d5e7c13676ed012f233636e532570879f0b5645cb415eb0716bf450e08ffec856bb3e0c41b7a57b2f6fc995e54fe8375ad6399fa7677e31611af27c090e987fd6a58eefb250ae17c4bff9187131f8cd19abd6699f7d57fc3d105248ec30c59ccb89a08d7db15f95abb2a7cd4ecff51d7ff71478e3c377311e500d529d58f81633b3c6251e943b21c6cbda463019ca6e467581cda08c6b7a219969067010b1cd826ccc5120560ac405c3a85b92a5dab1d27383e2cd9ff858e58c8b11d45b19fde20115c0359e33df1b1018e6bd332e810e7bfffdd2fb73d3a7f500505de9828dec26efeed41d1c771bd142c105a514faa5424f6e7dee0987d0dfe7d2e85cd8d0ccca608c7dfbc5fd55e3b2e6179f2e0f7bfe97609b803e8cb3d10a0c85d28eb9149502aa7411d7a9af3c6818452f303f3e2c686c3e51ed3adafdcf0662d73a9181a6966a724fb5693851c0c4e50538a5db57cd4ecf61dc147fe6939f17356b2426b56ea2840018a48b6390cff4616cb07306c398422415181644c129b09ea5c6c892387e5f55528ff46c062a8f6cf7b6c735385239545b2075c90e34205a7cb2ca2641a0fc91199ea686b81a0a6465a8014512b1c432761e68fbd83a561218d3adede892cad82ab5834a3c6d6cdb34d5f4fb6a572be9c655047e4966b544b38fa53bc3a3eaa280df7f99fb1a8193a8fa369189589b1b643990c1fe896ec167961ae2155b2db58541f913905ddeb10d06a406", "526a52ac656a516363", 1, 672853410, 0, "1174411d4ae04d2632fa36a43a98429a761661bdf4b8e3553327f40285aa0aaa"], + ["02f505480318db37bbb1be98996af503917b3c9a3ba6c956d967bb12efe6e8c531fa45f4ce0000000000b8467d5482bb87e07a9f02e536fe594456ad74009b4828e4c620d81f4433713e571c394601000000055352acac63ffffffff2ee04143042fafe9aeb6cc20b2c221065382597a833d1f1a3dcc900cdbb001440300000003acac51ffffffff04a039e90300000000025151b619480200000000045200520053df990400000000035100ac1d11ed0100000000045300005200000000030000000000000000d6ab670400000000016969c0982d40594086a02774e58508714b797e56ce736a1196b3a4c44f9135899289dcef696c7eb1553bf2cd4f5d0a67e02ae5b74bc07d88438f570990c516042d4859f4acdd65c72892c3710fee4df5262f243de7da045b57dd1dd484d08c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000304f03894ef352d27c499bcff74fd0042c32aeba94e996c5d53aad697f8f86f3c5d6a78e0b978375872259a365ccb6af701ca8cbdaea120da9a16db8619363784a778dca9fa74164ccb9300dcb653b1b32bfc92a6f29db9731429db0ef4cd16932c7190db71c81986338f98060ab54c64fef88c99c4b4c677dc6818386f5dcb902144ffa56014ce120dd5c876d31d184cb62d08c5123b86c24ee9748300667881603295b5ea1afcd6c241176e17d2c96d8e29e00df645cf802a96e19140b739d25410b014c420f2ef4f73cfc5c89737ee9c224e8336b512382841ac0a6a98c5503b97db1232a27de9c94f0c89671c60c455b4c027a196f564d418dc92f037acf71922c0216548cb539be50bc3f07ed86133d1aabc1daab3a69fd37227986ea841c36bd11020360ff241a33e97871e7685815e21feb194a631e266b0d391fb645710062e303030bd393c567ca7d57c1932d3802fa199c81f65351bf16bd16c4fd194b095a3efa022c7a64a78500cdac7c88cb459f12cd26f4edb2afae750894aa7938a5e3ff55c103142552b4053c123ae9f3531c694693a7a3128af125ec91b4bcf4c5130f342a7264425708a29e8cd0db0e35234377b7a588783797ac4178c9ff6210b3bde2e7f5c0570387214920402a5e58624108c3358ea0ae14b97b016d450e217e1fc3ab817f460ef9a15c2a1b84618983aa61d3933b8bf3f65828901a83e4c0ee311284ef57ed4ca56418883188ab0b144a439ff0d1de763228bc5538276765c567f33227600fbbd988a9d433c4fe6576cb874b6701755926e122fac17fb01bf36068acb13a8e4c3c2b81c8cae591892338100f4f82384d905c3796c42ef96a708afab2566683816b3f1cdbafe5fec71002457501e03bd59886f9f222eb6b1c2b158952668ff3973eca598169544952db129d651852946eaf0c94aba0874c682145848ea6932deefd38cd46a9db7202cd97c25eb4d19205225cce7d6acc0288c74597238e079e366b771a90a0c0d7944eddef2da0176747ac7f7f542f600ab59a9ba02b5c9917c7abe96534b5fa6e159ede5630eb539293296520bae6aaff2e76f532549e9b74865590e82819dd1782bf8b910a26495382a4d8292a985bf947364069cc2ddeab023452288c22aee0688c641f00357643c3bf4c61304072ec42bd8c69a9d977015e6116723bd0ac8a9154cd16580bf072a32b3b022cc9e87b122389975e22202977baf9cf3e0632e0e614e992e04f6b7458f6cef1c7c72e2a55c7e42b5a76d29a38794cda2737a6bf23f2d4b5eefb767b5e06b65c3c2fb62bb7ff43b573129e6ddfa7bc9b8042d76f738891f835b6f8bd48b2fda5809844d0f323889cb81c8d64b0535945253651efcbd98e63a6cb3433cef803f80ea7ac2f23a8bf80008cab08ca2b665f88352840005e48c46d380603c0c14fd76a3ba70430fe33f9b77da9d443b17ef4c98fdfc5b63474e06267adbf8fffb638ae150224873ecc2f79074dc0747ce31e8a21aa0908ec4d90286a0a2f29eec0eeff135e11079302ffde254b1d4924e8cc04f5c4911d529dd8d7d17e7edbe15baa901f48e83308f9b1ca3a81e724f70d8bd173f8943820b189d8e178f7949e447d9b7ab8547399caef42f824746ef07e0f9d760f4bd632fc014df035cc607857842cd49dd0b9b0f5ef89b931f4ff3d6c0793e967c63d6614248d2003b73ed5ff0099eb269dbd606c3c2bf2bd24652db7067414e81539bf984facc02a34017ab9b5f583afe8e8663707e047dbfc05fc5e48606f6f1413d24dc197bbcec9582e4209105436eb4a87da466974dd7f5458b5c882eca29aff2115f977fc084ec4658c4fa69cc83b4169e704951c09e75cc8aa78a68532c6512365877fbcdebdcba56d2df4b558c796acb15dc6de8ad4db50e7bc1e6a2ae2f7b15c9a6d8359bed6e30ea705bf514504c83cc4b4aeeabc7aa8dc1e837d681a2fe32c00ac4b994e6ad8f823c47840d5c98cc561ba7d18d9e40621475db8243153269cfcafa03ec083a6ce6adf492133a3360f92c89de0d5166d152bd2df9392bcd16b67c360e5fec716189a8f4c71a644e0432b26be3798338f00aec35e6117769e2f240a27da6e8da8f43b90d75cd6bd14ca6720afc285a2fe4b4ad39336a13fa0483ad59f79501c18d34fe9b58c4d35989d3fc6d1ec941c2c772c4a66587c26326d65e27a75e640f274e28443ee0e51e244d71b3ffdf119004495e158503026be5289534e6567320fec980a817d8c5d2b9832f14eb652c7d9fc6fe5260df5090ecf74c0e0306d1ed13b040000000000000000000000005c4dc5172a739a3d2c35e6a063f624202ffdc161cd07922477b4e15d17c868f30955086ded36f46f6635332c2622f41199ea830aa43eda8d0b37567e0bde88cfb7150edacc51244963118142e682f1e23d56bddc1ae1fb0c041e61c853e0be6800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000abeda43e3d36977437be6057a862695f8abfba0033456fa9ad5e8db033db363d7972c3442161631f9129c3f9d611890710d0e05caeedcbdef387f1c92d55b0c375335d4b8219c0a98134927441a808688cf337a466cfd189ed52846a868cbc7dac53635a0d9e3b0c9d9a174362a86b62f86ec2b37548d862db1a6bb14b43255203172d7d343a7fea9b7b3671b3341e515e1674b259e71b6c09a4fce259c77405cd031c328ccc0419bc04cf5dc845bc57e8bd511365a1b009393a741bfb29d5bd493b0b02554222e45384040b842f2cdd3dc3dcfa1e1c7c609698489b1628a725b70e01d0ea679199414a8ba4ad2aa71797479089805f6aba65db72d0490da672435490030c66cebb6e4e0c31caed288af8313c5a0ca783dafac9a817c4b2383bfb97eded032be6c82137c33a984983f5d85e4d2e0a9d60e40083d43512b0fe5f087f92482f0322efa9de1e3069dc927cb69d582c9d291da913f6fc4911e03bbbbf0c2ddabfa202237c55a86dc6d39e4e648c9b1f0bee51580755bdfb48fee4b5e51c03e13a4d450213dc1f9667935bcce6cfabb0e3f3e67955c62fb85e9b6f6cb5bdb34883ebfe2c02e36523fee1840472c50e7f984a770766f91b3c0d1fc76f161e312e7fe56f573bd266fb33c8c1a157eba9403f07f262c31a5e3a4e92764770ea33009cabe981f589407e722084ac70505eabe0f08a7ab2c17d2e47189f7f8775c015471a58d74b775f0dd79f70c05a27932685e29cb8293f0042ce19834882be18111a9c0390b378c533a28ad169552dfcf85464e1431996ed9000ead87997d5469e584f7d24f7f2dd962691e8a7f088710effed2f958ae8373526698879087ed12db11f2cf86e43c35fff2553c6fb49f05f567f97f9a76b4f50cd9704bf9a2e6da3b15a389d093b91722c12bd35b0620150df064c1e81fe9d4d50215f76303631c62f9db429a65c2dcdbdc01fb646796be00e2576e1d867c6c0f49ee4d2085b45990dc17716848e5135a27900f6af09130cdb7149352f5a0a05076599c9cdf3ed5399a2b8e814e15a014007babb638497c21805d2ddc34ba28cb29ddf12b51fec5ee09622ed055f03c147249ebe2c5c19e064cb1102b35e6fc5c52dd778ab85dc42abc416eae33fafc43b01a992ddae2fcbd6cf3f3b1b66b0f6893c85f1a397d946a572a9ad2d2442db9dbc806d02502f255a43be7895f77cb08e894af5fa81573423585bda3d648459074d12908eaea9a73229b55e53b1603f6adfdccee5e9b1b7afef549d5fed83bc7f553fb4258581f15b483f2a944d8a891635c02ec604c0aea33d002aca6d8d57e52211f4e070f1653a8e3960573d4f5d92be65637dd9a44dba5615b78203d2351c005d04d3dcebd5459e91226af24d28b526120a24f78a492174eb98e1fa66131a5f6d3225c8d4f0e071badd1e2f6b9c17c74445c3ae9799a8d60d0a17ef05de4c7d4ee7525ca93421d249d6913086427ed2cf70deeeedba1937ef4a88a32339923ab0b120734dbf5c70638b41023250df870e333e129f57c57a243c86bc8b8b5d62727ce7e4f802e1a3ebb5482d6c5c14f2cffd919b47e352322b189ba6b73f2bde9ec5db7abb8b0fb129100ae12fa3e65e3a49a1123a83954297e2ff6df5db874d8fbc1ffe9d9c7a1e78995f2ac5e2d89a87dce7533c935fc9e9294760a84cb586217474ab1f02fc62040854743670eece6332d6f87c83a35036ba35f0fabb6afc7c6f91a3a2e69c4ccf0735eba3e7ed4de2b76e3f9bb0ae17782c3afc1e48b47252387ad1a6edb7160f643cf6e76732dcdffdb3c5d36f6a4f02169c81df14fd23f35b9a778e5e81b5b85894d67bbf30f942736f437d4e9162b07764ce4b2775b5d74be1135bd91b93de1781c521a45b16f5de2de84e44de3d2f4b55f23ee96727a341ba641aaa409fd45b4a5a531ee4af1af8ca1539be29231e32cb6d5b0dc4d52c0b648bd61a58c41e35a6650a8efcb85c2c8ee5c2838d68462caf1532f64be3adfe186fc5bd6beb906652a3eb9e4b00fc6270e9cd337355f1682ea9676b4b521b754fba571f5912b849c4970e1bef766d1ef7b40ae48c220d1653088dc2977c973a98d8ad8b71c1b8503e4e09c6cf6b31b9d4ff56a19e349828cb1d1a97710c8e299d692df0de15220b042f244412dd25412b8b3277beeba04709fceb577c616a397c9efa367ac2f38427964c670a2835c16f6309028d968fbaa3c336802e571acd101d3064a5e2247adc0e434d80e84133f7fe18ccc210accba6d0d20b0e6bf77c4d152d47070f08c72271e56252050000000000000000000000005a2d4eaf11e51783d9d6a1479f6536c0f2773e0405f8eb49f9f72888149f357a49821942748fa0c863d03d10d08abe7e99ac66d7208ed3a8861b3cd1856cfed216a04d14fc61c44b3d01f13479ea5085ea46778be3ca06a8db118fc15b10716b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000217c348823b999be8fa94457dc6cf31a1344318f4e77dffc3bbce2f7e859b46ad99c07d0eda58138c348ede6a60be407ee32d72fd3269214724a036440d010d07c4b7929e5b0d011696fb8a3ff88aa725d51a532d3e9ec116c709186e361e605043f87fc909682095c088bf149c2781dce5d901bd97ef68f4574024156f4d67d021772194674ba9157f2150b51136217603707ffbacb7d4f2bac3778e3ede24aca0307b45847b9c804b5b70a67a50867096b517a36263009739c005bb7bf180cf2990a03d3b3a988d42f932e760284b94ef4f38eeb186fb391e711fb5279546fa8331e256dec68a937e879c7cbcafff51dbfd37adfc266a5e922b94b06747fcbe073ff032583d9b0fe3a1f53fab39a2d462aebd388aadb41ddb88d100dc088d03a5c6c990201bd77a34e0eb639c35a6da4733aa4f5a05edf2c03d874825edce2f0f7388716020135f370c19a48603a8a43107d95ce59441b65b3e7cb1a07676ced3c95c7bb5e0328cbdb753f3685527154a806156e358dc60f26d77015f192443c96e0863079c20316adde491dde3eb49592814d33ba10a9c7229ee0c2c2773f030e342a8c22069d3adfbd02fc8c207c7aa7dc2f65dbacdf72e0df41f38a621deceb4539cedfa8868ecc4126e9179e377ca8552c7bb1cb62c2deb55ef72240161e2e7a6b95b1a581cee11a8f77574d45a4ecc8b4fb1ce5c1c0386ee8a61e916dd357cbe98e018b688c1bb028a5e5cff3f4d50dc78fc1ffb2b245b1633291b349791e628e5fb1d5d276434e5447e85e6cbf92735e06b9abc28041330d6c9352eb76b07bb3ade60e8ecc71bc08c80d1ca2419e723d0e273c9f84a89a92041ec083d9bc34b56d65264acdcab6239b94144900c6dbf35e9871de3e5fbe4e9d4b7a4213687c0933b62eb8fd6d728f604580ff3bb02de36aa31e98fca8ef8e7efbf1883ac2f28dfd305fb2d3d21118809b1bdb6b7350019ebd3d52b0782c212e84a7e4a640ac53031e057b01ae871b06f788ec15ebf049ab18d4d19b60c603d06fc50326bf325f08d48b32d74449204f8fcbaa1736040841133f04c04f0eb1b8cbc294b99d6d2af09476b92e1320a1b22031fe09bca70b4253de5057dcf84505ae4354400d0f72090f546405e6290ceb5b0e793554a41e2fd551dc6c5400ce3afd8e30cf9592d016ba1b8d96d88853f3bd42c3f768cd40a261d1b077d9e65cca09369305357eeacda429bbc0b0a530ed2c13a99a51049d95ad832d03c509d5e921a17cbe0ebaab4d2a593d8403a456806357560033c47fe11ebe2632b2e263782ceb38b83e47ebbe04a0d08f00910bd874c372dc654d4417c2c9acb9e90479d484173a29d6d472e69390bb4e93ca9733ca6ab891840f511e0a9cc38a467962faad77069e616480807674b5d8c486d1bc4c18a42e74eac86920c960b17f26766bb2356f063575c99fe3a0a900525c0b3ac3916ddf22c8f7dcc4075cb36e55f862557f0ba2012b701cafc5a3b075e96190be1be23c48651a03c651609875020ecc82c3b5fbecbcd544a2e1dbd98b79727c64ac0047676369e936faa8489a35d5e4c4b45e45b36e508aa041f0b57c1d10a4b3e0eea0d70e3f7103bdc6063af5a6e7aff6d50d5999029dbff5be23e3f55e1437e8654a818fdcc6f91e6ff3e0ba5638c60383aa4946e2ebbe4e52f90a487cf01c8a18d137c3e5b3b9653572e25fb723aba435ea5b03190a9993816678b03a5aec28528fc341e52ab5a4aaf6ccb998a1586d74bca8841accbd160da0be94f23ebe21fb15dce0011b8ad64f37139c26f8504409cbbf1a417e7c409033f95741ddacb1f5f11606395ff8abe72036f55167938cd9e26aaf19355a802112a09247ffa873b6252c69bda5ef57823d2baac530389edbdf03098cbbd1b33ead46efe24503eca198d0d8a8b7ec9f25d100755dc68c58a5478d5c3e43b981a88c33b566d2c8abc6019afa7ffb10424e41caaab8ecab7030bf4798555fcc6014e07804902525922363c4749942d45df6ebc3b8411e0c67e742afc1af6616bf5ce9139a21633c57b5159130bf798081ba88b84d73b390c90d8150c9c869f930d7035a7c1945651a2ebe86c3a18d78a5c2385eaf5c475267a06c6d9365529fb266cb2a63dbec2f47bd80ee3ae7ab797839be596b28680303420ae189a44ad711c484c9abaa4211c852d5aa0a126274a8c092d5f505cebd822094a2d2b15e9af6fe3b1aedce2578cb82a8b50a1fce601788f34e4c320708579fe9690704b4a2aac08ba7e85f98994583672a9069f5d2b68ea9ba41c8c4293be45076ec4026f8feafe3ab61708da420b696b196a03a5a6d7dd99d2b0b9e7c45db139f2e277f26dde1597f79e41481aaa2b4bb9b5b298afb58237fd298a2eb0db0149d28d3b040b7a3d01224dea4742676a0ba436530c8ceff5c05", "6363006565ac53ac52", 1, 2002522136, 1537743641, "792d2706d74c9007236a7bb472e9af632e9a7d587515c97d7c1dcdd7c35b5315"], + ["e2e50e730288517bc8989cbea5e67021233952efcd8e65f532aef0cb8f8dbc495433c24d5a010000000452515265ffffffffb1f62fd41113d825e96e02d80845fa3ffda2e9daa0c3fc93d1730c3e653bd9050100000002656afcf2652003c9f6140200000000046aac536a68e141050000000003acac5171383f030000000009526a6500656a536300b201668a01510fcd04000000000000000000000000bbe4a4cbca92bf76dad10a1ec611366150b327e931c3f232fcb526b743c224be0c0230d81b4e35c48ebc3ba571714e03d5fa42bca8338eecc158e1a0f5d620ca47863287475a75c931ff46d47d70cd218bb41ed00e7ad4a130328525bd6a4a10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006ee3793c6114e2b10b9a328002f8ef0d7db58a7e40b37123f1c7ba5348c717ad4c31706bdb66f053e2451cd55aa592242105582a3692a4df027a0fc5075ebcf5ff252d972805f5b15821a55498f8a67b8fca5dbca5d36f1b877d7bad613445badacfad7060846b715c6272a38f7edcfed0c96ff01161f65b00cdb17aae32ff2402006615abc08b284ccd85347958b807ba9f386be745e3028ab9cf7d227db224080327b4f3747d20a7ac811aecdf7d8cb7091b9a4f67f72f64eab4e41348238f585b0a06eb7c8c552cdcb6bb9ae64e30f8ba3c7da6a5f0aee38c18bd2c374590b6141d8c1dc86f8a09d3ff8dec7b2e923ff62767ce00a80e2331c0536315c431738a0d0316796e3f1b4e80bc7d5c4f33592f3d918924e14394aab292456659974d52e5c8022e0aaebcc11ffa9a1a62c0631e77000cd717b234a9820cf782275049ad9783eb031891689f18850ecf320aa423f4a22bfc5e19d449eda2363f95efd16f41529bc80316638c3333f081ac1d48b1ba79185e3871e5b268783a95855481ae0b7d8c4b6a022fa44d7bf60bed73f01b144e057643603c4e347c27db0558be01d4716025d5809b1903c275a8899bb1329aab4a914c3ed634899222c153f3ccc9094315e39813785860998d60bd4a689a2a2e8487a5e7d82f9c382c8712496cf29f4dbd640b6288c6eeae806dd7f6b5229d3dccf265a5c338ffbe6d9858d3f8d2587a4c7cc78d47ecb919a37f30d51c19568460f50adf24a6316906d147765d1a11a84da234890f37a989a1697cc59f97a334cd1313cdcb443f71cd8824a3df607d56ff2a7262c321418060d5b99e19e6859150531d8f8549d364c6e86b60e6e8b7e6bde0c3ce54ce4012f26a8567d7168fb9811ab23fee14b9a13e47f57e7bab107ec4c2f62ac96e57f7ec5e9f3ee32c05090fd1b641995c422625abdbf562558f3cf934d7a9381817ddf25492e01e7e05fd99ea6700165f63edb0ce6c94badf9a4bcacd888ae6e887342e4c54a3de521fe9a80ae04abfb7a1cbd7b2812086231972fb3978a8989add3e772bf936f6d1d10bb8b834f4be0dd4416598e6b0e40019c0a759d37d4d7b30924fa4606c7a5c74a594007a0f71cdf2e8b2a282b60206aceee313994eba4892031677f598b4481490d930205fd7cfa8c7189bec1dd2f4e13ddf33bb78d4b332ef85ad0dad02c3f691b406192754a95c50c7be752a1d54a0d9833585f50126f72fa3d47ca4d471f7f267f381fa144a10ecdd51a3fc222318619f587e8c48b383061157b4a0040c9fe6b2f865a567dca3cb99dd3d2360cfe10c72f1f3f0667161d3e0f8c1f9a22c87c2dce3163ae18eb5679fe49b4e812af88871675c1b47f09d451210574b250b64720d37b09d86504d1350e42f72b3e02679a01e6562f990dac81afeb1fdcc91ab3b5965f7d2545c85e7d1932045ac576001877f830e63725862128f47619065edc13c86a395ff9079115d9f1c4126628801ae9f37b451785da16d2009ccb20fbee49c6d2fd58a9a5425c8323314c72838b9989d56164aca1f1b5201ce71cb70719d89e11e7061276d46bae83aa913c6fd2b3958b7093e6a409875dfacf3246263f6a1f94db0edeb8c4613adfcc14d26ff7ffde128f7a10d8c5cea729e4affe5bc48a55fe0c79ee91db60d2421b07cbebde00894e3b05229a835003dfbbe5c619d369ab250d0f8568626f869a77fc708ed7486aeccf93a8aad0e48cb0deb893e08464e33fa8315ee9c09cbf2dccfd88ce7430a795054a70785a3f8d9d96c80fc8a5d04721c039a982f12fa07c42c873ba0299d74f6600dc1a47b9703a66616bf190a10a6ddbd2dacc82c1822ba9ee71fad5b856627a8affd828a06795ecd7b2dfd8101d3c8145a4e2e047bc096c0a612345573e32cca3a83c676e2c32aee7aded4b3d193fa6e6d3dea74d3a6b168cf91eda32eae07871d66efd1afb27dca7317c5f62fd4d200936e5dafb58eba86852e443d02989746be3120ff981db89bbfcb1ea205a1978b7d2e9e85b554334c8c1ba28b279f83fecd291da2af49900fbd82326c40ea6116d1b8c441dab1f19ada290f5d4957605939073d5a4bf5a138341e32d6971ba3bb0526133c362e50e4061e6ba61cc10faec7dac73115581c9619a96264f35f40db0b4ef05f43a33238683677f55d6bd375e9d1276a61cb77e4304176f8917e688c85868292c09f2c3bc3912284fd4e39c1f364b4cdad084989002b2d587314c8a520ab8b2898bae1055fa822be98068437e046d5f6c84ab31a1bd032a2fe0f010f51b23bd14cc49496f7f3f8682b3e32629ede21bebadb3b0597a613096426a71217b7632e4f7e3396f5a2b6c43b94b7b3f730a0fa4e9570eef4f85eb00d96e2eb7762ec540c1ed142cd6049dd46e104dfc64a2e6689de2548738824a13b23bbafa90d", "65656a53ac00", 1, 71011260, 0, "465a4d075a407914749c32e50ff9b13dc7c3720f17f7166ef1655ae1d04b67c1"], + ["030000807082c40301bfb3e19411ed89422fae3aa19f4bcde3e6e66a65fe6bba2c250768bfb6dcf90602000000026563ffffffff028e050701000000000751516a536a65658597e5050000000008535300ac65655200a53c223600000000030000000000000000847c69040000000045d4e58594a9e17af1d93f5155de89121a6ebf55980feb33a0a4e340bfbcbb3a46f35bb37852a32c4453bb9f3bf3d160fac3292eb0f1ec5d8f3c4ce7d20c136ed951825799c146b1301035461a38a29d64cbf37236863831808901b6b6cb4d7e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dfbe315d4e822ac2d78667d73d9cb2d71b49f3aaf9833d580543bdef0e4305027884a5179bce1868a13f3bcb0f4c389a9f8c87badc650def6c70fc3e515dcc813aa040e33923a2243426b630b3c88ad78218419d3b97c3b9bc7be05ed3dd4c192ebf5f1b58bf866e72c6db6416dc2329cbb82c0f877de9695fbe24470fa2dcd0022e4da3780e37b1db6ce679b6a7283663f8b835ee7925ad6a437c5eec85c01879022b20a19f3d7614af878d616accd0c65f61308cfb2dd381fe51439d25dec000890a07652215cd22079466fdb231a2c64453b5e63b8defe617787b39f8b3df6f65f5a30536416b21b66510a003d36dbdb6c49a41889a16e969c02a1c76b2e7af4922021e9885bc5ee054b3851cf704340ccf1de8ecbca601bde0f88606b8a324df6f120204bbfdb7432692845e8afe43a1c21f5c6ee5434aef69e0f204b0b3bf50cdbbba031559398ee6852bf9540e7de9052dfd1965c0b60b48cadaef7a3f3cbbdb24b015020c84fae2f898346b252866ffd825954f8be7bccd6c6846d72143f1ad90971382030adf3dab321d586ae556d30d6e44de188a88d5a3c6f456f449e0c9e8f7daa4817dbe2b348ed7091d0f4d6585c9b97ebda36156f3ff801c96c316c1b5706cf967acc64aeae8f297785217995e76daa0ba1a1af90b38f5b68964b2149c6cbd668bb57daabed20775da5b11cdfe7060e72778e1a86ed94d8a4c1261eea974922baacdd67bdae946b5b78652e68427a7f8b0217dac0252ef2d868f4baabc97d7b49dcf4258bb6fde017a79f1f4c9d2dc972f1fc5dcbe77bf803f8cedddc5ed23e8f595c5ed226dd55405433ef6975d7e67f74c24decbc7adb58c43fe6031000e0f413dccb6138673653c0e1e26fd80a18beb2987c2e4956d02fea945da11928f12adef9ecc53ade3cb2b8367320e94aebdab46c65d3b359e9aa308a6976e26d8527ebb7516c8318566bee8bf9e4e1f420305d6988cb1556dd907d28b524304f1896472c403cd2f3b9b1ad423e054bc87e6e1da0756ecdcb6e527b2ed0f12884b1f269109a0d736eee1d1c65419454bc7e68afbf5333ee65cd2839cc3923b4728fba08c1bc764f19009cce234fdcdc3fe7cb2af4a80e46a7bba3a0c69ce407c778347e87437a7b0c63d0903b08cb2f2d1faa0430d49a76983ea7176a8d27f2de809d67ec1efa8db52356a962d41220c38fecb702026ea7f5779eedd0502a828a3784dc519fccc555b98319097894c8e76e5cf51fc99277eeb1fb2c66af709c082d0b5737652ec6582c272f1f817d3afb9fc66fb29414f21383c72ee46ac5a489430f01cd000321e328da14433afb69a2a87ceb9b4c63a74fdd6379dcb874518abfc29ee025a1edf8b59462e9a378c9c7cb315aa7ee1298429f3a237cef169059490202e83045f827ea64293388b81c619019441cc5dd6468302cf246a2af0875e15c79e1cea57f612867fe0997c7aed135e449046ee0325719f11a3311dd3802fa87b48066dde4e365daa54dba641b85d0e60a1c8be29dbb46cdc1835ef555ff169c4a3390e30c68ac73cb2543bb459d26ef2934b58326e5cb147d41cde5e69041bca3c7f5d8e7963e201bddf89112a3266b0f380cf1b6d6e1e65a9be3061efe3f2b6101b78b0eaeefaaaa088dcbffb3618c980382bc85c25fd1472804bb4e6b4ef485956b0f4e5e8fd8b44365b81ac0d97d23a96b7b529ba74b8b29c3082cfebd753b113cc73a5f8d7f87b48eda645df562d967a91aa889d88f87b012589d05d5d7a39bcd73abb5ade2da4238f2a79f193962cdccf4fe711b8b0505f3e7cda86b1c4531ff5099011fcc514dd05b63f4a26955f736a99dc8eb8c1aa08c645cea1ce6ad406e8862ec02868b5df2826b5929e1cb0c75e93efcc69511ae711d00ca929e446c276b18f67319a2533408ec273f7f10b21e2a6623ef9a6b3d252e8adafb490b49f59bc053e02ab6893002fecc10eb65e7c43b23fa7275351deec060a0dbd6e423c261f6d850852ac3b7dbdbdb5da142092828ba03b6faacbffdcf2d92471eaf5ba67ccf4c6096101157fee88db9cc921170dce877e5ceb14318b34bcfcee54663d2efba0b0807925dd69e076ebeca1aa4632ed8b6e8ed1d975a7ee8b396054c0e7bbbe5aca85a62c00858e131de6a3f288b05ca6227c920bc98c920a02ef0a852e4578cc7ee6143148a843ca66f78dc8332e91843bbb869975afab8fb7920a8a849d553d33fcefcd24b4cb636bcfb8e06a0256d556773ec5efc810b2cb3ed90e4ddf691bb2911e46577ce602243bbf572300000000000000003c0fd40500000000f01de062ea7054f03553cf9123d085569a71298ce0c0b9efecc93c4060dd69211c5ed21e20c8e4fda6f1b53dfc313af978954023440336689b612f4935c64f0791eb4f47557a413768cfa5eeb9bf9bcb3c8321b5f7b23b4108e809ae2b083a960000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000068af5f5aab5cf36da90d6837499f81bf61e8fe6a41e6e4f7cb862f830d34b6c3a1225553814a783d04e6b1329ca0fefdb51a0a9de37b4f24d79d486bf5f1f2ac3e16dd9f5b6b589b87dd6363bc6485e55691ca8bc6e48912dcdfe96d42dd29cc6c88c3ffa2b3758286ee4122eef405881f3054860751aa93936d1c579bed9a1e02153a03f7a21608af819b713c8ced0bd62d39c05445bd1470f12698d46570a07c031bf4a1d60ec5027bc0a207de8267a5fa6798e23619971eb6d06ba9a7afbcbe6f0b068b6f4e2f3c67aec605f9e1a4faaa536f119eb67f494cffe8bf48dfa9edb6f02bccecf3b9d3e036c0009d977136e7553b2ed9ede1fdf315e4e9cd65559f2fa6021734a616d21dca5c260f4327b745e5bda069d14db3f3dfc4c074e824a9bd91c8022dda58387aacc27af4413140597c48fb5a286f7296cb4a19f0a0115da1510d57021e0d1b17cbf8c985e0661313dea30ae2ac58a7aa0f4345d711de52303cf6ef9a022dfe2f3dead1b02542b6992c11c01c5ba953369904c6908f1b66198d9802042b021c65cba788177b8c96f8d9b676f0bae0d2d05094edbe28f8d42d49ab5d8d1da409f7baea9230002ba53ebdadd90a304c01eb6aafc48c3f6df2abcb1c34ba915bd1de5861f2afeda34fe92e20c05d76084e4ed84e6dd167a71f77bfc7c4d1f4155c8195c4967f8e619a29c9d42ec87ac2408ad42e244e33efc4e639bde139c73ba109284b79d8a7f9379056e24ad681e5f7ed8fd040707f0ea874afbf100ce6b37faf2c2c8289b6b2ba614d3f8cbed7e54d68d4df10b24ef8424d01f8a0cee12a6446b0882ba12d8f81b03b6bb04768d7512110996da174321d1a7d79a5b00761e842712bed67f8ba405d57a64782c090004da3d04753214a87bccd6172e2126efe644956bf09e1aeaa8117212d4725e16cac480695db988ad9d07cf107721fc5977f8ff772514e6dc9fbbcf850b11928d4982046cc55d7c0cbef91736e93ffe9e5ef475c488d6409be331981ca4668abb106cd934399b3be7d3755a524145d7ca3a74c99c2bacc7cabafec17f05e743ef431cc3be66c06d91b9155175059d0f2dba5a9624205ef5ac6b202f626112f21cf853c47c41ef15e2fb77c484618a9881d605458d1305f062eb188abb0cb3bef63197144af2f1dd93b978b1cdec1d82581e98c62104466f7f88e41055c01a5d8a726ded154e88401a5ac7a784e32a7560feacef2efd5edac916554578682d1491bfa82fabf46e9ca1eed381004872e18429129a91b22d39f5dc142445f5cb86f04b6eb7af32e2f46b625435fa4ee8293bc9c6f5afad3c90470a8410c8dcf6fa505dc65e0e606decb7d2ace10145fae8d2a55c1e9ad8516f133aac3279673905a75a94ae76104a37cded5088426b0218f44363789a1a419f50f2d101d118bdf606ee3b801fb09d5b636986050c85ed94532a93da00726a96080b9362c83834a88741be022676d5c18cd2722106775d664da0497be954cbadcdd2aca7df745e668c425f0516607ae3415034af76185142e4ed6737c4e5d345e9ba6a6bd1637709123017b71597054fffe9c67153d44f9109183607c258b02601add87dccf316fd013b4072ed70aca8a5546d0a5f4918934b1a6a9252e21e272f43a23ec6faa8d025e04e5c91fd5cd90988b631119f3b375981103f53a8b15ab491a6f1d9cbf9d4c8af1d1532a0ce49f436f6a9298703ad5c692a1e4f8bd165774ac93c5fd6983338ad68cd16b0cd64d7e99bef10093bde29b74d127f106eb1acd443dd74e483a00b40b84ab467107cb24ff315bae6a67f7c1202cdefd2a4aab9fa52b80dc8b8354f2382cdb5f0c5ca95462418fc8abcf3c721eac151654f63714b45e98fae817fa940a864373bb4bfcd3f8d9ec65ae7d10e3a3e445347e9bc590748c03e1e476896d415c2886197ef1105e821b89d442760bf4967a908f96cf7be94b51de79216ba15cf6e0bcc8dde73d0544246a271aa720c78445d73b3baf31f38eee645c8317c4ad5ea0c36776f7c24bba35218a9345dc47a0daedbade938038482cd5c04f01ee6a03966c826cb5017680f24a2a644c2a4cf44a5a87e575445997c7dc0bb770c8e95beb2f49146604a968dd062e53269939446e5a4236a401dd4eb618e199d0b1beffb49810af41bcc262462bc063df2bec0a0a5b378d6e389099b84e049e87ce8ae5c0a197b99889b4324c946d2e3c5879aee8466fc0b13014aac53c927f4dc519877c82f67aeb99dfeebf0e816e289bcddbdcecf0638f2b50626013b0b2ec6c3b2fe66203000000000000000000000000988d1e16ccbd95ce114d0600703215f5201107d5f8633e20e72f5e47cbaa6b484147daa0d2fcc7adff1504d9c413c0add809f78be6950959498a2fcd921b08413bb488595fb88b156aea86172d40514c95adc6ba43acff43214e831ba52385b400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd1a1b5a537442963928217a6493706ebeaca1c0d63eb68ffeed54914ae8f344f40bd87bf9aebf2d6e221fa503c57bd46a09b2024443c984d7b7515a0e50c79d20c087bddfdf6c08aca53ccd755d18d70cd005f1f8cab84c37da97047112bcc4a4e7513aa15410790eed26165dcae54e3732e132a6bec4625bbea0d9cd9ff83403291138c8238711b5fe171756302639c15327c6830efce3664b010e4a3a95e00b032e6ee10b0872a38ad6ddc541d5f430fc0417a7b7108c6d5c7ba74263557f96a60a061a45519928797af13f4ce8f17025bd1436a467647c869ef691dc5c91e974806d5226d974539c7015e32969f0cac0d3ba3b435ef2250620666b5533101d691f03060d5904d5db2634e7756edf1a16a59da2dcb8d0db0bc2c1bcb63c575a2eacc4032b7f6ec91eea2661fe83c96900a7c2c558c3232ecd80fd41a05e8242d59c11f60327235a663c022f4455f95407c9d5199839e2ab94669e6923ee90eb1d2616169f0226eaa0c8e71da2bf43ebe8dda0f580151db75ed0b0163606b8ba52bdd6df8bbd030a2aeb021e8b4c586e1db135958b6b21b448949eb39c89c0e3b03b98ca34abe90f4a6fbf9709326d84d83be114ad6a022d9de239f9bb136128c8e4b6be9473468904b7244822af94a323554e722e11285b8750f4ca81ae64925b911a47b8b5913b7dacd105d718f600a334de3a34cf73b98b198fdfbd72a917db49c914e17617991fa08e2be6b1ce64a87c940f9e6ed5de9994cffca79508da462f0be1c55f19982aef4367f6e06f378cc0c19e52271eb518399ec5e9af6c0b2cb94c572b80642acfaead32e07275a7e003c3d46f9baa6de1de6f3115e86cf266ce1686c0424aedb0d5993e54580696daa52bb181e6af74af3a1e4fcf762022055efc8ac243ec08915fb29a142938596357e3ab0c6480dea98deb181728a203cc2544b13f2fd8b8d7ffe02f43db29f61ade021524ad978b1f419b53be4ed2b2d0dbd5b852f4d2fbf0645c83970fb97272acc0da9bc9113cbf8e346978b8c1b768266d661b9ec860f2f1c902a709a88962b7fa53ecc50d0d5c468ff200f22306d670a27047cbea1a307b3e88cd099f0a36b60517f526b21f6b7cadf906bdc6e639791c04af6ba1c327c31ff317a628b3c6a77f9913b2d39d7bc93e3d758bc35e24ff1a7e8e33d5ac2f005b1a5b7e9bdd5cb4c913fb8c00b3c5138de516c4e5643f3c980b8cab8e8b7e02bf9bd56fa3a444546c3fd3ec6287d64b57981684d7337517b7aea137b2b02aa87ee68ced6cd8be6195a63cebfde04b1d806dcf2a88225fbcf9b09e853b06ee43cb6ae627ede6696863280612494d8a1b0b8116b4e970f622d8b923c261631fae06050360a56e9b9dc7255ab8e25a4c6cc844bf02f2feae5712439410c72bf03dba89d9d07da675870b78b01533c27ffc8fa2228fc5bbdc4a81928847a670f0d74659937422b925594f75a42c57b4b6e31fe1da7017a4926d6a3e5610e95a9df7581f1f3cc1bf41813866f953db6dcc4b7e25df33a223278bc98208279c815e4c535e7317c2f2c0898f9085ad54b965ef5fa16cc7acfb397d1f38c80ec8eec9c240a1aaf729c42b8bb0321255c7fef4026d3d2a4b84dd83d35617bcc62740477f2fc3876f0eb9bd02003e8c437c912e2b9d626609cdf58541bfe4652cc843c92817df1cfa0f4aa97a51dc308b6c901d5174c3eaa4e66d54424cc4f98ea7fb60eb6b75ed1752188f8b5dcfc62859367ffe5504a15717e8d31e2c962ed7d3b4faab912b017641a8a2683fe90a293b895ece870cf187bd32109d0c871061f5579592a1550f367c77810c0f8c40912570148db6d0f0c3499bd1482234c80eef569eccac3cf66bffd8a471a0b143f9f9b450760f7d801c496c296c99e964e94994c9e75d1a2bdcb8ba00131b6294f06a5a6b02a0148f1d722a10be762b824196a770307438be1b74e7d126e7df324a347137830c372d153f98f2507659353eb7851f0e29b8b7e697a1a3a6455fefffc000c36b8c511b4d2f649f089e8877d958c830dbc154ab58617a1d44c0c545a636cdcc1bc4b4ff8290e455241140f2946895ded188e71e1352ae8ec3163e049b8097a097ced031ec4cd9f8ae57e059a0db5ce3957a82ee731d325a0b1cce29d978eb8ac33d2f8aec9af58e1ae9e69e0f1e9b254c7c04a7df363d7c45e47b679395742880e80dfe4e3071bfa12cbcc88b62ca3b66624f648e49f1d9496c42277de568033414ae71eab8d372f0bae9f3ed475eff3752f62e27c31647e66beb24db61c07193a35377a13c6953246f098b0b7aa2637b4876ea3e93304c235bf76004b5ce6220514c324bd752a399143b3d12706e76b0e08aa21b8a1d591d81e042a06ebb9d3f4cf2a6ffa4a382ad93f6def248691994c6bfbfc6ff6ed8aafc6ccf4089af0f", "6351ac6353ac536a", 0, 1619596157, 0, "5816be60acd5e37dc1464621b2237d493ae0ce17d5897c3c39b319a2e3d9dbbf"], + ["030000807082c40304947a6aa557e8c85d852aefff26d615ff650dfa2cdc411cab53c26d56812a455b0100000002ac00ffffffff440735e53772cac823395908243adce21fe99f71f2e8bae60e6334177c6b4fa802000000076a63515253ac6575638a041b64862aef35d435911a78bdf5178534a62a76efe44030096296042291782f2c03000000045152636a2b77d481c44fecd5155b1e9eae78e1e9b581023dc5f0f4e7fb8a4271acf41965c5fab71c03000000025100536dff3c036eb10a0400000000055351ac656aa297eb040000000006005200655265cb813c0500000000095152656365520000528e2f69360000000002000000000000000057a9920500000000c4800ccc94c031fc93428a1fc353a2e92cca8745b223279d0b3ee62c91452e2297cd887e5b61fcab1d0e4d4f2c0b58712e6464640a21bbd517c1a7b1c0d2db60787fe984346b0eca48823052303033b54545362c065569dfe3048a1b90e7ae0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a47c88f1ca26cf97f637cf432c4e05cd09e115e1efea7aafad28106c95f94075da594853c3e6336381b017a069e8d97be9da4a3a471f0c09ba401d5a8cc2b6ef6feb0b843e48eb8c1269bb1071a975e935bc1f4255c96e2d9a31d5a2f23f90c588bb733c750992565b2a9e0f9492a049d7cc3c562f37647f4dc78e8d57c8d64e02254b7ce143405191a4c6165519c649c53cc8f4221b699c130a8df1e271582edb021574279c5c3afde2ea08733faf6d368d588ed0bede00d02c71316b20013c18aa0a07a33b60e5a22ed33b22beccbdd6e3c52c1721513d325a2095edfd409bd6ee4a2bf6203bc36be459a48fa4920d7a4be0b7ee2e0ee08484d6ebc7f93273ea9c62032acc8de2bb07aaf891b2c37aeb225433f2d451d12dfb42b4b303b97557d0e0d7032465a732569570ff24d52e314548603d788bdb3b9cdbcdd44a0b6913407bbeae031e5e09243cf6a64d2a46e01cdc9b8a094f9828ef01874f5b14f48a52af895e9c0308f6374fa3652d8138a2ef9d44f3e05e40f8fca0efcb13a0eb20161b70c7c10903285f2a705a709cc86716cfd09a7375b8781f466aa4a8b743706633c4b55943b66c5f70cc4839e074fdbe1c946ff065a3d1dfec05b01398d05d40b758f2818fcf6495d811e7bfb4419feee9229559abf3201c16bc7529813cc6ff147c2abaef9f8d6491c17a78be7c79873a3fc6c1acfa01676096075efc6dc7789866277fe67696bfc8efa7b415e60763d06dfde547849a585ab4c77cd5c3a109979c03b1b58975f7f85639b473b0ed465aed89da78e8e8e4bc724ad36b02bbd7b0ef7262a580a47fefe0da7830c09f1b85a9ea392282ee973663ce210a525b164af7dc5adf246308473db5f72b3e040386a2c640361c9ee0b0410794e2ca7ad03610b8dde3206c61bc0f0c927b21df62a5a28366f4c7200a5384965ad960b6a42b025f7754fd99086be6db873d52210825beb702d5947638dcc4554d31c851bb680047be864811ee15a408f31396c37c4ff9172957d589d12d9ec4ab20dcf230cad39e3d4dbafdc2700300a5084ee3f4a662380548843919854fc988dbe61dbc091c5f24b98a751284c491eb55acb65422dff1e13737923da85c79fdde55bb81b8e2cbdd7191ea20b9c0ea43d75003e6c2a0acac4559e77ffa9165bac882c3972448ae612c4e7133b38057cc04602c26b29afb12430a0528b17c4ca37db40e379f4892ca8c17120efe68779ad9a8c509384f235df5a648c8791dbbe461b0c031acf58bfa85f4357aae63d5af2ff7b843f593343ce736b353a227532a8b1351d5dab0248b1aa0656b8c6c0239ede80d4ac188944b688c8111167663a82f8353744a09504b211ff81f282c69e2b63d92e6d6be103fe5520849ed8f61d4597397a6c3265be03408b41cc4c6b611f820be4b18952e4551b869411e3347804a561577d4c85c385e5b3de89b800552af08f2af18620638cc1aed3f775dc6325a5817a729f06729c1f33b46522cdbc4f28846538164b2497c072990964a38e51a04304e6d70af9dcff0c3e9ecfbd3106c6fb16ed364546ff509a650a75fe417878bfc2d0e89e77f59f657b0c74e6b6503503544dfe947de3bce884554f35a78e4d5c5cf4b051677bc38a9103bacb1626a800dec8ab252261bf25492f99c309d859d320df885790dcd4fdcd0b706bd84f5b668cd1b28d7db7930603fccdeac0b37af09a083c34917af0a03df262a5511e246668ddf0b5757d196968723d9b4842dfc261859cdb073862f2952fe777c42c879c5d30a97e5dce512af03bbd9fd0c6df95c7c7e3f98a73ac887ee5464844a4185f1d6acccbce28943d391932f09d86196a99f6dd7cac16e01b23a2cb01e71d383df1d6b5c853b7927419d062581c73343da47f3abcc0928d1d7741abcf8759b7d01743294a3f5b7d9c13e3d19f36975c783a8bacd6b17033ea97eff6b4c0ffe727492d68805b9a56daefe10a2d676da6da81e4a471efcea9cb2d312f5b6fcd6c2b00aa1b1c87d34b28b52e965712d69a5f7ceddc8f1515de3fb6514201643db7ad302e046a7d96305d866baab5f42fa54fb322d5362736e6c0a918132efe6c14a8d782dfdecdf984110d5ae887f360d3b673977b624920a3ad78e5455d5417a52e5386cb07d3bf222171b7c34a40e13982b59493a10b80623f2ed9b308c8ad94a1e7ba86f8b6c9081d1d695b19c2a360bcdb835e1fa184429676653ed9b0feb6f335b6173d4770c7d7d09dec530e9d1c2acea9306203e8b81fb29c218f91ba6cde365fa29efe8f1e55f7600000000000000005a44a00100000000ece5fb5fc145ba43655a4bcf60fecc764925202165ee5ab96fde33830190308d133e49d167aa6f40c5d15f14002338026f4e1dfc400d602c4021fcfb71fedd74ab2d871dd65a565229952739368588f45064d968968c6c2597b33ccd76609d7d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000158452f051a6d16c1e2ce8b151ee8a9eb3f70777e18b660e53eb3a65ac258c53d367ead0176f9ecb6480b5e0b1939036fc08dc91f6a964a794d06a51d8bfd2756b25ff4aeff15009b28a9aa6c47cc7753738941c051c0eed6c98c2c102a82a4c4b42a16973394ad3c97a9f2c99390a3c8f3fca4290cb3a4dedadbc2172eb30c4030e02898d67f884884a673c5d106fa9b3481cc0c6d5563329135f1b3a7a6b5449031d9eed3e5c082e0c401666aacfb0f8101d12b73b75a1cdba0acda0d9132010570a016e709d1811bfe6901749a13e578f12ef9c32768df3e0c223f916d163675ef4746e7c6c6face87170583ddc57617a1261ac77193f610142fa9952f9e399979f022aa5ff46dacfd28e59ce5ac31483995b0c3b6cf0791429f8fe066bcbe18b8567022de2abbc41637f8d4712fbad7b5163cb8c1cb0dac7ac3312772492178a743a63032a9febbdd556326f52655fc8ce72138e90f5479bb9c8ffc670b424c4d51f0925030c336dbed14f99208870db4c8ee770f137616877254f4c45d21a94ebbb9c04860305c2ff736c2189504096c36f3963485d2e07115c86df8febd6f83e1268625408801a02fa495df1d9e6c3a20b27d651ad6885ff882f0b9eab3dc8d61940ff896d82ee799f27221d684b15f8860acd47f510ff185393b38614a91faa71968d5d445726f73d0aa01ed22203449da9eec3a98b6f982df3c40e0c6f3db5b6d03cb745181ecb3d797091dd31a40b3a97a923fa98cae6236e56fc588a38a21230a8cbb283755b9de91f1c0c2b16514fdd8931430247800389f655c170c79872183602fa801e1762cb0c67ba75375d3f53ca2bbe561688b17a6a2059d9c453aff4495c641779709dcd6450cec80dd64702668b606555cda5481b1602fb7e59167443b5dbffc0a10abeeb7b3c94cf750fe0bc1b22a5b658d04fa17688f867630a9e8d6c9255c46766505d054e66fed460971fb65d5c44d01cefe774fe5cd92b976cbf3dfb7178ab6c88c7191da423be3a1a606818fdbf1551abeb85243986b901e29bd72c850cfe70abda4f8a925872360ed1bae23e58b36f4dbeb468792559f4b2cd41921336bec4b9ede066ff9bb6e10a8423c830659d57fd19b7eb51ef7b1882caa127ef75b56d93cad261e8029cba7c7fa36ca3797a8eb1abbef2198db312920a976811af64501f032e38eadb78acf576c1b3f28c8ce1e02658f2c992118528a360dfc9d715dc677eb285c01aeea0a74c5d7ddc4cf2c099aff0feda509d76e8a928ba048fbfd85bf0290fbefa73a434166105950b9c9a58117e37f25e79a22c74efe0c6041617a41a84bfe0ec94088b07d6c28b80376d6f6c11629036c5511b487a1c9a37f94ccee9abaafdf6bbc9c8ef048e78ec54e2e117ecdfef2e9fc06bcf444b99a6225c742080dcee44f4af729914ad5fc3d8e1d40de20a1542b9679d52b622d2e153e4ddb3fb6e267cb4bf5f170d7a88149e9e2461e3941eeaa5647a4195a22de4d27141115507ec55c916607201aa52a57e095d2ccdb6ecd41254bf6ce168630f851c8c058bec854bbfa5c304c9d3346f78e105892adf79a2812d0be7b5d04092559475b53443829a84435fd513cf56ee8bb801b7c3c25bc30cb63ad0ea1783164312bc5dc7099eeb2d54826534fc3cd6e46eb4f993fcd350c7e739e72db99784f46040e84471cba81cd8c2cf113ad0d132d6b8843ab7d587250190f37b1ca63e51d9f4c606c2f20bf22419574e0ffd8f5f9bc36b9d883d5db7adf7c53f47936bfb7ac946e23b52fe6750d679cb42386e4402a3ef7979bdae2112f44e0ac35a34f0c78b9085c0e20fb6477176baca581c5d53a8b03c0c9963a76f375cd31dd167c6f0c23dc30a6938d2b59d0fe41a9dbcc5f535141e6ff545469379bd79637494a181bfd08c1e1ce14f5c81a0cf2675c68dbc7aec0eec5afa3aa2965585161cad889764e866ef37a53f0818142e883da652acac4718aae1316d4e763fae90e8bc4115cc731f4acbdfc9743e7eb1e128cd528267a4a653f1f3beacdb9a5bb300da030e404dbd9fc248e871159a038e902965bd32173ee63474d016302837f667348128dce3f558dbdc3998a9aa35104adbea60db6d0c48b6ac57606e63f4e2f5687aeba6784761bc4e94aca2de06cda5bb6435b9ce963e1453efb28704da056ea839a8be648ad971840bf4b91760bfd0f6eef72b079bf68d8b32b209b4710fbd581005b6a87a1587fdce690765f8e2d1e049376ee91d4cb3e0dd86adfdf7dd18705402ed7cf302f582098f42f5dc74894305b2f51147a9dbd90c301900d97eacfea3ca3be3f9c3227381c3a1d1700fc1041aa1385075318236baf7e80648d3054ed683a7a365af6184b542d8fe3897456a3aac20206587d6da377d75c7f7967e75c1450e9ac948d1803ffd8b72d2614207", "6aac5353656563", 1, -940298160, 0, "28649cc69b357012f7e3e121f68f9b8112180a1c952bba7560675dc7f958fb58"], + ["eed4d36f044b9067ff1bb114fa5cfe43929876bbd0c0c4f33e5da0aa4df898eb9b6b23d60d0300000004ac63ac65bbdc606ee75a721784fecd13e2ff62cd60a1dde68bf795be4dd1636901524e9d7fcfd5970000000000ffffffff3474997df409466908b2f988a9b86f2ba84b3b35f515fe4aec85ebf5a5af95660300000009ac00636a516a65005350dc953b00f500326c6b6dffa6a7fc3d93ec3e0498528cd366e6f07633cf09429c8684df02000000075151536a6a5252ffffffff01754e4f01000000000551515351523ea108b703600d3c04000000000000000000000000762178fddcbcabe3a9d274c87a1fc0a06ab84d6da9c13e11733bb6685ed7c6764a0cc8c5c67836268c1a5f6e7191e663c38794ef123f23a0ad6be5962c13736dc18fb951924cdbdbe0a47965d947e8e9623721979d94042bfcfcbe8c8e2cb9b100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b7371ca50825f360e6bef602cf8157ebce72b2ce5c84567d3e7dc982f5fa701250d1e1ca29b254cb7eafc584cf063c9a59290276acc0a35d9b3735dae66ea97896efb96e6370dc147c905a8f1340de0c403548f04c668e4304aa35dff8aeca6a89471d05b5353c309b5498c05cb8525324409072609ecb91f826e06a2b7393a303110efe21ed184b6a07d94c129cdbe03e03b20cb8c3cce3e143db19f2d6e984480324c74687c976448e029105916d681501f058cec7c3971c8b5508594da55234cf0a07cdac59dd9312adb58ae0d81078033abde8cb81e82cca789f7562650570b63472f3fce5e509759a3f722137d82f3021d66f5d5dc6ca33c2ada5e9289d24a820031398c107a88c7331767b0c62486db25386206d10e435f91f8bc4091d00063aff031bfd45bbf8b2e5a8ebaeb9ad91043205ec54c86457986fd0756a9950f6639335032cd9bf226ad3a9f3fd94f1ce76dd973a814371ce083c49ecc29d23de99a070d40311052c0cce7bcd251b16e7bd5c97803e9c818b43870a24edca29e1d53a2d118e0323f146abfeaefbf669c82965c05ad91ea94d229a785c045aa89cf5a6ce39c7139a117b7c09538137dca49c18f0db89a14c3bd2f030905f0dff01bf65a3ab9feab01578caabf3d37a391e289b9d14f9d71ba5d2e70ccf8f40984240758e4c5ea47fb25b71722606c17ef386d14ce8df50c52cfe9bef7c5ee3eba1f27c075dbcfe612ed84d59e7f5758f5bf76cafb9c90b20588cbb9837c5edf717a47f2c61eef0f1116bfff0b5bd611c299a6b1fd332025a31e8153862be1343baf45f7058cfc004287a41a1c02776fc6545478dbcf092b77538065605613a18e17b8ff128140d8f46c1918955fbe1d0c3e5bd8105a61a905e460ee7b86e74744deec3ba5c9c2f5e10cbabd69b7d8286320b94786fb2d9db374e709ca285053654a291e35ef1b9e1030c1f1786921b49c4a2a9cb8f317fc62dd4d2c9323ba30c324ed7fc66cb559692654608e6f7c7aec23c1792a221956392595ee59c5e76d82610009cf84f77d1b631676b6990884dc95d912a8e433c1fbcd8479725b75f233f0b6d12a2fb1676f5c5028682afb8cc6015909f722f3077d3b608bd5d6c5f9e2092288c6671fcb9134c09b2fedadc19484c4f9070825dd72cdd8fa4558006765bf9effe152d573482c0300b66028e34afe9e0b696e6afc3a8e96508c7fa354bc0955dcf3fd6fcdeddcd9c4f114c617897f395acdeab52589b76b0b7eda832c015744ee761a9b86f2f31a060c3952ab7c49e94eba6b78f4cacef81cf258d724ddd42f068d3e5bb8f891f4ce1f4b0fc13e571a05ba7e79521cbcf843d94d1ec978b0464c2f013aad279031cf3cf7ff6bc384fb46903addc34a4f0d3f4a41b26be77589bf750f78440bc73d267f96e18667bd50365321e4b6aa68c204aea96fa18cfb1679326d5b4147888ee5e36edaf117571a0e6de8ed3cf30855b480abd7e0d5f75e7b1be1adf6c01605afd4ae4cb8e662a9649b63ecbe92de13572f1c137d9a998a564526f16eae2c027d9d2f059c05a71773be78322832100d4b3973ed62220508f1129f04bcedcfa4fdcdba4db1a1ec1456fe374c4d601b6a4afb5bf259c16dd9467c1d4ba1ed87eeade1335bdbfc2ab8aa4d1742de185a8f5b95e513e2f2f441809327542437501bf18c28c3677a27a17d844a8cea95c405a5202b9a017c7fc836fc8c54fdd7e05b7f72f1836723c19b58beebb2a03fa6297e7d6ffddce2ec18f22a8a7694e811eb78812c8f35d7051add4468c7068ae236a58ac4e0f954e78bc12cde3daab16aed2ed621919fce3f668f3ccdddad12d72c31b78a596280460fafe80182ece8cdebafb6ad0f29dc5538d51756965f8c5fe2bd7c856e146c5b855f75fcd1e8942e2a02fadbb46191d56f0bcda465d489cfabca668da074d4248468102b791a70708384363ef23a61364c89ef4109a27ef5c42452d5e5aec03db1c25894604f5f6001c94c55afb9bf1b10a7e85a6c9470ea17ec4cd54dc06bd680d1c1193457fa3ad6d62207555af6ba63d676ec740d0a6a78806948221c70d4a9617289ce75e4bc84bd4d615475def002d1b967daef2bd4cdcba71af6b8fac4faa7d0dc847681ad50ee406fb6a6d18d4603bb65136080cafd5fd1dfe1c6132fe6d98af8c4dc56d42d5e37efafb1f9b3de346b46ca0b102a3be903ba76b297bdce1baab8d04737fc9e66fa5f61759944d43ca7fe6488581b538ede7ab1de289b313a0490302c1d5595e3af586acb6f4005b6eaa49acf2b94b129702000000000000000000000000eae543635e80bfc39ae6cfce175bb84ad69b017897a00b9ad587940c854a16cce28dffc47caa0832042e1205ddad1854e89cee9c4f2bc3a8d6edf0d477b516d2ed97d15fa7b0d94ea83449416ed89d0cefdf83b7ec7bb128f6131ff4b7e0ef940000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000031b3e67525ad0c4abc5bab0c06450c5c80e4cce3b30e38db7178c2bb3ac98ada1fcc8cd3811bc2a2026fb8434be5f1cb4c58ff8307912c42a65ab8ed028da227a02bb34753bb497d5b615a7783b2cef3da87754f61aacbffbbf7516e91288de0dfb2fa5f5564f712a0034ba5c2d6037f2170642e90bf01198293eca36002958d02255d485f060b97f86bcb132a1a05a2f0ebd79f28cbf0dac57cac703736bdccdd021c549d28116cebc84d968e3c912e0cf096e464d422e30b8be6850af272107d240b005e20d4f467b05a4a797511146620947db780edb4f18c1ae8ebcc1f6759fe545427151de1453003af6f68e9eae846b62aec4b74926782cb279aa6512cd174e80302d2f88b33e80bf701e384acd969a477de4fe521da83de70b6a903af5a0c4d790228b8026814380626e2a80abe9439b72bc99832f8567021190741c4f86fab69be0329046894c930cc1e7fe112b3bba31999837029bde91ab20943e25f404e0a10be0227513ec16d8d8b1437bc874bed832b30c30362e940ef1e01b96b5e013b0f88d9022cca7e4b82400d89877205bb75eabd5e8a8930cb201052107db367c36ed635f37d3ec56e78401e7a3f248e64ee1996ab5788457d81e4a63292f5e16264ae89ddd8cbbc95648d28e471aee86ae5110b451dea77aec94d87d6cfb36655e995d9f3d02114704093e42e1e55399d6e08ff25eababab44bc855344c883902c0837449632e7f9a9832eaca50540c82f691121350faa9fae1cc6f495686fdbc7eed449d245d51ae51f454f8a50674fded8eb5713257fa9e88bba41836f49b7820b6ccf93a5181221ad08da206b6b0270a6199171ba628b70bff8eb2345a36fc80ea1eec43f2ae1b3a0c7c47e4c1c0003ac077224b59f0e73d944c6188bd5a7dbb41e9a0d03679a480fc018abe00b3e995df16b55e815ab2395f6b04b601845cdfead3de3721e856f577d55a8915d3c07cb07e9a396ce7e7b26a567c562f9897d5d210a3e2d204df5e6d6782863dfecddfb807c5d525e8bbda33959ae89b7914ed660456cf271ba239314152027eab5177ffb29cec68cb7873a223d00a222fd1e429a62f03990aba159b88c4161467fc1a84bc67976d1b55a8b44127c346d1ed8e2924cbef454e5ba25013a5af8c2369c7760ec426b6352b03506e3e0a4929651584142486349a2fb5039ea391b992172df2d19494e684d71ea72ffb79d56fdab1f80544d47458b42fddc6239dc1a015b4dc750d6236e62a6a5cbbb16ccf7562e0cab4459c974fee35c3b6bdf6e1c5c87c668b80daedfb61cc40c287d4bafc6d81d48e43f9a70116ec427b49c960dba332b6cac84ac53bbeaf84ec75223741a2f0d5b5e9f9a75f3e35de90384557c50455e412ee18ed05a408b3f055876fb3e5ea825dcba0da72a705f78143e0159c9bab0e2081b56732ba546862e9eb7bd90e05b4021474064bdd6df5edf3ccef54051fe282aea684dad21e813fd033ab940f69707c866471811f45bb211c8e3fab683e11ab46183b9c65a0e51d4c05a4bf698a35a9e850a6e7cb42d39df7792ff0f3ecfc46a3731d5509f72e4fd9047c2a137a778ab64abba432d967f7a041581016b15fc55a22bd834b05632ec57be52dd72f3273bffcbd8f654f0fe1c3ec9c04abe7905668742db81b8da5aed3b4d90efcef770efd84166e613955801df1f5371fbed7f68b45b10c743d3e213cedd53e5b0fda7d7e8ace1bc3eba3dbf40ad4f6f57db58c1848957a9adad80daf2c891b98695cb994020c13f6b0fd06a8e6b33ea46d32fec0f62f8124ba0977b5d9fc035d8fe48094aef0ca113b85582b3a1c9212eb994301aa541708bd916c4b08e8b55195fc8a0dd1c9b1b9805a6388473b742d7afa0b1585897bd05e60feace5ba53ccee36e36243ca9add69ac1e39f85693850b63ece17336609cc7c86769831d5236a7c75325a9bf3b23ae02b1b2fd31c0a1184f6b862b9f82af7f35471bfc434621943f05f6b160f183612246d01b1e3e617bc4f37316a189feed3f3dd7cb1a9c55d88418aac6a23e3b855507775e099806896ba772e70c05d28869d7ad1501b474c9a7d380d633a537baff376f2dbcbcd44121e4b34663ebf42318c1fa9082a4d35cc01843dbf215bf3535cb896f6f678559eb9f9ee04ba7040d71b17e5423f636db0f08a69d0314ce2523a7c1f90c1f9a77d58105c5efd24f5f1f30004c17efec3dd6a20defca72aed3fa35888cb4d010006382cef18647cf3b4783b6afa305d7f19e1cf8a0d2744f55c6be4feca1fb45f810a8f4503f0000000000000000e58e580300000000200b1866a20bdb1cff1e584a00b400f111b4d154a990f6c95fac95c422ec71f2809350e4668352ef1460bb8263604f2e764b1910199d551fa2ee4539a77106633b301874055bb988cd38af26d4676ae6b27037baac18d5f61da11e7d23a56946000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009ddd281ff65fc587b80c63fd7e20ba7484174539df7a127fa3b99739745aa144c78eceac5f632cd22ef36871091f6a1694d886d56a241c6aba33b85c77c5bf5b4ea83aa1269350e1136844f7440df3f55e5b7ef7ba9ab9adddf401c7e5bd38a55f87d03d748303f445752f228714e6adc5aab836bbb085b33f39ad34d04be33c030637339da816daf1d88f113c1339ccdfdc22e4bac8cc3cda6d72c08609f174660323a0870abce15a7008abab929aaf9490fe9883f53d84589f6c095c89d5d77da80b01099fccb9401fc546f60ef280da500401a0097ae0495f371b09d1b3356edbbf4a35e74f023200520c77798d56eb4e709eb5cdb863cad202e02db04d13125ea3022643fb2ca339b47a7c938f7d8c0dad05c3cfbbcf0fbd81440df396232a76134f030b80114ecf4d9f82d46f44a88e632d4bd6eac3ad72d42814ff40737b85e68f1d02099b8ec4ead014dd4c6936cd134a0cb0466afa06ef9f511440f5704b886552260229e008ed1b655ed4b7b92556b8d60606a0b74cb56a6a3cb0ce5bf02ebfe293ca02221277eea4303aa42eedf75209c2007e79c83ffd87826c7206a68ba1ee669acce853b550fd5a679ba0f022c881c3b861b61f7a80732d708613c67feb2e57fcc8818a3f80a0db3f00ab06645532b1730648bafb12a562ae16d224efb5498f5fdf408677e0e9e41bd69f3b32972bb025f477f36fb3ca13973168a3f58732952c067b3d1f7485a2acc1759f9c351fbc5ec8ae82adcf3e2e460eb2c5eabc9888756027700617baa80a48f84f7fb86b12b79b84591244bebe088e3f299bc5cf6eae410111fb750b70d0eb06e1d7590ec1b9d4215e23fee09fe8bdfc1fb5d65dee119fd2718869e28a9eec8f31f3cc532272b24d7e34676336547bc0b01ec0cad119cd37ddb188a97cfe9a15b6152f5eecd5406870ed137f36024a4bf496b9ae07dc4f24111d9026bc154fa15031c75da71181240d717b1c2861acdf2d50745651ebe374e7f523fdfc895233cf3d305959408b403f6198d1cbee04766bf549238cfcada8715683e847b7f8d3fc16b35760d3f1d8b4b88bcc0854bc3b9fc6f6184f8257be49da61b228d48c6f491c9395b73b949e2da8fe79e96198969e0e04ef180874e9ba16f3e0c1ceb53fb80c4f575b5e3399a732081690840ee1112591f0bea02529dca4e815d6032fe90d80295361f4424e1446f3dc4a6fe58f1fdc852b783c55fe80ccae5b9f4e3ac3a35782f8bdd4c0deb88f18b8e93d79b08ffb57172f683b618e2caa5f017209e3a668e57dd56d398e38d48e32c202d66baf5c565a9ef64d1b662d83ca7c080dc657d5124fd06d8105005ee7c9a4338cd60f84cdff735fef32e41331af09bcc38447acbd915c9fa4fe91d1eb4341c310cc58cdbb14e44669830b16650d722b1281a3899b8f0abe85dab95a768253111474b25eeb1656067f40b5f65d51d1f35d7325aefbd81d8c3c190aa43015a752c3ad241130384d64ec3e69caeec61cdd4becd88d9710768c6ad3e9c5a5bfec39f68456a2392620cb12212c08a9e7f230337db7964242339abd33d5795c8136f01aba4af23e9da2db39764a2b033be3437ad258f1a9a320824f3edc3e34ba5eea3c85f4ea865fd4908253e9e4d008b4d0361b6d904f39211c467ac431a580f4a08f3faadbd1e1f1dcf1819a6caa04c6497bf904a83bdcc1f077cfb8ffb82dc9ca7532e0ffba08496e0ded220f012a5c43f86eb7033b2761acc3b66bfe645d2b0f5265ca5e4a770a2c800c0bc61da5657ecd8550299ffa842eea2d974c41c3181f664c971a4c9da896c2075ece11107697898c0dbd275f251faa3dedcc2e08f006453f3bb2b58cc7e2e0016240e1f238b30dfe37d32a9c3be92f8572aebed315ec950c925abe914f9790a00e81441fe63db302984332a75c62ca9e4da83b9b9bbe909c7d6c517d445dcfafcdf897c0015f82a2395f3f1c8efcd6743d71911459f15faed6aea77f890d22e5fa1819b8eeebfdcbfd06691f4dddfb8487536482b3efcb6be4a0e1821f535b61948f833876f1a81c4c107c125a094b1785e3d9676d1037e150098eb569f1b41ee7c48996fbdbec406cf5e30be13ad6f7233e55e979ed30a642306f8097546fd8e74d826452ec6c1672c4c4522b5fc347ad105ec9986513e27fab76d686b0df88a5fabb62d04961850f02b04e1a5e84d72f5482f5b0fe0b1872d50e1ae517ec6fb85b5b9c17c4c0f891c5c31f17235fcc2bcaeacf43f0a7b32d9e27d59f071da50482cf35490ee46536ea4f3417c4cb5bc764f2bf946e96060a1ec2215fca73380703289518b1ce55a0c095ade61de07d93cb6b6285845e260a8fedf0d40ccc18b4999dcb2d859c30f1a838a7dcbd994f3c0687d2b5c9229559bef5e4cad6e1e58a171f21f977c49c44bf4e19314ed45c01", "", 2, -1742820425, 0, "4e70801bbb60b699f8f2e28434393966bfefe88593feee509c4749ec527398a9"], + ["030000807082c403023dc86c7e88f7350b6e05f50b79bb53c82c5ed548e8b2df04867fbf65c3b253cb0100000009636a6a5353acac0065c88e03ea8fc550f2abc326c5acb9cba781c0539df33651b9d96e55f46d96e2594563ac8401000000026300993985d2039da7f600000000000300006575d8f103000000000263634081cb02000000000665536353636a51ae475e00000000020000000000000000a8df0d01000000005c65ecdae516b8e0048841834f950b83c1e957abc3b9d6a36407e9916e3bdb97d382907651c9cbba297f96d09ca0b3df4ccc15895e143bfa23cda368f014c35f2d8ec5de48b8aacce2afb26d8b5f2d316bd87e64dd9eb62ab958be84f3d986bb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029feb207ce5c781fe622bacd39d38cef90f354f8eec027e7b0e009107faa3d7650ce6e0678790ddf5006a1f2c6a94d86a75e5533bcb2e808208d007f363d80db870af0ae03c75af230d79319b93342fafef033f8b10cfbad995728b2e222a881e0fe6bb1a41b45637b3b3ee79162fbd41219ff4e31147bded97820d2d7305e440325e655ba19e8f50c53e63e924ac30c07e6c37ac04dfa3108f407226a82f9f5580227ff49f8aecb927d7589fd2b8c4e3281ae045459a84e525192a6dc7d1280668c0a08b73927d97e067c9913601c389d97016db529cabd6714f9ce25827adfb94d84bb1174bedcc962997ff8e557834582c02667355f822f7fb3d7b04cd2bad66950031099c8e058c0de09025b03043fe6c03f9eed93bd3d228a7017f07c95ca72712b031e9587267034e0133b8ab93b02d158df8b70814f59b6fdd31a3223b4a7656ba4021c984fd8fadcfce684c66c4725ee1ccdff155a3a65ce89a15975c59219a8358e0208e9a6bea387fd7901bfe0355892a2f7ace8c3f786c353beac2c3e9281a2337c0325befaaa2e1a2c341858dc4e4c32579477482b67dece5edba55ac4334f190d6b37871a31d9c40694b44edd4834710f302a8a65b9e48cd547592ecb904a6e51e8dbb0570e67d93bea5d25403589105e6b60666e8d81bbb7ac6c6831821a73dd8e20cf1bf9b791170bcfc9b3cff774cd7f95a7ef6b27d38e1527dc164634fddad316acca2ded1f37bf18a2a018d99e556d19030c396df0e4246c5c742efdddc51d30a9ce55589e452c41afab21d6edbd9fcc78e0f86a1b68109d85c4c85573f3cab80b43f7903a347304554d51792af8fa910219d58d7a94635f1cc29978a2ada3334cfe92635a49dfa4526c5f955a6f8fba97ece0fca7f93f5568658b224f8edb79759d3bc2f61a0e8f523766d90dbaf22961966d3b2404a9967cee8faa10987af9692a5258f7915d601f8a30f44d8c39e030282bb0e95f7a0e277605c9897eafa8cd7670c5d55a88779c59d507b17719881557ae3885356495472f376e092b078357952bd747046158852fa151ad6dea970984b6ac46e474461b1c2d74ef3a6550b952c655404031d886672f324d71a298bd6f2964d08e2d8fad32e37e28b704021c9ce37d51d05ca9c9c8e55b9515eeb4e0543b57e18f8de6cbb6f864e9d7e82e937761ee6630d4e56dd9b46104ab3ff408302ee2e1e4329b61a1ffad8d2f30eab2c01274471a75f9427f3850760002ddb1d865ed66deb553e4d71bd963ded857c4b429d18bfc838ed2d9be0075db796580cabbc1a79a01b7d6e8d2c588d9b20033038a6aeabc801578de40aa3feff6e4f029629a4c55c822a80a633dd60bdcccfec118a62fc738a87fd0cac7db1a5e46c109ad2f5971f0d97e63afa24301c6328f6abd0f41a02d2a8acda7582c9c70abc1d748f8eabd7cce52462b29ca17f84fbfd8d407e6715ad44554b9e55b3a5ff2ed1b9b871706ce92d7c6c229bf735419ce045d98df002743bad56b3f532ea315aa26cfc717b69d560c482a6bc3b0c0fc06635a77ba547612e44767f7347c2f1a942598d1c61f8dc897fe4f20b85dfe6926550b91c605fd64321b0b9399fe79746f775880572d9881a03f66e2bcc0141cf0c84d8adc8549cf7c7011ff9d70cae003cfac6a802d13fba156188f43af263192758e8e1abd6b57b6e7a3d2e45565b5d72cc96d0949ac39b16425b6599d8bea316bf71bad6828c4f99f9953402b66f14d3e4de96f4387a227a7bb1bf672b9086b4b25f836a9a197c56019bddd3b1de1a0d2607c796c01baa35def00dcbbc900f18b052f3bf2f3a4f585762446eea072ea915a6e7d80501ba070bd0cc57b534168a01be637131e11aa42c7c79d09be51b914a67e4efd99978c7a22c3b5c5d9eb085313f21eff23ae5c490bb02af90b64f7292c8d42c175ce2ddb3a43bc15b88ed492302f230748dfb9f47a10b74f503a95385774e5e25454744633e09b7f9a764048afb37e3e77c0b7150f3f0da885360587901263b177cebd9831f56d0320a8cd8272098de7540f573ca85c49fad1e8824cb24d9ca7781499836d02760fd21fede3a955bd3ca2726e40dc12407c85e899f12319642459059f969cce3a5bbcdb1ea13871f33e0c8058e8618677e1ac3f5a99ff408f74d83425a343c354f9ae10c0858d75accb3599e3ccbf309ae2a365caf8f8242ac11260716c6e8b491ce02b45b6ea6edcd974680d0bee70dd3920d6f082786e7999431c6d72e4b41b8927680d18c5e244f06916acec862e22adaa043c9ceff001000000000000000000000000128a68a34441bd33725e6500a0d2b5f3df4074c71749cbfdd0c37247507fc6bf19705b125bf2a400d92bff09a2a160f42cc894c27fe44390daef7bbb183df95896e07e9678bb8d6f0e9ea8019a4116f621bc1a437a0a01e075f32ea7f126670100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f1dfb773926832247ec6d44d2ddabcf8e062e1e4ee79cb548cc7349bbc184936783101eab7adf7389ef3f99c9d1a12babe1e516dc410cfae5be79325f5182e82e1cc7a3d2d99d9513ba5a12e378e0e9ee8276e44ccb22605ed65c5ed2ad5a088d938eec27154106eebd868628e70bfc4df11d13cab72a18b7188c4bb49c4cea60202e6a02c13282f7bb1fb13726c6e17905a9809a406ffafe37956fd69e6bdac2203207fb5a10e42f6c8ee6857d1ed0db34ac8d08bf95af185dc6704d273d5d370c30b03f755f548056755eb9fe1d9d7399cc3c75e564538e463cfa307432cefcb14a47958e89f5c4ad5b8db9bcae6e0de32cf1d6bcf5d87fac96c8c3d64c7c5b9a7c7032046855ae5fe7b2d91e2f862248ce146f70f2b1f197e0a17d80b92ad48ace2d803225f26862ce3272f909731942f8efd785c68225dcaf967f50e4a782356ee823503197e6d31818b6d9524ac5f8a4edb73ed628f895de77468c7fb0d8d7acf645b32021d9e19a0d1e69b831c80ec1fdd677cf1f326430ca262e84879142dd20268fd02032bf31a1a00f20f5b2ea36fb38dc2a4dd221d6f7bc119812c6bd5cac7d0a52a9c44e1044799c534f8f090a40b9b657b24aa1c7c1efd0d7e2609bcab3763eb770aca3490a8b2a76d51c1ab97927bbda48cc4c10167f70108ab30d58958b1cc18ca4eac3051a74a1e3feafe16d3dad4b6d081e9344819dd1b2660d211d9363cff9df287c05dec97d3c2f206142b09fc60b19d7e8198899e2193b9033a75a6dae7fc6dd30a0fb73c9ebc1026b57279e4d8a16016291da9826a9e5824ef960acf64d2fea69baafe6dd3cfb5c549ba78b5e0d632db8eac78ede54db492c2e87240b61109ddb67637bf90af4f127bff06be8bf9a5f4d65ff4c7b0c3cebc9cb26e90d1c65fae66b9ca50e73c19e9e9b297b06bb7b08562a3dfe9896c7bab4322b582507220e2af5b4e53b5ce2465a46f74fe6179b70e1cf54d3f7897705ee665aac4f503e58064c80fb7944b69d73daadeebbc0af21c75441b91c0740c8cffc90e2c1a2ce16a6ea06e18702cf6b28f5adc0ea273abfda7d8cb02b1637777173f11615b41bdf6027a9a73f05d2b57f656358ec777258711b9f8b1c1cc1f2575cc24f87541ff884df236bd3ac4feade4e656d7ed5a5f7438bf1fd0e2eba4f2ebdf11be85c2d953da4d219676c3c9bba04c12f29948fe8878bccd02bf8617bd148d409c65f1a53c4324e41e940ccc5f89f7214b1c3cf95205e00ccda28d82c51a471c8e6f4d76e218eea16e2e78f7b0825ea9391400f0d171fefbdaf46033d016b4d89358b2ebd31ab2615111dcd1610046d5ceac1cd9f25cd135a05c98cb08d6ef98b76594971ad93555c3e26b2ffcb88a1d562a87c3dfa52672d8af0ed4e34025a0e987146550ec89d5465ea57c5987e1b4d702138274bfd2a7627b824ac6306a62574f70bd8d108986011861537ef5a06ac44d14b17194ee40525c2a765f3c0ef6430680346d46c9e5566e8ea8838de539a9d8517d66e84f2f5be2c3e4743f0b98b66f10041a9756c41de879a2c5238191ebd6fae5b63d30d6762fd90e6292183e9ac8da5a1fe88bff1d064568edda9527be223de3d646228d8decfe8e01cadc084a68280f1304fbae8b31ddd0cac09eceb59e0f5328f3b9c4bf429250b6e8fade496a432e059faae16e8c109ed0d9c7c92b664bb5cb60f346cf08da06c756bb91cec31d2976606cf14c0476c34aaa4e777e9acb377c295fd1bd0f4d16f907b45ee94ab46aa5cf6846093705952ad9b26c9ca083d2f8c934149df68707eb048fabdb6b15cd5d349f1af2ab5d8bdbe79f9af43d8036d6b552e44cc78b750712c9b722559a577be868a96e7c3479fa04c812fbba1ded01f24c14a4edfdc53cc71ee791e7073016ff7a141da55fa72b454bb3279f383b97e24593f3721d680ce07c86c432c08c2643360438b47693b684c54168dec664acec96045a200072178cdbfa0aef08ac5825b4033af4a582c3043d09cdfb3f25b1bb4e4acf896f51ffb8698476dc793127801fb24665a94f5dd6e0387d9954acf3f8b51ba30b252a22125d6820a7ab68766d2f40b079f65a8f67dff6bde80cd99df477d64475ee7d3be869c5a12c48cceec27fafc262c4ee180e1962ff8d54365b7d5ab651d21223b792ac676de0fc07bf8d6cb892ffac9a2ee9e515487ae9f2089a32a330e50f3f20274b31658eca836c9eccd5f166c9406ee41aceb0dfc82571b3225949f85c4590283f5a1dac4cec69f28b0a719bfdc363665910e5275030caec7879af71a074045c04ac4946ae4e219a21b8dc723889fe0912fea7aed1dcef4eba391d50e6bbbfc2e51c7b6aff11ebe5ccb1c0927ae23c8614fada4ee2db8eeb27c28d3ebd9fdd14ced942bd48a97ac964250f54f5a4188ed5cbcaabe4ec05", "ac6a6551ac", 1, 714134217, 1537743641, "17f6a8f062d7b78cb529d9d59f4ed1dcb51b2c68a0de6db91b7fa55dbf7b6b24"], + ["030000807082c40301323454a81c892c4be2adba2e842831b7d6521de3a408100bfd6f33d9e3d9112803000000035163acb040581002a1ff7f01000000000453ac0063a09d5b030000000007635151535265630000000000000000010000000000000000df53f40200000000eba195044417bd13060799d380575181741be826860b02d8d79c8b41026c98cfbadfdcd5eade947146fcaad1820fa364fdbadf458edd0fe730f635e1ea1fb88885e1db7a1cc8ee13a233c87bab64a44da52c3d4da88dc3ad2da76fda1d0664ff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000674743d8f8da8e9f6cd6fac3efe65f1232190fd8e18a86ccbbf24e505ed95ab4b3951453c7778176b451861b5aa1af004c060d1527df5761593e91ed8cd5dd9526a85c7b2e9dff6783dd17c7090ef815c50290dc3d24dd0b1ff14701936711474fcbf61102506a4f617cc785fd330676e1830ce6010049bc259c5d199fcf10f0021a2245991615a5bc5fe80c69a15720d2a7dab83423cb011f3029b1016a1e8e9c021f13bab0e6e5ee51276df2412627f903e3edbf00332650c966d1010225db51d70b0689ccdd9307b59c67cd362b71071a38c83a8ee0a1f516f6c3d88d3eb1d511a1c22d6e29bbe239cc998fb0ff2e8bcf9ff394edbd4396c2167479e249a0307831020fa3d1a65765fcab35724051f0a9fa41d91b93f24d2889f6f9872eddf77f0648031aba14195a13ff4804ba4c0f015f18a7aa50a3d3d6820ec6df5252d4a4091c71022d0d91ccf570012bf04d7f8ac1fa039a835809b819c2dc18af8f99a38038b90503111983352dd11e5cb30f79e87c3fe3e117ec43b121beafa6f2e79d39d2688c2102189cd5f477ea923ea4efcd460f6bb9e0f4f920260606553ab3fa593b3d8ffedcebac457d9200e10935b43002571ad3649de4ace83bb22d4126dd3f2b54a75d6aa9f994b52c07c448ee14375debedf6c1a6b86524cdc493c1863be97e59e281e033fe0984b5714ca1ff049c3d6a827d6d164b91dc467e3ff87fec13a08ae16de693e2cf7482870a64b62c5fa617a56cedd1de075b140182e02e68496d78fa942f213cf1b078b5971cce282c67effb194d30b714529ea89c8d60f4cee87dd399bb0c4e2ff96488bb25240e62d1c2a8f9f0c0f049d42bed33a37f8d5e55156e94ac5c9194b93bdcf06a439552cebab0e7bb7db7712162c131273cd5ed294a1760a130573233d912681a901bcdbdbb421512b457cf2eea60c0d95e11f6661a5c8187c97b0814f0dd1b810e946c2823d454585a30a7279bd6266d1dce30398e8ca4e4f0f17c8ea29fc5c724da548866ad5ae7c070b46b0ea3eee4f1bf420573fbc1c9db5b20d855f5bbf4ba92b25e40084fe9012f4c1d6691f2fd017928f62facbe8abf39967a6c1903b035ee70be88dfc048c2a3f036a2b9fe6c956e3614db350a9841183f24242022691fa2e6eeea356fec0a7e5001bee7b365b98cafa3141ebe5f12fe42500fe5d17944d215ffccffed8d19fe4073374642e958b273f2c05aa9818c2fd325f474d3a5af698d8b7123c66f068078f08d1afe48999043df014e0efffa7c3ee2c2403d674c39fc59649c6689311841abb3f3a9720d8f5dc0c7d5a3a3a58a709fcdfea35c0e7426fc690458a1b0fe21f54e1877c5ab6ad5afdd0a3746e81f5e0e115c0956eb7c0584fa218939da728c07ce89fa266515bac55cab0dd0b4c38975b5ba0ebd6b87b75a77a8a0b4d2b88815c82212d9df8d64266d6ce4898cbd4962dd40708138afc9f2a4eddc912f0782cc8ffc12424fa8ad0b02404150b048115342756dc240176567b88f453b2e9dd6b16ef1561c8278bc6a16031a1bc1193b15d7ef5c5dcb26d5116ad857e8cbd467cffa11fcba86aeb6b38b02abcafb7c98c021b894caf7bf27bff372d3d247249c5e2269a8c96deae37ea13c16ecf668ee249b424231db40a403a897ce13a7725c02384435633081ab3f711bb61a321004c272977ac1cba5138eccea1cd6b2f8d493191ad61c19b242c8b379827d62705300635c685caf9c1fe97899f3b77fb67a9d8ff779a247f1408e9d530ddf87c336d033d678736564785b2cbeeaf164c91de1f19d13e98b95e422b50590cd0359844e2417735b6c89fe0455506cf3bd2948c857d2d5ffb7318507c22e6ee9f81fe1ba3d651042d9fa888445a63aebd4073ab94925e3d9e4984b9314f08bb9ccf7d2877495bb8adfec98761749398d6d56ff65874b51140193eab259fa74ab0e7078391a88170c6986f811ef56d20c3e5134e2b2e76d8bfcb1a898b86ad8558cd544c2d0092eb2aa736466d5059b3188d914b150ed7652c1c52c71848d63f79e39993ea6a1042abd7bd44840bafb3b6f173d9888e2b4cda5500b859723828643b1f3516a8f2994c6c986ceda244114b5250cd28d71af8d56116e3f177f9eefb7aca44fc4fc0ad721b8deb35d6ff9eff9329910b5f27baef2e5dd873d5c216e8399ba4cdbf8eb7de09750ab93a589e416a1d31048d5d7e507872e0b81e707e105544ec5d04ebd11323ec6cf46cd61338622a2f9e479c133ee8ae027e0cfad134b95036c0627affccd4c605fe199e2a8e2c50d26884ee677629d8eccaaaf8cbac5d125e4f0ed3499fa860e324b790b38d8dd3488b81d0d95282ee0a672a1574f6a4cd1c12e76d91379e41430ccfec66e442bf2ca063c2b0e89f446896b2d156e3318d1e6bd5b0b7c87bf6ce93e8e0587b504", "ac63006a63515263ac", 0, -323886620, 1537743641, "c1bed08e135e72cb08899c11849fc5d743a293cd911f25e55fa17d7d941dd768"], + ["38a78b2e038676c3d807709c824052bb1a966b32dd0cbe9a06971ba5a489ef47ccaedc7aa70000000007635363535100000d3aef04b1d04bb67ab5200bc97454bb8c6b5088e77be3fd1ec501c8317a3c355c96ba01000000000563536a6a6ad8531d772e4f92cacf5f5d0bbf2ba455a69d8afe7c49287703ae167715187fcc835b7a4602000000006b0c905202cfe6430000000000025200a21d7d030000000002510000000000030000000000000000517a0f0400000000b04d90b069cb96569f5032b07f733e12865d998af1a47213d79d2b9ed19df75b2dc7e352304f7aa8a93f6822d3ce6cf9dd4cb7f3b8e256af6fe1e9ebf230e86567d23a2e4caec7c3ec0d54878d6707a797d4a7604d55c06aec8da85d82feb76c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003c47ddea858cb841e150bca736f08c8505e45e532153a3c336a38636470cd2fa19faba1bdea39e45e4feb02f2ca134773c92a314e499148048498784371af860569cd239332230a72fc798f0d160d0329cab8e7662eb5890ea274d08ed0be7989b9d75f9060e36fbaf68b5da5ec12c9fc5e6bafec4aa0334e96456ec004b52b1031a470107cff1c43440475c2e258fcbd1df7c6ad012fe26b99429eebc0d2fc4d10304e496d9569b63954fe4bd9475936a2ce5d2356d3a3c18573bc723d2a98f459d0a068c97c387680cba1861ea69419b5488f618e25e68e08528aeff25bfa0a46d0db2a79e26df614a2456829dddad1507451e60ed36d9156640507979a9081e61d102239d9f266e13547070b7dbdaebb80eab93278091936a7566e555c7e8f3b36adb0217cf4b266b08c2267fc8e25ccc256455f92c94f3d71b81e0fdcd0f8e55f798ac031190d9a4af33798dbb91f0064910c74cf581508eb5720d490449deb6ebdcc7fa0229dc2d515c4447dd44aa63ec98c80cac01ee2b51243139b6cffddb8cf7710afe02060482b34304325cb4fc39d14d7997d163d034b4d6711c1c0522752b985d2cf7e00c0b61f4dc0f7602def2dca1e77febee12a9cec44e1f9ef280fb95d0b00261ae3b25e03d50a26a0cb3ddc32a0c71cc5fefe5d8aac95c7d8beeb4770d50c3216bdc75cb69526814cb9f46a1df695d2c5cae5c793c63ca0bd5508cd86d947024cd9a1ca6ce66612c08e435e2b455fff198b7e92259b7f503bd421564ceb7441fe2060ed8703d36869a65aab297ac93d19324b97b353ecc8f5cda1685c3cb51c642850db0a6f130ac6f9001ddfab9a4931aceff8bfd0a36d553e0e4029c091d0b11a03eda8f9610dedc283fd31fe46b7b6c9e71c0b73e3f5c74691f2b5c480bb198128d41b28105cfe2eb9f2592735b50b5694d00070a37d5c194e95ed35814c49c52cb008627a967162fb4ca846530ecd5c9aea640082c941ef66f2fea22e241fe90c7fcf2e94901173762d166fd4e39ec4c92cb2d28f9933028ff12247d86e7f5d7833698a6857ed2da06343ccf4e99cbd173315fe361e46818fb6e6aa966d3863273c3af2f8d3c61bfc811bdcd425d552da00d846e9b54b4123305d63eedec55c415bd46167cb9fbe08c995b4878ff8d818bc0ccfae0042582fbe03fedbc384e126bacd9a6317041593dd505930821e1cd4bb011f708adfd63ecd8b01ac9827e3a1034de3cff5869b78bf40d0d883bfec22cd1b37e94b2b48a4f95643fe55f007f9e822f73fdf64561ec56c1a7312718087f7d7a05c5392144b33d0afdcf650b58efd118678e299867fdf1e87daef2b3ce2b6033fff75326fb30400412bd92800ace7df02495a1cf092473e77e6917a81df9037b5d33e00fcc7903c49d2262385dc9cdd23b89b2c894f75aab0ea0e796ab8a99f8cd2f1bde64bcb940e5b2f01c47807a581fe2806abace6df1ebdc4cec938981ad212445906ae71ea5b5ee98f35337fb9585c10726e957134c8380b885c5ecdd4fad2bfc19d59fe06592a1c0d3b6df9ffb3b305c80828a95adadc15f880ce19b91a5caca1d8fb0ba37e96bfd6be6cfb5f09c3ec2f08a6d41e0f550075a7bfb741f9f44d7ab1138d50e31c91f09b14d6f2af439015e9c5eaf12b640e5ea381a11300bf395c279cb5d1bdb1d3172ca2a652893c9d0b6c32adf343b6386eb132a4eaac258616a58d52b9e9f3e49725a5bed985fc962a55a3bb7ce73af0b4c523c05c0449cf2add5322a29c01bc45fa3700e9e0fc51e9ddacb24235165c7a87e09226a64ff6c54cf9779d5e835171e362e929e5b33182a67b21fecf67618d4d029b3f52568fc77532e44b090843cc1122b2cb3775786fdfe8f0a4f6cebb9317e55bc35e576ea9b3575efb3803488e27697e91cc51d5edf6911014e57eb3bbd615086813b244eac96ed8c7076856fc464e7ed58e6cd67770ce2bff78c3eb62468d70a29aa200d88957e2e08d6615115d6e119ec2723c703a08f2db78e6e322a6174df6b5fb320b3a24d25341e6d692cc1a328aecdc757b0588748e11dfae4e124e8650fee909c377dabccf246f87da01db1ab7e4e89947662e1d01fceffc38f8644f36bdf3f251c530f67214e3be6e52dbf8b8ee3acb51391ed56d97dfc245ba2dbe85b2b26de0e753ddb9b31e664393b6e8d90dc620df75db7f484cb575f831eccc7c6802c47160480693ad53df43bab729cdf9a811c70a2a4624e91c9dc143cf136f117fc7ce719ae99a9f30af825e97bb6ba765ad023be2b2384e48a8010140000000000000000f8e638020000000072a04fc7c437503405c6aeb7a896241f426f8b9815a8763c48834415efc933251b57aeb05a65fe81c7fd91175ee57f0be0927ab584a7ffee86133feca4fb5f0cac691f11fbad123d3e7b3f1eb662fa1dce49bc5af8b7c2e0e32ed4cfcefb6dd6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004e43687bd6e54c1f83e3c0e710dac9ac0ccc186f228e35558d0dd7e70c33b656810950151f2c09e5ca08cfc0a4f19d8ea2d672f5d3c868571e49ee7d870447948b794c1c76620459394ce4bc3723d10c08b075f4356a11d2d4e3f935af477ab3ce4c707adfa2504aa74b624ce303d1bf2d0c681dfac0b8988fe9511b4fce2c44030eb264682aaae26e6167faa74445f0aeb42ff15c1100533bf7f625e6f73ea179031ebb8eb6cc0579e6b4637d5acf0ae21fb017d0d18b843baf3ee2e1a591096f920b08686219884fffd7034dbc65c165a7df3de5c24f89c02baefceadd02ac976b38e73add3bf0e999760039662b29b326a1c2ce92ee174eb39fd8260096719e4f50032b02adf7b6932f59aa4fa01e0768d579cbf7a99ee065c6ca4e0a84433bc678d3022e75869e03df19c7ee98d1e71b8e44a3be198c647d0c5661c90b1e68292d9e820216404adc943da876c846b8725e3922343b941ec03a73486f90bc11a9c525c1d002205fb0688c50b1e52f8330a4076988858063aee40891d73fb289d7e470df0ee603293e11cb5518869ad8f2d8c19f90a8e9f1ed41eaf09ab4cab4e21d53ea4c44691b3091d2bb98fe8ade0e9d4f699108ec32517075655ec89bb3cd4740487c445da10e26dd5f6b56ce186df49c24d11c8706e6ef573d24613e28110b2be9cd9b0325274ce7b18498d11286bcf32288f3c729d84f70d82ec20ab13bbe9cb0835d84771f32b256cbb3e96d76c6faf6dd1932d4feb4ffc76497a5a79b2595099fac3c7d271d9a50b54ddd740bd54a729944cb7c248de5c7b10f7c9665cce2aee7e142582fbc3f96643f5d5b4e1f65d0c9130ca85936ec750b24e963975215b32ad509a96afda4440fa93cde0e4019e98611beecb12f5bb26f1781f6445d8723507db476ca7deb51dde479dba49e80b271edfe4ba52f6d957a9e4d8eb3785f9f8bb1468ac4443bc0790664324841407c69d6231fc69fc772d7e04eb6981b1d194fc0d729a0c74efae8b9af41d747aea7fe3df0a6b6c918bd455dde72920ce31991fcbb08dc9a9357b3b06fd9ffcf93202a6108ca740e3b6b68c07e43737ab471974f66d8c8c7a615d87f5b2371dcd90174c9f36ec9139db3b319a252380391e4bf34e04e44500d74199d510800a8bc8e8b23c143a8bac86eafb47680823a03ef87119712e6c7b8246a58e64ccb55742e0034b0b6c1347d83cbb9c62d910490c9b9f7913c6a2f902dae2f02bd74cc8c20fe258d25401a775f0447b222f82b3f0540da07f33a5d7a9187c920bf654348865119a2f71f7d747717635e7038a9475af56460dc367d9d2088f224a5dbd5d90ab7ddc2b7afdf8460c49a1bb04df326bdad6692e1dd2e85272ac6989514dad7c6ca2ed18d586d579e741fc95dd613f720c3676616c9c9fcdbfa528e03aa34ec6c38697a4090110c3a2b8f1d47caa2d25b6a2001e862e29ba483952f06cfdbb6759bf0fd97b2f0206a0fb98719c40d80ace7544f319f7935a4b39cc82550f50921a041a8ee761bef264805f0a22fb8b5bc3beced39b9899685e8bb9d8565b588c515bda698997e62dc9e71fb71a503db0eee12e0bcc60b9fa16247a32ad5387f2fb5d53b6c44ab7d8e509d98542635be08ff8ffcf5b482c9e79312b3e44e1ad773eba3f3fe5ad30d6f15a49c211fc4682932aad060e4281f89eb867935c19a930d8242999fd077da0fcdffd572c874d4f134ee745b5127133fe66f54676056e6f65b7dbc92b2ab46d1178608d791b5285427726922b9e721ed98937048175171d704e4619e7041fc7a1f0839425948ee78c96f3312fba6b747f0ca7ed99a3999b819b4d5beb55e27894c65d8c01689741a221ed6bab4d165d7180523f0d4f3015363dacf53610cf8367f1b78671a627c31cb4a6e5879c0181b49a0f59919aac6e8aa34eee90043df65bd26dda3891e4b37b4560d9b8f070bf1c033d6e5785933aaf1ebcd55a1ce76cbd3461c1c7a94666243fd3c368e5555652b1945a537e2a51a470b681d2ec1bfb02fb06024d7ab8d1532d13e7026ec94eec266c061a329580bd60ed4eeb4054461c5793cc2897e8fa5075b774895187926573c6ac0bf5994c922653ba1aa56a7f165d85ce2acd62740d0c7b6afdf8bedeebc38f303e1002f15e471e6f17126c2e19fbd2f81216e16d0fbc9d8ebeb811a29f3808b88c9eeeffbeb7cc4f0e3066aeb890228c39f05f694e12fbda3516ccaab82ce871720a82244dda822a089b876174e71b46a501bfd6333473a24f7c9bfd939d9a828a7683287c22bad1e81215ae30500000000000000000000000051cfd4205460e48b5ce21d8ba2d5abd1deb1ebf1a2d316af1c46314526a2c8e3dace4fc0d22d594b25aaef75a3e5ad459014c10fcfa07056579ca548349d6235b2bb22ea53b8a52df349d61e05c10c5f48501cf49932efbdc385a1b80b5c2280000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009a2e2d8244c864b07dc634f679fbff146aa1ccec029434229af4253cc233798bb8309fc03ce8ec54c52190189f96d821c0cdaadada4e2ac12a2c3bc7b11a58bdd69cdc1e4169ccf50930553865a385d882494e3899f77539c0bef58d9cb5e326c532cb9452dc3e5bdaf6285a5b1e1865b318e8173ba7e2e88251ad2a10ea54d70200658ed2c57e548178128c00087a7534e163be7b578f8a58b95afc122640454b02026712f162ce735186f8c72932133bd6a3e8885b76b6d5b45d51cd9758690bbf0b004cc5736b8f5cd464cf361a20893a4af45dd1a9370a95c8324d0fe68efd3b15e88e7898bcdb173eab7a95a5517947de66ee5beae086bdae0af83e674000337d0226b31ecdd0c1a6dc9fbab54fbeaee72aae39a31f40caef6edade4dc998906e0d0310fba542a6e44640aae278692948f5e78065104282cfa8d3587350db1fc1dab20322aa257dafde9f56a149669cf41cefe3bde677f910d8b4a4e4ce270071dd882b030643a178c35a885b80386b26090ec27622808fe7eed0efecdbdae5ca7733e7300208de90697b27e621602d7f489500643c4a198accc0f8078b894834bc1553634285ef91f38a7298a6230a111e64c42e97f11c3cb9455861b71075927f1ffb0ce74eecfdc8a372bd89d88705907c656e38ef42e333430fc0e81ff86d9ec3ef043276fadb3c17f7c77a0a4e3bd70ebc5a26651ea43804c8c45384a6b6082ac493522273b43168dc905572cb90a98dbde88f3892445a8fafa366d6821721d019347a0001fb580f07bb3d8e73304472eb909bc75d845e37aea8a03690ff6bd213689543cc5c7bce88e6dfcf5900c25499a680afbc293b205ee6e2ad37f8e1e02f8242b03e15fd7c3ac0cc07af3f15cf9487c6e6fb65fc3808d6ae8b86a87360f2dde60a54f17914c6ffbf13e18e6d3a0b5e35a3613d2e58722f35478e9af03b59eb2128c48743ad2829e34fbe27a5b2e3034f5ab83dcde2eeae02c0140b113e6c829b43b2b9c212a2fffacbd4d69872c0a896245a51be299dbe2fb07a1cb0c9361578f266a8f092fa143e0ae1c3409456afe9aae95b21fcdd5adbfd921dbc5e2e08e600a05aacc3954ab14ba8736364c14f39b91c0710e96b7c1a5157face0233d639cfdab6802b25f4fabc6cdf924738b6cb0dd750ed104d0ca3f48a8db50b2eff6bef7661bee53e0ff2f4d7f114e245f865f286289d6f1e98ecfd179861c0c7a95a6b98f07b1dbde89519d6806d6963a5af8e489705b45503df1db903b37294d0baef1885c0ffb4a14c7c38e0fc57b50d329b6d5f1593f0ef877e43f9d1be657b7427a88a6a6b7e7c0f224ba0db85f8da126e5850a5c54e95c674fb9baeda86bc7829bed8d5114cfaf0db9823e64fdb904a3ebaf286eb5966b08628b39e21d02fc9aba3678e9330fe452684d2eed28bfbb98fed70428fbb59386c140f030b9c4a1d1387439a4733545acd931bb438804c6f387fb39b2d874b10c0dc88155b80ab8b973f3b1c0378cdfe1dd3cc4d3528443371cbdf5162d5d1f11a16ea6fda4e812f99156f45a78e216e15e1b6f484a341f7cac4f4c522c46cc362d178977f2651e6872c14f2744dae0be784a850ce72cf241e5942d19cb1edea781aaa22d289a1d905cea38e916300045825fd161747b661aef4c00747d20e6144c77ca5a0d17c22810273736fc5b66d93cb04763298bd7f591124588cc4e04c7eb2bf9668fa338c9ebfc4b0cdbd2e9f6b4cfe9bcb563306ca9484e0a3313c040cf095de18f38a2924bc69e63730fdfc4efbc7e9d810c0cf607b0cd517cfe81519fdfbd90b1fa6272cc0e283f167fe5c8588605597f9f6302839d533817bdf02b17e24c9a238194aec4c7ca5dd3747a6a4d1e4ff77bb693e76253c1313cb1059178066f0b4c19936b3af395eb72973a8e755b0bf1ad6e786e09e79259544549bbd2367532e64433de19d2a7d4c2f74bc9e8c4e1785e776ecae19ebaca8103dff2acd4fc3f44da8a7b7db1eee4d2a9e65d0bd46df3416b3ae085bb7c13fc77bb0224181c19f43613a8b64a837be636536167f40bc4a7e9867013070f13c86f443ceb11bae3edc589042bb9d1632d66bda68d84757135680825da99e9c7209948841f8d72a5ca2d8813f086533237f4d182cb2fac35fd71e82109be76cb4723f43e89b35af60cefb8b57688679a8ce3a4222371223dd8df69760286bfd290f46ff753948c705c7028cca248ead3d72e45a8d166864049a46995386ddd98d91ccca48350001a52c9906a9ff2696c09d97f06299cb771b7de0cf8df7c3a78deaa921bf55a4be8c4fcbe9b9e860d22c9b37a5e75a5400e283cbe3837468a0d7cd9b8652faaa83b736f5310b3114664fd62261a976c09b608da95ffbd32d5ed0dc9f1c1fc33fd210347defdcfecece679208e9c651aa0b7b996b5a8a03", "ac515300ac005352", 1, -508906326, 1537743641, "a53f087692eea3847fa20ce6aa3eea8d57226ffaa4f1f54b006ab96ae8ec6bd4"], + ["9047211e01d20367034d47dad57f7070311d96cfbbe1ff78f3249e6551038b77f65c0a5424030000000951ac6500516352ac00ffffffff014dba15000000000009530065650052656363b944bae8024ace2f030000000000000000000000005922df4cb6ccd1416df4d4217bd1c24fd2101603a5d06f0d8fd75af0a75b40441b44973d974fae65a92069023c48cf616e693317886bbd5e611538d3231e9f14588ae6d5ebd0a43957732dbb8250979d5c01af4fbb9d1e15b7150e167bc7b151000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b23fea1f1bb9b01bf5db753abf818319c0cab6a9235eb86df9c4b2f309135c0cf59b633949d195b14394a0f2b538ca101faf8b7c90122876dba9ad37a9338fbf353c7514e0d67389f5467f3615e9b124de24529b4855fb66504012e32cf6ca2fd7a9693af45d8e3830770a4913f5341298df63bde447bee5f1beb29c4da365202055bd64afd80571732ac4da4d99c834c0fb60032de7ac762c1e388a74b868b3c02219896d1f5ceef3ea08574736f218ad91eac19ff564c2d1d88d0782ac0f6b03b0a0263d24d1dd786d8f82b216aa2342ab64856b3243ebbf18aeac2ad5186be833a91b6af3ad9e601e75961dd7b76df20dadc6d5a2d2158ece764d867c57edf5a48031c67836837b03948c3c50c6d7e3c6114944720f12fa3bf12f78e62d8fe41b7520213cbde5fcefde6b14dbe3f2ce2383727c06bde604a1d7c360dc6927bd2fa74020301cd6de9e859b2f25dab30462307930298cd0a9c6c0a386b6bcea3749569073e0202f04e826db8b42619cd4283315b161c8d1b811ca992f131676072410cc47e01030288ab09b5144c52e7abb3476f906c81e16c055cd9a345db6a963ea830b5daf7d676848001bb99785f4e58c9006c287f36cd09e3d9d7e850f1f3ce4d2e18dea3103a8b20aa5ed6001280e5f2caae2c53695050d444ea2a0e9c8ae683a7155084303706e66325c563c75a4885f2848eadfb96cbbfdd22c91c0aa84d2837370bfe476f18ccdcc2e0ae9baeae1dc4f87ebdb94e61431df1fea079cd934d7ccf609c710fcce6537cdbf5feec3c405aa44fe579bcc2e6dd17ee52a6acaba644f509873eb71f5dc333773118c92d8a6c99c9eb42fdd95e3b5ccbc77015ed3cfb56a152f2eeb2fc59ee65dbcb3eeb2ae7abc96fa820f59bab0201dffa94651031c51a9741bca85e255722d5c3ec45e7598decf981bc709a6aa8077db609e3e17f97b263e374f462571325aab37a127d428b24b8101b06941da4260fd30b6c3ca20d91876318bd248a8d01b6376d706a8569e7e3d995d444ade62cd3827a6ccf1ba358dfc3570f48126504d82eab74b1be5fe1d16ef234779f3fc78bbb26a609880caf9e47dcd0adc126da71270fee202fbc037a92b22f704454a04053fc3d915f4d5534fb60708e687344f5d9878ef7e6f3c96df058d484086d8447aa43b4dcfa9dc08b5af4c4fe2181be5d8c1cbac31bee0e00f65bf7e756536e5d67153970730211801b03e2d4f5966798226d1e408d2bf0124ea34131f7f8446657181bdc3280103977844f874baeb101761814a5530343dfef66c20242dd2e1f5d20ad03b95ff88efbd68a9db1efb487848b451dad280c29e668850031e324d5efc277f03ea2c2e4b56b70c5c1f9845e9ed1ce05204636015e19a9dcc9206ad31c5e5f7734dffa657e155e95619c70f4ed42dbc8094799967c4ea01ecf23a503ce54d623582c062b393b7e54791644992a0928e33b7714e5cb5f0d57fbf335009ce16d26a1ac021ac7222c54f5e24223576e3defadced9bbd5261e52a56a8804b3272c2ee26822930b6d88a2d22c960094d21aafddb1cf562d477589e2739c21c82692a108729c106055b2245e9ea4c76f5792c7156bbfe1410ccf9037d500bf7650e9b9648d7f88e326932365743b06a4d3f6d70f552660cdb45e3a3de575bd2a26d2b4daa7b4d35c5881e677e758ad442aef97b931f8650d8605ce2193360a7c28cf6eaa2c694801c03457c3fc17e31c2c06e890ece40c3c6e2d5f199f9b11d0619c34504e0a74f19953c99709490c476daf1b95db1a120deb2fb5ddd0fe515c17f0a4ad5420f5466f34b004c0f5a170b4bfcc420d980cec733236d42e9e617b22c981975d2d7b1236cc42f7348b6f80d64d0347d7c9a5dff185bbfb5925a4e57d9cb92482afe5e1dd7b393050c723abac0ace48510542f1e54212a7b42507b1acea1389bae5b1b4af95676c8127e95ee4a7408a3be4a8492d0892fdc4fdb14d88f7738f2ec01d6a70774e1690f402a752ad69f58b866b48515a483b88e72d76d1c3efb41de6545236d337be4ff0b0002e49e2f139578a6df329db638be957da86122196a79046981b790d655ecd4ceee4268682cf4d807208c779d3b4586deb754a98266cae9bc2885c47bf6ace5edae1575416dfa9946a45393fd2333ae0cb289b56d96a8f69807b7b1619f070634890056cabd9ef1152182818bca10d84eec730fa5bdce47b4ccd662f6adf371e496f0898a9ac762fda01c8e587a1e354a4739f9652e97287d08b0d6068e35d86643bce520de1e1d65a70addfc2020000000000000000000000005722d2c8fe62079ee0ff8ad21857f8890251e2907d1bce95c6b908970b5cde3b08955e2d1998caeefe724a8bb7d2aca3de632faa9f946f193be2a1cf2fee63f27ccd7a78ec02238c7d5f2b267390103a955cf78ca0d2512436a7360ac7f4fcb90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000049f9d8ca461023e09ee3cb4a9b4403ba75e37ab906aed894941d9026e1bd39e2c4a6cb181ce55128589dfe5f7d0a2745220ac5c734d4867202803df09756a7cd7315e7d7b23f9dd9d297c0b5220b1475e18aaf9accf3f7ae888b3c532e4d1cf5a099cf40d6e705ec1e84057083416a3bd29676e2a0a19302782887bd6b312c4f021e71345372f712bc29136de1d8a83d5474fabad84f0d10b94b80b67971a98f4202015e09d9b9b63c0837eabc9f204ff75e97340a1da8f2660d960ec83e1589c9190b0278e11c6ba883f2b89db31656ff8ee0d87046314cb351a81d479f36391ada5caa90c7f32c00f9eda80da8bcfcc56e1b6c23142d78337d42f071057c51781f950224803b8f9ba1dba74a727f48b0d0435af2ef73b887d35011a768cc19c40ee4bc020019cdd2eabb471dcf0eb225e95636a2f23d3d58d400a036526a9e291e8facf4022825cb2cb2b2a543a6015c8520e27d88e21afa96a3e6e4c34667c7cd17e21cfe030c534aa0ee6074a207eb4d98a1ba245c1da049442b94bb9c7df325eba6edc2b00307fac3e2b8c32b5b981ce2c5f1ebc902ce1ccd6a267580d24605ccd1c5bed200cbfc168ae991412dd52c0e6645b954977a5f3b34aaa158bcde146ae2dde754acc6a23d3e95fb9f214260fd1b9ddd3018568ad0cb66378bd845b15d98de9d79ec8cfbd6b068569589c3ca1eb2a45b52b7c7488aec9576e0a67625bdc22c33e89d7494df4c28835aa3bda4be6447a232bd9d48c88ce0c5e7a31db785a29d1a259f84fbdbf69fd08fdde336b0e8d488b244da0d3d3379d69d8b6cf8b962fb049a3691719ae89a3d36bae709297b2afc21c25ca2d9a9b0cd84566ca091b4ed54ea27c6d68a29ef1190502f5f6523d40f35f09614f1d0e574aa13a81c07e2bd11d1e93e08b79c5dabd3724221c51967f452d48b551a32382a3566bb186c12b067df5ea152ef4945653ccfb6c90fc105d857160741a921bff680bfa7b92b2f4ec45b81a8e3deef3f4dc2077d2ca28fc716b2e9c377917b6931942548454e3687fd9cb74387f5ed17d479e1d4ca19d705e806a495e612a604bb42f57cdcc6d802025c1df335f1bd8825726f32014204a46a47db4a2dc9cadaefb61aa88c89eaf8c9ef7fbab15662c71ea396def6679ac1f193d7b3cd7a862538f8895f779b79863b1a4ce170669659c66c4f62d892e75bb73e0444dfd4cb89c3880f4b95234adfa024ea2b6445fb30f8d419ca20d1979c48e83fc5364789c87b250daab9c2255a1ddd53411e41ef792b493378374c6d3474990b2e3f30f0c592202fc47d93d9b5ba5052f8b632a33628de71891135ea71a9e1d2df60aee0e3540d5fe103c09da0693276e6b55796f26acdefffb005dac9cb128fb30f8e390e3194f2b89d720e2d21b07a896e20881147e15cb3f32f7dd0cf92c9a2ee7769d9aec3b573f9faf7886722b926d825bb9abfb655f7518d5c6a28ca53033f32a95aaafa2139e7e2ae1120737c57c23bc0deba96770f8323d1cd8fd7526a6925dad8f139bf6ae7cf5299937a017071fd3d490890c71b67862239b4e94158c83c6b1d7798987b3a4be3a335489267c83eacc9ce2efd5a7188a76e211b8ecefd906d955eaa2233b681f8f8a97ff2c29b28a74e7fd4655a6456df6c6312a8b8b486a77a47edb4351278b474bee76369339173aa9bf1fd9cafae1ec5dfae77e4480f030d042c3187ee2963d5af896d83b8c5417bee0a31925fefe4f669855f3b4b1992dbdfe6bcc5732e28b7f1d00dbc66f9e30302f9acad7b9f0b1af638c73ea00e1ee4aa08700ed356a550ff43e5393653c6dbc8fb16258263a2c39c2edbf9030d0c81c8d65638a0ee3f3e82f42d53fa8bd6dcb49feed818621fe6c14dbfe5a75915e7d0cb609c02123506398f8a02c28aecebb046ad1205a50d2baae4d40f0871c08360910fb4c72ceb848e2efad2b1bd5811f1824864bfc889a2b8e9fd8d55d2f0d8df6447b6445b4193a84396049016a92b12e6f2e875e9ef6b418cd1e4e8eb5525d7463280d05ca8fd070d8a93ed91d63c1eacab786940292d039be82575c00985def482d4dbab019cb02baeb625e08e403582150b001b71a09cff42388cddcf23a931f393feb4726226dcc67e7122beb5ca1dfdc5d63a2d3fa27163921670c1f50ad73d33600643e03b42e0cd63e1591d4dd91a8b1a1e1294286c16dff783bdcd3a71588a2f62cdf256adc624f3819b5451e0e98198c5fa01accd8fc0cfbd5116683c78bb8aca4d8638a1d4de4107a8ce9f75d08ae982f69d1691d8c6efa2811daf43f6825520bb3e575bc96c1d71c993f02dc21f903fc28de0860f2e493da33eae990bb3b81c1b319787c090cd71a07e858ddaf479131acf5aa8bd6aa42611eedb74dec22be4512b2e93fd946fdd438ca33e02020f05c5822f6eac300d", "65ac52526a6aac", 0, 1579082880, 0, "d4e832811adb075b817033569e6ef1b3ab1c56666378f3977c7801094f0288b7"], + ["a3f49930022232123f07ddccc9e3f2c3cf9bb97af1d474bb428c0258457fe67c197e64faa50300000006536500536553f7179350ff2239485d3aa94f5995493d2b6a0040e07e9e2018d61136a82981c73d1f6e5601000000026a6adbacb76c01e5166a030000000003acac6321b16e0600", "6351635300", 0, 85048373, 0, "fb9939d7ac7e2cd4b073d1d79211e4b1ede102a3ede070cc2e84855fcd7c6ebb"], + ["3c4df75702f5bcec7e3c0af5eb1d55e7170aac0c3eadb2cd2acfca9401443840d6c490441e0100000007516563526551652eb00c6c1c7e318ff4c7b9ff97d451512b86b2668fd55519eb77f443d209657a23ebe39200000000026553ffffffff04e643a500000000000952535151ac6a536500950a9901000000000500525100ac15d09d020000000003ac0065d3b83005000000000451630051000000000200000000000000005ac9610000000000a3d5844bd251c74850d36670a4d704d7f6f9720609117f4a3dac53282fe386a70ca393747c88b3c160b672d80749b92d0534ed735f8b4fd50620758bd78f5afb2268c8b8bd80d9c8bf70bd1d5755e89c9e536a5303d3a48062ceea87d2783fb100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c6703c66929f9697e2d5eeab9ab9e51336e01f343e9a17451e031c4e88d664b691671ce55274beddf2e9a4d16a15178e175161f40d63cbc8fba4fb1f0944b3700ae79c771c1e7610d751fb77352b090353e9189ef8066ab96cc713531a3ee2a46ac5f8a1d723886ee4c95fadb174cd22d4b326b82caf3e43b34cd4aadc70b38f02100a4b4078eaaf51b5dfaa6cbc2e98018c250ca10c72ca29e0f70a230b5c0d18020473275fbe4ca2bcb5598a4373d980f49993f917fce74ec36969e91f6fe332470b0439c4efd5447089e816247588c4e861fb1a34e49d2e60bd0024b2e78934947e02550f7d35b6888caffdff9bf07df769ae2c48cae07dc7adb7b639b0ed01924c02098ab7080d7e5dea5a7245a0e73ba65785587ce492e8117076e4118f4cffd91103140dd7e97bf143885e2db6cdfdd9cc0e57a5f99fd8767a9baa62cafe1b23f579031e9e828e053e4012a0cfa17acfef3edf48542d765bdfb11b8ee892350fd6f0e2031038890fe337fc9806ded284a2866b936890fcda6c27a7a28c2492083898c4bc03067450c849085cb67eb232b9755d13bb77634e46e3ab3ed4106bbafc02f538a75b7c6da2479863826af256649760cac7711feb390cc56c760e9d64a59278d76cb180c2d52414945c44b846c1cec48432d86ec6c6cee2f58f3eab88a55f59fc86f1771d30e66d5beef64c3ec368fb09997f572a1e451ba1695ce74452a313015f99100ba144f74fe1d953dbfb4c15c3165c441cd9fd20169121f106ba9218efd649898d81b53da821ac76a41af74072c5d4aa83ad3b29815a4ade35a47cbfa140c9487011ed5a133ab1e2905abb9783bedf98773888ba6270c24513f9de44050bbb4632c614b1d146cf0a6fb96697b1b729618b916a1b6c2a05ff919cb646267e9800866c542f4bccd7876e7cb916c3db7c3f50cd1878a73cf09932b2666261e5b21d899c30f6099d967000425796fdd7a3baf6e0401813be06a22be6e368804d855a03bc048df3181179d59d3f3c9e70ffb1f2d9a60cf2c246fa902da531df63ab7d2d9173ee150c5f1af7044cf05ce7088cbd74c72b791bdf3899b3342edd8f0334600e9b3f46ce4ac84cb443853f7d647bff6a5933f0f4a2be62a835742b88a8bdc405a6a79b600253cab902309c4a81411eb40e227ffa6f969bc24fedd2232b7339a3c5cbae75d1b78a8bc1f0f3de6fe65b08bdcccc135d63f40bb059e2cc6570a89d98b3e1a35174fca15a94ce5a2674573772d1053d8e401ec66bd09fa6afc78beda46cbb377c8fb3bfa1ea7282a81f087ce2b77f9a574d4237f0687f6821b946fe2eb71614a47fa91bec38c943217a8cc3f765a2de114813727180bc97a5b777c618e8ae55cf44daa3aa0b9173136ec48719d5082a5741d5fa641d82f69c816c61a28962d4586261c748a9c740b07463d2d822b71f360ddd1e8f1c6e8fc1fbb4dcffb7e8d7aeaf7741bd172de8d3d0b499162d0f216dc0a64863f784658b228a96c12362bcacab3f2e2935796327b2e50dd2fc6356644c6faed61fc8300a1bc59119e55ad559e5e49a55377ed41fefb38782ca9b16b0269e8b2463a98261d1262123815e83b02cd0f23e469f2544d8320143f3ddac89c96e67003ecbb130cdab2a36707f741c2b24c9d330fcc2f0e4ae8624c77a05ebe7822c20dbdd871e28646b5d9bb40939d11b010538b2c24ec1647bbb9f1cd97c72aaba602efd77b68337bb8a233d75ac783536de43d634505ad7cefaa4fdb68ef209a0988e094f11b03078706d7ddfc252d5db3d8b180c6432632fbb6a461187e8c6cd7d55e0ad22e65e0f3bc6cf1f7830091c595c678caa8ee8ade51bac7310b1c39f59c0c0c31e5b799ca3504ed171e8ce0a4eb858a98950a929882d89919cc5843ef1263ca81964b4552a801881d869133b30cd56cea98973fdd70947deb16c51abbec0d18b4a28809c4aeefa6c17c06fea980dba94f858aefebf2d0ebb8e82747d1de64a0429635394307b42dd96c1905f0c34f809aa048767b0dde494754ee05a0588966cce3e4654229445710134992790d835fb05a3dd202b70ed747a5d5c71ad42858bcb5af772c9271dd2b51fdbaccc27de5e307e41305057ed9cb2eaeac0bd01dbc049d0372c94a5ba695ebb353c4468c94767179117809ff556119c90f6c3a3caf2cbdcc97a795ae023671877d6ccd891d908ea97e6c760bececde1490cf3ed831b7c0ec19015390b5d136f4fc9461e4464b09e284f30b7ecd36085c8bf5375f538712f5831758960eed472a82145587bd3bf720000000000000000c97ee40200000000a4df1bb92312adb6e56c31a5ca26e91a86e5940f88c8fc71526384fcfe1b04dc35855061c25fcb706e70d941421c1740c5ec68048b0c934f769b3289452d1d01ec153dca0992f807ab2e03e018d37298a9690c531d207be763b7c94d5773d1840000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014b0375864fc637700b6af8c8eb7a326af36da3eb07e919b1cc5ba82e5df4e45de7c77422821ca7ebec66e9a78a0eabc2f39e872495b84964fd5887db2a9320d1923e4aac867a086f6ea0e0ec7e36e9fbed330142d0b2fb123a3e4f5f86748e30097268e2a30eb1369346310ec5eaeef0537dff70063d9341bd4d11094dad3d7031878dbe6b12cb3d4efba6a8a63977a4dc660b5b73c4aee94fbedbe0df39ded3302204627b0b028560729e467df592ba838fedac53105fcc31d369d8a2dacd7536e0a032cb068361e9f4e04f8e101efd2a6a1d6e25a0b584905c954746a0bc3b52055989f1824628a621e4cc9ba79aa0fd6107a9aeaa987508ebdae533b0b72c7d7440311aff2928eccf5b9bcaf326b86035f84a05746697ccd96916712e7112b0f72ad030349a79736bfe472af0df9992e5b9037ddb5df5ea04b1d2ad9f4826fd12c292302123c6c9fc6c4514c2e9aa6df55ac26bf5e8fa5d784b68b5d6af3947068a16378022df91d6495d8304a1f641696b770cc3a58cb4354aa7c88b22f376421991b6d69030e5c3c56e72073b7bd7bcf02e1651f03626e67cff0b0643e5fda53e8f57c2425e4f7d52f07f43603c780fa088a41aec2a4bf541012f3c42aac759a2b58a998ff32739f1bf7ca4305826fea77ae9516ef71596291ae59db67365d444dba5de08d1e24c3adc0c7b95337c4ecdd2d89bd2e0fa84bff981f5d3d060deb8c020f0c90ab86bc913212e1eb61a5a897b7af90cd7188e303cfc2cc51b7b86f2821b864a992d2d01df6038729c5b2e427bf21aa7e76b6dd7428fe84ae14c553e6e24f38452bb20461a5d0babe67794d9b6a684221338a85b20b52300c074b0a0cbdbcaf40d1cd65f9500fcb42df2ff62fd53f05d07cf05654fae6b6740cb67ae06d9ebe9655bbaf3fe8248af28e95f71743423eee5c833254166ee83514cb86e49524857d5a1605818b200a03073e782bf62f252a74b290efee94762b9aab8ab38c390a4cfd4e1cd56fe5e8c241e306a27e8a32cfc5ed920b608017dae205ea5b4970ab9b7916c1cf98f3fd602917c8f68dee5511bbd5c3db0f5b2a96c07c107f641971c667cea625d2f5dc2a9b6bfc9f8b7211c3d9133980bc230841067eacbde0ce164c4c516a3830dd65b422b38c2449f6dce6c0d51fab583fab406ad6077a59698d1ecb43b6f054cf0f0636eb50228d6975771a23a459e26f1c8889cd0c6c9f2019a9a031abfcee859a2b8fe7eecb31c90d42c17592877e95b7f52649bdc8015aa02e5a6e07e459acd4c01536bfd812ce8089fb92e9acdc1e65b61ae8b4b803673363a90ebdd5897a6decbdd2595f18f1e66be3819db8306dd64d164867b6fc245cc585b0ff20630171736832aa14f1c0f3438deda9e49eb91a0dcfa09a23cc1241e2a6d38cface47a9b7ee13f7d14e26ca54f22f907e112e167d17f0871912f0f1d7401b035b740461ab6282fd95c51a5fceadc62b5aac447aa3193d3a6c70fe150552ad382031f749d899200c2e07c7cbe88eea6f731eeed7479f4ccaf6e8236f7398c96475bdace06772260675e1efecff54351b24e44d1eec0436ddc5754fb731b544542c189cb10c58f091f4a8732c3feb791af5fbd7dad6d185a3297c44a52d7f8e7605410923ee9510cad72ea8675f31e84ea1421b21b39d73c8c52cf7bc67aefce9238fd5b0d9ba9e432fd20a69e4b156afe27d6fad04ee90997eebcef182ce3967de9825731a7bd85c63a3f27e83f894dfa4be0f0f4a9b8db2e6d30e2b9754ce3983f02bd6b7d0deeeefc9131db5ab9466009cc703926c56ce2b9ec50371ddbe0c8671e703a18a26c75279747b0e59f619f39157fa04a650838deb7795ea1ad472eaa14f117e94f394ae73470e765691f05019f7a0c309fe8d636d1097952ab13289730ff35e6dc8797bb6f486817c91db3119106a7db934be035451f5cfbb081a57ec625bad191b178a9c5ac9345e81db59e87debd2ed82b5968ec24f8b3d08a6cc8eaba4609fa2ca366d6827fdac447797029cfa30e0963c79a01aff5f2973da287d1941b3259c7e5b6714527c8e21786f36cd99a531e16dcd17dc0477ec3fbcfddc3fc0f1141999196959fb3d5444424b271e9d8727ad45705d65034a640ad65aedfc3c2c0dc97f74c5173ba188a6c2992a5ce5b350d24918ce99228bd8e422d53bae70410f3a2193c0c6366650fcfbece2a1802ef49e001b77087c506202a623ad3505075f3598e1f8e19ea60ca8d4fdd12de0319e95c57d20c59b80428077d6e695c7f76bb5132a396fefa0b3694a79c2ee8a698771eddee3143f2d569e349c482f175f54d0996fdbe1e18cde1b61f0e79e0f226ab7ea95563e9edf8403cfd2d130ce994d779d70cad27c0dda4ce28b80a26f7a1a32e26e1d69e5b662914e5a94fd508dda71bb54e492f685bf04", "636a53635200", 0, -499873455, 1537743641, "b27b028f89ce8bfceae30111c2766965494d8b96cf369195df28ce64def7c95e"], + ["75639a420312e9b901e4ff27ba83d059559084bf32552be8672e457a07781bed68f5b638080100000003ac6352ffffffff388583892645a1efeb3fc485111d9ad421e7f69017a7fcb552f9749e3236147f000000000100ffffffffab1d6cbc0d87ccd6e981f0f176418ab65d690cc888058ee1570b52e9c4b1c67a0300000008006351ac52ac6300ffffffff03dd8b260100000000056351516a65bcceba0100000000085365ac6351525252762974040000000008ac52516a63530051512d9a8300", "65", 1, 390016172, 0, "2a76800a0ce6dbb360a3919a255f50870482aa5265868e606d8b42e7e5067270"], + ["0b07915204c73a57fe9da0d2be2efa1bcef1810c1a94c82e63a7a73a5c0d35cdfd78a1573903000000046a005253957f67fd0b0c0d6099925c976e15c8c4587301b2b8b134158ee89570738d725b612d522d03000000096aac6563ac526aacacffffffffd2a392f4da6f2118dbf01fd26ed32c08871fdb776b2a46333a7f626fa98e3ecf020000000753656a006500ac6352b6a4c0aa0d66ef659fcc4439949e08e7fffef7d5408eb810b98e7dc468a8435647d000000000056a6a536a00ffffffff04f7692e030000000004535200004dc60d0400000000086aac53ac63656500abae3f030000000007ac5252526a0052cfb2bb010000000001ac9c8a431c00", "63", 2, -843804767, 0, "96cd37aac21408ea03c4103241cf01970af79fc807e8412f23c34ed09f6364aa"], + ["030000807082c40301d03d0f5620ad22c0d571b1ebaad91a9a45ffd9e17d9d8330adc2cef9bbdc2f75010000000165ffffffff029c2fc5010000000008ac6a53ac51516a6a6b29e00300000000096a6a516552ac656a51a172493ea594d43e020000000000000000a328300300000000658fccfcba5e38e2efb45a17bb5ecf58cea3404392a362bdf6dc0db9429df09e96b7e0921cdcbfc3e8a7563c33b4166fb957687279bcf65fe7c180bbdbf59dc120c4273816a6c2acbb03460e220e0e7f28aefa03c10bb26fe22ba88ec8ba604500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d998ba29da849c3c2e576a04905d010a112937c4fc8f7a3113cb97f24e475f2e1685ccf83ffcfd5717749f1d5ae5aa95d156e7dc11582dc17a0e51a374fb743ed2ea48e43c3bbe6eaba1877a153989fffa7069a3b5e5d9569ce4a1cd3995aa56aa31a4faae106e98205b3f8649b321545926840f7fb57882d27c632d453757e1030f771e9cd375d30c3bc395854c9906d18ad4527faead5f59766f68072ff6f3da0323ea73a72475d01544fed4f014c9860d7d3672843dd25db8cee2f48901e5c0d30b04fddfaa085f32630d50044dca60d47f514a2d9187f8f5ebfbf03e067897a7cf84ffd10fb836f573a1fa312a8fd060e092a856c0a68f8b27d32a718301cbbf1d0314dab4154e48cacb84a3a062ba0fa5ff823821edc27d4010b5d137c386fcf4450230347d518a9fea09100d67d0d822dbf2b03c5b9dd670ae9ade7f4dbfbf3f60dc031b51dc330ccc4663b0927fa9cd61d7a77db5f4735f94e0b2bca2ad03af13f2a3032c9036d36e0c69d636764769794a661ae7dc22b7bd737641c4a39f1f35eb62280210a93d321f6299fe9ff4c29f78106a38b9b78ec89e918926d11cc77f285ea4f827c480d600c40a42dead3ab65ebb4e790221e8aea99e243d8b1a750f359cbd3577a9dea4bc3072657ab15888df637b6b6ca491e4765fdd91b803e9e08a363873a47d1e117a97b1c57f22487b2b2f404e9b8d70595d506ae7b4f9bd6256f672bc664e86ccb2afcbea5814c7d3d01fa912945cdce14e1eee9a961e003cb9c4227c59d0c6f0741bb296706d81afc748d73467f737c2ea5d04b1a8289782d143f07070b56c972b5866412b018384d44e6cd1cb9940de396e3897204fa06f189e50a88d07234825e95d9de86d0f4e28fc2a788e3775f7a186ad677ee74fe0c3de1615dc69601986acdb6aebb707afb5ddd9ca00fc35da3aa75fe1330189f3677f73a49d3eeacf2a30474451c87b92dbc2fefa8e35b6bdc071910c75454daf4c2fe693be4c6c878d62ff1ca158abda67e677c8ec87785452e3113cdc20510510286ab5a37afdd7e1dacce93119569f6f7e4947ddd1aa67e7cbef13b8d8aa66a57ef80205aff412137b0d498598e5677834705ab8a3baf1f429d23621ea23d044d881eb872b1757fe83a787d18ca3d6d5945a01eebcd7086fc7a1db4ac354d33d43f1018d0d7c57073c25579a5e779e6292fb05cad3e905f561be78c3c472aad78015fd4b8bb40e823d9cd2f1c4e741a3123aa97cc107f1066c450e47568281b72718976409d5d7c6ef58fda815bbcf2deedaa16100ce1299ac53a920f0e09e9dacfcaf052f9990241ec6926a47be7586336ee38b7badcdbb0187fd773a02896f485dce9b0cc4c04b020d465fc822b08d82cf4c54bf44e197f062634b87b05abc4992acff211362c04d1c0723d82c0310ff2fbd08eb69abfc2d2173658c646644815e879f142f5f81c04321bcd15c8d942cdb1cfb7a3851e31b2105c89e9c187c794ed91af2dc7ebc23fe05564bdce54cc8b5ba4611363985fc576878e356ad77912712ae9b2e8c0074f0c031d3e42de1b8c9e9f73f62160cca1736e96c568535ba484cf378345ed31eaf76fccbb4ab95010b903fcd70db23e8b578ac0dae1419298da7b8872953a22a533a3fc0f37fd2b53f922eaa5be84115c0d4c010fe7edf44af62fd8f9f19b67f30f06bd9f13684eb9a7539c0bd02fa99d3ef62e3d7ff0716208caf08de944002e1a5ed4422cab5eb5ed25874aa1877fc1c1586c4d81f00eaba3598734a94a166c613819ea18ad537281cb69e962c5d1faecca61f32f7b3b1645e2928fa987ef77565183aa11829381e3166b167d975523502c8a86a5032bbc69ab431ac3c166a2d7323af5658969832c0a49e7a544da13d367fdeb57e22942b32cf3f8fcc5c6bed4d93b5d33ea95d2084bcc8800c5bb68858cc83234e5963db37b8d163c4edfe080da2e2c51454fd80e92c85f03106c5bccc661978e99a718149291481c3e65b824b0ad531ded53a0074b3cebf985fc4e3ff161ecd8742fb634731710cfc54e09d9b948688638692656e4d504de60c90c68b67bd6288eab1bd7db1b469a286b024da9e22cd53394795fc3b0311f5d48000dc615f1b8b0e9020a080451b8a478a2a2ab5b7794e0f1bb98484ef9d724fc30d45ec3138e0a496098545488d01178ba5b53166f70fad7af8fde84c720610850518b8b87672f6eeed9df8acd49a6735474aa33774c70ba5e4c288e77dafe6ffd423f5b8f4503febf2a99c92fbc06830a7addc362a2351d92c04379eb7271104000000000000000000000000fdf4f2b093b91d5a548a85749ddd71eb27d416b938294dab8760c1fef76a228d61161e82e681aabb471fdaf26ef06e9997ca204811d0b7e128196f99693a133d1ba51c86904efd68d61a357547d4ea7b7aa681188eb6fbe26ef98674992f3ff30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043ac7e6db40eeb50b9038f4264adeb4c9a5b79bb03a538b33f9d8a7b0be442c634b660da028322606fe18126ac9aa5bf5fff629f1b573ed2387d4b26791783dc921bddc470c7f92ee3f2295fe70ed8edccae8731524f9070fa631adff3401a84230b8df1afdde08a924e5c7657e49ef7fc1a03137bf73b2c27def21b983d4a63030372e44509fd3b3193ed9f1ccbd60d675d506318e7f59ab41d5f36b524a84fca03255ff3564f28b18789c507777c308c02403d178e86a01cde04bef698859ebdc20b01646bbfb147085cedbdc7d4851c5feb1a69080bf82308df5388db2df76abbf45affc808dceb993fb099c646564ab8f7d753f8a80fd0b3d2b4032880bc023b4c03168a2c6599e1eb5c4e47d6d127fbcd69346e64f69fc0a69e40779518aa5ad3f20301bc395748d37968f02410a94730346d205a7b95b683d9a0493b8ff9cb326de0032fcbf98a19979181e25882eb83e543fa7af5789c3f3ae962774d5bd505013bca0227964cbbbe45f6006447489e80cc3fae70a7b720a2c3961d36fa8c64bd8c3ef102089dfc8fa4389d344331f0db6cdd8d9ec0347eaae355c69b83178facc1e293929d215695ada9dbfd4ba35b4606a177ff847c57c2ddc316d48f8356e5afc67d9f520cdd9df5b6b396389cc929888c2924d7e255c5df33b946deadbd4d90ed5a2871ef721b1443cf8e10888d3cb2a33d6875f5dda485d8719a7d2829d803a973d5e9739374fb0f5b9ffa7b9f775ab5e38d25e1eb65508e2fc2baed4a47e7249f69479a2692771dbccb6ef645133c479b2d9a2b0f830a75db3a21670ab48ddd7f4af2cccd4f593c4e7cc181482b54e96ad12b525810fe8c8938829a5fa470d09084c3b165adef6d8368efc72bcb8c78e8630e0a4dd6d3ab27104623458f67074dcdc68aabaa432c1e66ef349a4e5e8896cd9e6cfc015ffd476cffd7c60e079b011c46bbd706fc215451ed7921be07a8ddfc12e289c59a8249e178cd4cc85ac115cd1976dc28dcb78fa1071f36a4630e31e666b1e28b31c3cc45ed94e030dfa9a184bed43a2f09ad50929fe45e00fb0edc721dc4b69949c8696f174bd705af9bdcfb5bd5fea0636a9ca26369c1cb24cf7b0312bbacbcec963dff476ef71e5f7ce762c887cb5321e3d9581caab677692115f662a19728fbefdd15204b86d820a199471a10e3e2fa6cab5b77557ededb8a4ec0a5f0c1859e0909d27bc4ac38d11c60771c863087f85732413c321a350d2f97f80b49bb31ef635aacb3c03561f4ba8cdb6c7c8c6f56a59084ee308300e62ef9d9ae45274a7cc4140ca018cd048d785d60972c84bb462b2b0887871f23f0888b7288f0cd477a3a7acfd0f0d111bc88d10006f198f3a585bfab3abe5506d7b022362dfd3259ebfd59b1b5306d252f895b390030401539bd64c6d1dc060ccaa5a89739ad439cf433f601402bd1f0872e9e220bad517e5188548b3a52f56ed6a9f0c2669a78970fb8fe5f7e861225a2c5f58fca860efc96a1829b03c1ea28671f32efcc1211dd28088219ed4be33bcf6c5a5a724b2a0748c3323e2b5148d7a18d7ca1bd4f026622e6169e1e9e1820379ff7ddd1a071cf680bd7ebb9107b6a4855dd75a1e8092573601f8532d29980ed39570d9c235a53c61fb9f20ccc9f1c516e0b710dc9ce3267347e45d415ad6ce448bf1c21880150f0cb17af0e803f2780456cff926bc71c0ea681147f9f63412ee6eab8104e69a113d0b6e80efc9e6010b27e0107ec98a62a4577d5a46533a5c4c354ac57cc83a1147189f6241f858dbdccfed0a8b7a2d010befe4f2c49f7b5d0685a61161ab41b5550a9c9e1e5e4474bf1a9c5a7ee16e4bef82bad6a3e8819fcc17acd7d325eebc1bda32c518beb10c607fb951484543d97d63cac6e8e0c8cecc064738343cb4dc8cd9d4f0dfa6a25f65a997081beea439d721945eba3aef5dc3642bfe281adfc1af8546cec1704a482740ddfe992a335f07bfb3e44d808a9cc94f1d5fe52fda6d21ab9d0d40bffda7278c681f313f5353144a1302bac5698d8e15201c70b43991760be07e0977a328cdff906d44337cecb5e0fd80dfc7f9ff256eda72b4f139b010707c70fcfca835eb1a0319df57860add175b9a9794842cc84df055aad0e09c855bc63ab80f2cdf1f9c2e67b348fee85e9d267f6bc60c634fc5c3b2bbe09c8d6778cab8df945766ff6d58c2f020444fd514187b014bbf892d3aed16226fcc3f895a47a927e3ba91ec3749107c3cdf5796f51c3b5d1c059f74427968114e4cae7577aa82d9072c3120cea77b257bbda929e76f81b22bf7be790dacf568546a64dfef2c84673f054a3820f3f0a781ff3eceafe784dbca32141f9c2b9d04720b3369b407ddf76d6a0cf614b8ff2e0bc779f1603880ba7a00da6d832b845d083e9c374481443a7f9e7ee3e68539e0e", "520052536353", 0, 1088406930, 1537743641, "31739c6edd5b11b66b7defecbb7ba0a5d85822f8705e18b7e8bcfd769bd3fe3a"], + ["f515592204fb960bef2e0f66e515d3e47e486d52b603e9567042c6aee4cc44c80945379558020000000451516a00f1f27cc399bd14aa62e365a95abdf44051a7b8a87252ccf28b3d814434c9a496eb24329c0300000000447245735130f7394cdab8d1be31ecd384a683772d77839aed9bc704385f238d23679b9b0300000000ffffffffb1da7ca943c7c84b5eb376149ed62457328c91ffdaae3afc25244be5cc1bd53502000000096a526a515152535252a2d9234f035f9c02030000000001ac39b6dc050000000001637f7e810000000000036500520000000001000000000000000020a0d40000000000a91f3ef37c20af62f1f7d7a83334fe46130059a5574f63dd9d1d203d88141e734ae6857c04abb1a18a4616c63f62cf8177ed4df8688fb4e7376ef2cb7705bb6650313008e3480def99107acbd3d9a80046fa20d3d4d23c6c37c753c8c0d9b0e80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008f854b313c3ef84565671c29e5ab336677cb078214839bf39b11d3cd69964840799934384a7f7b1bf6d1510065043f4953380c4a32cc8dd48cb43e2b6f2a799d37b7346ae81708cbe829d265a8bc75cfe1ab960f5d81d972d4853c84f6cbb57250efbde74af3962b356f46dbc1fa8f72c9c70c1e2a66fa8bf8bc40c57296884030b7c884d2481260bdfbe10cee294ebe441ccbc530c3006888a8f82e3b95393f7022b2d6d5a5786b11a82de8c9f89b927081a0353782732980b0dc3f91d0417469e0b0466ecc7a3312cf30dcc9941afb072c628d88891635cb9319ac8121397fd5bc7b19530a04e67e1caf55d93f1eb39e377c5b82659854b6f28359b768cdb53a016022c0177d840e8ffa3ec87c020144deb0e20cbb3a5c5e4e897da9b00bf471097ce02210ea55034c32e66760b18ea7c5bbed6da6b5bcf3ff329192608a3adb0b6e9ef030d49b32b8a3134077d7963aa4d80a7750a27e7a53bbf5cef0f122393b22ed81b0205c00f21a116c69c6413aa9d51364b0f2fad341a9fca5e4701c00856e34707bb0206d7446200174959ed1381407799156bbb11b36d46061f208dbc92c5fe03e8428ca84152e702f9d9e00ac926999f6621f6a63af6102f3d2010dda80eee12af4c17f5bfc26e4f55cc5e0669b5488d81bff2f1ae990b0bd7132a10f0b0ffe27a354b4b543c3ef943bce17cf27650f25384142857cb438484152fb477190ca935574f0e25e7e1e6ff8f794aa05cfcf07991034b951654c7014b4a56092a0ac1da250a7ed251dbd16f83f2fabefe9864b2dc22808f948592ebc45a601188864e175619fb322227c9f7708489ade8a53764d293ec5bfae29746d90857d5840848314d36129d6ecaf4bd0f2666fc452e1afeea4b09ae41fe2e7ad1792005dbc9dd540acc67e020e3355d3d548278f7c25966b42648f38c2c1ceaebcccfe04f2874f5a06ca98abab13597f7afcbb31e72c9a53f4d32185eaef8c3241d51d34715620605556258a8fb2284e587834b8eb7db20581d721bb78c72988cdadcd331b58a259ae7da54b1f00f5747daa9892d38c0494b2b182e38803ef20abd3951e38999b493b8d75d3b7acd1d3aa406e7abc98f468d7b1186657543b93460fe2a96a7ffaf1e8121d65125b359f9de0146da4ba3a3611a6bb55637690e8dae0a441bb1d6ea500019a77963cb88b074496c96b81b03e0595976cc9f0f8901cfdb4bba0ce0c4035d6307922f175aa479fa81d2a81687dbdd211464bcd3ba0895dc85142c874f0a1e502caa175b4f33a9083f59c6009717366257964ed67d2d2620f71311610b15cbc83ef69d5b7f38512fac23c2f692e693061707123edd58c864d02f6a5a9aac4179900a3154656bece08f27747d20b309f6b236b60f3189a7813a4c685cdd77ea05b08cb26ebf0e6c9be8f39e1386b792e49ce618c1198c0d92ebad4747a7df376243aaf457e95b927794b8af3cb801db959b2c2cf545f86556478166931e7722ffc28a3c6ffdf680846ff00d066e31a1f593cd08d925165ca94b632534323a90102f28947801e610c9298297701e286b56063f4a7ce3518e8f9af2f08bc5a696861dd087580b1d7ed48c1d31729f5b8014ed2bbd945c63e47f1db13269f408d9c3c837469386b937cf10b7954b2bff8cc0299727ab1aa0bff11d44bd27df29289928b8a36ab36d1f88679806ffdb6542afd59d8bd53333870553c354fb01cac98e1095fd0b5af0f6a6e3a9f2fae645c8714682dc7940b285bb52ac9be146adeaa4cae3f0af6653014cd423fff79811df4a4ef55f7d7e167b223ed4cd1402e15f51423bb05be4524b7e6de50688f1fdd0b4de43e8a2a2e08c1795764a9e7ada3e543a16d4c9719747a5748113112bbbc2ba86e2e2b68c9252a0850029b01235ea367434ef6d4bbf7ffcbc3ecbaf4206719fd7d6b245aeb7e38d32d759bc452a76b1c540ea40cfaa02f59e7f502f24b5e4442b7f4f849d9a1ba81b334fee9e588f7fac63eff63a60e4125f5aaa53b670fd87ea1536be0ecc075db2fb17a20f555e99ea0f62f482630836242684494a40d52f3547935e9cc1f8c0b08baf766f74896f758ba4bf7ba51b273f9cdc13303e02f828946890fea96cfdbfe085c73f8f909132f8315a9956d5ea3f8cd83b6b6c642e2d480dce4f359c42c4006bc2854c9318bb90543e6dabdb5c9c02b9013d5f13d265ba98f7f48a2dfc6927b74a5eb83189fc12a409d898cb8b3350bb4551c2acdfb91c4d8cabc770b23ca1d02dceaca51c623f9dd0f1bebb25f68cc658cdd4dceca977b17877a4b7482e0e0c92466098329f6bf0abc5b957395be6717a90d041a884c980cbdbedd5eb348e1f2bbfe2b30c7cedf3b199053b1aa9d11fec83c17c838ec63d0e735bdd1f7a220a87679326ede58062f6493fd32b7b27c39a2fcc5c06", "000063", 3, 1558932383, 1537743641, "3142be52c7c29110c9db7f803178208f206c6b0f9825c06aebd473c9c701bc7e"], + ["030000807082c403025693df0520e4051660a68c72c9aece09973a1eccb3bd39d403db999fbfd1fd3b01000000020000c41d53644c9582baffe09e83e4eedf8c7ea368235599d78f3e6578bdc146240bc3ec5fd80200000006005163525152ffffffff02aeffe702000000000300ac51e4a81b0000000000035253ac0000000000000000036266a404000000000000000000000000a273fddc5f823e668b336abf917b433f033930a91c4fcf0333f5a08db18779060bdf6a50d7d66a50a21826b52b3d4c83f34e3bff4b1004e5186d9e6be8f0047a59999b772f9dd10482223a871dc44e83beec055c42f5cc6dabaf4ee476e4afd1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003a232dca8e83e6ef79ad8f0f8bc1b8d1e11cc9df7ae80e98dbcba1163d86db5207b664958225edd6886d2af3d2072a4f5eaa853fd0462aad9de7074cc320b60310b7954af21f396387af28c5bf6a9a0220a6aa9e7a82f14e6b3b5273096b846f1513fcb5009dc81a1acb8a01b74fdeda8e45efc4f601e223370bd777a5285f88022889dd3f7d61aee1a599356ad2d97127623b098b142546b787e87618c17279270313ac9f71f54c156b4bb4f33606bc7df7e4f7d7470baee47cc3c0d2d9106ea4b70b03a638aa73c056e649687f18daaeb713ce80d86f6205a483afc8794f8f7b9f7c1cbb5e262aad2ce45b0868d3ca88461b16ded2811270b508a247fba4123ddac1031805b51f378025e8299065dc6f4f4914679f9ddb33a3b1524e41fc6ef407546203054bf685839718e21b6572b58242944f648e1ddc554c707c72a8c3b7065f49bc0313516cd852c638f9bee7a6d1a7ca3b593f32fa530635952a37bb39c1f839c0560218ad514002019b2f538feff8506cce3f440d559488e2b84b6f68f0816d7539dd03093567cb0d880ed99420dca8c49da9530f4c36958fd8264a2d6ac4bbd129a8f70d4de13a4fdc3c8d2f4e1c7b8d95dc1fb9c4c6e632b3bb39aa371788243d477bceac58efb17dde025cd6df83fed60bfcb9d05e51be002d79f97911bc621fe0378e79bd8e2045bc2340af832df5d0fdfec2493851abb27ba556e0dbc44f48e06b4b78078ed6cf5d8902ad54eb7ea1277791ccf7c4bc6b7fac0f3871b3561de9acd3744563cf6de945d3ffe7cc5e504fbe1e45a6911ab8cfeb559734e1fe17e54f79dcdcabf0251380a0222711e0347e415b44ce8d28a2b963cecfb7d54b7b8426cef3f71755f58978607f5f176311b5b435ab67261e22c16f7355ad44b77ae981e872a3aeecdf71bfe612c95c9b86b2d3ce4231e56fd640f0c74c572494673b4244dc31e42cc27811c730c12fad2db630d4f66a0a908d03973c121a91abe02456b6536bbcbd0519648352452cf2573f8fea5c9ff2e8cc7c827195aa46915076a680cbfb852fcdb08302c1fb3518fe85c7ca35ad6fee364afe821da8fc2f70393db3d0f83e8504fde519ff2c1a216eab0ac04806f34bff0288b8541840f7600a716e7a29f8ce25ae7f9b18467badda7e7f08775a44ebfb73c2beaac0461f043e797bb1203722ccc84e1e685754f3b23dcb184b87f7d2c62f679de929f7472a4a796f32233a8818be1957471d2c0d7a1f74056e33b75d865d58b729e500bc44b521589777e9a70bcce4596a56ec4b771148cb2b54958925b0e89ef42b394e1cae736ef433d43540fb16fc32b02f3acf9fb7fc1d16a8ed11b83d5b15791a73dc8d3f27e34c067ffce8335579cadb7e04e44e6c09c9f908608bb682d8d3b4e07789becab758df8bd715501ac92960b02aba830a0548d40e3e2b4a6895b67bdb8bceb6d528e74b9e7f2caeee7c9ccacf20bdbb1e5f895590dfd25c8fec0030b17cc9ea22cefd3231a785f24fbc78a3c1f77777b9e5415f4644b6e6da4af1114aa9d2ef842eda7c427c13fe3ea735bad6f6a3bd0b1156d40949fa16639aacdfd5d3499e9ee58d59e21d8e0595f0f5080aa69dd5c5d3baaa5d19ec032ed6891ecb677427f49cb6e717ade6a8da66ce417510113fc651bdb8ed652a9bb4fd0d55524fcf88a8a33c6016b1605f8e5c98a8a3e9e3f92d99003cd9e1087b68254586e8e965ba5c626d21a167c34ecb4da681a032ad3b933f758dd3cf99247c7ccdf0429970160bcf69015f3842d8be9f20614dfb43602f11f91a674aa915b30d96cf1546f024f67a507a0ba948783d4805984b631a4b20fcd6710d2a92f14d2160794bb769e94cd473713d64c03ccd8db3b2347c01d29ef8f384c52484830a63781ddc16ed3141df3aeb07c867499d751d6b30b2221940204c990c90a174f8346463c6b132e92ad13e8a565fe3e1340674f66bc30a6ef0aa1b80b3a57a96178277c562f6802203c0e086f7ad98d8e9aa07ab19401729fb26e2e16b9162f6d07cc76221bb3c05d2210fb977ddedf90e284a5bc7275603e256293e8681e660a8bf94dc7202b0a8992225e73123c7b117c4369130cf682fff1fe53d732b1d4e7de5ccb190341f15c829aa774ddffad886e46075f40e0d69e7b49056ba8f5fd48ed6d4997af822e3b3280a2d58a366d6c0e86bcc6a663541c83a195e645243b6c620250fc2d60624cafe5aa5e934e7aa562c9563e76e407a578f764dc5f644c7c6faad952325201763acd25c3fd22b7cc275236922014594b59a00000000000000006216ea04000000008e49ef9dd27759255eba59c5dd6de3bde7c3eea048e54fbf60a9275589a9c4b9079f89776c10c7b985653b5da26bc3a87324180bd3ad0d9eef5a71499f466772e4c5745e12212a350b0708b6eda20265e7d97847b71dca8e275deff32d5e828400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fe065f4a97f061d8dbfb5b33285da9fba7cac35556f35d01868e5593de1941320f7165fe016c226986d6d2ae0ad1c7651dfdcd67f5c7a1829741ef83fde9c25e7ca955a1992e7dea57c1eca6bac63cd69c5e7be830f38ba551e828c48108ee3f0ebd1249439b0b7962c5a0e2194ae8d4a4ae7be89984040e33e42091f854f3a402240168568de0b2671c1eec6aa70c821f9d841e65dc1a999f894a8d34bb19603d030eabed1733a02a8d83e2f5bb05b37c093cf7d96a34ebe8119d23193a70d21f1d0a0351fcc4cebd577706b40e3d8f188179e1a40d154a15aeb77005ff9ee2d626084ed5aa98eba5168d5ad56f6977cc0be573b6c6896254f2391c0e22524a18e8cc032621f1bd87da0e5e3da62ef80acc2397ac1254eeae6f52c4b6b9cc8b4633c5f9030ea5b0f904e22a58be51a156c6ef59304a013173b4c9ded07d1ac7d26405cd8c032131221a3c15487bd76d6d347d82d977448d2dd4be2b3fb9150f1007a7fcd0e9021265beeadfabdbfa1dc0477f9e3f3a4de7b618fc0a9817ac4e99b0453dedce2c0303faa48830d82570e6b13fc291b534481761d69bdb035b4d50c7e2734211bdd72d8cd7014a81055308dd17e391fbd102cc1012f7dff6c6d395fc230570b4d4dbaedbe9810b2aacc93579daca029a9ed39301e6574220a1ea60aae647d25f9d34aadf719446d1df7c5ef282ef5ba15ab84cf23deb556bacaf9a2ea072617a027926ad494673998a5299bd584f327c29443901785cb43653eb822bc7e202e6c5bdc2b9f0bd8b33fb84e2c166cf4a3ec8fd85f0784a343178e1f72234a9490b1039a762eb706635e1869761524e6ebe6d6bbacecad2cefdc83703caecabef8b462593cd0d016dc452012cf7de643e5fb659a52fac541bb075bee3d97650d7fda75d112961d4fa80cdee7d6b5ab73aaa19721f44bb3e885deafcd2361c22dbf56fdd5f413650675aa32ecc6052b4f24cd0a6848938108c3508040345375049d01d93e76aa32b6ec6c8a65eaed7d9ce9d95d42998c3f0c8afe1ec550a067aa11b443a290e052ad157e4a60289a8f804d212a0d3916dcf2c2e08b323192b41c85ee48bd88b20e6ad01bd7a5e8ebddafa9d9a66563285c1433385c9b6cd924d8be632d1f00f079921778a798b34698ead6bdd4b4f67c368129f5dbd64543d6a1c8096b3e26de041d934a4e27310b1893f518d2bb5d40b80a06db88d0210f64f5c5f3a666d67fea40c798c71418c14cd9a80567815b8160c4a5a47fc3bb2ff41da9ccda38b9b9245548930c31db59035d94f12dd61e62e83bf6cd09b61550379273cf58d2f0ca965dd3b62c5a6b9d153ed3ee560d9780aaa7c6fb3f2f3bdacf376a58019ef7f150160cb3a467f20e6db6ea0abf4974209f9bfc4ba62295a23b0718e0561ceadf2e797be2072e2758d68cf6f2e9cd704c13e08ee5731b0fa90a4b9183a25154e2c43b09f0be3b1fa48190f891d4778ee540a2c02d635b897fac6d39631bd29ac8c48a6e113e0dd518567aefe8c2d13025faf287a357d187e60a24e6f93912d6fd72da869989a38f5df8e510107bc6adfc92aaa0f31231c0a3433849849ba526f0005b8571a9667a087df394425977cc8b5bcea379a9e7a4e70f075b74116426e2bd60c824015c0d99953874114a484c3369eb85e320fe6d9f81ceb4b995abd60d154e92e3a40693c9d9b06e814f416fff1bfc952cdbc7d0e99537e1636f31aea6b97cb53b89af7d5c77fd411836a1dffbeb5a149fa45abbe768f7f5abcf6f7aebe975b576e8ac18a2a3586a8d41b4bb8820286c9bc1f643fdd31da84246d440b0820d2d252173bd674cbb3a26626117ef27559002a22620bde9c58cabd6533e45dc9802d0809c403b6ae9dc8ddfb3a5ec447fb3e6714d949cec9bea86058e0b3e44b706b474b956f592ac8cc407a3a7a7c7a911e82694fce7f60d2aa153fa3bfb134c8d5b3929dc78958e9ec0a2706b70f8fd15036862e984f275bb30fd77fcd45f953b36932b8de5e3480f87ddb7cf4aedc0633d6be39dc576a5cab09f0a3db2199c0b9d6d9026a8df0b243953bbb183479ae3f8f0df32c3973b16a0b9497f6daa4b090be6b792f4c1c6ee491b07bcae3d036803a23796f7b0af3541544c26847902529c09a4dfcfec029682b682a37e46d393fb00f75847902d75effb8b4548020af7c5ba62cf668de10047301aef727af63c392bdc328bb132ca74db0537eb3f2c68a645b3bdd043c697f3a87e38a90301204928cd18aed7ca586e19ac8239a21e351e060b6adc26c72e0e0d4f38aeced9304000000000000000000000000ea1440970666760d24c01282c380e5e4339c2c2df87590d56cfbdbd68e6ba76aad24962715c9045675337eb8c6c8c93e70f8591462d1d957ddb8d504792a16514eb5c459010a2cbbe6b6bb3ea8c189515ce8a36d4dd3a97e4526519b3effb82c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c38aaebf541cd20d6ab1f3c8ea55695ef65b779616cffd8110e4ec65992d32e1a3456ff106cafd376deb5d95daab5388577b6fd36b7ba3809bd17ec59d847f9c8bb9d06b09f7cc25b20ee2c7cb7407374778315a0d3243d4a459113daa77927c0c757e4def05711e73f7824c19ddee24ce17c3aad99a9f622d51564bfd37c96c0309b96b5acbbf0dc313977b1378e395b9c442f5c21c1a4a06270d359a2c127046030960a98fbc4f0c192e4a5dd1b92b1737801eeeb6d5d3fec160c512f23f1c025f0b02144c9e29be0f750a134c9f7c7c3ef9777d0ec9e217de280dcf7eb8fdedf92c0a3c32f90e38d84725c96010d86542f80940d2406e2be47ffaa821abd5960fcf020e76a51212d8cda35482104685fc6cf495d0b3d54edc121e9a0c0b427b135f52032594a6125e62bb19d32fa8f5875327c59e3262d3acaa586f1da3964e5bbfae0f03014bb8f047df8a9b667149e8f6d28c1d0f4c47b8c76d28c6dc113ab9e358e6a60227b92c4f5d83d086e8da6a2dc1ae9f326ec5f397af5f1e506e737386b5b94d9d031614249d3510e63bdb93e33e887815dcea886823937be7e7220f4ab553e88d8bf8c48e74e696f69540523a89f4f72cd6070abb4fe65225356caf3d516c7318735ac4dcbd0de5b7b7bab8dc0084d7e467b95c642ba392d5128848207ae679485f051a8d3fa71e3af34806a89cf91477760593f533236ee10124f5669d929a4f601d248b8ff136cc1a852fa406bf44c12e0808be922aba4b85b436d0400499c9670588ad3462dd793ecfd0ea8c9049bb5c85a10a6740343a8f06cd810f1ff193a7c4c8b3df79cab38625797a2a253d506be0c7eb9b9c91c58de4983cf97f2217ffc46fae830602176af01907448d4e4ea82a72cb004e20f656cb735dc9c0d7ca9b8feefcfb16c5e3464f18717678ee7d645c24845bbc7b9a907afdbd21203237e30efb5d40620d873b9f923df24ee927d3d83d53eac163d2fdb0f930c1452e6ac3b18319878a9e4787964791257175f629ff315a906050b22914c9a727a7a5788cc87b49fca75637411cd70fdc471f90188717f7544fb9e5f753e975ca835d69be97d8b03a18a0ca51482f02afa64a0a4ef83a725555f70d1ef9f9871ac29be52d545381fdacebbb7bc9846cdd42734208173b4f5fde48f9806a8772712cc50b2f27d08b05abbe2584e870b2fc0017cd1cd813d7dacff797fb82895a434102eeb5ef3813d527b9392a021c244f7ac92544923611345598d20028dd65a45386273f5c0c798f92f52df72db7714314213b415044ae948f223f2b60da62a19dec3a3f9c64e3888e646348d5b932ff5e61f509013cc8108b36a6b30f4e578ae60e3a994b2084707f9a6564544258730cdcc047615aa04618abb8379ce4d7a9ded902ddd5f495207d818bfcc4f7eb71ff61b4631b6cdec957b2d884851b2e8550ee909b56ed1f74547a4432c56ac6d62a6e3c40a7043debda85d428c4935182fe30b876e63d8b0416cd6dee14591e8b51f47a608084f959622fcb53185332e86415574c18f85a79768f6becdb9665e9d8781743d607c6e4c35baf7adcdf76dce54f65b19bfdb092eacffa94cd7b760c8e3e18a2996db243647b0ccab7b27fb2edc062b8522404b0d40b54c5671b0d699b49d77284fa639b2d02341e7edbf255fc234b22bfb031983ca3abb32620c74d6dec97c247af789a88f44ec663c36a3760b7774a53eeb9499ee491095fc730143e7552b5f2e168815d4a8681fd4bfbefd718bda83d2ebfa25ad9a408c51e16841e50a04c333c2ed2c5b35af13d5a008d5bc104a5d9918549ad0a55459cb90d836d4cbdd665c2de7db43d6c8864da6764943803af9b23075ea6384229f187b8e9d6bdde66ca73b5d6d65d818b5d553ca857415acf4fc76e8728eea09da3c8a8677de237498d230e2cfa1ca03c462ccbc42539ca92be5b2618a44501ebc50349012ce9dd26ca5af2357d7c9f92da9f9e5d6ea815eb68f135a3484cf1a6a54a9b05db48eab08bcf610a4a40ab9c00f784d3a7d849b2f4dc6538824671d8ec67d464c88922fcf49ccc8ec6cb7b06af25d5dece6e02c9065cdbff7a39349d312c9192f0eadb6a8b93f2214f33d29f193e73e74a811b41002f0c9565dc8703bfd5b9f1a9ac27dbf2122920df30d6cea0e97ac3f6587dbece59306ab61fd868bc8af71587acc5461bc37bcd05b4698dfbe46d93dc964cc8bdba571a416808997c6a0fb55acece45142555858b415ca13fe35831b76976a0b17669a6e23b8dae175fe01c705c9b8ea9ca54a27673140c47c93d8d26f0d2fbe638d123df8e2007d6e6d41ca4cd550ec33c7669f8620c13b6018df36075629da7054b3047d80e3d5911c8e3ae7374e59b538d72881debca7f20184cb078eb8c6116b53047d9f52f3ad2ac0fda23b815fe0c", "6351006a536a6a6a52", 0, -1988452841, 0, "40d84eb7f29f084cd3c8abfc1c4fe2e3e019ea0244510d203e5a87117e864e7e"], + ["030000807082c403027e0d13da5cb6c012aa5c5073de53cea35a2ac24237e985a23be1a6d32ce1af0d0200000000ffffffffe7658d959e7818a6126b0e45c27abeed23428162a584c49b54ed9f9877bd46d5030000000152ea591a1103d4920a0200000000066a635251526397f7c90400000000060052000063ac511a260500000000050051636565000000000000000000", "63516a63510053ac65", 1, 169255014, 1537743641, "94f73cbdfbacb27eb9e52c5795a42004711b543fe966bb0da298b6e02e37b6dd"], + ["030000807082c4030207492ed69c2c0d73bea521f51754678912e1d79327e4fc98b1226af5788b57c902000000096a6a5253526a526365ffffffffbc02fcf56cb7a36a9cdeb82c4e8e121a9f1f58af7de4c8fef418daf855fff7050100000005ac635100512b7a1b27029c64920000000000046a515251ca237b010000000003006500cc16684200000000010000000000000000fb55100300000000db90d3e9b05e2343efbb70b41a94b58cb52add27d94d198309830d1c7670977e3839ac736328db242fb052deed65831c8773c7fc4d4840b4c5abe03acb26ddfc0a071e151458613a7eded35d8dcf57e7482aa9b38b0dd30d7e91b2a7800309f2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a8530e940e7a5ee48208999291fc4759de86b206c29f5d6bca1a584dd09f6e26ae91e06ca7bbdeec69ca2cda03c5d950478f47ba2bc59f12a9ef9a2170f935f36dfe4498c3a354917642e1bc4e0312c09a7e2da8e848b48b5f9e6c70c7b99d58f8e183b7928e0c4cbad6abf3b9643fc12cb2b6c7458f7d57fc1549e51a901f80315ca6160b066cd52ff64aa3592cf9b19c9e210ae311723424c47f4d9ac3d4437021817a55f6e7eb5a474297fc730d5ea748aa7b0659ee745d75969eb2663f994080b02b99c837acbb64ec585ab22ab1e8dff6b2fa81bb5e6049276024f6b261455307765f022f070137f9063d64bcff18427897e6ce680cbbec33de646c6722a7f5a0304665774a1606955a1063ba3784f86b1c1b49c61dfa5fe075009a56cc12482800222b3d2b2c22ee10ed9caabe2f81b9bf5620fd2f76c18fcffc6afd3fcdafa7078032af18676adb3d3326f413fc5ea4ba9125b729dcf90ddedb128fe2333abbb6f0a021327225be2be470cb697a9057e888858913f6b5d97d630cd054b936f01d2a3e00313786fc5ea45e46ac8878857dea3b836b5463d99f2dd294dd8a27b84434cb71862f718a57bf38339ffb74cfbe79781a1f11dc278d71f459ad09ab8f4589672f8f684180a9bef6610a63254fe0516b85b0071071f296ee2375cc9108c9d2c337c07ca5f82c3cb75ec1e3418e3bd6a3bf204c2f32a693eb820f19dbe325fedbe961278bef67f1cf4ffa0ec4e20b9e7cc20cb6b716bd37db3d7d312c21bb1da975af6738d225b8f32e28d62e19e72473d99bde8f3e3757e32cff736d8f7b9aeede25e10145f6a53c2ad5775789f3500e39e09f420e056e6de743d9073321cea8d18f70891edb5061ad4c3a33acc4f235ff5463a311cb9a04ec3c59e5651d44368a161f059abf9b5e4a8a0be83f22fe6bd21b01d81290f1b20834bf04371579f3ed772b2f2db2d6ba52b328ea8bfd32146f1948af1df830424ca6073efbe18c4c3a113ad88451d6e0c160978e2d694d440b77ca27fc34959f74106f06b998288dd5af64fe92c0cb3f79786c8f91d0baf03fd5cf60bd6844fd8917ae74ec847bcbb078b5ab1f032609d7e178b1e6e297370c58c90ba308e1aa3027ae03b4ccaa5ca5df4d3837b1a588fbc2db32b0aa56bd288e82e0af994fe1ee8f6d4e522aa82375d8d7f1ead1b9c9583bd688ecf04fe77c8b8cee3bd250e5473650e4f5b93fab04ae4937e49bac61d7b0aaac261fcae8296dbe3ad8fc74b6910bcda1ab61fd3517645e014c0d97bccbfb274aff20fb5a8ea540557c207b9d233ab5cab2e9309ad71d4180885e42b82afe6d7071e9f4b3c813edf27d4d3006d0d238e0953c3807d6b6884d5ba77270866cda6661e4671facaf922bd9e9a9a338c5435fbdb4b7907c2e9229f96f678060fefa4d23ed7b1290744701f35cb57642875b68ca036d04c495b9cb7fcd8a15b9cbdb3a2afbe034c1c6263b8edd345b5b78d6f1947e3d0345d8d9dbbd8aad95041511e14e2e8f95965147c9b8c306f2e920b4b3e62b5be99194f439a072e5a61da4a50ab3b39b1371ce8eb9f29299c338ed1442e5cca401a4cbae64fdaaebac5d687b7234cc8d5f15d071165c0cbf4657e9d957a7c6d509e1496c3ba42ee2330f9e7dc3450b87e541ac182b0be6e498cfa88a688c0bff270b525133ea858dac6cbbffbcd0ecf7e99a26c0778a21b5ea116cf7df9b7ead42f58d0ebadeadd75cee96b22978914fd587388decaab5620078bd9f032a060afe7ab1c7252d8acfce6cdf0d9df7336be1f92a660786e537df17925a2452ab9522904c517791f377ed1a5352e86e1580cb1ed877d935888943243a7bd68e49385eb9158dec322ff560272f0f0d573bb7cc4f5d8b23cb8c4aa2a2fc15c741aa2f5b2d301727884f1306c3f916644d53bf19031033462c21e098856153f90189d085a95cf9ef7b5ddac52f5ea69137e5c00b48eb98cd2dbfc6524ba6e16e2af14ba0d0af2fb23bd8182919c19296d53b42498aa39c6e60f263466e3100743cc975cff3fc33b6bea19eb106d92b46afe5f9580af427a18504de78122555c39fde49beba58fc2b12b05587a2fcd6ac06b5028d0227d0dab227fd1ce4efc007859d1bfef38ffc47aecca8ddaa35916b0722429b642ba75f8cd993185266ec413a54c7b3aecbb7a6a68ae13d21ecdbd4f6557b7d9548fbd42f05c72cf53e161643fac6d8318c7c8432f3f28e6cef9a1ee9c4c37bf4ff8e65c597dabcb6dcf5d2691363bf85ccb9dd18ef181b7c6675a307475f9b4060c7f91a530b815dffaff2f1b88bd3f00aacac013466eece8860de48d8b611447944d4d9eab0b4ee7b2ad3ea0147425489a947083d8b3712daad4e02b4176d90d137b96511d08f2ebfc0a46ec4fe72273ec49d7ae2169968374634e6048fde297c307", "ac006a6553", 0, -473169853, 1537743641, "0aaaf4c4326b705e78936a2100cc9eb0a4a2e0bdb87313224f1144adb33d165a"], + ["030000807082c403013c480d691b79f265991b30ed3154c72163023d7bb68f24d60af0811987b103210100000006520052526aac414093d701f184f00500000000002a4787d6000000000318b6c3050000000000000000000000003c645d29b2d5fc4ef4d62a2c74eb8854a40cb345569a43c1e957b579adb53a08cd547f94be0a9e29ed3e4ea9ee686b4742ac1990b1e685699c09b9e256a6811e0608d0c31961d4d69ee3adc9f465c573bcd716dce359b07a3bfb92aa7262ab7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002723248e5a6cd2f8576e2a336f2409fba20b858a3a976f204014bf6854bc1b2dfdcd62194867ee2af08c7c2d317803998c986878846bfbdd383584e224fd2961e37de7ae13fab0f1312717b0a6256224a69cc00b067911f6150c5e46c5d701b770525e5917f462af0f906e4db9a721be9ffc76278b3fb238deeaa97db86fa743030123e8f1b7ec3a17afd25686af3009785ea4edcb78587490756d38fc2fbe957f032752f03dcd306c0302c33925e9e1ff084cdbdfeebc5de3674c0c8b2c3857e7f90a0074a9c0b568a34321d10003f71e7b94df5874061d2eb751dae101a65527054975a06add6bee0e3dfe6db840a631fd52a61610f71cdde842436515643918439c02293753f2b24478c5d9b53f898cf7e6c06a0c47c2409dea8d73a9d14f8591543a0214235ccc9e3cda94dc702b3774cbff624914a9b087882fd8bae104a9a62291500222be6257be87457ed58cc7be7ed2cad43fdd7edb2df006bfa251dcd1cb0e9f580220122229ec67d283f6676b55392af9306a516b425a4cc037fc9e2b85fde2b34d030a246a9605b374c55a0c249bc0522d75f5c19afa25c673b742e5798c988e2bb37485ecc583a8c489a02607e5737374677662fa9185a46987910f3e0838f8beb2a002062630d537b1052420e0fcf808af910b81c2ca90b725b6c8c293346a1f1004717557ab796272ff01f7bd1d3ee99722c0dd12ca7b4c66925144a9e7ba3b301304aa9bee2ae17a013d47a8a8a85f3441ff2d360c3795495013e98894964a2eec0331a86e999fc752c5e8ec35fee7aa84595302848a8cf5e2fd4fd0173609f77b0265752d39f3c05f51e8cca41ca968ace5ec0c7f30293f2157b5f556ff3f720965c2b47507846a0c8baff4579d14eefb78e3913fe2e6e1d779fe9cd105ce7de526511b804f19500a92efcd6b319df1f27803aa91a66acfa944d9ada134ac5ef50751409c4a780e94b188f5c7e321fca11cdc16bde8930a614952b2921c46bc2aa56c0a37150a7b1b10d280d8f117dd3fb55a24a2e5c52429e77e781159f9a83268693d46b2ae121e9c624a4185d1c9756f2dcd610ed7fae0ab1fbe3e242926416bfe949731a80dfcaf1866132f5c5188aaf989fcd38c9e0348fc675308262d77d8342c81f62c65a083142b1bf4114642728dccfcefada9a06d4074b8d768eeea5cd86f92af5d211c54c7d1af23af3de99bfcfc518599e58a483b2d4bbb614ca0ccc8c8ef924edecd0b0cd8b8c13e45a3c4efab1273d65a5528e94958d05a5e620aaac483fb55e4a5fcd0d81c1e7eb852c23c8a1491710bf3c2e85633c87bcafaac1e978a9cb19dd7aba321e7411a1b78a680904b7413a87a6d7cf4f7c3b11e9dae808517d3bccaf901523598b7f473d4da641ddeec4147f88768f0a050d907f96a36c49f2032443cf47f62c0115044b47cac34f1d4ed901a61466a5c3cf7c7adfac80804a4ba9fe8bca4e508cf5e7070a64c6734b4eb7e451ab4045e8287463cb8d93b01471e5792dfd7a2ae050dee423b54e4dba27df2c93c1b7e8c00bcf3880b528241cd28472e00c10361eabf45baf7642183bd61709e7d60081d4f0625acc9323cc0558f52de9d8f3c224ea15de9c2b01516e49fff9896bce377d0e881915db0aa8e0fc6de8074865b02a686577a8e86d71a8e857944378931f2d7079c7e13a05ccba4fc906bf215d2e9f61e2a7991ae728598c5c36f3be8c78aeb11b59798562e35d0372ad578aa64aed4ff2411e047df05322813355c0e3f26bcef01c66dab1df009917722e8c291adb244d55b0f636160e85c5ece6ca286262bb2a887f7f84147c71f507234d529cd86fcea01cc250f7677be3b0c9998985e65df96f87c880ee1d88ddee3dca9fd684e26c0ed56a8cf4264d0e0a3e98d60eceb6dd0ae7646bcdcdfc5662ad65f7dcff229a438da7ab66f08bd38470442844e535395303bff3a2602d76f6785b9b57aa28bbd4020fd12b83bb5787811e3c6d46dbdb66ef4e934386ba204a61d2c2aeaecd066d19d20eca761e5913314761981bf5ec415ae8f1df66da18859fd8942c37e6dd37cd0afec03b3e60f941c61ef0f0ff3bb2ae8391aee1e679c04457b4ba24ca38e20242c71937c695828155b38ee4028564f053776e752f8d8fb2444356e7cf89258d63aa113c24a5cc529454628f2e0f0675ba899f16c62129c1b0316790e01f80d584512d2ae41fc962b03049b58af31ce8f3c3d6bd7bae2b54eccc4032895a40f4598f4e53adb76052696ddf494275a207a0591c2a0400659eba90b4ff08af4e8c700000000000000002c5b850000000000293b561e239c9cf56f8035cd66c6babb057fefebb10c6ab48c379ec329fc5dd273631f7e611e874e66208e3b3f60c276e9a2cad1e11e00946c8f40d931eac0a53f65335c55ce311d243ff172e7c8e67ad8820cf677afc55dd6979304dc865d2c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8f657ca7bdfebf8a7032dae85a15fc9e91052e39542d1d2bf4eedf56ea4893e46b7c6693b25732e2127668abc922c5c040069427bad86c388c73ac317d3f0282edcdd9ae670f642a0b7fb62488e9fe4fee6d74422b84045410d732b288a4da846ecbafd22f684de83982590888053130dc1541b733622a6d1af402c0b1a0d4030b3568fc2ecc8280d4021337d30b0522586670bbe41c511a941ed658f9e2100d032d684d78ed1b6ceaa07c1844a82f7c05af44def8b477974b6370f175aba3978b0b0924ceace270d3cd58ed886dcf2756b95e29ca6c3e86ace32437156e7b5a4203631d1bb9b360e6a7d9484d534df64fadeecc47c35dd509b34f5fadb9339b6c780228b340aa8d080363f886c8b2f8a35ed147dec1064f813174074da563c8cd341b022d92d201bfb0c72f2287e434d47c2ab0660ea4b0f8b88ec628ebe61c88c3365c03094d5be34c8287b2a1a06967ddd1da3a2b07f32aa8eba03436f078ae90833b9b031ab3dea0d55b58d6801b7d1f82f98dfc1b58fc485dc1c976207723c7911446380221bdf512bc49f1d89c8382035ef6e294882856c8d0481235627643e0b2e5196a36fa324c1cd2bd04b3da79d6296fa1147ab9df96b63f7c27bdf8b5e76b5c7ccf4313d6c91262ec7bb8457a3474dc3291e2d729a8a61724b2e1dd431ee8e24dfe3391d57b847467e8ec0115312b9d82bfd2f00385d8f6655b1dfdec33a799dd3064a4da578f5cce001f1b747a0f08af98e746a0d4631bc10cd623507dd495a073c36d308e43e260545960d39ba4962e380e8a3ffd8230cb72b362deb2f364d60e6b616d20c015c9d1922a17b407bb18f4571e2aa3cfc7fe6602b602c699cb7792fc223ea236d8dc71d335ac1c65b6ff45a570c644f7a0ba3ffdc4798fd6f05900d3ae0c7c8c2855d8d5457a1adf1eab819d8a93f6b155efa5438e61dd99547163aa8d112409938f185d4cfbd53be4b5d3fb2309005037cb339fc61f26d7aef3512fcd86eb0ccead1ea2e5f533dcf3e65bb262ccf89603cee4ba33ae868cd6e7dcd99e302334a3c9e4a42d677f7183d63014fab1733b59b5ef4e569290b18ad54ae26c0db941863f2aaba0b13a0be38f7e31a5e01a51e3d804acf6846e3d1e04c7ead9e8a939078e747799b30d2881411cf268547510c474fcc5bb6c2400a70225c9273e452648d2cc72bbbd12a94589ecabbfe242f53679f818395db258746b181295690d49b8e8b831638bdda2f0d6fc4d09274883975d6cc2922c44842c7609ad117cbbe0a37d75829d23b0dbe700d3c5707f1de5f64973291c197ebe8623ca0b16f884228f4b6b131870822d11ad22570e30cb5d573a186de77f9c8eb5c350b5ef9b880206234091e2eaa3c9678a0f1051d55db0472342ac9e3500f07d80b0a536542329359399b346570a0c1ac154874dbfe4bdaaf5f82ddb0a7e06c83ffb633ef5fe7ed77926410b97ee697670db797d2b65a5b4c264bf11429ebf6714d22a923610e62c85913bd22e5842186bda65f2c6b63468af84644f32935ecf45600c295c2a8e81c3eadb5b2aadda8cb0a56af0085f8dd782a622d8c066d275964f30bb207726d517ec36abb1206865f123c6870b01fdb18f3de9dcb0329eebc8ac692f2fcbc1951f124a76fb84ed9b2cca7ff2eb776eae0ae993a2df5d6178a10142d310329070ca725670311ce5fc603f245d477962bdd89d2c3e573e98322fe4c981565d8e4acf273fab459d771c6c11d9f651dbceb12ede3dfba3a901df843763b685bf93c1b3f0f1a489cc02386af50659395dbf5b8ca19543568898b49e1f9500807b4d4c3fe1d5dd29c5df7d9fe27dd90cdf1dd75dc7e41bd494e9bec429475b8641cf8e9c470422ccbca4bb309b962563123b2ff34b8c44e1bd0372317f1a1c36ca72d72ad2396c788285a1c3ab3d8ed454d5af65d0b5d5fccf47b5e705ccdbf1efa5a5b821c69384fff0846623c394266256c69edff8f18844e95887be4431a5c45c17c029f6fa0856eda8090449756f25a26a28d7a3a0b54aaaff9c2d23669600ce3df96ebc8e8baf797d55d9844b71857ec7a622e296734726220abc61b3584ffd6595a865afaf3b04962c499a88c2f9956047a59faf810ee4ad7d10842f48ac147aa349c7234d9f90eb3893ec61c39c1aaf90a4cc0a81409004eb6283aba1bf1c25a0ce604beceee9180db20343d49d092465bf65023521a5d3deed3f811340c0e10bc74918b2d18b979e9447becf914f9c5aa78ea7a56c2e726f5965c2372f577e9255024128b543f285d2487e0000000000000000a0102702000000003594550a6aa31ca0781dacaa05f1c8a4b6e85160a53d6df1b883650ad88f889e2f8aa6db46bc8a0020db7e92261c542d4c14ed7d9eb055c58a287c58306e7778a86051371676d7a72d7b7174686a5a3066504ce64f959b008e8c282c8213b9c200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b35b37b30cce8ebf72f90dea9b3164b6196c458cd14feb70ec14a195f83ffddb9965bfbdc4647b60d3805f1d97be70eefe9edc5619824cd81e80d4803826b8e3553bd63b21f8a5fa06e66151c5106a7c1dab0cba57b4ede802176bbd3d1efef0cdc69835ad68e1f5e116315a09a3e9b646efa5c3a6beb697a4e03a6bf807a6e6022f4d701088f6fde382a77f9c0ef2b2f7deed5e41d1b2b6ea2cf5603e9851ee8402190540313ce4a57767dab896ad444ba1cd46531622d93416e0042572b0c7d8be0b079923359fb616389b589983c7f1ddbdbebce361f4516e76c3a63f463eea6da33cc02318731b07ec12eed077db1c908ea0c4c7505c428cfbc5c7389fd17dd97d032b09551acabc2b7f98f084d191135a069e0dd5a491d1a5edf6822d9eabf7191f032d546d31994ea70f1f6c21ccd485c0f9da191d3561b2553de1da26f2539f08a9022d91ac5416bd73bc5f129dd9aa43148a8fc9d3868250d0339e8a335186d4f17f0205969501a7a1058ea2c67ad849f56c74a7c9229985eccf39e0adf35310ea23540313373ca936eb9b753c0f2e8abefe5c0686d796a388a3daa123573d88517838771e52184b3f1c922e92d161e87fa1f20c3937b47e425d628e55ea9c0d429b51ea4b925fffb33e2b9c55c9e760565ae118c36acd8723ad1d5ef3643313bc0c84252f21833bbebb654225c93dd12b56c66e89a317c2c10a90c2f0d5352eb25432d3e2245604846944ffa83df417ccd4a1d5c51028b1768acf7b198594e5e06545860977715d8b0b4605d9fa68ec0c691218f61f1c236b5be35bbf61d766ee7775654f9de89a18228353c00042e6268ae796df05cb167fdd996eb4533050e3d091436e72264d0ef19867ee06931cd3e3b1bb79efc92a8ec757670723ad232de3b2ecf0ba0128e3eeef08ef5f9c4f8125907fdf77cc1527f8413248a939fa39e82ec37783d61d9feae02b2f185f7e4d4974185e777f2826a8ca29102bc6fd6c1cd71719748f9b75d198b73051c2047f0778d5c818e8b4e3f0099f205a6af071f698e75aef9a085caeba80283d8a62705068a4dba8f70985b7cd495665057076858ac1c4a38ffa792dc36e506bbaf741bf8f19857bccc3e0f7112f0abc476f32585aad1416c834a98b24a45056c52a756fa4aabec85bacd38971b2b5354db05c41359c1bd58461ecb8e6f4e4a2e86d2d22e30aa54ec5549e3ceab879ae526ea42b888bc68627924ae0fd75f26d1ae6361fa8d0a2dcecdb1731cc699d27e0e1e6c8c7b9eaa932dd10bc20aa942b0541cf9b60a6f54f3c490e21196779aa9b9e5f8037d8bc8f2a650f644c7cff28da45b7432113dcf7a5d997b637e0c8b5c7f8b0cd6729e5b7c967835bc70064f65c55a27a384a6aab76612ab6c8c89be7594f429557e10f4492c7d26804dac870fe59837d32ecfaa32a814f34bdaae50e0d344a90d8dee87587ccc60a7019fc579f46024afeb3c29ff4c32db70d73d3044e0c0f24a78369b587a46b8d6cdde0af4efd98843fc0dc0694bd51589bdc540e4debc00df1afc5aa5bbfc398187b5ab0e7f6ff262781dbb79a69b9b87a3e7a8c46d57c3447ac3fd787c90f255ef46f3723983cf963e1b30f29dac60097d48c174cc2789eb7804e903de375f40f024950166a056425ac4bcb5496ca63315172c25ab49acc31c1fdffcb5b6d6b2b02e2c09b39e84d3a23ae9749b6da3ed28e7facb387cfd1005e7408f2fec7d04426ca7c5567a33e734e2adb3c0023e8c0ddd5d206f2d7dad29bf117f6061f0353a9f6901aae2173f670fe61f721a6f7999d84fe02a0a6fbca32390ccccfbfa7b9b0e000e4481340057da2d3fb15b2a4a4b859cd64aca8d179065c4fc2c063862692e354a12ddfa22cabc99500132e67f736784c8ae2faaef56a40ea55ee5921c679c16539aa9440e07614ce6e3efed4f92533b18e5edbda0ba6aaf00b66ae675ceb8c1ac5011c39e42638a007215bc606e3e4600c943fd3e5eb0d8cd0a6d167b0a5fb73248008e22022da405eaf31a4b1f15d7b2212cda1298463103713d9a90b7be6bb96288122ef37f184adccacec9d2b0332b0cc02bff1208572554101be76f28f640682d9916ce60a2d06f95ea87190277d2497759e44abed689cc9a74622dce6a5594772035dfd5e8d57d0cf11b41511b36c4f6fa09627db7ba0b336fb536dc815e6ea85c7519969e5941b44db5bba0964d822392e50a9a23df3760ec1746746dc0dbaa6e2568881db699b3c4ed9cfaa42fe4b522fa9aef8bcc868bb134a0279f02fb9cf0d9c4ac6194d8e47d0792d5691e7558ada10fd3e4642aa95fc3560a959ca78fa2a9066282486766f39dcaecdd903a488fc605df1fc5689a5d9b2f6e017dcf15734a2dd3ca7dc48ef5f32400f91741478971d6478ffe80e241f3812dc3432c174c9d45f1701", "636a525163ac6a6a", 0, -333825940, 0, "986d38340a4e5eca99e8627a5b30a52d36366e1ba1487e4409c558cda2c7ca46"], + ["030000807082c40302273712cca9ac6533c494de7fb7a2fcee0a80bad9cd8d6a6bf81e4d1ac3b9943b0100000003ac51aceb4e914b4861d992e680dfc6fbb6366d0f5a4acef71f8ee4777d5bc7ad1089b9565f044601000000045100ac65ffffffff03aa49870300000000095152516a00ac53006a9b51f202000000000900ac6552650053000073b8c70500000000065252ac6a5163000000001e6191ec0200000000000000003bf484010000000036d9acea80b5c3e0c76db0634861d114de3084b353261905fc0f40f4f2d61ddf07fc9d6f069fbc4b1ec9ef7d75c53e1a1af9740fcb1c73a2b3142c8508df5d6de124df335109e20b879c3a839bdc0c5ac7d1e0f931e4d923b9c552dc65151b67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e40f8b829d875d26335402b0685047e5cf3a0ece29719137f3d39fe3155bd74188ef0b756e9e129cf521750e0453c41d196f14c9a099f5f77890870c6c83942419d6309bc4e7ed73b41ea8502796c124205015f6543f5ab30722e86ee992102db1290ee75b71221eccdfc1127ec9510fa27d37cbc6542ac8ebb5aa139886f140301f25c690db9d3de063aeb2278f85db3d53a625cec144e46d7e294b01fcab26603185c073868f5410b5342cc6f7ddbcc2f6defd194d10e0c82e860a8a161a7cd5c0a073c46b9fd7192e02278adb1cdb9dc00bc0b3d4b7b8ce3ca2ae8e2a70d96c27ec1ad3ffafe0f48cba18feb33ef82ffd74b8338e4486596548607f7bb11e2538f032a78618537de84dc6047e7d20677664674b0981e96e97c65b5db306a4c5dc20c03154efe3e71e4f4b33cf811d41aaf06fbb66bf55f84f3fd4ff921b20c3e859189031fa1f2b86f4ff57e0afba246d33c514410603eab0df72ec4ece2ae7752930c7702033622d4daafc54b2bd8380f789ba9e950882166da0af8ad2f9a3b383744ca6702147763f1012bd250f1cb63d4d3be1bfe31bbc050bde2d7b40572555c787f2439d4e020768706c8211a1793d705419e9fe5beab3eb5f9c572b315b67eda6ffd043f6dfc9e4ac6b8b9292b198fcab6b339458354fa8fd015fc921704c52cc0d7932c2cdec09fd94e11d80ede08e5bdeedd826da9f3f643e671498b0b022f39e6402194867d833223b9c698bc255d4cfd8b322a36f3d50b913704b839ac3bd746b7ff281f4ab500be36e696fe8c4c9dc73a04ed5f51b1a6a5fefde6b82b223264fbe7236fdf62a316cf1925e3c59a9e5c5af7e13d9cc44beb4734874f581c969398d48af90d7e146eb4b16ee16414fa95c942ae7687ffe9d0f7ed7fd7a66af673e56a65882253662fc5edb54703c97af4b58d3ef1685ec6e1b8c14a10dbf8471ad8f55c801a78018167a41cdb36b49d8cf17a29a51646d7c301b81edb366483c0a56afc6691fbb65228f7e84702e474021649b6627d16177a31ab444fe55f5cef6bca5165be26cc46d147bd30cab60d5db907f8271675afc388def6d146980b601e0cd337c4e80cb897b53919e2c776554d7cab453d91b7b3c66846fc7cdf0a72a7ed58b724b524272d01b02b71d622437f7350e702314d3d49a02120476727953315c48b4fc182ddc810f61efb9d198cd6a71db20f1436e499cb0c1d431afc8be6fca94c5989e4d93b92aa6154fec7853b91e72834497a5a2324578446d4c04265489a2213aa6eab99a93fbcf68fa0e3fc037d2de2e5d6057399c3eba7e5dbc63b24f4b0841e34a5b2e0c08a2f5f96e741100f7772f61d1686cffa13240e18c2af2f3357635f8cebb9bae9b5a78032abd122c99ff5aeea0a02675daf7d78eea7cd7854e9f1968107dfeb78c319796ec583cdb50a468c06011f273fdf340ba2f7be587adc4b6fc221652db89b1c9f247790446ce0eb2cedaabb88b56f629954f1fca8cfccf4b2b064b6effe7fb84ebc4bfe1f976f5e432482370ae735a25a68c4121222bdd800a3418561badf2f66854196d4c1f2432ebeed07731e600847b2a7214359761bfca00792d8c33cc3cc31a024f74b2542e4034dbea4b95d582289ae1367d6dcd9ea9b24c0f91085eb673160f4ddbb340f496dd43ac9f2efcc78857d29278e81cd4e395934da4c86b32050fd98531985bbe8de1fe7ef189868e5593130d646ad93783addd2eb86b0b456e38cb497ad9be448f4b9d5fce67cdcd498180a03af7617af243455e9560f904e3fdbdd1809f936b679c028e5ea818a7a9c2f5191e37b882002af7c4337a06ce883aa1b0b4738c942dcc46f89e34f7ac53ef7f79f616ffe341786c41cbb2f13ee7d62fd51bf525fa82b16640d720970a50add2a30135459171146a1657f4346cad5e0dcc6d4e58978c6ceb621f200cf41081ca266ac418e7a5821f6465bf6b7d9801b1de317b0ded07e6837d84a543437100d6aae5a80f2a84cae00b4c7cee5a21b6d8f381310b3c2e65880a36b25550f8d46f992f0967c27d3a3594c2edf1053b204fc9c4fe6614773527ccb06723eb0a2055c1b52ee414069894a584d242b5d9561a7fb1aa0ba0dce715806a339e544b61cc6e7515110975e75547cb2d1ba8c93fbcf991e38030d58be49715bbca733433d825c45194e7ad21778316e3e3b259efbad35fcde404494f6d4e1a7d1757f5458997d1185dff089a892191af698743c6233ce5fb5f202cf9a82739a6b25f786cf2c951f5fbc978ba7a6d3f2f5ddbce3599a70560000000000000000193f4e04000000007f0e18f52c343443f479f8baf22440733f9da39612f0a9e70923d31163027fa4edc71b7baa2b937f3817af19c276993b93cdbb9f2eb002ff4b80de77faa64c9428f0c617d55bc55df32e9a799aa37006a6796331c8f4e2a1f4d53e98e81bdd69000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c52b5461d9fd4608a4124bec04ff18e1248223e78357c15ed140a3435d30735c0ed040bc6b7924ac16958b1868dd9f1c5101470c14d9c6436a5fbc9d0f0f1e64782cde3b3c7f228a0593ae9ac4dc63f93d8ad517fe8e17da87da78986bfbeb672e2bdc8296f8d22ced1aa4f887171038cc96e9e9b400725daee9089d520f3560311506185507e1034ec949b09717a7cd1a2fcda7bb386fc87a3746487ab6ef8b00214ee939659d12ee95bde924ff3ec6d9de3b4d7d39595c17708f8b889e8ebdfc80b07ca415b8f7e4bc7d633dd983270318084d0cb0c46c08b9932a67774b029017e27c3330d9949ad80a49c97be5fd934dd7b1d21283326061dd0369c719573da5703094add6b321f17d93fed1e713955ec7a37ec46189fd9119ab09d9d5c47a19cfb031a730f1e96ebcfca130b6efe79626e1ab76f208d68703fdf8aa77650eb69a119031d393edff11114b31514be679a2e993a2d2ec7f4ff9b6f251eaa2ed2fd85c3f7030799c0a5d9e43f196d2dd4fa39ac36569927fc6b2cf2d453942ab1c373ef430c020e9497cdd5dc069fa2ea97bcdc631ce7f5be51fb0e6dd1075a3798ca32a0e83651ff45d0def36a8d97c80100e029fece71afe6dd088d1dfa35ec5acec205094e1d125e816c2e9c3aaf3e4fe6948362a3957a50295610fe9e84f0890c43b56a4a6bd36148d1733cc98ff73d83a2278992f22ea8b68ad24034de003c76c2f0a2c18ec0747c0580e19df2cb6518b8f4d7626088310c602c477e29a91d4cfc5bd0a12bdc56dd9c4b00072b7e9cb73b500127281ac75debef631954da19e48d3996b305d0acdf1bb09f662d8a9584e759353769c7c80322c0f19083e5f6774774e01eff6b7d6d3f40fe05a6dc97dbb2e11c239a5122172a0b3d66713cd80bced56d9a44016a0e44552a52452dc69627060176541f2e3bada2f37bc926e2642cc73611e3aa7baf603b6c7ddb67a63e2531e515ea852cd0b1cf5db57d1e212fa38445104fa3ebfc48f3a6c2177f7118dab2298987cdb90ec68e98766244e2b588d1ed7521e54c78bcf9eec23f67c31f686e5bb83feba2af745cb09144398277547bbdc9e68c243c63c1f176aa5131073019949d6100c80298c543284d71ebe85f3480187563d5f39820ec1987cb59de8c798be843fc8a572bd4f535dea9050aef45564f6820a30cdb5f8cb5dee2d50298126ecddcb4ddc3798477a52b83da9f4382567998a802ce0608b3b1d5b2a120262f80fac8fca047abdbd494ee0de3581b661fc4521a2443162195cbcd4b02ce8c8beff437cb4c5d87129645d40fc1d93d7c8e23f25ff53eb0c32b865072557c6967729cd1246a63294372a93fd032769022fa732b8c2349a6349458b1d21db6da9ed09c0458108cc7f42c3d88ae1188deccff4bc66b5c5fc93fef60287aaf502077c5461df5884f838a1d09fee2d4c2a2ca59e5e50af162c849419fd5b998b1fad19e0fe39a5b806d9bb40904785ce7d3827e518c36589316317fb6e8c51674f2095686d6e0b40ebffa018c952de7352838d6654c22c1520289517c67d55907182ec8bffa43e6b7a39bb2adb66658738b4f7b7f25a0c68396dd4379431f023f5e8a42a0ba835a50f95bf7afd94ac8219b1f4ef89eda570cd4756b09a88479adb46d4e7dedfdc3c47ad77b5e03f2c51ec430f3a4a7af1a432bfb4f905656d245f22a4080a711c6ae786964341b082117be057bf0cc2779ee7b7b885e91f3341969fed556ea025ec4dd6c3e73fb83b708140909a0c546b5090f85bc8f5ab4652c8837b60900eed598030bc7aae5c3d54c9c094cd05556b8a36158d52ab0dd2121ff1e9fe73a4fa3248b7c9a7525349277a445c8fcb6a6fb86b00591d495b3d022ad08e3f8b49c85d51f0af6b2ada644f87dab0e55ac0b385f11de5632cd725f3b2e59c0263924e051ef62422efb4b884da9c685fb83a538cbc04cf4b7b26cfdbb4311f87d98777e19f77a91f078b44094a3fc2fe136826dfe2255d598b7d5f040dbe60cd66e4420033531bbe767c24a08d2a191468d8aacc6a659c849133f63c0d1842542c20df7de0239b62b50d501b172b448bb8217d00ebf051d14147e160413a8fdc8cb2dafaf36fe6e15e3b4e11dae23417810ab9820f1b80a312476e128b4d3364dcf277f8f578633231aa47cb574277d195efcaa79688241ea2c4de7f4b59f8dfb9169d3ecfa1d55a51af3a3d714783e0fdf65cdd69029c0b7eaa230dfcbdeee5428ab0217d845c43a4bd928ce3192b3925080289321993814002d0d746d03e8b38cbcb955139f9a091cd5cf1cf7397e99f65bfa37d42d0cf84ac1fb8d9a567bd786b33317859a325a16ac2c54bf65e80aaebc8e23b44cb4c088f582a7183af9057ee25ca17f82c972eb7a2d5ac8425f05a6ef5bdece45ea5ed5797bbefd12d94a1b0d", "6a0063636aac6353", 0, 499041583, 1537743641, "815c1021f7332572f6de105ed414892e37c88395e118321abb1384478370f505"], + ["030000807082c40304844a25d3732ad254fd95fd210f4ca1a04c05747307265072c20a00089888d95a01000000036a5153ffffffff0cfeb90ad6e3fc9ff3e30e9d13eaa0af2a8de808b33eca064262ec9476a345f2020000000553ac515300d207bbdf7b27685d5dcbfc889eec262692b608df601759671c9fb193c7363afdbd5ccd57020000000153ffffffffc1438abae9a75f41aa2cf30bf8c7703fca0933f1fa19193fb53ba3ce9e93a9bd0100000004ac6a5165ffffffff028b905300000000000200acff61d0020000000008ac5352006a65525392c231ce4087c95002bfd9e20100000000000000000000000099fc0c1f16b671a5bf494c30d4c835792a2eef01ef998d329f05a0fe2ded90f0d1e53fb2ffacc86cc81d530f81ea65665cb6bd496a06908b755a8538b2897c6a98975f7ac7a0a44e14ceea7e887867d7a9506521829a2946b50ae9682061d96e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000072062255a0c46846b345fbda53f320b7aea51553b40d85a35a629fee01ad74a6a09ba99011fc3f8474cc530e4c5c8c3815aaf8e73d408682c82bc3894136ea90827337e416b83ad25024262ce0fac14b6a19277a39a2fa4907a1700cc3840977f08b37a690bdd2844b46b48bab8ea596f6684d5f6cb4bac91f0d55c416376ca9021777b781f1ee46e4ecf6c2722d97d49d53ad77fa262ea01af972ce3c7484f0330207acfa862d9fde64a5bf30d037ccb21799dd116de02788ccbdef94269b3fb95a0b05b60fe4f1cb6d8d09262c6da2094af299bb81d29e164728cf6eb94ead5235a5d02b19ab18ff8b37b8ad898b4e5b8b40af6bfe7ed669b0f9119e39bbe4aec0470302c1159673325701b1ca86eb31d240d9c9dc611990b9066110fedaae9f697bdf0227db89069415cd5e8c720572bfc14f059c1ca32cb804d08808e319310f51ebbc021f1e0352c59bc85ce191583f90539ccd5e3b875c03e5b22cbfd74ccce86da5e8021fd4545a5f0ed44b8fee61e8926b822bff8b30b26e78f1aaac3de5effedc39b10329710af0b66e68cfe041c1836b6ff9cd23e5805f4823ac989da677a41b1e469f20859ae9fca118dfff7b78e619b827a34e8f620ab58b91c82818cffef843918d08c93ff5a258fec0465649a58e57647fb0c434334274b03e869d8990c55d5862ae69922dad665f794e4e67d21c4f835cac686d6ccf67748e61d71a1a26353a586d51f4b3284c5caf2a5e3a6c93a1670aa1b3102829709b0dd9d7e10a3a6da8dceeb572119b460f8076b53ee16889f62387ba8991643aefef59439f493703d929fe2eeb316e34125cd32c2191c0ca58e41a04c3b95861ba1d079414791383092eb9fab8ef7900dbca92365298ee7871015bf100005f1870bfff0d5e76c2888845d5419d73352b8530c38f804d734f0aa5317cf658c892e9f708805572d43206a0248e6d1c2f31b0f09e3bd9e504b1f60f2df5f0aa28f71053a04397b50e8ce3d99afbd87ed3652dffa36cf8a5bfd9d21d419d4e68da5c297e1db6f27af2592270944670606fe42e7611bee95c7df634355df0d6216fbd6b3a79c0bd701a14070122f0311027f15cf1e517a7a45ab224b92f5af3b38a7ee1cfdff0716353c4c4ebf90481bcda67ad4c61785884a4f87bfe570832726925587116b4db55233e13593eac8a99fae1a02d8e6179654afa893d60f3a61709469b0c5b18c729c0989e6dd4aa5a79b7ddf178e54961c7c9816bed8b0501d26023d74f9a8e3d301ff48e494a3ce9da6d20b2b4480664b3a9b3ebcf559f2a018c48bd4eb78fa1374c7717c0dbffb9009c0b2f31a5e9470fc0afa8a0e3516f57ce4f3137c8d39a7722fb092c3799abddf32f64d9c0f8f4448f9bb9951d85ded974e06caf262b4fee552c24e17be74ac86ecde81b1f71bc9f2f5f212fc4b7fe0dcc528ee55aef40ba5034955c9b931057a5c35c2e26f769790d082378ce52371f40641117b9b8b91cba1d09ec62df809883a1cc99b84eec0e1e3a7806ab76676f7323b59761974c5f1f95789e13d3089d9376b64b7c5335288dbf75d3b143ae52568d7249ca344843e9560a08240a1ce816376a8e26ce95fc9fa31494d6b1293634cdff7da9cd0d60a804a629c26b6e162c503a8dffa2518f998840e4cbfec618039e234856731e23984fa1af2c1e2fa4562ff7193b9f5dc285effe9a805c15d3e80ccd738bc549e6ff92bf0f65d7257cc0d905ab72f3cf0ad8b02eb867a1efc7f5d6b86007f0e5f1c2aba970f3fa195a8443b0c67be98a6a985de1965e1e9dbed6802bfde5f277be70cb20b8ec9de5da2c89106c184508953f2086000de18e8783d84bf39c261a3c3576fbda7b9d9ef423b84bd0671587b98aec798c51c67204c2440de3b90b9326c21af7ef54ac52e37d66a69a66bc2d349739589634d4848ca87b4a14ec05321292df3d968e870f86b3f948fceedf46409ace3809485fd312a4137c154c8b125b3e9892b29c41c43dde10e0c9fe44f3c3e206e882a64dd621915338a98df2c6dd1ed15bcc6e1f07f94ffad282e2c41630b432eb8e09e8b5bccc337ae122e17dfd4d14799b4feab79748ccb71f6269cb608a054024628200a1e80bb7aee87c5180f33822ace278fd5987ad631e49db5015ae9fa67fb83b88b2c6b4cb5019721b3ead386e5473ccf4624ed410e06275edf69598e26cbdaee7c6092c5edb8177e44eb3645f7a56daada1ddfcf80b9f0be4eecd1f72185f3ff170849210396dc13e8eb870c0ef33fe91ff4564f4a1bc910b54fa1231c05ed4a46c9f010000000000000000000000007b8690c7a169e9f927768b4f5f3b47c3cc7b52ee87ed4a5f248d4bea43d9d84544a846b16e17890d57fbb4fac33df46eabdbe6c7424eec514a11f02de80f9594aa8c999c3a50befe76bfbe0b9e4b0c5b90b8f85903f579b199a8a74938dd1c6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e450dda5dab2b919ac52312eb1a56004db4aed420ae06b13e553ee65585bf59ed32b6ad8c7c8107d24fb800bf280d70c29b8e22e53918b3c0f6ddc0ad192a3db30bb944470be0190e15e3f1eb6549cc03a2808c9cdb5c036bec6f3290dab3b1089e63ca8d8f2fa90a09093edaae70242143a516006933a3353ff0f90c27fbed90208a1650356a347e739abd9c61801c1d65666fe81e1f22ade595c206051a8ac2d030ca849f41d5e4117d87a6f83a8ec9bcec2a406733e9de1ec6b7be2be85c733d70a05a68f23c40e6af5e653ad54f388a415c60f7ad76431b2ee73498e4b571f93cc6313d27c0ffd94af288d09b93edc8022fed861f35dc2d39bfac3e04e436894630225e5a6b0833e602f5daf6249877c8d03205f97742e64e4523b40a0f8b3228ab9020913265a6e28fa706684f058ff9a8dd24d71bcc264aaa05b4c57b3b08c122093030f204010058e803c02890abf953851b43f7fcbb71ef833db38d1ae7f2469dab6031b215e700f7f6118fd30ea4183cf07ee0134c175df1b3113714839ff69e8f059020e51947cb44bcbea7775071e63da377d149885584752c5bd6e17f7eafb7b4c18b08d7e8d9e02f695ce7789ebf08289b8345960538da3f47d5e060164cbcf38f6bcbe7f4ff737c78c09247ad643c625b2a447eaa7efe96ec9ea2c998fb93001c371641421551ada646273567d54014719924a3d0006fcc9b6a83447756b5de2ffb241cb24c7b65a81348a986f2b64a8ca31d72798c40fc8296bfbe336bef9d7793b7abf3e50e0b331631caa2f6ce2ff27306e572fee1e19fe7552331af664f36e88794d0dcf65fcf04e9a308c6e4e29d87fc03f6aeda05956b25ad1872d5f66a047c946e63508a56991ab5613dfc70edef4bd1b9ed6f424a3dc31c8618f2d150e2d08d049f2956356eff4054ae37ee6269f680fc62899f03ef678d9ec82f3f483cb5194d11ef3c59039d83367c599cc16d8cd9484f53418170bb993406b2b68bc5e9d8fc5898461c5c6fdd816bbf9743b0968459e77df5782c6c97e20421c4932b8aec0599fd082c38c442202b283abd8db989a77fa6ea97029d8beb98365a23a90cb1f646794b169be8e6ac33400c2ecadc7f78a875d91150e3c0aebf7e5745597ea2e33ff211e54726a5a3f5ddee3cab9682a03e646718769498290fc234fa8c6a67d6f1cea97b12d2703fcbe6e4acf64fe8096c2fb078e1b34b841ef0676eb675f85280eaaef3a1ed6a8cc2923d450242e2eb319e2d4ec20d6e6a3995fa768c5c2bcdac27510e5759b316a1ff86b6c905b06522b9226df12d174cdd0305fcee8c06e804e8373238187f5c8ac6e966601a53876b0d04bfdfb6723f904fb7a792590fcf89931753c13971d80d209c3f728ef080482674e491dc0943a832380f8eeeffc54ef17964516a2e49ae31b5063c36db0d0854bd187a0418ba6bc08db32e973aa4a2beda33f845f050826566bc9e25275fc33ace34fac10aadf0058f8601595c11b8d87472a51c843b8309ad0dcececeff95b1806c1afb6286bb8f77ea85b49da4b5d958a1152bb473b042ae2648412f2409f81e4b93cf2dc38c3b5e426037168289a9cb6df84d52e87911538a4a03d4b90ecf3156f117dbf51f75c8cd4e3930733d5ba2d5ea041efddca950b447dbee215f45d7c800f12c50126bf568494592fd447e1fc1e1f482aa8bd1af5582965b39de13739e56333e8e3c30a67c865acadea32f3d315dd9de0c6d2f4af41c711f453ad419fa66bfed70c204abc13a54f699c1c9333ff7bbd4a3885b315e2edd7e80dfa7f3ae4bb96cf49aa576c6c8fdc08c3b6cb19033abf8f1afbd842c248be7aedb1ccbea4b131d8a3544bc2ec4703b16865d41a4d9173df769cb8316ea60da42658e9fc652d4a54997c5ca7fbb58251a597b13747d21ca83274aef5f63124aecaabb344021ecd50c6ccae0703d428958ac06dd9dba18a38ef8ee13288fed64d04a3dba97783f19974b520fe01206f93d7ad815b35f4972420c37d589735b4aef25254eda3cb72d0e2d292477f26d6eef80ff8655928eb72cd9980d28c1538a379ed8250eca9131cf709a59465286b0a194ca9648df3f763db1f4b1b18ee43b15f8301cd4262172c83641ffa94bbc96c6d9cb8715da0354319d4768cdcc7924f3c51d0de2e660970cf16dbc1398121e35cf8b00ecddb640f701d133ea3e45dfe4422b6d0bf2e79aa1a2f4187964ea247d0b60cbb9226ba7f65754d8e0caafd3144b7ab8bc8ac5563d7e8633951447451cfdd770885dafb344a8a7ee7c7e98db8a37e01b16d07119984f0e2b6ef4a1d3e9a1a449c83cced9924b80ce6b385ee0efed804143253aa64cc0a07b73ff4331dac3de1296ce9f0b6cc0190cd9e9e1ad08950a05e8a5669e9fc2aba1eed2da75777b7eafb249427ce79915ad19c2b03", "00ac6563", 1, -1259523388, 1537743641, "98f17fb78b2f5a34a54b6a9710476bf829ab7f5462f2ceb4298183d52a38c13b"], + ["8f822e45013cb6fa0744d034a0487b6ebb38af208b4ce0958bc5bceccc5b721daba95781450100000008630065ac655263acdd810b3f01265e0e0100000000075300ac00516a530000000001000000000000000006d1b702000000001dbd2f5c5c8c7e2bb8dea687d7503f975c95e64fdf46a9b8bbcdca335449118946c0f7284cd47fe15481a192290f7cdbce8fca59c3b431a36552465daddd8a882e507d4b9448f24fab3325cc35dee46cfc96d002c983d7ad646ad5d964d685cb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8564e7b5542a381b574dc645ef8ce3c58dd0966b24948589738712c9bb4ddba48979ff1156def8db470413bc9cae2a8dad8a0a6d45e65e0824eb6d7732e813659dd742ef970e7c7c0e155368e131d1ae76ee4273b57f69c36559ae8b5272e25948b8b19b33ee424dc2c053b1e08b7e480e5a5ee2b45d373045c375a1326b56b0300f705d2a9ae6066d45aafb8fbfd5b2a2955f322742f4e58f67ffa02d7b9576a032d05e243a11fee87787a25a5322a121810f5ba34387a2fea4615f33eaa3deba00a03a94c285a3b637fccc6b5e2a1f92ea2b0f4f725e2b5f9ba3d4d54ff50511a313c7c59393c5d78905ae949fc1585aa3a4f4febda49857a3f014d3a9c8ca89b3b0220846a20932170390f6b038055211df643368d9cb32e3cdeaaa33853d9237cb103056d3d9e94bae1ee242e0dfa63f47fecde556ec032c8b3fa672c4744b8cc7491021ed9ebd9a84ba5aef36965fc95b84449ea30ad3de1f531269fae36d3528e77fb0213e8fcfc68533d49f805fb30ed91513cfc39457e95451629505169d3102a20a20223a3291d5c81f25e8726fdb0f8eb59998b3e104ca7ebf3e652676a3df45022fb298ced0786722165df5c9566189ea5306f00b61c3e0ea149f17c6aade38b2d853c41c72ea5fc53d30fd9d268e79652195269fe235b723b44af0d9a380759f1954eaba5be1b1fe6eaaa1e15eed1c47676f13ebd620020b2f96f51f8a67897c50ab22b66fb2ceb78006cbfdd3a12754ea06ae8b28f7af9e5ab19a6718f664cab50369e3fd1caab56a3fb4175574bb049d1736722c2b25ce9dd38b06e26c44316bb11c3f16940b4d75df274131054236c53ceb9473c79f8493afabf49a1d883d4c2d697d54bb577569d571f74e10abb4cda3b2b8452fc03f7cf8247f3bcfe79d71d59bbdeba2649cc77fc596b0e05b8c79365124df1acd36c203e69541f29a6c7d245000c86bbfa4ee7a12ae32646f9e0a0f3a3a193fec3c26401c3008204b3f3d25415cdeb85369210c35afecff3f965d3cacf682565a2707d32f471f78d7d5e8cbca95af9bd4a11e766569224ad17b5046617264ce0cad7225bf5b52187bfe185f097b8c075a8a20eabed343770c63d0c394cae92d9bcf0e22d3407bc7bb356fe6226648f058af2009bbd5b176f3e1ab8c16ccdad42328dbe59d446ac43efa7e4717b5d40d6222069f128a7c39bcd636d1fd622f40be9b5c0bfd5352d405e286086a8f8bb1ef4ff8fefd92b2537ca8924d915d250f9278133ea04186da4bb38581ecb87938526f1a6f9c0760423444d319c6472df6970c74f26f0de785a9aaf12b6b4f3602992572b2ad24a0bc0f506e759f06a6d212aa0bc937696004c17ef96730dfff5517bde51b1a47812f54b95bc27828bef833f6872f25b806644979b7ad80a9de0812a42fdb5b26c65d9cf4c3610946d65cc191ffdaac961cbf6a4ce5dbde9af85527f89b9e2fb3a1563efd72a0d5a89bd5971b17f38665e145872f73a4e4e7faab3c284b0d1d0ec46854652e085698b23d7379716216bf09a1cf42bb73b5e6d1efb873ccf627aa4b8913b3da13944164d471859cf0a2400250757e46220da87fb31a78fd3b96ee8bd806ca5b4dd8ab8f2d968450bd11088c4c67f4b1a42f24da7869079f7de502bf2c1f74fb7ccbc08cbd2a0524f1081a56495f6d6d7c93d1781acffa0fa4696ea75ddee18c14554ef0fc2b52925781cfe3523d2d6d114f75e4f532e44d204672bf9112c71a5c73f82c30b775311539ff6d8f6348676be0d7c1fd2507511294f9643b64c457bc291562cdf30fb4cae7191b8fed5ab1326bc8d81db4ead56ca9522175952e548e27227d546cf211de7110b1aec29de2ee690627fe07af68366a31aea6512f082a02a6610053fb7b04dec602c7fcd53f9de3c3440e59b89e13e3bb3aab345c5e5cbd5744e1244e2bc04dbc13e074344ed93cc74a495971b9d2dc1f6c50c783aef31ab88dfeebbb4c6bcb9577db74d5878f6a41aae7c99e485b39e54f74d261dc0058e93cbf49902481831f20da9122cde8e37bf874c7064204b7d87b4cbde9871c064e13d4f10e66576aea9ccffdabde0911d07bd9f03c2897caa6e94c950b8712e637e8eb891add43590aa80b441be0e2bd6db746240056e2e70de806f3c0e7623066d9ea7b55e674a44f76e4f816e4a72ef0c75028289bc6d8ebfdf3da468b68ae6895bd013327314a2ea08a588de82af2ae1b9f570baf77b4978969717479747cc1fbafe3b59dca8841dc4854c724168223ccec51a89734ff8774253eb9ea40439941440680cd13b714d708af5aad638efe1c1118f9b79350912e806e700562288626f914899687790fb78fd95f40ce5dec29591fc946f3247361e1646b7bb752c9b4ec03bbf59d264f5a92fa88af10e06d91a28e41f5cf2a89e0a1712b47a1d08", "005363", 0, 1102593987, 1537743641, "e899c4ef600dc3606cccfde3b515cfb8beada1e540ed1392a003c773be4e0442"], + ["030000807082c40304bbf7d08b9f1f37513ad2bb4f9bc8e3402752618ebee85d1c7e64c159318dbec6010000000752520065516500ffffffff797e3cc1b6bb9addeca1b04655024af32b1898e4569f2577c3592296038ea30c02000000066aac5200656a646500506dc84b0dfac5af6040d4b418b0e931afa60bfdc53dd72a686bfaa2a3085dbf9201000000076a52ac536351001164f03a438b834a28c7af0f7fd672bd6bcb4087d54ea1b097cca91fbad850217fc6f0ec0200000000ffffffff01d04ba6040000000004006552ac643459f00000000003dff23e03000000000000000000000000566d9ab6ad2ffbba2acb015336a6c3dd43c7f871ea157f6f13a7d355f3f85fe14908df4de7bbb1a6445d0991ba71ca09967163afcdabfdb5e1c4fd8371c5d7bc62c62373683f72fff347ba4800c701adbb206a284bb3a258396f9ed8b0da441900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c884b148f17f541f5907ab3fbdc215d7976c0b941566050da6c28bf10d7ad5aad7efc0fefb93d0068ac1c245b8e833f800246ed32cd221c9bd4cd2f8bb53d9029815d01a33fc8f800a0977239f56355eef519160aff3647e60a45d67e1c5eba001f2877cc95642cbd2ac514ccd118afd88e0fc92262c90b32efbb38f3884d547020dffa581fa5babe53a4215a4a611ffeb5937ed42d8f8a0ade4b653dea51853a70223db4116783cbdd72f21d53740677748f5d3c47be889dcadb8d3a340e6d969960a075c4c629739482622da111684aaecf75cfbd40e5cf897a700942f691bda6aff16f6d5718f612fd55b7c9fb6acffe803f31a1fdd72532dd0e4d3bb9945bf908503020ef482aed0b2dcef561927f9dcd97e88deea629e12f3b0b73af0623597b70403197d37a9da4ddaba73ab95b7222413ff1cacff47dc7e7f82629976e60b887562032f166427c09c838c8fb88affbed59406ab9b206c4d2238f5589b5f1ded4fc142022d833f821a9ffa3894609175cede536d132a623c46266ee77318ccdcbc064e5302217bb82e45a15781529913cd297b2a25967f4c574106c89ffe93ab944fd2c2ca8142247e04ea11836fa9dd1baafccf831b730a6481d5a64e2c2d382711f664c5d10591efc6762f5da4ca8d72d8e40872b8306e2bd1458951f3477eff0831f598c57db5646b9e578b554d954215f4866cbbf48a68ba37b80f33280377a78041ccd1cdf6c96beed1927894966ab63809646ea2514d52e4799a5188b420f62697311f6ddc301739f53b1035547cc15a74a4f736420ba298e073a88a855755c88433114e40593cbe8d8e4668fb5cb75e8d6016014f2e4f28f769f80ea4c045d9ae84f2b3ba5fb4866cda5a9df6d444b457765e3b988a80d14554aedaaef4795bd6bce25a27e702cc570a441fe090ffa396a99afb4d33ec99cb299a98cae869924525dd67a893c76f2edda8d5977bce6bbf4c1fc1451976311b8a6b7bcc2f7650aac8b8ab187672f48d0e517459a1b40c6205a4dbf199d99d25954c85346854c17f93221d92583576310837aa1ca5370ad9bc3069d142ffce7c565aacb9bdb9231074d85c4f88a08d753ce52e1925ceef63aa1be9189c27742a0b67c461bd87af1f4d42905b5c293ec535d5ebd65e66e0a809d0a013950e1b06d398aec8492541c044672b7f64fedb8f8cf48508ce94eb6edf3ca4d883967439f7898caf668cee3a5ad0ddb8322024577027c3d7a5a354d8794e6d87f6362ac55166b97dcee5db6981c7622f67e96bf34a1c0d42f11d9df756ac0d48a02ba1b57261f28a9e91c09ac489bbd4b32d947fb08afc415a55801eea5cf0d2f93c23e1fd1ecae09a904af6e7d04c882998415b518c3cf9f148dba6d3a21f457a81d93d01ef6b716c998b1423e39991a330582c61686fcb83326fe61ef00de4ff41bf0846915a3aeefa7b1135f8294685567509ea5892316bb1ccde5796d1366c8d823f559ac8c96f0f1b7de9390f6a3f07ac8b6037becb672fc228effefc3df5193a477b00759491ced1361065c6032ee86f54a931d6e1f99088fd69219f86afe4a93e841896d919f1211eab4a272b4cdcb8a8a22114a4536d4df112a321aba58ee83b9483f15d65c6452fee7cea96501df70aade5312d3d275adf50e86fe5699505e456810035cf2f81be7acc181893c51cd64c677f19a29f7fe4a1da352e518d00dfaa49bdf7a0eaf3170c51ee1173e43ef9d221dbc70fd8e154a23e6ff605cbbd7f977a9a8d232d0073c37afd0f857df838960786b219b65838924e47f1bd7b8e0bc687e2d06690eeb90e82be6a4245e4395ae46de5204a0f49f51cbe4fd056ccff4a703f12bada0208add2f7977360695cecb03f684c33b2822e830edb68d43107b22aca33cad2bd78c5510a1d11bd73f87a979395b42fa6691ccbd0f83b886706c2b4783dcdbdf4df33d6b40844e90002f3921b5241ad8a594324aa7ab008ef0b11b0ec6b0b345536a74ffdb1b25bc6add0236413e28366c12338c4cfa4d8589c479b718629342c5115870328d752dcb8144c1eafb5c7e1291399c138edef27441f9b5d1c4bc11c1992b7f532242015806f6fa029524302060efd5e28861b52a95b099f7f074a6a985dbbd4bd7b965a63af0db10dab10e63377f65e787aeac991434da3676ddfddbfba3b4b95575225f20c40e0955ba1481db5f789f0efe45fb67b944e23187f77e52bf57589f7de0c0f61cd214927cf69a44d503020673fcb7194d2f69c88916ae29cccb19de8d923cb333f3045d0aa445c5fb46a0000000000000000a294a705000000005f11cd3a3df733f018c5bfcbba192fc4fa8db1c9af87bc47eb644506a3225de1d85f7470a2c30ca4501eacd75b41e1374225c7d85557a93a77ed684e9b66e5b3ade473146225172e0feb1abf0da1d3846b58583d532c668065bcf61479d4686000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000247758bfab2880973336ecc4e71ea7844bb2739808a1ef094fa049d3781cb232c11f31df645a3a0a3aed18a8770f44a7c02bad2f9004c7202815dc8c8424ba9f09020f4c3333ef0d92f6ff045278126b4076d746c20f7564b3f0587625733d29f3bf5c6a2d26f49beede3dbab33d535ef4a20703851d77287850c5f800d66fec03043c88e95710f974f996b7836f268d62e8a8793c7a138409851439481582d68d0224718b2c994cc47fbfebf41c2355a285e92e76429c2fa5ff4bd7912194b051870a065edd242bbe2076a43556a960b193baa73b07c9d02fc30413e543a55a62671fb36e6f22aa7c61b25c29a54a9b0e5bf6f1267cf82d8ac61fd253aea947b35822020d83574b5181fbd9bb17bdfd3e07918f041062cb14268f9f7bb40d48204865a402024891737dd2984c9b995f758efc2ac2d6e8f24b12a349ff9625c9d97b25005f02195bc195519d0fb61d005bffd04bce88771850bb30b9f4d05b82ff1591ac02b0020f25684deb2952298fe77b053dfc0bc184221e225110a30268723298f14c6167022dc3351c44d8aabb470e2d8f5e84170a255cf97bfb588c654212a2445db6f8f93d18e1d95159ceb9c525be41e0cc08a26a549f7fbff01867ee5ec35d8ae82df9add5fc54a32472bcb1e630c6c80e9fb314d503ec0ffa8432485b79c2590f619bb37aeae99fdf3f9fc21824398672bfe23949708ba6dfc9edcf91f8ee0acfc9ba242a17092c9a2eb4a593a2a21e82a8cfb71b70284403b10dfc7971ef60137dddafc8bc001d12309f6c17cd909102df038a90e87bfffc43b736f08bac57d413c4e3c57c0f78c80f624ffa2426c8fdaa59ddda50ea7e0ad8196fca7e0e4700d3abf306f8bc70b421b094ed06129cf080a736a7e1b4fc925f5acdd5b48a0fcf2deb0ec23c689684f6be427a5200317ba1d87a1ba205f87c132285a983d862f82ea2769b486d22b571bad459ff9596730112567a454be2511e63f248ab505f845c5d54d6668d0be384ac1cce190813ab61bec13822fe622c800a87c755bcb8522a0f9770bd58f52c83b59c0caebfded68eb83f63e231a4e5a234ff36e51594c605d8b995f34156aa6a7f210af54cbde7d06b9d32a5a6aa8ba74b1f0b28ce4d6ccbaadf29f3387bac70991cd8ec626b2c89e2f5e0de834185875361ce6f7e89b7e35b4d8af710d4cec00049ad3a5d1a8ff34953f38337707ba586c6277ec338122c586d3fa3cc03171a4333c4d1931b59028497fd600fa982e678583c871ebf5cfc0188e6d2c683f0bc887ff42eb96353a08ba71ee6f25e55bdb4aa7130b87a4ae1e3f3beb9066889e3d11432b5a3c5c3399d14148fca12648392c9d03015adfe547ff146fe5a36bf8d94aa43ddf99327192bcf256135b28270d309caf4d9f3feb583f019dda76b76dce925be2d9c91e86e479b30b089d23a26d7b1e78c17c51f5eddb62cd2df3b4dfc61b968f458fed755b1127d647a0f4500be7c07e56283557e50d3d42205ed0c72bc66b5082e396edf4796ce583b8a0e1187c8cacbf247f60d97f47757a24c889c64e39c75229d0ebac992e859edeacc87b66db30f4962647255698a997ff9ab0b0ca8f18425501227f2084a4bfa9c350264df84665b2e1f75d763bc2c9f5f59f038e90db611eb045d3b2350a421879b97a37b53509febceb241357e3762eedc803db13661a865a25644adb3643de42533988ccc5ea1aa1f5db341b49c3587895fd81817ad64c795e42405c2aadcb30c4b0ea28c6d3e4644c566f45c7a1262a3a1ade8f71dd56ed850dba1b9cb5c2811d398506fc510f6d078d7ecdd861e3e272ed97387e60147bded0910a2284f3563d8cf0e769abc57ed71ff872820f6b014ca2c6b9d682487fcb0afd8f5a702081cc0a5d09b2d30a20b08310d5232ae0b969420abb4cde2e5df2eea880f7a46bd4db7a36c7f628c6560c1dd967a81dbb5d944cf805113a52cf3028e4026bc66372d4b00e81a5e4da7e5afa13ed62728461102a387b8b1dfe1ac56512681c2b6ce881d917388e7a21c66c621d6f416004aa5beb8e01871aa7b8a3ce41e66d552612cfe3d3d7be905b652389c598f5db19e7e2d6d58902f48a210f0e45b744f56850c4b6112d5770444a23cc034fa845430f98a17ce8aec59505fc41bedcef1060551ef7ffdf06ebb75cc034e534ae4022a4ec3e3123a37a67647155e85797d3ddc3d8d91d05124e58a69ccfb7e98189a3e7861ea0e3b51276e58f3acb2cd615ddb6dc105bbaf6a8522fc839f7b09d1e035095b5d8aea712d0796c01b8a3f86cc2d020000000000000000000000007f1afc1751ac4597be004daeb51165ab0172c9b514645af78924cdaa895f6babbd4f990b68cf516febb1e5b1174c93545290ee9eeac0fffd25fc2d24f69f732b7f339fb930ad3d33137048f93e0abc7b833b1036837f39913a2f4ba7a777eee700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fc171c87651861dbc79f60b0592d66e24b8bfbe1ee290d82f5c215921c7f4d52700b362722313712240e26585f3ae4f7162d7940467e8ee32d0e529766d6f9f7296458840101064435cda8bdd477be74447720d87d8a0ba8dbce32a366f34c9886ce758f6abc5d9608a60710d2baa049f9306ca41f0a3307be011ffb8acd002f021e6179f2a377bce54c1731aae1e04f1cad70acc4cbc91f6421d69d9e4c7e7001020cb56a7f3c6d952688e53f70259d39f0dff420c6e59dd1c5fef74b0aa09e309a0b06f000c7ab3bec8a74a1988f113da7ce6989b8db9232856ae80ab3fb442d4fbdba3e5f7e9a43a77c66238a781a3a20fb12a62e51fc81cca0be24e5d7d5673b9d021e10f70727d404fe71753cd8ee096367ee2ef3542c5ec81b126d33baa775630603189eddd6f618d1bec04139539c3c887e9bbfd29cfaaa3c78b3948b4740426636021b5ff02c9ecf59a4186c81edb26e1f5c1431765c9a018e99efbea9178222f7d3032222fb545d826dfcedd5512b83956686a16a18c3846f373c6563880023ea6d2402103f5c7718ecf401255ba0377f1e70620bedbe48c197fd78549bd27b4b059429ef6a9de0c4ef42b4f94096fd7eaef04c12ea1793fb4d52267639db9a5445dccf0761b38ec7a716ec60c4b9f6b8e058b57ff7a610a77c72c7271f982fd387c4f4dc290152a4c3a3b916fde09b00e09cd394bc63a4dd866e6da11c62dc584b5867302cbb17782f057e584b777b72e5a395c6dbd04586996386ea6d3e7f5f4fbeba4ba7193cfe62902525fa0e34be693c002bbc21262985cbae8858a2b856ec2530b23e2776334e16ef20f1341c1a7e48b725d9f7f697dcbe32ef395730ae30dfbfd45fbe5dea81f8e197967ba6211266201736c66ba7b8e5721961395b1d8958e229834b1b57014107c5b85008fb1c80dc75347ea8eb22039e4f850d1fc48ce4f64f0e2d256bfc5b3935476a559f4762da5aa8da48806d1ce20d3e6ae5a6b08290774c9346461b8ef2242a009177b453901f469076b6a2f1ee09edd042a3b614d108e91146a60b2ec58741006a3abe80902b0b88466ceb0481d0e82af21a6ab287f32a15549d1ba7f00c35cffc58f91f12274038e91570ab2a95af341209980f6373d0294456de054c270124af521ff5f1b7217f3a699d1829c54c7c26d17478280d20c2ef789ba520f05660e12b927964d695004aa311403f91a08666839f369288014e05e7b2366afc1df0bb213a933c7aac2c947290ddbbd0e25c60a802507d0488a4009ff319a14ec83c84f68c7666e1d8109a5c0917fdf18b492180b4bbcf217198c0a81a56eccc00467f4adf8a255b1057ab79241a5f55c539fb117455122f070442a958f1505f946b7335d97294ea602fd4600ac164a90e88ae4fbc1f9955da451ae1f58c48de57f1a49f79b67406f3e1a2e0d3e6c7dfab358adbedee5e8b47e3d45bfe0357c30b61eef4e1f62ed3046d27c321012325e3a1b67b78ec5400730f814568ce2b262741dbaacff80bca3a15f6e9081031d3aaa769ed856a05ace0e77ea8814da41c92db85827d5beef55734f4543e62d9f6596d739da89a4312d956c4e746f5ab790ac797618317ffcb60c89f4b55abfdc740ed5d0f374cacbd38934d11594a0c133276e1b759168780fb597b296c0af8a032e93a4967c790e33e3c2928fc1711843a88bef2d420327c651c3df9e6d7164421d6986506cb1dc40451009304d67ee59f3d7d7a91f9c18111bbdf7e5561469b9bcb34d4bff63a83c1a2e5cae5167ed45fff33d106c664eafc1a57d9cfca7176a82f9f3c47f476548f483c944ad1a530e7de88ac864d6bfa4f19caa5335993548c2f031b9058d253ae0d83e617c82dbe9d8873b11871e0d697097e519f80c3952d56695048109aa53f21c9807337136666d64f84e71b2ad51cce508edc8d50b764b1c6411d748d414fdeb375076911c1fd5efe900fa9e3a5819375054a0b823ebba026fba993abbd2fb43096fd312b9c4e3cdc99f32a8a1574d76b4d785bcbfe8511578e49b435491d2f8d5510ead734427d443c09da0040f40084c77202e33a9404f9a585a5b021a082e665275a09e9b6598cf339914733fe171127276283726c663adf896ebeeb0a90097fe813ba1f49ef09309ce3969ee76570bb1c7d63dd0401280781d769e0abb28bd324295a8ed81f0fa630f7d637482e4f975806908cb0099cea6de9332c3b57aee54e4627bc5dec457de02fd3c48d123ed47476fa32dba17ae3d0ac88724a78c896bd40ef1f5865e4adc7b78eb18b29593cc54f0fc926a369d9d5115c2f94df31b33b96f26673059ee971e4e8ed43873fcdbc0dab9106ad3f1ef2892618adb66024c73e8be46ba384536240f5d2b8e1dbd14eec1bd74923b6fee23ca9ceae70c33135c33f2e7ddad1ac07dfe79f06", "656351526363", 2, 1770261637, 1537743641, "2d051b41d267955a253b92f06a67adda341aeec674035ac6f6e88ecd45053260"], + ["030000807082c4030419586f769d0b269781b5eb9c10a7148751d721242448a270569eea5fa9a89826000000000463525265aac29ff93b536a30a17d0eae18e4fb57ae85f0150fa95665e48e282aaed3b19a1d59cab101000000026552ffffffff5b9cf02c35859e1fdc801c7b6d74e58bfe6e1fe8bbcbed5aaedb47607637cbd0010000000853ac5363ac6563524fdf0b4754c5433a77ac14c1b501d8bf03d51bb86e9efc37fef46629813b8e2ef6e0ada802000000026a5318cee32b02ce11b90300000000025351131de300000000000863650063ac51650000000000a549bcea030000000000000000ddb8b900000000003408ce630135b305337f08f4a56a30675e00903b3dfc7c1a7178c2a52ab92d890fe109ef12a5d19d3f0afe7037965d67e8d60a2533b883ddcee9a02e5d1ba7ef789563aba9c9c00af0d85037193d39fae70a2531969db81b5fcb48e0bd77acbf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000862c7a70f5910301468d1e257f134c5e186b283140ccc1ccc1532b1f9a5cf5ea7232e2b151737d59950129d99276cf85b71e7720867c5b4bc1388000fe3a039431541a400656a683fa0b1e03562adf15f16e7f2030970406763321a139a69b1d58d0a33a38cea74799981c1966f4df51e97e4f65565acd55536ad512dbfe702e020446b09ebf0d8bce20ee359d3480a2e84a1a6c7b989c1242a68e26290eecd2390313c56e681ce586f4f738f0113f67460fa1fffc6d44b3db7a6277f1dce1e5fd460a076bde26fd592f86d825bbdd62af94e0e82cf3574035df80de0ac9d7b69a076c070a0d19fced5a3b9c273968c22e0087443a3caf89649b7d3d24c0f1d7df1f8e020147f35b84934c0cf76c2c9f877572c67cd9582f65b52d27e30fc09671e5c635022b953b0b4deed2c5229d8602675c583ef6544229eedad1d7f1e03d045f7b0094020f5a620a8d2c56750ba5810da653559eed5c98719205607dd6c9f0f458a27fc1021d38b742f97a6e4da8e80eb72c0ade64c14013dee3b9837625539918ded6225c020bb7732d42437322f889ed8e1f45cb3f1a75c6c24e5a4483de0aab4199ef61795fef1dc14ab4a29216c0f31502155e16d4008b2dd21d3b5953bb9e14b61fe2f193b958f44f97e18b961a6dbf9f3491ac8bc7d168d5ebec22a4b73c83e2406adb66a099f44554c0ec0c0812b47e39ff974240c293b922183c7230e7fcbb0c8e5414e1f8ec61c88ad3e1df2709c12880196996529cd94819c74b24cc1a7cd479f1f3482a46668ddee7d8ddc8f6867f8dbc3a56431f45e0a6634d46a04a72a5bba04303babe2b0755363846d7de124e91f697feec7e9c172cdea9d44689b19ee0a1be78e2c0ef246135ce04298d93bb20121d66e732bbd1ba759d788033123a90cb43c0d75a6d5e4d9f8b557549b4daceba6e096764fe3ed911e8a79f94b3bdaa7f591b24e7f5d0bbf635583d7e7351ed369bfd400899396ad5156c1e6bf75d38857bce71720195131a9ba490474dce35375327948198d4b4d506c63222e378c844f1fee5438b07dbf010ad219e658c662c4b3e35e490d75b38821dad5dcd50a710a4b228a8f880bb3adc3b0fc57505f0f613f1f83f9147a212b6bdcd334f46e87602a7c1193af9dbff8c7b8b2c435a722cb5df73d798b0ec84206126e22ce39a22964f785ce6d2fef5f5f75661e9a87416e4fdefffc450df1e7b648cf8df1ffc5d1ad985ade105dbc221678bc9887172f6e3c730b2a948e9038da4f9e3bfcdd07a85621604e617c7b1ee796c91a945311ad5f86ea1c4bc1054219c708a97e1687cf1ff15747ec123657a24598c83f8aaaec824e38aef19c6fdfadea171e7cf65c2921e692c361d8f73fee12b8a2d0ba2eb733f64beac27c96f577680ce474e24a8af63ec22a8b0894f983072139ac0aa3fda18a643e6b0177214489552129169401aa2680955970ffc6a4bfeaf39164183994eb648995039f77ea49a3cc700935cc01a7842525526e44f4be91ffbef72ea8db666ec294359005c85ff36720815028f91867f417fb1c5a08876b6e0c3172fa1aece189928d1a6657dd9623cf0eefec452e0118e6c7b6f0ceb550edf4f9b63892ddb57b5ae334ed39d4b6b87c7bbefc0918c3842426db5971002e5fe33f7ba9b654f87c79e9c28f4747046b7a208b23dc12a3bba0d5015695d0b1bf344eed1df724daa129784923ac205063db4c8cdd29e14b64e774cd05bf43cd3774400cd400c6de6e00c414abc8bbb94f6c55307e5d3abea25ffd2cee77249c13a008ecf7bac73ab1486aed5b53ee2fab1895b1f49264a78ac7b1e7cc413813ba47d423f07c09bb4456031159bea09c8307964d7826cfb660071e83dc4f4661a9c8d17b4fb95df4948720ef2078d2fa054842235dbc46c0034ee41f8046aa6df2e2ac9a5314974a918177bcee53b50f95b22a0bdb2306ff3358655451999fc2cb24d70e8ce0aeb95acac6b9cf2f6b1d53216850b78c000c304944926bd7e1c5007dd9001c15973d0ba815026b34590e3984e6549475e08479ff0f67e575a1954cbbb786e09ae83c53ae4dc9c1151e45a620d528f20803b305721aebf540f9b9e8382d800722b0d9be56bce690fa24ab01a3eb82b7213103cdaad72cc9bf87a017e6d51133843729c74ae0883353b7e916f734bec6f6e303ca26dbcc9e43d889b13d2e3eed2a0336bc61b712fa383a4b7a7d28691b712341db536da064680d6b994af1f6014053d7e5752a421f11efbd7a4c43d122418ea46c90b1b65617958b3557589277b14999cce04000000000000000000000000379ecc0631202afacaf87d08e7fe1d1ee691586177e1694049484f109c835cd528819f4727f5094b1933313be3a49224455c46363cb8ea1008f21d22c2e6763ad471175dd19399fb5fb6392cde087c6f5e143ba530f8509c6687ed70632ff9690000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045bfc2594b1251d3ce06b00e85bcbe4ef22526a8e0fc9f6bae6fb193f3084cc5b26092404a9b6b6b20fee39cac24e213f483b167e5b88982eea29863758a5b8a7a8e9c247e044933c9484437cff84f66a28c8e0849a74b788c4660e2f3f8ab163b440e242ae3906446f631b86998f5c72627945ccae4434e95314c23b084f866032a45b8fd2cc11873465e3219f1c139c2abd19658514358fade6dea962f0919c8031255adab5668faebd120340fd2efee7d74084dcb4186f643d046f9e92a8aee1d0a00bcd6ac77b1382885fe5cd60a6223ecdf98e11cccf8a6a64f732abe17f91dbfb82cbd6758e90ea1f03301bf1caa1370fc06c3e751cfa1a214e9a8841741450e0224d1c7ac01f818c571c597fd4e0eb938ff49e78424b8b10f05c9d50a938e66e20310ee7564e26c8acbdc4b4d678ec1633d254e40ece9de5f1a1f8119c38d40327503232dbe5f380198d3906f0b7577836aa2124b09c06cb556cfe13135ffcdf07065032afac314a8ac5b77318d0c4b1ae0bdf2432df231c5712f1fed273930b5e9a4350226a570c99ce10ae0d72cbe9decaaa65e5f843c253c8ebdf9e51515243fdad58539cf983f9e644ceed21d87b9f5d34380b69f2d7db797d7536b00d0986f5048f29ccd76aa2a4820dce993f086bfdb7ff218acaf67e4da5901d035cf7321671851f63a404054b88a5d56b5f27d36916f7b75fba3128474911ba830f41d0881ea7ca06c7a07ea29e0b7eb78927b3fc79cbb701234b1df979da993407577f617c96f049e09470c911c540f313eb645ecb5da4e55a75f69564e3471bfdd3bfb6ee49897b7b699ae787a55f86bac5b4a17c849aff33efd8069f9ca9d039dea8b9b8fb57a8ee40a656bb152aa930623c8c713cc977f71745b4c2cd5c581e5632e4ffb6f69db9cdcaf7de5156fd557f00ec5fdf25e4d265173284943618acba7022dc355aedd48af56ee596257346b26551af1477caaf6d96c5ed3b43ac5a0e0840a21934bf82859a6a420cffac49d7f6028a51815065a53c34f33a810fa148bd2aa8f4936f14be0cb883e3f9cd41e0a32999af0a1c53cff93e1108b491e1590ee6b863cdcb3007b2a3eaeb0a7bbff1d3e8e9eff79abcce1b79556af91ee8d0dca634137c7e9eee11e0ec1d8907eb64c90b92017c52e8d27a35be09f99e8591f07e701fff93e81a5aff4125b557abfd758ad4f0ed2e1f84556320c554179a404feda43e8104b55468222a1d3e80c6afa7b00a9d20481dbd76d702cd4d5106a0b9b1e080b2037f223254fe12a9c413b4147b7d9051c32c46d98e83b51b871f2149417c689ec9c3005fd8da886d52362236aee7f07ca9e00a973538ee5c1e7d071aaf40b97bd98dd25b82e597d2c460d6069728425240507c0f6359a3e240af9153b5d6f362125639f8baacfa48ba16b347aca0be58f44557d6d909d61cddd890499ccdecb35cd52378209bcdc50692569681acf10d95f34bcecfc7cf364f079a94a5c98cec27982f340c9259295721db68325c9e761212b38ad3109d7a09046bc5838ab89498f85b4723ab6b15c11ce2cc42f2aff397a62b106cb20e29d56b7c6bfa78044aeb98302f5aa632c54c144565e70a3aaf965c4a83f1b161c249ad2a0a0b35d6dcd039ec3b7bbdd3c6e20850b88d3a7ccc0d8a0478e2d2aa732e47647403f3ca237fe995a7e24de0e5168d93ba2a60140c93c3b28874aeea2e375bab493fa0f0e8b27a5a29468d7d77c22f4f6c4c660c44424777ec0e24200bfc8c0daaa72283807148749cc60b2f0ef28ce6abe8c2ea5442597dfab66d06c6621bfc2f8dd995d225cacb45e4ce7651e6e90b5ef1e9d9e8cf8c776ca9ecc7cfdacabbd8b50b8d78cb1a1e34f98fbca230a9c4bb6226a9655a060d39a976422ae0c9ce0a7821aa519988088d3d210230915cadd6e468d8f0ea02bc4c42a4ecde09277aa84878e63a01f94758cfa2cdd3b08235adc70421e82658cf5861aaea7e2d7da7796b6a9b244fdbd95b3ec463c831ed3682e6c610e19ad9e8f79190da4671567d27a6717707963ab5fcd7f5dce4cb82f7981ac7bf01348170dd98a71d653df364efb5841bf45a9b72980c4f1bc5ac5abaafca9c4c4b53c3c8ea2e2b39361b5e2001ff81e76ce0da4de9ccecc6f61e8856131845a5ab163d09bfe1f37cd1a733dbfe6a13eec840f9c5a93695f4caceef191f0b4323c24b172768708ad07716f81a15df0c2e6ae6c2ba7862d19fd62b5ceb60bac2d975b6753558e03c8e6a5e437424f325c0975e9b3873c14f56a0ef5e7b002d44056be1b0000000000000000c2b54f01000000006ea31b67e662af4f05d0886ff11bdbd6daddaa56193df904a848097c42149a46c27117b1c4ec701ee555523f457c4b2789e773195c54767ff8f3f8509c8925c5275fced06f49dbc8915be2b022e9a6ff1ebf31162cca220052603c3027cb8d5a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066f53bdd0211fdba2514aa4da4f105233bf2c0761b0bd7e4713c077583f9507e7c8f8d4cb7160b9dfe45bf28bdc00145b0eca46f2b835ec3eef6bee0ffd0247ef4adaacd8b3dee2e24159ba3ee57e6dec27672b6323917368a6380c543c700c13a0eb1cde78d27df6841fd3f9dd1686b75d5a7a5730d633396e1ce750056045e03067be382de2558dbf6d5b27b8a406848599ed85ce56b1215cdd0bc12ec89edc8032e5c29abfbfccbd97777526da65738e22b3292e9e77d7994a32d7a11e82f410c0b012e2a82040409c635941d10754fb475b52ebd92c7ee852fa87d53de3c80c9eb880a5a5d9a3147b154f25997d17bf75d8e73e6ebefec6dbffb4d6b1bf5425994032fa5b3e4da2a85d849c1c47e99edd280c7748246cf816a74f2ed2fa22c84ed01022204dca1774ed025bca78c154328d61dc78ecbcedbffaff3f55c5f3aa91d66c2020774f0734367f4443a86c25867c03235e2096e805572365d0aed7604f2756ad4030518d934a6fe596043f47aebb932f972e36f72dec134a06ca222ccc840568b70021dc7b58913a5a431b5c05f2b3de64e0d0cac546a8b2230219b8d4594063b6c68b08f21569e77707f3bdc9673ed2783620f886dd38b5520ffe2027bbaa6e4e7937ae466a40f55c29d17fe53b47848910a816dd74989b85eff80e2eb588802f8d79a689ef512fc94d8dc7c614d1f87fd3db1a2f6505f2403f7440d0d41a8a3855c9415953fc49341b2b4a7f9c518b3aa812fed4b2af01e4dccb30826ad07dfa58cb9cd889c354d1176fac6af6d10467f0e1a37a897833e17888619c33c3bd772154f8612715479ddd35aceccfe57e1cf7f4fa166ecb57d41db06eb48404534ba4d3575f3259239cf7d15791233a2e84a59a7c0a5e59ea7005cd3e1b1edf7ffb65f44185211cbcc0924b5e406f9e3fb5d4ab302b72af07ae9a270c2e491948390aedbdef640bf51d1452d2dddd51f940a4c6b3c29ad670f6d115702133f87a28fad7fed7bee9c305c9ce86e510b46bf9e40b619c0f287b407d1de3a27936cdb20c7bb2bd6f3ba97ae8b63e818951fd1b5862767f2a794fc7d84555a1edd282f0eba6f9b48e03cf5731a9be657adefea0ba3a114e9010ca574325f3f2100796d16da745d765f68ee878a7d9dbef68b493e89babfc8efb2fafce6741df0803edc881a623a3db0754af615a4aeac1ceba02b55d6d522ae3b5d417c0a92e3d77584d50e474815b0731f7a26887afb38f7531964cb11c960587cda7d6679040f7cb92300c1a29a935be8ab4a7f143f82783d9b1561de7440ea9c0b6f5cdd76a42c9aff6e2f664ffa30ff4313e6673564169484850e0048ff33467770f215f853952c1c917d5876debb0a0092344a929023b806fdae7606fb971c69a1ca41ddcdc66473fefacb4981051629588afc89ec591ceb78683a5b3129b296fb8cecf6882da0ba0a64f0c20b6a93b3d3c293e686c781b38344c4cc53d93ce0306204980a8b2fca97df58313ff182724d0f21b7c8ce1b7574d1e71f7baf81be208fc9eb0eee84d2080cfa55cc5fd25deeaad28a47067314650fbd085a70761f4d26a842df33172d69c8ca5aa547fee70df441ace74aa56b0e748e8b44898075ac0bc843df0bf6508fa77ce96c290509107292b2efe30305a29ffa58258a38d6c214a06aa5dc11e4d7cf3dda48d99444fefb86426159ee33181547f0fb65eb9e4a077fb6a14f69ae0f6bf59a59fda5df402c3fdaa90e3851ab95e573826579d441e351b8ef92a92eefa62830dd412522f76084070daed783da6825634d64c5518656c5a3f9385e24bac199f580a5b4ce3517204fd1c704e6ac9c029e251b964691d45266cf5750822ffdd91a133660abd496a5c783c9ae866ae9bc1962d4a597b6421d4b1f54bdb700a4ab68eec4194e3fe51a73a22000e1d920ea3a05d4dea2a30d1c51974f699cacbb73a9ef7b193d404b1aa9a0119c53078c6d3bac576a2041ab1678975b3c5d8825a82f86f65e7935daebf622e81ed40ba0eab851f8fe4fd38bacf529c498a00cb999f98af98dab8c2bd401449701cc6484b4759b462eaa9800fc464c214f32ce266c31b83fa789e53166ef8834d47a7c7e0f82b0b8bdc0c3ad9b409567c1a359559b29bf0632f99d1995be721de150cd5e66b55aa8749b049c611911ab3470efc0373e88549a84dd250a5b5bb73450c19ee75f8936322bfe803714b2710bb110a84583eee6c248bb2cf2b9df092d49c3b50aa041f709943b25c20b26b1e7b819cc5d28988e5668f0540d001215a2abbc25ec51912e8476006661771b62ec91ce2b7302f2717bda99e1aa704cc51a1c4e94edd2ec0d1a0ff0d3b680ff54f888210186f3116002924fb88f1a212d16c13729011e7ff0f2ff4eaff514f219f94abc0b14c667cb18f3bda0488a35b0d63c466506", "006a525253516a52", 1, -1642059239, 1537743641, "f31fbc4612090c302d47c3d20e0924c4e432e7e3e6bdf873ea35465c7b4a5934"], + ["318d451c032d02f58585bd87fdfe7d34a3a132625e5483e191c011e7df858c1c40165530e3020000000400635152ffffffffd6c7e875f2b53f1553f83b5bf191a2743048141fa96d8b119ebfd44af0f785730100000004ac006a53ffffffff0843762cc311177530ca4964339a8deafae245c82bd5af1aa1a47f964968468a030000000665ac5151515139cfdc9e01176fe50000000000066aac5165ac5188b3f0840128e59304000000000000000000000000b6695e2c64f37b422f7ae4ed18b639f8092aa14c5469a8f010f13c634438d9c6527dcd43614cb8b5eff669bce275b9192f9ec3af1705f3c5ab031de8cb818cafe998780efc605f9d52ed4c23f6a612afdd308c7748ad8456a38bba4eea7d5a940000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027b1964ed9fe7d1864596ac80920207ef566ef8231aa5028db8f9bffe54189f0ff111b7c2be0871a814d97e8f037f15cbbebb39bf3726733913eace33fa2d8747902cd85d2e00dfb601e04d3d5c6b912ac0c7343cd3c11edd4e0450e5893a0ce3ede1eb7c9917ddcde9e0edece6bed000ba17a050ee42801b29b4d746ef834c303302e25f92dc69c4eccedab8715907b2627703e5ef50e03d738538abe1b485e480207f6dc4cd8eb95863a4376b0f17666294bcd45f5801ac31d02cb2203dc9f25550b04539cde62d0be14218356fcb2fbeb387d5d8b0d29959b1c82771f975628967295dbceb35003b202a89839c78508a624f6966587c45e8f4dc2e2ff686dc44eea030459498356e9e228a4daf2afe1d81ae102a11f979f44b8cfd957926742475511021edae7e64139f5d4f7728a705795597316b522fbceb040ed3fba3c06226472e30204bb2c9b82b1429c6638e0aecabc80405810d00eccfb838c8d8664654a5dca82022899565553a7d9658c70ad684e1868b0cfe42fe31961fc7b43ce620d954d19a00215df43cb50266430198fe038037ab3f6cb9cbb6b9cf581d8e0d2af2fbd9b28b9f3a23357b727aa8c772c027be5f39d07c08a0562ca1c4074f54b5d060c3b77d0bda511e4cd2ce5a4df87bb53dea1e325c60296953c4f508ce920833c5f273612cfab727e61504c29fc667e245bf6125fa6a665b8fcd6809a0ddde926970956b19f8e2b08fe74fbe04a25defefd38ac9b6f50460d7e00cfbfbe6687c3447547fc1b33fc3cff070766e428cdbc0e9c61a24b78acbb9c845702dbdc6717c77486210de49c14f302b915509a32bb9a0158402f3fda2a45865969554ea20de82013b09b0cbed364ca0c0eec9a4ff2a6127cd9fa71331c259d4ceff91b92f1b6c5dc9f52860e06dc16873f0815317c6540ba9748c7f277aaa626ca0dc1e01aab18e18e64fc87ac5a6af810413aac73af3511e9bf22614fdfc7c31df445e6cee581e375d0235d7b5c378e183984a37e2a8c4177d77f61c1e2e14a6c0b54d009209f643755964352c5b5936c2083e9be252f8989d93d4741ad5c8d6bb464d5606962abc8a4ac557ca7f4bc9c39249c4056711fffa4e111c5482fc6e5f582e917561269220b99a5264b50c95e0b4638a08b978abfa0cc2bacb701193bf97b70f065aad177f3f50cae2e9ece9d2e0e39cd2fb1d1b08e1ccfc880edf744d63f9876a89d4e2431822d2a7a543b84dd5ee080a09b64a432e7ad9ab5b7ffe9c68698c31f05c20bc95904c2d94b7360e381a37167f820284dd2447ba660f108be8e77e93b2968ddc4ea11f4d9ae148292dbb9d75f232f98755f6b6b9fb94645d0bb37b783f8d08964884ebbdc9f15db5e458bd7ca3633e3ee790c744ba6e7bb8528eec5275e7c58fbc9680c2a105011e5e22b8dc14f2c626a2e4de3b41efb811a56fd30a68691a6d21ced269a2c90c146f7bc76fc3c60bf4886c5908333b4035b114ee9a923d41b71f93d48f8e304f5a605be28d2f093a6aa1467d156bcd148087fac7a07423c318073dbca227d45af0b8322028fe71782c1da074012606fd5f55ae538fb541a8cf157670d7196e2a1dbbc4210512e54f6d238638d25160ffd5c8df3a2051f1dbf065419a91b38802206b1ff5ee621fcc321cb98bd215d76677d26bf301d4dbd6931b7228ecea1651ba2cf19abb9f172b1f111c2ea9728ee518d98e0db6b4367c4dcd320838b10dfd41f6c4e50ee8fecdb8713f494c91a93327c3e0635808c044a821efcd5820ff951c3ff7f3b1ac4182f8feb243df163832d83677d97f2d0d85621c054f491af8cb84824b226b27f3d739f654298f91db20830f46ca46c3af8516efe4d8af070e2b5046dacb878fe392737d88202f2b6ee6fad1cb3a403e86f3772b09d2bff25d4e04149d2a47475192bab657c123741fb07a53ad2fe39e7ed972c629dd2a69bf514405c23fa8a023c40322cebb85254a35b935d0f6e47d079ea220c2dd466a17372137f14a4a2caabe81d7791dd181dd6522070ecb4cf1d9025585584c1bdf0a3b14cb338a779990a1c0c22d5c61543f2205eff575ba25a2c8914f1459311df0d769b6314db916a96b7d9c69516d6a292ccda8ad3445e30de8a5424703a64dc61bf488c5ab4ae7dad24c7020b46d26446bf17f2f9407f80accae9062321937924522ab85187b86a1fc142550a9d866122ac5d8d0e8aa442ebfaec962a38034fe47df96d3944dfa8895442227e818f56c167df5e602a11a5cc86c1fdf13f7e004b820bcf24181332110cd3f5920949d5a49063fda52ff4b1aee63081ce5b6185e7ecab64f98f7566dc6a42e3ccbb7affd7a9a2c259032b60edc3c358cb89abfb9123b7b64749d933b1813e88ea55da23116cce8026e19c54d7f8482d4f73c4a3a262a198ea7010db1c2c3b0b", "6a6a63515100", 1, 923629902, 1537743641, "71eee6f1a293bd9435a86b8a353ac9f09c2a84d7e73ad206a10936255072939e"], + ["030000807082c403029fe906fefa4f73f6dd8f3cf7a1da1a66c7a712424f452d2b9e5c25817defa76202000000080065ac51006aacacfa2138ff9bcdcab67164ef0d14b1bbd12596d3680b80a194c46a7653686e33b2bb1768aa02000000076300006551ac63ffffffff04ef7b3502000000000165154a1e05000000000752535365ac65ac5372d3040000000009535151536a00ac635231a202020000000002636300000000000000000200000000000000002c8b910100000000b43f125633d5508153a3c2c5595ffcffe2f98602673c6919677a16f0ef3a114d347b905fe7dbea6909bc4a9c29904a150a9c059a085cf1d83c638b6eb011adc8327f01f8e275bab6236a82d1af3ab74e4a43beed634e5f160acd056bdec196bd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004efc346aef9c3979ca803ca86a2fbe1f8d1760ab2a4941cdf78306344325f9c7bd318a497e843142b739818157c63e3a8b1703381fef955cab1af2a8f73390f35b26b23a6584606a6491c80098c08c0f530b31178b6dcc651f690045437c71e33d482a63a5a7710434123ac592981633172d9d7def02e46a45f578e00bcaf1c021394f0a2341a31526bf8571a25bb93e80282097a991978d9e5e457aa43b5688b0327d61fbfb46c14c1e03c4623a8b0054b9da00c6031e39e5b2443c9212249b86e0b049348bff216f972e048dd0f26c308a6260381453748acfb45efa612f7af5eb5f901c69469c08ac4760b87aba15aaa222f83002c090b75ab56bce95d2ffd7a99031c138a983b9ec435e40b2593bdfa975c30da036024100e4052941228777bb9a40214216fd7866ab5840cde5d029cf6a29eddf3ade5aebf8fb4558ad06f49a7c39d0300006024df3430ffa5b45b2e0e1e0e965bb7e26dc866b26495c15a0f7617e2a9031bd80d405fde3f4236e0e89282dd40ce2123a87898c7d261dd77236cd48da390032162535857f866c2f884f898f749cefb718f3960cf9cb22946317f477819be1e3e0e4bee0b9ee94fbcd8cb2382cd85f762343353aabc4650835002c77f6edc7ec62fc84c747724d9be6e62517bad65bc348d1ba6a0ef9dc0ba78ead1d082276f1a88255daea9cab0666c756ee1879f93808e7274c72614ff79923b68a15ed88dd63c65d53c0aaf4486992df31c3f49b4c6edbb904c77cc06ed6b2a9873a1ff5b6915ed3dd08c584c465f4196635ce7c45cccecdabab331345e5f0ec4681fd17bdd26bb59bd15db190c1028ca3099fd04d8ce3adf1cd255b329f78ea79429011cd774a6c9391d9f52c54f369bf5c9f1565de8040192a9967b8f71d3d44f37a61b8eceab495cb2ac6537e90caebbb90af9fac7f209ba2b780056eaf1a14ff2322a0eed5728a9b4b39809382492b9898a547fcbfb2fcf40de21fb0b23661d2216ce9ce00aef722785d20dcd6c58a1d9a2be880eabe8af42f90f2d3cb6cb254abc03d45f25a5afe94ffbba722a20ead12fae12ecd6ad414b3b9c76783a47ecb097d8c9e6840d869d9505a75d7ddede67afe6bd089cd33c4fee4ea4e5839d32301f77a3c7cf9f973c6b3fd056ab8f7beaa5437e0255dca2638bcc92b1fc84f30e8de7c4814327f9ef7b4f420e133d376b9ace4370dbbc52cd8b6add99a60200af7429768a9071f36c3ad1c1fbe4c1e1431cb4de68966f908ecfb9d9ae1d3c5ad7ac2cecd6c02349e2fdc85a3bce5cfd45dd59ca572c761322205642adbe5b88ff1c5b078c7f5e5556ad68c0e1a38432e934dd290e2bf42f0f086b31aca929a5e302028f80ea5a57c4ffc3618716f0f0522da99653a66653a455fcf59beec3c5b68e3435219fc25e7633132d7a37114d9fd4eec77389ab1ed265b37c65f36575cd3cef9c3667af3609569b12d0463ff65ce86f692fc32bf42c250b45d1ba1bbd7a9a7ee0c5c6674f7dffe1384c7768ed7c27de43cd6d70a1aff4becd67e0a429914c2128fb7b1d8dfc5b04956fc0898553e71a98b0b96a24e1b89baba9cfe979ec6542982b5fa2f549f1fa2a84dbeb0399e4efc04e6649c65c442ebe60b4690402e4032b19d6cb774e8f8715c0bf28b7e073601766d33832288019df1261ef31bff3fc5d9262cfb92846bc5768f04e82f8e32d2db454c8003ef39187a96c3847711bfe69ed6e9fab016865045413de6ce67687dc379e97754cadb34860519e649be171aa1adf993fbf16019fba5bfb22fff4d25b2052438ef00ae89debaf3a40a7cd576081fd899fa32773fd863f28d5f4e31b8aed349ceab60cf6dbd950511577802320c4b1077d32e6ea7164d89218c9df2e21c3405983dff795a5f797d726f9afc19df599d2a2029c78d75e7cd3a89d59da63dc05245096ab4a568a17a586e3feb67c123f6d0d73dd5123122e113d749852565ed73d99f910758088eeee1ae0943fc5362c64f497f120bb978635a26e426a1462c5ba8b0fe8bf266798a2ae2f7a781de666b84986f056a9c791ed082fa64b047b9f4d2ffb38a60d86cceb558bdcab22681250a448846236a393ff353bb381b1df108ca18f1734a368eab609d4d02d1f6f636fbd802b8e3c83a217efd27c01addd8da68c48b0748609432f4f78ee9b191f0dad0479d170d2a85262ddfa5fb15661b0ae92af4e2c2cf73c87e184141dc1f35134dd047b5a90ebc013c9bc22f3b070b9082de1d7093a027ba148d9a787d17cce08a46832b5df5c8618933bc7da24fb37edac020000000000000000000000000c34eeec8016a89781008a3e90ed594cc4f7f56569698e3a2b88794cae1724b4d720240441e8418d42865df49e3b4f4b06824b93347210062d459697e4bca531d457199df56c11ad93580572522217253eab437bdda0bbb9dfa09d021fcd4def00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e05b25ad910467f6c40e6622e6ba688b10dcde4e4d431ffed7a0632fb5d550422e9917a83b44d45d75ebfaed2fb8622585cab9045dd0c6801dcb6fff703f47111efcdb2cbd94759a09367fafbafcbf7b909315706c3bb3976ac6a6212a67b6308f419d44fae1a636fb278528faceb29695abd579221592edaafa62ca3c1408a00219db418fb85acfd30d72e69b646c419402d6854a036c43a0226d646f58b4c492032773c3809c7bd18b8d74d5c1b5ae1c208ef15869d7593d8f24e13bb39c7527500a056c8cd33c73cfdbdf70e72787f55c6325c043321a683805da6937ca4af48a0058d980eaac96dbc54b9c8ecc7c1b4ecb0b16741e8402d87e7eada738c00f54550311ae14dcf7742d4773f5a4c00411b93323acdea1e3fc3c5d40cf319788195b6c03070c20baae942a2f361071d9fa7619ebc78f9a21dc7f29110ad5ccfc6ea474e70201dda5b627a9a1213510879cf74066f32da5dc14d904dc956faf99c0aa5bfff2022a6276d25d36cfc007944dd286d2c69bb822426b176b57e79064bb700ab7c4f1021d4e082fa6b0398d9bf06bd58b676402e93fafd8f950309284ed8f452d5722850ae281c9958fc72b688ebd713a77578592d83d8783912d5790ec9a22c6b94dc24274375a27c448c16bc3d278d72a692cea3730300a44fa0ac75a12da2f2589593052417ba0745eb25a6561f9ebd37cff432105ea5fec8fe75539db04ed36606a0a2540158764515d5d50964b13a3ecd6b6934c80a28fb164f726b0d3757eeb9f8f5222d7968b7258e50a475bcc191f5c577b03bfd7883561f23e00d7c024d01a8ca2df60cced35b3c4eb9b50cd5d7d99c4dde4e803eaba7b4964edca1071a16de54f92ac80290803ffac81d2733d622675d8f7907f81848cd131274daef896f183db115a6cd0f644b5cee70cbdaad43f0eff9b2d195506af1ef8f5b93e17274ea0d08a209b3a054d354f044e19895f0825ed73ddd16e7083c65e80751e5abc12702b9f3653eb73f1749fe94361ffb3a77d16fbb6130223beeed393d0937660231846d6a6cb9065d09acf8e2e51b7c2fbf26e5eba498a0b6ba8096b4b8e1b4ddd2060001063192a0e72e6cf5755570450b611530dfef668d298d26ab4d14f4076d0e2726739dcf70517f2e88867ce4ea2e6894d3938127135fabc6d31a9f54f1ecdf02bce443ed2632bdfe8fd8f65696594f85c039a5d84d89d334099773ea184731dbc9f3bc9c3247e27cc739300eca0e0e262d9e2ce92b879e38098a6a113b36a906e9c4af71a05ac83c7c6e7a4d9aed9597e069f40ac4d941451c2f2cb8bd38f28c9a0dffc0ed70b34981fe4556a3e4bad20b91695d04b8ae0d9ad10c794f37620ba56585924b49a31ee78c5299782fc77aa27de9076cdc8989773ea9b3c1fa7a40ac741def8fec27c4e09b385976d900b94f10b2e1cfa24c56f421fe9f7603189cb2989c484e3d8b97d5a2201003ddabbf7bb55facd7d820258cbe2d42e925606cb0812672a6f187c155a5fd6e7a7675b618cd5bf59cf2fdaea3edc03e36dc3f820ed8fdb195d9fe3e783b3fe719231cce5c19c8793090f7d43f26e7cf540d7f2a16659a2c9819d27e42daf59ea751ef0ede3fb4b4febeffa56e90f5d8a1b6810d81ecc9a41e34f8f1ad17479c2ef22b42b419b2a81fa7e5d8e8c5ac0f9d22dbc891ea53c03849f56b8e724361fd96cea8bb37b6d8de6a193d5640382ae8b35a4ca61a6b459440a1de34f831984e8a2e2bb69755792c934f7dc2d3bff8629d9b0ac0a5e983cda7066d8f03d48e510c4068728eb25c25a6ba58ad794078c9f1e541dbef9e9cf0372608a9cfeedcdee4240d13e290f0339b7cf73800e227ea2d61899d5b2260fdf3675347377eee9828aeafd4fcbf84b8dbf887bb0f4396b5e84e5f3305bb29e650f5c35538692edaba2133ddcb276144401888715ef0d02f19b0de2f494c1c10d361c618ae04dc5f73dbf3191a3c5dea6b2877c7b8ac6048889304ed24bb574d9f26ce5f71f9f5a834d6eaf87775abbcb235dd491ccdfa1648b899aefa091416b0ff2a37731d5fbb67f839756b090ac452b3ec33061ef62a052559c1952663ca0e62fa6f953351aaa75a6708d5249ad445e821df992e2521092d06c2d5c584ae8a09c80c25d43768cb8610bda6fc385e1409ceaaaa46ee41618af50f1e33128393b67c6fcbd0cbb5894d93ad70ee539c9f1071bedeb5fd42b86df2b49267a16641a5204b8cde63324494be571728812c02c132e0e2de63dd4f28663c9d1d6c72a20ec236aadc3e09a8ef3f8798862d4b31aa46dc39faf1034d3ea1b6f41f0eb8f83ffe2388369b5e7851483c7d1128c56bc11f690bca433ff0a80bf48aabbaee55c18207442bf8e230c1452feb4b65813f485e2538710f7511950ec2555b400eef5c6434763e82cb2d20c", "536a65", 0, -712243267, 1537743641, "1935fdcfba6dc80c8c6c58f31081e27be38e26f29adfb7435e163d3ebada414d"], + ["c73534020173cb4c8787255cf5633c40d15cd24ae9d8e05e62b4d949adb766e5736a1b2be801000000046551ac53ffffffff04918153020000000004006aacac644110020000000007525353655252527ff63f05000000000952526a63535151516a0e8c070400000000036aac000000000001000000000000000089f8e1020000000081932c3d591c2e08a1ce45865d8948864974bf7d7252dfa6ae85e8391cb0f8a9e93efc169f3a5b7314083ca72df4773f80e762aa588398d343da755ea0369c27eb5374e50f934e5078838f2c782304e0230b47b67de3b70daadf88fadf8c25ef00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000541b544824530891b4c6f05b8a3a1501b468ce0f2b75eb089459e073a60080d8e7fbe651f12940e36c48304904139577910a81c5ce2fbb9cc63519c82f2438ba6f55d5763f0bcf47fa37fde4c4e6d8369e9a410e7abc882143a7b7acf8d1fc88e4eb523842698c747df27bd371ca6cc43104cde932f8942842c5ef5f270e5fea03154d6016136fa2fdd6e32c33a92b9417a9fe813011b9b35ddc49607197682ac5032f3a8746f51f478455da8fa8f6574a3458c492753951606d19f73c537fd773430a072adabb0429369ee533de8cbba5c38fabcd719c5fe422526dccc023b2c0b7af70c892226bcdb3e8015547e5d301872be8871c0e24eef6d997634874a8b91d34030d9ea3c35f45d509b70a9b9e4d0bd83ef32d888fcd7c8bc9091a6e1c15cf5e27020bd4ec6f51189a11bbcc211a131b99b1d50fcb4fbb3bb72fb7bd5a22d2ca454f030586804f555d8d6373e64fb2c5754cf2e8eafb162e24931759d87f4ed9a9b7b4021a417a4f867193078ffc9f56341824bda48ecac2f90651f41619c1336855407e03235cf4533d49933c80c0633d7d692e67b49f6db58015f1414f18c05f262e96a5db665162ed0c2eff400f7d72f531269f786ad55594e55415b7c8461f5346103abaee30f04871274c4ec05e4344f80c2556b16834d1a90884116c9048119bd067f8d1e1ed7ea48826ee6b77291fa023c4253ae338bb1119a1a8b5057080beebd31f53f8b63a64186d5b8740fd6eaf8e8bb5ddf2d4ce0d9b4b0d3513cc42c42116e93487898815c2b2b227f654a1e7b1ea0483691481f75540fa074a3cac82254d10e576f1370193f3e4a444d549c4b687b3bd2f6ac625ea5af1e2bfeb60823351787a71d5f93744c552d094c5caecff69204376ceada599921ee8e84bfd79cc94f5a166e31d4b2017e96f8c9443765cef86e665da2ec50036a00d774201bc4a1c94a188f438c5f885e3cbed28b343a17e8bdbb16b116dfe6b06749d55f7b38b251fc64423f3f3d2dcbae4f194fd9b631152fa77f7b7b9050bc29fd621c0ff13d68c5ae3e056fd1e2baf2a9c4c6156c2747f5ac2924bb9cf3c21ec6bae17934231dbcbb3ee268fd79e56a74c8e030c451bef32354d97c3d4525d10423c7e2a84ac2a386189e7b2b5a8a7bb7259d429af52119358b63310d00579de60e0fd397c819399c7673f3a033d46968e1ec78638ae36e9d9eae5d2506ec9905c47522d19121b619bc4ca605862ce07bcb88d59ee2ef451ff59a361fc98444b10f65cd8875f3aaea6c5f0a9e211ebc77169b0310a5928ec93e94250a7827173870d69b48c0204a48b2f0e0c818e5a742e134c08908001bd2b72e3f73578a2f0bff1920ba8f9038847da9072753dcaf3b1997408989e494a0f9b0c3847ba634f5b3bd33f9072a37a781e16e7b7fa31b6ed09e08d7c4440304f01963c08a5984f57c10fc5e2a52aa58ccce85353a9b99965484f21fc9a8c35e66c3d0cc454ab0bac346766e3a18266956fe853a885dab341ecaadddc5febb8a2127c0f56d6048e757fb348384cfc3a07a22ab03be5e02f3384902102375b72d64cb2e865f221a459bd20fdde303dfeeb386dc0039da0894f56cacfb26094ba8f319ff057498cf4a3e5b5dd204d5a453f3522aaef90d053d3eb7497fda9090303d37fce7f704e06ee85ead6e8cbb2f9c684974b56b6e376a148b5afdf3fe18f37d99979cacf75ee6f753338d92983360bfe112d3422f15523a2be4df5fc8b61bd0e21ebff094130023078945deb0f3015a1e01f1360ef2bfeb1068cb987b9841b6b83aa6173578e22fbd46aa6788d1c92e5118da8c6b92610fb244de2d9099992a2e01de0c37e461d601e1bc9834715dc1c4470407b872e7d5b1d8edb8a486fed2d9db6c62f9e44a257ee71b1017c14505b8fc75698b47e164f60a6e97080e03ad83d10e4a256539c3e9c0ee2f70f098cda65c5003db0c61c1715f0f52e5fe7b9cf81f9be383e73e410f2a8879062125e84bd958f00d029191b2be99df99b0990a9ff8bc1718790d1c079f56ef6b45227849346c5f208c6c2e963c63ace11d6b22298a31e87e9562c9ef00b072bd2c99b8a59820b3df81d397f2efb0179227ff8144d157a44f6bc4f55643ad8dd92d01c98370df06a0b7131fb902f793c438cbe63dd1f9d42492d5c3ac28a4a0b879bfa40ee649d0cf4144a270c94b254dc01ab6ba4d0a8c7e48d5992ea42eea7baee8bb46bd7a0c35c4a0daefd27c964feda50b56ac9f9b45fee7b0caa7c5112f4dd84c5b2877ae346d647d7c29b85cfe01a10885c624fe19046747af6be65c70e7fa451b1192494d3106c62a36ca0c9e0794a607d7e66097d34bbb319c602b848267e7d5a67836e2566360f3af4be65fa9e0169d8af37c77fb06c4110b2e05100d27892d64646ba0d63a2cf362b21025207", "", 0, -1688897625, 0, "4aab73ad22fd39cb77ccd6629f32ec29bbf211877a43b607798ecc8f40dafdd2"], + ["030000807082c40304f33fc0efdb9d008851d0443456d286a6cbb0f679d6bac02853f0c407ce13fb1c02000000096a5200655352005252089149568a972b1d2a40c938a2402e42fa7f59713becd55d623781adb46ecd057b0f6c250100000009520063ac52ac516a521aa8074534c48adc8e83d9dd4782ede9efe4ff3692bfed02a57b02ffa2aa2ee4d127ca3e00000000025163d4684ba8daceba1813e62f8dccaa68f39c23a2a462941aa135bbd468f1a2f8ed0c6c384301000000096a6500516a63536551ffffffff01c639010200000000056aac6a00000000000081c44acd0300000000000000001a97cb040000000024d4a35c7cba7ea431ce32c4e6627511fb9209a8d7d42dd172f024d4d0d8f94613da5ca7f5fbe4d53383fe457750571fd91a0b7233ba9768a4751c6ce31302c5515084a5906b7b4a9e93fa4bee97f201e95dd085d666134842bdd92eb5560d6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006c4d162047d3224d8a0f00ccf9400171d789a5a7826013ee503309f2ecbde818c55676a0e0d5b7d11a97f31d29fa7a1619c44729cfa4a44d102a9f511f48372adc99ab9a3d62bd57a66784bbf114eb66ab0be43c4f97b4516104efbf6473e0a4b0b6dde2feae3685f5b1a89cdd3cec54a38241a616d49ba8c85ea78b13af3e6a031baafa53cb749272e0ce597ac46a56fdbddbcc507d90d91e631f54ed81ff47a60213fb3e8197e74687c140bbbc1e4ce284b72b110b35c4932f414f3ab591698f650a07533c3b144e100a90419079895a59b2280c874daaf09b49cd9a1b464548fc80bfc482b39f84e782ef3829c5aeaa3ad1a9bba4562f9588c1b628c36e8130ae4f020a36a75ca6906020869bc8fbcea803c7ebe2d4b7b46f745486edb9f7b30edcdf032aa18aa37f338be6cebe41b9b7a863c300a41f210f53a76ccbc00e1183188203021e0097fb7519107dd85af9077f3788039389c6cc7b4aec6538c3a2204ab31889033006facb32447fecb680f48a4c354aab87e3fec121d294370e3c2d8c4450359002083598c65e7e4ced6eb3cc0edbc9eda6e2f78e19c49e26257f80a695fba035e0c83a409a17b3047fc83f13c94be0c77c5c05f69503224e31a83ea765024a95bc555326601ff620434b4d2aee2323cba8d8628287065f903424ca9c1327675b3c9893f01301c6d19eb5225da4efaae3399e5350d757c241dd7149daaae35bb1da6f06f956d38dc2083680c47cde541c9e625a3684c8af2b32e5134ba8552626ec8f5c51eacf117989cffa45839cedea17078e2e0af2673c3e00e6e1a15d8bb9d5d4587a4c7a87a5c833ff7f20061dd33bbe5861bb67e28a4b71d5b13a5ba58b9129b1b9659dbb27f5fd52a14a359ec5ae38ac2540cad91d854919689f51cce3a5ff3090b220e6e560132230bdbf51a98c33951a3fb35b20ce3704fae848327a15521a2abde0defc03521a351cfcce8886a6d5198e3907f6283032584280ef14be27f1616d79a694b3408cbd43f1a590792ee7d8813f9cbbe2ec2bfc1df1e0dba7be9f491f712e2b3c229bd4090c90726e2eb0d52bef736237bc07101893c2457055c8c93c1e35e3b6f27f52b1722775dd6729f2cbb4f84f9bc8b34ade0663cac0e2d3b1503cff9432c5625b84bce917549baa2f41dc7815bd5a2c77f056441293aafd03dc4e0247a543f6278fd5494457569eb3b0cbf692cf03e8e224ddf1c2810f4cd2709d99b112467827522a3c158923ab9193e25e43171cdc45963c7cb1c38de4bc3502c158d1d0eb0e5f2f27112901cb814a642757950af311d3f08eee27b0a6e8b6bd42c6dfcdcdcb15dfa3a914f998e0374b934034227a1f8351a024a26271950e9e97570e48a2c3a5fefd6933d87bc707df965be3d52a4730567c0062270ad5ccb5e1b6732e3f9d885011137fbfdd08b48fc851c66acca797f4ce7ba731fa5c01e1a41c011d6c1038eaf7b22f4111201a5c8e07cd535a29e4c18f45e65ee1f62e6a5977cbf159392950b0355b5c5ea8cd7901a7a0255593540e32a20e4209e7f74eab6c62d2634eb32bbc70aceb731827fe2734111c9e3942df7b5a34aafa30fff7294610f76fbfa75c386feec7e648900f01122e6188ea456e6d1531f64604e698ca8ca02fbd8c5687d5c1345c63cbc7787a28ff96ca07d36d5050494f14fbef535830fca223af40c8ab2b81dab21c9b6d97ee7701204e92d66d3ab633ace557cb8ce569a373d24ec7ea9cea65bf809c8527bc44b37492bf8e95e232354fb83d6ae7813bcf442c6e461fa7ef64c176e16d9cadb1fd87422da8765879d572a4b776e90f438c5a6061f26e34df605cc7419aebaed6c15fbf0ab91dfc3bdecdb145783bb69ed9a878daa0de3d4dd410521a3948b47a2d0a5a593624698b86e832a9e16283852c0f201785833370edc5ddc22336f831d4d671a67aa4cd03ba89be11bca7139822af4a7a399d14341f8701d3dfa61766e1fbd577d2e39a1833b7b3b7d4793eb1a5397a08a43bf631acee1ea6e623077dafe122df69018174b11721039ab1747bf32f0d47e0d05ac0ad35565caf548ef22c4c2def3945d960b9edd63ceb6239c6e35bf50de88277e264f3f8bf1b46971fe79c95450bd951a5c18a15a9d5017bb665bb8d6ad230e58d27136b757e68b98b3bf1ca96b4b683c5c62ff22fb66bdaae5735e03580b76733834ddfc230b8468703e6bccee7659760975968f43a7f1b12dc59ac87790e3a1ab25616ec3a31b3a64a6a4a8b8f99778d3dcf7387f2508a908f8c8bf60122670399af0000000000000000402e7d0100000000dc859508e1ffea21fff911bc203faca4437dc8277acc898829b61bee82bc88beb8e0ff2eb165770767fd22ce258388e69c36a37510313a75d165cacd17cc5d7fad1a96497edf76c70ef9c1c5772883c2e13ec2a04d8bb0976021e5ec20e802e4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009aef70de460ea87553bb5392ec3e185606edaf5eb99af50f95c6e5e211d923f22136498b7b46cb678e7b0b34ae3662cddcf14e065afe07494035c79503e3b77dc20407831be261fc2bf13054495c5c2a85bb84522e7ce32585eb98c4dfbd10163ff57fa290efb61c9411d159d74d29bd8ac745aba8b87c44ef72e19ac4abe8720229ea2a1b47d4b1ff80c69b0f473f3cab134b6bf0c03b8df5f09d3cc2bda670310313e2164d096dba342597b95a7301aab07756ffc5f2da8502cdf947180505dbfa0a09225dcab918272f9413ebd17a84da8b430b62b2e7e6489df870a299c58e219edc39f8980f61281492db4a5c72628efda2d6cfdbb03c8cdb93f780e4373a8c8e0209801ac3a0174b16d7756e273348eb51f79a4f812c370ff4bb81c15c612228b6020426cb95bcdb45cfb0e19c2455e3fd3cddd8306de5001e3254ac546af192600a031704f4b70cb573dd210fd70da1e1d271abb34577533b96894c74c770c15649be032d527c0632618b73a1efd1240be3cea8cc425d7a63944be9df51884b32526ad40227b4c06aeef908b5f764cad49e74e6757c6ae9241aa655c5c4f67cd90586d20c3f8bc6771a4a0e7f330f2c8055fcfee441af26f21dc11f1e05006632cea0d1dd31c3b08217c166430cec80a2958270c7ea5a73e00de35ac8f0ef7ab6e9cfda264bb601ad61f65a4eef6a48e43659a381b4c8148239e1c5b58a4473bce3ac8e06a80b6835cbc8fecfaf3241e4f3c81f204bf2684ed8a06fb7ac5d93150b275be3639b3e5a530af5c08d50374179cde0c8d421eb86345168f62b948116837ab6d69766216edfe992bae48e4c198be14510e152d7c96f4bb927a9d9edf7401afc5eb6c2d1a2dcc484cfcd0128eeb576e5ce1c93eb4421e843ecee3f5880e901f60174e4a2d215949d6d6b03f3a392e9e35f53ea8092f8c914fc359c3749d9a318fb16186696a14d1d409bab9233e0544d3e2f90adc798b888be45cc3b3c5b4f05a978577ba9c89bf464481fdfa3614d643a8d5f27a26c7c6abf35242f225ae5b5d7a14377ff1adbdb18377e943c24d6e56f71c3eef2a0521c2f20ef35b039e7943b786c1106d81aacc3dbd3a3f70dc2f9bf11e0badbe92b09834c66e93fc47ca84dda642d144665762f7afa5028a89b8b7f9f5a6a7ceeb4fa0ccf221153751263765f5f945e3b691db3d09b5550f7d1264518c3a44d62947abd16e2554f4b840415e0cbe6ffd8fb052d021d839f3859ff0c71217f552695ce151f6bdce28a3359f5de64a2b8fbca1df540ed2b24d4f4bb46efaca749bff3daf1cea0f069745fd9bf24e8dd55956985f11e91ec32b51fa6e58f0d60bc2b1c7c0c51529c2f532d0555e7688520494d3dcdb394931302798eaddc9b412fbbc1c50d39295a7c421232478e0ff8fec1b66813bee5c0b873075d1f98eb9e99609953e445ce2a6a48e4e7e57bfcaaf2e2c2decde8f8c637264e4a8c7feb3df87c2211ffcbcb044e2d01af5fbb02cac4e1eb6dfa31300786d8bc18a91c91877a6cbd225716723f0592d3b54b5e5c4d6c3e398928774364744e490e12ec6dd24fef9ff48325e0e338b00903e7f24f5f0051eec3f257a563a0638bd6bf5d352674d49008fd148cb57d5b9d7256a87236792ae153246c2a3a80897a8a100d57440e2b1fee643eefc4255ec242a080f688775e819cab1a2aa90314e943260c48743e066a89e8fcce3ca3d53293b25cb07cc70981249f5671c644594633391fa7b6ae862e1d84be39f286ead9b17fcdac1880c3c6bd8363772ee1d666b12205e69a5b506182dff09a125f1de2bd944e3f07f669cbd7d046a34c58a4232c6793ab0cf9bc74c37616a96d71467fa378803c3f1948ded325f2a435dc42d05266e8b271287e2d34837743bbec5849c8b7e9f6ae585f2ff0a2ad1db267eeb2e54d3ec2372ca63ddaa863b6f1113b9c25330298bd17ff334baa32d9d645767de755648fcb6b2ffe8cf9e2568da1cdd97db48990b691ad84314dd4884705b3f2188021e82bc69b9fc66fee15df40d0e8ded58bdd1490bcd48c537e39f0da738e9fa9e38db146fa4878d8847a0681104c0d76504f82c5b7d3bb2d4caa833947c0ccdbc9dc81bedf63fb941724e405c94a7af29f96885687cd8898efcd489f967e84578e451aa30739b7b7467285c554e6946db266a1b2ef343134fd2ec88dc8566d276778bdb788fc0ea405d8a4def86591c3f130944e5a6384da5b21bdf742fc0956481cb14930bbd587a2d099a549149d380491c18910909835e793aafb85760a49c3a90000000000000000cb50430300000000cdc7684fe800e86b09fb49f62b5fdce57cd4911f68dfedbefee830e0a77a2934469f6ecc4a3a88ef9dee3a7ddd45b9300e79d4184b8e6e4103e4867ca118fe10002fdd7d50cae6caf86bb9b2945a4d4c4a91a4ff2e50218c60f8848089a1d90800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc9b540bc241ad2659dc334d30911326fcff8c84343adee4f9b8255ad6cf6fbd5e5850296f7e446bd2615b21d08e91ce2498c8a9f7599ddf21d4cbf13000593bb902a55ba110749a96f64d5f1644d2a6e9c248da8f55f8c18c85ea9d64c7eec35bf38702ba6a8f3e5154d54ec47998dc48738fc66c27e5f26907cfa4bd0a1fb703062537af42ec143e9421bc7b43aa736cdd791aacdd52d9d630ce90154fb1d4810211050b1e0f345d0ecddc1dc7b03be6f8e7087415ccc9ec56c9f335216cc22da90b073edd443ef352ff75a9fb24c4e6bb781c7fe2dddb79a05c819fb648f8b3214990f2672c512dd49a1e69602e8e5cbe52b9c9dfdb3efb4ef808bc5ed5c9131e49031c92aa4829212b7a15a6e9e6bf56251dec8afd0669f8dab0a0abb64de141d908031f758afdacf5398ba07dea9715e6ac39b0ed5558be44398ca8577d492fd8623203151a06e98fbbafcc05111630dc553b8a0001c6a7e1ab6e3fe9d402f61403023d0315dcdaec637c2dcab43c43d7c04f12f1b147545da3a911fcf0750eee0eb6041f021fa8fc933de4dcc53021b38777112f8daa86581b1cc519b5842c37ad69b6438251eddb39f1d2b9597202524f5a36b3505a9708c8c258f973fd2e4436234f551b5e9ccfa008308892333525fe58045f7c577c01d70d184eea484fc451b1fc95bcffc614dbce0050f051176acce116e162dd69b5952538694c1ec28a32b4a463b5dd54b5eeafc149e38507712625c0746d66546012164c5e6f2fbb98edec8cacd68b7b21b5d54a80ac0755c9a06d8fad4066632c007a0dd5f708cbc00092b0b105d0f70546b87b8a703084b1a0b73ad6f8538a02c85525f09ee3dc6741ca562a74f4fac6060fa83ec791daf0e0fc2b5a2c64b79e637a71650712e0dbfcb55a35f67680d5ad45fe89911a17d650cea61cfe373036b41165629a053b22a70860d71d10dc3c6eebce827918c50c7a9eaec255c0efef071b5952db22272b94200ca781622d16598e74982d4898e6936df4183474524c0c56110169b0581be42efd4e4b0e7ba971a31223d526a76290259587770890561f7fe207287b6ba2c05b29c22ef49bf4198c66c0320270bfbab718983bb0b8af5b3c593fec228caef42c1358f0c44e22eb683991d0c7cc7e463ef45859c0538f616df1f50314a543f825e41044f7015b381ca0aab0972228393b9fc9ad387717c90b42152b25cd4ac9d1e6bf4ea5be1bff124dba17566a5b7ef0437efbba19a977f625bf515bf9d378b5fa2df4614c2a3629b3815b93a1f91aa4d3bd8fba9ddccb1e2391169269e9ac7c49470d58591a4d13b932c0925139c409b71b2fee3e6d93f2517eee4055e8ed608a2158d6cd9696ebfaf505e41250cda1b919a07b2e694b725a873615e208d4a9d79c922992c5143b3d8290c3f72dfa425ea10b60abd2d3432d006fb0fe20397ed259c700c81e0424cb0afe2b1e951f5923e422708d341c75e9dbbd469593ea3cba5e16f613918c127aaaec2f857284c776586710f1d1ac7de2278405558472aadd1cc5dabdc5bd658f3668caf995390dab0b25fe0659e6647669b03e397a80444c7f491248842d5e6ec0698dc3cdac9d2a4e393858d31946f1ad69cc493f9a61d291589c0250e090481678e1808204f40e146b03f2820f0d40df89f5e4bd7211a3202267b54557fee7db7d2b1b245d339feca9a28232ea9ed01dbe0581c87a6a88563c62d38c717bcfb152a83796db91b6c7aa633fceda0a222279cffb479c940aa5e8d515aa680fc698a50f04abeb93474376a6eb7cc9121593bba1ec8ea28ef5a988838e340afc76940297c84bddc337f79b401fddf6912704a0745590486856e2f1a22d35da28b969ae54314ee6e7fa07df67cb12f800e1bf8cc8e6c96e9893749a97c9480945c79db93ed08d1aea3afebf1643c1705cf70f9ee8684a625817c0cac0d7a27e901b72eec5e959cc38cd02d084f06b745c57c53f38446a6ab9222137ba219f680d6016906c96a506bf607c9ca5d4ba927d0c3f8a8c78bdb3e31959d51442aa960b52e208c0a4187bce238ee35623a77f917f4b7f908ccd518655661fa613e8033e91e9f5ae46636d41fe3dbe0d324ab6126b9d1c4c25cfb368d63572461570b90db7b0844e7eb7c1902217f5300df2a2fc1f2991fb8292f097426827821eb935b88f6f9f1dd7d636c566f15abd6050600a60bef8b1642cbbdf6387c8f7e768b10ab8dbc410c287d48e65e286583703c0cb2719347184c8999fe10b95e937f146894eb9c3c921c236ca9b6aa8ac4ccee213d5000f522918e8bccddcb4f32aaf52f90305ebe8170280e87b0af4316d94a4de0f94063500a4b748793d351efa8fd92b271c1a9d6e752a20c260705c9fdb08aaa700a7b21da3e90b533b42cc361ba6ff5ce9e090e04e8ccbd71f287a01", "ac5352ac", 2, -1598984668, 1537743641, "7aebf228f8607965b61f661229ec01a68377cbc7ea698998e8489f376f3b191f"], + ["030000807082c40303cea79b40ba32cdd967437a5398c4f4a47f0e7cf74a525c1f1ee2e27e209fa5900300000007ac5352006a6a6affffffff12c64c53214867aa693404e801cf14b9e72c0296969fa8ea56760a28f5b7f75c0200000007005300ac0052acb2bb78421bb4b2ea23e3ad695c7d270d940a5cae4d9243c679fcabf0d65bb28f56ef292d030000000600006aac0000ffffffff015d3ff70400000000066a5200ac6552000000000000000000", "00acac53536a53", 1, -1148597380, 1537743641, "bddde3d0e6cfbb2fac078a9a12082387d7a45c77e1d5a529b233aebe1f8f9265"], + ["030000807082c40302cdebd9ed8036faf39656adfc772b82b425fb1d16235c1c2b9c011791706b92fd030000000300ac51b2732acc2681ff382bb74a085f2b675ac78fa2542375002605a55160a5fe87829d329d7b01000000036a00acffffffff01197c5d020000000008ac51005252656a00000000001da38d6e00", "51", 0, 2022922069, 1537743641, "7f7b8550d600bcd23ece3776e840a5717c2c65aa818c0b684cd7bd8a2aeab080"], + ["030000807082c4030173ac7c1b3d0bc7e21cbff9b8c503416ede9342273e90782c1d23411639372d480300000001538206abe601a28caa030000000009516a6a65526a516353e671de4e00000000010000000000000000161ba70400000000d2304dd426d45ab894119d19c405dc1bcb60f093cdf8fffb0686e7d96422feda8952b99c3c80f54b1b73542c913dfce1df6dbf3cae2c5367eb68ad8fb07f4d93408e9d5c1b1950b6b0b15c51457dc49593ec75b96462ee6ba30dc63dbd5dda40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006d1f7e39e799533e56d2cb5edf1e3903cb061d2675d5b8a574c372d9d5fcb1b4d2e7d604fb20fd8bdecf5c6b8de281f965d4a902ee336c646282efadf533e526c64e5e8e831264631af4c08db1402eafbd7f3d905cd95aa6161ea8db5f0e13b8502b53ae1461aa9d6c97bd0215bd765ef87dfcddd6cd6b403632e35ebaa2a2d4020158be32a2ee08d826431f992a8e027bb963664c99dec2de10bb7f9c8261823e030e14471f4e95dee47a0a6c4705a2cfc047f8124ef939292cc047d5e5ad22231a0a0921a2e568f6aafc6c4f568c15696998959cc30c798ee420418e6659e8b286123633a74d6230a7ef954ee922d714df51ff18c5f4cb946479feaafb706cdf722c022c0be9a5d791bc6a0ee06e5f5aca29c009a083226f603e455f399f520742287c0220536fb09b1ef0217536daf974a1c04abb206db7645e4ef3a602bfcd282621e3031b678339eb92602945903186c5bcd9cb01d7007e4b8c5407c413522caf2b7148030635906c28c4633b186b6602660bef74a1fbb30a167101deed9056e19199f87b030c83f235b53d3bbd5560e206cf3d13cc079e700ea0e8e4e8b2a2dc1a73bb4c2c48fd17e52fcc821ae8716b953646bbcf595636ad58a83e1d6ab4f85e9ccdd17fecf837e03715d2b5e66ee9983e574bdfb34ee4ff37b2471526a7fdcbe9963bceee480291788af98275cea780c21ab9c9cc74daaae0619b30c3db1b68e41a0182874945da202e596526c0a9dde4ece116b55f8402a52bc987413ed2f4aa500d2c1c3936726e6a3831260620c48c5b5fe26a9d2318ca16865b2c90b577d1e8e127a618c7c1d1034595a392b66c995aace15a47b25712c8623c9cc6d5419a742bcc5cbdc8013f0f448d29eeddecf9647242340e1f566c08bd49b3ebad56867dce8393e70c06eaa7ba1897037ee09c39dc3e0e097acbf63e653d4a383233e3c6a882789ae4813372af0226d7847f8cc98c905a3ff89447348bfc3674b6d9e64591556eca9725d6f261fe7e575fc41aa52c2aec77d6ddb31845ca974efed48aad181c8545a3f558c5d758b164539850fdd7d00716a05ad8a6f3eeff1197cc95373d4fd7090e98d9646d30ee9d192137a0532232103d6e9bc199b39bd7b366b1f94a7edefaf77b753c5d85a7426b0b0de131758d526f71cdca9aaca182b1c8c9d4ffbf2dfbf170c36c97017de8a78b596bb31e6709c9f0fc6a2cde6f91cb0eabc59be69d2ee6f01f9d56bb455e5ffb0989400873fc12804f35413110301959dcc3ff73f90ff713c9dcdbbf117805c4e59dda7b095c6b251452ce6d7ed0c4b8b8b8196c5060a0eda7a47e45d88bb9466005e0b4259509a9d51c38d78d347f8b43c02f6f108ac884077c02f97e0b609fd8634f224c9c168e559ab7e370e50385bf393337372673b10bb466da889c3b607501afc77ac0cdc96376f5ec9c229d88ae35baf598d5df6395e6e02d36f593251dabce039ece3f3bfa2c4d1c0d92cd5a29cb7f37de1237912068b3f16512cc1f6f1a56f78043f1507b47d9b66a278d08caa09436c2fbfef75c413a9c9872895788b79da6826754066243d6f515b225d2e9f0706f1f3e560d0e2d1d1735e761e513ee7a6de0561e87f989ae9732c5b70121fc5d13f26e6b18d25502f33b4116678a9b28a07bff1ff55e1192d5b4950fcb828666e33a0008806baebf439a36e7205eaba9c06071e89a8410422f7516ffb92cde1e223261bbeb8a685b4799fd58669ddfc42b918716284f72f0dd1dba8531367575b81e994b84a68c1111de4c9bb0e11c639a3aaeeed62b83360d59f617a3e248e4891ed0bc2c462d9c0bfa96c30364e1638a1a1b2b9f7c046309eff942373738fe1c10dae8a41a8da35924d20084a5eba9b28627b1976a649582317d4bdab1df7313e64172e3ed70036b95c013518d51443676e41a5ce7a5f49a95163f08c7bba7b74b3d62b68fabd1198c0ec7440d5d8a6867f725514de5f15f046d2b2f23d41d74957c8d0499d67bf00f709fea5781830688bdda84e37d45ac2c42b37d6d6f441827fc2fb4eb058e22366269bd58c009a4b7a2b7625c4c5de83d946fdaa64048640e6f78c7af2f4d71f5b0a3d440cf1289415609ef2d1bda0a124da76d001f5e33d2e9035280cf9347f9e6ad264919d3af6a0c6780a0e33880f2cfc975f2d3f33bc26539af83293f1e2b19d5a98cf1d4b5944aec2986c3dbb25d829e623fd269e9a298ab27afde94d4d0744e4aab674abca42df9dba74e4c5a99be8b27da75ec08b93f39edf4df4263cd4b1a59d4a4b8846b8f248a7df4511ff644c6aa72f6e5c284bb3a9a605859a997d6936c5022d52d321d5ac45e8f7146a93d7296d40da666e94d6465ded0202a1c74d0187cbeb1b24cf912de508994ff6e82d91904f054e7d010bf4984c88ea0362d5c4dc21d9218b308", "006a63ac6363636a", 0, 1231309953, 0, "fad09a2944ffac6caf2660f635987ac70dcfc443f5e22f1b3ecfeaefd6c3115e"], + ["030000807082c40302ff0533e0bcef4dc0dbc4b74defdaa8e6163e2be1e06be0dd151a694b46a92e4a030000000165ffffffff256be1950c1618d7cc2c64849efc6630eb91731b1fa29270529cd03723df2535000000000153ffffffff0426807b0000000000016a20fbe9030000000006006a6a516563f8081f0300000000008f62e6000000000004acac5165f787d11c000000000307b96c01000000000000000000000000bab1801bea583314650c7e6992cc52368507c700244f4dfa9b066df8bb787eb438ab807f363ba1ce806e991f9f83eb53a3acf38430091319ce12ef9cf41d7e9e18cbaa530276288882443c8ee1c268050298dfd892a4c7f7350faaaf92fe254c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000321fec77fba7411666ae03b40d389d18749d917d0d734cecf8cef997d901b695801edcbb091c0a263fa1bb5cb9a84ee4bfa0f245dda2fdc188fb3683f2de348f4f42fdbc2f7dec432ddec176a8d98e16b57cd3fd59526c1df04bed9e9717896f7c8437d4bd77c7e4f6ac23fe234a4c130f092b646648cc30f5c5e9b0a4f482f1030acdcf9d41ea398d548386a33c11898c93507fa3893db8c5dc2b1381a409f01402293bb047fd276ac6cd9c2a041d3b8221d7a68e38e0e5b52e9bc07b971f12067c0a028e73d255b22a81fa4d2584592f87d28830c20409dc66121bc090c0c07893427abb6fc7dc46dd6d622ffec31ee77fbfc57c9a1c505cff28919bd25d6ee97f62020d6c112b29b520b6a7773b449d4b1f295a1a3ac4609ce19366dcf007dd9c7f920205806d11f0cb7259a1190b75cd6cec5b561d2a9d73c1152f01c219e5d95ee7f40300d57b842c538ca60679eaaf5bfee5539a09afdd88c95ff5e70de541999128e10312a72c9471d176370790af97faf8a83d4c78e29e09123cb5a335df2013c126710326bdcec86c4957a309c3164e63a42c44a4e7d08038b1b5020f556d5adff20aa2ff27b95205cf43ff48e43887a6620dc70b9b1a0c1c6bd158043c80f0defad7a8e37617615afe6771c017eebf8953024ab6384a4023663b8d40f3e2a0e15a87ad52aca48c2a47dce9fef2e3eab50fcb64feff938d8af1e612d1a425753336c65ac2e1cedaf6f603cbc7efb3c3b7ab8aafd21a6706bc85ab7fe8a842e4a62100ca541bdb82657e840cc0081a0c6334ef851047714e93bef390369c0ee434d54d95ba867c5429f927fd481957955d4ac88945a148e194751157c017b72d21f54126a4f2acb99503613b000dbb9c5f33fb71984c1e4a206f5fe75841664e2736892da67f5cdc603c994b1b586b63cfb06a16bf77bd8f7ab625cdb9e09b6d5466ce64ccb946b3cc1dd7ad0f136d586869adc34e535cc2d541ad257c443d032336abf55a1efd6eee62af4eea67019953ae650613acac1be40c0591e19f2c4c5a7c524549947ef0ba77c6d58e050642b53d8b076ea35eef4e105e60d6df259e7ef84abf96f99a43f169a7a67b944be006a51911bd1a07a6482904d36db15d3913489db88bb87ad14a732f194844f5f499055ac07d76ee8d0e1b2c27b48ff247e1ec67dce706452b6ba3a8292a55a5d2ddc80c55dbe83ed7c049db1023bcfda2f85d9c695498b7e78ffd718144a792808088312499f4fd8958315f3540fca486631f4c94ebb629d1d4405bebadf48506f2d12b47b6340afbe95398aea3434468245d176f9c831cf859f5e17b4c5163017b8b848fd2db44200a57b7994824e43cb50d9b0699642252b6a608f55b83db317550f13ea460562175085ddae63ff1b41dcc19da593d741edf96490752a2d91ae902d1365ef5dfd8e2e8f90a650d3bd3ae50a7cf40b4a4725f06e4294851568aaf4ddcd3480f5d0e4ce6d14a9be67a10f075967921abef83b329b8febac7589bc7915f60a3e615eeafaabe340156b01f510e5d1b0bf103b9b81bedb4871622c96d6640c2004e7e272df8f28fcf5b1d760cc2114a80a2e46a906c0ae2feb48fa411ca5fecd53b2cceb37ea18848b6e62fd3a7f35aa9f2497734243e2dc42a5479306dc2d9fa0c922b0872496558960c750cca12c7c8e9db3e3d937e1783cb53e4a9e377f785060c5766eafd12f03bd194edec3d78ae76b107b9b9fd2db135e91d3eb3b03c7d7bf7375a9ffd7d166cbdcbd9c026e827c682efeab4d6dd2b063a54b45cd72f72e64b276efd37a0113f445df7863debc0ec676a8af0442a73f8a8c01819d5ea93c7d4f9e2e27117ab0789c8f3621c80d27df8270d3636ed187f36d60704bab30c5adf4ee4254ecd221701bba09d83d33d34e4e31948c9625217e07d87bcc240beb3079f7445aa3a41f0ce7ed411b984baa083e0e361ce7edd9fdfe649bfdfe4eac51d006593c298a33d9dfbadcbf0d4524550596f942b7409b303f7b5613580e009abc0fb722811ba24774730e326aa06282e0ab3ce9683161d8fe29b767b3324d612f5c03fa27f85168e9374466cb54b5283d2a65584adbe7131ab80472eaa7b0fc439b9cf5857e28535f61e0d52bff7aeff05061c890d5914a08f94a11825e149bab892e719e3704c9a0745074c3e4a6e813a2203ee31b400ef7368154b197d37c24d2bbdf7f0711a930bdbd7c75ef8f64ebe367c0ae825bdd043d6e2472d5170be218910caa6e224605e4a12a626ed25e36bf04cf68aef6b5963458e102b54eff4859202000000000000000000000000695b0f5130ce6d8482ddfc87ff2aef14b6acbe40a59b2585ffa78bf7b8e724a77920d6b836c489f25738be56692c704ae67a4d553ca06620cee088a90fc99580e75f21676c3ec2595d7d5ea3949989c79c441f4e9fcf882c3afea51e56933092000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006c8e738811ccf021d9d2d1c0a01239452623540f9b8f1b04bb248e839a9a4ac927f569c444e9f33a1e39c7c4f72c451a5c39e29173165d389387c58a6250c85c442bf75ba00c93c247ba08d515099195f515a802a49de0d2b6b2d94be63889f7e54b7977657b5a91f1fca9857e31bd80710f3b2420d4eb20515a29fe316ad3120209c7a2e29c494b2dfcf3269a8482b1104054a5b48505971b458aba0907b1ac5a032a61e834d7a95f1c442c57b001d4fad373caa606309b08e5c11ce147a1b6ce560a002b58f65d9dffa013e032a4f47c018c5c91fdb45134d0a30a527f5402d05c1d46256a46b2cdb4c81b312ed70b6bc6503592375f28244f60db10b749107f0122022b8122a25359f6ea0032d6e862f5bb7482c67a690e6e21e9f9935ad2f4ef910703205ebb330d6daad7f101b0f7341507f4f27744f6d4055318f61ea6a716fb58eb03211949f06d5c717718465c9f3b49f3021df0420be9364377d61450db5f5809570223d9e619fad2ad938c2449a501da0465b85eecf90352a9b3225d815066331b0d0208de3b97cf4edabeb3be87b1afa3b2a7a76fd3f25c5523cf48e4e06c40098e3f4b090d9c5d513af869ef8a8d109343494dde474972f4c958b19e2bf114fe2422f0ec8230630f9b16dc4bde55dc3f46fac6d2cbc7278c9ae08d86f9aa55675f7778310734a01ec97c534428988ba91ff7db0b53c181cc0f3695d6ff420b0cf97358070ef3d39ca9cb3568ed757dab7e1e61b0ea0ddca45b19e205abfdac974fac2a2c3e64c8c0a6bf23d95b6a0bc1eefd8c14a34c9342b21eef590d27938e6dca77be95509cc07f8d6d3cebb9a73310ba037879545dbe4aed1f18e27727eca8a0768e378d9ab11cb066ecc73b5532bd4fd07aa33ccf5331db01f392dc59021ad7be5e66b903bf4e201382949e0b4870e003b25f3b4db7dd54fa914ee5e9cd277276874b0981a13b4f4065f265338207a7a2f0578e6aa82f5b1aee75df18bb537a01446d6c187e4b815f0856163b36dea7264903e22f4210c1fb9ff5ed48ec00ae595c5bddc9c2ca177558f8a24ac3a0005d3a0dafd4fdec7283fb1ab8d6100563e7598266e8cb6bf52f1836ea5628246791f95bc4ad09070d2f565c7ac604af4a87b7068d456d6d64fb1eb0cab91e79e4f26b21abeefc7e9648bdf59697712c04a3e4f4549f008903fefefef3ddb2d3b317f78a6c7e05a28e333236826dbd64ef93165bb8d2f20ecbf4513302583aeb852b4a810d354b13486d1798b3a5895fa2ccc8124f6c389caa61869df946d3f88778276474ffc41bef50c13dc5c6f9aa19eeaee6199ca46ce6fcbdac49ea397c861b3b54121020d60eb8fde7d7d55164d93f4d014634b99481283e098db1b0ce505e62ef24560b1fa1d79330d9482c4ac98882d7776449ea5c75dc3e6203112a0f11ab6a7ab1cf10b9f13e3f974a1c3e790b455d9243cf541348873cea22983313f3538b0350bc59d5997a727e11cefadeb41503b3fb16bdd116cb77c5e3d27fcecb96cfe01d3bc055e72e70e3ca0addb92a38f355813e818cf0a4c66d626c16c5ff0d3d8722058e167cde4ca5a69f1237e17dcbba29bd8258f1ecb0b2e3958f43aa1ca7b2375354b3c523d79f2051a6e660ae035c439ea5b9081397ec0e871de964ca302510e8098d3b4ad0a284f7857ea81a29db3cacf4f98929e32ae09dc6e4caf542f563a5c3e12db2094283bc4db2e485a341880988ccaf924b55044d1f96b5663204a50398b1f1d724869212d2bc7e5834a6e4019f10788b1ddc4e52f5342b7b2aff5bb1fbc34617f31bd6409c3783b6f891f77ad899476129536faa2327415d7fe137884f57c5a21b05d592660bc6c4cc49e783118bccd4df7665e44c790f1f7d6f563220f63836d4c3c16c5b39c1ccc45095386d91a5ca010d9297a371ae2b17fed4e2429205c3a60148ef1733e0f6a4f109f58b56c4e3650e7a9c16de7ad2b31d5d9db5a6d8b1844ba63370cce2f28e0fb3a958ee76967539d08e2ed06abe1da1f23426bcf7c24fd5cef29db935a17e4a68dedb02db38803635ffdd845763beaa410993e92b67607babe19202f92019c00115bf987663ebec90c4613049040c5a7135835fa6f85856881f85cda9541a3708e8228efb9466a21c89e25c5453a56d02fbdff46ff40168f474df36aa678855a3e15e5dedfb8fcb631f0085446873e7dac14a0d017e2231387f3f98c34f73f1cca39ddfba929a63f1d0ddaf26bf1e8ae1188302f9d35413ae3e6c406c8015f498120cddb4f5cc28408afae6ce190000000000000000f106bd03000000005588b780999bdcabb63379ee239466cbb0f51ce58cfca4d6a47013b5d08ab4f990de0def1091dd78953df5d7942a0286ebbe7eb232814e7ae32ae309ead30358051d72b9545453ebee8490dec885536cd423dac4b529b1c7d7738e7b4cd58ace0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045222b95dda27b849b51386fd4fc4b8cfd5f0e2773b3da8eaf702d9245d8e6404c0350957d8426ea0258bc1ecf2d0e142fac7e72b1233c70b81b0b85800eaa0a1f8d7da3aed6278d30b08505a20355b524dd168e31ed10ea87356a366f7ccce078547c5478a7f3143b13265449edec61f1fdacd20542efe93bdf7ce384a799f3021c5aafe6d2a8263252d86eb870ddc53620b967632d897448e719f889bacb07ef021b824fc1c447e471b2711520ad402b439980813aa3a1bd9e470987c1d4be33930b03d0e992a478eef2ac9433fa89665911bc00007748641bc1a3fabd956aca84bac1770d65eb09f7c024f4e19dd40220a83a387cf90b56ad0b808f0143943ecca1032d975db594fca234b2a48c9a769920e22ff8e16e954d3d63375bad736578d4d2020661259e702e712036246cdc6c6550fa6f08ef15b1e2c8b06bd10475adca97b0032f67a60de36617eb38de2d67d002ae5a149e0a9f6c7c05a5c956600a95da2aee032f547f4b9ec09b2b13def3aaa0964df0c8337c68ab2325f91922c912bcc6d271030d57af59aaf2f3cb1bcc0823e5381c16dd2c5a0e17f41b2caafb33183c3166e7e3be7b09877916d0d7a46b2636d10c79b1eab68a771aa53cef3b8133d8e6f24e1ec325168e89b2126a9aeaec8ca15d6c51478d4abfbf2c495d00e9560c7fd518c114726d99f478444f2706d92d17c343991cb0980278336283daa26c001f8d95ae32af734709643c379e73f4a265e82aaa34dffc424a162a4c43cf7211ae4bae99e0d6eec85c078c9d7aad7facd880e71819249b83d552d2046206003b7658a2059b2dde35d9b0320f130cc17f9a7e6a9ad208753e44cb6eeeae29a31e1b5e97ba34818daea2a31f455ddff13a334bbdd10896242411dddc666e6980486b89f63d576d9be22dbe91864531bdca4eb75837fc7087911253d5fdd5a59fd253b50ed61a732bde97641e02f9418033e8cc35922119bea8977d4640420bf9d13a2d6f8f7f92dbe4c4fe13a67ced22052ee11665ee3bbaa24afe463a1befae403279fd927cb2afb7c833a3d724aaeab784cc43549a5716e29cc128a1a4767f9a4d65029640449053cc71edbf3134d4c8a5ccd3d86efb1ba6cb6f12c3c55d16f2a48159a674d1c699d396d1e712936f19884f61f85bd63ccaa5c1fd3d7174aba4de931e8de310f092c1c9b858ce3a15e6dd9688569d1619840572df29ad8c1714e7b6028374371d8077aabd3b894f320c5e5f540a9b6a22f1c3329f53c347902c9620a4417542ece2c43dde0674846c46ce14c6893c0f290d658e0803d4f0852921dffe1d7aaf6d947234bdfc91c3ed7b964b4748be8c0727d31e8e67fe16d86c39c69b0ca32bcb29e9993394db1ec734cc0badbccc2f6fb8d45e859f885e6d68488c1cb7c4680b81c447252969affb5f47c0f762121f77035975933846802c04515e93d7cd86b3d88e4be3ba3264734cf2d2289b01f9e7af3cc902f46a67457158d9ec9f349133ed748eb4791607bf91f41fb2787f8dc6f1b26623bf53b371d00a3007f37179b67ab15cd4df9ab14e2c711c1fc8ecc43b01d20df18c9d313903f1b7e4ac7165549ca6fa75907a0b328e8ad39f7c2ba70502b05e8675ba5edeca61f12ce4d1778f38df6000ab603770128faf5a9a76d4e2ae0754ba5725cc758671e9a111878d282a159ece622fe553389aa4c7b3b9cf652ee473aa10202828de8268a485f40c2630fa07488fc7b90bb2b51b9647bd932e6862ac62a77d26b48bec274cea6e3b6a02626bd5607acbc0d1ec3ee06f2b70f7b39b4d9154782cea2e2168da6dcc011fa612d3aa273b9957d596931c08babaf556943e9d0764e15d4f37e28e2b509c1709171e6b50392d8a34d38dec14d1a8cade6c7c0784edbc49bf851a9a2dac3acf8c7f620361ed34b71e4383c8c400abe8d15bbcd9cb90108725a719d88b81b0a7321e4e8005292914ebb2def737fc9fb2456d08fe1baa5c144876a99a4e4f272f546e4f8c5b6f5069ff5886298086fe2fe29f798c9217a39095ff70c0f5cc4e3fe2d6ecbecad21f17db19c5e8f3475cfc0c13adfe14b2ce87c866b5ae07222a92e54599f1e2ef31b679f02df76f493ce01cb96550bc40a8d5eb7eafd2f55e35d9a69167df8098a19ac51d9b1ed7c27d2a95057bd5928ecd71bbd31a5284881876785e306623c4d51e830a967cf6c28adee057020457b1e16197c59f4d2220f1861f103db401d0c2f23e5bd34053439177fa9a63e358e0a92ef2955d4191064740116cce3bb12f1de99439a143a6c8b28b1b54d6f8fef24c48b2559f8b8e72f89a380195378443f21ab6037993f335940785268a3c7ccf7d31cce49ddb1f6f4740134b8c9f7d977f8471f9819bcf95342892a5600e7080c885a43cfddae2a446da716673218ea881fbaf4b200bda09", "63", 1, -999075092, 0, "ef8a33fb595d13ec923c3afbc9f0e093709e8b1adf4ab435e53bbd4977d6a665"], + ["030000807082c403035b5155f6c169604d7860eae0047d7cab4c2a56f30766fd11a2614141c82d4e350200000006006a65ac6363ffffffff83999df98cd14497413ded4580d271a19d0c4f1233799d99d2a011c193011d8f030000000851ac515153ac006542b58f4738548d90c3c53d4167fa8cec63f9c863f3fa354581e074575728b3b55769fae80000000003526a53ffffffff026ca374000000000005525163516a1ca3350000000000008d891ed12e49e37901cead300000000000000000000000000086b2a7bbfdfe921a0a2224c16ee848144f9a45510c9fb18aaf40cc5671103642ddf7a92c9142d1bfd10fdaed0e1c2249bac4735f1d1aca0f6abc0747ff846fff39b8b0077dff04ab4cc72a0ee993bdcbb31e571737ddbcd22f75b8e4bbe1711100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000be18178471cf47075b3a53dba146117f50be049271c555020dff4a21a2fa20290800b139da2117f0a0901b0279408f794cf550bcc283d1cd62eb5a6b115122e5b0fc12d21d18e72c406e7905c69867d29f20299da5991635603dc6f4b2709cf9ce2cf5a2849bb928bfb6d3ed1ea2270ad5d511ab1680cf55ef5c2e4c78f1ac930304d90fce5a6dd3eb5fad835b889ab10a12168ab9013de183b52908874a49294e0315aaf2c1494a613ecafaff1eecb168d9f4770c9ed9ab3a6151da4ddee256d8af0a01774b719739a5c40b70199d8d9cdffb45c19bc0edf5c5f87bcce73c0c118e1fe88f5b7dd470c42783f47df26cfeeb016c7b855f3b761c1a31c2a96aee05f126022103079ba1b0ea42f1718146ee429be80f256c8f50ab7f3a46c55f550a0039910320f53d0bd016e21b6423f1911ffcd62d21038b1d604880df380651475a03efd30317c257e93a78599b953530021cb2740915064901d49f38b105d3740f715f6871031164264ca0d1713fbdc8a84dbb68690c1d5f45b9267ab3d4e5e203e48bd3bb39021b1dcea0671c009724ece82d695f7c2085add350241cd02bf3ad390319ed75aaf69ca06f148b1f3267890548abaaeb00051c11f07b8c3f0ba643eabe4268e7d866a3ed75e3259f5628bc873777088f4767b3a2d98781060c5b776a3105de51e1b97027b6e4e8dafc9e8727b63ed038a36eae207c4668e53d30e51cb6feb4ae0433ec91420a8d51161b620d9abfdd8a370e0ec1e0dd26a1bdf2fb4a85e804281c898915154c70c3b92dfa34808dec752e713530a7aff6edc1d8691d47954e9515617061ff3aaef80ed00b9a576dd211973a1e3139eb59b828d908099602f30ca364a44f8090e5bf02f87fc51fa68e4face16757615c9b9a9bda95d28ac0b010f097537c575d631104046a5b45d40e26a00babd33f4b13170c1d3e899ca736dd5b25153cee3a51628d7d88c625d69229c19020b3df126f69cfd9fde1e9f99a859ffae012e0083d5286fa13e2ceaac298b30efb84e3aee1c771f89772b0bb135ce53a99c9cc684dc7a35add6f6d593deaf52941167218ea2b564695df5ef8888330e317416cf91db80b7e4a5e208087950ebe3eb2df2a2edc54364ce0e4db1fced26ba650ea97a8c2dea894545e3864566fcc6c0707f0dc8f759d78c367272b7a28b953467c5cb408b9b077637c5f998c3dd98c3b82213b1c4f0d2d1b7690a12f04e1934f7c5e7b4892bf776f9f2a98425c6e607c62aad5bbbc6d4c02b3eb62715c3962fc9fd24e5b471d95de0b484e652dc6ca4207a1d39735ca54585e42e8497014f67564712112d7d6d5f54ed45a0b9a5c12f4e032e0ed4ae7fd3a3a2d0e2a2d60b9285ba531d63abb5119b89a1a4eade9f748a780c58c3f0e888c8ca7b5cf0e9914276fe0c69e52be4d7252f627148e55614a27db09662cf49d2e7634ba9c799c7ab9c29d448fc54bcc5411f17cc8fc66808ec4f711a2ed581d88e25e93c1b0ea9e623ad7ed4f73902b488560a9d0bb9420153bb6b12e1e7bb988708bf59d6951f2e6fd25708d0bd478edf785c4d95256f0f47ba2a7f026110d9bc4c3bc11938562e938981df8a2536485c06e6398184e170889021720a9cec3a5656a84cf16db458771fb66dac454fcfcd2f7af812e25aad4062d450e3d8861ade315e39e0ccbbbd3754f700244d8b7a9fa47b4674e16dd643a35870263dd02f10fdf4bd809166182f74b0bee160e0275f0fa4257221e9579b1d9394478f4fe70d3837cf6f749acf42f2227f4d4303037f1df97a44e152a563608e305d6f454669ba17287c58f734c8e76887b86c8c7aabd6042c06be74cc67c0e67274b0a63a36d6b5a69df8fcba93035c780166930eb29f1872e3e701538f9496c64d37e14ef2f34bab061c2d27282d1e9fee7b2b86eb989d1394eccacaa2e4092923ccc2ed4cdd15487cd450a3bd5ca1a32cce0defd86734f67361605c9a433c277386f6d6d0cfa1f5130847c7b8fa62203d2eff32a9332ba833b2bb4503228d65afd4b423a09ee9c371577b3049aa6d6ef5714bee10af1404d2751562bf70f5f76234053d711822c3779669bae8790ee02059a476e8ee97106b3c5945b1c2ebad0a5b02c096cea5d60ab3ba94e035598c7305bacf7e3d979c709c1c27303c186947b972e4079fd19998bfc6481aa14b57fd72e2508663aade7d3cc850b98d6d69f89a9a9b9d61e320bc7d4211c92d91bb6b8aeee32aa945ef25a3a29fad4f4969c3e1c628adeb52249c2d81ab57c4598eeeaef47865b16b25878b8a8d8226d2492d76715443fdb070b72cd42d794630c4647a1450c08980ec65e6b1bfc9218354674aa6b0d200e1da008faf7891a2d84a450c35338fe3e03d56a9484377970f032bde019a90e202d8a202541b394eb97e03e9f059f747d7680085d07", "52ac", 2, -740108740, 1537743641, "d57fff821842dc043fd6cfed77911deded89b42d3cb705fcb216d41d2b6e3e62"], + ["030000807082c40304f2cf6afc857ae11eb2716b679a0806016cd497542185e597cdafe546e3d6346c03000000046352ac51f2df84f5609842b91808e69244f13e6b9ab9c37cdf58551064f6f04b9890f2f5d3bf3afd0100000000ffffffff388a7a94c1febdfdbc0b3f06bee4780f5675b16eca298e5b7357396932cf8a8200000000065252ac655252ffffffff6d8b968a52a82863a8fdb0758bc670548a8de6476fbe431d2ecab1899a30821b0200000003520052ffffffff03c8cde00200000000009cd8a100000000000852655152635352ace989230100000000003ae6f7f860c1f1c502956f3e05000000000000000000000000202a175ab2f2027bc149b97571826638f41fceacbebbc8e30e9b0debe38250828d6a4a8e1c297ff6203c6bf91378a8edd050ece87d69490c4d9970d30304472bc113a5ffe3c75dcc2419a190b3f7e17ff8e4a521144d6f1977b4cc85158ced9e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000081b7d1a33bfd744b1ad04f59d43c228ac8d32d99c78cde7d91734e8a3e85b47d80ab1f79197459fa5695ffee36cfa2fbe22e95ce919bd8c4339e5b12ee44666050999172fa3ca932212eb9de029c404a12f5c6b3c45f228daa5b9e4f03cf5a7392100a941964dc614773908f41bb7a9f1d5bd8f24e696d592a95f8f465507cb903025acc968603e6746ff43f3ca344c98e511a3fa13879506ace27afb987275787020c67525a8338907c6a49bb21db09ffbeaf431816c717e20bb78fa97fe9219b400b04a11c155f772819aceb5881bba99bd9d54f1c5cd3d8f517b50ea0143351c025bff03ae3663fbdd5dac3aa44ea1baa86965b6b0713646bcecc4e9ac60c231c5b0324cea82eaeee12bbb6750226f2777749b93ee0ce4e28c5c9c6fc226f0efcc852032662f955d375670f20acbf1d1a879fa7d45c6e58a68fb45ed5c5355c007d1b34020628377982afe4b22145cea4a175341bc71986d137715471cd5567a7bb2ae1ac031c77cfe32e2f28834b043eedd21ae568fa14978fc39e2151c5eab64b9fa9b9a5031f3671eb08bbf99d7822d6f8e2e2d96dedeb681db9fc668b566bf0edb1a50a807350c66b56b3c6f1ee4e65d6b992a9c1a7b13220b50c86b722132a693dd74e5f8fb00fa952e419ff36520b84a7938df73733b7c298a5520ff322961ba2a69648a401da9dcc4715c7277b4cf918f437ddf9da587d0340de1e136655b67ab51417fe1da646c487aa07663efe4215cfcf7dbdebb1ecbd8fcf0b4b588f44f54991b5d4f9349021a5fc13ba3ebe8f44a10a471343e7b1cfe9f17fa80b94058d68ad1390457b0cf02ca44d0a1db53eab2e6f30124d77200fc86f979020aab06f1dbc74352e9ece2c95b33930b6a82b665b203d84ad50edb0f90b5b7cd722925309b6e9fb3ee8e6fa6b810ed29f53d4f5a5239925e59271b7efe5cd1e9c4352fea79821ac48e6641014e4179b0f7f334527dd30b525359ba2c12394f5aeac3587621af6f05845a0183235d209f2d7014e3d81963a5fde3b9c1143a5387d1587db208d3907347b531f415b57d1e8ba19d79faf04a6b5b868780a862ec33380f016bc96c7101f40f173136360115440cba5d2978cc62f11d7c1f2f4a0793ae06b99c516363c68a03ad99e5c11381fdf86839f646bdc98c07e64c2e64f6a00a413c9c9d9d2ab9fc6c2d429dfec958716da6e1c858acc17c3cd2dcd7483a7f0cb6057c1a2094bcb9f512eb24a3ec49447ea7b7b70d0301024fc25333ad3eeb6604fe80075a3cbfecebdfce8031ae47dfdc25ba56757f5a4452988dc64b3702a154c91741df62ad7bbf8cf199a30eaa0e78b890156fd8ff0c80fa7b8ed4adfa162ceb56c6b082a518bcd29f0612576a007b5043284cd8ea1afed5bbd21ed60fa6446064bc1d9b96465f3f38d63cac078f6ff28646488fe3a58d88b8c18da185620e169a27530f6267249a5002c0775cb9ef5a2240a2f95f5c67f92b2808ac7ca161b9a2801d3ce3af98afcd80079016c3f6b623c7f4841da6e441124dc4096c0e219ea85fda777640614378a681584683481a3f8f93783d197e47f3bffdcd5f39d458119da47125886c092af1b46e14c9dcc52e7c0d642801b9b8b6678628bda9b7c24e180986870e12e446c6fcc5f1bb11d66c2536b88b90a66a6967a4687aa76877ad3dc15f999f61ec0d58aba4daae19d583f28fd2f89927fe8f040228e2ac1e03819db3d482ca16dc71038d5018e0aaca3e106bb4bca2543529380e09c6d3700e744a3547285c74b3dd25f91d450f85a86f5e364f6ab55b26337f8f77a28a6c3df53db6ff9524ea6c88f9a01a847db419c05fb281d80624b89756e19c417e0265b323b02446c5018a5fed63dc97f4e5dc9c55bc63699a411e75bb06e9671d5bb40cade0c6ba35cc442b9c58bdd8f674f891912089809c5f4fc7814275da1c6e63597173de8b6754383d526ca12e4232e9867b63924b1d448391b8c39cb5a5f273a46979655e5a0cd9f31b8d3baf7abebc7e0c53f188e9eea385b02b82d850699de4901fb59a556865d8b63b101c89a0ef34db9c0f9d3f8dda866d27ef86c629b790553315e2bc93868770f5e579a8b2830f1f15acb0bad494a8afc6dac7fcbd1a9dc8a57782c6b454895f57f3633b46493d44e00075896636aef97b684f2027d4374ace6a3370c2eebdfe9fe3c72e6c6a0572223b5802ed9b7416821036433051e6f41005b9c796873e3cc923b939802d5f20b9be50a2171a8ce7e99b767f6ba88d2508550ab504d133d5d14febe425321199caf513f20971b04000000000000000000000000c16329707bf6ec0b206cc2edf0f17e9523d0c63a35f97b84fda5dc18c6afc8f234f08f57a2be145bf0a9a7ee2d067bf8c9f0cf9d91c563bcc96f8380528c5c0acd8a994785f856e046e975ea7fddf67c1a3c70ab1d568a45a50af52a285e0e160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000097f44f9caa593c5fd966dadb4bd2fffa62841a94ebcc5d830f4268772fc3e9fe33c4a60e8c988e610144f193f1b733c7dc5ff5cb74f70b65e2e7d61895c9353b701a5689777804c3815309cd3eb479d0c72ec513bf8a79a47cdd7f65223214f712630a8f188f1d3e8d427686b9b7ef620a8fdec2bd210b6acb0384467d5ac7b70314fb410d243a0995370cbc4ac876e7b2d19332ef3cd83beb11507d7d714325b9020c667d04dfc25176e71141361b1f5e5e284830a613af9c2d8450fa5231d44f420a081d45174f7d23dacec1fdd1693ba3a1f14fee8a50f8101e09980767ca628aded8ada0152b2aed1361ec33dc893a12a7eaf839d84d363395e6e41911db88b31b0314d866a425f87daec0c41fce4e71a9237d53a5df67e4f65ff9ac885a0f5edec7020dc53f606084b953917c9a1b2076e22888d06e968aa4dac3e041bb70f46e15a003169c6ccb8174c196ef632bad6d4766a96f8c18648923c4561fac7f894c049829021bbc83d9bbf0f607d712cae1c8cd67c8f009c5423148ec0520016c4ffff20fc2021e1e80d01cb9ce030ebc3a4a59d65b17296920b3032526007cc1650780cec70f908d2ef7b845b90a29ea5b3ace2c1d022233cca795050d98211133f7ff0b1c39d122523ae25cf62d99a6cde1bf58367b3a9470f57e31c8a810f23173e90c6cf096a29f82485f36e6adcef5d67b3564ab11501300617f9f749f5d156964e3be9e9e158f373b8c248e9a0e7fa7f8af69fa17610d9562bfed079511aa061b10f4d5dff3deb60bc75b07c579fcee3bf4a930a806cbf83bac7f0ac794eb38700852ebddfbdc97b38c388df4feb31ddf4dcd8a230436c060f980b4eb43cc53abf3feec388f3b3a8d968673240ce02ee97b36655bfd2acaa0f3e503a0dcb244e8ca2d315642c68da11434d9311cbe9627cc024e5e9c5cd601626143b44a977dbd3e639393e009ffa2720de57448812a01b2e250aaf1ce8695f365d18ea87fe60d1cc333a47724813d927e3896c476d370311db44b62889d627e58139ee68099420b051d52655f37fe66d642a319f21ffa92a3c2e34bd90771d55c54e51592174a9d2827dd484ebb2c4b5779455e86714edb22c661c12ec7187cf9e06e29da07068151c9444c4984aa1cec59aa6f8e0d2311d09423e6d0226655e28fb34d98a5a7bd4c824f66b218a709042a33bec232ea441a08b2b58aad70327f691958b6224b3ff06b94d80b4c3d46a7fb290aa439597db31f57d097996b0752a5f9fd5b5ce5d4058a02b43fe92ca5eba799fd5712d371d5c71b35cae8171f5c014c80ba0d834d2196716e62b4581934dcbfa286c1ddc51b4a142695e99d496ff0d6d92d7ca16ada1e953e1388c02c9fe819be1eb9a0e76b61a8b53db6b1f33a91b1ad1456a457bdad7fc2bbb3911d86f1b942cec457cbd6fea69484d17e7721ee08b3abab3a82a2d2cd374bfcea7e28e074ddf2e1d81c37c22e6d1cb35beb1437aeb9e4c814d883f3a07b74b9a75491730315ab6302d23eb270e5490849173317cb8e11526c9d56cdb0aec9d73d60bb8490d9a340f90853eadb484227fa7f88f39b658c086299cbf73a023d904e0c0204d3a38490bb90409eb93599674c4ec638b8e326a95f6717b0a5f5bb91ec7c03f0e393cc50b0e85da1765e66581578fe9aecd18a88604b1771db53eb5128023da63c890ca690d447e29788205d04040227aa187bd8b449e1d78800bb379d9277d36fdc007298bc2c4d89d28a3cca3b36a21458691330677668e50c17b5cee6723864e63f7ee080dba7d039de477b334ca88caf7bf950d36c4140841c360cdb4e0e9cfbd2168feaf34c74def97d7546888e40b02525d3474efb7ec68d2268ecd5adeab01c8553400b6190ee031b44481ebb55448eaf0067cadb12fb0b6456ca6c890a88d3144f75e5b16639d668609674835cf4469c5b73205891d9b57a6915901e007125a55a4b3a01d4370257d1b174de39dba02e81390e04ae2773b922be1797061b724fb11c1777943620ad111d3dc81985cedc2f22aba7ca9ec2229bd6764c807a95c3b01b6928d1296a9a27e61c7b776bc64c29b4a48e2fcb835b2b268710ce2397da37ddf2e71c3ebe1417bf0ea064ffffd93e15263f7bfb76e040d2b32662972e9cb3c4463fb270af0ba031964c36a47051372d37b1879c89871d6714fc1006703062a8b315652361a8a5e6e474479e236b8f19f293feaa365d5fea0e6459248cc8e8555f53402f5dcb329d6a512f5edbde7c24a0d98ba2458a740681b9765e37775f046d2a49c731ca379d6c0f02dfd21718be94bdd968b454706d610bfa3cf1eca29cc1bfa4434d0b74dfc1c1074e120ba6448eadab4aa0bc406147f8ae86b03ec6d6691d951a19b4b166c57a1a60dfad954679c2d04fb688c16cf0a7b0685b709742f14f3c0e", "51515265006351ac52", 1, 1929323119, 0, "03c1e4522278fda7a968414b576e3d99c96bb763bfca39412becf69ae6386133"], + ["5da89f1f04514fcb100927dfec0713100f64de114bc8930529cfcf77a6f35a6b4cf694a26e030000000151ffffffffb1708b85f7bb73a93b812fc0a84c0949d23dfc230df4ea78315e9762f93dfa2302000000066a65535353acffffffff0b35d56932af83873ec9317444dc977fb463c08c84d264b53094f987ba4cdb0d0200000003636352ffffffffec5c65c925efc2a9439bb224e54c7b93ddd5d5f0f460156a908f69bcce7044f9020000000100ffffffff021e2823010000000000f618ed0300000000010000000000010000000000000000caf8310400000000bf70db30731b4c4bc0fe598d514f3b9b032e5da448e3becc9c2e3ccd6fe3eab3d65b21325b3940e44dd5f1b42505a5372e0df241934abfec2198d9fe743e80abb015f281f47929ecc88e9d0f25af5f741e99d2de27a42cbafe59119874610b1700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c36e0fd4c71044d1b00d836c546345db8061c7296ebfb9e3a290183b5abbbb4e9ee5ee2fb7877e609ec9425d0d71daee45acb59827d08df145000d4fcbab1e8e6a3aca7b2acb1c71c704c4dfe85defd39a12228905d73bb048d821a16318c5a7ab558878307cc4a5e54f7c95d76b20b2f042e1df4c2c9540d651321193524e1a030adc008f5bddd4cd08314c787f889ed2f6520e706a96d0dbc946eb7bf937d5be0220f203ba8e30ebf451766d1a420f13e0e4602d9dfbb4974ce0dfc2ecce4979850b07b74198ae45ba3c2d77d28c6b2d841c39ab6a3a75f60f20ae9aec4fee41723290664e11af421795911e54cf64cca977369f5f68aedd3380c82d976f4e38bc84030d7317882df364be530208a062eeecee2ad9383cce7dbdefb32361dac9a0edf5030c06d7cbfd80e8d752a3bd41b4972da891b132f3b51d5eb66ed5d337cfdaf32b032f77620843780b81384bb0388b1abd00eb52fdb0a6a41ab2883ef0ebb17ec411020c55c8e5fbf52e0bb96ca4d591501f9d46de5fd0626fb826dc14e30ee977af650312649ac776f7fdae06fd3bd20422b1716e28e9582613b1507272ff647914a07f986ab8334d7cc05f842080367be9defd40ba15b2fd922f4cf845c322fca006d0833b1c225065fc3815b3d784b947ee6cbf6dfb9ff9ea337ca8c972cc75fd281737d4ac7a3b1af695fac0d1199e85ad2d35717906988d4e5ad615677dfdc343eae9fa504c0c9c27f1530543a95031dcf7e11412ecaac1d04aaf4d9a4c873742c11b1ea55e18ba5044c6b65c2504c578708051f9bf16918e4149eff2a415daed03c01316ef0e81a4f2bb4282a30d8d6442ba464eb1468590b802c66aa78885f09a7790d2c4357d5fd5101e1ca39df7dc81bb0ae80f1894e41912a0e49dafea7b19c5464d4fadd5d913066b90c2f7c41dcde59c180e1f360a6d1b495e922c652b06f4d9de7dcca56672f6248a07c9914dd5cbe939dbc315127ebdd3d9b12b326a932eb359fcee3671885e40b5e5d479619b58e44db62b0fc0d22b0a1ad50cb554f90b15346f587ad41fbe9841880c842405480d31406c27aa5a49de368e41346a62698cbdeb0cef48d6a16af896f5260b3ff8dd8a259842292631bcb6285ae4605329d0ad9e19cd74b346a8ad6404ba691e3455f87c7db90c4950c36b3aa618ec4b254c11ccf1c19285aca53dd7cdf70adcc5d9b2d10b9fdc5fe029662db519bf495849015a114627fbbe9917151a89f13565fa2c149d0edb35fc5e522d1e31a2418ff71d4d1df28802ca2bce6ae3b41c863ff05825c77f851b7b5bdf327522c8f874a201374370e98c9e5b1852d0db2fccb07bad4b5ed65d5dd9137b75b7d228f22717497f223eb538160f5670262c6b0604f64a30aba51baafc536d8fcee2a77415728f4e165ad0643ecc33dfa0ed44801c3db25a8454235119aebc32db1d75116df238d27deb89307479d7d53695b079f2828e274ac23165c2ad91ff26c0fb61dbb568f982b76caf6261908f0cc524d8b8a98ff307273923a5609333a407e4f443642dca729671d92baaf4c4c63b36923701c6829bf3202bfb92ff089dc2c535aed47dc58cfa522eb2923e1ba1e4d0939d539916f2be55d8a006b4058ea28398d69eff2cb99e2d746df31ebc9352a9547f5890b6e0da4e318399c09183be906239e790e8a782aeda0e3df12dea29ae7aca8ee2b12b33472764f99d45582543a8f69800c670cd90c703dbaf6a0c532a51df7a9f6faa55aa8c935585c3c29143472156cdfe54bd60bbecf8c740d6f55145edecc133242803769f833eab0fc86f9157e61ff317e463b666314d135ab69db4b0b80559db5df6143eb914e0146fd3a3f8a5d8169c577b9d09fed8ff533651bfd54d7acee229e09fd17ec0f8c3463a1511eca3d0bcd71d00fe5618707cf44c921d2ff6f422e491ca594c3085c495221b334348c5b56314ef0b4802a9d85d36564078b6f642306dbf02236d7e1db28130077ed2b87b4cfb29d97f4086cbdb6c034593e30f66aab329d4f638eb5b0e02715c00efefbe05bbb80df61d1e7a09702f8af4c5618c2ce84319803b47973fccb384875f9cb4c6cae1ec9e5855fb410b71a1f6faa63202cb8a90be113a032e303cc19ecad6df227f1e6df922b63f2476f8005cbad75e2a699d0ec2a69e2f7c47d05648ca7f66b7305bac9d3a6fbcc24019c3ec403dd264b2b559c121cafbf501e81325cadc3b7f0a9919c5649ee43ce6abb4fe196ee7d90c43c40ebeda20a6422170b67d8163bd950e33f1db1defd185affcdc3e9f10dc3dce02456cf5046b3aabf31b65e3b24b08eef8daee25619734b2dd7a3fba54009d57c186f28338372f9b011887bcef9ec453f46eabb662a07ccf8921b747c6275c4dc9e78308331aa58441ee0042ea46f9d8b3335fe61fb442c5c30a", "53ac0063ac51ac", 1, 1527985764, 1537743641, "a1821fd90ce1e40920bed3d5152e11d89d928fa0aa6c262edebc229c53be8b8b"], + ["030000807082c4030174a93d720d06477a5538b04ed763e1711794cded63166828ae92542ff72f3fb10200000009ac51ac53ac63650053bff3092d02b41f1903000000000763656365526565537ee50100000000040063516a00000000a9c0e7fa0100000000000000008f2d4a0500000000ce4412e558bb3d1735061073729f08bdc63d5a1b7c4b0896b36c5daeb392f42a91a60d0be4a17f05337a61210bb43a213afc55e8a7cb5e44cae25b2f066af5144cfc47c4da636deea7465ecbc74ade89340c690c785d38a2b9e703b3f209421100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de98c2917707da4f46a04e16667317a20ba5662f30001f3b0ffbfcfd8add96f2d7de5af453636077ac5a90ca2f037e38c12f799cad97d872c217a7718aed8fa6ded80f75f91d390887f562433849ace6243824eae90552d035915269cf9cb759b9079e7ba45a54d1c7db2b62f85ce6ceae5eaaabbbb77f4fa36f2ea45248fdac021c0eac36577b58b3f862b1ac6aa3bf3a0ae5022377228be0d9f1c58958c4d65f03055bcd193437c950e9a38317d276b4450c3f16c64854338baeefb9f971fe31200b03d63ef9d429b5a5d724ffa71d47d024ddc4cf7cdcae80b1c33504d5a0855b9bc2becea1cb9e73d0a6fe6401ee0ff41f8206eab07451ba361afee21c0e2a1af50301924bc7b55833f2fea8e6ef76bcd6ef6a9ccbff9757508cefda160fbe7b604603253b2551424edff355710f2f5444bafec6715a805689d24697348faeea91f519020a7fe4b81169b6d2ae17a28c4778008df62742a3835570e106abe870c7c57a66020a1f4f020d9a6fda47fce6b31b527521b067a775ee49f9072ba6ed484427ace0030731dc00e9e51781b0593aa31b2a90fcd64b83eaa230ace5e64cf19b61cf90026f802baf5d98b00a12efd890ddf83aadc1b73fcdbf8d8e4e28672d8fbd37fe09cf6633d13c211ddc0cc4bf875f40728c7aa91497046f121df63779986aada996cae1500fdcc1c0cd808320c6d7539a68602db0b118713cf4571dd2da2e0c52aee49ac0e65e7e44aa9f7705fff5c8dd1c5701eba456723652a22aa2526e200601a0c3b0d80132c4d594db9935fdf4afdbb996fccf7f9bc5b53aed24842e6ad73763601e0890c5b60139a309aca20970de4ddad7a7bd7bf5498197528b11bdf5aac1f771a00bef1768565820faa5a5e2be26d68fa4d02a0cd3e96f5966a2e2be6a338d7e9613097617652d0e5f70a2cc5cef18b4219e918eaca9a9933062a01b8ca64af4a6a62cd53d7aedd746c17fc2376ad088334b3f32aa39836d66251821cbe85f028d15d1a785e7471fc3b30b8c6605322151ded462023f4d97383c975c17ba459240fa1b29bcd3fe32b0c06771a86e26708d3914db4a383ba32b3fee659e57dc9edd3970e1ef9467b9ba9482afbe25360f6d872367327617c25e2c369cdb81ac5fd8c70bb66df8541c0b57b71229b51e09dd5a8f016ddfd20c9f7cba5fa0d74f5b93509e81e74c72992d56d410f19c3b371f0fd37b5efb2353bb523d868e7b040bfdbf049b40a75c1ac133ec09a7847a3ed99159fb78e972a23a13797d0c99efcee32dc704d332eefc223c6db8168aa2bc43f33a4885ae2a195188e936d1c854ba6b9c158e3522c4fa225139357a8dbecd964f9ccabbfd108667f88bfdf3292d740a948136b43d689ef0447b34a9b5c9fbee151d761c09f83ac2312e1f1438938ae0a037fc333de705fc84eb61b1bdd14bf45ff5ff59d3458f1354346b662efb50f136104f76aa9e456a1f2c9521e451ea8b9b32981d01f8b3131cd52f9815f9c90ae3bfc6caa85e50925e25301e97461c94cbe44d6409cb056abc532af9badb0b03a69dc827824bddf280f584dda295f8301510df06939d81b6e0c8c97d6dd45e34b24e579063ca9c72604b5bffb0e1ef8d11b81336d427a661902ed4ab113300815cde7ad6733323c67037c037e3b3b11070e46dc1ea5109ae298acfd6dd644645064e4d552a344c5c9209d786529c7714faefa75e65e91c992871cf62e636916104d907a4074263ffc30ad242a130b2ab00eaabeebd50d82feab961269afd913fad665ee13364b464c8558fc0f689a973d349bf364bbf611004ec7776adba2ad68ab845bd2d1f5995d7132e8ed344a4367d7864999ab5de9992273dd7946886c856ce30b4b5400e5e0c635b2f92c7d4f188011ebe3aa4666b3303eecf3b556490e45343e0ba421ca083b7148688926db734b325f2bd71e12bf8bcbd339c748a75776ccc53177156338485b14dd7d39b3c4c1f69c9421b8eab42f7f5415aaaf0d2a3fe57a867a9ae10913ac19c880d1931f834cc6643912d39fa4935f58da845e80932d1c7ae5bad9b90e42cbfe61589e64fac841dbfe32fede8791d48638c48d8adc2f22d6a122bad4dc59cd31b45f234b7a0e867557d7c0d59479d3d4a5b15f27bd8fe340e2ae89f1e403b29657b974663021433f2aa2766b59a6bd97ef76da3831b356351a17340f94e107cdef574dd6625233eef46be1905339688e647d463335874828633b20d45bfba104d1e52e0d50ec94b5c441c7a200d7314fad25e4c4d712c4d31ca59a8a59357d8f0e2f1607026bc957ebcde75d16231a2809ae7a7ac681d910615e3e964a95435d54208f9013a6456c62c5b2f3995150d6efc7161fb2d5fd47f50b703c79511417abed88aec5a99a4b7c53fd76f7b7e2208a592b09982e5600a011ae3a7e365e8a804", "ac65", 0, -1094666649, 0, "f582902984d5fb872e9ec722f7b5d673ae01efc463772a6472807b98c79ef1b2"], + ["030000807082c403021455ff10b68c7812f46fb6d300875b18d1bc1c0f0119bdf89d7f4834b6c1a200010000000663acac6a6551b0e11960feaa425a1e02a23a008a9a73486bb09cd25c3066be1764dfbd22ace3b55f715902000000076a65006551656affffffff03107a2704000000000263ac0b8642010000000005515252ac00f9a50a01000000000453005163ff65819e0000000002ddee2f02000000000000000000000000b1a0a09e72a5d79fc717fc272d47670b4c9fcbda08ac65478b01cfc7e2973217c21b6e31ddb52ab60f058732773487d2a21f10a06687cbd989c1faa37c0cc596e82febe66724ad8c08cf2a13fe8f481455d0b9c0b18f7c2cf2a36488285f3573000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009b82b4cdeb4eaae89f5df1498a3f34695ae334cd6159c155fa290cd7fd9f3fe24b226bb73477a5c6a0f0a86212787490bc5962619e458b6a3ea0ebe6c756a4bb6a5a64fc85c531764132ca5e0e1b2ed4d38d1e31e2f4836c4606660e7d5d8629269a17c8eb467c015fc6be00d165c020d418bcd2b3dac63c0529c4de6ff57654030102be61b1701fda0e2d560dd1e5111fd8221e594b31a22801a7280c1ce700e8020c3730592d8ca1d894c45b3f6e340984a9813f7ac082321f12438b50a29024a90a08b9c1a2eb83c2db651be13894c5af49c78754d4f42df84ac01842e842ddba24ee6f2f530aca3310de72e102b26a90d14eb02a3d07e8df929472b73e906059880303d551c7b3b29bc3c4cb7e790bd0a32726f44676eedb985f93ecfd5832604cea0321e304a711709560a49d070e320444e709e04243e5052f7f3b48b3c5c6b741ad030e0d17ff61d41b85532f72801775a325876342d3d94a0023a1245a71fee2e073021bd230925bba218641ed40b28250d6936f400700418dcc4a8345c51ee70186ca03060aabed464a6f19ed2001787a76154b17aa6326c70a644f0a155d848c7c7d96dfa403a2114d7b3ab15b90a3457e1e5f09043365bb7b7a64831f1d65ad1f1b09bbdebb76aaa918d654c8f2447af561c3c9a46ec2969151fcc575a5939474f9c561c54123f9479e3084b2e6e0fe2d757237db1730ef6e4e32897e408b0ead9b0d05f416fda3ab966f0f3fec08db538d867466eea007ea24cdd56d7ee5cf0f74c4cf7bc4ca31a1a723ea28f36be6a4c2daa1c877c798ccac67d82a499b8e9d75aae08c3dbe5998f777e9d58abc00dc0c49a01db9463debb226c61cf89a9db150c5eca4c77d1b2938d741137b304b57f2b2d0c0878bfe0b2269f720e8a22d880bd8a25ba3f12ef1102ae28074277bc4c8a0b19388d7bfd0c37c3c96fadf670b9fea0d92a34d4062cccbd38a1f46eeff412f4713b4ec19548804c3e3c3aff14b412234cb376785e9f5716774d2742c053fd879c188b01a7e297a794a4efe74bd13c72fa8d1c871eca5c115e4262dd7f476d8a89359ec9b9dc107a03dae82ca19305475cc6760558f7e55467c9698e2ed556c64b498f6c01d286f109bf7c0cc93bd5024c7bcf6645a50f842e90d95df0bf9f3b8af8e83908c7e8f1578284f8985cad8545e889c5a544f9b75d2afcf06cc15998259887df55a23cac399f47bb6157cda42742e82289c4cfc293853b24fda8e71148d8936543d069b5b6a645dd3c27513b05bf7456d04bdb47a0ed6a78620df48b642c8887a63dd90ea34ee8390406418652f6109b91cdf25769224f19a2040342a87e949e970d6f7f999fd930cc599d0b1320eaf49d0f574a01d198947802ddc6b57e395fc30926acbab944d92532bc069b2641e1947aebe5d471cd6b1b1ff9ca545322c39dc51ee1bc6ed30149905152b828a36abe1cbe4e5d1f4216bcfd3f261a30789931ec462036b052d2894c19f67af0ec1443a457445daa26233efdcfe8ab097b05b1a88cc643a55e9728957b3057cb0fb0689ad7fd88d9b6e405a6af1d083bfe3c507fc83af23b5fbb11712552130962f17ae462e8c4be7799f8c94f9c3236a17ce9c55964678fbb4294564841709ee9784f9ab75a08d02db512960afa3b3631be2becc7d2082b107264d4178aebd24202d205b322826c55b437405cef8de02d0c4ed1360553f531662b508c3e4fb9981fdffada79751d0eb201c03e33d46e9e76d4ba98ddf195610bc8dec63f15391c91389585ce2adf90465ffe1ba502d873e50d2e07153b40ccac0ea058c06626017272a30a8b23d915b1f0dee5b9d54543b7d95e3667d2b05a280b5340ade15cd5064c2c4974d19b5a3a95004c3142269ef1effe1ce8cef68abb6dec0741d37b7c6389e602cd9cf7a3237e40cdb363764c57822bb190057c05b1e6869add1d9d84b7fadbb06b468ee7b2349cec49a1d25c06289958e30a02c000baaea95ec7713ddf0a266fcd6d37f37bd15163aaedff3e77a64ab17138108491f5373d8bb6a4032ea2181ff88582f0585f02c21188999d8547a5bad80b95e83e763802facd8a4f87cf27d186a27223da9add473b4ac54b457e86eab48de9065c7361428f6e6d897c279401a57ef3e977f9275ad370e8ecec6403b4049738ea1c154cafa6257ce453729827fa55828cd922c91c69b3399d25287b8d95ff0c468c8d99b4a37b5838193aca616bbfea3f814d861d6dfc49755a317ea942e11746599eca89872868ce9daed66e60373c20bc4262863cfc100000000000000004825280200000000581efb9d1cc41dee78fa9d80fc87ff53976c434cf4f64f2247abd6d55c8ad9add1a4fbf6268e38b42f472bed6c862bda328e5700a268ba982d9c9361226669e6843701258b212e89c8488885c547fcb1fb47002d6e793a4f78467f7bd90d54cb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000028bc573400b4adcda99b519804bb7a126a5be16c0d4b9e94052eaa4e8b63e441aaa5b965c3b3adc779e7de84c499ac700d01024ec128f8b994b2c67f48b1564be045a28b2d56d2c358d9615f8bd52a49a19ba26cdadf5a218d93b6dbee8991d274ddc2db92d665575eee8e0da54e61b08c335136f55afeca3d18353bdcf1c9fc03172c58b703884eba9ceb2325c07bf516a7fd36fd0219c94a9a8d42b9cbf21ece0201711f983ee1dd775ff8d3d737c5c74b0439e17fc0f1a98ec8c529af8dc429aa0b07dd6369400e2f014152a4694390eabfbfdfbd8b6cb8de19e742e4979098c87a8401357bc438663265a765d7942a5031b46bc3cda8f0ade982042ee94451da080308e20274c1cdd3d05004ad9c10a7fc417fa12c4bbfba96c4aed4c1d42287bc82032f7d154717b9ce98099b7142d39364c51f3427ae277ba64c2d54f6e8c6e3493a020c31ec6763d9c93981e511b2ed59796290127004d634923dfae2b11f7f80dc060325faa75a36dd4109133851ac91533195075906982a35712383be51d20a179eff031ef69dbad37377d668d8c799a0ee3a67e4cfdeebb3844041c21a60cb5dc9579314b0e70b23c31eec38c8de4a8c63d61b988b92fb91b84c1591c0692728da5c310eedcf5156be2883f85145ea3781d6c29bfe7ca2392622ae7b4f22a3af5d470d27f53785939292c68394199d78fb93b5156df1c79802ef97585e5aa5f498dbe3a32af4cf8a888636f6c34f7f8e472b41d97adb78950fb749ca1e5376ca6c0f82d14f48a3a1c32731e28c441e3d03f85836cdf6794cb2799c2be07abebee2f592ffd4d964cedb7c03ee2eec1aeed141d95197abd82c46960945a8905c7a93bb389f66f434af451539d404cca953637aa583c67653e11a862243f3fed6effc0c0fbf2475d658fc671b53a5446abf23a132402670a6d72e7b4fe55a0094af9bea9f01e7674fbdf9b35e732aa89feed9c1ae73572bff3a751e7d0f3785f2ea1e1a465bf94aa61d954b2f3e709618f88771e82ded68e58ed39ad8682665cc42a82e9efb4616846643df65f37f059dc07c03a6ef6f67884c6698f13a9d65ca8fb3f6184aeff66790fa852824a2ce3faa2cf6b1df111aeed04047ec82130d7a5936eba09a2db42f6270cc5a8a8ef6d8a2a8201ce07879760ea92827fea0c767b31286509d9896090b1c2c5ee86ee822e7300098515082f4b143bef75e396a1fd04d0ec2f4be1b20ae67780650ffab3356d10d62485f4c83e202fe315de5a9ef880455de0a6d26c44c47c131071bfef339b4a7449eb2a56d9a85c1d6c6357ea1aba18d3f45057cb591b2c01f091fa9a0aebe32871775948e7a49fea7d3eba54db9bc82836403561794332794c32e4d4c34a5949d83aa82aed3078468c7fffc83256f81e05fb0edf902b44439f74b10257f70071c19162f03bc7383428a42d7bbbe426791f0b9ac0714b4f427ad26850fb7ea479f2e99c1497fff6e06fc994bdf1d805048b534db2c03928d0bb08e655726ba99ae4ed0cfdacbe82075dafd4fe875a0c20753a5a5fb4e58aa86b20e543ccbcd5ade7fd0f2ffb670b8c9148fdf0c18e1d4c507c0414a127eabcb0c1c9907b9b431dff879de55afe016b2640c22c9cc4d375b7c27e266a745dfbda1dbd0ce648045c721fb3facbfa8c5a765c5febf5b9332c39e91a7f2dba4c02c05234165031f06d76bc6290fdc5a2d2a02a212173c0abecbce1cf0470475c4eb2b9bb07db52fd8725669dfafc87f3a26ed063f40bbe7a561aa55bea67d8e1fc042648cf3b3232a53ddb3960e7aa98d965c547d96820e3b14a9448224e0b8ea247fbeb1047059ac7098c982f6eef4577ba5a2e152364222d48e3756a4103980ff34364a111a74f8dda6d45cccaf5ee5fb9fbd5d7a0538cf4c61a41a177021ddf39501c8e247326e15145bbbbf32db785de6256a7d5f96083a25197ad2d91817b8d3ff0a8f0f642b07f7495c40c1bc8481e6a14e6a5883ef89d3edac71b6a22656cb374c21a137deb16a0aaf3149d4bccb6b382857875ef7e4153ae9da4ed43d92849cef21c9795855d278dfdd3d5fd21788c81d368f15d9c4b25b8af95aecfda15bfc93ff51d33fec73f4145b8acb4afd8f64ef26af129b1f0e87de59fc6c8450c51d0a2e5923d09987443a4552bb6df5e4f322b84088b69215e20c077362acf81dbf5a23363c1273bd1e58cf52dd0bec0717e484408d79af4a9cb7c52f9b72c71a83b13b04e2943aaf05915ec4122e64a75aa81da9d55950af459623cd076e87c47404d9d614d2cf0d48d54f1309deb6f26942cc73b503053ce40906adadf0dccdaf28f9278c8bab96ab84c612ad796d78e1ef5a15b964603f3dee1483cdb77bf7d0f055b266746575b3632d530f462fff9babfb81fd892ab6f2cc53b3ad7a20feb125eed8c3db82ad00", "6a65ac", 1, -1061482661, 1537743641, "2e4f560cf98ccf7c43eeee44bc344fcdd7c1d289acc005cab1d125b07d826565"], + ["9ae98a6c03dea91e2ef43b0937ad82e64e290a4a06c5229760eed9007dbfec0aac0c9f39c600000000015149b455dab2cd566ce4c2cb8ec3cc1882e267a1b13b9e8a744892c165de3d8252856401e0030000000863636a636a53005318649383b26efe9f7813ba2fb735cac31d56de146c15b780856b42f3f67b4df332321afa0000000002ac52a2e7817e04aa16f00100000000066363535251ac898c750400000000036563656745ec020000000005ac00656a00ae0f1c01000000000963655363ac630063536f85994f00", "ac515251650051", 2, -1397139905, 0, "1284d0a0342cc8612e18a30b9ff7975fe52c94237da69b2841c050165aa1c951"], + ["030000807082c40302026e5f09ceed7f2bbe34c4efed827fc9cfec2871384b3832f59d95b883284c6b0000000009656a65636a656351acffffffff20ec34899e3d864e686d27b757403782243662233872ea919f2a390ecc83e81f03000000056a63005352ffffffff04831626020000000004ac536565dd3fb605000000000863536a53520063ac6a541e05000000000865acac51526a6353ebd319030000000003ac006362f147cd000000000130ef58050000000000000000000000009c9f5aeafff0b1475220e2b170da4fc368c3e5d85d701640fd7e400cb79cc0b172295e8c6d9ca7dd6d05dab3e72c9b285e892e7cfe6e438946c2bf9628cd2a6ec363491b8ec14549d45eedc65b4c8d6598bc49565742aa96945f863c22bf75eb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005aa714aea8e1d4ba757e9d19992ea97eed8da209eb93123dcf0be9f1d797464bbfc5943a2a4130c58a34a0aaac3e88cf0e4c6c16f7988f6fb1da541804d4053f36ce9b6883660556a115544d66cc6fd8629095e915ec7fd9c78d6e7bfafc2a2cb7bc2accca569bf1a343ddbd9979997458ef544c180a780712895ee6c5ab8cb3031aa8e3aee28258dd69a98d2a88fe2dc5f88517d798feb0c5bdfc682783a38652032fedd70f7c0c1d39fca6dc6cdaf2c5c17d279afca6c355f9b1305aaa20fb39060a08556cd6f05bfae1c4a1a22e090f835cc117f192ae384055fa230ce12160299f1e2f1d60a4ba4000026850912d9c9557191f0260c0320d9b91ad5b7dc4baf207020f64ad348780ea5595edc8a9c48d0334bc8178eaca3599893f0eb81a4e6ca875030bd06b1f3216694c0f77e47cb35bf3b9cba59100b20a84bf4d5f1582b6101cb00324581c2d81ac09c40fce0c0c0ebd8d4481fb998ae752adc5661765319c7e612c0327d201b75cf75e5a78378c09a6c42344904751f76fb53bf44922bfa00846ad740303a816f44b963f61121a23409f830c3b9228e603af91e5020e6d037d628a4bc225b5a2bf480a53b40a85b0b56d7ed58582ecb16dd6ce029e9fbbaba47a0cb5c681cb1bfc87a19d80cc1b2626da51e8e9855c8eea7b3463e867ddb2e9aed1f278abe419dc69f127a2044d9d4fa20ab7fe8e9f469df8886d0496dd3aec8a25e73f66d2b7df36e05aab256f6070b8d120f66ab051ada8d5f210aec6487005995043016422cc2c4d7d08d7baea14ef40d4d9c6fd16d302ca83e89e3e746f9a95ab4abcc0da8b157b081e6f14dd65611c41a90513981fbc97c1e6d20a2910f58a63d45d8b280a6adb6ce7d98c62ebf4df4dcc30050d9a11cdd7569b8db8e155b5d783a891ebda96ea6d558c3982473e34c4f2934e7ecbe267dc173d9be787664cd2cc2a50d5f3404576f7c17e64c37f3036df7a4bfe28bbc2ca45b7086b3bf66f1675582627dde3484e1889eee598689597efbfa6aac0bc1cd22ca7e886e1985f873f2ce24cabb0ece867b39796656182c3f6f8a726f41b378b876a6b90934fb34c4cbb93ce29b3e86145aab2b8ebc26acd47631e9ab750a1562c64929769c622fa7df27f99cde24039f77cac14be0f0404cfad116d1c90d266e3f4718e05d585d9bb056cf5f380e0ee7e5599dcc0c86670288f2c6f42643116b8498d2d0924691881634ee853ee557c6074f99c6358b236d0db1cb2ec0aa8ff6043e41cd9bab468529ac3642f27a8507332fd7043516b8ed7cd1e0983c69e198dfef5f1456a100c6b6dd5feacd601bf0fe52cc4afc9cae8252d5dd86bcf02562b779b98a879d9166d30958c7a7cc69cff05390236de0f3d95b97abf35c0859fe49bd4977920ebe52459ddcdbe13b399a36f2e0e4a93e052b63bee9d61672afc8997902a3403380b5d00f70cee7ea42e2ee093d853f9dbf2d88bf6c5b2ca8c6d04cb9ad7e9c8940426ecfcd1d6293acb77dff2cbb995380c156c813cf35b9ea40a9b3f3c19a3b7ab669d27820ab2aa760f19aa7cc749765f14682aab1dcfea7a47afb024c6dbeb52670ff0d903be58bd0b451e801bb7352d8c2998484201a1effa111a8e14a9c5e5a7c9d618df7f0b50f0ded3165de11c6554def74ea0b110603f5e82327378d510905324cb98fb6678e70d6b4e54d04001a713a570fc57ab8d0a615812658ac0aeae1af95f32f3eada9132575b829d9efa073355f799c824fda54b671fdbd20dd433a57330383a924ee091953e0ed47a1cc3e27ecabd6e69e75ce51a26c0b4583434249a95c1a2a92561da03be3ce4237547e7368f502e371ab834eed7ad973be33011ed67bd3d9d9cfb42ddaaab65210fd45e32aa842be79b4baab01f751591e32494e2d735ef2291d433a5426cc668a32f1aeec74ad89345b8a4ddf3a3734780cfc0299447d09ad26c9fc1d31f476a893aeee6ac1e7f807fbe27b1eea24ae79649e3d28878c80106e0d13098243d57943e0c2840fc7e67b68a3fec5fb370daaaec7b6fbc138515b1c68859c13338c1ef7f8d60171d17eba6caa7dd4103bb5f63613874a0fe52b5e63caa71a8eb31d5829776cf92a51d5d2c010b8cf33698c0965c1468119ec60c0a895577d779793973fb1124850c2bc7b3631f388d29eb031928ac6012c9e5478bc8b9f7baca3043fad3fda70bd438585bb83f1316c1416c857b4fd52f96b2f2a04146a302a254bd77e906d8fad3da23b7e6ea3b1a28e64aa6433b0c24f97d682266dc42e910798efb5728becb50f0621a517ac801cd64013e9008a0d885c991f0058dc760f7d5c68551813509a88e689956e66cedbeb4607583720b85b3964d8a5533bc6bf0a54e258e8373d0c08820eb19b50b4f7deb5e448ce1b964073cd721675254dd8bde556ca18305", "526aac636a", 1, -2137542490, 0, "98b91cb008c90fb041add9e7bb8e49156662dc5fcb23f7ae2e5c381058b371c7"], + ["2f9eaa5202bb4ea03c5c0ae0286d7a957314912db050e3789b19f61206a413926425d2ae0a00000000085253656365655163ffffffff088a63ade88a071c0120548d8407db0116fcc7748a648ee1ba07747fdd902230030000000765526553006300ffffffff02005b96040000000007630053525251ac0af5320400000000035165630000000001000000000000000064bb150400000000a7cf8e8aa69ef68d3a788451fafbad0eb6601dc1dd6b58331ca371c5bb38f7a6c6133cc1496526c0c2259118ff3dd8572c4430567e815849ead0bfcfd1aa0a5fab2d752173e7636d57e6193c0a00e426eab0ae46991f403c4fa5f35acd82182d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ba2c87c009ffb1de0d3fbe1b13340ccb4527d6a6d8b8acb815d67d89fab0365a455e35d072a159c60bbc60024704bc20a47d4060de350d2f3050539237d094a5bfadc875fbc5931c7a4456ca621c115c8a0d2e98d138dadbbf9cae1d373ae6256287a3ce9b592cb8f95c380950408db91ee763597b9ddf96d6063c94408fd900021c68eb08b6f28a1270378de8a163c2ba416ad5e83796b4c2feed27433af35ad6030ce1b184f7bed5c2ddab0b4433427ec7ccd79ac5f712be3aff12b953116934320a01e7ca45264ee717dbe519deb9217df1fb3931f850e7152a1ac6792f3e57db92c6f8f1a6d77d397008cec4e5d361515261bc26c9d60bc4144f88277445ccf55e031306ffc5346c5bf2d17afe9d197110ce77243d676d975c1e37b5d54b82b722a2032d0d6f2b49d6d847f8b06e01004f193585d74400e9d9282dd095bafe7fb71b41020c83c217303d32aa708916637ac91217ee02d0cd2c1f9f7a1ece48b28889dbba0204ac91ac3653f7b26267dd5232d7b40d88fe67b7a1277dba917ca3af170e5442020c0f4acb660612956378eefa84c2cbd255abe7f465cb0811b7bd291096211b9e49a65f091b72e3d120c4735977c869a79fc98bacfaa3ce545581a5fed2c9cb9e97e67bc7bcf6a735c28b05c5ba1e0b5c5797639df86a325c6f1d3e1af8f2794210f2b6d02e7a4b12bad936f0b6c62e9e70c88dc8cf3a2935ab44e37d70610941360156c145adce4d791e50569570e2e1b524306761a4ab591be049071a0906d529b25f461cc136e881f1fda630f8eea025e3e1828d5a0db5940d2530f5bb01791dbf1882836e6f81c06ac03c8c23a3a994bcae6dce4dc553fb6700b0549332ba2eb057e15662af4070edcd9bfb2bf5072b8a9994edb32d4de7b7dcfd56bf6585a50f511f63ad4de4a2e2852a4229da91c0e8d23bb737b38af42489f53df67e6220cc792ca275b89c34270ee6a60dd9af03c3a28f29ed8bf3786e628995a9d425f406c3a5db62fce39146ced1b28fad6a3ed2d2ee206143b01a64114b53dfd7314abd4305bc6a20d7bef4ab3ad37408319a6697046e80a75000b0687789812743f3eb53e0348002e2d9add701751d11dbb520e96e479da14cd4400c97bfd888ec0806b74f521f827a958211a6726e08394b13eb8b6d3084a46f7959508f6d82ed6fab8e96b3edc66c249b5403fa0106ad27512db07068a1c0c827b43b472506b05d696bdae5d18b3e2c4dc09f50f5cf7f19008fe52055d8ee73766de6f37fa87101d57274b43411beadde884775a1dcf17e82e4a56f5e4885901656e5ea808f4c23366082f8caa6c5447f97d09e6fdcebb6d4299ab225cb93b44da3124a00393ff3219f832f033dc1f76a393db4d90f6955b4dca0490ba405b852df471a5ccbed8bb20816e4b4e5b4ddc726770ac922540f2aae8b18b51e4015a02d7f3c531e1772d9b0d368bcb819a67bad79b22e6e45d4be81035bd128019a243359908bd9b1c50dd894b29fad5efd3ee7e7b3048b1837e622486a0fc060869181c21c4af5abb89335c3f94aa103a449bda2e61a26bd37f19dae780d2a863fb9f85e5acc26c98a086d1764b848b5cdd01590d03b5c0e884601697e8971ba197ecfc805661b1cd1cd5feba76bfc0e08af2bd90873f07753cf3c46bff6ac390fb61bfcd406d2bb7d025218ba6bc4bf5ec0d8b90a1bf8a8f4b5d03fdb1b6b8e9b34ec9ef634d85e31fa1b9c030670d9ce254361d7482e882d8433fa1010caeae5a707e588fafb516c9c2da6ad647d19cc2da48944e19aec1c5c78215527143ddb00368fdecb9d9fb1ce0b3b8e65dbb0181f3f0058c13c8530ca635bb247debb319c7b318cb345c45fdcbaa7eb6a280fb22086cbcc9bc4db5e0725ccf389048c775cc71d966ac610cb7903859a9f7d538c7f97db1be866cddf598ad8a0500b9f8b863bdc12a27c8cffc952080d1a049d8190c9ae2d5fdf41e0c9117c361966e95743ccf40a29ffc2ed60283774e8969be5aa1a13e61a3825fd5dc2e95f751a0d866706344672d7c9a4cb08d0f3e339db28532a353756384bc865a6945891043feb055bbdfb6c5f80b6cd82ed867922d5057a9005be6649b14cab2ddc2a3c9f8eb62ba33e98d6278555a0092aecddd36b321eb428da3b4a8bc50f3df09094b923ac77746f758c5e2c5ffc308d1469d0c8e9d0d3ba4f461bef76e5a9ea94b3b69307636d5878c07e1db35e2a5dcbe6d0a7d2cfd6ac1048bef6c23acec04f7b461abbf178c2d38e0e0bf91ce791d6272add4df5d2df320bdec7a902f618c293559144ae2bc7b42978babcd7ce51024f841faca77ef316370ebb7312c1dbffb0858ee3765d6e1f55b30a5ddcf695e983fa88b77977f29ffd9ea3851b30135a9634459dc0b20708c0f629145252e4cac9a3b3f0771b37a9b22b584106", "516a636353006a6a", 0, 1823461283, 0, "8f8543db77a3e02a6532ff83d167c92cc41a53038addc15f51c2e0da3d8e476f"], + ["607d5461023be04301ce84d198585f995f1c515ca00d5a60c09381e9edf83e367b1bd743e6020000000553006351003458eb54c656df26b45fdbcbbe3b656128185035b4df240f153ba2a8efdae8b1c864fe1701000000025300d815dbb4049dcd0900000000000452acac002cd1200000000000016a75b0b900000000000251ac6bc1780400000000000ca3377a026b764b050000000000000000000000008ce102ccf3c8b01820f6bcc697b2d500cf9e82af568218c164dd89670519c7d0ac4d7b4611fc4044088e92cc5c98675f6980f05ab96a030f4b209283e28be5766bde96435ac2c0337d04ec6c1832fdfbda2537f5d3c0adc746ca6c29ef44c4f8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a5d87f4416d20a1d323164e6199b8a401777bfa118f107107ce1bcc1cdcbaf9b2a7a1150ad306a2f09bea4b13eef591f1e1399ab9e2a59a755ca0694672e2b53c6daec661d56247811d82e2720281cfc5f978b8cb0b2a490164c33097932cc9a0078e30fd5e2c611f490542c4ee720e3e25b0923db84f86c827190714d025dc0210ed8501a064ab726b9a2470c0877613d440eb378287abec0cc5269d7be4b188022d2714ff1a4de0550a28646ea343f0b319ca409546179fe1a4cf691ade980da00a03b1291a4061fb70dfadeb936f228b6bb19fc725c05ac902c6bc5cbf9e82f786dd86baa0ee8dde5e10aa12fd04eed988010654bd772a252def0542a3ed36d1af032c8687222f5959ec1c0136719fa1ca195ef5b860db7fc28f98ddf57f7af87d11031ae2b1d291371debcd6315cd221b191ef65873775d3bbafafe1d48eb4a8beb1f032207a924732b52303e2429dacff5065915a95c246bc633ed68f410568e82ea4c0227bf1149ee80730dcad6b9598639a88eaa603c9e7cae9e881cb1f8c0ac66c93e02071451ec7789baab8cef1fe933b0cef5870bca5fff07b1fe7a41bcde5c8536a2d7cc0b2def05b30330a282a40e3e06a5053be146209e33aa856a24ef767797bed15e09d60cb65345154ba3a4beaee39aebd0245d42cd14b8c6bb75ec805948a661e63e21a6529ac735021e3e074334a7f45040d2ed9444d7deaeb4ab935f24861634c1382de1f2763400b425c870083894ee5b03cbb0072f912a0d4e0bda35d103b00fffb35c44653d41d873832c9acd34fb34c8f1c22f18a7e42e4ce8ff94057255a244dbffae10c6f5f1297fa7958c4a122a3954c412e1ee084989d8037c0c877852a9dc3bfecb7695f773d20c970cba4369b3186a266c30b9f1545ac146d9aec474c285a7f9f9d50be0a7e8c9e63aebe293b3b4cb664c42052aa98fa2ffa65474c7da2f2c9b980014255510755df47e4a88e5647388524216b9704aef71cd6d003d3d0f4bdba212117e5ac2be7a66f8c8d2bbc9cd4c29a06885add55d90dbd89ddd21fd42824ac6c72e4ef5f1ac0740893a2cd350df67f5c120ac4f86cbaae86879241202658b5e63b95ef7baab97ca1b135b554a69ffc775ccb49842dff12e0e2f4d6b7e77fca503aeca6021bce474ab86ab398275a5c7c9da8cc87d401a1284a58f8cbf81c504d48a62f4c289ba0ec143b51e870e172a0497e8d4397e6a0211ed26cfa1c4b4154b7071e4fc2c5fdf16727093bd30fb3f9f12fcc8b0b12be3095ac9fbc1aef316b332efd92df5ff985558d9e5d1958d052a2606195ea6997d3b06dd156a9a03c1591e8dd4fcad7f8c708523f2ddcdd2721a10b12372c117d2cb74790e39f7c9a97ccbf95e9128a3659b56ea8edc3fd29669160b5f6bddb958b4bc08bb82a28569dcd2d128926ea205cde1371d3f42e1a3bfad4aa741755e6f5dd444baf809c3a660915ad474593685f0c2c9fad3a7327b0a519286349aba2a3abd1c4f8de10d522a2949d01eb8c3b14b938f1cb7d1de4e6475e233277028fcf17937503e2c31a00deb74621f7e504dff152c7147b6687bb72925f0adcc79c852b7559f451911fb95c01d95266f40e67cfc3a48cd67211c3977d575ea2dd8d475bfe9d8d5f119b62964f86a9501912f5fd5980af4c672eb8a4967b4698708a3d33af4207195b25cf4d8562266b3fd280e890bc47f4c9f8bb2cea18cf1ff33239e8cff5889dbe1da52534a5eb295b3ffec1b73c6ac48d3a04e9b6898d8cd96f2af88656a5cbbca78f6cc28c55942505755387012980c003ef7eabfe2f2c6cd239c5c2b09a050c37d08697165169fe3263b7ccc262f2bab3b54e5c768c3bd2dc87e5c4b1015490bc8f1ad54807ba8d18913b19919900fdda1e0334c64b005d58f080b4f4f2b25b52109bff7b6f2dfffa237d785560737c2b5696b44fbd4d541666fdab71a2d2648e4654901cb37bc6608b4944ab7fc994323e7f2e6cb36207e5c9396dc89c81654a6473eaf8f90aa81396783fa40d7e3be3011baa2f052c0fd7df53d1cb13155f7b70fcb69c63a324eba49106cc94e4dc145d5f5eae47ff4745ccabd8f829264334302acf19d4ace5e952ad6d0f5ade73502ccbb378f15dd088a4027b98b0b3f93ecf9db5cd17cdc8c8c7323022741d2813cdf3d4e74289d0a86fa009070906edb1cd71af9736b33f601a038d6702078a634d160cf1fbca7aee30f386925495af7c13241b350aaa4ea3337f6d2fcafa2a272dfbdfba211613626349bea01337fe9a89f0148eb4e18d5b0271a94da03000000000000000000000000b62daf8ba0e2410489722bbb81e74a8061faa38e1cdd403b44ee3c7ff3a90ab997eee9212206aa9622976f6e6f3ba19dd1b0dc9acc66debe5478e1b648933851914863598260fca2b28b2acfda99173523f03d97bf650c507d0d8fc0d03ffe680000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036bcc95da2981e883a4fb266d7237d6f5df1361335588aa819d07ce3ed868103ff247ccf58aa0782c4cb201e4a08aed3075ae60d5cbd7ab0460b353e7c55a546dda57b766bfa8f67b5421a7e9881699d2f1cec8f9d4218bb51c7bd3004e2ce602d17b13f822d0f3ac52d4abce58432541fb52646f0c4fb15655886cb6769c32a03223b4375c5098c8a893df8cf97e6f5b26c7de1445ea956a6e0eef849a86d591f0315ccf5b79259a2e48a3dd118df26fa10abaeadba50f0400c1be9443475bfffef0a066a662e379dbee0cbf6c9ad96f337f701b6aacca3b239f441a6ad992ddcbe454debe55df6c94911e2f7437cfb5d080f9822612a9c60b9b164e3df0b73d7f4400321d627c567fde0a21d882731e724a6e995fd4f72a8373cabc903a104a28f0d070206a5ad3b6f9f5305f42e1fb0d47a4bd254b1865782914ce594ddfcc0522b53c402057397e5dc42db59399728740295c05414ee9a52541224a06fd81018cf66488f0214c98c8d4c0d30cbe11103922098568b75085c053ac6bcf6a6f181e498b77f400303a6bf5a34a42cad1419606fd3fdc533bafd38b113589d99ea0490c7bff58787ba2e2ac1a3ac44858017255a2ee76c79d704040b95a8a159ac8662393ee4bdd377998c0ab36c9376ea2f04bb2a989a394364411998fc657e85379d0e59c79b141b2f4967bd2055338ffaeb87b2b803bcdec98dd506c2aa6012a42264b11c15afcfc608d42b9233a2dc60ae693e66c5fc69b7683d2ad6c0a0361346e32d0bf4380ed1fd96ff2a8dda3d9fd3d23fe174767189a464e4e3c633f5aaf14b5beb1599a5a239da575765868a351831530042cd5e17a2f8a31eedee2664b56dcbb0c9d62954a293dd53fdce54c82977d37d1c344230cd3d37b0f0eced2092fa484cc92b7df78ad501074b83e2aa537507ea4093bb3aa6af11d97395a95657920e0cfbb3ca6d61e00f779c502b47e002ccf46262ba3e047c7b70c24e7825c4d48adb2340e35f3365fc9148d86baccd8778a7d3b723dfde4a07dc3740c41929aaad92ab635afa2ac9af0a9ccced5b40f454a145df78ae0c7c24c2473704d26cb2f84f69b3c60e95760cbf695c8dec5e7c4ed31a5c678ae0477bfcf05371a57dea517e023efa5980178be24560ed3ba39c455f5ab71a5cf82a09295bf616c739567d9a7f23017c4d2ae63900c4a4ee1707b86818bb83786acb6354240015e0562f529eff84bef9b011466edc8a6adaa5508cfc6fcd68df5c5979066b29ac5cd002842052c8c078d8e17d778547353e072a8e59fbbec186806fc5e99236b7b4ae90f244758040368e2d351b3a9dbaace7b390eecdcdc4931f7f01895c1d30010eef5aec9010b5849ba81bfc528a895e5542aa19dc8f0a3574d235ae045cf586016ed2da5c8f0a6f83d416e3299c8130e5b46a748879e8e06128e27b8ee3c1ef18e1438e18123516e6354ed18958cbf662db7e82b5b0267241ad6d2d5e074b6ad4c932a21afc8de494d5676a5a458509ddce9adfe555a149909a9bab71afd5a00094ea0d6d3c19c5b68795424cd6191498ae121716e80d0787afb895795f949104a1d16ada31a252534979a48781f7492674284993a351a00e4481ec26d7e03e955386e7de5a6eb31416457ffab5be4e5b25ad0d649aa63184359d9841ddaf9db4faa4c1574b3e258d9d22666736da6570d482eefe33a54fcdea6cc66a987f2c4c8abe5ab531d6c9ca139a630d59d15a2b45b40094b5acf67c629ba4bbd72d2fdb3acaa180f52dc8db58927c66c4fd0677e8ad97276b251b8caece38c9d4a6bd40dd7569aca5c09c7286b51b4efc41ab40b5ef6c5fa9b65d63e1da655528aa01fcb89cbc5a46a9cd0b6f26968aa84ed68ffff18f9b2e93333c905927b0eb201638c5405ab3b553608e7fcbc245b45157363f0cfad4ef1a97575175bf5f93b9fe217751895f0ab8b21251ad6190db5e4ef8d9a3536d3dc3a0ed1c31c9a292c2ebbbb55d3f9fb213cb1ffab8e2d8bf59be46b68982f8990c0a23585ccbbb57452ee4eb7021d9933a7ba8c259643c50c6c93a2a278485fa9e50ed6293c04773ad119c19399abe062969ed1dd3887871c7639919027e08394e08bbba6523422a763401825ccf66ad38c85a2f084a1b4b55bc0f57e6799d330327f239bf94a7408588b4bd2c1ddc49840a1044f63edf1768f4c14506043c4df36679d98e6ce637d1fe31f5a73b82aba33cd790e154e01809b5af0f57b2ea8be5a10099667516b6f1272277e7cb1b90bc685e5cace6e7f02dae65587e7f10d77a63ac743dd1137318c5e37a4cc8c5d3ea06aae43a7085bc995746aaa14ca57f987b55a77da9f1db365c44b27d8c4dcfccae3f11d5eb69d619fbf44ffe5eac130d60728575afd65988dc907fd953770407d97c0648f3c089d5ddc2c1e3ea795dbe05", "63000063516a5165", 1, 353261683, 0, "c6708f7348ea020fb1df9965b191a82dca940b26756989c29ba374650db26fee"], + ["a5928e0502e98874d0a436eedbfe91134f46b379fa03dabe573981c2c6b86518ad8b433305000000000752005200636a00e1b63fae2b728584ac3141cd0f1b52b04e8afb1652fb1f67b0508b6e334e9fc0ac09e0f9030000000863006a6500636565ffffffff036c51e201000000000400ac6a6aa1dab0050000000008ac656565ac516352e853e605000000000465656aac80167ff7020000000000000000b7d28a0300000000c5a7443181a908501934251887fd65687aa94a5881d006f0266f823cdb3b84fb2bb90a32ffe30bd822e365338dc9265b64432fd33d81919d0594296d419b462f2176cc39f9409b4460ccaec89f7c56862db2b8ef8d989bc6fe242d4c063d81900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043a8349a97f8cc28307a09dec69e62a7bfe411a6a9eff06bb37494877da6d240d4c8dbce591d99a6f6467896366c31698ac79972a908236cfffb3fda4fc54e1e61111b47874fe58dc012909d1079a28be703efaca6cd63646fb0a93ff824b63b302ea797e3a5772be874ecab261ee7225a28551919a75e5b5357143a3c32462f02155f1651c1d3b9c3a5fa9468c5ccaa1d7ddec0f7871081e1978cb57e6645441f020a89be40619bf6d0c7b4e45f1bb56a7b570599081379bd7553b43611f1d7e2e40b091fcdc084980ab958e94daf03ed06462ee86122dc8a791798c7ba311ff654900db7a5e6bab0cc6a6b340046fe8c2f2c30b09ceff196ed68fda7d0ffb47b6ccf0329e7ff8b0231743371f76c9d5d45cb3aa22234f83e890d7153bca6b41b0b9faf020a265e43e2e2179069fe5c62cd8b4961c7068a1e35aa725e635c3c73295c2fa80301ce93acd29882474c24e14d064feaa55d0ee12ad1dcd9ba56813093aa77ca2d021b64a3ec7cdf57bf1ee4c81ac7e248c8b966e4e41523ae6a4f00c03ff515689c022a5b3b80879d5874d676117b1dc88817a30cf2b8e8f06af13cb28a3a7f8e469320f140d51657c36989f1575ce4b8781f24d87d285dd168b858c23a3235308837466c485011b96547b057d4ff9673040d467e0f52cc2410bb4b0a465df4f27b7af9482bc7144f6a365f7b9fa6184dff92e64cf04ab99b0eb237d289895be3a0d3edd35b8bcec648400e9502e54441a3db860f759788d22591d5251a1e938098a931abcab52604b784689883825cca3f5e8b8e8059fb0be613d9a267b4ced6a0b24482852d1578b9921d70997e10dcf9042dab7d8df36da1d30101d824570e86bf90825c8d8dc281340f5c3a5df5bfaf6e9c99745546d8590be9752724d2ecf4d5ce7d779bdc7b839f7d224daef404f2210f5705605f02c30fe61acaeff639cfa7da0c49884e05fc3853c519c08836f743ebcd66e9fc3ff138b04949147d129502cd08a0d459aa97d7a6087e30c2def2d3fd62bfff8fd77d5b959dc8209529c2c211f8ebe2d85cfc27dffe3caf4d1485e36828a9f260d62af2b4d9d3d72182abe632d99d4b67c870ef2bd431625607d46dd463410a6c76c04b2b88f1576013b199547fac4ea318d7436e03f7c2debc28c15a131e7d61e7971901ce9946295008eda972d581b2a17434107a5c9ac07ed36b75ead9eea4c05849fed11bb54fb35c186dca1f5ac8327697cb99fbdbd4800cfb5b2e1aca5adf75dd02f70794da684667b4a29bb372a8a51eb8e721455246a0dcccd76a40a42bd961e3d075f7c405d1328af70e0c7aacbd35858057594291bab5c87cceff84573b9f707683b275f6f47281cd68dea1491213fca8b7abe207a8b6679f254b94ca5294c58a57bf36a82bfd60878ed0b1bbd3796c0ec6f05205cbef21e0eb2e4b5a79ed3bf8c32057d57890811f0794d964e4ae0dc392979ce3f757795874903e583dfcc27a4a115d66aa83805c73b0127b882ff133cac17747b4d012a2f04aa698d5f34e0d5df7f7a30ef9a3254e551d8886385c98c1bd42641e5b6845f92544a2388782e58fd19787ccb8f051623ca33f71d72f21932554f5625be0d6b0856c7b2f430bced37daf223d9852b9fed5c9ede655044c1578fa41da0ec4db837b991ac98a27cd2e0621de02cc77bd4737ad75d8d209c53c1c644922a0536cd5675a17ee6fd407e503f4642e50e6baa2b1755562b7456398c21a5e6998ef8ea409964219e810cc0e76eb1b63a17ac500bfb12cc52bc4b9237975b124bc4d1a80678b9213481a48dbef6552ede018b9576ff806bf575707301ecfa4a831b09065c69a89652a162f9a83f38ff5db6a08a2bc7df3021da5781a1706a266fb11cd54dd22081c8a8ed0981f2a167a9ed1db741339675c4f858bde93f588dd1defee4bbd8047a5efaab7c8247f0d2bc08e208668044e54d75236a38a236b31c868bb55688872353e6c43a83423481326867b22780cfcfa5b962dee87f7c7166e05535867378717808e651f8229b2aceef3e26ea78b327516f11735912d6e9fe4493116ae91e01d0bdc1bc20d9044e1085402575f1e1642a3ae39895e6a12b923a77e237a5a99d82c27c042b6ab60adc3d6a062bbdc10a9b5f5d53e42fbbad2ee7bd70c245c397d4e0c27d53fbd3b68a511e3540b6492a9a42a5dabe049b197ab0b587361ae5453a22f344ee40bfaa68f555a1d1be84b0310a6a51a89e6f9182012a179e3dfe2e34f9fc0491544ff4bcebd695752c3d7f5218ecdb94e2e641dad2e2530ad9a050000000000000000000000005922be6635611545f2b34c29c9b53d64e2962f3d6ae3dfb82e0305e5c63a96b727138ab7cbbe1e5d75fa0a51843bbae2cb07893de10b45b03b463c3682d930ed69d98c91aab6d85888205532404d90136a1101865c1b3581086c34d86ae9018300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b3b12c9b630d16516384738c5fa1b1df2e6d508ecf6e05509a00d442de2d2172b6eb4e44556cc26609d90ba23293804e6de426ce4134b1540587a2c79360e4eb056dce4a0424c7c981f8873511cad08834132368309b5f1e4c58f63289a4d54638c03b70f368bb8cb8108d15747a8b38cf795b339f85882074b93cc3d943df90020072336aad3c8eb527c7c5bb040c7718f9da1cd04e653d57b024504c9bf1450703289c67b4170d885af39e1bfcb4d245ee34a4f853a7154fd4b9f238c98e0c68220a023712f867de3d7dbf2b716d0f1973e3df2b85daa36ac54bb0ff4fd42be1fa533f706f517547a82a5c4e0584b5b85628427299fc000a471f98a0cc8ca51e7a8b022d6fde23bd8a96e2a495fd6646dfc716dcaa370bbd8caa99f770fd96ad858655032a355e9c365998f92ab5c29e6c2d4ba460b8b0f3b8680ac294bf77d1ee983c0902269e1cf246acbde4628f126462042ef52ed04c5228830b4dda2b9f5708b0e31d031bc3f1719ca256ee7ae8bdd7dfba5468e0806e087c9ab1aa8a0a84bd8da8ba5e0210d4df9ccba9a1eaec377b1af59ce528b267c51a6768d3de2c549d8547d96e4f98d742227c0ddfc41d1ad0d257c59516d04826d1d12d430a6210a1c432f8d95ca7cecbf7e532621b889186aa91a871057cfb81a8cf77f1d6fa978b64faedd051ed4c8a6bff87218fe4404451719f3305f21418a47193c2eb4abe45c4ba9c9f92e60b09a5428d3912c767ad0c2967c3c9d94c1c7ba7103bf4bbb381d822f301fa9b0bcfe8ffbcc211352443ccdf2b18ef1f4a8b19b0300fd86c26e3266f217bf46c437b78b77b23752e7aafb90523dfa3b8a1d68f279a6a5b600b270a81dafaf4b03edb7657ee98166bfd2b0c347bd9bc28139d820413847cc21924c457a5e9725ed6245cd012ef2c19fcacf9b90958bec4bdfb3664820f35deb108a109f3acbf9f303628693fb8728dbd6e47aeb54f47de81b9a12d6433200deffdfa657dafa799c288e9110850150379347124fcea65f8bdd5ee06c438560bd2082fbc18cf1f0875ceca05f026aa84b45e4e55c97d671e4c60335e2b1d69dd532bcbbbcf06f3552791ec39151536e7bb5fe5514feb572181bb2200571f1f9f39078c6a393b02c628f2291e14ffd74664db3ce9a735434e4d00b254b104839bda260a94f29b4730c9c6579d1acff7d72f17277bdbbb8ae4e413caca99f0b08a5dfdace64c8f22ae5a4e244022bd231f29043f6f6159604a4c8d9600f275df51adfd4d385d02463ac35d5bb16a8c0fec297a0a009f37dd9be603ce2ff0469b6c4a407df7217425603da6300e9f82c9a73fd54e6f6979571ff0f23ee9b4d62f91d4973ced8f1cd99a7200e5a11bff50828a2881a5b7e4e9572324b13236d14bbb2459b2433fdf20af9bbe89d27b4a2acd76778b384c90a2540d7154d2a5b7301362b0958a3af6ffb70377016b488f043bda68fa6bed66e06ff991a9577fde36bde497eda07cb424e505ea1b52c1f76c76589a5945594bbddb417d56fab741856dbbf4088950505dd1effce067d9dbd001dd8de66a8dae62587428349b3fdbd87be9e37fd082395e5f3d27757fb9b0ac72bcc4b853cf54301aa178df0e8bf59950917f011d4b8e788f737acfb84e0886fc1fea1cb29ed6b89e9a550a1194b8b72312f5f9ee71f0eb2fb3b425158711669e264972ca89924e0efe5c289d64b91760b25cc6d8a5063cec34f40bce392da625b862e1b32a75b1c1011b2280d18ad11abfccce6f8a385b8739f947c7c62e125016a7cc2f4aab873afe622c63e79d1f205e6e47d0b8a2c83e3c5f802c6329c98545cc7bfd9f002756dfeee01853cafeb40f8adf9febba7ddda24ce63c335c5dc9fb7c13d0449b29189a98899a680b8de29db88ae1a2c65c46337d0b16006e2fef791b6351de064551da5775c6e179279bbede8041ab13d02bb234be9b9f84b010a55c95b4f32dcd2b1582634a54ceac14828e4b2aca481b1c7f0485f4ae08f9f34f9014c4ad6fe485e99efbb08d79344dfaa7634f887876916ab15f7d3334249646c2c75d24574246338017ab7d52041b1928c42cffd01bcc961d3b20e5e7fd01b96e35e6b9a26cd136f2271ffb18fc00abd058a26cd2fda75faf38151c33d3e047e28e5da1d35c29f10625a7dd878e3a12aaed6652c39eb2a0d335347b6befef7d39a180bfb9702a3b8b4391cbad46bdd7914628e3ca3cf194c24631c6ff05d928f8cd490e1d52b44ed01a97c201c1fd4801b9878528f7940c4c0dea258974bc06b87193a6a622de33fbdf6b44aaa710e959ea1a36b55a235e5184b81c87c0dc0c3776b0ad62aad255118e4f81f85b7b668712688b255239b6de5ee2751b43c327c2aa7de8b2938e78efd819ac5be3ba7b71aff524bc480c17e018cc8bf1015edd56dc23681c45000c", "", 0, 1217284831, 1537743641, "5f2e98eb044ced84c44d317aad7b99a446585449838ef73d052e36259848f826"], + ["030000807082c40303cf61ae91cb4b1203097eced4fed61becc080308bbe99ea15b8adbd5832189a470000000009526352005351516565c0c970c72b64cadf0c9f817e70888d19f7196754de18c01d75ff996ea859835327f99c3f00000000065153acac6aacffffffffe29f9e19af3a5fb29c714940ff84691ec75070b671b41c13ea628b2f1428f304000000000500ac6a6a53ffffffff02c997fd0000000000036a006ae8e10d050000000008530000ac51acac65f42a21ef0000000000", "525265", 1, 1436171982, 1537743641, "4826c3b3ee9741ba34ed3c849113d184cb592da8f04f735021160f4887ca8a11"], + ["030000807082c40303673df78e5cb0d6c91c81181fee3928f9c29b86863e848198158ef43dc4e466f203000000036a0065d0b7f754cbdd2e48d2e9516bdc3fa0618e8c2bdc61b0ac6ea360c2843713001790aec6010200000000ffffffffcb6735c54fe58065e9e24e63c853181ba28ad81c8a34eb3390b059f07464e5ec03000000075253ac53000053ffffffff0455c51c0000000000016a54938a03000000000352ac63a80db60000000000040053ac53ef14f001000000000000000000ab6039b702db79a602000000000000000000000000703a69bc3b98a1a9198a7d443686304a922ded01e93513e4b1d8452615f7367bcb99dc955dee43a8db4ecc8e4a68390157f9713c21266113cadff76636b68826fa5f4cedeb38bf59e4827727f56f3da30d0fe09084039e2759c367820a09a953000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006510ee13c42b440d5ff75245230cc8ce624038ff5582f8529ba7cf609dac169e96879fea7ae94b97c37b0b9d8525a0042d33e6e0d6c4295dfa9af5ed5eec391d39b86e9ed194f29b53b4020dec2db362fd2a4ca64e5880335c7d88f9ab63a7af7290db1602053c83de1005059b30e9779f365ea44251f1abac001998ba50613c022368445e481f45ddf223662ee5118b6fda429d0571ad2d4b4150cfffcc96afee03001bba152a1d7544393436595157a8d4a8fecd68024fd5065ae6224acb8afed10a06d3170d4aa96cc0606352fb48b03eae81198c7bcdc328a08ea629e8a91d96c912472e80601360c41be58941ac9ddfd516c61642d5061e637e9c37f01d2a408f0301cbd7fef3b4c50ef5f769039ee4c848c25c76d0aaa435a1013d508e1155902c02137e1e75140ddb4c7744e3a9b0bf374e585c5c120cf404491b49680deba16c81030ad2de9b568462af4853b5c087a1e9fe846001c715a2c2bdd61c664c4d1f9ae702303c86e20bbd73be2a1f35735f55d16fb470ad22ce879c3c6455899c99600f83032f7b3245ab10d30f271337cea40716f6e3fe9e46045175b018cf72f5649f832e917a42b500dd7294aec22bbae1bffc8265808e12decfcd5e04131ba556fd3cc32e9b91ce6b18a67aee01afa9debd749c63d96acf8beca12bb5f44a5519041ed772fb47694ccdb1f515014113e80b5e651f737a3190a5fec013deee01965526979ac3d0e009d2f77f2ba2003d6547d16dc4e81045b318c9df4326847779877b693ee4457173575c370382b296d0a49a68805ae0f3c2879a8872dccc8f1165d71fe3c89989c3e6907157dc3b6f34ee91e03b1133d04107740cf1d3a8ffe8b0de1741edf94317de2be1b3d3991dfbd1db1321c21d9fa83eeac21ba02ac175504cd42b94aa15c6317b2c39a59d9c654006ee4bba4dfbe680ed29360ca8e8261a6913147d872ad6845f8dc1f9e4c4cba57b8caf31e128c73f92b19a0ed95d30ca1dc5fe83b95cc960e7254db5fc27080516dedcc57e70f7e2a8eae6bb6cb77de30aad32ff25491a16d996ce9db6ef3342f3c283c7c3303d640f9be07b2a78db37e2346a2d628d86692ca2d6b9f87a379c866724a2bdf53bc4aae9c8bd1fb0725155a1a2ef56868efbab6ec1be550f0e3775a17e8f6d97c70d2e291abc067ea9db783c2dbaf64f0bc6cde385a5efe6960fd970be88568f1e8721f1a4af50ccab1f87b214ef6e3e837bcebc53b48fda157e948850ce85bf48bb6c9ca6ece48b3a0aa695d02a0b2684a751614c8bcfed5492672a12eabecc39401b02c37119576d8cfbc2fa642cdc7eeb489cd1eaf595b6afb8d3ce5620e9c661b6fe748ff53bfdadc170bd651e5aa7961560efd4afd1099789b7acc86eb394811b8dfff373d4393ceac8fcbbc1c045303a6d6a6b440e9afc9a9cf9ab612d5e7569eb5a1faa41023d6bf7745de62d97c53311b5385e5c9fc0c09783774df497b85a1d723d11a2f3ee313dc1d24333f15b3c8ffa9e4f08776a6635fdcc0b8148731f1609b9d5335ca5327ba3996798f6091c5653d6eaa964193effcaf1cfbd4a92d0752f651df2808aa111526e4c6e66eea70259b201c965f0c70f91ec329ed2d102a534a8482d55eac26eb53af1e15e116f391682faa9adb367d1e373330f969293f1772cb0470206fa58b81f925760941752470549e5ceebff052ae07f8c6c286627334ee11ffb17b3a25076aca37c4f88cc3b8fe0333900f5bd97b1775c5ed3e8acad444d2c0967dc06fb49cffc655368b29e628cc93fb4f0c2cf0eeba6257c9897eb7d663fa5551bb8f9e9f510b63c86e3b8cfbefc01ae6599c511d7ed4709f8198433c48079dbebcb93d576720dd8f490626711ae10ba49c9e60373bb3581b28c36ba2e09187abc1795073bb503f0c4198f3356ee0d0eb323b3ddf90bdc9bb545e9ae2cca5bfecad58a0a5376550868662b2412f179721bd1a574839aa5e9c0b36c3788dfdb4f33c7852d5aa7e3d5f52c6a094d3558793f0c9b3ae84591fdce7044b5004e8b2705346c59bd6e4d11c6021965015f09df4976778128ef828421203c2681329beb3c4a7967a6fc67061dae40c97d090a1fb98733154a2eee37dde8cc5bbffc23871c0eb716496337a179d305533058db8141eb8ef4fac558258fd97a33029569329ea16303e056e5c0844ca02e521049b963d11dc54a8e4009d686fcd57a80902580bc90e1b5f5dea596bb1f524483447dab54563abc49a2efc6966cfb43cbe41d8fa1b66ab533b9ab852b38721dbd28187581cb450000000000000000e752f50200000000368b0fb46b8f282d2d12a00745be97df03a79d862bd5aef4d1d9f5f1d4f49a4da7503eb9e8282ff54eded3757d75c3a967785eb3329f0aa7308b7f333d4bc7cedc805588dd662c67c53bee2ddb16f24fe76e7632dcade2d5dfab2b3fb925b43e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000087f6bea3384f217b553fcac5efc33b55b837994183876264526e9e944050058ebd30bce5f58bfe71cc26e6b78c22f48fb61034ba0af6dc282fc53a0487082c8e1963fa413bc439f4eb3b6696f3b12cdbd7d5bb87ce8ec3d5c4be57436a718cd513fdda01f51658dba27b0d44d857d9d0fe6ca9ba863fc72951b4aaf5e3a3059c02217865d365b541c2828b235c4d211bc3229cd88368d0a738e7b2edc1ffffcd7a032eaf37643c2275554c39bbb7b133d556abd298c5e75e99e8345aebd0bb37f8960a0526bf6158e8111043e3a15a92078df6999bdccaefc477e5e9d18e0c1b51da1b27e59f563e3b34ce6fb3de18f89d98ce86452d4dbd71b1c53570c26952bb0a6d031f940efaf30a923783f7c761a965e90a8bb6f601b6f3abda960e1d0b98136ff9020f54e5ce117b5fd2d49ae7bfbd6f132d7c5665a61d83494db0937c207622ada4020f9163321c94f96412c008159e24bcc9b374bd427aa88ae7712b144826029c90020cd33d8ec7759f4ccebd8c7b93405c7fd4e049328a7e7f1f12030ae19deff0cf0201de62b43cc8bc7cee1e37ee2bfe779fc9129e43cbdd7613b1e8ec1cc12eb7d26537778d8ef1df58b34be3915cd89face57ac54f12d0f4fc5a62b6d0f1c3c1d58875f0c6fcfae446b69a7247768bc540dfeb179a1e37246ffe79a2ad8e9e9f0675860ca0420b93fe5ce2d073ada94bb1163d1ff7c45e86cfa8d0d07cc658bf2982b823537d5763685965e73ec747fd01e49c81136f8988e9c7764886dddec10c34098aacc9b06c93d74d861d89cd531707436cda7a97881026d0342cb0a67e2759d96078b7a29f76627e69aec9eb58a3be1552d875175f591f7a306b1922a0f0ead5ad8ca583253d5f0cd63feaa92a7c3fe1052460d4ca3b7e01add39cf607ff569c741f2dd95fb73c592529c034b06514bbff8d7d207b757b41ce2c03c45fa57b52a7cba94ff7f66ecc712063b97a0a5a160cb352008485ec575e305ed27fddd5eb5e398c857be63d5fed99a58078976d5356cf2d5b42e0535ee8edc9bc02c83237c5999a0eebcfd35703fb2d289fef28bfa5e524ea1d2d66d2823146ca72bafaf96155d9cafb9818e4e556c356e7a269862e15bac299057de93b58c708fea86c978ec8376b2a74f10f2971c928c2879fa1ffff59669ebb6f1710d40fddea8cf3ee49d01822117054c43f1446aa34f35c66b7946e2ab59adb892a79a651e1b4a89093e8d84639941e1dcbe0b799e4c599e530f9f24303a398c602c21439a268bfafccd5b93d060e4d153db9fe522034460a9963047a647cc44af94fb17e592edd053d3205285c165386ce2d0ae5859c6579f0748769ac4075774265d46589dff30f1cbbf4cb3a8d6f7d9f5b894817560fe59b54791143a7b9f0506ebd8487d15a44fdb3e346e64cf51e480354d408964dda3accd1bc42532470427c8281b56793b51bfbfbf5fd92c00b1b63422ba7b0999b23f9cd79b6c400046dc789f133329f547014985386f35e9fde151c0eb899fe3e423c68f9acb117e4e1566423d79cb5e11558814e1d92cf6e84199210841712547c24e48e6d27d0ac32a4f008dadff4a0da407a187cb9b346ed249a2b505a3a363c19b30e5785a10b0c74c585b03b2bee98ceda8dbfef6f247f1e7f803926ba49467d2cf293974e0b05ef0324e219694211b49c9959d4146f383b91dc696323a14cfa8c93bbcbd1c1a56ac5ef897381c74899529a261dfc6bfcdfef9ca62bfc7d23aa56233c8de019b3c1a522d1c236cd5bac4d49da0e2e19e42360d9aa1f86fcf957250e3700b103476b317a01817ab0743f072c5822971d5b80193d55efa1f902ad02e2b3bd0e96fb4b40aab0f47bd63f17355c02def861e7653fc5fc0895683b5516bc4caf0649d37b8fdcffa21a02eb340365d65913c96b4ec792552c1932d5cf5ffa3de36ac229b177e5d1b82c5352a84d675d9ffd3822497b6cf6cc54b2ed1a2601f0e4078f3c88cf83e682037eeca8863718f90366cfd8cb58ceaff0609b5b9f05c0990f4a0cd540be672bae6abee914202cb33c03c6b25b5087ac7878eecca4a90cbe93e2da120bd74cd0dad2549b8c601e70d3714386cbe916cc512950d05b9b923dd28e062047e9bd0bc42a9ca791cff00141c9911620851e11facbfb9ffec4b8c32a5091b12134993dcba55494f27db8dd8dfc0ee6e1bd5bb8e957ac11f0a7670086f2401f1b3b8e5ec25ff4b0b1c1bc17d372ddbc7fe03c8c94dbdaa48b0526aefb4668608e23050c97e4cf6772c32549e607abc071d914a88c97f5a538da2b91061eb386cf807732c2345473b8631614f849c79c9e9e14b05c8824af8000f7a60e247d72ff3e3fce46515c2f74bb1e765c37a4e7f03181547f6af6a6d0f96505d4629d4bb8df7713c3610accf3198a700bc3af151c9b6beaec00", "5252520053", 0, 1547416838, 0, "a38b854668c61d9057d942350a21099f88050b4ab3a5e2f6fbd975938ce9bfe7"], + ["030000807082c40302775d1c5b3d560ee9b45af3847059357e6b14bf42ad04c860642ea59c81f31ea903000000016a6c7056b9229730857636ec659a8ce40c45d79a62ba2a10d670a8195791a038aebfb6648100000000075363516a0053ac537b6874027e36d40200000000095200525153535151009dd4fd03000000000353000000000000a8bb61e9010000000000000000b869e10100000000e60e90577ce8bc27120b08cabd62a28e1a1333d4834b4e00d938403ffb5e8208a9fc19b012a5c64751ad429e5cda16a665d525caf58d284aedf96cca2c04e54c651b9e1a8170ad47d62c8ead30fe0dffeca13b08923ed126f85d6fde352f9b1e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083bee019c7d1bc0848641aaffd3736ac64bd94d1de2e9de7688af5efa58d02b40b7196e531ae97f8dcc8edab81ba653c4409cf979599f982049ffdf24f42159c7f6596d5656327fd89d3a177ef5f414cbc26b37c3d11a65613f7bbabdd047f1f30b9d96329699e398a5292ad9a4e1a7bc1af22c48dd9139348a8086f91818abf021263784d6d7e379837febc37a42048435872c32974e3e6e3a66adbac68a8e9390211e8c8ba7b4870b091148b841fb941158978ad55c8f896d0e29efaa68bc6965e0b02bd07cdfcedae810766cb2a20f75f1b0ab9e29325a96ad2b41bc7330dbd42816e75aa9fdb634b3eb04228997a1fea9702ca0d7dd1349f62ba4cbd4f4ccf1924030eb21b139a493739e14d1185a00e2c1bb4872a533ec52ea006a96d5b499e36da022c7c0041f7997d5cfbd0df383b46e768aa5b077279d5358dd647554c317c6fe70219225446a6979f3fb3f569de22f0e11ce9cbb9a4620156faa49d6e7ab0f04bd8031a150145edc6718a0d6943e4057d3767a9d4379054736ee0ea0d8981f2589b4603212ca719e98977847414b0cc3e52ea48b89afb7c3ede3bc11edd67a93a4478242f39dff1a4de513a13e79bf489afad716de0fbc58af99f1e95c8cec321ffc5251fd0c653d56f3249125ef33d50044c9073ac5bfaa0b5db9e1aff002d24768009aa10d11001eb62b28feda6bbd0494719925d05803f79ee02362045650755929147f1d21f93eafaba43506e24b633e48b47b904b4b3f68f2b0d63ef418d91909d906d823e6806795b0490299d5ceb8273744e51b175fb8a653c6452ef15d2bcfa1558852cb01ae7fdbbede231c52c7f675f3a187ec353bb4498608a61e3568dda9bf159b2a6b3ac74b40b0e7a4dcc8e60bd107fc026b37342b26a7eb4147350691643b228b01d5c170f8db3663949eacd6fd7ee7489e6383f2938fdb6d05378c814578a9e9c171f37f95956f63f9e4f7186eda0b0a55d4658250e93c90b18334200c101a2b7ed84cd87da150f8844f1a6eb55143f16313a2d4d84e5d1ce16d8c879dfcd5abd8c866dbaedfaae7d8067e812d75b54f84824352d0a8cf82044f1a71e83fb115286649ca4532b1ccd159e61b874a8849aaa78be3df2f01412030f741584ebae15ec20007539ac3d2e4d169a5aeb1c1cf8a06f4d5c0df984946832a32779c08a629f83af9f21494cba88ba73f36e1765b7df648bd2e87e2a37d4e3fd3f8ccd4f5caaf1c3f124a69f0f3b7e0327e37b64434a4dd0ad71b92dfd398d0660f1780d894b8890c7c687442d5b180ee8ba29ea505999862ab9d732a1b455ac76f89e1f1807e81e710fce6f8ca62380faf1be18e13ac460ed50dc84124563c2e658edb8bd99f3e574284afc14892d50be8074d8ee3df5158b4dd18161cd2eaae7db4d77f7ec344914b0b9880b5ced44bb5df14a40edbc7c18b95b690470d245e9c22a0709b90effeb693bc9dad83828c3481e8713dfc35b8923554494a4823782e8540e984aab63bfb4eddd9019dfeedfab322d5ab617bc405c8f2e39cfa02e59ae9cd333bb9b9228a0bd2da4cfec6f4a8b4a6c188febd6f6dc01578a5379f5300267ad2260e8efce7c53af31bb210784ef252e09d6cc75e9db356724d6d125e8f588e727128da5ece1f4186601f9e7d2625cb52fc53ea946274251583d51acff70d11ca8dc63fea015ee4283a1e4a5d96c96aa7cff8e0cb1b7884468c542ca065dd7f62de520aab68eba09e8325bc82962237a35686ff8b25552173aa2f9afa5535071c15523f1ad18bd5a61b66a4edbf3859c12bad375824acc840f876e5b193a0da0331ff6f341c4fe41b76534a1568955f9f3561009f79bbfa62cc4c246a867d5fcd0db59592f321fbcf7e5beabf0c8cbe259b2dab5f1973a6485ac710ecbe17cd31e266d3718c7497c48e247b5cdc17506fca51c061080144ece1948bcac6ade7f227c1cbd953a2ad5bbc83521863fc5ddc0fa7ba20b63db6d0145f3b0aca6b6d967807548cd1931bc3de7532eb8dc524130410c2da78ee827122536669f80294c468b2991f31b942d279d7235fd766568964c353e4baf8b31a1b298b77dc22363fd8cfc55b462f1952db6b8ab01a8bb31e75160f226683f798e8f556c8786920d559d9436027a146ca48f3d669d545bcebc0528ad8bbdb467554d0d560cf37f58fd90e7565dfb99dcff735f25370e3b7392de2d2b7002dc2fba2af3663befe823ca16676935e6bb4b8f03d30be8a60efdff903fb941158bf551c65111317da6689b78d6a600fbe96b78e9321bf87a0c968f1640a4824b772303051d6dd5afd596848ccaa7525a302a0f2e464e63805d6548c56a07051b0bf4eea6ab0e1d6787527a01b7c9187057e32b6d436193cdac3810dabbc3df89c43bb3b27a26776e21231ee5fd2c1e1447f0c22fddafff0c", "", 1, -1439560012, 1537743641, "84c986dd512183a8f7924d6ba4aeb4c9ea714f5801a9cfbfcb4b566f06fb7bbb"], + ["322184180230e968d0c5c27033bdb870c514f80b2139cd1d880b5fdc78f4cb31feebf835660300000007000052000053acffffffff563166be9fe55de8e245aa70c28f354e400f1e4b3703998c7c651c92ac1212b7010000000853ac516a005263ac7b4d9dc602c44324000000000003655352822403020000000002ac65cc20531703d79cc301000000000000000000000000f35ef86b1846b3526c54e5920b60811383086a08d4ac1df30559113fcfb1318f5069c4641a67ad144ce5942cb8f5ecd2d2d0666eb675ac4fbf55fadf0ff9c8722915ba5fd42dfe0ab03fe6bbd81481b640d4199ace7bc7625003fd38f8950447000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008e59cd2f521a46cbef0e4f492755338db85b86b2934f54c3ebd09712292b54bc35ace246cf4c57dcdef1c3475c08dc12f502657412adc15a1d8efbd84668b88e299234a414d5d82aa2be6bc70cb5783a9988a061bf82256cc08f1a937601620e902c948452c8b64d09d18c295b1a351e9bcafa6f0cc42c6f6f0e38077a6870820226ced571d49b0ed831ea3f6ab72b0cda74932255ab759053ec4a070be68562e8032d063cafbde24ba2cabfd82c9d6eb80e16c0aecf24cbaa2c9c0ff53ff766743f0a06d9a5a151fb881f932430f809144092bc64b5a470aa6c68bdb59cfcdf4868c9cbe474231ec18128d15c3d9cc34d5f8652af79e83f0e93d440991f659c238046030da69dc9b99df2e4c1ff7041caf624ca7566af6ef3f6633e24bfeb86faabb7b803281103911a1abd0a987a9961bc7c7f8fa081549480dad5f3fbdb897d29326b8a031f15b5cc0cdedcad0cf42bdaeeb6e174f38059e14561c5b417a502af40c4ba97031181d168d76ba241f4d1f36db6742461f6fcb02ed2e4a908bd8b06b3ebbf71a5030f94cab677b02c3f710998807961b26a0fc37a3c9e5d453cceea7a935fa8f16c150725983af09064a5ee12c483ea91703ee4534347b5513d63ad41b85c93c829f706746274761ff1d938ca6dff4ca5adc07f5da6f8ceb6da08ec0cf38ca9f0ba49643747a19d6e902062e68d089a0b7a440a9fcb197aac7571201eef824f58d9097cb5501f21db88231fd4aadda6f3571548063e9ed30ecb1b0e1478cbc4bd51ae2ab01ff6a90acf2ff9838b153dc5793c0409808a0ebf32a789a39acea5baef80f82f2c97ad22b1ed0153787be0221885f38678df06a2a95d0cd014189e49aeb71867b9da394714adcf542e8cfadb6e60966450c97741888f7c73cc08defc1fa8ea68d85a9f696976bcec6da3edb5fe741fd95a6cea69c212b32d1120df06beef05f1551b1b2677033e9bf8662abc76a6fc1b9c17202107c94e48ed4729ff105e66a2d5115281b596c53dc6dd65591bd9eba6be9638249afe2cfe9ecbee619788c57c35487630c3efe01d32f86aecac6e0f180312db89f4875172dfbc9741635cefaaef5e574ee21905cd1ba474d032ad35a6fb21b7c2585ae6a0f08d5c8ecd0009b7de2029fa508acfe974e249dcdc74a4d6d035fac5fa30175033204eba2672bcbb55e22e23563a9ec238799987add3bcd182bc444ae707139f4023dc8819586bae4c13fa09b6739705122bea7e5bba61210694f3b735cb972a1ea32d1efc80474e3fbd10c1e2d15d1a58324eb828b9974ccf4d7257822a8512f31623392fd31d1bc2099a72bf30b887794d9dfcbaca7f5a6b4dd881976db00b4fdca162af089f34226a7267b9acac01adae06b60b9fb60a00994717b6e0e23380cfa520417dd130072662cd5d1ed7613bcfc5939b9d57f79857310f3ab5466662975c9644f51e82177ffbff3c02c91bfa7bfedd9b0f38f155f385c5c64f506431acfd4ededb39a3446a46b310e2e764a39708dbdec2af36ec59dd9c77c642cbbda7a66d30a101059dfb9c2c0ffdd26d9b63adb621b629d66599811d6bf09f7ef669db69fa690cc87d79ce42c31549aa009843a09fd34dda8245b2d3ac3178e656789cb8c7ce9c1f730a4cd9aae643524e5884291d27464b69672dc385cf0f303ee603d2b65c6cdb3aacf5d18c130918671f5dd73fabc30cf314c5e8873bd608c03736c4dc44cee85f67f8e3540993bdc21d763f50e39977b9eb1c13daec6f3a5cbfb6c2b911360820819562157eda4440685095ad4c7576d763a41a2498954d42d239267f63cc50003d4109c2f5df9c5f227c4306e420a8996712cc917e788faef8b3056852b64f545f45ce248b62c09f605e9ccd1e84a3b3ffad8d4e0297b0064731323391d73c87de75efde69554014c4082bac1e3a217e22be5431866fc642c7fadf067dd83547a5d81d7e67c93889b6670d6aafdf7c7f17c705c48759583238446a11b61280fe5c16568acdb3446955cf0d9d58215d931c9f7f37569ce332b63cb635e759cd8bb44184859175cc1556d46c6210bf67c4d855284444a032ad4bddedf50007f41002aa5c58752d3f3c4ccda5747989d9c0a22838f5942ead8d57960b9bef20744926d13621b9599bd0062bf011c2da9be1fe3088952582abd634cf90fc6080b1e7a78295d01f20921009744948ed8364d4ea461545f1da1ca2ab4d10d1131275ede985d84feafb539639287fd571f32a01411ec03c8db1b38bdbd2480b20885b78c66df8c467646a17e1622318bdd900000000000000004f85270100000000b041d8e9f67ec8239df202385de8f605964382cb1f8b8fdd106afdbf8a7b7dd0c6106cb742c3d592e95dd84a8aa6b586cc1e8880ab3609ebd705b01a2ab007e41e6229ccbefc59bdf0945602ba0812f2ed362bc46919b871303c9e60a6f5314f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004f24fc75bc56766d7994a900b0b1ec45c14c98f979c15c452eb776efc019acfebd00fe2cdbc1f73c1c6d99cc2203907b5f3a17b923db68b1889da5ae96c033311f4c456ebfd5d4611e3c463f2124f46e16e4b84ef859a312f860dfdab6233bc2abf97ea0a1604ee910eeaf6eb36ec4967740f49d28de1029a5f16a3883df5049032d30e4bc7d222e5c22cbc7ceb6deb0e24de062233026f5bd433f581c1a3a2002021d5ef506dfc7a5c9a96b14ba3d21940b42a0a460db2bfeacc9e598c38b8995b90a05965db1d45fac6415506ef37dd999543f82cd4f0977fdab04c6c10c70e70cf17445c9f78b89d181700fd996eb4639ef606ce7960b3beb12cc71e31b980875ae02136f79eb5e6040b4c59a1f4fae42e12235df2d532aaa37487182ce4ea2ac7dec03151068588951695a36267165bb45f47f9e9725cc687b60a1cfbd2fba726da4890229ac5380eeaecd483de2f8e247b5702edb1f06fa4b9c1dde1b6f93c22c7c97030317b0f2ff220a172999c26130c1b077d2cf0ca1f6b4e8d0cc58e5272200cc9038032dc649cfb5149cb9c5c2969d97a7b5e3f560489db211dc676131628757291e699c89643b5ab3e98f0fadf1e4095794252bb454fc83e485c32fc6de2fbf9fe0fc2bb58889ba0b9d3235aa900f81de251699984c1b882468627534bd78a2f9f0175520e6f922445979e57b4d51316e5aeb3d682932bbc845ab8224ca872fd63f64087486b713d93c020070b755e50153e3ec30f5c9663d0f4c967352fa093a68454e479bd3ecbfb2b9eb5f2c540b4b541a2e459b22b70a2966b4bb43604d12eada51cde364823697e18a9e82af9e35bf443f5ca48d7edb28aab7a45168eea670158899c98ab1009f21e7cde37f90fb0d30d38fb4110125e04d23246a665658f1f00a19e347dbdf3f3b5de33212bd68252f631610865dff717b392771b55c224fa00c40d427a84f29f95bf7db6803d35b77efcf93278d125afeea7dcb076ec3bd5a88b115745210eff317c339d033967f1ec6afac501100be059793316dc40e0a08d1edf8765df14fd217af48905add20ba20fd038e4f1242a9679f7605a59a4896e283687d7358af218973085e061a847b226026c04a5575e41f8673052b78f1bc383a875e0ac6c7b47fe56f625039dd2e8ebed071740971916f254d5d49c90fbec1e324aa80ee16cd8abe03abb7e19f853eef31e618722490709a43c82c936fa2dd93cb4b42d821c16f4ceccd8528b73aacb0b3281cb66c1d6347f93f871a7d6e5c427a2a40c08b300f8e57718baa0d38a319e67016d860003808a78a392acae5496bf9e7af9f0552612c5cb402d914246df2a4b13031d271d60804f61a0b7e4c706fa50c0e25e715269b1c241a855a1821034bc1c9916f0b674a3a234e72e1ef8f2505215a66ef7ed0b899c569342dc13255459f537cbd8b2a711c52c9a3b2d5c149d3f1a1c93bedae3655b448e266ad094df9ac8b465d08b36dda0cd21148b14703790653ff086100534637ebdf4539848048ae5b10324780dc8d87c5825cac3d69fb1c068c8563f129424884fe7aa9e2e0bcb3fc6a0e14345e6f311585f9e028cfcc7c5d1da5e366b28a2d686e60fc57ad8198e1981500d9b5302527e7f5ee5218a09dc0357f8f9045fcadbf01d71e0b1703f5f4342ed85bf3c565f9b8169f727962d96d6c898acefe0e5aa4ecdc4d05e94a820af17fc5a5243e307ed38322fea3107c4f104f6d0248068e2d9451498c43c402ec226473fdcd728296f8e67eb8251188314b767ccb67c3becf48b4fe17da5e0366d45a86f37834dfedb540bd759ae4b2f965088238213c534b617aec1f35111e2ec370d51846646129314fa5bc7524a0fb54254f65770ee63c07b98c06610c9c7165e916f4feb38f76e2987938d787159453f6ccc5ef272ce2c3ab0f1977b2999eee60424b245e3f9b4427eb2d5504acdc812b3f9f4f4c764550bbf82f730ae28d1147edcc939972c1bfb1e18fa32859c82b93d0fd25282737698a5d8cd3959065c7432c9acb202fabcdcb63c3e023d0c6f78dc8b81948cdf4a388c681486d195bb13b0ae450a17cfc8a91f484de669684ab60ffa9a128a80c8bec5a9a7dfc49878ec967cfc1c94f6ef5a332b90e6b30feca3525ae82fe0d3e7ba1b1f72422291ef729853afdef7dd86bc80586ce15bd4955bf1ec46628427471135205896161e68b0f06231d305900229320f5832a6feaa5d8c94477ca89b5e46f8d35ea9dbc5f027e1eb149e31adc52f86b2f14199916d60a29b46a5bf927b98d3bb0c223363202000000000000000000000000e3bcc9dd883360c4feccd66025c3a8fd23b5ccbf620216bfc31ffa0ed0deed84e3aa89c0b93810c81ef4b09347d22e6ff5f2f91ab2c1d56a7c23bea4f450b75833ae1cbf313f25e643fb7e44c1e03ee925b37b4d78f61c7fb16dfe2ae6ca60b500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a82958887eb12834190fb0c8255fe628a03f3f36e24c8ca362d2b3b4973a1be5eae0fb9d56b3b795bfc2b2889a9050e09c51435d97ad09fef9d03e218a3e11062184e60b98c8b754f4c00d2c935aa95fcc9735784d7659d83422a595339d4635d8f8c23ae6da194ab145c338c5b19e0ec27f60b02386d7e9286f16c31ed3be4702025e0f1c9ab7119bcd82a7f1c9ac4f5bd38f52d952c6cdff2fd9a800f4169ce1022969a4a29157db8ad1e20eea7099cdde71635c36d3d57fdf23927e3ea603d3d70b03d7d9c0fda2d71efd1ef5f8b0f240a2f96f8908a3a0a1d8263722f488b83a35f79e18261412857d09ba4002c3ce06c877a4344aab8667b495fa4afad9110c2402137748e2fdf0fd334dbf2263750da38929cf0270923e16cb81af8b25cd3b9633031915d7309f5522353a6ee9aab9ec73e20e21c925e1fe8f3d8c196c26c823f8cb0323d765c02280f5b27d7eee8dd205656eae6c73f4026cb165c0e0d2672e230b440303ba0336d8266e3e3ee07545ba07a0afd9d3bbee34a29d651845774fdd78e61303013ce13912f4629c44997ed5d43432995220522d5cc1b828f89f00652c4f26d93dcd8cc31b9b6a8b387fa69f56b9717dc7c3ac6c7664f5124215ea83646d3d61aca080a0426020d596a623e29e5ca57fccb0e89d347c1c8df3ffc2629332eaa751431ae2a8a08cdfcd029b7a8e0d106786c1718320b65fe118b7e498ec9641eb26422d13be6763c90ee9d71310ce10320d843421a7b001019c6af2a4b088d1c579edce799e25e3717c66fe4f6be5ebee49129ccc23b08aa6bafd3dde090b93ebcc9020edaa4917704be36dbb4fd91080eaf30107b14972d55404f488d9ca99bfc463266a4ae863fbb166281b1a4fe48a0e98dfd1b60672ebcde31db04c8f023796602feed19eb935f4f0bfdfc4a4252197a6451eb4ead33a6245f9ab4f3fd964a4cf0b1d755d56116d1c445ad795e6c80ccc2e97acfb748513a092cefc83e822be1bef2ca94a7390697d4bc47e09110a60c3c65e3365a75bb3b96f2160ce85fc82e6981d5ebe9a74ed0df58e5ba21c4312c358c6429e6db0c88fc2abbf907955fa1ca29dff5d8eba3f209173a96e718fe14cb026a5019dd7b3e4e58a3e6fff716a7d62bd779f6d7aaf274ae79d918cca09ebb9ddcd91a4adedd8393f6b606052c11dfb9f0759f4a20d6e62bf462619e4c4e86e06811fad31cb341e0bdb209f012e3f7fd13ee9ba216a011d2040ce0fe1c48b232ddc1f12edee33f31b7dbb20d3ef9ad98aa7fc5cf835833b259487878a83158fc509e8c2c3f189e7cd026daf1a3aef74d35ce384f4a11fec5f73c102c8f05fff28b371264029d8d1719fed81270599067a629ae28c65d6dc366fee821c2b2024c89ca3675b747dac4541ef4fdd8029dfc7708613ff57fe7d82b7b591eebfae32c9fdaaf160d78e6d0797e8717e47023b18824ed00f7e0048ff87889d6f977879f13564e609c35d83094b10066c5c832d96bf2d4c5c51eba571cbb76322c1c9b01fb04d6018b5812d2a22b908d85e5b5255ddd551586d2f4e4e6c6fd0d47ac7047bc55c70ad560cad5352c9de143c9afa61482537f7f2d00d4cabd9c8b9e82798e8e8e202e2fd9bde3faea78849727b85c51b9707ec74d75ce3b97d72ac46ea8e4d53797f89a0fb998d206a0c90a74014fcafe1d5c8f66ca8cf0bd003756b9888a2533c92e5e4de657e5d2fd300d72dce3d66904d2af898001a76333bddf83f7a26ce2279a1ebfec0e82f1c202502304c012cced77ae119828a8e0df6e7fac6853c8a52564a9fff6dc28970fde7fd7c0a61229031fdb788786edfcce2ee58e43a4e605981760f49e41c2ba67084cc666d97e48f44207f0b7c4c36dc866528f1dc8054d296754f9741fb190e8fcaf1ee72ba0b2ca23c390baeb7a73e5a9f3a1b46c6367f85926e20ecd074f0cbef2f4404ed016107981177f2e3759cd0c6538eb3293249bea4926b37be824de7bad2501a797b8e55139f876984361e448ac71ad8ad3a47346a873911e270bd624725d502fb46cc3f03be26eee35076c99c57d44cb18035031a136045bf73b8500e7d065ce87d38ce72bc6e7a691a7ae883b61350bf30539ebddbeab4dbf42ddbd72f6a1aa6c8282f443ae04320295d22fa1f2a6298529982b0b219b4fefa3ecc114e6ddb6a0af9ddae1d457467c2925fda7f9e6fe56af0b8f0c04ad4a4dc89e8790ddf9b0bcb494a66b07bd428073e2f52c6062f7430be64d388d47c2e83a2d95894bcd51a26a049c5c719c29cc23bc92d6e7e6a8b25411bddf6bef414b851c271bbcef75d68f5bb1367485d0fd393a9c9733e7da85b1edb5099ab567530159049d1a67f7306e2e26f0f48a759447a7673fda840bc84c6409c5eddeb987c6ec5378f781cf97f704078acde6b6c4fb0b76efb0d", "65ac", 1, 1352377622, 1537743641, "790c5b5e528e3cf8c53d03d13319b276b24c95a505658522c1cf515cb947313d"], + ["1a8dd4050218fa5f6bc5dcbce22e6e86ad688311633e0846fdb7d82b53995c36bf760b06010200000005656a63526aa17f9428bfb2cbc8caeb81a446e7842ef801a2d21ad2cef35d7b28d104bf5436fd40765b010000000165ffffffff02ed15e401000000000100f798f5030000000002526a2ccc513000", "656a656a535152", 0, -684260667, 1537743641, "aea686139c018e81adf89089f589da986bdf0c18c60ce140abf61088fc76a78c"], + ["f067753001e785b5e4d69a594a97fb6d0a3e12ea3e567c5e6bd06a70bf74c8b682d1dbb99e0000000001650c05d8a3012974a0050000000008520051ac5300535100000000020000000000000000e93bd70500000000c6b291dcb8e20d044a666e21b9bd6bfce53989c570715234416bae25c8a6aa243070e23db94c2f94530e939203488f12325bb37d5f02df4757d8a76f3e0410a6e3fff1d18a2e5cca9f44d91adb8feb421867637ddb4d1c17cb67fa1867b208ce000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008890813d53e9f61a644b164a82bd0c62ff071e6498342a7502a6cf0dec34ac0abe1f0f5f330753a1eae562a4c38daa79026a7c88b2e00b3a3c068130c76c58ed6084bbb5f8cbc25e66a625e105d5fc8489dc135d2b23e95d920698376a3f12c66fb248a1b798c66d32ce022a9a896e0ea6f87732b4210d0b33689a6d8abe7aa0031d88d0fd0e41fb5d7a8a38bd586c12677a24e7df41f99718d159475e295141db0301c2ee572a223a034df3503697d13e14ad8bdf989b8df7211df6d3cc2f2a23660b07cc26905435ef0494caddfe4c11eee86909aa80a67c65d1cdd0ae785dea8486725cbea2c9858f53901d0982431e02cb52307d053656d6b94968f0b04db845650229793d7fdee7390f6205e5617345345bdf289dc4b6d954d079d1f88e8702c88b031b0f58538d2f3c6118595cfcb6675e0f8731823a4d0e3478be863562ce052a8a03081ba216121549ffb94228ad7bfa8786b41cf0d33dd3d87174c96d24d6d1e4910217cddcd45c03806fe6f8ab0ac8d649ab8c056cf6155705d2226c407b564f620c032522a81daae7cefa4991252a5cba5bd905dcc11a7c4fbb663ddfa3cc3f0d1b3e426e4f5b36ea414ea8d99b287d8edd5e80486f6d6f231056a0cdf6496ec4491ed25f0f96116d5323f9086d2ae2212c36c008608943f8058915d4d83bdf2dd1450d2dce122436204eec9f28861b962865e65f9b546ffeb5f3e43127a6012cedb4d20cef9559509d2bc516b95442927ea2c24ddb323b35df8a872ccd356f87d91d6b10a2b7728a40186bafc7b418ec87c717b004d2c2c5fe789a3e0aa0d25295a28a0edd747f2aa38185acab621e4360e58e3cb781380a95b20523b417b2c0a5368b80482da9372ef5e18f12a22cfcc6395ad7477415557baf7de30ccfff156e93c3a88fb9b22b973faab26ff621c65822a0ae33e3a6923bbd2b140db492bbbfd33301756c3ffe393cc7b4118809030e0d03087fec6bc4228233bcf3d29c2f8b8c737025fce8f0bc731bf3e1f2bcda5c248e7eb9c647312ec45eff5122cbe1c90bc6e2bee9846236d1601ebc77d66182f1536b47dbb708dff7693f70123286077a0ab4955691040aaef1a6d4793912c84c85f686f97f88e5c125c35c043098de1617088b90e63016608c403cbcc1c87eba8029e497d9dc3c9f3f287bd01e858a0df828a1c820f393ad0d05ced1d6cec6e0cece688f4cdb8319b9d8fb7d9155b5c48cc856a1dd5b271be617cb3e4cff115847145062ac47bc47f82581f042a3564296b8669de71b528a75446f23f10346698758480bb01445008a37044a50f53ad482d77a39f5cd72eba4abc52a8cd65c48b01acde9c25c5a1449fca9eb540e1c942bb5d3f66e07cefe58a4ca03557ccef140e5a9b69d4692af638390ce784eede20b59788732acf26e81b374971d57136bbcd3c5ae5d705a0770181e9b1fa4af2ff05c9e4bc55871bb677b0260ee06777459b8b6924faf100a99415498d63aa71b771291565fc847b20beecf05bfa6a12ffcbd7bd108feae40e70d3fb77f5c616ed5c2913cdcc7ef13d71441f52da209b2bc9b3787ca14a2e109d6c811ec07a7d5af6e5c1d8bb8e850718388808df02497b9b17f8638511ca1ccfd0456cf6c0fa79c392cfa71602b3fc2b85e5edad79b996c8036d3b181a9fca2398898a6f150dabb21a1b998e23880db1b34d20e74d74be70e84644c678ce1167eb31fa2bf2b1398664a5f0e207e09c9d26decd4b26604342351f2d84eabd05b0d7afc2b73bb98a4aeaa6211d83f44340bc592d569747f061e2a80ec03c9bda06a1a300a6c968e6d81f2169de43c5382466d7091d3382397036ef11ce17c8db2f8eeb14a030d2b6e85b6b7571f6a6d3cd03db44ae60fd402ef7a9c439644cb741ab59d31f68f4cb9d838f5d0c410c851491477d0f1b908bff229cb6f8c992408f88db8b873c5f577b617f503ef2f198ef9251dad3d90c6705e5cbc833cd29b8ce84be929f370231f93e85f7a42c9635c4eecc7bc65f11c0eb2ff5b4a5aeddcfaf2eb90df365dca00636cf89567dc5ac00e2a7aab1bd5dab0f696aad5f84409d2ba0c2ea170010941753d7b7d538f4921748d7d83ec80b27a742ed9cdd637a77cb09a5cf9e5d6606a575d207cab8cdd200e0e0fd8a9635f5361d8b68790f8e6fde13009d2ea4ba42f4181e4633de438f6c7ffeacfa1f26960e799fbb4bfce82a58cd4f8223d8e0998bc802a44e74bd2cf6cec7d619e5526d2728aa534838c91ab477d7c59a3a022e09370a942749e60b9aec6498a11ac35029e00000000000000003901da0000000000a6dbfe8a11eb7afba35f1761df422e40d2d30d76f00c6b1432b8707c4bfdde077ffb35ad71c69622521158bb5a43f66ae38b01e1556a0784bcd99be6c6141633abd997b06ea2e3f77f1ad72010fa06d9c11aa30e256f0975c7fba43fcc8466ae000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009e0b5648958813428038754b24373dd411eb3e25fce819119e3cf55cb1ad0e26a5defca8816217b8dafdfedfab85d85ec928e7268b8c98cf4daa052894e4debf66cb8b46c985443d675d6e4f6f13222cec8460a857a99926730ac60d6b4d397a7b748951aac97ab0b70c335118d5099ab14a62820e6b29d91ee55eb0f53c2963030222b09ab9f8ad23e75655bc909c88fd7063064b2a5fe6b8f298ace142bb6eb9030f18f5985220c57ff90b44726fba4b3968f7d3509bf74a785929e67ceb8bdfac0b00e8b5bc66aeff024b5b8db6bf8860e10c36b5d0f8975f45444bae5eb56db00d258ba5d76ee829213badcedb0c7411214e50530288f8b1295c9f1f0854a9ec51032a31e097a64b4f33ce55a519bb89574575a4ff9733aa95befbad46556eee4615032346e7ce5ad2f7ffd7cbcb1c559636839c0bcc2999a94642b33c8a4c7a925071031a690e7c96dd032b4055537cf66bd42c15788d82465bc82ce468578ad09ae6e1031950f9808d2941fbbd1381a70a6a320675cc332ab9e2a3183a73683b7e237c32031752cf76ff6dda4f3444af17aa03da3e69aa425e92ad0511b581cc5344d53a5c150f257fbe4f4def602ca9be3e3e2e942bd0c7157aed4cf99c44e6e26ea9ab2ce7b05bd39cdcb5fe8243f1e8148d2b3c92cec482bd45ba803baa0f5360745fb7e264ce43783340a5d5562fcd800de3d44905e13a506e5cd9a9268fc2098b88755f6083a19fa0ac998473f40e1ead3ddae8f5502e0eadfd6f7e335e48c75ff45c49a8b107b92aab4219dda328b77276182f4a7608b179d04c3063ea00883a7a97adfaf1d918240dbe1cb80fd2b8e52302fdc57477d07e6878bb33ddc408cf285f84c2544fd90bdddb7d4715e919f45889d0ede7b0c68eabb59b9cb00689d2ce5766727c2a35abc6df6079e04cd6a1725dd6ceedc274c08236dd761b2c64bef1b6acb163f84bc6e5fc617c989dfb5682b54f99bc7adcba733c791a2865e0a330193b43cb6937688abc86b59c1d9b66bb51cf0951d4600bbc7db4d6ec0eac2f463e780c66489bba2f094df65059cc13b474e7908f5b8ebea4fa295dd0dc4695eccfb6eec40a74c923440a5745a8ed16dc3f5f87239ffd913887da5b0ad786aa7a8957deb9a652f3d22c70b468eb2bb96e73171549a2533b33d9766a2fc2c10d0fd7fb3810c24a3b9e7d094889f9bdbd3caba9619b1346452892bf492b5837b1b80351195d2cd3a6e94794943d7a867fae2bbe9e0f2baf82312e8ec20c5877e9c1891fec299de458e1ce77d8c8acd3f9849547654e9ddb191bf85101dd39c5a84f5979975b5368ddf7e9bace8dfa6577adb219542dd0104e4a8fe0b96f2b56de9df2734b1c002e2c85b48d0a1e3e9c29910b016b3dd61704b885a8250b379df979ec9457a228dc08f7bde58b2c3a8304cfd5b7032dc1b83371766ad9e1e2f3f8c8d6d3d7926f42dea4d4e86e26be566426fc2616976e1ea690d9d470f682fd2ddbb8f9891646f008219bc83896902965c1620aeecd79f5805875c147b2735deb505ed8ded71603af8ebd108d1278d0f4ef63c6de3fc5923bfa3fed3d68f7bc3a414eceb3c145d15b5239361a0c3a9a6f2a98f58c0f48ce7f71c4c20ebfd2b41c22c23a150a35c91e4099070fec441c308f37a608621dccec4e8f8b4e04c3acf4092ed2e9398859b1eae0649114de2f6f6bc54916fd7d96a036a6abc37fcdf9e94d173ba6707f04c71ede5d892ac4e925848b994904231f7842358b9f0ca24c78190a9539ae7007658052019edd76b22d40bb470c6d406fc4c1602f929295576bbc4fe91b970f5e3c1304116f1623d6951ca6d5b20a9ca48bafdbf81a081ee9b3f3f224ecdec7595c530c55af1357450cffbaeb42dfe7fd103b95491edbefeeff09e4cebe8323dddabb56b4ca10ea829657b50a981b9acdd7af53cabb43231afbbe6d4e29c9a78f8c73ad43463e6ad71b7bc8d9fc7bc8c46e45ea2f08d70de3f497de0a19df5805c055c33049bffa9f808e6f8102cb39b85c979d90f5200cd58e39268cd16cc7aa7edbe94459bf389759da12565547cb3acfe75e01e8d8c4f95039955e4809609ea4efcbebc0c7eb9acca6d4e0ab668e9fb6084bd87b8011bee793c883dece25050e174da6cf0b791dee147c0ebe7e344238ea00a860330ab680e5edeeb48a3a86ea6e741b60a473b0f5def79186a36e936dcd9d764ba8c69ab7e983c05c4f73b34f04e797a83e8715cdfe10924d25b1c34e03edfa5c708cc88a5596d77984d5dacdb2e485888311e5fe3e617e1a579d499a6b349b2b80434cca32669343cb241808c95ee4254aa36dd6a9a4e235902e99203387be6dc08cf3441cb3cbc9493502c9cf8dc4a3bedc13bf249eb4b07d43fcf06f4f6c79479f0fdfbb22a388d49d7981db3eefe46adf3023ef416305", "6a6365ac", 0, -1488409917, 0, "92f0b98c330fc2f3a703695ad9c55fc63e84b2d3c9f79c2b30b0fae7e9f761d2"], + ["030000807082c403028f0db90d9fcb035f9c13ccb71404853d651ee3825cd7f9f83ac84d9589b008970100000000ffffffff8d1bed0074e5bc1cf6b7f1167236fdbe68b511b3ef6f6f8d76e10c336227a1a301000000086a5353650051006532bed09a04d4d315030000000004536563ac0d214303000000000753656a6a6351653ff40905000000000463acac00ef1ea402000000000451535100000000000000000002000000000000000061cbe100000000003ac7346c7a696ad487c42626e18889b07d266e5307efdfcd0ec431aea093efd2c38cb702ce743299b6634d998568615c299738bfd50cec5bd2a31df10da5887f293565f90c1507c7cbf8ae7a2844de1432a860d7a3d95c496bc1ff4ecff25e9b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf4b47dc1d63c432b8dba76e69263f4bde294608a3a52b36715923b2b9955cdc07cc26d6b2c9cf706f77ade0d1783eb2cb1886915eade80f1a0acaaca5d9c5847de737322e547d5c73395f73da7892271cc1d0916f9898debdb6e6b8349e187e3bbe4f39d2cc66aec176a15a794326b60e71c0b9eef5e0353d410c9bd3e4e9b2030a6898a4d5641694437cd4ab18cb1604d45130bed0c2b03d2d8d56c47157df5b0305c2bab0d22b8b222dcd2bd67e471c5238daeaee8a3e2fe1fa967a15f21bd5b10a0834c8b7e439f033f32b87a95c0d6968a547ae586b3e5e3dd7a7985db179c7092634fcfc8d05e9b3f7916c260add294c37a842e5d878871169384e751432f25d02020aa17124eaa8fca88a2ac6a3215dc24c7ba196da3825a36e5cd90cdced2e6302178c24ca44496039e85dd2cb0a5c5b9acfe44d769254ccc632db3d1c55ebe19f020661d1df80bb0f4f7191eda986ea34fa73e9fd7f0286010c3f79a51ffd1b837b0204155cda6783b408878cb746a6a57ce82f9950e2762e8373b28ab53018f487a003087ef763700a601c1a663d1d037029c0f1777254031c4b73d27f88d524fda45a0e3732a24477a4421f6b750e01fc9f597eb3fc88f29d2cdc90aec1c9bd2d393e083faee31b3d2ecd0750f771aab7e8cd04fa2841dcc0dc0b470f233f3778ed43cb6ca9178c789c0963fac99050a6c32f4b66ed0036504745978ecf37045392f0576d51f1df0e7eba790245e40f316bb3fb79a3d0da7493b876a07aaf729b22a9528265c415fcd04d93a99f27a10ad532a15bb1fb01bad59f4a419e6ac5f0442ee0f865747633db92995e4ab9ebb7134e2b22827255b8c6b5a2364f569ee2c9f792d8a4b90c07d00559b4c93d3cd328460e3dd33b73f3de3e2245e3cb1fd3e1e000c4f71996120631d244ac19feedbf4bdf3fdc3a4547e18e29192a18586da4ee64fbe0bb06750942facbe32d9ad48fa6eb3473ec51e3c465fb2b88494a2cdf9c0b800e0600552c70e8ef1a475f316cdd2b4bcb929828cdf7c7616f34beaa1109b751029a34dcd5ac53b35052370062c57ecda8a524d325ea4ea109bfa23c826723f5d03e7ca0752add1796e9ee3ac34043abda2df158f217412615044aa1b18193aa531c6eb14c3a8f5b1a7c41da449b2b9e4c74fde5296777cc2b929eb2d0952c372dd34f0e67703a84c86b313138eac3294cc2b4b956561c696bcb4eaa4aff578226414bbba9c1f083f48b976b9b45383eeccbc25c53fc713cbd661047b14745e5a7035d46f356e2cae48f10b05532174af6f5e7f5812ded43d5ca56281da5c3ec9b8e2de55ef498c1daa0fa9d9c5b08adeb4c904b5e15c9528c8b01b0a8aa7c6cf38b9a3731ece3ee5ee14737a12de088c025e929a799b9dd303ea089112f01222f7fe09d0ade615f7418f185254ccbb92925fe7473d788e3b3336f0a57cd5dede0537807415bc67629ebed0968c7a7e94ac68e7a54d52ca7f9c468823d3022f20bfbfe92faf027acab1b8b94e1e8955582cc02ebc2bf7db81f10ab3e3027c426421f2239eb603bfcb31fc3b01456c54b84f29d771128060a4df763aaedf2221255f452258cd7e3af0b8d8c128dab8dcac8443949f02e12fd395d03ae8579026e0a2735f44a301fe9ca600af410355b27db4aa1b1bb4a5ed06ebb41ea8a6dc0c201dffaf5fa7c1b0d07ef336b37f2c429fd5349cd7a7df33c7b9aedfbe5bc572a5b3e8324a02743bfcd9e185630544124baec999cb06165da323a68f9f2d36e19015eac050a30f97a6f303687a1847bc49f83420bd8264f12c9213921c24ddfdda101e3ee99c75309f6664fb81bec45205b7edcf3877e3d20d6c9c3ebdaf1c96d81d282a1674865717c4e8284d6e28307760dea4203c7cc0d1e4c8164221146d0efda63b59933718b229cac8e832aef082f2eb66606fe8fefcf934fa32d0b3cd3df3d3bd245e887429aa6f007deb1614fcd18b4ed11bb634a0e6eb879643bdaba3117b819dfd11b0d05f3bc11d12b2009b38b500612c358d985fc39d10eaf17b4244a53f051ebbd09bb019051a09851c11bb7a21f9be5d9cff9fb4b1ecd0109532623d357d1ded2d3a270b472641abaf7893d7cd022680d7bfa913db2f959084cd436ad086b4c4e7871e45af4f174504ba0b9e70f6f8006a1c253e581e061aba223af369516ad64721e81e76461def49ed7886a256970616f3d9df56c5d7bcb75023cdb4d1062bdcd0d9c6e1d2ac2f9331ea8e7c4ea3f0cb4c73ffadf9647a974be56d812adf9c0adf86e7a9670de438424915a030000000000000000000000002ff6a50564924da917b29cb696effa00644b10961a0e02a8913b836087c7ee284fec7e378b3d71240d7f0c00fe7f3e64efe5ee297645352f5d0abed31d028d5f76339f4463a9f4d73b0d9b98b0e66603ac1175ee6e6a875e4865732789dd9a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000634bf52a5716ca38e711faa4c07f974d2616624812f16efe8db906ae0066764c116a568668e019657a10e7d5f4e94dd4bfbd6c272dbf5206530da74da873989cc265fc1a1eac02d1deecb35bad50cfec92ab303efd7f9b93b54d6dd3afc4c2c08cad0fa8a3720e6908a1c2789fcd16e47892960090cb2c5d7a5a77c6c5f4c210321c66e3c7758dce464957de305109d843152ddd085fa98baf2403aa83c82d0fb03281ebb91d5e17887f05b74683e5c9a04325000c320447ebc2a6ed70fae9888590a04b0251b11c6eb72e1e877ce1dc1d44abf0a2ab891bbd775a9bb259de322ae9af4e210746171ef8f6a26961bcea4289592646c300580ac1acae9500d26db03e8032c81472fb7be00e95701a3e524d79a2167b9d5821bfb15dc96246f00ff4e42d80315b9df22eb99d3b91e46e679609d35807489504cb354f866b6bb0cce1fc284c1020d6a9d9eaa0768eeeb7f753b1f84d54574416df337d6273a7395fe3c30f11c1d0319dea5182b608c67e625c983eee4dd48a1be018ec19104d10841d4e6764f739b022fdab6f42eb093897c0d77c5d3bf9efd56dfbc4f6d8e05dc7a9e34910b9f391948547a18d8774c877160af04d271fa637de7e49665c2a53f71d8f87ab809d25b49eddab2ea1298e706ead155cc10b898d7dcf017c8b647906664bd270abc9d0a6a536e3779c8b5f178c916e968e07378159bfc0c2e88f4295a4606fa3933f2a432b1d0761ed9dd872decc07d30bbf84365bdfbbdd8a7e0b19a8571696ef27ac65c9d6ca112ec10e0817a562c89238210e1ef6010c692d687792210bb0a11bdfe7d8f5ce20f8f83207799b653a8319b7efd797975a41c765cc1afbab0a03a2d8eb1ba7382b9a96e6041415bcac73984d4161279144910c8847c27726c508b4b4366740fd59742cc4e088afce87e33c1c39e52d12373923f5d24efa9a801536ec1012fcb78d360dfbaf63e4c33f382c07fdb03d036790d408e19bdb1ddb3976f91e1e1aa02c84267c2e39d64dd7571f946ca5c6590dbd16a2f339c7479db27d2139a8373387c5fdd995c2c6fb1c0877866e0bf70b0aded5f9c5edf5ef49ecd236f7c2dd988dd17869a566eed9bbf1b2695a1e9c9088bb41916c94cab38628febcf9bc2033af08c6eff48e7483f2f9428839f0a7790007be768199dfba046c34fecf6d775e3967b48b7492f4be57cd5db2025c3471ea7ee89ed9ef2b3ff141f562cf27f4fe3a299c53bad633b6e799a13ff74e94733bff48430bbaf7e0924a33fb79ed9109168d30bdf9d6cd6e7eea96c121dbb65f17d9773520e424c83ba15d11fa9a8db7cdfb0ed41cdafb89cd3685994ab965400ca6662d9f9b548664cc3a93f5fc689449aa8bbfa989a7f70e92986a6f2de37cdde7bf06b3388c365185b208b5883342dfafb0aebff8f433d54d03cf1467d8954c5f635844a988e2a30e1648afa3be18f80c5d759f6efa3ee09d83f4e0c62fd3fc531bbf5a47e658259deb799448e70b3eac3400621bc18d29908566a36ddb5983c468ec0cddc0f67ebf8ae38712c1c5912db57553ae7713e502d01086ce992e2253b0fc65795bf7b4a2cf9f6cfda7194a06cdc37e61b4bbc6147fd50affe87eb89b9258764d7101ca54632c0d52722f54a1030c02875a44f22c57c9a56dbb907d06cf4355ab47367a4f3b6f7792c904cb6910db3087305e244c1ad2377ab79c600ff2c254b00d46a82bb6c278412899e2abb428881ecd39f98d47851bce409df2f1c6cc148ac32e5f2f4ad416f6b09210a7778c18491bbfd5e2a48afb284d8b797914c23ab7d287f46fc12350cfa7151d3647de577e50dfb1d260895a11becb96947b16c0ff7e87a4126c1b985319a77796d0408a04baf9c57883f02714f2ba83db128f29f5c186387c5d0315cb61aec2096ef8d4004352444f0a552f4ac7d5be349480ef7d141106dedb90df01062f6807c22c422298c455df7b35eb7fb04e6e86522aee1c5e8604d1d29000859a41e7044d8ccbf7b9af6c693a03f0d539a3ff05b428729002baab0bffc85533a9af995b04b7be60a6e717e0fd717f46d5d20e06ea02339e9458f60d6e14e2d8f7f120c708fdf3232f18d0d8d8a454a75fb783066ef316d7b6d3605c12b51bc65137e36ee9ad121213acdb98161c2ba39094e5dafc859b2c92158f48ff19682da9a3b0332461e00b3c0ae632c43163be29236e388cfd01186d70e1475a3388b20a15e00398e669b42e69c83ae6671c88a180c8f8c8dfe196f6080226b9121e2bfbb4f40d5348fddd21cd31360cb8258ab5d9f8c6429af64f1b8f46f4637ac55de0e41c5c2043ff0748e3dc32ef42ef014cb9bf163bd7a08e1f30f1b24e8d99c761a0cf5826a6bb864257b013effe3a2d9cb24a24c0552ddb3365b2d39f437001a5c609a71b69929f9791ce9b9aafb700f", "52ac520000ac51", 1, 806868455, 0, "e22875729a316e4fb28c943ee87208a3481ee5efdc26435c86f8c54bbdfcdbed"], + ["030000807082c40302368879cf418f04a110b2e6442b36fef990f6d720d6300cf0c16f4a554799e0700000000000cc5eb367614417bbc11fbfb5b9ef84c2e5b0662f7798b04d89ea99e44eb60bbba58e415f0300000006ac630063526502f897d4033281ce010000000007516a5200ac6aac825dcf0200000000036553acd7712a02000000000453636a6591b96b4b0000000003c44225050000000000000000000000003eea18eb793b2d62d0e805c85dc965d6bffe01b6c6fd7fec8a077ca6674ee1bd0357a1d28f3b22eb161b216e641e86b04fb83d7476e00a939f7d6891bc2a4f3940095d8889b102b485e77bcb7d559fb6426d672de4d9095b1eed007ac837fbbb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000da5c9bd1543eab8467c640b84ad13c8931a0952a5596101ebad65742a981aa3a4cb1edb32cb400fbb9d349988c464f3ed636ae92254a8bab2ce5819839a661ea0984fe900e3b4b66704d9da7c533788240b01d1020a0e08980a4c7f402503b24a7882c86e22aecef28bc419d55b12c5f9406ed06ed3f5aa8d5cd63b4899f246d0322d7f412030aea24988279aee51d0821d27227dfec392ccc4a7d25feccda095f022e6d7e6c756c659eca336649c5d0a0bf73a01458a5767fc15075f0831bbe3f070b0807185d05e0729e22e29a32ae01d7e0fc88f8e7636e23c175fb4d113ef9519c97bd0250fbbea537e1ccc09230358c241ddec4dc679592a6d51b3c48aaafb8390226c21fa5a2dcd56f47d3454ce5b91a67040ac4c583d0575442d7003e3b8acd2f03192057eb8691d6132cb0873f2ebcf46128c0b0a47dc8cb51f402fdaa696a2e69020caf2bc400af366eadb32d4554ceab3e6400addf6eb2bf74dfde56b1fcdd46c20209010c38b756dd61e7c93bff30ce895dad3b00e2038a131c3ddb60dfaccee581022bb546403dea8469cb4517813549097efdd40d4f51ea6f20cd41fe267731fbc6d85e3d759161176af8c5db90874ce47e0a281f4f63cf3b6e1cf47beb1a1ecb5563c79977d54c1258d6634c6752aa64058b6960dde52e0e19f8c0b98a81e45dfdbe5cda50fa40fec2dbf3445195954baf5ae3a363c9aaf92180373de877ba999740bdeb6537cc073bde658c99ddc223b0e60a8fa34fc8848e53ca409ed341c5cde9235eee16a6ba55b872bf1901051181b2c1d569fc81e6fb8115a7c580f0d5115ae7f9bcd1a980cdbb910c8a37caacc57e8020ffa1d29b92f4c4529029922ccec4d81076ad67b4bcbc6cce1af7cb6d528b782717449eb1faa11edf7f82e64c8cc8a2e5f8c94e22f153dfeeca0e3ad98b892430673240f4a2a5977bd437d5613d4c2e8f81080adda13c6b4b288d962fd8fb3bc15d89aa60a27dfc4a4bc0ee557dfb1c731552804dda0fe8158b79dc2fdf3fa370d739921735741b0931bfb0518e89ec6bbd395e23a93eb1a5fb65087f179f87951d7dd3f543efbcd95f57f5dc9f23b0ceb63826de36077c333f35247c1df5c3ae9810f347e84505ff59118aa7194443f44525ef5363d2081e1e787f39427bb5cd8a8f20e0ebd87289cbb6ba242639219714b4f14bcd4749607aee94b68e8708584f90d08227d7e01c769015fbed389d95121ce145eefbc917774d934d28fa308f6c969a94e0cf4354113c65fe008a34dde61922f0d540dfea6c75f9581e73e199e45699b69337c0c26e09f530a9b0f47aeac588cd42773ef3546b73237da90f72dd333ba6b81cdec933a65e44c83289c52bdcf40da96ad3e1cac198d320521766032e2aa30bde417ac1f574e124cbc962a5a0613f500aa76f6ee0b1102f294b8d2c200e72ba2d0581376c339d2a2e7659af6b7a619394d4f84619a967ed631dc6c89d6a5b32693545dcc63faf9aa7665896d5ab855966263a9d16f62ed748b4623649acad68d19a63e9e0d94f27c34bcad3872f2daa65d64e4129c84fb4160a4b5eb39084b120bf7ec2cc3e2bade97460703fbcd7621393e8107ead1457a2cf9d525d0ce270d07fd9408133157641266452d9ed8884ce5057f6312c79321c4eee64d1cd3c193ddc262a25af4a021d4fa0dc0abfe2615057c3208c098b2358e544f5b33ac1080b9d372894cf18fed3e37664f581c397df2f72252d03dedf464d29a4bf07a2f9e3b8e4365b210eda61fccc1dd6eba31697c83f010e49adf130a3b401e31d60ce923353ae0b0dc0afd27f7b922f797b7df889bb2c6f4992e3aa4d475f6a13d2eada4ebda883d7d6f53d7a57699373b9acb678316c3ade0d0063955584c0cbf8b2db1ace82d9dd249b3803cbbc604ede2a6f7e8f6be4765865eda92b72d810e9834d78456d97ca2a827e7c3b5a9ff7449b55e3ab8c772e4b8be468c70d113e3658738ddf5bc995df8d190f1d3c906ec9f25b5edd165d563ecd80bf95ad7ffcd99c72916723ffbc369399ebbca7aabecfabf84e73c9603f2841554b7bbc3d690a1e64ba6f91375e82923fa7046bcdb108f05b3b89bd24de40e36fd8e8ac33abb4c30d8d9977ac26455db9353b5466543221275f421fc2a8e522bfa7d9851488e3440c9b15e4cffdc110731ce96b40d3a1ffd165ab51e33f932f1626acdd894fa416ce324c91b245c006cd0a152486c6f0f1019e9beffb23b760f6959644f0683cbc66d623305ee9c8836ac57e933df50685d1eb811d164ac4def54100000000000000003dc2d400000000002f1a55e74ca6474759efbd660caf6d4d7440dad80bb9199e4ddb2270f927d2e62fc67837452cd1fa1605f7cf647800468e13251dfc826192fda4b8c924242e4581619b3d710d59e3bfba312d38cafd5d6e72e9a223dd216a8fe6283614818ff5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005aaad6c2cdf0503911c3a76b909b31c9cd8f0a5992a5933fb8c5ea1111de8da784cb550c80ce42d55fee207f1e7576d6e16f7c4bde8f7a8d689724bb4e429dffd97917c1d0d7b81517435c342d578a758e786201cc97b7f63a1b81ac02964821fa17262920fccda5c3d27d82b3b833c71b7d84397c5685f0df0ed1a4c940b3d9021bb14224a5f6761190f329baa312ca447794e58e63460726f4efc16e8541c6140200d5e63949f0565e9bc545142bc7faff43bba48382ac418c2cd95d3cb03507990a00291be988762d9c81bfc5ca082e47406c553a403ef387d505f2bf0b95c1258927c58be902cc39d116c06ce263b5c0d6d7567a844619f3ec1155924ad877c5bc0300f2ce0254160d67c74bdab5b6bd105afdf6da80409f6b3b4f6ee3865ba6251e03257d8c504632c98ed2ab6a174ef0061265126091312dabe2049ce2c238c9a1520201ab1f8abf33158bfa77efa54341a084f0ab29e70bfec3e483e93e647d86ad15022c0486a9e60fd537addf747877162909a850444c3d9713e26e0ae02253adbf3303029385cf7d8b0b816abd621ea268a743ce0dc022804262adf472c4222f2510a3f21c96776d6cc7b2d6578bf946d06454de385b2ac1db017e93d1420f811ce956737395c63c98aac070483e8f75299e93a1a0e46c6dfee34369eceac0960745ce83f63be55ae3f96b9cc4e9e06f729c438a47f2abfe8b63d194ca55d8e02f8ea35171f375bb3c9b3daaff1fce2159455d33260fadd7f3226a540d2b80045ee3ca4c7e74f64153116e5dd6b36fb5e18fa3d613ce931741e3df917504c7295b085dc091fb6674955ac337a3d60eac33cf12e9be485e36a35816c3a2fd56bb96bd3998420ffd824a4a568018a090bf8db7e427a9a030659bdc06f5965720c354314d1adeef488f57d7d323defb7536998c8efa7640a925d4bd3d0404e14abe66aab97128c820f277c9d5a70d01fbfd57842ee482e5c193ca2a4d46e625d4ab968b07de69589ff97cf629a3cd97d9389470a71e03e4355815c1dd0380a7fc5c717e2e717fbb4f3ae18278724f2ce46f7fa23b3242f77506f2264b4b143582f7bcab5e0f93367a2426aa711e7f3e51f78d8626ac220c716c141323cab4ff473d0178045b2fd1fecdc556a142e00b5a3f294afd7441ea4364ba5bbbc93b86b3a0ae3b3d42466350a1f3ec2e98b61da2b7751610f497494620ec297016cb09203e51d6b04d3f0af0d5c617bc4cbce4abcb71eafe28938932c67bb57c62d7cf53c0f97f7898533d647ad8d3f55f3e7bb6af9912fcb2c5d9a36c0ac3a61583127470c1ecc7a62bbc4d82b0e5501510c58b759e71d16a1dcacbc4a3cb2c32208c14bb6ae65af68bf19d5cbeaf941712532170aec39f2f74985e466ff527b716cc75eb257dae9bdc6950a4aa8dead1e97556873ca7cf47c45619ac42d26792a740d7aafca83e4dfad1c57d5758b0eefe20f1ad5e4ef295cada1a696c39f9fd6eed10272521ac9acb1898df6eaffb80410eb9112b243337c7f6bc3862d23f9007b398b2636098f3c1e5799625c6475654eae63e92ce86dd6e9e20743d33d413e721129cf3994067f7211eb73a58014ba2c74b126c57ab879bfbb47aecd64cd7abb2bc59b879f679e6f9a4dc7dfc21f078eaf68f14b07e26360b3cfc767b0c97ca19f4e146431df6d4b71dd1f8f81bdf81cab414b63d7c88b7f87e1c9bfda4cad402b53860bb4309ed4992fb5627d00ae891fa3aeaac4ddba0ec72643dc3af6ba371a1b73c5bae92f850db0470415eab8ec47cb8611f082170603ca3338ad28f0572e7a171ee21845a4c70378635b487f227a48035f7120d79b020b3ee5163ba3d1f724225f8dacf6cb6062bbf2ac9173b5cabdbd9694a2f0ecab8e09c2d3ea8513b1f482c609aee617ca45ac1d45abaeace3b4f1a44b4e80590b278a11d7dca35cde53adfca33ac601b1a5b9b07b6d9c834d896f83092ba21aa2f71194e83adf70d759a3f22b6b3df62ef1d642f759e2647d0056b3e8380c91c7adf26ad68c52bc6265a1b8d4f0357b2067d81def678f46e35e4fb1a13960d5ae597b35fde26c7cb0633c37c0c672fda01b33f5738599372456b62cca8bcca38b4d63a9c3a40ffa57f522f5d0d0615e37cf28e2d6d9526563e85e66dd32d65266f5ecd5e7b3be5e389e23e326b6bb25f38f4f7c0f7d3b79d3d874490cbb975c1550208ba55812deb2288d2ec236a57751901621ea29933f8a41f94090aa4ef33718ffd031c7c7a074f468d947bd5f5beb1b899cc6e7085c9414d56818964b40000000000000000d94c6d0500000000f902bda5b63225833e5626cca7c66f4d84f25c2b194410df472b4efea776adec729494102a1fb826f5e5f683e359b97c900aee8548b9baa663cb1a45eb4918c1499262199f42ba5c45c2298e10058a6088ec32835bb5f8ea12c18ececa224edd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e986f6ff7dd6e61493a0c9947b21c45432e344a9e61484dfa43b040fdf6442d3c562836d83b417c161940ac9555095426f3e08c794f653b5625adde6f156b145cd71f1a8944dd58232adeb2977dca69780c2cc5dc40671d48c7ff97d6d278c936cbb49aaa65c9d597871e76384314c2ca29d79c39fab5a7670f1982a62bf0190313cf3bf47d0be2a3d10258bc63cc7b77e668cf157041c2664b3a3818d928af10020d29536a7283c1f4e9da3eb3b7f24791c984d49ce1ff43864b493d93afaf03150b0862fa1a0a43375b36f52f4e1ff47e438c8932dede0da6fc9faa31edb428cb3d2b6b80eda189654eb64f960d7f5ae2543904dee4645564c21bc241ff97db28d8021e52351a9e46518343f4b239ed176ad2019e42d22ac8ebd436298f32e797b9aa0212382d044b4e3d87c72fcd324d0e32972c4250700b4ed01c19d125c03285d8da0212f62df597c253d2a049989bdf0e94b1ac673f04964c76ee72f678eb5e5f63a103118d8f9b88caf8fc4a7ab34aa6d7c1afb503d6a3a30705f2e319cf281cc03911031016c70aa1ad435996be5f383d9dce79332b4121c71a446064991fa2f7da527abfb55b65abf1e1d2bf9d4e96db91fab713397169b28b27a484050607c71c0b450ce250830925091595d511a912356430e9d5cef4c3b46271959cf434885ef0a52f200dd69a3401fd3937d70b30ed06b98986a3c819da57187cb47206427bd5ff65cbd3b03a2771409ec52750994149d6a7bb22d2a39f5e3c2541649f04d707f05aa4d22cbe229dc0f5dd601161f6a2801e2190cf505c2c9a0ec14b5196cfd75115b11c5359eb59bdc807abe0af18aa62f65513bf204f41120ca05cf46668c01ad9278cc8136fd8b7a6c8c22b826699099a449c41493b38da7a0426af6622e7343355e627da0bd476c5d0bc8aa2ee923e77cd534df6369ad560b796f2aff08d9ed4f8a9d145f0b94e88b85bf252f14ad3d41a91c0d124b614e2c306c4b84aa5c2761c88d166480944d9f5cbb95c8aca1fc75f0cfadb1daebf4a4522b333acbe0f5320582ccdaba84aceb26c4bbfb41056f40b1725a874861dfc2fa6eb0958806750d954ba7acf01e1995b6e85ace711c25ebae04b0438c783a39b48cae8cb63494f0188f44c305b23c9d9a3ce9bb7bc8a500cdd26d4bfe0506e5e43c890e4d849a359bf1beff8148153ab0f7d8579d03f01b09bde998353047f241643dff0991359b594b1a8adb100429d816baef4433b3af78c77e73f0e1e61a17f13f3d75e7614378f805b8fb755b196a6d46c77fed7cbb18a4e427bdc2e6d212e841048a39adc7fddae56576ffd5a61e850906e568504f261821e9f6f597fce339ef6af8eee95ccde9fa913652d72619bbd955f66aede88c159a86d48693fb4d6cc1bb4616c7faa5e48e7360fa199f92a323dd0fdd8f5baa818d17a91153d17e7ed0e3028440cc8b46c1697e66779187002e3609c01b5e9f35176204c8ff3ab10fb8a1ff8cd7d7981172e24cc2b125ca5a32c926340edc0dffc146a8af26e0adea1cc50995f64a8c8cdebfd0054a06828c3bef8037ded974b59779ac953f04973708f566585b1634c1749191d6ab02e940cc75bb541d6447431cabb20b469f5ff947677a156509bb0619413d99a287ab014e30f41a434b02483398f6e21e49ec39e2d4d3b363f36585c6919f66319c45d9545ad436206375a1e271c2cefdb914aa662f77b824023f2938a591dcb0c16ca31c8a907241ffb04380bbdfc386bbcbc4b2f3ed77ddbeb2e4277a95780f2f630b41b265b5e550ac10ad5d59b12ef2b1c17421fdc939306dbdf5972457c10c2ac3117d3a2001b9370ff8c469b77a8803b5ebdbc248289bfa46d53835607775a82d46aed2cacd814ca42f46ccf9da75205d1e6a36f527c63a1415b3696a677cec35adccb5a2d12606f5d1a33a3afa2706fd5e6664db65934c35f1cb62f3ed159641674fc6f25343187b2740775fafda368fa02993b158cfbc2adb8a1f14c153c51774924754de84c471731242105f50cb71fc92111e5ab4c678a30d62729a79ab3002f47c6f027b2503ab1c1c2061f8a7fa0611f606f2cb7ec05065b95a80a2211cd03b41f74e7642f2ad7deabab5ad7723c8fb0fdefdb777cbdf6cd3cfd9106af75ef7c5157d9178351de8a73a64c8e161e6dc7417b79f3e2be0255e0ad5b1dfd0349915fe5a88e66f0a22c935e6f950492397086b8b7235d242fa3d9b65c7cf6fda3b38a0b910875f60c5d6855c56934e8d43ee202611ff0a80db4e634cf33be21e355dba483ec436b047388a47e8c25ab939e864dd8cada09dd5999752baca296d7d03b3fc77779ccf24c02bfad5c4ba109a8230c956a5e2c14decae9499b50c4a4693d54755df85306741b229f7c2641581dccbec957f2bcf43f4f7162dcb18af23ff373bd04", "ac", 0, -1461451764, 0, "35a6e26039e9831031d396fa0cd42ae1dfcba1efa15ea41b6e93a9ca2bd29c66"], + ["6a345b4801551b6a3b97f67d952f325b34ec675e0d90ed916f5a2386135a28d6a297979600020000000963ac5351636a0000529010ba450483f1d803000000000700ac6aac516353ecd8320200000000095252526a526a636565bf121e010000000008006a656551535152c72d2103000000000665ac5265636548903ebb00", "516563006a52", 0, -1232720034, 1537743641, "fcf97e3dda3235c8e6b51dc3c69fc0d65b2fa87845f4eb02504ebc0097ceda35"], + ["ea5f331802292071de582365ae628963460dda4062bd666a726341efbf27d6e730c3ccbdff02000000045152526affffffff758050af707b800fa0382273cc5746fa370d1f13bd3013164c27cd7737ca13e8020000000452516551ffffffff03b23dbe01000000000853530052ac635352962a1e050000000001001841530100000000055153ac6a5200000000010000000000000000435465050000000042be46e96c6d2a1b3d9083dc8542b9f3a12af4a79561327166e590f28d33578c17eaba232929517bde4bd2179b652749f2c19646f5f0490983c5f8b5c74fdbda1fe65a588e45b2d734e8b49c789b12e652f005672196f45d184339416c51c3970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000085558324707c65a9ab738bbd0c74babb7052d1eb6c7262f43ae60e3c71112e6b5e621b90e8722eef649a180e8008349de85928d836d8f6c435179088796199a582ae8cae96a6e0e77083c67f657819dbf374b3c79daab6cd45b21a915bede4acfb55e96d32cef7c01d1082524da93ce0c2c27d788a6271b55f420311f4064754031d8197f3f4382cc55902b0be201324ba462b944cd71da99ff207d66df89e90770213d8718c8cad5694dde828c1c67cce317bd68cc6922cbb9a5f4c351dff747d880a06d31f9088c06592851f8130d97359d32ecd48fd6532b493d295c2a62c7eb69e141d9d2c7640bc23e37dfe21ea2705ba3de8d7cdac8880af7e1f8264b59f6df9030c4fd56923e767b540eb95f8097cbd1d6c26f7ef3555696cd3ac9636c56a62a30223e1fa96a0d0f30726fb031bba2d1a22b6a1e51a52cec2463c4919c04315e2670215139bba33b2129fb717e466bd468278b9f976f7362ebef1fa53ca14625b26530218499b921c8ad4e62d07e1e92b4f155c86ff9bb1d4ff34225861f8f711bdd3bd030be62bc320c932eafc21fca1b17a1b282acdc3d1d76ecc0b82c7a9ed5c0a94f0d6f0898be40c6a055d983e985b14185455c24a6c2ac30051acda7a3f8528e691e08fc249e84cdcc5e504f9c0d2da6a96d7e4df319998de3905631f466c1d6cf0671b5676f17fca01c806eb1595dad9f3c21b5d2f3d082cf7ec415e58237b0c468bd43f10e0668d3a94d03b3acdc3a57e5959f92144f212e20f2f5d13451e0ed478012dc442331e49a8e731b33a0db3d633d6531d7edbab063a4372dd088bb92bd67a3fe67f6da23177adf145b7a5a6d30411c4fcff00343642b1db72a8632eb38ca1caae6f54b0d24e6b2f294738f95fc569dc4e76fad01bcf6a4229367aa0d031c1bc0751ada21e0d83c39b698f5f027a4b3ebfebd0fc695ed4b774d6c11c9878968106376900f5c10ebaaa603f3acdb28fd74d728a7e187213f3e05ffb8ab0bab00dc6704703294e35c3c4de91f665f65da1c418f22991d8156c25160af3d4e645fd8c655a46415e05a98db9bc3ac8d9186877128d1e40999ad38f4d9e0dd176b7d28cc810cd46905b4b3f66863ccf8a42fc6875e3962fc2619d6b37de442cf5b4caf2c17151a216608208bbc242c887bc51cbb2dd84d4b40db780a22e8409a8c4ba924a2bd5398980a77d2bd66a0c3f1a503e6d03213d618b7b89fa8e219035a117a1aea1d1d1c32f7b7ecaa376b712f784dbad2bca61d2897e3e21365e81c04f7d27a3d8b8ea2fcd9a2d4c2232187ecf2c0f66ba9121986398c6919aea806bfd664800f0202f7b0213a6851078050a6044403e20e03b6b178af3dea37ae82b5eeae0f1120c58ccb3fb46c0e3a17768969fd5ff4949029ef9aa0c6dca5942208b9720a9a357035120f4ebb18ebe58195ba2426d8ad44a2f68376a79f02e3958445c01bc7140ad0a5a7ce143919b75e235020a3753487c63f9f9c619bfbf9e25320ff5c2c92ea99a8fab2b7029423bf57a6a30d8fa6e043e86db6fdd8305e40ebf3ca615e577f3a4628007578678549e16697613ec9097dbbf544273455b493e379880e64e78bef824dd41eea905bde6738318a79d07fcd223d4694da6e7ce68431bdda8014093653f9c9f69f55b46fa845f90bc97c9bf43be6c3bc88f207043706127dabde5f4a29af9443cbabbdb9bd2d4d26bcc0a80a4c8d1dd133bef710d40bebb0c52f881f5228935d6e7606f22d95e226d18461449f394f1535f5d83791b5ecf7351c45677e3d927b18f3bb35a28c97c138a2bddc622f87532df7445bec208ecd44b07639be390eb8eae246a7b07553d212430181d21b3ca2e18ae08356894601e40b2a06ce4cc178e7b2cf00a642c0bdc1dd86fdf30c9a835b69a05ae33a5e8a84acd7e698b876a954cc5b130605bd86c7d595ab39b03f7763c04b99dcb73d7383ae6d02940eb6ea5eb77b11c9592b7ba69d18463b4e9eaf67cf66cd3408798ba1a7037c48075aaf09e15c7d7928be73806ce8384ef415576c0c2d05482403d0e0056152d2b190e2ce12590bd2388ea48e3c2eb41f97cac0f40e90a6c33b871bd94e3e873480bbd7ee8b40396a505b305b84383c39e5cf98464b04580e73d96a1b83a442163ef06bd4af89d3d65c96356971ba30b835e35a090ad1f0b92497d107b847006258e38af9a54fdb89075a57f6f9f2c132e46bdfe196908053ad1efc9cac9dedce57738049f37efcbeba739c665a34dad08842b977f4ca3e711bc4850026cf8adf5d1570661e52fb7898bca0d97ff57a0fb6c0c7b24efc9ee6a8197b8613d9c13190dce3a0163d6ec046c6b87c519320e73327f4bf7ebfcd2fae7c36fb84c27af34a58bd9b08162561e5ff12c6a0ab0e37ef55ccc592a92d5094d1d949b54742584f1e6feb1e007d708", "005363630053526a", 0, 299713053, 0, "9e0f6bf668a3b52d63c1490bd1e5097561a40b03743466523ed83052dc3876d5"], + ["030000807082c4030350ac85754925097a9deb5ec45a8d1f13802a004e51a8039dce252525229eeccc03000000075253526a526a52aa69bdd496c117e0b42549d5f891f0b9b366879963519dc9e037c58a0963f797094796b60300000008526a51650051acacffffffff9954d768080bd6881278edc48869b12f604d3ca8e243b7e752929043b9a3f83a0300000000ffffffff02bb076d0100000000076a536a65ac0051b49a9f030000000009525151636352ac5163000000000000000002e2274001000000000000000000000000954fd733cb814f3f2b9c25b4ebb3daaf8845e504048f2ec4e8fc2e9f9a9fdd79ff3e62956cb92cfae968e54a5e0f9a7fbcce358774d7453253e22269957927ffa92b775663a2169ae647c8c6b1c0c150301e1677a94638924ef7a21cb2e5269400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b20e7ece78494fbfff1cbbadd78a9b959bca0e4c61b419ab096279d832435e98d6d3a9dc9c397d0bd32a20643479156b41a28868271200d17285292d75addea0ac1f55f2fff8013a6bd19046477b293fc14fd0e004345edd6ee5f8e0346362d2bf91966c93259d1bcdf5744aba85aef90aed6245fed8a17e8283e95dc9bca24e032de3adf34fecce7a7530c6c6ed483878f58c8a1a51ef2b8ad66e1e04bce0643f0325c74626eca125dafddc6ca8c244f6dcb241d55c9b3bd505d998004976b0fb330a08d5d82d9dd567c96761e2239d781cfcd879fa8dce5c9453af7e3e75b37a093f76a4bf43390bb13ceec305186ae38c004a88436c3b6a025220f029a5f522fa7f0218549175d4ca4cd7b39652002ccd275ea6d890c4749ca6360c072ab04c30888b022883ccfd2351b21461747d19f4a6b0c4580ac152f1aeca8a6018aad1ddcbbc6203022fc7193686728e27e4cf9dbf8159f36ae9b71de73902fd2dd08b1b2a4091cb0213db4d657635fdbba2bcc21c33601f8503a66835bc101c193c1c1c77539429220204ec481de86e6ed22d2084f74b358ca9db98c2494b58448ac385738561f04dd68135b70481fb910b12dbee29b745b99150363a82e5be06948274f235289a7d25e7aef46fc58052356563dc953e1cc01dc270a8371d25173dbf46a7644cd6eab5c3f8a08eb7a488513ef9502a377d63620aa76fde11b83b38d7411ae396ab0a089d83e80ec5c99cc3be8fd93b19304d8709c51f66c4d0b13ffde4ce6320a8ecc8647b630f31081eddc28050b0050ab0c026d84fed74c3e73c7cdaba48fd79423d5be20f7bf80b368a835fd601ef72a8304c2936e484f628784f77d7a585d16a0001f229617a497bed7d7604d5c02c2b30cc51b6131766b10bc726a4023c73b72b5151874b333d594307daa6bce50d80b2998fdc5aa95901c766125f75db1e4dbf894114fdf5a6e81f72d8e49553f9a6ad05981916d4a23691aa0717e10471c84753447931721cf66e314268ed6e1a68146c7e0681ac0a4358263bbf2109b915a83bb43deed97405c17bcff5d7d45bbface2cf5774442fc2d88b49285d026bfc5e0649adfa9d6028666d6883170ab8f749daa36accda091158e12315071df96d33d6b3d6b75e2cfe016bc0ed73debbf35519f7c936c3249ae195013c52a1b11a448e23f57984f4796294dce76b41937123ee1a02e79cc4be40d5736a9318ac5ecf736cd683b86b715c65f26dbe9556fd74f16de540170700a462eb474e0adb5772958fa601d17fd9f5ed33cc01cb5bb56ca860bdfb1ac43e9645cdd0367d3b582f67f8e3cfc0e46040a4f7125515232c950cc92a789abb87d833cb41d8ae8d0a90ac3419311990a6ef3de2c528eb1ab0964d696eb7a3bb29da6b7e6ce04d0cb8b6f701b8f5e6f05ba8b5bea570f0a6e81a4fc9cdf8f117e3f2be14f02f1142934b6100a9a52a2b67b82ee418123f9b829604a31868e29d90724e0c0df77f936269cdcad02a98fd76d254b10837ea7a5043443dc67050033ae5da5165c678d0103ef0e00ec4d2bfb775152815fa03d5f7d456610a0984dd1d3dcad9aef53cfdb23fe1e792673895b025bbe6e8e39922d387c657694af03b56348c9b1df17742c745e4f359c45b826f1033a225b1f9f11be6e4b597b4092b316da4831b64a195848f8489ac7a2bd58d6257c279bf0e9fcf871e0114e5ca1c5a44cc7d2d8b3ea2756d3403a3ddd9bd3b235e2e7b2faf3123a31c825d847dd3e410bfb37a18f10c5cfbee9459768538915b0fa458f58e17cfdc94a5e758e63b7468072364c2c79cd509a5e7812ed325b9a83926ba13e05bf37e61e78dd5ffdea468782ca522e589b225c4138967ca02d235f0321401b38a07aea2a5562cfc652aa8dbd23fca877a0b23f6a82fe21a1094954ee1eb3ed5f63605b61c69d7f4fa178ebf5fa93da982303ab52c5a456bf95d3967d52867f4de08df4253e6146c28f26b86f8ba0a66e3ef794d568c1d02244b83ed815f7a2057ab2c275eb56e5ca876770992734a7ea3dfc764732a06451dcada21dc623d1c9933d2eb07fe5fc71e6bac740be9131023ddd5fbe35031d26ec2366f680ed79e5823a7bddf81696dbb2cb8443bb12a5a1c872a01ff528b02a8cb77cedcf43ebd6175b0261796a93c2cf2f0f81763480afdf2782f32aaa29f4d65ccb681d280b78bdd9658e7da8dc9e42ee6200d2808c683e79afbf7b9267047e1ce19da010e2d4ea9cfd20116518ec027458b72fb2c13b00bbdcab22d6d2f63ba90fec7b872fd8cf01d9144000000000000000015685202000000006dc167c0f02b58ba8f8e800255a5d89f2093296f2c28e1adefe95b4384f8bb4336dc2cb9f80d9dab96e2f66a6612abb2b8abe54ea96bb218135044057f85ae8b9740b3ae9d1d064b100274edcb6e9157a152be179c1065ecb2a8ddc624ebeacd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042a449b46f6ad534eed9c52fc119586192b4a7a755765a24533f247fd53ad9f41a4307943098ca4f699b9faaa6475241fc7be3758180c9cf3837dc20874bdebea7152433fc250de3e2a95829f91c776aa79ae0fbfc6358b10d66c89d901f86fd58552b6d3b2bfb6e568cd5e2417db46a4306cfdfeed7b9ccba7ea8c0d7a6da480214622785e4ecb460995f4df052c53d50f75d8468522e60067849f865c78f435a022079116063ff0592d1e86035accc21c2ae3d5d47dfef9a58580441df6c6a8f4d0b01ab37002c3dd4742ed87e2c292b4e0f3ce32f540d5a314257a13a096e03426e4b96472c54e73b19b6efefd4bcf3f801ea91240cd9ed03e8060393ff66c2240603294b355f3f4c853ca4fb9b72408f78089e7d4650d4c209e265b434c9fe04918c020d95495baf6928cb61edb354084eb7d10b19f63320f8864ba7c1aadc7f9068dc0225a6ffc10fa00efd9c8f8340fa02db6b7c236c08addfd61a8528a8c5f57858da031502eb4e99e45fd0c6367b89f495bd6211a464edfe65da559f4cea58ca57259302244e26b0b7589517fb07e2d0d04f6afc89e06b94c76aea5d3f7d7aa00703070518e39e071bf0c518ef08863ed6f27c66281d0f5e973c34cf656601592b22dc2dc4343a9a3829697701a6759647b5fee44e33a9a2efdc197fd0e641fb7a20ad61d4ff6f9132e04d79268db08b49a017265ef7183c2d8f14b41dd08907542fdd02305b4bb5749d26b7ed9bfedc5d49e761418c0668530cd61e07e7a2f955603448ad9131de6626ea5eff57b12a0698b72cd58e9997d17588aa2a87ef52873e79af272ee91c52fabfe6aefa8a1d051c5684e1ad7434fa7c5512f5238d37df0dd0afe655a7a07bd93d5eff7a93f73d1b7bb55456a4d35766c3a64e8b3ddc0abe3e0ef0d8f01cd38a277411a491c17bda5937d4e00391e8aa3dee9f8b44874d391d6d948a847a8b900882c36fddc021a9fa1aced720ef27388d7598a2131e0807799ebc7cc8fdacab38694c8335570ecbcd80b03768d6df95b36aa64ee0bde3487713e7abdd0df7a87f6094a2fe722d4ff9f5a98eadd7346ddb7cb7b47602b49ed425934a4405733dd58baa00a7b7c9c3d9a562d151356324a5e4e643b6a219d01ff9f55b6f89c5185ee9bdbcca38427edc4ca8fdc689cd4dc4c9180fdca9152cbbcb78b23338cb01559599eff01ba57b963032c03175722172efa436d39a9055d998c6177649843b419fd7b1ef9b2d1eb3109e2e88e96daf1ca7ac5ab88fb851ac26eb5b6c1321ae7cf6b8d85715ac5ee804101e76c07de7ebca06ad5116d00ae4ed92d74d114055d202d9dd1eb8e3d4eca564e764fc33bd2fb2e27d7bb01fbaeef2041ae3e9cfc23bd1e6587108c88d840accac160ddbe80836c9f5534c2b0de08766253fa0b69ae4fc001ab103b6b62e957ca8db85f76860fe1e51c905101660320d56db8f141df4b973a1f747fd6178d8a8d7db9552f9647ed9fc4e268e2cce0c75b50c0e482d6c09a40ecf2303cb57cdb6d0dbc8fa1ae38e698483748504fee01ff2538695d7bbb79fa53c8c6f194a4a54345a42af6b2f4a8341c176615185d6967d8ad0e7164245449c851d3a6326a46154e711a2d23a25ee98c26a2c7be8faa5722270ae5944d5dcfad0f80e9d7a22375e40af52d0c871273e9131bfeb09ce9ab0733abcf8e68e1db8600a43f63957d46579c16a6e2757e18fb2d05a6ee2de59a161a569d01eeb20d71738e3ada842263ed4f5c2ac348d6b207eb6878211f6b90649db6dec51e49449be2ca70fbe58b64e4ff513651472180c3ab79b2b898c88930051c02bfcdf4f58342e01fbf0e3c1e265349a110d34a2b033f252ed17460f78c03d463ee91a9fe880e78f34b00c8b34a7fd7cc18c7827e1f74f1bbc06690f67b1ce31923ffb82aec22bf8e11e998611a438578f7425c21ef99930f62d72cd0cb283519790975592e2c46bf4cd573887e58953794ab2cf4ade8407cb7392abf2fc917615a539361f721ea133ab96cec67159fd5ee7fefefcf8a47e1b58a6b472054b966a02db9d944b23f1526af25795652c278aa3a0310cb2f71245050eb923730e1fd271a9628e069ffef4554c00eeeb60bae5d7388423f6d1caed60ab4ebd35135933397f72fdc3548582864f70c0a4dc5606231983355c8e50984542545df1bb3c9de6408db2b400ab3f8ef3310e8cc9db47ef9a56cf5b5abdb8cdc18712703785b8a99a93040359ae5041444b4ea5f2b26986027fba93d5b6b8e6b263e017d5d49ff3d7bfc1b769f68f899a7c4608d8e86aead7fccccb17a7c4969937352f81c32f90b669cd5b1a24f0c10e53138a0a3c29cf3b5cfda1b5eccab4220579ca3291641367cf5a33ea28777ea8ef6d7bb9d0edb673a634ced65b332ca900b933acbb8dfc85d3f1d87122195bcd6806", "ac51ac6a00", 1, 1596960850, 0, "55bc69369fa744276ba89fdaac2c2c6987d71e1401174bb9fabc7f0b9557c5f6"], + ["030000807082c40302ee53e5a06bccde1523c4e963e443b7c4a2a7d19ed96243b8566059ddf91e3ad9030000000952ac6a536a6a525151ffffffffd078f8d895e2575c00c2e1e2035139ab34a1e2dcb36f7a978bb182f01358d6940000000001537f15198c018a037f050000000008516365ac52006aac90c8fd6b03e95f9b02000000000000000000221803000000005bac2ff9dcb8f50fce2a281941d196cffd3ee602c002a0d37c87ed7d30d207aa281952422873270e8041962a903870d2054d6a5e1863bce2bae770cd3db6fa3e377285eea0e72fa501215041da0157efe1cc86003ada99b49e2c379f240ab37900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dba818a2f0bfe5fbe82ceb53b7a3a5e9d275f02cef20721c1a1c19cc4278115ac49e07949bf1614c06124731791c3062ab64826c9f773617206115c203fbc5043677169f276ac6495efc882e9a15cfff75504800f3e2aaf444a7b611f42a09a031a14e9168184a7f7b491b1c54a337f6b14ad829978516391f1e4bf2286b09d403069bf53106b3addabc9804d8f9fb1bc74af5501255e6793c19602dae699ebf390302f971b4f9e03c44b9104655643cb06ee19798d2cf7b2d8c9e6a45c4bce753220a008d44654caa2d1f1b87fb3fd1257d45074eaeccdc3865feb2b0923feda9a557b72bf40768b5e7efd09d12de383d2df0a4d24cdfcd472db772295eda436f4c9f02043cff44b4203525f5ef17a1e350d3816f4f7dae62396d69e73b5b819aad625a020c9b0a5b2695b0b830aaa6ee3d48200740c2f26350e6a6a12132460ead4d4de902269c6f98cd7c461890cad8585b5cb5eeb70b491bd2efa0efe1ed13afebbf548f031cc6df2e80e32ac2a974d60a4a24fa80e72b1e29c1d8372a70965ac0b9e408020321a62ca95859e7038ba562f4ab5b631ab92732a632ac468f4c2a99b5e9ec0b5310f65526f61cec1b0b1f3d3b974545168625c5e9702f12a83933773d9122742e7eb5e870caef561cf4f8d8b5b710db8cbaeef6162247308518e6be8f7f40fde52f4c732a541cfc93e585568f4b1844276d4e6aa78429084c03e859f1e5c98845745e5e0ecda40d2617db30af89b2e7392af2cc0ebcad42dbba19532115f6cedd96515ca778bfc68d8286247b9dc1c3cf8831bb146e2f93c0528c75020bfa34d8d4f986c231cd7cae82183ed5d61767923dca7d306e360f7d3bdf1b33f61599cef24504e65589d87950ff2c95e0a6ada8f4ae602b21b133c86460b59b43206f25049ed63575c821f53e345f779b1ae238a062ffd5029ace6940dba7251d609de70179bb30205da12960302f90ea67b4d850a1e407af45099d83b7c12557a64933448c2b34b0ac7c8fd87d21143ff098875bfc0fffa79720b4c57972416e7d86994e9e12c419a77f608da858c8fe8b129ab944c4791820465e05105c51972e1c77df217133de1946507023593f3b7cc6bd58f4b645d282f2d0fd03db62d5c5acbd61b927c26d73138fefd73f5764f14382e133dba2842ffbbfd124fbf40029ba34a6665dd1657bda83fca45e02759b424796d125d732b2db5d74898e049a7d53316c25606c3a79c97c950924a658f80ce73dd76f34933e7379248e08322fff23cf8e33b4817e4e1bb603d104e197159326f9b7ab6b876fbafa81e831b8f3f203cf4cbc5c6feaf0674378e6902943067d672316375d3d0a6989ee00d682414e496514d7fc8baf552cc284f22e579ce7cde038a369e9e6d3a4bdf10ccb11af94b9146244866704cf23eedc7582815bec9abadeaae791df1f6228417bee9aeb94ffd1110ae3cc6a300bf3524095a98732cfeacbb3fc0911fcd6230eeb88abedf43bb8a57117e55d5ae06c5cf96efb28699d59b84de69bfd3b1b82e591c346034d7d899269caf506d641965c75adad862e3327d7092a7c7339a3779fc5ef5e25376eefc77d738983233f33e9dea8097d6d0783aaa5a16406166642b603d4f7a32f5cb129386a45134bef18f87e28cb7ce5161d03db7ac8c4cab92833a237f905de9c91e134a4ca8eee5ab5d582e8cae3e8ee76ded9bf0edb15fd1a3a926ecd1d237fe50503b17965359a3f581bdb3574835652bb1ecb26f57c2e94758b107e589217c7bb6d3507ae35292c1534fbbb5a632ef98f7f008565683c19dc52cdb0961958d125f390324c26365f304ae6661b812947cdb1441f3b8a741ea4d156dbf3a252c8b657538940e8307fa45e1e5d0f87c91725bbd0eca560ef1773048872833e1fe4d7cbb82db6007f9c71ccb77af19d1899246daa62d5273fb618c62d02132d2fc0de08e837677d6e7968020a4748814c1d00ac075f84818d798918c322f27a5eb677c4134e55f648dd0a3077fa7ddf684450e4f4a59f2132ec14877d7a16f627620d7f30571db05aa6bccaea8e463ccb5032a5833c311170defa45b4803e966c5a1719627b60b07fbdd492f391d6413e796532ce789e077ab4f92a99c9735df2ac9666b1ea3bc2a213ad2e7866899f3c81317a2c0b295a3190569c6a67f1cbcaf6fd39c89de120d200e4f77de442b581e966c2b23faf6e82f0b9138310fc421c2fdabea7c822e92dc668851a86b71bdd8870583d30c0447ac69efdc9c8fbae197462ab3d5c5ff3d09df4f590eaa86bd2f17e9800000000000000004d4a930100000000afa867d24f3afe53c722ec62ac0104c7c7dcfc3d3b09f9ce2e0390618b4acf3c2d4b9789c6f23933fdb98a360fb638399da0bf76e6fde88409ff6b1c7d274e5d1951e33dee7bbfa4451641aced18072b057b2ab62a922b01524cc5e66a62fb7200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0d1d1d4292f0404118f091c881a0611605f572ddf62a2c5812c28e2dea888d8a33192b7aeeead704900a9eb306090cded98ecf2926bbc60d236a2140718fb48fe3721186e5bea8328c083958b76fb94dd8bd11c86c4406e78684825f3a82ed785aaaa1d3b8179867d65d51f3db1c2969c4ba1b3ecbfbb329b4086c6bbec48c90208cb83d1972c64d71fdd2e92a1dc017f6adfa0daaecbc2b87c1d96c202c0d59302031902c5eb82889415407e71a48308c439dfb237efa4a817aa477c3151e1274f0a06fda1dff7e56f0c571bdba00a8206216ef4c9bd1a56204a8df5085f0b15be4597fb8dbe323a45130a0fc1c4415eca9f86625f3ad491d4c153eab2e4f31f76e7020232201024d34c32deb98a4f0e241c76921de92500a2ae2b30fab3c24f4d9b43020a878d72d196253cfe629ec55784b118e6b80222c0fa85f5ce5a361f280734a503136dd89347116c594aa3dbb031fe88610853b66468408099a30eaede8cf5568b02022718028ac18585b3749673df828a4b2d8f115b6f6d8bf426636499f94ea26402105d9964a1e8df9250a10a5d0f41dda2e882d7340d068f52c5d53d9a26f2cc69a66b57983ab8f6dedd136344466a9272f73aa6a6eda3b035ca1cb808666dab72d42c941a9fad84238abb696b934a91fba5ab80b96c45dce75557ac09d0cc15ded3020a26ce136cdf8626ca5a11b497874d9eacf31087b85cb50d0969f9c8d8202f3e33282d8995c7cc0bb4eaad36037cb2613702026e7c2ca67f58aa63088bb4a929a0b0c075194586c1a177266595b1bcd9c2467753bf24c726fe6a89b8d911b3dd3dc23be7561b1c38eeaad28243d17f21cbcddc9dc299f3c3939de654c5dacbf0713632e92d7ca3b41988a8bf80556aa6e304d98167a16ba5d3b1eb5b04d5375870ae2db070f812843b5f3205dfa9007ecd3f97384ad6a8f84ab70978a81a66d4097187a089ab9c12d34a53e58c8616370e9f7f983eb43bb54afaf869eb113a6e48a27abfc2ca607756173e5f028fa57e70bb30adefe206f17bb65731e4a0ccb6320fd2e01b21f3e16d8fda5ecdddf0f8a1dc1ddac7bb79e72f7655f96360341faea948284454f614d475dfa9028695a04f43be995e5e9f7bbcfb5b9c14932b448febfef79b1736874c467c896eab38c0ba3d88edb77eb022c28e382bd6c885d0135c205baccd49682b25b26719b755559143464dc3c4a322ee90c725521e77406d65923f3d6c6e01427a9c1d6ee7423119ef2bc66eccbc62b70e19b69dc3dc3fe7977f8ef38b7e48c86648e70ad686b73b7eca980adcb85e556168d91e25e6252d9dc757fd605ea00a3bef8655ecc5abfe2d17e843be20a7c61438b0c3c7b8852eed390bf3f1d593aa0ae637c92fa7ba8dd9e04a3424624a0d57e8bf5b082ccf1ed64b5f46fd2fa1fd726336dc09b308879df43c341a00dca809284b7c4cf0b8bb4b400ac51bb088b11893a7d7e4187f6c9fc8fc3cbe41d6c939b2a9e8328eec66c9928f1898be9de0df554027214df3bea5131b4821ac0df3ad8484d252432a1768680e62164f23a134f4a3fc9fd86e52b94505f7d64d331801832d47271145ea48ecb2e2dfb3598e7ccbabfa8ee38d44101c99ecdd5e1002e8f021ead75306795af24b3233a105e55bd6e2a671948e3eb473931465045025b570250533280b9d5e1749a6fa6003cc925ec4b4a66cf2a1047ea88ea0c30c4f71a201d69f22e6f69d7b79cf59bf46fe52254b49cda93f24c7ab9685810d83fd83a06c23c5e64c165785be6bedc47ddc456a653914bfbf96e6adf939cfe3d03a155d12ba2b50de07da3042d2a80dd05aab4e19c7f750674ffb6d25603e1a005270e8eb88ebd63fd7ddbc46918e8fed9414f57761254768c9502f83105ad932a0d012d81dbbc9d35249876962d6653ddfe125e2507533d670f76dcacac16a0d3678a2b639d6cb0fd1c43f742c8b17c7fc0275f60985f609cf482d5653aff9d82ba226e6991a24d02e441c934d8fd51dc3122d6974a633f519033afc6aa6ba3bbee4d647bc51897db1afeb8d795d365a8ca8d9183a283cbe1a06b93d59357186b53eb8529a906605f3a5a77b39c75c11670c2476a1492c1d0d5e19a120ed2c0b3f86e4210078720a4272c7586629cd86f8a17b4d801b05402bea321faa939938590163e42b5f1394bd85b1d27f2a2243bc003fd837cde263481074d69ec9ea03dcce375504020fc1c3f715fa33606d5366c2ed91988de498f553922524774c49343bb30a889028d76257321c0664152dddf711cb4738dda3959963a2f4c6aed8cab13ddec7a8205b6c80c6d99528480dbc8c6be358a2a5cfb7263a6bc639efc69b843817a87f40289ed4826df4949aec052ba61a851cba83158e329e11fe00cc457ef86f78ad6a1a26e31c692608dda54a78c43fea66090b", "53ac5365535365", 1, -1509103470, 1537743641, "2f9f7940d85e006a4172c6c9765986c8b030fa8a29e63477d5ec9119f42b9315"], + ["c0853d610468d4eb527a0a242fef86eca4e0eb94b3b25c624fb6d7cc0ef1150280195506280200000004635265acffffffff4f4271e6b16d08eea21a1a979803182d4d2eef83497d55c7045a73b09ad5dc250200000009536a00655153005265ff97a34573852fb46383d6047a268d005076b8cd8067d156c305a85d8bdbace142ced3550100000007536a6552655300ffffffff90b28ff77fe736349fb2ac24f947479cb6b891ed994dc5f6f1c1a2d00587b96f00000000086a51ac656aac5151ffffffff03938852040000000003006300c734000100000000046a65636acf4fea0100000000096a656a5252635152000000000003c8ec6d00000000000000000000000000e420f76308566d59067988d1f1c0e84f48f96aa0943bb6592c6bafe64de3d11ee790146c62a401822b9498c27ea6084b3cffaccd2de7e6a82ab1ad52c4a3352a27bd2bf037da0a1a9a8bb02c58e77928bd8ad83bf9c44cd19c213549f37ae9de000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001376e7a28fa416bb05ff04a2fa4bc83f5282364ac856fa23addb81d90e43003431fe866a8afc6c5665be795ec24d86f81c5723df1c45cd941b4e2e654a8497a8694b97a1d284b52230c4ac9fd9b7a81d0c84e51b72834c916abead75b798fd347b340cb57c7d5db460d11722bccfdcb6546ffbc5453ec0a85ca13f62772ce61f031096ef60acc53e0ef2dcc9e740182355f920308b29ca311ea0b04f3b80c0ae570227ce140cff026db4e4f29696fd635865183076432e429ec732a419215c8d2c190a0859042e53eded2a7ae880a508e9c63ea77d0f8542642800a0320d32e9b70f890c34030e9f593b6b06960766bbe0b6e0d024754ff6f646de4daac26740fce5b202182e0398a2d2cd0cdea9b900863e7663c04cf28ab74ceddbbbf6aad45018ed7a032504670b6a26ad4a23528b6aefe86e1b70332867c101753e146e3244084ad01c03185e4538b84f2dfbf3354ff4984b34c20afeff6179b20435ac161d6255d74f51032f05d15567cd2e674e6cb00087c7a1cdd2512e242b88a37a98b71e7cfb7012de020a7d31efb28626e1abea679503ea51d94ea5402086defb7fc91a946fd43f4393c240285222390627bd7560a46b4fddf1d195cb05667e57ec2f66ec3318612974b8f37547297d655338d5312226a207b9650ea1e92e34e64f8d661a5983941fc9bb41d11e100ccf0345dea1b811eb692525d567ad4bd086de344cd0bac3aa6225252b84f700a5838a025ce5580e995eb128edc6e0fb71e1c8dd13f0d33d8aa804811a762a0e3a389ca7f02c2ed6b287c8975ef1ec1a962368929832653477b3fbb4dd736facb429e4cdb9b1d68cbaedfa05a0923430959ce02d01c964d7278b8e6d29e889df25ad92e898fd94f762f0b5ab939a1b42a4583c49255db5fa0282b6ec3751428b753873a5f24ccfc021c68ae5a68683543dee7e72f8b0efbf42c10b620b61027bd01b23d19b612d74bbd8cfe239b591ba26432b0734f13d16866b23b636cb6182a85356007e4a7f828f57679e502f93b6e5f2d67af25a87fd41023c83d404f5b07996a3eda495f1baa9a22e7dc5982ccf7fa4165bbea00c3a484f8ff579e06334ff3c367654e0c047cf5560937070652b40f6c04f9bc19424095024239a89420ab80258f3bd8a1d4b6adf4516f0ce21d94f0778957f4bacada975af1a02234ca90315d5c33ae471514d74746e1d22c003da1f76249c363c101aa4a07e1aa919ac05edbb84717001ed250dbc564f0dab5c5c1c6efd9332b0c1fde0996f44cff5d1591da644a2f3872709d335922b8391bcbe5232ef7f30c18c29445aa32745ef38e3e10dd28cb4908fc0c80d6f5c016e0469054941a00c558786499097cdc00f7dcca7ea85e1b05b97f30ee13a0c0dc37410e0423a8b1a6a634909fa9461a92c794b976930a72922fbfa402c4511ad07089a3251149bc77e20759a517bf91038b1258577f42c6d2baaf1ca43de9b637f09965c3c6ad19b3b7bd368c881019f53c119d139ba754c48bb4df0c10612e06fdcdaff09dce04a55d2a56981825b3d1494848eab387c517f401d80dcc3fff94fe178275b2c50539cc5ed1cb886d5bcd0b4216ec5c76ff79ab3172a346353acce0bb3975689b3dacd1e43ff7606f6f9200e686d6c83c98da1b1173129727fbcf6422c7d12ea9b2cd34a4d370d16b6d5dce5b14151bf7556c5dfcc9592d206fc670bd9f62d150bc5c9827868d0d358433c629f0548b754aa50f6daaaa7c458f285078d261fd926303830c0b05b91d73678e6474e6f02bbd37d42f614be361cdd33222f3d5ebb3a734a715203aaf26bb8f363211767bd6d4e1292957d439c8ecadfd960e39aacdbaa5a883cd897b25c1a69e863be4fc76d11c7cfcb51b24eb5eb07633a7a5bcc6adee4ff45a57e1953226ec85d23f1579f399e8aefcd0bb58f976406afecfeb2d9dc7877f2a595f3a28d14c92c6d0bb61d708f4bfe41a3d2ee31b6c11c2d7361d88e5543d9bc00cb951ac6f529fa5e580de4718a8fdebb068848d14229fbec74411eb82b2e061f5f0d4ca86bb61e6168e8852e5959942d0d01a1be7df4893fcbac6f1180a9d3bec76b4f7b772549b3509913979d6d22b850a4fd0a3297e45aec9cd9082efcbaaa5482c9a11076252c89c18a32b8f8d387adb6010633e3f0b5cd3cb04661e056371a2f8d45e4acc6b899d4798bd55244ff406b2f94407fb2bbc3f550495dfc3c30d8c4a7d637bd1cb04ea5c81376a5648b96fe0845befd8ef968607e30b8a30e6f200f2aeec360314e67b20fbe5cafa98fa3960000000000000000b5b0f70300000000b84aceb4251a643a98bb2660372e3078c852ae0fa25b8f5ccd193414a3406a4e67bf127fff8c9e1eba28f538566e0dccfd6c8cf1a6657e98ca91cff0ee01472e6448b9d2dd18ff99ce589839ad21aca9505c556633ab232abe3c9bfd73d9542100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc9c4e6230a3baddaf0ed752c48de02c2f45820227ceb649ec9a607b6833417725ae8f2fe5138b6d35a901b79d4911bfc7470f75694228f3309bdab47b68649c8d9e6eccc5238f4717b7361f0766079895cb4e5431a082b8975d5781430ca7f152ea7ed2472cd881441e83a53183f2122543cc29af8a40b5d39f5d02e4e2a12b02260a68323b3878d74af8b438b10268ba04f07336c31022b2853884cb9f17d9ae021bfefc1d0c3d3f4660a94f27d9d537328bd3e3e6a2b17c4a0bbc3d4d22c892930b0797a7d9151c99d84742f47f9a9dfd0542c6f11d08c8e92129733484bf56188b09ca885c295ad46914d96bef4d38850dcf1df8a7ccb1afd80b21feac2a858d11021a2d34b5dc9508d166f5317586550d6558b67715224d1db478e6c8ce9809b0710316950148deb62d3d3ecfa7a4eda0dae5c5217f706abd8695c2dd1fa9a936b399021adca3294bb0faaca0712e640f3ff3c68987284f2afed4a8c1e96c9d0f684bba020107af492a77dca9e656f8073e6d011444634439839e3ee4d774378cca21e7020323f7a470c25bc1df7003fed0656a577c6697335b097f737651d7eddd3450b7ce39fb4f4f0643b0653640a244830c8b622d27942b9e325b022e85d05135ce1546db6cc8edf7cee84c8f5a3b4fb8fa7a54ec30f697afd3b7d55fb5867d4795d70e7a367eb10d694cd9afb61dca5de9525e22654a6a9682c1458c37800da37f25283df1b66ef31b7f5db4c896064258721c7a4391a79942259242ff765f4cd09b66b5d79fbdab8e3017a933bca6e79a1120afd60f7def9a417d80bc451f08956c00b74166ef3343f283905e824cacb4f237dfd86f4f8904ee9efc80004743eee3c382077ab63825eddc78703a3f194b2b617366101eb6933126bcfc71b0fcd3d89c374cec275f370e01dc6ab5e396042dc8c841de8dff40f11a09c598756c25904f2e24927e2d54a0a561ef477d139b102bd4b7680d28a4061b24c9608eb6215c984c4b271da7f54da86c4cf72e28ed8d7e07c7a7cbd7803142869b03bbf86d6913b4ddfa860d9c04bd622b43f96c51f00f40adad46bac4ca5616ec2d6b08907b58df0648b63eebe0186c5f8bf4ab8030134f116921cdd5e81f5b9d2dd88cdb096a61b6f1d58cb5cebebe59bcb9804b3eda335e1d268e6d3a1bf8bc9acbf7da4a9f7bf0d1d2780f26136408beed5e61e84fe598204c82cd5c147a558d726e7d1f24fc65a25f334872afc64aa8972927799047b3563a3d3df8905206e2eb4fa7cc8c8328785e81d8b29ce424c3ee8de10aa2f98dd377d1aca6fe6f5d8adcc20690ffac327d6dfdc9ebe6b311560454aa11d18a5496724dd01cd6fad5e85a1fcb7d810ba4d41f52bcc8cdf6a194d46d47dce3fc4a69a459a08c6bed7d57226bb45e986306987fdac412cdfe15ad298341707f46e5d36c481109820a4bc9bce9df91e401db71d6f5187498f9b6b6b8cf762ded20fa9b0402724ec7a1bd997a320f18bf032c984c8b4f22fe1d0bd02d3644311091c01c9655d28966507b7ba335f60057f27df10723389c6ef466327a318fb4071099edff15fdefe81ffba9d104df67012542ce8b67079c1f1800280389cf760bad845cef6ac0c96aabd478640064c3b424c9a788c8ce4bbd03b5898097df12cff86fdaa00d180acbc3566abe7007df38e8722a005f05d0d2199b8bdb90b34fc87341521bef9c0f63903f75b430546453265b91346c194f976a5fd9cbb57334a6cff8a9faf21b2fa4433aaabac7194a47716405eff94a5059df39c63d98197b0b12bee426a151c8a2ec73200e6b6117f937046d7b85ee0e8fe19b2bcd7a12ae7bee7905dc4c3251ea79b24b5a05b745b906c86c84c0de9fbd73679323612c1634a23f2976451a8e04ffd7de615cb2281b339d87dec033a1dc3485d343b254153a8b2c2f3f00305d389983ef5861a34e405b4aee29bd24993d16c448e106fff6af1db9acdceb0b66dc8affa33d8b7cac844a7024b68f10697f7cb00d9ff6740ceff874e62b41c129e7f21a4dbf3bacd8e23e856320aca94f59da158efb85a659bcfc609c2da52ac781cad9ba18bbe499e739f95c984c83d6e4d9637afa3cee7638801a3102e4404ef48004168d577524a36b84275bb6fd45f0d78fa87af3109c2e7fca0e79af5ca91e0f0ffe11009fc8c6bbde16d32e3af2a4aede75d9fd261fb4538319d8f4532010ff255e902c541ae964810c9a4b6de5aecde7e606d94bee0f73a6d881a7aa6416adfa1424f34506f79b741d1e104c8da6b390bc8aefa0c2a37d36570958040000000000000000000000003bb12eaf98e63e7ee2323ab9d7d42781a136f5d3c11982598d043d0a98625150cab2ee50347173688e206058c469fe158ffea85e2cb2e114e66508b11110a9ce79bcb25efbe3c6fbe07916b4e3ac288212d24c7af0b0a44c09323227eb93502f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008f1596b0962146c70973ef3e97edae8577c70e285bebb8ff83157ef5483248f186579064770fa955b30e99fa416a29dd47d4f63a6f457845037feaa715673921dc4ef9e9dd738b5bc551375142d900416f9c1c4a6487f6da85fad224cb156766bad754c3c0b9c613f54aed2da8cf70f7c80e8dec7e4a6eeffada5f762c3c2ee4032b7f88479e8a16e787c2598018debaf909c5600217deb033fdc17941b0ef47cc02210689467461f90828a87630d7ab7b7ca6cde9e4325a36498ce1e14b3dfbb4060b048a5c917c1ff5b914018ba38a1660a00d12a5a911862e55f510015ca4b9f451c414ead3f843e0e7fc5d32a94505e8564bcda2fa9da32ba77eb1cc94701d4acc030c238373d6157f39bd8a7213afd7a2340d50d33dbefd81a0409e85ae1337d95c02011ca57db8032821ac4da6c7af92a4aafae60e36ce5b3a5367ef5f9e72b2a7a50210fa65aa9792426c15ba0067c6658d29f3855a43d9d6de26345be102918c6a5e03189f100537094e49deef18a56580ec43842452cb0291fc0e48ea1f90f3180d500314281cd7d7d1ece65d015cbb72f53dc724c39e12387fa3e44ccdb40d9af44639d52401c36213d0862dcef8d760c62b67830490974a956698ebb5a2456c2f01ee3056fc35407c2eb81c2464b5c079924eb15b726fb951e8c9cde6e93ca96e266d74175791bda0cd1290730b302eaffcc6c8270b88d2ee64526c36c00de3f8a99e1e180d4f83e2473fe6c5da08e86a6e5c96374fc21722b1021ad4374b2f2939838d706fe7ab5d69328e42252df283bc57bd3cdca435af7c726b38672fd6c5e92276629b96b809ce42e28f3df1f8502aeb614746d34b99d748592b33d678801b80c051d1989ec4af36815d4c802d63ac62c4c64d31a396da922c4aac2a93aeef3cf44e3ab19e3f9f5ea3e0a90003394087ef8badfdf440cef35f7a3ce71476089b1558eacf207db0a26ede3930c18af69aa5324bc97f3a43bb3e5bd14852d1ac664a540b4b1139e0afca1917349ad281ba14e6e9fbe2751bce2547c8099c9003dd8812da5c552966682d3cbc4698e62d9c8a42cfada1b7dd37d49018588d18b4b744acc5c7b37646dc07743565c412ba6e6e93cb9f81b01ad27d8dab8e8e1b94201114080ebf1136ff7b70c344d2e3103f6e621d831ed28214c4d82ef38b733d999abd963b88162ebad6303230ddd076bae33c538179029cb4c7a0063482aef0cd377bc2f1f525591e617423c0210f9dffabf80f39b1737e7a55511e64e13ca8f52e09a523bcfe9298f4d0ebcc4e5213a6379066c740341d8f283fe18902f4c44ef2645db6112c3062f58238fbfdc02b2a73834cbb877d993b42dbda585023554af3bfb2b1b1aa3069ccda5012dfeae21aebff4eff6082e6f6efbc187c9fb4b8bca743e556a655e6b904f1b2d9c2e56ef4609bd3a447265fccf119e760e28161175e6c83f5b82a7c6bf82067dedb0655e2918179bae367e9d826c7bebcc68cb89a904c2aca19e3b6d2acd09cb30df3521421a83f035091ccdc7fb087eb366e9e879743836f5fe7a10a9ee29509755533200463e0fe3da6021d1d17acc16bd3c394fd2af2d1b003d0788c75d5d58fcde1874e056c19deefd97fa2c365274fdfcdeb885d2744088521b49d2b07cf1022422a0dda8ab13e27c2e041f371139f0b7c90f1b5dc03c80bd2d2d242c65e9b939e8cbe63e0f7255335c465094625e39c97ce615dc42cde15a2dd3b27b0d01683d32a729ea8cc398432f1a8a8e3cdea06fea93599eadf7b3c8a20378ae65dd0d7d5590a6a454d7c75906b173fd7cc85fbe9e43892802e6b150c840d0dc007434e3ba911a76c9d9f360048d0205204f946364cd0fe191dcba95a9ad4ae4f13c4d6742ac5bf09dd14be8f1b9606cab7dfa501750df4ae2bd39afa4a032d382a1e7bf2fbae57cc150455d74947910c303bbc9af95e3cd3424bb04f1b7e45ed54ecca0cbb59a651ed9f423b248f225f40cec3a5c24360dff350d7196c02828f41064af346f6f74118659ec057cd299b22ca9d9da04dffbf651055701a192e1f120bbb738e8aedc5af7e367fdf6014e20bae49319a3ba98577f57a333ce26c5711bbd69b08f90266dc7b870e892e7af11e26ce633247848006f398059647bf51e32985b13d1d6dd7da8eb6220bb64c2b49d128c95e06b271f633ced797f0698b6ed52ee7a8b15298c5344eecec4b51a425d40212e261cc2a00a90ba643da65a41f7350842edbc2cdb88d813eb583e34ae16ff60fb9a6ea2fdba34c646574542a2271ea9ea633e6b8bb5c0dd6b87f8b4fb4d144c79af12bd92468b219c7e7861bf56dad89d78eac2ebc78da5cbadb4650d3bc26cd18b43c884b42f5ee62264392afc47d9ee2bb375896947e80630435f4a65544a1520dbc71657d5461bcba30a4b89a6033409305", "ac5252510000", 1, -945032891, 1537743641, "cc6b5f5459a8116c05003215d256446d21a7314adab388367f0c01a9adbfb505"], + ["030000807082c403042d2e865bcee815db24dc3de5a11ead170b8f596e5a49c30cb6319d923da8d3e00000000009526a6aac63ac6553acffffffffcb4d7623d82306cebb5952b89e1599041ae8d659d9594c25f57ac0d5feb8c5e20100000008005152656363ac51d656dc19477c7b2a3eed9615e3c5f8a690eef3046fac803a7ed046083ee12aa074204e0d020000000851516a63536500ac8406fefa3afa7749d82369b416be987094c7cee9b4574b3026ef5021719530257d508201020000000153ffffffff016c7207040000000001ac000000003925a0c20178a4b60500000000000000000000000070005759b8b75fecce091714aa5226969ee3f7d0b9116fc250853c42643fc84601ed5acbeefd1e0d346605ebc8b6e9b482345a39d6f42861c853be0255b52eecbb59b6ca6e508ffe697935f4ca96c70eba8f9403b574608bb0bfd10ebe554e610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051db51357b034384109f4d268d9c12b96bc2d63d8d8c00d53bcaa41d44b04286dee5e648d6c8fc40d8c45400d6fec8bc412b30f04f467a3638c15d47e2e415dc31978684ad2b7f9ee516e95fe5d10c7eb6acffc1759f28b81e662e1633f3704fac8ec97dd79ccafcb6c12da716d19a8f05445e6ed11803083b5459d518648d3c031b6a98cacd3694d7fe80b2e96b749f621ef7e1ef61ba841ac5f676465120a4af0214ee9573c13a727143a3873d0063be0a7c1ec2b7b611bab601fb5044efd22e030b08e4679dbccd7ad03ca9c883054440cf36263b3105f40a76a1f98cebe3438b8d9a9557b84b5cb212c3417c648ef9c1e41c3ec208e1994917ce0e6d4b831f123b03077568bfdd566093261997e764a1aac7900290d52430eb028d5d90038867a30d03238f388302234dd8c1834214dc0155555532bb5d3bf3ef6692adf02b88e9bd23020f168ae403408a8ca5986dfdbddbec3e61d3ba2e0521304750ff18a7d586cf7d031062e68facb013ef1974bec903c3ab2a34404ce0b11ef64a69832a4c7de9de2a0326119c2e663d683c27d6e8575af049cba5769cf1365c86352ce01ddb5a8f65a5b3f36a940de0a207cd1e514c163b674d0b237168115daf749804be992278440ad8bf2fa1ba3de9f833875ddf34a9f33147bcae4afd71dfad4973516526e5b48a5fabb3ecebfa58e5fc29cf2987101ed064f3208b0e06fdfca8806192889740effb6f3e48813a5d58bb549e58471e86dd66aa8cfe7d0ec01d4f0cf145a8aa48e35e3776049dc116863ed6402ac0cdde16f2c3271d6964e696573234e76bfe5d6adec314fe6955f4af84e965e556f2a056be924b1504da0679ec800726fb1ff43aa72b2b6da3a11f081b5fc5baa396fcb890925aebbdbda11ee6ac99d4eda0b2207d59c47babf6486c9d2da6f0264760527d064de3842277a217228612d2c5a5d1ebe89d393afd8797c9caff1cfc968ffc77d6c6ae2c96f8a6712016c339d0b5c48092e5430fd3011ef38e2e79c11653b97e80db8ea471744eb941909b83cb2a3efaaa9e4105c9d3e7baa876a7a29a326ed600ce15c6f48fe2cbe38676d897f34f5e8b342e1d66a3c66e3dcdee7d68132b043643837000ac16ed357ce478524389a2a05eb82ea3604a1ab98d20d3444da78029f4628e76fe78425a3f791493bb9baaf8a9b5bc9ba1fc98b7a5cf9d50308f0b07f0d70260401f1a52c2f0dd450ead2c9b018bd44972232234e2ecce34068739d26244007a9d922fcc83bbe1cbb38098f8d2dde27aaa3b7a4ecc743321b6d5449ea00c7719ad837056f9d7026cb3ff425e28525d0a5fee678c1f43137e727d057f69d182965a00fff6965087e5ddbcb68121edfdf74ac7a1f9a73f6698b28366671e3ba6744946f727d76741a1990914bf7c897f8f08a73bc96a758cdf37fcf55973c2b6b0551e5bdeab6fd4b4dc4ed7dfd2f2e3d767fa7bd666335d3d6b91be3cd82525afdb755a956945b0970872f74f1fe8560d590127f24930af21263228ff09886c06e07b9196e8161577ed20b456e59b15d6bf7066dae10dfe328dc206c2f707b20e50234657d590818efaab68b8359543cb65ed0e6385b3ddef100b340adfef6d206eca112d6db7ae8a9dc2026ce5a27ba9d3c0b9da73de30ba8629a5eeecde1649ddba11c45c0b0e2c7cdf655e6525ddfa790b7bdeb353edfb200e623672c1fc4a5b2708f55d2bf10524848e898283da62984aae2e2875dfbf916dab0b729a5f63acee47f4d31c0b58ef888ee7ec102a20ba1ff14637dd9dc87d73ecf090fff68be91d3f14e99c790ae779f99376e3712d8e6c895d595cf45803ed5339c7e7c5b3f3bf6209045a3b6b5aed6f63749df50cef77452ac463a849b1fcc59dfb08d52ea53f74d6233076354070419bcfe52fdd65153147086ed1f02a0a23522736081d2ba610f6805a54aad895b7b554349e8e4998613257d37d14ee2b130d74f5704b1bfa57c7ba6ae262cc734605c19d1461789d136862bedd8b1ec029cc314c1552ea9f8d88ebf3544fbbd448dca54f71be3f1a3f0cfde5d797a764c227dc57be3ac4f4ee0b91703569592d80b99aa35c56c85bdf1562ebfb489af8ea22fec36484b2499c36f6ef7b937d4a4bd083889f54799d52c1db1a4e6079a72f42a70fe0e28554e0c0c1d86c8c052e66b00f5a1e8da59790e71b5fa38ced111c39618968032179680fd57fade9d6ee27c74ff2e2fc4daec32e36d251494ce0a2b49bf264f2421e8efd2eb7c61154cbf4697c2df8d60fbf1d6c9cfbc2273e80645d345d3fbc7442688b1eb059976e96b9fb6a9a4aba4882fc5c698df313a9732a268f4cc84bd1ed0f144ad9d73ead1faae2de14080a8b3ede769e91068bd9ecd0ad03888a9cf1f89485ec97471ea69fdef72d0aa1cd3384db9a8880d2369fade503", "6352516a", 1, -2078586765, 0, "2d67074df94fa9dfb7b9fb61855016694e88cc8c3274a13ca9e209a8dd0df65a"], + ["23df1d6f0233397426427ee6770aab05e88415373843370831b89bc6076c69c775e2b5ed0a030000000365636affffffff2154396ec0b1a1f48e2bcf462d0506434bbf57fe5fd29f66097204194f1dc0f1030000000851636a5251516a6affffffff042081e10000000000035251518df6d1040000000005ac53515200c67ee70100000000007a4e0d0200000000076a0065516a6a6a0000000000", "656a6a510000ac", 1, -56634375, 0, "4a75061d37186e6948669ff36c1139c0748968e18f8f052e43effa9db3dd60d7"], + ["030000807082c403021f356a7682fdd348156a1bc6f2bb26f9eb4d739f4534d429a76d60b35b592b67020000000400520065ffffffffe2350896b2b9d38e54ecb598d8cc14f575ce8b2ab4c203d4f509fd2ad73354520000000008655163006a535152ffffffff0499e527020000000006ac65516563638d9e1a04000000000900ac53630000536a515d2473000000000002acaccc19a20300000000036a63ac0000000000000000010000000000000000dc16e6020000000030e9392d363cb9a0d7f821de1ec97e86158ce6769682c66b7edcd623eef84780a5a1c200a385ca7eef4f38908b3de0de85a0d9f79dae35aa7d1ebd01f72e087a17065f68bb3c7a5b4ea12ce205b53fac235208ee25d01ac0bd33f2877d0f670e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b277666229a8fef0d8fecc1ccb8adf2900fb124ce5f91574b1ce6eb31b495381c106a2477ee7a844a9d03e82f9ff02797a6ad1164eabd872d89c0ec10f867c95d2c6dda707ef4956ab1a7099fd5a8e67b8a89c2aadaa4ddbb6f664025635634945d10711ca1b7b2d2812188a283d65b35a0d8e17e65fa6a7a031383b6611826f030415fe9682b15e65a040005dc19b689556c22d225b50c0eee5ba8c199c4d61ec032c30a6c6707e5120bc2d4a11d750f4635154f37ee234d1f26684e28093ad53d90b079cfa04ae476124a848734ca4f63908a6526e8f9145b3448e10c4ad0a3647fce13068de76a8bbabe19d1343fd3afe93e198b3c6aa8e3efc677a8ed0bb1f8cb5030d4ab52162b9b99f9ba8c16914fae6024d30020117db94da6d25ffd2466585c10226df1c9f9f646caae869325ccd23c2228bee968f2eda561e661a3a9c2d412624020bbac9f3a3f4223215ff11a312684ffadec8b4c96af04a3f7f9457f104e0f160022256aa890c4965eaf32cbc90f64379a2adda490e39cc0e7c02e644763cc337030317176cfa2a6c0f5c55587e89684572ac77e06e48163485e0a598076e845769a448fe537755aa7fb90bcb3ba6cedbc64e2a8fee5a925393545efc3c52ee915c0be76db97f81ec75a1ccf7ad77b3cc0180b4fb125bc328eaa44203e8c9f87427b05617ee85398266499d9b730f88ef2e637658c26e1395cb51ebf189186f7012db1be45b3c8aa604cb0100aafb40d2a68562673d273d4cded5b1b60329df4449bf4ad435c3c3352093151db38f8ea77fb328075b35bd8f096668323d02e2ccb97be36670c90766e008d5f13b3ee5d6984cff019acabdb28988ef1ade103f936eba9fad883a74f30fca5524d41328d39965ff60fc66e5ece1964a2ee723f306a6f3c32dd3f28829ed8715fc525798793848d0f8c1273e43b3b4d174de1b586488fd44f93b082a518f4a091d8754ac2ba3320e49a9de41df1bb4dca7f85e6d46f8b6b3826669027e5da61de832518273201716c560fae9a1bb5d80bae5d45d9653acd81d06a101ff48aa7425ad40d8faa4d92088064b6ca74ee2b3227dd9b72d2aae28133fde9a7a81deb30d83a696cc3f95882d957967c01a20129d29de3035b3bef5441198a95c0c9c37cca47e2e1f84a23fb07413427b8690f3660a6c5b0d7273758a9c2e0bc78c06bf68187d0ca7bcb14bfa77d762d3414e7a6241e7f2168d73729fead945294d28b4e6d052094a7c1c48238fc636460ccde49fb3e5a6c7a4a5d57d2c6ed0e55384b895f2b3353055bb3ed6528d5fc12b548c120527595fa2fb0e44046719e90dc30ca357719afef51e228eb36cd942c6c72f3d4c085736e71f029a4c84f41655a3b400b9919e847c286a22aa2b3fa1da542887cf716ac21f305211f643330e4ee094e81b72d7c8f52ba8480696d03f2ab5e6af4bb94e360856ba92d87111c39f2862206d9669d3878a94d33570602ec5242eba1ef33edf74f2559ab81dcb3d88c09aa46657a67dc569d67e69eae18eea5ea47eaaf7cf7fcd0a140ad658498aede06b1e4a1bec6ed73d67a7d9696723eac5624caa976f91307d3317b91f841862c13cba59686f53abf66a71ef50e4ee7829dc55f40bd7672c0ad5a48f5e63d4efadd6338c75855284f0566124ccf57ee1bc80598d6a9741db0d275c83018933d0beb6bf81ac63740ced37105d1d381318917c318f2a779073a7cf7b55ba1fbc96994a07a74ca89541fc06989c36d0523818fc4a727502b383f2f9784cf8cfd668b603ae2673aaebb1dcd5c2bee631068d9a0abc7b353a46581bf351e317fff8f461eccabe7836141fcbeafbb509cc4ba27975378507dac711684e3e0c6bcba998c4bcce31235075a79e4f8da29ccd3684ca22ae91f7303ee195fcbf8b330e3a94f33e34156d3b36ce752d784da24ecccf2dfe1d08d6564a100403db576e53c914b1a0f22fc923121d4963bf43803db17bdb9face99da614656752f21922d0518a0a2a62dab62d303b74b8eeb90a15b777881c76f058d649dfcae8b023f582756e6279ff19f5e8dbbe53e0704cf2f38b22e37ea89c87f17f9993e87d6e0ffb80a2bf0e3987a9b178d66928261ca2bad12cd1791fef73c73f447e908aa979451f5938f796aef2892a9d7eb70067f9b641e39929d195022a324a702203ed93d75cf34781f9a45be8cb71154242fef86dc048e50abd8f9826f557baf4435b3d53ca77b7214244595352a80622583d59c903442c76f748ab8323b4cde0a0afe7f39e7f36fe135f3990c2e63d24adb75be3964972efd8864317185e0e10f70aa73d168b59ffa6e937936a42922bff94acbcad5825a39ef33d071ff99ed5315dea6c2da4f7fd8a96113c719d0441383aedbbd860212086b75bb534d92bdb7b0169b405e2471c9de295a896f837fd0b", "0000ac", 0, 1778545437, 1537743641, "7c2a9193f3ee46881b4d669652dae4f9972808b23f75ea4c3f5e99cca3ba21c0"], + ["2f41e16f04d349f396e38a90a6e3fab44f4c9c2808c8c3443452df87dc88988e282fa29a7c00000000076a636a51510065ffffffff7127af2238487f2f4f34cda8f0944e25fd627396cb37c5d36eabf9f1db3461550000000003656a63869498654b1f8a54e280b57cb1bc5a39f2c3f9aae82d69e2d507fcc5ccfd3ec6b56c40940300000009636aac0000536552acffffffff791e5520f1f75d6af9da5e8176a5241552c951a25e9027f7c9a288ed7bb84a350000000006515251535251ffffffff04627f52040000000000e9fc43020000000004ac52636aad2d6f0100000000036a63acde486003000000000853656aac6351526a0000000000", "5253006a6aac6a", 3, -1452687354, 1537743641, "1f24265b13212a05c0dcb16592980f58af3752fc51dbcfbb0c7b0bce1a9ae556"], + ["030000807082c4030311ea826e4987bd7b049a8d68d482be5448ae90909b1772e425e5e5328eec4dcd0200000001acffffffff663ba66b1cd01e1b5a7cc46422ab0738fa00e9387c9dabb62fdb5672c803308c01000000030051ac32362479fa97019113d39641188781b29484a7354706fc2cb74e662a549dfe254002a4a30300000003516aacffffffff043b53e60000000000007fbad7040000000003515265e6a8760500000000025163ad268c04000000000263525b2bec51000000000100000000000000005c4e8f02000000005565d691e4ed4215ea983c23ad2690f5356c6f7f5dc1b7737f4d58f9875fbf9d4dc2d0cdd8128f67d0db0350f1790b5b8715c71d50309c309fa98666d71643f778b9ff6bb7df31d59fda221e6d8c213d9da731030d73a02ac175b4882e9337e100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f9599beaa4372cacb119590b9f4f64979002bec8f4ab1443deebee41b9bffa787e36bb671c76098c45a392aaedc34940c2f642b78d1d2931b31fd163fb88f271993586f3c60a2fa104b46a06d1f0401751a3e7c39fd4742bef67649c6608e7109322b18825a156e68faca93eae97f2ee41b1024059a77b4ea616a41a5c2d90ba0223f2a19fb4426015a0afe14bec2576c891693847b3d0809272413c6c463707aa0225e20f5b1b94b0cd3f3e598e8bc087ee506958f3eef6a86ca2b5c6ae5ecbb1790b0723024a645a8c9fb57dde1291e124a79d609ea685db44443db740a83b9595f437f570dd8866c1335f15a0bd1fcadd8858f03cc1bc81ee3ce08961f2f7466016030644f99c73035060a1408aa7e8a2b7ba2fc040141e19206e2b01085e6cfd24000200c61cb46a4804d038fcb2bf0d7cd2d0b448a63065f7adc72d9f7e2b79687bec020f39ff3b1ea1cd96a38afba205e204346c0503cd4259d5f617ab4f4fdd004e76021157347223b7c311eb706867e421fe2ccf86f3d8ca7df91b7e1ce94f7807273402146c935749b01f1485e2f25c25619d64a5f1c8ad81ab06685271cb9ae0cb89abc8a9a189d15aad8690ac9d5c78613cf4debaaf8fc32bde24d7511279037cedc6f229148954a8add89cfaa9be1497d25a7375df5faafc6f3ad2c7bbed969cdc42c9738f196cf426185e972065213cc60f96ea07eb3c5383eaca0cef79d53c56dbe01cd7720d02e027d9fa983218342ed5a0dfff59fc8eb1e7ec83135e0f468be94ecc4c7860adf80cfbbba6b9dab97af2f0067e4f71427f80939eec2e593ab27d857cea9d8b19d34439bd17012cf7a5077ca0842affa677ea5078e65c93f6867d5cd04f230ba25c2c0dba4bba66f82943b3018e79cf7cc8b60fed109de3114f117efab693143a8495674f748512f3ed8d780c7af874c2dea908e0604dbfefc35a7636c653aa1467bb1f8e889873e3836a7f8edae0135fc33ed34ec4690281c0efd705a12c079805258c19a7b445a148998bb1141ce302f186a5141bb5dded404928c0d005862d9c61c09f9674287371bddc8737827b7509f395eed39484e3c4fe1cbbfc8311489395e817730dc28d7652255cb8c2135fb5029318b18a33e6ff8793da09dd1dbb7f3b54d40a4ad1c39abfd59aa8c68b3a51f82a549e384507cffbffb803271761796ee4a059b5c9ba037377a34ba80340305161cc53ed63895c96fae3f9d4be1b61b656e682aa219ce930c1d65ef74ab11902ec42517c197bb2b2ae9b9a8e61d09d6f9b64b5e329ae48fa58b771f74d5a3281c675921361a99f5f597e4e15e16a10c1e511fb8368273edfdc7c0468220b62d9c54267fd77d6279e417f7b345a01d9c6dbccac17f9ae4a7f44190d81e85fca12ceb3f5585c94db1247a90b3240d70aa3c9394133e9cc7134d5605c07b095aabaa17c8a502ff2fdf5c44fb8869d0f35fb36025f31d4598067804967c5a06524d5feb50c4c2d1d0d45839d111de2ef7c1496feeebe438f86e5f485306448895f3534a71c78f766d5828add91b3d5d7f5f219c1c71135d256c3d3c75fe0c970f818f9f6c8c482c312d5791d68465006a1cfe124468f214537ee3e0f4cb82d2044f98ad3a720b8a6b3c50de29906e2f83a41d3dc1dc03c561e6e05e1d924269cd9222ba2969d1a07435473732024282b3c5216ade05a61d1455f87059b373e6e3ae383bba5791bd1b8f0893ee1b13b185c692ace35c413c210a32ecf772d8ef04a09ed51376779baca3b100b25c7c30fd74f01e504212ac76e732b573353f735a2c0cbe4308072593843d47102ace16b22455641da07c03a77086ceb42955fd7a9da10c51403c821a51cba1c5eab515a976036384848899f1139e16f4439cb841528c8f962a19536ba35300d112cbfa4be1a36840ae8ab43b11745812e4464e8387d7c472a5b93d1e8cbc4fc3e749c9624d4b0ffed0300c414f6e285e3f54c2b5e49def77fbd92b9f8479bb1a1741ab5ab21670ccc58c01c0767fc9f785be60d79e4dbe7a9660e8c37209a5be6140171189b7a6f68f37c286801379fb3dafab62a2425f6e09718179546b180744b201ec8f7bbe279b2285c71922fe8864fae808c1bec51d07ec1dd2b8f648068f28daa9cb3696ce1d926ccbe29439087080d098b81b009ead62cb74cb7867465db5b4aa622da6fdf69c8cd72ddb62fb616840d56eaeb13af4b7fedbddad44893a08e559338758cb55609cb58e3f4588cf45d1f541d5d9a75fe6a63fbd2aaa9c844d7b40dca64c9c3d0824ce997f55c4ee7976e9834733c521991f9c8156327da6b3f7b36339ed65e41295aab16450dcff092a52b2b82a4cc85202f80167ad17cd5d680495daa4ace0009c79a4464675ee02371bb740c9255069c6b9354dce574b1b9ed9b3fcaf45d78e7e989b8d604", "65516a", 0, -1320400433, 1537743641, "0fe4805543af44e350a97c123508f01f2beacafd1ae77cf5a6bbd6fb99baf450"], + ["030000807082c4030282dbea71a979db913648e5c4f57a0040f2b5d22f9dd5ae6fec6f141598f74b9e02000000056aac536551ffffffffd6fd840356da78245c32e332a7148777c04668ccdff3d622c388f81642474b170000000003515351ffffffff015aa656010000000007655265536351acffa529a60f75e9c70200000000000000004bd52e00000000001ff56371344c1daadf1ae1bfcaa01f14130f4b0f0df72fd3eef89973103e16fab4223e82dc528c23e8795715d9be128553fe6cb3b89f2905a271f2e1398cfcbcde0247cf2c1305a3f6a30ab4c9d090af4b8c4f3310b45b4bdeb0d1bba2247a0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fb8292885276a5a84cf8c3917fe38e8b188de808c015f66260aa5fe3a6639e3b1377b17320ba6c6b2445154f9d99c857c1434a3eb8ee2a29d48e0b99c4daa91a1723fae4ca064f7bafb651e45011774aef2a96b888b0b39f18c8b3a26418d0dd4b37d65a43132689f75bf14e22732a028d51b0b53af0f2a604cc52675cfbd8590313a8a37f32ece894dea82c943b73cee6952c8869266c39457d53d67fd3c702c2022c5e54d6a5decea24f8092b6d034169a66ea34fdeaebc5c5b5dacfad21ff07210b0815f6e09ceb376b3285f7f83b6801b3974d173d9324e21ec55aa093a25b0994793786282bc2817d98a026493647b702834525b08799e94e9e77237e559d43710220b507600aeae4bb5e6b3ba1aa24e070b5df902586bc238be4c44b2bc9ec1098031600d8e4ca90f5249b3adfef1ce3b159f4f13e901372a347817f0f627fe4f41a0203f79a0f91c9a314876a4a8d95435111b773f557e477d5754ebf8184e31233fa0307b2a585b2c1c81051eb851522c9061ceb7de60facfebcf9be5cdf318fe36d7b0324509dfc16e351441e44bba9ab0337c7a7941c959f29a12550d0a6fd7b213b616a80aa905b460e644707e8ab715d25845490e0d68ae335e49321a5689261e29508852ea3bec1cbb799433c223549dcc96a9d3b9cdf40d202b7677080c747836519c2b4fcb3c03a71eb28a288f31b6902545e7378443cc30dbc3793f1aea526282896c38679e1378f68018e5a678cad408541fe16d4a8d35b56adfd16a1bc48943345a609f65a6393e0de5b8e21c46c53e96418474c3b65a8e30f2b040e9c852aae4643b5118ec7efddcf3d2d1c601240f3677216034a2639178494e2be9f8c043bafccd359c7f7ea1c2a28dafcce3bac8596a5e57504dbb6b015743ef929ca2c00ffd114113b9119d49774da3e0be28408f81b13dd702cae194aa93831980bb45255e7160fb88bab2220b4f9f72d9558405724028b70b3770f602c138fadda7c9bd3ab0e10aa14a4913f71987ae323a8752ccc034fab6d4a43d9dd290c10135dd1e1b492c38895b2e64cb760b34db55db99b5416faf35250cf0bc3c1e65046a326abf3018259b039c9bf47c09e01caedade63d6e0e6915dd5c9270a240e7f8a660c01906152540c12d14afe996f26fa5bf965a0f0c18f140d6c33cdcf71c9e227e673b805abbcd820a65c06892198cecfa8e495cf531ae216f039721660e8a7894e8b41c556f8a38f27a8644689259eb1b21449f846ddef648f76416ff1b440afab67eb21c07a25949d084cad2fab7e974ff8f1f47e1aac9b895ce4617b6e4025185c7bd09b8d0266052716933a19797ae1075992d8721c621f8b3418f1fcdef368d64951511fe5abcbe99663de750a9777fff7704573058baa3467e40c4dac528e2a9640dc6f303df44a83bcc129f20fee588d5db3f95e061559c7ac1854cd387f6ff68fd6525f4bffb1aee0f66207c1b0a4205b19b2d5aa7534c2707d6d9c16ddff9d4a859784d0b39c5dae41ec84018eb4e39872449f5ec9dd7229c5b57ab0e60d37b5e1a660757f8f1f8baa859c8e90384e45638d0a49c32507780245120a83caabc457cdf24bb8e2aa9bb2550aafb5d1ad589143e829145ee40301f7828ec0790f3cd885d8a1a968dc78bd48fb40014e0738debfd77089a5d82312a8deddb029ce11a24de737b49c150ca32098497d55454784f6be2815b069976011d5ba96194617ab5fba6bb1cb973f2576d94f7f0194e1d730fd94e5dbb14b6352e61b5af70b48637f5ca64612c25c65502dfa1202b45c3007e4900aee6f6adf1312d84334f549753817e4870909903f19f159dfe67200acabf8f46ed8a235249c5711ca53e9e0eb4562f9ba783578ab365e654f6b165818dcb03268748f4f3c0dd8f1abecc3c7edb92f5e7d1b9f4789e85de92305f4b319219fa60701a5c157a4b865f224ad6a297974a8129bb4bf1e886e600be1d731024b6b352b6bfdba843c22e199274638d2cf939f47c9455b529f8a9a60e8688fae2ed6ad2462d0312be6b754807669b66a1d32d485b62be3d7957f5c28d154fddc76b144ea9fb3b746325bcf9eaed64fdf91a6be19b91f3492768eb510002e26506ae050a57faa4c2de8e141fecf3cbc8b96215637df61aec65e7ed9f67ea8aa1b3137ee9e065a6c3172b601991905b9f806de452490707aa344a35c8419104fbe057a6087fd509d8999e88c4ddd4e721f1243bae1663f2d568e83139775707bae9036236f8b5fe2a0f2bb3be1b76ec4ffe36632af782ef3090de3aa7fc9fef3d0100000000000000000000000090dab05adb2255f63125f4f6f582814fe2a821d6b4292749fcebf0cc7837a18dfd02d153884d9a267fe1365d58d9a9ec2b554be2e3cf4b5d053fa30fd0b1e2e463c978f783d57ce4c1cf168ae7124b4bb35eb3f155b687c06eb45abfcc17593d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037b20df5d1c4c8560e8d9c1110fd41c02bdaab5a8bc5b407d729f44eec3caa1e36fa2f77e4bef258c910461f3acdcd63f0daf361022d5c515cc2fcef8c7c893c877977ba498e8150c2e3a1972fd5a18854b393bf9bef6e01126b9a3422e86004ea90e0dd029a16e3202d2b1d466c04e1ce8c539c0d7d53df0d43203c4798b28b0315eaa530c3f9acf7da39c8142c6ba9a5fbd0cfb6534edf902e7bf235e806828f0304689c86cee65ea4d0258857ae7c6a4f5d9b98f16412b3699cdae6c132382f100a047b0592660e925501fec1a3a4ffbe9435ccd8d70fc434de470b756c89b713d62847dc5ccf79f14bc3f919bc074ef5a8be0e04989309f6a2e198458d4cd848f70218ef44d69e65d82ef931b51e74438c4b1f068a72320fe022174757f8fbd370610302056a7f4ee0bcd2193b8c73c844ca0be1151bac7c8ce556c70058b9a273d8a103078f1cfb6d861025789458eb1908ef318da7b8da578eb6062fd3571ef828b7880223ccbc61e527449a0950f30dbc61c4383e2a8726bef15f9c6a694491e459fb29021745ab975541f5824e6f0c15458d35d9403cbbe761d42fae8caff4ba00543f7e807fff10174477eecf6c4f95ff3ba42eaf11a852af7cff1be65a879a0f688d32981b2fee59dacf60480b5b72f9b6b3b9525b6e2b47a162391d96270ba1ac3bf9cc8019c62991fffe4d4b4e720cb3f2caeccecc77dc37e136c02326f1516f33727414a9df76c130153a61e909b2280c12d60d1eb7e2b3cccfb1615c04a312fac98476e7f9119c036475f67c6902d3611d9c00c92fadffd921b90cb67d3a12c49f977156b5e6288d112531a6274df92657664f87b9cc81f037f6ed0b0e6e5d128b4d6f0e355cea1d6fc01d73437c04e072ad6af4f7834081ab8688fadc9bd8ed058a08b53578f4f91205960c4b6f677f140b1ea7642c334422d19bdc1e5a689bc33f1b9e16ec6a6a4099d08e2190e7c0c0a6a1dc1203be462d1ba61f8af960483b6d840abeb33c0660dffb7f50185a1f6c7b7ebd9c3241cc906b1c57801f56c6856f12e3abb77a641f5a91849f2f700a8e22117cdcf62c4f51ee5a2aaeadf04f18bd60b7e4ead8e5a8cf5df4f602cd81920551faf616e9b84700e3d1ef9df16db3e05476b79613d6cc319fb720984190dac96fc50354a49b8c6f358bf70cb41195c6792657bda69505059c6e8b60085576fdd8879c119cdf41d9853d5fc3e8b83eaea5cfb2d339119286fdb41acfa3f9b134bb24bd2acff769fd1968ec104f6db9e908d727ed2d27118fb1fbbd69a905d0123481a9a8789973b9fa94bd0eb8a8e521e173025d37eed002f279d1a15cf6c9b461fc8befc45ab8752ddd910d2ff3ec6eaa5c54a4a832487901da9c8be2ecb3868dce6eff3bbd938dd2616c28abd010fd5f4e4bf6119f7cfe5d6faa8264c07addca7260a9538cb17c04704a45d7e2f73879ae8d1097e7fe4f9b8a839f460df3f9d4d638213bfe1caa9748d1a562484ca6b03f69573c2baf6ad46bd9a1c1076f416dbf466cf07c6bbcd4588a6a8cabbbd2337bc372e19be29bf46d53731a8ad2ce021c7b794d007290fe630cbd09e30a5a4c683ad2210ec9e3c590b847db37ef922f0b388aa0ed913063c59818401a91c5d3fea4deba9d0151ad7644ded438220a3283ceec17c7b69839615d9ca20142bbe590278817a9dbc64f4f25ea0a56ecc680f3b03ba4fbd3d3c4f78a4619757529358b2c67e9bb06068f73300eb46310ae10662adaf02acd020b7178d25e0d4938a2e2cf6f3980f93d0d252381663dd79f8ca95077b0630d5c2c89ba86b47ba2d45c509f9d92dca00084d25b175263ad667fe5ce20164e5ffa6dad22847bf647f5fd1d30b0e97b7929218be0fdf3d573537a8e745b8ffc4ace81b14657c345ad9f8fe95215090b8e2287b12d78e72b7dad87dc3080e8ce5ca5bffed0b7f5152f6ce4be4e7cfc6b60ef742f7093c967ac1eb39eaea6c433059ca351df8e8fd64096c3fa7b42f1e7cb1d2d0baaac604d61d1dc2da951b0e54c02dc2800308ee76654d358a793f72dfd16da70128972ac28ee412db998a6827da7835b9cd9b3035098d06de337a5eecedd69bf0d536791dbe870f221c669980321adbe5bcdda97051078c613791baa4b92123b01e7246bbad9fab28f7bcae531539b22c3bb30f68c973803dc66c50b447ca9c70ac7bb8b3335444620f91e54a0d4d78104596b5059490b7d411b306f1ae983326f47d3eaa499c1e74d983862130981e916dbe0ede4e5ddf783009480ef9846b44c75a8ee5b928ddba971ef5dbb9ce54496519560153d4b7ef9c8de04adc4749c741ee214c331a7a5cd789985201a9c4f65aab6e5535383fbc387b4aa33158891809aad2de11c3f7bdd7e158d4ce3f14349b3fbd1076cf3021b40708fd866b12722be4f9e042e06", "6a6500", 0, 858850649, 0, "9b3e90db2ed7769dc90392e024d9a741cd7782c3fab0f6ce9e54b78663b2e722"], + ["9269560803b39df2ab9d275584e4d6f2b7c5ba977c59c9ce37893baf44f2ebfa92a4066ff70300000000ffffffff2ba1a49f7d815efbb66e152e43e7c56e297220821277da7da23e902f15a3656f000000000152102cefe7b63d0919c18035fb284f59c9ceebf8426420f76b35f9c974fce095b7c088444d0200000003650051ffffffff04e7bc460000000000076551655352ac5273b0590300000000010070d6b002000000000153b6b9ad020000000009656a6a536351006a657c86cdaf030000000000000000b4ba35040000000088c4cdb60ccd69d540db24b3bf551f1768e8ebb41b038ef89466b3e93a64b93f1f1b11b4ebbc885bb9834f743164f0e72c4f690495d7c01483e38bfc3b4d716629f84eba528d965ba0a4a4455bf18daf38e9f7c561a04e29bf8bfefe843e34a60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000074fb68d6afc4aa9d46a40a54937555a94831ba729795849511b4d6fadac84fbaeac2ec60b29784cfdbdac27d9d81c18c769c8c4fce1cb151d38afbf5608a1d916e736d2e9005f874df0c5cf84867ff594295852e7bb4a82630c44c2855f60aa2840a2c9aae0773ecc8b4e0b718b4a4ae51fe82fc6281725f6823702888ff4c3e020b4659b8bb03d52979329f4f0506c916eccd507c3a3c80c7b035e6f5731092eb020e0db9bb6eb78a015b78645c13daa7e65a464b7addbb53b08c04b398348c4faf0a076d45e096df5662e55998f7fb83d1bbea81fb27a9d763678c62205609c3f8704cdd1a992e97f6c3e7163b5311e2600fb8d0b0636bc5d95ef68ddcefcb6b591c032df8d5a4307cf699e67fe1b0edc2d78d6ecaf8ab6d55b0c8423b0ee3ced85d1e0216bbb533cc1146e8083be32a589fa13928e7df7f4b8ddba50962bacbf368dc2a02025e63a0fe5a332f2a5c2d9047d4b348a884d2bf77d40e724a3fed6d1a61e366030f1e01964f1818096ea3da46fa9d416d33d6100ab638c1bf0504581231a87235020aafcada081a8d809b4be39344179a9733939a06bd03d5a0916a86df028ca1c0c02a62aabbf7731b95a135f41bf866edad43eb8644230ba5cc321ea2545aacfafb95b8f322ccdcf724ff691c12fbbb3b42895d72cd606c27c8f0dee112d52558ee7138119fe2a7a3b447998fbe3474ec4e92039e7f3b9e1890bb92b744f11d0c423d04e0069986ed10eda54ac5798a1208090cf0e02580a85d7549382da1263d689bafa08e3e16cb13f93229b437b4a9fe956c39fe927d2ddd8ed67a21687d9e728e2992d446c2fc415b0f46d1210309b5d8aeffa4193a854bcc52ce20abfff4cd919fbf1222215c8c4498e3d0d660b3bd95f921ea00811701e72fbf358ba540eb3dde21b36c39554bf9494c8fc250243053ed9adecc868371215fa4c9fed0113160d56baaf923ee99ae8f446ef01441fa0d3f0957c6e79087b728434ebb0b9381f4551d701042593966d236034190d0295223edac52773aa55543066dc66a323254f32d336f96f23e6e031a25e381187fd733e9cf62ab63177f536fca579d8eaf56d42475f4bd64f860c0c5c5429454529899dec3a8fff54450155d60b7cee4c2ca48bcf8b357ec344199b3733041ce06eb94dd0fc6daac041ad3d37062f4e873387af12b3989a4a578adcfde108278033fc58c1c702761dc246ca8ecb0135cd0dc81bda0f2c280ed03236402d5dcb02ccc671995b1777d2cb0e9a109ec3fa781c58ef59290c93932c1959985615fd8c23ad0e2960270ad8a15e9cc98e9f2df38902398259dc8fb83f7d699f9ea2711239fcd49966714a48757270c32829bd51b458e123bd0d0cc791880be6f26eb5e4ba266e8e13b86bb366ae7a4b147e16a2d7214e4a698502d447a8ad0c6afe357dab455ebc829e0ee1d9407f2248ffc07b30ace6f951f886f6df6165ae7ad99a56c2801dd07cdeb57586f2d9a3bd367550bbc99e55031f7051d330a7d59eef94ee110b537e54d01411d102b0145d496a6102ab4f7dddd490c352214b2632e1718b7be2ca04d7df1374d4252cacdbedd5dbebf0eb8a12da58a87b9210efce97589e33f0b104796548c2782b6660746a7bd62192261bb80a33127fecb6aa329a1dac48f808fc9e77fb23059091e6d8c6fee13079223df97d758218865bfae263b5a2d0e22a6de5deb2c3b2b27de2b8774ec461cf93cbecca575148c3a109ae66eb447307dc1631a9c4b1ef7d2667a1dd97da9297e54a8ab19afb30c5d9527bfe2f4a0cc8e9bcd4e42c8efac4983434271f43127e71aa01fd7d69a9fe318865899ec6b3bf76f07646279ce97955a77603ef51c8ebd3d226680b3d8263de07a3230fcc595a749919c75b55dc2979e0c6e9353ccbaa265501a1aae1843c1b3af650dce06012bf2fa488e731cae92d2c7e04ea3a1d9c37070fb190a5994aba5d4a5e4ac87e6af584f175b345f6da543e004ed9ea751cdacb30b72fed69f108c0c0642e20bfb9d37039532ddbcac7927200cc42d2288880f598530098c591bf505488e5126425dfe96b3f8e4b6da7219c88170b448a1a0ad9bb57c6189845a78ae47114661e29e1337cf7849de8b6c51c8b051820b9669ff3165e5b5d6be10b99fd7a83fa6c65ae58d781ad6beb0bb1380b78ed6d786a05a1a0ac90d08e36190fe49d78e0d405edac46520f1b91f34e26ec532bc54b14dbf617ec9cd41540a58f7ef4a0f657c96b6c8b824f545483fa24140a6038cf7c896101a000337cfa7cb9a3e20bf6a91000000000000000074dee203000000000200422ae3ecce9501b50c039a1abbeab60379a22e4dd432227ebce51e9e7cc0dd1c884befe3b3164867e87d4af9da030722c2fcad9d8447ee2f72b021f0a3df5906db2ba7e901a7a947b9fa1fee650c1257dfc661803ea95d42684e2665c78e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000059b25524ff53521347451283e5acf3594759043d61249bc8b65e72b81ab5cc9557423410203045afbe7c102b86e754d74a11d5901b2cfd8d11e307b1edc9c33e9e21bbc1eafc4000f3409af0c02c8d59cf1245c757cf5f80f21b56f222b3bb41cc865b8544451ac795fd8b9384aaa40b9c0b9f38f0056f888667e837064d40f8022df706d62f0d524f787186840b111d0d583cdd6f49a5df83dd90a4847b707da8032e924f7b7bb96f8f58692d9471ceca66edd9f8bf4bffa3fee834924e7721f6450a03dcaad3e87da338612015a70585c4cb1985f089ba8231d0f1b264054391757c9ca93fea97ef5b5888fb4376ded11f88f35110cb06d60fc662ad4e6050679a90022f3e7c0011e008897a4108c13a520998e2f38c9bb937545f59c484b9840aa99a022c98f5ebfde7e1b9fd2f8bc766131ff91e41cf49060096bf026e6e09be00220b02157132700385da7148f8f097da54882e21b8c3143603f5994f51c27068e4d5a3020254cd4c7e8e35a3f258cecfdbd8efb9f2d7fa76a8683f8d1cdfb9420d036958032f4388ce73b16c9d3eacb5be566d3da97159e565aff5d40a7d28880601a44e1a2511dfcee44365a9b10cc7eaaf7e07b50b0633afea83af65dfd0f1b9b47a816f80e21ad03384a556947fb8b436877908671b80dcf9ed637cab436be5188de3d1f63fde362c5b3f1e05546552420a0a210550390abeaf5fbf7a927a56d61a24db3366d4785335ad94842cf997c748367e4699633948df77e26a1459dbb1653f30a53b8f8c386aec5d1d75c85c46570da6c3dd4a0d21f57d2b58b274865f12c90688a749357a66df112a1b745749118b2b89ae6083db14012d69a770b11401a5477d9a4a7f9d8ba7b64276f016a2b1ba744b67270eaeaefcafa820a8c786221a4f652a449a3078d7f390a09c9f8bec86ed8ceb9ca5706568e5bee1eeb38bb99371490bd6fbecd787df1cd444c97f13511911238cfdae196c51987e5334c1310ad97ddf174fea7fffc8e6f87daa3887d6dbd0d65a5afcf95c96afa09fe482cd4eb8d5fe320e9c577d8a82762d6fb21b907e245193b3d36fcad5414c7489b84851f49680bae0066770cebf7ce35f77081691c25fbbb908c59aec1d0d60ea821a284a2eeac5999d62192cefce736c9f548e05a5eea52d9457ef8c740d5291cc2b6703bce611d1d1ce6a8897ac0172a669514f4540fdb15167b61a9fc694a3f705190713024c0df36b429bb38f4c3e255c39a812f764c47de9d0e1c324e65367d661ef421ecfca2752147eddee90593c476c2606ffd60762857bf168d44241a1320ec480b24ac2e35e54d9e44fa3d6a62e4c50a9e7698fef41d27de57fcc589dbcbc1e93b40e1b1bc4739e6bcc1afb120548b21dada33fa677d5057404be4250cda453428b0c465facf8bcf550c0ee4375879b10360af85e72c07affa66128c330d22c3144b1aad193a7b504fe4cd4fdc1f618d3f362c83ffc26de957035fafd4765af764ac2228a783930ed874b16e6f10b5440b0879113405194993ec5190389c52e4654860793ad61b99826aa3ca4f8be99fbe3f7092e5a82f2889fb3af6be386be216915c20fb02603b0c2c403cb56f0a1299b6b92ffc7837d721d69d76ead2086097abe55c109a464f9be9f7e0b6f836b037cbe5e79ad31ac37645bc8ed957dc6aad37b98bc85d7c69a45e1a4519a04090bf4b577c404fcb3e5ebdea8f975047b71e52dab0be54dae304fb09a32a4355591b3714f6439bff7be211d1e5382331f26a5c2ddcb6aac4b26a926d4113da587d31db01422637277172e69184b48d1f898438446ed2c66af54bd54b42dd0c55978519628be2991fb1cf10a6871664c87e9dd522e0ec72eb96e0f80daa1ac507eeca6bd8e399ab44d3972b7b0ba437b3a62fd93b9bb252ccdadaf1babfffe506002d53cfec2e30b6a88d012d1631b7fedfca92a425d465c90f6124d6090729d2d6cc23f8c134293727acea1b7bb4f5194c9ad18df48eff9577c8fab751d3d56525cd09a1eb32d24de047ae3186207307ba2aab8813698c6f09b8e2b026fee55019113ad5fbad54189fcff1126e0d9e051a5ce14fbd5205117c09a28cadd39654c0ca94a6b5eeab6629040e91ebf7f4e248ab17885de9180f495cb58256e594c668818020212128ab130a07c3926d5900155de44480f485d08452da14746ba30e47bc0ad4873a8d232e306178261a2e9780a362c2d060f6ccdca03efc6feeacc709bb66a8d53405fb241a83c2bcdc7ad7e35e47cf1ccbae2e3aa1a028dbef03590dcb9000000000000000082fb5805000000001e612bb8774d2583d3db6dda6372db030596609346227211848141ad680be829f2ee3d6878e8e6d104b119f59a3c3d010d3c2b7de0fb87338b28af8ef3abed531c4405e7521d45e7ed1fbd1a6e57bb86f877f83b3fd2f31aeeba203f45ea7251000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f8a79bc3392acb19cb48b9c5a5ff85c7044f5c5d293225860c6ba9524a6dcc2de4b87ca2bb445755932754e168cf8031078c09ac634d1b7a2f8b0f63fa8c734e69aa5225e4421495d7e0c860b4bac8e8a80a2d028d419861479d8dc8ac78f43b4bd63532a93e29e0c1b6eaa9beb2294a42691fd001ef6e55e04fa00ef4d6e14032cf012ad39ff8b716aad7fcfaf8469fddd77745cdc80de8755af4ee2bdb82c49020026e2f63348a552b2503309856a69629ffde823f177039ba0a8f808773cf6620b02dfe911b900d90c0a2530caeb91ef50ba8f05274536536b3f52f29a73bfee87a5288460c99ce8c13d2b4970e8ca06d838c8e287a913d4bba388e0a4e4a06b52022259b3b21091c8a07a3ac69c247a79b2c063790764ff741e22cc8f1143546777020486b9eb41d100f45664d1ef0a7ed578e4a0ac694f47f9617a433f5b858406770228ac20fbf9e5ebecfe464e2706e93f33d097f1797706bd37e68a7f3abbf53003021d22324dc966de902f6fce2feb1481f0ad6885365d57ba4c2d8f5c3cec5395530219de8dc70542ea9d386076cffc6212616bd1569c525ace0ecbc1b247e197dd05f42f9171744a994957795874d5224bf2d59cfe440d1d527ac879f49c782d2810be8f9f301390f74811e384658019f717efbaffdedaac40a7e17a04fe8a0fd15fa11d30e3e0d81cd7122c397e309436fb9882f8b9963086bf8c2b1aacb95f4656c383128c2c6237d6d3c10d589c971f3634e5f539983454c4fdeee389041f703e05cb01a721fd354b536144f13731b3ca1d0b94534e9b620da5624762a98a414db8bd5fd4044f98a44908782ccf1b7f69c8c4fcb864c128ca52f489bd5a0bcc1cd20652052b365f9006a6ce2eda0f00ae420e8902aa91bff3bb6bc765d40f728fc8e9074f74db0e4747f52f4179181f0ef2f9130c40dc02663ab68858edc9992806f50bda0ddbd95b319eca64bc0fe36f77ae5de0a19c625061c39b2c964d1d3a221c8055de3a002718a2208f963029c0d5f12f763e3655ca6297cd46a793e64eebc6b808f34e7e4b6d89a445d4921f6a2821906464a08feb4510149c5455dbd32714ed8428a21c528a6f05bffcdd4795a5001edfcfb275ecb2ecdd08bce7a6cc5bde5dd9798543f84b9094cb0187392da005e80f5fe96438e1dd2f5a01daef1afaccefd93f1f16545726dadd640c73bee7d089b7f26d242526ebe831f43080c1c08b1d2b4163aa0af89c51a3d54ef5cde085ccc9ea2543b60112b0aef035def13252b8c5708218c6a26103b1c7b24b7835b37ef95c7b2fe159c03b60b5f2246fa561938d2e9c8f3e7c7e613a7aec2531003a42af500943caca1e74025473bb8a6237f020346b1f8892eba22c3e910182bd5accf7cf03a280aee45afae18ab7dfb18c518ff3532ee57a90c220b536fdf22a6a46488c7396911478faae5d2d2221224e3ddcf0af8db7a44d2bd49ee3269aa8c54b589b989e0a40c7eed2cf91f2ad8e6e6444ec65f34719ba6d16d5609e2faa55e2ed91ad06718913ff7b2dae336d6eca6ebbe9b84d5eb89c16d4432430db4e0b7422323867d2d7f3f395a7bb1cc17f9e4cb7507a8cec7ea22426cbe2d3faacaca80cdb46b9ba06b3808288515e349a780f44382ec9adeb0f26961751f2fbd1f596a2b27628a8f5a461bef8325faba6be0d158a5b694bfac66088b883b8a8f12e461270fb28b41bf7d1159e89dfa6eb1751ee3888059a2fb418a83220a61e5d6afe8d2149d72666e79b26d30e7354eaa0e06ab4d96d2887d3f55b7ef9e106aa43be2917d4f35b144ef9b1b4590f6a50a33055dd94fa6d640c0f8e534c4c071d73f56c06f8d3553ded891f15907d81672b4eadb3b3b3e779b914cfbe5ca3b61dfcea3d68dc1ee63b8c0a8f43e95d1ad6c4b4005d97cbb115b7efeee426466911afc24d6eddc2f4cd7d9e2d59dc133cc1db325da9e24ac69c7e8bf23cd6c0dffb03c79f0b3109a37db63f546e14f71155ec307b3614b7cf1a6064fcc032d40f188e5a6ba04c13a2763de60bc832502edb31a570d9eefbeea72a35f9da6b0297a8152db59a75efd0ca77f5b4744a66dbf82cbbb07c846c9cdb6add2089c6f9fc28235ea06105913e5af202102d24080cb199281cf5af66f0a1703c21fb652f409d32c269fefde1d06f1e50571483527baab5efb0e1ea7d2c0cca89a62b195cf7f738c0c46b7e2624c932bb9ebe457961962953bcddbde31296c5e4951a2c7df1dcf7feb31a76ee552fbd5e419c9512760f6deaec52c4ed8ad03cf95800d28c6c8ae7c9b2ec4a6047b07552a4454a8a429f6e5900cd2665d1f6a7998b261d322684db4fb7441945047e91a7db3848a290f877e2004caca7a6377506f3b4a4c5e03e54a2af295e9b90b902989e9b28c31431906bcf69c246ad7d44225b246b6596b20d", "ac516553", 1, -889140828, 0, "739b1a2f48bf6af7acdd28d053264824e3aa103b6bd9ee11dd16c30edcd4872b"], + ["5a97da1103c5a3c812b6cd566f057b1f291484400585889a185021728647be1f33ffa6e0bb020000000863510051ac536563ffffffffc626b10c1d9d3ac62e9fdf0faf51bb81037435d471ed8cac0420c6bddea2b6aa02000000056a5300636affffffffb15faff89307674d8fedb61272a0a48bc3607451ad4243687ee9ce152b994d7a0000000007515251ac53536a488817290499d054000000000001632b66470000000000065200535351532610a904000000000700636351630063f52e3c030000000002535236cb7aaa02584184050000000000000000000000009e72daf6f79cfcd56d62b2b7316ec513b5cd069fcba49d8b47cd422d143c1164b29462e0f735dca46fe5764bbc9e94f45e3291244c79b06d38209fc79c2cc98986098e62b68ded6a4062a0458f0d7e94ff17b3c4560377583fc2353d7c65d7fd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000097bf40ae43d683cc11113de21413fc344c4a97c43560043a1d270109c3f6d7e77674538174892b34a4f0a8e698065db5b2b4f5b7ac55dd5b0c3e610a8769987f231f3b71a131e187f848ab979f2b5532ae6790fbcc0b0392affe3f0fea7f0c523356f273b3da8f8a7d72db363149ec3419b4bcba26dfcecd865fb9c3ff1aa442031b97208fac96e66d47b5a967201f2fd849638d45165219642311cec0b6d12ee6031246da09277d5f7745d80dfcd876ae765fa746ac1eecb81d96367ef84495ba410b0298b9ba31e56df3598b84123fabc78089da3ded56272cb5023aed675521810a7cf8fb82eaa163db91b587017956ba0e08e034e56b999add129256f7d26d20d6032ed15ca89badb626da4524a59d4bb2ed3f9f51b150458cd7f0762d22503aa066030e07d3360b82d58b7b458fada8aa1a8c8e9172bbec57d743c4c2e2e684fe507e0319ff2f365b03738e429a1242f03252f33a05951e55acf291f2cf0f2d8fdaf43903225eb63a5dca7f4d8a7794b2c4fab35017db2b5fa4368f40524b09f14fed12320228ca6acd830abbba113444e188d03cdc3ca4e93c4110e10582e0247351155ead368cd8055238b0f4e129a70a0d46bf9a4cbe539a6528665724c5f1f0a10925d289d848c2746d553b214eb085e23863cbd5af4ec28f1e3c746b59d3ca89ff1dcd837a9d9b4962fef5a514008fc98f70778c63a8a1fe0d04b9cd834aab4f5427a6c7225d2e93fedef8a102ea78ae61bb44d4bd9e4950155d7bd8688980ee8cacf1dd3ed00bb2a0ea507bcbe8bf8f5d25a8977def47b3aa467f132171dcfae8c5df8f9cd3447a3feac6253fff5a541f3cbad7a96a8e33231deb4e8dd345edfc3d0947315d77a611b5eb3e3719e1103e8859c2c1927982f41a05a0c250aed7381f2441c9e2a62574d30d14cee930275c5ed8f62fd1aa69512ac36ef701762835e356e33af4cd3b60eaa636e7cab2e8d1ef23c0b52afb7b972b33a2f8a468a079a9f758bab7b76750910a7e3449d0e0fafe7d10483c6773c76811a1ab313ef6eb85fc0fc47adb68d7bceef19871518c2184d554146733d45ccd677b5bfcf2d5ed6c9d72fed77c4f8b0ee66eb964284e86f4850b6a142b193e7dedca22a380375299abb8f0634cd7762096cf4af4cf5dec0f2714c2d6860f8b8d57716c8007be9b3c688020918c6c014fabbbfd275c58e6f6e9b851cd8700195d85098c9336956397e3e7ee9b1e0fdb097c7d3a574413a3df2510869c848f03fcda9ece0dd2f7b925d3f0630ea01c44012eccda3cae3e997ffa82eb2c51dac26afb4c432a20f12b25caca1e94464a8b0973453b34e4f3ba9025ca01a0c4ee11a4a6d8dd4b465416c7b1153c2d16abcdd2efe4b1f5dda687226b19b7ca6902b5eea9f3cdac68e8681657a734a0dc0fc788772fe60e6d4be9e877dd8fa057c70bfce80948f43d8db6b09331180cd550c29c7e0cd947f6c40d6073e57d7f2c5d057a38d35b8d8810fe1115459d6a416b4d06fba4da3fe6ed20da27976f858386c8bd42b938352a17bd269a3648a391556105447d117172bbb0972fcd034ece421465498563199c10521f297aabd1a2b174e280c4b65daaff784f5932e55276abfd6f2cc8f05ed3d63113f41577492b79844ecaa36c12caeb89580b2bfed0447f4d5e24f986199af350017a4ccf9b4d8b7f1cdc591669dc52af10bdfe7e168c0dee6bc0e1cb5ca746a1175edc49e2cd0e2aaee22d9541326d6568c7526f90735e20df36ca6009b4dbce63dfb71b71918072bb905532068d2787f1654f8496dbe2a97c070ec86a2f201601350cabe5dadcafb61dac10be80d3bcfadbf7fbee84117fed9bf0e447df77408cba4a627d28d951b35b8ae65106246d2f5ca4f5f1f96d5c65ecdc96c555a42b576f05dc2b674c4dc1c4e3347fbece01027862636555de4bc9be116b081e065eba7ca5c6c90bc0cec98e33fc518315a99e2d02280f126cea6080ff9adca81e72851a62b0a35abd361856ab1016912341b760a266681718dfa16d622a09b20be37457371234cd65c6a988e15e223f843a6f6f25b4a5cc8482eb132792f56a09b1e18db3feac476d26a6358aaf66c5c75c61d4be7d2ba870fb393fcbc84a91a70be58278a304586cedfab67ac46ea3f5c96aed77f511cf70289a6218ec11278a3152bfc9a793779816ea228821ee4c4b2ac527ea2bbd18b610b873909225f77a05d0fb7734746cafd492f8b06423e5fd96092b66b66d463db01b9ba4152067206af22c8775cc86c511190206b6996e9dcb050f29c0660452010000000000000000000000002b13257704ee3919cd230c7a4966af62f1a90433f045ec9ae6e2129fbb3da7fbad04b0b57ce63d3cf7a1353b294148af8a5405d4f9e78db0c11b4e9870ac864161a297354ec93fd05cfb6cd66325d0802f5760963566e2726e3777aea758407d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003a76ef1ee54018bde846cef011d9ab1b223496732db9ec72e4793bb6e8634462418b5686695c5c3cbb860c29c39f012ab01627ac5a4cea8fb3e90b22a271e3b9e545333660d151f74106b9bbe6f38f5357f6d8643870b81efc146a6e80d6f34c07bd5b18a5863df33779fdd48789d7441428555e93f906e4f376b290576300e2021aae290fad0c4b652d201fd5375c0b012920c79f42f6de4d207b23693fc1fca5032d64f8464ba7f0bd650e658a2417af78512d17f73e1bcad4cd61a96c055d97c80b0530c6d20ab2b3597c17e9a574e1c41349db4aa03ddc9f278bf6969b1c69d901a08a2d10d087ca82c71a6b5ad03aac346f2ec9a230d473ac7002644fc2eb7adb022bc6f3e7d0fc4610dcbe5e687ec36126008a3b32ab20e6ef9076ce433149537b020cc00ba74aaa3d2c1f4223fb1599ff769561ba7c91c2c5f749ea179009c47a6703254180852bea2939bbc040b5de6e78c85d2f76fbb35b7cb6cdfad85a1b358aa2021c1c6e4460082eaac0e6dc1c046902d2e629d3e02df5504125ac8aaa5d67a24d020e535165337e84dc3effff2b2cc7e6801a6a444c095ddb3a98a8696cb67ae12a148d7fb79201003612f9f91cc85d8b121703e505901986589ab41e6c07aaf776ce19f50f4c934dd6ed1e6d4a98ca104c0e9ea09bebafa10a0362ffb9dbe6c6f84891f1bfdd52b30b8124dc462ae3b2de451d8b5111d1b1fd70e88e2b8f4dcc9a86b10ee74f1d46e9683c37e82df6634bd66c5b6ccf3e2aee636b99740a32f64b5bbd3fb72ce381740575095236ba5b1ed5837864b5e3ccf7bac822aae04a9cd7a5e7926d9e2cbdcc1b080e4f36cf5e00c7d96ae765c0a02ce297a3660d73f306233392caa4a53efa12bb5c0528a56575e140c7d2124880138776b848ab7676b76a87b18d90dc48651333e495f3a19e321fd6b132e31ac332056fe03bff43a7f0b8e699ab851853b034ecd9849a2a1d7662d0a914a9e353c279778a0f9b43291fa286365c5e18d4db29981477789eae822336fce18bec8a6d8be954c3dd6df3d6b65ff960036bc814b4f49b6e3a098c2ce45ca83d47955190c625bff90b556c459358d5ef93185dc07099caf85ed1d253b65f37a3bbbf84a4ac97d29a260f36ef1e701f671f6736d7b2859a67dbb36e288999c890569834a7f33c24c7f3085273c24c5e3359a371a7a1592830a1a8a6420a43bef1376a5ff25bdb9d25de7a5e588f6780da511a417be647bf9f64d1d48302b0dadaa97c1c9795a857694bbdcf77bd8df57d24ecf209807f9570e39eb8210ff7c98a251e45a84f2c4c20e2d5b9ea904b844e8d4bc36171dec924d2cede0342302027df476847159ed8d20b300e5a624756d951dd8d2a1735e335d526ffd52e82f58cdad0e5e734729072d87452eadbd7761ab33477145b9349030a28e4174c5e21c3dcc3340d1761d4d68504c5dac6706fc5b896caeefcd449e5ee1b2d79c887e0fc594a6c4b4ba5aeec0be9255a79d6e2bf1661485eed7b7ee5472413dad290f7f361db9d8e0ae3538999a300edccd30e2fb8b6302f5b77a60bdd7558627445a5370ad763938433c368877623c4971cf13c597472b9a8207ad9bbb94370cde20ae41aa4b21d3e5f96034b5a8f219003a8a53d5d7abcceed4882ffab3cf932ae639af6ea9312ba765da15b740726d208934f1a7c1b15eccaf8a16ea4b5039c91168dd283c8db5306cedd31701aec283c9400dca52decd529fa53aa44694cc006e68233349c6468a47ab1fb573e1d6dbafba157a5c5c7afc4313f102d54ff879f64f5daca9b1b1f1e730bf3fa15db66622572860286a43a1fbf48712307664b9c273c9a5e176630f0e933b9f1c5359b38f0d52f5cc55c16bcb0233e2cb6b56be5a22aa5afe23ba6d2fdf68a6e576e7517f784f25dd663dd6e5f8325680bc7c0afee464886355cc171c66c92e6b09087c7cf761dbe72dd055c071f4f993735ad2270589b97b7f0f0bf2082516b294637760b2d964920dd9ee0ba07e121b1e6b4f34d7e69f403196e50d2549c368b5ec9e7ffcf7e6f04d0c84afe893a0a05178f1116ab1df2d6cd420b2b0e674fc5a4a43ca76673b344ce7dc771a5fdb81e64d169d4365030aaf339d6acbb3d944aab7ca129daebd95cd4b1516af3e299cd7f2fe327e5b8dad83b138aee7cda36fc21286c38926c0e84cd894def4b359b54bf466fd8208601e43f214fdd5062f237b949fd3f7f51569362cbe437574c0b2eb0d3bac11fc9b7a4e7f9e4306e53989e050a27ad0c58e8fca336b829b3843923a74581af97ada3055111551414f393c4badeaa8b981b1d32795384cfd8c35735f227550d3fc74e12c25e8355fdbc517e6961ee6e2bf67c5db4cf6755fe287a0a3a461aa37442c68d5ff5dc8eb89631d3cea395145d2bf55cb80e8aa163ecd4cd7cf20a", "", 2, 243579345, 1537743641, "e19bd85f20084f9f562a0fdfe1450f06834866f9f1ca273373a00edd7fc14f2b"], + ["34159f61033fcf5e6d20d98e6b0d64c0e61e0dba913b7659936d24482356ddbf53733a98c402000000085200526a5200516affffffff42965f6dfe3fad9bdc6ab26b80dfe679274a4dfebf90372b5ad4cdd1bb6df87e020000000852536a52636565acee0178d20751a7ebb2b410261fb7f46e7c6c69c35d8bc2396215c0e2e75ade86bbe1ad670000000006656a51650000ffffffff04ff12c6000000000004ac63535295b14a010000000009635253635100ac6352e941c5040000000004525300ac017f56000000000001639ffc559b01f6773601000000000000000000000000ee31c0188986e89cb5a01fe8b473082f01b6afa771823bf527774d566140a8d78a49b1a3f811e8da0d3e683227345c1b9e39d69e4eff1d1d254c9ee6b52b9d5c8be66409938501ec1ec51849666c77c0c449f9606ef5c3e3bf2ab151fbcaf8060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021dca8baab206cecc62de21ddae8972741b5add4ad7bfbe1a65b743e735e791fe39c955b4c25be4f11937aa23ca904ec9e59a60b8fe2750f4c3952f701681c27d0f1fb503b08c0c88930a1fbc4845dc2ff238a646db212fff465bea90531b19068f8197c9d39f0c234ff4ff17911b1ecfd71bbe8d5baf2e52da5e8096716c3f3032f8b6e2f5451266c729b74d1bded3cacd1188021ca33acc35e4874ac55813f95030e570266f1437fd7e495c0eab63f94a8a816f1eb4d60c4208229bdec7489c6710a05a9f60b9d9614e2e831d46bf29cbbad2064edc8f390b1a8c837a1cfc82ca5daf0a519e2e1c87251e3e4eaa90416b794fe468c047164b7f91f5a08a03d9cc255031798d09d3006413e684afb3560526627a1285a0455f6fc5475ea70f8696058520322afabb95bb7728c24e0030bd5173de5fb1cfa4d530e1c3cc822e2ac1a61dc580301ae006f313acd966bfed637cafc8aeba9b6a9dd9cf0acf8f295c1977385154d0215534fedc41d89d08cfabdba30b85aea5349469fa1d67dc3ba33a900188aab4202062be8cbd2ab47134418065ce7971ad2de150ef981f5cd7dc0224ac95698f5d990e76e6b8c757ada6bc1f1f75747bcc30312083e2af79d9e76360247cfd88c8deb273c4c9c83abfd27275cd8e821b413ba6634632664c26d8df655291062dff07d931dc9f482453bfc0d2d801f81dc74aa484b31d216eeaf7c2527b7f5ababd0a47df131fe5fa7bd0119962e382dcd8fa32c770768eaf7487ee2ec97ef468d50c8bb89b1890548a9594644501226ee2de257e7594ddf449e1ccc7a2a256072d3d91861013b55859146df9bb90f5e1c9a2348718fc4a1d33ebfc6bf1b4c08dde02176675c229ccc088a0e313cdc5e50ddc56c404abd94b9cb8a065701a0a17427bc268e5d33d819f5e34209ee881847e8911eed8c5fb04cbe1a4ad32af88519a5bfd30bfed46b79a870acfaa0566fec8baa1ce59858bb8d9bf43d6edc109f89bb0c3839eadbae50f16e4c5dec49d4193fe7ee8fc5e6d1881d88bf1ff5af5b86390e97d2b9403245b944da414c717d82a96fd0b7caf0f9c045a410a06b7b3066f669a636484352afe607fcb1db55b438b3d0ff9e5b5a15750e4408094a4c01ebc0363e06b0e3c3f624b353c6bfe6925e0e75d6c62c70c6f2bdd700f4e2ca5529f06c498a8bfdda337d47142d173a7543dcbb4e868cc3de8d3cca319cf4d3a368636a85fc0fa1856b71a6bfc7695e4767e9925672c3e84fd38a7639b74e72f6bf3e62787cc7a416028cbe84591b3b01b8a8da201feaaef678b1731652733dcd00eef6c5176b95827a74004c12243b8b744f6cdeb9ae1c308bda543db2ccd1989c94f01e700cbb32f0c3e11b371b559e8b37f54f2fab8b6548c7f04ee60b352e26670eb85f536212c5807c7de5c053c86c62a2995c16f200415a6e844d12121fa540d1b0f71e4e5acf018f03ade409dccd806688c596b529eeedba955effdff30cfbe2c40d2751f05595c80f4bc1afb410561b924b674316fd201a3e431aed092cade35288bfcefd2377610e0f9b6225ebd201e5d3dc65aebefba3a763ff57abb2323b8d609b2853bf5914c830200607f8a27997bdf26a568eaaafbc2cd7ec999a157eb624715f73c995b3297e7b62ad6bf1ec9ad536c004ed6c9bf5781100c8809d29153fea9cde60cfb3437f84ea50dbcf4ec76689beb5cc4c1a07a87f489ee475a09ebc61671cb4612839bbb30fcd0c6b0477389f9b2e69afb859fe5be4f792ca5cfdb3c3e91008a3fdd595b0cd47d787a4d97cc9621501eaf8130e6a2b069acae89ede3251f698cefcc11a672702bc505a0114bee7684c4d1fb680d4d8a250a951a26fa50ec715f6545625e41fa78331ce618af864424263fbe56bb1e4db799ee32b0c55ff87f034a6a067be34a725d091e129b616428fcce9fcead4ad1b79c61d1e10864ed1913530e037b9280c9eb40066373709cea15fd0afdee4d81ebb31aeaff2c408b1f625251b2020e6c34c2e0de6cf6e8ba2f0974fb1c359a9bd784d378d26aa811885715d8ab1f35ced9a61d973d3bc271aa9a3003538b1772c8526811f66e43aa0b2a05ff78e95a40948809fea0e7299eacfab5149045af39373fef3348f4b17e8d84b0442b6b29dd1d9f801d2bee08227cfebcc0d0cd04f5769faf2fa81b4936d0d65f90125687ebfdb40839db11cb2565863f9def3051a0c73adf7552931acf9e1a743e9efd81b9d1560e1afdb5a0e2454446530b0e649feb7c6c801598f25ac5125bb53491e99ec31e2036e1ad9440e94deb8216bd5b2c90add5b5f79203510517f4ffe44803075f7792d796ff06c2a3e23930736f594e948184d4eea78598df1378d5c07971ca16cae506f66a228504944eef9c46b9345a5a186b6f7d7d6df874eadba322f66aade6b3d0e", "525200", 1, -1941464378, 0, "06391925e7d0e6d1eb380031824df0eaa9235a1178b00a1dcb3054fa38588879"], + ["030000807082c403034e0ee83cd69174cdfa9723b3c59c30c5e6b83929b77fc345e15e45bb27630f490100000008656563ac656a5365ffffffff6e68dd8dbce502cee4e26a9b1ef96959bc7358261afea537c3b0f34b68aa80be02000000035352636eca3e63fe27cc6fdeb8fc33514cd154213eacc840c9ad7ba632f6e1de45b9a2c678b60b01000000086552ac650051acac15f8f952047e7df004000000000163280e3d0500000000095163005265535251632bebc002000000000452005152ea90160400000000055365536500d54714a1f6098edc0300000000000000009010140100000000674dec17afa8d9dc73dd38de2aa21cce63e93e5d94463d89833a47640b672ad4c056319b454d9879788b13cd422ddf51a6539807c2efd7a32286ebdce9292edb0d51bc9d6dd22abc281ab62e723eb4121978c8c06550a3a3949d80379debeb1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b42f95b2a1419126a257cd654169662e9db4340e872240da3b3db7ad1e32cf2ab5789518878b14afcd3fea6df18f98bdb4cff097e79504bf3242bfa76def521608be24ff85b645ef0ebe21d2c8b41effd24be9ea137d4a1cd7773dc77ddc2813cb0bf3146cec145bd4136ffe4dcf24f7f6a25fb59213c2c485e31cd6f5a9dd7603015bd39c27bfc0bd932d3285542aeff60e3b6576a88d036f8efefa5bb3a388b2031e4edb273c559cc6d57751910e15c0d200a78c9a0824f97ae3b807e0307b8d970b07d9752539d841ed67247d879d6658067030fe13618650f68c606cc4baab32ff9601da6aabe14e140be2db90df3814a2663e3482e16d15ac83006c8dc9fa28a00224f20ef495c9065f9d82507cb940bf2c4465b532dce26e5480b5ace9cbd0b575022a5ff78b1456a73e960c7a842307dc6a39a4a7bca60cddac2f11e992d754dc4c0316ac8eecffd75081c64fc3103a547c470959203095877af052356510c59a5b9b031556253afe275368fe3661359d5e7d2750ffbfba6e1257636631a6166c75c446032457abb090e7278b97e43750166519e4dde503e4d278bab63de8808a304a8acd5dfdc7dd58ef9126e4a2cf2d63c3e14ff44a4d77d97d1511be783e9e09001d8cf72c94cc88cf918b0c90a68cb8ee32c936b083a4208e3ea1da960f3f7ae9e80e6999484fa1d108d6d46970feb13a9765d6b4ff6c1ad126ec45b07dd7ecd97daf413eb7dceebab91097fcc6d9131304ff0ffc9593e66abb1901921b35378cc8c0b80ef26dd58b5a577bad51ad5690db230ccd7248fb01beda77cb2e5bda8ba39dc22ab5a6a80a3023ee1eb044a68361709b0d6017689c9e1c17cf95c20978f84a7317a99fcbe59062fb2087d7a1eb56d85628d6559ae7d7e6a35ed4e37c3c4851bd2fdae2b656878930916eee17e8a4dea400c0a1513ca9db208fc6adb17d59fb5d7b6defb0668f30977bdfbbf3ef93380c00d244b2bf3a6f256ce2442af7e42a4b43c2f5ef1ffd1118596777f508b25f7efa29858871b09b8233bdd82902b5b4bf267e301347dd854c9c4a49272830315906db373437e90246bd2cdaac13ec4431b1a6937dea6d669a45923fd9031a1c0363838e0d7265d220206e78ce8951c05b605ecae4b8c5a2246553f238eeb0c3ce810dd04a7d923f606505df36f8c21b4941fe7c42a04076fe698119eb29a3b20f1e3a021aff3f1cfdb0ead9a19dbaff27b8e0a00d908be58e869d88d403ab07603a4cadd1ce4636b18d58a9e42113ec0431b2b0519a0bbd3511558c31bc01ae20b69562fb7ef5693cf878c7293cae1ae62713e6535c5821df32f69d53c0c91e1b14227f8e782e4cba04d35b3d154ae027f5a8db52dd6aac2cef38c521f52bbf23922ecd0ae7e4c3cb37e16de69bdd4ca67a284677c4d148b14156bb2041655886440d8a72ea0c771adee0083d45054ba6fe1502accea883757d81c8f34b49ac67252d268255de57d071998e34d1aabc36a7c2e0abbf90ee0370b88b9074045bcc73c40ea9fafceb99d704eb6ba459d9df0cbfc8579d8eefe9091a0764311a7fa8d6c620c4ede1e9291850b19b55b72dcd6e96cfef6a1ac82c97b2f9bf44333407a5342c15b43406963b091516dd33e2ea3fb64a4eb2a362364e2bab1dec605bacce521d1e30d802a8a9dfce677999da699f35d3518bd4fc71b4def3ef87f5e04fc70b02ff4f9ec887ed0407783210bdfad4dfdb2f484ffd748c03d671915b79dd7e34c9ff0b7a5f1b74d0b34d5794523f4588e62cb44939aaed6841390866eaedb7425002dd3e637995f7e495e6537c0e6a5016ad7237d048d952ec09d0d116fbb44028ca23080cb7720d22d73905ca576547aee21dc4a5eb72f930f73c4dfb2eea6ca62908798c89d2ad75d6f2d2d97dfdd80290995048ff080f617f640eaf4483d1b68edf437d24a833332a964576330e40e6d25e00fd6c34094e24e0d08bbe48e63d4685cfe1dfad12233cebfe8f5bc4542072503d9d2814078dab0dee493195def20137d22249c2e3ae46a093c63ea4353e2e2a22e3423d8c7e979fa353eb5e9cb0d8e2dbd50580416a2804a5c4259db9f171abc16b069658cb610d97bea4f4e5742ef37bb935c732d9ed9b726fac8b36cb052019b59366ccb2e51a53694647e56d313a3c5e0ade104d8c9068d449d313b58a0796b711175ee3a35a272833abdb9df05588c53655039610ba2c2b1100486a84b7dd71125c9394e5565cf88f203128ee0b6ebd26596d9755f42b99ac0366e55c552b4bea1ba7222cb485b681819b0a4c030000000000000000000000006e90fc39f0bc8a464e93ac9d25794148fe5ae653cf49811cb0c1299a45d16d9687ee97a5d18bfe203f3ffde2270a004e28edbd76a891539c920523546f6b88bd51a05334d858f10430f5118cd59a5c3505e0e76a8d8985d4093c48fbd1f82ddd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d14d602b46aa6b6cd45aa0b6cb0ddd33f2745569ab5dc6fee57ca39bd8004e7da11d2bff0ed4556e7cdaeba0d383416decabe3a5f71d3d3fafed886066e72a137224d30055faa17df725ecb9630580232b867fcd85319cbfe8f0adf8f64f3041ed521bfa1283f4b208ef183a6eb9dc5e2239c2d61f86b76c29464ff96119b69503139e711b7104037cd1cf84520f5a52ed5f3e8ceb290afe9d7bd810a959aea57002149f96e5375680f84949b6b376b185f1a20e575dbd19b034cb010cdf82fd79960a085e2b0cc175b39f72ac08d29510ff7f760c3caf407ff0a84365d00f7e70444edb30d7adaeb2d5682eac42da4f7791bbf59e1eb4fc3e3fc4662f5552b8d82eae021d166a6da809e5c1f743f9588520ec69217728436c08d2b1efab3baa7b38198e0326ef61c180e2a6f1957c1109e2ad520b879a7f9a555652af1efc4893164f7038031529a0bae5685c9cf3b7a260cade9148ea5a8263f9efc209eb07055aadbb18ec0226441a4ead162bb12975c9b6271f9e220533f5ce0aa5ac8dae354b8e2de12c0902154cd473dd4598f01e8013230c85ea30f15fd9873abc5e11f4a1d232f58c3a1d8a086fa6c296205771dbe678b171317ef9866db802861dbf8250ec9314689a0769a9dde430631471984a03950d682f7fec5a1c558d01cc3d38b97a71964698e461e8630626460d46e0040d31f39aa57fc660f603d1af5903d30c0bc1122e5db5e5e9d25faa07cedc8e1ea8fd91996c23a13088499330abf0f1038032b226f73c2b696b09a77a42cf4ea83ea334de781571ffed8cab7ebe922393092e89736d1be533d23d7530779fbc1438c0d626afcaa6aaa9d6724d7322a57b9ca9da5577d0bee44ac7a9036e6bbad8fff78ddbd73fe3f537c46f57bc7a085c8ed1b08b339f0ffe06bbcc419f760d3c01bd2f70c02c42a6cf3e3d6ef1cfef75f452cbf0edfeefb8059a06405fd27f92790d4393542a86d67de62f851158630a997a278c90993b73fc5e7a39d09bddb8b5994ab0ed30e36f29f5c682c8226cb0d1d92ba8046d3b2593b883e58bde2f827a2d8744bf3d59c5fb79cffab50edcf2802a8e8311325601ab90607190c6247634a59766013a67c3f15947d67858a054488bbee9f7695eb89ddd8983e43e2912f39835be898df0a1481cc40fe5dbe02f940bcd94ac2917cfb50d0df975024a7647e61384b7f3c031f0233151c806954d650c8d44f281f91acfaccecae26837e192df443cd29087b075296d4a1b783627412e84a4067d847feb468a29a9e9eb60e345abcee21000e830f157cf353060e5afd52db1d66476cee7703dd31614417cc11f33d08db84f6bc804381d4c424933a75a02c0acf37ebba6babfbc4d4cb3f15d8e43143d98e0f53074a632f526c747c1ccb4cb47e2e57a86bacf5a11d6ca638b76ea03ed9b3eddce6f02a03b118f0071371fa8e03e54df416f5f811fa2513c2dbc9a72223cee962241ac04ab4ef092b8ba1f88625be9852c7558e5a2caa8d3f388f01dcce4ca042a88e70471a2cae8295b1ff8fd3dcf9d4a46a435b636e90f28c1c40123674ae187a17d88fbb27ced6f81696e9fd9134991376c9bc0c331f65e28fc51ca27a2bc1c5289c30a2cd5f4509debc95b98a3f546753047524477ca7874bcf53726d89bb250eeaba690e4ff442432a7bd5bc712a2e72eeabafe53cb4960eeedea232c4d68c2576910d5c9986b247a8fd9f2584b823a56a3c4392baac6a79b737f67b17bc2086a1a002b39b8f7f21d782f3ae642b1bce7113c398a93518cba3087f5b0b53d823b14f8386ff9af039201088ef0fc14aa1997dfd54f34a17b8f7e8de6299c532fd8964c6e40a7951f931086414a5c80231263b0522c461aafa137e978316436aae9c81174528bdcccf628b94d0c5add848f609b681b0eb316d3098bd81d05f7f5835ab9f615464aa92ecda36c4c01bdd88384ed8324d6b083151614b73f5d0e1e3f611b555728d894c55393ce5964df64c460a43fefab115132e468e6f921dcd0a35fb18f96208315870714c0261d717f353214d3011f6256b5d5a168f124d51d6c0d2b4ad18019f532dbd2f9918d2e0301c353af3d9b3b335bb72d6f2dc0fcbc24328d26cc16ba711b100846185352571f44154593790c8aeedfb56d7ced86598d5f057fac68f519675d8cfce47f24bd4f85e8816dc770627f08d2235191e4cd7ec545a76dea3fde65e67f35eb52078c2f0a2972f58c500dfcddf48db22c299cb321e824a74126725390220ae95cf508e1f62b6a32ce6348e4283857214f0000000000000000d929d304000000009067211c1de6cd01f52ebc12e47e994720fb8be55e8543117e6278cb912de1f5a0ee35365dfb30fd8db30f1a11fa5e1b059b764a1bddcac364d02f97a5bb7b188f914942ee15d90714af6bc76b59d187d30d186fde9624ee6fb057c153ac8a10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007f362c8be2ff5d5d6f1888c57d35913d77744e6500b25df82c20f814ac1129985bd1353d8aebd73c8044450e1609640ccb37219ef72c191964ea19711f3deeb91a84d900e97c1c08c41018379564b23d2d951e39ccdebfd8ac67dbcf5efdf481325101b8ad616dada82289d164fecfdd46d6b4ae012789b8e43df8c6b5ae365a022f1911ec5ad3396907ac76737d1b689480ad735e8d4574f30ea88685a8f70faa0213aa0ae32555f8f1ded176c327902231059618e8f6119445dcbaccb26a1fd1750b0361678b07a5937670d8b6828665b0ddfc6f7aac89832eb27a9f0b254db28679108c6c8b01d86dde6db8307861941bf3aabdfd4ee6793431b32131ed85334d6f0222995060b0cdfad2e352e45dfe3570768a27525285a0830a3585980c6c1442ee0221f27dab916bb26c93c53d582bfbfae85a56d2a954c53bd216f9f8fbb5e3467a0306042ff4df34527286212522a244aec058fe31469deafc745d59b31fe4a4936d021aa33bcd71bf2d833afdcd248ab7f296632c9a37a32b4c32120846db5da0ad48021450c38bf65ae223a8f800685c7ca4f650ee906411d21eca780a2c8c4e8e17e0e9fbb50288961599c4f582045c9c87e91ff3c98b2f5371a63548a5223a494b3a9e8cb4c5d49d8e1efaf1b733fa36d71afb150781069843d9b35cdbffd5665e3ef7e87289369dfa4a64d9e6a0d92816f2c66e841150e1754f7bc3e98305583121662dd3e9947b272b7cc0582ca7aec540d03079609efa4da66432c5ae37e2e0f4c14689ea424ca2ac7b5bbd7ea358f3b7bc3b3a2b4e8c1258d5aac603b717435ff30f5a08c9d20d264e4a3184cb06a07ffa5f771c9cb93a744f9fa1d9cb2e40be053b3380a1405f5bdba4cbe02bbe14199914c34b58fa4573b4c94cd42da3124b516b94a39a25d343c46e525ec4790ad964571e702115297bab9c6e3e82587f0b3b60afed328c471adbb3b783449b45031fa74126dda0fe9dd2ac1f07008cf656565eebcb9b42127b07d39ddb1b4f96f1bec7837a3f3d32ddbd1e55c71e5c6c9fa107d63743b74e90004972b259282714e141f627ad94f06570054ba11afff8539fa0cb6879b750ec0ea9edcc15e41728dd2610dc735e8784ce358a2c4c8fed2052371a2eb1b61d399ff8b978692277a0852f77361a4ee7f743ac48dac877d6766073390a289ade172a6426a8a11b2df22e45a182ca1426202be7672556d927a8a14be2e9977a36261f3c85735ad1e7fdcbad909af183708ec714c123b5f5fd197c55ef3b122dbe72ad254e839e89c9b2f87d8afd7e07b6ed932e85d38635d70d17426801ecc134d9859cdb0edfc67affbd26633a421f50a6cb05781469635b36e1ad20a270b4dbadd4f7908e77ffa0b31cd016637ed7b7f7ecc51fc59fc00791a04c0154e3dded7c16a2df94d65edfaf494a085a3dacaf8768b7d64ac3699cba898017d3e24a75b12496a26b369159e5105f2daf6691ac59c2206e98f4d5e7beeda94c5f5b8257c3fea59112664572c3f20f056e33b611514e0d1dcc896b419f53bb2ce8c181490819507caec7ce3479765d215a7d09b85f4b941740b782673640e0bfd0c4c64091c90d23d287f0b87be0d0e9e9eb131a13ead13ca5f59748e605bf5c4d105d74cf2a3163e2d3a9f7daa5eecedc8b98256c677888a1b5d7b1ad89eb76e44cc5eb45f4862a7a2fff3b26b0dfb470ee3e649c99ba19897c64c8a73f4f4faf7c8f1adfe1141bd218e707f3eddd5e22b5e4c658e82885f6980c3f4a22372fd526d6733b7a5a5955457cf48c58880eb819752da5e433f802c8654a2519c43a5be8ce821c34ff693431c794d168bcb572d9e4775801c5a0fa9b855468463af2393a5942a768b27b3b87a47c19689c11e1586d9aa5d2f5feee3e61707238e47c5b8d9fce63e51d98be83c89a656bc7c1f388a37c735a32c92e0deff866b8e15463967a09fbeac1b8fca230d036be36d29e40737d365080df8779ef329cb07fdf5d2c41ba974d7736627cacd6fa5e6b60e8628f19fa4598e9548b888184b8c0ea23368b6dd2b87e7905fbf73152c34bb624c4f28f118b5d99e817b95cbabbcee5d92a4510c7f86de8cb813831006c4eaea2a0c7b1514b59a889fe3ed3468edcf55aabbe624fcf030dbf8ea1857fff96f5e186f1fc006d199b1e89118b62d65b08155e82a24d696930cbd4e86f3f29a42bdd83b3cf4f559877acc704efa43f7632747205455e20a59d87a211d919fcc50b61c4886c0f2ffc79dc933a4c39608b484405099e7e0da5cc6f2f9b541ccb50f9b95bb363cd024292ab169185ea7150c616ef0a015dc15d92dc378812224539546a1c401dbfe34a41cf13e840faaac9e13762a502f3dd5b30f479fe4e405c512a0ab94e0cd32545df8eab52bef1fc12d4251f19becf84c6a4bfc2c9794a090f", "635251", 0, 1857423729, 1537743641, "5e4b3795f131c18af14f7a2d3bae1c0cd59c08ea812e74785195c1678e92833d"], + ["030000807082c40304f1ea2f997b3471c07f92ef2b775d96f9e0589ca9a0b388b514719cf6f50ccc140200000004ac6553acffffffffcc43e0035ef0b96ef53ea8c72e0ddefc44b3e31476655b9733e30dfd9619eefc020000000700516a63635363ffffffffe8adf488be0da743d7230f6427b4d3100dee1861b2a8cf69254c7638b9277ce70200000006ac535253005230e04d330bf7d6541af1b9d166cc78a41a6874eb42ed0295b4455298b368ea78ebb62a91030000000253aca7c6ea30010b2df502000000000251ac18fca6100000000000", "53656a", 1, -1751386165, 1537743641, "fa30c6dfca2dc9f0764b1e0807885f83dbc7b9d70f47933a15af76679a44d5a6"], + ["030000807082c403026fc588eab305832dc7a8f2bf1a6fa7a7979b3aa82969eafad88b7c41e345283001000000085252656a6a6363533cb1547e7eab97379e7d412d9d65d4f28d3e8b18e067d77851d78b55fbe88c9db18a34f80200000000ffffffff03a6006902000000000765526551ac53651e6b6003000000000365656ab602d20100000000085351535253515163000000005294ba4b020000000000000000593d720300000000a29d5b15e2aea406b1cd3bc9c4f34b98c88107bf1525282dbbdc19e5a5b36b698eed86e68bb395f77d377bf5cfd93f89eededd16eafcceb9d4f2e3e4120d69dd205a22aa25ca81f371a2383d8d049b9635d94a10045eac530dd59f066b5119280000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013253c06a1ec2bc849883f454d8d714bbe291a0ecb3d29ffb94e9b14a93d8327548fab10ea0255c099066cd00b75be28004fcf1ca9c663442436331e06de15d1c6ee6f44cbbd32d6306d0c3427f300084ab63d6f63e771f0250f6b06ecac558067070684f44b89b9f25d341f740602e8c58522970724a295d9d60f0fb4ab2016022e353714783ae333b560a88660f8a49ea1a6686485c84bf1ac961206ba7e2e16031dc1ecdc9d0603be559e8f43c909fc02e095561f8a4323847255c6cefd8175bc0a030d7c730e0c6d714bc36a9f7357abf112d13ba022720d8af8627a39f38b3b459092c892acbf15c34ae1d03f556520d0ae1596bc9a2e685edd0579de701adb4b031cfe887af4000a040ed66f24efecd5da74ede1f89b66b8d0ea07e29bd123af64022035887085fbad82229725a7f81eb12cccdbe4af9119e71e270c96a06a757812032414682c587637841eed431e3359958f8580c0369de0d32ec9e07ed9d00a487f0304aba5959f05b7125ef1afd7cad903d984359a8a8e559c2685bc73bb0b51966e022c75cdeea3ac5e95097f6d74f1ab84d4709e67a3e841f2c8fe8a9f789644a6625beb638289f05bda51f425c7abb4d8ccc1a83bc18bd28b3c8775a1758bcbcc7e9745c3adb89f4d37a144b568eeb5c06364fd1f6bd14e3028c65fa22ea915529a5dfff76a7e77a0fd67b3a86639ad16d2dc5a1d91c83afd7267efc948f1742c40c521a6386748eaee5ce022e517b24f7ea198721cd7f5c582b08f354dd49fa36043350d97b30709c39b755315adec260c4e4d6843e10f57713b705446b28d8895ea5714faf7b8c771b95e6da5961135e6d7b0ddda75a16c1f3f9bd9bef0f719d6bf70b682ffc2496ba5b22b79dd82dd1f2ed98057086ae9311436a6cc3d1c2a23697f4391e2c5184da198ba1ae457fc0d865c47bf9bc0c0edd1274270fc309aba4309b23aa29f2780a1c5c78634f95a7d9c627394024261c38183978ef3f24739dfd3286ac0e6bd28b853821f29a3d6e133f8dd3abba9c4ca44e956d80e151e4a09307f2545cfd2958673b9a41bf6c4e494fde25822cf753aa5c69c3d302c400694ed3e1320d85603439dc897b3c6318afd2c2de5b5c491f8306d22e51da33f9da2844637e1616d18c2830cf55b8eebf6212990b28a73d41eb3a05e4311e1e1bb26aef49ec8077194c86035b4b2b0496b980b81678e6e061025d3fdef03b57062aa26f6bdcb7a48e6c762bf950c1fb716c9160f6f561ba575296f997b1ca419df09cc400f96683d7b0e31acbeea0d0edbe4c09a4e8a50f04567a238d0fe0a326402f9431f997fa2df593a051fbd41a344e9e043ae901eeb91993426ef00a44fc32f786294a918e6dd4685b9171382f1895407ca7ceef31e85dddfad0227af96c83e34ff8593094c28db6392c3d322c4856b7b23915e06ec257ccb9c6fe1c87640b83dad8b0af3287041a3943681669e7187c4b2f59ef1a0221753717b3c69a9123a618871e4e682d857b75c86521881b9487e2dc3e32c380a1d5f4d8e42797580418491066486c6636525366ea40efea311e8062005b778cfcc2ae1703c79a3bc65de9c08db26fce9b85f4b302f694bfa3b8cf1bae98cbfbf4524d4db5bbf33ef538d0d72b090a645a74e04f91b871f7bd68d9174dbfe8d292a7b2a9b58dda4b84cdc68d48b27e06423927b44869377f90b43ad6b95c029501483e54abc19ffb919619a711ff83fbdc13dd8ed27928a2362c4c7c3a2985df4502f96d0b8812c0cfb41e1605a713be2127d93cb5c81a42ece4fb9b3fbde10bb5fe5a1bd4a3975498a08d76f6c544cbc64dfbe414aba1dacf4f07ce294d74dde1d2470fb1673d3b5d962c7036376b00dfa4cdbdad277825883e7e440c0925c1e917db2afad3a53ca5ab8af24058d48ea164833a165cdb3a7b0b5685042236c9cc78638f3624326c859ec9aa43044ae27a18a45d422bc848a925ca10f88bdd30ad57cf29df68128c67a945d18cf44aff425abf895158aa9116ba9bd640b5cdf495619e44cd057a857c6db4e79ad37775781225b33026a5913175ee6689f545386c3470fb1a43be4d5b5c170b25336826b8e1c1bce11b74a1e0633a18c2699b541eab1cf55684277a6b2871784fd27b56050c40d5853901e88ce398e77fc6560de2aefdc2ef9b8dfd71976541d58eb6ad43c37273ca7476a5379600798bd394f17443db74744fca0de794ece75ef2bde9b9b0957347305322bd773666e0a7f0c4612b8f3ab39005cc50d8405cc7c101e558b0d0a493bd373f91cd0764d320500000000000000000000000051aaf11aaec97501588f4a5769f9aa8f8c0779d4c7a703413f6bbb430c31e1408ce5cc48724ce470a3415f502c9506fc6395a57be2537be8989260a6fed255af29261290a332a420138c8ef7b362b81f17155a0f8bb0b581e1671ec50e1f185a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032bdaa42757f14d3297ebefb2477fc5fe5d5ef85f12bda462f0317bb654c72433f9ed14462b0c77b42e004cbe55b1cc89a4f94f21135158d98f8f5d6f716cf8b7a7019f10401537e3f1cb7e213cfd089bb9459c0d8c2831141f63558e802ea0e0106f949c62cd353a8e2a9129185cf1c9a71c7adca4fb459f26d89b2940dc93f022395a203022161b792a2a6884a32ca55cddfcdf9bebb2a0b3c2ebdb8802858220204ad413c579b00ddbaebaba18efa786213de4d54c50162e0eaa5233364535dcc0a03b990d6e4c8ac49c439d9c9fa61a03a34080d26d9cbaadd1256c9cd1ec2e8256ad8410e2da7551fc7167292ea492c8471fb063e293908be13454881a13b5a3c03014aaaad7311e3e1e6464bc72fb87c6faba40553b330ea592af29aa7858003c502058d97b45cfa67803bc4bb9df486a15c984c590d1e9b5d3d2dde8e06c6628a44030289ee5925722248871dcb6eabf78080d20f459d5e91bed77061d69d012f78e5020ce683818987fa75cbb25550981fea1811bc9b63c98736585ca373df6f063565021ab4f315649baeaa527f61a469704644ad5156a4bba50b0e4f718b532e7cef42b5ee4784512c7166ffba43264af6d4a6f46f313eb44f7fd0b016b9eee69b434044a0632ed11972dc022d4a37852637bcfaf91608aec756d9ec7ca4706659dcd54ce7e83d148999fe40cd2ce85a280ffa2434446b406af34adf280b93cad853a43de068a2935102420c90f239903ea8cbc9af774ed730dcb5b3c005bbcf3d9252626b0b8a54c7e99714be235c60ddb98cf718722a23671547926dce67b19be29ab393fa125e34e96bd0a3c1f674c39fe058ce0caf83c59116b5b38aeb46f5a3368f55ee629939c40b71f9ff8570ba8051027cb22591d54c3187d8df1fd034066d001fb946311ead3b4d561795739e04f78322671c90cf3101948c7822f5f7cf9ff040e5e88c5b6fd27266523cf3741e16e3bb34e59c15da8adaadffd2f03ea85d0582880490c82d007b2608f83af422a8dff75186c00057098bb9226fb627dcf3dd1982ce639fa5b14a9b91a5e0140504fcd6aa26f66ba0283accb5ea8dd7d539d60eb8c0af8cfe8956e51894c88cd654c9bfaa4cb8269e41210705f8ecd0411d798b9c3b67c3ba7ef0277a0221cd32ee42d417ec31fd84be996cdce8710d623afeb83bf2b7a0e39dea025dfe31f8a42cf7a702bba0881ba2ef3677dabd78cfb7d4af3ef7500942859fa19b293f7752cd0ed9de3ddf469757f08867f358d7c8f648fe1b72e0e2ce1f1c57de3c5cf74835ab1a22f8513726489242298c0c139678768513eb4c4dceed2d83248a13df98c9af7e47b189109faf50bf9b14d0a3dce86f64d15bdcf9822d77aea8fcb0b6147451363752e26d60c8d1856555bc49c4973e0cc7801a779ea4fd595f9a503cb1da6115ef628f5a64d926c6f1a0bae693b70192c92059b274d73811adaa2662b3a82ce1ab0fb8c52fc8cb20f9cdf4e389836e2bb0d63affa3ae684eaaacd1a71655bc11f80c14f45f734b72f80340d3bfdf5fcf27b35b1dc2c30d12c1e863b0a665ebf007bbf83099d6de281af9c5de90d760ff2fb02d4cf40c458e318cdd4c7edf58db3e18134f1b5a61f8a73d8a7d42ed176b0e1a946c079d25560d20c99563f0aba5eb28ba991349aff8a4fb68b29c66796f1acf9776c9ae9f8112a82628b9698138e207f6c481fb59a2e5e0e268aced286fe5666c7bbde1ec49bac75aa461cb2ac467152c4f8c0b0c4ca18dcacfa7a0f1fd0a7cf6c59d691f96c058471cf0ff975a0e6ed70b1098cc01804a178ec5bd47c4f5e5db50c6f96df2d95d97cbe57954e9cd955398bd519783a5104d9259559ef6e7b7033f694ae3901b6762830b0e62b37320f277fcb57df530ed46054591bd756bd74f64843033d5e7b5dcc05732ae7d4dcec5b963af9101e95d34fb9698755f6524bdc4267342ab47867e718ed90f0896760d626ff6018b56adfeed8df025023d2d3b9ab2a71a57d9f6bcbc2db74cbf4e8a12c034f4adb9499e81f334fbc88a85d7e913575bd15e56aad3b12feed401a3899ba596b48d818a6c59a69cacac3820917c66bceff4aeff9d57c3456374fef643e333914b61dba72d92ed600f1b049e881b5e7b6f9203da125d52885fc6e73087944067c17e11169fe4781386dd1ea7666b3d8713f034c542fcddcd1b2410c79964edb7aef3f9c36e673bd698487dda6d0ab0b066589fbca67b69142c7ef34eac849f8b80d299a1dfcbd05e5dcc2e81f5934f1b1d728d3575e8cae45f7252d47b04b098d72c207708040e5a7795e2a3df5fb54ff4fc83935fe6991e7e4e85c12d6191f0357af77d486e54f1481fdcfecc6e7b7719e4feccb6e9ee043d7dedd0e7b90f1cc49ed74fadb85a95ebc524ba6e560a8047dc753b97c0d109f4f400", "00", 0, -36157852, 1537743641, "3964d96c8c7ca7b161a537e175774c8e00884e16fa6469d268aa29d165268e27"], + ["cd20ca2403bffb1b679c89eb82be20cf9e436d747da941f5828c6cde3b69ba8ab780f45b3c00000000004d64b8882812f6b1fb4726bb1cf764b6c9e4cb255b300f5092fe694578905b3c952eaaeb0100000005516a006363f38adb11d056b91d9f801a761ff8d4252fab8e513b39732207327591b6519b1b762234cd00000000095300ac5152ac52525121975f54011e65ce03000000000352ac6a24c9f7f500", "5153ac650053", 1, 1687145526, 1537743641, "61194ed560514b592b4efa90e5f9cc480dc3bfe428d69779da8d904e403c574b"], + ["030000807082c403039e532459e25d0ed1d6553e8d7d4b141d6b9d833a0daff9a10b9959e49eae74590300000001acffffffff25ee12be25bb4c8f389a5758289b7b13c71e9b2921cc9ce7ecda87137a58524b010000000165ba17a07dd141308d6fc06cfc35ff3a4f091724c6a704c6186f173f164cfd3c9be714006d010000000551536a526501c8f129032a0f5a02000000000453536363f652590400000000096a656a526551525165515160020000000000acadb18220de3aac01000000000000000022ca39030000000075e168ed89333c6a80ed1910a8204bb7f35e3b81c2335cea05b40bc81f06faad2621df2a6aeb6095c3412746d82a07447820599021d925276f202243f1b464690e92a93c01aa0fb396e543ef8258094581126de9948bcca646ea55c90ed6215a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001315508589115d2a3014d5a8a99587c1199a88fd4334fc85d18c15fd6ed02fdfbf9d2c795ae7db421e07294093c77db4a3b2975872a2b2bdbb1b21b7775eea741f062a218f8319fc01e77608478aeea83e60f3b3df41d1e39cd9e09290ed0781356177bf8ed5ed3ed9245f6af0a9f855068e7426e579e2e19d0cb3722172d7c40316a2f69e46c70e8a60b7bb17589570bc18dbeb7609e4d7f67266b0903a230513022f15a667b7b87c606d223e3630c5e4b7760d81e0567e03fd2a04bf4c55af33240b054cdc010774bc99d3e4661371b50626c6a544e29718e4881461167fe286cd1b7d29cb93e7a6290161297fe3af663e3b8fff26279e72afbf6dd32f8461defe48032f06d726c9f023c1582da21b41aa040cd525e50b43fe30e78e973ec323a16ae9021b859912a95708f9ae36ee8caac4859b8f4bb28948bafc4d1e5076240c0f927c022a771ad84e1af50ab7f9c1d1469ceec8a9ca34fd2d5bb5ad6b8209d2caf2005c030e0b3c064701242ecb7aeceb57cb340c1516cfb6f48541208a44995815f1623b03230ae317771f5c2f0ad4ccf9ab5f717aac04c28039849870c2e47b5ec83f3638728534ba459fd8ff1e3e28817d073333f76af0e90f63218d81d92e188fa6314328c6a1a067dc8886341dfec05ec92af1c0fae61c6f612e4cd1779affb3d0992490b69f900ab5a3bd0cea139d5fe45136b209bef95ebbb099d87070462c49491af21d366334e4ffe551f7c3530820d542d67d175b61881188a1e301b8a5c188c1179bea4ae0a97c85c369efb69cae039ffefa3e051fd757ad8ab72f5788a052222a36fb81408047cddad909e9152e0b9f1be2adbca931c73c7955063f8fc3ec35d2f14a69e75993522c89c04493036ca2d3ca28f3e42a52bc5b2411ecdcd49c3f451b7e9398bac24b067313efad866997c39bb81dc5328f8bdb1c70d8e4cbe6d58292d4b0f0bc82d9a74d160a09eb91a1fe3b610dd023995d8c63923927384278620f1211a902027904792aae9359c797ff68fce2b2cd791122e0b743d921868c1c20f886c548cbe33b1d237453994397eddb02b7d6087929ef041f77b0611e543cb2a2efb88031d9f1c99a62156ed25678e4b0079322f8bccb4a5272d10d15615bcf24292a6f1347465bec1699032a698eab8983d81be9e2e32ab8ce9aef36843865c296053bcfa6ba9803dbb1cbdd3bc580a8bec0c4ffc90e678ce2a135b79927cb4d10ae7e8c442c4e5e797b5d58ebda8474ef74664dd87ed46562c49bb522c1a1d14196f0357d85edf814acc1a5ebdc7bd75fa3950ee4246d7d41ca6374e43f934c55535d9e7e067e0929655d498f4a881d96798919d65f56075ab224c2fcc95690d9bade6fab809862922a742b7120f57d4610e424dce5a5774992e1a4e897f042d72e161860f523e82d954be8f121c50351394b739bdab9339c2624dbb58e3866329a9f54b87d6b65e39a0a2cf332e46a340d79e6d482339d22a44c98f0e667637952f4ca65a235bba3ad201894d4b0175a2ece95cec36c8f25a467c4172afa22a03dcd997419804131731e432b9ed82225bfb465a93080879bdb50447c14870310cc0a62ef781e08081146b0aaabba6230e44d5966de2b4d6db906c54edc087f420fac61683d4d0a0db1ebbb2700b697651f37645c46fcde35368e04ec93e116c8668a8cdc698d0093b8926251166e7032e09dd204ec3d0e0e415c06e31f2328880b4660f050ac6a05fdd46a0825599c3b5499ebd4b473648a28374e12b4d7dc378c27c752985fdfb706df26b8fe632f7b93313d54c212c612f5788a89c33efcf71553b9c6014b22bc3824a697f830c54cb06f251bfcdf50902492cfb0efd57456f6522dc1d80615f04d54b1915d716b3dc3a89edee4e5ccd60a1da32d1372588fa6dceccc3636756ebe60b6c579f232a0aedeb805b189e74d1fd8b940f857a83136461ccfe9ed10632d938dc57be0f233ff4d7aa35ad0af515958d4929f7e82b0b0e3f9de2bbb701113611bc2939b7d38b301360a5e241c4b62ee434424038e11441d1f4742adf5f32d938bb45051cdedd5b7efb1684e050d6e015411570ab91e57e009f024dfe51d9bf64312aab68bba51087f2efb02e10e09b763e3172f87fde55f290003583322e18aa8ce7eec0d1db9a0b82707a5ba8cc44f318bbdabcf776cfc93ad11512622abda18bec5d0f8239bcc9a1c1ae52c80d4c813b6844ee347c7619700c70b4e205b95b829f4872a4308c55b8153e57d6044d0fd710a009841a83476a310138410d87aea5ea8db1597b4756fdbc2b100ef2a8ed91b4aaa9794abf1acee49177151542f718148e80edfc489a6bbbea8c88dc94ecd37809f5f31610cd2098de2d44cf57b23df50365ec636248607cd052fa2fda71c65beef09953d6efafa05fcdb1a4fb1fa818a0e", "6a5265", 0, -1832888214, 0, "b054b0a4b03f7fc224bc57c9d1ab93fdd7f17245e5081285911f94e834682c89"], + ["99e765700267519050d5e47fe5281a6e74608a777a64ae1128b1f025755528ea615f76418b01000000065200656a6363ffffffffe726f10caeffc15cb9c85b978ec7bc81cf78546b64b01f8a9e3fc9482b3862a301000000095263acac6a6a005200ffffffff01ce54690000000000000000000000", "52525300", 1, 319053645, 1537743641, "b9d8fe6d800a9393949d90b4f04f0965f1dc565b5309369c9a5a7dcf018a28be"], + ["713d8276045957e32d939a19ef3968ed4f6e0477e5f28625e952e538f547800d84d02d67e50000000000691e81cd92e1031441efb3f5217014b984f659b5323f6b6d42d2bcdd94067e51ac90ca6602000000066365526553ac447b7f1633cda21b1ffd1ed1c17092670f7bea15cc1dd93657b1977f633c2f123ba53a4900000000075353655100636aadb21c7e04dda19a4956957b6337c24807d562435952919ede84ce369e1a368344cf0cd402000000046a630051cd2ef28e04d33d220000000000055263525363c3eb1e0100000000026a6571362f000000000003acac6544fc86030000000006536a6a6565650000000000", "", 2, -1718253438, 0, "1004c354134ef3e4ce6b4328ba0d6eccbd77c024a681ba5fee2c51f1b77ee7d7"], + ["030000807082c40303e77945de79d28760b51c43d9b80ac15e06bdede7357019d2a6ae75cd159c5fe502000000045265ac6540ab8f888337966eb00bbfd6d4a14f1652ce53f912e9415423e11dd652d1ba864d6dc7ae020000000852655253ac6300acffffffff654b9bbad0d3924a25434f9f26cd555ec38c5f5feae429b79916a52a9196ba79020000000152ffffffff039eccb004000000000251ac99e888040000000005636a5153523b855503000000000700ac5353530051000000000000000000", "6a526a6a", 2, 616752151, 0, "f89175c252c64325a215c0f0826b52bd5f49c56c28e3a2d8745f76c59487d7d5"], + ["c780af2c012175e7c84273c21fc958494daaa145c172e91fe2cd695db6759b11b1f824b59f02000000036a526a4b37be9e046b152e020000000007ac525253ac53ac6af35b020000000006006a526a5365a28c28010000000008535351515153ac51327ca404000000000251634a970cda01973b7200000000000000000000000000e5fe13ffbe0076cac239ec2a91f85471917c03f418c10a0443245e1a8409305ffbf9196fdde11b864718173d9a929e1a5a03dfb805c4c8aea2b350664d4682246eaa2d98db3e7ca61caea35bced603ea7e6af646b3ec726746a9b430a48c8f06000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004dc19c0b811c2e1f67bc3235b0087590d0aeb122be0b568a26bbe97f210d4decb7771f209329c6d1cd0ae49221b0d201b5af56d93b5c42f72a3a25434a8e079a954017865682a8c0daff02dd0ca294ca89ad7e5b0c87d24fcd376e14f670eefba07a22cd02c87eff0d6dc9c603afffe10b1190bd0731485bb510707b00e09ca4022fd3eb7d39bfca30543a09af07a38cd81d13183d8b668d93ceb1818740eca945030b9f1b1e33cd558df5ab0b2adfabab48852cdbbd9d70d17133411a9e8c40a2d00a024aa876b5ab0f421596798ad7113904f4f2529e2f3f3ee482de3cc5fac8486bcafc16462c6bcbdb3bc0121ef3c924a7228e13067c320969e0c7a5c63e0c1f3b021d43d00de0f0c6a235040967145e7649a78d3a32525f088a5d605a0feffd9af602112e55a837e5781ae9980aafdbf3db98af5491d770de4013788d38291f32bd0b032d5ff27b507da724c998f7a1810a884535a433b127db3c02f4c17032025680f2022dfc0bcc6d8b0fa5d820fa67c00d8d7e2fe249f20ea0113ada97a46c57962ee90320261eb91730a8da457f18cbc403288806a165680afaba5326657b72a73165ec9b01506c68d87049add88f18195ccfd35f79c0cf061d78070d6307661f51346faf4280b41654f4c3469be1902024e2823bcce16d444fef0178bbc3f73c7d0e9cbf04fb055f7dcf98cc952a4c38112bb64a3738336844c5e89335c1f7637ae9015efc30f9e3ff3dbcadadad5d5f8e5581c4acd3d687a3b4443e621660c3cf7786758c3540008f2a3025458a424fb051ad94b3853170150bf57590e5c83da351cbb5dbf2d8e4e00e644b8d323909aff1757c98949f9fe21b6ba6cdbaafa6ec29796b6bc7617627c60ecba16a4e08b246e1f807d9690963d8d3decfbd274fa6f8b5e1c7e69e4f53297908e924e74579a717af5f30f3a13e1c987691f0c6e2072c5e81a1d5dde8dcaee754055ceccc8161015b17970f13cf4d163b25aef4a03b3bc40f7153cc4923b37b55c656632593ac5dd9c89e9d8a1d792a4d9c0f890f79bd7bbcaccf32000777b3c64c5d1296c6d423960e78c26f1b33c390f355b756c013ed9df9119971506f5fdc2a74b51884e62143c794af2c4ea125806a6ca4222d88fe725b5bf2ad3a0bea2afbb5d40404928e26f5d9124911b0ec2873256c4bb6fd4d6c1230dc62826a6f189ac36a20e458f399b55e6abaa0fb0bd3560c6315b723ff163d6af673af863399a6c37c6c0d9f6dae7ec147bd0e7f80616371b4cf20a033c1f36cd835bf83c1747011416ef43989692ca66441754ad89a48ebe489e85c5be91949aa71b0746879b387645a92ed90efa5c41ab6707ff897ce69ceacbb4d53188e54997216aa81bcd94aff97c187fdd0183794eb0b34feae11e92ebd3b238b92f2a8500fea0d97b3222427657708f43599c24ed201f702b1eb00072aa9063027cd5ddbd9325644f0c4e47600bbf76efd273557119901ceef5523c948ad90a9846fd049235f10566d29a07d672cc096b4c585e10f031557850332e455eda625a1c0ff1bd0987e56676c5caa1fce6e1cb4ed6bd509901f5cd46883a416b87ab68531e471ba20a62fa550d745843c730397856c7b66c4b233905cc3449f3adcda52f13990b18af9816d388f3a0370b57797520c11776c3d988a69cd4428ab362fdf57d93f9c1e817f7c1eee0d2d0b43ff6fc3360b085170ec8831468e5b7224529967fe3c838ae56d389359b1a600589ae88cdd19a1449bab9dfb94f2852d595334e340660037f5683eebfb79862a2451793065645ed8b6f2bb643fac807fd4ecd34b614039c82d3e68388c8cd854e8682a696d6e33b1d6fe4ca998ea944fb218fb834e3714b98e15f031a8458e55df5166fc7612aa1b77de06a9c95f3f232d21f942b725f2a13947947b30119a9c3c9e43fadeb850f5b9230be138c7f86fc26120946ac6ff16cdf250a60fe3ea6d73ab1c51e420782dc09f596ab8bd5be5b85869fccef8c411d6104e7c2104c91f5ecafa994dbc01bcb8db79571ec40563ce6f7c0f9d90c611248c1d11d19f4b4385d388ea938ce31ad27f6fc82b1ce0463dda049d83d75c7e5ad9ea74592285cbed234b36f781a91cff5c9dd497aa2385b189009a6be99b9c08b3baa0f9307b7c478ceaa199271935e1893c6a5519bb66f86ecb02031364df34847834ec35655f6205cac3f44a46b91548db4f6ba3b075fda7f04a517c994c522a30e08a7e2f5c45e73f6c61ae8a920c07d20134c6917b5254b57eaa15067ba36582d54ff3bed792a849eef56d3a1f73dcd08758516db72cd65198c87f8dd0f7ea91cf9cde9532ee440e7a7568014fe854a45d010ea55c1205f0fd46655d298710be4b20e0439c206da04605827fdf2641507f654f5ec460616cd73ed17599f30c8f5879892e7e049e1e06", "", 0, -1644364371, 0, "5037d59255ba254c1212d0ccdf49f8b7ad73aa86587c7b2821a831aa0a35c27a"], + ["8210ce4f0113d0e038c536f6ddb6d1f6d2f55997da1380007851d7979769fb45b4c8e3731c020000000652516a536a52ffffffff02a9f21400000000000165a03bf80400000000036a6aac0000000000", "6565ac63ac00", 0, 1435527252, 1537743641, "2355ee38eb1865c7156d59ece21775d4b1087b5629f6a64f1b2409f228061851"], + ["030000807082c403016df50e466e672944040dd45bc45d23b8ef8e0f88604e80cf224e74ed737e7db90000000004ac00ac65ffffffff014a3fc60400000000046a63535100000000daff0f7d03d19373040000000000000000000000002598d42cddfb043011d20654e0a0968446af0656f37c42af0e36288a8ccf387dde7c346b41631fae6a8b13ecd846e32210ae543bc08e0cf9849d4c830b02276eb22777b7ba63c72c0dc8a915dc682976d1c4828827c187d3979dd7b6272c74380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018ccadcb1b750ec46609e20d79a08369968de2d1bf595782ef7c6b1ab0bf4a9eaf65b8a42520da5c4d86e9762ed09d46856767fea85c6eed43f46f355f4a4154459a515da636174640bc1844f00804010fc5b3f2c97d81e4f47f88d900eb5de51aa88c536db64e06c9b52d7700bc17264ab3840ea466b1b36b4f22b56d9e7e17021d9de388514859a1d12485997aed66d3eee2c8a7b6f4b7ffc88870e2071559da02102bd3d5d08cfda205fefde6df575a1bd488beadeece1c3839b0b34fefb3339d0a0408037159023b05b92506b3e09845d8738dc7aa50a96aefb56dd226c59fe9bb61cf573ff0fbe3cea89e96eb7870017ce5a8971c7bf072a7084f01e5cf7c5e4b02203d3915587836c2294ae050fdcab55030da4dee7c811f713352930333c62b1202224bf4558752d465df3121cec53dc00a14622e34143d2b1222ef50a862433dd20200c15ff29eeabb88e9fbb33611f86da684813c48d53eb8c4e8485e9b25375613032a49b4bbada57cf187723f949d6ed7e03f53cef582fb3fd997f61f5333ca8902032164c6006b0407226ef436d6ad7a31d79651bccea37b1d3ee7584b37b921bd1290fdaf90b13e5d0b796fc765d141712e4f8c9245eafac9b7865700dc10dd37c2904aabc3aedc4bea963cb808c7b3b5e95f90614489add44eb4f41732c84cacc8f8119561da70987944b650400c5465dcd5af18b65b87fd38cd43a3c68119bb070bb8fbdbc04d6a295c23f00b9ce88460e668d113e0a9877557812e2d4c6f57a38708a5544744c4f5692fbd93a36d310934fac45606ede094f2411d2d9496b395ef418006e81d8597661e56254607a44e69ebf0c717b9ec2a988e534558d2550578c7d08c6e94a26a73f5d68b799354d0ec88d7890f5b303c42638c5762b7fdc81be3955b3cb802060d34d95f653bd6b8c56dadfc23d8d7715e90eea25df5f516aa482e9d6b253fcacc33024787cb6084235d0578024cc348d070bdd27e5e5af2aa3579c297f8c0931dea75b27dfed7931e4aa3a72330acebcb54a6c81b481219f884570217287fca57a5c84d494dd8d01604e857193d3832069453428dd5d2d079792135e502f820a2b1dd1e0075911866b1d56399f8e7550421c987d2d19a71e53557eaede02b9a5de192f1a890e9ca333aa7cf51004c205a3c1eb0fa1de6ca8042e7bd183557999f6999affacecda38a723b6ffe98d6860d52b7a1a560881bcc808fcf9522c559f3ab89f655ca61e0345e9f81b3a8982440d905a10ce9ea7af43c68544c3709fbdae0853af8392f303e6ab63481d81eadc9f74e5e66ab5bd1eddc1ba1b653ef448b85125208b325813f1ff89fe4556324f1de8c565f68476920bd1a7e7f429b416c37b517f954c9d0e6a2626bac292bab17bb4b834d0c9373b264f432746bd8c5f7555b7f37311f9bb1d1d070b35b57c8c21b6de39b2d99d84cae32c33d61de08947cf824ea445fae1b8c0916a8e4181cb17a54b24ba24fa06c5f700776e1c3a6f116b83ee8500a163792a272a7d1570c29c62e3aba971403aefe840cdbbf5099bbc0e1d2eb0997467641421507531858104d861129dcb003b8ccfb373f54a4d49f8824033c85dc2d99db740c92b29705e53d69cb8a49fb83196eaf8b3ef0a555ac1b1012bc1b347b631747be0a6f2c8d629b3a1422be177c254cb12c0b1726ab88c20253179c88f2453a956c09cb6d2f0b49c9fe9bc8bbe628b1bfce86248839076c7bff45e76db298b76e56f4196dc085382b00fbbf29b52db8e58d536d07dc9123bf3be5a89a0b48339003012fea087c4147e8e6bca836e84933e84c040fa0d88a2e4c3a556c0d6db4e43cc7354d0330a8fd1cb61ea8c81b394b7e0fb563bc65958cdf681bb2c55890fb79b72af214b613e024566164c34e8cc8f8f97376a55353650bccd51644fe5636532f20c0ad9b7258918d32b4da574cae34b79924d431e3b2738f2d1016f4216f1e37b126894134140867def5d8a7ba350bbdcd8f24d316669ff0e1b59a60e9ea29ddde7c1bbcdedcf827f4f0fb9ff6fc2618ca6d7be109f939b1a875d90ea37ffdf45aba55b6bc06432077377c3fa15cf59cc1ba5534024263468fa8b9c8d650c1d8b395acd83838da2574da61c9b6bf21ab359965df8d11c2700f90827dcb5e0465e8751fe061251c7609cb537b01fc0298841ce5794a24f272b7bde65133c6a0de5dbfd6d69a762c0f4a8d90aec1eb62b526d22d919656f7393917a82ac0dc9d1c7ba80d4db1bab8a032e7a4117436496302c5a605753a1cef5d026da3db5c83e002000000000000000000000000bea2f02b89899f2df7d1ea49c21bab423a00c7352b87ca18ca823c0ce382560378e88be20e47255c8c1c53f216c8440cabf3b1179bb065c47f9361191775481da3d0cad2775cf805c17e9691494ea9fc5ac2c0ef01d095b033d21ded1d42aa0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f5b3bbf4ff5a04af5e4db6544425c621821e2d36a86d4ec817fd413f3677e504ff7fc0544fc08515272a5cfa7de5d4f1be8824d601e32d5179bdedd63ca0b33d5a2b7c5632a3139f290858450eceeb24ac7af4c8f7763c2b729516e515690b4ff6482172da12447161005ce56cbd25917390628c948f7217642f2f64b71f41d8020094ed5e55fb504f088a31d1a5f69a401da4fa6606f8c1aa292132d0612e3b1302012da5810fbd500e10e2f5eefb2fd1588a4775570655ba37c5a52478570878ba0b010dd0386edf1d2b291727f2085112620a27fade79b1fde454f325229806fb9a5841ab54f4db0b918520b1d0f46f8922cf3b8bb503bdcf65a04833574fd641b40311e193873a7be4ef621c548ca820e0a6ed66539b13e25d035b0c4b01dea9de22021d87b888d0ca714ff8faab26ff06c365f9de52abfbf18c1ad6e574418b70478e02277905cca615e1b31faa1064bc8391cb2a1ed25c332365189c8ced3afcb37a95020668e01dffae5402b8ca95ab0bc75cd9ee330dd8251d4415c75cf71e34cf397502073bfb069ec83773779c7e929b064df8ff68dd771cece9d34907912b8a7c4e96e082c0b970e5172129a46b9767dc4b460574cf033b49e0c77a60d86719a238b71c69c30d5e78f02505d25f6491898c10a68dd1a663580496f5e94245138415de5471fa60ef2002c2e670dfc10b4794914096a40f2fb97165b2cf3b1fe39c9d6a20899cbc8f00ac0ea9399cc7716ba23c73f7db4a4d5fbdcc8373cca933fce0ea9b621d786741669dc5d0911fcac5a9a5eec88d9f66c59a64848bbc34c923811700c430f2a2cedfd84fdf1e782e68250fc60bf3e1e44a607724424963a6adfbf37611219581313b7a8168425c99a5b54d040c21749b6795923d034a4aeeb1162f6e354f94bacac1ef7d42cf0b57143350099a2319a7f674eafc2a04f990fd610e3537b147f425e089687ee87b5e58577a8d34ce76242bdf6de034122734f700651b58ebe1525a86875d70a0f63c0ff7dd05cd1f13451ae52c3994f14fdb9d13c1aaf3a90380bdd84cee332ea7c93495cc27e14e27c7dc356ffc90d372fc5a7e17bcf8cd2b5e3fc7259cb8bf3d8faf6d05ee8ceab678c5bde60e865f0e5f6adaa6ea7ce41c04734dd020bfe0f223fdc9188dd8b213924a2d84cff04ad205064ce2b91475fe7a3efee157775fb1dc3a76e609585bf5574467c48c0edc32109f6a9824c954edb1a570c2ca3296ceef904139a9fb79c0c8aba0ce58817dcd95ebb288a81666d96a4c2da77b4338ae2955a9974752c3f4d18f8cd0d5a0be572e2e53e819e854ea7d7043c4a056e0334303b1f142e92175af48bb4b6852e5b36083a815544506244323ba316fc4b876f096e7abc78677832e68ea5eb7b136e934fda8af42543e1735ed988975a2edf034e3e982d44b180dfeb80ed9eadd40c08a2b61cb03992d0df450aafa3f461f53f35635d451155e2d415a0f3c7ae5d1c7dedcc41578466f7447932328aa8f2fd860f1c7b9a286e6eaf0efa3921e534d0c423c63ed6dac66345ebe6664cc0d34e6dabbd1dfa1b0f5a89f66ed2683906b994ace3d84782b987fb0a0c61ec55701d1c24d9207ed536a2745a19c89696449b7eb5c84522625170ad544b872b107fb92e0550aae445d19a6d48bb2a71e57bccd068dae01ee95d11d77b1ec18ca3682bac172bf08e7d68c671c2b1e84ef3e5e2d6b4df0de55bbb763e1115941dc23b2cf51fa87a86576bf470b3b0cce1bb3db79f48cf63e912f20ca7f9bf72f82a5fdbc1189ad945dfd9f3bf2938c9a541d4ed5bef30b7b967de674bfd20938a8c1cec059384aaacbeb50caec7a22d8862b20707f04fbe698cfd0a2e12127a7849006b5c84b8a34c3ee6d230b1dc91e47bd02823e6346989ac2279f86b9e687bbb6acd3de2e3c8ee745bbcf7323015d1e8d4bc0ef7dac5e6ac82c2f58ad74e9b69e8487e6a9e252cc6949a2876a1cd6af16a3083210ebd7b18e961b2c24b2752bc7c1ae38a45c48dd95cada79fd1c6abe465dfb64aac11f2c1f74a8965ec9d139670fedb54eed315d4f890f57d535e58ba28e0612fc5f9403a41b18cab2f1dcba1352e8baf9d38c3ba2a4e04c369f875ad08790ecb59d64c493af3f1a1e5b11db4cbbff5e6534f11ec0c3773cc7e033b980c6882b868e923bff6e3d0cd0b66541cf3eb484e081c5dd60d35f8c771510e1d61e3872d1c4fd91960e7bf7feaf8e721bf0f46e4c0fd0a5bd36a05f95ac4b33e13a264a7749e91200a16855fbfd4a14290b15cc6b822bf9731fd085010000000000000000000000009bb506f1700a0d1ce797813912cfe8629b1c830a128ea47cabe844c26df76ab3c3cd8a8f79be3bdb6ea5d4442f02ed07ad33fc98ccc2289a7a4e807566db71c95a332e4b621dd847e3a23e6bdf616c2eb47fa33172a18cb634b08882a1a339a500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000efc0735a2e8a75d8f6a1407021e75484749ddc51fcd53b81669b63807e9de0e911d98769a10a9b0a9215a8c96b89d80de81437022f63060250a9c496bd90b4852660cfa2b2158654bfaa0018cf990c4dcf55c4fe091244a9e1c87e20bccc97803e5d51f796b13444ed1d21479a70f4264e1c2338e3721bf1630632d7c20690980306c66df05409ac8b32ce9256d51d11263ddb5c11033dbaa1c7974016e9548728021dac1b936b2fa4d9f598330a9d34473a2ec966f95d4284595a9d89052eff32850b00082f1331cb9c4f63cfcf6c74ed6ed27245ea8656e65d8fd212fbed5dee1c5229a728aeda1da055132f86eb4a88643e8a95c34a5fc1a54803528dbd4ca4e4fd0326c4017a0bedba94960b89c169b19a604305f38afd53b02ff4e5c3c5906b12b30201edf6c0c23801754afd3bbf6ea550164d716f337d61988c48942f960ee06fff030f5984d2217b768a20529429df7ceba29e227f7a2af9477169d86d178084f52303246475378cd6b44c18abf1a94b5679e23b0ec948015ec96ec32cd8e612fe409c032e512365359dde3d772623b98055380ae1c964f899987204f3fd7de4cf436b6dfaba0379d7c54337a9bd838069d3ae635a5e81ea785d53b644010c2900dccdf23f6d344c01589a8942eeb0d096d6f9eb17db8333f91793f472e2ef82f513f854b040c156a617280b3af6c0f5a249dafaa30fe57adc4050fff6583a064638e1034b26adeb6d6ef4055858c6af95127a90064f0f03e1f77d2f31055afebfd82c52b27b40c8918c60bbf5705015ba5e00bf9d0f1c02c2326011dd7d50b613b2a264b21a0d890f06440c2332085c3847380dd3623ea6176e74d9bfe6eaffa080292aae9c0e068ff64758c12b376062bf82c33bbcc082b52466fd7e7bdf4d4f7fffff1d76ff7c86e0867ede702b3006409088c0532978da702f7a681daa63d17a83bf08d287cf21aec6ed29d6b0f312d3273723bef2de071d2a243e93d3621f5ba6aaa255dc6c7913fb87944aea633e8c7521955ec589636bc2258a45f3c6b076edbd8d9233fb49ec936193e8b315be8e1ed80d0f4f9ac2ce6a13050f172afa123aff697ad5ec688cd5da6d5505c3383454a78810473c9a295abb6fe3da753aa0b163d1932810f341353041b18efc8125931cf5075e163f431f5676f9affc40e4727da594fd9cd86536ef91ea21b565f170b5f26cb1793bd9abf6b3308e0bcf47f22dbdf1f1ae4561cba146ade4d9c1a7b29c78606f30b7cf3619f50f0b24d517066cb2e357a25e43f9915593e400aa3fec815123eb9fca21a5aab206628ae9974f8a6c2310c071955250d33db3e482945e808aab81f571cf793efe153b9b2604e57fe30a1abf4ba0045a5ac02e36a597088de4b30d0ae2c5c574014cec07a5daa6781b80e9d870415cda3c71cb856b941e1298d4328bdce47af6d35ef7f845a22ea24882c2468ff5823b4cfc6a55415d1eb309e871adcf4c9d9d21a1674c0fbf4a7e10b11408c943fe436a7948e7ed6bb62776aa30bfd77498d367009254440e2de2a9d50c4afffa29bf988ded70a838c4f7ff0ff2eb36448b525a4cfba0db0f88e7a182af22117ecf4e55b78443c78b21e0742537608fddc3325cd3b9aa62de6885369761e2d25d1cdff8a42d69ff400b5f1f95c21fc5d31dacb006f44cbdbd9426483b6fb281664d507c58b73d5d38b740e6696c59d1471b2d085e28b050c72c608f5d9e66018bec2f419b0da180d8de63b9e3526c6f644e8419a52249d513075dd8277428b354760add376f09194d6be6e9558589930ab6ebe93655135dac775becf6b507647a0878c20d02c1b4e3fad2c96af5e0cdbd059c08f8f2135f770e6882e492cdd319fb2c22a388cb2bb5f1a842da95c781c607a6f598d1148f481afc386eb7ff8b3ba0cdad945814383fa4d4adeee0b42e3d27d46d9d3468afa57087da4fc2c51515ad969d02cedb54c6e00b3890871daa656bfa22d5171503191f73447546068754cd160b4f665dad5dfc519a8b1ced9c584e2ef93428db54559d54b9635398448b1da1fd507986fe8ad22e31c3ad4ff8414de8931fbff5c20a29e7e982bdfc946748e281202392261d8ea0fc47aaaf071fa4744360661689546c79f719134fff42a2ad7ec5deb55c7197fd4b7b2689b435a3ecadc310b8eead9211574e091206ef5a8ae9a28587dc46c32898f7d48238ccfed2469f362aa3ac9be2e30e6c8940d0e404f200c2bde2680f3d3227a5cd22545c9176fbad32f94cf3530c21eb1bf4c1ab9d55dde32acfc41de2375626d3d46a84e4d0e66df070a784ed4d8672beedc6bdea083e2608cdb47f9e1f520aac66afbbf04bbef5587e99b24177bd37289aaeeee99b9e74963134761a7e7f40ef0667b07eed996d95f950ee808a72093959a55a80d79069666c506b715808", "006565ac51ac6a6a", 0, -1209250835, 0, "603a903617d7e22fec9fa07ee1d281b95ec45086cb99a2f8aca729e3d1e149a5"], + ["5d18a645029609d7f163e89166303175877acfa80eea98272c4f67c8ae49ade2c0dcfc95e003000000066352526a6300ffffffff30d2aea4e0f01d3a6cd17a8efa2b2079eeb2936a484df564cabc1c624e49a471030000000153ffffffff02577bec03000000000500acac00ac8156eb03000000000463005353591869b501f8cab10500000000000000000000000052fb65492934179b90b256a64467da061ae263780d2f01e83f3543b3758d74e5810e3f4a0e01a038ce5c3e26d7cc6dd735147f8fafc10d76b4ad8d8d22c37126b56ea07a14c5b40978f6ea14234ca4718e1e80f2d1a279db04ae963f44ea70de000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003204e8fe0b6834ff168f09ea8150f1e989f8822ceede3d3abee750bd5f36e3a4682b193b98b4f0922f9d86b839c23d9ffa6ccc8abd9d62747d2475ae1c8ccb80b749fb8290fa4bf41e9df7cf65459400482993c94c4b2c9dd43f9333e3a3fc9621f56b443a100185369d5c26a660cd8fe258bf64240b9fbe298a7ac916b66438021096310f0eb843fae5f30421f2c3731d7a463dc1604432365fd04b0987dabb6f021fb49444a864dc4332425abd898efbe9f96bca979cbce8e2259a4bfe0144e6450b05084304f873122732722d27bf43f4f2f631f02b52d260626f4656daa624a767d8c7b63fc4ad51f1dbbc8beedd2cac3b3cacdf00004e2d6e574ed0bbc3514e5602167b4b24fb465416d60ef903302d98f2e1b4f44b534cdf79bfca06c04fd015b90219a8da681be5046a811b3f62ced863465bf4aa93ef65d506f100b6d64498b1c10217213b9847a92160f3f84dbcc02ce98a71fd097118a818d7df08ff261fb7897e0226a5e8e400cdf7e98fb1b3b991b98dc575e2ffe8d90d2595bfe455892d17479a0308b75d115ba03b353a170bdb86954de4c0845c2a9ee766c2c0d9a437e412008043238d7a59e7b4652d7c8e372b0744ceff4ad37e57432a7bb5ea7d22313826415fc43963e4a3fc59c15bb9bb791194c876694b63aa0fb5dab3cf3b0fa2d44fe7f8373db81622519299ec5c704828d155e9b6a0ba3fb6075b8aafdd6c76d9db640b2decab937ad664b753364ea95d6f717a2d7de4dbc841140e48f45bf41211b0b55e86a00bd538e6a1f8a9da55f2492316b8ca38d33298f62a525ed1b95387ddd5bdb8b43a40da71a40aeaff4cc065731fe5fe27cd7db650bae18fe604d4205d8e8f926e07e9cbb33ba241b173eadb30f2de80d2637f0b27f35e520c0788e3204a9ce36250fd3bcc649605509fbddca1ca33523764831e10bddcce86675bc4b19516f957a589975e217b9c66207a31044284b1414353e054129f39f787222c9e882ff87c05e4d63372de8310950823e494cfc2941a7844e874576d0b85831bf85bbd69b15bbcd0b09ed5fd1b8273e846dcd265e5dee375dac8314c33b10d8829ab161d6f57aebef9a8b2ed9258d4d71c9c64229c1677dac3fcf1f0a6e189eb1dcb5c50db2f78982a4042b5a9aa07201bf012891c36a941591055365df39364c5627a5ce87050e6359136d48a2041b8c4f48b2fe62ce304c96af601d28ea291924a46d93c5cfceb7036ba0fd8668b5cf54db8c6eb271b25c7413a07985f43ae2cf31dc6f86f9f7974a65a2c48e9ca567e4dcb6a81ee64c7833e08618ed70e0bb78186172addf1579669d4eac4c7f72c66e3f8e6addc6326ca022bb78d0580e2fc3c68edc077e2fdecda931d859a02142ca6b1a7fe86b342bedc2d720cc9fddebc29691ff8ea2f88518ab48de29ae2f97a8832b917c08416b45e8c93ada78a38f8fd394f56abb373815f33ca5996890c3375e7dfde21f7d06e3e4ae619afa17eb41ce470795f2b540d691d50b0a392f31f067816e0534a2721e53169099de84f3e6e5f3f9d35de4238c1144f262d8bb1cbed5f00af4526d96361c05fe48bf7d16b71a1f73f2d0621ef3595460a57dc6d1096b371de47383c60e0543334e2c5d1c63b932665618993a4d909c3304728f9f58dd378e35675c6d4ac7595e1ba3f2fc109dc249fd17a83ea298b3a2849476baf70cd28f147ca8c8b842bba4e1c905e57ac1830467e00fc7bac690dadbb0395db499d93ae8e11d40cbfa53a53715a8b9bd6a0558fe87b62e136ef9b739e97bd9d6b706b5365e62426992a68ac15c933ce932f7bba7e91e7ffb893e3f9cb87d9592fde386e15cd31ad049db979ebd86ae2e18090e71fbeb36f08d4c54bc987d8e24310367d2c4bebaaa4a90ec0f09dbb9e83fa3b1d02e201d2b8a654b2b9ab9e3642b73f9a373be5474fad2d8a67616f4293f5d6ac92d446882c8b479346a5c9f61c6e8ecaa5b15c25a4588b00115cf406c0f93c3d6626215f28712edb8d6d5b5f67f5ffdd38a8c4c4f976922d61b5f2f6d7457cece36f679ea15cb0bbf541549d88f4b76e9bb3cbe948f12643e05dc61253a7c9a011a094e4aeb776b3dd7c8a6f29a5a6b28631abcea33753e5b2deb8d819f3efafcbeba6fd96c2c6a642ec9f06375837b4e12593634ab216751748613ebf40c05f830c91481fbf2eab020768ec67cfd1f19215be7745afecc9009e67dee15ad7de69f400c5a44eb127b478ff14759013125c672be2a51d69637d246d3cb47bd545f9b2d0575932aa25a72bc718c88a5dcd2709b4bf3d262c9943cd32d6fb1cc1fd8e2cbbc31bff9ae0e16f9ed147a52fe5bd3dd898a47bf15f7c030d9fc5cd09c6c20fd96deab0dba2d4dc8afb002be1e7a621ee9d67e8e3af0eab7487a9d36afe7f87beeefe83d02da842d19b200d", "525353006300", 0, -1246051117, 0, "5c7693900f0edb72eaadd619ae974f01b4e39a498d9726949c712354e27768e5"], + ["030000807082c403015a0805951a8ffc76690b74ef503bcf45b80a70f6c1dd8cea2411680db2535051030000000451acac53ffffffff03b1d05c04000000000253accb7f0f02000000000563635153522034a400000000000252ac70ca5107099d3a2200", "63", 0, 1974096124, 1537743641, "859face0c1fda2ac9a4736d932c8ad2a1f6356391eb8e83a206ae4a236fc534e"], + ["e198de390220c687a5e23ac5d730cd1b74019ebcd52aa3fe35b2b094eb84d174381d6865450000000002ac6affffffffd904e034d44af34c5b45a6771963b87434dd2080a17569a7a34ba8dd73c6ae0d0000000007536a525165636314bb49e001a97649050000000001520000000001000000000000000050501b0500000000d80b32d541f1f8c830ff0fc09ae146165288363ae7fb0c4e1fa5a003bcbda6043fb1a1badbcbd9595e830bf5aa9a5df53a813c885e5ed52d548cf2f14765e2091f695c0a07dfa4f1bfc9b65223ed19232055c67efba1c1ad0319ba7f30a36be000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000aa06eb89fea39f33af386488013e6eee8a0730b6d6f12053eb29f98b81fa3205f37747de9a40d08e590698f6c737310ad86817fa55345c0e6613cbe4cc3477f3d87fccce72c9f083700d15900f0baac8613fed419165178e4f3eaf76fcee1fff0a9c5d2b17cdb578a3675521acfceb83749545503d92205cf47734fa7bee069c0208c12ae2df006f8602c8eec2fe1c00771c91f2fa6f9c162333fc6b9b29527dd5022e24c527b7ce9f783ccf94de7603e3471ce90b8356847f3f3aacb10f82c8be0b0b079a55dcfd0190b3087a417efa60540fea5e29da60ebd8bc0c1ebd26d81646863f4eb6e35a2d35794d69ba7469aeb485eeb45712c72125c1f2422d052f2f8e0c022081c07845c3d542372ed9b485c4d559e4008ef31e85165f2a05877ba0eceac0030f22d2522e0b91561320f17de60721287f63028a5c1e9570a07ababcad8861e4032ee5c9b701be169ea3abe489275a6e7d4f0bc7168c2400bf2e973c72eceb65b6021d3d848761668a07af794c7a0b5c2ae0448435d88b2f45b068a531901f55dfa6020f55f0585068f688ac1d070d6f2934b70eed6bc4d797a186eb9ac61bbea55859a95f5cfdd609a4fdb8b632f4cc22e79fbe80744e5e6a6c1195ba98d2406ff3ccf50fcba2a1775576c5ab3ad9d75556926b2b02bb5df3a60305952b4e3e20dbb39e1a1a9dc9e4e4470d8d9028a865118d3b8ed2ea2215cecc539bc7eaef6c2062bd5d4152fcfdc1e7ce2f43576bb015d2153d8bf995f31c01362cf3c0acee1512e89089f958908d00a219f9997b86945d298b26d11a5bae4e1e2521c07f6adc0561dac798926616d460790356d7671004a2870262f32e8c0588ecadd39da05cf438d3916be9f297acaa185e2016224cbc739e084b86e3b04a8f47786145e89e5cf28f259341bf8fabb642f28442b27974d5ba77012ea1e8f468821375ec720622323a1119ec32b86a5adb31cb7718b2aa67d11f0d6e785e3b531491538dfa488ee8bb9a18db8944aa97595d340cb905af894d1b32505142bdc757eb06c829488c66c681aaaa173e21286cf7ed8091a7b630ab02346825a9c6d43fd58ae0aab9c5ac57a94bfd8d1e5984044ce1fe364831af31406d00155198af7f9c6f939784a61bf9f7c06a4966e7c657e7c230a96b50900c80ef074c9841f405bf637d122b91ecb2df9d8dbadc9342691597f560c6a5cffd26350e5a613cbd597d4107088f06b7fc12f254f2661c680ab3a454cfe29de9ab9c0fedce02de54c7f58eb1fac44d7b182d0dd13d4de8c0016b9be16963bfa294aecd8b9dfb58ed4f3927afb7b04b17f65bfcf209b0a028ff4abd92e7cddf419a9ce25b459a59d058442f380a1453ddb01331bb1e5bfbb0fa9d326f13c6398d0af0415a179b88767b42df052f5d2353484f47fcdb012521b0e3ef033ce8f4a314fea6945ef2d04c2ecbfd406bda77d500785ca58b995ee2e7fc39b2993ae8c74cfdfdf6bd8abd6557550581c9a96e6c3d4d03d3de2cf69278be645abf33d63fd05f97737741da651328e1334d804481f8d9137a3529f148c12b17df0c8b51fd2d2e109bd861fc0391296c555394cd930bd846e322708d54f5e6d37e5c9e00fdf96ef53a894e3fc5f44b71189096b18c79c92938eb7fab8382960303b2feaef9f49bb12d8be47807a7292b204c791f73ca684a7b8210094e5f28f128998b7e425b3016ad22fc84400cf4f16262e45d3998fa8a6833f4f2e6291f9892e5133df2182924cccc31dc4cb7df364f8c51453e3fb7c8790bbc4b88e450a8d63dcf802170c08b13dc5860ccf84f8daf38d914d8dea2aa904931fd66b9081a2baa791889c260e99bb640926fbd5c39243aba6bb3c09d1d44504637d1cea9e2c12a157ad01bc8639e9a917a4bfcdfe280b2b4fb67e909dfe56aa8522bb613304f68ac092f97250b03483a95bf8967d97b50522084155bf457ca8305e0f25590cd915e0014a7d5f50d97fb687603e339f17da8a273c3fb4a63a36ef5b73a4478d5faa3cee775c57a5bf74f213cad1a037a08a70f406b95c0e155e34a3eecb47c2d0b989316c9aef3287017d2fd47cbcfc7bdb5994a5b5c1d1a834b692be684af098ccc38482efc63b7c91a2c134b784d67d90cd6d3faf3627813cdd42f21cc3bdf57373ade657c86338bb85a8b6908398fe35f2812eadc0bcf3efe661db002a91244d5512ffb44ea0458f503a78ad53d6d744cd2d52dda416ae22cba7e662a70366b11ef47748512e7dbfc8b6442bc811f508d04bcef0d8a7793dc3d8f47d5c9279225496ee5d80ebd47fdc482d670f73a12b61f9476c808f3bae6a97b7ac50d491500333f824bff2eb8a797683bb4ceb32f73f00e981c6b8b080d54295df7338455f82642c831ff7fa28329d8615b3c47ad61d2fd609dd1f9be6566f9a0c5ec99e838e89f00", "6a5163", 1, 1235525320, 1537743641, "6e6efbd55b2a9f64c6fa1b52298f5c2f01cdaefb12345be4fd0d6875d8926efc"], + ["c94266200133cce88fa56cc7fc205eb5773941db35e968de61d85b891dfd4fe94dabd0e9430100000002516affffffff01527ef2010000000001530000000003000000000000000019857d04000000000ee33885c006c5d1c81129202599ea4d2ed914ac0d45d31e8cdb6502eedc7868d379a3b746a95beaa4bf0bd21e2ed248d272e27e0570f30150f218d19df9345c78a494de26ab5376536043dd1f96fff6203ac164c43f152531a8e52729e9560a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b0f35e77c62f20185c1d2832eb41f95de14ab021e822afb35b989de9ff310ee9bca890a8a0ce725016c05b6630a9af3a90b48fe59921a3914295695d0390bf9cfeed670940d78ae0386c1d7b8643bd95a8b5d7cc8c4e692a865f3d06d5484f13b9b6458b1da507592546ac8c39159e495e7deaa800098bfe8ee41b1f0aafb896030165adc4b23cbf1cf1013a5374f7fbfa5fb82430bf514bf258e357e8def0b0a50226716a5c9f5726a0478fdc7f383255c7b698f11fdd9f9b7916a179f3394216520b081fe0d1ac6dcc11653167c47ff41ec79176c5cf029b13e284222a8a57d27398a588358e12a592565a8b35802c960de3b8e2287f97bc8364c26a891bfb2c64680224ff98b1670e4e91be5bbc78479f894550cf0f01b9046e30e9ed62eff2dcf6c9031d43400c81a4889164227a4a7df76fdea8318d33ea1578b760c3de0af1f23a6a0328d1411bf1a5132909d6f59ceae67de8170ba3bc7646c5293c9d8263efeed0930317e91f049246d5e7b11bd807f51f52ea14651125df8e61064d790cfad71f0f8e0323f98cc062e94598a10e85561acb19c119d07262a7ccc65dbd99407516cc9d233ec16ad2071ae6531c092a3a0bf5b0c27c119c3cc2a214b979d1bdfd990d4f0ac37ae96f2ccf777b915f46d7f4c58a878fe75a476a29343d5507b006220ac5dfddf98cfa5e17e0462005f1447baea253f03925cda582e67c7cef5e2f2526611506b763e11828c14259d6812b96d6490b727d28e9311a54a42aced57d57e43ef112f8e1b36a9686a40a0254c2651bf135b3eb856b98613c52fb03609b4b95e5caf4bae879ef03174c42873785aa4a680ab9f8be1b0127413807e6764f5b9cbc053cf21f4700c73bc0032195e17a7fe4607b41b4e1a33f26109427ae7ee011fdbfbfe4d7391127ea914c3cbdbf9478720023324ac1b515052dc9993cc36de1b03676c5390d439352ef55bea31a40580f5ea6faff75174738ad5eb7573e8c4012cce370a891631e9b5ed699da83e2a12ceb7e16e65d94e829e6c515d0cd8c6feed505c6b40a1df946bc06a3781ae5cdf7ec75796072df4f1b0f4c76d94c3169117bdf591a7bb0056362c0383a733f95d56f9d29b9c24d8b0aab03b713ffc696fd2330c3a19ebecf95421c5e90253053bd47038322c7d919b11df59e23a78a282f4b59612f38a8cbbdf0713b327cd45e7380aa902d4a30d0b7441da4023d164baf1f9ab10141e2ddf8c6de46aa7f1a7d9da4cfe6753c4a8d4a0fd66127b47fc9015efaa3cd905b110954d650a4d17861ed1f8e33f66c2d8c9d02d53470d28f334fd5b711d2eb33cfd75648d365c4c09ee7c5567a3ff4e6f9e0f70864a8f536ceab658b76ec1aadfb213f54b49dccea382139ee2bcd4e712c566de7493b3016163e76f5be3475fa8bcde13041fb3cc0ade52a5ed9fb774adc24b4f751217638af9a8b94cb90a2ce476df4d8774ce73bd788979a1685ff9a37c087ffe9e2dba69d39dfaeb1ca32e2bd7a14c48fbdce4db4609129f2eaf339db0e88479a8ce96467ad9ae575d9a73dec44785233114da3f9d3075da08078cc1989a259d3e65aff55fa7a9fcfc520dfa775e62fc9620ac53e0c5bc15c66c8862e9c59dd9a3cd1d0d5f0549c8f63a7e0916ae145839b20779e803ad1fe7b0080e565494cfc5d3c16f4a3f6b77ef0a23306d443bffc5e5550ac450afe4832b8878f9b1671cec9ca62f64e724e45e2ac878f84583d3380dfabf82576a33e091282d6cade0c1019cb7276ef8bcf0c4ea3f33a4a2aa5b3e8e8b7d1e18edd01c0fa3544a47f42fe28ae048452928b6d14a31ed638d97ce3f49812c78c15548acc45a70ea2c8193937e5d1fd5eab5ed0ed3d0dcf920670e18364503b2d48d48f5783808ac5c5fb216688b29b42158fce780ffe172af849172ca2155bdd8b380c0ecce95994dbf3f2dc0dc7776f782990635d22d77a65bde3f23ec2245eee4bb16b44bcf5efa2032275464613b85f5cceff260afe47cd62b3bb9c9de1e9c0a6ae96d882d7dd3ecb10648aefaaaa35f6fbe085f9a4aa7f6ca0b9e41df2a301a468083bbe1f13880e966a84fa178dcee72cdb2c34a43df3fc698837a7722e5c70da884f7b98b021260c95a71911869d3a7b7fc06f15e9207f4587052261eccc86ecd554bed5ea9968a5fd99b9c19ac292e33c12727ce887b391a6b6faf493940c3b07d7bb1ed8e8a0a2c38eb3814288c1f9ad0795c29317a49f98e3a3b6448ff9f30844a3eaf545737109f575befe6a1386f5df12963a4ec3e125fb8b705afc3cc500000000000000007ddf9803000000009cf159b769c8f7514c7b18e518f050c01046532d672cc63ba97d5da42a01b07aca69bc39062af361edf0ce1fa21183487fe98c4a5140557fe31716f7fce53ebec9019243a27261a4a69fef4638294b21a5fbc1ce209ab4d1a5dcb26c2435fe3600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013dad0a7a8bc3afd3fdcf38ae895bad10dc6e9b66192b3bcbfe69f4636b93cbff8dd72506da6a45268a16ba8c0aef8048999c819144b9a10646ebd9d81d86c3bf2c995a96dcd518fa864f424f5e72b954a8bb3efa57cb9348c414ec44fe9e87da5062bfd599155ce65d71161bc1ce90572eccd9ef8e3569aaab12de7a57787e030db6946bb2fb71fd5e84af86059d9eb76375bd0a8bc34be9fbdb3023fc44c91a032dbc31ff18b38ed1ac9ab9e19fb9a722b90b2f5a53e5253340668e586df3620c0b04458f41874c4ea226a0303bbab1e0dd22849d90ebf2eaaea396c70d1bbc40459ae4f3ba5c0281e366570fab7386f98fbc1e610b5f55b9bc89acfc5390b6e423020df4b33fbaed680f80cbef3b1d56adfc4c9d361c4276ffb930cb07f0af8e55e20329c5512e0869db1d97a2f265b7b1e03f09e2365f3d8c195edb76ab54e3722c01030f07eae4a0db973b7924a786e7ce7e9dc79d0d62e9cb94493c22d307a25c6ba603113cc1cc232bfb99687295e2b6330c3bc4a97c83c44adfd4d07bdcdcd3961c00032995259f0e6973afb2af718950703b415201b4890bf74f95ac19e8e22da486a7745e190708a0923e0b366062e22feb505cc2a24f5086097e55b1b6c81c7c64c65b7fd8a5149af3dcfbb0d13d224875373382217cc76744ce393a5dd6f3d31781748738c209fed89d6d6358ac665df8f43d8f4a777a7b5a7d840becaf24b856a2405fca6076982f43fb46a8d11a3a68651563f20b0c17f161a8a382ac04261197f3c83c4b99cb64c1a7532fe8c871c2d44e19577f1b403676ed99f49557020639dd49cd967591b624ed273bf83e36870d0526e7d14d99d553cce3bccce93230ad89cca3b9d427c3c83abf24f7124882ba1f038c507995e6b664f0b65dbab13d26473baf47f37534e41203b4cb1953a0b4c02757a48563b97dc619e4f52089d41a851b1473cc083c62dc2af253fcff4566c9dc96c59160b41b1dbdaa7d19aef0e8165ca829da3b54490e4bebbeb4444592dc0c9e771269dc2d6430e24dedd107a73ed6d988b5164edbab3ff611b2d9e53ad6e22876dd3a29c3317de8f8222b3be525a26b659e9b120aa10e603686ac1b172c68c453cd7f0f0b1f20f1c5e3d48fedffd83c469ef25fec185ff1e5f43e8f7250a03bc71fe175129d434130aae2d3c124a02a3a7efbe78087eb921d373ab2ab17f1e98e1d4c109acc064d9a5c6fd7237eceadada4dbc7d749e396afb463ca41cd5a483a17f5ca3115587a207f1db75c68a5a16ea86dd7b3e798b342acf1ff78926a9f6321ae13d396157c842070ea0e5e7ef6695d4923a725c8059ca2f15e0cf95e0fb3479bedded45ed7a0d5741e05e28880e08642d60799ab961624fc5f84a774e5213ea6969129e840a1930cac27171c300f6380edbb21b58cfd26f86cacf062c339bb8664f80de3eded2414ce59a63e9ac0203912f575308acdaa35ef8038d999769e4cdb1830638f13fb2275ad7bc85b41259e4a6482b32fd2b96db7bda27df269e867f4ddc54009319647953d282777a3d52e36f4ae8a530132da3432689916f7429153e8e15a5f6f953c0679e030272a66da8856fb96d6a04fe3096047340f2f7e5e87eb1058c087b6623a4f667c851eb6e4328a4183ec5c884e968ac4eefc8a7225f08e9f680eaf3441c22521dfd145852bb3d55346d7837712953706d6c8b43d3335422c9734f71a8b81cb9c82260af7de6cc4207af68872e32f1f5e6a384e7ccf1e8ddf2d317e0f8c018cede03175774f16d976a7be8d7f8790959b2eb4a95ab31239e3e27484a90d86f45b75851fd35d4aae596eec28994f7f210db9233145430b1d26de115367d0b73dea770330b0ada61dac22a01efaff8da0effd52e2d00ed84565d6363677b80fd957e09e9361ff6e22f02bf6cd5c475f6603760eccc59fadecfbea3dea84510975849c71b8e6e698e22b0707f31554fdff767c5372ebc9785d26666868a795bbd348177a2ffd868afb7abf9a68b18c41b72b014a3ee282dc007c0db7e9b1bcb6567c09d83e9b13e31c537b2b9a827386154b7f2ecd2550c425e0dc7c923903ebe175decbfb2cd130a14ac471df45e7fb24bd3e78310e1704ca6c5feb73169e08493892f1639e82b2ca81e6361d15f19e3d29e0143b2184785920338b828905327133072ccd475376f5bce499edab71693df74782e1adf30371024eede9e18604300582116b09bd9dcf5ebd102d48d486cf64a9abe7f55c6b2532920f3053c65ca572222f89295717bac7c095804f506d8dd5e7216f8602000000000000000000000000f40dfb5f6b0fcc1bdc3456e03c296107a47ffb6f4c8d55ec4e9a3adbab0efab9ddf23937c2fc169788a76926d254bf929be94ca39a17e5f1a381e3965780b26143467874c94b86114faf120e1f8c669d4b870aa329c096b4d3af2d914016f3e90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000077d861e866e2c016218168152295348087975b5be40cbade46bbaa00bf96e836a8e3bb542f0c1b235cf0349bf3adb79e614441bdb183f92f8eff4fb0091c0caae66517424b7a140057c3f1542519026478bd2433213fa36989e470c37fe0ab2d153f9a8724d924f3a95125b11fbac2b9b9832188f2f2fabc40cef50eebfc85e702225635e30a34c757e6a8034a8d3b78e83b36098ecc71e7442d811b5cf23c48c90223c994a8900f1702f1ab1c860a6fca1314c364dd721834d8a1cfbdd81b43957a0a063b3ff410462a2623b9c5f5bdac6c1864c68972ebf364ad742d659defe370e8b01eba2f4ed9249ca86d11bd177a6d2c99ff6c8a196b7a06e515df95ee9adc810222176190d8c91f7d60b7c7c5955e3b8d4e459408a954d38b217dcaec79b904e10205d95b8ca028731ceaf7de478498424550e99fe08cb6c79a413884e1314e58530322194f6c8dd957df14f7cdd588d67e2a0517b51b7a976a8911716fde37dcf49d022b1c9e166674ec90446b65814a6399a760e16f0afad3ebd2d182b6e42b6acb8d032fb3b9eb7ae0e0fc65596eadc340c1ce0e778e1e3386d236f4928f3be86b0125cfe046a4ede3f38797262a255c729dc2c72add8ab8c477732e1acbc46b036342cf206924d1856073f47547e0fb119f92257a25d7153a10057a045862056d925b2427a0e8a77a09888fab11f39e47a4c5a3f1fe8e63beae002b60aed154bc6412f7c8777e9b8adc6f0c34917527a7ebb0befe09314895e463f4732e862c3fd03078c66b3056ba470bfcfda774b2f08b0dba37be78ecd157d1626cdc39124dde78d1cf8d0f852c4aee31038c90050e1170c65ce6a3fed3458ef3f1328ff1fc1eebdb6423b70de66a7390f4c7202e8c390d85bd375bc00944cd666d29eefbb099bcc62ddaa143a3023e2af5a36a7133bd51ce633ba2ac495c6ed13c4d05eef0f43bd9e0e5c43ee587ca2afedd65be0ee5808312f805fc8498447ad4ef2e8e24f4053a92ebe61ea873c3d53cbc327ecd06e7316ef9ecabafed8892e994148312fe8e03819fd3abb9e0cec4e8fb0190bda5b34a82d7948b72ac53be54f4c0b48210e2c462789d96c15d9b145b532b7a0c5dbc629566ddc442b01b14282860630bfb924f5804e6ddff01b2b1287b9d1ccc9f153abbffc09597a005a973132f48c68e806c63e8775df1fc44daeb9a6d1e10d72e46bbe78f435ce1a8c8c58eeae72443f40dacfbeaa5a562aa080c17167c883fcb795448b14ccf725f767c8cc4403d75d252d64d876625b6f1c44ae20b6de62b9de8369b20d58303e6378ed3fd05faf645077e8aded1763d782d46c6335da041573e99e216bfb59127898e7caee2216919b6352a245100de4188c1ed929b77e2da6425b125d4457c8484df0c58dd6866205edd239aeaf67e34e21c9f39e102de3a7ad6dcc0d73c4b45da6e224a1966e2f7cb7407e73a08c0aa3dbecb22bd4ce1d523bf008f3c21618fdf8cfbf62f7d2eb4c3aa09c58a85d9df7bd6520e4e4246d6bbf58aadc6462934c5e77a1b2929399fa7ecf9e1f7ceb7581d533a60d8382d859a6fce2619e70cae4da62abf015c82beadb639170cca867c7b69a1f3e62730aa339a54d4aa591cf20630f70cc47f2476d85104e97de764caa70277ce0550fb76cb2c4b9b7ccfea04b99f4aa01a271bdfd92dd00d872d3624114ce3ca9ea2161ad5f15600e89ff5c533e1d8cecc8e7f17e3ed88f390b1e797b9f72cef0448b0da665af5b28d43e961c60505226d31f2c126020de49c928e5d4f13bd8e59a3aa8259dd2a9d7444e3c631abfdb2563c5fcd6d5174aff266159886c9ebbeb9dc8c5e5a24fefcd4d5a017c97b5f3b4c06e4d63beee62f7564885cb1537e42a0353f437c5892de4ba46edfc1b615406095f8b018f4b4d83215067cd27bbed5d414ed7cf3b5a8af4f34f37b82df39451e5effd3db9919a94a12bdc214d6f55e31a6f4857d9438aa4bd53ae572a5aa02b3db57d84810122e9447c5be719be3b736428ec602f3b045253447290718c1b6528e532a9f01319abfcad2415e3df5f353d2d23a8227aa967fbd585dbf8809f0b2e067d37939ded9985d134617780264cc54f6e254f50cbeb058aa6c8a8359c1a5bf4dd169b605cbc4bd8dfd677ef40458c7c84acda7a257de9a6338c32a725fd7b85fb4f047d597d0e2921a7a4f698ad42940e6427bdbed4d8ea1630e94866434854a0c982e18f5bf2feab8b978f498282e04986d717e6fcddb8bb2b618e15a2370157f5f9622f1f7e547da962cfbc878e8773876f1a379f4cf4568f0b2f2121030ebd7c2df8f3b894099b3bf857ec5def50e409029d85cc2440fb997ba32d49f072fdb7fd74c7e3866598e78c82e60df1761304636e2707b9b49fe05f87ab338b0a19286527dd7470e3960bce3fa6624a2fc73080d", "0051ac51006a636a", 0, -299759723, 0, "5d84f838cf8091a5e0905ec9fd3c242f4f14606642f952649ee6c62bf4579e1f"], + ["8593b31a02ff7ec57a5884c0af5045496609e37d35b12928617fc8ce3daea1d66ff0af5aab010000000953ac52655363ac6365ffffffffddd8624db79d661c5d1605e77c08ed76f1c8c381e12299e63ff093731bdd3cb7030000000865525152520052acffffffff01a7909e05000000000300ac6a0000000000", "636353", 1, -1409998479, 0, "64087efef30e3b332cd501b1c4b94e02e194eab26a0595286a6501f9c04917da"], + ["2323a94204f29d2da0241e8f1a0d5af9f4f38d08185d5c6e771dddb40b95c818e013cb111202000000086aacacac6a510065ffffffff94294a8863b3e2d55dc24bc7528ff437f9cd4451e95f1b0925cc762b5df81643020000000500516a636affffffff210982018ceb653cce67cbc644389ac25b271e7770c90795f529dad054df82d100000000046a525163ffffffff21bdee68628be3b281bee3b975fd76357c867a6f56872a25e4b4fc091a2396d0000000000100e1fafe080229c1df03000000000200637937980000000000086353655353515100ffa25486020000000000000000181c12010000000036c9390b2fc0c26ab6ef448edcc4140ea40363772de8ce408d26dc7d5917b18e153d6425aede70f1ea590aded59419399ecf0503719471836258550463a998b50ee430d0c7782792357134d8ca7b87c458c3a2c70713f678625c7b4b2a536f90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006866c7c43660d1c68bbe04a363f68b8dc0e621e8ba067efb73de2605801cc7d19cd0eec413d919e4ecd4394f00ccf521a1fa60e8b6b89099545c0807c35704c757c91e4eb99e82d269f598a3c0b8bac93f33f8b0f17f150007d68ca847ce6b2eef4015e589640e6b07484329920d1f4bf492db1c4d5011f73f1447f866b6017e030200e7984ef3093b136d597f14c8e31bb0642b7112bfe0bf1c6386e78ea87268032b42d08df94e9b0bc18230314152ea97ec9e56246f64885a85d75447516de20b0b0861c073edb3bd9795fe739a6ea39a2b8d7ac3fd7e36d4a3e8bbf2d14ae366d2fb388ba31aa55cbdfc71d30e6e8ac6b6b46ad1900470912166f57f94221e7fe4022774e31bad4a7377c16a6d08f16856ceba8d41f8e007dac949e87e9c2d5806d1030173fe5c0e3171a970b273fadd0cf701edafb1e2990202126ae442c4b46c84680224779163a54fadea7eb99be4bb1f10c1ccfc8034caf0e34d27f6d7317bbee43f032eab307f80173697c7fc605f3d3813fbafeb622d061a1ccad3efeb352723665f02097d77139fb53c06d2758c19316b153e9959d9cca981328d3264a3e6511c09412116099b0506335cc21f62f822e385a06516174561064dd678f9346c2e0daa01e67661357a39ef98f6510e41bb28508ca2b4e38fbb6a0da1265627e74aa1dad644695f59e88088c9e97cbd8c402065a59c0cf812a3421147e8aea5bfd2da83a2ad1632e6c85f4d7c5f702c5bdda4579f76c75a221682b271576ddfc96c336b89aced9bf803d11bf7688075936ef6686421c69fff2e68c7f6ff53bcac69273f4a4345258d5ef19be98d4c4dd5d8941462b63e35dc4d8d24936e533f7ec91371f0cbaa9e24fdf4398e3547a0bd50369d9ac0ae8338492cf84bf6fbe7187e484a7f55d1daf1d2a68952ff892159fb9bc5b7c7230e1f040d3ad82f7f02de4fa83dce1a334bb6b7a2d226ecf0ba8b07f6cda7dda4e98ec9423e8e0c40f9ed98e545aeecade7bfcaf4231ed03edfd550a2f968618058a08fa16d2d37c087790a00318cfeb74555d394468885d81c1d3086ba9d73a59f1047c3114be74a92fdc69c45de094e7a29b80bbcaf62f5e78c4a845ce08904bb686d44bc519d70f403b7e0047a36da18e87a7e73e173566222465930b61394136f5b730b9f558efa6ce2a3392f3e3536e340bbb34c5b032730e2253f517ab4d3061cd6c4a9a79c9729e968f8a8cd535471a1adbf458663ce109545cc645f744186b5e749ba21536b3fa7489b366c0b793c000e42b71b8b8ef26f4bd63c5ae939ecbdf0697456fe17e38440090c7fa042e6b39a1eeccf1340c76f26779de5547637b49fcaae66bc6c1ca36cdef73e9337b9aa2377345b4a203a5da45c84096fe31fd1e7768eb452b5a00691b6b93cb26c5202bc5904047f3d24306c0d44a631778e3df3eeb5c506a86985fe2f7d92ac1fb483ce1c520696bff7fce2d7690992811f9d7a66dfd7116aa6ff54f5e9c0189ca2bd946d6b0d3f391afba9cac8ccd4fee2a089f1c57ec9c66bfa35e969c4df23379461176d659a3e7bc8a963fa0b740d4e91e7bc54b5a7ece1b1fb2d244064f957c2153e862cd82bfdb9f5ab24e7a7a8e85613d57a760768becd03af5acc95faf2f8fc6fe047178b463950dabc11c8259ac6b884bfe9674626b93db25616219298d53baaa3a85f4859a390facaba63d0fe94ef17307829cbe31278ea681572ab38eb10a2061b050c92306ce23bbb4d2eede9f67d7dd8ba74ead1e13f4690d92075a9f4d6da5b8a00cfe3d821ce0eb04b8b16d8bd3bef50cdc2fe3cec9d9a5f9bfe6595b266749ea022c70953f4a781e17baab1c6625b196bd93f654c76f5e346afe99b87dbe0ea5e9c701f0916b5bd857668c19370d15e0d83e6a0908f383d0225da40af48a96b5a64b21df49cb584aec2419adf0de8a2b112a39b7ad1c83f916e5567df2c43044dd8c6a1a1a88c8e9baaff29bcd48a891771c1d926b03f6380534eca581ea5cbbbbb5cd7c4a21cd5876838029add9ec555af9569ef2e83873f7294a6bd81fd76e1f0642610f932f8c38e9379881d240639953a8c50e0e00e15a69347476125347d33218f5e64e81c7a78aec1e37f6fed58221f6de868a7488e5740ac334b5711679ece1e1f744cb91813edbeabf4eb08b01f0d7236453cd5bc593b84ea9a36d6d48f2e0572cbdd3d7e9b82b4e8e0635e6dde9200d9dc6689de832fc709fb6b45e316e756064ebdd404a7fc75e22b58370c1bff24b3298250a52f51e9d46763c152455175375d9bc8aa0f4b03000000000000000000000000aa9cdbf299fc62b8600a2b3a19fcf66641515decd37eaa2e5a10694e4d42af286348883baec69da1c3c278faf3230e8ca44fff3e20d762c67348c16283ee7fd164b5c50f264d9e22b8a913bbaee81a52ef1b80e24b02e354edb24d29d429f3de000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009d3fb6032f9ab428120d0538f47d4d78c198040a04b865021bb8d8f9e2476d0ca333c2df441fea73f3013b97ff68efed8b38c255804791954ca5fc6a6d1c6c8253977da8bb6f6e76d53ab7f37271efa4ad417caa5e35f7977510dd8fd0f1c8d9737595b0c9ced6d7723e349a2204faa0b1d3c76d5f7bb7b00554ca5dcd9abc9c0325bda50813f0038359d91908f905a4307f538b740f84090590be5481f8d8e36203065b7b9b47e379e733ab5c2877632b6877a5f88d71ce63fb532ce068e6bba00b0b028aeab2ca9f4dcd97d53fb6df099e73402af12855d3c7350deccaea6935f63145f9421fe87797cabd295c3b6a4be5c5177fb5afe0964cfb7b8eb245186308ea021261f4619d7ea74d7c08ceb0d4bc06adb7652c44fd309730977b6decc5e756e2020c0306fc5e5f61c1747c88ce02780bc95908604d9497443a5256ac03c1e73f9e02242d51468a733bfeb6d4b4c7555c2a954277795cc6cf351e5b1e90d8bcf2c7520224626aba71ee64303e2b38f78bf9082123f7a5c296497913f7afb87488d5255a030bdcfc1c3f6add59fe43c73042dbd84494145528f7dd2f35b6d876f137c3a93807a93dd5a8cf7e27fc118398f18d3c72e88810958eb68c5187e4454be2f6a0f99805f63cd4c50ead5b6902ed06f027c4fdedf6396aa0206028ef16a4d724b05a273169ceab782e107348e34cc9f9c5626ce74cef30f04fdcf7ca24c3663dc093bd3486bedcc75ab07f2ac5113539242155b381dc203e2f6e3e2d303b44f03c84bdb47ee3880b34e30bfaf76225fb53d3d22d5b595b56abb864349260b528a6595a62864d06b995f97a9b191f422c213d2cbd11d712d1c7d17f4c97c269d6a0d0bc27d03b64327910380364f89f8390cfe367eabcba545178bdeef80991b0df6c52329ff2c9485ced0e8b143900afe113aa994111de1e2e486ad481f7349e5915e1d3bfd0490d15025edea4c70c52c9506914f0fa4c4936658bacbb65f5634b525aa6ecaf7a3c9f7caf108cbd99c06ee1a1267a278568d606a2ccc62cc27d8df2a192de1132d85095b725de61ce8e75b73a97c4a134fe7e6fff220794470dcfe3f4555a12edd52597fc891c4039ed8910a4f1cd5f604d5a5e250ddad3e0917b9749f7fdfbae5cbeb3e851e5d821067f0de54d2bcdfb3eef35d4f9dab5ee8df18b4b3315e457da67e81b6de7c437a7df91bf0fac47f25789ff50c983f077923dd7174511698376b9d7801557b3997331396b87ecf617bb7ff4fdad341d4bef9e139a9aa60a482fa3d7b8574124fdff9f746dafa9858aa8d7522ce94345e408fce12bcf8b24445d2508cb0aa1d94ca9d77b28d3aea9be0e10d04d3642bc988ab1cdd3d2f1be35c85632089ac717970c91718dfd7a3a906239138c9d022af119150f88ee59d26a46f0e1423781b623eec5a8b97c3ca91de3ce253a9652a3199f195ef2a1fcf18de0ff0fbf4b5130e10fe743fa2e594b6260c43cf673718211936754317b9d06648e1e985f45f2f7cdf1d67f7a98ea695a7aafcf77a65d6b22abf36c5401515e84aa5328552bf1ba0207c34e06a99fc4cf503c2b2fedb38b7d92a8b8b66e33eca54b708ad2a5d943a1163fe8f86bb13762a19299d6ccd31c7d8a3bb4b389b277842055f82e96a896c097ae007a05f0c98dbbeebcb9f528e155507430bfbe0d743fde24a856e61db70a87d3421fcde5e3a6e43326524824fa165a5823bc48b340dab7da10f7832b34213fa2b2507f3f69a8354dd2d824d23c6c8a70c0a0a2877a299d95e0a51ffb22330974efc5812c58105c4a39efebe5dc5573dfdf18432ad5a73ae2ca03123f9f107e1f3c04916c49015358d40cf308bbe7c59c70893fc74c47f066618f924d5b53780fd3bd97460d9e975206f1ec1349459f473455aefd11c30622459b224804626cca26895ef33950580d05ed178b2c25d1424fc5bb3b2445227eabd785c94ee112a98c2e4280aa135069ab7a96a3616ba32cc090a27db4b023ee5898db8c93516c975cde42ac2182ced5d0b126581a85b78621a53796572d0fcc32e60e6a4b0699f31c988cc86c37771c0f044bc08aab8ba49d05801df9574737987eb205ac82e821277d2d8597d90b3cecbdf13330ae3ece4a2ded2bad1257cb7e1afefd7028e4a7a28f0a833e00ef1c1149bdc97f44a429c0be78ae5168a2ec5b385c59a32f9609bcabba07e8b644a915173bf6894d243fabe5df82a504807fa445a16e0573ee4f5e98f48f4e20c5cd014013588eb3b21fec8442a411674463176f06599180d377bb747390e78e08120ae77a09d5fd019606fb0d172c2b537bc242e7bbcc19e60db6070f582da548dd40b22fd503956f3ba19d4b815af560dc55488e7eeb324e21ad2581724f43c194325a5deef3fe6b0358d4c8e5c9920deaf325bea24acd6d65cfcc07", "65525163ac6353", 0, 2140922588, 0, "2e39b76077c5cbc3e5cac53bdc50e780220169ba4e6b4a2224ff52ccb08ce0cf"], + ["bdc0300602e3c431b18ea956e29940b3b6d51ff513e496b1e207f2fbbb5877a54464f35cf50300000009535165520065005363fffffffffe30ca0cd06504d7a185ddc0b8b01dd6607764869e917ce467314219063a16f00200000005ac65ac6a63ffffffff02572b8c040000000000d06c1d05000000000553535352515c63635700", "52516353", 0, -680847213, 1537743641, "8cb179386ca061991e2d7921da44a8ef68c1079ebb824a0dd84582c82e77f5ed"], + ["4cee7c4601443f72e86b86cba58483e7c4aa963f8e3f582b6e6c24929f73575992c2f453330300000000ffffffff04049a3d050000000000d8531e01000000000553000000003b9d00030000000001ac1be4a4010000000002005100000000020000000000000000a27a8f0300000000753db64aa3e7c7f774b181fb125c555c3823f14c6bd8de96ca6f7d10b7090d03b5d5d4f3c6679a017e71d1e2c24f43490f6bc45f3036cf326402ffa2c759631fba59f7ba5eb2a2305d6723405eb5925189fc668bee52b79b5e7ea826c26d4e5700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc0e03a08e8c1b8bec6234a06e71e1d28ec34c1286a8ed1337f5ca61d8911ce1934ea1ed8eceed725a130effff95a00c60e212023c9ba2cc66bd4160bb88d8f971cbd810c378dfba8e7dfca28a02bb360a6d646fbdf6b76d6a27e8d6face3e05682e5b69048964c4d2528b39e9f93be3484de9263e0f971878c38c6057fcc0990226c191a6b7978bf58af811f5ac72b7f096630582887015022e3a20322c3761300204dc1e4603d290753579d0af4dedbe5ecee65dd9595e0c41705a346ed14a833f0a071756b2510818b5fc7f0310ba850831a85d99753de27833e24ee3373616a640502c776199b998c60a8ae6a0714723d431fc0e14834d6669117d2ed7ee78c376030dd7a97b1b3011054393fc6776f7e2c3fa568e690ec56d14a69f7fbf296541f5022082a982d55b20a3f8d963cf501d8b082cc3c23eadabb10875c50529a7ebe78f032573ca1665f31bfff7f95c0a4b8cee1cc99d08f306253f0bc089d7c7dd2819bd02198cc12b7ee7894bb243994be29431ebae5a16bba15c65eb0869cae53fd58219021c61aca366aaded833ba8a059a47c22ddc1afc310a5b7beb2e6766ac2c0d057cbb34c988e8bd104f65b55701815e8a55ac70ced620028ee3af1092ec0e7917349f7ae21115c60ba20a22f106d674f51f74b51b4a3dda84ec47ef87e3a409158392c5666343a727279869ef2348628e7c7b8c6e74b273bcad6af2975f28b2ecf8145cec6b352da78ae377bb41f9af2a016eda61a6a21de8534b2a1e79026838aad5852b811ab06a3a365ac2eb9e64f9e6ce937df48e5d82e0122562cfd87ca851a7af9609a7498ac2b5a468e85f7ab1d3ff953af11a104a49c7d68d2d39ed77554a60a66efc35f240b1e9a2db9c0b12c3022b356708bea1f0403c010ef9fe5d2f355663dc4ea58a809e3207339212fa06531375798fe976443f1828c9404487a2a95889c773b61e318a715956a52ee20b89112de642f25b70175f71f82374d8d44089b64d8df49b5378656a0983263891eb0871721edd52d4670327c7b3f2f88fae2bd5322bb59dff08ad5c0d90b53ca96e8fd1332c727d089ee5f09b859744423d3b1c8499cd9f62ddf6f71e4e1224b1e5250e62e2ac77f9afe261ceb1d9f51f910f86d2324f2e630fdb667acf27d801b0fa29690c7407a78ca5da9a6a350dee6eeb185aae655a5fbad101f5d079906f12b5add7e9b0e901d831711f4c87da59fd4449513d25e2de0cac0061b0a5b556709757713d582b219ebf89278b7292cf4608d27b44351eaf2b94871d9b3cd1fdf19d9c97bb683abe5cd1995be126666ff401c4a82b72b5400995e66947c3be44b0b594cb6ebd3abf88c9af8aebdf684ded600e5be4fc340bd7b2c5aa08b36a2871849accfc98f280d02f356b61bb584be5404f2611e56cfac94b474a74669d14e9cc3fe203b945d3233c349014e38f0735f8f15b06cb13fc99c0a3b0a56c4dcba612ff65d98592c4aa32a10fd8dfaa9271e46fe8dbd7dead85973c138b5a4c42a41a81fae1ea8020dfeac0046d81a738fc238407820027fccaad87eb79969979932da11e4715c55e1bbb1b52aa7f02ac85e102a011ea89ee5d7654402f089e58e52f56cad50139e202a5a9f2450d4a29e7b4689efb26cfee5546663ffb49de3f0a090cc8acebde00ef1f6e79d5ed894a9067293978451af1cc89fcb12f91214f983738ea99ec031601e2885822285d0d3dae60b4afe853443e54beef9ace5780d99e5744044ecfb843d9630be08c8bb1b2f235e44dbcdc48d6e0cf24475e1afe72d03ebde2d94944a31c0ca26580f4e9cd6661687c28f0d65fb86b756cbfa2b760a1b39d037bae8cccea90c474f0447c81d0ec467fed9064069c06cf3858c1622f54ed82ef5dead8e0613442ee064c53751eb0ce3596a65c4973ca5d73b910e82272ad13032eb216f3684e5ecccfa4c5d54425f489c80e2dfb24bfbb946832ae54edb2a2114593f7aa59888101002bb46c2121c3664d6e3231d7e10e4a3bb6bd69b35df5b76e6f07d16d45d360b4fb01ed61d969b8df0e21838eec70b7dc1b462c28b1ce56bb37a22b71735aedd9cdd793fa2d5835513c96671502f0cbf6e2c2d86c7a52364650234bd59f637ede06a499acf47bf499050590f7caff3c93aa062b96fdebd3baca2d990caf5314374ca5aec1fa4519b42e04b7bf0bee35cf6854c82f154740c74be5d1a6d88b5f39122c497d883fa48b3bc54e7df2d7fae02299110874e0132987e54f238df4d8055909a5fc084634b9b9157b237920f21a477a85fb32c2c402000000000000000000000000b2327e6f2f897adf583ff9e648917e655598f0efaa40097d8ded07ef48c4a2bd291f78e2a84006cedfefe28a4c2f20d8439a07a8cde7ba48644faa50a9663e8207b41d5a33f747bbd3a1431e46d8a84ae620641330ab5411268bc7904942bd85000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dccb7361fabed62a907275e9925534e9712bb63889ab90020a028c58f96785afdd5d29a5ac91ec96fa1625ec6d1b082570a56bb1790a29b9a6de31a763955eb180035b0ba30a92e7542e3f9c7b9518e6b85861c3de707612fda6a51f6b5196aad726fd77c9fdc1b2d3189ea2f8d4cd7cb54499a1869e6426dea3e4731c98948030445c826adb7a56bc529a7442ac07ff1211f5cefe316b421914e3314dfe0f785021f7f86460c89f5449ed1e37e9ffa12b163aa9ca65ea529b968269bb4b20401430a07cede72f923fad495e8967a79599f5731eacc9a3af86caa0e90c92bab9a7deed23a415565d1347840c829ab5c52dcf6005bb6da04f7246cbda8f58c872cc1a20313fbbce3b889b85b0f0e1c611a9f074fc1d88cad4555b48565fd0a4b7ac6c6ec031e3960b8509b62970b5c97d0d306509067c2ce0401c5688eb40f0a7cc4bd61dc030b15b764820cf0b90ae0c3d9747236c8b56646f0027e953f41a31cc2691cc9a2032b6ae3d7371be99292096f5f1c1040b7a43ca54984eef44a611f2c02ec02c8fc0322d4b54f7769ae60d269598ec45bb2781385b2de4fd7996736e7460902aa965a0c4c8d414d53ff8208fd91d67862d54efae0b6822ca384e40f218ee4af8c1d25c130a2818d4458d0ae38d661b7b5dce4c01df124d0216f11391220ac0f192f20462234b525f5929b7af7e85ebfe59be3f8181b4864701e02ea679037f66202e08a4e58d1bb9dbbe295b3b3dd34045c94f8d52ca51eaadf5ddb5d03d7833c96c1e578c8f7ca70e155a4e0f8bbcb0ceee29994626d20dcfbe15d1ea6902d02b1cbe6149da558c27632865c581a7d28556d9bd2d75f73024ed8c2a68f715156515f5d35786d202d09aa807abdfbd4b957c667c236bc4e2e5ef982463a8f94bbe1c2e85e17bc1d4135c23f5bdc8d7baeb55840ad9f218a0ec7db175d9c220a61438afbedcd8774de26e7e44278f3da5c3fdae818bb0affab97989aecbbdcb675ff2815394fd24cc9c2b5e54cced781eea2c4e57bd4dbce5ea8047c4a3e79b0796377edf7bb6de16b97153a4f7f2956c921096b5e696b1124bbca4d940c8330a1653d03a9177ba51120866959124155c9795ba8e3002d45e6d330bc22fe0b6544e2e950a5f48a175485e9b9e6c79173857f5fb31de2b5baae567f0687edff4b778dca6abffc92d0938a31b53cb73cfb4a313b02ed737e2f42fbd0caca097fa317a107bef246e498557c10a27bc62d1c5d47c85157b0ae5a3121c30b88e4f981517b47332e2acf45a73caefb097c9560160984292937f48ad5551bdc019a3a1790c00c388cef7260b9710ce7cb1aca32a558f04f5ebf75401389a39647fffdede118169ee0e0ecfaafc1142e68e78437d930be07ed620187a9cc8ff18068a20bf2a26b8738f84f4acfd2ea241623e6288c6a5ebb0888c150524b7a7b3695bed342f750ca5a59ac25b728c8eb114eb30867dc79f832c2f8645714d0ced8ce8f8cf4eec60a74f2284384f14f3cff2e368d0a762e8928fbad7ab35f149e15ad722975e9716029b934ab48615805666df4849346bc3c318e73dc2997e7a07a3b69d8215f009824977b32c5b49b67f22e2f72cb92fa6af797db8f1c3bac21f1bdf156fe061aebe8c57a2973563578d92ceb2b50093bd50102a4cea2c8c53afb9e62c99400e961159e36dd93538912c501075e86a1bb65b15bce829195de0ce44b2446a26067c87cd72bc80be6dd1f5d7896ceb0c3ba4ebe236b9c6f1ec4fce4e267951accac1ee74b199f4892b0734e705e1cfc41ed59ef4a00e5df0166f6e8d09a7ce18a37efe8fc164bf99a87ee71fea37315769fe6a92e596f9f6105e0bd8f7280be362294675da75f6dd39ebda7e3994a2c626d8435c839a947fc67374da7abf1c8a025991849ac73df21b31096db8368324a24dd3a958db21d668d136fc9480b85e8c7e9df708a10d6dcdd0573fffdc05f16330f54a29cf00f4d07a51b3a769381f40e43c6e9830b22deef192bb11500926a4236aa66b2cb1f57ef8394ce4552a3a9c1f27a13f317cc4a812709298ff3cef8f7a6f50a4e6c543a249d30508d3bb48ede17fe7a796d78fe8b7f4bcd5c004a6494c1104fd60bb81b4114b43bd3747e6e8e8d5bc93a914efa496507a6d36fc86c27e2302f41b5d5cb37c30017c11477456a7a620048d75bc14b98014560972abcf32468de16ecaab8cac4aaa9eb6d4784057acbab78215b9ea665a5470ab34039155fd6777d2b374bf3dee5fbee4aa1b6594549f1a94cb1ad1b7a34485373e5b8461fec8b85e6b1da30dd642d5ba31e337c955bf9f6bf27bc93552ed3145b447b8acd472d60b66f94d8667e5151d4bdb9913af2ae9fb9440eb7b3e4d119223b1356c15674a674bc044df668883d0cab68e296e178cceeac85a610d27a0a5e81df049d01", "525300655165515265", 0, 710671951, 1537743641, "91345f2c78089bd583a5e2e7321d6c23280eadf16115e43482b4a66839ab3443"], + ["030000807082c403025efde018dabfc5378dd71ce06395174e80ee038f3cf3fd7ced6d5cfb6cbebf890300000009526a5251ac6a5100acffffffffb4ff330143f1cf3284e2b4fa55a955522d254bc25cb66e30603373bfd65a9882010000000165ffffffff0379d931010000000004005351520465630500000000086351636553515152a399e905000000000152000000000000000001b6ba5200000000000000000000000000547957fbb816dcc3b367c78df5592ff317f714e2b00dcb1a81f187be5ec3287d6a110bdb273989d1e5252062f32e7c224db6d10f9838c278a27fad46180a94aa12aa965460172893197cf793f1377d494410d277a78e7233cb50c80cdad029dd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e924e443384abc5953ec381001dc8d32d74ea6db8b129747a050c1a1b1bc95a95606900a92dd9a6af31e6829afe60d19beaf8e96f6d3d8ff6b0dc3bfb47c4fd7a1b7c2503b4572b320c0c534bd802f7ee4c766e621a68e09a52c899754aa8e17a7f53d2f8e5bdf812c6596718a1389d5402f01b4d5acda53249210290411b6030229554de30fb5001829a66dd747825e9321f447cac2f16e396f0ea35ca9647d1f02106c5b87e1d8e193018123eefcf6e6c4b208c53a02666eff34a8b0693efe6f460b02a09cd6a3cc720aa92bba0148e9b591ca9c816eb38724821032998be57244946a5c8b4f0f1679d244f2896ae638de7b3bb635d5d2736c413c71344cbec8062f020186b0ff97dbd785eaa4573138920c8a7e1e4731db9f64120f0cc5849f28a47f031626950a018c114934fd7a997838b243b17520d997332e2efb4a31b127bbe0cf020f7aeabefe410562309182355d0bc50d0433d69c0c543c2df60e854f0c422a320304e1698bef6eeb1c4767951efec232f30f2de2ae92dc144b270bae99455024e7030be72cf7b54f3acbfdd477fb8463f69fc78598f4cbc5477b31e4120cecc16529c1ae0b585b7a3ff79362b5803794936338608b52908fa24389a8aa6dd7138cb49c6a7960dbeaf450b7264c505ae9ab57f6e98ffc1dbbc6e16d63c9ed7f2f1674867087efb50fa47c9b7e120f35c096578c73c5d4a72c4b8e8f71598f9f3810424ee3b70f626af91a2d492ceeb00b70e3608fa2bf71ca4bfdb79c074616e9251394850e856eb9263d6b9e8bbd43de320f550016ce65c9e0fe6b146c92e66b5cdfe562edf201be2ee872a94d6d2acda89ecce8a38005358d199ffb557fbd60d19c226a3ece92dbac6789213c9b0aecdc706fa66d2970a83e0da70b133881fc9afde07de26bdf022313ee882dde2bd6bdaeeafcea84de0d997a9dea85a2c7a50c53b0fdcb43ac73a1c85d9650c31f909573eb584a65a6e4ebe68c6756c780318fe4e1265b379b0af3891b83d05ea22d7f11e4db8d3c5aae1431daa628d6a8b69999ca27029b3e819a59dc43015225535defd56264dd5125538eeb7839a883b4c7a9f5e1116f5f4f879b936cd0c93974a4c5301ea392de132e482b282bd8e3008107093fa01f24343ce14f9428a0ed365999d8c824a2e79b109f461731fbaab28e42531f6973ba5c365d277c8200cf4ea0cd3816573d95ca6fadd48c56137ed27da9d4a2628e31f5b5e48450a1c1c5971c6ff8f2e7eb61c97b98aed48ed874c95dc841370ad73a230db570f83f214adbc3846e11c382ad86d28a07dee1154a0cf9294374d471f812486f5e6c0594852b50f1cc820a7c7276c4e3e70e778557fdbbb0eb50fcedaecb2347ea8dda338260d800c76692b1f77fea4f375635b1248015b012823f977b5ade3bae231665d9f04987fc87d4bd867a14ea2352845da8216ef19ef37b8bf8b7f0109c909cce2a58c8eeece7d16d1f7c59a9cf94c2214ef105069c84434cd3983acba66d28d2fc0f54682f129eb299afbc2b1eed5d1e87942cf48cf3110fdbfbbf80af4f3cc8291c7239edf2ecde826446ce1ef53bb0ae9d948be2671edcd6ae6f1b0413eacee5f5d899a8ec7eb140e46fc86b8b36ffcc2ec3db868ba97d0dfacc4c0b4fde4ecbf8cbe5bfc99f2974f876789fd27293ad82003bab8038ee46ff9614fd3fff75f62f7fc6446051833b283fde7eaa80d9cf9bda4da4ecec266471a74341ae9de4577b31dd1033646542d5825c74123f522870508abcc528755806ba785abf3f062f60138ae67e83416597e53f18e2f59ed1bfe8d95a6c21e740d22606aa93a734d17ce18840e052f37a05933c8adfb4d3f38ec6b7c2a479d7730dafbccff839fd9fb157be616ed064e23352767742b464eaac2d36868d76063170f34cb9fc0ba676ac1ba12aec461cf6647781f4c3fe0142b55dd433bf2e7955e5e8744b5d03d2a24af6527c06e5932dfc92b1afe239bcd02eb01b0bf1db2f98475b7428e943d6e9d79ee60c3c477b6bb67d72b382f2527c4da66d34398983e97f811e8f2ed47639ab50f6eb7c3bb1568eb4b1a130019960dbe818740cc968a95171d0d71a33c338ed68fc9d3fba17f23062b334c7066e8476ddfde80f95345370f26711becb1ef74efc49fec4944b5fbefa6d99cc0fb463c4790b67b85579bda62e0f47a6bf8ce3afcdb19403bee6340615fd8a103afc04d57a2079230898f71c915e7d9ddf643c9960a2c1bcc64a1a14bb65979bb610a116e507748b478daaf337eead754d18fff463cf3bcf94c2d6d7f4793002455cbdb227406ee1ab63020ea85f32dd7aa52e433a4316922fbe829d6b368f32cf89aa4f9853e6bd42b79b6b2a3271df567a923f43de33563a423e71b9f3100dae57029cb24b46ad824521116475a286d4ba9bca0927900a", "6a53ac63ac", 1, -1344800502, 1537743641, "64bb2aab93424009560cd18c43d23d31aa92f8b729c9d7b8af7e049ebdcec20a"], + ["030000807082c403011d06838e7cfb0cca448f533343b5bc1783b2a357af4188f0e71d1e72201db6120100000005536a530065a07f054502ace88a04000000000071bbcc0500000000000000000000000000014dab7d01000000000000000000000000d2df2df13825b36adf59033d0bca51b3d946a3b84260c57438a63ae3efdf7cdcd0c2eff1ae7a75ee5007c20a8a6e8bb2611554809a42a705cd394460f2ea3d2bb8c9e8947ccda59a1b6c682c96349253a30d42a286d86aaa17817f66ab3ef35300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000102cb763b8e41d99f82f95b5961509bd427318b2ef73076053002d7d8c35e9d25942e986689a4da3d0d88408cbe2569689c6615d1b65ad05f4dda5c9d4580b8ddccc6edc5e36335705a734f5017f944e79e2e5714f4cd39436f4d16b3e426b098e4e0fb50d11f9018172341b48d387940c0d784d3eb198423cc1d5cd9dad04410222e3f90dbc170da9744bed8e54088b8230ff95088bea4bd5208b688f362a3e300213c1f5c8225321a68d04534c553bbdd7dd1ab664eae8a3af856ea921896a88540a08c6fc1e2d7d252592e5ccbec4ce502a0b88291e3d807a0a9511fd2fd83092dbceca3b52d98d5ba1eef34b2f7c0a2119672565b9384a02dd24778d79be80463202261199989dddb0719e1759b4c88e2b961448da8fd1aa0a27b93d897d9c9a5354032eb777583da8b88d2c002274c0f0bdd15fbf30a0502c41bf8895cecfb4b942e7020e6653aebed92186a36a2603829eae433b0466e9435a2a34570424eb5c4742930310e911080f964b1c2fbfba534750e0e856b7525879af108763664d036a7ca0c20215cbdbd8e4b37f9aa9e8737d9f982861cf7d6abd38d6e4406a66648706f78ef48716819e0d09c24877196360a7d69c1994e4a6b213184d4ba9e388352c43dff9e39c77930854a26254ccefe4a125d69ecb22a19097e116a6b75fe35b701ddd01208ff9649b6861fe3e247b114f5d562c3dfc5684ac2d99c9b96c6485d454f13b1b428c0f53b957171605eddb66ea2a90087c00ab9b0492cfa29d00cd5834f4bee6389effdf61318f91053155f3de5e803ab5a86c67f138839c3407959e0f95097fbf987694983f5d36eec7ad5991c6b1514d518a196acb48a960b178770f392c39aedf24b43e1bc9dfa1b028e58124bd98e45fb930f43b7baef22f66f026e4303d9b691e48aeb42591007cfb08f2b8a2be72a4c00249a2a17447e920cfafa0049f4e9192cfd8461b61d6f347c2174209dfb1e1228b6a8a4943370b4cdc0166307108ec470eb778cd98dde36a9b9e9a2b2e1039527d000732b41bcf9729905b5d3639218529c4db904380954b1af6997d4a8bfeb6e369d81b8a323f69235f44c3d12d9dd88a6266c22f63d276da99b376faa03007f3ae682d5aa9995a59963499a2fe6fd76eb8901ea2d048ed901e0b45bcb69209892a447830201c5ca1e4fdfff575b23e34a780d56ddbb47dbad64c3c3992234b6fb5a8d516b2d9af43d69972a8437b7a6ffcfacf106d7bb18e12db1e500107321b1d42f804e272655c41535590344c769c7c1eee0c569643bd128362842c2bf92ffbc5f1c45b7ff108c879b5f8bc5e43150a53ff9b0ed23ce2e64c7ffd3c656082cb7745ba80f0acfa7ca36e583e2174d566f24fb867ed19d481d9da0b4937b0d74929a025b770fed50e21486bca3c483c6a45071307772a7dd491ac2059a77d22fe77a15f9ae9ee96aec567792bdf648b69708381ac5834f2aab880d41b9e184ecfb6662eab95fcb5a11da16b149d25ea85a183aa1764d1357ce9c08714b1f9782d8615375f1ab0a7bcd31d9f81e840a197db94bf897451b653ae5c68bdc0ec6177affdc4e844a7740539332432d027eb4352783653b3f994d6fc7becab0dd130b02a2f1aa97288bc4d6fc08d6d3c92f3d405e47c40f2dd932538a66d1373ecd059a81466df645ea8ee55eb2cf6e7fd2230fd93292e194a7f4e8f75cef57a2876b80652bfecaf0bae508926ce03072864134a5f4398f9dee75dbb0b9fba7658e244b67452067d195708012ec86f8197193f62722ae66c34468360e72ab27cfbaa056298fe6ee02542026ebd9508506be31999ed5cc0984dac965610f27eea02f95943ef2c9c188fb63de93a246c64541f2cdfb9cccc94eae375896e16e24e7b06f79fd8a0846461c64cd7d6d05e8ee26f8adfb4faa4a77b9d2c831eb2add18a7047a42fa24513b4ded0ebf00c8eed8f01be7c06975f401fc34538e3b80317f868329f66774123e1362a64e5e1c5bc3cd139d0509c3dfb3d7b3b9ee68579fe2b8756dfcdc6c7703a72a76fb7971cf9554b77039da93b56cef040b39e866a60ed343beaa10885cf357cf82a34839e9a9b8545d91f1b4715e7ef03c2e8d04e0c77f9ff6d151c0381849bdf9b8828ea5139414175c72e5a046bbab824c0f00c9b2d95f9f1e90872d66a526147ce95faa64639c239398112eff03c1a633f4c587c31c5b0a9ce80ffb5314e2dcf47f723e54703dc77ca137e1da2fe2d2a8cdf4c811ee561b747763ec04045ad4f734fc5f36eb54a276335899da92cbfdfb8025016c76723f7c21fb4c4871fb311ae09224f1aa4dc282f6e9cbc92a85d69ed030648d92ca15625a2fe111f7854c16f8139f9112d78ea7de9aebb6db3eac3cae1bc02827db3c5591216b52c66545aa2c82ed43edb76d7a3b3ea8fe93c54dcc6380a", "5365", 0, 515227481, 1537743641, "9d8598a891da9899e508725c2025d1240717851caaed1b6e67fd2963e5f60306"], + ["0000e33e015bde0d887c452cb548ab933f3aa026e36298f043a4a15412d2d7d6895112160d00000000056552535200c540122204ece4b202000000000953655153ac636353630638cd0400000000026500bd6ea70300000000076351656500526a9503480300000000086500635263525251df2c271c0100000000000000002ffca40500000000d31ae2f9bad16fa666b3216486d96321c956cf46d8a7c4c34294a2e6de622ae7439712a0534e1f1c96aef304ecd6abe7840a04bfe17532080d409150526f9379347a16f05411437a91a154d952853442221727f382cd65670e402f93918ca50d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000242b8b81c219d7e235c767f9b555a6f0f5140bacc42f3645fea785726e4e34a957ac80674cda594132b64f91e086d106c525382f861c39c402e54e39d441d67e1a5a3734f5a2bce76c1a19c93bf1bb0240abaf5521df11f73bef5cc5b90efee1d1a3d0d9c0714a16f1cc5b77dcb822556485eed39fc08bae024511e3a8510b71031b5e1d72522258db55b06f3002f6307d99b67acdf6d1c9a7884927e52f0cfd51021b80d9ce658616566bff4d629e94b6ba303fabbc544e8d8e7d488d41dfcbe9d90a041b301d20c1d30cb0064a39c311d6e50a0bfd30838c427f513826f3631576c3b149bd288078acf5d99be73ddf4f5da4bd42b70759907dd9d2d3f1b08f1ff71c03120f362ea10ce147301cfe070d646d7352fc18411769bedb369ee083c96749cb021669338c04ad02d7b850dbf0baafef066797295e6920ae3847f6e4c7c6a5d4de0303b67b82c1601c85027eda2a1e02d60f7482a9d164784bf1a65a07fc83245b74032e6ab600ede624bbfa4c08b33f2f8f0930bf04bec3e1b356199deb1ef0f92b3d022452a2ecf93f6a0c6f996560ff4ee9c6f74859a1613314dc2039c265c26a3e4152803c5cf94b67f34598e55da629113b41bff5d9ea63bc6b3b3531c497bee5a85292f72b3c3b4f347866f05703daeadc3c066d084b3e4a6f93c7f9808aacd27f6ee08d75afe53994ef911fcd66ec064208cdf17eb82d2bc1250b206949faceea372f8896399f4319f48a8ed9f5f9890e644b516bb3c45d42dc29fde8ae4af89f4d4150e5e3c36bb9bdbc88456c219336ba498410f1c97b07d8934d90a108ded11c0a13885331e846b1a5ffb32e97367e0e4290c5c55ee88f05fa76eeb6dde41ed692f57b13f057ea593f48a93dd4b272243f0fb91cc65967f0fc014a83a503f0f5b99adf741a48e4bd33c79ca697983ab20c5b6420f1f60625465665e1588271422d3a01d5bc5c8221940f5b121979fd76f28637df0e2feab426a9fe9ca19f6cb37b9a5e26614528b864afcdab643e6df47ad96b1ae2803b8efd8720d0db6429b00c1e46c5c6f93849fd59b5bb5e1753a42fa74c806b910d8b1d5f92d1d63420403bdfbaf8ae18245e683f5ce5c40e8bbec4c4bc25061dfbefa2856ddf7541bdd1053355d2782a7752c4611de3841dc4b31b654156bf785eeb2ebcd88663d8ac99cfdd4ddbaf16ba0900f1a63384e15ab2bc664b6c3ca8cc8d34eb23b562d8f50ae87df7b637cefe5724bc7a070b7a6211f0f3b6ac5772ca315ff45c51b6376b7b8be334bab1c8c1c23c31e5bc25d66ff52b7c5c8be584591e23659feb6afa6b06002261c9f8019d75174ffcc46f8d1f0d99636c24cb4df826198e400840c98d11010595ddb5d64d44425ffe82e0cba72ab7e6fa5b5cb85f44d4397d1ebb08c8ce715daa23b2306b7ab05bddaa0bd54de6d1b6d73f90a333c284e8b21d53b7766387baf073ffca25c847bf6cb13ee8726648a8905d31eb91b3038610424a73d6aa96d9dcfe90f146b055d297b68008824e6640649aebde5d32f115e9698b2c7030a669bb535078b54cb92dcc12a0b8066e93fb3d14dc02772c713356bbb59cbbd2ae9ceba497879aa5b469599c440b70b572b762406d8efeb34cfca7619185132f50347b0e7de9cd348bf6e53d07e9f249beba7f3bab7ddb5cf5fbbf212702d79ac33a58755d5945631587e415dcb4d7a8fd6235b69b2a3c1037855a68cf088ace87d9ae532cbddcd0024f3454e0fbce262334d3498c175b68f27cc34c7d28e93dae59720e0d11c93c7a1be0c3cda86c604dbde6f3c24c30bc6110b4e86594f050ecd31a3dd06015749c92b99d4268846319e1956d1cdba42bf588ca522badd260fe00ac2cbaf275b237bc849bca3c3ec8e7cb287963a1bb9c851713bdf51e4abf613eacfccb906c7d9d1f71f5a3b24ac4940ec067a111b0b916bf21ebd43a412c9e219a189f892973b895a150dd39f36538f88c737e0ca9b0595c8275d50fca6dd639edf23c02e8ab369d5a6e94aa621532f8234321997bcbd45da73fca3fe0ee3c29a0888e4f5ab991cb8c6fe2a0e445f2f555569000599efb1a92820c9e7140d5c12e30d38bcd035953e24ca180ac5b9fc3c5caddade7742f50ff78a524cd3a44970318a62d98e197302a5b5fddd34527939fd641e8506dba5a0abf789dc6f4a89e8b06726bb30ae82761fec111830e471ac38eb41ee267bbeabe31e24f79481472ebad6e8c35e1a8fc593fec924ab1a494062349955598406ee56e5c57c739493cb8e96b74e2f41664d1ab64af57c598fab67e1285c3bfc22ddce2f99c8e280b4096fa6e76454ba3ac7d68c3ebda7ac66cb23d115440edfda36f0e2fb7244e33f1eef589def5465e4b2d4dd7ca934169b90107d74b2055482e0f93135b3e93af556dbc91c0893e680ebfd5035afa3009", "", 0, -1239815601, 1537743641, "1d074401d8ec7a11989714791c3ba45e14db898e47269bd4c9460ae9caf1027b"], + ["862f207b0477c51c424565738c750130567d806d51c3445e3ea02ddef9ad167d1c9bcb8308030000000400ac5151ffffffff6e21038948a2c9bd2909d400f789cca09c375b96305f873ecbecd897ac17ba7901000000055163515165308c2d34164bef31a4b5e0b09dc764e794bb5e8d16c2d8d22695aef2106e281400311ab80200000002ac5306bbd9b832e5e941daf11c296029fcff22de1d91e7c31c784c124c950f96b0159a89786e010000000163ffffffff041103e402000000000353515124ef31040000000002006abe98e802000000000763ac65006553630c0dc4030000000006526a0052655300000000030000000000000000c95dd10100000000f99b6f9df3b92f18eb87bfef84b2702ebe8e1af44ccaeec9ee4e6bb9d96a3294adc864fb98d22ecebc063636268cc297d1041283c6c2e4ffa303b0f7cbdaaf41849e2be5522420b981b22c4280e6ab86ecbc0bbfb0a3d07803b3efb1f95672b4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003b11cb5c0bc56647ccf5145dd6a7678de0275e252261c44d9e8fc7cd124a633675827b382c175be08f73c391325971bbdf4eef7d322caf8c0accd25db04f1eae226ceb7f5ec3aa5c828b3818aeaf1b6a48baf0f8fbb14d43d13ab5e159f93722d88c9b8d9c49ff5187a525ac994aa9eca71f581908b218e3454f9dd38a8ce36403240bedbeb68748e62c5ed32c72c6573e4836dc05aabc7572984d08bb89e2d551020e816fc3258a29c62ce6e037e8233e62987a2f8b88259fc35da5a45b48fc5ef70b00c2c1b86b674e46fdaa40ec9b55ec25a782a7ff14581eeb40ffbb297604ec011903b7685b417da2dbc72def72c129921ec74f3a5b06ce5e53ec5de3b9fd91ef0300e969c5367a3852ec4a5a30f78a796aec102c2d815243b4067739ce7c3404ea02120cbe5d467f7b59303b5e5a6bc2c4eba6e07897bcbe982ff15d035036e4d6ad032d482a551cfb772508f658c08b1006bb2ac651c632cd98872570647c25871a0302218547f6714a29654643332e0528b60f7dc8d568d2637648fa2fdcecb4cb37d703145389c98e7c4560ebb84101973aab1cde01aa938dcbfec681151da205d8b21d2ab226ad5ff20d17df729b5256cc8c425f77863c53a0c9ad7c46f73997806dc47bede7c65bdf859ca2a30d92abb1e958876f33045d6a0180e18c32f0f21d0cd50d63ecf2b3a97543ce2c84de868eab750211a878a834f6d5259a3abdd0ef4b11deca7366676c1e6355a3440f12a49c62593f6b6717ac627e35362877396e49a0af8fedb80e4c4b17e743d5650c3591ad0bb5378e210cc8a1a01a48077dd7783762a2d72af1b98b3c33de7b1c27dc5fbe473625c3a5eed7cdc34cd7cfdaf960db121194562e068cb0c2d28e7b34ede58a7fdd086878c3d9d6600201b46ec3fa5057bf5f07e64950b5bc1c6b749da8862c76269218b5b5e5694adc0076815b8f4516f5ad30e485cb9127ae55c2144ab19f0197d0db4f66d84c8632b582ecb44512f3a60e97ea85c1bfed0a9ea9d8b91044beb98383c047b418d4a9f3510b1dd88eb17d69f434ca3fae7e7823332f9c89a4af138e9692dc540fba0bf0e7fd6d1fc61e86ff8bbe7e321f80379584e3bc996340146ca652c981bb2056abb2e7eaf2acea342e9abe8f63f912829691639ff9495762c05b0f0ce51e67060a5ace195fdc42e1f44ed2fccb45ea4ad28bccb18af97c52e31aaa55511716632f3c3e84a3b13f0f4387b2ec421628f2098375af81129331f4f2d6092833ea454151e09c8fc7f3b6a52daa5980f7f0121da00a5d941367038a59ef86f2cc656fc50cd185323152f317a4bacb9b029711652bc199c6bde122d184ffcebc4d9aa3b477c930cbd683042675b173a77df60b0fc09596f70ba327757464b3c6f5d4a2d01fa295001deef6460f5ee04499c349a9a72002e3d827ee193c807ad3813e8b58eec1393f622f0b66cea39aef9816c544bdb47cda50af1089177d664913193a523d6e6d3cf698a161e53b92ddba1cd8b1549cef798ee69dec8f919330c5cc09339d8ecc2d6a273cd27d457e1e86bce8c56ebd9eece3da5e9d0452527668b1d2b9f05e3086de106916bd4ffd270a102689bfd5bde825ff5d05784f5f3a042e93e6fcc254b164d9c0509b96de080166e8898a0b6210029b41ae3d38871edcf6c0ec16d7649b98aac216c31493fde7796ec4e0da281f497c08998a881519efca12fe685b5c2bc1ff889f18b573192468e9cc5a4a548d07bd5da82c1843d43922f0b449583f1980335d44071826aa8d870430a2a48ff931fc116752086a17356c44694f8415799fcdede8fdbced5499afe7a4b0a215b54c2426a395c633f5105b1bd35bad68744ffe5794d8945743947175827c60722e7dc42849f8dc1ff8a05b850e98e44757b8a32fbf9baa640a3e7a9ac0c5e18563e588a2484876079384f0b9caf9ca33c9ed931a3b095caaa0b6065c724f7ddb52a06800031a5f7c34148af97090b8750353b767ec8680c8346ae7f03cabeb745604e411a32d7a5c5ba955eb29c0f6876c1e5f3f011ec8d5c09758c6f5ea9a31516a91b6e95c5f74c9d5efdf4d4a5c6a22f321dddad7428f2897a2a0baef0550ca9573e12c4d363aae0d1e2b51b255a295f8e222b8cc5a1b2f36ceabbad387540ff95367fe049afaff4af28998c403eeca0a2b0174c1f9f29420cd073e959c35bd51c4ef7e2f2f68bbac2a3860f605ee35f2096fe54a0ce5c484e0e5d1f2e68bc1502ae5aae63528bbecec38f0faa5edff9700be8ff0e1d4dcf56d2d656c623782b9c0a2308aeb0100000000000000000000000075e63076b308882c906db98069f2bf032762b0c5bcd6515ece50c504509ad0687a5ccda6e7a6efbfb894d9905bd7ea45220d4784f7c8c1f06ae68b99f8c3217b0850d89a2834d0a35387de63b80e2a7d1d9c33e30c1cbed9682b355c5fef608d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab06e27a9a81629c70e60910823c6c57af219a2a159a288490efe9f3a324db4ddee762f909fb1e9f38dfb4f00a870e5405f81ce32d5f3a70973df651dce6acfd305eb635fdc02fca802f64c0ff3edbe60aa30399f470f602af924295c6c15932fce25974f561ddd85ca3ce16fc604f676e56226ff4f9c48f4d0165e075fda2920221d7a9ff3e4e16301d8098978bb58b2cc0adede75ad54e1fa027fcede2e1f2f30211e1a5343bfbd819d92dfa6a032e80de1b416e89066383977d1faeb3e50e84300b01b1d47d872f7267a47539bfbb10b19b237878c617c0dc56ae56884f24f6449d428af9b4e1e5d45997378a1a7396d8036bae71053ddf90a2d3b1d3bdc52b1fad0229e42cd4d5c1419346db60bce2acffaae749991d54a8722951f9161d2de91452021a74c8c135b8b5fb6eeb9e5c907424b0adb11a7dd751797790bc5a85b51a16da0223f312f7a909264f21f873bb9305c571f87b528978a23d2ee8d47614a02651310228e6a1604cb9177996633bd320a1553194b90727213c5db7a3dc4051cc10c1e5032ac15bbbac005eb76df37c5ce9548cd6ce2c4b69263ae26f6b9971dc0dd0c7e33330e341dc341cdcb2e3e84d13e1214046800bd1855d71d2d393676fe2e070021b2961677b098c458f3c2b5e572aa2033572c8ba4d7319542d2f6d104bcf0a76d941f23fc20c7499162c4f3ee7ac083750d19dc50e89ca74c95c8a82f59bd8977f3cd43caf9b2c552a8645ccd26f74b2bb1c4dbbb95125edaf705e59213e21d28aba5d88b5cfffa9bebaf18998f96089a86d1214eebf4ca6a437957596d1ce984d7de1f6c85be5321965317c4204458ece9fae887f137b968593b873475554213751016f7a8bde3f007f411ba8d811fb7b3b44d4bc2520de87e3773cc219b49cbd3328064fe735085dc4d4608d11c9c34fc95214ec8cbfaa114fd723b6cf64cf22864780e6a39a5bbdc0191d1329b9de50544c5b1c526043f1c39abb22996881948514f6b0a3c70bd13104216c15fe64a0c2fbdaf032611a9b63fc2343bef0b115dcb439222094a4d096c7e6937351099bdaec4fa7831ce1bfdb1a189ee0e160b58cf2dde510043a5fb87781c54567279e146ccfe28a2f9f0238cbd93db9b033afafc7ab9b4e4c770634c289763dcdbdbef976b5cc40c25940214829ef45a574347d3c7d3c1f6b53a803239fb3832070015b2833d60598b7f23870a1045303228f38d1a9a3edc39e5d9e844d8da0ea21cda70465ad751e6ef142ad7b84553565bf0c4b51af45db2f82ddecbc71ea5be2d9bb395b4ee7c32c4155780093fdd722053b45f131aa154eac3cc330ebb12dabc6e2b8883f842078f6ded8f8851548aba2375a86d24e72db7311372093fb960bf22c4d12b2dc7b3365e5e63137c7c12d18dd239a698e1006c55efb36a2a3cb87ef5d63343be97bf015cc896478d6dfe5106791b4fa9e104e6571b49cc0069264199e13a2ca72e815a953a380c1e6f97310e1fe7e02b6e01e7842340628e2780c9f72e09ebf2d1dfe87844d475965bf07a69889523b97cc7faa27163c2225ebb56822fc0f791cf012f79bba52924249e338155316027b875de3bbf8de605dd3fc4812fe6ea97c24f824c215c8bf51b4312480344535429f591e5645b5d25f0f4e0d6fe428ac394336396b4e7e2ae890f41c12bae1d08c06e6c0a9510cb04689d2b8137f572cf46cb34127dd7681fb58b0a49761815a4e8962ae8d4314f1956d2270abd24d7b318cb9a98ae67c12c37f470cafefcee4692434c8b38ab1d89f6511a8cdcc76f6dbdf62a737a3fe5f0665a4b78845f695d5257bc95bd7b0658abb85ea0b30da12c7b9853b988b351dcc30994f67342d17c8f1f1be193e2b97539f94297aacdc16afa0a0ce469de9bbbe246ca15ea858ac9996f7bcee35527dca655f0112b8ab7714b722969f28d99169f55a199291c1f517d771c92b7dab2680e731ed4683c3e138869f3b513531aec91b3f2ca1033dfe3e15a3fea45f9684f4a6248d2c2b4a160e899c80e1b58a26db6c81b7b2eb597a78616742577f53e1547bb629ecf166b98b1be95e0337286d94aca3f7df1588deb1ac3f381d367bef60baeb31a0f8fde0d2feccad9a88bf69ef1d37ddd965e97a50e6ef6740cea856f5bf15dcec0eb6a2f0b16424abd23ff72f3d5916bdc7b785c952588a30954d30ac86d3f993b87ec42e123b07e52bea5e8d56c34136e811664cd4fe1e35a65b1927c0f9c6542f93d373974fb6f5cf1714a1f3b4190665e036c1abce2921a7676140675df1e6ff31a40200000000000000000000000029897be972177a9e6801c53f91c4f1ab618302a063e287a35d29a8bd294e6ed05a7e61a3513b5e170d495d155593f5a37d2e8d22ca52ce09d047e48d68334b30a31e475dd97aca5be4a724e09729a61bec7714cc0ba09e5b7d6129ad11b81c45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ee7ae9f4f29d3a6b510f98dd320a3e4bcd039730d0ba30b157b26b71df0ecb57cb2df052c61d7a4d445ac53af00b671c071c0afa63e69f183a6251763a58c85ed6ab988450310d4644cb9961d5dd373963d4406a75178d2d5df5672e92456a00ba1fd3b6be5bdf18b70672052b44167c0b94c06f866446aa077d9a624382fd703188c433a8f3b836d62fe3d2027038cf6262d6537c37625efcf91aa52f637c8bd032c356c5d61c13a3f33a63e3d32d112f2c1fc325f3089ccfec96b2bc926a3d7b10b02a37bf47dc65f6bff7769f84678c04c5299be67e62dc8e0901aa46d574dd477ce1111a570a4bf0a8b7b73b40331c54fe9161acbe67b7eea9f211a8ed9cf5ddc030fa6e6c0889116508af416c1de133d8e5e2e691464eedbf601c3aaef27bd8ba3021d9845e37d82fa63056dc47a2593eba2e9300a421e450bd5f3818ec54e22c0c20217ba069d7983eebbacc32b61202060635d0d9399f001b8338b9813ed4d6e7c72022d308fd5b0ef09244164ea50df80b460f9816b8fcd70488d22192f5d4905be3503089b6f01c1988640f1f8770c5c5ddc53d328815852e9d3d6c54ce6a426f48a4b3e811c169907e8559ddcb5c1e76be04d3b6a7702a280d5848dc77e3baccdd60b587fa180077d840007ab4d9c104d74f26ca4417daa6289140e4e78ddfd15a043a469234d119482e6d54bdedb8552dda82bebac76246c887339651c1f669ab817ad23363940fad32f0f6f5dceb5f8bb4798a0527b8cf51f9edfa694012d06855f57af8ba8ea7e6798513e76481f749f4e2ea2f57c242f11b2796bcd41c0168f1da6ff7ebc62a8a59a6b1d2e73152e488237ce9ceb21b37d976364634b5d27a02c161bc424b9d5dd0df6274fe391e2ee8e8dc3d6561f9a19ed54011b395529c2aedfb64e3ca04ed31a06a5bdb882cdacb10a7c7146adcbcf7b1b325b89c5fa013d423af0927e48f3b9c697e54b9e44546bc853ce6932df647c0442fd7d193967589a0a46a6c52a3e0357681f27669a4ca77276c3f524e3309de4ccd8f3405b4ebb366b77d3eedb451d255f5e0fcf857c8af969f9ab566bea735fe29a34ae77e2d802e97aae33ccbd6ea9ece7ff2f889c70da8d06973c5dc1a42bc4b34d978b0537b5567979c94aec5f52f1cfbcf4f3af91f32067a943e986d63e727c96f3cda861dd3a4b7d5925b5cd0808dd7479bbcbd04d296258ad9a981f3c1930a4fc0aec99dcab4d66fb46c13272303eea5c8755859e2ef0a862be8b6a8140a791563e8d283d28764ae6a6e6c02b2bec7b7c9e57015d4202d7f3ab71cf5ccaa531fb51e3a9ef18f0f0d5d0b9019438a5a903bf777398c714dac163d261fb80c41710429d26303f5f2560234d0b91b01a9a37f18ec103ee8dafb2c7c4692b2c8649415cbd55f56f1bb5ed492b9de762706a0bd11193b29285ee9c6575b835ceb4b5dc780c8b21d5f173780be68ea9f5554a137ad784105038bf2571a041e8e4a629ec8814d2591dfdd8e4f356ba90d182be595bc4108aee81a9efb68c86130a04f2f90433bc42dc8107e61eafd6a61f30ef103e7c9042747477a5ec88642f87c2521c5457ef954c58e968ec9d8b0e53a424398f7bd26a43922df73565786106495d2542c1f2a4577d4f92efbfd177b67fd8fb447790662a1548d0abdae89ed56ca8b258e85b6dba25aa912c082fc71824bf50213492cf8d2a39e28c53af7a4c262f207082fe3012c5b1e33528cbf21f28962cfc267fc2a37f84ee22b8beeca84febc7400f9cf4c23be49a92296fc95f5be5b2e8b25f405157cfba0e20ae8ef42075d46c84a09d725b1c2082927ab82c917692f2e3baca3d345a61c4107a3e10d80319fb834ecb1d832369837b27c7c116cc02c39b05cc3d65feda530a3aed06e8ab47b06d21150efa14a47b5c9ad7662a41f840b9918423586c1a1bea5eaf894920df32464f91a1367b4ef9269c9d35d118f527c3e6fb2e6e197fbea87c66559653ae54885291e600fd7f6bddebaafe61f5f557a883b89933edd55ca2c16cd7efd2376afd0d60f1ce69c43d3fdd50229e00cf37638e90ac7712e1fbbbb5853ca56de8230b34250a46ad70813d1a075380a556c9f42c54328123a747c5d648c1be654f1e9b2280f1b4525c56a94d361f7eafbd8089e352b96e2afe68cf6ea0fc4c6e93200dbb4435fe2a54c158009ed489f5468a61ec015d2a4f417eed070a8b2abd7123988e0583d417d489b653d55f54a7c2839ea48f9a31b6ff588e7001c2bcaaf2524d8adbe0f8f44408ec59c4ff63cedae31010e295a1aacdb34a9e98c617399519d168d330f9282de937fc9857d1abb5f201fe3aa3fc406e140a0cc23ad5d45557bd18bd3aeb4f49ec46533991874f2b75de6d0c968217a1921e0d8b65be56e21ccdb3752cb9dea836307f003ad201ac40dd100201", "6aac63ac5265ac6552", 2, 1901831755, 1537743641, "43c08c87e6992364753b4b53afab8168868aad550b94287f5c31821d486b2c73"], + ["4e672a7104fb706b40c47d3e5c1032ac9a16dde76a16e6ec258be88e975489873cbd0f5963010000000965515263acac515253ffffffffcd182594eac286c0eba489f29a6059bce3daaa784ccd21b396ee7d0f6de5f70a020000000651536a516300d351681feea24602b53a63b141ed816f79ca6d781abf0b884903a32c3fe1299c165718420200000004526363acffffffffd36e9edeb216979d343e709e4aa777d43c22f631e39825cf79e30bc72b4d84c102000000086a6a6353ac65516a54dd3ca4040c4beb010000000006516a63acac5125c57800000000000653656a5353000db5380400000000026a517736ea0400000000060053ac636a008d502c8c01f4291f0400000000000000000000000069024f706b6de05f7a4a86060e84b38d1e22aaff1646bfce9f2ef33a13fe25934d142c780f5762cc2d2176716c7a77285bb009b877cdbb29ee51e14ea35440faf2aa55f296f218a9ef05aeaf946d67beb5b943b2a867bc5a0ac58f5bae7ac96f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a78a8eb7c94db00d4e06f29ce8ad5419bc732904917db0d572e171d56c0da859dc6ab2299ccac81cb2c0a0697df377eeb37211ecf521d4d4a984687371da7683e2f2d5aa05a8ba919fa0a36957629a24b6beb79531becfcd635950726691b2a8b58eaa0583c7a3b509c18a95d1554a1c9418dff02bae1f7ec2661c98bea025c503173090ae44aabbe61eba0cc2422bd719f5ef5b10d8fac122b868a1d72bd25dd40208052643685b62e7b11a1282a591b375a50134abb809084d3eeac36ebb5cf8480b00e344b1529c54f26878d94de3744fff8afb093e64c2bddd067abaf544bb67c5ced98ee7f17ddd79651d46f173c904aabe20040324e45a184f867ad91782b370021aae754a64214dd8899017bba62a82dafa4805f49fc04202968fce5d803fec350210b56b7c6c36f36ba8c28a2fa39a05302e8520b97f5cefbd5fe366df5cece70f02128611ed965fab821d0d65c2169daa5b8fdbf98b1f7be70df2187ff89b721534020d42876eb7a1e33d1cd4df924701f0a82f40f3fab48017781544e6cae31b10d5020b71370e9c7971c8762b7040d1773a982db22b6c0247f4a41d43cfc77325302298ef7451169cb922b2119255851410e173289e538c92082eb0c0e449133d570654f3576289a5ead1b67c97d7a929cedacc1e6299a36712da738f061e8938d04acc0c24e17d7451b63b8f95c391d7e28b05559d8449ed3f33ca4f7c82081eaabca746d006083dd9b084fff121167abb89c2749fa4be2a2a4b96dbf79b7bd7bfc5bf1ce50a6b2d79089d7538aa810090d66c78f4b3c53e4aed6d9d02d4491aa0c4f8b5efd4045817339a71490231e349a903c1a5e7af95d3200ae4e277af5f6f28e7a084df198ca84f89a4fca5ae31882e01f2dd6f64e8aa0951d20969fd921ea4acb3219010e99e1d6a5bd53abf00e4e0a416c8ba312f8fdb6efc55d5efc3fac830a60749044b8d100f22753bb4ad8c6dc778e488b93a735c129879aef9357f129cffc509eb39585b70301a072e3b8e476bf56cde37ea9fad742141ce8092ae3e807e08ff0f328e57ad5703faa65887095f51073ad3d4176753cc72f7c3c7c3ddaa6c03c1d74b8fef0b92992198893c6f3e28476a7e699109a3e20ae1a81e77b97bc65e0f41a3a7c1e4db1349af1778e14dc2ea1cdeabeee793d347ab60b0e88976ff446695b35074cbd396f73dd0048f9bebb20995dcde2a7b32c0c8ee7bf9df717f4b666f361d52fffa48eab59b1d487bcb7bce5ed6c2d707cb2dd08c04d3e7490636413a320994a46b8223ace1eb0c58490c6f2af74d98cac78904b4a68eb1f0eef85d7439efe4acc7447195160af3942d903251d9a615ad800f9600a4e2dd444b834dab66cc34e406b15918cad588a7e2c23a7073dd45666b5c21489c038dc3ffeb7fd9e4e630799764213c289cb6c1a5fc858de52bafaebf0ec4dae7930ec8150a1acae991feac6c359b5bbc82399dc84302b469a076b6a61ac27ab9ec9dc1ab7d5877654bff0fa6b12b0752fa264c051c6dd75e8e44bc740bfc8cd7dced4d5ecd13df6770fea627be8a8e4f59aeb61d7d5490a9905b06ee12d7e2a0e4c636f2eefb5e6438f1ad7eb03f4fc8f4e3c35e2db5ea33d0676096e560dc63eeac429e5bfb70dc3d6464656add853f8fef3cb16f8070a69bc29fc3469c25b74931f76841587e087408857aab1c6281bae9e2dd1d437e093a7c61747656e591947bb2aeb77f10c89b1b55e5b27d9c96e630e05670e336c57a80ff0ddfc298d7ae9daf650f3ab186960b8ad2524a95b07626a1451065d72f22738e13442ba9ebcd5895f1223c45725910b94cc60319fba31c151f08be43e5118188d312bceded3eac8142858e2b161b86f2203aa6aee713c2f2d513cc541c075162dd6a9e4bdfe7f983101120756355d2f8f05c11c6f2ed2d098f276919f101a8fbbc367aa02b9a0a1c9c9c2603b05f929ed035dfd7587d4dda905f7e9800be2d865b82621d8e7a384302fe32a4c2482bba0f6b9040168b9ec0e2e8642010b5d6a2e93ab78151091320b5d62b72acc151ed885f6c9a3b9bc266791b7c55ae3b4a3c073125872c625a283b6a9a52f134e73744ca347e1d777a64dc80e0986b995d5ce9e8b1e9ad64f5c05a8d0f77adc343529c3ff8470ff20961ac7ca57a071b4b48e736ef670ab6f9664b1fa3473cc25d414532b7d4b5a81cd0a53b8cf11593bf4f9df41ff6c2a865a57f850319469dadd18494e3bc66c3abdc552a0bad31f98745b0b500210585126dec9e7608df5852fc252ffa6efd90d90750c823247ec866052a7f839c15f8fe5fea9409b98f55ce0786af3c99e0e4e3975171d90ea03f35372307745fc62833cdcdc1c16a75d9b1696ccf692c721cd5adb1ae7b795050e04307cc077395953e23eace8a760c856183bddd19bfbb17139c02", "", 0, 289614840, 0, "07bd8b6d9db4922f89daac9f6d8dbe07a174fb851548c14c62945852806d1f0a"], + ["030000807082c403045e2fcf5c24e501d5004ab8b41c2e78c1ae87ac9b2fab729d1e8a83be9bdb1b650000000007536a6a6365006a381b9132ff208f1a3b0f8d22ad3940c2f3681eea9a74a21219660cc0e6f1f2d4de99648e02000000090051ac53635100000019e8cf2741a36ae819cfa427b90cf44a8b76bbb3e7cdd94acd4b443c280f4ae0fe2e3f18000000000152ffffffff8c5a0fbc9b676d70f7cd47010cd1ad11e3b043b3adf2145b51189e5f1c98c5090000000009510065ac005163ac51358e9be0013356de0200000000095200006a655353526362e4083770306f370200000000000000009349bf0500000000155f611bb4bba5c7a606f8d1a3f7de83509db579151be9083b5655934163669d6d1f758bd656fb604a218aec8bf5353e5b6cf5bde3ed9f522bcbd1d3a7a95d20f3c35dea09b0d6ffb6bab489264d677960fdb3810a11e255d041ae7cd7007eea00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e7a6917d594b31e078943914afb8ea5351f646be9282e342fb50394cd709800402ba557171565c5ccef148096901f07eaa5656f412e3ab5db8a5811ec29cb158b9d9319a086dee44fc3862489080941f890f5368a6b90af3fba15dbb8dd695bcdc0890967ff9e2c3b1f2a200ffd858243f0f318ebfdd1f182c05066ec25b2135021224c841ab4c0409121606ccb168ea3e0af4592f6f321b92467565e15fb22121021c988f700619cb729005d88827c9da28dfda2ff2e1ed7e61592bde4a819619e10b048ab61b1f87d5931410798c3f7c5c7f6b8824982632964b54a35e03d3baec093d7f4e37b4c4044396746d952546f95a0f8008deb4e463466da28ccca5cc3c6a031e80598cb716c8ffdeb6c6c2fddd38d5f8ec3c2854464495c02c04773ea5d43d03230b2070691b0e76058eb3f0cd899f2c2c5669fa8cb413ac67533361dd26f5ae032e86ff159d188e880a468896b3f1370d9ba340818736c4d47e33fd776a88fb86020a8aa5941246eea27248e9f32804b5677aff1b16356fe5dad047ba2d4b5dfdcb03163b1cb60878cdf4222b74d5ded2f84d06f70f46b0836e458059fb3414b5799e5d264bd100b3c04d470cd9de55024e90cfc257ab726a99324cc6765f22eed2d69b466782758c12399c74d0894c30a4dd67dd38a2137814f9b60d0620056fc2f0c2d626ecef5fdaf417ea426bc0526d4011728f580a1349e7ab38975f080ff09a7de501eb3a929d23dd6863c3910ff7af95787c45c73ca46a159e0a972807b24e92b44986d937d62da8e4726375e765bb268dc031ef2dad5164ba9c1d7becc0c480f4054087c67a0f9493085b7eb5fed70f5b2946959b38f55f8a3722821fc23f11ea10716f1fd1c1ba83db2ee3cea3eb3ace5b6be6110b4fe8f5a4da47683f6e98182415e6a818066906f7b981dce88299c7d80e57f40106f7d7ab234463850e529279e13d739f20b836094a1f7004978df341981525b5225b851c056c387c1c7af67936821da32083575505cc21abac179ebaedeb78f8835faffabc0115b78853e4d680dc21ab4ae2284c8dc645c4312983598afd07ef58f2507d1b90618e05007e300bf04208b76ec8c26267e72b38234a9c17f3de8714018aecede54b784f96e75515939b92036bbc76a58e544fdb3181b23dd0baab17b3a94ca97b7c8a1a1bdddd45d5d84026340a8a8ba25a4d594a9d5f5fd450d352ca1d40608f60ced509d213e9f1c38e78a8468290e7d1ce6c5097ef695c63d1da2e2b371e4c3c7d81f9080badd4875fbacb7a9b554ff9b6f881b49ac4dd65d2649de8a7bd3a73f563d9e990f3d772348eddeebff660f7e8ae59f7efab008a269e88043b73f15178a247996194a5cf25c418066b636819f7c162dc1e7c3194518a38dd9e4be25ba803789a7b84d877000bceb192f4319a57ccd20932d8dbd5edbae2260d291d73102eb04a38e1af2f0984164f8244570bea2e83ccb0c832b80a034dbaa5eeb65f6555d56c7761db48433aef23a89764a59feea9bc38b1c94548468eaf698ef1a11cfab2ee88f4f8d8af083f27be5364721eb8c6e9ad842dcdda9150b72c9cb21fa3a085ed97d0bd06bce8a28effd811223807bebef80a028464ece7e7e4e53612937f18bd54ede10b51b6e88bf60a83d6451f44deb48598d6413a38f46c519a63c031a1b5b7a82ab82af158d70bf6b5974d7c8da8f6f68d6be67314592bcd5fa91357174842a3b5b257c0d5e834c5dfbcc387df211642c87987dc8729ccce1b7e4635247289ceaa0baca326c0a824c59903e306cdf19ef0ac04752329a2265982ae5279bf011ca65dd6b707d860e7aff6a1204ce709831c705b1d3e64f275c2bfa1d1892c9024412b623fefa1db65d9d414661d3f4e7436cffd6cc52338707c09b3786e9560316c5cc25c8e1d69f7c2a0370984fb81555f4e4a89a94874ecf2fec1a7f1aa3c27b5dec94be8bdeeda38c86f1de2bda6f709ce3b5605efcb47df116d01311413667276a74cb737cd7b760b3b7772fd9423d644de787f7ec8eac6d77a009ca22c353cb889183bc4472b5903f1dcbab4bca64bf96dda0a147f299786f0804baca2f26f42536bdeeab33d33b6cb45e6367c4a937b53e81f373a5742129d5dbce17dde54c4f0b346b3e3204db9cf5a774506ba789fc83e0f14478998bbdbcad037ae085572a4b633ecb2d141b488ca43ebd0dbcbbeef6121a7ecdfdee1eecb9fedc4296ccb37c78ee3bfd9be9d4be306962984f0b3301fa99953193826fdb0d6c4f8547403f983ea656f188aac1c71276348a195020000000000000000000000009de2d54e9743bdb28eb878ab265b49fce79897c8c3454d9e40a0232c2045e22605dd4a9b858ce169f0aadffa8e1b2db1e7bcccedd859a5eb2c097a66d9b9a18191fb88e3922876fca61328bcedf159246a4868e9f38025585193b3294386e7fa00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bcf0525569ac941c1fd17c736bf80b50907c65f626ea46f859c076ca3ba4914b0f49cce7446dd85d0dce68d09b35a1dfacb659f5fbd637e1323b3326b869f06f95d782ebe18723bef7df33c3ec5d4ab84db6b574cd08c7b38fffe7abb4ef996ffae678b2643355028505da5f3844a0550e3e1508b1f5478a12f010f63284b00803304e3f5b15a72e918dfdb0d1d706db4bcc417bfbf4cbfcbc2d00d0cfc2bacec3021f07cb2746f431ecd2e52ed6949c920354f9b4a4a0968974f19c5046f1589e280b0750e35942207bc04daa06c9d89eb6d5ac6be0152c54cdc31b4037e9ac0cf11111824891063cc0305fb4d0b56d0d6670737434ca685abe579f3786786f6348dc032d2a27e31404c712e10cf744e210668ffc54c17f0e245acda356eaf2202d8ccf0313ed863b93c2c8799e861dfec512fcca3e3f2f79fc5610105524872eb7695882021e20cbfdd182617c880c87e3655f3b189b1448cf38e2fe8f91a59b82eb21c03f0221c3400adbcf3450cd16f934423520321f9b57b6d9cf52629eeaa9c8f881678c0221ce049284b74f1db34073acc22dd08bc41ee693511f2b72237300ea77734511a0433dee8c4c5108da7b653d947faf11f03e0bc2b03c1b44dee0bc60e7c46d8eea0b744d5fb009fd0d0deb976be00443b5dcc41b97c147b2d6a134281bb33848932b933e99e0a824971aa3fe5d30f10ebda869c8c9faa089dfe1cc1d08013022fb15c9351644b779724318199d31f2657fe8bf7ec3ea7393bc2c89d1d78b0dd7e06e2351e56bc4fe519989e45dd7e6b0062592a63d54ad7ed4cd7e4545cdbd40eeeb3cc2e34798abc9410d255429ff00432b2cb517ee595650dd79aea04a507acc9238750a5629827d9134ed8194fe0eb9fe39ee1ceff6471380a6518115a18a2a69472d4308cadc2e2d54575d0d954327d96a17db89de56567dd81ab7f7d1fdb19beb634a94857c468117d5329f787df2e706e4d4948a89f903c08630bc52fd1ad8c4a0caa977d9fd5b6efc7c8c018e4086a736321caf4c11330003583edd39c0b5a6e312f7cef53f5dbd399a57a53964f080754892ffc4526888bdb71c7e9153e7ffed251c6267e0a90169fad36fe936737584c5f70d861cc6a5f3f4498357b0cbb3448d0fbced61b1fbee76c7137fcfafde6618acde2e79ef97b3d275982236db739ab83fb7434b8b232279e8959ef62b6dedb03609b36575f8c18ee978f8e64748fe4648f708d6ba50c006816f24883aa858c656c2188e1739ecc4aa970cc41430f6438f53d0a183597b0a1191119d8c006a6eac8feb0d9d6d024cd218948d34cf0267e051d4f678f64922bc16e74a84ae6c07c559e711a96ae33788d965f0d21383a1c9a7c877aaa5a5adf01e00d28ae96218e0e0896795165b5a703b40ca9b889531b28bfd3a4b318d4fd41ad424ad2a30036dac46624b6c920cc05d13238b090b05e2647c266f62553d38b5f06094de6d28e9751eb5393ad8811ccc3ba6ea8bc64c81f0e6bb07a3270744d828f2a2fcf5bda016e6f95c10f854989ba9d7643b9fc6482a6c883eacb02c853011a533d190793dd58d01ea3514bd3f5d35600be1ceed1f1f89b5060752a2310b88a807c3d549963fa5a022fa98677e4ccc4dc1c08bf94a215c51dc17ee6a384eaf29b83174149e49afa213567199940c8d567b2d41b2f6d14d9cadb14998804db6963d41db27335d1c987c590cc80c02cdc1f7944b0dd871b92ae78e413736fca3895f893a317732e85516d11aa153b8374152158be46c02c97e1d13e439701ca6042556ab607ae6c0380f1df11b0cbe7cae0dd5c8c36f91b89741cfbda161c0cbfb148e068a4f39b24ce82c82421965fc95e44b972697c8f07b609e5ea5db4d92906b395a7ca3e1c3361c17461324df1e87fa5d1f8b5dfbb00ae95b7ab0f4adcca9f08330b496b7a0667a4867d2759387bfabcfa0e4e5643255b1da70b11a6cb85cf309312a37d2bcea880d12dd841c58bb1ee46b15123462143c0bf5f28c0ef38ea22a77a8cbbb40b606a5014b241cbf2062e8c22d2ba419ede388036927685507179b32907b547a00c741f89bc10a4ce5f6ecd317e261dd19aef7211628f972f5359e356121ef5f726761bd2f3998f49c1ece65a4531a73c9efe9b18600d7ec727640b9ef56df057a9a7a549a1c8fab93748ab3616720fbfdfd4ab06266b4696d4b1ca70f56f4083be5294f5682f00c5931275214482bff98b484bee4b229a57032018eb37fb4ff76372bcf8e489b36e2f9c98951184f1b05a6eff5ae18492a555f5777d277c313232ac61911d295d204edfe98d9257f849c6f7bcd0a2d2d226e9b053de004346fce67446bcac53cda15c24f58ce583dd5f6087a9804ce41e5476e26683d64eca0a4b377b75b3d2eca822885d26c4ead74eb85675dbc58884ed903", "6a636a00", 3, -1127645152, 0, "52d640156cdb73118bbd8134562aadf8c6bc7435649b86f7a28fed36404beff1"], + ["71b2b80e03a5349d590c0cb3219e07547e063131aaf446a0fec6e9ee6cbdcc5e980ac7f353030000000752535352636aac07c184b1709ec8b248c661f88be75a89b8311626c17e370fd0ac7ee4f5305ce3c51326a3020000000165ffffffff4f1491e780a25081153b75cab91904fc7e66805711632a1ddc426f11c461f60203000000095252635253526a5251ffffffff019a2bb3030000000000000000000314fc300000000000000000000000000094abd0efd883c1398d3d61b3490116b2eff66de6f99c8eb49d7af2bc20f644863b2b4184ca7419ce2445f69798322177a10513cc8c51d4009dd26bb4bf4da5bd6438f139b3e23cb442af1303b9787b3775b28fc350116f06791f68f2872a9cf100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab9537f327c3f08fd44aadfac200fa190ea5e4b59e646697d1f9bdf944aad1f996796431c8dc76340b8e5b1d40b8d0f70ce0b9b7059551b429063e00ff1ab89d15f833275f37979de791e4d9f291f884cdc1aabc007d5d0cb8022a9b0fc156fb9fc9ddcbc2c3ef2ce761ebbf3ac1cfed5250aa0a971d4c5d723c12430fecf1f4031c7818a7cffaec294e98ad75c04cebf3c8ea40b6c0435e7be0f47f67b3ad566103271831d845fbf79d69d891a88e1f1c71b7acdd871682e4d392c04d10d75c71bf0b0099f7d09dd16b0c2fade5c065722a4039b81d89d4e2b99ba87108fb0ffc6efd76156aa946b0f0d4bac7c5766afc1b93a5f42f0697bb7f7c0de8b465bef3e5dc032fbee2efb1a7a67d4cc9c7b4f077d50b48dc5662fa5cf25a9178837a8ba1095f02015680500048ec65905e5eb54db904c92500d67299e0bd3163647bbbdbf18bd2021905eae619ed0b4533c7bf0911a12be14edbd4c357046c9e88abdaa192d0e666020ccfe61d00bc1a324db9b739fb50b9971cd12cb462013e6fc2ed76d9d3d36d5d0320b492170ab84037a7711ff6f3b37412e2f550bd5d5158456c50811d4bf683b9494984d9fd33dad9de87c074aa999275b3753a596333c88eb7097e23fa595468d85a8bd7824d53e718d3cc006a7b2feb10bc33f266569252187f9246b16a94a26ffe3f56f25433ed8cfc617ca94eb6a5232ddd8891c901118702b10960bb5d81352b4b0a0a9f0c7a7e19a672033016f5ed3b0cc23848db22a3ef9d3abb2f5563843d5344c0e99345f0393f21714d4f44b48f231ecf538306b2900d088dff4dd105a1dfe49dd0137d6532fc461cf08f6424195dcc72920ef23fcf146a2a5fc9a28edbc2e75ca34530135d870e3ae4eb610a74d647393bd84b0b3e51e87a490681446f983e587d1b7a4bcca984a6675f0f45bb976dccab781b96fd5256a7ae0210ebf5cf8bbb95d67dbbcfb310f4a350e830d001a143060d2fb6805db689b64999f44e7122b83d55e1c1e3c3e9a991d5a2c49c9495816bf4af2c67fb7f3d6018163a401f86b40b9c92c2fde9ef5261224065162addfbca45bf9c27222742e79dd15bb7ec1ca04db59efd7cd48a99885dd6c8b675a8492029be04920974d2c6222840a8e65186d48e229595e1941340d763f691c83fb17f217f868f28a34a237f95d6d8a63cf737d22f7b989cfa65d0322a57d87065c9854f3640988fc7d232adfca12e33c62062aceaf7792db7993ff0e936b968e9fda50d2fd32dddc78e4ff7457347173a32dce10b09097f37bee8771421eb522366f169a0d8b58adffb7c0d569bc8e312744f32252eb70a75b0317aeebd754d650bc2db04720a96c1b5267f0c47935e30105172d29b84e04c6b3a35e9cef01d5a866e032b94cfc498835e04474687b2e4ed4c1a42a302f21c068dcf78914ae6369884a1a911c420befe89a030b77417d4f042b984392c6278f4a31f4cd593fb87bafede372d5f8f745b02912a4833a6e7a042781156b123260cd84180d3e86046bd9d67cb15c5ebf036d38d22c41c66cc8700d61ff0ef70fae33a3650d17f3bd8bb3dd0520f0ed7d08b166fbdb4f4d5f4219b203ac06e9003a0ff71daaacc5cf578d625d384b5bf164508cbc0cd8e14cb0a1498939c26e1559585b7149a8bd2fdfede950376b642aad2b01b89352d3ecc1098d67a0df4ecda74b16fdb6225313c0f708ca57cd4e24dbd4c81b1b03ac507849805949fc76385d44a4e818cd8a801031f4de143ad44f09fd4f9bf4688bd2434dc6e82ac16d036a2dbfb3c81046ab8092ee224fdf03ca71748ab58916abfebf0a9d53133c545d3d2a29b8f042d150a83f1c56d39434b2638b2ab144dfa1e7c97f0bfb62662dba329420afe7a6d283a3e5a93ff37569bd85b33ba4bcab7a40d188f7b4d42d2fef8ee1bc74c8b611cafcf37ddb9c4fa17f9c493c3af333fd0e89e570fc4dd6ec5055005b63c78b4f12e294a40e48217671e49fa5d5c19a36a9e9d2434931149a66c02441aea3499f55dac6420439ae0bc8b04c006627840460cce312f740070ac9b4d6e7a9362a946165bbee157d7ef7a8515cefa72a0ef22c8870381742cd45702fd8df60549555e0085f76886a14944fb2b05c4e58e8a8369eb5a1bc5344aeb4bef90747817d5320e5513d7459dd4b0c3d62a8c785c55f24bc96c7764133cbba019d7f24ca02dd83572bbd418757f628513e8284e85d87f8e7cb9147b0747150204079a8ee2e874f809ff3c63b00242c35482948775933b2d88441a016cebf7b4ad82c3662a9000000000000000007f6ae300000000008f1108c66fe8018256c742ed3f8e9f90c1e437da54080eb7949362f911f84189e803952ca7bbf69f1186f19368d6248a3af41ab7c92305437568534d235670b6d05c3329e2c2080b2b2892d01831c64f3c03c8b52f72b0dfff9101a398c72c31000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009808c6cfc3fdfb9bcdfeb5d4cc09be6bebfc8f72bf257eb2b43d0234ea07a57f335689ae6486a4d72a8362d272f7c506b6cb9b7962ecdcfb50e61b5c7517f21a57585a78db0af02361170a8f0158d99c1f8dc3a41c6e6c4a5c06d9110c0b374da25534f65b1338a598575c48801fceddd13f3b3d4d31d36c1262705fc060b29103198dcfe54588430da3c5d09e5f83a784aa0b428c9a192907c5a431cea771d8cf021a17add2a2bf93e2796410f482b4edae32516df2688dcdb3ae801a50220aec560b088ec6aab1da53e43bc6d7872eae3547bd45ca9aab2412d9c3ff9c75a6a5d2b9b80653a5db3debd2b3534e7df0edefc7492f615417f9fea9d3945face5ed5403030eda6056a24596319185bc63e8f69abd4fb76f575e2a12422a7abe0d0f23bc1d030cd00b561035c98d7bc2052502f61e6de29e1583e25f6a277c46696c68d7282a0221d0032a9535cddede9cab534498769b08c391b4d25e23c2e7ffe079754d5a6a031bcfc2091120d28f4f81883ef5e393c7222db58a009e7fd2cc01a1aea0856b84030c934efc4c8be1effc0f1ba580c3fd535f4344aa0c80ce4924e96558c796651c86f945647eb2ceef0d16688597301c551e4b07b8b4b1d2b63a5f11bfb782a67a8e8fdb7e3da1e7ea63d0d4e88eb14e313021c653ee49c6a46d8d1229f8b8ee9461e00f9638767ac250a9cc45a2b0131afaa03276ff447f6dcdc9d39bad82a4a64217f6eff500be7ae999b8fe24a0f8979c6b07e31cf20bcd43ae407cf6c237960336a85ec6f51ddef20d4167d004fd42d99b5560f09bf25c5ff0768ddeb595b8cabb372ef7c61e36c1b71189fe078f247b99bbe82c76ae6cd535f5a0270b65c2abc64e9a76d618ed27aa692e9aa7aa7a5bd4c13f31d596b0cace230a98de4d9c79ea20ea7f27e8b852538839dc6ee61634cfc8bee11327adf227633d2cab3add687d02b32b86140513d430d6475eb84444576d1793ef9fdb21b9f2226541624494a29c142d677275460b82f8edb0b56365b520eba64335e5a1160765ea48bd50c0b8f6212cc2b38c81222ae83f2e584bf08dbf3ecc266f5869f8ffe04891261bd79545af6562bb8dc844c2555a30abda9aede8432b7013d9c794ecd3480568ec30ded0b9275dcada8ec6c7ba01609c31a0bd2a405c7591dfc0ddc154b697586c43f396a1a1b9c6bb6dfcd0115428cbf71a03f8ae199987bedae21652d7b73bf56524f1d368ccbb4a957e0bd24c060a57adc0cdf6cbcb110e63c52fbd11e8d7e98ccb0d6980511f3c80c29d96bc19cf75411dc6016218aea884ef1de737c718b1f6c9e615b383c40af563341fcb4e6b650832f91e66c7e34be1d3780ee39dfffb8c829ca85433090862eb7e44fa2ea65efd2c286c1f27014c4ef2f3e7009772341def35255dc18f3e8b969f315ded9edd073ff948b37dc1dfb18e6f564995bf12b7955667874ce89ef2804d4fca1e9f1130ffdd23795740891bc47aa6b1b4c213ce638fdd102d91158f13ebcef6517a9cbd76a604cebb59fba90890fb3877e8fef0ac63333de1f8d333465edc6e74123734610f5eaa9620c6450d40072fbac078d44125e695b331fde7f9fa49950cbcd8f9b7a303464a6c0f4acb7fc9c8881fe2375758ee723d7535755d7835164561c4d3b8faad1131c24ea9131efdd72e5e16eb2d6c25ca056d25e2985c72168bd391dd6d75b398e6739cdfe672e0f4bad46a2f11d6792e4f9b6d61155a2c74d04203e32ed3ff9ff12df90be77af303fb18d59b472992026bc28220eebe9a09dc9cb56820ee5cf21e9ec921d27d798e3c5ce0881bb9d0fd307f3370c30cad0c6e64646e4bfc9d7d3645debf0e97f1f28786230d89e6b2f1f3eaa80b0bbcfef159509acdbd89583038c6c9ed549dd69a6940d5c1bb0cf0f203bd53c886e9740c1bab18688ee470bc96573fe8f44213817a6cecdb8e6265c790ec9ca7776316de44bfba1d8d922e46ba02118895d12215ccf3a59ae861dd8652bb888247a23397523ade7802eb53595cffa90e6bdc4638d787615e3e02a6921401c3d45e1fa83fbd8b75e8776739789568023be262cde36f96f384fcccb96baa1f7db29826889805ca615c374dad720b8c69477519340a68b51431f9479c3b438c73953b87aab352d2854a575d9af684343223d27cd64c75fc70a41c05cd0a7e7e98b3b0651813c4a0f347974df82291abee1b7df5aafbfe96ada8af5ef2317a3ba5d78f22962387493e1d67e81ccc061f4fa4c42c3f319c346000fe4ec68f70d342e7d3bc368914890254850000000000000000de04ac000000000035cff9e7b9c257bb0f84765dd027db1598ce8c6a39593bbf8ed0f12e80aa8a56447d21d2b1a105d83ebc25aaecacc09a1b8da70ccadf888a6bd49354fcea5fb081ba663c639f9376e09d78d66f584a6559ba8a19cf34ddf9981afc670f8af8360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000073c8f8512482625936861087a9fdbc3afef3212f1b3a000663445864e84d21984ef3b61090da8f539a0c7d26a3c83d6f82e3b1a39e1f6e7bd3f8882317aaf20980cdcb39b3d964f3b1f0000200f9b65ddbf5d4767f2d143103ed689d63089012dacaecb7c3d707d59dade90038dc07b4a2435a563a76d05a64266c7871c9a801021ee8704f18e23e569f7aa0a880cd9b38aa5f4ac52b7670cfd2d1e8fb54b50361021265a887b660e22fb91dc1eeae1382cbbd8a60bca0a9c61989807e6b699829e20b02488dee91ee6b1d9f19672c6ac35a1e39b4f33902ab9001e81ef7d123e99e98d32aeb3d5e12dfcbef1dc6f905f24bb7c803c168337656207f24e1f560273d270311f88d636ea09b1f16024b63f7811c3d1a8a50e143e46ac155ff2be0a1fecdb60227a9fac805d0f435bea29fa1b72ea832bfee5a7207726e4167d2cc5b3938251d031ced0bd29f90cbaa96c8cb98d852d71a7344c94b9135d44068bcb075645f039702211a3be7e44cb1d9733cf141f5559d70e6dcba3d720a7ff50fa3272aabd2e134022dfa8e99b6c07f0c59c2058501cf51bdc2199a9b3a8b4b7f00b509ea4713ccfd7143dd6cf34747d5220a181842dd111282897e787ce0f50e087e1db1b09d7b076376690420af40056499b30769fbe132457496a5e2001e1e1cdf4aac8282a02bd7bcedccfca77dfef1e89bd99a957069633b256865c799d5833114f2326534837a5b13b69a6b8fb972a53c1c316eb34ebcdbad8d8b3279334aa92f558600ec880efb03460953f6521aa40139c337c27f466a56187a62704c79d51773e67a73e41bbd3aedd29a3c948e7deea724aeacc32880ecd9671ccf2524b1559330adc3c81618c55a76e5510a2ba46c8baffcb571ea9540bd5f2116945d1d0688dbe29f2b251ae5d52dea15e6684939a281130d858c08c8085e85186b19fe8362d21d855a680d0fa4014c1d291fce0f3734e5ebbd7007021b9d440032b3a538be3785c641b071b73373bf243b2951ab06418a4caea47de1223c60ec5ad307c82fff6623281481188d49091c6337af82cdb7caed65d3520db958194dcc335df770dbc623694977472f3bc4bfaf2358507dc7949e9161769c4544b8e2484206e1e9cb80b7693766053143738f14cf2191782be7383c36e5435ad600932639f0c1ad04de8a8dbbe51d4b1c32697d3dce36adcc24fd7db659c46e8c2b4954a4c3aa465dedf06ae858f3b89e97abf307a24d93d849d9902bc6953f86cf4cdeaecf7185b9639be690229ceb479d0510f49c313423c20b7cdcf60b5d58f8d4670a222796be6b3cd531f44a248f21f8d5670a99d9aa209570d0c0c4e4c1c9d1ca87808f74c600f70032c07e046adc78a842258c2985e9fa21ea4ca410fec187016a75cf45b1cac6b07e98df2187d43d736612c90c383382e3225851484e1a504eaf281c029ad479509f571a6f7e3cff9fa095634164a839307778c37848c2d4c0fd683ba6bb4b71b0f901a298908004d22edfddd3392413b8eb2abbd45f2d8148981398ec351c55cb7cf2fc9938fec028eabb2659e197b16ae2c04dcbd031f99979ad0a90794c0b15dc5126787328fe0f9f222bafc2135ae2122dd9628ae1af9d20f2082d47926f878d805eb6888ff4e6a304e28366673ec80457d00a04be9a3f32e76aa4053c6fbacaac7dbe8b0f27503eb4364d5c9ee550387bb4484591d4b92df98dd75510069a54a46a595f33352c3e21ed92ad7dfbe367e59d7b6f4cf187ab9cdaafa19d51de10b4f5717f840d31ceec6e40305ae67cd15ae623066cb9af359273741a9b0dc3a5f56107f8ec8fa7f81d5ae31cb8e98ba8ebc48961fc2eadce74dd2b1b9f3db25f1f150964317a79ca4c3b13d5cd08a97d5816c7d768b033c27fa4ad7f3bfb5164f48be15f72834b302f75cc75175ecf39712ad3b22335979601bf760543653f5a275d92f6a7c2cf6fe5edf459571c3b5293460849c2f3522c30700100d25e23be1727443c70804310df59cad8831c24be3f4fd32a1e7d7108ca59f181a687e0372421a7b6a19e396058aaa9d182b64c39d3bf84a2346e47e96a43b191bb986b457d92bc26ab9c8411b146a7f53760d4ef9a05687e08287528ae420067155634cb9dd61723c1e2ef330d5cf22e81665832221e040ab91f532ec6904b1f05897cde42edaa1f114752f881898d2ec844ae39dcf877f31a936fba5d461f8c74cb95ba59ce313cedcf61390be61adeb4991d89a7c1dc8e37e0766f4c49b65db3ff62e6360420740388d71f4b7655c13b51d11d905d49df291d86c079a17e3068fdf2be0446499ee435198cb039f5e18ea737a7d7180711f171ce8d262742616f80539b182ace66603c1f77d04ccaf0cda2f38aabb3ef31e736a4d2ddb99ddc1512f32f54429c1b9f3230a2bd2bc6ddc38924720c", "6352", 1, -640717403, 1537743641, "591658179137a3441e5932437f119588503e9c1d838eb31ef5eda9fdf1ddc740"], + ["030000807082c40303fdb44d13310c4fd4391b696573f23ef747c2658dfa2e6e3cad40b71d5d27f96f03000000026a51ffffffff87e0ac38f41a21bb1e32ca6be16d907c61a1fa84ce9c8afac549a56362e5f9030100000003515200fffffffffc054cd9e5407b58442131246ff8ec5bec4cdf7166f90db6819bbe6a713d9e0b000000000463005100ffffffff01dcfc4603000000000465ac515225e39cf600000000026fb0cf010000000000000000000000003da3985d4cc079f9aab5e24fe0dac5b09b02ba801a8fc8c29446d24fd65bc77ea8aed5ecdb4caadcaa4c530357b95f864bb9458004f5691aaaddcbae969ec2dfc15e1e3e13df9ed9160cc641f1804eeea8e4982c1f9f12e54f19569706372e0e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007274c7ce972335ff7b778780cedb909ee1323f7f43d55ff6e04a68c859be659d6f808b52450423ba1df678dd36459242feacd44c7de063e8fffcfec8518727aa70d23fc65022054a14d44f9f4f3cbd8be41146a61fc279bea698472efc5f910884444efd44b6c963898382bd137bd4f241e3e736f74f9245604e03a7551b79d7030b16c2d7880332204c5ef56e3b5b9e86050520fa6f65c82965e04ad5037fdf8202021950c54a448ef3d4795a1be40307bd097ae2649944b278ae6616746ee136340a009494ccdbc2240cb79966d786dc78486e23a66c45f31780b692862e8e61df6cd79f140bdf8ea75e3cec6fd7e74efe3797f09936c388093c9537bacfe708d643022df3e80619420695e3f5493bc1dfd2f543d310743130ca030dd69fc3fbeff56f0212e1497577e7e123728bc6a92a4a0caf835964710b1516f4cfdf722208325a570217e1cb7f13a1212f05fd907e4e0869bfd07b6fa5fecff3741f54b9319fbed930020d7ad99557bef407ae608e311c907029802a3b098c21d84a346881ba6d1fd5c2022ef45009cd645f5717582851cda547e4d3ff22c65811a8a02b36e1c6eab5d4bc273e24c95e1a3bb4df68a9e2422dc393f5b90b71f2703953aa99e37668568847dbd1c3b28c4b07ea4c9f3263f01253fcf641ac503dc4f42539e5efe63e71b68e91dc26326a7fdd32faa62487209657dcc340d61848ac6f26f7e2e5f511cb9df3e33b7aba6ce52d2b40d006fee58f59ae22a7499edfb56428380d5b5b550c36e92a66c9484cbcf7508f330a5eb731c7f0fdb705fcf6d9737cd4c40dc8a4b58bee66d2e4b453647773d9f375565318da961340b624414123d881b406b0caee8f982d125e4fe4fa7109cfa5daece8c665c58399c9e19630ec48470d76eaa02ed82ba9f0f9dd307769e8b53f51c40ff1ff21c6e28e5012ce335b34385c46c7f00af74faf64ceb27bc564054c4c3534686c76ed96156c6f8d9ab338fbc3f7977324dbb7e231791e0446c47d47091da1cab619ef032dd69361ffc955813bbd3091ffe01b7add926d0364a55839a068ee39c0903e2a4b4a2c2b13aa93b9ec3e9584de4af2fc80f49d4c17b68a828d40dc6ccdc9fab7a086e3f0eb6eb407702d3925d908cf56a3cc82db22221ef0744204fa8c8f2d2d8f22df72f186489decd8d541f36fe9836194c92c4aa7d47a2247c500349906d5961a425701800c7be07ce4f1362d2961df92d5b58c529d0715fb5ad19210b300853b2b8946c41905c72d2434f2cd9b4ceb6f482e022cbf9aa78e09a2ffef5e791d457befb162aeb56a4f3588c5672a618938e9ff8bb161cc6283212a49432ff83cab0cf4e569972c0e82b7675a16407e4a7048bc7b7b3344dd1a52f3dadd0b23d1e013db70406d3dd57b7cd3adf681a27fedc02e1e21047dc5cf80d4537a3d73e63b5a5a6db3964802c8a3f282452442d4ccacbe280e8e9e8364dcd837d15ef0ac34476af503ef22da979aa44f67f3033f7a94cdd8d9e6625fe4c465f964e7529fbd0f1e0d4e684b7bfab1c947695cd8263cdffe1dbbb79f53b240f5e6ca7d9044ae6001d589fac641fd18ada0fe5cc0882bba1659caaecc64f325c737e41dc2b221325db99f2e53b289ecf4c92ac41a5df24dbaf45e0fc3d4cac404eaa9742b65e364fff08d4467ad181f3c5d7043d6efbf2f6f959ef8504a7d6e924900c2ed479d50049f122eb73a66e25699f252197c22d23ab75d1e96537e33d1fc6ec2ad7d958bd2b62f047a28fe65543517717263ba672b6c2c9feed93d33a6488709701cac9429fe144c65ec2dfa779066dba9a36195d750fc6adaa6ad408aa94a9951d9e13386bc16a1f7ccd0ef6b44f8d349bfa056643c37c96c2286bb3ae5aa666691523db1f23b701c1fd086b5c1614c6683a5d9dc286e5313955fa87ba7cde15ff7b840ccf7fb3ec1619574123890469a586d077e0fc1875342792a50bb8d08b2e45ad0975c51d28b5868ea108d4ef277866bb56ae3b10e8f44158c9a3914d803ca0c481ba13ae7af3ab67ecdad3d61a03f4b13d2003563eb4c2e5b23f8e83a7659be74e02267050300c419ca3ebe1a0c6e9a5d915a340b96741cea8bf7cf52324f43f344b06209c000b2d4f52a734821e328f31e26a91f1de8d93769475f2aa9781732bb451ce55799af32c44ee5c4e9a0f645f1b49a269d838a0a62ab376b736bba65fb03e154f094369d55696c0aa14a54ad3cd589ad5bd38369947ce0b0eb725b3f74463b80863703a5ab0b213e371272837fbf726cff9992bd0baad3b90700000000000000006937b00300000000b045e6161e4d0ff8f7c91617b255e985964e4641b3760f8367a9ddd38ef47b99b81e6ade676bd0463922a51743fde534b474a18d34b04bfd57bb38efb9ecc2c5c3103b4d8a863042820b09d2905872841005c6f0a03ab02cebf4d3384570f33f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000147173be7b4d928d785f67004779db31811f3c4be5a263a5af0ea40ca359f4c32b17d7f2a645a65fa1faf925b96ad2fff8c9e2d78fbc267ea6027115dfd41021348202704657e7a1818b7e7293f51c2e7d1264b92789f8eacb6f69072d1b0cca077b8ef56894c11916a187eadaa64c651dd02f0ff30181a15eba0c1fda2b8941032b9c742d5fac02af8eb44d288372c0eb4b25e8bf2aa569ff747741d19c54b7c8030f00b553415364354379b41fc22b2a57f3158e1d89de5583076e731f86fe5d760a016035400ce042dba3b5d253723cb267db7c3b56e8c1a876148b3858b2b2413ce9e3bf939958b71b2b40cb74ae05bc6ded22bcbe26eddb74079dc3329bfaaad902136517792f60f320e83401ddae513c1466f43509db61c14103b04e13d19c659003153bde51615cdd1d884a2398763d5428322620b647148da9590a9e3661833ceb02010db99ac428ca120c25b38175030cb6bce8f40e09b9b4f9439e496f6b9bbfc3020634ce79010c2f57b44361f711c65840737126de717296f4f3938fede7bb35d00224c4c33ed2353086a94064304a47c09e9f3f650e7a7f5f86dd08a5656df38d198f4e81fa5311f7dcdc0059efe8ef797bb2f88319c3978bc5148622f4709276a27b72657f5f15e55eca056589ab159b81914b44f09cabbced7245b5611b4484a883511281f481d8e1da593a16182d7f5197b8e52cd5b87cf7692531046bad4e356c4263906a0f0a8a170afad95db50bac2c0e3bc2a7b6d6f11d1192b10ed38a5a4a36cd1efbbd7e3089438b1da3e1af3bce62b168a2d444095506b723c763155c9d3ad33a3ae1b2933ae06bca00c6c54c514211f1368c621a7dc75b0041bd3d108cf5c42a349f27787e02385f0dc3ec90d5371248446fa0831dd9d52acdd9f4b6c9d8ed27e9f384b6dc649b16e6bdd34be033985bb2862abc0bb81eb40dc8cac95d82069fa87412cbd22ae36d7f8b1dc7c14fce59338a767c733ad0d65c9217216b3c921190d01ab0eee4a41255c4120897e3109d3ebe749fae684b7162d3696726e01ba13c7e32e4fbff22e0841bb21a98da6ec7df7e51ed8b23153f488edb2bcf54d63e179eeb7c2ddd97c69823d8b51081217070e4081e9d3996e45d7b5781da51fd55801420bced4cf7dbd7742f98ab342c5a9e7698e839b75f28a68b82e12ea2cb44079a6adc12c7b2108a3f38582e5d038ac827f9fd448ca4934c75fbdcd6e77aa0ab3ddec9e623c24d856228f519765a732a205950a78b3d922ac2cf8c764851fd873268909b6d858ff457f6dfc2d3e8febb3cc36a379511ed0a5567b791f502678b5402c00d2e51bb13e7ee62aa9b898c92ed7a1f65adf71ba2b8b710a2912d5fc6fce62c9dc72c349cb82f34cf90c9962d9057d6603f66ca4c1c2023911f927f524962207f6126e7f0795b8fd5dad12a2f0d7895a700e96f584dd56a4bd0ab4c48acc981a5324f345d5269f57abd51995386ecc0538571d4f0d794627b4775146976c079643efcfdb421217e1a5057ecf49a904b979b36baa7ad79213d87bb7524ff27b24bcfe6209e25ad5b019ca7ddbf2f4635d5d1f7dc70590a4024e66f7099bf10c43fab1b7d8dc664dfa846b4aea236ecd1bf2f338b45c5946da688d8a3a59797c5c40aaf0af5a2625cb0ea8d024c50bbc381019f21f9419c2cd96e48e9088a6f9da08f11bf616f2832d3e22f2b15a89652b157deb73b361228961f86bb11c92a7a891357ec74d6d968170a8795d8a0e5995fc338a5a38b12ca88704544176a251607c132a4ce0c74209d0a404143367d86327e418652c26bf8983dac26efea9e6deab808c84b5c70c4d436133a1bfd7003aa70f3fc28be8e0cd31cfc3d0611f991233dff34bd1e0dd0541eff7e321bc956ff385a01babaf3dac639406668182440be9612e65d973d5ea44b4ff78bb4b36e9add0b7c90f01fef9b236924831eba55a9113145cddbd102fb22b38294f1d8e1df64fb5e8bc9b293e61229da76cff80e24d1756a63ac5d768c4e466805c0e4e8e1e831accf1a4ae7559d09504374e39f93dba72e0a5e23ad72bf3e93a868251338b93f1a5eafdb34c39cd80a71b0df2588e9b85fe9e4497739dd7e3afe9e8f48cf61e392dafaa7a19f106c6f3551ac6b10c8a9eec09b0eec3f31c5e327a4480fa1daa836cdc54d46946e4a859cc47123eeaf6f89dca7eb5649072135a0c42e94902b8977c2fe3fa014f69e411fe10544a21ea7b163f893c776ab55562e6891801a7b89bdc7f3cbdae0a887e1df14a4ac5e59e67e8efc627ff781b86d52c6db526722b405a5b500462f2861f4eb944bb0d1a8a369aeef0f3f9d677bc4b4f1459bcc6909669e2a654656a3fd53ec5a4d94588720f408dd8002084b281263f191e855d72b2b3a565c6ceb71df19ce67f48f93b2c6b2a0cbbbe4ae05", "5263636353535100", 0, -489642415, 1537743641, "ad15e8799f5b484b23391f78f35b8b1872d70e15ee973b2ef4015c0d04d1da0d"], + ["030000807082c40304128849fbfdc3c1baf71e0d9fe06c66015e05200debebd14976dc2b94cef4c1fe000000000451ac52acffffffffb62de784bf9e213b1a101adeeeb3b61d2ebb8c8bb5d417a6c2bfbb8b6b9bd556000000000252655501a777db8708232bb9d16d5bdabc06766977c6da40486d3e4cd4decfc4343b1adc20870000000000ffffffff6c3c7aa7b69a102479a6877a9c9cd83e387cd92985c4f13122578a157a42ce7c0200000001ac6cd1dec801323fe40200000000086a51acac0053ac000000000091d4d04402178b0a03000000000000000000000000123658917cd6c58aeeacd645cd8c37755eda80995b8d27b66aa63e75c2d5476d11ceb7645118ad29584d9c91f46cee5d9d464ed97ef8f7436b6fc1e2de2e699edf46208d722cb108784f1673722025b07a782f081e2c5fd5a8967a470780f74500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000df5ea280917101851328954f8be4219058cdc56685dfc1036b035b30690a1bab891db61c9d96699aa7608988493a0e15da0e672def69215236193f4a4fd13722aa239580ffa9a898f904b6dd447bd321c6a9ba8fe03455116be93832c400541b8d40fb99cdfc6c092a9698fb6723d9c844452e6f0c5fb3d348ff27338d375b7b020b82817af2adf9195476b7a34624b3f5d195a9254b4d2a365fd65017cbbf32b203101bc21a2fd5201cb73ad9f9e8521207f4736d5b79f32e54b781c970fb43dc310a05c39b2164d2f2585d0cc2d86196f9c8f18d58de257982d90872d78fe64d5ec8d3ce62509c697e28a2e900b3755c3ca9c3fe322b810f2af7f3cc59c678eec0e6032a9aa8beda1f29311c1dcc428a5d3eb250e6c00db77e1374c0106d8b9d370b2f030f0bebebfc8d4adc17be79662afa1235fd260147ab046a0639020591f48aaa0b0205c8e354ce5a5b25f99a462b552802a76ea3e9a3b111cf88e7fa86953264624f0326cf57b7aab2814b3fdbf0fa33afc83d1dad4977e07d40cdd89c724a435f047803002bc73ea376ad884383a56acc3a08b42312d2335d5bf398213c214ebe248dec6796519a532746a2e53e3ba06714396c104edc59bb2bc3b4845dd91b5a880e349665ce68c7a0e78788c73b37ff5c7bb35083220b01a17a94c3ba092e957db30cbbad32d7de7e312c2c18988763fc4a8d3a4c976b617c29601068c4d0b82ea966aac075c6e204e35a94f4733f21962b55705593fd89147638c8f97f12a5354af7c5216fe398be21b2ac19b523dced7b388b554584f08c4832122cf59d7a166e21729fc044cd9dbc190c77a9f9f1916289486bde4bef1c91a9904fd54b9eef16673fa055740b7c12b0e9e0f1cb3a5199f7a57e11a230525df169301693eef34a00877e38e8d05ab536679fab6a6540e7ac65f076ab7cead440287d1d634b3d6f87bb9b9dfaf082718a7cae09b450a88b5f2208472ed7e11fe1acd8a3b8f612e7549453ca0bddf3fba2c8dd60650d0edef98f7c810801834f15a5c7e1de74505e70272f01a5084f7e8ce8b4ddf86cc2869bdae83173babf468ad5391e8022e7a24456a434c690a2f89817bdb6e2be38aff2f88682d3e3e04586efe8867d2ee13dbe4ff41a6a01462c3491bf8a53aaed45771e9e90b8f61029c57b09d819ba23ed3ec4d8e2fb18ce507b4a7304ab8795893956288fff3b263ac61609d9f44711d9a2c7956ecd793bf7965b9746b9bf8d28dba787d3ba621887ae21b0cc0a94719e8ef131f923521c471467aa339f06f0ed8cfa618c97ae09baa185fdb47468e9fcf6f19c841807d6622c004da8f7452cfcbbd5edbb30f18c154ab8001f60cfd49ec0fd70e79df69a6a1183c17fed94df2384520a254af14f6ea6b2f9890f71bcd7b077ef049aa3c8b51bbed6141b6ceccf2b024ae8deff79bec2ef710da564a5a4cf28bdeb8b9269153cf0ecdb99cdffa98bb051b1d701ed18b137c2f2eedee6ae7bcedeffea2c2e16ce8551caba48e356637110991d35b2e5e9c3594b4305be14fda3c9bd0bfa8d8d2bb4db6b638cc46fdbcb2029212d03b30882aaabad5cb679ca8e946d7a913b07a0cf3a93958d8832d940ac3c57bc0d6e04fee270fd3aa20bd165ee06cda9d4dfcc0d9a65c68b792ac226a20274b6469bab522b2c34df39ff6263d3a7fba0d1a6ce9bc0d8ca9c1f8a63a5366e01b7af4b4ce0faef306d31e97ce6812381cc88157fb3551194709953f99f86e4f06894ad6966da093f635505d7c2f2892ff6a8ec4e4b42a27e0ae41d3d80a5d90231a0730985890c73315631f99aedf05260fa2c5ff79ef7b32db11c3052107fa63f8d006c7af40b9756fae82d09e7e12051b317e122c935c60d0ccaa248f1c7079511bb1205c61c11e73ff780adfffc2790eed14fe3a7d2bd28b14470ef3fb41b8b86d2cd412d082a51ad4e1e6098dd8d495ba0fb9d237c05dd31dcf501c4f0e8ceab3abfb36a8960ce3a4648f9ccc907f7a9f602542a28efdcb8e73478f28d286bc065c0696129df8bec496022075c4a6ff127d191b754dde7a84ec0f0b8f54afb27407457473c64b80ae6696f9f6c61d88c7a63462f223cbd368a9ff947700ec271e5efd886bdde1a92c1ef2148f357312c78da2a39b1865bb2fbcf398f0779350a6e7ca370d6b7af6e37be2a0b11b1ea3b6387f1447c92febe8846f70e962fdfdf69fec95b66338c7c96194843d5ea318779acf98015b7d7d19d8ad2a10e46061ea3f60c43228287b8b4b6b68457835c58214acd74d774870e77c76f4c0000000000000000b9c33305000000005b9ee7d96bc9c33d587a83a486c28ceb2c89785f268fe56bdefd83e366b62064da8e0c05acc3e8e0b89263cc160474a9e44489976124a0652f3f2167bfc2caef729778b281c7dc28af2f4d0c63425cf39460045a41b3829a72ea2e9ce8e2171800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d96a51c8ca8996bbeeaf88a5e694f85fd24812b16f5574f0b8d524edebf7d10dd66dd79f8a5c21d4d0f9d4b1ce125973a8ab6f08ba889d3b918354a64dd2b74df9e63011ff8bdce7115e3592a22b78de50401e1fe00cdaee4352349ee68467734e7220c7da39085fc040595a8b5956b55403412e3f45f73dcba7123de4b56d5b022ada67d0386bfa4a93a6a09762b2f2e95abc27c558d10241a06510d5cfee6b62021a0e00ac0ec8a113813db99d6a936b77d4f4f6ba559524b08c7cbaeb2328ca670b0337087500d9fb15fab1fe839b133126e959115eb33df768d61fdcad5679ec8b1aa870f0c1c5cbdf8755f061057b2b6d0d90c9471f2520046bd287a331b2c6a2021f16eb2647c4c07f1876727dee04b46a3e1f9ef9a469b8bbddd39f850c95cc6a030f13cdf899bbc4c68a08d95c9d824b63c06722999a7f43b385ba783627bf294a020144a2bc91b2557a646cacae67ac16a642f1689f535fee82364cfedf38da49980316c2aef56c21353539ffbdeec73e3ef4de5af1a9895d1be345edc9472d5551c5030b7c9e93a48f809f084cba18c62f577d825399a283b440dfaf3289e239d3c94e7c70dcfd1ee534a35bade280858d8ec51096a539f765d498e6afe0f45e34c4860778f3bb7837a04274f39c51f438206e1d8b8f5d8dd2f431188fb986f9afd65387936822156af7b0503562a5b15f811684bc37dcb5876acb4e394a29fa8aa61b931cb9a0a8e0cdcc0d5ef2ba7f706bdb57b46f9b3bb0c6d8863e5835e0229b0463661d74b734f05ca0eb9b601f514985c5dad4a5c661f1dfd589d368e0d7fb8fbdcda3373f81a9c153915db71ad4b59a0bee04671f06becc6bd862a6a14c3ac42dcced1a390f39bc2e2258a6fc964d791d4d1c71597c6af60cd08d1eca357f37a5915574c7f480ff1c58a7c9e36786639a203dff25d0064691fc769b71cec0fd21a9e586dccb9edb95506416c6a5c8ce6ddc4cba57d38135bf4bb33434f955eec5ebebc1109aa406d1a9aaf8a734727f48008eb1fb4129b7b8231d496366adfb89d0fc037e7be02a93d9a071aa2474bf40af4042c8aeaad4edceaf4e426753c6614eac1123b64bdb612a4351eada09560d6dcf88fa9af8ce3040c34e0bc830168b954f5df7f448f45be998cb74a670f32acdc61d9180da815ecc41b0de5896b3c7713b583e4dd9e7142412065a13e01b12ca6e2a6e9af4ab20086ce0952a5e240991468ee879cc7710ecd6af35d9c4b1f706f6a71f69c945de6317fc13343311e000964e7a04c586978953b1cc27abdfd6d1fe9e46cbfa3bbefa47d042ea7f7176e0b03986ad1bba87e5a569d5b677a64483d602bc2101b871769ca194b831ad7a863f7ed6e42da68d187ca8f6f8ec03530551680d8361cee02b7c4f12f20c7c6ffcf28d104dd3676cefc8d164d76a0db8a82d87d703f20efc66663d80d1bcd64624de3ad91dc8d54effcf878666bd4f0622c40164356033f2628d715df1e87bf67df686dcc8e4bada206b82cbd9ad8ecbdeba9f84f7796fb25e5ba05438648921977f1014b41b6be12b63e862e7d87ccf54bfd8f1495229cc6460ab2efa0d8ccdfeef5757b20456f9559a4d63aac4b64d2681f08bbf221dcfbeb7093900c99d253e08fbddcc4116e361709562fc7207e5363ea15e17a499dde1fb50ab06f30c5c102ea082c510199a2fdbbd4e5d49a496a0b94c9b527174d57477f0ea12c91e1c9c93152b92ce58cc99b0ee5c7e9f632192ded603de4d870b635bcdf80483c238184f1a5dab955dcc1e606fe90ce9bc3373d48c4a6a2c890473ff755d45d35df036f3a39450ae2552df1837f1df252b67e1d9bc87906bd567cdedda96e1cc8478957aa534e5da5641f254e7321fb8138ca4f5caff811135a29b89fcef7b5e2c643920513ae0314336d3a6295a5c347008f369f238108e04e3cd336ba6665718adf871e5f9b3d0f816eb2f60be883f4e1dc496aa5f741bbef9c605ff630d38a7d9d26c85590e24124ba91e2f66db192b27e3682e437a74903ec3e68870524cfaa17fa41527506f12cf213634067d04a80999c10a874b0b54d94b221acbc7856eda83190640f0e55b76a500f8307455f28c0aa70b601bd7a48e4d1941f838ff5d65a5e7d148bb382b68f44612e8997f7d6c043c1b94e29a6f2a4ad8c1da2fab462f1e7c676ba9cf7b14bce5304e904c8bea90328dac505075a720393569fae632c2bfaa3b1aad65356316bf6c7c3962900b31f410204d67c2adfd7884417ac0c076e6b648bf5232600dec83869f1b9da1a2088d632acbf3e9be0044d4eae711094f3007cda77ba0d4bcd86653d41bbb70d86d49478c755400db28ffd53c45fb9177f64009470cc8064f202afcf0c2cdefa8dec6fe4014938e22433c3119517a3bf5f94c08a99fa2d8ad371b9f88edc7aaa100", "0051535151", 1, 860576797, 0, "a06b8d443227e2ef06fdf53062656e32048ca0432b9b5baf3310268958371f25"], + ["030000807082c403023cf1ad2fa43014f366ef6040bfd923b5a4aced432ae1fd5e87e2bd340b98007803000000085163635263ac6500ffffffff58383abb65de0444c374045a59aa20556df5a3810a71fb8dcc52f29e512373c903000000016570b9c3d602f876c90300000000076a6a52006a6351ddddef050000000007536a6a536a5253bca2c9260000000002a1bbbd00000000000000000000000000724bd4333a168722303a9a74dc64fb28d6fbea3d96eb4ac95ea692bf6c540312aceeb0ba69dcdc81b2a66953ce4f06a4de3edfea224428e3755f8b95aad6575fd5ee33f7a824ebdaf06f2e7166ed2bfac73ee549469a947065599bbada09a9bf000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002886aa6014f2f9635792314af2d8f22554a83b261a7daecafe2b072b5fd9d47aecfe6e91730a4e3b1b3eaac628688c4345ed20e03da4ae56e088a7c88d1ee7965d6be74c4df109b21bb1c00a1dd3c4d17866f16498ae8d74b45366e6bb59f464311100d90a349c103176eebd08792dad5e7accc64d65e3fafd0e9f70f6c1719e0227acd0f6ea81894b6b1876c7fc7629ddc210c750c8a589a443c18d8649b38cd9032c491f7ad8c60915d5886948fdc3d68aa37f4cfa1aea39c8b0fe184f046caa6a0a048d9f53f2a6bc14aa6316ce5b2d472085743d51b23f238d60bc75f1dbc53d0cbfbee89d48d6969196bb51408933a8791fc6569dafbf992d611421d32c7c4a6302159ed31f8f4559827338724c37c67aca5689c9676154b23b2a8c4cb0fab00b3903064cbbec44182e857a713d3a7982fd20e33c8c396b62f38784048ac673fa810c030a91c7d0f787956a98a541c50e758aae1a96bfbf94aa5f7c147f0f8c976aba0303285c276780ca4d0cde7f57d576b6c71017c6358ce784fe9d8f4e2a466e772afe0224b0c9592602b76e0a02ca11716799022f2f962dec88a9eafecc76f1e86a9435fc2eceed8eb93cab3f9444d9e2c5790c1491e988bdbe81560752c59979bdbc4061963b6666f037a88bb4749045c4bceb07c29ca6d6f280578b01bfb9c96ff3bcf91fcbee5afde900d7f4b4996d6e581f10d7f512e38f1038ff33e71e38e762e44397342e0f3a7f605c1c8e3d759eb32804e62cfbb3ec1504c5697b3dc2e18fbc162f23ef31d853ca17383596ef9e31ee75c12cef5d4b50fb6495c96253ed8bffea70127a72d6b30f85c7cad324b99d606a64bf686b8230384d13730a52fbbf1027c8baca50763584c07c004a7a1ee84e88bcb7646324c27f178fab910a1cbc6414c35139c64de7cbf1dc279fb4aba2fae2aff2e833d71ac2e47fac3df4bef189522842420b5eb03509ab9c7997fff1f574d7f3a4b20f1b89ae5fc332f1c484bbd8db44f632bc91df86c940d5c14e15765122cbbf72ac24947e07c7aaa328b2f9900a599be7461ea8ff738462e260a4661bc8c457b144b6e4d14d34075b0b2d10afe29d0827ab48cb9324485134810abdecb801561d622c8010c97383c30713c8e64cc69f602f18da30da49701d0cfa8a0c8e2da7c100648fa92a0a9d57ef688d8d26fb6c1cf085679f60bd9f069027c257feb89426002f3c21348440e1617e89e65c161d5258e71efaa6377578319b081ad2cc3440d1a3d553c90a9ea2c78acb916f2c1b082fcae18c2804b852a28ddb0f477d50ca42ad225357c4167983c7323c147391d367757bc1066295bab4858567824188a34f96a647f60f4889b8b00717905c44bea63045f91a2b060095f552a2e1960c1a8a893c0698d871f3ac52895778c418ae10677200439373c9eab19a13a737fd7d46627c5da5e0b654a260c3e45e5f78f642aa400db0d27619878934a13a06d4585cbdb29bc2726ce69c629794e5c769b7b45e8e81e6c2d469ad48fe92fa1b158231dec1855ff255ff76709a457ea0c429fb4b9b57459dffe41caee516dbd3d75805e62722bd7ef312b76a34f45b79e75f94f6ed34bcd5e42f1d86098be36aeba8c766fb7e1261c598f7bcfff2dbd52ea0ddfd243e93c52b95ad4d806855d8996eb11e115f600d565e764b2ab009ffc356166baa0c4655da0a70b3a818744cdb472297e665ccefe783bba0d76e29a9c33a7d07aaab36faa836a30831057ee4f09d48a211096108b0a6f4fbced0521b400ea7fc0cfb7b8cb0a74503c68fee170bb6cf0ea9fc5b53bcd471d459d95ef49878ff4af1b34f56ba9e2116303a6d31c4cf8a1a9fea01eca2aca01216680688b7afa9b6c3d0e4b64dbbee934d1f4775dccc60d27d4476bbff58bdac17dbec48cb3fd973801adfea2d6934ac06cbb7b1bee11cedc386c7443f1a1888977c0291068d0238ef8e468a3ac23dacefb683c818910b7fc27d383f15ea2efaa8556cabc6b06d92e6905833e1c71e83c02a9ad50ba891eb1a924c690d2ced687ae9397b849086f49ab21ba0f6efbba6c3aa15b0d3e0a2736d30e363b8b76687dbd8197d8e433e0ae597eb52dbd627e27f6839384aebd157f50378d7e7a93225a48f1dc62784ceab0dd381265a79660318ad65278c296b2491d5d423cc45309fdc769c5966a4f4bf243d735aa5d70b822259959d2fc691e454b9a877afee60e9cd9cf0e569e74a180897fee81a8cc37a1d0fc899a200121387a4002a6d0a16b4ccee3982aec4ec9870c2e2d5784052a1628861813264010000000000000000000000008caa9f76d0468ffbd1193ff802c2c2a2581ce996b010aac98468a4638a1d9cbf8dac78eb71e7621f794fbf470975ea3b534a9610c13914831364e10868dc56acdfc5dc08083734dfcde57d5a600249555e9e048ba1d04cbc41f3968da7aae1790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040cb6df9cf5ef0c92323ff25d9e89c67f8197021249c156b27258f942f018b18c999373e974fd7a2d47db266ec6b1284154c9904d6aa240e2cc5733deacd49e177b48a01484aa965aeb74d20727aa553f42f993b77b48ec6c6d4a1d93207b6a2de6a16e9bc2aa7af1c8139afc53b91e1b9f22d86645afda9c5e1ced008b5c2420322f88cbabc67872a6c3685b6df801ab2f9896883fc667ae4954e492d731f2e1e0328c442eb23a11493ee5befcf793c510a0a1accd2074ce8e3ecefc8022f3c45090a003695335a3c4a4ce84c458c5cd4a3d58b98ad2febbd2380f080570f0b3113782e27084275fb6c7fd2ae2b3da3eab8ed1d1599fe130808f67714c3e1ffa1b70f03091da76a39aa2356300c1a31af2ebecba83d4140ef3adb254114a0b32651c3ac0212d7b60fcacf37cef5e5f722144592eb1ca83464660ab648cf2c9c920ba70eb0031141318ff949a8b0c9909f1290109a59c29b19c0b6eef7398eeffd8d886612fc030bfb20eae0d652583e094cd54c17b3249ec30dd9a9f7ac36ea4d04b5b0b7b0bf0327ee68f5d1f9051a9fb1502cff907e8cc14d1198ece623189490dc8ae5deafbd91420b4afa93ce75450c7e59bef0026e978276b42ed9e1cd29b635184ccfbaa651c6d011918fa214813178e751dc32b8446a9c83a1b0f9104c929ef14a09bbd2f0278b3832a1aa9f82b5811836c83d8002b9ee10689c20ea2a8e96e7f01124aaa4af3610f4ce3ecce8342f2e1640f14a7fdcc74b0714902891c547987fc7e8045f2a645160f8b85317c6445b132738f34c10fe16719e914b786752be183298965e395c403540f1004a89be3e2cb346aece44bfb4aaee4a1a6d69b56ef9186daf7ddfbec72ffe9ce5ec8e80b9b30759648dedf5df73aaa0c610d42d5cf8d8b5df9552afdf01440a68677ade687a4bd11d2107f88358b98ff4f1e1f311d82ded364064379ddbeed041cd1ae3ef9f53e60ba1c5fd220d61ad16ea35954ba79898d09aa18c3a21d61acdf32cea652bea9575e31145b95bd443715b2810271430a2ee5545813c816026b55ba5c9311b354b7e7b7e5489ef1ea25c55d866a1cf962a6bcdf3831149407f7baa5132b01d51abf4492ff169220a553f2a2c87330b17d835ad18ba07a6a38434d801fd1b0bffd215a5f1849e0026d6106809891daaa6a1dd99187a692badbfdb1cbee80aa4788122c63387576362f4a66e448751c9bda6ad8375fd41de6b88516f4408a7009880cee25ca09c1e735d4bfe91696aaf5d747a77fc4d47b6bd82d6ac6854ec7713cf76f40cb9c17bf12e9cd3f4ceccb1c712dbbab3cb56d4d34227e279aedc7b4a396b03c6605946caf46dd8810005132b3f5a0f04141118619905a1a0478ece5f3a21967654093d90f22a98af45a5c58a94bd47f4fcafdd7f91d20d4a73e5d72686a2dfadd5d6780e797f8a990cd6712acd6e57b0d89c4dd94a810536d192bb1f54baf587de94c84080b63c2297b0c5281f4a0b4d233e62ca00e8b0b1b7bc06e2785ac0c17dde6c9cf3fd7524374350be5fcc8f651e0f9e09e43fb62b5605b597a87da3764aac218d981d998454da870ea02cc7037eeaa657abdb52f682a1fcb21b6a87f54d86d37ee21ec7b1f22f4731f34363e83f779be8ba458bc452b8266443663f47eab329926a8d7a001ffc24f0c9aa78f1b381bc40a170d3e8c43ac110abd4175d086eca4c183248da4489d2848ae1f3e90c789d04772dd535ef8ffb97d8eba9ffb3165fb5bcb51b034dbe7432eab4f72bd02aefdf52fb4740f4deb07b08a223c36b917149990688aaafa212ce3dc84945c9240f65579fa599de42b2523f3fdbab036e35edc544d05ec217f1d7fed966ff4e7647d4ec961b9359acc76661042221b8a5a5a5014002740a7059afb7f884f5015374b95f7ef91b340a29b0c1f8ffbdce32719d9c35cb650d5ce4a49bb9d7feb5ac42bbbb29089d85a1ccb312bf7b79e1566fbdcee9f48c5a8bdf9487dba8041cb35e0eff4683bba440998d096cef9fec906b91014c147f3fb21155a2f0c6c2e201f644552473cb317ae495539abfce95637c362e89f9994502dae4fe4d09e112c7aa7bc2dd40091f27f11eb700c216b56791bc4d36602f35b4c109e23e1f43993f4b8f66dd8c9c26556df9316f3467d502839e05c31dce769c353a426057b7f5a7f77fd2cf4d1dcea6c81ed4a93ee53f9a6f5353b3bbc672d0481fb912ed17e74a1b1cd9fd221661f37e2672d34bdd5721961c76484393ac9fdc6fcac2ce8b70f9167bb4782211a84fd852360551203bff0a9db6151fa1e154cb73ded6c8a89eaccfc26cb7aa9ce29599302f0d34dacd8ffaec8ee90d189d49fa3fa91cfcd22b382889fa2f67b16c7378db24c33ce1b953699b3071f8e4b7b41132978974e355787dadb22059f78b09cd93f2608300", "006a", 0, 24039051, 1537743641, "dfbd378f65b1d3db126d57cd93cf830992c220ec77570a68247f184e7ad89694"], + ["030000807082c4030168e495307f90dcd30c4409458456cb215ef24dae80622685b8a30edf0e12eb9602000000090052515351ac636a522d4101460197d94e030000000001520000000000000000010000000000000000e216e00100000000951739234bb641442194466836cd0cf61502bdea402ea576c97814039c11fc598e38915a459f7315cbb8f7bc2393556f1bbffcb0f462463d777157ecfe4b54b4150fd169ea4665aeb424fd6b2ae0c61312bd955efa171f9e3409b0d1b15b72d300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ad7815d28310be564a680bded1bda94be2223c892cc85b69f9d22e82c2b8b048cb992e283b85135d6f9f235a6043dd6ffa05f7d6ec8f466045725c0663cd9dc8868b9fe6197243ca31fdbc665db66ee3691152550b12c3c28941a6180568307a84b1cbaa3d6b0933e6cac1594b7c170f4bd815f46b9cc98f887d6529ba73a98d022fa6aa3cb945340d4642a208f439c31a7d4befe773eac07785577e544a53f11c021cc2a8d39e29889869c32230698d7bb9eaeba7ae2d7e5338760a16159b84196a0b02297d76ce22ad209327b15cecf073bdf81a8c1ab25c6ba29cc1a7a5f951c186a7beea4cb5b53885528b93c51b142b872171a0b73f22295ae34e5039dbff2e2e021feb9914dffdb2657543f53e1e2a011f749c9d682923cd462ddc8d2afe435e600228eac5bea68a8dd3a4493c0e61d0504c08d60d8563e636feb788d1c24db04220032878cf11e89479d8e211df64fccc09ff7fff088515bf622569cc6cd9a4bb3cf90213e1243a2831327bea1a4bc122f244415aa5401d55c3a09fe1a069565a94a787020154778107fe44bf88d963f3329dbca06cd9f2a85e42a964358cf9a1f8095e1a65de964652d4f1df0a4078a524550f3889513d82fd18972ff7740dbac9a02e00e6f887e022961d6a88a2894f3358ff3adc1b2bb8b88da5ebd1f592c9fc78bd46c23ee92dc93245d1cd45931f7adbd7bff63d0f6c4a4ac05d62cf5a1937ad41765db98934db88e4327decaedc32e87324cb36a9a5b811d69fc1cc88a55bd7193303d4b0a05e81a6271f915b8c32df26d609f673fa22048a2a11a2ec03653ffa7fdc1d1f1d727638406df6686f78255a499675be4096b28271346852ad3e6a92318fbd324ec9eeac8d2dcf1b2bcf38d3d4f85ad53755cf527da6d4d64226eeaecc97e5ff092efab0d00852b4211c5f7de8e7b4e5abcbce6f208a5d510c0def68dd038b834f03bf77147f3db0cf5977c5ef181ef8008117dbefcfdbef56059d60a578714573a253b93bf723a9cc0412e56f94d8f6c6f595de1613aae5a495b9d8b75584664b18ee7b35a1e9b1a9727f42818a720712955fd8ba3bd18c546539d6854af7ac05c3b9b3165556e46b9bcebc0fdaabba56eb15bdb49f89f32b5f251afa4b1bc04a461ab925ba07a42c430f262c82452eb4df6efaff623d24c69da22ab44d84ad0769f5216eaf8153138e15a08b515d7804f2cd48f11ab05f335b1fefba4236e08a54fe9c44e31155466589fe1e9f939e276505feef39fced7294eaf4586ba9192769be24b973a9c71f1cc9c8a71a8a9a2ba92ef7bf291039d4e59b5a92bc48375c30e29252fa722511bfa7c1a89ac07913aae09665c5251124eda6b50861a41de213064187f63561bc853380ad40d6111041d905f260cc378abb421ad719154c6074856805b4715d2586a14a6e15b0ca76975d913a14a9055ec6905dbe013abdaf3f39c67f73a3239a5e0062d867f74feaaef91896bc8631d26539ee0b1cdfa61855de6810ed9b42b5330f7bc2d1d7ec5e12c1fa6763b6f5eebfaf47cf3a418a54774cd931f20a92ae92bf9299b166c5c0291774029b03d5bb43a24bcd53a01565d9471800bb8513647162744037a124c8bc3abceae1c4b327c369674783436a53680731e1fa0c792d7c09535f5e57e254ea380fc8bea90368265f2069d233f8e49e5af390eea63d52bd58de49f38a1df81fd52d67fd3d840d3274f0091171616632b88d2e1fca654a54f1bb69fb898425f91cb76b6a51301ce6cac4c60c5e93201d885a6fb99064a10322566b0c95a850fb2a496acb318d10d9b3475d34238225553b99439da3c99b7c4628810dca68292b7538006a9ebb03ecdb90fcf82a974bdb4378d9eea8e844e0ca3f58e2afd010b46f83d4e06ea91b051f3a0aca1672305f414b2d354b9107ebcf4c56656b2717de76448d0757816577789fa0604022d39c095e6703aa92b43ea54c4cbf8cc3b89258b6ed73fb956151cbe16f5286881e03dee4b92fa4eeebbfbbf2a77744795c5628c9b741bdffb2330526eaed91eb3817411c0dc7988204a0f3b2680a95da515bb164fd1e6cdcdcca8c81bdeb971d5e5e423de76bc803fb64426e82600533d09fb8fa34d9866ba2d2b051e80731828fc609b92cadfec3e12fc0e36413031d576c8b8074f10de1ecdb4ee30a9d07bb248e1f21bdf1ec263458a6aaf06f5259542f3e37f729773e5251b792a249b74d178fc3f3eec7488179790b3ee4a0c434c7c25cdfe4b611188465a80cb60a590a95936984c23f648cac128fa76d541b2e03724d60758e7de2e3ec4ac31dbfbb317e8cc607359b8ccd1df55b413965c04024c428ad0005a75132ac9b0d208f22bc37a5380ee9034925a339dd028ab20355dbf726fd69e2ebde773bdf2a98dba732a8473437fea038b65da1fa8b04db0c", "5100ac5200", 0, -382385243, 1537743641, "6f78622c386f920336b64d74c2aca1e5bf208c968a50cba9473d2d176c0ff571"], + ["7afc9c490126885824a337877ac2be4d53f7eeb5cef0d86a749cab7fa2684b9be7848b28c70300000006535365650051ffffffff0124df1304000000000253650000000000", "0051ac", 0, 1082198691, 1537743641, "1acbb63e79f31de178407ad92ae4e8848cf20413ee43f937e4e589d8c30f5ccf"], + ["286cf26703732379d143f5792aeec09dce0e05780dbd4a5ebad11f132b054dd62b9ee6f51e0100000003ac00521842bf3cc1bf3ebc4d698f84f5b3f2be93b7fb0f31aaaab33262172d7c5931d6320e34ca02000000086300516a52005353ffffffff9c38f96bfa2527fcc0e23b1419818679c0bceaf4b734ff7efc802f452b3d3f610300000003510000ffffffff04b06bd00300000000016a16349d0500000000046a52000030ba7302000000000952636a5153636a636508877d000000000002656576e7315b034eb89501000000000000000000000000268955c1189f71fee5d29c813be63c0229ad79d8d806f4a902e5bbdf60239b0831abde403fd42b766d2c85a388e652342b31a0c51c6e642bdb47dcea610859dec1a9b07a1b64a122b5789eda93b9bfc4dfbd07f1cdcfd030ac30d02e5a9731c1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009222882a76081191cec9ceaf4a22cea62e60ae9a975b84122eb97de933ac41365a1a0830a5fcd3ce42dc99ada247a17d1ba06bcd172519765f612bff5134fb0e942eea9ed9285052fa17dfb2ca6558fd20250e1b40573e141b324ae3afbac94bd647141c86e48c79b743d2ed5e6329e15433721754c155920aba3bc3f481791c030d83d6db2b8f60d4a5913def8000ce97ee7b3e0e6415a2470455f81063433122031161ad67e85450851c5efc196511ff6c706c6bfa7b5256e2da4f952ede6e45230b07b0cdbfa745ac3f4875f619037a43866f5ea7449897e57314a3da1d6f5b1c6338e3734ada4b4fb419ef14b514159a9ba8be028fb5040a04710d6a629a66d4fb02088dbfeb0106484e75d8fbf4cfdb3d1d1a0024575480ce1d9cb373c2c5892ed20207310c952c21d78734fc158c90640271199359d2a01a423a109fc4266f33abaa0323ec7b45abe156bb0687b85f3449f35119de9fdd3ede2f0880783b5151c8e03103092256d562c9528a6fbed2412532ab52e4836c32b24783345ce16264014e2d4d030c2ba0bc0f8203f4bfada06b6d9fc5760963d02ba8d9a7af0e3e06b32d218f72a13ac4ec377be8f10bd5cf2c4f37f1a897e05a6f1851e2aa64fc8b6c4957460910473b7dbb7f1852571f91dedadbab52ed5351b0add205c5ee76800c98221afae017126315770e239a30d7968163e81f73007d7191909300d2fc29a9944a0a8ee8170a54de3029420698a51d6a18f3d0342a1dde8d3ef781b3f9e31cd83d01baeb9a8973fb9d20621d7a97f8afc188ff2992ca74024327678ee7a48aa1bea2b718a34557b425cb9334703e6fb1cd71f322b694ec773fe14194eac373e2b567dee6d64c057f2c200f8f21a7b7811607f98c034ca17ea331b1c70f25d15f0cae261b71cf74205e8f543f334e14f686c3a507ae4b47fd9213522189d73e54fc3369d8bac2e01ceae1b963728f6d9fd26ce9c681493ad61ed1642694a26ddc70371a7919953657092c90d105ec62ac0d81e15b498c0aaf890785a88eb6fc347e0ee08cd2d756da38a4c3d873ab69aac4ab01547035b7074e384bdd3fb203609d491b801521ecd3d642251a1a5af0d8c6f3df3ccc94bb667235fc55cf690caf242bfe52ba092bea1416a355441bde093df23081eb80b2fabf4bb9ef1a456f5e91512849402df614d421ef3ea30635bcbb677eb6d1275b1a524fd1c44306b9553bd618e6de361d677ed6872ac5dfe2206e94e1cfe33a8e59073ba3b44a00a35c09468aeaf12e18be3a6734ffbd22907f00b6b0e02b6cdb69b191e4535317074b26a3fdd68bf7ab1b9f4c3405609c6a75c63e41ef41d40c999a461f332ff871ad53bbbcdd3da1bec393b494b408d33b43f60e0668d70f22adfb0cd79882b3f76a3eae0fdf356e6989a10920429a0a565d9a2b145324ab6d59904d32efe7592f90d335d3f26286c359f5f1627e19ac0154eb34865b7fbf7ff45e2adf23aaae215c99b7584f0084d11e1807215755af5bc9d885db10b2c2b5636f73dce622d5c7f758887cac5ba9f0852570a9f1ce38cf38de5687386dd48681c796e7d1ebcdf235cdd3ccf8e96477184181f33773002448ede2ee8918918a82cf089367450abedd3a648cbd28f7b11f6120b5a5a9729ef8180ccc86d80e4d7ff491711846cd80cb3c9fc6873b628fb78b5c00dc1e04c843a5ce65aa80d77908f5ebb405fdcc7b70ccc0722637422ad23c77abb7499e94d4f0bc0c0ffe2db68b39a251922e11e0f086fc93af5f6c20730eb0cc57c8d6b1efd82f97e67dd5a92f11e269c43038405f1c8900cae5cbf54afa9312278309bf18ff28da44ac082d2f8d08806c588e1b04de8ce10d4d1474a5cd638f0d06fbcbdd1cd849b55f28a8ebdf26fd74aef59c95a6c175fe7d41b1a266d8495dbae1667d7dcba5a533f6a6ecf82bf3a6b33d6cedad4484a724a6a33a25a87ccf2685831042a0339962cd4a95bcf1dede26fe2ba4f480634a2da04c380f1c876fdd102f3134bc34909926de96133add934598e702586aba8222a09c87530a9161098bbb9cde054d002facb43bc4205f976d78503f1b0611a57d5e24d04527a8176bba2104fedb04d6b02f392a28c2d8edfe3545939298a902dd31e5af9eafadcaff1e111ebe18514e7ba397b1c889de613dd44ff18225fd7f36e5f7cc0f848b0615c44228230c2969fc20b64b9cd1de001156e4b691836db3cad4f91f0616bceaa9bed6cede5737284635d04f429535677d19c3249083c9ea170afd07d6faa2842fed69abe7c81a89764ba3160300000000000000000000000075e7b84269445a52de6d0fdf59cc28afc0289c4c6a30e05c18e9b288a2bdae70a877d8b842e42e73e9a8881848dbd0cd9a83b7af4344cf547344a2492e846a779569ced4920cf1ae28d3447af7bdf1b5d85208351d62fbf2ab7e36ad7022f317000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003663dc0168b8dab5f2f0987466a4241e6540488f0a8b1858bcbc282d3d2d45b23fa43115ca5fa0400f4b1ba4cda55b509bdb6e06865700ec2d52eecd928cd269444779d4e1d0713a94c285dd66bc3de01825605dd9073998625ffc1407391dd5fd830deb6a8646e49fbdc79fccda172a8fd584e609d850a6323eb7f4ec520b640227e65e990fe61861e9aa08eed05f93e0a78e2350fdc416504d65678158cda81903115c6bf684061be787a4aaf60d3f168c8b33a0df275a3b948a36c68c7509d4290a037f9c1b3deb4b30255f6b4fe05ef83921c1ffaebd833fb62bffe745fee6de9b130b69b8b2aa725f3e5633d4b829d59f686afec88efcf6e7bfd13f3b3eb0b422032c2f94e76391f2b70a3015ff6100ebca0d720fe8be317bdd9be8711b7442df86021cf4535aa2ce2ac3361c9c7c435e10985435de9d7781297bae3e5fbe58a58d30030b81c2f01baaddf99aaa76a7ed09fc9b39794d1ae07a4c090f800a2b5d9e4b410325f1c5b1cfc4ebeb6d80b7c12e5f1c1b27fbcca55987c0591b1bd6cc084fd703021c51dabcbcaa29b2335d93f8f30e71509692eba5b1df83f38964aa4e861f1ff8cb0653ad09ebd5b66a1a4c0b415980fb790398c30d745f9618ef10b084c402d30bb954de81979dfffe8905c0c6e436d71ce93ec886520c1a261bb5ed68e5218be0dbaf2380fdccd621dd8214ee474cb286ba6ffc23ad3d69493d275e08c11105f040fc19e9ce8289ef8605fb433ce9a226495a465f7964f855080ac118d6676371a2bbbb2b4dcbd494d7a20b79d4f1947881ba6d5a11923110aec41a5b6d0709657be207b8bb1c1f28aed1bb894e7f0eea45d308323482f77350d8ed360cb81eca8eae84590c4acfc1213e65ebe6b856069fddaf036ad7b11c17739db89eddeb4530d32f7087a0b9d0b45e03965ddaff325934e4e0fb7e8da3a54ec180698db3cfd3e6d5bf18035927c8bdba08142f285a346949453192f2c329d00de6f5be124b1e51f0aada80931533bb9c23c4944526a2bc56c444039b24ef2231f42e61c5d171ee3009681615ecb47db6173ff38dc7f04b0a8630a50392416753e7613cb86e6e06ecb7807e42324f2c295b12840b8164bcc9e8e3a92d66735096690ec0a1b76239f34c0f25c6f7171386846ca70b16df28f3c6586090de3603ad2101d4c15862428241a34e816716464c3ac7de119b5eef2c39321f5de94366d078113ccac220c8c8d5dfb0fc2fb5d2007469a1ea6b2a1b65aa7fec9115ade7b74372d1f510df6b0967142be8904642bb145c91ce58ba2cbe9fc77549f85596fd479234bf24281dc05e6c749cacb031de158d82420c00db969392f5fa12c0050546466f990c5b6b884e70d6b8637c1e9783871aaaa76cdb11d2c6caec227813a6bf67f6650637279732121c3f347c69c5fcc6338802f6b8dc3dd4e98e98ef4b10cf22e98ff72164dda77593d268885499cd67f404794676089a49dae25c8e52834af40df3f53aa2df8e7da488be717b7439936ac98c40f6d5965243622a90e9d4d3fd80e388d4d1f7d29c774bf2d1eecfaa4d520c01023b74b095d42709d38862caddc482ef0bd15d4219f663acbd9b3c64dbedd02dcae47661b2b3a3bffed510c4aa70b88bb77d4246ab600a20d749c8236ae0f4afb25829e9965d932006bcd3963235ae40c1e52793cc78150a42d6452c42cab4c6cca68614cf07a6deacb8d86153e25d68b12f740b74089f98ffd94eecccca5397d2117570829f4a304ff3c2b905ebf87db30da7f911bcf65a5cd3bc0b4fcfa9bd6f4cf2e6478a1c9fad3b1b36a61f551a520d6686f6659fb58de9c886b603ad6a116df231d5daa481bbf087fed614b530d935382b734ffe8bdeda11b926222f92deaff7f9bab328ed5e290ae9bcc04bc1b10a65981b261a716c4f861f744de8e85696461ba0bd29d01c1c4d97bd30e132e1edb92e1254ebd6e960cd5491d4fa0d6b8dbc1142a512db51826b1f99252d024df7ff0aae4651a913e7945aaf40a1527fc62605a4de2baae5b258786d0894a34daa35161cd158e70f96cde8eebcbb1c4c1b4a433fdb7e307412b42456d3cbbb86ce49c8db99da5aecfc0b1b3643a1e9b75b8b018f6b1beb29d57d7e13abe753c4d733ba2a758d5e1d4aa6466ec4762b04223209dc9b78312a484d4c211c13e0bd7add13a4d4cb44a032d9d27d8a9dec5c8c3172606bfb1333e4966deb31a56b01c3c8c89511ad7bff15de82ba88d6badfcb43ba7f23cf06a9c7e30ed79678df593ab1b1ef8b60a53deed424333123444500000000000000008c6eed03000000006eb8371f2e21ec4ebc600402df9caa89bec36d38417fbbaba344949f96fedb864cd362f408fb5858e32b40ce15a8e4885cc43c96228ef7e2218928e3cfa9cb478a1aa8fa432aaf1892acfe3adc5a11a524f81b58b7606236c5c6fd857bf0209000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eb8712747c712b42dcf70ba558b319f351729fff9cf4c29f5e63347abbf1c37cd80831f9fd93dd65bff6f330a2d2d3b903769b398739f5f59035a1710c8860b328eb5f9ab9ac30e53badbb074202ff1d0411a7eea8fff3b4f2d0b6bfd3dd9c736ee1b3da39f1ade27abc0f8a450b1297daa2867092213ed0d5659a5e8dfcfead0322a93007a79f5eb6555bd74ea2e09954da1c071195e8474d68dc85fc66aba6c0021d338f6bb12f84873b97f839c0851135b2bbf74f57acc2ff9247c63be23204080b0266ccb98e301dd88af5c866721ffd98d40880546678770c5f2672faa14ee85451954f16412a2eebb6f71b70a1fac4a56cb74cfaa43abafcd7988130e54d55250300e3738b9786a3765a41d3846a8ab0aaf6b38610567d3b90e944dad6bd7119340319ccd0965a8b696059646917f06ccbc32f371a1580303fa126fb0384a700ddf7022409c7ed31bd0df634388e2df41778a20397a593ffe45c0e19d779ddf088fa1c02222c7c76c46e9b13c1f05b81a36fd5e9a77601264f99b6bf37b9447e44ecfef002108b6bee19a15bebc322a33ee619966956f10f8905ff48eb27010cd66b3686d6f70b7f4b8baea24b90a2075ddc4548e614af30ebf5d6bb4965bb3934894daf2942134147ced39dc72116910e38d4fdb8ea0922e6156e67ffdd9e65380965fc4e1beebbcaef861bef562ad0a1ad9647fff08ea3c1f9461826c0e6f1f241fed553c3d31026913e594a1a11ed9446470657450abca73cf3eb15cbaf7b64d496c6d2f30e75335318204479044ea555a7654b7e6a04bd8a422a7989620a36745794e73f85ff6256d20d498a1a917d0bca0b43e24cb1356dd6410c840e9cc0cbb5f09cd4d6dc34072d5edf564cc431ffb2f9e9e55484afcd60914c389f25f0ba3f46284f942c4ffe65a3cbb7ebbc5537c76582604ca13409588f9d81e5872a32f668d9d0de4d952f5f2e296394c1dad34ff6ee80cd1947d887a1db8698d08ad3c69b3c6bb0e66297a6e4083f3571a1dfeb40cb1f63871497097e6cc2ed64e986d60940ea7c3e1790f3c203a24144646562a496f5f51d7209d741291b0e14dc8d3c0b2c99a8d24a80d65a01dea4e9ef98c736d6364534670f4f11c659c03438e890e6f1b6b8b46bc1ce6e7d454a4e55c49abff60cf52d8f06bae53f40ccdb1fccf5bf84ec5180a034dbdef59527ddd47bd41d0b1bbc261e98c5fea4b4f03e3b2168f3d3606c7d12f87b7ab468438c64acf08520d63a0cdf29ac3d520cb638a1bbb3cd541b5295d3db419669fe3c7ce1a0676f1a2410388bfb0deba65a3f1caab56107344e3022afb08fc18d5d3e41d8f86b991479bb7af65d6d0f54b09316af120de473fe9a5a5cf862a2cb85a60d93ee0c95d60e60ccac06cb0e1f5f90ace6c308c29b50cef69b2df0007e188d4854d09d35d4e9347ad28445b515411ef280c50a981e2e813703b1b93f211d7c3b999dc2db77e010f4580598aa46525966ac090c80b91cafeb9197f515e4edc59846dee1953b2247f5aab81c27ea6207303cb128ce0b87c422220da48d03b9e8206761dca0f8ab1344a69e554dd2fcf459fa3ca068d69104ba446a3ac12a97dbb0062808d3090233458da254b7ddc254973df5b79a4cccd3049e54c46cd65a9097714c08ed21a3daacd6aadec61d1aa5bb0f333bc8c82f3c155170102735bb9a61f116cf9ca78d6e83c1e12d100fa1a13d3b282d799d2c06724b255875a360b327c3ba187df81378123b6bd9c8d9872ef539ce43c73f6bad4294b9269d9516d2f326e89558b0905eb32f31a8f74da81a669c953ea9058dda538fa1182e6d45474dae5b0675c4dc32de9dc3324347326d86300b85c1630cd7259787f4b6d68b8e8c14a202f03718ab2222a7b643058a0cb2e68f36e81cbe38a28e1b5d29b393d4d847e884c8e420efe58c29ed8554a0353e280f574ca73c178b257e3caa7a15335035faa2eed8baa35a62da4ad13711212528991d36fea0e5de647c0bc8167a0f5dd04f0359bebebc1e870eccc4a8a2245c0024b10e672ba91143c899dd4c5eba8659df479b267dfa11eb28d4c1fcea0d3c726932e8c725e54d7a88f4c73b6ec7f7144ba64fe51bbf25f9a14a9560b174cedec2c5b288ade00d52e89908431449abb44ffa2734b592215fd1f675555fe5be0b286d04d96de3ac6e685a203b21c5c718b0fc5e81d80befeccfad049f52766dde1d5231451414e5ab069b31ff5c88121695c57fca84e0c9d55bbe0d966c3f963d14cdb5d175092d58ad13265f27f1c71c90f88faba85c84dee9bd2539341f7549374e2c493209838b350a2822497387c0ce3445e845489b7a0e00ae410ad21bd7f962554d71226d6706edd0b76160426c1a60a0adc127f8dc565808d6665ed42215cd4a267d2ee05ee88083bc07e4ce951a836afae204", "5152", 0, 2112136949, 1537743641, "eb92ffd5503693134e31a5c6d4f0139efad8cc7798a65f3f8ab3d5acc4a5d5c2"], + ["a380d23303a1263e6c4097a36e4927ce641f94b14c72a0533213c176eb8411bcca6f3eea5a00000000070000ac5263acacffffffff0fc105684c6760ae35c7930e9714838ec9d09879cf05db5e8178e1ef89cdbcd500000000026552ffffffffb665de3fe26c627b49fd90eb2c6e38cfeecc4516353c7c46abaa9c30d8998e610000000001acffffffff02fcae1e04000000000753525151526a6a2f7ce10500000000020052df167fbd03000000000000000015ce3e0500000000499fc721307a576868511138d2d4c599d2887441191b5c8fae8eff3e30acbf13be0c4dee86762c49d52a575240613d7b0fbd340e55b7b83a408ed7454ff4ab2abb2e4b734b7178f8ea9653dd4b5bc0721051e9bc6632681e94398de2e825e660000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008007ec8027bbbfae7dba3d330d1febc500f1a591c08e536991cdd041ffc1a9c491bf0443b1712c8f9aa42c36546af1f94460df76805730f890e297a9ed818eeada4926e3f5b73553930134ad6969476d57fa98d80842c522b9a0f75bcc522ec356626dfeb61bdf2fb6a4e8ed7d32d84851d9f05b109411225c144949774589a60206dd79c4a47cc21f945f4240ab9718c034c891b1779253a4325736f743b95ded022f59cfa0a8cda5636bfe9fa8e5a2bb91ef839821a1988e86f93ee66ca40b5cb40a033599d40b3379cf15766d12af4ef382c77a04091affb064a009daf511b80861bdd862fbe73dc083c82f4de3bd6947e458af4fecde1889477edb174f9604ab7f0302e415b54ffe401bbe640e8bc425321a3f1e176454a2d1ce34c25d3d2ed80497032d47eea0d810cdcfe291fe4df885da23051127545b7d83be651f9b68e942605d03196365007e0b287804dcca9118386e7de42b84941e315775a894ee72d8c2deda03270d10ed68f8229a466148c44b79f53e7d81b2de7393363b41aefcd259ef405303078adfcddd66431b19bbbe5d106c544e4d5e1fd29cacad27eab2bbd073f44fd8b2f22a3fc03597e936fff8105421c25eafb72ef2b853ee2cf713447609d2fd46f1116d327aa65881a09a1c4fbbf36472424c4ef7e1fc9d348569ca630fa2baf7b41ff5e684db1ccc0ddc62af2186985f7735900d86b9abe97f24ebb6348ccc663327c4aa303f1eadcccbe6fb30340606d9ac479245b60630b0b96abaee8365ff9fc4c965c7cfb2e273706c776ba7d2099831aa330b17899d11f935e5eead0da3a6308de171b5646ffd35f64a2c60695afc836d6234ba8cff371077d961a050b10b97347c468234e22a8a745c9842a08463b585cd6327d07e01fc99975f24f3d26444139d4e0ca29545364adde265cf75c0d82a83da717775f63a7a4d729ed73615afa5e381252426f0ab9e1176ab1d5df564f97c097e1edf586a2f11725d18e7c94ca1aa9a801a3ee4b5680c7cd1ec8464be8d2ef98590bc557845ca2b071cd61a8493ddfe70d93ea0a2337f6d0cfcd62a3349558af22f8341037f7fc3275e469142e0d47f4777b62c9c989dd99e5db4474bb8a5b66f11c579fdfa0af046600006deac2a3ab0547540acbecac9633a03856310b5901d68e5cde68255f25ed05aaad822f50f995050bff57d081259c52fa5c9596f272f2f181384d341457adc7494e4770c3d936e3a1dc4b6671c0573caf173102beccb37e7619c0ef94868f82cac080fd75bfba635b52e3bbcebb208499ad71d0be252265aece1fa854b74bed89d629ee883b7463ad9e83903694625056669d6944281f24afb8971fc784bbda9fdcd0251059ad50c55aadc68cb35688b13da8a4af6cdd40f11660c520fbcf3e856ec1cf31880377b1ac65a547016d1841a7fa5c4bfe93445a61b6737b606e9c5bb140a72e81a75207387a7deb57c21ad8e5fdc2dbecdb99745495485ca5bc1b00ce093cc3211c14c2bb47b80b51b96f0e66d08e79b7d763665d3eaaa292cdc0e0bb80912b8665a7099359742246446a02d6b2c116d3585d084a2580794843c21313990429dcbc55bdef5feecda61de28c0c04211bf9f3dd92fb1985a38a37fe335080cf302e2e3408308fcdd5bfd2da4f2279155cfe699cd0ae8295559bd23004b071498a85fbe3db0b9ec600736e7f0b2f664ac83cadbe7bece883eb801feefb809e4f8d2958f735752ecb82c1dae13e432d4a45bb1e817008bbd1bc79c72bfed982775f8fc16845275c1a19c140a0b8875963a427439716917af20b38001f6569bb5a63a282b6cc499daea1947ae78a58d7262fcf34e3f216517c5e52f6312f66b78c8bcc7c0c651643105cd18dbaea78c3011229249b4fc32a802073704fe4d4bfa779159cb9d8f6f2f4cace04af508212d4edf5763d88905ca0c234d3193499d9442f7349fbdf1c919dfd9ef84b463d68a1e54dd27387998e809134e2b3b462e172f6af584711e0765572a2a11318505e3a2ff13dc39fc31d23ee05c4a4482dc7bd05610e73265228c3e11ed069b90b5dac09136336ccc1b1fa20829ff765c30c7d61e81776b1efd673ef1a4c858d4ea9fb748216da1ab56a11a01b32e1751067b19ad914ec5cacd47a8ace47d55ed4fea6266ceb1972c34b147ff8b1d35febd359601a073d815614f91d565401ed95bdce6cb7441dc301d0a9d1c45e6571bb08a208aa6f9d301fdf88bc16f1abbabb0853ed6ac64f713f2f1cd89c2f4f8594d6487d1f73c9ab7d14e1b630ca7cf45e6f829d002000000000000000000000000cfa69122ebf3db277efb83027ab3cb1363862099525fdd0edf427b9f32e94492049b8891eaa52ea38d8acd32742a737efb4b1140dcc4936c116b34e3f6a366bbc7738bd363fe63eb4f61ecf1287143dd6c7a476962084131cb0483b88d89c60b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009ff5dcab7431f82d540d89537f0946a34d9b774ec513188976d2cf465134bea047f02b54c6bb8bb8ad2b9d68645255968fc390d3bc72638b00b1d2bf3fcf83112fee3026cb974ca0b0bb112838b58a6bcb256027bfe0c1d3afe9389d09a100590f6932568917207e7bebc843430fe3adf9e5562be0d7e97c3626d3cc7a3041530203e11410eb80d746c2840c08967a31224918e1c0f700ff1cd55541a8e661fc000304246e7218a19331f42b8dcdea2e40ae6e693f988854aa4f5ddccfc1a0dfd4e00a003799b2de57da7035a73fb7c6d6f497fb29ab43d8f14c7e5eb307101d7a5da12d497f162cef439d061d905f38b5a557e482273c738817306c89d4851cf240e2030029007bf15adf2567e38c70ba209bb3f3c61fbf67a199d579a36782d186c1a70313240bf43523436f1dc15c32b70fd2150e605fa490240e654b28092ef66f9ad1021ba767603426934cffa09462732d1764c9f747036c6b9f412bd3b9568b2833fd032d05f200326f304a6ffc5e257b2d509c915e45ef3e6035852cfbbd7530521a8e02185f48de8a4367958822dbc7982964d077e4a035b552e6163303419fbf49f3a55981ff99e0eda771048af4a038bc932c2d0f86bd987517bb9ae94d040a904f38922c51864c81780df1a23441028bd456a9934e9bc895baab4b312b47f356eb0b1d2d42825732d0b8da044c192fa1164725a8411a5a15dd5a9d4db0217f79563aef37d5b8194b2c066935a586bd7ae681d9af3ddd3b59d9b4164a7f43f34be756a4e8811bc1cf4ca0bb56ef29a4911868c260871ddd561718dad995dc5833f0437c61dbb40a805ec19e07e5fdec12aa64b4b0a4e8c8279707ccca2e051fc9ebf8113ca009fd642e4ccd90a5329280c4a8a93765c6be8e8eba5f534747756f7bdda38130857e54c7d4180e6b1dc90a6713d483d0fa77974002e29b1d1ea4149fefed900347a7a9573f4da3b2f943686449d6ae50f8e2f23ea2bd56f0976d1b6394c502e99fc7ae3d0c4f92d0980d46a4e1a9f82defe3e271d3ca1e3d0477e31717b13613adf44a2a7f100f463420ca2c6164d7dd05cedcb710f749a5f76c2e0c6c2bb1b809b15d04eb462d9be53ae87fe1fccc9f9ecff03d3eb69fd2a8d8658c12cea7027be8a86fd84d3c66ad21bcdee665e1bbce94ccd1447f210e5dfad9a8699ebad77779281022ea9b0a60883d30f3ca9d83a4a8b4fd4cc75cb9b8f72bf656cb1edeece8a633b1ee1a75da8e8ffda1aff75c851ce7214a55635e85c8e6e48b49994733e6e47cde8eb579783124f0c3065dc39b11f97e1c844c14a3a763f73d565b4f84fd9fa428df2493bc9a303d937df1de384fee23749f599c32bbe4d5141c449a95a71758c488255a642c585d92dc9d58bbe1f65d3cef54157ea45a00ba978685f476e14445d87a9e68f08f16f8786e03956d9d0800db27a55a780dd4a018eef81e972dbbe47be7138e7d23e29601cef2fa1de11281cc7b4525eb454e8949da1a71032f78200abc414defaa87ae4e36cb125e911bade94973875d0dbe13a6a2da7a00d53ea39dc224bd56d25a0f1e56a81aebfcbb4810a2f50e09471715abfa89097d67655e1fb66cbb74b6b6a4f8c1fea7712c3d4b4bfc51def08749e83b091b76fa35abeeecdbc37c227cd57707ae11df382a55b510cc1b32191d5b3688fd89374248d697e500e539c9b6d84701720e3fe288ecad75885b8e808013ac771203494c02f87e1b65fd83e630c1cb9b06c7201324e9c9815bcf083fcc34e4456dc97df47bf3495bce1c72242f7db2ebe80aae67347b8879ca390a0405d713c9af04b51ed72e1e845ed6e7f90ea817fb69166c72ed677d2a7dd00918e7846624c28177de12b5f8fefbc1695abfd73bcf9fc243909e9bc91302e879d264d7cd30ead1e141c1f9a13d435ef92b29de4188a6dc329b5538f9ec77f27b70fb7bf03e32b8f8bf345cd44fddda75c5f0f4340eeb62528c86cfc23d624a4e8e2fcec174fca403686fcde281a7edf1f073613c278706458e073c9ecc2b5b942d38bfdc7777db1446c3ef9b2ef025458d3e0c4115c956d271f2737b9a343bf55ef0b713f28754b4cfc864a1fb30499f446789e127da0b4e1356f899c5b8e4e5c5e1dec2be00f9852cb8bef471740293203d704fbf54cf22ff2a906f0b582ed1ef5a36d2931cc51f56094da23808b813842f845e0eefb19c9b96d8d92fa9d59d8a610a9f924a2db8c6526017940619c4308612d216520f2c8927ad9eb0e6f6379c4635798bd765b72fdfc060832e63320f158b5b4a8d1da7ec0400000000000000000000000074ecb911ec13ead00d7733fe9cb84f434ec707f75565f6f5655171621469cbc64aa14ae7f2e9c4aae94eb11669993c8e55ed3c58f04207631ee81670c6079a0daf0fb1cd8d2f52d45f8b5abb0aa2c69955e9d54bda6418e1337fea368b698507000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005d7ad584ba52de6557e57367ead77a3e3f9703662242b96e91b48bf9c933f9aa0b4293e38750c3bf972357e656abe5fc5b376992021001c273d8d59de06e208ee639b3254169ed821b5e5f976f0da2f0831ede6f8e23621356fd49637ed289299e2bed43f030a69f60314bbdf8f47ca63c218403d2e7ae244f64165c85277e77030e4e8da29a8a33f382979443e3b3698a32e4a4429fe91b334613ae0968fa40eb030fe81660a3811b863290c58dc51513bd8b831b134a60f145b9b230ad306b3ce70b018fb34e64c977dc557f0fa40ffb91f0f16ce7b6251476fd3f15eba192ade14b2f6515c6b93e8673467351d1facd311393e6819fbcd761716bdc0324d42f9a8d031e0db017359204a8198479733d04ec202fa1b3130f50f25d6de7500434de5f2b0219c160367e7b96d751c164108adf05d7af415583c1d697a350fcd70c41058ce60206ab10d405c7e28a52a83c9cc60f695088d6fceeb88d4fbf6d54a9225ce678050201fa8b5764fc4d66ec9441846b64e3e9bb101e2921a91b0d0febb15bfd9c60bf022c7516e39220692fcb12ee43578e318488fe56290ecf885da2b8beef7fc3e3ba93d6aef194b2d61f7c6324760bd4e739f33ca5c0688374ec6adc0e2614d1a39b8a7418e9391d3215d1d9b4514c269b9c46a90e4a2ac994e986c47735183cea3bcb741f418d1330bd805a173e0b8cd64f76530ad43db9f2dadb8a237bc4cc1c257480d9301ace9df44d8e03d657568f993e450c5c76d442ad28f23cb021fc16a93d72e85f1af1da50ee279153b1295130feb66ce9a8afada609e03428dd03a18c44cc4c791b3cec250886ee8aa8929269f6ce1c7d5d9ead1c72946921fcb8dddd0897cb095a032ef0de5ed818ff6cb19149337b5d1def75e9bcd35f225542ecb22fe7417a8e6f9c0f933e91607263c9a99e10efd9013dce5c19398b2faf912a26b620398091ce68f1e8886a5f708f6007474e6911f99173f0984d0358f38515ab8d58d72865e95257281c24e5ce6b898d3e87e1ec883a7a7c25f0636e92647a24eb3cf7ecebf3bf0ec42e793a5e4371a63b19f00dc0def7e2101344060d92b12de7330300aad36708e64ff735a9dee0a66e735dadaadd57822724162856d77c05e354e1f904ca87ec4bccc24ac9bbdad9077ae9fba4fadfe891f3515f67c48c1aee9dd11dd8bbe751c12719c5d32a0cce414e23bd72f3c3cb48d0ccb827c0b872e296550dd9f5861bf6bd70aadf406a967e0e7fbaaf84ada26593dbf62e82a30151f8aeecdfb4d443dfa9d7f196f03c7640b1f617d3a28100889a5ac5b68a303e405201894bba1910cc1e92d119e9033e382e912f3f1fc9ee4ecf8220c19b01ad5c5de5b3444c8ee9ca031891274f3e17dc86b699a8026ebda9698fccb8dd9bfd2a7e4675f0b74ed4f79c6366be4770260856f03aa187fc494c8fae881f8d1f01035645747e560b04ed0452c0bd24decc50bfdfbf64f9b3a874435e899466d83b4fd598c3744cc2ad560ab5f37d008225cc84d6afe4bd1c0993f175e10a1cbe3061e24b815e794e22b8208e036d39b97e60d0dd0d529415037970dc13232708de19c6bb30597d399ce95ad3e4ea94063ca347721b3c64adacb044f116a9a8b99b850481a62a20e65e2a07c2ac580760430a40f005a613469f20a8d37749270f83c8afe29dbeed5420005e0d4f7b12d92533f8ba818c5e9e2e2fe4a91df2112b67e2f159cbe1db49f395b69f8b8f7d33045ef0a96dc364c11cc2919b08cf4f1bce9a494e131a18db962e964321fc7851615d63f4de9c62c7392e781bb970a8fca76eee7f475ce232cc8d10b53adb05fe34d3748e217015f60aa93b638f7b03044efe23499cb727f7e267d93c076477c5072d250ac13cad9312ae988b3c5c5d6ca101c7bb66fc97c3e848b2b964affd09d622d289bdac594e6a0f120e5593047c53c9af63a096fef60da24a9686ff6b3641995c42e827ea3bbc7e5a8cf8d517b7859f6b98c1af0163ce45150c4572c39005ccd6462d2ab73a448874e4be8d283842ce6f015aff10ee2f082c0c4d4bad9a047aee46bf86a5c8d680b3f88bac9d4c91201cd2e18e511b85d7159fa7e36b6bf797d67573260227d75e46f567143761e8f6384712b576ed887115c6a1c9c2233089234c02713624ba6c2b7d88ccc6c872181212ca1979fc853e852424f5cf2e33f7f4fb203bda01d19ddef3ec856860982f8ccc42a594d3a5b3d8974756b36eaa1fde703bebd40ab86d328b8a15d71f7abec7a5477a6b6bb880792968bf7a965b5fddc46239729474e53f36681c9631049cb0a45069ba48c982f8d51a018b58801c6da306a8d04c42ae401835a775f8d34cd1f75a9b70aab34a747c480725bcbd4e7d06c925c829c28ca66113f5c04e593e051749d0573c53bcfddb9d1c14fc057108", "6a635163", 2, -564800263, 0, "86eee12cd8433397671bece77beb312f1002d8576a4451c7de04b9ade1a8b723"], + ["b52c6a2a033d1425b0148dbcb8ae5bb2544a07c3c9d9b0be786469b90f27408d0b17f10a1600000000020063fffffffff0c027f56f4cc7aeb872e4465e8586a978bde3293fcd3b79ee8b73e9a18ee47b0200000004525253630e134e2f8ab98580b5ecce51899c9c1282028ac3e3b0523d4b32298b3790b14bbf18ebe903000000076a53636551005392776a8c04908413050000000005636a63650007c232030000000003ac5153846b1402000000000153d578040200000000075300525352656a0000000002000000000000000018a1f80100000000af769b41d950b8dbc56a2893fa610fd6520df6b734f07a0b607783fb8a1a32ff344eda042f4825ab130f4a2c12bcbc591eecad19bac652b2d6c912aa27727dfae93e7455fa2180bb1c0dd425665414f76cfd4789712e5f6801d17820dbe136170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064ef40898d37aa2b129e9a15f74a788ebccfd23d45fc344a15fd2885669899f93aa87cd967570e0b402febed0455b2f08897a247084825a38cd7570edd7ca4470a6a0b626a56a5e663e1bb05d58b27623965c7f941a31d51f9a6c54fdebc738528b69026068b6adf7f125180d4f4dbaf6c39d131e7dde3b68f4e7ecdef2a167c0305c7ed998e26f0d695d96c2a17c5ac1d32bbb471df64ec1e188b73fa8376cb2403145ea0b54e265ee6621a5d24a5607a875387aa3bc13284788d8ee4dca344faf80a03f2f6ed7492593878412bd853051365b43c18a077ecee10791e6f7abc4043603fa080203efee48f24a81cbf2d35864fbfbf8d3187bf72da81a6d846d522112a032335db8ae513625cb73a4774b5d50445f98f35008c3d3e2fdaf3e4dbea42c6c1022f0656be2c9654991d9ad9e139cbc4eb6f8ddc8c06dece3f01f4210ea924b818030dca5827ba8c55534aff2adf13885487a4f74aa9d49c28ff8638dfd0438751bf0212ea895c19c0c58d0e494eeaaaff64def30bf0ce72a1598111a58e1f2842720c0219ee768e76ba8dc9546e56eb78c89607d9493f9747e7fadd0fe26dbe6f3fe0421bf215c258ecae35d2157b6ecd350e1d88a7fe6b7f409633896bcb140d925af8d290a09a3d1b3d4ae3e60a87c93b8e2a3f8f7e82a491e7823c9cca658dd1b70c128aeb60c5881639a36e0701bce475147f10e85091dff7ba82f572c467c71133cd3713843f446e25dc2a07503c49609b01d24592e1904f61f132664d8cb872b9b78740ad2b6a276a838a8bd439c5c776352bba66a7ca31960657137abaca952effa274b13287ff6a9dfaf6647dc870dd4316694a6ab597dd28d60b7225f2d32b8df664ed2ad55e3d183a0bf96a984441af0e74a7d5568fa67cf25e484d356fa7bef3453705435935b39e4189832e4bb1e28bf286df986be218811313ab69ae5a73ab7092e4453878dbc4f427d98bf80d94d18629335abcd27fda111ab12822ddad5d7a931cc40925660009a65f08764d4457c735dc7524ce1f35255b5c379bd087581759c3630e1849e600b8ecd3e3be6d8d1fb90b38b69c0e2de3699a9f16d963493b22f66476f6875780f7f626b48ea140dbb21020b3de5a1d09d3f042e1be3304619ee9bc62b6970654dbc42cc3eb645db5080268d10acda687b9bc7fe90b2e883f01d2e6146a6ebed79b6a6d57b28c3fe1a5160b746d9ff83cf5305a2a13a8d544f0bf7a84a49eb91d36d1be332e0a8484168ecf5d52272a80f95b98a3364464c7d57a99150b3023d2c22f6778587c79a4b0b5e16b6adfc1b875f27915ed12f66a1e5a0688c82e326542e858bb1adbd75dfc7a6f57a579722b1eb8db7742f083f1843a802f4c59d6981d8e1fa6e22b15bb433ecf32f917e0cd1972da85a6d7c13057df7771b721e47f187b2f6f46b8612b40da87d7d5f53b610bd17862807895fa465456754aff040319f63e01011562c831e76b756b97ae64ed1d8edc1de02c55c35675fad9527f5dcfe5ea4186ebf9611f5241578e6a9dc81221e6ae4b05e76335f11863dd05ee818368cebb0b966c52fed82f3045dd0b2165a03146614e9e65f7dc498e611a027d0023cb3e6d262e5259495b0f08633b6b9b6462be4785e397da802b7570dda76febeb6fc9762082da639b85814f7851cb134d77b7d203bfd0ed80edd292ac0be1d1b7453508fbe1e0a1471f5b8c0fda618a610862f45782e7d77778743c66845486f443622293feea20a6e9fe0415d3fa8782b695dfc52568352f0d15c833c2b72e88abfdf38f948c5a8846a61fcf17d06bf472929c41565de3668ae1dd978ad54269064c5ab715dd19507e4fbc91e4f0f5d5b71e031799755154c20d16c83e64f128dcee594f49da6db6fca769bae69c91719791d02cad0e7db09680b2fe10c14b21d2412cc95ba7294bfdd82b64b37f8925e52cc36d72cc514e4b0df9a36b8086baf355c18b0f8b2b121e2c141d11562ba351430000123dcc8c8375051427de9afd64b9db38c19b05ce3efdc74719f9aa013d252aca221c67b09caa782beba14cb82f8e85585949f53240c6320c6cd1ab997ac06c1e83fd26fc70f4c3f902b4d9db912d34a3681e5484076ee543926f0cb3b3a4db80387a457b72659c33c6a3d79f90ad5fc619c5c50a062dfb77f19eb2a5e9398ecf7eff033ce5ea2c5bf55e48ab10a32e0ec590ecc56a3f1f1ea83f71afd2f383f25a2d1d8904d5fb180c36ca1987e01de38f3acde5d607aed7552eae0a596c75524328c63d602026a082f4f82372bb3946f0000000000000000025c30c0500000000bdfc3b8e7905a70a6fe7a79c6858a624c04ab7d3435178fe6b38f83b1e3af8bfbc1275a3fc748f62ec38c371239f2bb2dd590a780d4ca3036b80b39c88115a213f9c8eb3a54708608dd102bce8774076a32e66e4488995c45f4753d057605e9a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000067b578f184ea433fc5c7124952a34b0f14ddc4c42bc3e74c4fc7c0e6a4c72a13ed0d6a1f928edaacd8e158d6cc6bd8d4230266d79ccbaffa14bf98a559d9551e461170e536712a0c8e23325decd439e0dc2cc514ba71713b50c9b7b67109921660c0cae0b7732ced3b8e114ea60ee953f0dee26c3bb8385ffb02821ed70d520e031c6790edc826660917d07113430ed86e5a7ae820e8fd7bfe379b57634960b1a4020d1b7f43ece669f32a96da265bb8505ef93dc8ded0b15ceaa33133c1d1c198b60a081daf709af4d4bcb1987ccaaf0170284d3901d1efd060d7922e747391379e121c2295a3dc86742355996d97c07c3768d2d35a80e602dd6607293204bec4a8b003021dc0f356f1bb5f3bf4c5595613151dc05d8e775c739b5a9750b1529465108e0302e4eed6ecc36f508b9d258bbb5ce8c93763ff120dbc50922d5fe07b6e75a711020a41ace7e881a149b0b831bfd7ee6ec6b3148e2a698d966dc8e96dedaefdbaa3022d53f2c33e2bd288f54be36f1ff444522f92776961c4cf84b993aa9128cbbf89020651e8d9df510a6329db607837d9df090ed9248d2c22f4622546de186314ce9edb721a2514bc34117b0fad90a18857a73f8cc851acda2e4f5db46cda092ecb7852a5aaa921c8acc6ce6c942a2c391101329986b28ad179060ca5db75c04f3b81f4876e51124659d4b2b2992ea276eb80e5a22c41bf87d9bc35be8cb1288a9fc9a5ce85c2cc92d1acb1eeded4e94c24365560c36a8b3d77257c4ff94ad3781d8bc93771f881daa2419b12c3d19f0e0b96bc79e015463ba5a4869a7cd5285551bdc93a8e70baef03c9ab1763408c25c309c64f7c529747b5247db62c94a7b34ccbbb34ebe932cb3a20966f0889440aed8179d6c5613bc6d5cfd18401d11f1edc0d69421b4b9b70439b0ccefd855324c17d293577c5e83a503237e75a0eb9ae2238b2e4bc88e7de3bd8a51de56811659d78d81d44638f4a75b7bc1e9e883d123c9bfe56f18f1b153584c563dfcda647b3c0110e21980cbe15a171b68bcb1c73c9252fa5059a9ee743ea4af5ac5b8457d7f741e28500cf590a9f102a6d500b4b6c8a991a246c5844060703557a87872beefe280cbc6b5f134ab91b4e5a241507a8a111e4d39d0a44b38089584aa7b3b9f6af42c1411f439777688b8ac67a15b446ad834199c0d6f5a83f48117e295fa290c8e4ac0bd59a27959faa5054c4959e58b430e7a701c09af0436803680b081b4b2268bf7c3b2c0c876a9f58b068656b0ed084b92899cdae5e46efaa6a06d8e3d194ca06c0d88ccfd78d9abde92b33bbe87aad023a5df78cd115666db0f6256aa886f3e7e35ba16bbf6ffdb149090a6c5d422b2fb51a4791700545e7cf32e8031d5b1b1b3a18b5ba60dab6d11bc4c24b2c0da28a469bd8ea9b36ff47b48471d70aaa8630b8e5022a2335da27fcab2e3cfefb47f80e970c6a6fa39006ff7c85506061facdb4f3b99284c3458afe6413f8a75056b0eb3b6b3defa54a496ac65ce8bb0201d2760adfa5f138bff074977aefd6e0722314bf629286bcdb1a677fd2dd2ff50e722e4dfc340a069b3fbf03f7f34feb80327af69c19f82fe33efc04e5d2d8f0207729d60df007ca784b64194ed2123a916ebf2a7df2af0a6f7475a4dc3e8c9328f2efc82bbfce2e320654138a8ccf3528806ba364a861f478c86d1fba4f358013bb836e68831a5d7138f4ffffcd17e90a3782af445f0d08ed99d1f5b527642f34dffa02c5c78b5af4628ab37974eb0039923219816c097bd3ac5c84b28ac0689763c942492f23041422eb20bfa474d9129fd62f08d1ee3581d0c157de89f2e00dbe869e9c9c8ccaf84e0380d061b3ef15bdbbd17f84226038bcd55daa8706727ee328c2398e1a60aecc6564b83da82b9e0092877dc077494cd0787745001e0cb6bcf3d9f1490b937838626220b74b14e6cc1f1582f621a23e686840f1fae0c1f9d5f57d8b3d46d1e93a4997381aaa91621199ab0d73e21041ae3556ca51f3324939be069935642b25f944398cc52894e5ba489a4994dee696c247741aa0372467dc5f034c72fe4fa0f40d07ffede8fdf00df1d46d8821a6a285bee97b1f0d93280ce77b60217cccd261ef0a52ee12ca174a189404ec66a1dab9781b00e1232bd8622056175f13092e1882ada4fc1974f2ed597ec7f22efd8e9b2a4f98e16f87ea87fd138d29dd8750ce8faaaf4d390e1914c53686e83192198e8c3299754df0ba396d6213820c6291c28ac1ce9a54c0018778ca40f22c1406c03a016fd930643434a98bdac2376475f8291594e40e013c1d5364254eaff8935521a3fa72afdd5a358dc602ccc48b49accc3320e64fc5757e94f393746ca402eacc4197150305d70877365399b1e7e93c663602684aa4a0dd77e73b54507bd956bb5aab5a54e1b009", "006353536a0063", 2, -852992176, 1537743641, "2e3c42985e6de31106d186818d8f652b4db1d42a224fc1aad1ba4c7b5c631b9b"], + ["030000807082c40301e4ae05eea4afe4e78cdbd6f0caca5709ef73b061a9782eb64333a4d26a55c5880100000000db4130e80158b5100200000000045253ac65aa5905a00000000000", "65636565", 0, 1270046897, 1537743641, "5560992104bac14912996a4a692dfb9bf168583819454407e865232d0ed6539b"], + ["50c23e52011b9ff0034b3550d0ad468c37ea249ba07ff0eaef88000292b74f9b4ce89f950e02000000066a0065006352b68de66801b9025b0200000000016ab93a9139020000000000000000b461e90300000000eefc8b1031a83133fc6f8a9341a0d0df1c30799e02f9050cac71dde7d6161c0e89212db12205912cf8ee52c4ac5871c391b7997f7c9186b6e4a00a10893eabd26a05dd3d297a22dc2c1446633f66fa9a6c67c646c23249e4abe50adb922d30de00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c1a651aae9ebd8e1bc9b0f220bae5cbea35744ae70181484504554570362116e8d7b3abac066b7143f8105887702adeb1e36f40592a03e0e4654822903136b1d70bcc46cd452ff6b2240fbc9fa4301167f0e829877ead5257a1d2d589297a557451760f75fc3bb810c8c5f3b85dada254b113128d38565d11bdadaff8a6755d80314f5a21bc85921302468182ef499415b313813e1267966e9d24a2a5e4a884c0303181098a0edef6c70a7d0f8c7b0c3fba702776b9742e5f1158aa65fd2bb66bb4d0b02c9474f8a70672802976c700b2e20a36033dfea6bd1b217283a03d235012cabc1e4c149e21f486d72d39c9d4fa07cee11f512c85d5b0a23987e64bccf825418030dd61b6c619132cc14cb907184d0cd921d049c466b2254ea08b2e3d5c869e1680309046b70f2afd616c9eb2871edbedbc0cf631054d4f938bd7944f4b27c32f455031ca5908a67dcf61ef50fa36a5fdb40ec1741a6926bde6abab203ebad605cb66a02028d68ed8a3f52443d7280342be9d3a5af2fe28cdf4138cfe055125f2955a2f20317169557228f7e0aa2cf70288ad09eedb51f9f6477a1da9a1c83137c429cdddba9d87c0be63e0a329f20118e36250ce0088ac5f60228a12644fecbe8e92dcd47eaadc68b029241ec86e405bd1c4bab467e916445d6fe8cdba274073e2c60eb94d0d408366ced6d78af9a43582804c58039329bf1f99d74e13f1e760600c22156edf32d338b43c9fd9c644e709ee45a1ae33e279af4f8b14f0a9ac2c0d39190cea7448791ba4fbc86056470c36236161a3a7665c4ea75f654cd648a6f213ddc8a48381eab05d8b912cb8238e50b9b29b9622548db0dc24dc9718a2fcce20f16cd01315434e7a61fdaf595c29e63c8a7cee0aaa8a18a4a3c55d89627fbe54d041ff6593dab13771954b681c080c4117124d773a3d1f21d498c3c62fae29b1632cb4e924c9ae9bd8a516419cbe49e0685eda603b879cdb87748a5971155ec348b87f1f736a793a5f9377d4bf2b8f14211d9cfff478611b54af6dc206d80656b6ab10afb0bbc66d2ff9eefbee64db109bf3cc39511f27885678caaad6c2df808f142b59f9dac5ecabf36eb92869beeeb63aa50d1c3dbf847cdaec7ab98e14b3fa2f4ba5eb2e7840e30cee5591ba57b4ab163daab661cc72d3bfaa0b0fb826cfb485384b1f8f99cdc5c3b160eba1989ea024507ccf7773ffd7c58f4215b3c7be6eb6366138570183122a5e99c2c01203758ed6d39ed883fe313a455fdfdb2cce709649af0984c387c365d7d01e6afdf2596006790d3d4207241b7a7c15ca3b2e739144025d3514a5c98e7349ec3f478f2915f2d27ff2bd629085eb159d867557f941bce213fd5dece052239ca9d82c324707c42d711dbb3ccf4fd17ec43d24102330676e3e4d150de1f9a4607e521afe27ff3e334da6e91477c8a0d1e06c80d0ef1f083040c98cc65484a4bced464e1239ef2902e7c225ab9d9d04e024a25c23c6419973183b932c78223e2c7586598830717ab1b15ad6d5c434a5b89dcae0400375ab1f10d70f28284053786871133b147e5e70d86a662e1c16cbaaf1fb350ad214e593d20993c32eb80053ae3fa41a0c1c1fa7f818b6746eed6f499a421b00b90e689f81777ebf4ecdf9c5bb9343f2655c424894c9404f7cec6e11b1e7a55527396542d0629bacdb2dc45b2ad9d292b2051962522a0a8e923a99a8560edaecbb7fa6c34adb6bd32c1555d7528a7f6f8d37bbba363283d8f2321063052084ba06b221e32b06fbe3bc15984c50cced35bec0c4110997d7b0d08ddb7e8bee7f92f51364f685d725b8d7d8ce6f61f20c9aed0b452494e73cbd91fd1296960c5d5fe53bb669faff383eb25318abae418cf2c3cd16f252869234ed87d671cfbd460ea591019cae6acccc889d1289ed470d48b6c9921ff386ed700021b2c83acf6fc7e4ea3e23a8401643a098745be44f74f9c1552a68734fc1aa75a0cdc906d7edd0bdb19d80b8f856971c305c648e10e3f7d85fcb9ecb615ede35b2b8bd77b3302d6856a81986b041fc7fcae989276a6d822077068e23167a650b3719cdd379f0870fb05699db510a57305f4523234eb90c3691de4753a1d134c18e381d5ad017f9269986aac110060a296e134db54ba4639886cafee0ac52257443ff408812941fc00026ab5f43883722e3039d65b0c453db952be63701dfa2dd65bfe36f49e5d41fba86594bdda0cd54ce0f704a0262f012786af4dcfab652105d6b6464334b1f9a4abb908c8a8d128b249d43a6275c4b4b05458470000000000000000ca2c9004000000006cfff26e195b31befb46eb15322681de09415622c8db79a7b73f9914b3eee89530637a4352d81cf31709e3cb203fb8ed53eebc6fd72e77c71d44e2b39f046d85bbb166846d9c861c9e8229e6fec9b7b1ee4cdcaa79ae80cd16a3e9a30d11f01f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003caffb3e1e472f3affe39ace681d4a6d5f953ace2d145950b35aab5f1e201a83687425122969f0491d046523e5b06b7524cf67a93b99e3712637d267cfd1b63b51328062d392ebfbdee27dd306c96375d018fce26ee827a1699f128ec18bdc699703091078840d0b2cd51db5ab417d7373bba1f89e849ac81fd0ee79d902c35c02291dd5331e834fd4e17e0a666615d3571a1e72ec2701487a93186d86eba91fee0224a1503b82c4b60aed09ee427e4ed25a52ded8c6f1dd18bfaf86d03a930963200b0815eccc56da4129c38dcd3d33944cedeafba60d2f9340cba0326ab17cccf5704c9c542deb0b011bfea08bb5581b47e9eaafcaa4d7d486553877eef016b0920d0211e61fb46d23ba651787d2d38a5dafdeb548b20dcc7d3e0b6e909cb2d60d8a40021e3eaffc596db45177e80a26cf4670ed5204e67912d896895bc55dff5120e0cf0303be003311b0955164fde0ff52efddfd4170784aecd36c399211feab6bb0f94d030ad54a893e76d2ca5f35e00ff7437905f8b0a2029603f7cbdb545ecde5af7e87020fed1271ab7792a936b1280c0b2845d2b0a396c5de57b6c89adaa23c152a6c0eb4fa60679a45b2f01d945bbb99e68c4c0963652f9318737c69dccb8e1e4fc3e5709579a8f4c701c8c1f3cb689a265d0667773553c8641ed9fb916c427cf978f5b8bed22684ede79185ad9bc82137a12bbe6cf103824455db98f30ae554ae006f379de76abbd725facdc69fd28e5f094dfd9a4234706de72ec2d352f8462f4863459989f592ab1c4a074d65ac63e5e5d9b206478f90bb8a61febc87712ededb90f213935fa5cdec7e082d0165672892d9b334c59e4cce38efe12b85d7d6b37da0a66e6bd97b66a90a66dafcc6852b183456ff06779eb83dd10c65170b19afe4c300385b826367c540d0e5c6ddb722cd06b6fbe9e42419112d695eb89ad79161ecaef20e0f26e775ad1241c7d1387a36d9f475327cb616693b4156ca08e2834e538b373320aa8d12b318278ffffbbb9e956a1987782ad8837f6f7b77af1cd6b41678698dd81063aaa99fca16cc275c5e1986b936853e851378fda4e5c74bd17c93b272e0fdcaae667dc0efc323292aba1c95b98173f203bdd61217341d1b9bc726480d9e98e4b98aede63c163869438ec0523ae0dbc094bb141075aa3e9c8ec10345eae2d25ab0ca6831b3b303494ddabfe80df140c64673ef4e92e83804696c078e8634f07197ee4c28f3f18e6badfb1c89b95ce0a5667e53ed59e7186dae95ec57d0c0e323db2573b6be93a679f805c6fbd2c873ffd490b385da9e6613abd4b295028b65d30a9a9311646255d28cea091c4ce9eb7b2b2bd6a1cf4060c8220a8dd9eb4ba6bca3d3af38c811bc0ae84b399f0188ea200017e4243041e29f92858d8af6df71a7353a5759883370679dbb3e8136d82419584f80ebc9c999f5da64c8bbe3edb2ae6a55c63b290137b45295c6df015755d13c24be418cb8684c075a55a63382770aa3187042a7c94ca7b9b235cecf1adea37196323622e9144f362bdef473689a286cdfcf2095f4dcb9c2c2615f341cee30e75082407927a702d6f951994523c6665e054bfd978f2ff63af226531e744305860e7120c9a4f809f8de440e632f8ed06993b01540565004dc4ac953b5acf5bccfc2da8fadc01ec66fcdde5445757ddb8120ba567dc5de8241c6a9dfc13e88a9bc0a15c070e487ffa688e441f2a3c1a09c8c5a0dcb8dd070a4a4a5948a97853c9ba26d65bb98ef58e03792e04695f2d21c12c78bbae55f507909ed5d491bafa5a0d8942d51eda08dc433ed0d2f0e59217155d4a1bc6bbc80ba40ba9524a5a764d6bf0668fd1e371f1220011f096f58f90780d4597280e255d1ec8aae142637488d5e7d98c5fc60c7836f0663d6a41cd8c58e5ec5876cdc52f2d3f239d1bcfcc3952305317d82444a22dfee1e5057aeee5e44f825c2933946e33913a0d8b002765beeca34cbd1d1daa210a7cc14903fcfe51ff77f5cd96b1f9956c84469119d738aa1c99cf6e29ffe2b54ac785fec676743e46581450b87d65cf306a79391be93566a0d623694369d7945ce37ce0bb59584dfac838ee478b06d03cac454ff641526a5e31cf8f96ba6f1421f9e1d0a12002cafc84cc3b470d1f943b82447ef2cb840fc8bcf5fc5e1da982cd16e159ff7481fd6e4fad68868bd016d3691a814620fa1e2ccd7bc60f973c2e671a380359f0f6862c190c968ce69217401f6b2e6bc4b96ea199ea618266af3acc9f1ca1c3923720b4764126253622d66b1410fdb00ed7927b20f42e440d2479d49ab076403000cedb34c251c68e97431e990d922002c7ceab330e6674e08d4ca275337d679af62db9b3cabaf4947bbe21669f8f97dda982d6d8340569d6fe62691dfab6f51a4f7b616d08359eb1d6aa5256f04", "630063516a6a65", 0, 1570969214, 1537743641, "65b7113864d3245ca36cc637155c114c820b0bfe01ca4272f28f4db058582644"], + ["030000807082c40301c04861e316c8351ab1ae7a8e737f1718cb33d4a2ec0a5a1dd0cb6d786839a21f03000000026551568a12ae040b10ac040000000004516565525c06f90200000000055252655163002c5b0200000000036a5352ef080601000000000665ac516aac65000000001172e3e001f6c88b0500000000000000000000000046de8444a31bfca39659b06d44538dc99844710f7a8ee3b32287f1f24e658eec1c0443b6df34ce9bfb5195a82008b320482a26a642b9725a670ba3f47ce7256a380f523cf6fcab20a760ae24efcfa84493143f7dc37a4b4644bdf8b85988ea3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a54660e3184d52bcf3d0479de9e5c8b933ede89f2978e28b81e9cf08d24c9e4597a463f0125df82f78227fea09528b7cb1df544ec3a50e3cb9ef44ad3fc652118f5d3ebd67642f71fa38d5e3fda400425cb5dd3a5dcfc8b199b872187ae03a3bf0384238e35dea7a4295e6501e693026ea659c3cdfd3c490eae53499f4c3290803038fed4aa4b12c29bf540389480c6f465e473647c241baa7cd6985810f31d2cb032672561b3958754e55729dc422e547062c83a9e8b258634c861a06b6438c927c0a07967c0db4ad0bbb25c0d043c269bccb6d2ec0ff9769fd6017a868b391304e4d484b1b6b9009d53274b8834fb915aee3100918393470ba0eb90dbb15232157ce032ec57e8132f2d474903a19d4351c83a32c43a992cbeddf64748759f62dd4cd430321debf5e09975b99cc4a52a8fb7802451ad46bcd32c0b8a6820408e89e5f73f902294d72dd73e4c146d5030f6ca3d1cc7d253a1258fff67baa60bfb2dafe90fde00216e421a76d62b72a31863a2f009047431567bbf31eee777546de0d3e0ccdabe9030554e3756dd493d2687f6573bbaebb391dc31861bc680d28dfef982f32331063ab0690e132edc74bc21ce62f6f59cbd0b256faf173157d67f680b828b3c663250a26b0c89de7306b7c627cd4b50073d1f0d8b014fe7bfa64dc2fbab395a9194a8f2f83dc8b0a1dd599ba62fddb0f1dc78f6b8bed4f22b2dfdba42bb2943ff75294b3b243c31dbc4c02e6783986a303dcad72279ba68e2fcecc0aafcb4f8e7e8ee5e394b1c29a0868b206b80c2c2ecd5f91a0c4685e738be67ec555a20f3ee68031675ea2b2fab753924ebd2f7826e27c9760e0beb34d5e45b000ec142ad029381af1c51392252423c90126b228a1769417ff4ed9c4c8235c332ef3ae95b3d1f6358fcff0dd1c535864378b6f9abc3f1b0cf038f3a6ddaf4d08f3f650a46b00c05b9699c590fa4fc5833d6632fc6c526a3092218021720fc84cee22b65ab50a52c8ff3497ead910884deb72c6d355a48a788c37a1f9c761113aad75863a879bdc891d954ff0649894ddb3c2461a440a8429e0b7e604313bb6da4a40575e6dad70693bbc125bf0dd8ccbb02b2f095077774a38c8d769484f5364077f8b96a07a30b2fd4cda1273972b43dd1491af9e0b9e0003e063c2febc8505884fc24fd8e849c922cfc521c936a3506d2ea797604ec146f0d46a2060abac0ce220ae181c4a2693e9cef8885601b11c0a3be62b3ee7e5984e4e5b281643ac2524913e3d93b05deefdc91c1a1aeeb78de898178d95270b20c44ec9f3d650ac879988d5d0e391cc767fe102c1d6f881c16881df011690bfc2f209b44fd18bfc63e97ae7ec2b82b333fd44a3e0b377a78ec4a3ddf5cd74ea7c2ad73b8f2226b3e021aad3712c7caf2ebbd79fa3111c142909e4fc1f25f020632aaca06c289b6ee2eca5f9059d6712fff5a38c72329605bd66a44417db3b51c5822d1c04851c5f489489ddb4cd27a5bc3e19b1e6edb42da0eb48f7d33f9eedbedf12ab853628d2c69b8326bbef81d7b48f158a11754e7da88e7ae0baa240441deed87970aeaa4baaa977ef9e72d641c1bb97a83ab6c6a836d105a61918a65fe08496031891a3b3d86b7ea4885833b0c107a863dea5d556a782a49972b600dc647d17e1a25757ea4b179f04229a29b94a699428a0b8190f22c9df3fec6f029a1c7cf78372a2c75e328153d3d6cbac826fa132773a4d06b2946e052d11c41d9bbf68617f0d6273918307dd35599d1f6dda0414b6c430118685bd979b7d85eb236a91efd0aaa6ebbb65ef34a253a5e863176a427ad36d7feae1cb9779f5b773949fa3c94b7762e962fabed4674475a90060118c1fae6ebef5b486c1629f0c77a72bd75fc604797005d24ce13183b94691ba1d725b70260451cac373a39335e9c1e29ff9862892f54e2e0ce81c8632f04d9ad4d8389805293c20e7e6dd4ebd0421bae045e58853e2d767496012bef3e073a3fdec0d7ed9dd85a531fef7f3afe96baa4154c992a0c6c6ed601a5f89e3e2ad60a99fb9f000ec1e755e564592fa62846ed6cdac24664ab2e9a3203b31cc166169cde5aa75a3778b8b809f1abf32d0e08ab4c3ad1c4d10c0edb6b860947ed88faa705ca9ae0b5444eb62928781652ffdcefc55af69987a0b23ac9182fc2edcaf2c08b0e60ace0d2b5547ab0aca1c58bdfc781b7bb0733651ac1b776875e32b0247e47fba190a7b61d36533443ea55b0beb2d96dc58ce62dbd2df5d20cc3e1fed902df099753d049e0af93f562a3199f4b727b059b5bf26eb876289a1aee96c34269a9a7fb97e3a5c47980dad10704b21ebfb4f5d9b330daa97d5af29324d8133d4222ee9f3790268acc8f68f48347823c7c748bc9c009d7e033ea1647f8dff707e5a91cf43795bbcad0aeb304de237390604", "5153535365006aac", 0, -150717590, 0, "5b5ef5d9939abfad9ea5218b6d831360f80616cc21f9c42f3c6de12bfeffade3"], + ["030000807082c403030245f922ac454436f685f11926918c0ebea801cff31ede78086e85871d4929e200000000085163535263650000ffffffff3d6ef9d379087b58d527ad689ff966f930ba4a4696d6e379b84e07f2758315810000000007636a5100656563ffffffff9486974044b51945e41ff35ec62f7a3f8c88d3fc77006961c978c3e599de4bd602000000086300525253000065ffffffff0311d60c00000000000900ac00636aac6352651aab7601000000000453656a0030679b020000000003acac00000000000000000003eb070a010000000000000000000000009ae00aaf4c839ee8215f273f685ae732c5ac09c963fbccd743ceabd3edee769b937cd802e0f6fe85a9e52b40ed91baf13338605e9591778e4c410369a0c51afdf226591e0dc8ff410ee9747773d767236468a3bb71b92aa4fa8bc99d3b3ba00b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009a6b26e3077af40190458f21dc0e18f3356c4157429d67b9cb62c31c9d6aa5ada1e462f440c491ca40a402581fe1909bee37a16123e2d716ab9dd81bd615adb7418bc90b32637b9ed56654fda86630bb415fb5cea11dce6d1f74b233be78029bdfbd24d258e050479bf9745b9e0e83086ee5cfa5e43f3beb52c1e8fb9e68c11f020151bfa7518952c7e599d5679cb92304f37dd1d6224e9a4cda2bd8b5c42bb0c00225a9fe453adc7c7d056c6fd413540fc74c9a074ac827330f9f197c17a82691820a013890254aebc364bfd0b40825b7b7d9de8477524afc88bfc8ed7718e152e0ee166b75b8265e7fc152243cccab8bcacfc09cc653caa4c07d690c53b053c5374f032d9188d881cb9a27a7ac191b44815222a9c338db695d04732a424e2878cfa6240312850a311191741b2ca13b767c9693692e3e8541062d3a7d170051eddcc42b8b02159578cfc62fdf4dafd968f3cce9769aa9d47efda889dbe00ea6a1d294abb6d403237dbad5b726c10f89bd80a9c8e99e3a3bd6ace37ecc2d81f8d77a6b2222d6740311ddd5442df1b97f5552547ed4e7b0fd59eac0dff542913925ede151bf96d39a28b6f49a19f361a4474de669cb5cd11a8f77a16431207aae37c49a05114a272a6334ff70e14e9f56099b07c1a1b70557079311637b85461a77f26c8a7181bc5c9876a029d9041e382066fb6cb9e8a4ba5b3aff6a7e350def760c7677de170388f5c0799b87496efcc26151353366984fd5c236c9578c20f38591219680fb43a74aa285e95c3033eeb0391a5f023550ffb907515306750a8c1c9d054c7cb061edacb204dce8bb60e8335790e02aaee62c95bf3ee1e8740fb75c021d968ae0c3cd6a8862934534380cf733b26ef698076f4fb0639328fbe977545a61c8c0dd4fa63fe8ac0aa47a2044f24ffdb057ccff5e4133c5ced9e2285e326f7928f7060138c0038bc61085d45e4b5f3b7fa5ee8ec9160d90822f9cdad524020c40fd54ef9a78a8d0088cfdb584fcb483ee022bfa37829db09856a3954574be7ff9f98c69cfa889bf60efe2bbb94de46ed7f61bf39488d3bff46ccd83fb3be6c85f0b50b3ddcb179b4071c04cf609c2b3378242935d6ec4df82986341322d3d5acd04e0ed26b1e5073d6d73dd45e001eab2c831e678d2da7feb75547f037287d70881129fa9ad5c843e490b85fd79ffeb86ecc089028b594b94392b77c8d7a85211b50052132e391646b07b5e94247923f7b3a398fdd9814c7589446da91b4ba3ac0f8c1b79f3b01ce47507e463141779bdb11fc51f3fa2c7b762f1e4286ccc855494ee10670417336a749a128b11770e51193b0f4bd68eb8f2ef69028cd1a87914bf7588c84c832dfc1675e815b30e102e09694d45d128ab6a01196b97c0dee6e6a20b75b487ad1882cae0c457dcbed0e7ae45f57958da044ce2aa255fa91e2dc55eb9728da36f4ff812b4b2ebb932467022a5ee89adf987fb7adac1c7e018faed12a9b893332fa946bc48f5d4c20a1c93b7565b4c0878ffa5cb8a4c82f2bd83de4954a4d652053089087a646aa1406f9c79676ccab63e318a956b98553ec22d01a897f53199b84ee371df86f1a33a90a295351c6f7110811de3f898f0ad7c958e8b325aa7676be5acaad72de10df94becdc6151e0b47d168bef2e1676130dbb14a52573dae2f87a082b2975f8bad7c339fc63fcad3f616caeaf4a6294957b6810a22b4e451a9f26ecc556a5529acce79c59b69846ea175e3e4139d46f6b110735bb6dbaa7ea32a6fadd48742343328debb8d8894097d37bb895bd2b9f248d95a9d08b539174343a88e2192ea07b03336fbd1d168980f191834233ee11494ef96991ddd159adade1e033614917f053f43b34f67e53756eb6d361beb682fe697ec3dfb335e22334f94b9f82c97474cbb81f5515d88a1691a6d8e77d2ae4ec506703be5e899d7739ec15d4d873c3b4b50bf418cad993b1d1a2cb6112efce9ca70aba70932f6970949c7654819e1ca60f2b657932fc03b5b02ffd381db2bb508abacec759359547697cc82faae7aa05b90c8b5ef4eefa7f0bef90fc912683b6331497b33bafdbdf01fc78076a5d57f66f53650df6a9d9aafc8c42a7f1aa183b17946e1af9390e1f44f5a1a8077bea1998a29453fb5a98190871d0fbc52c1eeb4921c481bb93c17dc51e4ce5d72b72ee38f6f8acc65592b4cce39e6d8320b0bc27f7d7d684a0e3c83591d1ed97d85af68aef2b5a0c74892d0e3fc13415db8bb974eb3118f6a2a8921cfaeb0975151bb22baf1dba01e50a497909c214000000000000000000000000003f88f94e0cac5eb6746292fd411dab3b4885e7c9fb511f944273375d1e99d2461db5c675e6696443d3b77064288918061b033c71992a5d7579e2b3607de86a6d20d362ae071f38ca7255d97b7cc9a5683af413dadcaa27225284dcbe700da4ab0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021aa4d47c0d4cfa48f4e3e9d8ef41fef2032f698911ddda307cfcac323ba707bb0bc5273f7fb4e4afe5e72bcc1fe8d378d4d7dc0982f213dd06621dfd814301f0e13e963503ae5f42b1a2395b5505a0b8ff9246b244127f93bb5572a887fa154abf74bd788b04f539a7682b1483d371d0e686fad714eac9fe73109acef421dd7032f87336c7202dc693ae997c6cb7b7abbc997e4047cd2fd3c4be9af7a42dd356d021b8010ec405de41a4658d232eafc872b18c58fd28e6594e4e8e72a926cf35e950a06c7beb25a142fdf71554dc2b1df895b52806eb2528a8e7d3b7fce95b35a0d2752bbe498d6c2a62567273bde02c20fec0218d05508e67dd2c8d8ead33e289db10326fb78502e050013e2ae2cdb1fc08e4414fb937d165fa9b407bf09e20fb67905020e1266b01f4ee89eec217b4bcddc193a80f39825a534b577f2469165e7a2ede30208b695451ffc045d2bd02b46b0d1577c82ad22fe1fdfb90d90d187b6e11978030300f3e9b1c2229f70526067c90a133d54e039c7e56defe451d8a677dc69237e7f022c2053753989906a9dddc0d1b08361ed37bb969227edc4d3e9b58603c1fd2d4bd4fac9be6e257b5add330f45c3e181ed68514b221fa6d84d2bdd7288ca8614bdb7edd2e3a83e3955934710526268512c691f86d5326c42216b3f08471c11716ee090b71e6548ecdb035dd82a1e1eb1552e196a0971b16346dc931e10ef8caa2125767300e6bba475b5f58756cf9078a9e19600225460b9e4df0765e65461ee8af065488d2295a9f1e2689ef1a86061e42fa33b967a5f0894b2333e5338698489520df3575117a6606fd38eb7512dc48c49b254716fad1f93f26ffb9f9c552fa73768bc0f8a65b94f5b89ee7365b313b04f675d1d730414af409e8339b178fb5837154d06ffd5c6d191e91eb77be1407053ec8e9729d27e67865d957b605b921d1e7a46c88c0d350955d51907a5ababa8079d105b60cb80249e95fdb0a0f09600c9590bcc2a188df763a3e81066d32a302df20ced1e11a7d459778a4f4327377f9330e8a81154d11b3d2f5688ba433b9ee8604abad13119fb9ee4f7b6e41bf817a1e922a2bafdd6431aee4d04fe7fd3d3ecaca02a3f2817c538bc501fb3a4e47733c58d157bbc0cb12edb7c823b95b6acc7db2027d1c23736224486e6a9e8faef8a91d9d3aac1e0767d2f066fffe76011054ca4d3e020e312bf295946ef8605edd35abcfab0e106903e2742e1006542ace0416965c4de3ca732fef7c5ab3c045b4fa2401719065dc4e15bf42eadc337f1da778bfc53e25a8ce8cb8b85e90a2db1601522a22d34da89905c44107408aef5b239c20df33bb36902cc262d4efde97b63eda9c933644f3ce7f42b52f089aa7bd12b659ca65bd86d2fc49f2e87f7f0518bcfdb7756a8f489cf869dd7ff38d7d6f42b3192e1d9ee3ac059abd657103b0bef16e2725902c9c2b9a479800b5b4be7a01d52bfea510de0ab0a0cfacfe0d31981cb07b91123110b5c621909103f57f806eb2695860b7cda8609238b2e23219ed59b24b5031fa8e38b17462630b177593410adf9360bfadc24ae5fa7670063fd1e353a93e3d32cfa2811b49b33e79240ef4d6b6f2d3bc9198f23998fc35c2ac98c45dfcbb22248405e51e40d9e8852489f414eb0ce03c6a6f7121c4c772fab4d619cc731cd1ee585885d4b93775ae9f7b2e7bf3352dc96f62cc0a8a7ce1994e4cfcba58a550ea9c030c56e3f59722756389693b3db222afa79a99c9bafba82f190192c7d3d446eef24846614a48590fd04c0805ae4b2fef32aa1bfc1d183dfece19ac366931731405fe2b0b14494570b86acb5c7f291af8dd1143e1d9c6f3e47f6520c219eee2d4f87dbacf0d7f81e263a5f61088622d3cf710700512995e80c8b8f64272432fc8ab8664b1dee6caee447d74d428738a47180cff2644694fd542f7073db39cad8843548fee72eee52b347e7c51710d6c901be3b2cb35ea6aa42f0f71ffc95f085c36f6f08ed2202ed03fa74a167bd1a50b9e7158cad179dbfd444f50b2fef574f792acfc7b815086d3e3b03d7e29ab6661e862129a8f31a8f26802c2b88111d7741697cb0db9808303e103a148897944651d14032e8fd0163aaba3f552f061a5acc79d08200e3d581f7c7b8a1a377f0dce3a1fe95704e6499311f6d0fba745e200efbee335fec02d5fe1d411c035964c6a7d2e9b752a5f18202f6147b84d7ed517b0b89615c0947724d0b65b44f0952850891b15dd73e1f766bac0dc0cb5bdf9b1d4c89af1326f7a18fdda9000000000000000099990103000000003b0ff05a110db31d7e77b943088fa7e7c99d8a87361dd2f38ff26807fa82fdd8f696f94e65e1ac46cdee24c64ada8d8881ee40a061b4bc9a9eb44d600cb4e558d461cbd76724a0af2ae1f7d5afcbc2ef5134c7f57889271d7862daa3dc76b8ff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b4d30a9c5a2d487d7be24abdc7211d27d0c0ef5439ccf240c451571d9ab55961bf1b3b6170b556cd9c2ffd60b6120c20d3cb7cb3e3df9b14542cc280f9624010102eaaf8c32fa45a69fc4472197a70e23eda68768e317274022947b3764b6df8cb4128f351f480fdaed4a1d08c2145ab009f81b397f4925c09af10372432fa5602132ad51b67b4bcaffbfdd6fd1229e586c84379b7086f2ddf787eb6379fe8c22e03167d330bbfd4df3416fba887b776f3017a78d2d5b207126a426857804d5adc2e0b01401670e2d466c1de960cdb75765c17bbceaa8e5e1334de8889240e67233e6c2cee21b9adbcd4806c259b5dd3e933f7a75af080ac8bcc6349fe0cc86d7642230226b0b3c2a2105caec234302145267062750e297b5ae1993e0a62338c9b8fe34002082a030391ef3cb5afcbb6b0ca5e5b52cb9ec1c57ffb69880dd3407459fea48703137ce5b9bbabd7430b7be0811be546730b59f2f8dab6efbfef939a5b65ed66cc030d79c3fd9eb6b68724ec55c80cbf0dac9397fa7adfadc0b980ff9c77203c456503126d9a6be9c2dad7b0ceee9566cb72368ae77b37674f0135051c63240efc304ce36a9728cc0a1c756e85a975922c5dcbdae69b2892f88b48798bbb9455fcc54adc517aab17fea992be57928f96031af03d0d147aa733bf812cbf94c5419d4fda4f23a8236d8f930be4e88c3fe3013ee8a5ba69b92a013bd0e99778fb98fb7568c6fa1cefded53bc82cac3c00908ab81cb35d825ebca2662556b1ae3749ff668c338a8d2764b3bac920319c32d1a24fbc439a14c04c4696ec0d21f5ac755255a1b2a3572fc4aa2fae775bf848d59c9f420d283c992ca66c5add47f44e18ef82b9b62f513217342f070abbea95c1ffadda137f0d731dbc73a42f3fa2ffdc4b79d30e9deeba95fdd559c4ef59ac75085b35a268413abeda2a2c9aaf8ec54cbaa216fbd72489c7cef9a76e976d6902bfa1dd8f94def8fe2de8ba5cc6ac853b409ef7f9f913e64848cd63bbcfba676461a70b15feb7419b4006036665853bf79f49d0f3f9a602c83a1842f271615174ff810e75b8a14ba4c9de3c24802a6d733f20c73f99512fd53857ad4c86562a0395799bd5d2626569db8788cd3422f196d7dc362fd774b8e5f43b01ef540e035306945b97990c8349b9d5a3e72ed9be33254a7803a9d84719fc97e935825603042b70023686e08fa1ffce5f590907b934db6c01f888b11cd27750c3f407bf51856fee8f125c7eeadc26b0b09c23d1dc58e8f46d0189b5db370d9e1dee4cbdf1f13fe5b8a5862922fb160a8d14fc40f1ebb70afa82ab06c784f7ea8adbceb52d3d285a1e28195c91705214aa44e3832852d9c2d58cbf7677894bf7d9e56cae14fb4c224397a0f23da5a123b0b4d3a8be586211b846a30e3d485c0ae76cfa9e47123cb49b5037c9fd400af41b528be068a212589e6cbb3298032db1463181e780514a23530225172923f2fcafa3651064fe72b04a4d190ce47f988e4c5bc42a9281eadbbd75535bea68bb1fa840f983091b3f220035109e80178021bbf329a585073db6aa72fabb4a8c74e79a9c538145b61f04d64e5eae6e0867d4af84228eea4be151f702b945da2492479f045d63b16eb63201f131d0be5084ae3b40c6ee6fdebdd1daf39adc467788e42503416bbb19826c4c1ed0b0ae0d8379bfdb3969fa6912cd61056a67294b39ecee6f1867a3edc5f661c9f3a41009720d6978eb0b5b0cbde5949cf88020ce06f0b749eea89f340a1b2cf353e3140ff9dc1951658725ec91763ab44f188264f63e03952078175499d7fdde2b4e5509b2488083b07bae45e0e6ce4e29492da4353f726cc1e634a8d12f3d4ca60cf648d08acb2b766aa2e9c6df5b0e7f1ab4cc8fe0cdf7146e2c1c7945f15710804a51ec300f6cbbb2dfe4575104dd95cd978619e9c337244f302ec43d4d2c2dc60a5ff59c1cc3928f630555dd1d1969d7bf35edced25b90f3255baafa4862c2f47027b11b55515c6850cdd4e1ddfebc1f9d014465a766253d772f8547e0f5356c827f0b5cfa411a1a7a54fcf994d63ac5dcfc3987ce81aea6ed1924973a85a768bdcda9fdfe27d7455dd1301b3c9593e07739c5194490f6939daa4fd08660a2bc3114122430feadb7336a3795e85351483f5d984beedb8156b94ff38e05e2dfca9df5adc4b89425b54bc05d944a58bcc0dcdbb89ee9a91c9674105fa82c74461a8285ac7f8189a1d06d0db4721432221d2312201b5115f32f7cc6d66b62dd37f9805e20dc0bae929d777c66c9b390463861f81d69952828d52bd98c24e1e5620597e8dc2e7fa278fdc5fc4d1051bf22f63da40e340c880045ac2838759cae9190d832bc490ee70dddad2a7e5094221f385a145906a9f4a5fff3def54d368625f8f2fe845145bbe997ff1a82d4a2430e", "53ac", 1, -471772630, 1537743641, "776b5e94880708f433d5b605972c4f6d0b37e204a4dde644547900e3d4378f9a"], + ["030000807082c40304b38aea5354edee912e1aca741bc7009c35c1be5a6c467f635629f527a200719c0000000000fba75fbc67e23a76edeee827b06e06da0867b39aacb174039318926ea1a81b456fe38aea00000000026363ffffffff4feef7fde8f8e1f57908d6fa361677f03682ada8888f4fd68da21bd84cc29fec0200000006655163530051ffffffff45a3f095c36a1be40fd9f3dbf2d78ce66eaea83e8401386eb1229c91d1664c8603000000035151513b31022f030849d100000000000351526521367702000000000665536aacac006d6963000000000004656300ac676fa15d0000000002f02a990500000000000000000000000003c7dacf6a62d2214fb9d6cf380760dc9767e33d367a7ac10f02540b1fd218104058c23dbdaf9515a96da1ae7a5e945a3faa4955419bcf6a625a3e35607a0d6243dac96c50ea2142070b63cce7ae9033fc15a7fc76dbf62dcffef76b483ccdeb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001cf4329654e76e4a62686fb3930349f1d75ff7fe10af9fa9f90d984e537f83b672a89ae6e41ae6ad2360051b1e5f5116a5cf43b15e96381e9130adaab360cd66f830bebc7ba69d6cd038625be3a1e47d2d595368e9378688b508fcd7b34652466709e50baf383960db494d48eae5ac6ba95cf925d87fe216b128f42ceff4714e021afaaa21759d8d1f8a7ddef4e07d537a16a1571d1840a730620b3deb33d584b9021e5dbdde7ae3f40d58be55f12283d76341c60c6fd431620e768a4afce665433c0b07d1e1aecd6959e3d531971eef4ea360110dcf466a530519b624f81e12d53effa2c92b8ef4d081e2e4aa1dae56e8e13d9186e89f19eb9a5e8907d9d75f757ed2030ce7fc67e50b53bc40adab8e7d7b8e104ebabe05c49bab685677f06e62c0930e02131576fe7acccb1db5b2e2324b6cb9607a8b600026a677e79e6458adb3cd55a8022ee0d5e715e451f528e0136eba8196a55e6fb5e82f3c56a8c220dc2eb8a4dfba022f066c02d6e60720f80b254a3e4068de976088a0c19464d32becaf9a86957e6b020e595e5de6be55316aa99aa2e29c11bbe06960fb91ef8850711d277ddfd0c7a8434da77bedd4344709266ea9421a5ed9a00533d5e2b4e4aa7a86a635a25e6348ac60f128cdd6638f9ee4b10b4c0f3c1666d51b1a93c756c3e90535e3c6efdc673fe0533d8601501c66399c6c96bfbaf4d434f19e5b7f0107f42d3321277df494c858ee812f2b152ad6f3ab78bfaedf3c1f0a51613f97e551fb0d9150f874293f9d039dc95ca466c3154a00dccf6c3dc1d08d3b66ad81f49c58e89155d33790a1d31431faed392f153ed8f38147c5988ded8c4b478d51bd0daa7f298272e7e3a374dfb302265c520034c9db9b4f3ea1b425d144c0110d6c31ee13a09de9d611da02c42c7644058313179ebc9d47bade60abc8250e6faff2b4c2fd83ed572db3e6007b782978c337847800af706a45d0078337439b82faf4294c293a741d530b66bb5768eec63e2c99d2ed7694ff2cb711ea4a8a774e5502d6943f6f5d91eee15b3901d8d8dbcb7d9acac685bb74560f4cee27df5beaa0991e9bea20fe68079d39c50334e4ede3064222df36d3cbb9558e98d0b3296c3115805bf8faa12169b674cc93ba57409f810ec4826fb8a78fbc604262c302c7e617f1f826f7baf75d2a7ae6debbc422ca5458966d3df55a2f06ef744e238f0c587fd645c1adfe7eb9f0aa57da6acd4692bd63e874f5d7f60cee161fe47e411ee94b69e83fbcc564569ced900960e6f6e84fd6a423b8cf6923e139f73e173aa203c4ac8269a4d4f486c53a69e7f4abba03b0a7c7583d7398bfdb5a406b31d5203ae1db10dfaaab13adca7dc301c4b50581452cff09d0c0028bb717bb8d476682b67c4d03ae6f414d829258581edd00a7de350624693915a3c688d069337220553b16c510631da588df0bd69e28ba7e506aff480e550a0511307cd7a296c549d85930259a28e5706518ff59c37b16df620403d91b17a6708b4d9589277d59c38cbe8ca84798c5eb70936ce725316d29c960d59bef45265bdadd44aa47b016129ada1da3144d3eb175155592c7f43931c01f3df9821b33cb36679d71a7e2be59854c7ab9d9137699795fd5f6bf46e21f3ecb0b26f09053e06f2cd8273290c944b6c2baaa75feb33adefa857a5da0e7bf233fdd72483cc5bbfa1a39b404efdd9fc183518e57c860f2cfcfbc6ce61c48cd33eb898f0696100072dccf99f8e22926b0c584ccfc4864727cd3d1a48b774bfdecf02271a58a0589250fb66114318540382b1b15793cf8efe79115fe54023b410e278966161a9df768aff561736f2614afdc19a1637d026e6681e6c9d2126cee81ec72831d2482b95327040a2e324b83485c818da1a19e42ef8436568c22b46969f0e4eef1d0fbb6b1762e98fcc234e51590ae4ede6fb6d5ed5eb91162f242f98a69a84bd984dede1295f511d3a81a26cb0f0b45f4cd354772adb71be7f5bc8bff7d2c6f62b355c39e8cfa412a777094f20de1e6d0839b456c1ce7596dd56855709900b3883722b0b88325c5be2181b13bfbac1c618d6038388a7cd5d82cedd74b80931ead33622ae64abc12bfa01e2172c3e2908e36e82f7cc0c8df10b02054e6ae43a565793a62adb0632fb04c5c0fbf8859d65bf7d611bf577b2c7dbad68660048ca5f562eda7f1f085faaa2ff9019b8b77001c009abdd6855df78d7bed19f74c6de3e3e97041d169a43d8f2441e969c831e38c1e4ffd25a009e93d85fbc631b9a29960d4d12a4a3ebcb5315a4754a3010000000000000000000000002c68df9a6ea06afdade977623ac73ee532371d13c56270c5ac9dafc7a4f4b3e447295d290f3a2d5254273fe8c82f2ee1e130e2909f9f440008dd03c3cb2ed4e71af3faa0b02df8ef81680baed3faf2f24b9d98c14a34a4406d81f9588f2f77ce000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005fa945660af53491a349e5f466c12d0949dc9c854ec939e4e8caba56ceccfdeb5a48fb14b93fc25145fc1e5c367e350e27927b457fd8a7e957c670392c0b9f2cee40dcfe35573334fd8df79a2e49ef22da0bd559c8ef5df3ab678a0cae5a3d1765da655646c5e9b6ed2ac0a190f44613e63b4329426857f3eab23a042848fd1d0313ede10b102211b4af7ea1b3a3786869ff73a274b60d44a0888d1889c5531c620215d077b171505a724e5424715599b58c0e3cb85af10eae8c992c67de32d5e1e30a034654812e1ad84d9f260d12d516f2985952978249d678227b2bb0a9e2af013bb4a7d8570a161b1aa955874317b02cb1998a3b209dc1dedff3994d5ddadfbd000215f09661f8c0f5c55912923e7db41aba65ac316a7a4d475c758e47ec7de2cb4d022406b6874483aac8db3f3f45d844378dda7a5fb5455704d183646ddf6ed04676022e3f2c5556e4b62eaa64791bc3fcb210394f9c9a99258a01e55d623f817c69f20309ec73e5da2b226a7ba39b5d2c6104d3acfd27baa978eb24a05b36a8ded46873020006f5c019ccfb1f4920a1d9ee29f8846a606289f2740cddd010e25ca401dda593e2036032f8ba6cda7599a29d58b99c0d6093b133843b64dcf29c53b09e50972c532b19cb07e5f818fe7487b363d9aeb353fda42e09a55016a300d6a2d41d812f75d86b711f75b51088ade8f4b7d0c383d510264d7bd34c63f2565469a26e6d87bf611aacdbf36e9b7dbc75dcfffcf5d425e2b4e72d52a0a28a1eafddf855d5518b34436ada75c9f24e740003922634d19fefc9b3b5c2379a4b4849b53658822ff2a6e9621209fa1839e8726d36779d7902337c38754ffbd4d4d07760561c7e1a59ceb9f2511c91ac48661608ce0992a13fcd770f8464c7e5f2fdfaba8d6e6e446fa0ce143229b3b519278a3b8f1f4500a651b3b3b1d4b62684680a4c7a918a611f2b5c45e6ad4b90028051570690dc0efd6982a54b6bb6c81fa9fb4d9820db65ad0adcbdeb5a598022e42f2992522559be4768a42be232b99cb861b83231054b76fa16317c869fe462c1977ea63ade234861976c77a159936f815bf05bedbb3edfdb08195702ba051b6e8a16d3925a01d8e0db60318780b74a1cb2c92936ea556d88d2c31b2c489724041521581e9485900a585f15d98fbe924547beb2bacfd06fde511cb0750962bc852c2ec5929e2dfa2bd87474e848588158d0b65ff6ca8a359ca792f51bbd129fa89b1490fc5686b7fdc468563501cfc1d4a5691947794a6ceb2bed21558bf71ef009d63f1ae84b382d550d75972c02835795d113bf2e755c89d2dd81f2c5ed8c0c191e6096e4ceee42b9f0485616bf02decb2562f9d1d7c254c7af2786d64362513cbf7d82184b45a9c3d68f462ca55bf52d4f41d5ec1c72b688e8bd24b005385f6278f7bab4ddd0cd958720123c5251339dd2dd0de32c4addfe7356270a5d0fbc6679cf0f24ca2dec26d6e6682825788fd95a7a1e7b03c9eed34a35439795762d6bc9e87c46c3ab14377c9d42a6f12216f2488644a81e764b5630f35ae38620d75bd019e73c2a235189fa34be80c1c169a56a007689650404cdbc6a2cb72301f1bc0be7faac90d342c7aa6699444e6cef75290fbb5401b7bcd7a49a02d806132afcc967e8a959dcbfb7478287dfd0f0d54ecb395471ff9bf7b6ea979d82bf3afc3c2df2b4e8ffe90fe7b97f989999b1d8c9f75336cd30c4dd7ab33118af6b425967a7182586938209ac156d21bbe5ce28ffe563dc70f58b00e72b3b31aca50d9de7fbff8bb99c226a8eb06086dddb6fa23eef495356837deaae68a7f28456a3af7d3136200a97fe2c6a4275acb5793f830767ebfe570431ebc662a01cea876de2e23eb58839c9c4eebb37ea8d164479c5a0839bce58fea7a4c72c8cd094bbd79b225bd3024b8e077b7d1378c75e961d7b9d6685b843c8f999e2a5cf3d2291b84467df888b306b83a889def608c03b589adc9e0a8da42c9cb4b61983fbb144734b73c807dfc210cdc9902ef18f3180dfcf6bfef6f7a9685b440620d08d3bfc2ed49edffae5cdd02f0d57a386f4cd3a137cf7c5c8ae067a0cb9e3195c2c7811c937a3e5fa9503683f052e259f7530de998290693d4a10192197dc82a9344a94482f9f1cfc1bd25791732a4f412938dad397a152b3434b97983a8d22fefe5c2acb2146784f2a04654ed260577a09f1f1dcfc46b469c29771a9ba41d88211e4d5118340232cf659e8371368be038cdeac375dbbc08f14e91bbda5845c5a9818ed52d5400623178cdf10921f26cd9ec139fb8777a281af858d70452f3ac32be799dbc3972253c0d8224d3093a3b9c63bfeeecf1bca0e3fdfb3a5762947a722499f5c2b9bccabec5903c6ff5a81f5bb5a62069ac923d754356346fd0f39bd037a2d32c2c153eb6dc1ef0a", "ac5151526a53655365", 1, 1221443303, 0, "72c01073a3d806c726e61c7c965d2ccaf45965b1cdf33b8ccfdec10dcf45dbf9"], + ["030000807082c4030253d8d2bdd86e819c4bb6adb8a77d66d5912bfee923557ed8038304866831fc8a0100000000ffffffff80335fc2cdbf20f6baf9a497ffcd1c4c86a3a48adec4bcc9f76e686fe7b26eba0200000009ac52ac0063525151009c2f0c7103823db5000000000009520063acac52526365e576da000000000002ac528b26d7020000000005ac536a656a000000000000000000", "", 0, 1726733575, 0, "93fa4e45718860e72d977f41bf6723898a236060d515601cf8c6a9d5fe484056"], + ["030000807082c403035675b50da6de9f1d7fd94940726cd66324ac1ad37fcb73cd7b2378ad24521be903000000026500ffffffff354df6831e85f59303a74c677d448ff6ccbea07ab6b66f21985c9bc561d55d540300000006656a656a6a63006b160a38db85c722abd381c1b3c8ab1c3b54c7cdb538b4817d65dcaf5cdc37df65b1060100000007ac005152ac5263ac137c6b03186f5103000000000953535153520065656360186a0100000000036353531d63210000000000010000000000d9b09ffb0100000000000000003fcac7010000000073709b017501d52afb18070b2a72659754bd876b8b6ca0d05aca8fe5f3ffbc5556f6190a861b4aded81164b66976bac6bdd14b5531b01a282c74af26530fe143f5591764a98db7828969b38d6dfedc9282b3395e698af722549b6f35fb84845700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000491929598d4c0d40df2a7e8b2835b449a52e766d7ff1afdf058e2936dba5631b10289985e4618b1c06ea61805df0cdb290874d7e9318a729653902c9281528d581665bc5ebb5cca48d28215eda5aa7f5227fd218cb41267003d4096ca8f82ced77e10b44c4bc89aefdb11e8ef734417f2ec8521fb25d032663a070805e2e7cd002081732d7bfb559d429d88947c6f02f6b52555519cd81dbc83dabfccfd307431d032791331614d8f9b75fd568a87d4b09d2535ecf28d8b1c19c4ed53cfed5843d830a0326b228baee23f2e36a8dbdf76184032aa7041091ba3ca5d26a4c30e2cd2aa22b909d06415ac1ebf1c42bee92828c6c0ef816f0549af1113073c4ee481276e40317c7bdd3197c8dabe950855dd4e24c38ec8979adc72a20ee2890beb963be6bf7030c23451956e3bf08613766c0cc669607704ad256fbab874454058cca9c57165a031e25df8a2436761404cfb852f6927578b0ca133294fab66fba3c8f26d559f79f0209a6dfa43e7f1f349e26548d522789826a320cd0fe5ab5ab85db61451a26f8c103115a8bfd2d341e4024e8087896827e74b0a1d7079d5766ad812432109c9c6c9e1e14a29164480050f9ee5c583e58b3353db6f38c05c90ac7dd672c7e3e5d7ca242d47a29e61d166b044076cf76fafee269a22fcc233074f7fdceaf3d0164cba903bcfe209fa0c7c76c2e897acde79c644cf47a5a3d93d0fecd839e64213fe93b27e21b7e611067ecf87d33292b4e639606cb1fd5af3934b627a1a6b4457d47ad2bfd2d0f3da3dbbff20252f1c12d2c95c132fa89ffd5a32d1b2264b181bc818db9b592f0665050d91a067a69074a4ad810da4bd385177ee3e2ec2c18af61c41597ad0644d72aa39fd3b664bb4d71adaf9f29dc6fe0f13baaf8d58bd05810dcbe1e20a5ace349e8a533553d1e6fcd40bda5e6c34e04a3f3253bd881271404f63762115e4815e2706a59ef23e2fcd2b1388e99d43aa16024a2f8d7639deb6fbd1844f8c9fe08a696e35cfc0450b102e822a678cc422bb9b0e14f8b2a61913acec44c451bfc110d98d35dd9f7a69b5a08ed675d69f73701a0a580f4eb48b7967ef7799de2426169c0d78bb2cf2ac5afe533ad3990fd25c59b725cb69039b136d5de18c284c0502d9a1f3436877d20d6c05596e7712d160dcdc8ff69a328f810a5149a29ba831790151a5eb14b881d596b8cf17af111467db1e0f7fa792e2ab1787e23e3db99844ea4fc4edd000c6825c6814e35eb2e50cc84a8dff41d017857e3911bddb5af471398c00b36c627427940ae644caa574f912d7143bd37ef90fc304d09d10bb1436cbbb33a9bc3382543cedb7a01b324731e4fdc6d8a6ff83ba28d491bf494bfbedfabba2af1054d2e2402071e98ae3cfb9793e123ae2982838f8f163913f0b58262f399fa35c10c870ebf2c3a7fd43141524f8e02f016d8568bf0f59a72e5c76405645346ec176c17a4c21989df3c726f616567d2e0c3a88ac88a7e6023576e955955545fdda33674acea5f852560301e02e7b53d679d18cd81ee29de4c456590c25788c774839d326b87125e7f13bb7fb5c59f0cfb891561a997860a9650436694e68ea3cc649729cf5f67c1a3235f3d9b4797931cfdc14f245b4ae2f839e7a7de08e8b6097f372c18f6eb579e6554d38d80ed3954a7951b956f89c8159e481d601c57dd4518b4fdbb25158af2783fce0867a19b70f3f12e1ac60ca94da7afd35fafff98ffe84406f900c644d59c0846addeff2800111e29902768302bf9a1d89dee3f98dca9a0f70a524e48d01d6a5ed1b20d1a891417eb908057ed484daabea03bc5d7ce97d14cb0a9c42f6e27f0765cb911ce2ca057be1b1d9f5441700a009e9f7c2892d378e8135084347aca677b12c12eb0faf5787b68c47bf9f2916f96fdb80d8d7291376ba68a9e76f5209db999e1ac0ec9e62a37ab5a80528ceb5b46a16aa8be63e0566476ba47c2e64e04c07fa1cca491de4b905549d4f841f1b19e5aa5162fd92215f2d286f9763ca29fb62e90c1feace8c15315d050bd179550ae2f776610580448463e0e1f4dd9d98aeeaf58dbbfab85209ac546c6b3ab66cd57caaa533ef0ac00deb7bff723417f809c5bd145025682b73712c4da0babceb9afe664dd5820485c1d1bbcaa9ee10c12247770beae3992d26bb38e56356e8ab80f3ceaa55b7faf69aa7f4175aca0836e725830103a0fba694f78598e919c629b47273d7fb39909aab9f1104de0f811549ca24255a6783fbfd2eed69ab605968b47b781c5012e790e9f8de6b2f41c39fafde3ceda2cc869c2944e27ad2baf7397a6b339c914fe80b88f571446812e816036430bcb20ef7b96b02bf919f4216ae973447583515bda2d4ddb54342c9122b7c8961636f59700eb5a801b887c1be46248a05d435f634a92ca5899b72a02", "006a52005300515151", 2, -1471148157, 0, "9c808ca475b92d50f8cbad097be684151735c680855ea9cb9e12e162672d03b3"], + ["1199787f032f774c49b4b0e2af92ae27c4c3350c746909fc5f04a460f3c604190b03bc4f3101000000046a6500ac8e3de8b121dce3acd7a9274f7aca63b5b8001036c0597e222e9734cb1e7668b8040fe5b6000000000552ac005200ffffffff18b591d5c1c47894a644543e9781768d03ed70f497b25f1dc02edafdac7e50080000000000ffffffff030d4d3e00000000000252ac8ac4da020000000008006a516a655152514ebc7e030000000003525365654c4a2d010465ea040000000000000000000000001d6067cc3328a16d552966398b3e27557888da181d7210dec746567e8f6c97aebe47960e95caae8d333e70057b1d6ba522db7515dbad5fdb9eaa34f7c0960ba743e982119f76af994b799f6dbd217365b885302516ccd97e3f0b40ff0f7dbaef00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b16ecae12067671709683720331f39e33afccddf75574059a5eba39e9e131405f965097833222562e95627f59371ac520b5ca79054a1b87516b55359a30a3a4c55db3c4c05679822cc825ba2dd34eb7be9212f17277575a1dcef32bb8dc86ce7f57276598a58a697a08beb2869a91cc889b554b1d1cb6406f9c490a24c81015a0328cbc97e2f6da87863153e27dd40473c5f275fc8b6b0b13d642e765bb313f98d020e0b96eb99454198623ad4dc394e815ece66bae524186af50391c808ad4b87d70a01c3b4d26b1028657629112663795156e617d3270f0f3276547c7e59f6ed1a69700efa987a5f37523290afb10982328bdc1df28c4acbba52e3ac74a1bc16bd81032313d866f01ef5a556ec443c0861020813f91c5aab94dcd9586caec0e447d6fb022a7f34f2a30f77d1db80c33bda00624d5f614b8e7a1ae581906143809f9224be0224dfe0124f9d2136fa23ff3fcc59521fb70977145e0818f2635cd9902d3a33eb0200ae4869a4813fc3903026569ddd9750279f71cdfff2897d3b85e2fabb5e27f002187b558c30f64fa49f9987913b2e9693a34cff49cc5985879f29ddd4d20afba827c2f6adfe5b9ce128b29b8056cc9a73f309965316a7349c4a5268a24c94f9bcd947b00e5ed99508d54065adfd1ac8e8ccd0c2df2145ba592accd4d84e3f1eb31c12e475635d883313329de682c3056ba12e116eb81fc292ed8e43292cbabe39ac838d6287679b0d68d538dd06e16e2ded34198487c593f7e827ca466a65fb1052c3a0a5033528457dafff722d8deaa87ba9d74829cef2f828a496f15138a2e4c3325c6c8df6acfdc9a28d68f1ae9165be812b870708dbf9aad0f4c9cfb0d95f23ebe083bed9c8f5989b51265b854e10e44b486c97b3ceb4bcb9f3f21b0a81aff370b9fb3a4f33433ab148d06433ff03901a6cb6ef22d91e70e19eb043db63f30b1c9cb30ca7c6c4f05984c0d692c009b19db0553daba7e7adc537db82362cf23c1e357ddd09277d3ae2dbe3d309aef810b131df24415ca26cc3bb24049839093845d2db29bd49fa191ab6ab33531af6a5dcddcc40cb311392486c39642958e587becd73f84008b824a798106249265f8887b020bbe76c7202e835a39a1ad3631452c326a6ec0447b200e5f171df4c905d61efb190e0322dccef9f8bf66aa4ba4ae18cdcbf88fc65f05956491084bcb63c56c56d8c570147dd73d9531e0bb12d535075c36fb25f2553ac51b2d9ed03e9536e8493e7dce9d7b09c54fab65be3755644308cbce0c0f14bf5f9201970a33cc19ba4ff271c17e79b5f68cd70d809952013b811c750547d5150c8183483b7653d666bb0e8e5c2ddb9441ab2601b5480eb2af8105729711bb42bc327b634b7e022e20c18033539d27940ef3a2d8f9839c6280cc0dcef065ebd3cff0a4c6480091742d0a12c9e00c8a93733d21f3f9ba3d2c4990fc432a62b8890d026d8cce44bd3131d7a1c1c66fe252d90d1d893b9fd051bb592102051803c117367ffab1b2e0fd8787e822b65c1c011adacd86fbd7648de84f4f41f7505a481339fe7b0a339bee228cd6e0613803a66a6b7c560de5cb85452e06094691de7d52d087f090a3f7b0bdaae5cd0c85170304a2d48048c49cd8a077659d5de51acc035ccc7448640ff7a47d6f5869c776e77ce7d070056b19ac76a1ca0585bdfcfaf524be92ff8dd39b994557fddaf8e45b358e284998e0146ee55a5ad1b881ddcb9b52ad79ca1fe79ecd6f56675213d35762a5e5efee2b0aafaf5a051c48b851fc16da462f7585bb99780c2e6af17afaed1b30e5ea45dc38d2adf634467a27958fc447a6e880430cfcf27e34e25ed327172b6dda1acbefcf8eccf04fa4b3803b13c608578dfb503ad0abbc5c054814e85c700e621336614c9cb2a44844ef529b5075114d088313c538994bd922567137f500b4a52473ae38c2fb16c51dcca6d284d434224cd68e5f582ac2bbd7693a35acba25bd5dd6f2519f1b231731887b3e409ed46c5f2b8f496b6ebfd1be114523161f4d8ae34a44b41db8968849dc3e68b27cc220da43f4dbc59df9aadeefa2cf40587a61fab88390e5ef69b5fbfd362096283f5217332cd35f65190ef24b01e5127a0279197e62575ce91d29bd8724c33c355f9b474d382be2072cb571e433c84fe49ebbbcd154c32ca96f931802c6ab51380b1dd84f118ee15dfe3da6af14f90faffec7271a7dc511d62f5a878c299a042537ad8e993fefa26c3eebc7c1891989d76a478f3a296899fc46b58ca361c05399d03f6a3bb9fc05eafe794809ed4d84a14b6fc1edd6fe60b1bfb3709b35d3469ecb3a75b713668e8b2c9e024edd29830f15cea620fd2caadca936ac388da9fce8f8c45003d36b89f344f56b17de89464429896167b5d519a904b6821993bf0573fabdaedee93960d", "53", 0, 1221588783, 1537743641, "b49d8b2ad1d6aeb9078cf3f93f131a8f1c582e41dfc699f2b28b19e04d73ae72"], + ["030000807082c40302ccb9334a485469de8ab7de99599e32059da5153ec0c2028c397c577ed9aaa2e70000000000ffffffff0f7cd19c37a0812cd1f2e007846b5cac37e6c079ffd9a9362aa1f36d7efb307501000000036aac522e5c057d03ffce390000000000035100ac274f60010000000001005f3a56000000000008525165ac51525351b9640f063415bbed020580e60000000000000000000000000058549365b633e38155b05e372f3e6efe74e5935247bb0394096202e0fc86bd50d321cb992788c713238d9efc012bc60de263b55f049fa1955d4b2004be66140540eca52b788f95f60a979810ff2c0c51e9d88c0597de7b90b9af88cb645da87600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d75fbd2de93b8618551d14c32f057ea240363d004df0affef2505be5c4b89a08c974028146c1eb3dfb254de760dc9a3c30498c92072d50af3bbda66a5491db4d32a07bd24743d6f8f29a301855ee36c9dbb25de64d4e5f7af30223ba5f344c3f8540e1c088ddc10bf3233c1249b7819eb4ac8df1618a0b486026bb18bac08b0c032b01042477210a4228bcce6532baf38dfa0c3f3c19d57120c43a73e3f3e22f49020d979aa7cfa1a9f2aed11c0ce7e89ed124fa24d7731c2c95ad012fa63cd6a8400a02327f1dad1a80dc3cbf57a0293aecf5d80543f7c5cf806d6357c9e3b7957b26235c836813e16e458f3829dc405366d9b7f50ccdfb74634d1b69a084af80537d020cb8cadd60d91f546ba1fff4e1192a7b39ba545194d59ae63081d5240a701842031970294ce2e0201fe6ec6f8bdbfb46a7133981e72534e83e60fda355c600510b021f79f219922d0216d82fc46048be8a8dacf76fb16d2363ca20952b3227992777022edb1b610141dd62b05041dc7cff40d0222fcc77db07f1d0fb42c7df297b5022030b4b4ecbd2e25c17a8606ca11f602dda25c28b0cc5e35041e1de70c9e5ee37fd414366bc95823569d0b61353e7d301d5e411807c7dfe2d31e4bd7afc2a1640f29e3ab1006450e8533023560e1926273ef444b08cbabcf3a32259a18ae86b6302d151f9adb90e4a1fe0d92d12cd9ce619f4c36a8ef4371e1618173c54a8861bd3173bd28cdac409a65543d2a7740b187441f260400a5056165880a093518f0a23fb1f6e3aeafe1b3b009a2a86d7b7fd3b7846aef2bc2632cd1c6c7c42c92908d48ad82c3a3002df62ce2c386d64870852db80a6a9b2b96b3818358aa331a61f8d254aaa73121c9fc96bfa89ebe7716625be1ac51b223cd60ce133b900a454c891756c71c8e59a321abfd373dfaa642e2efa519a3fb66a8fe8b0d9fd3dd6835d8b22a1acb36f55ead763bfe5b2f7c8f431b3cf41f45b1f192343d5865fdeb74b204064b0308bad456ffdc2aa19186d511029b32a7b08f9c2b8365f545322eea7497d5df2c5658ff25e728c827feb1fdf73d1ded5506a26468bfb063b2fe6d794a38579a23daca205c62aeac9ff64f0cdc0a37a6119676837ef9edee9ebacc0b07b9a4d542ef5ecf8bbc065bafcf6d588f0c193f075ae9cce2127c83ec9c459c9329ad9c436acdb0d23e32b9e691c2775292f1273242a01c56bf77a67807ae1b7e73b36b59c94d2b36d3620ee67f06c0e1bd5a7f0a342dde0c732046901df9b99979c0c33361700327e54f6e7edc2f6ff3014b2f7c2e3be18b600241fd0f251a317d18ea508865b05154632fd1d02273961b71edfe52694af4500adc1fd99eb1b2058096a32390872c87a0832dd5206b17820aa5e7a754a180420ef21a5af1deb2ffc24795ecc732426846aaf67a73e30a640acd8dc008464fa36030392b15eeb1bbb04cff097ead04ab20102cfca87385e651f62929552eb872633d69fe68399ecd0b5637c0acaa76636ae883a5ad4913c44cd1887df47993b4d27d3b3134d9b08beca14ec85a6613cd3047632d0556a73ef7fec4e72a17705fc9ee6d73ac8b0f8a0c6035d9c721b8771236b957ad80103ebf05e1cb6e553193159919211968b7b19c7aaa723570deda50f4c9b9c7e2dc24761da1aa08be614060e26f1ea497b6b24096fee53c44b67cc4a65ecf44a33c3d767accc63ed32516cfb17458b9b9efa4acb7f5f52af890bf0a75613e018ef58f078e5faaeb77bf066694b277960b87aac20d0f8a2a58b35c5838fabb7a46a32d6c8df966bd338fe00b09278e45a7d31d12d0ff586d142619dc60760759ade28878b477c269d261d27f0fb918ffc909616173d8f5fca99787a3d1e28c3eb7170abfa14d292cd47c5938309338e30f0f6609221ee49e52b1b8d97253dbcdec3c6ae3700baece1380c303866f6a7832189daf64b23e519d2d5a1fc208507dc4d7581fec9006052ec1f38d4c8c241eddfed385a3876f02d3b9c23bf81d66f76d0952d645382c50c4f7f4878ad048457fd9f145178195c1db2f80c390a996576825a15b91aae863d0e6837c24de10c14dddcc0fb822bb4adc90a65ca0438818dda7c1534d37005be7933f8e4b50bf738b94c12bd299fdcb6f5731eca31d9e2da5e1c24f15b94f28cd4e555c296e9533d0c7c06125e1610d76b3f1281317ab4a06a1b5ef6bf7537bd723496aaac11e625a5e7099bd317ed8255123e535de704078eba99ffbe870be4b507e38ae0269f2ad23456e03c8db7e240936af313edb4981ac603650000000000000000c2104a020000000038fd514b94418dd90144b02b34d1cbb614437a29f97fedfafcd50e40fab78d46dd759120b2e2c014759bc2bfaa79f3aeacf3cce8347f640d12fd8f2f24ee7b573bfcdbfb1f2fe9c0632ac4944466c949aeef2757176c9f611691a00df530c7cb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b0ac99df12b9c976c715fec1f2010a16ba77a5eb45a49bfd35658407d661286dfa58dbbb3842b1408e6ab4ef0b84c20d4a575e74073fc915eb08aba373970fd82f7e53ec189c4b60116395044057206d778f7a49ac0b3c2bc7d79ee7bb5c1ea5c6bd146062dce84c6c3d12eb4481ecad206d0414304c397702a76d2a4901a1203178b3d17f06781353a5f1be951f3de6bded7f4ba88265dde31556973462cce02032efd26afc751f3a5266b1224c3645cb35a951cf21a4ead0ce8da8397695823250a012ddbca974e84405533e8e1a7320b72f4efdb88afd7b29108685a2e014f3e1c73eb197be6c09943ebfc780c67aa9ae35f33090a6c9313287191541d7dff476203011439b665d3e3358c079606f9772933a5c2e08da8b3b92ca3d171842a5ef5e00202306653862eaf419397e2cca0020774f77031170c327f26012f1f38a50002d802117f1a84b174f78c4d296f748be0d499342fa5eed06e85fdc91ce35df6fa696d0315e3e9e4fc74f607e0f0f0b96c65f467555aa6f7bc94f0e7f45650ee4ca5a21b031041b071f86ffa3a30d6d2d536f8ec91a2046de02571370b062a1051fe6bc1e62adf54e4d8a0f9e9022209d11d8d33a2a07c8b16885c2b421c1ab133b6b630376e77be1a51d7ae4932f4dfa82826921d8fc834ddd489135890f95c4d86df45f46bf45af9e48284320fda1c0527efab44e5a48d928bf26ad1ee77749132715dd407463835deebda86373b34c12dfdb06bdb35aa17a96e5bece362fe7c5116ac1e5ef1ea976ec8dc39eaabed26e29884eea385efdb5371c0eb732553ae80beb04d9366c14ec0687b30bd4e60fba72663bc4e02e1393c28936b5e62248560bcb62577225768055652ef781e1068d82ef07bc70714ccdef34d6ed4ee647ebacf3374b68460cfcfada437b1233d83ec9fd617c326647a5ce13d0043c6d39bf3c374ac77c50399ee8421ea4cfb9425dfe590d0f66b91effd6c2055a161c38f3b7a8a38fa9c1131a476a9620b40b012d5cfb505b6c5c5f171060ced00fde7dd7d3667f3b7d8009df932646474bce5855207e0620b4f8383a539b508d8147a97a26b871dbf0be0170d1ebce6286d1f87e5900f07cce8217f9a8bfe373b6c560245acdbb7f257e52faeb9061c6968cc01bbb43a6f0991d255b0ee85d68cd1c96d84a624a3200a0e8e8905ecb8fc89f79d6ab1474e1d77fcaf0af6708fad37cbf2ef729307ac0aff2ed32d1eb4d67b385403844f9da10481d4ca8dc5c2ef630343d9a7a006ba2f89af1663925338427bf25ad5ac75e4e7ac32b974b64c21256416e4e288d366ec7d0cb8f84a257294d923844ee8121856a45b97ced9d104f2bdde5a1f321591f2f4b3f74fc9384df9f3b70cab4e5b68ebb3636205f6ecf6e616a8820e5a3afc06045166e271068ec9385ef376e5bbafa698d58ad44885436f25e416ec08a836e5a429caf0a8fc548fe6b261efd46070b57414f067e2d3838868ac943d44c44a4402264d729476fdc0740ae44221b752a102ecaed85874fccf8e50135effd5c87d19861b79a7a316309894a9cf7cf4763e16a23eede359bf6ca3d78eea1646a7cd655d2d6854347fe7d4c39bd6996b00e5924effbda2e5574788fe29df52988ed52a6c5561cba8d6cf87c3022ec989544eba2419219accf3e1975bcd762eb42a9a1cfd4cb9a12614cdbcf1e49887552908ae63de67bfd2e823ed6d780f5dc74426a3c70c4fc3a75a220cf0ba9412c4a07d498db71eedbf28ce5f7ee5dc32c1c31d1cc54c0129c7c0d9d19f506e2c0bbc8a857516325e1a325f9ccce9acabc73690c5d570c3b7580eb4c612d6dccb8ee5f9cdbe8e2667bf77746f364881a4465fd0394f3d5e2502c23232351a52f10c9c908c038ed2c247a22bd1f8636e05ae47fdd95c0a8ab16694fa40ebc8013c0355e1af240fde98222d8567f15367f2beaa2fe232467010da75473e2bd54296034186e95a46b644ea7c4c748f2ba2104bfd2138784984a13f7f75b5d7ea015e5d6681fb94f2d1a13c0e1c61d4c6881f9f89986d3197becb3bc2267b96ff9e0506f6a6cf496b630caf473fdf56317012e691f649c9623ce98450602dfccddb146fdf7b1bb267cfb654df1a2791e707c8ce18d0509660e984732bd2ee50953a9220265866cd34efc1dee456b88eb5712a55573bd458fa2a5e33f1e4a6d63def7a787e9516383e8e2e27bf13b50c7ac309218bbcf38263ac4aec4e60ec69b1fe38be2b5b8df759e8be4610f9b3fa045ee813a09621a287c7248d2e6cc58fbde92ebf0118bd47dad967ab3098689884fa9d1d6025702e2e29241ee4777ea9c081a456243e281d08fd6b49727f175d76bd1158088521c3861a36a74d1ee151fa23e7f705d7c1c8b40442c33aadc29853760e9a916a9fc5eed9b578c1648d75a2a104779607", "52", 1, 1577874246, 1537743641, "bec62a88f29475957f6c1ae2f705e36791d3849b7023d69d70995c23a0d06d05"], + ["2227fc2d02c178e956b03bdec4e0204d2380c5c221a83420cac9a9f54aeb20fe2798d5125e00000000016ab3d37bf80515e8aa35586e22d2c679a7954d35db190f7cedba83a31e0114e2432baa503801000000086563535363516a63973d52dc016910c90500000000016aa0a9260102f94d0105000000000000000000000000840f0f64edfcd0776d542034e0bcc80787b92126ac8e338426afcdedefa28548adf1b473d557c8101918cd32c020e2cbac4be9fb2bd0c6d72710f78b85d8c398f8ce01ea5b690d1dafd28cc601d47a276243f813a9b4e4ab7177007babb1e5ff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f286978ea018fbdedc826beb6238708d00d5f09133c22bafcf784b15b5c8b0917fa0a4f6f4690bdd9a868d51efe0f989c8dfcfd5e91c9d207bbf221b99847e1183724ab8fb3fafa78287ed4bcc7581bf3f81a5edd96e057f48a0fa0d729460739b1f0988c67193e64c7172c7c2b6fb942dfa945e461107f917d481f6aaea08b0300a74aab9f68074e49cb5800b02185fd9eb3ce8e8fdae4cbdf5875a5675dbd6c030cd677fb670720df00392995f79e3e55dffa77bfc18ceb19f037a9a3de1757fc0a00f673ee98810020bd76dbf562f8593ee4d284e84854ba97c8f3e152178f26acc9adc6b2dfedb1578ca841f888f5dc956ebcb3f24721276a5955364be407c406020b356611db0170d270d2d5f1e6096e7dadadffaf7dd22016ee2d0a0b9609a7b7021c111ba467c03bae07ffc085a9f0ec05302755238f92818255d930a54a1a1d98032a1f36e4c61274b9d2d864fa4e63c045f61ea044c76a7236645eebfcb9dee2830313401c47749d07ea26ebd527e30e824e845d8725711dd921630d108bcb847bb503284b94e2d0d6bf1193abe45a7de8f7e956cece8dfc61e7b099006e4172397e300a5f12189007112e6fbf9b9cbb2a520bbd13619bd45008837fd23918be2c0ba5dd7211c22c229cdc9a8a659a0769f6207365d1ef09ff14ae1c07860cbaa0d75ac39bf6d38358981cb03126c888a60e27ff558495d86ae6e2cf3c5b5cbc33ae7fd2adf5b73e46b6892c4dba876f87170c52d5e4580db6d8aec93e21ae74a3e3ba564bbe679036f9f6c64de224c091d028a0764341f25d757285d64c43e2dedbdd1a74790b34c6f4aa9107b0788dc7a348ffbbabfb3a2b55e386319bef80c71616566618fc308b89f06f82e84259abf8e6a6176e56ddc93f0fdd3b118ad8e0be5520896dc7d93ff4db9a2d7e52e2002578f978c0fa375b90f1fd5dfdacba1cecef114997af4522465cea57d9ae8290a16e688a8d561de27c97989c51426e322d3641cdb23aab1ad467b4a7d22a0307f8e927d102107f1f6e1957137d2e8e87b3bd1d1a777641998d1532da03b24997ba0c1c2f209eecc1e2c6f8dbbf68c9ecd5e79cc612d7d66fc265d8b9ce769744742183cb032d0dff972ba0fa25a981a843468003160102ddeaa047a61b5dfc6e1495600a9410e303e1601b9138c1941128df11a889aed06e9bfc85ca43c7b31cab1b38d4d6fb90db33f8edd42aac2fa79d0205468e3efbc013ff91ffb7ab7008eec62c0fedb5ed0fcf752e528ec955be7e06ebead1cc70ead66128e4c599d522e61675a4c8155be9bb64607c402164d0a7d42ce626e193ca91227ea55243c4cedbabab672cf9aff5559701be6a76c9e860f9cae652f2f0028f8116ee3ff208d62cd9ef46aab5b63fcaee098ad128fd7892302ef4018f10e13a02e77c5e904858169f81330585601c058e96135aff640d71d07c330e69b69057c8d10d891e5aadb58646f4b81f35f4fd7ede1881fbc2f4c1a6b6f364d960468ae9eb1f3d45d98be415c77573d361837096f7585d1458bc3e1add785d9e3641aaccf3c33fb56eee1ead466cb68ac82ee1419d93476c2d682a13b6a6d6126476ad7867cac0acf4a10a0b0b2f64af3430566c9831c3e1f8ea6851de42be03a466650369f23839da2417aa1f048e706dca8644903986ad90e6f9ef51f508e5593936b68ca013b22ee6f9fdfc1f9f8faea7c3cac05105f813dcea1ce9a7c8beea6ca2971ec444377919a3878905e6f770504265c01f235ed831a42b044bd640925f6d35fa40523510a49d1156aac9c1d61de76b683579afb20df8f9c715b379382867989d55434b23c81edce5a995d1457fb0aa6287a340f6ad8657d67656d7f8c33c076833379f9d21543d99d4b2f4ca5045670c17374e69859d282e838b37bcf069657fed1d67bde7c090a2e407a6aff2e7bb284cc8abe6bc5fb621877b8bf0c233654b58730c08642f7ac2774a1fdb76385ca04a350cffbe2580e6b6a2118ca31842298f3463f83c5f26235d7fb4d739549c782540bd45b7b7dcfb7f466b48f545887f05e5f5d0ef8b8fa377bbc22afa84d58d28dab83c5bf1b76fd96dbb439d161da0a7c2b2e9fc8b35af7b339d82dd8d063b9fe3ec734bc4b68100d98a33e2fe4fa0dcc028d54b53e1d227031dfc97d9e20f593a101215dc39230cc82823be0e27fc19ed2b052037f42f953e3f21bc6b663221534439a70b7437bf3ce11cb55f330f341fc763534deeaed7da74ff978b9784e64e1c6eaf580e0b3eb91eda1d26792d9edea2d903fc933a1e0000000000000000c9405a01000000001521b03ebb6c146973a5676ccbf82d1f550bb079ae711b8f9eb89651e35130e4c59525e53a317fe997e6b1f18cae2819fb61a17af0beaf0b756c8cd4ae164199d3069a40390d132fe1477e1edfd198436b2065fb6a9026ee3bf1d8a48681317a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fcc2702d45a0a097ccfc5301b5140e322841a9f767bd7abd5e09778f0fd48fcd7f906f69ec2e6822f8dfc15e98d99b4ea94ce374e03a2016825239ac0fdda89fa267b8a45e530ba93a0022d1671e158133b3bd6f97bc319f8ece2af30d7095343d3059866183ec09f4c8aafd1d57fcde734760ba416d83766ce08c497401f2fe0313638b9feab0989890f9185f5d64a8fc8c0140c53bb34b19c2460d4d5c7435b2022afa31e354da02bf95dc99afe912d9db34f9645b88d68ff1b79e6f24450d170c0a0627888d4780ee8080c2529b2ad59c637c701127a8a54100931d57192ff7883506fefd919643ec3350348cfdf8e2dd6d7e31b0a94f701879915778792b5f179d031134409321b4e4bfcf7fe80eb3df6b0bdc1db5ce08c1505b09ff83e531c29108022d86657adf29bd3cb48acae04f3e9b8a2f60a022eaa79465c582b3d73812dfe8022f0d5454baed5908ab70dc65030ed6790aa7e6709799cf69e43682163b13aeb803254724fac204589db273e321d82eb213e8481e97a5ab9aeecd610156584989930227c904d01027056416d38cde4a4df17a2c02052ac06eee5502bc1f3cd4c133051cbd73e6c56de8f653b9b68ae77f53f999f1ec9c1f4b64589c67c104af55b756a8438368f6a90d5d351c953f1a2fb016d4d610b88845cf107fb35f1490000ca719c4070fb1aed8da133790e06a036406ab157a3525b7f6a6e2235a630d5ea4b0517db4e71a6f031478c8dad5ee597fa48149ebf7c2bdfc97f959e7541fe70c207ca521b97aecc2c85d1e4f4d789d5dd9f843934091ee3284e4aa1f59b9c1301ee188493b09f05f5bb6b9ea87ee61d051df15e8b7585a294de5ff545c7ca274fd55c0438ad2d58ddc503dc5ee54e583112183a49f3be925a9670c2de00acfbce2f302ec6178bb023cad287902372561bb66b64c54e372328eda568c604698457fda2604cdd9c47de811d790fffd159492d45955da30bfab21b820e52ebc70c19873cd68510b8f669ec7c06b910b2484be3a0614c995c748eff27bb02c0372150451c276402a992cca6b40d9779a5bfdbecf34285e58dae0346fd8fcf0d0ea874d24ca7015ea65fa6c0be59a32f727ac9421dcf8fa1f187dc466d2b80f0b7eb2e9a84866f90b04a2f988d2f2b4bcceca961776b36f1121c82d75a473ed3eea671b2efd1b397d9c3b4ad6f1e0da7859a6c63971ef3cb0987e3c541d6b7e966ad929e93351916213cc479b6d64094753072a24ecac6c0c4ed5513faad9c79579b83b31718db5690abe39f183ce1e86928edb1a14371edf76c0a85de52c3da84f884335555d1761dd3c09e65e5c1193cb3d10d2bdd0e6fa46de4cc7828b145181f129b328cd48d9a98574d698b329dfba59defb75837d8f7e38ace00493c4659ae20e4fc2aea24993389ecb5db2b345e546213cbf21106402bf805a7f808b773fbc396871998f5f8193de82723394243d39154460ae353e3fc982193c94073f61885868494fd6e3efb07437c1b6d02df9fe63dbc059d9315485391a95e03cbca23c06547cd2418b815173c7bac08bde5f2f53c33e378e8fe21dbcaaa004b4747d73a1a8bf39f8958b47958c7d7b22ff4a67b84a4a6e664bf946d70eb95fd28191fc6ede232ae62f88e4082f7b0725ea77ffa946204d98a6310da7c9430df3c61dff6ac9feb399f39f45a7cca9b4412586a424fb5413a0dfc9c2199c8809fd1b4ef87b1dffcfb60982ff313212344505382fbfb6df37f1ed0c3a45e758a582e4016b0d6e8dbcfa38ace34a2d17c9f55708cc0c8f2b4cddc8d9dcf8b563a59770e5179d2270dd74445f9cf12cbf11b5c23603e697901fc0b04c35294a10b40e92e7b1a36860687c4b11b57d2411d852cb38e4f947efa5b2d8e66848fa278aaa8c5ed07732f629178ec27c12076e34161e168055e901cc6e014a428eae2567d8c71f39c75b3d243a4bdfa746b6d1b65012b287787cd0b3ae51d6b32318037c750e68ca02aaefd086d3b47090caa1edc524a51b0a9302cd8886b51d4f106f62a7e068ab2f4d968e4c4f2b4cfeb93281592716e5eeba96c1ae2fde2f808537d2c90058d1210e1330bed2666bab59e7b73ff17457bc5a513f31b37539e48eaa5036896212d7493ef246f39fee2e1778dba379515eb7f7692bc92b6da83c338e6eff5e2fa59a7f178eeeb5855134f5b30660d4165505ed7929a55104f98751b2267cd91166aa6bac235fe090b990c9d18be707e146195a0608b12bbf15ec82a27c221a4182c9f6e8a9cc504c52b312a7ba970312c95aa01ccc5ae8f73228163809bafa1233a91ae1c8eaa22f737dfb1822edd74d1f4380bd1bfbac13f22dde4ebae0d43e0699d3f3a6846f6259b119834898680449fb9f27ca1863d33b7eaf6c076f59cb99ca7955203b06ac5c082b8f4c59fd7cf039a303", "65006500ac6a6a6563", 0, 1360803834, 0, "3063fe6d649f65d2f6cd86dc8d9645ee48c7ae6f7dc2c824dda61845eec9942d"], + ["030000807082c403019309719ad6c6b605421c2247cc0c320ba7e3bfbf66849166f76a82a0f55f436c0100000009636a535353516a5153ffffffff039e018e0200000000036a52515fb78501000000000653655363636a97a9c901000000000463656552000000000000000003d6dfca03000000000000000000000000b2dafd43d253c1c3a2db68a5e841f1063d64c82aa1974e3eb339eef22221e0785916b7d3e6262d84ed15466f9ad0ce2c19cad916a212fa4f0daf4b168798fd983ad511deee4f1d0d2eec66b2410255782914ce2d6ca50034022a05861dded65900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bcb7d9973ded9bcef84711eb2034fd5c27b02848a99fe6cbad7598b1f8a31302bad7aff242c56733c0245c22bcc59db79831dd3131e9a457b02e8b291330710894c16a2dc228608ec1cadf05616b4e013cd01faa621efbc5a3456d687b5e6e8c1b115c02e83d325b2e13fe55411642020b7b9c4060851aa7964bb16a8f9130370219d2c7907fbc2711c0ee769f9c9c5b2b4d796bb01239b5e6cbef1606755eed1003237e9dfc7801a12f46f7b012f8c91d3f4c4e5240609643cb01e41896bc2b9cf50b048e3806662a4ca68ad3cde81b1e4f0618f181c5ee233fa54621e67d826913eb3fe044abb35ed1efb8f9e4204ca20f5b0169334a4e74ca2033e2c6b3a6418a04020d34db209921221d80a4dd063f375f13c27232b31b35dec94991c43582a585b40324b75b4485f9a198bbede2f0b4ea3b1279abbab62b5bf417452c031219d127c502295ebcb3cf59b0603b58cdda0bcb0c01479656c4709ad68a511a5dcbe57f2c00031d33d950c45f8f56fad09f4100c66d5d6d63d99ec27b1cdfab7c76dcd44f799a03003261689d9c51d7d9101e7ce7cdb5684f12a07d7e45334378030fdc445edf68979c824905601016cc05325b6bc9669acefdc0d856dcc5979a0dc2761d08562157b148cc9f7fb9a01e87d7e853490f732756de97f021537713ae43db24e0d6c59494e761b507668ec7362df04088f6ba83ee618cacdc6754fe947404009ceda4688db88454dd10784b6715910daf2b26ec96fe4cba7f424f0361480e67a1a9bdbc93d201f9db510fc5ab78ca5be3397d00adfa4604fac5ef24a24208599d7c1ab5a3ae9bdafb4740521cc0e8cb460586441cfd933380a29e0e5447f134676a09f17b35bb89ba9c9309638c8a388cd5c12f9bd91ed570aa14b0e5429b05418f8677c58eeae83119b123e477287261b84737e3b4b36f28716e85ecd65bd55199b743b1b7c0c6906236b6776a69f4570e08143a7ef858876cbdfbcffc8d1b515bbcc9190e537bc8054c3d060eaa21b93f3b2f942f923841234656df4dc190725fb41029dc63f556ebe43059bcbdfa76a69df2cb00c432de6b8ec8cedbfa9d3d56b1a16e17b7c6f3657f5f19557baed0ac60040fcaf573d20dcae6be77b931a153122ce464e8c6e1ca9ba847429e3623c2e5f527d5077b8b05a9db8589c528b72ca06c0d500a8d48c7db939e7a1310791a8e79a4fa4bbdbcb774f01591ab81ff3554c7327aa731317139f7127f64610e499a9eddf0ff013f5b99879bb9f793b78271269f68c22eb6ecaf4093cd3b25b158de4a0bb60f078eab38284a7650f518ce5758803fca512f27794d0c49d8fe3a3442c7511838d053a207bfc9e218a379bad54903b52cdf79fb1f5244f1e09c708e7371ad31496bf14906865543078cbc0d6277d88745f27e213767513651f45c944f2ed236d49248389a91892e6d769b748edb7630af63450c910201faa7755d94bbcca9d5c4c8e9253c387f46d5fefb3a0beefb9bb43bb2e355217c6e0485964aa3e387756c94f4412daf6a7ec1865c27a312d9ddb5bf0d0183cf281c7f84b5bb92ff67782e53784d28015be65a7dfd8c7a3f99e723a759cfe3d51a0462509f0b29a3b69400786da222639db835faa967a06b333e06e700d8c802784b7824d0255f26f961e310ba866f978534f2bde7529b460d5e9ced6896e9456ed3a45e2ad0341aa8763ff7358a69d5213fe7bb325fd69534340848fdb8a1eb42167a695d3bba859751a4b07d0ce2fcc40ffbd0dcc79e95dca05215563844fad89817a28bdcb0185e80e19040d981536aa6906d10e09995f8dec8e6404fd0655d4e4bc9d5d0113108ffa9fb9874700eb37ecfbb5ad0b49b7c51534e94c9937b272fc085be620a74962dd7d2ea48caed73235b730440ecca60ffd18de4f6c98815736cf3b37142891983c7c2353beafd5f0eaf9a6f7778ad751d8fdcbca2b5c0b77e57cfc4c6bc66c12fd266d4a6b5a4de29715c8ab5673b602ae0db2af2e35a3d72ad6173c3c2a887a651ca7dfebb1e99ebceb6c10ae722f81d1c9b6dfa7af917dbd32fb7922fb8736f05f46a3c6d0a3061dbda41d444a540820f15d796849fdf05850c9003014d30feabdc18c72b09ccd1e0074afb6c71549b524d8b0d0b84ea818136e5753b80e5d45ce730e9e5e733895a56cd37c828913fb1d720e8cf0c88fd801d0a707cd11ea45777f6b68a756e9f3ae93c80adb6558eab0333c3899f08ac47531035aa35b33adddff6f5ccf706647fd292d14f108c9eeeb6f6dc8ec3b661c02a9bc81910d529a3ee0000000000000000000000000003c2ebe99f56a80a10aec0f96ed46c2262fd8b1d4291bbf15aceb8fa954e44b466e56f3595c302383c61178fb04c210d41529acc40a33d4bb95134033f2034f341b727b0fdbcc45a3d5784cce5e226e682a1a49a35c5f39ddd6e18edb173b934000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001554ffc6e5373e366b3db226c31ee638b13a53c633358ab8aa9a7d79ceb2e915a7701e79fd265189f6c5f8e4fab8f023ba7fc00dc4e9f6bbc061394245f201edf3d9eafce62ae87d3aafe3a60acded315762b17ddf5e51cc1bf2bb1f4e3e6f3084bf8f3fd3eb49e7b1f5c538a686103b72673076e82eecb089b1461752bce345032017089d2dd5e0182d5c269467f8dbe6828a40d5fd659aa20e0028bb8e8d26a602266f693b9fb1de0d1cefbdf07d32ff4db52e163dc64cffab5a44a7482a3ee0630a05f90398d8ae66fef014af24d30a30931072cb5b0546b62b6ed9881bbb61312f8fe11d2f28a85ab30be140750ca5f981e91c88a23680b3b2547bfd67938d5b27021a6646ee7a3329c0ff5f1737e49a29fa7e663caf893bcdad174f3de0a756f0a80206c9de90537e4ce3deb678c652f49892e277adf22e4cc9ce85f62f6112caea4c030792de54e4967052f056dc2c4f0265e8ad17515db764fbf176a5645c7d33dc4703222b9d6bb2b6f1db432d148bda00654cfa23caae31fc649bd70ffb1bb9fa8b5c031fc0512719d06b0c07d66dcc4bd0f9e5c5403c81531b188e7c154bf1f527c2d3127e0da6b7b3ed5ad25c7f414c5b306df67b180af6ba75dcaa51511acdf484f08659593522a531a473041925a7dc14901c498286fb81977638a5e0e7dc5769b897a23e5e3e115fd5f8d2b74654e71b7a52732ee7e690de212b214526b4d01c08434265d6597bd519f170f8c899c89dbe63a0e2c36589f871801aac477e6373d818722e1ee5a546eac98a03ea6ad5144e84e4fab502db0735d44964b31d6d17bdddbfb1abd6059e81e33adbc4d5701a37a374e37de1de315de22631982a6fc23175401381fb5ab730c4e2e3b40ac85602f1f241b04a9a451ad5a4c0e70778dc3332929e6199bef5be3e56512d463432cb5d03f9f609c99fc067dd8b637279252fee84c39e2acc160b0a1f9422320f60650eb7d0d22ec0ff7b7447a61a421c50a55d6196e6519234342f0b7e277408f34156e7fcde52c89a84fe83c46a974d3dcc65ffd87fd87360bb16c09c30476a7ca992db2f57d3fc460f47d9f5c747f557fad224329ca185900c9f2d713c46fe5b08b81b0a0efe0827bfc125f6afb90027f7f31bf1c619259d2b6ad03c4ca6dad8a9a94580f12c0b7130e7a5fa517d00b7720493ccfdc297a5519a773195c6e1e131af3e8a711a9cbe6916222e9cf770d19f2edac979ba9fe57c9d5dac51ce76fe3ae7a25a70dbf06b2fa3d1d2c2eab6cfa52cfca64b0c332e83b16bde852254e0ee9da269030dd660aef3869cd6622f1eb323e6a1264accd575b3bab06dd48499bca5f302f5f4923497d00ef8cb4b9948e31f2932908443610fa17187fff32b243546ed13be9285cc35d1733e387d77c7c14b4626fb607942b09d81d04728dbd52ec6b740ff1ae5e358724bd97a3a488bb365324382ac4314b8a7548c3eb1e374a32b51687b47927677d97bf2b9e6bcc4331ecfdb785476750992e7e61dd06c13e771455a5fd72a7f773e256a86fba5196e74be6b37e2be17b831cc763f977215307d6cb06026ffa4aeb24a5146b914454a8983631f781bba94b0146d596e08c7bd10d1d099d48031cf699e8c41a840b8ac730d84c0032789faac44b4ff2beedf11271f6102cb2fc708cb336f499c8105215e4850f16c849c08cd174d0321c5ffb8a97abed2488ce8b3f9fb33db97bcb02256507161f5910098aadb08a5c773aa5fda969cc59ba7a65f4f979b7d3c51aa9ac8b382b663f66ed9555cfaf06633bfb3f405e95471250b4feaec73fdb1b19551e8961575cb29e595f46c66482a9bb67d1c95591211e55da3be6e006bb54a85f7dc23427338524530438af0a8b62e1b1955bd8d82798996e30872ce5b69bb00e5826c33384cc743ed9c61becf4122af144f36c587d730cb7908229c08284e22128ba768660fb37b2289c9b0cd5611d85fe4868f8fadc7a07a4812fd6b667ee7a4fc3e892bb9a063e28c0db39369a16cf87a97f5b5c87d581477eea5651ac347513baf239fb36197d57138856e29bea33e6ddcf7bab6a5f81b2ca4ea0c0ec68abb42f958826416f75aee695610e063642e0e81f2d63615b0f548e3e616dc67fed83cbb5d26aa0041f282a28b25cc98f6f27b0a2055d831e606e7ad040ddc0b9eb931786604e3d460b5d804f0ad2a22fa12a390ff69a67d6dbf94d7e0dd51442ddc7f2ad35d3a982981a7d996df80e7be885142a30f7aebc8730026d5df1126fa3142f14494d52d12f293915911d16712889acf0b3d4a05000000000000000000000000e14c8bc5ec9d7178696b28ff335ee0f9cda583946288c39c4e3c823ae01cc3494547132abaa62a88107e24313c543091fd1e3b9b4655511beacbd79e6c71ad1d78dc2fedd70df84b28505f51241bab092ccbcfca16461be025e1f74e8c70d6f800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bf23d4d396c2740205b97eaec621bb4d7aa7ff49ffd17c8e4e2ef177e8d7e6ba92eb6341d21eddfc60ea3090b2305b588facca2f1e59d7d25e59c249bccf19c52cffb38b5b3de3b0d6ab02ec8a67f06e84bbf2b788322101e29d35a0890ce848031d9d6fd40e22d0acc091f1cb7b6973bcbd2edc15b1283b66b3cd5ab1ca73950310c9a95bf2e1e4910a6992fcfc18df4f09c966feabc2d133f80311beb0ecf2a9022206e780fa2f24e7e11a7115bed82d9d0d572bbf03cbf2fa850ce0d78a26309e0a04717df6ea4363f4211dc45662b911ab67b23caf5071c74f75bee58d468177099fd25458fc8ace48b86c13334ea905c4eab436dae839ea70d9e6107c0fa5369803075d6e70da62cf055618b3492b9165852ecf16c735328d2f1844246b9c52d45d0320371e081d60a5d66ccad47004e90f3905c073d4f3e2c51421751c1047344650031688fff7292336fa48cbd8ac0a5c0603836b01bb82ee65bc9e4423030aee9141030191de17ede6770a5c75e3cf8f354e18170a045c4de2b3ccb1f9dedc4ee63268032c940ca139b47f7c6eeeb96958514ffdd2a1dbd1018ce3952009069fa27ac3db9f7ee5544cc4b33f3df48d37f7c63d3bf2ad8f286d3d042d9e3a6513e1dd70d585c1daf9261dac7956fa461cf3873a791a2263c4ef904f34626f703eb83c5bccee3c63e12ee806b92e211bc628a3a6ed3485fa9fc24ef370cfee602f11113cfc79b802dd838ee9c2d3aaa85177eb278841f4307e4e642fb96688b281af7910ec7d23e9ae3de45587d60368dca7782b971b54fbfd55ec33818ae5f51affada54b9b510836d1306f379f0a3074941b500069160915e6cd526b35b106938d99bcab8a9f39724a4ef5976f8055ab4f1e74dea204e8d9f9c132b2026aab5be7649cac2d9800fc1f22f5235ae46fbdfc8fef8d9169937b012ebf40f39054dbb3a820c7077adcc707800f7a8cdefbf631b365f9710e502a76c2a241cbbdfd01f9120b45631508a89d1daf97c8d29d8f0f6971926dd286d64ee791b913ad4d1777e5663aeb78330014ebb2e01576d025d91ad250279389a49b49fc2e8630bb64b11ee081cd42dfd60cf424c54a2f27eeb488074e006412c3e6329125339f1e66cb7ee28ad9ae99abd336999ac9ebddeb8c145486b459a9767c6c1773da9437590de95021e48864fb52cf0f741b20ddf76cfd7ebec15a083839a540328664ffc42dfb69f56bc9d33e6015421a5480cb8045ad8a571c294a7e3c194d0348853b51f823673b08b217b332ed8019bc4e0b0eb8c0b7ecb9575b97f5da0530526a830f111dcec5b1c59f0aca3cc02f50337d4f794419c1eb3ed1af32ce7400395a9904e867b0424e773c0731ea4af1af01b8c9af7e83454ab8cb1a8ffde5cbee858092ddfb25a95788cfb8f0fc9d8527fc09018c136eba346a78ebc125f94b0d4861c002286a6f03d9c8697103219f18fcbf7f8ca1115189cb1fdad482de79209d215e41efe076f2cd030a5a03a137f214d806024a21189cb51137e3a20777926b6067be21efca0cae62162336a5ce8f06cccd99ddc649faff0f31bb6f4381a209a3e21e83c25291678e25b5714fd49bdaf13389dd9fdf2596ba036374b58a04ea14264754703c352d4cc7a3f5ddafbb85035a8904c244ab54ca77ca66e60b0c7e0bd244711b784b8ee4c09061f5405cee3154813d9a03ed63976057f235f8da3597cf1c745daaf04e2db6f62e9b4bd7d404f36b0549d717055934e70a3c822e71da516bdcae0a00aa3b4aa7fed03ad0b231d2414a46d5383b44d9336646bf14aed1f7b0452e6edcbd2e814fb64271161616d03c79332a0cc7b767a7807474e0619487b5b4371f7f6974b6fb7f324834e440583c627edde8a1da8fb31add952235957eae4913e696a82982a7cea4ce5534f232f14904cf526850dcf73c2a6197acd44bf150d37b12aa71d9049adbf8e0ba19feb32da0f52aa28472301054654477aba6bcc7dba9f7876120f59538c91c83b35a71fd751281e784a55861cfa8afbd268a1616d70c8e8c19fb6a54a9d3e0f9531dac2f865168d1759a98affa6889e5449c1d375f6ba2b6da4df41a99d1369f7e1da85c6531f80cdfeb6a214f93fece5029478c07ebe88c63ee279d8ea9a3ab348217648c10bf172aaf91770f653f74e3d0933c4a43f72e762e27c8d5b6361c4307f3646bc8533d1b0992b43aafb57a106b0d33a628aa4e996a2b5adc258117155b312e221b636a42376965bf21c740a8ddfa247f4d4dd748e517e7f4ffa9cb9c54d240c02e7e2e8eb162d7e621c6bea69c87360ff798494cc78982e29de2452c76b12599ed2e33e4206cad3b7cceb2da695f1c4ad4a6e0da6698a3ffde00dcffeb83a6ee77d801d49c4bb72fa9f5566a93a0df393132907adad0287e8a1419cfc79046a27dd204", "", 0, 1077927338, 0, "c7c2e8ae96fbf626ff7d3e1eb069d354a799ccd5aeeae11c409ea4c3f6c5eb2a"], + ["030000807082c40304572788a2f520a74b38cc82b1c27c7e52befe3b0c59719488f5d2af46f0c456ab01000000046a515265819fc518db47f7a091591243bdcd9e01785aa78b6fccd53edeeb440eeec886c682267c6c010000000551ac516a53ffffffff9a1c255d10b6f89a453beb1453da3668ed60eaf1fa54230dbba0d4e75be5d91c030000000253acffffffffcb44b9ad92816d6c238523bad785ed7404f77e1c49c3d0a6dc697e96632fd23e00000000002e0843ff04cface004000000000029a473050000000006ac6a5165ac0037cfd305000000000400006a633de13904000000000265630000000000000000039f0335040000000000000000000000002bf570ec4b36e6b5613f38367d7c6e8dbc97f19eafdbaf2dd4eb29a13e76a5ed6b96a31a0b619b7db278c22b82258567f5f5079161e2444102e403b840cce9aa778a2d3a7842576bcd100222f1f9d5cb5dc7fcbad4b8b7c8d9cba6b27fc67ae60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022bfd0ea3953f35140853d6414fca45943c2b6e5df041b85c05c4c9de43697c806834b66beb44d263bec0cadbf611ff2eb5e1d493452fda8e7f4cd4d6534d3be542addbe57734e47d58764eb0a4d17d64fc7515397710d92895cd08ff7ee47cdd25207af4bac9d96a37aea2fafe5666b25c479d3379d7aba33998579ae163beb020be8185af77a0059dbde6f5157fd234f686382fad93e4c67f35d57eedc5ba4bc032c47ed96b11f04fed70ecbe58b2aec3a6b1f3afbd066c959ead563ddb1b0dc040b06fd25a2186e69f21c58f9dbfc7f948bbf81dec968e89079e0fd3ddc6c8445dd2c9a6a0bcf76a53d2686a8dcc82b6f60f61f69a9fa6eddbdd67cd1da5782063d02202dfdd56f77da829ef55661d8e9e75d47bc32f343d07d600d60dfa8a835046e0327787124706238cc1630fdfc80847a7fa20b48634496891d36faa96f3cf1a4990316921e2930daf98e3996ae3ed54ab43e6b2662159ca7d4a84373b3b1adf1e6c5030b4087dce8b3f6dbaaa055db33c84c3b8fd66dd406cf2b107b866235497154720222c78945679081ff625bd9612da17b9e969db635efbcb22d9df1d66135846b4ec0c68399f5c0012f7a73b667b5047d0f8335cd34b15483533408f86bfd2f96830d4c7b8ab3596d98fe54b8a7001840309fb5858a1752e6379e6068ee222160c0e0892aa27efdd91b42685a23601d157b2d1c97b2aedb52a5c05cec99328873bd3a06618f98dab0d40fc4ed61413335f2f54e0f737fe56c53cd13d04c89288ca86d2491a5aba0210d1dd06058b26bf50f08918850e6017427cb2bc208397c0b0c24242b528e5654e4da7db285edd647a439cb9fb497da6ad4506a8301783ce0d5655859127fc887b25561e011c589e9ad94f3d9b09e5eb8f6a4857be699b6b14601a11eef7f4517656487dd159f56d9daec07c607bde62dd2c5c1b9375318c5b0fd5eeea48bc612d4c911b3d9b4f199929cad37db60149b00bca391ecb3ef5bc36063673d70ce840be1b73b69b73ab39cba736dbfad5173f14d70842b64e3a58bd27ed91413a21e7deae89f02c4cd18bd44415d2131f6062a95a7de8700d7ff32a28b8da0db54ff6246874c393b570858a9b76e7630fb78d305ef20019b995e206f5493a9f57acccedad9ac85a054159fd50f685fc26b2f424278d3ac480cce77945f1d3ec4f971906a3c55b9961cf7e0b616b1a2f9aa3bbf1e348478f6e5360711b657ec78f202824ff5b592769d0485641672fbf0c14271f425da25a8e5c03c2003683f68a022c99530f11935996c26cf30dea2713fc824bb66d146b4d99bd8f8fb21006ae30c78443c158bf95d207ea5bb2f0a5a85911e0c97c97a0287cf68a9e45a84565b6f621e69f4866894956d4b946f66358833b0123d8eee9f0e39796ca16538184f3f9ee5055fc0e58467e8d0ebf75b674c9d9ba2ea634cb0ec41fa359bba7df98b465c704f79c1b27a5bdd5a5ec17103665c9d36c1ff4295053c6fb7eac3442275dd2353d9e426a20739c1df076eaf07d28ffd76d370f31334ba947f0e664addce2d4534386538d64d0123c884bb6a38a573572ee8081e139a9f50020bcb3d49d9159fee07832a51a6fddbdf60c7c670a48d4c40d1e330eee1c5c12d505c8208fe35e7cfe148a9af90bb620c5c42a886fd025581a0c8fd1fac68557d60893ee103b7c60e1503047d337e3054a2fb5b7dcec33668e912d190ddc0a03ecbdd176c58db135c83beca83bb61da47cf8ff1cfd5fc56c39cb74c7f3eb22b435e83edd4fd7930ba0810f3b62d6dd525cf803ce07a8e7e192d1530cea4fd6d212e3db9ff9b9ff5032baf0c7f59aadd030296dc903f6d92a6b44d29d03ea58704b95a38ffa679f380f2d384ab2b2f3f3e3558ec5f975d7f421e9a63e01be1ffc023fd319e6d0464c66fbc2520e37474f4c6e9354d2cba35f96c169eaf78ea35eb1c486377c58de7a70919962e0807815f5b26a987c41ea0759af277200de1e374ede8245b6aab2e3819d72918eb54c2c188d6fdf0a39aa80f2a530510bff0fd28a51a4191b8b37d63e6b936f118712c02db471fb5ee1abbd437bfb2b96982be0de1e484c3a41eabd8566b7f0140d12427ff1f8a7ad1acd209632a7c060c392175ebc1fa3261380eab761a2db8988370d9db5ddf732a8f57da1a3d304f32f536b2d6c1d2f192a197639e5d2a5f16de45b917a29d7607581a8c8fb6cb9dda5ef3914e62c9ef6c4a4301f6a8948c21c119004a599bf95f522ebbd272981b1d5ee4c1b08f36de18b08fde274031dc8f04636a0db3eb2000000000000000000000000000af5c9a54e2ca5e6da4c87d23e4f4e7c143baa03256c37a8187bbe6fb8c5a4ed788afe92c8ab796f7fc0fcbe4403471a5cdfbb2de47e26223acc1997f14241a48056e271319b9c5d5131881548304a54fe198d9d6db156b612427900784feed5b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f4bb226cfe18b59ad7c03ef24db03651dfc801787bfbfc9ba0d8827993fe3dff1c0d8c8b402790c60d677951ce54edec68b74db3b387eba58950a4c145a40d71e6835a8d8ebf0ee55ea060f79ecfe4e82db6b7a80f9a9b219998a368b143a4591099be96beb7408a533bb3bffc629fc98ea5e12c661f5af0f11a6b90c66b5caf031b655ad9d4720993aa5231f784dc9321ed2bd21c5e42fffbe43a0265db6f779b0312ed7f49bb096a66f11c71d1c017d24b7d8f2ee2dd973481f2f7e3cbd48b802b0b0328a1654f9ddb60f11bacb326af331420561820f19b79bfd65fd75a2fd63de6078c97c14165c1c7a9935901be7016698b3f69049807268542fbb8c1a6c3e67b02264603da04e577b287d9641bc78afe9e5f6055b32b1e9514f9e36cdc36b6549502107b64cf68916e90f36184a955bf5909521998e162e5fe9ae2ba5fdc3e2fed2c032749fcd7e9f2c9e5f5de5a4e3b63b8bda507c137340b83db88c6d521ead293bc031947dfd6b466d4f2a6249258af66f0c3ede2bace190adba5194ca386fc37104e030ef65eac777171b343d8185e22e47256ff7a04362539e8fca25e92d289134b78d6d7e616682423b8d25ceef984301a8e7e113cd3e6c728f84a111817e5e87d27c734cbc00319f9e1381c3d1ad9d5b83c446d0c09a1b298692784432c520aac1c79cdda2dbdc546ba0f6338068e02ddc0331a7279ce92e003ff0503e77ebb4dd43742f41ff6381c7fd330f64c8ec68cb806b1783acc82d8d0bae00693f22d80addeeb2c71066c870266e685b506cb72f4073df67150e8e40ff68f5399e179d46b21ae31dc038a845fbc2c489164b3d136b7e80df977a1e08b04ad218b01a4b2a29fcd6ed381e9b7e31b9c8faf80cf9945179992110c21db00ac4af1694bce231bebde6daa73d610ffe5f7043ddd48e9659d07246456c2ee79e7da48f44f0d157b34a4e4d0839c76e8c39945dd16d419aecefec873b518583acbb76886f93fcc52d744b393401de2dd7de1327633bc083250827915d7a1f9a092e89444ca8f59da86b7bdde41b3cd793e5632fd98f7c5e5754afd5e728d52698c5e15254cb01bd3521fe004cbbeae17b6b710a5f0885a3b3423b5729b15ca99cdf46ac10637f3f555732be0c2ab11ca4ccb9ef9eb87c200f509c6ad7637fd9d9f07efb374a04f39a582fdf36db3c9fa72f9c52087eb1fec21fe358ae6cf8a494407fe999543d50695558ee56a2cd85bfe739a938119d68d4debb04ec1faa245334f4d2d103b17f8e9a51620f68fe8400562fec0c3d23221b776bd822a911a7e5fabb91988b79509eb625f287f559432492baa83d39f6cddb6632c5b528d1338f21a23e5d99726d7b2d081a31bba04dbce3af4ed6a3217ee10e1aff6e853a431dbcc242815b88fca9f425eaf5e22bf59a51c34e9d85f31659171d0753abca7c17f81a0f8eb00b626d816e4cb6a0a858afd6d7b00f43c550b9bba7ac80de1aff43268babc6168076b9947ab019468f7bd9eda95c3e628328b1a6e185effcee88b052ea8818946dedda6df6713c5b150d04ff2d216261c1865c873d36b6f05470af68f0c3826f9e5e041cb03c61488e8d73101afa40d8be20840423f09c67316b393b9c071c93b3eb8ae9e8bb228db1a90c15481f03d2c26524324a714b82d2c2e0035295ec713fa5d3a27854f3fa0131a4db3f181ec72dc868411b1aa2e26740c424fb902e7f39def8ea85911f4cfdf70826aee1e7a6798bf6b499870e809e615ff1c469b49c0e375e3a48e7e5cb1d375378acef85938da7d57207d05aa8bc8a41c7d67cff6600e6d9676653631cdec42f188b4ec1772a1dc7466def9601f79b0171af704fd7016debe49bfb36bc63ce95be156f43969c03d4ff089bb41babefee5404ada905b449cf9bf583e6a6515b8ed291b0618acd15e7000280a51206cc7ed3029464f6c5b7a367d3aef22c8eaa4945eef9dd90f04752bbe6225c40cba6f7fffa7ad6a06fd97a5c12a2df2ae2361a8fe850917855f44246a22b528702d6c1fbc24eb5e2024fdcfbb016b6b45be3d05162a8edfb9ee9b530c8a701f6577257252a12f659668902138abbe8ea3b1461c6cc8327a6c2ad73c44f45b4b813fa1e7b1961a4d6fc3bd56b174f6b45c3cfea5336ff08bde315a52230104c0f841aa43ff220996972bd9bbc644aeb73f847ad91240534b204c924df8a5a9c4f2bd84238c0076c585883c6df75cb60d8354309e5b1e0fb5719d8e7ddf90d6da5f7211c1964aed3c6f11e51d95e80e9bc3812db661d64e6b8b812af7de0000000000000000a3e5e00200000000e53118b48522044beb9eede435b3a6b87dae21660c424707c9510172d19a836458c8b10ec2c467f0b7e504b7c0060928abad050ea1615a04254daf8f80f0b4d250dff4534ca53d97a08c47ddbcaa61521502b709d74eb6596235f1c392be0c4100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c5fdc9b668d60eee8f5974239cac9d8077b9befc0c8d2e7fc76a17ea21e300d43577e0c8d5300639593af8d17c0345253d4b6a0b7beaeb911d87aa3d7cdb69f1947e39d5a1891557afdb3b253058cf234edc7db56d03547c468756bd9e98d2fcdcf5d1e64068004f695309a156d35c669c51a0f0b50a915d3429022e42909add0204119bcabb43f0f66327da673f0f8f5334d3531a35756c5d0535d4ffcbad2fec021248ef4a0f2b8e7007c37b19ab57d9b982d89c2644a772296274fdfc39d3fa3a0a046067f9b11977e5de244890b313d4d71ab3bee6dbef1607a6eb11dda820e76aba2f9712efb3c61bd2d43f96d31889d5a717ff43bb775b5cb060a99c8d451cb7022296509b3e77c2db28d205158464eb93791e31b7c62db6b555f2939329d8a2e30328e19732aab0969ae8ca32602cf1d1d0b1bbbb8a675e312fa5e069462554faad0318787e00f4f70a936d73ae7c69b0e1a1d69284cce68267372cd7aea363c654de03298b7c6fed719f8fd3375f8dfc509f818bb2421b9d1eb2d8207184cb7c8fc676021926eba1310b7991345bdd0744078f0e6a9ab9cbe0eb43ff75893e40fb45c9fc5dc0bde126f202b0381f3f689dbe1a9013fd7ad709460d44da11781c01565a301c87af0c6e0209104fa4dd38be083424278c8378da906c52e1b40d10ff6da46c2dda80d9064f8a11d2859f43b80223518449ab2930c4596320ad40a321296b543aceaa3e4d88b4dd77f16d0a050aa7db107dd5129f5d0a6c9bbcadf2992e7570023c2006533092e703a03906606a85ccb491b51aa69d1b32140b7c9a58a5ef7a133b98667e0f82fcf99558528fd91921c02fd50a35ab3abd01be1e4c66a16aee794bf59b2fdbfeee58838bc54f7d81cf73c35dafe70983de4a853787e38f32bd7355c35f31ca65dade45650892d2b385a3c23400376f74b2fb9ef1a82433f8cbabcae05970dbaef73d6f183b3a198929c5ea815edf8f7d27b21f3deebd27e4535805f29296f7d5f83f44a2463548c739910eefcac13a9b402b15187f20c044c395683d86e8e3c0b26f6be63bd0e72e0a1df0a78691894ab1ffecfc0057c0f51d31faaae3f4914248980212bd65ca1a29fc08658988cb364c571a06a52b1564d7587731b6cc6570b88968942b20527831dc1953e38b339c874d3da3bc855f0d236d0c657ce0d9b51e7f057126d3182e041f15e9f7b8c4768c21a914d01e265fbf777aae529bae9f1d99be63a681dd766f37c0c981fc7c9858ee81d828aa5a55fa8d4b695b3e6917af1b73b7bbd5546a2282de224eb1b577a9bf8ffab30b47a856b457017811e7012180cc55d5ff45f8db9a67507149acfd8a1d53900b4d93043ba6212ef6941b0f0a9694c607b205fff16d2b65fc9bf7aad988326e55511f32177f4afcae60db94084fa87281f91602d206a83d47c54f319e9c3b7ef920b7c3174aa269e6bd96ac528353d4738388cbe556b529b149a9bae7c0505e2a3c37fa3c8a6b99b0283bb41f553719853e1d8d6f48798b3aa2e94a26e5928ad5f63740953f42e108a54eeb7f2312203f4f46af27e37d73dc9a37314f09a01059a3af9e8bc97f59a98603ae279c6b6e586548e7861020122cf21c5c2aa0a6b37f8d05ee0a486f686287a754e9bfd02b8ef64775bf1e8170f741717a860f81fbda61ca7a9e6d8cdad5ee44798ea5db25a9fb37d66186c4af8e5978bae9aaad274ccfbd2ab742ae938d887a5691c78987417c58c56ad635d7dfd9c7cfd8708ba6fef1ba43a893744dc91ff07115c355061510859b19fd3c44985c32b2a81714035572995e7a86794fbc97e6fce208ba28fb9fe6e5e7e53ed0cb40476307c6d3020720a2a4ad4c15808f68cc72ae4f75ed0a0c965e9d8c1186f0a1362948bfb91fe7d6c2461cc18c9937cf3dcce6924a8f743bc140c1d815f86de2ce633f90cbec554875c192549872cb4f98952d252b666ba4511de1385f73188942df9dcbc4b11a72ffec3b51a806b9eb3491c022ef94f1d852ff95c6c03ddd2e07ef90e04177526145fd0c946c562ecf28534a2a38f96caa13283d925b9b973214d4104e96e7f780edbef2e8997ccdeed6fb3296b47511cf96b79d9960ebe1f82fc34ec08f8e141a813d1a58c2de0da7340d9a5539ef7aaeb3387117c73316079c800ea5cb06119549f8fbd8d713ad920682743eca65645db4a42edd53db50b2230c9838cce942c7a02feeb5a1424006d5ac7a6c5314e4cc0cd4e07ea3a0fa3b7ef14856e9449d51de85c94fd0647c0017ee867af3925029c1184da3884b49bb80722162b9c8ab10f7fdd327711128bf990ad932ca87ed09c0095831f4c920c601ab5cf86ed99a70498ce4fa5715f7a0815f02c4af8b9c67d1fb091bc16c773517f54c885ea35d245e2c01f039258751aa3a5f533ee173f7dd34c15100", "acac", 0, 1175556952, 0, "00a838e6a513a2a3832ee738a2ca9e3fe53001f1e5639c87d43687b1b139f725"], + ["030000807082c40302ec6c179f46c4bb91b6b998601fd04b9d14564c1e20acfa86f84f32a92de0dc260100000009516aac65526a5300ac000f9e63c3a324ffe68fdcef50305003eeed11f2a538b65d258baf75151c40842dd7ede000000000025265ffffffff0313f1b5050000000009515251656351536a51e45f500100000000036552521fa5e90400000000056551530051ff34e75aab91202d00", "", 0, 321495878, 0, "c58986d5b25c0fa8e6f774a85302c4d5e0d1545a8f7f2a5058ce4d90260d389c"], + ["030000807082c403030a0ffb963fb582e4d8bd99d19b3374eaed2d97a85b80519d2ddcaab521eabebe000000000500536352635a58d105c32d13779d09afa7c8d58033282fc804819a7a6646d5df6260eafda160eace67030000000763ac0063655363ffffffff69198daaedeb56cefb7c01c92fbe86c81cd6d96be7f8f148903348ab0e54de5101000000075253005263ac65ffffffff036cec7f0200000000045363636379d11f000000000007ac51005365006535ce260300000000085253520051006a535c9288c400000000037fdd1a020000000000000000000000009038276ee9c7021c186374be58849dec0e4beff609974a8ec2ff8930881e67a865e86a23c5caa30e8e78af1a12b569cbb9e5b8029de7d5884ebafaeae4a872cb2cb6a6d02e8942ce3cd2d67b177b2e191017251dc1357e6091b9d582943619c8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007c38759d57f775f008f25a0ccb8f5edbd7513f6d8d4569496553f38cadf4e90e94c518ee57b5cd5371d8263d588db0f590c18f93f4a1e105aad5ebe469c71fe37faabdc978919603f9e246a2a3b7cbeed9bf5ea44d1bbd2b6b746b0959cda3330d5aa6eebcba46dc931f748f9fbd6855b3b116b8fcf8acf0b642660de9246dfc030187c241915612b8c79869443d32d3eaca155ed3bd1fdc74214074c48175fbe30228de84e6f1aec26510f5bbec2626d0d85bc82bf25942a6c75c5ebee87e45e5340b03dbdad6b22031e70fce86500b36c06bbefbf00a40aff62a79c3d0fccd1c426e3909d7d34871e14165eec96fa5e486e332b9fd35b006c3d3e162646ea11d61c003148cca59811064b6945abf370e26310a64ee9e8d4701ddfa670bb2a4fa5535e60324d99bc6c1581f12b6333890c1d6d17d2b1adf70c1310d8aa991d4d695667d830330052efe217015e3f98669c3da9d5f4988246527fbb4ac644f979a28394e1009021912d563f64e33c412b0ef7db87bd54b3e4bf6dec0842581fe67094f30d6296a02064aa0252c970e07a259391d665ccf682e2cde1ad512e06c78f09c484938d267230ad01bddc5c15c3c5133d657bbc72cdefaf4e04797eee687e4467b3feded78436e4aab05a00506b1a3e424d68a04d500ee9bbe34e37db8c6b713a872ea6952b6e50f39f19a110e26d0b26cf9c6dcbdf09853fb78d7e3cc6f5c8c07f1b261877646f56f62973adbfcabe18b79736e3838689690a1561f7be679503bcd8132f2182cc9e487213cdb7c468abeff673f37c879861b71a7a77161cd576ba408387f770fe6877174de401a8bc73d410fedc84753e9666156f8164ad464b1e393e987b19342d3a0279b04f5591b42a94798cc434d68af15a41ac5dfe70d39e5a0a629db42bca814ca2c9ec5cae81335cd583e1f4a80ea76688ca5c1221af15ce046215285ed4aed3211d57e4efe2e6aff3f49d1baeacf3108ec78399ff40e721cb5442ca2d6f31d8fbffd6dac1279ba02da3f3e2497d7a466f8959c40843a6ae4eda595b073391ab13d4e57d1881a65ccb245870e0a0dab8a3acd097fd1c46400855b398d99c0d91f444fbb0453689f8e428793d99dd60ffb6695fc2e7e8939a127468b9fb0f8a9ad1e7a3bae37a2f733191852042ed58d11b247e9974b47352024d59e3cc106d3dbd06bf506da7ba1a99368e1ce85507feb20c3fe0b472f193d950e0608848ac1633811d2ddc4abdb30fa1cf434bf0df4899d7f7178b3842e9cb091505e40f90f0eb95e815d04c339bf3d2f053805147ed16b492b05851736f32c3ba88c05c49cc2a380d71148f15f70b2572d8cd5db5732a3a10a08ffa58ef64e26b40478852c9e69e70b77d35f437d3c3b63b571572b965e05faa8eb3769747041df4c8745e2e725f30d5d2be0dfa0f487bde0e7df652847cb7dd44dd85f2a1ab7d11942978cf0be458f585ec0ce8da5f236a43ef1a5c1f90555f2cbcd57002bf934118f2f52e81d3946225fc6df68a7e62c6afe26e96879b96c844c7e85f2002cce3e76db6cfcf4fb4072dca8d318ad501ccd2661d20eeecc040759abb79324da8b9144b34ca455feae4406b47d5a5f963648767c7af74499859e86e31d7663b90c2f203a326ef0a91db740a7a69bb5f5bbaefba62fa75aa2754ea28ea433fbd161564f3f62c22d3b2917a171dad1f71e16c6accbf8fd487b70337e5241629c6ee0005d7a9ea526a8c7640ad3d68a43185b4a89d1b99b1cd8c1e8c790a33ac72fb530e972302f419f2e34ccbf4d0c7ef1594635af689696b0d2855624adaac86f4b50bd9b7e907d3a16e50c4e74bdbeb1102fb4d44f5ecb610cdc29867e6603fa7d8dff10af3e2deb907cbd62c3582416b5200f9008f26d5e4c7f0f15091412d21f4a0d3f3c81d902bef8307482c1bedc5efdced5de0bb5ee7a9545d7d425ebee186e25ec7cfddd52ae33c0a2bfc06bdf55beddf81fdff2283d29acfdbc9e7c484ea2985f8c4c02d098e814165d2f75309ee544d2a851ad6449133618305cc6fbffd3570429987ed12109ecd112c2260ad72d2f3fe054699bb0ca04800b68cbf91187cfabe6531f7835480c9466ba751614d4c0aeccb0e6813ea8997ee1ffe461ae3fa0f237a5c7b496e9e4c475679886aa25b24a3a11c8ce008a197a540ca9fd68b0dfcddafa5b04beefc6c62b6dd02c3084f69c8d0604a48d434ea97f011c0aba3c32b53bdb7d74efa01f7f91daa96b2a6ad4ae133473a4f20c502e8708d6ce5d0b6a9f6b153448af2aefcb84d32d43bcfb53fa7a02000000000000000000000000350fe5670a919389b01d6bc2eb23cae26dca9e92462863c272e215c14f4ef5f51f217269159d016495efb2b1254c9695bf743830a87c837079b6253f60c0863175b757a70c71b22585cdee198e16e1a810c38eda5089e2d197516a71346607e20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000087a56dd91cdbc66033f705323f776412e457aa4f2ba726179e7dc9d8866e09993139805ba57ab65a4949cdbbf8593076a865ae20739df919d254ede299895ba55717bc1f1a8428b66c12dbb50fbdf71059812195a5d73708f378ae5b8bdcb23bc44ef32ff7bf2c49bfa9d3e9a82cad747fac353ab14c87eeaa6981dfda01daf1032d7622d73bdc2741f1089df67ab981d23fd5d2177c9faaa57f0811203243f7730201f46ea4d53916a1f02b55657c5978fb4002667f4d27d6965e4a82a2deabc59f0b038c173a9c53e7d4e21ede916015a8bf951b9bf2ff8bd10a788fceb252de4eccc97e622ca1399827d5a2b840a2ade11bf376f6cd05ac5664359cecd6714b8fb703147ccfbe37491e8d4dc86b4f759b4f4b72ab94ee730f09eb26df780fe2b37bdb022fda9a5a6629dc0e97c7d900f6edd9f026c6d3c187b2397fc3a491db152dca750228da2159ab0ebe0c5926da0157ebd41065e2cb4744501465b99a808d7f3bb7ed0327d8c5981543de6ffa365c90780f6513855543690d2d75a6af2205cdabe47787032529329156a60218725d1e069d628bf9998e7b778ccf73aa732a8053eb263ccf0ad7adfe9d5cea14ebe06b45962b005b200a4dac11d38fee2583c669a06400c8ffea709e858828bcd95112d0958048159ffe2ffa877a8cccfbc462bff21f4344a8ff9001149801f97d6c8e4cd0929d762ff6270f12081d91646172abbb62d6bb571434af68721ede378c7aebcba600545cd56fad0b6c2706f439a3632d58ec40f2faa93c3ac07b6478301857f0eb034325b5f296bf2f0957e7051db3e21323c8e829cfe9e1ae0d085cd29b5a8cd79d8ff0e8f5621144fc1af206e85a267fa5020964349821094ce11f6c443675ba1b1ea4575f64f46b49e7013c08df0d2523a257d3a42f03918f2df7d28dcd8b80a47318d9e35d55b164398ed9a02319a62a372e32ba60c42283a37aae2532260e87e9f4768d5044d6fd1ae614a32b72204677f471df2e150b57d1985c2950a84ac72efd288e21d8b9750ee535e76e0c0b0c8c566479c6a008b30e84287f6223e96202ef03dfc4563e9a147908820fc09ac146e59350c33ddd10cd05a91f370bf273327e64d85461b448656dd4f305f0aa2c053d620a8dcf740f09f99908be6b250166f9ccbc585bf433ebcbf3880cf6702c437db3cffbf9c4478e0d39121fef2ef3900b5efc6a83e829f4cdd22c5ce2b6f90d79f0e2ca5a7978d4cdba34de5fa92ad2f99e79c44f3867f942d2ae3ef1d63f758ddad3f1a7f364ceeadcd5095482f64335e0d676ada2c76e492640937dd74a2461643eb1e3e90387dc800d7f44c0042b5b9776e9fa764c8f8ce35f31984f133d9783fa0f0c3ad4cc4faed195291316ead7fe9f6438e308d59b699e011c0fa258f7ac1766cd33d0ad5e30823f3b399d2a9d74aac54e2559ab9cb7118c2572187d6a0770bd5a1f331c71b89fdbb106d7201eaf4da99e8939de7d3543380da61faa19b3f20955bf397f1b52de1063c00a9ae48b951afb60619474c999f2572e1d735175115fdb7695fb695f438b1d3ff121c3d1e3892766169616d0259038680e7e2ea5b40668725e2398acb95a64aa7320d4187908e186868c11b447b8628dd0480c4e92fc6aeff3ab33691ab2f0f9499044a6bb0a92e50f6404490879a2b37ef218ca746c5da8c91c1c340399ff7a651fff2bf64f2cd80f897dfe8460d1c5a7f72232766bcbd70f476634547d279da98dba7dcf5ebd9bea646fca5524868a33c2e27d69426a50fe97a131162ff31305f1aabf7c619d2f4894d54461528d6bee0bebcd0e6e813928c88050842cbdf2449a6cc10529919d2786327727d49d8700573fc574d68e384c55cc1d306b3e309b827ee7e964e71f42a51954c67ab9daa17236504a385fef8064d7dc6dee9e879782d17d6a1e70172d0624a93035530f892fa7e7ce8421051f1e844019ec7044c46312620e30677a33fb94d94d759b8807c76eb3afeef3536f4ff342897bdd3d166fe1f5d2dee46b6f4cdbe42cd3ae0a0310c54de4b3e301d743fb7d4dc7a309fc2b701864bccd00797be213e8cbf1daf3bb6d0b0028187497a33b7c5a5e3674b20d01ae3dc6cfe0bbea30f3708794e2b6dec33182956580545b40b55b66f3f6129c93390a30851591a6d8af48eb6ef8afbec36ea014d256d6f3b994f579bb63dec84a5907f9ceb2caa03b39b6908ab2cd98bec70b1ef343fd811339566c2b34bde46da5f44ca227166c6ccd20d92142b13336c2bda5026cdf96fe6f14908bb97c976ec6ae410d04000000000000000000000000f32b1b511e1125da6a734502e3b892bd8fd8895d3e64a6c268abfd77654ccb392ef9bab3e993239364d04b96b436cf0e93a1617429096534449d7ea2a830e79e12fa467ea70e052795fd18296840198e4860441e744faf2230dfa398c4f3145d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050263c4d74101f0d2e7e0f2db449d52bddbd18ec4781d113e9ec7c200e5320b578870e0af60f1e3d66e5b9553f1ef1261f4fa45b55278daa1a141add1a809ab80de4494a49585b665d5e1c29e9fe69daf1b59a07c2976e5789142fb5188f9bc497c0864661ecdd74f45aff4b3cf90694b02d0d4f201debac5c59dad5f6d79bea030cfa3a7c9286d100e882f090b0f5bff4adc9dec6002dd694f387e270cee0d41502205f8848ba803d50cc376bacb11caa1e2f3146fe830a4d00d138b68534bd07100a0599365ebcf1792f6e3050363d5e259450cbfccd78c26419e34e18643012337a2dc2fcdd34b3ab6c82954668eb742b1e1e65ce7b71eafa9628f1f7713902e511032a88df8a0f53bd064194e9f69956eb8dbeabd37caf3455a5b6c12dd524e6e96d022e27a35807a7351ece0919958a1e4ad749801a2bd38bbb691f88fa0d8a6608f10226075831b2323321d3e2f388ea6effc8a9ed6a582d47e8760392cc7902a34e5b0211d7d1235b9165e1fd12aa9b4fc528c77378ecd71c0c5bc25d4d25dcdf568cee020e570da1ed73526aa4513896e8ef6151186a1aeaedcd1d2f50d39e5d59fd22d832c36e68ca10d475c5adff3f65ba8692b51ebb0db30e2968bca02a466766885560db7104c79910be88e792d77a4bca3bf45140b1c541adb5a88d2ad2268a811c4678c637a28bd45d2da88ca1d38a821dded489b9d11ad47f3ed86808fa44c7e04da8c834c52c91fa57afbf254fbaff298a015062e72084b28ec649a8da53addb237082137083b1f32fe944bafd9730f95e491557db94b549901d45ad63de76762d2f97fac668020a3f1da355568565ad3303b744f3dc24a3006024c46ee6569f9f114958277a5462fbc789b685a3936fff5f5c16cee5c6f2ed190b7ce6ec65f81a310e716fb7aae9e6af50ed9bd663e758fc9dbe2250f310266fa770544dcd8017cf89bd776183a29a859fffeeda1455c58482484f0254c124c120189a981a49ef0b3ea8e67deb8e1bac9558dc69582f3d538c2074557c5556c3e735047a1808cf7e305c89bd76b4704cd55c5838c43a399c88d5c6942c608a4b82ef98c2dc3e035ce667630ec1ce553ca9ae1c2d873ea8e2e258ab9b66991abc8e476d9abb1a0ed7e281e37ae066ca3860852d4bdaf1e8d5918c403a094a1badd25d8a14d5c8718cdfc10a5ad079eb2020eeb650b06fb4f6e212ae44976fc7bb725021357dcc17a3b4849f02408fd7f4134dc604f1666835ad87ddd0a271ebc435d8e5e7520c90533a2dfe845867867c72b7aaa86bc108d704fad90c306c8f7bc98e0575d19797d8bfe5c70498e42200fd9f3531f87a0c527e6b2a95fb5d87724ccc35d9ce499a0389aa3ac6a042204c951c8f4965b280e0fbc4646ecf74d671e8b854540bf60da7e5f3d40dc0b2afe48a7001a7875451e97aef139090bea738d28ab9a2908f315b83b6ca8910bbff51134f703b5fdb9177affd97789742c76dc666f2b70bde9c41af40eb4fb2058a1404cedebb23190174e3325d9bd1f5a88c04ff3746cbeb4a990499bb4f9b531c263fc0882140ef321ca252617433354a2592523400a9b049a0b1f72a99d25ccd765fee9135ac0a0838b36b0880b4b88f38294fc34049a654e4d28f95d10d29bc67657cb2198ace5f13957477190af93b6e1ed97e8f2fbc8abc77817b8132f3ef1e981565dc0818be10e1b6293dcf863fd0b9984f77a9da827e458798f7db4399eb3c0f332871723b8cd74c341034947d6938abcc47930607850e8775e3cb67b5226ee357c2cb23160f9385bc2fa445d6b8c2c6bf4bea23cbb470a6271d4cc91549d64e30ab87ddc8c3446556e88e04c4d066da939a4079d7b0dd3d29de13a72cbdce27e32c97948ccb624e6e295b8b4ef05264bb5175c7e9249dd9ffd4774b9c2d7ac6d7eb5257c74311a215f58f85fdd9cea62f961538861970b973b291102b1d8f0535e8d4a8f8129ed778d2817f4aa30c26a71b8a5074b2e967c987e7134a3453b8afb77dee5d9a14dfc4bdf5eceb2ece0c4782c27de5ff83a29bf7b4f469ce0537ec74547f9e36ac5c13eb2072ec5e90cfef78859e185ed403189ed7e4f280e4002309481d9b961a608acfc64ef1433528937f7a55ec8b6ed0ac4d47a319d29ab179cb9c15565f54d35a97c38297839b6cf1c6b27bf95b87e5082c12a8dfe161ed89a39c54fb20604053bfe8bf0c7369176b53357c78f7ca91d9c7ec63987e4f4be7b9b838b7df7d5d11de3c87aa143b5f0a05477056d889d9a01f5d969fe4bed2a8586c4202f4d5499a24ae485e11c3b2b4b28b0ba7846c1ab21fdc5bc76a3ad096dbb7d85118d461a30f608e7aff439dd13834e5bd0e885d4dd02b399a1a3d197bdfc4532ed679bcb450abd73f6b8f0ea1ff3bf3259d1469b11b7f5b0d84eeab690e5270810a", "510051006a51655153", 1, -291795576, 0, "0491eab34a35eae7d0231750f8f86d368132e105dcfec59dcde90ee8d2eaea92"], + ["030000807082c40301eeb9b1364610df379d5699fd7c6f358438a40bead2d8b09b0f687394e5be91b00200000004ac535165ee0e23c803dbea1a03000000000965ac005351635165006882c202000000000652ac63ac6a6ae90208010000000005636aac5100e42b5de80000000000", "ac635151", 0, -413149335, 1537743641, "b8db28e55f3b1a3163a20f0c72d10e29fd9e96fef8058e793eb351b712b6d38d"], + ["74327f3f010b2cfcb6433c22fa35d621e24ddb18ec9795d0d082b12d52d48ac590cb4c752802000000026aacffffffff02a13c3300000000000751ac63525365ac4b225405000000000351ac510000000000", "6a6a5100", 0, 1104014219, 0, "cec9df379942e732aba7a35b08b2ee864672a332236e67fabfda93c0bf6dac32"], + ["da722a1e02ae4ca534df85814e778819953fb92383099e75a897a71d79aa30c0bd6aa250870300000006ac536a0000531e8228dc208034f1e99270422dbc823df64a07dd7ff887cdcbbaf24d74c6ccb3e6afcc4b0000000006ac536aac5352caf11e5103131e930400000000046aac525209cf3a020000000003525265c5019d020000000006655300acac5244697487039e6ba401000000000000000000000000a3c6b24caac9320d204ad4146dc10bdf1b2fcd53878cecb6f74e3ce750838e9a2a7be36238cef7553b63d8e5a6681634a33fe1683a2f616ba6a6a9b984e07a4b2c70c8e15f52c19f6fca50cf4d91979051a141349abc35cd02efbfc93f8771c200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0b889dbef37c77e59e83b033f6493d35414a86610d16f7c0eadd971c865b6909335124db425121752bfb94f5f13a287420fd7ad107a8274dfdeae5a43d634a9bdf6785a12b765262951b742e2e57168c3bbb9705635e8b64e270abe0d87e6ee73629cdf3b9ef6a23c05dace2e629e103bd94d0fe8db32386d058cf35b177a2f021f0ffbc867628b44c06a224b5c1cd31844cb7cb72877316e14814e5a8412529f031e91d32bbff0ccf09ec2840596fbac042ec7e65b784cf6c5b2650074c4ad40180b0803d49b4bcd59927a0be16b6e6c5262f144d26a964831aa454052b2797b3ddd0ad913860ac1e181ea6f87194541e71919dbe9d56094ce37c397c47ea0d817e00206cc172d3f9ec90427c5a8ed517a3547af32da828f2785cc8a632665b0ee83da020a135b86aa08ff608108ec9c119c9a8319a136d85e8d68b5ce7442ffa7874ca703078c81534afdd48cc04c5040da4920acf99dd42aad5bfdfce0f0867b63749bb2031d88891741e63cd789f673e451bfc17332314454f804f2c9ad51b04f81d2e7e8030835ec00a37b5fff963dbc805b0654410e4764292c2467cca774e139a0a76c7667c425be70371bd0f541147cb503c7699be58970696075c9a4e1e4ae3d6a8fc8304649ab03252dec8bf4e3362497285b9a7e79ff75959eccf09892dbb1206e6e3604214312aa1391d40012852e795b8fc71dfc8837cd18ca32f7ecbe1734fb34a30322244ad5d35b212c50755ea5bde752cf03b9ed3a52bfcfb01b6b6a5159cfbe5f34771155581012fa9807a35f9a3d914909a465adfa2772c479f2608652624f0dfc4fffa7908e56f6bf929c283ed0258dc38936168f994db9fb773ffe7ef0f8b951e3e0e4af19a55cdf88d26eae2acaff0746c8c5166cfcac981677976ec5fffc65ee6508eacf1e3c82ef9e2183362ecd17508219b7474dca449bca54be63f501b4c360f60a016a57684f5829266565978851876ec88b59a3222fd480821a9b09be50d5dde2bc5be3fc568436838383ed33012bc53bac300659ecb8ce3e4f9d25f7731babc79f6cb6199e118d4abd09a4bd0a31fbe30d15d5c1a2a476d42d73949962980afc27ea48f4752daa2bf6d18285440ed6ebae68e249b0a4c08aeca75f80a7687c0d1c7f193d39c8143d7930f5b566ed0cc3cc5eab42cecd0303b716fc5e55be1f111d68017e3c5632e6a7d802455ed0f0c4fc538e2ff57c618771cd462645a1491ce2a79fe81d197a4d70317ea47a200709ee5fac56ffef0af95fef7fb975cab2fe3bfa827ff636fc8a0533288e67bd9dc2333d789a549cd4ab815e0e3ee611226f35f0e11daa5d4edcaa2e91344355070f4ee9b472951e7fdf49bf582d030957bf83817433157bae79f7e43903a18d82414fb71bbcc490162ad536144f8401d255df4a6af3f0019e12e7d5c04f7afe690375ea4136939a18f5d32db9ca47116bee0f03cc951459e647c01c1d13fc58917d375b20c2ad60b5c43709212899c862409624d387e56bbf16c18b3d2afe7b80366ca29a5b66305c03b2a2008143343ddaa014aa4f3ab0d22c45ba1ac6bd4be2d1f84ebac57df48f376f8c32a77476065a17babd700ac86302fa7912f578b3b5cd88e4b2bee172980b66a8178fbdd67648cbf6c594f341f309329b5a48bc9cb1928bc67767d5a023bdc92cb9a41125c0093cf5a9258e72c26b23b141fba83deac59632a96bfac7d1c616157c85a8df775761de925142781418502d91dbdcc49ef861c01665db1cc6d15a2965578e7f9f83932dc97beab9030d58b514a88f7b76c37e18af3e56a20097e34253729084eae5ef0c5f651883feff9faa75853b42787d12d6fe081c47168aa0438f9f91fd412748009d8b340a5b8d0fcb9c9417f3ec017bae99532c5d71708be76c33d3a3e9a5f0f6c039c76053a4a7a4aaea28a0993d1599c66b4c93599b96b7805cb81746dce401557822490634a2c8d7a3f256ea805e46d2836378fa552cf0e0ec7fa07152e1459dcd009e0fc7fe640e29a628f21da7b0e7b7bc0eb831dddd9c3483e81c101328b7df076d015a7a680764b16497f5843e11efa4a23378fb4c1ca1d70b1956cc72aa89f0855d52a512948e2583aea4c712e563099468bd5e255d430e7a94b4d80704a587d484031b8c6ca30a0d07137d6e57da637eefebcfd7ae019e17ee771b2631615695e557845ae7ce30cac56a4cf8fafea67b7d6c81e74a83d498e4a59fa6f5ebae8f87dd3a9e4e2d2abb7c37f3f0de1eaebe51df3289c2f124b143c671f42421ea6f8341e12181000000000000000026b079050000000094607a38b66c814b08c4d19e3ac06152b99cf16b95cee72cc71fb3227f09a454a706ca30f46a4e007f908cad5675472d78c0d54b10097ffc423f3d8cc105636378c03ebd47624d46aa4224f62fcf97ba281e596bb608dae71bee5f51944743c300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000da530673eb711509027530be27589469a44f855103ccac9d3b3c760cbb4da8d8e95e0e07269f1f4fa505d6af1eafc07a9d12a0d962dde3d4ddd3dfb8e7538ae3be8fd5dbd1a962cbe4899478b1ec419c7b467865f134971f60dcc563a2e23e2e68785acdbf9cce7ff645fdc82929ad91f697517eddb948f0ec6d89102185acd8021850d1956486fe3bc811722a2930d29b1318e5852af8c93fa0bc101f38af51ba022b493bd85b6a457df5c0aa9792581456a671a88856a6128755f7e292d1fe893a0a01c8fc6011278225784c223151cdeb3aabca2b6fbb9873f44b9478815b60f9660928b7a40ace62a3ece8a07a8d416c96947661a365a78321d2cad994bcab0c31022bc10ec72e45f2f390aaad9c9828d8ef305dd4bd4a12368443fdf94d3634e3ce02015eadf942dd2793cf04cd456f5cd2b6857a0a58b1d83a478bfa1f555a5042f20317f946970c21b184e180372fc91a47637e7d377a6e2424a2099aaf36e7f3f25f0319dacd451e159925029d31bc9136f680fc2fc64c425b5bb00213ae4126ddd442030de4a1c6ebfca438c5d3381586358aaac31452cbcc82b66a40fd78ca0a39344b57e6aa436f3640dbdb9ef4c1fed4ec6c0835b0c116f8bd0336e2d5a981f560ff4e76442702770e89e5b17447763b60e681bac8501ce3f4423b4a5dc4b1e2c2499241e8385864c95682297e402097ff16972e711acd1bf868588fa6c730339181d0615c3f1a1c6aa4dba467359c458dbe76c4203a1c01131439d5222feb59bb5fbc0b79458b29dc1f140a82bd895875f609d51d5917e1c799eadf4439284250d37dcbd03bc2d17d45c5c8dad2a42766c990a0247ddcfd657d85825bbdf46e81a13d2e3b8fdffc29b46017af3662418debc039d4e7c411585f26e7b17843faecc444072da0b6c8d5b682ac5b9eb903f65b6924c949042273964f0bbf1b6d9fb4a83d6ad996ce6f5faa8251873dd0b5c1d3f504f4d20c847937d5379a6ffb172f9a50dd328706c55549b5585018f40692f8e0d9baeac60042d0d8d2f1976452026d48bcd9f635650f6c62e9c8342f7571099ec9d6a41091c1ca8689134a3f56f3b0f28f32aa3ae1eaab1ff70ca6e3afce2ee707af86b5975355f5004c1456275a113d3d1f3f93dd42797f8f11ecc27da769127c2996e8cb240f0d047e960c119a43ce800e0d0787e15a02aa3b8d04d22048b639c1625ef5897dda5a278ba50748a93fa5c9d3467c66e2494a08517d621099ee10152c436825192af0ecf251628c2634add33e80017e42c693a7ccd059db476d97a10de7321026e2b71d82fd047628b816e711e0bd07f29e5421b95bea45c73ae389d997c3a67bf9e5a9a232ae794912290b16245c8b33dc96fbc14254c5d86b6921be7213f048f72d7e8b3a113d5825f1b3211407a9991e6df560475bdf0979cfb2faed53ff2c4a5e82e44f58d1edf7f0ec1b6eacf842be9b96e7023c6ba0e34ac6d5f6606a39ef9314b5bb38ba5eab9543339d1bd7b7575ca902f3aa7a9a666eb0f8caf07ca5518780dd4400674b197420b82408441b97bd64a163609e5bd28caa41d63abfcc989e1ef68fe2a9ca4d283db091104e148e7978f1f3c469bac68fd79e960fa8acf823006c9f6152c736603c4f06e838655e12af720df572b190eb251d4d88f68da538b03a240b02ee9e6c688204205f98d1cf3012bfdd497c956d3a0e0d6da9eba96949e540dc95903a4194cd2c3f843f3880965711c889aa6b6c3e68d0a48319a7112005e24d71dd283808f494e7c2c34f142bd3ad4046fdda332f03dd9eccd1827ad792c2a5566866094c1bc053721370e6bd35034ea3beb8263557cc89a0af039209efaa24664bef97583a34545bac2aa0b5e82720a7a85b3e95efb4b396a534ba0f5d6cd22ed7e0c83c488d4cc53a87985aeb1eb2d3c7d071d4b682a537765bd983f058177d9d397ca43bc08221a0a426239f10fbf5da5624d7148a1a0fd314613625416babb495d8bd000f2ac67515f434a3002a823fed7c9e0de832a57e21c470ce31e1d8287c181f5767585525ce79b6a72670c72c263434489c511778212d590b8ecef4c7bd82a15243b93fe03fedb68268c4ed103a760750525ffa1ad09df738c6b6b61054ea6e7924e945ac8324e50e7a1b4aacb255f5ba2e9011bb94009ac98c37518f2ff6000986fb482c5ebefd31fe72d2f9d86b29da63e4eb56bbeb20881ef3c9cf5fa3009c3c1534540bb8f3851628db48209c44457163426c6ddeb15010281876c5be8dd0c9bbded6e5def26b26c55158de9d00000000000000001ee2eb0100000000ec6fb4eda6eb77aa5f532554972d303e9565742f2f454f38fe649c7a7e6de65bb699f7312169d7cba818037142a4a1b5820e0ebc9cf974058ea41806e2a77f3d0978028cb920b4b2549d8ee707f59b7781b74494dcc74cccca45e17d244d273b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c3b906d4f93e29ed56e98d5148ff1bddab5ae32fa75697d82c64ed6267be540fb8ec9d41125551c78fcfa5b9b08b3213818fa4dd299f826e042e13591dd3f2e4e13f5cdea5efef44a068c5141bc2fba03dab5115b0a2c40aec6e7dd4376cc09867b892f4be4ecf7201212460db84270b2d4bc9a679c52d7f551ed4aa0d3ee0130302aab15638bfc47fccbe5cb10ab56cada8f85bffc18f99d815f0b745dfc84b90020b210345dbba2dbc0348fb8f2837ac704d4ea33d0b622658df88770df1e442e10a00f989e4a75160e8fc7e799d693902b4802f303ad03dcdad64b4254b11b53fc36abd6d25bb04de382261ae16ea2bd6105f65685c6e2cb3572428238a833a4538030ce8452bc81bd337b8855347d88e02fc641f6a4718e80c17114fbbfcfcbd219303027eb31d32efd20b5482178dae7f37de4fcc03b275ff92341ec85f5f928a3a9902303bb354c4471e5e9f161efd2efca93fe1772a2de922ffa2cc100a30b4d58e3b0229b0683bb6cc3e21605a3de79fce647dc6568b747be780b147990c46444b5ee6032f073000fb5d4e443bd07dd2d798f4913af4b136a46eccba43e08e61b93d2e10a23af6010af38ebfbc5a2c1eb675f9b1754734a92a7e3beefb2ca5af9c3f6437eb5bf7920f79d5782478a6973ac961073fa2e02ede5cfd8c893a2d9ca6ceeafb742655e50a5d1adc623bc15c1a308ca002892593db9e8b94f2b4bae47165063b87a9683a141e57d2481413a65963e1e0551d5c4e96e2f28798622b4edaf333d03ec53a6bf551fa13b01cb78c95b86247c6811d02902a4611dcdfe2d5b56719964a967178155f1f017870aa047188e32e1aa98cd5f70954388de0ae973ed78b82d19a7cb551ce3c0acc7f635a4f4d009b28b27bad9f378266b24177b5f17742338e3eb9b8486f81fa43bacab7f8a923d8193b5d4450ef9e1b42a0572c1667a8d9ab4cb13515a249a76ac528abd2f7c38dbf3409e25073197db5d175328d9f0f2d0450b2d78f61a79dfe0fbb20ea6e00e41afaad4146d23504beea93a812f0de96b1b9a0377d44cba8533eb8eb92575bfc512809c4138e88b121e9600cf0468608ebf0237b980a9e6b9f4982d87f4f4becfdef79c89c4740b69f93db6afa1dce9ca95c2c323125b262e7a7e5cab2cfffce74617b8a30806fe69e173fa757cdb1595a7994baef1fccc304ed627a6dd9ac29b739a09a04afe123645361516d3a615a604ec6ecee9706451e96dacfbf98b84cc62a29320368b81827f3843679b124aba2de73ffd36427f62fe8984515bd0d9ec96e66106ff31fb3ccb1f1e8ca48fdabca96975964ee13dfab44d4d479d4fecbc67b0c20e189f4ce59dd1b124b1813e1d8a9bac8b5e31debfe578e7d7b53d861c9c9ccf3da93bf6ea5a55d740f686f525da0c77999a26063f8e5be63a75019471d7b2081e02ee25c45f0d22ad7f187ede468fa1021cba7f640f33d028cfd44c0f21151523b4135ef1688c13e9ed5c43f90bbf6ce0ae929b63a3236316e4539a122596ec8c11711cf5f8dfdf137348b4e884d0d5c04b0233c72d177bf4be8756f2862737c39284035d631594ccc3792c934103ba09a008be3a4f22c4d4f61c5b1952fc32baf851b1047731a1017864d1aee57f43388d48f70e5aa2eee988d6c9926d4dde1811e3f0b5cbbe59a0b2d28537094de329dc7730de98694f7a21406eed019384d2c7daaa339bce8c579932edd138100bd01f2cb26ae5bc0b02a4f34a8d11e6e6fc4d71a3eb24dcfa643c45c80f070fe15080050335cf9f583d4e9402aa8e2fedc324e54d13885bbd92b3568264797161ba18c33b4388538c9fefec54c31a78e7085199f303c730e0c3ae0e3940547cdb79fb33759a65b2b02dd711988723ad9d604eac11537f3ecc11c3c612905c96603f0da47f70d180fed2b5a59e4fe2ff5d2bd612e2e32701dcd837997a60388180abd8988dd924f7bd7c32ca8f86637bb217e5b0838363ac119f93daa482647207bfb270936bf785e8f01697c7dd98709b4d7cc3f259ba7c891a191eeeb4dd4ab967e2a9ef44c8be729f19f2a00078e68b743f023abde99b4509cf6d41070782ad764c978a8c8be15b470643bc616bd5eb7958576b89ed8fc2162e764fef786dae7b496ceba2a5ff35f0dd5e1da6071bbf43c27e827a4b0d37328cafc2466e3f1508d55e9c01aade956d3f886e06d965eb057a61c40dafbbe5c9404516a768d7aec974afa4fd951e3f1ed4b05d3351ec0e76b5f88cc0012148ca7571fa8b70d8daf2565c13116d501738d8eb3a3a3589aa939faf41fe2f32ed751ee1107f3092200f3520c9980a54c958c5bda3be4fc2bbc45f9efe97673e5e7edb9d7ea9aac796645ea7b32495b6d5663a05816b9376eb4c262cd3b1f3c0bff2f332d67dbaec17813e1ea3fc9a971fa276677cdd70f", "6a526a5363", 1, 1954938644, 0, "5405bce2a43b96af8b4c19b913f4b03be6e13e98107532df2f111c17fe65f90e"], + ["97328f2f01e5eb739a8b22f2b3f3a3293d2c193536f682d61683b3133a28285a2392a5ff8800000000086a00ac6aac52656a964e0b2003afd9400300000000036300510146bd02000000000552516565008c0d2e010000000008516565536a51ac630000000003d8aac5040000000000000000000000002ea2ffaae81e6feb7d39ac8254ddfad877934996d7a840ebc83e6e31064952884432bdc96b9bf86d1c0c4e604f2bc6477c2c188e4315018fb473a216d39159d470f2246c631d4f9456b740aa388b4bd3240592c8c3d16e20bd9457b50f9202c7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b987f44ec24da97119a51ed7436a4ce5f12193adc6085ef978e4fa09bac4a09ae57c9f21a44000097bc6b9d90a0567df0d7e93f93035cae75138b7368db32196a8e2aa8006fbafba55df7fb08e8040d11c6b2f018a9a7aee9ff21cde64ce4fec7e390149b1b028f7827bbcbcfecde8ef5d04f1c57c2e7d8358a42af565858070327b0d360fa90f5604f7b6be3d40068672b27e7c18654af377d01953a07b7f740031aae28f7d9cf773dad3657d0d413b7c05d19b5a1e9368d60f74315f59d538f160a01c52186fd32052c7ee6048f1427dd542d0f4a6cb7738b5919ab2e31d3c5686f1b39d31c0628b18b63c9a40c01030018be44bffd1c0cca0275ddc6a147b1f1420222cc2b0d5c9bc9169ebfb00e408160bc53ef164f6aacd53d00fb58bd4f869586020ab3a83a10b6f5d375d8038386f54359111b161a6b5deafa88e5550f683d877e03168974e265f0711bc837ef871dea9916a7bf2d6a802826deddc45d03990851ef03114f8009336571408ab82ce98c631dd7691dafe3389e4fec7737c202f14bcbb10221ef770ef221c91493df3262f8d318fb7f16dd9a4dc095ebd9d6351a2d5a951fbac558adaaf19551e7e9788f547ff53ba8a67245e6a6220ab82509699f88a7138f7a84d771d5a623518c4805daa8b3a4fbb2f1aa6fa721703d5e23faa2178869e3a4e3464349e3a3ff0c034a4a1b2b8c834ecde370398ed40625f01072ac4ae654206a0f7b7ab72074e606b1f2e82902851c84c53dc751611424254b1a9a86b3202db206a32574e58735f5e7f6da832861626d43a171507c707aaa25bd9586747bddf513b1cd2c74b480ea7d304558e4c4e6ff809a117812f7ddde74374ec7dcde35b6a2dbfae8532496f975409ae286d5b32f3b8947cebfe6771814096444d65c7377cafa1180a43c898fe23441cc8c2d72c53ee8ae7220b73dd961db868fc4a5b61b7cae42991dbad19fd00ffd84a244cf6861895f7bcf1e8f7368dfbaa4db8d34e3bc85d0887fa6cd53800189ffe74e61b98983823dd004aac6d8369cd87c62a287c724accdb0571b428841cc2f0be2ac8c7e1c3d2111f26b4b8f4c6009ff92651bc1cbe3e6b27170106b813a85fda2e9e77186d7fb9d53452774960789b6ad9ce2ff54cbc139c2bc86877bbe2b4d2024b4aa84383c2fc56ba49a6b896fa2ab54660988af85601036555ceefb790f984f4cc7034007ea37d0276fa18839c79cc26915c8388badf100fec3f37be76378fea321e40b79903600f3227bfe66262d92b4231c9588cdccab5fd7d2f6d33b0d9d7295d71f494a0b565c87e63dd61c7c68b55be71ba2a1c4ffc006222e75b6699cd4c4996721dcc020b7626e1903d1f98601e3e33df54d95d4f5c280096fc695a942f4bd237752e764214003329aa824ec121ca1f6b01665fa6dd59fa762db6eb513fe6957da35e74b364b6a14c5ebc0a8f6c13df1b39e2c0f15e7803d94dac7bb31d9ce5edcb470228f7739063cf55a0d2e8fc1fe6d017d38f97689870e79fac2f9a0c7deaa7d1b9837da147298356d22d29e266b7cdffa8acc76844f70f51fafadc49acf46afcd8bb705dd515046531b0e045926180b222058f3e702b21dbfc439119a2deb1770e87a8f43fd55bf1d6682958f756c7e565635032d9b13a2ee287303352fafed76260a9df11691e90bb8460a825903390c172521ed7ad9f6dc541a3c8678036dbc964c66741d827c1dfbff96f80d2597ad0903acc1326d1d6e0cb4e5777efccd2ad2521062e4b54729974c8a015b8deeac1145d6879361e18b6ee4bf8599283bdc960869169eaa32f4d97d179da4b1ef797a5764519dbaf1928d475aec7fcda3c1396b6ffba32f0561c63a1b84199cfa851c3ef79dadd6294ff2d66a18484c7560ae2b16ec22a21a28ea5785010cb942e75ba35ef3955ed5fccb3f29a456f705727e1f961efe6527e3491707aa7a0f8da0e2ac093a6c559b3052dce09c9d6d65b181f9ae6d07976445bd251a385e4e581d1eb3ed145aa0b3bd6ab8dc014a5a80155ce0ff44d348a44db9568aa45c0c941f59f58838f54449500cd9d77db0e4fd6d26244aaa05d8c637faf1dbeeb0dd2cca8ed4034f33e09da2a918a1898e399278a5c8821fd44c6e213ec2f429a3f627944fef239c8d4a93927039aabfa579bf71f045b02b202d374ac4f45144ec27abbf78292a9302ec08f79a7f1256b0bd8baa53c06624ad3982f06a2493c2e266725cd6fafe361289989807ef433b0a6a30c4c8ca326313c13447fcc881237ef6a755c76379cfb5656a439c5c32c60100000000000000000000000008c9d5bfb2a671e20d51c38fbe9bfd5fb5cf96a572fb626f55f90a42c141e619fb16b7a28c0cc29e9d11ecee7e6185f4f10de1e39faef139ded5831cf061366f68d95ef8e7a507da9701d0372da6b41196ec499b375ecea29c04743b8ab0a0fe00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000144f03419b468efb6f690587064b8f8d4632ee65218bd60aa2d86a01d394090f5ae090eae2f7f070d99656408acc21522577153adba5037c5d0bf3a0bde1934f4f0adb2eee841b1b13c9661651e755c94ad960cea19cc18f281b22a15a9de3366b3dfe01786727ecd846dd99897e08b8c0213b72879ba2dfd1402ceeca00e84c030886376c3502fbeff986a2e8ed428824fae4419c8f111577af2bac4e3ee11034020d38767334a9c1cf847bfcc06d92632bf47336a7e1fda038a1d8fcb83e7fabe30a07f387961c2c15c31bcd438e02e5c06ce54aee05e61d826e8010639631a7ba245e146496b3c685b13ddf69cd3d99bcc85b7699fc22ec93a03835bf564ccbec59031ffac2ec27087517d5ff625eca2068cd6d8c4d6d8ff67dd4c9930c581c1a94a3021f5ea7855a297c7aba4d886942d1a712fd643f4c1b98678fceed14195f1002c1030d8c0212d9ac92e7ea75cceabd6075493edfe8b3b0555e9d361ba5726567b074020714da5776964ec238b271469adf45fd55db9c2d221f436b07e704859fb9d6f7020c30e98a1273b45596d360d4da13f52ca91a9190cce26ba173c6b4737fde42ffc776476d30cfe9dbca217332de282b4e30eb5673351e46bd02dadce599e4daace8db54faa766fb905058096ba442d99e8c1db44d361a11d706ad811924c0ae2bc1d8aed930cd6800cabc9fa7f68bb478ddf919f19f60cc42b0fd9ffde82f7955cae60d689b5612f84f7e5cf8b37880ffcf00dfaa8fb1a448aa440928b48c7c1d371fd36574efe94e05a889ec29cb4f2af0f3e2437bf4d4d5691f405f26b70dbb19c2ddfd6a28746da6517588dde437d0d9f92877f01f92da7ed5a41e13cf07d881663341c26937bae79a6ed3efccf82eae8f34393bbe4f7c0d52e5b7ff74d851c28306582efc8e7b3277a95ba1c1d11cc623c684363a4dde0f082b8e0d0f20c60d5d8ff7be8c7a6f6b9eda8ab2c2431684dcb6613e76ae4cafebf979d7b0ecbe43bb8b65e1fcb78889f40e78ff7b197e6c78f03f1a76a79655f56af4f89be579ffbd9a4e79f30f09b329deac795eee13b12c7e2e1f8ec4805ac63e71d054dcdd74719efc65482fa27c4a3fa7119f722ff232881a59e6ed35a6ae0c1a7ea36f6c5aac59ccc3e88763cfd241f6227eed522d9e21cb75ab45485cb59f04c796213754963e68a71c9f68ff9bc24d9dbfcd919d1b8533938dad03a490f7aa8e12c03896a670457f2f14e87a0f9819d4ce571bf65a3df73e3cc4474b4ee314d93d76d610e8b36b2e6b38dc70f14df027e280587d14b96c9a5a49487b1b21861017d46cab69aedb094f853e2bc904fbc4d966cb8516ca3fb37e5dd1476b4076614a6fabe611b76666fd3a294fc81963817ce6b75ba8269ce9add799a02f063eaaaf35767f6e2c4cf6e6d60b5d2b8764f4b1405e2c0cc8d103638aba8b1f403effd2e05bf30446f0558b6cff7ecf3718d860978d75d33184b5698909d1e8da0bfc5d098b68b030f65f5794a1d5836d190933f026ae66d53ce848d2956ba9801eaef31be501319e06b93f096ff96bd65b850c6b596d0f6251fd9140ae93fdcfac493b72fa1705f435cc086c4516a8ac061c445617343ffbc9d78300c470250a4f8b2fa47300630f4c950a055a3c96dadd99edf3da55346c50febd42ec894dbc18bc2c5f0f281e3baf5eb3d1524fe753c98cc64a337e26c3ab5ddd30aa84bd8dc496d107893a942ac83d9dde211dc4217a0c0b88357c781d88e2cd03ed7a490664e7ac84ac0bda840c30d570b700a5c3f8d9fe4514cdb1fec3ee61630ea13745dcd853a96e6e45618f64397ee4cc44220457a980c662468314ac5850356e94763c2562b1c08722d4391a9a06a03019903a8b989e4f8c95610fabadeb13618cf2b22a808b6203c37bbf3f94fbe8f956469a6ac79a2ff8e5af0f7c5dcb9554679ff3a1a71c12319e6316132fc5216cfd4d658c4753be03972bc512d34ddb4118164a40cd772072c71ffb480cc7e609fc56939c6ca2914a990bb35f898d1aecc4cf778740ce63f2425206bce0461f382563cfc7ea3bb8dfd17d807a7c494a4000b2477734c52bb602f78588d867ebd7626d77842e6175004bafbbf4469a23b6ad1dfb2cae8cfab1ebd6159ed8e2990ac39ccb8c7819a7c305b15a0a32f3a734d89668028928586a19728e1db59374c5b5d1c7088b5d537712c491a18ce60f130ee1a9c3ae66264c8ef5ce509484153776929e869b3a560bba09e7d2cc17c1044624f13de4860f9fc2fd8d86fc7ca3b978db9686d7dbabb10d00000000000000000a70af0100000000dbabe7b0508844a7e595437e1215bcd7d69ebb16a19613a91f3f6b3bfdd497c34ada8f4451aa6b0aa0296ea0d50d632fdd490c62ad1f3582dc8f4d8bac9135c4a7c4e1828891176eb65d7272da4ac964894acee33778c34366a7ad4371c5cacf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000481da27c160d528e86164065d9418e10f0f55fcf98078d3bff93b4ebfc174d90a1387aec8e5cfca053e935d1a018a971de86200c8a8a89fe2a62b0b3490df118f35189811f089f91891228f73bd5e83874d49d13db5e2eb1a1bcd8ef5fa75f92ad1b67b55dd07e2c4a2cfe1114a877be32586c00b59427fb5ee007fe297e4bae0319bf63fc89e999093d96f8d0b177c324764370ad31ae30eab65e1b434ec3806a032c99ae8d792658862f14d3ee8606f643a2b418a149acb64e9a2bf1719ba4be2d0a00c85be13cde4db126a154fa6b8b5424eac7fc7a9ab90b61e0fdc2f53f75855f7147d939ab9c4dfa3f4ca2693aa092cc90f2673b49fc17483c5db5f8efa377b1031231644be6c3395c14d312b6ff579feeb89add5ec9a5fa8e6b290a2bc915b97c03199e94e6d4fa1ee12e0b1df3db1b42b1cfaf9fd5a1f570a30d39eb06f952badc021311beff461fbe49660eed065127134b28b53b7216046f16cbc013f996258243022ae7f7e277e459b3bb058d93923be6f64682f68c959c3b8e9e65698526e486570300b411c4d9bcb3a818fe24527ff7c80bfc8cb2b63cfa43cb76a9c76624053f665e680aa1eb912b13eeac384bb29200e78aadaa05e304a294bfbd1d94c02f16ecf5e4a96420405f4124c4b914531749eab110f8f92cb9b00f117858eaa5173bc7d7100d4567877c47e8fb0b3f411e3bc857cd2674c6690797cbfa4e042e5e03c13f1ccb0f3c4f701a7761e343cada56916df3ccedf437e1726ab747e405e8ade40fd0878f213bb7afa97fd2c26191c93138e5815d4c2041ac2c4f6708b802b9c8b8e571cc8766f54487e631014502aa53909b8fd3cb522dcc7b3c41486c8ee5964979166fee2fc9889fe309cb907bd15e1ac2d92c2ed6c842436348b0e01964ac69d4ecbe21137c56e8ffbf8b7271fd963dfee3d3329b0b3eec87c158f87b21825135439e4b55a2807c83c3f6b9452ea7fd38ae7d52e8f9d74ed248ca273d47943320ecd906b7b8746765a8581876d564b6eee2079a389cc2b4f0d77b2456da4c3bc5f600fd83d608251464f14d0c1d17a2916bc403105c6ce3bfaa251dadaf77ebbf4acd871c91d5307d84aad4f3116d01dba7e97d751434b793f15b5d80b0c4d8d49808139b20092746aee72ac9689b12af8c90ade54c8d497117ed7f28b03833d629776b75a989757c12558f66bd09145e3fe51342d50ef3e004728f9461996e4aa3d70ed8ad13962079c3fa2742e84a72dff4c8927a239ab280b3516e043bf00997046bde966c4fea2627cc4dfa4897ab5f2663e516be6b73739315b4aae49a47adc79f3ecd5f274598ae260f16d13e2cafbf71f59da044163009c571d49b1aa959678267b1ab54ef90f3c734648536edc06b17474b173adbc101a59a47885a9eae902978c112e279e0cf01f617d7ab32b61b9cb70e287552622e77360eb95d27e1db9f83fb7218c02f37c289f32cea4c2e9d7885afca51a40bd52cef9a1426c5b9411e0c11866c5e523fa8dd511754e585e63b1f0676d1c49b5aff586e79bc15d51010df4839bdb63e693bd49ec482680f15bfb62c8f25feb28e869c2fd4f88b4362ddcf8cd83db02b4bd1792e60b2aa16870095ace02e21777f2e8f5d9b75691708e557e001349d5c01acf595e4d09ed2ba6ca6032eeda262cc7860c04c1231fd223b36f75acf36ebf530dd07adf06a742b9beb3d41d2ed5261652a956142c253e91dc915a83c050f159555dc7f6a516e5e590c3812cd7d467803913c093e609133d0ac7042f776472a090f919f52daf942ff0bdafbb8b3ffa1238eb63ad1d569aeb4d65e1136e006c71187816ee9f80a71e68eb09129e82c770bb9e3323fa2217712d0d55d7c91744314e76b9162685f25b3cab1d0be2f3e7e185c0172172cb8f0091659447d8ed2e65c990f8b68268a1defb4b14a18aaf3748f7f052db73a02f75831fe655554c9f56625f59d143c6fd3c18f40e70c4961c1fd45b32f470b46e6588c5a60c3aa3f4ae70f3d2624e9cb68b64f7c8c39562293801c24c7755610102a64a715117a9526ba8c18df83bf22905c9cc1c6f58d6f7466df17714817c65d38b79b823e49ca8412f44be2792804d5e787fc4ac9163d80038ebe840da7737bb6b545426f105670fcadea9d1816a0db0ca60cf36a5bb1b1211240e1e106e3ad740427de2499b3cb844acb19a54abeba2fca872f2a9a4adf96762f95af5ceab27c84fc9f540cb15d64ea68764d32109d83915a77d23d1467bfa3cc558060af999bc644e732116c4b20f162940a26bad827b86c9431c7d9f895e732530862d7e1621a16bf042ca6a902b0eed1f69b26558607e138311b3d65198c615af4ac3d05c9f5f7a8f6c94e8d8b67a728423c77e50ba14f22d22dd978a51c6d96379dd7553c11efd488637e3df1e8f200b605", "525353525300", 0, -881952397, 1537743641, "7b71f52e8bfdcdffd88b6a25f21e9136617b96c7d0ced1316c9afc486560724b"], + ["2eedb57c02f1f4fcc503dfba2af84264bfbbe1e5b5f852c01f05625f34219670cd3f41cc30000000000600520063ac6a8d06710cf49351c44555fa07b019d971fcd4d3446b0c2428ebfe137af8c7d7b4a3cdf6540200000004656a53acffffffff028f89c40400000000065265656a5265813c4f020000000006525151636500bc2c515302000000000000000087687105000000004bbb6bd7b3a8b21d1431f306b7bae9879829afc1bc18b2ca0c45b7ac3cc4f8863b69c1e4471f02369c50c699584e1e03637f8d33d022ea4a95b350f0f5cb67cae7fe8ab64ad1ccdb9cc346c1edf79b614e596faadf787adc9031e7fe7fe624e900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d2de90bf5b21ea98499155697b8b387393b666c54104790bf6fa0a52386aa0fb4d51d2b69844f4951f1b2c97af5b3485edb9a9f481ecc95ca2f3d93bc796fb0b7eeae2193766fd081ecfef315d9ddf3d6b6561d3ba1e2862c3f5ef00fc69a291e318e3db8aef29ce06c455637274ff61d044b5f7f3899158431db9771ef285a1030308ebadab22474e40ebfacd56b9c53ffc36da09f4504329f753d3b0aebd6ca20207bf1352ec9cae8244c1f5a63db338212a76606255727e5f2882540a2144db930a04724ae62fe265a93197ad28c99bf868c37fd915cb386f201ccec07e17b602d5d95aa50f410791c06c3fff1837efa00ee98f42124d2828c0cb617bddb176e3be0328af4ad93896d6e370c2033246fbf8b88055e61b8e681dcb1b0f27f6ebe9a94e02035427954d25eb93daaae98edf77567ac39653e4f1931e5052e4a6527132479b03030b050d1d7dcb444d58cf85d05e72d0040d091db212867b622da09a832a738b030677fa922383d4985b70f759cb7f257c2f05468fd86b8f1bb02fead40ead9f780216e8aa5d42cb9a1fa3231b94e4ebca43c97c531dcd507c5cc627d3bae31514d5a8d6d346fe1b6c0b44d364cad82eea6504f65b82369ae4abe4f2f3a29a551e686ac6749421858493645741fa40814a54f6a190fb9543afc5e6f4427e190be30f3a1eb5bee14b0dcbce45caac0d7b616cbeddde70565fb134c105a5409b4e8ce9cdd4bd4e2d7e5a4fc2699532fbae6ff8c64a3154fa45cd205e6d22f25d326a182e851e239765e238aabdbb958da585303336896d0bac499b4ddd11872c4209624043409d01bf6fca60397b8540c3cb958cd2bd3a5cdf5d305d95f0d767535e2a6ec3a19f7e1de77cdb3e8eee64562c5bc6ed0ac553d1eb16511b123e99473158d21c725affedebaa42910c8987fee046f6684f7b1ce51e3363fed46f56b6dc1e5939b10028179d7e99e490224c643e5d7133e6a53e1bc1bcf051b99886ab132242c2795f9990e6db8cfbab25ba3d624f676ad197abec186ddd185f10037f5610a5e74608df48e984e2062b278401bf40c04c01035eed241bd6972500c97e7a53fbaa33fab301dc2cdb9192772af01c52fb4f3cc15db9c9ccfd553ce96fc80c56b7b7ca3b95506fff4272ea38a5c08dc2ca5a38663244d5ee7244b148dbf9cdff5bbfc0ec20382690ce84ffac5cbea2d1523e1a772b6e9853250ea1fb9b0c168d95db19ff57446596333b2ec7d55e647a716dab0e9708cab2c145b7a3decf70921b4dc555b628cfade9882ae17c3983c62485747b1f06930581aa93bf2be6e2a4121e9210f27a6c1a0957f24fd78c098930abb8aaedbf95fda7d8bbeafd76bd3beca3b18fdfe84ce53c33d02396f25bf3f450ff6e23818bb360c670d94a6cc49f17151039c3a0af5f036945cd7b11f153540d0f11d5c725e0e10a6d3af09ab42454c7ec8df7f24b386cc90bac812c4c0e659b7fbc339f77ff0f2306916ade3458359f70496de690f4e6fe1e9171c3c912c87597e6be88cf6a2bbbdb2256056e1c51e6b31bdb65d5f868280bdcfe77cde062b0c7b88876e710ed0eab1a7fccbb4b25cb57780fbd744e89dfd0cac4fcba58dd33e38c895646cd178e35a36784a8484912d2b0195919f7bcda33090ae0d14c7010b07c3f5f39f2eed0c4b929719b11215d2d3d0acc3b9963adf4f960d8b16caf5c1fe821cd904a36e652f2aa384d46e9df01ca912d14447b731b25d30ea5811b3ec1fdde2687751918149ee790a120f07885bf12ce4fba1b70a836f8e9b2abde0f7cbe3df65fc4ae01433bf9709fdb2cb1de24384a48e56c3a72d64717d41e796ff103cfcb845703f881f65bd7982a0082b30f3a68f9f70c5da64faeef5ad33f2ec263cfcde56c18b80fe8983a0e68a833a8605b71c4d8b47565d4689299fcd56a071907a29a45c70b803b26ac092baebb48cb23779c074f61bab661a69e6036c18e186bb01801a60f730c1f81990458e3f65e245ade9f9f0bb28a68b0e28e303f213844cb83b48ab56b5c433c34eec4435927ea7f9d9f88587ed1f3592c5a462909b8cc55cf7a74e852798d6b8982a4f165a37635befcfe3d794428efd50d1bfa22f2470efdb6df406296d8129e11f8c124aace4a2107dcc2e9bf6a6747461613f08899469086be1f6342223641612e2c2a3f1c40abce6aecd25c4ebf66ffe5484242792ad825564cc0a51c6a7eebbe4ff61aa30367f801841f057297d25945aba4c4d41c8c3a3a60901cd6c861c76db27427db0d7c5e1d720d72d4c819225de90000000000000000f262670000000000cea4a8bfca4382911cbf87bf1dd4b21cfe7b93b353d226094d78de0b46aad8f9b0e0fd8a17ee677611b6e89313ad265aad1a8991c3b27b675b052bc6fa0d526b5ecbbda2e0cb76124a73edafcac55702104a9aa89d866ea2ed7f29baec84ce2b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000244a55efadf34675e47e4a4ce42c4577671d69a40dd8fcc37c1a0737f45acc39da79d265c056457cf58249c4c65db42396d3cae99bc0874ac247735cb3c0f1255f4ec6c9f6ee744d374082cee34ab8569ccf17d53cca9917358079565f810d92156e9199657778eaa89b56f704e46cb97b9a34c71dd3093588369db7737800802303b28b607a20f518a93d79fcdf6ea75c3a2aa7779dd8ec6a8f1d5484fc13e07031f9684ba5f6c447961b8cdb2e19737f946db57b7e128eaead366fde10fe9d6ee0a082d67e71c9a49e588de1eea19a9fc8d2c007e9a86b8e5bc343fd060e5fb98954ea7f1c7f74c955caa948c5bc341aac178999d6e5f034ea3ce18c478bbac2c1e021666653b7099ea504765f0fc0e6cc5fc3397492c48e60604e0f4e5343c4762350203e0d481959f95bb482e7e0d22509ac9a7b64d98efc182bb45b26a75039530ad03021f30999e598788580bd28eaa3b05bf9bc05b1ea998303e7036b1d50205925c021ade0d01e89e6772c1e5efe7436638dbd4763b9d4c8a756b40634e559a7228a8031c843f40cb21853b3977a2ad32872740d13220a6363788d4d7e884bc912669e70f85ec7e321860bb326633f70bd4d55f36b45c4e4378602c9ac98d064d8a6953f7ec6653d5d8f463e9f373dc8be3d4eacebef24e7845f20aa1fa1f03139a98940e5cf9542bcc6e359df0456d937922a8596565248e22c3fe1954daabe56fad304546079d3fec97aaa7b2b66c86f75c3cd0ef546819d8d61380330625cd7527adaee0875e9ae2d6d3a8ecc6f01feffd8c17e9555fd248e195ba03055e3ae10408075ad3f3722db4f74503523e87802acf67a97147a2e1b333e98afb2cb95e6bb1f3b0ed886f0e3861de2e2986d7eb11bb847776d60fd00e6cafc923200fb13e17f59ddccdb828d525bca22941c87f160ad3a41b87db8d2f258382ebe4aeaad4dbee05c0d739834a836397fe7a99c7b787979cfce436a92c02ed3101a5c2a7ec7c41f4a8b2d1901dcfa6e8bb130d8817c496933c34079c289e00a42609256c4684a7cdeba4a4e723531ccd66597e41a81bda5842fb1238b6c438744f67ee7e048d16aa1921415faff8bb3c12ed9b561222f400c6a4869ba6dab225c04883ffa59f1d73d703690319633b641ab3e987a84e336bbaf577d58b623933cbdd2b3b36e7d1a5fe2ebe208944a52836683bf4601919b36e44e6853162fb60037cbe45f861f7bace1c47bb29b26b98c2730e5ae87a2cb9bed61e9f1e9ef75eeee5099cf340a070298183c8b2f554bca1a4ce0b10f0538888e3d65b477711417b9809b8cf3abab421c18911a84ece3637cc8554785052008630c67d5d13e4d41d470213c13cc1410f2ac35f9214ee0a057786e5b06c7b50d36679bf56808530c1a67101603d86190374feb413a891b2188e599e2dd2f8e5e7685630a2a24d17c0b540d942a99db867492339987c4958e97a8840f716f2ed088fc5fe1efb8a873d5772dfd2ca26cad06845be5f62560b385c41bc33294be3c5d4293ba3a33dcef46ddddbb6f048f66cf371fac7b0a8282a6d71f499a6ce7757fcdd4b7bcdd7ec70133a9ff1a1dcd336b8b0314c8d99758e6312ecb48c539238559ce1652ae8057ff787787a9d43c5a50486215e9f88550d0209c521ccb2658b735665fa45d3668fd3d702ff6203269f48f4f14d2b79e97bbf1fc2a2d231af50990c5ba0225a22de6ad0902b758fa544c9d51c9a388a7fc2b7d9320c99ebf05f4d3f83f414596cf9b849e917637c9c6baec0716f5f74a518fa2d5ada7391ce2c0b41e8472f779c75af663a54865efed294176e4c8d7bb5d8fd341d5a94db603d2ce71a897f0db9389d42a39d8e2e0dfe2b21c9b044a3b3ce55c5618a74d2d7289a6d603f55b2571759e24d3353fe4c4718c37e835e40ce642ae480476568b1625080210ada5848985f17e9ee49e80fd5bb0e28ffa4fc4e68efe0811bf8ceb9c7d61421680849838ea7fc1d87f89b20d91c1683399be74c0ac1927421987779cc334cdb7d7311f4d76b828c881dc640c5013c4655f8f3310cb10038cbbf0036a97414625cad529c20db8dcf5b9f287f2a0b75453d400bdbb33bb0edeb39dcb67d2d503d17651e5e872a1679ba0bca43100a38d7683e041b0793821773dead55a1d1b910e250b211e41179c367047c60bda540cc9031fce258b57dbdc60f2c1b181052682ce8b6ff1239c938fecd8f68b28b1c11b091b47f4ae314432c434bd38d91853cc62cd07bdc813848e7cd283ed8456fcfb0f2a94213887416ddb90efa1a7b4d51ae92eabeb15afd1b4a298ceaef2645f7fe6bd5ec65e988ac7ba8cbc31056dd4f4e9e6c678a7ec942664e73924e4ae4c74dbba9cd49fd87f1d19a39320cec0c930bb95e15d22afad720dd4e25ef2242d50637d13bfc4f6919d3122204", "636a6353006351", 1, 1173042975, 1537743641, "23c2da6086703cdf4e993bee0a4b9f0496abcbc9b769853d7e9ed6695d5c29c3"], + ["0bb4ff6f033cd6f0b5ab80b76fe4901cd436c15fc6a2b60607f3bc1a6b8af67f676d640ffb010000000151ffffffff0f4313e2bdade04e6393d4fe51a07c443eee2aa83a0deba2949ef84d08a445fd0100000008536a6363656a6a6a4cb5d6df5dc7188cb4d5c153c76b0a019008a8ea7e0946e004b1978ee30895b8ba4f3d1e010000000853ac5200ac636a63ffffffff04ff66430400000000035151652b29e4000000000002ac6aabf699010000000001ac11439802000000000565ac520063000000000200000000000000008d5c880200000000c59c3c072ee46aefc5e481b46524d36e48f18c6735452a9bac87411de2818e341f7a0eb02ee3588f9be55101f3f29047740fd0bf2da4d592e00911d266ee1bd2ef8f2feacbf97dc3607e4f20875f86b36469f60981c9357e4d2f2b06ceb920f900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000028303cefebf0124c1e3e135191aa76f1110e1e0ad3067acc5a3cd32d9ed6f14d7a8d280015861c47db3ff961ad02864e576b0b0810de5878de8f26e15d8e390d4ea4485b5406cf3d8e2bc6e6da90156b5990c022de767a36f04faa34ba010858341c0a9248814bbb5b0b1a42d2e99e390bdb6a4a9bdc27036f1935c62b5c293020e4bf11123fbe0fe9568deba554e0769b185d8a405060fb3e051ecdc419dadb302217a16d85e0b6e11599469053dbf9a08927b1de7598989dfc4f9e7a54f584ff60a03629e8e40d196de104e23bab9f2b1da30c7396dc5b059b1c94671c5a558a379808657b6a42128e6a54cdbab46c1c872b0cbc2d74c00db6746dc931d61aac943031464a8e972f16c8277a1e6ff64586570551f7f8f4b932cc05a4846dff8405fb9021d38cd70f177ca052536b5e60441570796576da518c651c828ba2f00547bdd7803125f160936b496dfa880dc1e508a416e099d63e976b613c60335d66fadff1301032e4c718db1e882662bc6b725c575107e27f2c5c954c4aad066b006f89d050e8b031a336f0c958970d8ebfd67c215c1d9df51d3989e104d42fab0acfb49da77ad075f8ba0d22139913b5584c5c8b4d83a60c0aede9e04cd0df61b537bc5d558c43798b5c9dcd3903d5b3a361f7941336b1e2e71b42cf9708e17fc09bb47b407e4e9f7dbbb640d09147ef77ad57602fd2c190bdcafba1f598ee382f69e894b7c6770861f53403e8f4c2b7d37268a471ae0c179f62f4c1da9c51c44958c3453c2cf43ed7f2b09b63e996c99422195022a61bbac2f21bd786efc431ad3a44f7d9abaa8c710e9be789dafe5c68d08fe013b2f70c04b474e37dabb3dfab35ce7807716017f6f7bd84b2ee20cb1cf247fc2174d2c3898c612f1443193214deb91c20a808e07e3b335e53c57aeb424653b24865d6f7b58d2af65ca5dca502afb7575bae0d431c886c1874bfcadd693ab850c43af7c4ce72b002b897cc1d48de77c18608d75f58ad6ad4e0f284e294131d739ef1019f27a7db4e466af074e0672d80ca6c1959aa387d7aa40efab0030ce0143cfeb910a72aba85b75b36a9bc18421cd8bebf59e905a6286cc52b17b46df0a5fcc754b84161f67416f01226cf4ee8be345184ca880e71169fd9225ecdaf8589ff0af53cd74cc826a129c3cfea3d15ff453a421bdf8f42de19cd1b5d55ec14c000f77dadc1fa68729142ac9d2ba9ba282328628f63c6b06d2899557f6e4c9601805c7510140dd887e459d192b9620d323f7d00526fa72e3fb36850e32dc65d65fa0840a5a29a550b24c295af90868b6207c941a2500a089f44354628055b25a03f37d3276a4645358fad2addfeb936f72c7555831b3d5a19a6b21edb7625de3a74b95e1e7cae9ebc81a05ae7b790698aa7b516e98299fd553e83a3c6c01d0114a2aa21288faac73163436f439f122b489dbbb4fa994dc885fcf423c86286554843812eff6006fe2b57170efaef1654287614ea57500fde8542fc591b818ce4f91c20deabcf6e980b84ccdba44f58ab5ae130b757c588f92889a39ae3b7a701c0c39c027833ea3c55f207b515138348eb3e55f85bbd11a6f9932a4dfc9cffecf7fe7a62f42033dde4afa59c6b42cd0ecb148c7b48f73b06b311237f061edef1e9e13a163f243ff853f4c8add1e9604fc2a2c4274c09e360465056d51af7caf6e51ed9a3703f50d606184d84c6d1d9d0ad93cc725e69d4f102192db23a4ead0bbc240bfc7e0a4be9b3141f735c8fb0d89d1015666e7a63d358258c30f793171ac703abbaf9979bed5483d59ee85ced82a85c1a0e13646658557fec29f56902577533f30247a449064d7b4103d52edb2b158fa61f680223c1811541628799bbfaf25152cf19ac5433f8b6d1427edb4721477851322e331195c445e81aa088eb79f5b3df129e223aa53b76881ecdf802248199230a4c6db49394dae7a8cd318433166c6366f06e80dce1c3cf857007c576285b1d6e4299f7a1498ad4484ef8d2a40d592b42e238e08a192ecb1cfeced2560f5047e37e4e54fecfe361580017f5a764aab9b4eceed2d9ee7edaa081ab2c6d6957320e259e7cf29db5ef1345669f3de2d193279654200647cd09cc6ae47b300bf688de4aa67b14c8a185a10ee7177870a5ef1bdfdbf24d881837d1ca871b75af6fadb54fc19d67db9163320765f55d8f667a8d0b8d3e5d4c0efc373f4e2164b6126709858ecaf0fd15acf21cafd7470998d59e318fee9d4eb863729582f14c2224884af5f6fb224d7bc01e873aaae86328ccfd3ca4d0000000000000000bd72c10100000000deb466f2ff691167c809824bacec129d6523c23e171403c61d7717b7748247c8409cda899a0a372e2faaabdc2a629f23cb49490ddb7e65552dbd0c98a9230665124c8632b470c239d4e46bec45ade08f588ad5612cbec064156f97a44a65fcb9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009204b5c11fb9ff10e188f56e50e19ab4bc4f91a0c7f7af37c5c4cf77a9a5bafb1e84ce5c72ae12da21448a1200e9b9a135c37864ca5a84250a75c4aa3e9ac7e04f8d5b60f4e4615d335910da42e3c90c13e6d70e5c739d659ac50a6936fbc972265b74ae18b6aa65af54e651cd04fe0eac4afb1123091e24997c7eeb58ec746a020a0e8c2c65c537b4704480a1fc8fa1c0014f2f9eb33a98400ee9b618c2e2032c020414b231139faae0bc9238d29c27432fa40496c057bd76c094866075243c44e60b04233a6f8fa50161414802289a380ccd33097af3e9157dc8db3a9cc976f89afecdb4bf407b34d35c821c70656b89d7753650cd30f7e422847c4babe72d131223021997b15a95505412d6af73f61e1034a3ccf1731de1718d667a4f4e83e8aac970020f61c771d8fbd40f195ce5dc801e3abb7c75e13bb790b52d0fb996f6b2ae032903098336c5ed881bd35445b786ce030a1f691a2f04efb28b1bbce741fdb4f5e051032dccd22ba3acd7f4f22e2e94d9a718cf2d4ddbc4c89f94cdf363659cb56eb42e0218cd58ba7b1ba5cf81a2a4657cc43c2a7b11f24ba2a03f5c70671112dd5748aba29ada312cbce549a1e2a46f48f1fc5a9c29b2212ad3b64fed3c9499cfbba252712981d8b2554f9ebd5e252f5a016e48e7845a8212bdcf8973a2ffe811d7f54efe6e34c9f0eff2876ab6044c3ee53f4b73cd3e441caca6d77394ae0456dcdf29628b28f57de0099d4caf07f72e3f34ca73b8d5404745cf04ac2bfd10195f188f3d7fadd172400fd8c8d7b5de7620719b0a9267aa1e2e85cca02f4e52b566fbf2e91bd54b3cd44d79bf7d9ee34251b07d07d0db63d1586d534b1a887b813c5553ec8661de9c79d09a863cdd286b25f0b253c585ba7b2198ce8b393029d63eaf2c68f4c656b9b01df8a8adbfa5436b893a0e8f6b2c3135e05abdae6911b29d510b54b1f19de48cf92d5d7e3d04ac19ddc85ff633d3050ba15037087160fb3be58a4f05c0b4ca5da9e4d803be13c2181f35badcfd12c329c80d885eda0314a2182a116b1e3bc66db36ecff2cefab4630ea4a373fa458faaf30d3801398952250d5054aa991aaf412fb1c11c15ddf0eee3f446ab96f5878ef98cb74146c83f64c535d9d9b85f3cf374f70e25c4aa34462e1587101ef78197606a075be9050c575d31a6038553f41ae0fa6783b915fdd7958d0b10d70d51fe59e8a3769518173fed39352f91a171af04f0032379c602ee129768a14c992744187c8eca7a8bb7243a3560de71e329b726d35b287a823bb6b6bbb7d324baa74783638b7a339e27adf7be449cab1f7d446357043c443b1aa4af193500162c77da2034daf1353671fda6df35ae95dadf50454cd8873f5d6801c111f37f574420bdce860805388c688b160541f36f4a3cb94a83e5372180f806d8caa1016f468df7ff06a98da1333501845611545503b8ce35871d396a0656aa67c6cab8acee8783d8a7394a808780c6af253283c240714db67b0cb08c479b994571b33477b87e0baf3fb71e729be01e320bf8cd8f160fa7dfdbf8fb11fa50cfc0062279079742b9f8022f76a253992afcd22b869e6825decb21e436756249e754042bf17626d408b58384b69dde867455fb6f85fbf3a8ba26452bdf7a678b8fc3f002664887231faf71fd5e0eb8942183d66ddde7344570e396fd9d4c24d5aa5bc1938ceed4be9b0e0945fbe166891f8af37b1a8af9d60b749a432879eab59a505967d6b9024c118387a0538007c6e7a488e36a49b98d8ce7b4789f5f5118085fd935252244b74af7d84a9ca5d1b12a21e6012f491b56371f90dd96cec7d905803343aefccd05b2ab7390fc68f1faca65f4660d8c965a6472dad1e8e09fcabbd317d7fa61ab45fedda9152ffbb721f53f0374d590a57c2ca2b67a095d40ca1fa2a3fa25394fccbe102e15d5c54939996f67d3d7a08167446f40bb97de02cf47f13879a718c50fdb27226598d2e905ec4045cb36f567d968618189db23369a5e404c982a28c045087893427270d4c52b6f2c86f9260d5c0afda8261ee49aa93d6e3a4f0fdfb2cef432f6962445157b290d06d566b12c0adda713adf8c471184561b12f6ec49e15a2f425e4535a48c50daafcd3b601022f4ad08d142fb4a0b5274c6d47c3f33f9e374e02b68c5bdcfffac08f85232bcda2bc71295f1e2e7508d035b4adf7e88a2c403efb52214f4875aa4d889315e9044c6368cb9a1d48ef6f96e7fdf4a7e703ef35d4f5a6332bb4d6fc22f1f24b4f663ed89cbbe50c12af61aa9a8cd4c4a1d4ea55223bef5606203aa80ed045e6fccaaf7464187a6590bb2006be293aa9052f6fcd8e47c21069bebbbe7f71b2c6a81a845cadf2273ff2bd9547547e7b648c6629f83647652e43f8c8e266daec1103606bc9491477ba26903dfe820d3307", "", 2, 1091925640, 0, "5689c31260014ed91aa0bd06bc914e8b90e4702539aaf10e812b1d8b9b71a532"], + ["f06d6c6b01e2a5d0336723a64dc7653c16b7879769a8a40f86864703eaac71e792f8e19b940300000004526aac65ffffffff023f5436030000000005516565ac65afff170200000000025263000000000388ad730400000000000000000000000084da4eaeedd36c666eaea9f92b5a3154cf167479efa952ba492928dffdb8975f5d3117e6f1f71a9d43363cdcfe1e82df2c78741611940b8f7cd88a99e84cc5a63ceea395798285ebd069f540721f62362c0e8374fdfb1b5b7ba4dd3d1d6b298600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf34e760b08f7ac3f4ec6f4aeb2fc2cf806ea8f32841f8ed744c261ee206872fb97f809fb277b39887db883ac51cb25ddda5027b9fcdd0b92582b1d59f54e4d6a1b5a51d4ac06bebfa6a9fb160df281c7bb6d464a6902cc64e7011b2284b4d9bf8456280453ec9ea044e56c9d58602ecf07fe47ebb19077e4ae509a119f53c41020b77bbf0b2669a4ec1368639016aca6e7957e7d10aae8a86145e0843e0c9bb12030527e32e14660271f2ca2c4db247576305d3653e2485c8a9d5b6880543df92ce0a01b22bbb9cb06a6e4aa6b90c4f91a5de763e88467085091bbd3ecb71a88638b72896efbab8c7873fdde5254f1291e097f98afbda90e1c8ae6bfeacc0ccd4103a030e6da7d3eea5d5cb823a82dc4314a090d45cf7082b9e25dd3647323812e0eff0032357d2c3583e8b141496346ff2b2b4b531dc0ff6c05f0c72af3eba09c1f214ff030ac2e58ae1b9d645c6b0ed95debb53056ba54c583724257f14c48312eb2c0a8202210a4febbd2acdbac493f640663304b7212cd8da0c2c06788ec34358a0704c2f031714d61fa63001b74930db5c601de5213a814dcfc876e42da7ad5e31bf4abf220c58389bb9512c7b0adba57e2bb9ca5fd93ede0d00458cd281cee1e612edf7ec3ab77b2d4d732c2691e4783d2a7b5ee3ed6c67b3a58ca47023f13a7c69a4e2a3e7105fdb7cb3d47e87e06eb81d9f1aca7d3a8709b384d4d8748503cee0ab6dfdc314ca65be19903f84b8d8d9467edc921e32e767a6dde11b2822937507c7b46df340c8dc8bfc0ca5ab3ebd0782b3051b3802a709d76f43bb6cfa1e5d748540f2befa811920417271a9db1f40bed09d92b0136e87dadf4fa31d881086937c8fa5fdde53f81257de448b5e70408df078f27b156c276720a73d88baf7364903257ccf31908adeb89a55075530c54c768b8c5dfb85e21ee72aead9e02997f8c44acfbba4e42f0f45ed35d834654dd7c248397db0d76cbfd45311fe6c8b8cabd30d31564e144374c1702aba671a33acec90bf65fc4993577d44813e5695aab42dcafa99ea7c74494fa6795efa18b9794c3c237dd56958178fdabd418cd46af4e1e14ab6512ef1492e5ce4e7128c4482aaca2b32613cbc21fda8cb515e1a20bd4068e1827a48c4cd658baf1741b57e2a74fca16abc5a925dca8e2d0b631f8e498acdb0ea2cf0389fb63cd3c6a35c6ad8fa1175cde00d57adc98a20dfd179f0e650f25c6ac90adee4de149ea303ba1dc798143268049b0328f43b2b67c402aefcd8a5baa1d94f014cf44a6c0676c18f835dd152fab9c1a4551048a20cb9f250e3621bf804eeae9086716fbe99674879b1fbb86b7bb63f3368d74dd1fe772cc4b89fb9bb39eb48055af50b390606acf7802516e7f153f68bcd1e77ed4c3ac007c90419f45144ef6b385e721664afea3bd970d5a5472e271b6f91eb38e578451a46bb936e8485d35bcddf877b38fcea5456d26dacd4c33a78137222ad8e54030a96778a8c6a820295a3a4afe5a63755f86aadbeb75fe73bd8d82ca26d01c071737572a10e51cd0772271c70e08d821d7df2a63f5eb9b05cd4e50149e31c6ead871c9519d7b41c434bb38a75954fcdf4a17a3cdec1412874150ce0aebe9e7c4957f35bdfa38f700cc90ecf3837ee1647d97e263b7d3316c4f5a2726668112e8d570242cfb52b59934accd3e153c29ce9f0eb23a4a8bb923f9e7f4d4f10d18e8e5a6362aa4d171f62d14e85d7e4778a0dc6bf404f9f7c90601b1eb330458e726dc064b4e80030eecfcd8a46493f4d5caaa4ed50562f50cad1bc1ec9a9ec1f78d0969800b5b1e9de71cecd9f8ac9265f23f48702be2db82d84adf288091c3ee427461d0a65a6fc5d430c48d717148a147fcc49bb1bbe2133fd573a6bbbe3e44393c3dc4fabdcca0d6fa0cb3a31230da351af29cc11776f450543ce583c8ed2c896104768621ed8a97b2177f1427020f94df91004250645ff189196a863fba2fdce03724549229268b5ca361f40ae78eb01df4c8627daa927331714b0555a153c56ed2d00d6163273e616aeb52577afb029c466ea0275a852fc0d417e3a2cdf76975a885d680f5acadb601a1e6fc38d270964cbc08a554c3a306830a1227a795f8fe8ac00b962277d8bcf91be5930ee1224c1b105d30095cc7e61c7c307bd7e62fb386bc1a610b5c21c73a1f5aa0aa9c76fb14d887c1cd7fdb896b859b759f838f1f275e12d9f7ebdd9ce77e2047858667ba0c2a191435040010c5002763d251c6b72fe8f1e102a31762c48a12f7d139b03f1a35938eaca914cf15402000000000000000000000000d2c808a9b861dbd30fb51481a76705a6190108ab9f2cbb4d37df02fc7029a8bc1067f89e501a8a212545d01c51958ef326a7b0db02b13ddde5244781815d907a33dae275263500ecdb49ff0cb6adf2abc22d1c37d18c8fa75915bf5491f2013d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c50ae3d71a8c3e632a75df524eeea1a0392b08512ab794008feacb398da99a06205fe02aea23c5e90115b9a24f4b3a5388968fab2dd136a438b995a5f688e26bc4151be119905875f0a7d35c0ab7e381594e44543e956aae2396eecde7306f4b391f58be1dd95de944fbe5e9b436bb3b4b540b059933edbcb01d19015a0e48bd03092c5dc17760b344275d41d3788325e89bc822a7c68aeff9f8e90eb1e2a5c762032f6c661e9dea37ec742c33fb314cf91d86ee3991461a1901ee6456af01d8f87f0a04519e4d69a01fea61820f0d14e5236ed3cf8409681109c7afa71633df5e80e890768891c89315e4972afc373cc87e5f7c868bed9097d875a1fcaccaf920a87c031516d4d6ad7dd939090b3a795a0fe3c1d4c0571dd250d97cf41fb6a488712f97032475c07d76bbf44a06d3f073cd6e64c8cd71039d046cb785bbac20586ad8329e021abb7378705789be467503cfa89a86610023a92692adc6a2378616e93dc586ea0324d70de3f385154ebc641f1a7e8df7a45544bb1342c8b1ba4e4bd18f0b341f67020caee07750d5bb7b51cbe56a683c47892a54c2ac42cdf34fd24de0a811b5d3318fd7844df6dcf9812e26d32475dade61ba17433deb4fc85efd1986d121414e6b8794583e5b7ad82f0bc8551ac3f55b71ae8a85a3ff270f0d0a0c4a08e221630a8bc75dda4133dec2f5fa3cf1382b6599cf44aa8ecae7283a90a158a42db8624b6a0eb6f35539162892d889fa83349b9d03f879616a2699b2eb70ae901530ba16e444b31620544a525f142b29dc39c473ae605c94497936809cd4aff16bc5dd121014c028cc44003a7a60837618b82f9e1e613807cbed63fb86a59c6b88dbd02e2eeffc4f2956804069c8777c5e78281b2d6ad7f0ae349d58ab7fb310e819488dc02219e7fef531f5a71e6c4e07487851277804fa9e0b1cb01d0c215d9c6e0c32d04c622f1fe181725856bcf9408399575f0f445b2e3fa84880995e9a9d1b44f33246e43e8ecaf6763b3f8837bc7de1b42370f433663017dd854f7cd3e2ad6a66edb6198a4afbf50a7dee966f60b0c3b684ab6611ebf858dceda4c5faa29e6cb85b8b947bd688cd57ba64ded8ff00920ca79ab2336c97294a91bcd37af4ada74c256d77e17b529f17f6ec36725e3910645b12e6caeec68ed75027c307c44cf65323c5aa8627f6aecb0afab24289847d50917a346d5843798c3fcd05e72183cb712ced58acfc1380e6b207f0577df7cac16849f670d134f549531aef0839a1811a2e9b02da7ab1b0b8c4106565c133bed10d3abbfc51a07f22b8dcd37776b14ddba7ebcef842a360c1d1492089e7f2aba3cfdc21b45ca06b062423a89b4849b9cebddec86462aaee6eb23607fca4b2d109ff308419b2ffa3d272580c57de2eba58207823441065c4eeedd246264a7bbbb5db4e6e1c12acbefc262cd64db8554bac2b39d6f98eb482ab701d061c8eb4de5ef3f2976f92e2549f00a3f513019e44a9e0a747db64eafd3d5b98e8d210595996a809432e82376724f7e297b3e472b38f8f0c581f8db6f69e58ffc3b07ed88dda46334b86c5a8f0c07d05b9ba43ba11907e40cddd25f49c76951c4b6b3fa34df6edce3db54889d8d44f928ed5ad7b4db10b2a70f20138621d7048e7b58e1539b7d39f8751acf323657c29525b57288937b590210edd4f48ca94e6d7ecf7e0e4ad0564e175a0454f6f218b3f74a53cd065c63994ad7153427bdb7958d7ee9d7a8152151756bd145c4cf47e6e8eaae9adcece05440862a43996391639e23da141741074b6f514ddaa31212f8f93f38354a1b889c836d21f0c33f2338cc9b369e24ecd8ef8a1db42a88f959a05c3f01436afeb0ed04d7073c061bd8ab8fc79bb7be2fadc56612237d3cb09feb9a80f3b9e498f4c163b6f0b15bcd8cf90c37df65b4a83482bb1eab624278cba4122dee1a56cf806b92c0cb1e079f9036b50f9ca19bfee2a5cb140f9977870a3d62d73a4ba8d4cc48ae60e793742aee5756a63a78b76bebd91db20e61eb785d13487b6f855dae6b4819ecc2b3f156b1ac2c82de957ee9f82f6e4749e40a038cefaebb244aae0e785a3aaf7fc5010795f3c68beb8087232e38f5a2ae98fac628b21e4fd2292863ba2a7bc2dc8cb13c3af304815ab2fac49d9d83c43d07b4476837b499f9fd5c4f6017048d2dfd8609c93971ee20757fde44fc554e25ff7496dc53ae47e20d7d5b43e3095f5b0a1b71cd3ce0a34ebcd5e5d3d10f6fe17fe8f52d48c496ca2ff4a8276cb9b8d36d122a610cbe1694ffeaf18d50000000000000000de986e03000000006d74ad6d768e82517a1f8e41bc6054fd1f6219a62b42f5d1dc36eff2825886dc8ab2067a33a4e57d169cf4e56f4f1703f1c475f5e71150963e35a0e21ab45ac831942750565ec2f88cc0fdaeb283861942d7181fa9a6c0c19395f72bc4cf97c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030b4c94c15ecf201e6660b4d09827cc6e9f84100aafe39d533b9c6ae10e559409174aa1db79d81c76f412ef1492ebeabeb88c1516849ee04495cec82b61027e98c811f441df02ceb4e57cfe757be8f7048a500e27c98b7b1baeee59f729ae7d03866eb9a1e682d5b79544b5b56600b67356e41248dff0b2946f0faf0a8be56d7020d47911c860ae3fff3853074ee125d5b9666462fc58a97efb67df02799109ef2022c7e2dd0d156c953797d1aa794dcee05258482891a0fa5b4a7edbc7d01a82a7a0a0463d6916fd5b027af47f130dde592899e923523aed1f99235707992009342ad7c1e329616db3a7fe6842268ec8693867d8677c024029b6c73276a26f156197c030f181f1f246001ee601bc09f74e59e064ff06ffc36546c701299c607f9410ef90329288a70e24d81fb08c4d246e08eb66537d5d6b41cd2580515a0a6436e6d826702227bbfe3c00c7ccc03d856980d529654d19d507c2ec5fdeee16327624c63fb84030257150d3b2c5f9db8cbf5a4f93b5a26cca3bdf66361862fd013df5caf357dd70217bb1ffe23a66bce22508b5b2991e0f9342c76bb097b5a5f41eb36cfd44130ed0592479c220dc19c4f84efc35a018e6264fb1f08d40464a9dfb12fcbfbe56977b7a0c63774372d40cda8b4f7bb137a1f0c8b2742ec78c554fe7cb4c858b4bedcb8e6b55350b4e19371f3637b9450345f997df55c86678b89922e9d4433b47fe2c7ca1d0c4427d2ffa90d347dff3c7ce59163e0b0b0259eba1cd008cc52448627004a2d8d270c91521346ec3a293d9d19a277369c962d355a99e643b1055d10110fb8b02858e2af0580067a367311a2d90544a1f87077ee5b859504beaf7253847920ee57c940b5fe79923d5fa8ac251c55f3f064bc08d80c7f1b9c160397b5be662971d9a9e0615cd2f670abf877b1086875bcf7302d6191fadaa08d63caa08b027c82deccf3560fafb7f4e8a1fe0ad9442c6fc248913dd0d448c0dba31172aa366dde55d8537f0fb31e67d8568181e6c0398d220fd406165ac74685f6c2f4696695800679c3b9f0fc7235a47695eaf315e848ed9aa32fb045c830f8ffcf3a224875816474ac8c9381080ba2e889d2b37e26beb97353d5e2c458f36c16c6599ebe3d3960b55e80689f8aeb22607336cc62e96d40230f51992f50033908c65becc2401d4fa9ee455ef79c1a649287bb874f63c1d40b8038a250b19c58addee27c2a4521336530a80dd23d95f837beaa328ddded6a5cf4b381e369227071e75dcec99ef13c560f2559f5266a08e14bfc0edc11d9228696236aaa2b7b0494f837e82cba80c8006cb5995703ad73b853fcb960bc71a12e19459c036c8d2ca0003940b708e8cc4828d210d82ef3519f17ee64129f3d988c3d55f76893cc031989c0da7726a5c0a7094f2688868e14a2d412c58c4a9fe78c8ffb9500cca3cb502286013101cc69b8706b258c99ad76d6f7c58b3ce38751a3fb3341f1d6238f36b760e40146732779537e398e87fb42107bb7148b57b201a7e6396ccbd781622f1685bfcb4097deede2f85d7d6bdab4af16b14153a969fbc9077ab51ebc2ea2c05805586a84f7359da3ffcb8ae4be57682160b32746c7208bdcc344b2dc94ad70a4dd3925e0119b70543c3f5fb5f3d47c0d80f4bfd7f2ec66857fa79a824babb0169078f0b71fa17dc8f74c0f908eb575a2ba20b99f84acbd588aa4cceab4068e1c850bc2423bd51394ec6d2b907a1e91fe73c631dd9deade28e77df35ad222e25c47e9fbfd5b61ec64f547ad4e4ed3168a36f1025083726950ad5c569c3aa2ac1bb94d41c131210189d3ae811d8c42c2fe9197e4feb4d2112ac4668a491c6cc68a08579d18dda04cf8f49a3f3aa6212732f8a194c65a162ba7a28fa8d1ac158efcf25c1d2d1af826bdd4d8272d51b88fd1b2dd7d20f5b34605bc3fe7f57b5f28c1c95b7380f42f89e20b1cd79e15ee04227a3d9c056a9c64bc46a58deddd2721c73afc29c304d3bd9db0d1320affcd85be7667f33485d7eb75cc8c42dba575a118464fb7c7786589f8198aca0eb90fd8bfd7bbdd7f679ee914b4bd1e01d35cb7df03f31725bdaf89deb2cac8161e62af1670f022437fc5f7d878e0840cab1e7caf26065f0b8a0c6cd530cf86a45d5da49c8a08b2828b270fad69c4cb229a924d69c0094345f72fa2671a2501de389d80fd5d9c7e2a68b316fe4e63666a53dc3ca8f669e7323cfa816110a15d17417cee7297ec3335510de2236ebaaad89a2795791db9101a74d8c477840d8ddc502a264dadcdd09736b9bd626b382ba9fe4e8270cd83a66ca91c19578c07610470a931577dde4e48361fee22d307b066b0157a5b9715998dc8c226fe550f4ecc1e3dd4741863b7f3fb1205b58536caf0a4197b4ba6129ee124927c03e65dfe0753965e08c3190d02", "536a5353ac", 0, -1826300334, 0, "3e659f9fb94fb98a4586c796b750363ed3d336bb53451f4e308a37c5648ed8b2"], + ["030000807082c40302d6821992d6ba4d2a5cb886705828fc4404a72663407229490094474e22146e84030000000452006a63e55aefc68307907d65a831164b1b99891714b211a8e557c6d7f6e35b6aaf8b38d9c49be60000000006ac516552ac63ffffffff04888619010000000004636a516582fc5105000000000151b92c1a01000000000165767727030000000007535200ac51ac516ecf99c42b1f567500", "acac635252ac", 0, -1530100607, 1537743641, "b29366722fc28eb319aceba44484eeb1ebe3dee86788d8f17e23f0ff558f6ebd"], + ["57361001044333c4a1319975661d75fcf743815af687597fc12356fd94799dc87893255c9d03000000026563726103b3391fd0fbb4abbafa0f3d3eec4e909ba9f5e4b1e746c505f670894c29b5cf53f60200000006006a53516a00ffffffffca97178886b1837157c1df36e3dd6b9ef1fabe89e59f51a9b1b0356f662963de000000000363536344d942d4eb77a295a11f6fc4020bc21cd49441ef7855d7304fc472833ad2a0f0177121430300000000b375d1b901cda93a04000000000800516a635363510000000000021d0caf040000000000000000000000008d7d1d5deac8088c01c31444baa0780ef26f7a0429a1447f24f0d94ce5b3a6649389c777692d61cd67241647eedf5f4141a36fc8f1410c7384b436790c22e72d14f8a0a48cc2bcc57049820846ed7ad6e9e482e2ecfe713fcb5b9f6f6f01923000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d01f108eb437652ad180d55a18d19e26df3740d94373149d139aeb88f0b4c9050e82ca82d2a903926d0e8c2f97900528f4c50a7f00133e0a5ffb870b104c40f0df30fafee6d48c5b6b06faec7c89e2c661c4ccdc5b35aeff1ca667b02dc5b9e92bc34f2d0996ceff5b9acd8c6e388910c59e344363ed319ef665df2473ca67b20228264ce86d7bd45437beca620334a2bf5273e6c04bf1b512d72d6a2fe553d654021877142a4cbda327c9b1eeb8eb029d247de2802f0fc8e28e901909cad4a8de640a0346eac7803ac54bac98cdafb53d440bb76fab96c9467c8b5e2791c2b6283508fc9f3ef6e23637e6d250dcccb207ce1a1c7dbeabcc37ea006f580167cc6334850213d50d58341e9e0b2f460d2f10da6a6318cd62ea0a618db030266e18a2ecd5ca020824bff7dd0673a1e85150565783961be5208a4b9773d2b1706378dcf8c60bc303118eafad62451b0647c60f52be4f88b339f162f4f6ae9998521653d692941f130228f7577b28a5acd68912d9d7c645b017906210281e58257470d47c5ff32bbd43021e1dc7c9764c95fc1840367df941f01a811338338b601440859d04389cc59da98f257211c5bbf66a1f1235e72fe4409f9c76e18822b96ac6dfbae335bfd431cb91f7d0100aa58a0acd1b2e50ad9b390bac8337cd94b0efa727e8dc6154d22dc1b08d3b469ae4af1c3ff394c68837997dbb9499f6a24f64d624f726dd620bc074f78fe0b8bfad3446c8ed829959be45836a82a60a99d7c89f527b7a559ed0f2307d45832441e28b39e014ddfaa5789c923ab97630cea16b4ac8a9e8d97955a5eddcd5e60027511026e5c4adb685160fdb0b2954ed55f12bc4f1e87566d27c9cae7c0cf89118e085277dd01bd4b93f808a1e32d6e1b220fc8b9f6164ed3803a7884be5828dfe8841fee06d6a9bade6c8327b33ab468ea50514731221c6000c51ef2e12c049dc2ef043127adac2a96dbf4b5d0425f00590540d07b596e2073ac20f76d3e0b1dbfce8b6281dd4ea3c6021fef57c4e7966c059ef4a2aac682bd0064cfdf95a23a69aa5e1e695ec817c3afaf71236d0737e0bd845824da76cb52d2983643327e2eccf62949e77935eb149882255996ead5eaab35bc6e507fe27b74547a4842c9e6cd79653ac1a9b22cf43b9a1542762b9e186eb42336a5993598dd91607b6692dffa6617716cffe4c938c7b08f20780053a4c301bf8b6c97b1127a43fab09d36dd78a1cc84ce279f93f886af6a67534ebc33f4d1cc4bcd9458493e368f1266bb58b949edbdf8423b8ff1537e52c81da66451d592fad8c98c68c57deccc2d249690c56ec3d2f491a812595086fb6a0001fb63930845705e4b020da9594cd0c4c761657bed1b45c225f873277f3fa8446564877fbbd7652d2d0fc600f1e78e5d9a633480765e197e5f0402b969541fa8ed311c675ab7e20a8475efbdea71677f2a6a27ce87bbc93e639579c68ee4b269c84c89a7960c9a1fa31835f36fff3f991fc5cde5c4c909afa0745beb0cc55ca61f758d1fe184abf0c17917f1a38eda96c8721cf6502bc12b5f0ab8b35ed6bc3d9fa7abfa8b135c10c68aa3ac0bbce71e3353e3872dbdda6d1b10cf54dbe3a3baae4f8c391fb2edc4e02ce94a4df8f282f837cc322951ad766800734abdff9f183f6339998f4af6d6e17608592b11b4375af99e5b74bebce8365cf7245aebbd0131e34a5a9193cba33e90af11252c7de65b3b7d28ccee302f5016ea30312a62303cdc2adf210c81b68e8d0f74beb7604696c8b64dfea572e3e49542989975bde53afbd94de1c6d12d76fc62c2a56b0fcb001a53853654ea29c7a9ea7f154fb2c05d54bb4ff133e1d9fb18e8b4517b969afe6a5663fc6ca35d2c22c940335ced6cc9f489e108f6b0948a79fb0eb71a88809ad5eb234f882e3db5da653b97a822beb6e8a6a6788c68df8de8c52f97f911d9a442e64b21a33143166f73815771118dc1e7191e3239968c441498f055a37d1ca11af3fe2b4996c7aaf45fd3797d084376c8edb5504e3539924f06891ad56b11e1e9d66fe0df84bccf57abf4b0dc9aad7d3aa0cc604b174a262bf5d4d8edc70f7a7eb2ece9420bf237473eb8153feb75dbaaf07d8a2611c6446344527a66d6cfde08274d45745d444bcf7a974f0d3ae53708511c28c85957ede74b37e004e47aa8c35343acf4a28ec84ffd1f12b1b3f2c86fe1bf5aa2e87394e6c674a93e81a9682c1ba6ec57a960062f8ea3178f4648098e4bc9592290c16c8d503d400c50f0d955344fe62b87a08073e8b7ff7d9d39b72c90500000000000000000000000021fb28ac0e18c2d54d1fc6d2da70abb5184176748d8e360456be5141c7ad9125fa8430b4e23c3ae2ad38e4c3e8ab5b01449431cda90df20e5b1a9c5493f54427b93c4d30cf990391333621f2e6eb8fd21dd2ae916bfb4fd1a71e2a1a1a00b0c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cfe67ed554f48744394e2b45e2044af1b7549f4fd86fb6201edc2a3bc0a27a7e534a7582c18d6ed1f836a810ec40ed221cf0e047a6a4d4bba6e6afdb7834b7afb6e2bfa91731028d7e138864311466c7dc158db4a51a335e0f99115648e15dd28e7bf367b4d155b323a509253db3f99e8dad6d2f4253a6b610c0f58d843ccb4a030fba1901b1fc208fbd1410333a30b8858781f2fc899c664f6195d5b5e444df9903082c2262a0a53b0fc65a469885b290c5a885d1bc6ed57ac8d75f3d93788311ee0b045452d897af9363d0b00423ee009712803e2b46543493ea267d9d23a1349b8c57711bee36603ae972001f47c4ca3e82761c843abda2cb07431ed6b23abb49480203e2c604fb5e16af6253352d24eb6f1df6c867ba4f7cb973393990db1ca1ad7102254c1b5c8982e50d07c77c79579100555622a46082fa877d47b744645cea7d5b030017e5ea8b3e7b1ef021b78266b21f03812cf0f274f0c68fddf3fdfd9e0988b6021fc7ccc377296d2e9c12a6c646b3b61663e48ea83509a4e7fa203cffddce7993030e91ed0888512ca9b3ea65af741197980b5538dde8a7eb15be1f027fa7acc4216af3ca6ffeaafa309001a4304cf30ceee2bc03f3312f76ae85490ae592c3c20066a110a1300b5942c197803bd8395bf4030a51b7f742d9eb62b5c435c2061dbd31b9ae9704c4e23ab69d25bd4691d1090634b57d54a92dd5067e0db732c1dcb7d08b43a57d7f61608c9a0fdb809f9a1e44dd6292691f35b49a83bfecd04dacc2825959a8b99a7eaa36f0f8ac7945f289fb5a4d51ccda81688c1036715b8fe761be10005b1b2fc7e8598b9968e365229946631c2ec07045bc595c371018f30d4a904c592dd171dea82a5b06177e4108a732ea7738daa152037533ab3ac9ae11b5ad43b9db67dbc6528104c19ef090cc2f5c392968c0444b4ad2dc2de002da5a636528d62ac675de9ae4189eff0330683fc2a9378cf1fcf0e0f4aa7478055713d2daf4fe20c8d35613188840ecdc4220ed4d3e5f22f9a135d7f81dc9100ef38ac061b3a421e28c88a650a1186579a23ec55b83e60d6359a5ce786be38383104dfb960e634a7352fbe14a919865d4324d52898469e2242a1dc9143ee911b580506f2f26208426abd363d4c6b80082ef361af81065fee83154fd1e946244daffc7b00e753a5fc9ebc8de603ef3e8bc01cb618b972f95c128c45dc98978342b025cb19fd7099318fd31f7463655ab8d57c6862f1b48056faa61bbc916672d33835296ca230a940230cd44e18a7347fa78a04995492af6a6fd2f170c859af425626f349c3ec680f49e05908a85d3a0146e6bb1db219e3b7451163fd94956f2e7201e8295f8b6164794b3f9af6de0e6c4592c390e501bb630288885abf5d5e556088b7ec021ef18e256cdaed79d83937cad8b0c547fa1cd8f8154a885bbd7ecc5421859693b5f7bdb17c8c7ca7867ca42ee0dfd875daa0765746904156cc0a9c4022d8ccc92fe5eee879d39b7a4cc9da284f5459f662c6dea45da7d851e1621945c5c36da0721cf4e42ac9c8e852dd4f641d9fd79c59a0bd299a388c5bac5c912f041f14f10e7bacfaa1ce1233c42e7959ca8c86ac88f42ff9b8dab7d5c9fc2b48a8522f25daca9e1e2944224914b3e5bc789bbea924d500069cdb2b84e2472675a33357d2567bb918a85897bf754ecfe7d171162cb0da45cd175aae886dd9974e2755676ed070d70bf6ff6c3cf7f1e67d6cec11fb49bd508bf9db57766a81ddba1d20e119addfc77d511423ec37baa97ba7082494d22cd53de4011c172850b22c81c6f9fbe88b2bf3013ba198c9d075e7696ad88636a8b78662241acc7ea5209085e50324e5da03e460579a0b63933d6f92a6c7733edc011d7fc116e1085aba42aa6e8863190dbd81a4e2451e702cad27e780038cc110a05c34f0313017ea80847a30aac3e7aa9978cef6242c0e2dc9d0d3832e9fe1964c511516649ec522b99d872adffb27402cbd14ba245f01c9262fa0ccd72e8d324e88ad489f0584f7b222237130a08fa67dce36215d84680afd3b3c91c5d133b58cb43d01316e3aab4564dae65e11ed577e741a063e934e9552e305cdd7cc368e6491ca34587e09ee6328a3f3c594231d219f657bc8b1c54729b2b1ee961416908067d88e5f6eb56cae30d529a219630e850907af63c05cfe9e6f49590c2dece82ea6e7a6e45d9ceea922bd95bd4bbcaecf90d4e5a0adb1f2a71f1070da7f3c8508be0a41a1c0239e6636529dffefefc9decb6bd02966deb904e56554e739b655c0725af631fb5ab8c333147c9b39670f17c431e117933aed975f30067dd20696f5ac118298989b5df22d4ad51e804e39185fb35c17343a3b086b42034f9de62cc7a31d13f988858f9407c8743cc844a7533183942fe75cd5e2908a3c65101", "00", 0, -541017162, 0, "4b79c8b4aa56312fb7bd1de424c0eebfd514b89388f6d186182072922d63ab9d"], + ["84e1fe3f03426724a61c58ce0550b1c9610e1464a3ed165d94da89255affba2e60b8b41c5b0300000005655251536578af64e4109740fbbd513e75b43b392033359b483325cf926ff249e81f3a570296994d2b03000000046a51535280ac90e3ae7b408c2fcd97109551176ee74ada93a37836815137c8f2417c2d73be2dee6e010000000952655352536a510053ffffffff026db0e7010000000001537dec5000000000000600515363535394cd701a00", "", 0, 1878472092, 0, "94bad49c811e7863e5066ef8c280d07bc4a61eebb45ebccbfd791d142a5634c8"], + ["24b5953c032b63ea0f11c7f96f9b0fa6ad0f5f67a68fdaa7d45b6e5e6317fdc466bffda4f4020000000963526aac6352536565ffffffff420a01efb9ad67c6615487cfbfc14fb544f79c416a6dbaf85756718acb794a1001000000096a536a6500ac6a5165ffffffffbe81cc0c3daa5d291a05b02ebcc76f0b57c78560df70a649b306658b297088460300000004630051acffffffff04715f9d000000000008536aac656351ac6a336d10030000000009536a6aac51636a656a363b5e020000000004000063ac240b3a030000000000000000000100000000000000007967f2050000000050d1a5c90e2e90cc0f6a85f9ae08c1bdc4af6d2bae7915355f2971a46288202d9b3ec2ddc58dd2a9bce660045a601467114dd56bae20035b8cd419cd2919bbec223430125f7271fa9ddf2962224633e0a744fe8c09c63582a93d012e4856983e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050590427f7230c91a4f6ab206b5e4733396922cda0434a129a2197ce4b3e510bfda8b64db12f3fe947023ca7264ba4d1ff347683732ac06547c3e24d9b9cca0c4cae8c2be992f0e364efc49e76a926e03d0243e2e8cec1d7d2dfec27bdda54f8551bc577a5b77e73b14ab056b4bff50aa313b2d085e872bf3fe554ac045dcba40214c7d2b8c913c0057c6b7dcd4697823743fa76e89f4b61f4697bdd339cdeb0520312a0b68edcc848119c5a902f168a3989c9eb9969150011fe8399be400a35c3890b0709ae01a0e268b666de74eb9b16ffe2920db8e91a6f0808664ddde7c32cc59ed6b25efb4f766c9163c0d2570b8393cd7df8b19b260cdf83626462471f98c0740221c475b92edf3aed2f8096c3f2f6c37ddb96a6af219739e214ed853bb5e45ab102170cfcceb5160fe2ea1d05ff2363d0f539b0f139443d99fd35dec0824198e492020e2aaff643d97de3805513d13b9e4daa335a31e1ebf42156f0fd44521220b74202113c799dcdb95785b21c04066ee5571ab4f5daea63ea8164894c4144422cf73c021f61a437e430d67274d032e13b2c197ef7259ca787645eedcc0f24820358a37af7bba97912a91a22b35c4d1d79a048634a7b268ecc12c78c931e58f697788d46ba29d1bda01b64a6a681faeae7e4fb0818b96c58e2b01e567d36088e55f9b2bf38942ef43121b4e54bf4dad58d8cf45a0776a0b92d9fa43ca4c26babd6274a0835cba0e736667c686c90c8bf4336fff24ae2bc569bc27f7a8db6e03b84b0a2a88f331c4cf91aabe6dee8dffdedc2665cf121d4b007038b4978891e4a192eb5126611c2ee441a3a38ab9925afd28b25fe5e9631fe72a52cdee750b3d1979f476d42b2eac764a689f0708325dc891b98bd191887a1cd3b09f57a4cfe43d651492362f57e6e1efd391f1c9e17d09fe9554f7fc6a6eef43d3efb87139584e5959ced96e69bfe3aa773c06204012911d4e401a730b36c5fd532f9fe6764c808b5c2fd00afa62f4627970b80e530246348bb7a224dfbfb54288bd470abd41f96795b794e88cb36e1a5f9fb9a3635613e6c861e9d17c740f8bd1b519155f4c8ab7a8217d9b4cd4d65f6a34a507d30a67a26b54a3f79d8cc5d39d21f5f6b2f93a231984dd6147119f537a8724eb23b6fb21e7bce712760e423326e78e426506d08b1391245688d8dec1a77cefc23dda54fc2124de2d7063583e3bdb424f0bf444ba4f1b785b85406a819dee436d692577b5732a51749870877b9633693cd50b74b5d87d55474b4e31ff3b7a3856abeca0aea067a6691d58a753cdb797837d4c147058439a42ad9ef3e191c892d405027bfc526f4fd7c40ace7cb009cc2bfb8e6f227c7d0ebcf95946bd16d41260a75c38007ce93793b370f96c61881b73cf9c7ab9e8087384d5d4378510c9ff94d132d02072c348c7b6ec3024044e12e27c794fa42540d6a157804d09b2ad4e54a2263aa0cfd679fcf562fca7bbf9724c80ae3f6683ac217930cc98cdcfa45f1044f99df18cfb4aac67b6c8a1e5923e9d12d414c384044ac4986e99bf80af6566dbef72ad70bf91c93d21ffdb6c114ad87e3be6a2cbbb71f5664c1750240abcb4182d915cfdc57dea76228dc9d9cace2f4d5c9b7569a44f67e92f9313655cc0b9d8e9a3781bc121226e00de447eb13355032e62a7aec9b811b0db2be0ed41f02fefaa97ab13f7ed08dcac40bad4a6fb80d9037f89a8531ae80e80f48a89d419176a196f55783c0f2c9cf687a3b42a85e6769865bd8f29a165015739c6e5d9c93e2d766fd62d1924c125cde6f50999fb5180840bb3b2b1fd10c17195590813c338f29471c9c1ffa1655569d412f9ff309347aca18f966e99129b6d84ad872a0209bf1e046d006f721c6ad457f61a50b19096c40fd30bd9a5f8ed6b6b7d5ebcaafa21afe91cd31438c289fe0eee0cc352134232ffb8298cb1c01c3a9d6c668e39d759f84747ffab7306670f402b785bde6ba11f0dd7316b716d206d8b4027f7c150d7bbca1221d31cdf0ef5cd81c74bffa68c6ccbf19e38c309396dc84ce6dc73891f688dc9d65cfc2610a8219b8bcb917eec5ae2b4485cacd392e22c8b599f05cb010de0e5556442a6487e52410d171a1035f481589b99ffa3f2c7eda42a2f2f75d38bb29e3911c7071a87dc4af8a2b2ff376b75db92275e7b3f39cc444441bd057838b8f25b3cb26cf1427a21cd245f329ec781f657e4fc6c10f1f8f7796c5591f6e0a14d2ee202052f29fe5c52c275eb75cf41ad934a6e8b70551c13007b3838a144c8a137ffc88628aa465504a832aad254a6d34346ac1675911f9cbe8a20cfb340490c5e2d6baf7683fcb914104f42ff5f10c70436bb35031ec3b3203253db3f1c727c2413510a0bc7c48dd5ede4e869203995cc0349aef1e607cd881460a4ff2e775b4fc517f0d", "63ac65630000656a51", 0, 1518800732, 1537743641, "7a9dabd283479c3227f448c8b0975bfd03e6c06402248c4b86a1d8411d949190"], + ["030000807082c40301ce503447eede5cba2aa0e7a9f7455c7e8a0763b08b2c32156d8d6809236c55f30100000000ffffffff01f95f2b010000000008005165516a65005200000000000000000200000000000000000e4eae000000000045ef69c33ffb8f32d93221248e07ce2bd7881fca1ee26e5b721f10bccc32546187003c5db2a536493a407e625fe0fe62fed1aeee4dca7b70e37edf7a88ed09f09f8330d1e4683ecaa6d18768a6926fa0773028a294b6deafb73c262cb0568ca8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004ad165aded2e3f17d11f3f0c29d3da85ecb6623bdb9831a34c365a330b64d8773ed152e3abf562518ba0a74fc10bf51c1fe3736fab0778d11d40f17fca49c8962c74edbad0e0b7f69c50609119a184edd545407a22598f7cebc94deca706e94dcd6a8aa9896d99ff72a992e3447c30976d6b25b2e2fb2b0b15af0e801fee6031032f875abd130f53fdda2078beb7306882df149b3005e0cf72deb3bae7c170fc8a0226839736a61866cb842b1d96e68a2f2ecbac2921ffe4daa032ab3d747afceae80a05ee6f941bab2067e66f7d8bcb0fa94073c4c837673d39541f8f5d3d9f5df91decd36b00bfca537a8cec836d79a49619d8ea83344ac98e2fd9535f77e5bea40c0306a55da6e6492816aa93dc165ee59532f72b8cc32e81e4dbec7801ca7e01b44f022c02b745ef8b7c0986f41c0636ece392b44ac7280988e7f4c0d7ef739a242309031b8dc3c581e3ca70c9a54237199f0a54447ea795717c34ed4c022ce1ac0d4b60020a94a609f7964639936e62955d9ea7a6557da1f0729d2e5ecae6150d663fa4fe032435fa7b897eda47274b00d219e15133211f48dcf6dd590e180034c438744acbde921d0bd84e8f9c9c53ee86b75d6ec1693a3be51e36df74264a1e56ff4bd5dff1c00987bda34c1edcb9f76c7e97ae2b26948931c364911e08ef89c914c44f9db46239f2fc84ec7929d45ec63a6bea4a87369888ec1f77629fb4fb57de7e8b37a584ce416a68511c4af2e0d879f0e73ff44a4b5c31231dda436c3e28c38eb1a6031416abd2b590a5b8b80e456a67ae6c7292be75aa976744de5176975e6d39ad0daa360d9c0a130038fd583bf8d7ecf83bb58a41dd9d75b55fb37ea7872241ab4b3313ef6ad4a507d313f92b3618387cc9c29dcc2d1b2d302fadb740aba76047eefd422bf50b4827ff814cce6862ad0743a6d3936f77c454232561200d2734dad7d563d4d5d32ea18a4ae6fcb0089314be641aee3ecc714a1ab7b234d4e18515c10eea52f36f020a927bb0a449d61a562493bab56cd738500f94095896472d7f45a2691aa650dae46753804a5969eb053d07e025c40d983cbc46533c75fe63eb2e0a15b72b292acc0641933cc2a353051bb82925a96677d6b3bd0d7e83cada5e7eaf06877964e5977f93c70c99f6eb79c36fcf04174281c63105515accbd0c343e8601a23b12c06fa1318af49c4b4aed2c824f49b17ccbd4c5a648f6f8e4a96364e2e93ebe6aac4d063560807682b033f834042cd638181c3ef33e3f10145d0e9979e19f746cb98d4db93dedc0de0c30d29287166610eee50b1645f7e1e502b207b28f75ff9088108006a86704c45ca7be6b0245941f200d3f8eacb33d8875c2961a8718220b7af948cc554fd30332b06e1c8d0552ae136ef717759cea31f97247cd52b456c55ffc5f86d7b47f93e13cee9c3b5c915097fb3b11741f081a2defedc27d6e7d6dfe84193c1b1b161189daba76a5619123bd2c48bb42affad6a5b6cf09049b0c76353191cebf831516860f2626bf9731243ad428c0dff27a5fd7cb8b8c8359af43a68059dbf96c91beec70bd5b3a886b2f9011dce12acd3d7d99b57af12b54430218edbeda89935159d27a619339ea27f90a1bb7ecabcb5603592c617d8c08579b3e1218cbc001515c1efb3ae3e79ce27a4452f3baacd9a25e83f6041b8799b37996b121e53de21cc696afda88484c0a1d5c1c38204de12b5efc32d40e2c761c56b1d3bc9a87aa6eb20d4dc44fae881407f4f5afe032b78226b39eadcd68bb6da55561d9f897f9be93d0c74e9c2b70aedccdbe431b0a771b199f54f81512a22be53f4538da2cfd474941f4589e6ec415b91771745020a46178b2880074698aa7aee149e1983d510632fef3ecef27a2b368088041336dcf4840ae4d570735e18a3fc1e123a96bd05c819c7aeeaec25fc9076742a97f0481de25ae667b385451631e192753fa2254bcb0c43936750d1f6920292940cd929102fe6be15f10653b71eed9549f85b724cdade42ff30d2beb7287c2ce17843baeb7251d76f64d0f11618c77119e62f9d6b15adcf2c13951615c4177fadedcc3b07d15de8b72d8b84114cfebf8f95162625a93d190d4def38a5d8f90e9080f9994fe754be75a99844bdaf4f517e0f35479d6c5ad4434c770209d17e3d7167d248f126b181bc8db146fd7c0ebdcf4e189e15709deace893f4b856da5574013688e8736259596da88a13b305ffb4eeba72a582f5006d7488c47091a34098774450514f1ff0ba5d5c6a554d7e6f7ddb603a7adf1d456be130051e38050000000000000000000000006f885c8302e0f5c1bafd8d0123e87cc616904bc178e1140f861104a57c015fac42642bd292bb12fb56ff40c377cbe66114dd57dd62d2c57426b8395e19ac4158287909afbd3f32d08cffee33000331307cb3b4ad99cf0b17b913989b2ad6c519000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004b2ea9fb1cc00814c82902c05ceaadb8cf3caaa4d69fe2c786a25a0036bf799f6ea00bcd2ae8a13bca396b1bfc19fa51e7785380be5f8a67a5ba8ae079a66e00d286e927c23a9a6fdb28a19fa1fa7eb01c968a41071d827102f3fed09042e0a763477072d0f2ca9d6a5b70ffa23cb19d70109e059d9ab6964a0b04d4c7b9a61c0223accea79871c8f7b88af2e01fa6e7abcd486d4a8b9c0b9059717e16cd8c260a020208ddb44a15caf0c23755311586d0c6b6973c4dd03301c1d85321177c6ad0850b00cfddc7699547e8ca262713353adc1db62a992005591bb0a2ae5db3f04b731e481762e0da46bf92a519c110eed60bfd9cc2e79e3e7e3cefdf92f0f1bccb563b02272a82962f55c005a4c795943ad2863cea47e05e4396d0894c678bf35473ae4002025352ab717b7274b186c37ceac66b881b2a906f6722187ae064765f646fba720310126c400653c8a901f6d34a384862f645de9da1396c62b90ee85447031caa8003121c11654ccda06c1124defc3fc8a5801e90de986cbe4b8a1d29d200196e9813020dfb72fc25d006bf624e0541327ee764d4a34adb7158508e23f8b223eaf3df34560fa2b3e8a97bec36da64205e77e96ad1712e1235fdce6042bc868a8b5243de60312cf109c885aa6bc0947432460efb6471804eeabcec20702bb786e821e115886af9ac08da096de9610963c5f981ba053af5662083b46133f722f2912ce7bc465cb5d907c810c1ce958bd8223772ebdfff03f03e2a5b94e4e3fcbe295a91ff67294d5ee7564f4fbccd0ca836461363f423ead1fffe808a8237d9a668d8edefc47c054901fd721119dea60606091b2681a585a10bce9bff342366d0c55463c2590d15e5ec3cf628b8ee9fb9425421db41dd221a279d201773bca42c64725708cd1bf2a435498d020c5f3839cbef1d6b56411fdb00b5234745662defee21a19620aa9082bba632003d56c90859caa49c5ac53b1f584f146d48d12d37dcecedbd50a7a6b1ee6161db2027e9be482c21eaf370c7c9e73cb9c11b9072c40f7c405ff9bace51444970c9b2183f83124b21606c8d2330aeb3f1fadec1119fee6f898081a10e44522e7ceef237625bfee15e4853a5e3a7e36f06b05b5a331f66ceefcdfe9949dd8e8d90ae96da907c5d5b43197fc688c5e9a311635a5a42c205444a31227d73fae6a0f33e439c5bf37467dc0a9d861f88f5b746266df9be66ed331fc36be5966b1a7e555cbf90b81c9a114e926192cbe9140a9e98f20356ebcae8d618c367ca776e69537b824272572a086fd9946ce33bd29b8edb4dd931e875732cac3baae97422e7e71fffa2142081b96f0c3eb9240db0ab2c0d9d7da15b0293acdf78da1967cac518a981e2fb87768721917e81247a400d94aac86eb4708087cdd02852648933821399b76bfc115281b47dd73277c0d0e48ca7bfccb0b999c3c19cebfc33ef62fb4406f7b3cab532e2f53842c6ffaecee6c072f109d730060b3f86d59caad4619e4478e0c75bee808823cefc032be907c3d3a76b56ef74c77bbb9dd808b8e7fb9dc3757793f4ef77caba8d47f6e4486defe07f250b8508d7594621e02a57ec003c935aa605eea85d20b30cc108321fc068169753d280fd1178bd89fcadc64f612e9c33eac3396607fd74b2c53360a11f7a0d82241a39487278855d4f1f4a718b5d320d0490bf5ddbbadcf49c39e8e9c07df6524928ba4390d7f1060c61872974e6852f0441bdbe5459525b5788869ddc686342d47040776be79849a504c43f5e673e8cc3346868a7c9e320baddd6e7be738cd5ce6876d2d39a2bb1e529a4c3c0fef38126237abb97034852b56ac3b3e6479edf666b2ea8b86190c087b01027eb66f7d9052023b9cd1fa6339ec7caecaebd6fa068ed94cf135450e472686f7b8eb059884426cd3dd7841b13e56fe9459bcd658377b88d34efeabc1cc6540c599e15de943be2acaed572cbe76f8686a8e534a12bfa6c2ca1638ac4d66e67fe787b718120bfe02be45eba27476dedc837103ab363c31ed355bf9eb445986e4e87753dd0278353b0af38ee3e227c3a1cb6c3fb9d7c43e3130b81047c10b2a07716f8d1085eefdb5e62e41348a35ad5710c581ebe68994ec976f414dd6989ba4b1e6a0649b078ff7ee658dd87d7312586491386c6c2159fe9d8c59b84a421b46d74dec0d48fcde95445c96d71e4d4229b486629583f679928645f2832f58b9359f729b72dcf0c4dc2afd4926e085ce503b247a69c8f0dd9fafce61e28e6f711de09971c5a3eeef8a1ffaad14a0895b346901adfcf83e68dba26f7daba30617262c72179eb660ff6f8af0cb992c809ce0496f81359960133c89ea0bea7d05a3241633ea19e3c7b1e308534aa36034c64ca8de14d2196ed03be7bcd86fbe5f41632e28288eace326ce1d221aa391ade0d", "520000", 0, -952761114, 1537743641, "560b6cbc95955bd38f4b07321bed342f63b5e6722d2a2a73f09c68d3cdcff48e"], + ["065f892201d4a519f0f1c947bd47410d126c52993f5572ce80f479ca03931c462c94a97c690200000000212afde0034e16e404000000000152b1377a01000000000963acac5153536a65638c4ff0030000000007516a6553ac00630000000003df1605020000000000000000000000005a070b2e8f371f34f9270a141dde8c1f28c0841de936c4baf3762478b16b06d99b3a4029f72a3e7aa8208c848596e6934aec26e40894774d177f6ca8e1fafdb584708abb463955324cafcd8dd385176b419221a7d7eca022eddffc5cffa09ec100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a940f456036dd7c2b73371685d15561d2c9739d23a7b108ec474a43710b706c76cf07a9949e581cce87b542f4dfd34c7230aebf28dc3b2a5f1f2432bfb7765b36b0abb21a7983264e922d4ef98f29cab296017a3ebbadb8c3e9fbfd84c14d508f59a46c6676dbf3be48e24602bfddd78c77a1b37533d71c108d95046dda2054103294fad6684472858456fc1ade370b320210c85e41d7856495bcbda36b76ff6e503002ae327849ea1bb9077473ac4b4d1f27a1ab7225eb420f8e2740d399d0aaad70a06372435c83d1546a6fa6f5880ac9a1bab781d6d96d25cb6916dd3e16cbe734c0266c010ef6a9789ea70f9b42141c1e6f79b8597c686c84af62ba1a66e3662f7022f29d5fcf8931d936dbcca32412dd83e49959485815e7c838c95a43ebe993d75021686b8b4206505e1bc6ad568e96701ad7c1e4dd837bcec6523ef344b76d528240327a10fcd7425fa3e3bc1f213a5bb970bd556ea893a4d7df2e14af310bbc2f95e02253d368a2b5ba13a085240eae751cf76200a47938a40fa4cfd955fccf49d73450214aa5a1efb49e529f42601ef83b56b789d1b5c82a60bd3e642b8b85a13d1b391d9b895d5805a96e645905f643a65891d22b30d1870c6c64cd8f22b958a99466fe43ffe1047fa74af95e1052ac111b79260c9ab4a31723178d385a22e908ca0e77bc1859302a7b39775174a257645ce850f4543ebf7186740eac698c43b3bee0cc0eea971166c5a9956e975b1f178a8b3731a1e75fc057545ab248af1cff189cd6f56d7c7c32e9c64af6621b6840d9cd92b17ceda2f23f4673c452a0d4ce45c9273f5ef371d2d788d52e6ee272ccbaa819fea053cf0c0e82d64919ff37cc2179b0fe6bd0694fa1916a0b1cfa7ca701754c40eece4584d52abf4dbb6acdc76f7016dd046b4f41442fb84d3341ee83f6ef7bb1648cdc1315c1c907711d6aa825227981c285754a554f1a8313f710000a9dfde5ef1305909f71b11e7e74d81c3d47b6898fbd943bf0ffbe459c9ad325a6f27e0e344c45a3a90fd382e7bd2570d0fab5405088c912bcbb1e47e8be95cc8fefb3736776358053fbd88685eb926bd93157cca48d3b6b71cdb7f16a188df43f9792877c2d93dcc51baf27f160fcb687f702cd8a1c2be15c0d9c2c6a386a113c79189744b1cde9a209fab5271878f22104b6168c6f46c3f4ba85213646c54f25429bcdab908ad1f394c5ba651e941857f078ff68718e7f1e8edbd48875b5928fc3f09afac0d38160b0aeadefc4ae416477b2f4b41c1cd8da8856742333039783c97ff451710ed1d611bbf9c72b7b91ea468a5cdc621ea39d582d58f288118c72cf4e24c8755214d77b7f6d438b55e784b87e93b9200935131b25d2ad196aaa7effbca5c4526ab69ddbfbbf2ac7e35aa25b14ea90bc2578499f17688d407db7a31ce3d9425b0285ec1e7c674b74e93a6c80339c1efbc19417eae633a8e3e452bb6e3c953c71b7436831e5aad356e58b9201362acaffa8d10034267d2dc7d3388609289db1450931f1d836455e50fca735d87ec02279b96cb9c204f0d285020ccd96aa7b21808a8b6295776e43c86f7f6e09abc938ca9bc91020cc7b73a3645fbf716d912b3982ced0e03fdd7fc499012859bdef4d2af40c2e36b78c9b1933e8d95f92aaf32b2eb03556a18019fe4e32a55581fe74bd17d93d5020b987239ab372daa029bf3f0a18a61ae33cd31c4c8ba3ae7b4e25c34d90419d5d45662272706222d782e632d4c32d73fe61be73d76e81b71984cfc6db2a45f669e31fcbf756ac9ba6ced0f5e10783953c46868a84ffc1edb43639ba68244ee6a1413810fff98148bba653d92cf5d70bfea4c50d6b740eae25b219125e51404511acc1bc5039b2aa378419c682fd3c2cc4a29a77f95ba93fdb38ba2c1daab2574e86b99e17bc6c31ba3975cb309e817018915fae574bf357b688dd9a32ce4f415c6f07ddc1a6965281dd714622ea79a863e1c55b5e443c7de710595bda407fdbe1a94f16e01bfe10253d4e7ae318a0fd888baafefdaaa3667e5989949c8fa6b766b0e9dd4b233c01dc863e7feb1365ffd936ca0d7955038323505c7c6b16f6fbac2a43c123cb1ae6c9e0bfd5337e738f74d564495af968be10970a40a4d391f81845e63da61b6fc47cae5891151d9a8429238e1a31bae4e5666f762f024cbee65c809e2a479653ffdb89afd5b99b24624f9fdea32bb5a3042f5322fe8e1854b4f64d532c1b9ede1b1294efe234f6a04990f552b3dfdbb7da11692ea4a9687f9bee6f0c2a4c9d46d253ce6b54d41010000000000000000000000006072a9b16805f1e6e662ada3f4c7b4acac1cf26af7459c9063d23ded0662393480c1dcf2f0132f4c4ccc9cd8599b51ca3d27d4b19d3e3ae775732453fd9382025cf686d40d5c026ab808a5bc591b1d52c4038a9cbbba5d4ef1a2455a7cb96ec500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fe80d18f0baa3fa04929a5cc359de182567fb8f64ac87bcea8792dcac7dfeeb1953310db6d8d27382ca619c1a372dea449e31e2110b09b5270852530138d2d1802d2286eaa9331543318594b3eca6b23853e4a44435b60b08ce1e19c69cdb3d63bbea380fd7921de60fd57841c50b6a2e1904fa80cb231ac4f291cce1685619d0223b6e084c6ae6d6aab84bc43365f3b502e41a3a35d66bf9422e728b930642f160302bfbc83da86b715e5f5bd2d4812ece6f6d146b3a7955051ec4fa75f0da3cf210a02582e9e0418e96498f312d35d6ee47e9c25f26e44fd3ea72652c24a2f8b6d1af019fe808a2cac48deda06c35158348511c3c0f30d53cc7ac91a4ea3b0ff6089030f47a3fd45ce795728906020c039667e40e16a9d22bf820a18de5e3bd76d2fcf031a0f21968e3f4a55dab70924eeb7a56e503b0d329f9dbcdc60c95f34f3dd8cac032f0c629e618312011e2b976920527e3676524f5c4a1144f57e9006b6ccb0b1ca021ce978666ff829d0e3cab143d872f6684a06350ee34ca0ae255174e03c901c2e031ef53c14ad2a46af69956f4a74122a1b5b42cf25b7fdc743fb55c321ca486648139d0b5edcb656890d17268cd7a77cf7e6fbdaa47760664d9fde5186e0a9002b80bc599edb5c776ea5ee9e199298a69557b1ca02b8671c34575c7f501cea434d9f36899c7cae8c0fd754d62f1579ca12812632291c44c68427c8b4278f3dfcbab7535b1058db6f6a48f6b648e53defb3f0b8452394223ab658c9f55c18f63748c27f2b2871ea638c8d002c84fa691f20144922b0e8ac2b0e6e6144d98078ef4831788841a33e380e94abe54412211d76f289cbf9b916150683ed94492cdb2502ccad43aa74701e427c8ab1ed193bc796740f424ed1f42be315a73677cad92ccfcaf922f3903192adb1cecf0d8919cd338b1486f9c9b0683f727a14a1be0ee3a2b89562d77dfb169419b67d6b77aa3ddf7f9042754a67cd530fe748ab058b5d00eb5da7910173652481f85e130ccf7e281cbc753cde86009895f307edbbd18043518599c9edb4650cf2d8949c19f0a8e1aae127569d4f27342fc4b2af28da34208f1fe55be006fc04a75d7943b249b6847ff3beb61adb7b9d0846a9ea9e45bbbf94b3a72d17c4b4856ccd3c98abb64bd3075f34192ddd8dbfbc961843950cce6a0ab4992b762dc00ccd3a1c94559998d2e0a6f430f816155ecb468e2d06ccbe81ac0924fec6a92299f3356818b39c1b1231cd1cf2aef909d8e6cf20aaadef88dad53f445359d4f192c90bc9c5b0fc25f4e8af363f3751385fb81ef51b6a8d702577697891d96c07636b901b240c8726bdff0d8e5552be55e15c07cf349dbb6282e767b339939b3649dc15eb88d685500ca4e2ed2d5c99cdf7c65270815a50704a29a885d897e125534c80ba22c4a34158e495d157beaca5a0bac9d22142247c60ef4c0981fb0b33176b1c5497267b88bf659ee2045b61ca53d8c72ca821ce1af6c9f2372dc4784a433e4803ffbaac984af403f0a66f13a8bb6bb644e78299554765aabf618b23e471a9d7af376f1dd1e06bdabbdc6cd86a2139f29f81ad8165085881cd97831482d306aa747fad81e812a5ea17dc840ac1fe0a4ee64f467b1c485c4d3f9fec9030ce7d57c60e82f65759d2b2fdf1a60540a98ba9acc704dd9e0ba07ed7ac825cb1911b09f23f19e29ff63d4802737ee4a9acf4c1a685324ae44dfeb9c4e3cba7f3f5a0ee92fcbeae6f3bd63c116895168373de564edaa4d1f0aab39d61020ff6205c2d3b3a2eea3ed7eaef0c9652ed357c8da0ee51db0b5c09caa3ff1289d127ce433d1d2c5d390177d88d1e17c906d9f7eb691561fce9a72baf7b3cd4390f08109e14e93087109ce903d9fd812e46aead45871e4c910064cb5a070a32dae7b71d916bedfaba7f445830e56e45deecf9b81248be9403451d0b79c889dddf9841383afc840d5042561fb21ff99c2a6bbf0bb6b0d84d91b9c37aa2788071b88ce23c1cc0cbde29245b4101d4c72bd87f554eb81099515d772698ee9efc35b1a81740ae0ed6afca395ad9dfecd3cb2a152c0faed7b0c7afe462eefe2b1460165f13c5a32e0ff67861b79a1e4314cdddb76528732b921858e9dd2a11b45f6e6696e48c1e5389a1b06b0b92fb2b20f1742e3a2f93f0f6e320c08da626eb5dea1c373292e9fe81f8553cf187622875e3733c0294ed5deb5c24bd0ab84bfcaa80f6d7fa86be2e8db46883bb2a589c514d1591e922432541bc7cc430d5275c6e9aac5f101795758ea49bc5e973e1b2e40000000000000000ad708c0100000000d0ea4eb0a5f4ea9dc6caa90d33963eb10037e8ad18b3e049832a2afcf7f058e56b843eec0aadba827673aba2c822bbabeccdee1392b34e6565bf9526bb91ab7cae01a3de2a3b42e9f26a50a8c961966bb122f0e72b72451123c7263ff2785daa0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015e46c7756ed1d9f4b5abecae6c6dcab700f8e852d99f7589d592d3ebedfc31c04396b9c0fd1d78bb8c9f133bd7fb5a0ac41809e5349ecc22bb1f21d341cb5a48a00390eb7165b454c5aee4b39821b5d2eede112317d642c55c266eca9b98a13a8c94fdac3ef07db4e6e58102a2105473872aaff8f5c3fc832b10b8fc31e23d20325fb7ee02c39c2485713a7889a0aba317962b21485bd95628aaa48aea418433e0305489ecee4e1f0f5bb6c1316d4d2794d7ac5a9ee26945ad9adb8157600b7e0690a019ead4bedf4cc448193d7a3ef6251014c184df9918103095e329bd04d237f49c25ef14759f588e25314ea44606492ed09941867bf8bdf0d221a06549f855a400324b59accc537717622b0147e2da02d65724e8b527bfaf0e628de84b1506c67af030361b36b66463f031e88c8f34d0a22d7610c4666d088a3063fea31f8f8cfe55e022ffe221c9cc2954489257753432728b75b1d3fe409c27c87524b348c1fd25f520220fb6bf51cfeb9e891316a2392f28d472b2107cb4eb77c86ca1461c44e76fdde0213f5ba95386d8bb985007d1d998062872faf65302ac9b7c9454ffb6297f0db66f319462151ff9e83ae0980d04ec291c10cc3ba667888c291c8c81c056e52c7ea74968da5d182b2e641b6fac55180817bc7e4862c90e3bf61dc775428b5984353b71b818db175217c14137db9cf58a5a7c72551df141b7ccc8408ae885462f97736d71d2323b1c023e352e902998d2f8f9fbaa399d0c7a31b61c93947021e7e0bf023b4197e7eff2d73f86d4050be82e57f7e5e9a63e507ae85e0b30e0168ca8a36070737a94600c4748fd14d1b5e9e334d12a8d944f5da476e923869e2f4e8fd45124f8a9f88540cbed1f2380f296d5ae6ac266d2a4efd9b42abccf44ca37a252158f4b21bd70c196f0b1825398891ab472a23de86898c1ea3240c8e9427ac09286e09e64faa6a003c5a06cfca34fb05e602084a2946026b1e86e84847a0bdfa31be5a6911bd53d6829dc4e203a89499724a46ca16292f2444250a1a6ec31fb3b9b9f14adaa677d00f20d0e1db77e7fb1fb7ec780bf8b7d3eb56e5ad386543b96c6826b9b46005230485b792e14e18c438f25a0c592640aed5fdf88a48eda50e836bdc5569c3e81b0dd1ecab92ac22f1bc73b99f7ddb69fdf228f2f6c90e9ef7880622d3a959cfbfafbb6e88a29dad64023536c89b706dc22d6b6e3ff2b04c3df8c8a0f58e4055286eba9f8458ea9ef3dc1134c8c313c00ce86ec3db0d730d4ad81a35f8d8a99365adb67b838c66f9b961b159a50f8c632a077448b2054c9b3d71a90b7089e96e86addfe76dd51596a11656e0f2269212381847f064cea87f2e895a3f5edb9cde0ad5ba33023291779f5957884020ad68bd9fa103f08743bdef24f3f9e84f8a96299c6f359fbfb6e0da11e044867769ae167bb948af1b87b1fb2b7c80a1fe330e0f61b37754a1c6c826e71d2e1fcf133c9a649ac8de2921c856be46323a65a45720a453f5ba502eced76fd2713bc35184faf67c8ed0764f37e23fb88aee832bf8a6762a073283e0908b9e1d037297d16b7ce7c5755fba3990ec14d532cdf94b69687e5f7303abd85573f0596d56b37d336001aa209e5ea0066bd472494da03c3f0b30fd2cc3e238a590802243e299962f3dfd8d05121669541e431d5c87a3be0c262f5ec7168acb2ffe50eb8942e2dafb8f85729525e9311c2f067c83ae8cf71b60e1f401a75bc822efeeddd94006069f9099365f48e8c073b759bebed9c9c65d74f5ecf89d73a71faca0d83f4dec69182700e710a20fcd3245c3ee036197c389920e3c1b0e491cdf8f306ad32130a67395359cd943d70fa75ef969ad534cb098133202406a5ea54d6c711e1faf9ebd0499c702e0fc33207b0acbe6314096463b04296bfc90c3a8718a853ca727e9963cd7646056e938a642a9cefe65ce4fd48e6cea00385b144b48d12ed243a356d2a4060ee4be20c0cb2713c2772aa48c4f1d508f8a074d40801705e4f7e2fcff2f0971cdc667b4eeb70de5ab210a3c7cc982ae27f502651fb9441ca02d2ece58517d8671b42fa5d74b78d5b99fe26fbca0add167c3e2035eef3978324c26c8fa09c054950e60fa1f4d95f4fa32d24f2dcd912f29ab483b3dfea9dc786cc2f5f2c33893982805ad05939005afb8c272e6689d8b17795555cb8b629c20d09d32ec2e06505154bec92238af6515f95d5dcea29c6276d2b47b42604865827f13bcd5a21aac9595d4c75eadf3d8b4d7c93706ebc75f89d5f04dc9d22856a3db8bfbb00ab34ddaba07c2898672accc9e871bd2caaddf7b20f42c137e63aaf58a0741d2f1963cb399ee03bb736328301bd0a01bee9b2f73bb748d3a053124b92873d69ef3fa7d152237904b0ae8477a7ff1dd59b46a92dde32b47f2cd4778b303", "65536a6365acac536a", 0, -810573223, 1537743641, "df006e9084e54f50304b5f64ef527dbf4ac0f84544fa31cec136a8419ea813af"], + ["030000807082c40302010a3057fe70aae3f72c8f81340a40e90bb1c3d1bd7ea5979eb4e4fa01f74c650100000002ac00ffffffffdcf2ae656f5163022e68851c49c47e35a13b548cd6c35972b6dd4c63946b361a000000000851525353ac53656affffffff04dcf4a3040000000007650065536300522967d0010000000001001b9d9c000000000004006a656a8e6fcb01000000000952005351635365650000000000b3831a7a03000000000000000056caa005000000004daadb0d8ef982425cec7b3c20c29c63b2e3d5e797d69a04cd9b50bc45b27698b2a6b733639e5702a7f031d662ee29132721c6915e8d3dd60ddff1a207207edaaf31f61cfdf0b53c67586d3b580304e5eeeeab9efd4c76f34cb588706c1a7d0e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2006d6f907528732ce29fc0a414cfd81bd1569eeec21dd83caed9983ce9f0f4675cd8cb923fc3890e49abcaba7d926a105892fd0959e168fe556abd19bbd4913debf71f33c9f507bbbdae652076fea74b6bd2c978fd49e33b185c5c63e38bcf1f40fccb98412a261cc4990227951059b080aca7a154aecd34ca52d9f046c276021c7352f4ca9a7f5d574121b34f26573c064ab04259cedeca3b150f793582e3660309e2e7bc90c0fb5e861e6afe11069771ec01b0ce14dcd84c8383ab357b8920db0a02c65e8291d47003e02b7668c87ab83e6e6f5ced9d0276e5b5bfb25f7fda4aec9675f5fd0a1c501d4ee25f6022c57b59512e0302ee8064521cccb61d4e2a1f0a0325d7d8b44d54ca7db9167ff034b964f800e10a160819855fd6df1ed11bb824b002102e0dda32c615209a910af1020afb59104a0ed60addffd7fead3948eaa4d2c8022d53d61b30dc00c0ea309de654da61d52013d1d7011c587dbccfc351a067eadb031eaff41f6ef2a5b4772e0c79fc982275ec949a731b6e0d476d75583c44b5a3f2021073f84db5969cb779f25fbb9e21294b70addbdd76c8467e1bf8af693b04329d6dd647d40e1f205581da8cd4dc43d6aea3bcc10a570198b85cd4888ebad6bbbe2c2f21c32f3d658fba1a1a9dd68f198a54814c1c5208ff8c5ab8c3acacd9884f70f74b3acfabc1d84fbf4788bef87ca72a0bb1433649cade49762db34f01b01ebd1fe145455c8f70dd4beca27b48b214f1d19577465f4d129d30d21f31a10c7316dd06baf615a1e68dae0423e185d200b5cc5d4e44e3c98124ca885abeb6ca5eee2eaa2516832d62376d598b2a56f7520a0a208762fd7d0ced37ae156ed603aebace569a60aac283f11b0bef0c92f328cad8477c8bc8d41751d1b0ff1d523684c0f4a6217e1486f604ee69b49ba378089b196cc738a253c705eff9ab9779ede0c6f303bbdde98b96f2fc483320c6583725ecfc78b22050333eb5d7a1cdca13757bf5fc4ba6a434467f56247b78e3aaa58f4d1f72f4331309e3c98512ba8427b440418cd0929453956b1b7dff745d05bf7532045ff41feeb597d7b77e0f147a3ef810cb2bf54f684b1e2ddda902e7f8fe2d232cd3667555130eb6c7c5721467aae8a7a78003668c45961e0950a196aa08e7ebf6afa6b3123bd4c9c633f5f147b1dfcbe79220293bf1026b2439607c469699963841af56cd0c3226525796042d48ba610901cc300a7ecc8f1588891b3fa5b760c6c3121f45c5a06f0f7284dee691cd699ac8dbb32933b4040420a77b77c4abdf333d3759378c42fdbecd854564340fe0131915b27c901ab1813bde646f3325a5b4d4b4ce03b51a004dbd68803cc3d1690b32814d4961b6690ac082ca5bb7e34b9fc6e2c6a870adac64a5e2d4d3691a42228b951b205bba2072e06d2657f79f174418028ebfdb8fa854a4f6ad1eb48797eb74d2ae68b46104c19b6711451665b90f4ad523e8971438e7b5b19913721e487230435f9b3e9a05daeb75c472d834c56be9174bc6916156ffa7376841a5ef4bab68840943d3a1b002ef8bc304cc0cd78492546f35fceb645f1a66a88867b158429471d94ed6aed77e7fea0e2cdd4027553b194d3ec80909a5f5e23a134fce4f6829ab3c433053f1cb6226c3c5ae097595abb58a0b35d90bfea2699440f40c0afdc76a2a28a8aada8d267d93fb7502c4d83500555bbe945519afaf949b57cc9db27d0abb3be22af638ac0e8ef231e6f672e5a93420e49b5ef48cd686ff5f2d9eb4005da839b7852e7bc913f9d96010e407733a5fd8eae1efba7759c393ffebcd561f287f724b0faa1225a80f1150aef42e43e8e604af05dd55639431f56a23fea96e43381aa694e399b97bf4ed23ea48d9a4418213436e1b217003399b6c419d938bae3e97eb3b7342caf4efce98a6ecccba4a4633fb70abda84356f9bacc9fbe2b54524fec099f44c19c30d71ad054322502dde2c88fa7aeba419b5396bde30edeba9e7b13d73b5f516750329bdbcf54537038e531ac0e16bbacaabe0c4ef42214e93e3a89989dfe3087569438d551bffadaa26ce886c5255be87bec0b0d017b77af50381979dd54b4dac4e3402be1eddc461586bd2320729099809f178c1d74898e132858bfb7351c7fe91af3fb0b8f0c71f370fb9850df74570016cece66beb4ba570129957b45facb5b534231e119417ccd1261770b9a64ea9faf791f9a149486bccf28676a03f009724ccdb939bbcc4b9f29f68398206b93ee5444572206b9903dc2d1b41be4fedcd6fddf7a2c48fd0f6020000000000000000000000004fbb7bf2ddb1d3b35d90e6ab6f290d34fa766ed15153aecba158304b0d4cd3f7a9bcb24cb74d295e47cec249cdd46dab951d0ad8c946512b14ea4a2c0cd07bf727b1ce8eb16228c4c0351d2705e902fc733642550e29ed42fa525679ec99fac400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fd1b460fa2859186cae3d9db3fbb31b15852ea6ab67f1b04392be616d694820be0a2a2702892a7e8ca31ddf080e035b1fa69ee09276057b38c4dc42cb92c6e9cec622fb06c61d6b0df8ee8391ec86d6a4e0441f5dc7e27e58cd901c1ddbaad9658ff964bb4120687df46823e5d9364101225c24acfb5d615ec808761b65e7344020ea406d6ebe2eb3920a88497d6a02d7509d1a320d0d40bce3284a2cd4cfbe47e0300bfc1f0155a46be0522f16aca86e014383b90a69f78976f1855e81c049dc5170b04103a5523353872b81f6ac15fceba0e394d4602b6cb96ca131ff59e332981c02a47a4b3b4658d7624128f05fa9e7d4a3ec706b961a4f92eb158fe11fa06212902260c8f74ae5a04328d5a033f4b00dab36523058f866ac1360c4e151c545d3a5d031b672d29866af76138980ee10bd00c71ed580e1e1f3cb3d34e85ee7d606a9c0c0304b521cae86312ecad1cd827205e15ef4c4e1b1a922577da34fe5f441522c28d0311f0121ca4d86cabe67c79986230efc66d6ccb93ca5b613f4f0ba2749c187ceb021a83c1544773a0aa95b3e8f3c4cde172fe7198e8a6f317c34807d7e46c4646fc4b76dabe8958130ac3767e8bcef2c3685d36b0bff52b73b32f7a72c20c81cc5062066068af854c9ef4052f7b2483eae066519228945a65b8f93455fadb634796e2127a5338d09c2b5d9cdb930419a7ef84016b8f8e7eb2ff1d7da8c480fbe17ca9901c5a79a1116fbfe44f38bdd6a3a341be29b9b50f056f243ebbf6b7f16282b12a198450799e67b67ec2f109f8163c573eb4030fe83274a5f947af82f51c66aeb713ca0b7b06041ed587a6df91248d6fc93535ed8b54992538c22d1d834148f224bb26ddd31098d5229a69ab6b37d87514c2cef37bc09e6d300ef056611a2efb46e25040d8d51c17a6a95db2e7de0938ad5c4bea88917d1e623f0b186526efd47506615c4ce42bfe5b2331ba2d29c5eb2a4c3b697e9027f4345c3c06e0ab48347dd1f5ae35ab561715bfa68922ca1c11701d46139e5499f022b4a64c81e94f1a8ccccb64131a95f628af7f54bf84f9577e4afaa54e22f68a6347136f9a5a3d3dab2e87b83c8bcdf63ba7b5d155608fdbd8afb7eecb424d3f22475b8ec58b1db065d95c392dd15862be019dee18a9f9d1e73d37ef27d2a3ec8d05c5b7166614a9236d8ffd3fa45abf662d1b25311f9397469d5b8e0699f34254d6b715929dfd8116720d69f073beb52c73501817724d089e7a9fa2cc3ed41d08db8221b703f2c79a67ce0e0f06c12df2f257f4adf294017ff48731eed4a62a24786c4b8518409d48b05c2bcdec0b78966ab9e1add4666100278a76befe6d4796bf4a4f6451404218601cdea30d82a3d5a35e45829377d17e0296dee40c4e62f15e9232d5494b0f4c9f4ecc95640d5cd69539897d04cc8a5a4ad4d4c3b31a6311995bb80474fce0faf3eaec4e63fcb19948c120bac49c7b41863c769aedcd1e0c59a0eac3b4bdb1e5b9242023fe290570b8b8c749dd0b3d956e6e7def2711506d13f27ed45fac7f302573ed7815c8dacdae86315f9a2fa538a1760a5d9dc34c9dd11bdf422610dfb6eb9eebb0fc9fe901f3cc635a9ada5dc25d450bc9eabd11435191f5e28da13db3c79ed5c8505bdff9f1c9332fcfe1e2e08c500cb92162f0db4ccb2fda920f726683617558725d6024689a50e5c5a63bebb69c206a64b0974ff887c0c22e469eac77cd8c6362ad840c97532dafd6dd711a3d9b1dbe3e76357456d14372c7c0484b31cf471b0e4208d824c747c088b3ffe410dc6df09214fa763188424559b05037ae2bdfb57dfe6c2ff1658385b058783da9fda2cc5ddfcae1210d7776fe8b53c642ce3ce4dc0de688ec2cbd8485776800fdb602461705bb565bc3662a83db22bb1715b25730ad4869ad3bafd1703a2fb287fe9186f9dd1ca5f7ef6cfb84f6f447af1386b8ab488e53a6110a603c450f9a1bbefd682b7dd614a9b15b9e30376b8fa684118b482c0e9df28668a7bd2416ff748cb56d1a59c5cc9dd03e95a8eca2f7163e6a73e9a77d2ef6b43c4a083b9b77fe4ef8f9a7933a8e062db7b7cf9ae04df1d22395cfb10fe6e4b9b8f36cbfb7a788cbcc3aae3d05b5acd847575d1b689bb0b41e1ddd9ec2e9357c8340926fde436baafeddaec89975d1ef56264ff6bcf85099448a5ad8558ae8193d19c7011133140a2852e5447fae463a3828a331ef24c3ac06199e25f85fe0dac4ce3df583dc712d286d1637fd4d7ca4f1cb34d7c4e88d2ccd83d816aededaa5159cbe68a53000000000000000002248910000000000dddea4c3d892a156383586ea7b98405e335272e0b980fab7da03d386be8d52855a5ae24ab3bb13878f703db1184d015f10e1071a5a8272d40074b9c601cb6b011a865aaa90d60d315dbbc951afdc7ee8ea3b3673493724e5c684759fa590d7aa00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000291c660fc3faa8f844307ba6a49fa17a8ff3f8a8050d07addaf3e6c2eeae6f8720da35e6978bdfa9136a09171cd4a6e4b84e71c2c46289d31793d8ef464fcf9b297f6c87bc38cf539a8cee2bd9ababd756d793cbd32edfa764a966e4deaa024e268ab6d647732d55763cf68f53362c774503375bc44288794bba44befe7212030311b4de8957a3342990d5862f97c1acfdb77aa8648c6dea8aeef5d7db17d18c470201db4348e37b66d0b8d52f97305d7672da1fd6a5fb87d7c44fdc7504ec2cdf670b043c26f4a79592fa4b8e42492c60f474ceeb423c019ca281c7e123b87942e24efbb078af83f2d3390f1b039152a09fae3d8c34c497d402d821a90ef1bf248733020f7a13368184c142e6f4b19eeb6bd5ebad147d0b97aecbaa57840247e33eca22030904a733480cbb4191197be262e9ab4eed91c0d6b075a392144e824df1576b36021ad2249df354efe1247a56441aea44d905c6e3853a52aa18402ae5a6771bd3d1022afb947a61dfdf2753d31272104525895fd65def42d0968b3b658f181b32ab7403119149fa804a622abcd77155a34e9b9517fa6e45f7fda4c08bd438594f1b076b95bfdda85ccb2e4e56580f31e551831e1ff35ee857e8d2cfb39c76c298664c4747e131dd3eb478be7202425bdfea0f0a42c1b2090d90201a6c3067bc2ed830e2d312b6795e5b27b28ab19141a9d7f8fc1b257b9dabed883d5116400bede0c2743a233658ca7b810e8f8d102b3e0353cd0bc4992da91ae53780bb0ad6d06088d4ef18998165420be646c823afc3df33a3d73091737801710007833d73311afbd372cf2f2706fdf9e997ab875596551d305b3d53755104069ee179bf9aa3fb2867c412ef799566f215fa385a27dfb31608919a54b91d034e2d24eb4e4b8d88f40c618855cd11c3c38949cfd3ba1c0dbdfc1848b2ce0f13d6857b48be7aacbc187beeb63afa190d1a2f84185b0351648cd8509adb49f240626efbc7b8b9df98960575a03d1993d6bf0d93f94e0c837a07622f2c2e68cb000dc25b04de3db6694b3714fa99bef7e80d831cf908a86c146451a4952b0c37c8afbe03f8cea5b2a11ab2e107b61f1dd7bbcdbbee99499e1d5e463b0e06d688cffd23224515a8bfe1ae2f705900eb6de642ab46c725f5fd0f2517dd77666ff36df5100dbca765c0e2e34f2e50da19d86201541037ecab7f8ccc27ffad774f83cce06c6e362bd4afd6b01522c8dbab4eb2bac09bc8411d7115fa18d4dfda31ea31e962dd69a1534219b895788298cff5218e60dfbc75923420f5acd57f08110321d4da1d0140c0eaaf889192a4c68768d95774dd281bce4c64c5bf0fddca4da462b4333d3b9df8747a8ac93d1a6e0052b6eafe2fc3973077b3d0fdb66349deac277830fc8d3069130d0a7871ef5ffcb700a805bae1a02683988c1c503cdb71ca067e8d9ee4ac24d0a84a318aacbf6c0ed75c5af48f2d2bde6cb2fddcdbb8ebf532df0b0e64f86bda6d10a4953195c40ce105a187a34e62b97158a18e599defa7cbc66e90d08427d09fa56c0842b1494d4436cefebc995f7656a18ec5790373d3831e2eb8da740ba77828eb91fba3f16ea3cbc635eede1d21eb58f4949bee8dde81f1c7874c0fb70ec2bb2c02b6a0fdd7119958eb3f078e6d7c2a7abfb59dcab7f343d0ab024b1700021b1b05a160e98ebd2483321cce96135a36ba90ad5ed2f57170753fad0d1b8ac686c902f3728378e564de32cc9e0906ac37f27c905b6526373e79976440931b507d450762aefb7142f7f491a83e55ded4c161fb491364a69605dbb50a453a913ddbde6b4e08ce1783bf539c15a7db57032d96ee7b1964213b5ed98862fd8734aae01dcb3f02cd7a8a1d5fc90a4b3675845cfbb4e83569b01969433bd7756f8544c801d0a165e13f59e2bbdd9ff4531bac2b9924813b0808d2dd8a43895e058cb857f2927cc8766f8573889c62c1809d3559c3a39efb1abe928e64275d0202e186179c6e755f4a4248d75cc0913d6ee8ec2437099b4d59b90b569c93ac97cedd7f2196560b11ffd9014c690862766393e7e06834117d50057153f5b60839cee5198e6d2862a8ee55853a0b14dbc3ef2ad867b604923fff356beb53544eeb176ec5eff03dc4b06e2b8c436a99b5cd3359400bd642a17e0a7f7edc1239da43e40ff04a465ebaea561ee26e0a53a38efceb306afd86570df1b9f1df2cf835efeb3031ec054f42ccd76e0c800b1b04f880ea6b884a3303753983e0fb697a1a37f7c6a180fdad03412a251625cf1acf370c6ffe279c23d5a5ebdef910b88f792665563c83cb5bbfbaa52fbc8b1204cdacf8204640bcb45e308bbd4e84db0b3fec531c600b24e8276a21ad82676299f4de1a3fc7b0fdcaa9f9b6c966b6dc3b29e3b4dbc94d22d004a74ed3870711f486f28e83523fb8700c", "5251636563006a6a63", 0, -821722306, 1537743641, "81a1b9735cc730c6137fe3eaac8ac750598acdb0757e84153ff00cd4d0346c53"], + ["3b9b45780207f7bfa2854617e065673b807584ccbe95fd2a414d120ebee363c74488eb1833000000000353536afffffffff037f8caee30747e5145eb706966f2f59801af0c4933a8ef49962f0c569cb48d010000000363ac00ffffffff01099014000000000000f0b9e2be02944c550300000000000000000000000039c3b68b9032f186aed9353e5d879703838f573d99bfa75dfbb89015980b091fc57518f0b310c2c4146cf206ce1caa7a7aa01c50392e17c887eedf249f7c2d58756c21e0e5dff7ccb110fc0c1688d964876d73ccb65c337ba9e426df9f417f4400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c09bfdf39f71a31e5f1bd1d38b21e0d1b68223216726f6d0bf0986dd5fcb5c76e7edc63c087a6a08e3844aaef764100f971fb0037c15d4fb40ca1f05eab441fdc05610466638d6e0376fb335c6617545dc7a52c2d1918463662533434c43484d4a9a0538a085e0969e2f1181db09204e63b76e3d74915f0bb2049d38201112250320e127577d9e2c50b3e2cd3f6c71b870fe0bc51999f968358c444d8dfc706a01031f1ba499a2ab260307d96dbb1ca6178e7dfddeda14e2b2f78bf79115e02d5b800b06902feb936fe0b31f1fdae8170fd8c415ae8bd70fb396972debea03f3d91c973bc5f76611db3ccfd5a59c14403a7cb46ca8e61fa30467ee0346be1c30437f5f0313ba4c9576c628e79cbc11fe6b0d4776ccd71ef666cd567348d4db971ec4ff1202248e5489b01b89d11ce1f05d22e8f9ba1989837131ac0e2504972e6e68b6bcdd021fd3275f38244d4da4acff4c7a2c616ff66c6c160e11ce917371e1a6c02b67df021fe993d7a0edbed3f0eb63addba8f9c060b4393fe127995a4b2b9a659a423e6e0312ecac703fc94575f9fc696b3b49bc773a40be99fcc96a4f367bc983adb920647ebdf24ac31d6740f2cf186e88313970eba39a5492a06137788f5343c80b32e7c884cd4bed7acb886fcbe27c6132844e2072d2001cc7a9d8d3adc1fb21969997cfd3bdd040c4afe1a28577c30c77c17c6bdb21d1bdd319f0a667b3e95f73e9c996f5c2fa88f867d4cde228ea240a7122eae1e00dc590c929a2660ea0188af0de01a6bc1f0104c415d4bd970a8fd6dbf482860e50b5086036c05fbb95fa78c0e706d5844ee34f174206ccb76fa8b7cf654582b17fcbbdf11ad0e6c4bf6928ff7a95e0f16f46c70b8d7bdaadf2eb9fc4c38528f9db2424815f0afbc5b99ea9d6e934f5204276818a1c8b0321e9b865117a4167c1d73f54cf7857898821573f7e6bdbf59b7b597a434f172a34135021a94a4f371cb69344667d0c021eccad54244ae0820ddbfedba8b1a3dae41e2c9b66629632b82ce4d0be65dc00fd6acb636f6752dd558d1c737983fe5d567430f50519dbcbbaaf0041930122fd7142f2d16e5c97bb07f1db519cc85023b9921d9f317d9aa4a1570b4c02cb9e31a3472c61f60f6a372125c000a6493624cd07c48c21bbb1056a388af5c73b3657c8826997c19184d16fe9dedb87a9c4edca3d80d75202583da5399344ffaeeae53aec52fd4f74470858ea52ddffc66225c31a91df8f0a8b3778276e267dd46758f91514295f22f6414197f847b46f976918610dcaf4e6e88972e2e978790f0730362dbfef401d691e96ffd28e3d97c881aaab58b935a22d9610d3011e76a5b17d43806fe3f507c7d68f71eae32c85af9502ea49eb034680c5e760a0fb5e0264c3fc05f6a3b20b4792f21c26befe00b92e5b23935a808a714e5731afb9483f5fd73f4dfd99b34e2d37928286a4b0af3c33825fa8aa9a0ec64d302111ec360e56d6b37d0c1112c76d85644ec8a8b2877126d9f46581009ff3ed6735a1128adbd4d9a4becc3748f8a6e7dec600d8c9631d1f1850132d302942aaeb9db3181a9bd3defb9cdf3ed2bf70b0fa3e40a3f6ee957ef0097d6a593c9f87496d26eaacb7ddcf46c976dc289c8f5acc4e56728e62d312b7182e5a919ebb050736133a2f951b237d909e6d7f5515062d5dc469949e6eed7a15821188146e8a31cb388ec2ca27b2cfd69899114bec151d06c8a9014495e19dcf2058a25192df81b202e25f5ab58ca8361fb32b6cf78779b7e3c729f1edf7981c84d2f82ceab32bee6f8aef9cf568d7ec14851f7e64ce57a112afe949b9dcda9230e7025bfd18cfca6810bd9f47aa65af9d56285166d49c8ab4ba92fa3ea87699530bc314657fb265129967c4e1be5746b8baa7f8c5b043875c2ff0853e5d71799cc19497e1bdc925617b690bbdaf492336ef29978bac3b8a622c7cbb56af785b5beb24811f33ddd9fe679d1a2063e0d5c22c08eac34141476c05a9317d557d6af41a2947f72bf8762d2470d9d937e4fc4cdf351af318bfa36415a1ae56c2d859f1f3df53a08ea53be86890c4dd0fa3d037734aef6346e14e21d549be31415d0f4f6d983de78897510b6c5466a28f14baf0cbc2ebca0348f29d7770e3835409fffcaddff6430cd50f1b683df506695c15b1b17166c07980c9796cc2f8048f26769222267a478c023e234d1502b0151f2dbbf11315eaf6550853b8472373ebe602fbd9625007a8056eda6788560ec9747e6eb97b2303c316189fe33ce18f7ca1028684a515875c00000000000000008b74a604000000008dee903af45a9da8f852526ffadd94e2fe072a23caac6277b693ecb05031d87a64ab2ec1764289e8122bbd5123da5d4b1f44b8d84906785bcb948c492a22cbc34c2da2338f67484bdeadb62c7344e7c94eaabd490581e085e46d3ccc7f4a252800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c55eb44dfe81c4d36811116f00caf77ab4f94760d918b967d81b3ec2be09c7be8fa18b67a623c3361eb088760682a917ea585f4e6f2a428724c591b8aa08827ddea321f8139e6b1c6fb8be4f92586defccd1a705139430bdfb5ef864b41ca96ece4de14831a6e1c9bb0231afdb4019e6a3f888644c404a2612616e29b9fca3bb031ec804bfd2f7519f476638364eeb95e2bb1ea4b96d9a111c705a923baa76c023020546f2d7c2dfe308eae719677523a0004eb9a85c9c60c529330c23ddf72108160b07c2fe3d575cd153431197591f30acdbf69b9b59857e01cb8c620421235d311d9b2fe968a3c0e714227199b80f0ba72ce8402c95e178d06c826b2bdff7025cae0216916c9d8e7e48d3667f732975db9a1685fa644a8311a354bff3ff0e291b26460224b1f07d93195250cf796c1d364c19aa4b01d26cb563b23d22bd7d02cf60bac7020add8d8a7edfc73b4798177809a83976dd8bc64433ba4ab84fb2bf9a93c3a2e6031fea4f407c4e8dcf160ba67e87a01cb9ce9c450376598e488cdbf7d53e3a59580305a86153b1aea95e68bbbe88905884bc28cc9e7445bb177198249728ddd35e68584cb69b719cec46b1747e89932ddfe0cbc584144e9e218a00d00f910244b89be806e0fd17d648202da8e4b3ed13e607cda2124ba978f7172cf6536f42eeaaf18750c5f18155b9195661d5d938fa4a399a0161e25426a42a33407c8abf3b5224c9cee46a81758892b15ec066626851bed4919a860b0e1476bc64468c101aa5a91bba96ddfde01395e65c09251124e7ec1b02b1c679ec64a9a0539a8fcad5b4401b6960c38fbb539eab96e819cebccf3a2e9ed0788cb265876a4fbf54d87e5b9b05ff0e47863e7e389c86be4904459bff915df428428f78bc1d1809a481a9f77ba913fab8e5254ed493126b66cc890dc4d93a06d32f85769c02e8e1fc5359320aa600a6454e1ebfb2088c6348d550f7396ab3bd091856b8152b00f8105e3c03b1d1ddf50b9682307e0840b49178d04417321b1f385d53cce4ee582ead2cb4233f9f14a3e90c8bac65046cd5382798715b6c8b08583102a07cc4a4fab529fb616594657f01636aaf2154d9e56d8dab7360b320325ef43ce3a1ff784edb4fac0c704b5acb7166985b9ce689e88d39a92c823263cc12ad7b80de5f0b22f68fb64fed4a925c2a8b936de9973e2f3d51b50cc2ae399d601cda6fd72a8851442bcf404f046ddda73beafb387b9fbd4a3badf2d39f009c7dc629880435ffa39c98738d9388b0aadb52b48a5f9dcb9d6f661df52fcec02c1c57bfb83358eeec684d2a5520e755c4f8a094f34c97f2201a70d22c9723bb21e088c4a75de0d8e24899dbd668de69931d2cddf875201e4d4e1b455736723fd1f83ff47dae90c8d13a5c795a3aa139c7de48c221730da9ed71aa5e57eec0570edd8315db9e607e9be30670799ef3f374ec2f2224b3029ab25f3fdbf0090359077ed3edcc6ac16d079d483edd5b502df88c1da932ee8c72fae7bb033486fe9bd0f34c5597e1a2cc9870d93721b9cae9b268066b16e442ba39a1f9d6e1bb2e499b85a487e037738d2d654d3b2810e643eb895aebc8c4d31c30ee1330eb50a35c850c97938517061dabda88594feb7fb17ffcdf323d692f016be0fe5e41c66dd7811163c8d84830e066ce76f349774d581d65fae89f6958fa59b5a1d97b61ac00a491726ab5611de937e258c7ce412e65fe9267304ff89981d7a6b0186b692f5001e8906849bd616aa4f7a5c1f104aae33eeca2cc976d3456caa14cd91c2dc110d777a0d846b8e8e7579e2bd5d58a5ab3bb3b41f46d83a5dd917dbf548c1ce24e3ca5ee09748ed0c6502a228c1a38bf033e743c8aa95de09b99df18d2e2dbf053ac27219b25d0f405c4be22470fdfad6eeba748ec98b0cda5d30e04a123d4f20ee0075c5c0ce4b3e88c2675a4f219844dc7e63090897ebb6075dab19229aecfc132755b3e5cfd1eb8d0522891186e81db9d721e453caf5b868a138672d9c4f95e6593a9b418dc61ac485577dd918efe2f4fafbf83f37884e2e20173aee8c79c17648f5660002a2bfb0279664999c2cfcca073da2565de88010369795c1a6aa9f72343d233fd2b7e9a6561fd823f70e94120862e8eac7c00a3e3b82bd0894e98c5bfd5a0ea78e11b7faeb1c730f4847a60640b828ead1796c670254a7883024dcd97751b55a5e2e5408d3d111bf4e0a2a7ae3927c7c7f0b7f977a26b96b65edd66a16912ee1716f9abe643ac5abc22e2e127a0f96073fc01ec83ddae8096b02fd152279cdc49a5c23a6beeb0145b91c7273d5467b3d0231811289376a7319a8da23fd2cf1d2fae16f91aa48981f6d911caabb946647d57a782e4263985412490626bedb5b5f7e31e9d07b4fe0c6326e37bef2dac0962fbab573b05645242557609", "00536a63526aac51", 0, 570397389, 0, "eb2b38e156f1b9e9fcd84551cdb9957b0b8d4b9350d114eaf3dd1714adc37d38"], + ["33aec4100292ccb42576e997072e5315ed7dfbbb6bae5377901b3b92f02e8bc82ed3ea8c7702000000086352ac63ac530000ffffffffd4b2b7310518e1316b8afbea94564838ec4e501de880ad2788622230816f24fc000000000800526a6a6a63536359157dcd02b4e8a9000000000000595cbe050000000008656a53525265635223b0e5b5020000000000000000fe047c03000000003a65d07dbbeb9a6fe3b7dcb3ded7d2ca86652d986958671c5ba67b83873066922bd9bf5015650008a3a29ba4268b234eb961cece388809566f283f7428c6e00c74245af2021923cc3625cfee2030aac22a8bb6c1403516079b1472f0b27ecc37000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004f488b136fee0935335af50509f1d181ac7bfccfd6d8e063745e099bf0c7daa19e692067fed921aedf9c4afe8a5fc5bbbcff48a429d8e0821eaf2927e212f957cde11f5b468d121d5a44522c6757deb02321023154965708f33bebe7a37a8f45b11f052f51def0f6b80769e6d9d670274b4a71056283061d79f5ac4c23393b0703302f7090b48380a29751b994bdf7ebff8cd1d76852f49c412a91b99e06f2a3d3030d7caa34784a767e0d25d98844bec72b06a4b3b8904b42f5c71bc254163ae2ed0b04439f82871e9a0636f9af098fc998a3f3c34ffbdf581e506a2b7b8d8ed053c860d69571e4d98798d8be5e02f972551f5e327c809a0257b5277a70a1a6265d7e02094de109ee14310b76024bd02000193ec518cbd7401522b5968b147c3ce87d630217d040d6e68998acf1e6b2fd10b144f7b8d72da3fb542905a9ec9721b8e6be8f031991c24db2f0567649d796f34795a48c640d2e66c3a94684cbea060762090b820309022bb5f2c3909480e0d1d8ceca1ac6c8c1292107b1819df0049fab1dacf33f030ceefcab9ff14c0c49681f7a8d192351b3bccff518f2597780c177b87523df0a286434f083729779a3d1b4e6de7befab932b9cbc7c4790055bcd765688ed6c2494ae033aad4f44a0172aac4a3558abe9c7f05941e3d75be6c2bd6502fb23740355d69b88fc9dd51ff5b518a485f1e71696f6d3cee67203b6aad78d8c9c1a659afe353756ea91bae792ef7f8ba4025c4bd8fa548a19c69bcb2e801479eb51618b7312d8601be4e9dc93a64bc65188bb180e88137f145ad3012fe7eef553e405e859d10c9d465940d80f8fd1b8a58668e3872fdf1ed0651f128022b3278c2e17320135d528c23d7fbbd5ca5fd3bf9c306cff7572b875149a9197b4cfd85e4c9aa72b76fc9e9fd20cd0babe501509a75508eccff42adc6e49a7b8d70e8649985169b7a1b1fa8a8b5db1da8097a54586e36840c0731be4e9bd5f62cfdbcccd9fc4461a0b4ee88789e510d6ced8d8e2971925c2d2e0791600432e9dcc25c1e06474762fc96a8d0d34e0faa56cfde9a5bf5d04cf2a2269480b2da32ce1321afb368f478d84803742a3dd61d727f3fb1a53f07a2bffe071bcb2b6a693d0a31c19ca59c012b814357d0c873fe42c9816e49e38ace78ea9f0b96d3ae22032eed8568366bb3ae427aa06f77f1d09670ffe48b55fbbd186954f260ef4f27262e13527cfe20b9094e772f09ba083d0d00124ad57b910cb0541b7ba5164a739d97e8cf630e4da2337aad53b2a74d886aada1ee9491255e2f919665323e5abcf371031d68993e81248352c2dee1b9a9d1d83db652b84b8624e255c8ded6d2a813c6b77624a0a60d920a0b9c9192203230838be3fb35d999d16bf065de3ebfab013c9ba5e33d1e9fdbf184845ed942f445ae75c7c2fc84eadb8880f88dac1e794d97116c41870b81388e7951fdcbf501c983395e58c7173e83f33b7124453c693814664159ac398c1d313656939c9492fc80bc445ec0d76af3240bbc1e1e89362c71e418985596ae347b4b7bef683d1fd59b2fef5cab7692ad34468845422378b5f0a9c676673452010fdea17a84080fb4ecb89ed9e3e3436cd114811c7f65fd79ab2cbe5fc90dfca3c1adfcf421c33a2724a3721fda38b5965dd0736fadc95817d3df46e10cbb11078fedc3043ffb52564477c5ce695b19bc723a5881e01f48e3efb0acd0a2c2230cd94f4e28cdcf216c6dcb9ea8a3a852c0b729372079b28d88889e568e699661654cc04d10f92e965a38a7f39d0f9d753dcec374d262d1f20eb549470ed40b2ee52bdae1cd839763ad0223f4e8c9dd0ce62f431def9354e6f4ada310573bfd3ea79906d9803f6deac10897d733d870e0804b4291d7d9d0d1a187e6a1e007f97fa902894d2c37d6a654c349e4a4030ccb355469a4561d3fd3132b8bd9526940b7dde7fa7e6690f352252451eb34bb7ba5ef1246ed0197fd78c6e82825a243764c1fc5eb7c6a690383ad770891d2c0e8c2748b7308d3d3621230ec8e244348457f6050774e6c5ea505b538171dcb34ef28bedbdceffc9b1c88109ebd4448c82506422f8539bd92fb2e5b40186b22cfcb4b08a63636154ec064bbc6d9f1dabe8c53750561f3c8962aa9ec3492eef2d811a28db47a72b2029a105c0161f72c993b656b282bafd5563c14e0657e9b3d61332578ca033f41826f0afe09f0f2b7fbacaa61143f70165a7d16d7f3fcf020a1b5680684666a7fb589564d33b3797fd954ac1d48d31d8ea1dc1d4caeb5ea9c31fd3d0bf0000000000000000c81a500500000000c7714450a7ebc02035c82030c1ced7379e67a862c6f89c773a833a5740544e6f0b93d200efe841b765d3ccc4b1ab775ec85f6c0c241492a474c6fdec9409b4627c0cb096dac37577eae82a3486e6ec289c45b82728d5509613ea382e31e4270300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c73d288d6c9e8bf8175254c0eef84fb0eb16d523b0683f5dcd8779e4b46db2e133a8dc7f08bf24cd46d6d608c35625ba0c595fb2cadaf6ff947125ccf695fd7ee0c12b6072c563883c1fc135f588757f9e38870c6c4feabebefd42c55143a15726da9f10fb9f29ba5f6e401df2ebabcdc268bf10d65257c43d21d6aa3ed824f80226c0c322c0bd5d627b243a7350f4738eea59c551dbbd296102319b1151888275022bbd9327cd09f64af33b0ae5cbd2488ef5591ed2316add4f53a7027d76a76d410a0878b8cfa27527677b23714ab47b697fc12e13cfcde0131d391dd4e94d7e145fe2d7c4b7c95f667df387f6417aaad24a4859b8643d29f6c2247a3454876b2859020ef043147760eabcf31515983fbbc781d0222be30ce3ba2474f37166dcbc1ebe0219a17c65060176fbd5024208c88f163a1394a2442384474d54b7bff862676b62021c661e72179a71ae8f530e599ed891fd122013f72d31b2f32a0095e7201257f80329a0bd1e107c23c9a1fd6f90dc6674bc0c6e71ce33eb3bc497747cdf5063f3a002217c3af1fb43a282eb60b116b1831eafaa7983567997ba69c79c7c467e8e6bf46a62f8a00b691b281a52ae61069297ab523ca91ece84b4b3658d93789960e42cbf803b8498d40fd178ad772172a2fef01774e4b16025a796d092d00344631e80cfe35737d1ddd7a85e20164629d50754a02fac00247562aa1038bc42cd5260ccaf3a4292cd8f9b27e06e621b25bf7fb776ad271950ff2db2fcf3e2e849ffb4ef27d15b90c4fee537aa195cecd9ae97fc6e169c44ec149cfbf8e30011236d7319b4b2d2ccadcba82e263b6f1b4c9da86e6a7febda94dee49a1e8b90c6a3eb423c2d6ac4a051bdfc1ddcbae288818f3315c176e12858db7a5f466eec4ccccb9a69b996e8638dcbafd203c99477fb3e29971b937659ad51e23c6e58aed9808bee491cc87ebaad3d20a46b7080ae4ad21a2c9d646fc6cd7f944228c35bee47fa1c4b3d20c89ac12b1d3f2be4124ef64fb2511d8da397175613660e226ff850f36ac17fef1d26832ac55e75f993db65f287e71c06ec8db380232a090fe3e3e8883eee3df6d76535c9ee9825242a3fd659c159ef2e8c253a953ffb4f7582f9b52346038eb49198a762deb519a6b441f446cfbc37a8527e1d187b1139589252a8e321bba056f65ebf98c62f7b08893718d50045d00a099920ce774279a9daa3e96246eb2c0764d4a76c69858554a7e97d66b7af5a19c9a8c6df8a7e8530c4db1d5d41c089d051728ccdaf4518d369cf3e74891428f31a5239be8c74b2cb6c2f5dd918ef3e5775129e215bb2dcf6b8b4258c76a3da278b0f32099b749fa60c53526a644037e9e8fed4c5967c37e5c1b3e0358b148ae141536534b908c0cf7267145132537e8ca8e1905a16f762a8176978009ce020171c1b1d531ecc7c8662fa2791d75fe20f9a7774411a41fe38086361f3f42dd2b40ca8701f10055c09238f7a3f66fb8bbed16bef8ebbe1f96a4cd0518f8368fcaed8f379a5407e0b36f9cdb4ae8704bacb39db41b875ea5147005257d89f79d489e9149a7cea1d5fd9e8cccc236ae0a2790ffb58b8dad5b6873767ff423abfaad8e445c8768ecc5a54b1d8896c6d45b99733fd347835621a2e56dcc33bfd42b5b042cc3c05e713fcf77c8d0f2cc6bdb7622def9ef325afe3d8ae07e256cc024ee78515561324561d030efa8a027d460cc0e312e4b2c58db2fc359a0c76dbef8de19b13502aa0be8adc9f3034c97edeb929d985dd81ee1c5ff988a0f5fc4b3f8260dc7366fd8195dcbd08e6fa05a164a4b8716aec111af315dad5e949ea7330390be2ec1c5aa06703aece2f424b1149495fbc33f000d7d5e925f5fb96b45e07161f0d4c8847fa04894bf5c27ac6c3cc1e1e9b2da8d2e1356e721990f4b075948188327de44a88d9ff77faf1b7edad980fce24caa4c17528b817020e969a3f3d299671dacc96d266a8e65e3fd248f003c36cc3954a8fcdb3051176d8f05a6d754eecaea573f623ce264cfb9e468c7ecc3900cf2accc79729d2b282fff4a56e2811b65f1a70c11578d07d007fa9e36f8518c0b45ad4f3ba18933a7c89000fc42286de8d89f391377ec1561dde5a209e9a04997ed67a7d6b9aa8c7ea05fcc7ffa2f9b59b3d6305ad62d08ab5d5f187952dd848289eaeb1fc46434dc58628a6208e3a6614c546524253e909534adc0e2dc25f52c14b58df8fde5e8bf27b97546e619cd87b0cb9ac2ef585dd6132e87e08d98e35d6362617723aadffd80960af256587e1b1f034653cf1b213f1c14c249beb0e63085c9f13c5a43659ca40995b9369fd429f9d94af054c51d1899cacabb56ac7b40d1b6450f5439bca5524c2f0897790264634634d3d3ce0cf56587e6d70ed5277bca28b60fcd1a900a7051adc866a480c", "5300ac52ac63ac6a00", 1, -372912135, 0, "1e35ddf28f6ecc28496caa60711a52655814c31dd9826a173df24b6150273600"], + ["0d61b16b04ccabf3048e1cc5559723a04b0e6f120ba55153734eb8e63f465956923960501b0200000006ac0063516a65ee5851cf79b10263431ed9b36a6aaab987ccce5f50c0e65b6404def5e1980ae42407042e030000000500515200acab010423d7459f5051f4591fbbe5b3f1b4ffe805c4cb6c4fb77866d9c1037d9ccf36abd40200000003635100ffffffff39d8e1ba918c37dde4139bb40518b65b99cf390210ced3f7038849e5889ecb2303000000016affffffff020bc5b1030000000007536365515251514268af0200000000055300656563e27bcbc50200000000000000006a8e2c020000000064b33f8726e5e067d134b1e49332b6cffb65196636bd90ab4c899aa5e16d24bc1023bf1a490e445a64fa434407f9f6ff9cdcbfc17ba8dbf0a7916b8b4ca5825e8990dea243d181486ad071b04e4ab531117b0d8c711dfd0cf76390f56067206a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075899fdf44be99d843c26557846b58c04bd9a3698a05e537d9ee6c5df00bb2594e82be365ecb5934169751374a2a3810589cddfe73634e5d3175671a539fc8d0f57286f32060c974be7f69669e745d0effadc365436776cf7282cc694c40a7d7dd506bd61e90180651be455ec40ddaf8b248c0102b751f6b5c59a67a2801bced0302615853b77ad1fd79ae294b0a471751c0b911a8ff4465cad44b6dd258ab55930214a4080a73ac8150d56dc994aa46b635d917c88e86c5167e3c91d1ce4bf947f40b001d8b7923143e78138e460fe484e84dc9b94adeb87789d7ab9d5a19f0db94ca2cb7fb1ca56b08bdcb03dcd7798494d18935876464ec9dda4184f635ff53156e030639d183874cb57f8206c0058f8a13c311ba31007c013351f95a4682dc171125032abc72528935c4b26310be702e872a336df8ad4ed052c42229e6956b48c1a935021532e3868d147631f80f617ee251235eb3a1b8fa53289faa2a4e7c860d8d49000208e836aec7a21e7f5f2e9c861d4434007637eaccd9de54efddd97c75132dbee70323649aea9ed0e3736a490e568d663897605f031fce738648d45e78a6fdbea4f799e8138eda3d133db47cef73371c8c10e08bb7b7d2fe094331fb83661b26e2054d32abfe9a99e7f8276ad508ea9df76bf71d59bdd0457d89274fd545081a201721556ea024bd834384640e65ab995909139fda9e7e36059d489b5cfb7d11732e304dfe54d92444c96935790642cb1aba94774a905242a13637f341176823d73a39ef64e4e1bb22891a4cca24b2260e57b7e9c06abc0ab5393132c15dea5fa31369a757fc999740029df36ce5a9713a215adf1e1bc008654d1d1682dbbd680afa52696cea625ef796351b3c313bd3cb47438ed17e5266ae77f43f89535108334e4abdec622c078a1d363f6b59cc201d0b8dbd12b06164f516140a19bb78f633e21ec87413fb3016252e006c65d26224ca4459b167b9e57b210a496491da35f1e57aa99acb0e1bbd23c4d6bfd6037bdad461a0861288ca1c81962cc1efd35b065c288b5ccf741148756f65bd2876a3dde6c1a3faa8988d15d15942bf0d54f415d4d596b683c2c000c172495accbfb98b8f5ef6787f33673a528a60d61165af1d9b691d25709491a9ef97e00f9fc1239b3f47ae62a5be58a04b30c307dbfdd5f83cb0550aa56e4ff83bbb6b13809f29e53049304ac46582ac12ca2ce81e2bd2e5a9e5ba9886e58906d0c2a45799be811bdbc8820ad943cdfa01b041564d738a6bc3a3b5dda9126ef1ed6539d59a423414efe992876af5c324429223b9f35d6a0eadf79c0823c05910f6af8f01dba532d5035fc25e0fe24da09184118639ba95869eaf50332fa61ca8f827126b319790bd12b1e213d4bbfd9f6d5e3b02c00f3841abaef57a82ee5dec5b06a7068aca5942f5a2523a92301188c6a90d789cec7836604143a05329c975a4b706638733dfea6d18c73569b61bc5dc016dae3a5d2d53c5eb1f570f7fc05d7c4c4cc1e8928ba7e4cce0a500beaca957079f2e4e3636648dcb4ae3cc6bf1877c1dbc6c8b0df77f2c817844f5d63422d7e2b7bd5c2822d65f844ab32bb3fe379129f4afe4f952979828c38099cc3848d866e5bb5c877d111dceedf37a29f81f07e4a74459268b9856e72b59c42c0b61d1c2906db47bd1ecf74a17559ac9123634cb69d667269e948b36c9306ae2d27c9eb66b82e5ee37b202f5046bdeb20a6372c5568da0e74c65c541e9b54719d3bbc652ffaaa8c90f849f8f82d5bfcd21808db10c0b18f5698126469007057dcd50c15bb3af02589c136639becabb66d86c37e2e6d9e61866b964b5cd42da8f725e0d3880625542814eca6f356053b2f7c99ffb1826a931b9ce5539d1b9ffd86557664cd7d78aefa6e926ddc3aaa36d95f140a61b7f4c7e51324866be8f1541c84ac8c35983295c7004b6f4911505ffe29665abcda52f02955c7b5e6a45c320fe2acf9830c9beb6e9379ecb311bacb0282541fae11ad1800748c5cbca013522e137cc24f2d11b0d444019fa8f291d00ba2df6c3c26f03b88634c5e2763d59128f509ebbd4ed340adc54c5752e7c7b0398dd8cde4e98278be9fd08adf0f1123f6f2e952621638339d4887ca3cb7797d7cfce97b5269c86e51e89ad04d430ac951201e3680dcc7dc5d10baa139f6e255b2f71c8538bc6fcf7a8a5534ff4feedaa1422b6a41a02001a7a24febc7cbe3785de3141004a2074a7543658ec098c7df1fabc327d75405f902019e213271df1bc2819f1d6f886601711a7ba97970000000000000000a481880300000000b070c5de8aaa3477ae58fe13539bf5c624c3234ce6daca9323eed8e159e53fa4c8dc2a9e01b8e5545a9a538cefafd6c7f1f961fc7b6a1c687833896b58d14eba821b3662e7e647e6ea66598593ca8be01104e085df6225f429466e2b48ec8b11000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009b2b988b7a77757d0d6ba1103765cda811fd6d7cb9b1e7bbe2947cf90efd3f2bb464674a7d2a579313b918cba35ee2d8dbadf6e6c487798801f3c359d0b02959bc37134c6f8d8da07d1e003e18fdfbd83a0489bc46bbf1ddcf335683afe5f2a41d5fd203aeface4da1c11f010676b524809768ed8693e57e13ba9a9042f171ef0300c8705d955be30f965b24f5850dcf51e8eb1b819ded6f6d48bb5d6f233f3faa0325d8b3e80026649ad844e7ecc26e5859b7c997ef897e47a852b1be6a0404efbf0a02ca1c7fff79c7840588426e28c10baa5bdbc9b0d2ce2327544f5f56aa557c5c7172f3c0dd7a09ad14b5a1dcea96cd4d1eaa74679cf4db98eba384dc43e2fd9d031ea4d3dd46aaba2dbbfe83091cf32eae4daee2e00cf01693dd191e78b59d8983022fe4c5cdafd7e43398342b7b2ff1656521361e762b0aca918008ebf4c6b3f2240208419b23b6462cfe52f60feea72cfe92cc439f3674fc43adf26925d593213c3e021f604acec93ae1412ea22be170909c5dd714340e3e66f724a733201016788eab03124d025a6ed56e97e159b17af8ae2c9ca7224b165f6c1e3e9efeda9e3fdf828bd4ac410c28db253f464f4dcb2ae0d1ca06106cdf0beadb6fc708f9da6e21d31c117efdfb18fd15e5ed9101cdafcdf59d0f7c5e5bf682e9d6e13371e5b8d3fb7aa4b060f5e5e7b5cb801042a76c3fa90fdb870456435d7f6ef49230568280503b9edcd16f90a59dbe9888d9cf7b47d69b54486fa3d2783dce40d3e16a953eae01e0a25561cd1cab38185256f4154e6b39830232b7054fee75fae23f523b7304e70d4c5449111738778f1e5c676ec0b0559dc07862aca96d30efdb7bfa696df1527ce5300776e942d5dcb086ff1bdfdcaec09a0ee2cc628e58fb0f92fb4c5b51a8ac84d95023400c1deb1c6319eae10c70e28d48197b8ef17b6d1a2a58cbfb09543cda3f19e91888fdd477ba5664cb138259777226cd435ee3c9cf34b8cc0be79636237e38de03185dc1290550719686f0329b51cdc8694e61eeb531a55c9cafc27012b0a26e08249d45c4108f962f8dbdbb39f2815791850d548a63858daee825dc7e7ad7af735c89607566362983ec9728e26a6554f7e1e3e12f784153359ddeb787cecb43bdd89f844802c39c668c35a69a4a8e702e0c4a30bbdc4cec89b0cb1817b0a2f6baea5805148f8ce9dc1a81eeacd1db6f4312dfbdb4de63e0cf6ae947699f925ad1f9de4271db420c85fcea6c91f3a4619176327b0e551de96a061ced409dbb7e75384fecf336607af3624f2b7744bd83503106d10ca48816142907515137c82f0c6ecfde6fd1e2f2cdb45d18c7249cc6bd79a8ade21f21f49ecc3ee8afba95e26dbc215e56f5da6b34eccfc9a6ec890e35b50ccaaa2cad3f4ebe270a5bb3eeb658e9643987b5230489ea33e7fe41ec93619433140ad17cbab4e532a463fa08da1f48f3ea3be7741a6ab5bed3260232de3ce3fc33a7d61f1d64376964be1e79be7b286e1392c5854aa66568b883727d9041a288a533b78b36bf536b90964237294de16b2d9796132343e98b7d518ffaae981cf99d7c6e2d32f58e26806497572f4a805597c81a47466878dd4fbf04679dd76fb4108cef4e13ea0c38d5b54c4ef2e1be74b69f753ce4c5f6261b3b60312e75e053f133ac58ca0788a28147005634a08f00f59ad9257f393f9d2a342b56bc4d0575de17b33ef524e351e9297f9baa8ee930f71db03903a7450b81641ad4a3d057d7d3213abc57b3f0dae961b5a534b3dff9d61ee51e09e12c2c78cd122e8a956cd67be56fd3c4ad13745457bc4e016639741c6124deb620ca9dccb47d2d1f69a8dd009cda11d594b652a3581c6ab0a6a96d7b1651668529f886958906a74bdcd8b558a945a3107ed003403beb7f1c5ec5dc4d1505469c9bbfd1ca4bfd857b12bf66f989d4ced6f9905133a4e5870e44024b3311256cb75c2092a8804624b8122774424a57aea2d65a16448c059c58b5425cf311a22697abc90be4f1fc7228d1abbe03e7ba40f2aa8e5e58d847bf1b4e24a67fecd567cc91348f3bf55b12f432459a168b9b9ade35ad02f31ff1a299880304fbfc5123ef97667da1a3e24c5dc413ab0e609ffe2486ad34df1a320c79465eea2f3f30a9c603d562fc9d1804afa2bf0fe1afbd379b6aa004353b683bf68bf849aeeef7d92e849a0dec06ea76c591c7710042781342508492c8ce956e9226dda26185e122d274d50e01fdd10125037b239d5d96516da5099582239c83757b710c634d6b539e9b5ec4610202e40daaf716535f4dc1d080e69e247fc3024cd4cfa7e66cf33028d974fac34074cf89585de6b1fd05d4c5771a604b31adf55129bc850c481bab9c6bf145d7cf876db2714f8cfab9b9d79d5283dc6d15a69b48ffc50a124f6ad9cf0ef0996d0e", "0052", 3, -502082915, 1537743641, "8a126f0ad39fb1139a6684e69ee303553a4af50737c25a2bb9c7722527d7a7ac"], + ["030000807082c40304aab3704d9a7997f6bdf6a7b21496d4e5b3fc0b6eafff4eb6072f66a77940f6c903000000056a516a6365ffffffff54d8ec39eea9550c300003c702a2ee031ef7e26efe0013f5f22c1b95d945b8f70100000005ac535365acffffffff1d9c9b0d9b23d2cf03dc4da5256efbf52d26158c403fe4b8854e0b3b5d566cab030000000451516551ffffffff4d1e20ef4af679f6ec5d3bd8fea0dd560045eb92ec2e4e25e22bc31adfb1adec0200000000116657ad03d2dd2e03000000000100f12a0e000000000001516cf24d0100000000080051520000515363b55be44ef87bb4a30300000000000000001432540300000000e2dbc278c8214c057b0307d05c0d253944ff36f906c4c6f4632df629d6097a8d80fbd372b92346da4b8940fa98bbb2ee8dbd011466be6b615752bcd70d2c60e7d27e6a35eaeb5f19f38d607399aa85ba979d61249e41b5522c59edb6da2bbd5a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000055eddbfc937ca881faea53247024ad23e84d4b9f012bbca4fbbbad35dc6f58b18a911e050ae5f4d35809ac65ae899090dd1bdfc66f0008d0897187a607fd1a807bec5afdfaf4b5d5642e0a210ea7e982b897fbf0a5257b41c3f82d4b29bf0022d934c79f8205a087515fae1932a56f4ed15c5cf0a29d214a5c2c3b05c75c1d02031062f1b8b8053b59632b7409008d439f8fdd174feba54be92d6490e5738ee5e00212b4e19fc5e675dbb2949cedda9931953bf044db0218c649777861657832f0ba0b06965083227890131220f04d871f74d4290e3c5a5b2c4476b5ac7be5f6a5880cc5f9300f19f50ceb56d15c4130ca1ffc74946d94379f421463ced3bada60e439032de48283982c1e74bd2bb939763046ac2013f9229ffbc4b6f911c0d69f7d7eb6022ea9d9ff6262e9d514d542f5ae55e937efa698ec268a7746e9acb05ff3c46470022a6464833162bc2dc0841f49df904835c3ad772b8ab61ecb4d6d8fdd5ae43ef903045e280ddb791105ddfb6dd72526838893aeb37155becf8c3d881dfb27ef66d1022be61ca87db2e47381056371a88b0e5fdf9d25df599fa527d3e69663d277e8b762db9e023778f1f7e33b84d082b6452ae19bb9e3e3668e55ded12a4fd958c36dc7dc7051afbf4c18afca7c1cbfaf0c290b16738124482982d1c08dccfdc71fc174ad3480bbf541a2d5089582206a53795dd0702e83465830d1c269e784b9f6ea5f4aed0eefaa24004af804e4386ddef859b3c48c62a9adb767613f76ba110f9567c8266df13fd0ed28558c61c38e282c37044209ae8de588010cd38b76ad90fd779a99e07966b421d64b02f7b0a3185b168fe51da5d238604e410e801924ab6a1518b54fbade1f8509d0497747e20ac5574e73d21434ff475b97e673f51498f75872701497f68b12e29c82bbbbbbbed41fc79e9834591fc56ed76246f20bd4bb19e9a87ec24c04bef3e00c95c399ed6fc15166f2b9470f533d3a58f365cd0fb4ce88506c935925cbf5c550b107b8d8436690e4c86edf48ac836c015567703361bc155039b14da54c4e0a8286c52f2575f0c1e31051c49395d9db75a20e11f8895d5d4c9e64b6598473314102538d26cd5ce0847c4adfb736d7f841b5a8818bb2d6b0bc2709ecb29505da07e550670d413365f7d5522db9d8855fd099974f3b6e3c7b5811f5f4891a6f4f2fd4be02c591a2fa2d46255ec5ea80deb0e4141a8c352308c2165bc58f3a0c50eb56550788da8e770fef508d6f987d9c0f550c8a1627b26589031690f6b5bccb6fc176845adde14fe5a303dbfab13780d9c8f5ed2eb326c8e1ef9b21095b3a47c831753307c1945a96208642d68aceea2826d7d5503cfec092ff3996837085a8b218ab6853b40a23e5233ad66080e3e79b4d6949029016b5f1e73b2baecf1c463a33c9c18dd3ab55614c0f33b67baed609391582f645aa8e8a3ca8f97804c4a999945fd746a4a844fb3a6086033932c38f2891943af9cccd3da523ca7c4d11c2f79884914683e8b50a96412ec298b05da15befd7d55c4d02b401263c62e62b47c25d692821a6d8511811794e7516bfb9e29124960f1b2e815bb7e21ab69f2d63e1aad6885840c31e0b2619a5cd5526196cb1e2bcf2c4e616a170b965e75ec0922dd42ab93d783a6c3d158306010df997dbf2dd88eca5c95984f7d695eaee63c4109700780b1eafa6c1bff09b40b85ed27d3def95ebb4292d3ab9ff72f4c2870c66a693992087532347ad41ec7a39a481b5c5bbf4204797ea8e0b650ebdd6d69c82ed8408d920ee59a9a99d857b8db85f8efb0dc4ff51144615f157f7df56aa6b7d89317469a96fe40e0ff5ab644730be369a3bd634f90c258c229fe3e352415904bd7bc91235b4232d190f9e808ccdfbfe03b72f0d25f6b6afd901889ca2b49dea03a5a091487e26b7b0d52671fefc05c413eb7437dc2ed548f14aece5d5e6c9a409df78d03142022b40974fda081de896d01df24a0bba323c5067c7f784e3b0b2c281e2daabe8603ac1138cb524eadebd412d19f70dfea251978500a7e37321dd13c12b81c07c34f49aa1ad23f650184eaf80a325a46092da674ea1e2d08d333dac730612f30f072e4fbae8c599c92803ff3d79e6c40677db02a3bde17bc12df9456f80fb3029e11547cc88211bc18e1d9d247dd3bcc4706f9ce04b50a53158edce7ce907e32b73d0e9620af1b55b92e31f2c4f0b6a0e930e7e59c0184922766fc7623ed05920238412db2c33ac128b8b204057331c2dc42531e8916f77a18136c53d5968a4f323c4d850040000000000000000000000005b1e4b1d5e488d14ecb449adf973985ffc9a507a691746809e836fb580d5b12a5dda9e7723baa85fee6c278df74f59015a387b2768407ec4ef063290cf0621e9e09187b5e954568742c43c6e901d792deaea9fecd8c76e370ff6b2781707d96f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007c4b7291c6442877fc14d500a0ae9603dccdd7f700895a072fef1595e183aab11f2e86fe7f0eafce53b46cd8f3526162cf9433dcfd6aac90af0ddef442f0c636b15681e342a5877ebedcc1f6439d6c0e8051fe45773b762d687dcc6fb76eb3a4b494a07a70cf2e8bac1a0147f429aaf7d23efa827eab96063bec0fcb1bd2d4950200bc2c8a3b000cfe9746e75fb13da35953d91a47c177a4ffc3bd6983586973e9023014d9245ff9d78f5e3a528b5149eb033afdcf415bab97b83b2acd753517d1240a06a689131a0b139f2bfed9beef37f419db95930cc980aa594597a9f1fc8db6031cfe33f7eab28eb3b172c2939ce42c053f2a8ab0aed35b3310aa478da3791012022ca9c650c53945bbb4270b917f3c8da94b9034f2b9ad643786d9673ad80a0d2e020f9201e653f0da68fff7c613b9d5e1df3fc18a906d28839689c4e28396f53b7e021b67a21f5786deb1b185d9fd029b1ef49e843e6a6f1c17b5da8be34c56951cdb021e70d03ad88cd68a5acd9b2bae5562aeb54e36369ad37a7372d7825212d947dd021b681caa6d272e5905686cd14393df5178e142ad91a8f4137c7339da751adb83cd1b0c298e1e6e2c7816a4739969c775a55d536756268b0dd0effa9d6d2f84de5e966c78e95f2bb2a6216b002a335594f25033f3ef1446d89c13ca99f4f78ef19b143648b77de4c92d95db62744696106e701d3cec09d40c5afb06cdd19687a11cea5f374fe8eb6ec7e66abdf22262a9b7e1bf4ab2619711182879c63ec0b02bc50903350f059ab822dd29a46b05b9ba0d8b32f0446724764820d9375b5d8fc81bb1352408cc83b604dd02e9d255f85854cb1faf61e631592835332e4003600cd36d48910a09d7121cd1b15812213b031766c4c32b5c631bbc1303dbebaddc08f662607fcdb8ad6095a1672ecaff2cab4fbd38623f4e39cfa128e75f60508bf8282a2f7108d9d5d0db5611ca6cbb8f15288a7b96b75d5f3a0615cb28b5f5a9cf26ebe2c5c1cf4eb585fc6130202642747b8cd13a9d7b62de65bad900388ff6b0ddeae5db0c5291236a8a9a1270462657bbf7f12e714b11064f419671175e46c72a154ea2a9dd9eb95f84bae91d608a7d4afee88e3cbba160cab32087193e3db2ac82260d6b96373754685d287724903242dc41881f4763ca281cd53f3749605451cf72c8dd3079c77e5bd24a90746d1900eb28a5f9e22b2292b561382e54c4af91edfe5911a74a118bf1b7e5abff3e346712253d74660d3433c0dd83db1ccb11265a1aeeafdbe708654d1b412ae493cb3aceff51be5b04a11007e172ae6341246c722f6536d60978f05678a05cff4d2477ffbcd66b482fa14a3d03ac3803f5dbb145e65f39eafb80fd16f4f5d6538bb7b42017668d34f32f0272bde6bf4fc6c7eb7b5eae643aaf9e2b5887f2596847b8781bde2ec1406d4eca6d67d8b059feac6f78fa469c1a8cd57ba4c3688f9b485ab2bad0e548f17e86c1b0277429e4d0e0fbea9bf85c533f4244afe4fee5d4be4c3647f8a389b6a53b589d9f76309000960d98722b70ab6ee6b8b8672d514c3032e017df90a695f475041a9811267fafba83deb20f987943b190b087e14e27d1041e7e7b81cc52e9b9efa16ccef27c5f2ae29c6a8fc3de66d0a6e982a69e1a80ad0ddffc61bc765765037881cccb711eb1975c24b62f9bf10c52b211db1f199bfa82e4b824ee6c19b7e4de7adc4b37bc54d690880d1b2dd164d92cb96507a94c9af3e17cc92984833e0686fe5fca69b7e8a97fa7eeee0e68b1ef5536f6aad8592db00ad0db4c5da614ac1bc50844d4f126e8305743c333ab071deed6886b7a1cb421dffbd8c780c7a1b0c9a2ad2c0125fda3ea5f8dad492707dcc57eea991141a168ed8aae4d9863a9053165b2b01177dc2774509e23dc09662a73d719a2386eac84a11c1f3a36c62155c2546553b9aa4af6b336283c663aefc0e946b7322134293b7a3c531e5ce5ab5a0f1d6c0de85279c03654d6fbb2a916267eb224821d0e7e7939d079380441ad0afbf9856247fb733e7d875d3cee08f11a440313c35cc88e674db6486884bbff56fe77af266b6b43f1a9972fcb094a28f4469f0c96079d7f3249b710d67c0a05e0e42fc0e1f7ec22be17e21272a952f20b862fe498549e69a40095857b3ed015e5c70ee6b91b0ae4e1ef6b35dfa443f30113e6dadce0f0973d6fc126ab5016bfcd8ec4daed8bc29cbd72ce9f1dd49bc2dc8d821dc207a61da3c645aeb99481920e178692b1f25c008815d7ca681d610e9bf76dba54739ba9f14ceb5d270200000000000000000000000060f6a898ec39dfa3885c878a56040d505cec7ca2927cf185480093d06abaa990031d041551d07a02a8c317377bd061c0e2d89c48b4af72b739a43fa4bf0dffb3d341179010735d9a8688fee5b49a88b27ca96bde639261d5e5fc2ee2d6ee18a800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000aa75d344f2c7d761535d966e0f8ee4a7ad8da3edff9567a8e795b371751eb3006226ba0264033b65e670c2034e6238b7a33ca9a3f92a81c278decd25f0ad12b76448237256f9cdb02fda07241d8415ef834972ba8f44f1d5b68bacbe7cf2533f172edff5271aeca300c1d94a86532f0a3dfa71e718daf45cad8b2c54aea9c00c0209f86741c05e10818d7f58a425bef69ce86701a4052a1cb3bb3aaea389a225380216ad3cbd387e5b02c50fae5320cae755f0e74226eeadd7421178a4720146b1cc0b08f9a64bb78c102d6797a59f20a0acd77deecae6d96774f9fa72aeaf53f42c61aab7c7e0794518d60c27d88a2e7f4b91ad6b12af7689823a6d90f9a828c837ff0322b5525a165bba7c7dd753e732e3d2a351fa15af0f0d05e886beae2d331f6c58032509012e715ad1c946e53acbe8112cd113783b72cc65b5e3094f5928fe258278023035b3de3be3c0c04bd6f2f92cba43117fcd1dc67c559bdee8861e335b062fd10204ccd33f1a9dbe4547c43e007d8686e6bb707d5b4b5aa00c8844fcc248243c790312cfb50a138c829cb229c227b1e3e1c7363246dd54040b009744a9790346337ed67f2ed933bd7e6e66477a37a922f2645361378139d82e6bbb9ab306265257ae4df8df1c69f4f0920cd949a950621de52f81d8fa5728df496784ded252da0be072070c357349bdc1bed6d8287209ccc90936f50b86b70198a6b2168c6d176e26074f1dc8efc3630e4437c60352a33e6c55e146b0bee634e67bff453d7860afdaba0c9ff94f8947c330b2a97b26edc0ce58dae1bf342fc980d1075272638e28c73679c1478cc4dd9a7cf852f87d66dc2189261d10fe67af8d972928e16aaed561a6a876945852228ee6d1db381608540968cfe8c4febd09a63d1b7a2eafe5afc7163ac50c37f95cd16c149d6dfe9c9ad7f4b7a907e3e0e293fd13d4f922ca6186450c31c8e22aff93eb9de025ab45a2226d275fccfd57fb98bcac099d03624357db77a8a220bb6580e25ad3c2d99f69b833e262e27d8961582b58b5dce7ade2866a47c26efd512e5373baa14e6af05fc3c1d6b5d32b1629afc923744afb608c188bb4f16d1672c2e050e17df06d2d18d7ccf9641b447823abdfdc6d18af3524b8bcbdc6164331f13027fd0705be8e86bc9e1d22029d52bf47d7983f274b58ba3124b2cd4d5be6cbc2b8e0e3c4594347c62b53e6ba00e4598fb52cce903fadaa4ca57f8fef7cecb7d1f953da3b89d5552e6c09bd60ac209fe897d41feb5f06d1783f7a4d7f2528c891fc7c4686e16518d8a7ecd7dd3482b46650d12aff76f3553608c4feeb88954cb7a3a83eb1496bf00ec25bcdd7149b385153d8de6ac2304fe92871d0d43f513674546e86eb410a7830a0755a0f2d1f4e363a422e73a57401ec216ff1ee9e3443a101ca046134c5efd3bef6972958e6492a19098e38ff040f6425b2ba1399e119944c15b6fa4e6461247004846de65d2ad12c6978a6aa385382022b6594fd7559ba6338d8e4e1053a87178de7e3e636c1932c8d57a8ffeaadd3eaa9151d82c6aa3b71fd789dfb01debd7314a1fc9d65c2d6a78b3a3293f307b9b7d60c9b5cee25dfe695adccebede6d716b3ef90f75dbb9150274a00a2963f5af3d25a0955f8db580e5902d80b39de2b7ba4ad60e07e091aa7e8ed8a0edf44008a27937de5eaeca23145229a015ad7d02ecaa302646e02f67cf0144df8328c752e46236b751892a96833a18cdb8bb871d4358c62e9830673fa3c188d7e5e21f4a7c6178e03094f76ef7904e184ce915234fee70289ebea8105ac43cc54d90f22761ff7df67b36e2b67adcb2635094e1b1507a95cc151c6e9e0af7b61b891d6248288e779bc09011fc1db3a73c373fc53ecbb90adfef90d9eba7f420a6abe7349b930908b3d995f84720f3edab064d5658a956f62950acee38d9bbf91efc883b4f0b77f1760daab69efe4fe56ea476f5e61c1de692172608de5479c26ba550170e3d437960ab0117463064fbc92cecb966908a2b063cef65981153bef57f407bf96b132c94002e28fe29e6bcdd2d15250ea5fbe98a0b0d576b821807e76e22ef605d6b9e0f70ee1709c7d5144b4039aae87afe1ae119abff38ee4693d0d9dc08635e5f1f34ceb6dc4574c7d3aca2e317ca84d341315facb9588fe11e134ce04b33fe48f15d2dbc12bdc3b3e5c76a425d016be2b9974018b726ec65cefd05e7b5e97feae87de5695e0fa8486246907a5acce734df4b026e4bb7edb0324b391d99b571477edf87f2234da90e11c765f432c7ebd9c859dd34ca14810f469602ae04bc5c9731da56e60f7b53825dee5752829832159b0b8c6eafa12e06dfe4807289b0325382fce99ff49ef7d0c5e2de99560afaa91533d4c1732051d73f19cb30a3019e128f707ac1da362cba06f20ce75012501", "", 3, 783418331, 0, "4c319b85f12c9b4076efc11626ff88e18c94075a4c7c0e8227e1fcc8186c3068"], + ["030000807082c403023a32f7e9445c6f8fb4e61c558f96e67b1de69e62b6f079599501b33ba2cc16070000000000be8369afc0e5b21c1b028324fe5447fd0b4db08261111e72b20446c1906ef41f54efdf0e0000000005acacac6a5328fa219a028ec370030000000001ac4c5e6a03000000000051e750a0fa6d9a9e0122a44d05000000000000000000000000bdad9abe14efaaa3a32fd34be42cb626582b1893042e9c400770293728c45ff1a2495298fda9c288aeae9cf5d5ec8f032be0b82fc3242865b70051edf7e4861f284115d15c64ad33b393f7f83b6eba82cc074dd7d7ec68ea560782859c4554e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b778f0c4959aa22877d57e36db223426305ed0e0e524716856cb0318266fc205e83c6f2f91a020f65ff41c372d9166060408ca774faa0e24b74ae836061e780c92290eb7de84112d5df52a9117483d4273f68af9ee23799da243eabe95164aaee0f4ea217601ffc086c948463c9e2e51681cbdabd48055c528ab1539660f4bdc0226ca4ad410f5cc2717ceebb9f420d49d7cf732ba3df54807277cad69143c4c4402000cd7ec9ddf6a047c3bdb61f91a5810bed928a034199b339bec39e975798d150b041ab103e60220074e95a447aa7db7ce459bf88b6ecaefcadc97ebc3890183ea891f56766fef8ad8ef8dcf845f6f669b6ba908c50c950f348697c21b4017e9af032b2bec456b42c340c1828d60771fbc8b35eabe926cacd457b7e262d972f43ed602096b27fccdc42a7bcb3bdaa60fbf4c4ea7dd900fe30eca795a637bfb3e160f820220ee40a943efa8fb2abfadb445dbf66926b1e4b70c4709ecbe010774ac1ebd4a0324d064b2ba6e9af997a94b3e6e4997c73c871dee67c5d1023ab32f4898be3bb7022b9db32b2d7aab79e6509ef5ce4555400503359a4f15ed1f619cdcd0f9a31c4730cabd29ac3e5331ed10af4b3cd63a959ce334d13ad87cf6f4dc029b45a133f4716557f40a1eda6ec313e98a36f1bd37394b677747b3524d9d05aae902d04f34d0ef5be4aab4aeec5ffb44fd5c2dc702d48635417e66d235dac9b623133436ee265629e4c38cf47c6dc3d845395d27216b52d91aa45c5241b795933a96b0ac2c9639515b0de747802ad12c360e55d1677be0a3013dc120c8541ebfbf4e12a89d3deae54b9d5549c82f1f7782f3dbae163f3b05581a1b78bf0b31019bb2de1a0df217a708d68fbf937e8a293b565fbc562da49c556b74b64f7b0e0ef227b85ec95f59d6d42d8cf496dd9af7d872229d94bcc2ca445ec5d42d1217d4090fdb37c1b037f34ee94fbaf0e94168b1c95abb8858aed53f84909b6606e4c43742557a394bdc8c6669dde2748df49304492c2981da26d2efff43cc483d2b408bb4b1a0dcb3608ede7cdf40acaea79fa9821d60baeba14342771f798c8a614239ba664c21b1acc4a921fdf1b8a41c765e2d4dd088c6ce46aeb618b7f7abc8074eba1fc9284476919d7ccbc4e0368166caf6e0aed01b0bcd2f32a6af6d057a9401d5dc2b5725db40630f48f2eb8949dfb6c5499ba7a30fe49e730a2ec96b29956b4fa0cc734bd5d425fe8a6d19e2e08b4bec874d0130c34c2881faf3c3c5d75b876afe01163982b98733ddc5a0589be3b0b938eadb3eb74777c2bd37655566f7f38145361ee81871d83b805895408e61f0c8b9069ef8d485f5eaedf3f0dd9de3ee982b66cda71b4728d5950ce2f745ae292f6368baa46f0ff6c0c195350b5a11d75b975f3d57dee7d2c5e1ab6c8da61880b7ef394db34f95a62204ebf140a437eae25b21607c2495a8d8428aaee60e8158cef160c8cfa15f4c8b3cd84b9931c63e8f76b2c3edcd88ae86baa1a0d475d5ccd3c35f4cdbd099489736314dfbe6ab654cc48dcc436871d82902473a1297d8a3fa4050d75cede42c4adfc7fc7352f07d39cd15fd4a7128ca622a90e0fe76df25d1048b54dbf20ad6d21867738bdc666b7eb1e1a46c286c495ba4a7dbee8b55e14d357ae49fb943cdeb1eb840eb492fd2e60c58371d7ae52437f2752037e7b7f02ac3ee275d538ecb5954a27575bf95cc32dfe46b69939679a8210b2787b1729930d95897f18ef387aa16afc5f4cc1190f208c7fadee6973efb29e12465546089673c7a0798736d2dc71797360bd80add0388990f657ecd0b646a0fbee5a7601431051045962661a68329be7930f353b7084b5beaf4053a32280b232dbf6a5e4dab8763296979940906b26a1bf966b9c534be686522e2cafef32edaec4f59c40eda2ba88df691176370bc9d3876b74951a5234beb1b89970012c0d8934a7bbe67b11d4e3497358c8cd109a127bd7c236c1f4a26de7e155935d73c912595c7a51c15869edf3c0267c8d0eadbfeb1b1eb4cacec869578bc2b1a3fdccc33eca5d64e2df2ccb121e96739ecb983d33e47628d5166fc72d7aa22248d3b08b20b711fc1f3ad1e84d77ddd5728868d40108a0f365941bed61b372cdb4c783bedfa28cf51b9f2dc4a088d6763b7ba04e20866bb17efa9d2c0960bd21275d9e937a50da0cafeaf105180f4415e16f457f33e4b7f2bef54dd365464359bf157a5fbb6c57610a9a9dc75894eb28ef0ceea860d0408d71230be675451a82baaef3226d09ac649eb4a3f21a01ca0bda913de25cdd187e74bcc43972bebf0ee86a9e46308102a488cb1a9831b9f6189fd06047352ac9fdb7693c3a31e3e6b4e9c7b056071e95cc2bbfccfe60c2851f3306cfc12776296ce9b2e5f1b2ff20efd4359334e5e4f1f6e5624f22f9c03", "65ac635163526500", 0, -401298350, 1537743641, "58d2917e7acfdb3466fcc7d850ca2f614e3a0f48a92b31ca7c26e07abc7706c6"], + ["030000807082c403039a443cdc19e7cabce9148fd66f86697e0bc27531c429a596f83dab0918a6c6b703000000066363ac526a53a7681d638218b276dd14e3752eba2d47ebf002f97feeceed2610815ec4e65f03f5a9c0da010000000865516351536a516502f8e37f9598f1bd9ad5e46313c1059d49657421df5059be6cf1ed81faa885ad7e1cda6c03000000045153510072ac1ce6038ef9a30300000000015224ebb404000000000865656552526351634e2a21040000000005536353526300000000000000000131d3d601000000000000000000000000b2418c181a3a70860891606f2ba258b4aa3adf99c7f02da6bd90d7b8634fd2c454eb49d949b00546ad3d45527b16c7bc2af51e9cfe61ef0d1ff57b4e805b3630b81009ccf6eb0793fa62dd77cdd3b73fd759491170def837160bea1569646e4b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff97aeab63120490075c558de47061e58f288e26f44b6dbb853b8c9f3586b287e473eb0e05b22728349dbe641a5c34267b7c66635515d1e95247313fa486ab13ef97925d20600da9f1e6e04758ebddf4013b4830f265559aef2416fb4d5d2d2c9c3008e2fe14d1845c24ad0a95e3056a49fed99f63ee7436d29aaec48703c21d0209694408f8017178d5e6f9294c93096d945e04df5fc68cc960fc8c4969b6b55f0308e81973cd06e0721a73c46753556ef306b7619269bca2b528580a553acb76a00b03e821b6d4a82bf25841fceeb897d245304338aaf7df683febf75dc1d568ce13c67c1d5502626614479a201a9a9b4e6bfd08c9306c09f195a7307aa8f2f5f35f0308ed7314a41f0e0e60c191b73fcbd4da90f9864dc2d568df82b4ec4ebf93cd09031d7f84e45a3db1a87d212be84d7f909ffb8a81ece2c5efdc73dac200634af5f5030d1080746deb6f4ac6cb6f858a4e5d8beefb03da9771321c136cb149b8deb4a20218237ac821b288ca57d8e4df2c19f50351c25e15a8332681fcdc8ab1c321be350306686d31bf858de006105bdeb8bcc8dc70e705983e66d91e613c934839fcdec37ceb7fbfdca81429417cc8ea60b89cf3307669cc6232fb7bc96e38bf0465d78fc2d4c5a7057bbcc2c443026f677f3df894465462bcccdac084467b343f9ba174ce460b5a21c4046de3d8f723e904c197b82d63b3bd75f18852745d2d12ec6673a6c20b6b7ed53cbddcaa9652a41eb63db1c9d1bdd19e1ba8bb157821b9f8eecd1bec01d23ae8ca061ab56960d25420d0b5caa382199c86573ac0b1400c47619669a25e99f4ae455935ed78e1bac30f1403a3fc9756ee2bb4b506a2fddb7daa94a0b44a9b38e8b6455439a77a2bdfa134015bacba9a09e6dff3b8f6f0b845bc300ac8c1c9fb2ed01d76fea413351bf0909d72b54194641a8b47b248003065c2ad4dda9e76fdb30dcc81a27876dc0b03bb02a161184205559bda77a268621b30e7ec4d125c4d94a10984c4494cbf39bdefd129290551168911c2da04d7c4b10dd97de8e3deaf97ce84f3ac07d7ee3d3002578dd9e6517f3abeacb445fefa9c8e235f9d775008e9e750ca8ff93b602b0e78d7316f828cac3af241741ff7f1149e5f2a99f1f6057bbc40d34bbbe3243a10d100250a5bf3d4b4e736f655986e8aa7dc5dbcf3377c511835ef2b97a9c88a5ce96856a83975dba3bc937ba8858de87a2fda75d62e21748b7535d4b3b374142dc5ea2aabf0903ad9a24fcbe0ff2e4a977eefd228e3675b0ef26371849d22aa6f4dd923ae06d82fac5ac24395dc57367c18026eeea482c82976cc9c3e41e711103343f300b9f71d0f4d555c488b7f2aed49d9ce0a377e7d16e10e06148f27c7a90d94693ec6694b8a12a43b4f04e2a9a83804dd6dee3ad5fdb28a24a1df5066526fcbdf226a1c7bd19c9037b79ba3688256fb3c3c260c75cbc2a1c1307262b87c077b4a59110b58d071047d410cb01a4c929a4f4e5c81c5428aef52746d954978dbb500caa3262bb6557cbc4bd4de5ab92c2014de11a28510e0b99830abd4e735ff3ce49eddf7524453e9e3dcde6a757f9edf4faadeaae1335d7ed7ee731f88eaeafe8b358ac51f2fc7b48b1a63bf8d4998da095a9a25210a8f098ee17400bf55701d4b6af52ff8fe61a002c77186df794ccaa48f4dab47b8dc0a975edc578b4ee7ff020b1c47dfa4a3b35167f3c504ac60a45cc5e4693d5366d8cb5568f00c33d1095a25210e52722600614793ef3582656c0c3a92797f87a1c3862949ff7f459754dd4ccdbe4cbdb797a0a73cfdb5e541915cffc5cdeadff813979b4ca11289db6a4fe4cf6bffda3ee6b239ef893d7c96ada4b075bc414b51bcc0ec746c645363a1b96d4fe09e897cf53d7131034147db37a1ee228fc706aa5ab40a57f2bd93fdb2cdb14fcf41d9b82a79e4c4b9c286ab18e110c3fb3348a363969dddc2c9843a105350defbc072b846539db1c83f80321db619750275ca36333c174530d746001b4029bcaa2b2f21618cab3520281a5b1b8e25de0b57187b62f1968b2dfca9d8dc937fced1dadebfef4a6250a54ccd08716468c4558d22179b867f67aeaf3c4419db6f1ed20cd92d5236b2ca57b1272fa68e0ccd4db74e5cd3fbc401112ff743a5636407a95a700a50696a6c00e194a865833e334900dd983f1b2a1b6607642d5201bf6f81497af0c28ae5ae1c9de0d3e3442782658405bbd135a47ad54743c4e1c9dbb66c3d310868b6af41c89d55bd3c338446c517d77bc09622b85456c6bdbe3818d4bba14eedd584f3c98939e830e2edc2f38798e25aaae3eeb00b21ae2b667d707da55793897913152387ec9e30905e9f931d84000388b48ab0a9b5217c0e21fc28c50f2386393c53a585007dea6ce90de82b1ef942d624ddfd588e0dec8f06", "00", 1, -968302226, 0, "13f1c9a4bc1c1894ba458bcac0373e80382ba09a7db914d9391daadc2287107f"], + ["c8bd6226031d94f85fa616406daeff78bce7122624f43b804b256e80b36b626aeddcdf08a60200000004515353517ac5018189f70a50fed2aeb67df152d7e0bac56183dd9173e70d172b22b2c192adbb98560200000004515153536e28a9d8e0a4aa44ccc293d64008cf2ca7a85c0a898c2dcd3a9c80fdd17a6d108d0ce1200000000008ac6aac6a52ac51acffffffff02cff71a0500000000066a0000635365bc4c29040000000008536551515252ac6523880da400", "6300", 0, -177608855, 1537743641, "eec51927cab3cd242850933ee93cb87cfb3ebda687dfb7cc41c165b2211abf7f"], + ["030000807082c403024655c05adcaa4cfb9e3fc3b061385de0dc86b9f307cb901622726b010f4122950100000006526a536a536afffffffff9bb9a14259b2ec93bd90f7d5d6fb943ad7716d665bcfcd21ccc162e4c1ae01600000000004974e57503105d9a020000000000719f71020000000001acfc4bb802000000000852ac636300656353112f61f900000000030000000000000000acce2105000000004b32d264994957daef462c28f999489fada11b315eeb2f2bb6a43fc48b2ab257b3e7742d679f938c3e8688fbf027282db76954e22009b6f459a4757d3191b32d93b33bf1e913305fe0b60e01d4e979b6c0ac23b782ba5102a51073e449004d820000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082908d40bcd52c08c64aa42e4ff0194f2ecbfec7cf3d1a08f3cf112a6f5d154fc0f139da745df1a4ef2c389c49e7b5d48b62a5b25816f31f33dd2824fa2435f93a72716c47baf4f7be348374205217e151a90989fef33de3505752c97b42267a3c9dc35f58d37495fa71599dccb3ce819395c9a6e628891d54fcdd3f953855c2031078fd81a5ef11e571fb24d69b4a4a007279e3119b6851813c50f8a37e57edcc032e94f4e790b24973f259c9a5ac7ab1b690b32cde123de5161180d8aca3030b850b0320a3a0ee9f48c3ae79f45cf522ec93ff78072855041b22cb21db0f2729eabd37552d4993a4bfb39c70a21802285c7049bcba4768d5f8d793797b2bfa5979be020d8d9536eef2df62adaf9f38cd9644c5480402e2d076cec32ccf34e28ad454d60205297a451680cdb913f6b03eff9c76a404a0ba79b7a350da6933058445b840fe03027483db522c4567b88baad552b257d7a0d635482dfc12c803a246321bff66eb0204a453a65cf0f7b6cc7b60d67bf6814f4a374af2ec0ac7e4e07a45e9a4b63e280327e19020a4873201f11e6d000db1d5c3b167cec2fa158475923b7b42779261954684ea34a89761ea76e9cf30ba8c213400dc1d9e54c48691880c1a1a3f1d2469462a17e979538e4909d8b17c4e72b7a9506f816df1b21e98a5bb52f76bf7db8dd662227f2fc0b8b3fb9c210ccdc18f77e64736168588c458993f0f6461aa35f0d071506599b0b24ac4f1f154b31087cb28a8c432cbaaaa7982654abb257f40fc6dbed5d4e4af881da6937a527c28709bd3fcdf67dcc26b8ec4524419c66f0309910161634944e8aa8bd38081dd5581a50365972f37c9e27d5c7c27a1eafe7c8087f3bb588e7cbdab80ccaa118560220b11db4e6093e9306cf0c3ccbbb31eb6bf938955f24a279d2c47a4b140f930bf25a09cf46a4b9d681f1eae137896a58e5c2c1b259b8fa83426ebbe45cd7da4008b4ca8efd46483696265b558954e26fdd879291e14e60b3e1b439444c22e25304036c596dfd43ec9ade2d4fecb62063d620cf91014cd0693fae6016bdd98c9d13da228edbad564c989caee5404b17fc78f9b4d210a1ea2c3fdc2ec6f34e0526d901ad6463e4da9c63ccd7e83957e71235b7c291844629b7a11417adaf30b0adefe79d67f692c6e10ec2ce42e21da4563f9d187fe1352622be36f9c92396f6634a7a55abdd9329a233486d3ee6aa8d4fbf2f5b1bd28538157ab269db401969aa8f18546ee5928f87612c6bf7c596dce4a3dc0e49fc7efd2fe5beff52123fdd3c1f4ecfdb219be197aa50c88c536eb25922722952cd8bebd5038445ebe83b32c43e3a64825dc6c60d928e0a0e2d420bf773a2ef477132c610586549876fdce4e14384f68ba8e1249d3b4fa985b669308f043ee57192a53bdcbf3bcd2efb67314bac51fa041472d255a2124be11366e231dc96700d2730fb568f7d249aa47eb5c13e5b939377b6ee57469f83a1602f971238f4c17e734102c1678130f1eb7962933c7a68c852b4aad6934fb13d695f01c29642c41b700a618505d7cb1f5a1266e3a6bed1a0069458e324f20bc002bedc42b877bfed18aebb69f63161476e3ed2466d866c81c13bd85caae5f26b249bf0997b90e55f080e438168c37ebf7fad3eecc25c51f1f764ed24e67abd583f5607c377aa6d3dc8f5fe5d533eb880961ca8af3a6fbc2d2d43a39a7a5d80cf8cf7dd2efed242d6174f632f78438d8578211014fca8538986b3928d37742012a093cd61bfd6086751ede751aa85e96d0c182cc47c7a323c4df39ebfdb1fca0135bfe8f7e3a14b87af85a33419a5ce59a734b50cd29b0b73d170635656db62290ca51e79479e6db37f43c17945aac24de73649d3ae40408e46c0aff89f41940f29d8194727915fae6a5771ee3938db164b91a2af41596dcbf6ad66991d70f05a2da01a1ebf84a7c07f751ac2738c029761da8fe118dfff3838e32bf87fb1774d274b1f70ec04e4762bdfe9c0800d28e56162e18cf10a029db78848dde112deeb6d6c208f9c1d082b3b3cba1aee276f6fd5e693cfad3a0374ecd7b8daac7349014619f9efce583bfc8d3ccecdea65c33fbbc8cce3d4b76d8437a02d173e93d86befb8f0779bfa5f842b5c5e007ac295c84e094e3bf50f8f47c63d9dfe2696fdcb7e1b37466fc2af409cb447fa4a837fdc3dcb9db66ca8e09597f9fc4c254f9c50000407be8709c41cbc782310183b276e8047f49977873363298ad0d26e382dbe77eda2946c352da17947ca1f8782b2d48abf780d2d2ae3c65bf6500000000000000000000000000f81d1355672f3550a352fe66380b1f6047eddf8c27768c30a838fcec0bd18bff945d72a37dff243313b1d5722b9cd06db4e10c4b4be8df28671e74b6e4dbfd83c3f5a96d1944b69fd4b48ce1080d918e23ff8f9aecac18491422de35e5d5f74e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e73ea2648a4e32ef63529b368d7c363735565e5f1699e4e88307001a4f8025e08f7f93f2e4eb2d1ff98c9b274a06f3bf57bb4cb334528c3da29a860e77c1c3ef58b0e8840a6cf00b1ae2d9290ba3b0789051beab43cda6ad16f6d2c6ebddf250c009225b4d51e3ca1d34dd7c72cbe3bf40e84e6d00c9208e64846c48e9191f09020dfc6596fc9e7e3802de92b14df48dc7a196caa967bd6fd0873d23ac87d827e10312521e18bca2744e29a8ca2f2ffca68361b5ca447902c73342b1cd0d1580698a0a0218dc5c144fec79b9e5ae2589f1992775147cb0340935faefcd816748cf694cc06945177a9e5e2745db7279dfe88f50ba3ea3198c234be5738ae2f6592c6be2021fa6a726199749e08284489990acf68024e48d04aebe06d8146331035e0ddb8e021276a285235d406c7940f15f973c02e9c19f3a3f24c19a19dcc8ad8f475a0afb032be4b92c1395f0e3ec83c398e6a70d7429fa8081943346a7040ad7c0e8757f6d0202af89f632d896ca8b4d59f12796d3de7d39640c2661ec41fe7ac400cc1a33ac0322fa23145b958258254b491d0053df255e493c0ce5ebdf2fc8203df00fc8f4df549ef505a6cf83a9a55ade666bf518e67a9f206be2bc63804af3913b230848444cde4acf72996e0e4eed97a3bf2c4124205f9def129900814bce984b1828e6a89a09e19c21bf87895423b2571417f43b304e5218771e67ffcca7b94b3f9e5b5fa511129305841b3b0ea6a2c0237ce201c17345fbe085c03ba896ef573e9dff4f7f9e52ddef15f85e4f8f9e1d0aaedb69685b542502211d30717047d7b3cb47833c9a97a36ec708a53c1ac9b91b26e8906e7c34872b07b108f78875ed9ac5bcea51c16f715a222cdbb8c9577861905edeaf677612f765587e5a23e179a5c0c5bf45bf3e581df414a621a30ec30a49a15ab763bbf94843cfd674db3608d5acca3778515ea01cc35c21c11f75425298d1be35db24d734765bbb27a433383a2e07621cbbc472d42ad30a53bcaf342645713539cd740bf5e7de3793fd7255ea42359f232004be9dbd64a805096ab821c109be2fbcbbce53d2001daa31ca82f486950c2ec237316debd387e1edd3c23d497e40167aff3d0a62741acc35416fc8f32985e3d12c63968bbd989d7023787e100df33d5c2be8e3adbb56152425332004de321153d75969560b8831989492ea1a7ca17e8c7a4ab90b8a8c993c59cb42a54bd3e166c5721665e754adcb496e62a04f2279bb98d624734f0e44f9677160d1242e47f2e68d49ee18117ca22370fbab48ce3e40a2a4ec99cddc5a84193716954602fbb855b527f78734b1191d121f0362eb3bbfd679f50f33818671e0bab869f37a3aeef3eadf094869c6f2875b83f60da29da7d8f6c3d15338523de7bf1d4f74d1010a1bf4d134675e0570a59bf4963a1c0efacdf63f77a59cc9dd40782886e84273717fdbaea7860894fdb86d5929e89d0e4832d0dbd3765b190978f46adda54817c59095adbfcf0dcca79f7a8c86c0d1a219d170270f5cb77792e3a324ac293c0e5708a5223e9ea731cd6f45e2a885a43fcc8263cda9361543aea31740a91699991f40b583930a2f32428a7d4af4a91061c15c6a0b6ec434bdae41d38fb8db9d23f387d082e9753677975951646ad09e2a8f1694c1c096da01bcc6fd94e71af4cd30747d832e197dd7dbf50bdb379d129d5b2ea0dc3d89e54627885a25aea8ca0d50909abd749b5bfbc06b33602325fbf48cb925ac4e4e21cfb6f44f99ba07bc8987089882511f60c7d7197804223efa6ff5ed808d5b57919c7233f5dc4dd25e176161e128556d08ee27dcc8c06c41bfe16c2e8e649f2e1af0eb819b4a618ee75a75d16a1a780e5961089e3c5acb9a8b1fdd2c755933e46113808281825feb76cd1cfa368ec8542e595e534e6ae3d836118cb3658b6109fbb0b284633f2c2af7421581deceaf0f680bb74497885338ea821e29ccd632a82072285670ec8bad0abfccd1221c23d47b2aa56ac34743801e26fec910fa305944044c3badec3c2ff86be24aa56f6d14b478c8eb3cd889bc22c78db18f9ba42bfc9b5246c2b9b09f8f1821e3c6d6d4743c242dacf0d51551fb8f02dc1fc063d23546c7a2a6f9cdd18a6bcaac327a256750f1a44cf722380323c1901805905a9d4419e7fa655c78b6533e055cac0c35692bff8cdb8bf15e709b0eb00443cc9a903176814cfce6fa3e90d5c647136c7336b734908dda10992a5ea8a9a0bf6198962e1d4e6d07a6aeb603456e058d4f29fa53bcf3729d824c584274f70000000000000000e2e541010000000049e249c8d7baad2c3ed7428b62b70de1420236763b041110930c9a9c591e5f7159ea85a6d8012aa8f317c3e40a5332365954c02b84c97213edf964f9cc67d86d2a71a75529e39dcd81eea07eb69e85ddf6ae9931f000d28084d96ab8968aeba900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139d694391dbb20b7ed8c173193d977f66a6497b7b50e13248e0d7c8c3298440463c711e74ab2538e9c8da72b5b25ec8883b854bf1ae948cf5be1fceee27bfd43b76de63ca5177a0080f76cb164525f3b30381570202b40a8a58f9216795cbe733bb43371d2ba5da9c72792990ee208e04e01dd5895dd9b9387f5def08f73cd102068380a04cde7b956136c71b91595c0b0519bbc6797097203a1f1896bd92b3a5021cda8e88b7350b12de50801b45e045a9c50f004fea79914b97443dd23f2530490b08b475a8827000c6c20a3edb3d47074f856f1f8ba5cbe03286333b55bc31f4c037185b67b18027ab56f5679a4831172a835465525dc1fb18548cebc93813e612030e3219b2f29918af0d821c8806e014f3f8d025eeec2c5f6303d6a5e6b787cc9f0316243080b0e1c71bc4877f2f85901dfb52b617dbecd615e268946010f7f9cbc503078a44c5d6a2857d6c0976ff9ee157955ccaeca9de5a14950371b1cb9b09f192021aeff83a5ec0c63ec28dec3c95946bcbdd67bff992aea39c2dac53472749ebdc032f3930cf74423605baba33daab9f2e55b21dabba6c5b79e516458bab4bd5924c72b52e2bbbb28c472c31e89dbc204b2144e2af496b15f627a4572984c9976ad9b6ef321a0692c1d8dbcbad55f55f571a1d842d7f098fbb7c6947ae274863d214848241cc33b0cc2ccb2220b7fcf8407fac1d62ec62046fc6ecda80bec6b1ba5ab21e7103ed695ca10781b090fd2f999ebc4553ed75c0c2943f64202975ce3a2a65872092a1d274d434fb96024d19c1a7dbc2be33b56992fe88e7f9b7b056bc1e6c5ff1c665544294dfcd7145055f51391a093aa7f6e1d1975307d96497612e17938db6cd823aa12f2ca3f4ea17adb4238381252a5f8a0098d7f0ecb2c08c049676a33e2a327c3de00a5495cb1aae8daa53acf71fb54848f61b39aa0f8bb898b6f5b69c1751ab410b2e3c0189de63e389050daf61cedeb5271399ef5e1509d6297ff45f9092efe17b50c169363e4e5c642d7f6f52aab1c95b9f98da1a1b602b0de828da7398f5c4abd82660dd33ebf6c6824d89f3a7fa303f25121f199834e056e17cd71d4510ae2714f14d8dbd3d6a087ebd0db84a781a5b597714546b435e68cf63f820ed46fc1cf49943208b5b2cd1de3f50c03d5fb33d12d5de8b7e41b3f0f9aa75640065c0de67bade15221c313cffd332b7a28831f060ff553a2af41b97291a1abc09aefb9c0f68c6063caf3e9d7a32d643be1b2fd3e721a2a7f030e94ea1ae61b55ba5afd4b209f6789e08bf849685e15e6b0a9e01ce058bb1581b0599f233aa9f4c09965cda51522c79ce60fd248497999dd4cf82799dbc4daf0c4ec7a69adef2b2836343a4ea60c8fb4e7a79252f0b920f21b1db81f3b4e2225b61665b12554c18696e186f017d25f717c6bc0417798917bbc78347b8dec02b23ed10e66ff175171bb88627890d2ff9da90d6a6a44fa724003dbef0e8c4470b5594a61879eb46da50d9d088a697c16e284a1da99e626d0590779e4af613575d3171ea3e5c6afb796756a0c3ce2eadfcf7786df6f52f1206b626f3e72ad82fe12f803f521d5623b19c896603caac7c3eae3f09fe377dcc046b5a3419e72488a0e7d439f7c21a290d59716896605bedbca9d6d803f0eebda091616d87dc567970f4acbb1b3019522556ee574dbf594040c5ee5092cf94fdd81fbf403a9402705f278769db063c4b6e7a450dfe776be655f22da4140f72cc0b2264bcb0a2bb35bb4bc4b84684c24431915297e5b4b46930b1583ba8c8baf8ad11da8407f52b99ec2c5b8e997b78ac1a67d33ad820fc93b0805e721bb36633911a517fb925bc771cdf526e6be7edecd4b4645347e0aa297420000eecd0cf7d73ce952afe5b3bd40cbb584c522596cbcf5789aa45e433be05e73980ad25e66804bbaec2ea7d9ad2d81920623e99f6af8b57e84c5d9bbcaf8b6036ff53e8d99a476ba0a71c377050cab2df0c4d28ca44cda779d3d49f9fc842097d72d1042edc917c7f47ee964e751bcb5b5ef66d3405b68c65a0d74401436aae8e6dacdba8f8b5fd7fd1399b4506c953d47370f72dc3b2af62716d1a16d35adb864f041d520fd84a8ff95a29d721a0bf5a882bff0e1cf57f4ff1ef5537261802fd421439a027c51cbc08475af4747afaecaa7d359d5a6aa7c7dd17d00afe0d987c51942c14387d9d05ef69291e8cf5968bf9cd4e9e496023b01289b3b720d0ab42e95137b4980ae063d8519a7f183b41f644dfce51c81ee54aba18b4837c4dfa9ba12ab1f3f445cdd881599bf32c08adedd2d1964653186305f73dc4ce277c0de95c1dab2e29e064029f7a231b533394bdc5f6c3da499b4ffbf768e1ec7ada0ae0ade6273e49e89f18f3c5b8a1e5f1740bbb897772a8ab2efdc6aa16b971110a7a0f9600", "65", 1, 1484102376, 0, "1d169fd5a37416746912e604f849caee9ef1cd4cdbb7e1cc80fc715294d70466"], + ["030000807082c40302b64fa2b084cc8d0dc3190ce74abc5efe310b525bd1a87b0268b3ed60fd701cc400000000025153ffffffffb000d69d826d8d95cdee4523c900851e96fa36a3ce58a018c31ed3689eb875d5030000000763530051656aac4c352c2401730e4b020000000007ac520000655363000000004190d5d700", "65635352635100", 0, 284378056, 0, "3f4dfec149f041f1d35a618bf32f17c5133a622bc45b7aab0e7ec05212f5598e"], + ["7697a05303a36b7e23fe0a4639c0b69b788579b795ac88f1509fd1798bc823fb7b6b03c9f10300000003ac5265ffffffffc583eef68d5466643fb0d7b147c090f889a05129bd3f0effa568fc4e1d1c0aba000000000765005200635353085a5427b187f4295c94b45bcad7f5db17133c6032c0177a00abe0d83255995cd4d61f5e03000000060065ac6552acffffffff0459ec960000000000070051636a536a51d9ecf40400000000065200650063530b097e0300000000016360ac740000000000090053515363636365659c20f0ba00", "ac", 0, -1849733952, 0, "8b107da50fba10accd8de476a387d592dfb55fde8b266cf1c8f52b1837663432"], + ["030000807082c40303a7c39170f8d457d55037b0bb1bf7f339cd448c5fed6a34b8270b3aaab680384a0100000000b0cffd6104eabf6464edbda0dbc588b1cc43ead8e8b6f0c8c42fd36251beb048408f59970200000002526a0699c83c4480434b08836c1c78474ee5e3147c2a5ed51ab53d880efc16d8c882ef3b6bf80300000005ac655252acffffffff04b3976c04000000000652536a516a00157f9b010000000007ac63ac5263656520535f0500000000055363655352a3420a0100000000086aac516300515100000000000000000003a3d76905000000000000000000000000ede5f48da7b3f3b29aa7889663614d9d4c820d91f1a71514005498d9726a0ba88458b17b93e8a3fc5e08e8732e92d90443c7346699a55c2e352d9a4d7bec36496c34dbd9213b5c7e43a742205d2ba7168404fa846990571ee8d9f6ad1b9e75ef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cc630b836539c338e3b33f9cb9b770fe5a9c6e10369e2d4e57b614e6a35a9e5f1d5c7de94c17d3af29b1d86cfb2f7adde7ddba3055e2146bee06c3b15f3a97e704bad46448727addcfe9d0b641021cd0731cbe4b08388471dd052a7fbac8afdfcafc1155a5004460c3a3d0e311619a9aafc9da312271f7f9fc2698757a7f1c30222c39d6ac61a111a6be7021c7c6fc6c6204b59fcf2e5423f4385f1299fbfe304021bb5279d0297aef4c5a2aa9d66b84771cc01165615e78224494594043fea35880a0293ab3bdd91cfadbf35ca1bac5bf4252f5d09ecc284f9f10279b641d82a4414fd2cf8991d2013689e59b5b25395452e417a9c242eb7bd63cd8713e5fff3735c03299b69ebbe47d93468d2d94eccad7d092f8b573567ca09b1930f38ad640262bf032bf2f4e29edff7fb7a3a3809f0b013e858a93304cbf9b05f4178eaf38387cc0d032027c6dfa2fcba9ac622a67910a36f22f8e487791148b193b89cc22fa10796f203009b0931f3eee209cc24afd4307114b8923743ea4a4f38494e32247e4ec041bc022ef026ca7a084e786600a7bceea943126bf72da82e80ac7f2dd19089f9eb8ebaf7427f5d3298ae72cb41a4fb707c69efe825d134ed445ad4b576d4efd77541d0cd6718674d0ba3193ed34333bb53d605c2c194e5671ebadf6b0002a8b92e308cf49b880c2f5c187f7c76abd88f62477ad279627c91f58245542c471975ee87aea1914aff523f70fb8a5166846fc4b4699e8b05cf72ae0fdaa92d75acf46ee76a93992536299e27b224f1f0644b05f596a6b06c0e6ebe5e297d13b4cbd3833334ab477e9b47f27ae98892c8e71e3afaf6c421db715356faa844059555c2c1a37ff475774fe1735b356e20adf3920cf52398a8848a1672bb5ad83a86f762962aa8172f27b644ff00e28799a61cfb2f6dbc99f6615903af73d46dab2c10b6971e073b8ac02409e1d899a536311a9cb5d8bc03224323104f3fe823fd09b5bc3bcc234c6c994483fdb5b2a612ff307bff8426b8e6c30ebb5749825f6279455aeaa07ba31a3be88a9a7abf214fcaf96514fcf01bd9d871ba3e14a04ea84434df4ec5b90b1225656d2990ba9bc8ecb93d9bc235f6469f2f5da0da6cef5283eee46dcfa284b45bc55263feb3ef5844de7989834d4d0968bdb4a85064af7789859ae6c955ba6244495e441832120b57fd728c665ac3f57cdd495c2de021c69d28b1219aea1e09b2cb0d914595417aed4a7922b3954a89201e0b29334a742668e4c2648ffec106369ccde6f8e9f3998f026a3b4e5270c0541c8082b7b0a0b14016638d5e4ec4b20f87d529dbb5cffcbace884c3906e8268323f0ef2a44a956c3848fa01a7feebe69964886c82536ca0711b2f9ef886e69ff4083a43f5a9293306a0268e805c65dc947ab3c1fbceaf2bde07b07f0c238f50d64b0763bd20bbc4b35ea242853a49a7318e98cf68693a63267b0eb479c922fe64eaa46d7cbd70e2c06a14a05acb81e400434e427b31b3669359a42760ba74ab04edd304ad6ce12eaab28ed37071940c0db9f35058a66647b5e08c98d5ca85762edfe3b047808775b5346b91c466f559dec3162a145aa5ea46155f589d32147d58011028b3564d55a2885d0339c5cb0ed4bd5a398a1a51aefc255e9c4d918eb447043ba52295dcee8138ad39a53896f10094a686147d42bc94033bcd264e9dfc54450c4524e27a8c612a20a501a7f02a1b29f42050bfbc3c7f7c935333af44a03713804d6da68b308cf04c836bcfc7783213911480f4c2db1c989f4acc9e5d97297819ed28eafdfe2e26f865f53386bb2ca0c5d16d04552f72ea30b506f4d2e44abb9df4221ca1c184b38b993796091ebf661650ad6c7283c214b5e4f2ba23dee32525f487f6e76a208c8c32f8b4361f33f062c84961463825d4bf7800908d1bdb3f9e3f125417fbd653802e901d813eafe4df633d45e428337f232ce493883538032c8fd299d1fa3a51fbdeb594c0e975f1b98079241248e810e36285ecaeddf837e6942004acd88c0cc53ff032d8911bdfa282bbc9f8ebb89a1cc8d01d095c2a60d16252fabf8d544c4b97010b1011451b6185dfddaa9c87ee0a0a49fc83543e8f7eae47c40f2b7d824c96fe3029630d8bcf939b9a74a028d73823cf7e43331398d169ed5362cb3356dd1cb026af341abf27be9d9dbcdc9be841133fd55d0a24b1bc2d7e71fe68c5af9a01199cd54f4be0fb5e7e76ce8b9c61308ccb80b5b5297b63e068eba7640ea16c0fcb3a0cfa1f21c7394d90538251c4ed0d4819b64471f8402000000000000000000000000f1a925b483b217993485988c4cb29d58519dc41b02dbb40ef87b17cf32eb672bc0fa739a6f5beac8964c99ecb07c667db4921679e1879b2fea6df63eab83dbe0240c86e186416e4d0a584fa05ec4a7cd6b70e09c3a6824e8ecef03d24536b28d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006edad1f6cea5f0786411355db49bdf0be05062592e2a6460956fd01e9d3b6be78677a28be36843ce71768656831aeac52e7af9151c01d6637ea80bfee9dcf24b0382e8d48f6842a5c917f7a64053f00fdb306aabadeb42cccc5dc13fe1087262e649b1c0f0f33a13921293cd8c20d1d7a9741414902358c8b331499e1f53067b0210bc9ebc822272cdaa7025f3bb4142a53f93a7450a691287a0fc61c301785c0e0214383c7e04c657f70f45303c8458a7096e8e3164b46e66c37851aa74311db63b0a04c90efb4b289d2e4c6ec37eb3fe9a24eaa6b743bba0dced90d942917e1b73b2336ada7c19a25cc380c7b6511578eb73d4bb300e941b0bd72c74fc5c21e8a11f0318dcf4f14dee041850f056244b62636966349d6da825c23ce1d5c096c4d4366e020b4d7319677a86f567d7eb092eb13e13a975e98c4f74348534ddd662cfd11b140303712916925a4a8126d35a1fe32376b9d6828159d324ebd0c36099d9a239dcee022021762953b44f5cdaf2a349eed0f4665100867a136ae2ed68016c486cb20516021a4d825d75e684ec08aa3e22c9429a7fe94e9644cb2a4aafdd12f0db619bbd2adde29361babb027f4762e4b0b98eeba71f178c0ab50c53365c532bc6725b89aaddd5f8be8dfffc6ca7e50892768fda9f31cd94f05f64ee0094b3e4e8a06cc04814e3b02c94cd82ce6ae951aebbe6df50feb37fe7529ecebf113fefb08af82aa00e5257eed489152d664e29eb4d7395f8eb9c2a8b0a639a25ac88218ada6eabedf109b605da859dd6d58584a457979c23551d2f31fa53bfcecbaba576103207188a330aef3399203c7508ee3c4dcd9ca59205fd40abd0299486bcefa6568d97621d15b4bfbbba2456f8e26f7d8bc3dc6aa448927b6ab9dcf022dd816151df719071f2ab8f74128d568065edc22e05935ffc9642e078bdaf33ded87f1dd58dcfa5c68fc75cf83b0cb52d561c7be9c7aa9a98699b8b87ec40a86eabba644d17a3e7ff407a1a2acd55ab0b599319069d1cc142286f3231ba0eb59af457acda4ac6e26521bd7d0d8fe87c08354359b02b431b3e25323bad6775042edead6e1b6396c1111378119d6c076cd5dd16fd5f55433b8a433f373e3e297ac566f03fb49b7da4baf05c9398b63c8c9fffb12d55126f03546c801c805438b108cd60546e31e8ca9cb4e5c0d89afbc4f27adeaee1aa0ef91466fdfbc4e9ed0e935410aad49ec1081bfe7895ac9aa45c43eb91cf61d2d8524dfc042a1046ae0cf3ffef27b677e7c75d502524d539dbfc1383f05303de1925e75493b8ca83fecd3c0bb1ba4066231232ccf8a8330442933aa6ce94858ae0caabd983a70d85a8c185beddc650e37ddd76fd5a399beb5a9b368299a2344531deaf405813973b4a03002b2b0fc528901dc36b3f49d95c5732ffc05a661d9760e458f514c41277c84e1b91613abd8da14b094c3e971a789442edc3f10c07e527d2abf9255b35d5b45712230b47bec4dcd0f356b194c543b750b8da38172440d730ba6b1106c70ba685e9d9dcf5406cde3ed5111e7f890503d3ac40f4516ca9389381578f0c60a08e2a5787ef2394950815abd2c85cc22ff9a07f841b71ef240657e083e8cfa897afdfa7e91fa64c355f9acc7a6f702fe2b67c2c3b251b988aeb49ec1964db76bd77966c1468d33c47106248b736c41c165b6c06de453554135be55bde3309fa33ec328ef702bc058b451ee9468e5d556a63efc7e858dd315f9db77da0c7127c6efbafba56a59a2845036767949a0beaa89a96821e1223d9243dc413ea1e6caff069ebb453efe0a4fb1d69ed73028f18666f8adcbdc9ff148a7b484ba16b910f274eb9c5ae31d2d1877a93eb6923f8721b36dacdd83714990dc701f133a168bcebd50fb5257d101ccdb1409b67bea6c733a8c44082c61c5a0455f2627dec43d28f769fa61f6ab83b94a8b0caa84e14a0a0f8494d5c5c539b24eee48cc94b72194038aca64b24765be7c853474ba783dcfa4f570b801dddcf54148e59a243c5168e44e6645e23f3316feaa0e49e8db8d5a0eafed0eba2fb282e3c488a4f9fcb9560bb7cac7e03d5b0dcae47c7a9cd0ff35a400a9ced90aa6c7ea76104d7ce991ca8baeb6acfe9255dc5134dc3437f297172f24ee7b30c9c01835bcf3c37547bdf565b36ef3dcbc121f848c6b0d7d467b87706cca862e5fea0655463a204a2b1b0aed85649d32c5280680c7b2c2b4e471c1b868d50121814dfd235e29594786d54145a62ecd2df9fd66cdd125149aba02dc42c66c3c950b3b121b0b5940eb4677204000000000000000000000000a836261dbdd3db24c4a778124f382d6883966d7083b1f4614f2ab5748921e7b53f21d9cafbb8959099da47680789fb701268a34f3514217c386e41f0dd66ca002e835fd6beaf104fe98078e6ef6df181491af33b466d83f62fb9e821f61762550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001758cc6574514b6fd893053f5d6a3e4aa709ebddeaddf530abca17fa1607e54cc8c39b0170e8f0a144ae7d0051b789e7723d0470693892d133b1c70be7835f37e83470d30eb759f124726d46b5d368c92534a8a841f388bcd0ee88cc5dc29694e3c201cc770462b49476fcc05dc72ca26d81506385e62bf1062b3dc9e46c0400207b4419ad9adb5695f90d09321b293fca88d680019d4279f190cca1341f8018002126231d4879b18f5b0c49cfc86c1d8bab4d486bc8b8354df283d0e5bc63b72530b07b76829a556633acab3224592e387b1f8bbcae315d8356c48f0cab748d52b529bddfdcf43b2af23ae66fdfecefaf848416205e3c4a2223eeb9acd7877052727022a4c4639699596c9ea89172cee6ddd2e572a8cd2f5aca1bf6e71e3408bb52d86030344d02400012cdc1e3d2c2ae0c155357465d686ab4ce2a9522de3694972fbbf0318381a8a7010f7641748767374e102988c884746ea1d3db7c24780d7808e82a60214952e40da9b0ca408991f3abd1a4a978b574411ef6f2a154afd67f0c37b30ea03019a2d4981d165cf350eed7864907ee5cef47ffb2a91547e96369eff7f066ba23b020fa07ebec13ac7596c6a327a566b499875eebbdbf23a9b90b2dc61019eb7ce79c5476538a885b0c9817374402e0deb4e6c9eff3814314adc4452fa0b067bdfbf2b9bfc270c71a09e3e66ed85979f8fc6aa06cdd977ff992475572a5866fa6aaa5abaf0b0e3c6186a77bf479dfdae176b3be954fcda5cc1502b131dade8ece579d68e95d0dd50b083461cb49342f975833dd7500853f1099ece6226b88347828e93d81374856a7d386f75a2b4afd1fab85516fcd794804674a05e6f18bccda5115e538999724700abe1e575e5ff98af707e649b9852d6699cdb4a5eb5f6ff7708a3435de1ed98f99e57d9501dee3fefcc7d3b4ac98adeff722adba4b5d393b68d380004e4e40ba382dd8a4b263edb77235b48fb41affd880a8e2bb3fce558b7cfa6b95b05f2598c390a3e90535d96e29312a50f758de67cd7e469c0dd57c8059e4595c2fb0b854b84fb1a8780a95b984c5a1506bd6f4a0f161837db6535fa826814211af70dae273fa4213012a0364d0d08fb413394bd93ed80dc4cbab59ad39d2d860cb71aaa7d8e99cf89d8e71416ec3f3edcec61926211c89bc60fcc04130a1e5c2188dbb1106a357d840e5969190eb647be4a4061cc852e9e832eee17f025cc73d6a9e2e4cd8622ddb0bd6275c5ce17e263ef8b6d2698428808711fa634589068f5eca3720f73b69ee3eae534cf2d79700ea55db7aca8f67a1552cbabc3124b4842cc7b4ff5586dec1ef0ee629237cedca967643bcbba4cbd5d70df35dc33b1309588ad2657f69be7ac0fca8cf6bc167c4c5a7798da98fa61bf6d85ce404e11a36ad02829279ea63d1b00bea95ad78ecfef7d8bcbe91deacde2b1a8360f41c943afe6b0af79b5388f2739f8b95168e88d95b3f8e0f3688dc7071a06abafede2424edcb0d040399cdf1463961748366dd058552fb0f5a639c4d800bf0c231a31fefbb617f5d21e9d167c098fae6d3787f15dcfafcf85d5d310313b173eecd9abec56710f83a9df8cf4adbcb5f7a49f2626797c7741e5b840c95901f60c7d1eba1c0a6e83943558c146b657418d38ddc9d2c0b89c9950f6e976f20345b70b0a661cb187d0287613cf123b81bf7b1db3f991e46dda94ffbe864653be6e98e38d4e114d7d082d1c4857e1450f223fbbd66e905b11e21cc635529edbc4d0109a95cfef6fe0100969c32e5112a2644e3b3adf3447c550097d3572552e7e4a176866481670992ba468d71c2fd7c65bd9e595a16316a5c1f66e95748840ad71385eda664ff85a9b03d0abad2be8efe30308b5c83ab1429b74a4a0a71369d06e1dde801e14c77b40ee860ab8c088d6ffbe93e3373e7419b2aa9df182235abcdb0d7e2561f877bf2dadf7bc2d5ae1c6d28c29cb4fdcbe41bfa34470fa1185472cae882a0dca28cfc5692f61e7f27f25e6857ecfcad3753358d748a6d1948ce08f1df89b37be1e9beae092ec99a05c9ae958a67c98f7e69cd53b22d67689df2f0da144d602cb381801eb0fecc2566edf9b42ab425ea1fbeb209025dd63ae783ec989a7144b989f632b3b396305849784c17f1593e4854f3c4cee1db2d64d38ff323f52ac42f710ff2f007c99475aca87a5c7a5f74effd87adbcd4cdaa436fdbcc21f5521bb1ed9a33bbc5a2e3168cb2dcb818932ced95178dd4cc8b08c59346d9739cebe5d8671c37cff972fa87030108ce764f09e9fa2837ae3f8ba932fbd044ad485a9fe330d9df3b600141077254adbdc6ddff43a6c04eab21f43f22fac679d63dc34f252dcac8a69c742009351bb8c4d23b0e6869f98219440c4a16e0bd9f683fc742629f446add8179b9cedf8d740711a0f", "00656363635263", 0, 1025392125, 0, "c52ad4d989ef4063a0bfa7f0c13713ae3c99b60198a8bd010f5b8ba782862543"], + ["edfc012b0477d9ffce42d7013d0a0aaa4ad86e286f9a27394a061f56369e4b435b4708cb7e000000000952006a6a516a510000ffffffff8fecac3fa23c95956f712890230e4545ef6ec1d0ef07d40d98a75c7725c59eb40100000003ac5253ffffffff334558e6e611340cd30a68fc5274dfda6043e4bd94bd61e95d9bc2b411d5046c03000000066a635253ac63f5b8c3b2c7c7dc6cfed45b39a252efecf5bc032b037677afc7e8505cc434a2e082d8bd8d03000000076551006565636a9b617365046e904e010000000009ac5351525163ac00524900530500000000003864b2050000000000c6da580400000000076353acacacac63000000000200000000000000001330460400000000abb550202d4845872f6da4f2d89e37efe16ea997aa148adb7e602bae4caf2188b988c33a53dc53ad957eee363e20823824b67435afaceaefc2cd81c51a173533b32eac696a57e8f0c7431309bd12197fb583e4aeb5758701652ea36e2c373f9100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030727c0b0413663b4e64428bb0fbf4e2c6cd765c5825f8775104956861c63ca7af8ad674cd1b411ca6d64baa56cf61eb3360b5496c0a54d4cbf236ff009efada501a157005db4de8ed62e4afc52969dca17aec661b6ed07be030b7f65348de852e950580b3c60100aa845262dbe2982466f0f81ab8cab13c63d26f1331ce4a9022c787c7c94a4af1abad31d5082896e483e0f339b7243f9806ca785273c33353d031b958a3610996871541e93a0d56f9a9ac34a21be0fe521550bf67a4a67fa9c3b0a03fbc3283d2a775f973369a2ce852443ab42e1440e23f32994f8f085f79b8b5120075a87587f05c6a03153282d8de86a60dea9867701c92c19672bc2e984b962030debe0f0a5d3091cbccb041a7b661fee2cb3006fd71e8351c62debed395e52dc02186d683599c1269c2539582eafcf79c96153dcd7bd7d327347cf7e487f0c66ff03022e3662b0fe6a5b7722cad92f6cc0c786ab2b9ec096c4b39018894553dbe9210313edf7b16fc9ae5a719d86d34475f3972ad37e00cf118fafdfd3b02a435a95e2030e28a60e5493abe60640bfc209c250f29a7c7fd4b34ce6b6b4a38e06373582f026242535c36ee452ae4960be7015299d5a9fbe8ca295c891e45537ef42da84d2888421c5b2ed507d4530175504fa2f266441425a5b008fd765313420a705485b35b304de9467d7636c980659cd23c9e3f5948413cdb53f5120815a7c12ed1299f36eac5afcb71568ab99aa13f387c81d0c631d39602a5d21c10603dbd59064b237c37cde4f660c1962e09d3ff2feb9dab9fde82dacdab8291f22c95bf031dd3f7d8f422464a554d2ab17b9a010c947ba7f429dadbea570567c2537c06284b374e216ecce73e044b250513720879a104f625fd51d18e874c88a9c6ea56a6019fa1ed64ba3c902a2ac3bafa7170cbc21a928c32570eb9fef5216ec30f78a9d812ceec9ec2db192b0d3cfd1f9764364025fa57a0bf8ddb98654969806a6f690b435617fcfafcdcb5f6a1f96acadc717eff47c30738c8c03cb0ea6c4c1362fbbd224a9cd6369eda5bdc030779e25331c885b07c54d85b0902dccb81b147ef25dbaf5ef8fc9f22c657c6d85c6c02bae2a3757783848ba3a3be5385638b0e7ec7d9f55e083ee3e616fd5293670e22dbb9f0c44833e3ea85ad6d53ef41fdc447bae51a7a39181579bcbc08df0a978bc105e293e591b99fd76dfd6e90a2faba2cd73b342d696beab3238f8100cac8474d40d95b111cd8bc74f88cf209dbb3cfcfd944fe93e7d1f745a7f5f68be630bea79b49a4e8c471f3f7594d99ede4acaabec2a2547ebcce9b705d93417f1a0dbe3e385eeaa6b7d3b45b332ce5c1b0873e3dd3d2d6491e063707168034bf4fc36914fa191ad0d05988abb23737b2b834757f8f86c80371f96f804a0e3cb47a45e66abe89e359eb9f1b73f0a20128681af1f72d271ca653d8954aea02baadb2a08781f3907196e8a4d3fe7aefbbf89a90b9dd62e45111afaf8d85555d3b64ba624923497cf2bb4d0cf08c85a460ce0a22f5e4e871b5a8a9b46d52858c3de6eb1e4af8902c02dea19096d7f9c32ab9a8effb8d0c4ffd7945bffdeb79ec15549a97fff309d955be9e48be931a5c373019f42eeffa784d7ece5fea682cc9cc14c91f49c3191f4f51c9ed181320ae8cfe6c15dafb766984a7ef0931aea8e84247ff20f75fd3ce266a114fb7993e5a1015ab765ca4a6d15d0a80b886781d5a93dfa0c65ddfec15e638e7da56ded8e24205e81cbea7d860cf3387bd58f2bf8f75d5d6ed44c7108692885bf875dfbc17dbcefc0ccd3b064b764ff84b8cb50d86f77a292969f5dd81203cc0285d7ee5369b5368414bef4ef20f451f7ad0b33cf9210652b1e22e0fac92808bcfb77b77739e96ebc9732bb06985855648d6a20f23fc5ac40d15ebc0fa61404fbbd9200bdf87c039a7bed4e4a9e0e9fa336c06293a6619a48c608894d0dc5bb0c62b54db3d318e27f3c2f4221e5c4d2333d09bf7243ddbc9a46d48708c4ebe4309a60ab36e8a1ff6bc156fe1c0f70e532760a23bd900244cb9da0f6f402191f5693853a9e45ba4a44b82e8ee19836df5a3f727fbda8f434943c8221e4b07e82dba13f8a33845877a610f700b6bb88920c88f6092dbb5ebdbfb6cab9e667e25d24b6284d1e17389b840788b78ee97fa873b8362a4e6e0034b6d86e056dce1e53ed33ee049f3d7e120660e0ea2a33bfc91a02c725e43d5b6a4b65f86e3a86fcfab6896f3083b6a8b6e278c3dfc50b13fb686794894ef9be192b8a9dc0ce841282ba783b6d0500000000000000000000000018bbeb09445571bc18e806a8a9215b162a41f48deea7e75e2d2bcddd5e95ef6ed217f07ba47046d8dcd22813a6e0aaee3593e8f78f1244f9068d9bff82cd0f8fc1b91a0d741cc056297377b9d907f2e562f90b3bec429e3b971c9d6e83b0b0e20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029fbc567dcfd43f17cc078b9def8694985086b06c3f35c537fafc025d6d2ba3ad13c470af6d73a083395d2824ebd928c2ec5e4367e5d4f2c9df56448353b2de61f675e9ba3fbc34845dbbf7c66d7e0961f3ac1db2d06ce030bff9d1b8837add0396891297247259c183313fdc75fccdab49f2835d3604bae0723cd3b66e788cc020e40796537a5665435d48bbe805b2c9f40bde12314f833bca69d46652f1088ea0300a933b8f48692bccd35ebd30b51d37db03da25f0c4bc37beaa64b787307acfc0b03d4cac6f3434f4c93dd5f8ae7c3435b2a8945373e4bb5db2b901b429b6f17a208273c4f4bd3c219f36b088879835ac34aea848eededa97c2ecdb4bf719d2bad02136db36f26f72f86e2d08f5b1c919a08cd8bc003e97e3538c55bfa2eb5755161031e28697ab28610bb5227e0526bee94faee743736066f4699e3941c3ec90bc2f70224bca9c28a25432f6abed2f02e229508d46c08a5e1db0c61ec26a23f65ba4cb90304490df91447fb59e3ee8a2c06012614b298442313540c916d931723b3979a77031d8aa06f5d1beec2011b12a330e646ec4cfa7fd661bd164418792cbd221d50f2be5944a9049d47c26d8a73ef21e9e871194c3fac741041f1b943997081563c38801ac6732ac4a09a3532b8dfe1c97f97031d3b73b9d0426a6da1ee1c1a8c5f3dd4f059fa171b8cecfbcd7b1d5c90063884691f8f3c63db28104a923e9632222ec85fd381a1f2c7e891b673df55da2e793bb7c296cdcc7baebc071cca6b791203511a40aebc86fe4eff54bea6152635c27a9ec9bc1f9aee9f67b7a225f08db3f1d4e81956a0606634c51061ee5c8ecafb0e1efa43c426eb0bda51fc73279246154bcb1c2cd5a71c6e91c036a35ea0b4984d154fa949d732d3c98a5a9f462b5cdb03c56a8939093ad6fb07642ee3910469ca73753c9c1bf16d4e0114d92aa82ec5c576712923b361e9d537694e233bcdee1970bfe659d9d57f3d093d9f76d65141059e399110a4c87452020a5c1efa94576dd58b23f440267df6f95d3d4c213c46eb88216e1e40d2609fd18fe23e5a12c0f726f85f06b7fcd615d52310a5161b50cb7382fbed8c78e8ac76c467e243235da99dec702a99ea7bc714533bbfa72244679b343935fd272e8056e4599a94755933bf0f1d11d31b5bc3e05fb3a341a73ee9cffbbf160d0d7b00ba250c330c4bbe9efe0db4e7168069322f037a52c2a051188982ba2773256fce062528affc4c94367cfb238013f543bee4527fdc31fe6b2c93149771124174a7445980f57d0865dbdc9d2a968135e1c3ab6f02a200bae1103ed623bddc69d8433efa31197aaf2f8e782cc3874b59de5ea972f966ebd6956cdf27ac9f356f3b158855b1b160711e4e127c8259e6840f9a82e93ccbab2af71b956859874cbd00753f4ae151530dcf291707ec6afb7dc3995a4335b39525aa37f723bb465d4be3daf4fa7bb0fe41fb856660ac6b901afa027a049f5e2075d9d523f9cf9040c20be94faeee35ec7cdac2eba4a0409df0e82415024ad063dc38ca24b54436a723fc8326124c6586d32936872a6d212b6ced2a76ae534f180d9d26a902f4ca2be3d378ab9f1e5e89b9b3813bca99aed5d89dca34a604f20af2c48a33bf664884f145eecd5fe6054da608a53b0144029ece0cd91f8447186cb71a23ec37381c259eb6e5e852f44b310fdc0989fa4da25edff25cf1603689c27e74ce71e0c8659b083104aa101d7c89be45c6f17caa7733326e9405cac7ee3f46ef47885521e7293f87f543e1c1854a058c331c92c60c0f02a58ad94cb68f6014c0fbec171c9497397a9c11cbbeb3e34060abc08387aca11884c19e73474d356dbc83cca66a5803e2a98abd88cc76bb291e6c2d8012860bd1365d741ca29aaf86f635baa34613277ad9162ba2e3fa8b4b8524ed770648490ccff48d2f70527d94a7967d853b8782cf0bcc24e2b5a7b5ae9631f7c649fc87bc872eb3aa3fb1e194e7674a5e918c9e696584a6683b76f72b72a3cb3d276b3fd0a7b0975e6653724b44152a0c50e37db7e549b3832e578c99e6b4cddeb086bd29a861fc542886e5ddbf27cd791b1fac84c0a94cabea4fc3c952f3161a4d77dbbef3ad540eb59d23964ad34e93ddebe662cab2487cdc4e602484cc9f410eb5c26a4eca21598bae1a90c12c644fb9896b767203f3154e148af64524185a7594cdad7719f17c62ad31b21c3c3ca4572e68907a70453f3463edc0d75df973a9651bf74f4cbddea09134df935d465ed2d0eaf0bfd34b33c5ef426b6548a398bcb14fcef3cbf5e2ec0b590a2f229b754d56cdd3db32d64986efed64ef9960252f6b5e2f3c4c73533aebc5da0f97e8e6eb6e58d5794f355d3c6af85317b42d694faae4e432b5ce500e4f523e0a1fc1a635dd4eb48159ae792e0a91ae106300", "635265", 3, 1864993491, 0, "a6d33b8e6acbe8432a22027cf5ed901994a7d3d6f43b7222ebc93fbb87a5491b"], + ["030000807082c40301923dfd0006da40080c211784243f7795af15efb84429a7b12d7958cca87a10820100000003acac6affffffff0296427e0200000000076563655253ac51b173400300000000026365abc444ba00000000016dddef05000000000000000000000000e2d9b32cb7eaeff703286a4526b6fcf9f8db431cb7052223bc68cbbaa2c568e6ca6938ad7d7ee6d3e2f51705b279b7fb36be96feda6b757d4929424f530053267d8f56004be71f579c36192ee5509aaf058219506acf8e405abb64b7d33e1c9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d9c59b2f0a9dee67a4ac880ca8f8858f98089d810b57fb8130719d1a900ceaf3a8510dd73b96a314c75d1a450df9e1b86f1cbd66ac05cf81bfea7fe46aa936d57b89043d099f2ea8cac803ac57bd08273500dd4fa2cffce531db3568ab6f904a6e21d398f0b21a9973698f7e910e97c86c115231fdaf4002ec55e81e79766a3f032c9f23dc8e97b1dd01c508a4056837f5cbb24089eade942de35f955415e8fa280220e407c47fe30b1e429313a829e1b035d438db1d2d49f4da3ee319370f3229f80a0774c1406b4edaafe3f0976b673c54999efa7b0216eaa3150a8d8e77a13fe6173c26fd1f40a1c5e8bdab4ef7e17c2ba2b730487c92a80efdd9068f44c4f85697021abd0d05299d428098bc74053da509ea1db74de0fe6e13b217381c4bf8090eb80223dbc71add09b9b9316c7776683ca1077a8498b1277aa9de38bf7577dacc26680225c453c79bb4c34a0effb96d554aa387f367157559b342e4ceb4f46ebc32707c032b80f71e017d4a4fbb4367e6330569fc212e8c13a9b66b75e43be341966583b6032f511635ecd6c501ebb9e3356698cb79f8a42bcc26eff0e333ea18a09a3c238fe5b1b3d8c1ca1f24b62770b8197ef25ec803d8c6e31216aebbffb411cf2e01063fec37ae01cf6b0506358df7db4689977fd682c3dfde642cdf47d2c1fe1aa593ed27ac6424b892dc0c677857a45283a558cfc38dcb968403e49fd8280bd70961904383a9bd088cbe345fb46de6594ef3496e7dbf2062a54a531f9f4a7268b27d759537214d3b2b0b8548d724129ce9d352775172b91ca76b1fa5e15c7c66ecbe608da96db44adccc53c91153afce54d3cd7d1f9e5c17756fe764e470a76f2acd025bb520188f8fb389528d68e66c889fb891876b6e3b621ec19ab47ba62d6b748e87432ea084d9095f1c2249c365c3d404038e4d6e8b4f51f5bc7504b28f84637ed896c7bad5ef1bdc4162f5a8ced6bd045d3010d55784f5e13f6d30f92eed40f8433f52363edb3742d9333ac1180bd5a5e7a94cf2010b1921e7b1b1648124ba0c503490a3aa4cc08b07a67efd40f7ff9eeea10b785b39320e40e36848d15c8f1d8b0dd8404533624399f374e4d9c6d2bb64fabfdf0c286e6fe2c570b1b3573befc3cc89d40da59f9ee8e7f0dfc9927780a8be70eb64ddc1dd02ca05b46b776ee57684a090aa826d81001c763a34e5174ea8bf72b14ec8f8c4e3f9c96fa391c210cb7c50a4cae8c35c404ac9751c44b3b60cd75e42ea33144cf70ca46551f11b43ae984f61fedb9eeedc160552923161585f44a5833bafb2c567b14598195158dc478779df7a23d6438d5d5e922469a118846e603afffff9f2abc4f08cd0d3979b13b1c736d8323a279a0820376e7a9eb05e6bd5e28652025f51bc095eb8fe7f488c8f15c5adc3b6e38297fe7afa6065e5c4ff6e1bd12b3ca02131a558f90a5e364c6e9e39d569327080d1623908d4c041f5997bb1beb37002a089c9f60417b708c1b97ff6ceb7dcf32cb6fbb631f89ea5df665aa33b57d5f02d6390707602ff07c7e335d903539484553e535a83777e9acc6b19ce5c7a85e4fc893755ea8abf7dfc9733da06c546a05f7b6a2c69e9dced40f52940a496ebd24158983830bac003cbf67a979ad3a901163009933e241a83e44f4dee588588d16931a0e1213e128bae5f2e6275f12d550647368d3f69a4588c07660277e9c0920d239f9311c5a2212b0cc6cf6c7e890240204df472cec171a0e0b181e008b21901787986e4f748eb377cc5b1a62843128ba7f76f2a84e77e47e43d524b411321b8d98e9c01ab91bf78ff1406fb0bc2fac97c74bdef62b6082469aab62731bdc395eb36d752232c2c6227ca095cb0d3791f828d7bd4a73768d63c512ba8511a8111e3a3c4e872afedcf2d98f6c887432ef003f4214c0f905dc9e84318756ffc9dd5876d12d99986beb5e1c9ff77a0aad119b9e46f3e7b167e3938b5b1fc564a3cd8406ffda8d881efe9a4a25fdbedf91ab3d421f5b6700a8adf7c8e31b063a0af7f5bad71812de43005905068fb0b16cd910236dca7d750d768e73f9d96658b7380bf88e785a08467b56eb1d2bd819032c282e829d47451245cc8e86376c31b09996fe9ad20068063310811e26c5f278bd46bbed6ccfcfa5fde25b4f37599ab3df95613e80388fcc4d87cfd4ec1408e2c5b7f7458484771fe84f99b3be40542350ffb1ef4e111584e9e8f7430d75c4af1c4e16d03cd1cf2cd5983a5dc4fd2c01f26b7e61bd4f6c9097a9271fc7cc29a1bbb596afb982b4a415b5fc189bcdca9c86b72f112c202c77394ae910f682a133982a6e886435918c604419145774f8e0336ad15b2295ec740a60c17b40edf2962aae844257fe7ae042a10812c9c6bd33d815cc7d51135d80c25e6038526bd609c13d3e238ce76dafd0a", "63ac63526a6a", 0, 205200832, 0, "d8a943d1fec6e1b32626de0efca5e3eb05fdc3de040a37bb688a05110cdb1aad"], + ["17ac6726047560706b9016da1bd4a95317192a9c208b29e63972f9b45c253d7e1016f0e8870000000008636a6551ac536a51ffffffff6e00d18a2696c2b1a14e1fe72dac13acfad3f01ea42ad23047036b1b85b692b1030000000652536a00ac63ffffffffab7bb86c3450d9dc79af4fdea4dc89d5bee1f66cd3df6cf6d4928d9ac486b020030000000400006a52ffffffff0af4667ee623063897662b635d8d5df5fc742782adc9c7b94d563dc6abf0ea42010000000463656a53f4587f3902a47c3d0100000000035353ac9a6ab10000000000003463938200", "5365635263005263", 2, 1153208478, 1537743641, "39b149b97283c43a6fae698cc9b33c4f2d7e66cff2be143209b725bded32deb6"], + ["030000807082c40301841fc312e717c241d90e6d7cf2c9c0ecbb845234afbc9252e829ec6c965a93890000000006ac656a0000ac73c90d3b01daed760300000000076a516a536353655fd29f89000000000200000000000000003139bf0300000000692932c2e57ad1381334ac0ab9becdff4f18700b9c0519154617808bc133b8059dff932e2da76ca4023d38d37cf3038fdae85124544cadb1decfc769302f7ed7ad39deaa25b6f86c108d065efbf851051400f8101245b04846cc405e06901de400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f5b836f763810983421f4d0f71bab7e1fec17ef053e9e2e40ee1e9f169a44e6d175be4f8fa337e7f2e5a181328ddda272cfa39af678d81beb7a4a44805fec47be2767345a32c64f5d751f011dc166dc3305b28d1528ec47120edebe766a5337e1ed7db7dfddb9f84d32b86db621800ffad29bcd22542c4eed6d330e19da770ce0307989db99fccef29d31f41fa1991aeebc56d64c689112a64247bcd685b324b91020e26d7dfd758b4df3d85be51f5b7d0f50aef62ae969cafe205b70169c57a818e0a08de3da4dc50a2c686a03b7594e90fb6eb365272e7e7b6490698df3bdf32c01d211f30d0bbd97b807655481a6130c0245b963f2b6b75c76d96aa46f8559ae14b021854dc4e17c974db4e6cdf3ff8cfe7f06e678cf3de2c7d1786630432d4f45d55031d7e4f84d2841a48a3a80957ab543c2609f667a64be749a8ed05f1a6ea0f067e02004f3f8cece261a222f3e71434ea61b410d398aea5d0306f08c92bd33562b5fb020d50a12de513ed3d701c2ba0dea747447e7a035c534ad76ce40cda6016cd3bb6031721095d5336ad55a1c72897a948b6117d83cd4e9440ae0ba06d56b32395714641afd4541c6b2eec3f77ec633ef6fd1c24af6cc92cbb9016b4cff85654ac9a1ed3115449686d9c6273019b6c878a59a99a847a73d3baa7972ec3455dfad87419d950c76e2841b9344da40645add816e4ba445e66bde58dfb2ddba0622d21a9e9c31117c4c89eff717511669f2afe52c625d7673e4fe2bead018a55312516dc91151c68ceb66339cf0e8141b823938cab4361e5793f72ce4cbbb0118b6415079091bcb62d23fdaebaa98aba39654162404268c0fa67a9b0634b7557ee7fe38da8aa49db0e34fa4c349d41e0d0ef3c17813519fb89325aa0577a596ee533b07926292bc4d89085dc6d61afac101b2e031429432d2af87fa808aa3f948a87497e0dd7929df8dfc415c41d9a5df9f57df02d3f2ee19fcae23aa194c68443a2273f595195cfdb24c09102b2efad3032869a7e5ef3caa8508f4fb0fe3106e1c7046ad21e1e11eaeaccad58ff535c51e382a35081279d6be1db57587a8ed28247dcf3a4fb75fb1474ab99eb865d9b17c469927b08676b6eab519aea96f9df71be702c1440860ff8024731c6d3dad6c32fe06bd85b5cbb344a8ada2c4656523df2d64537883538b0224100f744fd729180e1895f1fa7eaabffd76de38f3eaf80e1b66fbbdc4cae11194c3df8309534cae946818f3da1487b8a8ff17267b98b9a49013a9b05fbbe1636e1f44029fa96bea7ba85fcfbb845fdd699a2ccba6ac2fa6678a4e9ed7b548d65e2596fd1195bd914570a8e7cdf9dbd1c24c60ff14a664f6361a2b1c8864a3bf33678e93543650ca98fc0cbd47450fb0e70eb1e6cdbdebe783edf5ea58c37f60d0ade0f59a27f20d845eb48da2de8c5885ab270a32eee3fd2bc165e42d989f9af61ba3b1473a1cab69fca248ac1f108088d89c2f2b776f673ecf266d6e2ae46ce785bc2cbbd921bd57a20d95aceda3d3483affabb3f1ffc0e140875e46ea290e93c059118625da6d0446e20554f46627b9462d086eb1a1aede357b8d760861f65e22f6d7aa1ed221159911292fd4963a53e6599f613a63f6968674da619e0649621f6b07d151e4c078474cfc8bdd28b0ad4a8e919d0356396265b6bec7f438ee206ab00a30088d03688364c1e09b5439472bad81a7d0a3442963c2fff95d96fe484b26427be3760aa72307c54ede9eab00390fd3c4a3f24e6143b9fd36afb863163d61b1e19103ecca5f89b789f903d82165c0213ae1950d2202907a7e3da8c3a61d1d5091b8e40296da70d57c1154f54d5ef77b07cdb4f9c38c35c38d5229ca3d82fed4ca1a65eb7a17f6b366aa504b60e0f6af3360ddd57016d1f72dfcad52ffff75965a86fbcbbc7059f11218345f42b8f929ac5a69fa3e634f21f34d6a8829d41a7b4678a058e32f706fd8fd7e99ceb02d91677a60060feaf006e952b4bb37c335c0b35853702dde94b8e626a0020b5e08bdaa0147442dd770baf66542b71da02a7eeeae85a163b8817ee52a285203bba0c3a94ca13613b39ed9aa434310456439b21c4d1a248343ef7e30804c084ab2e299cb05889dc249e500e906abe3caa2fcfae9be2e61bf781170a6a6be673af561b60090da3c3dc9d5a809a56c6f784149fbb7831914d5fe4667fd2c0039cb29dab657257bc7c677234e146dedf1b725deafa1418746abab3ecbb0a3f67740f0b77db2d90e97945f880b65ef17412a2e5aabfdc44287def8d7d7f7b000000000000000029727304000000003f5d42c851e1e4ebdecb2a76e0715f02186458ad2b6e951cf54ea8425b6b174f688b9239c83b0ce4859bdd86d8ff0a8ad6e07fcabfe1944037df36a4e0e303b2ec79ee72730a12a20ab03b389344d1ffabd8f38b013c81ab64090c8c83102a57000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b194e29cc808ccb559d67b80fbaa7a3f72d3f515200a032dd79f785d60564328249a78488dfc606b9ca0042e0136d349fd550d74bbac00843dfa6675ed03f9599421d8a4c5a2d0c7e468487821ebcd9e72b9ee2084ba8cbda7877217961f0f03509d7e16f7d1b8dbef96f5fb3b6e732e557b6c3151da252c171c3a65122457a02127f9c65893ee8e182937711dd9deae27cac6a1a400d09c949ab971aea4379c7022b25bb710bbdd40e9f6c919fd9fec21b660f85a5b2b71100ae2e0e2df71097a00a0856af97c2565c8ade0f2eb9dcad6e565412de7b6b573b9f6cb36f2d2545dc023ff343b751c540e1c9fb2c7d33d5d1d65f3ec3d6e9360bda3430edcb7f5421f2022aa72f55f5f27e99a0255706d2781bd6db5e1df969e2bb30af8d85043d1d55d502063e997293a8a0efa1c876003b822d7668a3c312e4a03c6a3a9b19ecae9b54d5030ce4e9a6f2a8a8a50464066715c0ea6f6ad6acf8059eca6f90aa492a3711f2cd02237cb13f5f117474f7f7c05f50a60f31447c79a68182c6a01188fae2712cbc8f020320ec7827b1b7f1e15956f48bef336d29265471af9575caaeb7da21a7ee3a7047ccb6164339e22490461a9b344aaae75b0bdbbfae171d1144812881da3a9cdd8497a4f781c32a6cd0c16f530997b596ae6b8046326d7ba8a9c401d08fe4ccd537c6522727891041de815dc2617caa01ba2a525c0f0ab29f5fde7caa64d4103156d5f141c95bb8634c8a3e2f126550204afe56cb6652adc9dc5711c33f7100f847af6ad294f483aa5bfb5bf417dc065baed3ab08cd3c20d26890be1dce1ed03456fb9d06ee19050ae7ed6222380e98178982ed686024032c3340a18f8f67ab7fdad0abc4fb9a0a502294904f10681349570823a3b8d1ebf4b91364ae7ae44995fa984a1cef33298b27a7d5ba576b24a946e9c3d3189da8e0bfe5ffe04bb8e74612719c0968dba6dc25206480ecde18b4d89b4fdb87c73ef9cbec35826b9f53e1e9516717f3c67478273feeed06a12aa6454897983b55e62e194c5818ba66c8c19d986f5ff4e62b97f41ba9269807abe781b80f3870098764f989c75b2eca8fd3a94c0c25ab2dfdb8fbc9794e187ff7ba95d3dc433d3a838855b3f2e099f2b41444545c3d35d39bc07e89a21be7fbe18723b78691ebd420d05bd6b7abce3679c5bf232b08e4a44c20cab11c6d5c38df34ccbe92db45adfe37718ac30ac8cf9a788a412eb72644b9e58084db4ce0aa41b342b93f4443149209b0eabd6bc80d995c073252df06d002181c4f3d73649910e55c1de2a058d180fcab3884e32cf0f923dcab2f20f8fa2aa955ae0d5d7772c4af41ec4e971cd7fcfd60cd99abe2420e860d6c18a08f543ccd277ca9c910293dd60e756fd06a71fa295c6bdac7e6413b71050f2545e8b2a4f7fd58a213e16d14cd91580296beccf4437273fb0b248dea987c33cb9403a0f53cb8643efaab48e3ba289a52036a3119584992c54d827b06bdf255007d1063b16de0a5df6f32ddd79382b8cab49a5b488577ead31b7bd53a5cf5f61a842747ebea281192f0ed07889ca1e3efa87cbf44551a785513281f13bf8413d92f951eb6956883e1c64e00f5f1ffc6c6c9004de587dc628155f89db35af6349ddd4416eb124466ef6bf8e9db8b5044ce9616b07ccc2f3d9973af94a172c2511ba65ced2cf01c3ad0dd5329b1f58a2d7c3aa0a2f0bb109f70cd2197e634a7eaf3d4c3e4fb055f5637de898c6ccc10e7c363ca01d2927f08a025d214c4bb7dcb8903115727a035ac6d1731473bb8f3993cac7e80b2b47c869dadd22d66601d1c8fefa957ec68ae48d8ab88eb9ae859e68c9d3af9eb8c7f0e25c43ebdd02e21760f09c06332c1d5fe6303470d1a8b8c3ae42c47a0e6a266b29d66957b9a7adf2439bed96f222533732083a867f943a6a858e4aff052f278ba4b9d14fb35427c4d38a54aeea979621cde044ad33a3cfbd54a2433a768b1c9ad201b858f8130c51797cd31b3915abc381c7cf172bc3f5360c7338a18aaa8c32b5d5979f8ed2a730b453a63278db415895dca286eb5024f52462ccf4e015796bae15c3210ef6a8549b5c2fce1808f05d4436a15e65b1fa74c855dc3178735e793670cada56379e7005ad603b16b3035130ec89e1f1ef58ba8dad23dfa9145712e0466cab6d4995cce05186fb75f53aadf1aedb1d916c6a6fa0b0e3ce7d55b288ca08ddc5290e165dd413f2c63a8fa645c0f64a95ea1b4fc9d2ae297f6491926488c6f88d05db1c2d761511fdeeb75a3be9cb976492f87b99bee29751d7aa316901edb597c5133c3aeb51e46f0b67d546660d8132349951e4931c2d58db635aea5619f320c5532dfb3a905d5eab41135693b9b8984c91da671e44948b126291c4ec2a01b76035f5b40ec64ebf5d13b0b8518b4e130d1679101", "00", 0, 684908530, 0, "1ce8f79fa501019854730f0d329ca1178c33bcc6270c451b6fa14d429ec973a1"], + ["a5da193f031115618f6fa2ccfaf87a51c9084bcb013a738bf9d041a8c95605c5d26b8d45810000000005536a6a6352930941dff1b62e780b96c53c3bd2561b12f5306ee4ff327a92aae3a6ad159d284a3d42220100000005ac516353ac92fc169e24f4e6527246a832e08af878cb8227289814b57955ed2d7143ee83a62b7a3a7d0200000000ffffffff039c6dc00200000000055365656551fbad4802000000000153f091c7050000000008ac52530063516351387bb76700", "0053", 1, -457316893, 1537743641, "d6ff6222776ddd7f64f46a0cc43f34e1492dbaa9e564e8f3f747a58eb49a6da0"], + ["940e020204e3e13366fdd525a1a836d15dd53e2357eed410d45f86daa1ad11d6fa5c4f6db90000000005635263536572a039ab10a12395d23bcbabb6671051acce104ac2a5ab762fc9335bfe7192ed4534f8cd010000000763536aac6a005170eae85b97f4c4be7efdc8b74f38693e530d0ecf210e118a1cf41d8b5440c10d179e8be402000000036aacacffffffff36daad406787d777a37befff3ecc645bbb11da6e9e4eeb52235fe7b4cabdda3e0100000005535351655272782a7c012cf1e4010000000001acec098fe70300000000000000007e45360400000000d7e5f0ed26b756402a135f6445bc8510fe19cf817f575c74a1cdeec93755a75b0ef88cacc22a4b3036a6b7e7d12e29f3489fb3d7958713275ea781e08b78e69f0a076f08d508e79d8ec694f0b53dbc3129fe118480f0296a494e81b6cd33a657000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008d87fd33159a0eed6187f939cc3f7052bac6b0fa09be42be51c11f3e633c473b97becf4d4a358f03fd1fad2120eca93ace554021f4584eb7aafb862c94445f3fb6d8388144bba14dda7007eef89a6b339a5eec107410eecf007f124970f6f44d3ae9828cc739be1899472984d5f822021019bb1e8b1ee901a6d56d48542a403b03286fb0cb844aa9e88b30c9f3db06dd4c619b8fcf7f636097070d213921bc98bb030f092ca27159c9c884a302a6d01b0d35523cab1a78729d206dc4ad8b3083ede80a03480be7c214b9a17e5a700a4c1d4f5eb72e574e736a4de66e3261222ed15d40974557510142023729e6bcf52a2d654b325cd4053ce78e1e5e8ba2607966f765021ac7d1f72363b98807cdef2ecc094bfa943ec0061a1db15ea7a2d8126da19e1d0202f728736ceb42c6af173ec06992dfbcf43ddbdba1c7368ad62de102afc35cca0307e143686afe413e8a4a943544207dfec7bb86e6ec02f000196dfc270ce20fe1031e41b570aea3140fbc553c58fdc49f17ad0fe764bd82f2d719a995243855e31402204a62e924adc8f835b8a4d833d6ddc7cc89142bc4e6d63871b2f38f5f0f7e1b2d877abc1c468f4199f64c964046f679595727a7e991947c5259d47fe714ed6d4e2327ac7a485f56a460b8d78af776020f9a41a8a7802ecee1ac4e4386de3b60c8fdd06700c8b973fa5385b8403f845712f16ae5d12a5f681da1cfc272bb0e1d25e79f21111ddde5bfe47670eb4ce081b14a88e579a810c3374bf97099ecf1fbf81d38a64ebe6629178800d32a004762316eda403d776bc49abaf6e3ba11f5ba916b44608c1f0ba9f1eab3e52bfe1b3a71e3972aa07a2677e62722d07905ca6de849655a5129fd364bede1b7fc649df064d8dc2fa5ed7878a06ae8049df33289514ce6a425cdc923448ea3f09b686932822106849bcda61a2436948f0b451a5176f6060314e7f6e3623f4da9b923adbdb110d9e0781c5cf0b179776c9a48825f4093771ef1b880c62cc00f23f9ddba37fa53c0f5cdf6b42523414c2006a5e887e5766d7bf0a2246d6dc06ee37f54d446acb873488ccc5c7c6fa817658dcd2438a5162ce3473b644fc0a245531307e6306ace128a90b0694f4988214e24be2515d6ef2b0f29d19b287176bb53a94bea9e0e30d687e0844d0b5df6260cf831dcf8d843a85a9b875de89d50ab45e68e2e370d0dd3a832e8782c10748d12557ae4001d87c534743fd8c4d4dade9e04e9a5a709b7ad81b2fd472285a0dccfce4bf28cec32dc088aff3c96d6a3b6ae7d134a95a71461aa6602626110eda7df0755d5d9c30af6288ba80926d174f696dd5bf4ae29a88464bca17e80db127f4e66deabc638e713c21acaa2da3edc884933a819806bb8b6b0e529a2b84ec22d39e9b8fb92e0396eb3fdd492d03dfc6f4d4769f3ebb76fa580561d188a626ad34aac613517d16ec505385fbfc1ffa44e5070c9dff77105468a81bd3586a2eb3c8bc251f244f49976ed7ef01ea2e08db753800fcae1203025668ea2a18ed31733359da0409584944751948e35cb6e16ac104818e9141f513f7c7cab83d2f925b529d1bfd176b0083defd04511a9fc1774fbfc4bfdf1c2224c3b8e1ae1f6dcc37cce7b899952318fd2b271afc65f90f2e7b8743fed051872dbdd6784f38c7249819a5cb91871e0822cafb20a5c429e4089b123b23392bbfbdf2ec309419ad8131a4974b9573f50b09c24e5c1b1434a38c86b4e6efdb71884de53a69678078dac964bd94d1e781a129a4a4ac37b74c032050700a34e6458d78f2ce8b941effc03a6d34e28ea4a05a51f913ee5b686637dd236a807d7bf0b98765c9b711d2604333564637ad13d1f1cf770b27e18b333ebc91f1927a68412dc42479e665cd084986d556e8c60b6a1b7839a435dfe228c30757cfc8495de15086c8567f7e0ea4fac2d0a9246236acfdb991048bff0f82b77d64ea7e288d533a64c2e0e65270e4b333f60c10a2ddaea2528663d7fd603cbfc53176eefaa3ae5d0af5599941e5dd0517a79434a54517ebc5f5db234e2c783992e950f81090eb164cb099674c05aea81cdb0de6a66c2c2c8c13db771a3ea56ea004b2f922650a103685a47713397c85581136a569d03483178e9d5f8a067d9849598b6059070b632d903eb5bc30dba990e602d8605d561cb8df26edcfa465f3724c54041dc4562bbcf02d8024bbf3e194ff1e88794a62f457cd502bedf7ff16132080783e328fa44c3da6e4477cabc986de88ac9038c7161bef974cae8555b5dcc269ef5665c9b66d30795000000000000000000000000008569c1a213bc410d98a9e32024e38e30b778a649770a4773240baa96d5709c8532105e215567e6f9bc3ee53a8a0986b13731dd6b2052715d005f55b8c7cdcd460e002da843d435cee4b45544a69f3783e7fb3d760caca0cb102870f29cde5a0800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a74d56b508e168cefcb78a83c8530389c239cefe1b8646a1e5682e7187546f798bb36e99d3021d3dc9ffa151aee0f065f2096e4cb08482ba3781526bbd3b07ba1aff20cacac2d46f6828a409be01819657e499bd5c5e7b90901b7ee7c90ae92aff6ff610cea6b3f41638a5fd200c3fe6a1e78a80aa237170b81fe2122fc910d10224006d8b7f7533225b8d5417597280ae8a5cb580f75fc8a5a57c964c969d1edc030a9c2cae9073f15f93101029fdf60d066d04de3a412e0614e17aefa9b3655ba00b001765ac06ec1dc51a3bb1d9abbdf5016f010a1ad1d9372c62d7cbdf5b38c0e0b420bbbcc8f779d83c8ea40ac6ddddee9db8e867c40bfa95d5a79df1344b896e0203406e80a1bd363be1a4e994898391b8bf50f11b470bc2d34b0468120593d8b60229fa482f88ccf4a24e36a58f51dc5f1a973dc965bc94dc0353a2cb071ca91623021916665c5ae1b38abe5b1543ecdb3b0e7336eb0122a8b4b18185b4ad0e3a92c1032c45972d595c134fdbf328cb7adb18355a2b4d9511d3dab8d3bc390d26bb4457022e64875d93a8bef9627c4899246214b47c6a389a51d38f5ef51f8716b97d76e4f9a3cc1f33f910318070bc9ca9230e7acb6c5ef841afd88a5183019ebe5767fee15338cb59acbf8d18766c5d2973dabf1f9076b9a856abc67405f90bb88cf18a17ae3d83d26635303ee39606cf531d65eb07d5361f27f3d7c92f0c4f1230f34f7eb4b7b4a52b0b21b3ce63083df1fad9344095b7b1d0cc2ff7681be9c40135eaed51d424d915a6cadc26cdf602dd16cda2045f6af439cefd7a9085ba60a6cab3e0d7df01b7e490200a9a39b47ce56e996594e00ad874236324671c4b8b6a7ae27b255ec7dc09e37d6bfbf2abfc0d3255bccebdfaa0205118a9a6c25c64e21dd89421d5c29171a3a38f833699b0b63567a57c85a4420443611c59a7b6fbe5f82485930cd96ee8910ff226fea22cda583f44e1c00345815ea5e76298f0c7b596234ee15581ecc8a1e76285d82807abab89be805db63dc838ef070df1545d2b0ad431d0b849c71a826e8bc2b262e8dd25b1fdc7504a4b50336d34ccc4684f59e2f440f86c80dd7adaec5d95166893f8b8abd3bf06f4796a8945684033388db661e6d10384d4a2f9503529bdf59cef04b628ddcee28b7e928356e2327d5661d5023392e915ae3b18324591bbc208322c0362a9a58af52348276fdc5373a77a8b62bf9d4e1d01ba6a5089bd01c21eb703433bd944a2fc8fe39a5258ad1d2222fc52af17e3e9ab44f1bf0beded402541fd581ad4070ee9f7098d2c750eee5cb7beaec21c2ebea49c1ff16e108eb158dd72cf2f51a57c763b90f8c6c9b225d47b3656af71d6d8a2c064b0c63fc4c099a7f43a3404fdc1342e9879ef4c5c67f3af3d756f362a9a82873da8c22ec4d12a97d77e767bb22c9d59539f5cabcbfacb0f986136d77997d6d9ce8bf1bcbaf7b118b41abbf9ce0306a5a60b4fcf14b45e9de463a7fbca76ccd75c722371642c6b8dd9df1b31930b2b1a83c642a85a9553c8594e615bdf755ef4788cdfa5524ce219003a0a881b64e52b38f5c9a5312d29735062432d72670d6538649244b86fa33edb4ecaccefb129371c781f020f72caf14611ced53e96509aad8de4c2f2899dc2a14940da813f25415a34c4680d05c943877cbcec008e4a051fb99a287e4e935d3c3a456ede172497ae73df66910936399ab0bb1c45ff37b47b0e180eb53755babbe1f6b053e95b55e7c0f987d2ae7a924a8a5827bd3f3220ece463345c9b29d8281eba2fd06ba7bff11ad0b38a072b2d6b862a11f26c19f68f6741b5942ed1ec769a72ddf3eeb8fe48ec57b1ebfe167b9561a1b66843eb8f41dd87fdf9571397726c7e6c61591e5246d4b90644c099ebf4f9a509f255b573c7223e3e4cef99c16b7d4d6f34753886af2549b19976b0ceebaf3034ec9ff61aa1c6a30184432ad1d65961841934f10a52026887bc65b31f3cda6917838d61bee77dfdcf03ddb6e741f94e13e412ba97419fd7f565efaf681d5040525fa2e698a6a666b605d868c13fe674939d874340baa03c017f4486b349a7af5a16bd7612f958f15ea3686d3bdaba1e61d39277b4bdfe9512b9d15e2db36e263ff18362a6f5292f9f592740d3b64feb35c8ce3c27164514bfa235f808dd7f59605671ee85553acb54eea290891884c9ab53dc313ef7654b54f9c1a2bcf6d7fd8be5e530234cb591e32098a4cdcab9263308d3fd7847825b709349e9a080945fc0e13faef356d09934ca659523664a455cd000000000000000000f25a80300000000b3fedfea286389db922979700ad24edfb7b9612bc06facf199bc25ab330f2e8ac23f247e182b8762e33b11b204724c1802ab7dbada7765b5c32e295709a58f8ab73ca0af9cdef5f083c33b2a00eacf92afee351b9bcae800e7d7637cbab7193200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d8b21b69f3a61dd8f0654ab29564056a11940feb3351bb9c2bc384e22caa0d9f7a95a7647fefadce3a1cf31e8ff68dfb46bf1b4a2bea3d3e3aefa10289ed3571f18984fff30b1ccf35d6ab8402206a5a647fb4c68866d85c32602e442c7019ad35309b138a847bc794cd758497ed0cd4405bbc7ec916eedbd57c70c5a30bfc000300aa55e6127764c562524b81bc99dbf7922df082ae004f114b3df37ea16046fc031dc98766cf29aff8fa4ffe9737cd104dbe2ed212e7a5105e8f3f18584cbff2a20b01276f8b57a8ba9fd5d8a883ec588e8a05bf5c9c07b74eafeca93ad69d198c508ce40884214a2014934d5a3367026a560e982865fd966836d90fff725e11b1f80215466946d2204f3c4a76e5178e5269e14fb2ea11ed1bb1184f35e623bbcfe91903009093e9ea5673ca444beffd68dca8a1d5270852734d770dc77a41e35ccaf23c021e64e0aeedc2eaeee798e34b92346a627904bd2dfa526dffb53f77309f3da6d103014a67f974a694c0486bd2119df6312b93d25e8ff5fabfc1d3dcffcfa7ebba3302224ab2fc18f968d522e05a1969699ccec618e78e9907a5cf18f1fdea706be2f70c315c66e2484b1edc70960366cafb3390e750976186ccfc79d3493b57364cc3ae0a4a09d10c97c3dd6d1af342473ef011904aee467b65b61f72f2bb3f34d99db2d677c2764bf063d0ba3dd00c6b1cc0b521a14f28ccda6cd22d4bd146f95d61cb997042d25d19c012c4bb5235e1bfd249099e4d3a6fcdd6ebc32db8acf44099fd28cc1560f165e2d952a01e2f4c12e5689ea70276d9315ec683f72f5faaae8c3cee6a18d674f63600d810d7ab52ea7f578485b14e88c5666bc5e4ccb8cd6aa34c21787f4703e7e485698cd39e6ab07d3e2fe926d40bab502217eec1991580dd58914c205f667560a5eda86724ed87a53e716a1d4a9e56bcce8019f405992e0bd7033bde5d4851726f77be9456f76b74720e7c59c958a4228bd577b5ccfdeaa29103a9074e690b70be1eb7350b1f97089300924370f5f18eef4b58552f49d49a40695c106cb30803aa32b66207a745c388a8f2963f12323b00d5a27dc3cbff9c9a9fae49aafb9479b87d845a889705b8944d28aca4f8550ecb5efabb6b08771566bcf73be4089b469e9aea15c88351be013605ff650d4b0caf100ed47e7f205fb84b82baed577c429453eb6a2fe1d9562bf4bb8f85dc670e9baa9105681ca38ca61d8d84b25f9b28bb4406947b0c55b93a6bcdbd857eeef774f1faa8f6a21dbaa396c3b3f654c4995a6ce7875cac62e6ab898470a9434b7396b2865c06a8ffb303e86a18dddc98dffa5e459221d9fb49ade4be4507c5ecd3df0f9b523380ea9c4f06337fa56b120d192942ad6dfa1cd587b70919b9a114963a82e9b2a0db86674ca10d904edfb7735d16da72f49db1b3f7c1f0bf2d516379193c54024a7255c96605e2bde9c9c1ec93fe72408d264e82e73a143632d8337966e519014b6fcaf4f9402d1d8b0292968bcb52a2f63dab964f2b0ecf7bfdd8c04fb429ca3b31abdbefd2e4998fc0e62be117b3803e393ecc8a0b9192a0739cda7331a4c2550abb093f2a66f38cc89f4f28bb7c48992ed7d343517abcf53d2d825fecfd80ccc44e70b2fbcfa092ccf8bf13a4febd41d4483783ff6e44d42a2670eb777ab4f8026ef02c494d3f5f46b181659104b027426275638d3d51aa90704008eb649035dbc654d5421288e790b06fc9c3e62c14112ad5d1f506f8fe7fc7b80e9f132812eeb66480a0417483012a82a9eebb8f221183208bbd3a0b3292816deff3b9a5ec743a575d7a4e2942b0bcddc2dcd842ccc9b8d056f347d9f984d87a3a5c100b1ce920ad0759c3ae18979c669464dcc730455c2171827519124e59fdfcd194ba0b98461d62e875a5927157689438191b8e06b8cdff48c87c128d2315183c20ace5023853bc22f0dce096ff08ce88edd8d78f4015c8683f671124b7250c29559b8f7640d768df676bea5c36e0ea3335faef1ec31389cd92b86c76b2b11f8e04be8eb8094b493460765359cd4ae406790d9a683255fc17d6d0bcb1567a77cc720a89ed4673b3fe062f753cd199e0e9a151b89a02e4efe488df6115304fb86f4b98f96edbe4fa9be99eb210145858a3de0f6b098cc3b4e83d4d32f29c0d630146718955c09f0606044fd4af63cf1e710a2d5bcc38423b1ee3277105b3f5a1b4e6973c6c54e2cbe57b06572ce9cc16050ccd61d4c7a6dd17944870a790955699fa7dff1a9dab0f2666d4355e36418811077ed58b277df331197985ada234496e7718407f6182481b04514b96af42032bd616ec771622fd9aa736151aadbf31a60894bb6360ad032d068507d5c3f89294d5a971a55e32e1a849de48f30d6976c5da8878c0340cd64a381e75c654da92d8ada1329769c6860d", "52525363005200", 3, 1997281448, 1537743641, "edbea03fc1e415baafe8cd3a8a7064066e1ae1f873100b3bed5bf0574fbd9172"], + ["030000807082c40303b1289c44d309d8a31db47e3491e5ffdb62817180d684d7a94e213fe2b6d0c437000000000851ac65ac00ac5300ffffffff5ff6a9c53f57ffcfe2a08eba7ac1c687d2189ea728fd95bbd333789bb755a06c02000000046353acac7fd25c34cd127d41936d8bc146d495ee7a7d3e0f63ada3373539f25b860fe65575c19a4d020000000800acac6aac525351ffffffff03511ecb050000000000cc52260100000000004c0d7002000000000853636a6363516a00000000007953db28010000000000000000f6ad7500000000008129fd51040ae7fb6a8e3ce32cdc3a8c7ac0c17443b7ca434397e6995c127fc961390a08214709cb48ac838d0b2c9688e9db3830c2e287f1639a02fa045321d461857d6e17770f88953ee3daa946e11d5d609a14338ac63029d229d40492a7ae000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d736d4926c1d552f3a110c70ba19ee5683004d1cafd3db61046dbdc975dba7aafba7eeb5553a2e54757c8051394b51374fa09fdee4d8b7a56f4d9f922385b18b50e411cea57ef85273c93eda71930ef004a330d9cc9df5e75764d32ebbad9514b8dab0a9738078fb575154e80e74cc151c75f98d97ee5bfa32b74fe2ff28b2b02300085c4e5187e1dbb38ebe0c17dd97978b9d67cea57696b3c7e5fe26691dfee020907acbf609827fc732ab62c8c4333e5f07c559caffab0ead3ea99c8fc5919240a082c19dcb81dd215c453a28f74ea77adca2dc203887c1af6f63724979f09bef35a858da4eb026e639d1021aaec43ccd5c90a7e36e2e125dfca88c1c1c6ee653102256898d00ff2f9978f81a37bab54587791ea8c739f5355f0cf3e42b75cd8a01c02018f0eec96b7909f60fcab40e6fbae71a958746f00ae74c08519d8aac8482f87022a5fa836991084fa451284e50120c0e7c0353770d786961c3083e1ffe63b5ab50228e90834d9c4827f29564a33fe6a20a4e2daab6f54fa9cbf84763395342c4070022a7b37ca6d79cc3165e66eb77438cf8ded51024b1944f2f799df5888403c9397caaa6f4db823b9e29809cce99b6c94b41a3a63f9e2b0de93bfe8b6dced556c078ac1f8490c41dfdccec0b5aa2687f3fedc1161842fc9394c3e5f1887f796228d5e2efdd4f8c7ec4118f23184ccc173214cc4baa18661c8b8b59e0ef0c1020840a696b1a14b601d9c0401ad17aa6095b2855b9686b961659e6cdaa0235025f324369c0cfb40d58c5d6913e3456557060fbffb5afa39689aa7965eb528ba794b5840aacc3287de34d0c01ed05bf8dd5942fb11bf9e6b9c931c5836a586115a5bfb9c4798ef645bda951b02f5e7b000115ac7dadd1c7a3fbd5cbe596485eda03b21bf384ad482642697762a5e60cd5548e2e1214d32b2e663b683a2eb651da98615e15a3342ccbd5810414796e941d41fec2ade7be7700e9401bcf4ba9efe6e91f5c48774193e2b3ae0375b5e62729ab26ab1fa2febccaeb101b0a722b334a2c54f01df7cca0262f6a88e772262a7f443ac60f416495665db7de1095a550ae7c1119a1764cea373b80af830bc761979bd316dc4b0c638e9348e31acc582ae2478775b25a2d1d7c395cfdcaa5dc2d0d4d27aee8262501889b168681b80bc39d442b52f335b1355e1fcf9a2f6e7e6fd3ce738fcbf6263f2505ead17261ee17d12d135bdcad9be1716955316c098377c2b1264acf510cd7cd5b0efcb2db40685491d65f6cd008f609740806dee733b8d4efe6a8674bc06716d1b907bffb1d90cd3582dc6e2e1c0ce7a8915593a382aa0669f9702250158aad77a43b160eadae4b86d218714a3033bcb2e535311bcd29b99546e871a2d64381d13c98a55099e5b8f22e8614c74944a0dbe46a9693b65779c7ed608836a63de3a503e60904a851851284fd262b751ea22f38355c3a2060de608b14c9742986f26d7c159a4fe0d44e3b8f11c96fa2375bd6ed52d9d3a1a738bea7dd36dca90c37272733b9144d9e1e041cc5e1d45dbb7a4240c0b4d76778076797fa861cb71f18060144553da2d0dbf3a0017ded4115ddf324876d18a31ec7fdaec805d11df5d5e523cfc4f3401c12e8cdd517f128b83cba7d79edc7533cb0572710cd450db155a950bfc2c08189f3efdaeac32e1737e10acefbe82266507f436c6c8bc89cb79095599e0987e744935844fb858efcb2813f5b5f3b5be03d75a94aae7791ca95cd51d4bad40ec4ada4e212c76e8e9a6f4dfbf63b2a6d715ac8cc3420af06b6a3efa85f0cb66e3c2ea82ca49ffc42ae1b9317f8d16726c5f125a3fcf01b8e7b6059fab4c16b7d08cbd4891f0eb6d6d1f386cec73a801be41de5715b1aadb582602e1a3d7c2d2ed61dd8337bd6a7947d84b756d4a1f342f16d4da7e06d549fc04417d4fc6a507895075e4bd32e76476a73691e8bf7717a93b0a7335c67461214e3e7cd2cc2fe7d880dfba35c586549568eaf08d584a368644aab8c56904d26d663731d88a29ea77e067387bb2a9e49632b74d29f770ab4a29b71e11e11cc87e8ebca4589893551e06e5a1d5e65df4f6a899536957afae85d853f0ffca4cf9937c156cd357f14c30be7e67938d985c1612212549861e4bb66819e94177874ea4ffa6387f2ef8ffaa3ad43cd6645f625c4dd99d4b99821fd5725de0aeac499682356a13040be2199e8f40923357363d8ad321e9a7ca366304b95e36f053162272fdbea787658afa6c0532cb71fbb7134080496c10aeae4aeaa440c32e6c90662fc76f367e1657eb15e0ed59efa2948fc03ba5dc909e1de132a1f92f013a8624f93ea161b8b2336ff891209d466e6208331dfff44c29f93270dbcfde29da5f3bf2e6c92155961ad32a7e3688a3449944da6021604c0d1fc5c13813a563903d0f", "6a51656a00ac", 2, -407502766, 0, "3d25e7a73dadb321b621aac4d41ea3a8cf7a4f52c74227a62c5b9336420526be"], + ["030000807082c40303f1fd691987043ac1067f92c7b029519aad8f689cf8ac03e2ff38026cd824dbd7000000000553000052acffffffffae1ea23993888567f03b653185919a1efced3d4bea047f185fed38dc7bf2747801000000056a53acac632df04c43ca0f96f96f692632cfb9c00934557b9b5734c00344b849d0b32b3b74d87125f4020000000300516affffffff0158257003000000000851636551ac6552519f0b7c0b000000000100000000000000009837410500000000e66ebb1d01c96770feecb1aa8382dadfd4dfc725c52fcedabfd74acd8f48a33105e6040e64ffc017ca2b192e767b965c0bf0287179fc6a8bd1ba6ca266a698272aff7ad092cb0373d604c00abc356157cfe5957090c26b4801a7a9ac7669616d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fba79d09131c6cf6e4fc1b4b8cd68b6cd8993f9ede9ea37e94533c5d1e283e61a44d57e59d0f8242eb294c39a7a1a384163b51464fbf0f54a3b485d238959ffafedba2960cf22fe3f8e47783b99658e0b8cd00b05c3d5005e34f2455d8777d9178b329d6a6b31f75a593f99e426b62e29edf95646f158cb5f20ccf3d1fca6b210200257f31face31de5124d1d964d08bbfdefc752e1ce6e09999759eb1e1c425bc031ba0297c66ae298b4647c6504c1b758e29478cc5a9b0512f0d39dfa5fd58880c0a038f38243c5828a73e77f9ba3f3f20b3c226b7bf986bf992ee3651edef449c6a2e1529fecb1ae95b2050ebf8a60448a299b0909d584156c9b5e0416ad1f2070b0301c420cc5468ad86d721b652f488cbecf713d190b50458d44ff04ee4729f08950321a6bf266b576a082a3bcb5a9d428ef79618bf3ed37945c35b8cf0633451edef0325cfbe2063bd8081d3f9af04c4a43471239eaa7dfaac58aecf2b987560fc9acc032edfa3995d3a7786bdcd00cb65b7f327c94b34ae996e9bf7b597e2585027ba7e02211572f67760d3b3ffff9cb1e803b93f5ec050d4dea2f8849300dd289ef920cebcde5aa4daa84de13f262572916dfeae7e1c5140c8d532b0348c580185462173d5c6710afc6795faada88ce6e730aa084b38d0d35aec5ee5edf35e5553ca861d88d0531b6f69b9314a9792f7869022adf502c6673f2f7c575baf92f858fc3efee118ff23f49b7c47f6cd44bf872012ed3a4b8b750fa09cfac7f48233e78540a9ea929b4f036a353592a7e71947fc9b36d870006da9f395547d0d04f334b84a7c1fd7131928ec4a9a297c182f7f67a44cf9f7e1a089f7a475f663b06ed49b600920ee925cd101dcc6aaa8d28388bf461d98ebda245884e5a563fd440ad88b13fc4e62a51a24776193e2b944a9ec4d1ff88581579726554ff8e9b49520ceafe3e6726d7bbea3298fe1b93ba92022a82f7fad3dc4ae2795a920a6d676216a91a842a51fc16a5d1ae92e0d289e6c0d4db8974f8338b2982214926ae1820b72dbbcd184bf9616e02a33079162cd3acfb80e19b9e5464a0c47573480e2b2356ef829a81a38016197a118aa3615faf8d4725ed9e7039896d47a25fd9072e28a40a3c79698ce7aba7c2c8b48e8c803cfe70f307b190656039661ff4800cb2d6cd74243c04b42e2d8a94f305873bae5bb2fa351a9ed98577bdc249d9441ede0222b2ca34f2139fabecc3f84ccdbfe4c5cfdff9ea09b6ad1b732836a300ee2fb32c3b74357e321b8d166dd5d75d2d5893c0f07b19840a2580e322c3600f78f6303e31877958f2fc8767759ee80221ca5ee28859441efff056de197a36b3a1b95fe97167554f704e47d652e3d235a1c6038f2aff73f4f09de7352a6c990ee9e48c07f8386e538d5bb53a2e36f650c45e67da68a47d140d25d4ace10b0659e2b58a87bb9f22ca157f2b0e04a2c8d9db1ab3dd063228eaab5a17fbd6aeaec4b7a309754bb3e6d1a6c1029e6707344fc43c0f7c6ed1f39986fdf2c738267aa8edd16b216e62e8160bb9047efc524e856d63fd9d0acef2ec7d3ac3456c7a285add9a410f7fe7f132fb1d1bf2c34175fab375c16643af1c02e8e499f149456721c15167803b06c8d134c0784ee798ebff83dac102dfa0786f4be8dc3ae439f6d51bb744eba447298266ccfcbc9ba5c97184fb8ea6820f48ff91d0c59124155f8e1c01f2b7c8412cb8c602a19fa135e61741e0a671ef76977a8a03e3d7631977e566a7468acefda7f498de5a12fa39177d284fd383d4d656245729131e3cfd8ee44d1f93cd2bbc3f6fe4ec849952acad8028eb19a6bdbbf31e53d0c1ae66a40f69d6c94ba964a78e7f4c5cd2ed04fb37b33df923d6837d55ef741091034c7a1680adf37658382d5658df531222c92093a078ab2f1d5d07f71668140773a98ffdda40d08d08099886fa3ce93b35523894fad6cbcbc4ab2120154410b91cbb00487e8d8b95017a07c078614bae9f235897ed9d22851d301c5d6bb4d6b50f6fce9e6c752ca4ea72e36e06f1b004828bf7c42831a1c963f4cd4caca8b08200e609be1c58db846b85b61d6abf4746bde8ba040b490ba4f1176ce7189cb5133a33cca4ccec80a88ec5b7795be0f12bed344f1949bfbd299ac580b4aa150de155d081402be2c8b3ff67b2a1c804f44c7e0fecdeb137a2a39007d2b0001e10c64764afc2dabb3e47db6afdb4c7415bc8c73a447c5eae0dfc013bfb7d9f584bbd17e8fc4aa73f8b5a07b1d606540ec487d581e41d02ff6e71166552ea010fe7f3c53ee23aeb5d20957829497dca209153c5827c53f664117dbcccf6b74cc5225aacc241b72677e0681eed7ae643103129dbf1e93e913e861d7ae43c349bf199152bef0f0b78e52adf23c130c53d3003051aa32c1d0a9d76ba54f282199dd07", "ac636365ac636352", 2, -1366046387, 0, "de558e02c9d5aa15aca7ef033db48d75ed363cfbcf140bf079be804f82ddeb81"], + ["e62b8e7c0404dc6ccd6e472322b3e5456ce95cc0d5036f64828ec12b0418d7596e3201368e02000000046a6a52acffffffff131ea6f6a2f0b70ce2e7385d80bdfeb1d66b0d9641ace852c898be4fee0023980200000003516551ffffffffb55e99aa607f752359d99bfad854214896ce82290f49e67c143b045bcf3aa66f00000000056a525251535a8782e059757f754fdbd858600a0c39992736742f51afb59dcf350dd0866ba72e65d2630100000007526563536551acb5dd8b100135acb205000000000851635365526a00ac0000000003000000000000000076b9eb0000000000e5351f6136f1d26d91b8c875e1697716770769082ac6d4ad941c06475777878993082ac91dedbd1089a60ef711334e39f28b6b77715becc7e111f40f056ebbd5d48c2c8502edd6784c1ddfa1e455752a0bb3f7545dd7b944932e4f46ea3c20b10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032e34af2161d8f134b51ee041ff1c3feae2fa15d188ed45b6443695c85bcd80d0aa2698fd2695daf9182fbd99ac2afdf26347ee8ad92bef6ce136cb4252286aa543efddab2c5a1988ed566fd5b22d79a80f4264e9f0fdc9951f14ac7e63cb901abe5e52cd29d88257588ce8093faba3b39235a8e098ef7865b67e0760e046a20032bfcfc762471e90f1c84d1ec8f43bd6c9c295badcf4b4abd6c2ead376cff77930200af33aaaec1f7a5e13a0427884b891b46124e3dc082afb61cddcc12b807d3c60a03d286dba1cdaac3266914f56f23a43568f33cf2833c9e0a9cdc3153fec679f9b2da37863cd9c2db8cb6c83c6b8d76c1c259a99a7b5a3b2662462c50ad85291d020957c95f41f7862627650f5c34d51e142062a9915f045489a1306b2b31567a330311e534c56cb0454ba81c3181ed242ef188d648e01f829af4517d02900afa1409021d5936ad9701974c6f212520eb1c256bfb245644f46a56a8f2fcce3c2e86cef703064dd4720268e2d12fa6d0a20ff23d0360bc0c9415b96ac3de92eb2d3cef849c021596ba19c5b683bb49f98ce0fe0ee56ee79eca5824f1f8b0265c2a730865abd9a310e9958a2d8e1bd3189ce0cdf655f6c2ee6d50c6489143c92e866869b850d9970ba6f2c9cda5d44d9b4a5361d88be2401fa1d6b8822960af7bdf0dba67ea198bc6e30641605bf654ebcdbd2a9daf6d1d66034fd78b2e596dff55ebca318e26b56e37ab05c2d6e8d76ae75c7efe1805bc9be3cd8adef6faa2cf98b39225710cb2b37bbc5c0e2f7ae36091ab4fee06231388f4ee7840f2dfe79aa6737d2764953acb098f499ee51ac26fae9f2628d20b0228a65ca40655b2191854333922846c71e206a58df93935e430013f96fde8747506a1781fe3bb1833f214943ac7b3c2ff87294e20d5665ed01844446c62ddd0457e3c701898a3652cf06b1a210f613b69b7cdff3e12cc476ee4906282597eb767dcd6169dba312ffe351953dcd3dc4acc29ef4a8bc4bb275c430772d7513b8ab7762d1d250043a017b2319231c85c38553fd82ade74f89fb332d3f377d10114474edbcc5fee77689bfb2dcf7d164b2022cd37a86010690c858e17e7344e189656227b01b6f00b593b248afd0edc4c55836ed59cc000172fa490c4d0ab99e823d4c48092d6cb6c08a7ee8d192b5de653a35bd48ce220a9b0fd655f148dfb0cc1c436d552ff6b3b95576536a1e4b29a8bb6fc8a0a6cbba70dba5b9783fd72e1b0c879335b8787dd1138cf257f70c196972e44f6cd5e48dddfa316d9c48dd2a039d2828579bdb23171f5fe28d5da294bc1869f38dac30cc2e2937c82a0a47686a95e58d2a50a432841feee2bad454147e378163a12a5e490ef6801286759987419c490c0a06394896c24ddbf3588a7557985946f37e48ceed7976c4d3b0d860baa20a5b40e071b174f1b25423a19025ba9631429b566810aebaafdcf3832e6c5538751c164c031efa75a1f144b59278282c674316fdaaa3a1157b120e3c52ec310ead28e6c89b4f5573b84fba6f9a15a8465df90d8af08054658d037b99fff5397f80a2c206873512c3a2913b7791b3c6a3353fd68bbbdfa56a1f11294988415e01090e4fd4e5cf0ff13f0503811fa57e283197c0166c3c28d7dc38284822837800cc353a1f858f82066c13b6cb30ddb732517e04729ea5cf3160de7f89e2dd3b7c309269d1864faef6dea213a19465962efe4d2923a5b31c112778415cd9839219db6e193807af7e0885e374f4520a728b1ba8eaaf6282ba3feb595b4a155818c8d1d1651dc374454c17e221b5684adf19b58dd31050ada81e125936d952a34c47b9595af43cf5d140403ec8d7fea86a3e4b81787ccfb5b28ee91cc5db85c62735ad26b73d46f82eaa63b74313e294bb452b22acdbc4da4cd44e58300b14af07c6aefecdc8b7e253ef18a88223b92b02a147641a1371091a021b9715dd43bcfc4af132456177864ad877fa368cb4b88945afb22c6bdd38f93065d3ea7ff083e4f9d30d7b9c75d33681aee4c0f2a4dde72a5d102cf6fb3ee70726f15827a2ae5acc2eebbd85d1a56fa9171fcb8fca9b1aac7da643282868b57f323ecd990fd01cd5e4b2c9e49549a241699aa53e4b6299e1754113cf0364dc6877cf16648ef7ef952c010db0be4fe6cfc61c7639abe7c990c8cda6dd9f6ccc96e2e292185471b3894b0cd76a5472eeb5161de04791d88f50788dd40ede308f4d3d1c60bcc055393426ecdd0636e1fc5ac2216e78ce908c371bde02221b8512408e093f14abca961a1f40000000000000000520eb70200000000b0e9322ffffe4cd5bb69c06432fe84b16d6d310b714273a0589242a996d8fda771594a222cde61ebc8f8a7135d7313fdf1ac6f4b1d60214104d18430bf7c861224c2d58387da540726a3ce82aa28310feb9d260f7c9f3e72924eec66642bae56000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a187e274c65e8c71927e74f45fc163b582df56cc84faa66fc1cb162c6455e6689d16d6ddcb7c172475849d8fb9785c289cb2677718d6f07efe0af0594f18b1a0b7e142db972c1c9b87b21902ceeedc2127bbbcfd3ca9cadce074365b08eeea6c010402b98a2274386db8d5d06d62ba05921fe1770c6f448e7e8dbb4b4c66344021e208bf9dbe008878e4c561b81ec833b0c7fea8ede95a68b1d9206fea128933b021280dc32fb7a62cdd12e99612b6b38838180f3803b779c8cd602060931addbfe0a002136ab428e891d6318ba2e1fe5454096c2f2cc55072528e5421b0c5b539ade649fb5a6fa104fa5e7f91d02c0574ab32ea5b658431313ce4843b78797797cb203300bccd912c74d24201b50ba13444786eb8b481d5e963225f17dea9301424add031f6d54a62dede0f33b4794f83a8700a0f44f5072a8045a81449ef98606f19d0102255c795df750bbcb2e109701ab38acad5b6b93b8d3de56a9e609a44de93317710222d805255c0dc56c8ac573f874b2813708266d599d9f532f3f0107e00b71894f0208d3e2ef6f3cd3fda75d6ecbcfe698b7f2ba4b0bdc2cab37c89751f9a9d1bc2bcbec9e598b031701c2a4fe5b2d7730cdf035e5314e358334876448cdede4f089bf1979257ebe5a46bdd54878f2524ab4ca72080057a901ed4d726e379ef7f6071750d2eaefa8fded5dc8d208392bf0be1a9a7a98f08cdc5c3f505f8b0770cb467d13133e370c1096de923d3b52310c3fe9b29ac525a20d76040567cd0523dcbd9805f498fd292c189d2069fd3f0680fbde688b33a1adcce76e117cb87799f4add55486ec9c008577f2f20d6f481f298f92b24e352cbc1f764115817b5a11d2aa52b0c60c20fa3c86714798b8633505490bc938098391b800bc322b1ae78fba3e74ee66d4a0fa4b280b3f16084226277788f5a2db5485868cb8458450470967e606f428bd7127ec8b87752421b7578a680dd01d2e53ce3b2a7e8df8d84868d406cd006ffdeac1809c5842427317c5958220d152559da0beb5e3f71c97e448c4baaebfa2ce0f6e11a3997d8d6341c7268e97f3071b4239377fe1c4479045b59bb6076912e021e4ac14777e607b6171805800aa876118eaf7e0f6d820f0108392e0ef8eb416b80566e51e2e2cfde798aea6ddfe6b3cd57ae09bc3eb01e9b0766c37f31b1fbb86dc7d8ed47aa92c0db17b462189599e517a9d690e39567e9aa110d7bad12e02ddd410b91b475ecb4af65c32e592ff128ce52790b9f4b624f025bd36d12d7b6728f05ec94aa31a2c5d4a2739dfe5b7aa4c0ad0b416743ad2cf7682e92b7a905419007ce7038688942d8967d496e8ae641350b5f6c9c876768ff56683bb708bd0b203f5a093b70c285833d99624ae846247e223fe5b461fad77632e01a89ce7d2fb42f7e2a8f0622df5249b35762fa77c3add3cc540b1fafbfde0afcd78a0914701fb06b79b8c922fdd2c1f117c53d902cdb5de0b85c047bf28b16e9f1b244d21e09c57587e01acc2111d9d3bf16139be82f667e8f58b6ddac45e5a082d3d0588b20c9612a525a5fb68f9db9370f15562a35eea571fee1c7b453ecce4f9cb66a97c13e5f224abc10efa748362ff8f6ea0cd304bcca186b1db3ebb12ad22477522e92539bb504587f59d6103514b84302af382a71eeafef56f4a74403fb6c4f751ae6a4df7116ab59dacee9cd7e2166a51bf320a673b73b3f3cc84a3ceb5ea92a95bae974650542cd97aa7257e88166fc94ae4769ba09848cb49e7e1598e23c35c689c23d4bf9f892c07fa3053652840a7c3840c29c613b3ba5d0ae1f3168f1e0017dd2716b1fc4f81c3fe176091f1314d90a0373991da199173060bd0fde4278109fb985ffc72eb6c06be35afaf3d55a5d7e7ab4aa4f676d8ef94f9d5846a7e1092551ca3b87b437a9a17e51d206e803be59b8f551147372eda4c4e41a2e681250429ca319baadf94c02924fa42f05ea5652648de3ba4bcdca6a1f69226e49c423b3323d2ab0867d46e1531e524aefab1ffae4488e10cd2728fab6b41bc4d9a3a7f263fe98b9dc272fabeea53032ee87d6e7105e433647aaff5bd4bf2e6428ad28e4980c407fb33885c476d5f80f034ce5f38f23ab363f5b166e995378b1d4c0dc19971cbf7ea8a07efee8ecf9b5ed1106723d94d9519f743f522849e942a08ccc577e3059d911eaddef50b434d7399c6c0f894cde1c220ef8fa004df8723d0fc6b1b4e208f15603180c4f8dd13e1c58d487345502848ff29eac3bc95620b7cb80a7c242c9de0512694c3c23239410000000000000000772f44020000000043fb0bfdefaeca067e9af9a8c633bc85ac2a6ada10269eafad949ed1573af737787cd2566aea366f9431074f2c5cade89e13ce19655a8a277ce71b0280206c4b19251296a71106ba8578cb794e339c8998559881eeb3af1aa841c381062daabe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054f26d9c3110b48f52cd2a1c9dc8dfbe79578a38c13e3da40c48bfb77bfcb9e6422a89cc2dfeeda9025e410a1f9be05b5eeeaadc09d427b28942a63770e4bc5541018b0c1f46673fff9c1b28a21c21d2251072558c88e4246a94a1a90cbd5eb8736fa39dc6accf01b5daecb5357d1d396d65b8fd8e10e652e0cf020d3864d4520215a9a880870f4a9b9eaa2a904216c40fd61bcd8cbfdb8600328010981690fe250220f54fffec22f648ff5fb507881d2f019db9fd33a7fb9059b51998f5cab4cde10b07c321e1b7bc691f828339100b5d2c4fcee44f5f9adca8adcc210830c2b541764f35a0483524bd59be939c41821570c8c9a23a86b7563050f896f79383c4a0ee02264d85da7672a9822e4468bbc7ca6ec33ab9d61337a58166867531cb89f95d200321145204df87d4724677f7799efbf2adaf6b589025fc2fa7de10f08369ffeb8b02178a157ec00633bb862d88ca0b406c8e3c3f3dd62359bbd2c4396256389a3006030b931a2fcceda17cf9b872e785205c3c73b21b584df1150a492669e5f7745faf02226fda5a6c9405661a1f51af6e81ddc4461129d6094667d14c2ebb2ec9e2eeee07f192bed1a73fb915da723e2d93c1856b9d907d264a9f181b5cca22deef8c45eb8cbd5cfd3036cccc117713258635458067e0c064729cd255a28b15f50d273bba34b505dd9380f2d26e746118c54c15793d5db2232f88bf1bfa79bd09b3156b3a1e53e6ae664407bf4fc2c4178db69191d2c8644894fd03546218e78b13e155140a30266556f15894546548a57f4f6da49b6790e788653c3c650a7110ae30c47b93bb3330bcfefc7b36eec33293a8957c70bb78237082a642be85f971323017dfc529996f4915603daa0bb92df1babe4fe243299eaadbdc8ec585b034e379d57a77264a422bf3b0cd8de83eba65201c2df4179d763102168b7e44b49cd17ca14f1263feadaf6890f2c7f203faed700a5849a429067ba400d0c8b66ab6032e7597897deeb6f3d1625a0617e7db40c8535a59a8f56d7b09c6fdcbba1401c46ed3c453740edde6bf5e14565e2e770945c555302ee63094e0616f206bb6d072724d1481cdade766347bda9d5fc8bf71e69d3061ed8688c1bfee1401dc8e6270117e7df40f9be77ff20de1d72cd294e2497655db0b88c3a9d1c60222c76c61463e85a57c807ee48868c112406ac0abc0162f4934130947f2c5428e2d3004ac866e96cacf65a6792b1f4449eb2e9aa1dd18cf8935e1eb9ec92546d3130c4f29e769e3eafc81ebebd68d27642a83139ab73f3c47c5833068e03ef806bc4294ca3c0752674cc9c8bc024ddc5f75ee7526512be2b4ff85db43010e6d49a8805ce7d641bd59457f0727af237983cd15bcf5e0c5eaf202faf2352aee0ac2d4a40374e7dce693036c33d0e963a7a4e1ae5fda4f02bfa8e86629c05e82c4cd4793c57222d032b3aadc5e0de233d47acf601a45f23c7d720a4643291510006415855d8b6e39b2140987f295ebcda7952478e4a6beb1161d7047665a1f373521c2335d765d4fafb54f4e24714fa764c1bec8e94991126396ddb14364cf2ff98b2852840426d308ede9d7367869457ad2cf34d14984d8b8b6ecb9ab9c7e44a1f750b9599110e957d9c12eafd97654a8ce29f500bd44c3f99bf233a5a2efce5a3c18feda5bbb412630b72e2cfd3fa6bfd4cf5c68c9597a8c2d0a0d6755ca564b80c81589bd32d5d079fcd05ab73a6c214d95da922e6ebd8fc966f39cc2e0850db2ea419436062d47f13cbc742eac25ef14124d9b9d18ab690900e22c2d298da78470642aad9b94475ed868554f40aba11ebec0de4bc0cceda30a8853c441d98e7a89d0643783c23d2957d148b7cf012feff6fcbc1423b7829b2b28f10d87aeefd5416fca8bd96378e45a5cdf8404c155fa9d74e4177109ee2d70c2a53de81316ba5fe17bc9e0dfced2103edbefaa0bbe0e98bc64d1a4c211846cf83d2183cdcd24843b5a700c08daa2e5469462c8d85a626b68d8578c12c31fa00e43ad39f99f79fa1191b8639c066823ea476e1f8e0c7e431d7ed30de7c146fad7078c3e87f843ec2b6368df625cc8d0aa4c5a80b32aa89b2cef0b6ddda6ca7856059891df7bea7e9e681fb61a5268feafb5efbbaf43d57842cb79a41ac18e8a76ec1d39c6df11d0378ae37b439f99ae1e034f5bdc9b268fdcc5bf4a239d7d9d9c7899cf6a71abc202af34ba2d161103b38e6c6dd8cb0da96457f7012a1252c15f505010a2285c785835d8830939f3ae8fd449ed1d632864166a0c76d8ba3f2f4753ed6fb4ae3080042cdf4e656e8768a1bb99be9207fb876ec2bc749df07a0c850d15405d45ae126564e69ddbabcc722de2a19705939b7040a124b0b6bdbfe3b2c539314bc7e4a35ad7ecc53d35642da9fd92a05d754d7c935ed9281bf48907", "ac515352", 1, -365062026, 0, "c15b2aeb64e48ebe42498cf62ff9783d16ccccab42146e4e886e31371ebc5cbf"], + ["d002096b0432942d7d824c64c3ee63d550c0c398cce5efe3102cc0090ff5ad80f80590d2b401000000056a536a5351578ddf59769e626cd34412175248d22b62b1d537f3014a5aba56348dd1ec69a63024dca20100000008516365636aac6500fffffffff4e7ec7c8a3ed2e0a23f650551534f5859cd8407fd3c4e119e6c8c5e99ad904d0100000000fffffffff2b7116c643c74c396abbccc7701aabb64eb773b8ffa149860c860c5daec2b230100000000ffffffff01f224d7030000000001000d19857f020000000000000000ccfdf603000000009efb882415896d7b958749b5035dad73287608869f5bfb9f053c25248187880903ca566386b720c932d83b59c734946cb9cbc28d707553c7334e517eb89de5ae619e1fa198e277beb236296b4d19e2736d3955252415b57a04570949be5871af00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b2e7b6489d0eccb17ab67b4d26dac5a83661e2e5c009c3563a5cfb363eaf5d16114b702e6754f6b83495ab915a1b0bd27d977380e22710aba4eca799b6791028e83f929ad453ddb49b8ee78f0f4871635c5eb34190b76eddb8fc4ea8f7e6aba6ef8b9891a98134d5d4ac05af26bd5bbc5525be1e261f27bbaeb4706c66d457df021db715bb8e896c4d63274c154f97a25453601b030a2b7d70f9b761275f0ccfc8030d40d01a551aee797e27c15f6d5a26c2b01efb072d7cc9870f3b07a28a47d6be0b001dd79054301499b3fce9f2ead0543939b2060713a8cb804ee3731cb9e8eb181761dc4372cc603c99cb9ed506f4d02650d013aa90056a6dd91cbdef7bd2812d03218007ac2f9897f6462764bdddbb4a77ab3e9c87a0fed99a35ae4640a859a6ce03304cf08e0c7b8a9f03cf307037d1df2cc917746c12f4f3e14adfb0279cb3d3cf0324e7ecc3f3f80d3cb6c607290515927d329acdc9888bb09657a12dade2ea570a0223d7813a3848c32d461328d12e8c0eb79ca7d5a4197322a7b447c143d7626b7e0319c728580f1afc5912e1fc65ef97cd1bf94f516d157022743423ee1dcbf5a8ac04c363a31412179e36fad4bd16312380f18118aca76c81041efc7c2658aa50a0aa34cca1dbed7e1df1d97b4b47a6eea8bd1f81f5311ac0304d84e16c438e465f56091da6b3c4469257471b7970cc301f3d9fe3002790f00b9fcef2b3c7777c18bf58c2fb374c6b01695e9f1b2e27137a5da9101379af036e6522e246cfbe88eddce39d7ed983e617d117e9e29a60b5427896547a9c03c65d67d78241e6638c00ec371418588ac6459d64ad773e85ce09e527f57fb6a10fabca3455510282dd64bfe9fe2b168e3b8c663569301a2b17ec6591c50b2fc28c0f9ba6c3f24178ceef2fc2457476df46434b9e03b2e048c24b94898aaeca909f174dc5ce87f487e4d97b328aa785ab8c3def05b819ce18670d4be48ee544534efeefa6e7f57cdc5ff9f183633e520ec6a1d802512652cf85db9c1b182d562a5e08e066b0bf4a43e8bf29888cc4f9d6285a26a7e21376511d18de4b1b94108954594a44b134580c0ab0ddb88b2cc70f0b6505ee0cffb9ac8f570737b366250799325293c82d84066393cd072634d697c755d2e0efed7bdd303ba614344695f6f374694660489eda7842d6b51e20836bb356ae9d7716f342ba70e75b6407d4483da0380003486576638aa5adfcdd53269918b16c7f762822d98bff8b0fedd2a132d0d22c5851189110f5a13d355e21b9799746e398f696fcda2f9df1b9ed81cd966aded5d636cc40b7553d7e4a7457ba1181c2977d648df434be760b9fddab43171d3a7fe9381df1b71ba94899fa76fec99dad8278c9e9a0723a3a5bd95ed59b6bf2c3669347e8056e4f49b95b5b61c1160e71b65e7fad05d14e0b869c9ea016ece47615b48c9f456ee9446c99991f9bf0ca66c30913a7df6a38b448b69c7d3aeeebbe96ebfd050a0b7d570f59fd08b57b93d3e6b00cd125d0928d8d885a52df45b70383104bf264f5fca852f2ef14c5c05de3f380c38d38177df0c14fea416d8cc46cf5999f4ed9f2f7abfd1557e753d100930452e3847b839777bbc8f09ff7a334faaccc4dda56431b9d5ae1bffc22b318d06d7c86e3ab2becc1e1b653f97a07bf1ce54b6b161ec2e73951949f84f6cb5f87fb19044253c297c4347444e5fdb7cce616ac56ceb8b35a66e357e67206864403610d66be5702eef6ce31e80f4e111b1b8aabd76bb10068f9b3ac1caca13f2a41e318fad989344479eb27e8ed43f61fcb62e2f6658c390b5b3ea9271e03837405b4de1320a2137db62e0dd2bf236fbe4490cfc37ad5932707c7a44f51fabec01e1b1d4c0574ab7ffecd2ed0a3707e52c7dca6987bc4f6fe2f0cabc1ff397eb30f615494f148e9550fede88e950448a30ccdc0fce88bfb073d527626d91e55501c60fa5fb61decf785cf8d7a6f4ad346d195f9e868f9f27561f9b7bb7738ea9ffab97987a889facb1d720fccd786a28bb6f574348eed1cf4738805712c15259ca994cdb7a16386f3bf0f60040356c3f4202c9d5509eb43b6dfa41fbda227914c20579098acd4598c612319370ecd1fd75b1cf9365ac47944febb5c9014f0946de872a20c328a65a4cbef83f84ecc5a8c6cc95669c4e6a16546b8ac7fc50d31775bf89ed7033d9a5e6f1a9f8ce135bb7a906a29d62ce177613ed88c18ab4653b72e8b7aa0889077a61c77a67e67d86b9e8ce577bb43d73b4e5e30ee6914f1321c8ac78461ee8670c2abbf00000000000000001b9dc505000000003383fe4936add65db586adcfb7d52cb7a9883596c1518d21aa883c9991a3946ff556c96ac474da49682abaefb909c08752440c9bfb59ea44af0575618c576ae3dfde2673872e984d4c8e77345393c51cee14be84e3317faf0377e29e458899d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b3be087cdc25090c4606370e8ba3ad93891ef3de797f0d816a17e4473d19e95ba87368b873310a57ee848a12ebf078fe32c71053188f91af5560869da6a35fff60e043d4cd51c5f1a6254c5234f419b19592fb9140febd43b4e85f28f00c42e5aa8fa1ce530b5b024712c713e7e6221b60d7d644ba4af51fec007f2ba1c2e4303003beca1897f7fe9c701a2d0c4eee7de4cbcfdc128d431e07453fba3dfc5b98503036f2da58a0b9e9b5ac5a454a61d0222aede0e885c662eb051495bf1cc0467f80b08f8d9dbd6daee03339c8489814b923204c2473ea0c0fb44bcd04516ab7273831836d88c826c8ab6d7d6cf04b19f4f7a65eaadb7180acd352547bfd7c231a43c0226ac6e2f2ba670c0a17eaa7b630023a07538be73a2536d038e4c9edba471e99c032e6bd1246335b55a331cbbe3612140ec991f69de8cdbf39329678d54dd770703020b3c40da222377c4668bf37e8a539e92ec847f7f9507544b45a59e70bfba16ef02108bcf3041d768f71f4d4271886c8cf715d12920919526a3804741abaa18757c0227e0aca1d7063f967115d4c86b6313b5c55b35ae39ea3f09d42426cabf4d17fabe95f9a18048dc01c5acfb47b443b149b632693c2b4a2ec783a36ae752389c04f3f6e73ef5ca16697c8af68ad5508fb34f1f946f21e293b9c6d12cbf5f7b052ce90bd50ba1f87b0b9ee5761f51debc63897f770fc45107cda4c840c9722e133202b220192f95831d7d4ed62f74fdbf41242bab1630640579e6bccedf66d30c0d5613e27d168ec19874d8d8ce387e73937153f10e1da9bc8fd63e34b6223509b98450ec8a03a92b3c2021a394d103b73d10e435663863ac69474314da39dbb399a6cc378aa5e05c434cb517c568465adec5d163edf2601e9200ac3b81548b5911448755012ec1b7be7682395de99c25b60cd763a8d0cfc4fca42d70c7257d5d24f7f3b374e83eeb4fe33a6dea204c38c4df624d5b7db694bba177b5b6a3b2bf034399e5e42ba160be39c9fd3a449da5b885ebf0a8b88620089b242bdbdf7a8062ed8acef88be34e6c10c27ef279a5bab0a28c215d15e3c782c2f9315cc825df3d7390bf7792b6b3bbf9cf38d69e232750bd849c329184644a1234da800d17c67a50bfcb2facdd1f2b57a7f46ab91a50aafc7e63b219f3926c2ca3e6f5343fbe805decc64f3c09229d1bf0726b41a2b45a9086986de375962f3159a3bf1279144929d468eae73948471bde32f4fb433ee9f619402ecaf79f4c08b1a31cf47fa0faedee6232ce0288ddc035b9ccb0636b880eec0eaff30c492928d44ae79076ad32745e20b36b30531af21597a97b14bfc2ba03a30f719c9ea6d7a2a0b437d4d5f1da1ff4eed9c03b6b4425605ff6a816abfc52b8408aae6873bbb5a37cffd8323dd70d95854fe88aac6c00133dd83e30e93fe53dae5c37be9b610b8eb79fab7811ce501f7c2aa98b935978a852598318f9dd070c3282344c094bf3d31fae63e8caf00972ca8d454477c50621651bcac829a73f5c119b924a4723852f2dd56f416a8d1daaf1b82f3f2be80e57e00dfca1b833336d9cec108880aa86950f64db4f365836e987c03a02d9fe2992c444e62674665459f48c53ea29efbdde2c7c2347758619acb504972f6e0aa088492b7cf1946c598115626ed2dde0a00b7996f47dd9956d1e42d7a4ec4af9049115f8a291ca0153afdd7db3cbfa959ccaab6c94add9e25d939afe8c877645e250f86f76db7db1cd161ee812f3d2b01b56142e388bd396fdc836d084d67099f94dddeff8de2d4e7558b6c1e9f0ebc8f91489770ec37a35222cb27a8f14b2446cbfd8347ffb5b9d611fc8095f6f11c4b690e0728979b7ba8099a507bf1063daa55dd8dd1498124f73210b563d3a87ffbbe0c03d888044e6e1c614ac8c6ce1a770a310cc75e4c57645e6bb7563f1cd443cb3505611522c7f0c2163a893443448571eb9c372b6e61d3d12753fc25d27aba802874834dd01ab9daf7b3d7d0a8409fefcc5f3602c33e62afc5b890b1a8c97d6fcb177e2f764928c29f211a660654a4822ef291d74b18a2751d3c04195a67401863b1ee1850bef3a274abb3fa0ba121f6d2f2ed9439724df9501e54353b29981d97c1cf27b18565023dccd7419a5c2e0ceab6fd14fd5e5ceab02f713f59e157e83c2250376f97e4908b6321a3e855ad65989f2befd26c6e25e0b58c94f5f6565cfc1f84cf907623291487c0cc27b97095e34971988ac97ba06a53dc7ea2c56d642d9c1a8e83eb44d05a198aeefbb10e6869701bc14340732b15fa98050eaf87b4a036d53d879d8351f87656409bad309ea7ee3456ecb5faee4f22178a8cd67d1b2a7d484bee4104fd72283b5239069699c27786dd8d6141ade54023daf94fdb5900152d43198240ae94bf31f99cba7f6bb88c40991f6ea07", "63", 1, -1375261697, 0, "73bbd86e93e8b460e755d75d498d2bcbb1be3a8e3c31a88c2b7ae957f6300d58"], + ["030000807082c403037aff6d71dde15341d836e5774ee53472e4c4a67108a1248b82bfd5494eb619c9020000000963acac6a5365636a515b3b090d89ca46961e88414a9fb6602a15baee21a7fd2c0f64ae82ff28797cba3678c3d10100000008ac006a6a52656300ffffffffdd6d21411e39e0501a83f770c7e77980f9f9b755be2afffc943d8a6369d4ddeb020000000663006a6a635162db15e90400c6380200000000055353005200e02162050000000009635300656553526a53614c7d04000000000851530051515265519e64cf050000000004516a6353d74ed7f0a08a83ee00", "006a65655153", 0, -1060634110, 0, "74cf48df48287c5cc8768bea00d38ae178ebfef92e272ae791f43c5e28488bf6"], + ["030000807082c403028ea66838d1c700b5bc786aab1bcb993250eda43856f27d9486b813f3d5dee76b0200000002536ab6bab7a76a26381075d5501b030473c0add66740bd30a41a7d42da916b193a08affd97040300000006636a51006a63d51d48f0029a7fe701000000000452635363df13ef000000000005530053516500000000aa1be45701593d63030000000000000000000000004575935a50bca74e2c951ea2dbbfec63d05be42edda804f8c1b130d80de8af116ddb4b3ea8b1e8865e8a2ab810c29b221f21e61f959693aa2b4355092a222ab2ba32ef8705f7450a574bf89b2376b268a746e497ed7a3fc2a0947356d4844a980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017b0b40ff714acbef95d2eddda0c4fbdff41cdfec2166400869cf543f9ffb04d2984ff97a7c3e705a76c7a95836045374591321e8b35f848644ab2dfa1c947e43623d340d2a2465c179f8f79cf96a5b090e3bbd6349ea299fed0f77c372a7aa4826700ae5300aa8f67f8d7922b75efdd8e564aee8923df19707acb7ec0cb350c021adf24d74e734dceaf19594b5268d5a82861dfb3955a27682f84496410dfa960031d886233c75c35255ea1bd748315445d6fc09ba7777d525a901f56f129d5da0a0b023552be7f42fbe66fd4282e23e569c303681b8c7572a4c2c97734de304889d85d2a6f4774cdd49c960efb9633cfd070d6806b4f92fa6124cb00f1b31a5ef9dc02219a7d73253b28da8e3e7724d94e35de39bab8e3a742c23b5ebb6bce7c2c460c032cb52b8a297f91fd3a39a86bba43c76821fd1a82f961e6b2eb25f4e0ae6050db021b1356f2e40b5665e53f093b87b1f18d7c61961353e707a740513560699fd53a0309bf60b8e07a485d4913438b74c175fc85be4bd5f08f3a75043578dd15483ffd030424a886b6debab0c9637e0ba272214bc507c5a6586feb572861158da478919a29605aa5f79882f040e385e8521270a90ae8c6cbba07fd63340b26a5a9d88ea225b21fbcf528bbe4f50aaa1fce94fa65292e08423e33f7682c57d259bd1ef281e6a83f6b138834094ad3b1bff2b9d8605d9a676a0f38ff968f4980e0eda22d403cc9e81c33979dcc6b378d771a94777afd02ec778eed5ae95d420f8e48913952ee6bb8d6bf4761a54e00f40e57547da7cb35b5b3ddb526a4a3a7d33a12fcd89fd4e486640c7ebf91c053cb036ffc62d424c8ccadb3e68fc4192a8041d44947cbd4cc4acddecd72c8e9f9882f12f1c932daa54d077562ce5c038a88d2251f01817a42b4e387a13e06921736b82ce4be15fd930c5fb5174346e311b719e8205fc388deb0a3a38123e7f7939e8ba58a8768b1130a000e5e69cd2903bb48d63b372e751e8ad2714a3f9456d3904008fd5d94477b2d42623870da29cd0e631b157d05de6792def11b54c12f92626d86b26ba486493b9a833abdddd14760449e680dec9f17bf1ecb2584f61894adf1eb6bbd501f08fe8740ec7af7c200762d75a530f0e1207f6518436a1cad596da5ff8379f138fa627a2afb31d6879c3a46cbb784a88feaef7f71b1672ffe82b98474e4eb40cd8a9f8171ac6b9b63c522f89489fa57968c6873f66689966479ab5964b3f94b68ec458da9532cefc4a6a8c89869e556539fc86ec3a322b595747831bb9e501b5cee9da2caaad074e748f00000a1a379ec695c2da17059c04c5bf2833922b0f6e553c1bb7e08c20cc218831b9487237a1c344dcbff2ed070c22f36ec225787a06619f386b1177d85c69e58f69172a36577a9c9ca6481396289315eb8ccec4b68217f067f58c2d03524215c326ae1c3a12eb15d60cb1c64e8ed362e14d34dca7b8431f86b6d67f2730d7bf832beab7c6980c6ee3c11d269daf24dbd2f83a19eb423c9b0e2314929d9a6bb0b9adf7cdd5af1438842dc4abe4b65650032100ec13d6875df066ff1c1c087e6398209f06b491f0fa2e5078d05db963f5c5c20feeaa82af94b3a350c4804bd1df360964eec5ab6550967a05a69dc296280c3032864b414aad89612c0001915ceb3bcb59ede8f11bb73f19b92b81a9bd43bbab7edd613c1e7f4fe19ffd8677f822c2181bb4bc640a98ae8a400f3aea8acb35e19786fe57a64273ecd995c6ff54f552d989529d8f7fe0d3df0940850316c0c1d31a14990004e4e71752eb9cbc142c5cadeea4960599a3a13bb3918254e8a084fa8a525a113ac288e9ef49a6fb3442f27f71ad00f3c3f45aaa8491939adea58bbe8d319805ba5bb026ab0790ffebadb719fd06ff3650a59ca9d678dea43976802bf0d24075942d571f65d8b284ae54fd31178bff2da7b8a83145fad163fc33ea899839af9ce50f4148f65f05d7122881ec31419659c8d4032018e42d423a9eff0cabb19aabb1c323c49bdeecc380a3a6b54d6b5a74330741ff544998c8bb348264a7df602f0f92847ff30e55c035d6d1b5cacee1d591bf51308e1e0ff9196a00c2ed8f994cd7c402f3ddae809f502eed15a1178c6b2e7d80cd11e0c62755c408c41fe208adf6d7554f9b667d68112c755bb98be06164a68ad6e8014d8d06a201279985dffd375fdfe5e060447393b398459d62f0df2e6c07e29c6d345af7a12c78d97aa8fb77ff151bd624eff42314f2d55cfde3072c3f5e1aa932dc5b0d18c14403829c726fcdbe19c5fc77200d4b8b87413d3fb3caa9baedddda955b965fb7847a2d24cf73d36920a0c91635625c95122b4123cc6a01422268152ead703d40008264de282af9f21764e2e4174aeb521458828d72323e24510335beee0e7adb036ee89fd7c09", "53", 1, 470102923, 1537743641, "4e3ddff5a15be684edfa2b9e3bc56f118eaa223da358d642f55752f9ee71cde8"], + ["5d791f2a045c02948334191b79afd3c0cb894cf662529c36dc0f128d713d8a98bd46ea0381020000000863650053ac5152659f304176fee51c19ef00a7a36ec5b585196d4ab6ea9348f8fd5ae9f193d1b4155a4efe0403000000025153ffffffffc9cb0ea46ec3ca7866aaa9b00ff38febebb5cd9d15e75eb507d06ca50724594b0300000000bd01ad7674f1d228802e0ee05087330328e345c0098ad7804a18fa193379ec2eea6a3d6401000000046a5263650da2582401994d910000000000095363006a52526300acfdf9ddec0208924300000000000000000000000000aa55278c4cf5100996e4ff98365c97e3237e7327e796b38cf2ac3f93eb7484288b03fd8effb693d64e3bffbbbd526218af85d75bacc0bf0eaa4bed7809dfab51a047c20b3e1f1f9fa1c0c76228f8aaccd99b21fb1b26951bb182b13cc4de77760000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018782693001f62d0ae0db5cfb4d4fa801f66c730da2b6814196f7759e96872d83c1465c41649b94744df9e4725fe50ce2bfd4433017e7a14af9793d1ece58efb8056cb9c5105097a4e0056d5ce4894f98335f8458ea13b2e4cc67caefc6adaa71ea4e0af29b9daa16d0da7425daba9b462e42cefb87c594e0d41bbad1faef1240312ebdafba75c4720361bc411caf6c6afad7be093ef65cb27ac9f3d30e5d5be6a030a8ac67f37845343b8f20ecb0ecc23a1283a7d016cd198a94a536d66349f79cc0a07a7f669d80d0c2ddb10b5c380f9ea0fc780551aef4585157fb18b8a8311b8cb021d07d9dc77e976d9ff749e611c2447dccad67f34a9f2218b990f486bb8ce10032e1bc1923272c9344057acc41969bdfac53d3ab82a6bb421bfd54dba4de6165d02304aedabe387c008763a0632118717ce7085d73b382eaff1de0a720cabcebe610213e81e6ec6622cde9e8be3877e20be749e0667ffcbe770285a24c36ab2060e1a0318fe80dc130c384e8a3b5e12fa2c73ed86fa2ad43d05b8e5d9933a269c8042a40317f0d384a9952602d802aaa3d8e24dd518740283a785466b053b63bb9bc9db28ed10e8ca3ca7508b29ee154c2bb2831d8a5cce67476d746379fc25f28428f732a8daeb760ca5565d603d69d1ada0b3d4e18aacce830a52033f01e7234d135a993ecdfa810b5265aac70f59bbbcefac78e6546d450d323cc5b106de5e069436c4fee2a5b6b816bd801aaab015b215046ed503d9a7355a656724491316894875758e9447bda592857240bdbaff33a1ac5aecb0a512ca3265215f3eb94b19ddb2db2f50c679a6b79b360837e471ec7e8367a5e51810f79de836235cd1acf9e36f2d15337443aa6e865dc26746a72d2853dbc8df248e0d3b7d5a77393f95da48720ef5c24f1d8a754a1439f2ec879443468b2b3cd0adfe2a1e394380a57340b766226635d24e63759f3498c792d5f3789165769b15ff18124b8920d4471fab5861b5555d488feea0859f7adca1ec9beb1b1907c74a1fd8ae7f017191fe194ed0db81e2d22589cedbf3668ec58834a164f47f435ab6e5768305974c36a7e2f48966f098eb8506a57dc61bc60f6a1bbf8516a9db4ded5da9e5ce84813a5e8c57ac7671f1bbb0e4d7e57c1fbad6d40fbc1097520701b9ab374f6684fb4f689b5927139ec9edf3f0d48958a2bfbd9480352e02d6d5eadb7c8ccf231cc1f56411062776c820ebc62fea7c610d4544fcdd401b12e5a6f5fc1c6c3f89c50c03fd1b0769aaf7c5b7ef46b7d70a6368d5c7e19e0e3344c86975fb11ddd59dcb6fb0f2d2b6fff2d5e51fae6092dfeb800f9330d4c1c251ff90e31eb00d6d2b6d9f9ba8a056eb4108d0e89ed90e6060bab81f4f553a72e4108a7f3a1939e17e243a0af35408922924cdc80eac662215476ef6d6ab0d0f91b1652448e4c13d78b75dd511872590cbff09ac746898e1b4f27386688551eead9068eec6714336ce1f642735a171a3ce907ffc1ef67add712899d4853a3f9c694fc7a0e56515b1b0a0e0923bd9cd954238410a4a790ae04af9d5813148493fdd4a6fa26d90ac1c0d8b8e2d6c1a232f161857ad2deb0067ec66bbd015304d97fb1b645cd275e0a64505a5a2ce02ca9e743db17db48b5c5dc35a93d1b46e29355a55d4fc1e52056b697a3a5f5a3f62b77a70ac84fca456c4e80368db0c18a69fb77c9e0c0bcb2274ac6a9a150d274ab4aad7267133922b39e96f0fe5e10de3cda5ea4bfda67881ec9e79c1d034f348475d87687b77097a0817791bb286aae711404ae1e8c37b72185a0528a38032d6b4c46cc0610cd409e46829060935f8629e5a3d82ee0ad1fd369b3d0378a5ee9e66f5729a717d978010a470b56259100b74fc8b8a415e6f65df9fbee6db5180146558066e8d35ac9398f6f6a62aa65d3c7df8c899d21c69107ffae28549f351e5184f13473b065723fa36965e302c4981aa95c13ab349f7731d22f23f9c6d91e3624da5e8c01efb216515e561c79e4e573721907602fd2a3a09fdd53184ee48a46b93128d6dd8ef75dadf7b848ddd73b37605cb9bd9d2c09e1ed6b1922763ce0901a60d6cec0c99abee640cb2f4c71c9fe17511bcbcc6ac7159bae44be1ac3f8c172b25d09025f415dd7052954de06cc3ab7f8c631f1c79590bc937ba641172f9a0b040611102edb30da5193ad88e921cb91b9a266da5ea68b7396fef1ecf7aaecc70d6b1fa66c7c2874ee4f73975afcf16fc66e126bb00d825539d20c6d094f352a1c9d6ba959848d7ba3ff53211f7262a52056176c3f500000000000000000000000000b020ab2d89ac6506064e3ed55b6b2f773984752df79b2d96a05df1260e69ae835da6c8eaea230ff5a7b50c56ac11eae2b31b5876a0661b180eacc888ccc0f876f1ea55a8701c60d13ff7cd424289472fef3deab1c5ca27c633579b5f1c12eade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008f582725da98b3e8a9cce8d5f06bb57a9bfd179a9b05422eee18cc4a028634eb83a53abe15564ff4e32139377d63e1980c4891c75e91dfc07df955c5d62402185a029801fa9b53498d45717ee1ced0d2d10b56c43801f9e10848931ffad8263538065eb3fe58c134ca118d7eccf038d0d9d6f858fcedb55edca9783b9368b9130227a15dfb474c0942066161baf8da1f454712a99573e4ee3b864da384fff0aaf6030b064c6ca070d6b57ace45851e6c5f1de77669aa7abf61e71537b39a5dcf48b30a03c1e6bbc8adedd7d5d338c8b5c3f17164d817f6a29003073aeb20e1387d0aabfe6b477356ede104435cea61ee97b66c736add131e0bdfeaf3187eb2d8c6c8ba0305cc4d1e96a93dba9bfcea99e674d4e962a8e78d31543baa933d7792aab849820205b46015d819b2bc2bc41e30e820dfd88a44b76514cc3165198c7272a004dfef02207d35fc8e1a4134056de513de5e0a338f4c77257686c664ec4717efac166c3a032700b2f44a48733ab9ef3835a18af41584252ff14c28f926da81d4ef0d3d2118021d10711b6506a7b97fe2a1102d276ee8e39d73b0b873d536e559f4279a6d80e26c7287e22717ba3f7ecef438810bb8536d49f19a3308646571d0b33ce463e0ca7b0da199eb672e58f317adf5d49e7529642ebccff39325a7f4073d87cf3399c1fe242630be764e387aa34a6fe620362f87b56a40757d8d02f162bf1df720a69b4bb33d202a8ba6b31bbe3d55bfa459a5c077a1b0d8dcbf3d5d42b4976f0c409abde1130b35259ad4e9824fb8c66201c3b7ad0ca80db6916a6b52d6cc979910859d31d20cc60a6f0dac8d6ed9b24c5283a2ad574d696590f240c402aefd0036d774a4c085c527235b83b793c8a78bb76394883e2e8e5ddf465392be4b405300a8f327f9b9bdddc8811c9919d556da28ccbc199b891ff8ceaab066c5df99841d44821378d858733edc55fe3aca37b92038afe1a41762a9ea1b321cba3099ac12cefc4eb2ac31e823a745e69e739938fd5c6542e22e119d034f62694a873c9af488c52574df4c2cd90185c9573a1a7b6bc92b8e63575fd3b20d1dbb0ab12e5e472671a74615f5eb9c4ab59b60570f2ef06f1f8ecea683eca0f28a9a07515bf69309e735f44d8a63277bd3bf2c3e3c628c58557ddd1989626e40e3cf5cc42f647a5f62fdff66ae6048004a46652cfc9c7e2b5071e0ee194e09b937c3ca67940ae827888b40eb369b4541ad4f0cce8570bbd6859ea9b7d3440fcda54d2defaa453b87d209960e4349bbaf7b1d571b582e6888780f5eefede0dd72a66de76f0d38c230cee2c15d28e4b4a70f2488a5647ee7cb12f43fad9fadf1e1a626456c95e349ad0447e436b311ad1216a184bb5b5a56e7d2e1066948a4e1d6f0b39e0185b7c8ffb2495b44cf0c9e79c9529561b8197523d45d7d339a94af4a002d2e19e84bafb4be29c78ce8199937de2e3c5cda024f96b35fa5dbf9c01b3053ff31f44153abb70ac35eb5763393222fa74d1977124c44f39abe6561feaa67152540957a3e881872abca7e29a09a27e5b7e3b94cbddc7d8221bf0d55f50ef4133ae9b34adf75d8cb231ff633fc41c476c1b98078a395f93890985ae84ae91dfbf9567d336d970d40e0eb887b1b6882ae2585f7ded98a933ff6981e0d8134510464b37271adffdb394822135f095b88da10e0da07b4b7856132609489b72687b2362dcc9899e3419581aacef589f719995979b6376db2e381bd4beafb360c58ecc05c809f53a66a73b56f541acaea08dcc17662353d3f70438b2a6db6bcffa927a576eb711777fff3faaaac70a143514b6edf5496b17b3c535d8bd37987119d18d593285579df454f8758f40e5b39689f86ea838ca434d867355413589afc4eece2e52e42c9e014956f9910241dded21bf8e25b0820c7c0243e0cf0f0e5cb7fba8f55ee35c042353c113072fc0f77d7f80b9d6921784df80478d94f3b71bf8f4a6ab790ace7c95d80b232fc011a76cc08fb3c05183c88c64da6bf787ff2714cc0a26e7e062218e82bedb26a080374155fbb4925797d29e1c76f9f8c8081d4c9b3b7b6ec57d5d88efd290bbf723b2ea701a254af76abca347fc232e20eaed13907dfaf870623b22a1863eaade4fb963900c52b959239a947e5a50463e14e3227f979ad6cfbc962f0c0c5228b6bbbfdc536e4df21d311b0cc189edd1e08ee9f9bf3f99f95d457761261d96c4d7771bea1390bf11e7e4c7860270fd38fde407ad856a7b58f3a11ef3635dbffaa70dd1ada0d7083d389cd5d32ea7fa9cf1a00989abe7ebfe76d51989771c7517f0f30dfb724a3816d027f73b58db397dbdcecd6823e07a9214d6bc70d3d704bb194d185c5fd8c0816b53eef13f256d17f491b94a517af2a4f9414f38f7f54243f1ad7c0595d5529902aa405dec507", "ac65ac53", 3, -110181336, 0, "ed4473f61e084a071a8fa5bdca186a639b7955e1718d683045437ee37f67d464"], + ["0fede06f037898444e0f04bf78f92dbc6c149da756408a07212778703cf0c98395f547f9b40200000005006a0000acffffffff05d1eb06bde7ffe501f8ad8e1cb95f4c1eb68825d52a7f9b829ec27f88f867330200000003530063ffffffff4e6907b64fe6d9a1277e0b5fa294a644cd8369d0b3233fa5ed2d36fa336493e0010000000865655263ac65ac515dfc8fd801e061dd020000000001510000000002565ae6040000000000000000000000005722e51bbeedb8560beb2a6cf2a114570be327af6f70f20987821b4546bd94639073619ca1e0d7bfdcddadd90afa981523b75453c1831035e92dd1fe45de0ff07ed10ff28b99425ed0a875af6f9bbe4eb9be7aa13257fdb0f3554968aadc3c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023b83652b6bdc9914655ff8dc8509069b74a0ea116859c6e934ff473fd71b68538b6f09b88ed8efc1462a29afdb2a801dbdc0608d6f049c348d0da9ab50934bb0b8ccdba0d66351c37971d3f5b93591cf41d398ff0f3c897ee57bac167467a8c015e3f89aa8ebb3ffc8ca552ddc8d3047cdafe11ba55671a66a3e8027efef3b8022408601ababd812b5f30e7fa77795e85d02e75f3a955ef69a064f9db121a990202094bc8a6f086a9012208cb5b10f4cd62f7e0fa123a91b96666ea410857d8b0530b03b2f068ddea4941c07e7ded0477f4b10906cc1ee116d097ec6c31d696881d9139728345bce00a087f4a86dd94e1b660dc667a2f1620ad1ae8a8103775d4aa7d03052d75950412d81a04adb1d025c9afbfc25e858bd3cda76c17b337f8264401ed020cb0e79feec88ee826a43e0d0ccf98f7862fabe9671df43afbf6f7333665e7ed021f5458a8e2cacdfd7c3de8e63b47dd7483e33c4a283fa60c25f23f37e612ef3d022402f78f1ed99b31fb69d192d1205afd6a1ed32d551ce616716ee13aae0ce11e021f7210a672ab12cd3478481d8352ec374c05b28420de89868bc17a5d5b443d9eef9f92bdb871973597f1b476fccf1cedba5e27eb6daba6279fcdb340acf99ee135cbca400ed9ebf145bd190865d4d42b0f6d6dd527553176d5e5f79a713926ec37cd743d3731603afacde8ebe130f67f799b083938e3302c3405b6288a78687dc284cd9d1c49201f85bf94d5052ad6f8a8e180f6570f88297856d341218a58322d3fd8e67593dbe210a7c7d61e1897716cbd367d45a644f451b7096b5f456249975a2e57044db09603e6b1b743d8db17aa38853163772dc8a56dfa30aa47e44d292349a8cc243d006b22903694c14bfa8c9cbfcdf84d7a58b8da7d98d143b30befa2a8793015383f023667cb67a13b799fc9e8490a8c747f96564033955da066dbcc40ba62dccc8d2a93057c742820354918fd51f2e56c77b9bce534731a16afdc0ffcccfe14944bf0ffbc394b13093e126dd48eb6c5186976ccf9b227b3aa898af86bfba95a6c1ea2a21ba832c02b47a12372970cecb9ae20f8b1c9abc32ef108953cb0782a0ce77a76b52cec2354adbb304634155ee3719b8a1a8e799c17228167b3a195db47d0f6b9d3315a4078ccbf4641535f5f00ee634dff31e781fd5087a9c47d3cc5364a94a53b8d7fca150792c10248b05bed3333760fe303ed5ceb08c457bea0348f35cd4305fe8342ea6d29d3686aa5659801afbb3787c14650dc24a2525dbae0a567ca459fb3aa07d8d6730b65de63018c5141a10a0cd638e1b6e9572200269c47ce2a8c5bd8c35d3f0af47d4e7dabcf966ba5b231b06d562e3eb5570bcc189a093bd3deab710abd98736b4be2ae0cc156ea0bd3e10a5bc67161a7f6682c44fea43c16fbc4ef8e0a5fc90791da217da751fb8e708ab700718a3bc0e028df665210f3f93271dfff974e585d01d31d347cc325070414943cfbe042f72511bc83e7a5daede968c42a4490a917404f6f49d4c66693f503bbeec155ddcba0aa4caf10023ef332ab6d7f1513da59cea2c0d7ac8908074e973d071e6f8c327324e91829e7191559a8118bcbf405038eb7b437c592724b5097725477d3054fa7f9618aa7c4b95b52f358b52104f26b54002b5e83e5af0f4ff278f7b807990903898f2be4024b5a041c7556dbdf031a66e45015f74f4f2eeaa22d91a6100e29f2e57b2c0a1f959cffbf8684b82bcfdf0c10cffb65ad4c94899720a453fb310d26206ca2c261bcf80a249e866792874ac6a505ea67b10a60f0f8858da3c5d8ea05c7c45cf4fdf4aaf9fa226b55cf46868c0d90602b8e234a9778b598a5ff2fa13ad4956fb21b906c52d43ef73755f4db81ea3f5dcd9a26a4f7b0e5f869577500648c2e766b854b47218d51536e30c6e987fb8b8c65af2b1f0c370e8c5bf7a738bf8eb86ad3105bdf8a09126aa0e2074c948f60d22bace3744877c2ea971aa31a061fbf61f5ad2b2ce7478e4032170549b819964db323334839d1821e03e5e825968839e70c641e39e29886bb1ed427e30d3d5edc60a6a08f06c2cfd8c5163377661427e2f6ce87cc58bcaa08855017985fd8ebab0e9426181840cb316a7fe5b4a48f981ff4b0b98f436da6aec7b99d89e2ef1e9b522b50207851a69a09f9f17fcec1ae95fae4e6d6e34ab0aa66ddc27a2d170b9a41ed5809e2e8b4a9ea580f5025dfa8281af57b400d023a61e3cdc438be6a7ba99514f49c03f857fb14ff10866eb784f2d5e9386453e9c9aaed800aa4b29fb9ce01000000000000000000000000b5c7082f899c9cdb653d7d654680101798dd15da4b8a425e8c88a52487c3ebcc3793cad3e0389583a38bd5c2002005919780fe917a961a21b56f9f359de47bba0d837c7bade6734d2633621436753d18f46b88f98b6b0dd84a40f5d38e3d0de80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000094b5bc80adfbc64b8086fd4927826c4a76d7a0cc1fcd94e304877d50e1bb3ccdd3832d5e1f1787cf1fe99bf48662620f95a7c8d07508472788e05e2738541da18fa65bf28c24c8342f2137c648ea5264d6440ed5f2489b6c90f9ae314ed0b6197cb6aafbc9493d347ca2b1ba7a8acd200536fa1c66d01bafc9d2807b28a8992f0312a1e636854b6534dd07e39f6001dfa95d6f7c6f9ed34aa7aeecc08ec6353e7f030ecabe4d56f7eba62abd88bfd4334437e73a2cb4515c268093a16bf410f81c440a06cbb18c730d46c98b538300004364700f943358b1b17bf42b54a075cf9de696015c4caec48264f7ba6bb5d071cca14042b77b9c5dd2d694cad702cd0109764e032c517206b18d999128ad5eb2b6caca3ba29c6f7dcd9dbb4be6a923c0aaea2173020c8ab44954be7114fd99263c821dd723d8366491a8b63edae79bb6b42e0cf08d031aac270a2c24f529be389d4854b5f200fa07010da679f7c37cd4a3b2562fa8340205219907b127c32837dee8d7d0bff8ad1223f3fd3af81bc7c40f8f8853ae3b5f031ec576d551e31378a78a3cf542feadcdb6c68e2e3dd208c898f5f5af7e39051cdb3ea13375cc0d7f13266a93f9d3e62deb24e979c7cfbaa6902a82b2f66f0fa0e7197cfc3e4f037a1425828a5eb6eb3d4338839dc78c31b9d245c9edf87501064b920389b3332da01af4179a5d96a14b98ad801cf86a7bcd3038144f8f8b20a4902c0b29d6b228bb2c4ec3d1c5d03a692e81810375da3a094ee9570ab59ef84637fb149ac8bc69134b7ee43811bc1aef11a7be554f10ee05e687d0d0a34b9afa2cea97e2b171522f254664ac98c347da06bdf1c9b56c30380022d6e8c7b1b86a8be572c758e5fcedb2428dbba395d27fa5170668890cd438d7290f08da3aa727deb2dddf5fe11f15410f3324ba10afca8f52051d9706646725c57d90c3b99e399c8caa0c22ee9b3bbcca6f4bfd23232cc18d47160eb4420b9b4e75de519a6fcb2a3d2854701b7e5be1642bbb03d41c89fd01ee2893f8757e2be2805ea0fd828228afd5ae710f2e89fb5ed500dac3eba8891cf3c9aeadf25e904bce0e22fbcb98e1b1d7bd889030f9092b7dba2fb5d879fcbddeece9db21cca76663d13a859530c46dedf9c22ea830f4d827c5f503b6dcb1804c234ef1c5e04def374c8c166a439f4989a1eab6c42c13cf0695767b8a6774ad9f049636227d116cf419b22ffa5a2998e576394d40a462830604b7e5e5a11beb1aad436c580f235bd86d99b910b40981e5f3b4df1050383963d11b3476833ac605f62768e6b64fa2c7cda2aab76970be839e2c694fb42244f7ba7a7055110ab0098022cef28f2a48a082b1d5b501659d30a2c6bf893553a37b2df72a46798a756eceb37599ea34d76cb380a376e9e975ef1a51ef44252126b1c225ca5059810d26427a69852b7a0427ad978be638bb7f9216328ec615b0e18b79c1542a8b7d3527204587ed6cd5bea3d1ebb881e9376d2c70b70efbcbddd78487f240f7ee9c490a86f249ceef3addea3f0577fc3580870fa15e7399df18f021bbb341f02e5261f9ea9184b06011f85f5a7b632786c05074b777461dc8e5be0c11a40dba670b2022972247ae5f7293bb84957714bb968ab7520e299e44ccc5f5da01d88406f36e5ade5a6121430fe917eff92abfe762b4326825034c275c3474d31ecfd6128b6b6552d2fdabedc4f8678d3711b77b8e750de32b6fdd3c0814535bc559217f6b36b588850eb71507cbbd0a417adbd4627e8dbb15b36967fd7f9b875df64c067d06f796d4d483b691b6992c531d7663ab4044e981d77122fb7417e325ae1e2d6c994846ef47bd33fb847e80f869fc849f89c1b224119187abac7009774db8ae02ac0215fcce99e4f47507899b2b91c6670faf7152a61cc2aafdd7851b123460aee9e47acf399c9e25e73e8fb03d0b739afe9ec893bf1f00387a78885d89c143bb0be4793676ddfd7dd0eec7920725a15f8e8b61649904ccd58332f1c7def6eb41110a1161485c0899af45ff5d5560893e6fadf874687487ff010df45a758ec537cbcd5b3870180d90db2368b73b424ab66a0ad21fd95a24f8ec6f5cd61cd854cea2762099795c9b3cd21544205375c478effcfe83523ccd4c92156b5c276bb9b4ef6a633bc976b42fa29229471bed8d99034e8aa3a1e6970e4ae7b5caf504157dd62727666f96aa48bf29fef95176e52e2c90849d7d5476818a0d73ebe0161251a0fd67e4398fd115908ec58e1b67eb76438f0e01cb19e10cfd94563e4fe4c20fbf5936c1e2748208400a64baed8af1249f299c308dd9cb83e70ca6c5f462466ba56e789910ec81d97cc0573f1b624cbf9bb5ed9d1f01aa5ff7084fd873c913ca00e2ff1d88c39249e170528b5f04babc152ce987f3f4fb08bdec0b8cfcc5706808", "5351", 1, 2061477248, 1537743641, "8da4cbf6316c42bdcb24672104baf5a06e8d291c89f3452635a69b97721f4b51"], + ["030000807082c40302e1b82e1c8b046e8d4a21af51c487aa52b104a8d4e587f98ac8144c4779753643000000000765ac6352515152ffffffff58835841be18f2c0099b59f26b957396e753cda84e73be338248c1a1ca81d9f803000000076a516551526a00e4c44327049d2b570200000000000d941d020000000000740a6d05000000000015054a02000000000000000000000000000360b3ba0300000000000000000000000001de1c80e6ec94046756c8e32128fc860cd0e744045bdd24da7b1e0b38f12d0d418ef0fd9de3f64b5bc6bcd11f075ab7c043c1222fa18711003bf3766b95c461dcea88723fac32a7fca9a508c2c13cf2c68c2f61551a08066f7e09ae68cdbc4d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e48064e4f3226ca110d0773ba2a973c5422a9e798cd166f60c14356300abf0efa8260fc46b742a3cc19ca879eb22131ed5e729f0fce6dcdbec4b0d5d66b36b4daa9f278c400900ac28b35d27b0e6c72fc36351fc2241a38a24de1804c6f5da171fc952bda477a1789b3e9a4ebaa0eb67994c49f01185fd2ff720fa98143774700309a619fe415041c08acf9346a22c9ed2aaad308b5d09800ed833ec9df8cd9d1a03203b8f78deddc816a4a75f60ee8189ca0d6f1362b7a87055cf44f54f246b6e300b0853db26aa6cf379920ba735fff9e48130d0d6d2411bfedaa1edb993ef87ce16d4cd7bafe04112eceed946ce6724e917ee058e7a6157684577fa924ef0e2a6ef02250dc6a43b516fc55f627e2802adb2d682096d570bf9e645c2be7f138177659c0205cf5db425c821bc733905d69b43318806cd9709e8c228c58ca523c404fbbeef0208d2db7212697bf69ae1b688d9779d58f4826d1f3bd9af6fdb358b4b0b4d003003287e5c3ce168a2f16e4d6f9d949503d282670f8d6a6ecdfe8ddf261d70565ecf0214542cc9b724e489e5ec6533ef974f8e03d3cc78224d56a7dbb338b4dec6405c14b3c426cf8debc922e31ecdd88443e84413a8e23b944dd801faa76197ee860c39cc1cfbafd347501e881b64aa430cbb15475e8b2c3dcddf7e8daa14efc23aa16793e7887b91982de3a4041d3f5d581b2445eeb81a6104474344811d3d829681fe4ffb6176136640f29450d3c6e3c7d4a9e8cffd24011adacc1ad1335465fb4d73a8c1ff37966257a13e8d3ef7b056eac750e529d968d5b958e9a4505f604181e33dd4e55a1d811c6c833bcc8f749e932a106045b881472bc46edd8720faf339d53129969c4aaf3d86168c413ac0302f871265dbb63f7798971c4ec7248d8f74d20895497865c534e5a12f3688925617897f393e2e851d2218c7e9b4f44dc0a2c458c8ff6a60cc08dd479f5d6cbc205d6e33b09a648e163181998c529eedd00f95df58a900e7e7c33d4643be49de9af31f3a41789865d7483409804813a90400dd27aec1d829975567891358fe55391eac0def4c4f62d4336e7f39ef60c42fe0fb3c000a75b68d73d838b0e92149a62f892985bcfaa1ab3a2e72772c3dea8433b9afa9096f4a0c885a0334ca2d33da3fdc13073aec919ba6a7a7a316a0b998ca77da4f90c9369f87383db40ae0de50d656207d379559341275dc322d73dddc2c0dc97ac1c9fd2a2784b06e84f0b2dcf6995a2fa4d40b5963c34df066c451c34db21fa54d914327657e655f816a866655b6dca5d3655d53abf653a66dfcca8fa6a3b350ab5a7d9f854256b772e5970a92e7df6b4f5badf098542c10a299092cb828ec991cf575338e1f26c6297e97b9224259cc3e7e9c17b64308b60bb228ba9008ffd984db1673a05dd20b21bdf98be0be8a8bdd283dd9e4b16d35799d03c828b5270d43a8bbdc06f4690db6c8fd5f5312312363b4005d7f05cd86134802b1900cba070a5c98b7130b35463f4e757c523e9ba941ac4980fe4c556441f3fca429de87fb0ee528bfef143fd344e2677db83d893014e0066f81fdfc359df62dd27eb0de644d642ea76b3932fdbd6534e2b1356e6197522abb831e19507c6004755394bd7042de696986bed420886cf947679f24937ceb914f5672745363bd916edd5d489fa435e8455682bd5e53ce0c812275d0feaacb96a95ca2ef02f13702fd8dd3f0146cb3f59c9763adeab243c10e643984663bbc996921cf73399403b5dacd231fee2f1472caf6d5aafcb7100f74cdf948c09f86ba926bc3a71ff3c2252613e0cfd14591a0ca1ad229d9b60c1fcce09e134c58365089b2c4880e099a6a36fee16ef01280e3377fcb265e4f21bab9a47ac6256f410f4ce1a92e30980514d229b1feabcaaecf2e431505aec195805172df725a483c0c1452f21b7056691921a086dfb39171dff32a3a37c1ceec19c79bf925be0aca3716a6f63a24591a24474411a587a1e7a4259225532970167fc4132377be28544491f78d8e9e406b50dbf1d091007939832f2c9c0b8544d58aae5b097d7ceea8dbe96758ccc9fe1f64737859ea323aab194b387c8cdc393874090b8c3005b8810ab4eab0a20757277c70fa1923c99ed2f14960465a5435418b4c17d9b72bd5013fdf6ffff28fac107e609953f2fafa225cc0cf472709a4cbf57e17fffb5a9cd76b48f4a9ea8e1374872c495a660678bdc78dedc46a674ae16b1585dc25738302961d44c5b951531ec7afa5944fe1741224ee7a620a04e42120a62e3c1af3882003000000000000000000000000236657e788ff1e1522db287312ae3e10da3654a5aedf4475d75c7b5a12ee6ad09af61d6c625b1f47468644746357169f8eb1013d21c270a56b982f04532f8afcd3d6783cd6783e92b76adaa6b603c16e62da6b6215fd54dc2d34b3073224c2a600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f3a28a69351d04b0db6bf49bcc3b002beb87a516523b3547105f1a086fc584bd042e0f4a53f020daea9f77a07c2eb940058ed6ad8103717049e556b97d6e9410861d1ba262c94fbaf9bce7ca3c8932b07a710b193d8033c7c87159b91c01f61160cae3cc46d8f28062ff10beba85902f31de611dde4f44e2c42dc1d83859bdb6032d70fb9adb5f2cebe0f771963d3d474cdaafa2ffd69f52fcb5e1873885c469a402186e5f0c6f9155dd97dd0b5ef248f61526277cc7f77d458515fd111fb39c9bf10b04e1147b5901441b67021118d5e2d130f07f3f6461e70bdb99ac47321c7020573d59e45fbde83f5357eb19b73b33ca2e4a099c1802599d01641ba000e2aced9d030d9b7282e74f1c34171c07be6263a0fc0badb99c30cb6f2cb8217881f9ad50e10228d05eeccf645c3a41cef0fc19b20613b8c55e6c995c7d89e8f16d11982221f9031636bff3e207f219a9046562dbc084959e8fb9e77d01cd710addd83a6b30f44a021518bd6e486fad58ace6bcf43c588510bea7ca16ca1b082025acb0df3637da950314014ea72d971e85388962432e0aac38134b6225aa378f23dd8aef10bca8ec5da63e02bd450d60ed24a589af819a8ad9e1207135597d0cc5673690dd866a301dd54de10a0cf6f13d0d1ae6e9dc6701d90bef46295a80963b4ff58b217b0a48e06c504581006da06476cd32f014678058dfafbd61117ad885f7611c566fd67ba9eec5b8e3be416e6fadb73ad43cb612184d94bf54f99cd74c9d954f5d661c3fd07ba56e8b82308210396a9752f5db53cfd3ad3ae38cec71a3f8f9ad89ee31fa95022031ad58bae47c59d0e6581e7ddc4624f43ff88fa7a6e0de7b3e261e82a31fb3a5928313206767202500165cb8647626c58754aca41aab023a5ead92d7dccd05abf6e4663c70519d8948ba37e6811cdcea33b0d623cccb0056299f72eaab7d7e47f397b49cfda316fb23f78f3ee6d79fdf2c01aec1981c33d5b2415fd6cc5b09abf16f8eda4303a1fed0931bfe0fb3572ec493efc6ec6d11b55e96e50545642c094d0fe4eb517661fe4678c8017a605f852c64d5f269061e3ccea2087686e13806f9f493438326d632b8ff205d2d249af6bac89128b06114a58b83a268c53b24e011923559af77fd2a7e2fd3948b06b83c27116b79ea460046617fc0c5619cf10ec313e2e3a3963b354ba97a1f7768856e1c76f4431b6564766b203ff70c952656aaa700b336c27ea83f5cdec49d27802468f8ce90d5b5d85ae9491e2e7e188bce738ab56be184d43a2460f9ce7e128a8d628c10de5fe0c5902b8a56a1a6c3de14995fb35a7f4e3adc9b1a231b34e4ff333b3f0965722d987cc8a5bc8a3343c4d09f0bfa59960e30e1ec0b16c51f27b4c08daa3f41b98f1ad56bb33513a75be5c05ee7bbef8b6ecad0859c773ba58d6b554780d9176df877becc0aef2fe16260a67073f373c253e8dc49b69d41d5b87a55f9427e35ccb54286d125f459acdf0295fe001a8fc706e864154de9fa1caac4781b0ba8b48297d9af519a2da51583f12f34d73286b17dac4c43c8abe62d11ec6ee6723e70aef86524200b270c711e5d982eca2f4d43353bc8ebc6d4e058bb991c68bcb31fd58efcd7f25d6728d0995f2e0dbf63aa522292c9c9c84e426729144b741dadc5790ab1ba630a312523c2e7921fad321836e9d4d0e0fcc0ee4b212c9190e94f3ea9a4cbbdb840f15d575c0c337f3d5609e7b3f70e7218c9a3480631bc21efe3972073f16ea149d10b307c39ff07c107a182a636c6ced64cd15d4ff8870715265ab883a99edd7d18121cec6eee8472a87abe9e8fc5e77689139fe02ac7ed03eb1aa1f49f393ee912d6331d055bb7359ad784f9b9d68b04b6144034e7d5a10ffa053d430da89f0532b82d3fccb9b0d2ad06bfb8cee78fc43b2e90a8b0133d26aac9bbe23338d08ea9ba935ffd0c33cde85ab2ccdd9af6f2f84060c2ba89ec3b8f9ff4671cba4498399ada45aabd754d00c0dd3b03df23b23d07f59c9dbb758cb00ed6a1ed09be33387bda30693c1b81030c577db0ddccf8dbff293a0ec683181e466ca4f3f03f5d646600a79de3d9c9b351ce929f2200af2e553528ed0ab62b84d312e77523b672d7ff60330c3389abca9a2901e1d8288730f6a96a5b67b72761df760f79f6ce7d43449292928ffbf23f8d3b50e21fa9f27ba18fdbefc6c6464bad105c6d550a4b8d14fb2f25a0ccea5d053bcaa89c53cdd81127b01d0b95aeb9ca0fdb7ffbc142821d074e6b1c656ba2c8275a9c68ddbc5e1a9fbef1273b68f1040000000000000000000000007fee578f23c01cb247d9474ec7029628cc2c588b0887a0709a016c141bfcc8a8d2ea166ad6f2e8fd80ea02870c03557f1fb07b500f4db683b4881383be9a3b1089610f2c101d36357c4ebfd04300febbca11e0d45362225a978ba0c87a9c384300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000351daeadc05f8fc22f7c5ba70d5f15a75f313778215d2e75586384f93601ae437bfde80b70da439b6c514989c2cfe3d5979f78ee16d8d673c68ca3ab19734ddb79c9989675dcd64d820720dc6a76a9388d856e7697a2077f487fc1149862a949d2656893053c0f46a5ae34a4a6c11e4be808d9c16c520d8359e31863b77127d70226dfc6cbe0378ff8b6818a2d135572eaa9565812d0ad07b5f70eb7ef2fcc2dce0323f0aa1dd0904435657cf0a4e03164b10c2d7efe673fa0e38ad2f4c8ed0f0d0f0a00cd664a6d17000ca2816ef34aab8dfd5eaca834657f2bda83346f1e8f15cf1f7a5de0ed37972f247e91b05f3fdac99720a6aa6337ef8a70483536e23fe70cfb022a1cc31f1b3cd46bb7c339feeb514028e80d002f4a41f1eac79f3a6fe8c5c95b031871a76fab9ea76501c07d89adcf82f60a7087a2e360669209efcb365f5b3f6c03071770883b5248a79493ac25d9f1431215b60879e058f4a0a29a7371b37bfe83031721268e5111d5782d6772cad5e072168b87779e45950ec94478ad3eebde5cd0022f86374a76f46b339b32de56e9deb8533433c53b9f984fce5ae67443b309c80e6d416cda890d9a1942eb1ad849150a9a2e6014ced485204f1d73455f906504b7fe385677e95a03e37c14684d16f77a511786d0e6752402a0de8e0c2a15e9bea2c6f451b02c40fbfbeeb8699353b91089e429cb8635a3f921457aa242f35384ba3e8742b2c1c63a65ea0d4fe2b6c3dcec8e6c794b3b6461d95cb8a04c6c0fcdb26cf0cb583809787a173ab3a4d78989e13bfd1551df91b725c2459b78a097b978b0f0ee39bdb0929c79eaaf6d873d7b3ef483894e5a2782e56e2ccd2a3f491c3bb93e8d23f6108e8ce28b3c57e51308c2e840a0f1cbc2056f01aea48bf430d1989d13e293d2f52702757eaf2d84d4e074de7193a21cdc1b0bfb970e9895b0a12cedd7d38179555b3ee57a8ddde5bb72d7c591371d6015bca5575d718affe6cbb57d483979290bf0aff1d6ee7d565533a59a03f2d921b3c0810abba2f974633c4917f40837702e41ab952772959e3567a69238b1bb0b006075447093badad368a11e6939d7983f69b693b9c2a56e428ba1f494951f40186cdc69f2eb5031d453fc861b76ee3a9514e72bc07a01382de6eb9c6514466b951829a2007fe9cbc8406bb970c8f96f01f58f89cab92582ebe2111d65c8b6ca06bcfce110798f81840317ffddfc53a131e6850e42a8d6afea45c17c7fb05ae5235c49c680077c4ef7a2b78c7f67cccf05c3ef5dc0347e34d644532493c7c9ec67db942f9018eb4f7b0afef346e6e58b5a5eece83589c77279c8d7dcb85da9c7841790e9dc3e961d230e00f549c192bb93009f2b688914e27e7b0080bdc7590691dff4256aea41059e9118a683dad8e0ad8c729e3735ff23cab4784bbd14d5bca0fd3862aaa4e6e91f054db99e77f284eafdffb1ae5ff45587ce942e2c8bde0a254d68d77701f93fe531068d4356d892bc37c8da6b43d8f75ca89f1bae0ad3d0706fde13d64127c4bc87ce631cbf084cd002fa2d9c96b488e82ec7e362cdcf675f28455ebfdeca81c66a5f06be59b35158fb280d5c74e5cd674bae40ad47cee635b6113ed74b2d916bde8e04dcdd6bc6cae4aff1f4aa93f035d1f22d399b4a42b9cddbdfafba9979a82de09a8251bc02b88115390f5e267688af8df4eebf841c330a6a331d663c61565db48644f08dfd1c606917289ea2ecb20f8874a9ddbfad7ea5df59c90abeade4764e30441ab889aec5ed95187cf731f078fcbf711fb4c56ab92bd0d26b227435288b50ad048c8fcb9c4dd28f6986905fb9cdcd4b11b4745ca860e5a9f46dabfae1e513281587bdd435419b89c2584231d9684a37625b010f32fde0efcb4ef0f9a86e9634e8d275f7a9128c9c24a71f15650a0995935c399af52df9126284ff68a80c8e8e9ce9f7962f70212f2eeeeedfad3d6bb77761381e4848d9eb44cae10992d93941c2d2510b20bd3b3499fac8fc0f92ac1757880581b9e52fa8827a5cab9f9488ddb85331b8a8a37c2c3f30d7c3debcdc89df9e3814a1cea18d20e3a4ed20305a9c219fb6b0333a4f9b47c01be3769847c50a223bec2006991b2b9628605e1833cdf64a6ecfbea1d8256866846eb4634c87cac4e7ed822acd5ac8b4a2afa65b8186df5fddaf7ab2b801746b7f4fbc13c31b5a384b4f0f1967dc2d947d6b2c3dde0c7691fa5e4fdd7d6da438b98f88b8623b54d5ac819db01c3d87c0099fd28d54f400762b73dedd456727d1fedc123c5229fd623a62ab8d8183c987b58a91472decaa794d3047b8a691e958bf5321b31aeaf18722ed7ac0155ccef4881fe252477f30f70ddf26c1adb9520feaccf25d94e08175bb92e5af568befa6f7fa47739e7e37ecb1436a2a066f41d0021f79ea300b", "00516551515163", 1, 248496992, 0, "288ff06b76d9ddf7454b5a17b80d7eee7501353ed1458bb649f38538ab32b68b"], + ["030000807082c40303f19ce5d64bb7df274669ac94382496ab2dbcfb275cc24ab5218704bd8f93a7f6030000000365acacffffffff90b8702f584c459a7ca71c35fa275857b84986cc0e94519b20176aa047383ec10300000000ffffffff4f83e5d6e2724ded5833064be86fb54dca9608166041006a2b7ef97526052495030000000151ffffffff04a9605104000000000653acac00ac6a0c9403030000000009ac636a65ac63ac5263ecc9f5050000000009ac6365535365655265f02a4a0400000000016add8a66e45e206b01018791fa03000000000000000000000000434e4df25ecaddc89bf07f9db23dff189a2c08d485839df0f27c79f0bd0d80ac533e4abeae111e2b1962f301bf31d66d97b6f6c5d440ab1272b5a21303158658c31c4eb795fbffda0cf5aa1bc0adcf302211d7d40b169fa35fb688041cad448e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003a31915c0aed6989aaa85b4af2aae703ce3fead8c350842c26494cadf68227a8256b0591ef8d856879c509ce8124a2d1f193ba6c686225e3092a303bc9325a046eb99414e31fb22bbcaefbdc23c71de3f0cb6aa7efb8b04e3701d0ae4a0e2924213fbd7eb95e2d3a1edfe87ac10a2dbb1b2fec9f912c5d755062a46a1d913586030ad33e06e72af2d6ba3ef72a6dd592db62e9f2da4a263de69d6fffac8057742c030529adcc84468b741a0ff5435f6d5e62b5902aefa95a8c269127ec8b188287640b039697d61b3df21a2ef659bb415dcfe22f5f97a0d2d3494931eaa6ed4e20019ee627499c1e79056f31b3fc64a56c963362bb1d1b5dfeba327632f2f5ec1768350210f08a75a7c3fdfa09e94e7b8dc9b561afc7ee1206378ed6f5bcf1a32ebe325202040c3f0eea75934713cb8818e1ee2c18fb03310b94df553f439e5dbf1c8827cc0208a6e25f83e452d08e6237a780cf85615736c6ff3840ab011707260ce33aa9b3032a0b254aa7e7acf2a12d90181f13b99a0cdc6c566b98b09d03db92e163651110020ad5b2994bff0a2212ab5486f386f2016cbc8a91741fafd65a146e97a4aa05985ca59b74feca0c2205f65e0cca47f35d914ce7045b75249e86f3cd0629ef6395cff66cb42f69a413a65852f1936e05b9f0c6d265670c44da1379111210b990127fda2222b960735431f5aa62c0b74c8ee1f85fa1ef7d444814b52dd0e05096bf15dcc95b8f1aa7a654d322f488c4bcdbb7fa75b60f67fc56a80ed78e64a7b9132872424ffebc95bc173acc13e2abff894e217a97982eb1c4d16ff27225332fb1a3b25653f22d5e7c3396509ab39d22e8b396268d5e1f1d5dd599777174e1945cb3957780f05123034e6cf70a0f416994160ce5f3b969e31e8e103fd48d1ad0d9798e522879ad981383bd5fc06acbf4b6dd3cc0cc0f3ab20a2b1a7a3bf3d5e827c7bd989a286625179d8c36b45754078b5d8424ed3f1423e39fd0dca0ecfd09acde7a811bebc7a9e584bc2fec66f3e5ead68d36e3d15b19e037483a65f7ea05fa076194feb76f9b69a7d914c8a9be42b99508400600d733cb55495ad7b0855df1a386649afc6349b338b511cfff3384222d90bf60a25f6fd0ff1aef03cd0e4d6266878690b68d8bc80c9dcabf0ee5315894cfe41d092d2a8cbb81fce47b4f63a30e1d0d00d7726f577012b03493fd5a80af842d39a46f4e5744ea3669806a9b418b4b3ee342b5bbb671588bf51ed900b52ee1ea42d0f3400e79269967a6b6a6bf606fc84c804b9831e006dbe958c4c0ad74e177a4379bebb6d3b6c9281b09b2a73f9f8f4971c6b9d5d2e1bae21258b96659a29d95152db4532b708548114ba55b520f20636bf4f6f019472594a765f75fadadcdeeeda2b7f4f2e15877a07a0cfd0cc561dcf74ea210846aa7dd2e9694231ccbd67639b4d197ceef611baf8fdc041b6d74fd32b1d5b9648783f4820ef9354d6fdf1b5853f16271a78f5d4f630f9faf8ec2afd256ee68e5c9ee44ca26622e9813f0ca044beb203aa61f1b9c0241d3ec0064d9c753e6dbdbd1371d9b5459d09166250d86a2cafca59a9037b355b3a1335c1b74bbee3e7df72b0b4e449b25f094c6cf722c9aba72702d98d3cc9920b7c61c6179cec4a5728c711ec114d4df401feedeb92fa29400f82af9fdb1846790fa9736516da02f7bff42df5267d58b5f8ee1263dff2a049e1a4586566e5ee8bd30e1b0e764dd7eb00b2604c45d226fc164b95f8056c8299468ed5760b925e1643f02f37bc2887d35df045753334b4dc726425edfe4d5efaa32a4cfc533c7f97f6a700ad15b04035966ec32d4abd17ead9b7ea80d335a787f1b9a27052d48338ea311e5f278ad67ab2aed8949ce2d4e4198b0a4c2191ef116de687d6eecd9a5ec38610165965aef0f6e3fa964fc8614d3b470aa68d940993457e2edf0a21f6e318c823b9f8df99dba89f2311bbed79b56eabf388710a89880564a95e17b46359e346d6b295402b0b11c49be04a944e688fd25c51cf65ae619a47fa66ef1789b3c9c59a20d23abae54b3693882387099cfcd480a86f2e4309337349f55328c4957c271c758fae223397f063b23b14a1ea8453e1b1c9f86fdb4a191afc73b22e9ef26e355d992685739b4912e4c98df814ad45991f829b39895833d45cc52f0186c99a06e2992ef99010a232c9c2423115a45f1889a3bae62a198cf2b07311cbdf52eb7a49f697dd051a624bb2877e87a07c9bbf58068ea3ca79a2a19be1133019d83a7c3a71e2fe76885c076e26c2a2e2bb7fa176c5240007c430feef27eb481bce9ab85a86fc0e831fb80d91fc0dd70e06e1e7ec18f91ed4d0d240e14b7f1283e5baab67ca2891b388272dd7ab69d7e07d705845221ae134b70cd635bff27bd010357e35190a47e361fa3757490f69774dc07", "6500", 1, 809535198, 0, "9d292351c322cb6ab4f080f2e58b9ab97c9df1da9e7887b10a595b03a60ef6e1"], + ["2b714127018e080c01ad6e309dbbe92abe18ad34cc4b76f34a992e03fe2da2dfe90adfc762010000000034a66e8101910c85010000000004526a6aac000000000264affc040000000000000000000000000d485f504bd83f73184de06f5f3df3391a64415b0a68634452f183f452ffb21b3d6bee723fd8afed30ea54d9796f3ac295e4270cb16b6931de63d0de5e7e00ca3474984476519c210e49e17fb0d1d936af34d0ee73712b03324149a0256956f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000117d957f5383d219254a5098b1cad1d13c5715b4fbf7d3ca752a15a958a843ab08b6f6380417d93bf45060d4a38cfe39d4b18efa0458df1eacd5a2f9602f1c7e85f6885853ae35b615fe172bee0d3fe75ce4a6722cd18eee4293033c5b35ab8ab9d0ab6eb6b4a1706281e33957be262a7d1c2a3bc247740b3d153ebba0dfd7e2030061119689faea1b6537a74e00a1164911b72499590e8f1e9b968850d7a6fd40020f21ad8c0e8d3994a401c3e827a39895fc36e70ed5575185b8db37a51dfe32560b023a9fc8f3bf785fe67db9081168cafe2139ef1c48b3dd4427bcedfda2ceaba91072f97cb33e6a91d32fb191610d6ea056ff51aad030b2305b2947f0d10809170224d4002e59df9c7a7a17b1a7b2c8eab1295c6a598141190cbcb5574f4b21d9430209df5650074f5244792a9766dbafdb7d3f5494ec59afe6acca37685273f741e00314744069f1f613a6ed1d23df2ea960f67be0a305a5a86d547ba0231ea190de6902201f472331beb6b91a51d5025b3ea0e3caf72d06b85a60cfbfa1fc89c49d3f820309eec67c1ac679b7f4a95bd5e3b2bf009f081ab4f100ec1db51816dd992e340075e2ec2e550884f9f4c196f07e7b2afe28b3ce96f405a9da0ad70124347a0a4d102506340aa6204ca327533033f3b0f883d33777fcc273ed3bb4c9a91fbf04c155cb92d8540ee2ca2aa9166545352e96e1cd689090a3a0eba05af03893042ed3f16e28f83b240b46c7193be1d07160eb6cfcc992228617afbeb0323e1adbcde082ca27ad26a0d331e654b5c01dcbbdfe005490abd1ba4d6ab5d4c46331291cce9668da2924aaace9715752cbf672a1bc41aa495e2dc30dfb5408619d8eb464a3721a446d255265b28958d550deb27b49bcb4aeb4bd07e1620cf56a605779dc8be6891972389cd9aecc982697c9232b3dc7d74cd081e8bff392088e27f80f6be15ed19bd0e665e38962e1d205a71125fa66d9209885fcb7ebb6eb4f1857db900d7f74fe2ad229ea41ab9d621e3a595bd450806e90f5060a07e75eb138275d014a00e130a1dc3cd3790bfbe6052cecaa3cf24a99cabd2ed766d6d35c85a140872dd0f3f73a3f478237ade8631b55f1ed981291c68d87522bb161ed69c895f5e6c957a0550f1517400f63c944a065434bde26b2f3b53f1ad99927c2866b786eb04d0fa001cc9c95cd242c3d7978deecb66c09a3c5ccb1725dd9a02641e24cb074498b05cf32c9bbf88aaa7cae57739e92cf878cadb6db30540ead5df717587623336dd289b75a657086550952050a7c7198d6f70844e6a6e0f7cbafde7680d71a4e48456c9f5f677b0a51907f3d9d6eda6c88f59ac2e6c21375207f5d7596081f1ed359f22253a7b8718ed60975ed527a5e203b560244b44aa07b228626a5cf52411138784bdbc9ebd7a12f2ca01a1b4696812728a0db0f0a2ee46c59a6121ae1716369fd539fffad68cfbd6a111727d56cb0a8fd09e02fbc06862191483f044e3e44e3da6f477754a7e532a306c79bc6fa6e246bb63f54645c772d3ef34c14721885d79143eccb29cda73697f76f98a1998b1c87cf148eba605e0a08f78691fcea56d508a6c830621b296175b6d69dc4554138d8a74963ab9c7633b4c2c8a55a815c1aabfa3289ea14ae7ffc3d88e4f0d322189825f937b01894792fc6ffa8e062e0c4858449e4d8eafa564bece408e00d5af0d4a309a6108ab2f2b4e6eef57cd83b94a7efd1ad2eb172d30eb756d115cd93227120cf4c127f9746becb60a2a57a71b0375f4674617d17daa12652239cec35eecc160473c0d05bd7d7d3db343e7cb63d870ff8b912df23e05b1d859df7af469e29e5ec53753c12eb8e3401bda4ad2ca5b2893c12b6be3385773e1fb450d22136944e4fc7e1153deefcee1272a73898f7ba7bcdf211c48ab6b46acfd9329dd10689a34666725d42fe10eb9d7f478af42a002970ddc4552035235852cac86a98fe664e202fcda996c23debaab586ae74b546888ccf05fbeba38c24a43c79a00a014d9186fc4cd1bcf223a9b15d940d4b85c01d0a5c36bd91993006d7731eaaa8afd01869c10e2f682133c72b011683e5fb58491c814f26aea84c3d54dc1ca32ed126c358da5e29899c28fe804cc07928395840a42336696ad9764d1427d2aece2552b8c4d06c4cf5a0924ffc6f0b1ae01cb31ef94192497d982753a89ee5a1886d866b794239f8792bdccf9f28b63c113762bec7132ea245219c7b24a362f7f162fc6b57b79325c70c0d0b09f2a3a41d40e144819d15a7a1ac52a35baeb84601a0b3c08900000000000000000000000000324c354bb5a3fce9937acf99646837d4aec018777281ae7e0cbed89045f27f4abc2681b053e2aa69c57eca8a5a2165ed4c2be8f724d7c74e68f267bb6f54469dd6b16154a9e3534a4aef37787a84afdf6e5d407e8b2b7281882fd52ee1a92b5d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034911292aa2fa5072992cb8e95d99ae4428c6ee836e56bcc0d98fcbdb7269a7297f4ef11692b15d7dc0a023985604411af1e9da361dd2ac0a7b8e3d7bea0c278c6c3a0aeb0c60c52d39a3b0fcb020af4900648c46d454776808a3c988a3cbdc06c92afa2fda3dfc3c16dee6606b5cbebfd51064b6ce24d28973f541c6319ee2f0220aa04477f112b4aff981f63eb4989198a0b4a691268aa691e797b4605fb650c03110e71d85cf6925db4cb49116ebdbc67a6d742f359c6bcea3cb2d900cd535f000b04c7e132f0f5cdda3635ae59c3d3012759d297c51e04d63ca1bc2d0889b281cc1d3dacb9d00f4b41b3cf4f040bd656b76b9a95df66338d09f840c55bad9add910329d291c4f1480aec2ba7cedeef2b87973a94af42744ed2ade43893b81de3022f03262cd130adaabe77ea411c2a0b4383523a52b2ae7c675c437dd0102f7f4308150326c9351bebb29a361334043884ec5f1a0fb3b3ca07154454b9b44aa548a12460032021c6843eb9db11abebdc4ddffe22ac602339129898968f06a6202c543d7d60030db7d9ef9e0fdbedf00b1703da03105c1410eacbc614bcbbe11523389395a278d122509d78f258a0f41f1b986c1b73a6327d9306953798626bed1ea76a1afc71e6b45be746195038c920b86d8f0cf486ecd9ca37051a995e87a68297398da5fb9030fb89c9225b4a2cd41ad10c7ccfaf43fe65fead53f05324ce8a251ea87c9f8310e6e2d84bfa8f4449f2555635152b6d3e178d71af8f2a9457d3432aba09198085f29fe6b045ef04dd850b9f9f92dd1676c4b4d2385984e7f97a9ce06f920efece20f1fa6e606ad9208766b29bb53356c7ad647bfd0af9a33a76438fd3fa62724aabf1df6fe48c7c5c62020a68432096703e3472c07a5803d49272a5c97c6fafd76d16c4ec56c24869b1e2da056e5348a9be8ff7a7ca22b8cd56131e1d6604d83250d7c0d4ea4a78f39a9a69ecfaeb173ee43eaf9df2e62338788d634e592633ee98555f7d11905c11edba8f5bbfb84bebd6921fee9a61072ea501f958079b4e6ceb53b991fca984fa08731c1bdf5abb008f7b88515e3b53f5acb419f2642200cac5298a9238ca5634792f72074261648c6fd64489d99c25f3a094c38f7aff359d92f4624961793ccb23cbfcad4f3476a554a69089c675fbcf885aac7ca742625a91edef72ce4bbd0f66b8ce561d58c1951c538861afb5f54d11758ddfbc93565aba69ca6a320e1363b69465258de81ce2f17588b060b38d3641d8694326d263302cfffde63834049c0b20041ca5b25feb8855330bf100483b151accdb40ae7d322932984de3a5cc535e22893389061d538fb972c4a3675347952295977feb235a17e3e21e314bd23e84b8b34d52c0249f506cbf78c1050f08bcbb5ae484af7ac540ee82f22e274e2ae903976f7a6ce4513aef01c3fd5bf6116a15fc27773a7eac9bd1476cf7897719959051424a82c5f31c815309320d34454a43847c83af07e9691401ad0051b39d1c26705480f6205124b54712e975a028823ece3d5e349a85eec1a2d360b54a2af95ce9fe418db525ecfcd0f066a6397578accae3f442da29c3933c238c572955b4cd935c7ecfd98f95561a228c8dccf60cfed93c694a15bdba1142f2a0db3d6bdbf46d0efaf93b5ac2792379a489e25c48d511e5a679d814bab2c39164c03ffbd18afe61321a1fb989369b105313bcfa6796f90e302893937dc0bf322deb0c25224fdace6c0180013ecf05006b22feaf91f1532729fd4ddf00c95de36bee33d213cf3e604836bd2202ec06a99dffab0c7649cc6edd8370ec3ee4c9505132dd2bfe267974740e67541538b070d33968bea86c66f3297266f333cb7bcf8c431c731c7b840d78acf88974cb2410d567cee8a7edd45a56e6bc5361180b8cbd63f4955d2e791fb270524adf18590d96fa8e9cbc7e6baa3c353b64dd8b1f6c3b1c897a0f62d2ae51bad28ba58cf0cb995ef930983fe4607374b4b140dc29705ea09ff246c85ecd1890d0aec00dcd43e39ec3c5bb314a8bea8c127a1acc3609000344d8b59904d7e29bcee44e81cca9a7a2bbb8de4e0bfb923585163c1ec143ee3313d597cd1d4367a9208e3e229b5965f46d24b4548ac23e8cc669e1018ca801ccd8d2082cc08aa878b8e8edd17e99d1fefecf3ea73443a76fdaa986e837481b7fd75ffa0400c33917661171e9492930118e30a9a4a14d9e28de10cdf1d61277da920ff1aaf31ff09856de8313e9d72ec13888b032ba10c94efec4e2237d3a15572a5415b12b98fd72238f9191a248d33c5e8df61a970e06a473410e46b4b94f44a12bafcf7fa7f51ec593a1a3caef62b49c96c626c50dbee5348796888d43c483b83bca407d313802595c118b937e99d82b619efee460ba07a1643fa9d0c976c11857bd0324ced7340201", "5151655351", 0, -1242455307, 1537743641, "b59e779a19b18a3e1b9e1524916fd4e60b80b61cb0639e9a4dd1f2ff284c861a"], + ["1100e1060115fef90479073c9896fcb2416e0506eb0e1203989899eaa3c56b2818a375353400000000076aac53656553ac93253243013f3d88000000000009006353ac5365005200a920dbc903000000000000000077f1770300000000a3d28d31805f55764215620606bb7910dd6f4950cc42ba6ffa37f9891f348ee8fed11d16b593e99b6fc9a44e78a4c8df482a16dec8c4e667676a07cc5547eca402b1e833656a7ff79dd0e3beae1b3018ff60217cde2474617e5edea0a4d38aa2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004f2546dc58b75d5966b2a75baf91c2211792c3945cf9a87ef07bd7cfb268acb7ac68895e5a35f7fec02f77e24e26a92e9901b108311bea736fac5d69b1ac6bb8aba2e368eb154cb2c2bef49f88422b3ee573a5dca5bc9943ef0d5d47cb8da4a1b1dd9ad4bf786155cd51b68810b9530b3ae512017968aa93bd1118d68dc8444603195200618e6634145dd9412a1aa5665c72b35eb5a4f3c6c960d11b91daf6fe3e032019704a53857a9e6c3bff4d35308b1054fd597461d1ce9f442317e9141e264c0b05bc2654c4f8a1c342373e33be787cb570363fef654c6d24d9fcadb1ce44f91bf20b91a0ec6161ffabe55816c3fba5ab5f787af811a4e0f64f143b83781b668f03075b5f83a52acaf5c79374258791ca777c4d3dc8aca8cd5cbbeed34ad70a4bfd030af4415ed3638dc877b32a37dbe39e19503e05466a7a6bc78e9cac3199ffd85b031d8851f75a1b452067d1df4325a00c85dbfa0b49b43cbd5d7927b6269f27e3010227e95041e4008318bd931d0d4400830ad0ddd17339a45c39b3d3d86ea8febe830328178fa65ac52f13b52b738443c5f385554c8bc293addde82eeee51aa5ecd3334daa20a3b48c3dc82cda8ed02039f1abae00b849b2d59fd2eb838a406e928a2e23f0cbd416317669e6d1dd864f4cfab644525077d96d99b1634f86ca18b28bb151f34929494ad28752eea766da03eb094f4827b719a53fdb818a5d9815b83bab78255f7555752f1d67f63dcfbe1d295949798210b941b2cd0e176f474dd0fadad147d4ac10e3a51901f1a6b2994555de0917f6ee515146de039755b695cbad1f978c0ce1b1dcd1f4c1548d8211411df9d179b9d2ba5a7605d81a432f0d3e632607bc23ff82f4a09d7080dd79ad849510e055831fe9b0f5b7bd24e7aa0e3601d6aeda058db16bcc5cc053f26ec6ef992f16231a911b9bb0e78c712b6824d8ec1248d1d65e200be59f4cd76683c76cdcbfcd27e940021b6744c1b72b4cd6654acec07d0aedd41b1ebb07c7787ecd2bd510ab8c20e4e616ab53515b6da27d4b5c13b1151b49832dc1c580a2b596344fc8bfed380509dd2fa8817bc35fd5517bc1cc487d83704890600f4787d2b97da42948bbff2e252b0e5dc4a7cba082e0d418a1211b3959651af2a501b7d07679cce60e1a07c3217d8d3d35e6e804ea9140db9607a25921f7081cbc06f222ded624ff0150a4405dac02f544a1c86a1ae1227ef94f746a2c7bc99e414a60f88c9a66b375e21809b8482062929b4f7f4737a67646dd99b275dc6da218c13a337bc105c176c777e41997aee2718929d844c82c2c5f59d04eba259892748cd9f2f7ab86a7714acc84c1137f3d67ca41c32b39fa919c893b5ad17c627e6693d7513de6d41eeaf0c1a49017a0da34aeee29232288058e1e21e212164cb14a15cf825844018788779dfea14c5e7cf90a5ba4ed49558ee0813e897bd00a4114121c74f262a4ab214924dec6c904c793f56880e136b1414e7eb73444cd8ef6db742a02a349ca711126a1a2b3921e098273f5a04f4d36ed6559a95cc4bad830c71d0640973c1fc7b54f66be553ae3ce29df681396f649bd6774b0db8baa3694b0770ee663311a11702d9c29f67e9366e35212e027db7cfb1aaf5a5ff4a19531301a67d18b234bd8748dc121ef0a3716ea822ee18014356f0ec02655fb58778f9276c22cf8642197828e2d418e99480d328ddcbb87803b4d93300aafb64241d74bbad08009f0c9f6167477a42067c41c5c0e133b2870f881f1c1b3709b696b48471f12020e14c35fb026448ba722cafd59701138ccbe46a2bdbb5aae1a35c31d333223611d4468643f75f357b934e211517d9ed268baddb835927875a07da2a4ad57c197e920e029dcf210714f4b8fc2ed4a4bea90064b3d79c227cbefccb8e887e3e816ecb461e2875ae115d618c5d1d4c767e409af4b436947357cec4839925cfcc108c9227a77abe6e724fd85b660410da28789b09f53ec0c1b9bcd7b49fcd25e02a245e18a00518b300cbc40d8093b2098b05aa008fbe61fe5a3475e6249933f03f1b03fa5b71bc0e4db90d2b044432b5c94ece87d8fe1061944472edc944e44df59cb7cfbd27e9a0953a39de68420535586cd45dd0fad285ef42fe498d8eab1aec482276fd2ae610b469ebf204fe5a1d647680558668a708ecaa912938a9e30db1b68bbc95b2079a8c19d28829fb60ae23f45a4245f57d456dea43b43716a716afba324bb7677d0d7eb0019edb27a679f6170ded95e60c45361d59a1872925901e76059ebf30dcf31a7cdbb03000000000000000000000000662e402e9bc5f786328ed19b10aed9cf2635ea1fc37a9e6038e4253fa7ed995c3483f4cd46d264c5e88a95802bd0b306116232949cc0382227251a484b77a05121fcc23dee8c2612aac096c8770ddac0ae6d855ada28b77ad3ede8e70ee8dea800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e5ce8bcd326874214f347886fcf59e6dbcb91efb6098a87007d712fc67cb3674702ae8dde61a03935ea90c0ef364518c2c7d6c1f6981bde23cb107afce0b90b1a84b0f6dab3c479e17ab3a5b57da5bcefb8591fd5cb93a7cd17a311deec55d3739ea3bbe55319a384cf2929ff85d984462cf8238c817bef0d52a4936a3dbf8240326e651ad7b6fb59d93d72f340bc14a99ae5276f617735543d1e0bb24c22fbdb202202f43acbd5afa4e9eb03f46b1658d8d71583ebe98f7fbcf4ab3f7801bc50b050a08b4323fe6e7add2c909f09760487c5d77b8b53ae99b22b76921f4c240105c98cfb43a61bdf78426965f8eceb94043d4f04a38aade5514d3e6bf92f87ea4242f02151556bbe769eb7009431ea9896f08b93db9fb7cbfa6f9bc6a4e12d10d7b7973020c811a2ce6d7940fd503c108939a672901d664eabcaf5eb68f5ee6b2a37884dd03009fcd787df37d7ece59b6adf9885aa78ebe64900a16653942c2aa431a0faa02031a385c47c9bb1c5d13b5b3ee540a46041ceba4d70f62a7d37b97b1ede2f028f402176ccd6dd4fc2feb734b139e3ef1e3625c29c3a006d2e26bac167a98520baec996bd3c50988faaec9851930c5181e65d158458ab2b247cebcdfcace2af085c452adbe479a716bb0862ee56fcd26d0537486924de1ef9de885d3eb0090003d2d6ac143791b251d67d7d39e565fb14286426e400a473d5047748ccb1e70f01b3d8119d068a49a50322bbe6fc15710eeb40bd2851b4ef66c8133fabfa4f81c708636c9f84b39b7f5b42f09d4867485a80dcec2ea688e1e76d1d059d934a0261b42a76a0b05c6ef948e461137b79f0cc155b7419f6f7dd07838bf46700a1cdee9ee946fe0afac5c71cef559eaa5cdad7f0c5ee5baea1a601f8a1af64d56dfe38b56fa052c52076110412e79ad168db07dc55f62bc3d022aabaa95fc32dd3e90161e1f1cd2fcceb1cfdeca207e02bf97e06868e8bd816688a0f67ee6f9e68aabdad369ce740dc3b8e72960c1d1b51c88aa7d3691863965ef4952cfc6a3c4e2507b3e4202533acdeb05384c65050dd3a7868a6734fdd7024adf5bb7694aaf9b4ba139623cb7f287edbb976e73c6234516c77dfddbd6a726ec1fd4e2b9c47600c336d5b633dfd3d7686ceb92f20c56ab3076d451531a46fbc04f2effe512b72f9ffa343054ecfa1121ea6ef19533fb0036aef788f2e0e2cc04b00a7a1cb9bd10c34cc9851e4547d42b08b9fc16ac07a170a0316e31ffac92182e4ad433c4417bb9ea172ad3dfb618e1453a90c7487d4d96f909376f08b4c765640ff08f8caf522399ff385a02c16e7a36939f1e44df7f4caae9b6c62ffd7afd1650a7bdb2ce0d4105c791d305ff3cce86c7b2a8ffd8b48bae9435e80046ca7f29b062f6bf5c1b7aebc48b3ebee8684c3bb64e4f80abf2e9b200f0fe8e0d33ab659a2eaa35219e334e945cd25e8a23b97017957fce5e6d1859d000169df2c6dabf7c0dd35fe041b4df70c459e4d4fb6647b8b0166ed532c817ceb90bbe1340b97c427bdc19fa961a3f80cafa9cb2e571754a87f05c41b176f70ba4fca6f520fb56bda79b4bd1d772d94e5e86f6f249e989572deb9bb61e79c2bae84ba2d96e0e616ba940781f3648ac109e90aa12ffee938803d74bc7644440adb4119185165807a4c763d7f51879f604db0f32ed5093d4557ac010faee8c14b43ee4c8d803e82a86ee905ac82f6858ce5b61e277a3fca159c2e97941199d2d300f7e6828d5823fb94d4c352fe4dd2d7ad4f6a015d739d602805a40e541bac0ad5960fa566c51ba7900fd3d15d8b4afd87c82fee3e403acb0b58bc3593ad5ef5708a8694d269c20b217961fb098de14fcba8c0d3e8973f042f269175197735359e2540b4cfddb3ecf88cd14806b431d9b773dd990f47395503fc893c4aad3c9f9c94134d8690eb8230789fd60cb058ccad08fbabf9b5e181425f9a1f639d436a17b6907049085af85289190e47f671d7bb3580c4e86c12a4ef43648c516776e39b0567593f6a87966e571bf19723b69420f81a06e5623e0f6ebdb519c33079d49a9a7538688d72f70b6993d39c9895363b050d5a194cf3736232f5dad253e853c0dcf8470f359dccae658adbc00eb966b64e5bda669b1700c0af687626b95194ae86ec1ad5c36e30486107ef155e42ebccd25859dae7eca0f464041720f792a5b8ad86de865af3c75dabf0baefb94ac4134a7b0f404e4968b8113fa492543727cbaec83069e3be10f02917b91ea02a747f8e08b2d360c24295087639cd09c436f3c319cd960c050000000000000000000000000138b09c7794d5fa3de6ad420cc1f9353cf55dccc44bdc06f25d001eae2cf7b53a8dffc83c321a7afe54a044c0e4d795b9c1697920748579d37d1f7f28e5efffde6588aae99c4df71f65d2bc55e5b75905224355886c9cf438ec5d24c865386f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005dfcb3151782c188f6aee13306684616610f3fbfdfcc0efeb4fe01583de0a9f666de7787d2bc4dbd02ff5d42cd6307490113f7f4e88d2835ed5bb07a72010160b430ec7753aa902f35f3ebd65c29bff3ee252ce77a5b04ff30115f116a623851711237c622d09b805b8d030f85a291dd4a9819bcff54f4d4a4504eb7fd27f7ba0301a922464e871d5efdabaf4bea48fc55027260d337105b926d0867527c38f747032517fd886546f7bfcbd35369901bc9ef9e6e6f5f9b20327a83696978099702710a01e74f7cabfee30e07fc494327ce5282020dabee2043d3326d977faa8a1b94d5b80d52eadb6efacb6e38dd20c9f55332ad10a7dea98e3200d032797d2963aa00032accc1266666b55e9137bd93288af35afbe0097749d1e5791e197a2a3b3637d703125f672958f6515c74b2c638ee016d540192fe2be7cdcbe2bc54785b72381ccf0307eebdf84de2f3c7522ae58ed9e27c5eca6b70b975f5cf3b0be39c93525c8c9d0215a2eee6389db919066c5dabc20f4f7128f1ef91553f8e778cecc1dabbbe68c8020cd3771c6dbd6dff1e81a14b90f4f57c28ce70450c6c2c69c89f10226c44b4ff66ea4a667cde89f0e9e3b01879182459a48ce0f3e1f86a479daddf574081b9a47883a0bb5959bafb2d944c0fb03ffcf6c5f0966df9f9a9cca400e829db20b8a25d879d7ecac8c97e48796867d7c810d2340ca0b65731eaa2d2896b9d5475fd2e40a08860301a4dcc69f796e6aaa6560ba089199701ffea5b5c03129072048361dfe586ca7da0d7a6148c5cb7a74e1e8d7921dc8af24794645bbcf201d48ffaf8764f183e0cc05b850b689b78f0a21c7b319547ad52575d5255df91e6c29d4c76f2e9b31981725c0cd72a95ff8fbca2cbafd2bf0b720a2a2eda3e44ca136ca47bd5a552a91b26ad4527fd55b1f8af411ad95794d5ffb06dcfe8180c4c26d71fb3416f6673c20e6885f7311695d0490e714132d475061abae2d170458b03856cbc8b71190d08d5ff80e337e97e0e93106f618f3d4e9b609f767f0ffd28f911c15cb3a8e5a09e313fc307c174f07fc94c8aca77aa713fcccdc38e2345cb3a44c86a6789f977027038463a3c5edc79667e498de84c7bef9c1b8da0f677ac0e2b1678fdccc96fdb3ea67791a3a8a735d5a70a34928ab1d84469570925df026a2ac545dffb193c7693e25cedf07e672ad96104932c6a65232f4772de44892cb98a770760aaf71abc9b2005e9d037ede71921f9f9b86651f222d5425425093ea3aae74a6354bffb46a6443b6eb2da9e78db622675702eeda3d70fc5b62227cc805ec7ccafef7057ceb58aedeec85decab557df9628618a4c7419543a830b541a92e218ad9e6ebb7a63cd68069581db58ad17ca8978dc7216c50cc6c6cd87118c643489d55921895d88e47b1535293b72a2220ad6c6b471b77c116b75225f0ad1b8cbf07b57a9d67fd467bacf2a453401c055157b30052656ed0f9f19e04a7b2a3e30fdfe1b48858b98de0e10649eaf2df51fc0632b20dbbe0bef67fa620882208c065ada663dc5198356cc5db3ba8186e2906e99df1746b786dd7ec885e3a4a2a71538700fb8f06e8013102b4ce60971fe387762d86e76ac884a3d8dfc4979fb176f71d4bd8a1235eca5c9d3cae1509fd43c66137e0bc48e2a905e2f01648106a395526e5f7146626d7cb53d3e8a1d1d748949d1eca75a1aeacfa97f3837d159bdf5c93dbfe17870240a87346ad37dbc8b512eb1ef75dee853adfe99434be8ceee527249eff88ebecf7ccac8c0efd2f196cde255ee1c2fc3c8a309ad019bba16ede96988a533b2cac04cc29d7d4b03d3de251e731a04a6027cadd0320974912f7827b3268385b844572885386ac600af381fdbbe025870b33e603b4d92cd81ac2a3ba000413ff7df17318f6e72c9b8601d7d2bbea6702ce3157cad8ef1f863dff201e9b1c31e42bd8c630c55c7d22431a56d8921348b437612148f9ceb8c5c082c2dd7f547d20d75906e72385ebb93e28efcfb1936fc9c989475f17dd40f942f0ae48e6da6c53723b9e5ffbc5665750b9ee047f593b2c0c5f533535cfd34773e33219fee613fabe4e36b5b05548ef837574ccce484607bbc6344e1cd953a2f91836af92ae1bacd6669a14c4d969d4e040f5fb391e8429a48eb6ba13ac4f304b12eefd3175df1ba5e0c55349462b263fe8b9de8a1b136592f1cd9c52b3c2bfadf2069dcd6f9594691bef23a28fad82e0d4b03793a10c4e7a730a48a009668722d6f1cee9af8311b047ecd1cf77e54c9d90fce66a4ebc0a853b688d01a8a1bb892f063fb3b3e7ff8bcca64b41dffd9f3fc7bc30a283ba57f212b75686680d44599121f114eeb8d04b463bc4dedf4a2f95db866d50ada3b20f0dcc50b2b7ddde34a1154701a5f6e3f395c200440eee67718f91636b820c", "52", 0, -521153239, 1537743641, "dd611c3a574a3b11beea8827c883a3ca865f32fe3602a6343cd7b27cbbc8540f"], + ["9172a3390248debad387849f7135a83d84cdcbe082870630911dc6d5308f241d951cb5311b0300000008516353526a6aac529703eb94b0c1b57dce4766ca49b10ce4c0a697c2671cda22616f5a9ef596d02d041c4dd1000000000451516500fb1462cd04a194df0400000000056351acac51375dc802000000000753ac6aac6a65003ff95c0400000000036a53639c749c000000000000ff05f21f00", "0051630051", 0, 622476469, 0, "0902d020c4b4e75953319d73d972881a42a4f87af193b962f30b32e1e4eeccff"], + ["030000807082c40302f216d6e3cdd47574833e40c4fa00e7f8f12a33cf66f75bca54ac4a0baef962f601000000055263ac5353ffffffff0d013f1f31d639a653f31db70093b8ba2eb9d13f699bbf1bae1165758aa2013900000000086353516aac5263acffffffff025aef47030000000003005363a109dd0400000000006d900e2f74eb579302a7fbd803000000000000000000000000c18acafc67516e7626bea83d2e0bb5bb7bed96043d69262c4c3fb2d0eb31e6d11af153bc03e440970fb0ad3e3d830ccd2d7ff6cfe7c97d3aef8dc5e65e293f15cf760b0810b5ab6593279b4c15936a81e600dcf8293af48d8aae383cd6ff54f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000358a6b6746f7e83bb2ea7f8367651ad4721dd1ba04422a14fcfd90b3f382fa5f7fcd0ef3c58d2680fc3a9235ca017c6e7f75087f3adaf9397bcf3951d47953908d3103c49cf16562b2ff154c01d63966462fab8f86d3d13dce1777fc614821b8f413d9979c52c4d3453d9e453f2b22a121652fdc5f6b3e4ab2e918c60e429ea4030507e2f134f60d03c78a4a53ebf488b2d0b376d940ac8e5aa51e4ec5451e21b3020d92add5eb6791ed15a57ac4b9bd5df5c5e7c08a28f3d8c21684a033385e3e580b077d70b63b31565bdde190b2089f41ee6737d92e24732248d2dba5a35ffbe6e5c17074714c99d08e9f2457840b98bfbbf9336bfb0bba96979820e82e606a76160309bcaa60ec868b5366c9621646f5c2d884515c4469463b84ffc79abdefbe966b032238367a2553f16d5a31aad6481484e9dbf8a3c258fcbe2bb12fdaf6c33131ab020fd66bf51197040ce92698a006a24a1b23d8d9f303854aa3c7537be6e1cf441b0223220af4cfe3db0d6ac3797defe583e4ed1d85bec22765f054c0e4dc839481ee031f5e26bba0701eb3c728a4f5725f7df96745fcbbc1190607c0657538758a8a7d82badaef2952d11cd7d23c7156cc8bbe21de46a67028b61a96acd13170ccc69493c859d8bb068634058f2e3f68502ef6760b001ceff4449e82b35ad6d7df29a11291437eb69389a1c1064136f56981be8f40f00c5f3fa37863c465aff6580152870dda23a26a7e8c002aed0602eca26e6f49212206e3a1813b1efd5bf13642f9a59effb0ecb33026f631ed9464dd72cdf8ca7466fa2fae79347a9ec571cff04eccc3a63d417c4d8dca57d98ddd809640e1dd5c44da09750e2d015b14647d8681fd1235892c04e532b167fb8822179c2c1ce3633c0ccc988e73ffa73ae46eada7fd6cafe2e648c856187dc9f31377099c3f9751a35c44f0a43462572ca3355ed86b99bc8984a329d392d2b17a3e7668bf8ce57c5f7bdf7014311127fe2220981d1f02d6aa81353cfb377562fe67462ff85ff5dce077fb2eed642e3db31f1c0d3901aba32ac544a38848d8ca7f9672035867f2a2ed0c1f17a171d8116e6391036d0783b744e185e8d255369820468ffdae3b441e00653f40b1f800be83b5da50ca79f5b4337de286c53ac79752a5a0217a6878697f619ff897af75cef20fab627797166c37befc07d64d7bb831662acb0bc7a4acab11588b1f7bd66b1626e8b4fd718a41f42063d2b257f63bcf78c6992fc7699b28b723fbb645c1fb8231d341728336977d7a3d6680f994402bf2fd3cd8680dfff2fe18c8523b8fafa9bcb8eb962860f7e9504496d4095fab9be9240ad07565efb24075363d122d2338ecaf72a6aa0209ebae275f37364b9caf5b7356d5bcdad797bac573f70513124e11bda8fa7351c83ee26fbe4521beb737c33b139348295748642988dc8daaac812c7326c319fb3a7e4893080db7f3a3974dd44b8635a41b847e70e45b13054e6d04f8ee19b9d5d5b2edbc29da3e04ba676ae25d1ddf3a46aa43bce9d2191e623bc075def3f104c7ad8fdfe22041cfebfb338c12f92a6b978c97498c08fa881385c9468b217873134688592fd9eb3f0a6ecd8dee7da29e1c781d5db2c2cd5b64d6ecc754b5a6859d12ff2f4af27673810f58a04547177a3c049d8f6a2c922f1336c6e86708f2c9d81878b46c7d354b21146693a6ab0dbf3b6627e536ed6e8e3ab766472f8dd80aee3d75590d400e70aa64f918df8802ce5f3d1d47f70d95d7d7669000394d70d6cba766f34537c23feeaf72d0698d242f5596a219bd9d2addd9ac1da22ccef2d0f49fb6687a57112a3bd97e91a471d482d01101e3f3bbab1f6e37a3c63a681796064d68965a08d911fd431077a8512131c14749db9c00751be3bc6233a251ef08d8ebb3f336ec061855bb23d1b42900332831da214c707b3a15f321796ef0de70cef6e7efb6f79774d8000d01d92f89493a7430b6a5b611c3d1c73a1e0945064484adae5dc16b8cf015b6b6c3e70e1d17895223be90833de940358b83cbbff41802898eadb7c113ade9134352caf27087d04c745620bcdbd02b5cd060a85ec2e60c501b01fa9168e235ebf7800394300c73c158723912386d1da30d9374f23cbcff150cec9ba4c2e09e3df872d6bb786756ea153f2bdb7c8642d4c4a31f927057075773c443ea0870927f8e76aac9076a599d78fc05e00ee242085f24b10e3673f4169610f693f51841668745448bc93ef9942a3588162aee36286e518ac9d601613623849bdc59d83a124d9d491c523900000000000000000e53e104000000009cf1bf6b208b50559497a31f91732fd3f29675ddfb5e26eea7f88fbd27a104ba36e3819145e460119cf3712a219e4ad5b46cb0f650ddb4418ceed268406c84d338a48ab153238c77e73181cf59f338701859d0df790e5ca98e4702330cb5373e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a3488dada63d2f5520dfb6d563d8bc05206565383f76dc613a094bcdc0b49914a771b7a7677d6ac9dad6c295db8a8f70df36f1e1475a67deacdea38c79919785645ff0039211e0b332c7ad4a9249380d700e45e3877d4ef949feeb26000c2b2ecb854746242b906edb5236427c7e5a4db63c1f63041678b2a71c9665f51b11a50216e3e151a20103185547cdf9550368eecd187f7408307c4eb1fbd20086e8b87f022e5e6b057456f55f9cf09051b598d0e3427b35a2c0af5ffcfc639259f83f82610a068a66f7630969db005142d84d11d6c55a9f6514e7ba193ee7d70c7ffaa5177c91b16f5bea3df5e24c54784276ceec4de6e6655032d8876b7efb06db30897d9203209774a1d02cbe50bd54aba976edb98d98f0e57e9b93693198b27eb0a140ed540309a1ccaabb67db479fe35e450ee9a063af5fb67bc6628b09f3ca6566a1dd4191032e5c82169d05d9c9d4839ec7935219f946941a1dbaf3a531a7c490b552cd00be030f01eb927e141e3461bd91887328642455ca5125429a8126982caba20bfd6467021e02f440a5615f8775cfc2bad35044acefb2a0027162d417190143e97dc609f0b12f01ec12f21ec8c03aae312d5e94f2f79a1ba1661053b4cfc15f9d5c909548320b3308dcad892af20eddbc1aaa770f2caafb9ed31d2c2c813dc1032f9dbb75c612e3fd06dca4e78efdf75c5965c18e4465cfe5e342d79279f4f7f6a0c1cbea8b376f243398c7b528f20f966d8401d5f61a9aac032d933fc32fd327b390f9ffbd05c35bbfdb5c8ab784be1190c02028b8f81902a73512a7372b5e91131bf924815ee2ff87e13b6d96f98271fc60408567da281cc136d68a9e3a06413e7492760caebc8d31c1e82c1e15238a7a7c33d2b91f5a34a7d5af298118227320abc216446e3bd5c776b1a6841cb8191b5e2d9d1b495b7b4270877e7da8f6e1b1eceba0274a14c146aac07102edbf83b39fafd7614bd115c036b3d156611ff2406ddc7311bbd0e6909cb5bc80ff0d430709f1a458ea880c8651d8a4c022ab8bef56a9f02032a9e846352ddf5647fd4d7eaa94e3b038584590bff7dc2b01d2fb48d1cf0e8bf6f0144b14b697a53c90dace5c5879b89ccb78bf9b90a20d7f46088034cfccb532901b7422e3c17f55f8df9d1b782dd9df4d6259bfe4c0e7aec3ec4df867ff14d66d697fd211d53f29ba8c7f30d05e94efa1396f560663d0efe8eb5ab8fa2375f674acfc65ab56f30472318ad8ba6f95b45ea402efad0f2f17f06efcecffa97c0194d825130aa88e6f36d3b8b6da5cb5bf6828473dc5a4a1eb1fbc8636bc5d62e45d0ab55c9930ecdd2fae9ff563d01caf6b32f5374cfa75d62c2a77b3f701a10c2081649d7e6030852d5842acd778b69b658f72f140b76e1598a217abd1d635765538109e876a2d49a5f681dbe9ce7641ee11248a625ed893884ce90a33714e317e6f3bfe7814a1c09c169acece2ebb4e8f88500fcdcf1d49e0165e1be146ed7a129e64b1dc1da58467c932f149079675590840a22d89cd0654cfb9717054195258aa55a4d85a06cb4db935111584ef4b1f1041faf71a3a0b8d84cf89c8f39e8d3ef6ccab3ed539a1fb094ffce70fa8a6572f5933dde2cf0dfe2d287dfc8bbc56c6acd7a83bb4a1bc82f399da3074626f7a4df11f71f929a177dfcc06893698a7340147a332719a49dcdafb205933a9230f8fa1873a58d08478b069163290b274366ebdfa17b7c99464d45690826a1bdd2b29384efb27d43f7f64182143c248c1cf196f53bc5299813cbe1619c422f8daaea5ed227df209f4808390d57cf16b96c69634a704310adbae2d9724f5cc38d702ff0056f981a07862fa880be65168656bbb233979d27ef55e9a6fa740210ce5f1e53f42ed3ed4c4aa23db839025a762d399f64b61ca7c188208eb1f7ecf8752516735ba7193e80b60ca4a8068dec563cacf3c6c9afd0986d781274b255338f643c961c81e9c2982756a8d9dbcc1ee5c3952a8423d91f742e9084f43a0427c3d7de9e32853adceee8b4389416854cb32972716a224a17f46544d8bb93681b07df1cf6be92f44ca0d90513394e56a6405a9bdec8c70d165bf526891b00f0aaefe56afc61fbdd2e6b04e7aa43a8f66c2473d99a520b166b4bb46d0828155d6f9bd565081daf402bc3c48706fafc94277b32c13d308038deacece01ba9bc4eab0281d0d8fcb0ad862db265cf0d561262a4250e670b5db52a723c97249870a98f647e7eaea2b35e36908b660fc60d502b51ea081aadda7abf647f702352cdf8ff765dcd3fcba83a3aabd8b8aded0be45f87083eb54cc514e17c8e734beab78e680cdb43cffa728087a2dbce873bdb026b41c5f2b35a5c525d84c525f9fbf98ea51c55f1355392beaedcddd280ef025e4e98153f161b98c7ecf48af29cd6477867307", "", 0, -694020789, 1537743641, "eb91b5a82c6db796777583705df2d1821b326c68b64cabe38a59884d54f56434"], + ["111f010c038c4294e4da4cb06d9fc7dc1d67904eedbb5e6fc173df1ff6fd7e87927e5bf7e603000000096552ac63000052636388e64a004bdca5936743c3c504a19fe29516c4ecccedd06ae9fe98862249fdc7d2e302fb00000000016565ec733c7c49f754562f0689280ffc81ecc8875b226f98c221fe2dd710c59dd7ee63f5c203000000085151650063635253229b6e5802249ad40500000000036a526a32393701000000000663acac5200650000000002744357050000000000000000000000004b1ac16b7a37da77528b1fe4fe2bc8eab61f9930e99eef1d8fdff92b16dfb21a72154e0add5c96842d1cbc3d0ba26c2328d73a1b56728f9ec27412895edc6189749f16aed63310662b28a45263b63dcecd62e8296d5fe968c82a86afa90804ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2878aeb3d3fd6fea86e2b4670e34f5a338a3e01ad35bf43e064ec059c24af46ca56a149829e5e2d5171e95c70c987fe3eba69f6cbf5f380ac7a8d55213af86e08b3f430c54c70a4c55cd51f94d3cb17495c9d6fbc70bdffec08a161ec09b31e45ba24b8366714a06331f1cfe22e5683820d10a60df9c4bd1b69c2d439f7fb0f0202a68efd9a948501516da64308a8a8eda001c12ad09f3560902d5e81551c04ba0302727ef106116d4a5a55019017fb3081fa1380e0f8fb8a2346f75bfd1814a5f50b001e891a54b41f05eae416bfbea56707d85db34dde04e7cc59c5cc97cab3b18be3421a8a504a718aeb2c7aa5ddfb3f828dab0fa5737d8666960767da5810fed702134215c422e958dfdad54c2a65254ad244db65b4bcddae099b6a7e6c2bebe3b7021ffc157ef42b8cc0d80d5ebd85a7208cedc4b3af4b46c9ee6b7419b886c4656a021d1e71be65aa582cbc17e3bb9798d668d34143e8ca7e2b6b1fb0d75263178504030580d2d151897f403cd5e21d6b9a6f2ddafec8230d7f86652c08003078c37a8a020735e9cdd068706771a738181d4c459b7ec27f0dba6acbf26e39559f11867de6e85f7fcf25899449f8f6990b791528df57a00f479f486a7f7b8c2936437426efad969f71ed78777486bb790da0252f4b64c32e5a847b456050bfcf8fb15390a8d6e756c2f974dc671d510c6edfe122a02e521859b455942aa54ccbe1c02ede62374dc0e203842de7138d2494126781b97aaea44d7a09069ade6bee1476104b06a796aabd95e97d9e13db960c2e43770b578cd190535b3b01b320511792c52b231a66c1a76e4e49dba99b5e67d92dec21c7f33b66d2326ccc6a2fdc7254d2e0daab33aa59ed6369627e1715a429b802af1a01d997d680b1855dd0b0a6cfddcbe6cf4f421c53dba97d63c661265f4b604db0b03b69cd29fb1381b423533e514dc66f1514c4322fef2687992b6aadaca202ded7d51f5d109570d07f27e5075788ee0b310b886c9fd62e451ed8670a18f0f6863b66e1bdd87f8d257793813d32634366e887c5093340e8caab1e04984e662aa0a56ca1db6e6e5d5c5053783f55710985a7a14878c0d92c4bef49cc6a31c7676cb6c3366f97b30221722064a2d65257617dad9159308f71ca9fa11cd2d822cf2a5965ca759e0cb35eb65ed19cf38d67d8b939120248cff48a4e6e9344a518298ee3e25045c2901f2a4e0e5265dbda8b5e3ee84fd8b6f933c776d41cacb3c35902b6824792c9794254eb6a496b7b860baa399d7a2740d8ae6622409789303972d6b42aea7d2f7d7860f28ce3eb449b038c68cb2701b526be236d0a8374950519e73608e4d869bb497e4740998877650000b8a5c3da062cf1a1e75a0f0d8216d3e44cb6470d3e22dc55987709a6120e11994445f18d6b5c83f29b8ccfe3f4ff88e498727ef13fbd39077e16fedb29a875000f687586b8bc3d8f610d12452552a6157ef1251798e61febb1c5dbd652b1dee84df4bcdde60e8fd2b78b375fa946595e74b385f87ac9f9c0f118c1f2b07d25e57a27fb473bea3b042377b7a35d2752f9167680ca4269112896986bb08f40bfe4ea1d83c1fa0aa6138de0fd6261919b0c82e1f0191dd13d9f074d3ca4f0b77ad4023028b566268bff211bf35066a17eec2eac9ea990ffdd9e2491849d1a13a61b149ad6de9c4b3147f37ffa1c2d7fac4a5702ecc4948f31cb0a41886c5f5b314d0228a878bb4aaa41919f45869a95387d2f8e1ca5674a7e987902be60b91bffe0c1a2e5c55a2130866ebe36276889cb1d1eb461fa275e798fbb41053ac6ffded7213ac44457389e48775ed39d787e4b9ab9f72972d7487bbcd8cff0ed36fcdf7c9c12bec8bfa6791850db5b59422b8d2908b5f7c36146920cd21f24ea1e4c73b4c178dbfd3d6e56b592ffd93250e022606ad1fb3cb762b8e47274cbd58b9c48ec3cb5b2647ad80c289c9a6348ecc555e257d9b863665007ac439341d79dcfe4dc6f6c4a8c4be28620d1e64cb26aa6c3ba6b9dde06b7dff73a066fb9fb2896add35130c7bd22737bd2aa0f1d778a9595c2f8fd91e21b08b441884fc4f2295bfb65eaf581cf31c24a0724ec5cfe6afeb7ea1317c339a40895c20c1e5fe359bee71cae588272ff9fdfebb35f5a7939e3a446fd34dd389d94a9d46f87ee9778e49069ee8f40ddcabcbeac1a716c6463b218bcdff59f2429c2e8ffe933bf9e5928cc8d716c3e6a9833c36defbd3efc6e0711e7578c0b5b7922db2c0e15cfdc7c9b1494cd2434c04f46d68de0ca0c47695619d7d83ebe07030000000000000000000000005faf2549b98320073e1a4b451769fb2ff4776be93961cc76e57615b55f1defd23e0acf78019a7021472dd5c9f30006c360a47d204221f17f9761a7a93a383d67a0735e19ceb0d8caa65b2ff30fe04860577937774e0fd05363a32e20d8837f820000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078ab9dc457db4cbc765cc230f4c7bfee6b20f788a1241767b8ddd9c6828712c2adf985d45b916e21769fcea079c8081e712fd0ba6f0d665a7740a126e671da6a9572d54402e55babfcac51bed017508a3f60077dac980ee551fe47864cceda9d5e5d63db816e29540e0a8fe0c5ce1f136770f2feaf0a65b100edaee1f8aacfb4032d50f804ff0e262ca859f8cea40ba17ee037cbf0b38b94618c0286a8e9c89ec0030b2e8a6080d5380b076cb70d476f2fff00f991912270d81ef08b4025376d208d0b035eac35904e3e7586668ce9af2d66d4de86b2f12df90a59d4c014e335c417a1d80e721bc4d35777ac0e8c94d5ae8716adab1147fec5d9cee39500162cb4b3e803025100865c09632324a3785eddde1dac3ad1b8be69aafc01ad2694bf0977446c022fd650ade73cff124fb0b727efb66c7e3c741a0ae6e0ebcb9eb3a377eef963ad020fbe203a1f093d1c284aca5e47f54431bbb88e7b9585bc4814981f7a942f6c30030dfaf4fb530f24c32a19c13be12dae725741914e4fa93494de554874847acc20030db4c17fdcda6c3e7606f6b11b6664464d252126f4b0a401477114f5d5e353b20e68de48ddc8f80f497bcd327d6a149cb404ad0b86d948ef03952b4dbe89de7b4d0167ca7a4c5631008ea31647a78c9a09d36b35ab1eb66e08488d4a71b7c9208a929420b35ea2d520c6f5ebf8fb3ebb05d37411852bc6199438dc90ea4ae157754626c6c1adf96186a4822c049d7bdd69b0d084f714712594167a2e52d2dacdd736c5719f4624b16bad8ea63137f3ac6a0a02bfc79990204d25f1881779f8ee5cf3a6ba9f9190eb2a77cc205e43b053f53d76abf51728d4134d5e790161ab6c199931a4767b1464734db0a3198e7ead60480a6118eecc31d72166b8b150037d4b06c5e97480633dfec3bf2b0a6668751d958fd22f58f710dc4c25683cb76381ce60a146a3989453ca2340201903f907db6451759c39151fd24f32e455db687afb63b28e83c1b05ee248a1eabf5f330af5e3fe7d3bfb94e255d385a05001068d641d80929ef5e5016167f5f03b8125b25b9d2a2c6674b186af8dc77c61b455e5a36c8c760f25e92c2001af3ce16d37fec75ae0765296a734f41f03e216deecf533ade5b8fa1e41fbc7c50cbd6bb4335cf2c98dc2d114a17783bc462db109b2765360f285001dacaead0065a2ebd5ae939433ea47869bc8a02910a147ffa4ac848889c1c71c303fb0738681b5300cc7f09e08115ce87dc0567f89e6890991988cb5549b262e4ece3047252199164880523678c0c5057a5bae371a2563c3e3038a698e49fed078add31af52de9d7dea29bd664e10136303b35e0a912801f174f78d4e3184d5c1ea48d4252468f97d47b1e64118a16fb8b0d2caa2ff1387c819376233fdc436e7f9619b34160df8f147d0089661f41067d7601f905f71325ca4ed2948c1f5320f05d2db457eebfe8ea3765e025c32c1cea5e0cb90c0431a97ad326404cb16053263c4fa9e9dcb70945e60d76496db7e9808b4c276abcc8b71b6c13a84b38bc417c66b1283b2cedbb86db3bfe186409b028ced8323f07c3f808f12480b173a20421b0aae29186ece8c180a2cb5feade90ef5d09a9b064fc71bae8158ec86a3a5f6b1ad6763de825273a4596be2288fbb88d13cb7c89238904231d9487e55fa9854037c24c6f82dfaaae93a21c7e35564e2d020af9ad36d590d51583c45998de0f78dc02c96c37cb1d4e05bea6a2f9e449fa72ab48254112741259fc819947e689e75f0c87777e27e15c2c7903a5f8eee0679d54a4cf20bb7527571ba8da7b1534fe3d31ba18b9097c3bd2b455659616b3b794dd66bd97baab2fb983930002853160bc97f88601fd881c7c6a75f2f27b3792784ecd16db79cde377594aa59ae9c1cbdfc92ebbebb982b84239b43afb2d6f655f25d457b1aeb32b3d094bffc3622ad1b5b60cdecea2bde6fe387500926d235a81682a31badf4211f7c656225038d6f4efdde134e34f45ddf89d71553639b4ac4b274053b4ddc45ef897d0f9254232c78056fd39e434297f9e0bc2ff60ed223aa2dd6ebfe717909405d92dde59233afd84ad5291185a0de6daa6abb0ee403d493337fba183ae3f89fd0baf9ed07c1860aceb62f5b76cf4931671aada0c0804d7b899191c863d4e4e03e3dde5cd8e4dd5328b641c200fabb8309005747e30cd82d456bcef1f04e409c57d5a5aa7b3d64f4e89f57ef1a0f065b5caf2e32a457ef2655e5f3c0817168b4a2f19cb9569813d15943a240d343b1ff030a7d359d7deb3927126982f8d2b85fe666934cb880b1bd56ff3cb5d4d3020a22a71a3b5f1f739287e4e229e55235a3017d2d8dc8a34953a2003c14f619f234e2ca64d0f9f0b2d93df401cec64ed446a2d95148dba461f6e022021d57a0083b9ec1d0a", "51515300", 2, 1510545828, 0, "f3d48dda793f6d8c6a22ded6b041789354c98e305a9a90913e3a01e8ee7f8416"], + ["030000807082c40303d94904561b88e6c5a09505ada3d18d8c6ad8f5013e64267cc54e33e05d53d219000000000253ac0be9d207c0ec2d13c2aec5cd9ad9fd98f4b6322e24ced7b429c13dca5d4f09688227d189020000000552656552acba012766a71460e400aaea7d5f1335e05acf6360270f752cf2445075f37ed86af5e5780701000000085152655153655253ffffffff0342c83c030000000000629d11040000000006515352636a6a8fd602020000000006005353ac536ac99a33d500000000010164b800000000000000000000000000347b62e8d36a860095a2ec866b1a0e9b8e820a79fdc6af664ab8c599c882511685f65e3d9eb3b5452e235d0404f589bf156b3a9cfa8ae9b5d96995834f7228511be1971565dcbdf0989e81f5ad914e32fdeb92fae6da36fa79742fcfdc048c6100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000486dd7707b5f90e20cc4583ccf26374af4df303a278d052deb78737ccfb2a6e951d0613225634a67c915a565e212ac22b88ab7cfc9384e859fa859024aef86b6356023f2bd8bc79aac17a89d42e1ed7b90e0662d2e2edbf099918cbcca9b9c9126b370d7e7e3e30cd5d045ae1fc6c6da7b0dd8fbffdbf818096ba4190d881a0703188bcea12c7e84e366ee78d8cee6cc3cfb72c8c0b52e1c3696037ebdba9000350320780339f763a006ff3a95a0f1fbf29fc5d3beeff7e4fd15d926580839c9a34f0b084420f781a866c5bbad3722696e90d3fd5486e4eebbb2749181c80ba0e29b6f3e9ad21f6a9083c5ff3e8c5179f2f13ee12e5e8bd2f017d0a7464f79d9b3b53f032953236378e63e19b75f5d6d8ce7195723e200e8470a8b54c9f3cd2904008621021ed70ba61d6b5b457bd1d9184970fb7adf0dfca7e09fc9f9adb34f6da38c11790204c944225b6a7586ca70d432d126fcaa2890b7ba44628c303f85322e6b9f452b021d928d43c8dcde1e1f4be5a1f60bf30956125df081bf71b7fe46a386c8a9d6aa02029365fa6bbb11ecae16d533192600ffbe00b66e98777be617ff4bd5c308aa47f0549134ee8cec2ee5b5dcfba595cbd72e25eba041539d0782cbe7618170e6a587ef0bc735f8ae616db0d4803c22ae0318d3fbd3a93341b68dedbc68bda5227f9315c40f41a8c8a117c0d9ea298c5d9d0a00b427446ad4bafa8893ae003c0cabc945f582ebbe8c32805ff5b992ddb1d3482fc86da8cd42e227451621ed3ebdae7044e126dfd7278d34a27514dfd8a70377b32d27be17ea8155366c421b73490b4c72cd6cca1eeec0c8560f5765f225326f56dbce84f987e983c130bec83ecf658dff7a9625a73c4746dfaeddc34210cd3e8e545c032a3d7f1e9816a8a9803a61f4337db51d07ebe6f296ff0c4a461d7a00aebc83a734751394d87b270546f0e666fbe46b4dbdb69d989f481416cc5d4e14031e3778100b9abc98d911de2f4bae487335e60af222729f64751512d8abfb0c0e625f54201f814197ff9f9b04385000ee817275548898a92badf86e3f2ff489a57a69033d0ba18a6fd7d2045b5b228939efe1493dab9d5db61a2af685b639ad0a866bccd348346fbd6fd65810f2cc498e2d50d953c145f8eb43b6afd301bffdc6568b9fa889fb31caa609d0bbb68375e91911dfa0b560e3413dc3e9ed63fbf08c184647804abef87b336a345694b8164fc25d90cc89b85d5e3d63007e34b1731432b925caeeb82029c412588ae5a635d59399586cfd194a0fb2f3bf006abb008b85655b2d6160e002691e0edc6e301b928f14bbb1aabb482ed776d1f0becd533033a0a070235ad98eefda476f26a18d0da419bb0d8a0da61d2224299fb04d0da88c0c6e9ef097cd4fe94e53dd01c0ec882c9d8d1d8149a9b2a4c2b71fdecd7c09f8b7fe533de80ece6795ee445efc219f0528e3d614a1bc1e7dee0133d5da7b0724109fad1c52fdd69794de146b6224cced92601df66570bc1a9a46b97cbdee8a62f297bb24b72d8c662ec1c11865aca842a225aa5f768b9a3842cf307754ace6f46b0dbe5639b9dabdde9b45f8c7fb178b70759ffdaf3d749a294fc2a40926728905df8e23dfc38b0368f8402a25289b9a6ccffe3fece5408019ecb4a7a2eca45d34b091b6f7400361daaecefee6d356133b4c524b9157f2f190bcb4d7facc02408c37730f9b0d299542f773cace85265296ff1d26b7589023dcfb9d3be94fbf1385b4a10c54341b01dc52fbe3d7cf86795cdf692fde6fb6d6bb91334bf79bff0852db4dee42ecf93bf2d08c5a18123dd3330d1792fa79e7e7f532610e204f4cb7266209aae5318302f1bbbf9eff84209ef29e4ffd6c632cdcf10ab3e4135bf67edc45c1c8e834fc0a2bc96c0605869b76747445219a6d3fb0cade1b03e00591b38a5f77a304007285a6cfc430f016cf731c541811f7bf254a5a6ae7a7d76f2835f7c80338502e3ec0317b528c6fb50e4f4dd74c06f31cebaa3e115e22f38309064d20c1c158c8002d1c1618bb5a0012b774968daf317db73ebdc70d6dcbb3b0c80ae7e573311a0dd919e781926b661c9ff22a4402d0493c830a54ac12807847cddc469e229ab579bccde5cb4fb52eeaf269415e29f862c1e0a9462345ef31878634e0d4a141c5d1f6295b6aea86361bee3e38a5a8314f8fa274558b825b377a7fd79a202cce00cd4a887eb84c653d54f3525336050bb9342825a5e980f97b2dfc905ac688d263fd40f4e7aad6dd5e20d2d8b1524ae9948e53e5109a47577243e2d3d87be385138a4c70266d71b919ed833a4304868677498c1850a705695cd040329975062632af38f1b79aa87ab5572d86d3e3a492ffa9051933f0188ff21d939d441bd033be9d902970c0f6579c962dd7bd9bf9eaf449e48fbaf54df8d606", "", 0, -1338298954, 1537743641, "1307fd92432cdcd615b6a4c35128c29f8afbf773cfc0ca7e6b1062563df903c7"], + ["030000807082c4030307b7be7afec7eb81507e46e55e99b2b9c7ee259e085563aeec6b22f7144d8b7f0300000004635163acffffffffdbd521f6563084cbcabde0a8708fcbfe54a6f1a62b44d4fe9dd5cb20283e1a680000000003656aacffffffff06dbe0e6cdb71767fa3b25d3f4f23700aac746128be7b7f8c028c2469fde2498010000000953516a6a6a6353ac51ffffffff017e5a800000000000030053ac00000000000000000140567105000000000000000000000000117dea80a627db26877e83e88b2ddae72873803da7778e6240c610b69d2c45675512c3518fa5e7865af73b7ab616b6823f5ea79ca666d44e6e6ba4bda8357291cd95ec278f6762cb8c1e46eee59b141329639b8e3fdae5a2ac82c35386d6d48600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f184b65d914837561d562390305885ed18378cbae16ef4c203be3345cf14fb41d255ddae97f1751ec7c90d6b6e6e82d00af9bfc7442828615b6b3797dfacc406c47493844eb0cc7efae02d61c11edea560083527127b881cc841be1b68649d4e2db9b36916a3e9979d0db6be75695914c2694320e5cb235e67df520104c3d38b021e550ee03204405aec4a1e51610b3e8c37c687076d06241cbd12c3dc6563de13032a420e0eb05efa2319cea1ff1d2a0a2754dc71903c6cc2aa979be23cedc2adc40b011c9617e8c102a36c81f69f5ce91dad05e756bf75ab059ab05457d1a17af2f9616d187ea37e58ed91776d75f8e8dd9a6111fa88bafee2a95357450b5284905b0215970c1300a63975b660b9cc65b4c0f5f03402f0b23b7e80a800a066349ad4ff02063e38702be128ca12a3601541dd051a649913c0aa4d6c9840927cdb753fd204022b2678567f3db74caecde7478f3b847a2e6c42efd870ef749173bb22c027c6fa02077c77cb740286602491614d10f34e1de92c6703d57b91c0061ea27d88117d53030e3c8a4bc8138885def42d29ca2d4eb45470e8a59343f95aa2da0509c4d1fc8568ef4ad7119f857be19e83b15b662d93c9fe6ac0c88c60746a1c0cf02fb9db3e687236541f9d07b2c89d9e1ac633c7e75c0be75bc9000e39ba44fa5a15f976d9e910a5a709ccae1083f0d0b50cecd410a3bb581f7488892f6847d6a06a13bb3a7231ca16323e1e240e0bf74e6c3fb4da53d23c23f6239bda7202d4293a3ff11e7a5f1d24068b87580555e3eec9c077cc9bfebd9fc1da59ef220d922ba2c5b3e33992b3912687c9be79e8b78b87d6aed748301c7afac94ed59c6d8e568f9eb8416bb77d613ba48c387c1d7ded141ab0d0ddc3a777b5ce6aceaeb0d103b09ecb7a703392111fbd3991128d0b45a5705ce1705d80877f139324aae743bb72c243275ce2cf1d6a35fc2ace2558b82608fd69955642d21385effc06d208ba0363f71e512714580942c072a14f6c9396d1608b18d3b93d58410589121d21feecbbbb095d7d42c1bfd92f96cbae18da56e95b9b8964af2fc442fc018dd63095bb983a61487a3018173463811293b05890d24cd78ce2a7e6bbc9892a67b5fbb5990a0c0cd5d3ca932d2143cda2d4d2690c0c6afcb2770ffbdfd2c65922ce565e852f0d36327ff61b617a97e73858426f8737d32c0ed54346ab620ff34a7a55e26f5b367f02c3253470d1874df42ae040935537c61bf47f83d2aa76bd0bfa2d777a277c048cdb8c89936f427aa98fb410e46b852afdad7f91ebee58d1681d03abbef68bdb646e7320a3785841bb2735a3447af5ba327c1d26c9ab96ccf0e9bd26bba2fa3cf1fef1717cafec70317d32211f318ab21828f06d01e1414ecc69d74d43491120f9831e003331dbaf7bf3da483fe7b937329a880015b20c09c3c43cc64df852eaa0ca65d03edd22a17a175be4460e4992937d0f685c437d2f4f280d7aa237b7a0e939b3eb28bbacd821a6f4db61ded0a0798936859fba0deeeed6786f1c1a01e491339d365b4bfb1105fb66d734f2e2c731302e786fcfca8532fef381df56a6408b34cf826704255cffa62af87ec54735e6da018d01b72f5e2c0cfd256a8fa6569be5f5352f47ff8b359cd8a7fd81522d684f009950f9ec1759c7ef011ef095c5e916a4c23c46574e4f83b35146b6371f24f0d29ca3cd675d0e24ae049b09fe4a0bfbd35ac10090b0c2f7cd75c387b6e95c7ce2670a5f2c1799258b0ad53da4f8df114751cc02d98831db13940bee3e04bc11362d38220380e034dbd37520a6c8f529f5e45e656c95b4a6ad4831ae8996fbb1b2c1938c668a7c96b3b7e7a20cde8189a5d7ebca81355d5d63b3754618e28451eb055f272e0053c226e720f2b3ef114c00978f72699e7c422a942111209819bf24af1bf03d3ecd8014c345cbc6f9cc3bff8b576ef0f7dfb035d6bb7df614a0ae3b1289219936c512c1bf686a27023891a25d39fd46758c48201540ad36beba5ee0098edcee00cdb82b3d82b41532a3c91febf98fea5f639996ec4d63ec3e8a8392a75471aee9d859257150208542e9150a364ccac2f0466626ff5ab9b82372a367f33db398a107b773482feed0141322aa9f685ebb9116c7bef066340a5caea65fa989d7fcb31d47a292d38268d32d658fd5aab5a0d4fdd51e2f580c69829efa60bebd8a2bd3b3f3c5765e7befcdf0732c1c6f05c56f88c4fd7063708ef5d3a256df465f480212a89e8f83af3f705a0a645a5866e54f909e5cc48b979c98d2dac8b7ea02c63e7757c661e7c98bbcb6ecc4e0a7c05d28059a7769a6f57ca1915fb4f701f2e4acb8ae1039f594c8999e0bd5fbaf5ad2557093c7f72f86c668c1a15d8318332cba4b2bb565fc1ef5d37901f34c795d6b372a9b9905ae5e02fd580b", "6a52526a635165", 1, -298455192, 0, "043f124e7450a2a49b8818fa3c056f406129a2b8e584506ba02acf7b9646be6c"], + ["030000807082c40301e50c9c183bb6e648cf7115be28e8000efd891fb91138a1b19159140c492a82d301000000036a5253ffffffff04f5194f0300000000016a26526a040000000009005251655365525153f00499010000000007526565656a63636e77ec050000000004520065ac00000000000000000300000000000000003694d20100000000acc0a24fe230e4be7a7b2f89a966fbbbb9305877ee8a868ae568710c197dd371fa3e3799d1cde84e8bd93550ce390057ee244ac9f6024b90de96186be4ed089d516ff18cbf0eddec09d65ea3c71231995b1c23154d252d75b20eb32ee268262c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035d33b974ab4b1c12a84aac0da667687982c25c0fbdd481de13c43d65f1f7ac286903dac6cfb089341f89b322a0c91d3595cd7d7f2a84652caf60aa05383a9e43d07fea48d685bc83d77f9e04f769c4bf8a036ad96cfcd0aabd57840be091a32bc8fbb3dac2f13f49e79ed782af1390fc3079d8641cbb88a3043d7c228e57e19020e00cf66607b5a3bc407ced4807f4f8458552f392c4a143eba823a435db5a77d0213783dd7f724cea6373140597b5107cbc932d7f7d7f3533ccb2b591b6dddf8410b041e3e2e73ecc66456cec3196941befc59ea2ecc31a3983a78e877d0d93605096a2b72bbde42d01e56408ca6bbc8c6dda327c4a0a56fe4ec91300c4e73d3556e03239d8af96c99702dc159255e839f51c264bf428bc7e7c8331e69549564ee0fd80223163f57cfd541e24396d7fc5ce3f353456e84d76cd9deb81c1dba21db68a84c021292cd4e813817619eba2389a0e47a9e080ea4ec0ffd12f166d4887131513af40308b45c3dfec27f87ceeabfae284f6a9cf487c170e0f408ecdd7b208e2e450b7703110c35e1035039e1cbb8f2cb35639f89a62d01b0541f990a5f28c1a6050c0581a38d0b309fb2fc010238a44ded760d6ec1ca48513662fb581c196ef32b70cdbf5ee510d854f8bd41ae48d0c8982a650a6b9aa5506c922f0a7812459e24e569fc70896f58ae00b15296041f014a07c3e1c448c899f5da29c6ff93f258e3abfe3fa60db80c5233280b03dcacedc3b3ed7f71b0e9022519baf1aebbd29137171b5d24bfd456360831d01df5a4ad9280b29fb7a5e6e2d10147166a7d9010507f663c48b29ecd7aa21a011658032f4f60faab844b51c4e30c7e5e6078181071c6cdb252674a6821b858834dd81886056079c6f29ca6a5313e7822a5cbaaef64765748b0e416c5aa8d92fbc2d75c6ae9b59b3134c30b09dcc82a6f4413e8529e58af8bda2dd16bf4feea21d619bc38028c0d28ea93b66a622297a84759b64fbcaa9a01961d345cd71e87af0754817ceef8693e481a8066478c8b5301d4d4df9550dd825b651671f17b87bf832bc2f58d1516144cc06b3cdb54c8aac207be3a61391e210f86d78c65f3bc31d022eb9aeef53723eec439ca0fe663583c63afc934e86b8044e472886cbf5fdca9ed414367a7e96b898e66a372cbd92d48a2bc59ee91a1547101f373a0d75cbd473d3edeac87c730b4960622c36925db35dc32d18fca5b933897b6c6300b73cb0248ffb1efcec3da2bdec822013693f3d4eb7c92aea1511bd183b93712983bf9a78dc3d3fb9c6f91296bbb83b964a8cfbdad9c73fab1945a37518ea9c6dbc8040168403df8c6bb19e59a6cbd38f63602e72649d4e36fc58ee981d3e530885c7ef8b797d5cf96ca144d21d69585ec4c52e0d081ac4453eeb25db9a07048908c24ccdb87244f07c7a540d89c3d4725df07530f06ebcb6b42c26dc5d3833664c54dc1def0e4444256aeb12ba6bbbfae7826e2830714effa7d8f56b1325c5c802e94df6db73281821aefa8784a9f053e1f82322a462aa2f80a1321a5afef1c5aa7dfc2a8f218a686fd8dc4159f243b10e1358002b4d8706110f07749d31b96ed7f1de8620f6d637b3952ffeb1780b9fc70d4c209e30d104b26d3edcec01d8aa29398b0b48b3d8256434091068d0482ad08b843921766c8b6af3f07ef55d85ad26042c6f161c0edb0490e14f298cd96730eb363368900463fabf61effe61b9272722bd997d1b73db74286a086fa93a0db3b18c51c8c372bad555cd8eb69cc5b6e44935724c90799d2f5f6709a3a0cc017c8b687d48e5d317c6c410b4133d801cd345717cdc40e068d2b62d24c9184d23570a27b0cb9c2696799766e66f9511ae53a04dc3c8dc96955bc70d388cd36b53264d2497e38abbe68916465328dd1a89fb1fa39ce5e33165fbea94141d7766110d96c5c2bcae51abf6f6ec8c0efcd66b3259bc4ce2fea018ef99f1f0461f72d23f6742bdd82f1c425e50f93a7add3e6d9344f7b44d7db66ebfd60c009c437ea81ded7df61aebc7a55aec468d41a6246b81cbab6c5fcebce79dbb78fb22a30f03c5dd8eba950855ed7a7a0094d729d8f2859b87a5f79f0bb7202edc80c97ea6a0119c6705d4018e886dac606f052d845650300efb5bd9b95775c525b7f1d0cb306b96c1bd47176d01a789a4f1a2cc7beb8d63751e96ab8c31b6bd1f1dd8728ca7d7e105870f12e5bf734778df036056af51d8fbc962f522d64e67aa6a07f2dffc09248b1cbd7154d360e1cafe43fa6e76f566b2587389dae39ad4d0d760000000000000000d46aab00000000008ac2aef5afaa32a8cb72c809988fcee4e842dcd76d36df53097f804ac61818d6bdffc9fefa461eec0f5f372330174be2eeac31cae6682daf35b0b4ffa1f76acc694d4740d2c7bd59eadfb0dee6c74c736d57016f67b2741b043c4486f48e6a13000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003c5bd84f50604e8dd7a73d19f7d82e223887d9db79a7b3a7bd90867b01bfe9971d6bee0c878c3406f6360d68ae923c76b6a1a7817f9dc5502426ae0799f4b16c55ad61950279908e2dd25837322d58d3e739162303bbf297e8c086d30c904b5653dbb2b9d8d4665c665d97321af8010fddfa72e3e73338e63c9d6ababb9c7d99022da5a4d12f26b2ee82cfb74092dd12c56d63287d7d10bb5a616838c13e566e75032794b52e59c5764217f745e6c625ce4a2af896f3081ca44d6a4d678fd7fa87770b07c277bbb8b12ac7cfee320d3ed3da9f9af51983b25902701295978970fce94f191614e259c99e032e69fa19d04437b862a0ade330fd218898c98d4ab03b38d80308b999e5a006dfae3c6855d2d788dab2d3a407bdaa018b838c354c6bbb14795a0201bb552ccd1220b6b7deb861cfe55a4c87cd4c6d30c6e0d0d745940d923c5204032e9cd9dcb5416eccca2694d802c0ab41cc1474892e7b285c988d3b66fe945f79031339bb359524b8d24d335a204ecac9b422983f6e4533fce4aef45d8fccd4c684032baf1f6451ea4c1115bcaacbc540e4a7d8a429c4be757696f20496892477ecfb00c26ecffefbe5647ec49a68587862a4f2c61f8073f2d634ba82ce6aaa5a9ad86139b921e0343fe4e365ccf199339a9ff6ba2a0e759b191a80105ba86e2ee2a9cd7cd12582d4607d9485d5f5d6c8e73d4b82c4cd4120e418c0acfb38c821117fa77159d84ed4b90211512a52e415579f824f01947d2b6bfecb10c78b1c74684df52fc4f6bfd38a412e79c10e342be02eeddd42272c0be98142653c2b7955b02c8b6c1d7351d6446fe08b412cd73aaeb28890834e3f617f6c80775628296c893d3628f4cb675e996a5ea9071737582c04a7bcdb0380a11fdf86c706292f12b2edbf24a0aa6e14426c5f69253eee6ea3bc4bd9258b09c05d39677bd7127cd4ae093a8ca68996dc6a8d22e55dc425ab6a02fce13b6cb6a50934a6672c236cf005f3f452b915151a99c45f221b7b27b7980cb8b3e2025b2692ba01f049ced56a8960040608729848eaa5673166616746c3b0a632073bce8fe014c87231abc834fc69b08dd154a5e17178c0e2d5bc148df8e6e8922e96fca04a2f22da73c71d204d0388e37a8713d597e7d493f37713f38fb216deb814fcdde9b04ce918fc8281d17d2a204732878a086ba61960c0293879f316bc056ddcfde5dcc3a2fd6a6992332142f81f96b414214734cc070251a6620780e8682af3a8762b5a856332cde5d4ff50620f5ee39c6568f42d56ff3ce82a5f917226055c34932e5017773e28797758f85ddfbc568b3a4bb2638829f8fb1efc23bb7f066a25dce4bc3641a60c83a0097234d5602010d186ba1c79b141db10b209bdfcb122db61b20d3347717eec05517c70011d487c734242bd53247ba2c79601df6f79654812f94edb0e868331ade5325a2d19c6470000d5e7e534282f27a04e20dd99ef9de3c053749659cc672cacfdf6eec8531447eda1968308a1e0fd1f367722bf532fc3991d6898d1ccfc09716636a9625c63ece2c71dbad31565131769deae96ade2abd4ccead147b33bfe40e0dae5bdf3b26fa16d1907891fb4d26f1c98e8260486e022c764181bf907ccc0c440da955928a766066ed6d813452de9d1f450e666d4f7ef4ad21c3ab7cdd4ddfc8134c9b2a90e15d86a67f99a736e7f7a612a913ccf6b8eb51ccf71c1974ccd06d1e522c89775f5ca431b4f01833970d093d937442043c40b839beb5b5333eebc6424f2962eda919b166dd2a2f393ad97b5631c941f3c75ca6a9e673293b07eb2869c69ac9957586a24ea468c82d258fd7fed61723ca4b8c512c081ca5cf737a9831814592c60d2127ae7c1897eafbc16eb651295864d08f2a7a1b09e50743117e4d5477ef4d3cf7ddf797d524f74f854aec7fc99fb1edcbe86f8e8a8be83ed638b8b56f124f93358fe4d3a3027905e257728be953d8a79bcd0da21a436d3a8e778c280a174a6585dacf02e5aaf5029fb93508af979bafe0853475896340d2ac0311e0318c1e09facb8d359ca572efd02232e79d83f0e7ba66be95b6ccd3c06371289e16b5b0c3faeb8a59df8ab00b6976ec7567809ddc7f460723107609f69ef98bfaf28f78221f43d1af72b2bd0c5bdf9c865ab09e10b5ff0782368f8b11028b83209b21c0568e2c12efa31c8ab28e32bf7ef76f03261f16eaf6f291007ecdcd5c59411de46826e7d31223b9f7751a81237392e6143ef84d5b0d1d5c6b450019d4f40bf6f3cb0f9102006fcbf8ba32ec2bb061602000000000000000000000000b256c14dde9372d25d8bbe265fb7ba97bc7758fd15df0ea55b68a5f62f467d91a16de45dac4d9f3c14965c5e27604ee8738398b8c530579c660fae00b131c933249729c4b289c79e20c70ef83551b3f6fb6d28440c718a54511b158656c627880000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000038ff0cff79abbd60b2dd5aed66350e37de9ad45ac187fccd452083c4ab3a297cb7aaa30a8c7d9bfd8985b407dbcdebcb0c11bb8b6040bc66e5c6d0c019139634061cc1d8ff74fd79d1bb9a3c224a4cad0ee1cb72e32050d7be986faac94cdad29bf4c942051b444155ad33da4a583c21c09cf2a0469d5d06f7d02b3e75a8b9e20228f781f04feee74c8e98b321ea9a4a5722b1b3a4d258ab0cb911be90b7b552a1022a6b4dc81488b9a109a84e507ecbb101e1b0866f3a62a9a3553652e9680528d00b07972d806e46eddb05e854fe71dea9a111506ea80a7a4fb67e9d1ee320b263a9cb2d4abdd274ecd8864062a3604377d381147693a5653a1677f9d885b1831ccc031dbf5647ddba4ba4b8558ee44dcbae8f5d441a1738c8528e0bfa7106d0261d1b03289cb64c32732ff3f77e42931e850d19a01baaab07f65dcc2660ce6ca391de7a030a42c8298667f88ce73e108310af97d25ef672299f7fcea5dbbf0e570fb103be0217c591dbc72dbfc4f89bdc3f80d1ccc2f45e6deadbac9cfc280f6ff59053d7c40305a5cf0ab65b2ef5661c9470109499145b07a7aea3db7d11e192099c33e66f6a76da2a306b3c89adc941c78fda0d6187bdd20e382676718b0e944e26878d0e1c798137024cfd01b6057b8044b149923ef400e345c5c1b806515a9dfa0e2b4a5625f99aeca8d92c56f1bd6239077c28ec7e6b3b33b4a7c715aa917ea3b16b6565d86451213f647a08cbe9fe4991158698c99b69b9f57bcdb595b7a084666f1e9930431f89738b9ebab706449b7062508eda1d6d376cdb5f2565164786cf59504cb3d00def260ae35fafd49a40eb6bb7bbad6c94038e5eb270d516e2466d882adb41d96c74f35712f6b231de22e285702467957f0c59cc4eb377bfa4e6da5a45bffbbb1984a2a0a377ba6e3ccbd7fa061ed56aec7c2a292e5d87d5cf4294fa2b72ab96be5c52678b0103d4da47d9c98cef3604ba59e2f4e681492d12eb09b538900cbb000dda2100b1cf583e4007594501b12f7fd73d0f17b69b80ab8de9e8ce65a4a7a54c8fd4ef35b33bbb1be9c09235755833fe21649f089237675be40b72ae0f14dcbed90ca86bf3b24ab9a893b2498b34729544157887560b2881c2b47fc80c11453a75442b3a4d91856bd0d04e7e966ee854c7f87aa657b0cc6a12bd53d3dc1adf277672fe6712e70f29bf8e33aeca15a706f3275a1408a1e61d2d5c607e949ace3e99231229564331ed80d65d050ce2b491ba37e63ce526ef05e83fd32fa753baaa0a15a915356a876833cc415c36bf6579d551eacb9b2f44c88f81433714b2fb724576dac2ad82c7a6cf79e34b8c19b08ad927dd995f30c8d0c75b786d5f7cf69ec0f6e23804b0dcf6ca7f238652c505b877039b3071415c1abd8e9c6169fc39bf99794e1263a457d27cc3b5f406c37461c1e7fc8115b78af0c0ecac117fc248f9ad8a0c7757fe10f295191c902f07a016a8e458a580e74f6892bda2850cacd043959b7125454239125346ee03b912e017076934a36e66768f5517aab0ab8505a578ee08f21ab4fbade9d803f80044a07a8abd721b32ea80ce446f778004a3e3ea0800f24ca1525ef59cee1a14006969695a8fa9c445b8c61c7a08ee70fa574bee19e05b2f9dc64dd6ed149769edc6ab4af46ad58a80057146a7c8bb4baa59a0ea15248a3ff623ec30992d317b273aea7d72324e7c8fdb2907575f4438b300ecdf23c14ec495e0d6ce491338757aaa7122e66a1750d293b7945ffc5d5b1554dc94745259e1b4a4d4c2e463cdc63982a7cc3fe0aa61e231b16c1d58a7333a5ed6efd046aa5d1e449f210e3c2ea9b635882096bef7d89f5377fe7b5134dd435c96888745b9c1a96a5c39e71e58676b13e2914e85ad69295803e6f997fce55650a6a7b85d82aa124ee33c0b77806214045b5cef113389abfb9828398a68baa9fada6ebd3169d26bc0ba4e766e910bc69d431f2dd1effb8a0fb7805e83c4b9825d1426c94e30419709cd772962fdeab9f1efa99c9cb191c71fa12e6fd5f61d15ad685970038ef7f7f4aab6192a7425a6a22f5a9914e9a055a8e47aa89b1660c7439903ceb30c8e788ad0985c0312b34fe25d91431c819f3645f154888eac931f1d766fd6941046967bc5453d6eceb36006ec7d65dba10820fd74d75f160d7e3cc865994bcc59e9f70b8368b15dd89d8269f537c5051994799c5b1668de2b7ecbe1a686d305c8ab89714f07680995e06b961f011da5fad8a5cda220f393e7ba138180e436033ec6b98671409e546823fcc43a3eefa8cecc2506e599fc2e1fa06d569a7dbe27be7d1741642947cc86b73b451862f427c4a38e840a6c33dba307197afbbdb06bd98cffa533da63c958dd76c51946d25d782f918233ec52d9ab6783cf1a87002a94f23cfc3f2506f3c9758609", "63655352526500", 0, 421208333, 1537743641, "81022f706ba645575b3e6776ed99f109949a662bec1acef7964711255646795e"], + ["030000807082c4030356b786bcb5066d1243d13252119a786858901ca202fc9c0cee9a83f6872cdf040000000009630052636563005353ffffffff0af50a7db9ca3a872c6b69dd968a446cc0eff7974340b57f52099ad586daaaba0300000002535156a94ee666158c0c4aa839c2025f68b4b6820e74fcf42ce8887b9a64dbecfd184586f05c0300000004636a636affffffff03a63fdd03000000000563526a005164268a02000000000163b720ca050000000006655351006a518efeb1200000000001000000000000000090423f000000000041bdbba2500417a5bb48c27b7e831646d9ea31a599e12ccb78511d5c1d7f189035f140ed0fc546d6445bebcb89a35b5ed5786800e655cc6bd87fc513320bc1568f73721f4673a970f0a8c25c39df1c855c86ed2ca2a8a42ad4a11247876ceafc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000094444785875a9ad4da6225173f04d8c8a52cc0c99ef4a027c76d8c9735b88164da1c03422c4b528d0cd14488c28073dddb46129bb51f30dfc62a99d079544204ef209e7be7b45b41140835805bc02e8a8fa0b617b99609e5d423aa85181d24cb455dd7704c67f4aa4b83993e3b0cbf7990d8ebe793e66c884dc0eff8500f537a0204a362d1063de5f3664518db8c997e2b1f487613a0c01a50e802a7b48cd1ef2d021315b00313b21a7d4cd4184dd5392e7594934ef90701feadd9ac174ccb83387d0b00df2d57bbefd807d16903b6e5b5e53e2bab19acc7e27148eb348b204a5186077941f1b76cd704664508d96b04ca025661b49422f92cc9f4df5fb3500dcf21f9021dcc88b1c1171a1251d7502b6c3184367ecf3ab9efdc217b6a4d703ffe112c74020ea2d9e62629da81459228a1f82548787948f87f272001482a7bd3c72a8e9cb1031664f9ce90e8a3e536fbf12b52cbeff88b3b6293610fa7dc27e2badc248811fe0323e7666b96a513e8d741084fa936a6854a91a4267685db9f1de1371c49cbc4e20312e93eec073f32bb2e114afa9b34b18ac1395fa43de626e6317a7c9c0161c512e3459984e786e12ffe3a7b43d0700d97ba0cb9798ee3592e9b7f4db7aebb33fd88078ce3a3e0f681d21fb1b00a790d636726ba65b91b29b6f6570dcf84a5562caecf7c49b427b2e054ef3b33a1ba6760b4aae0ca0156b7cc867c09432facece5956f1501721b5d6157ceb3a2a461fbcd219e435c69e56f5bbc3f8df63cff4693df16a8542addfef8edc1df86cbac125208458b155aa3c3c20f3df58ea1216f510608d3b0a6b4e3067990dc01f896888798de833b0bc2374ef6906d375275b35f184d8101de2341312a7d77a02c095966aff0912a0a8b75e49d93e3bec151e64d67aca2dd4887ad767690eea36d99513ca78f18f1e99537cd8eb1b26f07047990a6e11b9321bd49ee3bfb883a064c857149746d7c41af8252ebec905082dfbc567e04ebeff4c0b758b9a044579675d99c5590831578d7dfef9cf4975857d8758f81cddfc2fd81859a749080ba6c529c9b0f912e808e749911cfeed8e97ca498de1367bb0e42e7aaf4e9c0858351f21a920c635897f8b824739c81702d63f2b879511ab4024bff15e264892947ad59a3f9333404f057272fbde12b44b50cceb4717365bc1fab37283188ca8e1a4ffc5785b5c7efe7a408c9767b617f7355aac8277e377a9375ce44d99bd022936fa3daaffe64b734487198bc99f7dbe71a6b2497153c3847c8cfa78b18e003c7169b7199bdd07a36c99b115a7881cfebbe33e1462c17f74fea7511279111258297b8cfe0f9f250b804826a8878b917baef4cb9d9b6f7afd474e52513ad4c9f02a518cf2d649be9ed57b27b075966b476b848ebe3c2e62838c3a742dbc24c599de7cd6906d487262ae1916e5a80cdd1dd5bcb48c5d11d6977245213846d8a1b373779cdc5687118a33f70ccf2537db5411a33a02c03028dec434eeb954a025354e35419d2fc2c55489af06cadaece2f1a2e6abbf8d8357414b33df2e5aa88b13e08656438156b5394d8710cb796c742e98d44e57a08834444d5025775e3df3c838d1644e586b957e6b470aec22a7ab9aa6834c9c600173da4ba978c6cc16f4316d9cb0d9c9d92cae217f292a35f40ef7b7ea73ff9dd49a31b614eee058f8a518ee0261ba1a670bd7ee5c93bc643fcfbda3bbe11ecf08c05ddd042ea2993860e872320152688d8de733537d3ef5497cabd203a9cf5b7eed25f1fe56d1f4f08cff166764043873ea008b1b5cc978e07c3a37564c48fab5259cfd56ba66258684f6bb0ed9afd8160c6c0d49f4fc6a3b5626744bbad384364eebe4a489e793dfdc5e417bb8e069291143b0f28389e8eeb8803c5bc0a62b8767783cbdd54cfe21e785e9ca3ec50545261bd0ca4e94fe4728bf3bf7718a6ebcbcfadebb27eb2f19fe3f8945e3a4d46217cfd23a8867c46efd99ecfc7646ae94a1af209e41d1fd1eecc388b445127ae4aa889792aa558e8bd9299c945a6615667261fa2640014a380288d9b1f34544b570e2d09a9680343403b08e5135c5e00c23eb22045a20f7a9e0f1d608a1cddbeefd31bf2af0baff87bc9bced793cdb8f9ae7d4395b8f7051f95e3eb4dba2f2a759a9b82ace6f9b0f64d8bccc937e2ba10e680f48d8942763f633bd5ec3f4a1eb647de6f84ea41fcf8cd56aad4c47526d9f5e02bdcdf9d437140ece13d29673ea6735f481f519bd70dfdd8b6095e6a7696026ca80bf7c6383eded32443ee1910dddacc407e1aea295c5b796b78e278174c8d6fe0a2ab0975e6bff323f69f7b733f7f8aa202f74e9386ddb45be9e343bacfaa0def55c78b155e412792edc57105c4360251f1f2f8059e55e2b6a470cea9e962c53cb7f085bcbed01152b33e5138907", "6a00acac52516300", 0, 1695924994, 1537743641, "86b9ae7bc7fc2e49e8740d39f1686c4f81799c3b7b893c01f4ef440c42d9b05b"], + ["030000807082c40302ad0c250bb52db2d3fd65dc251c0321102aabc46c80d076b983b50c90d58f307100000000086551006aac6aac53ffffffffebf37e7488b940b2bf783917ae37755488c5b87d1ecb577815fb2cd94ffb42f30000000008516a65656a52535359e5f06103a5976a040000000007005151650053536a2dc00300000000045263005300cabe02000000000853515351005151650000000024811f24014c4b2e05000000000000000000000000d16e36bdce7efd7d1c77fedc193ba60606d9341b7d24048e0844798ec729a59ae83cf006b5d991753dacf2627cd4bbf185dc3b86b6fe89b79661a69dd012f5aa3cce4fa63c2471fbc7161b147f572ddba01ee8cb50d4d7c2139d1b3e490f784300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d09b53dd8f891330b0dc29b42fc00e5624fcc750f05b4a0e65f9593243e7b3fe68a7d5f5485a3e15b24150b1ecefae1f985041e62e2898e9aaa85dec8bb0b534effce4eb0056e8f27f606b974c2d07337d3141b1d1ea3592472fe48a17fa1f090ee6e501789435fd23523687a381c186c974fc75745b76605e5b5b248c74f6f2030d993bd34f02d38cbbd29ac9e019b3a6a321f412de4620da6d9bf5797ced33620316c33c686fd200e01fae4640c9ded3654af39c60b3f17504b71e499922301d390b02536daabd8a7dddf9e446a2f3c8d44ae6846188bdb761ba23de3f94819fe4d029e48725c5d6ca0ab25ca8c81cec1529c9c2cea8e26a261a38cc1a198c988d52032e78dff4dd67d4bdde243324104df6d7ab86f90ef454b3b5c342299b173cfd030206878791e3d3c96467d6529071127a06b5962e88b7d2853db72908f14830d61302213424f400152a733ea21f84221e4f05032615ece35a1750d148249a9a4d305803280cf0a379a823f93d7d3259c012001fc2874e8d6f3f081586ae76a0b723f96e021feb42de981327e88aab949eec6eaeb45a1efd8f2a257b5eb73186d6e5b2ddadd16029a8991ced6abda98df411d2cb17447635cfcf1049b8a9bb59dc0e5c9d2bb8bfcaa85f4550e01e9be18529e7d9fa45a500091070a9b27268ca7c9c5cb1339d83a5f58f7cc3756979f7fab51df7818d8cbc9bf0b3474a43ab52f6440a03f602cee8f1b86bfefb6708f0870d8fcdfa56d8a9891e013e37e5f3d7238da7c4db4f5205ae87865115a783a4a49f7b630cf5e10f18e13a191424ed2fd99ae44cae1b367150ced6442c7565ad1f40184fa201367d2ac4d08033b6f240d3d7c5cabc6038d3f763c105f236e38936b17cb29c0364005d4a4b1ba954d5e638b463bf246205dafcc022807422ebe982fe55919f22cba40bb4aeb94b9b892ef46d386acda246a3a6d1144604ce7044a9b5e4c7d9d4a619f403cb60b8cc5977c03e38b36827e8f9d7c553f5a5a65031c68ceb4df4f3c253ddfd98213cb3efbc2967785557a419c1c3b3466a8b419353a8019a7dcb5b4d07593f0974c2343ec692a10c06f1bb8c0fdb2dd331046d87c4fb54a49bcf0f3b6408288fd559820feca74ccb91e59cb79ffab8fd218bb3d4c469c5c79af6c43442bc0ab0b705398d73df0212f4d94bb32fd4025b8b64cb23b2f2848ea1a6840bab041d90b723d22ae83eb8aad5024fbf266b8bac4b34ac4160001fd9889a576637e943d3994267d881561cf6bd3b8c0e0c8cff6a4956f5cbad13de772b17b96c1f2aa52b641fec16a985703f5f298d51b2e21f4272abf98f3075df54865bb62f9321943e03bf7050fd2cd791646c154ada5440aac0670cb8719c3f8bf1bad975937266f7b9c4e4f3d6cd290af5a1ceaa47cfd3db84e2787d5e7041a02546ca65e3647ba133e8da79d8a7230a20ac6d1c8cc9f07f50f4984c6170971e362205f0903071bcc8c34ec7b9c84e1f1caa9bf28c1e6c283cf9a876a96e6321212ec6fd48b00815718247c6da48e317241dcfedb5c0a6de213d9e3b46b4c55609e7129222e7d410204b75ea0759587f06dc07a2a334b4650042f6de42b9c2dc403c438cc4082ccc5b842035276cba92fa0aa5580b0e75df9a3f861c289b8bbe7bf9519c3b2a0a8f067f6c32d78618807733a39f98af4a07133c3bd6f8abe31c3d66439e92d2d6791c843b662f7ccaafa15d48a5b2e96a37fa67a715ee1dd684581e1d158001b249ff4663d94a61eb9a018d1f286324d2410ddd8cfe0e960c3b2cfc3127f1b097489434cfec25cb9d299e43c876039cf735704c678f0e78b17586e19648fe54635797a656a49b77c8b3adb92b0b20e35adf174c3db233b1d7a86ea5ab547f06c8a1ab42816e949304e4571c946f4f40799b2e07030ab5748a9517c304e21b730e50f5c0c49630af93292aefd079407bd106ff0d63e7e4247288bdcfe153dc1f80726bafe900f384f4a33a7e7d32d23f7b540b065c7ea7a7f253729b4689ef8300dfa131ff31f8b4d8a7246946b9d299c74cb76362b297322a085ddc22ec83bb06e043e46b7869e0d3b11d357bfbce0ccff6ca68d97ffbd97173735731c14203e691f784ee576632ac51fbc5eed920438e4920e28106897de34a162134176e92836271893654e37cb0d97bf1b81c25df96f9db359ae712ae109599bcd18862b4526d5f576ab9c47c975f1bb63d4401fe828a004fe96b3f34dba29ff8573a06b8e9d427e353a0565dfa2b5498599218a6f48c049c4dc91eb2b857d3856d71e622917c41befbcc5954fec819b63363decbd9f2b95b12ce1ae5e6fe30e303f820931a99de8730433e3d004272b1640993266b6f985709062830c912f90d483825225f0892e7110b18425d93501aee742eb66f83447deb5a0dad1657293c5006", "63ac", 1, -43392376, 1537743641, "38c862d902b51f32728252534da4d52acc3da86e28d32ba253a64b7152d2b650"], + ["0f3b747d03de20efba80d182a4598d1ada8158f9a57fa56adea7c51b5ef5731af1288436ea0200000007526553530063ac2cf1330d746d7689f7a29e7815408852d1cedd61da7dee326db361bb4359a207fc0acb110100000002536ab94a805c3ee5a3985569f6d3966efbeabf73249b0f7e3dcd93820939b83a7e440af67e8300000000065151516a5200ffffffff0154754d030000000005ac5365655180be135f01a2eb5103000000000000000000000000d23f041fe6d6a241bd5fe26e65e4390027f4945419d7011f4bc98f886a57c78d7f68b5b22de9c0646211477f29b3e178c62d9a886b8a1fdad19179f0e825a5ff173c4e8d108c30f67af3ddf1b0537a4c34368552bedfd9a3b6e5896fd54576ea00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b5b086f4622d3621a38804de32de637435da240dcfe14d3b6c5335b765ccba5aff98c8f62700d41171806da411d731205a445e19cebc89d29b9f0aa1e7d5aaf20513cf0dc55b7cad730c57c5b8096131879b99a2831b2ab90aa7f1614e30ba291eb7e638d50d4451a99a46d37c1064fb673c29ddcae68b348085d15190e1da3c020c3cba1fbf8e434023fa5fa8e9db45715da7c85845b3819709dba9011b2de20f02223b52dfc53aacc82be70c1c80ea2f818a92432aff869e90f1e8aa520b457f250b00cb703c5aacd072662e57eff098494639d7bc954198bf6ab6c14c109a6d810b7efc4535e6b4e79297005257c7c99ab05ae1399e10566a6af52348087578e589032d57cea1f50b783b7df7eab6a31a78ab18c514f5fdfe08bad524dd302b17cbd6031e01886101d2f39444a5fb93f4623e95aa4db6257bc83cab1d08ef65b16d5a14030e79bc412ed12cfc6b6011c306d914b1dd596948452ffe1f76c91765aaf1f4a10322815616a44cb1b07b2f349087c4d6897fad4508c777267e208eff2376cb083602080385eb56b510f19dbefcd7926a9dd928686d11b2cb4f2320820022d7ecc2e01471d6b313d98a4282c719908777933c2058ebcef73bfb3a6dc0666f405ad9825ea30e398dbed8d46bfe016f37841573167a06723b0350559f03a1a837964e17e2e23cf32ee17750c0b986a8c9c81660d2898aa3b95b5ba69a77b924d11c713f0e9ffe8a981cf7290cc53331e08d0ce4a1cece6c2c6dc6516c9fa7e7d0edefe444abcf1cfd536fcb05c1250831a45c6c0907c65e6e3389cf6ec66b2d2868883414ee8caee3e9c5f151df845fb710d2945e27cf2ccde3445dece60814191b7a90e2a8f359e6c02b48ec6d85cbaf5b46f3a8a3858285b634bc3adec51fab97f06d5259875608c884ac59a91fa84e1db68933f24deaebec3b4954f22dc2bacd32ed292468f41dbf797ac87059673fba05dc9c4865bc08af34eb7260e011302f09f8a799af12e885355031de7a7083c0169511e5657b4aacf86b3074d0fb4cafe4256dad21323b043669975c2ee3fc3ea257992ce27deebc178666d597fe66782a9b9678e3caa7ce0f4ec4a5d4a01e9477251470f40a5d76147048f3f31a87f605e90f79045de95b1cbdf37a1f82e000fef3852a09d0803bf201f2cb1655dcf770e5a9daf86bbbd7d38d96c4621d3b16a74b7d7455220ddcb609e8eeffc73b3b6aa3473fc38fb246f84874746e208f15292b6ff997e4dc83727c1b6d653af88da5f66e02874c2de00f8d7aec4e9b0044ec4b75d7967426cc5297cf6050da84e2523a4aaa40fea08f6c74957b9a08f450f27614a877429696dd045436ab5783f9f6cb6e5a73227cef1cd4ca433d071ae709093c8ec59829ae87787cd4fd1bbe0170f4c3def9aba9050275f91f9677c987e761eb55d4a644badbea49d947a265aa30decbaa5fc6eacb140954f90c6d09269c9d53ea5a17adae17c890f52a67b47fef437212fc22e2801e536627d4dcb1f9b13eb7a28cfa082ff986327021187ecc83ba47ee3bbce119d0dd49ed91df30b6a16674b25833bb2d53f2c0b057a484a6203a8f4c0f42a2208151c1bb0abd248113a84b2054d0a52a553b9aac799f9c37781f96ef25f714d7f10a002ee37891bdd803dc897e6f4004794901aa3c76881b5eb43da55dbe157c49c134eb32d151640a3f79c134be25197e34968a02f063a4c146ea7673a6e3cbaa3b2f223cf7d4a31f00118e0f25b3e2cea11ed9ca87fbb13e4bd09e77f7be6a7908bbd935b891d73afbe95102224694827bc340e618d2d7675514426de534dd1cf03e4616ac3336f7ffb7b2fa79780115293f7b179ebcf9953820b9f5e5868a590016dc7452d2a14da4c5c999bd55f070a5b1894e7c02141b6ef718943f48c7fe9529c5ca010b706ef59635356c86d984afc498bb2e60ca5ed346bfe40ade273d3873a7644a493a7c1aac724cbe9b9c18f37fba7365034bdd7b389e90930d0f0705251fd1783d3001ef0f18a80472400e029957ae4d26b241a8da3df740f32047c2d20b2c1354450050627d2757a6d430c7aec6934c963dd02e73403268741d364670355a1e9005f99ae177db352cc4451e548e4b5b99ccd122e1d3f50bf7bda067edd971e4d444bb7f907efa8e5071527e1e5422053ea3b7002e5ba68e694d4c330b4a908471af985931775eaa614d9d629204e847442c8aa61c386798f40755649f344f472224156d34602a0155081509fcff438c38ee51bc285b21f47d17ef78498ccb4e53a7b5d677d8f8db478fadc74b7f8c87d554e31afb24cb5af7a970836ac4db348ea61e1929264126531b651f059e0b98ac1eb0e6c402fcdb083d045d58c0f7824f2f66fd7bd6aa7c0f36a1121f7ac882022c67e51a89cb7230b2d5ebeb0062f1c37a78c05309", "ac656565630000", 0, 618953117, 1537743641, "3269fa0b69b3d8c20394f4eb8ec685f662575a89d2c37d79e76e6cdf40bb464d"], + ["030000807082c40303d34b0d60cd94036af478fce5a2ef6957049a94eac7610acaa6cb4178e50aa0300200000003005352ffffffffeb4bc88a86f5398db3dc7d975aa81caaaa76ba7ef53c99f054e4d55f49a3c3710300000006516a5151ac00220fdcd9e14c043c79f74e7b9d2d25e614332544d21c6f4446348f0d448ba93b585c0a8c0300000008ac5363526a655351b67bc279010c8ce90000000000096a636300536565525100000000536ace7f00", "5252516553635252", 1, 186677060, 1537743641, "b6d93168d1d68cee1c35b4212167b86283f55da53f16dbe4c17101158657d8a9"], + ["030000807082c40301e36c2ddda3639af26a68000d85a918171b66d57aeb910cc4663c4c226cf2f96a03000000096553636aac655263651f091be2047ea78a0500000000095153ac51006a00ac63486947040000000002ac517a0c6f00000000000765510000516500a57c25010000000003520053902b203f616db791026dcab302000000000000000000000000c81d9ce2b0bb8ab92312e5aa926903c3a0de12e45befed19e125cbb800b3e4228fe7b2836f4bb6780285eb7a8372c822e1f4c27d6e083cd3f15907a7871e50f6c339c5af104e22d5c21184e7cdd460cf7ae3bd514d8e5177b44ce36f4db7fcac00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000155a25990522c40ca0331c64f43989eca17cb3e557cd37ec02208cd58553483bbcd2d656f74a8bfda5087cb796286a7d69625359a2ebafe367bfb87252d03ec4a452e02aa710eac06a8fb6fbb3b9e1f61f58dda95bd2b018abf4679fc25860297f7054d709e480b54dff297a697c3893d22720881402814fd701d9fe5561d1b00221eca0b974652a5951547f34f7b336cfe9571dc318dd4353b84f1b78792ac76d021310b071c8c8aeba550032f486400531923276ffa36faaac0e2a7220bc5f3a890a080d177d60e34ba499d560e2501476e76e463a117a749b3437a0c8b6b139361c18145479a47c90fe06517b524c63b36a5cc6e85575834cc24725eb9edbe61e6f0313c0ccff18f07b54fa314a9ec1d2b6985fda10b1f81b1628c277a33ec805789a031bac2fa2c662dc9e27ab6e98fb3ce08d518884a32658118dcc0dc81bcf59da93022647ca45f1f43f0089e00019472a33c7e9e26f5c597810a47b68dc3ac92dc7bd020e668a38d492571cf2d09e4bf91c254010d84e84bea19e30429e08596312a753021a8b084b951b32d37713cbb231a475b946d8c866f732d4c75e8d4f142a96a2a2034d4172fc9841c406cb6720983205cca12989a5cfebae1d741ef628ac0851d143b264326dc98f390ef436a0257d3a41723d670709051310c113cf385cdae7cd70c41fc25c307f46432647927f592d97aade3bc9a6a102c21c6fc7e31948a0b62423197f06d583116b00e5dda9ae078105cf6c17a9a5d989dc6a19e20ce8ae9e9dc71535ce8d0c353b63cf985adb3014b99d7b8a37e7e48f812fcf3c52b06ccc503f81a8e39f79ff3479705292d4d121f67e77fb7b84f94186c521dc6fa029fd65a31ef5d7f2388fe6c0fb7a1b233cfa985df5ca59eb024934907d8c4095eadbe779e1f6d302a22c5dddd06f20c197f7352139a832044a35e9d146861b7823511bbc80af271d8fd88835211079fb47b54d575684ef8e3e0ac04216dc7c11a046dec4d51509d8cb791b0d42228f6b49d27b5b93ba6981e27bb627aed06631afff12db611c026c39bf32f620bcb96c9f07d896c2ca13dc6944a57a3b02cdcc005135149063dc690f54fd18ea5278bf43a237ca52c47d17de17a4e39f661d8adcfbd32f94495259d593268a6865fc5332b75733128198156d8e8a631a29f82ebcc5a52b7c9ca30466896d9e8651b095ff059aea7d6718d68d35de11183b91212839bf675089aed869a9e5580a255a99949f5a2b5f22fa2f125634a1a66c48418a073ade5dd5a5f0b1e667c7ab17e64d8dff6f677bad6ffa3c0db1023e0d6367228d5866fef097330f559a531a944debd4f3848f9ab16cc6288d1cb0b8e0142fadd20ecdee5435f5bc0a3b11219c4d49f325b357b97f3470d7a0f6991aeeb3ac9d509b233c17207158b7e85a3dfac740e95a08e8db3e5dff3b153ea61f9451508fd0ad87cd78aef2258a65e6318c58b099fb499af3b335927ca72ddebdaa4e02e5f6f151b6914eab1b3d2931c0e31f8435d0e88999abd4c129fd1b5e0d90f5d1e3f5e5e187f23c9bc96b79d82582d4ceba8cc9382dea9626f62eba760e143ec8a504f8dead771fb831cba78c05ab38aa56c8c5b77157ccea77bf8f07360170215de22b2b6a0446c779835319863ad3885865d91d0ad10ceb688733740bc898bcaa0c5d91afac7115cfb5ede87594773db559bf80078fbdf281d59c591add80fddfc254913780ed7a8f59e75a321ba49eeb481f18409ef341cefc999a8eaef63d5b4425bdbe75ee96a960e74b9a301e6266af1100896d5827db10954fa61673582a853dc7e1c0b51f47d97cbc02b856a9fbdbedd978fbae8ebc999e0d5c5671a2510e3dd382ed9fd82ce471438b36fd068bb87ee0d26493558b7ec68bdcd503f81e7065e7c6aafd50b61b80d2e1d73909302639a26d9d936bf5cd39e94e3388e2f400e3e1b1b08c587aef2c9cabf8917cef0a147a95adc2ee8c1cbfcaa79f6326178212c31a36c4901dd138e8bf5b8a29adce71053a563bb548159c3cfc7962adcfc07a85b36a95dc5037efa4df9b931d3644c548f30f7aac9dd1c1be0b077ba511ddbb2e149fb23b3c741638528c65bef0e8f65f79389842cd6e3be631682c41cc113b379d421bdf9f2be7052b6c1f612e118c1778c459674093797a039cb136034720d7a4fcf6e16094de0aca503631fa66df3926faba77848228297703dac452f1b34451517390ac2cd107cd8c77ff09b85f162d999629a21b2a515e4a797525285a3df846e60b73c1a87df5802977e0bae57e000000000000000097f09705000000008506f7bd713ffb292282051bc01cda2911bad58553dff714ca083655cdb540904f1e7a655f4988f49f9bc1dff0cc9c4faebf2cbbc9895a4b57e53964619c63b8b5eb33f13445a944cfb5391e8c184161c1c48d338954576ef34b9ccce09e75ac00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000201a607b4c80c928f51ff5adf3482fe23c6a5bf112f1e85a972a9b5bc4e01cebb5429c7be9e8fe193e73678a9614b5c290d3b0287996d3d161dad7d115f199ff63010d56899cd39a90bc64a9e30fda76f759ebf6d4ba555e22324bf8288ea3a98a296b27842ac7e68e431cf6a256b2ea4e2f0f1a789b7c36aa6eed00ed702725020e96f39bde7c2803f15b61b8c8d2e9a8ff9d165201fb16f9145f02f291ec4eb90208028b62df047fd7a2f15b53ab63702dca5561a51678829c3ff7edf35b65bba60a0543195d4b91141a0e08c4ed4deb0bb363bef43467ba25f647fc7ef9b8b3286d16afbb71494cb83b34eb270e52a3ef224caaf35af8b1f9212f47856f9660cc1302290db732d87d674f4f93418dfb67a247c2babea7246373ac72637b4d0aa776f6032781cac9098d2822dc187280712985d846944ff04c1825f26ae72f3bdaf040120214b23536c0266169d0707b60bef2ae5c2b7cafa035ffe29ad8c5954fc8a40487022134412725cace425d355b07efd078f8302d842609f1c6a565eecd04005f5d0e031d4a132aea39d64ff5454d3550906546606a2553c58f7d696a4173dc85cf4be7ba28d2670411ffd0411244d0d6f94d813a2d58e3e7dee977bff83c944ae91e83f18570426e95e48478ae8dcf406f81bc88e1cbdb29683e2d36ffcf6c11fdd2e8d55d6b6af59aa7b219b2f43e00a4e15df64b5d70a043511a1eeaadfecdb29a36124d0f6e32ed6b5ee18a75c81489114affbc18b5c056dba765f7b41b1bec83b5318e429cb8ce7ac76dac31329c6bf633efbce3d924891051becdc09143525306f05f825af1e1c070a538a2b5152865fa8a0b1888f99d45f418f80c34370955a324b6f3c47fe758c77a144c913446d7e9e6e5d0e69fda97bcaea51ef4e584dfe8108e223db9168bcf113a01d77e7fe27e6fea7b8847c6d5e30c228217301b02d2874c396a119088e273d1e3699f29362a2b0f20f674bb958fec353dc12696fa0d8b33d2afe14cc256745c0cfaa788cde7a38fa891a5c3a9426355a1f01b8d168eb69cf469fe6b404161b35e92142573eaa4fd9675cfb782397789c72eb8a2175ae224435d1e0ca6242ac10199524a294f7078a3ca97a455f7aff1c567b10aab65a9665a4230f15c92a28c22fb732c04c3ae016152c3e24688ebed5ab77162a9c91701b68173be2285382dd71f5242f34cce44ff12b652a6907dc0d11377f5950887ec7b9f28fa571543cacf538fd4478a7979c73305f86fbf3a41f430b8d46f9d241eef5f5628269febf246ede4d2641140b04dc52ba32bb33c91ddf95ffc19e5ef2a8bd8a6f27f96924521ba8154ec6f7a77abf0c7d3c59e675a6c14593ab0b31ebb406644b5bb16ac6a46102cb34bb10b9da978bdbd9bc25da8d3a8348ffa9f2fb2b63e23d8acfe105243e3bf97de430c5387dbadd0a71dbf30f8f959185af0764ed1fe74462f9d30d619bbbcdde4fd2e9c75572af551e221901c906b2804674e33a7839c5315142f9517a9af08c702e239a2791ded009e63d27cc909402098b5519ea0a68c59590d8da4409d09a4fb2234e20e10ffd81c2305cd104dc33e4dff04bc3428fdfb804ce9f7ffe54ef1cd859741605bfc11bbdc53508647297af115db01f8dc7618142c292330914ed98c2a6c623256a269e371f95a44dfbdffdbdef1af4ba6608145423923aee2e2c12e0bed2bb675394f13cc805b710fc15f907343d4f9a833e46ec01caf47a062e65bdb9d7b3947e081f030cc8ea8eb08d1f88bc31b31a360275fcb6b10b11be8dad329233a38a7bddbd327031428df9a8d91b376738a58ccbb7ad07caf843e115e0b292838bccf38820e0ce96834bbbd77e92747838284cf87ff1981ca9ff40deaa88525c4e7378d0bff244a5f40a6f11a6ea461a1c071b4bed6228710732d7dfd475a5993136d705c2f458178189f570bf22e00134a7605115c03d121e84a33f1dc6c9eeebeb6f59c0db8c4a1ab46ad12d1fb5e08f6d8e796dabc36528db922394c098294757755a8620626acba2a5e2d5df7d98340e98245baab5ba7985b442c8503c2348796eb52528a631cff9c776b9d3b894ff409d3fd45c24bd79f44cc81f3dced2473ea003c0c541b0bf9dd46016a736e4f573fc1d214f0719a40d3445560c0264f53316941efbbca20cfb05d8a30c04e8c117718f1ebe5191c6eef0a0f4c37902c790df13ec6a1db6a25f8424446168c1f8ddf94f36ba1a3500ff130f5f1ffd5dd798d6cd86a2f30f8d86eb067a956ea0cecf3d7c6555af1a35b4ad83ea4e9905b8593966974f79cbaf8b30c34c15c4eb4c650a4e0da0ad0a7c122cd07de89284a5d0969b03b45ff9ec0857a37d8e32be2ced1348b042f8a3982a5f5f8573fee3b8c83ee59bd0d91dda3e2cee9cdc1b8e41000f2fff500bb80cd6038e5d9bc07", "535363", 0, -549801415, 1537743641, "bb0daae130e0c5841b47e9fc0999fce58df16544a9b9955f2a9fa98d323772e7"], + ["bfc96a2a0498b8ef1f984ea8a44323ae4f3295d674101aab59fb61dbfbe0480a6f03d1eb240100000002536a0f908c63911fb063588ba010f073b82bbdd1bdb70bb44af372f0650ece35c45cc4326a370100000004ac526a52ffffffffdc89b4b73752791d46c63b59952ab06a06bb71489b548296d8c0f1772f57436203000000075153ac636565acffffffffa2f1eacc474df1095d529a08ad356a741bf2ccf9e6bb7199a22fe320116c2582000000000253acc2c3085103678ae40000000000046a65ac6389a2950400000000026352dba93a0500000000085251535365535352e360bea6020000000000000000e059970100000000454156985a6c556997f27b60bd96159198ea23a132dff6eed111183a6ee5ffbb25951820551358b1ddab8e671ba25173c6173b049c58d33e65a750335b7dc5d6fa7d63f31396ca96b20565026f94b252882c5604e5044f979a1a5ed673b8433d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000884d82392bc7cc7c2e1db31c11dce3b10ac0e69794d68c0a18c87b5b54447e04ebc6a81ec55d946d98b8fdca9b5be9e40c46f9fa77ad2ed2b0b528e6061b85f9c85f266ef43cbd80066b65dd21d89e0888f7c65c6051a61d1a7a573e6335ff6f9af62753c775bb7337aff5020320efe91b8ad2145a40c314ecad9263965a3ec1021c5dd4190b911026eff1d051807c2f37d970c0d11c5c0256ed87df4b54a8769f022631fd6962e8780281e526dc6f966b9936cac48e92e8c8fcafc02afc67630f760a026a313dfcaef0f4ad0dc2ff38c5b28aa2168bc168b36ddb336ec8966082242d0a5389cadcbdbebf112a888acce43e2421e152495992ee022e615cf6df119733022d3e3deb796cfbba8e6f7e2ecafe9b1b4ff94c395279946d597db2a79615541203264258bbcecfd38529b271381cf7349180a67d80713a77554c86ee269ec22ab5030cf43296d3977257a840abd30c8e0038da97542b3c5284d7090aabb5749e9d0b0304faf34e374bbb7fff342576b4ac52b6cb557fb3ea9de85ef9f2cb55141446f1032e749c99574d73fd0e928b2fff86a725d37693357645fa5006ece91a561a61551752afeefb7d2985a1d8a51906c9fc14badecdfd5d251ad80032a0c59587deef02360a20909bd4ad74e6f8471774bc62bb0282dea852e11974f8830d5f937d121a30528852d0119c442aafe8f1dd444e94b12499d4b8c32991a5ce612b56ea3e580accf286a11b14219260cfedaaefdb277feb06e773846db78e14df1f35c4a5ee994f2218ea4b1a91868d3e9e6f6bac93d6a7de766f919469580e065697a69b4d8e57bcf1158954fc50966bc2ab1d07640e7a9973b3dec32c92c929485e9d01c4163ca2f7c6a755dd145174af186e3dc75d34dc9bcb91b7162519e6dce627d7265e51a52b970eb9576bf8849e0b9d7e90736ba75f57fc9a2fc97d48ca0ed85940e48e45fc908e4e16e2db4ac9e6d96669e91aa4c1419c043fe73bd63a4e3ef2e0af9a27fff4e3ec1bb1fe46d2f9293d06444362a1e8de72069adbbb252af203661e1111b11adb4d0f77bf4b210df45ab12671381d32aceec05cfad89572f35f91ee318bf9db6782022288528408c032eb330faf2533986085d07123500682f077befcbaa2cab5d9efd1a4a26b8fb7abe2c43f5187a5e91548fc0ac052e93580036065669fa3e4c3a0dbabd5fe3065c2963d0f4903b32d4b5ac072a0a2d0fda1d294a7167c00c9a22531d73a2d4027f347a46c3a2a73671ae3161ecd1188cc9ec9ba2276006901ddf24214f4cef694a3e71ec34923fd7be0c060fa7528a5bbe7c06d0c781c1ef3ea8e7399256f5e0d697e97c879b51343dda22a79425dae33612fc5b6a08c5c57e426476224673f69c94217f83a96231afd683851686f138e257a2e96f3b2f2ff426c029632f690a90243c66db26c40f2b222b1440186a3c7c93d91488ab39813f42663fd021a52a5ac79f32a135ef2b95d2a5484a3385439744d76b34c6e9944c67b12197bfd854e180526ba218763147f1916b4691ab5f9aa7c6848112e9ff751adffa04bea6c460cdd131c57b8291bddfd1b39a7ae1eb6c26ce9415d4e31cb509fc203695f6f6daf9fe62b384e5c687a9bc0e7390fd1a201707037e689c35b32d408a023b99cbf73b83d085970b5c99ba578178c4207ea07f02ad659e3c0c66cac8da3db949daba942bef757438aee7be6226fbc46088567b904c9707f19020f8dcc1865c59b229717715b6afed4d625bc9343a9429f27ece431452b9320fc5a4cc9ab8a4d789827f553c88afd590eb16c38d365b17964695c87bd7d7574e1fa8bb81d2ff01e4ca0696b7cbcc7d0be5a643b3edcf11b2c5236e10f99816585b817a417f7bd2fc6aca9a14c8850a9a2282c7de51a8daf87010dd2d4343ffc2cf62017afcefdee5b96298b80ae216cd1042a556a4988ebe2276037a694a7f855412ffdf215fb628ee11c968f55bc4ef55c219dce140258f8ab5547fe54fc5883af48fba05e96bbe1350e066ea4d8c2db865a4ad00138b6bcdb9b8ef50567e7df8079be77f753a3cd8b56a84a6ede1a8796fb4bcb2e8ed83d5bc007119f10eeb94e136caca2b0394c65462c8ecb05cb72530d2f1679f96d68dc70f99d83262835b05722fcf25d21b7c3bd9bb6496be4310d933d17dc6c8bae5b33fa6b137dd32217ce5ec33d766aaf13ee2652664034324d3a8c25d1b96890c96815fd4263aeaf2e7522260f9536177ee67af853567a9d72e2305c0862fe802aa56d17bd8d60c48c5cfc2e7f6de57d1f31d06f2e5700000000000000000000000000aed511b592259cc3b87b80905943419fac70f1de49065b46ff8b22517ea2dbbf71b9e08224638f5fb5c4c22f446bf89cce3199fcc3fb314abc11d77eb97a1152121a1276ef28eef1c00b5d5f79362e6043db5078727bcb95771791faabd2f58a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f664825f9927c6643e0f9e3c57c3ec82f0e1ee35e76f7c41647877572dd8a0e9b8839ce716a1a049130b0d88da47a855c9dec040a7c3f5304c031231a80dfe9c0b33ce517737ff36f58a6793241f2018f91cbc8d9b86a19bca37fb6c3f73778cb5ad0b59311e5cd585b9817eb6a69b942d9ca8f700b178a2d4eab2ed5d3cbeb030696b76dd53e3ef76633e2ed8681540cd6f8df74e5cb0ae5e29a5b495186b34c022ce5f1ac223d2e7e524f2b10bed7b39c2ac40b50f7efa9ff79c6bfa516b866050a0844b3b594aee64e9e3842ba578eb7b6aaee4a2e246599cd0697f848fb35a89782e203172aeca8d99e9da4402bb96b34597dad320f783e77da6e053a226fdc2e020eb9c8132445c0052a77a551d4fcedcd54ca9d9d3e87ecb6ed9ffef2259926b80322f6610314c48510dbe58f1067f8e399249866d59168bb53fe862d02a12cf0970222847547e408a63d26acc3985474b5a94b7d91dcf1a01a1f9054f141a4d280f4032afa4dce9b7f8b2608f19b9a5281751b297ae0876a04105a37f550683acdefa602166868e8065a5d48e692b1709d4b66d4276b56a779c87297afbf4201fab93929de9b82109bedd41cd7c8159048592120097ca708bfe644fbaad58f29947247e169cbc98ec1559ea347de6519659e49fc80cf2f366d0dfb46b4628c7dd92b172de2372d4442cadfa1ab164c68d6aef2102f6cc35c66d0f5ac8cc24202b072028de6c30afe84a1d2fabcb60433e096ea47c8507190400c6d8175a648d2fe62142e75ccdd56ebc45e35066f472a2397f399781721c607df8be03ba0562dc8c64f1489d04963586382c664aa493653e7e1b075853816091794bcc7a767c18d95dac6cda71f4e305801546e7589343494fdb41c40c24e57cc2b041f040ed159bdbba64883b696a7987b8eca10bda9bcb2f40d9d0eba0dff2a66199c718465cbf98d4508ba5e6ae556008ff8fcbfda1c6561aa56117ed84ebbab9a0e1053cd0407b3e97c499596945c455119a2974b5c69164fe6913c5b0e429119c243e34ad5cbfc732dc5a10fadef1ec15c7a69f7092f70de5e804a71577805d953ee4db3123a6f2ceefc2db6831be108e1916910f04d1e79328cc4243d361f6f4e70bb7f6363cb1df0bc596f4d829ed9052a3be95f43714bef41cbe8e90881033f5d5002b3f5591c84a0bcceb343f659fb1370bb3dc55236001c5ff3d27f16e104061078a1bed3d6c6d272c4d1eda26abbb8be9e165ce6f956531af917c47676569917f49a1cb6895d06e1bd83dca166513268c0a0ef1053fdf7dfd053fe415a9ab7e31cc024a6419a0c816df1a51684fb517fb506f30ad05ed8ff44f5af76d581444ccd030f37e297d151b74a279cc2dc3d3384a6ce32b7ff9cf4c78c2da91de788a148dbc9b6978b9834e61534b67fb67f4daa37427b621b50e131b05286367293361a46f6a49f74097203d91cb9d94d03cdf8eaf50b00611e6ba4f0d862a9770011dde0099e2c050aa2edbe05c83a10a34138b88630630f9c9c6e2d7765b677eaa7da2b973023e286845c60f10b0bad45f8a8820d35a221223fc660161c680f23168a06d7aa12b5b2ad88c11feb81e748b4a9e4be62876f30153ee2f603354c1f8fb092e0166f8a34a485f80c0fbb82ffaf2e81ed19dc41b2c88f8c13363797ba5848e04593cf727aed13483c024ace79f6b0dd6478253853386104b99c371d882fbce7d0a12a19f0ed7f73f67d92a7d57b223ad02be0a25da93a9b64ac37585d6edc0d2c1bb653828f823eb8a04c6796e3c4931b5f04ce18c93c0f7ade1053d064678be676f4ac767e32a5f2adc00e8b3774ab4d9b7b377a7a4e9c43ad8c0594f7dc06926570f44264920ae4ee64f51412c4c15924ad4eba83e276303292319cd2a370370ade893e8ee36129adc713069f28a171d693380316d0fcecb4ce106dcc8ff82d6d61dd3dc44bf8e0ae36b81af2d66263bf28cee502062da8b6166ef9563c2f86da629a17956489b9f218557b23c8eec51197a933725dceb856d0d4a158500408bee9684163ff37f87de37747d53543636375ff6e33342f4767e5fdb06a1fcf6bc792c427597cc7ed5555e583160e70ccf68da9738e8fc695d5a383685fe7d1401fa916a9d801cb4c1747785eb6d1e538e542723f67df08f1995d1175e2049a37159fc689f190e0588277070737ddc102a7457296fbeea8dbedb8c88944a578ab4c68badbe6ffea13a400a739224309a37118f7d57c66dc4cf9a2cfa5dfbdf1df96afff8745c2b8bd6bd9f3c9eed0814a4f8f1828f513f7b7df6988c64504a6627dc125188b66a41d45e1ac890ad5378ef1064f52a95953d272a1b0a8d4c085f92f4de9cc33cdba1c2150ba721ff581af844cc5b4460162c1b2e8a7b2159086191f26485ed68ab92a1553ac938c0938f02246680c", "", 1, -2044221074, 0, "7d851cbee08ccd71d52615c6be24383f819973286d0acca3ae46fe74dd8c4205"], + ["b866b52d040e57df909c67b3b09bd165418436d85a8955d4085b0a0ba58113f10bd2c432d801000000085100ac5265516300ffffffff5a5de176f1392540b66f76b52a86c14ef3b8fbc56cab5831cfe20d6ece805c5d01000000086363516aac52006affffffff068cd5b2d3625330c9201dbb635b07a9cce05879a428eb34a1cf2442ca6c9ab900000000055165005153ffffffffe7c82b32cef29471f784594029cd34324e157dee5d86f4e8acc03dc730e8f599010000000753006351ac525128cab918015a6d8a00000000000765536363655251ae2db8730189eebe03000000000000000000000000ef5fc2460175a14913dddada8af856dd33068cf95e1e2747255e7f1b61d203dadb0f7578ce8acbec871bd8afdcf17c103505bf792fd97bfe9ec8ae2992fbd3bf0a0b80e1f4b2986791c699517e327c64f50d3377e6826572c1efd368d59ea4570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a3d17319b109769e602d14d2fa14d1418a65aa8d4019fc93d46ab2c6f99fb199d6e22ca62cdf16c40d1ac1499be25052bfb841b335b8cecf0e99e231e3991520720d644616fc76f141d1061bee6b1f38aaa4cc7f10a0a1f74efe922ed8fa3a55f8ccf01eefe6b09781f8786b2deecbc86e8b607e3167e548c80bf3e7c5bae6021446d0086feead3c12f4512ab8c41b0ff816cf1b18120ac607fba39c047ce162020c0ac0c147470c472df2c060a6aed808514e341259e2219bbbffca8f8968ff410b05ba855f08c63a35f7baedbe784c88156d034ddf8365c40a51a20a066219ed0b79ed77f1cf3de00bb2dea381253892f97d8ebe3cf1f3116a8c63b27cc33ba9ef03185612ebaf31868c75664c3480e69aaf30cbb05e3b111673d478b8f5a63a9696023041a8fc5796f85ecbd7c6d107a3384d26a2ce6453ba8589f81ad6b9a55c11d9032594ed66efdd749e20c03f3be70df14a540f5608280f8e7ffa82a7613eb33c2103253ff1235edc39fe91329155f99122336d6f79f096938f6deaf4243da33668c90223cf741f1dc610f3ca8b792b47e506adedd5ae3adaa9dd762f0cd55b7f58c00e01cbaefd0c40290d9e6d5044be2534fcc2b4c5232e11a6f1e610a3853030dfea44e84216a97a3a206ae314382a21c4a2788fe59ce1820d2be1eb5516137873625197e59f148a882342fe63ba61e8ce713394099806f19a0159c1137d494049ea34143f8a96dcd4b2ed3707f57746d5f7aa37153e7b9a0ec081cb178ec772905651b1f12bc59134df227bc7480661c94497df0dfa4332c35c6ff290b6a833377156f019e882af5f9bc583f2a15ed1390658eae97a8ce769409beab40cbf7cf8500e3c23542fdc15455a6544b66d4088e9e53e16a69de27e7a1b6846a20b18da248610ab61ad2d74a8cfef73b1e2915af0cf2d7297e8cfe49d1349453daa66a93b32c3f3dba0fc13e494804d3ccd98483b1798de0613012db70ffa03968f1d257d9a3e33fd292646af9e65cb0f414e79605f8375ca65c4635dbb487a7b8352dd3c6580021f8a80d02724095ebb40b06ff2e83b782c44a696a15f4468b518ca6e3d401fc028217cc9994b306f6141c9d949e3adf9b2cbeaf20361953dcb4b35772e6ebff36738524003be4cdc340a3a83151a81df22b6aa79e0c004376716b4de8ae09881af893e614909fb6f6bb2be3a4b7013e1783899604b641245da164ea1eb686c897c2483204bc471271a7a5a2abd280b8b11e1f297246366845a3fa0255cd314e0b9f5af6276acd63fdd6a13b562c051c27fd42e2dc4c129ea5f42d148d7129cbc9d9e571d17fb697c5c5b016f928958a98d86a5c6488065e87872c9acbcba07126fd3b3603b5fb147a0aa75bd7ab675d217855fdaf0084be2f757740a2e1336e9368e7d6c3fcdb2bc8de5c00506900075fd5aacf0477e527f4f48f2c92a89c1bdb4ff459bb1f5191eebb3d6b5fc5b7d9be39cc5c0c2fb5f8138a1f08a7c8b0a02449afdfa3201162027e764955d3a7a34b8682d44f5a28c4cb030cb3145a4126df53ec3640b49fcfa8da47d67d3288111a2bed6dfbb1ec04c741a2232bf3570e9b07dc5d1eaf0f5790960dd329efdb887692ff903be485080670290c52b59bd7181cba50be3d0be65c4af0838a79a274f409f5caa63ff13af71e5b56c4e77f7caeea33a91a2cc943fe6a27883a174638148ab1d57fbfe70e992b0b40e9470a7b7087c3c2e9e2d47d42db1b2e5da7b108f0e74136892c3d6d4c2258d62cee556630b5fca821a8d1a7a11b9d9110f3e86fe8df5080eda4f4799eb63a3b09acf6e57ab6a3c017b0049685ff7bd3fc9564ae6f4ccb78f085d3f12e9090b0e85cda19f13b70dae6f84875c4ca2ccc3d2bd876bb160729124e95eeef82a92278831c51fcc489d54c44901c3d9ca3f9136ada1688e09646c67032ee00828ba69a40b921bb90924d9a6f497a7158ce2144e9b597b4d262ad421a9216dfce3e6e2d18e95394dd0ddcbdbfd34c356096441865c077da4108b2bcc0f2d01d5d0991d403831f36e4af440b310aadd6213e9560e705d3f0245b960a40a3a965fd1eb6731af42eee63ee7729ba05bc55177a63b1bcd642b5173b1a3ef9f8b2015064e2ac2c4b4cdad1ada1c3b281e473fbcea38ade6da37b3923ce24e09707175af7e7bcd8f3159cca7a55e28ea166838b0ddb2053604b0c8b642f1d6734259ca4ac453d690ea136b86a300d4e00a2a9aaefa7eaa92b544ffc841fb50ca6383030a2b14cde0997e7278c3a638aa44dfa2d5d00712b5df710b3d6e25a121cd0db2d7ad4ec10eb7889f03988b581998dfa1ffe31b690325935cff3de83a655ec8baf3fd54cb264275cac980338d0b1e5a3b07e0db625cab7a7eb14d219a524e1f4c1cbb5416af23ccc9414ab58dbfacf4929c37647cde01", "52006a526352", 1, 1642598088, 1537743641, "d37f2885dc97ea833fff9c52281301da17887a516541cc4a8fa5557cb6f97255"], + ["030000807082c403032f9190b82d71825ac3626cc4a1b6598359080f4d649d59eb4f426bb1978cee6c020000000463520065ffffffff32e1642faa16466d8a28b858557e50e2c8eef3906e216ea3bc9b7951089ef66b030000000151d8c567a8bf9f9f94d9ea086023738ca25b05bb5a9dc33bb1361d9ee3ba3bc3b60c8b770a02000000020052ffffffff0109d495010000000009516a53006a63530051000000008eb4834402000000000000000083044802000000000503ec27b7f78dea6cf0ac25d548aab0e36041722dbdc67bca6fa1ba95e8424627ff3c850eb96a11c05a0319821d917b89274bc57d9f57bbee1755f5d80b309b650c7266bb9682b05991313752bc05116121f7d3775e40a4f2522419139267400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000028f85d40fa176d0b3066d41fb0df060804aa7e49a34e37bd13fc0942711bc50c3217376f1dbb03a2cc4dba4702af1df6e8bf99c5e56fc38ec1ee425108fa0252252b56f6c6795d3b56a225d254dae7ae3e5dd8c87f5b9e5014a61822be0a86035700bf58542e481f70d59df692105e2b178928aa2d69b1aaddb2ace58e80d2d202175e77e62b7ee3964f966e0cd59cc5bb6f3f49f1c2e881ddf8937fdeaa06c496032957709b6efefe8ccbb86d90c6a7ea444d495331b2835ce3dc3a6758b7d74c1c0a07757fc5b5117297b96431a83e079eabc63a0363ccf5b75037b6ed63425ca8129c125ec9c2ce52f84bc6bd294c8e2861583c42eb5c9a723329d1664fcfb7f116031068e81070481ef9ce4330205cc27d1b0cff9ed0c181ab067cb740774c485992032a27e1cd514c4ae4c065abe77f8cf71eb7f1fdde948240f9c5748d651de23ab1030fcb4ccc97d55f5e9e106a0653fb9dfc6ce90d311a8055b06ae605ac7b35d93503196154aa2d594da635badbaea3dd436a7e74d72683ba2e0398e816e3c4686d250318d8cca178374610915bb60eb0d36e69a1ad515cc7326f07e84ea2f1d97fb3a933202e3e99589d1e56e89f27df03dbf3787e60f8e66079bcdcf35f1ce233d677c13c2d4ac02a70107f1bc06559391b45b903704c20d6e72fa0b82334c0322ad36560b5b2bbafab56ad3d80570fcb7ac2bccc8afb755f2ea058ffe710d4a38fd24330cdea814243f37b2f02771d1c97e76b14fe67776537c42f019e68c03f3894ddbbac806668ab0500c42883b702f634d625a518b0c47763947c7a5bad89da3547b653dbf3a9731fabbbac0a88933cd06cc5ae685e33fcb593cdcc594c0cd9fbdb1a486877277996193e6310d17a730943d56086c94e8e90856d6e54ed74652b74ff144387905c4f707024241c3071016a7b9eadfd9afa596dd49e096006b765d4893b3dfee07e2b08cc7482a8635ebfcce2d072e709b5d76db4cae841961a2fc2601055e3a43ebaf0f47ca7b30e891d64b49363b028c6550fdfdbc4dbf6870cb9e1b50df818c3368d744840f6e82372b8bb6c918113c482ce06dd60ed77e0545cf9ae6a1abe506e3d9ec52c75cb313f05b1613ff45fc310866d18c1973b9fca12fbb9e65939dbfff60f7cf4c5d8bbde8779df16fbc411a98cf74b842b29c9df5f0db83c01f24ab817f0c285ec08d3f32d85cf22906dc125d0c342e759eac6e2db3218d01552ddeda19052c0b040d1021563e8e134315f7ce57ea9a206d5965f695fa3365bb8a85cb434ea531414ccba1582dcb588c84d74c0f3f2ab0cc4a4bc4d3a808485f068765db05117ac26d171202d7c6109427281c64e8bfe45cc33c6bd29805610c852df06342020c1fc6153fa1caf2400eb85fba35512848b420488b8170761ae3caa1b6814e82c723aad1fb4245c76c58f8482ea589a09297b2bb5e51d37589f64862687cd055ff5719d512a9e9058c800f2915851a45d06811ea5643ef83fb7cd396e08365024274fcbc34fb47e0bb06561deff6792625f4ab66cb468d27ce985c79361a2a2c09b04003fb211593d32fb441559100b13a7bef9413ac71ee8a8fb6f8c2d44e366012bbea181368f514291de0f5157d74be24b7fc6a8c81b3c5fda81c9e3b9428b1383f3f9fd6537914bed70253df03216714ee085db51b4b2cdb9d29b611799711f0170085984ef1589e490e874ba635cda2242b6a1e11392f4f2a9806bfedb84ae48ee70a5c0f53ddfb293406943be6b0a9f858309e98f3551796e8dfdbdef4cbba16f9d4a16eded759c70ad5b44000a0683df109cda45486151133a95661f47e6426b79ef28bf7fef8091193c29dac5ae4e0ddeb7e5ce27af5495c937c6ffb54f01312e7f22fcb3bef33a7dde28a4682a8579b822d4d126ff700adda39d5609c89c65739bb99f629faa36b86cc8c046130663f1ec22cae05c62cad3bc067e06029b463353df6965a9903b9edfcd95da692fa45f2416eb8be3282eb9508ca368e99adc73e493e30ad1a65bb134ae6576b565cfabf0bc8b0d9e1048a2ca5a3a2f00706dfc0429419806491afaa78b4f50e646d6f44e2a15ee3e68611073f46517ea09e42d4e55ed72c5d4ea58f38ee905997c0961b9427db18583874855b970528140182a51afefb68edea747f2464bcd527ac724d114edef0f6bde4df8e1978c91eceb61b42d581e4db88eb67678d3ceb0b66e5dc06587b24763a7d4c1b54374363f882bbae4dae3b4f71d88851004e88e2ecbef25c2dba5489e323bc140d94540aba76fe7317690b50300000000000000000000000033de9c5e4829c1fd022f8e643a10d8dd25ee36159689159d1059f8832a824119c4295e0cdd3d72be02f79ea0d2019694b2d6b8feaddb528ffda0b6e4ecbfa961a1caa4211aade6ed1e34a614289ef00227d4f4f852b1a65f05b801609b3878a7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004047da47f47401735f6dc4b934677c88aef757a3300c63bbbb1f8069183fa694716a11306900ffa58b6e9cab1261fb431d4a9e8c68f9a7669544ee7f66f322569d83bd632ae9ae158d8328ed8b1add5ecd093dc5466b678ffbdb7d36e04ff0f122e0947b4ba81323fbff7a2df226532a228ad7714745e792a6d0c5e8024157420330000cc129ba88d5759f70fafd826c161c9ba7b1f41c551c47c2587451d0d749021ac9429d70d0a79233f85335913be9983567e6f3adfd8ab53bc533d779033ecd0a03639ad105585fb0f34152c07543f9cb7cdbe342d4d1e21666a858ae6a2b2a529fd5f1d75405c27ff9b809129f0f689bfc7da281634e1acb2280ef9595aaad480220080f02eed721e734cac5573c56b47a03367166f85038431176f94d3a1c1486020891028319ae99f99abc420cc70c8e2075ca87accac6d25b74fe3228d70938e8030835291d41db6866b72c8521e209998215f51fec65413643102cbccd7e7c576302188165baeb7f901084092320a6ec01d2d86e0fdb58ed30f0cfbbdfcbefa6749d0320b35f205da8f0f619dc28656e78963855239c5eb45ee73ba66b07e56ea21df26d2460fc661d7f551f541ea8cf8e8ff9f6cc06ebcf10ef9cef447f39ac64f359abab43729cf5e1ed55d547475569e356d01b9e1177dacdfd83f758d5d8608de613c6911749281b7e3eb7ce72a8dbe83003c26285a97f3b17f3796b83e3a10b43724ca2f4c91df831255dbc1f6bb5325403c808e1aa0f27650c8ec7247b99feed6999d0ad96b19f470c00bbedc790280812a8f9a15ff02fc963c5b90dc62be22e8a38590f9a5e058e09a527d1d894046480dbb9a4da16bbd2e1e3a5f5aac4a431aefdda25940f4b2745459464f7349e05c10470863d420ce2f5824472b74a58e6a7e324a9cc88fc9df3e59bff567d1837ea76681d1e11d30160bfa4e13e4bd15417fc3dc2885235393d656b5fdd90f9a269263a51a034981e481cd0386b1fba82d659a30f5a35438ad43948ab6872b29fb5d2a21fc6135f32b8f49495290327230c342b995a285f0ab7ef69caf85c7782fbba23d7e1a7f45bcb4180ae7cd28303160307e33058ee84b629abff5cf2d5475d08f632d0fd4f55783d3104be3b84a248cfda1efe5892a277db7056df0f0fc4687a9afc0ec8eefe6ca817840004039b17fef68077af0fa2c2889b5d498f1c7eebd641b28d91be394403c14dfcb0e81b3caa3d73e62591172c855829f331b668b300536c01a11bd72a13076fbdbcf65d7e18e6640509c53feabdb5bb234d31149cbac6a99ed88587fe5ecbf486a5b1334c7839e8af48feb7d4824f4c4ce59c33821b466dbe8967fc9e568c28d8da5d7e3317b3134d096b23b9f3e304efb55e5708c337e27f8628ad0fae462f65c86801b0539b70d697efb30eaa680d5844cb936fdb2229d4e8201f8e98acd9bdfae9e54741db83e8918ada9a9c216b32d5f37b4673d2ac67294ed68f921f830cef276c36fec6b8951d25d5b8560325676e1f1baec826eb2025fc4e4d1833fc89478c2d623b4d39733099b3d45d931042e14785df3342f2d20fdfa3a69a9282ba2b093740990ba5d02e1c45893b40a084e062ab7aac747a6ec1c3ff0f0b3daeeafbf43579f397f486cb81293874cba594ee78f12db5ba95e99eee3d0025cbe2e9dccf8aa7ec728fd2c09955f0764163fdf7a9aa9fce3300bb62e175bccb86ffc361428422248101a79cb6144ea4abd0406d65cfc8012866557a510b705b72a0a053c5209901be4f4838d1e8e2952c77a2b966df021df67c7c90b9bad2d6b93295d3e73791c65689248354117428f7eb924fc1e6ce81681e3bbcbfb2a77a9821a5f05fcde850ffea971f56f895fcfaea651126c749bb2633f4b37782dd31eca3f274001330cf688ff0c9e822191068646a3a8541b8b9cab831a6f6e8c05b1aeed13ad6e06a052ce365abd9e0b9d829d2971179101324fd3b2339de37213cea2a2663cebad79dc9b77a71f93c2ac257f9d05d1e97854bce9653920964e176a207da11a8b209fcbbeb42ae2d78a57e81090281d6787fe0aa4c5154300b19e698abe579b28e05eac4094d96331e92f471759066ad6d5e49f90fdd880eb4188cbe5cbc279a2616498297ca554a64b62ea23235067a6f06f395e993f3579bab5fb2dccfa52d604cfe0e69301bc6c955ac893fa3776d03ec605e36020125fa6829d0a352c59ba808228e9aa4e83caae1bc7a96cdbe2d49c36543e3335c5441efd169ea2087eba681cc5205c004ac4fd1eba322fb5d429f451dcc949addcf71a1707fb409fddfca4d693ea99de7a201a119c308c435e1af2595b0dd7d9a88582878af26249518452a96c87e7cf06e8feb15384280d71782b47fdbd2c0ed8f288aa0d1c0823480f1ec332e64849b30ba2038507be5bb218ef80e", "5200ac656351", 2, 846273779, 0, "7819b1d350bd8ac24eccdc7bbba9c1680ac3ec3f0dd153a4349212b24b778c7a"], + ["2910534703bb7e66e62eca3aad22e72939c5946b7a6afe0886679e6e278a9ff6a854e967400100000003656a6afffffffff9ceb321f05ed8956bbcaed2a97239891fd86814f45f2c6cf8517ce4f67262c902000000060063526a65acffffffff54e6fd072d4dbcf56cc193eb772addea12ddaa1d1cc6ba0df6da49c11d83419d030000000452ac5165ffffffff046a6e7203000000000763516352535263d5421a040000000007006351526a5300e7f06503000000000500535153530d17640100000000066300ac6a65520000000003000000000000000061d4fc0100000000cfbc24d6023ae87b64ad4087a1c47e464fe67af6c5ea3400661f2ef894dcc71e8406542039e0e2576a07b833da63e18d8d70f2015f28a07d912d46e41bf5db36ccb0680051813ba75b54fb79a5e05212114563b1a086de4bf88d1e547a89ff25000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004f884e810e3801b69d52f11dc010ba64f56e80374f6abb43210ca925b1150beb3947d477ca2d7d24f9a611ccad72349fd1768382aaab4010dda3a69b52812ccea6fce209222575842f54b8d7da11b248c4a632940bdef563dffda0fb5a48e9d6788f0e2d9051451e0e1d18a2641f3efc65fb9686a3a4ee891113753f44321c3f032757ec2f8021d3ebbeb2b5923125f7fa28a2ecef48cb88a7a38f23fb19205d760313bafde6e93afebddf158cf922bed30f0d303e8a8fcbd710a3e5f43635268e0f0a01ab0419a985730741005a069b4761df38cf6d95bf2b0bfb6a31077907735026bea666f866eda112b30ada45166cc978c06cd3d5743d0f96cabbd9c31e850d88021b5576f74c47b131142daa4725c22ee75c3e5a2b3c93daccb5b2848a34f4cbd2031066f2fde8b5a1bed5ac053e8b7a8d66e4aee3471efa5a3beb0c94d27e377a4c030938c1a3fd426718976fb2998f7152b130af7fc4e17ac93f94e54428710ae42f030284a3c66adfc29eadc4c140b813f80a0f832bcdf2c9162815d151fd9ea444b80220993758109ef44058d977fd48f7042c6fe0164bf90adac1cc9c2435703b96ef0f57d6a617bd926ef79f8e6a9064ce966fe8b53ebbb886fd45c83aa1504a0708696e787fee5aeb612d25650c6b75cf8a75f2c9ed3d97256d014bcee103a47e464af1e0468fa7262130a70472365c825cc1421c9adb0e504b285b6b6501f1dcd35ffbaa044a4f4d385da53a06eb58b1e94f73b66d754fe938dedd939b7c32964dad4b5f0af9270c001aedcc337401a6a900bbbbf7abf2804beb1f981d0b2df4ee52466f32f7fa07951e57769e6028145b686c26f7ea720cdf513fcd56601c35f065ad79fde345941ba48363f46981e41285b732e12c81891f9b27fb52b7c96c62446ff755a2bdb3a08d4089dde2fe84da1234128c56e94db85d6e6b0619b2a278ebcd9a37854470aaf81b0fa8a9427bd3278e223ff795aa39dd1d0e7dd0c7c235c78c1abde01cd71507a24b1f18a0e43abae432b548f79c513d752f7d23aa9b95f40245543d75cc6e48ae9ccb64cebb833e4ce614051482b0583c762c350b508ec09ed3cb604508c652b3478f52e149b6e4316dc094e3716a0f94c99b749f92a23ff60635df6ef9f3c4ce8cbb869e85ea38b4b800c71e2383f3bd9be22bd64464c5178f347ec79bd221270bf3fdc019e1fb96c278c03a78872169ef41f7a0ce7a7af2f5b2ce0cb13db69b4e39fee4e012cf4d88e48efe098e849be84cf98d75709b4407b637ca91cece473dddb655d4cdd6be27f1c470e73bde72996fa0b937b8c6a680694919d7174527ae2783866d3a741cf85d2ed6a917b2cfd1c22e3c232bf49db614cde4aab2de0b18e30c6d3ef37b6639aec796827d12159001aaab1697b4f0111d22d52139abece99eae98e07bfc211289559cf518f6ec31b6782c461fcda046058cdf2a811f0b5c645e86b5cc04998717f3b51d35e9b62b393ea7845acf645654d51b4d8254d0c0e81848d84e0a07d6e246e7ada9048d6872a8f02bf8f0a8b4e6c4b889b05c8f73f4c5eb21de3e00abf45b118d7ecc69d41149c20dae8041c8ad8974927a50f70051fa1caaeb6e8536fb3bc558cde0bd4f5b2eeb42fec616885461e21df911d67fa71e570b94c8ecd418c70b989a1420945bb4e33d5ff4e8e6e423a15b6b5ba7fc062c4e930e7f1b799777f0bded76e5d7c61046d07be3615e25de7d06af77c9e562f127845d295738074814457e43b193d93f75bc1615c513b050ec79d58a3d8cce16bddfe84e37df07c5bbbf24d951f469a99ebb4ca0d4a4a6bee872f67602d63f8fafcadf3894af8a662ddad5e4e9955e354608877ad33ade9018e75bbdbb4cc0aea19428970abad69bddf2097dc44f9f6857e0fb566401305fedf6d57c29db51e60af7d285e1d80b573d40323ffc478b3a1500042c1f43109e0eb8aacb70b66bbaeee552774f146282e4b22a152c4de6c2a0f3efe1a8491064ed514c3640bd7b23d678ff66cc35076c7ca944814962fd7dc38e8850ef11c5b6175ac9a5b91dc449a7ae6d9af7970f209db72c45dbed36a1d4124bb181163fb802615b10847e19b654cda9720c7be9ad23e8b28e89240a082931e2ef416d69bd3390fca18ddff9a10af5f1e26742d129eab8b381bb5aef5caba67f78a55ef7129fb4bad3a860d8519dccb5075f78947080422a7ce0627ca575bae2d72ed20c171a6756617192e68cbd5d7d6d360a09a3eb7589bb67cdac8737893e2b8b964a75aed6f844600dc192bc1b655670edfa1a05000000000000000000000000e257c5de9d485fcd065fb4b5fb6c522d54bfcc28442092cbaa58e8cc179dccc45c3d341cecec4b3e04aae8d98cc6cb711c4d2620ca3b147fe2c65e4a288541f0ffbb604310747e662832edd3489052e1830a849e142010168f36d037ce54134d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000383444f3ad490d0a6600b41e3e04334d0d7f755dab23b6b44b99e6bd1633d7e6ee3cc7b1cc67132c188ed34c4e9c9ac658f12794146757e9704f2e3aa1ddb4b32545090a4e370ec29da673000c9ac7efa02642497352d2aa738be897eb508e8dd88aced9d1a020dd5bcd663e2cfd1878f30b87b5dd4e1dfc972e1a70d65c1800030a248a2e5cb827c22d6c84ea63d953c420f055f4785c7720a61b24377ac572b102305c8535172866c02cfd4f96601cbd586147dedfad3a1b3a2d51bfb789b221cb0b0050c016fd08b0bc102ead3300e9c3c56c3c6230ff8dd98c0663ecb7116242a1293dcbbb4b5bd09c4b97c04b1f3f9e77a9bece3efbb9c5a1b6fb9823f2094c480300716a59f12f861bccdf33c59d03639d8fa8e3a12f3df013f35dfb1f757b234c030e88d53730c051e1925a317d3f9194d614d927e911cce5c1d8e680394d54deac030d9270a7d1f5e1ae76da1b42dd86100073f784c8c4c7dfb040c8a7d656c7fc3f0221878d3bce217fd5ece63852f2fa4668e6fd248e01623ff9c1ca1d7d3d3150b9020ee9d7a3d38465ba4ee0b6c700e6fdbd6ed28d956c993200cd1248bb5a0020bc0fabef7a9980bb6feed4eed4caf85822b7f8edc7713dc97be5976db47cffb677b3475889e7230d188309e0d4efdaa7c974d2f955fc8dd67440899e3b34ad95d9ea8bacc1e885eba813a0f2df5abf85a9691094dfef8bf5f4523c7137b6abfab94e9a469e488321d01a5418a4495ad83b2616e760ec7c4e29bdad7e77d8995980732f028254a79107e68e06caaac9cf635d87cf48e73eafa7538e66c74934b97cd126f1bc5987eea8f85aa0baeae0caed3e49ba737af88fc41c4db60be5bd5f631ab8de3dd34f2bedc951adacdc070641bdf5f688b291e24f69b054a8889768623ec7cc165710b8c09eda8e322557b2473418fd7fef0ebfff04feba75b93cd32358e8b10221d6bad0f92d629fb2afeeecca4680e6a9c3f5ab268a87c0e8373541c5b7412d39758d72ad8432246208ff4b250afd525d36f2e00ae516baaadaf324243217f03355b2759f7fe0148f3c9176513d0a6875e926f375a0fe18187caad03832196bffcf463bebb4f9dd5d4ae5d80d3e0ed8f4445230c407811a5cd8a4842f963bde0ae3d93d5995b33c8407fb39712e13571fd8d6e484547c2635ea0d747e2e7db643a9f73090eae631a439feff8d31fe8a5498ed576227c5535b17eb432b07dac0228e4ad66374c1c0e02864eae314c1d1c1bba658a65ef431b7d162c433c22444be1dd7a7a3148d1e72a46f862164ebaa10a07396e9b32f97d4faa7ad47b33c39fbdfbebf54a201f30e4bad08a428174d7744d8a4e19f6175a3b3cd03a299b640eec52405c5bb37f8b1457ca867d86926bb2d5e5990b7ecf5700594e0e9bc5fb8015489d6b96e57b0d4e613d4370da4a877ca7afbd48cbacd24c7c33752fb6e7a5a6a2f61b755e96c7f747efc3a4df116b28e1204a0fa675b3155ab57a42b449877e67127a6d7d98c8f2e8810773e108680f88595a5bcdf2a0f87dd8ae485de2f8312c7f7f7854976678a798edaede051afc0471120fd91c8169e2bdf2a6019ab1f530a37501182c002a8c07466ab1ef50e52f2dde4f6bca7aaa2ee79ec1e1df542df14569f62cea56e8cc344399404b27c8ea2f67c453a5701271aaa677b1337a36abbcbe7b8735bd19ef98819db13d95d08e736b40b60c016936a737894db3604f50ae2a35cf3a63c1180e172eb6a0dfa10d27dba2e8288829cbef89f01f557732830197388b0a61e65d0396eac56d953f5c70ed7724c61a4068b8dd083c9462cf64bbf7588f4996de00f9d2230ba462fb0601965de50e2539c620765f57016d13c18358da53b2ca5e70365b04a32bb6effcb0377ca8861b16901afb4f6b651b17b05a23932f9814ef3c8e50b4f8eba58ad2ddeddcf19e70d021be93bfc1bd3ea9e2cfe37b32721b393f20c6ce5471563af190e607b6a5ff2099913c610c26be41738591cf48750fc64cb5ee22ecf142fa678224fe344e3e23c219a9686a0071c3d68ea71f0d2fdf649ded863f2295cead339f0c83b96860c43f6eb94516217dd876f433d4ce14c4c0425c5663ee89f0913d011e9f983f9e6e83b1e7d5171b4ac6a9656a30a6053a67af8799eaed10713fe7b10becd40c340f6037c21eb9838b9066c220cd664a6b9a13ca01c387d7d65d8e48c643e8c69a5c0a82ffec116cce0b04c4a837105cd9962c88f5dcffea0272a488e31cd63d8673e653d12f9d1fd2db766110229eb7fedf0293e1022000000000000000081cff005000000000dcd84a6fee3979ba4122debfffe7d1ad30a12f513d71d09289005c933b17ce072dd7fb8db07521fe3571511d60046d0b993ed81370dc0a9a943586fdf8e4c5be7e7a91a1f2976a09731f66602179d0637c0135fc21c8bd0beecfba5b930fa5c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e984cdae59105d756c7e2368de6ade74ce66d55cedf7ec4e8d75ab5c929a8153e0280959278dfce0478cf44a4e723078214170c0bac7e414d0668d4bdcaec9847e58cf4e7b1b653b80907817a39ce0552beb74b6a96084406e0400f7a502e4d217c7dd72d8dda6cd0f66e10a26b90418bd7b9966201c015853786534222e47d020723fdf9e6360c9cb41f06c4c88eae4d26beaa4d86bddeb757c1dbfedd9bda8b020f75b7f8fdd83223778745986e83ecb9a3762512c242ec0919eac033bbe19ded0b01c45ce157b3e4dc1c901e716013f669ecebf5e1c8db6fe3ede533f40c9afcb3d934d4e895c795a0e18a67daf961f4a2252f2dfe61e3ec09779c3e9a6780cce70215a036ab226326e86f3d036094b6190417f4d7e9d70327aba6464d4715f516d8021877e5963aaf308976cb3a92a782a848fe49ac07d7dcbbb60e7c96c904d6d82603145a3edfbb691b13eae095534c3e14d88fd669a2ec220c6fb11a42a463d24024022e4c297f601d6b48cc4789fc0ef806bc300289b801fa82a16e6928526a75491d0209f44c8d9875c1429e95936c379b9b86ae1b4f149bbcb58b0ede3f8799e78ac8ab6d603e02eb85b9ece9beef13487ad1526f3aa9a97be3a06dbe83df879d7992da03257dc499b479d537e4dbc31e04c6a70047379401e9527c7678e5c332ccaba2828302545387abcd8076af534a789c422326f12d6295bee946980e286c071d772aa2ebc43fed062e43455e182752b2b127b1b1b2b6170ec474fe55d174534ae48e9897f096cdac55cca26291bbf751bc3f255be541b52e9078d36abb8d4a51c47eeb2397cf51cce02e975bd6e062e2d21614b1c0a754a4c1c320a75498fdc4f18916790c5e028a3bb6f2cb8724b5b0cc4e3cd003f1479cd2e70d68fd2f39e622ba553b80237f328ed2fba781184ba6bc129201d0f40612b2ddc257caaa003d878722a04b97320f41701d7bdaeacec6bc2644054db2c4e4fdd3d9637b8cd3299f0af7aef0ad4b2acad55e380940681553f441367eb16f87409f5efaf32ca7ff982f787281a93c103784e7cccf468a272b46f24c43a03a5815859f6bc9159ea53ea94456a1bd497dc727e7a5479dafecdf235c775d9695cd079106b1e52b874193e72805f0e54e8354625f798f8d041642e88f98fa047bb85ac1d9fb3a5354880b37d954a54eb6b06dfcb60accd3dad2698bb918a03f9653ea7aebe9a844e968a645b2afa6e3117914d7f0cac2682d036e2d0d2db04bd7b201e6cb149a186ec38e4ea07896353dc2bbe89f91ca3e5952535f29163e06b1dfe0cc0da0ecb1850e1e80f99c687bd8e188e85f834a83b3ac9efdaafc17eba0d54393b18be15cb1073ae21aeb7d18f69de8d37abe164e399a81ad7d9f3b2e2a3c0ab1fa488c018de6db379a880b4a2c4f97457fbfae19d4dd2cad58e696ac1cbb984947d5269e7b8e4a4aecbc6ad276ac707f48400aeee84aea5e403b2aefee9fa00630ea982da9b712ad19aa88914a1b11c60d75f1224fa829cc90eebdda3c34cf0972ed3ffdd0c66866925445013b735f3a67dfb41c604024a5436f5cb829665db6ab1fb1ba372240382d5c6eef069c41923be2cdd6146e513a426f6d612bc78879bca8a512fdce7e366cd9470e57a26911ee29233e65389a4053159faed854c97b2d0483522567f1aef4ed1d193191e670e61254c48500c2c274a81c90efca8adb8ff1b6368f8b9ef612a9588704808052f0f36e8be7045933257b5647d4cccaa4ebba30133253e210582dcdbdce339fb07d079bfec0b1c709212bf30c4fd1a63136305a663a488a6f8d9373597f2c0a0594c33e8ef5fd82f270649fa538eeb055659877ba4ffa9cef3e892f43fb5dde58d35663385d60ce3c8e4b536277ae8b616cdb3c4c6b763a6c55eda45d63f07e9deb792bfe2492267294e7ea9497eecaa4488222825e31104ad975f0c6f55072a306d1f6e10275d259062e6a0aedcf3e32e2dc6b31a89d25907945b69d43050cd25056a37530619f4a5302f27b5001d2abd81268ab1e81eaa047f649e8d2c08db40c700f94e68bcd8af66bcf35269796f3d516409e0d594299e7c22314347d1ec15b553600c938063b8eb2643064053f8d3021319ce7ad564af520c4941b4719788955d796315b549d98e4af4c1911cb92909f062d497cba25f0142a34bd0d707a7f785ffc808bf5adc79adbd63566d668496e9c187078d450d658c481703fb1761a05b8b8b93b40c2889fe5ae66cc662ebf172dec73ca4cc5af4a05582edf60da7edfc86e4684945eef29a5bc58dc22a623ba88984945dda436ef2acc3a33ad40fe57a55e01ed95681d1b7f5ce2d648bd0dfb6ee9b1ff36a2e1c50651ea15295c0c3591e1443979dfade061a35afc36b8193610cddacc023fb24cde326b484fe3594d65a99373e804", "", 2, 754103613, 0, "201fc955a01c9b32a554501cd3c343725834ad1b05c29a8eae4e38c106f6f614"], + ["0290ff1c038fe4d51858e2a8368b25afe54615e81c26c92da62d5a977cafd9be3bbf76c96d030000000753656a6a516551dd97e4a2bb50b68e40042d3f07a42d15009f6f8956f4da366d2cc5f9c69bff1d35eca7a60000000002636ab4e97acbb39feaa7c5df5eebc964815e41f672bbb4b3f70c8dc0b307cb2f1474cb5fbc9200000000036351acffffffff0113d1ae000000000006ac6363636a51654711540250e7dd05000000000000000000000000ea264cc2b7f62f3530ea3f813cf91707788df27616306c0b109b289f22c22e4aece4b607eff3015715a8bc54a7c5e2ecc1799f9227ed435eccdf871112117c075d230d4a5fa38376a1b18469c7d2a3f04a02a4850ce2a785675a71b803c67c6800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000596d5a0860bbc69cef07e5ccd0b60b56ce496f3962b73ef1064f05a97f13df059b409c90b9869427ff6dbde835e8f159f83e9c8f9a01be5dadf06826d11ef0f6c387b510194583149ab55a41bd16e14ba5ec5121d86062bf23402ede3d773fd47944f482396cdcc0dcc2789031acb518ab69810750d4bb18035da74a026fa3a4020224e5a4a96a0d2ddc55574bd76457dc41e93f6ada1a836ef851bc5d7e3024e1031525007a22d420cf512f8eed9fcd24a509ddfff9012649ce37fef28d7957e9910b034720c82b9d1f23627f1d6e852940f0e44f9edded57a47da51aa77c77a6295bff74a3ac9f886205e71e179a628289622b5692f4a3db0707ac855f5e7b3ca72e020c4fac776e7a3fecd869f0e0528d84907a22615c2e3f31cb6933682b7b4f1356021d5e8b596d434bf0f67c35560b55b1663ebc912ca1723fc1329f5b3009f0706c031ee4fc23013c4a39225c312ad51ed7fc8a21c556558b05fa190dd5baf0b335a30315cb33ffca17caf21b2b88bd275af320c135eefd521a0ef712f9dac98a5db0c7022fdfa1cb61f6fd3411010e904c1b2291dfeee4a8c0a0013a98b9bc218e17392533c4dfe6cb1d5b2e41e1f6d3a74826fe766c982432bc1b0685eb594bffc39e16b9edccf6850ecafce3921509858f372fbdfaad8d5641a272f975922d2397e54c1d24982ef0141e9c32a15ee67b10f04cddd0e6690fc00d19c43f01b08aae30140d8621d5bc22d718434deeb26b808deeec46fb5e4692b4d07cf9e25f1b4fa7ac954fb1850dd1a454ce18e9c77b5cbd9e6d8d5ead045094c40e58b2cd72c88d44c135d5cd306674f1019b20f1187ceeb9e30b60fb7b448752520e4e8b013c64246dcdd39985626e416ba1be26bff98b2fe309a129b9b96b85014b9ba390a06fb638fe20713fb915dd7b337be54d008be37f41c8eee297e50f4b341f9a5c4463faa7d6a085117d5304281b7fcdfd244da7cbd4a39da3908d76734f9afabd67314cf412e2523676c55dbdd525abf50d96e2385fd9c75954115fd0bc34263d86e5b4b6e62c889fca60aeaa10c976a746c1ec259ae70d5a88cc31ce0428d68b12cd2afc7cdae9ebf7d1072f1edb1bc436803c84801fe85af8a60f0dfffd76b679736a4e56a5b70c9c71256b114e7008a8e1fc75e54ab65d7eb4fe39d78500b0eccc782dfb28f068a7d2955d526a73ac2f0c4a50e67ddab46565cf86b7bfb90b43303e566662e66dfdb9201db1b52ef286147fafda627e77ca9101721e8e6fe545077b75bf5a0a0b211e6fe3f45c8cd055fe338878a758cb174046e0ecfe060dd41754280bb4575eb60abdcfdf6fcf2042b64cd5f66acabd6b32bce6f9cd00a369351f52ba90182b7e5199a0c89a480fbac4d60b6647ceecb41719b4e15e9335f5f7bdd44f12adff01b2dd27d0940094461fd35de79f6786a1aef52f3bde41b702583720851d7c9f47063491058a889f7893ffc6b360209c50277aa077e7992ab710524b07173dbbd23aa5078cfb07a6b71c60038c56d51a259805657f79230e2cd9b5057a0ea90b16f6b4d3b4f68c4a38872d6bdac37f9fe5cabbfd5304b5cd4af6da41514a25c0f61781e353c01dee8ea5ac651f3db2434661db32007de050a347bff8fff0c50758ef3f1f4b2d2ebc58934395b6b71801a1023204f925451f1c7be62a2d42ec1edd1ea3130fd3142078418f7ca1a9f73daa8ebf4218cc695996198a64e0b232efa5c46fd9ecb1bdf9f00901ecf10fb9344ada723516881dbcc603d474e821fe39c243ec2483edc40b77fbf11341c242615f2a7609a9111c2a2465832510111ee7b97ee322589ee33ca3ef484c1648a69965cb57e9c2b2d221f12d5bec8e7e4ba5a01011ecc05ff0316b7cbdbda238704ddeab0e824965eb49b51debda115653b0757b9cdd0a3455a4f6d119a1108b366a7957953cb18383ee1ffb6e5739eb5293c5db80b73eb07eaa73eacf07d4fd5b9e9cc89f76bec9ed4927f6668c26496ad44071bae319a929a11da1b07d79bcc14d13ced6efca59a3844375d6639bddadfc7190c5cfb5075ad36b0994d87c3c2685b32e83237477358534e1b6a23b92dd41023f69d366c7a7cd1fabcd2ecc7f6bfdc3f21bb4f606e47930867c38f35be91514cc42129ffef9c911451b482dde5ed8461efda2a80adbead82b5e85d818acbbc9b632592e6f4956ad5b80a39686ff6ab34127bccd27d0f2e8c1d0493fd86e1127f94d0855fd4065f84f563b034601fc6ee747fd050005b6c06e69ffd2260bd436cff503d2003ba1c62f112bdf2ebd7a05000000000000000000000000881077e9684a2892d4ca6a400719dd0d36cb394c785cbbdb4628408b380bc66e5a9341f9cdb78435ab018fd70da7d7d79b06051c3da904a353afe056b1d5a17c9987893ac5b0fd1ae5ca3f716b71828b5bf10c475bc499b457338ed561c403d400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ef958db4198c70e8febaac12900304533d6df7908e736d7aecd2c60fb128e9cd118030bbd32c3134d4cd7e1409397e3b4985184b22b4f54a4974a7d7014ccdb7efce371ffc3cf5a39016d42ace562f99d8f1d625f02b9f90c021b047e059d4880525689054ee39af2df3c914af0046375383cd1d0d9708c4b7019fd4ee2d6318021d2b1a2764d85c065a622c27233c6335c14df6efa0d6b3a99e44fede9d64d737032c5f67df7b51c64aaccb2bff2b36d6db72de9675aec9ed4c8ae8a83c4740644a0a00bdd073a99ddc0e9c689577b869e3a877149a6cbdbb038caa6059a85945b39b22962de623ae9635b6b6ea5aa9572baa93558cd1c969ede58bb5e06dfd660c42032f88f24b97baeb4e93381e36ae1f7753adea4140a156c9942f01189ca1965980022ce85589baf514d23ef731ac1c2583279d52de1e4ca89607265b7b3b3a1394df022f482c5cc93d3e637bb00c65ce7fa61b4a0fee8fd5a408f44b0cf08acedbb4c1021d1066728b528fb8d96dd1fa3f4c19777bb98d4b76e90ff74efb5bf6f2f7991d0326df64c6aceaebf965f55c45395cd46c9aa686dc49235757585d551f13df62ca1b9c7204daab9b1208a477a677bb2792c919120a9a7cfa9d47c64d9fee17c6a7bdce24ae5826b2398940f9430280c50bca8bbe4fde0ed23b0be549142806ca75cdb9cbc73bbf130d4a9e8892b0906d055f2ac8985e28e8bab1c6f18ea43d99460dc1f3b2f175782b147ac2054a798ecfff39bfbc3c719a4a1d6e7f09199586b77ed640c45cabfa3381d5126bead4e20e343f3662415dad395e5cf5d91a0716c33c1d08b32592aadb8a8b9c55a3d5cd758548a768fdf2baf92ac47dcb9037418b88a580517ca6a0432b4eb8c8ac15d8d63371928a3e934484484fc22de641cfdc7badf296215e1ede57cc248d498b839647620dacd727d2c380792ae25608a4d57dfad209917714968972c2032e3e5eb323aab25bb43adfd1da99b43188a6297e3f848389a6fcd558d6a462a185b7493793759900374a4a6c1f7a4f0b48136749fc698cb80a773a29b01c5d01c3591b56686ce99534cf6e7241fe75cd6932d8dfed1526f798d313517a1696677fbf4f35cd52f312ce8363acc0072e412d38799149d63e6721c2c17d34e3c461af9b8e56ebee1590d4841cdf379593b3a7bc8312023a7c58fcbfc30351cf8b15b22b83bd1e8e75dc3edfc330075554b9993d1e739bde3784fed4fb176a79c15a7c3348ef7d8d2c44e6e2cf07aa87346e8cefb68118c2c523a5c4867db2a5b36fd23943373a2bc4a9d0e252349aefd280f47b2e7f2b542e80c128bcbe144f9b8253b08e459eeff1fbb4624a4d9ec599d3d4fe85f781d9ea9ed2c865851983cca830049183814cf8926c03d85e3097b6b03b7ae0d35acaa5dc4353cf6d6f8bf82898e644625ecdc39c25d97d48c867846f68287eaacd70ee852e4396258cbe1982dad6493d3c03cbf04b1b93fbc972a5ac28965ddcf13b1a96f5fcaab8deb2aabdba3c348dc007047cc9f911f0608134507b694c89dd349a1eb0e56be7a0cb401d30e77912726a40ef2bc77526d4d1ddc81400bb9a53fc58d99c39245cb29aff880399e1e9a8790f546ba7540f34dbd7bc5a5a5f10e63229e8976bb8da04f3d2aac092e591749eb5adf1702018bd81b444aa168d4977d3fcd7ddf44f3db8e1e2e0d6800f226004ce3700a21dda3c5ff5aa7ecb827ddbeef5c60b787f60cf57b5918b2c8ebe8056a930c613007548115e0cc62d3e9b2eb43e7a1678e01128df19c15553f314f6f27c3fc11b2d43d37d2b83911156d9fd2fbeb083e312a82554ce18bf0454411ce52076a3c153ac6649aaea32d8ccb8e5f1adc68b2865b24490ef49cbb1b30482c58a91165e3b6dd087e4169c5f7694e3a3e55722d40f26d20a7bb0954271e74c9aceeab6ac2dcc4b22d1e9b3531c7ad05ca4a34088a0ede98706a4b84d292553a2467439737e27bb6cf01b96554f7e2df3f811ad6e1ee2dce32bb9cce8d2590861132b6844fc0ecbcb2f63a35cf1d2ad47e1aabd39ad10ebb7ca80f295535ab67c141810da136607c03e12a22bc72cbee8f5417eb3c7b87b1e1617aca6224566109f53c4a4909644efadb56046008d18f38dcbb755f2cb4746df8f5394cd003768c17618bd66342c649b87756a2df03fc1d19be9998cb330996174577201ea8056024fb05f769d7ac8fdb0aa50b7116d5b17b76b634c73159c598dedbc9c578134e6dc85f0b9bb005ef431f1542fc63e134cc0fa0eb81aed8c1328aa230155249552756b884d3aefeff33668df1a80fb61262cb4bd07b8bfa47eeff71ae3f1d0e8aac33676b2833b712f181943fd1f13ab6c661c49610a8006d6139487347d6d6b324743cea04052dc7bde13fb9b9b768679d42f759ad22aee145f91b6f9c93809", "525163526352630051", 2, -1667522293, 1537743641, "25b07dd9a20092e2a3d21e93a15b62c7fd687caec76c7d3c106a645fd26605ca"], + ["340b39670163adc5ad0fceafe7877f3d160ee968b336903b57573dabdf7925cbfc24730552000000000363536affffffff0433066f01000000000451ac51ac0e019a03000000000763636353ac6a6a147ae400000000000352ac651dc14d05000000000000000000020000000000000000864ed20100000000457bed3d12af09deb7d2d9e75d4d91587e2c4c6970c84112fabbcd0a44ea1f50307065485b9889905f61d029600b51cd1866e425e6deeb26834661378243d64998bef23197d4746a61e63e5195aa7b8f4adfcc68537c100b6816e6a78f65c5bc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000db09509ef9fdcb9968bdf098485aa9e70f8ce65f416c28eee52a10e0ef9ca294bd527e9242a2c486261e0a7af9ac5492e5f415abb74f5483da3dabe1556845662e31dffe7b2f1a5316038fe1da40c5b1c09b1f2ca81b4bbed91e3475858667c0201076f4174345a262b3264d7752ade62134c1faa81c5358b9570f45beaff6b30201b9cefc23a54dabe0e923f46912ba4c8c79a6a13bfd87e80f524440114a935e022ce8e87242223050a6d61f74c898fd6ef281144ea1e73a512f63d7eef2c5a3750a062e6aee15dac39646375ab76e80fcec48a3abb596e0eb4b88835ad70089ba3ae8f16a57c3614c0cbc41a56946affc0d1a8499e4f2b122558c546ca8c970200e02220172afe8848847a2c4eb09828725d372618e938639c545d8ac9576f2c2b46b0219defb82fd396837ab248d8c52b0709cd71615f6373ca39889282093ca2e1b8a02266271ae1b93587630bf0976131e5fdfcf2b469e61c2f8d1364465294caa86bb032653ca97d4a4f23411bbb36095de7a843d680c9cfecbfd47405c1d0552c2167b030179ff73f301bff35c9692f2b2ff88c99d8a593ace68768f8a673e3c37ae71adbdeebc6340cc778b61bd8a3ae8ae1f1ca9839395fb674425001a70d979393d74bf306d30f418e49c4be819eb81e059cfc7006b89604467e967b60c5c16fdc0717bd4b1c6f66726071e8fc3f868e7eb956b15c6a321b8868f0e5d72853bc03b5138f0e33fdb8b7ecb944ada11fe2585847bf3ed0866cb6b840ecb284a4ab627b2b414cb27ba02099f1057e56785584f4955979d2bd65a9af977b08c3c1f9bca6acc1571761aca6127525e8e65320e5770a036e0bac1c68e967795dc0eeb85ffc8bfeddcf1208a749d47ad7c0571cca225776d4ac06f42ab463471297baec9d1f3219b13eb47c5e2a3d6eafc1ad97e661c54e3211d81988cafc379d7245240958012c2b7110acbd65d99957c43bd7a579df46321347121495a36afcf353f68d34c88f9f52f71efb670dfed56d257c6d7d304fee9d9dc8373a4d430b3fc0463f1be12cd47e35c19d7eebd9bbc718f54db10647c38193693fa4f611d71ba88c588f45fab2bf6556d025ce56d5114c67c0d0f00f98b15ea9c5dfe016d738ec382bb5aaaf268bed9c2b422e8e01a1ceb17d164dcece4ef39386a50ae5f2b0a43ae0c9774c8d3e3c3a3cc206ca8237f441b1598627cd8f3e7afee9f8c4e253a88e66348cf51878366093b1bbe225df4a8d3b65b286f5cb8b7125a9d46d70339e76a36fae6c5ff7df647d90aa868ba95e53db539d573cb64b5e9f22cc30d363c883654bddb320616e13270ef4d22aa76db1fb84888403861615ae57a2a06e3769f7560f75db8440e66a2a49ff7a253ad05dae9c753a4dbeec687e0341d24927dd4653758f6349bc2d7b973dff3736885aabf252538443eb06dde75b43aa2b3903661f634dd21a8e619935aef1e81afbe83a07e9267fbe91ad3d6c548e05b44b934464ec92d77ddc25bd6a379c14e498dadcdea6a2f9848175590e16659eb15e3682e65111ef9f571365f193ee97e5e319bdd9f3f07e13f06c2c1f2b474852256b53bec1750db4be441f6c4633966c225fb301e580e520b530aab3c697ceea1c9733c54670fe63d11d62bd8d19e42808b71279916eb3bbb8b9f04ae91949056c3b9d5dcd529ec69634bab1531640966b3f8d4d5ec1178d78459981268c3683da9e40cde0cf3e8cf5ae718f889128f4ffd9728be79dfb75341ec35f5648fb0896361aadd8c66433648eb89c1e0bbffdf33232665daa9c9d905634e6bfaea96fc9d1c9909dcdc0f791b9e808be3ffd0c528ddcd7fea5d18f9412906a6079c7beb1c40210356c0d0a3b8cb7230d1741fd4197cba8cb62e0707f21895b1431cfe055403dbd782d0c3146ac2fdffd7f5cd454fe6a04551177f7ce223d7d99c76c96ccad3e656e30873e4922765182a35a7c985348eb034206d3dbce776f2f0ad6c69852cef598317ed7e3610243af394365d0f14518354bff952de890eb8c59720bffa1f4099765feb2fcc614a5830f66218c68e7d4a677524692579e4e18c74fb1c4f242c6204bd94468f94bf3899b44763490f256731db5ffdc27860adf6b2ed41a4edcfcaea189e00ec9fb497d19c087da92eef807fc02ebf52972a13886c654713f06d45f270afd1c42f177dc32f115d83637ca43819f06c1292cbd8675ee026003c6f0848967fb567a8478c2d57f19d9e8cce0cf93c6fe4d89e1fd56f4c851f7b0a46a5704b51c5dc0e82eaac99d40e27344b9e03777257d503030000000000000000000000006b409b13f52131744d64be1156140c90e7288bc69684eb04e6b7a1d9d511f897c36db957f6d1b0ef0c6443bee62a6eda571212adb73ebc728b539b4e29f7b6ac9e93865de5cd76b65617c6470c254f8d31b3565d0e695350fc9a71e94393131a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000093d1d19b42d70f4e609c86ee9072ca617b033fce2db2aeac1d5ad8f5dade161fd8dd134eb4cf6954b1e1c744cbfff522b0e31a15028c16421bc521bf1d7340d325b37905f98038de79a75854cf9c6a46c1ca19c654163f984537b2a1c5f669bf951d5481c0dde3a3c1c11b79670e13f216c6fc2a4717dc5d6abe98a7a4d11a75020ac326e73c094ee31331faad31827cb2cfdc8e8fae65ea634781bd3c1ae9e5d302068d96fd48e2f767b418d0072ed047cafef7ef3bf839e028ae3a0d969be093e20b01c09c312896cbabf77c72de4bd3d689288b6c0ecfa3d74e18ff637db6c064e23d77174ba83f5d916c7e816753eb665eb61b18735205fec939ea08dfc8ba85e4020b51e588c173d3326524b758cfed60d2f0a7f0269f0a331c12422d42285999be02148501ba89e0d27bb7be0e06de578b52284e8558fe9571ec88136036ce9a27b90222f38bf2441b671934c68ef36b950387feb4677c4f1795c850a1723787fb3beb021abe5f4b5672d7161bf21605a1ed9aa5ec0189f61e8d5e881a8a6e4c7af6768b020a1f07e3e2c02b5ae3edfc0f72c7eeae1373d416e339e50aa44ff93ff9b368f80e62eab8077f2f6e883f93e8a632c96b89559bdd6942222729d51a23bd81abab3dbb232a41275f823e20f543e201352af7f239541d29d4d28dc0317a149c28560b49d4c68a87eaf7e052b46aaf9dacfe7fab6f92e1ab5e995f6d100836122e222977b02aea7173a55046e84e695cd83bffaf7ec19e87971275f7c8fede8adf4aec48d5d79f2b0f0634954d4a17faa4bda5e6c3805fef337fe351be11c12ad4abb5555be88ce830cf7e4c4e23dda583f1e229893f409f094b7377407d07aa34bbf664e12fb00b4ca026bc400b98d01853d3c09daffa95a72a63fd08cf55049be31acfc8b7cce4822c9640e1a85533ad5774cb6e90708c8d5a89b277f7cb3c26454a9f1e5220d5e948a4f58fc4f6affc6e17d29fdfde231eb67c269ababd2afba642b2239c4367759cef5f9d9ed494d22abd2005b4a7d338efdfe66bc7e4ecd17c5d79f02808014342da8dc9c81c81a9fc6b696166e312a8815f3ca37aba07b074047a4d1627bce7198e7470adbe50d0f8258f9a1ccf254f16451755cbfaec6bd442db25bc6c1674bfaf66f29c481b1ea379f0d9f1f2de5be16685bc58d51fb8ff70d102be6f72786cc9f1e0c1d2e08bc18fdea737709d8fc5a3b2686a04ffadc09a2fd5aad50f4a343a1778b7f94894443e22992c8b545d6ab6c587b182c614fc0ff57fc6f26ff042457fbdfeab7019e88848a61ba216bc22240f206423043f96141ab173dbe0d490165edcad1eee29d120c46a94319f737b1f6fb100f09a8c0e9cd17383f22653fd3e75b932ec97c07bee92c0172f6ef313911a4e2deda7a76af885824fe33f0493638fe8170b72c238f1156cad6407ca98607511762ecaf2cc10bc90a3926abba5f8142203b6e1368df4c1c90a1cb2ff554a2021a5d1f988ad4f9d1bec6113959c28fc0bc7ed3d1bf5d1ff640215f6489ef888453965a75a672708c1dba7a23601a91fcce4000b5f15a55ad582ba863942fa163935e9013a8ccd43531eff0d95509c316ab9485c389be7cb5c64db1b32d5f502b8b28dc44e7bada5f2c18436a8e6a0929f0324ebcbb7864847a62c3ef339c9b735d24fc8a5fe3aa5992dda96a2c689e4d0c1531113147b2c5c322c45d562706339fb615338d83ce6a3b6dd714bf363149567b1402786c419099ee66b8a184355eedd2eaf7081ad02fdc1f3b7dc9a0d88ef0e70eed43489b6662a8c78946d5db5bec488b0c3ad6d2523671fb1e95a42fa170bcf303cb5e125cc74a3692f233cf5d5f9f64c2743426952a70febeb4a7ecdf1cf43d2dddf9ce9f4833751fae09cccc5ecd20220d80599f25cad00958b4ec80d0de630de72f8c3470feb22f4c96953b19829b6fbd7fb1da4bec2dad739b11994d98dc86110e225dacd239013630ab72e360c3a59b9f87345c6b7c031e3fe7c6635f2ca7029edd7dd8baa878fe3a4dfe707e2dd8c809a014156b7736b37a92eccdc7a4563e02537d166c4c8b074f6c4927688f030ba42c06999d408ec4fd28c3e2842075514594be95b1ed504602cce2b79ce1074baf04a5673dd2d62d0d9c8850fee1c41f90c1766057b4a3e420205b9ca09bde45e78f5b90e3920f7c577287ef6d35dda10715af4ad4e3e6557c47fbc16ef5e07e8dba65ba297b1255686b401d18f9cd5998ba0a53b8806e09699ff4a41fa9ac08522f9b9696b91a21b90fd7bb629b7281a5a94905ae6fbf742a94e5562b675cfe78ab7fbec27c3af0ed073959b3b018ab22947cf27633a5490134ee5aad31f3a62eeb22fa88cb5695a88da7def00ab1e0953526c34a0ea1278f3e8d0090785e807269765f624365ba8ebec1203caf2d4a0790e", "63", 0, -493055947, 0, "acb6bba3534db83bf76b3ea2d81866cb82ec21915bbea42e233ea7a517f46125"], + ["030000807082c40303e85610bc2a9bf1c88bee81ca36e6128aca67206f025c13d191415e2c31d885de0200000005ac53515352ffffffff34da49ca63110934372180e70ed9906820edee5ababba12cd26bffb1185868190000000002ac63ffffffff050d77807608fc0a30b10aab5140aaaad67adcc5a20857c7cfa01d75d4adda3102000000035265acffffffff01a3db3601000000000008979e0e0a88cd8100", "ac5265", 1, -921674116, 1537743641, "08798e464b33379f2c3d2719984f538eea0164744e35a88528e6c77c412af8a7"], + ["030000807082c40303c4a001b5d77a06d75b25097d6b5ba5c60dd5628924438b553fa99725063134890300000001520d5a0127a79e25e1a8691f593dd242891076926c355e1d814660aeb3f5b1fa97f914f5a0000000000652636a516a51456fab3a4bd7c71d647d2e5c07bcfaee094159dc9e4a375dff653819ec4e258c7e4f20c001000000096565656552636553acd35d76b50430eba6020000000005acac6563ac5da212000000000003630052a911d804000000000351516539833d05000000000951526aac536a51515300000000bb815d83020000000000000000997ed8000000000078f5555e65b358105994c2fa568e8b9e8ec6c27e8e140d27803b8d02277bf00c41f35dd95317b666c66bb9ed11b3da3c1d91c9d18df4d25672bd63bc114bea9e34046f4501131b90eae6d8414572722dc4e0f134e1b6b63bd66f353be746234f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000079ddf9ad9c628e19afc5ea49c613ce38ff816697579c3e29e5d3eb8e471599dc473e9f82d84e48e77902a24a63e2be24ede89354c8db4b02c169fd2acafa11cd79eeb9c63da91e485993dd2874fe024b23f98dc3aa7a0814852fb57c4a419dd720189b32fbe1a3713a0b07ec873e2ea5373c4bfbad177b656f2ac07e39b758c0302c66c41f72ebfe1309e25b844ca0e35f666735ebd5e19015a2fb44f930f54600215da3ab2c0553260cd087ddbcd00fec1fc9e8476052c9ecf88e65f25d65a47bd0a044dd2fa04f87d5ecdc0bb2e842a1d26f83107fea199f761d210a636e17d1b50992518d2250e35d4c5549346d5b319dab83196c72e130ede8d4efafdf97d9f2e03228c3ea8ea521134e484157aa667f6e0a808559db77b7a3c617f270facd357210224a2a47e3c9d29af8e083c7e542e3a604407faf28fcfbfe6a09def8e4b7cf0c3021c179e6f92eab28de32caa091334e2ff924903c937025dd83a70e72b9ff49909022242d2d46c6f1e246e705fcf625f03e9899071eb8aecd6396c635270903303740300909cdb1465abfca959b3f0356343ecb7a210502c9dfafb433d86c77644957e6276a27a640ec9ad5653efc50f6e02cfc8680631210cdd6d85875003c6ea76740d320172011f8075f8091c9233323cac4440281a6f13da8c6ce63981ed52044088158a9a2ef6534547679366dd0b4591845fd2a6e884ba9c801f35ec1dc2a59c2bccfecf5996d66556d7fa30868f94d93e2d10bc6a6235363f5a5722b28d2f2f7574312eec3d24c51ce44f234dce5a51787c2fb5358bd57e63d236448ed27b796617a556c9fc14376c88935b2f113dd1969fa25cbd5704f28b75b12657f2b714998c1f068c067bdd483ab46321bba0a14cc6d89a241e234b7e78e8b418b022b38f489531008f18ad8ce3de07516c5c96bfc2fe9c132e94efc65f0e5e3a83088326975dd250a68ebc5db9f4ce6be6af3957379976efa9edf4ce98968ce35360be766a03d30ccac6511b9d76d2ed4fb7d1749facba087e1e795597f9ad5f783dde0b037eb988f7f91cf4572968a3a010ce1890296603db9293e56cae1a2ad98fbd6b5e72bde2b2fbd922d6570aa075c07922983d2e303802706a36752dbacce6766426b99777526b862f083a50eef485ca05de59c2d857694535cef648b2d2e1abbde180f5259b00f74067c1f8486be3507fd79335c7a9da9fd816a48236c28781596815052aa005048c51222eeadba0af9a4fdbb8f50380dfca6eb9174ace8453b363b3603b57fb70f4c96dd47a6e7ff68ed327b22de7fc99d17bda033252de31d5bdbb25e5533de5b7b67c51bcc78bb33a6174e9ea30985fc841d8d9639bb8f22f109fa4e357433f8f6cf9469345a187e29a232a41d3264145742fad96ec4da5a7fb012997a72a667a449723d48793926b598bcb94e158adc6ebf2b19c80dd77ad1af64e43faca2ca2d074c888fc07af4c2d47deea0b27b0d76c5b5223329a42010cd51f5a7c362ca23b28a478a48427abc7885971b1e1e7ad0e31705d065f726aef123c1a14607924206b9d2c53c03bccafe454fe1610f017508fa361e0ba190d7d82de9de7a6fc581033dfe7f20d908e74fd1e615b23da955211bddac2a2df8c90817641fde0cee9828579a98b3825da500e265fe3f88afb4e893c058971e2b46eb0963abff725a52437aa1c411aecc9e653fcd597688b13e682fbc88cc5827d41f331aaca9a7e2d8519026c3e46941984ccddd051634b81c8c1e1a3a40a18705df97ef535e49fcb385357c74a829bb2d3c3126efee41313bdf360189b9a9a55045e98b7cdc6f16ae7816909ec657d63b8d4b233b53eee66320db6417bfd7ac690fbdc8ed4bfcec3176e334467ab516c07cbe63c3eadf4be35289bcedd87289a367493fee0235898e7165151974bb93ba1daeae965753f76b174e81f34a5e89b471c4c853eb1ced51f8550b97221edd17959df15c6b9a7eedaede532d4ca1de2a310e913a74d7f8438f729bc8a6f05cc71f05b433b9b2034c61462f83971e707c5ae5e87c46db38ecadb8318527b41d44aa6b383125ecc31f1c9ec8ba6a2ae4bdd77e952d04b7f5ab577d6717cf1eaa9df56ae286a6a121e313cf66530d5e9c25dcae6065b155f6670da4679f753626378b30450037fc827ba2ccbe50d5c08d333077a0a3b02f76a0346a2439b64a0b219ca24ee20c8c2817485828661a4f1c047372e634bb887d90c38946e1bf25d208c0d5d5c0bd2c6860751c31eb04d62dc86fb86bb6b364a14ad911291ceca1b1221ef9b9d010000000000000000000000005172501f2e8fcb5b442dd70be8350055d75e68dd81f20a6ecbc6e09ed6fea939c6c6c265f2f9c9f4d354f7184c6fef5cdce3b1e6a992d09dc5cc812f75c459ff2785a986013a7edbd793c920d6b813e5303677b4d8c4bdd70c09fe53c04d6cd10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000059cc242744641266936cabaaff7144796c390e053d94a6218cb94068482c5f67e44427b011a2d71717298dbb0c06cf224af505b87a129f2d056260315a20e71e6dec94bc27f02ab5553cf0410edd527d8ebf7cadeac243fb4a3082eca332a81858a643726472d04178e4f08347a5b4c74c56e2b377bf390f62b1690cb35566d6021bedb274a50f50c5f5fd259121fac2d5311eb47ae28692d31de233f76535c98b031bd31db02a216a9eb40548f82926db1340181259bd2a23866aed632d3439de850b08e1fb81eefbf267dd7cbaa2da90cba6c3855a204b429c08305b374ab9cda36b64be3ddd63fd2650f30cb97dd93856cbe66bf7feb0e80062d359d1453a8a7e83020b06d12a233d0d19750a86d8e7a5a1715f0d0bab9089edd8e3ca6c2f91f6e71403144f1c457d2ddf7ea2a8961bcc5418084611eaa4e4942eb4cbdde902c573ec6c031c92663709fa1d95cabd36563b87a835cfb2040c87321e87347bc39ed9d3acf9031ad0e44e2d30f8c0731c5c6b31b191a6e9bb2375018b82f1686b997f67809dde0201a67d1371e718391c68dcf2bb525add64dee0ebe653cfc7695718ca18b25f47430ea43816f4633030c3385e6de37e6753eee05a2f5280f82e754725792ea37ea9644264309ca77ec2748a47438d673159157225c3edd11eb8709be5f6cc2e9354bece5825037068781e7c0acf076f672ffa61eba650bfbf10d7b63c55d4c22d40256933d902b5df9fd733390f2c94e8e837b098a481712dd2e7c7bb100275fbcd2cb76279746c10eb849ccd6ba2744f1600e7384c2a1fac919027fb8befb222fb4ed0a34201a8b3f917cd45e3261a3913730c36771f28edb5f42220f72292648d804ec6883462ee1ff7cd1300fc456500d6e385f742220d80ba34d558702613381e4d070e7e9114c3dc5d946e5f3b862ca193b8ac672ec28e9fb13594538efecf131e2503a7ec19361cb60508ea93ff9c704589b7837f52abb2a52ee520b17e244226a39eb96f44dd194fb40785ead59fac01aa736b6a97b1374298ff7b0b0be9299e7275277525607647e0de4ae7f33e8e15e6e468e8f3aa1d413b110fde05dd40cb00aec8d72a72c15b7ad15584e4f263934eba816f127a9fec2016d2789d0e838295c510ee6be1fbe7c150d972c963812af9b5cf9cf9534f3b6fd802c9c8361537afd47b6aac6c4878b307b6806fb18fbacdc1df4e0ea75d4f6ee7484779e7ebaa89e09810af2c67142beecc5dfbc8dd3900f1388fcd19425ea6cfe4462edca63ecaa509937fa744a17b04033e1956a2602ac1db64f77dae96301b53659067cb1cc2eac7edf60d01ce49faa211ce3a9974ead2693701f6a85685afee73789c258fbf957ff622d6d12a15e34ea87fea3d6a87e5ed849ce8d67eabafcd8eee882da82b9bcc59b74c81290ca73777ef895115bdb27be33b576ac6c89e28fd1a3e233c02eb356a03a85985ad0615a974c27629fb07cffc03ce6af1f6f0fc60d9b87d6bb86c431b953c62d672fbdf67ac422bc333f27c5b1041540f7de31ba2817f864e8d85e34e1763cd68fdb38913da40f421c28189d165ba5c35c231221e49c78d08ac0400133b40e4855350a2cb0b35cfce3eb8e2f4892feb8b0c75ee349233a788d6360f888046b314937e8c76e2aff5ecd59082bb7d303eb46d592a60e8c3ef925c7c66e0b73bb2722b09c272107ebaf2f674428012bf6ab5ad8da64e333486264b55c9510b4c242f2e99a681313bd7a156d0a8dd5a8e69cefd0f314a5143095f5a3d30001433782b2672ef08cf4bd92ea5b0a9558aba7d9517164e3027f8cda3767785d1e1e1e79137471bb6745e0cdbe639513725f2d1435f18c3bf41e795392c4983951ce2a2a91911f9139050a80c46aace42ff0493c18dd8852b246e158ed363bbbf7f09a39a4743fbcbe508eb07aa08f35c12f4a6f77a0955a37431d95d1f4c82d19de2b908be6fc71874a7b7a68eb13253e9a3958bd6135179701e0574d84b5288fff1ae0fe80e1949347c6216ee7237bfea550bdda175decfda5d661a8e7a3bd9d58163eb58d607fcfba15654cb39d7cf977c4ce4c11da800d1cef7d134f370d356b30d8ea6224d6782a09c7d59da38f9299a9a47766a32cb349cf1225a210f6171e795c460a0a184ce0c4d95015c075eecf8434a2505da03e876e36ffe7e3faff14f6d2066913a6c3f6bc0453b02fbbbd07390c730813c354f31fa4c83031cbf881c93a2763457c3e05133ec6758844d60e65e80486f13f4af479b6076d11e75b82ad47a1652b3dba710d9e2f09fb50ada321e2a1b6ab1ff8e635b3eea18f32609f50dc423ae57d6e0662aebeb9a1f5d632547dada662cf98f325cf3915895d17daf7e14162be0fc9e6d3264cf7808ce2939145a4cc88a5034169f210ed0f20e5f98c613f821d5a6f25d03", "", 1, 32145557, 0, "8f279afa6d629e2da48de1fe57e2e51abe4289b0aded28a8bd7f73b03be32a6a"], + ["286b1753010a626bad2586ae6911d48968c99e7f8731ae31c9d5dc3106710bb457e3bfc1b80200000008ac6500ac6a535265535dabc60424466e050000000003ac65539f7ee70300000000005a11520200000000055265656a51efbeb8050000000000fda76a610100000000000000008f07b7000000000066fad4b4ec97ae57721243e8d9dfc801252f2d8bfdeb9a30be7476a3db519a3c7861c3d90722b278dcbfa2a8cfcbe08e2066ed4c040c678d726165dbc4895adb090f6aae5b79bd5f2290661a3c79779a638023ccbf9b63716036a7f0ac7a3927000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002bbfa03bc4e49bddc6125310ab090065eaa501775acfcbdbe189689762d14ba6f0e21b3c95e27e37b59e52076b0860d462d748ab2db1dc057e83af4ab92edeed1db1857b7443927d573b1d063145389604fd63661a1d6acfa637bdeb8759ebb66a1a23f8c354f088f5c47f9723ed2c9afa50841dd298f8a81a8b200998ced61902201a311fa1e991979f7c59ac15cccb054e75232c40f2c24fb7261521c2a6f8d7030dc72c13e9abda77bb828f132fca3fe194687c15e047346e0b2167f656f531e40a023cabfbbbdbb7db9c9c5200ebc47b812a3c878b590a936aae643dafc7f8b1d41b8cb62385e3167d98cd86a02d1d59e802fcc63892cf37cf7f073a6cf493446103220b0b2958d9608e8686a974a060fb01f3fed73d45efe87c30d2e4cbeb06c913031094f40989ace48503f23b2b15dabe0dc5bf25c25eea5081673308c1089b5b5a032f9e8912226bf185dfe93cb149e3c708102c144f9af085332bbae610e52d0c53030f31418d33404eb255744fd8c3426b297e4da2fdd0f23ae09a361bbc0bcec98c022cf1d3c4970ca9798f29535d21df843e37c836298027cee41e5b32808d04c36825330c81976b14e860fe801fbff11a785ae7f8d0bc312bedfed3f4177e3604a48262ee9ab550929e53881d12353468b31b67480618a2df0ecaa0add197a1f6c720825f5a336440ba4157fbd9eef9f3aafe0bb9d9326fdd7070c8437629ef626e3cc96dcce664f67bbf6b391f0a1c912e7175c0c4247ef159330df7b0e583614d373e7075e23c211b496a8c4f145148b06affca7cacca320d032633d5c69abcef40f1774085a6b033b1d055f8bcdafc0fc76d50bffa8a590f449578fb99a6d40339fa5bcf6aadb93868d8e9ae03f5b909a0a59c09795c4f46724d8fc96571fe7a0fa24b9d89272f396366a8a719f1d8b5c8a7b8f47c5e7739c543265f7ad99d91eda0bba3f4416c0a35e702e6603e653d671550e1443ca072e1210b0ecb52619761b0af6215294d6f76d2c8d6cd8a26480e02339edde2351e86818fb8989b5f7e73034e1080fd4605393ed9f6edc6309dfee589fa769412be2b03af6ad9451f40ebd920836e75ff60e360888557b61448ac538dbd477dd3f1dfda1b95a84673d93b6b4147533c94c3ae3a8a934d80428b54adef5d1e09f99ec54191584727cd50e2a1a2c66df0c652e0904f569db8d26c8a7db1f3bfe3f7d572e29b8fe24d97c2f82da3fb5677264c38e96d417568805c599c909bb85f580198b35391dbe7e11de116db143a1c1333b98f8c0ddd1a220006c3c55866bc6257255430fdcb96be2b8c62fee215cd265d21eef118f436cb1eb71c1e39939064f26b934cc019d6bfe2e1eb75907c01424642b911bf066dae38c8d6b58f50a0b99b61c4f1b1c6592f74115217b2f8d708095353b749b6f4a0425fcb12ec8831ceda7dba6318dd5d3f62097de27b35787e6656f91f62ccc5334f9124ba3eea86a95468cc12bae351fa3cd152e46c83d27c2f422e39c38eabed8b001e10db22b93904350be65bd8c551210dd356bd75d066d55929d3651697da2309e32fa379d829b070dd17fc173b22d63ddf6ecb7d24ff532f9a90b2eaa8becb26c59316f3912ad00db16ed822b78916f731cb07af8ac35463b198199bba555c6223b03d80358a57e462bcada4a128c169736e041ae0ccc41e02d2d5ebd3eb5c37d08757e88c17ee84f9183fed050e369b5101b79a26ed01113b817d49ae26dc4540f9379675740a2940696e3de1020988d2032e82eb8f94bdfee0c5446094a32503596533e2d613b3651a628eddf87877a73010b98003333623ea167fda4970ed2e29225e1693cade411194a8379eb48177524509fe4eee33aeea5d2ccce88c2c8d0b15acc3275d63d181d3246efcfc9fe850caecb0a487cc1b339c518980eccc0d607999cce1469163c6ba43255563b092e7f08f08aa782f22f38bc837db8cc32c685b2f334b530fba8ad848a221ad830c824b31cd43e3bbeace1e1f94a6ed776a9583dbdd33a4ef5fbc88f10a4d7febd5c5461cb713bc87e9e98b33bc7cfaafa84af9aee46d736e82fb68b3798ace5bb3437502708bc896e2c82e3d5dd35848b526656cf503e30e205d403e158934e43b37403edfab8519f2d4fa7d3414f6cd129f7703c63f245f7b8c59378e4a110b8c69ff9c938e1f26487f58e3c4d587bc2e4b97145e81b57e9b7c8ac920b367adb2fc1e618fb3c2bbe2d3303e6c945ac32c41fdd38b055bbdc78acb9021d24a78d1763368dcd38e23c9fed14a6dec02746d96eb4870cc68c9af4763a47e04259031b0709e0c388cfc8ad7156f81bf04cfe22463888ecb57e79f2e017bb38391c61cec20cc1e83966eea2a75a43c220cc17c51d318ebb7c81563c14c3998ac09d1cb69161659caa8347386fdc7748579d20b", "ac53acac", 0, -1740182601, 0, "73f78151539f09073533f53d2c202904682ae9e8fe2346485214cd05f2184eff"], + ["030000807082c40302a2226d8245970f809c251ece7ba560c59f4ad657a81f63e545fb4a5cf226d7360300000009656a5300635365ac636b55d0d19a4978e63255c7d76edbd661ae72f658986db4965033d97e964527a9af0d5ab3000000000351536affffffff016f9c84010000000005516563636500000000000000000100000000000000006d39780400000000f765e39bdff8bc627a831449fa8105360864ade9b31d879c8ef2e7b93d2b05e0e77b2ef8668c8553708a072352e633d33f9176e717b517f92583f945ca08132f18d728b7d6378b80678330c2690a590e8fd3f69402a6da901153d1412c9de620000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b3f916fcca540a506407f069cf12340a3a0c1381de6557bd29d18b9f30a466f7e7ab17efc8f1abf3e1d59633a54fc7b64424725395d588e1dbf57e86bc9513cef88524e8f14e00b738dfa56213db2bb4262edc10727dd3b1ac933d60b0ffa15ac9ced8a71d8183855f94b2ba3ed15718f5b61ba31f48301e00f37136fd1cc990317c20da970fb2d1942bf4c160e76c4d944f771bfed098ebfb3823dd34520741a0314008bbbbb7a6ab3d5f8c8bcb1138b77beac8457d5f6b697f16eac16b3a8198e0a08198656b457f4d685450ecd3409b70c21c128a4edcab3c856b296e121ef34f9b46b77b37154b723c1d412e70f54da3ea6cc0726420cec0c6a6656722bd71e4b0220fb3af2ec592cf5cb086a7669cee307d5f2f4c5fa7de47cc88029407b8ff468021c66fa99e354f13317aa9e5ffcea88ad4c40bbb8aefde882662ca96810c0200b022243221e099bc2fad651b207d47f9ded3085798fd4cc44b6fafcb7022e8a9ed40310562ff9db40791b9a2fb386b2e799c3a2ffc02d37da7f581685089bcd95491d022867e1639bb8fa5c197ff799ef4b93ad9d839b317772f5cf9c21d92e71026add3e2a741ccc29d56f952a371011e69464c9f1a00c5e5350c0b92deffc940bf59926306163cda99b48cb3ab7828bf09448fea1bd44770b15746690c0e01465cb049860e9b4238db23ae157544dc8dbf9c2dd0c77a4257cd94e719170ea2bbf3c335a5135275cd119c805e9d84457cf800a8f1d9bcad2bd16e95fea40c44d4ab2cb037f803f56e54b2a475aead6750dbbdc3ec16b009fde5c863447853b5968ec00d4fc6299dd37f713531cbde2ea801698d6e202f51f138564964089b0626008ac74651ae0f9fe010b9a19a8d29028c12c917c679233969ac583dafd2ab93d65fa9df030d6317541e2d89c9ce76f6a4bf6bb3bec49a8835d381728d09b86ad6c9487bf391577a981b87418209c8e85b4cfdfe44ce02719cd01e4257b82863437d13d8faa0e1e54f17dcaed28d4c353b09206d74f24c8865493d7f9b0201bb71b1102d4d51980aba2b7d352e358036f0b7a7a9a6f4c0aabe492dc12b230278cab584ad75b525638d55315b58def6874fbe3164ac80cd6e7efbf2e3d95541f1c376d45b65a38647e2861c4d6135dc63a25c8c8df1c4b53f38d556b08b9186edd06a72e29d80b7a3c8963d94e05b81afb159a7ad596c90b9862c3db04721d2d8ca7dbcf2b52fdd05100720d2b24e31704a6a1702fda074b54518bcde795e5522cec2b262569cb6564bceaee5af76bde7479bde70ffe42c419b89ceb4a54a78f600f3ba05a2c21028bba793aa021cd3c7187b5607303dece8dfa15f13e8abffeaee33267abffc25691c1d1e9bbd583b5c6400520917a70469b828f2e41c9f3926c03800e9be608e9c88ee121bc94b92551c365a4ffac09ee058684700887b72f3f9ecab8ddc216bf3d6c589aa941e8d40bfa48a0e2456f85b515abfbf7096c5b787bfab1be0451dbcaa969109a8cb34de6e09200642871cd32eb4ff8b79712bcbef4e6de1ab9f2ae67e8eb466ff1799493be5ce4f14db675b843f67474ab199dc25643ea2ce83ccf46f1aaa03865bbdbbf7e1b0707bd08e42a67e2226126ed92b7ed6f2e4d3082613a1d54c85018bb3b6e47b93ff3c094e18fd7998e0f6aea8c178efd0524c7a6f8a4bcfd7180144d969a2330c848e032d22b44dd35e91cd13190948da6ed7808ec9236f0a43e4758c1d8e42e2e2235816169982cbf7616a8c3ee87f3806a46bfed18e3eccb47729bb7cc31a68428efde611eb7a1ecfac4aea068936946377a170651c38e80902e7565452936423bf97235bd10289869b0d02658f2b9a06a5e98fc07978024051185baf30f62b1189d264e4d4734f3c7e44cf2a776f786659e9a3cac2fad925c9a887adf092c1cdd92b0654947ab275522c9a83212576d2c16bca1d26d6088ab0fbc826e1988c10d465dcf6e2f0cfa84fcb25b6db623683653acb07e764ebc1c69bae2f3ccfbbd9da33b979e00204d7c133e0772039c41e00eafe089a4e6fcdc188fcb588e27ce68ee6ade9ca4d6931162984c9678ede8863fdcb8e7e8dbe2e613475c6cb68037350b8029d9afcd3a04835e60ec52d7364397e300828d82f0ff0f3c8e5cf058071eb0a1c4fac1b8c8d32869a6fd332217c3c22f4ae2783106df0fec8241ad82f606ab64f4d8b3c26cccf58b8784beef1180e3e616a8550a6bb9bb81e35b038c61e712e44eb2b173f9c6762b57ea300ab074089ff72a21ddf55543ca185b3d6c6484e397a4634d8aca50e4a6b9313a262df15c045f2de3ff4c8a9d1a074796ad8f384924484ed48a9f95a6e8a1a3aaa4a96384003ee5ddffe7c79fa2856f4c445b85a9c9178029b41ed465deff674099fd44bd81248293af90241172132fe00abb0b", "", 0, -753011132, 1537743641, "d82d4612b1f4c212d2990c386bb53d8541a243812b2da9c23c50b715000d0c83"], + ["24f81e77039e404f1244042411210daebcc2b17657b9856a8793e33764e444c1227ec967ab020000000165ffffffff6696982821f4e0b13950eded40ef5f2a4621f59f9287e7a50c07bf71150a3da4010000000700acac51535263c9d282b520c29525831e79a12d8442e44615707eaf873171cbd200d9b887ba99d71b5fe7010000000865ac6a0052655263ffffffff034a6b6802000000000765ac5363635251a5440b0200000000086363006a53635151c6cadc010000000006ac0052ac53530000000000", "6a636a", 1, -2140714474, 1537743641, "28ba3c5c4d67474cbd78299ee4d8cd7567b61e0a0d07878fbd5ec4afe8c02bd6"], + ["030000807082c4030425b49943153fe14df58ec5976974a6642a854c9735bfc56faf9f67e5cedbc22e0300000002655393bddcbd84a5e59304ef53fa2e626c6c963dbf68abed11c1360de1d8aa83162eca878c8e02000000002c962be970a9fcc32c0b66c3d54be84ca4a30bfc03da4bc4955d23e350f3ab80fbd210240000000006005151ac6300ffffffffedd380d5db3b07d9af3e4af398defcb6dc5a695b0bf3152b211c60ed018672cc03000000086aac6a63635263acffffffff03b78da403000000000665536a00ac0006823d030000000000ede023000000000003655363286c428a000000000200000000000000001289490500000000c85b8a411e40c579710b53e4d0bb76451b60c29611a28516c113d4393f2afa1fd1e901cc6aab4fd9a4ab852b41a66a14b070f546d678493f0f8a82084d802923217bcd4550c5d5005f053db1d47a2922799e67bb57994d95799278bee30c377000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eaffb264a5498621dd8bb9e71cec09d428502086f275fdb7f271cf14a387ec627d4f54f583fd5ba829a9c83c45e1303b07d5cb7acaed61ea2feb4b793ec336a24d650ca966c125bfc0eaefd93ea54ff2af2bfc89be063c9397aac04179a847180f959c62e44f6858e821a9a8572f0fe8e1d10be1427946a482cde59cba2224f70315e273eaae94e6aea1b55d68d061b8762324507b5800e47df77d3f7f32066506021e61ace0976be94ff1541e8bf2cf1c38c91196edd7e31eb14839efa0ff0e6aca0a00d40fcec004cfaa42bdb5179cbcbad667c47339bdd59bab6331cc9f052636a3432406147bc1ee15e2d49dfcaca9ccbd2da2659091decf3642df4a0e090d895e0217cef162f3a86edb128bc0a6e7a8ddf96ba0d0fc84fa4b44dfce2f188d71b3da032a16f1a4307fe6b8ac594df1c959cbde4da4e4d10c5910d37f258d1ea5f9cef6031a1a34a9dde030a41dd58b541508cf397d425205983d64a72e1f1bf10802049d03109d5e314ccd215439fa5b48c3b6d909dd8f934e5ad331b59896f93541b06e65021de71e0fbc4c144a740d3f72663ffae11afe4b4eac8aa7673376517a4b48931b4d9510a72e1c4b23dc556a32244bee57cc1333d72e012aab0667fa6ffa95c0258612be41c20bf84205b95b737169db472097b1c3b497edac0430c907d1303395e906266ef7d85a772b0a35492e87664070e5b6fb247dfe4d0ed73413c4aaaaa5caa9ac8d8f952a57161284e937f420756401ff1cab3bc5d3cd8386bc84554ccf1063f87a1044ab499b4d181a8745e0b910d8b56402df99a90cdf1e51def77febaf4ad387db40c945d5bacfd4204c8d04a9ebee58177c969279b4801c361fcab1f5a71363356605ad1c93a8f2e9cab9c804d26ce008c82b040ad5c770d6d9dea21fea38b6871994c72a99e9a05bf34e83d74e39357a5b5ff0bd9d9b2e0813c506a0d0907a82eefcdc040e9ec802833c267b1b578d133e558b9cdcbad1ccec0140c01c535fb4b3bb4348bedcf35c4bc63b0f05eaf865bf990681f9902dd03865f9553ea15a7f39481e95446fd5318e793d7fbcfed48da7897a47c6690ced95dd01c6f3e3e7042c726c96f7accc135dc1ea615efff4a43a963a88833dd7176be4e6d0d36cffe8eb7e4cbb937c455ca5199558b2a0522202aaa63b8edf9de958f61cd4fcdae9e7057da759785459f9c2465af8eafa8a5d6702486e6248edbca2fd1c8c8d1d07acd098c3d0efa0dcc48024b4e6dbad33209efda8a2a7b63ee5216d55c8019cc69fe9485c619eb4e093e8f3f0aaaf3ad35559cdad454d9ef80ad6bf758bb9f88df16bee977f99d955c2cf72cb65d70f10f1cc5aed4997e2a52634bf3d68a02e1a455b6fd59ad4fd84556c7d241ab31595e7ff282237487b9b45f1d0c350c9dbc6d7bd4bc797287db354ce6ad13b8b29e60b57b39249216966c5cd6b0190e4a477cbebb1a94cd36c803066e750b777bc69b3300ccbfeb7e081db5f9df3f6b09a2ddd11cfe9be5bc0f73fa27086c1ca6b6e7c06546e59a9a458de09b34e20017a51ed8e2281b2cc62c1cffd24ea467e38efb536d4cbe1e9bdcf54ee79df783637818fe9b2452cddcc7da540b49dfced5c94644c87562066e093e7e6f499c28f198985affdb49bbb36882efeded8d247d47fe63442ddd23d145cb5de3347e2434f5f650078e69b6ce030271daba38524b9996ee3639ff94b671dd9fdc40b0b8200508d43ece58913219a11893f9f867d34f275ec019ac2509de7e2efd5ff9c1c3d9c05efbcfdad3253f7feaaaa3caae57764764b32f535755e2554e3edf9c778939c87028b2beb2829208adf0152638e5e187490ca584f838075321fb3fc3cb983ff16828cbf1c21ed5bce89b1358a29b730701461b1cd815d5fc1f0bb1a7645ee3e9ecf88f338f21029319b9ddb380512d49ee0f258c97e1578f33cee2b4cb2010ef3943676fa0d43cc632f4f894a6b5040c95e5f2e2f909ad43edd2a6de3a26c994c890642cbd1bc719c5f952c22a2df369984a97d000c9419763f9e09bcb1bbce64c733196cba89bfc624832aa3de6a5ca8239896e81988f851824081fea030ff539d45e464c2c2618f1a0f7dd3dd8a696450e183666fe7640af407de1536441c6d7038d975e41f6c76f69c12faf50100352c1b3835f7915b58d042758ce0dcc05326716d727e112643070471121728fb212311dc3ad3a05017f3c01cdb4589c658c5f229380d0a4c3e296aac22d312991bcc4de0261093275b02d6ccdf7ee98feb33ea2e3d14661aa160a9a64f2c000000000000000068ae2c0400000000516823ebe2897fa457cb26ef2faf2192787348051516604d565e2d43d94d33f674e0928745fd6d054f7f16d019072cf09223df25e7a8dd00fb35285993218c47866d67bddc91868b8b93c40fc52e2a8d0429d4df4a4d53e6ff1b8f47cff36bbc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d4962bd503848bdf914baeecbda162bd0b96581649b7a9f221774cea31274d24f18ac90754807766cc03d3ec38e9f2d25c373f93fa77eb952d06f0fa85fc1b7805215a6cc1e10795af6d0b768112748cb1e0510944a454cb3575dc10311cc465beb081f5a4a42c9b00268e81ec5697a772046d634e6cb0fcc96885f634946bfd03163c74a6e069991460e759c83c153c2efbbc847d3dcd0d10b0f82929a478869d0222e98e71b68829cf1c2dc36c9a4265e9a9199b7ce423638cd4c3e59feb4724f90b01db06d58ee8ead65f69bc9a511a18d78ef31b527a306ab749bf246a7a5c1cad4ab8ed8204ea4daee8157bb9fb1a8a757fb15dd2d7a586fea4c477bf50ded990020c6626d226db3ec93d501b188ac8ac480b31816f74078a551d00d4da1b6f5f9f021f20afd95a34cd0f3f3290c6f60e7a91b9a5d213984532324e14def3c6e6a5cd022e69b08b8d9147302977f131eacbf2fb15377b7af9c7a51527193eaae46e073b0203f42c2a0b375a81e9174d2ab4a51e68e956b42e1983327fdbe384c915a6e203021c21186797a3b9fcf25ed8922b969071e8ebf6d27bc617c4cd97f280627d4fb6c8c87511ba40a42063ec927ff8897a5b988c650af46e1e0cdf1f5d12eb957660a6af6a3767d2b67113d680de577802fe0bdea2cf28bdd168892017e0ee2ac46a7df47d4d233663b73c0d81d55099f9c61c2cd94d3cec2a33ab2fba67f8534c7da090e380f00bac8fd7fe98c9db4ac774f9ab58d2e94afc202968561836c12bf609a5c321b2e9b33ed6f815f3025f88d0dc491ce3f286b65ca7e635adb49cfdeaf3791c529db247ecb2d46e6d370e6134a66fc00d9d8ac0f301f4ff3c92fabe6c15dfab67e906f581ae5aab93393a2add9677fda74324d7b441deb9121cee691fbd2489980dbb4fd9813909b7e2bcfa7505b2325917d9d28dbba39bfdae3afe508625abe86ff295613258f59b6fb39d9cca752788a490be636a05b88fbe4ecb710397545f0c38ecc765d8e31d1407fda0d7401708e5f161622fc1acc5e0c10b07623ce57995b15a91db30751fe1b1520155aae733f38df2f44a776d916cb6f4f1cff5f7638e81abdcccabd72347636cc217f6d4a3cb21eab035691b0086b3b8daaddb3965023c749b5313db1f62e3ce62c71ef7d33d78fdadfe3633d33e942632054e42c0a8daadfbad648266756f3a1192a83fb4bbd53bbfb2276edb4f861cae34f0f0c607d57c98bad954a49c4c05e39df92fe5e46d6b0fc6442bf84403095eaa28be33926c26f404adecc7d0e5f545be09dd9302e8fd685ee7e45d2a42bf246bdbd7deea63539016059bd4ee0824aea6d702a39dd2826f4048bfeda93e7cc31c00e79158a33192653609dbe9728b45aa373735f0856c35f96c6923dcf5aecb2b3824c936b061b2ec1560c45e41c8ff50b4395a3766365070f49db7e523cfa7459b835a381a48af165065a0b3b4b2468e071a484f6dec6c776ee9274544415691fd06b8b9988367b7ef181477e9c8d10b7b416b1efb692a4a1166806632f7763559712925b9b5239b6004135a6f3be41f391e6be0caddfc19d075452b38ee8610357880dafaee0fe9a3d539e89cbcfd8c69ee0913a6458811d51aa7aa06bcca06ba8f9b74d93c154e4a03e615c21c75e716cdd52e08ac0eef87dab181f70cdb020bdc6ce163cbc8774457bd4bb9c64b12b1422fab66c69da05859bda1a835f3244c8be4d3e335a7871a6fd3a6f837d9a734e1c7defa134f57e4504a88af2ebfd3b798949694c294ffed3da94e5311b91f7c1dcfc35d50e3e24424d8dfaf165f3d09f794f3818570532fc07955f879c0a5e41f4ccfad9673d06f31169b45037f514063043ef6894661007f7a46fb0e3bc0c0bbb4b6ccaef8a18fcc9d1f1ad21dc78216cd04f9aa96385167de0b1b39f24d88d4686c0e3aa55a578b4c5f3ef875134a3198def2c6c12ec8e0e2ce275eda5dbfc9161537a03ddc677dcec9d5badcaff5f0d73164898f774f91680fc94a5c2dbd0e085fa78e848e9cb35a780694e1efad92c08abab5198084d6470fe48f4fb6709d82e83bd66804bdd6d0b61c3fcb1aafead10b561917ee2f70089658efd09ad37fb4e2d479fa1fad80af35d4df0c2f84dd09aa5e79469f6e55cf52434a403ad66dea774e5e33adc240bd9e8c93a48f104b25dd3bdde65d044f9006a339cb59e7ed90ecf4e466a209f20b40c94e1e9f6be8d06fa94072a2a83c8984ab8e4d18968f8bec42d796fe41c65457ff3fbd0fb0682df670e1c8404cd41cd665b69de9f6da53b9d92449988a7171994cbf0af54c2cf2cdd62b092d411d216f74cb8f64587b807ed998518933c77cbb1f2a1aee6521e12f986fd30b865715861491abaa8f3df5b93803bfee762db105e0878081a779bf5d2a8284e4a181e74edd6640a206", "5253636a6a0000", 2, 1609480731, 1537743641, "4367f3e65c4e2acc71dd0aa939de39e9cb47b216b108ff9f9d5f231dd8b687be"], + ["030000807082c403012172aeae2a5dca894e95ba7a5ac555f499f4685d86fa1f7cdc40b4fa6267d87c01000000016a5a0bdaa903a296c405000000000163a5d45a0300000000008ba886010000000000000000000000000003000000000000000065dccf0500000000e286d36daed85bc38f79875f33730e5694c6c1979abdfbfa5c9e20c40c0cc0ce4c86f865881109017342845636b75d2b2f7cb31bf631982ecfcd3663d804bfded0d0eea597d03b43d118d701b4dc1ee59943a8a3e337f30ba29ed1e6688a842b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004767ad9c78939337362d0ce517b92cf4a3a24f826d4da22c7f6ea9ddc175ac2811a27f6297a08f9e9ce2676024145f46029a37a7f977ec3763ae298d684a22a2d18ec32af6caef1b262fc7a8a68d6847abf9f83bb5e4dcc49644fa54bad60aca42b3c6e057123980ddb88d985419cb053cb2712f10b8fb495d8d9ff591a5048303003d6fb951dcf96adcb919216ac398f76afa5ff32c9f105c58112d76d9831d59021c6f02611d5c5b9d4f9be541f29db76000af5a6283676c4d30b2b4cee55dc25e0a020e04d73df62c6111e9bdb5b9a2719e642d2b3f61873905232d98df4bf27fa4a49ca3d38802ed545752ca8c7ddc692aa97e2682e5ed76e19d89b25aeb2524ba030be33582faa21521a98f9cfc03de2b8256e456547271ea3601dc3d9a1d33f9e9022bc03a4261f375684bcbccc4bc2e3b8b7e48938100db22d82c971216cfc7726002038c8ecb97f11c39629f08cbb48d2b9e3360f752a9373bc4235cfe48ff503a07021a42f74c75a1c3352900e3afa16a6f282e4a5651e003427b35afb5e95bdbac83020685e377b2021eaf3362c27e657c6976d802fa4588e5744f29848523cf06e968941730fa7e712f1e92f5e16404cac4878aadf25cee0fd20303944bb11cc91a6f310de5477ee5db8c351bd1dcd83bef4289c8e0b430ddf93ad9dab36e3452c1f3454d70694b3ec507cfeec098149675360acfcb60f5f49f52ba5781b6be3eea91fe876f7b7ddde1bdb2812a0c89c5bf45c193c7ec195575d0737cc19e041463d45d63feedea01ab15d21a90962003b8f0f2821cd7054ec543a8fcfa2f0a048c58faa3b3f225508f21dd1b3589ee417f0f3bdfc515f05034cefac80ef5b89a9429931c416ec07b50c998069fb3dfae382f495847c153acd17b998ce2d392563f5906068aebcbc7db55c1350d5623ddf623347ddb928302b7af7a2000aa486139fc8229647e0d9eb00b6cdccd2fd3d08e8708123df0af7c039782926d5988edfca25ecbfbcaf0c21f23228396695254da4c68fef78bc237657ad658126a081e59c77e9db3922dceed08c8caca986ea01c552f30e53f7bd5c32e56988d182c5b127c34cc73289318e90fba8e1852ded2515e8e18e60c7e4b38d639e92514723af493146722ac8f5a81c4a2bddb4e057ae97f7911b100b2e63f4893eb06a2a036f96777896f203dc61bb8e8bc74be103b2cc5be7a9d9e23c118fcc30c522d0c5dfc32267993e7ff2de48c5b5b7336f5b0b5c25f9b21e81b70ab5bf47a7397690d19e9ed561610f6e887ed470c46b7c044d04fce1273ad2b3255436cb9e1c37841f243c3752e568c3895cc2e58f204055b0c82bc6340c3beebe69a197626e1503b9df9cc639441f0d2b6d376f6b37ba485627923480371a44c0c226d2b620042cff946b152aa7f0d5f0cb321d45fbc925e5530f2fcfe1402c72f8bb59dbc358c6ebb66cef6c3eddd037159cd3b2692a22443a7815559b2e2671dc1fb8a1f6555622f129680127e040b56cbdcc1c1a23a8641416ba80cd385776c783d14997058e1874d527a4683ded050078a9265ac9349a43e0967350bfa6031647acbd0a4df7f0b0ec3b2dbd0cf36edb010ebed779898902221c5b3cb6073192202d1f66418de712743ec0f19beb5728cf01211a613c305c1e9447f51b806c7474ea884658b76c00d9183479286ce8a6ffa1e57998cc03ce6b2d68ea3f340e04109c8fc21d43b2dce940a4616b22787c9c691c026855e90b5a32adc4089d7726d5351481ade5a937861ca4eaf2e358f5994b0681edaa09aa4351635a8f5e06d28d22a8988407bbdff4c4612e9a5c2ca9a838a880025eb468a12206ed001eaa6abce003b1093b214075a6cbd6caca0a77ae85182c23160af4ee1eec7726be1fdc90f849f2066f8179ad53017d306a8d09e2c2f873064f82cbeb96338e6ccae82e38287149a85036b0b1fde6b59c3460874c2f4d4810fbd90a74cd93e52e0a289ec0953d29ce9fe7e6933282e31c309ac20079b96dab20f57e54836f110156d926dcb733c3702a21c1c3679f29b6dc3194ee9a2abf7dc6ff85c035aa1cc82a84297090bd3c15a0ec5a0e020dcb29ef610788b74609874eee09dfa388039f784b77a6e2879b44b8ad6999df05aab3a39875f2ba2ca037a69ae87b4c8551ab4a095f0669e46ff0ed1310759a8f3da3cc300a0aa43599d37005bf79b9262e1b5202af20f0b0e071a1247ffb1a2b31c15fbf642d773c96af973eda8ca3ceee01b72c0d1d0a0d57fec39e9852effda78044cab80d578c3d92caa298d9faad6b312040000000000000000000000009a78da2af5dfcfaa25a5e861a6ba68a704d6c546235b2172f08929217b31a94c67a9a4504fe762b11128a4f6ae49dd7c396f080a27f34129d6a26ad38fdc95b9d5388d3e95a86a2d08866b19e10925ab0a44047ec51056bc812705e8825bbdf400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b738d7d9b9b4933563c36272cfb52c9e40b0ceb940cf3d97185198fbe51c38ec26c5824e8ff0d2f06d626bd6dbde522f4f5bbb9995d1ec30d1e927a98df946139b3744739ab3590af5b3a1de74a0941a2545a9efcd153bac209c40f582a169c65e9dda63480c2374b1316712f6f3685581dc2369579c783e1f54692279b054a50325aab2a598b7917afdb39ca46741d89141eb520902d052611e69d00d98ce34600206aabbc38dc1b48164a13ad1747dc36d017d39256476f1d233659e5cec6a9bb20a0199b106cfac1b6bce896abc9bb9b385721d326d546790e6f922c9d2680e350e3a3dcff07d4f6a6626efac9ea1b907fe06cb232622ed4f52cf4a4b16ffd38b620229781512297ddbfbc3956d7ec7644801315772c1f67689f0092442a4205932090316113d8daa7d44f7731a47a473a082933f4a65f12636e39a52a716a17159c0c8032db30553b96693eccfe7d386beb4eae4cbc4df0fef4761e5ac0e5dece27162d7020a8d1c4b097fbd75769f31ea453e2d0fbda41458ab86ffe58fe3f1bf4d39cb14031d0a3eb4987931fb4314dbff1f21d84b72640c1e39d5ff0a0efe91deca58c65f077f8bfde1557bcd9f9bf4062c99c035c74298e97d014d73bd9e379b55004c2ca80eb8c0ee1b7f5472a6c5e825c815a11da9f699ce633128de636ce285532b2163c419a18820dcd954955b40d89a2d3fc05ebfbed744a4524433e1b4ba6df918b96bacb268bd0fb37eeb6bc6692bdd900b74d9d8ebbf4c31022db4ad27861ad208d325ed22057fe6ef661e4f223248424993babdaa6067c8a98deae66e3c436c06f5736cfee62e5390568a38f46a66c0f7b8dbf267101139e559c9b88fcf145d4f8ebef5754504f811f51489cbcb59cb094aeecd5d49d5300f2b9c2097e75a3fce68b7dabd7c5e02b5dce58ceefc85e67663249812b6775ec436cddc6df08586ddc1727faf8ba3cdc8e4b8dd9fd026d9a24cd6198ffcde27b14a3833f4e8e7b0cb6b1a543e38c4175a400a0f5808c6cab49fc5dec838afe95df13e265f1c540cee58dd20c241f0c4d2183378d51e2ad6025bac51371e0de43bc6f4ceb20b14847adf6aee52ca30a84e28faa2f2e65d895553b802e69ae175610d8559c6d4688e21a7a166c6695ec45c4d9af0217067fcd14624c15a8532564bb5e9e3b16f80b8d72ea79b8b82e0af39cf1e8670fb98fb2aa949535660b0f9bfce35b33f79fcb4827cc63e31b182a0aa8327c8950b43f53db79a166035323e97f1f18fcb3fd71eab90b5a5c58006589b9bc6c5e9b6366c8aa3f1aadcee07c207a1cb0c21927274426d7a5ec888cbbf268b79b9ab008beb064650c315a3262e2bb1baca367fa3b6d7c1c61adb9c4060a17071f98309ed0ab64dd5816dcd6a71a960361c1ee209bbad66ec7f51e6a0b0c99cda68223c22e1f2e487c7cbf5640fa08cdb156839e794cc1de95083f76ca8d99ff176665c43a65fb3583cd4e3613456a0cb2b5227a3b606732f387cc5df0c094c996865d71ac8829a8e105f81a2b3538e6b617b418d81e0fcc225dcbda06341a19d053dcdceb3b3b4513967b29a4192d589616818d872dc2b6f069fda406745fa5930a47947d7745272595f2c30e683c4c3cc08a29b7c882a746c0b158133583ead2606cced2dc23ece49b18125cebe02198da8af725111f053b9ca7ef37babb6d91d37d9b5469b03fd909317f9ca0d2911d6a6ffc0261e3bfa45d3c1f862065d92da6337385666f8320a6e07b808544ada25350cb1d4dd6a66a6d9ccf29f3bcc5f5de82c9a903212787b3a89ebd9dadfc6b5708bea697c05f32b00d7d6e78b2d3df110e7ca694c6b11af849cc14b9a82e602bff586a5f76f2fffbf4c24fcc3452eb9d81b986fbf68692746bb94e849603ee40ba3a2254854b8dd755431dce4a620ef9bb3d78a42a61878b69b24dfdf97c1a9f6c2df6fa5a92c08bef92a547f2311710452bc965f8e1da622569cb4dbaa712ebe386222b361f396ca45cb43e66ab408a903a546042520eba7b7d83cc05d8ecf8bc60bcefe69b0a63f3d4f40496244eebfe029187d21744c528132d00507cb1569eeb8c024f37b035d279556716689ac21209d4b85af14e39ed55419517d4daf9c32d7495e496bb08049ba17dc6cf33090c27afc07920f605ad89298d1ff0acd23861589ded0f499b312df5687e85231d3d5b26d943faf246516693d091503c20d50ca28f1fc384d0e5f7ab060e651be69c06c747a9c5fa6cc37913e181546ea57021d9a360e21527bbff4323549ca13e4fc707dac0100000000000000003ef8410300000000b38ab676314f8dabb3892a5e89d19832d5ac53e0d1b054e398f17f8b381a2f774b227bfafeb8b4a043e03f8d7351557b2a5fefca513a6116df86a4004e2dc7b071eb4b52e85679cb40e98c6ec44c92714f95f4d0029ad4764863650f362e8ced0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000098c4b85c03b42537590b9fe14c2af523bb196f86354e40ba37f9bd0f4acf28a07aa287b154451617fb050ee07ad227f26662786b3bfb25c1bd1a80ff38cc6520aa658f55e12bf4139d813c804ec76f4d25b153fbdd71c253893feca760f000970f2984ac8450df2ea59e292a3621624dc72483bda7bfcf6a15124a1204101fb1022e282674ed9a8c6301057fdbc6de59a770b1a2157698717f150d8fc2021789c4021caf2646664a112dfc1cfe73eff3165b6f6dad8bc8179c5895fbee22370106610a057cfa576b3203175b624796a616243bacbcdad2e128bc8c7463c2e9393596c4630a0f7337d8bef8c9f0f41c30b599a5e8e090b50047d5adb69786b1dcdb6a6d0320cb46970b0c9d29c170ef086108727851225db9a0c45e56eb9efb50193f400c0204adf640161369f9dde9e38428b098dc65c960efe3891f28895a3de35462e699020b5e34c41f10669fbdd7c12b446dbbbb7c5554ff06334960d3b66e6c9068a55f021d44bf6c54167f432d52d733fa21743841dd0510a454cf56542d8f6cecc05219023040ace97d354c938e8f573eef81a620858acba9389910f4ae8e0f11e3a2cd2a5596cb0ed1e0a6556863e8133c9a87cafe046fefa7d84cdcd3b13f204b02e555a0e43925b4db814cacab859fff661c4a21b3296103a52b82804ec7225728652d875078a7b411c317254e37fe0f80ad35b2163877253e8f977585729b49790c59021eda07a9a61c225d291d8cf1a1765424c6dafeb9e4ac97905a56e17d916e1d50acf9e99446f512ab3fe1e905f4449a8be8cbab12af4c56ac9cf158cd86ef2555e682b23f271e75d30f2b6ad948a7f0d56a295b7f6cfd7c5023f12720dd3c9349f3f7c25adb76d6307b86ba311b8a581f37d4350fe2bb1a26997ba2a2ed658ed70f018a0bf808e88f00240f5cd7a2089ef3c83ed8b5b7a54c52d92841acfdd3c3acf6d0944e11b4840631f2606a2be21a742f0511c4214641aee812e112563922fc2b58e644e9aec16f5ba0f41ccaf9614e3b1d5b067117e52abb1be711d6e96f9fae6d8415a197af059e65a42e64826449546beb9723575eb97d95355a96cb469f5bd6ee3dcb5600e252a8b42e62a80bfe2ff5debee835032f833a714f61eb90db1fdcf816c730c9fcf3320027eb978b6e21f17fa6bacda101b47fec7a4241cf6da682fbcc74dad7c62c57d62d64ccc33290a80c2acd1052752f5a808ef0b54839560d2900e83618fa89363f7cf5b3341a2eeb7690d23733ed23eb44ab5ef8f3d07e4bd1a63e2048b7103400ed786d3c7069b09d8993ce2aad6c8497cea5eef02dbdb69fa9901d64fa8787dff3288f3fae9e113222c12eead71c2b1808696507974ba83e4571bc73f66f8f60521fc7d0e4055c3dfbca9c4d0c39e6e494ab7e4d30aa5c4440ba8f023d7527a02d002f2eeb33316563ced7911fdbd008bbdd472735d1dd3672c41b8521d50aa3f565de5304daf8656f2e95b92423a9637434e833c794fb174e6d7cd52ca91157fa2ba3602cc098e975e0ca2d94ad77ce6de51afdd4652ba021b74c7236b18db6ad120b67927f893c4c2d04147308710ca47f07a4383a15bf145377a241cdf6334b2ec6d4ce53b1e6b07c88af6f2ce73d4384c3f140b0ea557a58cb2c9a2d91f6c181877d7f99a11fcfd52ace54d363933e5389cd78b10e21936e85c1086f9d59208ca0e0748e03c962393d33031ecb9b210c898a6826bace402b520de98c210de0b99cd36694cd4294a121a3e848525ffe9fe0d6861c716f2d3e9ef50f7683984831f8aae867e9eec10c76494a8f2c9cc93d873038e71ad732bca7dc98a0ee651d5a9d66883bb2a1028c111dd40960debc3a277816389f81ee00d423780a84dc52ca17f204adb940cd10cb0482c4e6f36ec509d3165d4a069df60e157568b04db863523eefeb33e994ab3c14d30c7570598bded16efc396bf3c43577c367d5a47efd31929dc670122c87723fbb13c5b2196fc91cad56d501ad228e4fe732339928a3b7756e8f25bb6dda05ab218907beaac7696fa744175123e7f6d05ad2b57fb6481e7638d02c43ed95feaa2ad6409f018782a6f991cea7438370abdf788b4fe2dad8e8b1ef63186b00c489d92958e697a896918a64de655390aca712aa69d978f5dc212de9ca9c61312ee09e218b4105e175493372c92de69007dbd35f8776e60e7920370350f135272b9501fa3a7d1088ddcf429cec565018ffa37c4de0efb5ff2f79b2453e249d464cc832433f331793770a1c03f6b080689f65881a068844f7092626bf2b64e73d205df9c7e466af736f287d245d9cfc9507cd4868b7fa59b05647ea2889cb4cbe460c39ec1de2f15dfc67574db4a1a082d1a5c1f14b80dae94d5a52b36c5ccfcdbc6d3c808d4b33ab40de8f8855eef66634f9e4ff248b04ca01a000", "", 0, -1656043844, 1537743641, "1514c9c10e11334aaf6db08d8a157469879b18c82a9226788b9cc13a63592638"], + ["030000807082c403019a0ce9a093a3b688576a43104ffd3f3892ca65ffb975a76816ee73b2969305cd0000000000c4a78e5a04e0692801000000000963ac6353ac655353527061bc04000000000153469dc302000000000863ac0000510063520cf0c0050000000008535252ac63515263f2b6517200000000010000000000000000753bc20500000000c89734afe7701f72893ac07f8e700ac92c985c23361364d68a8862dfb88bceea5451bfa3854a988258463c5c4fa0188b5d9ead46e6e3af024261a4ca3d06d491c17ba614eb2f6efffb9e205b3d494580603981569fd864b2ba6d1f79c19d65f400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d71fa7310e14513e9db55fa4c23a4a4ffec145a5b5bf2b7be99a69613aa9c9e30be98ce2bd4a2b4547da7091410c04c82ed6375df4ececc87e9d96da7fb55e0643296ad68f73c09ffc0d24029544b48889f56988f1f630b76cf9ade3a61c1bfe94f31a6bc7504ba2a135c497f00be198c5eb095a3b0b45405cdeedebf7f16e40022a53d5c76137e5265ba751ef43d3028425940cd28ced19f26595bdf2fb33ed420306984c87fc0a71650942ff30408ff21d61cc7a432c34ac91a87fc48c1a2e811a0b064fef6918585a09846c0ffc9a3aef45fde3d24ab01b8deb157528972abcc3d8ecdd353f67a9159689999c3e1646adc687c72ef05292eec93706e79f76e7d82902085eb4b2ac66aa1efd4a56844b2af51b1c7a9ef629f755931145fc621744cbe1030274e059b377289ff7cc7a12e1466b8e4d2f4a51f5d1bd724a1e09cc041ff560022879ee17ca0bc5fcc0a5edccc350c6ee59190838f223dc5ed953d34cff6915200201275884f6f642e29b4a28d48efe55cff15cb1737a0b8979c0172164c69ac32002139b8bbb056b93ffeda21c3ade69d6af7cd639345eeb126c726072d9722c02e608b8a8b02a2e70d041c54cedcd24e00156ff39c29588c901f32bf23bbf86aabee94f34f69a4fa5515a1f7ed19dc1f57e67697c82d3cf060309e9ff6f6316968bf123856484117b228ba9698a7d7e70030291daccb62a4b78c4224deca253a836460932f906089a2bbb180ec59a4db2372e45fb18ee88ba4b1f5bd77f451e732f807b7b7890e709b786647698702e2cdb8af98908f0aac4fda74c5c8722f3ed6111ba2263a910322ad35148215203d8a889f7c2ef6ff4cfefc8caf5a92298df6f6f6ea6d1d4cc71e2d24accaf6e0bd9a7ffaba3925e210afb128128d6636d242759860ea4a7ee7b2b5356cbb325e204f03b93138937e25d68eccf5ab9f250aff2d1de7dfcb7982456887b7af51957a4fc1d02b7bf984451e6b11de7622b96a29cf956c23ed517b53b2f04157f97ea8600a493a416f2a13743b8c999f229a55ca8bff5721f82a9f687fc26355616354b8c669256d8968a020e183225aa666fcb4346793d8f32e38fc9ada3c9b2c24b5ddaaeaa1d3b1368cfbb3a88f9780d997fd30b87bd840d31be258c56447f94b862495ee7e0d5491c6f9b25e8a41c1650787ae90cbed03f8308d25b8bff2cc91c0b5b50737fd34796d04fef221cc139b153e086899229f4f016819480ed3c56e9ec19b0994391963c749b37ca2166d5a876df309fffcf9ff2b3cf136d42310267378b1e855bba1f5aa7049f993f31f1d366595860f139d4ffefc5c7de30525b95442a5c86b0954c3da0ee31582e251c5f2410249694effaf8fa4760d95814e46dd9d4e93e6b4f8ef1d9177846958966e6c85f2252523f68ff2cfc8579626cdb910af8a0019f0d8b9a54aaa0cdc98db2573b7c7048fccb0d73fa35a875a41ab1e5af56b01503101477cc0c3a0de5dc48bcdb2dd9ce50e7a7894f6d1a1e7b401bb8f2ec7bb065ad4800189e30161f05fb8248cdde53c566042607cc0e528764bb1e8bc981f7a1c57e6298342f297eaf6fb0dcb3eab8001808cc8b782eb9a69900c26e618c00f3182dd2aca3db46a6dbcc570ece6369ea21dee7db1fd11853d45bb140025650778cfa573c1522f10882c6e5d65840d8fa346c76fd4f6ec599001a8819dd141d868858b069a4c76371d0aa0d08c8e741f0cd573254cc891b9f63be5cf7cae5683515d472506bd94cb0b6166337b31e40d64e30e668875bbe861ccb0490d5eb069973d3ed56b6c617707f83ac7605d55862a2e2d005b5cb085d3967f02426c5eb3ca2bb9dcc0485cef32c9deac0987d9b132a771839e20c9266e2a80ac7c10967a9763c797c79bf728ec94f4dbe9b0061ab01a063668ca6bf9057cd429b566fa5b846c1c427cae20b867e0ab06a0b7d0030eb1f61cb9810ac687fe5b8bad5ccde58c0972dfc2e5d82578c7465983b147541a29bf7f8d898b5360ab7e1477f03a451a08d464d8305c65a2729261afd01e67c63bae0a44229e5fd2722deca420547fc677250e4048d968d352882158b4174f4446e957c407e07e488d0b1605116be01b1020ebdb8f3be5c1f5ebeae0f22182275b5049f3209b4f46192909d1f75154685b83d8457cbd3ef1b9931fa11d01d7a75402533c4725d30d08e18bbcc9aff5391266f6f77eec4d49cfffcc139b3946f239b4419b347e45b270b165ca4de88713fd9f539404b190b4e8a9355ca23890f3f662557a6fd28bd29fdaf187cceb77d6210728e87f61aa18849f1d3aed86991a07cce1cd0868d900a32296b102f6bd8bf569476a16a904110de8ff3e58acb581cf150cb79040c8a3214efb1d2d736db15672ec78b58e131e338b1a42da7ea9ef77b9e02a44f854032f40134fe0b09", "51516a51", 0, 1447336534, 0, "18a22c811cc403a0940bac2c038a6c6fe90ea13126ff874e51e83351d5518a9a"], + ["0855b14e01916eb4f771458e479e6c921c88550f53de30231190f93e287812e270acf4cdaf02000000015248f7b57f035529770100000000075165526a53005345b1a30400000000046a006aac435ca00400000000026aace64474d0020000000000000000d9b50001000000007804af560769cc96f869159dfcc185028c52ebef2adddb6cfb7b6a45f14b05178ebb9cdb425dea5d9bfae023ea97b6359e7c449866fc57346d7e1b34b66e9efe1a4ff8773efac813d99a9210140fcfe94f36ba257570a681e36a3548bab509d3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002024aade5cfd9390213ae1f64d869b350e0bbdaa51f7dac346515adc7be9ac8cfbbca86ad704fa960796d26740eee1cd8d5f6c9f0324fa0079467e9318792d3609efde9f96e545c66a86dfa5c57f5fdcfc09f83a69a7563f234e93d1cb7b3db06715774b178c0a56febf7fdd2a3f3115e382b20365deedde11bd9c4a41f5a8b40323be528d5efc27b1864e97ea0af46753a334961c944af9538a82f0f59990e85c03140e0d829fabab4a4055b9c60de118509f04596a72f25f7abbe9bb5b35dc95340b073a0aa6b763c6fda5b618255dc9fcd9ca580fb9957a0021146ae14c251473f54d8aeb56cf7bda8191eefc3fbb5b02366fb9d24e91cb7a1f747cb1c3987a00a4032ec599f192adb762fad368634380415dc87dcef16ae283871af078a797bba3c1022be6c1e26fa91f7be89a6a0f447a6f1393de986b27b97b195d47868b41b4fa7c0325c298d9db64ec66eb6b17da7e9d76821f8b5fbf53c3d70d07c385c82e07bb47021974e59ba8590b332ff356013330a3fe7186f189c83fd9fd055f4bdb09336326031a814b401c07056e80c8f208da077dd71848093f276b782bf42e59ff10f2dc037d1abbf1947eda56bd4f01bb22a0c9c7cbbe7305c432fb350b61cacc9c611a06770615b7e3d541bc8a1f232d0efc21ed3177691e20dd25d0305575104fcc93cd71b9a46dc3235a383f10e65ca3a713799471be4a9859508868f9273bd3e63eaec171ecf4944cd7b549cebec904f5f4ecc8d9619356cdb6edbecc57dd64660a2e9ce05c0d48718a8c01b683867bb0b502da3f9faba7a7dafb14a6648a27d2b96e3976a1ccb6cd0714a9d66dda6d47ed99ecf0d6fedd7afc77d6b596522cc948af1be24feda757dd1ea64f44d83cd6c655a859f95a97ca5c039f7aeb839f6b5fa894b2077b68e904f44efd51e1045139a0924c0f722285f7bbd23f588076654bf5fc9fb664597ca1f6df0f5aa3262f953899db1faf4f7aaa71094f1d87fcc84c3b265a077575e621588434cd968f72d86c6f68487e817f4695ded82489c3d3410d12415a35ff0244e049125a8350d0e721db210ef79addfd3db251aa7168f3ead348bf4eb00130214a1016d33075f0109a69aa6313a2edd5102b8a209740c8e7b31663da8e3b2c047b56f5acebf49cebcc6b116daad469caec62126c394c3a4ae47a87c997228f5c8cf76d1c2b78522b9d47cc9048fa47a1695b6bd11ddc6a0d971bb83a31309f5083cd57f8ace0935b08c29c2981cd836273806c1233b2541142432705fa987e5281a36d349f4e3e1ca19dc8869844b55cb3b8659e8404cc6fe1c44f44eb850dc92d07e8d73ce8b5e7b7c91abdfb202b84877eee6e2404f993964c7a8d3429dcfea8333c231f2ad96097b9f8bb8402f0ebec45d1866d09ff4ce56c330c67e0ad35e03c0b0d50116945423adfc3878300abea3560919dc232d73a0908aab53e58ede23c22fd8bd15eff16c64ea1667cf09a7a58b34f127bdc321789a8a91f4309799dce09838377993cce2df3edaf19b0f63c27c491f5653b89ba3d957b98c75c2b96ffd2cc6576e8255c97dc24934450505b2a9bb7438100bb9e3a4c2a93426ee5a1af62516fa9f157dbf457004aeb938519937baeab723fe39294d96622f45d60b4928930590a15703d881c5676390778c39267081d50bda50b91c53e77aa5a912dd14b464cd4554baffdb4fa0fc66d74f1248cf219fdbb97b7f46e48016a51c5a517ba3e22450a7436fbea002bc80f1dee29f71cfdac72b603f11b9448d6046759a57e93225b790c488a15bbc7e366cf149a6ed28b4c09f69dcfae07ed9407926471aa824ab0d954ec26bcd505085a957d1a93a59074283fb3f91b4b3535987b537223696d6d7fa39ec7ab0cac6f5fa68193b1c297b31b0843c90abac6e9a5753fa8842ff13d23a569c693def1cc52162124eb989c158d21ef3a9f80e9541b914816991bf35d59d8c5b381d45d62ce79e561cefe29928a6090b707d53ab1cb7c05e2e212e696f08f24c9d8f20b1fe67f64f39bb6eec859760718571d5a3a0ac2f1e765511d344d2ed61476e06ac7130087e8df939372358604d4c11a85ced82ce24c18beffef7adefb2aa56a28b07364afe7b38fb43be39bbcbf9c638256b19f1a60ce3a568aa2198a7727995c0de56b831018662991d586f52b2964ebdc1381f6ab252e8290de1be367cc2df59520e85e0d731395fc19d7ad9091c38596ee1ce7ac653db07874866602b4e4e316a0e25e845a20b2a119a15075a4c1072aab2dd668c3bc56ea876cb0c74d00000000000000009ed1aa04000000006c67838454314adb52c96b057ccf86534bd88dbf0c2de710c923a5219db154aaf6118b22dc4c07151db518a1beb7ef2a9a927044644eec6747c232897f486ef83bfc52b6d52faebbc7c9eb1f5ad06bef5621ee16cce3aa61a4e0a09125e1113c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009f9bae5cde044112c8c715ef7a05c2cab382785671c5e80d0802056d51d2c483076da53031e02214d47bd2419482b2d20017675798d81a81980ecc4fd3381356edf20abd3ff5b7026dbefefcc74791b1049f911777ecb1e753cb779ac03de8bc5bb8113e236e704861836371ddb025056f663b2a6a7adef7da35cd13c5c68dc50325edb71f85e765aa556b36e3c9c26c7daf75b74d840cbe6b1dbc79afff29ebba032b8921447d9141cfab6a51fc59a6af845d78b7f914ea19a05d05b221d46ce2810b04fcf501f55940faa5f5d4e9c2ab5490dc53c04231de96587ee7e0a34364abfccbcc5a0243d9d892d08350c44c6ad05dddac4e1e61db4b984a8ae023cef6072f02247ec0b3175ad060c5077aa108e7c0dc83aa8ce4f0e9060794235ca56b0b93be032792667b7e5235a229f0650ddcfc8fd388716347888ced185d6864229ceec4cb02034833c0971ed7680a3525165fac44c771af436a8fc12aebdf18deab5b6cccc7030059220dfe0c1a583ca72924c4a4a361334d06118a87f342e8e6d68e51014db40221bbe32c6239c5c1c9611acf868ea49be917fe8acd4f25b3f28bdd64b5dc62b1c284d5e463edd4e318a5b325e9a0398665f23415ad9026928b0fcc5f916aaed4ad9dd840abdb81b0a42ab2ba5042682ebf484415199eecb5f50a320346e3c05d8c3618ee9b3f421a806c4235ae70bdabfe9c7860a8291c08e7e54f699f5cf174289aa80f8642b49078daf123a1586e23b0a2942e72c8e6bf0fab43925734bbe78ba24da47c557ba65b5fbf80287d7fd177f93905b219d86032b41448bff3364bc97dd639754dc62f3abc95007926caa85ee50e35578b3c8eba2b447a3fc2e8a1e376870f711bf6e76b925915210ff4726b78f812ebf26bf10bdd91c510bd5445e0f6c0c9731208f53463ecd5f236e79e2525685e12343476a31b241fb147f50cbc76171b9cdab826663ccf114176853e1b3d02e33b93f72e4175ea57475fb451042af0a3beba0245d7151374c1d164bd1056aa6b4e1a204c8aaee9efa9af4d0c4b98750f53c02276405dbacdf883309cae6811f397f1ba73636b021de46bc6c4587725eccbb43bc831ffb458ac6e7ea6c2d66ae458fbb039ec8b04a90f6f10555b0b398f9c82a794f067f524cd4456dd669a21ce5702a7c8dd89be5f5526d2f84601370523d0f31ff9bfa7844a377e6c5210fc4ad10a4a343df2f0143b1406d141050b5cf1b167dbfe5fc3bc94bba9d21d8f544a952b000256013ef3be662833eb96d64e7ed15d0a87e5762de70881319fa1ed8801eeccab8852f0dbbec3f1794c7605560530516b7b024a13f9fcdd26be7762c819e837dccfc998d01cfcbb5d64fea04dabab4e568063a79196a4ca68c7826ba1c54649ea63142da44356b496a3956bfa5f35b0eccb65f34e0b569f86b590156516d86ea559a77eaaf33393e3a0f960f0a936883686684d4a019b6492f85edc59988e9e3b086fc1397fc6ddaa72c8e05497ca237983296bd317d547a14119b865f383d130449bfe295c4eb7ffc41a3f7d6bea570c577caa53fca2202405776ba4e48bae77e426a64c8c087162f4c8856b6df8e69f6bd474f74372a8e55931511926289cabbaedbdcab8863a1c05e3881477940cc3a70d652df92d82fa65e1f44125b8c494bc2fd9ec9bae400e280531dc34c34826a1c899c86662148be387950d88f81a60d6d035aaaf2ea1ca9eb0b60df344924bd119de4fc745f8ec30c99239e61bb75d984498f181d3cb4db1132444e73e04e0df5ed5d97a250f40d06715368ed93e3de3a0bbaa6ec005a2e6c35b69822fc42c1b9a8383b33d114ab4f0890b5c33c2cba464b8f37b365433592b8550138ee14c97ca1d68814d73ab5f00628cad3ef6bbf4fad446b18e505501d3aecbc200e2f1d63a22e17d9f125c61230ee8c9babd8988ea73c43bca1edc7379fcec27a096c6ad299e9b268da6b614de5e0d755c7704207e03172201f59a1bbe66046114f7bc3f35054ba1bda7ac548206d102def244d4d2a0075a51cc5e83a1c376a2f4af3272995ef0df36d48f0ff6f5025edad1ce6f9f2c815d25db68f4ed0f0351d9981d158dbfb5421b2c9e56fdedb2f5acea787d39490b7d9a82df82815535aa0907683c0a8752ef08a566561a02ebde9e1838b0e5c26516c3182d92424973f0a0a898f0e6ae48e4973e4b30f3d17076903a0e9110115a21662337657f1dc159773e65824cb488d406692b3a141673631da4370ed89ac663497a80d685faafac510dda98fc903f421629f99242c1a03d4d47fbcd41d79e8ef33fe7c7f904e0890419fcea65b83f85eecf7e5f4375c32d76ebd01280f47642d42d3014a2758252bf65de5dd5001c01e64db5c1313a90036cd0141c4aec308114e6cc1b63fc5596ca90f00afd9a3a95eb57792d06", "63005300ac53", 0, 186284734, 0, "f45bc9b82d65bea7fc4063059298248de234f319bf2aac101510f7fe4e86ecb1"], + ["030000807082c403026767561a73a497d80719f0a228d294ac2397c4a40e8d22637e2f12dfbe30e5c50300000000ffffffff29c94b094f6af8738654402245dd09a7f2305be41bb34677aca4b9fc5670c57a020000000363656a37dd916101e373e5040000000004006a526a0000000023eec16a01d9ba51030000000000000000000000009abf96056b83f198926f2a80778bc2e81161ccf1c797a08444725735dd7cd178a9e77ecf6979681323e8664e4f57eff45b7979e271d797728b82988a6c7d108d6ab8da63f30b59c8bc8c584eea1da0a4e6ed2d04f6dc89b2a2a63476c269912100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000653d6a614ca327a4fb9c13d94741061651fb1d0d30a4afade460484e641d417175c6c0834c7c9f56e4c3b0c008ef31bbc1b9c42b88b2c4ced5989482adc0e359a276cf0961142f05ce3c1cb30a29e3117b3f87d4b6e4a4c0cb52ce6775123909d50c00c85b1a81ff806673e536576b23d3a38cf45ad66931110e319c7169e12b030e6f0dd7289ac9ef7b1158ee2199e24bad05ee86d28764727dd1698898db19dd03302f6d34c213ce1c4727f802ca4af8bee9564d7964558320580dbe348e6858d10a0921d586ecd1aeb2da30c0b65e82f94dce89d8dcd66a0c989e845d2a7eae8f76da51416845d4b7304379e6da8be62d0cd9833a8ed14b5b4d8e64b8e567a65fd40202b7172d58d1baa57fba806da1a097f2b1cb4a3e479678e196feb2d497a059570313c018041adb94024e4a6a7c1658011782707b88827b9161756ecb17093b5ea7022b34bffa6e3df15c5029a66635a066b21b6e7a6b2926c1548fc6ddd118a98963032fe0623c0a3b28c4d3783b13b4aca7b86227c53f45a05906cdc512c27b1f60e90324cb77604251ccb3e7a02f4f65f391d3462f3acfac83e34e404a3bdc5373ecb98825ecc652ee90cff7fd4dcfad6ada2554ba6024914f6ace2818c14fa232359624f8a346847a0e54cc4c100c41588dbab4bd0c7e83efad40d016c3c4b67b5eac0d376aa90aa48f1c1066cfc26b605daf6794017d8e1137df1cd137b0527536ac37e094e1bd883aaa76667a5deaa51568c9f242621f40540fbc7ca62f3050c0e6f92273f1557f6eded80e7e3175a517cfcc1f459a7eb32ca33cc617a1389603cbe55c0282783fbfe56d052d0880be9d0d09f241e826100344a3cac611fbc4beeee94dc90185f6adeb10a48597705380bf0dff28d3174f0f9d0b26b8d7b2759bdb5c552fc975fd5150467fbb3ac042eaa3fa58c0d9fffe0ed716ba713c6f2ec02c4ab5c7bda7c18cc6064480655cabd160124dad21236640267b8f7b5c68d9b1386ca669d5e82a06dc82492ef31c8111cf42e6a1024a307312f8a8438a696aed7a890a77b78755778ed06d7801f1b8d1fd78134965aa8b06bc8b3d6437799471bdd1197f33c09095dc0fb8e5c89777367e2a48da15c0ccf8fe78c81c20859cc306e6b638b9fc4366ccad19217e9889e08fed2e1c3bf4dd68ae2567c76a1b222e7cc2e1473676d6f892a70393e0d4576ff35815bc08222da8d8e18bd576a1026ac7835e003241e4e05a5524731edca0436bd9956db3348f43e84442bda0ac80b541451afe19d3e69782d05254df072cb7b501fde5b11b27c182a1a0c927c05c3b1f02cb9b87a0e7343daa1cc48044b0210edb6bcacf66ae28e9e2668a673e28c3e0ef037bdce4c4e2f58d5ddb504e74ab714746535e90004a0c535598d3de277e68adfa3988db054e1aaa77daf25278ba91bab5500df84b10925aa953921ef8737a0753ca4510240a1695d61d42fff9fd3113992f5b92fe41ec1ae40ef9a78416816b0d3fc055e95f62be6f37f168e24216d3ada002a787808f269ce59d02d23db346023e210a9e9e09f0e9f74a2f06321c883b9f016167cfea2ea7b303df9d637e89320480a86a47a0fca88cdf7ff6dcea56836fe044738e8dd6a6c364dbe1e4cdff047ff113dab8d9a92b2e98915d10839b70f2b3983caba80131ecfd8debfb9bb9fe6990b00cfc528a3c51391d7fab2e1e6e991a168bd3439a077bf1aa9542c436dd6e38028357013ea4f7654dac871a39c50bb926e673b87fa98969a33eb54362cf208852bc9e1e070ce95b61ae9d850b9fcec92b9cd49bda2ec5d8367a16c68dd7f71464470f312986c58dfce793bdd22bf59f987a3d6e06e868bfe16937fecdf7c1c129c93f89c85074e2503f7ee77d2e772f79ae2d3e18cc21946f113bbd30405c982422d978f88651c1fc7eca3b0cd269f2f8888b60d58b7c8aae563270d905eb7b07986bd049ff6331c09220aea5196d2a041d98c6e1fbe18ed2c5bf204ff2972453cfc368d76bac58d2c88464a01a821d1a3720c58f049724582974e42b5acd0d08487bbd6736147b96e2bc80f115fc72d22adb107068b6157ac39c7441d090154634ec96a03e92f4ad2177909836b2169258624889740bddec25b6c559af8b4c7d3907aea1eb886e56b362c777c9d0b0808efafe4089fdc322204a4b597e1ab74e96a2fde05b806b8af470c2f2f47cad8e7931d3831876a6b43ec37bfcfac3d7ee66e01adce32c80a19b6ee978eaf167f0082e0abcc039160259932ee780128bdc2b8b154a9f3ae75fd294204e2693ac7855a8c85aa940846b3f037caa5eaad7a97f0f66555330508edd0b5a2e3461245993f071b9387afb96021150a4300f69ff80f6bb1a66584a2aede8a4aacf6a6276c13b5c014df147eafe5fb57d16f1e107a8a5fa4383eba3eabb000b590f", "65535100ac6551", 1, 713730048, 1537743641, "d10ee0dafa0b2291a1c0032fd9f7c355de9bd92e366306f3f1fc629f554d0a2e"], + ["030000807082c403045b3215536956388db305212714ab011d732bdbce50d0a319e8941641703441cd030000000551ac52ac632260acafe4bb642fb7f140370675b6a8fda871091e48b7b58d5642c3c0701080c5cead3100000000096a636aac6a5363516affffffff0a0907b2cfd762f359320dabf1c2adde8bd3c7339dd5c0f2b8cc8cab2a5fac710300000005516a526a6afffffffff8a6edca1d478009e676c38eda706ac094c20d1f1706af70edd38c5fec86d32a020000000700ac65ac535263ffffffff040f3e3700000000000265ac04a18100000000000851526553526a63ac918a3f01000000000551ac63ac6aa7028e000000000002515175195d060000000002eb508e0200000000000000000000000095ed14c2cdeaf1c5acb0d85a4b7cd52c90916d843501f40c2341f9614d30d626ef809931b673a5177b6222035888690f50b91c34c268e03f8f06241422286023b665ee5b6c4122c4e01b624db723e32d667a247378513fbedbe7be26de4ffabd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eec1249239777d30af06a7ffe8582595980ef922b91a74b1c1cc4276a7a08f6fd0eb9a495ed995806e60a13507892a27f9df47428563cb9354cdd2da4db09f43b4ddb8ed5990403caf9cc7ff2e52a11507f667023b6ed0914607410285f340aabde7ccb06fa285fdb60686cd65e1fa0c11f349f9434ee48da11b6cf5d6276f220212668a150c6028c814a3dc4ef9831b79fb0237904d1cf95aa65638714118b0990305b90e24bd758382ce347c5ee9ed194978f9ca2cb1553fb769b7787d956cbedb0b026a7e43b0b75ec54c3768617708211d9dcef953ef31821b2ec2e59a3cbf8b224eba0ae02531aebec4dab2e8b005e57c633cb6b9062357c6ed1549e3ad46c2a402235f3d804255063ae7be97b397105ca435bb1e9a72333200b1c3b5039fd786f10316e78c1e07a8e12809869474b63642fa6cdfd3a34fe04e13daa74fcaade72442022471db879e613b1b9c5c152e915b2e068a79565aae0fffcf2c29d25489f5773a030a4579170d3f63db29521d3422804c112cad8ff9dfe12e811149e70961d60d8f020c0346f57b0e3cfd2bc419f915b1ecbcf7b949ea17a9b98d30bb0959671d31aaaef764895a27fbac08f0dd98e22b63e25c2c241f8d61b4fb8b4b6459fb686963c2114135df33f7960bcaeceba017a2708e3e3d25c0f6a31c548a58423179c30c9276194942728faab30efaf9e7729f0998a2a39f2721d2073b2ca68286868c75e79d6d1833a03615c7c8d63f7503396cb9e06932f3d3cb296690107c7efa64702299fa7b8e194439acde0ef26090a8933dc27eb6a99d88651545cd77432001ea12aee9e4fc68f2f3d14480079970389ed70952b25531b32f0d8a75358e4b7c9bf8e5d46f23b38ebb0fd49d31dd9e7c1da8e90e99332a54ce8bafe62e5013d07dc1a6fb49c31d2ce4c1d24d79069079d87e09dfea07bdf2fa6fd85dfa2d18a9cc17a864f3690c13ee0ff5fe9ddd11b198d6f3157b4e2852f81d801738eff428f08eeb407457c399afd59f433fc7c22564c9acbe01a862615a596ebf1ab4271eedf3e5c0b97a72dad0affb3b6f665d32ef147fd76d6c445eaab90937e7ba299acaed364626a1b37cbc7727ecee9c4586fdbcd5f1550c8498ca4565db92eaa361907171d683a4c6d6a27d1ac2bb10a4dfb5d63eba61c28fc8f8deb2b0d1795e0116af313dbca0fd1e85c194651c1576f4e6cbf1d7bf576c36844c76d1300b4ebd45dcf4c59a18ea72e1f7cd456b12b86cdfbe165d1b07ffd087a97fca614a965e202d89d113d12780351c29679d2f2db33fa1ca39524a541a71bd15c5834fdc6f16e546ddf4a8f4e95de56c5132b27b7c12716db0bea7f9a3679fabc180df4f12c39cee023be1a8fb345ee33fb8ba498d3fdd212586d16320b3f0d237ac04069287162c177eec98f9f120f44ecded074b7e7f4b2a8375ad31462205bf77ed41ca003ef3aaabdd5f5e508b7e5d48b4edd985416742db5efa35fe02363ba62841b1bfb31a79e35be8dd8e6f7ece8240fcfee6accd97b03caee02706081bb5dd205911c6e44ef807e91c3fe5d4a36f8f6fd0314c7fe71dd1c564e54cbe1e29082b450d798bb37171ae90f014fdc5d178901a9ed9a2f0e24b48b42d716af762fe855b0add24aaacd1f7dc723ca16a1c5281e5dc39c68380e959b6a3a447a17a5d0dc4ae8c7852b4f032ab9ee94571d7b71dc361dbd1a93987e03388d5cdce4875f333d4ea62fe5143f7d58707faaa6e2edda9d897eb75e326151a6c35dfbf686633641186c2c0a8ee947ac6d3e118ba10033cb4020354d1d0196ab7a9f8640d70f0ad6e8f5e886a144bb90734a68d96dd24cc1d3663fb3b3fc5485cde6f4a1737f7bb569952c73b0b1a4c6915b592b5f46f13662b15f0fe2d41f29056cb58ac66d69ead34d561a80550ad8a8908873613cedfbcf01b79f1483180ac12ed973dc2efd3276bfc1b63b42b37e09a3a9055c39f2e6b6fcd677a79a72d9c1fa46f5bb73d13b0253aef34371d04a5cb1cbac05bb543ba22d038082a2abd30572ded65d1ad79f1e0a536132bbb62e19855493791a027236210ae1ec791de1e635e1a8e09e7fcd67465ec1c0b994a4b37f21b3bec07ad97b1da412cd3330f65d59d2b453f38801d38c0fe0f4aeca1bbdae3a6bfea0eeddf081ce57b16302dab6cbae3040243ac8afbec78a31296adc6d86314947cd5f0f0b3205c3d3d10eedc32b51a68a4688bc61a2cf5a5086790edc044b641b3eccdb0d858f243f07f82d0a2a01a35367e58763c3f9dce37005f02d8bf72b167311f68133c3ad65e04000000000000000000000000169f91c62d622447256fbd9d47f927ac1d296934621feefcb613dc8732f3298ebc29cdf8e486f5fd1d86c4292be0e98329f3123a053cb983dcd5133d1d32e4dc7c73f2df5d373ca34d580b35ab0e9fc0283c3f59c325a2932d91eb5390970ce800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c340386fb085224742c22e6ba4500da7e6e12c04eebc1c5af1ad199efeaef9f4b496f04e47686a28554370183116fe4fc121af0f135010c377e64c1b9c9c2cb9cfb3ac78f93f2456b76b2375ba2a1ae76a77cbbaee088f4534638bba55879798246749d48b42ed2604c56fdd150acf6cafe93b5a8974e9ce2bf5e106bc84c80503092747810c58dbcc02e073bf49910b26dffeb95575e09051db1286612281677f0224ec63354ffcfd9a58f76334066584517ca2790d2077d2376e4a36d5fb3485a40b04b8384145a6bf6772e3c1a85417b364838b66523876b35fb70b35410fa72a403a0247e157100b491630f1eb09da78c4d71ed7c8da8fa77a539df3bba0a2b5e10222505c98258e12a0ce03a8f7b1867092827cf0ad66e8bc57857eac05a939b96b03152f63efbb47d50eb3012f01e273f80f9ad7dffd6668a0fa06d9acde07f1ffa2022ecfe43429a5de2103c699de2c2033ff66599c741a55f535b6af13b996d8c15502291130c5c9bb028a5b4cb6efed144b927eb4e5594af70247811399599db072020229895264a89001f7c506373c30dad28c0c2017d50443cf59e4daf362264aeaad9b25c2e084532a30977db54a3f7705d52db7af305785987189719fe80bbfa694df6b9f51c8946d4ebfbc042b532c3b325278f55aca310144fd484e12711e206f2aeab81e6246818eeb66e8481e242de2f88c77042a7d986384df78b894aeb976796e3b2cfce3c4484640e322b712294cec458c45f9cfa1f66d973e6e4a7c3f72adbd4d7046ca60494459005945e668db1ab725d0d8eec0598f745fd709fc8924174c899889ee669af8fc60766a5cc6d1374baa93b3234fa1902770a60a08172ec643f5c0780a9e1580413109aa6518da62a3ddac7fa7334e8d53cea3cbdd53b41dcb74f939a894d8f848e189ff1fef7645fdb6cb86e18389dd7bcca5978d9512c9157d1c22a099c48ea44bda8e74c5bfcf0d82e5d6000832b530157f92ec345a1289362dd1052a9b0155319349c70bfb93092e0787e3d88add3f8bc26c168fc8a4cbd84e3c2d200ce7979b6e7244ac7bfc18071e41c6f6eab2a347a669e5a01af8addf8340774a671c9d08442b20ba7461412cf2814c45f972a21b463548c349c16bcaf3ea0a574804c56e5e3be3cc9882502e36574138f51582c3f0b27c3d098f8c21163774e40d9ae90a0c0f2fa8083ab4a66d1c6b76a539a4cf11138c143c633d6a514eb4b41d16936ce5684adbe8de9e93b988254084267f3b0b2dec6d1c79baf3b3fd542e7a7f432d0dca3c33c915be5ac491b48393ffdabbbf218e5440286104d63ac9d45f35bcc89f64524e21ebbe8d9d2061962e653b457c4548b156e8ded9e683bc80b84eea748f30b10f21b69019d1164fb9556e615ab7afc498c9211093c5470e0d5b4d2a86294127857615b5de5eb6e63999fca8b66fbe04c5325603be6854c7bf7616a4c66e02df5e99996c6990ce6d6c8e8bcaa32384390adb05613abb4890d05617f1b363ec7f86b321cb1c0166e012afe5a217d8604a9b0771debbd81ce8ada02478a9303b7959c96facb1bba5bb20601275beb0207dcaf71d0e3a904616d9f6f90796b9e44daefa04439fa1b487a5d5d78d762bdf6ee0255661fbf83089cd596dc337efad8249ff33ef40471482c16453f0116b72e3fd66260d4a909d89733d2f9cef2ac396d9fbbf4a4e54192b243280968ac5b74cd651f6cfc87d9ec7ba01582275f5ce403916f00f666151c0d4c82c1729099680f7f6df42444e3a668c15152862730c605a1580ab78ca83e2502540971330df5a52bbe3648ff03eaed470586ba2aa701444b22e6cf7c965dbb039fc6ef84e9feb17b2ac90e3eade534efd8f26864b6cbc99b5ce0819947e3465e5c7c33e3873b71fa7324e5df0e099bf43f9f3b9ad68119332403304cc7a09eb029e567f597dd435dc200f06a69b2ce5c92d68aded78940e8827c3e261dc63809470a2d5720e3be1bdb05dbf45668c60cef338b8f79c210fe68cd56cc1eae71a93e26c3f3f521fcec62944065b3a5a18fbf3a434169e04ec3f9c93dce5a1b5dd952760dc5030d83f39bb7f44ca32638e7cd41745a0b5af52a3512e0b426b484f1930b04ca11a38f5702e02f5022b5dbf5ee10716650ea1aa888587893069a549e1780b3cdd03d879842c9fc53ae8ae3fc3897ed2377334efca187b294bed17d4a3472eba9ecd42d5013f8977cda017933c151853fe809471b9fcc17d7f00613adb70d855c597fd6c35b5fd5cf824d7f38abfe44a7d5b446cbaa33e341c7a5b975258df6f14a63a786bd691fc58373502a3d3b94644521226a81a235adc6461d15250ff38e3eb2a12417a41e4cbef55743f43dcf3ebd4fcb1b89b96049d24b7e6b8e0f02abc1c4c6f3b6c873b095ee0162e125d09ddfa4452b3c809", "ac6a51630051", 0, 1153267492, 1537743641, "16efb957df372ac689541408337615b4337d6e1fa5b532c3d574bf40e5e2fcf9"], + ["030000807082c40302cad97cdf0de4519239ab2f319e2dfde0f8aeddda070fe034a70a025e5d1c223a010000000652ac5263530029f73362df30d54e807cc97c2d6e9e0bc1586890b94cbf6fb734ab2d6e3e84c71895562c0000000004000052523aa455d20268c50e020000000000a2330b0300000000070052005165536530d9b54a944771be010000000000000000d06cdf0300000000ea13198ca341ca75c98019a114b68606abc15a71192357c93ba6250a7189b0020a631fb348dade40b628152d4931023ec9172f6811b15b6ef91f651bc6f490dd4b0af690988689326be1ede4fac9bf98369aca8ec68ec9323e76d57e2db98997000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e3cd791e7891379396a96d8e16121b3fcf925d7a99d60a421570749f1e67c0a48229f1c68e4aa47789d381804a2a1e79aa0971332c888b1dbf689b51f4e7e14e5e2ada5edfb12fca78c6db08bbd19956ef247e650cae1c9a926d076b16bf682447859c4d398d69267496e36e768fdadd99c429a358a2f6584faa22f189b375d022e82dd813e0baddccf91d5843136ba598cf8fd896616b870ac2bcb5b73ba2777032725b3a2e207687afc6cda1a10b45394c70c64ef6871bed2edcd2828df8b28870b006c51e24c3882efd4097c245867fb6dc99b53525456a2783d8e29bffd9193ec2aff6b0fa0b61aa58f545f194728798a70c583121a9aafaf34083ee0828ad8a703130143176a8ba638435c59c1803bad3869519e930196f8837b353ee403c2a835032af310ff99e388eda3c369eabb0c7441783f9164d6d282aef6f4b99dba9f64b5031a62e32b782b7a173ff8abfc5bf997cd532af61fa3668e107b850e5ce382958d0318403df29ebe414b90b694b788a0e25e876f56d35a2f4086135706751ef9bf0c030b366385a895d9e6c49c504e48ba875fafc22b948d3988185b23486b7678a16bca4d3e047dc0d0bf06867c68d73e21087e693fe62d07b489144ed3ae39c4fffc65bc62c147acfcefce831348ae63c153530498bb54f9455e5546f88766b18d7302a1c944e18a1521323d79d490737893bbbb5bed727f431ddac8e2ffdb90aeaa03abc8620e2ddf8cd495dac7be0fc2f50ce501cfb12639193764e0f0e005e9d995e4320c6ba713670ca91eec1da5800e8eac613be0f281db058360eff6b66f5328c17f611abb1a85612b7b44171c4a6575f1a8a3e0244e1c1ec734f238aa3481d6ae6a801a84d0206fb29bfa3c947eb967219d59ca860cddd407b39c813e30d25560f63440ff6c4cb2cdf8aac9e59d02f755cc5348aa05ca0a0a1187810803e29dd30e32444788072df9441b9d07c893eab97be97641f95925dc1ec6b849576ffd6b804e6996d0d1d4622aa68ae18a0673d811c6a1c2460a10321244b4215dd0b633a869929ef3092f0c16039f5b8828d0dfb64991f90f94cbd78da9aab41d89f205200222801702fc44185770b4f34ea1ef48bb3fbb0a8dc6ee1bd5607551b7a13bb1c84dd31758411711de7edeaafd3a072975bbe016fde851ad444a2ce46e4f292402e6f0fd006122450767dca454f3c65471e69a15d51486f53a9b2e1dd200dd0eb830ec030d122f11a4deca3d7fc084921f1a5c4ef094683f297a2ac4be18e6567009384ab27febbcd16e688920964defe688bee5b77143a4ef483e72aa435e78338ea1cae09ca3a19d2bcfd5ed87474d776f02ab268ede322fa938e66f4ffabd87d6a809e6f361e7ded21d4b22ac918a32a84718059e054700f226d4af77e0eec54a3defb5549145f7546a8c167d5bfbc05e351456dee8c7eee7c49c6f10ac570c61dbf961014eb1607349b0a773a05ecb5a0b1eb7507feffb12bd8a31fe0fe8b00a21c5a6af43a2b655eaf5073467eaa48f2a875eff0a9ffacf7f32e1bcb2bd4cc3abcc14bae7796022e0b848bfcead8d04cc48d96b63b608de65a274d885bd99f50b157e6730fe06463393c5c10d363eccb6e2874b068224a21a662e477fd9e221454d960816d97987058a28282f3b00c49575af918916c20a8edff01b6f89ccf079c9211905b0f4be243e31e32a209024a04e5b185f3e545ff2032de8608d842a0333199e470666b440a40015834908147f9099b5654ed72e3f1c46766bf7c8c7babc512b6fee9fe7afee551f79f480acc30da7ee8305583ef1c73350aa9f69be4333b751c5c923fe22bce0b1623a15ea4957861712e352ece30d695460050017c11383e85071610abe2849b969bfa087c321e9594b1c811878a19bea93dd8a6a96873c194dce12e6da733012d997795b130f64204c2a522cae1e526b198c5ad22767c09f01e7da74547f6cc852191010e884bef2e2c40cb55c18f119fc8fb11bc959a5301a2dbb361cd15c47ac834b0c93363a54bb474c6f7b4e17d14e693125b2aad8d04f108bb0b450ca4f96e0e39c1dba582dbac3c186a0e21cbc6355fcf9842648bc2538ad0da7cdebb0a9c6bb3e6078fedcf39d690c50c0283277763562f2a5bd91e6f7c5db8ab503a666ede1b16574af125b42b0e34a5069a320d5e0fabb3d6e4a69162a4d7d81167ae0774491723bc3926a86510f112acccfef3c262bf9655f25c5102c90132f39590a1c2706da1c310a4671b9cb303d4f3608a47498ab42d646c8561fc3aa152d52c7c6647926c063f868bd973a91a5e6917aede0c94d1dedfb227ee09b409c2e08f3a2d02e9d974a9374241c3f783df5666b39dd120501710c25811986dfdb35945342585114b93ada97a5726739ae6394e61345426a314ab988f776dd33e0d35c08", "516a", 1, -218171987, 0, "d965defdab1c59592d14d888b9d558ad1d667fb6b53b5befed361ea1648c6649"], + ["030000807082c40302edb553e2013468202ac250aaf895430c3b033c9c4ac129b111855f8f12250cac03000000066a6365636552fffffffff48321b26532d2184eec336ddb8a66206557ecdef983555beeeb8e82f4ea138e0300000002515261e640a903ec7a3705000000000553ac51ac53b648f9020000000005535265acacf863a10400000000000000000000000000020000000000000000ec5c1a050000000064a3d291d37b955fe8c175362007a35b0b4dd1d655c47b1d65392647fedf6f09095efc9416d7162c49e80cb87ea06339170063318a869d6c96ca33c75264fc453aab813e0458cf1fa0bd0e7d49114cbaf59b0282026f328b4c06c0c1efbdd81b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000644ac94e0dbc99e90236088282b1a6de4962f0d8e8dfd6679f956a72a392fc84e9ff5d9e24df52dc94c4357174bea34317aef96ec0ff442b90a5c5e683576920db3161f398d15c9517c390b709eaac9fd9523ec7ad0874a40c08457d8eb6daff312862cbd0d1e2496f39ed795976c2cb9e485de30804d31616c6fc8c4249d35f031796ec8a1a0542cc56b578f3c87fabffd632fed2aadfe8b7f894944aef776669031e773644f54c3066505735789adae1e559aa17acaaee279bfb1398ecb19f01690b01cf104c0848104c8d11b94c0f36eecd1609c84526d7d91175a40e3477b323aaee3a87721cb599d8e2ead5e098930004328f5ca487a69e6317aefaf3127dfe3a0203febe51f8ed2c0e5ad373539bf9d863246e3c68c67f78d7629fec8532f973a8032cf17ee649ee8002f5e980be8c282f0f0a161f45604c5a40f16c59405bd748fd0227f3b8f10ebc1e17a6deebc4bf3dcbd6debfc33b02e02da0491b1e66549044940324a51753df261842aed311eabafb6d815084dfc2b4c0d97c0f69bf76722a0b59032dec6ba8a57db136a91c4fec5ee22d8a4ddb914ad4a09be62a017353d866124b73926dc0bcff0165ac3dcf9b67ee76183c305595f00bae59a99bcd35a8997d1aa7b3d80797bd760e2ac9fb1127002068b9ee5a749f6a2ed13de9294855f7f71d81691e509a12dd80df21b3a6009a67653fe8132db0697a85cb7bedb97524f4d059abcc0b7c3352c8f31eef51be800fa2477581558696dc0eb6bc7294f224a8ee5f382c5cdcb5f273bd3a68c906469cbfb8f499aa08d5a202f492ad99ac0d0faffc9583747c0b237eab09dff58e543d7d56cf1ee668f3528c13ea2704c9486dabdf48fecf68a5760f0dadd86cbfcc8bbea291c19533f9918ed201b5c6dbc0ec8317c8a2059cd0bcded8eeda29373e68b52dac19831b4cd0fab14efef0a2f252bd94c959358d3129f7fb6858dde9a3e8f7a36577b3f05a87958c647688f518917e06354765bcf3d6289d0170697788300395c4bb4931af13beffed86d3315cb160df2c73a5a94cfee8dce157afdc085ed867044cc223c8247a0668c8362aebe0694464b3e13912ffe5938e1c21a5d6cb10dd4afe6422dc854842c4ac596497d67befec9d17a1759b5fe1e12195d5c920217627d75820ec5dc9d324a95e6c052170a54be45b5c171d3d15e49c4f5e83c51d71af4ed42462d1f721156a1156ca3272dd6c5172cd1d4f3cb9e8cfffaf2609af201ef21cf9d021f5253e6fcfe2997c67ae22a877423e3b73ed71892dc7155a0e8950263520d7f3ff4c54bf9c8fefd819ebc422c8d80192f6941c54c69999ba5e523ce6a75d75e41d50c21ddeffe022c042b6ef996368b906eb48626c1f728154b3de10b395ab6d55a1040e4c4e8295491418c39d669bb327318b16927c8055edede366335e5f17fb8556773177fffac8c59185cbcaaa3b7f6ba485d9b6dab99a42e885726fdffa294c8b0108709edc0389116bd590ab61a0f7360e77451e50da0fdad6161f905a987262a4ac0a12f0d6b98a8dfb0baafd5a44252ade19e1affeccbbb38f4cffc37974e7cd0c7d10784eea65a2af30c1f11f2d2ad8fc5a9ca8388336de12b7fac7b9b3de35974f8514e95d7188c0ebd9c140f277dce8202928dcbf8139185e48e62bfcbf8cb072ffddd49a1a59910f8c0a33d2654b87714dba9eb36f2e64bd37fc9f60bf226ca551a765f2ebf351b70c5cde24b221f0cf1f09a83e807dbdc95ccd0449f651f661ecd40f5c69a3787b45411233f1b5846806c578b3454f4cc53c5c0e49bcd8b021c4d5a91b79d7f0e22da48adfa196875b39f84a2f7182c382e846c637c54e6e0651863af0e378e27a5f5cdc6abcf811518ff9219e4959c143a42ec3897b27438a719f65541addba3916d35db01e114be753da33314b67875141a6f4c03b532228fea54b471c46f9a1900ce5a7eebf7287fd317de66a6a7590e84adea46abf5922a1fa27e5c1e7882b5696dbb457caabf0009bddbd6d6fadbe3d3a0469357b94028d1718420d589257bc830011749fc7bf58524da5c45b2d8c96097aa4afbb2da8c488c1543d8fa91a65decef7b6f01a122142a777b3db861409d8ec4f7c06190c40b3488b302bf529078dbf11193de163f7f1622a695666c10b1ea31aa3044c7e99c4d492d372f9a055d71b03a4f0cffd436ecb9482d3f0c6eae1d89fbbd218ad328d674d6bad52c2772217e3b57921e7523f27b14a995001ecb679b869e18b794581e6f0ace78a3be43bf513d024d54f404e3a47e5c698270300000000000000000000000090ae2254c998154a75ca2a23d0906bd51f1a10842311c6731552572763d0e678f3b0c77901de98c1aca2ba8331a36d7e29081b1d6db0130f013149e1f49a70bed56037adbc8fbf8638b8bf2a84c0e8f84ff1e3c93819e17507f00259d9641805000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003555f9517269fbb3611f2beee1f1f5b94a4242737a363ea116bbca0e46d323d2045c4af26d37923dd6a80a18b1f76f072477bed5d8a77f06b2652f9e743b07b0b2242390729fb0874714cb709d3a692139786c83118f321b5929b245666e0863a102979d581a5c5e8ef5838694c4db0c70735ff575f3dc81eaad130acd06564f02115753810b310b7b1725e4ca0519801d64d7119a8a6b12a1cc79077d1e5aa122031d3eac246bf0c5fd31996dd7b6d4a3a65ed7c19f54abab1865e4fa56508e8f370b021e17cfea34226542ffb24f46dfcad6c3964d5adcec1172f67f418429261bd3927b826a522a3c52fc6255a76321f98f1947d60ae38a059f5463a1a19f5d362e0221d3988dd326c0522eb116ea5340ad186de4290c544bcccc7138d1f78830934603007ae3000aab4f4a4d533783f265b94dd6202d8e561f23a02f3cbc946e8925ef0306ab4f73329e120e7851829805a648b9eb4c976c932954e3dae24792c0f767e503170db9d25faf703de6a29d8e17c0f09c54920888f040e6daced91b4ba4757745032c3d6c0458221b0639a6ba52b0f0b799154ae187950c04e6edf12920c09d697b62e719dfe0d6711b497ca031211bdae61e0f1ca53a4f1d15487a63314808b93175fd9a4d985927bb433130473b77530ee8ff1ae41bbc04f0361d85ffc6eeae76f7ecda3267ce16d7a4bc9a8cc42f03ef80b962ac606bac04d6a018dcf1d3e5ae640dedef9a5a3070c4020dda229839e378a810880f9c054d7346e6b677f9cb1a47fe929875bc5187d8c6181c86620bb07cd25e05d8de6df7088871b739cec58f7b266fdebe950dfc4dc7c7417efaa2c3f67be978d70fe1d9b862a8b97344c6cd2a7661be9dd9e27cb129cbbf7efca0bafe06ebaca20d46b5846db0bbe4b351985a4e3db8099aad20256e7491a381f95a49379d5b1805782e64c68aa48d04a58853b9e71e09f17e1c2dd0e6a7e4840bacf5b32e75da599f966fb8627a39fd49a2aeb28bd61a7c6a855e692b8c273ad120b03b9bd7b9b2faff2edba424eaae8d3e2537fa38b6ced6ec67fbef6d23f9f16547a3680b4d1b55379e52f33b534745dcaf26bfdac36d6768592e874c6a31684bb5b946018e3da52b91f928bd2927aeb203cdd6d6571dbd7ee3ce03b1f16bad3bdf16e77e9c8a47e4689c2fe3b8bfcab15975757a58d2395a17d8a2bf0c56b56b769d980a1fdee9c089bb8a856cb69395e9584087ad3264e87d1273f922230f0215579895c5870ec7fdd77b0037609afc90680338d99218739e9bcc408d63bd14271bad344a5e1d75316208fbd819f132f6eb7068a2840b68857c87b83471b2ce8c5735ce0dcc60e55035d5d3766dd651a07addb92a93434c6cb60b5bdee383f9f280f4f22f7a972beca3cae11d148a5668190144d814dbacde6f90d8d73ce2aca905eca71973215af04ce061913154f38d00bd9060f2ad02bebb197c47a0ebf25450e150df12479eabaf579d2b21ca1003d49465d1cd340438749a455835ff45b066cf8a7cae097bda97efae9b9ada06f9699296695b9b612f88fe9e91bfddbbe64b94553eb2bac079c6eaec064f8e81f230097984f0522ef9c16e8c4f61525af4f7f9453fa1457987b822b2a65d7ba4f618cad1297c2b7ecfcccfcdeb22b696fe8653dbf797c8907fc0c8298b8273461559956b4c1069d329066b98344b0ab42bd51687b5ee331068758581c9d1b1e5534b49fd68776688d62a7eaf53cdbcac5ade703cb7e4bf737e46f0f3c29bddb3c0ac9165c3d58ac5a7198b87c5721c07c040f4b3d3576da0ae18e1c2bead14a053cf5e21e699b18eed45509ac0dddf1e90810a6c8a68a0bfee5eae8ef7e7a49adad8a60fc6e848e8673d0b58b798f8a5d2c24fd7bae4f7a1863d292d15780407f80dd22a2347654479ae83727af9d46e7be1e717d0dc321dd2cf7c99c16847ef239f9776482e69a7bb8b52dd22e243122cd7cb94f72984d9eec0ac6e12504f51ac06b2bb933022d3bcd3d3433f3201b809c8008bf0d533b012e717d7a82dadd591ee1c5528241d28819cef4cd497d87e5a78195bdf9a5f15d45d8a5220fef8eccb498c1e8fe8f5eb925dc3524d6054a4a7ff118ea0743ab344a1d62d4455d67a6e05a100b3eb052c4719b078d77a78877a54399c10d1b6c23f5a22f46da12f82e6a49ede77415a75e804cc6d8dab78b0d7558d02731163a99a791be7628daec2936fd6f2617da25ee81d67f0c3fc6af63bef534749387bea18276dc3878cf028068145448438662026d52459881da86879373bae48702c2c7f257453f9455dfc4c3f2b93cc32cec77492dcb9a755ef9dccfc138d7dfbdd7720274d139b492c30412b37b661416d684d121042e016b40e1ffa7abc2c4b30e463f636423049f32ec2e24030d5f69610ffe90a28a8cd33679f09", "53", 0, 721362411, 0, "f1c8e9c31afe8b76df23cfaa622ca5047c70daa7151db1ca5f4b7ba173959f21"], + ["2eefed0b03836a2ea8838c901f0c319e0d0241b34ffc5525dfa023d5309014c3291b6e43070000000000ffffffff8ffe34001d582ea0ec5bb4579cc2c09adf7ee6568d64cf4bfcca6c7c64a1d85c0300000008636a5365ac6a6a531a18b974cc64a671c94fecd4d15a3442353b9251ca7199a135d5254d0d78e984f4f27f7f000000000653005165006aafff78d8043d4f6503000000000600005151005182efb0050000000003650065c97ce00100000000020000fc9301010000000002515200000000017882270100000000000000000000000003010fdbcded18dbff54f5fb7ff317c40c3c09436831fd6ad46c035a34d288f910b32c307c84edd346052ee9bafc2368e723b00dce6151384521ec659251023b42e4fa20f5ada1ad4fecde4494515a810819bdd8f59c5143fb06d7093136c31300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c01d93654c32186c21b5f0a87c93c263bbc958132e75b4ab630761fe0dda7b2049495c29bb85a006f166c7dba39bd69ad1d55cf9021a0f1a3b3eaeb4f088c0cd8d88bfa6488838c4dab662c7ba557838da5e74fc6c00bed111b066e2c0a8e6a7b6be4ef34084dc196930c21576d1763d64d194a9a61ba50b173a98e961d3144f020fab306b03b7039fa6630021018f1c0c7a07eca76698481477cdd9b8a772cf170229bc9e7da6f4ea8132b3bdaaee18563a2ad13c36f6bc6c1bb30db042bc27ed500b08178f20d82ee86bb55acbffa1a1212e23666fca870bf56ed08b5cf0b0fdb7428f0e06be0bf7fa3ec8209704a9dbef269fc5972ea4e38a7207cf8059a770c88c02182ccc2b6c10d3a341b00db37d36ae3c38e8cfe6ac56ce2ebfb37fffff5aaa1c030e41e298bc34295b0fa547eb907ecd460bb1836062cc455fe76dd9efc7ef550303069bfdddb8625ca0034a2cd26400dad326d73a222d3173e10fb72b62bbb15b2c031c28472cc8f49cd93f9725af37abf8876b9dc74e2bf97a5bc162d701944aa65602270ccc525eeb7e117a7b9a72f102ed92ddc7a3822f655a9f6566672cce5ed14512d60629f9e77b3fac4d0ab4491df178bec68fc3aca1d2374fd5ec765ceffb6ea97de44f0a8090f6ba85a9c57f84985ca23b97b6382594b4f3ed6f1437d2f860dafc7dd216ca5666df5e4154a0a9569c8274ea7dbc5b93abf8842d5a7af6aadec6144c74831060e43d9acb8b5eb851301f376d1c60fd1376c21cf2eba271cf6dd3a3eff3b7062b08ce48d3262a50a48357d08f3f49a426d5282590e854c49275f18d9f6f2701856b35310206e6d672e0a936fbc8ea2bfc970cea631012da801eea52c3c57f7aba6d53e2c5cf06eebdc0122f6d56e547031e26f021a2f3a1709dd930f7d12d4fcf4a5aa5247a042780f26460bf69fd440f052c63fb7318c6319d6e356389862e79cba99b8fedb924e25783a546e58bd9263352df3a96eef8f19e184049dbc7fcbbf16a01a3086a75d43105a5780b2a487874c9c5961c40aff33f5fca88a6cf9fc22e324eb9a682070493d40a97611f0b520525fe7532ad7fc5baff08b6d9cdaa04f7d5d8e76d20ad7ae865e6ff9ad8a24e0e82647525763d45f8af6859ec15e648647ed2b8b2dc0a1d443a66dff32a44d6e5b274a3ff3e0b690fa3e10f40b5ec2c54f1789306ae289bf7b75274df437b809dfbf8c19d45cdb816973d28bee02cf60d8618fc622f7f7b2721e80c1ee7b4048ba24a1ecb518c16484cbd4d1c96169df48172f761d8dd2b2bc90fdc7414eead944b82a59ec6d81b80270ba98fca33d6d3778b1bb77372412ce4e817007da0be73e3a26da69bf1f07061284cba8e2c944c39de88ff5c504982d452daf9768203c959648825049e1f0810ba6a40026f5f6c407f83326c4c9214c57ce24ecaa7ce41db8d82f19b4fab80e0a61956d60d64f7fd71f08538f0c827e2bedfaa49e53f2b559bd64f0b85c62a66332b57a342373c9408af6ec520cee003a71810c12ff311609beb9fee8825301247f5a37cb4b77036ee2f4a56800d869966db2258cd450ddccf3f777bf3028ff57f94391992c2231a5adeb1a61a0f74f2e8e364f7a0004eb25df2c7f79433fe928db0cce38d25ee1a6966d91b1d0d83f2fbaa57f2e8c2ed20715ca78c4ad0b5272dd1c73a91b2e8c381246a0c2293290e09b45005ed06f9a1f1981cfcad1ab6dcc06a64e2b92d4c7a18e4dd903e3411cc7893d170d9bfb476663dd571c3899ef950afeab950ad344d182d5fd83d7f0f5695ef24f0cd5ed2b339478b5789973e503800afa1cd8d4ca05207a39edceabc7dd1aaba24f9a415ed56ff89535254ae5d5b59009529388e08a3dcfa8294df4dd501a37d47768094b39bf8d61ac1fae118b167d7e040eff5a09360d517f90e957610c6ee85a49b8402f69e711d03a6f17a2fd732ef81f9a482f678d0c29334a1aa1d3f749ffac7f83f1288b93670bc16d58115670fd6d19ae63606240277e4c9fa8ddb205bf568b2274a9afada3650d8ecc91c042f29c675263ab7500a743d43b97012b2f902d079826830d3793c4222c0d659c0679d1402a72705bf2fbb35f006e52e84f70fadbaa9c6d7aeb74bc02f33fda26aaa3da1e5bbccf722457127e3137127d9d8f46abbac6aef6398504367e853d2f50ace8be97c8ed37b5b28b06b01df82f697eebf273e8efc869d513a39ed5079433c1b0b567330a11f078a3ba6d172a42724da7cd8242128970ec93fd64e17dd92a517ac1be0f4cf4a5fd2817e16a851108f0695b8b3e2c8cac3caf9fecc140398faa57d50170f1102d11d5b72e407593e070704c5f8032ce1ba86465fe1eec1589b47af1135ad631ea9bde990d2f304835fee9b505d549d85f2e5b5e9d508202c64a631b417968c01adba2726e006", "", 0, 1393646884, 1537743641, "caf48507bb96ddb779516057f8cc659fc464a1c02c6c3243fbcf278cd8df4b64"], + ["030000807082c403035e1b565f52ea61c962272fbd2f5dd6511d811d1b402d5ba563c6ed4a041fb99502000000026353ffffffff817a04ab0c3184151c0d7cf72d1cf19344a9411346ccbf7a13beda5c0b4f936100000000080000ac0052ac5265bcf28e8836739f194043b20e86342509988ed7b8f08eda53957e172bc08f10182aa068c4010000000653536a6a5263a3ae7aa3021a2d8000000000000151902f5705000000000700ac5152ac635300000000c5a93c4c010af6d400000000000000000000000000ae921f09bef61c44194e213a8cd56e4828774b9b0fbcd62c35e83e495f9936ffd77e92cf8f392b023132b72c1d966fc779987cef11d7d5a69b6c91d8d84da3061fc987eb55a75eb6598c7930af7b3254f1102138109726fd709b4bca8f67ffc5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009cb4cbbbcf702a73018ba9c9124cb96d36c8fcd9f9d122adbda9592756d4cf78bb498ec1c89b291a48544eb92acd2a3ab529046dd8a154fb8595cb485b5489e74d740e3ff77e919168d2a4debae102a034a15b2ae1fd6ef23c1981bcb0e805a7c4b05b519748e8360204286a9dd3da812cb60ad680cab24b8131b4279b9bf258032f3ce4bfeac6686b96019d4d929f7c7c43e0fa1aaf97fe30e10307f8a6372a05032d272982d8d70a10853cfb7f636f0d7dcfa0390bbc66ef363bf65758c852a70d0a06f21f30e0ef5c6a3b9bbb63995a0280ff1f7522061ab0aaf2659903331fb962f580feb5c3ac1784a6cf0d23f2a26ff82f6573464b63bcbb71100809d355b8ed03160217b92eeeeeb69e8a6a2a1c421b20f317c80db9cf7a24d8e77e305fa7fbff032d9fc64ea061d2b351d4bce411fb2009469f1490359cdd84e34c3042ea9a612f022a0f72e374a293b8d1ae410d32909b3daf2cc7b6d2808c1e7f6e50b17351118a031a16a90d6346838c4c9fd8840dd938f5d9da1e06df35434cfb9899968d010c8b0319238471a220062b9448c9649645d6d6a21972a71ca641b19da00d9688d48fd99532a2d3534fb6c33b542af10c60d7331da431ae5e40bd63589524fbe06e0317d98acf19e417a98ef92f172be9d88ca96a59341dccf4bf750ccc35ca6bbe9e43db3062bbec55174c59966d231f769ef850de60a1cf2c8d09034966ce2df575829140eabac5288dad05fe38623896701e98ad5f007dbad758afc156193260c8209a4b2fde3aa2eebca194e7a8c186cfb584be6f373dbbf3ad1adcd07d73e07d0019feb3627fd82e1afe4e5240750584e7ca38036b34671bd5f940d4ca535ab438d127ce38e78bb3adcc8703a31fdbcad5a5754cf784f267cc8762d70ddac699b363440bb681f496edeacace219ab0d7974860666dbc256db8b6b4772ec4bc83af572dcb755f9161c0afcbbf55e3e13f4a4ec35ee1ba24500818f0f1fffa4833bd3a3c7c3f5dfcfc55a66a812a422d37fa2191de579707c62ae9c414c79f0313c737bdeb215026e90796e71a9030aab1c91128a44839528ba40148e0c530bdac60dab3b4e179275978102fb7b436e877fc4230b5a14619a27dfe11663b7581668ea634bf156c5d1504f741b15447fb7cb420eec8c8ee6668475aed6e83783d67bfa28c1cdd07bf64d35c982fce558f6c60ead3fca48dbd938562051cbd39e0bb9a39ceed849a0be9d26a70e7f0c67215264a9034ab6f4d306fdced454b7b798842a89f5c54e3cadb17e74e18fa983a6aaec002ccaed6b4901c6770e999712f9ba1f9ff7e6f887113675253365cc584418a954bd64f830089f7673f343561fd0610c1581d25fe9dd59e00a0b3f603d3da3f2897a7cb3bbe5ce4d368e159197f100bab1e5bd40324f95f8f35dc2a92f18f42f61a4a74733f4a7e826b34b6aa4722d0560c77205ad7f606e59356ac230601d4ee8bee9a1f845fd9b423c0325b6d58d29ccd7f91e595350c8672228584031bd2e7fda9015a21ecd16a407f50be985644471b2bbd3df2085e490e8e17e3ae439ef085fe86015e4f1ee656688a52ebd3fc358c1038dc2f0733beec6afc5291dca70166766923fec88996b60b185775ed0e3989ebb89c963fb2a0c768fb27ca84c73252761451d14d868bdc7e58370ad8bbaf43b4cdc29243d8ec0214415974557b2f0c952f7af6a7f4a3fe182aa76a53a719961c39e88adc17169bda50904080b62fc0c4e384931f3c623e3d68fc6996272b6c37ac9c8e10ddb469fa24196ed9b3ce771327739ba16cdff98023f588bbbeeed905bd26a956085a49f007c942f147ab1850de90a5eaf7698abe2df1cb27cdaebe1945964d8c22f246a1ecb8e315a03f1d5ca65a5775f4340ca409e8034c98c2ffad79b8ed2e826b98f2d2765a8a3cd3f29021ab7276303f65f0b4de0de410683f9c69b40cedccb136f285dbd925e57a1c7920bb3f656fb070c59c3d66f6da862fa5d60107e132d77c9c3c086a9544640cfb7624bb0d09e2ac991be82be1da8547824ea3656c59e2a61592814927e357a35bb94743209befaaa5c28ab03937b2bf4db1f30707e137a12b9ce43483b654402389967b1e4d50796f2d09b510f36d12b9c81967969d591adf52ebda3d2a18fc3da6ca37484195cefb155c512f2b6d6b6f319ba0f3067194e725b3186e90c1b9e9f53a02c5a6ce30f20360ddee460dbf3fc7b654f987368c09fdcb0046fcdea5e7d8918d4e950d36684ef978717a5e6cd8e77d26b3e451f9717e9916bb14efd38d0264c3c4f60f5d255e150674b03ea8e719a0c4b5d16fad68487f3892f3e210a6115748330765bc63df0d7dddf5443c8e957c433002f76935402bdf89fc0bf78fded8a9dcac3fc7410dec94fa1ab840da6c0d14d0e9a9b303adaa64a5bef505", "655363525252516363", 2, -1885689224, 1537743641, "9158224395880878ac9efdd523e80b2b1c6dcd49067f2f75bc0afc9db5e283ca"], + ["030000807082c4030137531794a4758236d32b01765e3b5614a1490efc5a77129b2bc82d5ab8a2117301000000066a6300525265ffffffff01e470f50000000000030063653e04d4c40000000002277ab60500000000000000000000000015940f6e77a2cfd8bff3641556ec0c3c2ce61f0f948ddaf18ed38a6b53d7b0956ee47063b3710611029f4577c76c51b08bfb55f8163aa3a3b0c1e479861c22510c76cf3ffa43ed2a31a3fac510396928f099b1db4cb676542ea16c72c123178f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005557e124a21ff45f3e8a9586a7077172d91ed934978afd256afde920cb05097bf269a0edfda5d30304e6df9bb7fb5989cb24a9662a7ca991c245273631b2a01410a3f79543223fa3a50de8465fbb21017604c9271aa607c51e5be020de3118532bb3546b3fd5ccf6b7954cde81919b349f5999ff3993da63510ef468ccc3bca103075466784bf5d07801ed1c11e018ae44dae850453154cf5ccfb9173d325426c3030abe0513536bdb1fa815e57f1f5734832ba4529076b9410f5ce81c62982366810b04e3daefcfc56ced3bfd0c9081b43fefe020acdeb989675a731885e1a02466c3b47233211be4a508b4ba309cd85fca13f211f7bdb1b3bbd6b59d3589d40ca1550206b3ae98e9d00b8314020d234a929182ff03ef295505d23d7f0506da77748cb902122cd32050d86cea4ee729e54211c57a4da734c86f00b1f54e5c663f7ca15eb7030b463f68bdce98a2e6094acab324ac0196d5bfadb94337bdad71409e3e8377c203089c052af0f45b7978b7b485b49bc03cdf3ac2bbf30daf787c8c84719ee6cf0d0322e6fe448eab259d785dcc326117a9b4dba97a16f080691126406ea9d31301e348f6cf052b529d9b2e3344d5090815ce868143eab9d42c39b2f412bfdce186449d501bce01b26ddd11ee3eec1cd4b810f559413d48fc9c658d540e4e1513882b0083466dd0d45ec1a985d33aaf48aaa20467dc92cda4c07a6b6fecd908d61def3cc8d95b3854d266eb2568b5397d9e19103909796128c16215c14ce1c4ddac78c0f6bd6b600cde9436649ac5f6520c95161f1e751a45a279960aa41ab5789fae4f9a13592037d47571f1830ffd1580cd2e250d3b9313eb5ba750bcdbfca4f8d4d91f8217dd6652c877a6fee0885c8790ee29effd7d85a68f7efc06abd883e01973c4c3895d2cf99660788d6e3df3fc7bad45e241fb28ee8d370607aa71efc12e95291b98df66ad2277fa4a2f7abbab5493924085879325cc6328e68efcdd6a6ff89462004bf689863345d628cd00828d3f13c85f25c669fd4bf70b8cb0e9dde20d3e013bf53a74690ce83066b0940d940ea90f1bf1094dd6d11f4af213db5a8508fd1a9ab26d2c68c59064f00b7504b0fb847fdc3790a2ab20e3fe5cc448b095225e540ff04b45f70b7df11d521c93affc84b7b51c4cb5055bd181015663f2c250f1aa063afc2157585539009db856c83572b34bf9557af5bfda09810f4750a56075bcb13cdb8e48cf4baeb3414520af5dc7ff9301f1a91fb4b7192a151ba6b13e6dcf459b1e58e71bb0e7a93ca20293995e0129f921c00e3ade17de2d227be0737b64926541b303f8013a762852352f91be78e3502893966350336abf1ed89986065ad35f095ee6235beaefefe90efec1c614958920ebc57a26ad11ecaeb1fee1700ad4bfba62774fbbb846c9766115f15bd07c527ed70cd925350ac0c01500a41d5eded1fd35053c7e549d9249a757e24cb824e2887e703bd34f14713d0102ed5638d505d4bc4729b98844256d860232bc107f91268e5b8cee7a7ed5aadbce8f4a2d59e298c9fbd58525508bb656b49d91abd6be2a039d7e647256f70cf00569a64a22988ba83fe9616113a8633ec5aa887eef380d89d102d30be907509293a9378b80116ac816436df13732f713d60de81afd6275fc34d30593da4012fc99729daabc20e77559639da2d23f1874124f3154a1235b2378c82f3acb2f4b977a5dc23e2dbb321cc57916b2776e3a6abae1a242ee961bf7b99d04386809322d02863bd8acd3a75863eb942bd0b9084ecb9276999dd3133d882fa6f59d23f213ef203c52b055824f10e9897b2a209a5e5cf76e0541bde2c91b9bf72b30a711e2c1bd34762f8b103c875316df402af880d328dd7d31ffabcfe7f3f4e85d3c69f08bb96482f01a45e9b10f9964783434886224ea3e6a7bc4fe0b125455ea25751d8a7e5ea59a448a9e045230843962459afff94480df4ba0542c76cea9fad6e5514d7101823b4797ba63a6471d3773e7b14ca3586c8ddffcde6dca307735c010ed6cebc9a44253aec76f01390b8eb0c7d3f15d097257c0ee0c97b150cc19f675e2d005562128f202a3eb258ccb566319a656ed6076d44500dc0e9faed8fa402a6639963d14e5648cc8c27f697394f9971a027ccc702ff3ff182e1a5879fb727bc79fe7325e346fe1a78d39c0ae61e41bb4f907b39fce77cfe9d3ebaa6af1a089379513b9fa76d814e8ebb02aba9ebcd920d49e81caa5a7ab29424e91edfa07011272b06d3b87a8fe22c1038e086cfda29495a3a76049b403000000000000000000000000c6ec402e1af0f28928c1b281f9a80ddb974e537a0668e2ed20535fd5e0479787fac22445601bb77f172f99e048b9b7aa7825f5545607eddd3f8d044a588cd0bd52753b08d0e5fa44f2fb801e349d9693c68e7edef7c1228b539e3974334bd5f1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003188ec129839069adcd7d0c93bb2f0b2e744ba7a2475b06d9e57c7e11f8423a4742e6bccb371e99316fee46825b65541cec3773ce9c272dd9cda5115459ec472d46bf7c20ece6d6b052fe99f983024570461e3cac3caa3961515509f478b2c276b4118aee50407cf49b1c8f2ed0f805c45974238220c5a1f12a2ad4525f051b7031849668aa0599fac36a207f12d797358b49e7f71118549f7fa16d172ffef03bb0207f9c4c0c9941ecc4442629b0a2c136e58507ab9da526ed5714d6bb6124030a20b06fa63a54dc5f19a5d134fcaafd0bbd015a90c60f599682da8b5bc6d2591fb70aff368c05305333d4f570bb04c3ba8045dcde61d41551045b40b862fc31e62b203163ee31abe7e067a2a6dfa5c7f0022d5321546da7cf4398b9132b6c779cd7d640226bd0dfcca157013d7bc93ab464031b6790d0b058894eed2e688b9d14bc311a402110e367aa1f370bfc9a41891a62d5b0e9e8df62f7dbfc4f64efbb94aed47c53b031c13b665a6e03e5a43ff2f7ef56241897b4e0dd33e0369e87f1609546f91622a0229c39c89f9726350de8d7faa742258312d1b384ee0c5c512fcf99b0a0d31c168e265d9e183eb2eb57102fe6608dfa59af984ab713771a4d9bea6855542113fc336743cb12e1918a7e289890c794ca89e190ca34b5e795e03bbf4d8c4a1b1a1b197ded77a51a192561d5641d92f9d10b3918e6cfd9a9cb355d15413b6263344fa46a83674d865851e6d9823a95e07962f53407f71432b5126cbbb48f709bafb61a90d3bb2ec9c4f9473cc6e200609e350264bc2b5be35a2a0a156c644d768bc44bdcef74c9d38b61254b18ce952bfdcf0cf87b3788a204ba4ab71b1adee80a2700578a172df9957c27cd302dee3cf1bb6e2ac5139f2cd8411d791efd49c5ea5f4c941e507f3a15bef34818cdd4d9deeba4d7e9123d226c06845976fa70a26de4ff501aa57fea7f994731d84c69635b33e922e17c7eed8e34546080ada14305466fe26ba7d1290b954d07c6bdc8ce306cb213abec9d582fca106f6ccefab7ec661beec76e400c21859d49ea0ef7bdd6daffdb4132a53a727fd17f44161ea9899c339e28052d17635dd40194b8f111717ff241b038749e4c18826a6d83c8ab40d904b30aa2fab2405e8dac93e1d3a0824dff166f4f58a7573a2d1a7a99842c312f064b8876b1edcabe6832631b2345447a1b781e395f230ff6f27f20e47aa588488a71d346598900fdd7f2b2f4ea7a174c448bc76064c9d72cc6dc8606c90a452cc0203065e3e9ee6ad81adaa12f0a5d8f7251cc9b0eb79e4e813457a1f4bd7e2cc4ec1c0d781576b06cad402253907d8f93155db3937309fa25f1ec30fce13219fea82cd64dfd0f12ab4bfc6440b8fa5ce2d4e29a1c3912409a8eff91e5af935bb7d143708339ab5246b66db720aebae59b158c6ca5c5f58ff8e6fff895ca8aae1c5854e8fe733050a9470a1e8916dd6fa1584b5fbbda672bd78a548711f4399526f6a0e3567d323ed87605edf504faab20acae7232945728acc39eb728e10db3a1d43991258886e3522cbe110c96cab4f0ed40fa37bc97efb62269701e986d63a9d97cc8ef8d2138ecdda0ccef76b0776e0e35f89c026a7dfd6886d173cbcff0c8f88331d56b5e7294a73b2d81f531f1b9d76354b55b4fda7daf09b578ea8a9b663cb94fb68800b7988ac44bc079fd81fafab847ce14563fcf3c1e9e1db9abf9080875486b2dc6f6c3bfd630bdb130e4a4bce6f8522831b0795271a75ec22ee5730eeeaecda27102970210a84f70700fe74016e8ab8e74877b221be77f2506712570ff0e020249424e1e2225e4f237f5ab1e6e3a5b92d08218f7ce049ccbbb727b63e57ce83128a5db9cf720a33b893b1b3f32da804174d1d37fd9d989186845dbdd122a57bed4b835e668e7e436f114584e1ae44dd47b9cd2ec3deb0e81346f74362b6f2aae52604c834cb403483534fe6939e3d03733d71cef44fe6bedba3bea5919e2e4a0f06c145abd3540c7658a096bb126fb0fa33a4df04c448cb0174d9fa370d1755a877c0d64072a84bccd4de63948ed5c8a69783225769aaf6ff0c39309675ad4e7ef38fc68d9a24e3bff3adfc17437f6b02ccacb8092f03dc58f63229af39610f35134766eea4a0df297cc783a7b7a6da7d9e3c418f997624b6e2792085d4a8ad4045ead8748fe5f7a359f4b8614c5ed19f4790c8c6dfdfeae17248e880623f5ad08209831d35e35ea6525c914b87b27b17ab52edd4bb72fd38630cfac3834083f221f65cd09355b137f641f829ccda0b28507204078f16422b118522d06f2c996ca1aac829ebbc180995d1225c468291a32886e4aa8d3dc9f5e3372a49177bc7a0c135f286b08541fdf72f714278ef99cd6f5c1f3653a7f0b67c35450b4bc7025210b49b73ddc682b017ca4d06", "516a52ac65000051", 0, 2062222163, 0, "33c90c3af17f0a2d300e691538dad8ab43e78f6dbda49e813135195079d5bd30"], + ["e82c3a1b039116ec65a194083e7dd3247db99475625b3cca3be4dd470fbb95e066df8c8a7a0000000009ac006352ac51526552ffffffff4109faf0b57a29fde101c9501ae2a41b6f12376afe5e8e705f45c4b5075e11fd030000000600536a655265ffffffff35bb5e47851634493bd363522b3902622b379aaaeee500523f43fa4c57ec68cb0200000002656a0a7556b20371da01030000000009ac65656aacacac63527a2326010000000003525100850b280300000000065153ac0051530000000002000000000000000057b0db0500000000fe9261e629670cce94307db0b57f1bf11e320dd1227914d364863d9c0fb395825490730b435d4052ee5e525c973adfc6438b4bf9795bc39308efea68f56417e88dcac60536b3db4cb1f66b6cd067864aa359ab7824e42536e0e285a92d9bcb180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041c3e6188a9df61fdeb038ae890a74238d3fbda78cfbfa841305534ca1f564a9d96c6525fc5b91bddedf24dfa681cc27c2a91c391628b03d5ab8fdd50f614f8e724072055769a416b551a20c512b55a74f18baf92f6a3ead29b5674f605cbb909fe3e761776b6f4106e471648ad1d9b4f2b9a4fb152f726b480ed5543fdfba10022999a2b337680e6284bc9d70b31965852c73d1ecd360af1b39e51466bc86185303150754171aee9dded4e542cffadb2a279687fcadd528bfc9e55c270e1b0d6b580b07ad779a1815e90a3b0ae19603395f1a0aaf0f7c157ad9abd7481c0f59ce0b13e656ea80d3bcb04138d896ec3b49553bdd533e6c76876784c53f237f37dc51bb021950004e27b627b5d88ecedc6f160a89537e4fcf33e772e9905511fae50e4571031b7eaa74df9c6e0ced534e65a19075228dcbd05e8e2653ac57af7f070fd2380f0229976da7d38375cfad6bc3fc39265f0d4ceade3fa969d70f88f3b9f85cc128010221fb0b03e971f544b9a37ff3ecfd1ca0b6e06c0609912338e475a180a341d71c03198090be68d560d1824a3131a795098a3af2565ae8a8888d3e20b2aa93bfe7d77dafb709b4e84025606406e775c1c84740dddb11245be63fd7bacba2a83afdeb5ab0308f1ecc4a7844d602c847cbb7e2ab0f217b4301a45b1bbd5059d9d63d6b22a0d27f1146a6cd874f6d28073b826e3cfdd5f498e4f528a05ae04ace92101f3a439ec7ad5e915e1f57cce804e6223fad0cac99264bab297dfccb14905b1c91cdd6b7aec670f410862bfd0c2a632ffd8172a07b67b5199173a4a3c27d1864f4b27ac2b2b45fef541b1e002d85767e940057e06c8a8533a617f5999c0dbbaa6a2feaad56fa1fe6b65f08bc56e643085c79f3a9b2b7a69b5ad43a0ad5191f3e4a030a39541bbf986018d9352820bf57db1fdcbbe7ad2b66cf554ee23343668afb7ab597941f02014988992c797ccbcbbcfb5c752eb3e8cf2fe77f92df89bc457d66e47202002eebc50960cc9c45561bc83b9110b40b6c362f878c6a2de40103a6166ac493e1637a31a1f8bb2319eac0a2c7f0f8747f221c9c9646ff0a953c40f9cc20df7fe835fa8a685083ed610afe7e02457fdfd1982858b7c2abe670a6180e32505efe65c1632cff0fccfb2b734528dbccae8a044bff5555751c567853ab99aa7f8a62358602db1c6472fc0dc42b05140d44132594f934e16a7590bc05c0646ae93900b9bf0159f0cf64c966557f20a921cf396626e579f5b2783482aa74b5ab1b1d5ad54e00032c5a44e3e6d93ac399c409933793d5c29dd3ee478446412551a778f4452be191c3f9e67328a0a36780a63d3dbfb47e0bd57191437de2b72e55a5a7c3f6143b28f214103e9124803e7d4bdad9a625d9bcf4f9bf0d509dc64fcd2e334a0e0438d2c3baac88f90b8f457c91201a97c0c89b03962cfe9e0cde0026447ce82fb1fe8ed8d4db927c334994eea9755a6bd0db21b9e967a5d1b613b659cc51051f68a0dcbec443069da3ff101088f262e83e037b579cac6c8e9a5a86473d4d7816fe05f3114711c3c9bd1ff58251970f451089d06d2dc2571d7f7dbbb93fbfeef0fd8eb94c3259acb1db0eebfe95c86011fca39c1724c790bee115fff2a9df87a84ddc4c7c76cf16f30d102707e6bf33e6ce48659755d2f2d2ed9f27da559ff709d1cecc6ac83f6d3c0a8b252819bbb595c6122613b86de295719239d23d49edf76744be3380f6aed9ffcb7e4500b346b9f418f7ba5ec9a9ce69e0af12b29a15c50afb6ba356b2e5d7b68587832cb350e6169c0811860a8ef3ed3751162fcc13ebc34c53d4c41c619e7b0480b32f0cdf9f6fd3e44096e868358bdf0718c64e3cc3f7dd00aa5b988131396605a1ea743877b0331acb14ebf372f08c88eb66c4c5bcb325bc7c5256bc68210b457c3f4683ffc3e29696957814fcbb72654b6a37ae10eb271ec390a9f41ac0ab7ca8d47b80e692640b0173bca08cfbe0ffe4ebe347b5dc9a8c41505c85a0716d9492c5db3b05b862cbf9e4d467f02eea192706b0e39ef2fcec80f748fe4c71468799b0ce21eb40432401aa6c18be106d95e72e84ba7c580c7733ecc21c389be689b24ecda953f85dc86e313f4beeeb230b86a0111c59f077bdbce97fa820114b104b4f30d6120478dff775edbad1668d93cf0aec3d34b5e4c33c77cd8f08fef0ee9ec7d3de408e6a87cb2f6d52ada1934312b7dfc52de6a44fcb63bc050fe2f36bbff8c062f97c4b066e025ac2f9d2932cbafbe7af4fefdd271d12c61227d55ef62b8c13e94005000000000000000000000000dad1ce45f8b108120953f02070feb207d0f50d0b3b579fd8637c9035ea6582d31e857727b58f68261e0118e6dc74a9b536c12016cee45bc7ff08af8226950ecdf78a29488ba75ccddee62683b1be3827f454f7ecb993ab8c173cf833b2ffeef800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c90fde57ea52fbac06f71e6f2a432b60a96698b3d6d79eadb679125d3bae82baa3614d841477e8077d357b701857d08cbfeb05e70311facdf1a4eaf3be16573bbe4bfcef853152f87dedc605b981fc2ced2bcaf5c73622d9e283f158030e991cccf6e3cc21249de8d3c996027390c0509a111cbde3e1e510a86e62ac782be797020c8a0390d8b2a781aa5bcc847e068ff5219584d7fb71a1e44b785087f5b1985d030ac8804194eda4ee7b84622c3e45101da771be17dc0c38ce21afe0baccafb58d0b0827222ab903924549866168cab6716dfcf98469f688198b85f163a31ed4bf998040d48340b372867a1be8997b04dacd5c121a648663859cabf56943899d490a0310c4ddb05b12ccb9464b4bd1d26cf0b0dd0fd12a88d2794969d2a53c89e0d26b03071d59b976eb39939c261c87cd23370475b8c4cf1454b64af9d8e0d9653157a2022d882367f7a65f0f0ef0d6c4c21eaa39eb5cd59bee5a128302972cd736d4c49d022df3f8c6148017f790e1c3cabda119669d77a21bc3366e3b0f5861b6d071ca1a0201546b4af9a150dc24eebeaa80e048ca9cfab2fdb8b7dd1a9d1f7616a330fb2b8d49dea9dc573c4f02fa59e8a1af7c684b66ecd22b54a519bd85620506510c40ce36ec41e6a6b779fa6c1b790af1579f7b4a605d8c0399cb2c39a0e0d67d804b11baa7ab016bfd61a3b93227b74839338a23b1eab8deb387ebddad59284350674582b640ddeed91033c817b41c29b7173af487ac6e4f8265bef237ade22d5e90ff6179c1e5d4fed87dc7b752f06bd9d59e79ad830c1e33eb0c8933a4d3ccfed839e7e01e0baf5981bca0a6ca56774db0e4d699a5176440dc0f65cdb4f6abcc3676940a96a0ee02b3353850b234a65005972c10f2c528e5b6660d9ac4070f6b1d75d16a3ffec4d3ead68fa91c03c4ba577d56b63ad7251b4d2708da6a04f25476b93e338f9ff9e441a839651fc8d279283425cd4997c5cae5fa2a26f489323c73b606e556f82f5bf0fa68b2925204a7dd8ee29b0ad6fda0649ff9a3149c6bb9db186d3fcef69904cc555ae0fb41f885c67392bbbf7c8b74f5e47f5ff8be1b0b15c1fd124629d0f9755a7655b2ce149b3b657b0fa2644627df1ac39f9ddcc20bd2c67a5431952d94c2e02595f32a4b2cca5a6c24daf917a06407377b9213319984a9b2c3afc90f1508684e157c4be015a50f5ea08de46c605f4c685d6bd7e1dd470467461fa202d7f0b000024e2a315d30cd70d56199d7b8d7bda8efb66aaef17c74c74c177cf6cc486af829ea550863cfabe931eb9df996288f82115eab00772ecb1cab1d7fd042ffcabda10a21d18efb72569756727498576265e596fb95f38677f672d67d824ffd23707ef1455b07a0cdc3bf091cca3613b010d187693308b0b1d38fef4f7be2cdf4a2758a2a7d664e03e4fd7558502e2bf95d3632fe36aff8a777caeac70c695b0b3a04288956ec23d71958dd1d6eb5064ba432b73a1c5cba0acb50fe03c282673ff2362b00702047886c8078844e2708e30fb68be680d578ca5e1b3e79d8531d555a7086bed96e2e69c6892ce2ced1fec917545b511bc05e2b83e5063f1056aeb2b7f6dbc447837c76aa954e79e5cd7c3b813932697938a78b355f3f31f2c0604d46a4a3c4f7fa194d8d07e6e9c0827341f45172ae112eb10843628fb3347898f9712f1ec26b35245bbefa2f6d02a2faa98cb068ce3af06b3f4595e0b15c1e84f70df01a36e4d5ac01827d074144a4487cc24b377ed5cfa9a6c8d14d4b4ec4fbd7f573122a54f3a505e9c50acccc45ae72f8f0d2878ff44fba1736f907dd34a30da3fd69fb0a2fdfd4f427d07826583bc6cc12fb9971cef642f48f464f97d4a3c1e53428dc8fd2b7f07e3581c0720e1fb6d9694b23135c97189154674651ef81b6cd07ab8e94ed0104cae5edef7e8daa1d1d4e3c7c5f01b3c26b81c58c0d53bf566b4a8400b28018f1cbd1c8c79520d7d2f45efc5fadbf90fd6f755d0c7ff11bd8be9026dd5b3ceead75a7cc5eec7f053d71cae4f76608e24dec78287fb74da41405c45b6c76faeb86bc6df5b848749e4f7128993d6a69bd36e6f9fc24f36863a65f1ab67c2122c544f9b88af5387da8d7985c5f3f4ebde2da58da3b91be55233bb405e7e02f5ad2f00b088b1f103bdedcc6fc0284f599236488f7ba0d4cb4376eaf1fef4a8684d46dd0dd7d72137e5ce85d856bfe9f30c3b177e47afd63a1b3b99a2cf4b536d301bc85ff6d68da14d49f1ab218518f56ba6649068f635450637845a2dbd5f71b9e3d2726336f8b7f966b8fca7d5f7b009c03b654bbba19a493021466fed5272ace2010782d4189e5191d1bfa5e1cfef0268599170cc62380c6b0bcbedb4f567f1a3c3dd624043879adceee4ecf105ca863c167889d9d01eb62de0f8cbfa9330ffc9705", "535353", 0, 1923920036, 1537743641, "44f3699542d35bc5ada15d2e5cab2e2bc99a12ba2df9b91835ac2a779c0f7724"], + ["030000807082c40303b62004337ec977727cad8dd05982fbc3136f8bb772e76293fdd911c0a6a86da20200000008ac656563656363acffffffffac8840d379d6a9022dab282d7ba0ee12d3a7fb9501cca7527dc600356e506b5f01000000035151ac730088238d7fe2794e96497c2fa9ec310fc99c14d062d4192a8c9a225b49437742f48de4000000000553526a520041c76bea014d279401000000000552656353ac56403f99000000000127fe230300000000000000000000000084835773cfea8bbda714e3ec896d2453a0af9268937538d83aa51db49aff932cda60774e357fcc9443362ba66722d4b5fdbbabf697cf1a97228717e98ebccb87bb91105a69a36df7cad6d952af896886b99762e5941c0c97ca26d79d3522b31400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000267c332e2852c9535b64c063768e2378a9747bc2e74529616aa4b161a78134ee001c6fc4543176f5ecb5fb107e82491f16de00081d138bd01f942628191745e59f76c2135dbc01da632c081e82ec0b4a7f2ce04941a67a303704e60f4127aa6acd1ed1dd0cffec86a0307c3ca4d148c81ec6f6dde07de7ac20fa2720d17813cf0310c6a94294495db7c18dae792f1a4babf50b1b1be67d59b8cc43cfc51d14af98020a730b1f949db562699ff3e09ee6e3f6da61cafa8a2a9c7a51316ea99d2ff5c60a01573941c45ba750b36e93ac0acb00bbb48914d6e58a5f5a1f7fccde1695226a09e9143da234817810da656100a7a5ee95c1d88ddc6fbd689aae57ed6e321899020602a9cfc17d48f8e24c2ded891bd32f3000004ab3fc826ee3bf16826e7b5dc3020e8c03a797f893ac1ba252b51d4583ea5a711fea1b1cc285af52853954f94d1a02113a9e8aa063773ae285f0698186ae817d631439f1d8193012c7c8e743416b3f0201da8c41502d58d8b178b7446ea947af2e64d982381f2925ca2ba63a836f25d40314ef341ccedb8ca7140427b8402d49f059eefa77e8edebf2909a90d8ee6845aa18994aca9f7e522ec789fa1d0e60828af9db41ae21a099d12512dcd9beeaea3fc5930184bc8bcfaae6c74a5d8ccc684d258a53279c8a5ff1f5b9d9904502ef855ef493853e8ea45d214b418b04192adfc27afd74f2693dcd9bdc41604862a42701cf62b0af1939911c710b3800e43cb88a6f0c3f1ca974e898e98e4b135c4a111bed0a73324fe07dad4eb1b00b5f8bd26aff8331ab0ffd9dc9400d8763d4a39b4154fe1ed76e2d736505e90563df5d7b1c1d07b51b82f0cfc8ec159906405cf83bd2494f45d1ffbdc96da9f1ef7532b43bf837cf74eb6e6f677f45e8792cad0ad2c32c7358be5b44a4cdb63558e9372dd8e32d7d9a23f26447d0ede06f6a54f857bee7fbd751e92088b16fff38abb9db1a6ba63f44804b3da318361f1f69a3b701b737cd18c0dd5607895fd5da270f0fe06fe8fb56f637865bc270de7526aae3c14e3ba3190ff9218c2b4995db2243b39c35a5c43bad104030b6c7151cdd2bc4c8cb428c17317aad4155da5843f5fe7721321816b1877a5fa723d3e95c93b5e80d3e6c7de08049d2df98a41c9a322b90da934aec361eec25ef1e215fa5f781e6ad29d1cf13e036b61b2719f87ae7a84a375101617ae1dd8ab6b0a8ad4f27fe2272d372743c98870e6005c8117ab96880b80b27fc19ee5ddd9ae441f986d199a9a603320750972740eb9b4547a3591ae3a6b05be448fb44e3d85d27b77535ae9fae7b717f8b7a94270e766a38dac02350bf1d4a21cbbc4c60b477d643bdcdf3c24a96f59c089248db1765392d345b4648ebc57d0b05d94ba65731d0702315cab8c679e974cdb7ec03eacbe1fc84388cfc6346b5e57f90f6170153324f2b0aaf80085373a52c485600141ed0efba29453ee4c044994d44dd592549eb3f174563e9d1fbfa58142b9a9a484943c4ee46b2867e73956940a14f79f0d8c2d652a67a62bcae40a335f4a49fa125116bc3b052714bb55dbb9847a2eef8eadd10cfc5119eaf6ee5b31924a5e021feb63488eb61ada72a35c213b93c694efb83919972bc544ae9659513d581c491a4a49bd848aade675039de1ca7e90cc517b71ac2d657d1869510e303ffaa36fda2030595d51e1b82ab22dcd15f7187abaaf7a8015c5c209216a9e809739b82e6c06e04b24fe2995391000d39bb1ecdce811835bb89669b8c57927783c0ab890a9a3df67ad4350648a1406d7d3980f4b224b2d8ee945c90cf9f3e993c0ed0bff1db09a0fd82321f6bd12e87edad7733be8b36a483f242b464d0d9174f113c0b7e1e32fbb27de8f228448175585667cd6d72a1a64e1138ea3778cd7796bdb66ba15ff231608d18f8cbb24dd263ed5deac3a7424fb8b4455873372f6286ee4319a4a7cb58456b0ad254c09d9880bfcfafc85ed8686f4d0b47f1cf0a269006d98427c2aaccb6fe9d0d219d3c8a4c474bb0f9a530d7dad7e974fa939dd32b62197ff71c6ff799f486d40d3003981f5e33e4e70ed23ce40d2e4c988bea40c0bebc0952f1662a01c7b5bd658be4fbaa8657a235e234fb2999479fa6aca25b29b1b66a3f6db954d1f5cb044ae3011ed976c4a78d2514931db2f589f89f986dc638c91717bdaa8a73067b89e69365b4334e34446bb018e6573eedc92ee45efd2080e07caad725b77c08c89df5e02b200461cee7fea16eebd32fba4446e61c7f688b60648ef1c4604b5fe3afa8dd2bddd7b064ca02be85f0dc78dbd8353e0f29b4fc59099596247e79255dc09c378be5a8ab712d11d5a827b098f3646fdfef8bcfaabd6b2dfe9635af3f9c8ca902ec5fd35f82f761de93a3093b610369314f67a9a28e37c5f1296910e559828d08", "65", 2, 1313816017, 1537743641, "917b1f9203bac77029c96e285c14de7fbf8f310cb895e890a2115d8567a7a3d2"], + ["030000807082c403047c8463dbc3a1464f94f591d3c324f0b5c98d152df8500dc1d869c1e918d5e897030000000353ac6aa2062c684b9f2431dca1fd74914af2ece574b43fe8a07f81232507056c4242d0585c7aaa01000000055153006363ffffffff688b00f1894c9d8c51b04a9d87e26c237895a47b1000c12d778dde2ea78440ce0300000000ffffffffc18ca11c288d2bbba2510e523f00c57f244e74c39a18f4affc80d2556fe7196e0200000004526a52ac4e68a0a80490bf170300000000055152520000cf1de9040000000000d5cb8c04000000000500516353654743aa04000000000263ac000000000000000000", "53ac520052", 1, -1889739853, 0, "462cf222dd95ba662e04a53747e8d0d777796865b61e187f16a7f277bda4df04"], + ["6095bb16028be3fccaf776acd66a922dec096dc61ff9980fcf7a7adb8dbd2e99d25fe6e114030000000753acac6a510063ffffffffe729bbba2ac24d48431b9704016db9f19fba2b4240bb38449f1b1ff5108fba9b0000000000ffffffff01e6287e04000000000353ac6384b1468b03950f8f01000000000000000000000000bd042899e82b72207af69d74f921abc84b366af14bc2f61e360ffac311b883e553fc52f359d345413d22947a030e5993b336a96192e8366fcfdbf010ed2b963c7b06a4d68332ea51fe67d338c5fefee0c5bda3c799400b17296615bda78b359200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c85f07260e0e30cb7b99dd43f4983801d907c1b2f7f1c75c2004d375207df96d8fb76730771b189e5f5abcb63a4d0a82d17ebff7ac095ee78633e5bc978efb8a66a3147902f4f215e66f641e7ccd3c55d6dea6c3a0d376a87b28e598415d0035d653ca9780e7fc845fe5050352e44d065d586ed04e152b409f8bb2f80ca58b4202167df02dbf6f062a40c8640010dd8936c16509403faf4e7392735e8d169457c40306b4c5eb47fa9172f9e3610ef7eaa89ade1ac2fdb91b3a4cdb5f46f59fa7bc420b000259be126ccf2bd66fb28c50c0e2b6160bd0ffe79750baefe4e3989564b4ff6a57129a1328c244c69e31564580a641cadf7d2c01bf9141400a3843013de71802175f053d39ac890f412d2f3de06ebaa7a436e0054fa747991e49422f80dd3c860329ba039d66fb902907d0e2aa3f0089bc1fbb55f62d58432f9dd06a8e061b0fb903046a488a93db4e6a4ec72704804e41330ec19fadbbe31dc7172fd72c44bac8d7020a90d20236abca5161b4c3019261509eefb08932b732f6e4c0b8c94a28081d89030651e1995f8899943f7654605a41856a678925c8a572770808e4cfce2245b62047163cd8c4ef829cfcf9585631c03f4323b596f74ae97fe7a8f5fa27187e3200adb26e81d04ab4062a65cad01b9e438312d2fc5924bd2f57dcadadedca325f24cdfb92458c512739a0190433f5f5862c023e6e5c127ecca5629f65b4f66fcc63030163bd811c3a1555546642c1d362edc8350daf08edd2737c6cf88ca77e8898ca577381d2f1b57738b7b86fff04d0910b05aa6edb0f8f12be8965b0822137d41523227c7b0bcde6b641ce2fa0a75f03feb990d27ebc4ebb84b0cfb5eec6634c9d4f7e18a7d414aa37409b95547f1edb8e044219841e6f92b032439f85593a7a2a0c685111066613d8b1c996969fe72078beffaa0db56ee9e340df0ad98b3b8d722fc4711ff6bd620df13bc115a6608166b3a7a2b6edb17bc77d208606886034a70a8452b2c9dc072ad0ece817427955c472614a0c5c0ae3f1d91af4d43340aa38616a567d633359aa29ad6d04cc636c2970492446573532518879747eb01521e35bb5d81a3c5a6a6e62e67c751e552033fe8872e392ca6351a2306dd9894d1a560b06f01c41d51ed389e0bbc1bce12b1c45ac58853f1233df3b4dcb7c27a6a3176289e07203c99cfffbf4f1992ce73101285d984a73cda43876a73512d6bcb42dd25e66ee3fb83ec1d80e65ef5333710addda2163e70ed7dad4c7c29f1dcdd05f927e94b0bfeb445983748d2462e237aec71e9b4d30b3368ecc8c2196b08935d8e700868e998f6824313a472f9f066f66c939caf1b0fb5d579209f464399ad3af3abca8f70252e7e330690dc392bf55c3ff81bec7b9efcff7bf00dede2684fba913121deaee9679d786be2609de1c210e4dd55014c4e2de455ade318371ff7eb4ed20813465416fc8407b12928f3a1dd54edb3b19a47ea8c4075a334488381b55657761978563d5f2447b75d975bb31f265f7fd3f271e787d8c6d44e3a110f413cc70586a20dc6c75c33314cc7530d142728c92564195185f4da4b565b8008e18c8f778012ad54da56704bbcee9ff71aa41b744e6b662e321d55e3b40c35a06fc2279ae55d501d91663896be6ad6c859b10cb3111d6ec3f5ced1d61480b18061163d2f45677f4c108ccde8df90273e5b7d1300788e8fe09d03d725b7f85366983257e49a9682ddd75a50b3a4c681d6731a48a9697d2a9b694e2b945174a1c4485d84250399bedb94ff55af15500bafd16d683a02ddf6f5b0d8ac7cfcac4f1bada762dd99d28328e76f011d9383047e14f058bf1e3d4614e934ec9bb939788d2391db22c95e00a1d8fa3f259e50c02bbdef58745ef175491d1288a21dd87e6a248d035015ce41dc706cd41391c3d387e3a7ea86570994cb222477e8a90e9dc6b4603b7977f3a5e08bee7b3777798a0ad70b715372ce6aa8f6cbd7fb00360740e8a844ee9983601c6fc8b934c07858d63dda601a6b2ddef8473d39f3250f6c1d6490e834817b03834469c9dfddb1fadf82aed925487bf9df9181b9d4e83cd385c247bd0db6a343fdd61ce27b0294593df2a1378b402a29ff445f84eba888da81f401dbec023b4f38d0111ad696747ccadfed0e7a49fb172491af3026ee00e01de5e0d85d7053c78b16cb40fd859e8afb48e397fc0933eb0d4c8d718b082f3d49b95b401c7e94bda98fde5820e06aa6c2d9deb6297d9a1eadce3abfa76cb154961a6a9cd775e9f48ea3bd651538003c1e3bc230000000000000000e324f6020000000022f155c7c9611e9b58776c650180553ad004348e8b7d33aae5c3aa2345046a662259d9d7e9b6874ba50cf7333b72aaa1e2aff1f742211ea4ae8e03dc9c88ad8800562a4037d7337c9c1083977a817dddbfab281c569ffaca8b024efd46e4a374000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009a1ebe9b1c40068b671420b33f2d9afc190d9719e0f6188b92163a855e18d3ea8cb7d3b0d688e69ea691177d0a6e158697718fcfdf98eba0ec9deccd15d7553cc4e8cad91f42fad09cdd36186262806c2de8176954508ce8da432deccad072faa1dd2e816c7f88f97244a7ac3e206e60a3736725e52f50eb1b84332a691a4e3603157c470f3f33463e51beb0f8790d5f277ddb9e14c7734de2590a7733641ee28e021ab8ea07e22126acf621da70f317f7437f6da2c672acdb5330c06206831557910a06e147899efb3341d99811d2bbbc061f6e95b574572aadd233374e42e25b8f2963e990e218633e96af1a31a3964516a973c10aaa086af8d59a96da2a4b0798060329ffd8ff4aabb2d563d23e53c72a1db06d73bb18210aef3460361e2b1bf868f4030403542a0be1f3e247cbe1357af3c413578773ea022fd6dffcfe407153e18458021883a271fa0d5a077f47771215ca898d022f11eb880cd6986e657ae8a1a15eac021e50af34c156d2d5a03b74a5cbaa360facd4549ebd733d97d9607c12be65d4c80313e92e4ced76c911f5b3a3503dd4714cb28c18cf053349c0a9318e4f6c31f70b8880883c7c9f972e99394c926d096598e8d319adea3ed0327bece4805f739562780c35293a19beb4b0a02dabd497260400b94e0fe6d41ebeb8a40d926844c14acae7bc390aada5400d8294e4d5c9d4d9f5a523e8f11ae2b5869bc9fbc50adf493210f7ff9eea1b502a65a2bc187bf136380ae7c83dd02720365dd7aaa910b182929cc0cf1615af3f165e8e7d6cb363da42f56b6a162b125d2d2fea5c9f62fcfb7002197f9ffb9646ffabf763a529efd80b2037ac9a6323b307dcdcf84657fd49900e6eef519f1942947e8602bb9f4280fd4cb6a7d279f012d57b03cd56e3b1f6a119deaecae1beeeabc17f55b9ef800f4f95fd226dada6f1d48f16dee4452daa4cfd0810d4cf7b44b0ffb6171c7c84e526d52b1663317bbf7f9e3a0c7c48df73bb11ca627204afc90f7a6ead177c7dc7aad248735716db7015065ae13b0dd7a75c63d21ca38fb9a88648588a65db15679fb31aea121df4dd1f14a07b443877584a79d4d6d7640bae2af788df88f99e7cc82e7a12d57ba6c1fbd4446985e2658adfcc2d84bdab724dcd8645ba483d9127066c71b03ab09ac03c4c0909bdd3836b569714fb9da6b479e1479116a5269111624263f1309ad0b0956aa3141627efcfae5e9a9c9923898374c001175d0529e98f3b90aa7d8e1eb4ec1e9d9498c63e4e64b27a8a9d046ba5e9451bd09e03defa5efffca67a88966a542ca2b5723612112bbd0402a99b399f405f625889b603bd86a67aacf344c3a53704922869d574def5ccc1535cb06a2d219184b8e4c3f5af0092ba3ca9184eae9b15383373da2b315e0e0367b4c2f6440f78c96ba7eaba110174e1482f10ec1bd08c05e4d95405957b61a179773d096170371b25b6c303635c46bfaac18ad0925002276f26ed632cba3791901b489f8c7e110fa4baf2ce313a472e2f90dcb2dcdf3e81b8e041dba74968fba6e58bba82ac5a20b5db191be6481097b5b2b8fafa3f013c4ca6882703f0fcc05ac4d5ca55903ed078a649618018c790d999cfa75cb67379f4cea73bcb72ea0cccaf1f8f34792eec73b52bba7d73ea7e215005f185822c788efafd801923554bc88b060637b9fcee3fe776a943f40c7586a9554fad074ab26ad90f67b1ab88f8d53b7d6ace4bffce53fffdf42f2934782d26f6fbe8d76b4da61ab4ba288281c54ac715c63562a8199fe2686b04f010129a4445e2a1a3cea3f0c3ae0033ad36d29dbcbd20764ece764ab1b2e9c6a43f540218d97b796da7bbab051316f9df33410a65f0f6535817c0de80851be7cf4af839805a07b89abae9cd82a8098a9ffa3049ca6164be36d7bffa64f96c14066bed4b36538f6083110e265d1510fdbee9f1a1d76c03c081a9a6a49cc21dfc452c3ee49b609f2225cfadadcfdb881496e35c5fbb144e7086657192512eab20300e1332dc1f53ae4e7d61373598cdbb831cf89ab7f679238d57b566b15a418099f684673c7e3eea5df9f74fbefc8bb9536b3a5e49e0699b5e2b25090f98badfb91df171a06167f62c372ba5f15cd59ea374d857f4a2a394bd7ab1253a2c034121afd5eb5caf92627a5f6610e0d7af5310c9051858284f668ba282ff06fb9a286eab910dcee1b30341ad398e4e4f95f1cbf2a0add04893b4502eddffb2118e60a62e520b193d1ea92280141d95d6ed0f4ae8b6f6084d942c87d2b364a51f16ee3a754b3c81020000000000000000000000003874a3516f272ffa7005202bdbdbd4520e77cb2db33b42a4ae2a5de21a2a7775f6eb3ac8dd643f336115f24bdb34f2b5af58cf0c8cba8d5760891274b38086825e811005b01d53466e28fab72752318a3da33b0f896c91eb9115a06f35a5b31300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ad0c7b985c54dd8b6e7c52c9b68ea88d2eb590e23f65b0b0fbd75943d382c57bbcb8e61d4ad2665846d7ab4be875c7c2983cefba77c3440cf6d2dfbbd6bba94d3094cce228f45c6d85354aae237861cfb46065efab0397904346112e4aedea07ce4e75e1fc80069557e30c0da066178c7d887f1f97870ce4c676cb1f671a1fc20212144fe9e0d97dc00436f5bbfe96c0e33fbc01f4198afa563072f013f967831303292acd7f79334481778bf3983739f2442b07cfcdbe6578a67a37e517c7639c950a0823141fcf36021ff127d90ea2e76bbb4a7aaeaa0c7e29cdb2108b8d62a57747f5d9e83abea248ebaf2ed8f361ceec1ec80b06cd0e92db3f68ecd1afd908ec3e030685361d446e8b5bd7f246f0c892499eebf560175be7d5eb4d2388d89a75da06022ee4281b256fd30a1820c114a97ceca0fd1250df7bea9da5df93ac313136a6fc0204143e3d5ffca2ba28075782a55765e617bac1e60951f8333c6f38e16d2b3283030cb1d86176ca43b915e43328694c8c62cf551394f1dd32f41d9016cc22c8c075020f77230b0041727105efe94d5f4dc133ae12e7492e787e642626015327b9cb48efda0ee60b72a2404e9fd1eae5cb7bcc8adec72c46229310ba7667237372c0839791ce98783b1da588adef5051ad4a57c4c0be8da1d4a397c343c6917427d1cf6cb57388886ab9183898d11a724a0cddc4e8857e3aff45387eb5bf7a5b354a4179ce7241a0922751a7b9fc867d8b463d89230a38d44fa60c4207c7cd808efdcb0a610272a9a4f91aeb8bb26051f54e013a1fdd5d20c71006260c8755997fab2f2f105cbcad72b49819de1cfca529eb78da3a60fb59ae90c5fb003db52724736edc79dc9d7ffc8424c95f6be61f308e84d0ae6570160b5e91bf5e632fc2163eee6d271177326db743f30a29d4e1038d0ddc22437b75c90fa99364efb1794b2ae24823ceaf5c674c66200179509e2ac76701b43ae185696fd5fbd4c94dff02f19337743c75c3ecad0b169a8c4bf040508d613baa04a55212122dd207361410017eacd18aaa1a74a01f6f7f3c9f935c927ac0fedee398aeb89a20f31bf24553014e7d0768f270058ced148a9d70b781354ce53e2d40d897b96d4102fb96ca6b6b6722c9040de2a6fe2094d9aae81c0668b5e912d8a44172d067f1227cdbf86d63893560bfe02f6285bdad7c9a16086b4ad113eadcb148587ac942bbded3ba5fbc8ce31719cdca1f04316360293a3d1f2ba0276e638573e3f6dc1200c2c7cce15e9a9702d743c0fd94fcbfac87e1f6702eff444a12773d8e308cc2d192bdf4e72a525adb1be27b540f3fd835073258b1b2615ba7ce6ae75626d58a4e2b44a39963975282cca42baa8079a164bc548e042cbe96e0a2f4abd2ae3a41b82da9e17ee38aff749aec3a8a2fbef2ab40323a4947a74c896cc16e84e7399bc09d315b85ccb4b4e4b6b79431823ba64bc927f83e7f9b389cbd2b5327efa280fd0560a091e0edeb2507ae8fb37e062c12275f4b68358c22b281146c5a6b9212220fc9a208803fe1f3131adda8cd7da840192e3b10c9586be82343977999a1fa1bf87c383c4476e35edc398765d4d5a08e349d51b513bd4247808459baa845823c2ba469e6de6c62f8c38f5598e9b86420e8de980bc4fc9b20770d61912b0591584b7fbe5917960a35e10c41ba536a28ca0d4f8fd250d6e62ad4a17641412e380a432666a91d6603bf65bcada900a8c591caa03077af5d511491090bd7b8640896a278a0703ad1a55c48fec9462a8f60a831658b1c77fdb5f6ea243f44de1be2a354175fbeb8ce8cc72a8ae35fb48feabee10f20c54f91f47ac814fe41788c113d1091e7e7388731a44219359256d33ffcbc54b9bdd5931f1b704756763496c237087166a77b4aa3617112dfa11c39c9efe0874f279bea433e064df89faaf3f2ad08ff319dc6d58ff09be2c2015b2cb17d6b0cddad0e90725507ea2542d83a6e6e65ffa6911527e65a6fecdbc8aa7e825ea6c9a9e6d1f5ca6d48416447b2c709c1df8850580d50b84ccad3c59d61b96a24b5de88e36af593cb8b08326265493c584ab850b9651e4b467a46c832c5a7650958ea2b9e1ca5475ec2103c92c5de3e62853c2f5cd4ee1c060523f5b2790098fe37990cc67341249faa94d7379ccdb2fc3f4fb43a113a00a8d062df62c837c465fcbe71ca250e39938680c841c4a7e761d92d480c6419422e7fcc90476f6a783bd58fa6532b9e28b3b9a0784a89342200ac16a3d9b38333e977429dbbab87a85955bf2139e6ba8f060a7881df7652fbc454fe369ac99dcb4959a5083c985017b905b3fe1fe127c347ee9c3c8dd2ff900561495a12b07f37a0018d900e767db1a9d45611cd14b797a09dea884d15c69ef01a7cb4ae0b74c1e085710c0fc4ceba5589d45c3539852603", "6a536a", 1, -1088114906, 0, "fc969b4d56646f2bd71341d0d762f9f157a751fcb364d881f69da91447d8ae84"], + ["030000807082c40304bec517e92e9fc0f8913e515a3848421800851ee377de405e09141a018294cebe0000000006526a6a000053ffffffffe7ead3cf63d8584d458e7ac762af336d32c04387450f1419b8488a9c551c2cf301000000025265ffffffff615703bdd78c9019e2a4ef38f00366e28283aec5f8035c45915ddd209acd112502000000016aa465bfa68e5b7a1557511e94a6c671534affb041c0d7a192ba9919e43b3c0c417d37684d010000000665655351526a7c5b1a3f044add7805000000000700520065ac52534b8f720200000000075200ac515251534adf64000000000000a5eba40400000000046353ac510000000000000000020000000000000000592da70100000000d4840c615069e147b7d38a2ddda50af05fa567a4259e23c25802526bd444dd429c51abce33f00dbb70dbeb4d891c03e0246aa76abc28cbb11eb935acdf3ceba42ba80918a4045913c05f7fecbeb0db3ee92d08523317f60e876926c4b9323f2d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e72b845f81683bd1071201adc9ab2852a74b8f9570fefe205068056d2d193fa0ced9f12e2efcf2bf81717bee25fb5d76eb420abe13f085e396bbcc3c69cd3da566d3f37204225016f53cbdf579aa2baf599ed7fec69444004a6fe5b2bec6b6da82210846c913094c2df2b6007a455c0f9f2d346a01a783b13c61d91ce36c2ba60215efe33c068537743832d089653c03bef5947f520868082b32c3bb6b8fbf9803032bf3a695e1398c0d8b109e3c245e20ddf538dde6696233cc879d1864eea778400a00036dd0d118e2a98177232f32a2bf6bf751b7fa74c7f5d8fa36517fe2dcb8850c3eefd96b026bc494910e57356795d0ea773e6d140c3d51484005f2d263de1403109ad5037691e7c24f393b348358c7b73e9543bf41ea15482e32abd244aa8211032b41d2716ab159b0c4999ac45d27939835f2b538f37bb35361bd936126df7da9032605b3fc79eaa29800c22972a9d3c8e766bb9ae36f44899c26b5cf204d576ee40322bf73ddd1f93936419d6ce60072dfee7e0a22da98f61054b1b3d782ea6435e30201969ca1b036492fce4f6bd74448270764e229ab609c1832183a6b2fa25b2fe2c678289640bd36a11431c78db16c73e649b5d9951a7e4c0a4ec4948b20e47e252e5f38028a4fdac553041750f96b7358d5d22d3bd0af1260c5682be7c927c2bd6a12c8cbe0b60da341641a3c4774be6f4e1ae554c1999502f5dfbda8f3b05d44616591a60f3e8d852b3e6d0e201238b657dd161eab93827147a48d2c2de832662f9b5c6e134ebd21d49755ff3ad383cbf1f7832c1783122cee7f19428384cf2a826db2216fbc034944d96013e529162aff6f250af5e976ba8410259e5767d1eb0ae06f883de416c42e8a379a5c2402e13530e3e79aba1903648b48c0c487376de15b11fce7265e3dc19855c6e4bc7e027a71583647d5ab2d18e704b9872195edeea4404a09c4a3e962accf1bc37b567d000ffeb538411902fbacabe86f3cac6db0301141c82cbe3413b45f39c33294968dc1e4e6c6b20446a020bd37489185510cf905fcbd25e8a74c48afcd02f1f242461a1d316048acbdc68c8aeaad72acb6fd8385eff25c2cd2a5d7ba80570880f8d2a03462ef9208002ca248913f19bf9e72ef75a331fc3675a2a28726841c16708c98c16b5c26d851e94843a8534e3ff361d657bb0e279081e4dc9ff12319a652b46b6e7c51b03bef18e1970c25faae68b3edca3e159aa80af830cc3839178a678f908e2ccb52074615dceeb12b87669ec7d787d40f0e762a8ea0e6d1c2c2bc0c437931fabfe6496e5a625fd19a0e7034f0d38069638819518eb643acaac0300c66611beb158fb92cc7391ea88a284ecaaa8e85d43710a4e3ceb1226371652d0b6836e2e0a3218fd6a9b6319bb4f4bce38b9ccb042cf1bf7167f18f39115925360e56923dc7ccda3216d65921bf77f1105b6ba974689612d8e4654bc04e1e3a1229481ca19b7bf542643d300737944daf8f0dc590d654d22f49e665d0bb2d45797550f0779d6d788f697f48c3995d69f5d3a8acb3aa3ee807db31f488f6263182ca57ea6283a44c72bbf9dfbe67cd42753f8f8361c25a4d3acdb78ad4c871f5f80ba66de66e7270cba8fdcacbbf122a0c940facd22a28252eece59c5c3077dc7e6587dc9ebd2eaee8d38d6b8921a7ba6c93772bd2ff43e02454ce72b2d3a290dd63222c954f7ffbe3cea90418f7683c74a1f6c3d0f24616771702d6deea02c6dfeb7fc52c1e9506c27de0a055ce92d810337071800bafcc6a48e3249f1ad8747aa735304b5700be748f955bad3b83d03835250a2868d28c47f262d27fc03f772211e1d3c1146b7161ff19435eb2a1685eb16f8caaeab124a9d05f875ea111721739f72b757efa7256194bbdcfccc4f8f339d990286d0accc618c333a0c1927c16eb83c0eaa08c410797561d59e16049cf84099530456c4ca1cd600405a325203d632613dacf5f9eea911a15965837ffefbdd8422516a6706b8e7116fd9f8f214d92e38fd30cc98a7fe60cbc94fa1f6591e6197becfcf05627e46fb967ff5fd67956c2966a86ac28d0ffcaa75781f5e3a9c555fb97f200c2b74f80689fdc4f49df43b5d26adce099c0768078612acc3624645aa42254dcd572da26edfa0b024f411d2c27a1812aa8200d83f1f888ec80acc916ef170de15ce9572aa9ed236dc921df841727dad579597ca370a41b72f5a6f73cf188a35eda0fc6740532326cc58f03faaf41bd5eb1f7963a08873282fdc9d0e420fda1cc21c1b35b5bc14ce70bf4f66900000000000000004f4d7102000000000e051c2def64b99ff35747daa63afd82a674d0bfc3c0b5c0f11f862b3ef1382ba283e0f28be55061fecd6ff56a2a7a6759ea2e28c3ab7f6e32b3eb87821ae63fb08948f6b15bd0aa16a2c0dbdb290b07a024a373f33753df9aa64cdba87ba5ea000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004aec3dbb30210a2b7074c66237808152890d8b44cfe0c3707e4f1c00481cd765c3fca3de5926ca36f77f6f208cf033ef867be2f3eb253914b3926100078b12f119b20a67a0e288dd468e2fa497be9cc793fa27fec17856f44c71cd6fecc38069743a95dac0ce65f257ba753c6bb254a3c28bf951fa3066cd4234e935862879d3020234e8c318f2ea44fc7d3d83470b548294ba27e6544fb5a6bbc163c55cf89fe00216fff9c32203ebed17fea9e7416a6f49ced296d0db3e83bbc518bfa9f1a9f6bc0a0915292843b53595c8ab0a840aad5dfab268d777822179f59ab9f28ce03f0071c50fa1e2d512cc2fa13ad5dbb496c64da5ccdb169e578ffc1febf1500af50302031407521d2ced04a6af5868e97b0a2cb39e3c230f8f65daa6578af39ac208d7ae0306b1a6452e69b89e4e2889bf7334398afec2713abdebc0340f772f170b4127bc021cee57a19d955427531bd3f565b585bd8613ab09c191a1d173d1c8f99db4dd5c030a95ed630d3a04fac8504a347abdc5b46905efb971fe731dfc33b62ad7ea4cba030fc19ce0a619eedd515a6f161b03d731ee69ef7d28b594b9a8c1e949b64da83ebca13d950c36d2a8b29354084a0bed6f6c9e5d6b978693aef4da159a0b484ed47a9e4459a12f125652fdd5fbc087abbdd5ed422f77ec2a54cb96ad9c4fcfcec6512f3463bfcc2e94580c2d5fb2bce137792cb433ce881d2b69e6b46da447602d32fc344af3d175f6dad60eccdec2d0fc2bccd8f3d226b1f986117e79140753f9089490ea82bdaaf289ffe4b3577eb2938cb22160d1091eacae77d0c3a6a2a088a969b590b2946caaf2c3e1d606638ec357e5160daf03673ebd72c5716745cdaa69736dc85b73c7eea4d8311c57101ad5c424e1052a21ea9b284498805c7ca926c108a6c52cf590d5683dd411defdc5bdd58400281a8a94906a7f2dbea0acbbd823a6f396d8d8b21ba00a2a2c48a067ed9919dc6f9b120ce7fdba95a11071e0264764ad373d7d2b1290f90779c44a4bf421cfc64472aee019258769b85b5d0ce48a684d5084d5e17f5bce4bb5e661dcdcd9819a8b5e8eb4e50265efa81514e4170a2eda50d06aadda9a01d4ca336de58b0184764ee6600ed194175df74434880c962244f07daa14ce274a8a3c47ce619a9fd0a7ea9ae49414b575bd660af794bec3e48978acaaa8772525809c635cd39a241c4ac42d7da12f1f78f0c0886f7cada98517d10af2da021cd7e6f46502a84f73aa40857ed076c3976e2e058fbc3fb555ece32624c5437d7c64b03dea678c2c7ae804f7c7af2020f3557d986643facb9856f2b2611bea22dc7ada1876af675257ae3d25f49fdd677de428e66756d4e3ea160d11fe3961fe250eeaef27c6dd80d254717580db328de3fbcf37984b1f6d8c83419bd6648e4b569960051b5e1d20f36b3184dbac753da7a22f1b1ee4657172a4991aa6e3aca8eb59290b36cef7b8c02f5ac43a8b10cbc9c84b99e740b2e34034ffb1534eef3760129a5ebd7a49f967579ccb3d0acc8256ae6c3e43fe5c7fc62344c0566707484a4aba3436d7b561008e87660d31d707331382ea110d3f120153af5db4856fffe967d9352a3ec9dc2329c1ce9a3541fef199f298a40752bbc17fdc092ab251ddec057031b057c977edb45f3af588421ee3d6f87d16af0741005ac37aec33e168608f8ebb90f029f86d7309592c095f39eaf1edab8cb870149ca01b5b11473b0422ef9beb769f58ab23ee96844e0fbbd007b0c6b81a62401cbbd1ac855280deabd6132d6a8bdc89f57fc11ed24c4a7b1878f8661eaa0802968b8fac7ef21c4ce810759dd744139f250c62802de072ed639a05bf94c0b7276bcbd955092a393b43bea67806275e8f9c7c7887c43e4bfe87ddaab140f5e7491cf811b87f9a1342a91febc609118aad280b36c5bf6711736a24ebf824ffae6ae8e9a803f35e87a70faa537cad3da1c57b137501b0928616f9d4b60405b813ff107824b36bdfcc3830078d750ca96874eecb925b34046ecd55ac6f7d9d0748b4b48ff97694130bb26b52d9e94cc7850803d5d633e2234585372b75011ba127907600a2d39300b611e2ba1354377b7cc1fadb2e0df61e13f4a243d0faa566dd17a37202eeb94b65c38af4cc3079fac63f5f0dddc0746b1ffd56d666fa65b51484f21af4820a2a6f018b5fda0ae47fd7b2ff9fcc7732bad3750992458c8d8b3eb42ee11178d6d77a5457cd219e10773d005cffbfa4ff9e62e2809e7fff4c3a4d08b1e8dbd2a845bb67ef82e54688ccafeb44d8de7a0d33dfcd44ccf2a8dff7a8b2c75c79670ff13ed094cbcd0166a3ec5288715b57070052e1edea958ecc09530ede6787cd5aaf8f3afc9b91ce16c5ae2f1b1e92636ebaf15c6bae173c30659f41979dd9409e96e8301f586431b0abdf606ad903", "", 2, -1576750550, 1537743641, "5358980c97ce38a14320ee83dd3d65f2c4488178fd20f0814cf2ed174bb63dbf"], + ["030000807082c40304128191a7654213580c73c88c22fd1f2e8c6389213359929f3fe7d82bf8cb46a00200000004526a5252ffffffff6df8108b9f84d17e71cba507e47631955dd38fbd9ed94b892967c3e65cc9727102000000040063ac529ccdffe998a35b0d32341223ff55722a3182a02aeed8511fdddc22d6907798913d89a6850000000000ffffffff95b3d50dc22735763edd7ba6624d2c2ee4ec107271a7b3f9e18eccd531f14e0b0100000009516a63ac00ac53006affffffff0265b2910100000000006351500200000000035353650000000000000000018f92800000000000000000000000000008583a8f8ce3b8eacb58a4c2aac7c5eeebecab4fca2507fdade81e26bd357bbbf131872401f929176e95d9d7fe79c0a0e928714bfdf32e93f8e41a742ea61c66e179bd36e0385465c3c46f04765f91704ec4a3e253c8643ef7a3c374a1a5476a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004f15d2ed7a71fb108f2f0740fe1ae07cfa0f94fd6aec43805427621775af41b608ab7c0d4bbdf2212739f3b7f9dab2a9a0e67584dba8e84bbf3aa658cdb06dedf16a9c456805bb52e79cb2167d974db9b1d7579694774aa9e706592b03d7ea3d0fab878c628e8ac7ec0c9201a6e10211797bab9adf80e99b2bffdd28c2a94e870325eeefc8934af8341db89ff0e745812f4533162176235be934a28844ef4ffe1d031b92ab4bda0bd5860bda3950bef065aa5719c4935a4d8c4537f91d9a39f07b440a037b6854e3c7f69dd33789f7e21968114bc8e4bcbb7dcf0a6b95647d20f5e0ed7c10f67624e30bf792eef2fa7138c7b397d84d6203feb8cd958d32689a605671031325ca231768f217c8cf8d9bdee6d4f3cbeeef08631def861b33c2abb72647270309b35423e5d561563c669720c92c897587c788c9f94d1e0bfe6c7db90cc710dc032b84ccb5ef852caed28c5665ba5422cb5e57400fd54fd86e8ea29244e2d323f4030a1b0ed9f9fb91343e352e9284acfab34eac8695af634748bf468af617880946032e1abbc5b1bd3ad7ab7a382df6ddd58b975939c4ed2c6da2e655b4f98664571ca56816cb272554045cbdafbfa6211017adefd75faf165d6a0aad75634697a5b4705692ff8964de17d69b761980c5b31a401109f3954b923b5828f941f030182fa4da1c0a548994a1cad1b951a5c9d6618e7675c37581361f31e748d6c01b3538802bbd516cf688e89d7861e9de431e99bfb6a92ab2285c019a889f37c4bc48f5d8fdfa9a1a46b59693a6e7a50bf8609232a539afec74be5373f46d04bbf7dc0e1c6b645814616f545efb2311d45bbff762c7d5643ee7b309f6f0eb327563ef7c6588ac2400cdd37e442751916cb1905258aa7f7952b5f660f81662b1e7b708bcd2728437a683caaf4fc54212495fcf168f86c4728ae34d52e291439c6b0a8f009ab19e2ce7c2c08f118c6ba6cbdce9ebf95e2fc4e5105e426fc5600838069e1600d4145561955e34405a8e5eed0dc8c1703137f3e4eda7289e79724686e4a7d4470ce63b9262a927e4f82b0cd0edd59001fe8de3490200284c508dbd917d19cc8d73a274057e77eeece1441fa41c599244b7680a8e18093e82e7d6f76f9efde5aa9d5e875a560d4dee3c8399cc9a13ab843c64c45e4e9e52e294a916fb7e9fef82506376684eb7b84f05e56f6680ee44b404dde5739a26ed7af2ac555e7f7f2e325e7777dfb1b74c60e3188d644746a4c6bb092799205a861697de06dce16e1a77cc85a8ce0df11e98e9500ca492a68449885ddc79d569dd449fd0113377e48574903336e3f6bed22178306d4b44e545104d056169bfbc8fa4f9a515a40fd843bb9257899d226ec2e7cf965e2486881a453d053fa3808a5a2bdf19b09fd3c9f26725dd331af2434ef30d4b4da7ed2a7f57d3cfda77933874db5c2a3d800532b72e537cc9394b9531e93587708711bc4ca649ca33629caf160efc6c2d5aede43d8a8601c57f44cfd473679f10ae41816fab6f3679cb6969badd002c0150ee15aadc48e4594b3372165d6d597efe0e994975b7e46f37beb00d99c59fd6bf7bbd2737ad8ecb62288bce018399ad91ac70f205a66147f072285dcd8d89714093edd970f37e6b071c936b39b443556b8871d5e450094dea3b58afa7ba5287c394cfb51f2d3c5923dc17e24ce793b17cd1917297a7328caf12f15ea189706bc1ee6de3df15984881481f0fe6c2666f53cfa199da179ac86bca9c1326e6cc2a0b04d7c90a850943fc15db1ba997242f188383c37d5ade7e6a59be42a412d7bc139cbbc93895ec2be502cc037294fdda537de78b61e84562a107d12faddb86940dc768067cced6875ad3dfb4bc8f9fee4f532bd96ae8aee1b13ae4c1db65a0e7c5251c124c908d26a9378c5aa4d4bc8027f2a58c35c88d4e43d7123257f1b7da46fff298e9f37fa202c7ad99614053ac41263314f6360b1171187fb807c9fd1c04c92ab464e8e031a9f7df4461e32fd7beeb8d9e6df93fdfd5d04a7bf52957c7cbb47ace798edcc6e1ed893fea2e5b322bee5b36eab108b35dc5bf05be80ebbb281de3420b60807020a0e4980471780e3c518a408680826f5e79f1b3ed0e9feb85d146527938f4115d11de98896625aca0cd8a180bcc8b63ec3e9d09c8b2a41dc69b7ba9120ca75ff5c1a45c91c91416f3e43268ed3702410f28eac27272e915babf004a9d44c715e881d9b76156bcc26c72ee2500cd01305e143783aefbd38d838abaa5f2ba6a09b1ad2c293345cf4b28b709c4fccf83bc824b76088e6d12415822f040cf1c3fe7ebaff9c9642e4800d2f57ef700c402fbe79c3af8f461fe8a3b215ee994b1fb8f0f79ac66316ee2c739c01fd05444f76fdf115e620c46a44dcbcf5700070ff80f15be329e92bf83507d9181ba4605", "53", 1, -1520838297, 1537743641, "63a8581730e981fd4e1143d1e46a6ef85cf729a33c086a8264823bafafe152d3"], + ["2b56864504c04a48b38211aab00cc198f99be44965cbd1594321bbac610c621887a65cb84c000000000163ffffffff6a1060eaa807eb73d6df7a86724b13b16b8c23dad84cb3188c32d9fedfcc3ce00200000009ac6563656a510065acbb4cf7ac20ab19f78ecef3837daf7a92386982d413779b609fcc296f5bcfb53c86c08c5d00000000096a63655163acac6300ffffffff676cbaee7d33ffb5f72c82fdefa42fd9dcbabedbd3c44d789696fe2de2145bef02000000004a153d30012ffd2b05000000000952ac53006a5363ac6adc8037a3010000000000000000562a44000000000055aad333d17781a91ef369bfae97614ca4b7c542562c79b793da9953e3f1d1cc59da6e7b9193a87b3f07786993f451471db5a49755a3a9f98a556f5c4b9787347814e720fe646128e5a17fb6b50a1ce6248231306d4026feb1a301c16c601a0d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d5062715c5ffce1ec42ebf81528c5723fe2d1330f31ff2f640575dc514c5c6f796f700f49cb54e73039144a47a8fa940366fba1dc3b610588afdc65ddac2d86841d5747607a666130621b8227949112d47bff166002641fc58bac91b2156838e0d69041be1fc705371c994caee6cf9357d28abbe89152bc4241cb0f135b309702259a1ca5111c42309f127604346a682b67ab84b322652b87c2e571f93ac9229d032651f170fae581d535c44b793fbd53e14d06f66c358ac1fcffd90ad7f746b4820b08a10c16889347ec8f5f5274f18cca3ad273f7701e511d3759b659db9312e0e2c13ce0875a29d83bc05a18fe212bd6c0b86a4d2506ba71c35f8bcaae10444b6c021ccd926f452b10709b9e76675e4d8070472328a4c3ea3e1f72788a90328c2f11022d422a529c3c77365a10422b1a5f86832737fb9c0bf3e7695162e9ce2be9dd60021989a732ff22957b4bc08a9e895a600e014c2678456ad724bc60901f6dff771b03010d7f2cdfe00a3d3680a950f68bd4449efe90766afae7ae4e07167ede9a64bb0220219c12ff75da3cb5e51f28379ab904fb51f078b27fd1525670d8a593fb91e77355f51c14e485e4e5fc9da3f1569d65120300cbc1fa47ad2dbee48356a473941c56fb4dbc929b08989df2ef4ce5d85e0731df4e47180ad3735c66b30387f65266f91f982c33d052d05e9b70b475258c53f9d06c3784adcdeb3a3aa2c1a926c19ba25731a24c7e220d018241ee9fd7963f1f4a4cad231da4ad43d23de19dc299fabc71f86d2d385e1eb73f1d6ad9faf2c89739c83e6aa8d660e486a632a286a5adb63b63c54a5a4ff9c63397d11aaaf32ca265c4d373fe1fa7cc8ad49e67b2948b4ec6c398756d556fd23d24c6082134d8d7cc994fef6282d3f0ab04bffbb85344feb6f29ae0ab666d987ae5c9c99ed181cb79d9fd285c6a66bb3fdf2df48fd98ed15cfb07103349f56592bc53e90bce096b8cc28f81d1cd610ed0fb55c1f5a18074a14019f3c02d44e0ff833d4f5cd56437bab336a5465c34e9ca35f3ffb6e3ecd8c9cd6740261fbbb53b8db3678e72c261db6f34d16b9ea418cf547f02b1d6ee3339796fa573234434ed8d95c5a88e449c58b03f940647c7accd8c6982d41a1cae6515f145c494f4786d2935e32f376f79126a1c51f2c33b1667335c73231fe401ac9d725b00ca7c19fc1d969e5f1e92793e2f198323bc32ff06a9662472c6422aab8b6c19d55a2116bca579771c59f8cdc01f10639a9175f212b6db864814924933966d7d77c45acb7910348c6ab2e650c144a1cbbdec1432c55213121370907014c0651e2061bd16c00c85e7b0efcd70b0d42666314431b1a0e3a73b37ed6bdd32112821a2452ee1ad6be58cf4682f69095804c8e109897eba551ca8b1127caf40b944ef69b08461b20105a6006da08a6f3d783deda9f0fe3bbf7c64991eaa8e7a77a5dfd32358a06431ecff0a498b55abd4cb65fd974047ced9ee529bca2a9b8b7fcfe4330e5a864af3a80632d7b3f442cd5c5362ed7038c88a9e2bc784530b930d2bb5f06c2249f46460a9156c57cc00098891986851b7bf136d218b6eb1c72a48c1aa98c09bc9a1008aae06f893794ca7b684671fd3ce447dd82da16defb3f352f783eb8358e8d7b08300a44d15d434620201cb7bfb7e90bedb0a7191050bb4e456dff1fac667466ff7aeb55a071919b466478f902299bb51193b0631719bed369e83f73d7fd3d63634aedb72dd4ccd207f241c0a9773a2641b8e5086c861800bf3162c7cc4b111f2ff359ff76f97b3dbf3765865e466715f3389a161a7705df54c22011144152c413f2b6c9be314bf85e64f6176ce0a496e73c914a7f20ca7f460d7f654919b1eb34b98a1eb56ad73ea576821f3c79172cde18b0ea61eace680e3ffb240cad6b1e63ee9d7da954137fe4cade5fe7cbee1cb5b2e8f6d018ab224571d21ebefaae9fbd167d9e8477be8edad4ae22820303dbac06de359d0cd2864efcc770be567936b2d59333fa7a86e714bdb0c10564a3e3c39b0b7dc9f13968216c12a4e513d859b0568f1e0b2b5e7718efb98d0f3857ac12d20a27e13554881f64e8ec762196324f7b39bb3c79d226d06dad004f24ee081c723ed1b79e0efb56dbbb71eb6e7e312643f65c79d6305cab57b9a200cda092e88e592b864c809c4978e2bbd2b180a57fbc8f107ff16725a76b1fd7eb025b50179b1bb7313afa0ee6b6f52c5f8b31c715d904239ca3031c85d16613c9524bdbcdc0d69ae9dff6f0d7aa7f2d60b2a85190a7b764ca50132571f643dd5c7ead26d39f524aa65b114e6cb6d4117764c8e106f1756623e3107d69958077425a9dab1dc72189714184b659a7b2d7eb314e4e0e7423db4995dd8b2bbfcd2eae1f0cdda6b798e7e59f6ae9a31d879c8d100340ad937434b3603", "5352ac516a", 3, 1584858262, 1537743641, "8d8698185987c70c85e8763288bc8c30e82fbeb112f52844f4490ae379eca38e"], + ["030000807082c403027eb28d3c058855bc67d274006ebf0dd23b7df504fb7b7440668dc7d2b5f531d70000000004ac655200e981bf82e31c67ce88aaa1439077ea24cec655e56a75db8b0da8af695a7baa9b7e7f4cc303000000036a6a6af222ff43038ab93b040000000008006a5365ac5353ac120215050000000006516a0000ac00286a50030000000004ac6a526500000000870d080401000000000000000015af0503000000004785289c23c0159884229298c2721b454ea3d7ad708da9944e3f308b8e24021f603e8066ce1c395a18cd6e415364ce85e7f86bf23d4bb9f40fb2d0ad01a4757d98f1e9702def2eae12cd67372c71cf238541335853acb22d8d83b8a8e1ddb3380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000053fdeac4c3228caa0aa2d97ae7aa21484c4c9c5f44113de9d9673b816201b06084b650909bafa8753b5df2b082979155e2c1100b87aec13d372925b60a67bd029149c327f8de4c5cc245e8dce28d08a1c38fe252b8047f19b8d3b7e0c3af7fca4cb0addbc2d9a97d5246b742f148e504ca1635ad27ad08c1dfe9a33c872a21a8030a374f590e064fdc6b2d406b269f72c0bc496412ae2c9ef9caab1bbac935285a020f1859a2e9986c06d34d213dc35488e030e1ee7d84e28cf1dc4874189ae985350a01172f653c32c738ff93abfdccacc12de23debe99e73721d52421c686fe6f1a7a983f086e97d22b1c0e03a93a1334083eb9b4bb581c0fb545e2c6a5a98991b0c03289736d6516e282dd5ddaf3b8d23cbfb01ce617bac93ce53316b3fddb1b61bcc022e81eaf2117ddef3625ed47a7621aa42da95a36c91f4240be50971a8f7b97ac8032fc0f6a4e2836b6d1b472a5bfdfafd5cb588ffb2fa26afda9253f4e65c7a859c03091ad5b15c546d70cddea9e443608b382010737556eaa3674b645b085e8b18790312b73b195725c1abd75d1a5195d8c39f46baa61042df85ac5c1acc084abf8bbe243f77163518ff4c69ca1bb0751d7473fff03443076fbcf156f36aa77e1a505b2f2b5d6e1b8c35f25a168a9234e5203e36860aeb4b982b941bec5322294eb6a3151ffcf12d8e132f15fcac1613f822925ac3da7fa1c07c8b735b1323ffbe71556e0ac73a67089233a63a84b4fd3c4a5a1bb2e35571c4a641d251d31a6ffd3188b275f88bce0391d17038486af43e4e809087bf916d50c483d2c162990a9b3308ae31353d98310b9416b5ea9d95cbd7623ff17c7ada0c7534cea7d181aa45fcc3e86e997932b6858e2458560fbcd34b07d2ebe7c288f9755166b159baced86cf7e0e5b85f7100e997b6c4458947bb41db3c621a05a13f0be6eadba1b471763989f03c08a87a60a94b12db4c5c39137865647a7be95d7c61f02099f7774dc7c6de49885c1bd6fc83f9d854440e5ed466ba985cfcd32a49b54e5acbbfeba1141dc9477e33c963d733cfa64699253d4d36de94cc885a5bbb9e294da18be349514ea70c665090bf2689b19184fab65d6b4e789cbe3d03e3646ce27a24af8ecd24e316051780077c53ff1ab4702aef9cd400ba041bd3d46c0a10d2be816295e62fed2483e0dbc0bcf5f000bcb62a79614aa3e3d145ce2103c5e4b6a3c1c650482005650d6c3edc3ff8ee684b7d350bd46fa120490080c6906950d7e903957d6ead971dfe3b784d79b06b9d13f5c1a62a8e2a775b82e19a4477aefbb7e1fb6b9769816f22f287d7f5937362cf77cfb9755efd197e079f4b741a7e1aac4f9cb1e1d6b865901de5bbcb530776d80376afc079b8a652fa93e6eb8963e10d6c63977e087477338b5336b6662f17502eb0f6a96aa05b382cf1da20d5e76a1ffe16dbd09aadc79b1a4e60df3d50d0b0cf1c6a0e83be0f5353d7d1c57eeed076e4d7d3752f69fe37bd0fdcbb748524f8ab90bed22770768eff7650ce7d4ee2c42c3542818c6c17c5bccd51a2e29c94bdf1b530e0d222c57f134c5fc2f27963d087df6c46c8f897685c01e2d06ccbf1d7457e9b89e7a8e4f8e384d9d53468f84a6a245fa8181b3cfd302b585ac7fbe5762e17e73f6db9896299072ce99721d551422f53eb5b35dbbc0f611e8dc1c12a17bccd6e223dac9271195bddfd4d6ec0c96584eb7d0feb831f0c8e936b92943a71e47795c099dd002bf3b5784b521f085d951c83c0242695ebed3f68ca857712f8dad1cfe0edc51b241306ceaaa84a434e7269d2f9933a77bf69078887b4c66633d1e6794c248c43a091023d92d4ee6f5e803e42ace4052584feb7e98be04944683cf40fbd55030663e51b0865172e17eb6d3486bbbd8161d41f5346d51b163aaa064b06a88ea8d2600152c8a20f1199e5903cd8c1d60afec0a20592e2bfc43cefdad511a313ba7fb4aff2c88bdf184c4f15e96e8c5fa0cb590f216c9b4d925bf40b3103040992c80855d71f3fb5b28bd315e7524c1451646a487b4e52807f310b2360205840841e8a0176395e08f898c74c44a9c79121c98122c7e0c8b930bf3ae50228592356220a6c5f0f66368bb7d6230a8136f273f8bb41ddcf0bf86af59e091d10a2fea158ae6ea2dca4eb2b605b3ce0fd5db5bdf1e7032f60e68fed531db53e5260e8ca67ed83f88599945aa30ad079e30a14f94b55dae18d6db0aa3fb9192c0d30ee04897976cec604a78ea6ef0e4712acc740a15dc2bece4403746ea0255e90c690ba60453588b5cd57e658d3daa3a92c5f0fddb50f4c252cff05b6d896cb12d076cddd36c1bdd5199e07563fd97d2fddbd5ec0eb81d1419e328802e9eb6130ed93b360e5c5258f0f21f534b860b7b2ba076bd6d1eee389f611425dc63cf3cecc5e61a54603", "636a655163", 1, -876018883, 0, "bd288970466e141c059a9674ca794e53cdaddf8752c5493e54c48c376bb01fd2"], + ["030000807082c403010e01ad241abbd99d89f0e7d714b1519a1c8f7355f7abe4c71f4065da1a11f64001000000036351acffffffff0362bd24030000000000524b91030000000004515353529dbe530500000000000ddd5bf50000000001990cc60500000000000000000000000099dc0bf0642e2eba96890f74fc411eaf33e95ae4205c501ac0116dbde44af8630703b67b6677c640eb7f2291904878614d737ef15b8684ec29581e0f67bc79edc64c63fcfc5e9e13d10233c98bff60e0afa13a70f92dded9c6d401746a5757220000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000aca20c090062d3e67e8d6e5d76727ee04dc2437727f3841b8c0e78b28e48d6776f152265751dc84ae1d679d1279fe3e7284be00d0884dde294d1f7fafc96ab989e4fb406b95097a93fd1c65bac193029e5131c7e39481a681822c0aed909629abd1362b2b49f308da4ad02ecaca2b826238ff594d2580307d3e111d267de56022c19cc2f475f5cb9580f09963e50b4f76cd60df071a88641a451aef6547619b3032d56770c0d509f48bbf525e2e790a8410695e10575ec29b434a8be4caa1938fa0b02d815ebd9b05c5ffc3e91d2b9b51fadf4688c6380dc6a8fc793baf931404b6aa689d7153d85a96b0bbd7e10c2d871e74e4470c1942a9eb63a9abde1952cb9a3030cf7bf7cd366d12792265e7ffab2aa222b822e105e3d185f74701deaaa3a2393021c4839b8e86bee540c80d5e6a4c4945d97a74cfb28ce826383ce2a08ff79861102228627aba0065e62f25972c9af5dcc9847c9998ae4a058b2c5aedeed5174714803094c409c96c2419568375e90e8caaade808edf77e100373e1b009e3a1df94ae50314772c4fcaa179a9ae171dfa4a871fa81ff6321552ba5447f9c3b195fd19fa9f91fd8a504987ceeadc89f24d9e258e317995951c90e7450e2f38074a7ba3ddedea25b917cff05d4dab24d0ac12c2b3133886c4f24f3e304372dce435c484f5a4cb34fd5325a1f8c5b9383b202fa98fc44ab8f43a18f3deccc82846fed7337fe9daa00047b234108e8f49cc1cd6305aa9ca1459734ece782e8f020761d37230fc1423c54614d4133e721cc320f3cde515bb92f018869f177848680bc1e9b781b33b5b1f040501a03305ca558dc8ac675fadbb1f9925e0d97731ab2a5a7607562c6efb1f257a2cd504d51c5ec69fb819fcae0b706918d94c8b5f8520ff71cac4f73cd099b99ea815e64647bfeb1f88a5c2c2e4c63b651c0b121398a6feb579ca3ae61be8c507babc1f5af2c2fd162f8b79a9e946281b41747558efbeb087cc533db2594f6b390e0bff8189b283997066a02bbfcc64903566d1d7c659d200830356dc0eb279476487a127673a39691b99f7dba766c31b7b74fd41fd4e998648463ca84073393ca7aa737a6066ea30fb1adfed6fd69527cd43cefbdbd4fb4bae863428366d9cc5de534ca3e7f1fa790b54cd96084b4e515aeefc50aa3ffa90f0aadcd728e35c335e07289c12539e62ef385ae87a48a8a39ab9578ca615cb562a3fa3ed7f288d967ba1a5755010f1f1dbd9570623b3bc7a8f816f553c4398b3a2c205b0d2bb0802aa5307082a222fff2a8a568b92b16da0b4ce376cd8bf116172448751110b3bc900fd77b528eec6160b90bc508bc0f358c70b8dbde6656a24692e58a8b1a2cfb14c9a6d25cecf031e81422cc620990d080fce5b2e964ca91a80b57d5a57f3e511e00931779259b10f9a39393e826f22452c6b214dfc480f6101240c543eba5e0be6eefff9767faf7f6d6a6fcf861762b718a630bc1c2a413ddf8aab66c538bf21aa1e040ae704a61b9f247e6dece2123e00aeb57d6c0b8e1359db7c3c2c445adc0c16cdd1f5ba20c1a53e710b103da7e157dc3509af6db022ecc9130da2e08aa1bca3bc3eeccc8d5b7946fd283f147525baeb92f9ed28ed56d5bfcee56dc24ba432db35c5dc1f59f5fb70dfbc2c654d9a192de543c577d649f52b944d6b937f092ed3ab20ca31424c9112c0e42a7a6fedcf2261196d74c972ab9550080320263d753e84e309365232b13b6d2bafffade8425af1ff0b168a6aafdc40d7a15645de8b2342679c9b723ba1fd44f3332697dcd47a570906be322e980a740e080163b93e465bdd9df1631688eb103d5f59dfd8eda26d315d3de6c1bafbd20a1e09ba22570f754695b1ae55c204e74524ce6041de4ba3ec461da7bfb70c027d7116c53191466051046cbda277199ac563a1c28753c0fc6f449151e0613a9855658ee775228e395fa325f0491f30e1b1877307ebdee9e75dff8b64aec58ee2129012a9f71893a4d870d38328d4787849673369b8d5c0201b925f47721596539c6e95fb368fed691394498e985898e7b87e1fd2774f8cc82e365d78b01391d5f6f756b66bdd5bccf03e177a745a0b4423ce909ef0ecf75b3d3af79ac2b00c2afa18a7f3b963d362f46d17bf92c8dc997c88d71377db6b7c3d8d11136c4a0748eaf7f1d96c29c9246ab526dea84a91ee89877d38b61c1dad9073172e31dbff593b653eef3abf407d93507f16ac22b9c4806345d6d73e2f4fc48dd80ff46366f372de0d6efd358fbb8300adcdab84cbcaad807cdd3db2af67c2f329b4b556b5799f42e92c554e5264b8a0e94e585afdb7eba41767274efafb3d35d3e39dcdfc91376dc6bbe7e6a5cf97e3bfd7f39f69876d125ced9d7609a9d7d521753a9879d990f5c1d9e268ac84fc7a4bfa96a71a4bc200", "63006a63536351", 0, 704890566, 0, "9cdc9ba9fd78f23f3420953f5403ea81d7ab4f1c4bd69ca136b9b4e4d4eff5cb"], + ["030000807082c403028c64b32847bee3d494a54ab09d77113cdcf2cc9bcfd3b8511aaee3c9496b737501000000076a6352516a00acffffffff0900114f53721a990f64bc1eeebd1314046f6409d8864ab1b8f3c5a1153b77f40300000003ac5163ab68a9ba01c292790200000000056351530052000000000000000000", "6563", 0, -395493780, 0, "7eb983804ac7f9d20b5838f566041c4a65e6ae474129e7702400a0efce4f4c48"], + ["0409a15801d67428c04084d95b46d1c9972de21f5fea84cd619442a587ccc79d381d603fe803000000016ab0b2e5b702664c62030000000002005391f2f70300000000026563f3ce981900", "63535165ac5151", 0, -430015201, 0, "471d8ed0b52592757d38ace04900a6dc0945d32194e43c904ee485ae0643769d"], + ["2acead59043cbc85212cdd60306aaa5f9503b10002b68a762b4d2fde0ea5b5d7e5ef16e60f0100000000ffffffff96d1d5a82e09942a50ee1bf9ea4c62e80fda035e091018f7d214e9fa42012fb503000000066a6a6a656a523f3622935724da3e91500ff766e1c7c8d972663e48f0cb3673e6445ca52461b83c95194e0100000001acffffffff25b97af44b14095b60eb89946521f1b8313a078eb6e9d4eda195828382ac46120300000003ac536a2237449701364680010000000002516af5729ab803143562030000000000000000000000008e7e3758f2e966fb777cb9e30e2657e0ffbb669cd1f69cea0bf938e00515db523339e10496665e6a79a563d2a8f4057ee7ff2423ebb664482241ca6257339858cd0a324cd66e47f697882bfce73af0a6fd86ef1d09e2664d0ba9d0bd94c032f7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008bce670bc46d35b750183398804e0bdec16bb2bdbb4e54e741b01d1fd721dec58e437ad18a9bbafb300933640147926032a6036d42500a65ecf12a3039054af251a5447bde4b2e8c521c3731849ed68842a7a2d73477882461daea75b86b8f11fa4e6e420477b61356afab3ea35fbf6236d97fa06afd43e0d441dd74acabfd9802118cbb07d62b9c2fe85360f0f633bc483ff9cb41aefb0338528fbea5294f921d03213a373d9544011a3f44d72542d7d98c9e18d078a89b902cf321514fc0190f670a0000390c48dc73b873e5a6425b44177c6cae4dfa4fc69148694ca366073c95ff867d540704352eee3f677b419505a2893870dcb47001369e7b83a812e5a4a052022a68abda310dba7abd422971b23798876ac8f691b676d97c9e21763377ee69e3031eff28b479d4fc564bea6de3de78ee1ddd639b184fd4d0a138a8762071a44d5e02057efe477eb8efd9ee113c683a0a391eda100bc9723877e18716a17425ae5e430326219929a137b5f8f0ec97de529045eeff60a309b84fa1fe6733600d2940877c031e15aa2cf30e64aad9e014ccb7b6c1f4e79e3cf2bb7f14d453aa7aa294e4daf5978893b7a7947520824c057cf22d88c5e382715fc8d27f58b4b56cd34fbd8cf893276f3c6583ff9b02623ee9070f1d2aeb95a91631a341baf1a9cdcaa24d9ab2ce09a7e2830e995c03eadca1cb47b0f1acf9d7da1383d6e8c0592074f90957197ff749882199e4586a94626a71dab75453daae4ba4ea65c29920f005421db81c1d4451f99f2f7747e8e4df8d644ff7790607011d4b5442ea583c4b961e4378e6d441b4c4776a53cac6c5a2a3fc225f554442b6d07cbed7967260cb7d5e5b62eb7e52a8143b829b363629b77ed64fb7846cea530aed5d335336fe1d72354be63f584fa0e7ce050852d740738f64bb0306cf3e017dfc4aad4bd8223cabdf14d012a1574889fb5accedd89eacb0e44a9b9e8e87033383c9649737707b19e201188d93735fbc2c559d99b88c5adbd6af4072640741cc856a1d9e51b2a6e7bda724cfae88f84c352dc442dd1fe9d317fe24efaef71500ee51553fc4b9aba756c42c9b4df9cdc733f8c12b469db8be2238a218d47bbc75250ff121440b3809847603f746253b6d19a644ef2e6bcf64b19872d823d678d88c6d1894487c162e177d27cf9ba685768e46e9db5365c092991c88c34e2a5e77b45c09cb6371a81d9725c480cd186d7d4e6500c36bb500f028db38ccf36100c682f21d264a258f5abf5e7eecd3f11212788b2b1e0b02a24e9f275fed3edf3c02a5880f75d8e4ec90d8dec9dd48513ae62562cb18bba19f561faf8f05c591e4e172e46230efd70d163a0500d73d775549a5b2df82ba50ef8091b803cb0eaf4dc31697f6856f8125389875fe8edceb6a35dcb2099d360b6a1a2d66276b2cf013095198565ea2b9879fb084b2d7f942548dd01edc91467904ecdf99cb16159ab1567d5a3a6a31d919f321ece78c3ff7d2dd2d75e9d16808eb7b4ed7c3baf174c1a6c22eeadfb6648126af9bf6ddadfe88aac7f4bd90b2441b3b97abf5fe3c22ec2a5501702a8cb555c5d9c40d1ebdb61c02051c7f654f5629c485bcbdec4d04a0899d135023f9d3a8c69d7f53c1e426803a409895c120359a6225df53c03416bb28826d807d3eddb723e189c3b16bfebe2b5b49ca99471203c5cdf71a04a1ea7fecea9b9af38587de38de00b2953a3cd315f86bdb1e43ba8fe5e2ec5914f0054702dd39f520d8e1d9c8b37e037c1fc68bae01ea2d493ef2cf50706f0b2a87622fcd54ed3963f2c9a88680ccb14e84e83d22d1b511625980a124dcf683af9ae05fecf8cc692e6c449d231071b97d2440aabf136b056ae3e7cdbf269ce545cf3697901d7367035062ed06242d61d00a020a9a679191dae0fab9a12d6dbe59a077c7cb27d88cd0a9e893e371e07794fc41f257906b8a972ca59804e6d63bd674a227711e101e5c783911c01d54e940c396da0eaf76914f9690a87e6a2936ec1886e6501e0e5b699bf19b4ec8d50c5e578da3ac1c55791737d39d0b228435641507894917ffc70a6a505d8be6bc15f7771b3e85ada30c2af8a8e25baa21d0350dcbad407349af3191e112f1732a6c6329c24106c284d7f0986fcd92ba5ec737f00ced044227763ef0e03cbdde233c151d071c4d31b8bf93d1b6a14107c408008b5d34e2ee39796952aa40c475f072efefb36707a5f5cef3f022646269cfd552d888647cb3e7ae5cfac65fe86206e131040c2526b3232442c0fe24fc09283e9083e487206b0100000000000000000000000006c334e1c9a98cba228a9e4898e1c5d6b22323591c517e0eb40217df0297be0d3d08546d47361b6b4f4c125dfb105f69348b6f2b9137d73ba0e5353fe3f2b67fea34b2842d8acb38030e5094242e39986ace7215d8773c1b15062d25212d9d9b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d85690158661c768a90c33f4eb3fcb9e35c8a4c001a5405c9d9198e93653b2322f67636e7cc56e2cdaa370eed1ff7354e3eb35f6391c27c704c6022ab6100cb8edafebf8b5aa5290181d22fac0664556ade6bbe6975977ae4d410b4455c7f4b9ff54bc48183fd422caa119431e02c3d3f4de9ba31e2cc547155386fbc57edb0325bedb2352039a0fa54de0e9461315abb3f9c20f3b1162a5f04ee0ac8f646210021bf704da1f512bfd39d9b811ede033493219e9b47ade4b39218350806e8845800a015c39edcb80d036260971c358c62d927cb3edb7cb7f02e6f5645659aa24c5ede464092c37717541cc2416ab4b479d28632ff575726c7416aa63a2edc61c33590228bfd67f220a7e5ef36b3e9ffb9365a964de33f69c1acfa1d3193800f38d78b7032d356ec8c7197d6d1181730b51cbb7f3b919136eeda0e270675cf061dacf8f2602239ba29f01971dcc25dbb0b356dec5b2de34c07f8671f09340bafa0c3c0f1fee0221a82815c4a17077bff3b02caebd18ea958391e9e8e19e26a9904c31c3c296510219aadf057e49ef4fe2e676b57f929ba527aebb0e11030a7e0347bc500814375bfe62dfaf4d29d4e7eddbda8d09f5be73d5279373a50af03a8b5447269c4067ae5b6131724e97ea732be59281c8a5ef8cbdbf40d341dcbfe954df19b7650129cd461343709af7d664a95f6a792b9e35f0381ef4c7b3a94159c743ee44ed63400852ea7aa53124031d4fc5f0a178185c03ae3b772ed4d79469b90f3f17cd13db1f9c49f7010bfddc241d5300982452d4586e38ac42329ae44c923acbf0f4eb9dc91cf78eed71aae124d409aa289d1df4e9520d1ba154f7b5839cb1c63814500039f7213f08e0d6347e62bc94c89a5a88060d309751fdf01cfccbdcf8a195b8c19cb5557c7c4baa0219c9b316b98286660a72043fe587f5f2310fd162f4b8b6266d893956854e0d7ab4cdd901e1ec11aad7de2c1c92a0eefe068f0defeab9fda562ef4037dc8fc2078ce4b451eb873f60d9a2017bba4c4bdda6fbc6cbb20f428ee64d69e14f8791257c44313eb674bbdbc3befe6798d88f3a7496520f8e08e723c9c62b91c640fe3651f689370aab42355aefba93e1c6bf07ddae66018bf0a4b626d716bde5ba4df1e4e7f49a96265d362b530e1bced5aef94c6f4800b5ab5a94e798d4a56057e43ace585ea2517b5996b1787d403358cffb836b0981c10e67deb292caec08add8a2837fef249d880739bd59acdcaa360445c94137b8be75856ff267baaee4e2c97d0462f0a1e940a541c0e6f2523a569f7836a970d92ddda66cc849966dabb794007910ff361bce1e62ad324404d6a3b3b0dd72777e6e55da531a6ed351da3a119d2b506d54c374d3dceafb52e0f973e9982d9c882807922fb311879409abfa5a1ae70254a5e2fd59d0ca354f2e5a38f1c54196eb8db521aed9f019e457c52c4edad209f09242e7d8b44c69cb652458643c56afa178e8e2aa699a1a5cfdfa63ad67d6abc0a8a1eeff56ccba9c0ed4a454ecc6560bd0a57dbcb842edeeb71fd7b8949fff2f087307fe30b65d78f974bfbd627001981fd967c6a357bd04e4f967fc85fec355bb73c635a500570d94882a1ef4d7aeac03229bd4d38c84d38fd1bcd1125a8fe49a90b925144fd66f2d8ba4fdcba91898ab419dfa2d34f83b6ce4e9301cbbbebbfcc34d8d3d46aab8b6201ca882bfcfdb057274f3ba46f39b1db593396059e9a3ce6150ba8a4ad444c2cbe51106f8b6d7b9c2d9ef7205faf3ab68bb5e0b5c3fc231fc060f747d9fa147dc18b7215cdc31bfed7b64ec1fb4f6b5b76f5a2d88ca63dc9d75929ecc74f7d852c3822a89ff35db5de928b39c464bc2df8c2f9172cc879403c03991ee14dcfcf01fe4e1d8ebd583329b57fd243bd4a6250c97535f678dee829f9c9b80be57c91165da6e62ac3da14b6c400685476ad9e7dae379b0f49e462ccdb52f888cc8acdb648f8c4f8e9860b5c0076687049c2a883dbf1ad7096dc75c21b82714530e4b21eeaad7316b9e44ee7e70533c160dce9172096b36b4418e4f631040631bf69b55f3a31aa558bb3e6f7622215b431e720694d899f623c77e29c473fe8830ea02f64b41a053f9b7062d296651d1aee85d55c7a838218de684b09cdb6320cc85f25a5f9d0b1c69666afb0e8ce3ffdb090a7358f7ba4566b2cdf25c8e4bb438fb9705a6011d283223fd69136786ac42d53f54e7fb5fdb6470a8156b6715f2d8bd8f45324dc1d8410ced449d2f5f3a7b981ff607b2f1abc46eebb3afa1abc5251a000000000000000099f0960300000000db092c2ab32ae1f2ecad853334d9bd5f0cc0d8267261ea1a6ed5b19a5376c08e0bc83176b2a4732679163cdc90f93da570de57dd4d8c426b788e0b82731b923a6fedddd592ef959aaa1c5640bb0eb6c4ee8b67c222f406c193fbe6df03f78a6600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e8483f021d17c5161d673e04c914488e7c35c2011779682177b13847e8d660048191639e9fc6c9803dd5f34dc6e10139cdbc47321e63af8c4ee32ffeda5da167f7ae344a384dca349180cf82aff2286f3c38412eee2b7b5af5e6a0754c3000b23fd81e023fbcc737e38485f0c3b95271993b45c786a299a434b1c83011e18d66022ac45817c13b656af5b5031e3917f4cd2d2fd73f90b818ad6194b00864014f1d0319dc962bba7fd2ad54db39df6ee020a205acf875abde489da6cbfa31e9cb7ac90a01cad4d0685b7bb24c91b59e8de780c29a65d0e194dd33460ae7fa5788eba6bac50c01c7ca632f0670ca29fb5cd7bb2d07fec0174673f25588b366af954a8a92031708f6848190553a0f3f96e56f77ebe1c7df8c7ce507da46b18cb2309da26859032fbdd1bb87a07cb81afe6161115ca6509139944a9d40412c271638e92ce401b9021db4aeee5de5a26eed099ed9ac47d0c30f58f8dc088675e411fd477a9ea171a8022c3a4ca9e5947d805464c59e401d67344ae073667e446f77e851591e22d1a3fc0226d42884c0fdc8f61646b3f0a2ddb754066b6ee8a945b12bdb19eee8eef793ea4bae3fc5cafbb17bd6be09eddf1e1cb0445f062821d13a01d75f813b9914f2115c56b2429e63435de3f14d01a6d6e2f9935f223b979d180667a8786e86275448977449a2c49cd0f866a7180399f740fbe290dea0b08708c8f28726762ff362a43d0e45a31c8326ae84bc0141e86edacb474d51dec66c87afafd98878119a6b98ab13efcaafdc2ca535d724c05bf96910b6f7e4d00e53b75465906135735d2be1b85c72666dc76af93dbda719f7356be14d57bad9fc6e3eca477230220930a4817e5f337617bd2e2ee912f171dd8f3b69a6a8f417369f2d8b228562975fe031c6142dbe53613e37495300dfc97b9d8de0c0b6c1db77b385023974d00495b3a484a70109257d869e77b71674298d29d1785e7084368a45db5ec4ae5cea80fb5f0d2c9a39d372ed16fe5c188230d5b5b319770f695c44658668ac9513d465c5e8a209d72153d809c7fef95b541a4122535b310182cbfcb917dd6f0676551606add64f229c629712437920e9a284f329c8d68926b99413b284a5af75f2d48cd777e211bd59e3798ceec65e16624ac3cfe878de432b3ad5180f348758586c884435dbe148564fac7a7ca66c06859a410eb1f790a27db648072e309fc58e659e1850ad33e81323dd947e2f6c464d58e5f58d4f7f56f3e430c102db66d82e085b3fa9a010fbde507ca97c3d6bcd0ca72a42ed10f458a4d088a0ce80deb2f56f0739b039927be8fecb13483b3b978422cb991ffb725f198a436fb1aec32eff15c51e8747349f77fef315f11119722df9662c01c7f008ca4a9bdd183aa33c4e88323e4e08349fa5a5646d527ca269255446e45e8284926c69d19d9766798d8cd385dfe8049c0d946700c490a26653ad3bbdeb26616a8dad6f608c0a5183319bb433290db7d8b49061acbf4c6fbc5932cb30d22fa126aff94a6216d7182d6b5f348871121dd8549b55c177430725eeb8909ef736b2ce068d7fa9859087ea6eb5ed74463f55bbd25ab587d74f56b5bf6d4bd3ff851167e0b86495de0ccced166e52774325123c6fc701a659f777597c2794c653bc150be0821d3b46603254817cb6da75a8cf74c70f6d49a78d997087a51dcbe5b9a99e1db8cbbc8ac8c7822269b822d9bd18ac3692d85804f8f560f7be074654b4a0b6a302e44fb303ded3d5e941840632163ccb98feb1aba2ca472f6948d41af26b991882410f1bafeee2d17f82cf7fd4fb70f10661181eb71ad9f79c830f4584fac7074bf2a79cb37277339dd353b4a4d38bea77385c2ff6c8790c45270e49be6eceeb46beddc0363594aa53f8eca6b1f057b90e4a175ea5e13f426901f81cb0acbad82cf1d2b219b77e8bb9d01ab879e4c6a00e8783fb312bbd376fcbe21c99e3cd95b879f33edd25e73e5654403a65245344c60b3daba0622ed6e0e050166346f81f44c832635b5fa929cbf437a5ac34b6993d9b833c3110e08d848da990ff3845dc5e4f4da03335488be574b61b6be4f92321b330fc7988dd17d673349a63ad23bc4ac404c09bc55e9af64fa1d1784f6ce71da16161944e16e17bb83893fe33bdf9df6fea370b9f6d801def8c927283481e503f003f45151ab433a96f9eb4e1d417170abe2873186f81512641d42ac8f45655a1dd4ebad0febac5e2e674aa7f0935ffd2d2ef0ab87d6b3cf5e78923a1340431d94b498694c3d56023cc8b54c83f63b0e0ad7bfcd4e66c08d25c3f55fd4bbdfcf7b539cf0944f43fdf739cfbd985db60530ad0abf0f3acc33e7a0bb83bee2350ec0b5ae5ff2e6418f1ed5a28def8cafa6116dec2fa1efd98db5170ffd7d6a8b9db00542247045ef9694f222b427403", "655151", 3, -1123167027, 1537743641, "05ab55952ddbc5e431ee2a8375a9f278494f840b9d5e637206ac5432843b509d"], + ["ef720c7504249444d2dd7d90dd8300d42bc5f703b08a8bd49903db20430c385774d3ea6570000000000865656a6a53656500080d163f0fb85c938b72c70f05de60b6cdc7c92e149085043da07d988dbd165ea76953900300000001acf94a949809f35d88f4d25b55ee1118502242abc1080b5a2b91448303bbd48f87a5e458d80200000006526a5252ac63ffffffff0fe03867f6ecc14092c60d32e3d4de374227236083644e51f67eef949742550a0000000007006a5100515153ffffffff048f9f3d0400000000056351635153651fe20400000000066a0063ac52639ab8f8000000000002525197bbca000000000009536353ac636a51000080756e6e00", "53", 2, 1472115369, 1537743641, "334af7ee648dc318475dfc27403191d99cddfce66dec1449c5bcb93ea8def845"], + ["030000807082c40304b865e34f9a0cca30a1c6cfe13c9b19f32caf5dabaeff5ff12ff1e8586e996f230100000000ffffffffd1209d224737e3f3464357394f4d9fc44a66917278253ab078f6c320ab2f1d1e01000000016affffffff7ebf704ca9cbfbddb7509e57cf0b9db8028dc23ae1e0c2570663da97fc62fa00000000000363635128990280aa993aebbfd1bee61ec2a1e315c86fbeb0d01f7377429bde126f8b08714b230b01000000046a53acacffffffff0351ca06010000000006516a5253acacf3a5480200000000065351526a51514d77ba00000000000463516a538b21ebab0000000003000000000000000034013d0200000000cd032f695e6c3e1e3d47caedcf137d71c6adea7030b8dad307752d3d61fc1b4e7c0add70631e835b164f1a027b904a157bd5623c1d05f615ca40fde8d0539f0a913312ce71ef5496d66c0a381fd7073f7bef1423d50a74364fa1c59e4e0dc90a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e3a50d0322d0eb7a61bf7957ab494b452a1b0dace1ec5e293142e6165ddd3977fe08b40467468cc33fa006c9d468064f1e2196129a0e4b80997434b0a7459ecd3b1123159294a538279f13f144df05a2d3da2438c4d80a2dde532247b073063f2d8b089f8c26323d1856a0a52020ed95519544e3f3d73f2b307d8f311402c94f020e500115ed4f3ff16d16d9f6a21a78fd4996f575d3589afe3934a5304eaca3030200c21675249b6960f6023237397a59e1486637b627796961cd03ee7969b207800b0330706a0b9c9f1492139bc5ec162ae4495c5a4927ba41719a0f92e9340d1d09505122438a82ab76c9555b0ccb68e64091a2b3f425adbb8b9b66eebe3dd548dd0223174344a3bfc44267f8f9d7229b5f613c0ef717016b369f7e57ad024d886b56031bd0a650dd0c1ce07bad8080b98b66ea445ad86cf892bb70e9eeda6e33a95ed00308019c5ec830e81f967b834f6bb7913a168f165460eccb68617c727c7b995c9c0203fc65fa4d9344f6ffc41ab565a95f1edbc8e7151bf87254c5ff9d98726566f0032ef91519b9c4c4ddac4fa4d1fecaf0e59a4a2418f6ca2d355e5d2b92c6cb48f47d03afb62d01c9a661338e60b45cb935793bbf876bd11937eb50d1d65df7d5d6e201bf7fc64d052151bb508ddcbb1ff5b6f66955ea8d7dc1e6b3d718a1b62a316d53f07754645332b5487b7ffd3dc538c368bb94fc9791927f09d237b59519a32da4cff6c775a98488bc990281531d8e7caa7ef11e9eef4c9beff93af30c0e9a38fac591548908254667bd81f603419444fb95a794b0962e01fbd25e13a1577f67c2d58147c85e68c217cde1c45f7c93a73854099cd4bbc50f5efca569eaf629176f1062d0212be4e7e9813f83e414a0e6fdffd6e68555d83a97c5e48e8d92046b60ba2bff8041a79628294a3740fed867f6d3ba02a4ff961df4d49f05467eb7434f2dcd35df3cda31fd79572457551f414338f60456087f6467bfffc0f2b03190b4db265d5eefb691112e20b62fc47e81380fae28662d9b2a3f30c06c1ab569d8300a6b5b1a6cd38c976fe804ff3d8c118ede8818d78154941edcf3339cdfa350815f475c22b4b15040c6d36a676af535e0096a008913e82c133b1f416fb1bc9807ca45d651c2234f0a692d46132d9315b24613fe66c4b59d4a59cc7ba8a0752ab7a977be28cfeb73b0d8edb5c8134b60f941e039773633d3c0a70a9e999c2188d8474cd81e9a3420b295b9044361f28135f39ccdf0abc8ea612e7c9705dd04620a7df152751f11b7056ae1b66de3e17e41c9e49bbf6f66affde03f6b2287f8fb92161ab940c8bcf76efa68aaec84c7e78891c85205d65a9e4ae36b4d6147b7f7d459272457031157ee4ca5d15dbcd957b2dbdfdfd1683028c618fcf974841413f31a6c3353e3b4f297eb7271634f12206aac4d339c2c8feb2df3561aaee68eb20b1bbc022a47ed663ee017432a19c5caf560a736ec09251d953a3b1156fda27dfadd70873505f72e16458f146450b1129170f95849e0bdf11dc35827e04694eae250885c6cae138719932979383d9a63754d181d9527e7c6962e17ca7eac2363765ec0488cf0683e1077560aafdec57523ff94aa33bdd9ce65e9d1c95f302e4729c329881f6b0f000ff21781035777870ec32023c4cc5d58084b81f913ee9bf3e811d6956e9edd44dd7622066936efd1d47cedaafc065574d2ec86d1332458874825289d28983eee22ef85819501a83e84998f3d20e5adc45860af464bd46c2e9be87a19b1886e016d8eabe751e067848491a00120e970852b51027a7f00f1f3c67e0eff0bce1facc9075e9b748ed3a4c3f346bc9229a492373c9dce7fb02a125bc53de912e0d96fe0138806aaf4397396df266cd1f25142668cb16f8a6727fc73ac3e398d3c39d957c807288952766e638d3ec81ad1838e2af5ec3a0bbacd9395e40a322894c2943d9c08639a3ad754034156e063df1ca7e21160dca411b473b4625ecc5ef4c7aa940a539d409a01bd523ca0c8b5b22876f1dc92e25378f6336ceecf75f4f208fec29f1384ebcacbf7fae31126a53099390cceb527ea12b267ccca3ec53ec689ff64c039b8c969714e5fe87fa3c320443b657a042c0b3969362b8ef94344030f0e4917a72e97a39164f8b4906466fb837e485a98223b58ea925b03fc31cfdba417d0dfa0a68b5a511d0fbcc05d9d05ecf5390b4eeba574ce27c054c906d1635f21ccc816d4a06113642306b5bce99e55770f2f2bc345c4f141c23ad81eb84ec22441de94b1968bda0e62ef371203000000000000000000000000e1199d80090c989161747518992aa2b8b70cc89e44db2ee4ad0499a8211157a2a021a3de94e860cc13b028210fcc55ed82fd3321265b114406e808f8f3475e3ab2cbc7f4981b4682554b5508fbf8de2542c9f5a93986482137fd6483b9f7e8b6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008c87950a9e9ca849091cc28ce468f6c88adb3684f86679e125ffce1e2ada6cc8f8f9e6c40e33155e892d45de67e948947791782bd72d76f68f52e56e0e99829b80b321a20ae1ddc742a4dd169389fc67eb6423b36526953169e956c7cf3392b20f71c01537ab8e7bfc8d9c075ca47966cca923f55c872f275e3e11bfd8de5fd1022ff5cbeacc967036507ad7cbec5b67f7dfa30e470f0c07db23f6991d6b6507d10312288e33a4fbefd37aeb897690c7fd7b716905d801f8bf704ec87fa21efe064b0b04b5019322028e81abea57e05864dbc446de088ada53676c02dd9c0d4bd90f2ac93513ddaf52cb0d99755c796652e926e428c63e3a3ca407aebbfbf1049b96af03156ec03c5876048b88243b02a348942a840181e2e276eb672049836f2409db73022f6d4a7da14479860f28e0bc682c03129031b9f799508010fca1cb7074ebf6930324b4ff60b45fc15711cb4f6fb184c59ad8f27f20a1f2757353f34971dc6f34e4030b8b077d99c3b37aeefa1f50faf1d7976499cacee235c62c06366be38c7b527a021886e8ba49489d8acadb4982ac4ab26a737f78ee0dc3a9300d09cbf307a96ebb5c99bfefb0ed84892adc29da3fa67f7ddef17366497d271d1cdef08fffe28d12c1560cbaaf0a8a1d8fe236311298155d8e4ed86ad1071cfe030b572097f4706ac31090b53762906c50a6c957841218204cf2aa356308784372c053c4a74ec4cc71c37fc8255e418d045d4057b7982b550c14966e562aa4e08b93a4664d470cc1647cec1d414bad467fb52b433a4e372637f2b75454c276ece098bfcbc3e37c649f4c0a2adb75b0d75dd3e6f063650dc13934a2434c876eed60e169ce1b38ddab51df4a166d84bcc6ffd13e61cebcec3b7198e34adeee3ee726bf130d390cf731b0bef27a5cd6246c915364211fbd0d05fd18aff712104d0884b933e4d038c8f86455f7be9eb40f616957a46840af5057489474539ed6cc3f5a3c31bea188d448765f941a549c11830d2f0ed72f62d2eea9422f1ccd8c40bb31ac9c1285f111d38277881c0bc38008baf9ed673747076cae4bb2637f4b2104cd68ae28c0776e50a673b6e74b85ebb54d927334eca99140d611621e4cfbe5141a207bfdbcac2b46c58faaecf70c48e2d4937870f59220d6c625fd84ca934be47fbd9fa24082b094919e49ff6dd725f7606a47285a778c89eff49befd93d7881dbdf040cb72112d44ffcc8d4eb3b7ff5f41e580525761192e39386f110265e3ba562f5ae5a4b0710aaee3275a4c75bc4bd16955415bade33b67a3365434a05652f7263b622a0b636b618478c4d2739f8c87ba793ed45503fc8a5a4520b98ccbe00cdf759716f548e500e6f3ab6269069eb750d90c493820b112656f5edff7220a29682624b1e933e5ca9cddf853d7de7a5f8d3e56660dd9cbd522b2f5a21e394a76b94651fc58435321688a970432c96b13be0ade323168572c7ea65fafd6d442498c019ef6c839282830657cefddaf4f298bace309ac985cdec5d72c3dc4b0fd665b8b729b43cca7266944f4313112e8f01275e228a9f40483eae57d628ff121a16572cd07329c7721821ea1d8d292f8eca4fd07c62ad597bc97605ea102dea662571a5bbe348c89ac4f7bf7d6c1bae24e265fe7a027dd5b38d22e8f88f4914e7faf5873bb80e6fcfd4e054ffd56311022a29b505a6337b4f93541aaa7bfd4190ace1c29305e9e35676c1b289aeea2a2e4e66ba487f823f053e7c42a146798c53c80236c504c2bbda708707ef17c068fab137b551d2f4c250084438834ec1d4e1180ae03c3cd3262a0fa4d63d403764417bec97cbbc78d6feed6d1b6f67630e0f79d6866fc18b55308f87a7ff9daeb2dc7678648507f2bfd7d88a368d54474ee3cba853e0688aa9cef7813e5c2e29d4e8942d14643b0f9d5d13569f6c633b548eea662b6e32caa403b84635c48c275d353df33ef63dac2f43a5552ea2a4408d0e6b5220fdb8276b29c4e1c624d1c63db9927a020575e23974b8e0323e87c591221cab7c27748bfbe041a9dbf4638232c7516ef1b22925c318aa28315c0f8570a0ba0588c36caafba79bb45b66c137bc57e93ef5daa0b498ec89e611642905cebfcd2c6b6967cf7907a86d02f143fb9a9027ca51e39ac2d78904c4e240e877d8f7ef7a401c4eab42556c9071df5063f565660e3e1386db31747a0f895f25f7433731b4079e3895cb2be4e8042f0b7b9e459382c188a583a94721638833d91b1e16bf3f37c84ec74c570dfb1cd6c4f04f3ffe8b55ace589d5dfb257419f040000000000000000000000000a14c2451a9862bb75e0e47d5a59e9b2b68006430038da9d4b0f5aaaa7f7f1f28309354323ccce1d0609794cb03815a48eedde5ca90a803fe0ef58e2768a03163e6441ff2403565af52ff9cf7708028dddcc9ad40bd01e374ec7a69c4b409dc700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034527c9618e4c4647423f4d82521cf16cafec3cb61b2a7bf7f2a57de4ced09eaaef78fb9886870754275f60da93ce97e37d9c391be5db7005bdd59cf2b78522c7dfb014c17696128b6eb6b1617e541246283dda417012d0f8b94070d4335ab10775698c09417c2ee9891d504f0a18776898c02f265caf12b1b60bfd1a331692030f6cf240aacb9e067a5b79270a859fd6c312d2bbab33fe92746b752914595abe031c9cb5eee80a7e13b3936dce6293be7c63f96605747794f555be008c32dccfd80a040903198271e884f70e3b8907cc4cabd2a688380e717296b1191ccd85093b060fc64e0f7611ecaa3e7bb353a4e34646e5613905800e132bded5e8a9191b1809021cea7b0b21d1fec68250af2e4a759adcf66297a5503f8957694e4dff313231430207965f4380f953007cd82f184ced4a670ee8355336ca24cabde4a0ddbed8ff5b030bcd311312bcf9dc66f7d928954a7882bfb81c4feb71e4df53fe3a83ca4a0af3032ac5c948eee6c763681eb91d5d6914036f9ec1e79423c36b1022f17b6aa69131031d6e8e45ba97bec7e99bfae0d8ad84703af0cdbb0361052417ca8f215e0662ea2f09636e1e0c7881c2a156c4d1ae10fa2bfecad82d0b7cae30b8d7fcd2bbdaaf7b568e263f240aca338f02c404d09193edb2cd6ae9d40094365316363a6e5d78fc04bc7cc4c9781f39fa126505b48821226c32cbc637fecb58a90e7bc9c22c3d4c58819bf746311441734670e27975d2d03be37b5a4ff9e5a5df54213b86b6069dc45413c34406bdbe09df4e367e83452c2821b2c503da361395dd062e025f47f7754d19db76b40ba93fde1380abba7a85e36892a02387f7a254ec858022ddb64019f47de565f5eb9c3019723f82640dab0c8ff83881dd300cb0703f79bfc141b5ce1de092e20b69bd04ef87fb8939bfbe43d7d69899518e785d5f8a498f736599deb93e980b1ff6a76bbab806a453683e81a70a630426b9b3127a6f1444dee0a5c663210294050e3acd18babb1bf9d4cb1a34502bd6f428b11f7cf3d1e31d7802447ac014256a6dde0d638466cfba57f44eed2b1eb308871aaf3ecec24a4039acd7d4a1a0fdf1660ac8c34cb83e708744ad9f2947e6b247a6eceda2d9d1ec051450f84f9a5b580ca1734cefc8deb179c026dfa2ef2cda913e3c91df24674dc53a81515f8b76e785f39cf5d7020b7de921e40637bcae2125fb1e14e11016795b3f00425a12a31fc0f1335d3550dcfc45e78588009207d7059cdb81c50b8bfc8e18a2cfe3f5b4f81e999981b0680df4bdc11d94076e86b03c365e1da17f38492719bfc7d8e97dcd5cd4ed461a02cd07f7c48f768fb8c3867e96d132cfe39a101a6d4b56c61f84312bff4efef40467ff19bf18bf51fd6214ba734cee9f5b992d9fd8218dad0c5b5aca2ac9bf2fae9674836bbea8166a22fd9a8f9a5f0ffa4a0faf64e519c0034a4c9f0b0514fc39e96b04d2b8d3ca2674604d94ae206f828190ff6bacd197118b5064af4ddb150f464f66df78d3c1dc181bd64576c97b37068bb8f52d4c439995bf6dfe40b368facc21f0a6c59f004511890341f7f477a8f86a41057f8e66a3a217603018c7ca0dce30f6144b0172a0ccdbfe0dd2a7188132761370067109b72fc74225abb31356eb3eb1a41632ebef517edec48112cfc2c6f4666c071a758ee15e63acfdb462cd5fb36eb40df0f0d11281fa73df618e51592e7f76d7cde17560d3e2b223a394b118482639c6213f8657e6a25b8c387f936d6d30aae06ecb536dc7b3953284db557a8ee7ace3129999b348d53d6fec179897fa731283bb91535bcbcd2e3a663e9d3a6e65ab44fab6e9bcede3967e538bf1e18c191e5c11ae3069e29233108a9c0bacc4770b93f3d304e6014730345790a329eb52e13e5b99d83bfd5f8cd4308e9460f0da2cd4c0682c242937a2280f0b9e2ce066cdff5837507fe83c00e998fd3eff6cceab6d1eda5460a98a1003ca7dab7ac272692f55210d1b08044f5264305ae614ae4fc2f9ae16efecccab6f8374299cdf30ef069aa51752ad342da98f27c43e312f755b8449b2cc0cdeee5512e8bfa535b7c1c11a94253dc6c66040b362f2923ba9e66755d9587672df2cd95c863fc0dad65ac6a86bb0acf7eafcf4c224ce5b7d6561560af89415c32907942fe8f4f8f053610507826663128cf327f09fa20374e2e71d71e6d0733e5c905e810b78eb65d95885a60380866e781eb3fa0c0c4652dbc6187c396cc47dcf9623affd56e79da14e4929e9c7bbdb1f0cfe3c7f49e3b98b7547477530085cebe1d1157beb111ac0cc8fc4d1bf71cf53c197c6129cce3abeef5946c0fb357053837aceb4a4f6e35b0c9a567c935266c32d7cf0d0bcb8b1e6e12183dac05170fa1d7f0b1769de32a5ad750e1f9da7d5f23d34b8bcf32536cea70f", "53536a", 1, -889702242, 0, "ffa9ddc2dab45b65b784f3b7d85abcf2278fdad228f3ed9c76749ecbbd069ce4"], + ["030000807082c4030488e6a3471cdad1c7b4b122d9a2aec4c1ddb4c380c65469b8a132d00bbca405e800000000045351526a1b5f0cb86f112d52157ddf44c977e09dd34bb9d38ca8993fe8a99cfbbc31a7f35234670303000000030000536b70fe58e04314f628f499c509dd680e3438907e4e5e3a551330a9a4bd762dce44c6f2e502000000066a6363535251ffffffff7702facf866e5ccb958b1a7d9f319b51633595180be25192ab09bc4ebe20b2950300000000779af697031d354703000000000765ac525365536aa4d23d01000000000056004304000000000952ac526353526a000000000000c651fb2502655c240100000000000000000000000075899cd7be23bb90560a5995ef35463739ff7040990a2fe262cb865973c3d1989780eebc53f7769e51e91bd31986983f3d4cf465c0488d72d999a9db53207f883942a2a6db2d16a27b0d549e488b9c5a215eafc20c9ba6e345f43c2911bd730e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2a2c433cf3c4f3b3e4fcbdb15fa8eb25a054ad88258bdbea99f4915156e7d2b4cb79984eab14d82786220844351d8ffc9a6789d1e247750b72c415c026ce976a8bd54490a97b5a99ce3838608d46b314e89cc475a86cb40802274f816e797d586247f1b8c38042990390d0d1a2b954d0875704c82d66c3d6a2cc3b5efe228cf021477652ffb1ff84381fb67341389690383345fe41b36daedb19c7a83ffd005ca0211458251daa36a8f54cf2a663697d391c84576d7805caa22a4fe5f9c549c1e400a09100089efff027a98fa7654767c3c78908d3be14b489541b2f64ee0f32fe35299dd0f939c28c6a21f929078e0e8e5ae0548024c12eb0af63bc4be59c607b64c0226348a8081b5eb6248ec9679512562a3276601ab0e7e16cd7d92454fcd6af6e9032406a294231b77d829514525342eb482441027d23247225dd47a512ffce481b6031608a95ce4f53839ad3dd3bcb02152ca1c1a498309a7c9ab1ff141a374777f13030f0e0951b36de7cc523a6b73b2c1af6656b020102c51ceb36c254b67a5ba89340222ae70f7296c42958e64909742f94420b63cb3fd829841109fa10c803cf4cb78c6dd1bb13b469d4919f3963fdbed1cdf57cf6b6b156ed204575744d58d417df105cdd10bff34edb89dcf901087c60c89c421a68e45b700f8083c9ad2f301bfb5ab0eaeec759830ca34cf9a03558c5e6a4c2f58bbc60c24d2780cbcebacbfc2cec0cb58dd6f03570fdd07686afa79e62565180d221c41ed65dbc421c6bd634c26e52c01743cd0124a8d33a8202275f0780e073f77cd6aa0da52e082ec670290ee2e29745aed197d2efb587e55ef836b081df60adf02eeaa27b268141b8914d353323f4151951e47775698bad437a3ae5c265fa0735ab20998cfc041870d81f53b9854d3f598d9a663697d0c2d2ff82eaf6e49493af18822de331b296b36776d9fc730cac623142ca3b53103a046206628e130e6a67797c2bcc2a8e2e527771f3534f48057ddbe6a165a67e0f6c692920ea0b31f90fdabab281a79f5539161611d6842e5cb4a4f8d412358f486595f3c8535945f2bb28b39b75efa935fd2f819c65bf1578a1f2c69e86b1c5453137090a98dd67feea8cfc9762ce1d0527371d8a57aa9cc1075092358283493055c2b1e8dcd77cb160fad812efa74e1aa09a111edc81d403beb4cb3d283b41a902d45d5f58b614c3f744b750b100353c7e5667190a8f951f1fe76f8595b63d81032589a5fd2ffea25437f34ea7f6a4bf041e48e19e5449ba98834170bf8a5f566cd6601d8644f0c9d6fc1f07cef4de2e8a3b27a8560b836c11f5c0216be01d3332af84b62eaac2a38810d2a46e49d1823da5ab3b2cbeaa81f4431f2f9a09b9f55e0b881e2097d12616e249c6867cea59feca61a9b1d72fa0c1a13ecc6102950fa9b1881c5d7d816f69c4809a7c10a597ec658f960b0017670a9be9a288d3c6da411e0e9dadc3367b4940e46d799d42f4ccdec09dec8cc1f06ce866c345c98d9e51750a82f0d954609e3bcb14c0f1a6052422cda82f12f0b61d242f4949d96d49c335a1f1baaf3bbc4d66ebb66a1a4a8c64ddf662520425a77e13bc586c357a22433a098eccc12616b285c54df4d06a1569ecdb9d0f768e75ef4a57aab701e805ad23e833fdef91d87bbc0827db397a043a21c2143bb1cfd54eeec3ace428f13824d73e9b32f7a1a052f4db5b7deb5ef5e34fb52e5604726ad00904a00971c265724779c910bb2418238238db369747b774a6dea30769532dfaa3f357afd44355cacc168b0e76d14bd26e30f66f6067fa50831e8b6a990918d863dfb8addf41e34e828e5fca5916e10b1613c325260572b648606d497d55d444af7183f1b658ef8509c96c4f891fa798d223a2fc58ce591250fe86adfab32526861c163636aadacf27c217df6d9f0912b312e11098a52ebc7abfe7ec90a218cd9a24677185793a98b48ec3d0cd10615d7ba2e54f18bfd461334ab21f2ae92b83747d4126636c17a3d4f83a9a45b59273f2efb1520c0b5d4b1792156922233a4fb222cec60388315abfb9e37185c754bccf786043fd715e5ab0c912ab65f86a2b3f5d812ec901f472b3b5cf321b37585605d516f470655a4d97e87a2c579ce4f1ac82d10c689b73a678ef913e3e77178776b71570bec89ac6ae2d64ad43db19513451d8cbfae0529e13dfeb76c098cd9d1c8752a4f1d433dc45144cbd3eeb00282c76614249337fb297acdacd231a88f8426d7f864b661204eb420a3e02ffcd676c9d8ac814e659cae1e122b35b05d48df000000000000000000000000006d688970cc29d456b69f098d69aabd1a72dca41026145801b7358d936af1967605258b3c7738dad8a97a8cb0529b4f3fc3957f44115abb8f29f47ecec88c547ce7d1e7058f5473ae6e9a1a840eef79728127fe05bd800e4dc42c2933e01bda7e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a694c2a0cae511851e9eae4db4a0d98187ee8f395096c255e09e26e0f036e394eb668bd166e704275d967ce2da5d9b174e03f886603840be3a386e8b0e6db47a8cf745b2f384e53396e12f0159462b60bb41a0e33d6e158f4fa93c95162ae1c4b96ae1725f16773e57ae384d13bedc68633cc6f86f559168edf71179d9380431021aaad02993b9dd3000467a54a3f7d32c2b8a78302b2ca1432086c3b1710fa8c7022ed728d9d17fa8b6a047baf45fbbd4ed35b34f3a1734ef0106332e2da7ec0a150b04a3a381b66c30ce5180fcfee2a131d3b6a8ecd77da450d2818ded349315e4877cf99eb5c318f3f9111c7c4bad630ce751f73d34dfad2d3a4ef01b2889bf1053031bbe9cd676cb4b24f193472a5b4f111f40b0393a8104d192294bc5a0fd62790c0325953dbc0052e6cd709a5e74167c4b2bd1d072116e805f415431920cf6effab90301800299e038fb5a9a4e108736a84018354016d048435a2fe372bf1d6e4d8e3a032a677c3124453fb9cbc6e60855b4ad535cfaceeead8034441180af3e49ee6e3f032b2270b52edc06f533c17eefafe87cd3139bdeb83377b5ed9baeee86b096608a433f6aef41e9f4e4ea0461d0e0dfe261f8659610d9ac8050ddc9c4b1cbb53f44a777995d3fb3317fcd4cf064743cc5be6876d570731e69d9156ff8d21f566d1b679a01e253a5db0d0d6bbac1bb3bf106aec4e72291a172da7e79a5fecb444656e11b3c8a7150f84b989555c67c1597cb3217ea306f801f34b736311a9cddc3d9ce12dced42fc188c343f526cc40b499fdd641990b85d9ab2f6332dadbe3907df75ff523d176facc1c61a81ff0acc7d7e88ebe7bf0270d7581ebfb31073700a30483787fc4c2efe6ca84db75a9d532f3b5f863bcd602449135ecc0815999498f9a8f0c10fd934eb780cc95ddb770d5cc7d127aff6797eb9b0ecab34b1e94971ac961e2492d4402dfce0e18bc0f08068d8660a6740e587fcaad43a0c3b6328337123d7f0fa49f38d79ee1e2de2365f16d389d3165092515d3830a57197d096865d47bde1ac00d40a14ba504352cc6bfd63018e61f3f00b84c65afcbbe06b8d6d0796ac7e564714b3634e06c8f767c35d6dd7ada04c0751e5e892e3ba4c4c2b1235b85f9876cf178521337b58e5117e7c0fcd1d966e5dc146a2668fb1092e862969cec7d1bacb6880aa9416dd5b54874631fbd40977402dcae03f293319b5a9ac158a431217f3225b47aec56f66bc070b80ecc3601d7b5a58ed052a69863f1086ab0257462fdef7a3d698b56f2472ee20a21e5dda073ef4b40721d63bcdc06baa00e8627a55331f2efb70f1836239abe675737f2c16128830f07ba6e79840fab4a5643279d9c645f814a37b7f4791c63d4e3abe54ee3b8130d3fb5e8441994ab1d5da7f8b13ffe0f90fbc63e131e1d3bc5c8d449f421283c20ce239f68b43b5dc6ad3a80fc1737356a5501f9bd119e85a4b7191aea98389c5ff7563b8c569efe5f9d06fc9fec564fc9241f4f30b345759d80ab750001d1946e634533ad1b062713347b83cc7e35676d208dc4b47d4cce71f27f25c6adcdaa4a7dc61b68c4a8b0f2262f614d054c69174ab2bdd91710e36f59e33e450219a640b306683b52592f95aa3cd4ced9d775942cac943af262cd7cf1f02345a20d0f0626aa5db81df662ba1f98a18b58ac140600d58b360b79c92254d94bfb2a52ba478155db95dc29d9472d93a9eb581cb3e615166226938a5b2a2cccdb14ac53a4b1d9d51c1be61c0419f35c0887f3c81ce000b1140ee9119c0d500ca1c8658967bfe377b75cc788eb6535fdaeec046a9210ceeb03c7034416c0e3006e060bb5097c644126c04272ebb09fa72afa4eee80f0a7d26e1347e24ceeeb44258160470a995848f2efc9ef0d0555fbcf52627cc244e83b76e62df6db9b4d27c8153082ba87ab186426b7f600fa5128fbac84098c640686944ad051a63d7fb56b2d19af6631d493170ea94001e1fb2c976f6f06d350e73e4305886f37cf68fb195272fc74beadbdcb3b6dad11f8bebadbedbcd8cb8616b637c7c2b33ff7762ce8c1e5ac72b166e45849f1feaa63ab87d1210c366707a0a70d493dc3435838d72656fc400c370a4b1ff6a6c9aff64b84baa903818112f0648eda4c958471e715f61f6f7cb851e1232c150e9e0d348b6f1ed83c7779ead3ad69701f9a8d12728e64c159db37d774d92d9ba5d75bbbf1752d5ff5f6841fda395e1152e76af1d1272853852b035a6c177e16a58090ea92b3d7ac827cb94d94524b17d9f001627aa27ed5153a0daaad03b20b3a5252114b7e4eb1e5292d2f2ab2d8ae0ab74160d3846d92d7e20e10fbc7565f1c4c69b444c90ae189357dd54a304b79f212d2f462aba76d3b2532c409d446bde91561b5998838872313f90a274c61ce6067d41eb5200", "6a51526aacac6a65ac", 3, 2126205303, 0, "6cbc801ec57e4e04998193adf0debe7b951fbd83db194c54b6ebd0d380d238d2"], + ["030000807082c40302f914e29c147a3619a2d6b49cd96b4f97aac4a335b0961c1ecf7f7af125d99d5d0100000007536a5300ac5252ffffffff1d6fbef690f94abc56ed76c33c46cc526cd8c5fc0de0523835ccd4808fd32e0103000000076563ac51ac515308f1230101d208850000000000056a5300630038d52878fd14f23400", "ac526563ac5163", 0, -1308679946, 0, "49725365eb94ac4144735b0ff05142ce8196f2a0498a851c22ded974ea5fc0dc"], + ["030000807082c40304af7d24a47716a21d99763ffd781dd6005a961b975f877be5da712722cf428c0d03000000015268050fe91bc1cd0fc4f6c430b3193b2ea169f8cf377c99fa42e63f014b691f6adeebc77803000000026352ffffffff988c103de18caf7518db6336d97d0309ebe908a096e8a0501c84045e370d6bd90200000000ffffffff3555daed6eaf34b1a87fbb25736191683a5fbd06498306ac94930e07a451f0af000000000463636363835c0f710131ebac03000000000765656353ac006a1713df5d00000000030000000000000000aac2000500000000be24f6d67610bfe7dab43ad7423d0c57d7379aa5a0d6ed7bc2b2e1045b6b232e0fbe8529a3af1355ac06b760d49a0001a6b83bc9e558d77d5f80822cf0f5fff7c31521cb54a19283cdc82942cb27701809eb9de5596cae32a0858bafc6ddab7c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006aeb4c7a3c6e9868c7fa22e7871c064095d180c081bd90edbbbdbe24eba2bc9d2bda1aaca29e8fc8dbc92291583c488b64e5e4d44ec357c9159db25b4be001b331dfe8448095f4c01b999e88c58f6db672d7a5d4fda5c1dabd58efc3416bcf43fe5b8d452a89a43b4dc8f60948dc99bbd0203670457e622d1d74d78ff28b2cbf021677bb5171e158bf5309d0d4b2b456cf44fded13cffb98a353ac943a75295ed2022ef1540e94d3d1741aff706c7dfb345e4dd9eb02e4f6fe21cb8717fa466d988e0a079e40f4e767027f5add5820d971e1c1b5987f648d0806054bd9137b544d4c0f8a396ddc4313faafc14108a74954fe77aedfa17dafa9935dd14a5badbea2bee302297b987d0c9e398b341b383ed2cedb8332b7d5378b78470c27b63e8e7176354e02153b55461cc9a5a3153f0d9d3d1de6e6a8419d1aed08561c27fb51aa851d75cc021f7292c0432ceab119483479a5ab4e37fc9db301ffbb9fec4d8b355347e2209e02208d27a09dde3dec5a691afc72d9fafa8f178fb72bde78876fce1123946966fe03267dd6d2816cccd32294b59ccc7968364702af8f7803f9d64d4c286b9c1d4d171543fad70b68b8f46c9718b898f2597cf6e7547f67276c0f3f9c446a4cf2b1004bb993e38ba9eb7a14ba8b7913573c0bf77c741b62a54fec0f825ab6fd723af4fbf2ac8d154a5ce21adfd7cc8d90e514928892c680c0947dc343394049b90294fa9d28922e1f8c3475e5d07eacd3dd1bc0b908df8d5ac5215c3bb1c7fd05ec73792c1de4e5c6735e85c0eeb66281a4b83c3ff3b7fff760e665d5c61d9b781af415da622174ca56335610a030c87051a138dc008f13851d750d657213732d9233f4c384b79b09576000792fb64529c159861bef3700b8cb745b320fc06b503de544b83a5ec20bd80021f82d3ae186975dd0e6cefd07dd9a41e3828de7c17d4b5c0526fab5846f310202a7aef89007a89e0192968cfbb9237f38f7f979627f9ebfb55137f56465f200f70887c33161399ba21c00006aa988c595b182e51fe5b38966993d61f0bfc90cc7ac1118a4df72d465cb723f34ff4de81eaccb21314d66c149017900dd8f516454d95450fe40ad4cbde522058494e8a0ca2a4b15da9d5a21d7c3f17c96f06935f9f2392f1e8d5260a6ed67b615d0d75350db63cf32b2605a5ef62c608bb22b6d92d83cab2b0cfd0378eac3d4572606052a2b0e10741925f94915bac9ff6a3b10210c37226429213d10f223ebe19b4dcd44a850c0d875d96e73428aa6f2e9dc35c6bf97c8f103932f58b08077782bfda7fa02a614c4ffe4ec945cf0aa102a9f556e6e62c8bf95b5b7b1ae8b5713e27306699bc35b3cd32b894cb388fbd0f85e35b398f0159de5ba99b681309f2ad0c9144adf89423cea3f7e644106f8452c9323167a74a1f47c2bd190c038c37ea448cb3c47f20812c3e6cbae7aeb92e4c92d907866483dfe9ccd1e8ab76ac590eb76394180a68af96dc61ad1facfb138764dc8f68bb36f517a82259cf941633f2922cc682df1a4dc4b3796cd7ef84a19180d1e1e8bf21cea8e2595e6655674a44ff59d93bc88dec2c0b3877cfb8ae4524ff27d02ee3091c2173f4ca926c7e7ce4916bec0653da3eb5a700c5c04e961cee422d27740d7e93c3093fbbf244f633f55b9055d3d0854a80c25dce7a3cd39f317e6f8f80837e26291b205af2822c9dd7e75e50a5e2159da8a9a0aaad14ee26497b711a054bf670eae76eb4bcd78f9b3aa9b2e8fb10d9a1398bef94722e0a7720be96d0d3db133e702dd7b3760affaf12edf43a064627c272c9dea98c5e99150e2cf40765641221133436e27de6eb680f670f354b21a65b61721972e24ab8498fee147fe283e7a5a2abc3602a2774d7e30f8dc2a7a5ab1d4db7abc853b0bc0fd5ecf21577ec3b2af6b597ef5ee3f7fc9cbf0759c5e354e812f9ae3982d3afe4e60534ff32e6f5676fa9fb47731ba15067962df7d5b1d4baacf0f76b7e1347dd711d440bc1f96872367548971cabcbdd65b1a28f2a8cecc4bb9c70c569dc223546093a230fe77006505ebb948029740838f26620aa5f91300ba797a9aee753b50be09c62347e8e77faf3b342a6055d81dc60c46c18a31e134290604004e0942b2b9b990fce63ff113d7ae1e13935aa55deff4eda6064b327ffd7ca22c0f7b303aa46713d5fca68eec118c588b8b100bd80f0b06629571fe53f1a3f7e65e350df42f43303ed7d4a05868fac2fe387cadff726c379d2fac17802f05779103fc7ecdb038dec9745e930e56d0fb9fa36309dc0100000000000000000000000098c8af62e1d984a8380af0fc856fbee734dedb06b498c7525bedf6b918380dd6445b29a2ee7b6f3d4ae7f1bca9f597b2b52c9cbf0c140667961c744ed4762661940b195fc73df6eca4dbd8fd3231fc8a521351ed258f920a5987aabb3db7581c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bfcd21300d218c1124e27a27f60e5793a3d4d364c6c0ffe88374f249ffdea2a4c3f4d83e841541d23f456fac2c22f8097fea3c380791c5ef01fbc7807d58ba798b4fed07e7c84a5be8a8d4eff2534ee3c6e04da2076035954e12b479ca936c8a200d2230ac02d55e7e75662fdb5cbb8f5c9c95479ce6420c0e77de0750689cf6022e73741d3eab75edf6c1b551ef8da28d61ec36855545043d2c296b627356120b0329418da7279950b69e1267a8d21fb02e721ab32d663bdb81971bf6440466be490a06f860a06c46c35e7c0e307f52faf428a1e514a1ceacc40afcd150515ddb4d8b6e0ff50739c2817f98881e6f9b624bf1c89b019c16b9875f774c41da9a49f9f1022b6ec5f4f38310dd82ae43192bdb080d12858147c1ea94567abe7341a1f28830030584fa4f2c27bb8629725009a91c7e32cb27e86ae8eaae2f647158652a3455260324ef78b40d8fa7aaecc82261292fe3c7afe879f4a3fa17f2de00ac6f4a97fa2f032036226202ca5d45d46e514581cd7fa34f6f593edf2585a09701314556a2122d03271e11ff25ad7354d09846335911c98635ccb61dbc52687bf0876216f293204cf9102c361e4a9700ec7147f3e418c3dd35bed583e80896cea2447d7cf55ec230717465a1df2a90a35cf1d02ba9185fc55479679b8e35bfb8295f006f65353b77ffdaad665974bd97afa4a7d5af43e2b8dd0462f60e3382ec9925d152a2bccb8449bf11046629038bb438788a030c083430eeed70d6b124f0b198950456fcd8e776dd0a02c7c704af6fc599d739477f57ea42266a58884b5fee7c05874a035954763d0da0d8f5755fb9dc6e1641cdecd50bb317888e0b6eac91273f95c660344df08495efc97f410b97cd980130d4b48a9472e6b299027fec106bc5276165ca1554f39e5c39a39865097b7f6c5bcca717b94ccf4b619984670d72e63a41a2a011ab987dd439e9d4fe2840549b3909723fa1de11e96fcd188b0ca0b742b084cd233c4b917c31d6a37021ef2f7d1a50dd8df90a58895fa7dde5de422cb93ba14e0fa763656e31610565d6d24a5912354213a9c97e12dc37d20022960e689b927d313b876dac4076d20ce511bdd7989eed7bb4c7934d7c020077c84fd8896275b044fc8a92a20e800d6281034285d00625edfa99ad297e515ce1dbec4bb2439732838d6b986d1914c7d04accffdc7bbb293a8bdba386344d5d6779f28289d8191e0356ed753b2f538cfd2f997acede99abbd68969b989959fa58d94af79909ff6423e39245f5612f83d7978ac7511fb10418346c03b6fe46dba353efdde70684d6f395f98ec2a635074de8db6f7e7f1cb47322937af18e42104363ba2c0258d4b99d08536dd58a898fd66f6780a587b19b4fd2f6e0671c6112fbf2dd467cc84e131f81327d76342396adf51d9266ae913f66949e76a13f0abd2d519957ba6500b7e92ebd4cb906b3418ccacbf4a9ad87a80b074605855178eea42ee81fb9e13827545673a998837ae05cec0c881020da6e0f7c6f9121f222f5e39200923bcf31a3357d9a7e4ff3987179fa26a99c516a49a0ecea8af2fba7a19d9d836ef9a8419e61a7d487823f0b8b4cc03ed288fda3eb617f7d46f8d3ad097c003ef746adfce5a1fab50bc71308dfdb24f708228275329ff450ee46d69a3450d990a61456982b3d8ab257804314970f0e3f104895d31aefe0b3941f4f90aadc11b104fd8fe57c26b61b5cd88352a0f0ce56a52b1191630013b70ffc1d817e69931c9e5ba4eca6454e5953e755463200707376e0268969d10b660651338dc9e7e137c7cbf4fc78029316e0d8bf1bfc9815be99c058e8e11b03d03b236cf6b17bdb535836e9ef104183660bb3c269d6c603d5aaeed91124a70b024db8bc871de451a44d7a60292bc983c17b865bc1bb4b9f84e6cf23410c3a59620c5910c07aef35a6adb3a0a9f963ba09e37384dafd597cab28452b727ed9459435566125347f37b852d1919ee26bddebbb09ab31dcfd5c5247cc18c8b888c5f64aab4034dc8cfac98f8a48cc81ca3f8bb34793c4f2c91db0eaaa4378a8094b060b7e4e292a78e41edf807f58de9845269e972de666d11be93163ae3225bfdfe8b31baedd44d16d2c81284dad7b37ca4905327ce0b16f9ee7ee65cc24fe77df5ecf99dfaa935d0ee18086be63c8ab8125dae1f0d86fc8ade66da8ad9e022cff24ae52621a1ac4b7e81aa0a2fadf48a33e86f7004b5c7dffc8a7d62ac196f84378cf9c0905269add1d3857c6413529b2f3f25d2fac651aec9cf34dd886669973f400000000000000005a9aaa000000000052d4e2a33246fce5e64ee6c98d0a270e73f70bbdfdd7786f2f365681e1eb3f48b3cb931cae8e23885850e8cc4cd858d4718b5fd907d935fb25d03b70123447ae39b00048078423a011b3d060ee4084cf88e601a5e99b2fad1d4c14d0a41649c900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232d3b972c252d6090b5043b6cdf8d7691eee3359947152eda7957a861269e8195f9f4c05f86bbe86226fbe5246daea997393d82ee9f90046d6cf68da2066d83b8142b6b4d5493842cdf4c0335f3d3af36c7a0ed90ffb24ab3aff674f32747d1b3f0e7afe816eebf8dc33802672a155e213a59bae8fbcf39d94e708d93d2bc5d021273c6a79a213baea58e24864ef9102e7fdde551651513042d8d86352a17c797020a024b72e768e13fa5b48f58922da83375eda0f379af188c7ebb60ac73b4681c0a03872855005387fcce089ceda60f83c8b3ae63155c20e02b7c2eae43b2b4093a66a4807289009eecb70be6078bc6e3ea53dc2872f67992edf87eaed7b9b1b45c022463af0b7bf533def7af3afa695cbee0fc57cf64c8dc96d9c4ece1521655c585020f5e76f27522757900dbacaec9feb6ef09d3425cf2af6c37971ebcfad995c895020ce40742ff45fb5aac5b12c97f02320da96cf6c7bc1b55caf4b4512c2156ddc0022f94a87b9fe472ff79e94d8131cc94fd923b2049606e62590243d6afe9d44caa020710e2f9e5602192406ede91c2526cd1b6b696afc04a89bf3998a16dafbe2865b34f05ee61707176f8de1b6a0120e6f2c743f15553eac185ab4617ee68ceaca50f3a03bf316dd1e23e2400901c6acbd31e46560f9680c18148a452cebba24284c3a5e576d5b300717808431003423e189d549d3cbe62c2c24e2c4d2cf339c469efd2bee34910b2277d85e5ecb84a1a1c4212af352c661821c538f6cda31d22cdc3428eb01401afa5302f0e299e8ca1d8f60d1f38a85facf9f42e3264012c0bbb90df206e8dc965744fd80178d5ceaf2cf0ce10af40f082af9af2cc40cf0aa001e1ac6fdfa3b6927e4d205b719c711d03cb016808ae2efe9e81382ca5430e4042ba515d8088a3811e20a7cd15a41e9bc157c29c3e318b58fb778969b1bad508e39297928707107e4271eb6885b15262e9c4aeb1fd403c5d71dbecff65383a45cddca7dfee82157680c986cab98b900b5cfe2156a0d8ce9df21a92f0d5f1909084c743ede753b3b5c3f4fd914defb97d16f5f22969b7c2ae8c846da439c7a42c4fcaa19f03a48b7a09bf943a22d40d5e6982eb06b6f61d0cb55b9d244e38b2580b2627c8f003a656769d92294b1a85fb9850493bb1e1dd1cfdee3c7d23a79d123f49bd748444be9575db81143dd99a664aefa24f73f3123f2c0bc6f952197b30e765328b370d523d5120781bce81f72960cbb18b2bb9d9a5eb7577f6c24df2cb7ef98975a3f95dc63184a7cf2662834ff2a21e605e2201fb2014ea57171809a133f956bf23f8f857441a8548c3f94c7df9cfde5338d5c759e0a37c7cbc6bb028184293546b7335a6eecc7ea5669b526330b826378f4319ba80b5d7d7ba1f6d8c3dfecb4722d6db255acd0997ee6c19fd1855813eb1ad17c2ff37d280f6f15b166bdaea570996cbbab053f949cb179f719d13b5922e2c4c8bc9e4ba108fab02d5c850628ab725e73db65c0b7a6d66f6ff8ea6ff98ee545093baf5a358bec5b9b2c2dffa3bc0ac6961fe6a38881da2d7f12f865894349cfb811658085b944bf266c3a3528cda33d573f242dc55f8034142f88fbef07bd23b6a63dd8e6c48753b7ce51ded4cfe30b64ffde409710c80b1af2a3e6ee6b7e2a8e549c13f17633d4af13d0f6d90daf64906d95f5dc08a60c032995032795f04e7873bd712fe8930730c9210a851857a1f83e70665c2ca1611a446d4bb1135db9e3fd7888c164c1807b79677b0fff3ebca9d4f7f94813fff5c2443eb7b4aeda0b125131adc842b7140b69e0d1fec44bde0ac0fdcca8b655e9684d85b49576745117a9de0283e1fc70f4654cd2e64992965cd93c6296f8f18e3b8c956bb5ae74b8b3f05cc65cb84619939939d10a4f73ce4f86a5909349e4a61a25a76723db07f70dec0595f7df4553ccbad389c8d2f33623f827d6350e21b10ef7e997d68377dab55417ece8801cb7443548b86f494f67381f19d829b157de96b4f67f3e758462304788c1b756fa5407a38cdd1754520898bef7fd85918b3ce04b12f4290c9ca7997fe97a4bb9191f03a6e06e7cc3dcf6bc7256611e83217fc4442fd4ed77e89e569fe0febe06c7bafe0bff79f53ee63894b97c4da5b4ea6b5b0d25e8007a4c6d3dd5bdea9ab877e6c9a2898127ec1f8cf12be6bc200a8cdd64573ed68f42ed80ff712e859d8c0803cae08ec4dfce068fd5db9be4f9282c68046eb4145d8fcf913955f7c5d73468aa22855b3a1431a4a4572ffa11cc9b007814551e8b492c35e53a6d0347ba6c6dede67431dcf336d8b660c238ca55073588f0b2bad480eebe72703ce4d41db30b988ecd8518f945034c887ec81564ef3702310bf4c2c21e4d902f2444c8e0b30f62561b378dfc0be65e78b9236a0890a5bbf5ebb3b0d", "", 3, -2118255587, 1537743641, "17db3110fcdea60c990a028c0d2eadfe0619d25b3f3c161b9e3410d135ed2cee"], + ["91bf162a03327d367793a4c6fd5459d63506d08136e62716298ad146d9ac994f4ce37377520200000008ac52ac6363635152ffffffffc3721db41e2adcf18fd1eb78725a215da4950eac7fd8ffc9e34136843408ea8f0100000003516565ffffffff35d9a46103c41bf4f088e67acce2282e7a362d9d975a3c7c7f012e267ae0c8400300000008ac6a005251515351ffffffff03a9faa0030000000001519c01fd020000000005650065520099fc01050000000004656352ac00000000020000000000000000ee957e0400000000b2fabdd09efbfab1cd4e6558321c3d8b16b63ba939b81ec3ab90d09f5c210275044a524464ca5b0bb2c4d078ba63f1112de6bfea96a4585bf95b383bfb9a85f7da49a3da6e43c96966da75ffa3bf346795331419866bf4a527e2404a7a6c7c8c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000efdce4f295d7a748b17c338147ed741cd153c1e604b25971d40fd4d3476229fc51f36ff31c4a123111d73d43a9276e894a912fe4828c66bd0806b8a2a763eb1fa2a2d0d4e55436cb8f7a94f1fac205d4ee5cbec54c65d25c20ff059829fb77e39be9260ecb0967eed05bbd34fc1d18c06902515437c317c17334a94debcdf55403051f61ff23df7cceabfc28fffafed2e3693479c6172f99899a691a79ebf0108d0220bc06de6a43a93bb57267779cf83c67513d8bd2872abbf5cbf7f222bfb9890e0b0880956a14aaced5b5b4c6671cb440fc89779230abd94bd339e1a30c28462bf8115cc393d31704240ca98bfe4771d47b244152c2f3a2583c5df9d92c45dedc12022a498e93743c04afbb7b2032dc04f3bb1ad08731cabab073fb11021a76504255022a9ead057361aae44bc5da152a86040c7754d7ec52c4e6b9ecbf32ee3c47bb4f03066640c6165dab6f9552887cde85f6aa420fc2926a8fa1f5a8658b36fc755118030cbce06a067d86f8520fea0b00b89d0b1c0be0a8c684b05a315f6fce1063e6090227e8ff463db0579ef37f2181fed15b3ac0e22316e6974af47865d3832f7b16f73ba5a02e99519ed4e4b40abe4dffe33914af945ebe4776e57dd801651af58c89372d730ad5f86ae73e24ef7c492d6c1f1fd056ab02757d802ed4f73af07b2dc5eb80099cecab8bdad67bc63f6dbd67ab179acc7c4a9f82cb48264bfd3f5b7677afe35024b31bce184f26edf616fc72642bd6136ae5ac14dbeb27a222be2cbe70a2d3bcd36b0425ffdd7691b5df9c61e420037360cc3c6989e7098718d399232a4e96cb3d2998ce14e97b10573b48811df3eb42a79d4548c2102b7ce603b3cd71af27d50daf06aa96ab3b5a8d266077a9b14c3a677a37718fddc35c351cd63df00843ad7b82c347bfea4686ffa2c1cad7ce584e8748b7d03a2bcf3380662a84286b461881a6f8057c33cc9ab0682021a6ff2e7d57b000939f2f0bdb73de1b90a54ed9da87253ec9428d8b7a4923be980c7c1b845d041e2c61e1b67173fd2669f4137ca2493c33b2cacefc7e8c56190a30b0879d22719f992d25d0fdab4d30d1f2804b8ab89975d57def1badec67a89c3b393d700b08a33eaadcea7d52fac19d008f6a61ec3e8a72fe15155f0264c078abefee2b91ee27d245b5b57db5ea715c0c5ecbe31bf82219cc64190a0406b4858ed7fa897503374787c1ebfbe62e40599ca79516a262a0cd294dd8c6eece3c1a2b018a9575763d5cf6688b5b6148222bbdcc01ac015fbd098c5530f46bd9150706defdf2e1730fb3821077ffbb4641986cf224ab46c966e3a90f685a19a7213e3891d7b7631266c4f3353d546e51a260479aef58a5018c2f2f432c05eebbb8a89d10a6f0b312267d2810bab475e97c880e4316ff2e245be5561205c0076a8ac51ed9dfca90f9ba2e6e76c8bec927a297f0ef1116f1f8bba20ebca313aea27b10f6fbc42368045a3927e29e11fbce17ac6be56deacf4250fcaeec686c3de738c3709b4c774d66f6b44e7840d997279ff584849b6a1a379ca6df898c5f25bb18ba3aec24064485a5c3d1a671d1ce71552101abaade72858251962bbc07be04f430f373f412b6700e5bd12ebc5ec32a05cecc2c1f81ad802d4a4f9374bf0d6855163c715bc63f7a70ad80d61010ba343095b0b9e1b2dddd932e37998311730b12babff029343c8e7aad356a339b24b095863f83745b9cf3773b4c369fa5f3345d41e294621cf4a83e6ac776e03927212dd686e419bf66763fbf2625ac1681cb9cd8068f4dbc481fd5174bba0f63711f3857e93ac39b05aeacbf6dea906c615ea21dea3624347ed64010746441c8cfd77f650796a12c006f9ffed5cab14a7924ecf068de062b258e3a0d8e16e80c08d44b4327a750ecd8b2d27697ad38865f64d2d97104e2b88893f019caac195a2df8b32785e348b3438b56dfbfb3e8437a3525ffbbb46fbcace41a23e92b931f882bd742a7322aa4527fb5045b413c4ca81a8f93e702a80fdedbc40a12ffea1681fd79788ac079f2421bc3fdae524f965a82d99aad00ad5d74674b4abfed251092a9f8bd78ff339ee788cd39810f28c2ebff7b18f8eab35fa32f240bf9505ad3f056f4afd079580bd9358c24a4619872932c4f798825aae81c1e1b781ee5fe1cf429b03455440c49cdae54fc54065b768bd395c0bcadc73eb15f452d2cfa81b2a69662ebc9c4843688b8c729854250f528b79cbfc559c64a029486db192a562f3813caf3234da09bbcd748ca518e86bd717ac8d2f1a55e00000000000000000ce47601000000007b384ee5ba1475609fc4e2d2f76da71d6947723a88b9a47e421b63d59bf4c682f5cc75a6ae0f6f13cc660ee4a9a54dce059e84f149865643bf3b0766fbacb8dd9125300bdfe7b99f4c69c9ce2a0de23ebbbe2bc44b0b6897ee47ad7983277e32000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e5fc7b83235e9243fff1a91f092de36830b24945c4ae59561ff2ca849ed90a78b92127faf6a5de33a571e9461a24769064430ba8c60ddd6ffa8a46a93d19ad9d5b867dc166b4377a09aff8c7c207cf5b9e37928d4bafbf82202dd390374ab87e000964278b4581246e0b762dd27477e1d71b3aa8d9f86e5c492ecbe2d03b3fa03287edf0b54710b353fff8d949965a17691c0884c530a31f39b332b33135fbded0316ebafb043d8bcf6dda0e3a528a3342ad780c21c1860f4c7b3d1bedad01048810a016c90321b6c884f90c96128d231da718e0027256a856e8f8b8bbd88f0bd0160ef25f1b83284f2f06f98b18f8b3e9d78438ecd98fd380c4337393b6f447d3d79021258c44c822776ee3eb1f52ec942f7b503bc62b14d0aea14f1713ddbdba40a450204fa13db446b38a66c55b5e56c9470bd86233ec25f33fa95508e4efe6e62322002014b1680e8a2cd7b1d1bc96cedbcdc88ec5a2b9158d507334a9b3db4c7266a5e02089ae65d72bb03d61fcf3f627d3ef5c526b99928e6549e5466a5a718a32222c0021e5545eea5ec80e733f57ddcf9cf04c22aff3aedb00117cd621dd1413386252c60679602312a24b0c3da79dd89375001894ca9a78929f9afa4b650dab120d58771a7ff3337fe9252fb77ebd02a7333d188f20f5a1d8963ac48f70ebd75c2d83d80ef56f9bc3e948e8ecfbd516af02dc975a3cb0f256e79f8b9f23aa53008df9e4a232d767733b9cd96f04bf612d4884519a7efd19e1558947aa3c6c9829e1440454c57d9db771b2f73380220a10a7823c918cf0287f554676c6ec32b706f3f140451f399378a03e48ee1ae0e3ce043beff782a4c5b743bfa0d0df88eb4b4db1ad950e92d048336131aabf39380df9cb176359cd4a49d6c45c1c1f4aa0b4ce5ff1b5180130c200e27cdf68bfec6cb5e07b2d16c4a41b7e09fa7c7e0a5cfa76e783edcde0672870f5832be565556310531e340d7b6a9a2a934ed08595796827f70c99332564f5d24f84284c3e9fba16b71e489f966dabef3704d4adb8aca60ce1bc6844780993d52931214454df82fcaf581fd48e2fc2709e3801e9afdc74d2e481160b43d4174bb5670d0bf81568eb93f94d21cc9254a74aa2b8de2945a455335d1bdc5afa25150b742ce37df57dbf2c178885fe951176014fac74b00f9f99bf9c2ff872cd2db66a45e0d761757c29760ae70426c48f358536b3637ed4cc82bdcd2d6d2c25c5a23a2fc742f84047273bda7af3cf0c5996b05d5045c27bf788b7b70b7e2783455311cc6a82dd47a03d4a40039591e9be3eeea8fbbc230e18bb804987086b4c67f7a1e190eb73b58715fd46bdc2033f9ff4b29ac04f8dbf7343af50fd77c4e1ffbb323a1a93191b7d8ada6c5049e9d09f8f310ec3cc72ba2f3f622a65edb442324aafcc74c554964ed7ab7267334825d7e4ad5817b6efa17530566d590c826e39595043f714bf47a10f978e57e72c0eabdbe537a0551885f807a1aae48137f322f6f517ee64462ee714894b4db1f610265a50961ba0c3d36a4a91f6db27c3043fe7af7bb0cc59eb1769eab42df15f50741fa5efb60bd69da7d62143f0d37052b7bd7f5bd1aa3fc886a5a5542089d64f46a586186a3f2c7941ddb380bf73fbb3fcab62be286cac1ab2b737c33e1858e60d970651d54d8414ef1b45ce1a2d278fe4f99f0d43bed45fc66330d8e22b860fa96850265c5a7b8d97cb537a24fc4ab20aa6bea05a79ef7b95dfd173f96cc25f685816ee352cf1aa745fa9437b1d983b82daf26f6dfb42a5f06c742b8b9495d9dca4a428359924a718b982ff383ae2813e12102f87058e78f5a1d3920c1c3909f4a9f46dfe37ee6e6d36c0baf5339eb702ee078587021c19065f9626c9a8c18b5e2593f9bc8587b39c3c95acc4339160f43848451c4370961a1b3939257b68f21bdf4a2830a99fb80f4f2c31c5caab6b6bf7b83a7fccff456593d59990da046c453f2943fd44bc745049da50eafe292776400470f32c1cccfbdbc121e3ae0be950af5911ff8e5f4539adbfe77260a1e901f8f479e000123f5ca9b87ef9900ae6573b7801cf5fc5a904d7475b12f9f47c104b2444d46b6d31aee1784c44a37979d1afd4dd0cbd50fd84b2d15bb5aeebdf23b1d42f3e6f93ded9d1f677a0efb03f152fee8abf6da052931f87e4d90cc18281a394fcb1e6cb442b6d2f322e2f7a17bf4b30b30fa0ad6817ac383189480a2d8b2558163eb954e8f1ed357d2c65fcf509ed3127c998783833d28408fe28773b895b651665c0ad87c8e3abde43e9a2f966a1c5cbd8a00dd2a69360c8c2536f0936d6f6da507db609613f6b05f1c983856d16b351b7ea23915733a84f41be236d3ec0e55ef3528394c572cff987e8af96d3668acac3f1e51cfefcf605605530dab7beb32f01005a486bcf2de1d08", "53ac536300", 1, 1595648217, 0, "4c48bbdd660d26379e52e6de8c4edb4eeffe2cc3e10d5aab68c62dba67c5386e"], + ["bc23df2b029b59bc4f5b8c58cf0a9bc05989c6852ecd576427c24cb1a31dedb5f736f260c10100000002ac6affffffff41461df9a8e4766fa1db421072fc58300822235d508240f293a4c613b90f27790300000002ac53ffffffff01c83a92020000000001ac393881b0010000000000000000064eed020000000000ac37d25d79362f92faf23f395af4e6b78077f5bf1a841e6c93de961eabefdc15422b7ef9a997352c6e9d2513e3e9ea8c43d20251bef8e9e22d6b3bd040e838875068a010a2054160cc2d9a9cd261401c54e1fd64fe449e73ca62d772e21e96000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005d3b9f93c0d2d0df62fc8c649f5892862f6e42c4f422495ad90dffeed192538252dd7c2876830927b5beaf9ab635157bd3cc6e5a017e7f7ba14822b30d021e165add44b8d2d29bd7a7aee03de4b9f7bf52f926e3b345a54fc08df010f054b5fac1023ed7b8686d54f67608e734415ae8732747d8bf498fc5e0ba6556c66a4cf7031b6f3fd8fa46eda56a6f936688d70aff7a88b7d67c2bb25e06f259a68ae761bd030400b585109707d5aa8d404b12bcd0224cd894b649168eab5d46b3e69edc77590b01d09d924c111b79a84ce9efc8a974aa6f458afcc90481d915b6cb02a667ff847a81860fb09010a1aafdb9b38153e9c6c98482954258947fe145bd72529435a8032262181e2cebcee2c92db9a39a01eae6623b8d6da7063fc25a1a9ca0ab11b0fd030be53b12840035377ced19313ce03f672aa6456abae1aca349917e924bc14b320321c2aefe7ad83ee7ea47a1a647b37253ad0b1628c29faf1fe9708ffe9e21d7dc031ad9a9734caa85c0019a0d7cb2c5fd39d8b36a7d3e114441ccd44b346fe3cbbd030eb70496d1e8e47121872f8cd331bf17c92277e7313737dc20b1d9ee0b986803f5fa0a118bf843a4d91a88d08db6cb06453356ff77f933441ef4148a70305d9ed653fe22653f8fb9dd19b237cdd8d5ef6b3018dac28f3acc67f1e0adb787e44fce7453f7f9d7840ff26c634b7a7b0e4dfde476a8ca377c45a32a290f41dad0773c465f614cbc4a7855f8404088276e6df7d217b7273eb175223743efd55585aa84782d6859d3f279ca37a1180dc18387cb8c5fb6d3b3755fd178cae45b74cd6eac7738b589d0540847713c4fa90e1e241885ccdb469847828a6fd4a50bd02b5f02a9d7cacf783b5ad8eb169ebdd445dbe03f3bafe5955f6ed26ee75e6a52ae8d3d16d49916d9e8169c0ffa8dfcc3b77b75e770204c42ce962840a941c463c3b250a149f66cb747123bb0ce65509d9824c084b8558812d6aa9bbc24cc51c45a539e734593b27b4ff44a83e762dd3eb93fed831140886bfd1e96a2cde35b82cfc253cd76f16cb44d8c62c5ca1ca9ca4dfb3d1ccb76ae62d82a5ae015ee7873910a9b5b704ff03cf2c13ceb2ec46d0f4c16d302a97485d17f3cf8cc9e7960539527a704c311a6159e7901526e9f9c49282e47ab620872a70cc30bfd56501d453980e65435eab521b71c78dc604bdc55c49211c1414544659ab71441682012ee38eead163047f1aba4e35d02b86e1e8f6eaab491f4879f1bd571a95bf09f5220524e167466d7250f1b72138d97418ba1945c8e5d6ca013ebf7efd3fdf4eb20be6e7760f2d67786778a8d878bb9bbd8f50c92ff404545a8c06baa36e7c9811bcd6a2fb7bb3e1b15abb3948bf8d7bc66f551d1a5e3cc7f2a4f53a3cdf449ee7c5372c3da343c1ca89f478c182cee07f41344be56a6b63d347d9beb85503cd53e889a55fcb9f20fc5789a83480c8bcf68f241d71ab99ace4c20f8b48bf091ff3225d4aa2503e1aa619a440e96a53002a385650bb922ccc58d246a406c01c0e5b82eadca4bbe8e82ec92d9f30e866526c3f00041d17d7e3671fd6ee929543a53bcd08bdb7d60bb9d4504d39a4c84ca5bbb8a1fef106df9f43ddd2c95d93af260a1a0449ab7a9884c63b2b4ce8550b4d7b2b54fc702ea624390081d5a479dac7ae6c5a0afd54de2c1fa9864f47951171b0bb87900982f68c5224557bad1ae4828932aba298a4ad6a4be48d404329ba9611e9e583774076a313e804039b478f9de073e2f23151270ef47985e8fc1efa88a7ee79937e6e0f363b6f58f276f799147f4801249fcc9cff89b7e5107bf02d6121b5adce4cdcb95f03ecd8cc84ee43103caadf5c26d903ab4500a071140201097fd56475adc40098703002406d9126d4e004c926eb703e95d00b16ec21d04e64d203b5b4a4893f54c9c6102c9c2ed3a618631212bc9b365d57f0b800a04cfe8db57395b46055d2c5187e16015016db5300781275c587433b3960bd47f19cf34532ae7b9ac365a405f5e0810129830f02b3cbe602c7898abce2899e437cb8e0d638540b51c816a7a12adc3c4feda8784b5742cd652842b70c17f7aefcebecfccedb35f503c8a2a0ec14ebc20a1d6af27de0548f9344130ecd3376cdf70801da160a9eb986aad85a3c3075cde895f4c361faa61ea6cf0a4a4f03ef1657c6b9e85fa371ad3a9fb309ae585efbf0a1516cac532b39bdcd2b28c50a0ca272f7cd2483f67de8bc3ccc5c91e8a3592bd0435da336ab0e1760c36f4aa2f0fbc80feb54c7ff9bc5509b95afdbdd6102795982dcb388491526fba52f041a464807f9c8c10d92a99a7c5c35f1dbd43a4f8df2ab43de04e982f1ff9934ec89dd71036f0ccc3f2c86b607951502ffbaad06edf4a2d391d7445126316a8b4565f79596a1b71cd9fc5a6745aaa0e", "ac6a006351ac6a6a", 1, -158057810, 1537743641, "9938317ef35cadc6e1229e01fd07a7dbe52fd5d23abb6e6903d429edc0cdc1d5"], + ["2da3be4303e1ff7f1650734b59053cdc10be0b309c02dc2f73b780b9c3525e2013d35af358020000000765ac53656a5152c7934de9d91ebabacd0050c07445198c93a29291cc47e84984016f65ecbd0a89e7a67e980000000005515151536a3302e5248f027fdfb59c6f847ccc870c610dccd8a854113f18584d3f9bed441ca81816d70200000003ac516a23eae71902561e4a0200000000001466f002000000000351515200000000020000000000000000f2a9880000000000e7b5e07f4fb9d83477fd594b49d44152968ae97d7e1b2a447699c07dbe5364b85bf4de6399c4f820bbb6095ef652f8d80b28d5f3a996ae82748b2565545b1a14e1c7be70ec285b3657fad7b28fc111b8c556d925b71ab59fbac59124c3f7372d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000927dba415ea5c969df51a53fc9cc579283c67c2f54f8c7b91a776f683a5bca570d421ef9dbecd59edc77124699de1b1c01a21fbca01bd154f23adc86476253d80b326388a394402a768f87109192fe47ffa0016880ad5cb8af7bbcbc5349d96c5a9be43b170fd6c9e5af249dbe118e16875ce6519815b0ebb716b2be5398054203222188b036a9579e7baaa31fb7d2f74457ce64ff2631807123602e1b3684e54a0314028181a0649f2042b1ff3b59ea477dae8f4628864e37e737c7d87f2bb8f1410b01b2d459d6ba05e5ee046be20c567fcc23e9be8404a8a8982e44e80c4945d525afdfb13555c971dd590522fd031dc92b3cc54daabe4df9be144a6d169702383e0201b7056347935875c1b3ec7f84c4aa253518c73d709d12757713f975fb93fe79021828b4ff73f4b547b32f81d2cc17d1e00dca14220cb40b9bca2fd401157722e80315e2d5f6baa44c2fa4582551b830bcf57ab1f8275bf66f576afebc4398f8d836020a3d0e94c4eba865705b86f24656cdd00e6a812930153cd8196b630136d1b4f2022a08a6ec5898f96c54207eca0e737fc9c7f0d2017140decc5169b2d240ffcefd77a35985f5bfb0b8ade3cc4650811cbf3fcaa8eb640e726e135096271a6a7bcfe319e8bc18a886bd9d9684337a41d63dd7a985aa78793213275bf95ee93bd9582e57ab4b46f8e1a05f54fd2301db8ab0cc628c0847e55cf9a35c5712607cce161b1256be3b907cfef6accbccada6479c062a16515e7fd8cd3024bc0ba2142ccac817b665cd5950211b0a77dd92a7b5fb88f6729fab69b2a221d58a00357b88807da1bc6efa47ef25d3c046ed86257806b0badaf7acf0eacf70c371c6a45422d35d936461a27e4d9a59f3a5a18bf3555f51fa1c7bbb3a8b2dfb4af2df0a79550ec7d345342b6e6da16c47e57f0b91b44b536609e7e48f8290dbacca28633ebe243eb59497204960aa0fdbd9a871941b7594ade2ccf15433288b7a86f0f2f273226ac815c2de968d08ce97c8f33ca80bdf8fe01c36dc729a0cd797de3040ec0b385b7395713c4db69744f2bc05cc6cbf016d2deb3333da917f920ea6a1d66223764bd7b675963efd23b9b9ab0dc834901be3f8d875fa2406b3ab13f1af6d20aeba715cfeff6b7d74610efeba99db1d5f88552f622a8dffc7cb5488e3d28165d45b87339745653961e5469ef74654c2233858690818f22b0eeeafebb82dd2002ec7f71f193f657dd21267305f220684983538808f9c0ea1da0d2276c1e7d84f896e8850b841ff72d5753c06a748a5a51783a837f7bc343fe696fc651c3b61454b1ed186dc7af289ea3de26f706a845b4515dc123c6bfbb4a0cd22bfddaa8ab801d3df974c631db0623bf3b71a54fcd590ec0b1af245e8dd309b519b193d60b61990af6e244ec002ee7dcc3f6cca768611f852f3ac94f28370eebd00d1660e752d8b06978aba1604808022dc157fc8345ef1fe5f41734bede35163b2cc858821e99177f02e69b6825b8ce01e2c99f201ad179e2eb909a7326188b649c833e780e8aa2a6f5795f7556be5fbb4b2ca19f969a8ea68275bca29cc4b39e5e3ccab251b8f0e6fe405529a108d0a1f98de5579fa164267d5932ebe929a3429181d05d88d6ae797ad1676ad9cb11c98057019f617cb0bd3e5eb57b94f60f7e05c308fe0850830b0fb78d4ce67bc516f02501489d2d9c1258d7ecde855d77fdd7ae5c0667014c5f704dbe8e184bb2462746178fc064aa4b188672f8b444cad8ed9bcfe504b462932d6660d08a3fb012f508aa7f92e882b3c7ed00564e28653322ebbc32c42589d2426e67fa26c7ee034fdd025b2ec9b533ce1e39b202a7bb22fc36ed020ef67f8c3fae5ab4b279b990b67337601da7df544bc24537d0ae6f7c007b27ef2ef93865205ae5f511b858a4c4804671d2b41212d6d40d5a510205e755c2287bd79e90588157e40963ee80cee98739f709c2401a6828f929b4b5557bb6c7461daffafb16789de5c7b64dde96bcafe5d7824fc85e15ae70a22a1c106ca4f16b38df833f9a3b37ac703350943a7f241828eac9320f8213b6d71d77470c3294f8ff0756b88ca77634d3cd53fcb2e5bfca99c20b5ec4f5c54de5aaac92d0ae9c86f08323ff7b63b0d70daeefcb4eb9f572017d07ad005b16fc99ecc6c700b7b5088f82092df124e4765691d379ce83c30e74ea89172e7b9a699c14f0a7d8a1194fd9fc0a47bc40da1ccf7b21316c578a4f56297feadc3c8af70d5a71e64273f7bebccfbd4209ccc4db58368ce7864847c4e88401c1bf2000000000000000022e5250000000000aad986d35d193df13166f316a05b1d28ff015484d71c0164b6f410d1bd0a273373d2e2fecf31764476c4fa8d680a5aab19c4410ed3383a60e76c8e434405226778175cd233f73f103143e7731d7c92cf7407cd1e7dd6536c81dc4ced94b3a4300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011ddde7655a0795056557dec1aade092c8b144e03cb2117f1a5592297966b431057821a9e2e694161bbba23e35df8499a5f4b19c19df403851b66d66b079ce74edf3a746661dfe6eb850ef91c36848a04b291c4a3039b016c11539b8ee871bf76f0772f699faf2ac3544e99be7f7575be24893599c9a55d00b5380214774c25b0228a89bfeb9c60e2de704afd06368b01b401246461092375a7f0fc5de86e21f450229766b45de1685a055f0ca826b1c9fb2431fe8391e61ccb6ff405f9c510221810b088a5b3edb012adad3704747581a39d956f8a3b7ee981e5ede17a9df2fac7045cc8dc91e492ea30dff57397a2a551f088709386948564c5d5eb2cef9f1776b5502196b4f67f2ef426314446f11cad10d2a7a3b94529791b06c58ddece6dda18912031002b58b4b7581b06e1d2ba0baeba5e8689a9f08a376206a06c9f7bb7502fafa020deaf3745ca026cc7f765a71cb9496ed8e72056a34c9be442fe60c17eeaf38b5031cb541cb98c351b0e0941276831697cff31f5311fcf4fa563a46aa43180e596f03045df57992bef76aa0899a940237c314c3099088034954a3c8e399d759d5a1a0edfa87fc1a15cdbaed7667ce3b4758c21e1775de034419c6bd097dac40461484c388ed2538f6b80e11225db50f709a2fe753abdf0d10439131ce9dbe96af60966980b6c24642ba1228b9bbf4f98e1d8c5e3d541d83fce9a86c7420a91c5ac2c7b8bea465873a3a0a8cff481f02363ebad56fb3eeb3e3277dd036d77df95da2811e917dcbb8ec90958865e12aa7569088fc74d0b29bc082e8a7c31958d8764ad0bc98a4ac0ba5b8658e7b0c14764ba531da2b279d2fa674b779ae663c12d5cc2fddba25719f0aed22558994cf79582f369850603c2eb93535f896bcf7fbc171907047eafbc8951910db5f6f5f78f04c47cf9a7533d24a1e7eb925791279a5c9b03718ab6741762894f99f46eda61dd393fc3d1022b579bd2838422259db18008be001f4b9cd9207b4fd2aa6e639dd35397310ded79742e12b3f4fb11c192b8e079616883d83b71ccf636dbe3a18a5080fd4f475f2cdcd2be1c5a6f48ce4ba9046bf47d14edee0478855c405889e680bdbed489bc4974e14f5c5030ef510c07d6638ba7b59d134a226225f915f75f79e8237498fff54a6b0850a0e76a64e911b444ca37b4539688624ed6070293ba0de03c59835bd3cf3e21864861171d0b05ffc90b67180569e1a41993fe7097c5d54442ba485e98a8e88d4d998f32680961247a7c0b69ba2d52b58a194b6361518e263879150b10e27d0bcbefcee01ceb964178c3dfd4b4069171f3b09bfa86f3f33c4e6216680a31274c28c7ba3fa754506371327be35b04efe872ee4ad3f1f1bea80419343a6f92eaa6ca00f793187f5a34a7e25494b616f302f6384cdc7581a89e0209e6c6b9a2ef0ec2e2305b5c02aa340f99f905bb8156dea3e352c6db2a642175faa9c5374ff620b2f794cbbb2404156ee8820ab2eec50f7426df149b893c47d353ad6c9a9847a5703b637328543eae4d987b6a783281246f1375e78a604a295dec39b3635649cbb21671be111f366f8860bb0dbb1b4022b3d41136790ffc0c14111f9257a1ecdb81994e80c86532cac8ec1af22256a9ded0da3febd6cd6bc6ba669b1907420d375f5d2ca734931c92981286216499bf6dc1b762a27f9ed67e9b8f7b82a33f459f67486706fc78e0365d19952fb43a147df736d659522e34318f436d664841ae4d83496e617599943b048f751700dc6e020d63c5f2875126b04e0680c81d27ed321b63267d0bb00b655d00ece21b595bbc540a52ddcf3a7ea9d61873c7b494033ef8f8332a60547fe6d71c1580fbc74e7ed4118a8aaf80ac2641fdafdd6c0703711424f5e6be7bddf20fb15a8ea020198cc693cea4fe7641e3fd386d7225142c6e9f05550123bd656f3f270f6d0b90afd502d86d335953af7d17cdc96d48c89a2bf2cb76fa8976e5693e42ac03b840ba2ed1cb57a8111c0abd9eed5bf86265621a55f1fc4cb80f181222c49917e69a85f027fcc2cd0218d356f13b9d5f33688caef6f85e6ee64121705c15d752745034ecf9c53262915ae0d3adf3d33f512698b114f2d3e88bbde2a4e50a5a4b1f4b36aa9d64e7b31849d7ac9d53d93e46480a611bf27e217878256307d735fc0296ea4ce2aac0eca96b5565054c7cefe7e52ce603b61989724960cd2b3af1c859754b46be78d9b25f94d92eba6d412d83b2cbb3bf5f05ac25c89c57f992af43e7a793a133fff4d06b3c6895fae7b36f68742d2ee4ae2660a330fd47e59e20f90439275fea0eef9253e1877377910565a94b321d52f398250090e1f06bdc15228eb518b7eec5420ddd5c2ae046baf3ce3740437dcd16615ea37d922e456f47ba4277b21403d19a6c77eac7b81c509", "6a535265650053", 0, -1657081709, 0, "456932b184c52f860e0692410cc0e8183639ec9651296751a1e68a3f1878cce8"], + ["030000807082c403015cd8aa3ef532a26dd14a96c632389ec3087ddd11199dfcf2d58577e5f60bbf40000000000165ffffffff016887930000000000086a00006a655151656dde3dc64bf84d8f020000000000000000c38dc005000000002eb923c0506da12599207c508495600523d22c287161ff8618618edab5ec3a245943a72b8044f99e23984d921f0bd66962b34e91eb23340c3878ed7f72b90520ef7fcd48314bc49259fc571f43ded88655bd169cb5bf4789eef7ac3e0d42d011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d1faf2c8f80e783fd3bcaf5264675c7561317473e87636b26c14708145d84200616706a342468e3d0475e026392f1b23a6e723f09ab22823686a82bdefa51ae05ccbf31984f63b9f60e0a1ca59541964874b39ba2d4e5f153e55b6717c75544c94ef813d304c93d2579f952c8941d9d2bfc1cd96ced086fb10b70a9739ae50d0308e49fde9f80c16fd1a27d676f40e1d029c92824b6308d69c89bf05e5fc3148b021e21030d44e6d30dc4512ae6b1f0d9770b523920793336ac21f8d461afa5b61a0a017b4865f347d51a1825679cf9e8c05a6b862a25d42fdf71848a69b29851957972ee22133600156c55acf31ac9a7c9253cb33d1e6db52d1f90c1f3646c073c02022021c6704eeb0b6c576dcfe469d729355273cd13f64235d8d88e83cc5e273e2603135beed1a0ddc15f9def4d485e9a047b285793efaa59022f455e4f0ee9223fe50306cdb04af54a77bd6ac8f6fd8e1c72b031f376670e7e46e4f6c2969b0749e628030fd920ba7909a66af0f03f9f6df5a7b553076d6b3e0032a34d17d06778f83e620314643afd5ec28363673ba7ba19a17f9491c90a2e6afda061bb5174350de4b7966c81a149b203805de1f5eab3addf9775707ca85e9b9cf7b2474a55c664117d243754624a2ea695237e3b68c5bddd28430a739d28401e3c7e123a5e067dee837c55d0660fe1c9bf378b14850c26242888e58c38be417e0293aeccf5e14239f27c24547fc915652ef7c01a1215662adb3da539f2d70d914cb260357bcebb0625bf4c05f78a14d685032b12b2b08b9574f2e89fd21667e0e5151177f4e145ccc3dbdd1d6b3a7c67e1a8103c6e1ca771e6a81081d7183c5298b420434b32aadf87d42e69333b8cf92c022ba839609c0ad45703276751e91bd432986c163ad883398895162b88a9b662ed77fd587147611a9ddee423469c655c40d388e3fc89a9e71360ca532813f96c34a27158b45198f02f2488071e78f88d9efa08ead380dd00f5dd10c9d6cea56105d20a5e1909ed039ed7b591d9345593c93391dc9949317e2f7691b9991f6168a306b134abfeeace7bbbcd01107f9520d660f3bdd0e8a740c04ae02bcf2fde9d06d6215285b1ddc8b6ec2abc5cce9b77efaf0f379d7c22773e0c599ca85fe5e1947dd460eb30aa7de29eab806058ec6f01e72478fba96ecb8eda5b23105e2f4d0e99e77e2938203f02209e5c5aaf512b38268e6b0b84bc42de2a0a617fb3fc3882e3c7ed1b2e4910b4ff34206e3d67d4fc4f37ce7a6732f82c3606ebde9339b68c16e50e85c08d2696d3e7a5fe2876e257f91f54460322d04c8aa8e176ce8852b86cf04ad272d96060e010fdfd02b5267ba109e9df226b6277392e71cb3f0aa8f759c645a716f67cd578e0d57d1c73665395c544861a64417fa249f6158f892d1acc3f37024786ddcacbad99ee1c85574e25d6cf2df4d5dd990f40bd90aa18b8d6e15e7365fc3e127343bbf9b90940bb9cf1bbaa19feddab24e63e61a689a6101cb0b9f340aab4e1faf054e78cad3258cfcd7635b8e4935d0b6176bbbfa677986b46a3978da3372185b5cb0f9c942e157c1f2a1dd8499edfd35a7226806b3ecafec8d57eed0cba17188b7e4e277870c9a07ee8c9e4024bf186558a9f63021195672d19eb64d0dc940e06fbbd49fc807f43fb02aa7b289cad34c571ca318932e3edf450f1534e1af65cf9272c3f5b71c3c004f6625e3d4d6f08671bece34566f045b2228a39e39da5e8e2e0554ae16aae041f0b4b02661f416ddc80548e4b56f27839deac7fe31c82377793ff449d3f03815e3b3eb1ce859aa2d4ff846f1e306e0f1088691281c1b979a753e0e9334d158e3c51ca3d925fd6b62aa873071363f55a0553f41f351877fc66b0dd4b7906f417d5423a9896dd7496e918923885395bdf1d416f4e3502a3338785f1a4f6e11a1c818d8c24b3e96912a2755567f6931a30c1a73bb126e540b28768f6f7db97c4f11fa6f04bb9334d5ce0aea1208f645572a67b9b6f4450edb9dea2c4d251a7f6c8dbd3b95877f40c27bc660e9247faaef1b621a1ba78035bb14b109c9839f3d8aeb68c86ce82ded41eeb71c9082aa08bb45750b205f70e0e775d59214d4af75600566fa495c806851e83b11b4ce0a26dfdd63ed21a9a2a402771ec702fa11781804adc27a722e4672fe8101175a4ee721381922b46e480b2286693d8035fa05bb10617a3638361bb66cc9e628b5da4902d447d9d05a406652700ead9961431412ee4cc4a783012cb0c256dac87bdd721292736a175c9f14900856877a21b00000000000000000000000000772cf68843066f8bbe96ce9aaacb9423fd10bfdc72362813742524c4b3aeabce94ffdeaf646babf67bf5f6ececb61e185fb160efb93bf112584fc9c367e7e4a6cd265b0c9c4691288142d1624827127def7cfaec4c2839a6fd6bf87f4622725a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c4ecd0fa650f1cf831a8aaab0f80a7b2637e60cde66f595108433e2dc4f3b2ab10b7c3754fe294e31c3c09b94ce2c850b820bc64d5ad017b41f460d0719e2c35235be3e5eea7783d87bc2cbd13288b080f5f9fcc60e736c638f5009e00cb3d43a0a111d3c2f534272cf3a62a1192b290c11071413c57e2c3913e0b1a2dd5a873031e9a4aacf761b4a0ed392a5dfbeb8cc5718c5967da3615888a51b055f9f92ec10324f584ba3e0c9881effe2921366e718552fef8346774769b145131bccc43edcc0a00d324277a7c5d0fcba6458f3a6529beb72f3df4e1742cba6157710e9eda7cc358b1fa07b61dba5f31c3c55f4871f954191458a37868585907a7235529f973ae020775b189c5d5976109b93ed962ae5d094ee28b2f7a52848248193c57efca6a00020f2900f5911ae98f59d8a5ea7c081ef5a4aeb50aeda3c2754a5c64fddc56a1db020a6a498e970f73af89034e187b25a0fad264c1135808852d72e320139a12e3550314661fc5d3b947310bd0f407f4941be89a3a73cbb0564eb525766ee7bb64c9f40305bf2b735991cf8416044f52fbfff4b25e92b76d5c86f213179a79e8afed74304dddb802a67bd94d47f1e1901023e23d2e4fade4093d6722896b6c75f44da02719fc37020d414ad2f55b7e22658dffbebf76dbc29928ab07798bb9aaab666c64daae412629304e3910fa978cd70198666cf1056f78a875aa6be43c6600a6bab8861d6bd9bafb95ccfbcdf538cbc9a19409db2559fcf1260d988662244ec9a8b8d76629320c5aff4b45e91b2f44dc8ef87634cada8e1a2967ef5f0fd2227f5081ff6294894de1490d6fa2af988381c15eefefefe1998f028f8b2bb25912c0a738ed594733eb69646ea990ed05f0ebe7589f57707ef3651fb6b10443a235d89c64fde5215f01028daf476d2150e130d9d25397015d1f08a8ac40aaaba54ab67890a33c76386417f08059110b09c9489c1a8839fa458a1c6b2dc3ed5b1cd15aa90736a9b3edbfe6c70a734bde897b9a9e7c5491ec912d8248d8bda15dbcc0d7e5084f49d5e0182494b15c60b930fb04c323bc6c074ec3b0841975385765bc323e44f4d5fe67c6b9a1d6add4725a31702a6e4b6679585c41d9b201da4b6ed3fafc0fc2e74cd0d1f756a97eface9380e490627a54fefa9caae4bd756bac4c7afed7d021f95778b435c98efa2823d78db92eb425bb1508db6669c3da2fd61dae5d043cd2a67c92b4be25dda437c46dc6087253a81e51c7d9d0e7ba706e90206f542b41f5b75f0bab844d9db4bbb1c75bf47f91d35c84ccedcc5d90ddf04d981fb136d00c7f60ac1ce6ba789da3e8ae88d4bcc00e6839d963ba4f9cfe063d99efcf0fa7a91e28f85a55d4bac2e670a7f7854b98298ddb207ffe5da155f4f754fa1f937f43fed33624f3851c8914f92dfabae63533f4c12231dd14d74edc4923da296316aea9f276f55f176720f4d8622f362231b79cc876ae5266899063811fa585115080c8f067bbede8e99a3ec1a0359cf8361d33cfeb8f58b679a77bc7d7ce28b102dcbf5bc69f656646e7a4246c82c110d54b9385ee57a51c8fb66a5e9508b4f1e62d55697da7748d5e33ad4603155919e92360d6e7fe3683226da728d999f7362c30e479ae117a402af080c5222c441e49f3da14e359d45fdfc24f455f9706a6d3afaed7cf7096c645e0848e9453b441d244c3b244dc7124c8bf8218cd2d9608f0d36531980a509a433713f9c62eba74bcd4c7ec2ee342d8ca2be77aa84da7d6f50906a5c251a5f492d78033c60fca0a6ee28a43e45485d2c3720a560073e192f9e8a93e337c895f0949afabf713182e1092206f1e4b9a1ac1389f889978b416413ef7a20b7fed3a9788fc903324cc17ba93eb58c19e5d6533f8b73d44540827e58446eb604a0fa0024ae837dc6e15c54aa5166a029275d5c8ef6b91dd377e6b07b1fcc78bc8c22cebd742f87737c252cfbd7657b2ea10296667464237bdf4ea6b852dfbc6a40026d5c72fbdf1efc15fff0bf15d3c81fa45eac79de7aacbe9cb85380d39af0d98fa369bb472483f1b394c0bd22980c8de91524474342966332dcb325e8dc238eed5cc3d95ecc31b82f86a5ca9a34b50bf0e0e5a3e4f612a8c0fedcbb94961dd77d97a9d0b98b35463108e34d41e20f1689e8619e76dc5c8319862138955401245625905d4ecf48ba98644297c49aff44961a6f32bd6f3db4f46c2fef78e394769503c3e10a685d3890d29bdd5896831ae25de1c7652f290f8c333d52f0dd65f7b4012e485c5012b6dba3da514b67d4d0f7f54df5f84a7e24dddfc3e4736743f4159df9b5bd31c3fb0e8fb12bb3731b530aa813f0efab708c81778ae9bca9d4b400a7e33272660edb2485fbcc934bea0bfc81cfedbb2d33a7bbb4df99cec92ae7cd52f5912a055ca1e01a2790c", "005153525151ac", 0, -56721546, 1537743641, "46ba0f0e4a3072bcd842e2c83f81ebe0cab8c3d24ca3757733ea716b614713c9"], + ["41aca02102f5951a2f1d19c3bff41731e78868a07d141e1b0cbb67279b37435383503206cc0100000004ac516551ffffffff09991956f436c10ee3e1db93510ead737be05de5a5165dcc6b054b8408cd5c1b020000000665000065656a3faa844104ed991c000000000008656a5200636a6552d760a30500000000046a53ac530f34b50400000000005e347e000000000002ac0000000000030000000000000000d04c4c04000000003d6d61fc82a01b9cde747cdd1f13c83c00b9133fb46c1698aedb048732c81d2582c0a9f0a823c03c8dd6a5f3a7ca905f95d0eccbfb1745794e81702f6a462dd93c796cfecb2a9a24bf337a82ee1c48bbbfe19260865cf9f0adceb350eee04e6400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140dcf0485d845aeb72ce16a193385da67c3870ec0b1101c4365f99d9bda773718c86a28c96e4a593294bf06c2fd3e1449e105a51e681ced4e8a8584549e699c5d485612d97dfb965ba740fc95e15562e20cabe17ba2bbdc789cc36732e6584cd91802831030622fff26aa2cd45c21d8ae463489bfe2ec334b3de900bce0e9e3032c12b7caaec45dacadb61f45214cb8a4d61f548dc81c210d400e792d1e655d080216f77bdf2284c82a4582a25f859c84e5af65dbdc2ef89ba68c345f5ce169aa330a06c28c51144109aa79099332e21ca90b3418c16b8b2e3715396b4997e96485073717763b1af53543560c0e26b99c2b5fe451c481b203819e2404e0e529fdd90402190a67bd91e744939eb2ed2dd086b286baa6596c0ffafd3a40ede24dfff150680221b715121102032bd9d0d94d95f00049e2ee6b2a74bc059c15b5d82a762ab83003261308837499d75f2bf987647123a4344a222c584e49e9e1e17ffa97d460b2250322fd3b3ec8a2d9bd7fe792f94c644bcaaf216c41a9d55c5418d3f859115d20b0032be0d9b8da962d2f34aece443f0d36acba428117a6cb77016528a5d19f49c97ea4b2fb9b527af089b936bf61e76e830477c994c8ed1c902467b0c32c0b34a2b0f971201bdfadb5d74f69d13e69b445475b63804ecaf9db0366703865c52b0ba8860db066ab77e4f564ddb5cf2f0bcb01ad1e5694a1673d3e2d6a9b5927dac7710e30d0cc336097cbe9607613f308e6dc8d26095ae9ab2cc56e2927558290c22961ba062f3e3a2bd6afe379f06801a60420fd96b884ddd9632b7e0526b616553b133758d2c1f519f19eab36953d6d51474973420d08f1046538c56640b4b14a0770dcd35e0d80589cd4b12f396d5a8b624c035e1e7d9e3da22252b76f97c2eca1a3a2e630d682a660aeddf818095313791ff8137cd404d3d8be7ecb75395409b87499a23dc1813635ab36f151668dad1ad9d73359fa94e1706ebd82e505f80cee146384bf6593979598a803b08ef4c9081a3ace1955fedae6db604a11cdb3800ad53cc8691647a5ddaf19213271f7c123a5fb19a109c948e84ff8fd239f4bc38374fcbb2df5776134014ed78c0a7ffaf8401ddd6d5b221c487079d966e459fb984f3c5372f70d3c8fbb7046290296fb749263285e7c5c177a3005cd3bfddec870b6d84a60e896b9f998f031a29f91f946a12c9a4c2e3e1f556ce5b5616d027b453dee024d3aef03e4f3c265197eb79a93131c91b6955a5147f2065501f981b344ac1f29545d74faf8774c420ca8661b88ca18585ee610a6881c1756fc805cd10a388e69904c0a7d82bde7f0671c496e3d52407979a3f8c1ad83025582fa804cfbc4538fb516d3a96631b12a7d1fbc07f3c58d5b17e81511dedb889732e81bc94cfcce828f609a8db5e242338d9158720d64b06cc35cfcd747d24a6031f6bb4cec4a3c206689b3d35db6b9af8b281770404b70f8099d6158060bdbfb0f073b3d8de6afc5d20a2814c04880fc7e82d3cc26ac2993787d87eeab20ecf76ff9c52bfec6089481521a6c7c75d353d9b9a85f017bd6678e07e0bb094ad81b18dc5a3113bbba8664104db0d12ebd306b2504e9b4e2a54bfecf3d998fe543d93ad414199d9e5f3fff7b939c6563bc6dbb1cbc1599586545d2bc093bd601dac89c1400f5a843161503c71f40d015f84c633b0129b147da7b592595113ef1a7c6f8533e9bd21624150570c5779a371a1aa3945a6ba4ecee7521c555c8c70cd70aad8fcdccf7f5edb7e35ee64737eca5c19ff8e15e44b34643cc2dff7c3a57a724e8e8d907226b331b1a430ee7bb0623690136552c4d2c6adce1457f2f49f62d375a247fdb36a0b67ec3c458a8fa0067f28c9a1f2f1faf402d84b36165180eabd6977c469d7f89c8f3370c1fce9ce5d16376737dd5fbfaef9b45d0e301fc291d9907b3f1b4fa9a564fbd5c994d721dea7391349f06d5e1a14a78a83e71bdf5c6d98fbf2d5eede75b6b8d3d70c416b252a51795e912c5e30222b4cfae5274c52514ce439308b82cce8e3e72ec346f5ea00bb0aed798997922991dc8df170cde278f8aa7ef896413c72c60fc474d9ceb4df8e789ecb79e1b58710e6e9c509ebd99463d3da3a837e78755b3a0bc59121ca5e62e138c2d87b1e201eded12e3a7ffeb9468c8453cfe8b00209f691c3420b33125ec88e33f2b682b39724dca771be8ffc8e7a9bc8986eb4caae97f44586418555050031da5efb57e076f84a416e1125f530fd9eb04a0b61ebc5abbb7b31b4e5fafaaafbdfea87d33dcc32803000000000000000000000000cd37ab5980240fa71b23af8a49a33a7aa8633c37a216ac92081889d2458bfd3e43005b8449f45a61c4c803ea8d0f862db4e5942b166e0b671827e988c658b949b28eb2db655a0ec9fb26d29d74b6d9fc94b3ba96a084493176f8ffd39c186d8b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008f03ae0c697162afd1ef6d7e4306f025a0d9a048f96e4ce887f291ed081dcc79c773d41b4139561a9c1b614c7b5753ac8fc1dc7358ac12d6c8542f72141a24dccc3419b47fc0e7886b766804b22fec30178997ab821d87611afd191679637ad5cbfac7c8a334501a594b38ac8e98f8e2023050687ee25b70b24080c95fd5753b02159587a687e6b5ddbfcc098e746bf70b5fd237053b865b330ebf6da4e82c85b90222ac8ca6e0e91c29ecadbe233b490fd716c65228fd606f41a713237fba80a5550a05b3c95ddc293b4562eadb369b054c4b1b01be99aafc1d9e552c3e8e825484cf5fc62742e27bbe4efd88c5390a94adf721aa96e20f66f48a93169364a3b8fd3d022137f37b8bdb92e51c6d80b0c0ad909113e2db81796cfc64239e827a9e305dbe021675ae4ccf77c1723ec8836a4ba72fab8bd4a06a963ee4dcb5de560453cb93d8021e9e941d22c4aff157782894aae3daea67f01ba2c52eb8b915d8d9a0ae79990c020db34eef44858cf987c199864ea17bb4049b2679bfed9ee7af1ec6733e7ec96c0313fb1e60bbac715be79c0dbc32dd605f614b7b61b2ddade08712c060defa1518ce94c0f486d40d4576016eaf3964c7d03368140fc4412b1f8d78bc562d1d94382965375c6d167db604352f045e090b5d95fbca58afa0ae896a31cdad96294a222bb6e9027c5588d5355a667a03c79e2f051bd826c5c3d3fc82209eaaf2e16a71d1a045e99e1616d0880137302b9780be404dea9178016b61e9f2588c6240e580746ac6808ed7d533435218827b099c07bb2d97cbf5f906db22552b7d53e3749716ede59df4af7379f13e7dba583696e5ce52ea26d87d6aed6dd39ca295e271a508d98b19646dff335e4cde2ecb9bd5e46edcfa75eaf7178abbffc17a5870cc8aa1f1bce04d1ee36bb57cd22e2469646e6a8e850dbbd5b0b5b98c332cb0ceef871e03dda609feb07cd000fc394609f55541fa271c70899418dcd5fe601a51bf6e4c3ea277c5bf0ff4d0e24a75c8edba26169ce30e6b1280d82f9e6c0fb58b5d3e2f67a23bc1ff0f4f4b47964d4d0cfde2934cd258d98a903345ebb19977e9dd553944dfc8c7cc2d1400afbe13ccb354e10a877e7c761db8f043f54b2d7481bd9e0cb51a45bfda3379d972f1d36afe30935238fe09302ea04ff6d123bb099a21bec3d6c4a428716f9678fe6a05ac128b2d30db31bb5c417ab8a7f5cb36875793ec5a4541f52434aada83d985bf37aec25551083e98c7301748c67479bbf6205c846934e812d2ce4d4f7119c368c8f289fa2e99ef5eb53ccd96ab407a73f3d053271c66b0522b5c5e7c7e5d791c2d8f061b08cd51de063f9b031b432f75dcb56a7f6f414caac6c253f3f273dc48d7208f3f3f612f2b9da0158056a63ec6a3f3121b3973e8e26faca1e4eca806f614df76184c68f8fd039e7daccbd5a32a09ef67f9806b57bfe2e09c1488cac3efda58651d8aa1a008fe95ad8a9393e13534e96ed7944b6a3f9648a726973ddb9b9864c53861743f3eafe80c308b3709a6bf188681135af9ff4d40a724e0c35a8582912087a2d7737bf3589defc96998396f2a0cb2c68d15d6f9dacc82de2cc398c05996b6df0b750d9ede1ccda487f7c7348223c18c0bcb4c652398d80798493e1a058cc85bbe420eed4b966ffd1334ac086a07215ecdcf9f28141ac0b243c15d4b4dd6f7f0f6633b7cce157b0bb05c320c4c3c39e3daa7f446481271080d5d394b769ec2966f08de381c492fcf5263840af00d1e3c70231003b80cab395362e14edef8a287daa2586c62711af8c7d227dcbe25f431bbf2ccb586cafd8f340b5aac2fdffc3cc193f53779cc14d4a292f8c892833f25e6f64b80d64691b1d153e69a193175fe7892e597ca6a9c40732da57d8c868122701fdf28c1f9088c9b1fda399bf865601f1f2c796135ca916a61c311b8b0450c5707819330ab5474a0f121d6e927d4ca25459d231d4ada187823be452a3873e0e3a6404cb5b8fb355474fd69580565eedd3cd718d0d57e3ce231e19201a84b606fec5ebfd416ef92170f742c42a876ec053021a968b4aa9acc5307a8d64e3759b62c2db7f9a0b50bf3f36b6b26bfe413919a8cbde18688ff941486f730aa15335f6f5f01f6722b4f766a51b1e640d96f1f9ba196d6a8883950d99235aefb2ef61d2a760ea5b45379223dad08f3490850954fac2d7d20412560243ef0fca7ac61d99e1b67d11070148c1dedd9f8f8403571010752e70da845844009face553dda151658e124ea600d355dd1961a4b1403660000000000000000ded3590500000000c4365075feae5c807c688aeead36dab0f657af23652c7e6080ab978319bef6e9e792df99c959104742ea675f1302e9a4944c0a362572fa15b1040edb746d7eb83953c4c64c26593389a4712a886d9009b27584b5374c14d93a7494c30ebfc065000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002f42f69601686375aa901306690b00059bc6daa869ddfc111046a3a91d4fbeaad5dbdd72f01d55fb397ac110ac5d37ab048f7bf7b83ce90b59d0f14f98ebf000e5501a2f78911237f68446ef1bafaee561b5b05fe2c6a01098dcd4a7ac5496fbd5d2541b9cfb24dc89d09f84ac7269930a1f146fd964f88a2ba7fa5e20a483d50203ed1df87809462af16ce7230d9cfc0a65ab7f3af6dac4ede599d739b0ca1c900325b7bc319de5784431daf7da4b36c4ef6b30a30dd98c45aa4dadf7986442db7a0b0340831025863376444029b117253a58ca09bb7413eeb986339a4bb0347fd55aa5fe2bdfb645f96fe53fe292134fb212c018ca8d0f7f719f6a7753f3040a2fd30302121ade1241dabbb52757390d6e5eea7eece094e06fd436f2f52f9a8feacfd70321aa0203bb98f9232dbe385ab669164b6ee373765d47cc4e9c9f9c09c34e00d80313848b45dc03c6b032fd458b1ec610a2d22a15d2a3660d2f83ce3e7e24844100031884dd39037ff2b59f0e0e921526edaf1d4e55f6aa05d5fdada74b4e30081cbe0214e1f71af8d564da562608aa040f1746e812fb004dd97e848cb01b1524d5ae5f561f1b38cd71e7cd30f4c01452a0eb1da8282b6f70c262d1957cf34899ced37010ba2415c46dd8c24edb4ef95212eaecd22d9131dd7291ade65fa60059dfc3b804ef746205bb1bf5e70ecab8af74313f7229704e209f38f907304a21526e2ee655605b2c5606f4a82dcbb17f4bdd7a37912a6727611a216af4787359c7efda9a22db03f5cccc714bc02f193e3c8ff76749b4639b4d4240da2c0e48ab8004db2b27f13077f08234dbf4e298833689009177e7e066f7b4456ef5d2d04d287e212cc66ec65e9ac997a57a0f482194622c8efd9d43cb928953c4e37242faa648e1be96e993e8ec0c1f798d49d178a82fd3c33152cd05a5f19acedd5f4880e997c0054838a1e41a50738bccc8e52ad5105cf3448b0115fc9dfcd3c8e08779aaaa62d73b5bb6ef9ba0e1191bee03a37229d356e3381983a0f47e0015fed5029e636cdd020800c303d3fe42c1b3df2a673a5435b4a0f1d4d2ab914c37fd15e19b5c271c3b3d1239f3f47f91cfc61654224959047c959746e0312311163d1df2b2be611ee6ab06e8476390687e2c35a1db8f9bca41f6238cca74e3a592d2a30f4f8d0bda5b1f294adb28d43bded31114d17ed735083a1774770cd8c833add16a9bfe2364f5af4f13d26e157c4cd49720a659dcf0b4c6c56ee09719f270784f6cb8dbaca6f4454ebe113acf4899a823a2cc7852e1327b0ea9f8b6a2ca3ad6fecb4eb0ae626259ddab202eac4d2d73907fc30e65e4d160149d7cc055bbbb03aa59fb848572c7b3d21fb6422735f377e9213a6607f76bcd52278ad4d02216a492cd4115a4b77a76bcf3be8651831a8561c9c6a4b2212c9bdb2469ebd443aa271edb0b6b3dbf04cb60643bf47d3568c41d3effa2606c77b2811ca222144c2b7b29e472f32c6391eb8e5f80365ea041192a12ca69152a3b549565fb6a2ed410f53476efb2122e4ba63b44b2902133c20484b88665ec356bc1e6f3dfd6acc48f0fc7eb4ba26c4890c7b41cc9e5951ba64b9e287faf2f41983238ba860426c72d1c6ac18be71ea5d7c6a665e1d58bb9cc3ea33ccfd6db7b37885aa115ae6d48da6c8ba554dd9798f911b1d5495da9d00f87c6c937d709da34ed6dbebcdae946b3bf1258905f5e727e75e71fbf3a12d68b2a003abe96e8d974771ff6efe41edf2d7bc636c0855573bea7c6e654e9ef741fc955cc7967092eed7af8b6bf1fe3c19e28920e85d4e77893153690a51f3af70523df53e2fb5afe2eea45018b5a4e38a35eb676fed0b9c7a576dcef2b0fcf16cc469fc734d5f3b5ca8b91fef09ed3c922265d050694e0f4de7ff9c8d0af1d8724e1127cee3ed61bcfbba0c658e341d1e489f80c12dbd1e5ece6e36f472e6a83d169cc55833d9c1b42a2be4e97800cdf30e53d5fc2535274261e96aeb356166f14026019ad62a896870f42d1abb884f3424f08e3a955f44332a353a0f8abc91ca2a1b652421e4585f4625f2860bdce09b2002f7dbc19123df99b91b078d6f1849e56cca685c4d75b0a878be78ae296b52f4fa74970beca5455d4d52862a9325b3b1d3946b9c63e209f1993330dad8e1f5c830df661967391666bf97d340efcf056ff6d1360c2f19a9d7d1739633cf92d49c32b7280582c6ceb748af568863c83cac97a97a776d5a73b380783a42b50853456f6423bf8dd6190880a7157f6464259cda45d0b107ce03c77c229372e4a35ff0a1419baafeb491f5686eaad34fcca41f7e7c9281fd40c3de89138cdffe45faf896714b7c2457743fb36fe61b2db86ce46ba78882c299453c2281ca15fac33b27d7ca6c473e6b591e717ec5f1fd2e945b7b35970c5209bdd0c", "00", 0, 106926605, 0, "088eecc6ed775a7c15d5aaec1aa1f1ae9a4595a75f6ed3325d9471ea7c39e579"], + ["66c7400002495142d14b088fc60ba192778e27f0a88b069c726ed29058867c272d55785f8d030000000151f9e7764a40e1814d0271c1d04c79bf47c06c578576963824e8754cbcfe177192c8b2ca710200000003005300ffffffff04f16eb803000000000765006551ac636aa7966505000000000453656365f7c4d805000000000953acac51636a525265674e1a04000000000751ac006552ac6a7cea33e90100000000000000005027f90300000000af8c655dfefa4210b59bc51b5ea37cfbb7d83c99dbe8ab5a48998662e18d21e33c647c00158d04be705736244dcebd7f525622227033259b12fa993dbab2689777f3af66e604ae7bcac2cabd6e9dff15df7a03ca9d772400fbda59dab8d5a554000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008b4ae02e4e288017d933436965f52e8aece52771dba49def019805e42a1096f30b25454cd784a859316af9e04fcbb1265144ed5726df7bcd1d082d887c367d33c2883b466d77f11cc440d327f835ea9403a964e6740a3ccdcd05607be0b77ad4e5578bed3d7fc85b9c825607b60ecdfb36cb9e9d46650e8db2b0090887c289a9031d2af43e029cd8366162ea0f964942693c1496301cb2b28e2a51de9d985b26050301bed72018e5bf42c1b38df8368c8f8cbfcb9ed3ff5437340b200021b5392d220b051faa1da3fa2433b496cb714bac276a664faa129a108b063a381d37cf75877e95a060249aaf338d9afe06066e3f16334f62ae356f8db0b8a8a495be8f0b353803125aaf2c11398ec5689c03d1fa237a8f5ba8b85df58aa08afb1af903629652940216bd76c60ade09b18f433189d0207655263f23591655b18c2f4c6a042dfb2e12032181dc46045eff52e4f6d810abda04492420e32dda2061a854c8ac4097e5d8ce0211012ce5ee10cef0303bd58eee2305f2d479ba49efc1aa48ebc6603a9e26e07c020b665b2b9f7518afa3bbbf5e78c47fc69796711c7a875ca49a0ee81ea76c53b5857bf9d33d14db63a336cd4ad2a8f527dd1e5c8b4e1426fa02b967aeffb0d597df5e6885d0aee3c26105dbf5b80a7fc109d9ef321c4ccb52e071735b29ec289f634f79f1bf135674b2ce4ceef2b5a1424dc49df0853d81350cf153945503cbaf5c836cd2bbd152d51b1856373055ad6becf0a4ff99d4eff4ae0c277883553f894ea262eaa31ed8ca3b40377a0de80891ce4577899e251d56c0cf0795fdabeb558a29e5068ea3790ddfa3379b9cb6eb83e1fb1e6b6b9929231f5943d3407e6822aeafaf40fa70aac0815b970bd998f61d8ee6d039043cc06e6a56fb8809735654a232b91d5d95e63b2ac4f5897f659474f64fbcf8d901eb31c1e4b226d03e883a3bec79cd1cbe22b983c239854c5a38cfed4ada0ed62111a0b9f3eb76b93a2428732440bfc763b3b398d00bc987d3547c59906792b0a149f359db9a2c8042af2b2186cdf2db73bd3771657767174cf49b85812a4128eb665d86d3169bf52d0604556bf8819ce8ef710666ba60764b3182e70344f743f756ea38fa7e13f856448cdb16e140fc946930cc3f0cc461150704048220602829be2d99e0a7d14b81538d58bc9aaf935baac0a10eb63aa6b12e6f3956ee72931e57feb7c3eb58983790f59a0742538c5bc5c34e3672095642f9a1d813c296aeb301eb71a731d2b438133e0a678072dbfd118d09b62cd8a1dba028da7cfd453a27b73466e018408fcbb703394cfe31b66efb4be07e85fa50647cbb8bcb9743280e07821cc855418ef606720b5230ffe98960f0ab9e85c505a88fc4df6dab259fd5e7f649b15c1b0100f0ffebe0e4faeeaa1fb5a959103efc9c072f50b8c24f5237c686bffb7adc0369f9bb802cca97906015824d13cc79d10ec0386487deee46c37895e11ef9328041fa2e069a3baa9e3a87d1d2ffa2fa0d711fa69547228b9debe5264c8b52f26c64b7602536baa69b6bcd0f0086ef2cefe86bb81613943010830cfa1b3332e0bceaa0d2d41f6c5313a56ff205248fc48aa615d5a8abecfb2976b872dde12ac42189b3a31459b9788a2db570877934a610570d105248abc916e12932550dc161921c2cf0859508c0fb69068bb5e7708f653ebab94d8f153e01a01d6df911bd4f4ffd96d9999d5b185b172501df32838f535660b348ed18f6c31ce5dd92822c66dd93acc34b8a5f0ba4ac484c44a3055a5f0aec61c519ae8e02af1f74a916a240db609340ddb56801f24731b77b1b760ade3f2b1a41d254198ff7ef9ddf542b8e62aa4b9a88afc362a1322316d79b30d01bd4414c794b387e926097d45f9727ae0fb9df012a8364dbd91152b9b34d455fc217a1fdf38b05a9883896dcb7db49bb0dc36f4714c43e0cc77f97fcf4a6a975e773fb23d1ea97c4c03e58d505d98b7935a812199083785ac850ba539489c3dde9d76c1754eac1a63afdef1b240ffc2024051f00469712c6418ff7fc29df8f25a9a1527277cf94f2e24ba279c6be8fa72fdb81d9b3a17bfef8a53c3558e854695e2a397f0825cb50d88a69df1eaad0c37a94c2e6a964cea7e06d381cda8283034c4756bd4475a51625dd7f8fd5296950e70021212a258fa87677f1e2d4a01411679814b6e3cf567f3968f8b4577c7182042b1f4c6806b3088379b9b387d25c6179f884405a77d245aebae744406837189344c9690004f728ba43fc4f15885461c1424ac0c9934304d89c45ca3bfa9a92de799940bc615fc53c193c034973e23ca0dfc91e8060dc14b838261b102b7808789ebe6ecdd09c6e1d7b6567aece4e189ade03147c2004f8bebd6174766279a288b245d6eee17fac6813f43c70a6c5723876e8afd209", "0053ac0065", 0, 667455739, 1537743641, "de889cca9db460c328936ff963f71e9d37ff46438f6c91dd0b32fa422497ea0f"], + ["030000807082c4030289f277ac15e1a3edbc27eb443456d374d3e845c0cef676649bc46ed5c7564b6d0100000006005200515253ffffffff897ce833a63e1cf5f8a2a467326f58254897ccefbe18539f01033b3c47300f3402000000025252ffffffff03f10e02040000000001527c2adf0300000000030053000ce06a000000000003ac51ac000000002af24f3203524c6500000000000000000000000000e58a2a7d29001bd92ea7b3de07e10140d76c9d62e3a13292f1565f6c502652127f4e0eb0acee5970072c8f66e97b50104a98a97320ac4f503a5096ec229ab96954165eaf5adc3023c9cd8b5c8cb725a1f9389f6fc7d03456c0649f08d137b2c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c732d5d1becb1a0b1f5089048fde567e362a83eb7e48b8fe7e860f20a90ee8027b1b0a83157d20311e4a1be45f4c3d4dd2ca5324c9ddb7c25014585a8e85c6d4bc98ad7e805a577e5ffb5df599f0f539ecd8570c3229139bb3bc269d7a6d817ae76e47b18f25025e5def18f76d8dd86adb31daf0d2d156fed6991c88600d4ce0204b4db5dd6cfad57c06169c3d95d20ec04b456491f5523002f1f2b24724afb030320fb0df7363b5cb18bee2a66013c580af4a326b55e6f9b9a77f80e5028951f230b0129cb973c1314c052c6069233842af084464058faa3d97820b6345099aefc26724b51ba094c53d603d97acbb52b3dce666a605ef5efd1bb57f0f0411081b83602195d941e801a951a8a9fd591961ddb8d5fc7daae4da7d7fb7f8b026874035dc30202975287581e90062840ce80e1363f37d12ef404352dd103d218c9254ff0d7ca0315990cd3280991ed130ddc62b9b0e9fda6b250c03113b5aad0f3bcafc8c70df1032db7222e1e170b7819c36966bd08c8e4fdc521e7278588937a4797d36e792b590313182901c8a1d443677bb941ce10cc54ec64d5bd6f25053c5797e76da17e56d04e3297453f56c9d446c7193074e65ec46d0d462c78f83291d6115321557bcd06d18065c3d27bd94d9b2fef81d1a23ab2831e8b9b6ccf3800eb325901a253868a89860cd2f52e753ba4c2f0de0f3cd8df3e7553b9751d4f6c07e393469713327cbf7ce9892dd2422d4b1ab6adddc10c02f6bbfa100d127b9596b49593103755737e7b3439faa448079466bf2b039dd5362633957b2e9dff756b8d03c43621c7faa987d000267db6fbc67f194e7f22e52d96020a6f706b13088019d468eb93e6455dab317145d9449e258c96fd05917f6150aa3a46c241da046667079c910423593e8de40525e3f39bdf392ee1fc215bfa61ea61f173d654a26226cfb071fa25c4c1d9e9830aa0cb0922ac478876cc05e13080163f1c59a3fca0da8800de843e69175813348666614e79317bff0ff2d004d5c8d572676c048b699319eebdded5fab14a76150f2417514d45de2935f306a179c49dc5de8db70755fdd344e0e5c49ea4b77d9503215e210aa860662e71fa1840cb884799632134cfef1ce9b6bde3d2f52ef377c015f3b5de2216b2fd3d2ed44163ef0a30306e5a0bf8221678d527c92a69acb6e2e66d54a649572990c0c383ab17c782ad3f15e22c09c91884d4e1a014c91064c53155bb351fb364a295f40fa8bc63d0f2b46d6c5a148719e156924efca6291098312cef9b90c25e312320dbdba93851ac915c85cdfada7a02cb77383bacd2cc4e95d7f66caac73289ada6c35177bdd8223889e2b594f843837979e78c7e923c562ec4bc2d2a0a08aadb9d74a9319a340c43200441ff12addc02d038a02275c2d09c0e460e02489ce3bfc2c74cb297609430c378b9528708a9b1c72fb7adbf23d12504690888f8246fc04c0c720cbc1d026020f5475922561f3af0cfcde944e4afac580ddc952aa7d47c5482550d8cae49f73af684b43c0444aaf432b2f52050d98249820802f10015b4e2562c5b7ad430d52612f2ab2d1709734d0f05ec9fe4e20c15508f417803d900b9da34a5bb1b986daf4895668acb6ab81050dc8074bbd3e2ba716bd0d4b1af66286815fcddcc8c743cd68c28c268622c541948bb042a62442a91e140ed9dc0b0e63d495a29e4c9172c79a5a3ce1060bb51922367bc0140cd2442d99ed95c8a249aa250410a228bb375f6fa2ded2521bd97b4b07b6d3197c61eea3e1f070f1fc5c3a7e760d1bbf494592886d4d0b415ac16fb9a4537a21826a034e0b662bf498028eafc0698cbb49b9a6724d93746a426da8224049db54c64dafe7c6a368a7b382c98dc76a0bc29eba7f511286598c104e7205a796a76aed9e238d97eb9b3550f85a9cf220c64233d71e2659a826e7f9f332f092c51b36e9550c74f15eb8026945f877844450138347b3c64876cbc4e13f3fed363c6c4041134e3cc1da6081b5b6170ec4a764742f5ef13edb1e716dd45d13875b5e63fed7b07e9faaabf0b717938674fa8dbae23a234549e16dfcf18f6987edb8dac5392f42384fb9592d9af775cb6fce1c7cb01d1347bdfa2cc310d1ac261c023cc65acca08445dc2c488466a97024e82a2ae8e0c720ca981434e989177df545c0dec8eb6dfb017508fb670d3110b6bdbd15e61f15315da88dfdaecb898b43343e20ea6e6b42892226aa527b873a5314353251f294722b3832751719f16da25a16affeb192f8655c2cc16e5c879bfcde900000000000000008453da0200000000d669e1b04a004cf7889d93a5846f409a93a00b5ab6c4d4ef777d42cb1780ca64bcc23c964324a9eb21d25393b467ff3edd680f0cb521de1a6f6ac707a084e0b60bcb75cb3764288affe5fdc9463d68f7fc4efdb82ff543893312ba403083ee16000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009a18d9386037b5625426dfa5c87571f9c684129b2c15fa640bd3b1d5cda05a651ea1c06e5b40e65afe08b26c5401ff39e093cdd852d867c42fc75918858ce3b38a89440c508cd14637e29a79f29869241f5c2a49f9c8a47f992408df81285f747c6c7c10c51400bda8101df76443db05054c8011b8a3f9f401246883be6610a80324fce1e16266585003445ca2d33e45025e105d2a24ec099a9420f185c24ead91020509ea32f77234afbaa52b86010bb5d7c6e0d5a324771b5a30e21c10e6f566f50b02e027498ff6dc1013a6603533ff722014b23aba45888427010bed8a4658c1efa85977d5a047529b6754bc5c572c93958cfe342ea58a999dedd546b58606f42a020e654fcd8087fc45b47bc2b9349bad7d93830c746daf8ffeee395ebf6e1a7b0502163bf9f15337efcfdbf500c87004ac39c140ab9bbd98ac582e65420f7c0001130219fae4b19a5a82dc16c5a901da6d84f78e1f95fd0283e09dd1bfc4a3b4c7a30f0305d385e6560d30767a18055f8a37a985f902dbb73ad8c565c70437f34481d09902158fdf73ebc00a35062f4e54c9f99c9c8fd3bc4412224efbb3165b4b6b976578c12570ac92fa0b01d3018f6959cbeede9b2fb2fe5984be52a9b62106b8e964298150e0a07736a3c1411ffb27374610918c45883c71064f90d06d87034d237a912670c3ce908b9df3e9376fbb386e2c9cdba71646d61f7544e3874a182100ce187a94f7a8272b965e2cad448c5729ec49ab57cae88c812db82fb77c13e9cc305d1a6f323427d5b04c04f9d6d3e71d57bec29df389c237ee2f315e99b3336550f109252c8d0fb6c653f402df1c9a5f6c4df8f8bd38851283fa4cd7c80f5e0193734447f38cdca00b90ea114e00e739399149bfc07a35f48724479b2949e1eb27ea1d53950b7aa866cc15177f42e1061007da12534b4638533f5bf5e4218ee08f1e1b04e98e5182d7f40981287336919f19d7bc96a64ae21adabcdb637934bc62398a72ec5a59cf70544ce9d0d32967a0fd2b12306c048f3cbda4a05257492efb52aa65010a9b2c5461a665d7156fde68054deaaf9488a29c4e3dfa22d46d8b278bb5ebc30be4dcdd18cd4afce3376c209a9cc4e3a3c884efb1b5edd13488f409a6b9982b7391d2f4e76d46bfb30e89e821e1d5f9f93da1a9e71cc83b2ebf7d1991c42ff5ffb1150eb279da39189f7741398c07b3875fc0e9ff02fd98d0de16ab6b371a55508ff3ac31e0838f2dfb5da946c2d9075754084349e309657cfb5a7750caec0e5631cba8e2df49b4eee788ed1b37b509956f98909304e970eee917578685d4dae10a98fd15f13e6109d6e95abf0646d3a344eb9a20e64984e45f55dae4e9e150675aac3a8f17532aa93baf1cf976f18913bb7c751728a33cd6778d01fe29806a997ec157edb01944d1683b4a261573085db4c4eb3c760818eba0312e90760ec277ba9c539b68098618fcd0e8b22609054c1bc8f0f6a5e7d22ba539cc46a7ddf58d15bd17793b3bdbdc73369b9c6408588356c8d4b8b423f160ec255ffca1a870f877ea467f653bc413f0fd294d339a7e57178770837bab9244061396f39c7fc03ef592dc685b2c96404a84b5d5d2eb18b1420ec280ecf6a507dc4835658e6770a319b79fb64010e7f566f8e65deb2b04288e85fcfb6dc06bbd00b805cab87058fee777cecbfbd6d97713cb95c817675aee3944c6905485d50dd91428e116008d538c6cf2c2554d2c06e725d0dd5fce1a7426c084cf258254fa6f5a2f8aaec143d855012712fc8b73caadce2ff19427225caa7866347aba9c817f0bc5a893f9b38bfaea9703e9135e3f42f3aa8c45a488e9b7b4bf693e73a9dc0e173f8d42c309583a5e32e726e6efefdca990d4d4cf832bf97eb78ebdfb55e3216afbca5454f07c50bc009546bd33524d7c076711f4b99cc0e8ed612a6863b57add168255a1239752bbe469c8b94a4bf233e8bc24dec7ce013c0875c6656d0a710d826f7414348c1913a29c15db156dafe3a3d58da8d9acb28978032ce748ee78bfc5e7bc98f664a4d335961c89dd11e865f1b87393e6f660e76fe6376095571aa52029a9ffe1afdbc2311c65debe43b38ecdc041d7a59c1794e5aeb1d7eead9e2562a8b6bb7b3ede4936456676c9c5df2d0fe510803cc1ec388d5e0621c976a3a81ef8a7c60218a5311d59760bf46b31a01caf0db777d2abfc6f0bb25128868d28e26201ce5a33746318989b4cb27d77c910ea8f94fc5d88cf67ed629d4f3d37b46b81a8c231f53a01f15b58c2dda019bc36ee2660086fec020000000000000000000000008d041a581e6cf1f7824dd97b8b224697b687b3c4cc6c6b21071b78e014dd4cec2e35d0f0c213db27d75517f1da95168b742513c7c9ffca8533f14e733adf6034d99c5f78f33cf66f054df225bcdc3624addb57a7b19dd73b4717964c072fa0bd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009260f1418002dd0aad73aab9a67df1a47197a9cf888dd04f585cae64233ac8f1e99e2f3f9819f3fc8aca8ef99fe19f44a5115441e020bbd35242c98b0972d070708f716eb8a64f84578a066dec8a28689785e4cda313a0b0fb77084cc6d2e0950201436d6aec7f94236ef33ccec07f44aaa243bf3604587e45643302045d580d032cf0141aa57b7463eec55a193f5ed839bd097e6860ad359a64409998d7f48cc0032114148e576c450e18a2f3fca277ed23777950f74425589b837e46bfd3024d710b05153cc89e78d7ea89b68be14d6467aa0b02728cec074e2e230f1d512de27eb2632392b2aacce1aa581fdad244e415fc6ac8150d07b7042e5a64271e438dff53021b74115f5a98840db01ed811959651c8aa09aff630c0282ddd0327c879e68905020a3f1bf694cca7f64a968d9fa3e8a576fc3d4462fa51fae01fcaf4105382034e020c82de0419ab3293f79aeca197867039fc346ab1acb5d3a2c7c4c9dcdbfe649d0303de53a3492feea820580b968b0571619838567dee88ca593766ea0d595826ab022f9dfb55e47e7577943a79a00f147b73d13667123b760310a41fee1ec1e00fa8599c8d2192046cd9c4e64bdbc01b285cbc36ce4250333759856d33e1f124fddfd869c9ea1a58abfd7ca3fe1884953c72fbbbbc0f98896e1468011fa1c6e2b0903971acd8e8fdfa6f76bcc0f50829ece214913fc9b1837128531977043fd03efd555a176d1fc5f3bb8311f0c7defe15b2b0d52edef503b78ce4af1a8d75f993412de3b0690db0876f70eb424a9081c06feb21d3b28b899cbc11d8801103cbd006fcca1178bcf378fd08a6c0440ed04bdced819ec3e435a307fa956b297ab316f55c4ac9d97d7cb61093ef386766a3a383d3c02872495d82e3ff547e3354fa636f10a6a0b468015704d5086741c96f0ab7162adc89a35202af3947b975212ca8fc24471c866d47cd497c45e483c47110e0f0ea036e2429298b3aa63abdecadec4c844777e4591ddbdccc45ee0cc58d9635b5230eb6e7cc78434fce4424ba640190dbf72c387dbf73c5faa99da2fcf6991b7c62b44031b3bfa55dddc4e7411f61785542726c666535ea86329db964d75ad846aaa1f3b829cbcc9e0bd56e946659ab7efa2ec9dca280242336f6ce8ca7c035b5b1ec87b9c74ad5526275a706cb1ffc05d31b4a4c0751c33c9907447253362387fc75380103ac00d55727eaa9cc32144c31d2f11307a742eab1a3f2a38c417b71d6228a213d2b5f7d1ed309114f2aa645cc86c7dc6bd1852a7f24e3cbb8b2b6be2d1477a7b069744c8520fbd3323144f4cefc83cb0e63d591147b450d3a3cd01b66188816600ec97ce7aae5355b4f0cf419a35ce84b84b9d1124363be147f8ba79f33906169a02cf6e03f5b17be77656295e413293a9a79798b72bcd2d42a4c4e24d08f95b3605a65ab21e969e9287cf86743a837f0f62262e1e1a2d4e6bdbd4340b8247868125ff8de037dc180b993bb1279539b6eb285aba1b9dcc7d87c02b9f05f235bbce03e4cc540491634e054aaaa79e1c4f1e5a4885187d09e69bdeab1e13ebbd372925976b1dc21df6e66905f961ba4637e7eba732feee6b88793687c4c814540eaa863fb97e825f516724ff41e4eeef93eb8cc3e20b44ef93f44f5f6fdc97377c9d9bfd192324fa48570f077414d37c0f2540720e4edb42e2a82016aacede535da85c95468714a12dfd30a72665748bd926b15c44af59b2d498a7c6a3c342463cf2fb34cfd31c8077eff321f37ce3c7f165e575ba13bdf65370a369c989c667c0f885174af25fe765994961c0b9cecf7b401558a3d0a9cb693ec58fbda277dbde82f760db77629e91cfacb1a454cd81b57c42dc1e0fcabd7ce5a1528192ef19e3ee6a50b9fcd94840698a1576f95b89f29581ba8d2dd3ebc404131e793b753602ca1900eb5c3b49268eb3c089c1acf4899a2be95e52f016a5cd12e2cb97306e1c196c13b03e136b01aa77d08dde3359724fc4500416fcd7cd632e5860dfdd3e9a5045eb1a253c5f29af8737ecf5f4e6528cb084d8aca4337e821d4db3dc68dfecd2029c6e3ba06b66b83ccc8b768e6803a48fdfb3b6b9dc4e3649fff1d6f484ac0a7dddf754953be69dcf6fbda29e2d63cbc432f8e8c7ee0ade69d51cce996a040d9843b02683d758413046bcbc7b8fa40a4e10cd7d3eccb762862bded565cd5ef23edf1927897a7b816e328c3c8abb9bdb199ceb0159714318a73f77ba0ee53d8274b24b1959aff93f4e1e1bc88a35af51dde74f36a41abaa8f826e0929bfb4209df8c331f0113086ccc9a76c31c26c982c8abd3ab6a8302d6cf1887fd5acd8b0cfce7a58215c7eb1d017854872360621d454b969def4cc0f22d0d6c12a0e7b7bf57070ebceae02f7673c2965d4b2ff99ab16a3ad2d22bc427c9d9a0d", "6a", 1, -1222303870, 0, "1aa5380bb4ebe17a9e117c2aaf25237ad067767d7ae89730aebe7436d3fee66f"], + ["d5ebb1320469612a1c79b0d2a3e35d03c25153ec000d064830dff5e60869d9cc53e99384f00200000007ac52636353ac6ac9d09de8b5025d45c8b5536820c8b54a44810d4353cc08c8d155f6d9317a1309fdd3311a02000000026aacffffffff2167db1876305abc521e37da77f773b4a6349b99554f45139867f15a3d685441010000000652006a6a53521c00f3abc128bd4d7d32fc8e29ba9c1a334e01a03595193611db062536353dd7224934470300000007636363000065acffffffff02cbe0c4020000000005ac00656363e948440400000000095300ac65656351656a00000000018afa6501000000000000000000000000a819a91fb1f4591b89e4f0283e61ffb2e9143bdf24846ec98123bf4c3cf013d917f8660782150046a13ddc6a5fd0eac543339cd5e2dced9a6e4f804220ae442ae377efdc6b2b40eb6780ed3f46194ff1ce47b189a3002002e638668424a6c3d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b55500c19d4b582ba1456fdef01441b1fe320df1a1761649bd824a87f127abaf5d64292ea2f99a32c627d3997fe8e987e79261f226775e12e58150275fe871aab6f29e6a065fb4fb9fc4e11656115ad5c4d92256214420a4473dba4be12681d07f67f99c28e446c22ad061040a5f83d692104eebe6ce821f8aa0a5564375969f031ebcddf89577f583d162fa8a1635b583ed3283d9c6c53bf11c2179a3da96e5ba022eb12fe3e692947d4b3857360f217e7ad4d12eadadf7472f5e46e34bd30de9f90a00807b890407de01e76b11c5805f2a1583b68ae34fdbed995b0f69e052788971018697974d032dab895c35be9208a357345a5301a70a386744478467125c739f030f4d15808acc3bcd03a11c020a0d8b845c7d887b17493cbe9e8c6b7be5873a6b031ef3605658de028e9331e378bab31f1bc402907a6dde2615ff2edf8a7a6c044e022e1af3c5e5e747bc68780f043e8a9d681221f0cc74880183dacf06b73b0d9ff60227f232ef19338d5583f24f53565c6595a6b6f063478ab6803857749ffa8c80f40302c39aa3ef6e0fd6ade0d7f80d49ecbd48b5bbbd50fa34c39b901dfcb599a8e2c34c1cac7f3d2c526536fde04607485db4956d614a3d00c258c830a36cb25789c66e6983cbe9c44f1badef35c6255c76fb0a94c5928f9e7467c1f8caa0b13aaf59c2b65b53c746dda925462ac983ecb572131e738b4d1808b3f59c03b7a5454499d1cc87e2af9fe922f34924398b524b694f5b39f7e9f39167341acee5c6ece990c9a211c1188217bf095885fa11aff539ca802ac7ead0cfc76adffb13924e3a813c630a32074b025ea57adf1f735e878795987d5c752027230aeb4d733b01891a682bced410397cf271297c26c3241ca00c1f00b4dcdcba564469065904ee052045faaed081a4f7288a572618be92591672cf37981b350fbe3d01bc16b1f4acda938a897833a2ace7143d407ea01c104a519e6c77d6dfcdb6b755f5028ef7c880ab86f8c7475e80e0ffbd529f7954564bb691978a9dbe6b8e5f2373aa33ae354a92f094c75c0e24dab51600c3da28c31611e2a780166ee0799f2334ed8bffba1ec2f4de3377012cb7e9e2be56f2431935fc390c8ab231ba3d4bbd75856b1412efb521837802cd8d1ab9803bbee1ed4d091aef8cbbe5891d75e88379485f7164ebf7f00fa2114ff9d5d22f0fd1132310f75fdbde9fddc42c802a290eea6abfd86a9f8368487da313a294a7a57f23bc969e63d32f1df6f803c96dd85bcc80570d2f5770126f08ddac68a9fec812423749f1d163c6fee7c0d852c05ea806ca0f9a1241fe9fdd8e30f73d3a699c0f219d60c8a627fa954cede86c64d84934ddff1067f2959f54d42f318696432aabb0650170565cd14e531aa79e7ed6d3e7daad70de99b395f834c76fd16c10dc21849a7926856702ecfd67eaffd7ba6156be92d80466c938a3ea6079b8f6961b085b432358260a21a25641efca4ac92eb86a99de70501b2e885a7b41ba98af4ae36aa907d45ccbc75eddea630aa0a1c40923f007b3710fc756dd16c0736dea646b56a7b4e16117fd48141a132f5c6204c0e36d662399d850ddd8d2488d3fa0111d7d6d556992224c917aaf4515754cd61a5accd0f4e22613d4de8086a1944f56408425645af5de0d4c91d90207f78fa8c3ef3837b0305f7e66398936ec25f6ccb581b954bf4c3ca03ce0789d1a3050da1c2170c495a756988d4a4d914b1c383f223640a3e4a75c0e4cf217f2bb3c37be2398b90b62734838e81d54e5e6e16f2a17466f5b84483212586f71e9684791c895383be3b4fea5674817d89b91595d4ab0580c86c8c2d757f65a462717b8870aff815cd166125452ff0ad6a4c5643c7e180b150a31268132e42f22399abf9107506f9b18d911822ddeb4535b4d7f5debc98557824f468eb55092f584c201f3abbed66f465108f76d9bacebb0771e80a1aa317a25c8cc0f0c6a1932f19885adc2e665aa2b7179391853616ebb7877038529aa9a46c859a6077d9d7c49ae34cdd8e376448ab2c2c3732d677692aa66f0fb9c107b02446de57c90036af12897047cb3229e328ed67a87202312684e488972a5e1e4ba403d7e4873c2347219483d2e9b79425d724ab629964f225e04f32d6b66c7c75b9224c75d7942dd4d6094caafd89e113327b6193ec8b5b1f5276b356c944ccdd529b37e06ea95edaa09899378f9517f5c9169c6656095c4bf20ad0adfbc42f8586a4299c8af4591a5d0bc8a202eba2617ea74f159e4e522ca0a69468f311b3b2abbd2a1a380e4a62ca7048b31da125f6cd535e7709adf2fa2b7208a7213581cfbb5d0082642e6c115dbfcea55021dd0c6cdb6ac691c1c5ea7789ac4ad17a7f34b19693a347245024590be4ce97b3c8f96a5193eb454937a7819e116e397d4578f6c06", "516a", 3, 456556175, 1537743641, "c389852e68dfca984011766845c9228bd8cd11a340dd353df6d0cd689b3bec6d"], + ["cfbbc21004db57f4c43b88dd993e1f27a5103bb45bff6d30745fb5066e16d91663505638f20200000006635151516a006b4bf3d2576f29386d8a046a4752cb0434963a520b8e9b96db434f5b91964a5dede081c10200000001ac788eb485d7aabd5cf339a9262aee4d79b748a95fab8c7e03723493d2aeadcbe2d16a15cd02000000056565515352ffffffff0ccf11104cdd90c40dd03755dc5dbc47d2d2a5225249eaabc27b244d14b36dc40100000001acfd87064f04a029040200000000004b36fb0400000000045265acaccaa88802000000000953ac51536a536565ac47351f04000000000000000000030000000000000000396ff00400000000c19e1258336f6131bc6fdf61542e9a68a7e98e12671d548b09180fb4927cd38bfb3442a7aa5b10dcfa8f999e34c8ed65462d550393d1e5515da140ea29eaa988e2992678c37341edabc1c4332fb2006aa02a24bbdc242fdfd446c0dbf1899e520000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000077a61f627d046292e2fad16df94ff0f660c22f3be25800049355677282701d4bd149f573e1d2dffb189833fcc94e3c3978e65ac4a8de77e2e4240b718df5cae82446ed121654489b336227a57417eead6991d366658f298609b122cb1ef1c718f5c5177c2d091e2b6525bb50ecb52f6a32ee33fca8848a8d2790fd6bc5ba034e0213312d5170ec55296416b9a3f33f13204212919ef6d44c7ef8bc812f2feb04350225f9297b343de50bc33709e14ce56546cb424bc11699db500d22e85438c3dd240a01d978eb7a391b882f651cd1b24d82634ea5ba8b79765025c51fa2c8c2a13c53b7ebeb38861ac9dc3338aeab2319f2008df2daa9593e3c1e4d4035e965b35c980218ac40590c617187d72f84d25052b872273ac07dba78901b4837813daba22ddc022f3c727bbc8ea152b08bdd2f53c72a3168df30eab1de2f7006f46998dbd5cff302190637902463e1a79f17a5d093d916b7e8001cd3a39fac853878b1f61b35c4a2020912ebbc1decdfd41f74e630c923701d75fc59742ce7066a99335a549b6d930902128c53a58ad614ce280eb072374404618c8ae3a3f69e2c9130b3ca2f818cb7e17dc5d0edc800f3452951669382b6207bd2f890edf8d92458b8d9572ebf1331095779f74ab3cd17905372556a3a4b2de0634feb1b3cf63b50a964f5c507828c9a0ba9779d4b2eb0717b4465158fb62b27a091b6ee81b3118141a8cf9572b18f711fe865fc9844952fa035c31d8a12fa21792bfd9393a03eec0689137426275a798afff93248d6613b849944bcf1dda6d8b58b7ceec3e861c45f4d6cfc03c55e81da2781f5a387830151ebcce8ffb9a31bc3d9782f0ec281373ca32ee1f26f6b69628ced6c1098abecb42993500e75b28fa058a77c3590724de2303c5df1e31e0886db833dd6c498acded301b09b63b94988484062bcfa201a979e1fa150128c58dfd642d25b188c43767abe3f22667f5864b6ec3bee58c8e4b3202f5b24df32f96e5e8eb85db2077650a8f4d7047b6c20865ae7c4c66bfe6433b2d07ae29fa47f22fe0b77c93ef3013b122e8e28ac1329a569ec6fb2251c4f19607eabd726ed43cf467f842ab32017c2546051a29dd0151ba6ceb6fbb556079649eedd847db0b67b3ffc129b369796b6b4ba6ff3e76e397489176357050776661375e13871d5de63b441059a6f898a707a1398ed8f550161645575a844ed9ad5890fc27d7078912dd70301ffb93abe3af2a5588d7c8cdbb0cf4bfa63667a9cee5fd8c5964d367ba84498f54decc0565a917c05a2e063f5a478e56cef4df479a961a14430256f19fbd29ec2608388940a565796510fa5a1aa2918f9f2f734df23bc974da3f643143e24bf680a6f82c317b239af29c7773805a856c65162c9b915399de5ac238d22e64b5679b7d62a492f29f1c8fe64f56777af6549e780cc29eaecf33d2164e9dd82084c11cc86832b235c268106721946a73a80969a35301e716cbe634f1f5c2ada97ccc96bd4908482af4fe6d7b05c39bc93c62115edf51ac9ec85cf2a29f61d382d7afcfb1d101fea0e1cde3692cc2e3110489c11c28949ac322eff9eae47d350703aa32ddd023731454ef87ba138ea16203166dbe54732393404b560257885b31e0a6ad1d877ec8dcbcbc70ebbc7d1228919521a5b50e16d7f35f0bca24e8364ef98ea1ef326c7ac8398206a63c3e8ccd9f507e8ddab3092d1bbd49c2d332e2e1879631cd180b40a404ed7acd268a81b0d7c54033cc51f99c49675099c4c096c734883caf97169d402ecdee6cc29e0629bc138bfd955730df2680afd8646069e71762b2537cb14e4047be5ebc3c6d2e068ab9a9b67510468a364c2480b80ebdb12111b0a43f1507be93b51a606677387ca3b134245f3c359d397e3cdc68dd01c37670a87be4fcb46c1f53a01a18ee085a2f678cd5628d62086d719a782d35673b7112a37fb6b1472b48af7a1705746c16e3bf9f0a1d40b6a32b2bc2d9a2faf43c63aaa34c97bed2162258546a7a4e122ba9e33318a07efd3abd46720f77bb60fd08d6d1b407d8e23ae59a069d2a52d33b82bebd62cf6a0500d0a0eeea9a2805c5def3a3f536e80c26e146148ad8df509969c85b8b5242de41045a0acab75e254c5df1d73c514eb729c0555204e3087600f63c21568c785eade5ad4f371f7b9ab1d227d8efad63c8180bfd23bf7401b2b873898e1be3bc461dd31e0bb1c1b6e7f38ceb76bf84d5fe8628587a7cf9e92963e25682ede10a02c02cc8d3a6629f282c6b31e88a20d40e4122800be9bdaea6fc30000000000000000812a7d050000000091c6874a41398149ebd5a28851e491fd0da445daccf1becccc0055fe9ef3016caba306da7cf1f0d774eb8d716b7c4d0cb563b0a33a1de3853f3481b9cfe69943f0c7237c4509cfb29c7ca622d49ff6a07eb90f23c42dfc9194708e4b3f38252100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c91bd72fd83de781b9df0c9e56df0c0e88e0f3f34f405442ba90e0de30b10b620c8095ea011f5c2f93b4317cfa8d4b34d33910aa344bc14baa9e64d3da26e1d0e653cfad79979c46741a85ed89b4e422b98f71b40680e90755cc4805b53b3b645ed7abb9b942dff38ffa3c802f3ff64cad2b1bdc598b2cd6dc94a8d982cb075b0221c20fa3fe71249a5b76f2975ad36576b78927e3872a01b114eee6cdb31f107c03062510aea61188289cbae21906b4a8dcedebd51759d64d0255113bcbc71286440b0645b0d9accb922cd0ed9f7ba1a02a81c7fbde358cc906d18159ba197285e49342d3c532274b27f1b8439d79c74d394fb1f0a46285067a941a83eb998694f26f021eec69bc26faa699560a953b76c54673cf6d780db934cef39493e76b0787cd950214117c7d97c74b75dd6c248b07307783c9fc7524b090d868781cf9236be8edf6031dcd189c4e382f7bdf144260a2ec712b89b98f139262cf431afffbcb076bd3c4022d45e355ae10f102edb3c6d2820af52998874b08f288a81c8f9a2444dfacdf4f0203e1ee662fa002093c35723632ee124ead2c6a5844443a4abf5ef1dd6c99e9cd605be2e2775a9a352579f973c6e1588d9863aa0c93662eaf2feea72774f6a545cec5248445d9fb26afdd2223d80090f95f52c3dc5d976106917d8749eb423b6715e77d4243e19d436e4d46ca55f623d90fb093daefaf899ecf986c9e0a5529f693af4857eef54f68781e597dcb99087713b928ecc75ab46ec54f21b8c8567c4c59a068b94c79048ea54abfa51b98bcd523ee251bb31cddd9d5c33c780de3397d0f5704c75eef7077ddde9bbb4495714664f5a15a2d588cd6fbe69e19271d5a393dc3d5116656cd88c5bcda9bd691fc4e54b949b8a5c98b07913bf7d2106348e3562b12c9ba0f2982b327b595b86d79457ad20dd25f903f72f2a9508c6a9ec34b59022111b80cfff93cff54d42e28d630a08ef4ff5626ef6ad34a8c2cf61d8ca89470633dc93494e09e7a8777d79ade2b5c34fa7aebe4780a19fe38c5d8e1a4909749ad649c7402af4e36a3450dfcfb9c02466eb312cb2c871ed52a0c059bc1b1b2f5b22d41985b55fd225ee731987da8bc61c3f34acea5ce4c6d8b087ed31b594fa8313937510f64cee72d5a37479120f601d5f1c3a333ea230f6c21e7eb69820b8cce0ff7860deb50af4cc21f935d4f1e1d657ae2e384cb4b58e5b37a1d9984b9ce5b3f96ee6622525f02ee856ec7d98d6600a75cd7c817b92fe3dac769994f22d56f62e9a902a6e477c655af223e6dff052088f40f0a4a200373bd06b538dc8d0202e675f181746f5b71ecb98305976594bc41c0d081067b7cb1b1244ebfcb3546ea8f929d1cdaa41aa82b272c344497a963e46b1002aecb0c482cddf559b0b213378d1ed1f2fadec2ebfe9ad0f46ffd3f64b8a4ebdce9028e1aaa43a406874532f86803df7b3bf30325d4f5501db1993770965e7fc3bcc95d0a41af575d4c60424d2ef3dd575bf628c61712652e1e85c8d914dfa96b1753541916aeb6935d6eeca28edee36b86eb6bd65aa59a9bda736ff9cbe819e75bb9e0cf24730cac9c6600eedcc1889a6b804f5cdd04d74daf1bd1ec3033f99fd5016b486e7a6f5bbf476845ae064ff002db9ea2a926524c7be9e556112610300730d30c4a6ee26ca02657648c97258dc97141f583580446e327c57cad89e1e9a66776d10510847521dee858bb14d8f183397ff39e270d46c75c4b1a95e88c8b04f475a5367e209bbc48e0949be800024a5580cbcb4f01f51f48de15d2a8c3c5516703ee61db1b400db37e6ef2f8ea0e5d24f990c74854b8d8406f49a7772d876b7d900315ac79665bda2b717fd017654779ff30ab8c46cd86eab10683f541675523f34cd89d234d7936d2e51907c73c02725077ba0bda91a5b84ce2fb1261ea074701c95e4b7fccdb8ba8f1832ec286cf0501ac06b9358d4458d47d4a612fdf5ebba230eda377f93426d4289e03d1b3ba11c8819547893e37093a7c987a31bf1e6ca2b2d437bfcf383f48b8163382e9a559f06a480109d8ea75c2c0ab61b4d41baa3ca18be2c52c077d5029167c3fb02e8d1d5f9251072eafb92b018b8bc499d3140ad74eb146d83d2e5bcd63a9787e111b81aaef5c6f463b570cfd37887cdb0789e8f3642acdd53f381971360da02d513dffd40dac2799210c2223676b74135ba2579fb27ebe72d7ec3667c6c277f8af83bc411f17c1e46ede2e9220b314be8b0747b70ccdc09c181f8599e8d92b665e0aca5b50b7272818c57f0000000000000000ae27260300000000fc29c74ec5e20927958cf9db69ef0cc3439090b7ba5b45627aaa8742e32d0daecf911ca6fa134d790fd7f7cb705d73c421ebc2ec7c71eb0539fd59d5eeab7f348cc9096666b931db71e377403d2772cbae27060092477e1a62eff22c05fb42d9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008cd1c72060a2807aa0e310e7cdaab1f2de4f66b876bb37e2fd24a51414b32689ce65133e345fd84a6d791a9534214b5813e580eea8e2237740e3e73a710c7984bec1fbe41511563f4be388c8d324086f655502cb7572aec4f90b2bd498c5b880533ddfb5405f07b9c0ac38828bcb1354b48eeb7a0eb39fcae6a16655fa11a7cb021b80687804ec1eedc7732e20d4fe274ff0fa3b0fd9f5553f9eaf31929494ca6d0325d1258b553e827176f16aec2202519d72a61d0a5354369f0b631817051bfb580b00abec5eeb5de30114bd5754a3e8429a35b7f7e8c99f6b391b801838286b292c5772008d43818a02cfd5c7450ed0208a712b70abf2b67d1811038bf1a9b492a70219a95a0ab722b0ed946bffc67bb42f3550ff6aa51b2fa48c3c2bf8158519f625030531147e86449627880416febd3b9c8b86d0e3b229c8bec5e54fdc155ba44ee302276d5b13298d1b11842b4b68b4730aafa4b2031fcdeb55b0f0ef3a34981bd5e9022b948e494b06a85c5a7b06749a5031ad61fefe4b2531079d9e5cc6cc78bcfb110210a9d05bd3a448cd8447bf224a98caf69de7d1832d7babbbf8775ad363c60adab8770613c2417b265705a71faca91182a30f88ff52ca4b6c799ae8d681eb3ba5cbd5509c5d8eb0ea378dea63af8695d6be062d8c048470a4c53a1c2322b4198a48e223ea664d0f51aa6b0b158830c9c9a80a02dbb6170e34ff7b4fcff371b2c6dad56896a65ee040924eb7959866379c799de78b3bee44ea2310099725764a9be425d83727de91ac6e00f168296105c795539dda79e8a2e8798c65c0368a11e1b5d1324255f23a52b481b574401542e84da0c0ef25895d08b13549ce21b0073883ee73021e86eca96ef260d339e2107fda6c775b007ab99c501a365b7bdeb722dc4c23f0b78fa6d426661a39dd747bacff71683eb3e3598eefbee377afce32887b4c42adefc687edf9b17a4e8826f1747a57cff8ea28c5aa58be97f65d063e0ba4a9a1b4a202407fe853b7f5fdc6aef1920c920721096af19fdcea3a2216a1facfcdad605dd836e1014f82d1d6d8fc640488ef9efd3b12baccaac5efb197736e6224211d3dd08700a8e14b0208a8fa2886c5c71176e04aa376dd11b776cc6d7a50f6720d0fca26f7ebd897d885d24c2a53b2eee84b7aec55c09c12e480dab18ca3091e33afd89a2b70990936c706d88a6b22675597a7e6eca4995223dd911838f71e4379f696753a9a0e4d35013a3c47553cbd38f88073364e3fb5dca3983dfa16633af445ba01b1336cf48f7187cd9669995c774421e7151a96ad714168e656809fd1d9a62b9f115d42dcae407ab35fc36eda9aa6f5e150e2d4fa30e01946467dde1b704b2508a777b9e812b560a20914d0fa2371816ea1189c7157c806023774b783eeb20c745241e2415b0463ad546919299c02959816f89b71eaac823b8ecf2e5cd6e00c55e6493ec3b20146601e8e74bb103fcd0c35544e38e54358fcc2b61298c21baf061c75f6d42ab20a583f5ce2e09bcb9f723f8f72f511aebbc02aad77347b3f4bfec3abff5f5dab9346227aabc4f5c374e8ee5e8cd847db1cb09fcbf8395baf5e1d356c092d8e7bff1f0fa5ac4992616adf03e434f214edf0402680d26bff3a45b58113dc33f0076a7b24afba5251c61b4d7c8a7474f38cc60efb85b138275d153b7d0db4747b852cba130689e582f67b00d8425abf4a544811be13aa94ca8528e0b674b7b5e1663553af36fdeeedae5efa63d389aaa204163c461a5dbe0f4b0d4674c4ad112b19647fec449c50382efebcc45fc46786b90713ab81bf44ab3778e69e31f507658d893a41d3438a524b4abd9d0e8f35a74f39c87204f3dda521e4962e66e14bc13d81eb566e0582b9822aa2c61f8f0df3277c3d9bd650824ff6fb1a32dd9ec31b5a3f9b20e1bf44a610639bc5e97ed971f04e44480308aa6a5ba109125b72380ce4983854d77cd6263e00309b04c8387e2ac7321b9ed1cd0d01c34efc561bd726810f074bcbc91258a40c26a2db497d08e70d435f3bbf2f1ed5fc1b32257892af1f6653c763537281652ed27fdca08ae8cd4d5ee32d7306f4ed95a6f74b2a4c829ff578b3008b03f5794de487477a53cc1b3da555796888f5be84037b9bfdb6f8af19f18ea8c157fea34aefd337ea55d697bed327ec937094d3ec8fddb9d90d4913bdb723e9322a503585e1011d52d5e31a52d0ad095cea3135f808f650831b354426cd48737fc6dbfc19f189b49d32ea4308debc416e656a64d2fd15081097a0b2e9ada9ab70ed8a26ab8597b72a2b161254f7188f324baaa74c71723e6215c9844199d036af32ea09fab1f3a1edb378ecb140954cf8fbf0fb41144c4f9c18356859dfc80e4aff6c3ef27d08747609456ebed32840d1ea9c142bb2a16e32b3a188cc0019e50f", "0053006551", 3, 1575428823, 0, "2d87beec6ff7d96be91f95fe8439f9e2c8470a272388165a4196328073198199"], + ["0260154501a8bca20b25225b3e5d8f66b1bf60e17b826632f54f62932a388afded065ecc0b00000000026565ffffffff02cfdd4f0100000000065253ac65ac6542f863030000000005ac630053530000000000", "5152536a655265", 0, 787355631, 1537743641, "1932d32b2dcb95cf374fc5f5f4be46a8272a70536e98da150cfdfc091f862e78"], + ["0546741902c73a20f3cc22095994bcf6ea8fc42fd52bb1454480cd6954ae6a905e3b04326002000000086a6a5353526a0051ffffffff832511c77a32fef2c1ff0581ec6d9f5d39429e75c10dc0e2c3d3d45e1e97870701000000066a53536a5351ffffffff025c5f130400000000056a65656351837ce7050000000002ac000000000000", "5353516353526a00", 0, -1924696480, 1537743641, "3c58667639ea7056da12413f0a4f3e44437db6c3f316ab5c8eb3b61362e73622"], + ["e029a40a0227714e4609d3b6a76fc76bf6b36d48c485b1045719df08d2388b370b218a5c4303000000055353006551ffffffff09aceee327be4659658038f3f976ca517bb7509af2c5220d3176709d68c57d5d000000000700510065ac5100ffffffff04eb5d14020000000002535164004901000000000153cbb19b0500000000026aaccc388e030000000008510000ac516351630000000000", "5352", 0, -367524853, 0, "5f7e5dd9fb2f1e835a1d5db1c3263ba92295702f13062d3ed3d9a660349c4940"], + ["c73b5f4103ab59d3b5bae63441e72de22394bafea62074187e9555a78d1b02aa3d441f7f5501000000066a000051005295494b2c9dc8186ad05cd4b44147a6b83e47fcafc3e4e1fcaef786e5381dc9642c9e078c000000000453635365ffffffffab2514dd587194cfe32c21c1a9c465f993787d5cb215dd045c9c953e6d1617ba0200000006ac0052536a63ffffffff0320b41d0100000000060052536a5251f8baf1000000000009635363ac535352ac52fa3bee0500000000066553636363000000000001d0d3e104000000000000000000000000982eae68379f8a103d59b35a0150fedb2203d58239361ea0a61983c9918991b407c32f2d5795a6f3bdc363849d7f9f9807ea170692bf282900a4283497ffa3200cc88b802b0da4e74cbea45eafdf50edf0b51c0b329e8a0e0ed9d543966c27ca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006010c744d252df627f6b6edc8b3491b3b8f4ef01aaf53948bb9ffd4650f515a51c6c8bfb0ecaf0a8f0c4bfd5e4e87ce1672083c2e45e908185df8fa49b50d03da1dbf4ab2395007dbb3cc9d23eb77a5d4c7efcb174d350cbdbb102240c1e4991b544458142bfc5df8b3e38451735dd3e898d6de1934bfdeaad533be1dc4e21bc0313a45d534eaf2cada1798cd8cda6aa9c2052d51096ef6d51dfe15b790428f59602294396d4c8624e5a332bf9c129681af3424e3b9b33136bc63bb883f47160be6f0b02dfe19645d7e4217ba1c369681d49e9813e59b325a4e1bb8e87da3e48976fd8992b6194ca78f696c89c2292af8c6dfe0f3cecb0fa428d3e621876027beacff1031c3d41b52fae170f824035ec893cabd1ddd6419c4c49c97ad2443c881d5f76220307cfcb58f5cd70fb565334885ed473de2738bf1d455784677e909da17a54cfd00320302992f94387c3075ff3f9b31fa2d237e88a1900dbe52665776ea99e66513a031cc3b006a0f7c694b8ea9cac16c93c32838d383bc601946b2e236939359669aa03064620a06833429262db733cac3c4ae2e57371416b97d321787b723b59ed417dcfe4b3c2b58dc643ff2f6e3b4f99df0e6f8a69561eea93f806cf9adb81628e75258673f9264772d0426ad779f7d23b5a96f10113731e3fbdc4f482058f647f38f476d241eb4347061a9e5d7dd2f77e738de8c35db7c1ae9011aa622fad39a770b7170cb20bb6a8c4eb3535b0934265690d40800b0e02c686cab30e986538917fe1283575aafc625ef35eea0f0ec9d071554b9147ded1d737527eef93547f8495784e9a81c9522d2d7dc73d18119a77199126d559d9913f0e45d5fd4c5d98562a7916499f5e0f104e7689154f1d6adbb4aa20ce1a52e3e0c8fd9f8870ccc64a97582c429cce80337880fbfba399f2e35cb83a76b58d4ef244d11764ef6899bc153e2f5df0dda551ce4746fa5ecfda6297c3ab44399445f797779d5a20de9d4fdfb3d99a945824cdf2df058a0533f4e0162fbc2202a76a58cb31b288f488f3516d0b3294b27cea912370cd122f67c211ba26657fdf171c725ea33edd1caf7a9631ae0af7c5475eca60425187b09e37023c930db601c3183f3de468d762c3d4fc97bc950411c5b5d4e01df807da4e17b9fba0fac4969d791792e71dad83b710a86c9dde58dd8e45b7e04d5e55307ee7954da346e95cd45b3a870c82fbd9175fdf0ce3faf2babb69e08d202ef39c38d74f232dc1fc5497797f8899cd53ca52667349b5d2e7393ec40ba3a81d9a4b84cbaa21f36ed6cec30f5bcf07063caae9f939e1449266adf7457497dbaabfe4c816582726e27147af80be1adf028994189cf9746bf9b4879bf36d69978c6b90f8af8a49e129a961b1d4ec373bbc371e22a507f03f816bc582f341fde1746f6b76b7b9d0a870b6c095e8a21028e146055cc52adf56e22c8279371794bdfa5292154900d61b2f98842b7cdcb4dbf4d4efa56047368b54b4c818b7c72f8205284b2e700b8cd98b21c3a34625fda57f324c5435f8dd6fd375597c002c3239c2a8ce44a876cba59c000e4bca75f4358a3c3aa7961ac7cf21a27388c0cfdb998b7051a3b8843c97be48d8fbbf0b08dde9f5803d2a350c565926090fd1f82ed80c12f25513b996657ef65e73a6c544d3a45cc8fe0c575dbfdf7cb5143a1a74133a42c64229a72dcdb5924c7b5e1da9051a5388a129aab50cdc71a1f81dbeb8920ffb615a839943ce1992ac87b68ad7a522aedbe9cc24efd39894edc884596c075d57ec995bd216d1151cc6fe9c9d220e9cb3ee200c1713ed90829d85f0bc33cead1a026da6cc90a58eb002b412b730baff5924ff93a8dfcee11db8cb544ddcebdb36c8c29ac7c9e111cf9a65f1a6f347584cc2a8d0384bc155760d9b2230552668320a20875c456ce06cbfaa6a6040758e7286ed27429c3bf3ea2d6ce771d0b9aa1bd21c226d8ea7de96cd3d464bc8752d9b7de5864e139d2e9d8a7e323a9c49ef8c15fe8b1982cf82d87e94ed4aa49819462af54c606d429cbf3d3e34d2495d9475724b235edd3f58d0966394408677345e620d45ce5f332c4dc793bbe980b965bfe8fe48ddafb6945a4a36bccdd0f2ecd44933bb11a19e84835639a0235078bace6c4657b90ca8b2fd31643eabc27249ed88e92d839577edffdeab38f345f2a317f38d89e3247519f7bb9bdf75834f27e4c8e98b0491adbab27f4853c66e9b056cd2418e94c6ec8e84ea1dfe280a26e69d7429e41edb11319f5744d0c773872e537995d1ff99b1743c51552ffb977fee9fa71c8ce012a4605fd60afc82d850b27c32e1d52ab085549606c34e767caeb2c5bf01a5e2660d18d2e0ac44359dcd508d76744778bd4fbeb062b0e86a7ae08b1ebc0036dfbc49d822206c81b1349a6728c3a1dc04e97d06", "00526353", 2, 1612076931, 0, "ae3dbbe55a4f7f98004224cea88fbe752f3b159c1a827ee5c587fea430de68e1"], + ["030000807082c403036e68122408f75f2d97cb9d7c81ca3b8c1cce95cbce4639ab5e99ea9b94715d52020000000753536a636353002e249675927295eb8483a3c48c1eeb92ede7d5383acf44fdd145f29474eb3a0680a9a6ef020000000351656a5b3abbdfb2900ea1463158e8e02a96e5cbd3a5dddc4eb276c97228cf49f042e67f4b10ed0200000005656363536affffffff0461f08105000000000453636a53189e5d02000000000965520053526500ac00c5cfc2050000000003535251221fd202000000000153000000007bfa25880200000000000000007a1f7600000000009bce305cc5263292e5f0d0179dccc4704d8a6fa992632d4698e668e697c463dcfd3aceeb33701a11d411b2a49a35c338ecddba6af59b393931fe6c60757aa0a13ffe8d258b0bb197752b5556cc0b611593370eb66ae7c7009f4e98014cae7a1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b22e7294d7330e075b65f94b4e479c7f03fb3591171722bf41fc28f9a1ef7adad2dee3e0f260afcbef9215ff73d82145deb2d8008d98d3139b4f6b00df455213f294d64d2f4e2be0599d63475c918688e11e14f1547f75783fde6406d8aef270eb43f9e29a3c306afb8c44fc376d56180e66bfd292a8d58434ec9a862e357fc70316e528faffd5620b6d518339252481bdbebdccbb81fa67f12acceee78b97322f0319abc84a4b29a049ab37d3491b12796833499b1d8f6619da5d94c08fecc9ca600b01e4f5acd5ab9a5647e80cae89651d09ee26f64bca9f01f7eb6295c1cf56b4a7a080b6c0d61eb8282aa90948cba574d1c0f05a192348f495f6097c8a07a03dd702200904fd4fc995963ed7d062d445bd3245f5c40cf67e374d23d5010e0dba81d3030dc2787693427c99f2831ed708c537ffe9a7c05a17cd2c45d49ebf33e9236702030a5376e71d10e427ed23b9a9f295c52c5b44e7336fc712adffdab5e298360e4f0306b283642efd60e0fb563c8c5d260537921632866c1bc07d8d65400e7bcb2305022f3daf8219b2d548cdecbed851a3b2dc92341acce4064f077be82c91b5b9c2ffa97db89d1cdf27e376684c485adb7adc3d637780a7cb627d82955cb4cfaee0e8c30964f8899711d4abb3be144297a329e628e6425ec109e0ad848d9d74559987f6224a626ef4e0d72baeb94220a63ec00a09f67ba19c11ed519f438ec1d20956d17f294a9c52e16c6b12a4f0395416cff9b12ddac031f9e3915b874b66104d3912f123427111b1865192fa591218a78bd5041a8c21ed2c0baa8a1aa0aefdded14aab9ece6d5dd7831e9e3fa52c15d1c629f44dd7885b84caebfdf782dbd5633b305e302a0e855bf56ce43dff1b83b9276d2c9b61b97ebf7806cf7b285f81114efb77988f65dcd04ce6cba440674e6b5d9852c4d11dcfb243d5859079a6e41ca514ce2f420bd3a2a3f312f6f9a3cea3f96821dc62e49b537ab3cccb4336a233e58732d4f5e9d952a8316efa9383d81416c512ee29a4af03eaea7c152fc81c861ba15429d317c11b4d172bace9df806c93bce2fc583a199883bdb3ddb0a357e14859c9c919b28e4562b9409f405a213f422da65b0a71d2db78509caded62f4fbaf9f3bd900630481dfe3d62110e5802fd327c7ea6a99d1e6fdee79c4fc6c47dc69ae64936b180b5491579c159743731d0af857236f7d480b3844be70597fec904ecf18559bc6398e30a885884110f3baa9de37735db266dda1ef6239784195bc363d55f07da4b6acdd581e0c7c879719104c81aff644198e987ea285d9b43c3d84c07161fb312d6bcd1817385f823191899f231d53050a1a2b8a40668bbc5b503dd66d181730f31f882d1658bba81eca41a570dd5350b276f3ff361eb07219bb7686469e9399c5ad913bbb08bf4387f5f58e745c84f31228e097dc66becdebe70056d4c8767d50e0ba4f76e13cbb28a83101a8ace29ad32b56bcdf2d528ca86ac2b655e97bddbbb58e293d6c0206dad7f595b7554148b29f5eb83c8e169dec3fb7fb51230604654a92ea0bd6de982a574dcffa5999e26f02e8c8a3cdf3e830cdd5c3152c810c3d578dc0163d200cb74e74e7b965e29e15fbc1eb9fec8b63feb8e74d7d246d64bf274d7e17e6565676b812b2cec5adbbc4c23b14b9e824e4a4afcaa3d5247be70297b221f0e106895b632089d763ab55bc5fb464b8306fda41f9041f54ca20a1e03b5ab942c2ff0b86d8763c796a6a75d2626244169799300b8e32388d046266663d510cbf87f6c1079e894df78d0d44d9727d9e5f66b2b55026bdf42cc0e20ac9b7abee59eee31035534a94453de04c9d0fbb5918424684bee6f05dcf54e098eb2587b47e900df70860a681ed7dff7ffd268305587b2806741ffd653bb4f1fad5391cb3493cd62125a4a27e17d90754a0d474ca2418c9dccfbcb91a67a9021931ca8867395667b8bd925daa44dea1eeae95c7e562bc697cd8c9711c560f3c2357b2c51182efc8536e61de26252f02431dca8f0fbd7782283d4b577481df04a8c0682b52efed20a0d197bd7c83b272915c0a80a143e335130ec991a358d8d31573922be3e16b070bf1b484290f4766b3ea6c810330a85c6eb9d1c8fa723d205aebdc3a3897db302c5e0cd64986b03e258c27b1cb5ba326d2a9c5956e0b48dd4efd106806b7a1f2d466ed0867fd353e00aacdb9cba4001ed96d88c0a28f3d51fc935a386877dfede513338f6ed408e90c7a05f5f392fbcfab3999390c0f5161754ca281bb4972d201cf38d455bf00000000000000008352ad040000000014aec4d73913d89a08f7f9a3d932f4bc5007626d7215713812736b46250be6a53fe40f6d2f87b309e141f239a7ee7916557cc4326845876a2d814270ed6b57bb602bc846165bf501a1d8dda28ce7187aa624ed46f0cae5a0d6dd3b4010364eb100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000754e565cc712467392785ff3b353ce6ab7aa1611c17be6c83ca25cff33e42fcf76306812bce8790bf2fe45d24c35586d20a6ebf0d2b81504448bd6559222957f9d9a74496e8a5423018c9e031827f886414a8ccd4f2135c5742c3f7034f7bced839c1235ee83ed43ff8309ce4158e1be8a15d54aeb3d3e891ded80e0668c32780228a72bf9d294edd49fddd68bfa60bcdc1a4476a641ea2aa5bf08fed19b5cd0f20227b13e5b52471820d87a27436d8701870a5ea6c936c7970bfd2162a7a0b612e50b05665ccd3b8a52909ceb9e8bdc622da39f40badbe930e9e32c89811ca6a201075316d8e067d246d33e260d5f66c6fa541237a5d558abdb5743fb7c0065202ede0314daed4420c5659c5c3c746e585c3469ecb35c7f4d7fc9d05053cb74be8ba34803164e25bbcb24ab23c4e73b0ccd24a1ff0113ddb8aac84b62e06e3a213454d4360209b40aa7d91e4bbbcbaae3f4d5c1f027cb998bd44073de85b0ae08a91539c0ab030d3322ae2d00403f63a41e50669ec718060b21bc3a4de02b38d75ba6aa11be8402298aa75028f49c7cdf932d2708046eae357542c691990fa47594b523eae90a39999569bd96ef9ae31241e19d15ab9421edb3bdc962e415840d4e6324f06cdb30610ad44ec1e89effa959233fbf2d08674c2a8c376db7fc08d1a9ebe130bd9b329efdaa1c79469c7076c2e8019c52e0478fdaa4e11bfa44a83ea1b337abc58f9064f386b6cc38d89105a8abccfd88b24e96b54a76969673b05fb0b2706af28b9e7198e2c8ede0b1f1603a03bcceecb810c76688d2881211d0961b5849f6ce562dbe33c0747197ccdf1f8a892129f783d9a0bdd6462a1f837d02c5b7ad5c795937fc67fbad081a41d9c8f1f35017c6c0508c9d1922bb1659495e5229447312672f3be088e4f4fc7cf6420b89f5bf2a8a6aa7451a52740ce648d3affe143b78c8b6775b222b7a5b494f10695f6571178cc3ecc97d6174455f3085c64dd647d4c88ab30a9883706a3740c531768db32f838f9c4fe238e1b1d477513410fc61529d64596ccb6c5a030fd692d8fccea5662ba365a51594b50c5a33ca13a2d163bae74410ccf15b2c17fa9a2ee2e4eafbf9a9d68a0b9207cf12f8a5fcb9b08099438dc877aca2c4542750c2c3de4c90c7f272d73e98d753e7c14d1b8e6a9cd996937444767ba6dfd01c5d39d678f608620ef919e4aac5ea699169c4c89607df56c8ec629bfdac803355be5ec5e2940a9708a49183ff82b0b00b5ce738dbb5ca739437674ec30753f9322e73e1315a5529f2384631a56690d6bdf69f7a6b2828b44881deda96bf78a655bf5c3150d0cf4e72cf5ddb6342c8526f9b240b5b5d4617e3e05f623707d347450f7a9fc979e0d0cac6ae7082c1215a8272db5b5c52f26eda91001d838c2327db8e70b90f97a72e9cf06b52dad1b9d8799875cc18d0602be5896fc361a670a83d97b92f257140d12a1ce8d85b4fd543daec2eae8962bc8f23508246de18a5b0dc6576819e0829eb27845fcf85947103614068e70538ede9bff0f3c702915f4a9fb996deb6956a08aaabc0039163c10d7a92fc4d46bb4181869657503f96214ebc9d3a1f10405a55ce643bb7ff1ff3c493909dd52cccffde3540201bc508b05c51766cb51e9cb56a656466998bae3ccd903cf92387d8fcb355b717e5d505fb42efac5d19775583e8b3c15dd591a1284e1018470d72fbfd65a6c1d3686291b6771186a2b4c523715f44f721f34f06a40b04119aac27b5acc3bc02ad3397efe4c165fe6e2c51f078ddc55ed0cecabd736d9fa858aa565c48659a89a64fa3165160aeb3dad84a6c9a25df17c79f7b5beb0981698d03c0cd4504797d4f8e5962c2ac22c2d9ecca3b7ef12453902a82b559a1bdd1a73756bb7c34a3c50d3ed2ae3cebb7e95f13d901b091a169a7e56c911b4cecc4ae3b78910a7ab78c87cf64b40631577402d67974633ee9029073d81d56185425efca7cf5b65d6fe2d0c96f2dcc48d3bff2891d4bb915953a34f84fababe573aafe4321a2806b8f2b77a3504389f7fd1d987694bdf052e0ac566fb4aa5a60bfe2f6a31795c1f366209269e4654d4e03def3ee3d760cc45a9c3b96cadf811c568156802de0c9e0097094bd04bf02e933bed22c7d46cf180f6671bc2913f8efb70c16363bcdd938133ff38728e5a204c9fc923d0281c6ba45808bd4789ec86f0a56c37d56e79c9fe057957f9bfdc2b1f2d321e9533ee0d404b38f49ca113fbf584cf290b01462a6885de48817eb0939be30ba625776b85a0316aac3047fa7ff62c50ed55869d9a61eda005e647dfac8269557069e30f27cd73ef4798a885ebaa301fd20047e8577e19299bd6cfa0466d6728a4b06382d485307f9280219d1a44a2f2e2e7ba45a5044d75cf691d85e7d9eaf8d74ad0c236fc380ac430a", "006553005365", 2, 432100598, 1537743641, "189bbfeb57f9443fb895e3657c23707180e9b989967e7aa62218c1f34bbd5f90"], + ["030000807082c40303482e88da7538fd8e3fe6141ea04e3637687422c72d66c0d4d5f0f5a918cf606f02000000025152ffffffff296ac87f86cc22eeb95b867888c4ce34f715d0627fe867e38ee79a3055ac2bfe03000000086a53510065535152d621effc28c4cbc2e9446dab4ad11d73333d3cde877dc39690790047d2e9da1f78d416d00000000006515351005353806c6c0203ba8da2030000000000a09dd703000000000451650051e9f3550200000000045263636a6ccb2d0f000000000300000000000000000f704800000000004000d1e8e47dcfa84eac3a2eab3f9030a5dac723ce1232f2ae62a31f91ac65f7dd83563a0cd975fb355f874c7c4da0f719f3825af9f4c79c7c0c10669b291e5985bea6664bd34658ad7cbfb9c0b433912320092f1ea820dbbad2495a67b1d8d4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008d59ed3b1508c9fbb4592827b3a87e8a333f27cce47e7e462aafbaee57136d089cb9b44c605c12509f4b1c7c4651150a8e9907ffc401e2c1562fcc823e32e4b0aff733561e18ad27bca241c684075807dcb9888fe1d1fac7bb7024c012afc89063e528aab0bda368f5b5d9d77c0ffd1216a69a3fa2a73b4470ee0fd156ecb8a3030ee91be5267e18f97fb9522d844900d3d8959e5915838972fcc0261caf4cab0e030d26a61cb9cd30acd5639723ed61d9f73f13c5f3c608d08ea4ba603fe7d4ff090a087cb4568e346ba9d7b58b8d603ae9e51c011ce6617b0afb569fad305825bc0c2f9f845bc11864a55eb5cb1ef60fb746c381cfce6bc5772a51fc1698d54085d80225eb5da24f617c31825a38c0e48242dad04f87987154cbf00291326bf783735d0312a11bc72dd10a5f35ffdeccb568129efef3d085e78c6283c02be2bce60da6a90308868483a8d14cb9e2fe629a2dc14febd45c738c981afc26f76b97acc306e1550309bb5b9b5aabd31d03f8316bc71a1e01a1761a1728c63a66f7b0b0447949462d0227b29f4b993cf46b841373ccbcec9d6882fa6f29c04fc1c746ba407ab8d89545148637ffde46d7fe3fd66c9f7e0bc45c644058c3f3cbe0cf315ef0f7964a56b6801135459fd3bf94c9104dd925134c589e76b878b5e9c507b1c0940839b3d991516ac9c2b63ef3a7dc5d99eda385edbcf46eaab11ec47dffbdfb9251b87b1147ef38ec1e4cdc71c2cb442e1625563ee21e47401308eb5e8c3efbe7deae9b0ab9eddb0a49054b2cd32cfc1c70801eed98ff07cb62c1381f7967cd03eaef19dd06575d2612ee9928c5de0a9315b98935320942d0b0441e76d7f98867e28682ccf04c0f4c782149b217c8fc4c7d7ccc58f1c755607f9d6d5c70388e170b5ea84f2a421593bee0fbe63e9225704cbbb3de80276c4838e663cbd734a4ce46df66a6c2280cae43af272968d116160fc8d87dc2a1782cac368f5d4284178d32aeb72e1654b7ce9251c9edfccc49e82ca3681096aa1353b74e6bc98c604a8b2e4d0ceeb26a0fdf9e9ba484a4cc6f9936a928e63aeeb38dce5773b86ff4228e6d3a5e3cac1bbf7f30608e40fab185cf6aad5cc0c856af4800623361bd09a228d63600aad3a47894891c4bd0a378a6584267eee4b1160733ae6a5faef3697f939cf063bb3edfe7cee34a9035934848b6536e58f63ef32cd153742a98aaf430c7503d73fc99289248e662a1980f14b7004426bc6d61863f7234665928d9abae73a7a7e542ed174a9601998f6966728bdc05790656ef16714625986a03562a7be753b9d58bff2c91939f5a189cbc9d232609097814b8b274803af0df04ab0601fcf14993e53ea908a6f7f964f931f00814e06dfaf59547ba73ee8aa71c5039aafd1a9b89709ad49183c9ad17b7300d3707064fe2acf917dbf6382ee41f5b98ca7e04277f7a2dde88fdc3fa407a680522b456250e70dd954c74a91c3b9a86e2fe2a64914f221fc12455dddc349e899d9640e90c75bb64a288a13f7f3153ce34111632a267fdceeeb67fdcfc7c65617cc6239b50e8248507d721ceb2df200909118e471d04afa4a2bd71e1b5d58ebd577d4cba3da691bdedf77b6e4de5ea71be31669ca6046497626415bbd9178ad507b7ad9078a876dd421b20e408adc22ae090ad3358e48845ab24169bf1c21da096769eeb4932936acf8a95ba16c15555e83989bdf17082a896f1999bacf271f1d42f9c9ca488882c614e86f3d18a26700367d36efb583e8fba4a4b074df51167eb9feccc13554005a4017b887bfa0df8d568de0fe819b66fbb35b0d1d4eb61bc9b9a28dd675f7120e08c931d7f45fa33215c4259fcafc81592f8f2dff018ecdbac14dcfcb1d07b6483dfe93d567c4e2521495a2182c157a8d42bf35dbe171f51d1dbee269bf27edead4698d955b846b81602dc25e5dc0fd1a8803d52d196e1129c6db55426af2b1b8073704254775cd568e43f486998a41419e8444c3791bd0c51a07af4c295325b035649f5bd9d7ee42ea28ef4711ed556e523154f78ab9a7c5782f41800b9ef7a4b495ac15c2975e632906f80031b17eba7c1c46fb60c9386650486c976447f7721fdb5e4e918e4f98effa9d34e3655c6e7e32681e7dd7083435be749a86b2f7d380d671b3e9981d4ee3da56f30fdcee4955362b6bdfde1497a18611ba3b5ed8036a4677a4fda65ca500fcd95a9ee44ceed32c77c64636ec43e17c2668ebdd99127ce744ea5752fabfa42d809ef4dd4b06828dd9e9fc62046475a7cd462433a7af1d20000000000000000e6ffe2020000000020adb623ba399977401969095f018d59d72c8d8510d725dccb821304f1465b0bbcf7dae10b638dc2048934974ff43db45f6901ccb5db6391ee4d6d107c9881fb228741d4fafd3bd41c062769dacd0d68138955fb100dfe075e13e3fb1295dc410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063bb0effda89f23d9657f2396f877c877845575f8c6317d435d7af78d708e384a345eaf1f18bd7f7eb9a799aa98c11cb6dc47de8c01a16a6a87e23ff40c1340f0b9a57ccfd87f4c051cfa35a3738d6f70b9f3d5c121b3b61ed0b0360e275d7054e98291920a2785f9966097405a10cba6c600b6c23783b47fc68e9596263b516022d579c0b879ebf4c9f8597f830b6f35841a854af08c2795246e521ba9d5969a70302ff28417dabfc2ffd01adf6cec907903febbadddc251a18e48a2d174f5e42ee0b035a6bd634c22e1a3a0c8a18c959662c2128959c9ebb7a1fb2209420bdb8a1cec9a5fd6c0090399ad7bd8a7661147069a2aba4c09bfbf4f9157ad6121d53494f02037ebe0dcdf5ca6e1c689d2ec9799908fd88a5f9d009f6a684e8ed207bc58da1021e8eac0cd65a7e596eff03d5f018e6a4f6fca51226dc441059ccd2a59f306674030df20c446610d705b12a6e21639cfdc40da65efa4826f29ece4438e6c93af4ca02258df474eb90739a5772f82929e4475be50ab068bf3e8a66e139a5dc8b753edf022bdf9aad95443079604d4041f76714da175d2d74bb9ab15bbf9db3eeef0766afff338024a94bd2a18fe389152c0dc5a25ff156513cbde453cfe1c621bb32ff35a9cd40e4799c724f9c994d8c34da1001f2e14607d4b475d594dabd12f15d86f62c1e733dfd4b354eb4c442431a93dc785b685a9b3b097037505a5a6024afb9da066aac314585dbe29c24f09cd0ebdb81e0f0a6c13f309a52d5850ce33b21f9a31c38070bc2d6166a6e6d5f17cc521fc9f999abd62791b144b7a7f12072d1c24c84ffacde655f6e438c5febcbe998013fdaa8806f169eb75af4300905fde0970780a4ad3cb32c3a28e3615b911df82dfefa728233617fac284ed29846a8ae83e070b1de01b07fe439766ead6f3bee71730cf30245da0a5002a1f2c329225b70b3e5209c8236cf04b54f6974853de577d8382a1c2e4472214c6c128050c3433513a63cbe5fa64b14aaf6926fb457d8f0e423a1e0853bcae63697f9691b43a1afd438c4686e5fc0dc0f83475b0003c048f24135becf844c2bc5f5849551025888881ba26e11c031ff221dec6333351997f96584ea88e23ff36d2b8458a08c9e8033af5a5c7a24f2e7572fad216153eff098e6405d78c3e8ca9f0006387c91f278dd8789e71ec585e9f9160201fa1442390c08e7bae68c2f1c413ec1454a564f3dee7a6865961c1b28aeca61d85908f952e1febacde2ee8c9e0b367540a736660c750388f35e150b8ccd0e3089bdd67f5a5e7443b8ceb0f7f780002bf81931f6fa417ca8c5adb89f5624b375321096ab3f3ecd1a22c67e1580022feaa8e253e5af66f4c57a5a7d850f5b6faf860f6a9d34573dd448ca527c7617c40461ecf27047d6bca0af21f6035e35ab28363aae3dba97de9bfaa0793e5e7252472ddb192964959a4b459e14fdb4bbb01a3773fafcb18bcfdbffda5377026e37fb0ee419b1ab38128581549a65973e621c648c5d921c171979b92fd15fbb3a6f173651a9fd2e2bd1e222a7d55e8cbc9337b601e9f85432b608bc21774f0420bd349a57f2ff4b393c6408b3dd2c57f5cd7f6d5b1bf84334c96e32301b642f95b28c4e72fc2d79ac137a1dfe2eeac449d7f606cc6a6d6b12e06afc91e47ea23d5915a68827dedc3677770a9af20eb99c2321c1fb40870aca89fb9b08856e66692e21f662c26bdbbaa84879a07f8f91299078e11a5bebeb49bc42c85ddbc39b9dfe3316f66dbc78fac37105bdc1e7532e23083e2a12c366158cf707048a0ec3154e1831ea8b399855d29012e622710805246aa0280713660e980b1dcbf83c1a2465382c4c515c8022e059ed2fe22f7ece9a1d67ff850c683cf616a4ab7cee0d80d5c1625a9f282314be310cf8b43661386a773d65ab0f281b025ac011e2f1155da665029d35a9b3cfc127d60a6a00a111e56e50ac75a9316b86d20db38bbfc907fbe3748c840636601ae25163a5cd29f42d92d7629618b297d9e3949145b260a77249579adc9fdc56f327d3ae1cb139172ad269592b7243b485009c240763b59d736ad7885ad78a618722bd1e1eb8ec7686c1983a9ce439bee6e36c73b913d5cf76815d26b99c33a431e64764ad4ce1d0ef74bbcda3bc064bbe096eda164b1b320efac5943db89a684fadfa6891f2bd7be368ce27de3b4aca6f14a59812a4291c70d11df582ffadaafc77379708927e322a8e0f2a3f333e2aec601bf860d2796d448b3e8adcf36657b63f21f07b44f826fbc411876dfdd207df7a00000000000000004211580000000000ce4572a3463f2066307acf87cc7c313e0864aff76cde4329f52773e247ee8aa0ad6753ccd2a697cba26cd02be49a54d5bd7e563c31440dd9154665e28100ab240c99a32d1daad75e86f5f061fe86c07f11860395e4eeaa1a1a0262374ee8ed850000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086b140714d5913e8d4b2940659d5bf7c85ec5f3a87494eb2f98f17158083aa4957778781c1dc155937a7d1f76e4c6981cb35fe98a1f0449709451f300d55cac70dd6bf0960d1e85c63ac7e8ad0bda259881f8e4676bfcd76dd69b680fffdf847d41e0cf13ccbd22b3298b55d7cc95a36edf150b265c5ec4cab3e071ce45067900308f835bd433456cdb54096287584f460d5e11f59cca5de84b2a05cf6d539e3af02279b3a78bea45915681b841bf252dc450cc021fb4209889f6852347729756e4b0b04bb50d5ccf9eff57a46feb90bbf50ac3c01eb3d0b67c5167ef0cabecd8069e62c1d411b733217f9b39ff3b30032abc1317b8f3134ba2e9dd1c8fa085d31149903128d06676cf8d81a16c61b19c60d90ffdadd1d6058ba962b5f571022ae745b6602167a92716b8b069c4bf0cce880bb235f9671974e8897c8e45d40c7c843489c420226489156d3b9fbee3f6ca0cfaca9be910b55d80d85cbb47df2a3412254869428032ca60ad80d220689b09e95c5892e95111df456fe07a158dab8816dded70402d70205195b1b7b3aee7f421496f4c7629a77cd4bd361df4d88c493494aa94172802827730436a85f4e9b9328e276ab59fe6b4eb2d97ce9b3cf7b955c454d93710198c46781abef1c53a001d6b54b9d6aa3cfb98f04c979976cc8976635dd06d1f9fea8c75aec10206222c030b00f89d06f67c25e5ad608e3c639c13816cb811299d41ee5372e20a2bbc64604e7edbde73047e58f3f56ebd05d9207b84c9b331c0eea4d591c319009eb6d9721d6c791951838d8306e4eef08e0c98fd54dec948a2b92c76ba81f75cbb4bf42dc34a3f584c6de8a238068cc19c56fc00f58c05213b6db16e4c5ed4bb7247967d58d4c3b1217319d1e21e8befbafb5f573beeefc8ba5f5db51d5d6258f6a2f8504d89900705cd5d76d5e74753d997ab238f7fa946d61f5528c40ff7ce8ec63a7c11958e27487ae5759338336128e49b0a0064bfc9baff7af522fd3686b7ec5f56a4797097790c79956912f200529374595b613a61b4db125b4be384c6dce7595ef60169d3ecb3d006d023959fa962b8d5d1dd246e46b91a217fdfe2c3868afcffec36778a88b44b87855a03180f8cf895aca58a2bd0ce54038ef57f2630f35ccd6a7f58669478bac8f5683dce808744b250e9344efdc33cdf1167fb86f5ec90f1aac87043e5460684b6ff368226d7520cc91c7a5ded30fc940a07d954f25ddf672761846076c1cbff800daf9f9db9a23885f8cf4bbf3de22a9a728f66d8f4bf2322095718243f6e3dc08c4f1f12c0a640168489c9b26e97052eb7bf4513881239975ea4a256fd5fc412de3d7e27ae9bfc05573ae9e15fb5285e1cd73454b97b328553a7faf9d0d3dadcecd8b9ccaa705a10180a9258ab14e3960d0008d89cff085dedbe4577490132e1e6d086dfff9424a051dd99552f00424801886b89220275acd959b70876dc06f3dbfcdcb30774ed35b8142676784878bc8981788edf29cf4451a72a35306401949e1fb4f48eefe9d7c741ed774747dc5775c0730f882d58aa1a733d27051278a00c0979c917d217cdc39e12321a217018d5ef45350c6aacb657a5d3b4ecd290b1e81d52aeb522a0efe8ccef5cf72eae00ed9136e4f77a0172a75a0d5dc523edcc3370533dcef5dd81559c68d7d14b694c6592b7e11e446a923db54bf23ccef02a2f83c991b4d27f8d9fe3f93ef7ae98a3943733f7038cf5b9b72b972ff9cd6e28e06b60630feea681597f9fffa8bcd995a83aa5ead5dd78d8da5dba35c6c0852dba7e9ffb2e7db6c1a92ffcb1b1d507830f15f432ce6a8e6647599385a5860f27d6f2d86aa91df4ef4beadd61345dbb86d590a3a551f6f8b1c12928adc805c95b71d5aea88cdee6845a95bcec5dc7d38463be2b5934e3a8e4036b3765bb896d29d2679cf3a90dee3aa3232b3787c09c0e7bbf393160f9d8dab46ea1c2bea7348c7d77aa6e914e0ffc9f06548b859e3b19814e9a17d18047df3e75357b4ca0f2675222e023d43ec0eed39205b293b4bbabd597109bcb92ebdd22bf10fb5af26bce03ee0bbb6dd33ccc4076c07c50e0d7fd0af9e77ce9411456b437a16cdc68d2eb5ade472e134757a2cfedcd2b3a5cc2c8a8677ffa74de8a3c2ec7253b503d68cfb8eda9059af305cc86975fd1f43d19d0496b34ea88f65dbb812c7e28ceb5794e7d75afea070a86031069f273538d9a27095d0a5a66ce0df50e55709a9385611ea1550cf737826883323cb2aa3930ecd9a4f4a9e85bfd0dad8425696dcf205da2780c6e3bb20e444c1426075fad940fb9de256824374bc709d62a71fdcc66a141bc3ae709a18001bfd13d6d1602676a0f036329cc45ce1a9c14fc1bf01760364f107c843fcb3cd5b924002909329b11b27036c69a6c69520c1e4b144e85b790a", "", 2, -321161762, 1537743641, "81003245fc01e90cc80a125cd46ad2515807074acc5c99ba90a502a51650056c"], + ["da81d61a011e817c9ce94555d5b5dbe7114bc618ed9ad4b69c359a185ec7bac2d617b21ac600000000036a636affffffff047d893f040000000001631020ca0100000000026a65679b140300000000065165636a52ac7089d2040000000004006300637582f2160390208903000000000000000000000000dfd977b42034f9534dc9c2207bd2f804719e11933625ad83131999240b14c28369a1f7bc453fb57c4a85a5874d6ce6a077e2427ca14c1860045b2b3f28078c9516fbea9b544351200c6bfdde38a9a35fbb392971bf004c5304c1a130c8b3487e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002cd357b6ccbf83f7437552db002c61caa2044f403faa3e5658b4fdb546df16fb165bf2dfdc1bf23c859187eec8ce6d02b1604fb3a3898cc07f8e8bbe3f898f2e91307eda86430837240ea9664812f151ee6ecdeac01a4e092907791e01793d8ab582a55d07aa836c900f1ed79228060b7129238fdb0caa8c225d9b07311ea993032fd7470733ce368bde5c89d947d6285ff75fdd355466855cc0df724618c03e47020a784264207a83244ab673ed68bbfd98d9bff6bcb9f0e06bbb0cdfda716d46f90b0784b3debd91ea9c414524c10dbb9f8745316564c8da41cd1b05ebc3374825aa8fd99cadfaa9c0e6006722f084448ddfb05c463c513f39805fb0b03ca0433f1e02041a8bb40b69e354fdc93671ad3e75bceec1008d3c2cfff0339e93dea8986bcf0223dc20004b5536a766c4a09a3ef49feca219349fef9e51540ef322847d62c217022fee4b21e15bfcccc48989e41ca96add0e12b8fce0072133b3554a3bf799aedf022b11dc6f702f9fc17557abbf6b8c21a0b21965296bf0db0a6e96124899f2bff1022256e920d5f8e28c0a3ce13d71c31827a963fe1c90ed3c8d523a549ef6d5cc30a304410c1f746b6f199638243f5cf9bb6e915a93539fc6f81d75044aec299c8adbd8d55102947265c4bc6bfa902a5194a556c0e24dd4844dbc6fc97e3166406793b279f2669ba5eb1add7b256af6fbb753a5d8b82b3b1b7864b8380ffd5f289f3339d1c34b99bf648f2b0e86497c5410ef3706133ace729c3f81e1a040c4f8c907bb2faaa4023de73c4eda78d2f3e3b67a0e8213d7b41256639724081d96556f96d8086a4e88c8dd863e4801320f9ad37106713f1c1d89c7fac0cc9f48031dac85f49677af6c77531417f4a469ebd33cf88df9ee72c5c16e54693b712a4bb94b107ab0475b3bbb130d40acffb082566f4308d4985f5db3d6aff04d0c4640e5e5ccaf1e607ad3ebe15cd388da122d59879f8edbba5ef116fa71e182a23b631ca8f2ea86c82bc9692093cd13e35b7ff676bb1a0e635fe33a387b4f376185816a38eee53c92941e2565cbcb90133e241dd1fd58ead526937791f2a0a6653f9075e0322a61b461cb82d4baeb3782b4afdefe5b7a172ce33c4a45f532f12fd1d35b42f6a48ebc9e180e72e9b3a16150d283fb6c8bea446ead8cf66a368fe13073408a63404bb563b8ae82f17a5e07d1f5db366d863608684c57d210a6e4206dec9705d0b2c6f47c00c990d6ea6fdaa77ea74104284451e5f15c42eed3a2cee52e72cdb2b93a0d35654598f0467c7d5f02db3c6580d7fc4591d52cb0a6c1ca5724b7d039dce9f0141882ade09debe2064b802e7fa2756fe741ec4354c20f0d4940d6931d083fe0c72b568fd5db627847b85f7fb903591a92422206cd47d965069c6b0a462c3c59e1ea8883906420d54062441630837ee73efb8d3c69e8a802e83fd5a7952091ea1c0c26bbf61f9797b2f892ce38ee12d39a7267a9a038db1d051943c4dc2df076b50d97e26b2b6cf1675f2602b9d49b13b69bb39100f7ac1a1f2bc7187a7f9215a4e99a92d00a74c9e916ec54352f26f224103ded8a2329630fa7096a215b2a9525e805069478def271d6436ecefcfa929e195d51fbe909a44e5b5d89fba5748c600dbe4a884488aa4c39ecbb2dc94bb25b54855a587123ee9764ae5aba43e97b321ce71d77f4d5b94150d592a5d360bc4a3f7ab55c1e2339db1fffe9505ce5e26b3f37d261f878f55f48242af1ad9ec269f58ef7ad0ce67ea4292e606ee58cce94120fbe09a94622deefc736708f566042127bb2d4292297cbfd9d6d7da303585fcef8762b7d1544ada0234ee604d93a480af602bd5756439622b9b229b177bbecd0cc9d62b1325ea88443178c8ad2a3eb441d7d0de2f849a5bed5d23d339d1dc76fef93f8230f0c4a5ca045bbc70b72081e6665fa895956188e7fef4aadfe20f998c77459fcae21cbae75f288771f5ea4bdfaddbd92e687857dc6f5e740b30954c7c012e838c6f259a6b641d839604461808fdc1bc51ce4111e14b4c4cea9643ad91edc33acd5c8ca7731b91e74f415ab9f60152ed888ab97f0ae1051bd8a49e0019bc575e715a2e08375269838355e55edcef4b487eb283d3f09ed681a34b08c3fb56ef48035f1edfff803bf80a0bc529cc99530d432fe73547b703493ce09ac1d771d8429240144590140d5a23d0998eb7b2105ee057ba89a57e912e72ca8b07e71abc101b437dec0d08c877a8b89b9cae17c121c7ced0b2a2c3678bd17e986ecd467a39be3bb9611c7d8b740fb5c9002000000000000000000000000bb11c596b6b5b5d51c3bb90533c40061e03d4f5be258ca791b70af4d31845bf8669bfa8ad53f5c2a3505bdaf9bd94c77d8f60f571ab47ac1169c4b37ca424b4a43df26282e23cbb640ee2310201a117d42033b9aae14c78fd27e0890a3f5274f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d76a428ea25ab0f0702aa45bf9ea8c08129dbabfb8ba603120add79d06ee509888b393e557292ce292d0de6f91cfec4d20ce5613fdef9222ca04941153f38c764f764ad99683b3447c5e791190b862f74f866e5556044a56780cecf4396e5700b4506fe1d347b7074f31efb7c75ab5f83a169a28869437deacbdd361eec9524e030e06920e9d70dc7f10865207dbcd2efb6b0685354ca6ceb4d6523cfef4e01ffd0214bc34b724e9f1d284ab43547274bfe4c616c9012a54aede015ffdd27cf9fb5a0a05b6274425a769f2d443c3cdef91886b064ffb9836c48032fee5db27e5dffd30303da911cbd56ac92f28306628b397fb489c0abbcb155a1f4b0b4eebc0d552fe0317d32741aac7baf9c02121ff6d1d9fd3b5de3f0b41fa80786b4f0444d527479002104377484eb6954a276defab5d7a8cba6f2a4c8d038fc47b8568c70fa79e134b02265809d02328e5391e0bec266e374e5ded2efd4f2997f73c476fa647f39168fc030f4b4bf4883d21be016959d5320b7ab6edd9c9d56d33ac0aaa5896e1070b2b3d0206a672fa7c4c4ed1fa28599abb74f64a9465fc2a22e3c1e056ce0642e73b052ac233a315f8c7e676b8c1c9e7b6f0bcb5aaab1ae3c7be73494995d2bb9e314d03408c317e7d391e6d6f8ab08c4c0c16caa090e57055fa92ddb2dabc47863708320493d1f637ad512a97332011eb52f4913ac6ef4b327eb21bdf5cf8392c716cbb4bc62b63aa9a097d59a4636b0e3d0270343c0753208d009441e902e2b03818bb44d53d8a50b1802f5a48cd35bbc97495b7f1d295a755abc1677f8c59e3c5021cdc9f331896b54d4e5f1855e23f0dbac640a9d3a723e50128e6a53fc2f218535e7d8543c6770feb0d09380509aa0600990d5b0763f1a69942fe6fd9f0dc69bad6d8fb6793b690c52f0acb66eb907725e41a460aec829622380f2cce7e524e9d37c1d4a75f490328304073a65d472d933f6b063289ae4d8b41d64bb5747330464c339f94ef9b72981bdb0c15e5cbcd7fd526f7704aef7eac5df6b9d9d6f7828ebacd51d28546a840d57ade69ebbc4d1f6f8b5a8f342c2a59bdfb4b8d91b71f10ef93e69c3947e7d09a1a4cc0645b6456a82b33bb868cbc58eb82b1a8ece7a6e771725aae38ca60ac781d25dc8e1b75ac1c6916819b9db54d0daef5e382d68b6033e5e3770ba4463f15ed2425d3531d655d0f5f8753b1763bbcf273f7faa9238b4afb9c4d09f639f753afa9b0405cf163cdb65eb573025be1cc015e188151510cf4a0baca2a5a55d972ab0a549f256a1c3935feeea1bf49a876b9ebac26d12f24f23e1dab70ebf772f992738990a03e9916835b37a4919f1f252a5f2577da1bb3865da7ab0c6731ba1f5b74201baa07b45eadec47097b9716caf657e987382499e58f21d87abd80a294fab958ab817c7ca5dbac3017b586be111ebb023673d6fed02ee748c90deea74bf4f245fbf815645b2918cdccdbabfa8fc317e11d1f9316cf2a89105ec2838ee8bbc92ec265eaa8123f7a6d934fb3bf01175c46a420780e0ededf5e09b9c196a7cfef69e978c37aac49e6fff4ba1b501c9d5a9033089ce50f46dfbaae3ebe0d933cedd0c6749a8e5c1e5c2391e14b6793d1e459a28e94e7eb9b76ced4581a45118c9f4faef7b53019a6d94b394156232f29b2adf357d839d46b2d1892886f9e387ca462664901c54dd7e7e75aab868a0e87f5cfe7ae77b3f6dcbeb995874f4e1a35ff532269c330e863543223a903363bc8b5fa4722dd382480a4071d3baebf795a229686eb127af42c83a29c1fabd646747b74b5fa73f5dd0d5a4a7f14c1da1a1c824c31530dce2e5cde51bf9c366c735378399638368b757f456e4dd29203a5b22ee439fd823b40e98a1a9f2c70ffd1fee9e49423e47087248ea01f9d20c28d4d85242eca8d0fa2b4037a53d7525c4ce2e37e6c3861a6703df24dd0c7d1726b2f14aeddfa70584637b2ed2d336112dc69e5d02a8881e461870294e554f57e631ff81d7a8513a7f0c9d2a7e5421c010966a9e93a2309168e4ba9381c544e54d4bc12887fbc25cd7bfbed56a39803f16cbe509b7783187d97cb9d7d76ce707645f55fb37e7d1a6ad1a0a36b425a54d585af4a3277d72cf490a8cc09b50188521973874a0d04359aac3fd6b7b310d97071e219adedea9d8a10630c9b49a56a162634d8511d595a24850ecd35676a4a4a1b19e6d93e12d51b43ea68fe680042ba8fc700902c2b010886668084aefedbd166f989d0f6af5300a48822f341e168b81b7d62547f86344601fb5600000000000000002aa75e0400000000f103c02e977c7b50bdde067669182ead8d846c4a4dcc763dca61d1af6c84a961cfb65703811ee09ab117ef56bbde6c684155d61b10eea512932fff6a8d611c8f200739ca99b689894a4976b2d7d0ab1bd57ffb9f01a4b93f74de577f4d007735000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d441fda6b113d56747888628c897086b0349aed041ef167410cc700638312f07b8c30ae03778e62e1800a44c58c6691b7f0b69220b267a42515dcbd8fb69329fddf9ee09b7fa706e6f74090b72076930064533603c1b37519a6c074e5985eb4abf3c0fd1bc7efaff57688b516da09000aae7bf120e748ded0b5144c83e7a7d4030db9ab565ba86f5a5163d6ca1a89e9a2ed03400cb902d0f4ccbae08b7cddc49c021c01c37273281de0955f51d4a7204f303c865d3741ec1a1e525c2dc171992ab10a0444fd4914593e663db0e0425a9eb00837d0a6c44cc106ff6ef969d7bc3bb506358858554e51ca1f417da1d6e90d499512796f0ad37756f2abbe331bfed6840b020c026b6654a7875ccc6b2e074e12ffb441d1fbac869b2d4a1c1edbb342620869021c43dade93e963410922bb36787d6082bb960408913d50b3f43c172ee886c0a4022c5805ef2930b2ade2451288eb0d16dd1ab752255cd0bf091190cd90e499f779022a491acda1576b8215913fa84bc331350fb1eb1ab038e918ffdaeb350671ba9602132dbf96308ba71bdf105b4a6462c97056336ddb9c8a7631bbb39d3d827e762b00f81ed7e5a7804b03ca79d5c9f48c06b61b251744986bcb191938974c0f6c6fbf49d45d1735ed7915aeeddf89d55f5e4b63e05068c4c8c8d766feb332c3c916f957f3345da21be888e3be61d431458d3160d6bbcdf4445d1de78d1167f53dee5590f2a03377428f06eac2b0ad1d0bdbba9a6173664bf8fbe1c36f957d7d99cf0e3dc55152910b6f4977c6c296aad545780e6eb02f99c34ce493a13f493657ae9d583bac8766f9e94fc3e49ea1ecf06cdee66c07ca598ce062c239b49cc283e76472ec7377fd5f8e9f57b9ad1e3dd4b6954eabf0be066ddd9117442122827144d9c2158297075c7dafe8c6ecf2c1d6fb72229080b4654f8b46123b7692322c7bf921578bb59d31d04b120d70cc22c436674c0995c05d27668d425bf445c61f205aee96d3efbcdabc286c39d955687e14157fa4cc6dd37b1672a9fa6183d34c65e3ebe56af7c3d28550775003f431f59f29f71e46013491900963f9ccc5be5c05179e6abdbeb5319a528a269cd78365cca8bfb35e011d1eee99838ae9163fd468e02aa70a998749609e4fc1f79500f32c03e5afda0072ac5e30b8b2e9e19eafa8bd645a94db85434ca060ee9772487c90e57dc53223cd62fd8da6b3e2d0d42e76ae14cdd21f3e5d0fe3f0c163847708ba6481d32b6db806e7e41fd613c53558d649a22f0c83c2e96cbb4e92ba55dda3b43c7e34dc174f598585d1aa8749349524dadf35bc3ed932b01c49726ae9c33199cdd8e046c32b23fc023a3bbe6b0dfc228743d0e603d98310927121304b75234c75ab094c19650be324f8ca7b638d628e14319f1e215fea084d6d2580fa5d0670e81120a488728b606bb358a34af869dd95d6d661343dff2ba17ae53eccca9068e99d6120791c16ce935c775756bdda7189f5351f9c14ca8242855f1607fd9e0bbb7dc70d029117d686e520613a831ca9a53350ecaa57c265815aeb8434d40634557a3d2ea813071ee4a3f5d8f6e46e40582714fb0c4af97119461a1f5aa11b901a6385acb081bd9e5bc4e1db8f7d7c9f8788b84c60f8c631deeaf5a0dde6487e92011fad2015a83703bb2f932c828729490e84377bf6ae359250f40277a2f01d391d32f5dd28510b356c0b8996807fe8ef4a882242c08108e1687e90b9a5e8c7f32e9177a0f35b205b85037e21a225c1fd8aa4c5748fd0eeda14359ab8db801d8f5ff7fa470a86d7fea8cdcdbf73fe8f342a7045e116e597a07bd53069b2f5027c9c87f40bef0995d02334f5df9b57971330889951e9c5408b444524ab03d70877d6b4a8a767c614a1fddf0ac7f9887252f010a561358afeb7534239a403a72c1bc2b4c1dc7562a9e6a7da7b6fb178b95630cd9b8fc12bc0fe6c0d0258462c676c6c5b177e3fe27f1fa53f4c992795a160d881341db40e592e9fc28cf3b8ddd91e4e907cfdc290162b2946d136cab39497f480c09f7a8cbdfd6fa121df61207f80a1577ca26e101c340ab58b0049361776bad81e196ce1799f1314c15acf34bdac1c4ec1207e630b039f93320c894ff42862a7ebe48f6e57e3b7bb0b44463baf9a2678104a0bcb9c6b3dc4840f926e150713aaaffab27805c29e1c512d187c6258fcc99004d4b37fcfd24388667cd31b1babbcde262a8dac4c418025695552fcc0d0d3c21a57d1225fbbd1a28e96e6d4dfa25889a7d8cd795b60288cfbec34318caca6a9cba692b90ed9f1baffcd24a4da0740361a0cd264b1d791dfaa9d491819ec6bbfb2f606969706458f44792d1bebfd189b0c264927a8a4f422429fbf623731e64ae262da285b735fd510a9644fea7f8430e3a1a6d5c4e16ed19b0ca96f3305", "516563", 0, -1807184100, 1537743641, "105384dace5745d6cb7b8264f2e86f85e6a1a1c3b3705b64c64fc0f380f2f068"], + ["030000807082c4030184af441a599f9b3b5780fda2925d6fe98ae155dec4de215254bcb8db462d81ff01000000056a65516552ffffffff01d41fd9050000000001530000000000000000039a09d6000000000000000000000000003bf3c4d0e1385897373b3c35238ebeb9925b9539acfabbebdbd9fbf8468a60c2389f18778fe27f5354ec8cdef883302dab795858f6822993bf12121dd694fccd8b9a615d99fb5e341902ee32180ba9d45c45bf65bcc64fd4af883fe0d75451a800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000af0d4f3df62d12b674c7516daad880f56dfd52d3fb472f66e6edd6d8dd79a0e6b8e6b1ec1452a9999ae6bc2733611d9c63ef2f689302a9933f741ca9767dcc5db613b1c0a1d1fa95e34a939b7e015ba050112f7f5e8998ede396593fd49306985f14e481ddf320141e8bb9ab9bb5445ad4d5e9860a4927b3aff0d4589becd8b40312636cf8b96c0782e155827980fd53b85a491e84ea27d072c64ddf16bd09c702032e09c0e63561b511236041006e19e2cbf99b874503ae0fad619cccd9b5eecac80a042be4bd2f76ab354a9059ae117eae35ce5d223bd2e1ce722441b2bc01b5bb9dc3ee164632203a930b54cc233fa10c2fa6803ef83371dd5fcd2dee7330cdfdea021b7ba3556e2c5b2c3b73efdd96a2778adc86463afe368eca9002e8a6f1d9c45403189876c09280233a3f1dc44a2f2f22922067868d62a8ed4bc46e8f966eb2adb5020c29c20fc3ff147af795a1d87dc5c9a3692fd2662abf3a09cca9dafd274e2e6c021508f725f46bbc414301450e1b364fb354a4d0529fe0da0fba51df35307b0dfc03132c3238238ca60ca43d3a036b4a4e09fda0a2a984c917668619b7d94b3a414a4812a970bb2298c06ac462b653c3611e337cc49ef66917b30743cbb1e9fcb832055e5659f27297fee8b72daf5504bc190340bb21a877a895d42ec99aab9f4855293d1552f08132ae11fa705a202b683c77f293c7c9867829896786c904e41bf4ac87b6745e7cb4299b5a0114c2dee217964e28d2df7fd08a84fcf93f80417e6c99099e5ceaab9fddba47688af049fb023712044cee0fdb69d1fd7fbd857a3def60bbb706307cd1dd961dbff91b7811230f224f6753adba07c567e40e43a76d5e56987437d4e23e9b0e7b368ab506785a7834e2abe7432bb6eaa45682edb107a3d1927b815b3ab17151e25f2d0bcfcb59a087f4500238200c95369d48b5032f4d0786c5d968fdde88d0f64991c87907ea78411ec43047b4d8f4e6d5419e7b89cb4282cadc2feb6f03cb896f7f9883603bae8f2b7dc7614a33edda99ff3f8123e9dc47a0d12ae5da70e2212aecef3b454f03eb0fcbe6774d450f2278348753aa5728f331ccf58a67c8935efe29fc9874cbd5ef47a2e0245290a84ef34773e2320ce5151d5ae899b3344eb061970929064ff8a239e85796f4bebce280ba224ec736e990792e83456faf63862c9fd67b27d1bbf9373a222d7b06b83bb1250bd527c4c7c0e6e6a26b3ae01bd6e243e01bb065588cf22855ba9ad66964c81cc4383ef18ea8ed91d9865c133e86653a386dd420d416f50c7aaa3d5379e576e87fa0f844e884a5aece92141eab52d6f9a040ef4bb97cb5c807327d9de9e384cddb77d4bfa2a67657def13277d92bb98d9f42faa71c538e0766d68b7eb872dd23eae5456226434f2ddbcd491ac1aa40b8c7102ff925f022aad6f5889997f12b6410675c6f052866c2a5dc1d8c822ce8961dd973d73ecc357d6d8f3ba8d9f3da32a0fcbd3a1925e210499e522b7d4c5e9b2ca5a07101be4d95f34cf0f2cd22fdd46fa183c4a4e0d046d26f4380c3bc269a61c34aa2fae9c884cfc45c429b3285243b273ebb0a87d51e0e1f7e978dea0f058e721cfd86e0b2365e512418195b5cfad1c013115c8d171f2e493144af8bd3f5c9df5eb54008f31e862df28c393a3fd0e432a8dcc784319915f189c5f9a0460e5c4decf9dad1456198c9f899135d954732c7d8defbce9fd64bcb5467c54d17ecf59498b448a552612af6484dfbd63e8392558124a2ba1883a1551053eaa4591f66b495d32ab2e829a9f0379b95f8fd14c6019e15e592fd8227480532f60e2cc9797e5d2337bdbda51ac5447a323db9bcabd852270f3cac7d998ccd140bc3aeb8a5c00329f295fe0da949169557753c037e18937b48a153ca666011c9938aa3740e2c4b1ac56cbe2f5f2135e4e45442ad6f5611abd3965b331a1ad803052abc50db6cbd057d96a922ba5c297fbf4628a515422b4fa1eb3f5a95fe777b2ad3acff8d3c953fee0b5595cf60cfbb39d9abe302ab740b439f8705a33520c81a87a65405925b953a28692bcbb5a97205d4baacd630d92aa1d26b9feff8206911dc8eb4b2972e3135b4b4e7fa951d09d34fa342d08b091c3afe00bc027d6e580b35ba2847003cd2c42a0dce7f4e20750f48fb0726e7067c774d4bf5a1b6eec31cb20ca3fe467714f97291d23c86c200dc4fd3b854d56590d971a419d0c714f63664c0cfd9a10e3e0d5b98dae041a0d92ff30e5d65b757b9b9575a4e2762211bf4bc022d5faf72a97d3e000000000000000054f9b90400000000ddcbc15995229383d7f49bfde87c5df6b9b4f7cbbb992bfb45ed743904bed6ef590f5acbb9de58524c8d5383e7a0263f1c507cbd7f16673535de847244f0bf51996d76a415485ab6f0ef9cc629ef702d84765e4591294b897511691486ba7f8f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009967be3eb9fb1e5ee87ab68c4c3edad3b4efb0bc8ce9d076922c9e1f8465a06807949e225ab5f9ff8b00c87d3ccb1cfc4893ebc1211d83e092bd7a0b6a7c64809a06f21286fc767feb33a315a768f3f59ae19917fcf8d70b254a83270d93794f54089b46034ed455e5f0a2ded5ffd72b4bd83f4b0c14aa986742b629aa2fff4c03099052c1ff3377dcf7ca6a7c7e9629046972fbef324a44ed0d4eb51d7c643ef303219cdc6416fa84e0e64a048995b09297e552cd2a62687b0d34e7fc94034e61740a041575059a08af353beee33536e5262db09fb6f5c43d71baed429b1d3dba6dba2ad8e4e07cdbfee66e0d69c894e09a49448ffee61b9033defb96d84583f81cb303017c20527ea7569f6579b7713f7edec1cccf018b24be040442cbdfd7b0b86fa302027cab642daec99e2da5bc2734ebfe0ed2de9f0895bd7a20ce0b8b2a7f22dca40326e5ad51fb628f5c32ed61632f5c8b092d9875f1c1357637cbdbc5adb644d740022142dfa22ff5f9173a4e526993b536ad6c4fa53ebf5295e264860f68cca960a902276458a7b8958ec37193bb172ba2e6a969e9cdfb7f0cc66cacf509581c2f5f10fb576cd01a7ca53e319c2897e186373f976fcd8dd1f872f6598808c2d0b29bbedb498cb64e6c66ff90c1eb6bdf27eeb3ec935ff14b71f73a47df939bd2362f63d2bb052c2108958906cd42bf4ce4429683bcced9d771eed641dd25d0ac351a5b19beb4d9970ae871e284c78ffac80f8ff72e1d25f8c66a80c4ad3bdec70332f75831af6db405311754a2be7f9fe84759bbcaaf153dd326a92ebe15bc94adc6b5ab2ae301f4b4aa7054e649a30660bd24e2d108b0b2ce7de562d04523abd22634f58010592e2649c3fad2fddf2d2018ccb5124d7e4adabb8d65822ebbc58f820cc8fa4d72f8341a290d83e777a287c812547bb65ba83084c735e2758e5735b559420ad25c23d33e3cccb766577e2d2298c18e591700bb8b966a3e96a325d76ec207e3e1e07972b7b49a1b16d2a8cdd2c44b47f5694bcba9d72070f2c66bcfcb5abdc718dfeb2cdcbb008454c02c8e9524cffb98e2b9178891a2a20deb8a77df5891616403298a15c04055dfa0518b226ce69b6a72dc923c6ff99e4180a8ebb0854fb0bd4da0bf010726c16bd9ef6de861b6e073bae70c832ec2b1134533865c5546bf242a1f7e0a2fd31dc1934e386d6fc419949d8f84929c7e7e1ff511df82120f128dce7642df9b17e8a8a8e382e9205f32cf134919974fd6622c6dbaf233b96a06b0dd3b647881a30f7c3a2c7eb26b217ed7bce7aaf6219f989ea71794939c1d8290601ca5b1f8a08700c0fea55845ae88d7bc4ee45f83167be4cfa294aa6981f4ce54912858478c7bf78fefe075867b4c1a811b4ab545de68f78caf8f514d031466ce2097a1ab2ec0817628051e942aeee4672b0e4f690b93d1d64f66f22a75017410eefe403fb4433b1e987d08e7733555e2e76f2320fd1186cf8b99e2c47274ed5280e170c426b08665896acc62d649008580f23ba7e6daef5ffc1847109ba3e125597645a08c08b9724e543fef8c906d4969b3dcfda4529cf5d232147a03e09e37ea50ce4f8618bdfd63447aff8ddd8748da6f8e1c30ed5b8967412996ea9c6e183fe5efaa3d58d13fbd03d29b35fe794d387f70d5a83e79b4f5af11da8191d840e4395550d387e44a5a0b90e487f50352be15f8a3c03804ece0719cd61af0545f1af077924eb6ad29ea6178a95819c3c550c67ebf37919830b8258f639334327bb27e263d71e659ea3714d61f1a3143e2e089b5717bbaba7e4e046cec21f4734b81b40dc7397aa6c8b2176e74eb2ee2d82476a1dd6bd8704bd7b98d4f4f8e25e9e7fd3c60ca5625161973fb88655271da9b6c88aa924a02c2cc15ee36f8944db61ab991178d0df69917457331749674dcfa7f45ead66eb4b32ebae6ae2b5b1fbb6f55c6fbd0d8dbc73207e2a9c5dbcd1ba6896053e2d524b7ab1d93cd89de05d9e9139d1aa5245292ecc32cd8141f887451412a8863a9e7de5ff153a3e024d6cc30f6487eb35f1c3fed5702ac3f11a09fd448e1bf630821ed7d37b2ec781e01ca4237ca9389aaf4e5408de3fe0654bafbd5061e543c7b96d0152eff5b379dd3c4ca8517ca5a15896624924da0772a4cdd7a6d5198b4bac210e8e1cb088b462a4688fda55aa42e325369a65a1fbc5700dc405bef6938550041f5ca99ef37cd3a2dade73b9a81546fb4644831becf4c1c2e073dd8bf85cb2d2443cd385b1fcdfde5bf4e71181b85c016589d1abd31ee0000000000000000d65d3404000000004521fbbb975dcfbd346facf305207fb14a8ff72afc9b36294d12eeb655686964ba3513e169008383219c2c9c64551d5b9becdb9aadbbffd42e0fa3a43bb20fc0c317e092bfa9163f89dff50a1434b4b5f141520321e1cc2f8284cc5a93d451bb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000165656ecaa8c40476584e4bcfe4abf71764201e7b94ba06a3f69befcfdf12df3ea88b8df5b51f0fef2f2e7b66ad580a5f45945fd82490124b8e5fd3450ce3e124868eb1e661fefbb3b9f33ec48ab960e0d23a2b35494e0e05c4026d6def664949f47055e762b13539be598aac8e9fe9593c7421b35cd0e48d8631cf1787a2e2103094dd544a3e6df940987891cffdefec1c6d70fcf38e8235c5e1e95ad65f09242022805c100890963ab30737dc28b92881bf9dc1047b36967beac82d0027464c2dd0b03ee18a6c459ecd2d46995cda369d2bad08813e68cb39eb3a62da815cb4daa1c98db8f2f4add66e27134371687b0942dd7128f5ccaf3da142c2cce00bc96b141021278d3aec3a31ce4a5947083782d0d7dcc763350e3485eedeca1bd44bbb689c802099a5a5c7aed1d1b58211f0ef0f27c1d507794994d1243208326bb42c4b29a3a0306f2a58568187df93b3c41f20f2881ff1edcfb6a572f4476af9f349f7e028a98032e174b7b45df3a17fac5406bc9fc2bafd5808f1493ddee736cdba6c28b7502a803097c8db877f91d5de43c1b6adad259d8b64ae80662bd7f9cad76e9a30fa4cbbc328124fe2254467f0f20b98cf028b6fe7c34df620134a73f3a2772c7976227a4d7980c2ac676bfef08a19bcaf20e8327199d17b45c678053bf351ce1e9a45b63b1c67297d6c7498b0383dd2035cd5aaa2a2f967bf0f0ac20de13cd972fe23992920d0af46fa48831bf23b9da65ad2498fec77acbc6e82fca430b3121028c54182d99bfab3370793e0bc9c3f4bc7e088a345a6b2297de41a8303dda3652c71c41d544d000b733e0b4f8f2f3bfb08db879f3402b21ba8386674fc43f81be267b8e89df1c20b3be4ade3022246c0d2ab37fb89ff0a91562ac28c1ed41b742713b63d2152505ec26d001495b58d2f06efb25272a189b362fa4cc006b9390adf5a01cf87f8f5f9c44b99ec781cb4b72687f37e680d624f405bd43df22979e04b574ee8abc70360d954bbbefb6cc22871b0b2fa48c0e75faa7e62cf40e78e46614a8ac9c8d973b7ac5f68510e5e2831b98ca9353582c9de42f21e314129929576781223c2d0d246394a28e30996e8c8ce426aca980303fb556f7e04129919d288df481ccf3020750c21589507ba5a61a4228c9baaf5e444b10739bc2fe3da27def6ed7490c67dcc329cfabf744d0988e828299301789c63a4c593bf089afce99ebd6628fd1de98aaf1d248fb80e93de58b67a18d8233e1c0c0a2d511e33bc049edb90c76cd11a2465b23eb0f4040cd3a412cab1f8a45ab5a873554b6ac09cf0d3be2c61aed267f2781033a9bfc71768dff3c8f9ef3664e581b81ca946a8baf7b9e6dba41cb214f2d2f65240f967797a5f114dd164fc5088142711d1f7a286d953434c3114ceb6ccca321c032a30b0ffa00321437870de85e73dd5d23efc5a9bfc305ade360474aee4f851cd609c721232e8bbd0508677e520713d2cea5889a15fd1bf0104c50a03813b13f1d0758f0f50c752c2405deaca1039c6855736617923c6581820f344854ce6527cecbf00e8c9f6533a142597f0c07e151b8396ae2ea23878275c4d0e84b134a7c6d21ad48c1a7890e4d71c3d34bb764aff17fee3292115021309e74d20bd2254de77b3adac40d5fa0cc5b2c57b7f6ce8ac31d10de351060a3ab211a3fc5c93f0f1f6a7f5774c8776d2c085a6abd6be3f4fe6cc71ed430bf10632605870c05ade3bc9ee2438773a02da06aa84bf2975422b63f02104427f47482a7cb34bb596670d7300a13fdacb4fb5899377a7a482d3d5891aadb29e5428314319d378d57394c8a60b84a8e4754be66ccc1c3ac932dedc702e41399a88798528e6f2a9c763a9fdcc19231d348517e3bad20cbb05928629a808a71d084e309b161cefaf164598d12449b456a45ffb53c1698ef60b02398cb6ae3eedbbe3f718bd0da8bd06285276496f66968339b42ac9a4f4d2d63b012a996897f10d88b6ac5cc9b5759ffedd3d355299603ddf1b4a329cd87b61a2451bff023eed9a31911b139d0caa98f82b1bad3f21a53b914826ab6f96315b44df2eed3fba693a4f6b20c70cbf629b27236a28a82df944e5683c55ef189d69499cc11e83b4951d4bf2e4d3a28e084991caceeef6c904bbe7e59c7405b8205f70d5c634200ef5c1b7852ded270644f5a882ad97415983ddc6b9b937473d7f9702785f88c4413640e345242d5e822f909ed61c3c905a923283cb4bbf7bbe3e9c59e0c314a9e7ec2ac5414899a9da3d3aeed749c2ab7b772f43387f0ffe67ba83230b3a57d0e695a4120339a746c2d1b210bc7bc56bd65cb221cc6a16b7575409c7365c922e41baffaf26cb56e76ae802e2ef7411eafef4b278bf593d4840531fec7c7a4780cf808af86988f4a6e04f50a2bda727492a5c7f83e05d102", "6a", 0, 187932507, 0, "fc5dfb9daf75e1c73fdac9475d1076554d7f5294ef21fb86722f5e6da08bc2c8"], + ["1823580f019ab03227fa017910a43e95147e98a69ea42c938eb968893c7c6af7ea1ddee8850300000006526300ac515299ac652a0402d4eb0100000000056a655352632700c00200000000000a42f90200000000056363ac6a53977f7e01000000000000000000032cd71d040000000000000000000000003488736fc9830d49b30b0e1f75efef1095b9953039bb42c975842fb0b65b66cd992226b4404c7b3e0f99d67ac9b6fefed7d20879a18f60ac112b32a03fc122127d122953eb4ac89ed947f8bb25518be535472ffea75cbfc96b44a4feab94da25000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000be89e14820879e31933dc7df5c06eca40653b85be3bba5043bcb9d16f701a0ff015d052ec565f0e6fb025eadb74702168bbcd260d53a5896ab69556d3215354675ac765829b4f98f84a18f8d0538caf55add8dabfa0a78a63baf851544f359b18425d14ddca599e13d3108c786bb6478da3ccbf79d8b067b726ce13faf76336022cd79fb5f9ba03b5d0d4110345578098b8bc99e430c21c0c165710ab4833754d022f77f00d508319e3be68ba76f51477b6702f99d9ada11920354c22c059ebc2b70b00137f207fbb52cdb11f7d187972d39178ebe510ec8c7d39e9dbe82f342ff83b1d7d3ffde965554cbc44151063d80ed16b7e847ae1ab007c67759b3718d91264031c0c23fdab4536a2b7b8f731b0a34c1219a4b610f9e2018f4adac3e92fffde1d030b7d4d4486ec7217b1bfd0e7a929e0fe2d55d4aa92cdd26b47076166e1f170e80206710eb2ee2b25c26e3393c180080e122707b66bdd727830bca396567b1ec707021a53eb9f8e2d42fdbfea1d5edef5d474bb144a4656cabc4e255212d0fe3a7284020eb5dbb074fa809a293382dc11d6abbe42ed8b059a92a584c5d46b0d520c99a13c1a0af52467e55c3a2ac915610856928f4b60359a37996302d5a45284510794d0eb06a634fbf52ff06e6b9f2216ecdb49762eaf282d41ead09bb2982675baee74a9bb4cb9251106d619b7fd231ad37b09b8d7089069397d3410ea71759b0e7cfdc670708ea5d6e3c24b26a142bd1ffcc9a7735dbb54a1e69a3601c5ee245826b9838f26d2232a99ca830d27f427478acc6b3b065bfbf540cb2c74e60a40b14d4e923ca8261b48b49a1562e1c956c9d44c7fed570c49354125871cafc6a37d6092b34aa1560b76151bf00229cc88afd05df8b002a6b72e9dc89f2bae7d4c0de001ce81aba3d9b602242e4d94f54b538f9b632307e955d43e91afd119741c5d1145aab11068e4c37b02c9299994d457777cdce38e235ff6364124dcbb532100c705c79bfd6ed0134b30237eb69d64abed68b2280290d04b52cf65e68207e97da881e7ae918433d5c11d71fff911dc11477cba43e05c2018033a113b1a66cb15180fa462e30575efbeedf36ac912b4e678602ffd59efe7c58657e2df8c25c53b32a37db08f13796bd9924b2918b5624725add986ea4e6b36b019a8363248924a3336b611a23dc9bac7397d69cea3eb87410e85aa202df4d5b4ed94bc2e8eb1b8a4cbdb140a472689b81c72be390ceb1e22908b300bdcd02a84d246e06e7e1c9783af1794db5e3b8a9926a9959e2353a3cdb9f44ab6acadb62f7e84e8bb9a68c0891e36899687aa1fea188be41c1fe335caf27b86cd1c625e4d9c3d2d719cfc4b6b03b5eeb8d52188f2f3d1069565806f35ae36e6f2c56032104b5fd07ebf90a83726b1eed6aef2ddc8847f9f2079066766bb34f43a08c076f2099bb099fa9b6b9851291f88b1fd28a3d5314611f1746aed50e628a428dfd95e4727609f6ad73399761fc7267ef2359fd722616515a49388392c23706e8ea9a64337036b6f3b95e5b93117bf8c10d286fa9544927874bd8e9eecb6448c93ace05b4cc835f491cc44c01d71cd0e29c50cb493614a8fd7165c670724ff3a13924299e7fb9403861c92a3d1e57932d1777e83eaa3d27cacf85abe72c90fc9bda81ef252bed875b513f85d28401a4d4e75e27522022e9f9c7a73ad149fa8424c894b04f2e3d07d160c47ee67de5bdf31fa3031a13907f9c633762cf1c45cfd3b9334356828812e5cb723c90bf05d95e4e104a055be8e498327cfdace2c7582830915b32a8f68e3da1f3439e01630176dcb695445d8a8a767856dba66affd90e98d707efce67a364b75dcfef89602f75d544094d1643bb97a555ea6b413439f79e9e78e619bfe732d703d6f1008bd513d18452e1d8afae8e9b2c042e63a651ebb239376adf015b2a43c3204f5a29778eff0351e9512b29ed292d47f26182f5fc9e3c7e37c430ab325862d3cfd85143279b54278dd2189b21032e8466d7de4a251839535e7bdad474ba38767a7a5286bf915ad28ff1789f5ff17b717d23c1fdd07cfa7c4e18f047538d71cf886f06b7926615201a880697c70b5895eec190c18129cab6a4b63fa5515ba872ad24c76f81e2014ae699503c8ba0a11445ce6e52ea9d43f8f667dd853e0db43148bc50af29bd9e7ab5bc2c01386c31a4119165632db41ea6650e23fe2e6b7e6b03380536373ea584f87bf84519b65d20ad5b7b869c591552fc79886ffe52c653c2e8fbf1859ab98f46bd68a4a4586276c04c5f59b04000000000000000000000000f9d8722ea556c12cd87e758aca772ba89284aff4d6907c43192c9cbcb06c8bc9858782d26c50aa9ce99f5394a083aa906cf554c3ef6261f62d7f27e60871fb7933ce2f60620c6c0033d7c923a7c610189e7dc1ed8442665d9bc701ef59e3269600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fb8b6d399f3bb7d0c3718b0d58f20eb79b99bf72aeadbe45da358ae722bd3a1662b263c28eab42de5e900d215aa8812e9634a970d2e5441959004eb37d58a1cac4a7eddccd99081a47dd18acea702701072a5281c43df79e4b64d5427ad7baf12c5a04a07482bd8668b19ff0f5a2e03030512481850ae985cb42032b6b6aed6303095938352aec5c0872ae275f8ded7126e7c0628ab5258898e847e6ef089cf0a0021d5a7308c4972d238f29a86794d49073bbb898928c810be3f51554c3097328600b08b05df1597f5875449c5804a6516e9f7b0c313dee0ee85589d14382b43f0f0c009b1aed14d92272c74023f3f2557b18a827519804a79d70ce4bfc7a6a1fd629030ce0871978514131e901243e5e944047c2c0352cc227ee17cac062a2ca3fd90a020070767f52b939ca1b11ffc951da98db1872afa1a76fbf1727d18d77ff1cfed2021945cd550d570464e562ec8b146d4dca7191d02880457bf9c99213e78cd60ff60206cf4241a3557106fc208b16c18e6e9b8c6f61ef390a496c749d71863d4d73640307e878a8e1b0adaea380edafc71d6315a37e7996f4d1d032f971f80a23b5387b2e84678dc850f7a04e0ad4a855a92f00c00ff68cf145e9d2650a6920a95e087c317724fe09d9beaf063e10eaa069d26e55e89bf8b9430fd447cf7098c1197d2e5d831c969d274d5c12cc9e7156aa0e023bb8f2d6d64b6213dd84b8419b4b12111740c59c2a414e170b599f7346384d8e02d3422d42f8997cd67bbde0d68b8c5c61e98d3aa38117ea4e35fe3d200e93023b8ab301731cc19f90c04201ca58eed8530e194535064e8103a2f72dcb578a0ce634403f9c73597696a5353539fc926ea705ad8e6e255c79b5529bc1e1b2b39e1b8ce66cb02f58c63631ceba7e79f6d73f66154d6eb0fe8912734c3c2316bf88f7aac1ff0877a95d848ca28435d3cec88207c9bc2fd094e0964caf31886f579c761cada40144e8eb7e8d52554f6ae39d168864520b01e4c15e469cd0392039d7f7ecd0fb0c394c5de5835ed8cba07d95454194367f7f474952941743a4f8802785cc925e6ff9922a6547c250a59cef979d71af1ef96c2f02a58f7ae4613d327421fcf1df9b3e7a050252247d55751abe6bab347ccad778dbfe4e7921ca551a536d2ba2deb53272cd14ebf3d0bdd9855e0528b21417f01386f1ebe9b3c79341f68ff538dacea4801404b68ecfe3312d607f69907bd39e33d4d771da7824988742b1a95ab3df9ca7b1f6c450ca48ee129c2268030995e577011eece1f810ff3a287cbea6a5b0f1a01dfce7d9f4289519fa28ce7a7eddf60906e59b977021dc3723451c46bfffcfff450aef27db55390294090143ce6685ff6edf81bccaf0583648ba393e329484152af450e050d23a4a91a382c433714671f722a701960e1cef05dd4ddd6a8a790e3f7b96cf50be1b78c27be34c0aa6aa74c7875d816823be930f350740d9b96a649ca096232666dec838d043c676bd9592d7f0f23292be7b3f8b9cfd69a39c580ab4e0a5abab3cf84fd9dccbd9d5c0537192a985831d315b1d63c33e3236ec8bb680a8519a3961fff8099788f018ffb921be00d53cca519b1a7e666a5fd3719b0732380cf5ca14a35c1908aa34025614d700118e9d31de7aebb48af85cfde133d06532a6b949f3bb9bd1c5b443e689c0af9024ed2eb09a673e629db81bc7057f90bd50b9b5c52d2028992258344bf5e0a40bf21e686075b6407305936afe04d5d9e9b8458d64f23a93cb25eb28d0ed78b8f1028e205e74ad32c27fa522068dbef9ef770c1fc4e0145e44a0ad3390fc38174d98a4460bf5c900931fe1ff9cf648356caec780f368e3ddd66bfc8a7536bf56271ddfd7618772754ee0c4870208e836274de4c42fe22a715a110e70b3c32998f3a9f62f9befa894299235e48132c0bac9e9f2119bd4e96c787ba1a7584a9422fbbd061a4043b88f17e69d8f37d97042fc4f1d5dc0fb00d2e63a7ac1825e1511935ed6d29ec9811cb871cda15b8d28b48909ddb5e6da5e7135606e3c1ed00c8d774af209b4a7c7e5a07ad8896880814aac8e0f927eaf22826c97bf15e6934ee290739955799840f0eb9851f8aa161e411fa9505fea644f5d5871f0639510d9ee83a64d1d3ae86f69203fa870a266eecb6e3e9bfb7dfc3a090d9af736ae1f9985e3450e8cd9ac884deefa8a9bda44e2e35b41414051d07aaab9de6274ed876204fc9518b17c9c03f8e80502419ea45cb3213079d6ffa889487263185164f8b8b6f4b3a6dbad14a648375d7a0000000000000000facedd040000000088f02883343908f97d096826b38fdab9d17ab4acfa83b6a1948514454c5504b91981df9b2cff2092b3586eb15830a9d196c0f631d57e6d9763df30d4e503aed9161d42c5a5999dbea0c538830c39b83ed307425804fe78db0a63e0663e5d77a700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b12f5087be569116fc913680e5bffed83610ab355c8efaba0f7914f7e8818a133770116528aefbfb953963c69465c0f9218cb25e99775e513403cadb8f579bea0f221962b616e8e6987f29f863418ff9143fca19315428db3de8cc0383df5579f5d9230ff82c3e35ca8dc0aaabe8b4b4fbe858167baf5f70026f135faf5a32cb022fd27a737e03506fe9bb2aee6051e6f80df5343eb12fe525bcb83e417d99370f02024afd246520fb5a24da2a59d7a7f17bc257673f3b6d8bb043faf8a8673c7bb30b071486f17643c3083bbb34c255a8e059e9307933b478a541fe1d70dd749bc3820bb5ddb0fdaca8186dace105b12dab47fd12c19c8984e8ea0fdbc224b65ff6c3021ee61d6079c012d7e46d4ae056660bfd64ebc0e8632dafeaa6020a898a97b8860321182a426aa9abcf111521a24a776d28593e6c57e86a8f331aa1d4797a449db20212ae744e20f18fca5115aa6fae7dc8cfafc89eba2fed08eba2073f75d03dfbc30221fdc2128d5fd0646df3c75b58285ac82573c67ef4cc5dbfe830b5ab23004cf503250486b707bcab1a3f2fc3078d8f36bb683f1e1e4ef3fed0c04296160553c8aaf07b0066a2bcb2cd85f5a558b31694af32a3a54d97c0b20ab4d32280851b9049502662209b8352b1b252b8372bd8df11d07a023e18824d4c42f6aed51ed7fbce650cfd5d92d5235a0acd45f8b456d303f933b849f84e013fb4e7a23eeff293e7fdb0f5a69174f42304ea176d79cd5422d0a683bc06a139ad999e4e3b3e0ccc7a179e56d0268048ebd3c07e235ad189b7528928232d91763b329965872e0236a46f4ff6d5cc8b5353ef9ae4932b87d9d36bc483bb8715d1045dc9343f45534dc51adaaf00c8768bf02e72ce0102914db950b5d602084669721512d6d508a9a5dd8f6de0ae19bd3f851c6c4f4ec64c3c01c3554b0ad8e2f884879b6122ea3cdb052fd5cc3d20d0145d8d0f0b9ee6762fabf2d33f3d96d2fd68ec6eef1fff0d2df21bdae2f06fc178017cc8b50a9cf934b62dac53f97bbd3ecc60455d8e9132811abf4ca1d7f986af0817d5a7cafb5f12d0e3aac072e5b7c565189c4557c261634c245fc24425493b175d1c89968be1d5ebac7d3597d71bfe6502eec35e7c3b9dc8014932131a77fbee3d47ee3434b94adefe8bd06e1c31736361bb1483d4f1c4c323e87a4c7415112b89568f868ed3ea167b8d65584669093e5bf478cf10fe085820042cda1290c9a199b0f890d7f45cd2fffd09bff2b770cbe7e3b49491bab0c5289f0b121c923efbc2bbc5a569d19baca80a1221498894d5a847f83134efd3ba51847de68c881fd75ab948fef50e8514f5f8090b9de03e0ad9f030619c3cab4f915918fbbfde5cf73c0d51118e168eca54ed59f52c641c3628d4b1ed66a94f1d2b286ec7f491f474d21bcb70dc03472153c1b916b9d7f38b40453638f0aed466e3e23f4b8a28f05b9b6e4d4f9992f301e9a53444b471f6102b2e0d2a72ae93a5cda1f8cefcdf8b905febb19e291a65de58e4a501f1bc1ba7a74b1618d4a77fd6a86ab507c22a6acb9aa063701abc697ad6c8d855efdcdce0b4bf6631e2e7d55f18156448b348129b274f1ecb6b07b7fe3751426ee5360a4e86d0e91c2a46eecee60857c811fe050f3fd6cdee5a38cb9c7341dc6a559b05f66c62832285532d56bc8448d2045f35e8687717ffa5a6f959352d2ef46a32abd17ef6d5bebbf1fd0544987c30831a2795ef8c382869f85ea61066889e62c391a490cd58bd80061e9afaa82cbc37ff205963d9c81f42b7023b2dfba07f01af19be65c53781378472eabcbee3b0ea4e9ff1d237998d7cf17c8e26782045405aff94f71ffd492af1971efaf28f1d35917661fd57e0409ad010cee5ec4626d1c8e6dad09c80a9a1c45cc4b54a8e4962b050c4ed6739de1083ec8d6c1ab8dd7700ea5130923f7a70dc932c48f3e9a9eb86a7d90afcfb61801223d246d644604cff77cbccd3da4d9c746f581573cbc703da1b6f6b1fb4688cf9c1b73356a7322c2c3329b147dc9f0fe04c702c40d6037242c4b2c1e0f15322ed6ffa2e520f26fbc9adc9902761c55bc885838888894d04ac8203431030f6a720ef4c314c78e2824ffc5d539029462b5d8b497cb0525391fb6724f28dfbc56194d60f295b79a1411e58cb5240e2f7a615263472664ef8be0d88d68a01403f94a8e2bc3a5b5116373ea9b2c048e19dbb8162fce5c98154e1f2c3d7cd318201fa0f3d10ca61d6960a6be1ae22bd6777cd6e2cc66fdddc88ee8bf0e46e96c6ff37e0732968056fc1783fbf72a614131b710a3dfdf8a0d25272883ea8ff66ef7d1bb7adcfd7d5ae290a87e3a06d18694777a277d323613d50dc585a5e390ecc51d6725efc32c0c25ed59f3d2eef0ead4a734277355fb15b2178e5a13b812d09ec726569aabd0d", "635351515363", 0, 514726874, 1537743641, "0d7014f6d66481c7b25b8eacc5841eda7d401e9b615e619ac172f3937fde7d4b"], + ["1551c81b02e0aa4c852efc8e925e7e9365d39b265e5e1fa40fc6cd305d17bb7680e0902fca0200000004ac5200acba9abdf9709341c196adc518183de834201eb838a4e5cde07d4149cf547085d08a7732de030000000151ffffffff024e026d000000000006526563ac526a2078fb0100000000045252515162ce403f03d0c44e020000000000000000000000000ac382698587a63c36ec24d44ea4bb9835c2dfe334104064bf894bd36052771b6c5a9e27d874bc9f072ab553c335509972bad9b30faae5b0349472a32d081af4f280cd2df66ffdf3dad4c88fa0b2cc4daefd14e3cf7e85a1caa14cac73bd13ea000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6329d3f61e8021cb7e532b3232d1e633512dfa9d6f89ca08581a9d13149371bdd904663386f9385e87f864344b8c8d096841ececdce0ed169ee1dc468c4d1dc196091d63611698d2dad49f3a24d3c97d90630a79fb34748c7341cca724f5e135558f159ac5a387f3d948e53dd8828cfa029adc6290be18bff9cfc012cc6d5c0223e900f0a8ab6a4ace210dc1c62eaecaf116ff2bb72e2f0690fe8d81589aa51102188ef53b2479e2c6c697382b57ed7c01797005290d869ab9ff9d909a6cfd72140b0343128c804ca54eba5b0581aba91451e131fc439fb6e85168d128f1e7b6daea7f579c456189b967fd9869adad8bcb7edbf773714cc54ddb3ee8bca9e2b577c10219bcfed33156fdcbf529bea41b759c8ed666a6ace5fb69d21f205042dec61ca50209545076acfb6aab2c4f8070b4af85c85ccd525f2c3c40c6b5511cab0484376002261e8c4637a7f76010d58939c4f746dd7ab1ab9c4076ef4c107f28fdadd5bfe003279029ba9bc6bd2b0e238cdb486b20bb3969e4b3a257c55b806e02a3e83654cc031f1fd0e181f3fdc559dfd6015de68affeec47d5317d07dd2e409cd6e975f238320840573509c55d30fc895d1f38a3a21fedfd7690b744f6c1706ad52e42dba009701ea2f749164e91f4a7ec5e864ec6ec4edf305494e63b967b0d2fd0a6eae179c1b398b986802ea8db40983a148d7677ed24b70d4b4f3d7ad5eddf205bf458bb89a3d176fd99a7688f3d72d64f5b22b4077931831d709d560fea2b531d750018fdf5b9f102107bf9ada162b87325be8369a5f87227869d64b769d3d7d9af66455e7b97fd429b255ecae52c720e051d4ab49fa3bdc5dcebb363d4cb4cc9c46b54703ada0fdad5eef5b2f4f273bed64f18f554dc1f507d6c8938b5b649d70e76567670649507b6a46888f50a09d0b732d2e9b276d63da8ad373320754d149300d4a3895d1163dc574f784b7243429f285421858e66f4fd43a26bf379c8607d3493b60e18264c40d84d38624ace8b806745f7c4bd8c3937497f3af0a59979e717fc7ffbf461303f4d128fbcae06dbe7210e930b8da750a4733b5949c1a352f8a34e08426ffc7449767e6b86a8d6c566c389b231df208ca6e21551d6460a2d695004e02262cd0c0e9fa625fcd922fcd574fc0bd889ae758d1aff9b49bbbac21bde3afffd11b75fa1d30c3c3b3824813c58808d8b104a703aaef1a95960df9b2ff80274889adba5ff84abc5c769876f7521c64ad57dd6aec8a36a906816a4abaaced1e06972d1cfe4404521f558e6442874f42ddf08e0c8e75b4b64ef42d34210d63e5c66a4b4c3e267da8fcb084990a2cbe21084a9c9d537e3138a37c056db526bca78321e9b269e67e3f98c452800027947e4afe3a2e5c921741aa1a4143907773eb16e2abcd6dabd687970ae23741205ecb05844810cd28e9e2b630add8c218c0ff826c5f371818ccb3170d634448798582c6cab450091d7bdf7cdc818076f4d8757d6f26cf53d03f7dfd08cb7f04b99e0cfd6bea77c05d6064eb14427156976e2b4cada9ec9ddbe7f0289081c06e3deb53fd230bf93888dd65e4db1d277f7c2b3a0308b0c7a526dc1fa60051801c3af63742750a5df76d06e5b075bba30885e8005e0811159f3d562c490e04931a74b128915391f2615db4a34bf2654c3b496eb0222b03d09f9fe7fe566669064d432a3ca265870efa414b73456641a101053b44d263a3b3add7f4878aa4184b0f45e331d566106441b518b2b2dbfa9f4d1455ed753a830ac351fd68f99ce7cf9dc3b8f76d4532b0ab1eee0ee8a15fa2914e05ac63d0dae1a3f8e2906629f9c7f05a63e07142956cfea743558b64462ec31cadd6c9d1cdfb87329682942a617a5ab4114cfc1d4ceaab8b9b418a66a9644d58758f88f02252a9ce102767297b75332035ca631beeed3a0d0cedba4b73f4656e2c30aa0a9473ca9e18b85f17d992a2b20ebebed1086b295c265c0eef038ce36347daf82e69b13b43a0ee58a6d3f45726a77dbe85b9004bed3d66a9cb5d6ec2b88b05db7f5674a82ef0b807fff136f79543914f9f2e8c05e75929f907d4107143a87422c09c24fdd8276402dd2d61c8b838fef2e56782a9de845917d9f4e76d22f2ca148eba761df9153274adfdcae8362e6b8af229afc94698acecc664219292d6b0993a423293aecfd721d7819e807fc4ddcc22d35473de03f428d58ad7f72acb07c91fbdf900b5f135e8593cb4d5a4fcd00e0af0db01f32ae15ab33cd882df7babcd0b8c9edb8b36a2638d228f310329b72530dfd101000000000000000000000000d76e167fff51cb49cbdd27d311561d7ad04e867823742efbe474bd9a39e7eb2ad800098ad09dc6f915bdcfb67a9deb8dbc2c81cf57bf115d138dc9b27d461d36ff9bd91e7254fb0c285cfe344b6e9de5beccc6853a21ecc104028128eb12be25000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002c4ac866dc89f19ec1fe6b0af89d1620540b6d091145cfd25f619af43e60a4ad7910dce1b14e462ef70b1e8ed63e68d3425dc6e7a7454b7b04dfd14596f3383b904c5b309455221b82aa5a911e5349ff35d9f77c4c6220448685a6ab833015a8f85e54d3b76145afc411924263e62333be0515f29da06f045fa0f3928bd78ee602241519bd2bf454e9d16e1a3c82311bcd551082b21c9601e193b536e0d4c8b5330226205d28e395597fd47fd9bcf7c67ce8452f47427c463c685714a239f3f020b30b0151eb31417e78258cddd6f49ab7086fc4cb6038d7cb87ceb88ceaa76a619758138439822e19c073b08406e3e736c913199832368974b2bbcc307616909ad6e2031618f645cc480142b43861beddc4fcfc3a62a41431af785e1af687c942259f8f0225d5537cc3b125f6ea2424b25e41ffbb917c1b0ca1f41efad607a80788be175102211439bd3378d1b5529411a6f733e3176cad817d02ff1e8e1fb27d69a0777e3a031496191e88ae4e31f8109591475cd0d5aa452e33b5eae9ae35e6df05ec4350010321b205592ee5cc734876e0619173db0440b28c85b0061e39c855c1801c7642ed259d3e13819b0692cc3755af1727c9348125efc618bd01dceba9857d76100a738f1405faa00b7c13025c84742d4705b6e333ce411e24c6295a397af1ff77c5f10369f9180f765fa0201391aeb1df7ef64e24f4a6853940844d6703c0eefba02ecf43a3616387d1d44a06584f6e525856dc24c899b0a45ac65fed4f0d415c52bcf013af7a5b8f8db697cc2e676f1255b95f2a4c50ab63cf25eeb89b5ccd36a9cb3570fe13a1a1f5d2892f93c4c80fd615d5be7233ec10a1028ba041e4ee4a572a68001fe8b0fae543aea50bce37d627b9eb660bc2aeb63f41630e5570adf37a9ce19ef2df09739dd3aba94a40e0b5c1e53bcfe116a2cd7bdd8e36f5638c160fd0043579e8cc9cee9c7333dba40d1954f912dc203624526023d2f4137a5d20c51b389b6e25cde4f732c444cc5531247fb74d4353372e2f6ae8f0319f2bdd0ce9172a1f62a1303708695c0b5b04dda4a8e690b4f9fe982c3a40988569c564ae93aaebb3ed4ff813cc71f3891cadb84ebadc161b33ba3343d2ad5a2e784557d0d48cef668d00728fb3c08f0001609d4b11777081dcfbc6f6a6470f13fe2e314efa7de300b87a5390cd9b790c79de9413d8c51c69044788ba1bf6fdcaf5cd53b7c32839e129983d73449d7b0ea24da312ff656950ecdddac0fdf572504777b70631785e9336ea387bcab66832c5014acf15aafe5aee3108bdd1faf3023c784ffafa6216b4e8867adbfb9aa404217ebcd6856c888f5625ce69a89deb0cbc5c05c4be1c1e8f7e5c1b061eae42590ef085b775d97377eeba80dee6bc5c9070ff02f6106f0e986323a183af51cd021f1ca0fec644ae84bcc7f4925b023ba06c963d459ddaa12b1caeacf2aa0bc99f3c3a693a4e5136e14a8824631ab8966d8de40629a8035bb0d0f61380906a6579a88de39508472e8291acb8fc05952b66552654dda846d88bd6d1cf41f9fc047ec754e560595ecb37aaea506e6bd4311f6d955172d2c7af84f99d280828b422a14495e50bd24db79fc63542dbc6acd687202e848f0c4a83a4cc6b7f8b9219103528358ec38edd9be55b860088ecf50d220265fdf268f5be67bf56e270b331f94990febc99b27d5c16545b457daefc4134200686deafb8f66bc052a1b620f817c58da26873ea82f9b17eae911dd54a879a3a6f91f85968326bbaea47769b5ba677e05550c3ee1c93f1acaa349693ff5bf40a6c969101b67bf00fff88b9c439b26d9ac1c64d92b359b356d56fd5191cd6d208852069b991891b83545ccd55f4eea7dbecaf02069823781276da6d763bb2184cd34c45358cf68233e5a2cfedd47cad7191848ff3e924933ea53ca6ed537c335a882d154614465e5a8d4111584eb4dbff98705905144fb3d254239af4fa046af234710d2827e4e70e25b8cf3cd7d53473510a3c204771e26b344caabae013901a54c99eb2dd8aa1acb02d9adc759793c1a326e32eb44e4d89e038e1110977a5ce8ee42c38cd5c063e3a6abaabd1220197dd2873aa87808eff4d2ab20c1a1082af06dfa0644d175fa7c75179e475cd8824b476c2f3b8593a29468f0b7f27ed367a4a63815398d0b259e6920afe6897fb9ea011f09e10aeeb6e94f9ce80a93419e36490594d2838cbdecae97b09f280c59e90e7c0cfbc9997bd1175d74ee171153493c2b8157a73d59c2bbd126ad79b74299a60a960227467000000000000000015ae320000000000fee2787eb2191bd32cd37e9e3e5649c16f3fecda26c171bb78fff9fc24eeb3e9e2305cdc40cec5475c56962c7cecaf2b97a5b46910dfb78ee91c6ba6bb710e6b8f5190fec81665661fa941468fbfe08ee835f3571c72f0fdc75cfaf0d843831e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a25b5da35883b02180a1014faa36f3b0f7b003fc4b4d1275cd99c2ec4993c71214f8ea93226d1d717aeef3645ab0bb67f19cf244c0149ca15d953588e9ce19fd9b46e11e718b9404dd12f212a4c2d2f31f39afb62bf825968f474a08eab5a9b105b2d070a79d63f038a88f53182075dd80cfa0932c7a1108c23e48590bd05cd402129f1e9ea09fe3dbc83ceeab34ace11529c559cc4ba848939ac5c42b39b7d2a8031d88a182b86a4bee989ad142c1a2a36d9d5a3fa36a424c58214b3510b45bae580a015b7413ad0ced142a593b5f032f182658a1e9e58d2c9887eab8e93cd2a3d1cfa438d2096db805f7ec7da8a9448d68eb77aaaf15ed2b2069b32d7c80f6ff2010020386b46f7096e81e0755695546aca2abcbc4849a40c468c65ba9f1862088e5720221de012b8ad9b720b9f7bf415d9c445c2e8359bd31a59ad4bc7690aa8fe96310032102335be9f585d010936e43f94171b1251c858bdc9eb7d42a385779df8f5ece021562d82485dc2b2c79c0ecf20b0daa63f7fe586cc0f47dfc8c2005b5062f0e88022fa61ee8656ac2a28495d38d40643b7e3f66b10f100f42046d3211c0554842466e4b0e3328389e49e48ff8213d42f3f3da69161a5779b43021dffe5396c874c34b7e3599ec749a297190504d0d5edd1c6d713727bc0b33ac4282e670aea2bfbc181ee0941947eec2d85919c8efd48ded0a99c7fa02f3db6ad4af8c2726b89e23a8c1d0c9a243aece3e8e65ac052a1b04bee43c094c0ee85a6fe6f772e38d83f59804dcf086ad188e72b51469519965cde4def7e0d427ac15bd1778864ceb82708488504a444a4e3cf02b17fd768f866d902a43e79279dea13592f6b185f9d631e22fb4f68497c9d97266e08b8c035dc5daa88657201682a25a09d613b1dcb638bc560567d6f3f4db5c5c11f89cc2c4c58de19e48685ba52dcfdc612248289f14115d825e37b8c58258f89700ca99a8dab2c23b02c64727e242d0f80d31ec3e3e349e99a40b15092ba2f5626fd86e2c8e1d4adb14275016cb774f174ab42a67814b435f0a949d12dedca070fdb59d4b291b8615a04ea6a034a3a676587481245dbf31b0f6c8896fb9ac1973001ee78d7a48305c02f0ded7f1fa61ab421adda0874e3deffba0c6a3c7b50ce1085d603f6ff4e797d4604ce2d6b485527da65390bae132c8bc8a0862c0893c75e72045eb91118aaef2e5f73c25a6013339e66060f9cdf9824d1041ab102eb888063a1157b19b629b04d5b1043b03d0313e6132949a653f1afa297fb558379d2a22a7dda7cec01c2018450eb6034ebe5b47ceb2060ce0dea62949d0639427f09333c8de2df197b3c3c0ab7a31515db12e0ae4f8a06456e2de9bdccc8e3c47747909122f04c3409f668500a7a211fd419270bfa373be4bd528b18b356d6ff8750149bfbe795d44ab1af79dd587285a1facfc7acf5107a04036cea189112ff7cc7ce098d74ac47bc47b8141c6b704d1f5710e07a43ad807a6f6168beb1c03da895e409763649c11872852dcfcb9a9e9473ea74237a7cff54274be5f67e80b77b0122fc67747080952fb83b70ff95f5ca60aea3e1a3f41294f3ae94b6a675ad18cc090a09f11b8a167cea9c37e14eec25641baf4c6dfbfcc999d57e80152c35e346df57f8c349e27a324d93a67dc74805c2436b6b7695791311c1c28998aecf00a4625aff69390670cb66203225d9b2514240365918760f830f2c3a03e0535062882f597cfd2c4021f51584bdd9de112639166653d17b0206a27a0f7b98215de4e90388aaf02f8213c277dee1f1839d4bffd8f8886fae23c3b9609fd946a363e9cd49b29cc4ba90bc5e5dea7e0787643be850743a1b656d9dc2fe21f77f6158fde184c01d4b1ab23baceee71b46f9bee71b9a69598f56b9ab3606689dbea72ffee3a7275b3bb4a191abb954c6681c370de776d78f4927c7491fad6c3ed41b55beb6ff919bae5d781c504f0fb9e52844e4afa099a7c509952460ab4e3a37c5aba70c0044d062a90d4c7f6402f3b3207494434b4f6777b9352268117b666c0ef18a633cd6042eccf184032b10b99589351d2a8a19ff079f869fb263379c1d4bd4ad39012efc72f9baade917eb1e7e2d69e35a08aa7975b103b41586f3e791f11e1a1d6bd9193171273522e9fada160afc352be6f8cfbc0a1c61c2deb8beb35c45d6cc3f7c361d8366b1d938df8dea7196c9ab48f41f280d774aa683b54c0fa30d0d6c548ebfceafff82e3a1b3d115cece0d7dc00d4e9c529100c2e1a4e214307ee2b44ef78482b55794280e060fc6586ffe55f108084a693b1ad5b7942a7a652cc839fba5ddd45fefcb90cbc47ef070443ca9c5c714fe9ba671708dc98d958c1a8a7e172cdf97db233ab3d022fae782d2d7963e8b9729e7bf3b310520d2dddc9c05e3fa1b62100307105", "65535251", 1, 331693917, 1537743641, "b92a08a87eeb5d86733faf5d488bbb85f527c8aa13ccfd7f858965a5a609e034"], + ["a8e39515042e90f179d0e6d5891d682913600d5149e6c78a28390ca68915cc421a866a2d4e0000000000dac3011071ce542b6f38fcd6b3aa2843e7a67bdd3dbc9d2dc172735f6e0d8b2484d2161203000000086353ac63520063535fbefcf5540a44f2742e78c56f8bc42651c393c940965d1d65335320e876636c983657d402000000055265635152ffffffffb06252da7279175a7049e54b1f72b5a99956191d904afb00d5eed8aa06b9b97e01000000002e1737770269f9c9040000000004ac515365a18b44010000000000a1cc41fe02d9492b03000000000000000000000000c9b25ebb913b3385ff0c34527a436949ba9243ae3969a358c526b63bbcdad59098bd40f17b821467d76d78e77126179a132940c663dcbbd45cfa967329164477b5c562944e23a09937f6769804f9d9f0f84c6984a8d8c5c09fe5236848f4562500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f963e3b9e4682859011b273109da7875ec45735154edf46d9c471491def250267f8eb58a6dc649a0b9055dc713586af98d969520f3c80e100eb60e8825958d052fd780a82d66a0b4587e976e8adfebe764439e373237cfd7b0b63fdc02c6568c68adab1056dca44b0142d436bc6dae4bccdd19c022397211843fb83332e39ef803053ebd7888b7c669ac037ef309eaf207fde1b0d35f58c40ae1765e51769c18e1031f0252e645e4c50ab436bf920becc4463b2978e796c31e9912a0ccc0ba5bf1210b023b78a76c184065b0c43a1aa3054b2e6e18c157016d664ae1ee69be77547f0e9257e65a8dae016c830aa74592cf089544bfb3494a15599df9ca53db4dfc8c1b0304e6e156f0dfafdb70b295891ad4f24fd3c4dfe691411799dea4a955b2d4ef93020314ee3ac4b0b5803981427ba3b33fc63f567585e9e815c1c3be25434233a4aa022bb6a0c0464de9c0281865b220bf8dd7009be3029debb605783c8e5d56edef3e0328782b2f8de653574b38b3616d2ed0630a58d642579640520e041d234319cdf70213fd3e71dd8004829a03e096b28e26e76df98f4fee55e40a540d4d4124bd6ba4c652d01fa580d898341ebc402cc7eadc752506046b4676bdc26c5ebdc8701c2d2f739a2ad330e8d13182d833758d426c857ca1e39b5aec57ec0a40143b9de96a08fdbe55db77cf90bdadd1ea8feb0009c945c29dc46e8d7125b4388ace21453d481cf267496a822c26c52ae9e283cadebccb7f73fcd4a169cc0f1ef2d4e2dda371f7584720270c1df81286a61a16b1e9fd4c61226c295e1e4616a009e0228fb498a53166910a59b107fb55262969c8f878b97be8b3e1fba59f90f2a47f8b87c10f7c3b60b8d6159660d74e57b220dd51567919ef2c11902e7e540367533bfe1a3533be7fba3b9729d51df1de94cc492248a01edf7602e1448b5572c49a9960bcd43312313df6f4a75a9d44a74828bcc3ffa7f1b6bad180ae4da1854ea0d7d2b04e9627b7ea86dffac41f5c5fd7f690a6d744d14d37086859e36a1e0bbcf254033c3ba89a8af0878d5b02ef86e3cd155394f0e05b550c41c36765ed606fcf0fb8e3b4c93ca9977563411de035405f2f4323071ade6b2ff9efbd112f0dfb99e11b457e14488e00e56ea5e5c99d65284d96aef32f1e8453812d62b524193c169d5d8b92d93e5a02db845eb0155a5131c000e79f273bede89ac00d38017447d5d1f87107255812512d8d4641e28864df53c0653c57dcede4d326ebca532b5d6058ec80f9e113a5859be655a9fed81f9dfb16a5fd12d91ee3c0258abb228cb4bc9184c68a25706ed040a8088ddadff8bff14f39f6a74dfd303685217cdf1bc61c992bc87e6a84e71ad9ae16472ad6bc0f1bfe016139cebeb19b96205d7d8898f1394c0ecd049a53bc9faee54bdcb06c69685da355644eb427c72fad70dd163736aa9187514c3329a07464130c905b846f581f48c3bd7d728b98bb2bf78efd8e92a83cf7f7650710c513525b0bd1d32c5b08ba19d150676c65c358377c7a7452bcea4af78adb73807af8a2b8a726dc873b0f2c5938c91c109c0d565ca3c9ecad6bcb6f6f892f8aeda0db92c2d8b9c8d0651779c70980d347ad3fae0d373945f039568bfa248907637136c89fe3cc1e14a9f6f728893eaca5d8785f91e2533727b68150485037f846e9bb9a23cf8c1dbb099f475ae3587b66185765ddf4da8cb7f6f309e3840098b21e2bde9593f302e6217e0824910d0f0674c504fdd177b763e115da84687c9e579431852c1880625be09df02b77caf13256f313dec6102eb907db29a7b7effde731497c821d79846f3e770fa56ff91fc791bf3e5e0daf34d2a8daa9e24ae154d42f852c3dfc6fabd4055f8df37d80c0e5fe9cbec444c6978865679bc3ae9d00610a9f4cd42ac46ba6b621834612a8ca9dc86a48c1b32f5f81a8cf8de912eea8f7c688d0114e424597c7b58b79906e2378f5ed1b76669988278bc686bbd944baf82e45caf3b5598e738e9acb189bfe026fe52d3ba60743ad9bcdfb143e505f5a0a603a8e6c06dc214810a040182e57b767d27f7ace8e4c6165ba43b2ae69bfd1d1a21ba28bd2e621d0bd35c08b39ce1f4a085cee248f4edbc19bc6a7a43f481abcd2332f53feac47b9bce7054114da2b7b987e4fc31fef7d337516d8c3a639b439acf188c25b3910ab317ff82c07e82ace8025917d61d310b609641a944369172d41d4042a52d0372655c556f3bdfdc602e8b80c91af301e7b241685f12927ce7daf1bed70a416b2ffa03bce74eb00000000000000005237570100000000c79877c0666f08f669e7aabdaa3e64c4eea7ed3514508b7d0954e88ca8898d665e521df7b8d8833220ebf3ddea8a667ebd40c0b9bc1f2ffddd80c870b3ee96d8479b59ef8fbcc457fe1820cfa339417ab5bb5982ae75986fc04f28fd586e156c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e82e94de3c8876344543fdbcda4dae62e83f7c81b9a1bfec69d21d56248f30bff9fe2a64ec4f6e2b037ceb3f90adc53a3ee2e0496fc4fb4dab3ca94a1e5d8667b4e0589f48a521e23109f26d10d376dfa532d5ab7d49a297493f4b15c56a8e4772f25adaa14b19783737cbbe3e8c8983a2ae8c56bc59fb82b2579c62e38087e70313cac0e69135f5bf416f035b4682f0491449ebea9f84723eee58fbab1a7f787602168faecc701b5b05aa5c0b431afe071c8cc0a9cc8969a69893fc686bf0ec0bd50a07fd6d4d3e01f4a213e55bd82ec10f00c0c041c447017818aa20ca299f435e5f069befab50ca700244f3549e45705db4ddf188f7ad1a9151fee773b6c0d0631e0214d3a068f56ca127f8b89d754351d3699c87e7f76e380559ca3aaf414c16c4ce030ed4ddec54e4751b2eefda7212c4d0486ad69e9f4f2ec8a925fccec288e3e07a0311410284db2b0c955099929108f195be5c456d123c959ae059930e39f422ad2b031fce97e1cb49c1861b07f131b5b95fa789ae84dba94f0b4bdfa30d3d916f37620304d0de4612ffc06d8147799d12a68c9e25cdd0b7d6550d2e0cd96359272896030e9449af746c15f320a286e3b1c57366d56204071f8c0ed655f0900962e210b8097df4942a13d2e3a19240d4ff38def02dcbe24dfb80bb5f8932bcd467a28fc01894d0f7a1c387ef59825f3e1c361e441f863badba4fdc22bfd23c6b523277558c23633d9cfb029e0badd54d5689b9067724d7f3554da202722864d0445b4a0515ce28983ee84a2c8ea3b24396e95a085625d1e124e562f91d4b805524498a21d7235a374d836ea267f977b9ae93e7c96269ff9fcfbd73776a06476516a524eeb2c54640737c36ebe508ed6f544ef9655716fe79f8bd21ba312a0aa4c77d13b304c8a048d8c8d115727785284d6321f0e89154a02199050c8e910035d2d850d182363d47b65d3b4cd8cbe4568059beb81a2aa8e7f506f326a4f777089a5b23b8daba6aa6ad8deefbb842a4c0ca4c61288b5b2faf16ae1bb3ff592bff61cb9fe325dd7da9b5b65eb95d4b9089061266962274492bd204897b9801561e050cd8c23f0047b42dacc9f92f938d2514dea22e4025deecefdfa58493abdce704d63b7f7fdc6057e99a4004d257bad03b74936044b5ce840e8ab6607f9dd57ed6a71d1bead62fef7d5cc5d1014cead43ea121027986c066b775cbfce9a9b3d2bc76540eef59e067242672aefcedecfd8001b52c0514c31539909474a72b888b5b4893a78029d7997d42dce60a8d43b519142d69bb80f58124e6e99d993a6d02fc81d955e4f2c6d528bad3f4d28d2308f75b97f2621393cba346d3cd60f6942f9cc016bfb8f0a8d74ca921daf2e077ede21d9fe9c8283274922b6dac7a117298d71b89407dad6d57a5d578cf61aa15a02aebde171f439faf4ea18bfa6e8391dd42cfb1e1d3fc42bda46b3807f491c3f09feda0841050b90870be7fad610b105577b7a527191bdd64dc63c9f58e90b021939d0c62c9bfcc098d83aca3ff28851100ce9b845c61bcd489c1bdbf71a6e7188909bb30894f4c2228e994d7d38452090aecbc650648907f28351974831d88958fed371cfcb6073733503de3970e1edc1674fbad266f58650fed845a2182ba8696cf38392aa44bb76245c7b72f7c1434fa4bb8f0f272e39bc24f69b50963a43ee4356eda7e5604056a473cf7d0c57a2195e6944b7d8c7a039d8fc70165d131c16d05b5e7302b97af5e5ea63d72ac5277a09d382aa4e3e44432a242d6ad3220ef72ce2418ae760c0ff22a96facfc9afd34e0643a764f01d57551039eab984e8fc345f3539237072af9034082edff505f5e3f822fbcf2eb06a8b7ea5d7a4a2a11fee138dd1d0e874d3b00477a65180d84232027d08ce8a3a39f1455b4c4378d02960708c107a626be25b7524a5d4df9fce8426460fb9a62deb59180f1d49488252b1747f1cb8c7307e2962b47c9924334c3f11505ac5c7bbf9ed3a455a1751bbe750fe4361d78a343144cc65a22c3f4eeb167f19d92adc08df2df32ec503a14bb046ecf9756629f0f8a0e3ebb3bede77341c6cb3ca0f22dc0a08a8edffe8235d9c3b6f98bceb5cd12acbb6fa33651b6bdb1e6c89208d146a3f27daf51391b0d5859cb997119bb6389adeeea32747d51e89ffc94d61b4cb78e0f3eb78fe9b5a42edb43603844c534a5f9a45c701ab1935489e0d23bffb7977a354c1aff2319d612756dc5fee70c94c06637baa5c321cd5dda4c9e5aea3e33e1ecabca99f630f0c6946a333be4b67567d574a4cc38f96abb1a73746efcf18a3b3ac2e3d99742cf963b5804f75dc32b2fcaf44e14688d06914c0a7c15a968fe3e2b16081e562852b7a395519df2f3f6cb59a3de5c129129b2b81988aec05e3992873b00fb00d6e3a7efb8e07365801", "0051", 0, -1287531766, 1537743641, "6f9f56cc0ac3e5bc08e74462812c44a3c79f018112bf595c07e2227373a0cbcd"], + ["030000807082c403011660a7710175e9a0d31a7b9877f11458fb114329f1cb7df9048dae420e20827d0100000009ac53656a5363515251c0344c8203715e2a000000000003000053953a5b05000000000453526a001c649300000000000000000000ae61f70c00", "52ac65", 0, -820935966, 0, "751050cc7c36327938342b9f74fdcaa543e3329478a204ab8236a32e300268f9"], + ["030000807082c40301891d995541c87f2d520ab4ab3aef31effc9ebf9aa565ea5ac9f2999aa7ca6e2102000000025100751ebc2901b0685e0200000000005cf506dd0cbb371d01b8e642010000000000000000000000009531e6643b109ace6240f98db2b1411eb30303c68243f560a56c76b6b2ca539463eda939325a7d58a02b2277d12234147b4a5592cc051f3a7eff4cbe85c838780b4c6d8c2d6240cc4dc68ceb3f12193da36ff25a1e0c8fea11576b940de3085800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000538354a1317914ca1e1aba8aefcf26d2da30ea576432f07b41a404cb6afa2c11b08725f3a32b23424970f5a82ef38d5060bb4e8e5156ea19db8d0cf956d765cd7cc4cdaf452ab170b5b64d347546279b2f9d0e1c5fcf3a3f63a2d48d28a08253f90b774349d7c0144cf23ed4c327ca1348d4abe04617e18efbdebc00970f534e020b38ce41cc8bdc1996ca0ee3e7cce194bb89f14e10d5a2e8268ef9132a540c970220bb6fbb3e17d72e4ead9ccae59fd10cdd2051a9aabe77be0a8a41201f4288b90b04579ebf75a68c1fc0c371d4cc10327284ccf9bf526d02dabb6fc30b7090493572d0b99b07261941bb6eeae66c77393da6dfa8866c1a4cf01da3e1a602e28ef00212af90aefa2733a3758ddfccd70ec73e97e1e6f125115f99b461450dc3b79d26022d748a098eb6d32118cdb4b5d6d8a1b9f09714beedbb76b7b625b7daf206aea50307a00886142a59abe4bc61057838ac46c5f89b8b278388a75608ee84c6194906020893635d404ee13574dcf167ae07dba0bf4de9c5eb7cb0633e799f33cfa5dc9c0217eb08f524017bfb7f143f2122f2c06fb469f4f80fdfafbc6336f3a38b1c21a55effb00d484ae472a115c3e5c2e59f78989ef246b7c77166655cdc7d78bb73bce2f26228dfd756a6356e95f399baaa69a9743be03e36000d9f6daa05c23e1e9f4750702f099868c16ed8e68f8a28063cb2bb7b506a596a4ec5ace6fe4777ff40b3470ccd450a0b006bb0901596f833a02c8239ac989b25ddecf8893a1ace0a199d587cabdf420b630cac8d5006f4fe5e8be09f1309dc55991ce6720699e3b4cdf262645410831b297f5d75c69315e8223637ce15322f27fa4c454534f39cb5053fd1db28acd7995a4df9029391aa43ebe5f4662ef6cc19858a352a18aedcb9f7cba5e5d4ada325fba03642514b08ce0c263b99e6edb8bb37b3bf37820efe1a53fe404c223884f5b829f605ccec2c557beb4541a8a0ed5a96b92dce6633ec60a075aa0e245b1ed8b671493d9cf664a568222b28d7d709392cc564325f36289709983172dcf7a46f42c2da59170179f7b4862fe9df200112fed63f16464b3c0f755f8bc14db8deaf579d3179ebdca6b6496e14087cc0aa905f5f0aac1df341438352f7786153d6139a4eb280a532fb7bff46abe341f07320a7f42534774cd33e1f160599c01b60e510195d205b0769f7bde87524d5eb77a1e4ceaeb0bd73eab70dead45ad0826e9732260c1ef8834a4b96ebcc64dcc905e60d23727fe918648e21c09ebf1cb2219ef9210b7591913a65e8233455885218fde73552b8d2543473c9355f15739a5a0b6093959b6a82fec2f8a968168187ebc7612df5e15689fb21a04fedf0118d74d5edf6bddb9d5e9a144131138375a4eba86bdc5d913bec9b9b4c3e25f254c97564c013eb706ea6e7971375c8d25e09af6e6019cbc708b745ffd474ced9f70f7922d3b5a5bf2f51d93d93f7da4f843b498485915c89173ea58ac8ad8ea061c695647f8fedeadc0e9cd1ec5b6ee56efc640daa81468272e253d50c23885b08707611a5d9ec68dbf01a71a9a2eb7c339ed7d6b902ce8ac99a662344b68a4d7020ea9ab381e0273b842311cb7a62597cc58b58b712c55ed55208901607c87d2334b6e735fdaedf3beff96d72ac68f72a8bc47af542c2deed83123f7d2dbf65b6ecdafc02564334643bc3589ef82cc235113a9b09236ea92f5e9a2ba9a5731ddfc35ce9a32c1ca08b34a9162440ad2509677e4cd92a05a56794e144266be99934e73b7b8b387b035b8b54ca83ec200e162f8a7ef5b699c76e1f01ac844d5a8a32ed087fa15826250db9184dd64bc0dd961da06622f11f8393cddca8b16f9b5adf55d232f89c5b213d4913466f119b3d92baf4e12023e7bbf42b848a3b17085b82b20c2cf7632392ff0a9fa88a960578be8265031377961e981cf140fa509cebcc6bf91be66719d9e3d1a55deff223187706dfc9de7780883e89e3eeaaea8ed2f79143760ffb0d4beea9bf771e6cfde3af10d1786af38a559fb3811b80033c1e6ff60e24e27cd97802f9c20efedc485eddee04f306b478f4e6c79b1ccff16fc6592dac31852f20c5f09ad2df17f9fc75081fc413cead24b7ef46a72c966d6c1abdf47ed9aaf96f786072583df36709de4cbdd0e85d8d30f3ec3fefac7617a82b3ebab1f53e574dc11546236c44f70e07d1995f20f4f227f79a4342543f274977438ba0fee06c65917f31b5c37cdb5e2f0ee42b0541e4415e25cbab030a5a0dd3d96955026ef89d68cb20eab294e0b2eb4e0baff3fafc8dcc4a01675a91777e6183d8c472ee78baf3048e7cc01785800f91d699e90daaefb9f00c6d74d851b9bdf940665cdf132bd714aa0ca60b2c03d90a785a1ba9b93f3448ede6f5703ec9b395ea52c7aba4ffb1af8b44b1317803", "0051ac", 0, -1086482984, 1537743641, "e1051f93a238142d4b2736ed77aa663dea781cdf81d1eff7db7fe9248b1b9498"], + ["030000807082c4030205f4513f3256bcbffc4b2babf96677f05f530d334ea5aeef8ce3ecba8a65500302000000095151516552535265638b910512704066fe9269da3f475d801f5334aa61bae50ba5df1f549f74f9f78c0e95e4ad010000000165c9842b120151154800000000000800ac6563ac515151f9e8b05200000000030000000000000000625ed50400000000780636aef158605d7e18f60fb5cb9dd712fb31ae5d5a4992777cc272ab2b58fda3cd1798b8a2049e499f3a4eca650fd0527a14330f5552824c9381cd318582b1336c171b5291c247cb58381b1c015c891d2065ccce128e1603811c490952e96500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cbbe0d47c06b70e8d12e319da10849d49bfdf4c2d231941f1bc774e55e8f607cfb459c0103d54d75787c5316117bad75dbda6debde4d6853d03a54f9f7d7c6b9db8fa07a22f0c2609d261d67a23d6f5d3492d421a5b29eff6007704df70a612367f5762012c8f0d1efa5664a474fe46c4750f85d93d56de24f5fb7fddd8b4d83031f3f4fb8ffc4c5823f7ba63760729e8abc920a4bfb0c45b18193a5c726aa82d3022f30641383f1713fda3ddbe37c29bfff2d534553ae3536ae364e74db608b7e2f0b00f68f6c7283936c1d017bae86151df98ee38787b34d770dbefd457c2a743a966ed45b7ded0ab3800dc7eac27b008b218c9af22733bfb718ea5a9f063ac63240020a1bf2e9832c0a12c33315806ed6b550cb16c97854ea9037b9fe912ca1c155050202644bc964865f008bd7aee63a5d8d46b015fbe6284af241576e3357daf1c7d102253e62a6a7f2bd87977c870617d17e890dda9f12f3d7fa5e203236bac14eb6aa02111c39d4f2b08f7a977cf0e9a0e48fb98b92beb57514a9c98f28d607acd31e110302c9d2e2712a2b29d54f630d157ebac0ee4e4be288aaec931cf2ecf29da34e10d338fe45bcfa46da012d4cae9b2b132491e4765acf0c54c11019347cc76d8e9e2565314658e41bb85c70545b3b565aebd7c2840750b2f703353cb68f01a2c8bad339988dfbae8790125353e9ace5f2694ed0cb9b857d83b896920426c63cb570ccfac39c1ca4248e4d0b3afb48dec0c8b73342dae3edddd452ca1dc868f975a29750a739470927400b8f7d4d1550b8f30f6da86f3ffaf200ef805b6c9735d208ddaacfdda04a152c52844f64200bdded3c576fc2b9adaea9061003884647425b3c71af3e3e9bea2b7ca610e3cb9112d8adc80ee8eb2a16e48fab323c4984d20a392e47065710748ae9c4e8a8b4c7ad67c8df75e99491e5eba1d1d25dbb00629739c1e5ab9a6d113412eedc0fe25f90fa480422b3d1c62fa6c510818a63a59ee263be3c0498ad234e787eb0ecb5895fe5fc223107ce0e4d156f570bdef566c6a3db8871095f00543546178bcd809587d5bbbfca369d4a413f18748690c1195385758f050b3df208c32ce6a63d0650d2304507fdc3ec2a819d229b366b2b7b03c374fcc728b017334d926d7e2196e78b119e933f40a1f83efc9da5a0f1299d15016605837f75d25afdd8a653ca59ce17c5a614b549997591df2b1cd729e544ea8059df97b3450114ed524fc22716991b1a54ce96fed8fc3aee7b10e3a593c54e4dbbdf9df3c09b88b66c23f5bfeff2a09147b1fd1e41bc5e2d5f76abc4182142e84bc074558d1f24e0635c5f7e25ef7691c57805a911c7a0ac357c01d652cb633e4de9e40cefdd4208027d2aa9a5fce6e64d01a0f14e35d7a0acb4a8e964620503178f5f5c1c0bc82841729fec411aca5ba7fbd195b24d3a8d6eb84f99dad6b5cab5e9b387a77e27f07db50f5ee79fc659c9b7fa2dd57eb0c625a8a7925aa2f0a9e027ec5fbc4ed103f4a9871a177a468889ca03d03427df09260e541d83f971c58eb52362646d6ed60d2674b7e5c9266ce957605158e258b8e6069e99f97c0a80a44e501cc36c172e8d6907b9bcea8c119d18bdb6e4da4d01fd76988ac4f60c15f9e19e2c1b4c6bc95e86e79ac10bd8d49c29620272bd904c9bddf84e76287c296bf97bcb4c04bcc317e566de80a9bf70a26812bafcbfbaf3ecd525c0f82ce190f537150c4ad25a0f9e806eed52eef03545ff35b617a46d35cd48f7a749e7c050d0073d8aed1fbcb7ec1035517bbb82f682892a3753c08cf4842c297bb065fd57be150c6dddfe2d6dadf350eb64b255c393a4bcae395de0e6bb2c75d872cf4bfd3df866583d09e749ac330214bdce853cf44e457958cc3a257c0d5902bac5394fc9627ea617278473b84978be15a3a845711501c05c15dc5a86b91438697e0b92687c28887d1ed887014366b7a0a6a5ed8068d206d899a534f7300a64a1c0b21f26cf7a929cca6401143bf6e66e5adb4bf9ce97cfa2089a1b88734b5574737aaceae0a69f8d9632ae057c62a846560e3b3ae2d616cedb873286a12cee9b44326ef9fb5f64b482100d1db8c74868c4657c3723ca664673a51e6282c042f45c87cd795c115d236c00be4863897265b6e9d7070ddf514b10d1c60c886754f358d8a3be82c1afa797e5ecad33c3158f1015834b524ee6f5a4204d3a7647080c687b752ad8781bbdfae850a73cdeb7e797ca797e16795ee28a65ac7aedc04136f7e672deefdd5667f8a6fc040fa738c0b925b6ad1a7495810500000000000000000000000004e1f0585c024114c9fa95e83e10cfebbf2ca3e9b841e4ef8b69d4e07a1487dd0b23e045cc630462f78a44b1bcc6d4ae5eee07641b558144da6cc047abe656a8091eb6c0c1f8346e386047c847f07e61e6e77bc00de4df9ceb6778cb9ad69b3c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000aee0b064970f61078053807f9ff9c510afcbf75458e93e89c3fd0447a4ff2e301cd1e38b59155364253df80751bc1cc3f9f1a6817f347fe357a4552a95fe5ed4aece30900ba35aa535ad03446edb30ccbe57911052dfca1508645c097f3629fdcedddbff28cc054e293340056441d5c76aaf6fdb0960e03bd6b75b829d14d7480319b86e6948012c4d3f48c13a2bf38189191cdace198716b19ccd4604ca0b8e77021e0ac48cb9f4df8218a894501dc5283a0e6ba5665db461ecc2d3e8b2a470bbb10b0796dd1d9030abf402c9e462c8ee6e3f1bbb67b915c8845adfeb26236798b6dce7d52315e457398c0981a58a4a5ce71bd538ebc99299ab7817b9449c48220c0b032c95dc4bde6af4798f9a847e9df259887c021d268ea9fa0ffec690b6e54f6340031324e1c7360d67c47e3f9c06f850bf0b70d02f6e47c7f85000d77a6d08d2b07003032e3341bf56488aab0f23ea10b4f25d5ca536a1d057b07f1d4492b4c2665f520321e77e479f33427c7f459733020761232b4bf72693c413a540b7420529aff2ab0303d8d331183f4e0c8be8563deb4af2c834506ff2016d2de0717eea6b8c4e5f3ec4de04d0f983c0b4b741f520c8a78bad038959d8f7a7714ae7e5ed4fe85fda0262660fecde2211bb71fe37660505bb068f3e3b9f5a2ba2dd0c0c4a3725fb36bf432e11d08e5c6ff98f0d3889cf75fb770a33967979dd20d6055f8de24d097ae11628e359e1c8eb1fc1f8a2a9527e460771f1ff6f87ca90d6e2055aa53e9f35ce8d6fcdfc2d17a433ac8b3caf242bc4275eb581baa9ed3131a9a5b2bf7b35780bdbc0da8850739322283c5374f83c8814985762a2ea169b591a029ef1d517717e3c1313ec478bf9da827411f24983e79b527c966d723d4a733e3e0330cb33088accb5117d32108982035b3e3e91c509428b7655a9a3d4fa5ebaf8a494a98d1eec6774b79cd8400a211b2244e1162405d20ef1d32afab331d35b1113fe90b6cb904ac4156dcf2b7af866c5a57b758ac0f4d3417ca817772327132aeb6a043278fa8cfc61ab7a822b5ef14567ab5e0db148f054305e2501d0209166195c3a66e22fc0272e1e587b4bce5275904786678a156377ad800e8a60b952a1c85adc37f7271a4587d6813db173ca5137539fe2584c4bbcadd64879f4f9c4932c548f9b64257bf9cc199b734725b739a1e1ca431e53f1a5a7436581d2404b3f708ac231e8eb99463bdb3f349e996d68c180d94b023c20842512ab103811c10be628c30ceb67a81012fc51234581808720f7f34eae2b378243a6d56b23e905978ee6f122d117ac13700e1e9ba5b9eed8ff4b4607a9d9924ff9bed6957cccff8371e2baa7fa3698af52205b775d28ca59229c25517486ed31e1287e3de0df4ec5080017ce5f9146e4d06c8da314788f4493e34b8a189668fb0c27eeef4dda30a9f5d7421f9f67d284de91636960ef395bfed887b3c4ff86791db64205e490d02fd589d9ff7ca90441b7b3a2404bdb524475e56dc717b8b7d46a025e1de6144492d061d495f8a7944f7d91c08d019417ecb7272cabe536893a1626656ac81b9955771a7b4f93ee2799eb58f582bdfe0c60d293ef12249fc39cc0f480a5da2293b3952d61cec9a2667b505015a92da1111d12791576e1a8e9d40f5929a5f889abf83eab057acc990e8ede90741f30da49fa385da1504d71b5bed4d75059b124a19789cbc4f6d63f17842619231008916a9f3670a5c6fac94ec0215d880a9aae16fce0b32f324cad069ebfb947c4f5dff485a3e2ac48ae9d757e4bd5f1a1da0ba26f3a0ea6008f5143bccaa004d7bffdf41627661ae134f6e0da0d9ab55f66df3f96c99ee617a2a57e09ddd482e2d0f9b4dfe1fef00ccaede67f99bff8305ab0090e8ea2b723e258a5e14ae8ed96693d729fd7a53da688e691fc5fd9bf0cb9590481387394e69d04be18baf9a984657ee1588b445b23df3c491308c149f2c908c9dfe0029fd4c030cbf68267f72ae6e935dfcbd6a3e83c7ef73e5d13f94bd6b9ffd284791b66513835eef075972d1eca11a6cb4ac7cd263edf0ddb0170370d3c9eeb93548d2cec1ccc34554dc41dd31fcab017bf487f3c8b1510732fdf999a8816caef3c95ed10f16f487ef73444d6a033d19800fca9c57a376105de84571ae8bd25f84969969ad1c8bd41031b3505c0215388f27c6d2f531625e851738f990aed83601c7cab96ac544bd24ae5736348c857e13c2c278fe72d5b1d975f40f35aa8d81d8a25a1540a03a2aaea52b63b476b3e1c0cfd97c65d41c000000000000000003e95640100000000b7bc3afb71be0774d4a857fd28c854eccedec53bda6fb70f60128daf6a5dfb917e58e3e4885b00468938b49f9e5be7b279008db9c7da3c42a9373d0c8a612b83e857dd5e59d2288e178b1f39048b923e292d08821b9b08ee8c29d9f6a9c2ba560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000074f29cb26cf064e4a2a684e757f9e18ba13f16b75332b6679a2a24f3e8ac9c6331f1d9ca7b0197cd35d7ab1346e6f5885bda73beec257c37100758e10dadfa57ba0e342c530ab9b10d000f20ef154b18cf9d3938cdf4a64f1b2e529daf4e91142fb8d9aaaeced3bb584d93d87cf8a1cec9f8075064c8dd5c26ba7f992c646a0e0219b2b519a4d09d7759d873a67dc65a3cfe8f97963c2a02be6624942bdb162ed0021e7d293bb6d2ae4797d3acc890ecd02d8e403a3139d2ad07aa4dc954a98683e00a01c2baa45c05a4c82d859f8783fdf8f5f2f996dc7e04617ad7aac014b49c5c337a6d47e19c013f46a6dc52a033f5f8c2817028fddcfb1ccd4804815587405c30020626548614218cb197c1e54703724eb54324171b1b52e57dcf533ca482fbce14031aad64e357960084c14bde4e043033f83049be8650ad7e4d582207ae575eece1020f879d290698df37f6db02c46dc601e163a4a8598c8bf31b5654d58e8b2ff74703200f13bf887cfb8c684f9e9c2f4b2a855ea4fcac9d24759d12b98c3165b739280209df4602aa6e54780a843b2c49618146abb61e623fb6b40a33ddc0721b84960ee94b937ddc816a6ed44f9984fae8037945eb41729c1f8c6ae22436b53177b80b21a36013b17ed4560c8452e0cf0cf05136687f4157b2a2711743f4c6686ec61d578b17548c34dca84cbf94ab58345ff2adeb47c5021db70059274124ae89918aba009115393ef6046bd6d45a442536f15c7a150efb98edc9874ee766855737e9c6006c679c24a015a3fc33dde6dddda3412b281b85118540c7b913ef902d7c1f2d69289ffcca7b274b9066ca67775a2b698eb524dc289bae35b3b92ba491d684cefa5b829e2943544e9eedc00e7297e27262c8738b67939a03b29a30b8ca375d8a393e2807d9e8bd43ace544ca27e859cbe3f5b76674a09a33eae62241e6c1c15c90768781e6c142ae204077da5d3b0bb5fa188ba00a594ba0b013baeb94c99009057870a3bf23792b72942069173ea00b2ef19ecb20fbf2b6a2d5e28f4e88923c041fc134e892e5b7838220b146c46acc85e1280c20c45b14ee317f87483fd17b6b3a4a4be2f46083cc338f20a83d5589bd282c0a27cccb28c04d95863915db130f8b5776915d31df62e83aa2bf8676495b7a8aa247232d2e081482f7b32333afaa4c5f84617e08f10fa3416b8c563a72091ec91a19cf45a2d6dc185e988189c9537dbc068873eb36359834c0ebc900b6317dfced8aa15fcaf642192807e4626813b2b0a4353b14e8c87baf3dead2b5d29d8ef7c1f2bcd9ade30827e4c79972ba2bfd360ab397df35c3f828629864a120655bb2fec9802dbc18806b4b79b2a46f3c2799796906276edd82dcb16e8c604b3c3772e8cce217547c3b8eff0cd4cad1f14ad72acedbc4e6b93333fe894af554460cae190c5ad29c36dadd6f6ee172ba67c8c3828c86ef8b1ba8b6b372b2339c0d1cf7ee5cd263680f6fba4a5229c24c318c2cc2647e0a4f4e805cb0e97e99495b9b624dc1f06cb8c8fdc874128b1c7c4eafd7ab29abd767baebb315726e27d71b9d81fce17ae38ba1cc1d2aba1c122e9b5a5e786fddeb88898b9ed3b544d6fe668ef59c1f5b286c9cc3e456ed25775d6c029cee9a78ae71ba9b190ff78ca3c7a411971c14308989343ba71e3110f9602cbe6f8e1a53998572a13051355ef520147ff9296cf34b1e705084ad7526c51b7116c8b70e70cbe03f2c837529c3091efdf2362ab8fea9575b9fe1e8443638e05681e48b70e7a4c5c52f611f4de6bc754fdf42e11722913d8e730a1ef1d45a724e3ed1b68ea619df998529d1af464ef8e84815a2df06f068dac368449546beec2b6497e283009cb952876fe13649f3b76810274ba3f24a5c7ab02b6fd4fc92aa45d12f428466b667fb7a742370a5798d767367b403c09b4f3b5c68b291519ee189dea7e8e110e6d213cbb06c1fcfeff9b524ef37fe9da134a6d9ac724cef54d8964ce686c9071cf85fe8cd50b45e7f4d93c0dd55581713dff5ff273769257223876003ee173693dd9cd75814209eff8c9da9633b892886f4980a841d35cb3d0b894220f837462f078ac520eaa0990b279895dfdb34363b6a263b1f8961ec99f27ee69d4acdb3ee74c3eab597e9cb992f9cac95af4d11423c8a2bd3abe14a6be7bf9da017924c3c560a94a9319e0d937042969fa2d172c3688f7a7f4dc5a3a909bfdbd4cbbc5686d28e97ceb32e1bb7296fd9d94f308f1cea5388f8e820288514ffcf4166253f255dd7787b713d8affc2e6b92e96724ae1c5ed12154df740165182ac07a8c335bda8fcc7d45bee636656936390d4c2546980548e2464bcfd2d695e60c4c326b01772bc2d3eebb3fbfa4774a9f19fc394e4609982cd324732ae1075f690b388aabfea563812b9470bfba70c", "5265636aac5152516a", 0, -166871512, 0, "d007e9a6b53bbd86013b3a3704d2597c5990bda0b59b8a3c46704b4ae67d06fc"], + ["e1dd5255034b8e46fecf433eb9def3b807343771a7e8f53e5c949de1a98fff515230fcefc201000000006afeb88005a8a77de577176cd2c5d34b3736cf267156e4d7749d5635ed3d14ffb6e4909e010000000563536a6300f48dfc22e562c18f49569f47b5b17394002844af4cf5c0e07da887313e632d190c60bf14020000000552ac526a51c4343b430184eef00100000000025165de02001803e701a105000000000000000000000000cc7851191685299b2e12e347bca8c63d13d87b39e8b5968502c476d7cc48225537dcb57f977f461f902dc85fca19058b1ffda01fa65fbc670a33dacdb0e3cb42619491e91cd2692228bff2a2cc9aa3e44ba57becc5e80300818fa717a49f9bc700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b04ce49e8f798fa95c54062014ea5e3400918558b8b33b300412bf06e1d81057fb37c84409d4ad2106a137885183751772324b43ac21770d94147babfb179f37cdfcb6aa4f46e32d2e90fb1cfbc82e5a763d95cb1984ea9a3705d42c692024059fa0965aed922081513fe8bd41fba1a53c342475770f246374b37a0f0605aa43030e2a3e88fa1d9f92a8d900a6b68a5d2bc28c526af3ccb6577c09a19543d8f4b0022d87a022c6370917ceaf50b68bb789c144e9af816c55efc0f390a119dd3d55430b081bb2e5b8491de7e32d710d78ab666c832e648494f4508f53828f7a80e259e40caf75a0e1f5cb9b0f2f641eb48a78eebf158bfcf7225754cc3aea7e0e7dba150221f386fc625c0f5dae87a3d036851eeb345f54875b317fbf57457e9fd64dc17c03140298a5790b0b5f3433a1194ea09b4f1f057f5b9de4855db8ab9463eb1fa3a403035ed03ea1c83d34e0401d888c761e8559f0b94540f68891989e51cc9061b04a030fb37d744a42db8db729f9dceb8a6ad5f82ba111b4a48f104f6f81270d6a1bef031f080e2a9bd45afee2d659238235793d3cf057e02c4a549421fa74aabfe5f756a9a9e59699f66ceaf08572f18076504b64ac61a918c7569c44f87d674299fe5059949a88427c2ebcd40ca5dc872c7d6dbe7d3245b9d557ac80379d540425792ac5b7b028f6ddd3a75387b4466d19301f47cac324a98edf256c0280b61bfcca5bef61346e5780c68587d1185fc7113411b9d0940bb496918657d9079e62787b68c10a4e70a0749ae002127551f66561d2478993c3af3636e2592f40e9bc28ddd8c9f4c286f41989ef47b10dc802e35c6edda5c41175d9d3ea983b3fefc96adf3f450603fc35ea8e62dc56683478b187085fae730de68624c804efe1ff61cb09ec04594adc082e7b62f372398478955cd192b806bf4f904a5e928cdcbd7c3e5e8d2f491b61128297ace9df756335ec4642b40360c16d8bf3e5de1afd7a1b7d70b3f751a116a1fde2c88357e75d42a78ee6417e9e12ede76417edefae8f10c4de7e17421fd567a9dd4b9cc06061c08f5c31acc720f94641dd4e5da381a80aeffcfffde767d98a9f107881a740803b37f1738a189e75e557edfc75a3048a3fd220fc0f2ce8f9d543d3825d2a8002657dedd553aa2178201d12076621ed6faae0cae624aea1ac8ec48738ffd4adabb1b8a3220494e0185292e82957dbf6e3f747add0f71bde0a1389e4c90c2448f79f2145979749811438ff016aba83e690b6996a16c9189559c1261d6bac4c35330cd914afd59b10a72c4995264340dcb1f05c1adad70900d201ea00bea3c268722806ed1e44a4df339d8e0289f1073e6d5d44a039b435b1b1c44c476ce7110a83ddbaa5d497aa3daacfb3f166bafbcf614e953779a7a8829f25a8baf61e5026dc231237d3e3d1e96769a99807b53251736685a4812d06c22630a1f2e2319045c2c1da10058873953d3ffe794297230087a4795b562773e273cc1974dd697e081040868495fae61dc4561e363618e951c3827850e09ef33fe0db807b48d16ed45ddacb85f1d7eae7bd5ed0ea8be873e0942a43846112b8982cfd9735675e40417874b0e6acf02ba87626c04701d8d5688ed49a95f2706a5ac0aaad1f3becb2f6f879ed322571b5e66e6c994d7051de55f1558e1f51b8ee75f375c257d35b0b5abbddeba5f2031d29086c87faedfb78a2d9095757feedb0d47224c88807b80588b16ea9a8c6ac0b72064b5c7a7cecf8e0498986ff0513e3b0adcbebe1c7b4afdb5501eab4dfdb4e453db862b2dc330e1fb4d0c8edbe0ad88c95a4009f9c625434a4f815ebfcd5dc687d80e05af535ad5cacf938a370a5551714786bdbec85a24464218a88ab47240fa0f2358928f3c8067fa16cc2c1bc3e403e7dfdd36090001dae6c87c3c1d6b1481e27289ce5225069331f0c03c45ac5f3d495dd767109e531888ecaabc4f03eca2aba5e7bba68f20c8daa3dd709e34e6f359a293da5506fe130fb50e5b7a1519efe4df459f7632f90bae4758c501c56a1f6b0f7f160c6a6550542e824197a987d7ffd0cc1ac1af8875e7ac9d7ba4f7341a58d7957b92407201695e2f280d850de6784eb8481513fd3e4e1fa0c181c0fbd480c2d83f589764664d9a9dbc71d11c4c680e111ee8d35527ed1695892883ff4ab9d3cb5d0de932ce7845bc8f3c3d3b573692d218d9c23322187e193e1c35d5ee61441716299a66c45ff1c9c0d35e75dac09fee285fa17a182b81b4effbfb29d668188a8ca4077822200cf1f9bf5f6ae9f7b020000000000000000000000008229220086948d05b9c51cc53851ce99ffcc9b93f8769c9e20a2a380b7db437e5696ddf4813df400f12d528e4f615ddb17974433819f072954c4ac383b0fd6510f35f7a30352cb9aa0efe6bfd34e0d29f27866746f7f5ef2aaaa25fe38dd435d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eb9540cb2c523ca23ffbea48233e5369cde1714e977aec1b6b0ac4fef21164076e2be0b8260062dcd4a4e1dc56aa9605aaee9ca0593e138f9e37d0449575deaaef70d56734abe12835d0878575f358d54c44b0149fcf8f48c56c022b116900f05302b950b4b70f1c33b7ce046506dc46f54086554358dbe8e5472a11c9589f9d021d4b885050c0d2e4e2a61a6853f3080c6845d75e53470f0c023827616931e172022a4b168d25ea1b4d27cd78f7e552a86acd7fe1dfccbf4fa223c71d51f5ef73250a074d547715f1d2489aba793db650870d19c0bfda598943c757eea9f4773b185bf5c575038b1f017817af120795b0c8110a89db9e7e7888a63daec6ec515d2d9f0223a36df293999f77a142c3dc761379422820a64bddd843849e63b56c5a5051f3030988a48a4090217f3acc34904b7ee89b781db9bed40bc1bc7278a8067551a67e03029b57da239364788da28bd6cf6022412505838b7a57887524d17d8b22e34e360301d5a38cf0a9797e6d30ae05a6e5ee4273b56598654f6617977b23f761d955e203249729835ae17ba365608621672eb52ab21c4cabddc6698877ff31778a0a7881fd3e9e7cb0b5a6155c0398c0340c7c02b82e18815d5d15089ad6f0d1d33cc54862624b4184a0106c48298c326e971c9be5204b0d301d3c8726b3ba44c9eda6415a6411337eb47a11ef546f9ac61b23f4d100ce6fde176f2c760795d3bbd99e8b4551ee08c0d9556bd061a3c17c1e2a62c8be7b2e4c50cd679a9c1e0bdae993afd73bdbfdc1daa54840f80ea3f77ed55c98992950770644e3702b597d7e95cccf771ce18d9acc9194de219aa7f3dbf10c863b5ba60bd0a670f962771760a0b08e9893368cabdc758a1125434a1fa0b3a43813fee10c7fe725feb032d155fa05ff8ed14a3822217a6222745c1329b8521859c21a8909e4e1d31002d6910095d24a90a26c01dbc13801dd4dbdf97dd8c659773685bec51b1bb90f1052f34c59e1560a6e54bd4336d573814dc413f32ddf8619458f0b6cc33565ba51b98e29a8343a9eab469c331199bc567aa0c93ce0ed4bbff952f922511b1739d39571820feb62d29ee1db24f86dbbd90d50b9c328c93fc4c3ab0849e7881841754a70c2baf171c3a760bb71c61014ca29385fef5d18a3a1d5791c9d44164f8af98189df3d29854da5de7e404419b84b288b3275608f37fb1951c81eb6857961afadf58812ae43b4bdba25332b4f0ee7b1a09cf7c05a722227692aa623e4a58332fe6105a7e137ddf375dde1d37d72aa8a295d8172695fc05385cb3e8e3cef3e0dc342723941fccdb4cc973337a5e3076d8b4aaf9388b96c9e473a3bce95d1dda83ee2e2dd2423f10f93cb771c75e23e01874d1cd809f75aa16535db4d5b7ac25f1d0f6b6ad80f8b607659bf87274e0f33e2af27c25a3658599f7ccc886c6de2850a97fc06e4faff37b29ec7b77239554ebffe9e12455bb1c20ce18ce06151705fc389206dff5a653944148d5b3fab22ba62a9a9051dfa42607edc9fc84a702f05878db2b985c34391d4bdd24587da42de31cd91483df2702cb006650a205577f0a69b9d65851be7642be1ba804ceea1ffb7bc94e85f185eff5290e5f494471fccb46c9963f7685f8fbd3309e2ef1617e498bf47224c3b0da8e26236cd0655513224199e942101ec7ca7fa47e8dcf293307507b40409899c39613a299997408e41e912024b9aa86ee1249d7dd7d472217c2a5c271ee01969a24e94894d5861e8d02592781e951d0437dfc1a18a2d6679d773fb794ea92fec1e37154f62314bd50b6315fcea262317a45f023a26480aea0602a8b16c11950ba53eacf4dc3d823c5512587ae61f96420685ff19b5538edabc51983b0093050f669b9b9db3052dd27887c5bec1a0b75b1ce7dadaf87eeede8e2f706a3e0c3405ce721e5819476b693809f075ab0afd5302be11cbff2ce06f5d433ada522b53d915a0450860f6d624a6e5e16ee2e710047b590360f167c4d8dbbfed5f828357900800b66cfee6792c67b53bb326a3cce1fa583127f0a776130d1a04f6093fc0c4b688f7d8dc4f95c9cd060a81b58c69f02278b0271b0e3810579904c9b5af2b87272d16fd8199bbfe8480143615fad301b176cf7fb0da8ce5c5e84d2fb134b0c9eaa18ee7c03ed7d40c1a8f21b532ebe74b0a60f99073b2fbd612c92fb1cf97a72e0453ddbe45b51a9bbc7a65b715fbe6520d7013c6407b9b82c313c65d46bbbb811eccb9569dd6d8232892c8d1cacb55874fcd8dffca466c903c95df4cc6926820000000000000000015a10d0000000000e4315bcaefb2576201e1132caca229c4ad07850f9527eb1ba9623a8ac402696bcf85291c54749a38dcda39dcfdf3724582627803c9cd5a04bce067017574854fc5cab424ff79ae1a76a23e561b5a7a26ec8bd19229f514825ec2423d7c6916fe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014280146da999313aacc9b9cdc9122731ca100822af7fc308c16866143c3f89b44d8d6c4a804b007f0e2a844b28c29a62e787cd246622dbedf4d1a9b79fb839711b67380727759f3bd3a75af156548e527774c727a5833f7e6466c8308a77269315c8371f91fed0677554a824342bbd3e1ac2c6a45a457361e6c560668cda5cc023023b2100d24e827bd1330b63a0e98fed36419ed487d316da42d82b2bbf00d1e0312d7a4b4d3431a4859f3d54d3ccad1b7901e17f2844b1f118d1779daf4bc960c0b025ad096688d09d3e2e8d57a8e1f614bd42a1a8ee454f1271a675074afadadb0ba65403f34c21cc56af48fe7dde2155b5194e4823cda6eac58f054fe8a752800022927f6f057db932ed9ea644fa5fb9636ce953726f5e130c1f1e7b4328d01ff250227cfe883d73eb83cb91764dd37b8df88de1861ef9dd955f36f056d2fd5ab2ebf020bbe1341bdfe2bb53d6317eb6050dcd4f03bc631a574b14e044fcf09c30df038022d9033d52ae9f0811feca7cf548b64af18f90c5a399449c987b96144d20e9b36030504e84a83514dcbc4cb034297d9c2af4de1958fe081440eb0034a29da31b283dcc23fad7a3308e71325ecf06da44e6a70b0dbb872c26760ec56c03b490c07b89210ae225d32ff7a0a0989c1c036f76280023455b60430bbfe90129ef8079e0b31a4453ad49b6dd6a3ca21277aa9e59fe87e43fd4d09ed44d8d1d16cb7d70a12d4b9a669410ca7bdc29a4acd1f8b9214f98519f4ddf57fd87339c1d00c567109159c35ac42d8f81d1708e665956eb11f980ecdf29cd8a4785d585dd681cec08521c2dd1529612ce9bd1cf03b5260175793dce905e6ec72006163f672d11cb260f1fb4921217c5efcd010c30b4d6618bf8a72266847d9b0e7b6747777d07a14029f8638996367ceaf68d8463f8929fb62288192486b0ffdbb7106a4ca50873197cbd9802394dcb225287c328d7b8a9a56460511fb92457aca190248c8667586b6cc4a49228150494b5682b8035efca9db5303bfa97d6845cc56f746b9280d6dd5e86564aec6e2da2a4435b684d46669ca84b414c51a06e0a958608d74d7eaa96e1e1cdb5f556bef1d95e0e5c490cfed072e11be995b924406731759108118178b9a4b64dad3574844ac4acf9ef13aa8cbd8b0cb52b6aa8d3f46e2619345a30209d8ca74034702f51bd84493ad675de83779b460ae6021dc55b1ec0d0258dac237229fcd6473da120009c4df9d7a0e54450bf8d8c5ab70905e81e6eefc5259ed02a838b1b4bd1a7be1dc9df88034280f4f0573f1da715387aabff7faa866c4b9c6ac59972fc55b7fd5528932629193623a7f9832389b47693716d003fb858ac834405996c7ad24a8a942cf5ff209ff84a1440c853ba30215aec099f5b6ceb7b69d66d52c6b793f5f5dc88351944b3522aa05fff8db2dde939244fcb4189867bf092cbe5f0139b810291aa60e7cce7bc28ef08e4514998d7f1c20efc62f43d27ca885a647daf9398754786ae2d9b89434a976befc7a533d19a1203dea587b679a93485c0d8fefde0bc2752c762101bc1da9803b10efab28e017da4860eebd76a655a9d028e1523b128593785ae3db9f25545b390a2d6bbc6ab4b5a3d43ad3aa9be14421bbd0bcbca8f9ed723df357c55a0bcacd1c6270a64b0bf330eafdf4600dbd9aedfedd20033cc481a6abc62163f308c19dc4cff204130e808011d9a0d2f448188e1af724a77c910cc001e46294ac20fb3a4ca175d3dbff19bfd53b8982c80cb6dfe1dbea0e53d20ee7be41cd8967e23af12aa8d41e3029ffd759c9e1dd30ebc65cfadf46a57cdb1e6697873eedb5a5e56de937f408667836859baa7d30eae4c55c3e1796ce3eddc0db898c889e778bcc0f162de4a33069a3573d3bde643d9efa82b21089280d0d0efb22c4ce56241692bab564bed233bd2edd114381dc0bf900082307ee4b92c2dd3eac694887de6c0e18a75030f8935d19cb686c98a662d546955b6ba3b77b268624f67aa5582051103e7e511b8ecf38c24686a2520bee410baf7e4b23c33f885088809bd62112b4102139d3aa8aa86c7c87b70351ed530abd6606e1e269e250fb074dcd2798340151c1f934fb50dae33896b487aaeca2bd8651e4a02e0b373df523a10af5cbf0891ac4a16b36bb96ff191d3aa756798d34bf734c5e3fa13285b7574b3ba2a50710206b28e36b872669f3c85bb5c77355222927ee18b5f5b1e3c6fca8b65e3478dff44325d6d0f7819ab4b6e36279667039c4dc7ed01014106f572940aca0855877744b9390a99f7554b5d688792b36b49a64c2e999b6ca27e084dc40edf753ab13a119b2d02780f0db7b7887c0ace17e44a2a6cdf7ec2dc787bcb95ebc982e9bc4c5ac0f0215b43a5b50c4c60de0982293408e3aaa7ccba9509bcf4e40d47358386f02", "52526565ac", 1, -49015535, 1537743641, "5c221ad10230cbf034ab829d18c7cd45af9d6be5b0a14e8674a9c9aff3f3605c"], + ["030000807082c40301ff976aed71d3495c51c0a057ca9d66456810f10ea2af896730f1b935f702e177030000000153ffffffff027f536501000000000451ac536539f8f5010000000002005200000000000000000300000000000000008768320200000000969f937bc429a593aedc0018d05540177f3a2fd55dee2ef801d5c6c7fc1edeea922d1f956585f666a917a6a698f38e04f637cb55387ed3344cc86945bc7148ea2e42788e553c81d7f2e6b8f48af04364a8d326dc836a47620aad463193ab16ca00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cab1008c7ada49dfb5236ba732b4654a0a9049d1b8933ea658784b6076f902c33a68899bba005e843c0cac6e737317e3ca5d3f50a9f33607606fab8b9212285a7f2e57593842874b089ddf611d9cf39b098bfab413d3fd12777836a3bc871a8c16ba5f632f283c582d3f2cde5182b2de802b732093b3feb181b0466d0619d69e03027753259f3e3505f1e216d781e9245066125397a67f359ba38acc24f69494260226f9f414272be73ab593d554f02968913a3d884e8cf7c0663255695118b61c520a00fd7bd7d2e39c0185a0e6d677dae926c7237b94d98efb72652a979d8d6533ed0c437c2112f46ec5373b9010b8abddb2607e00961708d7b03a4a1847f985aab2020ddf06908abfef4e77e59f64d25c15dfbac438646dde1596b1ca84500a79203c020e22a6cf8c9022be54efb8162c35f45439c0ddb530dad05838168cefa7b7b2cb0313d234e044c3b577377039eb86f815cdc0c931e2968c500b87e741d39dc67111031ab7527259845d9d5991d93c7ea52dc82ff7a21b6f524b551590c62790dfcb6f031f9ad53f2f74156eda949fc43b52bea87cd03eec6d3c1fdd4354b3bad530ef9425e8ac1bb9c298a949d41cb3997ce15db977c78c99f17d9a6c96774bde8ab65710429cec5043963931b4bca5cf3d401183dfdd9df886fd04d14bdcdc440039aa832428da4449d3d7f00fbb80b98a6ab3be34758d871c01dddb2c585d52e79e9e3de3f9caf932a84760407ffad4884f05eef14881990d76a4abcaa783fd9c0f7328437a771810bdfe92b2a7c21a28d0023978d660ce66e9b1fb687c74eda0d2a1b6615a5b581faa951f29f1d3825ab9addffcb412e3a9e0b8918d6efd2560e6f5b109a6b14b9b3428fb556c053582b769363ce5e5794483578c406a79273e2051e20552d3372904761d9c5414ea31d6daf3ebdda6772a3e7f58624695627f6842d300314fcd078a86fba10f18205661c43ef8be8d61f703c1f76d4991c828e528eaf4fe7c9ed4c9fffc6c6b0fbcacb175e099b0da24dcf7a6f7812fa56cd8b28192de8ffabe71963cf94ad976f8d05a6e7dc83994874e053d8a01a36d34fbea147df96111bd0e841d1d092509c66aa7bbf0513bae3ea1a0c09628ebdb6ee990c27b13ac65fd1f59444bd346a9c20bc6da22c0921a3305df57b4645e5a289391bfa90bfe878fca3feefa02d11b430cd9a64e8a9e1c6698fd86dc7b5b14a1fde53bfed458c3ff747d861f8beb1b57a005789b8cb3f8ed401543a55cca9c8cba549799b63cb28f0fe5e10d078bda03ad06b8a00ab164448d6632d24a38bef4f056a9a6254edd7ba40599288545029aea887ded62c212da76a26085eccbf93cb28e366d5f762e0ce5f5886eba9fc4b1edf23630aae83b33eea6b7c35b92b1bc91d02a93bf58939c16f3c12956ed205372ddef6ae738a914daa18f4af987210ace632a8d80cd6daa8ea72077388f55f57f3318e02ae3a709c794d570bd3413afefbe21cdb993558b27e616a1504f33ce2d2d72449773b29b94a29e155521ebdf491d17a9bfaef11ad83c013b83c81faeaa225e9a24e17f24517590bd457d3c75a2a6c3b9cff3b0549fac338077507292e73a7973450b8682de9968dbee28384b3daebd9df399ecf478adb1d028111b2189ae7e138cd846f6e72812c9614df66d8961cd74c3569822e953ee27b3f9a53d33d39df521eb432310588a217f1bf40931d8f9707b3adf107882a081a916f9d73fe96f9f767af5424b5da72cdb5b5fdae345e2ac761133281ebbd31fda0fae6e79bf41bf3045a392581c4e066270def35172d9871cf957a2cb773eb234491d27ccb27863f917d3b678b56652608cddacd1fc477df21f7bdc96d055ea8085423034004c47d740d527b0a4bfb93bbecc8d03a3bf16fc86d51584ddca45858514c7fce16efff2b3db5f6ac4cc87f33ec4f85b5dc125c604ce59497d35aff9db57e49c7b9a80fd9e286f43e3b0af4d6f285300e0411b45d200ddf59071041d5be51d721d40b4e9ae0b9f5879b3317defaa1aea67409b2d502b7279375eface4cbbeb2065006547019cec9e65bd24420898a2abd780893cc812a9799019e8b78705a5f47ba047c1e5284bdffc18e5d173709b6e90c30d3cd0eb3f6c6a12b5bdf6f0591818a1082a855331043314ef78e07c8747caf2c5e0192d131f8340c85d4e10adfd388082f88e3bf09873e615289ab2cae8a954dc0cd09ce89c3b8394b99c743ee44e0f02e6693d70c96441d36cb1939597291aae964a3ff329513dbde60b3d55f67cd5cc360000000000000000091e4e00000000004d93e6aeceb38d1bfb1c163219673de0032295eace9fdcd039b22225318334cecd5625208d3e6fd26240343854d7d4ef8334bd9dd23b8b858c2cdf3b4815c404979c37caa2af731a87af4be7c7eaa4f3ca5301c9b4ea79200c5841ed16ce0a40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009741e9a8f53cc722b8e8cac5437fa7d3b92d4c3ab1fd3da939541bd58942f4be8f598ca2c23e55140f78dab9361336280100f66ed83fd75d709b4d3393b5ceced4d00cfcdae81065fb79762a54c37264a465961ce5b57177470fd83b3732645f42c7a622a4a4902e264fe85a1020974f0c1eae5875f4ca50311605a49acf04cb030c4dbd2a58eec9d39c2caa2785cecd5bb9290e6f4e08e91613bcec56b63759b903080b74e2701a8c6ecc6393140e6183d71a82a45b3d94bf7292008442e301e0090b00ff9a4e9c11dd044e861f0aeaf04c96430ffd46c3cbd03afd2c53865e3b698b5605cf9d42562052df28ca99fa2a4ce5ad6bcfbdce973779e30548936dfb732b03072ae6f9b78727c9aff756d1ed9f6fe01a548ce0c691ac2bb432406d694a7e4c0205c2d6a168b77bddf35b00c002e2837b850f74d4f27f4b601d0a59fe5dfc4444021fc579c241ef29857670b0021c2d5519bc219c27205aafe5ff991716e44d5d81021a3ad2a1045cf617870eb1371099655a1f285293546c430a95734bc52770c8670319d2a178f4a1e77f5570a20d26c043be54e83b2dbbcfd3e45662676e5c778ee59dae67ab9f1fa535c0b5612c79827d722140eefe30b90a01c958ebf420338888368855c789c1ec9cf30ea597b04e44be4f1420acb71d7d88195d5346245959559e5c1f0ee60fa3d5082407dd79c13e63f2ee905db4cfc6a2e6704a0a88c114cb0cf704b99725b2aa1a76a43e4b8753ab4fcf9ae346537ec682867767411bcaa1460d5907581bd45a42960546beb5da5f90fc2e5dd693462d3f5ca5de47f8c4a3718b2b7a79e9027e81d9f71b3be78242a926aee9fe5beffdb28f6cd6d7196b6b9193233de1d52d9cae4983a975e1be64a61629d7e88e44913863b4ef5e5cdbe4bde1c8c667cef988b9f89c8c4aa46f6112d9bb61e9061c557604b7f9ced370be9e10d8c96c08f9fd95cf0fb2bc4b4bef9cb08cd987b89caa7902f5881891a5fbfadc8bfe631e9d492e0e5ddc63c14f64bf1aa3fcd9c989f050b8eece27e4cc6b580f48b29ad8f1cda5cc825dec3d7b2c308c55ffb09b5f7c75706daa03004e195cc6c3d5e2c2c44362f4ea5dd314b0e0a3bcc9b88e6eea6da20c13b25a9d8971db2018ce60d8b2fcf14c2b5321b71f91d49c2db6866da54ead528ae4f8e98f55aba0ce6e284ca2b77e7c91bd9771782abda8a3f5c6b9bf23e6d0f08fd134f1410953ec42cde8a88a87e4ca48728e602ff268758df18b2806875a7fb90b64657c5a7efa41c0ad57c9de2085cd0dbb40b2ced74c682a3b13664393704150e371a9f0d25dd0a52321045a032ceb46c5449bf829f877ecafa620644b852e5491b6415c4c9f840bf878f98bc7a6578d2ef04c41340e5aee2d4e81ade6653c9bfd1839f3b48ed5e2a59f3a46971b40e082102411d223682b7f1571daf1fed6be2d23e496f9cc22c8267457902161de3dcb319bc8d1b7f82dbb7612a5903b9f46204e18f1891e1a5630e9a2ea8a5c63ba128ebf284b0b1e868a48011fd468454e072398f6aead291fc405810653f0b7411b07e5dc9723d4cf6faffb6ddbcc223a9dfb3796235c859107548639cbc3fedbb57ad0f37d5756749e3276753e123c1cbee81b20f0c1d4ceebc20515105e89c77893ff52bbfe760cc91af12c8d3d502f56984e52ec809b3e54aed89fc07dc43a57034a79cd97fc8b70f693fd911cc9a2486c72f35c44d72e021067f02e3fe46cd3be56b1af348ed885e32302f0294815321ecc0a61c104217201e18cb95fa2442f41895dd4195e5ddd6f2a04071066b59f41fe77e201954d70df99e997c7e60fea32a8874a9212c3ad66c1dfbab980049fc751d8260983e19fd5cc1ac871053a18048b6dd1dd481b7baf252acdb14df1a4a6b0e3064ce1ef2dd653804d539c12468d59e3384cbc32b6f72e9465798a9a3601be3b9ecd8778a7efb23d6be67f8c5ba11be7dad069750e2fe1d912a89687ef6e8c17682a72e7eb807b7744277cb2f677d85eed9d6ae67e12bfba4edb6a0a5a32146031437ab918bd0c8d2fb8b1acfbac1ad47588dc528bbcf72102c056b45b492750c5d1fc6bb87006e1643d1b0d444456c18cc136efb8e9b10b32082cb13f29ea7e54396f2203fef0aceb81cf63b877604dadd9c22ae7680be0d34e173e20a74062a9555de198b2fee7e107061b1ae68bd14af2467f1ca052827b5cacd17152e26a3846c7b2a076adb2e602a56435912f1b8ffb6009d5221734c4019d6a82a8f333b0a7d3d295c68e5a34ac7f2f39581b868800000000000000007afe3f030000000008672ba22142e5f312befb1500be585347fa4b55c8faaab08b1da7b287e3c842d9a428e777a37aa6307a31cf7a49fa9f46f730712130d45032f12e1616bb915444562477b019bd55dbb00ea9cc8df3863592531954ce511a15a96608923f926a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae69eddcb35f964a4e9acf7367ad39703fd788a3fc7e6d48d5c59f26d0333288b17e6832b07e0b5c338b759bf549004b76d9c18ae87906b2ad6aecc259c821411c817b5f5618cb6544936827b826ddcef3dd290b49839dafa59316d4c5d870a411e44e615fd9641a3421fe40d1ba4ceba7b57a27a9b4a4f87f8ba18bbd7eea540200e68a909192cfda5f3cf4b8240c4ab8b29d7aa6417d3c3fec12d9a85763fe2a031947552dff1b2a72a4f457f98ff62c8fa7280cf2e17776fa9aff33cac0ed20da0a06ffa7ae02675333cd0bdf07de8ea8ea58e8df927d171b412f4f5a591f76275bd812b5a89e5de5084dabbb34ddca1858c461c0d211ac244bca4ca3a25c407432032c4c32e68fc7e6a54a2607cb370d3fa19d98a4695dbfb57fbe1b711ee50ab6d8031636f95237096db14b46732383e1f2f78d04cf60f8f026f6fbeb6399f031daec032b2a02b9373300e73fbaf4492798cf7e06ac8c3c604b0fd51c276f9a8a5d8e5403252221f3db084f29730f0c313ace90668208fe9c88c068a321ff2ad1379ccf0302248fcc601ecd73a8538bb519ec85312cefca18d26aa6a128bdfc32d06edf100783c30e9ff36d086b304ac500dddefb7777ca22d31a3637ba240644d9e8b83d1d35cfcebc81b1069610457f18247e18bdb33d13cb79ad1e72f258f54c0867e70b154f2c187d2d911cd2790fe05b6d7df1f8bbc55179c8adaddd1c38057a7f7be08a0b5e3ebedc794b4ed1df900600f77dd0e9d5c48600148cefc0863d4d6165a494e7dcb8f0986d793cf318a8b88b1ec27cc82e4dc48c79ea5568a2d565aa7753ef1b95b552042febe0cccf26bd97d3c2acd4a817662836408418eef8bbbc6adbf4b88981df82eb41a00bad733277431a7f5b83a391a6fea54c5e8b09d768b70a0e8774aea2dd86e96af1cd0f2829e046487c492d7bd1b34087e20b3e3fed709dba0092a67d77768524ba5e0763010d2428107124f80dda6688a06ff1a97618751670609dcf23a954ab3b47ed32bef10915ddc03f7ae248a316bb43a89ddd278fcf356f8158d500b0cff9b748e29f2d0d970bf4f6e74eebfc4a9d95f4bd127c25ba01dd0595718c4fd7cc33390c967cefd9d75744428d7076d590240839dd96e73401b5bdf02a61be61261a64bdbfc08af77fc51ef86d64f99985c7d6457f09f93d2a641e3ad89075f6e4fcdb444494fc179a0c793244a1ab8815e38c63d9adae29e2cfab2720ab28dae70f124719bc561b95e1f3b570a2ca8d1c5b702444841fac8d6219a98bef3c95ec38d40848b64674dd0ce5ecf64aa5b310dd863b4e129703c0047fc078e60fd6e7f9b86d57aa4831b6ea12ce259214f461741d4553a6b8e144aba436642045c6321002331fd70e4ed7f2a0fbb7600b77ab9d1b3276cc0f5b5c469d5211a7ced0525d6630883ac0c534660507964f93baa4e49f31fbdcd861fbea3cb6da63b229e6c3162e004f3b4d989d504d3ab30ae28cb98bacbd995361bc874c0698b060d5fbd18cd069f1eab2a1944c8d4d8a136c6231fa719593b6973eefc98d1c796989a4a0e1b87e3e2f3f733a90e093aa0a19fa4957ef649a4e31a408994205d7697018d9c7bab53cfce6b233862f178c32c832ca2bc5450d138516287664e5c0fb3f438a50b09bd839fc68ae31a9c50c81618458daa5f5df2f30034c970a9ec1e5d7c50fdb7fa4a202300fe4dfcb5601020c55a1aefba1e129accd5e8769a7deda9530c215e6862a753008e051ba73228285637f7de4c7590da12ce31510dce82b2fe531a0e05611df1ffb01425e5d50307944d68f690c8ee04d9c3f64e2cbf3bfb91e88f39c7face1d49f1f85718d44f9caa30c4cd296f0fe252f4b9592e931323a73b6d09dc8be95465c8877352a564538df9c25242801fa0969af75e680b2cfc78d8d1bde6d14723e2cdc7dff569ae3c860da25a8f6558217bde45d46ddb374b8c6e54e3832c0d35606b4e49bbedc95a2e1b2907348fbe5639295e818cc34126a14c1f583cae31e738a8d0be17d9fe4730337aff200d3b8c62cc852c73f05ce377b36e2f753784a55d32de784ad6d1c446af4fe073b0c62d9f64b4c5a576a98e4140427d82a07301402b01aa20fd226b3f6272e332de5d012d58af5d44752e78c1a83c5a9c6471f29d3a06fd14282f433d2ef9bf007f9bd25cf96fba40ef853eba8844836092cbb9cb9ececf297935cdbc144cebefe2e643cf36c9c7ef8ad0220719dd6676404a0cafaf1cafbe3790624d80a3559632008b161cbc3058e67ffba9e70469a7a64383f14df055e09503518096c37681a0930becedf8e6b16351caf682bdd84f007527f6e104b549b0d41efe7a987ab25ffcb271502d01f38e0f42cfe0f275822115f2283a2aef1d43e020b64ef7e8b3673510ee2377a0d8c291905f45951bfb8e5cb220e", "51ac63536a6aac63", 0, 815141338, 0, "ca1395e9b626f7b0338fb35b47e84bfd3d3913057a5e2d0f696ec858954b48d4"], + ["030000807082c4030214170f63471229151b95d3887e0d7d223a85504ddccb3035b1f19010dacd8698020000000263acffffffff1b3db6276da4af2dbfee7a4f492cc9f63a6045dc4b4687bc1f088ae9d1027e8601000000055200525300ffffffff01cf3098000000000000000000000000000000", "ac65", 0, -1879166880, 1537743641, "1ef5628999397c261f928ef89e4cfe33e58af284400efc23c59bb687e3afcade"], + ["030000807082c4030135390c671d2b33aaae7d934b6c9f61ce0c48feb7a694935669daba5eef22f6ac02000000096565520065ac0052acffffffff04e1e61201000000000852ac636563635352f5a9bf0300000000046565636533cd8a040000000005005251526a9ce706000000000004535152ac0000000080e946e50246039b010000000000000000000000009e66a4a2573f75af8ff73640d385286c1eb2e348dbcdfd789a40cfc5d9c5df5a3c0df77b2fa5c4fc42849f4926e7a569fc045696f285536f82807ba3be017e94c4f49da2397f55ec5cb77790f3dba7e3d2a0468fe607d8421e88821e64dd0f6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2b4e111ab0663e377b0f8d9426f4b4ec29a45868137553ed1f967cb8530270c5eae7defbb661e76c5aeb450dad2ec49776a195f9d1a5a3c425e7b16c61c3be90bf1d37af8537b6d67dafa78c91d5f01d4deb3ce0d2e38bfecfc514c335d9f4053901f5c9a419cedea25c6e18000fc752f13fd1f78a5a2690b51c174acfbe0fa0315768a1253f5195de60446f4c7b5cff34f40af0cf2b34ea821a765a47837cff3032de7f73e0ffe8b14c47c1bdbb056f1eb5a8dbe2a256a30c26b5d8558a2a17e780a0045fe004547b80a8908ed7ec3d07a6a0cc0a3db3675b83b45b3f092d060300993791e360be30320d330ba4892fed04deda84583edaea5180bf9c8b23ab07ac7021fe7698b8ef16ee4d74f7c6e626000486047487a6a3dc38fac6c32ebc3afc7b5020d1ca69392c8c5cce07816f28fd514be1be381a24cc0b20dface6fc1ede017050323fb5591a911f9d65c910d36c9a48a7d1be91523088c494b23bb9a38eff916250323b85e4093f6ee5c0af9a62395b85a7ddab79463851705b5adb077a2debeb05903143bf461fb0c3ff9f9157c6c37855e3bc37b59d9eee465ae3416a9075cbfb79e703a6b173db8980f37de65e73218cfc52528d1dd6e65809fcd16f0d9f691b12eb9e3777f0f78843afadafc6c08e0e65674dd3fca55fb401e871c04ed9c3912547c3da41882785c416ea7b9894b7cb71b5d2bc1a51dab7bc54533e756e14e97ccf58535f5695cdbe925bef9bf128ca844aabd1656e3b481c76f9c31ecbc41c7c36b2a6af815a0f829627493fcbd9dac06902a951c92ac22edec40da899d0233aaadde38daea8aeb70caee7e8d79c641d9557a7810b93fc6c913772ff8ae15e4b0da4ab1ebe628b76d5491a50926cfcaf3166ce367efd0aa7935e7ceee56f73d8d1fd4994fc384b2a7a3dcc66c9ae9579eb17f481ff86df8eaef21a90e59fc366ff7925b1d6a12930a025c537c358400f256e0da3d6f1ec065db12101ec3f0c7dc9b1ee62af923f87ff14201ba315f3bb221c96a8cb79db0dc6aba3857fc0cf65e4eea7de1faa170e29c23fd7b259e18e1b18729e5d6826c71d14098c0ef61d7930451f798a65a7c807edf36271642f27dd11dfa90d4b25799c3df7ea04ed339fe149c32460706737bc0bccff0450b5069c637479db72eeae59dd1f952da892cec3f1e5091df94951bc0264b07470415183ed77d0f299c8b7363019e4365cc06275f5beb54a765dfc0429e0a5c21f80daff3e020b6761cd30a0abeb82ef4c64c77862569556c83228c46600b86da6beab824072663fbd053065015de8a952a943e76872af39b0a2a29174a99519629b3396c034241db8d95fcea6339527d878c4f7bfeaaf205366a9bdb04db66972138bfd59ff005b7b47a78c18729a29fcf529614d171fbda665ff5fcdee0e57b879174f59c5089115a40b409098a3572a90abe46a96dda8f02024daf60516d28163ea081f9477a85215b1da9d360718debeb46e93ec6752ffc2e055ebf53188b88cd5201284092cfa99bed33355094c415a788231491c56624b3879e939eba33e8efbcf76fac603f9e964bd0690ea975bc0e4a66a7e11229e3b258f1d0c8dc0d01961f2826fc476d5f67f0ab830040285b2d65be8688803c2ddc4a2558d6f471111a96f246e39ba1f3419447902b2829e6bab2d475a9b5fefabd5cb5173e7c556e8629afbe67ed730b5a2df220d1d439ffc597cdf45ece500a718894562754abc8e78253f513d8b5324ec0d7d3b15fad45dfd1f15cceac2e2f9e74fdd5b2d9f274d1bc8aaeb51b6870bb1d2b2dddaff68cfa5a69ead06e0f4e555f637f1bc455fb07d30584c510b558ab02d6e378b975364df6378de3435fa68a0ea060e7ce7f04ca196f6826f303deded6df975f557a6c8246759a08d1801ffa689a764bdf93ad5c8aedb28c3db061248a39f716c482a0ce65b59becbeb29d5811f5ec0bfb01373f7fdbb9476b35819a45243af3afd70c2e00b98030801a47e32b7ae323c51cdba3a3cccf4862208e6db0d6ab22a7ea4305f03a6fa3ee503891f0e71fdb05a550c741036d0415e787c0fa504b26cf879fc34a20970af6019b2babc4183e24d2dc4e94b21a9288ccec66dbf4fefbb4cf0fa9dbe38f1ac709780b7a898a35d24a84fc6f9339b6c288143a75583edcf0d63ec960f221ef20c29b5f0975a3516d82688ac935628879d8ff09a4a5853e215bf3592bf31502f0484a7a0bc099b1992e8323446c9c55947e388c089e29ce869e60687063db96e8ef4f5ab62a87c0f1e23e9c895af84f8bde030000000000000000000000007ffe7eb5ca0ff6f12b3516c2c936a094d162cfff038528a3fda1be0232243d2902c54de227109d44a24e6d5a627903d1eae7d3f0a1c6e258dc97cebe82d785bc044665a8e29d40737a7c4a11f1503938262ee72f47618f0b395730c1959575d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f82b727b69ca49996c878ad489b2089bc476f0ab8deca2c373df58ae54da4f40c4a12939cd29cc8d59266c50fa0a9a04d1b83d993dd9ff2b511779b93cbb151e102b5ae27c67d9b295537752c5a721e6e34ff7dd161b88c3f755f568f0ed6b548f0c975693b1687abc049aa4c045066de501700cdec32bfdb7578bb3eec2543b021f5496d948259b3b44ccce4b9f469502e0d5eb911c1e5adbbf11f3329da33d2f021be86b1dd6b9da799ec5a45da1ed7774c68cab706c2b30b1c46bc1b01bce85ff0b020a3f16e13cbdb2d50b955cc6c4dd3e33d5365141e01924dd7ba19bd654f534a7810d60a434e5cbd0301343491a8b8c373f9993e2a1a6ac09fdda4c10d9e698031a60ca33f213f681dee0e779731cc5671626eed22b6ee64eee89b72039058a16031060fe9bfa9b7f6a16baa0fdd5e780f06ceac91cebf15466e0a41bf5a43716df030d7d0490f2e6fac5f5bc4cbd34c6a2922acb0e6b3220bc8cf0bae2aa973babeb0228194be845e4799ca180cca3efda7b8aa7644d97dc011ebbf97751aa3b41cd9b0223e4d8a1b334b957b0ef330f70d062f518ae38f07f85ed9bf003fdb7eedeb732257ea25599fc3161e0c40cce024ab82345f4ecc02b86a1d41d62cdef3b9be7300ccfc80fbd74a796687d6f26e10bf57a746fddb84661a6a6707e0581c512c08df7f940e11fe7c4fe472b5f9a310b08e68b237aef3beabc9b020d943a9047927a969e42e2f7cb26cbe6e2a8301ef81aa4635ea45fdf02ceef2bdb821f906e45f10105ed1094c02d33206fa64040e7cfca32ca65b049ede3ab4b57b82d666bdd635468d2c199b8fea502e42a7345c9b5d6ffb3b7300ff0ad1eb8283410c704adf9aa3a5b03f4f33935e58df0590cb26303e9d2b2872e41862f87da3fe7a10ac12a0a3b269838db384d0a82691c3aae93c992e35ef0e58b2282adf5e5d8904e40d60f4b203172a19f0af9df5cf6c8180c9484104a4d81cb8b535db5888ec69d483d793f42ac79bfff937b7e90dc518fe7302481114f38642f7fb35ef365b2391fef1e8a556dc2374f0488d8cd3190c534b73e15fc51b355b648d5919b3b2befe568ce9aa9f19f931e29c50e281ffafea4ee0b7ac7f865ff6ecb909f8833c759c9dd6a25093a99dd22b09f95d846bcf678d6ba6152a6432f84bd83940e1dc468ea80e243bf0eff8dac9fec10580d1f1c1422d6971057596b25401ef2887d4d0c5d22d457b72478a4befe7b5b4ca443d49c31e0258bc0d12497b27369da946a0d90731e1f0b10dd64b72ad954b01c16d825891edcf16ea7344d51b552e96ac6d0415e40a58eb2fba3c2bc5f4034c68ab2a0a195206095ed2bf3bed93b6675771bbaf58571ce2d8ebcc00674ef5ee20c81e543597ec9bb1ae2fc79a7bbf05a34c9de93d645a192e634e977ede659dafe561d7e6067b0bf9c4c4bfc096537a9dd0eb0d92161401c093d638e890f5de6ddf678472ada430eff845fa98ccfad6d2bfc2fa17613c2155ff891020101a906b9e775144610f7640ffcb543d893f4230fdadf8c496786e8281bbae67bc8d52f55dc08b3f323f207153138fd3da1dc0fe8cce3dd9db5d41c2fc5751541de37e391187ba9d71054e8ffd641aa7f8378a9fa037d41ce66deb431bb9d257877de7ecf7373834df0efb507b22dbb3314e528e2ff399606c9f091e4de80f02732c293db857a9e52265af7f613016beeda1c2407922e456e04afd3d1a5f4081a3d2cc879ac5fafe0e3c873da3912f3c729fa655c1c28ef0a40be533d9f21991e1e43c0447301ef4933a21e50a382fe82b768d708e6a8119bd220106f2632639e508e5636cbaa48959edfbd83fc8422be9e682d614d6b0c854be9cd292ed49235cccbaba038f8c21c90a20f8518b12790be1a7497a6fa4762b0f50b27311ee5821b0c15c0ace3d8d4b767129814bbece0301f7180f85fba2a82266edd260db35749d35b6a5155e9fd2bd5da945d3e8121e39ffb7836469a921e93859b1c19bd045fa84e8fd9720d7afce7b75ff6604df5d4edc4b9c300bb0a4af24f721d1232535c1fd63cb25239bf723d5d205c044728ff4b8c4d4e79fc239c86ea05f349a2bbec78dc8cd53c1b51a9b82de7761246c0f721052e6eb4ab79a31dad88be6541f05a61ea9154ca2a0475cdc8c9f4800fbc9679eafde01c4bdea8a4d9cb7f7ee8c25a3c7bdc36a71bb5da436ef65cefa97aa18a6d2cc24c36c52b3fbdb2eac62aa8c86f6b13f8b897b688b9dfbacd918ee3c5148490e6383f2a685ba07f240353d35b6f7ec1eee2e213949f0a0fb91022c42ae0eff11c2a7cd576d09b3d85c9c16894f19103b729c42629d40790ec0c8cf76c3ea466fa446c64c9cd7a07ec60393e197731e0398633ceb37a9d39bd82e2fb904156d6773743203a7cbf37be74235009d3f0e0ff2a8b080d", "635263", 0, -1524549655, 0, "a3b8c6621791eb6e3f09c41ebad81ff20c38ef2579378e38b2ce2679ff97553e"], + ["030000807082c40302d7ceebc9ab6928f5675587737819cc7702bbc54d16796156796e0ba332e88ac101000000026a5160f9b9074bc18c703895b62005164fcdaf14f2a155e0eb1f944f0cc3efc3a13563565c3b0100000009536a000052516a006affffffff0425b5e5010000000005ac0051006ab9c8e802000000000565acac63008a2c7d05000000000300ac65571e6b0200000000086363536a63516352000000000000000001a65468020000000000000000000000007c240750da4238896354cbecdd1e515db395d203fa8d26272bd98b88fff2a8586555e8ae694459376290e9ebeeedee94a95cc239e2041bd4981cd69aeb1618d04ba253e585b8f41e4fe5a38df7f58053cba1cc6a60a975b7629474641b23b92d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fb55d68c4e84f5025a603b36796d64a1910e8170e9e359383579c07a3b80f6fd2a53aa167b9bf3b2a0a847130b77793653b8e33289ba845d9d69cf8b3d654ee7053b9086dc165875e3dd1a74b9183aa01e53f7d96a25350bd91c8a5713f2ad1ed06ce21487ada8948e0358486d41b286e6c5b22cbd9bcc7d4455b632ef36da7b022e77d841f31391b03cdab6f968cd3fe0175ed2cd44f1e99743239b34d3f59cba0317272e85fa6605bb550f1bf15d3c72c0d2c8b6145d530beca231ddc7315367aa0a04b42bdf52ccfb6bef373107ceb44a327edbdbe6562b4f229486ebb76f1f7b64ea4c0ebb1429a8a61b46bff28118bd22de4023124dfc00300e861ff46daf0766032b24266e29e1b0d6011eb357d21e6ad129266e6e1e2c169bb54d6db8ca6e77be022649b32cac081541c925d057a98d727baeddfeb23ac0d9a6a47e4506af131d5a02136b30695b82864985618a271ee5c21f174837e714b3d617f52c3e19c42af85c0225eaafe32437916ac56b5fc06b1b26e546665f8d9b4f6cb13ec5741ffeafc2e60204312999fbee40cd827598777e7c346b9d7a44bdb9f43b2644a81058cfe31914607eb3ce9f1bd281c02595a938a8408fe7899b76ae1f9561c56b218a5c9ee1af83afb7b5b7d65acfa4ac572ce0e250420f3d12fc2b22960e883bffe16ec417a5b8b82828e9a76b7f78b16c7253c2863d6b081434f37483b4285800d32984b78c0471f724f13aa8fa7e71d526c62dd0d790ec1f660ee04e31ef51e9465f11006789a69a351d30ce65b643c70ab21f6afdf1b418388ce125c80c0513fb34a19d1c4cac1af0240c5b8823798d90c78e390d9b03894d24d5165f2d9713e8e3e746157d27434113334422484c3757a4dfe81ed20cb9c9d8a830d8519ea782a28eba32c757d7bfac38fe6776aa0e70420844a0337011651f07704acaee6b35e087fa5295c4075a236cdb14272b3c952a31db624cc38c3199874621d49699a4de0f6650281e348b16fb536a8621be850a3c0b0f3a3bf57ee8722873a79043a0698d590ab95e742e691b0198fc2055401063c07828883e3ce7b671fe818d83d66286f65d671e449a9c9ccc7ee80a7876ee2df4d175d54ea15e4d2a5e8299d6a72bf7b65eb42bfdf80014bf5d007a8c312ca97528ffb9951ded3d74b7db5e5e8f045df5939e815f81bfd21baa1ae64568e1415285dc7dd05cdb96b3d42242b10b4c751636bbc22f12d1b13c545f3462bc92fccd66187f0b42635be4a9fce458e842a53c50c6ffa2e8b4d759cc385521aeef7ed30edbc5c95f73db64cdc90fd490e7ce3e54e1c99a1a346e3d48df7accfcd6632e81c49bae969cd61b7a1ad82ee74ccac0f6574810bc821d389d3ce902250fc40b65b7d20bd2a66af751bbd15d70aa1692a06f55471ca5ff5b754398ab9f2e9de8c84551081e7d9c7377391ac168247409a1f80193b25e6ace99c753d508e2d9475898bb942e9338d5d64e0ed9bb6dcc7af74b6c48b702b33cbc9dd67b902ad93a5fb4dd40f90fb0254c8939270bd9ded3ff4db1e904f3724b69314a69f6b7e76a822ac9c072080efdf402d849a4a7ffe032371d4d81342ad972f731d714eec006003c987fd2196eea3597a6f20be3a60cc651c19ed1f2404b25ac5d6212fcfd9911d11023436c7e6c32fa2352816c154abc90b9c4e78559aa6032174d4240f5334389e0d7e394e48d68551505b7cb1ce1f83af0a425817c29fe9f10c9fb9d8fc592349e0219cfe2e1aa5cdf523cf38aca5a3e574332eec7ef5de9e1fc92742813958d58d80de4cb2c055ac95f48168f10ec74d36d90c1a10214073e86a00dda48a821c353e24310c6dccf365179e3687a7bb5cdd85d8a2a14b2fc9c79942e15cffebe1dc7c1b223f18608dcaad656519f644521d2912a718225fd4259fe7a211a4d6c67277a7cbaa8c734691466fff60e285fe5918ac705bbeea33a5d0f58c0954572a478cf7deb7afc15750f71fd8c55d886c81627c18d8bc9443cbd12b75e76315cecb75f879b3e4435c92b28574c89cb44e2d73254894cf6cfb86c9f974682dce93942c1963e166b82918eb69277f2f523fe3ac246c57a11121d6593d89a9d955aae4b6ac1c2529c6621baba91fb55ce9bcb4f13277865a021ee68b01266382c2ad1e4f74400ebaac6191c14bb9ec0b9fc5744e03f24dea26d140e1147faa44a90393c8abb302c5215e56791e1001245f70a8179a515c41a2ce025abf237eeeed0c0578e00d00310791dbdce9f6aee8c87fd45d3d1053703518a4f369e7852a772a58b62e36bb84a34a62bbfee5957c9479a0dcc3b41e4242bcaab39d33790994a6fe96464d9d79bca9655dd407c93f51b3034f0434cef20798465a7f8724982788734af4469780bfdf4d6e8a1fb9ef732fa486414d054d5b4cb5fdc736e48c94a02", "6a6352630053", 1, 1278606574, 1537743641, "3cbeefa7b28cd4a85d30ae5de7b6471f72d69adcabe3555d7b0c1697327752b8"], + ["9499d2100300111a098c40c5133b89fac9a0ff1b3d2c69d942eb65c113ac0f92a6ba74bec10000000000eb74b07e0aadc89db0f3afb7e36a786db766b0e635796516ee022360a968c5daf8d9d328010000000353535133eb713febc662775a3f6c5f7eef3f357e7aed49cec8141bfb206e4415af348c848e16c50200000003acac51197d48f404b7f45901000000000852005253ac5265518908eb01000000000265acde2ba90500000000096363ac006500536500f57f94000000000001ac00000000018344dd0400000000000000000000000015e47d88bfa8cbdb6b0f0618c6772e7be2632c8e1276a857afdd07bf11e86e316e26288fb7464bc89b7751e29173a7b5ae50cbabdb2e555f788556a0801be409c246b052c16b5b9d7f95444eba25eda80ee35c6ea77e8a6214596c31dfbc69fb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006eb6a25331797f238362eaae2a0575e0b2cb88a55e3073fb0bf32d30a891e1323170ee05d84b2cf9bb7fdd960a439a0302af868324b9da668c5924e940d2543bab9a4751706e5531bceda4f9c860ced6e1d2ecc479dcfbae6c5fb8ea64b36f89ac172e537ab783e8665e97c011dbac2b48b439a69c5f0cad58af50489c1153303197976c3590ab3a6240490f600f992acf3a08c0893163bac067eb89d2601f12a021b314f51302b88bca5b9dcfa9753eb71bc3a82709064588d3781213e45e4307d0a06a4b007857c77b15ec85262d55d9b82037fa6cc8e640f3353f65ccbce867d74b8bf0b1211bc1bd5fb617442c857cbb6d4d13b0a788208a67d100b81a087a9120220e300cb489047e6f2e0ed7a8023d5d2865ff7955784c663f424d848e2435503021a4a445a8de16e9347bbfc2361822e8be96670990d3ee7fb821209460eb06ffc03182ba5ba3486b2b1e30ca9dabb2de6aee8b19aceb633b12468f3f875535e9316020d755debd2b9a3cd0032c4643f3a690007458979f605473ade88c20bee638cca032d6634ba6936edc612679624a2066506340debc9c7b930b641d4d398e1613dcd207348fa8f26a8f8e5d19a0d109f15d18725955d8f9b629e1a5482f7268a424162102a6928c39dceafdb7430dc4a863c535a329717a9ef8fbb52f17012290d836b487217561e1f47e4a46a06e5f59573c536e4d8347b06c601d71f01dd0da6fbd4de3282f73d8f10223222546eeae22cb1752bcfedbffdff2b24d42466f34cd9e609bdad8062c776a541bcfd6d3ee06241e93bb9dac7aacc988fbb4af3db06c6bf7f1c0900df782fd6d2bf223c61da8862ad70daa5ffc03cb52187e70da0f55966febda1d5a1677c3a5987b2c4faa4226229e1224177fcc15b8267adba488ec04303f0547ad26a163a140c6d105a8f668f887e7d1aed33f6fcb3bedef0ba5e158a39303dd1fba90e7823d16169c08ff286013159c0f6d43c9751e81051521b86bcfeea7f3974e5bf20a9aa6678d3140348d58d868ebffbbc327062884ab7b69f730016681d08d8f2bc9067017ecf7fb6595cb41ae04bddf06c3b7f005adda459fcb8032a61b9c81ef6471e389775b65aa4bd9a35261d4797fdd1ad1376eeb54cd9332e2ba65b3c6311446275fd5c5ef985eaa7cd6b0dd606f71b6a9525e65e69787353f66777895626f7529d2713c8a2cb241d6aedeef9f80a609b1b12fbf27fa0d1510fb192028b7d34cd6013cc4a85dda73bb3c6eddaf6eb22d7209020a822ae4972308391b07585dd456e703481c139b1f4ced6bed9f1ebf55bdf12edc639a19f14b0da48fa86118c84c26bafe10389746a60888abfed5ca15b46797bd81db58ffaa4555e025fb1fbf0d1fb47b117bb802d328451791efc58dd9ac2df369afdafe95c4746cda0323e09afdb8dea93cf885d632fdebd1a64fb108ca40245047ebb4024d9d5dcd4e34edabff512e2ad8441ce895124c35777e071a4a0ec3767d209a4dfa62485d9859d2de8b28ffbf0791d8ccb4214297537b0aaa25092ac7a8033fdea65afe371a8b5dc0ac48fcec24607006e3b038e2de572d5601ac9f081412c4c7aaf6912a3ef82cfe65bc07665a4e23e82d31fc31fc64d62d22baac7440462ff7e9e354382b95d5b4f480b0e85c2b33a6a7f914950fa635ed296290fc7699047d2a3f8e093f849aa030af45c3f254ff6f139e94c52b81dd3c4fd0249314f4b9e3a7d114ea1bec5c83642fa7ef706fde4bc7f90a651bf65da5cc888ae53d54b0df2ba9475c9d748263a3971bfac182b6da8c6173469e5d4638de7ae5bd7111c5a61688397c03747882d4a9eceb7732bf7aa958b6cdce86a79eabffd4e8188470f68260a33d13f86b1b7d271dec4c6802c2144456e1038d1f1b6aa81f95de478d1dc6db38973d37128a69e3c7da0c97d478a728255cb3d2ed3347506b16e20bf3bd387010c97c069db9e4a8dc08e154d03c6eb8ecc4b32f2d4bf0a573d9e5f61f15964b3b5ed44334420868bb90e16a0df550150c4273b8fba1b7b939fffb56fb721fd35bbb441b7351d0b05df95e5e18114a7153502bd118b559fb179eb2dc143350b0f34b4986ba74a4100abd8b8ab1249535298bab43816a8d5c4565b197fd118943bc3251a87c1a07e5d182c742cf6294ca6445966265dee9e705a1ef2cd98edaca8b9da0e6f25f6b29771bbd9dfc3bde2ffe537e1f1baaeae0c42ab3d84a301328475835e6d12aee79f52633e17b38e17444fe0d7f4bf9e5cdc07717612af14c90a2af93f2235d4b7fe66d3d474a1ebeff0390927c7c2bda5cb395bb737e6bf716a146ae2039915ed36a66fe709093fe0b2e162fbcd3fbd07366137254dd8f4292e5e76b3606010bc235557917e80e5fe87bdb1b77780d68a7fc15852d6d03f0cfe1aa942ddb17fb33ba1fd0f0d", "515152", 0, 273789000, 1537743641, "71da7673f68329cba17dccd9b7d176387acf0f1c7d42ace03bc4202e8a14388a"], + ["030000807082c4030149d52f8e48166b404f466e33e44721e07a37836da6b93c93842abf1da35fed43010000000765ac636363ac51ffffffff0321761d0400000000056500ac005364c6c705000000000100ed2c6b0400000000036351ac9a9a7741fab44c4b01287a4f05000000000000000000000000975753241cd6114af11e0c2a01d1da7c741afcdc1ec9241cd903e7179e7a6753ea39667a451cfd8769722f3b9bf97f0d2f6b375f1802fc59fe47a0ceeb2c2af8ec296a4bedb1582439fa56330f7dac0e248871ce27a682f90e9ae3c5dd99ddb500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e9df23722df050fb55f6f9cb65ecb1a10ab3d21a00c94060f288dec2b79583f2197541ac0296673c98277be80d2fb65020d69e3b4316f3fdf379595bb837b4db8c619f7cf6fdf58e226183b28bc4ae2458e485b0302283a20ef2557bf9605b96f66a85959655274e714530bd1bce1b454deb5423df4834b928c8bcb041e40c21022bdb5ac6b4711b8a3854072be3fd0b69e6cfb6822e17db28d6adc0f65407702b0316ce62b3f88ded95dd08997c0146dcbe6d668fe9958e3c07de4a315ce9e0f1a00a02e7a027227a0795ce654c2b9cbe2a620bf0b38813133efcc0416edac324a654ec8ff7a792aa5c3f4886fe6941cec6dd9dd819e5733120944c0a44aaa86cf85702024c618867cc59b77e443bd151090f67617b945aac3e2c7baf97c0b4605422e4030e012a366f006846882d1f98adcbcce4debbb45810eef41280f5e8d23800ed6b022314c828dcacf74d8f52b4ff26e6d9eeef947654a147ab7a44af1b09c7a9739a022065c68e5b0085da78a117322e4ec43ca28c10e93ab803b429b42f93576eacfa02075fcc09097ddbe1f006f59b7e1430f0e6990f21c712b80b2e98e898389d8c0ad8ace009db252ed8836664fd6722436519ddf2dd3ff784b6c18335e31b2857938d7ba085dceeabe7112bc08ff457c75905fffccdf15c866fd6ba2fd12149ee3f5b99fcc17d86302c794277bf6adb8421a4df356889f15459b195357707aef7d524c5ac68bd70a7eae416192585a2d4cf5789770c58ee0de3fc2156f966453ad2fb3cdf607ceebe0c8247047a707e6e1afc5bbb2892060c1aad52057cdde4e3b717ab5bb6a8c707ec2623fbed045f0921d5a88e4db50cc026781bf868ec972a426d7526b8c9dbe1d496c5a1f599f238a914dd78162101a05cbb2b0f3299cafcf3647eb50851e127575d5e9cdb5ec729616d96bfb3d73b399e0c46af43bc6f1c6bd833a60c28be8a88a9c2e69148cb1a3ccdcd857c1a2358178d683c11110c90c36b6b329f26fd8115c8fc413011ccbb6677fd55687ba55fe9910d464dfd92d244bb6711ac6a27bf706fd4abe11b566b51bfc361c159368326605244d9be404fb4ac3cc082bda7e02ddaf6c9214d2e0e7188952243b1ad8d735fee72e66cb8d69f8c942dd4b551acc8c88a6eec039e7dc5749c24a5fb888d9048cb398c130c4b637b9a3e333df740546ddd11ff99b2cd83c189c5027b1dd27a58b9c6358125a65d1715a879c6a305a186ee4062e2bf9fc1a428dbe001b02997eb5111eb85549aa1d6054cf1cef242712c0e37ae92019050ff4f04e04f92ca6ca0cf2cab83d311956b0ce4b49b6387d89edb94ecb92634727803f9630221b15d57b8556bd1da20cd32bf081cd20beba67c476b4d59679d4c4332aac5fe37180b87ce9d3e98600478a43f4060c00629149abbee4ef2ab287a98ca13e79073938491294f060d777a20c99dc98ff7a19aae8672518d37cda4602f44e908b2c761cdf09101a5194c1d1202adb2364ea7dfa4fbc7a444e5d22f1f0d44cb333fb9b0b244f00fe5bac649963a11b0e36c060f13fca4c0a91b9ab0764ec66de56b3842085486b4e96dbb98a97c4c188c5309075e84791c6b4be177a053d806be1611b407911391a366b307d771b9ea3a33d5719d19ee95b791be4febfa21ff8765090b236271c46b25016ab1b64e3d9abd07ab74b56996a8271e138b6439a884672e7fdc463d9eca7ab8d606ab5593284e4aaf6ef6133b34f31cb5a69e4917d05b49c1b03443f50ec2dc24a3dad77569db3f65fe022b22aeefc5937751c28bb8ea4f339acb633eb73e23af0ad3e71ffa96369d1b12367421aac11f986e0af748386e469718dce3b9e86682c4c6beb0bb450eafa689fac3f8dae6c3a412e6dd3dbf75a638ac94a246f155142d5cf3f1209c2bf7b01fd13390e40b5430401ab6b8796e9e0ca31c58f1d2fe6008f3fe4385e3f75e01432bdc0f37e7deac6e6f58cff6e6fc5efe99ad7f65accfab2dc75727c34ecc35a3cee474a65e1017ee2ded1bb8b01580773bc0305830ae6afbf708d6ba966bd8b29cbb4fbf703cb03db487fb9af5fa8aeee5953aad307db91884f1964c728a6f1c669dbb487bb2f142674078b25f9ad2d4aca23c64849dbc9eaf02b4868d85b792389bf4d654d676a8661cd9c6351d5d5328b25b8935ed8cf05ecce0e32e7f640e0e8fe97e1127adb4f8d31368186f72fa57744d7ff17ec612bc375d0e94d0f73738eb8d924a56ee1a260242f8ff773430e2a6c58970c65e0ed1f5588d8f9140de578e16b8b86d569aa870e9ab41ea79e413db3df8152650f50bcf33774e7847bdd0ef9fbb6615f8766d61a69b9fb4e40f5587dc6d627ba44df9ad76370ad81591eab708bac55535e85f2536367ed08e22ef7bbbb2a667c6fff88bc66c90b1aa1a961d300f0fbbb75808", "51ac63005200515365", 0, -714958258, 1537743641, "f8f1d3dd1317271c8bcd7d33fdde262476701d336f29bfd51f43c7c77d5f2041"], + ["b3704a16040a69c5662d0ed6df28a3f4b4dc90ab3b44f6a78adb9d9b90909e3fe05063f11b0100000009630065656300ac636affffffffc458b0e67c152d03703e9ef7c021728ab55fa59cd3e538effb8d1e9b76760bc70100000002635337448636c2121e2a0a4343c4e116a5b1db5e29aa79fcbe9309821caf2b5891827d5615790100000004516a5265cacf5458d1385dc0e493e5c13735a26d44cce94ca29d9af0f3af3b1d97d015da90b1840a020000000763535165536a6affffffff022a09830300000000035251659c850d0300000000016a0000000000", "6a635363", 2, -151433051, 1537743641, "d3e16af00325ce3868e65cf8495fd598f3266234f46f0260262a8bfc089ac13a"], + ["91cfab7003256dde824c5ba3f6216f6f7a6deb8f8d8371b56fda0180194a4aa7d510d8ab4b0000000004525253acffffffffc5cd13fcecd10f8c4509942b14477d0428ecaf43a4b530bcba1ec89fde616d02000000000351636ae45d15356d28c3fed6a8de46864862b4355dbcbb0fd2e81d465ac6286bfcce6c3b93661f030000000551516aac51fd481ceb01be8651040000000004536a0000000000000100000000000000000014f30500000000137f2d2f958166d21f1fef7bac79305bc366fcba6adb0070d900e76774f82c5f80113f24c396ab473564ab7e822872fdf824caf8e62a8c3fd51dbdc8e75116b0dbadc0005fbfac40474e09e4d783ea31989f27ef1f2f47c98e8cedb8e77ec43900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a597265dfde112e9e51c251c679c39657d9be2b5155dcd587c5c9437baf9730e938dace07350a87d26f4a41bc8feb908060edcc6128c7328aad6d8133930077e09b706f403db2eeef56d15eac3fe0f4332769a317282bd1e113695ff371553d3626a65355343dc953b4e557d1d5dbdb2343ef496c98e09784d3460d0dcd9fedc022980f6d491efcf26e99c4011e2e5d52a0fcbc469747ff14a2bb67e818d815c3c021e0246a83cdfcce4bb76286ac5561baef9ded55bc27f8c0da0a92b0a5cc6664e0a08db4559b74a0fc5ba9c223cc021ca368633bb66b41a3a67e1ff0024faae20bdfd80a22bf12ce1cc8719919e223a3e8696632bba09e68b45d5b681d648cc492f032efa29a39a1a544d35a18ed2df63af53163cbccbe03b19390ef40ff4d1eccbfc03278884bffb664abb6482323bc6add07fc3bc99515984007ae33f462d88c9e478031e570118717f43cadde900906500cbf0e1a01d452e4dd55104cbcff0e8d25c6e02070646cdf54a36a9e9806ababdce63da27f9665c69e5bb146c188dff4c6c131f030c69ff9a71a200dc1807405aa1f3affb7330b6957864457bf07c7f71f32c052bd0a770e3a607921957e01c77c0d0a4b8acde6fae4e28c143da658470d77c1128ca7c228373d66924a8ac104d406b2fc96684938d48ad0d755e6f99bb5a27b719ff6a3fe5531b159dfcb365ef2de4f0ffa0c77d82d147cb99d60e42f0d9df4dc5e3bbdf255847972a0f12018b13c5904479a803f2403082ac9ee3d851420c966b6636b51ead92885b06f371cf67e4960716f1a0c9b86b0ff44e0007196963887bda658e18ee5f2875e06c6a6d23ebe1a9c6ffee52456c5fa850bfdd5d70657ee77cbfd2a75e3d5da951b6fa2835322243585934c8ec1d3d8c03d8fe26ee31f38ac92cd2fda8b2c795ac8084bcb733962ac11839af1ca919d6e6a52384165b11779273d072c92cfc7fe9441b2d4b0833e90dbaa81926b5ba243d83d53abdd6f01ab498d69b113b738f79f603db5dc9bbeef8b39dab200080eed3a2f3f52c16d1df81006b2035e7a0ed1b9920054347893ef63f410206e611de2f4e5ec700c4164c10d1692802f60d3a5c59ee9003ea40cc635d60f4b0d86adee6c50dfca02b59bddeedd9d020be3942454c77b71e92945286b79cf0c18043094d4b47c5161eba0bd1d5a8de811ece3bd18ee486fce21b960c157f690ad759efbdfdd31ce89394aa79737e50d62c98de9f951b41f7987eeb53acd52ff69f4bae7caa6eb2765bbafd51216fd45f67f9c265de446acf5bf8f37f0d1a05ed9e1c55eb327aeda164da4e6c72a8c05dcf6b1465e0e54a915a84ff2afcced3dca5ee4ec02a44256fcda6afbf2892df555eb14348b8016eba2a2bde171c73101b7fe4e344708b5c07b706a34317af7876b4eeff5596875e9aa98bec31b937fd5e500b6b454eb8059b0cd50b40cef1de8ba2e8a11925920466566e51a88bbab1878db423a910653a844e5e145349f458c2bbaf7b2ce74f71c922e755e3089e8b524006038e2e30f0d1b8b32f21e2dcf847254078ec9563660e83f25194e81b14cc91133d70022abe8e90d521f9b4cef4dd826d5b2046429bd2d25b6707d512f189f288032d379252dda433212a3994f397292371660d6a842ac3e07a4d171d341ea55e5e99dd4f67b49bb72bcb92e4dc5d2433781f93a343a1df616156bf99d4875c5f087c1e911f6a6f0974bc1b2fc48e28e7dfe9510cea86f2c9f756eafd2de033b113deed237f10dc04a5766071b1f131c7bfff2a645793a7eb0976cbe2ea1ae70ec235638f5a77493fb24acc73452f38d6e23adbd2a5e801db08f8a10812f03b964af1656f4b247dd3a4ca3d27832cdf887ecf088ac47240e0e12dcf9ad10429cabfeb263191849747394fa0ce050d3aa68fe9750c974c5496e58f0652e392aee87cdcd5faf1ac33aebd991a02601de9bb609841dcd7c59cfc5c12dbc915fada9db8bb8e97c4ae1cb386433a26ae8b67fc562b5029bcf0f0fbc425d76174baf3383e4d64642d8661293605f178bc363d3237cda9729d2eb50920bdf917cc28d1ce09ea5a0c8aff74aa6c21537af44c62356311c64fdc285fde89629c9a13edd2a15dff65786bc87bb582d490f0047561b07a319f21aac58e045e0f8509473cd40ec7ae8acb75f8575633c86732e7ced2b62cf775f1172aa4443736e5cfbb612db9ca92473261dd685faa254ed81d86585109777573f63eb225c0a2feaff4c4da78c3e0649790e0a0dbc5540efa4c4d81e4eb77ddf5d824e19273adbf2ca490aa0cc17210ebe1dd941810bb2dd0c61d9c387de9933082e94d166b5c6b3bf1974f03f9a71401f92265d8fd68cae6b35c1e073e59e05dc7640189cfc7df8d92ea2cc6e6d56f1109e0e479e1062e2d44df41f58db74e463a8854ce95400a", "6a5251ac53", 1, 1019001714, 0, "e836787bc5ff28892b4d83b8b24cb6bf6cfcb60e5cf7b56fbbfa03b36b422ad7"], + ["8e02f857045a93c447ac6a0e684ec1eac9dc8c275820d9c840632ed64b9a3cc1848c9a020500000000086a63650063525363ffffffff66822ee3065c573ec75f58a6d51647d4b7696297c18814075616f17477bfc58301000000076a006a5351510003a25fd0681c8b1a3a34b7f58ef5822c8979526708603406035b4ae933ba399b6af9664a0300000003516351ffffffff57f01abbcf9efb6a7b85326165a29a624b2439c77056b704911e650ac1db14ee03000000096a65526352ac656565ed6422180481021d000000000007525365536563535a814b0300000000066a52ac5163510788de0100000000066a006a5352ace4067f040000000009ac536a5251536a00650000000000", "", 3, 1941426068, 1537743641, "31d1c50bc4f9d33ab112144d4ebd80c215bb3c742f6aba2ed6c774811c6b9fc4"], + ["9fd9aa41047659338d822e852e7f581667cfc98f6e4e00ec880ee9543b25fbb1828fe022be02000000046a5263639f35f8e419a4ea62d97662f6ce17a409c4c5f77bb4db66436e134d554e11f133a89b3b13000000000900ac006a6a6aac63acffffffff4bcfaef8c5e1f00e07d494b5c6c94b35cd98da4b3b4a67bafac9299b399b91a2010000000965656a63ac65525153381704a1a4270ac342201c71bf9420a73427aeb9bd8b50cfeaec9c1bdf18aad2907761600100000008ac516a6aac00516a7260f84802a359fc03000000000463510052e54680010000000005516a6a6a510000000001bd28c30300000000000000000000000063650ece4445baa2a2f4a16b1d7fe008d7997dfea432e06cfabbaf848da7d3502e97a2cf753a75e3e34192ae329f891846b3f29e404c6a89088904f9e35c5f2df91dd74f2ea0d91d331cba1d978de3e1e3b79c837e9503d742f7a1b37c90f1c70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000019ff89bd56ad3b0d3d4edd5dc40a4c424c18023dc6181c64c26a0620d9ad930b62ab7dbab4fd586e4aec9658de2b73d44d3a986e51e859f52ee981a61ea93a8b12f8572adf0192d74298924f29c0d87e5fd9d7064c8e60b657887e609e214f4ff2550d0f9fb8ca6a5254636bdad350dd486d6ebf6019b07b5569c4d4895018280209de7c323103973e039318b9aaa4a257c8b0e57f224fd02c38f4ee4ae0b5260b02151027a90b66ae0c7618d4775ae0c83497843ad2d3d050be42960d8ffe5f0fad0a044450c420cab583b9099b9ca269369b9d2735679b4eaaa4ab43900d17b01bd5980534f764a5b51d75df1c2e976acebd3d750d790fb4eea43f674fb88b433a0802227f3c59f147581741b873fa0a6fee208849570ed3726b2876d622b1e2300d8c02256f79f9d9ac9f41e366878ccbf2fa023a22d38df71c734ab15bc305f756822203027b598772e68c5b3c4f92f879f758c206e887c46dff08a2b2b11591749aa51f021c39c3ac35ff2c6a2ff2ee1857267f5ebe51b4037ccce2ed1532826dd7e25e4a0221b77ff26255a022edb59f86a95fea9edb06bb077cf005e3f76aaf86c04cfae05a38a30c11ac1dddc6d0c14e4e5013dd9cb7ff46acb6a3384882bdca63b490282541a03f12786fcfaab84befb157dcc5a8d5b5fc8768d4f0bf54e7f49e04dac0f4117c3b93d1927e47d80700313aac9efd4de3356946bf70eb9b209a9fb3823a26cec5544847ac2df4421f9b5235d888542bcbfb8430b4e099659864ec2f9332467099b021b387ae6c84928284744cb5ec9e0bfe09053db31cca857b5dc6f4d6fd8b2d16a2bf3c0debd72d5af9fceff726b95622f0d7b10bb0a27f70269d64f1b461b4e22469b2ddec122c77f9ec2c8fad0406f702b69f4d60f86864711bf336327cb468b729e960bef4c72f68d7e3c97ea774a8271465bdb70d0853410d865c5707567331179d6c550d18c5ecb76810915f4a26522d41a15aa322535c44e1635b2e3047c68b04b5118e4ce7e1c876f15268948372a1faad24854aceca40c74f1794508a9645e4f0764f48af58f8f953df1dbaccc8d7f630bae74185414a3f706e05c812ec6a0eac6200634cadea13c14fa3f0c2447798db5f9a8cbad29706534747327dee3d21abbeba002c9b59310990bbfc6a27fef720d8a72c5ad0d7b9c567bf2f14f89aa92c3264e0e534b9ecc2f88ffd0fc6c3355b1164a6648c19bb17aa0227ee160a77500477044275c0c868a0aae017b51786fef736964ec2ead60cb245daed3ba9bd63a86902645d375583838543594938aa07d90a622b2ef12f56c2ad359be4bb27ed3fc33f68039e183af5f46eaa98e632f64ceff9b6f62e8f731b42eef05876be2c5ae0558ae8f2b93d2b50cc4ccff5d1aa59d3fcd02bc3d9248b22706abecb4e181f256e611c0dd50a11692248f81f5d78e21e36f4b6bb5df447c0e5aaaaec05e9dd2479759b716c78e73da917ef6016d00f3744fc8ede58162c9dd0fde786c07695ecdb03f65beebc21f2b0843b07a35ffae76b95d81cde133f8c076a86c79c1dc8aee00d783767bcc44a18d759a25035bf4683567407aca6e9794aedfc97dc1b36c5f7bc09f524b1f22e5d6b30262a4c19a0086f900e20aa85a31e069338e3b53210e5dab647809d25a15bdc919c7b0a9b3b4ecf586460e176c021c1f80ea1f3c31615497afb40da9874b622ec7b40d88fea3bf7398278ba5f2de533a62ab5a44013c3ecf754775276c15f06fd39a1f532253fff1f2b432d7b51e94657070b26a7d54fb71636d1ac0f0368cd28b27176fbe0cbe7536ef9ecad256e99007cadee3d31fdf675424f0f1d9e82d16a14ea9d9ca6e887495e40eb00ee7dcfe926a27aec9930ac7aa1b14d8e7899a605550f0fb9640c1ad637943e025a72e458716266b55036300256b458caa3c29343446b47404bc156bd8480aa6867e2be45714ecf52b8e95b14aa1ef3504decfbcdee4aa7f591eaf89648c65a23e83f84c27a870ef7857733b1327743ccd56827f9b0a156d2c2970b7f6e8bd9b16f896cc92beb2e3d8db27e1b15ed1ef0f25716566e91f8d81983a364c69b6e6d58a64e5331647c661ae076df5f42116780c3d7515be0fb50918dc5a12e36a44f35c74718c6207804d991c4e3210458749fa26b402b39a11184dd03da7c75cd54dac657c1a4cbf92e6e2cff36d720b032c4ed9206a930eb7513c5e6772fb83f0472a5ac9288a358877493ff3f0970d24db7367e6917d9cf2da54351880105e74406c5b49fb8febe6308a2764034b1a4d919df195381c649951320f633621fb92e9dee69f158262896a8966886a46b02c67ccc7564122b08999707113ad0ec8111dc5b0ddda3428fc2a2ab874bad4c2b44574495bd25d1df4b51517cc2679f987f925fd9f1bc86170b12e4fa48af73a7d707", "6a63ac5163", 2, -1688082806, 1537743641, "fb2682c5389be24440a386b2679784cd14165892b4cda18eae247ddc6cd71bd2"], + ["18c9e76001eba1611efce92c9fd1aad14d87115a6676a85d982010a65e8505b7e826f0019e0000000005acacac6551ffffffff04b3690300000000000263ac90de85000000000004636a5100d6c90401000000000451636551dd18660100000000086a00516a005352ac00000000020000000000000000b7ebc201000000002df2a313e7f10023c0a454a7feb9f10ed3a21978632ee9f3b9d11f4ae39c7712eb0e629b0c773ccf54248e9e9b80a1c6945e44db42bd28ace75ab96318adfb8c495759ce37b41f1899a9fc1a0e9f3aba1145cc9f4a27a0820979ff7c19e26a5c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007ba109cca7c61e3f1f269d504f12c882a33a85be8225a67902baaf2590648d745d2e86e78a7b186a4b1f9f3360a1ab71ae5a903ffe39377f7a0fa922a7a81d6d53ec01ac3b81926d66118348061adb25733a292f4587314afcec831760fa64a1d5ffc9a586848eeae4212ab30561929c7e1bac88908015a039d2b28c83b249b00227731fa97e62efb16620f3107216f31bfc6a6d386e713d1a88ec907096f50ea7021c05bd0f021c3d6700688fd642ae45009a86d38a8c1f162440962294d7554bdd0a090bec766b4900b0e5432b001d208cbcb709c2ce4d0c43d865b3ddbff801b35713209c3b357179aa99203b01833e5055ef267bf3676cb2830bbf39882a557b6b0205063a3edd7c0117f86a0c11ad39414b62ad73776bad0a176bc33b93c2ca2243022560e588227cd2d62a4d1668543c7b5ccc9b269ab8ffd40c5e00257004252b5f0323f3c29e55a2e87c5825038b2694e174a81bb6f9e10030cbb533571f2d4c6b0003239f65a22e658b06f65656521a804e7bffcf4266174321de6de241fd5a8ddc5403118eb8a39c8e0b4a97252fdda4639a5e952693aeb6cb3ff3729a4e75c357e13b69ef05b47a4999db6b3be2ba2f6f099ee37e20296a64269444af273a778dfdd5d3de115e01e1b655772b28e90c2e5ea34716651412c8a5fa88859778cf1949b466f5bd02338a7bdefefb221ad1b97636f234a30b939596fe864e46d8dbcab95b615f5c3b26551ca6770cb0dcd4eb976b5419d50209460f05fb881cf2af7f8b51aba9f1c09ea3b2bf58aecfc86ccff8ecbc06a501017a58159efc0675bd27fa7c41bbee234c99362300cf75d4da4d7ed2725af286eedb1adf63a06f18767924ee0f2151fbbe75df071e79b1aaec5aaa96a50cdfffba18cdd8e457fe052431c3111ba26b4e38b826c765fa82e15779af6ea727575edf18f07feeb0b00e2d04af90ae97fedd191518c262988c96898215c55397359de85ad65bf0a6ea58c986f382e0482876ba5b13bff26763bdf54e9d0715e49b1cdec6ace3ceb6f8665323cd75177bdd267dce30d715fefacd5690e5c99e1c3f34e2e3745d4de41b6a4c160f66355b2628471420ca18ab79798cb3a20632e1aea208b1fcc049f165f0f895ab60c44aa950b4b98e5ca39e340511eff2593f8e2a3f30979ee5aa7440f3723a7b0a2dbd6ab4720ebb396d28257a57022e9bd7eab986bc89cd31b36161ea856dfecf2a05977e57bbfb7e8b50e5116fe7aec5254b106d2f69570a79749d2db4505a70a9ea0b5185a6861027b1bb4e05c84e60b7d7e0b69775ce98b6a13e2c36e00c3a1d90665cd2ed4ae07c2cf5b765491e9276dad4e4ddb0c2279547dd5ecc0a9f49d937f28f8813bcd6e55d166a48886b19a13dbb681763561db67df7ed18bad2d103079b274da0df1a0858bc61ed0562d67045e9d07c4c41ec7ca667070541511f0d83cd4cb786c01a1b666a31f26133662f8bf9495f6019b0fdeac8da0f39e5a7d3f7250fcc50f4c5e16f7648766965850cbc247eff9165180002251ddcb2aa5da70d6a42cf76d538df8be2a429e4e999fe580ee8b965df25c7b44d062a538cf13c8951bec8c365173dad40602f807a452c44401663bcb1a2091c3036fe5854a35b0e324b3b941d6d6f10b275e32394f1352b3fbf3009b1e77055717ac302f1c0ffc7eb035b38c7b3537ab5aca0a878127254ce8a50ac663966f1bb0d38467d341ead5d732f23cfacc62b119afbf14f0521f793d505f9a1aec1a64368d757102b100e7731c54333932f21abc76f603b4842f3511957e4d192d5adc56eab1205ab4446e81e07de9a0d8c7ef5d9b101b9e8ab762a2814b8fd16dae8b23e1a725f289aeff1f075d87ff61ee87b4b60f8a16007d9d103814df0690650ef6dead94b659cf32bfbeac8056bbbad9e4a3189ec7e757029421a5275749463274d388315f066b1d7846889c024632b8fcc6c1e17d411e6e48eb605a4e04f78dfc072ab884d828e6d7c83aae4088d31957955808c1ed19a2fe1a717f88cbd8ef5f59f958a582419e5d9ea513f3b0a3e659bd755b0a57efe785688ffb2a9e139b8abfbd7f759376e356341f9efee0b874c01ed70d9ca48022a1f532d5f7cbdfd57fe98cd40642893cbda1a9f1d7ac64a4b56552c40dd29e15e3b5550a2ea7e08da86b40bcc6c9786fe5ee0d2103cd55e556318fc04b8bfdd20786ed6753d5650457fadad6719ba2771f06247294fee46c9c8832a4f89716eae6d3c9eb7dd7991385d099066e75eae941d27d26e8adf4525d832dbd7a0d71a0000000000000000580521040000000080585dec1cde34d45fb3d9987885a0bfea94ef919451d117256899ead7bea210823fb0e87a3f65ac465c57a1ad7a784ffd7ecdefb203fcc72091c557f155b278e9359833979f36e553865022e4b0d284ee7e3b4b1a945748f98e582a471db263000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b0ff95baa3af710f430e416d188a0d833545e30b8e7823a8e5a0867b5bc3a35e1f6ace5dcbfc65cba05da354e8ded3cfc4a0856da0564e80b2c81670a2e68a3135f9cbf82fbed7260b9c8b443f2c51a3dd161a781e23e517338fa67ab83b0870ff1aa7f02e5348eb7d78322eb79940def930b4bab729a6543082ce873eb75230319f1bfd47f3a9d6c970a2080fbb6f206dd43427203bc77e3bf0d95e551eabce60225a15ef39020d274d5a3725073be8e1a8edb0d726302f326fcd123cef3fcf65c0a00f92b6b9a47e8047922a490f4246622e3b65845f51497a3d2ae7c3420b1e5552556265d048088912b5e82abcad74229c6ab44adcd1a0813159dfff1b0cc50b2021545d872893b7a1607e54c00899a787ab7fb207bff9752bcd3c34bd6bd25fcdf03106d5480124035d26bb125ac3547b478193b8814c2081d2e2d05782e732a45df031f85256a350c1cc88de80870c9450786ba38d78d5207b94f8d754be005df7ef40219eb8dc6a45ba85b69a95bd7be43e93c3485c95547809ba49c6681cd33a29ad2022628e9bdfbafecbec07487dfb9093e535505276f057cc498e68706734d3132f22570b97819d5a8ed0d5b7c0dc5f38cb773957356449c8c553cacbc62c4cb1f24b150bf6c9cdf17089685a23a34e569442385ee41ac5439ed52aaaa9aa4e744d7f54b5d30d2fb5c01f12fa181bee5c7dac32f2f6f9b824ed73074a55f6b831f2ea61c9b99761aa179f453bec9cbf72ed5f9d44ad12081a0c53f866dcfff6b62cd334f122647b3946b087784aca588a268e35ecd277af6e8db0ef66374e42a87ea45a05c7d8c5a43fc779c43829aac21b35e8b694f2280de77117883ae89364c0a7fa00b94dd905abb97500973fb5374a8df1f3e271dfb5b34fc8f5e0845fe1d3474cbd1d196d5ad0b8996d8fb149d4f9130d3931a5b4b91d24b2e5b4217110a0ea8e4b00d16e866a7bedeccc325873f6f28822e9f1c2bf8a39ba4823896f7e241a7972748c0c264966d92de07b5be2c36b4962ff052d85305447984467ac904fdc44752808eb57d8820cee57ba46b5fd79d48cc96854eafe259db8d8bab21b0412af66750deaf4018280aad9bbfd296b466cec89ca093de0305683e76899aa7ce13507e480fdcdc525f46fee5ab4f609a4e76802143d152ceba5194ca5cc439b1944254beb381b3640fa7348cfeba5afceb86f9ad396631984ca9825cf53313291aeb0797df42558e8605f081e7f6bb2c77d1168cf4d9c2763aa9601aa70315fe27a4fcff7805d813dea5c903aae86f2c340f3ccb009618af27f39cf92964256bf890604dc2a27daa57f61c84ad84f35323cc04f83568f1b6935c9e00017a11fbc907847824d7234ad08f5e6cf9e3ce31585a1dea70f0729bcfcedb1779eaacf9c7d1200bdd23534c773473ed559057d377580289e758d6c84054eed30b0c8973a0b2faaf9d28d7f67d6e7c19e64cf19880088fd898da0f42b124111ee831b24e160b4ab66bce2c8ad97cd05d10da26cb67031d23728088d84dbc712ea1ed1503a93fa928e2b32cd3b814de4a975a06b98f4481afaed546ba8ae8d6be7e8b6966b09cbe7020fe65e0ac1109258adebfbd66037207086107d48816b5e6da1db9ee9e7127374d8e8e831f0c4a67f8b7160662d0f741b9210d61c7b191810f78cc64420c20db87fc713b7cc836f81d7699a87361ba22754271560ede23e1ab57fe812ce36475013b6a882466de8e90a4e2971ef8fe74b4bc61e234569f81d0f6ef05292524a0b5d2977b60b0b3be7e4bc77da03d97c600ed60fffe45007e395d8e4dfb1f7cfda467d2a892f051f64f7685d30006b2ebf48282541bfe141438e22c03524b6b74ad0b94de471287b0091fe8895bd9c3e7311bca5d76ffe15dcd1f029e1e4591c927b4423fb5fd62d8457e38e67d782cf2ceb3597f11c1b327d5eb052e58eccea1babc19309d421f0685070271de5c51999c87164dfb751b45792a3535a1f01e42c7c053822b3ea5872374ef62e15847c4c568d1939622e518c42f4b06de1c3c0f56336737a27205ec251d92f14f6dcfdddfd8866ba02fac6722378d1be54fc698e2b1f6ba4fdce5f5dcf6a5e2a78a5f4d561b845078587470ad50dc303763440045e30edaa90f0d50c4d0e0edcd4a2e271ee9696d0623a000b5056dae69d810ec52132e8dcfd754f71786944751ac5671f97f71ac00ad69b21e0b5d32932de69cb5d9a0e937d8ad8fea4b05dfe828683f0d102bf173e8b52d28a79d89118c99c0b70b3dd3e136fffe73457406e84dd76f2c60de5c6225313653fd156a556a696ef253b2f6ebde1b1e07fc0c4daa241d06d8991be789f5e59fad606a3c0263a302f41037b5143e119a0fbc9d8d814b54cbb7f43dfafcbf56455e95e811c3073d9e8d62ad56a0ef0d3fc644922ad907", "", 0, -870437408, 0, "0a7b28d3ffaaeba96d2fc41128a9296aedc4142e8e8733d45a9f0b24d8684bb4"], + ["030000807082c40303a7d7a973cc78661fddf6e34a192dd866854a14cc5597095428957b15fa53ff030200000008650053ac6565ac6affffffff0f14dd16f5f11a1e3aeef65be0bdc3c4b2470aad3c9ee2472f2aca55ff56f53503000000096a53ac00ac636a5165d746d46b1af61453c08c83524842826218055cf99810e4b38243d890df8fb0ae796726d80000000000ffffffff044c76ac0500000000046a516a52e2f3b802000000000100c4807b030000000000408e5b040000000004526aac00000000000b3109810100000000000000000e758e0300000000a050037b6a85aac738c4d81e82b72bd3a233aabd870cadeb6cb8f7556ed03c491e399f2e76bc869b1241829f1224ff843b2f8bc4b489d70960ed1e44d4044bcf9eb5172071c4901f041c6195aa6c2ab9d6b262c99943ec5d8a37383466e3dc7400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b3567c5aa41a564f409c348474ca85502213b2983d2de3e932ad366582a281a6c8996943d987c1472e52b453376b54f4033713b3a84fb57ecf336516bf6a7789554bd8910e0b735073b44e10fa1cd134f6e69e0afefd8d3d04501f7643c6e356bd487ab09094395516f2e89e4b46877651c034577ac5ac90a753aeaca793466e032ec1d525073e8fdf5be82434b10455ded1532fbea8e731f256fbf1aa63a951e3032b62dc4ddd43ede4c6401e25ca654963b8d138ed055d173d6326fb77872e74400b072dfe3c5bc5484a22465054f34c82591af8e7364503243aa145acfeb27b5362d2acbb31227af47e41d97455f5e06f288c89d7204ead8020a401d14703b66baf02196a1051605aaf57a2bdc26468c394e0acd3c91d6a9b622a5da87fea25841f8e02228d4cd6d7deeed5e802c82d5a26a241b91030e9c45572637ed317a4bc6074aa0304a0a7fae4d92c5261eaf05e91f9d7230129944f176a12255885a8af8c534d670223e0f9b98193cf241a8f8b4b6d6a42ba6b3a92eae80b7ac5bfbb159856dfdab303300afe6d9bceaca0910ffd597624e17f0252ffc049922bd6090a6fba31c0bfdc2bc3194e5ab9637efdaf8ba5b27c5076903321c0420e87eb048cfe68bc925bcc720d80086a6e271b345f88342726c77c9c97e98150d6d9c08ef184286d7a23b27550b8ea906360008a9458baecfbee81c98e54e97d5113acd76d027671ab5d8a93ebbd45156e3833a24a376e55611b54ede77ba191fdd97cb476e9687d732843f63314aa72954233ed7c98e577427e2ee6c368f9696111248109539af40f084c69eacce81c777baca51e71a5de13fed894e185509b62f89501a4fb8e2e5dda821acc0dac963f5ec22fb6821a0726070f38be4a6dd16389833f1369108d4ef2543b6a08bb200ee91e2a2ec72b31313244917935d5ee7f9b7c7cee3026f0c4ccc6851ad85e6f5fe878de6a8d11b0d873c0d8d98834d7b9fc529abff58f3dbafa9c3c399494dc0fa30d8e577700e9f241fb9dbefc4bf9f58fe5f41e0055cffa309c1d3361d486f4ada5bf50cd3dde9b20591a268577020d4b435170f628ef28424d8cf9cd25171cb218be21b076cb7f3a7d5dcef3d86be6cde9dbed706f878be65688c2715fc6ad46b2af7e4ac67e0b10db8f7c97e96398e4eabbf12fb0155984738df3401209ce321ac35904d757b7c70e0c701ce80a14d5baf3f75505361936803ef8e3539eebdf1f2e251ac085c2fb41908b477419adc74744974b0f41f45eeff9560da06e0a44a74a0720f30ee4a32dbf0f7037c4a6d79233f8881b4323fea2c9cfb8a2c41dfce4282028252123977cee5fc6690ee105fd8605e2289ccd369d77fe831449d41d409fb121e9d8e6dd7198f710069acdd735e903c2c12d1ce8429d11436d1dfcc34f7810bf3bdf5ecb25af7f50330c26d6b31a985cc60b05aca8500167a103b43569bdcb0fd2b6ad24f845aae9d6328043fe27019791cdc434046dd3ce1e3e798b8cb91d6eb188447193ed58aed8d5c3a0f0e3097a493baf24a87f5d6252a32781dfed376d5965f0336bba3bab8fd62a9a542972d93998e09056520c670b375567065303ad3e8bcc6ee40b448c6d554d032c8ce3485485f52aa7662c1022160d928e41d1162d9fb6882e4f77886b28531f2d6889c330d6abc3ed26e4a1c51d0c6f37b6ba24dcb3a563f375abf6430f6801de1b28b0bbbacd2ef47917151c4ffbd186aff8a83524e48ed12f586f67e880394dac93a1818562b2bc7d8e15a3b3908e862a5783781f66d3645b6c31f255d9d919ac77613613e68f0ec35d42b344a1f0a2508472177453507e2a77ca94bb004b610b2cf0a37f7c140cb8489acdc146096b5018abed82b1a0e861a7a1e12c49cd69993807644cc67105cc085754a7e571fc84101a9898f84e53bbd0c198df5d61700a92329bba145fd425752956ff4565f38eaf51a03c39830b7ffda68bce48b016e22ddca326627ea4f13c3f64df298996a22d2fe2d71c6ef73240c2e97b1a6bc0a79de95cb0d54af0a48c53f0a19f49c98b36312e547a67e78a97053a0ccf68c440132d120c607b91a50a8079061a01c3576166c9081e385c5d0991822664240c64e13d518d30d4ddeacea4b6b93924c8303c776322f54e27f90ec8525340790d5ca0b2a101fde60af52b3378f807b473b80ac0ea49bb78cc8d796e8b97588efe2ec2854964a139a697f326d5b5490fde6a72621cca08e7a988fa5691b78cb2150253778f741db384bf9d86f56fc8746c77ef29b001a0fd409a2920317bcb5bd57924008b1face314da9a111c98068630c1f76f2a63e9a1a9c246a01e3456359d5a609199f252ca5481606423c3d81af8d22ac24a4bfbb5d2587b7276df3661d9e1a21df84b9f48e83f1cb6fcd4921c471b5fa33cfcbc5e37bc06", "656500635265ac63", 2, 1899969149, 0, "0dc177a37dec5bf20979d01a7662513f1c8ddfb06122a18b1320c4ae834fb09d"], + ["030000807082c403035ad8e68d9eea428655adcdeece176d5610b95ac0c144c71ad126b13940e008970300000000ffffffffdea80599aa8b726c5d641282c77870288d53800aefc693cea04690ccae9282bb0000000003636352ffffffffde0555789c468e8d6278975126e505a6a56b2234a10227c3144c98827bc149fc010000000665515265ac522b2b66fa04659c2b020000000007510051636551531509120500000000040051516a1ab05c02000000000151d966a803000000000500655251ac8748249c0000000003c071f001000000000000000000000000c3588b95c920c339b251320ee0329002d43e8029bc550ddcbd2c12b0c6f39384842df08c2fdcd6a6805e6b44af1dbe0a19a483d5075e5bdc2737c8269bd4e981eff468fdccc2aae8bef6035f400f5ee1b564d20550b2f5719ff49c368dd068b700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a1119cd4e24679c5f81cb2bb4e78400f9d823fd81eb650db7fb915e2027360c8c448e95e7c364c1d4a41ca5209c81f364e59d3f656b26ecb8a058c66547902a799e4ea2e60cd79aa4cc42a3fcc747336d9b6e9a90dd6539d4901839e9b8e2d9fb3a0795f10ddcf9d2a91b22ce2118cd0232345f95ad86f60ff66a47ad38b1dff030d0216dc15b809405056e4a1ba2f03d17e11eb9fe3705769fabe90ce612fa74f021b5b1d95e10481b26e33666a6b5a7dd6d16e99cfed52364266bc6a5d4d8aa6890b0651b66834c9bc81425703b794f33d0b48bed49bfb732f95fd4f51f246f2105f39f92af6e9e0948b65d58e6a78f780cb90003a56fd9fddbcaf716d83998ab1ae0319df2ca9f0c3ee14b5b138c1988145a206aad00a93deb2c9e967a82bbdc27c98020af82ffb667798ecca12eafb82ac4c54532004848e7b78795949a03c6684ee2c032f64ce9a2add431d13504bc550dfd578255f934cd70878f82dab4d8ff4a12f900221a0f766de3cbb14f9d89453478e7055b9e717489e9cd72894bec73491dc3df8021196eb5acb810b32c5de84f546c072716ba54bc963589423c41699e794e50bc53b46ff4e5e67c761056dcd0dc5f8e3815e0b8b9be8fae44008b2fada95ab5e3e701acd5bbd71372272af32498d2b6bf202407fa11d0d5dd739a9be917cd4a9fd17799059b356c10034ca0beab4b76d12e0a731cfa01a587a43ed2db143520ad3246e1ee770b58956ae322a3a36481f841fd5102d7950fe135a0ebec465e26efefbc242d9ff3b643447aaebc7290b919b7f5b7742d9192e42db88c8e28b98dd9d8cc75f8eb8c03d83065ef640d542e8da944b7bb25c2d60554ded6611e1e40f6dd6e44052a0c9d9e5ee0677433f55f3add654c501a9277607f3f158233c006615a4f35b8aedc3e84f8afce5b3cd214ee239fb396a305bac936b3c6a7bcfeccbcc2ad7e7cfe27c348e0cc7fe73ba100bd978658d49fc083ef498aa6bb9dcf1ec5aa2b91b6fb3b0235da0d7a68a62b27bd3a0cbbbadceaaf4d3cb1ea892fda8558ec016526542598499fe291f0eb3167c74edf41564c2b907e9b22fb91c72c9885d72612e64396843fdaa27fccedfc04ba092c819996a27deb209811f6ba20ea1508532cf6188f8031f228bc8d5175820494efbcad4d14b09f35a69f91de51da017c68900f02b4139d037b8c87cf16ef8067aebd3415cd481ac980b18dd01113ea322d860db334f12c7c8958ed35d04629d53e4b1fa5e3e4518ac15284baccde1b6b44776638b2359343235b452840b0a1f4067ba64706d51f179f078e1c1698610c51ff8e6b52ee3c8b3669d7138ad2b42c1f7ee106f715ce2ef55e80a8a18462674bd7cd5ea2cc85d40fd3a38b0a0406d7801b2acd65d8e74eddb16eb81c5afd5fc115fc4c69b2a3edd8d6b16d1c2e033afcb802c441464f8e3f58e53cc136115eaceeddd8634b66a0be4cbbaffdef9baba8ead7fb9cee38c0b2f3f26100e0b6135eb1c3de1d91a37274787c7c9705b39eeb4aeef91947e5737bbb01dc5f150ae117194f93c18b4e51439c3b70f1adb2c6848043e5aa2ef0d90c9cb3e0ca3df859a85313e32a8b345f04de3ed0fdd1992fab2f776b05670486f06b690725ab09fe92b45135d9dd30f282abb67305f91edc9d529d8b84810237de24c21440980b9c43de947d0e1f54d9dbdcfe4b909b352c4d33e07cd06849debb4bf9d0d3987a933d933c4a0ffc4824d8af6ee922ac8954d826f16e909c4c497c7d97f33bde62c52531c8f7aaade80076938609c9a28eb3ab99d04f3feff21e8677458b4abb36ee6b5e0359e6a94a90e4417d329d544c98fb6dd917fe198d51809d669b839ed9aac96b0c9f4ec6d7da5c4163723c24e61482c95a47bc1c3e5ec8f3fe9496f5d4ba588f570c318a2b49596ee48662aa2b3a1d5f6e36d5d251be898c981cde1f5ea780ed356540a07e114820fbf2a713c2e1aab6c327ef22afce3cc6dada47ee3f8a8fd7e367bad3d14dd7c6a674eb936990d26a8018f67e855285dc6ad14b7025454b55aa3457ec11572fd0521fa7a1505241966fa145f342b2ac1810c33a3d2b6c40217fe42b89089260489ac04f0a364b4a13a0ca3b1e1879f9cbaadfa34dd79f3e695ce27ff5665f4ee18dea30584ab74e07a57e628b5eed72531f9f29101a75e1bbf5b364f3487e21fdd682679a0581db567cb44fea07f6af44a11c9e2bfdea72266cc088a67eb95adbcce5a4d496b5ba21842115738ad08e5924211cd69f5f3a93e80b91ea5de092a64602b4ee119b24400000000000000005aee2e01000000002cf3c0b4d2d3942a53efc70aa12cf61452f0c692d24194b5e91f8a8aa9b20f5c532653d35797c1d6973deb6dcf65b889c766df11a07d83fdc5ba808fe14b8672897185a53d524eb9cc4f83993fd1d92af1dc133c1aba9f2a7d295e8021b5720a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029f880e9b05e20744c851796300922b26ce9e740b42271e8602ce153598d867f4b6e12671e432e155850db06d10eb4fffed1ebfd5ba13be5594dd791a92784cdb662494a08c93f1542cf84e4711d27e4ebbb43dce2ab23c78e0938cd9a4dfce71e732c80bd870b144791db5a344f338ac20b7e8922b20b76f675fd9589e3025e0327bbfef1ef92c7dc387dd38757d7966387c25f869016f3680ef6c3df43acdd790302b059b2bc24654938bd3a5ff7b17c1f5b89b1522b212977591fbece96c7eb3d0b06d8ef0c4dc7985eb63b9bc54af96bf8f39720801270e700bd5cce6123791fd408db077e20bdca5baf5f462a80762c7cf10170d111ba4ff4f738d0dcc0397178020c54cf5c7128d158613964a00cce0050d00942846e4dcda54a52f1e0bacc9190020a0effcd753a5d34f5f6beb6bed9c49076601826ff5362b47441bfe76c7668600307c6635f4b59d68987015442305b6b84b1f184d6d09e5db543f2f7301adb591503201a876d24fa076968f99f65e6d95cb1eb1214e6c5c39a97cca8c9795aaf874f030c6c11518c5ed5b394551e6d88b01a33712878f7e9c3bdf2df84375f081d666dcf5ce8e1680595feec4a530ad65391b949790468c25dc15228a9a7a0de61f005517fae89ef9f34ddce32e18512549687f371ccb68349a700939ef407695214e799cd6ec0aa823cd2fa6006bb44b623b8260a1321ad9be62726b8591a7397b34c44de42e0dc7631ac6152de65887932d5f719e28bf62a5a09c383f722c9e8ce49af6993aa01cd2a0149b4262a8abfc55843baccb3ef072346b84ef1574d81367930ccfe78fa3a731d58da6c14d72a9f2884cbb3083e9d79b7bee092e22e560644ef1605507372486d074526a805c9d68be4ec54b4b44b0d74f223a112075689a72b4106405bd8d9d53ffef03a5ac5d8262c783a71708a9c826ddb59967e579ea934e83de9562d716fb1e3e3d978249bc84df0c5d07a460ba620b0e24e0f6da9dc7e9027f073b21337c0c385577e132a600eafefbf1c26cd31c46a8ce6805e9bb2570c2a56e6aab6d42fcf0ca78bff195a86757fc847b4e953f6f0e97a4cecfc540ee2066451b9f9103dfa277b13e4487b59bbf39341f7e54e806f4459787b47c3bb29858b375211b63ba8b4880d6bce55ac84ebee17fb1c00a94414546c9c396f4044902ec378be293c43933f4c70bf1a5bbb495ffb882655fe91a5f270eda8ff8536af97c4a58f75a4008aef05f9835edce5143f697c215227bec6111ccaaa841be9426e70dc6c6719266887f5b9c2a3ca2d9f076a921803c521e63e496af6829fee6af95f2f7097677b944a77b4ed7538713e5ec098d2c973970a424ba944eeee8e84cf23b7f04d4e9a35c367a5d5d9ba61b0e8784cf60d2f3a2e361b5a393959a2e146009c72d1c60c2739dfdb86278975f1933089c3ffca5595dab59a0faebcfb132e78c68d1291bbe22ff5c93ca677d274abbfd4caf22bd66fa48bcf4c26b15c6fd4181b66a60ceb353bc93d81b48abf667d3c7733bf6420cb5801452b9976fe09dbe91a9b9213336ef047cbb0003961cf800eea22221a110eaf65db96d32eac912931ca15bd2a952546096b77351e45f954caf6fe299545f3e4675651d27f850917d4a712549fe2ece9bd920d7f9b8d1b350b5edc4df729756acae69b1835a53f111016d311995da68159c7916f66b381d8daf03f610fda6dec247e4d8024e426f9f70bdf30e3469c10e5e8c78559ea11ed41982f5e42b437ec0282d66665299f4c7d5873022d9d122ce40198a6bc66563a50d723fa7a6f324afa27bcc317c8c52632e1d391834b36d1c34b6047da6fc4ae21088e6cced4d8b16093c1c1d0d9e202d706c7493014e11a1980523f9231f77aed1ce04375ad84a54df81a64836351d504ca678e699d98984a2fc25608f846744c610b957e99d9bc5aca35c7761e082d006945b3ee439d98515a1c2d042851d4ea6f5fde15499e8849318f7835bdbe94948d6651d11a054e71fb8712cef3178e3035830aa9486b8951c6fcbbdd7e33ba87185c076d94fbf70c6c7de65960d470e4913e09dafca14bf71f89742f330743c89b28c0a60110de08390b932104f14fbcc900d3b9251dcf5beaae33f1c262ed50337e5776b560cdf830a2eb5ce829891bebb8c3197b386f342269c7e7aa6b9764971fc0ae02799ef9d455339c85b75fe460933f86c7e748f7c2b55ca0dae199fa995f8aa3b523538dca1d4688ebe3f9664ec70b2e3125b95d9053c235b2c9c20a4ccbc0d0d91e3ab4a66b7481ce000000000000000060464101000000008b64ddc84f4af1ab4a661d1d76e5f3127b7853c549413253ab9e4ebe84d031b38e23e7301ec0b2f53f1a8ec340bf9bfc435a83766ab18d6b2a428491199f551c2d5b88bf8b8e8b175d4f164fd0bc55f82cb45fe763fa706c021576562d200c0c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003421d5cc183aea9973f29e849bb3ca2356a24e4ee93627d05285b27b0023f2136acdd3a7a27df331fb3b1a2732d676b16f3a106b42ba5c1cdad0e70f20b2b6e40c1f6b3a67582f97e606754422bdae97ddee877bfab531bf9dd0c87ca0b2574c20a507464ccdefa60dde446633539aa303476ed1031619d0e98165da84578a8d032ec4869dcf93c651daba7580a8c0c4ba0f02d073311109c1308311578d30f6e2032b85a18b8fe9abde8effbb7c6f97eb28f03ddfaa65115d39530fe7bb5af08e420a0429834469dec43d197fd6a17c1e571beddc71fac75aeafa8a3bc82d2d41bb55ecb31ef3070e302e00251cbd38f7ff9d471008d280384f3f35e2846936e08ae40224f12535d2ab3cb0d4c5391a66c69d38f8b564b582e7924a5ae45d2af2491ceb0316fa1a8b07516b79d806e42d59e402caf2e4863f05ae9929ec95691e5d7de591020154ad5701ec70f286e8281d4caa3bedbdb5bae79415c9947b418e9a5a411618020520c819da894326ec6fe629241044e8c0d1a3619a0705589e5b0ca48787b5030217ca7f8999cf8410a400b083e8b6c1a4a1b52321dcc68e321c69a7f7f06bd86317763be3cf92b7c710710be92d2abca2eb1ba8ebc01615c92c7f6ee1279da0836e36a6e2311dcee4ad102554b788c1c61680b1e893c0728ac7aaf06e68d95ea6ab45116d37cc474af1287ec62053b0b3f2de841a68d29ad685cd87510298aad1852bba9dca4a4a0c80ad7271aa909f6bf787fe9b4af4447e2d13ae8924be8c65f4ecfdde3da95a6c9143aa352fd94366bf3e8217176f8f4bddb94a857f0cd64444264ef416f637fa92b531aeb48a50d60293b959890ece21cfa9976e69e7f7ec4430646d77e1dd8c2ec457242f34d43b117f32944afbcee90c2149ec475473bd3355d1707813e09d6fe9e00299a2cd94a8fb08b8991eda04adc9ede705f3ed299100a73a1ac50a2dbb2ba68422298478b62d72ddb92f622f32ab80b20ccf5495d7b9d54169e5368509af26f3f329e6f37e232e6884c208e78d46719c870c41bedcf02c6e7880f1fd8765ae1fbf07e647ea1d522627f0f79e808ec91629a169a792229c81a7c27ae3cea95be55697a51ffc760767e8f576888f16ad2840efe83b999ed6373ecf635035992a8f3e7996d24fdacbf80314252131583d8c79b8004c3f33c41936b3225a7037217c8c75581175e90d807528987cdeb21bfe4510c95160260b8ef9b709a3a8fff6f1c0404387e4a6a60bc89a93292b248e8c6fc0a4603d71c766802cfd2d94889bbf1d377dff983e78067c0899ee277237072f07fdc28879fa796e8c390f144761988548a16f94eca0e4ac9f4679593a52978b6f87e8c97e2e324d3efffdb7a06f3de6937541b64168cb3884892faa517146b34db8c110e507e2b8b9e5139d7d24337141682514ade55ac3f60993127f44723bb167a43eb468b3d1aaad76c11f1bab47e0935cc63a608b981894372e585e06b72de7e59820771fe7e76f6c20c29a44168bd2a8fe87a2642388bef3fb5c3c1cd4e2cfec9913272090138a6c1cab12133e52aa11fc134443728e9d0b63e69a96b02c9f60301ecfb63ca569b5b5af678392f71e4c049c4c9d8fc426c700dbb6972203990836e0b322d3951fc8d08df14fd1c5586c079d450f4a438e90f10851bfe34d140d8ccfd5ef691a9666db6ef8fe13553e384e9205a26c7ce20a211b203a432bb00d2322c9cbb7cb90c53c71942d7138fddea435327163c4da0498f3497157ea1e3d0dac56cd336778d98ca7bc20e61f368830e7199b9baca0cfcfb1a7291da284622b6db8df6afa8af98aabbefa2abde8cf12669cbaaf0e5bcca1569c8188833873cf10d6324976aafd15e3f4b0e4ded0d88a025d362c1b19964a6fde98f1e88900a2251105c2ce9d9358901824be9773ea71a104d2161952daf033b60dc81626c34b0aa44964f7673af490157cb8eead5c6af6ee82d75582b805190eb6c87c1197397b31ebe341b55ed7866baba0ea5067cae90c4e4565da597fbd4d850baff360024f4a331e0054c72c1b36e9c31a1b3e1df582dd5547b7503be946a094d8cecc67a665c50ef17758dcb0f3ddff3d0ec5ccda3cf2c90f16ae15cddbc052824df4fd3667b1f1a8d747c84621edb6ce6e03687844ea9586e093705f0ba533b226b6bf89bf2f508c8d8b8a01208143877b2b0eeb88f14b500a05894f8b4eac46c364712a7688ce6db181dad810deef69382bc1d2382d1dd162d3a5a5b83706fdc6191623215d441efc7eb64fdabb2551700bdb1f6a7506e67fe049cd6a7cc6d7240f63031a0896b0375c32e0e7bb2aa1dffd949c99adf5eeca09f90bc0ff6c8cfcf9dcc4c1eb99fbe90f4aa80af89875c1079a271c92f4891a9ae9e0fd498e22f5ee88b716879ea96c062c1b0815d98101a6c70c", "51526a6a", 0, 1183303914, 0, "4e851ce8a95bf387e3d02919c2877155bbf3b25e3100378ef67ca0ee78e979c6"], + ["40cd222f0167087d2711273805536f93358e161000d5b4df928934d4e08832a91079748e5400000000055253655365ffffffff04affce600000000000563ac635363a11f430200000000076500516a5363acdc787d03000000000400656a00831795040000000007530051ac6551630000000003000000000000000014fe6c0000000000ea5b779626c7fde08ddd5b573a42b68b75049d3f1ab3adea72f7d3fe133491e02dbf529aa71fc505b98f335cfe1410a0ea4432a328548d253c2c64aeb70c38a6f6151d60f4035b8ece882dd2ddcd5588e2cf11c3469bcb0cf597f248a03b8fbd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b0904d72b46aa3ef8b24bb6b976188ddd204553165c28c7f3be6c2f9c32bbe84839ed0e99dc307e14c82ff731857617dabd07291ca5e14f54c3482d0db4108368281a9cde5f5466d0caa4179bf3c1414b89f5a9cd1ff0585b35389bb21118008d7e22903e2a7d5775b0cf92e65802b7010174249af09cad3e5abd1e031fc0fe10203d119ccdbd911487db248a332ddb99ec51cd6bda53f76263d6ed0e68d2d705f021931e0f99469dbf0ded6c6c8d65e8bcc716123ba08b11465a3030492172b9c150b0660a5184ec8155f26e5cefbbd53b3218a9a809546fb9288a3c32925537df8e2ffe2983abebc99d8f8c7bfa5d722159edee649052a4d5467cad96e9d27928c68030bc02d9e2aa22b178785a109ad59ce8db362299c5f9e9f9a906393e6f2ef6d4a0205dc86272b7bd303d5f8eef58cbdcfed51a7d90529e8df307e576e3dab9e6403030f185696bf5a7393f435ef9cbd863e758d09c5618ba4d49a2270db4a8955b6960305be63d9e666b4ec30f0a9d96ad92760c313ea6aea2896854f9c2cac42ad92d40324133693ae1be01607509c6be7f6d4e735e7b705112850b83d170cff4f0a3b28a083a70400edb480fe0c09821a7dd42e2d0c429fe30ba9628a47a4f3809bbe77aef38f7a78d9bcc05d8f136fd4aacebd3834ba0d684176bda174580eedd02e4dc3fcc51a06c9667d95ee3d24da47c2238bfe9fddb8968fc117b8d76a0454bbd8d6275ef08ca9659a1cc98588e571cfcbf0137efaf0b4efd1c26b4c25e9d6388abd2cd30491929a37033db74a1304655f4e2efa4f6166ebeb5834c8e4a67fb2069b7435c7d82d993033a58e3431079d55d924c4195a3cba6d3ffc8baa331856f203e28da6ceac15d5afcf7af25d45b36adf600086f9e6dc8a212f0a67982e994048292532038a9f5c2d752f30c6510f979f592d7bfde7902872fd475fb42bc736f17d8d122084446f106c097b5f366aebc2327860d432414f2b82d85e3463aa35edc76d7b108c20e37bd56e6d30d5b094c95255debd4de595b0ae0365d76596effcbaf9b98ece08db3213a617e3da55d32c935b767eb902557b072e56966cc634e5cd4fe07cd582e3f0b4e6ae43598d8792dfb9b9f34349958f93477393c4b5426e39038f9fc85ffce279cd6afda3c21d3bd9a95eff6a224f2dc41ff138d43c63bd9be7bc0399ca8aac147d286f0f3fe27a06a9d72de921b2b7063fa3539462f287b99e92f22015c7e69c9b1c473e993ad8df53fc4630a68554808c44a250e2b8113338218af945dcf2323935550f55be032095d1f692df036ff2cb8ff7414ef4f218064dbfce3c18826e780c5223c281249db909883624cd2f80849be79a27b801e4d498fc6931ee36bee705e70d63c8702c9cb58429f28c4ea5eaa51d51bbc71365870efe97446dab5cb4d3c775722bb19777db4b206b72c2a3004c9963cdfabdaa99a4c582165ef20ba9dea0f1bebea0b29c073a20d7beac6a39f16f00dc1191996084da8b892fde1c3a400e2f0535b6d244c2d9583e7120e3b2db290845e944b9d0c462efe6799769d1f9d9420a089b5fca60515aa6b2d6563951e43fd9a058493b66060dd1f77121749eabc314d44d8a5bcb97725afe72fe17f822f6527d9809fd0b17b5992eefc8fdfa84408b23fbab321df046ce2a5eaa338c1c6601ae1317ab97f36847727a72e758d4bbdf20b7155b5b60798d360064f38cfa9903366ace2ead599e21bc015dcd01a3f5b21d43c66fd5bd2815dad56acce77207a5fd0454c68c0b9449025f414292951be02ccd6d5c3538f4923cf4c3f445d83ab0d8fe1421d117ffb0adaea73b87f5d4e69d52212d9a5e17edf3cdcab00a4dd0d3950b517526c4020ccd1d70a9085a460a7e4b46f0d59467ae72be742d3ff5bdb63c6dc0cad6770cbf620f06001801c1d6980023613aca2fcdd2ef6170e95c23ec85691bdc12be3d9eaca0efa9eea62b8bcef116ade2f68ce8ddd9ef2dfa9a20c6c86ee59c8bc619eab07f66015790c99dea603ea18951a7d02ff8a99f245aab86b59f4e8efd0018d96b9cd6224f5a80400bb7bba8bceeb403ff31d1814f69013276817fa20817402ecd8e63dd9f30512fcbfe3af199b209d5f896266e7e51a0cfe7fcf76de1d567714ab9f0045f6b229429481a5b43f8782974fdc288a397dd7ab82ce3d437528bfab5fbf2dc1a3e6db466a76a51abb28622b4703f424d6ddf70d1edb672f0ecbf557e138ae9e74360dbe5a07ba743306a52333e3d86adfe0195f1ceb6570b76569bd2fdced1d759276351cdf7000000000000000003f49400000000003755a06265fedbde73840cfd6efc5df04ae3fbf5afe63db2ca6232743bbe65a597104a8814cec43d2db95eaa19cb190bd8362650b975106a8d2dccafdb1fda559c2a0d78848cb6ffc4666ee10febc70a685ec8d3cac215bc1a9f55ee153d4a6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000114ba3949c016b7d5d5e311153b376b45ef140a2b9ec793ba0eda7d95ed24662ec361c627b98f89b67ea4c2727a7f86a3dbe6075c3a7d3b7ee791b2696bbd1f726cd13419d7bb81d145c4010ae93b8699eaa5f2c92508af31fbbc2fbee02e840accd7837cc67eba93448dfc2e8126b062fdc2b860600f25a635179927569c4900225f99dab9aaef738bae2be546ae6d6b2295537071352934d5943e90b6fb4d76f0318e566b2629929d879412f360aff444bedcaca184ec1d188ea049e6b931bea780b02f858f7eb2f0fc8ad1385262bd1c01259fc75bfd1af8979ec50b09ae62c800ce93ac60d5b08900c0b304b96eb930215c0e654bb9312380c7102050035935b2e0312df4f315aea575d1893a35e4851d6ca3d27e6432f0cf9d86b99de26a01d057703145cdd76c6f39816fb6b3aadfb16f2c7efb97246beaf3610de6b09f239627a5e022930786d598fca07432c31dd7cf2d2051dc308daae2506253973fb25db8f06ce022036d2773f9eb0a631ea9ff72f71e73915235cf786eaf801e67d6d6fab301aed020f9799033f5f2fe65b2ad82ed780f88f4c72a57e4704081758fb65ce7f3a59fa1df120e2b8b68f0ec574fa18918a54ac6c0de79e9a3b3a3db22694f7b0f05896b258999b509579d2a005271cd37bc8f89b6f7076661e4e41a7c95c5998589b197842a55c58bf27b4d1e1ef01b4591a3b766d011bfb768abf6650092c4dcd22aa9c4461fc891dc190a16595acb110dcaf2d290ab04e2085ee53393da8ced6167bd9ed9eb165ce799dd6f051a99d24e96115c0e4e78acf762cbcca862d9a66b14a891c0cb76373143641f46e49c6620438d3184f9a56a3996f6db787f0c3f43d865d4db9a7d91c9ae0001325b7d4c0802d1336506e34fd95b462f95bd83e03d5d90c5c8e8d17c090c89d1b8a0ed9fc2d5591c21b4413160c3aa6323d30301d63399b820fcb15faaba840a39fa6f635463fce35243372c788f3cb2eafd4e031261ded044d5fa8fe092dde74dbf9adccf4feb2582e351990bc42a54ad182023f300900927002ab6e5011ff0f33226170633f22792767b401da6fd6613db011e5b1c0a2cb6c8d61c35e4f23947abdb736a00bdcf7401f7bbb1c165c0088fe2ce81b478037430182ac9d93b8ece33987353f29f45be8c3b9fd0f850129bf667642a959b956e208536e2c835b3ce94e3a1f34f2a60b9655ba18f0fe318a1633864905f7580e2a6c9748a87be4d70fc010eb30f105b0f47b01aaebc05c6462585a2c1e5211e8ef928b46cac3043848f2f2bdbbee567586f8d2ccbdc834b830676912c7d4d4a267bac8498d9b16b1fc81bbeec45c3b014dd43126920fb4be22dac0acf10d057f73030be2a30ecf0164ea6b88d43e391e083dc7211200c2778568145873e7793bad405a5c87a7e8cadf0979020b216403f2fdeaff85deb820f9235d587e47e2cbded957d7c00e0fe077df001705921ad87b363af1f431d540f01bc53324bf44182b90b2f6a70e253d20c6d960e6f4a0453d07d2001347e0b08ae9d4c5ffec85309fc204b0464dc7c9763f0098684b8dd70350b9effb2b3b197bddcad615e3ba821ad20d40a8a107bb680ca33c431bc14253956b23c6138ac491b577dc35d34dc1ee865d26b0fc0138ba00506fa1e866f50618382630f248296a5ffc770b9a67c0fd0c5cfc383fe1629db5296d3beb0a7d36ec2b1447c714bc7d3a2bc4ef1a302329bdf0203e9d8c0352960d9c42fd58ee7b7911cb477c7ecb8f175480c4ba17106c4863b8e00ace8f8c5a96b8349c134bd1a05f7a84dac680da62efca1efa4a947c16a86bb3775437f69fa7fa3647635fc469d54e55b8c3255696bf67823d0b2783db339c4635ce034f1ef4d9332cde356608439de077b6713324aa28093f4c66005cba5620fd03972df3245d0642f0672be9b1f593798837693fd03c08276137bb37ad1cd363f7c1b266c45b1b9037f5c5c0fbc01919e1e6662ba32b6872fc960ef318c005cc36227be0db4dd0ab7db2b722338ed65fb815ffc70fa133857d43c177f20e97572e4ebcff63ed8caa3ca250852fbe1bf793647d7fa20cdb126427250a46a6ef1942a5e680832d5103288cb4c06d60e832cf84be8db1a1c358751e7a3f90ecfbeccbeb9b7d2503e3fdf450c33a7aa97754f858b81ad7ae1d46f403980fef1a45d711943fb7552f1f462ee43769ad5d2cf309e982b7ba07e5ae148f32d827c88d2c8f30a6a0b069bb0b700adf3f2486b24f505e9fae0c7de128c1396dc9bec0aa47d0184166186fb8877a915236de020000000000000000000000006a8841dec34d84895bc6c0b4537b07148a929ef17208d0de517969558a6dc463c05c599d4f698ec9da0fc57b601ae5edf5fac408194267e0d871feea953c600eb5812ab75a0ad9d7f5ef30971a9d0a55b5c1740f289d1f295c5cd55b332d2d4d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a29e9bfc148187189944f1c8e52aceb1a10b0878c98b875e6f65b858dcbc91dc5d52731681e7209d8222c98a42d4bff0d592726457561ea95229e65caf0105e0ba11367b51add4fad5a1d833d2c53eb516c9d4dae1faee90dddac8c53c85a4ba73851800f40085b4f296285e5565d440c09bd1a496af1a6325b3a91d38c3ff78030f4ff52a63b6e1186b79836703165bdf834313815af842096234ab74326987d40328e8a9f0b59c9eee17bd0d2ddd306594fe4d5c42cd73595103a9813bde1660880a043002270e521eb2081f3bc88e2695e634effd87cb3465fb5a8b70e23f8eb62af0435e58029efb42b7b07b50f1e570d213453b7a0457191b083d22a12579c7090210e92fc66f59bc19872402c4939edf71a4dcf78a01570281c2715a5b1e1d72f2031416c71bf2687fc31c446864e00d31b0f24a6760f064e9ff460e5ace9e2d031102181e69582988f746773793236d97e67e6aea9e70d7d34f006145bf50c11f39910211743cc1bcc8b12f8a7f58ef678cbaf5e5f2b71b4893ca4a5c1e7e8ec780ecde02098e1ae8c938e383145b83f08f5a2b49e6f76f3ba6d41cbf4cc2465cee1ed9d0569cc3f5e0f7dcf8941c55e828ff1271b889caa042f7694e49a2717883f99f7572fcb3c15b5132307550b3bca898bd8dac50bac0e22cf9aae4144fe012ccfa0db6c271ce61ee34b05335cf82bb22091bcdd4e81a0ffc877c47feada9f89857905b617e1500ede01824bb6e84867a4a730de0e78188b85997b854031b08137f80dd29b996c50b2979e3ec5ad128d877aaef094aefcecbfe606609ebc20f7d06a2e59d1fc1fbda58675281e5c9dad100cab4ff06620ba39344a97fbf356ccff91810d1e6dd306f7b245dbf32aa80ed6690d1e6e27477ed008ef2f2ff1f344d49ad38792df9e74f470fd9a856f64e09c95304cb0ae3cdcf240bd315280c37a7a468d14cc73ad63e0dda1714241b5b5295cd2a9295e6472993955bbcb550ee85cd4700e311e1d4b6e99c5c5e76bce0f2d4a571d628dfa54f1e742badf0617a501517143685eb032881769b5e46ef1477d16f18d45b551f09c6825c98530dc7fc0b521fe8cb1c10005485a4d4b1f9fa310d465a1597d0480733a11bfdc83c0a8e6db8512e1f2dfff022ab1ad036ba2eb6555b1e6b9b8435068a81227dabb869a5b6fcbe35fd38692a562b09ecd776d47c15c32482000523f3d7b17160048aaf3f2eb9640c767b30738337841a629356aecd83fb8acd70c55c2e142e4296c10aac6e32de4db90a1cdbda954e25705a246a2ba93a7ade33996656951261f7d9b495526d1b02c145d1937cc4a8b3fb3088d9f05fc2562bcb94c87fcaf06f7ba473843112041e903b26d3abde05a2e45951f330bc1da759ca13ac0853f00a06b58613de2036963e67e2d68d5b5d80a0861d4098401343db01007ff5b24359ff8a658cf7f48382d16f6b0406285e218ef1e18414dc3f9721c6cd1910edb6b06a069cd2d83d6d65a9727ce5a6dd06c2a3edefbbfbd36b4cf6f71e2bd10e28757710cdf82db4058d941a6a2104b13d3fa15779d69498550f86fc03333b0cd9dbaa2e8567a978d90a923b66a383b0340f9ba8f38a2aa97d31fd1ae556dd8969f021d930fba51f74533e367058b884d8a9ad7faef2a6761eef94024af3e4454743840727d3afa3a733ea98a34f1808efacd1961b66b026185fc06efa2d81b53567197cdf0cd5128c277d53a203751fbc3bfc836c5c4a9bf853939631cfe605c547c7ae0ccb5e6203a71d29802d45226780f10f896f5435e47b89578052faec6c688a125d107ef5529d44056d50f29bec9f07920725a087ed6ee919b715c84af00c5c152673e332e23a42d449ddcd18b2b7e57755cbbd8c484c4de04cbcd78de5399c75ddc9094fb53ad7e4a0e0b762dadaa37a3c5eb77eef64238b0d153e6121a1a4c0e603fda500ae71cba65bc962584ed4f773eecdcd9d4bf5e3ee7be0db12d00294a27587e69f154a526eed17ee5e04cd50275ae46866197b0cfaa91f7590524d07baf7432e93ab6e890c758fd3f8e42e31653fa8b983703793d437b32939526cdd1ad7a764b91d1e21f929eea0aaa6575ab84ddd2b0fdcb2bbe9b56009378ee6f3ffee11925230b925894d783a305a66bef4c714099f32dd76ae22b37db85b922618cca9b4942cf816faf6ea2cb5d832bcf91fb7137d38e9172517a22ec3c996a4469ab4c1ba844531020d80d091141264e6814c0af2bca8b44b5864311d13a1c8a57ef0dd16f1e972e9a130bcf2243f8791d08b5250efc1fccb001cf73beb16d62b260ead295cee0929bbac96803d6dc82bbd6c4e3ae23194d1c7fc69851324a82dc8dcb239e59ac26c2c8497842750a4bf7267b8b9f4abf28e5fc732cd452017c82d1db1f30748f5668a32f59fe58069db605b3f0e02", "63", 0, 248378420, 0, "26a67be665be779e5e6e196556834efebadd1189a9ca43d1a8826ab6e1c130f6"], + ["030000807082c403040edc61dd3bd6b0083caffb31f323001fc26db89cb905964f2ee7b8be1823987c0000000001acffffffffa4b1a30a2bcd218f46fbc901697bde6dcb39d02d99fcf33b11ffd4f7853091c401000000056365650065409beebf984ea37140213b8f2fd99004f619e706b3a60d294f59fb6838a95fc8815efafe02000000055363535351ffffffff4b79f1884f81b1eb7f6c857216f44bffa55db371ad516a6c80cd678fbf9018470000000001ac34033f73035712a0000000000002656359827c0300000000010054bd0203000000000565ac53ac65ab1b17f60000000000", "535365", 2, 28371754, 0, "03b3ed9c981fca488c55780e5148382c86f0ffe706b9e8070c62412a5542d7eb"], + ["030000807082c40303550fb20b84e5be66c3332edd4ceef200e0718222454f4ef24156d1d977985c8c00000000096565ac526363535253c89143eef192d5fc1270b28a4acd24195c0556927f30c09532a691be4f6fcb48bf5219ee00000000086aacac5100636a00fffffffffd2d541dab3c0a63d854dec8964f34686bd43dc3fbaa36e3ae7f3ae3af86e13402000000085263526563516a53770f1b44024a2eb7040000000001656dd9af050000000000d41c1790000000000388d58c0400000000000000000000000052957a5c8f1009f8c0fe5b9042d8cb611d9dd318d5166df74678cd288608f3ce034a6e39cf382587bfde1c1fdd43e461de0b30ae52b2b2afa0df08ce299ae97be4c5cb8ea6378f20fe5bf690ed2dfd63b9f8b3f5617e22920473873289ca4eea00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eec006fc3f685a069de52e7229ca3053f4525fcc8da7491f645f969159e5f029dba80bbb6d537c1deb3c38aa34b7023e9f4a52ba94b1df2935ac5215df34d24b996cf7213083fd6c213c177f655d4cdcf3872471679e95c8d5d9bcf89f9e0abb1ce2e505ae8e9f607c1f9a02b7e46452c56f27e41154bc323271155b47be062d021e9217e97ec73fce7255d609f46c2eb769db3a66cfc01f00d3aaa595798acae4030f62a9f620b938b1fcb8d5acdbc69fe023582762c6d94861575bd087c6ed0d5f0b07893ba77b2c11ece975213b7c3d7eb5ffdf21f6bdf5b8374b8e2d8e3f9702263ed5328d696e15927e9e5deb7045cfe482f4a22b2e92ffb4ce7bf7bdd90294c50311edba0767b8285f5dc5577bebe4f2f8446639fec72a351de8ec63a3629eebae030cac8bcdb148e1942593a25a72a3335cdfda407c5cd3fe1a09e946e0383b186b030ad72622698d8a48f7ca8b845905b9ff7f6d30f1130ef8b955d1f7fc9485e4ef020f1998bc9b3533f799016fdec9c98f68992980d7c03ebd2f09d974ef52e936e8032a6279795470791f18152ab0e65d1b0a7920ea205b3ad21e383eaa30c258d12ae11b50211e7616238ba66afaaa6f9560a4f1adebbb08df6884b6ccfd095b6841adff5c126158c20a7185336a3f9549d063169b19b579a611f026a5f8c28f41903e1c3b83cb231d52e358783159de5c0ce1cb6327aef3baeec3686c23d6302c14646707b8d39a487dacd0d944b0376e12435b2b86b9e33e22201d8ade1762a339f668cf27eec69d0de95706121a645a1c41c42387c241e1d97b284177d80f36dbfb501aa3145bbcc4cd96f8ea40c1b9d7a6375b9eaafece1cba619f59338351d10609c1bf04a8d3d8d736052a105a6e86838148a456e3cb14783585d2319314dfedcc8890309476ea8b8791e027659f40fa7376466cea5857d011f20448821316493cb3c4b4e174a048cf849b7a157ab63cc80e24e7b440c82f753de6b788da7f1af2445851f5c2b5cd8888291ef2b4eaf9bce3b70b23284482b01bdea78d8b39667190229c4dbd7c1af41c48bd3ebf8671040b71522d140037b756058a35a76d7e4049ee62fa3ca50d1b9c3ce9237a7ed2ac5e39f52eca5b3721a82793d172a784943ccd3a32ca7c35dff7d10f9e53660edebca4f5c1bec837d30151dce3f0f8811a5fffc042ad47e2d11a1a0b74447097f8ce69040eb8279f68a131413cc13ae3796f434e4f8c0b6d7be4c246e59142c7798d4dfb3f6c6437ca225dc6fe73bd00e66b0c480cbf4927084a27e007d06d50d4aaa1dc1cf9707b29f8261a6ea0ecd768e1c0a5ba47ee842b5c5440401d75d885850ca5be256e437514e7617f47c67dae36542b78a74b93b8cbcf295a279df1ac4a77d62eb147bb385dbbc46cba73c28995ccda4ac84e8d7fadf68c8b0cb048f93828037572e397790cce1b95f91cfad503a3ee8f8b2594d800bd839ad851999de547facda05eb2e25657996ae7272c928a1b8d0f5dcd0703fecef8f6677ebe8953277c67f104fdde70c43267a93390b291acc3f4c09b4b2af3356127976199885f1a5521f4511f8182f6d8669320506f6477792692c2fd4a6af25c0146006b36c0f622c6ee3620991ef137d200b3d9d150de7ab145607735ccb7e445d9884ef0ba30a3225ca6c37c1be41a2f82fc0cd234f2931f0a4aa8892f79cd93af6333ffeafbef7c97ea8de214999035a09e19a2f80c61f759a75c2e84b66fb1041063aff25638dcf60f74cbf47711b77775563205ab7c47fe9ff0fe29d1be0b2b3b225e35b7af94d0f82904cc31fe48d223516c5ad23018db13d567fca272969c00bf3d91498b92b502c6e1caefd8eda9432816d3fa914150b6830ddf6403819577b40ea7a9ef31cd8ab70aa508452615e07d0bb5ba12676997a44f5e7df31bad27d97f7aa96e837cfb5a95ae573812f00dfc472c5ffb6a9dd3bd5844f38a0e050c5b5c24996d945044c51f808f21ed751575f732acb8aba795af292b7d8c76256a0ce3f6aa27daa4b59527b1f5d84444a78d3d61fbad348de9a9cb6e5ae95eec9b2a1046d01be1d59e1b2e653cd5899bf9a3f64247dcd7c1d2c130b1fcae14f67c7eb2eac8a21dca405fd5150b5de2a4b9036535641b3a8b466e96e82d382224dde65dde3c9aaafda87a9994e5aa33eeb8a4527a7e96fe7dd79c489cba2757bd0377a7b89a977e6b0a11076ecf4b325328cf25e24eb9094b845b3c508edcf9c8b4b4cd3bd6e57b37e3d22a92649efc1b3c5000c020cbc40b0c88fd522c6544d8559e98f447a505000000000000000000000000e792e2529ed195aea70da99e4ab796630d40a0b71db87cdcbf3bbb1a3d6010db216fd864587f40fbfc74cf822e10c314e9727e5a5603d4a5a556e2e9ba0b122f531ea9263fa4676603bdf9a720a9af302b6ad31eb1031162e2b4e979518269c6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c020cbd69783f3e88670257095cdfd2593d1b4e56e75a7489cb9982ee796d71179c921bb16ede68b7e29088ab6c6e1da700ffcd86602e41b8f21fb47bffcf9b94c1f4b8e891da61d3fdb9ea3f7f7441d1705ebc443de0e6acab6f89d0233345a29adec8b6da66011e7838f4425b80eaf47550dbc1071bda7b9ad4a63108240e021fef108fd8acc487c1e4c4f394a2bdfb6beae71da8ed5eac706f25ea220dc4c8022fdf7772f2172b1dc78ac8402708a8c53d6ee9c92b1b0d2cdddfd977e85a0feb0b07988e7bfc7a4f2ad7b703b36a5165efb040b240b8df7637d89e267472d0a1bd579d1708a9644f20499a691f16fc4aec84e151ed4369f492ec3af0e64c895d42030ae9ab2fd1351337507df27ddadfd34db114dfd4c31dc8189c943cd08ec85aa1022b4d77af9457d8d923cbfe8984e0c12115b172f91ebe7845189f767be359e8590315f30cb84615317d2809906deb469a81c886d7a67fce965260b93168cc81186803036ff09999e910e3d6357edc8991002bf0d401896fb3318d8fe4db0491987a60030c979e402550d7f44e8f7e7c2e7f89ac5e6d623f83d014ab6399ea0cc122f8abfa51555f7e8c5e7a6370934fceac346e2b77604c5a400e3ce0df5c0700cd6b9a375e4a95c0488a485a899b44d14466e42b6ff13b3b59d275fd4d9693f7a4ad660153829718415095310c90cea619058ffde9a990233a5f4f1f7b19a5c882417356efc7b05967dd70505db7dc56d2e8ca702307f34776b1e360ae0b6c01c6af7d32475a7a35d0c17bffc233ff8455a89874444ebf242eb00df6c6fb03e4f5b3215d19bc89d7b6e0b6deb65c45c7f968587181e0f047c963175ae1fd436ddf2f9bba9e866f19cc0011770107e1da46bc25787f6753bbe129dcb73b9e9ed83846441b307424c962807d779f863a2242f3d52f1898aa58669323797b2c7e824978133ed0c00e810327a14c1389fefcad4efd180082718423bc0eb60a73367d9d3b9bcbac7a5aef8a263a701349c0ad0d421b206bea54ced82ac6f2d59248cf3620879d3e4cd8e1a1b08cdc25569d0000fa4d9e27f6817bd762d89364f584f0bfeb95cfd1d5eb93211c2b3040d5cc9c431310ec3d763a8ba14dcdf638be3c281f035ba6cf8b417decd3ebb9c058a31e440916e7d6d4b1d85db3883021a770a8905b089a407d51a53e20a9de92c57b13ee751c8038f4940538e5c18d9826b922070f776558a2aa6ac72ad739c486d0bd3472788d2b24a2d957d9cf0e68398e5ecb9e45faae9fb3d1b68aad09e55250251eb0aa35e7357b572e5cc66c4a7bad8fa330118cf18b87550a8307e31d3ac8233e5097e76c15145dbfc524fefd8e5a829e53d60de006a7d8cc7b5afc709918d2452c6cc6437786411a4a7b314a3f6607fd9391afad4e8523808ecfe594e79957641770d987735aa076054a78ce6e496ddbd6f0fe4a37169a5c9fe2f3781ceab27113987c68e945d52e67254044691d85f4561913f801d65406465bf2a04a8f492d4ca4f0a86dad7d2257d59eeabf066a3aeac25e1c6cb0719c936031ce528750f94dff7c07470a2c7677606d966aafd8b9b1bb4aa32864e9b927e9c64f02ab5a35340751d36264ccdb204d7f420dd4fa000c6abf26bea6d3c3002688e8dee4d1c685a530180f4b8270454248c8f8f03ab156e3f5d3730ee1f0502efe1709c73e4219ee6c99bd817c8badf4a4fe78baea106cf92297845d5d1c9498ebf10da76d44f2469a38b5c4aa117a0d6cd4af6585c05250269d7a5933e1fdd1393bf04d38610d571735598fa6e6119c147cce856318690ed087b8558fb5004012b2947301c2c24be93aa090c902d2913069d78c1f784179e5e3ddfb48586126ff85da476fd8b349370600289e0adf22b86e3e786cfa9d35c7b3021dd6126d9bee4bfc431b866ea91cf7e7e9fdc0afaab17da27cf224f56b7dd8aad02d29486f4e4548cd5399a4eda4b0f5e81e667744e2b3102774db41153e87c6964b23d99f5fd2d75ec0198acabac7e210a8acf8f57007df3d3bdd0796f98c430735b68ed119292943bfa14b69c9d469463a7d8bded639d48ff861dfe82db7b225f16c09198056cea55073b4ba1aece337872601d84b9e4460b7cdfaf7dbd15dd73eaeba79d423aabf8a1ac23fe0faad042f3006f87881bb3df3266178973cfffdc408224baf0e99b164208b6dc5ea98ca489b95eb85d95f492b645fea27e14e103d0b26c04560d2f8e514c3be51e751016f7a394e6838313813421de21a4df530fc75301c8507bb9a97162691fb440000000000000000211df1000000000001f681b499411be50a04e55e8dabb12a9b430de40662c629a82be4caa2d1df7047c7577494bf76bbabe17a0df7e9c093a0378aa656c58cda10e56fa7185cbc796c3ecd573cdd3e2bed85c4d57a007ae3509084e565290c0d38bad549cd56d68b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006502c355164485a95c0abe4033237fe39948430b917f3e5c81ce1d0ad095162bfe0381bc45c4a43f58e4970ff2ecb5ab459fca7a7562f5469a4d2caa855a4ac4afc1f244a46dd8b8de47059801da8dd6807602a724d545c405b8526290a2aa0b75849c729a8f7f6fc9037583bb05e81d3a5112eea3d31e51c2bd600279bbdf2a0317c1cf0276027357e25f2839c614de72a0ba9cc37a884823fe13d357096759c003194b663497ef42a904dfd4573c74398cf9780d1290f8301078f27ef36ca286ef0a05655319e5be2a3e3d92d3d0e36f145fcc34562be45fa2ae58818b8ed11f3590aa62beb1244a4c8d2f1a8be9c1f9348733dcc3d537d5b4f19be7c59efda260e103250d60f713fe83debc9fa800d1e8c3f45543c084d035098542906d6cbf217ab3021674b83ccff8325d2a99a72b7f1df34640b0b12ab462f9542ab29114e5fd7ffe020069da5cb44302c2cd05deec8295522bf609858f1618679e1946f64b84cd2c0e02090a782607156731574ff6cd5e47f6d806b7a6c5b6733072d16ef3322bdf1419020e135fc65f160ffa1019bedd39667f35cd3fac583dd8316197e34a94b97cfba823b17b4f2b939aa041a65a90226ba747287007175106706f86826d9b584832e55c2da259ab5ea9a96066878de8e34539f1cfe45c8bca8aa4906b5dc83b66d2b572fcb4343a0c039722f53550ebba54850e92ff3f5dbc3fad0ef1bdef55a4611860456b078eb8179d0b14df926cb5d86b012445f9f3d408943acec30974b5c2817f070f18d86a5cfaeb49e7f560d9218946fbdfbe5c2979a5a4f0c4696ccc3e205a7164a13e73bf69d50530e68e4d5d88fafee8aa132acf5e37c776a2d36477f1be5d3b46ae83498d83e61c2ad52b0306f6417d728c9d4ddac7030fef6d4300109487d40e81f400106c177f32bac5b2292af7328d2ceccaf52883ad091fac8f5d88396f6e5758f9904049202dc7a37c2b20a5f631a9a403c6a47182b51e04d6cea6b25d951494ddefb447cf50033862b30d8cdbe7ec8fbdbbe3c6d135c5298c987eed8453557688decd290616625c86abbc9b48fb8fb3a36e772ef9a34bae91b7112c3ced46080a72ab4e55f900e130fc729538ac04a69e6dfc7111612b0b398c922d148ae347f46c6eaf4e23820a46d97614d89bf411381ecf1f31740657a95ddc62c4bd7a2fab0b402be141a546a1a7dee64d8d884b9143931a663064c0a2b40b1a9af32f7941a13bce9e9c3cb2edfe68791d79ed145a771f5c60eec26f9401cac0cd1b734d3667dd0f0d4402386cbcc0e2d6fd0e87e4d62588462d275dba839ccd5798f4b51e21ab95497ca5cda0c3445dab12b2f2132df632ed096b7c4bb9b627eeb635f03c2b72197eeb696065f7494a6d24c1bb32441feaed97062e95a30b6e3b1e0c438f614a9528ee08ec16ac8fb98e80fc17a919eb231a7694d93d415a244beea42326ea943854d2e3e8b47277c243a090d7ba69670880a05ab02a9ef06dccb71cae288b045e5a3bbeb484bf9f4a971cd209d5c51ff332c6bd5c34e3ab7e4c9994d7504818f8048ee332c27420efbff753db6bceac0722f8dec432de86a834a78f39d3813ec0693114dd42bbb203ec552eca8f651bdec32007b45649d1293dfc952889107ab5fde2f4e062e7f0884987b0b3eb1aca0b17126b8046c2898b9ecdfcb220b44401c0d7f2377e08b109f0c85bdb7d8ba53ab280b4bdeb2533ada011668a163f9eafbb7f725bcd0e40cfdfae93f3058fe49abb678438c69d4f65890d693cb740eaa61a9c23af709c16c2dd6adae8477717d5827ff433d850b57d7e0115fccfb16ac1c7cf32ad6020fe8cd09466cfd9ecea21a3ea54d27885500db9e97d60f4e4270def5eb5e0e62444c83b995ed5506a6ec3a351c7ae17393b2d514595c1205c9e02815e94220ba2a67916adacdade7e8e392bea03d0f10b7a8954b20cac5469b78e3af7bc100685cbc0a0aea8cf51a151726ddd971c9042d2f0cedfa5a80ec12e0dea33ddb11f86623f6a5d0cc26f3b1d207054cddffce3e9bdcecd2cef37003b992e330ea7d6d54e81d5d420b21a77473bf28bfcc2e60a5d8030a2299803978beddb2d3f42282ce69e29337a0fb684a2a7128b9c00767e7a5623fdb70668a793668f84f4eb6006cea1cd0d55a954f697450af6e6364adb32c5b6b64c578e04705b99c158198e8bc2c3f8f035a7f87b9697334419fa34df536705ae7aecffea6388090aed239ab06b44ab9dcfeb653bcc354e680a2d4a1311134fcac3d7405eaf04530983d4d2b77c5114b778f4ed5e7d372cfd1435cb5e078cdd8317cbffa1f02de8d67e081703748927ad1a58d20f6ee9808b5f63799aeb5be775d6030d119554a022d0acfd5f47a077ab7fdf07b9a70ee134268f3fcd2247cf70b52ccd6d110af91a37dbd26ec200", "ac526300000052636a", 0, -798177479, 0, "6ce36c9939c05ddcb6cd14e1f199e6e691ec0640b5d76ecb995f5e163567c020"], + ["0546a37a04780d3ddbdd4552a73ef08c065430d52b3e3aa078dae3baf08fde5eebee3318f903000000055300636a52ffffffff590dd74627ea19d592e2247b76e8b1506fe9135305b4bc566378fedbf7ca7b5501000000085300635152acac53ffffffff96d94e89d3ff7150350e27f36ef64da475448964fbbea560658eb93eef71da240200000005536a51ac63fffffffff5682825ced18a924b9249ddde14c3869eee2fb813754ac6a3079d274ff5b7ef000000000852ac51006a5200acffffffff03db9009040000000006636a53520051a777cf0400000000025200656730040000000001514e1cb9bb00", "ac", 0, 910430291, 0, "f6c115ca3678c8c38bfbd4dff9512399ec52726587b249da100c8f85826d1aa7"], + ["030000807082c40303d0e548f793813b4e2f59165cb688f6f7a2b905a6cf3c8a85baaa20fb66c9cd50010000000152ffffffffbed6433bdf5e71c248355b1df826c85eaeec87880f374c08cb14ab93c128a37601000000066a006a6a655335f5ae8e8a7a54940d0d2c1959f1409c393e8b347233c445dc03346d6b436aca842826de03000000015170253ea40223eee002000000000163baaccf03000000000451005263000000006ed875aa01545ae6030000000000000000000000009dad46ec81a77fcd76594284289322778e1230a257cc19c955cdb8228f030ed4b0273cda709dd084cee657eb1e74a1fc23a90ded9347cbdc366963a65e5798ebcee8e95ca426eb1946c4d83f2631a7d23b71605961df8f8f6e6213da033d715f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003593373f709203caae4323c865702a4d3402ed5a222b46f8f76dfa64207c13141f611d40bc9177364937e88b542a9189f49a471b00b54b179c6919cf657b02743eaf20b55feaf5d4e2fd8a2f0713a51eaf90026e1e45aeab4c0b2ec80876f910d48b5142a6f06a30c632d603f27a2f7d1222f1f79cd8174b917706cb9a998a75032b818c35689344767ab1ab78947da6a4756623350485de9158bf1403012db6a6020210bced545e0ed186c512a20e9a583f404256124bb0b4030a82346116d972c50a01803e84b3a5127a1a8e96acd4d906278cc031ddd91a81c5ce32c93a14bdc5da0718c0dfea0f9c5b72a2609c89de7714a77ac9d8a3494a68c4982c350055d34002194b553dc5c5b8a68db788ee3ca2950f0bbbe3dbeeebc8c437b2da8fcfaecc39031cac8a6d312e7bcc0d62b246edea86f58ddeb35dc4976a81a94f5968bf00848c0222d0083ec6b00f7321bbc5f47fbbcc3ce7797054c8d3011827d3f1ee2913b00302170f36244cb5ce660f5e9dc699b4344180b20cb94f032936ae3a835291334130032e8ffaa96be8de67fc8b2ebb9a2281846227612785482830ffbb842ad7d4e0ed9b6bcd4c6c21faa34931857d561ad3beca88aac445febe193a5be1d799f7a2147e7296e468df9eefeaa0ac90de8665f1b587537cd8f32ae44d104dee90756e085a7c03da7043ec52b49499484b1aef9033179822db1925642719c9ed480a6fb21c504e6171bfb4e84c1e2d713c6672b326409d327b6fe174935db141108da62332f509219075da865ad98f424ca8ad82a0043fe29813fc37fa89af7e2127e24750ab3e77b0a249f76ab83920b41a19493c32fb41a2d0679c1ee3326fa2ecff3c41b89d281c2833135bcaeff310ea72bd6d4015fd1e04c3238dc0ef656a5047bca8e66a36e489c576e3e8d213f4f599781aaee032378681f186f29661e405142074e71a971169ede2ac26a8fe4b9e07b60c39560a917bb1d07c81e6ae2d48af19d8881da12f573fd8169cdede727f327b7ccf2be48b03927c56bee2ae2575a3bf0743d48f680e01722b0095e40015fef3f3dea0c87d252b5a947b5e03b158962f4a7d9a603e5a1c19209a3d7885799a5af9bb945fbb7d0a223de0b1e72b7693e68d715b8e8893aa5d1e48758f6391265f77e4b9bb35eb7f26e5baf006498057faf5b698a7c1dba448eb15eb23a299cfa64e80e0e66d0e853f457a0bd5f9663c95e27b6062e420d5d60234835242b0421816025e0660626b2bd4a0d7b02a4f401d6b482fcb48a67f0e738848388a612af27a796e0acd324e762ae98115143c96d36eca83dd8391592fb52018ff4521579db817b68a097f4aec9632e15811e7225ca4c664ad287602499e9f6e744803fe651348d85157efb697df8046d944ec28c9c2d4042c82d22e4c89ceb445b8d8c2b98d393b609fe2601ce5145a5b885edf257b5d50a6c25fd6c16b61ae25c790bd9f6b9a0be69192c5caf5183731b42436b36bbb3c34b4eb236fea4b2b6427fba22681c250198576c44dc24a6ffff504543ba587b80d7342e6b0dc46e286dbd52189e41cb5124aca673817229fa86304b44005a2651f7d95f11ec9069f92a6b1a4455e07440d28d6ded9051122b98887021fa8626fe54db9ff4756e57d8c8e8c2224abd10521fb0391916f81a62fdc37bc9eda8160c2c053a31e88804bc469c864a02d680dba8340aaff79376fa0e6a0253bc0665904e6ce9e305c03a48b8592d0fc503964812036a08cabeff42dab14191154888809e76c49c82d8c12635b0309debf98b0ba22fb7fc35e26f9cb97b6cbbdf0a2519a55835b4b264a341b185e0501925e1a2e10b78b54540f70d667e02decd720b70610ab7d653e79a10db812e148d6503f2175b65531d507f6ae09520a368181af5fc6675d4dabeed29a0d4fff86500b5f6654ffc735b2cf278add6d023cf4dfafe2a9413f0d8a704f7757a22d72bcb8fbe85b1adfe924ed6eadb422e1e2210b6be14f4400f847451d0177c8ffba833b11f409204012140c5cf5038c7af63c3a9695d7cccaf1b90a86d7e773131d9fa9a35a317f29a8f95cd1c85f7eca02b110e6a0da2b58ff34bdbb08506cee643a30edfdaa2211fb4744f28fc9fdc6b939405cfaea05d0dcb93750c4283100eec2c06bf3d4405c3a759046ea17c5c5b2810ea4e6ab6e6ae886a7d82ee4a0f37df14a9138cf9e569185859e3ee0228f82f349cbf47f32b1b8633451d2d6c0ff41979a3209156260518b241b834ae7bc996b1aa420110e00cc95d934eab75a8b865f749187f0b2460b640f5c4c5fcae17facc8147440b518401bd657b308d33959997330fb4b5872804a514c8e022181017d71e4e771f8461fabacef5c72294f260d84ea21611df5ba8783303582ae7079afe4779fc18b123d479e36e56f5c696ca706", "ac5163ac6353ac65", 2, -359073691, 0, "d4ebc1c492bde32c68259c9b0759d9505cb470faf157a43e4349045245c00782"], + ["030000807082c40303be4b019c299d439ee93cdced75c9d4c069c285d9330a2957e484d7b00cc8c6b90000000005ac63655163ffffffff1d86ebc4bdc71f0bb662dc408a878bd685dd45f3d1ffbb4aa397aa136da46d420000000002000046f385fa8d45830e5c37e893d579f2e9b7e5b9a2a5b26e13a620485132f8c7462124dd4c010000000400655100c555303e03afab3f01000000000352525378b0e7000000000003006a65e4e07f0200000000015100000000000000000300000000000000006088570400000000d35b81b5e3a730af77d5c081b7bed42462ace0f0e5f455054558ff8110205460e9980379105fda23f7bfdb7353fb4c2f656e1a2f12a3a9316ec1054a67470c76ef08297a88e43bfec79216d31400c1cbb22aa880ddf18e3e7df77a313d19c5620000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029106fb8f68329ba8e2a59bc9333fd782f342e5c9c769332341f3a7acf8556643f4b449cd9004fdd3a9cd3c6e15ab4eabf2d3458b7a944656dac4e9542bfbd5d13ff8adbb3ce85b86cf853263aa65006d8a4f0d9ca19cceacb32f964c28416ab1fabbbd034a8aa8b8695c702654ad41e1bfab309a9a73d913d15705c3c35c7f802097bbc22648f2dd518669c9e1ee19160d80bb7b6ee6e962e4fe7861a4958b5b8030418a6c17b1eaba79025b3e50c9a70e25af04ca45e4b64e32e7d040e88fbdaf30b083e33941e4967b5ca454856df0bb13223a978273c150a51546168c885e81a25cd5bcea0f38777292cdb335047edafa117cdd0e458cd471e040750f9916bce98022a0318fe408cbdf46ceb5921c81c4493c57e5f53d6fc30b51a8d8b655e3ecad3022a64dacd8ceca10afce8ce84fb568aeffc219caa5871892bae921aa792f57916032fd8ffbaabadd278b39108aefb209be9b6c6098358266c4184059e43bc1f79da031633179d33769748e8a0b7452e11e1b04ff27082a890e6c8676e35802669a5d50223496ded4b5f93bd755a2df608195cbca227dd3ba162520e63df5a3a8f65b241f5e74c4406af8917cf4934e37768313ec2351e01523d82a0c85ae42264283f0040a9740973195e7e0d1e9d3498a42fb7b5525575d1d674e4efed96d76c67128f3fbeec7f2ad71fd86247b87958cebafbdb61cd9b7ee858ed98bd98f9f1553d04338ce8b99edcd12947ca0c68a813287fedbeaebfee3960d08fb1d9349834a6bdba8a17515957d237af4835c238d16410b3d8962c41eaf8b1e9102be304303382f30e35869fde7880d78977f2229fc83b4971ca065e1396226924af73ae0df3ffc1422c2fc3dc5c45d06259bf5823b4f9f9050306bc8a25a2f00b96cb7e931ced0e842239c7f3390e4e5da780cd63ec6f0d1141da6a7ece4a3fbe7f4a9123cb6c0e0a6660419d937c411d8dde2da45c67384312f10dbcf6bfa6f98e5116e82c69d1b4bae94b421bd9b6f5187e49a49a9e163e57b292c5907ace9ed776343dac566d79989600faa9f69393d9a470a48aca0cd0d822d16524300db965314536e93af3efeb87dd7505a9b31bb7409e5dc7ce76c19fa58f83e138a1a8fb943d9392d4829993f92a9d86ab21171d183b5ff166adcf5260da494c1055ed504254c0ebc32e7b8efe815236fa0068fdb2755c4d072f8abd1c6d79a06f9d828516e755c0ab4b49befbd3a05a5b0aff29bb38eeb9c0f9f2fe6519ffadd4160f23a0d3c865ed2d24db2c1338a5d98db28b1ffb61edf4e1b1be976c864bebcba9cf6ab325c0c53f58e133d0151cb74c1ea2067d8f835a80b25adad2f518059ce0ea71300c111e6fc8a2a4321a3622be8f9b73b108c56f0cc99616a21a852b288580fa70e706956df6f7ecd59c4d37467841074c7673c0d5b66c0eba62198954ce01ac3b0bb5a9dc93339f81189ed2313580931749c0d905c2560e58c7d06623a961e61a9ac538fc5279ff009872ada2568c6f2e5c73c0f67857aa21376f00d209edbb6074e99c2026c911ba22d6a31055e1f0f48ad8870cab8ebdcc23729f145c92ab0cf1a7d0f3dbd4c86db883fd4620dbf9d59ead98d0e5325df5618c2e7441e07b8530d8dad1cca9cce14de170b45a2caadafaeb9041bc5b33079bcad628f0828d4b08f8bab672e8bd937177660f2b2eac990ce19d4c4f13e0f9e9f394a815534e32cddc3ea27ae86483b41f827f14bcf1a5e54b5bc66ff8b7fa3e6027297ff0d49758f582508377d722d16f62fd07b239e1505f34a4805e5786fec8d576ec4cfa847146c2e5010efe3ce57629671811965d225b091552a45a5b94352704725246bc40d7f42874a8d3cffe6a6baa4e64567106f325fc80e349f144fcf37a99c35c46f4e7dbd5917774c930b35ec9bc1619a0ab4aa06c611a6ebeae823e63caa5ccb403404fbccead6087c856314c38676b9f08517ccbfbf383442b2d920b18b959caf9684b0a7a68122e9d93f213e89b226d03e0a7daf7b7190fec5aac6ac978c91202cccad72b8d161cb92a5f3a5667f901dd70d5a929fe3eb9fe2fc4dffa5455b6906d2ea171a5782e1955ef8c64b9c336725d28ecc2a4d5805884fd7aeaed8a103d5c115a039f23c317c96bc8e523f73e27c753e796f36ae4ba49b0633e88b3a5bea27306f4617bc751311c895270b7b40bd6721b34c72d714457e4226984ea1bb344d370f7dccb797964fd954dd0dd7018fd1af07b47ea999c83faf5a528d70bb05c1e65b504aa66c1b17a08f74b3e79e5a63c782b00000000000000007d3cfa0100000000975a63ec7ccbc71dec59c7e04f86251c50a0eb2adec2b55688e7ed8c90139f6502a17178fa2e61018f9b4af7a1a044140b9a2192b6c85a0073a268fa0df6c2ad18bfb4ce409913f97cd63c01e894f2c4334163cfd584ab1e08a92af968b7f8ab00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d14fa035ac622df1b1a10aea77f54d06e3612e9de3d6c66cea4043cfea679db79b94014a2ec9806fe97bc4f2db60267bf8967f4f7570d9bdcb39a8d66aa4717e0e3de4a504b56a4d346abcb35a60dbb778c2fd6e2222ea62e9a04b6784ea3402f03e7baa69585dc47a3d218543fcfb2b2f112bba96e688881886006b67f1a1ea0216b1d05d5991cd31e97371afc08f860e039e44ecbbd431763519575026e89e4a02157e3a34ff0267dd55b3c332ac631dd4d4e879480169c5efe007c17d65fa2e3f0b033dab21b1951f733f8b7f1948d2899ade714892142bfece37593ecb77f594b1225d923a7d0f1b7fdbf997dce30918e127da7c13bbbcf3452a650d07e360f7f2021b95df8cddf05498512d48678646b28d59125432b8972faf5a2aee0c1f812ff1021d04caa479dd9f0f8ed03a461bd519ac872f02135295e1728400d1269dd16c110308f2127ce7d266823544519cc54da341422ed609db3c2ade910d077117450e94021819d5f46a4b3f480394ce465d65e0cb6549b5c05f797bfef231c14fde30da4103180ddab52e592d5a7d4a18ad03207cdb3379bfef60b29fea7920f13bdbd463dc566603e18a9651388d0a030ebc75bd25653ebc4a0f77a294f47a6ec77efae2f9527ecc7bd8114c8944f8ddc9c50ee86a5b6549ac43bdd1e0947483416dc933f2f3694925cca32c6d09f607053c945702e84ccd457a4cc737712572042bd87065df09033cf0c437373e6ee3092757d6a932a3a1c85eabcc5afb2d979f7d3309701f22c691e537c00f72cfa9c76c99580e1d7b489f27ec59c715a09902205b76856f46567d8fcd7027c3191f34f5018b2402824d3b52e999ba3733e20e74bdc4960261db51bea0ef7a1fde8c88300b1e484f992d82b52939034894976fdf8fc85823476dc9c95087abf0f7717395e488aea4aaf26d30a8227a74e71a4f9c8cb41720622e49f8cb7e01b587dcd14afd14e40713709a2092e406e71fd4d21876b3bec8af56cf7c9114bd2a910d13b1d902f31a9d4301f80fb33f3f574a489c5c9262cba0f95ed02d6f253865efc6a9a6c1e6f936db74571832da462e10a160a051a6e6cd96b58fd4944a9fa6c5de9a46065fe0bac29e2777a9c7255a9176bfba609b99ede7dce36595e9ce86e33d12e7a1bec56c53173c3fb59753b2a5aca8cbadc49112cb47fe0b78c44a09111aa2189670c983be682325c0de1ed899b2f2a0e5ee8aa23bcd9dec7c7ad9481216022402b09ac1df515ab687cfd2ba87cbf4f740ef81276a68e6148190d9f030e38c7d0d2bf6bd42e7aa259f57c1793dd0e5d193e8a8d27362e1a7cd75e1ff34a98af88587805cfd2474a155ebd621ebb83f2482c62adcce79f7ba2985c4f148cce2587b9050a70ae8042f76b31da187b6df83d2062397cd7ec08486b01d4af95e6ce143f673be34bd8c703cce3d62b68da41173179a46baeffe828e16403fece5795ab679ef7ca24f4c687a8746c68eaf4c52ae8ed25695b03fd52ab775b1e58afcbc311437fefccb979e71afc9e0f890f0c01ed786acb306d961c1215a7d3b77e768caddf263aed64a1cea637646baf38a9055a0f1a677b8b1b4f09a102d2813bd637ff0f7e0bb12146e49e697e96ea433b1dfdbf65f2bc0c1a51ba897259d11539790aa1a379029e2e5fffa382e3dc92b2349b30b4f95c5ae06abb2bf0523f94c4dd4c7acf57c1713c871a79ca234e14941f104a8d4be457aab32a233577045c52a5c7f57d3cc5a3dbb29f6b7340ac2aacaeba60294a33a1716046a7af29fe2f00d0ed64ee7159307211408a8f9935cb2f028aa1d0b21b5f3217966ccb3602a7df0a4d22abcae81017fca670717ce830041cae0dd475385d652e58fc398c865a09484e7d9464e7f8c1eb7ef05c154a467474f3ba75ee55e2d1de06dcfd676e2d220112842424e58c4df9f1120735e2eff66356e0bb8dc9d50b8996735249e9ce4e819a4db1204a1ef76faad2c191f42fa1396935954f856d6a6dd93969d88003f44600fba3fcaa81518bfeeeb76d33aa4298bdeea5c7a17e70361b0818c47b94102312da84b0fbde3c1274738eeb15e32138d99a501fac5d0485533f0152a98d96214538d15359074293f8c11ac02090bdfa43c62c3e4c8f46750f463296e00257321f65803981dd61c4899bb2cc4f6704ece798789d149462e27801ef0aa3ec8237ba6211e244494d8b1a9b7ef67360d93abc8a761e5d0f60fa5104e7ae9f088977f5b7ebb0831449075996ec5d4643719630cf4d330cd73840d37a3195f44f99815e30f240000000000000000d5d2eb05000000007b72161478779598c8c36f5ebf43027042a8b60a5d989f273ac706be206f934103b92b9f4bb2cea9e419d8ddd54ed83e6c72d13c92b41dcf16b88594fd0a209e6780c7167ddfaf8b89771ab887a834383920d6e9cfecb56decec9aeb2e99cf9a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000185c44fe8221d36a49bd5d592b87e3a7226a25d7719848b87a6e52db87cddad921fd8cc2dc5932f25f9f9bf09a64efb69fb7a8dcad2b7362a054eb069df9dc98b89de76aeb808c1b278967ad6a034aa6fabb89ae6d2a1b97c37b8faaf993f795915595da23269041d2cfaa3ccbb68f0cc7eb5c04dfe8342ed43cdb8e1cd29b64022b7e660c7cbe11cfbe8355efd08e8d2bdf72669e5478b248321437ff0a7402590318611a574ea60fda4cf6a63b22d56339ca709b72d01441caf72fae492bd7f1c40a07e2835d8657a799cb1f102ed81d694b99ecaf5ec8b1dd9d830160d6d1ddf53c4adf323c9afb355c457928d7bed1a67c6bef27effad76a9a68b6b98e898ad0a8022e07c8d49b6bcf127f5ec841aad7669a7fbb3001232b7af93414153e814c4473020254abf5530065462c8675002bc0f98eca22117f36b68b777969ff21d22d5ad3031aaf04056d4ab0168c4aab43e899e706c4e2e1758e1ceae0b00b567c1d5a46190220695114c52b173badac4228cb41447e1abfbf1276a51b53c6c5d6b49c3e39920302f30f2fb2f8e606378e984c72bc980859810d78bd7d778896b509a8db008712c07163e3884c956060944d832ab04211f94b7a60703509523869e09234c12aaab260a953e137b0812641df8056299c93b192c76b0a40c75c315c39f26695af127b69fb3ee439400f7d086bfa0973daf95cef92a8222265dd64426afbfa731962c446c092d92c02619ef775d00483941c553a826fdaf2fe6a27a22cfdb611505cce41431bcde0125c58ebdd5d7224c3e2adc9ccec212ae29cebaf52ef805b3de56d9fe125b704ae2e0c50ff52ac33264c1e66bf5056bd05dec1fae781cb86f68ab6274a4c20af85b87ef970291dd741e83897be0f5d909c0d055193b3d2aebc37a4668864bcabbca01707cba8329de171b2eb7c109415629dcdbe40b24fe9eac22d6e454336bd6436e78618f1da5683b4aaee1f0e5bca5e0840f9fd9e698e30a0d538154570e6abea199d258b2d3000737364a109ebc190bf28d3e6bdb968dd639956abd2d68475beef379c94dc62541e0d3d1a48d819c3c02a15f32a19e0eabfe2cf416b864ca9954160839500fa6f1a409ab4fbbf0e4280b6d8ed21673907d92302d51b855d1f95007850040202bf1f2f96443b9881fd835fef4b3af8a18811579529a45de5ae7d804aba5167bb860006cac3b0a12b2e3ef4c65b9f0d98d6c1f287ab2cd37a955bb8e15387fdeff77257fe91693f8b5e1cad31e80b34e5a751971952afa02bfcd9e569d65ded4579b93955a4eaca22ac856976439078b0349ec23c41ebd710a59d81260a0cf458fb6fbe28442ae81e19a24c157e32c61552ff1b9f2e4d32ef238f5a02d0f1cb6b6ea76076b0607fd6ba506e634eb9453df46861b91a7ffcddc56378960b16b054036e08f2785172a5bb64e5db3d5da9adb65e231e3c159a192098330c66022428210d70207dc7812f6f9c2c6310e3da4647cbca8b7093674adee5ba7ba1d85ba16438b5b4c0ad14484083ed19ddfa0d6078b711104a4ab3144bcdeadd9e1aa027a6438601fe1c93af402b069500b089ffd9defc76ac0dda692be464afae8fc478a68b7e29ba517b2e375c43c8e2600d12ce2e008c64b2b4f6b47d94277467b20f8a20c41e2780ad6428e971d7063716cfad9e5c83c1ee98edf12bd560860aa69c083c1076d99f28dd91b68aa19aaf493bf6fd19955553e449d7c025ce17b9bbfe34c5fbbf17e0b16cceb040ac62ae92195c5c63017063d9b30ed23a45f0e32fbbbe79bccbe8fe94fd1802c32f13c08aa5df84d0ee02103083b4f65f6622f20697397ded4bb5605a432d6c8663f7f7a7c9c46b30086216b6a36ccb6cca52ebd486860130dbfbf25213f575005dfb5b25ca0bb84f0b55e12806f43fa97ee855abd3be07a5cc7ac805917109346e44e08f7ee6f06f95215605405a9bc87ebb573f327eaf03e3ec33d81e385e40b434fcdd95f6327a58c0862eff4186d7c6c507d132eb4e7223b6d0e666b0adc5902e8cc1f32edd96e53de9c5a539aaee231501a5dc6564727a2c62b7ec5da5f98e89076d0cd1e2f8828372b81a92a0279f0807dd41294890cbb222f439bd4d7f9c547a5c479a1ed3aff2f52c09ca4656587c2e175595160a7c824a977013ae83045bf5e488d4f00a8ce6f3342a88dbb26ed6fcf2ff655eff4c699a78bd2deacc119950272300134a6e49d875a4680528891f6363a379a2339a5a67d2c0e0ac0b12f2efd7311ba1a44960bf770baa34cf0598d0a57881ab6c1c9689520585b6be68ee04193933c2a24abd0bcb19bdf8a75bce9b6370dc922a48d90e1066501489588d6f66cdef53341b7612d1a041869ae7e63b9b540a2fc7218ad6299a31b4813b89f54a9d0ca6dfcab484fc5c84be185edc67968f8a950e00", "6353", 2, 1381533410, 0, "cb7bf268bed5123b8d9be13564e4db7a0f2d207290a875ab353a597da81931ac"], + ["030000807082c40304227940b7af6cd7fa598b1c7ea4d77d67526085f86b1c3032103c5a7f0713c5eb010000000465515153ffffffff06c647aa92d3574d3100c7f2268e40059c49141263de8a31e08f9f277ccf5a3d02000000026500ffffffffeda284c4816f150d8a030590767a46449b110913a017f8db83f5e6028936363a0200000002516326d27b72c618d5adabb7a6260ae63a7f0d23e19b6d2c0242eb3e344cf199bdd0153f9d9503000000065163ac63ac514d1fddf10416d0480400000000046551536a8e831e0100000000045165525170feed03000000000465ac6a008cbb4a03000000000600535300ac6a00000000eb388ce2030000000000000000e9fffe0400000000940ad60989d4626f98d953a353804c047f4fd88e2be3ec7ca0deb16d942dada056721422fd0b8508d8128aaed5dd5801483eab7c3b4b2d164360392537572c8ab556ac6d8ff8653d67fe1488cc4adabfbcfbac3da2a7101b20842fc52a84a3890000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000069d1a6517d1a02ee2614cd08e585069bb8e4c4d8916a7ad3f49955b2ade780143c1d1d0dad021304ae2f0409d600b4c446799d8da79406b661e2fb5d4b9612a69120ecad4a313d119a2126bab210c7a686a28757094d1400e2f2bb2217a8ddc75b710761ed9b21449ed7cd1bebcfce37cc36075299bbd12006474a842dfd328002243d83429fb13046e73dc100b3abeb286d4cd6b2453b54719b96f768cdc9f5310311a9ffa8a8885f8d93ac4c5b9eda23accd4de8f0c0e647e046a673c248d959d70a048502470ca0c7ff15795aaaf6e745dae6987a0b252c78111c173f16a5c92b5d54589179d622358b8d2ae5c60351e981b6df9ccbb323613d1b1cc12d6162971b02085b17bebbf59f246f7ff5458f426fc02be58aaeed62998d3ab067cc149fa52e031a36d0cba21b37f8ef5dc82e493faede82bb0c1615e7f419130b361b38aa75b10329354ec177e2b87ce8ed3c7b537923be3194b4b72545d9d6a9fe1473f3bd431b022c31d741d5a320de2416f4360e1b99287b125d04cbcbe5ded9927ad7cd521bc8021e1b5f04a53ff0376bb29edf997da63bf268a1d496e053e7836e0b9791729aa3f4c2284c8ed59d679b5728957330d3bb1d3a003968dca7a660d01437fa9d2c06b0624d1ea38c35a7015897641b5d4875dcc711e4b8f9178fff3eba45ce259e2e684e3a4193ef1fbe502f1951ec173c7fcf056f11d59ade94fcc86bfe19d89310076822fb3ef614b5bf81e645dfbf1d48b76d568403d1b043a480dc2dbbd07ce2ac432623a999057d0e0742563c8a47954c60b70a652152a59d6b8a8f6112cf6f3c4b40f8261bc50b7f66b3bb72067e6a560bfbb247e7e16e04cfeaa6689cb673ac4de3095713f411bf1fe5137e6b23c6fd6a9839b604398df2fee761c7fbc1fd7461cdd4e253cd83d5bbcc52479da6d3c0011998fad96b44e7dd27e2ebcdccf22b9593686ff89efd636797acb6796a29754973b1a41e5db9fb195138377df07eb1422a8d7d871138557539ebf5927f821d597d2434f4103a1739448df065a4b5c92c9507891594531aed390a7f305c202c14128c2dded3104c7836bbd88b31b36aa93f6c10ae5a841bdf7f14c691931ce7fa3ca300c22804ab1b5929b4d59d675b905cdee606c04b1de2c8ba0b14d1dcc07f360c8128a140bf341bfad36f2ce3616d934980af1a1890baa964533505c1b3bc7292e7b18b482e7fc9d3f46583eeb2786fda00833bdaad6c4bc8e989c6f1ef4efcd43e3fecb1a0731218c6f9c596ee24c019e90a8ac5c172395ce5d78528cd1e2493fa104e12882a263b25de76814ca2eaffcda87aa53bbba57104d6261f3ea32ced255f845a9c349af68799b68d192f8e2dd637f132b996eddad9bfb263180ce9fae488bb1fb464425fb404fd2bf29d8e15e8ae519aec5f966cd6e96101f79b954ec4179219fc15407322232497582ac90406aa592242bb33a6a20564bba4be0dd7b4b58e9fd25e1606698797fbc19b1897b86f7daaaacda6476991f7adccae52a0af3f547f1d2d5a9c01578496a1fbb7d3b7736733958b93c2477e5de743733fccd901c538c9f4e08844fe455dc560c915e13cd86201dbf35b17654d5b3e942b3bf7b110087a8f10e89b178964a42fb1547d7fb6c0509cf4bc984b944ab791ed3936de2f8a69a990534c1350a8b2c64a1e6198bbd41fab7ba6d3bbcb6f4255dbb496dbd0d7cc2ff5c9dda4e31333d34d09ccd5d383f6c37a1b06dde5f6516094f414cce03fda117fb83d046bfd53e4b0f911d154baa1e163513f64d3040164de3553f443ae735ffca215c574a64a1d66d1bdbbf3f464cbf05cf5d4c863c7350f250ca4db97f10e15e3cce23a5e367660dcc7698bf04fd7785b53e7ffe903dcab388df9bd164b570fd8289c826d801477e7dc167a1bacdd23f51855fe3698ee34eebafd63656430a02017991323d699ddbe0c40ad736b6f5b0d9afde71addc1ba03ba4f15c0f1e10591a59cc78428ea3ba543db6e6a9c91a7032f1cf78e71a8a39f8b1fc2589a1c0434cd3882058696a00b6ff67d2916bd07a79815f8a0be3266916a994e64549fab60333c706e88c6f751b9cd495e0b3cd2da8ffef5b443d30fa792224fb1da347a85e8cc5bc9b17a39c35d1da250b757e0f421ba44b6a68faa362f92dcae227cbf02e115a70aa790c951b827803825fbabd017069426da8b9def622d6def57aa274926623877f86e9aad3980eb0d4fedeb4d297144a243dc40a46003ed212c1073b7fa3d3ca38001e7bfd75d384a17b5dc092ece3577ed2f39056604000000000000000000000000c554d7f95f42ef7260b4662b2803bf14525fa7f695939b096550f7de3243c676a35c61f2aa0af020181369611f104bd2adfdf1dee0d020ee1236ab492c3ed6df695dfe9d2c7c9c070cc898bd87076f2a1e1ca57e14e775103a8c9b7c5bf2d40a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ba2c2a31e0083e3775d251f594b221dcb0da6362fac9071704018d6d330f903cc7da4e76a252b2d838f83bf0ce1073ed2ca9b1c7b4c84e6d4c856bb661e87e525a54a430aecd1e159ce9bdf0aed8f11930ed216f241fb307168f103498dc8419aad1400736bc12638256e3f7baf6ca0ae0f681162c56b56767861aab91434c3c031408816dfcf9f3fcb4af6749bd9e2de381c7aa1b20cd0eaec9ed8f89c626feff020f4bede887047fdc194d49533fac21034b48d7380db332f193b4c2d15ab9cd280b0615f767b0d3525a7e8f0da0763942754f4ebf1fa3e4006fbcb06e05d624eeeb4c9888e9baddf72a891114f8b2163bd4c52454b9920b74eb232e6ef32486418c030fbce0743e5cbec4dbb1a6646865700feed98d93f162462c928ae5b0b532d75002294b96be4715ecfea204a1a7c3b133a7a84d7b92f97ad5ce5f358a938e09e5160204d54d6c562327caac774cbac2ccfdc0ebda235f9edb6a35d87b56f4ed2abc500329d0190510b052c13b3bdd2d19ae5bab0993b9b212824fc782a00ddc963639cc02056abe4390a6ebd747a4c9a26226fc3d89c8a180f7d72cfa421cb320f537a7da72934fe9050da7a5dcba19beded00d23d9bec4e4ee5727dd1653cfab5ad36c00323192d13e5d85586fdb8decd41577796f991bbb4ed68dec881010e8ff63703da3306e99c8c5271e4d173a9aa24cf9efd6431e497c70d0493a1f4380e2378f15b21e0905f4e57b4e3c93b7a6770fe973f3f016f862d83c01e049f336a972ad1fa7f7ddadb282bc779e6d8d72e9dfe2b0b35adfc2520b24050c59ef5024a2fa065b56231a4f02f3830f208ae3bd1a4b7d269134ab99368437a4774bb3f7163adb7bbe5caf07318cedc07a09cb1a6b5b10646c3dd641bcdd1f5520436f051f0d4ff816fe45e6aa452dd286bb6111cd35f74b7bf11b1d0e9625cc0add2feac161e350200e6a12abd37f21f4ed3cf73befc2f405a1f96aa9f2168bf0cec2e2f0494c68add67781711ce6bd31d2d95755e8aaa4a2ad9c7138ae2e39fbdec80e549bbedb779c89222fa1041656f5b57f8d417a4ae3c784e136e7f83bd3c7505d065d4dd554dc0aca966743dfb4d3ca0e6e983f52eda24742f5c812165885925255ceddec643a82afbb28088dc013852fd5655fbb5658768c9ec3955bb60be722a8d08528f2ffe62e492e73b38e7a895ff4b63dda99f552257c0a1d8e1c4149580c6cfba50e51ccb56aaac3659763c22638649a571188a0f41c631a7e652e6db0328989aef47f99baad19003b62c06190b233e9dc5079d4e0bb3399ddd0c2a9c8b9480ee4cdc0d59f12be36cb0f5b452d877b861b2beb69e6845f1867ea4eca01c9155cbbfcc3cf0b11efdad2f605f2d6b0fab5b11cf4ce953a2240dcbd5400a534b5a56a233fec518784dc07a9dff348a2ad8e2507ed5dda3b25fd1f84008ffc7b883faccffe4ab65123d5c907af5d1dc9724be1a8f5c2a96e202a772a561a4030d9e58328e5f79fe8de8f9167a7366d7852d46e7c5212f8ac189be3665fe80f61b539d16e93ff7dd7cfbb6cecc2f894813cd248e249eb50b38c55422dc3f6d0b054a91822154c1c7763f9d5e1cea02fa786572cb514efa438099a1acd31a3bb67df940f97106c82004f95280c1cc93a016d66ec740c09f9b14a3b75edbf8a25063a44ab076fb5cf2ae7afcc4f01c690be1ec0e16d5685082a76761b740ab71c5e45e71a0f34221ebbe50493f60943e40d03d67047c6523782e35c59ebce9d86b11d81c54df0841c3071be9a642872068f8a3da5c60219600fed0f4591d02b39ebf129bdeda265b939d44338de50ddcba17f7f220317fb16c86c6888e4b6bf72da60e631faa30d2837f077e6f7b2a462a86840bea8ae938bd597b97e8d248fd7e2a56a1a9d72eed7decbd049fe6046f1865a8910e94535e11941b56479e9b1a5dd6ae3c52c3bd21cc1e3f020455f970ddbcb7f8cb64813df1b0d6df321659eb939eb1d5d216973c5aaaa84efabfb030cb07e09802c952509f9ae284166887cd18c5e76e0977797c32594ce7169306aadd7f040325bb8f9d70695a6fe6f43dd67c6f0d360f9904a0ce452532ce3bbaf1fa72bfd22d403d9441c685d56b278cf83f1e6c755bced6f22c7e040280cf02c301f1272b3b954c879e8761c59591fac04b7fec4b8fc43e8016403fdefddd7ba55d05b51c3df72f1c11fec22569a398b5121ed552d70034b835b469a2509f0eb9779bd46610214640bb0bb662ae505fb81f68bef137cf46eb5a60a1260c2644752d514fdf9e40000000000000000700b0d000000000008c92b660ba26315ce71bf9cc36a03c41f5da181c79a24b9e6884ba4a10f70d5b879995213c6175815e22cc6b8e9a176997139ea4919a42261c339a2317d0dd82819b0dc718686488aa86771cac4e7f7d82d57961179bde1f91eaefbd2ff08440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012fd027d3f97b6689cd447841f94bb95ce5d7c4e358f2f2ea752030c67d8e3e4ab6ff86dcc6e800c1f939cb76260704401433af56c5293806407691b28cefc51d97092fefb8416e3ee3fc309a5cf4e0957f1e0dcd7159c9b9efdbc94d705811105622f61ed11958573dee78f7571239c7afdef42801ecb0311b1a51e19a0555b032b15a72eabd02c49aea2161ebfd4899cde7628bad7f4cac65adddd967f4da73c022e3928ac531b38e41c785094ed92e377791ddbc6f8dba5d33ea98ed92757455d0b00a01edc8999b0a1ca5f295f63c5a0e49369f86b9ce32e6c4878ddde744d2b2b7093996f35e46f5abe4e416ce025efc6d82385f7d6afd1c4d5d9630afbd351890314e32ce1d51e65e6e051a3e0e58216c7dddf5d3b07c8fd41e96d848936c0787502182a843fe3ea21f60a8c24a56b638599aaab8b16a9d5f42097768b3462ac173e031e035d771eea01d2bd1652093bd49d7fd8c62aae4a85e838fa112a8e30c60038020877780801d090de81e7684374ff2e4f094b0ed8c55054744aec64444da5a5030212247c8ae44760a9f823f5d010134fb5aa5de29225318d9d16c9a7b70fb9aa86e0c633410a584ed33bb0ced0e4d5359a87d6ce92450cda672ac8e927842ce08af12cbdca3f1cf8efd90901b579a4c3f38044416e0651516d982c0614b0494b3bccb02a3726321c9aabf1708afcda2a7345436e0822a7b173b392e3821097fbb3b931cef71164009883d3ccce24f537d084852c43f97e7a0fd0f4c6380e4907d7b45637ee973f6134ab1312783f3de2f01d5eec45a86990255ea66e2b4293bc84f5a53cae5a8e433c134bf52c68dd12b284870053b91f20ad23df2c747096702dd0b847008f0f385a2c4b4b93d9a18bdef4eb436dbd3dfaa73831ffe2b9af29c739ac550790f7f683adce027924af6f7e2f16ccef884b61978196f8ad8eba92fea4aef2559027f28ae64910724c0dab9e75ff959f519f29473a63d1a9c35823ff562a2722492f976691685d2e20ee6ae82338239624ac953d70a6c66484c3f6a6af4de35b60d5fc22587ac4def739ad4bd2d545febd0c79a226fde21b904471247b8479c99363297fbc2bb6fb658396e3e8f88f18189d9bc5ffee9a519dfda93298be8fc2fbf507eb780e80ab6a0787478e7afa58f80282f9192f254ad83046e0cfaafe44845254c8dc85c76b91a50cab864404c705447eadd77b8d873db7079f2fc322b68f83b0facd4cb914eaaafcbc51ab6f6c074f8f9b3326168be89c55373fe85e54217783766ed2f09fd1ccf5dc5092698a253c70de580ebf0d6b9755e1158cac65ee04a0d72b9be3425e626413b8451b0a344f4b8f1a12911810f50abfa204e9810765e380aa0272c6e60fc6fd98ef75e5dd18077e6dcb8aafe6f6536f426a08d0cf28d8c9c3677f2abfdfcd767a335b33e1fdf5207cea56085762cc78856184f604ced0262771840f1fa05dfd63b7ffc0cc9ac0bd416f3824dab333e191e22fc3b91f9938c720c1026000daf218f83f4fa3565a5cac2f2cf12391891f60323cafcbd713a7dc501c2aaeef79abbf6e0c85aa05b7071d88d62e5a6e65b22587cac70ddbeee3c895b95e0332d3297b8a8045c68700b19ae50631754eb8054d2db554a79f77c14a290938bd735425ed5b07b9490614074be003799f678f5dd789a7da0b1fcfae6e002eba140bdacdf700c0244e1101f02bf1126e8e05ee78e4591da10ce3f5c2c1609a3e0254c4480b5676088154b8ac542c5fcf1a0091ecc4d72b978f11939c610a04ac58b3f7ba47b8b990a49be6d4e325426b882e03b40ec3a79785a560c8e590a84e727968103c2cb086cfce506ce037d3c9d86f5ca040389050961730abd248956d1525f4f1aa34e19b053686f2b595413da2891c21e177ad330fa391a295f5598ea0cac4490cd348419d90f7ed5d7512f88b4bba3fc1ed94d49aa5bcc3bf6e572c007873143879ec83236f2e689911e334145235ea1a13fe783ee23bf9a5eba42a7641c358aee57a0aafa5fd1ce786156875fa7a1ab9a11faa327535c65faa8c9c04e6cc6972cb178d71bca1afc85e1bd88bb8345eeff9ad475c2e0def93c5cbbe100ab9d57b00e191ebd733972d57d7aca1330277a45de6a14f5524d272c343670d3f626b0c7994f8cebdd92af3341796000cfe722aa528b3b695465b6b79cd7dad549c34ad2ab851ee806ec0ae87d04c604e230f57e2eb7d1035b3d7f3a2e7c3b3fb41ffb92ccbede3709e2a6bb75a6d544496001441e83cdc5e28ebcccec55330cfb3b5ee90980b0b5fff901b06fc30459bf6e9d7091e6484d640179a519f216c4575de3cdb7f7533a0574b72a57b8d1cb0b8590052c07c496fd15db85f54b16b6bab0b0a84e7564e7d2ef5c250053e84617c7383d265384ecfedf668d536fd6232ac30e006", "6563005100ac63", 2, 2108154866, 1537743641, "5780af7489f22c27e42af9b26cc2e0acd2b4c3632e22a3cac66bcf54cfc78ee2"], + ["e913771302ece7c6306424e0bf065a8ec31a9761bd7637bf2d6e8961e1fec2b95d69aecafa02000000036a6300ffffffff67c0befdb86011def9920336669633a5b882f67aa9f66553399e0bc199f19b090000000003516500d5e897a202f988550200000000007cc2c60000000000096353520051526a000000000000020000000000000000ceb79c0000000000a472a979f454581ab1664648ff39eb01e6c769cdaf6690338cd3b43496db2921b2b87ec8589cbd4db8b1caf4f54a1d7c67f1cd4d00d7b37f62d3c2f8319bb09acef7e51d20e22ff0b594e1d59a1b2ff55f4b845f35b515971c898f6f4e027611000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004ed531e1e5cfb500de1e1a1193a5f2e0735585b78765f82b0e33793133e500cc0e89916c9abcc62aab64bd34df6b9ac6c7f2f091eade6d5e87dfc931f8b9b8cde3796c00d3596b134e9ba8e90e0a0c14975055f21a2a8d952d91440fe409058990a4382f86bd324f95d39b79129cffae7440009f614d5be9bcc357f4288920120329da5c5353ad7f03b55c6f51a13e0103caed212cf5b18c46a5bb10cf47c85f920204bcce8c9c8c11b53d145bb70ec8664e506bd3bddd524820efb504ab036061c50b062062f8114ba9b175238ef823159b1fbd55759f733e62a66a185113ba10d0d4ea09949e3396211f7c3a8ab33bd8531408cc9061d2e6c0d8af919a6956e98e4c032db039013fe0eda900c3f175de85464b6a765dd1b91225e56ab0b2faa2465dfe031ccdd1d839110f82c5c0329edf6750c014a9df4beb49fab74ddd7cff0067e2ec02013e5a240a26c9ffe538c77c2fcae43ddca0c53a8833f013138cfa75a58c72fb0201003b913d2b72a254e9bb9c41b8c72300d073c123ee3272a4f817627545e0360326fb916901c802a36bf55eccd46d7d488607d0e124ae2804442c4d86e0f88208bfa59401ef2f23bb1646eb39f632ebcc514ceff9f7aaa796f9d0536976380a61582e48249a8263ceaafc019e671bf69fa340647ed483de18bb22c3db816635a17b2b71297b31f8a652431615c39c8e74f55c33603988c277fc15d8f3c77e75054dca0b50c534af6c67f1466f01ecd688b23800379d7262df99e9d5e3500d45dbdaa51393fbaefd9f7a043126f0b2f0e6d0a8b87344f4e5049d919a2e5ae0332739d8b215764a4b4c32a5844aa2febaa95161ca1141a76cc342e3bca7ce7dfa02b9970fb3ec5b076f2d53ac2c7144c25ec0d78f709184295f4f7c2e6357279ccdc541eb22cc825ee10b376346b7b47ca5774b843cb7ff89133ff362c29c392ebb54f917ea60d4fba7db1d20f6dc872000d810eb779c4676ebfeee9a51bf9de72e2e447df7452b5cbc86f6eb7430f03089c4df1ad934a0b0c38365cf615b52eba9690280ee146279627a22058e968d051ed0d8c3bfc079f7fb5afce8f95853b4f3f87d7feae286e7ef26e61ac6c77a731b95c8102cf085e5350abd6a6c506db15eeb0af1fc383f8c5e6a52a792cd13f06ae5186e43509ae609a2aaa93cd0a167c24bcf2f88fc82e1a06cf17ab8b279c1d56551a382728d220c7b466087f82a38c6fd3766dab78468aa3a7d10aa69c723d19893ca303fe2b5eb3a710f5bcf264198fe17ddfea69ced1c2f83407ef3eefecf42accf7730b88213ad30ed06c87aada3e4c54029ef5183f062a89d90b556a18b0228d0f666f69fb38780ba2aef5a3f27a5fc8959de91bca58326d157515eb0483c701e64aba6c7e249905f9b2392fc31f0aaba6b0c0fbd9d708eec874aa90eb3acabe37256ba16f84f5039e056d865b2780191719d3b3fc576bedf1908b49612be2a3d67adbf3e6381d050044c4f5c728988137242b5784ad6c64bacf5c676e45e09cd18636d870aa224ab362eaa6bf93f4a7a2084f0786e2939c7bdd0c065199cd7c3234b3000b5b42d5ca91136de84b46f2ca356156d001495930c1fd34559b85e5137e0e6f04d0ccebfaa9a6c500f6bfb043b759c734a07e476717128d1a7a007e1c358d47508b47ac08aa913afd5ac86ae6713063007d89eb03a38017edc75a4deb7d87908d184f319e1fb24af548f624bf9fb8c4ea784fb7f72a897ffb91bd1e6448a8f957fb5c81bb3855eefee9313b094906f32ad18d74efadb76b239ea207e46fd4d320806753f95a895e3d7be70a9156e1d276681a2c2cec78c314779744caff45f2f0d7963bb02fc720506ff345ae504e9867793ce55e2d29e914617a53b1cfb2903e55350df9afbe89ad82ab1042903a7bfec26c1649fd41b9d585a114c82a3c16e9ff3454d0ea467f684b51e901f76b8fdc058a636064b4859d7fd1e9b1d7dcabb7b6ea9805496f15b8e7bde2abcd5679fe384661ab794fd5590b0d3716c26f2d99747553b73514676cb5fdc0d0c1bd9ea162e4b751c3068d248baf27e8e613973625d85ef90926a96f2bd56292aa642c2f5ad4a37e554ad80814721a2125fd2e13badd1c6673a3eb0e8afb476780710b66758cece512dfbe8d1e14cc0794133378dd58208549f4e4f50c9ebbadfd542ed21063388c3f20ef233c79c28fcbe92f08503858765d1371e85bc29bb95aaca50848550c8e0a40cf3d6c9faa5f2ca78dd751b3e999f342eca76ff47cac4e8f18935872d5cf3129ac85cced300000000000000000b96cf030000000075c126a347e0675975aa834606bd4b60cd56c72b8329f7ab31ca07290ac9cbdb25c01bbe862e0259c1f17750e18268ac2b737af7f910b45df4450bcc027f274d764dde98f256b30aa388cefeef7e2d977c8862cabd790dabf18afa3470965a0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000628a37ab0d913b2d907a2a25d85ddd60ea63259b6bc39337d785a14c662f9c6df0ec200025206757bc218479863652f9566e5f2768afba27043ee05bc6dc58403dd3c419760e5a887eabe00f19f2c141835b6758d35346d39284af9048e27eea6f9fc67de4c65478526055e44852926510c61cd0bdad912acf9bc6153c4ef7e3030c8a78d7f4fdd2855d93eac7c1997779a25131c798d9f7e05f265361c6f2aea0030e4c74e5ca96b11ca9447bb7303e0c06b14cca397c2f557f7b9152d4bca22b160b02465cb84efe4d05f2d6aebfbd55604a28e45ca84f65f4a343a2a9b94da0118273fef70edf51f3851031bcfad0395c2ca65fbd57db4e6764e3e903de97f7ffae032d178f13568e6b7aad6a975f2681d8d7bbc24a5a28bc06a89a4d2d9c029620d902304a611bb2cc852c5b8df655cb922db3960f3c6b94ef4ced3b39a48fe73d114a02129c6d8a5810e8f0dad8f604aa31bf3079c03259fb66732a397e4346207fed81020a711db19bc8bbc81769c3a26e5bbb1183c615ca8bf8e91b1d6716dac36646320330522e141b3bf5509e6e4a3fdd6b34a4a600a83f351fadf3db8c7dc936f1d72ea00336c070a2be2f58278e9cc4f262fe3b9ec7999a59e5bbfa5ea0578239e3b0100353fea9fec99aca5b4d7a7514369045991afe5e21f0f02dcb6c6b9b50a7dd63381e1ef2644c31561eb1d297822d06075362c4952a93c6ff9da70a6853e7611eb19359763283c81f2ebd76cc8e32bba81343b87144f0125ec6771e6858b230bc32963ff0ccd80b29842b423acd3458a1b19e88ebcd74e2fd91b441c9f126dc588a9c6f152694b8e21e61a1c9e3dbe6c32223c19aa0540e43c369081ef4206f4b939d4359da50080d0d3a41887aa08ba2506d4d4ab627b0298de398a0d1ecfa135bf2bc0f26367915922c8ca0f3cc83474757747642fdd95393b2320704374606409062238fe5fde29c46dee7c69ff455a97cd9d489df44558d0d9d565a0db8a97c46a622448ce593b70859ee0cb511be81dc172e44de2d0fd50ea5eac8f3c9277519c898ac9395d87b2b13edc7f98c23cc4b137f00c6da0ceea1662a3079517690571e232b1973f46bf912f4da21ac6b5f127573523f3cfd3b81325deeacb7965ffd76cd99d7d03189a572b918a162f211fc1204bfd16067897a143deb62f2a976ba53c0408d35caffb45239e5d19204715988f5adb6f143bfd29aea048cfcfb0dbbd4b5107766591e94b8e971782a585965106cb870a3a6db549d0ef41fc2daf8243cb0ccdeab283c7aaeac4634acd845912c91c46625255177e8ed8360ff890d9e97447a2050ae3185a2f09dd59642a6ec30369826e8a7799051792c73793eb5790d1ce9c4db274393bf4753c1f22f5181a7578eb03c2ff9eb42cdb39d42c693b445148beaab9dc37b3a214cafa98dcfe30eab999b0d21c831b56821d7dc973c2ba4ecec07447c781946b0c8fb35e5ef1bfb232fae5da5a243965dd288d21245bc3753e42bf47eec4de420ca7d1d4a734400858953ce0703e7470336ad49846b21e3a0c7b40c7fe7930f2a36e65ef514fba66e8a0c75285d2750e814edab546cb535ae60a782e9aa4ea7ac08e117732025f34fbc6b7eb942135a1aa3e8e3b1b4c669c4ea77a3580a8cab3e11595fca6ba35ca14af125b7eb42e69e80c0b3e13937c923bcacbadd2a2857c0bd12f0278421d6dfbc889e7934c1df5c7261bd1f39590f910402876a8660c6e6cf1ec71fd3bbe786e0e8a3f7f359d9b2b07a01731d1f8940de316c6deed19746278a77a22cce1ac15c2dbc5424c722ee6bb275e2a1c7e8898e35477ec23a208d065fbdb6687782468f5417e63c6f0e2d9464bcebbfc68387bcebe50a3647f63dafbd5bc9d6a5bab247f22ccb5da8624e01196374a5a01aee9cb2ea8d98950bfe86f03ca8a0138df7672e380b97c713d7f8a585d4ee45cc87cf85228bbc6df74ef7081355562bab72aa1503282ab964702134412fbb7ddf7f63e758b29fd519ebaa9dbb7841debed4050cd2cd6c5bc8f443f40605f5fe2957eace39c929068f924fc97d239868bb55006e407f06f5d2be7d3cf6eca38c2b36a1b6ce9c63c2ca63b6af143c427f46bc64f346c5227db5cf8720aa5e98191490e0f31364ba9f66f3b28524436b676c628d46918d557796dd50c8cf5b07139c2912c93043c8364a179047fb7d449fb847d81f436affb89aae69d2208f285987e2c9f43c24dfb7dbd523191d4f3f2203048dc5b9942a41cd460757cf64232a20ed29f23495214d8e333ab6a0daab8549bb09a6f5af452b21459d2f49a566249e1b2b20d8f3be7bd9b0035cb01cbe624e66e86849995c305d863976fc3abe4d683c94aba94e0d8ead97c26b29877743db06ad01ef5cf4571ffedc3dd8e415be9b94fa3c5d941f557b0ba474e85306", "6a005351ac", 1, -1648583335, 1537743641, "4ae3d4613e33ce235ee3dbdeb63f8bcaaef7c8858acd031802242a65bd31fa07"], + ["030000807082c4030376c7cea2cf8ce7add2a641b30b07636b23517870d67a39cf188f5db816a6844c03000000015393e320fc0a442550c8b287f892c67770dad471cffa0b7d3fac46e52279a4ee59614be1c90200000000690676d3297a62d7a442dc88c117372b3e273ccd3e2f4457753e646fba05075d3383be40020000000952516a656300636352bda0ade704714f43000000000007635365526aac00a31854050000000004000053519f40d5030000000002656af3428504000000000463636aac000000000000000000", "515252", 0, -812182387, 1537743641, "70b892580218d49d38e0344c6cd51ed289abb9308cde1aae3f4352a9175a4ade"], + ["4e913e0604161f4a6e972dde2aeefcc970c7d821d7f27a9da45a4f542304cc6118a2061d97020000000151dc744d9fae924dcf4af8a81bb0db49d6a49d7f7782949b2b11384d31573e0295c748f0d40000000003535253451ec13f9255b3f31bfa8e8de81c8cc7e0895cb72a331e55a29ba60407401023c47426bf01000000085163525365516a00ffffffff0797d0eabc7fbfffecc633e8846cbba2aa04cb09fe32c2cf5e9a981e1e533a0b010000000100f4f5a2f8042fa34c01000000000351510010e12a03000000000653006352acacba6878030000000004ac53ac00951404040000000005536352526a0cbdbc7e033f7acb0400000000000000000000000061965221effa7fc06a25dbcadd23849750cc55cfcd184fe25a057520a93c12b64714fdf9066db3cb27306dd49eb94bda88382b24631c6b8cb3b61efe45befdf266439c98cefa1f6baf85aa58bac622dd0f87948c368033e109271f295cb9ce0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d2bd1875bf1756a99fad9fe0d24c34eba860feea26baaad769786ff893cbfc0a6dc73a6c83a270bcd53afe678585d89a4e731d8b307148a5609758985c457d45fe13819d005d84e56459f564f7abf8cfec88c6990d20896ee6966c36873f7acc32d516c2a08f07da4b6a9f4f0992bffe949b95d78da010fd1ebdca78dcb20b360219111d50dca92fb1f5bb8a91a000f9a80109a20d39fbfaf9479327402d112e840309b997c71bff175c686cb0a8f22cf5dc662c5024e1cdb8f161a2e7ae825722370b02081c911fd70d9b56286eaa6e4533c682d6d9f46e3105947a7d8d4d3366d21035e9faafc38a9733e591cc2f61906cdba7d39a5dea730af7e811db90287f3bc50211452d4158e386f1bb78c7ad0816767c345c5fcfe3fcef40ba2171fee8bf63110222ad8d18279e898219018d7ed6c38ff7d0d97cd7585d65f94da8eaa427e9ab51031bb32009d3b916e4c5b198573238276e75688c11c51c61b5a7d1a7a35a1d1e72022ce53fd916656538add42edf46b8d785406948185b683c1f93dc963f517dd1c502151c2971bfd780a503f0f6b88acd452c460fef264ec2a284ce12684c0fc03c14aed7521f256fd4b4a14e80a1ba7de915c6437d15a0866f8acec5d84d01e1da013cf06c5acb2479c3663162d2921c598022744d93e23f27ddf99f2aad904f2bd3cf8e26027acd9eef47f59cb783af0a13f0a632a96b60c4fc0ad88294d61443a398fb4a6bea1d0f1e9f1998b85c3ef3bcc1e3c4bbd47f73cf526b367fbf16e5511dcc3ac8e507751b617d161310cfd67baa3f48f2039be877b3637ec9d873f764833475017ee3febebef3494015228e9fb51ae035cb01c95088169c0eac8b4662ee4c168c04015c6ba054e90f767ebe4d7f8e9f08f7aefb8fcc781c634b9f65e5a2fe403dabf186caed3d2936ec1725a6c39cec6ee48e48b6e875adec016f71b5df9e64b47bb2db345157b54fd5ed8644a89e8c49e71d43d9f7f467ea026b4d8561d8a6c5203ba30546d668d8f978e2e9e1f224d1c17f8974d22c376bbcda1cf84a14a879e236f59ff32714d1e9b7bac3d6012848116b4a27dbc7b1d82cf9fb1f521bdbc623b33ab477f2e9ab162a1dc2c5eff8ef21f3c26427f405a8cd2b8ad410cde0df4a0fcf11fb7280c9eb776dd0b0cc30465340b1608bda99dd0be5754b65123f50b1ef99904f9ac7b1b6b68a7da86b4c53583449ac444918b3b7f6a11da35c151c61d52cad2013400cf3788985af6e344f42e8f2d0dcb43cd9c6a24c4224f67373030702ed7d7b68131853af2dea483cb951cb44daed2560e8135e855c8b647de4a64caec451bcace9268dcb5c137ee65ff4930a1977f604314b568c956a53ec4a733c05f783941f5ee1d2b1d88a27349e3f56be9a4446f0f77dcbf64a958b424454750c33a11d584b029b8d0b674522bc0522012620596490860378b0735155bfbf2c48cb476ea5b50e98bc4148351be368c1f22a57920741101c60233232c3c51ad7b8d6942995ef62654141e77b94a01d67147f46742a487b40a668910352c72f08de6c13105c8fd3d1f65de7380b0c853f2a813a76b720e4415de90b1515e3a237b7cd783c07dffe616eac06ac7d6327ec6524dd43426e647846049c03ad5ebcc061fa622ee9eae80eb27b632c3611c6471004315333716827e7a78ede1b8aea2bd364dda02a4a4eb447456fe4de9838c5b500aa595eff13bce2f769ddf33a0f6454d3d4f9b4ca17bbc92ba65b1c42856ef05decf788d827b0097334d2c80c8d5da155e3073d4fb2cd19b450e59a95e0d9e7f4f4a03282b204743a317371a09fe44cbc5a35f7f91936e35dda6d7bdd7b3b25310b5d35f499c76a99ab132b1925f0ff4ffa75d919dcae18a4b1641583153759def04a0421c4843f2a74d6cc4458d0c91da8b210371e53505d33bac59381883b59dfa701b6832e71836da5c9b9027952a6b9bc4a8fbee365a4fddae83948887d7ae6c9ccfbe4c0e61abe318a0a9f7729598131132ef3580d2454e9cc35e80954901c121ae582d78b86a4c751e00739c2fd1fe5b5f5e690ad6c36c20677c130662f7dd6d100b00f4eeabd8e2115ca570c6cd92ad58cbc1bef733f6d7ea8102c7e9425559f2f6dddc2e22e6d073d23307324034bd7d760d864bdadc269c33bf2de3c689f75504f415ae4f7d04279223899fc1e629b849ded1e3b270e09cb660961f704c744381507497b71ad54d4c6b5301eb965daab554b3de221a47bfcb70c962aeca548a4ab3707729c348788f19542b3b46b3c0196f491e6506abce9f204000000000000000000000000e6a19931010041ffdca5a55ab5877da389c8b9e8c2dba2500f88cde749e733e27ea3e5b252c2b8cd16dc7790113dd2591f73bc33e63288806fcca10d97a51abc594493c49cfef2f4bd30d427c9f0436b08ac875f86e65db181a8c469191312d6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009b315e1ca9ce820aba6a9f6f6817f4bf94d33b94eacaa29254bff3e81d5e19e7f3de3a49ac38a93c969692402b67247802618630d78b1aa46a1bcb4eb3e9e738e293341577c8071e4e5483156bc688a512ef0a025b7e946d3a69c6f2c47692d33488078f4b46bd1a380cb5735d3be0d89b35776dae1a0a6485d93ed0b78c7276032fe55ec742b6f68cb77c2b1855d353e0194addeb895bb5e9aba85f3469dc30780219ac86dde23953aef57eafaa343abda574efa672e5eaaa492e2936f10c3d11bf0b0881070c3d891d810d779ce40792b687f21d7b8c73ff37f9fa5f0064d14f9d2958bf0edc5ea8c5fd5d0e0ac9ee5c632f792a6cb9dfe9c572fc3ceb409ab003f70207942387f2f301ef1e8c7fa0ab331e14707700dc9cfb1be48eb1b85296792a610327a5090d02f467ac39e8c4ecdfd43b3241988577e3766578f091189fe394e04d022e9a74d32f50f6e2aabf68680825b4d6968dc754730f2d190b9a81d383cc61fb021a6ae6c92b8d8c8baf933154bc4206aae0ba05030d66bc79b8defcbf290ba9f0020f27227640a7e183fadcc473964db245fa262e61c23650b51dd14982f128e85aea2c40086f336ad08907ae5f375c29570eaf6bd0c0a99f403d7885364809db39f1adb59880a4814bf2c0cececca729b0e6557416175863d76322068a73d23e7fa819334166768301499799906aac20d548ab8bb943b52561b313cb28a23e554c07ae4d4f8443472ece2494c5e9e0db67ff5ebc4972872746fe316fa076b881c9776132558e6f3a4faf7a2fe282b69e3e93903c02cdcf6735651acfe3bbc1ae184de373ef75e0a4986ebce13209458bab884fd526be29340f5a198ad3dfc23db4d3846968ac37c2590677dc8d6b5b1fa7a343254d52fa25ed0fb6b3071e1b09085f43e496ea96912aae3814fc22dc32ba5afc4968542168ca5f1b1cde31d19d9131451d44dc03361a151255b81bc29c04e9cfb4b2aa97a38ca02f804dc12607b8105f7891838f785d8c341da620905f815125674da7c1b9d61d7bf03e80c10d87ea90e844d820d0ad83511cdcb37ab143ea7e4d5d28ad2153f389ad1e87d4656a896871313fd3ea5e70c769e0e94ab9fe691d40b9fb518ffb0ec592d112d4ddc6fc168e5643fb04da04b6c51502a0d5f07e128f6fd52a0bde3588308fa5d249c2d3b99fb94f169e1372ea9990499f34f9516b36fbe4b9976c7cc8d09d2b089bad59597d69e2ad9bb82e7183de4c758fc1a8502ed77731812d1d8f802ce9660b14212e0d28278fe54b0f6dacfe50d47dfbea77ffbc589a82ddbe16abe6e7ef86b2f90ec8f4634dcaaf357457d7c97fae65dc73e25fde1c1257107a0982be925fb97df6ccc9e5512821e490ffd8e0729d8a78702cfc5154b7c7d78ea425bf5d2901663d9c4bc4efb93e474c540779aa440e60b1d15406ba6adb4dddad6efd092ac6781c20c97ef89d1c70b58b4a2d3075b13f9c3063053a580889cd2a8cfffe1ee3eada9f0388abebc16fb29e955d22a25ae521b12809bffa58935ee199a03bf5b36c54dfff1092c824bb659076d07f31e23f88442dd8c5e271895246c7d0a3dfe7599754af23e8ca0b79849e87e0e793a0194ae03b06216965d9314a4ea41ea2109f7b137d5a9e5071ea15d6869e1bdcb1147bd3da4caf29e7f069538a2dcad9de59b834b89be470b089c468fd8b1889fc00c1ad82cf67904b7ccde7a284cfec3cfda881f6b5252f1a7a6ef8297e9100dda0670965a928f10784c00c492657d44499b38dccccd5eb10bdf321d0e66f2fdeb5245e000ea4ee110d40983d6338983895be50a9ed08e3dc8f04becd0c74625ccd7872e691bc7089371fe66f81e84af183ad679c423cfe8c8f50cbaa07d6449cefb48af69eabf175d8cd28ec2b7c26df3f12390cb794a311220bd098b68f04839bda8c0e07b42a1e100e1d4fbdf90a90eaf60e801be9d1a1771ae9e8c01d37369d8f9d2dd7cca7c424a3f51fc39b721d2078adfdb6d22b0b1120d3dc2c2a53f9554849fc490e0fe7d637caecbd09d86bdfb8efa8741865c3ad15522c486f6d99974be9b9cf18a6a5e8f6a8ed412d2c723842cc9d25e797fa6cb8cd68a26dd508ddd0de2624e6bf468b6e84feecb883dbdff884a9480f05c9fd077177a00db694c68f002447d615596a6a922cc62a0041e5821b852f2e0f7be0aefe2e474b1db03e1bb467de005ef8ea6f08e53b6a281264f5cbbe805fcf8b2e2b0d23d3d96eec947b7179b0c496b480dc5d2b8715d3045e5b6b096a8353856b0dda14ccf18e76f27bcd69a00100000000000000000000000073a7e29e76f5f8bba0932239fdc8557b07c000a85c59a10e826509d28bb22aa72302470aed796abe27dce6c3a29200423d1561a7d5f55b9082537a9cd16e470b2df8a130e8d8ece21b2571db0ffc80db74f53cbebee80fd220f23695e34490ce00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000399215ff0efdff79fe5e4b961052932fb8c97b667327699bc7ddbb65e6e477d088f4a7707901c93c8f1aa9ecb75b636d1c5f897022d558968414aa394026a4e21c543c303f327c0cdd698a71bba5d5fb0496138ea129894e02c5e28dcff379714b0cd18192d54a362f99e2fa8e38031976d0055f8e7f349db070d6bf312833eb021e7af66a39ec70b589937676d91f3aba68536393e2a079f11114dfb77ad76dbc03053be665dc9bafac50fb56e435c9c780e8ee6e2dc254dd5d46142727d03fef600b08c7aea0b5a6ae0984a500bbc96d4a3b3f9e1021ba0fea1863c6f281cb28c530dac8b3aad06c1186ca664da18ef52a09215d993ea70f6aec0d2fbb567a6e06920316b06a6b26f6532b6c74df636576a60464b7f38f8625277ea62599eb831b9c52030f1b88d33b39e9edc2f1f431f9dbf98bdf683d6a99eb2869556855d8ed0a39110207344fa0491275d25fbc4c3ceef1ae6fddff14ef5bc1820252abe8218c5750dd031b21d5ce3e290390427bc37d79f695b9dd4cbfe75280cca0173bc0c97a33592003227ab6661b84015b555164e641c2fa049314720b62208c82f8a496ee615b5f2257d58703c5f588c0953e0ad5fe2a57cc8a42ca6e6d7b419c74cf6a38ed7f9e7a66dcd8830a34f0c820cf40104547bd545a21dec48995cdcb9ab9519117deebe21bc05c474f7652423135229370ad6116a6b85d03a5bbdff2248c3debaf2f50c83340f65a57aaaff16aa225e34e0c7baf71bb635da38d8ac26da497c16b3af2dbe4abe7ebf0da323121b2b2b769e659f070299327eeabfa4b9fcdf749516b3217b045d18447b1f87c42a94d7a76c0bbd89f6e3d5cfd39d1fdf0f6bc27552e42949b21a9b0886ce6531929f5a3b68eeb4a4bf98b556094f74bd6ecaa53df1ae1f8c9be362a11115f397d52b2f1c2665ff220b1880b5dd0a198a211dde8e8fed55a0cbf64494137fcc743486c721a2c6ba8247fab697abfc4db93d5ff269982753bc4d7e897c946884db70ce73db5531c60cd63ad0058cd5cacd5a436f0ef3398692a20d692a5650707dfed616b08853cc8b123acd1cf317d477f117fe050fe907f82ae5e181eecf13b22d127b00a43e52e1afb02137d217a997de69363861e7961f01386ba3da3ea589b22f10afbd0c938ca4f280b597b40a4ff472b3d4c34695f105e1f3e8ce74948af0429c61650fdd87b6eb0f0b88e8ca4a18823f1523e84fa00d335c54d8b3cddafdf1d60f6641a131be6ec5edfdc5b780bb6eb5596a743131348b0e41ebff5d297071f6d37d5e5e56a124eae0d422400b387a0f295bf16f27ff1a491556e5283d8b9b3474941be12bb69afc223f0e0d1958c4f9fc083d03cc48cf2232fed901c24e3b4cc97c8f88a49132263e328acab174444deee859c62aae72a18bf25e17c28b6e018fd3aacb2a38508b509fc76584cd9197f2471feb2883a3785fd6dd8b3e05af30e3729ff61352cc10ad86d04ea14f20ab9e561bdaf65feb6c7aaed2413b08590fc7e82812f278258a6a36655331d633472c9c91c0bc68687cc3246b8207c2649822a1852cd22b7143bc02bbab59d9a8cbd2520af6cf5af4270817cea40d31567856038833b037913597c72bb528bc0bcb87cfb8095be314cfbf3e067365941c9c2e93c8132f528550fe645acc54aa276383489f20be5e089fc5d24acca45059e0bb025a6395d724d4045c73e250cc3bcc8efa73885f3032ebfa2731aab09f89fec695698fd3edfb2df8207cfab6cedc2efcd74578e447c25c67fff1b75256b5a64aaad6632e63a187a1d0c8cfc323147719f5a8830e45a7832bf9ab407ee46ce562be16aa21b564a9128ce08e8ea8843b343dac22d002d1ce83c67e283ef770bbee3863792061fa1f7a5e65964c94324c3c69212a47c81bb1ad5db2f87dac7a3a09684d23b5d1b279db146d6c411671b700558e55696fcb4257ae51d3ac2c7712ff24c09676733d76bc1f88f12af3cb93b280d5503d8b00ae07fbdd08a7b6de258e10639e9c54f11a1759f0352df2834eae3afc6ecfd5dc745d70ecd4d3688525cb79d10664b6ae65fc4cd8f9616aff0eb8b41ae13759d2e5aed122828060f05f0bac9c0fe20ecadd93aee25906fdff3dc6bad85a720ac895f07cbe0ea42a364edcff4539b638208806527f21ce6fa10e991206186b92696cfce6e03d1be68df791621367a80e78d39856a58c16ffa2cc9a17a6405be564bba82776b114f02a483943a3b12eff6feb1b3d9e82f35888003221ccb21489f53a6ae77ccdfe343cc0f76c793214525021d2f80116078f76e35c4d51cb97cd1c14a61bb6638accad3559b91df1623705c9242e5b7ae4fb3a7cb6427f7712be3a4ee9a366a60ab1cd95bedf7c6c4d79e1d5f970ad6be689d03644fd0895aa85f2a273e1a3b3d745178baa6a3f0af6402", "", 0, -2106854420, 0, "ae35bd5eb6d6a1a242cfab515628b3426f65db0c8f38c28a2390bd0ede324673"], + ["030000807082c403032e55017c69926356baf84d5a05232093e90341f745d8479d486d5babecccfdc2010000000965635163526a63ac00ffffffff2bfea2b091dd1cb124341421a81a370efc1495e4fc90c57f0ffb70aba1a3ca7e0300000006516500ac65ac62fe2a9797246501c66ede45e171b7761a7a44a9423f49230a46322621f11932ea144afd0000000005525153536331ef28ee01ae0d7c0200000000045152650046242cac0000000003c1af1d040000000000000000000000003c455071a8f80e2fbb71ee1880c8159d0a668df04f2dbef55a6d310ce0819e607274b65b8f745af9aa8341e49e3cd20ec18d71354a04214820c9d5ec985ce2ff773196e7d8a3b06ab320975a1796ab1e8ee79000679dd2b637367269f05ad1cd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c73f38ee2c075072d93bda31dbb11fc872720a2c0f35d9e65771a01716e3cea630eb1ae171d9a8d2d79f13e3ab7147aba427df52992651d1eed2bcd9bc7de0fe6380654bc6983a91322f50834766c4c14cc76757fdcb052ff0e75ff809a311c2ef42d71dca9510251d5b8ec9268a714d8d6a397b0ca638caf18d864e0f0f8bdd0223e857ab61e7e043f69e242284b80fcb9d02b4e5b7eda2311e61960112358e1a030c53f86c4e899fc7aeee457ab169b301e818636294ac91c0a8eb52ecbe9910590b009f1bfa94539d5cf0e1a10cbddb4204a1b5850b9bab50a645600fbc50e9ba1b8b621760ab6dd12d4b20b3f863994016e08086d3c1575f55e97f09b88c6b55d30225cc0188e7e9ea2e58b8e30ae5e85390db6f28fa1b822e84113218dece6d0005031ab6d032361deafb1b160d228eed8af1ba3949bce35e9b9e0bf1b8f25860612c0318a2fa4a6c59c8efbfca5d649a846fc795ffe2092a9ab07559e37b8d161993520214ccbc1351c7330155cc97d5db84e0f381f27e3c39ecc414bbf8c1953d0f5a6e03131abe87f8c9fb0ffa2d2d0539edcfde6d5bbf7447158897a9406a1d08557491e001ad23dd83fa938c6bd2055c041011f7780c30a14b4e8cf7b86ab913727621a6f6f916b127970529cdd0f0b8d3fa8d0df69991e5e4b5ed43e9d40c8165187615bd0fe5a43b664b02df7f0051635c1cb60b21bd953bca5a4421e81bd3bb634d02883c5e977714b8e6182815154df97593eecf152db1d096c8af81f5ae8db5772fc428c0fc300d39b3a384b3bce857b41a72a9e1a06e7387b6559df1e57a501558ad7e1e4d545678fbdd01ddd9583955d9a747e271f4016fa825c1dab993bb0d28e491b27214973ff768801556b36b8c9fe71dd1a3f1819df2cad896fd1119889231b8c445a43c7eac2233ed1ec0a861e7c8e646be84fb72c8eb7d546161fb80030e52307455010d7307b3a42a9dadcadd5914a63902c16d0e7569f418c5aaaa661b73a1bc78ac8707d9abaafbda0d360c76500fdaa6c22199073a91c2c25b3bc0801e25a97f0649c1442b01608e03d98da0d6f85c0cc79deffbb7f4a05123ffafc6cd7d4c58ad9aeaf9dc0dcadcc30f8efce4972578b94c6f1ed75fa1c375b2460d349fad3c49d985cef0ed00cd453887e621ba978b336eadb471077ba7583c78322aca0acaacd87c2e57cd967ab07090e793885e0206d67e9d93e80da1aaaddd3d711323702eb21382a084527ee0ebad65a9d1355e5de220c861a4b6b9e03e05ee6b0637fa522308fbd50b4488a5f391a91a9a9878765ca5edf0fc7f287434a72d121c8b8e5c1b078bd2d894924b9b3274e535d64f418b2cd016f1186144ef0499fa337df5dbc284e4e92680926e75bfd23ccfda4b1fa3ba78337a7e35221c4b8ae02b8839b4cd9351c2e282c7fd45c945346f144e0e07876638aaa07f739625696bfac8fd55f30095f9a45b49743976db1117f968bcd17f41cf33b9d01d25a93c70b6ac2ada4a4790da3682036f097f307383cb669e5f4aca217ea1f7c91c54b5fa0f7e76203cbadc7add1d8faa38e07db3fdb3466a0c01917c768a5839ac74323dda1322a01bd9f993e2b58661845c4c9cfdbc993dc9e6b091ad3210536d05d47a4d0a2605d2e564858dbe67cc2a02f39eb5ae36d4b374b057d5b83890d89b73e755fd152d884d137482c2db747ae94cbaf1b84d1e13671c8266582140823ff9391841eacef83bf1a910b99293a72e640dfd1a6092127578b1dc93d8efac23af9dbeeecb58b10a3cd5538dcb8a0e24898af0d1a32795f83feca2bbe1fda17907454cdbcf586f6862995358a7fccdda8ff6809bb75c165feedfa401ada27cb63988a9b3f673cd7972e877bdd82885e4e6020967f4b4fcb69865563d5c7016f1ae19a2082f4cc6571e39eea49310284771b622b44c2af0c47e80e0686f29f411a755fdc063e57704132ea829b3c662701b3299748aa4fb100edd5191afc229d9fe0d456218a1499567cd8bfa3336ea876dcb96b1119322f3985a20257b020fd84e781d92fcb2634c2c7432f7561c4beb01777fcac8c7bca36472236de083862d4c4cc5ba241ac1c194929528bf7fe513904ead50d585a06fa2b1697f3bd0d710effb7be1eb80d7440db7059bbd8b558d4447ad570ae8c3aa8fa9309352d18c19a20872957d5358f88aaf320a94733389caa8b40124539f1e3844977fbdcd510cdd94f8b09cf270cb5f6e3b63768f2ea44e81dc4701327ad7d50eabd35a15c3dcec7fe999b7eb493a825878f09152da5ce6bbac5002000000000000000000000000f799c1c12eafb72f361d7d435eb37a1900b99fb26192c2f56dbddc9b9de933006890db51c74df590047e245007f4d31276cdf236825b771a0d92262afe898f42123cc585956915cd2d66db13b73c2dac41369e5eb99ab53ae1072b5da57130d600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0395737787fa8cfe52d373b7982f0818be07bc7bf7ca3103212a2734a0b7780576a497c075b07d61e1fe5c2e8b53bb8e31348467f2f3a32ea9cdb0c82070fda13df3e61323b95672003a206bc08f94b1945d3dcf3c7ed3d30753f889074e18636dd511e9617304af9be24546985f55cf7477984dfca108f0f6c5b5a4c327253030b766648b15c43e2034aeb19d24879f663ed91922e61dc2f6b1027d079d292dc0325d535c97748efadb5bd26c01600ec1f4aac10978eac28a18554b5941824ee880b0750dfcaf45eddf610b976d9adb04cebae7ee1ef47e4e0249311211e517f6aba8cc690876a16539f68943a31c0978aaefd4f63614fd4889e8786f66d15edea7b03290013e10426c6376758ca87c5e40a7b6c93785e772fd3282b9ac493f4e44ac70210dea2bc84be5fe725d44db2881a737f89c3ae13f650a509af328d500b4d61980224eb1ddf0ce3aaaf820c63e3e49f0a946190d6135f217245681c2c309d60e268022b5a29d39d70105919bbb106dac07e8991755d5c100c97efdb1d78bae018682a0310732597c1df068f3b0627287848ff847b989545dcca1a92502198d3d8d31349b53ee1f8292f99b14f80b82ad48cdc0619cc229e2331a37efd6534809a105e94058b640671c1f11edc09cb5e4379f6d3e4d2dbcd012e91da5058c7dff474bf364e587b91bb7f076d7ae4b425b5af8402caa5efce7e4fadb024f0d4e0b75764e04bf54458a1eb5f752d3152eea57ee5dd5f3a3f7fcd18544080c9594ccc1b53da1b80a853923e1e55fbf201eb5a05c775ed48097bbe8cfda8e0720e52fd41f79a3405ad3241b7e3d08f0998c119469601cab6b3ac1418774340e9954a738cd80068c00d1b5052030c5faee4786ed644597fc512759d682987ebc9bc13c42cf5fcb2485b789fb18046f349e85886aa7be0a88f26f0717c84b7eb74acadf805764a3fcefb830fc2e536305b9562de9cebb3deb2e4e8050615bd18ef65a49dc72c27d722c24ca333d0bdda3ee210bf9a5e044cbbf045cbb422e32c1999f366eae93b4ef7fba5c564e7725f833687430f2196c40c5895ac4f74a191e30e0f45278397c228b74423f80c16660b989d2fd3384c5e4597f4d970471caebc454a7e01a4eb8dc906eeb800eb9d60d1051cd414c9b2cebde018551f34c59be575db406352db8422e0bab58707b65d21740a036de4584181f0043e78b15f8a195ac987ba2acc7e6c5699fc7ffc86bcdcc10f6d80a5be04abfff23ac47aa1aa284335f498eff225d5b209da863722d7f602e661553a33550c933d4178fda95510ac6696f9d50b5cdb28ffdd93f8805edcb1386d3fbb9d223a9dca0d15098995145cc3308b3f09939b56c80b7e9d0d823c89d615aa47960cb08bbd840a165a8bc3ab1a01f0f9ccb3325482ece55a34d7b84f93429e60dee98820198c5319ad5bbf45f1dd1af00eab9d1a8f8e7191dbf02c40c3a7b665975a35c61317ab9027469315b681f75cc26a1ce431c754e30b9118a6b48e46e50746e301cd366acfc12bb338d877d79cf8d1dc63677d981fe5376cd48637c56556983b9fde45f3fec938a072d7fa0b2537655f50b7a7d236f83c0eaaf5d59e834af23380ab053e67e55221a01c9e85bf35ce8af69d3d9e0a0e91286a387c54bf3ae70030130588c26c9d10e8b810815fcafeea6dc3795bf7512b454926762829313ffce62fbb81cc9e6826aa6528791debe1c13cd303421d0f2b00dba65110efedd4486f18ca25d21fc371d9039a0c04a84371aa966ce9a4cfe27c3f7bd03ff00f003cdc3ef775803b5e73de74d3159ceecb459e9129b17b1d0fc75ff79996814f779c7e65aa29895596c85360bb4938001a3f8d8f26eb8d0616714a113fa3c88e82156217f0bd419c66c3e955131f80951bfc5ded6322b38f0c215fddae74036445e2b79282882b1c2948aa7d732b61d90407629609920d042434176fa181208b96366835973dd0b58ed9110ca6f057d908e839be83776f45ba99395bd2652546bf42643fdeeddbea83cc6df25619705e14d3aee67a7eb81a2c8c9e5dd1ae3a6077b4830669fe2777485d40f5da3f7a39aaa9eeccf14e266446db5cccb6436079b9c9ecde1eda92eb535463ed68dacb437b78d8e16066214d9cb40730b87930ae108189388815a219811788691eef3f53ab56606e3fedc760e1dbd8fd8124a39f69be08d232776d24aff65ff8643615dff011e6639a6ae8d1ce381b1a14f199d7d3bccb4e9af0b7215086facd691a603ed1bdf3b55fdac60b6312b140ab4b517733ce0000000000000000ba8b6505000000002db3ff72595543dfef70ea5d38b48a8ed9523622ae24cc895683fe42e82c8b9079c87bbe31d06ec4cfeeab529b7f6aba667214d7b24fe663dca614e9365e8b6bbb6538dee22adbd1747a3f3a1f4f0617b2cb7491db95f12bb3c3b49b08e02e8b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f726bfc2aca280408c1b5f6689b5ce72cf3028cbe97c59790371540f968390fe97acd9d2a1e558e126cca52da3346f2a545017e2ea284aa7a8224c6a8f1754c83222faf173bb7d186f30017caa5003071d58cc2eddc85cdcf2a8f4e8f2d580f0a833368a29427350f4c77bd4cba04279acb37e31d3c3ccb0bea5d338d88fa022a69750cfe540600bb399fcc69bd52288aa63224405f3127839d701b4d0a804003247f0cda8c2e57e85e8cf787c1c1baac64adf7f70bccb491eeec52162e40113d0b04f4043413b7f4c71d177fc7590c61f6a2e7a4d02dedc8be304466f2f24bace7fc7e2801ebe46dfe4f82e7c850635fe27ed77287a8c96dfaa58ef02ee903f51d030abce5974f5a6a1143b5b79145e48c0010af012191040130840a6b1d8ed89a93032f9d7a5aab7791bc3d4f0d16e0ce33f2ed97bb8ae1177bf89732d7293965fba80306dbfc2b3baef42cad42229ea3980619d0797cef9527e8688cad00f834f18f1102182a0cc6cdb3a02900d1ecd5bb5601bdac6d32e44d7f423b6161fea6708a8df403264c9503f6d38002a6334c70deb27de66817b15ebfd0c6dc1d4221dd287fda85ba79417a39999390cabb3115f9d13625693a72f16e15622ed8a9d538c784a783114e7f8e85acf3bf08f17de2d58a01738cbfd51cbeb35dbd138b513a2328e542732501f22b1cc46d712a5d9f22bbe8edd81fbe73f80759c51c91b553900c682484e51b04265374bfc3845d09a0e90decbbf9c580ef1bbf53c29c47464921685f2cec8750297c70d3b4b1a2e666646b53d68e41919679c920b0009dba85029222b57a77cc5f812aa22c965380961fc84dfd83cd12fd4c59a2667a0322b3aa2adf5a3140f228041d78ea408d7f574767112dbabf5d7395177505199fe457ca5c0c601970f3fc5faf91b94f4ff67595ec0dd37e5da0dc2f3a4b68a78de4295e45c5a92189fff54ea7a8e5cff9523ec36b33d2544ac4c9ba9c9b67d25f584db52d9b3f98ed1e2a801adad82f7470a7498cd6a5157bd67e18baa8e987dd5158b54fabde314f39d26909249035d9a655f9960b690063dcc1281634958724c79050478e339deead3b8f6c6c94072f892b7d17d11d3f71bac5241291e7cf9d361919a3f44072997b82a78d6d05f41f33a447b8e6fda31f8ffa5ae6a25ec7d4fb503e6e533d1e81fd4579813f83063a6eb340db45dcd02aa1117614abba1e0ac0fc1c92734763245cbac29b5e5a354567e809543a619bc67884f9f20dfa111c5946d92f3b1897865a3b84a5426b9528edbf8a9c572c09d489c1bab2d7688377846fb07524da77a56ea19412fbc251e663b8c1cc89bb2b0d267182bf114533980fcc9c35c1837c60c89e997161254a748b85149d445b1813a40c22a8a2a78a8b5144ca36092abe342f61a0471ab7334a789fa64a5dbba28563f094f547cdfc136269ce66c734f5007306edc1bdee6db1bd337c68a16b8ca10c86c914144cc3cba7e07088939c6607bc92de121546a966e337df8a4eca9a1f115f29c4d8e2cd800bee796b5b8871e7b1923ad6ee05c31b51a7fd1bb6c01140f8c98cbc7c7c64c972c07cb836666d107a253f3608f3ceb1202329f826b5fe2ee159ae62c8bf2519aefb91810dc25f7880f19deb83803d1dd4e1ffebf741eb2808940a0da1e247172d6e1abc83f0bc5010123fe02f1144624698b0c74d04ef533b6bca8de5406412a94e5ace4ffabea00763b49b9201aed30487999973d5a2cf4ff1e534b9d6a110c2ad24d76231898d9e47892f3e0bafe546ebb565a1878f5b5249f0033c44a48083040b4f9b36d578ac9758a629e942c6cd2c9c5bf01659b834931fb6256c8bd81f6491c2c2f2119fbc9a8ba29369a3a752b4d974efdd8fadb68c9318008fd553671a9ee02f4efea84babc1ba75a2ef44060e1f407ebea21c08d52f817777884cb6671c04232cfb50b1434acd5d6c8e64e1c5c8541331b9e968f13af421bf7ef5be754a15ddfefb4679d42ee23bf2d2499120e1154edfdb6952301090d8f3b238c11d3513aa0972e67a9ac14e27baceca01a9e3836ac094bf8af6743ddd9bf06cf39f98a6b822f06c301e09fcdfe58ac76140fe11e99ad88af8b42cacea77fab6ceabb428a24d09011528c0ef6ee1254cddfdc243d998057d156e4c898fd12243f9c4480f03784d521e1aa9d9275091d09b53975448bf84c0d9f6e03e9e9a240de006d5b9713ada7d438c8342a5c208bacc56cab887311959536f0761bd64ea179378ccd4f90fed2ad1e2e8ccffaae19ac402a986dd5964de0b9829977fe5fd25430db57a8f10e37c34df2bb7cea4b0400a7ed22c7fe65e3d9f5f97261613f7583332bb5c41075f2051ee2378b6d08bbf8048e016083428183db7ce353b329588809023489a3cc03355bdaf9396811e2242b122b0922b07", "5151ac53", 1, 186872614, 0, "411926509e5aa03bb6eee0825b0f715110639f622dab91090a46e6d4e7e9e810"], + ["5dc5707902035a6c8fcf19f1c7dc51d6a63cdcb0e26b4ba25ec16ecf02da89d6e3a3b3d14c0000000003535353ffffffff6587b722007ec23f615a6d99f157666d4d7adc32738c7aeeeb4dd683aafc829802000000066a0065530063ffffffff03014b460400000000015169eadd030000000002536a1647500100000000035163520000000003000000000000000043d8040200000000ce3c2e0531d85d8688f10c016abdfc4b321b5509817306bab03a6b398bda1e29228fc996516a92d97fb87e2a31def6ab98e041157102f38839a338adf78772faedeffa5233a97a7975cb9478d86751cfe6ec426af52f91bb720e25d17ab8c83500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d22576c94eb47320325c20d23e468a297798ae0b82f68b823cafcc57f4ee52e2d9237deaf01acdc6f8e404afd54be7db2723c408c31f0807139035f7f16d928edd79a61ce0d09a2d191d951c3d13f8d96073cd0168a8ffe9c5e809da5498cd268a2a017d05ac51b543da6a2ad2886ed8fad0643518613bd88a82f0e34bd21c0d0226baf47caa9431f88087d7f36c3af2e11dd3cd16751a532323cf38bf2bd696060222dbd00a2daf6cfbc851484c744f069edc3d661e379703b7743a56e8d9b4eed90a075c60b6f0c167157d4df854d7c30e0346dabf0219027274a2c7beef64098b452fcc4e0001f57c3849600d70eb0ae8e5f7863eb4b238ab84f8c614c58519d276022117f0a1715d0e6aefc481f7732b8bb17cf6b8406dbcce27525b2b0ad905562502163016ea9791e30bfa9984893cb8058553530f40c167c00f3d035b3c8041fbfb030365e014683a1b794669e4036c75aba7339c330bdcbadda95c37ebba2112059c022af3f2a46a13f673baf07f5ccc69acdd1019db63be50dd83f50564669545fbfe021dc4720ab5874241c3820f7b2c4321543a3b710e0087c68e893b18fdf550284fb7bcaa0aea3c30459fe0c68846fbdde9bb9b10f80fc3decac3d5715b60d3f3818d76985f2342b9f1cc3245f55f134e1b9b6ddfee513b975627415be7aa5e777f80b5ab0772e3f613f989bf8446064da2d8d7799315efed0be04a7b75dd9f70bc479c72627fda55b5b9dfc2ee06c78cb41125f63028dbd15a5059053086a1f872bd529c81cd405dffc328ad4a6827a2ddbea66cd597fdf17a3fc717b69e8b63eb5a5ef4cd914c0e66bfc5558bf3d8aac170d4faebb03d98329fe276349b0c08b154d663f348b30dd2cd804813419885406c27de3fc0a7937d412e7e81d76b1068542f5eba0df010be13d21e7618e60c813890317e8331bc6460c44d3bbecc81194fe3a199c40749f0f091eec3486619d816cd92583a68427a4ee22aafe7cf8f442a43f813c5cf04e0746eb14416ec46b52423b85912fff38e2a9dc03ceeca5b315c33ed19a11ac2062316c9f702042fe1e72e25180d4c7d17f57d65dc885032a4ebe2dde633998a4596157144f1acb0c3d46e7f3e353847d310fd1a430c30fae90d0a93534582a2bd2e4887cd7e8cafba2edcdf08e3ece6633fd2787968437821c9ba79ba970778def13cb9426762249a3c87a2aa35a11d87074125a8528788fe3ff557ef0fad57009cd374b8ce03a96d88fe39fbf8f72815b4d6afd6080fab61b54fcaa78302e2de11311e49db6f461b063a1823867f486104dbdba128affa3b669f1d2cd8eccfb2f40bcfd2b671bcbabb113112e017aa321d8161e233f4371e1e99fe7f326959c2a2afea34b548ba3f9b6bedc4ccb1416b0e7075b738b6432aa7a634e335e3b5aef06b974c716f9230a89a3b91418a91f883bd79207633211a0d3334cabcb6e83e61713faf4b288019bf03de0d39c9ed7f0000e968a8c079c8c85f3c2854ca12eb1dd47d384057e1d83aecbec42ee928f39369effffc7e218173d2f72455edd27888b30ef033574f98259e91d725592b5634ab507c98f8e2ea11e356c03f3a3eff992231d4da232192a7184f8ca9ac577132da6c5633fae6d167a4d858dfeec22dc18512d47f921e8e5ebd6cfc8d022a4756f25cfd4b2dbe51ae238f9fcbf9dffb9546baacdd64ee3b0f744fcd0dbd9f9c8d3e3d692004cb579a904193e442359db9091f20698d5397eb19768b1a7e1a322dad740248b27c1def045ec2eb0a89accd68ccb93e97bf7e6122fcaf468c834f5882cda3cab526c0267fa85610d30596a754fa82267ccd6c1e858a7eb6149f39e6952f885bd252d85941ab96a6a37e475652f14d03f1637be54cab2e3ee3a915adc7537e0707af5307c7ee8867a813adc1558b4624e7bd549f326680ad112216077036ad6df6a05271d73cef8c728b8b528331fa223fbc9ef2dcaec9f483c390bf8c86995d6bb031b35f46807c1b38c5094306810c3cc408033652a705b586a0f469273cfc856dc774656fe6d7e0dc635c59b5010ebbeec27f4daa4f2079d99327124160624d8dec808d0f209dbece713f37abdba3d7ec7f77b15e5d377a3dc58035826e5c46473fe402904fd80dc8f58b0f9fa3f8b2f7d0ff46d581c422e3492c3f0620288d1162c9b26aaab9f6582ddcb87be6313ff95c4258e45e211d7dbc2bca952a096a0790307d85bce7b9c44892414f5844a2889919c57455f29118c49ad794720a1efacd21b6f39ea448d9980cc043063bbdb65358310000000000000000c28d2d0400000000f88c60b43bfeee81a702d551b9ba461ab7230aad2ff1371218373f404a4cd526350720af298fc09c6bc80dc64956b35b7510d323de1e86512afe0b1540b30d98fdb0540ea37ad68a4be200774c7ff1ffd074101b8f267ddb941cd19d1d4fe4ff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002ba03c7739d50b33dc62f33566d7ec7913c633e33fa10121f8e6ff6645c0fb942617ce77d68cbc0cfe5ec95cfc60d2b54a686007109dd012348d62b592c70190e3a1013e8179e88e04e0f70007727dfdc89fba6a795d99fa239fac0991210a041193d2bd3efea74d8ecf459d45d1f7284c0f85855d57fb85f6613b3b5e187383020289ddc7be1ae6686666008dc45a4f1ecc9a7f64e3eae3d7d254057b67fd0e090318a5264c8b9dbe72625cd889e54b283ed167091d0eb0737c731863f9b6838da50b070f38462adf0ee50a9cc54cf242dfdbc62a14043e8f72b3ebbc461c63e62608a093ff9e95c2539077702528335d6cd9b17079f8b6f599d396720a353dbb6721021f8512e836b63427367721e28c1f9a1941c19a8d84a7bf46cb50f518f942d599020a0eed20ddd4ee767df97fb1d4006283ef6304d2a2b8258e21a3649c2c1535110222ed19ebcd97b858cd2b2b1cab53c25bb9e0df480878d705a65f50497f14c11603029e776bd237f0dcbe495fdb41df7b797fe0760320249a394bf0731662660f03021a53c51c76c4430db551ea84901bf79ee7e5df37474884e4e3bfd1df23b403d5de507317f28d50df8bb950a88343991ccac619d4b8ddc3e64f2e1339405c87c46b2633dc9eed665bf6a45494f8edb8e157ad5cfdc63ca80b58fa4306a50ed3032e3dd7a15b3e0db9da13eb9695ee0819cd3230cee3381df804c9d1884b3eb2d647dfee98de661fad2a74654b176055cce074b00c956063bf600fe4d4ac696f577c6d581fe0ce7d3e83c836d848b38fcde771754ee897e206c8c8360ffe35c7da695330653b419fa28b4bb6de4282ef8ff70a7a2d4968a0c66ca4cc2eb5473686a7992c52123bf9911bfeed366128e7974295aba8d8b4759a8c771715f07a178951dd5f5038f5c6954b7bbb7d4cafafe36ad2c16b3aae15b7557630fe64ee7399dbf62fac8ea38d711fbf6b0760e358f1fcc9a7db66fd85cabd1adc771172ab179d89f0c6a71ae5f66c79d165dd355ef1e981c39c9d77f073dc2691f9efc4e0e0160022a3804aa44af9f9f4d0dc37338c824b8ea6334abb72d9e8a74c4d544c90edd1f537e1e7cf554f30d856f81e9dfe75776624827dbc8dc101791748fbb86b422ae92f9966c532853726eb717101dcd53c4de3daaf7f01f8a9182de6ca7253ddb8edb522e0f7601e6532e1684ea21659541f6c15d1f62083c3e1c38a27b73d8bdc43fae2105d2e75cb9e5b3340de59cf4abbe74774b35425897a30d0d1b35e514b834d887d83c27c6bd35c97fc2d05ab4e512dc8cb0416d18db5277a713628d40810d83678ca16ddddc6d7ede01afbccef7d85d1de934eb6d076406e65bfb7ace6ba214485a9156b24c2efb09a014be9b06990614b8868f8ac7aaddf0b71515b03f3307df5359a3d59a31c4504a69b898fbe8c352ab913e7ae468f27088e95adb526170bf9ae86781cd7829f7e7cc33f10daf64866a852fa7cbf941b7ca8bb6f11ee30a7b888661912bc445b2d86e37ea16ec2b4489c670948b42ff824d88513b34858de22396a14d7101e0e90be2e691bdfa5481331a0520ed346ad9188e02ef6e3857e60fffe297946bf4c1bad93dc08f63c80857e557b37bf9b0cc5c9b8a43265f5188d1a8063c0ec970963eb70ddaa02e1166ff6cd2ceba5567c2043ad87f3df84ef9d50a52cb5d8f254a7a18963ab3f50e23d4b0f63228f78ff8aa9112cb2435e316f1761cfea49794240569e08b07e242cbc8dbee4ba047076776395c1cead74aef9970ac1432042349af13b5d3fddb5a58c3d7d6fd63120c334d37b33c2c9e7efac352f80a0e67199692ed3a7661d5ec1667a75b1761dcde8791f04de2fc5459e158065a5342e1e9b4db1f4594abc7e2cc895fd3674dc6f29604a14994aeb4e8bb81f6a7d5b50702682061f27ae94ac2d3ce204f589fdc1f45105b300095c62dc85ffcc7c9107f1f198d57207f9b13dc1ee2bbfc416013576054cf7537a6c219791e420ebc1c0e8d6626ce40b7285bfbb3a13f47e3ba92947971047572c70e727cac2da96566a5b216f51531757e5f8a9c7ca1f9cd959222eda8a46509db0543fe357bd4f1e9568b709d2a37a8500bf5d7d4df5b4f782b85edd276211f8155560dc329ead6123c08ece261988781815f482d0e226752ace3576ac51af5cc247624dcc952dc4268a334c4fbf1b9de3c61be0ad6ee03b9e958534741be07504fa7d229e1479fa35b86c87966fbc643904f546d9011227176479e67225ffda53be8e06a645bdba5ea6eab8c385c34cd583300400000000000000000000000089e48529dbabc0822a7c42d138814a6caf0e9f9cd1ffca437fb87208d8cd69587500b38bb5721fe1b0ac197c35c0ad469f337ad3757e28e6d32f7fc167507115ba1fd40be536b77f8908130ec04450808f08b93cc065f3ce67eac61c043d4b1e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c2421979e61a8e0c9880486cbbeecf4642390632e5ef0f6c65e2e54b93301eb54932d4306dd52fa767d7ffa48cf70672709355a162c542b7efd48a5defe4ede1eed3feded9fe56b5feb6b043079e4eaa69f95b40348f3c59529fedeb3b8515087de0d21184d798707ecc06cb2e509c2f1eb924b6607750745f8b2d14cf5636ee0317c0568e32f3813c4f04662781eba62fbe5868a2db8f83b22174ffc2cbc41da9022312f3910979623696787551b4ac5a21d9022abe79114ced2bf8095266a6e9ee0b02b7dc8d9b3f75a4438e8c9a454e87b2bc4979900b0fda512040fd6ee8132a06d4c6629e47e11bd7763b8d06125d741527975d94de2e8540a44016f0e6e11e47031769f6deffbde0cfada04c7032578a239d719a0d67b5c49d1a37fa6fc340d5d5030415115e0280bb76ca14b65bb05eccedfccf65d1b5e52a9fc742b0171895534902085a11bb2b58726f3e397a73bc12300c560c1fdafea1ca22ccb4a5a9f425e9d10317a8494ecd608dbb447a195ab34bff8471a4c5aa9e52867047fc1311643dd529032b9e67ff576206e5aa0967ed53d284f889c6997e84857218d42e2f392ba6c9dd997efb437375e2a05a4b929de14c1ee416ee9c13a11717d28b838bcf794f0b71ec135cecd5bd45019b563847f143e6b1bfb83c7b1640dad1692354769493e32b67794de13564d69b5f6cab0070fbd5977e979320d9a9a5923abf5d3b16df3ad1e1e88bc6d989a407309203f6b369f9a392bfe976ad72f9a2f7bf77202781ae8049ea06aa593e7ba65008bbf0e9262229c7b012c0404e27f0318fbf85b112b9ceb8c0c73a4ca01ecd1a855cc2cc70e936c1666fd0390e8116638f347d85d49275c4d93a43f4b634355cd4b11b5d4253e2c9105dbf08b1e2805c7a0b21265420718798004e0fbdd85926da409ba9f1edb4b4b3811190dfa09ace97a9aa9b400a26eacf643f780ef50ba8cbe2eaca3ab5c7cebb50eb5d9c2eb7e37250ead87a4fc9a272b4661d3989ee69ec7a5d24eedfc30b87bd971a75c7a354a346ceb206af5d85fd93ca5813d579bd12f6c55a6380b5ae89e65953c9713557ff304b38650b3c7a6b98e61424a565f428a3806092887a388cb1b3dc2f5cc8fd481f7cf17f55615f6c22bcbaa20bb06150de44ebfeb06408ec8eccfbb4a289ab4ec2bab10bda5cb0db8ce868134030c730553f044716fc3e80658670057633b9ab0320928a0c52ed98b01caf6a26ba308da19f594f7da635546947489a059cac589407d01077e8b32372072897680c15c1f13250329307d52748c3bae5671e1a0aee8583311b79f46c4d944e708e686b82cc14c1c138851592154597fee59fa2726c9dd0162eec190242529d615dc6460e562eb5219dd173fde15b394ea537e4dcece9f916bea1fd30da9c616ffa1806b6a1429fcff4fc49abfdf844f90708636ba4159ffccb920aa59343f2af93bba70361682ffd5e2ab2ee0bc49be977323d3b8a2f05ad097cfac9a6b87d5e4ba6cb8df281d1362518a2215535f05785fe379b604d2c2a2d89ff9d8b9654a130c85002a3b85907f20dd09288a31ab10af8eff5e4e6e2d7cbc2637e6dc9259adebb1773dd26e92b691eb13b465cf503f9286ce2e34d8fe3b3c0b61c5bd71766396c7cc6f1e6d4ffcf92664bfce6bdfc25c9d6190865355e51deb658bbe97e2c8c16763ea3ac72f48104d1f2bd5bafcae36de4e8238428c4e01b70f55cd9919541691ff35283d0ecc925fc11d22eacc3c8673fe0f4add398af6180a58747e10ae8fe8b05178d81a3c1f3167cd851a6384c476652423812c63646b180f8f6969e8d6965b59ae023b277f8f7376ce41d43f7afb022697488c85c97a0f640a1878acafe8eeca95963ba6d0a244da12b7773e7f6c766686075d9542b0dce72c4378015e081f4afa0dcc776622195e82bb98587a1d97e43d16307f15d552bdf01749e90e282f6fd1313e58f32429e9e6ce9848efefa95da422c6930f02933a1ec326da8671d266eb4dbb5b1df7d73379228f6767cafbae13ca2ea97885e3f635dbde942e67d1321fe51e646003b5ceb6b53086cc377109a30dafa452fa0197875ab5c607d85bc77f15fd45defb0b4048b8a125888d4b2d38fefce21204f50f710d0789a95a519b46d96bbdc79af4c986d4b531660196627a53178d7742b35581f1e4438b37bb6dc882278b5d79ce6fe017df34fc18465409b8bbf4ef2fcc3486f0fd41f1e7a75d8c6479745470f42c225fd2e16ac352381e4dd3f1e14933e5137f1a0963fe148f0bcac0b0cc43fa6460c3ccfe0b1ccdbc69bb97aecf5990d25224257fb8a530da9d50db876a6ff9ae1a5bc0556861571df29ec81bcdcc855362051713ad6665388d43c8df48a344c317e5a6f2caa81b6b2df41012906021f67282538ee0b65e9219fc7317595ce05", "65526a00ac63535300", 0, 72056376, 0, "1d5f13a8e6350a15d092d3b42e021c1f18a0f03db9125519be09526091aba51d"], + ["030000807082c40301aaf15af86fa90a77b7a11b94187aaf655b8624abaa94b7379bb212ddef5804c3020000000252633327017701da45a400000000000751006a63656a53f909e02fe1088e0003812fc904000000000000000000000000ce6f71322d6dd7660e2916940b1cb8bc9850da322c4ebf99028a6ea40493af9ae172e19abef1df94198ffc8291baf11c9778e5d3a4f7b031edbc8c306bb72f3da753c93a9bb3cac5251b25b117ffc7f52d2a572541a0094b2d7cad6a7f321af800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cfb47a639cffab16e6b723a6b2532a4125354d3da48dae62341a76adf5fed96aa6d7a3e5cb37b146340e2ec53c4bafd2aca7de26238da30f1302ea6d2065bcdc70cef7a9f82cd48e4db02a4898f6dacfbb7a6d2e8885f051aff0b36bfc2d52ec73e44f4cf7f70708563d33ad827ea2e71ac174bcfa5ecfa15a36d20810af56ca022170158981074666f34fca2a57f496998d7262d5f92bffb54d850ccf415d0be4032e4ee582b21338bef6fb8ea859de3cd8c2448cf6369271d3b2012cbaade787300a078e659b57b367eec229d1eaf2fb4de8f3dad3ff4232fd7db1614f9ca04282340c3a056573d3fcc914f32ac19169ff24f21dfe508c2063b9268ee697bc169e6b03174f1c2357907dbc989575e1fe38b535ca05dad915b35cc952512787a40ddf71022c359e742a6f108955693d35113f10917e153d1d2130479cd4de272e91487c9c021001f392fdd78c01e39eceaedb1784913a718d2dbf9480977b30a1b7201a8bc50216002039e67deb171843f9181bb99697481693a40d7416e905ec4be50f0960f203281ba2218deaa2d6cc3482c406fe525657f8cb388961c9cff2ffb740db4f2a6f3bbbdc8822078ac69520af0719ccdd97166f217cbca93aa3ec0d302db591ed9edcfb28d3c42092ec3d18919228c785e2b132501bbe3472fb6a7ebbf4cc2033573b1dae2295cb7a175a51958826199c67ff8e88909ed28f554dad8744c57be8b9ffc8878774e250488bd2d0d5d393622ff8f397d605649ae42e111f88cf1efadd1b8367e3361bb80bc9ddb0885ddba4696162a860bbb5e892795562e3ebd29de82eef24fd7e9377dd6c2b4221583fbd62e6a128db081438dd066f896ab0eb5dc99d953411b94230e3a14ec55ef78a5903b2f5d31b0efac097403ead2bba31cc6c7073597f46d253ec38b98eec584915862c1920fd825b825f36ae90dea8cf1550c83d821e08b22f65433c217f35723766acaa05cda381c9b3ce15773f4842dd61a9c55ce1d737c59075b39e39e59c55a56ab56cd9f7790836e667af7b4b129bb408fb5eb4a139cc4e88c46907f3351e12e313259e7ea9c6dd8ba8364c304b3d888925be51e6886e4e3f207dd8685e6ceee09fe252ae0afb3c5de82ca331e0ee2f50d85fd232b7089818ffc8355bc216dd7dae8095c2276d4e87ab73cfb54f2ca1915faaa7dba98fd7515fd8ad5e63fe233d5836af840d17fc896d87ecfa5ac4969c7885b75dbbc1c739579b6e8328924e1d4bb557cbddc9951ac2945e5676afb79ac42fbd256944b32292aeef9d544809b93f15b038774e19c34d09369b5d95be7fc7e5b0ccabb0ae6b3c5ae4974fc4325918abace24887f732beae8a9b9871e33ffd977770cafe465003eced6d8bd74b48c905447f082d38b6241973d0f3582548142e6d1677defe342467f4fd5e2ca147bd281af1bfb4dadf8ff19386c99b26a7585d981fa05089aaca006d64ea75c27d1b45c4833a623739c4e98003d63e7eb23eae4d7c424e1cafe87dcb2ea521027c84d4705d65ab5db47ccf8b85d505d0c479d9b068366ad0f6bde069a51b817d1ddf17d02f12144e73329aa2ef910e171b1111c42fa68ee00c028261194e2dc15ce0cac113daec609e20ca32cc79c8e7d7e6e5d9abb4fa6abc45b34c40e4433d7394eb7dce0e2eefeda2cbe8cfa430331418429fffd7079481ece0386ff863ca5dbe321323b4aa545433d1ee8d02a7b4c3dc65ed0915910f66e5eaa9e650fc7063d269ac0491d40e4d47213a157a1acd27d3aef586205332e2e4c8ce4840372bee1338487dfb87e7e0562e0d80911d91b37a5041999fad2cf21d2eeebbd4341be33b9994482b4c6217016f4fe65e4318c2af07c22beb7d075455a90346d0dafc2d12ea09725f137ff80a4bab61846d25250616aaef1f5dedaa9cc46f48cacfe990c14a82d552a40a2534cdd92f4594985b26d5adb78a6a7e7ece736a5fc32e24adcde638b60fedd2c9d702e7041da86e747b5edb08cb5192858b4613dbb283a4a610a3971ca45aa0583c99090a282910c37a554f875129982c5e14b6789c7f28993ecfd387cce1442c97d2d521f653c885ab76ce439241de6f7ff091a2473e2d04e6266122d778719272238f86504a5db13c1e58668d9a8b6eaf30135a5eb335e5d536239068bf17d663de930ca116adfea6ebcab23933bdb6a3a11d0a97122b463ac3b4da96b221429a4cadd924ff9f1e355df10c9e374374d2521fdd3970898a69b9c55790b91b2afa32b5e269e69dee22a65523ee4649e104974e5217afcd875d0000000000000000e669810100000000f6df55ee77501983611708d5fb106ee18e2f59d2fae0fb89da4dbc9009c5b641b642522aecf8d6d4d51f90734c06ebe20f7202fb2136438dab2e204ec30b57cbdaa639369de600b4e9a5ef37f74c1f57fd42fe76bff5112737748737d3824cd20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000091f9a159ccadc63661dc1d0727acd952db562749f5ba8c13d270a2be489c99cf0906edfb5eb6bb476b459579b6ebbd6d641318c31b180beaf9cde2fd9136a6b1a1490bbaa14abd899a63510089c1ebdc251339fd3bf0374022b6768d53f79ca084aa423442381c9d19fc8a4634d46d733d60f4fa693a69f78bd0aebdce4cd903020771348232220c6769347542eafd5604f732aed1681100c91053380f44208227022471c35f828be3296de3360dcaed72eb2e448218eaab1a92d778397c4b292d590b07e80eebf24cf0fce2512ed171921a873554e78025909b51145572fcb44b79c1904a3cb27cf323cccd5987f4d2b8acef0903e447392b5248273af62d6bb445f60204686f18e4e4e543ecf2d2ab45073327a9c853988c96853bddbd8d0da9a1f1e603097e99671ed3251d86e08ee20b9f0340f4f3924cc36dd3ac08dcf24abeffcfec0206a47e178f035d0eb72163731c2954223e17967915a21385f80a3db7e56fcc4f020bb67aca78fdccaeac84902b3a3a9f00ec4465148488ecf8da0b1fadba9d7cc603072a5c31826e6531e005056e4f9db3f3388f75278d7743376a316dd0d80e3691ef98349deac8d48cf89af55628f2232626443e8ad4d74c4e7d2cd18d045376c1d0a06e15f8226ae9504a73feb6ea6112e3dad0b3d781cdb0b47a2b10565313f29be6df7e4820bb207476e814f903e4e854fcf3edc2bbfffee88a274deab345e0970666cc8077e7bb7a1c655881103ff6ee9e40316f43bbe40868d6f1bce71f3fa702b580d22820d9c170ed8c764aeb34e99627f676238e9ffc8148f10b3b0b43a756f2d18ad13ca9d3544ab622b9e94cdbe2597b3f61c3db56c244dc9b78483e02159c317c3dcd47b1651f99d6a287db51583995be592613ae8f07cd5ba4e193246fa2609d09c18a541ef589010e78315c1e0881475c017c8d54a934c7293886e4d95b8f4b5c89bc7f1e4b9941919e6cc4e4091dd78d26465b551c06fe9b7921cf0beabd7d67373089cfebf4db7a2887bed2df275c5c332b5e0904463909fe12e5ccc990979a13bacac82980bd6ad7ac5c64e71dc8cb840f5d3b5849ff09d3eda87407baad487bbc1f6a5b6bf75822e9d7736b75e61b7fee6db836ec4a648babfbb7e1c990891b6722fde24e073862306cf5cc71d5503f71e9732bef51687c66fdb0a96ab4726ac6e327c2f7f60abd03005534b3932160cdbe058d76dd069ba25dcfb8e890154967d78c48a4bdde641926125ef61c4db296807123578361e9d3ab30e985c71811fdfdb42652e49caad62a007c983e8fcb2895edde1fa9034c9ace21e9106caf386ae7d20275dc20f2a30f435bec666af2f9ae38cacc453c2a89a80f1bc47a057bc7c7a0692eb41f0530f5bda5b799933aff291a999affc743d365a03a9bdb2b5897a071cc8573a9089a25827284af6b8dc65e41813c3008e4778f7fab7f64b41ed1fe1453a54ef3843aa34fb52fd1babefd1712ae0f8d740b95e9efc3e55ce96630ea22a4b89d635e7bacf966ab30d8cee80254e9186c47a7d96b23f050a75b928ad45bdbf27ccc4183632894c464f44bc749737b3d081a33595894a0f3e5631b2465ac64e5b930cfcece4bb796d33e7cd0886189f69634a40d2a4562749cc9c3364a1a6a330c60bd617079c92d7d69b7ca4d409d624367eec05cb55470eb26b49250e1f549f0332a9a93d7bd10223a97dd2a06f6cf890f8dd4f405eb6f10d827c1b3d347948320ad153d53dc8ce422c0ba1bfad10a896f930e90622af24f3ec1c2e2f9e6294585f0034e1101e74800fe1315fcdde817cd744264e5ddc00be98100ea07901c162217d66f30f446c580415feb3ff9bfdbad4798f61546a50d27fc3135c5469d6a1a24bf85f3cff723a6b2ff80d63310416ffe4d58a9edb34bd254831f6f5d9e5a282e8e4bd9a9953bf0ed01078ee65ffc12b603dd25b0680946c871d8f2a3e3089dccc516a977b5df47b87f02e7014b0222a02afdd9e6323431c68cbb4c7dcdf9a6cce1ff94bb4d902a2d1780b5a4f3c04302492c74334fe2adb9800186edc061a5b14c381e6f96fce87721a0ce8369882186c81db5e7272184727bf463a82720490d0e252856dd647a30ff2bc14ca62f65752821d4349c68d1a0154752f01938714bbda99ceffb47fbf91ecf1f485d80c5f850fe7986157bd6e8a29cc1103efdd4d96f3e8ae7331aa1a8f232d0c22e9ec7883fd9cf591d4aab81e88cf31f1eba35e1dba4db444702343fcf13795bf8aacfd1cf30846d3df421371c125f1beb9fb570a8424a000000000000000058b3990000000000a8c26daced23b27565587d0e028dc23c9f838c79d62ca3e5f1ac971236a39be08dc2f07e304af45b81804cd3b2dc2e3fb721c3341e612c58141a836410f9b9229df036689518d27c6f1a4d0af2fb88fd7ee4c4e9ffe0710e9cfc99529542bf5800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b4757f52bdd94315b0d2806e314f1ca013231fdfc55a3d509d8f8dddc1b96c30dfe65f6e82d34acb21f1b74080cd952ab459f87ad8b9595b80d7e19d242145c2a7cf350b71029ab05df548addcf4c384922b34685ce4920a6e6953ad162408af83af976dd41d7678fd31a617df91869f374598ec948313d3cffe9fd50b056232030d3c9100c3e27a19a90cdd2002b96ef53777628e854991d117c49a791d656170021a47d786651d0c4e508659d02ad1890116c0c88965d79a9cd51069b0cc93a5340b08c3e0b4e7f6b8d2669f69b1fdbbd16394794ea4ae4965cd31b94b65b5f0056b8f5acfff447be52390b86f55f3ce2587bef5333927a0a9d9bcd4a4b769c42c070214bc87aa61c7af4e5c36c2536deef702382ae7b059c578ea9c5293e9108315ea0316a0b130017b0268eabc2ebfb70e791d8b45f4bc24a557500ea09784152e37d2030895f614b94f500ae4e1fe9bae1df1b1b500b4a742f620b3f53e6bf402e99a450208fb2a9a7171f0eeb1bfb957eb89b800ab1becfc516b579d0a51a884ddfa28a4031a7ff488f970ab8bffbedc0debfe88e8529753e67aee80d016acbf19d334f337bc5a6ff212ad77651756aab52c083c88438f37a84baf85ecc964a6e49991f3a0a4013ec7b284be5f6a13863bf4ffaff4172ccf2b68f9d5e090db4013deee235c3457b619aec28105f941f3692b1eb86c103828f981c72165ec831edf08f18e25fa0a9e866e719c717a731e7f353691bdf9b96e6bb4f8b8805b5872d6475251a434021d77736e16b3bc100bac90f79ba702ceef93a3247b0a015fe00e65ebb5b54a0ede654233694612141e37d80bb7766e3cabfe310842e84d2da43d9cac776b151265d51a8f48888f91448f0f24e1068df9c6463d4329c1bf31a8dc791c74edfc9b68d1895b6568a6836a0ebdc775630e7e87d1d804adc60da897401f053050e01b827e39b388711297c28efc8b4ed99869dc86187414ad88e1e770633e4cc256852b58cc43c210d7c7eba8167d3658741e4073d98f89ea38534f66202c05387551923d00a2b864e4d15ee4aa3ef6a5a9e1e759d87c2128b1b8bbb7c5497050f9b2914b1b8d1c72c051bf3f07a174e71e4cabb64a8b324d60dfe2aa9ae20a8e216087c784a509dd09be4cbb8d33b8d37d66dd9c178c45de598712104c9a382900e37a2fc287968329707b54acdb7b80e2e344eb6962bba50c3bbdbc8026821b4539e9ecf169cd799a3992f6651ee37c539973578d62a57b67bbf21d8d3d05661b239cfa15443ede3346e92b6098fc047ebac280fe731598aa2bed0a0ace54c0c7fd542d093b258703a11b54823b47a2dc16e44e181b9f8651d6e46612d4faa3d64eff92d43a538bc8df36e4d6f120e88ca7f947c7db68b7a59f4f3eceea8c97d8301d8458584434f09af0be322df3def2b94b36ecd8ec617f6798b07cd00c58ee757c409aaa8576241270ad1114762e2fad8b0b5e19e1d9d25f5320bafce894c6cda0d8852a93bb2962a48f0e5072a96ceec8ce0ba5c1f68c82e38278dfe4b6aa32379b606b1075a51d7421a2dc07e3c02f3db0fb260c8f5a04359ef7d4f6ede4b759124dc6ac8b3bd9ca58fb950dc8ed9e5b560d3d0f35c6f7cbd1d6068d067faccc52385541c716ed4de7028b86b05ba090c5ba17c8a801f388295b2893db3e93704ff79af3a048a739ae15cc4bf24f7649cc6bf44115ad9625c9705f7a08bb98f50c54dcdb3b3df8cc55f7e6cf0b83923c0cde1c2d7a718bc081a169a0bc0cd652956b354d02b01a256cffbb8bf637bd65b5e61e87219c8d58d97d76a63c4a707b63c2ec07fdf79e94046eef5d0119786c93b72d72f7fcce0ef2a77a633d903fe138c9eb036e2b8fbbac9c611beb235169505fd12b3e48dc41a0a2a2fe77dfc284be0317830c5f20ae5ddbad73371424dcff0e28e31c75772aa6ae99a2c2a152dfdaaec4e9c8569e8eade9708c3acaab3e7d48ce8a77f6177b011cdd9196b3c2d036ca890b3b6d3d5d3ddccd3445f9e4a83fe192e2fb859ff52937e8258f19e0e46ebcf168ca5405ff18499009d1da15e32721b715529b669451ad3d1dee0b895657f2501b0396a0f87c003045ccecb54062ad20e79fd5ca64f7b33927f1ef366c477c0918d6bf935a468817a00ad28f88330e33a3609a1c451958f3e06a0814af9f6c328a2e736e8435b7234d46af34415fdfabae79b2557402de4db9765d48d891af3a6805c9b40588e3e3cd751b2f5fc6df0e25b9064e48cf77055be33b7ea304e3edf2d4cb0f223713d3157b7d8338190fb548de1f4fe5688a1963916b8d2de2b3b7fa355cc23873251cd0bb3c1bb85e0bd42495a00bcb3361ecacb951e41ae7cf26386be055638e238b572c049ad030e8c47e3ff5fcee4cfa213efde71b6b64290dbc4f2af4e3519a7b5cb7a503", "635265ac51ac", 0, -1858386960, 1537743641, "02dea419f79fc0f2c3e470ba2b916872f0dc410bb74732e16613cfbfe5f6be99"], + ["030000807082c403023c1ce03cc6ba8d12ec2005756b2b330569380ece25074956d8a7bc66473dc47a0000000003510063b053d830ff00fbbc80fc69390342d6041abf60ac350bba4001731b1983251ee5f8738e130100000008ac53655263005151ffffffff033028530500000000045300526364662d0500000000066aac51006552dfb7510400000000015200000000000000000100000000000000003117680100000000d5060720815ffaa3adbb9e6fcf48242d81d7a0a9c99936fb03cf7123ef772ed4257cb297e809307bb62d526d9b05b1fb871a415dba0ed92d2e6b7376207ee03a09f09fdb096f36539f19b4d5d64eca7db016a8796b48a737085c3a73c25d4b8600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e449ebce63eb9e32bb497d905b2e01d9f0f9ba7a5ee81270ef24e3a79859232f3c4541469032a3e3cf7578caf14e3221018e29ec18473be93e7d71fc13c90c58c3e582620fa1e091357330ec74e8c73f390626a8ac2173083beb706d2ec35e18ad2127791a4cae610f849ff06ea765a2e9ea01b18c44300cf8809a10855ee98a0329d1be4d788b7ab41369c43519431f88b0b3042d1f709fa865f3fbeb5bffd25b0300358bbcdf357fb0e39f21ced26dd2181b4a01778106eb2fd1318cef173996cf0a0568fc8932b5f8903a274b0c4c1e2136b1ea57df0f671066c14093a93d60486ee05482fb6086bfb9e570f5985117c9877f455541345081be37ce0ebeb8037f270328f525177aa5ccab38a31741ffca771cb12e5cb3a6ab321e25f7885a0256f71f032bb6ed1ff749a11a467844e15d163f73087a015157ceb7df6c3fc2aafad65045031f83c0b042286682dd9aec504af585639965460721c9a021cdbfdf73c7d47af2032072171ae1e67e99b5fa11efedd0c8774c1d42837e089003cbc25eba4671373a030f4557ac5143fb318a2b94300dfa05146977be4d6e4efecf95e8568cf6f9a213c6b5c821b35190d08a3500c3447acc85241912bb289a673428582a8e7fe9c797474f9765bc4d686925c5b86d5b5146cfc9ba5fefc871c1a6bf03d5856747a783c17e61c73ca8df8c80550851b5c9f4a87b84dab6aeeaa2f8ddb53bf4982139b172849142031a67022e0332eb84e35ce8fbfa9ffd4ebdcf9660283d8ecd615cc996eefd53ebc297623123213717e29826b2ca753a15a177848e4e3f79747ba2dd124afa6e14fcf28aac9287fc1848212443c69c524a42ed5fa85d6723862dfd7331e43bdc4d9511d27fa2a54e006872ad176c4faefdf36aa06d6ee62201dfcd0d35d47fde5b0659d1002b1383159f44955a936d7f68c1db535159b16e8792cfb3993d5c5de8dc3d55e43420f10b38c65375e9e71d666fec6bb0ee8917de8590f33588ddb2ea2be23f14d015ed7a5dcf4be493bb32c237f89bee643e310d3d34ca6c9fe8cac989c01b9643feea4fe1c19e7775fc5f3e2806ff46a5ea4372ca3487b63bdaf6756929adac19a0887014c5eefc6302adca3ba66e1152731255254339124f319d646e2f622a9a85f5e89a4da26042ffecc00f73f06f2794b451606250f2f3a0701bf8f303be887a01706e98aae19ecf7775266140686ad1f61731d0bb8ee83c6b3faa9d304ffa411a423184ad4115419260cf7658c82f320a9ce448dad9c52da30a8faa7e440976f45a32527d3544aa305c79474a0eaa688d69af45a85f92e21a0a898f9456f4e11eccde77cf56734620b252b9454a278d1ce36baef1519cbbd6665e8c728dd66af61327209f6a2ee780df6f6c57c4bf8d14f4898d407c2a1d709ebcdcf5a9033ba9239e769f4d8d4605f2ea40fdb7d83784b9f5726bb7c7572f79af0dde11bf7f599dae72616e55cd252343a19b053a3ae41091e1418eab8600a27ba9e72de7fecc952524315fb951e87d66a970e76f04ac47788dfb84e6f0aaba5900f979eb94b82aeea9ee449b1f4fa88bc42a38ad76abf566c9ba11ff52a8b93162fefafcec075a4f9b7562b29a09aa071f4b9808e7fac5d5a4079d55f9839d590382f987ec1fee62c34039d4d9299a7159a788390b2881a427517ceabee96be9ddd8426da3dff8293b7c6ca3b6bfc76d275c656c5208a23e7036a8fd0829bf6c0dd14d2f7bb03c30c495d8f5bd71b735703b1f6863a0e3f68f54382df64343d23037fc6314893183686ba6c76325a037ea487a665992f28313d2d85dd27d0a4d749db9f39a9d9f5caeaa054750293093bae1e3d94d3fa5773568ea208cbebdb9eb05c61984a3f49c71e22399ec834ce7fa9b34b05b0062cc77962bf3ac932984f035d14699783c0541877769ed80803bd5d688f49ffe6b1171cc82c8c73245d7a50fd3e1a13d90eef1dcc919d5655d249faf37419f5d6c24cafb98cdb5c3977a192d3e010a9aaca5bae99751bc954927d87f02c31cc245a31c3210928ab18115fb081c727e5ac3d40f2257a59fe00b99d54d01e8ddb2c2a479c42d7728f56a1a433596e496d9655ef10992ed5dc1a9d53fd71e8d904c6b87957bb94c77d6481462f32bf9f8ed4eec71798d718b60eec76e2dd2e07369cf8cf33d77d7d2bf1cbcce0f0a61af9dfd62cf49859594178ea860ee270ae1a610292a54c4f4415f9ac10b4f484e6d6d6ea680e038f33f866d11a592593286444d0e4829aa04977b799495b647934e9d65e4289a0ee854ff34c75e4151e528ec5a9f122d06cb17369d6459a37b3ec1870a34b31c6b4b5d48ebfab9ceee10d973286b1d3533242e170ecd1f3570d060f36cd3a827cdc66a72be015b07146cdfc0fa1f19be794d50959efce4094d185e914df6c27aaf09", "0053ac6a65", 0, -60658009, 1537743641, "dc3a818aa87574a9f83b915deb7bb5e76f18085e32614687a353db27ea19a1c6"], + ["030000807082c40304c11f5edf06a5597e27193beb2daaf78e5ffff720a495e82148c61a915cc1c8b60300000008636a636363ac6500ffffffffa07eba6fac311f147e0afae948efad0340c8f0dba26e0e2684bb3a185e8724250100000008526352526a52ac00fffffffffa32368e64a49a68f501cee43331b7fd618baa790e8fd633cb575cd3c7fd833e000000000100ffffffff034746b006ffaded3de6d410e5ac8ce1f60eaf590b7af594ea1143450b4bbfe10200000001518f8188780110c7a405000000000363006a00000000000000000347ff4501000000000000000000000000ed9ea3bc9c731b9c5773a121bed895883fd6ec375b1813edb37eff8c6dac2beab004a0a5ed13d4478862d7c193e8bc6b105e002315211872feb9a6a3de92140b17ea85599285cd967dc736d1001ccc8c28388090e149408b72e61c3ebfdf8fc500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e610178733e92ec3a2fb05a80de4956099f34cb295f1bb0198be80d1cd2d3c8d2964622dfc363f7272fe3937ddba729f19de6b34df0d85a2347def1f005e005279aa7504290101bdcfbf87f536a76dc94218ba0a51504efd40c1686b5c68d6fc2d26e6d6af11c25f03dbdd769a680aac46d5e72a9249416504fb641fc2dc288203305c6a25092b9d4d90ee4dd57fe01484b16df501043107b7fb367b0d9a5773e0022f07469d51cc93d7f8a97a88c2e33a8811e36e042d175deabe51215fef606df20b0737dafc475538c4fda28afbc6b3cd39f95ff03c9de87ac5128c6ca81fa918445bdeebcbc70e484d55fe6f15a6dbca6921aaf8c9a2907b23c9c09e2504a13bc70211c9b0fddc805e6be1cf36e63a99e280e7b7fa56522f4aadf7abeba31a70bb0d020dddfde3597b1731a9205be34787309328ae9f766c977b321bdb489cc570f3810227fee88b2c8e079425296d271a1cd5c8ef2f262257f17fbcf11d5b3ae3ea8da10209b6cd2011ba07345346bb46e95d5cd6f61b185def7f1534f02ce1c25567b28e020f442d0132f97dbd48653887c4f4b8e0198f87a295d6a24a86cd9efbaf460977c7911bfcdf653519f6c70e1e4165cd8c5ed50e4cd5a7a395e2f4140594760f38f9c9c6cd0c7035e0307b4f0e9fcd0e21091f1dcff24093bb42576f773a27e181fbab93734228469b58261cc0734d50062a8c962fe4d35f0f21fb613596ba51a16e975fdce212a83a424a5541092e79b18ad8e26e327470244bf803c3cc27d42f71da701c3ad769d5d89f52a6cc48184637ef888c5b5af2b161877dbdb693bbc35e9f2cd5c4278e455a53a63a8eaf83c974290ba4eb9b494ccb2778c227fe6cb2c151dfd28da612ac42e2727edc3a85e54de652c3a5aea7ae5de8236604b631845bfc1e3a34f7c03514ff31fd2721c34f574fa17022098bebd569b8d58e16809789a2426076d19b3b2d899f01e4d3e65273fc304d52f7ead1377eade0d2a8bb08e7b9a71d57910fee3e05f66b8165b07787bda4ec0c4a06105d0865798e6d0da3dadd2f2bce37595a3e80e8c7b7aedab86b984ad84a8191fac8c6dac54205963b81b5c7bb4d0acd75d62e22b7f808286a04755c18d40bcffb9393fbdc75a337c6e7c0d09f2bb2b71732e8fac71eef5b88ce0d7dc7c7423985cc20fc13897f34b14be55465c364488d331d9933f937ac058b35bc2ad1eb203d86fce36f6a88f1dc3933135f0517216fa8ba2d611449ee827cfa0a5a857fa1f9233863f628907c250249841880ff1baeb7455bfe49784843f20c929ee3de121af93c341f6b00d9317eb6b02b824fa74faa40d4ec9c83c3ac21a774915b5af38f6dbbb9f07d15557f5d9228cd2915241eb463b764154446fa63c4db48ec25d8a7db9d5337751173912955533db5773078357bba5d4ec714f89bee5a3331485f142cde8cc00b1e3ddab8c9aa1e8bb398718a69497760256f9acd523340f0b0dae913e1585398ff79f72c409de52958a7e70a0d1f13351d7f838e4dc4a09bdb7df22bfadd814fd52d515f7673f3f862c8e43549772b9311b1936707ec253dbc0d5b2aeec59d00eb3bee152ac3c832a477eaeed7ea75510909baa7b84bbc88140db7098197c3b9ad88dc2550346d15ac51ceb370233b9b64dbffe6c1e5472697c48e72c5992c7d97f86ddadc8ba253990b102a03e48217a22cd9a8a6178fad62f7c6cf558f8ce64abfa18596ce3723605fd00e9e6dd59861cc4ba2434be23d6dedacfa5e07f94cbf3ddb6feab9e063791f57137377eff9d33cc5dd303f6ab7739029fdba061f615e8fb6ea0ae7a0d26f534a903d177b43ac8968e5af972f9c490ad6fa77729c778f78fab8a27ee204dea610e45f37068527e6ceceef9fa671fac8fa85b29c3f8386367cc6a1c840d3d2ca8f65183f9e38202cca5881dd269aaf36f73bd08998b01d86ae6fa78f0d5581b0f0627ce53e549c98dc6212ee67ceb148a3037f322d135ca5a7e028e10abadd149c9dfd4d92d5e877ee70321f1801c6d8552ca6cde18a6c5b8d9b17d97b3b9eb2aa79f63fecef9b1048a19c413ac7783a71a2e6dd07ae06faf3ce3295595eb750d0a0f3f6244d076940b07d2201ad2371a8549995f1faeadb4571e54171c980ce303f4468172c4b23ed8e65b2757e615607368d9bc6b1dcc81bd144c3a852616b58a8e0724882c7dbbdf143a3f05e97bf272102293afe84c4f7f28d062dbb72ba1aa00e11f0d8c4250f13b2d16515dbdf74e18c8efd6bfd5c81fc1f8dc2ca725de120b22e048334ededbf720000000000000000157881030000000084ab99587774b792efee10d1238852823b7bab91e8550e7eb2d78ce900245e46dcb44198761157d8e55e85a63daf6844d869ff5a4171d3f0c1c97b748f4ed513f2bb20b77df73fc1910a51ac61aaf3dc760caced23eb575655ecbfec5da2b8b500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a13033f19fc31b8b7b3654c6cd3b5266295e305cbfbfb09e8477c8713c52ba383260b79fcfbda6b7677e7f5bbf19e12f7fd325aba88cc33777a5b9a031d1f4496d5e29781f2b9b946b2439705aa6f24031acec59bc7dc99d34fd0a89f0b6af14b0d61193eb83e8442f14ae84244a1f125a3e745e105d72966b8e0bbc7d6dff34020c6659148a5b98e7dd94d7bfd863f1ac743d0a05190a5889c361910be04bfa45031f98b51d02e68e3a156fea490032407e9939ca6bd6c748d3a54a0bbc842f1c760b00c125c0bcd0ed27a7d1021e382cf51d16cdeff2e61eb2fe968d3acf42c0029adc5a4222b3f6784aa962415d11543ee5619a355ee6cf5efcf832610e1fcc6e200228802e43a641c4353b5a125fb9085ad387b97f9236f1d1a0bdf70c8e364de26f0215c3ef8d78b16613a93c56ce248ded75dbcb8e274df0220cf306a2be48b1ab4b0314120768a34c422907b1b96118a0322c83cfb6e8a1ccbc7328aafd3da38da0b3021cc609e4d42d04732e9efdba2b3f6f8eee441ab324e4505bd7e211abb020a1c002033660916f3800f04dde17fcdf2f927d839bfce3d4746490fdebfe4645ce8dc257202e5788e7da0221b42efccd3b654429945b5c62c7a46ff0ec462a03e034b61ad3d573911282c9add84eacf00e6a8535314d67f578a2f39a840befe60a7a559e1c658089357dde68ee951428004b6231b3d63b8a53968927c0a24d046cb0759633b7a085ae76e8005148f8a6f0f0c5210e27518ec305508f1de25f2ed83f2b22d470af8048a82e15cac11e5b9090f4aaab34ce64724235a253393a23775a8645a247e023cbcf81681f10d30fd38386adc57ada4d3ab99e30a5f7b1e12d6749ef73f2c0ea006d700494a7313a3b647cc5bfa8425c8f66fc038fac73fa3cf730177f9db3a5eb4f149ef61bba6effb52a34f16261e74c92b79406e93be5df4849206dca20e80487e4c1a56e8ee4263efed3b8927598fa771d8d500e2dbf4480b9b78aa5a39b78ca20e7e1448434b06155a257273ced6a3e9747f8e968ab45dc3ea0d5cb68684bcea00cf82c424b609b28df76bae5943e84a85382f83ae45edc6c9d6f902012ada65a5651d02d3cdee42d03fde49ea08eb678bf53c8bdde45762a531cb1212f9de85f10a412a419a0b1b3b82508336881535a69bc48bc549fe3eb161efd04369549e674c375b2316afb716aa3bc44496d77df68a1157916e3252813ad09b68acf532b7eaf55212f22d6981f6a715c768a95aa32c77664f99851b30bb375955bb9054f18877e843dd18354044dc2e774f89371ab6bb6c08fcecca69b1366b90608ec878aa7cad1a1c619cd10666392a7396412d4b541549d302a62dc8c4d42b7b5141d1054ed14db046b9aace826f03af973bf7ed351fb4204b575fec1c06c11baff96e554bb74a84559f9bc2c3f5ea0ecea2c5b42d53824260fc4572c25ea8349b1bf450c5edab56cee9a22279e5c1352474a2fc2e28617573ba2ec8212f453841a485b62e92cc0f33025afb15caee8411f7b275e7ed5b8f268465984bb62b200dccc12a699b273bfac2bc38c889bda44aecd41c70087e1c72308e6f7e5040cb85fb969e59f1ec8626e806ef4abd2a3c18f2bcd8be3db2aba9b41bb0c0e479188ac1680e258d26616dec7f2764cae268196ea0c463e7eb974cc735e1e64e05f03ddb80729096497bdb3d9ecd54e29e599f725dff43daa026380ae83c4020ec4eb8eff462f2070c5209a82a7e4daac358e22cfe64004c362b630025ac9b555838d7b05a279f916c24ecea788a948479975317c462c480f0b1132d0dc560c998b16bde7cfa0e5b1d1f271436ab9419d28b126d5fdbbbf78e934fac19c69f5c15e81af8bc0bb4b0a388bf91bfacf1321c8c130a6dae56d65001fedd83eff0caaf5112ba66e7e02f55cd4156d881b0830573e0ee8a26dd99dcab9b3d91729489a599a5ccd575ce8cb435645b3e787b624c5c1e759a90c1444a956f96c3441cfaf31076e2d1b985171b9821cf5679c41aac8c172b140a3d226df6ac7ff7632f66bc9e63aafa356e96dd690c675b16aea9ca57e3b433e8575e8854afa95a6c725156f68642b86b81e14396d690ae309fc89d78795fa4899accfb3fc37fb427dbcb862d753d42fa27104671a90dfe7383014cdd8a9f13a292aa7738d0c9235dd0981770031ee528aca79d4ea32ec13ea6bfa032ebc4940df2a668a2de2779209d39bbe40cdf36be477799ce4844bc4d9394063a325c1e90c82c336eaec23a4ff807b06b511fa2479f11dbd32f25099625b2e21020000000000000000000000007dea4b9c2bc0e6c7c963a2ddd16de2a192ae28ae6d6982dc23061e5767f70baa91f8ca766f5649dc3718ecf2db8c0a09be589689f8f31f07d24ceb5a0d8399c906210053c0a00d01421b0396742ce54c36e18bbf65452238022723e1f6a8cab7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007237dcff2e99cf3c85889a47b13d3b0cc52efad001feffc3c69547e439acaf56c19de1cc68b29c25a97df2234bf33bed49204e522718cfc09ae77d237928aba8b4eb72c2c2e15c9713547ce481f820d64f463f22eb57e175276a6f632e930f506558ace4c9f4d3d8cb6acb3a44ff0f2d4e0523287fb1a70a53bbd349c7e42f32031c61b75da8959b15915e0a07e072237bfe54f31dac2baa4fe4a6866501fc4afd031936111b03931b5a080551ad1cc912dbfab02d2c48270d87ac923fcad687ba4c0a02a0f62f92a069286633101c298a10a32375b9745e71b57017da8e8b3c9b4c157ecae0626cd75282d118aa92eeb0b736de1c5a69ced10fced21cc8257e6ee786020c61342c00040a5ed244935854556feb4e200e9e01d075ead8c6e6e5ae9183440212ae344cf2c86e25869baa5bb8677d1dfc6b04a09c2bdc7fe5b4abe24941a3a303057c8c1ef356444ddce267876b21d25c6e7855c6c474b11bd14bc647b2c9579e0210ce0c973140d26d1a623663beb469c3a024602735d5eac2e064363f6a2d05980316e577351149e05b9940f5d7f1c22083b53f38c2838b2c91d081d82ad8e9037bc11511671d445909a54a68390ed4ab0aa9ccb80f8f54ef79591ef78853f4bcc15d43badc4f27d05aebd6931d5e0d81012f7997281831bb7575a6c11d956e868e19f601b24b1cf3dba2158bb14bfe7d67c0b289ca3c2d5747948fc43615c67e648553564728234e6fed1fed7cb76645e0b2afda6834692d1e5b5d543a6c28c67ccff3f12fd6131e07845847e895f15a3b6ffaff1a75eae74e54a4912c9e0fae44e75c878da6bb5b3fd6c3dbe4cdb822db9f415683b9dd9fb5c5d6509d728dc7c05972ac047c7ae3be407fd8ce1172d7a4e891c62797905f2af40a74735155808f462d655a11be2797685eb89960398eef6f65a1d21b68e8ff12556159a6686a2296c21dd1caaeaf104beaf924631888b31f62bd1813dcac0dad5c0e8210b2d47a13f4dffd4bffa68ef11f491a5c4aa99cef12301acd989094f4d5e98424621a748fe15f2ab754b1d385a452f42400432db3246481f47bfefccf7ad5921f900b1ad2d0146cca49928ac85db2963aa2ee79f62c46102ccfa763a28ee723cab2c9891fa0420dfebfd2132616a655dcd260605e2df93626ebec2433c6c344c078f52cf122319e355221a31ed8eeee9e4ef29c1f38cfbcbbe0cd3211d6079ed19397da63e32ec5f72dcac21312d6e6ec8c2f0ff72ef68f2679dd5d061f61991c3a8943b67c731538f47d388ad5a396607a661b385a6293ab3637e484be74b6eafabeeeb165563481d8a225ccef00c117a1cd32cac7898b552a66706b9cdce79fcbf15da996651f48cdadeb1b890902eb16c05c5fe15e60472de034260597fa36e3f94490c1d95cd19e20272edb22b957032c2bb8bb52701b0e6eb63ddf1acbd19a77e9073c77528b45642a7c0311d50d2f73a1843abb2741ce831ff9f85c5c01c6c092e3863482d92cc94a6bf0f1fb46c22c2ceca64c6b599324a0407fca56ced131fd5d22ac8af094e85ffab9c6af46094ae5cd3ee0442f0d4b18255f93b3d73e2df459df3aae45c89c83ff5f0f7ac5016c7a53a1a8693da3603af663899b479c72e042e32c710ecf2adc0e2d619216283d769fa7bac300327c5360e01233115c57f48d9bdcc379b728402bfc2d7dc2caf934b95eb5c7f759027425cbbbf2d7c522638cf414cacb07135705f8718978e3fedae5131c8cc248e7d1e0645dc04476f35eff57b8f785ded2519d3181b0ad2547fc0522df7f056340641b8ffc1676707d5233979c6545c66ef52d50823baf314dbb9cadf02d6cf20ff5c62d1d257a78e5a75c613f9c2286c98c0d80ace84db8ebbb12c73416a0a2c6018e92ea57b3ee567e70b5d99d5aba380d37a80a5ff20c773930deac304664bb25070a590b2658e42c38496d9d9726bed734883e23a9f7669c115a07e90173b12015c58ad750d4157724a0f4ecb427c5e8619a216b7211acdb8f5c3b45f9b08b99f2c5258cefb20f439484e48dc20860cb72eae8651df65318074137aa91018251dfbdab14abf0573c5acd26f6d0dcdc6fdcb8512fdda7b046ac403958c5c041e65f9364f110e3020533d883b769868feec689e16ec6b11b3d4d5a977450cf15a1c41c9746d3679266561c1bb21793b52a9239803d3f2705886564fe3b3b10d10764795952b052b7b18fb310ac578cbd7735852e15a001604fdd013188f996f57e767c74ceef6cd2dff8150fcdfc1d6550450ae2cefb28cdb3095d27a256a2ff09d8e491edfbff6931542842333558a3a05ea42399411ddda6d1b326708ff1c01ed0945fb5ee3592a06775f9886a9c5ccc51b3ef2016776625f5e8658ba245958631ace6e735bcae6fce3bd31d28bb6ecb64debf7468b8d5784603", "53", 0, 717545970, 0, "951eb914326d267a06fbe75b5c3bd4ab59439c3387230f653d3b46b9bf2e725d"], + ["030000807082c403016f33acc3aacd19b222c2130f38038cae803e4d56b77c7599b40469c685069ba8010000000753ac6a6a51656affffffff0374b74905000000000163942fa802000000000055ce8d03000000000663acac6563acf0e6ba5216472980020000000000000000b437a20200000000602590661cc1f748a7c230095d80f4a250615e1586ac1004a103df5139e4cebbb15691d668921c93d146c879e2a27025f0268daebf6497c88df421f81aee4aa93948e0adafae6ba87015ef765aa7efb6ee77e5078b5735edac3ffe2ae295b02100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000583f124a75eed9b4354af9590914d34eaac17f4ad5255509870d054cdfc5b26fe0e1740d35b06458d212c45d548c49714b6af92c9149b216e2fe17b7eb2ffa820ea83f0c5cc949036609e423c2684f20c3fa786a24c9885beb5a9101918ca19091e503462ce667105c46a9f6205d43f9114c5dc11aeda0d5c7cd9d2b08e9d7bd03138677138782dcf0da3066cab2def18165633d72df25a351c11e8bd451cc7a070210c0671e6d99b58d2d79e93af843e057832a9f78abfe522e58e01f21c006ccb50a006e303390abc1e4fb40a4a3b5e5147a76d2cda9e952276e011bcd9f625758b31e46a354748e083f6cb8039f17c3f2d0ced6995c50c203b8ad97212958362474030727ab68b65b2db6066f4949d9683b51701ecb2d78060578a1ec8d9d84e099d20201ff1058c2060c631752a8c9ea44b7b64cb73e2dfd93eb1baab631cb358ba81c032b195e55a3553e2f4c7765c3314b335aaafd47983e07c5f49101cdeb79a2814a021bcca8db8c5bd971a0f74bea023f8e0eaf994970042ede2d0adaffeb2ac01ba10300715335f875be96c2b38119bf88e5b4780d0a4efabc8658b69a5d0bf6d2ea808e55f61f3e29eaa384835c5eaa6f9a70b7c9b3539c1beffcd8cc8667b704b2eeb8b8003df3506f7020d881e58d91f214c2ec5b190a279cf133836176b28624541bff1a1e8022fa8d9dacdbae95a376407cc9ba726579350dce2f98f770cf92fbcb71a106aafd9cda7717b685652ee103bc7f150626c8de4b25b17022d68487f431895d1fe869215ecda7e28d8b41407cc00dfae5fc5c16329e2aebf0b5255fba9bde4a1d811095fe2dbb8ec05b9c2080af0fa0740940029f428c7f0b5ab2f3387d4fb9c84ac93976cfb6652bed302be14a0b5b5a84e1b94362c077434ea2442ac5db06c26a19499e51af770301a1aeaae93c71dd0c609ce28d0b5a02e19e9ab952abc9ecd646d7031ce9d8b2fd569587d9e4dd79a01f72725d97f549766a7b3d8fde754da748daa754ed648c28215c1c45a98e2d6551cda211cc5812c58208c97a77ca9967cbcbcc2d9adbec8137498e1f25ff8633d270586a1622ebb81f6556fcface677f2b2068c417c222dfeceddbcf75977d09298b5a3744f3ce0115ad254c513ec93a934c5fce5eaf991f39133671f62d964903656977d6c588e54207d5f73a5861a056267a7dc7aebe4c26594f79af8c81ddab3fa2b7e65cff149ae79d96e0707292c28bd63df9ba796a0fb30f8e84b1a8a3411f817be18c114bcc9af0b22163217141079f2a4789ff65e1edd68cd5556670ff9ce7a33542c7e77f57ed3bd22076b7d75d1c8fc2d5198d5a5b55f1c5ca6497bad6458d8c38831d330717b6bb42b249e5657144c05a84f0da896c78400d3d9763d8c6405e91875978b7aa51f8dc23909c117e4c7b9930521410551e8946b1320beaa9c5a38eff524ba81049a1eeebe66e0800aef8976b66c59389b6eb6087ac835d8e8860d2656be18ecf62011570dbfebfa59602394d0183e12468e4e10baed6fa955417f6902bc5478a533f16c1312d348da7571d77726f5aa45b140b144773ef6e60bee85cec59b242234b4f3217b50ae30a05c3903a2c4df798a1d3480b5953a920c98de626071b2347f2bf0a3172764b66a2848efdad4afd0dbba98860798bf902cfb2ec52747c18bf45e0b03337058f745f4e33dbfb4c3bcda0b9ccdaadca67cc4a34d7c3f6248afd7ef499ceb9b6bff99aab0698ee4d6bfd3ad7f042a7818a758d0138d3ccb37e078830116d6088f659a92c094fcc3b05fccf2ca0d3c7bcd8e0fb9aed3f38662643903856f443bc1d0d89f935670602466ecd380688fa4ad7d7f6bfa7ea87b4fea9519b2ad750178e4edd1bcbc410c010b67443708ca68f676f78a736a9feec4cc2032ce1539e5a86d5e36635e9a462c7b49f32d9abd2b261f73fe4d98cc158c503809dd5316cd7183260db02566176ac7b4408db1b1db7eb2eaf161885677015af0d82111c9bf84bf145f1f0bc2c86c292e0c776a246a81c828dd7e5b37a9d245647f4214a0e3dffdd8630e3935dfe336bafa0ae16eebc6f7b32dd4f7cb71a7bea71a5c550c066ca0257dde1772f4282e55d4a70dab90cd79867c118a0baab588e6b17da6cf9daa6a6cfb5b465cb8693619c156ad7ffd1026203c0171c10c802325bba34a0818f420ee1565bfd58c83b3efab3a2244da1e54114118740dd87666f56e0255e4ebcce22087626e08d42d6f12dc4f06e015089012ca15f45953f8ad980729876780f2531d6b3a7a62645ece50bc655d604000000000000000000000000c87a3fd582c2af54f02ac6b3645b1f2167155860ad7e5d0b2ba14441a8a9c2943929aa6ee463df053cdc205d5581fc1273b89160996684bcb3758f88ddd70b49f60f33541e2c513b15f08f99be44a27137794dac7d0096e12f0568b5a85e292f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d77a85cea045c9e40b47feee8c3aa3fa85a0ac81a8cd050a26c6dd36acddd4223feb48be1c03058f6b451e12ff68c6b465e7005a858b98128709b972534a262b53d30bee3ee6f84677525624a530b891886518a67615509e49b178aaf9309ab25ffec8a63eb85c03ec175a3adb3afcd906cf9a1c344c9390ede62ee90b98e4870325bd94c5667264b9b17062825653e334bd7dbb6f8a894a8b2ef8bfcb9cef4b39030bf625932fd99e1079172a83f672b966d47ba9a4d02c64ecdab803082d45a6b70a08b8a0973cd93e0a4f63cd81bbbeb91173fc2b723d390c137a6138044c601a7d39d554eb247758a95ee0d26d44670451387a71c3c838934b740b811545f3f318020d7f1105b526a5a1986d4d8fd91ae60fc8e6334c274c51a0f16ba052de206e3f0225f7940bf0ac9fe403dc9807271ed122b7de174f22e254f93bee8f42413f51a7020257aaca68f8493aff308e2b0b4671f523bdcc346fff0faa8c31b032aa8c81f9030baba35bcc0e607ce40b0f880fccfd772da605cad937704b96d76bad48ffb4e802294aaea6cc04a487e96eefc0da226655e8a85c5ac3432752fca534ed7ceeab2968749095782acd18970b68c5ef366092b84e4e8550ce9d7be1c41ea355f01f29b7b6d0d54933c614b9eec3a8ea4c919a413d0b0d735e001f2511cbcb2b12974a1e5f5b3004c24931565b87ffe4ff6e0cc2cd414c640ff78e4102ddde1abfea8a6cb37d587941b24d652e3aa0af4ee0e445de4000eddb562d85465b82f526d55f6be09a4e25d1964a472938efd774b78833e4999e710752ddabce7f89d755a70379f7a49261650843c3ee1b1ddc53d0dfe39d61fde2beff1beb1f43396b79dccd0c45b93363e37e565a8e21e7e533ab04073bcfbf4b58c142afbc1e2d0451a7d73e1a50bc5e4804f2792d3cadd9594f3ac036ecbb62650a2cfa74117c1bdc19f8e97f5a5c5695c4768775889999638c5aaded85f6070009e362a9e94679ce6faa042f3219ae84fc76217a32f64686849dc8d139094e422d9f2ec0c36f186ddd6d7a2485e8f37268901f62b2758e2ce7363bda59e6bc10fb4768cd92bf2868b5cc81ad87ab80f5f3ee54ed1caab56f406bd2985ae1ba50357250f256c5079e28562c3908766c08970ebfe1066db1150cfe754265f2650729055d3256872cadd1902a51712385f76a1c6378a0448f82772b3d4c5742fc034a7d6ba58a967a01dad66256761aa80cff1bb4bd3a9f91d7a8d3b076baf769eb4190b23c50dd4126cfea2ae4d6819442b50326aeefd924066106d067fa1e40bb1d58f4b9c1e2ea26de263bde9199322b9c71d6620743b96e26ad996f2d26c21ec137f0b6e9de2a0a81474668e2bda32ba2b75f266349f8e22cadf6ea7fc0acce8b60c4cb75a2e40454ac4890e2abeccfd55205cb1e056e0487ba0e5a5e1a28f54b6b5ea91366e04e73f17924594a6f11a3104227ccb386a2860d0d4cc0d0a37e3704169dd49891e7fc50240f4b947ac37cbfb1f0d576c3ae6481f14478b0b4a90bd61e43131c42f86b7a2ad86e4bd1899dcb87afcbdbf2933a70a9ded8846fb64213aecb3a797069846eba20046c424ed446cbd83ec1d8d7ee7d55c4099424be2a0d81b7b6c448249148f978bccd9826d1eb0c5686dfa00af74b8f55f8954434e4bb7da96c44790aa3647a2d0cf14684730aba70005149816361992ac6b01a8f951f7bd7e20e033cd93b43635956fc955ed2220ea2ed84e64d0dde9eae61bb260de9ed0d6f095f77eec9fd4d846de0998331929092448dabb27eea56bd6f850151bdba0cfdfee33aacce7c2aede5ba4b199f0a5fc5ab58c6ff241195a3f0d1ed198dafc2abfa45ec664758f4b1b940e7b25ba623be443b2abeeac4dd02763b70e601fd7e69695e702d656220ac47927f16cc223e1e198cd990effa53cc6150633c6fe858ff70b3a03d17cb419bfaf5ac9fff0642d247ae1ccce8155bb0318283ef48d57619348994e18be5b9d4c5a6585962732b11aacedd3eed8d091ed795057d065bfe2f866f727ddc5ba68fb4dc59e099420ec460aa9aefaf2ee52f69ed0c46f6a2d07b69e5f6661d62b38ea1f594be69090b1a106115b2b890eee55489748de355207cd753a36b27ae85a41e402d4ae879c5f934338a6707a79ae4077b242a85b95c9499b3fa79aa5af29db85855d625ed5ed842bf8c0296e47154abdd6dd8bdaec4bad31dc81f6b1e9d159ce0f04bfa178ada60c8d9d5298345587e610049278e778a9fbd3d436e0059a9ae2aa3055aca49f74b51f018a1f62351d7b7704e00da25e7acf1920c8abff668fa489fd9658de39d3bd4bd2ba04a8da010201139e79db376d268cd14d9228f247f010a693a79fdd9c3bdb80f1ba3b1da0ba2649f46372e176af13a92ae590cd2d0ac803056e32ab9a1b99113b0da0e", "53", 0, 2132613102, 0, "9cf1ae70d78a4f42a997bfba71a490244f48ef8bcaf9ab7fc86897e401a5ee42"], + ["030000807082c403017e766e71298193500affb03c5b14eee10c0da1c51fd0418709376cedd04cfab00000000009536a00ac6aacac0065ffffffff03b1508005000000000952006363536a536552861779020000000000ae8ac904000000000653006a63516a00000000486143f801f410ba02000000000000000000000000aaff9cb7de00824da553fdacf47a5c0bf4497fa3328d3954b0f9d95bc77d5d2c3701c9028c0c394190ea9a13a4ec9eee2a43c756a9715d7b923b95d3268a5b27f0d84baca2737d9da0663ed3e0750461b3afefb45c5d7c72b20902227c85c94b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0aa8cdbcac4e7dda9b92f17fb7c5bc1eedad1c22ac204d4861a148fc8af91cf5a68e65e469a801b97ee1ac2b54f8c44f1500c05840be7710cf1baebce325bbbfd8ee0b286326bbaa137f528e8457a5d4f6ef8d52ddbed19543794fdf4c691ec05df272d911a29ec63508ff20ea41ea2e614a5b39c97743de4273c61059096ba031e243bacfb9a0392baf95b446b3ae91d61c2e4ff10b34721a5cb9c670464467c0326ac3aa0cd94947ead19ec0e98013a708da2084bcb0b5c5713610489004becdb0a0424515a8cef41eed3cd2c645df6a281bbd97aec34b40671a9a3d88f7cf5a5a631bbc04b562811396f50b8b8dd030ac869eb6e9f82c73d6c1031e47457ef8256032a488e8834f86bceeaead247644163e0f270bc4c030a36c1aac0c1f74db854420323a9338f512982b44a6e9a2f841b37a72be4cc4573fd8663d2985f8534d9ccd4032bd09c25fa0d1ea4dfd26349c3b78c2f0653e4e77bdca929cd9e313433067a870315a41b9070abdfdefade842a62baba0440d508b3dc73bd183dd207dd5d144a34031c2e56ba26d93f6c7b9b7195d5a76956a93f4ddedf6398367dff9aee7e9d561a41d84a6e0c5e4917bd995989ea9761db05f43a6df2ff32181e3e15198ebf3c4b5256028d22e139ecc51cf3489eb77474f7c4331e5582e9e9b8010f44bb1d2ce78a9feef70026363b93fedd6957f35c027585e21f0f1b0ea800d77f1abc351953ea3c493446a70ff1f8790a21e68d3705bd00034754090116e87e6803ebfaa6752440473f99276ce95744976153e2d477c5b7742cefabd3d7e2556e291d3519b7d8f41aee94440eade118da04ce17974f56cb79cfd97ceb2273e4ce6d9555dac3fd01ce6ec0f98ae19752d24dbceb64d3853a94aea7cc8fb3fa3fa66abb44fe23df2c0356bedb8dca4c45bf3a68b3a99eece2ad15905ef3d19adbc2c18011a34d5526dd319f7ab12802a196f0de4c9c231c67c32b7f26f7d52725717ee2bff07db95ccf2b1216b9010614e5245c66b2aa0f65fd18846c6690286a3f45c5017d409514701b22c8b14f0ca9aa9a917a796c00ab64bbaa2ea95be1ebfb3ab806e0f39f86f7b2875ff23053b691dad8b8522721f1160528820b5f8442ece2304412301bf7bd96bc7cafe31f6b613eb3efe9ab816a9b5566421f837cba39c58386e72da3597025cc798983a4b115d8e1d5a44f78785fe2ffeedd7535726c31425b9c0422acc28cea576f4c746d2cb3eb1aca01b4dba78d4d33e2597023673725d4b752a05090d3baad0c0c73d9bf4a37715ed4fe8adc10378fe95ce5bc47fb63da0740d4b536dfff89503af6aebc9cb8fb89f0b7064532dd362e6d12479786e7feec7a50159ee649e152c80358a148bd603de1736cc2d05083268a88cee78f8974dee51c9e303c0b1e2a78c6c0f50e57f43d3932ab4924e2ff658f9f02515b827c9d6f8bdf4fba013c216c8fb8ca6bf058d8503eb45a1a8d1348c69ac7825d823ac4ed414a0d840073f1ca077ae5e42ec2b03290e28cf6f6e37c881309e661f1692810ddcd3fe311196280c6fdbfd36d222cd536f599c84990a30b67ba832df67d321b782aaf0ad6f5c8c591c06714954673cae540c80ba75eed488736863c35b6163461fde44e02c87167b532833e60af9dd070b9e43294dc962d5138c71ab9ff63542c1f51c4fc035b2741f587714b6e2208c6399e4ca82e0c21c1c5bfc78fd8f449ac65a5f387367579fa9f22e735ecc9eab6b10b85805d6b6a5321c6133d20f063930de1d4b80ba7ccb4de4215c065340dda7fc608189621019a6c2f89155a3908a0f0ec1c727b8864507d0dddca43f2f8534cd70c603eaf01c5581bcbbfcfecbac9f24255d011956987a53c7122cdddc0da14c5c96124d6c55f8f8f3e4ad8b43728746348b4dbc3b3fd0739b82cdd473d97eff519f30815e59bb0cec6e82e5edbb82883ee23370c4677ff0181980ce3331fec10053943ad42e2f842b9936e68458bcb2173d0b05a1a0c7c9004ead0061da02e6890c93b33db2584a6b8ab4ae5504f850526f3f9f7e4cec7eeb1590a22c37a372f9a7f20e5d564a0a0aaf0a08151c4600154f5c6c3f0b1efba5d43da669655ab43b36633ecaf9bfa992dff94c2892652c8d1e10dd8aeaab2ba63c876b5bf281bc49356fef8896b1f7fb28f2bcfc981e8641c3443f0ccd7e1f4d0e945d0b927eba827cfe477718c3952fe5f3a28a454184ee4d3657415c1558194003969857b8394203be7f6ea2cbc9b8d14f308f551356c3092a80b88e2e94ca152b652dfa2ee83a668e18588dde6968e648d69e4ed0d89121173229f4c7e8fa69c9a6f9461082719a5fa2bf62717d4d9964a6746d5a67ad66a9f31e83d7ea60da53bfc6e8700e2fee89cd0be38c4df6b626fd727b694999a0f9c1a752b375b7e210037a8370d", "", 0, -764619433, 0, "e335e0161ac80c883b6a1b2d45eef606746ecde2ccaca77904ccd94faa2bd06f"], + ["f840376403a92299d95e810fbdb0ba53f68add0d46c10d1728a929dfab49025941bb78aace0200000005536551ac513ef8c3f6cb3960fb9c014b30a38d3109894130ef4b44ad87007dfbe76afc7c716e8438aa01000000076aac516a535252ffffffff4ab1606c6f66569900c10b391cc6fffd3e68775e12f3452e4d538f99b4be489f00000000016310f72ab4039efe47010000000000c813b30000000000055252ac0063862b4c010000000005656a6a51650000000001ec22de01000000000000000000000000c3c2f2b6385292b5f01678a03836b1cd1edc1c65f4db9de8780a8ad447b1043148835c3d53d50fbfa3c068449be90ff2d7b4073097dbb896f8034472b550b2805c74811846debd083cdd3bd85bbb748391a7d50318bad7532538f724eadbce3800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000391a7738babb87e18f50a7923e2d71ca1c15d09d9feb04fc7c28b4c46242ba251d5ddc01f7566dc5df0c1d98e712bc11af8f6b929d9df44edcb608120a207bd8dff65f3a2749d938a26b8b8e48082365acc9dd8cdaee14854b47474953643be7396cb82df6f2a08f610769ce7b40ee10878d070cdcc4b12f16d0a12e16ea7f8f03219a3467b2267e39a007913f5415ebd7e059f8f7331cc511a7f0269b5e00bcf30305a69f82c205c62e18da45ff5c1721117efe41666dadb30699e56d78064abb8f0b01ed0a21cacced808114281e5605db5423db1f04f95c9b9410f654e8c0a91365cf6c6ef5f44fa51555808efaa914738968e452e500bcf464c20ebe83a4427196032f98462dc8253bd576300f7eeac486519a152d8c244e0c60a6df0e0848e658aa02134fe86e90f8901f9c37d636e5add382b965dd8575e2f793dc9a253e1ae1831e031f7275be490aced12e1cc8eebb4d83565bb6cc639e982f8f0ee9553e8ea2798b032a6b4aaf5f00e8bdd52c9089f664369d537e2df2770f0ce66dada4963503460d032ae72935a1d7ad31f4eb2e275ea5180e5d59df341007b03f6501037888404f50e574aab0484db44d549384ed71978c300f140d2f15ece5525e1200c22abe4154ac397324b07eea38b19c235af80ab5b1636c3bbd3254bc180aae17aed440f1243e0dc08a23938b195000eeb7c19d636ae9ebf0ef4d1b16388fc41ffb35f066c8e52b20b7927189fcb04bf7443c3dd716ae5891264237dbbf230ef525354492dea7dcc98bfe061373b993da3dd75991900c50ff3da8f0ccd18c9f423a08227c2c5894c4a34b66dc239e1611284a152e8acfb57b96799f05178a2b535ddaba110e6ad38371ed2012477ec0307f8a9918e503eaded7fb85ee8ff043578a48cafc4179459e087828f1e343d8fa83c7a8a5f6cec768f03def7c7a21ebd4a4e94a509555aa9a7b0fe6f9b32d8c4202ff35ff65129b5b4ffb57aa68b66df4a9acbf470e879b98d272da962f9433dff6eaa1378cc79abf0038ce7f68bea737af02150a9017ba5093cefa4f515814f6fef0d3410bed515566e5bf4b210007ab10248a5d5c5bcd0fd1963792247038de75ed464656d0bdc716b823ae4a3ecfe0f85912d9292e57712795614a923f10f22773a92b9f7a332ce5b44980e25694ebace80ebd281b98d45c612883fd76b2b7bc745c3b31de26e281882a11b9247555365a65fb79910e0e7556632a90769b975236a7f4ffd0fc2a4504201abec803d4b78ba0aff97e5683f885f5fdead82a24dda20a39a0f4017db86d6859505df120983cee4aea1db4e4ca03ba8215b6fac22e340d96220a79655d2228a240e475664007406f7aeb7149015100d8e615e24ae33e289efdc3f3e1667ea7aa186989b921053541ea61a33d706c97c4525490a50f48cf204190bbfcbeb3aa69f01db8603c1cbd219251b1b4264487d79f9e3ecd642175d13e9a4615b45d738c6dc320dbbfd2ee4e2c4d28e1841313c23c992666abe109b144d333ebd650b186b9626e740dcf2e82df0364d47422076156e910f2605a1abed1cec1374f4264fc445ae5baf1f8d0caac5d5a58f308379a3da0a291b7d6fdeefd265dbce1276a9f3520b44b31edf37b65ff661dca5923dd1291ffe814980a0272e640e63b0f11fb80848ab4771b62a55a2da473b2918aca27475cf2e35a5faa2ca985bc95cc2cb347d0c3f34de39c2fb42abaed31de5e51105cf8402a02e2fcc054625a33847e2214699b5c082868a47239b7b65c20b5f63eb82c6e83041bdfc3cc5ec0dcc59417a2fef4dd869e8615eda6cfab6a60053019148a8fa64a7280ecfaca14c670b7264a448af81bab6bd87acf2264dd2b14ec390920e0822280cf28bc8ad0c6b753645dad81e3111d4d4007aa1891be85ff1d2f27ef61e9fd84b1051ccbb2a9f37b96fbd51228705c8b2ed9f32b4fa6bcfc113070f8f57e17465aa0390f582e6ec4ccf77b83a1d1f9ec28aa9d639f3af98acbadab89a656d4be1fa29a36b8e1136d9c904462bcaeeb473f1dc4e71eac93ceba80c770954f5d673298c2283796a13f79b2b4551881f7feb4b9e8213ad2b27740489ca9150762972b29f347c7d27d0a57af7ca979fbd16ca154559a31d902ef35660385d1236d6c79ecd15907525fb3b69b4c15f5ff6ceebb867830592de5098f79bb7ec3e47254e06f384993229c9bc2af9d1db0193e159cf3c2bd4f97ef17c7cb716589192b4a74c8cb5042f7bbea7e6cec4f6e60b23dfeec8737ce09745f0c1f0685584ef8deeda13b8d79c45be23d655e4ec666814d6aeaa7cf81a5318a7d8739db40d1e1dda84a18d2859fcb99adbc78a16039f894919705914a823552ae22ee1e92c04972852001a007682880a091f5718ef19f7cf392ffdbade614a16c372d797c22342cd19b970c", "655265636aac", 0, -112534160, 1537743641, "7adc6861c7942355a14d9cda58331ad4c3c3e50b14c8a4f249bca3d100f4388d"], + ["030000807082c40304e2e2a47e5587c204fd12ff54c5a73e7db02d7bfd700e74ef28e3a5cd80ed490f03000000056a51535151ffffffff241e9ed477d84995a97a62f5c3874beff1fd2d4b05fd6c2ddfa2d8640da3106903000000025265ffffffff3b8c5272b30db34bd71cc79d6b233d8e42f9d308fcf76a382dad0957c8c9d6900200000002ac63ffffffffe0fad593f10c15f65596b8644518175c1297aa714653c49582601b5204edcf4702000000085353636a6a63515302024885035f3e840500000000016353a84b0500000000025200c9ea1404000000000151002feb34e01fa35f02540eeb050000000000000000000000007c56fa5bd901130864ee732c18c24d87cedca4e3c681cb1f1e11b91d7b714149460cc641e037a035de4e9bbe94e84a841f237cd9583d3ec6bd30e6d95be7756912198bb11f8f366bd027a94e7897adecf7aaf27ebfe8302d7ed9194b34518c6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001810387d682a7848ef38b3d1db32c3d25bd91568cc71e214b5fccfc7b56892445c7fba7a117fbc34f0ef2304b2b4b0c2aadd5e105636749adeb9aa2c8ae8a8b26f772af06b11110f186f39d9aca358791526a4928584ac478d492e8da72df03eb271db34d7394c2a529805af74e03b9ba113161c9a862ad6522bdf7f84ffe242022578fb2bb9ae823a8fecb9ee6d427761e45b20c330780713b37cd4ccf734690a022878bce5af1baa61fac3510deb68f65ab1f0a8a78b706e3b8ca88fc86e0e264c0b0336825525bd89a5d2e24b2a3c4c2f975f41b07b08eebbd8a17d72c07db84c9386c870389917ea84ad3b61f519f0e58492aaf90632b67f4343cdc61268671310020290e47a3d4ebc4c07e4465ae3ed62a482665260208dba3855256c1b3437e68f02283b182eb369331cfd38b55efdb6605a237e3a0e8e6a36293baf18f991bf6972030a15d6bb65756ab4cdba74e55d9c2a6a93edefa25f50c90aaf9f78e613d99b7502104885c53c97068aec065aafbe39bfb240c935458299c44edb4cf562d5052970021cd00d1946007c1d4f068049bfcd033127a80550b901acf9b5c845e7bf14ac997253a9a44c9431fa6bd946920caaabce3aca05cce555f65e086a2d2c47567d4b0c4efc2be2d74961703e3a72e1c9d6a32a3877678965c8d4cb485cd7787d48874f74a4be670723034deed5db93f6ccfc78a2d270a5109f6917e05e40fceccdf1a9e91f94e969683d5d23790603f35d2ce82b2923da873d827a666f816286d13dd86939c71e2895a568a44b93f94da238c980b2dbdc66e84a04b62b56a5604a0c287cf26cb57aacadbfa0ea5b19d216ac5912d03bff2e2ae633659c3fd0c3292d0856eafd377b830ba4a4882f911f3f6bca2d3d5986263e8515c5408ddaa488aa4f27699ee82c54f37ce701cf4c94db9b57852fa60a8e3a64aa8014f584c7d6660883a626c318f0ca5e54a6520f92a228ff9b6830ec5c8c0a53f57e30d1feb83b43ebb5458c77fda385b14d3a97a52d4b2d80667424395910104455fecb93f809da58bb0ed8db4f46c9aed74aff6e39a90f520237f27194dc465e11f67e13057c8240df78294c3a6f498fd61017a29ebc6c1781d6a69cd4f95335583b6e7f98f71c172383a6f95c828c95da3ea2f94509acd1b07db6bd004265525f9c7c657b4bf9056704e9fbf82fec8a7ff8bca13b4155f2fe384a6c9d0677c7648f4595fe28a242c7f380d34e6462e06dccf09494bbf95052e3f9133b79439e80138e81987aa2ab624190671f52624d526e1ceb3693ee045c195a195119acafa9e0a9ed0afd963291bc187f58881fde1381854a2188173242f5a335e310ce558fe8af8295dc45fc0c76b6f445823f09b5bc9cdd9685777b1cb646e6b3b571f8c72b9b1b8c1737724c4908a8d83a25817d7bcae3ab27c3e58a4774c487554ffaaf5673bc6d800bf9b5df80c83117fd23d4ec4674dc3750705ba5daae815525f82c7445591c865e283317fd9a3f22d963a407fac0963b7606a8b6127f36a45d3f3e20c89f44631cd4a2500e377d29ea6cfb198f9e62d8e27b2a431b5e548c2515ee5c82ec513efa52b1213e2c5837811b0cf2fdbe96de4606fcca439cc920df400ba7456f321857b0c495d9c831ccab187775c3d74364d0648fd5c05aabc4eeaf54551d7142c10a05e51d70f94a99b8e150255dec09dec21f7ca1047ff49928341d3a1bff807c0148d502b4ecf05ebb9c3be13b7045d05ab278332c8c5eec66c5ff2487362752b20116a06f2f13b7e2ac9e50e1a9439b1fc2042a6200b1c7607cff6a0f1321c8f4efa96646c54602dffc11b4c015c52c5bf63ba9c8808c29f0e36cd9cba510305ef8114574ca58f2f67dc3c64e00f9e6a235953ef29d8cae73ee330c81a484a013815b09320e52baa48c7383572be51aa3da24286162bdbcd9b74dac4ce442067380c8daf8b799824721b12a79c0697c81cdb80d5a81175bc9a719eb35b44432cc947db01e4388930878737b4c61d133146d47d2ab55460b4ec7e8437bdab0433715a077b3e7661a35a0ed33fc6be1fb752b75bb8af4822b771f7a327311ed035e11fd83722f5d8ecf7579d9440e83a0ddf9de4cb3f65e4b8979e98bba9dbbcea749ef0b1c9e4c8f02235af34a05d37418b28e74c9d7cecb8a2b14ba3ad1ebfc7458236f16b45f5790e873196ac1db77f52e1b09e338b5a23d89af01469a06b33e96f7db366fae4e228def3721e59ab3cd5905379ae7077951afa73eda4727d5b5ab38a466c0099e38f05f513deea8600142238b99050000000000000000000000004654171bfc0ee82de107b2fa2cdad6844ebc156bb89a4f84f721401008e399f763f6b9de3151cb79cb482e2e769e703f1e95254813e4550a65154a8693f644c5a585f753cf3f8bb8d176d15a7a2aa1ce657406e97092d4e5184c0e8d245a17680000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004fbb522fe17729934a1b4e10aa6c426c4f5330156d04c32825d267cc411d8dcbccfa0600274bdd2391d336e9c0bf4cc6d7dd8012f2555c1fe34442b78cf3ff3024e4004db4c372d83d1e3180e804bd8bdad700fabe75252bc80d6c4e0344a56ec3fe47d7419ad96eaef60491504fdde750b67fcb4c4e5279b8f9b917017f91303015d44d5eae12c928a9365ab2b4af9c12e089ad0b49f31e6be6e4bea805121ae0323cd65cb2f768416b5f13f513dfde3b685640b92cb6edf72f3a7223842e36c0e0a00af1c8c4713dbd3c5ae0cd3156439f48609fba978f337d8220ea0776e1b0edf91265c2aee6ea44081b45179b3c088d2bccba5cc60ee28458cf4084a66ac419b0306733a4e4aa5ee845c5f768bfb1270848323e9986aec1216512789319db6bc4a032f47fec1496e8e752a20df8e584a32c91b6326063c343506d36668375067f080032385aaba6088a94f701cf9ffc64abb3c843b6b47853f2b8c66bfa3e01c213138032ab98dc33f0dde10768d017406fce0594726e06ed290f169215ab48e65aefc5c020ac365a96baf25755ff6ef12e04cfd28f18dacf43ed1ea4a543a3bc1629f563fe7077ff7db692ecabc0b8066d53a760f73bd8f10c03ad6992df2f0eee6ccc219926c1dba947b3584a56f92d5883e81d30982eebe64eeb7e69ba39a96ee5869e1b5542761a89daa722818d8388e07498014a67681b91675ea51431a30c9743cb8fe81a9d251a0fa83fea312899d4ed597ed7a8d90f7bb77805353e16e20691b2e77ea69642b06128eb650926a60f1dcb214aae7d68bd18e3b1c10dd3bc0cff570790f3916347a678d808a57596b410e2429b095ae27e2759b4678631735cdccc7376ce591a0ead5fbd7651142fc25afc86ae55fed83e3b4e1b4fb13197ddeaa45c1d182a7f2b3901404b8d751cd61dec5a772fa23daf09a723dd3351da42486acfa49f5bcd81ece4f34c03e6c4e45d1990ba855e6a277758ccf2d9ca9a55f40a27959bca636eb88724e2598961f3088b2e79b45e4979dbe119e15e893fc5c0a86b3b315a774fce2b89050320a0b34c36d190b6059e93c244e68eeecfe78f8b8b3f39bd380e9237640cc6a319ec6b2eab133f266a635b4c19e380b5f2619dd4264eab68b70bfd642597ac84cd3a5f46b4385466d86b9f0962dae29b8fb938fc462086335d7cac48e35c9c1a89fa6cb85c0089fd99ca03e8b314a4b5e738392ecd335ecda85d8aba4947ce515dd12b402cd40d1cfdabd2ed7f25d3e32bbafe763b804e77705b85e437abeaf0bdb4f210b6558879f0cceeebe0a2ca2f979a37ecfe68a377f2dabee77260d7b529084cdd30453973103a231b3c65e1ca9c444f4373a48f6bdbe66e144f990ab7246066f4aca85a78fe44ec15c78d89049fa7087836135a78235544e0b869c4d7f8ec7db4b9c92b8d1adac5b027caae8721c9663ce621f72eda40d28508bcac8b7db1afbfb9e6bc706d30c0aecf12633191dfa9a48584fa0637ded8fbcd2581417303cc0407e0a2d0410307ac171a999136fcf450042370c8705df5aa568b31673a57bcacb1a73ac03f859ada877bed3773d86527526e0c2e87ba082f667243aa23684cb8b2ff7a3be9265a0bd362f947945a69a990a448247c5d67b6776c6ac700c72a856ae8e3681e6f8bdf81669b17f86a768724b8719dbaa5bbef151703e592c39c9bca3b02a2fab9b5cbd25fa623848a7505b44cb7c7681abdd3b010a2415ef26ea017b1887f8388079c3b04fbf57996d9ece54dc3b30abdba01b4f7175881013ff4018a3d0619c26237fc03edb0c79c4af6e2cfc28294193dc370d6aa2e8fb5306d77d8b316db54d626a51fa0a5aa9eea830f8458d86fb8403e7f89946cc74dee9956ba7ab9099b21ba938219b204376b45cef1a2669a2bf2347290da6543a1342e6de4089e5e83e8d9f442f793837b3c6baf389a5aac5cf9d3180476f8252ede39730fca19405c956ad5db47296b0c795260f35d9721d12aea0ba567fe138dc0647cb11d2abc33ee6125848bd1c7b16538b61341e61f7b4dc7fa7cfb22c80f2e34aa954f13e6f5f9185617ad9c3d3b51329e83b7939bb769a90a91a1ab58c9ee5d0e3153e78a19dde2507bf4afe38065bc8d34ff12b45970eb428f569649bd430ee27bb437403a711b3c38a6679b190db75eb5071964b33d20930064e4d3b5479b9515ae5cee5c1b784d9085503e2b2812af79e0e074c77c58ce5bec26e42c9250ada7725a54c9cd9f7dc2b49c3a288f49bcd043326d3205b9acd5bc87f59b19e552692b204b0199f702368ed233b8b66c62f4c498e87e747eeddf5ecb65ad5aa1d385a9524ae5e6f82438272beff9118ab192ef373c4a15b235fd966152c60b6216d877055d6b444f29dd9e6dee596a2cda2d767afdbddc5afc46dcd1ae021c57b18ec00", "5151535200ac00656a", 2, 784921415, 1537743641, "8479e12630e5e0e8ea0a0d3349b9d2b8f7558b54f66cf4add79e9dc00a9c6d4b"], + ["030000807082c403030da5748c8cabe90c87cdf03d24454e7164ee6ebd39b9099f8b370f3e91668a640000000000ffffffff032d6451a7153a14b4f0c1f482995c7c7000f20d8199b3ab6b38cc0f8cd65982030000000300ac6affffffffef41993ef801c9f2f33e22dc3b38126edec174ec52fad6f6f5b4f6ec73aa1cad0300000006ac5100530000bc06b3dc0453e62a00000000000165415d81030000000004636565ac99a37a01000000000153e9a0270500000000046a515152a03906bf00000000039201b3000000000000000000000000007aa6a17fae6b4d0b62e5db302e12afc293f2f0be3d779cb6403a69f0ca6a4b7bcde6099dd3c1c21efec7156399d3c6c99735a460f919403942ecf81974cfa8015ad3cfb0945f738ed84fdc1bc1d2d03e5689cd262fc78b805096677ef103366f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a9b7f8167ddef18fff7136e9a0c4df80f59e66ed7dceb5ef74121494dfa6d38ce2f09efd6a24de14d3eff3b4bd1a4bc1088185a2e9e6cabdef546b7565db8a9fe7680133b479382f2a87f7e7eb02a74f1c34dea722a45378d72c2b64fbcf9d16cdfdf41ee34e38be8cb7662cc18e5dab7c4266988ad0dfb5939726156dc54b9502252ae60d662c5be3d0eb57d193f02178e2d9e5e0d42bc25c8137656c3c2887700327a14f4f97b5917bcb4b009c09635dcc4c62e7709254a4c34592ab4ea59410230b038ef9a88d4238541fd7fb9523726f059e50b808836afc0aebe88379955da008635771ec300e8c22dab7e4fae88b0f51a24aa57c33db59959b27dc4e46663b53022a0209e6714cf6393e6e10296b5469d5f60e38a26b182a742af90c86b4a3e180031ad02586f58251f12d9cc37107ea6f24d3662c6a5cb2da2cfdaa18abe2921ccf032ed1bd5a0fbd92166dc28327d29530fa7ab9a205422173452d3fc1d2f20b2c9e020e3c26d79126be1ecd032f13317a7a51a1e14c0f08c8c78c2b761ca947d04540030c8272fd8ca2769bb48f13c194ea152d2b21101c1ed45a8b38b6f13a2b9394120485008957b531bec8b90734c11ff2685a46dca3c1e714c156f030682665d67991e2279cc7593d99a6c349edd210791bc6991a7059e896effa1ad655ab4ce3e6626ec112b88b964b947a74dc748c50af66165d8baf9d8d5d07a5a237fc8d48003a2086e4f5d72edc3da93f6754c0d6fffd553c2033a130baccbe96704cc894532bef1c7f01da9cdce3c7f4d5fac7527ac44556f22a6e95b64837985e66f172bb018961c7975cac781aa01b8a04f56c92d5c84d944fde83296c23085f5dd74a1c5b5f610b55f62c2b05ffa5feb72e0eeddf83ce48e40238e78dd06a146b05edc58dbb7e6917c4ea6d2fae4ac3c9938da990bdd557ae24b72f39d424b0f97acf41fd2af342c556cad91449c82ede40e9ce2e042c79aad72bac6e9a53f01cbdacc232dbbcaf9baa827388bf6fc299d4e0f776c6df6ad4e2685f1f84b8474c21ca7a33a9c7975ff96f13a2e47552f98d2b5a8fd8f708c457736811d1ae7b80e8cbb51a735149a11da7e3fce2b60f4b30d11a91ce6d3609de984030f41cd83b3f46480c4783d322840f06c43c3f054b060ab6916de6a1e7665381b3469fae61476bcfac96c78fa0c1d70ac4e521bb23ec49ec8689d7a6b7bc058e0e134c92b5599a3238bad52efe900f4ffa38d83d65fcc561b0bea01d56e16baf6871fb4ae603981ac07c1da7dcf60000f714ab0a8c8ac756f49d1a9905dd1a90eb755e6dc74361fb499c107333b4e0ae3d4ef98a5b31d1a25e4eba730ef7146b2e81325e5afbd5947d27f430f89715101faaa0821a3fbf052b2ca6c217cdea37f2d54536ac445fb83e6dc5ae0994b535ec2637ec6bda24386c5971fa9675ba236b3fa6d7ef19f49a9add8dbddb1601763ea77984a6345375216e2e1b5cb7c450f525e0fc71315d4bb2af4a74ab3baf181452ff9dc0a895d6e6049623604ec8dcebe5d89358d4d2eb326be86b32e06962bc343d352d88d36ebb3d60524b5281e0c15a876e7c2271fde96590851706dfc7940c10aa2f7a3008347e485eb2d03d98e68e99ee908682a4c93c06eb86c4f8be97f1e23b1c719ca0f1044bde09ac4b5353ef6f07d35a85d45fad4aec1cafdad294d161f320c504eccc46b292261de08a5eb342020301fbbce015cf289c1a08429ed8768399debc1c150ff47b9fea2d65c39731fa3b108ac56713047f54181e6552e055ea1a56c5567dbddb1a9e88186e02f7605cba87e9ceb4b8e62636becd6f6eb5aed71747903fc173c20bd59bf39b8b7ea0560f607360bf73537e817e8aa6a8b5509b2f713787d210fb38e6775044b5f866a32a44f0aed68c1cdb56f25a640b9d9c3cd15136fb144539b3439e2631475a35dfd2494b5b413de1728e25b6c8f17d0f30fe25f493d75c71c085f8280ee123bc31468b527816dbcc4d22674bdf8155cd60996dd57ff03074f936a6cc928e3ba1f10bd40d106999015eafcca7efe26872b46de0268208a5f3b27de18c911d27a02887ce94d7fc455d7f0027ddc54fd23ccfe445cf445cb447375e5173991d55ef1897bb88d5ad5b6d25de5301dccd8a83b5b2f8b43378f6748124362b9d11424fb42698c74338ddad11d8878027593db3e6bab0582865ece8f4753279a8e7895902a5c09e7b24abf7c597cb6eb51aee67577ccaa21b33f044e408c5ea8f22f760a9d3abed200f7b3ccd6d22f78ab056269a918406708180000000000000000035dd5a05000000000a086956937f27578e5a3b527f090f38518f4e8c47fcc531588fd8fe11ec8c39e601d3a330c7911e58709d836a33b539bd0fae28b3fd965fe761397c03558415bdeb2f464462be9b726966649ee360fee781d8238952832b0c20a0f0cdbe30ad000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005dd289ce4139a4776cebec8e94f8ee34d644ba47c03c5df576e6a9596711aa23c6b566fa2c2888b6250b4365e6db59d00a4e62b44a58f460402d67793b441aebc49ee08ce400f18c1356954afa2609d68f948b0c531aa9c0cba1758fa7bf31553c5c1166af7f1f119f864d92d26ef530184f25c180fa1f4aebca9bc19f7c72d1032ccaea3b25184184a5d25a475e3b90e94593d8dcfffed9832ace1f60361baaf202192826f5aedc528698a03c3ac5ccbc2731e02fefbbd6460a83b4831e877123610b011197f3761e678f04f3de09665ff07fe00d7906d873627d4fd7d666ba2c44f5a53a44a2b1b4afb6c9216b878ef5dcd3a624088d8d07958ded59b17ce80f89d6021a6c1268afdeabd322a28aa712ba238cf360b3f53fcff23d38ec6c92fa49e29e03097bd9ad72a25fcde17a67a85399c020af527c6822e677926fa97d0528cc24010228c79b9d78cbdaed6d3618854622eea9f67a2b59ea434d045947e3534c0e9847031dede4c67e9468881e06c839a9d0b53e53051a548fb3b4979079870d927cdfe6030e3ac9329a232fc8d4c08b40837e362f9c29ed019bdffaf37860980cd70a052a80ae198c9a9d5bd39dfe73b9fdd3130426f528775c83ae5ab298fc15c670d5f3855eef2f4d324b116536a5b275b52c8ff4f4e5ca8ff4ae09bd7068dbb58d3da659a2b3f2536717b6d7b10c513c60dfd7a00b353a37c23155812869dde0901c2b596b32929e8b5bf7c322271b96488ae8152eb7692ab76a7f93795cfe19da4caf8616d6d1bf690ce91d101c8b7b8aa5a768b0b59931f6a40e3d1c22fb9b512a0971e767ac1904bc3e577c841993f68c4e82a13e32d5d69e90b205f09de8a4a34e1f87db13606072bd338049e6e26bde4be2234f148b0190e856ce79ecdb3347c97ca70d3568cd23e48e0d27d423c1d5492d942a51f5f4fe7032346326831d325fe12e6d146dd379b25407b8af0324feed3f9ae63bcaa2a8c28c4946224e05cb2f64193755a9832e0564fb6dc7fc1f2432138c0527d82b7c8d2b4edb34a651915bd1727e22f454c26a7fbf92874db85252db7324f1a4705e9c66f900e62781f9f9cd0ba2f4d7c745c8fcdbb23d8068203bd867358cd5f75294e145d28a9b90af053c79e213910645d420b40b3964d82065e917907a82a9bb9a9931976e0160f7b6091b6eaf26987387da1878470e0a018521eb79b951c3b3ae920d5403a48de7daa207146698524d620c5684cf0addaa99ea66836c8a4acc8460409e8054f581882e67d6b5b3fe20317a6de5759cce88f493c940a3e78d3350704b1fabdbda2e7a56475db55691da3725d6bbb6134a9f7e3501cd5c0c9d31d9002a6c183863da7b3a13730eb618376fe3d73c375fc4d8377d557e0a4b9b67363ee6f38d20f33d5efbc8fc8679b8e7c6a74e3ba896ba676e815458d017691d4ceda693f61a39484c8d528e311e94f1762c2d50a1f246ffa88c16cb98ebe593fa328a9464fdae7c5ccca45ec228d026d4572b712d249f2e309f0a710ad316c1e14ab3c92868458fa5380fc3c96bb9fefff83b89fd7350eb590401f173c6840e4dbfbe6c9d15e309efba1d9efca74a897f9cc3085bafdd312c3a4eefd28ff24e89acc4cebbb33c010632b4cdaba61baf4eb30ac603aa107d5b4a17297a174c078d45707371c064d76b8f5b02424aeaa731f1542d047ead97c4e714fa381aa3b9e55a1c63059e8b740d80d26573e5b53da58c3f147a1a424f764951c9d3b33faed53646fb4612bf4890bf6cf2e1225fe3d5ff61323372ba8bc6273a7595856045461edfbfc7270a6d45995610f42f303e1176acaa89252d86d3469e3bb5544488f4e471386d57cafcd20f699db18600ea5e013552b6a0e8c69164f8e43c8e3eee2f6388d6d5fbde986cf99b6837414bb9962226ec49c395e07964129cf6d378ee93c1fa9ecbd803d75f875b1219c98fd7291e12ffefffed4ab9ab2df9cf96f7afd246e201d7df39221e523bd9b55bfec8fc64655cfab3585404306b6f320d0a9106a5fb6939da4175227d109caa8c25b19914724a775712af8010427180de61d177d49cea579b8f41778c1f248e0e02dbae94f4ea59765bdb8827267d237a415a38538d7141e3126b1c843ca3ba31814de57e9765858ab729ccd7dc2414dfb51d17ac71bb0f26af56cb99d3635b647d552b3c6c14a7161fac07845ee45298749134ef187e4b3d52e0fb0c45a4691f3bb7102c564c8687cabadea5390e58402c8bb658f1487891905a8f33225e8d86b22c8719157e0b6dee6604423dd9731d0200000000000000000000000095a8f7218be020f77743ed7bd6c3fd69d5e4b1da3dcff0e3979eb0374ef7c139695f5ad7e7929eb4b8e2c52c4b9a193ddad01f0243b3fa3b4e7ff734acc873d5587d62f44deb0fafcc3b43a4dbc2682ac5369acefb6e8bf60a523ac1a1001bef0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045ba3f6cbbaa3b4896824ea477ee74d27c57bc6217bbd823176a94f45f18a220185482989498a0bf87757a21df9b7ea94395e53800bfe90c688d34a85885fa3debb47be6bfcb54f626fb65cd232b6a2d438411ae6275ab1c94931bca4b9402581c7c1a10a9f03e7828c6d372085c9f44ee3042514c00ea2fa1221ec036a2afea031287ee42c3d763793f56461f53c4f2b928b3595ff78f6d65d0ffe83b7418999f0321d6f8561bac6c544ab81cc1c76a7ea798a42bbb0ddbb505da41d8db732c976e0b01d9c748286d41922506c1ac830fb74cbbc8e52c32895d23140d66f5af3b0b1b1ff07fa5a422bb19f376f88e11dcef66a5cd2c089238393556546bedb67164c60316d990b5e69a4198c26c06ef3177205113744a4030608b872cf794b0cc4bfc41030f8bac11c9b20bf83bdf1c7009a20a10e6718b78da8c563a979a6b6c1b58d512030f0abf780b449de7c59769ce706fc795368e5e732c09290f789faf1175e5a6cc022509a74b656d02c2b55658ddb1e1ea0a1ab377cfa0c5b13c504a238bbb75db2a0316aa28399d660f30a5a2e9581b6acec4a526248f5e73ad3e5187ee3a1a5ddb060f97bde562321b6a181ebbf2d5d82c2aa147a1ff2d9045175c6cead0c3b8e4739282c73d27322b80eed87a9c7e13cbd1b406313ac8f0ce830909d2cf36e8bd00869f598d0fa9886ac6008e187425ccd599cbcfaabe8234c5f081f8d704904e1f453c321a3573b55966d9b4b830a609b676179ed5f58ec23f92f0ecd7aa927261c8a70caf592a3624bef3471162d15f2538d50964bee5bf804aab00f092d1f371b1d566e00b2f5f5c7e266b59e6ba293afc689a5a1aed7b973ea8f998f7b23ce39dd6374355c824e998384ec667cb2dab247f4ab8d359619b40974c43e14663b9222a079d601a174495ed2d1f077fd237ec4c08e717233245e8197c67c20975aa0e3c9beb1ce123cd56274756acf89a8aead306feb7fd4b1b834004c2a121c1fa35aef0827d6e0c378f98830b4c433d445485c9df80699b4bfbcb035bd2aa0c46bafb8cc4f972a8cf6b4e154641e1b255e7eae0faa373dbd4ace1b2ed3a20433d9d180c65fc1eb672d0200b053153106fd27df0401893ffd5fc93e364e53b3f475998e7a3995d407cc1f93f77a1342be73ebbfdcf2fbe66b3e9e7d3037d897a6139c11e97b1dac7a92c77108555aa079a257098fca35eadc9a7fbb2e06e855ad51a2432cc9a590047418bc49f30f8531c93abf6cd53bfca6373c12da07c24f4c1104721096ffe0d2117edeec12b0a268244d7e982f43673bd506477ffddbf17db3e1881c6aadbd39d0897f2ba2ce5d94289c3ce2034ae32cc6f238a456a52e0dd123ffa21ab286b083c5ea9cd37df98752d8b9368d3956a30c5ff578839feb20b6e86a351db6ef8eab881801cf3b1389a21544d43368048e64270d1e51dd71b5c1a5110fb059a5364fc93b200a9ace11283fcaa80083c4b30af7e0ba8d2729af416eb719d60c5e99528a28fbf0e8cb8433e88f8fe51ddcd78137fc3d8a2f1e64fad055e61a9e44682ad145dffec83bc129713ec6a7ec26a973785fffd7bad04f6b13bc19380899940f24c6c175e2b9dec6ed10b0fec00b008a0ad2e12669103d124eda08f24881d4cc4f8c0bcaa6b6fb5b742e2fa417ffd39042391f669c8584932ee1a0cf752503ec6ceabf2ad28d8e19a878fe3aebfb44dafd690aaf9a07617808ab29ec491c65a79e5518cd768cd4d9ad140de4697d5ef246c738e815523dcd2a4d86b67a706b35053bd8b6960d77978533196b0d48ceee6cf03195c62759193a448124cdc9331510eb5ad90d2ae8e35bd682963008ebb33a2b4fabbd8659a6b59ec9f3ded0e372d357e37c219035f896e6b6166dbb65986afe9e31676092a0d519c8dbfbc719ed10b3f7ca34ca3dc9eda878f3877cfb32058dc05172410330b11e34b68b4f9abfe1b02f81e39bcd291dc6e3f4d955355029b6dfa08e65d0e608185cc28b0fec2574d7b3c78cc8d8963c1783d935761d74e57468eee205a1f86ef140f8c5a47878ceb632572f0de181d595140d2865ec339ce116eb3dddb4832dc9ef199d693d21bc190ee82134b84fced76dabdf444f90699919acbab05b74e00c25dfd9f3c8f6ee4d2323d4c89b64b888bc69cf86cb4d4716ed53985b67ecda73e5ae6cb9ad4195e39cf8723b54c8efd3fa28f0b94994a13666467e2b7e25732c0c23bb2ecaa1c239a847116fb02183cb25f6e3d3295182f6beccda329ce8cfc34f8a521813a91d019d6fd97ceafda9a6d3fe9856526b4f0adec0ebae6dda42ee4d83c7cf266390b73169aa43cf4d86ce29a1fc9720740f9cb40006fd7d9a78467197fd207182f3a016378d6fb769a26af178ba5d811d9f051806dc98046ab4a25bed4991703c706419a70a3ad552f01", "", 0, -1506474503, 0, "93054d6ec434d3af7ce10ab263ad69e385b0673b7f99a7f22157f17782b8e046"], + ["030000807082c403016a57ba705f5ba8660856fc885dd796093b88076fdeecbd55bb2ca0f6444981ca010000000651635265635255f455f601fbb49e010000000009ac655300526351636338f5bc330000000002000000000000000025852505000000005dafaa6beb1f753807810a3d53926cc82f8dd05ea1bff4db79e5e837b8597d627719d0d0411091df8a4b9a4c484edc55b0e138a15a271647d3ed93aefcd6e8d53ec65c6b4ebba7645e7e690f0fa5fc3e9d0ccf7ef4fd60c4a4d42cc0339f4ef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc7aba255cc46133e21c673ef26193d63954389a290614d8ae530c19db6ac52b42d8897f10788d574af92bd09131d910ebd7b9c1eea82c2e34f47dd46e8ec88773a1eb84f09814313bda87d2fe05a0bd4f5615c294ab8df76afdebadb3fb2746ee7ca0a1ae3a50fc1f24adeec8f84937672653901b4dc3a034b9b2d4251c9e9d02216842c5742e56247c4be54c8f92bdd68f81a09645e6c68f85240ef1a620972b02241a0050c49a8690948beefb3bb9eed0464eb86ef2b86436b82bbf05eb7591860b00c056dfa7526578e3adf53e591abd49ed623ca32b22fcf88abc1992c2de9888bbef17280720fab98fe1eafec7c4be0eda2d5206af09fa1e71c92bbe32713e9f0201ed2695b352393a3fa7e59c5da927181cb4f1e5186c74a525c09bbedf9451c90210acd8db477f1108dfc65a270b5a00a567e20003cb44bf9d33be7b0285bffc1a0202aba2b060448614ea3f9db36e9a1b885b4bbff207e6b664c656923896df045f0318dd567b620466c6077250c315a41c5056f56e870627691ba7680c3f2674a74f0228a6d82e7bcf01b2c930b0caec2d4259cf3818b1a6c23f32c722dd928cfa54028b5a99af4b9486dc37c476e1f8575c06f24ce7ae24ba8bebc9c4e4d76570f4fae91d7b28073626f25095e1058acc3e15e4c0f80d42e4da77a97ab8f2f4eb21f500da0ce9dbb1f1fa836e2e2e7bf1ece6b3dde545c1ed6ecb2127311af902e7e539b052c35865fb27707c6712a4e0113bd923e6278c90e89de225ecca5df160d60e2bcf5296418b7dcf764a5ce440356b99d89a2550001fd5fba6bcaa38180657f1de595d9efc1ea5ceaa87348097f1fffefb7e9aebd65c8ccb8076902139e1527e656639af7720fbc59baa5f0b0802f1ae82006ad631ec8e1f6998728482dbb38d3223cfba12948f6f880022f4adbcbd49be8e3b8033fa185a1f9a79abe299ae70efb5b1a48ca12abaabe4d737e6c1c6f8a512a648c4358064fb5c86242a1e15f6f7d3276f36e73d6a940c36bb1a9803eefee23182933eaff96b4bbe2912a97441d0fa054ad87c2f686587390df010caeb32369d11162c476afb408644a63b8b0f13299baff2fe6dfde96d4bcdaf748d6c25296a95e97f70d39f1838f10bd1f42be2179bd086407a9e2ec7aa08d2c122770b3643e160215c5e24902cb91a65d5ee7b81b57bacbd4a2f545adccde4638177ae08f6aa91968c1ce82c4066204e61f984ece0cad182f15d4025bc87c748cb024363a3da284e98e369c3de73ede3f083e4f58af4f9830f8784ec79b0ec4e2d58958b8f8903e11628336ae33021b7bf68e69d372f45dc1df5c0eeb8f95eb4e85181b3de1fc26622e227f4ec27ab775ace7d0d0d830a9b3463ed04427c27cc98c2ccd7d7b5986768b7a4d0b7352dc7c0a6f7766cfaa6747cdaf2a1a24cb069227d36d356196fd972a6be6c5a6e0f79695b770637bba984e30c61dfcf1dc0fefb9f755b57879ec9db3308799bc34b9c799098548c3fa983498d3121f38feb1855b3a8ae038713873e6c4c5997767679b36fb4496da48440636da649885303fe0029091ca55d56a4b6e48b7c823dff5a94ad392fec0142ab572503eaa949b1cd14a6802d15dbceb07a4aa03af291e6210ecf5c717e615cb8a7edbeb12b0b97289777bcbf70373d640101f49e9d29370c26471dd8f291d3a96e237b1727e6c65c4d24c12ebcc86664259013e3a8c79ff01f11dd29232c79d45d4e16979fa68e6b98773586114a43d58c6500622991f8091d9de01c9889ef837d4845f01d4ec0ca8befeb1a214717fdca7f533a4a5671d35e4b95613c6f62d15a8dd9bdf19db6fe269538fd39bfabe886dbb7bab14f5a1bcc32d7aa47fcff2ae0ed0b055fa0d93b58e73c40a2cfb391d721877e0af73a01db0370e65f6fff70195f413b1ad5dedb1d5e6fd83b53e3583dad2c2af767aa14ce0d37d71cd2e27ec762aff4ab5b1613b721ed492cc92804c3622d48116498985cd0ee7410f697321d23a96aed46e794c8314af0b79cc37ebcfdd85894cd9acb01801bf1b30d1ee1b639bd8ea3397b3eca402c67106fb00c74bee74988170cbae1c2d524597408fe69e1a7d924e97717d9f467c0761bb6704c1279d8f1d7658c4fc96cd945cf6af6d6868b2394f572f0e9a3f9c7bec6a4958eb3f622cadd8df969d10443d80a45f2d5516da64a54c2cd93abebdf49364ca413a58327eea412dda39f40e5d1d7ee5920c01f04b059346239b3af271493435e72e887f6be04d16516a39886cdc5261ca4f23b45839ae117195c87008f7401000000000000000000000000bdadeee689db13e46f447710913c92e2ac1040c612a5f97946299011f06c6a3bbc746c8dae85e6f44b627dfd72c51d53e43e7121c3403883fae2a4a4b4dbc0fa40f476498e89a9b9e1116069835fb91a1b9995d1bddbf730134f8f8885ee9d4400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d213d1bd5c50402ee93b8e64830192cac521c73dbe38452516dd5582cba7058a61039ccfef582e125e38a8732f6b06ea7c210a5525a5af23e5e202a22bf548ca2ad25aa54b89d001de766af0329d093fced0ce4d2630e371e8a35e9d2f3a5a2653204e6302634ed33abc8f0a9701c143196bffe30185a413b2423faa855e980f0217959d62ca1434357f664cf63930d3f63578b9e2b4794b4950e604a8fbcfbcd8020817dbce4db4b2e6d9caaae5d5bb47e328880c4fef3e745ed30637ea02dff5060a00ff68f60b9be828650cf7c3673f85ebcb5183f3a2c745f231127810736c45ac1b63281af6c42ec55f72f18bbfa48042082532e22bbd45b5ad6af6547bea20da0221a04e5ee97501eeee45845e897e3caa97c16268f524d7ddb825bd67c927692c030820e99918325fd78043a5d8088687a15834972f5e8bfa20fb75fa8605038e8a0317cdf947a7091857775bc0a4dd2f822c753e4b515e7e6ebef81460b8e26c04b30216cca3d30f213e46637061f457f96ccd7f4cb08f74d5fba6e6ef21e1eeb5c7da03243c9a0ce26317328815f3dae43cceaf3c2017f5d1e802a0db5821f8dc51c8fc8ec7f84435916b1dc5fd08d227519e0fcd89fb60cf9f46849cc12032ee95adafc111467125a301a83fbb8cb46023eaf7828a8aa5c5d5a1412cd7c652c6fe692cd09e4f43f5ab16ce12cc22eebdf69b99b8a27c059e73d7b2ddf8a941404bd1538fb921ca63f733f9f8a5a35704430f4fda37c077ead35af79b514fa6e405111593716d3cc5050367acb83044b3d3a0729d9911c4ab608202c23d3e01debaf404fb4da3a6195287f984a923a45bac88db8cba63e54dcaa764651e63a8361780875401a2095b12ea4e515e129f4783b1e456a7609f07e92a995f58f6201720e2dc797c7d82beb5b875239fa665653e15952d60d4acd293421aaf2b4a1ec6b834c298bb414a70893132309c52fed6c3586ab5bbec65d427397c37a8e7f3319d5c0acaa3168b57bf5789fc0aa288119c9f429164257c495a5c35c1261567130386eb572d2b889c4200650ea664e187f523c400fe5eceb6d7d5eb646bc77a67fdc01299878f1f25505010c6968d6fd0c86dbeabf28ddd4a768e5ae0dc4f8fed2f3f8aadced33c1028993517627ba94c611cacefd3f9d9667e891b4f4ef7551dbf9af8dd03ce5b270306f58eee9e9199a0c66f6aef0272437e112d1cd554cbd6ab94d8ddbcf55f06c5d2b7e415c9f78ee556255ccd05773af9c1270ec4252461a6a827ea5c5759684dad4cf565e66aad070a19cfbff4649d8d10d0e4ceb7e1a8b2c98f13e2f909d5c9ce9e38f908bf50e3271c0e8f70c325f722b79afa9f4a8fef4052e463b25e9fff11ca5544f2c877c961621c913333dd0488fd4bfbee69ec9380f9320844b0de8d0ee6a7d6a4a78caa4f552f8ed2cde1edd1e8bb6d1d463b872e59c829d8b3b788670525e4f752f411efc9affb8282829e2722adcb210d4df5edec82fcb53c5df62e353b3a6e868ac20f83a670149dae07ccf4a2c5a5b54070db937cdd421ec001bab947412106fbef8bde72175229b3468898cebcfeaa1e015374f14f661c85bf1c411944da997c28f390eabb37b5c2fc2cc75af623ad095feeed31eeb02d5db52e5b645a125ceea4576f63011646d1ccf116c6dc3ae31b817a9a13d65cc10c60fa7e7542b4f3c165cc39746aa330abeb3efdad278450f26878d68aa7074567c15bf8a3b3c8f2aac29f17d166947bf2a4df02f761e07a066399f4d0e9c638f234f9a0497a6ecdcf07a179f308e8e7f87ec487b2e5e56c4c3aba06b37bb2e2fe8ea127e8796c5916c96cfc9810cf564b9f0226706db57d6c0cb19ecb6220898a7ea7a3f59755f82432f9f433c6ffa2358c4ef38705a80ffaf540099dba17b4b365dd5c75c86d37465ecedf23035cb0ef6cdc2b1a2c3dbb88a43d6340ca150cddc0f58b3a5317e5e2f6ec39d42e4c33ab10760dbda5aab6a61eca933061200af8adae759b269047a7cded6bf829b9053887aca4423320ef57b2770b087444389a3fef8e08a4b3b6679bb58911eb2ba79104e22a40952e2a3d88adb08c9bd41b20e987819692b2466cc1751fd5518992ad042351ba5180390c03e266cd39d242fe0d55a8091287ceb08beea5f7281658541d7370411d5f0aa44d440ca4e05ee66df3f5c684565897f1bb64a733a7c7a160c4aa0755b4d86d2ad7e16ec7cafaf94fea8288c96e8f3442b3a2db8a2062a4565d543574d8aa9a4a73c1b5cd6b873f18af61f301e5ab1272f56191ebcf89201afd8a72ec564cf38bf1cb4651dc4b6c4f521591426c526c5d10819cc91e3a4f55e6ab30cff6c74a3dedbc8ef37cf82ef80515f42111bad84def5de32124c97638359c06bf6007614b62e2914708d7f15619a179a3bcac9c693f7245ab0e", "65636aac63ac", 0, -680068179, 0, "be4e02bb54df20a56342a1a152dfad4b8f039007df6a39e123af7bcb894a4b2f"], + ["030000807082c403015fc21b794ecfe157e69fa071d00ceeae76d152ee5c1eddafa15c8f0b7c84477800000000045252ac52ffffffff021e6f12010000000008516a00656aacac53878589050000000009ac6a656552006a5365aaaa1f1f2e4da2b40100000000000000001bf74f0100000000a3977f7a9ee550211c331af25cf6c9d88b0df25195061efc2cf9ccc7176061979ed3e1145efe38c6b7daca96642b17a4ba8c3fc91dc9ae8ab819153499926ddf00f85e4ea19754017bf9a3563ff2c42ae812a6617fad66fc336a965aaef4eea30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022ce1a3054e9031c7a7034f9b8cc9195656ed4f8622c110ae37c8167aecff331f610e7c1e31058026450f4cf7c0de2f7ecef74231ce5c631b5b955e2803119f5259f7570c4d951360d462795cbd3101fdf5491d8e1c12523fa26707624b84849dc806f43f22b6f5ae701fd9ff953f6a3e75589fec532444c2b169f09a1ff7e4a0311c9ca516cdd73533846679751af76278f33e228cce975d17a2e22463c3db3dc031400d11266c7065b8f13a864d9ebf84a140fb3329b3f6ec50cf7cd86e50940500a05eb280a9ab1d7a41e3b9a109b21c675183e94bf34520a2d05cbd928146516079e27859a6b81dea6e01576d4b3c5ef1abc4cb0a16a85371335d7da68e5a2058d021042833d8e566771c0acb6492cc8c18410536776f7cfd7070b7a4fca9ee2dc8f0229a02f0420e5a91230f6873c45778bd3084639daa2c8374ae686d5b405dc589b0329ff8c1ab6210d5e7d41f4124bffe0a9ba03d6867503b2f1eeff399aac66f7b30226c41284f4db45b4e763a5e2bbd70d532267f17a6b6aea91effec1003df2088f031ba8044528d94f331dbb3905aab7de38fe2f4c0e4f9d78cc9b255b25600375710329ea6587e47fcc8a184cbb406e9e407238150ccc7f7575c2df7ca87dbffe7301a3066f2e4b27e0a8b29827e8349f93b941ff39cfe9a56f3317c51ede5400cbe8ae0c78fe0e3b9e3601f7d146c393d79c9ada051226c39033037754bf38b5791ffa2946ccc1f8087ed880776016124626e59d444ca5f4724e62b9c61bfd8361907d09bea172d6714f05fc9680f02be81e6e6b557358ca37aad9a73a9fe1c35f6756ad7d1aca140c11ba269da8214c6337b92ce7bff7d2a4f8b778f895231c931fc563dbc2c4d73f8775d692f96accc751a97ed08467c9033cced12a8705353523253568ee16e504b7cb826d131b077778da75a771e8483256143213dcfab17f4a1c707c9d1822b02243f417fbf090b4cb28775c74c45a55b6efef9309fccf34ac5f2b18021d0948be3a88f17d679043fb7cd5e86bb6226777639ae2e036dc8d45da463485ecd4f4e90d5ec184c7be4bc9179022b8d3ab2a74ea374efbdb4809e9018932e95f9aefe94fe675a45c18681a6e498804ca9740cc418724b73d7455c7f86d12326d4d817a61d74074841f63fb946c54b2be31d300b2fe3d7ee6589a618c41201d27359ae3f64782b33c912c7e6c8541a41ae33598ceef5cb16eb416a4dcc91655931b41f29693378e2b43dc95253ac20c89ca0294bfa199cdfc6c75679632e41a0b5c40a407ed4ba421986b46967d8d3bc99cf7a90557b790baa370bdd1beb55a2c69f150139d5cdec49a6e13e81fdd0890b6714523264e9904c8363570cbf809298fbbd48da7caedd9f6e47c0293eb03c8f6d22f512e1a2fe120ea9c99370d16929691da371b85a30b89dab5acb1492eaa5b1e37cf3a83e05d637f324c5c5d8b75a43f27b2c9277b4eff4211df29b5dfe72f1ee18e24d7cadf5379d4beecab1f720850e23a9f8c898e2c5d7ba8d53f243bb216c62e95b3b2918d75d7709063d274cb3107ac461781692be642359aa0a43f02d37c80f902a10c003a9c73bfeafcdcec5375fe37c596c4ecb83384aa6d2369123d582b1775bea90ffef9b727743fe6fcb8051e735ce6e122171870382fc68830c77df4a3ec0649b63d5ada34c7e870ae466b4cc03d95bfde7097335bc1784257b4da932e6f7de25e437e5eb0b5e5d50a66e229d4a3525bb0ae4548320ba5edff02c991ec35447b43df41cc147c766fd04c89684026c7ef52161895be8f4ffefc4b9edc0b8d5538a0b8304325971842be04a882a4f8f51e8b1bde24c259048c3541ca9dafc28b51e1166a26b526c29bc89f63b1a4d4b32522fe48055f210ca6ae57cf6308e07a5807df4048316f9f09aab505c144bc87455dbcf812568432032504a8d22005910d2768b28fde4994b75141ffc26ecbb050c40a963e27ead703d76ff94b9accf2804bceb5b0bd660e4ba058fc16f7883bae97433d408fffb2d4c7b753a0ced96c03ed7248d4955280b67eec8aa4ccaeee088072afdd0b40d36da972fd97c257a288f9becb8b9728862e2ab52f9c9acc8cb883766cbd14193e285b36b307d28f3536fe7d936dce89ceb94f643f2d523a28ba4a1627790e81c609064d154e6c9f094b3f0bed2fb9bb1ce07b874c622b775f39ce4e868dcba2234c8f15ae0449254dd3b3ecb70d6ecd7b0d83e0fd48b9d5fd13d1a1e6a625f804bdcf9474571730c921fd3a80aad82913f3f7ecf9ffdb4e68edc7a1be3bab61cf00a7c911a16d0b314a0833c4057ca1ecdf83afbf059b60de90e4dbeaebd7c1ddf0c1e939ebf30e879227de94831729ba134c7bae26cf9e895c3849f5c1a442900cc80eb4d8f77d2a8bf7aac5f00a34ad40fc7d3609af72e0a7583d7008", "51", 0, 50196424, 1537743641, "a197a01d15959777a04ccbe19bb0c4e4514aa85953302bc6260796c644bc1f20"], + ["030000807082c40304ab54234e5e4f44ad5bd2a378336add9d7ef3f0d927c4629f43e47c9d7092fe810000000000ffffffff51f4d94205f10b090cbe85e477506dc7fda4143f448a5544c7ab861ced7bf7b30300000009526363526a63ac6a5146de706410e3df0371c5272e6b31cb80ed7f2b6445f6cb51b04c7b51974de9cff7270a3702000000066a5253655300ffffffffc348ceb123a86b8b59eb4bf8229108266f1f28789bb4153a5f350313f6d0017f01000000075151636365ac52ffffffff0319e4740300000000005ee95e03000000000363656532028b000000000004ac5100ac3b2add42d09b9f4e00", "00520000ac635152", 2, -1843389684, 0, "122f2ecf230b7ba98b0a5b355a71161d24f45be74faf3da10cd41af1627dfe95"], + ["2ee64a7d01050478e40494e1e52b5696da75f0c9aa3f270a9b6e5ed73377e789e4f86baac901000000096a52656a63636a0065919eea410198dea8030000000004536aac000000000003000000000000000097bd2c000000000033fda610b9791949ec1778a3d551313b707878fe29e9ca410f8b770f2127cbffd8342af1e688242c645af676263ba2d2874760c3c7bd13262fc0d146f2bf69b7138080231b7378c06515dc14bd030f46b757d7a9383636fd0dd23bb27627736500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000687202e963386baa88ac03a677baec0db350f2d155cdb8aa3f58a24d6db181c39fca239e07e8125346c50572f7a6035af883d1d99a422727691300f5edc1b4f63f9cfc26cf079159bd0d2fdb6c08170c39e652685e3eb64cd24ec15eaccb5ff2899a23c62161ec6c5d1abb14248370361db56abe24acd66252ae067a86c0148803267625fac571d1f52fe264440f09635954b0b44ecc7c6e8c66bcf11689396674031eb5dd59dfaca3f20c8afd6d4b867937cf7442cb61c811330031c23f9b79b7dd0a01f2acc4fc2eddded43521f3e0eeb9d227b8445ce357bdabf2f1a61a4e2fbf8b9a53bba457f21fd7580a237a7b61c4bf649be4addc5f3139f2c8fe5efc18ff9b0204b5411377aeda597f390a0b06ca27f37e572dc52f0bca98acaf6a1c39d126e8032d44a6f0ff9687abcea963bb4ddc6ecc8af1de0338ac898b822478bed2fff236022ab5e043f2012f13c8149fd5b90ff8eaaeb5ce0b6a90319378756c580d913a4b020d59090b4c21694488579d57eb467b03547757b2478a5d44af3c49080f9929a1021aadf263e66dffeb29978100020f26a2d70bfed549ed555ebb92735dce88d1c84349ed72eac23a21ced3a3012e4ec6a8aa6dfc13ac6fcad0619ad1c1820c3be433250d665199acdf930b8781e65880d826a308882734c128373327f44c2c77cd094bd0d035b668fd6b66d17b52e214b3bcdef5e81b1ef2ae3e4220872c68ce48252d352f9587de087f66691d2b2e985287063664c1fe1020b699114e9129eaadc59dc0e2b0b47e6b0cf9863954898dbe5a6b59d3c15a5b5e2f71c627e1e1975c924b82dcffa1fd373c949194bd52b747221fdca694056064a2ab004acd49e1122eb5be8d8e7962a6aaf821eb5da129ef03b72d9bba42c545785f042b7bceb14497e3e9c0c84e81dbcf6f1a0843bd4968f602e4d8fa66025e9d842e612ad072187064e25b3187f39ebb12c2144177c35fce7b4fdf20545125626ea11213faca6a42813af20b1d733d601c98fada1356a7df7283f88c6e7f1b9b80ea9654c01da2c1763dc35ee98aef74aa113f77eb2d5d31f578a9ec04539a9f6355b8fc71ca3734d573087623de796a4823838586ce917f36724df7e9cd4dedc0d7454fff69f49e114d903e396f13e56b8eec7a1bb1e84c34f20c4ac7bd09136950aa497fc386d37fd5447cc0febc8e308024b6ced8ef19ba5181fba02262d59b1f172b49a345d4200916edcba0bbbe651ced3062b62a2f231bbd3796779d049899df113d701cd297ebeb707ffc559d71be5d421f5372e717652c5c80718d58a6e2bb1b92103bf3b3e8a5947cc71d3a75f98f14f90fcfc6a98ddf3407fc7275704066161ea5e4655ff7c0499f2b0c11a97f37a5e1e419468fde5fb328f62798476504e903845eb831b33210733e23a7f87ac8bbad6f5250c9f3dda9f773e9a9098941b56d0845abbbe4e4eee3857f2e3d38d14fab88d969430e5f1a08763b88112823ddfc5ec5f9e89a6ed0444ab26976e546a8c627daf317b94fd58d28a9862a93b8bdd60f67a33bd62b555e7117a0226634109a425b33ab98782a1f05c47bebee6d7d32a493d36ed111a4c434f892660fd00319f01e583ff11cb88acd956d4db1f4587753c880387d87afe2348f883e4da40b7ac6399bdc0ba59b529fcc54f2358182c4c39cf8a2d5325d4b48fd49b8411c906ccb3225711a20c026154e129164f4f13d56eb2b663519e88fb77b9e73962a888d34fb489bafd7aea76b4a6915025944a065f76674f0b094f7832b0a130b72ef9e81cdf46c2de8416e959c3fa44000975e26355b6732dd4e528788bd6de7804bb979dfab44aa03c8d4b8fa8e6bb565c30710510ba860ee8f1fb5cd50923c5f72998cea30b35fe7c9cd57a8e57060aa64dcc495c0ee38d982db7f3846d76317f65c7f1cd2e2548938de25fdf1e65407d21e40a91e4ec15abdf613327449211ff4638cd02eb3e73323b44349fde5d72b6f399184049f6d021bed73fe9c20a5869e67280ddb730642a37b8d92eda3453c47fdbd78576b5df180cea27250c241872081727ef5ad4b1139685c55585543c27808e4b4f3d9f2e7d0cd3df3cfe12f6c1427984e4d4ab5c411f0931e52ece35edb673e78644b393e3e4420708c2794e02878bdf07b5a3a233f8e9f62ca87aeca4d420ff6ce08c831e64557cd5a4b9c210dc0f074d56f69e416ad03667f9bbb4fee262af1eb3f547b564f5716b113796b16270a4eaa9cfbc93beb26d36144bfbee1f89f86ea911d12846516de976d3536c935420fcc4eb2d419000000000000000000000000007d693a46e7fd8f8f9b267f191ba50099f67127afe88002b4d5819549c9625ffaa8a02b7e90a26f780effdf20406fc6d5757cd168aadcc14dc2850d8875c5e9122a4bcdb0a34646c8541d97843464df0cc45fa25e262ad28eaf033811535acc4b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018b36f0ee555e7c6ecdafb6199086cd9c272d3fbd33e146c69e9590232ac797baf60fc796145c7532d114e891b0bef298c722f57ed628fa83f19b8423426ffbf541a6233fb11e95dfb540c2ebb0ddb7f6d02bf14418e4220bd585cfd0adc8922980aab83c41928af00f8ecee3f1a6935f7e08d8530db2996b69fa6431583b2f403032aa45856aae970392a3d7ee73d08ad165d1cad9137f0511fa14ca34498fd070222567f3323f113be924e0adecf29e239deacd76b90ae49e3b48a2b202620e1e60a03dcdb34e4c6ea23b2aaa04fdd1263d4a562ce7ad4281d2f06f170e6f207aa59e70cd49d1160337d47a4590e7df27eeaa791ae68b34c4101e8d13cb895fec96a02174ad24838e437613e0da69553a5a9ee4d015f6aabc90ebebff723bbb31ee3200317f92103fc41110fb82043d2e126e3470fd13facd976f3a63a4e797313fc682b02187f9bbe7a74a772b4758b22363e9c39fa374bbc0a686cfb8679083d1a580f310210395ef39fb13a1d060deb4aff5bb3e4bbfd8eca738f870ec1545e026fbdfa0d030e048b470fb45e9b6755bf54b45c9ef715f60f49379453999854f0986fb5f8fbaea85957a30611c4bdc75f908b22bf7a19a077c0b04b7248b85e7336666ac3beff53fdc301230c567934bca671e43ff0823107721c7be3c06932b55a4601515b474c14f0f58e5913848b259e8fbcc1cf25d11136bbff0964e6d256ba0b80fd1f76fde5a81fd153a8f927b973a6d19f1853b0d4b52b18e486a1260bff96f49980201b5be92e2987213bd3c9913a796f2e8f55efc53b23440eb49f556148d1b4e73e574dfd24e73d5e60fb51b5b739ac7ec101980e7044272b15b4b902b9d32bdaa1a633c3f71180442a1c75ccf8f4a7470107c73b11cac9fe985ce9cc3e53c764dcee95a9b2ca7a615e936b1a66558842300bae139660c85f20ec910c90f771cfeb5711f6eb1cf9d69ea93758b042933e7abb2da9fd8ee4b8da2b16eb89f8d0d8516ff3e683e1ae95cf8950776e8f301d9fe54a99917eed8c1a8afdbfa1ef563cb1db5f28cd80ee4fda4cecfc6ea4349a9a837a3914aba23afc5bc6be0c6cb2d22fe4e1993f095360180914684b6e800a0fcd56ad5d1306e5b8cb2e1b1d8b5eaa1c1702557792af254a30fbbf801cc16151cd22717d8e738e4e1c5dd90e06de5322e4527cf06b5bd7978ff08cbec1ac1a91a798254af353d3250b9e69124ce859c8ffc50f0e86fa0b8349313cb1ebd7ba799cd00a104f7f23ee7988a4aa2f8b2449075f0d1c1571caa43e51edd14a5ba57bb3d2e35ab3b59de3ebdbc3c65787cd65108d2a532a2be10aa1d6df1dcb38108beba8f7dc36171eb253ec2af247832996542afa4df943e4eb64971fa3f275cb7a6196d4a077fb3d7007af91f04610e78a3fa298a971759a97b4eb57a219cc11f0b251f438c7792eb6b3915380824131586af7ea0b08785d279ef315b9723f457c95f187b96f303b54cadee520121333433023c3a414bdd26127d92d777cbfb2696f02d58c987607635db2303732409210b043554b90f378b1cee9fe2fac944aa3727ff89535558acb319e72b9725562e7a748f10d885ddfcc959d63c03fc78f8da060247d17580892278b9c2d88de754634c83bb1ae08b53fd42300e533c8f8d16a21e3120ba40cafb891e72bf52367f7393ef946f7a1fbd634d5d90f22ec7c34a2ee2a34116b2877a5f4f12a8e010dec4ab757ab1579d8449edaa4b5fc715305ef8a49f2f3f97d86d1d0784d4e8a1cd0baca45c27515e23ae9270bc96727c087cf745399d3f136d14ae15403acf9ad06065203f074d7a8c034d2e5041d717488eee3cfd036fd528c8f279061e2a186f13a28713e9d03a44eca67127e4fa0db7414c15cf012b8d2fc8d3e858c92286308007ebf18290a1e934af2027336626b677084d45f0ef0d2c256cac333b79fee0d7d74b704f66075cbc9bb1c111af9aca8e8d723c32dc2729e627c125048941ff73195636203d4542404c81998216053eb69521d0f47a54d4295be864d332ddb16910cb869f06f7abeb6fcbd37d5103a14f179b90babc5a17401ecad777b7923c743d4524201642342751c270d46eaf3d86c72d89450f91817c139d5794ca9c379fab0f06fca1b82eb40b8614d9e9d65ce071327ed556d26cb6a7b87f1b0fa83754a371f6c17374d75ecbb03831b6cc103032bd684dbd55ee5a15d78d556be3e5519ee5b377709711b62231130404d28ee2b273fb8a4149a448b9110591e5d9ddaccf10a2a6b777c39593d05f7c4f3e72270402bc30100000000000000000000000037709086284432455e15fe0afed42e2e16c096569b396bbec793fe757bdb83e3f40b3786ab6d1462ab0fce01248012df8adef08e1a411b4efecb4feb0800198cb512afd5827cd2221e020cce3573366befa62c9c560b13f44453e18c285326750000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075fa177b5849b1002b1535a920e1d0c28e30f111dd5f7c2f446c336eae6f47857e49b3e62ef06645348840c7ab443956e93b5dfc8d489c82e86aa173c3259eabf9dcd2ebc4a6958ca04c91c17877e3d397c906dd906f2730624fac16d4d30a09273ddf3c26e88f66c826b611a9853d6fcae0550ff3acaddf24ba8c627f81c92c021f56f32d3c053ad88e2d9a8c8e86d8b48a51bb50f042e930ad5cd4588102f34a021e6203562756f23c11be32be6494d60985ecea731375033d982709814fe1eb150a06ed7290c5fe731fc6930947fa531f3794046b5cc8985a3b458d8d3da2bfe968eb29f3782d57817c525e948b5beeebc0bbc477c2bd65e93a8b9cf0ed06c96aee030d0cd552c6564e9753a9b76c8a50c57dcb062d6598529893d60226f5942ca8c903061f6fccf63c8385957dc9ff29d7437b0e2bdcd46b150ea51e3d7892bc8a335a0221e72923add45417e39c41dd186629a57f75d7149c6c41d246772f475826d9fe030103d13bdece9966fad4366e98bba56204c5055e3bcb8fea31ee27bd2db5f2a3031a51db2567f640ac3ccc15f53f7a35999827350d28caef40a635f5132b97cbc3b42e1c56e415f03bf691bc0ee5d026a696d8203a1eb92cdf09557e613ab81cf59e202e6b61b65d2295606de67a983e41aa760f69a7010f35573bb88e4526edc41f2ef6a7f95bda7afd75217205311d3969c38d8ae7c888a34449cbd3be4aece1d7b5e7b4246df9c6d0bdbcf9f7330723e35e08d0395d1da2eb3f4efea7f0b2594616ec9d6df6917c956a2a742f2f03b184889151444c9b6e4d7110a06ee1eca37c00eb45e529aab55588a38b661864b0f33ccedab569fdf885654b3b88dc9bee70e414ffe984b681de2536de93c65105532aaac1e61ec541624c8739b4ef376c4c79aeef69b53af75e318a52973e380f053d243ab9e2c9dd19b3ad93a86ea9d01f3ed0296b0e3de46b0101302593e123e70c0f2f9a782778d95a36e9a20d942da0536ecf52535cd1a7be7bd26e3b6e5e32e40b5fc7d4b05204fe09e3fcc4b8b558f277ea46166ca4806a52b94328356e866c1b87d5f8549d85003135cec093a14c4738d751e834c1f026b25394dda57b15c62651d9e94c00dc8a02e0d5f5ddc8708dda62a068b460599f7c025c1a32722bfb6a4a6b511360d1f91686977286c8afa211bb672fd848f8361a62a28c68568c88b2199e282b638815ac72b28e14141fe6707a7a41dc674047e2eb05be18326d28d4a68bc1a3c5f2de6b63fedaac84568116e507c55c14a84d4fc9c2afaabe16298012e5ffc2be2732eb64b7797272ee6e692add780512d0f1d1c0c77189e1eb6b357c4ab7d6e8b382f8d00bf77f5b63e8e9c166a40a2f238124d99cb4d71d471bbc5b7567265ea53e0bd21093fb223256ba12624b629947062b28327389781a6c338ddc7c2b80e2254b5e3e083de22f1060deffca04527ccfc2826b572c477ba44af144df336cd50a3bbd9940761187d95a82ac678cff97faa5fe6dc5a34a9d2952218086352d858bc28eb6cbe7f65c62ca9b6c4bc5fe1d17875e099b556a3eecfea25178c59b92298b32f3e52191fb4cfb500e96d992c726f9c6a1d91daace0475df8321a5fefe5f799b2c18baf44880a7c002340d23e0556fdeb9fff0c845f035f936728525f77fb3fbabeadfce3a2a2de6180a5940d902e955f081ed3ebe6ae14697e94f028cf5a410cd64cc75a20a79af43f98066f495f1379d5f49fe40c31f6f6c93be411d3af965f983171a5f3cb145dfd424d3e0cb60f7cbc92cd5c417a5adbce371dff06ea5afcd2b548b9b9c2c65a3298003d53fe14cafc140a208495876c191b8bb277c6175ce8c04ffa7c6f58e98e198449c60276cadfa579bdac108fe4adae4487345e220055a612da046ece40116de09ccd52944b74f167c5a6ad586d1fe260812a6fa30181322707ec66fc9dbeebed20226722dd6586c6ee6bb0c5e609e6830f94b75220cd8115b33871622c0ffbce57301edc4884cfcb356543ced8206468c4e2e408db43b8504b3a8f3da764b2edd9698fe30c5f928a22cb4a69c25c8957074a8609bd800210f8004ce0e3071f7463092743e08c2623252d2bee62edbc24956abdc4df01c8a7e8438df887d4c62aa2704d8436284c79b5b01710ef62f00ad92e62f83bc16be8e7e98f22a4a0f468447073c7b8716f1447211cd55972081224e6484a60a62e97517b59f14e6bd5939082c07b1d96cadb94950c016a4909598373822ae5d04062a831ddf0b04b595560c9a45451cbae9cf936509de9ebb98fa8df20b29a869282deb32938413be465318a30fc52534e425feff8e697f0c361056bee1805d0e1aca57fe8610211c8641cbeaf83449bb60e48b69de79aeccbfed93c4de452091ef8c1a505519c31b62b8a14da1ff1868ce377e0c", "", 0, -997941885, 1537743641, "e574656e29147db3b8ae2e3798fe6b740ffdbd6f62413e3f4501f74ce474ce10"], + ["9fe0827e017212216323d5e4306fc7e041b1434355df9500a6c1fa9023b93f98bd16b350840100000000200195a6042008b90000000000025253044b7a0100000000096a0065526551635152fbc6250400000000026365c736170200000000026363000000000100000000000000001e5c3c0500000000f35de8d216f549f8dea13e5064869f0df0623dcab56df84c88f45f0bebc9186371ed50424d5c58c90e6e547aae11b049fe3ff135c166c055d4ad5361828c27f4018d2c174bc33a6414f570b107b5dfce46e61d31160d811e42c3bdfa9372326f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007fbcd2b4a1492458ec8f0acc471965a19269a9c8965a578e7709e4516fd571e713ed115e9e405ea4e0d5c2d93564f976c0f81db808c764d59ad1edbc9b1f76c2cb81cb5f3c599b0a1983d7c6009296a465be948308e6569af3bb5d53903ee5ad2bc6012455f2698a811513b8558ccd9b43de4773d49a76da1c9d76dcecf95783030227a837079731d1172664c9991b35dbc27590b6d98219c0d95203e2a11e5558022d49f31a6c47ceca674fe57b646ce1aa25bc559152436f10d656a8adfb6892bf0a05d029085a0d34b59531c2b1ceae3dbc4bd0607af66162f14466628764b79033a4a3fcf94d733e5ae5448d2a00d478686fcfe1d424e9372e1c0af41bf663567702122286151000daacdf602d196e324afc0457742732a79d935e5339abd8efec290314d823e0d2bbf37792612d936721d384f0003e66d8531fabbeb407772802a4d90313c2483992ab4ee55f50dc6f3ca677605e416c6fdf780e88853800bb32af3f4a0211f076a3af54fcbeb836303dc6c7a21c7f3f42da9d64f38d202b735f2221b667021e835e8653b85e7bc4e5a5c9890356f9aea10a9a488e9f266cdce4adbc77da42901d05e876bae826370dba00fe69400158507e800995d6e379b90b12582e5af744d4d01029d083c88d783ff2b44f88487132b74bf11b537468f578d0fbc7499517f449c9177d41d3a0e1f2bcd64632d4ba58c4f0342c36a176e310a3ad42a6bcc8adae5fa443c4f17e7c8520b808ec33bd919a6b1b4ba6cd2b73b2580ddd7ce7ae74a48184566866494f2044649b85e8cf82c63596f51401eedfd902e22ad73a4ec522f66e808890d6f00fbe62689f4ae6fe658ac0a3ebd5d274fe1cf906cc96c20e723d857f1dbfa35a8a09c008b011930af360baffffaaccdc4f1d1fb3a87f56ab7f496901f5e0decaa068664dfc7383b294b96740df6cfa5070fb16a150515d59c0b0f4d805f69a5ac5e7c65a19ce9f5db5dd779fd343431c5ae61f01229ee6e5747522b35e9ac2a330b73ad0ba30ed22e8acca316aa0f5a5b2b865616020cd91a031f4376cb6ba887ccc4dc47c01ed11842c707f1f3c4997c11c03c42c89e625e146499722034c93a489914298475e26b26f1cbe418bc809a531fa6d613354ebc2b35123a98c36042d10cad457729b88503d51738d5724c98301a80f1f7ac6598194a91dc6fc87d52841afbe524a7a0dfd33f2164744ae847dc05db51d87fb6c51085181210c0cc6dc98e6af4bd22387763f139f04ca10dc37d007a31a0974b77830d4c6b923c493c4bb17177ecc17f137bde60f19c2c7f0d220f85f1c8aa14707e9a8872e9e0dc8d5df0f266b7ed3d1a250f4fb48d242571b7467cd37acc0849de39beace1d85daefd1817444c38d903c2559a9ef8c529ebaf949f3ddc2d562fdd5d5c93985d75096139c9c1ac732b45dfc213bb99a3cbf543ec02456d1d5e27bba2531ae9ca72c35b5c3114a1bc6410aebdf2176827099a37246d1b206db5064d8efc5334ba845f570e1e29565c04ae01f572cc6cc6d7b48d89e9fd60d58691d6c46f6ce6a4b1565f8bc65a5994f82c44c538bdd09eb78518b69caa5f468bf7bfea939e5030941896cd59888c0c20826eefef19c08a32419ef56a9a94bc223f54444113f5cc7bb17b1cc4b1f3e9430d65c66b4dbd9b57be9eceddf182c17dfb3c39554951c8747a87b8ba315a858d6d85e5ec0298cb53c52e83ac89b8750c86863650a9e506bb8c11f49ea38acedc16d41cfbdef9d0853492f3de8e9634e711f3ea36e292d7ed78f91eb696e162012701569a627d138a232a43ed7e3b0884c8d67baee527b4228aa3daa80db26bc543391468e3dc0820eee85bae5a756123420572e773b27c6cf261e4a7424e71a7e9a803f3143b2503a7f7fdb6f96c650a55a644bdaa4820a5bcee330b2055887aa5923424935812cd8a77f3f6f0d1cfc052d524a3e2764958f242d840610f69b0363cb458be8962f20391a63aa460aa9ff75e81e066127b18cfe433d9f93109f9bd020e70ff5bcb22187a02fa4eb2734339ffecd83e6ce93acbd5c6ac35f48f1247dcbf0ca40251f23702445041aae15ed662d6bbb3b6431229f2bc869fcef4471b2eb7da8b6e80abdcb83ae8e9ba4c894a605e31ea72586270734beb28f1d7ab1f1c32e6a796f1feb1f9d7278ef1f7c54835beb3e93766bafad5efc4c398dec887c0978376a38e06fa94649408a1eb1f758025efe04b2f3c6a7b65975fa7a226cdb8c41db3b7116341961eb69c039820dd981b76d8e7af4b2f0a08c60f3febfe7e9e91e15aab095ec683e6b90653acbe5322fac44c92b2c61c29e6631dcf894c6d4c0f127fa2ef0ab2fcf4f5e49812276ac7762304e9b24f0684a89cacf35fe2e2add4f2e6914966d12ed6f4af438ba53861eb29519835ab716f6ce5cb6ba0706", "515365526552", 0, 669396948, 0, "8c8e3725f70a8b5d708be4e5925075393477ab8da53ac4bb8d934772b5d39ca9"], + ["030000807082c403045c512994979939d10fd1dc9c5638f679fce1b1cf850d2ae58c50d87b01f0df470100000005006552635143f754c7d897c0d445da2d941ec13bb95c1b11bceabaee998e6ba83bd5c21255d0bfb5c2000000000752536565006a6a46404bb420c22f740117d6ba5d9d75b79c6d0718c40fe8b5e791adf03f1f5136dcfe3f8e010000000551ac6a00534e9692b127d3b856c3200ec4a7254ffd0503c6c4436ddb8f258577a9ca421b60caba302f020000000951526a655253636563ffffffff042739150300000000026553e0e48f050000000000106ae40500000000046353636558e79b0400000000046a6aac538de2304c0000000003e98e8f010000000000000000000000009018b8834686cb95b390767e0dbfd6cb5de002682979d77d84a91afa0ed42c57a4dc755bd03e9d58eb555a5dcc83f0a028479c14d883f7d59a3900a4c3d115925d0dbab003be9aae530e3c08e4492fe42d3b2c9c2508d3ffc4fca46cf58c39f90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000069486805d24cb7029a35db80b8207d4702a1351ef743ab88b88356506c097ee7f606a6155c38e395c7d0a9f0895763942509fe5b7c9a8901e4349a18bdf7eab2d75284d463b2e6412d0097e18fe0d81724529fda8db119426bf57cf6a8cf02c46b8e4c208bbb45eb4bc772495a8953679b6abbdd445887bc9793967537ed7979022444cd18f72e3715c08a2b238b26d653cf71e7ff3cc69517138d72d3fe96223503007116d6b35ee769b94f6103f5a956eac926b67e904e3d6cc915e254c97e395b0b0707b26831a742766749aff231a08dd8515828a20fb3bba0e6877b93da6dfbb685b79fc166e4d2a9fc856480e960c8bb9027865bc270bfb883d74c60e07da3c6021e5c2b43264e2d3e0396fcf7b2f3beb6a4983edf3c33ecf422802ccabef50e5302097d0da5f52513d5f6185c8169d45d1d96730107005dd9fa7d925fc6a29dd58a03286f683fc889ac696114046582f6dff5237ab82b1907032a0ddbc1dbacc54831020ec7a021588d33e5fc9541f913a3f3e19f4f70b03d6658ceb07358cecb9f720c0220eb11bd4b082780837bc210d1d4518ba3267d16ecdc7cd11781ba913341a4bcfa3b8389711c21e2a3e00e98799b7426dcaf8627dfc4b6fef131f2d69906ab75584e574018d4ce2e94ecac58453d012f55fb2466d0dfcf486b356a1cd44784383b559b9fecc26b809d264f83f5e8ffe86521cb76866e802d675a85b538f8936412c9c6731a7367c85d867178b7effb4da71cf284c0a9d0fbee6457fa2cc4e9d91844231e819f21887c66f1f40016e337a116623bd7583e2492009daa24c4439c343a6f53e393538cd6668a4a7ccc02c83eb7baadcf62fa2a3dcc52ad2581682125f960eb99a3dd2b3d498f66f2d6b35f66ec00265632f23d802985c31887884dbe65db2bf5f09017eb36206c7f2b762661c205e7f65616c9019b9a335908452e24599cc351d695b07ff3580edc8104294caab068cb2d2ac5cf995d4eaecb394ba194ab0a1b19f19d1475539f9318447036c604bd6ab1a0cc7f6ccde93e084ee4fc6fba5c72f5cfa3ec729ea0a71bd3981124a4123d8764ab1c44b51a9550e04fad17efc8c511ed0fae1b1dbce1b0008c5d634adc245e5e30bec00c47e4bbe726d672b34c25b1bb5c2d060c7951cbe9d553da6261f45557d3d508564d9cd5432dd25bf735a2ee0e86c2bcf4d1cea6fc50e3c5d9695f1fd8dca400760e3b650460f34bc4ac4f14061f0d6c35f5f6a7cb3718399c7e89c2fbe03e64b75b4808fc0b7fe7bf3d2063dee8ca73cb1374265d65d7ab51700a79937fb07016fd93ec747d281044738995205ea36ff7fddf0e0768d0f507aeaa4ca560d794bb6775553d8d22897e5b33165ca3a92baafe23f017d39179f9363d1327576a7dca0f6682f19c6309023a0a63dad297c3fcdae485392ac293b0dc27a37d3d75650d28723c236e2534c67326a2b75acff8abb55bdce2145c7c5e5707f63a1aafd8e65c6f73724256ed628b86c5f3ccea3d45255cd7a76835b4a01206ecfcab793c136954041b85fdd9321228b865770fa614e193768e8e0c66738b7f8c98ff077d35496d9c5dffafe6ecfc144846c5a489f2d0722ce402015b04de2e532e2d6e7621a0fe8c1cedcbf70c391ee71c9c1c2017d371617a20a897f4f83146c2a5ec173346b18f9ccfff7a4677d0b5b0cd728937bb5558a634e8acbf66f7f6535038603126079032ccfb8494dfa7202338b88b5fc3e7d5954fa6c1586e554093b0ffa37e84e24c875cac01da5557972d83270a77d1fb70e61b4b304864f8916d9405be5c13e0e6446fb1d0692fbac715a9886467608b4d9b5a57b63910faea965a00d3c509e06a163e25c47e030310134d346edcaf488820ab7ce2cc91e1b98fda3f8b19cab5c0a1e35f73d38de6ba0da44c707a2baa246d649ef207da93c9f8bf8a2177afb03df050ad07dc14a2478c43d1a32f15fd0343e0110f6ef910985e86b55461887b7b38991d75b05e7007e190672a3ebfb5a3ad00eeee125d2601248e1193d41cbe9192358548bda36e1d34e3ed937fa72f9946abf1518b185d62798e3fdf0be6415a59c21aea5f95653adc36494f1c40b79f4f76777ab6d1ac853c1f60ea48e81f08fcc865f4a7af5dda24ce1422ed2c5473b14b75e5fdaf7925487a514923432fc5816bb57caf32f33e8902187d884c467e1f6f5a975452b01a642eb08bc9b917fca116b3b27549648a99f490673336f88fb3afd7eca3dea98814b1a8187da2a8d8d66396f56bb520f6a222953be35b38b9f77d2e5c000000000000000086623e0100000000bc775afe1a3d81345a34efcb80070910e625512bc654fa9dc79959a0cfb2d2948ab57ff848c066a3399982da25ea15879816e802fbb3cf1fe8c15891c48358db03a944dd855656eb4414ae984a88f6011440f8adafa4911c480fb8ef445b25b4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006d305f7c92b89fe49a8145b4fe1d49a7bf6a7c6799f5d6153485ff8d3b511211a960919ae13fcdf214dd572fc54c4d0e37146f4a141b34ca4730321d9097a798fdcc2f394f03a3287d2a521025f0e7ee2b129f2cdb20c4682407cc5a4315392569cf6b1adbfdc4e14fe89a886a1098c849b23ea4ce4e1e3dc0c0ed8259bf93ee020a994ec3c64790a298f7a3c3d62079dabf0588caed98e413b63ba21263fd1c6b032a859f29d2960af8d65db52daf33937a6235a72ee71e234dfffebdd1c81ef3f40b00246664ddbf4519067eb80601a63826decffed533350dcacc940e0b966eedcd8b552bb2a127af7c2115bd7cbc3e8cd53ec87c465ec1a1620f11e6de606ce7820330120a33c036a42edbac0628ba625d36734733e654bba5f02cfefa0c85658ab2030f7795cd7cff5009a134de873c6cfe7ecda3a14340723c7f56985e2edeb5195702276dfd55b238f578392581b7cdb1a22724d352e7badcc313d94bc7dba08e58bd021576d644471d1e7cb914fae84f5ff890bcc5fca12fb55b83922ee2a36c088c6b02110dd79bc3c439ddc1483e676e51020c0ecec783489c0ac3da6902239c830807bddf08b65cf379382c04450c33c33e57c28edd1c7c8431d4e431c4c1dffc4095992638c41ee00b42d81579973abfa3f403a353ba084783ca9687a0e6ef55d39703ffff307383afc4d0e9634c491323c194d98f5c221128faa44645e9194462f7d05ab538a4880c5f0784c2eb9e1a87c59ba5ab4a2d6b67691805d08bb5f41cc6fe14a0356b37b46dd36459ae8a5b72b45ad319befbac2f8115162380d85093041b75e17e971259161cca743415aa17348dba2bb55e5a5d346127f43fff53b96afaeeca61e31761b8e488db49ef3033212baa05a04720a34fbc3ddfea52f7f5b1378813d48506768652b9af9cdf80021a9c9d1591d6c482478dde81631b8040124362b5f0e5dc7a202b681777724cf3357f73f6499dcef6867f97aa93b68020ae7a1f8d0655c8035e9ef3a70983321506f5682392a13a72401137895badfe5eb2297246866fc637dc2f47f91f2aa5addf62db00a421232ed777f6e22f671c2724480c8ead9283b9055fc96a20a24d33d950b3de4b3b670b20b7334d357148b8566f768b5fc5d769a8553f50f91cde941020cb70c915dfbe1b43843cc07448071200057a6ef27a518da37263318ea890983205901fbeda84aa3840922e453d562f4ca5af15460632fda14b823631f9f8ebad892a1b13b82d914a7f6a7eb7a115916fab59d325303bd905679af868755852ab3eafb3fd98b63db5ed296d1b403c5817a5aa6a046fa13230d3993f958ae4b5b8dbb5640041a0b63c33f73cf340f90dfae9ef4c8fe98c94e807df3a7ed5762dfdd1d6d4771756d45875b0bd0d579b95b6ceccf2c3a7ac4c09f28fd50de940717d8a8cc26446206e01da21fd88030b697743fa940f0276dbd67629cb390472a24abc5c762e67da8e820bdfe34190c1dfc89a6ace3e2461a80cb82e7d007f3f7a8d669f244c49a10653e38a7e4d71529d87c90cd17e1adfe22307f96a822e5e888e8f19470fead01833969704cc4ca78ec4542fcfb5cecc23d8bdee5cc2cbdebafa02084e00eb2435949145623f15fab6af10ff45f61b64cfde55753bd6632d6ff76bf16ecc8e5b674bb5301e4c39eb3bd5382fb76a81965b1375704cadfe0d0e235a772a2207356d6ccab388fecce64f0f0d640f78feed9bf27b43abb0de81df472cf7363b610d1bad6d709a3b13afa81c6958c2cd9e607cd3dbfdf215c9e50ed5be359882b55416284dddfe6a03e09611758be0cd6ff40a8c07cc30c00cd77b1b763cf5962d7dbb1ef8efb2394daecdbed8892896f3cc92f687a22eef926012ee82289992e6e8a09881be996cbf9dcb58b539fe102511490accbd3af3aa4f2287af6ccb79b190f4b41abf789c4373de05196c142c069121cddf149921f78f69df197916949cdd80ae0ec72cfe3983b8d04df720f93e8bfde68506e6111ddcac878e85f71180b3870311e0a15fbc9f83617c7b2d63dac0a8b7af5e6d0c32e2a85952f008bf913e9f82875e55e469be684e9e236fc6ce6677f6e46016687dc35b72b9066cd1e62b08080783efc72e9e1d62c2cef6dca558f4d0eb913a6ff1a0037e44566d35d4edfb0ea5d0feb69b4769126c42d0868a171e382cdb293a41ac551feb6aa6679ab5b8520c513e37c66081d250a1b53fb189df3b5d89cc4441259b7a51cde4198efc920be262158ad4d889c3dbb831572b66394dbd2a52e49af36e48eb0000000000000000b5d3560000000000a8e7b08e2ef272eb86dc279468fa40f90c6e329f776cf0b9f53c6b96c4a79fdd82c3e7fb9da0985abecb9badf49a8f2cb90f678e23019f6bb350757b19b4750fa7a74dfe17465c407676a451b811873956aa0485550c54db61aa4f0e85bdf03900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d79d0871bbc63ce18f46bb499ae9fcd8a5842b8b08c6b7486f90b636f6086b2961e564dcdc2fa097476aac1a235017ab21fdf14a4311aa1f54fa433feb93386862f92fce04e6f88d89fde9ffd8e930e0f6b494baebd69beb4c6beabb4a2aac213cfebdd0344d9cc8f717b6b8f1fd7ce93a26918cc3045e86e49f035eb1174a1202135d9c7b62e1bcea6edf8557a3751777869eb08d8550b0783c1be421458198640304a5c2fc930782939666203a81ea9d2c28aa50e1494265779d3091cb0c7262fb0b076c07cf4eff622d2ba1211fb1bbae167fc7e90df280d5e75f53c36b2066ea383f8419fb322a1408d1303b7af55ec8d25dcf2c9bc6665d971543343f4ed856d8021133dfa76b9bf1a00414b2f69b8e7c3cf246d0425ad645045311f3c8b0c96351021df17565367e70b5a2d2dab6dde3635dccc2883ecde8f326a0948f1239c44c190301bfa0500ebc9666831ca0a7379444f86ae83d791d67deb0f62c07ad08c22b5f020625ef2b3e76295896d024d85dfb6f7fafa1c8c4fe2d111ac6a12497bc1bd403031070fc623fccba965de25a6538275b003da853f3ebb7e80fbe1738712362ab427f5f27a3f4214508ecfc6171a6d480d8576e94c7e915555c1f650565c32ce1b26a1180442e83acf9b0cf697a319b0dc4766f0efba6836393cabf676ece0f4d9e8629da4152327a9d21cb4d38715d02da39cc392143301c306fba807e00e59ceba036d818d5ccc51dee1fb3531c356778857c6a168d22262069678e2255afe50a21b45b154e162f3f2c503dfef6fd89f50c961e9ac0f910a12406f4275da71b5a44a6c843ef1dba10cd7bb1ab72c63afc03202244e48c40c67c72939f4bb2a41fac0438e4b10476fe37151f34b1bb1204bd0e6152691f3d1e1a5d32ac84dd91a7abd96209cf093276f110a8a4dc8a83a7352d98f5de496b5a1b8f3f70dbaa072ce5ff9a9fd7400b2313cf6150b2adc92dcd379d4da0b6ae4709d80e159735c65bb3b2b9f3c6be3c7973975eb90aacadce8417796da9f6e856530523aaedd7aba2bcbc5182f5663f0875b405790cbd1160a5f09065d70f710617e4fd1cbae040a26923ff6e9361cb268735cc95b71f5c56e28d85d08725d166f1686a21a568e18e3fd06c26005a9dd748fa57cbf23def26b1f20733545bac4f9031d52f377d3b959eb5229af4efcbfd194a8cc9a373e29c3da5c5cf8a1f1b8eaf270089efe4f76c3ee38b8f012dc793ac8c16c711fa3d185d8335405b6dd433822e220ea31ebd0e8aab3f07e08927c36037acf398536e4f39bc2e4ce5163b6d095beb011582d763a3b40fb2f73d9a1f1ee67aaa9461aab2abe6a7a0962b33af0f6c8ca9378aae5a730c25bec78e956034d7dff6cf5f04664482a33280252cf5385eefd93b71e032bbb6cf8a859b02f51d2636e0d175cd166b1c8542b64e51cc377074e167bc4a802eeef4cabfcf67c0d9beb898cc571b268ff1f91c6b157d0ae9aa5210a9ed1485cc70e826ce70468b180c3d9799a69465e290bce34996bf3bee52c0adc7154838d79f8c934fdc8d39f6889f9662cdbdb0303c59df7d02d9d99f3a253c531fb88596cc6f6c38c00ba8b165fdf76660839902c476befebd3b010afdd514dfa8b7fb9a9720f1635d1201b338da0c69f7b0e49a849094e5fc37678df26534442cd2f4186b6dd889321474db197c35ce8f5776553309c36ccc417f4b232021a1ff111190fb56e008cdbec54a2eb12c27aa2c4933db1831e5d2973067ae5469413737503d75ff0e36f360084c7d06dd4314fc2768cb26d5280101c5969155295fe7b6b3ff0c6fffceb820608d6cb3025dc6656b3c7bb966dbd62ff6a243a3b4fe85924bc101b9c0acdd816e4074b964c675d488546f088d2091a13736630feed0f70360fd3ac79fdb6f503deb4f10c4883f40109659d1047d6e30c56c1475b160993d5d4889443bceef8d1f6afd78e83acc15997e63dded518e6bca2662661bd0503b4270a7e61179af24d57f32e04c384ceea71bb06f1d973718aa2899d4ac45d3dd850c16e8ec506920c0c66745f55fc440c1f85e036bffd1a472d62877b0088b289ac17a4ebb1e0e010eb80b693351af063a1d2f58c316e22ad5d11b869f0d6d58c01318da4f70c3c97005c20ca55fd4d3a56baeb57b914325dcbf23071efcde07ca8d0ed5cc63376b36944618f666fb88182cac69108a4fb1f37266b186be48a3e110970b399b2cc5873f458afee6576102fcbecd572f151eb0af3432f7ae88b42a2ce17e986367320be07bfae04689811ae239687ea6d55f8a372f501d5be64a2d28920675f4619e446edef81eb3a1d8237f0be53c0463dd3d72894116886402bf04a8de76cc0de34529da545c69f0eb1299b856903f7b514a6ec51bd336db501aede8cd050139f1af8763037ea190267c870a", "6a6a52536a0000", 1, -339911593, 1537743641, "ebd9254aab96b841160505ce1fda7bdf3044f2e0edddd8c432aa597b90126334"], + ["97d85d4a0100441273bd1c1bdf1be61cf471bd98c60fc48e3e03f7091c01e6111e793fc29d0200000004000052006f3e86ef04b0605e010000000000b6d82a000000000004ac52ac656b7882000000000003ac526368a6da040000000004006a535300000000010000000000000000f8bf7a01000000008ee92a3c9e29f15c4d794ac5fe12384326585ca2419975bf36d27532933cd35b0e5a5c08b207cf07fb2e5262527b4ba5e32e3b2996ad228277f97fded41d237363b5d4518c37236215cde6ad6c6deb3ea64b6ed9f51cb487dc77921a8d23376d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004bcd2e926c8295e2d97b880b1263a2dc401f2951bdd177c028ff8a89cfff4dc068b4e28d0bc9dd11ff564ef0d474f8353771ecb861b107aeb6a379f0a9ef4ea3db98af7baab966abaf9b322a4c5cfe6c7d271d9fb984487f03b304acf4e9df1eb4d57d995f3dc34e19ad18dff8d454ddca66c33c44f82b812ac52eb4301ceef50317a09f9b6ecf6f3c9440ddb7a796a7db975de762fb2d36b799b4d2b293083e630208d47dc1bfb1522a5696b885993ac4a5421e0227450e7b67f7b4c4eec5ee63700b04dfbf48eb8de3d9c2e3e9b4cab2a6f4e6f907b75d77e51fee5f7d8ea476ec392ecf045bd5b3d9c0cea8e1ef385105a9bab7b758c1529135b9bd57ce33e6f06c0200e660b561f31d0f096a21984693aa467f24fc3523bf054f5e40c778b475f312022024f0ba94e31ad9c9070f62537ca1d0a5081712f5218020d11e0b2ff777fdc9030f71a11897ca28970f352c468b6a94cea8b5c4750adcb91f01730903e1abe3110213af951f56d3ab9fb39cce26380596e7640153854abd89a351ce7a7ab8ee5d360223cd4a535623cbab39d629bfc191d2da1da22fd9f5f41a49397477bc1ac921547a38fe23b34bfe19bce51e10aaabcc8815b79546ae0042e62aaff8236d79e10145614624a25f62e168f6b474ddb42b931d267e6a539df6e681e088316941cb052559187a136ac388772462694b56f082ce54ed18e1c5b8b7d4b74054d94cbe1432450742b3b39c4ec363760493fca405937ff78443abc4182fb0a96775d763d1f3e704b53a296092636f72ae726354f6e258bcf2765626ce260e0b83c2a0b5aec66fe9cdcbf5fb716263919265ab5f66f2d4c6d7060cd3cd2148d49308ee84593cf34288cb086c7b2f1c78a8e0e12e85f09bb04a49a5f525d52ed8decece41e4eac584194d2e057150d0df2e20f831d9f762ff14bc2fd7fa51b1f4974ab45fa53ffdf5679cef6dca2a223cf5b0d2e83d50dfe5fd15dfaad4705d11b0d25b0fb90eb0506e882a291588ae88ba35d150c38818c93b47f04e6b4f2d9b26c39c24e0b6c6cc2d206f544853185087aa5e307356f5855cde956aeabeaa4828d9a121e5bc57fc89f472c6fd59fca7dda820ed9e43b2aa2110ac494e155425b42d1e5bc6f4ee5db0f2e7d05ac8b8923cd22fb6f33121a2ccf2ff3be2212a4cfb1b1b3cfb7fa063737c5547d8c1f728027f7855e7618700f7afc622b7a44878341a6f117b7ce70b874d57c1b490addf4b85d1174c539a828f30d524b11162585171330194185f3bf3dc71c1df76ed69e339b21124b4320d489f52c5559743446971624be220aaf37e54f89cc793340c2182ce937ec259b51582061661e333aa1d820f44080248018524f391b6ed2246554e001feeef4a660690876d38b3776d6ffbe5b7ceefa90fd3f35e5d40c4576f562499430bd92e3890bb2ee79469ea7e5b4bf5dc3961f0d5c89a1acc8689d02dac22bdc3179bdb65b2d94d033af156aec3d6176c08325f7bb3ea1422f0c5765caf27d650bd0562a3bc639877e90adc64378855f132341faa5b2eeb4da7c3ef60ac0ecd226093c927a9e582700b121bbc8c5887c5377fc3997dc21b9c4910e21f87c3ba460e3d0c50ff4f39b2ce6ee620762e310e498907a45263da6a89dee8e8315bd1591bde6e6d173b678993452a2f128617baa0a1d9d0d7c143305deac0208546de912b6886074aa134ba05ae7eabf857686b57bb953d70b49753432ccb5de423d6b0eaf9535e59f38ddcda86a2604517e4748cebaf45dced3664a56c598c5d093838ffa584e3bd1d8b9680da22be74124067865675c1924eadfba70eceafcf0b1a9665bb70ab7eb248f23cb10880792b789de836c4b56ec3e4738662ba2f631b530997e62a3b54b6de13818e9eb85121adee6ff19a1785e293b2d80516e56c5626c65635013b61168f305f882a3c0c258db4e087325183b0cbb261da7d1bf1380d5f8f54b3e0514d7bca2084d6f64d15548c1a0a8a90cc7f8cadb1679109d4a02b819f649e1190a7f964dbf244e2d7d2c020a5db61af4bd647d98fbd8de4f68fb81f064a0b262c60c95c1c66f092fb2e6d61cca9a0ffe4e1600fe61c0c777d7e47aed015c5e97e5d69f4d5d462e7b36a95231a4d97f969266d95c711c8fedf92de58ff42974cb3590e2d86116d7cde07496f841439dc492847648e179d5c9e09271d7ada264c39a2b8b126ac8ccc0967f35a828c65eefd09482835c902edaa2b4e53e8308843be0c162ca6deb75d2ad6ba51b95c1700c7fc3e7252d1fe0ef88f8960213ff44a467f7191afdfb52f177be43d88cfe3380a56f252913bfd6beed1c3834a253252af5f8b4a3cc316fd8b50763930fd3aee31b62f0ccb8807dd7545896c454c746014913b15a99f21892ccb9abf14fd7ae6010cf988ec26476106b08234f7aa00", "53", 0, -1310719221, 0, "d790f8e8c76110e7e42323661edbc25320cee3e261e04ae52c746d8d4394a885"], + ["afdd515a04063af696e62fec0e3f68f3301ae16db760f618509a1b569d1b1349d5880a4a5201000000085100ac6351006a63a36296d14722e2b728f5c3efd2eea6bb98f453c1cf4faa9a2b21eb7ef84f083ca6cde9680200000006530052ac5352ffffffff8146f816172132a12f67c75d1057c6a2028b2c77f28a7e6186fdfae792ea37310100000008536a51ac53515153fffffffffb04fb88ede3a6625dbffa7b0dfb390368c20eefd31a9e9390ed570714b78fd2020000000963ac655263acacac6aae9c35d703b94eb700000000000765525351ac6551dc2fe2020000000003006a6ab2c2320000000000056a52656a52d671614c00", "51635165526a53", 0, 2098914000, 1537743641, "c0fec2a2802214c8025393796f124a43a77e41e0aec5360903d63fbd11d6538f"], + ["030000807082c40303637e6f532d205d8b5917a2cf206ce2450f348f1704a3d3cbc0718a587b859f7d0100000007ac00ac00ac6552ffffffff832911418cd8648da462b85c0852b2d6fd3a6a584948510e03521a049ea6bf050100000000fffffffffa6bf0106e14f43f3f3cdfb3d26299c2886c9c0170b4227fe5b62603e298d5850000000000ffffffff01afb27901000000000100bc1d0e470000000003000000000000000058bddd01000000006ec96e76d25bd75a6791dde729a95a977d1dd76e06caeeb3df2c43994e185b8e7569ad405934adcc47df2b66ffdd93b33b6b77bc2c667ef345c0733a32f421f2a99aceae1f62a9f1653e675147f7ff7a1c1847d48bccbc99d7fc3dd5fb11bfb300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000987a64dd0520975584bf7e496a38586b82bfcdd6db056394564ecf2f9eb07fa4520fb7c2e6febfc13cf3ec086fa27edac8179942feceb2a9f55a28d407ec3e128f550b71a950157f700646615bccbfb49691d355673dd632d0bb0ffc9a54e27deafe6e666e97501c0f138d551180f9dbe1450d4ea9a609841167f9ea48ec873803021a2936c99732ade91a68d7f5b3ce0e83232c82135751ad2325e5a8cc135f85020ededc7219693fb2e35cdc7dd91ebf4b50bb160b0ce4b9bcfe923f0b62a3a9a50b04a451ce7a4f6652002d34e1668f2c545bf8c80f5b166fdcf91f2e8eef4b4d6f5c1035e3edd990aa197117132357bb52fb6edaeb4a13e409913c206da63359db0312c2fe336f4b09bf08cc2cabae98b8ae288aca80b80b1e04859280a00eda5f0e021669085502a19f00b63d2d56d51aca25c5525b1f98379181dcc222121226cc85021fb56e2ccd53c23e78e7bb89c8f7962652f2e1534ba529b1da9e7934efc6c9580213ce8391480373db8aa22ca476bad9c7af290980230423c18e7d1e626666a14c0221b4c3092bdc3aef2a78d622b1ea3d37ebb82281144709f346fde31ed80ce7a6b275a54e6f69efbb3b107895cfc7042880f99e038cdb8ebb014071ee3de29fe1cdb13234680ecf8bf7935af6809cd31d15b9cb3606f1a4804be78cae8035aaebec7fe016b7b3d8ca06cda10ebde0c6d31ba76016c64f5f93e4daa446f3271cc6117cd0178dea0db28d69ea0b4dbda831ab52ce43d11d560dd48c1fba5e49718168bb6489b031cbdcfbe90cf929b4e452eb9e79ad57877246fa8e4bfa8f175840b42e838dda736b9ae67285df7055bd4e425037207e50647ba1513ff7dc9943ea005cff06e52ebd87fb55e3c7c933014330b6e4907abb78b67a5f0715458d980ba4517049688f94b7a20636ff71c7613b9c6ea295c7d441a60127d5bc558402b8112e328edb37ab4f1569f597cade68118a77c978a338f806b82a8a3ab5b124373382cf4bcb5eee11398137a9e88fbce57e47e5da9013177876f2982b66ea80bb6685555bc6a647714b746794b4f4bd38e17595ea770370282ba1323190686519fc37c6c89ebd15f96eb1fcdffc0b3a8103072a7a6fb58ae17b5136fd071cee7dd88c80ac6e2a46c9619610a1a1581a0bac06091aba9a7c6cc0193d3b02d30d2de08fa8cae036381c7679385c795b97cdacc16f3885efe064c2d53ae4d6f71a017f5c8cecad91d6e508f3751f01b686141f12a6ec6ebc774c4b84d49516b18d1c7ab4b9438ec3559be06012b69554aaddbf1b08cfa4f5415722821d0544cc39851296e70032b355e3b30b8dc6ed4ed60771b0896d90eeaf4141ded063a574480b98ad1ede6446c1eb793a308bd0b2ac69405507a00bfe19b619708eb9752a9d63576a40317930d4bc9fc4afeda91cd1a04539712e0641667930ed5a4ffc0f4639f7f6af82b687ba8c7ab1352791af4ed618ce4326b0f77ef73ecc94384a76a79d81ca3bba5001053088fd2a772c2199c2e5b94b8e0b0afdc08426b1585c638744f66a2d04a74a9726bcea1b18501ee29b6fa464b3acf749cdd99eb1d7610b9bcdc04106878734aa2a083022cf14e40897401acf634151e78e7a069fb3a8d8c0edf94c4ad42ba88aa46c1b6cdf41bf94a2b4992999cdf092bd69f1fefd1b7f2277635ba3b2b7627219606a54782b50eeea67ce55afdd7e28d3ea2da6086e68a4a8052c462ca1bfb6b34f74773b79a81e305a820bcea2f06ae8148b8b37d8e1f7f09fb46a324fac15ae0c92b282a2ea1997c96c7a4d7546e8759f99bdd52085a729d5d2b90dd4c5529b14d3826b9656235950b20a5d8aa7bfc9da8a31f383c60eb465391878399c77195b0e0f6071ff48fcb85c3d07caabe6d15138db5c6d55ab213b5973bdb4ae259fffa710ed5dc5c02fe6d07aafaccf62d8dc73264b601ac4f71ca638d77b5ac494542dc3d6f1d3b7354c1609a6e7aad10d3508f50c81dee0ea7cd3c8c94d36d592b839a7e202f1fd6eabf50cf09c868f572f321b2a59eb559d32c053da7fc4137aa4267dbb359e11e4dfa1d8dfab5264e2fcbd0c8f3f31d732e07ac7767454c0f1db38d99b77816949545e95b9bf33ad2eaad24b3e0bd3ebd6fbe099987db34923839fa820c1c9ce24ce988762d86a4467e495ef95905ea6c67f2d671a59060979e0f54c7d3d916fa57ed7816f30d583ec7ae439974c22f318294763a881b391192e78a8cfadb8bb6a428cc403c480103b42c195e2e060a6426f0994b8537631e592f5db9789a12e7843f800000000000000000b2861050000000086d6e08526adad2954e3686b271f5f7927b37fc5c8110204de0cca64a12b9be910f776f460b165490732be0cdaaef819bc1951ca53d71538ac33980fc5ec85eeba541610b8007686c7e73da5af5505a6fee5d995b360724883daca27b412cf3b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004726a2e0bffd5237bfc0b4e433f3ccfd23192415437487def77e3e09d86247b96ed35a11bf582791e6818e95d58d4e4972d30e969e8b86168b765af47ac5117124e05b03e42a142e3a433c156fedb6e6dd96415d222fb8a0f0536bb6e882fa4f2c2447dacef98489552e8c4f351a0fc2750f5d1c6d3b83a8dfc50ed85c6b6a210208a39dc1dae03f97d474a19b6abaccb18991a0f669ccdbf5823073ebae389b95031dd258a01c67d1f10bfda2b38c9c8afc27bd9f3e3eee3b29da48ddcdac1377940b08957e6ac2f12d0bb0efca3224237bedee5379da396a008432f878ae1db85ce70a371a4185450f0f9f975f15485b4be40d6a2cf10cade1ee71559cba7a8169e0020397b01ca1903e92865a8c6a5746323168f682e6e8e1ce0835cddabfd580653f0228e9e9eb3d5a5c98f0f4aaac3bf32901482e77d6c01350024ae8ab3dcf1cc181030c3057832c73927ae8692667592d5ecff883a2217a9e140ad06688578d2cb39b0227d7aac321dc0189d3c1947aaa00989335e14d2ae7b2d9f36c8159e7ae4f2c810317e6b820c93939488e4030ea55b2ea1116f798b096f3efa6d63726d067db95015f11bdeb818bc27d5b88d528c43acc9fedc82fd0a58eced9cd3fafbc040dd4769fcec326dab362812a29eadac1143d08eb589a7ac3b7995448915004f4822e2d9160848d9382ea53469f3be828e04e5194a125e9b884feb84f8c6af1be50d120bbfec07154eb30af88dcb1b2ac5ab48b05127b94634f36bc602e41f6f3a4cd277da77c1195d410911330ed1e58247edf486266e152a552df4bbdf6d85ac0c94ba364992001fcd1e9c9d83272e3af8affb840f8b2d1797d6182c635da49ec88e1ca12001c783f43a86c9ff11a0959c914d21c93d30b986a2747e1c0247679a90caec3325a883db5ee9bc8efc16fab8c93fd706577418d28901790e3429047047aab5eda0b369dccd975812a81f0c4ba263ac8eae194987cca6ba5d81c37503115346f7c52600cc2a39af3b34315c4848082435e6e836ea7a8a9d90df1dcdc4a0c5636f32f17de1dbbd79ac5a9562e4967420d37f9af9ec93c15e54c94ceb391614164dae4e4cf8bf16a81e3e44ba77ee6b031382f88d8d8d292bdb7b3dec987e2c05c838b3ca1e988966f3dd9c929bb6fcf015cdebe038381a5482742eadd6403b8ed6f8c80f7ac52633d69b14bdfc991a05cde7aa608622658cef4eac5556ac2db4923431fcebf297beb6f17cead820d2b55ff1321a5f4e81ef87b806b160a46c288b1ae9aec0e126b9d20c445652342576425fa08b75f9e2638343ceb0ba6c13f31366ce0fb6327822d1397fffd937078df7841e2148c13107480af14f0dd6afc1bd02f9002e82cf52c43e5843a89e401cb06ac1be746631cfaf52d8387ac86bd49c7a4f690cb18e8f5c53cf02c85b19ac00b7a180674d673bc73a6bb9194a919530dbb286b204b5a91a022e9bc6bf4c0ad214ed879fcbcc18d155720ec309b1eb0fc9f6d36f51cff6f4d92d2aafd8b59f3e7f58aa953e43f682571a4999a233f24b34725b74599e6de14f8868795935a7a9a466a27b4cd6458a1907ba8ea8a93ff861290ac83d61c100349848985c1a039587529d2d4c0e2e8878329efc2e17fa2738d18d552004184a45f9c4869fd5ada21fe73092c549f557abb3d74ad175118cffeedffbbaf51f44cc1872eeb62e4f2a34cb6e685e0dd8b9d56e1d92f7c93e32fffc60f64c5e410585660afa04638231c40c9247ea0e84a70d2edae659eee5f0bdfb59d94424860437c0dd2be977d0e8be3d921796e7614d40c8ef96eefc0ba1b85424fbda01964d6b97d9fbb2140854846386f5f363e798e9e2af4b1f3c745509cf87e7a9f92a5a20754bd5c910fe1008ddfbe7e9794e86aa9f2c9f81224f8a961441ac578d42ea7e94ee2cc2131e496feb13895a3cb0f1e76bb1575a7a4e2c490ad7f81388f0066bcc7a6775c00835a5ad98c293b4fbe8a8a47a303738c7aaa2d292477fd35a81f57fad88f03f13d1152b16ac0939d814886ee6a5ed93d930fad4ce09e19dec6d071faece531123d088edb3e1b2be5d110e73fe07196bdc9a234382fced4037a1a835cab506167349295ac82b9ca76ec6c16a42bd5c6410936415bfc0964edd79192dd1c8ed0da8adcca40cdddf50ffcfe249f75af77b1126ba054895d9155740649afc172d1bb53bc2bc2e1f48454ed12b23892230560b450ad7f9a8c5ed355837a6b90f533f7d94b4aac756fd540dd6d7d04f6fa94f1e9ad452872b22a4650a897440bb142c35d0000000000000000820b8c0000000000a7292d83b9b573c6c7b8d28b8d40cb5f3921311f79fea5f7f697e0d443eb15c86cdb87ac50d619b7b6adc7723f28265c8be10e5812f4223ac1315dc58cc4b23cb9f0457dc00dff1410b8f58e90ed046a63d566844c2cb000d657774f55e7a13e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab7dfdef30d9e517b8e436ba732717d7b20fc09d8369d87c125b28b86b5144050b66a650063284d39dd3cb3bc12cdc54fcad2db4cab925fb22f1a06a3ef4603ff4a6af183abe396cb85011014ec8531fa1ea5865d156ed8273cd68720d22c27fe91c22e0b66dab8ad134cdae2fd896f9407275d270eca62b6e7d20642bfc7ef60228f362263bd7bf481a7e995ca29a1eaf6e96fc0210135f9ff7e618452586ce4a020124a870cb2b61113be0a9c22be2d6525b6e15bc37d44da01c9b688fe6c1d4fb0a00b8abf9d9ab53c96582e51169e60fe8f559c7be9a03d54dbf615c5a3b6f665d09cbe3d50b7a25b31ad455b5656dd6805e9f469268a1c0a1fa847e59b3bdc8fc0212a27826009bfff3ec30535a0bca112f1c480d30f8e5742fae3b8b3d780afdda03196542aba6171bc69cb3b0071394c4e27bf28066fd623e851984756ff1f3f80802178cf9bf0b25e1cd2d1e9e3ee16a8e0a61618468d0f0bba806234226f974b2ea02147fecbc077abb297601b77d18c43b41cf891ee26adfc4c77fe57b5e02783b2f030ba5068478e738511b4024422ddb182e6cbde89b6ba607a2c168b23fac11088d1e925cb53cf6089b0c45bce5ca220fc9c3d92cfd306e776f93356e34f485fb05eec2b053ba4a406249f10f1391b0fdc25dd094381502efb24ee042f828c803989074d9b812c3a5e25b981bf67b7e395f3071444855950f83f60ad4d77c500dba61656f7ef4b68f3f65aff77d28330d1ec12c51bb0f8c30ba57cdd51022a6572447c6c9c3308f3745b646baaa04aa30714684a931811de07ef54845a025743659168a0a4aa8e6a90f23bc3e505c4089f88754def3409bcabf9a225741ce17c1fa89707c48d2501973890ea51d67c86f662bfdaf05e0ad0a2572ec797acc843ac5771ca75c100d027e6860bf251afdd0b5e112844c3bdc87095340e395cdcbb0deea0942182c4f16a4f5eaf01eeab14ebc31971baf09af05c79070d64846f0c043198337533f6e76cc923b7ea3ac198bb28c0ce68f895f28110927a2ecb1f0696f7d889fba1fc64be47418a10919405fee41a0e99291e0dc3dd43db62c2536bd45751ce8d8426a40c19c38996afa21fee8c9ea0d56274839a5ebfb96b5e0d999bc97311c01627164f834b6cd2732db33709d11cf0ae6f2bd937cad8b07d2a759058980605d05f90e110668626fbdb4b39b69b90ab6ac25a5b50598d4cee30c12884a847ddddf7f81243254582c70fcf385a92a0c9ecf90b5283e3da43a394dea28f475adc7d075a12d3cae9c8edc6aaa95fb83017c85c03adc536f913ee3d93058740f7b365873eb2884fc5013f335b87bdef17f638242feea02d3c309456edd82c72c0ba14de6601ba53e7603878ae8cbf1c73766eaa1cdbd737cd206aa3ca133651c2c5621dd7e2d36783d230e182a0c9a048bb3fb1d75897803f9e0732d3e9df136f73c1baaf77bf6bc20d779073db53e78838129a623d090fad61b3ecec0380b5be2aa0f914fd1607dca614a1f5f4f48f1cbfdc453c240b347f80c0493ca4ff101faa47bce51c2b17aa7d732455f1218e2aa857662abe2ddd526ea2a1c08fd2c1a30c19287a74d8f5dc771022e1592a79881821f333b6535d8921f83370227a0ce13956d968a48773a650df44800e96b8e93ecf32b736be763fa4f9b2ef3f3b885781ecd31bf7d63556b3bc1df5ea16749dc416ff2f40c1c491fd7146822ef5c34a8da0571f68ea16978a65edfb148741f53d529ab6496e896823041b07472d6153b9f44a01801376461fc397c7e0d50fd9fe5ff59715795b62b1f91758ac49308a8a5839c6bbe55e88bb628eed8f2f9d2f1ecc19c85f9f931407fc65e6aaf2227f47fb91d27878f9c74741f632d4fe9551235069eaad629fc5e94d9d6aa64077308ffc1bd25aefb721a9cc2bfcafd6e57c877ab24b9f71db5b88809ccc7bf66100eadd0b2af27ae15e3ccaf85c7b02426016f3d2b3bb9c785dbe879da497d8be76dbd1b250c1ef791b443e0fb0e4709a75156081ad353ca22cacf0c78dc2aef4e5bb19fbd5e780e72eb34cfbfe44450652a3b0f6c73e79ef65ef4f907dbbbda2924e5bec78e4c68138668bd4d8592061531d0ab5741e46777f640785bc8a6492129521f7d4577e6e1b869e06c2724b091071328e9c67a21e36011375c98b71f56c6dee4ae20802517e5447d8203738c8a99811059eb54cc63f4b4a5c24e9732ca7febf3118d87bcb574647837ac84ba928345b1e5a976eaf560437e21150360036b04aaff07afb4ea32e0782ed91d23b588e022455e0dc5fa7ac281a4c80d5ce0abc1743db607fe397ad3ec0f7a1adc04da73caa1bef84061d5953559c3200396d8651ea06ad80c72ccd4fced63450f97afa70af84787d0e83ba5943dfde67a94fb8ec3358823117dff15a7099cac7205", "51ac63", 1, -1175947998, 1537743641, "f4b638f17fec68dfba12311bffaec25102a8cdb5d4981e78216087d510f30945"], + ["35b8fb5302c82d4a316461bf1772160c4344b3ebf58875d32fa5a66c36cabdcc44a2462d04010000000152489ea9c333f483845721b88ba0ca172cce2ab15067d804df4c02adaa845672da34bcbd650000000009006a6353656a6a6a00ffffffff034eee48040000000000b93a1c0300000000016af02042020000000008ac525252536a656574fc396a00", "006aac", 0, -2007214600, 0, "3b6d9dcc2f197e30ed9509c1df8e39b763e7dc22fb910b348d8acd71734aee0a"], + ["d762e424049ab31a1be0b7f3e27d951629ffd13f5638f28e1d793afec6ef937176601305bb0100000000899fac6b2cdfbaa94a3ab48f2140d6b3287f37902883090154873384a67c5a9fe1bab55c0200000008005352530065ac51ffffffff45b427defe4a6101408591b4ead224f525d6780667aaa1eef48981e7d331c6ab0200000009536500656a00ac6a63ffffffff4bb1ec46554d31b95cf0891b4d9533574a8d3fdc3fa9e8d0a6e526efd76e7aff000000000600535153635271f1512801cb6bb50300000000016a000000000357308f00000000000000000000000000a26516fd4ea4a715b99f64dcac7c6a7d23b86edaa7fdeac4b1fd74650a404305d209f68b489fcd3205c724987b0af80c9dbb70f596aeee4a4e2d93d211aac3e9bc1658d99a03d0c562b2b08e6b2219e26d61678ac30f7df0a754e3bee58c90b100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d9453ed89905d2cc8e5fd6bf954b09067b8e91b1072601972f6f0ccdc5162f7e5976d62b40871fb695319df7fced1f5b019ba5772ef7dc75bcdcdb393df3b6688c5f0f9f4c1ffe634693b1b20f84c41f0c8e5ad587ed002a5ea13c0249fe65d4fdd31d69b192b0e7f5c29f4002002850756d43daa3ed2c6c33fad7d7202ab4c30208f299e8fafac2d73b6bc59387a4477118a71b12ff41712bbb29e9b03364b60a030981d34b1517d389f9026bd5de6475094cf52a1b61f3fa83cb5e10e72b97f9c60a075bce4088efba9e2fd1f4fc6186fcb72f8bf9a10c3e77e8310b8bc4d8f315e902b73f6a0dc0b0d3a29bced0e56009e492868c75037fa4d822e191a4b364f0350314ad21f0bf530771c9a54e3f5092b12728bcf6a6710d76164d075565afae978502095d81dae9adc84d0546572d78c2ea9a5f9748ce2edae6305f25dfe6b7a6174102211b5a09fcb43c5c8afdbe3d2fcda02cbd6363ee3b64843fe4122c1b1c8b7427031b350ee30e722ea5be46b0cfa97f96ad8c41d5a2b257784e92ebf4758ba8f4890304138721dce4594b55c8a6ed5181adff9da0081cf36bca3910c1431411697a4e74208504fe033faf64943a5f89776e94aaaefeb95607712a1e3cf58f863ee23dcc8de0a6695eeaa218abd3a8b1b3d5b9a5c9e9f493afe46e1ed853d38f4b844acf7f1fa155eef0793fcb012b0ff5f9851f5b8cee5bffbdda2fdec37360be3ed575c512405ebc9d993a98e16fc1fd14b203fe323f16d214dc82b0f92f43d394a86159273d1e232ef991bca5260a5b074be8296b0cb15b0b113f7c0302add50a5ca33d0f6610ba6ee5d283151155949962adc580f45e78aecc18fd12764547a37f69eb35d2a8e3eb875a82603dce66cf60e5d15820690c60a4e21b01d228bd2ec7f41c4d6cca018d4b43dc4337b36a443f55fe0354cfc108992b066ce644d0cf73a15e3a1f285806c402e1af7cb8b3d6202a8dbd23439213edcfc98622ebb216dc9901e4fe12801504cef33a2553b9c28b156df05f6cdd7061d9dce02a1d8616f57fd8165161aa582738ccf274dbe71353f73fe23e111bc02a3184d9642aed519ded114a850333b57ec5be42da952dd4cb5298c3f48cd5767962ec70b2226ad30eb4f449ce4fae8b14c1b0407c0a7d416f154748e182972e08f88acd20ef3d6c24c17ddca12cf18e6f83a6635bd825cdd01451827902e87ee5cb37512c3c35c4b1887655bd032ab53421d2dea2662bb45d6b792b7e87ed4df0ab597e9611f46b196261ab7664a8f31e947089969183c6db309c1830cefd7a82d55ab13e052580fe97ba7a3b35321b52036557fc2f76cedd74b7bb4c2e621142b96245b973a958d109fa59806772ce0486118caa99b2657306c8d8c2c64ffdab7e4171cc5093ab1e0427cdf526227f341ae86ee8c83ad189a02aa8b073a4086c9e43937b7e21560eaaa9430e63163a79a15ead4ec980258ea3e90a7f81f9f24cab7fb03c9bbca661cb9e456d91cbfc6290e2b30405dd0ca1f151cc7ab50ffb609cab7b6a497dc90a75f1018a51870b238882581b4d635cb10ba7e2a2a0f659eff65ddc3b4f54d09e56a6ed3509e112750eb0586de8c9efab312536c8877478ae3a51d44cef4940ce1633962e016de4591eaa73f5346b44cb0dc8f72d494b29e8cafd919cc6122b985eaf90c41a8dd3e971200129d6fc56729e466e648bc853b94fad0d3db365dde637f453eb9a91771d5cb132519123389973c9bb79c424f235413f9b7a3a1f63bbccb3d2f1bef65aadafc55e1365e2b6933dce72702cfec1a3be37b962baaa26f1e805bc4833acc0c6d92e3621ffd380de75c99c30a2b14de3620a65f028067d1851e251eaab3c42abf8f7a9bf835d8c5eb02e06fe6c31cd7ca4e2077cef28f8323f202c7289a0997c304a57113ab5e368e39c708db632cf530c15b4bd5b2281999e7dbe4fb499bc07fc162dfde2dfb60586e887ab9d7ce614b663aceef283a1c6367581b9ceff79fb9015f592c018c3ced409abb7b8e39959b2ffb2ddddeac323dbbdf16906a24baae822beb65afbda4de56e50be0eb57c9327851f0a2b12137e4ee42a905af68a3dddde57cdd8e1dad8e976d779929013da8e8ec6e9b7902d5977c5443d9547bb7c64a82e0655752fef044cb5ca45d38b88e59024eff2d6007f01f19f65c8e971a327d4ecf8bb54137faa38db7105ab19249f41e9dc9d2cc828374af0f8b49d552324ba68fabe6703a68f26b31ebc4f0a59eff51e3ce2172b07e17dd77275af3683320fa4b5d8b6299cae873b561800000000000000000000000000bfaec88bc752d766caaf628383c1292237541fdca33152b05b7f6cddebdf8f1c5bcd9b354f424c1d4192347a869c7f1324d25890c9c70ccea177e52056dea3e917e10a3071b78d51251d4b9efc815ce21817e5254ce5d10212d257612e21414000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000763da5b688a512cd70a746aa6119d19971a80005e1bd2784a2ee3134a3e1cae9263ccc0139eede9af8967b7137dac94a7fa59ddeb1e6b0823128108a41e53a326107eb0c54550cf64f5a902cbeed5f17839b31a9a7a31645588c0b91e05c32e863afc36754663d0ae4a44c0b78ec46b28fae405312092a8e10dba6f3859f457d0308e729bc10f360885a41d2d8e6dbe2ff2a88804023749e2d4e8faf8e731846cf021906fd5b9718e772661b82fa867be3db945d83bd852680d781fb67e28033b6560b00685a1fcf76a8d0057609d5867a3aec489349dc0ecfeeae3662a97a594ceb3db0fe6dc90ac20aa3cd77a4d5de6f8c16ce62751b49038063000eda03ead912320324063a37469d2c153c88618ff604e13141f4c42a5be198954459c2bb83d1cd070316c79f2d776164aad2ec2dcd5eb026964d49c92d3c8399e2513d1342d9dcec8e0218c9ab0840ee3289369177f5b2bb10ed7ffaa9dd1b0d946bd0ee89f0cb8f5f5602070dec82f151f881a05fe70ecfb13ac7d0e526cd3f7039b05b8378436b97833a0227dea8fc1c3c27f8e0308e5f230435b5227288c96ccd0e52aff78dda4e0071195622e4552261c30acb28f32aef56a03f01268b1bfba4f0a71c5aa0c8652460344203806f6ac939ebbc3eb6c452f2892d74c2799152de08a5abbe54e420eb6793981401c18f21af1478a093f154f3c68db0b2ee22070cdec9ade2c88be1f701b45fc448e21b981d30c469b34a17def709c09edf20366dcbaa943198ce17b9970dcbcc300ec98f414f7d7675d04a77390c53dcc3a011851f9d4de225984891ab735dfb1c42fc5e2c0c7a21ac239b23f0a424cb0463057e2cd531e6dc6deb01dc4fd8ddbe95ff4532b337b0d2610de3dca77125f33f7cd4ed74a8bcb9e20ffee0476ac295d354bad3ba128446e6204dc0177c650362aea6afeef9e4c68c1fe8b2221d494ed3cf91202260d55a8200eabd0de65b2ca4b1c771d8df14a6d9a0454ed7f273fc77e72c0f825de1166ddee10e06518abdf1b23f2085055902985185ea4b0352efe8ff9e34a475fa53764aa5e22d15c90d997dbafdaa929196b7ac0d4e71c9fec4ea6e013123a7b4c785a4495bb1bb1e9557a55451b722caa7f5dc28afdbb831683bb306c2b4a3fe73bbc56d807cdfb2bcf10c57009f44fe4927fc3e548b9300d378905c5a47e15535e78bb1573aeafc7b21639ee2eb72f3b2005b8140c8b1fd6aafeed12ea37797b46df07a46ddf2b2ce9c2c670def3026f8c4dd0c11f42034ad88ae9e9a7df29039fad7b6d3eefd093db9553586c793defe167e24cec5ec912c963c0f57afa3dbb315471579e76f61d5c65c340b558471624dffd363ca5527ef46f65e8e4c5673694510cb7293479b8d45082db08e8a95c15c3f898e54b3213bb5155dfe38c1fe66d301ac191eae0618c7a8fece11dcd6a6e8680653faf0b8e214feb388fded18dcfbf0d44fb99cb8cc706f19e23697d66d393c00a427340556135f43333e4261988ca582b46178e92ff235d430d46b6754ce3d34ae2de2df1e78417482a843a4ae8a7220a3ad6324b10d3413c7f57bbb40d0f51c0f4eae987c20eeb80ada5f1eb5abedd5af666e3985c017f934100b065bf0bf8ed7845524a33e5118ce04cae2f118cf2e7379fdb94485f2ddebf25c4b665ba61d64b4455184b5fa7e3328d71d2a40dc3d7b87af3e7d9a78c544e95b844b3432ccd1991f9141cddeb87c691af5a36fe03cb102c27d88d37b64a36eb42c51610c94cc70ad8735f47b800f6effa164e9863e5657d6ff6c71341d29ea26c82601496afe1100922dc88c41504970f0ccc36572ae5a5ba8af125faa11059f8c5224d13e7a8e50940dac8e6f9a23cbb6bb4d17518b0d6c547c12f3002665c430d2d9c18ee7534087281c4e5d7f439d94f0c89b088ae77294293b4dcad8a4a99ba14e31f98927da20ac5cdc36734a239a8df4078d6f5f557f57a28a0e71d67f0d8a90ccf51a9de323436766754f9d4dc1ed2af4ab88b1c3ab2c319f63d3c6c50f82809d36211b498f8d0a68dc993ae2b5ad39e65e5b6ca27b8f9adc45f22768eeb92be2ef87617e18a522d66c95a854761231f0e7e43b2486d8842487b0ea0fb5878029ca6645f807ffb7880acdeea518df42e756a6fb19da42d6f8fa45cd64a5827504d5855d8b78515694639caf7e9aecd51b64d4f3ca2a2b0c6beeab1aa2e1f8e5614ab704f34a83d597076506511e7b22ca2658a29e329e048f44809c5be86dbaf20874395295ad971f1508c63baa133bfb40e5125f9a70f65901000000000000000000000000971f83353eb2063adbc2a314d486e5c542d1d3d4034a43a97e764f4cebc42f48af614551d1f0c569c89edf9e9ee901a412544ce9e219a21af0203cb5806097bec5fbc0f8ee0bee9dd0c23911ba1c267a830222a7a38a5068c1ab0826c86a8f9a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e8d6a0714181e257580a60eb6657aaf70cb61b473ee15b170f6c5cf78cf7a24e7c5574a81471dc4561648ba751f44326f3051f58dcc01efb88ee4696267630ff6c871fc0ad7a9532bf7da0eed166cb4ce19a20b20cd0d1aba6fa1c0ce766f08cbffe1cefee4d29a0b1219736626d190376514e9bd292879ce46eda73f4d37fc9030ae9884cc0f826c04f353060be2b84c6d44433a863b9590533462740ec5d9da80201eb5bf9187d6c8c6356a402204a0e85d5823736a4f1b2814af27b47470bda970a068588dec97af56f3e0b415035cc442e6749e97134ca2b01b190c33fe61ad371b16e57e86e5cb85e5b791e4070b285ce18a462b28bcf04d521f37c78c2669267030b73f432c9866ebb4b5696e3e3435d65b82bd24682ce54f2637478f7efddab8a020561a40ff45e7a67c9cf1f1d0ba52220076674e3c7481a470dc54b9645a34e6a0226637fa23147531b786e1e6c856a192d4114bdc386f95cf7e0d66c802755c260021a2cd1f414d3b84b0c98f014e55d83441a1730a324f2bc970c5653b574bfdef5031152484a20817937bca88325c40cf40e69bea37db40f7fe6925a9a7ded883e050672a5cb625a7eb68a637f5b82556fb465e0753db27af03213dd24017533284fca27baa2c2afdad3fe50ebbfd12d74ae8972a9965562e35de407daeaebce8d57fc68b3df412a3ce1d0dcb5225190383ac18ad8b7b7564f1f70157187c299ddb966a383681d651f8cfe15590bece79c3167ca0f794a8459ae3bcdb3e57137f845286d18c40d9eafe10cabf64ce97e7967bb6b0ead37ed3655b9722ce4d91bed9808969375a48924c9c97c8a51ae185cce41087c9374f6396c80de54989c02796be370780b73da0fb1c6a2f59d1a86b8d3544e737ae56493c8602e766a778f490bea014debf2c68a2e7bcbd14e8e3754bbf0dd69d6127a6e9317e38e8bfddd38a43f90bfd8b47971fa2d418e18ac7b041712e018dc82ff9987eaff28fbf97dd337b717bfef9470b73799642d3f02eb2a039008479ff745d5e73145738f940317175ecb88a758204d15ba081b70d02194ab4e56bad9a4f1afc4a8b18da3f9fc7e06b56f0b5afa9bbbfe63d983c3794465eaead38a6adfc50452f991d3f564e42741edff8567acc252dd968bb6fab39678ccd7a1b3904ed2498e3c6868282df2867abbb92c8ce2767768eb0d0580e2a6fdf968d6279a8247d3ddd5783758327c304c82733d3a2eb62ecb4e8bef76477cea430ac5d0e3e58e05eb5172398ab5c31f9ce0cf3ed3cf181d6e69e69ad59ac270f9aaa508efbe53b1ff376186d0e4ba765767105cd758f115d2c85d0bd28bab42ad590777d0c585fe0e300eb8d44d0eca73e8607a46e934e5d56217eceabdad34d3ca6af779ca789a529e51722f79253dd01d26cbc583155b0dfd621423ab805c7e6922fac2fdb281fa8583777c1670d798d54e1720364219e7694bdf21f211e477ac787d6691980bca2b075a4afa4dd6aafb4bfe080c74d6148fca1fb5491674045e8a5180d7cc15f1a47f8df1b61466dbca6a4f02f2aa6cc3e2e28924c7bed03774a465c048c99fbd234d8244a4c419793e7112a09c57eee56c4f8b90505c800ce4a1ab101dfb7f7dfde0fa87ebeb49a1a4f462ece47e75930d2034f5528eaf20074f8c0fe31a5dc39dbf237a0db82fd8067035eab3b97fa24faa7bf4c912f2d2a4f35e947d16566d33982a8e9b5ba60e747a5608e8a74722bce989997c015a858437ba57d828741854b313502581e0e70a835d4f76469239fab204a5501f651b4d7326e588b98561d5e0816d17789588763f80c373afb143eea5033a8081fd0307b0593c47b1f6890917f0ef9057b0468bd231952f9b4cfd029b78122f50983d6eccf183f0f7a2051c7dd91af2c38c4deff17c314f4533b10d68600560fdb0b614feb3511ac43914158a3a4f3549cd8c9aacbf9c3ba9d1ea85d29478a92d427d029484d9a18abccf6fc8822712d0cb8ac7a53ea67016b73b1aa48a4035e53379a2e369ed2e9c15d8226b0d82eb5c5c59f02b4ec7bd94bb2b5ff603b4cbb00962fb1a4402b61e29bda23c876ca80c6f8fca4c496876f6486615114ea5a18b9ac015f16a34579dd3db259b0d91224a114c4e9cf7df17fc7d8e60f832c1f3ef1330f9be504da85e693390ef8a0b81637916cb6548ef9d9269d8f000df5e423c98fc68df75f4cd18df89cc6dada0bbd0af125ef369853f4b686ea2ce354d4a58388d226f3daaf50e87eff679195d0573367cc542eecc9ca7580488838c2039b5f8a10a3f7a21aa1790c307938f25a27c21dd02ffb4ebfba12ac4706801561e02ce6b77fea4b76e029faede99ae14df590b94468df399e7b1a75f397d8bb51fc6043328fb9edcaa0dd13267d54b7617e767a4589d137045a3dc1d78a5335d98db9fb5dd08", "ac65635251ac0052", 3, -145564471, 0, "41855c977078a8e8b5d4a48a88fd7ee2b504135143296cdce2871ff841af4f1a"], + ["030000807082c4030468b9cd6ebbf01f53afdfa7997e907816ebce05cfc60e4cc895b908ee56758024020000000100ffffffff624a69fc7efcae8aeeb4e953499f78e8512f68afe54fc8642c6d94c84a7596a00000000001656cf09eb5ebc3232c0ffd7143a86e6e2b7aa2b4e1dc710f11c39cf721fb0820deb30b9e5702000000076553525352526a4f91052d96cb2c307fe311529abdf7911ed74f9e1fb72f0994481d8f06f5d3847791a16a030000000151679ac5e901de54ff0100000000076a65ac5152000000000000000000000390f6ae050000000000000000000000002d325a4358b07d90bfd8015eb6e05f18a49c43bcc328fedf04987c0511e8680f0acfe4372ab09fffbe9662bb7e0b15514446c9f9b1a7f4ec99d326fc8d8895872e109d761b3d6a6db0291a10d7bbc2622d8c4de3cd1831286ac4e00af6e1492d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e6af5c3fffd7b999fc4ee43d9690254bd794e5db7f78546801265e7ed8cd68ac122af07c5645e9c1781aec15df7369fb651910ff11d87852ae53637cb467a3f670b629cadf1d6e24adb54e39ca67940c47777c8a0dfc5978071aabd572dac40d8a6ab988f70c15cb88584c7714b8f1de3e1bdd8e453948bd85e8201ee4eaaad0316c9b1b4f77a1c7806f3882eecc986a26a7e55c81b977f7b24fbdb918efb2deb031bb57c4d9ec88c978f42a62165ae50f8f3849e3db8a4412586adf5f9370dc1f70a0247c6b9e660531e80907023993902cd236524ea9bcb3ed150c720a56f6735908be34cede3a464620310aaa33614ae67861d7acc4f9f5e402d4df5bf5f7d2d5403198cccfc7e950199d80219e3844bb76ded1ff953fa0bacab2e4a1c3b1f9a0944032a958bb1ab244a7e9e77d44f92a02c23b3ed96d735eed623225408776181a3240324dad3983255b480320d5fffb0825241bb3232aa53c8e36059b979ad93ab437802246c939f249b357e0f02483d3b0f5fc1f1b64d6382fc57bd8a9adc91e3200cdf0328eb940fc321d93c4d9c6b61569abee97dac8001812d61962526f84ef2e9c760e7f3b36e1e101e99fb1495dd03bffe4f6c9c0ac386eaadd6c6d9073e3f70d4d35501e0d665a0e19d1ada0d95a5267170bb8c90dc01ebbd1afe93ccc94f0e614019a1ff9a4880de6a4d1ba63cb98aef2dd43091f7e743748f02d9f18427fb2198e291d9a517ea321a5d2ee1a05db554fcbf2da9e8b65a40ebdc2935ed04a69dc08e35b760808adce3ccd7dbc25dae3e0ff3892f2631a843c73353a1eb8090014445bcb49fc0da795d632b4ba426d4d61c74a15ca975098fe57e4a7afd32ef90749fa26a94ac8a5e431a140013f49cf90862446fc2887f9d15310c5b5835378427bf084bf22d93031599e4dd0fe9ab4bd9f2496b367ab6df8910e0b4901e0cb79ae8bf0de78dc3595fb5597d70b77e627336ced428b18a92a31c5474c09624edbfb9bb0e63b8601fde2f1bf9dca33cf8511279fffb0c42500dcdb3975537d479fd6947ed8587e9d7beaaeede1d3fae1b4853bef512f8e47004f76b0c3797bd03382abac875bfaa18fd550b7d311a80d1614fa3f396cddd6c127b2608c8ec3c6f3cf28ba3fdb78bbb7f05c5bfc07f26858e3f455e2558b33ea8f8c22b307a71141bf873bcfae0d4306cd3aa7b8b1870d36752ef3177d9d9a6e7ca0bdca188aad55578b7fcd15469a8520876e457afd7db2f1d3c2e4a0faad9b2ffd72be3c8bf713380c17f7c6099ae62e5dd4ac55ac61e7197323189eb55da7c08e5b7abfabd89b47d9bd70af560ca1be197cd10ee5d8d278a833f1c02df483742b4ec7fb7b54cd07f119e39d3699d004ee215ebf3b8395cc707de0eca9293d225807e8923668347b5431f409847b1ddeddfec796c320aec69666d10001062bf823ddb322f6bb767668b0b85d4117c74c6b8172761d78b0cca09b8938bca81b81d97b3c801635c821cc0474adc0d2b0bc64aae3cd211a9f52ad373fd4d0bf046910350ce1985485dc739253ae618f6c761b57c38e8f7ee9cdb4509a5958777d28b3f08bd8036205a42f3cdf800a8e44a9380306faf7b8a89217709f7b970fa71bd583a8c17b1ab6ab39a673baabbb964dd75bd1f0c889b8576a49e6723eaf25b9257b9ee5b28ca98053702d68199f3b83a7239db3e8a823ba2e4bbcbf801090d7dc4312bebb9a01a451085d902bf394eab8759ee2d1595d4baebfe4258b6b9d5a89323b6655d2d653e19fdd0094dc2ef89d2ceec105dc333e26f5e17fc18ade34d6e6386ed926c2f326efcfe26ee9643cec3ecacbe70c31e8c2c58825ce516cd7075ae307eb35a9ab0587195817b7a83e4980c20a5d39fed945b6ba3e791e197016b905ad797a4baaa9895f041649fe2162c141fa20435908e4355ae4d424bfba4cfcaeb802c4d4668ca7a134bd3f45d0ece1acaf06d6754afb40f7de71938bcd3fc308b04c32bc48ca7bdf243a16ca00b97d233389890b666f1c418a001fc7061a22b9771b56463aacb1ce3d486cce0ac72eac56b129bd62efdf14c9695330c5844e89b6e72ef6b7a179c7d09ed403d2156b03486e9b1e62c5d4befbf8b7b2d8a8a8dcf07035e861c34498cf35dfed601b1abb724d066648e20181599de27932a542ef2d03af34d9eeba0424f44ad1929fbdd13a410f6a42915fe821dcb3fcbbafd40e33f9ca714d0f0d6055bdda0072d228883d385d3d3bc02b012a08f7c42208a34c35c4038a6e7f2308d8b9153b57b8c160346d1b1cbd5870000000000000000d910a401000000008fd3f9730ec2f3694a65450af5e3370824daa039280602ae6aabb356d474446132308a671e1b5f09e735b8c6a526dbc8ac7c25db35dec980c727f40476bfdceebeea09b9f0bdc4d1b1c432df97d79719f74e49ee61199c5cda29e23b24fd605400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e6b7677dd168414ce4d67d13bfedb8f680cc471388476541a8a0df090076c25d105d06d9a9c234b32ed91c9f4ea462ce8d1c757d6fcf112af9d74fff5000ad0af8b8371779fc8ec1a96050383901c53c4e403f9da50788e7ab9eb08062d9e52baa4defd604d1a8eee4e9ec8bc5e5258e28360bf411aad9911ff978854e95356c022fb0901ca13c2701e620114b18cf2d7f4248e4915f8ee85dee9af270031b8bd202183eea8428646ad4de901cc719323d486eba6062fa336c68be968987b1b41b8c0b03ebd44adac87732be4c4d6b434eba04199620aba66c15be559452a2741ef7a1ad504eeced063f7d04f6b75c05ca2e2f645e66a50fb8e3b2ac6695842f4beb280217b66fb7507f00df7ab162ba69d9c13f1d2c327ed4ce1a1537beba898741fdaf02110ae4165037fc12697b21b70bf8207b143a783978d818e29b3626ae43629fa50218138a9bf2c797c853056f8253c5fb641c6460e768f9b3193f9ed5f1800ac0ba020c275b8d9fe9f327941e4d28b2601fc6666e7eaf2d6c8b7f7981d4580cd48700032c47040e75e22fa389aea44c0697faba238a5acafc4a1d24c09cdbd13c1c141c31a3741f752a489b1328ed1da5926965ed9497c3d105c859d22a8f62ef53e045635b3c91a4660429a05c2319d11577a242bbee7794428e39e3deb527ab3587089965fd3dba4fff22320f2c928fc60d24c0d74485c99065e98ef71d99ee22500363819c8fa084cbbeaeac73f8138297de14dab4b776d35b5c6f8969301960401aedb98239084a7e31291f682a51d54c2fb22a3aeff45377d4e7add5b1b11da75a63e0a87f16b39dbd5cb9151e732d8c9b901026b9bfb4d59e7e91bd4c96492772433383f202f66fdf46496014329dab7020eba7760c235224040cab1d764fd49b8fb3bbf7c3f1211b82f1b7690cc66e06a522abd6bf24a29d511c18b32020dcd388285417791bfd5310c9a0dce257b5539b8f681f7fac4b906f3c2bb79108258417af6cb8b9531f251dfca374ecc71e601891d04b918e9f129a0f6edeb68826c21658cd4066cfb3abd9289dbc6f78311e7a08052223883b2837b652ee44083018edc642c23cbff776c1e47be0636193c0ecad33d91d8307fbac1b413963be613f88e065706222c888df9edf498f45600a15140577ef903c6ad55135aedfa96c40a8ff4fa9f4be66c8e24f72928af9b69637775a1233770ec75aa1ac1c9e45d288f2e3896eb023d6b4fa81c062e52d6a7402594d393bbfc2bd895e208fa9026c6c18e42852a55242c81b9208c7bfc021620fc902ee8c8d7ad7878859dee38191a43c071f796c942df78869e0140df8d82fd28dc421625bc499859f719304e248010cfb191747d15cf3a11ed21fca61fde51f9ca8c2c5f925b2d4e77783831dc6294eb61f80fd012935035670eedd07b76a8aca4783c5863d99e76fc9da971336d7233bcdeb04853348ec363b36b7cf9ea2e5bf2a380a244063728617644e3d358f24bff7588d2ca38acca0e7cf2ed9b8be96d9fba4888fe6956657b4eaac07d7699744bdb5b0ceefc9dc4416580bdae5ddb216d362aed24389875abc408f338212ede05c88d9480473290ee4b1e1200a0507e6a8bed5340b7da2e867c3e05f58361b1b325fde0af0fd655d159a6a74aa64c8d572b2a642fcd99821738832e82ad2470c00ae44b8559a8698d4e95524071c4c6467956423c4b415f5c05fb28da3256347da2d901d88cc770b07b1a823092fa9e8a905cdb16ab34aa4e9a18b490aecbccfc5dc5b335ae0a2c62b2f2d2cca3f4209b91dc9e1dae9362e941ae2dda8ff5c13ed919a316adb1694e07dc4d493d89c30b2f26f91a1c8bb6f09826d994057a8b9c21a8b57408a524ad0ff12ce45f169724b978f305a42e811ba218afdef2fe4cb157fadd66e03746f36cee718b6a9a5604828684eb158c678e1e549f9b7a4633fcff4c87a8c721e25783136dfae57e023be7e703b2d743e0bf01f02b19fc6ff540b03c733d0f9761684707e23d732529b517e2365db676d4c4d30b67e08b1cd1853818d7e006fa967702a2a83067483c6d70064efbc9d8a8c69874581da1c80dedda7a840704a8f1a575b84d4e12759247bfce4565e4517496b317e712cb6b1a4ea7720d1dd4c91e087a6b8a21c99d38c9a9d8c64407ae92790edd3294f0a63432a193f5e7f07b6d1007eca304b2255fda1746f4790105bdf9227e3a58635186fa2466979a5bfa2982df200f06b2a27341a419f549bd217ffbd17a1abf50914adeb8bd5b0c8642333379fe08a0ca663d8000000000000000074603f0100000000ef612a01f8cc98fe1b311f286571508a536f338bd962cdfc9aa5c180f6bd1932338d870a6710740d7d17d0d588041a5b30daa5e9fab610f9a40be2909fbf24c739f37f1b1fefa729c115d55d2aeb07170d482d9be7b48bc2185364a58f69334300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b00393042c5043b6aa0d23ca1e61d3a7ea5659602a6802b5d41b6670e07cabafe73c5c2bfa5fef3c3c2c8fd5ff2ce4928b817e718dd67e65d16004e2c8f9d9e65e15103c9174d34e483c6ea208378fabb116402a797423d12b9efc4f2847aa729f7cff77636b2f6737d433a1c82c081339a8c12875d6cdbd82ee71b2a7b2848c030a153ae4ba131cc7c22b97d18b2a705e30941ed1c96a1604239b7fab7ae49b25030cfceee681b355551af5ac829dac0befd79bf68307c04da030afe764d966245f0b05f2527014124cd41946d15c7205041a890a1db94b328731269259cdea7a9950880b617afba4edc1346351d8fbc4090ef45bad3c99966d2393d9dc64b46cd4e1022cfeb4e956548a4228d578376b72939a0b373a270ff04ee069993a8965d1e4d9032a288dee47862cff0477452f3a4930c4534bf1327882dac884b04a04c3ad459703119fb25aa691af03823abcf1cafbd20d7ea6c5b89b2f4eac0e3f6805a13dbc420325d59a73a2d79161f632d5257120014410638ec63c9707d5d1490094452426a903191570f53ce4181a06554262bc3decf91de956d426af282238d0f53a2c7f9b705d03f453953ec70740dd2d86c513b710417732ecd82974201872d0309a93f9e5ffc1c93fbf5cd0f2c50db8c508dec05f1bcf137d8d3d6bc48d0c556720f8bba8d35ced299314e1eeeaf3f362f58fa74188f9a94b6235a2735992352cbbdc72635ccffaae12c9a1dfe678d32b274aa5953db3e37be2330f58657d23cd371a520c5f0864903de2fa428d55e1ec1d48209a0a1220d16ff32a0ee08c8256429caf3ec6ee2c4436052ff0b91ccaee90f2ba57f263e9faddfe7a44936faf6aaed2a4c39c7c1a949d67fda95ccba7b704d26f7fc6f8056ee9af3e2b1b6365e5fd007659f55065c479b58316e406223416ce06239c4d780ae0f961b49d823c44cbacfdc5a22d599005362b1b02fbe05816d5a0545cf9602142b5aa5fd09bb3d1c493b80f14b21eafa29d2cc659f026cdcd367683c6f589979db804d486259f39e624f6a611ddb956680fe62c10df520122870c82b1355a562be880f9933bf87a480d93dd892a5713d0632c1769dd207187da16e9b1e5b2d2892f3441e05e22f71dc121e61683cb6b5314da92c648165ee84705872895d566983166ccd81f7a47f09cab76aa91042c09b0459991fedce73d72af976bed25eeab372298e457c0b4ebc9fceae6351e7ee69b0b9c2184eaf8e8fbe0b386e532a6b99970f98ac0f817a508bc0efc79e9bcdffb2929dff50ff5d18bd1b66f491080e36ec3497bd02c3baaef91259036005dc0b136834def3f13c0687db4bcf5a4d01c35213e41ac72ad7e73f8785228f9e82248b39cec4be795aa70cc77510876cbdec59ff34016110a9f8bfc4449b41a57eec4ae7a72ce58f8232307e4d9aff516e7f97f9386734ee39652dda6c67f218e8ba8b0e2a7417d25b4f4f28202b4536c054ef590324300d9f69de5187f88670894007faf19ab3f80068632dc0b133e8fbba615e4c639979237b90631285d2627228b4202cab114e7085be0541518b1612aa174456cbcf7d219dd3652f4e786e3219a709ff8730d58da69f66eb13ec20845a122cc38bf015656159eab308d9b16269919c64266369a16231a0db8caf54378548e46d918d42b95c76bd7029d6cbf35ea01ec91b96cebe96caee52435c65004851865dd9bae8258333b251575c54302d61b285443ddbd8d16f1b3fb41912828bee3ae2a28a1db1bc6b19f7c38a51f11c02327c4fa8b96e3a8af9889b0e43447ea792900139237805e47ee3499d9594c6ad21d72f43ce1dcf83127fc621f4a2f830affd61d439d22546c02b8234e4d88dc82c7bbe51774717188773241bacf68807f315c7525c77443dbf7a35afa699f0568605457f5e91f9fe46bdf6973114e3e89640f2c4a2ae95a77b990371553ae33ac8850e203662cf9fad0ed7ecf53c5c743eb3dbd6db781a607b99fee827b4ec9a08761993110291e6a36a271f93c2994169dacb851c6172f86004c185980a89c4397eae89c8ee4646b83ac91916bfce6097a3e3702e1683e5ceee0db2b8b3a292a4a5090dcdcff356f26fbd58b16fd426feffc6beb5ea2670d43527c70a7dd62cb42aa0ab015e9175275a962582541c46c91ce85f323927d76042ca4fb774f9b6398449e6d0af6b7a97a0e0f688a18791eefa0f5a42453907e097d272856c2ab920feddf321f6c4bcb43548f0f1569437d0c62a9d570ea30c8a7476cc8b16e402a6917b376570387a333d56c52592f6c79dcfefdc089b891aa9b67bbfe3e4d7531d812c429436cd1ce3d563ea5adcbfc7a47a8dd4e7c98818ff6c12315974026955b33eb0b06c72cf3492b07d473057b9298441684318549d6d4bcce8fea6698c2997689e1ca2c33889a8206", "", 1, -1168474435, 0, "5cc47a664764bf31707ebf6c54557649c811cbad79adf0871283024f65534963"], + ["eb5e2c5f028043ece08a5b66cca326e3d61195fbd255b5b4797ceee7c18eb4fd384d2d34550000000001ac07716e1a15eddbdfe0a47fc7bd715733bad390a93906452d522916346bcdd0e6708caa5d0000000006635251525163ffffffff027f2dd80500000000006fac9103000000000665656353536adaa0077100", "6a6352", 1, -1784199066, 0, "d487bb4a762c1f6821dd5cc9d84901a74b5a58bde34abdd5fcc673150981132a"], + ["2803445d0324164b46619b80be3da7a0f952347ee79171cd29c2499e8e30288351a204914501000000025165ffffffff43e3b69474900c3587b54e91c64acef16dcc21195cabbab82e4b636341fd1968020000000163478d26a4de09a32709d109ef87d6e14c9bc2df4ecad878c20891d5f06030a9fa5300a3c60000000008516a006a63635251ffffffff03fba3d30500000000056a530065ac151903040000000000433147020000000001000000000000", "6a53ac6a525200", 1, -1801080151, 1537743641, "59f967a043faaeff85b780a3d8dd855f540d906c12d63cdf10ac65db4a296da5"], + ["030000807082c40304fc42aa2fbce5758458d91880e586c98bdf502fe3abad51ce96ef90b6a1b951e6020000000500005300ac0b6ac5ffa8d837761a9a65b429242d9443df3bc3f5b9486c6de7362e0be247eb06e1c3e40300000006006565655365407e37468f3f95cf79cf580892904402057c399edac8e929c01518c2c5cd18b4713d164d0200000001ac1e0009b9e72103bc8f403422e82eb16e0024fbb9a732062c443ea1a31f323ab0bdee328c02000000086a516a51526a5100ffffffff02745ebb0400000000026a652662380000000000035252009163bd18000000000307800a0300000000000000000000000058be4cb7af61014af6452c3c44dbd65a4f3c1f4d6053dc6c6fae3ac4232a00e3ade392e91ebd33e2acd9255a6fc7fbc956fd07edbe51b31dad601f84a6e8cd04381c66f092b83d8f8b976af1de8c98d49631e1ffab3af0f3e20c3a715bdb1752000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005255ab0f38cb212a340d1bd822ec2d1ad55f2bf3691eba85799ad4560900cd3c363066ec288fe5e3a5b7d581c6fca77edb8a3f1ee4ef94fec2ce185479190fc5e54f469265e4479f4ef092f5ee02e0d239616da1b82ff24d7996740e23bf6fe23d9391fadf4a6b0c44b038dbd516ccc4d890729e2ff32abf9c33dfa6dfc53371031806f3f7b2b1891c394445f35af43d85812fc5b162b21c1eb31effa5f0ed990a0220059634dbf64094e9bc8ba0951a501bfddfd97b3ea9572d3fe9fbf9f959db300b0777b1867487b8f40cf08d32c81333862e0f591061ab5609acf58717b01d1cdd439f57cebc2a2e4e91c54f51f6c00deffe5d5e55ec54841060b874ec1c96d240031fb9f4f576755dc968129332edcdd78ab20d97283481457931c38ed46894f9100222241417876de61427ed0474efc15a08e2a7b52e0261b2f981122f2762e0909d032779e003a0442186428bdfea430ec1774b917dd5daf830af168731485ee6d7d902197e1c728bf017af9d9644d76c66e7556b76ab00d5ecdacbb4ba4dc2b9dfcdd20202c324e6c73ca1e83fe7e64ff3d45b0cbc77f5d3aedaddcc552a5d368775d39b6b48fbc865849115943b0d5ba1e51970c90c10716fc92f87e45bc31f4908ecb49869cb5717e405e49d3245c2626db7162abc5d0cd69270302851b33e8dd282fba01caf9623840fc4075fef1e271e610ddb23ce646ae9b2e6a5eedae7c4ba5e5deb009d9adc43663058a4bfd2a9a9a00bd59e497d14124cc9a94e2e170913229b1a8bc96e0649631b18c9c2fa100cb0a5ecdd12f1f2cd3e054adb89f0baffe289ba08d843a63a77dfbe816212441d93a90612d5beb06abb0ec466bfc1d4ac595e640e6482f2ccb9e28e4f4aa68086bee23028847288aaafd2e652e7931e6fe6f22271be5843532b310f74d88ab5964f9b01a5508b2858e1e36fe490ede13aace83d8f41ae072c938131d9ad35d7c3d7d762267a2f63e3f4fe0afaad85c4bf1abb759fab06d6f824ccc232bf19e238dbbb47415dbf8e98ddbf51f084afe843f6481edfb371cdc09617d2de9573f980ee1743f57e01d96d8571706175061615bdbffa87d54840ac3bfd06def438deb7ee1868a324dacf0993da1e80909e8a234e8bfddbe9dd7139ce2e5d4367e2fbc149fed340a6f5bf6561c54a03ec449f397dc4f48796903e440dd1fa3e9fc096a2f699aa0b9d7d3b681094cbef935ec5f9489b3fd13539be385c837cb0c0924af4c0044afc9864e5fb24dca3a2ec124ae229329b9bf506b1e7a58847c3d26880d410d01896946ed0fc461825c63b0e0a916c15b238e311eb38cf59aecc4fa38608aa4952766d0ae07af8fd897c631a7f5e56044e030b87a32beb1320ece47a89d1ddd560a4fcaa273a58639935ff1a1e791f46f7cef67d651feac42858692aa814484abb1aa9d6005d867a7ff70deb7036a8388fbab4edcc114c0d4bf864dec4efa1b2cb46906fe13aa1b7d0c44afd5dc5cd818eca3ae61d1f0e3e7211c03db9ddbc5ce3311cf3459554b040503d7a8b61d27dadef52bf871a1921c44b193a688f54b78c2bb24229395318df9af3585a45a9d3dfc2416e81323b2909b69c5920a66eb56c4a27c3c65b0c82c1112f0464c8b51442c8c206efa49a2f2faf492327c0058ecaf1112c1861280e43c624afc93ac869a2560432c6d8075657b13d599a181d18c34a3f175b0c95546730c29281788ec2bd5b7883c6abb03a4010fb3bdd3dd28167765dd49d6f7da641c6978cee056712c19d25af186aab2f38363ab8e859e45934fa21fb76014b619361dc2bf5092162693efcca3448158e108e8167bd418418fb5d578cc79e615f536ced3f54cddb36c2b6e75833565cbb80b93d9135e239697ca8cfb42b1a08eac79711dd138c28ba2f0202567cee1652b4c1914257522e785aec1d02bef121e0389955eca8a81c7da50aea797dfb8b7aa43fc2ea74c761aa65cca4aa5c99d11e625663de2973ed5f2271be66ffc24d9bb05c28e84988c968d61c0648580a844f93c6938f9f7f7130f04b562c5b960a89f96bb47fcc279bcd5f89d96c49ac33d6bce42ffe155dd691b0c4a7f34c47e8ebe278cc72b5c3ae0856966d37403f02fd2490eb9a7ff39b85bebc4b889005634e0257d10eab7a3c748fc5e7e245f9d2003c30ff0263e2b4ec2640f4dfeb0896d467ccdffbd1addaf9c116a5310409cc1c5abdb4bb18a61473c26251833c0cb2e1753b4adf380da4eed6c7b2d855a00b8d6d616db8d461089c7fc5455f6069b626c5705189aa777c93646912b20800000000000000000000000000ce0066f15c71c67c3c4c77ab1d4069a0ff06364fac006bf601474b4cb8898e36b1c7dab2217d166a99548de18327e456afec970e72e283d2d19bf1d5132a8e889e5c0806f1f141a07da505943a25a680502939dbddaa34f9f6503da8f2cfce6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037db8791fac4b4c43a197ccc34b48d3d4b3c02f8144cf47fd16501a15067ab30bf397c31ec396f0c79b96e58480fec01395100ec207a9a6329f2f4ef3e13adb49f5361020c27ad0edb0bbdcf8e12c27a3fadaaabf1a09f3828b311004f5de57a6e49a8281dccab411414ed2dd28f70dcdf76c8998c401f8dd3ba2905cfdc87bb02300cd2c0386d90c9bdc65740abebf5ed1ee1ec1311dfb5a0708fb194626b36bb022ba6d7cee8242723397a57fc516c941ea2339bbe32e895363f406bdc756844d00b005a251f26b872b97d6619659824d911b7ae90f909c7f7d6ed84a7d2d425ffbe37f81383a1c966b576d0a9246cc84335d6d1e264d27c6bcc38557fa1565ca540031133ae711780f07ad14b70f817dbba43758a31bd30f8223250c3253dde690974021082668d488ef0183eb191d92af3c6e503ed5b16f304ca5b7ced06172ec55edc032949c26215095e56905e3c2282450c1a3fc363df851dc3d931016b1b01bf90aa0217ff4d91bfb50c20d75e770f10bafe619cdb4bb7d51b2f74702aceec18bdd0ef0303c474786aeca5855a0922efdbe80bd829952d2d9bf33e6a74be9f1d0ee8ad401b33a7b6df70b5ba67d0f52b17a5ee69ea27b516d79e7fabc1ea3b3fe2d8a26b52c726e7346b722a430ba8036353c81978eb606838e049ae8307b548a1138447d0e984f48f4e367f18ec250f1751970403fca2bb986b1b23fe1d2df9e5232576f834e38ba1feca9e26de369f37a17737e926b50168c4c56a17b3df7d72533734251f4d0c3885409466f15ce8e3e23e4d0e06ac082905225da482622ba5a42aa9867bc49f8bd922c23df2979fd908d35ba6aa0dc638f295035c76e27983d5a23486823bacc497252900e1399e0a11b6da90925d2938854c264f8c862f6a9cdfa1f7470ddd14a72be6288af9d03a77bdeb7e5cde3f36b206fe139edae8781de63f5da1d107c1bbd3fff4680f95cf317c9748746caac4c69432b4d931524f11188ae982d80bf8d995944f0d126d6ae68119541d0678e4976d5f15da984eaa15e625f80cd5c166c7c1d91ffdcc17b18974f3e242665726554c9ae2925e0de85b62f870a60d5aac85ffe1d5a513cf0a7fa2930caa30de0db3f22f7e5744b769bcdbf32ae48423b53c79f2c7f1e15c58f64e7e0cd159493f725f28ebf17d438c23c661c4068ef4bbe7fd66c5e09275a0d2a8725e81ebb2a54f322ba28e76f6fbc5173d6112252e63d90c7f66978c67e886b609edd0eab5e882e4a78837ec329838bd2c2bb296c44aef39939db27cc5232b031bc8d0f42ba8d895dc4706854f97c451e61e987f550eaa9f755d9020cf7732089b6e9a020b143d020788f21eaf1f4103847a320ac5eda39128f5a3ee02a9f60f8188135d26aad8b8da092c8812c1e2e942672f278404bf5d7a5a8c65104a17262602a277193e0a69f4abe792af74c4130459eb6263b715c7665c041c68b66370344371d1034843cc41bce5cb51a9751d16e50ee16de1a321a0d5c59860618b66b02cb1394ee75988126a61942c6fdf90193c1de617af1f2c61c8ef65a4f17a74c13753a5c1965227f6049444aa07c48f7769cdead8ac3e81ba12934cfdf6eab40835f367a519cafc1ef17090916306d6c762203863434523dc56d100c1880716facd237b8a56ceab31c4a26b257499373ddd2a70e3cc38ef83e402a746e5959ba8588ca2e8c93375067540cbef0df52866c71b4e97d2bc74ee7a4a4cd19c4d82d7a83b1004206a897891f5bf0e02d27b7265870ed472a21e93e1ad1407f026532a76429d02a23bab569e67f3a469539532b52ee73ac91ded3e2ce53cac27a3071fb7c70993258a8ecb9c1c425c2dd5b78c3f30f939bdfce5bdac2f72076cf4fafcba4e0af2b3b5c6e502175474e61eda85a136bcdd56e81c0708dbd7b291e41b272934d5260b66b46f943eeb2ba8a053aa2ed3adcab51a1c5bda60a95dec9ef875eb305d43f9ab77e3b8aae6b118590a4541767778d35f8990123e20c4909649389d83d3d2aaae78f4c8db04afc60b7d6a0f6e05b30cb3b90025fba89eaf5bf1ea6b5b45c638117183c7efb258b1b686359bf6430373b0771c9b343e991659a60c57f0bc654ac988af990fb4298a6ae717d80d4af22a5b3616771c08f77524d1c38b3f03d5b63fe1b5967dc02152a371aa16149aa8dd5509e3a0286d2afb163cef36edc397d12d328c3d045342b39d873c640b8878bd7eaf37c550622f8d798e8dc119032930850453248d9a291e1b14266be23fb46319395e41190aa5f9203c58c0b10000000000000000a1a18105000000001d0f3bd24db990ac8247ec1df25cb8913d933ac8538d07e4c0288b62f7f536441b80ba0eee05329736e0667a843949561dff08127db4d2d7ee0149660c6cd725da083ed20a6248410205e6ba2bcc18549e369af193a88e9f44b84bdfcbb6af5100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000237cba9148dd146ebd1c6c3aa915489e88a6cb8fa07ed723453a950bf395c91aa79fb179cb1dd70f28d1d516ad18134c93c4d9f3dadfd0952122101ee946b8808d71eebe2681d7d2c74479207bbfcd735f4450664c14280ec99abaa3819735130a86dd5abd6ad1461123575e167bc75133ded67c028396233f2e3aba778faac50323ccf897bb2dd2d573973628091718586809b52eb9906ea18002e17576b0de9b031a330efdc94abbad9e661ebc038be85802bed1a24b6d40fdf96a1a4804124b990b01461228c00ab1a1e966175039b181040386b0377daf12d917c349e1f3e717e26946ff8c94f6e24b8fbf4ca733034b04384bd782135f325288208dc0fb995bc00313ed6f08e9f851ef3188619f68c423fa07d42f0778d6ca53bcf8cf69b0339ed0030227214cf6ab1dcb70550fa0aa8c31e6fb76e7c803684ac14890346aaacf2b71022166de91330fd25375ffcb6863a4de5782f99f529d52fea87f7314f9b90e44100220f39cf072d8f3ddfe722946665153091d8ec52c5fccefbd1ae3d3e3e09f557d030b5188e1cf01779041e0b97aca6d3af487d6aa331d9965d30aacc4527fbaff16ec88486914e8cf35a6ec04ab20875983986129b6b0ffe3fad990cbb09503d6f113b1b37dbc43ae26d4f4f5067b539018769c1fd0f63adc678c5fd87622a82a8b00dcc9597c510c1e2fac28ce076519e3d2c02c257b09f8408e0088808db8db912a5427614a2b8e001c3e63309042440be99fc21a2c07dbf81c06231a16133f7d183f097227513735b582f45e6153bb44f331eb74aee00bde47bc1dfa3d64aa2dc4bb11d3c0ef9e3f6bb91fa0e0cf8eb6ca9eed7365af5d04de269cc8c2539d95de3a61360d3f55a9367b86b44e2f73c5f12f140961df3db16547a16271a118e5238662b978a5f8889e41b46078395c85655a4bc62a524323faf5e4364f113d45daed7466e22dd6f0191afb34b9271ad93c9af7b95a8f778d2ffe5bc5867676892e573dcfc48a15b2769cfc2e63b2007800aa9efb6f27177ddb081e970ee55a4dff17b4beb202dab975efbb8c7d275de156b966d2cb2ee0dbb09924e395ecbd9d08bbf2a457a12fdf1df12224974f93621cc8edc1eb0fc3a0faa8c2ba781136d14be3a6f40c5641dec85eedaddfd0d4d9eda6ea88270792b7771d933f16d65231d936c75151a03981a8582e13191c7bbd1e91dbe42c2b38a7a4b8126a90247b7970715d585497037ba92b7f44cb76003d38c1d9daaf6478ad6452f1f76c837d7e411e5b5bb9af83822fc97e2afd0615b136165ac09feb350303ac0df8f67d3cf1e8ae764c5cc2ea03f8a76e3cc3a468653ffde232ee16f4f97628c2dc58f77754bfdcfdcd3bed9d1224b04904b1af251b495905a7dfc9a7cd2b18f322e03c527333ac954b8b22f62138584b786954c1ae16dd4c98a40c607ae9503e68215a2fa58ea856716d7596538ea57b2ffc918bc25b3e322efb629bb04f692cf62b4663e81c628457b6264acbe157dc68560c34cdf544d8816ed2f7c2e2703c6ffbb48e14ec11908bba6e8481dbd24aff201aff55ac18171a1b770de7f8ac3cb7a8b91885eedef459c2a8e6bc34e696451cb708c1e34a1f4c4df2250168e9d767f62319c9dfd261530b80d8f581ae15cb4785a8f791c6553be3a3b3bbf1964ac2cc9fc4df108365431af1733d6d02bebff93fe5bcb24c387b0861a22dfd79db27b6f1547501c196074167ffc3341078a215f729930cbd8e88f2924a81197a433b1511543a67fc6aca001455ab868b340a2d1f40d3f8e3bf657db8b8a93dbba237c6979cc90f839fa4f79651d037ef462623e3d49da971ad15c93d96f885223a751edd716f34d365bfa1cf07c23c9562bdb0c4c1d4f93fc63fceb317301d3d881c3d5e80b543dff34536685a42accafafe9fccd6829a01ea7b3a58ac818cf0c5ca5a4afd6225f700c53c3681a1e8e9288e7494fe27a514604dfb4705be53273bf6d8f40bbc17981909ca3e2ed778ace7b130977f6039414de9dd60cafe2fb7478ffabd88c4af8d05062d769ea239a866f9a98647878b7e3b62ab2a3498508aa184d13a4c4c187b516f5f4d0b6b384ba321adb707b18af46be8c996a15c5804c8d33d9d9c3bfedc047b68ffea51568b1348555ca1ba29f136b1dde07b7342b82327d71bf5eef5868828879f05b87840684dbdb216384409876c4ad3562080ae61dcd60af827d93f98c1755749a914fa5f02a35140753a38a21c1ec295a8f20ab0648e94705cd838e8c22c8dd9e19d78a679d6875c2518a3523bbf9e3d823c951c046d8127c22cb7af5dbb3dfd2aaf51ec4be31de2446f8c0710996d7a10bba5d5353cdeb409b6f387ece08d9fbb1ef6975c5b369ed06a129824d72f6f3640f823d373e7f871b68fea54fea1e8b3f6aef4a6e5d1126bdd0c", "52ac53ac6a63656a", 3, 157206664, 1537743641, "606b5f893561832cc423fd8332b12cf24cc77b575906cb01475b4145764a4373"], + ["d35a5e1204bec527fc90e955d68374b678084b235169f59641a88689d29a7b2c4ea45fe3380000000004656a0053ffffffff8f11cb62947a9f062f24348781ba94cf5f3ad2c15798877af49556e055880930030000000553536a65acffffffffb08fd6be067c97a89ca4ffcfc7506f1c6e51acd45cfd601f52e75fd240b5462c020000000251517a592b3f922853cdcda0b161088664c5958ba94b617746abf4c1769538c5c96d6b2dae90010000000863520063ac006300ffffffff03a230100000000000055153536a537aeec80100000000046353536a11cf7f010000000000d5e937220193289c0200000000000000000000000053ef4771d31e0e03d78760d74a9c0fcc53170e66c1a22dad512cb7b06d7414384ac80a0bc71132eb7fd9834b494064aa20e9eb6de004cbef5eb1048a8f48cac0ed40ad7952d6e59738e02b81a7dd5ccb3605a087a8063294aa6e5562a6b902780000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000047551ceb1f5224e8579fde24b001129db6a724cdbc31f0db2e1365865ee0963f8809a52d586a231d08fa625ab82b46b47419eed7a90a54669b3b6cf8d69565060aa38da96a99375bdf9f2f89e4101cc6146274fe6e4d91c4688957effad3be4adbb8ad024584e0f8836ad62048fdef6d0323f11726035b94eab5d38bc82e775b032fb7f044841f7b53c324e8fd5cb41db583a2f09a021b66fc3d5feac09f75f97f030943dfba1148518394e0f4e8307fb18fc5f769fbe3dc7b0625e91800d018fa750a0455bf7c5b65f142a6cc7f160290322b8e9b10f6b0da16462d202e2a97c8e42d2aa54afc8666a36af93d5bfc4945bce322a6aa93207303b40d025a0988d7e97302137d0e1097239ca4eb1eba98145575795eb22c1858f86c61f246a70aa185a77a022b6a8e53861e1c9c4894a39741e453981169fe646e40fafb3abf12dcba3111b80306ac0471960996c98a47b42a99b068872a8a12aa6cf5fb298bf2f3e144841173020849f0c56e789f5139716d9c8ef5d006ebd2b391215bb86ccbc2e7fa71423c510201d912cdf04601caa5f51a4edceac2a5e97edec8d60acdc4b5d696130549a6d82322e156f77719803b291b8b765cfd784f2322090de0e0bce96d8f48d6d92acce3cfe57348e27a489200a7358c0306856c37eaa90edd6a000e1d3bcfe678407ab564e6b1479f56a9dd7195b5e7173af6c9617e084936e713d8351cd3d33ecc57b44c83cddab12d1d6f73bbdb04e8603bc840440d44596762cf65f5dab54dc86420cce17fecee29e6fbaa7d0de2c24e5f4b331a2b9bdc7b2649e6a8363646e316e905bffbd955b03ce995a3ca2708c63eca63e152797645bc9633981c704a9a0cfd591cf52587d6834cf9088553b7418d6eaa9c7c94ad15256c2477ed47c4bd4418574aeee63c2c8e289dc3cb1167ead38e7fafb6f75649139b38ee1ebfd975f4d6536c054c6558c9fa76e3e578ec18930f25829edb26b783b671f7107e7bcf646c2e2905f84cce1e0b1c32f475cb1ded09e61db0d0cb415b2939ad2fc2ddee0b5d12a3bee4184881067689baa2af8e7cb87b860df529e2fc0fac931a0ef8f3d400169d0c78bd03a957b118f9c7cb651090d0a044019414f98d9f6c8f4b3f979e5e20255d22f7f1fe28a54e3d169385fd5f138db2d2bd64c91e666844e8c6ff62d4ae1b10eaf4d8bf2afbe05a34c414cc951e31db9ab441ddb58b8664841629e343b9561bfe5c8b7bb472ba2a638942b45241eeee35adcfb8f5d1023b2be354f299eafb2ec9e37507410da6f4a853dcdac38d6d3fc7f35e0dfffdefd3f12e35ebfc6dbcbe6d2c374c8c2ce439d6e090d2164fd837cab5dbe08934d4a50ca09ab763aeab1d119d04f3e67511c813b7db63a2ad85faa91e4fede3f2fed50cd853ddcd2e8d3608fe9f5001ec011bf5f4c33d127a2e70af02c68d067baaf5ea50e377224ea11a38e200bf25ce0e2f2d0c89b6262d212b604044e8de25b5c5aa2660f0db291eaf9864181848bc85f8b6e7196b3f3693e510c2c5dda7aa90b87002a485b2d595bfa1459b5e1eed51d666c12212691ecc0ee9c688b12fb7e3b5f8884cd45705a7892a9d322bdea1145a955d854973a835c873dcb0a479c0e0cf918c23ff148702b66e9e9be7eb24c7b1ff38ad4c8568362b5d34421b1901388514e4cdc15b36548aac02d8fd4a3e5a3a0debd383e0d8404e1eeab68316213d460759bffffe7c5b2f07888bd001cf62bd9a273a6b5b9962eece15a8ecf40396f4eab914440d2a87d245afe990c50e139245cda2dbfe8f5d212ce3123a17562c4ec397200bf7442048f8f28c23fc3397ae6e31b28486984c5d5134c7b45d6190b3598268e97011ca81d0077ebe66bb0dde0e68401d1f7a7f91ecd973663f519a2e00394a4c0131cb54196b9dbd62c6d5bd6390eb5b9553321c7a33806a8e2f330e2509f8abcd1796b03a06671efef33f9230c4504161a10ee178d906511f1016c8884d205262f8595504a8131c1195074c6bd41cb2744a28ab04f8e9e4b5227b9b9b3a49f1bd936b6e6d257e620390d601d25228e712898f97be4e458cfdf21d6607e89482298fc1d537373628c12c0367d0f2b94f92da26728cc3e75ae66467f3e602d642487df631ff752c69910fd94c6e4cded80f76aa42244077e5cbf597ecf13128baaa20672b59e403e1c6895b908ca8adcc855f58eb99d70f76fb47e1cb49bbb7babcf0225e4426f15b1188100d6fdc138442cbc321d7914d2b4123f19eecb3ccadc631fce92837594815c7b7eb8b0cb732b7027961b2be00fad8c877eb97110bc989261822039b6c8cf17c80f0f36a3dc87ca507ab54904645595e299410667ff07a900a1e31404ad299d082f3d77446a17cb5b3ba66f124a79dd1f10d85d99789f022c4cffccd62978e49964b84932115e605", "656351", 3, -1256698903, 1537743641, "45217b0135742f9f6c4b323f28d80deba0bf1a95c4bfcdb490e20cd711f1ded4"], + ["ec5f6c6802fe1791d75b90ec4206f672343602c06a22f06ec9a704cf8a50efd436dafb404001000000066351ac65ac63fffffffff0f00647eebe0e44f7da5843f9e55287c769951fc40e149589d5445e207c51b501000000055253535200c321d0ca0312d122050000000009535200636a636353acb167e202000000000151d8a328050000000003516a51b7835258030000000000000000ee407b0500000000713142187dce2157e7698d51d92118c42ae0facc6d604973042028c5815e4beb17df9b550ffd520713e9866a1267c23b63735ff9081c1e015f840bdf352a05f079049e684562e6df94d3e53950b4c76e4fdaa45bed76bfc933f06de3f249b4e100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6e440baaffa8584763646f083d5dbda236a069cbd7a0119998acd79a5724d76732988d22126ae51c1b8ed41ded9e0bb7fbe614e6dc3b9a54321450eff959bd4ae3aea5647f8ef01ebaac56af1084ec0955738d5c053886c198f6aa472fd86fef7ff518fdc98a2e94f18e51f66ccea5dd6d8c82fbce278020af97e33699c0278030853d88c745f0a40832bf444958b5479a5f0835af5e02c3c92c9fe65cd1651b50203986aa8f4be2f81830a832666df3874e9809898492a6f11911edf87e8a48f1f0a033d8917f87e8936efc0d6bc019a0768acd1334e6ac6c153b5254762aa784db7a25d98181bbe06fe682f0b0324956fcf075ad90b7c95c1ca15872b554efcec8f0200f88d980f539d5a0e505502d123d8be14ca8addc3d2ca444b1b7f2cd37388c0022deb9103e45aeeab54cf21139743f51ebe39c5c8b908039bbb318d6aa4ad51e9020d3076c5d4080b4469e855cc91d012532d40aa226bf9d045b5261cd5be793b0f020767855d07b55f6fcb0ae4d159a05dbfd36f492bac8725e26743f4606bbbdbac032d7d24ff166ceb29000838222714070d7f2b51d40a25d34d93c54ebcbebecaf95b9a1298314add1bc8b869fa61bfddf7b544cdaf1d269815a588efd002f5c6a8d8c6962dee192569664bff97c435968d1691d1d130e1a29df8f4f509b0d4b725cd4d91c7c479a0fce925e4726215bd029eee7a1040a97ff79b53001dde23aaa5f220aa36a0710582d552be924f8338970302864e3ed17635fc9a990e47ef18eca08302846ef66e887c3333a604cb2a8cb5f4d4cdb2ca7767c694ea5595e2db963eb04e04abdcc535441c7db0265da456b3a07eb80719f06e51c2c82b71012dcd75a51560e31d5d80c7683834b8b55c63b869ca2dc4b822773f7cae43b86bc366ab8b4d6efefcffb2aea75dd864777f54d4def50d1564e9160fef23bd876aaf32adcbd6b62c60fb77f53fb8767d69bb2b685bac06b04b08ee084166fc669aee375db2686df7ee5994b018372b49667cb3149b5872309fc6acba5905994f8a4974708917a09fbe33ead9258b71dd230a1104af87576d945df65c74a5138a42ce31959a68baecb9a2479a3404e5edd8b2d3d7735fe3a3e46b910df8eb522b45185b200fc85e20fbf1eca75e7a85941fedbb1ba5c0e89c7d93ee527b37eb1781ebdaf8bccc0908ece832d798b1d8512978d0b9a4181e82f69ad300929c34467735fa69aca8433ff7c4f2d504a899b468f845e8aa81f23d56fc7f5328fb70de959e8d9fb3d446617ecf09b22566e9b476ca20fe0212f77d2b097d2d354e5e8fa06f227ae6c5019b18f9558aa1bf3ad8b425a5e1fee9915e62bfb8004bde84043559befa1e7b98f14341a621d7784828c7c2e66a09d25f2f4a40b43933dc5e81bc0010c21496e931793fa48a70e05bbc2e716c8ca38e22594d03f0ce717519064ab9fe84ee9c6b554a50286957a2a422afc43257f0ad2094420aa6969cf2aae2202f5b5b0a68c58889e5c9fae97308013b65170ecfdfb334f9fa5183f219a4c35b2ceca167a05d716f20ddd18aee09d27c9448e66e569b59578e804d05ee974378fe37d6ad3e2f4e87157229b957a66669efc30a3216d6fa9d004b1d66189c52b03b782874bec613fda9f87d8fe90525ed289e95d5b27ba83df5eb7aebc4a5c5434ffedf75e43433e44183d08ccf7ef81b893c68ccd37c1fd0429f18dbf4b04be4abe5c7528363e0f0b3976ae678451f2c8fafc16d672517024ca870f2eca157960cc48b286eba7ad1b01828fb521dc220bfedc518efbe5e944995cbfdf36dfa4dfe00286c0f29d8814ce1b8926edf3338a179b0152f05464b7198616dd1fb4714280d96bfd50bc03cb5988a69610129a5e578239674edd43090c22c47350dc1481bc2af91e27e6b02f972156de3dfec4e05efb51d94516f69d15d00b5a07d5a3638e66d9adf0a890467b1eda5bbd3ce28ea6d01744954485d4fe6c3dfe160ac70cccc3e2a2aba4299aa87784016f368abc6bc65697d3074392a5a0d2b04c12926bc09b2bba4d8cbbb971ea2bf4ad0e2bb1037c67558cef0f139cfc70893d06b2d23da7695ca38c676a90c2a5976f195b4f5da79f741533cfbe2b47396cbbc57006b1f368190f4e04acf12ef8c0806137b04c3e1cab6f75a005dd9636a0354a12b321e1d5ed8dc5817df4df906888db1f4838c160d3b9fee7f22b8e20ae9bb73ae3fb9d73420bbede4f1ddf1c382fe86ee744ebd1971286c90c095dc5c92fe89d61b1320571d9d23d8b869249a644739e378dee6e0000000000000000099b4b900000000006d89dd91e39b957d806a9625f629c317b4c4c3b454046849f0bc59191b6ec35681088173e4937a3069a0848040eee12794321ec0ddc2d2aeaba7febe49a01c6ed09eb4aa861a7a305a0db2353e67617e8377a920f29987b422373b48afecafbf0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000095873ca6ef98177f56801b8781531f77d557b2d59a2a07bf1eb0faeae49857eae450d9ecdbfa88a0dcfc616d0ff2d65830751326329dfc804af4d054e30e08381509c5381ca5e153e8de64c86590a6bc51fc5816c5a31615bc83c887b05de98d8fef1f46ba181d88b13edcab862ea8f5ec7dc5e175f91f6950a8c29e532ff896021bb3090fe1cd485add130548714d69ae9deae6bc4a65c298a5f3933821c0d4dc0220a4010d13aa1cbfe9d4065d08a1f0e63902e273156a4d45ef062963a5e47c430b03c48a92c4262658cec90a3da55ac8ce0da7828f73bfce95f813f2fec1d89a2c64f6e578d3619235dc0e6861153ac68257ae7b04ccfbfb4a34e50d4defa81ff6031e630a890d8646ba4c30841230a5e73bfa1236be6cb75afe260b9177079175080224b40102db53252b0d0ac69ec3f66b41470dbae884f43e73eef6a086429ef20d031e2b93fa1e78b085f8c78a105eb4e84fb2e4a5dca99039a32f4aefed29a2ad2f031c84986aec307e40b56aef054ef2b76f4976d675e0d4685ea64bf60b50bbb197031c1ed91ee43ded4e2523fe2c78d19d67c8a774453265ff2f99d32e2b379f189a057de0dbdaac4b867bae0890e0b3b698a3b545705f39257ae6e559d6471bd19ab4fe3541fa7808e9fd47b00ac5b282337fd4e0301bdbba7fe054a2389c0c747f9c16518a403caee8d54084a201be9765a62f4c7fb588f69652778840e7da35dabb0fe4cdd8ae4deea639aa430814738306f9201669255b17b1fff3508255636f134846fb96aa5940f114a4fc02a601dc878a843ebd712da95d0022d0a167c31c6c7a6e23db900fe091a2fded76399794c2fe6eee453a66cb0d5737cbf580c9d973be603d18307020f7340a010fcc6a605122a4ecbbfa425980412a266e5a65e07c7723e154dd059975fcb1fab5bf9a2fcde872158d8985bb5b378819ec24623cbcdd81d710ad3a3def6c4043d96927c3951fd3611cf7bc60ca0881dc3a58614375c9d593a4246daf1448336b2b0c194b6468805f3054c7faeffc902d2d9b472d23c2eca1bbe78b963a7c47b8bba61dfa3054c1d1120424a6c5dbaa6bfeb18a04e51ee5828915495e46f0503e1ae06356d26dc528e2651309ea662708558b6b38cdb2802ce63961d83a5a9e49ea9bef33abf4e8c7567157cc1de6868724736eb76167a405c92c9a4b685a80da4a25d4bb153e967ef50b533ea25fdcea9f9afb3cda7f84afdc26c58390eaa881b0c711e011871d3908a83d0766950da7790a7cf7b4242b3ec75c3d2230c604dd83495527e7fffb35cd9abe5e75d1c3c5300fa932ce028c5dec4c394ce40a7577840631aa442786a017ebdb904f911cb2e4a3c41b069ca0eb210dbc3141f77c129d383bf2813bca6117e7e40eddde490eb51aad1e086bf9d879d1a211fa6907ff214ed695da70090233441bada824f2b84c3977cd8e3ea5d17eec26fdc895dab295d5aa360432123a7f6fae21d3b1f9acc30fe594cbb8c1210acc4946beef6654febc92d458a46aca3b91133fe981ef48a9a5e97065ca65cd586a4320920fbac791723099a3b14eef50e0674b0f6aa518a758a124316050d72a25faa483d9154e954559968ef08120c14e5a55443bc89e0b13bac31f741361420ac6bcf4068b11b48aea41a56acb5a7f73e083b80be3bde1c2edb0818eadb53f720827fbcdcd3ec04174996ee3e32182bad777871028f5bfbadc5dd4878554ddf2a6a04a3e0cba88f92c29c6c4547de104af3145f7705bc98d91b76d359fa15fc48a2de6d655780bc3a2043af015fc012187f1cbc04060cea857ef9c03dda48c162c9ee09d8bbeae62479c60f9114afd13a93e231da35eb95de87ee50151ab5e1cff261f908c2749cce5ae776e2d7f16d6680026f31522430c59af6f00d8c3b1618d70b5d89136437aa5eccdcdba3c050ecb12567dc1a16b0e6dd028a40ace949caab5a1b0ef9bbb62f753cf762448e6b29310c20f378c7a80ea363ae937dcbc0f8f7b9928dbceb0b1367ada6e908a589ffda72b14085ca05625c35108c2688f6f3d336a06a4e3006e5ad1c84fe976497c33e999925fe2d84f863cd72ae4f3817b4f0145ac9f0844deb97930f37b05b0d3c3654a32d392367e0a4a7a8eb227ba99d56e34929a912c247d8ecf8b546172568e0d2d5cfca698beadd22be11c3ff050832361cacdde4162e6ff21988d74028cc8a476202f8445b0db1c136fbd0215dbe02892ec3068d7b6cdf604dc410d997c1862e18eb9fe54c658d11833d1c9601c056df9bbc16206dc22aa35d70000000000000000070f1203000000004c2eaab46c6fbe58386b053dd7da5085634722deae9cea9c62567ae3a6c6a3b5fb63ccad84343852b927665d36425c31d6f940d3d49e17d487417059104891b6d11fea7e2094fb1cd34b31e6981633ffbc2f83fdc051e173d1a5c32343bacaed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000745f6cd162c5a69ae133afc6fa3cf7bd94dd7d47fdd0068df985448cf534e03ad7927456a331676bda40ee7d22e48aa1ca166392001d302ab80a0c1128943c7001cc99f3509d35a923019cece51ea4fb5ea1615b8e2779ca16fe16c31c82015cb596654e1bcc765950969ea75151fe19cd96b15136878e4de60474c507c830410211cdc869c4d1ec2d634436e81b86c28257ce67d174745b6c9d8df032e24b2cb90300643b8bfbd06dc75d199e988cbde99080d31d6911ed322bfa6d48be2db7bad90a010c57272e45e674fdc478e60a2315051a5480333ceee4fcad72ebb772959701567ffbf07f7fc4cdf2e6d62cf396a893bebaa01c8724e21b403c4c7b3d08d0430223163222e11c85cf9ee1b6cd2a44890df6f4cb0e86227409439faeeaef50fa76022b9afc8044a8bee51a51a45d14a2640f871a448773545f11fc4f52e1ac6f49da020e09ff5461136ad6db7ffe5ffe3eb19f90385afea821d974cc09d0e390a38a870326500f308d9db96046055fc2b731ec46b85ab2cb979b50fca5f3c57bcb206e01022897474d67856e18c599e5d9156b6cdf807a4ce5d0828e4a037efc9f19afc48dcd46ed56bc9d9d65f534d92f98951659088ff0f74ff260b481d523f961f68f18715809e7d99b26e24ad91d3497c64bd217b5f8515b074525c8148018ac21c1038182228d3ef3e1f4dd85103576a4d2ec6713ca2762c872f57da3f804b6b310c7dbd19bfc3723c14c47b1e2047a18287568ac857d86aeab683336976489032ac5ba5b46704d5fedf7c4501e9203a750cdcb4d905b8ef76e16b80ed31edcd7c88fc4b0ef1514a7e5db8fc9c9e15c58cc25d691622402d99c2f081e8bcc3674f3a6b28e3731f366a81f4106016abb8c2aca72775bd368b254dec33f0801211143ab2dd65c196fadc9bcead26a03fe2d072edd19de44c5686c83d33a892d7a46e0d8c5a2fb62190db7aa06cf7bdcd4c0a08aa31faddd0ad0bef676a4b7d40eba7b2b226442477efb418e0daac42525526ceae9656c5cf4a880190dc2f8131012fc8c5c52c55a8d8029207e697db924c34cceeffc020c05e77b20539bf00dd9a1670f5e4a6b81644feb34bfefdfec1e7419e8fc2c6850cfb3403b122b826dd689b099682e359f3ba429484eecf238d3f709b46b013faf43e0d38510ad70d56cac793aeabb188d7121a554da26dc2b69329824e6277e67191cccaece8a242f505325da8434730a292877b4157ce94602281b8c0233ad86c3ee6ea7969b5b618d42fde2c63e72d4b8fd8e12dab4b696312174ccae3cecbe494944ab3bf921ff2acd86a5c91effdcd0abb58014e0a6cc75003771736c0ea329a404f2fea61f01ae28e292d6ae0516b4aaac2d909cadf82f8d0c5bf83a5ef46c1b81a86b9fd109130720a8d3c3170d2ae62e181191acd8dca8e8cffb5248b3ecff3cdc40ab4bed3d7869616462090667653a93ef581fb28a8d3e95343e7d61f9c316e563918f33eb09b19ee2ff2002efd5088d9b1589bc4026d94eb017f1afe11f9ba7dd85c6cb36941f00aa035ac1f6bf9ffa7c1523bc9f8519b4984d76dfb82ab1fdeb019068fd19932a06ca0a673c5dcd5d02d34514d07a2690bbb1c80f3a93a457c51be5f81db221595f4a597fc208ab497b27a721248ecd9ae82918e5a8d1e367fd8a08a044fb8722c1a76b70d230199dffcacc2b736ef803da3849d33f3c15793a68f0c8ec5fb11d70bf2e95d24997e18798785d4e4f2abc78fc5fb82d00207ea26bd561341ea8fc0e77c8f5d32f6a02d857a521d0b492e720fbd3c595284659e1ae029bb1c364dd8d4cccd9d88e3b9f16623a216bb34c12c5f58da05d20ba008e314062e1b850e99fc6b2fff56f36c40ca1e450dc1b7ff2937257851f2339eb056bfd0b9644be73c08c16e7ad81f1429bedd1458c93b6c3c31d34998f93d69f6bf80b6642078fd91564c4dba8da5f57466a5f09e7af2339fa7c3fe92c8580d206dcdd8ac51d91c0376ed6baf2d9fdbcfe410bc92a4acf69fbcd5a60483aeb120cbd3ce2d9a71db722dcf58686611bf39b14a0e18ec506a514abf07d78730ed9d18aca12e64df5d07c768feafe6729f4ef591e733738d562bf8eea264348477ffc75465175d8abd39032be61a45b94a3bedcb34520983f4a5a04ea356d58d656c72d625319f8bd64cd567dad9a865134ea4af380decadcad3c60d7e833a3d5c262570348edcc6add6012e65af00c281848f1af324f699851bf05440e1604c6edda52cbaf91eea93564d84135925b8764e35886abdd2662befe32cd7955b22cd05d6092bc0d450c10e3e3366320c10daf9b1c9e11522fa81117a8ccddaab0ba1c0d88d0dac3542df2060b142666ae741c0a565d3e4f86590344eb7f0c197bba48fad25a8fbd1ed63cc2a77760f1b0641120212caccb9605f38803", "005352ac", 0, -223476671, 1537743641, "408c00e5ead89c9d9aa98d1e6ec6584ccdf8717670adaaa96e979db62abb26fd"], + ["594f4b3001da72c6ac307feef443140d238ad5f883c774b7758cde4b7068825803cc73622a02000000026551ffffffff04de6b060400000000085265516a63636553b023610000000000096a5163635300acac6a9e310e05000000000465536352d15e7d0200000000026aac0000000000", "51636a53ac", 0, 840857718, 0, "ebb27ba0ca0a4e3815ba9657d1145d28c5ed21f7df430677548229693fffcb26"], + ["9e080608035010858b9ffc1680a88564028e020d034735dfcada93e5f91ad64d14b47bd07301000000030000acc52de7e2ad6a776b02ee98479dfa8e497ee4c83e686a011e86ce47b868c48d0bdd73f6c002000000010072afadde78774b51b703191f81da5e9270cc8972142901b6511bd2b664586db7d12e7cc10000000006acac53656300ffffffff04ce6e8504000000000600655153535346b1310400000000025365ab39da000000000009525153006565635352aff0c403000000000363515300000000030000000000000000feafa904000000005ba4d2ec1bf345719872eeef1a7e8341c4fb9bec46b5811d5058f9905f52e25b7d23a49e0e1430284f4e6f985ce0e90aa53130b3f45b9ffe25977a5a12a52ce3e5bf9bb06a7d53b7c5f35f390eb391da54779381a5f12fac06ff1c9ad7e7629600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c9b92e65baa4dd8454935a81fadf0d74a2f5154bdab752a76ae0d8989f995d96ce8c9fd8f23f4783504383bc24f8eb4134d5f720b610fc4341ae0a4a40022f76e101eb66860398cf822c7084e9cf1829d3fa1a9190025d5f09aefff7596dae82f4080807884dc4829feee3454a431f166c03afc46c31926e61f28bf7690b1db103071e51c6cf460cf5f3cfb689a27d590419b487222c0a732397764ef54c7af00a0228647238aa59d88a877cce80bdeff35e3141a7f42510b37fb3521ccd5bd7c7c80b03f5b5c64824ad882901b818e8faa614b18d39c3c32422776b7acc57351ffaa77279a207a45433bf644d5b4997c34191acbe7cb200f4ff9d4e4f802038bca0b3031ff5addc95a3c44f704435dba98968294e71a08547ed5cbf37032ead78a4c7fd031c568b414e6200ad7eeb6ea41c60bef43f56884f3dce67547908eaa9f922d811032924ada237810eba098ea6c7d0ecd2e75e245506cb930a22b0a2a3682e856d8d022375c6af0911ac3d36f1b67c05815d2b1d231fd5b9a7bb0680c6eca197d0cf7a03134a59ecfb22426bd336bd7d6f216faca54153c5745ce04d3b770c356ea3fa2d71534e42b25190ee0a2931eb12d383e87e9f9e37fe0e1c5007c11217bdfe932a37cf13a04035c29f5dcbd6b7e512b3aebe268444cafd241e4e5ea74b1edb4e7fbcc30e9ec7e17d51085977ed4fc047e293c01a8b0f7babf0ad6746f5739431adcade7445cdafa40fbb85dabf87330eb714b222b091b6584d7c5b8a331216d914dba71542186cebf65dafd649ccdc41833b9116e1cda78d2e1dc1c31c751ff03ee60163260a829599177a0c25594035444ca873a76b469ea58f175adcb16248239798277fcd926e7f2dcb689e6c3a36cac0accc5ab0b07a8abb90122a7df46f1db7b4c1e836e24a0afb7ac1f9abf4fb326785979993a5180ad6335b4a15640fcf16a5ea476af1546a78e6119acf89da88835974a029df50587c7c141bfa64b523e4a64b335188849acb40c82e98a0cd8fdbdf1e5c5434ea61d56704ba1bb8eb59c008e8549bd52d1246a8757e10cf1204daa22e6504746db095a45d80b6213f70c5e4caac29e040a1d4d18973aa0f432b76c9de33f67c4058c600b9a536af5ff7d5f7334ac93627e143d913f28bcfab1088ab743450de2ac9d2e4cf9787cc7c9c08cbcd735d4a99418e63d7ccbca9adbdc6e3108e473d12cb683be29b07df565fc029f8829e5eeb175710428622a42cd2b62541329f254a142f3000da7a9e06f9ad5f46a767560eed361ec4f69d88ec37c0a08914ef31bc7a938a7e0bec2223c67c6a6cdb23c0f2ff95303631e279a0f34abcd26e946e9c4dec8e8ae1c88890e5c04a54983bb3cfc8e7e8c996a73e8817013cb4e39fab83a728046ad2113ed66220d0e53b4bf21b4bf38a17e9a09df15282a3388b1a7aee4d79c1b4d7942ace13c8754d44e5044fec945c5eb63cdd557eb678c90135be6545f6f9f177f875c950b42eae32ab4545f10009416640b807473ab98207f1c143251193c5256f452f5f7f6c7426ad6e2b21a45eb36499e934c9d30726deaa87c88c0c76899094c0e5f4fb489e6e7fb52f4a46f9dea87553beef1b204c730b15b409d7e6d294ac7fec08f25cc1af1b3624f0250f938a9661a07be4bb67a3f778200d038828722c740185f9625ec12ef19cb3329cee5c2f3fb0f53cca2f465177c3dc61da2fd612b96a2da34dd6527ffee397caa161eab4a76b0e484ca39b453416b4b48cd2c91d5b5cb8434b020bd407195464e4b13ede312f606e0b9e84d8df97fb5928f5483f968cda61bc07512b3dbc1955e4d9e11e8329920147ffb5fc0915ad2b2cd0f15cccb82181d8dc312d8960c994084035072679c83ecd15dd06721baa755652db565b041a4bd125f17ea7f54dc58d4ff8db48a00a986fb98c2774d97003f523d630611cabd531de3d3c33ecee186a3ce2d6736ebb2c2e5efe71e9a10b39f1c9b11089288ec692c3b9f3b54817a7512d4405c42de69de9beee2fdf37c9dbd4202e4e25731be47337ed07d6fe11ee8b8c3a953c6f1c0b676d676d9a153d312513ad39e189cdd2555eedc683149ca48fd44b54546047c4c56b3a3591b89d12e14aaa3e5bfffd7c292bd7b62bd1f77a4f0b3b647766a271680b614637500a43ae4f0e2704205aadf30e7e733ce96a3fd21c5ccdbc79c4b08322d1b0784668441990a6b6b92e599f6305bec94b2205ea4ceaa4b018be6622dd347de009363bfcf24ca4ab5bfddc4ab005ffc95f0f961bb35f46684b992e6b3e145818020000000000000000000000000b4126a3efe821510201fcaf6ccda60d95c2892744dafeaa89bb3f0d5d7ac074f8814c5249b196a7fbaabfc50386691b52a8875a429b4bebc36b7e8eb9b8d19afdf37e486cd6ff196a92a992a3dceefa01e211f046b64c9f3778702b1c501fed000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f3cfb72872acfe17de569883a9c7c7e8841fb010cd900c4623429111151b3d520a9737c859e7295f09908e03ce0df0bbed057dca0b536b3ebaa09be0dd89f72edfb90ee041502c880ab74c2584b04c37db3f631d945fee7b7fc863973251d5f97f0f019333e5d4d454992020b70a511d4b11374a3300c4e2999d6487940cdcc0321ef3bfb56e5829aac4f5e4f2e601fed7b3112feaa3aa6bb1ed6548df1ceb6e4021045ae914dbde2d8f28849e067d0810f5cb9fb9d594fe1dd9414888549b452170b0269facb088a0a5915f3f64807a68bef8b061c06720a5133e4a59177234336c654bc9db79428d5feb45e1acacf77cfaa98ed238df67443243d4064af09f9880702302ba9e9af82213e3a9c64afe47cb18e3985dd747f86944a32c909edfbf23a82032c135b800c13751a34b5af5b7e75333f9f27e4f5200bf31d8bfe64872c3fd57f0305a471e649da8e92fd8715f7b5bea72e7871e7868a68d02f78a7b9b082c246b3020ade1544a6a634472aa5d009d6497ba99464994511e02359cb4230bfe1fb10bc03082b2ad4758993c2502ade355d43b332f5034bada4d8efe8fa02e24f9065b288554c6ee66c76bcda25b70f4a58874a12a6239b0e8996aa376240291749bbb9039888e8ba48b1b0c07b505e4c96d42ef555ab916455886e5894b1c96ade025505e574cf8d254b874cc9eb982c57f41a5ae7b0c26861b4ef8416fbe013aa7ed5e3891ef7e90174de0f94b04b18b0e2807b018af0d9b16ffc0d0a65d747315b6c5d75777d015e519fea942875578c1838486653a77e9a1fcf5a8ad81a0a00f35ba0f6fac3a9262148bc0c2c7b359cefdcb6c7175971105a61a66301a724fdadba6580fa7d56b132439c0fdbaeb0f721e31da86e376b0d43368479d3529db159b65e8cccf5b7dc0d5e24c0c597e1e584311086b60a981ed8d31eb3c324a1083875cf7e3cd2140fd2632445b0ad0985671c5424043a17dfb51a52b5238d369aa1d1d31706bdd8d51603646e8e2719391557cce16ce174c42abfb60c1bb454d9a9f0f1b32070351d5e3847eaa8992e780526a2197c1ace2e4ef6f23f010d7ce3808af82633fee667e02172a1fc8f800917aa4ebdcb1d3abaadd81627b7af214deabd3b2e14e3a8ac247515b07d8322f8bedcfe57cd03fc02eb0105d5ae7a9615014196b9cf89581ecb871a07f1a8a7e179ee7b4dd545b986bc47757a038c52943a78f793dc15243262876d28db31ba85b854b181d308645926441d3b7170d93ca73c095c705f9d3d5d41524ad8bcd39acf95604f576d55547c7cdd8b4f2a4689a319892659757ca2530f00fb57df1ad1a3fd1046703640bd6b84a6a4fa339561575fbe6a482f6afb35cb73ac22a48ee98240c2422d1b06422f26c964597ece0bc8c8aa2e70ae8717361b0d8b935f598fa51269bc1bb708e42e8fcf72c9e7ae34fb25a68a6238db0c948641fba7ef4770dbab3474eda55e71a41b23fb59901f067fbec171dbdefe4d7f2e283f6dc091ea47e9beb334a5d7e222e02c1cd0eb998ff4c303376d4865d856fa3722dbcb6b6dd9bbb1ca8ddd044c24c6ef7d715c4d22c8aa0061a509b60529278cb905927ad26a4fb77fd098949b6dddff8dbc9954f2a5952483136750526979e4774ef86b2ff5cdf69e716dec36b677844e4d54401da5904a14b73faf6a64b5b8e42c84b63f1381804898baf6f17283ca9d52715d8588df242634c7f091930da75d1b910ab2341040a5c68e26c4a472ab17fcdd560ff43a566f024ab34410ed315f43be1e9c3349df10229ed4c05a94027002772ef7c566c00c6eefa89ca480d9b3b78a3cea3350d0e84f0952a36869d06f32fee72c272659fadc93a811d866a40192ad0b0c143c466d3881a3444eb02dc2ed241b5a0b20d3c1a0979b1fd0a4709a9e001c278a972b12cc75b9391578d7ca21af81350aa4b34be220a9d0b73b1688c9d86c367add3bb52621483abc775cbd11e52617b2c92bd779208d5476ea902b7ee6abbcf6e459c4c3d5e6c7ce056f2eec3e24767917302724cd125f23dd47c74a12935a8498a555cb252202659026a145181e672e1a69893b431f4104a76a5aec2a000ecbea95e44aa9abe744e2801e260329e7c76dff78377ea27be9099755bac1c37865198c9ba0984e2d0dc1d16e6f1f74aee2155fc1e6665bf01fd5936f3952cc4d82e182b17dc25b51999523f24c8d9a02c3100ff75e37e507fb406229739fc88f24bc941959c2f568e987ad4c364048b88ad8c9ece3303f83974f93e6754fd584ac86393d800000000000000000a39e21040000000093e498199dddfadda537d90b19bf1c230c13b0d6553a1f736f3638220e6ad37f3e932010daa8a3996b5fb903ffd0e20634fa2e799dea18c653a2ad62108f5748499fcd1b19fc683b72dc64537651edc092a96122d1b8c90620fe1a2bbb450601000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009e32ada111f22f8b57ff85ad5bcfec0ba1cb88214b8a4901aef437824389c2f0b61a2341dd1c910dc1943d1ffa2d4b776e3dff25e332b8a686ac723b5994ad7162424af18c3b750e472cfddfc0a2952ff50bec4547ca95cf0952eeb5ce8a4f017755594a577b5f9355709e611f166ea9b0e5b7d6da35b1d59a738f2293c425d60301414c9cb781e8382da7dcd8bac07c0a28601bbec0d047b6a5f1fa76233554e60224da35554ad8086322a2284103603311e5cac9daee582201192ad870aa4ec1ee0b006a544f77c4cbfff0e9696e8cf89f0ffea79f0e5e62a2d8b210959651db6eb377f90fa0fe3a1f2d95cb9f7410aa8ad1e9736ac667e70a0b7d7bea14be296bb70221cca07aac7af2879fcb8cc50d689937ab8fe92cc784d8d92753ba5a0bda2781022fcd35e0524e97954ca4ca8b33598e5008f4ff38d866f3aae73a37e3854c06aa022fb48027e151ca2aaec3aa2beb39e98813b89b865f45a47a123f820bf151905702059772991c19c16ba5d69da510c7f75fe75b232171164422d84e3d5477007ba302290b6d4702465bee0aee34cd9b111fd6f69079204296b6511629c172c79ea71578f52d4502a38f13c7df47c41befecc6324719e219b5052d62e3600bb59c1e96c66b8ef1b358eec3ab3424da3954de96c86ac986295a8f41a9374af2b51958866aad7817373fb473d45d527ea12ede722a2cb2ae277f51ddf6714eab7ac01b7eb4a68102e19abc61d6fbdca40ee23989c8d91eb2882a683067cea249e18bfe8cbc484b5a9f2be82a6b167d56c87314710d9139c3b7b8115e7a48973314becfcba13506f57e178b05eb41e2a649c345d6616a24839ae2bde7eb19d10316e717b728a499bf2ea9cc68f93db7df6421f9e96b26aa342121d85d815ed37fe27ac0f56996f68e311e1794262c7bcdba4ea806c7da2a4a82ab7ef97ebbfcc558e427cd25eb28b80d5583660e70e560d037cfb01464af72a63af1f0f5b5451cae5740c5a4347f5d15d3ec5ac483f8219ddacb4712e9630ff21f4ba145d05c659515a464a1cabd9ea1bbbbfa35974ef6da08344f52e23fdc136c035706974268e124c4634c019ec5648c1306b3842fce37786cc87725ab5cbf5da5effd737c9b4dfd12e47d4351597a394e741c191ec3d8b18b14765238c1f4ee2b59ba49c330f336c24b3ca9044db1ef7740d97462d14d6bc3b85f4eb33bf35429c5ae748387c9524860300c52b912c9e541f2606f692bd6830f39a7f1d0599b0578b9b98252de173d14db47ff52ba1d740ee016a50644b0db00419d4ce11be42c76513603664a3e8008cd013804e26b8baa104d05b19261fcb929e8c0a9f63fc7226abb16b4be10b71faf2ee8935b45e8c6b0f9f27f0d2abd3c487bcbcff0db74920ce7043939a8a3bdb6e7ec9645da037a64ffcd7a738bbdd32a13b548b29322d9ce631e82aec15d9e0521e37b032185d3eaf21e650ad530806c95de0c54f869e38c1a2fbca482ff6aae4d768aad59a2d73f96e2d380997445f6c0127c25a5dd5b92252dff2380e90ba8d33f37147c1d0a4a5772bf1af492454df2b4820ab4954c117b7ebe3ad5b6b3575f6d4d79fdfe1ee0325776045df16b0ef1ce383c7d260b972d0d7d79468eaea58391cd09c4a06393b23a9dee598bce71512e92bffa0508392309d2f73403352bced4b8400c204e548911a39bc9557bc81ddaa867baed54c46984ef0f11eba3e96271c82e3f6757200a7ddc13e68b22681f73ce116fb2482288acc72c4534e5bf5017cef8a8100ba40595917096085287a9c490af27e44f272116b595b4df991424053c805aad2d84ba8901e84caa836a970003373ab3699d10660771f292b2efe0d7462c35957c56de0e596fa814a111d389eb11ed8786a1af6bc4df1c95213887f08f3ca8921f9975f7bd325d4eb6c57da20a05ef7bc9a37d2063ab20d086d51e8733c8829d97308e82dffd993da40058fcb559e715163962e1c2d1b9b9d0031f4a8833b5f3f16a8e529751bc71f41531e9e7d65bddc3a4f66f60580e6abd180872060b46bcf3e617798e8cd4631dba72df653aa5151fa4dd2be1ede9c3eb635aa9337483cef458f2d990909520235475487a279c23c67bb3163880b5d42d3a56a414f2aa221a47ed741a316603f555d042ff16102905b93b41a43389802c4756e28858cd7c68ed5f3d3515508c044c1ae282a043adcf146f4674568efea869c085acf9bd6ca1d4a7c453d6b92306e3affb5aa0e490410a1ce4c903f47acede927d896d57d4acbe76501e66cde4aa3b99e418675e5fd5325ac4958918bf74369dd75ce8265ad740fbef935c69046901e83b5cb5cde43946640805afe96594fa00e3478d88e7c66f70967bb5a695d495f5ee5444a9e1ded455f5f30366b1b99cf9483560c56ede8fb3edc53c01b2a16b0e", "", 1, 1166948492, 0, "af9db46eb05173cbd4d41b00e39007044b7ef5e1d9f5bf2f1601adef564eb50c"], + ["030000807082c403035675ed1e0d9dc818f186c6fc9e671b9248fcf78abcdca4777cd731d5eb5cbb66030000000752005365515153a1ddb53073a18bfb926bbf372b0d645e00a5af912927ab6e616d12bfad1588d54cf6c474000000000753515365ac6352e63df008031a418b9de8c197c99d0fd44a723a7b83419e8d9c00a6274fe7f5e00b4a0357030000000452636aacffffffff0251c5b00000000000055300ac6500111e5c0400000000056a006a52533243f06b00000000010000000000000000d791020400000000bdf066c6386f0e9419ac285797e71ff551cd2e94d1715a478ed073240be25fcea5c47fd47238f0bb98f25205c8f2dc9198ff8bedaefc419641522e99df28a3286c63ef2f7a8df6fd641265ff9a8fdddf6bf786e67aa12d5307070c69d1c240bb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000296d69bc38e7127fed3e8c30c8836419203d80504ce90f041adc12907f60d44b501b1297dc99e211fafa8b4194881c2f91300e26297b989edd6e0e13d347ba8d8e318973d45b68aa36c3108070acb72a78a9d02b42246c2d86f189a94e4758ff089a0b7e92ac97ea23f931ad7f883816e61c2d18c4242fe94f9d17b6248c83c803192ccd1faa621a310261911e672d49d0e5b1f9154ffcd62f31cd530494591c710318f41b7e530901d731602b3ffa345d7c545174c1276cc6a28ad04d5a377274610b08f36b8ffcf98016d46347e0812f2dcdc88c5fc0b595afc0c863197525ff0ce901f7d38adcd1fc51e803106b6184526f22b487fe264c65b01395cbba5cfc108d0323c7c229ffed569f484d238ad13c8324d34dabf20ea8fa572d6626f6dfefbb3302290c13f14257651d8f0cb74240e43833c848975d7a52ca851fb71cd94263fe91021821d5b5863c648855ab7b9fb1c9cc521921fbd7dcd993f4705bd68b3d99ce0d031e581652fd44c1c0f17b26a5a867bea0bceb1093cc2b1f346be97405e8f091e0020beef99808b59fe6a934514042c0bebb59655859b8e06c00d3c232868023fc54624b2943a293e905590ca38f5fdc59f39ca299b935e653b891280f6fe934bd33e7f4a45c5e2f432f4c393c579bafaeda6dbd1e08762d99b8834065d153cfab6012966b0462c70df47a6bf98758fcbd7003b41554fc492872e115697c37c9bcb76dbdeaefeab71bd1eace45501ae0cc075cb3c3e6ae9b51c8f0ab18a3688e98443e06d40d81f9eea880d53207dd864c54eaeb85c0a979e881498dcfb7e4a470f906ade29cd9f654b00356339c3d39a0d37c17ab3026297d8fba631e0450aa210915950a1ec3357d5fc8e69d438df1cc013ee90e63e9ac5c9870d6add835265efb79431fff8b1d3434ec40e348968df7e73363e9a32c630c43fac78af81a638df677f0803ec6e6e3cfe3ff006500e86f0b8962604ac08df995fe506b88418e5a14f2d8f0217e8c3df4208e30db54dcd78d40be88d011a0037b66ef4478e02ac4843fe003d002d2f7e422a84fd534be29d80d94fe51807f09d9f216bacb1323132b295f89100c3525cbb377fa95694f93c7a5f2f6253ec48325289c461df383c1f735f3f6d2e16103f317e61e65a61e3d06299f776aff01d59575b8a3e4655c01b3e8b5b5052c876a640271d97ba5567dbcc3e149f76cb1e4b1a8c54e4dbef1b4ed728a4da23811c093a4c5b58b16fd53cbb46dcb60b81688e108478f6fedf609a12f79d4dbf57d350a3a0264e34dad17b1b0ac4045415c1fb73e093648820ce9dcf1b70da2836e32c53667e5e83224f9d52804c4f18a4eadaa788c6091b4322b6ae39c6a8849f8b8a92ada7b61bb66dc1b96efe2af8785dfef4321c13fb910f58ecb8a8e17148bdd1be94fecadb18db8dab42599929df44669001d1944a81406985a70db07d43c2772074322c0b3424400787b06286c3170efe4bc0cb0727ebaba2ed70d80c5d7f62f354e1a1f6783a111d133cd4d5ccbdce236be3a4fddc730e039b1a424599215d7ee2aa2280dc7f9bc2c9ddda1151792943286fa6a4fe32bd09766961a22b86b70d30ca5996acec692d66a3ff21cdc1f29eb82c152f577d253b7ce16617e69885d0722c1da032c3392f1e04c5591e8975d949f54b03d478bd20d981f49f5494c3d9c600458cc8e2587dde6dd9de2cf66b31618f9643ab3c86c1ef8afd7e77d806d1ae9de0665e5d1def637eb90a2727808f783fda553f9e625a99816e66351cce0cc57d52a72ae98c533c135349bf8584170fab43712c81990eb7b48c00aec65ebc5653f77548fec8f001ce277df876c806ca8f4944f85f45f5d06ead37797d7555a6127cd231e5f139d6a2a12234fd61059e0661f0bc799a9e890981d66c99b729b5184983fea2a6b4312e37e0cf047f1955ac8a0e17aff096206c759f724836525ad1f81e4b19ee46cd8a994efb1af9936ac37412b9244f3f8f7e7255135909560e8297c1a1bcf1069a5a36c2574774ccb4acfb53f2ca45e41d64c0e29d9ca2f17997e471ad6fa3437f5f2c8bdfa10cf454374a44dc5c227e3c2bac036558e21dfd15c440180ea4a9aa9ffba03e0d2bb706893d0d450ccd2cfb8ceb267e7b402e6e1b98677ba6b479d565900ccb2e66aadb525a17f0cdcf82fb41d66b27d083d9e3874cf588bc5d8110ddb1c049e8c8dca6ac54dbe91289d084eb5f7bc28767296ae2070ae037e7382507c98208c979bd345187ab1d8f566302817444478eec96fc3b937b44257da114cd595bceac2cf3c06a5a153aec7a8df641113b1feae903ede12c182732a0ca1bed4a56de67c565983ddca2b94a1b7999c8a93910770fa15eae92f9629cbe1bb6929dfb81b9390f30449bdb4b98ebe11361bad7d384e74235527756a8bb48cc608", "6a51636a6a6a5363", 1, -661428148, 1537743641, "9bd87fd9dd7db6a6b679b5324770ed0593abcffed789a637e1186d20c2a69cba"], + ["030000807082c40302d0df87886f153e2f82c72b9296144bacaf7d92f9e658324651a95479e906889c0100000006526351ac6500ffffffff4a1a4db82f2eb26e64bf0187040a70f00cd42c3288d319025e2b9d47d77eac490300000004006a51ace4e23ff7040d6b0e0200000000086365656a6a65530065b5eb04000000000665ac006a526a16ca25040000000006630051515253fb9f4c040000000006ac525365535128ea09eb0000000000", "5365acac63", 1, 906935206, 0, "f953124f6b21a43b57e0ba24b95f2f2a81280631a8119ce9716dec89e0277520"], + ["63e6490201824330f4c08514e3914ac4a16b2b31533d47e640473d07b8590ecee9748c57d40000000001acffffffff04bd57ee0100000000066a51ac526a53a88016050000000007006a6a515153acb77a2f0000000000035352531b516d02000000000765635363ac535200000000034441b10300000000000000000000000096a6c2e659dccd4b824cf40a34a43c0a18fd7563bbdac1aa29fb9d321e9b8181872bb6e1416858331cee79dda1c53595e81f6fa99867619cbc4e4584f62da9a3504675177aff6a4b51450c808a560ef2d660e583b004fccd0d60cee8d944e61b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005fb3d95c9251a2faf9b734477c4d4733b753b5b8d0d51cb9c6609cc34774132e2ac6c4e10e9793a9cd9ae33bec905b719828ee914e2bc41025f73c0760e0123e9000cd4477d4077be5be16af9788b5825d7630025f004fd189eaf41b62337a1e23965aa95e633e8a61b02364897c2056f050c591b73c7a0fddcf7b478f11544a03118340d9432d4ad7d00d991998822e5c67ec3b62ecc6e9e7ae8ea7ebf0ecbb6d02219b7f777b8a72d48f493fa6bac175238cced663d0210eec7b61a61f50691b840b07d2b847139f0aecccb57759e75a2cf73acfca653a6a1cbb784cec904866660621dfcd256a0537076f2b78a61ecb967ad6ec00107d3194fa9b5b14bcd6d719c4031f804b2493a4d5724bf0080b28a5465ef0bea776e15af2fd57cc188101d70fda022b7c452a75ce8df583b04379802eca03588d681bd5ba03238504bd4cb9bfaa5e030e272e325c8b36199fe8572b922cb976e1aca941a80f6d9e5ffdac4d03f93ea50211b9e28f972d3997fb800b58c3bc2dc335291b51b2a798989ad20d1c5c2116270303b71b15f1aee5ed6e9abb8698eff44e5600fe54056ffcea934d26a39030370a5ea8430ef690c388a739c5287be73c5101b4cce6a1200472afc144ea00d7fd709905795ebffdd32eec390bb957a0926c7d491956daeadb7e8091fb8394fb930382883e8a99fea6525efcf1116643d726254545b8068658509b4c2d5d3db86eef002027fdd03b69cd3db8bdd6fe1c7d39da75a98284b441130b594982bf9197a123eb5a5d79bf245af5e8149991c19b5dd33c79635e4308d6a378b9d465e9ffba09cfd09d626f03e6d5cfe5b4ee3f95c7873d3952c0261ab77962f9d39842d145d5432ed87594dbb135ab5f5fbcbebf9ac753787d1e475c92d08ab9fcc31e3c90f8682ad5a48c29ea175e4dfd99839f6df0952330cba9170d3d4c9e1b60aeba88d71218918e9cdf7f637db99578669d07fc3ad6adf7f3ceef8885b8d72c824e4ab97fbd1792a0afc8140096b8f1c5d3ab85691f92e8cb517556ef5691c70f14224dddaa4bd52aef8c3b60f6b32b0e16e2855fb0323b1392737229325048231d7aa58a32ebcc53d0b493e314bbd55f9e42233ef3e0cf50c3d0855cf8ff9c197cdee44af8f02e5e7f68a7743e87a197dde9983c9af4bb87451832a3195df9ed1601bb01ae311ec86e1c0720697996a7399db5a5311ea4725d537460cd63612fa5ac44a9a4f49c6e083dcd5ff96e663e7c7d37161346bf606a04ca8ac22aa848471b7d9d1f7ac74da72989ad550bbe418a9ed619020e20f08f8e8d9532dcd1e5d19bd3cc9e50a7a25c73d50f016f158c219d487eb32967e13b958ec8dfd64aa62c84af25d92ccd195dc0cefc2413d10480324f15757e6295ddf91c385a5000359602ab06e2512617b39fb4bced96585d37e4b65b155cc2a6fe74b8e4b42940ac1688f87a0241cf802d359e46d14b6b521d8bf2f4f7ab2700960c46fdef717d676e7e2a08a39fa51873518ca10616ec66df421f50faa27b814b567f87eecfd9ac1e3b10a19c230d1487ae99251a24bbce32ff2233d899829a250a6f4ddaa3e0e8a8c34118dda058bc68529daa069bac5bc87950efdeb7dab12e55de5386d554a4b924fd27773bd5c918ecddd1c2261954d7ab23f20260cdfa1ac64a3406768cb73c49766dfe8478b49b61da4b3fdc5d7dc2deb2b0720c8760429549e841476aa48e3bedc340fbb407355923a87d7096ae924db0ea673c53d7da9e2226618a614980432ed5bd1d86527920b7e2387ecd6881913f8fb702e4499fc6ebb3d4c644a221a3f4f03b33fdb725391fabcfe5e23b8cd2bc3dd0bcd0f245522275770fa2636895811b8f9691197ee57381ba6e740984182422195534f7fde6f4f6b3bee80344e94f7f280f531184ae68d8874fbb7feb183c35eaf859a9be2be33fbab6e55204d592d609ac69ebf0c086d255ef75a2d802ee5f667e99f41e2093bcb0f83b4c569b0ef290671cb733c1206f9fbb3e90d2f8790f27388b7c14fdb33d38fc652229b4064a5e38ef7876dfeb34fd3971d889def2db080877279c5e5d542267d3931db89a5cf564483b5bbd6a32b8538343dba698d6e832996ea98c83eeffdec69ef09cf2e33dde515c2112fdfd6a325f3926f66f103183a67d5566596df6b89a4fe987e48e5d1aec4dd0784024cf5ce12b8112c878b7a5f8b09b0df56f8a1a1e4e7cb1cbdc2d70672c1e6dd08952357afbbd7134c0289e06561778b9f2803f6366ab8c1f4ab9e8e293298115f6ccb5ef413793ff9c00000000000000006678a601000000008ec2f6e99054bbc121bb254c20abf04d2b5ac52b50edb65156317a0a8a4ec6c304ff0c8150b03fceb4669f0f71a481eab5ad5694845ba470c9901fa3d6865470462fb5f3eb1a6b26ff29823cd20ca626bd080559df37a184621d34496ce4415a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010872bb97c0ccbe0d6a3dde1e998da10be22719747e6ac75321e9a20381d8fa9b95e907bde6b410a3dcff25a8c3fe4b76950a7474f157cd75590219d9da582da88bf904c0fb8b680435bda53ff47049aab0821e6e2ccac284d0e940607776bf4f81841a46d89cb2636e5be46885a5101e4ff337628f87ed75001adfb5bf78143032407f5ea10f3fc929ced3d0e2fbe71a46f0b0aed584bfe0708bc44d9c64ba121022b02b7df81565cd34c0cdf6ecde2f3b95771c220b1343760df6acfcb7226cad90a07bb0c7d77605aa9b43d1358504c56b927688467272c9020fe90c94bead5e568c80582d1ff6c2150e7a038caa01a93c15055ce0f702ec22673a67c554780ebed0325603add0854b5cb587f998d5288a4b327aa5a09edd938213b1eed102362a0e5031a9577090bb411c3913399cdbafe1d67ed5ffd1756e493384c58c5909bb15fdb020bc1a466d005bcf386e8780e7d91397727bc8bca64f404020612c0d37a3b1b4b022f3d270762ebdb1df96326e9e9ed8a146295860778efde3cfcbc793f4c6b39ce03035ea79c02e78b5a77ad8f41d5bdb33df0029ad18d07de47f671533f65ab1cbee6ca8aab8719d7a075e66cdd2900c1929dbb8818d9e8807100b28329f88550e48a2737d39d4f2517827fd6ea4887f156b1cb24e452560110b7de42fe93b1703a6b65f929d3187aa054a83005eba3936edb11889f1f05d55f1c75bcc11372351ed32e19682a0cc77b23f4a88730dec799e524d9f105b319ea8baf3b8bdd0d0858ed127e9ba6a9949df6cfa353611f389748384a573dca281a1d2dbe222ce0919217e862e0531c95bbde7290358adfef139db98fa83d018af7847d6d759ce597879be4a33833ee8e0519707c9358ecd0d30b83cb056089fdd81dcc475b81dd9915e198c37de8be51ec6feb676ff425d95a1ae4e168b9fcb2a047315e727d41c93031f1dd6e1fc1c7fafab740bc0dfb672d8ddf25804d340c66dfc146d6486f093f16a9afc5e381c90730d9d8ea794c12fb81b0cbcc4b3e55814a82cc0fdd8bbde612df2f40006ebe7df4b90a8e790dac51c508f9e22998f5fdc850014fff7d3f915c3ea850f0ebfd7d76e9a8be867bdda25f6381d24b62d3f5f81f2700cccd897b2137c38ed45bd696763864c9c51871f2e9f4fd495e7bb40f1b79c0e08dc1436c7781a4ef65a4208764f65dc49d8c6ec63fe329d6680dbc250f20c49cdba4050c3709b1ddcf74d3e767606c76177d92f3e18b1305811cbdebe259bbccfdd40b9e4304a706efca6ffac888a761468493f36c42e842077a4b3308b26dee7f6f377ea130568b0837af51cd6344d2cf0bb427b5ac1b4d2bd7d11673a827fbdf9212d972e50aa25e985df8776a75f86eb28674f0cc83522d1b8cf3f075c3acde1f26223684342e1a9cd5d792a95c9beab25a51d8e3361d273644730ccc8255a54337650fa3168f696579fdb9606aafbe69481b70cc42a1e3095795a76c01d988c624d6764b297bb897a6e64dcda5ae7a3d9c88ce452e8b2341966ff620bfb7157a00e2172c437484adebc2829ea9cc31f976a57b25ee9f011341eb5f1401b75abde56968dfaf57e47cb61cc3540fa50e74ef19fa1d24aebf60a780dc7289ba5be97db975a2125b36a66b33c6127db7ec180084223ddb4646f5b3cf3221455c919b8cb44c43f5ad7117f1c8228913af5d5f2d2d3b72b68c3217a96cf777aeea441bfa61d6c9f13be382441acfc8fa1f6238d3f67bb0689aae19f944dc6a5569e8520f210ca3e8764737aa9596be0fa5eb78d13dcb0c28fe5edec52a501c7cd61447a7e956e61072402d8970d7a3bc1f3ea8a0aead18510e91d4b2fe371f45806cd2143e45aad1b3412a2ca51ce5d57b0646e0a53128274d7f1171c9d73b748074551eb6ff1d2885b9fbbd9d44e97896c7d13c84a1ca9411874374e0b00c062c40256bb4827a63d065bafd8bb03d9b84d9d49176d370857689e3081eaec5f1d0b1d0f85eb8ace69cb79839190b06360b5adfc065324f9789b8b8e3eae789866279ab182c8fed207b9acb1396d2175f005f77f1367125204c4208c1384103e35a7c0f823dcee1dc14e89057f9ebfad51794a0c9b9c01974cc28dc0c3904b7120445249d470dd8e62c29a1ae69973e04694522dbd6a5324a2998d0ff83410211611a7bf58e89e0ff992a7241dcf43acde78a0b319144a707a3974e4cc187d6b9c86cccb20ec92f4e8d89869a91a0e2be8a0ddabb87cfcf99970ddec75285d7c5b0f02d96dfca9033d1491b802ed71367c52c32ee60a43d55d1cb05000000000000000000000000b134d678e5456902fc3681bfb8f572deed9d771e8cda5b78608bd3611a1d2f079bc8734c9e13ffc6005810a2af636fdfa04926b6ccc4edb50a7ef6fd9c9ba0b1a7d76026d089fa7dabe5e94656c2046ade241a569ce2df339faa24ad9f18860500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e51e0d739ad790274cc8a5707ff0b5746897fa93d37b9c079c29f5e389c9f5c8b9181a7f5fa89d97cf75089709c3a452eb55d4d8119fbf549d0d76380f15d2a8e31ffd87e718409e9aa7b9705279079c9cb5eeac9881c9915af79cced6df86766a5aaa1daa98e86f1e6ac9406669ea77be50ed9d2d5c6607b5a6bef954245032022d874581c61e514581d27c1e3dddfc5b90514ba145789daa449ed1baf775bf1702267c2166004a004bb0592a76433917f9583afbceeb1010c14cb359c96b14184d0a00613793723cd271cc6b8b4128e282f57ef81adc46965a70ea1736c741b7efb3760ae072772ac310f2d123a29a305fa16489325109aee9be1799eabe2e898ced03066af2491497e9f928b170aee7ee2e1403194297b5630822dfb2401237fa4459022ee5541e974797fd4f823bfa0b80e92d437d18ee9ab9b03e1c6d2f13de132736020d65a5e2451567ea5d083d2fbcb4dcabcbd371aa545eb3065ebad4bb2c088e440226fde7af032b5d4b7ad4d79c18d671bacc10b495c080826312377d45181f2c0f022b232e0850a9ba2437f24bff89bc2164bd11e9eb153d51cf48e316ca66c250e6f871e6506ea7c1afbb252968730b6365a27d3967238779e2b9f1dfa6986cbc4f27d5e139d70fa7b35afc0b52e0235ae0869866fb70e51b15456beb7e93b3c35e99e34215f12d7f81890d38d98784cac626f618291a4ac80ce752f95d77c18786a95dd8ab7f85455948fc3f80954c707804ec3b092b76c5f3de18796a330d606d512fe27fe42f0ed94475814924051b8c8b0fe5f54a95120c09bc388c84b001629452143d4064d95de110513ed3974596270a2d444294fc2fded27adaad769ed50907240bea5b17a7772ccf1aaf6c13120407e008226c11730224194468dfbe0f30a735acb36a4c173915cded74751d964760677e76a41b6046f5bfc1a6f30a35371d3fddd925d39e6112b64056402f1bf42de80c54f260ae92144d7fda502c3b1770cda712205d9053a1c4ad601b00eb998d92cebdea9c41b115ac06d82c5bc487fda24e97399332f03844e8eb1e96dc38f0de98a75138cbfefd420d28aa9fa778467ab1dc7834522a36d05a08d27fc7a0e25bab107b72efa9deceeb82b0aa22a7abd1c8af85a280cee910baa7f8ae395f36b1bca9dfd2b17298dd11f2465ad9c11ddef55409f38eed8a84fc2e7ab19706e5ffe1107a1bf41611e0da95f9070635699f24f600345ef10989d04d9e5deadfe04d9d5da54e104b9e7a1939d5c39f71ad2ec932bff3c9ee2431b97e34666d2fa273d8d18de25981c440861fa37e4dd9914c735e1ddd9121877c1502d535ca636c4767bf7d25f496fbc9dd7d47c2638ce49a18e7d1d2f7294d9a209f4248007977063887eed801dd99642571eb82ae72d4bf615a641f5ae1157ee1101c61159bae55925e389105e5efa312b20217f7a16dcf531785f55ec630e127dfb1d2ea937973f5f671b208994407193f029ac68d625fc9a637c93325f9607d76aea7d8fc40c37e28e11940a8260980cc9b217a95e889d65e92b92e6d52a8259f9b119ce7a88094afc538c9d0b1a9d7bbd41f83c0c09c19e07faddb3e4307ca3193b055856a207eb470bef4a4285a1edb442d1fbddee14220552d918fe95dd3f3fa96eaf5a9f77e1e76563b4c66dfa08903750bf2ec257d12392301065b3ce1ea9ca038255840f756db06d48ba10d07a802e4b90dcf2abc1e76e970c2cc139d79fd579334e578901491c07c9024cc4c01aaca04633543d2d3cb582e209a49107f31a7c536c95eee82328449a514e5f7dfee9815a08e3fd1d7bb66229f6ce8af44cdcefc31aa09f1c53dea9a40941697309ed85f8c66e5589ef204b8aec1d7f861035f5deb51e36ed23cbc7b45aaeec2a62f6be06e0efe8cf5adbbf0c8870de138af81ee6c92aa3aacc03f4c5b967de6565d03c0aae2ce85dd16eb98a120073112a6071316f47300be654f3764eb49636986b869b7ff4426b4f771dbcc80d961da84341f61a4f7fdbe99e5700e3294dc2e15483a674c5ab72884c345ab176e2069fc43d1832bb6cceed247ad374a1bdde1c2bd38a1382cd06a423bf7ff5ace388bcbd82931aa60476f9b5dc9b0f2788560b5c9f66e3256348187d494c869bdc3c993a255f714aeeb6c022174a2b2fa34aaa7f47f5183a0eb82118d0b9ee38bcaec7ed4c30d03673a600a6f65b8a5641721da8b8614dfbbe09769f95b26ea9ee5c6709e026cae9e5da19ae1136d6a005642c1343f592769895918a18f82f624d5342c79110de82bcf1c0125385fc83771cb3d638c34554de518280bc03da876f69ad45e871b24c99ea22565b02870171b465ae72847af7871f4efa157888c458ebbf0767f94122d14ae0b4bf9b1623a00d647db0bc2ee760fcd4b3ab4671efb58f88ed14ffa0d", "51655100006a5151", 0, -1863630669, 0, "9839b0e31aa81cbb288e20dc5a83c05ec197d1ba9a2bd59aa63290ca89548e04"], + ["d51fd12d042b36cc97b5e9d6a61e631b26b6551f8245a7c9925a3153838081fe809b62559f0100000002ac539180c3c16f7c21200b96af93baa4380ad573000d2c9e67bb38f3387e65835308b9857da00200000008526a525300525263c2a4f65a9baff9c8a5629874906e997366e4693fc50798efe8f5b9ce9a9638fb409e808c0100000006510051635165145876b2fb52a0458e8395b2e03bfbe1ba77ecf7567d372e2d5ee4824867e2e97e53a325020000000965526a536a635151acffffffff04417d830400000000046a51006a09bf2f01000000000800526352655363008412e502000000000853535365656a536334ab92000000000002516500000000030000000000000000f5eee9040000000082f54f2e70585cd81f2a2ffb8272373e6b280b34d3f16e665f195c3b1a783f4c7533732691e137dadfe1a15ece73c90cea6843e2e92350aec8217e04625c146223da9d73ddfc7c3b7963041f155ff05833cd1daad0ade5b93f3dce9ebe53c68700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a8a2e92c757522dcf47783d97388726f33be29b86f05d65b599b110314801beb99e18a621567bd602e76430da9e6b2490a6feacb5b8f1b188b6daea7cb6c4aecbd4e47053571735b842e0e82900399cc5f0517a77f044be04b30c13d48a5815fcfab8ac6b05669803ea0d758e18702e8960106bba9e097983f3e2e1aca25e713020489496cf79ba17a805a6002bdb6f349a020aeeffb5778d93abb8f38d64aa84a032267e721f12f66b839d155003b67109dc5bff1dba9b1b1a0b8c81c03792dbbbe0a0129c1280f5a8851c4252cce19c1609e317bb232a953019f7d0a2381c733bf6f693052951e339282348ffa5830ef39db87f08152684b9744e4f56e0b4316cb570205976f565188c9c183c24e3f02f021dc5b9492fa32dce286f64b446b76a8587203014497ddff6c10c9ba867a766fb8d89e3f86d8c881c772c0e4e600b718718a17021c10a3d01420dccb7026543aeca079cbfb60e257a04d77e467807c08067537b0031b75107ece3ff97ce048fc4a0f2040eb282ffaadeb8dd8787256e4af3de7cf7e032077bd37da415f2a61b931ed8e98192ba53cd4832b6ffaf0dffd293a69b065084ba76e9eb0ce3e300e117f0c3072dccda3f21548bd659c7c7b18c6c0bdbc491f27312e20deef268f3b2ca8561474d8a28e5d90b7e79d7292081e2a55318daf9f7443396019f490c22bf0b05f58e79efd39563541edec68d72ead38d3a06db341699a7df6b33cd0bd28370cfc41135a6268c847d64074f17682e9fec9a533f02f80518371323f406335522bc20052b5ffa685cbb8e8c30a28ac43e4979dfe196859f18a89dd1da01f58ed6128dabd0c2b9c1ee53bee288d6d5526aa903207b1690f780f2695b1cedd5580e6c2039e094a7d71059ddb19b16642a44262ab89173d31807113f30ebd16ac665d38e4d049c820d4988b5ecd7278a6142248f9abdd0be154e08c7eccc63a4a0846044726f9789067b73b1f87b3a5d429fa267c6fdd4a779cd238f3f328a41aaf5a9ad003d345c5af48c260e622a8c80f1d96b735c61bb330e725fb37182a9f6833ed5725f26d9ae73312f1db304893bc3e77dd51745c98c0511c73df34e273eebdf8b15252708eaea2aff91e7f8d36d5e941ed1e4377823c037b0d1dae05ffee6e29d7b0897e2f6f1d97f1148118a91937dbbd07c00399f14bc873b225a150b13a61c798deb37b888c07e42ef6fb07bee1648a3f6569e5abc3f57a32702215fb44b3c96cb02206e899bf7b7a0516b63466b3fddce2cb667fbe4bf4774e9b6da91613c851f6b85551809c9354064a7a6bd3a54a630d0952e44a8d7a51135ae2afb3f2c6b76175ce50c7900cf7f38c828b198b84f41b18b81e2de631e92de977e449014aa55788851353d60f869e8e6d814afe44370cfb8676df0a481a40fccceb3d94dbf4061fc5489e455da0c396008ed8c5cbeafc972a62a4278266878bf52f20574004dae4e12d945b5a393c87b601b91f8c6b3b23b0e9ec8b443144e6f8fdfb5b3d8583bd4563cafc7b2cf353322139c918188a3664c746eac35c2a1f9807603a15f95a619cba92c8e04647702b893f07d9de81f6f4476c6c65ee6bc4d934c9cec3d48e19d21a5704a89ef38c5050281f1bba4150fb081fd948503599211354df2b2d658f86b3977e53b7ab2d3ac02fa1b1669168a85993b511d41745fe28180a66bbf3786dcddc3d881a8c958529f9de718639317d75615dc6503062cf3f5faa42781e2bfe38cc915473c79ecc674ebf4ff9d83a901e16e6a1cf27fce26414caa316b742d880701c617e8ba8ec790934e912ee093a1f5e4bee20efbd10527b23ede1c2df67a758126350b8dc64484d095b5a3f8b364b03ee6f8c2b5c9fbd3dbcdb00b69b9ae332816e0d62a217669114805810032c8e2490c7193ce44724bfe4233dd4fb6c0c318c67c446a7bc4250f1941baec146c8e1ae85431d65bb5843ac4c27c119eb5451a6fd7e04047aee9779dae3924bf1da91863dda6955e0571f131201ca04538898df168e224cea0c23c57303767361171fa672a7716e7b8a98f242f8e32951fdb1bf23926f8b61bd95cd34580f8913b4b82b68665a0524ef63baf93503e7c94cda248042266adbcc5e1d70e21bcb863303e0a986683b1d71b96633d3452303fab2d8d87a1781174b6d97553e9ddc8c3b286b1580f3a47e68ab574a3a9beebe36fa36552aa4821a68284bbd8331e57ce7c6974a36a931d8a4691b5ca5330489d14e2866305e705d96b44bf418afb7297224c9ebf935e8c5de1d26d156ffeb1b5fb1283a03000000000000000000000000b6ad3c7df1591e7291166f3b417e177598124ffd116a72c02a2b556e0b059154d6007c5f8814e5514832f0e242fce2c75811b094e4f6ba328d2b18026e7c5b7e48da21662e6718984dad5b0c7c1f534c285163a52d2f563d3b459aca4a50f570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e66b4ef1e9d62c1363b42a519b2eddd005d1c897d4d20d539389cabd10bdad39c34d886a016475efd63ad913d7546f217b960fd7ed16bf1e5fa7aed2e751dc8c1f084526fa776ec34c381e0ffda311dbfe8e6f1e7fcdf4a5886a0c0744908fd8609d518d0452c3469b0a0f367631d876067d4f6835fe51cc41715468d4f8e66021ebb6bf168e0eaeda00d9dfdba5d218be9717f575d7e8c1591610fcac45b736f032fc3163cdaf2115118230fc9026c0c5fee19839bf74df256f8685f26a02430880b00bef1f3a30dc1427e83b525d207bfaf0d2f4667464f1df3c90a2d037d7c36aabcf513833a4cd2f5c727af4f028aba18d6dee23c9bd7b19b98f6c5c9b478b125031eb6d605c42e732b4a45c130799177b4c6fe489c801e45c44bca61329237958302251dcf459e6ca8fd4cca4490144870f3035c5b1f3cd1d119245a44cfd085c94a0214987a19558081ba73ebd35fe2ec6e92e2ae7c3326f000e2b93302f02b043a180300de9156cafab4c52d554a3363a4757b18319f9c705f9b99243b651455000d9e0321c3ec01a9c91b0783bd3c97cfc8e22aa8dc8c34d837304137506390b6aca213e2a5bbc4fd2f746aeef7fc9abb7ecc38181655e970fb81f55350c2520165e70dfce5a47c5db4927807a50dc6c1531e74406b1a41ad166c1687a5f5eddb12dab452541ef343a8a0b659aaf9508fa7fa0ee379db855a3d0ab2aa74186c7fa6456150d69479bc4b1b93553c56e5033b4ed8f9db59c6347e4802f833ff096e38abf3c9b7487e7deb811d27bb1490da714a31c7f0ab5cbf043bbac4e2b4b41c440a8aaafc65ac9daa7b7c1cd239c54d8093b853965355f2ddaa045bd39d1d35531abc593c19c89c4165f181873bfc23ef06bd3d5ce6e3fbeef2ac871d1f7f476b62f95f94e70ea135fbf208ab16b472ba95a6121cecad886a098138fa26d3663cb018ce132ef7826cf94f1a1bd30cd642a5751d68a0892d8bf71c1e461ae78fccfd363920270a372a8a6c369de43eecde809d422d06593899c472bd5e8f11a102f2da9e81d5c10a34b0fcc7509b70e266747f4b33265c4e00f97e1473759cba8dbe6b2928c4704cfa68c1bd7610d1a986f262e6e2d3b9851b49c26a0d6ef54866d5670e01bfe4dbad47d2ab08edb236c2cdfddbcd08a81c568b32fb25ed6c53e7186330526475a4d37bc552b520141e30ac27d22829e2871a5a60759b5423df44c5547a3e24700c4d6523dfcc1819f491c09bf005a81f02c853f0f5c76536b0a7ca7210a025f6f39f2fdf1eccafea1c059b19a64243fe55aa53d417bc310e5df3034cc71e3d80b6999a9ef033823babe244f7447cbae6b37e6d6d352d285e37b68c4e2a6c2eeea7cd658ce4a923e103a3949471cdcae885ffbc93dadf5e139c433d5b4397d7c112feac8a963fd6956109ffa9d14caa84039aab274e070eb999c4efe8f120bca42dd8c4f32abc1d67fa58535add1da69da24d53040831a73c47fcc56dc4e2895519bce088253d49eb22a4b3a435018b95b5c44a9ed1952647ab4a3e39575d43f2d152a582d6a45440c076e7d7536f05737fc1d3ee3ba310727204a5d3f7853b929e0343328a332a1d46b74160451577af87d6edd87cdb3842efc87b6121fb00e12a620bc845337e8e61133781c27444556b666d1bdc56b907d03bef99bb74a431d23d66dad6602d0040bd7240bef4a449c5e8325f8d500d954bb6e01386f44dbf84ebfe4b6863095c79ca1243680fa1ed7471d2151eb91fff9e5fdac07f0b25f823c3421442900447539de2262b6e5bab28948f2169fb93403f978d6500c01270a09301e15ae51844f25f3333ef73351aba570c1b8035dd7b80f4e57998fe125f06f6699a0c32c0b4f714f70d3aff2cdc2c1350d49db463029d9559ae04fb3b775e855c4f5066051bc574db9d3b01e2657de87bf6472791c6205233fc8d145aeceb8ae629c0a17460f6eff08a327d5bc9ed4a0ccb59ed0511cb6a1695101e286115702e92948b86149ecd3750d914233d143949ec95df12a055b9c4931563ee52ffcaef26bb2bbd2c174bc90d164cc34843f9a311eff4084860012086e0b449474695326a10f5bd871df472a1771a0c3c16f1b0a1bdeb7cfcb3014a21e85296329f9c39abbec734b0d7b5ffaa35ac9c00df11accff916d66186415870a7cf8d16302447025c8a2103af9be8506ee66b2761575e4262c9f8372f26fe2a6232114764c9b4c85c7000df0228fed28d8e103c8a935a6d02b0855784c6d072a4b1fee1dc3ca873f189781f2026f3474ebd0000000000000000c57b92020000000094a2a169ffeec8f1e60c3f161f22e7b7adc645bdc7ef45cba683db36f55356b9802413e88e98af834a2bffb6671747d4de56ba0d44227a73e8b30f51cc90fad8ea2fa88d8dc55253a277f9bfeb59279f5edb116c2749f1f11fe69f5022905e7400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000204975413bbf202b7c173ff7abf041a47dcfd21dd025c68800c002c2649a1b2e13dbf74bf160552ee067d1f588d6074db47e1ea7fb1a9804b09d0773dcc76477a1b4d18fb999cfaa3e70be5b9f1eb307c98bd64e96620431bf4199faf0a1f21c6d4d592872ce393ba4c5de6234b66c86fc9165c2a1a59b9f13acd4dd33bdfdf002132392c98a94406c04a9d3ea4d17c5461e280408686a6b3af96a7673a352fa75032e2ff5496a5c03d17401f728e957f6eb9985af67cbaa4823d106f4d162e864700b082896e27bafbb4b925160a8e708c6cde5f81838f86278ab6426dba95f0f44ff76d86e75c010a26d3d34ff5dae2f414c1b7260ae78db55fd4d3fd367541798890217e938276d34c76de92e5b936b6781c9d83a8b17a2902b1b87a2b50f985182ca0326d8e4233909fe71ce7b4d31bd4a6eff357ada50a6a4ba0cfe36dd9becc138a80201902975c4fba05df42dad5557981096104233ca8996346ae545bba3ca48115f0210ffdc9f0b0a90a910222ee24f44d7087e83d6ef5aaee8ee80bff1b3ed671c5d021291b90104c79a8c5159eae88342fade902f9b52af5eafc16d2eba987bf25ca37e1851e5db885318301e842b8db9256f008213dec8f698cbac3fed5a8e2ef15ff8f2aa3aa8a4bd276e2f016f44ed69af20aeb3c27b013816060e4623edde723d0f9f5d08a117373e0dc3e4f3d67f96e023c5c00b955862828684538fafb6809da2f7b4cbbc53403a015749def633f6d7bb70267f284c1d240dfcee95ef5b8010cdd5dfe81eadbf7c204abf8c74938ab61fb4664bc52797ba43883ba85a06932b7fad934e81bebf329a142415a8fb77f6dc6a5a5f07f90679117385bf969343f9764af239efbff15a13d1742a66fc888ab3900b7d1751f40562e033e07c6f0be3c0381fa3f778f4261a83ec30b33de7ef7cb4433e39717ecf05b611c2f13fd04fdc8c6cf8b8c7b8dfee9a72207fa90837c9f41a23c30a2580463e199d81010a86b0835257c63146b192ef743c7f6fcc4c941ce446316b9c6e74d535be259a29a03e233f100f5585dbb74269941dcbc47ba828f86831db15793e8efbafe8f1cc4e844269e61ce0340162c50e7f54dc60861f0dfbda1913f2869a1d7411d4bade990e43dff3d71760bff87a68d09c43644de0d7722162214ebba8edac94a7531543ae8ae63b1a057fd5aba96921f2bc12a5adec009c13cad0c5d36d69f918f9176704ab7bf1448baa6e170f3d9ad97aa5379d796aff918a71eec6ae1573971776b305324001a0e8bf448edbf9a9ef65264ede297c3e10556ca1b23bd67d7cd26fbf7b5d24c08a05f366cec34aa8bc46c8486d8a1ddb00446cf92fe3d7edda9c30f3a8b828ba89554766acb2c7b5c62d20fc1b22a9ef50bfebe0f7ec798f9c53907212b90c12dfcc4a0003791e7f2797eebb3d4c848a7bf39d5ba2c68b34cacb19566e55c96d073a2ada477e3887e3739b9879955e55a007a91b7e719cd0c2d09c5d90b2424ba89841a36f9bbc5b8b5446c9d7431cc6dd74cc79eb7f1e5cff01bc8133e39c2afb5a52ce707e68bd90ffdf307dcb130151ee73cbcdb97145deb764d6c1782b80a72646fd7f0396209942a6ee3f5fa79400efec72fce6f8108de6db71ae960371b108b487050e74a450f15ccc2c24849f64bef1f9b681e8444b35aeb758331fef99e8cd0746c580e89aa341f098f6ee99392b2f4179849e7114680f76d8d4fd404809f966dd422195c7469e3beaee893d3f1c1c6093b916fe3070bdb56291bda11090171774f6ebc94af8d67aab342cda1b47cec484794e88d09b8e7cc4f56be201645d31eb82c0f9cb7a3574c973c17cd1441819d4bcd13568df9165a4e85d8c021a5f44798bfd9b81c25ed25e74490581225c13693ccc1fa7cc2d485125709e2a8f8fc94b5856007816dc216e3e3100d0e9a6b2f13f79a9a6142fbb89977566db431337b1edb64d2944b7167805d2e04e3da25781d4cbf6f11af5b00d4c691ba356bcae4341cb4adc69ca2624ba91d08530a76c266bbe760d79e7610d6e339c752d2585e099ba41260ffb6a4a887e3b7ce56d5b7634c0390ff883df277117ce3b45ab4350a6cf31699c2b7fa0a18e1744a422fabb8cb31e00c95ddeb580e6c5d7d6a6c27357bc4fa219f0cd8b5b466c682087442081b0e3811eeb8d3eb36e8793ac054dae2082edfbeeaaaa603cfe6180970f1dc3318b47686669e20414f4965ef445fbe827eb04241dab328a94337735a4ce9a0382c4eaeff543de2c08539cb318f3698d5c08783f3b036594286c19858264a5b0dcf0057dbab172c602062856237129493e968baf29b2f264f05228137f9c26478009c3ec5fcf04e1001e78f3468564dce0bb9aa6eb886db0b2a9ce4651ba7852381f31d93ebbd061294f7373612fd404b3038f567e3214c109", "52ac63655200", 0, -615402274, 0, "34ad4cc2f83761e63e9e2003b6cee22bf0d0ffbbdaf5a188f0ae8c32d37a7483"], + ["b343ab4b0327f478f36a63920e47d8ac49bcdf4e84283e7f071a181468451b0c2dead067e700000000009e4c313b9f1c31dc03c0e885fde052521572e9f28463af080554078f3369a676a34cab9b0200000002006affffffffef32edafada28f4c318afaffb30346a14fa5d36ab45c5b9620903c6c6afb0abb0200000009536552ac656a6a65520bf3af38013b25a004000000000453536a53000000000200000000000000008ed68d0200000000526c68d33aa403fd99d8fa323d31f50a6933c22dbc7fe1b4dabbbc7aff21ea1819f53ece18c32aa44591859939d6fd19243cbdce5659041c5456f6cb6a4eb0efde79efe3b097871f5cf8c5a4e50c7e9bba8bfbc83a441b9af37b3b6a2dd6e66e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8314e22f60060f99e38f05c17856f44ce754af2d486129a9ecff91dc46da07901b79913606077a3618c6dcd8b796a7e249a9f81f6a6619b6b92bc69f86a4978aae0b46d35229238be4c59af5a154d57d39d7a84bcbe14af63aeb2d34d9a5d744e670d3cd08afada6477cb98f94cda60ab97950a5e27d2ec2c58fe5963a26396020a7dfcbbe606152ffd88c585a836fd07cf7c40815f784657c4134f795fb409c6022e951921befda01f1a106c6b11b1330902d9a1c27a4b9588a5d4e595771752950a05511a26450a21a8602734fac0cf560f708e57e6f974b366f101b1520b8a9356f48d3a88bb6df4bcb591323fa8c86c64639cd2f867ba80031cbb8376470476370310359ced5aa6d08d7bbd2f4e4e34b3e2388f82e318ea3f6e09b932309a6c63e20311f3f422c2b41a0433ff5d3e4047074dc6bd97fc591c32856aa7910c9f7d91c7030080f5f6be494dbd63372c8780dce1a9a846dbe8fd190ab079245672255916b2030e89522abcd995140a89f80708f8b58335c52fe44282cb9c7b89a78ee6d89f8902271569b9841f2a223acec316b3c338ac8ca1a463a117dcf5d733a5487e585d96fc9351a5a3570ada6c8e5205be477ec81e32d7a201f002f860c66fee95a0c66090a9e84316309be6a7012ce6af789d258510b1dc36fa891616c4229c85730ee46957dcd95a9c30eb46b01f14ce9b8e43b769ea1d7ceacadc2a66a7d11f824461b7b5c2052d85b81fa5b84ceb504d8a4904b27c1f48540c1d725459e4a57e139451a510d82112af6da593d1a0a07c44cf546ecac6c65422b13496d178cddf4e58764ced1d57878fb7cd76f8d3948aee1281903316c984ea82289cb685235ff02e39f9d74cf99de1f614e20c8728f32dc23dd55830d8fcfc1a0a798615a2990bd61864cc391075218c6057ace746056c6cbb41405df2d7b560194188852c6efb11c4bc9621224fcc991613f314ee1d9178e1cee74f6088b7cee78b64f8505786fddb6fb43e19cf70174f836b0daf5061e9e3b6394b8d3b070f733e799e6ffcee5b57b8e3ede2d9ac9e674ae0f63d4c7743c2aa383330efa5af06174a89dab5b52c0b759e6666f7a292ff6dfa452ed044f449d6507985e0858036636e805d08b07e870bad3d6702d8bdfe56d374ae86dd4c0434ccaa22fed19d2a918df78dd4f36af8b1e694add1ad74a2fbdc7053fa79521da40ab3a260ebbf42497c27203604872adc5b488205fe755bc67f22d7785fa574055d2d103d0ce2ed0f7e8aa9b8902ddcba7372123c2c87a549d74ed60c22f42447833413089875ce93e86ff52a442dee3b6e8773a82f39f477b4a0b6b13e4256be39c868de9c6c99e13297feeb6c97f453338b69aae6017a5ca96aee7e2133211e73913a70a05dcf8331ae1b344320cb3b07f1910193652039de0977e474ee97b7a45153f76596d890243ce601f5fc4c1607c4fdb8e2c9c6fd130b23b76cca4969c01a65c1d5a91b68c850a81ea9b751df87aba8c4da0280415fa4c45e2e88d99858c069839b2005a52414b9abdd4cf496ca43712d12473ebf81a2ccef55ab9e11e2bb191c20e4511a8c4b306e72554092de46cc592207e8e67cf0da26c752ee847ef376882592ed373672db7c5e88cb19d5a16eb32536b55d0e349f8437fd957e1fa89cff02552353b5bf2c79c649bc86e9d76f3b31af8609b78c1face726b8f6d8ae4a802a802d47da60f9e4009fd9610ecf93b2567458b378451e9a8a81606cf301f8f4aede369fe53cc92c983b6bfe139871a1d1bc2d61ef62175e60c4b5e1f2fdbfe5079c0c5f44860892919c0f1f092a821be51ab72a26d3e842aedb08e6febfb5785f4df25e5717938cbc4cf8a573ec623ece67a669c2dcdbbd29c688b19524d18a4a92a2001f2077548e0570743be4f23e17bc72d61586f2526e5a82de8e252fb534005860f8f6f1384aef0f76af74a2a71ae1b8762c1307608cadb7ff5ae110ecbb580bf25486c53fc8d2f0cce60a4dbcf62826fe121e28f6e4e617d841fc1a11eee3639b6286ed0cf01e84b676380e5c7eca5efc49446508908bdcdc756bcf0a40745c2aa9a96e3f0d887ca358bac431d9c894eaf0232a20ef4f7d28c919126bf1750947c7d3dff8a2b82f21f2b26d005aba4443a6372a85f096c79c4842e981d6d4aeec6062f9c5b6fa6fdfcdb596273ecb2a496ae0da7e57dac19129db3d6db70c67123437126cc41bdff8e8cf306f68b6e59e6be457d24fb0f5dcea53fc9d0b061e0c9fabaaf64af9a06d1dfca105c58e6dd10c35932b4a62cf1ea6ebcc030000000000000000000000009df39a059bd884746ba1e24c45126608c9f15e2a3f2fe569d8624fc9342ddd5a05b09c0d3d3693da443077b4968a5e219200162626fed96313f0068c2318d57090909fd17d30628c6d0b8348443dd98cb41759881aee927236dbca6b5896f02200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f184a2075b2baf83bc6bbc6b4907225c4e3c1741055790a45c1cc45f9534666b19e1c4c1789fae219b44bf9864131a1f0d964660c46606502e1dd4643e1d38233b211b3539f1da5716472500f5149f8aeec6a5ee69d6ad4d3a8e892e5680dd3d3409ff9e064e04e34f36f8571b2225960d59c4f87663f897bd420b5d185b5d5d03174ead75626f14e66c5b9fbd8d5b1726ddf629ebf17192789960a0eb2bc233ec020aa5cbdddf74b263203e239a74cf5d4f5d1be41772d5e17c17888452c3763e730b02e136d55b9d8505b781258ea3af6028e847434110e408449cd5fb20eed644ddda52fe05dbfedeb30f40867c6d4246bab298769c83ce41f60dcc5b68ddaaa191022078907e101d4909c8079716486fe4def40697a2d3617c47d98f6e3fe38c72080306a34a164e8479a2c46379cb2e7fb5d2e6fe4534dbb8a347b1f5fce1ae0c8928020fd519dab0f7f236df2795fdbf4ec98fc48aa2c84f5f29b2cb3139d9ed48bcb80222f2628b3e7860336adf4f58c4bffcbd499d6ef63f14bef08a8fe39ed5da3b870312f69c6adf8aeb06b4217b5e11b395f200ee684be007ceea879022918f7145b1cc9e840b8e98f0776cf985af648d508523a049fb776e5d55f14e1970d5b782defd0f79cd8164f5ff2eb8e34b6c504ccbf0f5ae7b597c3915a03e682995a0459f82dd4f3f4280c3d8c176bb5a4bf7ae2cd7bf380c6db75fa40a6e8a1e00b9ab780f2cd51a37af3f83d9cbb3971fcdfdf1edba67ba3456baa3fcd0f5c5a1a59c760ee9ecb70498345bf30b967f68cf18f4f718aadd5d5fb5c627ff17a67de116aa1254f4e1db25be7188879c6b1cef547848f2bdcfcf9b7ae91e96f3db2df1913fc762a8a603ff93e49c7e4e6f1409d3dc1411aa5cae5443ed00262a8663d0fff00a61d50cba2ac2649736eed65f9e7e2cccf524ccf457ffd2e711dcff6dc1be359b0e438744b86e878afc32eaaf682feb8836992d1ed96a82e0ad0f5ce1626ccf77e1ab22ba0415e7a7c904a8f50da3fed29742cb514232c09ad1ea0331ee9a0a86d6ef8f5add412ac72d9edec789ea29205c10cacf30f8e64608f0526a7ef40d8881e5574e0b9ff901531d5775acb62723907d017577c1b4e69e99732f0259c7ba83f05171569aab5aa9c07c1651b50e8a8b3613d6adb5d1b6ee140518c8eaa67db363380110b800d2a9dbc5bd6c00e7b4959324f8ec0f2d0120af3c9911d2baa73364dabadc5fe98f966aae778071241c490e9e6849437e823f26be056bd0645bc12e295ff815a51da781815a510f011b52c7c668254dec3758320bc2589a592736406490a011e98f436cc4ba5a17c339b01ca3efccc9d3bffc13cc728346379584a039f1cfe3bc398d8656594cb047531c88e3c2e9823231a7b99a21759ddc45a32281895bd76c9116217187e732dfe4bcb2af7cea752d3093f280910b6ad87d2913ecb5d0858ea0c992e48d7c3464db784ea1faa3af72ab5f62a8c6b1c650bff86befa082b93b0db5511da40bc2111491429e9ffe10bbf6fb0b40efb70586018fdf029e2ed4f54dcbe823f4a41d80d071ab02af39cc8acd483ea542ca7424dc87201f90600d14cf875262d0fbffe45aa3cc8023221da757e721fab7d6a7442b85408e8745ba944594e187766c8bb3be7cef555efa9dda234cf8b46a0eaf4b754baef308a2e6252b02983a41229201218780d7d43c40a8ca847f6d9c520e322198dc62ae3361bb5997ef53de33fcd404338e4715a70ae68bc8a54b874c46407b609ec9afc61317a98c82fedace33d737c5bfc39811098b3b50cdb9719390227aa12cdc318ec5c6c3a0f3bafe4dd3a65f249019ec4bebacd44be8107e01f4620a62402943fd6347dbd040f053f76faa76f3f256174dafaeb6d6e5b2521f3e20fb5c325f28de444e4e6b5346a44131b68d0d6d4329ee2ce47181d8c97704f3e18213e8e01e125984e653a59b663b92586d1e8602e2fc12d830a0ea68f72d2e852fa3aad506ef36d98c1942604dd31414535b7d5f4784824d599a2db5a14544589a438eab16db0794053bf25d4410201d4cb5509bc6ed430651200c14f668f1fa8f215dbf8a65321063dc3791d68bf719c2308767c0340718a73f8772439b09bfa7b24d86e6db0efe8ee43b53e6da0ad0c1ce8d51dd7b3ac3e0988d66b5f8b1ad3805c102a20443e1eb8fa5017234fd54ef79f0a0c5fc8324e6329fc445f41f2ac95385de81cfceb75b25171a67f22cb2df50c8678ba191c28da3b45e3d51f945287aec1c896950eba491180c2242631f23f7b8f55cd2cd46726427b2b1a4d634bfa00828587259d7a5e4640938942a81ae301da8a488a002643d699d3c0c9e869273537b612af0a8a8925e40d4a1016a6b9b48a46d1079e9d4c4182bcdfcb7d4c253d836d0bd367f7c152ebc828e18eedc09", "6aac0000ac5100", 1, -1066185786, 1537743641, "bfe5fcb55a268208ce28467f53b8d61e665faf450d8d7a456d09202aad76dd95"], + ["030000807082c40303429059edc86aab8801774ea13423303f89dfc8e42d04cf0cd2ae0b20e78cd4620300000009526351000051630051ffffffff667622f929d9123ba734c959b7311c6d0839f2d4f683eea416452580033d70910100000003ac515270b0653d3254ba1a160a6470559eff998aa002f72c86994a33d9aadaf7bf57b5f99025320100000008006a520053515352f094b96203aa59130000000000086551516a53536365991d35030000000006536353ac63aca364f20100000000026a000000000000000000020000000000000000dc83140300000000c2e5f9751ff4755a3c503877a0f3b6a742ef3023c3eb13b705eb70393d7ec5b480827acc7abe6da1c97d156c3183b20b32e9de7693edf148c15326a52b76d721cddc64ed86f319519db168714f6fd5148fb43303c4872224c92368c0b8e2e58100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000113342a58784540ee2b195d1cba33a18811dc8880454e24d81866bbd7dbd74ed82a06acfa9a7bf138359ed9cbc05ef446a631b33dffec4424fc937f44ab188daa0350fee46c9f0cb90fbfab34024bff4278275086b8222abf7ba065a647fa3f0cc1d6e5bd0d0364cd00c4c43c1273b9cd487601e9f69b6a4e95f70d4de3487d4032f4eb9727285fd7a551d99d25cb21335d5308e91102f3e1bb37e9ef2924bb6f603045d5474088c4561da6fff2fe2756f8af78abd580dc822444efd9f1d87f8edf40a028fd10fb3872714bd89c821617f3896a5aebc382fa1b58b672e622c87623628545312458d34e4c4960054cf14d2832aef3947d22d52e727017737dde287a688030bb23fd26aa57e54754a0a3f00df39f47da5fbd268ae4834013c8239b6a0fc7a0319757258a6f5b04cc47f33d3195750b252df0a0bec924ffb383343c1d65c2d32031feaa824c5092bc0d2af893aa9a2a303cf90c8dad466c3eb535c9eb689ecc03702000fdc2030b6388e09caac9cd746aded3bfd30595897a6e55421297cf8bb45c8021c8ca7aa9ef2edb850ec546b6465935a8fb1512025cb0a99ece5e0ed95d05c944d534c991d3bec62937bfddf94b2a6d119ac7a605df9890d6bb18de0a6dd8fa9710a540fd235aebf719037e919e5719cd0531d5bbc73bb6e90885182f7d3db9ccecafe77f9a89f6b39561f414060f4af1b74cf999527e2c82030ea327fc3ab197a00ac696a474f40c0511b4a1d4ee8918ee0db0d8df287ca9e79c73affd0ea65cc1c454017fdf8d1fb9902a459a584b3019b4b23dd27d1ed5dac00df4b869a6a223921d3e0d7be80c1275850586536a49e1eded99945f81eef016f45f5ec797a56386ffccf0e6697f6e11a93b9ec1bae2cea50dd7deade0116aca3d63febc16f96fd9777f0669266e799f2d22c29872732ec41369e003c600adbfb9a036a3778195fb5cc7d67f61024d225740504849a5265cf5ecfc278166baa56832141ad549aedfd7c8d4a450b985613d7a959d76284dc4c7d16f1d116e27a07be44c18b6f0aa34d846f748045c93d29b57f0de5de31987f7f08e4462f14927aa4d97b04b405d56a7b5ea724fecaefb81473530968bb266370aff8e6b575f414e966a12205d7de2c5deda19a437df95fd73693afd8bccf13efd0d57fe74d33de48317d50a7fcc3741060d8b7fde57798fe12d75728f3c5a726143cb0b3a4cc4ed8b15c28dda507f88dfd897caef8cec651697abc59702dfe80c051e8dd1be37b244076aa759a3177103e83a10b76a0f4cb4c216fbe7329575cc245bca9e0841aa4e0e785e8f4e69bf779c8556d5c730b5db843643558991b481746ed8ddd67cafa7cb2ffef5f520b96d956286f702e1c02bf14cdcd7fccf76c9fff1def65060a2520b8b892c40d61dffa319dc3fd3b1ebb90f6ef1e4fd7acf15d601978f7509a02b00b7b35950c3fe706d90869d772557b456f6ec0656de3511071e691ea73be2c15eb07899adc431f2bef9ba211f8a8122b500a9a68f50dd09523a1852ea1c75c650214f778030bb2b133f685aab9097c5c53006b7e81f9da56eb861cde2295ce47b4d2863f0834bf59118a3309d9685ed87b6dc49f02b55e6cce8b6c36341d67dff0ddc95ef705b9715837b87f0649bc214874c2fca35de37c610132f55a3b1469d940490ead8ad83e5fe7df0644e31e49901ea410a87cd0d80eb3de1177792f94bf06921659be93bcb17c5cd5ddf55829891c33f540b237fb8e8e4da074e9f4c3afb2efc7604e9b2feb350a6f9971cdd97efe173d733f7de9f46d7c06b3b4842f7784fe9b2761c6d11443c090ce4a4644025a609d9cdd62d506078887ab78b2793be6b3700c64ac1b30e224c11490442a35d762cd8c17976068b790073c9b0b897f0af0c3fb2420149137e2903e0c9947dec860b72797b894ffacf525031ab9774d796f841e62e8b7754ad5e1e3538a9c6af8a1a6c820797c937ced6d814cbd167bcda662481fdafddbd3ecbd37f52a7ab13d6fd8112d094d09f70181e1640f98c798954723624d09b769e9dd454db313682f578ca2d69df4662a513c99bb8ee8560c43c5da9f1bf8e9075d72fe17e7d953db385082e1f90298c5bf276b60170c900cc44440213f67dc0173d20dbb1db50544eb51f9e4486f0050b6e1179fc3cefc4703c52c37845bd4e6a4b31dcc0516cd15623a0034f7113d6f961c19eafab3fb0603930ee16e87e549fbd92a8e7d1fc1a5adf68d9c7c6d58f0b7efcdd847e36df24534802b77ba5862a145a622a039ae708309190000000000000000d5cd0e0100000000a5e4ca6db45c9b7fd3f7ef72feb17c7e8e54a9f6b27bfbd893fa18591ed9d5328dc6daea0a6fbb470ac1589540dd194e0392ea75f2d7cd0d7d1c207c967586b58638ed137c90641e087a561e477c5a7261c938d0c5e90ba6548c6e9e6436a59200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b2bc2a61ff6e0d5e8d1bf575ba843ede6585d56a2436240358d539a68d341f2a3c3f7e0233986e5da488051b7120469d972dabeec0d2e84c0daa5491ef11cb6c80768a1fbb5f9e94cb0d2d6e5f8bd063d50e7df0602b2f4e1a24de3105e9491fc7827cb2d0189572f8ea145b785975c37f0d1d2cfaf0712d02e1b69b2232f1b9032422caa810ea9f17bbd5324cb2f48d379554355efbbe13b4e734ae57f16e5999020856da825d1111b73d5653f25484c7e560bb9cf47e387c0b63b1fab206f1ed1e0a053b4ea246ec67e390af9a405fb869c28eafc3482cf948a44c242024f60b4a1c9a7adb84bf48de599caec3ce1c504c779c58169f98ec6053de5efb0e99ae1728031b799a5c6784f3bc2b6ffe789d128030ad64aedf666d65186c2dffc64441c146032ddecab3e0b7eef4244ce4bf2f2c665093e38b70ccc9e9e77dec34ff3f3a12bb02014f05fa3efedcc5c10a9f46ed0a9e82f97c6f7ef012117525471e86270f836c020e445914d2dca9f82b873e16ed0f8b774b127cfbacce8255c3df5a3c503d3622022c7a26e317c99d80d1df157a3bb9f4fd95c1c9d8a5360d5d6fa82e026dd4b9beebe9d3550e0ba23734bb876ec8caaef0448cecf18435e7f629f86092de6c53828c4a9742a2457980029dcbc51dcb6556e75670167be8c7baf52d381d63c637a94d2d01517919e04a4f600627c5056fe8a343435c5816afea33ddbd1c58a19c1f73d5154516c4f8b86669c107c644218657ec6fad81bff1de433e625bab7fa6cbfd3f896a7f638db8d13f1c5e460c649f70c496151e58b417f0cff2e56e1d245e7a76d4d1db9ade4a58ae5c69e616c664893ab5e2c16fbde163b7dd874650b91a5166c9fc4c35aeedeb57f2342ab6df4494e1b8ae59f037eb6566aed473204339c571778c332330c01243a9dda596e21f1f37c7a859d42c809ff8e32a444cffd5e437c41873512ecf856a8cc2aee4aa811e0c0d45029c817b5fb224b73346013c20564e92803428f750f4c1574747c111d35e11dcb98ec075672fea7e0a504db4ccde622c7575456b7f78e64f9e8d9aebe616954689ae8c43b0c322ad83301f36a29474c9373fb1b297f8d63cf3a2b9bbeac3aba09a9a86b74b594dc627c744ddb4c334cd18b3304d0b7987b3ccbfb628186ec352bd53d2f5125cb75f080b987f636682c90ad879f4eecc3e1c52377315434a1fe60b434f0fd7f52bdfbfe6d3bedaf86b730f2104a0c8a146d591cc7434ec6948df10abc39ed8fdbd85fc57d2c1de8a7350d41f05256e9967297a7463ff40c2107b973d9d1b7eb651fb2c63aed4cc5f2c7caa8ebf685e753a342da11bc66776f56ee20c56bc79e402f3a2dfec9ee03c6872fa6415504a1d447c00828b41b36cff1edd8c52efacf59eb5600a6746049fcbc8cf016990b3331b16e9cbdbe8940987c017e7681a0a8e3866ea9bd29222dc2fec6fc3178c85c0157c10d3f0d44b2491c2a004b0f63a41d937d77c44f5261747930b039b6f4a38f3ab9bbe620f50eebc2e54d3c0e49570c493ea1e453e6fa7c344b8f4cdc8cb777968e3aad61cb7420f57b1c2e14182e1963d5322b5a097f65b7390eef2fbc14cc92e2924736dd60b9ae5790205240b7a93a785aa724c6b1b1d68cee6e4f59e2c847d4c8877a436ccd3e43381a7c89b8c678b9d87e149b9fc2d20f2c81ff1db255a376848bdb94eacb785956d34d2a89dc9ba03218b2425f7649e7fb643bb25823d77127df4ebefd0d03e750e023bbd3b5f7c29f3f9b726156f0249861c224cf79fa5cf87323ff00323c390aa5be9d5144b1afa8aae672aacac1f5af9a1bc2085790ee5ef0676dbcc0093e4359c42b6ab183e75878dd33f714bc88d2ecdaa94c378224bd40a896d7bdefe46149903cce3b73b1761394a3a41876bc002d775152587754778e5e1beeeed729d10020c55961705cd48e28bfb3760e44d57704c9a17e1519645373e12cebbe1ea57e9f1394e1e5cfdfd41e021ed5d081789f05f92b60fc97f3352049fa2950a44ce088fb795869232ca022d34c7e9668a2a4b10d6ed296ebf3d90ccc74e0e11717327809e46daa28db120fe7369e9c5f5782228b475851a329e9989751ef9a436fcf030a435399a89da6043cb5a2183e10732092085d68f473207bdc0fdef6d31238ef1e51b2bcf47d61211e5762111bb87be361b5654d680a632d6d62c5e61f81577a4b359570a651a5e9aab243a0743f2d0b83600f8fa81178abca3b1fdbec86e15b82c5c2cc992eea8c4b7da9aed6170557197e33cf9d4fe643d6a2c8545f8489153551c1f51bab338027efed4af2f848862c02c5661593a77c63b7f0dcc18e22e3c91afee5a009de7b132e79720814559981a11a41da596aa120b40232894093f09979a6ad1b71fa457924ddc912ba7183484fca98be66be01f3e0d", "6a536aac5263", 1, -700187145, 1537743641, "f37e713dbd68fb0dbc3b7a371e1e32557dfb28bda58a674d278b1d0f4137b8b9"], + ["d7ff0827013cc62dc605cf7538fccec3be79ecbbdc5304e624a37d25cc5aefdca0c07c9da402000000036aac523f71f3e401870dad030000000006516352516a650000000002f1ee3f000000000000000000000000005486b3b69642eb3a6740f54574d18223125d8c4fadcc5b118762df216ea8b4ed0a9e5a06a37ca26cfa7d11537bcaa7c64faa83c8ff00fcbcc42f7df835845b91766d0b2c5e405581a664d1f7d7ccb25ecb33a777679e94f433f8f180a370eab400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e9d148a3f3c1ecae94a9e7a58bf0359239a3cbe521612724460754ab6d5a27c5ebdb2857dd04f1f2f5c181117107b934bbb25013217806a00ce2dd99827d6c5a90782ef71d892a70bb2f20d02de35c0cf7109c5acfb1538b38bf29b4c4da77ab5515b3fe89e0c73a28cc656a5efe316133066e6ad19f6e4e9f1c1895aba032e1032a7505ae341e56842fde7bb0714a19df375aaf8fdc4f714ab29723fbb16af634030730cb37572c094aa099e37a7068cc20e6207953244f302ee4336e5549e34c140a04aff3d3417ab2a19f43bea6ea03e9d1d5267428d0db861d5124ad5b2e7a6716af3373b6e5659354a30abf7bdd2da51bc29ddf1429b9ac93e64a9f4d4f1404100300120ebc523d8069bcb4f9a7b6c95852444cd356d42bec954c957207d5496f65031dcb00b6378c728c31f2a9a536dc19f6eb8df12c27d76590f45c005c844a96df020bd02a113133e86d92658411a75815d215b826cdd278bcd3e672a0b5dd0c2c890301c49ee6936e7ba5c32ea29a34520f8726bcb2fc09d886dcffb6abad247e96260211765cfeea0666a79d53a3eb9e280dd8bde806f1200e73632b22201cfa00248a18d05e609696c48b1f22d4b8f76f870c427505c71a718cdc8da58b6531c636ae9d9e6777c195ea8c66bae1ba282340feb352881ae29e50fa26985bc1777e1e4fd4d02952f03926f94c5a1f71a436fba3560eb302abbf902364c5daa88aacb65a959f297dace0a52063baa82828524a4cd14adb09a358a07829f1e46c0b94ffd66841f70c269fd5a516ae7dea2dfaa8d58104095c18893cd58b648ddf201867843a1fbb3033dc064c832098af0950a01472b46a22e144b2b5d46cd72d947ac3e5c0ea64251115d56cc20211260e21af879398ea67aba6ecadded2ae8462e4803f22952cf3a87b4dd2acb9bc862edbb0ccafcc18217d915fd9cbbcf2758ada37379d8c7136157eb7e8b9cc94c7eea6f3d5fa708428c524d3e99a6c1bc52ebbcfa4a92e9ded20c671efd3397a120d6398cb4d888fb1d5a0bde2e3b0b6b73bbb29718d5ad75581205d8995468197da70c8a0cea7700146afe8067a14889ced751b0fd326b64db530c5a8d2893c8bfc15889fcede4a92a662b173e4af0dbeeddc89cdd204073d43109858fa636f296b9ea763e127bfd8cc05a3ef1bf73a82c3827cab397f6f43eb436a4731ade28d854c4c83365f35bd95eab2f46d768f343d84a290f3c9ce6b024a8f2fd57dea4a567275f5d1689cc2e067d6785b968409a358fa193665aaec6c7b2f20c9c12a86bea75f2f93605c4a622324c4497dca79207f6cc47f96ff319a27f2e3bcaa39362126cfdcdaba1636e8bc1a7bc447a6873ef1ceea7b7c71b75ea1fa1601f6fe48f464b44bd684f66ca1c211cd67e2181b0dfc61c04feedbdeb8097a024d0785a3d81fd61e46b56d6987a56a93a624a068fc1f03e96fe348f98ee1f9aaba827d836f3d77bb8fd9d543f781c450029c5696503e4757869b6b98a2897c38ef42cde6bf4cd84b0d9a95c73154652e9932e35a3db5d8aef81a9f927273b546c13d2bd82e595fc5d3c411a6b83898af9dd0a7e093524c48dc06c47dc2657611a8b677733f636c705294980402aeef045e04b985e19ca25b4080ad61de689273f3f1afc6674b82b83196e8be9218d8d9061a07f55482db0bb2f587ad7f751f644067fd3b056f5ff7434a8f2fde063865a47ce963b98110b90dcd8ac178187e51bef2b9b41b5d6180ffc963902f3886e8a171b9fe986f83c35c358afe9775f685f470c0897cfe65074737c38f7810512b3682be1fec7c4b5f87cb5f657ac0f541d95733a0160d8eb68e90a245fc025f974c0c6df8c50cf222b03e0a728d9bcdc02e5c236da6c45cd85ff2c007249cfcdac90cdc18cb8dfc4ee89ca3bf161e37adc31498f0c4265264d0bc8f56aed5133270e93a4ed53cf9247d2e6eeb3e7f8c1d721e8d950ecda8fd978527b601a6935536462e70c26291a15ec05119124308df611fbb07f2b3d21569c7e8895ab5d068ab283756e76412abf1c0843361bc5f00881ce83ed5a9f92be15b9f42e684147c0a88e5b61d5f4f4c3641f51c8dae1db931b9fdef6a3458a1777359b547cae63616fb06964d5294065626d23300c0f19ccd8c63ff775242e71d6d44b971be352879e3f83b1fb858a6392f6db526616bb11709a3af8688c7d96ecb71919987f9ad24e9ec7f4b9ed28b76545b17af1532d3631fae988b6e02ba6389d959c316099b124a13c68a931b321848a3254a76885c8b00b6f12d3bdffd5134662d4003000000000000000000000000ec9437123ba462328c6de302c3c4dd931afd4154049eb6d107213997937bb03030a0d2e0832a95cfc93f67ddeb1fe903f888930cf47810340c26cdc1ce1d29637a5e76077e5ce52a8e194453cae4cad6231afcfa9361adb637ff9fdf9c83321100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dd635fe0b4ef10e2e06a332609412c53b055d308b5bdf79d175a864b7961704e84ff210cc23cf37ab001e1e0f6b2023c6ee769d4013951a3c75ca9eda33431ee7d86bfd818955b4f61a3752b4d376b81c63d71e5a38de87891d8c64329b3d406b1405ec6dcb5d1702b246e1221a5ee20b2a37efaa97f2426edc886c34d57a3000214c925b1e8c17fe4e81925015ac0ee73866a9b4bce9e2f6db3f9bab6bb96506a02084b3cb5389c23f1c9190a848b988715642d1ae0b0efbf9213286ed5c2701c630a0101bbf1ed4a89c7694a0c6d8d11f58929d1e0f0f8fe1be513b2b7d6468c62f22998836df086556591459607e976c30b3091c05d5a6a48da0a3dde038dc35ccf030b30989a84d7a3e6a50d7ca9c2d39fc7708dab8c49f3e4c86e44c89dc0f6ccb303029299e1b98b09b68ebed05782a82815a20f0e0d4ebb8734c68b8c4551eac68a0220de5aff4a637d56f764bf7dc73fad96faf6218f13778f97d1eeae89ef33941f0219a36105365f80dfe71e5be8f8e3e5f3d9c5008a4f067fe16d72130195f3b3b9031e53e43672e7c069ed777d2d788853f189d999b11d44b9b431dccb0bde8d519869f1c972869735df6747c171b0b61686ab55bcd1902d46d265cabac5ac1a543eefe8fb4674bdb5683b9755b1378706ce0b57a8b9eb3ac58c3d049cd3ddee569517665f5730c31c25fef3757078e5d47d293c63e02a656a66e6b20525a370ef8043ce6c00fc29ae66c51a6d07ad8fbaf87bc585640a643b7c2896666328f0d00e5c7285e70cdac3c6dad73855eae1e332a571f4b75302a31428897d0fe7997b15dfc5cc80280d21b0a986e8ccd9dd4b25e592953dde985582fcaaa6cff9b2432eb6c26ea1605b796dd3430a3edfe48f7bbce4c47703e7880bac318389016c1a6f8584b2306b605618e221a78486d55c7e6743ec80fbb41242dc57f5b3152187eeb57f3e2d6c91cb101f7021c44b741fa90b8a48bcc50e41420c18ad31df8f9b4612125380980f371100f2d32677e3aed9f11fc7c85ed3787211cd06cb4968a86abcd18b481aff9598b0bac29d074d29425e21c3cc1e77a14a3695d0cbec7c3f69856a5ab89cbaa7bf734005f3540a5b8cef88d7ad216789de072cf291fea8eeff98f842c805b7e1d8df22429d410a32e0028a710ebe5d98a3d1bb9730890e76900e4d044b144a0a07afd53bcc245874d89f3dc1bc9aea25e79e61e14be9f7c70e654ceabf01e38d4817058d22cebfefa8da7588f47dbf9b0708ebec8090ebf9bb59aaec83db5b2865be2d7b708e20f79665b056e4e995eccf5f969e8c77a08c8c0addd054eba568398e4bc9008a872f3cd8d372d86f979bd9055433e3b9b51e3b15a6540b21f6f938c7e86ac98b45625a028383f541ad76f42dcbc360c03deaf7e2b3b795d385edce14c7eb7ee56c3a802635717fbc1128a606236c064761102b1d3e8a6857a5d2d844e8eac79e5109d111c442b4a0551b9e0ad6a9242865165137b83ee5c41da5053b8501a2dc4a3c27a9564fcfe4f8d439ff35d5099164b266ef90271912716519e08d60cfa8b9f36e00215a26511c5b17c5e9358c21c03ba0ebd0bb20b98585eea2d75fe2746ef7fbd92f5ab1cd14d1ba9e2dc18a67473ef0b1ef6bb3521f19e9943827eac22de133d5c3b4c962475b1f09c0f0206dbaf6ada2ff7ea77456e336fdec9378b8b40f6cbc61210101c27c2ae96f993beec612d9466cc06f8ee50615e1b09bfecd81b23ddffb5d37130e70b670a099d8d9409d155f46f34aeaac19deb1159269c0dd95a5bfb31cc0aaaeb46d260ae47fc3499b636b3b8916c4709806ed8492d51c058c742b2b06414ed8d6c24860b7eb502b7b9900f112b9f66c13f334d8ca4f17799f44f86feba2318b1eff0ed3a5dec91ca9ad83a5fb5643893e5c9b72b398a898a2147ca6c69b27fba356ce52fc93cdbbb59bc4807d9c7140d14f55fdeb3da20fabd42fd4ab50fffa1da7b5200219fd92798e3320ba0fbcf05241bdb485e7ed0481657f560d16bdcf6864b63a1ab093f0cd125f16b2deb1d9abf884a4e50125a14bbde133d8b77b26f196065e364197c71da98f49ba1f725be6cf4da023c9864951cf3df984b2600635eaa119f77ccc32676f69fcf907a2e416c2d527fbb45d58d6234642d2ada5e89a3b0d7ffbd6d8ecce51ca51fd78ebcf3d12788472a05350d21ea9e17f2616defa30355cd04a3c2d28e0cc9aaeb9bf71208da96fa2973b5ba5aa5afcc66ca634de31b7daa5fbb479a2065da01888673b949246911e559e7fa657c0c865bf9ae9d5e58ee3a6f5fd5a92752c76e1f6c45d300d2b512683bcfa9042c2b5e9fd4829f1332fdf871f2edee989602d91838752c9888edca054d281d3f523f7d1286468636e03507edc547088dd7d6283e807b54983e90e78f47ec0bfaeb704", "00515163", 0, 57299556, 1537743641, "415586efe52df9e53f53ecd83d1dc8656b2ff18e905a5a30973fccb233dd04fb"], + ["c9843e08037a65994b56b7f32dbd2f843d055259c2a59506952d5883795b1b618fd353f21b000000000600ac536a636affffffff7eb0df6da0d2a82f4453948f4c244d1d6f6b0747cd60b67b2e7487650534e77602000000026363ffffffffa162c27043b8d0480e785f4cb0882af01d56d42ac69a11bed9350936d420e121020000000653516aac52634dcaabd3049daa0800000000000953526a52650053535297c4c10300000000076a515251630053c4393b04000000000952526365525365636aeea6a5030000000007516565656a52527ea0ec080100000000000000003aa3f00300000000b1d64a726f8fa31a88845a4f0f744d697c9585669546760914c07d8d4d74cff367f43a92c4d81d188fe5a047ff2ef29e70202aae34814048eaa8ffd0189b3a3c1423f917232a78969c340eec305e5ea4e224f54544b5460a9770af994987ce4400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000229f13521ea632dcdb25b93dc41605e04eb4d9bbf3206fd244d3e9034d9629abff4747bb244553f43f86d9324519449b48abeb877913a4f3f53fab60666f33981803357e51e83f7c2e1653f2d016a1596c56a86c785d9065d5420c2ac71704a9646ee3a848e928986e685f2ccba0689dbd4a7805374350289225a963aa1cf4ab030c210a3559f89b37968d3c07706c22ae7767e576c9dc8587a9fccbb295aebf99022a7613f62e602d4a59f7e7958c67bb6d182452071a2d062e84c4da45d182d1830b06b0e63647cf6975f8f51ab16bc75809ecf7779b206e002de621d1b41dcafdf78b5b299b7d4e997329663ba7ccea8389c9e0f1d36efb5fe91b5952cfc0bd2f22031b643ab23ba3b3f1a45b9c1d4bdc6da937865af972653eb579ce0b209ae4f8b1032e943cf9376044ff17be8291ada36dad9420fc6cdf9c9934c9dea7cc683f3127021f0298dc5fe7d5c0b15bc69e1182dc6f1cbaded078df1d5a98863991764e7a7b0226fd5043391409709c15b051e7a3382547830e2510ebf243d680c8696f2c4c41022503930f47c1398ed984fa4a0e4c54da5ffbfb2822e7a0564035e8c7081b954123e85a82037e9b31f127afe62c0bbafc72f5a9a0fed3708939fb6bf5346ec6f703cf81e4ea2b1fbbe9f9547506da6d03cd27b808bd758e981949d6f0e347734537eb83ac94f927cab24e68d15e528cacabd943780244e1b6f0bcb6a7756a4938e95c05b151e62c7feb2a3e1c35863f73c9593d0a889705c008b404c7fc7f7e30441e68f910679f9c90fac7103d5481d02da8dcc8992f78f8a71537fcf328632e00fcf07d51adcbde0a98c9ef52c5a0e387959a335930bbbfed50698745ab45d37ed0835a67d806fd659405f29ad9c788690456560e02f10a4755c7f28743379f91ff7d230d7863e2d398301f2c62dce9eb674c61b248c4e6486c2f1925c60b850f89b082bfd148853ee9b271175504e2cdb48e7308f723c2428f7985ee5dc583f49c6aa25b622dce637cb29b20f3c713bb904e0856709b7f37d7c75bf859138149eba5483ca178a668ad7c9a7098ee09f5c8aac6e776ce0ece82152fd2325ef465441623b3af2aa4816d5aa2f436dddd07773ed6250c62e84755a16974953aceb348c2518c31969ccba8d241d290d6b8b61d479a4501585e2fa55f7451d8bbff54d69716673d9c8f90eb1493d86e70827118ba14dc18d045968e4a574f0ac3211be15960a106dd8b30c45d344b45ca95517820d2020e460f595b20ba2e1db4b8c028b85b45630ba311b89fa90033e0ece49818f5c58c73a57d5b4c453fbe8283cf037c4864a0cdb7b05ae2e51abfbb728e0e48bec846c3d639f4b907b9706d74a7ccb2a08810a2eaeebe84bbd4fd9b7251d50137984b68915b15067b9411458102f326bce1473e0ff8b01d92af2fb8b499157ff3d28e3c8c99ac4562e99504819bbe798dccf34bdf236bc2a91c39a0d8f18f33b4effdd2553104ef26ac656537c800051a170f5fffb24a82e929994f0db36682e7c7263e1a9ff49719dc162d56f4a2dfa6a080ecfb3b0f9e30c541975b122a43858e8bc3b754f0bda6b327e013d710a50e962b9685a9d57f482b50e2e037fda0bb49b48e804009ef48521b6d95dfdfa4084dca265b4989f582986d28474d8bc25e26301189373b77434ae32982c5da7819c0b78703c50fb347a2df2fbbd46168958368eaddc590a8bafbe9151a37f9415ad4c55c63895e7213475b3dc35c6bb45296a071be726ad7f2ef5b5904e5dc8136c3a0487d577e4be00a83d3726962e4db15d03c54ef7f1cecec0c84052547755897299918e2f28a4d84bad78015fbd8ac0c6933ec6969f3615635b645c6b79b79f4d3723dc4d65e719dcc9a30c434bf03f3f72b7f7b127af711a9e7e21b82d3369ff1fce90693cdd92d3134a44664890457b68472275bd49bf4ddb1e5da8b3abb0f27d0dd7f3f87f97666b201e2b618fabf895ab73249c6c174154b13fe9023d516beb52ff355c9041333142435042939b2e4f5d824770ae52d0ea6f88da372ab25d0b57edc75f906cbd694cc21188533143d5cf09279301e00b5884de193154251753017e061d3597eff2ee396e12054f2b59c9d5a7b6041914c57aa42682c2896122f05d80156b003ffb1c2a6a098113b1b18f4f396558f58c135faf2b71387d91010a7b04586a59b4ad57e4e7f73eb5875bbb4a4ce5f04f1a43b1d9e7258acffd7aec7416febff03c965724cf1221378fe34da71c4f9c0c4c39183e5c4d84feea72248027a88c7c2c610085a2c5d2de52a1a11a8105dc3819a3a36f4eb53f86c471311d9f82b38432bd0ee20de60685b54f571001bf4b85be798c34081f70207d4451672dcf546b4dfd44ee6cde2c62c1a7a40390f1ef715a78ce8bdd7389859fe67027e6a67a3aa34b6e8bd0b", "52635365acac53", 0, -1950102242, 0, "b51d7d00700ad62a5d74dd03e3941cf0ac980110e0d48b6cdc1560f81ec397d4"], + ["1fd16d0804a90716067b57b6c9540081646b0766b375ac0ee9cef059bbada2548b0ce5471403000000015322c69c7fa5437e6bfc6819f5076ec5913e2eb0a1cdbb4a5d8c423066c020891c9554fddc0200000002ac51ffffffff99624771ffa4ff7380e90608756a05072d5b2a7a2f8fc7b563d5b19379c0cbaf00000000025152fca2632b6835a7d86aeb677e3085f9595fc2f8b24857c6d07cc92890ad35205859c77f9802000000076365520051ac6361b9a92101be241c01000000000651526a52ac516ed65b0803ace66b030000000000000000000000002bca0229e6dcfd7d7ee5ef2bab02dd4f4755ee409e1fbd0c4c4f3bc6d7511c5835f8c534d722d1a9873f9ef7fde2b16869ac33ee3733f966097b1b4f2820a40041a6060be7fde3052faaa3c204ee7f530d10562da8d370525ad73503017bfcf700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050ac06ca2510be7eca65284aa3e063e80179c34f461d0cde7ebef6c80c05c14d0f87807a5554880d73eca867d89bb4ddb95dfdf9c1bf6c58b0f1c55119263aa5e540577bf59847e8a11a37353641c4206482d12b424b55cf558c47901c696cac71ee7229a3f056035b57e0dbfb050fdbf333ab1e75627ab50ab3840c8a5f75d020dc56764e95eb366b9e36a826c76552e32ab493bb72a5007b2738f094c6cffdb032d377ff7c0919b05a681320a687670c23966be9f5aa653f4c8fe0e5bac9965cb0b075b129f057ff93b7c28aa85d70b03c51d6f382d6299bb64d2d8af676c8092eab921e648dc122ac73f7c26cc9173924736e528945bcecc537643ee52c840cffb02044071088562b9d93e75f08a9dd7d659b519c7898974a03d678a58bb3f4c1c2a0328d3633dc16c93101dc40be3bef46bdc25b2d2070df5059139c7f565936e058b02007b4bca0396a3d1c76ea000ae440fb57e580a69e7136fe9a5e97c5084600bfe02191c86f9772a687c636159b38c03f457c3f82d8461b460861bc7204ceb727eb6020e2d04582e631943bce08f8c7ada72cf180c6806d9cf1fae1e908e82e61944a4d3e4f97ee31d550949a58742f9da4f45ca01665849bb465eb47f4d70af42d9e9e00efc8f1386d61b3dec6bea76d24a4211e0cd3b32d073d602584a307a3eb930916930a52626706f232db147b4180a1953acfaa3d58b1e09bb6f4153d3d373f0dff23b6a8f84645ff25e20b0fd6bbe33bf2ff8035f5511b63500451a02c45e8325621bc4b1e8a02474ff24feae47b2cd1ad10992f3e9ef9579086e3a5ab2715e19e027434446a7c74b5e1cb725f99c27af18426cd0153d5dea2ceb2df31f956235e6c1cdf4d90c6e47935168581d599ae208fed535010e24999e5530bf53aa93d92c009fab1f5b66a9a97704e6af5fcc897e9cc3ce07672157853f9a35404990c3760923019c7b1aba6feeca3b34d24ea90e7479d829736987567837ca7ff8cc0306af90cb05315762170058ed5e5a1430114db9150af01ab95aea198976e1c600b11294312ceb6251a9db8535810d124c780c089f9b59c3da40f5d89fdcbbb8bd7e494c9ab7242cf6e5e0cf50d3cbb040aa3fd1ba3b24e4897600119c2ba1843f52281c4da9e05a931e433c8a03ff492b3131dde2fb4d52976e55d640297991a1fae2b4576190092a855a3a66dde91423f921354645bb9005a4c324489a10868bb9de168071a40af2bbef40bd15c626bf577e0bf9a3e0f154319825c0f030339953cf923fec633afcd1ad78d6965ba5001bf1fb6f1e6d84c7e50963d9457c66ddb8e976f3044dece856df44f19bdef204d873c826b4cb03e23bc8881b20332a1bfd0313f3c9c50fb966930f4c405ffdba22ad0bc0c9067071de66462716bf79b55bf59fe9e7b6bfe094ad4ff984a3fdf4344a693a47801f9ea6aca0394dbd4c9758a4529f51fc451e69438b70a491183a1591dcc5477aa186067a99ba08f568645eba3bc226708cb5e30cf3ff042a2bbb23971712603bc27907aab75931d393f70183e6b1b858a265afe91f202cda1ee1c27e62a6a9efd026315fbe2cd45885f4aeab7d5242b9539322b66669264710cdd3f29a6bd744b52c3307bfbf814cdefcc90e3b5640f2b0e705aee9dba8da439b732488c1669ea187f3fb239a4ebe5c730c8f15a8ff30a2e0530fb1be8cb56b0e94d491bbbdc2f2ccbc1c69139c0e7f525ec615b5dadadf625ed38e78496932a635eb298e4d93fa9c0e905d84f67fb9bf13c7532bffd07dd119bfa150bd0891dde6809f818c0204c07524e869693d76a140c803ee82c756657630a6b843c7ece364a2a3c968982a2e2d4569eb4aacb5beedfb9061fe19cc2b7536cd9993f2fe0ce2d5d4e421505e3dd7712a04f96e0a5a772bc6f6e376881582b4a51350b70659ae238378beef452fda1e1569ff85ea0231ab4d33a6931aa2e6b7eaee6da93e0be93225d745749e34fd34d9b723e78d1adfcbf90bec165f4ddf3343468e7c7ea9ae7743b192bb3a9399c4d530125b8ae8d087a8c7cd3d12b02eef93282ecee36ff92559d2f1e6ccdea69827cd6fb75ee6b57d17a5742ccb30682edfa6db66aab5008ad30314873a88e29f1f2a3f40fa1c5a664512e6fbc1d5da7cbe049e0b468824eebe31316884cb3b6b3586c811895bb3d6bed840518ff6cdbab7f4478599b1fef05f453ba947bc04f7f0c55368d2dd9cefc95f97400758a6bb8e95b59dc4beb73242d2e0b8364036f838b8975b486510119d468cdd1a7f93ad150aec7c1e662b00000000000000000d1ddb0200000000ba68f13a517a10ae42394076a444d854a00f8a7a4b85f1d5cb13085f73185e5897c6130ab60f9fc5caeb2a2b2945a944a14a2846a157c383bbd89c80cdd295f992719e4b4bb89a84ad514e25463b9965fbe623ed6fc3bb56c86704e94d666c3a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cb21bc44e6b31ab61ca593e62cffefc4fd7c90150135cbeeeb2e8e5225424615dd55327cda6134acbc126ad3c917b586d0d972d4746e989b53e87c9278f878d2e71b852de9d073d22b3f4fe91043bb952ee96c8be07577f6c723225f8eaf507cbf3ccf56775589c22178f6c0c829b46cab8d89f666338093a665b563ac19766b0319c56c1cd39802378c6f6a0cd286dcdd221246b05dae36b9a31433f88100a2e603181911937e929d96e0d538a32a8cf60885f18e940c19e34136f7fcc186c510530b05362f92998733c9188c40a9d0f2e835972120d8d5a862b4163d79374b4d1ac861cfd8cf9e9fb53a9f465c2267cfe5cdf12f8a78ea9db8f3bee87413f12d07d4032be44923993fa4b5b1d93c6128cf10608bd583e9aa027a910be0f62fe3bd511b021714fab19939b1398128f35cd6fb995f69e3692946b0fa7433ab362096fd6c880201f9fe33d8c56969dcc2798ebf012e0fd8c9a0e33abab2793da499d2c69df723022d9b2761c99d171ce06e001bc3e9f7753e7dc57d8d8e33035b4cd90dafbc050703165aba754f9b8da81c28fb3b5a4c8649605016afced967f141f68e8b08ea64a9916af555de2166a31be53bcae0fde958be8586c3378d221dbe48afce83c17e30829f528d81671ae93dca4f02cd981a8dc1897253e3c062092423aa3c38696a3ebd664e25f0dec3baed472a1f1e3f5e3240e08400dfef1ea9337b8e3713e7a8e8bf89dd242c4cb699a19000db57b95bc4256eafd34daf4b478776c8781666b1c75b805e7475405545202f927bcb3c01f9023d05402fe1bd85e31f4a9767bf2041c3d83275f5484ce135ba96beee560b1a4e13599cb174841b28e2f739e070ab7f61f787c484f6d25d3d2593c02b99fd5612d625c717a7fdd0d184aaad0012ca1b2327a5af3eed1692f05df67373ae16cf8e727f1bc679faf8f4752e2bda8e226791e7066563712c3e7f026a9031a993e88372ac3aa87bc4555b112f97c28d9ef052334ddf34eebcf904ce51f43f750c74f796a8adf251006afcbb13150fb8aaf1b77e5268edf3d7659d5dd261763e9f89cc4d7388c8770cbaeb3035f96cb5ab86cab0978ebac9969e086242632e0f7cd1c7b07e6e13db27c2a986b08594466a4c8728c3e647c67e8703da1764f117826047c9d3753ad05d72557ab24514e5c5a35de35f33cf461542eb22b90178966312f7f4464bff4527894265ba083a8c7e6f5bbbc3db98eee6d61c2d20ea9b8db44d5022223c2be8178d7584a8b3cd56e1b6b81fb837b9cbcf523838aa05deb146e18f081b50de044f028dd807047a226ad631e671899b47037111a82536ca7f7f62b6e0aca753b13e7f2365082c9a00b249e99303e04b06d6acea86f7e05aa0824d4e631ced0b8665a0d95032483b3817b8b15c7b0d84c12aa4467d771a3a4c2ae751494dd6110938743073963c10188541a43f3ae9c376b6393bcec3612b5c4498c8e3ef1f1e62ff34b09f433208f098cfb89e0740137651d75e0b64ae0cd5b6f291ceb34b95d9664ab01cb416f74d44a8437b6aed0bd179808d3e20a4c90e2195733ad884e6f20f59328c3a68f1a06e91b309adfdb6bc78dc53a8b054a6b67974c4774614ae4f19903b05aa49c91ad38e53e07c041a1b21d24307f9bd58ff88ea37588dcc2818b1bedccd958834426a2907b4e34748a858ef6d5c225433abd7c57ff3728ea11362336870be92d71fa8717db34d17e8b6c89a0742cd7d978c0612215669fddf57afb375d9449b9e11c2ecd68e9c2ad18f0944a7198e841dba1c3c52cffa5060f261c634625b13afba67fcfa09a41526914cc5fd0164483b2b5220e2ede2fc0f4e9204554ce4644d6b7cbda801ae8b84a43872b51b98b9bc596b684ff65698a3220328c93d3664b5d08130068698bb01f10d8327bf639f66822e0017cc1868afb95241c81af04d4c5d992a4d1edc94e1f6b2e237e81d777611f541bd8bb708d0626da4109c00008db002b8dc0c689fdf17dcc7a80be3aaab23800d082f049f97a63e69c3533b7c0b6df92bada2232fe49c78030fb89f2b56ed0d009a174ee3a97f76976b8ed1e9f76b7c4288eb91329a2ce202d7948bd413a483bd778080539f96cc6317a5b0569aeaf84d9cd772bc528ee29f3fe501252fef2e5163a4170927d6d8af2e8a9bf8035ef33d57416c524cc7c50c4da219469455fc43a4c24c6df6a3a8b111b568909f4ebd2603e610c7d93beae358ea121f041dee158f17df252f8eca16ac15cf6f7f33d5af8d1d3479606b4c36245b5b27f1baaad83d371da46e01000000000000000000000000be9d237721d3249050ba6fd25c329fc8f8788d8caa52420721062a8088ab538e9d3023fe172b737cbce8af1bc2d542b42beea00f9254dfb71062004eca25d6bd86d88be8e1333d76fde3bac22873bb084a1320f888989c987ce0c297677d155800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000538b90eb3a32009e818753da686ecf93bbcfbcb1eeb06afe1178c1818b9a763b5ac50b299e6e8869e5e3dd551fc830f81a008dfa22209f5322258ffcaba35cc1b31655ce9ee75aae05cd8fede70472f2e473fa1268bc55012b8d00ae41a98f2b687b88674af3b8938dc700bee0b065db72c3a5c939994f982b98c386b1c5a2a7021681b1b76148b7204afdd2d0cc6e61592999ec2dd5ab284d615699f91cee88da021df01133f5f7101f945033483809d97d07061e47912629c7cce25fc82f390dbb0a0767d0617adad0816b1b2d8d08f9c4b0124ff57bc064bfc943e698050251f5e6a13a048942f67666472269293d2457b7018ac9695113f2f69bfbb363da07a15802255761cbd3880628f0e17d95bb0cccf916930fb1a33b83e7d160c94ac06a2ad00326ffea99b65b6797112593115e19d84097f97ccbe537707b39984384d5a9e2d6030d8ad63a78f3c9e7db1d402ed481abdd4dcc6ad91122cff83a5a098a6eb369b003233bfc3eaa084397b12143885ad653c07677bcc3c7bcd9b561956da9fb64d1050220b31b550f14bc4f6a771edfd5066c56f4cd6fe145c969ca9f9c1cbc9eb37a56f280282042e268d144329a81d3076e8c080bafeca5d5b429dd8fc3857c10aefb70a01052337c0b32c07ffd9ebe24973637b0bb7854c7b98e9102d9d1b74c6a3f354d742c7042a532834ffd9eac4644071b70e9df1fe1fdf0b0a6e52f3b06f5d81ddb5c69944ddc808a1d27751036bc6588b2098e23656ea2d9bdc7e039e355143cacf7779dcaabcd25307b36708706adb866409f9b0871d9e51709a2db33792c87a66c25fa07999bd350d91a51a2cd6bda21629cd7da7b235c41bcbfdfbbfcad002fcc60186db77c786bfee2cd2d55f3116f8958cb4e38cefeebc2b13e5e9a1318b54b6789bcad1e803ec5c077784bcf36812a64bd4dcd99ac94e19a74035da2a72e3972c412797bf5cea742b79bfa7b5aeee342e832ff3ff023c2c2a9075f2c29ccd00a3b39e2c831bf4031547cd4e7eeb0cddb151fae21efd0cf6bfc85ef48f7919c286beed8f4ea24ec3fc8555b77748bf3c9cd789d7bf25e9fcfeb33549882b6a1ba6fbecf66b51360446d0d6e3122fff7ead181dfe77b15ae7e211ea60fa469c86e6379b9c2db2e8ae1080e9d9ce38365fb24d5432f79ccd0306b7f2cd170dc3fdac79168344af511420cc13624ad3d5554d80d8b855b9e21678c6dc45d0ea97f66d202afef1aa0500b8f9925b4bdc59b916ddee44b4cac2782a47c31300217377a35c56e93afd0004b42de73f90e5230a1aab877098c6162a55c5604611fe51ba97c3dba9f6deac5205a295f02d813e02d42711f9804d0dea3f0022e0d9f0a2978056f02a4551cece2296568138680cfc9ba4a963c5b1abbce1fb2346320b431b30c9d78f61319ae2a6971370adbe24b2c0b8b39f1a59d8ff95cccb3035459793896b35260064dbba56b927e7ade74ae1a813d53fa547bbdd07dd8592642b229544f662c7421a50f8213ca49cac371cef43eba32b55ee36a5f4fe495384bd6b3f501f5ad717ac1416d931af08a888371bbaed0df3ccdd6315c3174b8c5fe4dc6a591c88c740456026a75cb00aa3defbfeaa2b28c5e901762290017c13da443e1a933859e639ff7099663fb800c28d8c9871c73c4cf73e401b6016f750b09abc1798b61223f6fa66883877a027e1550cdda6aff5eb6f18e263045ce38cc81ec49dde6acecb0e52a6b41ac1fd74a9a2eb8d2ec3fd8e18906e53bdfc6e182d024026d8a7b6105602af9c362521ceea85f86d16207452f3688131f1b57490fa3cc55d8f30079a58d03e952ed1e7c0cc0c63bc3f4f53bac37ff50bb81ff4ac5a71bf3c54574e6b9890f9c98f874f8a82ca8756d0e73e60f8d5047096d6efdae5a0775117ca014a0c9767cbbc0466afacd89428d74f68a7c4896b4215af9a2c37b391e594a8c3fb0810a66d4dbd44d549dd916561cea11f3d0b6e3e789c0ee4c97ec5ecc4170c1c95c573aca95cd203936f7f9cf81122d10b6b48042594cac88297f43a9a41a10a8af7c44822de3c7907a00d35a9adc95944e3a9b10891a74fb34408eb62b3df86f69ebbaf344feb4a94e5fa4a60d590db476d8acc17b6c277d90605e17b06107a25568517b2bdac2c270a5707298c6b2164d8b01b029e894cf2aa7d2ffc9f723cf3bfb0d8bbddb9fd90f684b40849170c67624dab1e6fc74d233db5bf54309ff715819b76393c9520862bcf9a52919d67c3b3087976db83c15d276dfdac07e4f8bca7cea56e89d3d5db29a0aa5ee0bfbd9cad171f43244a07b95fb7bba174371637d5fdeeac694a57776627b0c46d20054a99cd7d88e73903486fe3067a9eb466224fc74977791d34d48f9f4a8344d661f8f03b30d4c0528d8863ff351c642ece2f6a1250e0f6fbfea2302", "6353536a630052ac51", 0, 1915258398, 1537743641, "cdbafb9f10bf6a377826e6d615be05e345e0b96185fcb33f9b4071c829871203"], + ["e9cbda2804102360780b3e41cf7a3573620c06c482ff5422acc8c4a862055bb8f5ce1811e10300000006536351535252ffffffff130b1e24ac59e9cf5ddc502cad2e05c2f5f028f44a2fda2cf0d8b7874d8757020200000004516a5163ffffffff45ffb3f7149b385eca8c913a4e769c1af3af8fc403cf237543beb8154abd05f40200000005526aac0065707f7599e770e998d5f3e638bb63d1d4fcf99e11d02b47548e5400a7e2706e05461ed5c1020000000163e9658d7804c4053801000000000353635142dc620100000000046a5352631d1687010000000006656552acacac9081c6020000000001630000000002c47f1301000000000000000000000000968556cfa845744e746d7e8d000ad78795ad242f2c4e52103d13dc00c2fe9f4e0690f29a69c0708b08c03f8f95ee9440d76317d739fcda6c0f56f57ab7c2de81569060a2782dd2465fdb1fd94ab5d03a8fd8eeafde4e563e52188e2e01cc564200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ef5bea3bb1d3fe2943b25f2cdd846c61a03f177019781ef3aeb8fc94c15891893a6a2c5cffef739cc850143354e4ed90813b8c7d9c33a6ea4e73efa62f385074bfc4e0a8b280ddd4429317c8a37ecd466fb4aa0a6fde71c8388e4caf81ce03be5f5a54ee341613e4f2e7b6d93b65f7ae6b6ecb5a6ee14deee5fa980692d7525a031e8ee1a2787c5f8a320de1f8c4653e4bf92929930bfa19ef129f813bf350a0d3032debcee6e3f2b8c4fc4b7eece81ac5e31fb2285c9eeed9a58e0e9dd5d98348c70b015d44bdb0a5ed9d2cd709e49eceee5e50319188aada38a389b44edf7791ecff21d65e8044a3fc4bc9a85b36aa8a97ba2c5d48729b97207aab43ae2194de37870208e8ed35185d90c87c0a153ccd3efac563e6877207eef8ee7e157b67080bcb78021b2f4fea196643a33c1b0db6d6c48e46255cfc028968036ee579669a07ae9df00324f777829e80df5c1a04be606865e8dd9ce873fcdb51b01808e90dea88d8b23b022cf39a5f42cb59b187e87321388c96f63ae22b11989427952deb8fc1ed74d279031614d5f3f2591269dc05eaa75e7156b8d1e5b34570fa16911e5caecf9f356aa66f0abec1755f33a2e29742af58c80128eb9f818ec88a6c10dc14d396c516b8bfe4a1a373cb0c73c61293284d601d3180b8830c13eb9835d7026992c60f10df80fb5ca32632517c0d450db4991aa4566bd453cb06e13d08a142587be07374b3bfc145ea02d78a1de68fde40c2fc4ab69098ea881383ebc806ca89bec90c05dd2f446aa3b9327236cbbe1f5918cee8d13e95acc4d0d0c2964cd5fbd71b847e52d388359fc48577abc402aeb3859fe848b635711247e77369ea46d42cf8623fd533cc4a12d11c376a53d79e1fe683ddaa53751e8abc22c0f37663ae8d16bda4b487714f2efa9035c7baf1ac878a1c12bcdfe0492cbf321a4ab5ab49d6739d26bc90a19f85389ec4e86df2e81909a2a225dadc5018ac32026b399ff2bba9ab72236123b97e4bdf5b6d76fed04e37358810196466577f92796147d9b6a7e5da3419548f41390ec82b451b090fdd2d73cd16b368f6a5591b078528aed249d5ee5a81c2fba609ddfbd219f03e3f3be03faa85e9e31c8b59699bc2df19da36d936ab95f69ee29dcbd96b03fe8b9e434da01128a94d7bf7656b70fb2b49e06d381ea1e1f753f03c8a9f93bd85861e902d9eba470bd3addcd714f03f11fef39b55f74c529a734b8112dc1577efb8c613bd73f2b9438499f8a9da21b9b3f89f52c01411d083e7dfa85f535370746fe14b5919618a40ee702d7b17e4220d16eec43552717a4282040441a5a9daa7577b8156c1742b17c9e468857c9788c7888c3da9a4ff45b16149051416579a49aefadd2182d6fcd97d10f263700c4fd9b5eed2754f541c841fcc2d16995a9de34f6c180dfba109c6c63f86fd3c1ae964dcc61204f1a3ffb68a3a0c81f308e6dded3a59d19ebb15b0fc8366b9f82871ba9d4c6fcb162e5d0f0af5ff0404eddd9bf16eb01d6f5eeb516a3d4ff99da1fdddab759399052ff042ba45275c49e03f8f5ef2de17bb46d178d97d8fe3955c231ffc64dd3fd2464b8562c66d02127f95853a086776a1201958fd58482a9d1d2700c50b7cc02c0d13945c4265ad0ee9aa760fe3f4438c34378786e4d368194827d574ea9d64a704e72c36fc4de240c3384f3be8d4516706a701a049414c49a0c6464d7426ea16b03bdbe65baa13679eb452a9d5d40d5c7ce83e4dbd7f23dc900642b6f0689c9ff88f30979b7352c91c0d4cc18a0acbb593e76501254e7899f76f59d37aaa2f7197ad1f65a1df0d2f0c3aff328279078c2f71f3347961416489d47284b18a411c757252365c3266894dc38092d939b06c848942b0651bebac9debf979fecd2ba679d26feeef9b8c503c412a5f3af12d452cb0a7f8132589fa49bc69727ab3a36b520c0962a820a2d1a994907c2348f4641f1ddd4ffe9df3553188a61af4fddc9550976a6d187a7d95f413e8984d1efd449b89af981caa94b50cec1cdf0f748f69c414fc5c21d897c6dfebae8edffa9d3d32998a6da1b2126cdd927085e8cbdba78613a5fed6cc58d97b185a1ad95df2dfccdc548f16dd45b8c05548687c014c0395a30eacbad0e7789744144f7577c512ad7d4c21f4eed81f78274d240142763aac1a330a250aef104da27bdd70c6349137d154be36d4ef889519cc9a2ac0525e0cfb2ae0187bcbf6df021622cd8748fa74d92568b158edfd3eb3e92af6599fa652e563cf1eabd4e37d6c154b83cc167f64c0d998c4000000000000000072c27004000000007ebe22362fdc578c79c535752791c4696c702bc52ef9701c63e54fbc8ace16f23197e4346e8daecbe3d2d8f7d404da795800cbb3b0997b0c645678466e5d71fe0668e469f173edf712ffac46a1303982c77da83de486185ef9a9783dabfeca1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d454c5fc04c87c3cb044c370a51236b2c6c36c7706e482ecccc5532bca492768b63ef641fd3fa43be2ce7522ccf7405c3dd8e9fe02edeb5da0442a7aa51fd9aed517d00599521f79bbe5b99ef2a8e1d6f5e4ca12cab63a8be358eeac06e4d6194e3a78c5ac8c27b7c58e8743d3e07fb391571bbae2164236ed764a845a086ad3021a9d0675c4176d2a4a69a2b31ce5e720f8c983f467886ca4e982ff421e667e4f03164e90951b7ddc76804e9d9bddaccfd046adbc6e86ed5f96271d794cdb89df0c0a0541007a2a188c466d22e524bef606f1b6bc6efee90596bb1ac6b3efe5dd80eb5b95947540d7d7ffd7ecf583bd77d86f036e88a2618aa3878b8d733b68a7d0a10303c6319ef51976bf4ff21f0200fde25d7237c2f58f83799e3b77d883625b9a5d021b6ee39a26187bf60c42d6d43b91b727f64f80f5024a44a08955bc0e9a74a30f030ae11d3d70af65af781348d4d95f863eca2a06ef2a43bc3ca1b95059cdbcd95b0303c6aca005e274bf7069bf34f780514758a0854d9d9fd0f405691fa85957f6a002168f27a27c2ae8b1f2de71094cc7e2eefee700792039b876b3a415c76e11bb6103dd21fb08d28a849dfc4f624ee34641d77b392e641b786e60019b18324be85f2bc56d2b7b1940ad886cb3526b958773e2945ae41cec16eaaa62c0eb2dc04942c4096b0e7da94df88cfcba7591c0bf5452b4f02b0a2abdfbee94d9eefc0c8c1193d5e10a0f39f008b4c19074d9a8f83a5340c62fee04b803c660e7cda4a226594b098e0e70638248eedb6ce16ed96c0c485b72a6fb934dc8aac30ed32ef222af56484a8a0bce2ee52fa82a1c90512c26cbc6d73676843d768eac0043e7d85f24ec7b9ec3f28fbac2e988b45789fb7c4ef32af7fecefbfafc69b9a08da4186f763e86cbbbfcc8b43837e03d4211ef842b4456827dfc07af9eb1173c0ffb7847f50254a1cd0873bbf373a3eda7760eed7cd24e696a90c5509add197f17b1ff2e18a19eb3ad6b481b69815f9a4045911aede43a0ce46837cd5379d6c31c959ff01ec255ee1790805f7fa74a4b5b120a7be6ddb43157704658f96437dc91d4408e8881eea4747bd4a49cffb57fb70a9afd13405ab29a86aa7cf3d6d863a3672b9fa277f9d37e5ce5a8cbd2d336a49ebe56aaa2a54fbfbf9418ca38e6081b854fc86dde6b1b98f53204697b9cb811c2318b298bdf0f39d803338fa3e65093579fdbfc32ec8e77599921f79be8a137f10a33f51ee0c5a94ff15be24bd52c0317c23170b602cd119017059d57aea66f98d755702ee0bf0e838e9684454691366feea9db180484d00ef19f03716ba2a855ef6a3899c1d3cafbb5c868dbcc4157d6a2430918c5abd6cad184625abd8edec9a38e843503eb3e985042f26e8166b06e792affe53d5ac9e58fb5fa4c58b3eddbca45efb055ba53b9912095ce209a4ba34301e2b0fcf6ba70f7a57059e6993df5cd57789241b89d56d2fe9ee8e9f6ab0f0a8b4937cd583569e06a11f6a7602c71655640c6ae8838e1d64bc972321790976c7ae4a7502cbcb37a6b343e12ed81c60c6de7e29727e9c9e3ba48fb06bb05679ba140a0d9588e66350b0c519c792af251bc2979be75fe90f962e704c45366cc2d77efd36e98faa5c1140b6a02186f0a7927bba2f51a341592dc61b68fa4102d0457a119eccc8bd5cd7810f8b2a68c7ee9d02efb3a39a8703d00386a4bb0616a6780683414f16b8640f634a6b4cdcb6f9a905eceb89aa717fb414d581481f21be8ba59778e737491f1de1dd41dbadddc73467e36ecdbc6f5b0622be5b8f908350ad697f987a4019b6bbfa0581cafbdc1ff27d301da8edaa513cb64dc81d0eb1325924a0766c851d44cfdeeacd65ce69af747d2fa078ee310ee1132eb848912ceb3b69489945589b7909c9b49119d4e84f1b3bf0de4007c75d133f9691a510609d50e52e13da73a655e217bc4dccf0d05c4f5fa796d718214046dab9142462c93cfa34073cd47864cbaae3c38d5af7218a67b77f737e6468b8cf16dd8e85cc8d5b8b858a8c31be9bb2c46a45fc9430b71052a586c00e5538f1fd994115a530910ea773e63839857fddec9af968182add9d027f06acaf2afbd5f7e4cad2584221fc27d9fb2abc45eeb6bb5e5813ab16f8c5d440766e9b89e7a96d729ed465366c33f29134c09bae501edc270377cc2a9054a851c1bef8314c00a30d08cd836ae9d4f14998689338a23a7102904e50164352766822cb762400af8bd80c8e56b1153d402dfbbab07ee93479bfdfae5791399422603b569093483fa2d716d2dd37b134da51a01bc6347fe11545ca6adf56dd8f11ba0a881ba370a533706148c50c1bcbd303e9af4d23e38b6b55cb9824fbcd985ed65b4bd66be93953aee2c195007f653364b8aa8e38e8f9280699c3e63226f8362ac1a0e", "0053526a", 2, -339457146, 1537743641, "da55f3fc4acf4f27b7d3621ec4056668ed61498625a2b3956d4fb224a9b66ab7"], + ["030000807082c40303b954e3134a453518bd20fca247bcafee09e5e4fc39f420ad24b9cac4350ceb56010000000030c644aa8778b59375ef7baafeb61424f45798de1fce20f38ca4fd1fed3e0dc8a4a5ca5303000000050063516aace4558bfa112b2a53f2f985107144fcd25591b5113a482e602701aa4c5d1cfc5c66930134020000000163491dc9190148221401000000000752ac63ac6a63537a20b13f2ce7d50300", "656565530053516a00", 2, -1557445573, 1537743641, "08ee2133abfe0e19a101862010b565d687ac2efba3b9d4a5fe1537324024ee59"], + ["1fd24e16010eca22163d91c16072d67d94242b9d2ccc078d680be74e2507c014b1b39e98c20200000000b42dd6aa0335753d0000000000035300630ee17c05000000000565515151007c06440300000000056a515251530000000000", "6552ac636363516500", 0, -1923103214, 0, "5b647b3f6d808317db98e8e196f412d8e6e7ffe9a64e8edca61ef8bf70505c72"], + ["030000807082c403025c02a4abf512fd81f429c237cdcab1751487e08595ee99624a7e3a50ce4e9cc80300000005655353ac5103cd1e8195174d6ef9727504ccd28b57feeac829f28e3aea57b0e8c239f0ed041643fd0b0300000007ac520000ac6aacffffffff03984f85000000000000bba0a101000000000365636342949701000000000400526365000000000000000000", "515200006a52535363", 1, 851486860, 0, "33667a8512d72bcc163409215fad62c8ba2b28e75256e7565cea98d962aaf726"], + ["15b084620216db3abeb6491eb0ba04a4bf42d3122afb4a0fc129a1677ec0ee37e09a1d94810300000003655152ffffffff557bec0af90686c74ab9fdfc10e39adb374a8b1359ebfb204d8cb98ffb3b5bb50000000006ac535152525115c0d8bd018de85601000000000465630000afb03991010000000000000000d81a900300000000694c0c460f4999aef56c639655b198d1bb386fdd03d9586949d9da415ded0d3e1a57e61d8562cfaff1374c81b0be0494a9ce008504619808c0fd316bb5e770a48f882cc9ecd7ac329e53833c52813a1ac1201ae85fff905ab655e6e38723ff61000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001205b52921052d08d336927367adf343456eb8d07c9696789d76e2337c306783f7dc6c5274f278de5f00f45dc7ec1c16f62f5cc885bca6c64c886ab07839996fe1e3d41d0d5d4b6cb56e63b40f7d394b7d0b0ff2b35f2b55763c4702ad3f894be247ba654ec400aab18ecda7a4e665c0dbb100e9c107fe0c7cff1cb6ec1f7ad40200ff3212ae9f5a19e014557fdb91ad0fc23d2306bd81c811c46390a04733682a032486a89a5a10ecee36c797fb1b83d82b5bae311457f27ca818ccbb12ed3b59fe0a0064e3b94b1847d0428ac0f3f10bb8d118a73cc21eae16e4cd45cc284c83b62934dfe6dceec959fc855c28417db74adf8bcbee21559ece098f882363f69e95c50326e45ae5ae0a5efae88674b189fd502c8bf19ff9442ad39ddce0ef78e4c9718e0318254faca4d9dbcfe6bdced720ba13c4b74a0e837d2737d4f707138e1de606a803170cffb701996f27c107196d4a006e48f17bbef92f9a244c611b3306cec1e7cf0213617687e4dc06ae8d60677c04a26a83bdf487f0c484d698075963c576b6b59f032242fa330bfb644223b8577b1bdb9794f8d3e0e0d1557c0693372b10ef07ee62215a48d6aa9dc358f5082282f9a41dff22ae336f9368b41373dc480a69dbd0e07d9607e3b2ea1adc9ca62d56d5a474d6fadeea002aa3347a5e7081f5cfc8b0bee12697921e40cfc5b5db3856e01d886826ed400106cfd5c3fb48e9fb6c3c7057709c801948ed9b6a054b3621eb591f3cfd62c826df3743dc14604249ab022d0db46fd7458dfa3329bd1413655507b14910292dcf07bb143113b6b5eeb2d6b82bc2ca54d890e09da74d8da2fcaedba620a56c9d8f0de1ee975417908bedafbfa717a87ca6dcab40c21df15ccf5e671c044d5d060bd91852337c0814e166f3e90b2cf4ca5fc66e257846eac0551e1496e38f75d3cbf6038fbcd276432000f3043ba81baaffb9266b580ea59b3ea97628267d897e75a626014208bd053a1163088c9a82eba80d0e3d5158442bb3a6f0552153ca0b7a52c6c3c9bc8f6b1937e2cadf9587e842deb937611cc187d6e47055f531c477a607cc4b13343647947a290f2ea9ec071143ce243e1386e14dd9ae45a90f7e804444beb04ab211ffbfcbe6d8376cccd24c022817d95793a88510fbd46eca096b48db13f6e34c0d12989ccefd94c0c9c6b8936a4fbcbec00c5db7bc11c68ee8563600f784f031e03f455382302c86021505a8a6475b185b70d6fc3d15152fc951edb2453b954720b8ec5d246c947dfdb6a5cb9fa1f1a347d477deae430a488a675c50be6e3fdc8c185a82070e5deee906585fd413cd745d8400b1b5e4cb84756db0220d0c38e71cade9add70a8f68b78a14e2687ed757b797b976a7700d307aeefcbbffbe5ec37bb3fb00f16185840be9ea4dd2cea52ee3dfad63be0b406e7cb0fed377f100a96684b68ee1b3c8601e226bd44ce70900a21d923beda131fd347bdc4834351815009e61ac5e4451a22e29be6a40713fcfb12173ba635bf426f753b57d6f2566a85e7e85b7a2af0af0af15a94d0e2889330d045a9e23ad690d7a6ef8563fc14a32f6258d9fff74704bd449e2152a37dfe3c707156041203bb755f6cdec9c38c561270d0065b2b88b5d21c22b26f96b828cd412244b8cd4e539251dedd1f1a50e67a47121076efb9784c25a1d0929f77b0b9fc6e93175a43554541694c7e427af233b341d1a4ac1b75f44737c62b2a181b8f9e86d566af2c8bd54a73243e20f287c7be732de076d51c0859688f10f473b32f9bf992d533c7f6de4d96f8014fe0ec29aa801bdb585c28bf7846e80904074036551005ab685168804e13b90c0c7b01c84c8249302cad1bec0516f7370791ed8c0d6d771891c4441089dfb6bca9cd4c0bcbab89b91c65dc9f8c9da609e1b3b021ec441e12e7c5ecf05a311d7adb1e072906143f93897b41e9d7a4a7b36034802e333c40c06dd7388047e96f35249c4e82c01e0d390faa5b54c8fed9bebff7d64ba8b12033dbaebd678553e58924d721001dc05ac1d9bf39eda9233566591cdb6a57cf4e00ab3f4409905eb18520f49ec9c47dd6e9129961bcfe1e72e1077551852b69a2a772de62876c56e823764dc405180aec6e5bd072020346ef025d1dce7a4bed20c6f68b2f4d1f74667c0c1a170838d66461ae70d19fc583711635bbff61bd3a031539cc83a202886ac810da0bc3e5647013e6919ed5a7a2a800cf6ef159baa5ec9eb74704f3bd04d9e6155ddeff7472bf64e79a34e3a722fd9998909566c83d0e7c98847924e60badd03d87be871a4c37fae1e8e54f848c598139c3df5ef037ed825200199885e9d61d7f0ba398dccd10d0585bedba988765236704ac7383ff5796a568fa7f30ac053f94d8314118038d02a900e6a297364d80c2dcefb6dfb83d21d5a7bcf0c", "00ac5365526a52", 1, -660328878, 0, "77d0a9185d2df8b2ebedd9f4a832d8407f7b5b36a712b12d51d3c5183d985e72"], + ["ad100770049c107f6015b3b720897faf24a669e3e759c86368ac3fe671ba394f87b6ab037c020000000663525163ac518fabbbb2461d834a94af95fba08327d97b3e735ec5f66aa0964301d95ee376c772d99c200000000000ffffffff551c0cceda66f079a303b54cd57acf22eb2a0f62e1fc0faf42dac20223641934030000000365655350edd031f90f63a3a191fd88aa09febefb743fa7b64cc37ece8ec5435335ebf635dbf042010000000700526565636352e398efe60271f29f000000000007ac63ac51acac529a8e7a040000000005ac6a00ac522b9803ca01fa920e0500000000000000000000000063c5520273808d6d34295a9fa76ce541d1c26e45f68719613f5a087f789ace64f7c4163c995c51747791aafd1c3d9474445cab1feeb21a2922d09f7dcc56909db110b5fdaffa09861f81a47cbced9a94809e458e463658ef653f7515a9d6aa0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f96f3c6f843495195a9bcf83d78071438d4d5a755560cdb62188d6fd0cd1777ec8e6ae44faae51a5d20921cb35194a7c6f3ef6d014a562f52bb3763c632be431f312161de2baf9aa6d6aec8d98db6219ab20a3c653a40458188255ed559c55533ff66657105d886391330772ecdca5cfff681c67cc132cec4ee5f57511bc67303133ba6b89d803e33cdbf59f9412f219c3f4ff2ebf436b97fd2150315439984c50302dbf2401e67ac700d6c5151a12a5c242e287a54653480cdb87aeb0f9aabdf930a056c67ef2bb86069cb4ea80ce6ea3ac9928f7247b6ed3cdf8498564592ee2b6ff12933d982d503329f2fa010e3d080b4a871c562f3cc6e582b153de31ad54f85032acface9d8580c51a42b44bbf314b88804bc33b3a190544070e72ffec3a9a1cf032671e1892b07e46910c0dc547a0cbd0d48f00a725fcd53527ecfc1b318bc31ca021e5396e5ff272997675e784a6175a9a06771bacd8586b5da07ec9291d0a6c5be030d5e79bfadfda63a10956c0c6b28d3a0389205c3074ac3c554f1c6eefdc9e519030280a74a023ad591da85c528f6eeee568282ad8b55525c51b7fe5f532c30762b5c29c707034e69b36702f10d820afd577baaa9b8a8cd6421754fe8d7aea1d15b43528acf5081e3f3da2f194ea60eda0ac33e847a60e8425a7bfba6aa43fa74e1a62270e44b8d445a63dd2b5878353b2b9decc184277dada68a9b07fc90ea61d4d986e1718fc731b1d9aff589ee7ac5c2b9f1fa7f8a06192d114a894f216552243816a6b8cc7c4168c3d84790663eb639b39886b65a12ed361e2e662c5da7818679f8ba0a49985fcf2e8b322be8c71a9b71604c48e177cd83b0fa3345dd574c9908296a22cec7fe987f3769c69828cdcca9d91cd9dc80229f5acc9c5927b41122f924386f4b3fb3a6f9a8c190a2bdaa8c2c2125f764822d45d3b98199ff90b193e2a595a89251425d6e194b03a3f6a2145e834df31c365b316ce7954d7ceebd080d649dfd46550ccae4e456c31a35575e164a6c8b98a4691e1d12cf06d0f8235db9b17ac11efc787e0d30b90f67e1d2925d663d8c43a61336b351ae225fa64b23c70d9f633f4d04b0849bb58799e4735e4248d2c3c6a9e3b019c86f3999ad0ace6957d2afcd3f5c83936133b7129328eba34eb59bfefe35543475687f3809dbe373273921734fc218e22828e574c2207a1b3e46fe61e34841db2112cdca18406d775717b362b952024fdafe82b06251e9d25b6e1fee359a6709048615b1803ee62f075a834ca5d1c98f2eedac262cacbeae331a35604bdb0541a50f5574ad474993d8cec748a03025b05d0c3b6854d3bb996cd9d829056f33340f8b8139649f249f522d3e061144116471167e1d0c9a6cecfdc938fbf69d79b6c784c31639ca83586988bf5198cb2c5eb5c0f26ae4e7bf7c27df8a2fe9e58982a5acab9350fb0b19037612f448f7af82bf265df6eb006a853211123325e6b08ea891076218a80e52cf31bdee118462faf0741a21f6e2917600b6b6cba2cc01d20c8dc0fafd27cb7194d400b93ae681fd923703c70bb67fb229c93879545184dbdb3c0cf27b99292ab0f138f366fbeb35fd0475128a104234d5c15cf0d81cfc51be90dc6a752d461a16ab94f154a0290189db0a2c4ee453db5929db85cb5f9403950fe0a0abda73f615e643932091d79b0c869d7e0874d1661452c6f7b0ed7fc768874ff3d1916761c120a2e6a26382e21e420951c3777125a5f543dbfa1966850e75660f0d16e0676a2af3e42e6858ddaaaff132ffa90fb329c67edd3633521c2355394d99350794ea74fd7ca21230d7c8c1960cf4ca8d7266204f109b3ed94c678917307f6dbd72ffa0105e3dfd537c4025670d3cf2fcedf1bee1d9cd2565755f0457e7eb37715efa06c080da98f92ade65411415becc77f3b40f62be89eb0d578d7c1fc4ad63708599d30be531eebe5885a82823f5691889b4bfca751b6befe9151494999786b81fb92cd548793c6a093127ac093444edd0fef79c40fcc258850ce9cb7f149e223026e7d43b1ff11800535e578ef7fe7713a458ee753eea07d5fcf455102743faa9ccac276831fa5c3060babfffa269f87c7264d2e63e0fd5e14bc501426581968abf231c10e68cc342c7135fede859b31d6826a6a3714f122e0501c48a8c5c7c9e17c0c9b4db53c6f0173213f4fe3147d9a430006b9fd8e8cf33d9d49d6a2e63fdb312f775b42da205deecc54b15ef8083ea1b760459e0924b66452e4422c6856bc6c00abccefce10b912c897675910faddaa1c5c59b40537f5305344c0c20c29ff1003627271a269b807079917097a67dfe8a46f5a91772e277c6531bbe36e22926a8ae56b4de19d58bf1f69288179091963a7900809222539faec6574b1110f3f9a239d7956fcdbc01a148d08bf2a20d", "006353", 0, 156952389, 1537743641, "5954be524ce6ea89f4e57e97355f2acab2bf4985a5e231c766f933242e94937d"], + ["b92a0c4b02ce0295bdd5d73447414b8312b7f4cbad9d1e2f510feb53a6d46b0d12e483c53f030000000752ac536a6a5163ffffffff90796a2a2af288b889ef471653c78dfc4b9893409ba950c29effa6079cd0592e02000000026a522232cc12039280a7000000000009ac525363ac65ac53536f277404000000000700516aac63636ae9f9bd0200000000026aac2e689e2800", "53525152ac", 1, 181524863, 1537743641, "970a22c22cce5d4e4aef03bfbabd36739d6d00bd420cabbf43c5df41fe402c73"], + ["030000807082c4030458a8239b5d5ddfe08fe402a2e6c5e6d4006e54a42dc5b8b54dd43738e0b93344030000000300ac6532749e568cf1e3c10ce7ca85442ae668ef0e964fea104c794b183021fd1eb8d03236a75d01000000076aac535265536affffffffde037e397b406d356bd571d452bd70071a853d4a87345da6c5d841d86f19781903000000035100536b67ba3c0466cd166a54578b1ff1ecaf27fb7029ffc7a7cda3353132063e1af183de992e03000000096551006552516a6351ffffffff0495d16c0100000000045365536a259db2030000000001520357b40000000000009d607005000000000100e0bf162dce63fe8100", "63535265", 3, -1904990253, 1537743641, "c3c9335b3326af073e5caab6fff8aefc8f8febe8dfaf823c6affc20ea9eab77c"], + ["9664bd65025689192b5a71209ee33be4543c5ace71617871f588bb92ec0434dd025ad403c2010000000265635b3bacdec99011243f6dc568cfba6f815e1b1bdda63436ac3972a10f032831e95291419f0200000009635153acac515100005f29fd20037a45c102000000000007561c0300000000025152706af4050000000009536a6352635152535100000000030000000000000000c9d79803000000007297df232c1594a272c3be610cf845548e1eaea7d5c33955e58e74079cf0907655cd49acfe9787cb1a86f84f67b307cda54ddbec3650872e75818ad49f87a1688d46d8f9dace0bdcc7a76192ce2a0bca6276c8fa5bcdb9f6e0fbf4ffb8e6dfb80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030d3c55b5c0871301309da26c1b441da05505a0db645c807f6b796a04287c38e600c93dd525f5b8e6db30bce061d71423819f34709641f56cd4de024b0e243d74c45ec0f696d023af4382d49c3664a54465b227748f4976c2f143850351167f385637653255d478977ec379b55598611bcf104e2f26bb696e6160d799ff1de280227e014ee219fdda4901975be18381a687b9e98ff6b7d4a197fde30f5ca9096240306fdc0118109eff5291b976356e0aa752ced8a02ac7a0cd66b0b13ef1e9b38c70a01a455d5271da38f675514393268454b20ae5c03d045a6616bf600d61e1a8b10759bb1a55c4661cbae830403dae291fea9b887859bdf584f5e297f7715ab095903197c0f920485dbb133bcab06c32c256f31830e4768d5a61b60ea376aab7e54ae02102b93985f0cf85eb7e6075ad99b54c0a093e7237b4d3c9f681c40165c78456c030741af77883407130aa97bed40f4eebe8e9952cdc86b54a93b69778e76d5af740225327c580e7622f81e0b494820c48ffff6383a458951de19bced14fff5f21ad60223de11fc4a4dc8a7e1b58e1193a7422c4b2f97c126c6ee869880627aae3f6c1c2854c1c2c23a55f3d3a13e652f28e00fcf5138fdcd2e5c0e3770efb36ee490143116a16d5d1a03c72531dbe2f865e9f4d744e30c91189cfc3f0bab6689add28fcbd93c6c7718345a50b2ea624722d0c2c0767bf6be0a9b8c8f98be059587e039356b37528e83ba7a3a610daec8696d0934754d01f575085862efc5c01dc3c3019f318f9f57f3b811ec17f299912825485b64daea16a601f6fc75e24a999a5976c0226b8325f21459861a3811c3af014488c641d01d8b63d30592f20d71d7ad6bd6bdabb08d30c4fdf9d25cffe04448eb5d051a440a7ac8d84c154b7897aabc693893e44d0975b8b0f2a747110600ca3ae5e5fe15116ed13d71e0545b9e61f6500a14271035d35f0f626097a1d91f0958930f8ca42deeb5cd3a82bc0b499e0fdbca9798e3c524b34ef97556e2afe79d39f2f835eefe90593c0a6f3f048ee50c59ccfaa1623f161a720ab4d5031cae8fc9fb65667894596443573ddea847182ea6aed2d1d4db2f795537db491c93ac3c504947191f23520de9bfb5d4469b7a703c04665282df30b27337b53c5655632cd38413c263bcc544297987fb094e591708c5bfbcf0cce17b3b4d907ebfc7d801ba6cd20e95cea4c23e81b1ae14d5a8ef3edd00b4b5f56697bfb157c3a6bf7249be9597d9f930985286d4c916c29f34a3ddd0e77b4978b21c444c03dd776994e08a2e346ae7dcace4af69ec66e26a57dcd1d12c7491659560466e6f547f86dd7207968aae6aefb701fef2dd8c4cafd222daaa13dcb6eb3cb66be4ddabc783cba75dcecd7b097badb63aa4b6010e342c95ebc8c2a3bfc3e56917a047da71f12438d8a80ea2aea3cf391e93cef764e1c306d85bf7378a6c7810861afbe61d99f0ee7ee47d14125a6324313db21aa7837ba913950deac2afa88dd4c6fccaf1aea8131382de942bb5342327c62a112ebff21e8b78bd7547ee5a327d7567aa291ebf08c9ee0ead653f9a70871a28a24170de15947f27d1c0c93fccadfc15aa3ea2ab5ee9ed8aff6b6f61d88ccc2cd145b45f1cec5fd585db8e1f0f58acfb1f91ec7f804af1a7d6fc9cd1b0293733d1265059424f789de0b6f69abc20562c01113e5f65493ec0f2d91a87f502b0c5af57bc19d5c0085abe578c4569690c81285edd99d31a2ac96f012c20bde6b066bd14cb72732fea0f5de378f2fe7e5eacf431061185a00881452ccd90afda840af4476a82f3bf91a95bf9569b121a400aa8fe89b2b3d8242793747f29e4f4a3d05688299d4a97d273c1c1ec5b427da7c4874df12be0540c8ec2b640a9e5a0c16f1a92e83331a446dcfa3ba03cfe938f947d982880211bead796809604d36fd3ffadbc764dd2803301d7b733956e4692fa0aeac791897d2189e6c1b364805a0c0da754ee26a6ee4307af7740b1edbfe5eee7690e07f82684d0b91cefef628417859e10138d4955eb79a8cd7cf615e2dbcc421f94a0230770b3451bba4be1ef522237f5df9089e13b108dff98cd7c0a2c621ffac6ca3d95977967697d73ddb95bfdf2ddbe36f641514e9e801249f695e979bfe01393ffb3ae3f9ec1eb7d45579a7e99e087862bc153cff5f07bd9d7ec721b0c31d66186ec7ceb1fa7972debed0a4dec4707d4e10f6f1b68c936d47d594d59d42a76fb7f719e9be5c8abca5ccf25a5327d4c2ffeb358924999a245eaf8a08e24bc7d223c4d072f000000000000000090a49502000000009bae751bcd5232684d57bc21a6779648d5713b224a6263ba466528b03fc7c3d3890d92c1b68eb9d4de403799e02751037cb55ab15b32aab280ee2bb5366ed72c2d2371f8577dbd5b4a9e1a5e7056c52a538d5d39f8fc51d5820333245da74c3800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000097d41635c53c69f9d5493f2ce46b2620a62336dcfadcedef0fdfcde287eb792c4f71c7d7393baa48057a7fd34012106667d9d0fd570329e5e40fc2be9e0ea45764e314de9468cc6a9606bedbc83fd9301c1c0e6e20d12881781771fcbb2189fb4255ff4c86de2e159896f4c043663d0ab04c4680c59cf9415d12962b695dc01021783e60ef5063aebe3a054b4b841c92bfd30970808ecaf2c0476e8010df1f5a603243dd3f305af3b45392f5bad0da8d317e06de86bb52a804b1b524dd543a876780a0413fd758ef1fc2054107ce2a48f268ca37ab16cc9b1bae6f882c44cf479969af4f514177117f1c278d1cf0ebdebfc7f19f452ceaee9c9ad2555ee5cc267b04b0221cd10f978ac78c7c5aa3f3538af71e3f7e6ab77fe33d08416f1599267021788031f7d7909a24a4a7bcd0ff1176a2ce65b9ed38bdcfd407c8a3889f3ca59f4b8930208337d9e9bd6deea72bf3dea06d054a74f5ae85850e2ed3c6aa1b0cabf32355703035b53b4cf0368d276ac6758090b5d8f677247375b8fabd9cf4596cf836093270201cee164fa742ef4707cab59645d56976c7f149f6a9a4805dc840c6b71899d43ad5db84dbb6b30efd775cc437090fd97faca4e5a4851f0c5cfff989e3969a19229278aaa58f51d212de6b334240e521068ee5f857ccc3a351848e8d59ec364829ebbbd13b9d7c247db24a7c8d4404507518697d8e5ba86ceef1b38ab456f4363e5cbea81bc645e03f44a4452baec9516c57331a9929c7da8fce4e3706c84f2ea5917924e83dbf630ebda87536b275942a3c4e7ea49723db35fc080974744fb9e426261d3d3f0a923a23491f95c6e03b6acb117a7a01a638a689afdbfe8d95573f1c5379c563ff8609f93fd00dc714c900fb3cc1531a3cef0d38e2000fef652d03283b63cc545cf0144755caf9eec86d0ab1e17aab5742e6aa3d0b7fb3213fb9707b2ae3bd5f929c97a7b71934e29c06042024732b20c526109ba53503a4fe7db46cbcb5a3b173317e4bd16d75c636acda887353dcdde8b0474ed203f93b3622942cd04b0ba9dc0fb0f60120715d81ebbfd2f90551e20f4e2ba6e361dc279662a9b2cf00b47ab9cbe2673a8699c5facf36e386a5062ff2428a7f383823d1dedc50e2d2685397c5cec9221da4a518d67af017d6117297cbbf0e96bbcc67f2fb23a89ee6bed0fb746ed4edcca8ef44aef9a5bec95217468587c8fa1bbff93ab2a1078536b4cf78272a28fc34d73c10f8128d187fc50455d411a2a4a62eec23703163e6cecdf613dbeecf741e5c387114ad9179cf538219f7e83af2e14d61ca69800777a09a657b43af1d766c4d68b0c4dd8bbfba93915bbec132922540ad2f3e3137b1f7c547fcbe4c644b74cc6a60481bb6758da5b10cad94e9cc88dd52554f063478a6dee4e9a5d1a67ecde742669b1d5859443fd1db2bd44a39768e06c1c1767209d1622d7f3e29b79561678789b80cbde5312f3a50f9f8c9c7216d1b7469e847ca2092136324f94a5f12ebf76f82c29417feddd8d977dcd02a786480c2334cc293bf1c44270f6a5878b8b9e01845a9804df5c88013e8874c723f744245ad9d8ad4cedb316b23c5ef042281fe1cec9d64f2f5575bf6d8f109ec16da1c268daf0d76882b4bfb7ed8d65e8ee640509fe0aa93d8147bccddd5ad4652b675d678bf36cbe8528ccb6fa21fa0774aadb256ab54d4b05ddef9627c2d27185c5cfb89712b3504da9eb25d4c53c5f3391cb737e2db849c4b670da8cb38d5eae3e2350f0d2f54c2f01dc2a92435c34c2b0022d205edd7b7f21f4ac9f5282fefa385688e3f2f4f4dacbb589dcebfab82034e270ec9a295bd1d2544fd83c369c22eff7c909e81612bbe7c446808842099f3a2332c3f216a16fafad1613e45629fe8db25aa79304cf375b9db32a4430cf37f40e05db360c51a3ebb84a5125456eafec4f00435fa30d32cda9e90c5a10dbc9a5df4fd37129667d3de0cccb2a511152b2ec69ee463d1d239bd1800638044384db7497f05290d60f8109649761b57061e54db38a490df984e9a46a19f533eec5fb9e440b9687f1b6279ed86b48fb424d24fe70acdc8ad76a435446df9fbea107266384862483374552feabf9d64f4068b42999d8bfd23ed4b9f4eb3e925cb5030ec9f5a6010c6839de111fbef7be82f4ab09a049b827960b4bf1aa48fd8438200a0255f8e7c19af49ed8855068e10e4b29941e20213bc2a4f12964c4c4b69726960031fe6114bbe1457fcb36637c833deb0bbacddc89cdf9082ff8b11ecbad535dbda2c9995328a8899203000000000000000000000000ac6ca096ea0dcba304da61aae65f9a73dd935b788abf74724dd354c46d3ccd97b341996eb84aaa79fd1cf9497c38f343350ea98d98bb8e6f8726b7232db001e7aa8926e85e07ce78ab26fb4470e8e882dedaf2564db757117443c45022994d8900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000619db6746d409be8e2584fd19d39be49db9a33291cb4cf88161a1380da1ad6bcca4b6f6a24d097e54b16fbc33fb36e9be45f6214c9c408f0bba521e55e4657379b9419ca81592ac69a03dd066281359bc872e282641055ae3dfb2bd716cae88970156c8c51616898fdfc932c35f272ab4a50fac1ffa79e63858a3f39a8a67e28031a6ae938bfcd4d2f902bbc6d7a880d9418ef263d82da963845bfc8af8228852d032f2a1e912fad18325d22ec92ff9a48a645cd6f415698bcfe0e10c1e94ada4e950a02b7117a7cea3147ca0961a5604d53a87c053ba93782558d3e99cf0447ad1503bde86d87334bf8d2d22b1dbc75fc6d3d8d5b054598bb883e495b8f1ea70cacd70329bd603af76f266f521eebb3f267c2f1781554fe7c7f84d83038b04f3b58fcbf030fd662017c4d01fe37b0f2d1728f2b3a0d7fa2aa5f53f2d8fe6a2480802f4e44021837d639e3167aa2d4123484abc5c1ba7b85db56be704456071578c1f13e67e1031cb62d108e1d8bffc3639384e2b797bf78f1bfc09a2fdba947d6b81bf43bffae02009f32ba68867bf2c6cd087eeefa3eaca259a3b3852028a69da8016046f20555cac9b1c9ef79ae2ddbc5143a4912096705ff1b40560a88fbbd2e4452fc2d908d989927e01f0b5debc355f86c0cd7854ee907d7198445e72cc05cef5b05da743ad4ed35169a793ef38bcb7403e8fe185af40639fa4ef376d9d9b30cab8f9582ed0f3e879177fc9fae348ffc8184d5c2d0464c32656f38f06729d2509c72f064555f1ef54e452cf4f64b1956c857631c43612bf2f4345faf090ba341416087c356eec2633307f4fec382962c20ee3292f070af3f8645b546df48de5a527b14c45f08223ce79c4ea38aa17dc163296098cb13e35d399ae38b9b6f3caffff3c8de56c12f3e5d40a7ee89a593b819aabee15cef765ff42a8ac9598ca3e40c60b4ee66f109326889a260ad3db630882bdfdd38ed7686fffebd24918a63d66c265bb2ce1bfe14d12b589860e68f4e18e74bc670dbb9ce56da8eec76510631d0f535c6d8c9c9c0dec73c030e2a4634d50c55e48250ef4547a535f369ef6a59232ddc5be6cf1a8f673582a6ca4b37fb5fbbc74a196a0e1e068484a255028c32930ab21c63411114645a9c663587e9d990d607e126737607d84a4fbcdb7d772c5d028fc2dd7497b33e2076b6737f3aca6663123bd31cf33e0593337a3ea2eebe54a23d0900aefbaed3f84b9e6d79dbe73706a77e340230dcd6498a5c620361a84ce7f7fe7c2bc450f46ad56cf67664d300fe3b35eb4e7f38204c81ab19987a2a06d306c0535d43d0f700a96972959c5ca5156bf55fc484812fd8030e62d67b0826c37072e1831acb9551a7eb7a594be8ec1343d9f414d682b5103eb54ed4af9d667fc6b72e02c67bb09c3daab071644a00ed2964e730f369d2ab78ab9eaeb6e9f3517ed2710d8ebe66c8dba79c88781878b591644d14afd6340255dc52330848fca3471eacc33a97ae3e0d65e8423c258a6078dc3178184a7b0abf27d88e74456cd197f2f91b1cb5f629de7ff3a805671e9ba0e7a0ed3bc41b9e19b9c72f0cf0112e5669c06630e0fb1e1718c32e057139d3647f39752f270988639c29eb6d75eb2a0fe949150e42711cb43043ded12ddbb03bc99d0b4cf4fd8ae903d76a5edb07b188f907221775a54fa5283266ab8e44da7f30c190aeb706669e4cb06ea4d9b6189e2412b8e3b20783888ea03e6acccbe553592ce617ff37df466df0884eeb695d886f5a27f664dc0eee0de084797f6183e2902492c57c4cc4311aeef3d4a00dee7ca4fe330b36a5f4db8f14747e7a8b671dc6f1b689c86088f0d7673a0de8a2b59dcd4b9fe8f3a8292de19ff9795f1d054aed17322460b46a294535466e3d5f0a8faebb09699da5719f557891f3fd12eee02ad8781914aa26694256707c8e90ba460286c028a8fee735606e51a7be16eecf24ac2ef48e542185659ccd23c9332e911d71ad6c9fceba7827b3af2898a7abd2346cffd8ae31edd7bdae017f79b96a9c63b386c5263e13a1e5cb43ee1fdf14a60bce3332b55a95ba85ba5e460fa0bbfb6c5ca8eb9892b5e446affcb315bbdb1fea5cb326dede31ffd94702ce552c945cd196ca2c722426574e67352513ddeb4a2d68fd9061679d4611f5bde4c8b73bb26185e24d4de9fa91381fa07c2dc880eb1eb230d5b2453772b01b55da154890e3e3ab2e2e6f781aea5629d724b2f066ce119880e8c43df4d900ed19e23773d738e4d27f6c5378b90d717ba83e0a7fa79a96509e175eef4a0b1ff0f7f89a3fe43bd8e5b3e491b003b4838e49da1a7edd702b9d3d82c49d1c18e0f1f51da4e5d67b338e75b4b64153e098cb257eba34c77716171f318c856730af4635ebf4cf4d826c8a3357eba6a4ee02ce7f1d87dfdf2ccad62007", "6351636353655363", 1, -222084454, 1537743641, "3507dbb6b4fb0c7ce7a8ebe816b59e1171f9a6dd42994a5de680ca13dd19c688"], + ["030000807082c40303ff5f983fb753a1e10bdceef94ee16f8f01e4eed55f5a9205f5de429aa27cd60b0000000004acac0053c9fd22cf0047040d2eb11fdb53f9c01c2b4bbe4a67b6901cc1bcf45ff882a0d737b9112d010000000452000051ffffffff5030f1d47f1c4d89d9af529daab00bacd2e520378d1a00aa5ff10cb99d25d7b2010000000953635251516a536a6375d0ef2403802c0e04000000000153d12874030000000003006a0067f4230500000000066552526aacaca7a123d1a534d4e500", "6a", 2, -909824731, 1537743641, "9a2a416f6d32ffdc8ecb2289d828534eda7c7d7e4d1f9441927c912315c0914a"], + ["f76ea95103826a519d65ad5972f43673b46884b721ee18b36ada46bd7640a63082572491e503000000086a536a5253ac0063ffffffff8adcb907b9180c459f7e080750d9be4c5a9298226da89f93fd7e8481b98b2e1d030000000353ac00cf980bee07c7d9f9c11a60b2fd418f4676e11a43d1cc7d2189a815bfda179e4c31aedf3f0200000003005351ffffffff029d36080400000000086a51526300516a658c0366010000000007526553515252ac67a929770246d0f005000000000000000000000000d18ac7b4095b6eed68abd16a1f784e0edfe1139576e04bb4c8ba794d17d2b53b4faf746d129ecd990150e9fb2d2106e208f77ee22ff061ab3af449b1d7975297f86d4c7ed024171183bc90ba8a0da5f0474cd0129f2eae321e3f8b9e30d81a5e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cb0421326f377cdceb8351198031535322936191e22e939b5c7ee78a4a63ce983a3fe9970e2353ad25b52e98956895f17307099aa7add2f2108eac6f5ab2b5b89e9819b064d97710d87b99921c61de50da5f26cf58aee71d6674432e4f16f58ced27886a456fd87c656024910eb3b617bb6cc1498081d04371f7d910b1f0b54e02051fbc6d25eeb251080a21658a2bfe308d8c81cd8dc77acbb388d8a73ecfca10021169cc8dbff1ca5b9037c28be6ca3d0d1007eac1de49e4964bd6a218f72d151c0a00ec5df826fe97d295a3484c6936a2e985451a3669ef013226308473707a1d2954712e966035af7be13b396a95f2407d1811d8b642b4a30d22aafa95b4d0c759032fad00fe4abfae1961165bf398ea47fbb264b2b5d6c2e1d17e31ff1d69039ddb022d2c9674d61bc1f93ea45e3d13211d94178e926e0387fa26cc22dbb39516597d0308f1d990b194f328bdaf84d53d2631a5740c882af71d7184352c416e053afd95030f5386c0cb14cb2a023d649da1f6dd9f295a7c0a1cef3b2d038e817f82dd1f2d02203aa03a896d45c93d5ccb6b26c9a3a86be001fc40571fc162122ec87874df2e9d6deb4b4f1ee0437e929e4a7793738f1143b8d5241e2af7ccc2535b58a88a39e931f3b821f434b3e1ec436cfe99fb1f23cb692227ddf76203df38c678e77184c9242fbc8b670719aaffcb122b8d5958667cecd338563f34860697ee53a95f768ae8cf6f0b48fec5c15e7f677c4314b0d5a09ba4eedebbb66ae074f3e312c9b55f45b9e0c644ee43fe217f2b7acbb128e09868562f3af459ddb1a7046b0717570d002f274cd04e8d59efb0dc1f7c94306c8e838561347baf6406d8409c324696cf3783fce6712f655b7b990729f846ef5f4790e214ddb850a4d533cc5df62edbe2c5d0c485958670c99d49cf834993dcd3fbb78d8db94241a85100e51680c962b1190564fc99d34e7ba4e6e8478b65bfa6ed9a51c98ab5098c7e9880ea5e8d86196508778f8887eff4d55f0eeed6b8ff6c967ef8bce35787e55af530bd4a79fc9e01f441a16717d151ea7daf0144a4cabea1682ea656dcf90ac5d1449e403fdeef97d8d88b0f6c59e8f8e383b91b0bad19d773b772c7bc0b4af6dea4c972e68e7633bf278c8ef2238847524198a3d9052d37dff0df2d2a8f68252794c53dc47356326bbd87b754d0098ddb8be5be642f9a9402cf694e6b5a347f632239e9ab8b89d9e1a5a1afc8225bfffa082e066495fb27be97913b5432b0851dc90a0c665b04bfcfbdc14731e18199ec95c1103f9ca51e10e9a4bf6200d3350516259c180ba1fdb22d0d3c274738ce2a23330061ae7e7064b2b43b26fd3c801e2e63d9e837b929fb9b28b8122fe8edb8f52d380ebd0d961dde66d1e502aec2e9c94509c5e670c809f7d8b388b78e1d2fbe34c6ca27069b248c3475ca71c72950c64811283d5213599c76202b8230415486ec35d7cb8f04b1776285c46983ad03d042e0fc2dda96106ad43d02d9a5f4d6f7f5bcad66a7e5a9cbd5d464174f4f33260ae5cf72869883e61df468c14e8935c8b40700324844d22ac878f9884d53cff5bbe6087001aa7d4435d490c9e1fafa50c868255a9f80ae33a6b1d4c9156e29c7ed7a3a78a0afa222d3dcb8fd1f896a6a34c22c92c1e7d35e8c4e6025968ec53ac1b650a611fe3db88db39152cdd631509f15af0cfeb5b62c073724f36a1fce58eed98aa90e113244eb6a372d3cc894c6827e11252b6bd7f221cb1fdd60abe1bf05e4feed6212413ff5f71fd4c5db0adda7d8b2a21ae3e4749b57cc800f97e7378519710338bce0db9f5dddb2a648192f00b67b2ace42a577c2e9a0c9dae46fdab24dc2764e5919c3836f82353b5c4b8a5048a08533f14710f85e0cd13b99bad54221fad76058bed559fb1b4c5ed3cac820c7993738535885e65474abbc08d913668822698f1b6b26661a8dc2a2cd543c8a7536b61cf00c1454e01e7d94ee8e3502a5a0a52e2ed0837ccaaba55c926f58c331444f552f5a5198896775f8728defc8b8ff3a385eb2342e16c05463168bf2fe1e265633c915543f0473405f939f32c482c79f9a532159e62ff6f8ec2f2b116f931ade9a1bfd1be7653f04426bbe282c104591aa2c3ef097f66866c0a46de6f4f91cd048b5b866d5c22f52a9ce4e3d3289361d686e95dbe0428604c007afe040e5e358705d1449cc6b236002706af8d2d6eba2a20809c3d37b781289bf28bd4a9a5d9c59930f2d69af93198fec49993a379c2801fd9d6afadcb300f0f3d86398c2d395c277c997b80500000000000000000000000074f0e8c8d65fe3f6972a80f56c940de484bcbc2af2aaaa0555da62fbbc6390707e6870cd0d47c336f8c6f6d424ecdab57ba163ff9887e3f9782e2677e35923c3e3a02a586cb9cd840e9099201cdb436383175a86efafe1caf524babf2a41ebd40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060d898b179f65fb0cc2774bac4cda66b08c34480ed76c84d090f96eab186c64880b1479fd1c47e79c5e379ede454cad5af8813ac144c362eb0faf4ca4549696ca86cd558fea394c427adaaf1dfb26744e71984e5718ef9f8668a56e581f4534fb873cc7bdd4c376ecc0c5a4a49d89a87f8fc7a6284e7f10cd42f573292a4d1c6020d676da441cc296aea6eeb246b61e4f5c2622e7f337a494d9926bc57a356b1d5021752724ebf21f93e3496aaa111613d4f6339b7ac613f26418d246b7982341d200b0654e8aa553b821a1ce96e147acac5ebf8d118b55b3cb2daf23d1483a9b2de116cfd640945a614de113b5c4f80a7d7999bfc7c143d02819875b7fc8dbbd71d0f03193f751d13b395eab7620a418a6205053e8d4117711713f9dc8fcb5e1894c5bf0210c164f2c8db267e7941b0ac6eb91fce248b79233a487f7b3bdb25141301729002202c7bf56ca687de0f5fbabb6ce1fd8bcf54312888a555fde502c9e56b62b58f032f4cf70847ac72be6e1fb9e140b969cdf96cf769649ab296f06a52de3b5cebc102239f50fd6c7b1e53072164148677d46961b7f39cfc82f1064dc9db7f46b07a5d6a678b650b2960147e178045aef9eb31d4778dd86a8226301d77c69bfdec1a671b133918411f3198eff5739f389c4a2348b629eb53699d829304277fe57f38ab39a058c6b60fd7b45a18606f0e371864558eae183cd7dc936d46ee8c9affa33f4484870c5e686d88e32fd5e7d16c60540043bfe6fca382e4ce6ecda5e0326a5b01e952a4db320451454ab75c0c77b4b60ddef1204f790449919d42c17228bfa0175d335776f4504f62358e0dc57eb5c846f485a94ce3b3488b5156d1d8c2f43caba4b35cb78f4daa7803981e1dac8f10ac039238361de2a367583d4b0f8d1304b2e840499e1ceb31d6195feedb01e425fe2258b4b6f32dead7ebd8029f581695c7ff08da4fda1a573bf4b3e89f760fcf5058c480871d01501369b351dbbc2d3517429f63a3cbab9fae783789d547faabd1ff0c67e86130436d9348bb54af4d98b087822b10436c6a5375cdb6fa5a8ec7f59cf3c16c2084f6517dc041d16f03eae78024daf3e4bac973d125aa64f65b3ac1295e9c7d17b0329c2cafe98674f5ae145614826e41ad549077ed4a868e7919904e83ffe2edb5ba0b13f70a7f31652b09f0f90306749851998a87744a6657d2b10f969a1708df55ebe7d73cb24fc8202bd51b05bc422ebf7b93db3cf47003f82fc3987a394b10830cd422fd6f84c495a948498194354d2af0e16602f5bf7c02baffdd7aa7e53080447d494da383057fa57853249f4edcd2122c22b9e1259cc4f3c11328ad72f769e496e008f64713473587ea643f18375ca3a17c06faaca0de4d07de15965049570c2c83b35cf9f88ef30a39ab0807cc0f2bdb6c17904cecd81849808bbfac6d8df757d02151fa906dbdd540f1b9389f222827f18a04b276b4ef00e5f6174819e50adfcf75227086d3b59b1193d694dc362ea81be7946afa56453ce82c3af8bd0d64939dc105be849f26078ef4b9d5986e1ba5b4404d660ab8b8e5299ab475a7a928def771419231cd40e63e180f9a3018397a5a2e2ca2cc8c267826bde56921c66a66c089f93c907d064c6efcded3d02e499fb01665be658368d67895da938eab663f2ca5a431bb39b2f92d3ee1d7633f7d9d0ac4a5211a6b8b7ebebe96bcb35800692a5145e5a9fbe4db9bf1e5759065be49ff4243aa309ee99941aebec5cc84d1104a0d23eb04a93b531730c2abcaf8f864170a39570f15804964c5901dd1c283466f2c4f8980bed7f6471a0529513b7647fc4b787fddae50f16864ed3055902df45d2e6e44d8b9e49928c8a73c0496889edbd1fd08353cf05c97acb77966aba1783e0f1c8f89464f8600882723ac39278e5041dc55c546612e585c4dfdfcf9a92b29c83fab00fd2ebc73d7e667dd7e24a808c7930b94204a8060d7aaaa1e8cf4b864599394a36770e80a5a4d98b0cfbd9a0384f838630bc60696cad28ab957093a0c4a8f0303d798352946f65a43e518f024a69457f3e8bf4ec1b82962ed7253ba17b8be168cf896e3be674d35fea7604793b2dcbcec05e6326aef5e7c966da969305fc07e50706fab0b035c1da6316ada73979ec23bfc352006463475434370dce84670f8657177a7245d99b9c257685d497cc64585782ab5cdb9cb23bc9f9fd7e178bdd58a65fac17b54ae288c73d7731305ab376235f44aab23f5eec53f473e9a93ea2d6d1eed922f7e32c856681a44da7fb57af417fdbb8f684aaf599b32d92a46f37a76c5ce108677a7cdef98ba59674f698667084c9f8c83d69f09c5a2bebb7383108cbc5447535c89fb2bd2597b10443248ed3f497455879e3469fe5a8ee806c22150e8e4d5014024f35c92fec31262cb0f2a1ab70c", "6a6300005253ac00", 2, -1153722531, 1537743641, "3a8f32cae5cbb3eee5bc5cbae864ff9dc67a4fce849c9a985c1f54c1bb002757"], + ["0ac29e390102bceb635b13b9338ceb9ccbe339cccaae7c7bc40666459f979ca274bf36205a0200000000f67032a30130a3ca05000000000400636a519478bfc900", "650053656a65ac53ac", 0, -1085558292, 0, "8e2daa9d34834ddb5e9777162b2e47f44e62f1ff5dfdf399cba9b23ba3517d10"], + ["030000807082c403018a2a1ba687afea4a52029579aeb1d5ddded2bed188674a30c125883690c2cc4903000000076a5151656aacacffffffff03e4f4e301000000000253ac262c19010000000006006a636a65009894d702000000000351636a00000000ec2a3e4300", "6363", 0, 1890242588, 1537743641, "54ffacf547ebc187e8b53de85b716f107afccae8b08dff1af74f0337b7689d31"], + ["030000807082c40303b8623a3f3ff9d19434e3431734f111ce4429ffdca0b89a8a421de97428354e220100000003516363ffffffffb20ba1f6f396bee2616a59af0ff2099efb05fe32c30f9c77e059d29598d508010100000000fffffffffc670846c89acbeb09c68ff838225122394b3e5258a2af8d16c971770eb1639003000000016affffffff049c96aa0300000000086365635151535165a595e80100000000086a525363515363656911630200000000070065536500516ae23b6a05000000000452acac53a0de5df0d6d82e7b00", "6351526a6a", 1, -502947489, 1537743641, "af50ccb58784c20e1a7e9731aea5607c426d700c491d33ccab5074f11049a196"], + ["ef316154027cf974d57c76f066107824238777528d3cc8460c7cecb77d05d516fce7a9b4f5030000000752650000635100ffffffff4e52eab46a0fb57070a9d0f51fb56dc4de70a6c5e9343817013973357aabb0bb0000000003005363ffffffff024dbd0203000000000353006a58e289030000000000c7e7b6f400", "000000635151", 0, 1968658531, 1537743641, "f2e925d91d6ad259bd40968c8045f55966cd923a0eb4044b29644e46ec6520d7"], + ["030000807082c40303c3df1b2dd9eec22f2aacb231785080089ab9b6ba45a554128e092f1fddece5e90300000009acac5300ac51006500ffffffffc23f486b55d90a45a6cd74e1ac31a575ef8d98c662205e45ac11ffc07ffc013a0100000004acac5363ffffffff40e41bcc92726fb663a1c38d9ad238ce3961535b3c63e48737a81cdef21b647f0300000006006351635100ffffffff011c08d20100000000000000000071ec68e603acaaeb0200000000000000000000000016f9ac81bbd982e0bd58a8d0249f9c4def8d3c6a746768eee4661c991eff2fa38a6b35cbdb7ca7ea49f1d234277f19ac920991fef0812a9fa06f09595e25bb3042e9bb00b6e936563417e1ef99407b22cbff6a0c2f534f701577b21825f1da0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de66497b230809c1cce95cfd042e5273fe091143cded579bce06c02fe0937e54ba354eade0b09de1de06a481c0d44fbaf8a8595e10c346ccabaccd2f008cc94a688a2c13041e63749efb7d258e92cdd9d846eb0841f85b9d3e7e216ba4b1500f7ed070649eb0c99a4ab7534c94dd53d0d354c20c7f93d849025bc58388762d800319ea7c49a3865d01f82f01e95bf95d664e91039f7afd58524a8b5515d7f6ff3d0318858c7ba342c66455744149ee0407ebeae6be27cef4dce0d43535928dc775b20a01131338e35a7963484be6c78658f32c108f8211fa3b7f2e98ca33f35d9e6f6fb1456c9c244505fa3861ce578d63aaba4b2a1f4e453fed769d2cae6aca630046031a9d1de3e363453f04cd0f9a92cc1a6db73d3e0753873ff829e1fb56b1ebfdb60226731477b3ca0d73049e639a3389ba02b6e37307980673aeebcbc512d71b46fc022f65afab00bf9c791dd7980143c9b689646b86bc39d1229725386e244ee404730314e8230771edfa4374fa2db71442849ab067bf6dd4118b8c45d167cea6069d8b0228f5a7e335561f85869e44a76b5f772c8b0fca5e8e7b3bb3780addf2643d2353abb35d1d0d631a29525dec1c02beceaa6450f3982b663d29245b15658f16f14e256f5787e73943de9dc271d912cebbd87f4ced0cc163f24093fa46eacd36ede6a22ca339f8d52707fe29b1cf7cc774f51242eb352c7f303a58b9377fe0e397f0cb34f6606ac35ba1e289441496daeaa72e11106c3891316c2da188169b6f0592107fa36442a42ff6fad8bead1279a6121775ee11943d1b20fd61c0eb77e8649f7296dc2efe802878224191224120b9ad1f540202fcef5391fa7c663980a12bfcc10868a619273f8694f16f9b226e8dcc72d50062d0581bb9ca649841aac558e754a266436458684e1c1ffd23280ce68924d5a0c046f128fc0428313c86fd72101aaa65e224fdf141c1b79b4c510ad405065bc9b675b297cb9f311e43949414fe39d55c5afc48d7fc655cff071e0914be05dc5c6ecee0d1e7a10995519e79839bae73dd5b4fd6c765f7468697a06438ddea3e308fe16d55314e18d83faa1ba88a6d05b87231c26ae6c3599520576484bfab5a290e902c75c41e8adc890f70154feb6bac939306d2a8b7255d3e17f3dec2c41ce2b0ccb5ffe1dcedf95241e970af918f88f0217ddbd3eafcfa4ff27910855bdee7de53f6c509354cba8a499bf569649b0b441f201e403e99c031d2c6b9594c9103669178ae6985043472002b68d8b5c6a7b2c548cb5eb7998d651022c81e8625dc307f15eba116f21c3712911762d41072ed9dee0858bf2b3378c38856164a80262ca56754f853e6dc7c88d9040b64fa73727faec9038b2fed7df4a87988ea95105a2be867ccfd9a20ea64069dd264fdd6e014e78de671c32ccb56105dcb90cede3354d7a2df9e5de356de47b89f1ccd5b61627f2f25bff792259c6d79e25042fcf7e53dd6c7f353921eb592f4dce9bff16a7cd9dded1753d0b2daa4d22e8e7d887fede3a4b9a54c2b4ac4364fd04692ebba4dce63448cca21ed1257192e32912a0344e13c9caf6e99f8e3bb5bbe36843ff13dd4d43d73ceff94facc0f81ca31e8a2a73bd0ef394b3ed53465d055bc95912f750a4cfac64266b1552af0d933dfab3edfafb687820dc187df7a19eca8c2dbae6d29c56934adf670a1aa70376275708c6371e271afddb08ca0eb54c93a41e718b46c6a6e021365ece4ec418df7b08409fb1dcd370a0925dd3acf1c5463b5a83b949c4e859937efcd190c3486b533a65395cc647c435adae6fb49509d5263e75f09a896893ef73da8548cd5d77d1ef8fa84bd54ba7dc562ae99e2a28fa435467bd42847c1716e9043262148a9b0f06387f44d7e4318ac690594f1ba15c7671216ca1ceafb50d89acf42258da2e62c1459d6ba68d24c00a3bfb6eb479f4dedcab8724d7bd55ce3a9787697ff9189ca3fc03a9eeb8d1d54ec5109b68bd1ac617803ecd32773d130a876ad40b93baa19f52acfcd1bfae246ef0cd93c1503a14b9bdaabf83a6d01e72f981f9616da5fa648025f5664b15d41eccf1360d931a586f09af69914be175e72bb76caa0aac642a807650af533abe3caeeeea6d00bd69be539b28e3a398dca7edd35872ab59e193588b29e26345bdabbba58b59f6c04762b11d3ae247101f9c6bdde6ccca703d0899676c1c3604052d73ef84559d6b31a36c6279d98ad9b6f2d2a3f4763e5b569d0b744e96b889b6f08294f3d1a457700dabdfb198ed03f8b37775c13038adf137fe3cb0100000000000000000000000081243d8b97d2eac10dfc3893bcc95d2f4d12a2351def841ab4e70d567d16afc0c25d08ff14fc8ca1c2420266bfcd1849e1de1031587b09602273cdd1c5b5fdc8ec6105ad0c092982473ce96c982330c46075223567b111a7ec3e337d66c0096e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cdb2a85e43599025ed31b589ab3358027135c031d8f7ec598bd5ef62e9426b62bb045f0827d67ae4e7d3a9855c2b042dba21376cbae556188fed8c4bb2cb78aa260e2aa88f8076d43b329e0afa5e373990468ccabe2bea611198545ed1b232d4dd02ee9efd4568d79b5267b9d3461703b54c55c6cba65964f4a09571fed64aa30325adc5f51de18f6a4b052ab46278b71e1377ef57479876c82288f2ed2c9fc8850228e40c01fb78edb7e54a4a379b712ecdf63356337d133ed08801c7409d01548b0a0693374db3738a20f767490a0bbe88cdee075c92763026119e2e35a0de4d8278af5d5bff70a12298c91f30b03974599badec14480340e85d0f6a21046d4ff0d3031b1c9960d8cdd447a69069d6a85e083863fd7273909aed28228ad371cafcad56020beee551f060b48332850ce4719347558c03c4bba353c26dd8f03a5524bd76c8030bba634bb56a0ffb54c251ea41d162761186b61a539bdb912e774c2a494245b90325daa4670ac493a85617737cf33894796a1e3f4682a5fead2da94604f3477c900324ddc6924f93fc678614860414befe71100027c895a2470db5490595545350aa42d4fb774317d1544bfe8dfca7db61809c54c320beeb8e6edf49a371a5c22c4504ef474391b7b1617c932ab53ec1744392e197e082a2d69a16d5d6826677cc113fda614cff6df58014af508c2ccb2550d9d7ed08183ef321695ffb5d7b7ab0a48ca9dd4606890dfafe232618c50346067581527c14538007c8d37450278bb72796b6ebf3b590fd4438875365a55aa5c3081a26e12d3a2c790b8bef5d481c34065484c77ebec0303fdcde2da7b60c868deefd664c475493954bbaa6aa343d6e82c0529aac8d6a5c0cd90fb157c14ef65302694de9ba4bde2eb08b23fc2fc8ec6f86ee7bf2dc6dfc0361e725ec5d65ac7b0cae191b56ab00481ac9af1fdd4a3f9d621bf76b5135fd17b86efd1aaf7aafb58b78df6b0861f17b2263fe09b0c0e1bcfc5650004d6963519eceb9c896824e5d0a142934aab254bdf0b09165e3bb1ce3fd3560230c1aaa9f86073bce6736d72fba972e08a5f117927c811b70c315bdea99133e49fdbcde41d7715fab06551643cc0e1ccd5530bd976d3886b2089408089c54923f5a0a59f4197946f1ce720508ee4b7036bd38bae966bc0a9ffc668611eed1564b3fddacfac8bc76a3a6e522ee506a4a61210ba8a934583b11e505864851aed4fecbac636bdc9cfc566782160892b49072da3fdd234d01f13157e9b220a05ffc9e22c1f5cdfb7ba6307e05b67bd91be3c101bf69adc1975719ff674db21b925ed558fde7ebf757ffdbb2f4a8e74e7906f0b6d6fd2ec7b8f70f69603f28c258d92b9b1897ad1ea77c6f45d2c514d9fb7dc2a1d6fc532ea98475ff2e78a12835c46ccf518c4f5d619f63e5459cd19d530fe98f6d6668787a409f34c9b6f9f3ebc117079a9bff3e3f17fc4f8be27e6eddb208c7adf7d533dd8e0d83865a5b17691b8c1b5c1b3ec5a9bf53bc4e49e99deff58139e385e7883458fea77b7c2a520c6fddd9eebced08e0d8ad0d751d2cd0f21a34f81222cb239c8f5bc1ceae9c3c53bcfb785d763340a63f2f353de1330da3b9d99398fa451d3a9d4f34d7e0e476942a1514cb1a8c6b3fcc3d9739d596bb18cfbfe75ee94428703429e3095ed6475d77f5b8064ffec2156df04379e521088815f56737c29718b34ed6eab0c7d8adcb52bcdf42983b6b190004780b691eb49ac4bbf0541a82e8fa9b683eec06c85404d2e1331d677cc4cce84a9815d48bd6dd8b1aa5cb1afb233ff3ade529456aecc87128d476126cf558e9ceea1c75c45ad7401f7125a5fbd56c2f7a318fa48f9c4d25f68fc395d04c9fedc1785c453e7735759973295ca2105e54e4eb885a6606d6be622420cb51c90863ee84b24ae8750c66c3a5ff07c36279c404713abd40583248ca92735a388236c039c6c37e6db02efa9a406a759b5a939f484ee9ce06255e796478c329fb087ad42c9c37d9136bcf1f3b146d1176cde0e5170d0d882cc3cf93fbf55fc9a151dfa44264b8241e83358d891875a844283cc17d5600459f9ff72afb03775a7c73e57a78cce2fbefb2d7781d284be0905ae80a0ff7ed71b8fe014bc63bce701e07c4c4ad949e84f6ef1094972b02a4eccb1782e03b6e85c35a27c1093cbf0d7b47217f29a5aa509fbfdb99e9d6364001c823ce6649b4154e721d4370ae7680ae25136ba1a9e1b92ae4d973980107526b08fe71efc86cf984d2b43a0de3c6912408d311c7917a463c3b5100000000000000005fc70c0500000000d57464a0b8b616f922efd8ac40246827488235dfbac232367122926d36ab0e10a22fea7c4863f9a3fc790cf5f62e66a8144405df4e0c0384d8b82aac28fcb7df1da7fae4d77c257c79eb42e3dfb09f38105bac42bd4c056c5e27a5a6226b2daf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000904e78ac898ba055a11aecc3368e7165bc6e09ff770828a3d692ae9a7f5644d2ef9f5ba7d0c63757021fc3f060b0cf92c97d8099f7ae1480f3c0692ecacadb6d4f821c1e057e613c1e28d800dbfbfcbd7cb18086f5c0c9105d45ad1caa8cf7c3c711470b96a63ed8b15ec0d467ea37df14c0ebfb7d16d31f6280ea444f46e4320329beda6867da9b212b9f48364b4ad3a5dfedc3a5b300bfcaecdb2347b6da272b03251aa9d89714439a03394d00cada713c1c082ed2e387a14dea0c6c5f117d13a20a01ad843d24d2f285a2f79bf148b5058546fde2d1274ed39eb8fee83c1e856715a1dc680484e4fda5fe762018b08faca4406286b8df3bbdd48dd63b0ccfab3a4e03099c71f699bfdb26eb326388435194965126a80cc4f080464161ae75cbe0bf090314b574b4b90817e3b03edc97a5b490b066c45282b399fea98ae33b3e300727bf031d20e071df3f8ddb25d2259431e15b764e07e20855f20421061475a4d3139ec0021a3b13eff413cbb0f0eace2ee88ecace05f100323a665d1bb7436ee26d2d0a6c0324ec29a8685b7fd866388c4f499703fe595c274fb12cfef25cc5bb3ea5d04739ca9b6d101d64821299af876ec1f55d4b5c5d971babd20920c3d7f3a980ce1e662adbf127cccbfec247f4126b3adb632f3944172410b173e1a52339bb1e540ae9bacca744bf38088901240c5ce6e3f80a654e7d52fd7573ed765f50d88a20a8fa54a899c91d3d2838829399544df9fc87f87d4ce6032d09107f1974194ef9a04980c664093e09d2c261cd66b6bf79532bbb9dafb1d71c71652a2c343dd79ed4e7c1e6cb860e2878c105480c02282869e720c18c3e3a172a318b2e08337bbbff92cf552373861daf5992a58b7a1094e086681cc820ab95f3a85d739bdf47f44ab6ed97766ebac005dfbeb68c5c4d33b00229a2c43f3bc39b372d0386fb94ee5ca4344a20fc14ed27733ee35f78903a1b12e886d785b6adf89806d5b424ae2a60e5d86dbc7c3ac8177f623f8b3cc866f410d3efcb8609746cf42c0fd1c717596a4eea137d4dce6eab5ae26bf7ae7af6fc4067536291786c827d19844e00059528b740d771c4f60626e74cb8f750ad372a83b4032c6cb4507bee1b8164154f130edca792a684b2f2a66d8c916e7eaa7c91022462e4f17989f4cf593160c813724001f03b5919442e6a0a9dc548e1d560aef669c0130681d3bb788d705618532bed1b2d660e6789a0b1b7b6c22ecaedcb176718b027f2504435a4b32ab92baa0939c5d359eb52ff33d0e909f676ec46dedfea43b718f771f3baed337fd9e307e1d9af51e21242c74d40da347e5fe4c81c9417d20a7214912050aed6517c13df2d8589401d99a059127bdc3b7780f7ce8e7351c113ba3a0659516115b5b6d6abca37ae481aad3459f32c5727903d3705e7888f2b75ad3fd43c6f99ee3d0a35bf5384edb2a2f14d8630b1a02e45d139b525ce465402ff5bc619205f125b8c8564099989724759df2b83aa53edf6c2bdab9c9e4d1aaf9d81c7c034b1ae513eed40c18914ac5da6354442f87bf45e8d6a0cfc4612739cf5a5ac3eefcfd75ccd4859b48a31d6253e34f5857c0c96c69ac5d4ad5ea67818399e00eb343a5d8d1217998242f79b9df025246065a851b7187e1531cbb5f134ca7603368b8d4842de371a649a2e4e3bd282660fcdbdd16833737630e5c993d98eec93dd0f9a7551828813a3a841d7df6fc9a49debc6c6d7397b4c9d3eef50c402e76f7f0a14fc5c15fea0bb663ff782a2a2d204994e3e1c2fa720718ac3274d9ebb483fb44a6cb9ab0d2dbf6ffb095ee8aff660223045a16e0a9b88555f1e1822b1a00c57e9e51efcb8d6ae656555ae08a34614b7be4d4449aeff69f229d12777503f0282153943373f399ec24238b52bc9f0c541fe259704a0b8c42b14bf8251d370b9c52d3e10c9dff065392a44b79181f2e312e0edb5a52807a9a17ec1cd77c648443fd5a8d9e35a60a4a370e499520721dc68337dc229dd7e1e66b309795fc74e014e8e9872c09844f6903ce1ce4cfaf73dc9f78d425ba7095c8085a1556718933425dce600a2cc6d9540257657cebbab77c0701e0ebb4d539a517931376731ec7cc2feaf87806632bd2bac81a507433ac44f536f9a9c05d853dcbf8e8b0f775a2191f42fcca02d6f6b836561f1514c8e8db834cd60b23a9001f8d82f229bc8fc9cea34da6ba6723f81a33a44ff253c68f1c6c4bf056f78012e79fc4772d1b1f11528e04703cc8379b6af67a2a0d0946b911409ec48f920d2f51262ef4139b05ae017be324d9dcc3d1eebb90880d4002fc185da8141eed3dcb12b55102f2e25880c89a2c4db268dafc25f62b084ab94f783d124da0f134b07dfd5007abdbce7b42886e49c5cc33e9e483710c1d7f9ea6bdd03653a5e3cde50e0e7476d0a", "ac", 1, -1145859658, 0, "8a2c637ee57d8bedf2caff01c20488b5fabfc4a38e6585d3b883894468d6da26"], + ["030000807082c403024a7411500f5982849bbfd24462124954c851d978cd867f2b20a61663f41f20b9010000000465acac65ffffffff1a990d6104bfc4e85e8bfdcf5c0bdda19f7ca458ae6f8e8a848324247f7e66dd000000000265653c5f3c4204d782b404000000000551ac6a00630a028602000000000265653228d80200000000095352ac6a52536a51000846010400000000030051630000000000000000030fb0d302000000000000000000000000a4aa7488bbd0bf7db1ec73fefee568e877fb50f55b2de104f140e43dbfd7c21e0eae010ab9bf9f32c9097b419fbe105dd204be8063932857a332762591190af8e1dcd02656bebdb7014a6fedc4c028d939962d9731f0a751f6fd6df651138faf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000986f387bb184cbe7871ef3561bce8f04523540c00b5b4a1645abf75e61ff67bf0be349aa9e15a3521d2e76eed208b22fcf0822a75e03a1efabcd9e9ae841e83a2e17dbb924a852d6adaf20d15b96204338671e8dad68e6a1fdffd96f97722aef41b3b162d2d7af05ef0a1bdf042d8c74cd91cad646cfb94cc8d835b3944da1ac03171fd89e5609e7ecff6c606964dfce35af10e78e13e5c238539e914f2157844e021253ee2409652ddfd587fee4e427fb8040e14be85ac7de59520cff87284327eb0b00fb0312b439b06f777165d0e4224f81d670cb93e4760958cdb7327abb36529215e6ead094f3c2568533e5d679324da24afbe2ab1548a9034fc2348dd15c65e8030c825e57f25b1194cfd8921758942975f02e98c6697da5bbecd521bdcc9a6ee6022ed6c124aba2567aa45e65ac46e332c219ceb59b53f3ef6b657adaa7e6a2b4ad02036f57b24d1411b79ec5604426d00e389443a82f746a09b5ab5d449def043a0402199ded7e4ad4697b8cf1854763d77636ddea3ae5cd2ed67a70f2b7f6fc0591ae022badf7528076583b191386ce00a7049c18b097604caa808c997fe599cab7e9ef29c4b1785bff095080ca6b5e5c1f335f7068590745203399ef6f1740d39f0e98ccc83ca54e51e10ae54ec42ba7912211eb886c87c08e899851c3eba47cfb3b1055e202cfa2ac22a9e5a4772b912001e63025e92e1e29d8d3b134b87064e3aa8637b190a38b990e004ccc5b32b662d59b78b3935343498ab779b74932244daf2ae2a42325a724ec0709ce1db403ca03d6483369fe0bf505fc97291f2358cfdb93bcc8dc0fd56f9c1179f226b377d6ad9ada3bbd70f4d14c3204574c1131c9fc05b4892f58b1a2b3d1bb9f19c7826230df473f4ee7e991ee22cf5618ae216a45da04fdcf9b07daa60cd461fcdd17fed4529e7626d238cef3f7c1fa31a00a041775fda6b942eb89537c5a2f4ea68399758b60426db7736cd484fdb07e6abe33d774d93565d31b35b8ce1e3d753d67c3cb0910fc73fb27a99f84a5429afab5a5899d79faeafc2f15cb86c668dfee23867bd1c592d2ea76d1e481cb65bb1d41532cfdcc32b97006b603356b976d0fe8dfde95db8417e3c186a273f354f4b3a70a1fed56f62ef1a8b8dbef3243e2e1a5cb602e72a10f26be42f9e52fb8e1a5208491a56d055086fb73e05bae35ec2015d557a49ce88901287075e8181067441f8a61ef5db0fa0a9186b06daf5d4dbb03cdb2fc110b9ee36fa0c91c518a2f94f262a762fdb26da207522d47d4d6630ca105992378ef38ded02b4f3184714f67c925116e3e23468c3575fbec8cd13179250eab102f3af6c99f85f430cf398b7e2fd8a3faf40ec388ac77ec8e0c548f0a08ae84d120fba345a5e16596c4da3c4e95ed3a1467ad844fc24f511f9e1d26833a792653f074a5f7e045e7ba1c0fdbd9e45f73f71cff58c750c7c739d31d119b3a6858e47d8131ff181d7bc78a741a7ae6253973898cac2a3c2ad5e17d3b4071ec1f8019e79118af2697d75625c88da0f9ca2d550fa0a3bcfb645f803f8ca8eba233ce46ff0054a31ab08670972bf10df231c706fe2ad467ac350c9251567e65b1ab83ac9b4119d5f1263c66d3fcb1b7be5b3946427e2fc496d0665cb915e7aed0947fee710f828060a7eddce7ca9fbac7cb840dfc10a82e788905beb048996a5547fd4e77fdb73f58f6c08d70c7d62295342a30e2cf76256e0ea4a63a6693334204fa2d09041858ad3832eac0999aae8441c0edede014e86ae6afa1a769ae0f007db4f9f6cbf5dea9e497155e0a8d4aa786501f51d901965f82069ade39d5288f1330240f6cc299943ca7b7c7769a1efd5edcf37ed2e1606cec694601062bde11ce9e09aa50494092765b6aff99f274e5abe2c31f4ee6ff0daa696dc8d8c6f7e129619fb1910e50bf65962f7cedd57c8da1aeb3c104c73ed0f7499c4434ac22a975580f9801993156b11c9834e89e58f024e68ca3ca745c0794bc385999945d76c189ac2567140f3dfb8c15eaa9227f0a155f3d0cd6161e12815728f42a9323a5868f96c40139182dbf4c6c2ff13dacb76a01eeecd5d2f47c47e59eabc7722980a5b501c7209b8721667757409f6e235d5c3cfb23a5a24e35b7aadba7867735a1b26ef4500a89a525593469599bb7dc249692afc9507dae6f92ab2bd346025cb84b12c43e8aebc69d8538622acaae84f138dafcbad1f1ae521c3f4fa2217503da046595be1e325c219ebcc829d9e9787fe609c7fc01150ec2f8c93ef04fd66bcd77e2f226ee0000000000000000f8a0a6030000000075f24809a5afe4bb4277a6d61b6a986e60acab2a9a9809635279644cb8a31b6cca921ea9e2a9709b045f38897c1e3ab04c881540eb1f33f753fd7fb528fbf142f6872880a1750f79907a82929c7b50dc0fd952630f34364c61f42ccf6ddcf03c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b3a6a7d8476b36be8f510e33809681ea56467f797746754681541e5e8360ca9871bd7e05c7841db817a1c6b29075e628977021525e42498d16224ed22718a8b8f6f67c8f469ea24106d0ae757f8d84c8875c34d4b12929cb1cd5191255f5067d6f9223e922497836215cb3331b3361b8d5b6a2c35a4f5543e707cc27935a9a603053c80a85ae6de6583784218ac180aefa1488a4678ce58fbe5a7d3c35825f9f1022ea681acd9277088935b68f72cf02050683128911b5899436dfb6e4cb661c4590a045c75a5be251c7339493c60da3e15cf972d9f23d48f6c009c4aab7f26c62b270469c3cd0d06e539ab4ce88fb3510f881b2808cc8a9ea8e95cb94dd51ea90b26030b41ee9b0b1d858b45a836a2328f2e8880a59a2183b44faa0f4ff4e6df6c94e10328d923c59ada3f4fae77c8b335e3d59fb5823689498e29fa52b5de1cfa2f3b0d032cfdf8769374e66337d7e76b68231e4183a8005b3182ec3de616f7706a22580a032e63a22418a3b2220b290606889b47520530c6fa560e76f825a3f109543c516f03012b24bc5accbe250ec5d4fd45aa3726e794b32a98618c90ec2e98638314401ead43c2121bc8ee52cf779e64b3e97821a1c9d8fdaf116d6c01b3251a976a4c29cf0d0c4f49dc786a7d3e4fbd6bf779289c8bdb384268b833af7404239fed3cb1b0ce1a0ce20b8c72d86dfff30fd2159c368ca484c4e02237edcaa465de2529a721fdfcef4a93139d06892988bca349c6c2d654c59bfc6216bf986892ef0b484163a734fb4468f7a1c6fae2a9c8b00b04a8f3392c4a0d07caadb243ff482efe1023b40d467d9221ad17f5a50ac853d2e4b895fdfda46bce1d629c40f8cf5823db04643bb38e6002a81f5a74c62f3406e25e2ad7142cd780e2e279956e9d597536732425fb807407bee52a2db3698394ec18747de14e356c072a080e97dc318b38d4695473f4f1351e402ced91be7f68bcbceffec4afff680cdb832ec2738f319cb1df2af9ea2bb479b52b43d0dbbba62b1c6f380d19db129c6e7acfbbb7349e86c2515978a4a47fc4f4917dd21eafef8f84363b8c05adfe77cbdb9fcf7177acf2275d31d505023622d1ca2fdb63fe2bffb1cba6ca4b5e2ee0fe441fa8e47a796c21bb727e87cadb82e34f3ab4f6e5221a6b282b5c381bb68577a86590a0d43467174f9956b59ececa800a03040f5a2c0ae83077c536bb662ce9c4ffb81c8522101373bcfe8b66b30c3df57ba33369399988edb8fd5019aa87dd392223f70f254fe821840ae06c839589fb6699eb380c28f1f50c5e2e2e663589b63b5afdc1958b4ee707bb7ae38cdbc2f3ac635354c9af477aedc928cc269c04090d443f8b1262f799ff06f15288c64c626a10ab71c0b47e8473c16955c995d258d10352e3cdaac7aa940047c9e5843ae4e57c942a6cd12b67ebbbcfa6ed6385ee9773fd2ef650747d24b3d18308d2037830d5a57be30edaaf1a5b1abca415c9898d9e94e3dbfe8cba71d863e7c6867c4d3b33481f0f7988bb3908a95dbc8c29419e950dd25c3d8739e3d80c0649afa2c992cfcf13b02d91cb0961a2c4e5e4f2aea332953f1bc9a1756d383f2ac310a6ce185b3520e5fdc8f8b7753cc5e79e803ac7ea408e2c328fcdae6386ea3b0024cadad7232c8e3b19cc57941979726776c94198b212e57b37b183277d4dc5b88dff470f3d2c4170d0a6c5cb6a927e58b53c194d4c0fc02fc55bde1c0d6bb8b0d04ec1c322016a78b891f562585c9acf803368ea356bfbc25b62faad035dda542ed37211fa4e6223e6fd6caf2ee392d64a9e15f6cb2d362cdf1d6c2c91460455f463b8e6a15e64a29c48d26e5d0571aca0273ec571ac474a95745292023664755550730e23b71bbdf7fa9793f164a6cd0401d50fc5d7a6172d5d446fd72cd8625ecf2f359d8c81d4545f105e3b81d382f8b89d11d3cc37d387b53e386bdc55373e22c78ab35cde97f81e1ae94e924e6a3803b47a8d6ffb91000b26ded02eb5033cac6ed524036dfa077015a3058e4b9befb64f8ac94ac8d6f41c5c3275dfb35a03c7663e10b5843a88b2e2c02a1ca366ce9dec87e0fb8a79a210a5bc8d6bc1af1d35603ccd345a42f78b01cb5123dc0773e5ed504e33cc6e0e68a5b1e1444f5dd9e7d22635b8294f8390d28da3985a147463382e095e0bd70387ce1469cd6530dd4e3d4dd4e71ca2565b605fba4f14c85b9232ae82f03970dbaa2f693e29ab9827427f1902bfb6d3e0493c2d788ec50ffde9f11be265ffd7498500c3d8c6b1a8025439e56c12d5e123f10000000000000000b8c6890500000000d100fcfc317f22e645a0fac59faa18118768c507c63fbfdf4bd882f03fceae774a05757a720f134efa130f0f2eae24c7b8c5e6977bc3dcbe63a7b5a6f0af60d306ed623d35203ea3f692e5d57a424ab9b6fab12718f779ae72a72c37e9baa769000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006c2ec908c7955507edb01ee4663595db43fbe72e4cada0cb96ee58edcfc8028d89c3dde305042bd1a1ed50ab00147295b33a14f231142fc51263b0a8b4e7089a45c905930c381f09f081ef130fcefa45a81dc2614a1d4576fc2d13a03e676386fe4d6a2f3a795f0beeba42a6e5370e96d2225854d0df42c09a5e32d32df4123702210fc754711ff085946403245a221c31f540729438466a550de2f5f1639bcdda03040acfd191fbccbcd3ea1f973a002a056d6cceeb86b04a2c94527bf1c7cb792f0a036d538d0e51958d325ef202e4e165691cbf692a7c412df1b2818730652e54afde69f4b2125f1d3218ade092dba37989c66bf9d5b74d062555ed5f1ec82ac36003268016aa478b1c4b44f2a29d0fe6ae7237915e1bce2bda8e3a22a4dfd48e8a630313b1f194df63a6286ca1f62af237d278c0fd7f4de31993dea338dd8406ce40da0219fd67a5488fe47acc34b97385348c4c87207319ee934573929862b83ec651220223ed3ef917dfc8ceeb5d0b04469278ceccb4736e3c85eed35d47831e0b03c9f2022d6c5954cfea4a228dc78f5b51a48366a8739102d2488bf3b989e16bbe02afa1577df632e13baa270051ceecff1564592f6251c9f615aa2b818e7a7ce406739983288e5dfbddca8a552ce7fdbd49b9baa0aa9f7505d65c6a01631dff9f9eea42a96b76b075a2dfb6efb9a536db7f23d733b5e28cf48417c39a5240c9277aba3795e497cfbb3704fb9686b326a1b755bb54aef58c71ce40b6e6bfec27d3b8e3f4ad90494dc5cafe4c07e83920ada2d9057275485b59bc6ca8368e9d1660da75f6a5edea7321084e7c1a5970ae6d3f6f77727f99649ee33c766be545b71702c71eb7be1fdc9c3faa80e130846d103e6327b51c298766c92414adcf986d36a32925826d01f4453f548bd87370bc96e71c4d5b999cdb99cb597a092badae5fa1e1a32cea392c94c3cc2216033051f995996ea17cd031f0b9a3b7522a3c3cb197710a720d8812def5c85928777d1321298441f6ced45f243cd43f88b2619a1916d618232ad78071734de30bdc33d0799e78af5e0ab6354ee260e456e36dff141c076144c395eee850a1fc7ffb2f1f675ceb8ef3145ab9be4ab3f134a4d52001bdd71ac20d55a3c928dc87c4a907ab91c2edaaedc8c8a89a9f710fec34ac731f1c0f73bf9aef353eb13ec982517db637c955fa789d1a4f491dc520724c6aba69c18eeedecf65aa09e021962211c5c8b490420a0ab1ae4c51789a39def7b74a90eb1cac005b317d6c9171a745880a7f61761b68e501dca82ab94160af5e481de0ad2ebd12fbb197efacb06915408a791e79544f5467f824ec6d09720dc5aca2749fd00bdaeb835d3858aa60e463850337509e58a64a7ee5646da813e46443fbc3042bf61d559c86c072c361a60897ad04e47f2d07bf2f976fd5c839bc06a2b7b630f1584ff4f8a863fff0fde8cb423d89132034f655f64e156dcafeeb7ddae37b6e7981ef00cab5854c2fec1703dbdc456a22f6324003147dfa9eb03dd8a545eab7df6c5e99c3061b9018e71ec1c51cabfcd10e354254d3c987853bb5784e84d3e38342003c1caa5f6d3ac81e4aa278af4e62a5e2ac29b39c0d0db53a23052090f519b00b1bc110f930bc578fc4fc45555d4d712d5017835f1d6bfb729adcf50aeaa61427c609f28163d69c5cd9b6de7456435788144cbb790bc0bf467821dccb152d34eedc402bbf0949497fa2db335f224e021c72660ea14a09654a2131034b7c3ac6bfc8a6e85ab18c1a7a2b21cfe424ef95a40dd426c33f4adb3a081e4056fd55fc461da5aeccb174b4ea87b58fc93e547aac8e90bdfc99b9c4c80c3a3b928ebf7b041d550fef79c83fee2f5b1c64d21a11d0d5201a5b12fd1ac943990b023226f9750fc019f010a0b2b9d976eadd9cc4201c0091a33b39d7c6f161ca442c23187a03072a0899d34de0f5416e5b450eecca9fc74075d9d4305baf9f1eb47a61b45556eb701f069f5733c238a4c4cb2e113e04824d3b218ff5231e68d317465a41443cf133e26089b6618df05eddf2e68ff7be8adf3a5b771dcf614462df5158718ac1b084ed86d22a353f78d0573edd43eeab46944fd4955a2dcade14b79108c6752a0e6a6c9229adb3234daf9e23786168ee65f6a491c157e5f5192263e9328437667f49071d46ae480d03ad141761ddbae34c2a82ca60feba079d55d3b05c0ad1dd30dde17b6d02ecd4fb6acb5b8c058f15709572767f5604ceeb5a5b06566af77e04eb475a41d02c23edfc32bb68b904e901a5b108217a13d119f31615313a1d381c573e600bfc78bea8c5cd7cc012b158468140e2fe3041423051bfcd490942c73c8033cc7f6248907e90692fcd8a25c6cbec4ee43163781f3207186c535faa92608b32e7ed24b48163be26b2213f32aa0e", "63", 1, -1539394600, 1537743641, "7075fab13372b72ddb9638fa99d9ab11f13419199ca6fff80239d029fc8f9f39"], + ["dc3c932d03284c3f5d3d17a5399f30dd7e15b151177b3231d49dc69f8af383f0db9f5710810200000004ac5252acffffffffc104354f8bb61478dc23ed3c87c934b32ced189c7a5b6d2fc53ad3ca52a583ca020000000165ffffffff79c4da29d33a9670be87a1afcf15879e33160eacb203e5cb1d6ff54fe80c7cc60100000007516a00005153ac8c270390030bc0b8000000000000145b0c0500000000026a52a27b110400000000055252655251793069fa00", "63635353655253", 2, 1405937753, 0, "4e662bd48278a0c01191e32d955a374276ab30255b4afdce21927edab80ee2af"], + ["030000807082c40301b632bc9878af44b4c14cac41903617d9e4038d719b5ec114726ab8db741f5a100200000008ac536aacac520063ffffffff033e6e560300000000066a516a6553516ab8e00500000000056300acac006aa6220300000000015300000000e5c51a4501aa82d205000000000000000000000000a2fa7f605d4f86d6bf2112c9aa66e6b489e5795f89a80e522e173a4cd3e60e973aa1d838cb253f1a59346e48f38f1da2fa9813ab7f1d1b510e2e6abf0e1dd4438a55455efdf512076396703e353aee51ac181ce5e6ee518426d66f0b7814ca1f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000766ce6223d657645623314e7c681ddf6e2cd8ea67f7d31e7ced4eaef0cf9f65b41df4c96a1377e9cf5247752fcc0462d7c4afaf54c6691e01f5bb6e69862c2455af109df283687c41aa562c986ee4669f922eee4bdae3120e7eecedbbbb6684627acf80ae63ac8c35d3458d4aef370af2aaa162e8ecff72f3dd3ce257225258030a9fb514ef18a1892a98c19abba2d91acc726a091bbe83c37ffc2309904cb460020a586746ee76464cc44147a7ae12ade4ef1292ecc16de336f8c888be10af74230a0725c85a26e146464dc9c64eaf6d65376a7c294484cd2e5a3eb4187f3e29d12bb80bda0a0501e0bc61428f393d7725e926f8cdfbd14ee955ce0890afecde22fc022146c1ef12098bdeb336237cf541fc2c7dd8959998d401fe79fdc5f92b6d06ee022eb37aaa255b61ccb455c3d00c8a6beed42cf72b50ce660e1b52621e31e2f645020fa12da3b001bc0e16724bcd9a0e1005ac0fd01f552e74efd2de673ca4b3dcc3020733d230f5432bcce61572fcf3c1401d8cc6ad5b42f3ebc62001d559a8eaebf00205d9493bd269bb4d6fdc6c85656ac2f96fd3527b2a595df6d3a048a7fc8feee2817890ff93c637185b64a4e086b2ad4b0b4f37ebcac71549fe7f3b692d856965d655ecfeead173cb0847767cfc8823773b76527fa6b801189d4a935a115623da3f75458aded360bf6ffc00c237532536f8a57a2d4fd0fafc142a2afdce8b3969e30900a8f7ca73777212fb35a9aced8cd0a72c57228d456209f5fccc804dc01c700488e570b9708712e039ed5bd5c7085f20920873451653b646f7497d8915e88e3720c960670712040ffdcb7eb2f92db4c4d1d64bea659d9582275866abc58832a5506cde0b6f79fb5abbaee3cb9e45ad98e1633f7cff9844f98e2b7a0e19fe72ba71d8085809b3dbf682e44955c5391d55b5e20b252f07a4e65344541380208ed75d140bf5b815e193093251e5c7cd9ed994cfcbfbc85efa00b408d25c44a4bf3c24c208403b61ce02d0553afc7983e212042d219d5a2960da8f5d5d23d8febfd1c18d5b1c8a4bb020495e134cea9ca86713f4640010f0f89b082b866b6e446e8b1f9046fc7051ea0bdf3c8760529d713e710a8a82b2bff38e2974bb2e3438c5654188abea3f7e8ebe59d7dd000e0c43399a008107a48d9f240f06c8ac9ccb506dea1e9fb10b1d3c80d94a0138e1bd1073ec1cbc98fdb4035571e8cec95c2145f9138f0a6912b014a5842b4fc5bb5f53ac9b1c380182ea14deb96003164fd8f83ddecc9b27a7a9519e1bf0e3f939479b4be48d972db1993ba6efe82032f5e84ab7361574e0ece62356a7b09b9aac655e3e6df216c1b20d5233ae4c8702da8affab6d2e11e8c412eceae9352c3b81d15a948136b02b6dd649949410d4ff9424251bbcd054ffa6f0f808929052c67af7d6747b9f278a8c5a6b7970e40ac34c10881194120352dccaa39ad55330ea1677f82b1318d9b7829d298b158e16180ba482c453d7aafc9314e5cb6e928df3f3b86561ce2fe51ef1ce58c5992efbbb813abc35130731cb9c1db788328e101b14d7cd920d528b8bfec31639d5ef5aa0768a86eb33e261f840ccf74cc77748f96cfcdaee164c01d849cd535ca0dac3c24f594b9cd77d969aa658eb186998901209b7d3ff0fdca2866d11fd5a94b3731a171272518178676e1278587be932eaa7201be46f1b15a5ed711d4d7946ef3962e9c90a09b1d0ae6b729e124cda72fe3c92e862e3fabe8db49964a60bae8ea7d449a6a75acdc986025957f349203ed97dd8eb7c24e92cf989af43758c641af09b5bf78eae5533435ec7db050da80dde1718ade1f453f1a55ba3d0efece494a5258b314ecc3c58fd338342a919237e3c2ece83882647f4acb7e7dcebe09d8d8c4d163892c05379959adebde0e9962ccdddde5a6ea5071f3fdbffe073f0fa04cc5aae39bf820473aad7ea56e0698b6b95dca58288da2d335275f0f6b570e2b7f5da4c12527c809b78572a2fedfa2caecea40da0c4b6123a5d3e583a9c71a8fb7c9320c6325298802a2cf83de4fe6e6f97d272f0a5f92ca809dc49a86ce6af9cdc1825e68b445b0e4217984f3700623de1f8feb36a00ccc323d3db545fd27d03ac37fd41af608bd8eb7929f2929754b76b7f0de13723906d768a5b3aaf4292278c48265118ac9085e8f11eda3c04d44174efa74192036fd5f9e9d9784bfa5e233706898caa7beeaef5cb83a171128bfa0f6e5c9862043ca0a72ace8e9d99f3fb017b7af4d299241abaef9b8bd56e00196548afcd8bf656003e55cebcfbcf7d2274ecf55bfc08d63e1a04033bd19db7a987c54d3df8fb2b5472f3ab69671e1efc8112bfc32bd363ce871f46aa814352aab9d8e6d6992aded0f41f6e3ce0e859bcb84157206342becce050f49458e8180e416e444ddc09", "6a65acac65", 0, -530693294, 0, "192fe079cf4d307c3afc309c4b83a37f16529ffd3d9e345de1f159761d8e601f"], + ["030000807082c40304277f5537146ff9f5774cf45f45eaa76b4aa7542e6be7da5f70ece7660283cbe1020000000265ac52b03cbf461a5ec2de5d3c7c9766d6c08aa2292b4404dfd2725caaf964510851e9dce43a0200000000ffffffff2d6f20efd63bb97aded0c891c347f1dcba41924220ac5ef488a38c1289c536530100000000ffffffff7f725faf1ffb5e77193f9dee6c43e257273d52a2ce99410957d8664efcb4e1930300000008ac635251ac536551ffffffff03f408f502000000000353006ab15eca030000000000445f930500000000056a006a515178d638170000000000", "005300526a65ac", 0, -1326419499, 0, "76ad0ae02828e3743987ec25640f9ca155e68adcb252400f8ad63c6e8fe42652"], + ["d4e40807040eca4df267c39e55d359b1d78a2e3c5d11d4a2ec01ae395992e27a3247744277000000000152100feca319dac5ac0c40e71b4f00f92deff14f5265b811b3efdb8e385f6bd9a0b2febe610200000007516a63ac0053acd9b0da9f6be034ec529937cc32d73b777c939bc8decf3181005151a584bac9a674dbb7050000000000422007fa94b6e0ed29fce1e0f296e59c7ea4de795009f0d647d6a400cac1bdb9794a799e0300000009acacac636500006a53ffffffff011c940301000000000652ac00005352000000000135287201000000000000000000000000f8c15764ab89ce1649df3c7b1ce6cc32662da063c04179e5cb3f5e7b6e99488886597c210599fc3649773d350d90d946c645bbb47a13b06047b9e24cde96a2ba90e55d5991300a37da1bf746df9a9ff02d4d1807494c85956dae8d3621b1d97500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000843c33982988d7d70bb6d37d7b80fab003dca34806f0b191703ba0ba75e35101736f9a1e9fea08cfe3f36f4f910236ad9908b329d6132b0c25a4526bc0c106e99793c8db1a9a6fb2155f882b402565beb48c1e95a64e165d5bcb0be2f09a71d6cdb481515fe3995d6fb37062e09625a56c0d65114eac81cf588db02dfae5ad89032c867d7c2e31b2399c4e6fcc9452c24b2de3f64140a76a78583c1d5f9083a162022a53b019de0d79ff257910b1c3258903772645cf17a4f9c83c3db069094650560b04e33233ef1b01d9b867f7f0666b3837cea333adee8594076b6c016b9a3d2df280fd6ea047d850f69ab90c710265da596ac45c703131d206d7e320b36c7d8a4f031bb5a3e4c39eaba5b6a5e155925f565b8828f81de91d9648548a7055cb15738e0323e0ee4994e5b414b357f5d564d8bdfe2d65743affb52ba495914ac7bdf2f18f030b60bebe502ff0efec447566cf4b8e35a54dc6ba20659899dd94da52909d398d0323638d02212f172c7b838f4f6a27d4864f42b877a15c596f918393ffd0b93b60032db875b26eef78f2404e605cf52aaadc509550dca356bd8b3d51152fbf4094fcd0f8347a3f0d51d2567a5b8d8d0915ca0b83465a988af4cb38552a7d76c5ed60bef3889dee6caf64e25ac9b7c71973c2f77238777f6e3360b23576edb9d7907b7cd56784a775c0076c3244c287ea13b605d47604922d06e9679502285a5b74821a0637dbaeaaaac673420f80b87ce7082e43864ba64cde121b7a690a18e43309a9b85ebbbfaf6231d617240777b29312327d73a812ea8188f0b76bb3d0fe436ae1fb9abc5181dad80d806c0a0248ff01eea641fcdd51455e60e05053318347d737c31b7f7965e6e129335d8772ace68e6df689e76a24cdafe7e15b3b1b9a27af4b1516b8875fe2340508a9d8eac3e2b70e742068e1ba6147097932432ff19d071026eb602d17b01ea771f63ac0500987497e3aabd907e43de7629e78147d10a9ffd5439030c6229227ac7c62a63b9bbf7a6e1d4ea03273ae42c1d1ca87c40af663a1d5b11891dc729109b799576dc21af76ad4361866ea0b6122506e6d7fe6e9758cc1fa835d06486c4361791a30fe745b9b5c23ce67232ee2a41e1cb3c430e318a6f4ebc767a8eb220eab4a50f619419dc89e25ecc0c19405034e9e06661a651e779d909e09997a68c21a05f6c1c2c553fb3f17fd9820745a6bf77c1dab65b2547375ce56d8e8c6440da349fce6ffbff1aa4acee959c0f45bb0faba9e1ce177228dcf42928970edec8ad7b478cf3abdadabab225f86f41e501e968fddf7a0b8f8c08ee38cfb494449040f82ccfc4894872f3b15e165e121a411ecf21edbfd080d95d20d28e669104cc1a5b98d852e77a3cb6b7fcf5913977a93eccc65eb345a0c7233e32870fe07cc940f16fc3738f6197bcdd01f5bd6050b0988baf9d6c6b514a9924d81a4fa5d1ecad9c3a29e7df3160a81de57228252f00799fb498148ad3575c5694116858f4b2a79d76dec817f09d55579d1ca463a89b1e0db56cb27a2826ed81b5b74e32b2826582b6d44eeb3050980af5376379e80718ce7b2c5d648bd3011d38c64a87577c2926904f90f26346f13ad82664d0fdf4a7dc1e6e28b89643a9fd0450cb6e3c98173c3cbe727127ff41031411823cc9873c02486efb77e60739e281b3271a803b80951b2df7f9fbee65ee91ba3384f9cd27dc6c4bec4443f69cc0683f953454b08e9479c2280796f8af0fede29e6df02475dbc7d80fa6bb93e36f5a0751addec62624c708864b8501640f63d8dd1f3b89154226fc14881c1a964663c2a2ecfb723ff824c4a2a296490dd11d80b88f88279370c35810e67b43eda3bdc4f766afe8883a9bf8b68271e4cb22ff6041e33440bd8fb86a87a2e611ca3bb981fa28be8784fb66f494f4abcbfd69d8ecc7fb319eb505a6f50ec37264f72e359a04426d8ad5aa7a7015f973bbba2aafb44a4e4284f2f7d896440d869385677d77a05fd61e549b2968da6b0604a4748e6be74548a35e7f4dfbe2b76c4298040112df075d099e37cad53341166a1771c2e6cddd5d3d8324e8acb5ef3a89dcd27f749d70c94046978dc7558eca8fe7c27bbf11547b4c7d90845ad9640a0f859abbf50915a591ce953b633589b9b514f3c28b20d423df2e1d025c658fd98193e3eb9ea0749cdbbeafdc2c502a962c2fe45319fea2ae5c86b3792f22b37acc1e5d4f8c1a1d626ec36fc1fc33640c4ef3a40df7b61706c2201c02962c934500ff70d5c3861c14798bf74fe96ebb774cb9c7db9285b0042ccfee14e3c82da50e116fdf2a843e025aa326b609145cd27adffd6dcc61014ba7f47fab6ab1453fd6e8a10b0dfe75a203d9c41792410c2fd9b73735cbd72a66af7735221802b2ade014f6cb0bdd261944bd87fa1d1ea784706", "0063ac516a636353", 3, -1812130730, 0, "8afca09d074b62a369938e369e7eb2c76cc06b5cd3e8bcd948b8548fbd331cfc"], + ["030000807082c40301ba1b30538da436a6ddf18f4c27c1555239e9112564c5c23e18881c6f95d8891101000000026552ffffffff039b59ac0500000000095252536a635152ac5383dbab02000000000800000065510052006374100300000000076353536a526aac240fdce10c36ab460300000000000000008b8cda050000000014584e5b68dfed589b3e9d88e0875a3be1869e2ec00de39f78f67682f3dcd5f6d38d0536e29c0913c9abb6198370124ab1104c04b556008b71b06b547a585c9d050c108b2cfda684a0ccbc71d8ebf3144f7966eea9cb5722e8d8e414a385bf120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b2b39b382735033087838433a797cf7e5cd655f4b2226733f4a0833064fd4072224979800ad57d689826d07824d86c629dd9bfddb20ce1a5fae40dc4cab7a1751e1acbb796440cc5c10618a6fc881dbde8f7ffed9b84fe46e91337577e9a092c3e320a4d443f5bad435389e99c4aec734f218fc8880af55677043f0a395abb0217660b86c997be4e908d8b9251b0bf2f4dbaa652ed0f70c82f9004bd2755714b020d8071d59372a1da2286f5bc9965ef1b942941e3081c4a751c11168fb0dc2f1f0a01228a08c4c312af46a974679256698454222350aadf516dcebe019122773fdd0e129aed37b3375e1d28dd2ab11a7a8701b933d49e8abc7f2c015a2881814e5f031e0930c32b844aff43c4a16613ece3f8a28a6bed43fde4ebc649fcdb7f9b805e020ebd964d86ff16cf29c348ffeb7fef6e12daa1c6d26f908b75ca43e6454f388002261a64d0825c96682d45790d7e0b3e06b27984da9cdbdb18437d898be0cd4e1502288cb7bb519dadab835dde5ea3d7dffdac9bcb07999431f29e044c2d680d4b8a021237cad86d1555af9ec6977b25d35c3f1f5d0e7553a69bf7c121905b69cd9631e54101e98650011978bd78ad2c0843e3d30a205e953839ee125f6847ca0327394ea940e4e1959389aa6580f492a0966a4e5b0dd416fbf251905807933e249148f2c43f790d8d959fbe5432caadd043f29041ef9fbd01d44aceb19fabb934f7c977bfc30d1fd9699c90bebad55f37949b2392cc1bec2f2ba66d3bb0749697e0290b5f4faa31bc529d199d436abf7a8798e480f338fab3912b1b7c4244af5a8ff9ec3ab9dc8a3a01abc6a71ba644b2a0d331093a1620158d7791c5a060b92f7475161bdd08119fde64494e709d582e630ec4c61f6425bc758f4f825ae51b3cd3b88c72c9dafae9bfe66840a753a849d3e23ecf3f0ff390abc67f50e088005d03dbf99f08f74c249df1d9f2d7ac223ce52032d1e98fcbd5aa356a45608228bb0d284a53eb9985ebb8f39a3ef3f7c0c8ecaeea18e095ccff82de6d3d1f06fb23ff1555c1cd49adb8f5e5e643355e69d920fa745a459cf8609ff4c0c011ba0db83509d4e666c08971898b96e5edf0eca165e54946894933791a256a7e913e6d99037b1de22db5fd2b942b2280830abcc27025a5934246699bccb52e11b84bff576299cbda90b9f01263d0b2e61ef56c6e769df916f870412de554361204b0fcf92458f80d92f7729da17a13ba14a8223681e31148a17d518dfeb2b21b082616381b49672e9b6e14dad3f707f94d2f68a17675d8ef8bd7dd3d32b176e3be5ad02cf13a12296a3af94fc5014e2c204ffa8c444805c88c2ebf100eedd133a594ded9ccc5861298a84f8b3a3c89c0c0ca7d2f971c671d6c2e6bda083409533478bd0a41b2e145318430b5e3f5ae7f6ec08f02fbbf7cfc61f47c92891e16ad33dc92f4998d541dba63deda9de1125f165f8147d7a1317a5507ccaddad1ca9cc4e6557ea9eff78fb62fcb2caf40bdb937524f52a7b8993c59ae81bd91d0ffee7e3235ca23bd71fa4991411de18a1dbaddee6831008b07ff396270b2c220b7d6732c8d924010ea0239c5e50112f671723c280a791e2ac8160d017f21c799f1f81e2826b396f528397622855e22dcb103714c9bf00d109f37803fa789e4a1b243d7d5149335ccae5608153b6f04e2033f7f1d82ec137359c097ab62ce3e9fabfe55f3e602b0e363b03e8b64136aae21aac86c7026eb5825eb2fb45e09015508954d68c20cb9ad2d3cd658cdd73c5bb1f1bccb4dad59ec08ebf2f2d54b4edf77799dc5de8185cc75b8c97b0162c144505ebca008389a75a006522f6aaecbaa32e321463490f944e1dc6c82fe4fb6569f7684568d03f531915011f54c6b0a42b2927aa7173c34bc87d57998f2deaf0f69a9731105a9da08816b0ff8782ad458b7d6920f72b5219606272b68d3c8a141384c8b4ec8780e89873dd4e218fcc28ab739a6da80f7b05498de13fe4fc1b3c6547cb2767950dab0e9b0c66877c5ffaed22816ce5d61d35848dc93db519f0e4c7cc6fdb1fcfabdca961a7264e53ec1a0bad9492c59ebf8d5aff02ad3cd9827d7d7402fc150fbaff6976625b5aeac5190f23d71573090ab976acaf68d1cc857ed6c448942e85d192ac5d5fa2241e38a7277daf893dd77b9f15278e5c039754d42a1f287dbdbeb2350c46972d8e1691653404761cb2e48763960c51de46429b96bb40b89df9099e6dc2e2feb30b80ad46d07ee2ef29c98aae4654936bfed9299e17c420ac15b2102f98f53ad07d6010000000000000000000000002dff20c141a7ac324ae57abbcd842a59145544abe1ddd8ef1846d1d01b7f0bd3bb45314d92d5ee00346c4b108d422d6c51c53a352193ed9f77ec52c8b0f28153c2b6e077746f60754ffbb6f0f8939e7769d6e0a9a5daba375ede770c42745839000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a729d894c1b239f6a237215889d0c0e2d27272a2b700f684c6aae80af14c7223f9f943e3e60bdb22d15936550010858392e0576696c9afa06c9a295472d8a6efe46c9073ea10fab75c0e423360f7c9a1882db4e0e2080248ade0ee32bab89ee6a5699442c562cd65202c4eaeb2058bc994f2b981e2e63294c97d8855021eb480321db1c910a5323488a545cc9fe20d79dacd2b9113015e36f77424ae4b862039c030c06936b17cd8f3bdc5b0481959461d4f0b35add8705a98bcce872df7b1be26d0a01f2acd100e53aa259212d7627c43714cc471bcb266171588455d78498830396587040f226a32fabd0e197e263fb8d2a399662d7fc108ed6f393e31f779b394a031dfa65b68fa6910583ee0cdabb4adb4a9fd38a96879937b55ffd2c702fe12a2902206bb661d9d76a1af1fe24f66d3f1b28ea170b11458e366d17704c597a85b3b5032ffc41fe654edf5b213ecc51b5b904c1e0339b4d459780169e6759eb2e46520e022535a5ad01c17e2b161d01e35e820051028edc4ca21bb69f0c334f9c6892fb7402016ba5e73f1cd61605f3b79b0306168dae1e2aeb3387bff84d203ddf9fbdadf79dc8504b52bec9aec057daf9cc6d755388a07531cbff83ead2d98632977e615a699bb79a85564c00174517507871696a4dd6c57e8ed62a24d020e13bfb626ebc385cf19ec38bee015e41424e3c13762a78c5ab85f6ad001abb84b92dd3b244e96266c0c2bcf17fda3b124e219f3fd8ef73edf8dd26bd5e7d8281c89327bb28e743d7aa87a9da4b7ac28cfe2704e42764c4947f05a1dce713fa7df2fd7423ebf5c8765603def177054fd6222d6b65ef9681e34724c4cc44c7031c55897725e810592b5078de007a595561df05c5da40559dc360820b2becc236da70928633756c006b2ba9927d425c5fcdf2a7c7b3c00d1630086837c5d8e93904ba4518ddaf07447997fc6f2760110930ab578ef3e9bf45908082fefb084cd44fbe02a901dab17e2ffed15f5f417b075e3f91aabd319f157ca20c45a7e0a2f5a59efe060d63afa1f0e2645bb602c6f42e271771262206b451a8abd5182456637962c2c475a257b7eb8103ec73642ee2478aa6076d8af3e3a9e5667ed25b6efd853b5e3ef152c844c3a28e2721d0d23c72828be45ac71caa63f11d6130422b000a8d9df33de06c8da8757b7da6a22f3f1c8c6ecc8e0005506bebfa0b8770a866e4f791df044d32dcd059fc52af5149e9658a0d6cb7635c0ba2b47380b14a823e2ad0b05375cec51de64d1cb2c8599445f5ba5a3be3a91830aeae97464ced09b1dc6a2375f5a52c3c44b61698430d84336f70b5955779c211bc9ee8c6182de8e08716f69b4aae1e79cc90c6157f82b08c1acd932c01e8fb136e479c6daab8d579e30a0072f064a398606d1ca25cbd9011c055603269f4ba3797198dd9795247ae33766d321a99b2a091a796eb90db4ea81f4f4c8e2d943da50864afd2eaaff21570544fda9e2e90bcd2d0bfa3ec2edb263dbd40b82571d1d7941ea67ad6789e50b86a914482c78114b120e3d8b0ae5d331f4f11204ca1ba1ce4ca19a6db27411f335a7dd96c990314faf36d2d054a40d1d0a801a9343fc57c253ec0e9a993366468038f6aeac5266d60c08f3a6ac6a7992990a78664ceae001d882d0354f8fdbe5fdd0786379eabcbfeb693ae779bac3596469aab006c24a41c99850a0cc53ca49136a7c3c8293c2ee43951fbc05fa23b853358ec6af6876cbfeac5cbf9a9ea11df7fcaae9e36ee85925c007fa0fa4d8fb2049473e0301ea239c9997b87268f8d06ceadcf2ca904bc593e1b02b7875765e407eb78278b32604359553b7e97e2f03bbc40bbe41c61c1a7fb4188005ca2cb135fd29ef9dc8fc6fac1773d473045d33e3bd0a4acfc1ecec51aba95cfa9f7dabc75aeb44c3d8f8fb3f7729e047b2d4cd0b9eb3b2d9c18b42a394db704594c6131fdcda75edde45c2207944645ae3cc48fd9c082f2f65529d0763490a99ee119028767240f3738a64aef3facf0c720b7a42999831b29e751e30abd2bf8b60e949974a40d3a348e8dfbce6e38fab9a5aeefe46924d5a4ee09bf222ba49922d3c5ae172077296f605f6adde0525c7e55cefa33d7b40fc8b1eb96f26bbd3bb76c923d38baf69a753ce751fb296327e318f0ab79722a08a5d57955ca14bad95d068d6b12454a301babda2599e7b2d3f8efb2c06dfb88798664595e3be6a46eae57cb3044f79de997baf9067e7c5ccb7adc3b63eb107434f285fc47dd7ab938f66f9545026464bde6fa515f0000000000000000cd4b7b0300000000824b0b95de066737dfe4b6965837aaa7cff3f670fb122fd8fae94560907a75b3ac55115e659b29221a60cb4cbc5507520df43240de9482ecdf2ef00db9670eeb92c1080f2247eb38a3593991a538a19b6c7c13afcdffdfa1cfa54bd761942b7e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000074e8e61e96a0588e6c05fc4858f38c47f37e44091e6b2264c5afa074cf65a4464fda01cdcfe21b152bc158d13e2cc6986f2cf45c6d6d1c803fdcd4bda2d1113b7aa1cf3f8bea51c9a46c26320aa9395a031869991b5e8205419eefc9d58aabd56ff67bf6eff2a0701053a4b9090e69e3850bf927a9f902e7b34c4f8f8938acc70221494e3eaa11ae277a5850138148024c65f32fd2ec989e8c401448c051a0c0750211717368c56f380b54be3993373220c47181e13bd21b1a79227b5bf83d0963840b00e0bf071401f4a9b86f575b3c6dc5da4bdb2f9b0da22c440092c2198bb3622a9406d59153b74a760f6c047127c753b303971e3074df072502c31eb119f2a719022e15010f0ca717cd552cf343720987373da9b802f5c0778bbdf6ce0dfbf9b00d02165c7be2bb9a240e746391273885e787dae7c2dd9f86ec134f3e1a03ac4002cd031b7a871c9a892ca95930baaf6bac58f8d017b63cc747fcaba91e143596502ae902107bd772a6db97b9f3acc29d0a1a74ea0444e8a18f496b94b8da90c71ff1415b031436dc573a8a50ed80b862d5d6cf853aa11141505863941cbc65b6ec7581f48499db065ded29421af68d4c5e2a907b53314b28cead2d84c736bb3693ad6e4f7adda3d947d7845f57ac71a011aee7e2f7d7237f31e2960226fa8c1cbc9af94923b324ed38007bf25eff8e2b1248d74a0a1f229c1886f6688b85a167f0f5903558cd2217c033d37616ea35281a57f0024a2696c86d563abbef24ed6420015b1a1b4bdb8b94aeb43a32d4ec36140fe2159724626ec7df8edad5694b874681acf2f3ad195506a32da87e1c12433c8ce1c69bb43a35f7d045783ad872adfd979f5d6893555651768023741c13ca3ba423c33cf19b4ddcca5ed0eef8041b5048970d9dce7e5764d70ff8f4723e5710e478f59cab39a826160f4c2be33fbcfaeca80b8470981cb3ba3ec0f01089f86c4fc45ed95ad4e1d839d5daba967d77d107cf9ad445dfa6111504b34e08ecd242f01d015afccfee64c02ddbb0b40be9d220021c064e0a105a651a8f6f9f65857c8cc3da6ebe3fe7a315a7798c298e189f3bb7a7755a04d0df4f87852b6873a2a6ed32275e7e7f3ec397c9efb2fc27ca7c89dc921eb96bc67b99d7e18d2a71d0427a2a2ee3041215b9ed14cef16caa48235154b57b3f62a95b160f04e07915ee24df7ef34b188782106dbdc555487916e13b5f764e7799c621c70bd729361ef57209ae55c3efb54089ebbdba5758c62d0b5090f9261b7789932acbff00efeb6452df596dcbdfe94c4e79ac68c10bd66a1d4290c2a8b6aa4dccb36220417abc9c083803222d1804defc1336b877b5562dd83195224b345c6cba1cd3cb2cabeb6a471181d15e1577a8737f33e574fac3b4b5ad3eea3fb122c555648e4dff864c634f99218070edd590b74fbd4bb29d71a5d690d67f4b15e586cb56b43460a985ab6743662f6ae8544c23918277bbd4ad87a7cda5c68703f577cd68fd3f24e16cac28b7750a698820297969077ef9f5637703ce34902d4b63503bd8c507bac96c6da321d088f81be3aa96d7bfa1cba33437420cdb5455028d439f758f9a2942a574bd67236d277db104c338ab6f1313a4e8cf3340333dfd6dc342784e96f3597c20649d3ee31cbc6152a173de13cb26e7295b790e636ac1b9699f9201bd6d8b677ea886e946f401ec600ce96e9503db47673a3699858de0e41f599d867aa4222ee70f64f74e8d22c678b640d5d54ae1ff5c9250041d4a64d445936eaeac7874b92e04784078c229363d7f55f0cdf0fe7b5a7bdb1cc7b1dd80eac4212c67f78277393578b041e1f6fce87b0d63c8a90dca0a585069ca82dfdf78c8f66af1f8124e6428fdba37376a583618b822c41a6c12cf3cb371ee981fe7cac8714b0bf4cf1eb01f7279feb2b28f3a54381b50f361f7328d93a93723f7034b6220cd6de90e09404ce49e5447e73bb014b08906dd92a6dc786ac57400e1cfe8ef237957c31797b04ff9ce5d8eb95298572ce323433b6ac8d8c8f3595740642d12f6c8fa757c2ff1dd69bede75929d6d6b9f7acc81e702e031a0c69b1e17a50f53624dbd8a5930a8a4c1342346bd278e69f0ea4cb205f97651e4de3fac6bf718f3c05c59ab5bb999e44a1978eb4a0cde104a972122c3b0bc39dde304d54019d7d59010c29915f5f8c41a50d5c6e9f0705784e68bd09f6ba353b5ae5ff2fba4f12fd650f7776c1b41291311a19ae2879ff4fee4a7147defdd4b05000aad4ad5a22a49f446bdaa6a8847620619f0817c6ad88a11bac1836eb1ac43f55d4b33963086712d2324f5fc24234a3a0d1ef417b5fcbed4c38e5b5287be76f5e83936872f43166206c6fe3c57073379a09aa09a983e9a2f8d25fa2d9ff77c33823e4a04ed22dd81e36ad14bbf5ea9839e070504", "51006551", 0, -1749758910, 1537743641, "59141dbf96c74bfb6623e63997fdaad953c996b8f8e6146cfc1399dbf6da1580"], + ["76ad533802a273ea8c9ca4cc89ac8a5b2573dda99c26f5cbe48996c0aa0639bb7df8013593030000000663ac636a63ac034146f4dd0060e33a20d8e4b6c0e3c03be98e25982c6aa1617c3bd9b388094d17645bf7030000000163ffffffff038335c802000000000800515265acac520058d0860100000000016ade742e000000000000000000000100000000000000000975b5020000000016e4436fddb8a69ec9e30e3b9e5676fdb87b9e115b6f12dfc903fb554069efb82cb8ae65c7da7ca74f6952bd86305d84a9ed158d0db8a82a6a513a7d280795f727d6bc025e6d907eda1c8f39de65bc4111dda6d98a2364bb8adf9d5db8b7482500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a53b355f6c0fbc05f3d61f4ed08c09dd7679c37703bd63666080b50d79b35eb038875dc4f89c30756b32bf7e4283467d40bb27400a16a2cf2eaa440f628981df39ab0e4ccd233542fa41bb41d0eab385ce0444af556f4f18ab9bd1da704244961269ce2af1723b8ab2697579a4f61f61daacf2ee5c77e78b8c7f4e2609aa0bb303240c96ca586f19f0149d603b6084e7ae6b256f160847b5868f8d35f3ae220be7031ebd52afe2bb1e1478a4308a544fb6bdf478d4d245af901873f080778cde87610b0491ef6959e419e01590edd543bfeb62a3bd4d2bbb234700e1880327a603bee42c57a00c37621f1f1d1d0d45360300fb793c25ae9dd5f57e834a31b4b634972703264fc6092b6dae37a240ee7cd6e934fccc1e4e246d796a8b6885255d5b5817fe02178321ba4955515fadc2c8fa3b99e75c23ec2da0273b5ebfbcd992bc22f0113502228ed3daeef6bd6a937e08672470efd44192cd4226f57d271ebb4b73a5124e39032d102939ebc1e08aef287c9abe0c1be0f205bf1ddf94a37db3b27375d6184b93021150b976f3030e61c2377372933e83f23fd4caa11200e229b2a525496f7f1fcc1648013ee7e2eb48fae476f59e573ff95f47e1a964395708953ac5bf78da0fedc309216973d56674a9fc4ea945c02455fc6946187e5847ec39ec6db445ef1621e90ca04e48b18089d864b75d2509e2c904d322835e2aa46fbb577e3f5df8b2c84979a8d496ffca91a91bfc2c79d2d575f10f4760274e5d770231548e66d9c30c5a0abd98317ef54048f4998456e586b9d1710446fc257964568a0fde3047c5edfa6a3b2d305464e79730e61ae0f5ff7e4d1a5fbd071a5849b0847785b6712f02628115e781323e2a9707b3c03f6cb593ba2e675e1fa0a840ffe263a5a1f312d2dc56b4ed27af20f330affd4407633bde825363a8d4650b928407e52e12d260bbb12fa9920e22d9ceb8278c892e53a32317d47a5c794ff2c7afd812a667d66746b83a4a26a5cfbaba6663821ef7ee9e8c16155e7340d06067a486ca922585ec7f8f2444a02ba35300460662fbf2469a36569074e2093e20fc433998709609a507c89ce89508343e41c28eea6494f2b477ac03e2e55aaee1dae4392b811bcd631d59795902271f08cc5bb390505a884404648cff79715a4482381da2498a647feb4f403f9c3e3d432f96f7d26fde90101628652a122062fe278df15fae55d3c950a897808e9f47421b3a0002554c4cbd07b60741cb47d5e552cba6460965a4459d40a0a219353682f7ed5e2049cef122864bd7834af0e9781a7411daf3c85d11c3fbfacc6bc4e881176be2a3be5d0448b45db08b9f9f9877f79b68201419dc0f8294d341ad07c07a66849d73817286426dcbd3d2a08208ea9d13f97dcf0b8fee693ad1e99523e592fe5c582ba902587a796f60220805c9e50972c3eaec16d01b5bf1e8d765f22d74538e3bc1960c74c19fae9a1ec444cd1ba74ce92cfc3141527fde3d55d7542514e155115d03afb0c4700fcbe1cb996f0168f8ca7e673b7a975ceac9484a7a194b221e0f43cc36b60811d9f95edb521e7a1efd2d407678ed72d0b1aca374e24ec0e0e89f4d1a56bd431fe140c1eef4c1f3f939e38ba98b66d4df9163e855ef59e219d28f84c843df1bd4a692ad012836d92849c4c3694ee82d0701a34e79cfecf2f35fed1fd751a050fede35004338157bddb8e0f9682cc131631b51a0401a014c534cd23f358c49ae59f9eca96f4547aed29f1b3bfb7861c716b84f9d5abe06cfbee81f9226a242f60478f0eafa03b4c96117fefa6d701ffe31795bdc9b374b5c961f63165ede1e5fd58c47e95c33a9b613b368550374f0b1d926e71241d0109774949552fce636cc0fcedd25de2aa227b24f4b0f0fad7a7d70c1b5ef417c98fee0bc2109ab65169ecf007b94f3e2658fe158984efb61e0d0fbdcb6e920547b3d155aa5ce00fe8a31369e0ce5f774ae92eea72a500bd4ad6f0e444a1b7339325f63a7cf4056afe3f4f627206b7ef850dd64ecda442b23e434430ed894d4b9813369d3a3d5e7967e7d64e14ce83c4fcd31e617691050da9b65a6fe4bd1574d11f3a018606b3bc5fce198e9a62b0397bc19cd3fb5ff73292d670613a0670b4b7ff37638d1da22fff0e4bd822c2d220e5ea2e6ad745f2f5a2830e1815940fe4674a3e0a83500dbeca9ed17d8ca74b223654fda87c14af2473197b448a1562e759889c9a189bf480fba7dce140a179423acfeee47f02b1c884aaee798a9469700570f917931ba51c4f62fbaf0f0738b825a91c43539e01a2848fbd78032e1723227f42196136ef00d7c811552141f1e4680938b4f07a5899a65811b11a48794ecd42299be9bdff1283ea3edcc888293cfcd8bbe4fcf4e3c49c443e4c1f02168421a6078e470148224da294f6e09", "00ac00530000ac", 0, -1223583658, 0, "8c094b687b5b150547316dfe325362a98edc59010dbe2801a7bca69574686deb"], + ["030000807082c40302c40cd2455256eb617273a788d4411ab4ef6e5cd03ea35bf9859787f1702d67e700000000095152656a65650051acffffffffbf7776ede5e6fbb8a92e4a1bd45dfb6a3f7ad93aa727805bca87ecb9b5bc84f70300000002ac52e2f9ed8c013633cb040000000006515251525200000000000000000000", "6363530052ac", 0, 772284676, 0, "7be9a5975b2b31c5281af59f5aec7754d204825d42842f401936a4e526b4c54d"], + ["030000807082c403034b3eeba36e8d8e4635bbb81c09738b4c19614006b291260f0c1513c8de6bd82803000000046551ac512b193626d2208353ca187300c2f217efd5577b5fe6a74a9594834afc1d6ef688d71735ca030000000665ac656353003750e1de6306fc3615335ca76426c1514f436e274c92e6d1817ff4a4282e4fc50475618a0000000000ffffffff020187c303000000000852655265006a525364b7640200000000055253526352000000006fe0c54a020000000000000000ea21bc0300000000d0984996dd8d80942cdc45377425105e9c3e25f8225a573d1586fa339de78d2c4c76499436f478191f7a3042ef6f82b8bfdadd4be670c712ee40b77bdaa524fedd0fadfe6a58da0c32472eb4800ac971d8c1e65b9c50c2df76be3380c5cef28c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e0847f5af1d55baa4f049160f617a001af19e47668b8e48510998deab3c9fc0fa68e4a1865f6c38e8f8e8eb9bf47733991805016f0f9b2f1ed8872f4a3a1bc4a0c0a21dc1df11becf1382ee82e8a484d809e62bc336f5c01c868e46e4485b9f257e649585343d3a37a66bb14700417aa7e868b97cb7c4f5c644f0dbad2930430211dfef417fffebb0a959b6276b7c91d257adee4e816dcd4218bcd1e0f183850e022a97f8fd599991ec69a4ba61630403c15f85a1fae298908b6227a549625a3cd70b040e6eef3bf1ab5e5d1a3e98dc6ce04729fb2755d5ac89566d8f73a19292d4d35e7977ec0d12bbd8ab3818fcc5cdbd96ccd9c7ab10591d9001beb37b4efcec890205955a09c1003337f78701b35c52b65da2910821a20f47375b66fa8e7ef97d9403067a20671f2d17d462269598f376d56e4ef4d3d7a3972c8c7a4b5cc05802c682021c4fd9c39aeffa0c945948426b79b5894299cef7f2d8791ce256eed761c7f69d031c212fefcd97633ff7c5cdcafd24881ee8d243ced41b5f61fa27ffd92d2aff1f0317a801fc076f87b9dbe96a8ecb7b323b21fd179bcc69559acf9403cc624d21137c102eb3d5829a1dddd0132b6aacdafbb6b94ee21a268dba5258ff0ad3fb6750813310214440a5ce822f1cd558bcb0a008ab9c15fcea46a2b7a726c503ada8f99b0656468a719afa5626d577a06b97ca350ce9c0f7c5308e8382f1dc028d2e20a0881bfa7e8d2b7f70d5d98db44f92a9a37380f022b90248ae3939db6c98e7957ddff642337992b252ebb50fd9e7d21335d1c3b53e683f598c48ba80ae84b8e768c5e04cc2b0e254ed58d98840baa2a75710c8c272b4a1db51b0789b2140ae0c12b7a7775443a53628b087e2feff3c6bc19b1c71ab26c883c254062f0155e10d80f7c4c96631793f17dd0487ee06d4af35295380ea11815d3a750341d294dafe172fc9d676341f10ad5b2d1752ed337b0cf0dcc6bec3f1627feabb2f3c7f3cba6a6d860206688d6aaf98a4fbff4d96e513aa1a5c28fdcb285a4ee9044ab5c9ed04b720adedcdb9d7c57d0685490928be906b6b3939738142be17ffee067dfd2bd42a289b69c4e9ed332f358cb0231e4dd956d1e75585b2ac6d6adc672ba75ea1949831f7e889dfb43e4ef4bae5e41a58551fd7245d7d16c40ee1c4fc4ce8ae64d342cf9b8abb0473bfb99103ff6f38e7431846c1587b2473bee691817348b4ba2eb6123523879833c3a60e6290f139b1790aa33457c855041b7af85cf0a77ecb892b436679c88e948634f0914d23ba0551a2626f5a24ed21880e760a53be11298082784481b58699b932c1707f95b33d7f441c167c454ad8782696b11e2d718e1bffb0950f8373cbb60b188f07f66c6f90b74652e01860d5bd7fc690f84fcd6bb0b57077a0463ac83060f3cb1c3418298973c5bd88dc8c6bf18944eacc521db907e6c11735d3a7a118f3f4a05604981625dc96ffbd3218a8a87930e41a579250c3228619b03b9d964e7b2fc761c44763730db824cef6bf02afdf0b55665b16690fa851ae6cc29e875e6a99d643c883467f3ddb19de4668b71854fd75f54945961170054cf30f6b88b25a3e891e0c6d5bca93c571d3bff3bc702f8ece811fea51e4b4847a21f7cce08456fbdbbb65d753e7f60a34a5f2b7ff8f41e1166218b69d4ac9901e67ab7108981a26d3eea96dbf853c79c2795916aa53588f1f156c6476ef78d226d9922604ab670d8b0897910de41821e930e5fdcdd0ccfc5983812f5a2372dcda4c3c0996a99c2443a54ed44d0e696962e8d86ffb42dca5502307c00c19a6a3ce4e6faa3c43db6fd343ff92d698295d195911bcabdd9457e240b2e67e0928799eebe8ed7c30619e0c9bbfd36e6315de9732293b3da61769a9734ca23e4a6ed5b992ef541cc0b52620fc4f48e53746f5f1d49cd24bc3ffdf2c0c2ddce08cc31e91ace14df7fa8c239433183749bb9c414bc60d89959244afd37d861f1434efcbd8fb787014aa5659a477cce30fd6f69ede9af976558c87185a2e97d2fd0c19fe95846d1a5f15eeacf16514e43ef679fc88597c6e3600cd295909bc70b5a27cc775a92529d5a141166a83e0ae152bc752367433e53f32ef38574348e15a898e0478a931cb259893569dc45a33bc5c1bca3bd3eaa575d57d42b77d4568831bfe818239148ada21af1e8a7e5871bbfb7cbfd472649636c7b875e6bc922468e9021ca38e71e81a8cdc6878b4c848e3d69de9888eb27bf2ea07223e19bd9e896fe45c405bc261277a96dfe7d642646833268720e900000000000000000000000000b1d0fb7bdd1baf73af879d3f96b4e38aaf5849fa0704b06dba3f4fd2f50ba2507c4d14d6b0b4fdaea68cd3a471df44d312b7c7021cbbd37ad317ac6167093bb3a11cbf98669f4b4e62ce7854e953cb65031a6714dfd11d7c820f1c59a868cda30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060f42ae58ea70cb2484ad5cad4fae671c91e27ed89c005334a41e2e9f0c0290eba2c33b30d7c7896bcd7ede14db04a062d5f2e438c3ff75d2b90da91b3ba899264abe7689ce6a7793e6b2f17644579d9af147b6708ccbc6497bb02b49e8635fc8e90516d548d15ed32fead301372d18c731202e3d99c2822e20b29067284301c030052fd06315974ff1078997be9671674c2bef1f1dc1141282fd68b81b682b822033004e1f95c28466690695559a142d068ae03005a50977796b950e89ce952734d0a08616cb770aaeacd967b44fef420366f8c261a0f097210d1c8df6bec9d502229940b79be0717b250bd430159b59554993f926f9c5c2945b4dcb6c61f9578f215032582751f4deff6a75a3dd1a02e841e7fc3f58b80081c2af67ee3470e471939310229e5b6a9ba4ab92f5689fd28da4b9009ad96c2309149c30e20982bbf434dbbc0022690e8e501e1655b57a9c45db1a8c0325373448672d66c911ee8c9f2fbfa57c4021a5dec0e5cd5a7e839b8cd0f46e5a534456d09352f3cef26413b7c22d4e025000319925bd144cd133df46a6688530e5a94d0c7b0196f9e9f792bf9d934938ef279ee40878cacff9041073012a3f05cc6147552eea91ee05f58d9ff20e44ce75a25099f9b543c047ee9b387c459b256331eda396e88d3b0a437de183abb2b141a2292935472023a133f612297b9182bcf7bbe1fab32c87027e88b744e86f03766b13412f056d642d9720180a3ea6334f69ed0d225dde0f1be31f9636267e6f942aa6cfa8c507a7bfcbf082d61bd06e97a9828bd058835a1a798bf70457240b4a79fe6d1346d0d1420fbb73f1b940b8fed9acca8fd42860139c0951db9e341c7eb475f01e5bdd1d674ba7fd1c891f77ccc7317d88b0c911486875800b0d4650fb033f6c261a7e0f5dff98e98f2186b2074d57c24a656340605b03c64ca4c64e100181eabc8f60f040c71701f6e7a61f18d0d9632e2e091559384763ed79336957fe84a376daf49eb8a8a4eae995c15435fdfd6cb70ac1a67817a620bac06f11fc6a40d137e5e78748941235e413df661e4f2726c4d99c9d0f3dcf08b4dcb20cabfe8d4d5c2a2dd73c9b8b51e55e8d34c6f68b7d8154dc7bd003dd5077ef2fd9fbcc87e674e934a19fbbd8cf1231419a326d2ca71efefc6efddf76c6421956222b050ef6694c21c2f7c0e2ac5db6271941b221bd0b8f064ce92cb6781497dd74c5e4c5c830fd590cdf9aa6e260b64f4ac94ca1f83178fe02bca0d3413db5483375aa77a4c84e210484af22d7ab8e01f6158c183fdb3681d2fa9a14a91dfc88edd7c65a03eeb5f96d307aa40ca376409012df83dcfa0f27109565838b77850088617defa937dcf1c32e22425a9048de2b9a2a9646adacf06105aabdcce3b11d72d04ec271d5e76d2b30f6c33b9f3d0bff928844f7323694791a873c01ddc7fa95bb8ac0a4c8754f5458ae8d13f62700e931c65a880404fff0fdd3bdd3aacc73104438c53888480b7d097f7a7375467cc33788b2633a7b5bbff8ed9afb65522a01748246397b37c117bcf982d90c23e3ee13cbd99d5ccd7b7e7126b9a914c11e691326fab4ddf2935733f63e1955a8e26f7878b2db2cc39d42ccdf1b5b11ee05107c41a0b1dea0832db1cd85c8fb8eb17b4afb1ae07ad6463a1d6af4e1287e0ed6ec25d45f1c84dea84dc90be3ee54aea337635d48a1c9250657bcf1a338183958e6df263c1dfa0e488375a5b35b88c896b17d3b4fa15d540390e6be0f0b2b59d8e8d5d52b44187fdd4f6d3bd3a394e3cfa06eb0f95ccbf5142e2bd8f73b96d3584982fcb123a2f085db81a8a330f5afce48778fb2978bf71ceccdb6caa1ba9e87476aa7ee63c879ff005052c65df2220aa56550b47687ee3892ad98f6bda2bbc637b5522f76dd86f204fbfed47f8c89da65805c7bf183aa76e2c4c9e7d471ef170e54f1c5908ff9cad30bb6e1ea3d5508830c5d008ba03ce957b45b08a2e8afe793b951acefdbe59da55acd21906ceecfa600df00fcc62ad505724fe66b5b4367be8b7e3dae32f20e03aa068fdf978c8e42d3d65c0805f4330323b567eecbda86b7d1e081944230ac12c4a160178aac0134b12319ed15ca2402288eb7470edd53d3a7d60fa395420100fa45df1e2c26704ce316917f1144399d0dc6acd33174c7fe3088472264b475c66159123ee7b05b28e9579a3fcc7fe072fbe6ad7745e4bc81765bb620e7a0e562d4bef1134446e799962f8dc57d8624d051a3d56877bbea0892f5ad9cf461494d0bf464c316093f9deac5a67f2237df661cc77fa36f61b58fc5c0e48ff657895171337190794fb09cae99d95f8c7f2c5245d071c078d64dcd70b90bf9116b4a70156d006edee9f70a5651b46c90ffa4c1e4eb108dbb8c9b5e76a6dd46ddaadfc45668e087c3187980092d80f", "5163ac006a63", 0, -1632957627, 1537743641, "b7cc4fa29b46d04090d94b1ad907e885ddfd896397d0d014f2df3d8c8298a31e"], + ["030000807082c403012868754011c04947935771c1f5c6ba6af0669a42fd505bbefae7234631db07d502000000004aa3a8a2045859e00500000000075252005353ac6a56cf0305000000000097abee0300000000055151515352204dc6040000000004655165630000000096e56ca100", "63ac5351ac", 0, 1583282119, 1537743641, "85296f30478d553e07dc2b7a2af2899430c89669ca0934722db5ac82872b68ef"], + ["030000807082c40303da75fa5336b74cea06d748c0e0c28642cbaca3a3cd3a9923aac2bde9bd59e32703000000086a656363535251ac30a82c63f98243876ad86493ac079a8dea3dfeb2973bd3f6f0d7b3b45327e4f00683aad70300000003ac6565fc3c84acbc0991f3e1bcf5a5003df672c79bf414969a5c2bc6b9c56838577f9c4ab8c376020000000163ffffffff035512b70200000000025252d0d61505000000000553ac6553638e52a20500000000095352acac63ac6a6363000000006d02196400", "65516553530053ac65", 0, 860211217, 1537743641, "e45b2412df9708bc9fb8dc99387469827cafedd5d318b257ff79f5d1e7f49096"], + ["96367b7602d8d06f19acd891628929c4f383c18e4a6e98f2051a9ec32f4a4667c24b489f7900000000020000ffffffffffcd893acf0e2b782a80a59f4634bd821a1c609c83de0731a03f410ecd99975c0200000008006352526500ac65d4a298fb0203bf46000000000007636351536a00638edf9004000000000951535163655300515200000000020000000000000000ff917d000000000065ed541b98723259762758bb7ff366fa10433e076b95529f9a25526ee820f674bb0de6e71f3c9a855d6d80b960c55ea1c431e357015a779730baf57e2f2c2113ed2f3958b8fe5b8ba4237f34498be7a624f25b63f94dc38adb16126ba6899b9500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e79bac232b02311714393c50971d4a678fca293fe924a1ac90317dc7013be22c96041f714ecc2416d8e2b528bd22429337ba003df25cd98c95179a5760b5ab4baf184f2bff025932a41378712a2bc4f816f664ffabcb9f55b7fe6e1398a65524e2537be7493822349aa1648129ddc76e5e382b74e6a05f4609f3ad4936b1777b022ea2ddf7a52c4fe8469b1103225d15df9a87579e9fcf1164acffefdd38616f6c02028e6dec6e09e26cb90de53ce73c149a655167983206fefd086212a0a2f64dda0a07e99c5a4c71e7057da1386cfe72d0cef1bdda569650acb17ce690e2c2b8fd7544a8454cfdb36cffd9b30b5ab974bea55cad4ad1a487dfbeb6619a4cb86c6fa902194a7baa3e6b20c54fdcd0b74770d5310ffd78c189f1924bfaff2e4ae7d40f6802195148f0dae148d9c5c748b915d9e4899a6847e22e30e59f9574c687525a1ec0031ee73953a15097fe84dad00d74af3c79463a606bff4d172fe7a65b0463994aac032b8c4a5b716db45d5fac684c038c292621b235bd0d7998752c40c5649f3bebe00212cfedeecfc2c746fc2679afe4c6667c199576f5f17a3c75cc3ce1e6acc77b55b001a82253d883888ffe2213a114816a50a136bc29a449c4272e9bee7ec7ca7dfbf154f7a0d5dd4951d1bb456d38bc89ffa81710b212e84261224bd6c3991d63634e58e379a903f4ed32dc5813b967702711a64e60679f74a6afd3dec582ef132fba445c7d470531669c173e8ba10d3e21e756d479b64e1e22a37453a7b508e041d31b955b755adb2cf386d0064d5b03a99cdf61cc9f3b4ff8842a8ee489d78b008e6549086657fb9d4f8659bd9670870b18db870ca755477c03916dbca84e8327fffcb077b7a8c20f813c484511b89b14dda22c8c367cab270fa8701f56cffda2548d0a1558bc88e096e38d2bc014c6107dcbcb1f1a50ba23bfe7255bc3e570a1400ab357ae09f42ffe5c8232e6087f5df2e11cf6899f66cd9d61134f837b18f22b95c8798612152fd79d229cb9c243a880eb3112feba4e425155b252f3755ba562ca77b37f6de333260a8798f22b02a3bafce7c45612a7525d8f2ddaebfd8ceff4dae724a5c023df4d7dd5300b80bdcce127683697f961de06f1a24d4a7ca483465ae65756a35f9a4d71b7016a0052072b687d5402e1d9fbee11014117e552f6aa74c05e7a63a3c4093ea1736bf08a829cd49330d67d465ec04028c5f60f3815d15823a8a014bfc6f4602a08602835ec4fc67f805b30b071285bb6da6c5da4aa2f9e9b23e283b88ec97b488634e560d3e55c3f95c23a3f21220ab56f9317a3220154b6f6d15de04840e11bdb52cda6ca8d82f8ee8c0602d44f76eb59aeee60de0561aae1fd65bc752ac860380f1d563738f90fac8536bf6517c9a292356a1a8da8d20cea397c9cb08dc38c68cfe8dab5705711e23cd4437c342d1c49fd6f9c4c54858fee49c6a1ec8ac6f55e76620960a9d9513a7fd26883024baca6f411d5b0b62b3bd9b66b505f92a898a45d4081895302496f6cf0cfed4b62c5d9330884066badabeda7b56be9720b4e8abc91814ae9e5b242db475191c7e1b109f884e4675b218f1577836b1cf46fd192ee0ade2725861bf63179c703b18f1fb96e69fae042409a16be3623b92682190665123c0c9f072b42a36dfaab8a090f51c25c7acf9f7bc113b7dec6517237c2bde013c43e95ae5a7d633650cc197dfce6baa069d1b65dd281f9b01312903f6f53d59f9303cf69e85e4d1503770a20a1943422a4796846e526fb74b9e5666e3a9039fc680590d9f85d932dea3cf0ac06d9ebc10f89be4a3e76c170a455b9620e5e60a979a18332ee5b2a1025025f62b7669ddd381ab2a712720cc8941ac02cac80415e3fd275016452bd0cd9034c3d8614876233708be744d7ac8c6f7e39df3d7d71763bb51f73b8b8bd7d809cfd8a276e4c8bca46a32db361f4d731839434c594ee1fbd781e73ccec2d5378a04ad67385d5630497d541f544b86d28fda3e2ebb66be7103bf7fcc31353171f67b2481d9c53d836074242ecb8d83918f5978377b3e0e9cd38aa5e49a3853088bfa0dd56fb4cbc85e72f8c873955a79bd0bdb75e060297d7f5e17f5e2e05f0f31ef62a8ba53612ac9b7fb13b074eac661973728addd8cc2e9bde6e48a69339d9bd9ccb6400c64fdc8bcd5068a9b40fe38dd0061bd68d027489a27f4d3b99fa99acb897c48bedc8b48bd1bd7b8b51923f043fe083fd4039c6f7f5526f47ada09176ff2ce907501c4ab11b36a131f73cec367e779b1cb3244171b08376cd050000000000000000000000003b2227e96abf3b9de3d464cacd9a94691627057516e0e68fe59758b3f67f12b4452f67503be16630540478af146c2b97af019f3a3d1cd2f852816ab55741afe8c1c99cb7edea8c078b2a8e6a77eeeddd1859beede9e3b04f63f6008ecdd710cb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034a84640d50609989ea3a69eb91ddfe58ffebe070875e6c1987e4e2012398bbd8e32e3cfccce51f2b6ec08dbcc900c886a1dbe12cbfc5f0bc42d6bf81a828e8e9978c1f83f5ee313e7651607467d2ac7e7c23fa67795729414d4a9b0523be729356081db34f4997e14f94e172bc3de30686ab498912859caa145e2a0b32b853b0303412d1f07a78b68340acfcf517e24abe819dd31e082ec168f9906b45bc3138f02097b092f2cca46d0cec1ba0f7092aba8a6ee3f63473e0a697f33e9ac9e5f1d7d0b0497f220ad0a5d4aa5e37e6fbea31845cb9f833890f66f6419760da6985365a2f1dd3aa8a497e5d4a2f44659169d10424dc2c69fa41d04b67ed50aa4a256c345021ae91b9f7dcbe96a9a5c4572dbd9db5b72f2efb4db185e462a2d6471a6cbcb6a0329aa8f9d8449c323d0345c0017f343dd7fc1a5e0575e95aa7dd9cb2f8223276d0304fe43c8066b1a9cf3a994a6c46d06c8301671c2294018574490d659220190b8030fc5bba8c6c4462748e66b9730d50ed3760e18686d80f72244e2a476ace5f87a032b6318cda6b519de34669222e04e91c219c372e0d4d04fec2cb6133541e1533ce76ddab91eb12d609f5d0e14f43d59aecf630dca95fde8e93f88617b8f467a4ee0bec9f263042322a17764a944843ee3d58ac6f23859d9d1da698e1064e1c4d27058f3e2a407ea20018ceb89e6a90d0638ccccc62301f99ec275dcff761fba7163350dfc99f88b2d249de9a4dc8a693a309fc793d5c22f1934648ba7763aab72a8a300625cae5a60f2850b9fd6cfe7a9a261dd9b8b5e235eb1c1a15d3b68b6eab1312102e49c3eef8d8bf30bf4097753529b52372c9734ed96a4009bbcbc8c1f9c0d1434a4a1e747b6c00db0ccc8041cac1b11c1e0069ad44a1f23b071d67123349d9dd799d1e9e86c9a6be297e9cf02a48dbb29a1ebfbea0815ae7479392ff9e8096c022203af114c9ce9143b9132bc6a27e6a4ee6e09500fb716ce4b302e05311473dd3521863fb13bd5caf492fd9054f0496c982f9bc67fc0ee9b01718bf01af90166fcef4f8b2e5d50fe920ac949cecacd1032da15eced72e54c437b2779307cc0256acd8e85a5d24412653cf586cfb0e864babc63f9f1364d10ddb6b1fdc9ae5ad760df0b0087bbe7edc6fa2b48535623ac3aebeab3d6d897a57682a9bae0fef9d15f0b0f4627e024d2da0ba3c47637a662bcbaa18532d6fbbafa5e2861c9d2ef06c432d19a6ffb2f435110c81988ff43ca9f565e64d907c1c6f3523320f53b299b2582a6517ca7d4dd1a4f42c8b89db661de64dd206a00fc551eb1a9d290efdb14f3ebed85c7badd4bba3b9a1e833b2488be5c1954aad95d53d537ae231ad96000bfb333bbb5e7446ba42c10ff39a8fddfb39b92d1b07f3aa1c4afe8f62917a87336d2ae1d665343ee862a60bd91fe08f2adb0aa11fcf7707dd94e8ae8e8f22920173d0394cad85cf14441194e4418e26099dd50d258b0227785af75b9711f274f6388454ac57fab3b700fccf28983c7795c22038a455a806f604e4ee10781719f66ce00767a0d5194a1d7f04f3389695b9c50eec0a930fa4e910e7527bd14152d6388dc840825aa48214bff060118ad6f7689da353c4d11b8eb582a06206031da3916d280f82a994a628dab5654f24eb018eb072d7652fc807f8f8c7a4c618d41a5d76557a7876a1cc6fa820da7f6b55cdd7fa3ed1812ce0f86665da6e0f9beb101a9cf46c890589d7a74a5cb6ca12001f1dae51fcc4503eb0d9ebee11d45de23f7c14b1673412bb19acc3a9d676cfe8dd17262c0c082f47f0fee0efdece4991b6e11c0bd9a35e71c2f36953aa7316d6a5eb5fe8388aadfc2a78636923ceb667fdb50467605ac7eef0d0e3540a32ee09f183f471764a45223fb34c76816ac346582c3ac4b11d008208dbe83a7a21697df5b46a7d422465db7aa0a31547eeab2cc08b2ddcadf22e7b66f5f03625a0de5a4af7e5d10657ef9913c9aff75e9eff7bc3457d5eb1103c8babb64783069e2feb6c10f7ef99996f50e2f2e947e6bc642e9f2bd40a221f1ebf7d3c207bbb44a1e70916c92f232ae15bc11e2e423d2c8a0cdff927b574bf2e666bfa26dc0b8ba55338b3186c5c20a3b5f7ebcd224617e58e238a6114a31a574ad001ea3990bcb089df0c9a7595393a0f525c93e8fd074f7a99689df1486fc3173f9071853c359546bf206a41e0dca96e8a7632783c13d7274b403a6033d8a98e302bab543923bd41fb34bb485de44da0ec52e05f054bcb0e15629f435091c1711471d78cfad35bbe0dce429fb0fec4bb98ea6af8dc271a103cc8754d9d669f5589418b24fb626a6afda0f9c0fead068c82c5ad84014fcb0baad69c07e0be34380029931d1984ea1d31c1bfa15f7022cfd499b06ba533c6ccecf9568de576d464c6feab2059d07", "6a655363536a6553", 0, 312005339, 1537743641, "ba6189f82caa3bcb8ed59a81348f672e70487bd6428a756a12b4dcb84327fdde"], + ["030000807082c40301c0c3e1f8f506126d5a3269be998eb8520dd6ae3259793e3153b2bdea67fe1c6400000000065352ac516553ffffffff041ec94b020000000005006a525165a74dca0500000000050063006351496ee302000000000851526a51636a52ac60e5b705000000000000000000682e7840030000000000000000b13da504000000004202c496a3bd165e2da735a69c90c1f512e9f4c9593cf3c539fd3ba2d62da3a190dfa640ef8ec64efeefcbaf8e5ed79c5dc0d8479a9568e2931643a2fc3c59b8a512301eda50db2f2ba41ef54deffc3a111f78b45c10a2db8f930b42c3085dec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004f5d0e73e3a73d994d7322b862d910f00317cce5802b97e592d4c423bdf0568ea338d4d87295928b1f0a73fe14cc4975f8013945b3a073dcafa5213604e1b3aef662461325d8af4307aa5d53035342866fff193efe50d8655c01abe6bbad50c2d88b183e1f29a903db94eebd778f1bc6ef8956651ef3f53404adee9c3a83874f0323234edb6a895103b79ce1edf30cfb53ed805a06e205af2e7246e89777445083031800116c209079831fdfc126aa84d418631676c765dfb38cb225ae4bad6c6b910b03c1161600ef4d117c2dab8e96eac86c91d3a4aacb7569b0609fb0dcd4230424191e48d596147d15bb5fd475dea4d3256c0f9a75ff08786c9d2123ae11dc70c8020540e83b6639c7f43008fac0380ab100c59b8c8f7a0981325bfd719c36e28bd8032ae46c03fc1a5e75320b846554dfb0a64d2dad67e9e8f1a5122f932ba3e0d91f0217bedc2f4f6866d659a8c2e628b47e40c97dbbe158f9a38773bd642396e70c69031b02cc1d2a01a5a0bf4c134546f34a93483ee067a9c41be47c16c827fcef6d8e032e8aa772ce589e97aa5862c00484e1e62893c6e12c0a333681ea7a2b1482ec1c66e38ecbdb2dbff9c05b127ab2bd5073e02a93048ff3ddb43ecba93ffd2cf6ad7f6e2ece8619621a19272213f1b411db8eeabb8216103d56bde5c7bbc0fd7a865f44b56f3b5f6afeb4d35b15f07e8c79fbcdc8484488c93e8ab01e582b9ae22c5326d8fd75cc2c64f56f24b7bdda6158770eac9ded1e1103f5f90abcba4f29317b4b2dc90c4bc4f8a38b07c32cbef0e82483bdb032dfc3921e46ae4f0cdbfd1a55a83e8dcf7062c60b8af9657f074d43a173071627050a6581b49a7a9b6619022677f5fb6dd41c382756a12efb47d22782424d5083be980ae4c58876cf641c4b12e54ae177133c08d858bf2c71b52a6cc8777c587da3cc0f497e23193c24e0febd161f4f994e1f47424627d813bc23ee7b277a92324f1d338df010e3ef96314926877d11560bda0f41760c591ad2963d2a01ee5e69b9bae8fca8c76d4c4b6efc0aee49e1de5eb4bb97a326fcae606a2420db58dcf1b67fb94149f750b8d80ed8f68747047de0489d69848979f6ba67711248a20f12bb20733eae245ab702963cb868db1b1611db379a43505ca388aba5bc7ebef1abdb067571192ab18a12cc473c40bb592c5ae7974ffdc5f33d46129b9ea9ac9abb14c03d9597536ffda74cdebf758b115f93908a399a84f85cadf6bc8b3df2e1904319e4f72bea4922e97d08b3d3822f17a241f4639e7df0f1e6ad529cbbe4c03a77332441b33d96b78218e272fb5c727a83e2f8147a34842edd74454271e6aeb049aace8f983844539d0f631956b9011bd53ffd50d6287cd97bcd511c8f9265424ed6f7ff83e3db9eac6baf702fc4f0d373d6251396d6c193679a294039960651014c36bd5539643c1b29a92678e693773d467cd530f5f4fed95d287cc0d99d4a95ca1acda3e89f5fe83ef273b8c6141944bcae2d1935580f68d7941d5b745564c98beeaf78ea44f8e249aabe3ee6020b7e485bce30669ff12282ac5627fa658c36590f34221e4472e9bdbba881c76a269012dbfc828abcb9af3002040e804353c132add883689e758716a6a7913aa45a85995e7cfae96fa8c0f4dd461967a1b3461c903b47b6162223579668bb072aa260bb68ce9da4e8b69347912b00236936d40ca62e541fd215e10c052d205711fbf89a8c0a5798db6f678c01973d71a3285cc53384158438b7543c236d2794bea1fcd953e36836f274152ac63279f6a2380dc8c7f7e8b442d3619386c261a46c7a13c290de014b51745e97d01c1aa3dd4b4e252ff0e0a7ed382f3d53064b870f6a1f2d3f45c7ccfec005d27932cf618cce3af162335c23ac82c7d6a93876c47c1bcc779ff34fb5653929283d9db5964feda531c51a7dc3f35408925a2469b15bff9eb5cd9d826214a1f479bbc931ef719c59568a2a97d2dd665bc6d62e6583e1d9c31c6a2df96c15feae0989ecdcf8098b1c178f625a3e1476068875d4ab41baacb190a24372ec795e01aab839357900da643c1077a7ede5dce8ee3d6dcd00d4cc991b231c087b3a4eb9bdd7a628f083be3231f004fb3ddbdb349523728fdd31fc57222afa59f2f371fcbf091f1bf15a1231f7d5b5fc596243ddd309068478e022f2526ea48448e67c22872a86453c334d4de81159904fd642d19d120a94064e59d528a24512ed53e7a01ce8048e51cf0ebaf7f31da17819c125e5212b66e608c53e419a394c9eabc8593b522aa400000000000000004bb7c40400000000785cbc78766a5fabb41e5d31af5028d9f23f2083f172a5a242dad418e2c29228d8e689fffb6ecfac995f3655ce6b895a9d3813bc2ace75678f8fde6acb9eaedf79ea3ed70cfc9ef48ac122f4df7614af2566848d53e056fb60dbc4024981752200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fe9046b34d73f5596ab4892ad9ea366c2fcf9790bd91f9d671abfe4a9e7856e60896f9f315ab8f1606c478478eb03e47b3531e88a2972db9368821e4df0fb62c0d0a0bac7feb6dd5e8f982c5508913d633614f235f55a3aa5f3e8add7fca389a734bd025c08a30b951f16ec2cb3442bf61be16b6e4fd500792ba498882fd6afa02265fe67d58902edb3e12bb2fa6f76fbd020763ac40089c3690b2c7699abd9ceb022c501a8d80d6aa420c7d9fa64b593e976e5aa5b563cc5bdbb935c12c8213840e0a0173a1674645db923ad27d1310fac5ef8d4c54fd2dbc832cf2763bf7832d5b68de62d841b500b73348a01ab7d609d66b3d1861674114a95efbc272fe37ec3bf502066965e8194b2e72f48db480e6dbd330f3da46533d8e745ff45087969fd6aecb0205620aa56307694d9c5a108b9e1eff298ee9be1ae376d58c9ca52ba1e77f262e03123ca0bfff67bdd364fa6020f71023f75ec55fd595cf33026ee0dea0d5eb04fe021056c23a4e1a2b1b8f7bc0d64d3f3b131fba9c09aeec584a6fa50f9500dbc25702190367ff28eec119f54b722a79e150611a54e4d6da0c68f9fd6ff007389aa6d32f67e8a1c8ea13a24d468f7ce0d6ed52531eb6fa93f6d6dcd7fbcea78c84707511c22c2b858aae541f7e0204d75f3358bef37f5cddf81280667885b48989b0bb93ab2f0a486736999d27686b90071a62c97b40071139b524b2584220911e4e2529fd1512fc46cf2e1de7b904a58650fe050ebcec3538a4e48a95cd4302dc8534b1a7ec7469ffa986d55a15414a3cb839ccf982da2beda0ffcc91769ab91d82b72d277eb783ee700b46a0cc21c6d074000d129a843e4c1885fbf181739f891572612094b680d79663b8d7aaf1590cb5125ec0511ca4e2ad583872364c764fcce413f24f21a48761a446d180a85883fa28944ab0198ba249125552526d719a43a902aef289e93e2410bf624911358923dc1366805036ca58ea6454de940f05bb46b38d2f0bdf6a7676a816fd6c776505fa149a9a6ec53f1b1c23401af255ead8a57ea9f4005f2dcd7dcc3d3c1b6e72868a28b55700c4f8b813192df102d32d7773bbf19f3a844fa4e0855467f484618e0cd90c42506c9d65e505ccae1a98480be22adfaf0ceb73dabbe5a57f4fa3956ca75b93cb04a9dd2113f9638989a846d3fb6a07d1cdfc96c38a0d86fc457ad1094d07d829e20084f4ec5513c7278ee125c853fa3123784753f4431a66de78c22f3efeb8a2d2241c128e63fa75e2c965b35f37a18a97e01ac21b23a11b9e8a749132e059a29c2a113e72f84c7de02871b8234b8cad0d29c2ab3ed7632208c4b781db328d62cfa21f065c5675a26e99c0cf879427b225ccb833e5d4e514d668537f3342c30ecb957f74dd6fb54f38415d0f9bd612dd55fc1ac60b8e73d093d5532f81de3448a698857c027f591ac87cd7be37b79a44016abf5fb97a1875fd4588a72a7f635a426027542e1ff7ab401467a97ebb63bd3e24084e4cce4e514a37443e5badcd8a03896936175d41f6b352bcc1c41932dacbcd2c03d0a98c9f549807a923b3d22ec92e3ea1afcb45afd60ed01716bf83d44b0447e7f1ec3bce8c7f81940ef54f1d6ccdf1e4bf691c3ec767e2732ea0975279e0188999cdf1b73d7a3b051767b56b5e7e7cd6aa42775e1cf380d7c0294951a2352f17e3e8b13e951ed9e1979040730643272e9f32e65c9b52e06255c6e3cf0bd1fe3921205ef39540b759a4285da6913e26fa00a6a912162f73107b6bd06db992197cca851925c848c07f762e6fd49294f48f2c500ae4e86371dff73b47d13d1d2ab0fea50dd197bd3577b2f9e1a642a47d9567f76a67d1bdf1d8be2d13670bd73aa60ca8100b984b2f624918b22570bcec30ad7897b4aae20e6ad9b844a1daa8bc7e52113b0b81981120801a28a7035cf842f806863b1047ae4f8632b825e55d5ea99d329e608bad314f927ec305ffa40e1a1434f8e325c235f0d1917d33121938023d6fb2162ee16734af6bfd16f8bac945809c69c0984904262a00053d0f2684fc5834b87634a8af2910c10cbfc1f0e594a87d440120ea5a8edbba725286bc4929b815fdcf2e7fedeec3a85dc4e43fb1fc2ff212d5671222ea91af6cb103994f295b58c56d338af984e7a2e006710ab96862c4420422c27e51d69ecb14824babc608a9bc591ce5cae29e5e827bcf6ec922e1cb614b4515d871fcc4073608818f4692d09e5c5a9be368c20d6f882188a64b2e239f85bb7ad4a5eb5168e8243e251d6bfae9db5880f627b367cf162e7002000000000000000000000000cc45ce784927e1fece08c1e36fd51e6f9ede8c220142a8cf02f1b1a9997f1a557cf8eb1c99feab96202c304bf0e6e13854689a178a334f7aad9290a2821246d1a863cb4141e476c994a15fd2bea192d5ae7e7c35bc43616f40b838879acb0deb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004b77829ca5d4a1021ca1058d8e7a9ecc73d20e8b78014279a88ff6481a6e02af5dde886eed2d97484329eb8e131fb82765f54fc3fbd6e998dd8d92348ac3dcea09d8a2172ce5c992157150e3ebacfce63bc2502eaa17b01db976bc9dc5fa2cfe4c0484c3862e9b7c99cbe2263cac5f0fdd36587392134325f22ed9506e7a1a8b03014be42ab666c3787037646db035c6767e18cab136ca5869a8a9964ecc62da890213bb3dc8c5b9a7c61786834766eaa0207d318c109b7a6c4df088e68931ab28c60a0747162e9ab247e9442c3251cd4cac97de08445518aa30ffa4096bcd3a6c7fe8346e1ca1e46c89004be431925ce9653ce2ca95a46e2da6d850843313612933860319f492b270b3033a64f0f16ca2f02d67b5bca3d5a2d2f2b10b05197263f30e9c030a31982ac4d953cd71cf8d825b61674b0d2df38abee7e13d696b5fba0ce3d5b30206810735fd297336671425d5d25afa158043dddb952850aa863a187f1239c31d0227c0fa57328fa17c541210d08457824a3483040d9627a2932fd359ff5363262e02213fdbe6db68b97a4ccaf0f838547652925a65aa8170a18aa9bf0a2bf5827a33207d69121ecbc862deb16c96d9ed8c01830d2d5a9448b68515f0fd625a9fc0ed0237fa2cc19b0f682ab471543aa92a8c9f7ee53b45ece78d20bf2d9ec2ae37e9fa3d871782cf5d4fa2eab626fb72d22cd433c16c5d5b1b4d8bf9699639d86a00e33ff9e85584f73d7761d10a227cd01cbb7e8cbc2922f5b8ff58fdf66e90324eb93ddfc969d62298179e7b05097afeb3c31004afd95b66e15a102cf804aebf0c7717a42e29a0b7a4720ccceed535f88ea6859a37f8ca7c64bc97da9a949345711fd9cd78d15730f65f84ef9af36b94bf8d8baf474bbcce2c58fdf0cfb3dec666d30ebfb539fc1dedbfee8cc833ec640b342f3d518777795807a18fd3f01f0d574ef77596f37cd53e4ad3a49d2f755539c95bd6b19c42630d21e81faa1c674c7186d0abd9a0a6de308f30dc60c253cfa936e5d53f95bf2240f4f41afc5e654ab9be4308bb97fd459ba756a2ff1af4ef8238692cd87e8ac651b256a5863102912ee0874d264cfa74a6c9d74c427f8ffdaa6525be12066b10aaf335155dfc3917f2ff58cb1161372b7c40ec2464befbd061f47494949a86f67c0d809dd47f5d1b4f44812e4dd30c16c35caf9f6e869c04835f930f40025e4b3b8985ae796a8bdb87107a09b96f97cc4fc980cb91e4e2779e12f5771e05abdf16dd75ab49546223a3637b3a01d53e2dd072a3d5dd2e0226af9c6c369bd81bcb6e21c4f345f3869e574d148c653860dc9aadf00dd6b4d05cc2ca8475001777d4eee4ed9530bc31ce2cba7eba0e5bb8c8d1da18f194b4f1dac63151b1621f78dd9b4ef4a1f522828a036ddca30a8d1527a32ddda137ed47064a2a03e952d12ae35e7cfd33f1366a6b0bef24643ccdf910a0662f056891e944bc50bee3eee4c3cd21481f3834178c8bd01b311eac00caf8f372994d599a6447ead0264822fdeb8505459cfd64eed843f7b354801c27a7b0f14db95b28fea14fa1c33b452ffa2efd8352a273bcff8f064bc9ed524adbcbc49037fd58207c98e55b214fc0aacaee5d90aae199a14f777040c2b91627a0a76021311433384d46869e8c138436ab7e7a2983b6d9fd55787215f12f68b53109ad5f1f822b7964c262f2c3e7cd66b6962a6ec47f5a6c88a312464062ebc19d1bcb83d0e207a6a9d95ed6230d1e11def23daedbbbc946093b3ff2ed28b7569ba10836f9f7e7235f50df814378035fd794b9d079025ea86333ed1e74f196bea7dd3f10fa8df39563aefd7de5f21156c2904c74a8d317261da7811b7e2c02b40d455cdff4c89df5a44a6c9d269ece29684188afff19b6df5116cc3de8134385b26f27cfa37c1d3f0fc5a4bfca3d1be44d9873dffd2a26434acc28a3b8f1972c54d0e0026bef0ee6d68cbcb958b55a26036f9660e789c92898468664edc029694deed9a1474a8d70ab09329c3e7870ab1edc19564bee42cdbc533493a546771bd9e6219752c4798c19562b66b2407164156a7fdbf5f175150c673c44cfe644804131073db23cdffa7c7a2ace50cf7f63641d24eb1fb72636a4f9380078194f81d85e8dfbf7eb3ecfe41029bc7d5162c36134a3c2fa8635cdb55778f3d8857385af03801b24c4b352262259dbef16c213dec77a216bf428f5226105cd2ddaa839b8da918895dc88633b68ed97b3286e2f4c4245a2c9bcee57f7a945d0ed2d4d052d244e39ec7faef427f22640b40c508a3f98d72045b5d32c65335787017ccaaa08b54f3ea1f9a9c5efb7d8957a27b27b01440f838e3a3880555bca03a2b45afa2a7906cbc64ac5c0515ac3f68b01441a89ce025db178ac9f6d8439715836317f4dca80111e73dd9298c7a6f8d604", "6551ac6553", 0, 1407451662, 1537743641, "c237eb497ce21eaa435b995fc253c823801bf9ccb3986477a6e74942866b238d"], + ["030000807082c40304ac48e5715332938601c404f11d0ea19b56dc8673558f4bb6ff6e31537afc79630000000000e1510f524c242439978eb65097e6cca70a2753b7306c6afb264abaf49b31be1ace95723c03000000096363ac5100636a5351396613a26e141e81387ff0f5fe6964c8989e34c8478ad0d9a491de5d080ed4dbd9bd864302000000020052ffffffffb97f70f1aade76dac58dd111da8d7f848cb87d6221946c62ad210efe3407cf53010000000753000065630000ffffffff0341c09e0400000000026563417ca901000000000853525252535265539ba409030000000004ac6a006a67e3de20776f102700", "515265655100636a", 1, 1595727327, 0, "dbd3e6bcccff4be7cc6dfdd0c2e3b97f7117dd72fb3fa7e6c0c018b71b5099e8"], + ["030000807082c40301a990a924141b4ed5b08f186d6a6ffa3e07794ede8e2f7093a4b173b7be0765b60100000006516a52ac525382b9f0c5033dd3610100000000026a63807b2b0000000000075353636a65ac63c242e00100000000056a635252530000000015f80cec0100000000000000001d0aa6000000000019f5217e67fb853d30e2c4882b70687dcd22ab95dc4088e74fe7b19689a16199140688b3687223ac012877f61397921e46dba3fa5a09cf73839b6e54133f6e14b34620ba4e31503054bc51f2bc5684de64ad9c449bc0aca673880e9458121c5e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a6662b29747c7783c959346acf0d182ac0e8ddc65e075076613e20a24bbfc1ccfaa6970873d736c64f9c43c9b18d78b23054b8d056709c429349c668b6495c16502556a9eeba6f9fd144c94a52c46974ee09c0d63b69850b309890eef2cba09be990667e49c3fb1729b8ae65126c5201fe51d941a0da46100f7cda3630d548b032e4211001f1e2be334e6d7fea9aa9969f17b8d0858633aeaccfb7ffa05daf5f2032ccc53cc006c9734f06ec9d96dd5e43e5c1cdfe396ee0bb9258141c3364f771c0a06ab118c686ec7631a277e2e13804bb372ff72d343d19475cb36881f8d5d1dc0b6c9401f4c187e501365597b72a8a43b79c35839473b7edb559df66b32821efb02101d800b88617c4e00d13b057f24d64f4e488be69aeaf1698293211787b9f7e9021f5b27ea08e47f8ed9dc21352c47b7dd5bf3392706de5bba92316d63fcfdb1cb03129b78bb7bbb5b1217ba6915e95403cccb99059ffeadbc74abe66279b6561788021564f6620a7bf33f7313ce522d05f0e63f1948012debb575e1e28a1f4bb58fa50327f40dafffb3f0482cf4240107c4a5727679202f5958ba23bf5b5bb357be9ef77dc667ace32cdcc93b330b71549af77c314f7542d0e8eb5b7acc5ad3cbd296f289163d1e6282f7ae1d78734dcddc570a12df8013bc5c2dff54cda1d9aa5c763fadbcba19d27010f7ffea49da81af2160e08a7c87e60b825283d5236083a581bb091175a09709eb49f5173b0a4c3c29269b8ee649aa71e3bfe3de976fa32e34a9705a539d405876979b83d6235c9fdd37a37ef5957a30e2e8721b9d9fe566a964a7fa1a31726dc54158e292ed62298a4edad8da448621749146df287956679b953b7e8964b3729593a26ae1aedd68b6ee7acf0565b532dfc4463ad612e2c4615ce4194a14b0aaa03b8eff534d76d1f5cfd924c59ddb035a81ca5566f278df1098eded82a65be0334e27e6de0e53eac6ab8acdf76fdb5e6d50376ff20c45f802383ca407d96d69351679ace759a4c8539b40c85deb75802c8ba3255a9e5ce6e9821f94752e4e1be6086cba60067357e113da5f40659d2fc4a12b5800e91e84e0180ebc3c9922e6aabf5009b57abd13c0bcdae9aa78764f58596fad1b90646d01ec5469578b546a1a671ca4189965af385e685526c233bc7247d341efe60c1e5ad386cfc7ccb89d8dadc22923814c4ce9d076fb6de4dda0c0522018e061d9d9223e278f45a4851695773f0d29adc13df23d85d47bd428d36b5963337d2414904d2d5ba11b61d0b8d423b2716e0315bf70ea9eb24fc88da993894d3acd37d75e4438bc03568715fa0d7f1f75037e750e3c667f6e028aad9420b28751820c5055b369ff6726cffc38218709c9ab7eaf4e42f87fce599efb96abca72174bfcc8833bd71eb0a1f41107926014f6b6deceb8f1baa4aa7c9e925433c8887531bd43f6ca64fd6cc29ae85264db46f2405b9286f2fd0948724b191e88a9a384aa8a6f70211ec088b22fca5285a052d4b75a187967e5cabe80c67f04510684dc0f051543ce798b864d8840b8b6100e2e4225a03eb9066486a4b36f77f88b5f1ed609a0df282f5a118e10416ceef3072daa2a18d65ca8d42c87db1967b76bcc5bfdaec0dc18da9bbf938ee4d3c9e6a421b3227764b188613726a6a54c44fa3970a97653ad548563ee79953ca142264ae6b2c8b077cfcaecebc4d44cf683d1e8243ebb79d3b62be58c4f80954346ceda4ec2070aebe7203e3423b09b32cf6a09041da0751934ef98d6d1185b8e9da9137e138003dc241ae6a0cfe443df23532dbd57ecab9b8cff3c9d22dd26f84f63880424ed5593de331fcb08cb3b21ca8d56c009df4d23e40921d7119847ef49ecc2a22c1a00c0aa9891a0d661dc0f3f78f4e3199b004aafaee30bfee551980c88eaff7337ca427943d9fcca188ca1cd126cabe92e449fe360db79ee8eb965c40146f66b40a73240fdf7d6d113bb5df832e9511ab73902f082ef309a0d3c208f027186b4e8ce0fbdee7bdf086ee8984ded9cc535dd021bdf70910eeae4b0dfaea50fd83579e2ef9d939da2632e32e9cd8f61edc9719ba885948daf7f0bb6dfea256c342b8da47525784854d16b8bb5254a85f2077bcf2e68b3b4197dc7a60842633cce57b01e43eef81943247110011c52036886a0b1127f75079a6f8c206fb7e66bbfeff9f1ca20430a68685b1bcbc0c8ef05ddf5e4042f45f2e930d86932f29270e10a5f6293190266302d7f3580b9f74f64a0af9ce59df72924ef788b7c82e2f51ab65fdb0f1a9a8b488a6cb5dc887f4895eb19b67ca35ca8441d51a6a53b3d595797565c4653010b93cce5ff84a0a16a69b391a901c5ab3c64388ab0a7f0a7099203ce86d9cb406175d41781678dffd1871a149e38786f0af4e3cff8d70bcef1d49bac76bb63a2900a", "525252650053", 0, -97007678, 1537743641, "4fc43a8ec2dcc5711e85b5ec38282698e26d0dbac92b7cf98cd2e5839f5206c2"], + ["030000807082c40302f87e93fa3be87b8c108ebded613dee5f7e6bd1252f4511b5f9a00c1f09aa1de700000000036351654637d26031018db9ad42e05ff133c991bc8c712f8d88727f2863b26b655d086083623c68030000000363ac00ffffffff0447cc1a050000000009ac65536a005352ac52ee50e5040000000000bc38c50300000000066a656a636a65fc0605020000000000e578707be76c3a1300", "ac526352ac6a51ac", 0, 1065294780, 1537743641, "695e79dbad2fe4478a6d5ff2afc572b6c0a69cb73046cc05bb5e2edac386c08b"], + ["030000807082c403039c7663105e6160b37b98cc7909ed2997cc2591f568efc58b57971235fe657ff203000000086a6a6353656a5152ffffffffc055631b0b2f761169d9071534a8fc834aacd2388f07b2a40bd18692dc1cbae403000000046a000053ffffffff19d5768103d50fcfefe9970b2290f3ce32e78dd76b30c05c7a0615578ee7bf8802000000070051636565ac65ffffffff01839bc0030000000007656a006353ac53c59909e600000000029b7a61040000000000000000000000002fb4d7c09efe40c2e8c988bdb7cbfcee26918d74db2bbacb53d398f48dff9316182e2ba18519e309750b57bbe70870d2d605d932db1738be8395d2e58408bee918e37eccaa6d88426ef467c6ccc4e86d188f936361c663e120c64df16a27c211000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009ef6676a11a04dbff2a3f185dd23c1a506be85ea546ea412f53d585c17293536a914b609c4e841271981519c310cac702bf8a2c666e1247f40c8f9eb6a76cf730820c657286d56b6617b1e0edbcf3f63794d1299d7055a74decd36d7f02d1b807da74d83d03c0c7216a6c80764b20eb530c5d4ee1d6773751e90bf447d68efea022646ea2bc1a4ceedc7544ebb1d3c9a9d84ec1360122bd28a9f02b15eb3e188250317b14d3f12f7d5296e659340ae5983808e648fafae9fd03aba52996c9de9da560a071f1e9ccef4717dee8aec047453ddbdac851e9b1ac84029612e6a015b29576271858c5b94d52abd0d0f04e4c636ebdc0c22a753edbd8a757db06838518ce48e0216cee7f386cff5279d506f0cd4c22c5446fcef062b29b74e8c6639ffb7191912031a198aeea491f1004c69cedfc06fcb2becdffb2240e4ef8000a8862ccec76d3e032034b690e3f09a91e581ee8b9e47ca777ea1d669e90fd78aa40c78ab03218b6c0228d78f9f38f5f13646740c4ab0793008d8d6e0d4ed800124eac7a66ad7acd22d031e13975a6d345e1d624e327bdf81828d59cbe7d0464963362195d52bef5fdbeac27b524d2173f184df12721abd4eba03911a9e5ab6d246a7f7cb03b39db2059f5a00c80853c826892390bd7d17bed9143acb3a726a803f0b5db9c03f92dc1908998a47e5eda4822ff3bb3e5272b7a67deae3feec1bb9f08798a28ff6a74a7bcec7033b082b52b0542fdeb4f0343803abb7c1582b248ae71243a75bcf54b7df8b1ebdf37f32a75413e6c9618550d5c10d029d983215f2d0842dd8a1f8dfa0eca2e4745cb3d42f954653195b72b430f253e53f326f9c1c1a6fe2cb193410779c0ef2abd3bec9142cc4c85cabed74fef6e3c474cd82913e44b2f8425b116fdb717ecdcfd8be2cf9cd30c4c35e36c965de95326194b49b8013b55f5cf894ccdd1ee87c8e1c510e00b45acba7df9d442624d488945508e294d21934995079dc09c3d6562c3de4fa2f39cc50700d13389fda0091a57b18eb3a5e1b48e27d3a2b5369eb1f789b24472edb773e31033bc186a437945b08249f11bb98882555584561fdc95cdbfb1be28d3b7aac8535f3a37149e695a00d25f270d84ac6b757cb711d19cfd72cc73d616112ed5cb661e50bc4d1fd3e0726c3bac3442415250709f478d083d0e28ef247fc93756fcb3313b7ad4ffd368a0c88f8a8beda90349de6b3ddf9c9cf830a9ed84efd71fd009bf6910a1c7bb9f5b6485475bb5a3c5dec3f8e8a60a106adb459f5fc4b9ef1d557858603bbc45bb0859539f86922b328c8967cc0914c33264c9c412324b14b20a65ad4ac759050eb91c9c8f27ee594155ce40ffcd5eaed020258b45e8dabdc28d8db776c2c37f0c2e2fff9bcfa8f7898dd1093623f3390a1f7e3307c53c0490d00f621dfd31a7e9ab7c5d41f3c7fb93bea2dc1a8d9196c53fe9278dab7e8bec47f6bdc4a26a322d66fae7d4d1400f3b00b58e5ec299f065c84bcd3e649946fe9290e5eafa828b628b7728fe18ca7f5973a5f8aa5964b075e15093d29ef2b894d9cf20a53b37c5ea4873a74fc3c68342013f821baf4c7ac1c4009786c29763609dda056439ac875611e395a2b64c3351a42820e66772a61f3c12d8d923221fd87e20662ef07bba7f37619b61d2acd0137ba408914d7ed43296690492c9dec01897d84f8e52001fd457ae5aeb317e8e15dde42d55ad192345a89d1a9bc17ec700f32d99ec6a6b5794c08b7594c51a78ffca551c29922190179180d1241bd7930d381a0790d472c2cceefe359b3f15ce161cbad2bd4a669c864b40297686a76c36601dc026e96ffd4d180fc3eaceafc591676e82cc36c8e303cef0a53afd4fe9080ec65d5d07a1afcd496a07be1847af25550c86a52b7a63a7dccb34d04be0dcb635c929ef1f3a2b4f96e488de4d5e3dfe42863d0d23f863805634699062b7f82b26f80776a9e4e26c1ab96b914e641bba182b56214b721592773bc8dab4cd787d343094b2e3422de60809c3498d573697c3aeb446cb312296f7782a2baef54641355a911d4b04ebb3ad2343dc2f7e34001a291239b19a2f68f10a40cf2b26279dc71a198558e5b85c95918008458449c2d6e5818d28367ea94a47aa1e826b62d1a688bbea189f3b49227e9535eb8739b4de6ec03126e2e384bd4c520807746bb02312b8a037d19f230a6e55fe9a5b5ad2dfbb8b0ba8ddd1ee0541b8544edde678601b3328d3df959e320e42d5268e0fddc5a4a2732bf24d8d45faae0cab760d7903a8e4f0431fd8cda000000000000000020b9dd0500000000954e781a68323811d485a54b720b8812e5fb4c8cc3602b21d0ac75237e19c5f7bbf665d9909fd8e37f75de5f9ff33e787eb62f0d2c3d75ce1d05288d10d6f2aef2173f2bdac4cdd94f73b193dbd0108f3bfad9e69612c51fcc83b12be67775ea00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0e9a7a17c566207044c50e4e761c0a21f45cf2ce3b007334ce11a6a7e42d8a491045b1b789c5af0ca3b9126fe678b6cbe6038c87b0915d42ae8ce26d92a57d936fdac06d728d1b73f5db8218ff44db1b6d51717b801ee98f895da44a5f159d0d18183e78b8581ecce2a4d18407dc1c00b443d4dd6333f767aad591e6fde9d72020bb3a5bc2f44ff759fb07fd9bc0c80428c0dc665d2e4073428c0b99ddd2852ee02069556267235e98ad4bf7e752f753470ed1f7bf521e39c7e2e80a4d3abacf13e0a02e0a641da467a0c52c3823b5644373ceff6a2ca8d3a02e86c6c807717f894b820fa13e75933f7acc0cc3d5e1ad962e7bdcb435593fa0a009ac404b4800cc1590219de10c27a2556fb9405bfe355839772afe466a209b54ea4c4dd57a3a9dbda87021146c55f03d4d2fb129d2f0a1eedc6e602dd24112b08bcc07a018eed148874320307eaa7052bae976055fe1514c4eb17b67788b96e2bcd6c79966914f03ce52ce20220ad845fac8a7181439cefaa206c117468888d3d8ec2883a028ca3cb84cd5e4403124772a5ee78f52c37710b687aabf586f3713b4c89fede5d74242a23ebec986f9ea26ab9b07b570a7ceca13a0ed356290ed95ca0f6e3dc939bca926eca5750b7bc24e9c0351d2581d893f034ebe9cddee31cbc902f15c0cfe13a81a61a34661a48da3befce2e9b3009409a718b00c6ac21fc285e508b00f23ebc9b9d641573719d5cf03978b2135a01cdc4d305b404a58518a3bec21840388d1eae24cd65b0fa2ab745af57b42f94d1ef784ea01d6df5a4cb10c3deaf59499f45b71f6c875568c78df98aa19f1ec61995855f43750c1f9ed0f1fcae2581e1859c7ad766bbe816b0bd07db38e40869ad7d32076c8e2ea8dc5974d0a329cf3ab88a01d26fc7e508e331aed3116ee3dae1cfcb6601b6f028c41afd1867bb9e35e564b97a71360c61adb769dc4fad2db05f670fd88cadcd7b3229e29ecf5fccc1affb6584d22b022ffa6637eca71d2a8e27ae45f05d49159ed2a0d1cefc7f40b179bbfda19d5f421ac69d2e66b335e1a3014b21e480305c12ca0aa40f4b1540e33541d2cefe1ac2a900af5c58302b3e5b98118c9ea1286e9beaa83ce51edbebb0b89b17170972f794a4214648f08e61d05f868517039a11aad7219c50ab1e382cc5cb7808347dd43400ddd0471b48112f842f119056972523c7b2fc3cae79962684e67c5b78726420f3fb27412fa4819ba492e13e361a5a0cfb87d9514105df678d0cad15733b89d4d0487da86c1874f2daffaeb223aaea94f87dfc111003063c6d5449f7c2a603132d6fc67658082a2f411210c8d81f0a6a30cd77e529ee5c0a7049dfc17e99f4acd5801678c587467a99661077c8b91e683ec9d6f2c95c0a12c3b03f6046250f5445e60d2aca537958ce59423a0db9d132b4c6268f357656501567ba8d07cfd9a075fe766dde83667a93c8eab36dbbe919f3fec0419db327ff2157fa0ca34375db91720407052900c93bd4405129ddf3e7a06f96ccb69ff3021c925f05a069a043aeac26c7dd808107a625f3d3c7fd117e623f94657c782c56f25b20e1220f81660aa61fcc7e4d474631085743dd2baff5e760948d16f25418c03fd61c7450f6225b9f9824b9a85526953d4606b3483fe2307d23ca8b9dabfdd0e08b3eb2111c8be40b6d8dfdc571b61a86a220f35f20dbaee3346edf497eb51ef9fe7161ebd832f2a9c9d9e77ea2bcd97c531b014be0cadb9ec1d43d206597f43e446b860e2853fad7efe7db0334c23421ef5109523de0cc0f9dceaa9cf397b669640de1eeb97ee15ada53c140cb6ddfba4be834c16f82126310f6e82324e6c81a948c063787a23b950cd80b5b0ca6e8bafd9db2f826a93e805bdb1d09ed36d9787502a178120baa4ab214d74128474514ecb146c8e97cb2dfdb36ede98a26a1855b6b1a03dc20e6a3ec7c267f682d6e941623e11c427e656e2a30bc6a74f1909396a8460a0b7976846305b6a54760f32d1747c82f0c658eded9222afce5a0c4f25f55d19735860d957dd07493e3caf198b4215ff770684aa15ca2b65a717c041092378e359412eb00081c84625e6eabaa44f564f406c0a113377837466c60f0e531f555ebfef161b86f9cf7e013d324e4cf748a9fcdc2d6b33da5c9b81bce8559f674b05472e8b421b63a78117c1d22e00c16f30536b1f3711df0bbeaa3774e3954d1827c9ac6fb74181e8e4e22fa0902147e3cd2aaf2ef448e391eb1bfb5ceee8ba98bce36b19b44fa9536c1973f6fff6c379aa45131d5e0e9c8dae15a0e0b841577a10dcf12f6710408e520bb8e13829dcfcd73431fbf844ec221c20d750192352811a6b71bad3fa61d3a1f53e1e5d7bdc16a8e07474e733628108dc83f94c658ad6fa5a96b84a38dc2c84c8024243f2bf7bc4cd1faa400", "6a63516a6352", 1, 985379662, 1537743641, "440b9425d2f55e4ff9098eca9a165aa76ee2e590cc3235a9d7622000c182643f"], + ["030000807082c40302c4b22d9d7bfcc7e34336128175623140cf19a6936dc01b1fea3031c365814dc4030000000352ac63ffffffff8e0355059090b7af5c0c1a29636608487a8b7935b3d302a6e9d6e186031040ff020000000700005151526353ffffffff015df9d6010000000002636a0000000058ee9165020000000000000000e0f64802000000002f1f0db9189b9d7c746115a9485979df17d59c579b93389a5d7083c6be809d4495c447bb76951ca07cbb5e07698802035267af6a29e1646b3534acdb7446aa40e9c049f36e7e42336ae77597cfb70cfdf393e70feaad474cc7fb33325101e397000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006aae08a2e5bb2f5c2e9ed651c492b254db674ad1c9522a818f2b0ba8d1b7671083c1e5ac003c20f6c72c23c96899360f175a911da7bb3e60b1ec62c4ba8c860616b82d8affa9a195ecd52decd08994fdf36ee9c6d57f3ca57d82ae3fd5b8027017d859dc6758a6077bbe8f9e65a32b45926ff21428beaeed876c02c13882218902178f82ac3134509fd230c98685843aa931068acba5a05e51bfe06c2442a08c4c02143a4ac49bad9aa8113245440c9b83e3cdf29f5ffd7ea9b6086465ca5fef6e170b00e76d30f76ee6b69a22ca9a0e81c9d26486f52d2bd7a0d7ba1da270f9b53d40e62bcd323b1ecf42cab5fa45dcc883d6c5a846eaf7e20fbbbbac1648a2ba283703260dd6277bbf11c92fa8be789d262c6c2a0a368acb42735e7b1bf82a6eafdee703016acc39278f0c273b87feb873ef27658b28070e14f1803501b23d96dcd3c36c0223bcb696fbb45195ea4451074225353d37b1e20fd565d43ddbdc3d3763975b450227cc6a776d9d75e07500beb0e32afa5bf7d1f5b8cb962c594f8296c1ec01dbae031d6ed827584a05b8a36dead324898fdc574655fad16f1ef2e2248045460593596ebc50aaab033bd66ed164f2a9f66089c257fd0a6db70a98d08bd1cb621c9579bc7d82867f652e474b551eefefb389e276c6edaa815a614b9885a25a51c93ef5cb844a24075257019e498c8a332b76228add36c5d2a1b53bd8f6fe331ec25ce73336fa13f54c3b9925b4c557581547064e66e09957aba58fdca882479d1c7fb3a63fd8710fef0f631501755cc74ebf36ca4247d4ec5e6c2c6537b2b3e379ed7e6acb8752622a3f7ad4fc5fea104a6a0ff89846264dd58a517a3e99e519348a68f179711069ca8d6f20b8a672e7a7eedf56b598aa5887b377290dfe0b961385c5cb5749e9148434c8b90b2988fc8f71a2a4bcc9557d9a8d6d666052d6c9c4ac3361736bd3531269d808e5c37b9dd487d056ed496cf0fc4e383f10606278964f4f6c277102832522596d61773ed7418dd24bbaf09807a8ae10113ace9c32735aa48f4f6bfcd29ecda44a572b74a1a6bebb5e7f964d304de9645a7ffd4f0320e5c50e0077e8b34bd6df0c13a3394ad170547661af4d9af5b4655d5d5a056ead3f9babb826ea8b4d8f3ece0cd53c1314bfe5ed085e90793f41274acbf6955259758b5a53ed4a6a300f5088656af5d7197b039dfc08d4aebbac7a4aaba5e2cd675764d98df4efcfef6a1d67e519e166bbf738c8d9f1a060859a292ecc95edcca7e014e200e752fd0a424158b6f4fa8a1a7bd94e1868aafd01381d35753693961f6113bf633c7c710278516dd0eae0eafb0a95537113f4d8e256192f53b1f78963c2f7edda8c6761c54ddd5dc0c0cf96c226331ae7d77112486a572654145ec3afdc7f3c18729f6a3ec452f19247cc7a519b47739829a105d04de5220a784838214dc56bd2c2a15081d72ac946c23e2fb1caa7d2682dba3c09e9389968679c36c091ed2b852151d61f2da0dc5727efd15dcd8db61346fc53b6cb034934069b8d4ed6c8849346a5cdd1334f0d92aa576546b90919829806fc3317f4b0131eb0c0ae8facbbeaf7d8a12be367b04925d775d4259fe07581b2579f63b4ab07201c924bb8e1d90a2ccb4be31a058d8c4e6a8cc47204bd19e8d6d7c79b38ae8935e62b6d68d7e97c5a324f4770f0453f30b281983cbf7dcd8ed4b432630a0e17f7fa051d5b05bc33ffb02a255355b96159e6349611aa5baf89f8563b68d6725fc8522ed07a449ec6be7d1672eaaaf309fe0e21b0fdb80882a993e6b6de5b00d13e97df1535dc9b54a491d183eabe45a4dd2cb1ed3e1cea05522902d257a413b9f1a98522f16a569fee7ae9e79a10643f0d927b07dc197a446a9a79e09fd196d5c755fbe9cc7ff6835d25b4611292aba9134d891b065955db45b2a5c56d8f6ab95dfa4b5e953e6817e56ca36942b73ce8fb5fa96052f96c979ab9e16da72c3ead939d711078ca2a4c4340b459296efc7ce16fb16b3e1cf1e9d80f2678337a0e81ce8426c46804c62e5696e7f16877cb821cb4e9c3e0ad2de080c319676e4d9cdf93aa14643e77ce3fe713d08990e5b76ee9a721c347f51e4d39a533d2fc54e2918561477eaa6636ce8b9b9c9ffbff673edf77b80e1af8ddaff72ed271b5833e59d24d179be1ecf71caf4fc20b2702407906ce4c903a71b71e8d24522c0c67c37b735021f8b916e9009f82ccdcd7de4f312ef4fb099f89c6c1317349f974e58987c521957b76fd07f2fcd78ed44eae8cda1393267f05b8c015d94bf503000000000000000000000000a1a66bcfb406c816b2c3e96b208db973064c6c72bdced27bc8282a7fb8aee1af0989181152aa1f2bfe317677f0e5b749022eac39af56f29ddc9e6ab5e40d4ad01541e6945372537c4e2ecf3f5f675bd7de2703f46de56c0ac2cfdf5ed5303abe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d0ea75fe41b7e6fd5aba9d0859dc951c8c9759f37a8f3d618e926ac768761ea14d0afe8bdddf4e8295e08024e58d05a5137d0f46c440903bf766c54e79e1881738622a956342582eae13fdd1fff7dde4d9d6b08e0bf08002c75a051b32136402a7d2bb5d1670ed81e44c57a3c29860617c8453e2900e684bed4926ddc8e3e030306b56fdaff72b51938d557b155e14d3ef222be11f8aeb3cf3bc8435c9c3c71390222474a13b2953af3df445c12a1b2b2ae498b4bfdcbb25f4aaf8a35316250cc960a090f3cc2543eefb0976ae97da3b87786399d6b80cfdd3e6edc3128072c6888c16f6cf36366b90da85391cb6a8a7426483cf4928a013fd63dda320c75370f4108030f42083421e4185bb8631e93c758d58cb116d6fb8fe483924a2464ad0a1479d70228c7746739cbe497100ae3a22dd0ad1061b40a08307193c9009662aa4d05f4e5020edddca6533fb9b89c06555b02d4998c078e6eeba06eb0d78d4716c3869418e00211c60ef163ab542f3cb59637db3505b2112dce7b6b215c9a10729c2846a1d5a103019334aebb4bff1bc0d924f3b84eded8266b4d45e24f1419fe648d0d35f088518e950c818d9f2634bd831f58de95a0b1e1969878427601c79bf929be2c6cc57c71fa6127057c52166ed640b835610707e75dc3d0e193dd6907bc79cc3f3c30e825cc8b1bdae80ed4d5a67a3830cf455348198e924593a55b3807576730a868106ffdca51711eb40b3d6f7d2eb556e3b6803b2bf5cbbb4fa8d64d13e13b44905f217d35a34f65e90591e1b60bb3043bd8b44c33bc954462053e0c0d3b4f81bedaa6488a78850d9858319fb9e043bd8d3bbbcda1a448ae09d01762fd06db2303f02f6f9fdc77fa9da15bbc7e1cdf4decc66cdc6091fdbcf2559717cff7910ac3ac39734efd2ff2a1c22cb46652558ec45cc11632dcf3147c16aaef193ee6a31ba42fe1c8863560158c5786029b1e600976f70a9f9dc4e90023f204ce3afa68c8af6d6cd634cad0f30695322588e98ae92ebece09c071d42d0a667c8949c5591ab0f5c1381a98201885b21a627fa59a601751e52efb6aa9ab86026002f9e0f0346f322057bdde46e3568aa859af36a267508f40c950f3923e1f5abcae8be06ee86836a95fff156563c261a6cde6afc8caef424801ed53f654e2800634c08c87ff7d894762bffa2f770065adf4b9f69be5c0b569bdf6b9ccf41cf3828218e7878b1adff4da9f1536c61c0244fbf4d55c267dc54711d0b0334984dbde69424172b66735ed33901c0aae93063c28971b61fed25b99f492a10c95364bcc487d65b919430137de23ce0f5c4f4988a4be1a92216126811b3b88d37c5d2b2c0b765c9f6dcef1e75ea430413050b7d80a3de6bdde2b272ca580a9c254811ef3ecf5a63c2f713c2a499c77af988c87bab52eb4b5e8f463a62f1dcff07d70b34404ea42f5d6903c958300d1691e4168b0dc37e6dc4a6142c793804675a49aa9c8d817c5e72f6b49d2f8789b44cf375e6a99555301a05d48a6e01c9bfa9105d6737c3e8472c9c83666b66e709568e94a9516a6af22d37a183a74ebef840aa5f84f9b21d5559835c2e43d3128956a6906709f3578c163013e6174942872967d4fdfeba68cf3dc6445aad2f3fe56915e01ef108271e9a52953b31e51e32d7ae448d52258b4a362412e99e7a61b7d24710b2ac8eff8fdd150263f5f573cbf23d834aa4acd41e083e6d19639e2895ba0efdd8389e4d501fea5cf4f292dd4640cd23ea10205bc3cc7a364908271c084d7ff3b968d65e00b0215aa32268279c8c7947ff5b3ab876766148007e73871b1089b6797263bd507f35984f63f645301aa075ae6fb24f68e936aa6e622adf4eeb913362f20bb40850fa4da368d0b0d0fbc342aa7260542c3aac164acddd35ba58adc78606b86ac62af47390ccf72afff8256e19436a02971c99fe154e553176611f691cc485fd7ba392adadfec693325f3036c78d1a7713d4f35aefbb76adc4eabfc3eae4441aa4afa80579050749076b9cbc1f3b3045111c6c427a5da8e641fe7b12c45417c401c137db301ce5e4daa1d1e86ab32c280e86f3f76b972a1dcc85c7fb436b629f540b6f94728e1c5124a61e18a152a18b2077ccbde4b339672c98f7ebc2f04099691babb8822d35fe199c7c0c86e68bad55ed9b3e1cfefdb0b4bd42222d54f0c88a8606a4eb55011f584e65f5429e02631b056164df81944f426f7dff17c2dd63dedfbf21fde9ec121426976b7166822c2d9fd596dc9c2529630e5cf89c54f2112104653c2e08cccf8b13e50309b16f03973d36b3f71262ed33e89ed327694e67eb6e90be481397ab8e2cf50944cd37f0b43600e2148d28df1e732dfab8b7e69eae44c1998744591215c7c65846312ae535c0e7de6d96785342aaf005431fd01a132b79e3808", "636a52", 0, 1242965623, 1537743641, "81ceb9e293e6e50c6115468561e998d2d2099d2d8ea04db958338b7a6325935e"], + ["6b239674027cbc7881d5f39dc64bab67f999a51f21dd81bbf36db13fbc55baf9638438727a020000000263ac0ba4946a1c90fd2b0d21f729614248a0608028ff71407ce9bb6ad5945d109e8bde6c8ed601000000080053005363520052a7a9d6250432c6b50500000000016a0d25d2050000000006535351636a6a4c87be04000000000463005252963b240000000000026a53e82519f40200000000000000008be1a30500000000872e966ff2028ce2202a99859bb77b6b84862850baf046eb082575dbb9592804809c9a71a26bba6f5bf13872c6d73056f999e7004db11e1443f536676db04c24da6395e3fa9174d2f98ed6e29fa9e1a1de23b6acabeed67aaa16e87eabc2671100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c2ba3c1939b0afc86b5f3b1e91ca8d5994219ecdf04b4ec54f3876bafddbb42ed1788fbeb713e5faea9b57a131a1ce3284b381fcb2ae67cab9a03dbfe981d4b240793e3428b1d269fa02b427c542de79b25f6ff503e1612fe03cdd2806156574e3f5a638daf19f2fe2f2115f979124da5b8895d4fbce561ffdce56eecc9b794f0300d4071aa607a9c020db7b12502054b8fe218a8ab8c9ebc07380b9885d12eb5f02015663d9aa204698acf725a2e90c0b54821bc65261f38414f2be46692d83eba30b00905b5b5c48aaef9709751fcc05a8754d79f28c2e347284de93c612d4c12c2141963db44ce7828dc3ecb57435c12d21ce20c3e6c53b0ff4955ad707c0816074031b45fa8fcc1a8085a755be04f2472edffdf93179875647d7bcc075c2e24a7688030bc75bbbf136a261797dbc08bdb27a701705ca85ca12fb3c0d140e177951ff9703003ca76a93e18996c614b095178d2efbec96ab8ef20d0b221981c79f479e09c80208ac11b7c958dd2210e6e4634f8da701294a808c3239d79ff15e1a0bc5e5f9ab022833538f6dfeb732ca8cecee1936eb75e21a03c2f41c5e508cd79fc1b047404aa8df28538f148ce8b6889d45d677b0c4a707e326660f70fd88d7ba07b9a8f7f9fd331c4156d6b06d689a1879a88c4f4c4a8338d1805ad10cb6bb63c568b39582ddeebd198a71a20f1958d879c168472fa0c1eb6fe790162e5846ec652cca3325d8ba54a1d54652d1a34df7cf386e1bc6cd606e63645fa6cf41f1c74cc6793feb1b3976c3cbc20684233495291f1de18d1c3724dbfe5627694975c4c29e688823cb34382e7d1edd1becfcb396b1c15b203c08b44376a8927b0c0c638a33a5b2ee1f423439c07bbd4af019f31e81316858bf43174cda15237529f93e6db35dee36f67f1f5bc6848b23f58afd12d1dace5769663560863003db30b64c83addbbc87c02e889c6a013366a970dbd812c255f27df4f97b0855862a51753e3201983a46111c8e5054f02321856380b819173901acf24ffdd96e5c9bb31f7354264e045310558282b9a95043e100371a0fd879e3fb65eb11c1ee92b38e4b04773ffdb3395d89866229e69ff42192c8b982ebaa4ef1fed6234ccc6438f56606fe2d0fb04b40e6deaa58b79d2534e167db7d37be57410899dfee4eea26398ec87f937dd7d09d439a02168f60e1f15934ded2e846672c53cfe28de1e95d0214f1be0b6b79429dcc5200520f4b9bab2666d63d513ec639507daeabcace1fc917e669a377f5c237a07cc045d4e8eba8f7bfb769b47ec7ef20d00ab4f9d39556e439923f7d85a8ef62a6117a682149edef3daf666fca157c3e748b4a65cce3b068b23aec9dbe690f99b9bb6658d8ec4ad586cb280497ed9f2a6ba07204f75f99f3436a2a963939938e89e8d51f9748e85c37709f6b4d634ebdbe062d0c622af3f06ba8a53d9c851faf5530d2ce65682be2b4bdf2d5a6c2300712635bc8880dd4ec69972f007eac104024b726965407333718b8a6962eae0616e05f1d14d9610b59275432614d16bc10c57b4bf990b879805abf0581652a9f3a212057fbbf380e62ff242854dfa39db98bb155bd5d10cccdac69af84644771778aec98ae669a33dee6d282e6299b9a30d61688a8a863b045e3936e44f604ac6a29c845ba6bb2a301d9bb5cd9e821ef2d461dcc7571cb92abae882f960c6d56f48d35749e1e3bbb0c0bf9c0ca512ab70822709d9bf6138c9ae1fac364c12d004b3987ff55eff75bdaa9acb9b7cc0ee9affeb4125d579b8b49abfe23de8ecce52629e7b8c825e878ae10fb8a85c07c9e58c0c37626862822c0531323a10afe3e25cc8997eabc3101f36af178896d4a973b4fd757b64bf405e42fe61f0b5587fb15c753280f26ad6b833e82f2b9933dcbf1c3c9fe7f4e559d66d64029c0fddb3cb3a5d6e04b3c31a13d3a61cb3d61315eaeb2edbf64880d99fdce1b9dfecc9f422241aa109698033cf2922423fea9ad836d7a9aec11548bb51da7b593e1348e78aca7d7bc8c958f7b529efb2d63ff38b02fc04e79eb6fec00844d590387ca6e3ed5d5c4d58dd90bc22d9cff97da8563e03832dbe2c4fbcd1fecf17022ce38eb2704ab4fcf2c7b5409bd9486febb9e27150c5f451fd4c8c12887e1b3563dfedfb4d474985df51c2cd235f74f36a991e5cee148963c8e167a3dc5836c8a3e87f127c28ee5bd671551aeb8659392f3442e72b17c119385ad8f5fe636f821e71eb0f51c4e1b7d2579fed8d9c46cfcdb7a739dc3c3efe476a98fa7e1b2ac4ba50946d19cb33472000000000000000000000000005ff78a067a304153bad4ce7a0336e0506ad5cc80625927395434ec62204335ac96add6d5c752a1babf21a14a3ee1dc9dca67fd231187efea4e3871e90385f19119eaf2eab07e58d89607233e3b30bf7dc3993ab771fd596d6d231d05d9bb48a800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000db9f674c153df22f18d9b58fa520cbd3c586fde2e7750c8eb06b3d5fa57bad7e7d39d7ba93b30c855e664c61d6759bf3e8253c6a79ea4d07bf10f51f4fb47e4790937b1858a9a324b4bf1230f942a244c112df554adf3c582eba0e2a4ab371ff9f5b9ca46d59e679acf934711dfdac51a0cc3dd8baab07ae7e819f182a3c02750317add8da1314b028a91242dbde64957efc4add8a44167e1d68524698d2188db00326720692ecbc862c92f48027aae113987a00e7855fef87926bdb95ed4b4642e60a061fd58cbbae927c823f7843027a37b4f7d18607ae160f2a5c45186faa5086e3f43ddac4634fbc375f995005d91c4f0412bc4d0a555d3590d6416f92251a6d6f03142e943cc89a491dac8f502abf31f442d9d68f3273709170f1bec2ed37c3fdf003054284b67207312a90b0cfe45dc1ccc85325399376d200a8a61db9d73d0b22d50305c7368fecbee4125064ae6fa5d316ac4e5037121646acdc8ef66b014705aeaa031e44ffa8c476c9f105eb0b4197ca6cd1684b01ae0bfea75c3f5de2e226055b0e0320afec1d0548aba2b34cdfd665ca437ff1646591690f56a41188ce2e965e202f521fadf629173b73997a790afff1eb64047bce569c93c1df77150590913d13cace388cc19d4932e22650ef863183cb2876b4cc801a9ad68c7499e8ad519f701c10d1041c0970425ccd4c3ea9f39b7da20eaf80a7bf14f7e6a1f381d4baf2078019b8a58efa4432396891f16dcabd6dac4fd9893c1bd2693fec00e2ccc5d8660678d40da61183b8ef448cccfae9f81df4e4c6b2b7ebfc51ed5fc8031685ee821d88dd74b4188f137c3d0b41571770934c6bc6611b871288c1256feedc465387f43dc85b38bc076200a6670ee94e8705e2f28b6315785dae3dc9e2b4e473ca5c59b5a7d72c946f0c6183a29c537425592f6e056cfd49b9d3ecac4d7ff40472c46576b9f22d563aaaf78496467e62bc0761af55bc55c6b5cc4bb4c8b8bd200b47ea1181ddbf3b73c24befda2c2b435ccf1802fee70f03741daf082d00f0a7bfa83e342fc39210ffb75055dfce09e6d8def3e4e8ec42892f1d389120719dd63885ece357f2ad2cff4d14f7386ff8851b1d7b0e2c0d17ac125dfb01a45a53a5e60823362a1ef97cdc5420b8755d0645f22fb2b42285b9bdd6a470a827132d89c6c6411fc3975022008ed3a4628b379fc8cc08573ca71469e980d84636718e9a914220962604a42ce0f87d49768252a271fbe553f8a7d3317bc014d9034a70575fced408743ea5e63ffb24788678770088f3e0f9e31d2d44179339db4a0e7cd5a6468f6a1cd46b9951cdd87536675a054cf3d37d8db6cadadb62cd036c614618d21998f0aea32bb5ab7c2fb59737da545c0bc25f89dbdd3537ba0527f039a5a0bee53fac3dbdf86e46566d65c7a60cad7a12b3ede0b2fb74e252604de8cc95ec836cc1def8c2121d6a9499a8ced21b3d9a078da2e3d71d281b06f15c6b7f136f2dbaa6ebe86dfa2e55af4161c97fcf47a0109b92b3a622b917722ad8341c5035b3e450eec9877782c2a82aff3a2976a85c34a4eef22d2e821bbb2ed3c0db701f309ff9104319e430200686f75d84a5bd40f6d63a8c2a93da098c135730f6abb8b6a0025f8aa608d9acb08312452a0e1b07746a495bdb1587bf377f8efb9b872ff1ffe1b072de1b9b76d4300760efe1543bb016fc20566afde5231d15390c5e2aa7e6f1fd7cae2b04f5870aecb0ab2c585019a646d39eedc8cd4c0bf87286757e104c55e5a1ad4dc698fedebe58bbb5899ffeb4f4251c3824b8dfc54e9faa5a65d58db43d2da2ce819b65508cf989a97fec99615a9e2d73d498af5dc3e8515c5982e94d7f16d7d95bd90c1a5886cfa92161e84e2fe87a3b4be6d2b52d29a4f15e6740f9529f997ea3bdf26a1a97812c4fa3e7e7131d1ce967fa7b4c131646d17c0ea46ba441353980462f63783a26c83b7cbd594124d066b1d6e699769da6f8dbb27b988c66b51edc7efe43252deb89a86cabddcfde08feb23eaac9f5ac0d97ca507d2f79c03a651e6c24b0ae44eb172dcef26877e2e88ccbdc333d913ddb355e9ace1ded33f30c5c56d3ba965ddd8f997bd7b2c784f51add3f4ccbf3131c8d42b8ee0f1a5b7d19b510d5810871bf9ccf0e94c10c90ea6cbd95b1fb34c3d5501e7e6e32c5a4ed6b75aa7d6893464bb05b39a1ed6afe3fcf411d383f9e186f7ddeaa3f4c96274eb7a68957db4c481f85d1a8e7f40dc072709c852e57ee1c1a8cb4929c270bb3e28d7a6c4a776e1a4ad53a66630fb9945f8f42dc42cc12528be8284a8cc315cfb2401810ae11cac09ab72c1d0dbfc87a74bb28f41007727c45a2126be1445658dd78589b9f5a6d23fdf1e7b2b57fc36532068ed9f7f802c5b354d3fd8b4493b1992891ebfb985748f16d8f63d2e91903", "6a00", 1, -871106393, 1537743641, "8c0fe6211f15c6ca908661b56b24a6da6ebe081199fa597b5a630fdd997afb4a"], + ["030000807082c403019ea1856f2bccc1f8954b97e0f4cc1918d451214988b172153ad966df831904c60200000001acffffffff0275063902000000000453535151bf50e50300000000090052ac5151655352536cf51c1f4d5daa4700", "51006aac", 0, -862178087, 0, "5e9e9af5d85e2217ed2af9b0fce56d4e6cc2bf4b7a1cbdfe81517a96f7b8cdb7"], + ["10ed837801072e77252099d6375322ece913552eb1820401350c90bbe00249dab61e7fc25e0000000000ffffffff01f006690100000000086a5151650052000000000000029bf95d01000000000000000000000000142d3fcfc7f157e83c225e4c45d9ebabd521412415e14373c2c5c461d03baef34f4c54ab2387fd4e83131b142f1dd247a7d68f05d54b49ba46028c1ba364d140adfdd1c8c320b40436baeb24a1c4c98d58fa0925be017d7178fd2f798c53d24b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae30b9f2d62e927f2667f64b75d0f75500e6b7b42326f71f91a6c04907707a2469618a4ff4f7e38a3dc8a886ac5ec7278f08a46f54bd04e63b7683943aa2819cd214418489a827be6e4de4c99a7579f903e6dcb326005e0abd446b6e80f171125d9c59be5193cb5bbfe936807d59ed3c0e2f09b98aedaca8f0103164dfad2b4f0301881d8ceb5979a5376b659230157437898bd8b550e3ba977f1c25086c3a72e3031d3d125a8b7653ba124061cc4889d964a2f134b8d018aa8a0959bb93605284360a01b3596e6c47bd47361ff86ecaa02b5c5987d8700de3ad48568ce407c1536ca324ba92e30ea6da6c38f4d9a1abce0a11ea84f8f6d5de8bda94e357a2d5c5d924021063850f5376d4b9efae50a00d1987e698d518b967ad499d74d0a707c0e0c9d50201449e160b0fcbb4867e9beac93a533bf94ae41158ec31b193598e3b006652740201c097e2c4dcef700437d44d2e4e45071e770a29978e2ceecd5157147f3b0b22021073fbca92e8f440d6b1afa643a41880e21e3c9fb71137df2f857464146aaf1c0328ba20adfa27fa9b7cd58f7d14078471989296679ac14f43a60d027f36e5096f3f50c24e2058af933709c04877747e3a11c68367895ab710f2196f4ba3de3334864a7581dc0478739811b4fd586efbabb53df82b6f843c1242f09cc3c1df4fa39a4d9e34a731a130b5f0e1f8046fb217a410481697afe37b53d4c5afcd165cb7401925b2bb502187333509252f0a6ea99da665832a3975d3824f3f817dd419b9bcd1b8f3b8a8fde2544ebabcaf20cba5a8c9ebfe6848c580da2d0f37e005607307e10bf0fd731a3bac726782e7aa4ad3956d5a37ea2bb8806a8309e212b0bc346cf56cae99b672bdda8e03eb3f5e53db63129ab9a95689ee946e8759105088c5acb6d3a3f39acd45a105b861475de6d22a66985b6438c49610343e48a2acf48b581f3bba82a381059c4e10e3a012265b980e15a22e944f075cc0e2fd76cbe53be05c35b0f1e56bee5d786e419832173f107b0bd4890e962583928950990761c1863febb50207c224214ae047561b30982e6b2b909ce5854d4a842d3c62fda0779cf491846a8118c56ed05a7fd421161cc3e74aab040a6b6241695b801b86ac5a31b6ab937a8c5c4ef00f97869ed6f4d3c1339aeebdb5a5e2b8aa1912652485e0bff148f711565398447f2120e2fcc6bb64dd69f87a6d0f1c44ccf6ba4703acc4df0ba701a2f3f727e7762aeec5d2b0ab7f4e1271cd98fbc69794df9178d666b113d94c56c4d788bdd324e04fddec3a94b864a542d14157b03dbe652af07acbea6476b53d2d16300c05a5d18d6cc345787262539917fc59943ffc5e364157555a2b9ef7953447cd389b2da1fb37423b8e70eb55bc99e0f7b5a8e6d4098cbf550767caf5a182033b10e4538c71878b70696a254805d4f4569876dfb9ff312e5d01455e5f14f5571b4b389bad84ed813dbb1bab8cfc8dcef48debb0fb8a01e7be281f0910a1064486aa68a34b2833111cfe5e01e08702f0962f5c0bdae55f481a2298f9184363875a6a7873ae7ea959915a9d6f42d105f3ada34538913123f050a30cfc77aa82a4175c1a2d89f6bc065bc4406aa238407b8186e6794282148679a90bff5015746046a1a88533c74b46a925912b6fc183dfdd7af04d596b6e6379f7e7bcb74a3db8842025b5b5547caeef6da2179b34c997c9ec86f3b02909aabbef3e7b16e580d6d3c26109213687cbb8d0f8531dbd6f40aeff5c14a5aeccca530714d6af9df10ad82ae03d91db7f36f0d0a3c5814ea71eee1accc3a5a5f5848a9645a41c9ac9b5f055e5186420734c602710a2f5c59dd251c9610a1d68704d82b8347fecfea04d1f9c619eda8b57922e2a1326f0b0ae4d5187a111a550f7ece5384e1d2a3605f7c2e1f0c4b91c9e185d58353df0d125a95eeb83f54f89dab3910f84bb1124a997eb727db6efa52c25fe746c828bf80464a3b74176cc9a6940ebed09e2fac807765d92d58c66cb8c8f0fb7eb5c0c056d548ffe608bd6ae28b27d4283de7900d29a5ad293606c28c7e3152e785c909dc11dd380ea858648a14166d34e57a60cba2d7fb21918803ab3a6a0ab569bb88a30c477f91fb08a3a2b18941fbe7c225413c0fbb10fcc713f4835469c0c330d0324ac77bdd326c2509d84dfa184a4def649ab387c90752d088d69aafff86a28b459c1d9dd38fb2846f3e7987bcf48c8e95e9f428d9521316e54a954a4363a1adc3970299713710a2588663101787a201264a767040e7f0a3e62969f13891f788d720500000000000000000000000013f417b2f577b8db2b67b9d016f6e1df64c11eb554f22ac1fe4b030f9345ee0f4f5c82fb2fca2080524532f6768bbc9816012863214c3c4d9994c973502cf920f23639e582a52a17a62770faaa3d7755b79abe131296d711ef017f2c21650c580000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005052837d5b996d731a8739191ed674a51c8341e0b2b14386800282f7365d88473652662f9e9f20e055e8a7cca65716ecaebb064fc0dabb8281a4364e533ec80228a490c6c7c9736feaa4dc88934eb517035136b72bf2a20abb5bdf3f11fc72477108a30ba465f95da3055adbd7536f3886ac5f09b50f9f9d3a1ec7857f77b8a032820d9a9867d8e87b5d4e9c7039c30e4a712239c81727d90aea10850a9db2612032becc40b65a2d3941a927e4845b3d1580efcb42d92f3205a05e34dee198a915c0b05dfb763319f27a9218ccb77e67480cab8d08ef8a2fb211ad9a153790aed8c94ae60c12563eeeaba8c17fc2e41f0de2b7c70371688f888d807396bc46f41c5d10215b20cdd23f1b1d407768e784d88bcc0c9f4361dc75ce896288b79115fca54f3031b68a5b80a0849772a607dfb6325fd9276466153d9618923916a393f1e97fbee0229bdaf8a660e45a373475aea4480e01168ff4ea7636b4275c5d302000e20b1b8020644822d05df6d8fac01d0f22f68f704666a006378c991237c0ca358f2e748080308a5019dfe26964aa93838c8cfe60d1097993d30f583c32529d2ad3ce61d78034fac24327d064a9b89d6f60f7e1d08a49efc1d89de0c883baf82ccc80188f5f47a6e3d662cfa4e192723d4b10e6140fecfb50e0b98f97f9abebd279c2cf10ff16bc917e40bae6bc9ab553465ae8827059b24601bc9a3b3e39da9991bbbb47cb14830d897401f859166c307580ea219f80ec848e605c5fd68c3e780f682a8e8842772d9f50c51869136f0f0cf475df035c8ceff7576933ae0088f8413e0f032b070080628088bf8d90e4f57173c3449f9c5c812bba4b5ae689e2f085d22689fcf3fa493f84adb623b064729b5a6176578bb0ddb1f5bce0637107a46264e554677c88799d76396eca4583629dfbfa134acc5076aac8e7fb29731705079229974d175fc0857ab173e1aa4dbe91925a2850ba84fc2b601f66aa2253a8004c1f267dc2bc9d58c07d5b4e0534e7a8dccbc15adc85f462ff34ec9178cef9d3b5b72585f6fb579cc556b8d61b4a90e1cf4c96c69fcf2806d95e4e33eeb204065ebf2a26e629501ef74f70cf964a73904c19f8a4ee892759311458a182a044d0261eba02dbc6ff99820d9f741eb2ab5ceb496f05130d95b93405dc8d4edeba622f6e8c9e55cc494455aa2dfe7dbb60f4094fd89de1b2a2fb269607e947f557cc8ef9a457e0cde19c33f075a3b27d492c22976be22c98bde45e1179fb494f11f72b97ed3bf96f21ef55830c62eff1654ef346167f7f3e34e8768bf91522808726d2ab0b31ee905f24ca1d9568ff34952409dbc9842a238378f4c8f6ad83bc10e1ae9112c15a5f25c24d1d8e734a4595179bd3593e57950995aa570dc8740dd42b16f82cb20e4472f15b593fd34d9b88d7634b3d0beebfb7b4dd0a0c33095c5d6a28d00a31dbae4497de8faab332c49aeb8c09b9bedfb87957ec15fa04e364f8b051c0c6f04ce0660db7a453d07b454fec4b6ee4acb981eea7d7cefe56b005358d53d85411121bb74aeab61796da4b65bb6678c303f9fd86d9b977887515ec614693a2110afd61ecfda80966f4ecc797de517c51b05bd334b28b04e2acf72a583c2bafe1292172fbbcfcc4e85c6836122d4e2e8453afb6e958d6c5cb3eea6a7b0184ba7adecb2f43004e989fa4aee43ca5addfaa051875ed61f52a0d36fc36dfe8d07aa5d21e0aa4b97d5e6740726ed7e5e84217c7ecd85f3709f5bc4b0e9405359250d6ab9639a4d921e17de37d6b863bddb4e24f8713099f49506653a8db4f1509a5d90388c518159451e87889744707d925220a57f5b9d879ac01d701c4e675a12732596b0ca8199eb874022aed2248ae9ef980784d8fa3d54d71e343b90b8fbcdaf80b031bff1af9562973fff2641d900420d9d79f948d19e62b0aa74d61928c15fdf76e5625fb6b06f9fe4ba57b72a4e49c85a25ee1f2b77e374c2230af3273ab75f710f8763fe464ca85cb8aeeaba12d0d957fcb11414b0fad6d7be92e8059fb7d785e88b7b72bc13ec54151bcfd6d59b87de6fe8ff40cf7d9fecd440491c31fe7e8ad0ce1af60c2c1a596085aac3e184d4edd7ad1da93ac503dd9580832a3666b50d80a8e2b59f089dcbb4f500475bf5373308c4f0e8262e6aff4318b76ed7ab612f4ee3538670c61ff228cf54a1df2a54acd137953e4b9a1e15309b288dfa15a2eb57dfb936d3d9414b9943e736f5b8bf8b4a4622f95189b2234f76b79711108c6d0aff1319a77f8e8a2599e97c11a3bd7360e2f05a072222dcf45886409a318c087b5cf7fc97333035f7909d46edc64bdd6f7ed8c35f6a18cef3c4e0d6ccc5fc51ccc0e68b8701757ba7486b6f18e47b9dbd5e78f2b4330d6c51c0c325be7e8ec1f21308d4c8a83ef79700db837335399afd04", "6a6a636363acac65", 0, 938823340, 0, "23bcafedf35e56f24cb2a742d6fecb509a2090c1e8b6eb30db1f20de7fd34edb"], + ["26380d7603d6934ed25248f177424fbd67e5075f7601a88be0add2c83554163b0a56cda2710100000002ac6affffffffd157ee44561d1368556a206b3020f8cc940b2a0485f464cb47eac95656663f610300000003536a65ecd586da2f0e935bd54967c7e11ab93dbec44a995a7a522ec8a377ef34614318aeb74712010000000963ac52525363ac53526eb5855e0242750c0200000000066a5365ac536aaecd0b020000000005ac636a00536962452903bac90a040000000000000000000000003644af77c16da9ad64f12222d829d62c318d841d8c64d9bfb531cf6594ba7bee515c7fb7feb93449ac2b0345c7c2f84f7d23048d4a8b670e0614bd6a6fcad6c713b0c9100d502adc98dd0fe9561cedb1bc34c824601d1c0cc3aabc73f384d0d9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005575b67904c9a3da0d0b74c91253480ef0c420f87be59f5359fa87408e451fa7780ad043da81c01e8880f432fb696f3225d7adb32284ec2a22fb0eecabca8112a056f05e9da9c7c15fe6d5757ab8e4039f4dbcbef0e106c8b40e9a4dcfed23ef863614413271b1ef8ac0e732b4fdca748312f49cd0109358f5fb0f7c41a678f2030efd57c86ec1d04db759684b73a0623d078d9c8893a99fdf0af5e4febcde335d020f998ef0ae852a11addc6d76b8ef4fe788014f9051ba9d187ed39df8ff32fa0d0a08bf764384d56be465839f83e4fba62c3bb770ce1bdd13e0da5337385e50ca3034f4e3e932aa37ac756ff963a08a3c7bc32100f39eecde3d71c164b251898ab2032a93787b841d672e5bf22cf0f9387291a839fde713e4a9daaa3dbbe9dec54940032bba8a5de1804a1a30d76e95a364c090a186c726f35f6c7e9daac304d07493570326e5164e50a5ea8e0c5af409764a86fb4c0a2c047fde3aa890571835ed73a1e2021efa58eb40c5e97f28c50e25edd6f8cb0bcc797111070f9e93fc499b88837519021bddce9b4c693f429792162fb4929df332e4da861eaafae863bb1b16e95597a9fcd7e1a6fd4d05fe76168725787392bd9b20e214054b6cc6e7bd6ffcf67f68ceffd46ad1c4ef251522f4f2bff486857aa784eeed44015a8efddffa2850a935c90e1219f3e3215a2ac8f372ff67d486420d6dc697a59b7b3d4bb758df87b1afed4d6146da20dae9b6bf59071c8941a9d64c6d77e349c1d44103bbec1b69e3da350d8371a7b3aa7aacd671ac5763d502b10e745f9c6a714b2917c97a45f98d55952b67379338cf3d283506f966c7ce4278bbc6b5a88bf13897c17ab2d399b3b5602c2cd84184ede1002bc3d77d788eaadd04e7226ef4da7902c9ebde63e44a8e36235889e196404a1f4d32c579ec43e53f86b4ed7f0fc649e3470b93805dbef7097ab278ceacd4e656ea04504265ef801fd8096dbf68066b997e7ef482f614d135407eac4d59689c438995d92440a3878f8e7dc1e399c484dc1d8fad1cf98a08c37be30c6f6cd4baf4b68682aef227bb364e7a9fc0e280f0d048e1112b4d4a2a7f57767a06cab49316452e24e0ac4b9502d1d1bf1322d8ca48fecc1bf615413ed74266a86470e53901385553470556adf5292f4d2224a0c0301a9b9d6cdee1f18df20730aee2275c3f055a8c02635500c7f9a11d7e8fdd90f17b1e24d9ecc8a4480043409b9a18a2a5eb78419c499d05e2ce8fb998cb17b2655b8892eb438c62a940bd60bea41cff4c235969bf581bec6f11700d726a19977679d6f001b3dbf8516a16080a60deffca2f8eaa901136679cf0b8e8f6b04ba4e6ee9fbad2303057462fac701a34aaf700aa325aca3b4e99e3f33d23dbdfb9ccefbd470300dc51475d20d0136a5f03a13348612309082deaf8542b98248171e1b701424bfeb8d59a9b0c55215aec70c503d338e2844d3be225d89294c9fdd315c9c59ff881a8450929eb8fedaade0e5aec2c80dce69216479df62ce49b8e18dad2c014d9ecbad2a91b986e365866771b5fad53eb8513b0f5cb57c8338d317c7863f3158cef034b18e14eddb397f7a60f2323cc6eee6c9a6dd5d91005a65e8fddf06c32c84c9cf1f05253564aa4abc9515c5ead4921e42d651b8bb5ed00bc4cd4bb39cb3b5e93bd2c5df25853d4c445d6d0631edf3965c6a8eac888a311bc8c6c2e05a66e8a4f6e5e577054706fef9da69712031aea7dcca218fea483131363a091a3ff55ee805b30a016e4b293a1821b88ecec8779990eb0194adfa8ef6bb472a1b94a0f2ee8679d70b8c60768670c507781035c816f024edeb8ae171ad7b4e7a04be3b81027699b79140bbe9e4cd4fb3ca2f4f724ac666823ff76c5276d349f577c614b1635a071ecebbf0194129785c992232b109d78365e879e7a88932672dde262c3138ad07136658af863152f7e8a0aaf91f09a3138085c88c6820f7e713253468a600cf3004ccc146a62322bad572c96fe33f56a22d59f068f0a16639387482376178f1b0b1e240799a2fe5e853b129ac720bab1e58862fb5880b31ac260c660894b443077aa3292ed91c83e3e45cba40f97799a4c81cbf8388f2bc3c16f89a5c76cd26f3ab9e617e34223b97b16f8f084730920f705bd3e50a18697c1bd7e9f28aead2fe739a8654ab8ccd452a8f84b2f81303fbe3167f6f74e9e78d2c2f3cfef1cbc729156597d1c05ae43c07aa8cab8b90a9760dd3d621a9f839b8a298d0d9b910af9879073ee5450a6e2515d3cbcbca23a3a4633801e0ca82a040000000000000000000000002e6643a26f7aa614df5b626e664795576e4d3d77d6a540f1efa7a430889e594cc16115946350f37305900d1aed2153452d6ca6c9e1c2959c17a2197739123af3a05847fb17a3fc1f1cab89cafe4259524f71014f7cf34b87815d991ec8688c8c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d9f533231d0065ce3b4cece870a1db6f3d72cbc7a96bd18898c4ea617a7ef5f020ae1ef170457b57dd1f46fc7d741e187af2d65e3ef1df5ced214fe87dde352c3bcb20f42993a667be157531a7efc276da11a9aba804db7c7e29bc8cde512c13c7e9f296af639c10ab363f0e99fd8781ad0e359e3bc88a7750dc59658a52ce55032d2364b463b187f09f4020aea6239132573b5eaecaed24c91494526cb51f3c420308d729d2b7d5d901d19fce7725829872ed0dbab4f45d3556308c02f4c7de6eca0b037dcde2b79ef6a70f123f656b2c923941f0b7b31849ba00a0fe5491e564507d8e2db008301785e6821549bc225bbeeb8afc80f1653cfcea0e4ae2df0fa421a70229e5eeca6308005fa54730e5c7dfbb00c52ab51fe465ca878ac9d5cb0c07daec0303c8b76223ed86b8531dcfa15cb26307b00d6dbcf8eb5c1cc430f3d436ec53b9021e0201164713c215f804aa1aea879a2c2f8f020c20f3eef118960b22c531cb9103126164e49ef57a5d75b653492661a294467c46dcbb6d5a07515bcf73071d1d0f030f6351cdf0805d8194b8c108cdbadce1606df97122b1bc5c4279bbd31219f1b10ce5446c9fdc64f5d6d564c6f889728bd8e7b580709fc81550052bafef353e58837df1ce47d595eab6376aa1ae85d5e78bfb98a7ac4d19e556bac31a0f3611a458d77eedc487ccc7a0ae3913d8a2cf669b587541e3eb797dc634b8304ee3a66265afa39687b9256743d233287cf1ec4cf5938d6fe15301b9ec150da0995f8a2aa8909e31643acd3963eb324c7a6402962cd3b658026d9de1e477505f3f5bbf63423b67c824a8d25d4152d64c3e4e1d3431338d7e7a99096e7e2639697de8bd1fccda9fcdfeda2be065683bd2901b0dccc1957474565af94a8a44766e0470239de14cff54906e24e9f5daec30f4407a22443b87b5fecce9b5954f1d645014a13f6600fe290c299c0478a38bf59b6244e5f2a79fcbc078146374e0a2f6d3fc88df2b612d0f62ef8efe5a0b234d84c885b73400765a61dc74f242b43f6c9f54db7b1ad36f1c1eebdccf4de2eba674cda0eb9daf92abb98582ebc3ec82a2825e0443d8b5dd8e5e58f68df71808410fc1f5516829bb3be27fdfcc0aadda2794a78bd38f3f2f01122925649d1db6bfe34495f7bc26d5fcbd110f403c7810dab5d0dcb5390823ed9e4c6b659a026c764d5b9885b6e2915c954ceec543fba7c01664fb0c77a81c806c927e8549cc5728c63c1dcade38ca0d35a322001410b9f1dbb3c9be8cb0e88f273ffbcd7f30f2680b313ee20938a952ce422d24d6329be5216d66f3f433e8630fc9ae3a231a9066757c7ce4c312e085b0cb2d712da3b3dd2fc80f65f26b26763cb3f826f9ba44ff9c5bc3d012ccfc129384023d9d7d8987eac4bee91496d163c54ae12d2d6ed28ce6520a8afc49016afe963c58830805cd1aea8d552ca6f010cd5873e8ec1852374c51b5f193af115f178ad5c3d06200d62e2a58a2ad0c25f595bba3273781b28a9b1cbec07232061b9566baf1af822bdbe8745659f53dc706b47c2d05e155e0c44c30200988a85e07e04ac90d76d1ec404349cfeac37b7ff6b9d9687546194a8c74001f9294575d41136ae0b5a54f09b3eab19d1a74677ce93b2a1c3dcb5106481477f6099de204b9c80fbef7f2e875098e128479ed6ea2c3556cbe61ecad5ff420e0078f9b0e51448d5a9e430b0e3ffef66b769cd7cf2b39de0c9e3daca93534042fe17ea3f6cd88e737b8927bb94e0c3efd05681d335c234ea85f59448ce2d72de47d79a8306f307571bdb0d23a735f7a0c987bab805c30f80b484444379d60a16b3311fbaad12080f6b381e9a5aadfef6e51a4194f8625c379668c82a09eb692d5c1ccb6290a6885565a0d997f59826595501ffc1462e83256b4d0baa614ab10264e643de75b2835f2b7dd089d7b3755eb69bfe4779cfdd911b9f2fb3c4825e3e041952c662335dcfd267f5e37c33055338dd729f6704d5a20cb3c5f97d5efb6d44ed273c6a8ae4ce84f4c76484722683390f42169fb4d48a8aed177b9655adaf590bb0e3f8c04079fa0260f3a65475a8074a8f83dc1f45bbba05de89375a3191425c7db4bf1c61ccd02e3fe976d993bda09948de98dbbd9042c9e2d1f2a2c2f08c929abb54135378016902e75be40aa0bd1a742a6d7a1876a93133832453e3146077970893696e45cbf638b09ba5b8fd49af3ac08c208f06c0bbe4d3791e2f3f43fc207f5ba417f6bf7b986dda6930383d533a18d00356346878949ca3e8de432c3518ff400000000000000005a8e6e0000000000f10dff52a01d0f77a1c0dc61abcb5bb325cd6c39ab24782747be042dd7567316e35e8b36fa3e872b8142bb216ae894a6a21b6bf61225e816fb8f0c2266353ebaef5f965541fbe9041094f972365cf5f9b5e0fa13d3642f800e24c5364eb47ad1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005952982f625b3896b86ffd64e7ccf38ee8a984052e1ce2626fde0cd018b0fdeae7c92515b8af1fd8ab3722500279f21c646b252eff8e9d80e78d21af42830009793f127c86e28809eedf52b23c16a2edcc13cffc986b563df31472b02459027f8d04b24f3bfe8751d1bcd83c97d96c09bedb8f4fe6c31ea3b53b110d985210b503141e0dc9d314b9f0cec49f3904cde181e134fd9476fffcfa3a37f1ef4cff28d70320df86704db89153a1a4c609df0c0d60c7c7848d1d40351058221517e355a2b60b05a8133a7245ce92d0d4afba28954dc267a79db861f2926304e47532050ac9fca262758480cdddda4dc9685a174f5897ee474fbb7d56aa3c3da7b756c3986dcf02037330f51cc6403a51d936cb460c0134fbf3fc819f74f039b92cf4af9dcd91fa0209e4b72c42b1d4f653c28fd7d16b515ad9986893d1530dcd481749043631dc8e0306efb6c2281594a48bf22bddbd9c2a9d74c619679474bdb66a0563449594e353032d2a6ee681354a36fc0b0e1172372001c798d3f7a35da3b8875eff4eec7859e70316e24d2ebe08c2d87038cfee7605cd55ac0bb8ca0cdbaae592be829c8df1799699a42a80fce806f0027717509c2a1f6dbf6fb449b249dde7f6a172fef94a7a176e08d3c7a784f6fc1f9f46ed5859cd49483603098816b894265b61f06fd8e866fe097bfebd5824c398e9bb42456ac103ba90b25d68df75e91ddd9c6229ed292fa84a4d528a866f29e1a98b64eef03e0cefe0b9249de6748b5e340b3faae8e99ac240d59ac0b39c0aa652699703e3ede2b6633aa623b82f040a9db040aaac42f74bba7c66bbd3e262be0bb51c603fdcbe9135aa0bac50cdce27ae02c12d34825d1d487b7ec84a64d3f1d395a24227ba69e356d689db7e70270cf2b90ee2eb10decf2298d92cb3b4dca781702b15870fdbd9b42218e7d54d6ad489b2461d5af1a3bacbe975022e58febb631586876d115f61f2e52dcf9c108860fc4d5911227e450f7f6909253262866817b6e31ca5e27c8e16a8669848604e0fc4723ce7f83b49e9470f93d2a494d32c29400a75b763ee88c765facb69a233b101d9885aec10ae187457152fcdfa1a46e7e5a76a1cf63397bdc06f11531f5b0a8ae54aa6344aba0981a363495bc954fd4293de710d43dfe118c6176ed08ba1fa0cc5c79719052e96cf1e01123ace0e33a51ebeafc82041d34367534a4e1d695a5b741102d9fde86658946ae281639a2158b1f0002b449e82bb53be2e8529b6bb3308a7285d66d026e81d421cfb3fbded4cb60070d7147072ee1a3148ce75350a57b809d691313c8bdcfcda3e5bdeaa469b896cc64f86116a5a8ce5c385ca66b6061825438f8d78b62d71feca1246a130ac5368b92d254303976555de35908ef351cf6359ee955e0497e303dcd0a8e35586cf31022197b9f6e73ab3f1b06b8948885c7f7a2a0c934d62bfb4be551f4c5c25b6d5c9c924eb8d7e83b315d06367138a141f36f7fb7814bedb8c10f364727d00228f5abd7f981f58ca9d2475651a0b3f4539309421da73e9b1021003a17da8b0394129b99cad4d8a742e453cd3c0d76e88863f401f58e7da140952223054c8d064c468fc7f329cd1822f22a833400c91094b64c8aaa0de5075296f6ea148b00f1106ed41a6eb2b20fe14b6ab6ad01a669dd160c40d94b6b6f98c16aa5d4fea233081547549ab2fc9af1b98edb4c625b3a94284b5343d5fdab83baff9d1f9fd8f2d44bb7134fdb2ef90892ba55173316abe433ce5378d42a41a8b44d81f06a38cc9c141cd94509c941f21ceb40b9eb0613755a753f124632d3839a719ff8c39cc4ee2dbf275185b1ac9ec948f89748009c17c408b51164e1720ba0e6f6e724b8857f12f07616a26b798f507b4402630413dc5e832f9dcaf0efd7feb16664ce48bc5494bea74333efd1613655f54698a8f0b103a86a602b00fcb4c93271dc40ec1d277239722f2dd65ee5dc856042d6cbd98beef15d356cd45bca5a5961638410c33f4699eb0ca9e2ee5bf684fde5130e47dd07cc0f063492bd4ac817799f89778867fa0d4cd5ff5fa226ef43476c7a14ed8fa71f4fd56ce5cca932039cb09e2542f381b61b3e2004c740222d3e1aa303eeffb8f99c286d4aea97c89583991cb44b3f8cd093f24dcb3c86291a5cdc36b64d58fc9d967750741751c83d43d06a69a08382728096612e695104fb21acc2764496419becb71e657e4025f34c2764487903d7a192265931512ad5a9d24e099490fd81649d5f50b613a253e3b8cef6a402666d0deb7cca1f14bd7a2acaa6a174a60ec65d24a192ae45fbdd871aad7f94cbee67f6f31d7797d24a514271962b99bbf38662665c21294d049004b257744a7d55561b18f01adb8128d3b6b73e70e7e9ef5bfe8791b44e60b7a2e381e8da9d73917cb79f440a501", "526a5165515165516a", 0, -553338102, 1537743641, "bef54bded6f03298609f4e0543c4afe02db4da8fd9d25021244b7bcbd0e7cab6"], + ["030000807082c403047cc43954f269b23659f8ec93d5b715a5b6fc2ed317de97ea3c1767c160497b3d030000000851656365656a6363ffffffff378b019f78117e41906ca2970ee279e7a2b9eea411588693255a0142c8b8465b0300000005ac526a526af1a65586ee921fcbc222bdd7cf7fbfcfd294f624ff36f8d399da4bbbb45c968d7efc41a70000000008005200006a53655139b781da042f859f859a2207699b98ba5d3bab50d90328af173b12d93ef39dbd1e98efc80300000000513ab67c04cf675a0200000000040065515164d7fe010000000002526557bfb5050000000000a6bda60200000000016500000000ec6280e300", "5353655163ac0052", 0, 609231033, 1537743641, "5162526130c6c82732cc9f9608a260ca40ed472ddf910a6d5f17d84fde5b0090"], + ["05ebdc430244ae630a5c66466c8ddd0f327acc5bb108bd30c8f86205b11cef44ca050613e7030000000800525100006a0051ffffffff2ecaf5ccb515bb96dbdc2d4a61739a9910e260f5e4342d12a3244366be74942a0000000006ac6a6351ac6a8ca8a0c60305d7ab04000000000563636352630b0b56030000000003ac65527d691b020000000008636a63656a525351000000000200000000000000005cf39403000000003e171e800d39fd35877a8c2e2b0f4c09341d7c88f171980c8511de01543b158a629e9624ea6d0bebd8c518e654da424001fbd25d462793954ad80584f7faa109a1cf30d5f17a9b555b2763103b070150cffbe705f9c150611d261d08dd73b09d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d046fd6a6b48435f762d1cef6ca31aaa1e49b7cefdd47a222ef8020c33d002ce960e5cb9bf43f9ab77b8c9d0231978fed58b1e213350ecc41c4d60ef4278149d936410e6e2ee4ce202783ead5c0e610edd9d6390b0d5d27956be560e231b3083d43e388366bb41899ef0c5fdb552468f08928549db66e733559b58df42eae730215d98639a41d5f44a1b34607b8043982649fcf2a3f1d368a9ee4cde0ee3b5192020623249dbf9e1aa38eb34a89ed164f64be22ee92c13fbd2703fb755efdb48e0d0b00c20ee345247b3e0a6f62e47837ae727ada6699487657525abcb47e30150bec8a5bbe64e898e06f39742597de830126254d770c87d83a94c9082b227083c5d2030a11f0dbc68cbd500fb5f19d0bdc7bfad983ae20587d5e2e5897f4768296a3580228be4ca536100a40ae4813e655430fe03c37ef2236a34761fb1a4e3806da09c40227a579c8ee003f3c865b1fb4a91fec91efe2bd7b61463dcd4d0d464b71ed250e02040264d4a14c063875aac39aa1d533d055244f901fb62d0250206f06eb7665d8030fa4ec16b5e8893f65bb16f01417caeec73ef60826bcf4ed7b842f417aa4252b476034f23617dc80cdce4e06e06d1ef832a49d3c1e34880c2880e144620f844e7a516491a7cfa4fb90da1d219f22d7e8c7ccaf5494e2d45f3ddf5e440de0656ac4ab76efe755be032e1d0c96b13848167e2ae0456235d139645b23a73a6c5e66bca732c41e3e8965c6d03611a4d627b37b53a05f217209eaa8a8f5ceb0d6d66f8b7d2690cd952533fc1e55f5acaba0f6a31927a00e9449986523f19d527c9540cb7995c2db6361ffaa6545f11c8ddfb12a987aa5e60bd7103c3e6e62480f41be2d1d6c4c1464e14a4120683993c9177fc4adc6dd385e4b41b0342de8f19239aa6801002be2479a20238a91623c14b57007754fcc4a359f489bacc4e3066a4ef1248fd367b0e25dbeea0af85e2c257a1541dd53fde4d3bc97cd5b44e15c5e4c06d0a095d5731aa42db9d83d39539fcea8831f5c5ceb84f064dcf89b02e1dd455a7e51cde7b470682921f5e5378724c712c55edc916ad13725398a057190e72ac8800b53cba465caa2b22a0263d4b055364a177a42621ae2e88aa1c3f3f8c655669e31d16e9cd275034420b259c80c73a682fe65292149727cad5e64f73c27ef9851b830b458493c02be4ca81e1006d18e7340b485e0a9f741226a27a350f79052134e0edfae3119f94516a31b79faf73e800927a4a1301353c6e7ec1a271ed435540a9f57776cdae92996f7e739a46856a9c71f143c97b43106eddd7e1b401b9e1e5f4a6c2aaa84cbd92f9ab62c2f3751ca00fe5e4ab6e61cf23a1c1d8366e9d145686630a5b98118ad3a1ba673a9d9f8c87db4cf8cde9e62ee69865bbfb3cd053b35a8763629751c5efc7c109f57586cdeb7c93ecacda7ba0e5d124299132432a4dcb2ccf5a1f0e901bf83a506daa4d2ebfb9248b79fb58a4a796eacb35f3a56418353f11d261dc9fe498e26480126b548e38bc04dda8d0551b9ae588620e9ed78961d39737760b60268cccf115f60afee126181fcdf9b274a58fb476c17a7e2c64cf48e1af974d77fb9048b0268ff2116f8fbf39e1cde91502430c5e6f90245394694360ea880b6bf159e0687236b34088ae708266f2d00ebc24a509b1affacfe0caa65cc87a9efc5107134e300831998d9ef61e30df725a82e3f3dca363f90823918f2d0fcfd03b8579e583241ceb0dc08961dc32fb5ff5ff4ad536cfed4083bd32299077bb9c0cc3a6bdf808bb365d4c5609e73ff17001c30707c7d2704bdb0bdbb2a331a939b17e11b168ee83db154eb32fa27681d537f9faec0ecc3ee911b24dff06e0fb1fb7c5606503410b2c541e81fa8127b3053f063f2ca168a85565fdc27ba476f5cdb236c038d8d5b91c13ab77aaa7aa45edddc46fddacfc3f9357363976a6d4fcd138ff1f398840e9562efcb7761168ee46958f8b86aa9130450aefb24feea7e70a8c3c63c0b7fc057c242a7f1ed4dce5f19eb52e49c205c98d9c4364f1efffc0ec8cc0092b7a1ac910013578fa0ce824885294e5ac4e4994a4d9b8ecd0d5614cccf552fa160f265b1edd58c87d457310cea95051c73da41691d9fa0024ea966b41afda2e03406a882902084d9e1aa3651f644c2cc2ac88ba4be5bfaeb48dcb9abc73c6e9c4e6aa5e28a4f74da42e81768960e8720b29f97052cc8c8360c220e80cce16fa03a694628fd94e769fd50e4376bbdad61f918c47733f18ac87f9bd33f846ada48d5b44a13d8bce54e1ca902000000000000000000000000f9e60f46c0c2ceb0a9639e46df017daac99180ca4ee241ab381b42e64c1a4eece33347be905ecb2bc4e0e08c09e0f8e68a7e727c31454e9b5f58f6fc776469f685e5700785ccb15f7059852adf7552b9cb1b9283aa9ec1d5c9008fbfbfb03f48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006af11bde2363b86b62c3dfc641e169bb2e8544ece1c87c60948745f771b0635d64b8992acd0e7f2cd2d8e81b7e9fb5d63699e7d0bb81a57c4830c23098954a2929c86cc15faba4980e9202c2c0cb734e358b1edc63d9a349c31ed476e363a022ab54b18badc3f7dbd0a009edb20e3254b4f5848232062d90f5e48e790a74179c0223b0e45d71d18df72f2afe6f82a7770e2c720a8c1ec255a7dcbb45e8448921a50323a20f83823c9b235f7bccd6f2fc81a335b9be1bee50bf9e859f1744268e3cf50a00f2a8b236586b983a53cdab561fde8ee3ac5f37e44eafffdfc97fcd9c134b83fd29fd2dafeeedfb0e7683b6ee70bbe3d2dcf88cf01312d668e72bdda2f47912022a71a9cff14e706e154d7285b09cb79ca73d6cabc86d566111c1fa389bfa9b0e0226c1ceaee3625b0e04c00f6011b8d1e2d2519dd3db2d7777641a68b6b3dbb66b021ae8fc4939e3b55aca2211e1aaded3785b21fe7f60c437759db68ae7abeb8d370219ae0162ea9f29d222e704b95e859e13c566553a03aefe4ac3108df75c8275350314aa2731fddd1cac609dbbcfec43a91500b98add35a3f2817ebb656aa69e60f948449a5a7f8efedf2778075437e9ed3663b6cbc34076f3d85aef93df1deb8b2a3220e838752034a6896868d295df6db985098dc7008ca11a2840e5db8804030680431a932f5645f245e796d343715c03824694c379c0f74adf8e9a71e76ed698023ca701a61bbfa4c8b712b75d0b71605b9b3c4987d9da1e6d44172cc67a52698dcde1f6e564b29efe7f2503c813faeec2fee4719a47a710e5de2323dc790ebb26eebfb5a3d85d0b57f5bcb76194dce5410270ac19c6201eb3413e94bd84c347871edd11d540ef09582271aebb64d393449678f74ba30b0426b3c51f1561bea7a31ebf7aaf4b0384cf3bcad6d896aed8603aaea185a341c2f13ecf0c21e8594f4e38f5392dae0f2de9b0814d98091d28b88f287be94b1f2e0b24ab8536b44d246bea3d9ac2c5a4ab2b3c6f698674af9d331bffc2fbe06de74960bf638de0084801749413b35826f7455392be9baaaa26a38fe4de9e2da878db171c884140f325d1ff58764bdd68028521a402dc1420c201f852a225fe866588d621031709986d6130d57c7b7cb396cc1a81750d1d4f679a1cf12f3d06e6817a3288f42486bb8c832179a487711c32d0674e0fc044f6a587f5f789cb22b99e0320429992442b06d8c5345d605bdbcee5087a522ca50cb4819e4a8dfa8cb9e95dada0bc5f667ae1129d5dc06014212b8ad3e6321b5bc04d4cfcbac74b90b474b3d56b81f7bd388307ebce4ca2d9138b5e952725ede771723e74d15113cb48dd50b61183d891c8290e552c1cbd8867bb038960920395d19bd27ce8a7716bb269487323f18becd295bfc51c82ecaf014c12649c7a7827e688f66c0bcbcfeb6d8f6ca0120f312162e70db641ccdeb45c2ecb5bb1d327b46678e5262af595b78294ead29e4e90101df67bd5770549f2b295008df8d1a3e96491965d6a40f23210dc2eb9b31a90970bac8cf47de430e0f5205e5aa2b7535d3e62802fa7ae52975a0fe79851efd054f3e1d44fdd277a6f5a9fe6f16da91c7a0420ef07402ff468b7e70e5d028d9c7c9f936ed0a1f05af146bcd142bedfb8d5c63059a6a3d1f5a314559d74829bbee6eba1d4208750b6ae511d8f779e86d635309063c7251229dac06852e42e0bda167a446f6335104891366c5907c7107828a8f396f53a0b5f79d4da2948bdfbfd055cdaac7b7004e6b505bc279ed8b0ed7a034d87e5b97c5ea466c76d7f470eb0f1d06c1966a6d214b378a7a695dc989a14b0a6fa900058cd1492c76558985917503e0e29fc1b6bc2c5ffa9a1490af144c21feda21e96743f6a7b5ac7184246d018536283b4129e9657fbd75e2d7403e176e6cb07c914dc0cd9960c6ab54e5192596a63419dc5a098fd3d9caf45b648ecd7596792da8ec5cdf3d0f27ec141ec05cd891f02134d7a917aec153630b4b87f05506740e0db011a41778136b9b7447dcfec00ed8f532b60bf7780dfeba3374d50ef4f5e2ef8980e9ced67cd80ce989dad41674cd7044b72c1a042b7436a09d1ced598e7215ceb35d61427fdea22b8fbfc388227e384e6e47eb1407ce2fc3b0617b33fb3d9da0d7123d6a6aaebbb19b5b2968a91cd7e503adfb5cedb22ac72530bc86d3e5de670d1cbed77ad28dc2e353aea76ba829f7b77ed88ac5251427546e6f9643424d856bf9ee51e73028ecd1bdfec0ccb88f84bef00f95dcb19946d2f505505e7f9d2f3751f2a8cf6cfe808cd0a2d1bdccb088e906278ac16bef70e6ab21026e15753f933f6040215985ec41f1adb7b185806bc624fffecae02c86ad3c3728791f1b59dc350b1b929dfdb240df1a1b6880808b4186138eb0d53f0266d0d3aa67200", "", 0, 394333005, 1537743641, "f1785d2097c43f13a9cdf4552b500600d90d026cf3d9869d2d9c21576272b3f6"], + ["030000807082c4030357f8a2ad0503d26988f19adb0732cef0c0b00cc58a9325e7069554050254282303000000095152515153005300003dde4cccde06d72a88f06cf6bab0022d2c83c112971b3b546850f7152e09087b76532532020000000551acac5265ffffffff33130ea0c6123164dbbbe9d8a1318a4700adcb2a9865e504b37a6a3016a4d99401000000086a000063536aac63ffffffff026715c7000000000005655365525300d7c403000000000263520000000061921340020000000000000000cac00904000000005e77cd7fa20ef1c3d42e1ea9f6ab3664f9e9c2455a53dce9ead5f583f39d4e912f8da5e0eda2827081127af94536886e3880bec7bef6b92aa1150e3aeeb76e622e3ab928164de70fa465e40b5f83db6918d378525f1b0d8e47a222e8d1d1acb20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054791326c3265ad17e0adb783885fd2f2e3495afafa6dcd4063645666168d2fabfad08bb348e795e13166416d326e1efd8d69d8945019df4feb87334900c116e18e28e3ccea1ea563750a1a2cb283f3af0ea5c60a235f486189e38cbca19352341a25eea347e47886a2faf566f2afac27f01757086f3b78318b46750d00bf004030a6daef5dc349c65e87ffd2341c9f8dba81a2c897d99867017c37bd7f547df3b021e3c9e6593d73c134500f3b9673e071cf86359e6e7fc10b0a1e805279534b4f30a0054d81db5ef08a70644598ebca450d7b0ec3fa8c07fd65939b3cb7e50ef539cb6c10e10aadf018c35599899b8e73e153a992f60fa20f6242f700ea3b591f115030b9dffe9742e6e3430906f1eaa68e808b07172c760ac77b23d3e75c26d70215702102e8ace1c8f06fc93dce6418e11bad717bebc87b9dfa10c475e2c3bb4537f3e0301f0d6fbd6f560db4b927d363863505998368df767e3ea15bec45527bb39bdf003173c164d5a4161a51116cfa209ddc38cb749c43519c5335db7ea96127417f177022cc45aff8c75ac1afb7fa6b0ad3fdef13951a236960079de42f51ac56b38fd8294b29d23ddd27ea18e108159db46bf46b0412eb0cbbe68ab55a6331fab5546227edba72dd73273308e4bddd6287aa02f475c771a16c089411ad74dfb05d0c9396892f1921fb31e2367a877b0ae898aac811255bbb87cac827e8ca8d3ea08a96875a6627283373e55ed7c59d2a913616c574bfff8da6cd7fdb880a15de4598bf5d1c8308ea9af21054681ba3d045a1ba692e7c315cb6fd3606eb96b782d4f90041ce1f2b9b3f0785e9e228b1b027707e314823ff2a27cf139c9095dc373e1a7e8055d408b6fbb09a8d2236665dfcd43e85bdb21bed8c6c9df38817ad6258ed275ab284be5739760739d02024be816eb97715c6af840a4182c1df1028a11f1b46d3eec9ab85d9836907654c7b185b2ab8adf045aea888f2b40f7a10a67b1bb413583fb58a1d649820af3d29771dbd8cb85dec18e5b480ab8ae3d07df1355019d91ba26f6594a9afeae5e8dc50c25dc63dbc0dfa2c8e19572b4a6a0786a8895922a9210e89c210af019f826c2a9c953999cc80ce3473c46caeb9a5f09526719da7076a6cd2f5d525fd8768b6a2c3ccca4ded3867a60d0f227b7e47d45ab133481ee12be51b8879155fd73c1999dbe18e675dd9fdd4dc7518c518f2ac8c67e6e3080517bd63e5fc0c7f981a41e2cea70aaa4ec6e33196813a9a58271af08e55a6346e3365f877b086927d61042d5f735d8c8b228fec3b65f9b72b610033c27db1e43470a3cfeec6add7df007fa02f37f0d07ec4918cb0ac560c0e4f55f28c698feaba64749077a3cbb5c61b56947a4458152d3fd3830df2ec30a240ba8b62aec4c7058dd4d189538fc5ac7832d2f31558d230f238ca66e1c6b3c7800db33d1c2ebd3d5cb53a2f1aa7dd57f32058602be0215a13c2c8a5f330e9aa3619b903118fa4d21e33abe064a2144351ad5ba26579656ba50f1203498690e9734ae966846ce4c51626f8d91dc995f7fb354ab4281bf0fd9faa60580c9dd8a161eb3c71b3c890c170d9db090da84d3b492fb2caf7bb37524b9b7f27678d88c4377e0d2db1695d46c023b1c1f62450c3c67def311315701c67eddd6573005f038db787122d2fe98c4571293ff33650943d83b48f68a9ba294480aa6804ee1cf0c591c5ce3fda6ca5477b0e3b30a15ec81abf5e869194b8cf153d0f970333f340664aa628fc71acddac399e48cc0d2ed7935c0067ff9583696aa23358c45141b55b8ffb2c03127285b92b2f104474feca00f23cf50469cde6bc3037f4f019b0e9b5d5dbf91e09aeb4aceb7273a53dc533d358c57870a9a7c6a314c11acf49cbe173a1977748accb449de27af5685dcd5661aacd93d6bccd97207b216058644c49077363a447024ef65d1995e1467da1f53e7341ab93a1e6885b9d2dd3108e65ef9cebdb7f004ce4a1a1826ad1abc40da65741d55d8a31edd8266fdb67a5980411f4f4b7e1a7c204a6bd9d2f9663bffcfe9b1cc8f63894cb8d296c1d3d365550dda412e085e989a706957d921ad4630aac719c8cb75bd1a385059b48d92e2d419a42f45de71606e0dd8c1b14808f4a3605fe6d5a3a1083738c0897d7cd4a2926750746fc4fab55addbf6d164832db64228d8afc7e4cd1b4f0a15d1ef217973318a7a40938b0bdb128222f01da5f110e5632df062580295c361d9e207198445d1f8ab89cc4af573ce1de1709b60bf53e2b3867e71f5f398a9c822c0000000000000000b46cda0300000000444bb55ade347eb56b95bdd5284e659f953354a69016ed0ed102d7c1abc6cec86b52d2e09625afd3d26eea2b502bd949b39691c557b671d8c02582b1efc96776688f6c5e0e6eb545d0029df2ae0c4de2189a877cb4e452801a278d5d9acc443d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f76f8029d93721a597beff6279a7c49325b33f1947fcf36270f48b6217253fd532a7557258dd8f89ec6bcf302af127685116692f48778c6692933f250251ace1efb1e3d9cde8a5275791f04da1b4d77d985fb787db639a3d21e5b5fc5b7f6b06a74c8b1dd02ecc31fb2526babb10b48f011ad50dba9ee36227379aef642dfbff0223e982420c0b781cd01aedc7f400bd19442f1bfe6a0cbf5aa2d51402c3952666030168b747cd5b572b675bb1f566cad962e7fc3f46b45c993fb2c665fcf5eaac350b0605e7bcaa0725905ae09c82d21685190c70d75959481bebf75ecafe57f7ab5a9edc7b3385f08937f112e687e9af91580a2f6270f5f2f057afc042895b36e326031b04c52569d828a60098e94d1b47ad474d50f871979ba61c95e4819e78632fef030a45aa42a63cb7b8f5b537d1ffc05aab6348adb29c8fdba416998eccc3a800620303b6ad73513b1f98371c5558d44485e037af8df78d1d5ddf94f907544d0141dc0317ff4cdd493a276e26edbc076203171e6759509823004507c0a860f9e97ecbff032a1e318f7ed56ec5e9e45872b94d4162107bda53f9c84487147a3a74b93fe277bc463d844752a274a2106cc04c8d53eb10ce470156ddaf0057bcbb0fd5027d6b669bc2cc9789196e1a0e9c35d01c2f35e480c82c8fa02828efe544901087401eaaad84ba975be7342e59d7404be3591521c76e122ef419f532b843c197c5e07330f8758b9b62f729d681a8fb2b903fd011981a18e0a66265f4a78a6292382797c4e3cf24b72357a10922b1131809e852aabe392791eb9b792350e9a5d15dd561708ead95fc0ed864a6aab770ee097083cb992102b3fbb371c5a34216e00f3399e72779539c4b05046ed4e4533ce2321cc302327f033d034faaca336836ad0af0404804c7e0b11016283c579764e9e68f9476894118956da11f1d48ac284aa63998820d9edff7fa3f0ded055e7833a756ec3fe7516790f5c1bc774f245516d4dc2355acc3a0ee34af84a5dd4b960c741b9f3f2a32451cd2e03440a837a1622723f9f90bf8dd7417c9cbee8799a72c9287efabce9b8c64157053a8c04070c5aea68686418acccd0141ae06bd4506da66b31c06e42311e7cfad22f7aefd13dae581907f72510de05f6e9d2feba87e098be54a3ed55c530588de486251bad5dfc191b537c386843c8962be4bb97df9f532add14193de1552e2a5d14e2d731ba2a932ff3d6b5627e9d5d886119184dc49c518e182d0f2672dba36f9a5b2882dd26cfdc6a1ba63f28d3b953fe424c29c1d76cc4b0111199db8d9158742e3547a1ccb384bcda17b8a36f56c2e149e8f0e67b014b562f0d3403dcec0a54dff7b42f9b00d71abc83308a4d9c35ab04db1f0f6130f2ef79fcb2acdd69f0b24a005e9564b78585d508e14d637acf956d1bec228acdd88946b1390c0b20152e49033cfc03cc66054b524c60ce1f5c00aff30edf7099970134cbdb74fe9f83cb4609ca6879852b8137cf09a9a8fac6bc18ba30772dafff03f77df3ef157f1e41ad689413c91b103b5349c0a9352a432e5a855df89a5a288b367feb9619d304b2f85b87f5c3ffaeb1b348a43d7dfea54b468319fcde77e23f549ac7d224dc8be675c14ace90b11367823a42c4d2e404b98765fccd3ddf91fbbd4faf661a76fa8e1d8207ff269a5dfd9aca6028773b884e3a38b4f49b7bed4bbdaaaf0e19191e4f5aa24cbf91aec77f05894a9b720a3e4e9ab1fb57bd134f933d57b2fc74e72d835bec1c989444117a36ef8e68f5d63b7b3f27f62b68f1db953a3c95582157ec5e3febb52b032f114144c94cfd94b840c239ac0e414a358009eb387f2495cb958006ff10005658f47271e28bf9d97a2fd3460c8b115b7432a843f631303dee7c619babecbf94f5e8c2b73696e7242a488d0d34d78835336b911db592d0b8432cb179879b78e7e2db75b41c3df7d756f3e9b2e038a1cc5e30610da87d3d07dfafc42168994cb9cb6c7e3bba8c85822530ba77406eb3117a852881f871b384985d51b33134e10f30915a9ecdb53355b59fc02f885171579b472837771f9d02f24355aacbf130ef7ba4f83b6d0084d917a00dd2afbfdbfbf402ce2b585c400b4bd355f29536dc884fbd5a3f24714d266116c2d6677b2aeb671b5c1cd8e095112da430f2894d7e62fc5cb17dbaf87eba5d0b5a086a8238c30214f7d6ece2564f9cfa1cdd9894d429e1e1590674c4988a19e74f01eafcc17bf39628458fe39b26e718f7dc22201caf6cbe58f7defd6e83e65b47d208a4db96f14f0b3ad4970f66c8faef844a2e12770ea7e8943c493c9e7bf78bdeb182c5632dc0b73207677ec69ad840768816994868c3a5961f47f3411b9e4b369aabe3b844c065ca53b3c6591f690edd479cd175a46a8fa2ef007f55894a0d19fd4402b7aa18e08", "00ac6351", 1, 1661871064, 0, "abdb48ab03edaab9a79576068b5b7e1744db7d1bee1d942d12c308448b77658f"], + ["413efa4f01bd5e454d217cb0d8231437c603385a59d661d1f436ed583ff2c3cec62354b664010000000151ffffffff03ab4c18050000000003ac5263bc6fb00300000000003290800400000000066553516a536500000000020000000000000000632c0c050000000097615ae9e0128376a912f9d6fd7cd069fe09c7bbdcdd79a299243f2d2c18673a5b882a350694e6f18ff8a986bcb5418c3edfa4c25034ed03925c1c3e4e0488c6f6004d82137ddf2fd9ab11c5f328fa21e33a2d1ba6559505c57dbdc9efbcf8d900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fd29ffce50d1339509bff2e274843f5697a553f5b2b4a90f8ef9c518cf930405a72c07de8e56ba767facd80c789aaee570bb1253a77242d05cad20d5da2ae04cbbb9b243a96a69de13bb5a17d61d68a5e02f60cfe13f7e822d96d6e26d30eb6d55fae09dd85b54d252f305dbb64af3fa3818af68b46b7f8ff6b4e451ef8d138602080c998cbf80968f0f55694ea0576edff74121c5ba5ffd2484f663b3bb4d9c66021bcea15a3a96a53fa5ab48071d6ec9c65c2ceb4200a98fea921e55304931e7780b08523c79b758112cfb4fa4f8328cbfceb05e9376572e8bfa89393fb02554374e27e9746aaffd809789f873e336ddbd07022c572abcf0ecc7d134f6a914a3cf1a031a9cec512523267ef12714717036785fc617bd93b009b20585a440569afc58ba0220c3474afba14fee97e3ad6439f9a1241301568b9032efb6f36fd740e03d81640216e8b2509f7cfa3cb5d479dc40b11b64117c38a99b10ce90b1df03225593138a022409e01fa09a627e1a529ddd7759e891b7523466d22eba2c8257018b6ff9dfbb020144ced145a0a9d7b3dcc6ba60bb04b9d8e03343c42f36fc3662883aac2f01aaa254d307aec53f908b0c82c02a70063f42a051db5fd5022d53011958d2238df7b199db19f9e81b927394e56885c8aec1d3696f7f40ce108b2f44cdc4c82024d315d3e6a6e23cc1f387b45bd46fe32c39ec217312adce3b82030aea55c80b5adec352ca40be57a773bb94f9e00b640dae006d8946bd6050f273c5b1c22d98097711ce2e10b560bd5a4b9f3849aa61a89daa68bd3c1f1324c2895d38c5955d248d34720b40f60e9114b3489db0c24d88992f5266934c3e5bf81e505e486bed503a9692c8efe2f8e2df1fa35d5e883a8dad07971a87cb6b8386e2afa1df7d4dd6228ce9d17e6e03722668571ab6c1b35e66a366e57f17304114df3a7b429f5659545c3212b7bc8625d150f9ac8d1846164d070a4e470d7cff108348cab321fbc3f8bdda005708baa05f692852ee86da1438427a828eb0e885dc93f0e0aea31d3634475fc099cc439220abc6783b9ec592f459b9533ac1d3e3d1f20d51be7d88d0f7c8436c2e50e9232d5b9f85e3266986fcac6ed9a545e709c9c55cc3c6f80d250aa822bacd124ca1849a379f84f6f057c93778c7066e40a65ca3b941c015a4a00a7b554021d413f789a27b1a6165408f31eb552967c23ef7cf4e36eda51e7ad874a658d7472fed6126329b3b5526f5c076c5dff0be449a2fce863fcc7044353a1f9b240dda03c34dfe0543255fcc68f51872e9969c1c5a9194e78b8e7456b48cf1f0b6bde9f7e8d3f40fda7b7408e515ae06b5d2a1c287593a711ba38e302a013a83f7c0fa22c910513b59b9e0a2fe7d6cac05c4b312ca1ad3e39630237febdb30af2edcf47136ae5b20282aa16c340ec4980d1631d4d1b616b0ce361de3928f15b5542ab230260847020e12e2dc430ec7dc76be1aab7ed15e0022da9f1fb3f207e40c33f0153e025de4ffdaf9791aafb55bae3c634d0691334005c20de9a6b92acfe3d616dabfb253a5e7b75474d29fef72538d023d52f915dc7631059b1ff79a48c38199755e9b7a33a0a7a45a4f995fbc1638b37c434fe93c9436d2e1e7c408a3d4e2f2af26094a780837f45de16c021b3328a068ab80f2d6360580dc4ed3162b9988a5749183a64009168b2cc3042d54d4787eeee31cc6ff3157fafa2ec18cf402f06e33fb005bd37271ae6b687c16634f35dacc66f0503ef2b39da8be1472c5f7f80183eb639bc911d119759dab82c626e51e47f508b6dcb487fcb50f2dbb1ba3d161375d8abe90f8224cbae83628ad93a60baeb7637f29281e0615faae673f3369bc2fdb8c543b1af3309f0faae740066a34ad6d16258b4201d262a1fddeee09083e0f3dff6f6bdab9014ecd86a511fd89d723ac4de30123065520b73b9cabb97141377d1cd9132c415a6cdb5f2956838c9cfef4ca763c01ed41d73082d4c06ef26729b4086c05dbc9db29009ef726272e654c1cf390766ec73ee885c0ed38775ed4a62313bf359a785c6b8b92132e728821d0047353d273b19c88bbb552512eb85b1c39da4e841dfda23a5b655e9237e3be67259d522e525f83fb60c7b90d0c511065c6fc8ec463d89a055061d49f28e46c6a8558d140c8469a3f7d09f72afacb3b22a9001c3b907481778343663afe08551ee0a5e2e2afb2ff3d47c9307d7c1bdeaf340f627c858bc6e34b52b4b6001cdbf369a870d1ee2fa8a3098d0068138924b9f4a11f380a322a948d7006364a0000000000000000ccb2700500000000882375194bed71d5c87a15fe6f25a63a6ead3599081b66c2945f2d4c30bb2aa098a7089359191e0f758c60aced9c8c761772c3e9428414b627069de5611edadd1a07e0a2fdbd54c8d06c65fc0a7b78ec4780c01a4845d020afba5f36164e7501000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009a91774181906a4634f9df9305250c55891a9239d04027c22edfe49db5d63d4193854ba189acf7269030a981389eb6336ca4cacd122dffb069a45013e6b42eac6d2e4e7f48172c65110b9891d1ffce625142390c56232bc4567931125f062b7b1c3d44fec3f71f1a0de697068f8c9e7077f325d39a037cdd1ca9569db39a81900215538f47126a8c48bc23b5f3b432d02bdeaaba7a0672de49363684d768f1140b030e1ce9d85578f0a04df21367589d6576b6150fb194c4f46b2949c684c49ef0430a04a381b57c391a4336b2604604fd27a513954e07e99c0091d262cdc33a57c4cc1faa26c6b51e17a0ca2856dd334cc3f3b502c92ab211749622077fe0d99555a90206b845c40280eaeea6a9b63d9b9570c2933e6fa9fa6ba171e80fd0151907ad48030e09f551545795ec8ea067624b86810d13ce893ac37d5d605be97064e658f42903147ea97e9bea8eb63989ba129feb4cb466ceb8ab74ac103c379d9db91b4b0e490324b7613226b69c1c6caac25b8f36efbf4607d77633b70495844f31291267e5fd0325355a9fe9cc65dab72a29f288f478a9f94be60669653b15148d4d594d7fc7a70e9a8ea44560c698f4d5c274bcca70b100eb05ab217b3f550d83d28d6e458ddfbab908ae07eda3ba330c5aa961543d596ea9257893780c7f551b1ecac38c6be2aac71b7a2f3039e2a66e0c30e48c9710b4069ea8e8d8f13984e0044c7655b2efa27588bd6c8cbfd859b3ea7d8ee20ac9e95726b713afa85a52b884ec7289552f1248fa097f4739f099cebd253f30ed50d1f1770104a30acfe1033a1d6a7fc1d0c675f1f279f3f94658adb16480e26aa6afd8f47e4e904acef2c04485a7c4133962434d7081f5b103a60b3bd80e04434128d2d7d8470364f9e787339bdaf9fd7ad09a3cfa4bac5ed9397b031ee672abb9abbe3bda65416a059af8c0d166d39389707d40446b2dd42993465daec933dc6192051e484ebf56f2b048a340a36fda1e99932a1788cc1e60974e29fd2afb92c53392920c231b7bf95be1b7d2719e819b24d6cde08756dc17f1399c11925e55a97caf8359462f193e13009ee1dec20c402d032ebd0a85cec023fd64dd10185c197775d85fee55290e71e2dda9215cf21220f4fc5733b6e5e9c674cb7829c3504ec3c285eef5112d449027203714b92f712f6e68a851b30016f66e5ef70ffcc38bb6262f44247c40c3df124ceec964fcc6312650b598a0204eca283d667f98c714fdc1ccc29f46710a5a244db1eba78d972b60e515c046354d877f44745a89b5623c6706ffae7f70a99b6d989c2d0a7b627552ad9144288672272b4a2f9f52e6f2e0f44cd082b1da3f7bec4b35d5eef57522d9aee63709f757370cc2190bb095ca5afd7371a8804b922ca369d689148a709eb81f2e3a4a3650de50dcecc2aa24de6d8b11be0e5b5a02d3bfbbd8e22f71eddd299020ab1f18753a6c9d9fe6c9fc4888e37707b59458546eccafeb2b40232a75cc88bc9743e9ac46b1a893607be33087ccfbaf68c4f66eacfee1e1e6d9166feca569e1fb6f6883a4aeacc34ce3210da29820dc83ec69c86baf30f457f636a54037ee4419aa2898f08c8db1c5fa3414ca6e0e4e4778552d712eafd90b2080db11889a721fe93bf55c6f1c864c45cacf2b2043bdd30894e82d82a13581e4d027891603a96c19604ca3f188fc10ea0cd928c63741a73dfa31970b9b73c6153d867eb507f0fe5725f26d46f38cd1bce95d062ffb55d1769edc220fc3197ebb94f3a0f7739213015a661f40c9aba5f599c3c598b6cd664aa271622e36e5556513e37f62e29c34559586de6344c772e8b99ab7003a9144651b4a846cf388b4533b416da73448539c4bca5082c12feab7ae7f09015b0493dd4599cb0baa53fdb72b20aa606daf625fca321d8c19a7e5a9c48dcef3804e63193c0480c33efcec3bff3bc7b870e396c89bfa82c832544931f5b5af12c8279938465476f63b0d14d394a702f301a91bb596354c4998a173e179bb60367270b062d392853e9894bb604e7c9dfe58f8e552fca3b4a8a95d15abf017195ce7dca84e228794868e08af682f9488702ea9e3d0255f93d7f1adb854b1ad9cf97a471277c22d4e13b0277b5768ce1b8a98dedb34a6a6cef579ce6dcb95240da415aa6fbc9ff63106846e0472dbdb1511db01300c4ce2b3db10ce81a054914e5c94d45f6f394c69cd718bed7fa7d6bea7062df68e2c1849116786e5f89fd6f88bdd2c3ec51f7bda334bd3df77cab044ae6736ed797a94e971ecb858dd146f09a4938914c1931fd9bf408a6da330713143556f82b922cd68190f2f09794e44932043abb3369fec867fa99dc49ced9a5340a9142593d3ff99367593c091193bdb1f6d53cc563aebe75fc51c1e1de4ff0179b59e631a76a4b6e01ddd96da1eb88700", "ac6a650000006a65ac", 0, -1754905942, 0, "1e135b1ac7c3c66a90a443f7c3e7614f2f4840e3c7190769a93c85bb7b82722c"], + ["e650db7603b0d5957ae22ef961c1fceab916a94e8b0428159f8413b6cf0b8280565a12bd980100000001659f32f92f12822321c8698b9ef393a8b807e05aa6d09b6f2fe92ddb27afd46fa1542c2de40100000009000065520065530052ffffffffa57bd3f867aa60ed13e9073b1f037743b26742bedebe7b3f9c144776529af8760100000002536aba15ed3501af72340000000000025365d9f368c60300000000000000003d08ee03000000001a7c31309e12a88dfe2c5e919e046be505747d0dc3a2137c1c7e9992337e3ba8e9bc78822cc4276d122f48c95340f883bca398547ce422568dc516b16f70c08876b4fb43670e0f1af711663946d9c33694dd7dace9e53373282383b23a1d06d400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000518920725ea3f565ab2cdb29740ea0e93b4e5865d2082149863561fc94ce822fe3a4e43b09e4c15106a059f813db0e22e4af52245d64bc4965ff4eec8c0dbaf21f3e2367f929e7451da7c08984b8a3f5a7589ba41cb788b1752f4b18c5e4b18e248c768ce61c600fdc836c95ef345228c3e3001783d727421db377b259c4d5d3030095173a39981845d90cdf944d1163c73b8538ce7293032d5c7144a42a2929010224593fad7bc618b65069e0f81272a2e3bfba48e080273ac7b5ebc8f3b6ce62610b02cd2e34e7d42942f87f58caab0a08d34e3539a73d83bb1810d1f86dad147882ed518396d42dda629e95edb7439d9a8be6fd261f5029f0c108c3cade8c2b0e5a0308ed5a3d600a11173ff8e30e6ce56f64a3fe8839ec1da80175b9a6372422588d0315c4c4a8413dfcf4638667aa36c01c3319c0d71280f725c5c3774967b3e70626021282082698d7a1d785f0e1b0c49047238b74e60b1f59d258247e63d7d5376c660224d1b39d85165b063e2810945413d2d2bdb4e0cb8b1a0a8602eaea09b2062ab3022f481fa2b52586a06e2b28bb2ff9ee007e92f5b785dbdc6b5e69e8f51d8c982baf3f074a24593dcaa430ac6fbb8df150cb23feaf8e8a5de5d6c1a63f9c4a90d128c02994e4adf3a495e512aa7ba9ed7c11b1f9f5ace8eb028009beabe0d5eaafa252329537e20f69c715273126b953321e571d6d1e24565ab7c6dfe99b86f188785e00d9fb25d993a6e1c1b16ab058f3a851a08a7264576e9137040619d2ca870c7c822ae226f228669296aed0a0fabbb992ab31f5f0527e2028ee5b53152a2bb1df429aa43af517617035f9a646ea9b8ce40c3cff6b9ebc1970260b68a2a8df2c2decd483398c6de0809cc01d0620e9ee2d16f59c06ca36a029daa8510d54ba818fed3d94f5c9554651aac2c258cb39bd1b5eebba8bcb211791055786940294569b51e7298d2fc8d290bfa8f5862777c0efc0843b9dcb29ab79131a7f51767c59988beb702e0d8a88c374dcfd5cece68f4e56541246a8e21a045781f1d44426fd3a67048e641220d5e678997b62468d3bc11426961a4f784593f8e21a9b397309ec2bbd573fa53f195e667063d1c7bfc3974ffc9b4f69378213c9d0bcf69da7a2705bf0e124cbb1ffe1eac98eb954062ae1e860616b52cf9488e5924d75d497f84036bfe6dd8cfa881b3a39741ed5af73b1403289bcc85e1efca8aa708b930c20244a3d092329abacaf78cfbf6e28e8af6a409c9d405eec489f05c6b7b93e642a7e70a9e90f12893687c8062c511c7477db583bd224eaba8d2a636d083b6103c7a40898eb9861e0dbeffe618c0af50ce5a39df824b1774ee85d6ec2b6cef1d4282ecbd905ded4cba8903853037361ca4836b5ca5dd069923109dd3dc8b81c56c565b908342c6a92f9aef55121c36665c5bd3536e3fa8ea9ab06a7d79391d3642f0a9f13edfe3f0a66971cd6238abdc68a8e092868bea869b65146c99eb5f639e434a253492add48534e24118a4f67fb712beaa41b8cfd37cc145cec2247563b8c1cf0ab0f392115f7b403bb339dd449acd0139fdc4f069401cebc617370be2ee37e313766495d9eefa39bdc525b0aa12bfe53a38eaa97d7b9e82f50dd45260881a160013c89c1921d601e79631a1e8b717d2e4e0a556288f44476323dabb1dcbe64017ba57776ec94d69f5fbc0a86e813f414ee2e956255dc3897a1421e3d29d2150a643106ecbd56a2a4255c780f3f7f2b601c82d33167ea047910b5321779487c5856769980502578ec8aff1577e1293db473af117156342458f1c12f32abdba9421ab21ac1bc1987edb2267f745fb1940367d45ecbac4e6e7d9cee0b6c86457cebd4de99938a8a96c1d81126a89bc664295522dc5c5ca6417d0bac5e56c93b104e0f72deed6fd76825b54a3c1160358103600cd1e390e718a5f2d1cfd500670abfc806dd1bc62e6242cdc78d792a78f81202a45f6d36d2d5ccd9589a0cb3bbaaf97e544c265afff93d80500b8b7bbe6999794eaed921e1ac09771daf1ece8c4e261e627aeda17a47c454e5eb98f7b95f8983731c12f323b86d908aafec63e8bdefc612c7a25b69ad0b1d833d89586e1cb7ee465d27810489c5d5b56cda2089731f7c9a19b76ad2f92e13745f13e27a64b91f6f6d59b04801718f2929865cf8db9dafc61febf6784eabdd3a71b7e585a9cafbab86abb5f1cf77a403909ca483ededafc8003e0bdd5f219da321884623a0d2d8f40d5edc7cc1fcc1b33d3a87c08e0a40bdfc0dd88b04536a1ffaf2a37b99c5b7d204000000000000000000000000aabf35de6c716a3d0a9d90e2abb12842dd26ae29aa6a518cccefc8774e1bf5b717db7431bdb9bc7cd46b94d319f3cf068db8c88c136e89ee1c3f0f906f92364e8f2e8361db7749402ba0a1dee3dd92b9598fd24f4c6e8001781071ae0bdda7eb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000abab7f96e6edca86498506b8435c4e43ead4e08e0b62302e3d5ea0570d2daf015a727d677fa4765aa401d16ef102f06bcb293e29d62c87baf60c6266126888db1303362f148941d1f808e24c41c6d508d7c968d2f91adbc3ae2da48275d3d1e68196127fab5d8e494c8b5386879dc1f616277d4f31a134fbfbbf2e3ddb714b5d03216638d7aabc8f165dbc9d042f8226f018e3a7eef1fd4332b149413560fe3b59021809338221189e38846b2b0a87ef164487df89fc78a8b120967b841e1387bfef0b08f3a3a345fbcaef8da2b6e7b7dacf1d8902e4feeb20f0df5aafbc24ec910f3d795d6e33dcc54da47ec034d57fddeb5d1a5b20a9fe67063269116aa285abd85602239145052e0d6beb3a7c5ed2f4fe417bc01516bc027d264e4401a6658dfd1939022035f3068837dc5207c0f5c96cc98197ef202f8521dfa619abeb5b1e245dc55c031742e08cd7947fae69215a8a4bf89364da1cbf9241d648d88dc5e7ba96c685a1022e7f65893af86cc49f8e71521021f48637f5727d10442ae9289f809948030d05032f2ea2549afc92904da59e4cfe9615749056a3bc1fc8550490500d3a41925ab0a169357bbca03526bce8d8335b9c53929a9f696349fb02833bdc068991df5a2ffffbbefcafeeef2d2f734022a5566c8622779e371b0158560fe679563ab755c4036603c76493bca3e237c5149faa67c579e400f18bd8e74fe69914557b7aa47b0f4e5eca0cd580960b0bb6717844844cfc7cc82d97744d0722027c81f12c0b78b5d6de5e5865a86a216d7284daa91267224c29a2971c6c8bbae623636e44df2d60a8c099043c86826e68126b4af62617ec82093bdd2072b57f0003e735d0a1e8cc690a92a290b7982546b1f176666eeef093f598b2aa96f57fb57212b34e2775c1f773a4ada4d29ac380aaf01ff35f3956108baa750e1227598eb2cc0d9062982a5269abe61b28acf29675bdb83722bdbbae9de18eb4a63e0c2abbc658a5bb027afc22d47ded18fac14c4d04a2c91720b81abfaba7988fd0caa79a846b08550adffcf0cb30bf15ab253605317a2dae6f551afd3f16e09ee50825613df7325f1be3340292827b6185726f4454a868f5659ffedce5889e23e453fdf4837c4694aaa768de951e875a782b123c9288ee74bb6f1183dc95da2fdd464b390ca7aa0a7fd180df464b4bd4d31e1a997fe6f90ac1413c431cb60e3d45658ef8c125e10fd8f40584f884debe0e2d12263aa3fa393d752fe274c2bd314b0c53d5f8d0b57fab9e05caecfff898feb691633e525860d10829c667acf38bc951d1fad00719548570fbc03297f38322626f56da29d540968de17a32427c54f2e5a8026f60c30955bdc765b5b8dbbcd34aa44d3868dae36b9075922d9d44c1004a2241ede0f1fe2e7fedb0d436485f1db59089ff0d271498d741a568b6eb08a01356fce94e92e871b9ffaf191dd3b3ebd8a68ba59b5a7c81ac37e17f4d40c3f71b3d706a03077f1d2756513e5e463e994e0fadb6a2bea3cd83155c1fa372370dd1fcbce49139b874097d948ea71aba12b9e30b80bff2d3494a215897da938264de9df79d17da48d6d512c9a4cfa51f8ae4205eb47a32d08c21b14fb45eb9516ab37e5086f875c2c227be217b773e9f8bb4d0bd4854690ecd6a38ccb853d86b470a3118ac44a21bd2a3aceb47b3633be63ee768718751abd5aefef8291a9fd99e2259d9b0afe5b12f2af901b3aa9418d4f5dff406432bd015a06cadbef0cf6f72f4a2f228b6613665931eb2650932711aae139944be703aa6adcd19a3c9c7fea87190af062eb63cb672edff60081e76dab19dde163359307bcdf1ff4e2c2819e5e2bf01d67f811d7bf2647f926f54cd9006cb4a30da34a7d79117755c397215f9e3c5b5f6b9355774d603296a489aefbc667bfc4da2c052439698d38b9a8c69fbd132e9d4fa477ff17382f32339e341358d35ff49ee3844d083daee9c27009a1724d36c3b269639d726cea4e3d369a3449e54ac0b794b146d5ec6e3c7758157a63e0d324ce4757b60cafe237a02b86be5658cab2008037848a297290652bd39291b665a1f2ced3f640c48deddcae603f7dcf6b222c9ac32a8116f89f851838e5e441e7c4f854e66596003fb6109d60ff4c691c611b2c55b500d94d7255fc5940eb340052e54862fc4b71ad046a7e1ecf303d4c8f5ce87e0695ca92f45fa7027a0e9ebccfecc6daf672ffaef9f64b5d84c5e417e380c7461105ae2419147a9ec6b23389fcdcc8cf2370f2f49173b69d22d7eb92050aad85ff5cac10000000000000000e1a4f100000000004721ab485e31d0c9150699a2ac4e474711cfd94719845c0f74b673e0e95a0c4674740379c046b4799695ae119eae853aa6536f2e7e4eb5b59511b2bf215742f494b1ed3d9315b2af742c6cf27e539ad7b2e76353a759876a8bfcca9e76acfe81000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003517a47b71d7bb821b3677bff595a874a5806542098ddee1cc290a05aa76dad292fb830672c17266ff0f2054e4be6e31be1d8d1692ddf1a1d6f5cb3c16a1292182c50a969c5f8f36d6555c9bdc37740cce02d9556a6f6f61978e67b00a5b2230376543dd942f5e631d62864386d70c47b6f6d416b1a85c5df2904a74e7c45ddc0330091aaa5e15b515331d11f9df41df818f9b1f49995e06c9852d51d4c4ddf509022b0afe9ab7bca7890294de5c38d7d4f4bea14adca437508e6bbefa72d0a8a5ad0a0258727f618514771029e4872654539c3ca4cda17201b602a4a62703bbc0a772cf62eb8566e4397d858831b0a0e515d3fb98d9f0bf8c28d6125f8f8f34f8933f030760a6fc185d9ee0a978afb5e53f8ac95bbe95aff329d37eac7724eec01bb293032c224714169fb4b732bd160e19c9425966b1985e8fce22724a80c945b63ec2ea032138a5db4f373654f58dbeb88b76a4500603bd9fe4bbb306af5beae9e5bc6df4020a6b7f14fdbff0c97edb6a68ed577baccfce515a773e2e46600b198451d27e2b0219f563289fcb9a3ba4a9bee61a03188f803734b3a33863a6d4f016352f9394aa16e9ccd4040a5204c776b22845d954f484e6132b5ee31487e0e8cca1c0a0866bc9464721fd2fcda370f13b6fcc0378e6b98b61da1dea7e3b867c9cc84cbab54d7f2901b1b7cc67a5e615baaa5767edbc0b0261edd92f0ada48d3eb0217e18baa639cb62eecd56c788d1ea0eaa893ac976bff1f7a3ec9572e31b944845153c42920b827d17652a8297f4593ec13ff12debb223e694f3dd8aae7e255991378f57e3523f8c53d020422f33d32c54e994480db763cef7029924a022cad0c4c2e6d1c6e44b02acd565474b107e62df0cdc9eccbdd1508a546d2097b49852e4bfae7a8c9d0d934799c614fa737b4b10e4a06ef00e4f23b891b6e85bb8001f3c58ac1d7deb2b4b7b9fe4ab79eeef75a366765f341daac511e64badc9cb85cc34a3e9a66dd839917414c5e64509551eee4a42ce7d57b9876ee0bba0a97f6d459498742d68ea0ec9754b3ff0af0719c89006b302a995e498c7ce110af3f40f0011b4154129a5d0068944b66736ed334293d1f0f0dbe53e67c122c8b051d8f184d536bd9e987bd8ed89d01e8fa0d73d1a416e3585fa5a6214965a907eaadaa6aff1949aac8e597cde4e1dd67b4262c0ef76051ab1278e1a9719fe68fd5d4d32c66b96342308b60c15713a2e60bd7871ab287923c58ae42cbdc97251a89f328efac58d96797bf63ae087e79bc85a1fd755134877b436aefd65ac1fbbb83de3a439e7695dc935302df5947808fbf9afce68cdf5de8d18f5ad9959ffb23d2ab05c3160255ad928d9d46dbcf1e9a2070c479d42122f581ff750ea11a3fbec190f779535cd0c3cac68b68e9627b88cf8eff9c94db0d948c24eecc39ead8527fb7208de7869fa8ea94a718d9d0552a2040973f6a8e92fa0cd6d3df4b88f8487c98498f9f0b383239d66a151f2ff0d1db844d783d5d798aa7a98b6abcc758150fc675132f3eb9f20ef7a07d4bc46f805f5c3ed364815590ffc64169eef887b10cfefa7e0cf09f38c0c37369647159511a81722d69806b154877d16a56dbd1a52c535075ab3f5d525c8d5b2256c68b78729c751a2e2b93e7e3867c7f4c3cf14a51f8f47544d18c0b30f48ba366262c51f9959ab8c5c20ab8b75fa305f11bdf7920357ed2c602f162c4320141d625df2328b646abadcb18ae320f0ff9bfc3238f4620f880c06f2c840933e210599d1d261262db5475cd21d5570f213ed951ac1b19df2db3b6597ed2ce293f6ab8f99e7e0a47248f600bd75245a93cf312a960d6ceed905107e770af724e1529334e1614a430a768560fab4ca5c5db23ae32d07cf64f80ad35a6980cff2c6f6a0ec554f4c0f743acffe32a21b54654bda07ab8f224470903b493cc8203cd5f97e94ae05e270db3a55779efd01c1c690d9b0c464be9d552a2065d39e867ab5a951c6e8c148aada9ec9af097cbf520d644292cdac25e7ce1f80ecb90c1c8fca50aae4ec312d6b439e3727bf7423076b2b844cbc6a368ecfc3d0cb2acffac4fe649d59c9db9691f063652fd78a4628e33167656e8d78a381cfedb43cb2565233c52185e6f7262199f4fe0da2b7b5e18d0d4504105fecc0bce3fdfd7207819efe57c011304b7f023cf09bdf9590eee5513b30c1f19ee84c07300456cd1850421c39731c088f33b8f6ae048f96c8eed7dbb5cdd0cde4081fe8503ac0a7460f48d41054ff8b7422f4f889aa507927e37db17a2341f99fa4ba441912a2a1fbcbe8eea6019e92460fa086f4bd7422cf4aae303972fbb98a3d7e2ecaf1eb5d2d0e9be5112bcd136b25170b1e388c6dfc74d867449a1b1f1fe95539e638ad2346f8a99907d5fcabe054397527b1e3cc3d8f8fe0e", "", 1, -1252882616, 1537743641, "dcf90a7ed146ad8784d757ae491e313018ffe7e657be01718087128f13ffeb45"], + ["030000807082c4030382da53b5415706e735f90f28c793588a3f85c6741ef82bfcbf9094708853b51403000000036a006affffffffbe0d9bda919631b6e9348e4d3dd966cd42f11836d6edcc06d844b1a873b5e4f702000000004e1e931c5ddbe20a335d79789c2dc56080ccf732dd89f53be468d95ac5ba8e00be64577d02000000008fdbdd890340eb800100000000020063d91efe0400000000016303de2c02000000000365526577b1b57b000000000300000000000000001f0fce0100000000b2442f37d3f040ceaee093ab12fe5e4fb5491fb2505cbe6bc6cb964a2af60569e6edaa8805efd6ec9d27e3ce2ee27f82e65711eef130558ff8d8dd554c86274e3d4d49aa2ecb563de9e1ab7283f6ddec3b01d3a6bc0774fa21892eeace4d270b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004362130e3537174fb76dfd7a9c3f6b7794d62889523907f2b6debc35c5447ad6305aad76a2d0c84b9c719161639f746f0f340350c4429b8582e6d60ad2ac8998b208fbedb33c21932a34edb72482acf9271591d262c475de6c3b752810837d6cd99dd149d8320875179a5bd7dd451958a6697429bb14745c38e7ecd90076b9dc0207e4c3bf5d59c0fdba4394499e70d6d00561b6069520b9b565e68e1d0fcabd8d022824728f2ee82d895364cdf47228f31437ec740e71d0a876b6a54455a587b9430a01bf7e3fd802b23056e44bb814b1a7312810e4cd4c8c9b1df65d6957948ef7ddd2be36f7a5b4aa0cad8352e9acfdb0f4746ee0756bf9e777d84990bd35b5f25c020bf5c44ff346392e5b6414f8d4f97f7093e0c35a1c309f0eb32bd7f4520e2b720322d3a22aa593ef5f86e30f65334cdfbabd1dc17529854f07df2f4599fdd1a6860300284d09328fe6bdc49cc16589675a674821f980b0bd79200ea829e04cfa54590302851ccbc405adddf11419c5f9bf1831b16b5abcbb37032553b1499bca3561c0031b6ddfb7c567822c9ba72cdd7130c8a6062996a609a7cb2a358f5dc4fe4948d2e09eddd7e5e82bae5425539cba825b4f2bd1931b4aa788edc0571e036fcdfaf86687d907d55b7d090d937c689e4793a5fcda38c844f0756f7ed21b354846c6f451a3deb7639ee46d28f49e68ad8913d03077d0343dc61dd95ad1a1e6944902df63802746571916120fd5f1d0dfe772964449ab4fb7719943e1362c30ca961f3ef939cb8127457cc93b661dd5580e772554020c89c7adb9304b70a93df52f8abcbeff8b7b0fc0e07a585860d06b37228cd29c4e60e162c39dc3bf30cda9dcc7ea2ba1f36ac6aa1ebe3414c696e16591039bec3b12f31712d7f44d2be440cb9d244787ce41fc0897fca0f18a5d626355e9825f080c4dae686958e42b91c89f8623e465cecdc91a42fe9fefad2a9e74aebe2d00fe314abf0088cb9c48d726b103e4c09ab2bae0f91f666e53e2c6e4dbc29fb73fe3d7c87d79a40e4a6d899f993c13ce466201dea4713ef62543fe858bb20d13c952cc9ad4c3c66e86260cec64c83b0d6bdf6fc15cfaed419edf955a7a60236ca2040ed64cc24d46e2fc520bc9e81001de55bbdc25d742c9264be4e1985685bb56a3a67b0e890457390741ad19f4dbbdb6d8dab3e691270b190e7fa4e18f658c339d894a05bbdf37914cafebee0285441d08a3555b8459ab4b125730344d955f0298768b5d5a67dbd71ff11ac2e46c94c7c90e3fe7f3f681457b76f71e647c94dcdfaafca44a5e4d82d5ca528ca61935369e2673afe6296e1d997ab8d7605643171bd0ecde548f9b8719b0074fc639bb07cb65d3fe112351795f69a03fdb136cea27d8d57b8884d1924880d55876aa5779d1dc8b5b23d9cbbe5e5095c2cd490942533865eac066670cec9c4c9fb0dccb617c15a95956ef9cc0d317d44f3690e90c449b69050107cf9542b28d0eb9f7a882953c2313ed8bae1486380d7d1e765bf16e25932e24534d0f5e6bf4648b0e6f062afe898f9c00a18777260bd61de2b9546c364004d217995635ceb726532622d5663fe26e6ee3540c9a6fc85639e5b050970f2b759853d1c6fffc1c984dff05a7099d8ebe30ad629fc516aaa93a5deb0ea4670fe92cb20a9d1218691b901d81e75a09c6ab9ee9142e3f89cd32371538ab3498115c55de6f50723fb681a0068db321316d5d7886a26c9d53a765a7b6e65ef40b3ea4163799d0ab3b0f064d9c249875f02ffcb86b19800abe6fa289f2ebc9c79803e0a609dab7d250d05aa94bbc1865bdfc4482a58214557bdc85ce7db7569d15b02c0f227ec2d53d0100abef775b5130309a7a06a5d66d13f62ed167dd8ff0760d0aab00c5de7196198a4ecf75de8c69725ec837ca7a325923efafb47a24118ee4941b3d5ba3170bd77c58831f303debe3ca876552401e7ac8792bcdbed8574a985c827a2a75327a65a10e3f36592eea815ba3026cb24d9b93fa3ec0cd4f6151b4eb240b64b248e27a29f3acea3a232711b46327ec9cba97ac6beaf564c4616d7c26ed1c6973ec1984be6ea41abb717b611785854679f1eeb1006562c25de1275996b2476f75786b3c8a63659fa6d4d5a9006e65779d399e972358a091b0091ab501a321de2e4df959d63f0108c090b0c20b84255e1810e8a80cd19f90274bf4028f5c0082e29e4581d5feb100e4b8e0d3311b0fa3edfb44724dff95d8b0df9f12a16dffe9d939e3d31695343f9e034f711b1ddc3a5cb14fdd26a7cc81a6cff1eb0000000000000000000000000085203a4e421b0ed30bef8e0372053b39f077ed5603a5c625bf0fa0d8909a1f84c2337a6838205069faf7714dbbab5309400f0abbd0b40549dfc0596a24dd19435ecb54179dc30cc6cb3a5501a56701b0d7d19032e3dea6933d13610d31829a3d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b3bd10df2ae8921a3beb1739ea09af53a13894cfc404029c16399ccac5cafe5dea6df71c6c0b208e0acc9e3f171adac2bab02bb0a53d39a30fe0690639e329cb6394a135a0c2e38a272ee8313d56f3841390ebc965f28629459138f7d5c9db86bc70f1b01ffc85d8208ce8626cf2aae6e1a939989a3bb971677eb8589fac97160323f294380885a845b62bfc454398945b93b9d4ef0071d26c68fe9e176a40b3de032bc9b03ae132670f6b4597e44aa6a4f8f473c3cadd25adb6ea5f69188a5d5b6e0b028a46d643305663ab0ea24ff9bb06567ee2fb3fa93cec6f6fe5607c2561a2d4409334873b7c1c9552da923a568eed16929122a272ee6117d4a7fb7301e29d0f030cecb5732311a8a40156269320f9351474a819e716af779e37890d2171593e510215611f207d40f560630c725a35a93901438e40f0bff0527182a74bd277c52ab60215bf07107a771566dead0ffa708bd7b2d116d7a9f8da5cf455544e31c46d5492020526359b4b7b1254ccbe61cf2b92d1fd8e4880c2282e4dd5d34969f68cf3eb64032db7c4eed1ea31c1cce7d1d717b85122a3969d2e910fd47a371b5765dca455e6afe7471188c21825a79e7850cd550ba4736793ae90a20ec13b95e7e0cfc981ac738f94667fee5e64dc37477111e60e55c0ea9a271d113ac83a9068a2cafd78ff106ba443c5387ef096e80aa59df8848a200797a5fdeb11701cff2816be79df54a9dc50422b76a7ee9038c946139814a4b49ce8e32d92aff749b3f110114a086813d8993c00bcd3da430b14ac40348098e4c0e204099de43f3a72b52e80f0c099243259380f1224e9a21954d0e69c4402b7d1e1415a1dd195959c1e1658b691abacfde851e9b174eadf5a22359eeabd8a41dce5bf4b6aead865fead55b4033b417802d036e3376424b66aa4407a7e88938556128429e6a6c4f96ded7b395c772272b89c56feed89edafe9440ebb5898193265f41530c411760dd325326e535e5bd0aa5ddd7a463f704c45164ef44d5c4a95e4b54bd679193af32a290f13278aaac3152ca47f0c514272b27d14096f7436b8d1455778615224823fa84d08694273c21e89c73214708e492515c4d323be0af7a2ba54a1a9f0b752c17eeff0c593ad5b4578f6cfb0b377ff445d1d1215c078157935928d98a8389eed8acc7f4c8c8b596cbac5a3fa7fbf724009ed791e0d8488bab05d8e5f1789f7c898049c5ad5787d194b97131fe3a68df1633d802b1f7d6604dd1e18d4af4cc559356721090b0419eb453a2cdf77ac3f4ea0e3285005c3efb3263ebd4352751eb2e3c38e8b6e11c5a7beb542d993dc4ba421cf9fdb7c363e79bedba437dffefa39c4bbd4f6f231d8d099ed0edde33f967fe9afdbb1c5ed353cdedc6bcdb4d11e7ef0dd7e0a3d78aa8c5a0ddac04bfa57f525a4f71f53cdafcc6a392e91625555be49e5a4f9e9f1dafd618ead06922ab96293b6ffe395a775c44b0fc84ff0b822d24c676aa1a11183aa68a85e3e9d22b82210fcb74a4bebf5143be543de76ba3026dcf7a52c33206768905546f69ad25fc82404bcf3434d96c303d08f7a9f8a9ec1c46ff732e555d0e77271b2d81cfff37fc414a56bcfacbfd09a6e4ee096a9e01198a9c0642bf6cdaf8f92ea742c48dc265b0d2ff07bc343bae705785bfeb30bdc35d29a57cc6a22a0db5bbefd2c56f9d9cb913629efae20adba4a1e58df71bda80164745d66772a7fe1361d76e4e4389ec1f71cfe4e50d9986e2b507b2c3fc91dbf2b12f0d30bd2eb1a8dba80fca5b23e4f694d45a3c20374035e9e27c2afefe89718cb23ff2700421b68ac6e827cf4ad65d97d1cf19417e88dd40e9b9d7322f16e8703a0ef2be49f6503e3c32a7dec27eb381d41817a3b388718d6741ca6cbf0326cfa667c0f03afe7e69777d24428ef8c76325df1cb27500f0418226f5e4d5b9240513650073afece771c18308d24b664bdb897e0b660ca48469e74a01f0651d1ad0c6ba00bc1ff799e89e5967bbfbf75bbe9355eb2b42e7188cfe472ce5ff7cc042f4b71bc4027415c98bb9116919f7fe6ba9df8fd2839153504f9c4e7e12428eeb97a89149eddcee93c92e9561e7231389ed679ab3a50f8afe065c0238d0b9964b85dbd03dccf13cd91869a21c49139378055d47a81056eb83020a45ffa065c9310aab09de2d824676cd44728c9e7561bed3a5944ab4cb7fb4424850b8d223c20fb8957d97f5caeba04adbae3146f4231a07bf6169b407075c30cc419d7471ec4497d4196bbb6ca8407a67ee95bf6d6bb7ff3490f7af500000000000000002b1ffa0200000000f1af54d6ab0f552ddc5556b0e78a0736b63a568b131b46c1723607bdb2cea61661f6e4f7a3fc3f37702fec58bcc3a135ff9d7982cd151600e95bbb34d8743fc74e28e060986213daf9432ce42cccb3ab3b7c9befa8ee8abc6c37b58af0dd43fa000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001d276d6edccff9504c4ce5db5e2f80ff75520d5dc44895928b444058ec1df780931efce6768d9a9885929c736f1a66d5df41dd8944f8249054f91c38c92d6ef0ad082a9e4b90eb8aeb60258da1939a7d2ae90afdafe4fc0bae25fa4744d6d9af338648ff85acbc7e012fc63aa1760b7d33df4173dd386acb4c3f9f8f05f78f5d022f3a8a1dee71641558d4534753d45e1b3bce7850831c11afa2c633e0f00b8b59022c8d9e9b70d37587a55cb69fa90aec28eae72df3470199b7fd3d9d46888eff2d0a04698be5e94e2d20bbd8e34e228fb72803fb718ef2f5bd42f9a135598cf4b5445b7ada7c9b5003ee998e6753fc61f804eeeafcd28947e3d37dbfa386d0ecf77d030d5e5564308400d1fe7d9799b8b497281d5f966c48ee08adbda259f883f9687e030804d4e707d16830d6431478085897ea9be6a5f6fefa1c3e62ee737ee1fe88ed021c0ffa649b18ff81c1cc03b927ecf78f0349cade5f97ddce5cb1a51414528c1303254fad1d25259659210964876bc2a9547c6accd328c442a9be8e2053c212ab7f021d2e16620ce3d6dfa97fe660ed54dfd77d79cc47a49a581e56f5703a95f73b8d9f277ee5963bd10cb20a596cd66af93a4f8ec790a30e9d665d0d3dc5dff88fb9f16fc94f3696102959f5b0edacb4a5d806b2c6799822cd4378a7e751db7d263df9c4a8c3d3f67b25d01e695825e66943b5f81bf409c22ef5bca31e59bb06605c6c00c81fa844978aa14b50ed61e95a60ad96cb9f8cebbcef0cecb2f08fcd7134f22fa9e7120c56d849e5070e818923b680786ffd76e0af7e45f55a5b0b0398c337b7fed09d73ddc782b004f614c24438519fbf379cb8a4b622d2c598cf3360cf9f2017dd06e81ba733bd4584dab2f412e5240a991e7ea5b50384268d9ae7b34757995b7f34211d70f261f50a1115ca20bb7d917e0cdd6be3349880c8bd8ee73fa83f032dbaee54c702168c2258515164e48017cd2024cf5e5946b225f3cb745ab0c570a31069d456397e7cdd0ea232a674f2eba5075c69ed243fa3ec209b24941b8f485926897424475b7b17e0f4d806e5a9c263bb6fdbd876986235a7a92d6cef1e796767ea3201c153651e14b02fd765e94b370b054ac702c151f717d5de8f32c0f9b8d37c23688dda5a937a8926836a10f297837d9462315e24b4380907087939bdc5d4143ae88e2104d207280924b0993e4723f62709f4b84e1de679c9d710cc38830421fcf0ff44cc03f41fe685de9460d43ef8698915f445de74f9961f7d5f058d95061aa0772a7db397af87ec2c42e52afcb012c69b032391fa1b654a7b404072b4e6aa42f373fd072c09574aaab2c293e4fe662cf1f65b3b1ebae9befb9f33bba603c126c7586cf643ad435f4427f6adb1c9171d1cdd39a4ee03bfa72a2235db8958c72f41f801e5a85c278bfdae65e194d6b52b255f77b4335690bff375570e9550c7dc1728eaccfa428709a27c4b06e681c532c0313366fa74912e764b5b4e7a3b6a58826df0178209bf8fa262c3bc57bcb434316b19144c138039720b1ebfe1b909c8fdd46a87d26f94b9a8f8d8e3b2c7687b33442d075928eafdad2c5ac53b8d7cb24d4da43871b7d399da0de10fa1e8c0753d99f03e5f05ccc4a89ee0f397a9d2af9eabaf7d6377a5c1502048e2ef746a38f1e5f79b4cfe7ab7285816b2d4a7c7fe537f748877117876fdb1453afd765daf9e22da543b4c76277507ff4c13816859a87db77ecdba1e0a252d70d75fafcdfd2ddb4561be377d63323010513827d311ae7d91df81c6ce44204dca51178896dffa50d067dd78d30ee2b5a0a1886990efd4eb970d2a87f9dcab89ae49d105c35fd1f9a488dd533dad984e24480c5c439c83b4905f64c6205e46e871845aa7fa7a0b1f3b157adbdcc5cb97e3cdcc95801bc3f49f0a09fc8f911d6ddd83dfac11db7b8451b0ca6707b49fbae3c94485ba32a21d073054e9078bb40cfbf18d207b71a6b71dfbb3f70f269718666ad3d0fc63325850cbf3d5a43b3f7f87272f2a801b70a83f2e6f44ffbae9ec3a98bc6dd8e8d16328eba2e303b4648c408bbcd135ec13a2067b47b7bdf0e90f69e983e17570524e8d9924aa1ae6f72d47928e8496e6d66c148ac9c74a3e627de969984308e313b3b3c216dbba8c3ef2927d008d362089c56c996fd38ccba14d3981ad4fed98b4b9756480d4a68acf03d3c66e7f448fbe07daaac10f49e35f6f49e56c6d65d9dc27eabc79f70b02c274bb36ba70328ec1064a6d083c44483b470246dbfe9dfc1f6cb77d391c5ffdcc66f6f603d8cab8632e1c439ba1c0c7259fac3e09ecb21a7761a495c286b32f686e9bb6ee6c212a7357cd01727ae77e5d9e1c280db28d01020e63930707a241db151ee5d49fcfdca2e55fe12ecb360be397ab50b5123aa4867e0efe7f1ed983c104", "52", 2, 200400939, 0, "1be0242883e4053bd417d5ada455724414dbc4690b0f55d04c15a3f0f8e093ca"], + ["7cedf330044e90f2592ab42d78c72851849411f176445234fef4de7cfa4195e7e23ecda465020000000752635265ac636affffffff7e7e282a6b145cd6889b43c203a86f4ed536464139bb5ed7e399461bc9a61b0100000000086352526552525300f06cf15c843a55d5a5fab5d6021f639a343687a516cfd5f9c63c396311dea26dd640fc1303000000045300515398b9c5c578b5c9ee1a1b3e988975c1d7b2d0b35593b731c6bfca280e7b4409b799fecf7d010000000400536363c0278e8703c2cc9d0500000000086a6563acac6a516a4c056c040000000002635223eb8a040000000000a08a1a77027e241205000000000000000000000000aaf2b360ee7485651f2e1c53e5a54b28ae9136f382a0821c269a49d1ac87d32b700c1de214ce629e780375dc810afb927c6a0d5febbb348a52e215250937521f24f25f5f6fdbf427399712a73f31e481d61e869553893bed8af738e7a90d423200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dbae37e80e05e223a155a88758bad028b4c78c4af5707e68e967559305bfedb987b61ad914450aeaeebfdb6ec0d51d474cbe837906f2598b7e036270db3ad9162eb5a6a5faa17301aa38450da72c31fff94df09dac256b4d5979b23983d2b2b7427d13c1fc28c4c7f159ff92b1dded089feb5707890992361cec0d925860f9c1022e33176a95348b18ca7124cda87c96f45cc0af9bdf23eddb1f8ef540d67b61c2032df7f5bc64de54c03bb04cbac13a2a72fa06ce881446a949e923d44b213206400b0212f083ca7d190dfec599c117b8c14e88d6a09b4ce4aa2ebd2030941bf17a4d61ec3f23279d207942b2688ff5ff10f346a85f02e2a0ffa311f9ed96a67a7233032073657b0290c998dc9fb0c0a7ccdcda6c42bb1213592f23e54668bd4608a680032eea31e92f2c01c73166cee68a4cb99820f54560a955340a5afb0afa09809783030e894398d0aba25a45688b7f71c72c9019e712a17c5b7402a1d4af1802451eb0021410e91d7e1c109d3da39b26a2230f8be7ec1e2d4965851bbb2d6e809c96941e03020c98707d237d06ea2c6d955c86a86b64138a46a7f49729958860947513a5089cbb7defdc60870538bdfd0941d301325b49d0a187a5e1bf8a87068e44b403952a21338ed711027d0785623f4bfc19c32f496c48a8ceb45d5e5cd0bad90c269755f6eb1b9bca74c5d3b3c8b991de1c1c515926adebdd32a9693c726dc09d396c1fc01ee6a4365c41f218b4347335617cd10dff4537a060b015c20d7abe8401fe67b55a9a0e974acbcaf85d56c6ad99bfd96fda41ed5549fc3236ef0013e8b92102341bb45a6933b488ad5d02d7ad3d46ed1580e10c7c2876526a2ae23e535c79b6eda6ae04e62fe2d749bfd40151f8804577f3edb755ae547b11ec00368c3d58c347934b6fc3b36657d173e4b0baa92f38abc48f37dabdba42e34bc77e8fa0b8a42f67b8b8a4e5c8f170166c62ee02fe1404480c539013cba291f3a5c6b17bcd7bf275a1d7dcd0a17791c9f253e80d1bfc41e6de1486a5e01a7b8fd09f565f8d58b97e822e7cbd78746874a1a01abd5be629e7c7688f7e52636dcb7efaa05a912a75afa7740207e796148891ec69ed9c22ed25e151aac8697776ce648bdec700ee1e39811c309a1ddef0304604e8b51e961b606252b45a386e603ef10732a84491c7e52273060ddb3f1801c7ed3bf75a5dafea7444e1a1dc8378ff8560e631685be7a0f65bd84f68f9df1d70adb95595ac85a7c4e7aefa815cf87d0aa0ad646b53756ad6298d96b13b0828da3dd35a87e18c628000d67ddbe226daa52f1a6952eda6b580e4ec89da1b31718d7ec0b3d506ddfa881fd033824899a20376a8dc17e491388ff1dde8476582aa8cde8176aa403b1d9df12c0032e2abf4735bfb19bba7762c56173dd21167313d34ee3abf338472e737a77f5f78456dd95eb8f81405037564bf734fb9463eb1c0f24c0b50d829946326638d319f8ee53f6c6c4e3677adbebfb9f392a6ae6417ba479599984f983a0ddcbc5528bf9d0242dc95d4ec703f2ac6b44e2d9534b39082944b1190630d5e9d0e6267889315dc114a399882bc9cb0fd27ce26ad4e44caf1aa44e8880d9ea2e513910b51fbce9c8f02bd8ac57479163b20f5f08fe386b1845562f62b7e2dd48cbfd055675d68c8ca601e77e72b4119f5256227b0b339f7e61c4fd673ce7e0f39583b884a9d4254828f85348fef085b0c978efe450c29fc129c32f0faac8356c4eff8f687d58ef12f51dfe21d0094ea9e3dd59fa1607e9d12fb341f3b7be13ba9d54ee7a5b845d094cddeb0ce5c44b335beb52ef79478be0a5e19a83b85585859d017879c322503647d7c26a0b14caf0c5b8a34e17668b4f822181ac8158bdfe39bbf1bf68f5d6c175404c68afd297a4ac9f539cdaf3ffbc8457349354b969b216d57ee04bd7ca249f748b2e5399c3b10b1edec483119449115dabfaea8bd5e158d0fc4a9ea7c799ea7c3438a9d62e1d30b1126cec4aae2e0e21b72ae27122915fec4f7b1628558b5564b640cfbc28d3dbf6f5ce8492a57274db9693fb640b4b6414df15fbe65a6efd9a1ae17315eb0da268f7c8e63bf05e4e125c7dc2fa3c733a68211dd1ee0bbd537a0d324a6df7fd0ba9199aaaa2a263c804b2baa9bd981508f62b84cf0786edef6444bbc2c95eae1f04d8808a8eeace1713923272dcdb718302db29f5080a88db2d40a855a6a89471ba20c439a2f07bc371de9c638fb69c8b133badfcd6d2a9a4af472b0c864ba2220290ca732404e79b0c38779ccc28fdcff5d00000000000000000000000000421dbd54dffa1d831fe2d158a47b35ed72bbdb43b16c9e852733d6209de395c1ea76a226a091f17c3a999e17cb4233b64779858652d2bb5b0efa96a1138a1235feaa2c38a0be3db7562ff01faf4188cc3b276bb10b20e385b60dabc36ad5e3a30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014f9876b3f5faf0234adce086f10a8059d00def858ce2a194a032025594c412813b36376f853347ba0a24747c6904084acb7ec5500b5cfe9c004a2bf0b405ed1e064456351f8c577e2f38791b49de0e2862a9645917a58fbb9570b32337955756710d38418b33f04b7579c7c64828e9d20a86d235876ce151ca9261baa074c45020a2d18d3c375bc8490e28ce640bf6725d8c3662260b4478624c4dbe33133c08f03117da05f3a06aed234f6ecb17d106ae7b92f2d74cfa8e4d51ec4dbccfb6267a20b060488cdef4444975d75837d3e5276ab0c606396f14d5d5d2867728e657ae79e8c0a7fc7e9b958d513a04c17098c60a05fc5d4c6880ee42c41d4dc1b48e674c60212f39b3ac07f5245f3e9cab068c24e63441d47e7b46107fd671e227cccd45f8d0201968fdb81c657cad6cb4c75298e5f8c98587ef6d12dcc70cf1b349a11215002031dab641763866efa5cb2ad1a9e0ac0588e1b7612a6d49b799b25307499dc5fa903062becd98961094543fdf3f8816e63705dd52519e6d7db88b31a763c2aeb7c09021af36c09d02e8b1d199a2bd0a48c9610ef864b3601461bc69c98f5a4d004f6105b88cac75b8085bec1cb5e439242db7bf9b669231746f763a990d655fd01e674bb57b5654cedbf7e3002267006d10ca156120be11f0c6c54c4676cad08d39f7d7172c554ee395efeb5c8f2da251328c01018ddaa44e8b6da738b15b1720d66713120236612021f42f91bcfb275c155267c4d0db0e1b90417465db700584e8024b61f7c46e32dc02737e6d79a12335157c0139bd2652aa9b4a110a34214b3e04c0dbaa1fae22bdbb3d6b28a1362a4c7bbad79fb12e8e83c59f87442050c4aef9018615a7f4cbb07ebc7fb2aa8860504d03ee013cb47117241fdc6337a4e7d1336d4e6b511f04f2d4d9f5276c06919dc9d110427ad07b824e4922056dc0ea1d6a5954761039bdf0bb2d59ff31e8421f6f55a1ebb4e46489d213a769210f3b5f24649ed9c22ac73623c14369a29916603513702cd0e4d7c5dcc0bc4fe2abb317c0979fe4571211a6519c8a80faf2ff74b14c98428376d2d22e9f07a4367aaf58b18be8e0b62b7f231d80a4a660269d7c9227eaaa987463654dd81ec1bb10a8bff9832a70568e31a514ac3f7adadbccb5dd1365a1ca2fff1fa53ae9d95a8c4168260931762f9385fbb80dff12e3d9bf7ecb24590302b6b3ff7602473e7df0e912aa0111789d0231a86ec514b3f9f2e2d18b4b1ae084e6afd13985e3b6d26ee05f076c8b1b92e36ee27bd268069d24164e15da51fc867778e7323808d06598df39cc68ec6957e8233874697afd5ba7a6e41636756fa0bf76d1e54a4f9ec84f1386702af49192286fdebd6976ae434b87bced9b05d0b1f572eaca7ed8290c7817f8e2afa2cb2058cf4982492035295c576c129631b605e9d9dc192e332053ed7f454ae3c512026f229cb5ff7f6dc90e2774f34fe1ebe49e7e9e4f52d3227639344e9cb06ec29caa1d4b9a27ecaf5757d993660a0fbce7b66eb8a6980f33ff07026d5a7add684352c47854948197fcb77779d82f3c13e8670e0eb20cc208074c4130ebfd927ea462ec0b15b857c903df8832d4de1fc1d44b064352e5b29811a31f8b0213d9d446eab9a1a89ec735bd43a324a1b380b47ae4ff6f9d1d1e4268d1fe81d166c30535eda06f91652458906e4662082d5844f97159a05d91b36193ceb3cf4b6934980b955e6fce8954c9bd48d26da35fd3b822de8ad491edee3c3e127cb9b1c3f28330998e472eeeb0380d2c592fbc068051e835533ef95a93e39fb298275a42cd2d0858c6c9913be5909e832bfef683336c28df46e72e0df453dbf1ee6d9a0b9cd3781fbab6d45489f5667e267a443445816bfdd957359ccca0a936a7a92702108e87c01ca169f3562706af3a2e1a253a578b6cee2b6523afe51166ecd4c633233fba2d5b7deaa5cd5f98d1e8d7b8c1fb95b2a433f947ead63811b56b145734848dc07c5c990b67b6c5a24defeabea6ecde68c3e4a46db0e149f0afab950d292470bc31dc0612a17bf6db7c75013767bae84c8973d8802406cce11703cee25ce89c64abd1b465c4a8d88bb1e462b13e9fe2f3607d9a76d85e90b1c6f596bcab8a12becd94282747ed8820d8fe9d49352bbb525dfd61cd86300cf2ed582a57d96c9e3a1bdeb097e005edf84de3fc6c294fd9c3d1413bbbe9de4251e1e6378dec70961e4ba809ad8b385434f059e0f128b44aba6089b9b201896f6fd0205360965a66f84e6373b1fe480a0c0fe2acf9c02b39dd6d098d3944e33c618409c1bdddab86e3e98fbb9253d2ac0107b54be88a20b951b354babb8ff2f13d2d433b9b6a8c754a326176310e89bf51289bb14bb9ae4646bd2be0bc3ef54cd186b6a1ea0930709b41d3f17aceb8f9af9d3c312c6610b", "0051536a6a", 0, -212402746, 0, "7e31c9f2de205ee29bea902503e2294febf8221478886612d39f5c1f8db5a783"], + ["6044a8490331f974fe088adf77f3f4a2220a6cd856250b4745e0523f1d6473a8e0c9ac83a60000000008acac635163006a51e2ceca54f7d2221ac2f64ff383986ef8fd627d375d96c2a4ab29e0d35bb13fe92db23f0200000000085163516351525365ffffffff2549397ac50c741596ae4e2396b0d8869439808c9613de7503d452549e8e7aa80100000000ffffffff03a95dc603000000000100ab817600000000000865536a63520063538c6c7404000000000863655351510052520000000000", "5300530065", 2, 1244939269, 1537743641, "a40471670f6cee6d10a2f35223fbf5d4b709a8d25349fec77f8ff79d50a4fe34"], + ["ce90b24c047fcf1f8a3b04ed4d3a5a05782531faf3dc24abebe7ba5e5b99b5f1e7a5e8a3900000000005acac6a6500714cdcbb30b5b4520bffadc708b3cc315f76b782dfc1c231e99778a74745b3c32670ed260200000007006a6a53ac5352fffffffff2689311f00ac52ad0cefa9d84aca75a2e0fc8b9f9815d5b9126453a8a9822ae0200000003655263ffffffff6fae4144b16aa8d6d760582823fb6082d3c9defeb0874a94e4effc3ccf202aa5010000000752635363ac6a63ffffffff03acf504020000000000c9c69402000000000300ac51c57e0600000000000665ac63635351f6b0c1f6030000000000000000596edd01000000002def07fa812b39eafa1567aa43bff6730b32dd358a6acb349799c070a3e8eaf6421270be201ad8c8971177efd5d17e8286850758b8f7ce2e745be4afd1506f1e702357838e73331bd5db60df56798dc21f8606cc10c5821cae9dcc0ec0bca7950000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080c701da9a9e6d45b417a393d1488abd377f64c9b7ffbc68199775a71a91120bf93021ddafef458a1636640dbf14e400b7db5f1df18cc9da72fc4473d6d4004d89beb0fd04e2982c6c81faa2951fe2167d4de64e8033764240f1e56c474ce02786075530cf9d41433ab40c352b2abebdc99588a419fa46009a7edfcbb9db73c6030aeea781597816228bc6be948b38e0a49765a62714f6fbfed9bad9acb31c1b980324ef6b2c71227949aad85885d442e5631631b671e83735b9f8e4e50d5da0d6f10a0116f282f4aa7d0e1e6f2b0f1e42e93d78042213d92c88ba681cf2033f02e2b09b155d6a318db0a9f07979ca48ab283ae3ce9e44c1b8b1e3845bda1c184220510211eb0663567f059eb75c5122dbeefc627a1f393e672278cac0415b9ae0833a81020e8fcfc2b4b6736027deccaf3de652439df236b71aade7c209c02b41d06574f6022af8eb7158e738c757b10d76d1ddd2a1d304b3381e40b16bb48024f40bf97a330229fceabc38f4579ea22898f724d1461bbf3c5370f10a9ee8465e2e4677fa2c86031151b291fac9352ea1eb8b1af7ebd33905a6885d8c7d60a827e454deca91108c73e96be559534959736ab82af477e7e0d2a32e300384f40ae4b4a8c006f1cca1012c2d0bec79788bd7a1fa3a5faa073e7836c674d49b4aafa75a124c1ae2dab5d3e6dcfbf0a289f779f9a452bcc762ff427a8ded60823251a472e33041b59676097446f6fcc49ec84026981d68b748c68782caeb51bdd2a9da9567f64d3366ab42f2de1740a7ec48273625c544b56e75e165658aad71acf1acce2df943b0a9ecc366c99ac4102df480dae3ff9f95d67d87c3d1597e6e51e5084ebe1fbfc218e0f09e8c35d398892b8c5e7c0abdb94e2f2ed7a641b784d112705efbb30e32280775b5109f161457c1d77f13a3675dc6f3cfb6c3a39f2ee5a79e73e5b2991e636d697a10a5a16d093fac679e46f8351c1e1a616f612ad4f0d0c482d88b822260a711e5b9f1ebe98602bed8ff0e352d87ca71691271281b34cb6ec91f0de8e06417e8153ca6d5f721085674de8de553335b99515daea0aff19a7844aef1d7e2aea1f4142c38edaeccd6f3cfe5bd90fe5d7193c6f8f424a06f7869d883d419255f6ae2554ca06e0eb8635f2e95355b43550180216a7a53010b00a45745c3956ad4922ce46c5e36ec8c2e4aac2242f0bb5b9914d18deff7af5d077440d25f5e0ffb7367d79bed438b0978d07fa2944942c586e0df011f377632994a0b5fae0a5a47ff5e6283460d50f58eedcbc26c6ff28e28dc875adeb1164caa90023f649c9dfd397f40bdc8cf620f3f4cc064e7013dae498682819a597b0de10c54f8c51e3f35cfa6fa12429e2576a51128a2856e5f33cdb5e75add2dab2bae07d7f5dd0b3898c943ad0480103e467c42febec612469b0f77a458ef98eee312ea57d90cea140c548bbaa1c8b0b328cb2aa2307cbeac368d47e6cb7fcb97f46b651bd78f2204c0c0c9b20d324681f53933199ef359f16f2177ac2ee91091a81b3a2f2d65d73caa87f2a6b0bce0451eb90d214ebdbe03888cfcdc3d061fff7f12601adc882c92bb014ef23bbaba0185e83fc13537a8a08c0d03f284927990a73c9718e2dd33af85cc08f69ba5bdf866ea044aa7c1265013983d07059c45e6ce78e55cbf6d46484bb5cb46ae3be9c256068aab52508d904b676c5ba4759e847436fd15098496bc54b1dda86d3b0566d99b8aa7241bd4e47eaf79df82056e80f3160d96bbc2848dc16166500439492f36554164e6ae50a89ee0d0d04c94d0c5d73a362bb7ec5bb1ebc3d93c71a5948df33873b1eedbcb3b9907c2d11dcf8a973348517468e0ca6e5a0e52d0e5b3296134033be3ccf7f770674955b2bd4fde1a4ec7ae0f36919f510c1016ef75d5c15f5cc2b656114285de823c3b587496f7b891f535c87d31f3afc1a89a19dda451f397bec56151db7b2c06b57bb37d26f41bb97b32b2d17c7ea5053f50e93e3cd63931dafe69586e0f7d18a9ae5bf1d30fd303baaa76cbe2874c5ff155fc080fb11d36ca679ac626d9a6270c27cfa69ea3e4954b7fcaba6070a7df2ba4dbf32f1530a958cacb25ad21c5dc8d23b3e85e551bce749c24ef0a2b6d935bf30b7ab0fec77fb5d737b84961014f2b0bde7af5228fc4bca0f6c79415f7c92da8cfd1ce1c2e729d06fe1c5bf755caa69bf2208fc1aa2c195827c4487842e9b05aea1bbb4d6523399a8698366f68d899cd67e222bcca7d1af2d39acbfdac1c539e0287893fac1ff639e56afe55dc18b8a147873c080100000000000000000000000008289ef52791f655e350a0d6acb84b7545ed9122bb81e3eb3a9be3fee7f22874a96e9f2f58316ce2b0ecdee424291a947809d37550f7cb081f96c269d9e76bfae4612ae70d574959d9ad8538dec07a2a4dcf8696ceccca0c1f622660b8ce88d10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000049711acac4ecf648033ffbde64d5302d934af1b3f84b55a4d140df37409bd9c75a5ad87bc23bc33bcf50658cef6b258dc97dce534d1e34e46c1ede8268b879f9d0cdfa59559649b51e76625370ce1cdafaae9d91c90b4f7854abe4365a722abad38572db321a300feb753ebe2d82818d0613d998a21c151ea3395276142541480318723adf918cfc89d467e83d5c14f58e92fd8cd4930905e1a87d7d480b5b39290219f810958967c048d61e2839f2a4fbf31b5e70ffdc4605c85e624dd74e39721d0b0512bff5328b476d47cca72575bde59061af3d823eebb8f12e62e3971bc1398f98eb8087276a4484a2249a82923dbeba93d3a0006161651c3369f41768665a340221e6601b8d959b92b75e5303c9649a5a05b84b9e705949688fb18bcc5548530002298731b863b6c422313259732c79ca1f7d85d1cf2cbbf36933de01941d24b3fb031b308e5118eca33b15bbe43780b006d01c4eeea1e49822a8a925cc4316a8f52c02080cb7eb12cd028b152bd4c378e6a494a5b1386f8a5f7d842528547f8ea91e12022ae95f791abad178ed5664b90d3d8b7720de7f8c18323c9a1415aba4b1a540ccf2339721d9ee71b328899ddb623978688999feb826299c9250890db7ecc6795addefc95bb7fae186a50d2fbe8476b53730e1dd719c23804ad5a05a632b3ecb8d0c0985266b55f5758339e089335422f0754b6454d0857bc9863fff26f25ae34ebb2a79415c943b81367dfdd0aab5ac17e2f0d82e2b69e72638604b6525b3ae4befa79a1fb03b6b43a9db93b8be3d801444dd68140eb6c5d60ec2af0f568f4e47b102ed7e7af1e9f5437cb29afcca499571ec5e53bac2dc333a0029129d707542ad1131642c049ee62b3cbcc0ed49653a813cf4206c5410532ba636aaaa87c43746ed5390e1f3af1f114d03d19f2d46d59834e65a9633f24936d2570d7ae2691b7ad189763a318bf3d799bc3bac4b3e3ec65f64733e522e671914e0c3c83b4ac17d51d6ff413943b8163c73bbac0f691387c17a1e12fcd7581293a5c30ad4f939caee7bebaabb71fa5eacb94aee7d96917f6e43e163e37b2c7bcf5a70805649c30171753d315f45f4f6462c0181469bdbcf0ed3291921c0b6344fd97b46a95023bf6591cc19780ddacb295bc73e658347007336d777367708245127e6aeee4abfd94effd697c6a0113d27e84bdf3d68394061676f5839e335d0a43b1975001f25f8565ef6d6d41e3d9b1259a465e9979e5e3bb21c93e6e205eb37f84ce56f9d0052c0fa00746499b818fa8e20ea59b9afacb13382288134109485bb4cd8306d3871c1930e507150b173f5c32bef666ce67e5955d0ee4d11b61ceb0b2676091aab142d0422a57d47848d3466a6a76d28e3d3e5ec98b104f8579bd84144d2632f5b70dedf943c45bd018737a79b732dcd186318a0b1fc3ac8f10074638a31a7dd3a243abc8d33304a0f318d582c2a06a78ebb3d811ed23e7b42e5e6ae4309ff63ebc35f5a085d9f021e6dbf0ab159ab35a990aaa88dd744bb55a74e13980a570db03026f6635164f417896e530de2bbde698e8bd589d3c7add57576cda6068b76cc93d1aceb84a3d73217af89fd11918f494435d946ae5a44be47ad654a5f6d1ae59e9dc98dac150d0fd7badc18a86e3e14e219662e586cb279bdc73740f87238ee7f6786eb7fe81a014a6dc5ee2eebb91f40560fada63bb80d573901afe997264a01fbb9148a350695cd2d0603e35a7f91970595a6a5afc230a5523dc20e0dfb662d50d5d3587937db0d2ea48767bf3a1ee7fa1ce5b9b1edef32b96255c415881d2e31f645afd0e2eac8827d7736897fef34b0c0bc2c3586d3ff41a949345dec1292af989114ac502363e0086891e12ac8dafb46415517828531d336c3ebdd249b0422d6dd6b4c0d27ed6acedeaf28cb5d978b68461fce51c70528e3fa300f136cb3f9622fd55645e2bf1930a2e95eafd37281df5bf0342889823096c0c13b4e28e74ee0d37523a787a14585edf151415e0714d34d4f4f06222e72f9ab24eb34538b421d9b0ac35f5505a92394ab2d5f4aac8192a20c552966c7e7ce88793330aabb76d0b0dbe2d0dc55511d5a2ba08ca5eb3f24babd7acb4223b38a9eb70b469c738425a94d7ceda099c39d778bc6a5f25848e22e35cae4610399e72b6d465c2077ce598abc33726411138da05ba3d3e0c52a3ac8f71d57f2b75408cd748b2d0cec5043760824d9fa51c23462da79d8bb7052ee794c62a2d06bd53bd90942a685ff5a9a18051ed8809ae08424ef91f5f15583000000000000000006ff990000000000f827656da3267fabe532614a4a45a7de63d17da0551a00abe62b8213edf6457b90250560d3fc76c0f90ade5b0da5e3e826a6661e2276a05beab6849fd81e9b06d960c6d50e8c1f3164010ac8c799308ee1eddf3adfa1e1c7ba5132b4038d072f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2506d15e012977c0053b776882f2795cd0a38423ddaaa8e66d81dea52713f257dec4282742bc02f2304257713030367da1f2dec012d78561e59635a6e468d4bd9bedc83c730a08aca128b1861da8a993b4bdec40d6eedb81909b34373df50c94194166f37b14f10510bb98719442035e495ea6a201c49923427186375f1b579022c45ffb769d58fae8bc02bef168bca2e851c42d3a0a6262a5ccd51bce8fc13d503153ecb53654c1104bc25abab970dcb198e3aae1d6f47a0c34d976811ed06808b0a05ce4fb74a6f8adf0947020834850fe9bcaceae64c52699e400baf77fdda864f684c914d1c5705dcd9a851d3191b7e32c6d3707922b798ddeda22bb0e657fd1902192b82f4e74c2e212d94084df07a04fd8491556034fe0a747c5df027eccef3b5022cf960e897dcb0b81bd3e45e9cfff160350d778bda49bb6dd1eb36dfef62e41c020826343dda60b2f5a37cbea6564566072fdd925ec2c94402a04e1f6efe9547b3030aa4998a6bb800d998fb8d2849b1d440fb535ac6512c0b034c612a7676c30fb302179694a2f129b06d91a0781656e082fe21f12b840aca792852c91d2201fac86d935a4dc31dfe14181341988086b5a2255e53f7b4e32018c28e3c5ae3e8b0d26e6762f9d3fed4b2a6266f5217284243c7a1bd7db37c8681532644f122d758e14d6dee896c8971cd5bce60c6caebbad8368f9d5cf509ac9249041eeaa3fa3bf3d46bd93e8a4f4e0bbc1d0bf06bbf86313a3300943962d847ce27dbb0a32b05bb597c99e185f84f4b8f3b2f6026fd321d1e01ab77811371c7967a962185f80238dffc2c99333779d8876ee083d245d8244a2ba9fdd94fe13b9fedc00740e04cc36553785889e16e9f7c54f6ce66cfa11bdaa20e3ccc63a8ff32cdb5c3a0c6cab5ad58d2dc846121a64eea83756616f53d550f43c9bd27a1287bb1b56937a7ffed53a1635535f2a6571a40c5340f3efb5b9a3130e51fe1ce416c7bacde1f36dde595d18b0af2beeed215fe3c6ac01dcd44475c2dfdb0a2737fc8a40e96797c0917d2baa98f581f53e1c49fc4d020fd93d819b4ebe1c2bafe22976cca67795560eef87fb3ba3cb0b1df930d7430f06a187f6c5aeba0dee5bfcb9ab66a80656c7ca6cc00c7e77825996ccb8261a548222ee54bf96f170387824fab3a1ec1a7424e7def0dd2a59c5ba64ad378ca24b9cca892239bd04b077ba8fc952bd4805e1b4624a83382390da5d0fde210416e7b4a11a40ff808d52d2b6eb921ae8c6ae67148476d60d78d85888af27066a7275f44e103c11537a19cab95b8eea93327243e34724039d2b0a4f70867e343d40d1357122e9a89a5a9e76fd290cb747566c1374dbcf09bb8550ec6003375af71ef074faf0a632317718f54bb31237120448597dc6b46789fba11ad4004080db812e4bd5790c437db3d34f134ed48f2e3b4f5c4b27292a2050bec1895689035bc794123d4c8ccfb04592dde291edce704f01d1678736d5782e7c8bc3853d1bb05fe258159ccf723621db85b2a67bd7481cde1cf676d4d42d2ecaa7ea98e55de4a526b91be482954ea778d819bf57a9cd92f5781a71b5c07d2ee8be5b790e5a944b034d1b85a1838e9dc5f1b247859dcd0b1af62a8a9954b7317c8e5ab87b6d8620e84718a47f4a965b9efe7011c959535ae27889e7daac453cc1bb503c8fc8b2e8c2308fa8c3144e03259986224d99de92e42419088202e80f434c8661121df53a99b506aa43e5c7f68defe0422d7387f9dacca12892b6a529c728aa412955e321f587a4c5d062e9fe7ce3e6fac71c1f1844cd28af602993df8f308e67b89818195ceab21c3e5eb74d0da6c6b9818b8caa43bfe29a5a5b8a4d9c5f624bcf574f805cf73735bcdf3f328f4254ac094bc0b0038ae79380590069d9c282ad9132968c3f4e6da4c8f790f8161549bd63661c180bfed5cb2a8aec83fda82a7f576094269063e50503927d79d66e42a4727b1b4727fb2f29cd3717413459468ec72ceb03ea553e7a1f7046d11fb5bd444cb2b735ee2edb1ada27b6a88dbafe8a753ee028988cbc2a4e9863ffe513f647a80a9f812347c09e9d4dd1540789d6e9dae4c0c0f3475dbcf98c122ccc64ea35530c95f63a99b21d7996c57043cf6eae705815bce1a7905a2737af28482c27154a756464772d828a3a03cf43255066f9760351a9b30a832b4141a72699a81630f73f48f8117069ee52bf4f70c0c20e2cd8b43357392b583dc3767d795265236133799b39691edcb0a48f4affe5102e7129b2f8543c513c76342a738414ab2f2448f1b3e7c7ef5264be21fc54e62c6e297e7a06cb41b874d09ddd97277727a4ecfdd3c6d0f5dc7b502bdf7c9db4ffa822af526efb20130e4f18fe290e836bcc17eea3bd2ec29cca49208370b60501e21781b7fafc71b04d1d1f76a04", "", 1, 1624702481, 0, "5861e491aec1b4512179065fc3d3cf8b99c1b6eb7c484c7fc5dd64f3f9bdc04c"], + ["030000807082c40304356c5d74b905591353cb703171fa81e91f9161ff561f2c3f0f0bb5a9eb7637f801000000056a53006a51286ad401dcedefa57b9cf4caf6118fb1c15c3a8a2f275d00108b36eb35e2567c5e39803601000000056553536351ffffffffb76751feb6bd5926f0465adc362e1fe83023a5a3dce2fda036adb22a696024b803000000007cfdbca6f0b0ff5bdb96344b2561e5bd23f2b94c7d629aa5544272ee17352f20da3cd2310200000000ffffffff0419c2af010000000009ac636a5152acac526a88586305000000000452acac5274713b05000000000552ac515151eec3770000000000026a6a06620a06de937d84010000000000000000e6ce140200000000912650de0cf4f231902c03c825a8610bb8b964e37acdcd753951d1aeac10e09fb87384d16a9712c477db279f6500b6d4c0547d495092a1002e42f9e4e8eb809b6a3c70019061fbeb9784767b03486a14e7b583a8512e800af8f3b7c80a22d37b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012c622b321aa1e443100364f4cad85990eac0b7329cbbd7bfba632df7fabb90b8368c60a2945ff8f67f538b3d87476e941fab2344279da88d84884e1836a7be45f5265b6c0a5fb92d3c502b0c968100cf5f581ca16c53121ce84ce6957bce9f38cb168709946b3c60a77da990e47dca557308e5dd73c843f92f3ed765cc215f7032e9088f03032f277aea1fa0974a5caddbe57af9474ded65cfcc730cd1cbac043022992865d216cc142f1024a648bc15bd2f91a51735c9799341fe27fbadb7bb7ae0a04ca22a0ff997be49c982ec380733dbcde186d873d6d1ed8c19f101b232a541573133fc0008a4d19e08a71541eaae0052d423fb2f8fffdc427e63cfd1efd4503032ce70ad4668ed2d6c0ee3031273a56416c707a301b524bef324e14fc4511d25c030ccc852e8cbe3a5cc7f71610a995615e6f8e3e4f1fb68561386ce12e4d0e56230320d98f0c6bcaba1b7269e3166195877723e61eb840b376057c3ad63ebeea4bba0313dffe89bf30bd2a0797eb42e5f190cde80e66edb6d5e3b776f172aee4c50b34023034c48369a0e3e464d667f8e740232489b3198096870b7c0730dc8e71e394246360fe9035c2a0e353d433d350dfe0bb63736a893310d8c7fea83e3a14b10cf6ea8768a4589dbb4d1ce7d772206243ff2e3eb0ad9adf4d9262f61be896f3124769a79414518f7bc6d4dbe2d17aa7f8f3acd7c079c49925b3751a86bea9f12a618bd42f236ba046b4612cdc18e550c4a0d3b3abfc08e5669e60c15bdec521850c3f35644d2e4e663213985157447a013eeaba486387e54c8bf967036246295a7214d275ed06ade9b236d40976b10f80a907aea2f2ec1e3d6bcefd9bfc9a5820a31d6e30d98964a4fdbd01ac8d2db46b10f770f267ee58726da5a58fa77892886b910e64df39a743b467a3acd7273d0dc1f4af26843cb390013de5023637dd0d2d1e97eaa4713e5e5dd6bb3b12be50a8b3558e4d7be8f7663b0156680345fa9476792179b112626bb3d3808cc0809564cf9378630d33f0a2dff73bac797f0d244b379524dacdc944f30ad0205e664f11a28acea9e6967b6b87d8f6c8a966dbbc22be7aaf22f9586d9a4379a442e6748f72edd5d3ad28ad1ebae4fe39b16e8c99e726d9997454c424d4fbfbecffb40e31c77b991bc4aa0176261ba73c1ca55b184279a925b99032adb0c9329bf3ed39ae4e81628d1f290acf2d64e38b3ad4cd976d5e511123e7c66642bf5e74ced761c72acc6cdd3df02c2be687711acd98dba810418563ba29e1bb69998083e3ca1488a4ddbef43350f81680b553f3a8fc55660d5dd518cfba227569bc53d6e032222cb9fd39d50bc8b192d7852e731c22b194c9d794d3363316013490996a5b98fedbda72b3674c987ea7b87123e7b45688ee94ddafa756072b70509679f4fe4f3b0ce0a549495c9e58544917d780646440f53997232ac03c235e3959ff555887d2f32231abf6bd5dfc415f40329cffb5a1b83299d06e283b9c438a0029b5c673182b6aedf0ffbfbad0793748c78e2bffff91f92aba6ca436d7e93427aced0869b56d413ffa316fe54427710cbf9638d79a938421321114aebedc18cf8c581253289578f8813c0cbe3487313c380ec375fd64f0c0eb56950ded032439448b6c5607bc8a47962a393a35bda63f429bafbae5c1ff776946e38ce4695ba678efbadc55f3189460a8b57635a844c79b50e2cd86a8dcc982f73dea14af9ff5aa0519aac2ed99affc3d2c3ed13d6d2883db2f917392958c8ce59ad3cad6be6d20bc396e1d3cf9a4fc9671d5f61ed887b2b457913d16a7c715900ad629a27249a1924b4a9a725dc80dc08b5293b0ba64219c870c97257c64306ab81397d9b90c4f333ad7c0f8f19d28db4802025e5eb084026520e7d8332acd0b4cadcdcf4be38e2b09787bf657989f7e28fdc1312d7896c8a032497a748ef4cc346c0cbcd4a69eeebab424d7e27dd114733a87cfc000aa9429b12fd5af9b0ce2541ffb3ea0d4df985066aa6831c4d0302ddf25a53c901a3090958226017046d6df5c437d483459a5e38d8e829fc2fca205faf3b246a2b0757b8ee7fd27b9edb5d00a7c9129c94cd680c2a38ba8b1f3b7b154dcc14ddf7f697a98e5dfeb37bfb42b7c2a2fefe5a3b4193f9aa577392f13320a6cfe5e796a557835ed06e6267da83ce01311c0197cbbb4e087103089e15a46d6e7fa16faa3d177c74612bc96e677a4d029fe22caa8d8eec79d05afeaf96a20f421a2b0cb92e95262b22ef78bb8804877f3f7d3813caa5802e64b0bd052676950a2d94977abe5e6fa0b10436095e686986e2fa278b0e92325caf55e1be1e10ece70a9dc91d177b625e631c6753e103adf8eb359bb73fcafe7ea53744b6d1b9fc84e3eed4e7e69893f07700d570bf2e576da0c08c16c66c1df13aff6e90a", "acac536a", 1, 912114334, 0, "03744471b15b07d78034c74d77b633a7c49e06756442d293d7370d3d9f5e7f30"], + ["030000807082c40301e41e3e68a89b0c92da14e3590e390e6669a46817f937b2e3478b41892e7d7c910100000003636a63ffffffff020a38fd04000000000763ac5365655263692b5200000000000463526300c9cdc191f5d6d96802bceb5803000000000000000000000000214b0d42945fe51ccd14245082ec73732a852dd82b605c125917fabc1e729270701785c40532fec15b247227f08f882750b381df2fbae0dc9b0ecbfb8f1f140a1304d51a788cababc7bc9d41d84460af454c05fb3eab2fc3224c69319210461800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b78b050cf346f6cad39080f1601bac9fab00fad0b6af6f059841bfed85fa94bebf4bbaae5d40e8dc16979451cdfe951ec63d2ffb0597143cb508603658713c006ea522e53bb79ed3b0112e8591ccbf809fc11d5135e24f3082f17adaa555c8117df080c759b2e77284396998890079dc5e2a8f1d92b12be5fdb7408f6b8024fb031ee998c3b9556590dc50320f176455fa713c2d6b563fd27cbb96e5fb29c0541c02267e0a574bed5df343a648df899d6221f8d05e0b2052654de0a75b36d0bcc1720b0519e2224a2954d1278c5686c420ab9d4270eaf2bd46a13acef9d2f049f7acebb05a8392e635787a2a2b8d26fff5f3be2eb44d82727e2e99326a7a4f4108fe120213c262fc11bb1e5a2d1e8eabe6bb54e0b9adb34944b93430f9e24d49857383170205517de066b26c2a82b70392cb025b26a1b63fc676182b0bfbe1b7379993e965032aec31ed7b230a5759bfe43b01683410dd9a0ab43cad6b5df04a74ded17db0ad030ca4100ccac26288b016329dc79d7c7fbe4ca9a2ae2063244eace93e0fe22e2302133ef3c27fff269ce28ec2c86738ed0ca836c43388b1d8d73ae4bd60228d66b9f5340e11814d7db13eea4c07749e1e1f263f32f61141ac2f6226a61d08b12701047bb17f5b2528f6c7e10db717ffed26e6eae4d76ab30d274dacb1ca4a8865a6d49d18780fcaace801cd57e7b48ea40858977de179cb85be71fa4e97799ef3638cbcbfe5899a7574e895a54a557cce738981a788fa4bffb2158577230dbb6d92ab97d930e5a25b04761ecc501c6f21ccd69d784b91a425ab4c963999af9bc516b1537ffca9abdf53f33f1cef53da1940a35164588516827e0444d6972cb62e488c1ed1ed2cc026709ad2824b6346cbb314c8a3bedbce3c2613603d372d8e70fcc8459bd78f460e59427085f74b537f0c2dfde1b690d786efeb15e46e77905acbf907f86b022b57f4e71a78e1e16b56ccb3ad2f6155be946b45483eaade18f03a6b30be06eb58436a820259c18188f885e0915922f53bc52c8ec104ad5f34bd1aa0926ecb03cb1f1bc5bcfbfcb7aa1f72e154bebef5189d28554e3c7a516ee498fd87b79fb8eee56049687a87ce73c894791f5d2411b4ba24831cf373358b74c28db1b25744df8e4dbfd2c6d962e0f3e7998c81824b500353b05fded9273873af7258a2f7409b68129b287429a885ee2d11ebb76e5794a18821c4717c45044a4cdbc3e1681c42be165676f5aa6f02b64d2e163701e50997556a42d1ddf962df34b52719ec8da3e3a2654fbd5e4793bb93e0b9223748572ac0ca2c9200005bb546447b51dc0b9a7fdcd62bbd4bd1899829e1972f41837de6a6ff0a850e4ba0890c4f52e7c16b157f9624eeb421f0fb14cf39969cdb347e48e05a496bec9a85837b78b6e0237fbca6a0ce2fc3597417b50caa1d74d31a4fb2f44d5ba00013cfcfef4597f6336903acd994477af0f7440aee5cfd24320b6e501808e8feba2bc5d35faf7ee26d6ee23aa9a8b3d3618281d31d925b7cb8a150b05aa8971ee2953f9016e02e10ee53cd32126b0127b285556d29da8e5c864f711b8af60565cc2021fab6bd4f180909548be7ce5e10deff92a22c85732b4e73afd7a21585477310d6b0e9834a39b5e2ff706fffc97c77845e4d547388eccd6856e953f3e3be2736abfbd1e0ba9bb78de2f413ebd8e4d1f4ea05241044be8e79299dd0df7b08dd2b280d539b8d0d7239145ddb43f8ec0caeac523e81de1cb9ba52f9a5a08939019f2633c98ef1f0472bf3d83ce90e74e8f6ece991a0fc15a4a4e3b344cb2ade300434aab1ace49058bfd2a5ed68e53c3b3e898c94641450545eb98c0bed4c08a46af4ba5962e2c25fc7e7652b4b4b79dc4346140dbfef288098f4b6acb738fa60b630e979fd0561380ad7968345994c499a10cba4ab6a84fd1971decb7593c9b4aa94df9b1189e47a9a00b3d34d2dab7d9bb61e2080302b71597f576d2af02a26ffaaa886e48ddcde38729d9fe4c61e789c57fd407669dd487f21d80e6d33be2c810fc274fde0f321a0854f20dbe68e82a700ee2e42cae1f26eb763b17dfcc31b417ccc39880a397b91e5170c58639e282dfd374c88cdcddd2f21fff885314b622c088003937e2c50b4001d166bfc1118ab58dbefeebdc584fdc404421435aaa509f6c17d35065593e8339739d2e23c6d2c6f8150e39ba108e723b48ab3bd343d75d2c6876e56d9a0826de4037f6fff0a1ef316a45cf6531f3f24aae35bd6e478820f61a3b5c970d8065e4423a841b15676da7f89407384d4a603000000000000000000000000f3eed2ac5393a6b86756c2ab4da226a45dd78158647ffb994ebc5802eef23e7de50288ffd08c8837ce0efd17f425171e3fcb5f14e75fd7935e7c15a58a5c9dbb5ce44fc0e3ec5b5a29f9920ff9462d1338d6bb3cc04de9cf2bf79deee94818f30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002f5293aeca98c2df7f26a65db7be15158ebae8e25ee2ff6b87fc242d81f2a647cac19337e5d168cf17a6da1e2c6462507046014f325f68f0d5562ae6f76ee732a4b0e138459bae7daeeafd4d55e26d9eb2dd8e0476142f753d22fa8ac342b129e562b1bb86dcc18324611d0976f80642434844d5b7c9aa3cd670354feb07ead02185593c28002b3d22ded2c1003ca8cad7b22f09754d0ece52e8f7b0fc2607a9a031cc9739426ba6b2d98363f73c8a4e64775609f106f41270c2844500fcf8626b90a02aba10f35cf74fdb888cf11d50da1e23f21d67977470dbdad021de962d2e34eabbcc6a20a7d879c814768148ec7f2dbf137cd917963ea8e1e04b558929d03a7020f40b690b9d666b1336453d5bb8cddd02dc9751bdd0f3c9baf666f7f395fe0ff0216475c6d9734c0884b6472230ceafece5cb6f64e9fa637c3d35bf7a9f344a2d103242a19c1d8547bfe2331793ae6cd6b60b7e0644dac999c2141ca43c5938cc43c021d4140f188e26a2f57590c018c9508ecfba9bf8a6dd1820d2e2181e33cf8d54d020e8927a085d74f5aeaf8dc7c3add4070559cbbcc27b08e8343f564f3206a8406b2740d85f00f493b3e6942a42e7fdbd83c08a827cc9830d6a9d4977744440dcaea2fac725f925c40191627570b75eec3cce5174af1d9c51a9def0d66c5964a15ca1e9c91522dcc7f0f81bc41c34e365ce4a0845ff2bd557a2607d72d4a0fa073cea669c821044a9d4b8c6defc14d8c6738e5ef00959a9e90aee887c334377aa30ac1c3c97451002da92e46b5b91d090732af6b1d9febf74cc421752118475c336b2cb57ad09d8884c87f76cbb610425524b008a0ace1449b49478c38666fc2debb1f3a4624b0338c7464a331fe9609fe6710c1d4150f3fdfc2d0cf1cbb2339b00a5df22e0a95eec3df0d51e471154540c7e5d9f7c247829a51525b28b8e679e073893693be5c41ed2625a0814570d4ea2e582daeb29d5ac215e00f45f8ebb3089bddfc4e3fe1eead1f1215f02ee1c1491c1bc53a7426f9dda0397fa077a3d4c34eb5bbfdd95410312b246498893d9f852d158444223bd906693fe7f1722d3ce8cd49799586ca9b0f2faa0731f104f2c5d3c7e631a7b3e80d46877b488f0c045d250d458d096d2e99a3450f07fbfbd534ea649762b5ab0d2dddd65c4bd19169ea50437b96568832e07a8c5d1000cd0da46d86f304b67b64c1bf11851bfaa0e13a92b8fbc55304ec3e2ca0ee6d21621cb47e48ad5032d2a2082b08a4e51f3e161f902a6d88ce23fca955db8251f2fc6ed7a22b070b61cfb7a5cbf615bcc8ae080b63c3c5ae8f3cf6051aba2405e07456e200a0eb53c7b1af498046609fc4fb1f7c331b661d8e51f22fedf8bd8b357fa0693e22ea6df71feb55ceabff1cad9d1e0974de4ef1cae03f3ecc59ccc875a98fcbd7e31f8c01e8e759b44009d62b9f77d8e3746961dbb70e735e3a472747171888085e80aa1ed66fce828c80983a10aa47ebf5b77f609de73cdf0ea8ff3b220c5f5557b72b7c009cc805438dafae11e22dc127cb764acd90c9579491564f438532309452c1831a4bb8175419ad4f94433480cc9c257f5a6fb94634669d667163aed6bc8b536d28895f972998e02e23d2107f28e72cda9f1a6bfc5af94738ad9177e36fd953d9b069fc507004de3c8574ada8aee91498974eec37a17db952c4ee94574590551c3edd048b80fcb4fe8f699ef3c40d64347a1923c80f332d2d0522bbac78ec26e157324816f5109fe8dc332ff0cc23f0b518fe400516c1e7233eb8fd7339227547780aa40918cb6886583df3a098fd0948d50a298ac3c424bb28d9400a2ee1662fb6482650c063fb730dc222bc343cdc9a1f348fe871b54aecb482235e84eeac0d47b3143f8d963b63737dd1baf2e696cb53e62418f45575d184193d3eaabc48470cda710c19f2ede990099bc654c88c0203b4a54cfd643ba1911dc87b80e8c2072f8bd76b305a66486cacf0d3002708a502e1fbef9e6cdf4d543164a2a749ef5ece0eb8442f419bb7cc22181d54cbd4728ac8e675320bc1b8fc509edc732d9d5afb39a225189100de53fa3b51c24278d2835f55c851e820a87618937d1bea19c6557bb69e1eda5c339233beb966d775aa21a0afda34b58224870925009c1944586cf524965e98a69dfa5e39683e13e8c34381d5768cf73aef675a2db4ce4f6e6e0ec0ae5f45ed8536f756aa379545165cdd95269eadcb1a3e7c9b2754e25ac2b73ef595faf4467335a63f143686ee00362fb686520f1c9325ff5c72345ed5dc7bfc059fd59923c2f9ea507ef831c398789e7a8a9c74e15f52bbe2d40ca3bdef1d4cc8623fd67d794805017dc3bfea7d7927e0531fcadaf8b697d363ab01317de10014060a58f17ec1f8a3563e421670809bd54e3de4da2cc49592cb6602", "516a", 0, -114642225, 0, "14c75adf28d2762f4daa704685112081097aece94de6fd5ceacd6d7bbe8afd72"], + ["030000807082c40303c015f0ddd4a35e8aa135e081083a65254ce63ba08c08e6148a4e89c0c37b1d0900000000065200006a65acffffffffb55de27768dc9224fd06a69f1494d24ef94b82201f2da2a27897f62b5dbe31d6020000000153ffffffffa95b57dc439d67f9694fb45bff31b889a39448c2abec654768604c820fff124601000000076a6565535353ac16ebdb1002107fe70500000000076552006552006aebd54e03000000000200631a77145df035a99e01000000000000000029b88b01000000009ec9c2d9632b2939badcb0dc88963f11ada5fce6fae894fae3750ce5d073db179296933473be7e423fed5d59c62d1280dc58536bc411f2a4451366c36b421bcd9b73219b37d5ea66f799215415f1605229152be37d99d9c2c6c6a400efac546d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000551864f696f37b6481ac5c9c6d8ba719b8bca87e08d7c9eaeacb07cd5bfcb9f12070129afaf727a0d9d0de7938764733d4f183dd035c82bf5689ae6e22bb210cdb535e1b35960b330a56117c7f8a2fe9a49d3c86052a8125b74eacf26a69626dc731415a0861f3b9705586c11ea163ce7a687b57e626cd4100a9969c8c34533603132557dc86017eeb1b2357f4f702e1c97eee7797ce3262b29b1c08df96ee6c610316a8516fe50f3170d9ff02a045c04266d2516fdebe4cab176a58361d3aa2146a0a070263d46a2edccc4f86a45ce6dfd693ecbe82f363ce8185b46f465833bf5d0012f064cfe8ae34ab4440423ac455763b37237a417325d269197c6ed56027ba0602033db322300e5a9e3b1786b7629198692053734ce6e440d4e580828ed8eba6fc032d14b6377307e1a81fd0cb280c90bdfacbfb1169ff0d14c7474fb39dfcbda87f0302b15a162d9f1e693273d41d0eb215264114741ef0b57b56284ddb117d7030a5020c8476f84b58c02c3d3100c3f7476db78b71faa0f11457e077d162931cbddf42021d0c22c138b42425c711ee0294c718505c8d95220cd01fc5bdd7e000fb2279a54ee03da8ea986684f55ad2b6e669379114d400b0660f42ca1ddf8816e88feb8a5093979c892f7a2a22c85f12e345020d371b60206bf9654629765b4dd3873fd58464a9e307ad4a38771a631f87982e640de45bf869b1e4524d8a7176dbc24bbfab3751f628cb5a2787df3487963f8b56feaf9527e0fc3d1488508c5d8d5f6dfc38f4819311076c9c20e165b08cb8fa212263d2b6f25eb022b888a16197590208159128c42a757a50020e702337c57187f2821d2e3a1b0b4e2022df92c414cabe7fb966584134df4e535b9c4c578c180f3cd31d2b3b6e23cd4920a58ed94f1c843a6ef767193263734b8a81969c52c0823d9768579b7f209d4c1e3d6a57eff47802ddf2ea93d2f14a817c7bed7b4f66f582a61770e44f07f9f4cf5702bc0e46fcb311711e46449c59d94c65dd76a6c8147ca6fc654991be18eee64b1fbfaf005f4a355a9e66b7ca6a97bdd0b1e6435b8a56b2e22717b3c645fff918ac742551416c387e5d8e677894930eb9b04b739d67f05f39a9c333334e5ab231f437494294f869ab95d836d9a661e1e143d720d5e7b43d295e5ed2741485e4a3a55c98d0015ee78f2f860a0deb7824e550d0ed45dc75f94f88c795f18cf37af934984197bf7d7373729d5368529dc9fe0552dcd536e86528c7bff2651f8fbe04b591c152b8a926b5fd7a18e45086a62bfb5d4e0978524e62954fc38d837439a8f0bca42a2672786aef60d8932aee4a85bffc065f02b938d477cb8af3868b3da564bc0a9cfc36de8689eb70c998e71cee52ab8e58e5e0ea4391d7a9aaec2140dbe04dc0bb5cdbce7144e882b3f6b5fa4d324925cfeff2178770f036c746fc4e547640fda8458001019561184f4dd1e8c617e1525f6237ed59108e0ac8da5af8583ac7f185264b5373a07f9670c19301466cfd0e5d735e35d9a1e9e956bd382e475b4af4f3a65fbc5f394b168d0ca0e6a1b78e6f90da3ec95bbbc1949e8221c22ff3ff3bb0336833c00d58975502860313ce7a8b4c5023d86ff87b237672ba272c8d4b01002e675b075db1ad24fd6e088bea6b040f633498ad76a81ffc86cba7509372e4b55f9263ac79e48c06cc908c2d2fbe88f84b362e9f351ef48124de74d39893b78a821162715888babef251e3997b6388d656fbc07461603fd2f8079583ffa256c0638ab65edaeaf730a88dc3ab0aab9de3a1e383f47ae83c9606e244cc086d9b0513285bca5159c8e2986519d4376c62c7a2f222992277454ac8d80227de3003e961e44f5fb3db4617cce8a72cfb59652af3166129f75477e1018b2f027c09fc624fbb5ba6b23812c4e9c4b9c86fd6c7845ca6d5f37158f4fde9bac942d20a1c766fd8e1345466a12e7cdecb9eb3e7635c7b2b306d5bb79d84c1971ad8961e197ce1cf9c17cf29310f3c1993edf9a3a75b921fa442a33c83773701a13d649a89b796e5a89ce07f3eaf20343da6edd26fd7e88ecd9851d00e941eef804466d76a5728b1da8076f97bfb5cd9e47b5feb0df9eed08ea3eea3f8b41473a35e264ba67894bd3b66570f5599caa526d9b0d22567cac2b227c56d5ac18bf2f352321bc455c58c3146630762d4bcf99ad998c16fd85d8b5443ddc07b5f4de19d5a3cd599d28783e786d4e2fad8abca9d5c07239af3815a7a164132c8c1d3cb29b974815fc4e3964314d8f24f39b31f3f449eea76364d45a3484b1e2bcb8f79aafce9c5cc85c6c11f873708563c581730d40494a6d56adef288dedafe9ed258fc0d38a4a5d3385c1e0afe9681ffd9e0d7b03cd02e71f9c46033b94fae0de0b5e82a15a3f8f13522c325d976c4f9e395b2b3c900e02bc30008", "6552520052636365", 0, 884530405, 1537743641, "08a2d99a66a1f88f3d26bcaa46c6cb60c5cdf6cc273461cec3d7cf85e554b6c3"], + ["030000807082c4030126f607bfff0a911ef3cef4d6fbfa4a9c4d6d7d24d68e12c5bf31ca0dcf6b6470010000000363526affffffff014d316a0000000000085152acac006363ac000000000000000000", "6351006aac", 0, -142191325, 0, "7b69edc913efbdf8c52c11e6f595ad5d38bdcc05f69303ab72d4083f606ca215"], + ["030000807082c4030470f62e68813cf9abb97405d27b5a7f8229497d954145a0bc81e97682a18d2798000000000023557ab741d8721e998b0f3fa417189570d0ac9ffb2872447363865ebb09a0080a5a5210000000000400516a65ffffffff58cb950290f8f68ddb84a9f9dac0dbea41e956ca9c25a8c4d405b5f3feeb00030100000003006365c503d26f4034c32e11d67cb5ca67db2400c40f5099ce0c4934be7e68454619da7830e439010000000551006aac63ffffffff017868ad0000000000046a6a526a0000000060695b6a00", "006551", 2, -353673078, 1537743641, "aab64bc74942cc9d514be0b15918eef5bb370a5f97ba3ba9de8512626bcd1fe8"], + ["fe3b5e2b04b18ef66b737f06890e554c194cbd11e3cc3762923d598783415f11432efe1611010000000151ffffffff6912e99b38a73aeb7a974a78064c4d439148b5dfcac563337389b7c8c901bbbc0200000002635179cd3dc1ae63af618929c24b7579d8f9c7aa5c444c2e7312524e140f92824c6f05be00cb030000000151c9c404a860b346d38ee1424506e859e6b6b38545062cdba453add929096cb95050f5f21e0100000003526300ffffffff037b1b9a01000000000753516a51006500aa6dec040000000004526363532b30d0050000000007ac6363006351530000000000", "636a5251", 0, -760812935, 1537743641, "3154017b75fb74967e20713852dbd5120099140564ebfb8050b3f6c816745d1f"], + ["030000807082c403010e480791c6744c3564c4a76e4c222fa3a015084934d7d21535127389028c650b0300000002526a4c9f447f04cb3cb10300000000056a6a00ac63982673030000000000d0bc3401000000000153a52bf60100000000000000000000000000010000000000000000faceb000000000008b537116e0c29f7f8bab8704fa633563ff5a3baceeae4a25c3499024f888d8ebc59c9f62d8444139f2fa15865b2e145a84c3be08994f7804bad9106eb2837570e5c04fadc11fbeeca52198d2ef27fc1a2d4effe47b66888d76d285c7c44fa034000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006943d77458cc52a8b09cbed46e4b9d5800a371cffa47a133ee0e294ab23886fac479594df93c68696f12d965d75f616a500a2dae086c9b542d90ecf117ab600cbbf099d78b7d6340f3a098f21c036c8f73a7cce16cdc3e6d2e99aa8938ed1b962dfcae391861992788d08643e491940c3ee76e9d79033b15dd3c2cd896586c730223f71f45eb9a9da348afd2e3dc45d8991a9aaed88d456eb96b753c15f4651975032df6abf011e57a0cf372cf2da228c6ca935ff77ceac1ef00129facceaaa2cbfa0b01f7be9e3c9b7092941bbe2fda13609720be36cc807899cc4216ecb1738ea4935affd772c4ec0922daa2d81aeaf13407a87d8ed9659a7e5eaa8004090829e8640221ce4d487660340f7b9f9993eda751e36da69b69884759f94b066f6c765a0aa60313f0121811248e315dd04a58670efd99ad558d1e09404580995fe738a5c4cd5c021a8efe2031c2f00c3a4f64fc6a3f6c285b41bbb482c54502c0db62cf19c4b7fd031a6f2f34502607d6917d3aacbf744896f020ed533e9e14d315776faa9621ae21030c34935bdf1cb75d2839eac5bad1ca5930c068dfd218c44d727289d4384bb78ac327733b65bb2f0aff786d9b70d3c3de23d64a1d25bdb9989fc07e8614f1abbacda8c9def60aace70657b2c169e326e2cdf42f4ba8483abe3d27cc1c32c7449e0d8dc38c7e58e102ecb16e34b853a6e9761d6b90115c2bc3716236b280b8cedbb38c01dcd46347d225f2aebd8a02f95b9b1b7d39640e7c69ab2d1296f0796cece95ae414c1ec999010f495e959070f58e42b7a431bd1769bef22f8f7296ca7a4965608dd40d588a1b0c577d9fbd55f252c1b68ea1db63a7cfcafda0829cc1418ee37515794aebee2d85cd1af75cddf9a2d18fe4046c008485684e2956fa5ccd4be3d7cac90f18c9ca8556e840c9825762653a38763220609914dd9065b3de8a4d17f7d6d3b6f55774d203a2d80390413bbbb50adba420a9b221909c25a088cdb9c8ed7fa63db901579a5ccc42ec37423835baa2a3c158d7f172e66e0f6677a2fc83c30a4e4074b81353b5f0edf168d82052b83ecab1a10d627bc4d77eb263199039fd268444f305e247ca604c75eb899887cf420fdd678245fd2e39c7fcf49d68cb1100f0e3c4366109be707736a501ca1e07f516dfd40158ee18f8c16eadb54499cbb4681455e9476a7d2cc23101366942b58c3ffe99cd0a6de851109cd70e44ccc36193500f17251161af7b3ea4950062bb9071934d9e3f71588e061e4a58a4d8f1b1663b5a25569e687ecd6ea7c7af5a13cf93c2ea1db97d544df55ba10d42d181114e0f641e9f43ed8e023b7c3f08a8b305a1b8bae5b5c389bc5ad311baa4cbfe919a867adf1b40d402101ce9128704c76f3efa5641884c832603e6b00400504e1d64f3d41c7499303ee6a63d7c7bca21cbb4538981f0f1a9a4c8ffad9f86c612150b3965170735b90b482a1821a81a729f35688a927e080000351ac9a1cdfb66d78640d8180c5e96c5f9b51f432a230ab631506983fd2717520eec92fc1b61e3d8d44064eda7572f9e9aff6865b8a4d166e4ce289452a2d31244ad0845d1532342bfffd4ac25517b05a8ce5b1fc1f7ecddeacc888349c66e3e95bbd213c8157742588ccb2ba00f4be4063ec39ff2b69f19375310b5aafe0619f789f33d7768a8578b481ffb22e044d4aa8afc5a3032f4aeb210764e963912d3906a1f2379190410ae35127c51fd3f04a486de2c50e4343059d36b18fc35b913c7203f702d089fc717b15bcf87fa63c0c042993834d4ab105db79d861d2a4e7c0e1b0880f2a9a51dab3c376ada5ad5bdc63b36e201097b9f8248527785433cfaa36cc3d088e4cb2a0736ef7404a548d7d8654eba074dee08341ba891a0266b628007499c7a9e83220f7526862437dab234c3e7416069c90e45d3872e921e31cf4824368e1f388aa3b64f58ca5627408ebe082354244330efda742f8d45af6735e21d4889f4fdd8d590a65ec5f86b0f85a9575c533ac310b34399c06f3ee500adc96b5cce3d806b576682a2c582acc4bad69e58eeb817cb2700624ab846bcd9e23bb8685b04ffcce2128a7a9f471bf0e25af02abc747ad9871885c27f19b58ca88827b5183b81e95460bafc49c3c28c03a9db1861e72c758f15e6f9366bf2c6695406faf40831be1912e22d6800bb4ffd4231b394dd29060b1a3bc1a4b3a626794e1c78a45ee4fb3d777374a49b325155224c4c9b488340e9402eb9dcce1be1b0c17f83687ddd412378933a83a11d09d9af950a8926a8e797c22c043f37f54a509f0ed4324df9b004befadccbb6fae137de55787a67a5665690f398a81409db5d88e490c966df6eeecb3b58f9fdcb6954e3f7b051d0aab63b4bb62a5da312bdd19729d78b29b23c45ece60e3d07cd87153f4e9a37caf0d", "51", 0, -539949440, 1537743641, "b41b32d88bb0b6d424fabc36309d99b0e76e2d5b17cdbfc1a5410a6fd89b78cc"], + ["9f2da00201da9fbb7ea50a17cbea140c6cc5556e8a5a305b9d2e07b34ce54e302ef2c6302800000000065152526500ac01b2e19001dc4369020000000007536a65515165ac00000000032f226800000000000000000000000000c49ad58483f25ea203731c0ae17ce11293d7118dd3b9043393fce2b62e140791027cc3c5dcb208563e3175cdf0a74df81959dce44f40120fe8a223808672cedda98b66639aabef52b482c60e0783dd3dbb07754e0c0c6760ece32c63818991f500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a62b5e79023009f726e6090fe1b0427cf2b4786094cfff1c7c1e41e2ba8e599fe822f952fb515587fc7bdf1fdb6fe199f88076be45b4e4644579932312f15bfaf6c71cdc98a6fb0ca5cd93bbeb806c4ca6f0bd75c0e500e5209ef660a235f7b4a492cb98256674ffd40a9ced118465b4b7488ce3597cc591b4f0f896b32887420309553bb15cb7096a231da9c616746c6ad3212bae99171a4e71deb71805960939031d91824bf9424f483557438d7d88da94fd6863b051c1a0fe6564afec2bcfc97b0a084ad223c9564b9d31c238b887672aa04a1029443ce623acd26fa829d0b283239cd72749aff4415d27ed2cd37789759b110320f0a3e32c1ecfb2158ac2c1608f032676413d8fc76c395428466daf37d885e29e40c286d10521e07b975114d48d5602152e3f42a81723f04c374d97bed029a66b69d0c12cbdbc64199c6e7d8bf78a300223cafa1c3b4fa32ff9d7880ba46a783e947fd81993d24989b7c8160e7fff770303175679c8442337cd1fd3e97f2991027c1cf11ee67a590b366173e3acd02c1b17032a3f5cefb066f8fb1873da44b7ce7228dadfd7e6d46f9b0e0a0d4299c669a26ad9ab21e5f9db6d22dbdac6c92fa793d4da34cfb521d3e40ce1dc2448e152a34036207cc77401cbec948fda9a63a9bb9c3175d7a92dbe900c593f560ef6c7fcb723a67ca4f310e7ee2d1d4752414975e6861b2cd0d15f42ce2159cdec7e829a40daf7f0f64c62cfaded9a9eff3ac807b2cfb013f71fb51bf707c892a3a0a090f5b2152a9fdcc4ae39fcf9dba9284df8d08b71d21debf07c154ccdb74bee8a326b677559c397c83fe2e762cdb5789a6f2a168e59dcfcbfff5987569ff66dcdc6fc591d55550e33b48e006afa7252eb0a86517e6caf92f260dd6dcdf419e94cea354b9ff364b03744210b2612a1fce552444855e22c14a4fbc62c1730ab5bc56d51e6beb251df7e1c6a731f2c29bc7cdbcd4227e4cfd7e49381f56a8cb7d48ce2d94d67da3905805ed37964a76340fa5c60609fd352aab842160080bad078c133853ce1ac85e898e85bf80358b998829f6bcdd9ac02ae16c41f912a303c61a49b4bcd9006ebfc499584dcb9dd0233ae0d7295d28b33238b99b0bf0265277d0be7e5e768e0702374f5995cec07be1d71b4c1a44fc5174c9c1173aa8558f522bf278162e613f15a0f7b6bb02e76fbf34c1631ca9351c4e4a2fdd46865fe55cdbb6c456fc95eab817eb387ac856602a8dade32e5ded37508107828261c75f822a090e010bd928d1f8fc40a27495d6a55d8cb7677f381985d7e0df20d32170772787df9b540c3777ac5995760f0eeb415655e886f4ae878f4f1fb811e9cb1dd5067ae4db6f9d20c868a74b8c93c35ce413afcf0f9b6cd1a7f0dc65fdac6ea0f92785b71c43e38956545d4340cd55f8e5ed895ca15b6b34439cb0cc7f5b525961c192946dfc32415a138267eec89fb01a044f9e13439cbe8a3998ba86691c8fc5c83f29b8c42b9f55a781fe9b9c771e22f70692a3fbecb2e23c721676a9972c62fad18cfba82c2016d844a948aa480a52f29dc8ec3f79448fd01760d77ccaa182b6f29f9a6d2dd9a73df342f8b58a0ffa6737e546be1da8c2635fcaa2c17956bea9c3b62f1a2e8960c96d12fcd9e7a1e43327bda41bbbeeb795c14cc91105cfa5943a4fb909c7368b881fc033619ce21aa27bc06e7eb5437ce70ce1bfa32812e8f75be7a40a3d667cd07519dfbe3e48dfb285729c775a21f058715bd389dae453aff2184ee1442e552ba291eee2c0d25707e32016c8178398b79a4755a0c91e3aff57ff5c44a9264f01eafb9551a2f58c3286c345c5cd85f6bd174aada08e46271a7a93076d629e2c91d048bb0248c1a88deb604e7c7a9e6ac0b7fa3f1257810b5b9d9a14eb2ffa6e01e6231758843d4db5b15acde16daa017a83c6cca6743a2d1de1f676ed84128d27ae4707a03fdcb3947400c5de866c4f88631f7f70a357e47a2485db0f523075f7be5b4bc41fcbbc4b4f0515d53e9c3e67380bc50ef7dce7dc178bfeeb26e3499196943e9dd2de5e0a43dc2ef8c13561af5f3f3710873391392c6f0326fbbbee36f517f4201acfaf35cf1d457f4bfb1b36771d1f4519872c80977fa418e60d51836d499ebff4b64ea226e91411cacaf030214395a053cfd4ee6899294e76001cf53ee9fc8038e3100f7bdf4a9847eae569a745c33cc2b74df56e7a3d0af8c4f4ebcc7faf02b1860c422cc6dfe70433bc64b1ca0579dae46e2543344d8a780544501b40a29ad36fe9b68b636e89b0000000000000000dc990b0000000000414f45873c6f1ec86720420e69b850d713b4d3f8315c1c834615bd42e078fac7ae0b1b431a9252eb08808b700f39628667b617558647df1468a010e9fdb3f07d5a8aaecb7e56c9945c5dbdb10770f140efc88d5cd952404cd5b2f95ed154fda1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009de0a6c013233f2c9c3a314c9ebe94a8074df35a0e66f9c4ed4ef329a3d3e5b804e6ad71e9700bba829311c4d154ec4b36a70efb0142781256203292f916cb5f35dace115287e5da90a53e5522a5f06604dc8d4e2d9efedfc51559ec6ea99144b7482591152af49d2737d2e94bbad538f2a651859b1eec2f5cf71c389a9eedbf03048f672798ee5a0aafe6fd24b576915fa7e11f63df95622d63a2488d74d375e2032e5bbf1f3f76df6113f22bb45673482c58ed2a01f04b8447e16b49eefeba460c0b00601109d56264b4b0fc7ec42e7476f561a8a500cee539133c08018ba69e7004679e88c241fed05eae6e74a97b5380c953b5d572456e4c2510db08a7ae476295031b251b9164d4dd8bcc7dfe8c1ea1b4cb66a30d8fe66590e763d9e9e00a4a54c7021a5ecc88c23be53e393d2a6a81ae77eb7c0787c8f6d543ee12cf54f5861f443803107d1ef6807d7e7c026dc17a97124d325b842aa082f34e7dfc67f2da2bf07d5d022d4d95a8ba55fa3c71f5d148e1f8c5bbbe6f040e42985296d0f85a22362db7a9022becd4967a2fe1ff81adabe3d24eaacb58cec223d0a3afb35ca34f563c5fd227c66aec8e7659135bbf2fcfb740af9756575ec99c1068d57a5449653b1898c3a1d8a48f8a0be51d1355f435fd041d5c41ec544bc933e728fc62b089cffdd1abf04dfd4fb8c6b4f1459247512fec3323ed0fd0b20120345a47aa8eb690c3b2e0a0b6dfdc902d66cd4914ea5475a9b8f12a7b387368007eb0e5ba7859d13031c9ff99c2ba6ce5dab12c178f56efcd1ab0a69db374fb852e11ba28ac8fc5b96bc4bf3a3c478f6cffa90536f7cf025dda37a257be5a7500f84bc008100fa2ce0ef5cba8d8d847a30c968f992b2930fb1ebbec7c66cb6b4323b61352c1e8fb9ad40220aa8db3c3472e569ddc35900a404d6699d4d8fb3a19ae410b823cd9342eac57ed135a3d3f62915763de627f04569a5a32798e3f38a28ec98ed4e49bcb6b0ba7d97a56648571917a6c9ed4cb8ec8b8e7c2f22926d2d7fb07ef2e4b72e21257c7bd77db0e06733a0b5df4310a3221b9d51554309342643c60e7c072ef2310e05778de89e3184d4d69b8b59503aa23b74d72737dbbc3e8527f6070ff3124c7ca9c5459041ee5ce29da0e48d90cbdd055869459c269b15b715b6a42f6e7db7b4c48c1c82b1d29dac22f7e27f001e3ae0ac697e6337d7b5cf0634179139a249be5719c3c496e55428e80c2e108c18065feeb174ec358ca85f0fcab348bda58e0f95cd11385094d6a5143c8614bc7114d3addeee9ade030308db30f5efe9d7948139ffeb85128eb97a3bbcb6a449229a6d56c6255228be9876e15fe7e22898e329eb41db8c764677817be4737b56a3c8e8cae305c2c91106bde4b4b189f603fcdd0f3c127ee3997cd38c27e70c60c0baa4eb23be70678f286bba3b5d7b4389ee14a9a691f2b934c8f4080e79d560302065c9404082fcc5201005586ee97dca2bad041a7117ce5f1fa12e92dc93269348493e53eef1ab387c6cd1eed12ca9b82faf68b16815b502773c72d34916a7dc2298d943515e4b6f336ed9d3cec975f03639ca4ea4edcf1b6314709748aeb81f8c77f00944e994b1afa8a60344a47123b2e91dde93d30ce682626ec02b49c6059574000ed6a9659190be7991b4359c3a0dda1456da1673bedd963c5a1a46c24b44d1580e986d85e0a7ddc9beba63548d76fd97290b5484d48610cab5eac0644c69cc94055ba984cd43d8b0972877f2811e432a546b72cee2127e9ab93283a10d164a65a0cfa48498a1bea8853b742a0a63b0aca2b86c5ae8e9e0f54b76707ce855d956817a276d177c7c793b9a77e561308789abab3a08dc59abd79acab0072a17095b8f180442e5d6328cb513a6f44f11022332ef58228fd0bfc07882de83453748db12e6346475adb1e7c03334fbd3252551208aebfaaa6e830b61554b9cc6f989b96a260797cb9663d3dda386d90ba3a7c8a879b8ceb8c32298d992ee2d2423c16a0b303aceab3e1e3a6e4e5ce362561c480e9b2be529bb7f91c0e15b19d5709b3dd43f1dfad2b2cf7f0e13b764a9dbe58b4ea24a5058dd6ba3508693176e707170f7135afb326eedbd448681c1b612c478cbc085f91455a5d30c2f6460fe82ac2e79b56b1188e7c3514c8ed8a37e55b31e98aeda7c0178bc48a44cad5060a4be51a056372e3e7f5e91f812ddc7af833b3dc4232a41200c38406a2f135a3ce549f067e677bbd0644136952341ec6a70db416d9f6cc9719c786fca027b3174334d8be81a8400000000000000000f1e648050000000046268abcb2b692dd725a958a074ef030580588e5b91e744d8ce6cff075bca3650a3b1eba653bd9c59f6005c7e69df1b8cef690fd87aa2bafce424dd9d2751114062d6b5c09c326c370e645942856bc9dd0aeac83de3deb30b84559d8a1f214c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dbad189cf31edc7819528e9b49bce987cccab2842357395898c17d6f12a9b76d3371c546368bb44cb87a2b454d14a5974f74fd57c146414c372afd48986e4b03e8b42e472be855af420cf2bc2697e1180bb351a9f4340481c37c04b77924f2bc783701fec954b645104af5c96f0bf72513977877ee48446d58b11ff4de6927e00216975dad2e85ea7836452d7ec68bda1e26237536d09fa2d955371e496c3f07bf0310895a43e334ed7390a0bdf4351c56933ab730e6e81003ef136dec22d8155aa50a023de68f99b23d6dfba652bb4cb8996debe1f3883adb9be6935001b64a4172d031675fd4a3dca89e1570d5ff0c42f3a6bb337c0fc2d966167f04a2a2874746830324f7a669c5e9d0640dedcdcbb79b0d07a51ca8f05dba537c8bc1242f042201e903198df3d15f5723fc12f5b910167012c279a71a552c7253a91b752f56ec4a5c7703172ccfd698fd685c674cd4a66844aa5b8e9c513a3471b7d8382e5ab58f2bc5ce032308bfb449861f8952942ccb31c38b07b48a6bb3e93ec115875c009d723616580305d23131aad77aa555bd8ef2a0f7c5938302fecd0e4da6dac31a9fd949a004f8fc3cf74c097284fee4f691f192018057687324f8a8eacff7286d5cd6b80fa8846bdf94f1f18ab579d32bbe0ba8bbca24cd313ba526e20e18e0c8a79a15547845b0ce0d627355859c53464c5ee3f7979990f719e89c6236e22fb323a984ecf4f0e13ec7ed600ba47d84e561bccbae5ada53cdde6867f1f55a324bd716a6ab1f7bb1d60e94e6ce38a995ede37a02cf3af4daafb1a4e2aa311da36bcb6a940c1f08d6b97b4c1b0b978838f8a9622bce068d99dff003d35ff1d56f7f50dc0623f96e010656254c69fd10bcf7f574d5fe3cd288c475ed51cce6ba951a6345afec8ed12d4651ba498f235a5c6f5ebb73de24e8ce7fd2dd524334b38cf40a49529031e7213f4561a08802d826f37139fbb3439868bd4988c26c12ea3e0e0b1c33b54c37a578b187efb9d2f1d584a7ecfa78b025f0319d84e72ab1c519a505b70e7a0a6a47e984e9dce15e425a689f8140dcbf9a85ed7de80549329d811dde08d207b5c78f30dc82db1314c0c92fe2470e94b0c63ee97cd92a7564b2c4c1c0f571fd864dd674946c065735ccee96b5763b4cc45198f2ddc3a1681985599417fab2d6d49aeeb7380cbce09e2c252ecc4840530a82c9d03765ff7ea84449e6137fdf9f99cbbbf245b6dfadb47097b5aa1b389c4ba78be265d997c5697a404f319ccde9257566d43419e4f202877c9ce295c53c2ddd5d6256102de297e1a742259ebdb1671b07cbd356293672a108ae35cff8440d596302416c0c852013ddc88d1066037ff57c0cfa41cfa1e39ec513e0f83d430f37702532ea46bd04775d57aee6058d68a0b1bb024dc080a2fdcf20f14b47f66798fa921ddfafcbb3cd3592b302c02001a15c68d30d7e0fe6372c9684bd54ba86a916af06c9fef63cf15b67f84b0ea631e75967e78fd607fec95690ffdd807ed3d4a4a7742f5c978fabd3f26b3fa439214c3195e199f412dfdeca668ced82f29fed779ea24482f9a654aced47fd7d14c53ff01c396a422d8ffb59ea7eec63327c665f3ee19efb972509c3ffb133f91c5fa428f102aba3feee847572c5dfb6b43122f6d80092793d498c9e9cda789c67a90800047ae047169f5c5dfde5d25d28379815f6554f19f8de4a612923e8d9f9a01cdc32640553dc9cfc67fb9da6259ce95b288a151bb1c921b190d6051862ced4e4b293aa015f26098fff675438c02952ad3b1b01a907afc429f07f1a3125da53a34a95e1eed2e0c996e3ba8bcb23e00420c871769133f156bb525c0f22dd61d8501d8f4aa77460eb743bf237aa1d5c48969fb89bc4a812b6fc14b000255c30203e38f4475d33a378eb89b985561cf9a60c1e1b3caeeed41670c8776e534430f7e9e2391edd955dad49c5ef90e0d8c46d8dcf358bfec833bac99337131259929db142fca1a5bf494b4c4cca5c0f340b7e0bf67b3e84ca3a0b9d2f0b90269c083e8c6aa4bd1d1ca62e47285fc34331d72fa7904936f530d503e1260e4e286a49f15aed8d614d44814082fe4b25a2381538ef403881bbf74d125042c9eb986663d658771e54edbf6a6525ca6593bb1a4a59bf1bb79ef463b502164e24f7e73a16ba2356282f76f2906cbcf11827b6048f4e3081942cd6bb254a213c39ffaf212d5aa501092f2cd78308aa4c1f16b702613101ec79979d7dbdf6dbf035f6e2ea82b4706cfcb8cc5711af2068aafbfd1c1a6b432434f0777925a053b515c81389803b1968673692bdb7760086b071fb33fe3e7fbf813e2a490ee7f0c322467748d3f84c284ddaf7c1c9c39b8ba9d5e86fd310ef3e1c04c4b7fafee286d0963d455286f62f3a09959b966ffc36a4c501dd9a51c8de0c", "636a", 0, -584490157, 0, "6ff7e51a7642677c41bde46bb71fbeb043aa76af0225f797a0e6b2667e39c767"], + ["e6ebdc6c02415b2fb27da57a28674d3f42e7a6bc252bb0e3862038008da899e35625c12eb5030000000552ac635363c1e17b8be8bfd00c546b2f7cb5f2f4aad3014d1fe14612cef1229e16af85e66c511509ba02000000096aac6a6353ac535153ffffffff030da2de000000000006535152636a633572d7040000000006656365ac6a6ae24740040000000007525352536300536dc25e020100000000000000009529190000000000618bec0783e662c152e90a20ca7a1f44eded7c3a5115d57e99b4e10215fce5b3e467b366140b5ee514016f4b462ae13c0eb6d1278abaff95fd56bfd5b8c5bb66d5f0f292dbf4b8c7d8c8c62ea66104b2339d0be49d4f420297a883862bb7bd3200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e1a45d6dcd9783e9052b1422dbc2f415f2de67ea0e3542bc0ad24a71daa587cfe14f212b6ffc7325aa83cdd4febb7ad5d62b4f7781de69aab844b7ad3967e39285e24c2084587f3f1d63ae497857015f4fe22fcd0f541fc9a722e14601ef527e3299d444d1b0c642dc28859808fc9fbfb2139996eb5096d5c32c974722451a33030214a8960c92c6e7d5da1ddcd3ddbe365bbdaeec0ddb8ab005120c620445028203065e9c695c5772f2fb0a913ace2a77b379b84089557eac02e676de202f8aabec0b0315e186b1241594129560871857991c19a2bdbb73b9ffed407483bdf5c0aeb6533376b0b9f40ed06ab8e3cd02481b755c316f5362ab569a5f056bc2df454a5e031df72550bf7b56d2728e5e746561be7838cf345b3c0d9b00a273e246449a7c5d030b13d3598e46b1343349e8833d899a0c5ef915077b0f8763288c78eca0d9d7000214196e8ebc005495cd5bc766132bce710605b98b1d638f22bf8dcddebe06f457030d497790b0d23fb1abebe2bed482d76418b99d3b56d10cfde289ff4d1dc2c09f0304b693c785ebcb61eb1da3e21ab2b109be757a730ec8f84102349d4a4e965e47784af66a354012b649d32b2320b9edb5c363ce71b971ce0e7a31ff3dfbff05e700debedbcabfb15a062f92c46b31535f630162901b9e1296c3fe91a5f3fdc187689ee8bb04d4302dccd2406c7ac579ae5c8cc5ee6ed5b954c39d599c561ade8a56129f938685629c6a2375337af9ea09d7a0f14e78ee81256d077aea856d80072c845f97a0cbff1edea3e2638336bb3b22545fea11b8f9fcf8b43f217fe522b73fc95a606d7b43264122e254ebbc33521551a8c0465e3175f8593ec7e3b293ee3d3f549611306596b3eeaac04d3c520f4a75f2acb3f4a0893900ffa99384aae96276af51b647265a9e799b4e73f857c23f137d0ce1d89eab0bcf3380ab01303b0ef9c8138c0a4cda68a0e6d36393e91b698454492ba887694734ed3471364c90357143ddd90ef9a2e35603ee9a889f977f69f4987274e6ffa0dd6b9572517fc6214fccb60936fed8c3aba8c2c387048c284caa11183bb9015f0e1c4e3ace05838e8383d15c37ed12e5fbfc804ae1838b1f6bbad5748f36ee27afb435c40f00a3e2c2afed723a06af7c8a3dac2a00e40e3de883bffb6f7f151164e1cc3d0e3104aa9e2596045b9e47be94040ba0f6f8f7b3b9f522b98a350d22bbd32016fdc5985e6b1202be4bd19978a08df8a35e8d97b7a731bff0551ab38256ce06ea5f3a493d1d94a7ce0501956007a0a475e53de70fda38ac954ec17008e92036711872138c05bb5b88e5643ea520135b4ae10e8b7010c7abfc23a74d84b4a4bda054513c4d156578d5963ded66f7e079dd9e0163fe990c37f6ef671594b2fed532ddefe3eeabcca6d1628d4e0603fe15ce2db9f113a629563da6cb107e7f71010c428754d0690f7130500cb0d0c878048e9e56d2b5b8a9dc061dda3ac8e49843a22f2fe3bb66b385e493bed47c9291505bdfd060162606a8437d3ddf1085df1f2d3c079b3fa28dca1150249cac6610b9072bb1018e5c107a9bbe42072992555096ae2aac35268a533d5f00596f78597d5842de8306abd86d2313a1e314820cdf4e5f6a537352741f81df39f59486bf123b21d4d835c2a0f0c6932f9f24e947baaf23658724309ed150b60e28d0812c7c9a0492f65d46b9583f3af35f4500d23c966bc97f621a7d4de1f6ffb8851c8bedf4ec20699edb51554910fd7c3657de967a16c7295c8318863aeb492782fe87e60a9bcc8bb08c8c92d1d0fc5ff1db05b4b874d0ee1a20f26366969cb2a4672613574d43f868471f0f2419c625feaeb09e09752e0a33c95372ec06ec6aef5e85a4aa755cba32f0b4f46b7886eebf5dc99730ebcb0ae4cef780e23895964d41f9405531c555644a285ad8ce603f2720ca1a91a1c3b5bcd2b5fe441e7f3fea7454f0d3941ace3836d33504fde30b6c28492fe91313ee4c29afc5ceaf85f4ee08a693b2d2ca918901d43526228409f80b297b38cee85042e2828439b68f9edde5fcdd562dc432129fc12669dbc85f77f1015cf0fb2a3e5c996ea52cafa36c68f57f281199d7e1c4aaf7dca2fd96716ff2618e30cdd4681f4c8da9ef0e5bc83735ac6c1595acaa63a8397e1f20a0da1b65b9e655c37961a51026f831272484a931c4cc1bdb39af350853650fdb39278b2dc17a6df1343705c87818854064022beb2e97a20dfb5d0741ddef247a1ea867940463183c9e51f9407d9fdf2871fd5b3be62d7859c93f8a1c7c54e9bc3f2fcd25230f0147feea0814bc2baab5d04a8c1725f439de0a415f6437ebea906e0e2929bd1ecb645dcc6e417295ccc5ff1eccbdc0b0852822ff64717f889dabdc7333e4c2b9d3db1a547a23978932168a144a7cd4ba2802724dfd09", "", 1, 1326535143, 0, "820c66675d2820b55b7602408e8cc7674b39e9caa2af60dac41e4461ea270449"], + ["b981b0440243c5047ddf3c20ad8277d0cfe588c6731ad05d923691a558a2150e35920f0d150300000000ffffffff8b3cb7b0208359c903d2784c1dd211f1f2fac9f3a01ee21fd0c996ea36796e210200000000ffffffff020bdc3e020000000000b57c9d0500000000096a655253650053536a00000000020000000000000000b5aee20100000000daef86528d83cbe2e6209163292e31f9fb812b022ee68498a2c5807c43fef1ede7210acc6d85e279ba5c95929440f913959b94e24d9ccde30a018bffaf80f5089855be605856f6b858fcf0427a70e6418e362e5adea7ea7fe740f28e93c7afe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f341b52145c0aa2c0423286abf641763b6c3b47a0f32bbf39e71bee79a637b3f9ca5decd9b6c28d27420215df33406c075f87e6877a7f2d087a383716d602db1af7b5f1d6be831e5c5ba883338931a017b99d4fa7704d83c12911c30fde224f70f25e5435e9057afb32f774048a12115531084a72fc94a2c8eb29613810dc3b40208e1422eceb1834d37c2b60f96b2141f177ec4e63e5dd6dd1a4b21761b06d731021be0bd49c6de687386d729b1b577c0b260e27d2fd4b9b290fd020fe1ced4b4ab0b01e4c221603c2878d361decd7bd64407c172aef5e94759d3ed61f986053bc1596dd3029b58195ed4d802d8625076e5d985567ecfdb5dead9294ec07b251fc4510303c0ccc8cd9d0b2437b60d38f2c8cb9b1a87d22be18671a95d0b21a709b972bd03273041758b0ad6ec68ff8734bcbef99d384443d2f3a9f66d43582ff787a13d05031c2ec09f175a15f78b59442244778f7a2fa2493200ca221c9d06466b9309f5c0022bc6285ad539f8035b34d2c755fa9875dd2558f12aac8bc18dc3d40fe325648c0224aa609625bbfbdf972e8396938bb483a7129bfda69d95cce80e22832dce8ffbb48b60270421012d1089862945db2cdbd4dbb0ccaca5e87ce2a4dd0f5e93cbddfccd4eab92ad02d074f3d70d73067baf989497cd4548adfbbdce3b148c74ff9ddf747132c3d9a9285dcf2461c71662a8c94e35ba9c40af169390d1d4833c740a7ac742fb031bab6237ada8423ecc0a405f08b4b24072b3205435a46c3a907d12ce7cb3da573b5db52353811e1c444192544a355f47094410fa4cbbce8c327db05d42675e351c7cace35e8d8208c73a76b2bd3f5cd7d74fc3c1da773af9d097785b521ad9cb34ab51f877222eaaf37a78b9d6540c54bfa3a0b1d651609c64b2c12539e9fdf7f0f6adb2cb6f72b3cf1583d4adc53081ac348e446f67c038def007a0a8475708a2e0c03de914bc4cc78e73b3c0a5488a78b9939ad3e0cdd3028b339b1fa85306ce8090b515df304edd10bdebccc17632ffd267dec9b4b188aaa1b2fbfee3f625482e03acf701d3962b22241ada13af161152baf31cee09d1e04c0a172bae5a1cb16823037a0e18706f5d102699758ef692ee424aee6f44d030b9c576e0ef88533c8ccfd2af723ebefc91feb74cde67e83477f26b3fd1a3a19b942f0e58a82c17b26ae42df6c677c8819c6e759d238439824c5de7a09663b95f161667fbd510175b3af07c957e273de1ff42a9859902262774c6b5a54a463c5ccec432460e403d04578f3c7de8116f33b5082e4e6f36e9b79b454915d6aee95db79684ed9feca51ddb470772467fb4e251b71b4e26201c404ce3a4aa407e47251062e049e00e777beaacf4eb1582d6b4f84c0db1118024e0870b6d294c9a4d1b7bbd49bc1503e841404da05d0c494605c52b230d8501275c5fc3d386592e439cd620a0b8d1d3567bc546a08b01aae263e03bdc5476857fc92cee383c0b7ab11205a29222a91474bb532453023952b5f8064359b0e20049285ddcc14e2f3fe37943db7f5ded9a9e63c095c93d7b16811aa253834f5c81ddb5c11fd913f0c97abb9acc4050912db1cb5c65df87116b4869e704df27a9b61fa808ab5271cdea29d3081799cc1093e9cc2fb7b3030c7aa6b4d11dc8a321635c336afc35aa0d78a5a6ac5e5b3234c0425521cc3a8adc12df2d9324866c79c08b46e9d96caa7ff9bf44ad19368d91d364edcd9400c8154dc623e2b2770790476ba7b0150def03b11972a0d57fa705868c1c1fc5fba381729894bc52d5f2b822bb7411928f6b0570787e00e43382189dea1334a2e136d2e3976d7378355328616ede4a8a1841796d3977d49fa79f6182ef8abd8a989db2cad1517f4300c97c1d73bce70db6649857a4d8a286ea808f3500ba1f797ef86373ff7a68770ce001cfcc8261a944b7c9af28190e0cbc469076caa318e9df8861d30589df8bf1393731f23c755ee013006da7e7242d1d06be47089fd3f6fc54c4737d6f0902e273ba505b514f40e49a3ba6bfda659c39bde54cea2fb80eb6fc1e75a97b95e21695aab5fac278c2ef8e6383f776300f87cdbaef73b99f5bcfd186751c916b65bcfa29b9202162fc01cd0efc3c5a3122fa13e22da25ef059ba5507240f1973fa8e838cfaabd4d0fcecc99c1d5d18114604da9677d875e2ab3c98643603e4a1b7b599e3ff5442e46d47ccb928859d555f9055b3a3008fac1cd39e7ed18457c338519fdf6741fc2a45f2658006119a57a8bbb08cc75f37d89aabca952b2f77bc5b6c546d8e9000000000000000000000000000aaef51b80af393dd8da5018bf241f9ab7cd1d071eb7afa0fdf119581cbca745716d75a40613a032b1699e9e01522506eec23daa5d5ade086c1473bfe102c33cb5465b6bf799dab981fecdfcb12c801995f30047b38e16915be1ccab4b158410c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009d748a61c533c269dd0ba568a440051e542ec88ce0ea23c83b8e88271db7ed0b90e72281adf7752c9511df730cda54eacda9684ffe8f8cd56e83b5d7a4d78d415260c1e5d77fa42b107041c185d17b4b40968a9082be90e6c910b5bc96077b0ce0232ef3a7eeb2687763db552829ff5d70926734a66738bfdf2afb1063187b690228ad63b9b185ed105c3e18f88c29cbefdffeeb4c44e5208981f4ca86d3c188e3030261e324febc669aa50278dcb6756229843dc01981702a063ceb2d57e68ccbf80b08452ca8d95e44b7295319877097456ed19c1be42c23fac7e76de5996042d52c6e1f2044d082bc9fac3eabc16b66efae59f70ed65fc6e40b48ea2e90e13f2288032484b6774f4875bf86c0f4737b244bd8b94b8e265e9cd3159822760b8393fc6702252e5920e809fb03e22f70c6e0fdb1d39af8f0a1805b19ed35ca5ba784aeda780311a51451e8da2a3a98adfa1f485b795ae05c91dc56e0404f7a64c2102a3a35500313eddbfcdf7c7c8a39f7587e984a499cd42ae4d54d00a808b78a9a3665cbd10e03220e13dc98061300c72bf532123f4f9199b52bb4e0b3f0ee2ec7e6324a9f1e4de64f964ed495fe4510b32d3cd96a86d31790ee04687cd7cb1caf4a4dff62d7a0c3dea40d3f82a086bb8849ac47f911a16b486adcb7e65bcb5f9023544a5fe3233b6a48da9a1eb3ed10ac4b7633562b6b766db2e5f31bfe3a171c270768875f16e27a532e2f493f04596c19eece5ab63ef110730ab14d539d4a83f105a534ad521f555325e42c4c5695232466e42283e29f6eeaafd7d78e40307da198105cb798a3349b6c0ab10a29f16c0fc16cd0fb03551cbfcda41bb4f9d325f972f3535521f3f0aeeb462e72a7bea733e3ba5143cc8c49b277cc5b3e1f63d50197ace33045fb9d4c0105096285c1b84d7223507737f5f35204c16e7b7b8c7d7352df016f437994cb6d260582b1082e5cb996cec9af04ff73508edc8bb88979691a01c5d966d8d6f0ad9a15c943b3b16a7d3227ae1a5e43950f8a4711792bcda3468d1fd91378b0432a95dcaa152a7ca070a0b5e20827d57aa16ee6d5b63351d31cd98d45a7fe09471e72a9d1b8cd3139ac54aa74910a37bfb8fdd6db342e819d5793746a4e7007166ff3394e4f30caeceae900afdbce3a43906e06553751ab415817d614a815bf657112c6032f7e2b064adb8935798cdbd2d58cfa1c5eb5c660cbd396a3630e657ab50302d7926304027065693d0a8190c5209df4f61dbbe783c54e793345c614998b779b619cb63a719ebd9ffcb3f813a569f7e8376adc08c896354ddee0689cacaa9df6c32e469789aa8d52972598cc2d5316c1f22b53637c34cb1fd83de5f7a8e7d50257cbfeed2e0a489bdd78bb3bff9cec33cbb3f26eb9a5fb460ab3ca4e563a1b6488df12dddf4b4548c7fc78f7402b6439c214919be915533776fe82290b1869537f76bd0c8ac823841001c2d0da04d9fb02c0a11ecb9e1e865d672eacfff448fc7c09ab7b16a45911bd9760584133f202af14974160207d44fe724c1b7b15ffb548bae9b7399d73cba4b5a53b717c45fb5c3827fa92630ec9b23de9b1a0149620ac75e155055e1d2b5aa4d459c37ae7ef73d5d60d2bc553dfc9880250641a5503a2387b36c27f06060b9b4f3a51504a4e039ff3420285d272995e8c5616e56f69bb96236eb1c506a3be74cc4c21619e4585217cd8877e86b8ffe406349c5d021dbe513b804228664a6dcdc0664c09e8f8fcfae2516f9c679eeb22c1e2cee882397c12e98b7fe2d61780a608687b551269e09de1515543ca9ed98df0a8820469ecddf338c7a70e842ea9f53528c72c557e43fba590cee0b04574cf753a3efc3f9d8520c63080c12ac4a8b4be7cee695161b7ca28e2ef1d886a7499d7034ef33a18a80791502f7aa20548f8533aff64d7624c108500dc0ccacec025b980a49d904022f3be2bae3d0d40521b227e74f2dd68333e302736fdcf1c51600b3ad5fa531e9c88ff5a6b0cd4f4d596c76ebdc42c1cd990574d26e439c24d927094bf3b6be64bf29a00d2184ec72a63a65f39a6d410e58d729d7ce256c470e11394ea294e89f879f0d4b4efda28109c9696dc7d3e004222f2f60420b4b493981460f7b0aaab784fd16805e3a9bff1d742f17547eb18d5f964d94ad1f4e2bc07e89474aa448a0803ba95f6b08c636b810815b303f41a01a739c2c426d2608f33d532199c86e7fe9c9df00105607d5579f62475ec62e6104eef93df6b0a2c0205c01ee5db87be51a08cd929376aba55079372c3664deb087877090c8f48d445ca7f7e5d0654e082607324d1211890bce56c83cb981bbd119ff4383a80e2b69d474e44cb5725bccddc9fe8c6c3a0727d5cd3637eec5e747e5dd6dc026950159ec815f66a2149d7cb53da61c8fd3382dc1fe508", "ac", 1, 2133401921, 1537743641, "44580fe456870b7e196d3c41ca28fd4e87215128b81c409d9957e19a19b42a94"], + ["1fffc42f024d5f96155009202f270cf30915d62caf43ece6f6964aedb0964114a71b9dc40f000000000652630000ac63cf7e14dcd821a84bc53c5c6d5141f152348bad3f6192e8686c2bbd5679e8cd8de71b05500100000003006a65a009c12804708fb2010000000004ac0053529bdcec030000000001ac9d2a1101000000000652006563ac517d52260500000000009930591d01000000000000000056fb020200000000b779a7a3dba3d571de333876d98a52d8d9ec21e1b9fe9ba69eef686dae347ca358bcbd06c910585e27ab1ee746837bc6ae07f5d379a90d4fa7ebf82ea09564d512c907a13fe802dad39924f15badc937a9005238e6b1ea35fd4688f50e10b5c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000736138f53f41464cff13ebee666d86556a02794671141e48b9ec80fcefe37a160132e5bac72733ebf41482d5a392a6f10d61ffa4e8c99233541bd9edd1abcbb144a16d12bb0e0fe8b7150fcfc680e9b9439a2adfb634b04839cd9a5a222ac9597ac0b103745490fee4c23487b0915f62e4403cec84c7757f91e957e0a1eab8fb020019feea91cdc75df091675df58e010d780fc2a96af2454376a29113a56cc72e021035e2baa32f57bf7174b49e71fd25d7260aa9467b1f64e69d4344e838a0fbbf0b08a9ba7a7be20fe8307a4afe8e674b9bc5a65efa39fd889efdc6d70e8ff0cc89ea596abf0a9cee8fd06f2d9bd6454c0172e364e088f66691614c86d3bb3d763403238ed84a52b8355a26daca36214463bcdeb694fab79c4bc85ff526e88c9637550211371c0fa00d35aca499599d207f7d3a2849c414b29016bd6d69f858bb6fd05f0302336c9cdf7e4f635627be64e1288418c4967474d1fab39fdafeb6b6cd18bc99022fa22cdf185d5d169e515c8d9e483ff9800c11dd9eb002e303d7d346045b1e690311505ff392234b8437864a57f33c20b460bf1665263335025c51ad6a3a9bb1ed4ff480a45cb1fe5ed9b38f40a9c3d8cd4aed78e791d2183a3409c4e98c1f548af91ab6120b7373ef820f4cd656e5c45cd2bddee57a0d0c62d5c1c43405ce782559761a11f33107f6dd96753f7707faad466b616d2680fbd410498ab96139e39fb4afcfcf1c05362089da661d8e17742de4c5654b1645cf13632c09dbf00f4e577f842c639d0488ee3929ced86f42e86b277ffd9492f34deaa4dc28cddfc6dc7b489dab8369e09967d3a293eb2ad78c361799af137407bef9f63f459e7c2844c27cbd90c9ed35abaf6a4e8ed44560eeb0fc99563ec7ad3e947db9978a7aef589dee364cc6e1395c1a61fe256a0f57276aab28c33d184486de25e86ef24f73a58cb1929076a3d938e6ff43bea12ab6f16727c19217beb8140fcffbf5889009620607de4728bc5015ef82ae1d31fecd9d9be7a945683482f8c195e487e9626a480779ab83f4447015a486d072bf85db352dc552947427a0388a587a14247c876fa70f5abb5fb3294828359edea69cb4b41cced8e85a80dcea521edfb1edfa32ed285612426bf37c551af8d03678339698cf1071517e90c85d6c90eb6512e04f4df1a83be66f194aba328f8cacf9dfd304dbb6ac73551c3d9eeec83394900c4f32691b3123481fce722a20ecbfc43b781fcf0cf5f3c7b1f04a661bb153795506562595db930a8a5bb20684214e17175797cbeffc0aa5ca4976955ccce1177a40846b0507273ef771fd9c6ec44b0ed1ae99d99f18c544c9205e1dad8cc461b5b5a37f474fdf63939f56945e8a0c81d5adb0aed36bff85be53e2b3a0c96c1bec0299d44114d677c0aa7bb7bf43925355484bac143446d7682bf6272f43d85d9b965be375b4db8fff259c267276ef1bb2cdded726d04f12171abd36b332f034958f37efd2a3870a4edbcb76835530c9a1e9be5bd9922cc10b4a0c5b4ecce0480b41ba5392c3e329f69334b2de4b0d6c1668e74bda5ab928c571acab85488eba3011d1c275f54d0b728f0808c8f2f364bc60560cb29708dc5a2a7edcdf8034639694b804a95bba9cb07b26046c9f31873b16961334149cd2804db665a3ffe37e5bffa87df78506d71e25cc39929b05e0718884c7c741513f1aeac9d9d9a37eb1b27d8bf3eccdbf92fa2edcd298aff9aef1a1bc7687f6c4f2b589a86c582a3ea3eb98cf8b4e7ba5fceeb4adeed62cbc49ca27b39a44c47b52857bc6317684515550dde817f22d2c8dc6ac26b485c7d940248322130d013157dcac33fa4e3a034c796cb8a3f10eda05d1425b57b046217cce18df9447ed4e811beccace219a6ac44171ae9c7e9580c1df6cb13a020a87988f5ed196119825130751b1b6cd8e0d9cba36220615cb90176301cac31b934e75fbc28860d7aa76f477d5c43db8277a4b67f47e041edca24fa4da863b8b463c7f14b91ab4d918e0a1dcd37d525ac2a6290f01343a7bdd4ca6378b4bb3c61acd0dcc118306a83e6d6cc307393caf3f4e44d2fc21c6a533db5fcada6d03a4de692cd469f1f9f8d9e06ec8c67c0ed97ac65351af420bf61e15f7d312e1f6eed1f93c171a434e7a22e329010859d481e868150f3e2f1adafe149eb422a9e4fb4ca1b2c539895d1fc80de165a61c1bd87c95f6075b2769ffef5a45c3a93ffe8da2766cf74ffedb62a4fd0a58f3e8dad7bd31fc42b2b883186585d5cd75cc0039f8ea712e43175a19f23567ae45d5e20b73a35d641fe8945aa8e786844efe478e8ba9cf7948bc4032cdc299371657f0fe7998911d42bbb9cac69a2b9913c42060ecac5812160c98e09cde85599d53c7c3754e89a8143b3ed82b5641ca2eb5458135d2a755fb6ed5640d", "006a6300510000", 0, 1859829999, 0, "2bed3f6da8056338bb041c302a48d6f0be911503faa2e84ea1f3d01e64b4701d"], + ["030000807082c40303788d1dc8e742cede463e295145409130f8c17cbd86f43180427ea7db0d08a13400000000085265516351635165ffffffff03a99d5242475619febfbf7cd23b65349d72bb243f2052c2d5c28a773d50fa3a0300000003526a63ffffffff460becd654865372faa6d790f208bab096a48debefd2dace375d78a5e521044a0300000002516aaea9fc8d0434a4d4050000000001ac92bf810200000000076aac63635253517c173b0500000000025351aa308b000000000000e769cd75f226a0c400", "516552", 0, 333701990, 0, "4eb9dce384181783c902f41c0f978df197de9791c67afe3f61d41bac72118fe8"], + ["030000807082c403011bbe2e5132b1b60d288648cba559cfb5618ea2e2eaa07773055d6de130c8b319020000000952005300635251656affffffff022d77b501000000000465acac65a397eb0400000000050052656a6500000000f7941f6901fc5f2905000000000000000000000000892e08667e501350dcebd08b5e43ec69787abcc2df606c6fe1eb2ed025f9ec94ac54413f7dcaee4c91e83e323f8a9493cdb93af5a39b7def15d31b514a4afa89a276a85fcb77a23266632a27ba0e19883c922f9fa823b470ada9c4de473d521200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d59622991bce72a3c7edf8b1a6261514d7251806607c5bf0b169a41bdf8b9298adb63d9c8a89e73510482b5e310b9f9c83db593c925efd429c21c5d8d2003447e388eb6582613c422bda0a879627a732018dfa68b8475f1345b3ebb868652065a94dceddbc2f84959e0b71371905e58347b4653e4ff85d10472b70b4240179fd0216157459e791e23b4a113631e87ac08c36882a6c1be514ecdfaf3d560e72a264031fa699bb0a491eec5a17739e7199d5c7f387de5e04b3651e9d3bf7935d6455540b08baa7463f6718e6b689b23c7e62fa2b4e68cfcb0126d076f3e36f3e7abd6299266b51a6f0f78634b4b5d919560c52c0184ee780b28b91115828454bc1f0ca67022d988516b883c712b6c452fcd7192cb0a60ab076f905e55873ea375c741c2e91020a1b10c6b2c640711cb81faa1cec63642ee9beeb0f359b53bd02b9b24c64b7cd020956c70110de84a0f9a87a03c01a8357032b9b105bac11605df3e40de56a47990304e92c1507497780a5a3329589d0bbf0b164397f66ae2fcd4494d5b9340c1eaf0207baf3981a0ba294479c693ba1551ae2a0a4b9549bac56e648f6edfa7da8f138fc603041af66161e0a782e3709b9c0ff9f8b8d882e09a269876c5534ec5d6756b5f56c0b24e2a1a8508c7a816a02f2b3778c7fb614d57e5fa1bac36aa7b7c1ba6c8796fa6e9b9cbdbdaa335ea0385ea278fc9c2b018228ac80ea65c10f9388c4bc4223327176aa15ced7d6c0e59b4d2aad91bd8279bc835fb8174c7ba4614f4f5e43ec3bd3523968565b40b75e960259f79e07d7375122211a2bf99330729539eadb73c0bb2b67494746543afa3e6c5c7993316350fb57ce24353041d685e328e10d4fe6bc0cc8807dc2036fbae20e0ac8ae3a1f43231dce37e856c017942a40a693990d9c39b3b3da647a7c4f0547e3b0d6802f5f62e33d9220c3b108d05a05d2ea5d9ff008dab1d36670cf6afe8a93f0f39966533cb21a6d69ef41b7bef93c2b953e387e0dcd5709fd6bc0a5a5ee312998887354543082921e487cedff5d3945b63a2120764543562f3d9b804ca94b808b9cd931f00bd96cef5a0a48418691a70351e41af5eabc96ed4d66cd7d225bbbe4fa0daaa365ddf69f24305751020d81116cb200a64fe6d2ada62d85c4d5e9ae788ea038584e78af6b8bf6fe3a94604050ac6e3ec2422eef32a3ca3897154fe49f2ae2535bed8aafc5f75df6c4df75c3f7727b717a96179cafd550b99ca95bdf9366cf4469f5aa591de248102ed607f0ce244a622e50e13bf7650f713924b02107ce519538e182722b2442a5d5debeb4c6e8c4d8d4c36e9bfc74b5d522cbf4b620c6f4d54b2ad779eafc749e09ab3161f73e7d31970accf18d744f53c97f5cce0c3ea88557e5bc5b449ed188f5a1c3a981cfd346e0b1eca53c8a25670aa5153b14cd09f09066da309e15561ac95103341bd0de712afd5f97d2f455d9950ff5a1b4f4e296c446f2e189114f9ed4aa614ceb0dc417a029b7d9c9e30fad2fc0d0c4331f77aaa55065a3526b6a720cd18b911333b062a064db2eeb35e46d99af335e0bcc9a7d6125f84cd97dd8884f3d4a2f8261034906543eed6c5a95ebb6736b68ee7a1bfcc0c74d01f3835a3c98833ce251f3d5514e821df6d514eb90f2a49d5ab0f80bbc595948a1ad05316920317af189845234f801820606fd3ad1fd30c3b5da5f7d37736c7ec08d18f92184e33509194732052d90c8e0b51c82ed5a8a4d245c8078fc3c61ca0fb82aa0bca7c7d75d85f805ba79dbcfa26a78ed1f1ac8204ad99460e19b52dc8e20ceaa19ee23eb3b2a24f59eb35f2326d2887829a752192af7609713f1fd49e1e7904f05651529231eb7895e83424d8cf07c76f83a9eb1807d038bc326ce7a5b6897fb0c330c3355c736236ec480a71c0dafb556a4c944e4425118b07b65563a61e3a868ac55377f9f6f12253505e3fef42a72e0d105490b2afba2892d69c92363522cf134b05b015098d1241e4dfd22c1eae7e5a7213303c75d4a1881632bdc58a424964565668520170c7fe54b4844f9a11159f062cbf0cf1ca0bb820c44e1a64ab291bad7aa42c98eb6e9a626e102beaf5d555baf4787123e39102d803ab2cd5d102879ff72fc4cf17db78f4034441c61706f476e255268d24da0cc35c989b2403157507623f59b3f3b9447724157942749bb0d17cadc6710645eb2a7d9302d7248ba881c2a1e5e52d916d698518364d5a52f904a4c969590db6a68743c2abd697ad0fb998895aa1ee91f168b553887a24f8c0936457367aee2e60876aac44d050c2fbbee6fcfda4884e6e6ad2702fc22e7885502bc14ffa05a87d53cebc3a7d32074e8e15695ee4fe912c49ad69add31fbc2add0fcd6998b44e39cc8f844944cf7323c6d21516c9680a09285c6b72176939289efe97602", "ac65", 0, -1949763310, 0, "798eb60f16a09dabee5e89ff9242110c266cdf5b1e86f4741c16bd5169f2c661"], + ["030000807082c40303b561ca7722f41e5058fbba86b4975184000c7ff79c207821af027d9de95f518d00000000025263862a2d3bb195392f19e4a3a0e17447e729dcbc92941628c30493cbdc6f89f25a69a9842a0200000007ac6363650065510ad8a3e29ecd0c3a061c88b1ac8a9128649c5dea78acba00f659a4034f3a415212aecbe5010000000163ffffffff02269c000500000000036aac0076646200000000000265007c5bab9400000000027d525d02000000000000000000000000357137489ee2705383028cae3b2146cdb0bb96e51ee3919f866459ebd642c3536b4b21aa1b2de38993fb327a77fe717151c84e4cbd0cdb9ccb4185190c680533fcfa9802840dbb11ba979acdc0dac2a967698b20aa1135e4bc80f98ac929e65a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099473af4c0b9c9b6f122c065db4d7aa370ad99dae883e9949c77e5faa1cabe63f6fc01d8e01eaaa723bf3f5558de0df53172697fff14cc08b161ff15df31813a2ebef9396555e23647672c299e850dd1fb1a5046bddca6fb25ddf6b551ed26ea2c238fef2e334c680a75b408651fa23ce0d3529aec31cda32a07698693262db60221a4b2c059ce1d67b56b3c47a0fecdc7eeac420540c035bf2af84a4885757bc9032d1348670194e6d20326d27765e32e4f6cc13bb3ff6e24ce07a1a99921094dff0a0516135870faa4f466abb274cd84752958f20dbef4e4cd9d493b0439c845d222eb9752ea45e93a27cf0f3cd2dcc47b8875dad336320f88a5a11b266e9fdd9480022169cc1571fe3aee26e600a4cdf332b77cae6163d16db7e15090726564ad8eb2030292d252b64df134bd8bc978f1ef42d7bad0b8131f45da24e6cdacfe6b421ca7030a78670c4eca7029df12b7112bc269b6d32fb6c1bed1b7b9272d83661af48d49020e9d13342c4664045f9ae814295245fcd95f22a28f1be2479ca02ac0308938200216f27141d3bcaaa434ee7469a1094d9c652abbe045f8f8fa078e3bb8c70a6194bc8978aef11155fbe7e39fca06d0a93ad7ee1a404535744b67fde99a5698c8b164e5874f92ecf3c500bc0c557cbbb32deba901a6c7760178a5b1d8939ea3e1215e3c3cb63b36cd079bb8dafd199d4f60072e14600c0659f7157cb6ac7f3d977e53527d9a934b2ba9590778597fd90b4fb75a34417f44bebf2bf9f0ef7814a5debea53961285f038f11773297360a71d27910ace00aa7ae82406cc7113c747fefa0c78d0fb857d57fa3ba06e07a2e341e0483789d387e0ba3e39bcedd34e634d5d3b913751f4e611af76bc370c610d3c68cc7fb141be1a232b477265078afbac94c002dce837d3bb10f9392ce2a93b1f7b7c02ee1f1ed47235db480d142722b2a105cb4d21da2211366acc60531319ff943ece40265397dfa323c3bfb8933052755756900aa0400e6cc2850904df77f31ccda6842515b67b65cadd83a1acbf4058ee1b2d71e2798459b2abe5d967dfdce445e15eba197d7e949d638c7390888ec87c5b21466f9adb8ddb299cd9b1e720cb4ad780b5192474b0fa56d15a6c88b31d7ed5cf89938649e978e596a658fb76c6adfc376d64e60cce22f04cc1a8bc8cc19a538b3e89bfec65d39a80bf8e80b16080066739cdc2a2db1b5b76927f33bff1a17eb26bac68eeb9075c11a202cd12001383615e22b240ae14611ab79ba09ddf29515d76779554f81bae367463ede1ee23540261931d9d608951c685eb4ab1f6b7e08096fbd8712f9c509ff896ffc6dc53abbba6409a0a70d2c98b779baa9075b1c7d3b45ac2d230357be3665bb23a86230434c3dc131fd816fcc32682f6393509835a7b3f7a0b35b4a7d4fc150ecfbc3a09f8fadcc1cd2e8df0873c490608f9d051db2f20ea530b4ffc3290f44714a5398c62aa84332faf0be8ca62aa91754fc502c3d2e67ca38f38fa2d9013ed36a453042cfb0a3ddb5b25a78ecaee36b7b5beb8d1191aa19b7349f248051d299c4c8335145da2b4201547c806f021aa648b3c68955e8723342f098e078c9b3d7e705e6bb2d93bc700812ef9690120075cd5b2ae738e6a33a13d85d8b1f3109c20cb53b152d16171bc514bb2705ddb06d11ef1119e1bbdf0123349abb70e7b4c3285fdfa412b3dbf8bcfbb5d226bb62f177ea0fa957f9b415d3150bafa14a4a98e6f943f4074478689b3afea753fc788aabedf47c26c64cca8b8f1bcd8a7c0cb34f5a524b69262901affa3409478394be1774c91f951e5e88344e6eb1456516f5672a848dfe77461b5452fe377a28a36080101145e989feeb98b7b000c89ee61574a29a1ceffe2504bb1037c0bc10d6d41992177ac65eec3467a286cf75bc06ee91c47543a7776533c16e3e85c3025ab70f5af2f63447596e8e85f0eae63611cafe51a97d862906832ae3802d72fe059ab24190e72269e7635154feb52555405591043c4b3f8c2928ccc89a9e7d2257c0771a3f58b2f0457ec97b1e30906c8261c49fe9705bafead70d97075eda38780af9f374da829967302cd4485b802ebe84735fda3f5114b15df82aa9181046ce4b652e807514c7f0d88ca4c94fda13f76b75ae3b27c6907152d1361f0a9734917f6d8d4916a3e169781c9d92a60acbea404ce33760c100e361fb0c94ec91fd10d1db913075ba13fbdd9bd76aba7aca119796c74e005eb91ab57641c4ef6945b67828cf8dc1e1a77f5d0725cd5b8d20f35b6f5607dc1279030000000000000000000000000942fc3d8529e323feebcf2d6e8d615e917157ccb189addda37f6d698a178e5b3436bd170ef4eec2fde6253944c9b2b0affa582129a8b41cbcf16fd01961ca39a2ea1ff96a24bc876db76fac0fc5b10b13ca11c50f257397838543197d7240df000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007d29690fc55ad5dc705d8c5c9452eca07116f38f821d7b9c216c3e77916266bc3d3d0a3370742cf5af3a016f33c48bd0d1567b0017ec799692d3d15ca31b4e40dda619515c8a8ac8139544e842e85d58b7e8d6535f928a3e2c55b10b9a134ef9e0669f2aa4d2f9289e94447d1055a812de99f667329185d0ad3cdf07c4124779030ffdde74c7337041670802b86de489c3acdcf727fa4a2ae7b1c3bddd74e2d16d023021f1063fc025f069e580b7bcbdb75f2e126ee9555db1e82f138ea33986cb710b04341b4d961d2013c3001d80bd381cf86fb6b8b2ebbe7af953067320df4c954d2362347f62ab9054eb41ccbe71fdc88b88d3cbd278acd225d78b393ad17d09fa0209dd9c8bfa0b4244d368b95c25745b2090a13c6c88ea7d25923001f456201fdf032a61e56f227f0a4e15fa62105eb64ac23546a6ccfbb4eafd806ec18ceb65f1150301e7c361fbbba09fbdca02e3a84ebe3be5e646079d09bfa424d1b5c6329990290210feaa1908814845ab11b3faaeb67cc477153ad7e721ad6e0c044a83dea08383032f735cd3deb5fa502d5a9956a8843d50b55dafc9c7b9e94c7d0a6b29bb3b4278f87750db6de54e7c6645cba6bec83a1cfeeb429643bdd6040f666ba40585b000f24fc0ca930ea5853188507b4ec532c2a7a739a5f55bf231b855659b5c60ed473cfdd15a9b4c381277730ba79c648a59f2cde6583b760eba2ae9c34af7641f22eee2c300b770b768a131799ddf9090d272b4c74716ba26e8e68907142068ab0e08f4823e066992cdf2b4cae6384c13f058a6ddc1d05a361bfa88ffa98fe39b2cf2c8062e5d7291c1015aea390f468b375f8c5e635099dd74967b63b2a677e02d6ad2e8889b800401fbb6f432699bc7b19343204ca6c610f42736368f0f8633061c0a25aa73be49871556dcaf81bb1181baac02419539b0374674e133c34365ff8fdc0d972f03659815cab22d19cf609e4f8a06cd063e0fcd8ed46d6ff97c0b2edd770c79aa597428666107960cb057c38fa24db4fee03d8ea635b0843f309b0cb40044551a9162977509954634c697088b301feb23b10fdeb20a6acbba5ba307a747faee2ce3bf7743f4913bc1db10eaef9fd9637c433fb3bd3c9d8dc85f0b819e0d074ba6c606026d3b33e4cb788f596b16a1de5b1ebd410ee55ed4416e2b2803ece1e74bc48daaf23edd32153c1a1cdaa7a836e574d738373422155209e72d83e94a7d693e92f9368344d478ab09c57074040693ba02009f9ab8b1cd59b4db40106a544a97ea8ecbe1bdef2a374ac6b6c38e70e677165b6ba2d40afbc0eb1d6aed5be8519b7139fcb1f1a4309a06bf760171244f8df41dafa38933295eca20da5e72c076a62346dcea2b6f9f1066b7845afaec64aaba904dbb5028120085d749b953231876f79b2ccc3d4be8aeeb85cd10a948bb32460fad71de19d69febea56607378c4012b1245c613fcfb085d8def4aa8d495c1e52cf0a1462d6f30abf8539d5cb1357cf1baeb0ab6afc76b5a216a956f5c7e346f0b3d9c618faa30cbf39d6903f359ef55c2c6261f1c64064cb278d6a9f8de9e958d273563e0be3428ff5895be5ebe59d3729d2c2320321ca1176e99f352671f5c7da44c64da6a252435751a602dfcd10ac6a1be19c1c334bc82ea4ba29700ab1bc27d6d57217576cc7b2e35b341ce69fffed6600bb4ffffb8147e646d81e9f2dfe12adea89b0481f1503d1433f944d891fe38e149a6a06078e99af9719d1473b0c42042e34adee6d2fdc697efef6f52e86e6e2ffc0457116f0c9801b8d2f86611d7311430402690177e43fa33cb9fbe7da4dba35ac80bb71abd272598a082e15b62ad5cb749b16334ea9f6c7ac226979e310bab08eb33c35e3f2fb2b5bc7f0e5b3caf209ab11fe3731437b2660be18a7db38706839c62a2926d7f26decc35749d03b89806a81ece72786b4c546379df4df9fdc2244d0916ff284a15236927a69506976f1a71200296500dcc8524b3d92df5e71342002bbfaa256dda81dc60417257f30ec0092fde226578ddeaeeb0601dac85c26b1cd1b67b5476dd360faa2a19bd9dc64a0b939265e0b47bd77668ba3fadc56d17f5462f936efe900bc9dc31c0986dcc77e801e850517999167ade7dad96a4187b55be5970707b8ba62e44381b7247f19b109741f8124a2cdddfd4dc47bd64146bafef29332239c5d4cc90fed298f7d9f6ff2f7cb3ad58a9444bc6dc9bdcba4e4e729cd6fbe9ae4ac2a5bb48b2bf3389ebf0b38bda287ca8e4afc608955434119496c3e0c96ee69cbbdd5dd8a80bcb2c3a68844a2732112223acd649d13c7de89db35dbc8bc0ba47cca6144fd39db703aa6a803a6ef68d3cd1c5302189267ec404e34f3871c9a495e84121e78f6f4d0b2a546a63cb4094834b00fd69e6ea201c7572112fb46beb00", "65530000ac", 0, -545910436, 1537743641, "03edda4937898dd1d0fb5f3af5c71d200e0be4fbfd92f291147100ed8680eec5"], + ["ec18073a02cb9e3ef8738768a2d55782ef30cd41b47cf83694e4beb832d67270c782c2274201000000050053515151fffffffff76ae42241ce6ccadbc2299772509a731ed0bcea525e35b15f2430b4a6f67ec1000000000652656aac655271e4f4e7024e8bc4010000000003ac6353694ba40500000000096a5365ac6a00525253000000000300000000000000003d27a30300000000f8a3528369549fb4380f71094d98068990343e3806270fb90c717bf2554e83e5705c1f05c23580bb71fbe1b08e1959e04c29c74a72b7fd2beaa6c95bc326e410e61ebb4b07af4b4a6ee7665695444a6a53a38527613621aaad2c2b8fae8104b300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f54b31b6ad682f03894dd87d556943e37e3a64ea9ab3a74a073d1704fbaaa66ebf2413fe6e3fe5d4aa7a304ea345a563a5ce1ce9f68a84f1b7caaefc949ddb26bf698407e255b5e58417537605c78bafb941f1dcd7874e285013e99d79037308897b11fccda329d4706508c64cef19e3d207c735cfdb6e381385ecbefc7ed889020f88ff22ed7107ae72c9c664dd3ba54f0d67f60bcab8cc13998255218a020e8e032015159442a9b86fd6bd5166dfd4fd2afe86aec47e1556f83728791c5d0dd5310b0568c345c282a87cd7f3c5c507be1262dcd82cf18f67d6aebe5cf5eff3487ab7993df0f9ba53f1b94bef66ea48fb08b9f536728ce97928f6054cf436f1041976031373b2926b6a71abf27e8c3695f8902e89d3041b042586670e229f65f96fdd9e0315641fae4ccb05f762e5507aa9749c6ec2d2aafe169f24d23ac891e1ade3dc7e021814242d364b3299b876b160a84153cdab5245c97dc4c0061860004c99b45df603180d4eef6f1679130bc5b5d8b9550388cc5819c845ba7ad63da9a56cb21dbb39030732902dc56605e5ba4ad993c959157799e76ae9b19cc33e647575c5ed9d9790c42f7399983ce9541b0725e92cc85f8e88a363199b33deaa1d068c70512c9583fe1d652ed5677a7ae28d756c3258806326da7559851f2b1291d6d8d1de29892638cbe68bee9df594410ac200274ccf6b5e3b9040e4c506071a6a479d883a980b3c14143d46455595c80ed787ebb5d74425109e3f575089fdf64ae0b73a1a2594cf3142fd59324f78a933157ba86d95b597381cd8c950b9d9482378396579be30d94c43a5008ccf02a9a33f84dcb3bb1679157c993144968410bf6c966efe7802804144bad0a40316016ac7c4f6b835cc7a72ed4a01761b9ce36e2e2a611004ce73e613c4609a80a6a2d18e01db5b869d7bea982e9b5b2179de04eb341ba5c6d2eb89c89da738f81a44c20b472078aadd965dabf45cd5d3f53cd74f77993b678eeb7d4db5310ef649f62f0e3e0fbf3089ceb04f0a84549aeec23b30c081a3dba3cf54e180ca2b1f214e80b3c00e595177ecb25cc7328b916c36a73ff3b5666478fe61558b661fa630dac6e6ba9edff2db055053335e0de4fa585eb24c4b4381f6f5d9cc6b15fa3a10496a6bc71b52151cf9deb16c2d521ff80ef2cee834ac421a2ee0b5fd69167f6281b08a9279bbb2c90671eee83ae5ca79007f9935af101ce8fb0f65aea375f9b3044fc5a54df5c4c7428a4a107fbc90dc977a0ed0f16e6251859ef840d1b546760a839189afbe26e1c8d78b3c6b4ae46bf6755fb20d340d47e8df04812f67da834957864df816ed8e0bd96326b58d555616a2d60fec80173286fd6781cc95c7e15ca853332e6eb3c5f1d7ad647efabf840a8766cd145d4bf3b46d7199e3388c9e31b87d521b4e579a153a9b96afe901f463b1aaa0cd2257e1ab88d61068eac336cdd89c9476f19dd381e2102ed0142999cca1b7cb0f7a66f5d8c33dedcbdc2ad5c1389049e6157339fb30efc201bb6b5fdad42da452eacfe9b1676069ed5f0336c1c458679e0fe7fd518c68659cde6b4b1a44a8765954345a4342ad024b1825ce73f5608d1d6ae1ebc8e59acdad3b1860be7e29675d44948123c73507525f06dab7602987bf8aac595c2670852441616deb85a211b9a65983ac36ff8f8c7c3fcbcff70817232aa2316092ef9c41c6047f61dc31abf8f0488556769b12d445a28289116c2fc7ad49d939aeedc6be3b9ac5d4dab93c02bb4ff0f87622b45f8708bcf0a0c2e7a7eef537c03b16b062a8de48122aac705b79c5927a4cafdc935041714dd5b38c716d9382ba9c1e18158101d3829fd9320708eb2f6b4b7684fdbdc334de3cda70987c23b94f87c128734189821956e55ca3b7451f15f86a4ab6189e34e3315e9b51ea39dc151d440022803953466d001e0e2c6fd8053b0451caa31447d298c6ac8291a86eac55fd997d6af1f2c8cda677a24a7a8f04f09ddefc9f340454b375eca64584046cc2647ec3af053851b73fedab60e010b0fb296528d42c9d87b2003f492e0fb8d9a8df40ffba16d3fe17b5736211120d15be1f8d2b1a26f53e186640f62168aba1c4053489490e4a38ca81143931523782a557b315f2476d9e8e059e7daadebb1306791b164fc17ada47a51f6aa403a669cb41bc49fcf9b878ec40958b3de35b63921a557d3ffcecfbfc3c715fc12a2e1df2c029084c1f7b5d52abddca2b50dd2e180c134d6f67ec5f9e22c4aec34cb6ae1f498dd41d5658b861331292a22296ba4e00000000000000006862f50300000000f963e65621ead3f5018d634efca49d4d276d9bc58622c3f5700081cf115b8c26a703b7d5ffab4b69f3af76842d9c0254dde3f22eab7d34635fb6ee90357678faa493362ee0f2eda32f895c1d44c9e4a815098037ffaaa00e85768e40c16d7a0f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001fe488ff75fd6ca6a413efb23ea0f368a9db576e4ab09c2bf861e57579e5886b20cc7ffcf6fc569f104a08e5f1bbed5bbecb9a2a42c8c1f5e0ec53db24ee4ad764c200250acf09b4d6533b1df9610696de336ff3fb54aa161de82a838def1e8c6ecb7523919c7c4f7f0f52d622c10e0ae4499a724e1814278b7d92e2a97dfec002235f2fca4f9b0554f279ff204f029966ab54e1c5b0cae8141b2aa4db36a216f4032841000fdb8a4dd739a09a8b573d22676130ee05f2c630134241e29903d23da80b0055f3a6c5aa41c1dafdd51e08c413b3ab0dfd8519f7f8ad7cafb58a9bd0c4cabf34cdb5458413e44ee1464aeb01266f2d57b9cfff0cb5073a8eca005659b612031b217d316af8fd5154de6d006d2d99909c7c766c776c4be375688e2ab961c1f603231c46baf3ce99bdca7de80d18fd66cbb087d7a4139c0ce119761006b42d0040022274621415899fb4aa0f4d2a4544bd9f7991a9c79ef834433acc8100c97790df0311bf2c34a69ab27d571935b0f4165daed6023b76e2c53303681428dca94aace20313ca6e05320f9ae2b8bcb396cbb52a17b04c279c45f36f7eabf9cd8210c656e42007c1c0abf81457985ef8b18c9586e988323c58b6e3a8c07e767697c8a90c0d544e126b56123e23da39b49cacd79722c2b191f7c8e2af0530fc31405baac4bf7f8af8e0ad699f819371501d58f27e8c875c189fc53b4cc435f294e8d35a5e7f8bfef7e162baf6a6a10433a642dc657fef495d72fb22f31c15274ba48e32327bb896c8f6cc97ef9196193a08e5b30b62df43c323f41352c913822fc49d366e03e05f800c0b9a965414e4044a2ce955e5dfb86b3afd97f28d89b12875ee79d3dd7bc1eaa0ab7508ac710dfa3535b68f851fc08cd2f7dc36d14ba506419d503031d3dc9b667c6e2f4fdc44617a030b35a42bfb4c3e52548b39c7fe336e5d1afb49fe9347cf03ecabc778b6a757b42a1fb56f86e729bcc029e7a43ae331fc40a6468537a406b4ee9cf2f7c859504a57c657ef3e0be7976e93d8ccd7b785a1e0e96ec2ae4f3c31feb1393a76be0dc44651136d2e1e86b8763ca29a95576a9e36608f2ff6200317155d620d16b73e7af8c249cd736a9d31c33c25bfaf3ad2b840e4700945d87896e1a72b512b0fd3bfea058aa5db60d01785ce05c16542bcf397aeb1fd49dcda08b1cd9083cfeee5bb0fb99676043e6e366bf650096a118c80b7632e72c284b8e13b64b13da98f4572f729daba92b6dae81e44ebf754ffa241b55c5fce95b7c4bae6c9e4cadb0a01574e58258a8fdb7f072ac9000e9dd3e2cb1d82b94bacccc1e48d0fa2b21431dcf4bc17c80c7c83960c4ad3918b2a6bfe5c9848e9d49d67c99161ea5d511bf67230d8cbc86f00de1d5955381597e42d6e0dd16fd9aadb3b78b6d0b2388a380da4c85c25730e7dc2ddbf0bdd86a211e070bbce49b6b359f492026dfcce089538c61a6782d251a7bfb111cc0b4bf0afb84ab49f95bb8cdcb756b56f854245eb28d195838ca39dac5ea6454236fd8d8863fe4a9c6716b6a2146705c74ddb1d2dd25027af030a7dffb26c53ef9e6693739dbf022ade4a5948877ca2c0bdb60a621d92b0d0f8964d09885a8d3787a302a19eec3ff5886317c7cce7877d5c5b8599740acdc12028df80cffb2b9997aca8942b5fae12269410a6b9541579270f125afd50d00c49a0a9f7dd8499cd40a02d464467fc6e14e62301a344c87f694fd7433bde3f9d9b6bde44350f1de533128d4966dd528d6bb860739358ce521032fad7fb651b33c7552b6c5c57a87b6286b1e89ee23d415fee140fd099bb1abe7e8f8222cf01de8422c4199fe70c8ff2f4da3b785c0bf192e035e911c902dbac40dd31c0e192c09880a77c920544e4e8370b5f09f8b2eb6a3bfe1a928ac210874fad23482e69fe163be6bfb6b8cc93c688e859d65513c2ee4deeb2cb022cc9fad4349cc31b71ccc73ad3cb5bd698fbc18124c773ea00f96de377742a324bfbb8b8f0a6650162a1a4e1e9198becb0bb4889f52ad751f218095f2a0ec9874e2b888707eda1022ac6d0c2f5f5c44fc19db108b02e0dfa0aaab1cd4a4a13a1f51770f66bd3f93d174a6f558cbbb45f2d4cf618c048ca0118432fda931e7601d34df7aac2683d5e3f38c9e5051d92997c2363b35de29ab623d8544d3759e01be91f94a658e3b81e026f66efe1c9b628448ab4682baedd15d02dc9c81df4c57f02a93e512881f3ecbca802b0fc4f6250ff2e249839bfb52d97fc24e205d6e6ab9d0cae0a6d9601495da993cb1e1000000000000000005f6a6f0400000000415026b853215595abc7f3191f9e033efec4d0d34bac874a1d783a2cde4ec5d6031559b873c5ffb0074651be33e027c5b4bcc72baa856a92f4f51728fe08bb921b61a6a9b74f6a768e42c30df34075295cad2dd4f4632f79a0df4c0d7dc26b2800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000be64c0ec9efa31691c2902729560351da1daf0fd251427eefea21fd750630572b97fc9c97d067fcebe7ea72d88ffa35e10bd384ddfa1eff16d02b30089dfe0fabefd6b429b7ae8844d56620a159d89bba7c1874b986929559b7f232b808283e6764c3bd0e3809f2f941c9929fa84faf0372eab1bebbb615adb3d3629acab7c7e02252eeedda8bd37d630230b48788b2cf92d409d78f4d5641607291ef83aede84c02281e01e72cc76687a5c89f010ca97c76aaa4bd76b016bf50e814b40d8b30f8890b009c1ac89f6587182fe2a1d5f27cf58c1cacf9906f82839a9cf85f59702e97642f6e6e9d9ada0cb466c765c92f8ec99202e88e4569e3d32660a3d39c7ef724b502254293ddb9a8ab29d389ab1d1dcb7940847a792b5bd47a0468866bd278c0ecdf021ddbb77f5038ebb6ef1cc1171b8bd0660883814e5d094519223f81ffdc2b675d031771173249b69be2a09c9d1ba9691a25426d660afd1fb2dde76d38d7805e5b02021959a5e303d1177c4bdb5ff0776e39f8bfbe7e1329fa6818bfe89c92e258152a02041fef7402a12563fed066ff6d2ab310a6e773d0ba55150ead850d3eb590ce8a14865ece1afdbe79f4f6cdde8512f34e9b399c5bce87af8e8b514bbc867996ef31536e706f5561b08deb3d0b5dd510038901e54ee412fb7aeee191e20ae19cc7286c632f394b4627a3a861b49a307f8fc09c425e8c40ade94a8f79624fb4b9466adac5ff1d0329ebfecf3d6cc566c0788af7be939ea8e1e485bce38b28ec56b523e6ca482388625a2689eed8b426df7661d5c26c20e61396a29ec7c9b8bb0de23946b802d743d8bd6c8287cd8c6ee046675aea1af6d6bb0a91ad61dcbac2f0c681cc7788bbace98a60c137e9e008d183bc02cf653da8a99e8e4ba98a65a69708716a540c6efa01b32d78fe91aadbe825ca95043f003150f2103faeee57d4b8f90cc76fce47465c5a38e6a76f707d04a44eac63416a39ad67dc7567feb7db8415e250acef09c26c420beede6e810b11c718b2e99ff2b9426521871bf16af90911a7681e780934482eb7d1b761e9ec8a9ce2608e0dd09f3c23ef8c016c0bd2cc00f7e00d35624d4f917af1fb45e7589a2239b8608960e92c6f8ab10af98c047490ed84e3989b39a2f19bd61aa160b7b7f7a7983835177e073bb3537036db7da352cd00cfdce483ea4d555d86e9547763bf26d1b3af4a2aa4ac294cb383bfd4447657c9987d14cc809cbe165df58acc4870a8f4a62a0ef9e8198dc1c6095d2aa8e199ee47506838842a3937dfd2a0edbfa9045fc553a4d94252c946aa6b45d7231caeb71372258fda868b03e12b7ba8748a7deba754f02cf94236cfa01dc154a58e87bde5fea9402fe05c0840bb08f41be39e374e629eff4cc09d15e6eecdfe1dad635cd95c058aed0321e81ca22363975b2bc2cffc7486aa53c422d36386bceb5b1b46fe2ed7799881407d4743178959fdbc25585f4e34e6d4122d560a188c298e59f7f473b30b3b91f350b6155995b8cfc0093558f6717308af478d988327e6e2ae3e1c10080f123d6300adcab2a455b3251e0c348d056dd7d2e85f1d180a7edf9bf1d72a816cb597bf1ade645c7d8070759f39f331ce1b99963546a0a99448a48fb95d9829364915b5fa183eb711c705fcc85347bbeb987170a0ba82c3190b1804a0d5737050dad97e3c827457d5d768524064b597c76971ac8d9e7d59c7797b8369bed9b8170cc431aede2ded246f63f3e30617702176f5384f9cfc7aef6c8e6e6310405bb03141fc0b00a990c0ce69718e1c7a57ce80637c43a70fe63b30f7eb93f4cff8ea9c958c730e8b6b69657452345ebf3ae8af5f885dc59951abdf0ed4613b19fc6bc715bf2b4eea387a21d733f9b48efcdfa25a068cafce6c02961044718526e2e09b3854409c16a35c903eb35ae443d06e6efacfe943de4f3f60027ba796d01b547386acf0d415ce1380683d97181a7b5c48ce753f89dc678fdf32c5ff4efbed9c3cde467afdd231bd1d8c91dbc7a09c3a6af9c5f2ae63b95a291df96dac55058ad37786852ff9035dc7e1e0cb18465f8817c228c91845d917bdcdda365b3a85e837f33f872614a4b85cde7aec63890c771baf6a8f38468cc0f72e38ccd4a34a7b6fa577dfbbfeebcb233797dc22e08ef2560380d8a76700267737898022c98154d5797f62de995c740c6d9c7ebb6ae186b948b722c11acc9c725c1d78a372424cd08bca10a0f03c87441197d01f71c593fe2467623fbb9331659d669034c6a2312e6c596329cd1afb4ccfdd6ac37f14c579660720932e9da511e461a133303b7272f6ac1dea6cc84fc7231ec838360e9846df68503670bec1fb3ae2c358512ded6c308910e59cf4b10b86723a628249dfecb29d41d8ee10568198f5e6f6393c1d028e2edc8702b2830da08208", "6a51530051", 1, -62700499, 0, "1d4d46bee1f331696c5961f2916139141cf76fa466ed3ba960464c7000d9d2b4"], + ["f4ec04090316d279e4e56ff6e8d13d74a8a03e1ece299c291e3b77090db2955317913860350200000000ffffffffd09726bb1fedbd30f064b97517ba9e3fb943bdf94597ef22e989263e0970094b000000000952006a00ac5163526affffffff62d13599e837272bfc04d53ea3b7912f1676a0b491ffec3e769fe7d5a02087ca00000000085365ac6500630052ffffffff0199527d04000000000853635153ac53526500000000031efadd050000000000000000000000001e5a6930c99c7d4138b5be9e3a9c6e452af723ba5ad5ee04ef510281b6f2b436a78511275fd52c407ab995858e71385fa51a3355dcd450f97ce87c75ced3034e725321b2024ec3af0ffd55418c0cd26f6806a971066e3d940d28f730c795bb3200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e1cee7d2bd7d6d7adec40a29a560b70210b6ba05a74ac60debccd8dcc097ad0c09c52395a5f0b41ee1edeab501fa84659dc5fdfeb9e32afed1a9f07a099c8f64d1b5fae12a50ec1dc378312221598e3a11c0474feb363c4e1941e81f2b64c5673b04148c3bccc7b618b515a45199aa3af7194f29b8694702d0390a25e0ca6c48032ff041b50e7d4cfb1feb15058b5304d298018f79080e33fd3ff7d845b041940a020d053298fc90641ca2256f3ab95ecd58930bae9e58f04e95ecbe4145ac9448950a0715e2fe5d3327ca4d7feac62875bd6a77537db8422ab5cdf23f5ad218ddf1faa5a8be02a83763ae927d6b25c3372b480f16538e5533058af202d58bc12b54d2020dfa7489a272284b3f416e8d9a227120f6a0f4b97dec63cfa83d897e4e292401030c261638dfda33673104bcea691ab8a89fb3a6da4f26d3d913461f378d7ca3310225f603f73fe617b8d347ad2a18ef45aa011acdc7cdaec6bf2b9f948066a7c16d031f0dea96cfe4e8660a5427f8207a3ddb8d2f4f5c7cfdb8efdce8d79cd891d014032c54efaa70ed7e4a0d909df98841a2c3e183c1dc996c7b8a84cb175b9d9f1d20dc16ae81fe83cfe161427a30955eb2794be15a3209aff9f3a83d641dda7825e0dfc050023cec540f9f8caf856f52e79f6bb4081fbea5ef3782a3823268b12a90f2520bc2c5e7804183f47d8b7c1a56db9518695688d29ff8318bd5d703fe6205d3b43191e2bf81d157afb0cc1172cb1fc6893c3b90e2f3b48e21c5ee902cc1c91dfcf546ce4d56a56868fbd846e0003490edb19b5d9b3e424c816cc316414c36cc6e48f9de6690009a8ccefef3d2b279138087b7549004967622d5b353728125d07f9eabc15bc5c003eb975a839654b9a624e822de3129723192689df11d8db405ca24fbfb53d0f493998da9fc8ccbf3b18483c70c78d78bab90119e2d346ae72639bb3b54967618187c85227088db28c47d491d809a3cf3bb2d81791d90dad5572909942cc16518b390712fc66fd14bf96fc74b4e32dc7486298f067474daf08c3dd6e09700dad2054a37301a001b7ba322ca304a63d7f9f5a129675d0aafcc50e975c7d342e208665ac0d52152f7155cc694f886affe58e156574d497e2861843d37a8ee7c4112c221f4e7255a1543003739afd0158419d87db29addf7898c87c0347ef6a763bcae282c6ea8ac83548a35602aa90a333af3503015ae7499b16484c26e377f5bd9d495a0bb5f79d7bbb4d3d330d5ea92b685d593d7445abfcf2e6baffee19fe6fea3d82b2d764800d2345a6a5b12ff303676ed13c1e1c2be9857672565ff0ee23210e33236acd985e87e1640e138cd70d7b3c04366447fedb774cc9933d785b45ef61ba981d54222fa5f4e1f78b9b7bf1ef7ef0c7248b1bb3add2fc43dc1ec955d0644bc6e78949f46df4a111399aaf3ea012ef1b44921cc40ed00bf7767b3fcb3f58bc8d3d152d5f29388449bd3a8bbbb9d19d7e7b3087dd1dac93f62b83aba6613cf7953f22b1fc1778566ca5770bc5524574be6204af2efc0dd7b302d231ec07c0cd567e5607513f1b28b640a29f6c3f1bce61e8e77f3c7c87bd1403406bdccd15c19ef339a2d4b2b0510cb0a29c08adc38fc9190453d2958aa39aa108d1846ebcdbb0b4e0601ccca070fdd662b8edbdac541157ac449ae7de6835d296ff0e513aa8ed2dd38928fc530abcd2289f923327a686dcbc9195532919ca3aaf86e6623571eacebc6c1a00bc8f090885197c7f0f936bd07e674fd8df5ef85c9a614b46e17ee8bc7fa3267b5ef75bcea445418ca6d11f13a7da5a57bad97a0f452b3be867210b6d0641a4f6120e709f593d8f65623b10a08cd434e5cb0e3e01e0258acffa098857b8084966a6f5d4e7291cfd8796ba5370fbcf8a638aac75483002f908be5d29eae917fa7573b86982dd099cc35f94ace1137f584d70de6d61ac3cc5cc84ac8d1564a4abb6063c0d4b345716afc31cad123675d8534601d232413622d4fab8f43b9df7752cf9e356ca3296b62cb7d9865051087190effe986714bdf94c610c611ed9a85534e645bc649f78195ec66d77274b1eeb9b9df7ce554033e389d3b65e613faad41bb66e57500040cef03102395bcb29467f9e04d07ae9ca57df250547b519b0a4bef06076d93139dd4dafef8da9dd583c438269141cd2ed395be1e0197c6e45851d950d88a41c5f5c1d7e1d12a7e024c2b2d8980605e502706bfa1cab939730eb29a55e8ca48779b05a40c92e0dd98d6f839b7cf9c7f02865d30267abc3c4bbf9d4a4e6b299105000000000000000000000000cba611fea1477d62c414a05c26e16f732c9a4b71f41f143df36ce7cab9af65d5649b47fadf8032cc7ab3ca779e05d1b735bbfb8c03b9f69fe50f2fb81799ce4694f4070f1c19c612d2387294426a983b8743e990e3331e9950bffe25ff413e6400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000720465713d740619b563452ceecc9c30f1c93767235ae33f3e214f25243b1a1db273fab432147684dc0b4095e7a8ea4fba98b74544b7ea6114aa51e4a8f0df449531e3011a5f8c7bec22adceded3372a4d8fd72706708bb0d5d4132eaf3172b7148c79548801ef5c8c31af7b7c0f29e5131827f812dbd8d1a08c5da5584def4f03129458d4ff36f4ba1112b74564b2a0ababdbb870ac9ac10986b8523c29e22b760216d5d9bc85361f3773a2c84c9f59ab9f022e34c0963f3b7a37e995b650ee28aa0b033a09fc5f37a245c8467dce1fb3ec187f665e391820bade0ffae645f5326659883758b45b6bf8d0bc608593fc554ad8c89dae25796207436e3c9ca6d4ccb5910206b560f5f0dcfe4a1cf60f4931886d08f0cc2ad225082a0e9bf32f0dc671cfed0200a5064fa2f2ff5c93110b01cae815d0640c4c9a625bb4c33687fc827709a638020dbf9009cb9da4db19dc78f76f59a23c613b3a9f18b16a9be956d24a312b91630302d642f156b2569b8c1fd1970dd07043f57df18fd7acaba04f315f54a3eebd4c020e473608dbaee1f619f734b8acf192486d79e82d986d57cf020a83739f55b769aaf11dd4e4f08b325268604b32023b7dda3cc1d9e54636dbf32370eb92a72a7553918c7a8ec993149579f825f390d7e5f5f394593191c349fe0846de10c302983c8f8da1e86e9731d4888d52fc9d934f14a95c5e83925ae4f53d63d61c0db3af989e8762b8af943723fb3a76af08bf762dcf11900ebb4dbfcdab681dbf857a18cec8d6c0b8b83ce41ecdfc0e043e5fd15d587cdeab77db45450d43c1995d1dede810f40abc677ec2d9842c8f6d36b5b044dce6ea7810ff1269eb619edf09cab97507f5a3679979efa743ec00bf95e29cfee9cd72d10c80ae402ae4b9ef089ab9eb784ca4528a09f014c3acee73088fb1961d42eb06be173f535cd24155e802a97c66419c316f8ba08e4c785fc1e15a7b6e766c5a257e2fb936c389b7326ef105956e906e22cace69a4e0c6b902e177698bb377e4047a4784dd0864ab74940dd4db9701aa57465cd5cc89fe3ec8c66d10b061e7757f9d5d6679d7e7c1919bab977e0ce0719f94613e7b790f6c1b33c2608c1a2ac31890adaf7da93076be6bd5eea6aa2dc6df7a81ddbf6926dae8da99d874d25fb65e9fa2d40a318ad1333107878cee44412973f7d13bbdcffffdbb16417a0694e86b700e3acfac6e6ac2c8bd7592286e80a27b18e990302708bdb2dc269686080df3da97c13ca9e824982e33f36d401957447f1dc661422801168900a367e1d9768622574aa63b59e9c6eb8ab8ca84ea24bb2cf32ab67ca6f0e2223c3905a8ff0a5bd1b9141613cf4394bb9e87157943935150022df733d731f4e96577fe02dc475f39b5c74d6c282bf7099e4709520b084dfc49559f8744425c931f0cc04d0b5567d98031e288b0bc918bc0780c172fadfcd6512025e480a08eadfe79a7588455619694685d98a8d4724b984c96d90e971342c6b661ffdbe2a45ac958d01a39d37ad26813c5ff14ca14121ce56f68f9794b1fce6fe7078e18b9e5c96268734a95a81285978d03cfc69cfe0e434cee2552580c63978f7788d592501a5a5bc1d17678f702fd49add146652133e91699c22d37cda59c20baf4fefbaaecaf420db9725ba8566d7b0b9fb439acd02fc27c0e5269792a637d7424fed6cb67709371ad0a7c94b2e398f875be6275fa381cdf194bd5a8408580e38218d868f7a711a302e4f52981ee3f619d80cb1a836bdfa7d73214110a92341b580c17e4ad3c9aa18018bfbfbd7a5b3190fa1c71d46eccae1588a7f3dd305959a560ddad350006dcc3d300ef6181eb30e6c4f5e2b98f0089718ca21a65abd1c6f44d899a7e4c63ca5f87a0e8fa1cccc61fa94df17f3e2b92514f2282bb61188264ef9dfce331859ee2200f94e0f9b49385cacb2681dd6657d5d6c53e64b6d5a676426da65e6702184cba312fda1eb75e42085837175d30d7017166689b110cc977b63be2f1eccba0a2eb2903d6515d5b556537a76ed3e46efa72de913aca0b2173488d818cbdd210b11945edce2bd8d639561174e2ed34e187ea34d34736fb4a7d32d49692777caea09575744932794390c979b5b293531357c5ebcb453b6a4fd3dc1dfd014341cf482c8ae312ac32f9ea67c8652150b3821562e9c2b451a516082124e21c7a7396106278d144ead487dcead9d79ac8ad8247f595e1b2c3e39e88e6e4c93096c263337c8f7ea632e8372c2cf5072294890de91beb97280e14acb4dd9c35ee60f2b1000000000000000055f34e0200000000f011d9b71a37cf1992004109cb0da1788e547e1f1575baa798ba614b0866045e7c31ca787233353bef0ab8b114730c4f0af86933b6df972cd025976b757837ac1f84537dd88148406699a4812e6723cb8ab2964ae7d091e1010139edf90637fb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000110e79c22600e8c736c7cf1a31b11ef500879a48b68bbd75c2fa8f3bc28527c68a11a0d371597119801caef304c43469270d36c5c711ddc6d96d13ce2795babbb8cad3c45e96eb28b193c491c5407b4b3442b00a495e2372aa9198a8f2ca3cfa80eed0d93021b0757ffb38ddbc9f363f0d84dd840a10f252b5795bae82d507880324672e08be463492e542ffbca7ab173ea124bb08256ab43d6186722e8c8f36e70316ac573aaad68d0c404816f505bd1b20783438c7ef6b28f4c38731bc6f08b3900a0895f4dc8f294d42d3a89204ccde8b5fe2ef97c0c49566d0fbf81eaa33a1bb8b47b5244f9bfa38736c397f6818446a93868c714b705380c6a3620ec7f660e88c022ba0ce3d8fbb673ac63484fbad70b98949eb5c80d69d90f92cfbd67cbe23b95002180dda55e820feeb8d50d8815dfb0952c395e08d0e0b8f47a00465868c6c17380223c6819ee15d3e07df8ed3f7fd0f9fa082f3245818e245744f1cb2ed1653329702251865f6c717ce2b3d11a3661f50e1e456d342d60d75f78e3499033b5bd4c88702258d4e246c9e3647a23aae3faaaa5381b528d25ad28ecfc3080b8ddee5f6d3d3b2f6c267e47303a81792a297fd4eb016492339c97c02c2a9c4de25df6d42756861277865b29339cc3e95f8623ce1016400269a81bef0cb7b964d08da214be0fbfa5d4793c95e8449fb5e78fc8aa3ac3610170e555b27824ed7ca32901f0c953fe0768c63bdfac39a2a27c2ff3ea6d42da7cc0086149408176f7fb43a3f67b606d5685e3925f34aee2c8347c2ca3e3d5d722b62b22a0285dc1c4680c169eaddf33d726e19ada646bf1522fafa509ba02589e7ed4e76a6ff7841da0b9ed10d3cfe195f9104d8f4eae65fa2f912e7f2cdfdb0e3f3a72abba267ca655342e24bd3d9b46c85bf319141373210f88d379259ef6390828f54c2c3f4aa991df8e6b91821fbaecc1950731fbe5dbbb51a62674947a365acfb4be70ee0d4020721229d7e992626476c2b9eb732ccb686a654273e64be7f0a1b03fa2c553bde5254c58093d2b58df148e27705a71f4fa9295bebd1d295b757d12644bcba7fee43bd67d0a3494e076b49af98cfe1637ba8be749b32491abad3283c8b1d0c20a1ccf47fe858c7b10ff5fed4014fb17bb8dea5bfbc66709b2f4b62838e920695baee158e7b1ff0e4aeae722ab70d79adc599066fdda6769f93812283e03687f5a29584d07872637a768e1d8ee71ef915e3056f4ecbbc4588831d57b5327bf2476bd6bd75950aab5da47da5732fbbc277a2587a74cb4b1edbb97c8e47333505bf113f1a336ab85767d7b43ccbe545bcfb94eb05d315d6a578eb2b63e7e7700913ea9032d94a330463af8a521fdffbfb561197e6fbb2bfa84a47cc0ce63c8c709b07b622892b328333b6b9635d2e49e691f8e41749f1f1b469edcaf187960443b2273df5ae41a3b6884ea9168a735466d9f34d071caf999d4bfa3c07a8eeb2cfaf15b61caf2ce7a843ce52794d5ce55500b6ed522f1ab75bd78e926c47d2810085c1542c2710bfd35421ab72fca8b836cce606e33ad27f00a977e3fcbb22fe618d3993101d3dd3b8ad91cbf590040e0e48c72ec2ee5066f29368f3af50262212269fbc835edc752fdd707afa95a1ce5a9e17430cdb18263654d61b372029f42fb5d7a17bb153bf7d71f83166c6d63368e45b61feb451f4e380f8adbe359665cc3d3c3b11a3ea59d94dd6e043c4eb4f5c5d2f53f1e562d2845560d0c6bdf3ef945453cf74003acfc5af36bb8afc292846a29119865b5895bbedf9a9b5d6677e47f9508c74550a94ffc860e55654fd23b13463fcd2174a2d31cf1ecd115b7a4b8d43561f8382ebe1c4a03020907e0faa7a95e9ab4174a6c3e203566bc585a1c57cee22897cee9966041964cdd91414b8693137e33651c09272debcad073e8aa707783541bcb7974541c23f3e7d00b9b6e6a7631b51f7a86f3139664efd10e124657925e60177bcd0a32e86bde1ef2e15d24c324f62657b1e95a2ad50509bf371b83e05abda018b3cd406e8fa60729f6c20cd8bbe3b4991eec8e1d80c875505f6bdd737fc047f2e0cd6a0e7aaaaaa812c51eca5a21a38d6972ab64a77f098d31bd0d2e7e31712654fde70f45d873a7cd04c35a8f41ed11e2c24b79af365036893b9552585fe9c1e10864d826483074991cfe8c55622a5e929eaca7e16bfd1073f87090d0ad17f8095970e1b99bddb69488396227ba7ffca379fc0ca7993de48e95a6244addc736c145db469980933a456f76a40cab8c0e98de221e58504b467d1e1fd2564df64f0746d57c7ae0f44c81545c93bfd0c1706f1bffc7b116f3d80e588125fe9eae7b7543ebd410947c74d4caa95b2289e8c547f3aa992f3b89531c6c3f39456b497b9fd860ec4e84fb02ba57f3df0ccbd0293be6d4706", "53", 0, 496383266, 0, "1956421ae37da37631b28531b7271195211b54f46c680111eaa3db5238fcf850"], + ["030000807082c403027e27ce690cacfd62969f1a2fc6462cb12d85729adac8371313b774c57bb6e4a9020000000800ac52acacac53acffffffff67fd9e0fed0efabd4a8c39440b3cc97bf51af10549e2e780f585d9f47cbb261b02000000015396fb6fe302b1b50e000000000003526a51bcfb4a050000000003525263760d959366a1f09a02000000000000000079fcfa040000000017720928d781b5db9d03e1b2cb9dd91214d1e7591d19395c77a8bd406f3380094983e88b3e7c25ba444c154cb251b0343a715e02f6f873c18b32e8a88fe52577457c6d2d8177718529cfc7f0af72acf38aa1e57c334c6553c014c5991c1e026500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4f73f03f5ec33da54bbde1bcb2345ade5101ef71d51a15084e0e8b9777952f8b61b3d2adebb83db20854d27d4d1994c629184e48822593b9cca02f5e6b42b6dc9c6a62e04ab203507dc38537a2b48d1f8e7005435120073f9ebcc73623279dc53ea47864ec39648c274f36ee738b093a633891811ece2b0655fbf88967c0226031a40080564a38c2767b362cad27cf3adb2af3ebbd1762030a60f360de71cd7ad031186f02b162f6019a5fc6e6ed387d3d33298f3543ab6d911451b55291db0b8ab0a022393634c8296ae7198b001b73b87ed487d702e78502db9be0737461c2d6677efb1106d36a6ef46831b087c4035a197cb5df8bf8f68a6804ace752b7ec22874022077e7200931cee9ecb159b29f3ed95148e3ff0fd273455c84cc0d7a5ee3aa440327391b4ad3187c4fa72e29354b45adaed2924c596ceded759285edb2e03a83aa03047f8060094a3c49d3c1217a1ba723dbab5d6259192c6072b5fa21d31e720682030c91473f9b9fa5fb98bf7132f5cf37c56e12aef0d0c862ec40bf4fc9798042b00303ab2bba149415819b836f9f28b6de1cc5b03417af709919b4e60fb5f2d064ac57bc6da4b31c410e704937e1ca31c7cec8f54008211e19e1ff28eaac2aa8c54e8839298e9a4b542eed3bc98d41a045a256e52a2a6b0c1cae1bedd6e87ae58a80ae283183601d1d9087a9d95c0d9cae98ef761f15866633d34c930c7f20448a0140b28d49e84bca3ae33902d0e1946b77e1368a068e0945a6db5e58259d9a08000b72410fb659248bdf4755c91dfc4a6144b118901d9dcd79e3a299d88430413af6b7d8704b53d30e33ddbf22f87bc03464571b86d8221fa33a7914e1ce3b962b406e1afd7b01f58eff6f357c2117799b55398a8f49749717ab2ac7385659cb2e252798fa5b65a51b5dbccbc852d5bc309b1296229271651b74734e80bb0792030154524875694fa0d122288e8a86f2be405c664e9565365f703808a69b189b1a05a70e9398717e8231cffa2e8ea4f30a177ff9d041b5cc0775c58d59b2da9e08495f6a9e127c1176f3365c631e9dd31795b19daca5366deaba46c8e615eaaf0d25d9e39004b42f165e4c6d3f72db0215c7e59e4aa67a63e71f8eb4b651a73802a09e73afe2b2b94d813c098176424d3485c94cf1aa87b500ac42a75a08151d2435f25f79f43b0fc028bac4419c1ecf29ad9c46620ea5199523de48720dd711000e0161457e176f8d7cb86b3345e01af7ccc3068df07f13348b28170bf2ba38a85148c8ecd59658d21765394e1850be31cdaf3fb0a29910c4f181dc31cc12045a89ef89b4cafe7a9af48a75efcb1b442f6065f999e300018385e5f8b715aabd01dcad9e4e76259c9cb79ab80bee2c4d19f2e1c97636b65866253aea2c91c49a5faba46e4960063d24682aa473126827a1995907f89608102b7ab4f2e76d260ec4ae91dc46c9a540da1e4943e7c1ad56e1639c81530c679b9a66e7f1f348e178db1747842e330e2c42914381d95f0819831b3aac110cc53bddf32d7981f6310cac6bf1885aeac3e22bcbc44833c99367590fa7ae1d587a02195d791b1355f1f8af186f7622e387beff03b1a7fd697ac4649ca744bfad7642282302f6b9b9dafd89949bcf4b320e323b95f4a59f21a6307a08698a61a94fdf2f22d465c7de13d2a6c940cb5c6ca71077aa0db3d9227f7a67c61d07563acd236d07bcb329aed3147586e285cda49ddaf6966cc1f4e80234360d138724f4683a93411f280a0f05da00ca9b1717c2991ea585cd5963b2639b780262619edb00f3daa92c749f6543a376928f6f216ca700b47c1a8c51e7425e09d252fb8335c5b18b5ddd81cf04f97604542b7418def4462a3b27426f3ef0392683eb10f2b8869abeb931b7e747238484ca777cb54145c7c41c79960353afefd9dce3b6b380578751a596bdd99a922ab4098f06183a3aa4e908c64cfddaa88b3d38c916810daa64ca9df3d352d73d52ea85dd280adaf4857a2a8475086247a60e926221b20acf2872a3e47fe73fe0861afdbbbe4c325c700217187395f150ec3bdec392e5d92ddb6b8bfc70953200c2a352e624179041be0671c53337446c51a92cd2f16f4d9c819c33a3c2681a0e99c4a52be4c2f43dd1c7fdb5b86d699ce7432ed807703e826ad86a29d2b15fd74df5d23922e84746dd7c95df786c3c42be74536f1dfd38dc3b935593a0f23ac534b7302fc5224b7c9b83648adebacdb25c4c0f493f01057fa78ca89e6e6d38b83f21182adbd4810d07a215e262e6b0713161066c78166201000000000000000000000000f2c2c1e30ce4243e43fa2ba0d290354742fb1896f0414bc60e3f610460b5dc8ded899760f18027c162281be1aaf766708dfb941c6533ff079626da5502aa009a47fc1420ed9eacd69e6ea89c6880bf44e781273ecf0fc84f0d08a6fa1eb509e800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a843694a7e48ba3023bfc62046df84e3a7bcbf90071276c18fb8c942e43434ba302d16adb4122c5fa34acc5ce12eff8eef498c6b528d5e0b9853b1248760ea98e0a7bcca4a26584d1e6382a72d746a71695a743037216680ed76d022d251a57f4f3caa6ecc37c7b08e9daeffd96fb318548e542fc26fe09a687ee3a81e673c450226703ed5dad32986311e49089d0ec27279a88f8251eb9394faa7e6d26d6e90ab022f7b4b28d8d35df5b6b45c84e363f974662517214114d6221cdbdf0aecb652790b04941e02ade55d7bac4af2db7436262fb5901fea1ac107a3591bae4c7dac080320dd4c9c4a2ddf89245747ab4e9d36860544f33fadc4044f7b470ec461048d620301529df588318f85b82984ae81c8f25cad60dee5d2ccc786cd903a911a7eeb200303becffb5906b06c72d09aeabdffff57aacad843eafe87fcf18668bf8b56b9b60209b7a11e541eb58a2c2d99e41058e4cbb94ea535da81e98e0386b7e1d2964ae602242bcf0340bb5edbf71c63a64f694968e2ff1db9b166494caf55d5250cddaa1d021c56a4b6826c0fc0f1113ee547b6bf860b214804f5e6ea46f95cba12c16fd7b1ce9764fb14c124877c8b4eb778f599eb8174c428ace4ac730e6abd1ae4c42bbf00cd37f55e00ba9dfa1c4bb2e6e6c52b29be0e76e5942da6846701d48526788b0cfe1e1b0046bceeb5dea5dcb468687591fbf2e46b3db74608165fcbcf564bc6fa9e3f546766b199896f09ee901e24fc53bab32de2531e9f117cf6eab60d9d19662914d0ce39b2346b9580649983069fac63427cdcfcb093b43d98c6ebd7f7d9d567f5d99c81ed1562a752c98855d2ab53178cc7a017626756d0d5dde07cebd8cbdb195dd819053d0364d633560ec9da42293526199c2f116bc752d764e858f609f216417985ecde6a4452ccfc8cea2f0f3f122c1ede58e4f2ae22628b70fbdd078a3eee027e5925ab31e9a706d71120d8ab64aedd4d531d56e3b9a8f7e5f53a8ab7f290cb92d103e0c95690a1abc7240551a037d10b1630b07ab45e1cbf20f2b1ed643236e5dee2a9b1c54ea3990aa35981d84ab5e9680b32bbd8b7a11695bd29f863adc0d2be4a8c462ece35e1aaeaae1a6428521c6276bfc488342fdcec88475146c3c0c4ded87b2852c96783bf48ce56de65fb0a73e61af9d6b9eb2fe834ff45e3eb33b94ea2b5926add019a95de58e8ee3f14941b914cd61dc1b2c9012ee8a29b5b94c6acbe3e5607657520ab779297b9e6874fb69aeb6a1d7ee53459676854862fcf611f30af94e378f99c30d264d1443ed64fa38bcd2df17dd1f8abf6d456f50de7a876181db6d7ee297e465c8459b9935378652c40b5dcbd75cbeb37a8825c10593828116eb3f0f8b250a2efae961a1865e3459ce092dac3418e6de4e82cca5cfbea4573067880b6fbb8064eb22b7a00d7511546e0bcbbe4150c20f17dfaadc55931f641f54a77ac4cc3f6fafff43ce5672685a1bd337ef22b8a5f5e759ce3019e9236625f78275869d6837e80d820d513fd6a02400d4f55a656a58a0036af7e5a0533e5a25823dd5877866f27d225bdbc6d1e22b0160b97a0fe58de6d8d210bf96d691dd0ff942b8a968a87260ed38566f733f6e816c8f659d101a59488077d31331d752188fbdd38c1af1e64d8ba184abc6294e856662b3ca135c59afba4c992455eddf8b4f48d9619a5fbe269920a17ebb648cd38b3ac7e2c324a4ae3e4d5c645c0f6739a70d5d5fa2979aea8440b99545cb042983d99197ef1843f63ea2d04ff9c8291d310abc7fe1795b4dc31532eb77e52e5df781a0a7dc417bb9e9f762ba8eda883619fa8f83a63be976cb69360c72b810e2652057de9db259488316de239b49631708ba02f3f388e97809af74c8a99a843d4f2e8d7b6ed19812344e1e7e84d6a8cb283eb7aa35ec4d62843b9297373015c4b0f52753bfaa40e1437d03c83b3e1ce9cc21a31c45dda2c535d2027aedeb61109240b5e15a3779abd53188c0aa61a0fbf1befd564fdbeb7f12c062e957abe4162a1872bd1eb2d22c68f29f35396571545e776e1399144aacc66f786c2c3a041afdb7237a76d13e0f1329c8ba0e947b7c21ee9acd29f6b176818b14af722c9814ae75e62521da3fa86273d0f0db8555aaed0faed38cc35a7b1cb389c7fe99544fcd8b2b1360b73c0b9e055cfffe24e27f4b29252d2b1dc9b1ac57c294efbf541735821970fc4d47356971c894eaac9aa72a13bb705ccaf17b5cbeeec6d3515d8201b089e3e0812ba1023369b5e1e1260f7b618d9c72bb25365afa2cb75af11ef6cadf838b15b781d8a3e749b5112229cb0e2647d16986a5275cde90280e69411c8ef3e3939421dc453a03ac50777063881c6c77ef0cc9b65634ee82472a438f42cd2012e036ec11fec17a2032289d417aa9164bf8b41fa440c", "536aac52", 0, 375685057, 1537743641, "50480d5580e07c9876fd1c733c4464ca52f305b7aa04517bf443474fee42b9e6"], + ["d69a4c4604c912ef2cbc0368f8f8fc2ffc4ebfc57677ee82cc31f20f0d431a3bfbabd1fed4030000000453515253ffffffff62873aef656d82f6741a77ed346724dbb25d58c2e504259caed491c57c0297f2020000000653656a52ac00ffffffff28e9cad39defccf070a9872d01d2ed3d8c0010a4059277f86a7017c16c960e200200000004536a6a5294feca3dba0dc56e7d5eed8afb797c62024799841cfbfff583bf239d7dd194a59c89bdc50300000006525353636351ab906b46031c6d44010000000007ac5151ac6a53650d9c0b0500000000076563ac530051652a9d4004000000000353526a00000000030000000000000000a3434204000000001269ca99d03508c7722f2e8322c811108e7c2b2d343f079af363c4d227d0120a7b5871be63c79634084212613cc7238c556eddf71df249d655d932c3906290ba4348365aa5650ede5f9d82622c75e2309e212bff50e8a17f4c1ce90dbcb10621000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f0cfa80a1516eea4944b85322c529f2d947339977291b3645d378e30c656e89fdb0d77a0817ea94dff1727ced14dc480852ef999f279e289fb2edcd1b15a88ad33e3f2abe2e81126372011ab5bc3506e6d3b90afe5fc4c37d6a7d9149c8a600b80981d6223200e1ff7321ad91397e27d5fc0eccc9ff916978d166b68c9ee845032b9214eb75ec1326e2461e32bb0f68bbb17519e05c8a8eb3f22525184cb9e47802099d1295a8630f50d99d4cf541fdcd6c3371bfe1110da96f0258e86c4a08ec360b056ec2e625f07564a397773f7df72c218a6343dc895a2f459494b916f4be95530faec293c7fbca18577f8dbe70451c93242e18a74fd949615c3a02bbac95466803029c191a9876873b9bd6b308689e78cf546c012e37f7bfd4a3a1b3fdaac9bc3a021a6c21220e6448336b1b3e2a7cc429c4bb5a9a976862e99b32e77407f814ebe1032e85d2eca1b15b9355d7fc5a207bddb35b04308bc5a69fb497b90f29abc2b20502125035078c762fdc753410f144a3197fc5a097ae1cbcaceec0588d7bdce6742d0302e6b6db7fffce818693a4d525e36ba6c08994156669fd69eb2fa50bdd304e1a5bc8c2fccbede6613c2dee00bdd1eb60018a18c428f407172ccb02e026ee6bb533b75f84f2c3da1b67dc431e916ce76455bb46d8c0d296234f2d5b85b109c7eea0a7027e479d92ee7c2a49f63667aa3d0b75871b69b907fc2814ebbdb5e2179550c424c19662344c7d7b57ec1b392d2bb99ed8af350976cc76c1528233b8cfce03ffbf3214027b76c3dd41afc098c5fb48ec945b4724385209cddf165fd715f5b97984e2cb26f1a2a92694767d61fcc7a51237868ad6d8e3e188f0e88af15171f66ff434e9e6f9cd871a9bf8be237958393aaf2cdc43e722d3767d9d563dbd6aa6da44da780fcb87163dc220d104c470367c9384fcb1d106f40622d76dfd5d6932123132cd182c9ae434895ed00e05f9f42c8c95795aec2eaf7a5252409b375faafaf8755fa5c0e8f1a082723530d71f45f34b6f249a6d82c369ec10412120d2844448933fc725c7f669ee21c5793ede4e4a70cad7756b4fc4f8806b668611f2545cd7631a63f77984e612584a0d759a31e57c0ad16e794864683daeffe985c101534d62f932587fe4c9e9889373b8a9a12cbbfc2a782868ed288ce10c7ac22371ce82230a69120909f671516c30802dea357ac8e36ac131406409c67d7a54a111f86a81d18af2459e97c489bbe6cf7a91ed6d30b0097866c5d049184ff96bf8b9ed9a03755f602e7b75793a8859b017a3a0f079770cbc3170289745a903251ae7915ea8953877de3e5373b5e787a22898f7325902f3393eb653296bcd1b63aeb1dc6477400bc4edac6084d9a8cc3e314f324b6bd9735c0faaafe6acb03acfd40156910b23e4bb558e7ced2de8e4cde52950e3701d46d136352e74f2435268db8ba6196b55b2a37b23fdbfe3f4111896b50521c21302fe22cf7ccbd8eb24cbb8b6a35abdb0edba37c0dc8273e33b28fcf2a01d9ad37d768c0df9f4f534afda0912e0a9a12b9c241e48a3e6bf71d87045941d5f77ec025df888d2d52299d3c1b7d8d989c5ef2043b0354fff7468b79e0a6562febc4ec62f50e57295f23e1ea4160a9d91f1e9d8a5744b1c1f4fb60915ce2cb975a40e561f419dec3c38201e01368876bbbaac39233569a94a81ae92c746586f8a67c446e36480deebfbdc360bf4985f28d950324cba0e2b8bc60a614b8aea375f7a2e2fddf2f7e4eae75c616c2966ee0fd88f684cafee1fdf87d28ecd2a5b971a43d9789700b58b525697500933ee606baaef873909affe10b6e9778c70a06c891319729ecf4d64922cfac0c0ce94916a268fbf30c3b94b778d31acbfc64f34a5acd16bfdcb4290e7804c206c31c168e17ecba438786a60de3f37c555cb5c856d0e16e56cdb19db43f5cca81b4f261d952420da2528ebd1a1bc9391a57ad38e13d4b22682e3bb2bf31b222c97e3800327e080ff08e389396ae8975efece0b2ba54a066d5950a6276552b9890df3cc8e8229887e0cedf9a218ed2683a06f204c041557ddd77029e64622bf75de61ef2e659092e89e6d97d56ba104f32f95d54e59dd7a3c77d77b1380f0c1e038e0ba6bef6ddd4addef4ea52a453c3a089aacf12abaae0def001a15bb4b4f5d679959e7f564e2ba9dc7771c66a7cc7ffcb92daaf6ffe4156b8ae463867b0256a24ec2c2d8482947ecf07120a354eb9748ad63e4008acdbff0daf93080b578dcda8d167150f040ffcf334a6d95a0f4433919a164a2b06f020000000000000000000000002f603e6922acd095d4ce3ef2087d759fdc921ce40ffe35463767c32b4d0f126a8d4149afb2c7ade2eb06283f9a16b01665e733685414b737f46cc8f289987f96bc684085bb1ba88f851cac708f3ca064988bf02a07c5ad69e3c2ac5d362d7cea00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000549c6134bf5f5f69bc86fa4451770417eff7b491f39e1f8da0106eb4b84dba74a3052736cca9a89cd74c5a420bbe61680f19250c2212c16d719d83ca15f3552fa7daeef7d7d5770eab618acd6db5e7c427204e033c28c47d61c7d8cb642ced1c1215b8c6982dbcec34538293c32f717e5b32009c74e912d23c7c48de9aeb1d51030297d688dfe849b72177c95475bae2de6e3f046b2662944b786637f8e70d6cab030c865538833934d3a868d3a748933fbc2cdd06ce9fba749a8429a084992c90b10b0632a9b64b35fb6994ef09956b430632770421a65700d3ac57d9b386f4a4f6d99b80ad283ee4ce188d4eb332920ef63792fbd608347a43f0594200ab34108bce03112f86c4de1aac84db6b464de740593537431012dfe3550dfa00e5ae0b3cb4670213d61a7185775d668a42de6fe6152f06c4ac8c2f474c7a69c8de0ca87725b61a031987b2cef5aefc949b959a8c216736c9d710a37d1a2b4933b4ecee0310e172d3022cffc7e2fa7e17faf3bde6928fc4bb2a22ee59698b843e746c18530f3d2966de032befe604bd49f825209f54bce19da50a5768c8812ff84415bff27010d9e54c39bcbcbae36e8b834a4fb04e8700f6acf98e198a91ec9c64ae93441fea51e92968271e5825a9496fee56ead2945c0379dbea9300da1bd3385b6f649ce49f32beb06175b90badd3b5a2f68bc72f021c0590b15ab46a025c8ac0d03f739627b29b5f235d96576c88580d242286b2bdf39b8ee0937bf5c30a2ae30f5c24f30a59a765546bb5acc515ab40d1d513695c724904a1d7215599b4c49bec66e44dca24e4ae74670b9e44437c5326d04322b58be2fabe992b8e858dae36b8508bf56c688cbf7805ccb19e293438eb13735b9a3f7f15942d4d757fdd2c853b00341acec05da1800e46e92be8ac9671030593959d5ec80dfaa8832609d01dcca6a062665eb014cb66d896faf78447af6abbf89e828fbb4c2aabfacfc2f58f3ff59cf3a2346281dc931a41e5c1865068ab19029af9217d931eb36d293b2d5dad58c65808ecf852be356791e9a46825fdf47e98d42d20679432bbb672023b308f566759241444c9e18db8a3b4202d6f126a19f82cb8fdcb473e20438d1996ba46c97bb85670eb56553222a0b414dbfb16f86d428305b7d04c295fca54fc58fe614b9ccbadf0fff7d7a3d268f59ec962ad64d7a3d3d663e5e9fbe16bd83a9c0b3e2eadcfb64cbad75b9e7eb13b17005661340deaed65940ffacacdaa1b5698be8716cf821d27ce56eaf492f246a1119f97c0daf504aa0cc0fb27714665f6a08275255cf5eaa0db242d784a79371244ed75e6412cd29143562465649b973b8d5b15944dce1ccbdeedcd6e8343075de033b07ffd5654c709a0dcd077ab9d3f0aea61cf9a82b9d05d4fbed34ddb5d5b31824cf7d9cb9c5230001bd7aae78eafba0a21bea035c7e0aff0eff2386465eedfa98273c19fdb067999b441e353c031a68a83953841c97c4ce063de15ddec4eda3fcb8c6bd06b208ff1e3bae5de76deb2dfd0e51d97c9f8af62d0383c99bc042bb748da21dc72af22b6b778097cbb496183ee07b2275410ddb4c74a521c8934616152be373aaaa5ffdbac0119ea9b42358fe075e601aaf45b23046e1464e299d29fd3ea3eb795964bd5b3b17309798ae917a50fe26d72d420ec8a33f5030627e6541790236ecadb113506b15554cdbf6a1c8bdf6eb1da3aa25b28d8d4d8cb7e9d8536956dddbefd684631f4aac7f7fd174e4297079fc77bdd20b64e15ef115fd1b6cfef0c0075ee8c255748104d1608c9d8067eb6309c9e6f1b60c2c3a00cab30732cc4c5d73a04074a427265c81fb86da53358ffe87a34f1103cb868783e33c19217cde933763c493c90b5dce429b096e277475be43145d1a6f04c52dda5baa2dda17b6bce07b0539f9af1ba51dc35506b765df3b0a89668162b594dfd5a8997e97a73e7f968ea33b96341b3b94fc2d55be45090e3d1e3e66e2040156f4166a6a077eb2ebc7ef5520e051b9a5d129b618d7cc42bd70cb599fb99028b47dda3bdc55aacaa3172a6825446d9f167e09db2243fca66368f58c9b59441328471c86cf822bf41809ee22c079339ee430c2a101eb2a3496082600ae98c7e6589bc1c0e99c4459558a0a7e637f3aa73521dc37bcd401a261754f9b6812679897a4d049c9053478a98d66dd2068cebaf1527fff1a955c8171896b005c5c9799d1a5b3faa0b7e4d5a6c7cba6af537742ba2aa1d6f3ac64f0b6807573c504f6b5d95b2236c3b15dab38d2539465bc0700000000000000000b099400200000000ba565002e51c454d628aa634d2b607872d282e7b9ab80e8c5101f8e803af9f6a21d9709e4e4af8f03c0ba593e89dd3497740219760cd733122106df930e628795f55cd02d5bc87f57a3b03d5ea2d1250fe25dc7e83b68c5bd01aadce2534f31500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a3d8c253ce8f622888e1bd79a4fefdf292d7d7c9323661712b08f553c347a6a63d808eb9f98e5060ec7ea7433879c59ad8cf6a8ffd077a3621034d8b2b9674b32d99f6ce605f8c02cad3f218bfc0452b0d903e4dc67aa7c69bee91d93445418b3005f03973f0e899fbabeae15ee2186436188e5a6a74b7d17b9fdf5bdb092ae0020016e145ef735fa5dc8e8d5f7187653fa5bff16561c939611104be817d6471dd0208b7f2ec3502c3b5f836caf2094044de10100cf0742ed342921dfae447ef60130a02c09b28f1900f4b96b0758ad9bb6ff93de4a283afba75d94ace54a85f47d1db6a7a562521b319d43befdff44036338126b2965c52df4fd33e5deabb8e221cd90318c1fdfdeb5d4d15d61047759b8dfae62a641395d920f6cf8d5f38d2a4da2e0a031b8e5e444762c3b05c19a95f73ba8cdc2cd7378f276a889aefcb6be5fb5dfd85020bbb71794b0136380963e0dde50b4021065a1fc62c9f17634c9e19d1ddd3e7d8032d08db17f3f4f0ae45ae0da0ab070188ddfcd233e670b3503d2b7117b86799fe022fc581259473bea87c2321ea68ca464857384f09a1407ebd97ccebd3cc1027dba553ba3598df43413ceb10eaf4d21370dbe27da1cd9c25787a9181f26a99dcf2748f84eeb20ff2c152a134e063b19b999965cfc1e105f887ee67e398c9050952de993d0ba6f0879f3af4bed4b780dd87231bd86fb3856d2e67c3176f786bbe6f598b1490f95330642f6fd97da153aa1ac9fb6edd8300ad6c5ed23304f6b8569b237e2432d22603976759617040a97a613ff17d60d1da161929ef9076dd9b068c06aaa6870af3090955788bb152aff75d92431243c1c34f099ca29a778b6adc50641c4422e49c14ed0dec143dd8e7aba25d2564021ffc2785a59b6383d25bcc7d02cf03a43c7adf0e9239fec004a9f5b17460e0e7a7c9bf6f7a5bc45b06335735c59ade522c89338f40d73647d4b4f5f8d86676bff43756f8e4c30672117dbe235aa108cb48bd873200a3b8db31e8c7bb3109e2bb3a293a8f663594eafce5314a237c7d7d6565bbaf79afd73f772329a50b4550aa2e1d69de05b0f23ff55a96255ac1833dcb3449e8d81c096000a28f35a0e41e75b75c635fc85d2ca2c6567b2edc9a2a42d6c92585c2a985e02e4993895b0a661e6fe26f8eb4713f04681ebc0dd9aca6eb122bf6b266820a899e9dac1e634abd97c18af846d1fb7f6186b0d5d429565668c73cffc2cdec5cd413ba3a26121ad93f5ce3711d92cc193c977bed8baab03f553b5a8eae376f2121dd3c66a87a7459967c629fcb32534892eaa666f034c21507c9a30b59a74ddc54fb5eba64d1598e6d79586cafec0c902eb0369c1f1b91b4f3cf97263cdd1ef0c32655976669868e483084fc706b9c593fa0f2641409986beb6be2829af11ed4c2f6608e441be7929169703ffd3f46fcb8cb9a583cf644541fcdf015d21d5267a78792dea3354944c28c90c374dda98ba1ca141d0b69a96900693007c4e4ef7b2f59dae4c7a3b374327fc7bc9baa098e58d5581b62d18e3ea03ea43b7af84d4365241418dcd91494783d5b4f39e71ed428d990acb92d21c0fdb01adc56e5bf74d16592d1cc0ea7a1e90e8a303958224dc1783edf268bf1b095a0647429f30c76e3a598044fa79c0f8b700fceeaacf6d8cfee715ec3867fa16171467ce7f6e3e23069dbab217288e555295199171f31033f273d607e1bcc5676f092bf8a5b9a0be6b0b34bdc60c4c914ab20bf0f0b9b24db92d1cfb7668065a867e0cbfa312f7e56a582bbc30304f94b287d7b7c7f305cad955c90a2dd24877c13d512f28faba33bf9144e4253b40d6b1018b62210c82a978fa15d92667278cef7bec24f28e417aaf046fe4529b6fc1120c6f571d3d9fe6552f2d750ae75a3be94c5d48b5b6092d8d16a226999cd2ae110334db8d17677248789d49da74870a35696f2e85d6e02977726035390dda81537bd3ce2b6005a6eeff93293dca8c767ca56c2b41f420f419d8ba6b941c8f582594bffa167dc27112bdbaf5dcf4136510759c86e54979ec8669ef384e9eab7ea7fb6fefe99c858ef66185dcf53b40caf36634e2bdda18eb42692675160ee6ff7a61fbd9347349210f910fbb08d526d607c224de5b2321a015e02ad5950ee3d36faeec53ce6d52b0a8dab0f3c6d19d27b2023cc8579539193f4538f32aaa794226c3c311e43af87a78918b9b36ccfc67b53cc935df47c6a155fb9693850145c026da718ef8901165c1cfd41c4d5c5a68ee7b8e9821a18b8106701b2de7b7652477c5aa2223b1b9d9ac90a2fec4668e583bcd386ff783b831aacaa1b688910acdf1cb596e0696bd3ff43720ace0521df9acc96f64e015a72e1f7125eef15ccd68d2816f045f70a1a0e752e3b8433ff1ee46850bfbcdc59c18c388e012cfb0d", "536365535352526563", 3, -1373976644, 1537743641, "6ca350ef302e0c4b5b45c110758bfe326d415736d325aec3f9bd4ccdb91a615d"], + ["030000807082c40302e3e16858f82ddff037407d2bd2ebc5ce61ce982c23c86fde6ab800fc2107cc0402000000056352526363fffffffff60d9ef254167087e70d3b1ba8c25563f898c8d1c05a867cd93e3ccff5d72e710300000007006a516a53516affffffff02e49792020000000006006a525165ac669507050000000007516a656aacac000ff4640f59133fe70112132503000000000000000000000000ab60c6206764ba77e4f58b79faab2e510ec016d056e0104c20425a8954a26ff5c3b5f7f93e1d54b7086ab1aae4f75abf4786442772e87069cde4d7fa815b7a42750f9b3d7ea625df24925fbafba4353b11e0f6d6156f9d0021f6b4b84c28cc11000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f8f928b328ef50cb35d9b6954e77259cd8a77a9907689611a9743117830207aaea058a5a472019f2ba5844b709f8010d21ff0c24b56f1dc6110ef9b2b42d5a0b693b61d8300ee91a4b5e3a49d4e3b003e19a94a83c17fa6e981be29faae642eaf8807ef66675d1c5452e16acc7a807932bc3d29abdc05063150aeab4f909799022aec3c007cb2aa249ed243d27b79af3653fab95a3d3f2421ba0a769ae3d4ba7803013eeaca9abdfb57a937a859894fa9b9967a2f02abd0dbd3fdba454745931cd40a010e7e87b198868ce0759e79203d904d3721348220ee0be8de75372cfdfa8aedb350474c0abe41289c7c9eca8a5229884ca296cb8912b3ad9eba3afaee1655c20326f78756a7efe981d2eab14ed2c39869ea824642a6444b87019aae0f4e1d3d53020c23941d306fe413175f653d8de14eff946744fa018434b766b0771828c0fdce0302b65190029badceb270de1245a13079d5aea024ecd0db9572d724232345399d022155bfb3b49692160fbbe6e97994a6475cdc7ab7ab61f9aeaf30cbabc54752e6031f238a15146090c0760981daa904dbbefb0e35d06a6250c50276be89905c82fc1fdce3740d7a99069d9b2d0fa4497ab494d7692c2c5af8beffd772ff0a4e1101d79b120ac59a2caecbcdfd97a1a6699fe0b690694f942ef81ada5f130b2aae6c80da554853f2bfbd463819bc017cb1a7aafb7e855fa548f5ca2a23e56d96cd626acc8bfcd6ba44cc435119d58c289367c2fb41dc2cff3a265e36239e3102ad269a51f3c359b563d24c2255bc3810e7b42db3e5d10d049c174f0243fc28931121770b70af9807829306d34358c4728cb7d12f7f9157c90e6132e11ecf12cb15b0be6eec45b89ba1c260be8fea474063975487030c81b96b23414df1cb7dc742ee46ff7bfde17df0e75685184366fb0cab475e31612fa7e5855b69c69ee8af00a3c6e0388a448a86ecdd0d8cf474beae84da32d5b4c51376d49add3f4f24dd7d0db5fba13f21055f781b76a10da484dd43f22002ff2b2f18f52f5166c1e6c720b2422ea8441f7b5003d69d3b047d948ded8cf7ed8b4cc26940e3f88d5801d27dcfc995a28c5f785205248099c1a860bd9cdcee2d96d87bdb507c21d8a5130b615ff681dd2e8b6ea3146e31042d6c8a3c79a5f2256d6f3a933fb5e55259778d6dcbce8202fae2e1385ef3bc2d8e9df45d6b463c9a3659284312cac7223b7da4ae4f1d4514132eac42dea7012d0fa233b1456abb7018f607a24f121aa6f4f3f67c58190660e272f1c7372ce92b287abc358a2f2eb6893b48b2fa5dc1f8e1e596e00d670c3191513b06ee8919ed8b694513f61e31b85cfab2c2885cfb7584aab9900073ee5ef6959aa55ccdddba9d1a6ebf36c5bec0b02056b47d84f06b389810c9be3e6d3c534809f42d62970569f138650602689ccfe815367773fa3ddfd1be7893a3a09e967245a7b87960e991c77158ad7c497250a55ee6c142174590e07b460ae7a285046070001d6e11ea93664501e56b46d6e5b557aab966512641528fedab434493e259a359b94ff26216d6c8b7a2dec3dbc9edd6168e40e5e8e15596307213c561bba915b78d405d3238aa5b4d1b4a3530f2d4690a945fb6fd91d70958047770250d5899424467a32dd231bf3512181908f3c0567ac5d125cb440129aaa96824f670bc0e9ba112f32cc0868a8f597b8d7680ad200ae3578ea497be6a9911335fc30512e15c64faae7a681d69ad7fae825eb84e8383fa1c2e3a2298e699c86994c442ede0fd9a11f1d9f8eae3ae55bced5f5f1c5b270d332e550654716d211bff859b1857facc8af777a1abf2913c40ceb524827a987c33bd98f64edadaf80ffa67b6420f90cecf6aaf4f68bd276d9df19e98df562033b96cbc3eb84637f905509fc911efabd5f0bd0d21a103fc3dfbc1f08c6da6fb5910c78c279dc95acc8df32a6837592014d05a2bd4ed6848a90b9aed307cac38575e47387496bfd2d0666ce310a1a487805fffeb2d5dffa0cbd472b48f8770f9f330fea888943e585447f0645f916cd0e9f774d129077aa182e9af06f8768b20ac49eb3be650de4a5ff44c1230855482c6a6cc34e675e84ed9dd1eca5718cbad0a004e91798f1a82808e69059bf1bf7828078ae21edff54d7a7e4150505364f86ea3131a4ddd15372af3ea17ed4efb06064ee6f163bdad0829c3510b56d95c94763c33a078beddff11859f89bd38bdcfba47469d621be96baa346746408dad8aa25d87a19e0c3a7d9d106904a316e27ca157ba8d6048a994e53906a836f582c74c30f0427c16c6a15553f2af5125a30f26db3a91f639762edc0e73f47005a4aad5c8c969f9970d9fa9e3e1195e8e6df84226fd9bf14dcd6e53f323911bacd512d8781675d52de9dfe15b8f567ccda8f47e1b4f7db61047e1df5805", "5363655365ac53", 1, 801416275, 0, "88743e8f517207abc6aa24e90a9b9cd72773d224f07fe0d2aa145bf7cabfda4a"], + ["808afb780289b60a0e9daaa37c80ece47455af9433d3c33caf578964e69c15dbb3b64136f5030000000152ffffffff6d63057a5ff568283ef88d4c7b4be772fbd4ce20342987235bad5ad280689ca700000000076365630051ac6aee3bc56e01911fe900000000000300ac657ce445be0251ec25020000000000000000000000005d546d1535afab76beebee62688954f09bde4ec219f0b0b9b16fbbe35fbcd858b71eb3804df9317a41e106e8327df4a78680186d2040def246b7526efe3bd9d2f6b86ed0e1a7ab4a31c9eef1b2e6f51d393372bd78f0dbf7111644ccefb25f8300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0d699a75ae2d5564d1487f1ce4b17467579fd5dd17a0f042f7f23f99a49daa44b0a26c0b9e4a16b1c95d0caeee6344f5b1a6434ecfffb5951431ac9d363ff7bb925f7336ae944471d12bcf0b33b5112758a6a00554956274bda278ab5c9718bdc67b280871cd00e64213f7d3ffffbd64fe849f4b7671beb13b56ec3fac16ac20224b71613693c283e26823e8e02f764dd4ea95592bd2a917536d3557a77e24bed02195a9c8456a960ea694a9c9ccd752401e67be9439d878861bc23d66aba47890b0a000e9dd27e84cbc87ec5651014cb9446ddb642a15b94f4038e59d7ab31cdf682c8ede61cea002fbb3c8dfa6cde6f4818fe91cdf0b339328e23d8064aa09e058002143a62e5059072bf708cc3a8e9d6e062eee5c8f75425f9f1d75eb68e60e4aeea0221c62d3c002562611e4b3f24dd281a4ce87241e2241ddc879561afbebcb374c703016eeb158225b77145b1fda8d1a3cca3f3cf31b785f14c001defda38ef348973020959a8e4302221470482db34611e3a12a8f50255f86ab769550e68d7c3e17a6602149287122186a1bc1351fa9aa29251a79acb16ec14c804f3b7e8efcf06ef0cbd4a0bcc40d58bc368b5ef7a134fbddc5d9824bf8edc1259fc7cd988d47d0cd2e6315359e399e6269645cf7bd631363fccb1abcbc3837f646f7fb4908a1db93c509bd467040777e510a50654d913c39ab49941aaa7a032c1f72ad78472a6c4a05b26d24edf59c3fd5d119a83e4ac2bd2b6d723e861ea4647217e07aa0d01e8c2b490767c02d1a0f92a0b4eda760dac8df9c3dcb740af56072b477f7a40e026410559a8edad69b5d05ad24c29d45cdb1e96b94f001ace53e0c28f5590313e55b5792d05adf2aae3235e2383b2e3a618dfcdcd04fd9b12f11d74798f4d3229b96bfa4972239c0da7703cb792d4e58697554dba040f9b6cf09b67fbe55d5e8f2a68e95d3a86bbc29bdb1f1febbb3472f104d4210adb9fc00c3ed025ebf44474b35ddea9892da08d88a1256572c7f4c3fac78f93871731f63919ecd43e10b22bf141cccd9399ae7700754804d4fa5538a4bdc346fd2c4b78a62d9feca3ef1341bed9160feb25ab5618574fef8542d7c8f712d46129a86591c8d6a69678b793b3d2d2f16f9ccd472a2196d97e9a51aac6246b0708c3c74085cd7ac2453cf7e6395a2be2b3851f08e85f67c57820aa4f4ee757c8b19b451f2878068d3da8e1b7f6b18a4cde6501eb576fa72ef890bda5ecb1b70292c228ce0abac22de3ba3760fdc31159d43ab3c3c1dd14584397ea69b8b543fd7f046fef7260ad80c95153cbd9ed51f4e5799f2ed86fae65dd4707ef06174a62bc7075df3a7588a0aa480e80fd4d3350376c80010a3fa2569356aba9e00ea0faf56fdab8079ec5eb964adbe0e58dad7af700cdad1f2e010b93adb29a68a2754130dd2b0b9042e9b9a7e7c23960f626a588c093d66f4db87ad591609bfd5c8c8ffe522f65355c61d79c50866502c199482e3bcf66c1d0a94c7f2032538fe07fe3045522fb4b7c9f7118cdc00d9f5f6d713bcac1f231eb333663c35f14bc93dfbe6ef5726b1a67350ff41d78b35d83558a60f5fdd0e89f991767048687dc16b8642b3607e807789f281ab3775c4e3c02c1f22f195b2d0701d944c4e6f1c325546da3fe35ff978561ff2fed5ebfd966b69d1b51f64f1b26eb37308d71448b35b956f2a0af5d0306546f3a354941b94014ca390821f27d1eed208b33c7fee328c587ca2dd5fc82e620525081c897c3514e7514ee5a32b7d89acd33bb223bbeea300772d2ee555d43147a9d17791404827e82e52a174091452832e7b8736fd4872474cb100f41d889a79cc3fe47499df546469dfb4c2bad246953f84bc63f650d74f348482654a021d3b44830a9cd0488412a5abeebcc25bd0ce83dcdf7a6f86af46b7b07f599292ff2f41b76e3c38e31dfb61edded4f5b9ad41d8148e4606582e1c0862a03536556183ffb67681d3c152dc4b92ccfcd022b5f356ef7db977e07a4984ddab1d11d1fd7d3f226a761d63077cd1326f1e8dd8d368d0145d897c380a893af59dbf1e2b7de95d7840d8e139cc9e3ab3031b7ebbd6fb24ef956a9ba39445fbe57892a558a925840ebe85c6c0f68c8f38d099f1860456d1d1a76902748c18b944f94d3361046d8fc8c8b47622d0cb9c5bdd1bfc12f64eeadf43f2c018b8d3adbedc873aa5bbaaeffcb5e81e4fc8661c2d47f1a9b6994bc8445aa22b15c5109257183ad6c93950c2ba42b7c15a4a140cc91d1414c77d9a2e3ab2ac166ca95756bdec396d3000000000000000000000000005dbdca85027e4082725499242ce5bc23d2da66d9451705d335ead4edfe0124120c9dfbe02814da8fde6318a6a9fb6fb9278362b951b9658bf8316986696ae3fd5757a19a27ee9cde42133c745d101c9d89f6e77c6149a02cb5c5386e0cbde0b80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000085fed1a0b7ba363fece63cef026a73e0506d51c7780788a259f0748ce799cf9cd917f0c7edb290b2d10f3132c5827ad70ee492462f4426b8551fe0ac47b34033530673f8f051bc6af8671f2bd6f27c48e542f4c9c86545cf59494d78c524bb3ef79684f895cd65091467d7ff09272b5ef6c2e8b0ae9fc56fe216c784da7c56070228f95702eb0da109dcf31131ae2e79e45f7c2430d0c64a9fa3b60bfdca38ac1103172e49183329b7f2d0f754c9a0497ff464d7a30650bcd8125bdbdddf1791d9a30b06fecf7ae943ad63bd0fabcb263204d7d6bc2e24bf89a9dee89a228f72c4755430f1c2b8c25b97258a13fcfac3ae9dbdf2cdd076e7436fd0b0fca7f73b1f4d810206d412fc184d1e507c93da48e25432bfd16bc2f1f158a2c7e6d3b25500017a08022eca8ab73fe5c1a4e32474d0178dbd1b229365c7291afb92b4ac263a0bf65222022f39dfea513324ff5bc29f4996832496ea1860e6d42796819118b14a3b3b4a450229faafa1a17b721eaf29fa5bf13d6554c89ad21423a061f1c69583b702e2849902161f31382592acfcbacd6b7474c359def407a34fd26381c435e6cd1b1ec79de10a2029464b28d0eae9820c6e6b9cc128c31276cee206915c560dfa33504eb74cbc9bdbcac89189a2bae8c40c36b2a3747890d2950045660c0a7a6b91866b1e0e068533ad1e2a41878b8fb7a75e92b6cf73792959b80fed47375f90a970f5e5e916421f768e27726880bd4c1f92f06d4bf54ab96ed42bf2a56f41df9a5c702e8a7c93e1b2a2725264742ba2e294d8fcffd96e058e52863d6bb732dfabcccc7cf8eef97876e680784c20799d5dc4ba1cef8086e190581509dd157e0a9eaaa679049240fb4c6d67bd3323143326230a19ce8bdf32ac73940308ce965feb7b5af77c9aa3d53c5634a6b6e83816f03a1c3358bac5e2636bee3a0052f8417d5f9d73a42a86adfa1e486dcfeeafe1e3b0319be22ab0f9effb82da5b05f09c73c39ea10e49f6d28f7f3f961050357ae6f03443ffaed8390fd418a78536572a75c17a9ca3f0fddc28090c90a83999533ca8ce498c311a3534026a65676976d320ac671cdf58a95b0f5e448246d5f15569b3039c1c096d89c223158415743b68fa1bf07869b9d0b655f626bd6433d1f93b0246334dd75431d94666657533540e3b853ed9eede4088911d3f3aa94a18837976baff1240ec8f838d123876a9cc8c330ebd0196feaccf80f5f4e66bf4e0221d18e5156fe208d28165863a97e9e79ad3d07326cc72de3f0869c8031ac583dfebd392c2e8d77bb6dd165c1348fd78dfc95f691bb7f1eba6643174fdcbf58175038894ca5759e831ae8e2b25e3c96f8235e1e3a9d1c40edc5a78d69f21e8b91872d46f6b275b071fbc2c228122877efd511bba502989efcad6c3dac1d5ac1d7bd48e09a7dda65977c7218da7a26a5a7924aff55eb73d7f6a8556af49e0966b434b6d18a0be7a4d9b4dc5d25d112da86da46fcb0d89aba389a3f1bdd5de2808d52cf08d6b8fd7436c62566af4633ba31abaa8896eb998cf489e140fe31529a2a83c6cdd4f7f78168251472f0a9bcbee8cebc9a3843bf71d810a979248eaffc3e34a5e5349b3f860b597708b3f3636216ad5d18e3179fea79e67bb3ce2571ec55be5b7460c4ec8046f0e839f4ab437cf39cce66ebd81c67620a72413bfbd1400c5848d1e7cbf89ebaeb2a4c8bf7e96d1731b93c263c5b09c2488fd206bd01127316f52a8155ad51ffe3ea0565e6ee83d8fff621a4e13af671160d90908e5d7f400c57230b74cd12db1afb29c1f44dd0125101823063dc01417c6b8f11037a157ddf9f01e87cc6f782d0e67db135239eff2e6698dc565cca1e61c0cb64f2a86ec4ff5d05091fc34b2483e51eb072365c821ba6807e1e9800e28d5b40da3809478c3127175e3f7555f02fe6a7f5d16f639cec6b338a5f15f906ac97635fc4639fa6a54e267e2d85bf9467d4007b87cc4c04140a862678774c94a9bcb7e05490fddbf1996812b74fd3767502dc2249d97eeb74fdf9b9a55bf5a29a255c4ecfb292f562adac87860146771d30ce2ac41bd01ca15c3816718682ec99eb3995eaf5d3d4d79c7353818944a246795af0b5936c17b24222f6e2fb0d6b57ccb443d653393991047542525431677bdcf4401252cfaaea1c3153b53109ac1374e94ad4ffa6a50e28f916898b3d69cf13b5f883182b70f0d8f79bc8e682e37e59237620086e4ef74def2b2300221b667d074e9dc55c1929416b8a87b8fc74f5975bd1a085cb1809ad7899b0854f80ab7c6e05cc130e7c82af5b32700cadf9314f65494fdfe2228f36c2fd281ad254a88dfd5185e98ceffacdf2e494974b7957e7c06a588bac17aef045293cfabdc5a510ece15d5fc14136bdb6d92869847298caeba3607394b3f8ac9404fbd1603", "ac5353006500630065", 0, 1826186113, 0, "acfee0bc56b80e7af6ab28588efe82d113adf47e9acb10205ffa36d7abea0574"], + ["030000807082c40303d04eb5d525b385b67c4df43274660e234be2c748b8cd03eaf91164654c8de76102000000056352ac5253ffffffff433659e4b1aa2ba55acfebe9020a9bbb9f2c87f9e6ec0a17c31773ef3e5cdf9702000000026563ffffffffd2d8138a195d19fa8b91876a0e480489ec6122a4393373acf667213dadc0147c030000000151ffffffff01e2cddd0200000000096a516a006a535153635437f74970096072028e817203000000000000000000000000ed49a51d2c9933c05cd2e8cadfe0f6a14d12a35252d0a92f3a2736c9476ed75531447f399e0017ff2490fdcb45a3c81f1d77cc91bf58d7a4e1f96611413705a2140be4497e03d731ed67076341be9a48fb31670e8c3e23d4abf416aa4fa8543e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003dde277346bb3fbec801db2b6048ba20292fec7f338f415abd2b14c527282ac37d6192d7ff2062938dc1a2bbfdc281511e4ab6ee964a7ac937743bb185673817c537b99e1c4936286783aefec686b4b80ecb316acf60f9939363f2719435a03fa9ab72898e0a3509ef323b50a237ce57906e93e779e68243b92dc7408e280ee3020a99966ce89a94d8acbe4229db32a2908a2c7cfcdf142132bb572e255237931a0213bf53b3a3fe43c56ae6fc2cb7c3f4fe60cdcbdb8c14b03fc9e26653963a9a4f0b0253aa76ea56cccf38d7e6fcf202ddb4546a34fda1260c09aeed4ef662f27c7180e76851c66c5717b78e88a1af9809974629b4ed3bce301af41477370f3a1e7d0309b61d98bf2111e83c8f2c00af63c0e5e200921c3f54d4fdea6fc333ec31c681031af13fe9575acb591de355d59d112a83d92bd5c86e16bc6630ffdb2c59e9524c021a6a1caceab8de27fae938f8f455a132b48d0a3c626401f29406f416bb7bb835030b656c0b99d608234cf670bd25e8eeeb9e6a941ddd0123ee2e8439b5b252067a020d4ebfd95a4a5f08de4d40c8afddc132269a4682d50cb9bfd2776c139d146ca458cf78780c903c2da30ac59c5c8f1e47b493ffb4a3e3d2b3224743d5fec7c5f8be14421cf7635495270a59c79f7defdbc998a0257c4f587b816e584da58cd3bb8e70aec995aeecf2f50fb52d409c5c734c6ab57f2c16fd3025ac779e0aa7248f66a98d664eb0b6398a77d95ce8fb62835dd9f90f7d74499ab2a5015eb0e8a82e95fa968eb647b7ca4ede90256d86160dfc69dd494626989247d73ae37edb2cf07e5e07ff2c0b583821667090695a65b85edd6aa6e6b4e7553240b5c68ea163db2097d053e7c307b93dfbbdb5bcebb87286e574923a3135f568cc6c09588b53165c3767a49d5a7ef0186d454eaceaa27cb39543d094bc91587e1a2baa8fa0213828995e217577382e86ee8c87976d0ded803ef1ca0028f5dedbff8c44e03bbcf92bb0f83125c3a865c9f8c6d8e9d21b1258562767967a14636bc970005c4f1a002d4857e8f89d8ce5e425861c161d814afd1cc289d07667fd1a1a8f734ecb50a5d664f9e3302d426b2682886c73262ec8d898623e2880b1bb4f050a43f06f80190819d5fd25102b5adfe7c192f9d3011a8aee1481ca4303f76e285a53bb8211bcd609909ff8d156cac7d5468f0a8543994262a5d3db9a8e52ffcac66dd2223cdc2288eec409fc55dd7ac5cab144d988dd928b89c274ad65a7658550a6e93058a8941958ca42c7b214973470cad9dec228215568b86a34ecd670001249630c10346b16198871b895c72fd5d930f5e9d16ce3e87f72147ba2adf5eab29522519119e672162c6c875ad69b8babc75fba01a6e6812ee9c49840fe2e5dfc4774035e886ee004f08bd7d91cd13522e45e6d13c04f49527397a981faecae4427e5a1a367603978872b8e42c730278506da01982ffd7491875ce605a23acc6b6115c7e211196abd8c66cb9c9420701c8d360177f1ca251f7c594e1f8b97cb06b58d3121351ea29cafa30ea0bb78e638d9cadb4829ac995a922a54ada42cb6b81b1fbf172312e26a7d9572f0cc25f5e72d3e5d60c21572c426bb87406ad6709286ddef94ee9111305189f544cf979480291a86130862c2d378ed2a71fbf49755d7bb17aa45ddf8a587e5e5ece269582bdbaa1f2e4279b708f713f5952ebb2ed5c3cb7979b0cc473edc8f4c3a605e1f3addd87440adaab154a37ed814a623411ee3bc9779ccc96e861d84e18117aedcc45b20e0ec1d58ee172e84c86655037da1316aacb075e99830a9fd50cd3f910a3ce667d3872f29dac3b58dad3f0fa3c391dc05c419e971cace1dc046af9092eeec48d5ee01ff023bf98f449585d3474c73de25a31dc757707565701a77a841f3ddd888aa71a479d6e5a572053ecb8d99e6e5f2413f199b62c1da5b19a382f7502dfe949c14553538949e3031533e108c07d93958ce71e8d7670ffca1d013addb6b90b5d02d2acdd264fc0008f72b829adb734d74af86837cf6b55836f7dde672536ef0a87450cfebb8d2537f80cd995377ce8d663e14f272a3722ed848c592dfa1230c3492326ffbd5de263f4368674ce7e40ec6c3e7bc23fc3e8e1ef89b4231fe39bb79f2255cb2faa0f12135b1febb4c9623bfed2b763aaeda82b54f2656953b22ca3405b78a1c03ab34caee58d15e94931083d9f11aa0e205ddd9369027202d1105df68f3e493d13206a4bc48bc1287747a069184ecd29843ef6fd279eafdef3cf74c5dcec43c0000000000000000648a8c04000000004b6a7b10641c4070151b9275b451d7b0224ac238d850bf103e01c3c3e4b02e29b6d55f4041998d96299a3c602d60e60d3430ddbf6b96ab0b354ef39139928a5a0c205551a20672220f011df430c3088ee0ac23fd1703744dae93831c5d5a7d81000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006abdd2e2dc0757d2c275b9224a5cc252805a564a74443bfa5505d5bfc3d81b0501570aab8a840e0a0a794ad3e7b3d8a94aa67603147acea11cdfadfda713d65ac248f39575b4e5ff238aeca7b164cc05782e7ed4a0bf10a6b58290017fd0b1d7cbb46f51b46be0f6eb55fbb73d927f0379c2530e7b0f20559ce56c06e1dcae71022901ebf6f900169572c7012550577f85cc8704c5a2b3e4d0f1497921439958a5032cd42bfceaee3695c10267662de9b9c85e7d8ebb5be5482183e0e9070ab93a010a04d8db7afcd3abfb4da7638dbd40911d098bc1b9188d60b11e6d264cc16c701034cd2489cd6ec208e1337d20744fafbe7f3faf06ac319e7a42bc0698e99755eb0330209e3497e03de52daf8134ef7d81289802fece1c2595e7b1e1f67e95d07a4b03274fdeaf3702840a9ddbcc580a00007c4efb9a7fa0f884477124d042f2ee8e33030ed031f11e2cd71dfe3d05820f55fdca179ff34ecc17a437f128f9d5c1c5823c021998445efc913163f4507b601675befe4ff57af9ba4ca5f953019e172243057d0212f7846a4be0eac337fd955e88a32d9192e1f70c286edc9b372661585952b9618342e8387d5daf8de4e1e973bb6368e04ed38627001754259e7efcd370d0a17898db2326b68f04931053144a4d50005ffca735735ee0bcce9592bfc8dab78f55cbdda59314c468f99ba85b53dc2b21d9b3e34dfd27bdaf37dec3e17cfa14dd0efadfc43515bd86cba0595b5cba61184fd271c625bf925601fc15383b536ef7170230bab25776ad8933e39f60e0d0a6b8520ab48067d8d31422892f4c9c12fe37e45608bd12f57b6a69ea2d81dcaa29b157dbc9bcb7ef523cbba2f189a8dcdd8959e451303ac4ba35528474fb1f05788d781814dc45133f6eae0cd0ea78444aafb8e3655f6bbda05cb1036c93a3ecf4122411f35bcef56bd34518b6ba6d1390ff0521b90a5b78c15a4f08c496f0c0b22cac6f3b7454f36b2ccad0094bdff729fe3a22983d197079a7b0a09b29d98734933900ddb5394db6bfd79c61653c708f0ab810cd0087497574fccc4dd48c7f0f9035f7c12c34b5be9e55fdc2879076cf1b460b4e6b9e993f4e33061827fb0757a311d8b4defcce6aa54c2ffaaa18bc66ae4f24e1bf09edb9a94fe75cad631332493dc16e3853e3419826cd96209bcb495f4446caa09f93bd7c7c86708da2b7b1bd393f997d962f3e30538150c9a54f4de8b1a57b8ef82e36afedbcdef8935d5c702b7e3e34b364cc8b989cfbf1c37aa5c7b3b029ec1e0bf82472513662297e8b81ffda1cbc6d0ae16452ef191bde806c8121508c60f30d98fcf50f654b5c9530ba462160e1f98dd5c58df91ea920a883b9b9f7bf4f22aa95bc105da1be51a18365e990a07b0de2cd54e9e307ffc12b2e7003c6877777a636fe25c99f118eeb8df6c9016adcf281cbd0792ea5588da6ce95e8e80a4b9797c92a02cf327099dbef7c2aa8774c8eb9a0b877facaf69116f1d5551d7125aee27baf6bb03e8e7b1a3f8f96e428d72918ca74e701a858f17b255c767ad8f2660787cb32dfda3dcfca67589e2bcd5b90f34f50f774a72d1a27b50c1545ae7d59584f0ea4188f72cd855d4b5babcb15aa4612bae43183a71126ce5bdd24119be03f440d3d4ecb4f454547568caf67aae03d04eeeac0e00015150fc3dacbe3205ae1bffd25c250a794479c6db1f4554568dd92cc6252fb7ea881a09d959248bfd5e57d19b9eff13821473aa397ab7286f8c7d8fe56d3cf1cbe3ca174016359f214f023486eda246adff1fad8ff0aca6524c725a6e053644b56ca55f75970189593c9366087aeab2a818f5a66b922684f879fc115bdbe7a30f1a93a955d11f655a2b77f5be579e04b90a695f32d772d9f727bce3d664e31c840b52c62adc46a1a742fac77099c7986ebea2c0c04d2809cfc3b55860142aa1969b48d367e949025b1caf70d3014c69fab7cc19fe4ba4a73eb0de06c1e19715d44658f85313f98ed938464b43dc5869ca134523efd2be9c01e7dc23f98cb3cab11ac302a08131e4eebfca3f89e2dcfc0791629d1016e0c5099def9c387cbbfc1041c14a470d3b4c6f50696afa427c4b7877937dbf1ab428830a164065a658459cc51f127986e5263dc9e6015e5e93aa8ad1fe0607e41a5017cc7395ec637329586f7dd656b9d6f34ae916fd9cd2db7d95388b7610454be0078c3bc0974cdb59dfe23f7f2282553ab986634a642b730b1c611590d2f41fc58a5c1acfadc0a67500997def0504f18446f9fbfa6cc1ca4ae136b61ec605eed7062a4949d527f9216bcc28bc2fe7fd556465fd568835e24a9ac107580965db4f5fbe188d1f27313cc986bcac51efac75be2e0c781d7bd846365600b5e76f498ee138f430ff1ef5de732c1da849b3bc671111fa28d7f3536cd30185b5d6d07", "", 0, 971178703, 0, "1ebaba9df16060cd2439534f3aa7111cc3ab0ed3b59c47005a02be58bd74635f"], + ["45a2a60401557abad87904b0fe2a618196e5136fc28483cd8c18ef5debc82bc80de4d7139e030000000152a42a585c03264a5a0000000000096563516a6aacac6a6a0d136e050000000002656ac80931010000000007ac516a53650051b2daa45000", "6363536a5300", 0, -1766309209, 0, "6a36e6eac9d764d63ebf461fdf5c6be33813cbc3bc39f4c1ec4b9028deff83bf"], + ["89553f1e01c34e0d8f39fde7e9f108717a53bca01135f8721ecdb6c537a43816c6c568189c0200000002ac63921d37b2014847b60400000000066a635352525300000000020000000000000000e6e9a40400000000593d7f9bb78dc5353307b3f6cf6013ea11e5769bba7a5428e7437458f23acd145008c62d971bed7713b2859a59587f9a1699974731dbccb1bd79f13de7fefc3d8724daa3420d7e4fc78d152ce2d5303ebc4eb3dd4790936a39fb1438caa16d79000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006f2247672c558bcd4695f93510e07b571cc63cf6bd0b38d471c1a3dd54bb24b2283755ea75891f27d1e7ee66ecefcf916fbab553c6efbec78c2511a93946705a5ab4ab714939b7b9dcb9342b7639182572215e30cb42f904d960f7f44712609ebd0a24e792dc9d4585c1ace7b7994cc9adc26af77fe8fdb29caae7d8d7d146040208645b1b0c8f7c7bcc1a2a1818c05b53393f022524185cfb8d1e8e707e3b4527030983ad25b3ad706cc37aa32a0aaeb7908c2d0639b1cdae8171737e4100795c8d0a020d97efc0c2f82f2d506d74d0609dae53e1d688169b79a12b3d814f5fdf8207f53fe41816461528da822568d708e04711d0e360125f5b3ec1fb7ce18931971b03150da68e8b66e77c26f08b2700baef95fec71171bf47254149d0343d4283c17b030a48c9af684850a8f25565261dbfafda8206803811892e4d26e05c580ce9360602035f387d30e476445ff8fa35930c76b65186f3a20a995156a3465297eb63eaa2021ba1417f6fd4560a02d0960de0598c59d9c49dacdc94676fc52d07efc79614a00229b6844c29e89766a9755c50e200993163f83dfd590f2a4ba07798e5a3e5d1bfdf7cbebe8b643e77d69e862d9f5e2e67e00071e1dea27892b652eed099800b0047364387fd4c78920bc5b71ce928af479128fd1ce5b71535c16f1d09144c63fd38212e35ec2c928d6b79477e00de16cd3832fbe1e5144b6c8b0e880992554f395289c178575c56bd42e53609f9a00f6a4ab5a98f8b6f0d28556e30e97c8a5d579eb811330afcb12120c107f1b2f13fc49d4ea1435e7fb4ab82cd63e2e44d40fc8f9554d5b1b6be8de2f7cbc769ccac226f30554ee28a50bec136e405c4a91a356fa2362abc4dfc702d940f7cf9daec293919dba45056228940e1d2f554701fe63232b8c19764fa5691cf1a99578e354f9c3f29e91d0e24292b2855819a1a39168d0a8c12619c4ec47f4f47362bbd4c651b7e4bcb2415013eeb67d36fa914566c9cb1fc9d3f0b05195dd501dbd5b85d99f8891857bad9339389e7dca4e0e3fe5e5b80fc9654ba52bda2dd7f50edf0d3d75f0230020eef820630661c69017398f5de79821cdb6cc7387979415edb42542ab19c95e67318375dd9fc55e7df81891ef7a96f64964a537fa67248e20532922baeea49eefbdfa145199e1df7e888a5f5c37b916d258048afe98db4c54a03574f482b629e0e033e184d5b3d095496c830d7ba6633d4c600b6e5f0089b6d4142915d334a306cf2daec38a251310a5e8f3fc2aafdadb042b122a09114c830de491e623a1cbcda30d843107f5de6b7b65f8f277accf5d7ee73b55a7d7403c098824f619ff1e1c4529dec5b6c7058950ac6479647000fc20383078a6e75910d96673bfc8f1d773ebc389191f1a360560c47ec3ce61ac3ad90873018400a14626bd522977de79a4eec659465b30dd1cd04ea813d3ae684b2d3d582fe71449e53c3db3e03e7775e82eedbc659babbb89bf88fae95b5f7b7cc04ecff95d3764adf10ea6afab476aef260a14d567ba50f73e7c599f9ad8e8d78c165d1b9b73e732fa04b268fd0e2d55b0b8a32f015f0aaf80760da8e9040f99058c41e0803a4a77cf66cf07882a1041efa89c90aa446b6fa4bccccbb9d829479a5a2eac11a4d8f55114ff9be9961759a15c0ba46312a939ded38dc2cb5840f4408e34482b13a32dd4902440d62528df54f978a79d4e88190ca481251e4ba3245b043a74524e494f0c55a8d171b39f32a02836989afd3de8fccafbdb55f61d4193b0753125706b911b8be175e14c3e4e5f20adb9dc35171fe11efd64512c5eea084a94adddd3faefcd1f6c4333b2cb7a6dc4da7fc259d8aa912b612d6fe6bd1f6bbd36221d0984de27169c727633dbd798b8a8d36018b4d2391aa06921f63fe1bf791f36b23bfcec40975a946ede2ae271eebe0707574c263f9feb7ddb8c04f16e9bf0e94576d277d141278cb0bb642585328e37682c0e073ada303c30809d17eedce16d7c7dc8e39ab89bf93c3fc9508855205197ad5320fae4b624f00e07586ba2c3c284751f741c57adea911a85a6ff8861453964ac7d8899160770ba3b5e69b3d092782fbdeee7a52a111b837e917bc484bda35f14d4bd9d30c3ea9aaf881d1a521a4996ab99de0cc28f7bdaddf19889f00a1104a1eec4588cedbd3197ba687c12db96fcd0530c63ce407ae4d2025945dfda0ed18120fc92cfec162545e167a9e1f23b8b347ed733fe5f30b6351e45e3a411ad9a6fb0745572072b087b18894769f177254641f1acb8fcced000000000000000049c1dd01000000001149a6bf21fc819bff79fa85b4ee8680de5b260e818befd406c4fab7448ff5e37b651ee57a5b1ca3e60cac54745043bd1461bfa4153a182a1a18b275734f76d81c9091a1a0f0cf9a48ffc6ad3934b2c57f9c19bc3ef49fc432104376a9b0a17d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b1ab7cb044c3ebf164daa4c2da3fec5def426318c25f0fb52112ee1f16c4818e01f1ed2658db7e42b8db9b38bbcb63719d27efe19c8648b100161156b21e0b575204ec7dd9addbfc8754cfcf1eb7a975ad32262841f24bf43c8c87c2ad9df26173579b715775a60bd3d3607a27f7c26192322c8a8f6f8f37acf094aa3b73677031e2deb1b790f4a0d1f433552d80fd71c79244050c51e37837ef989d3c0e12c7a022f5078bc64859bc66ff36a79bea9ceb8bc6e428a7ca663a479e8a0c1a19aee110b081074474e616eb6f2ee6283105a2c1479e8797ce65a815196d1c91ff17b2851acc107ad2e43742b828bc8d237cd9704fbf09a97d1bacc46e2f869e08bddf571020157ba962346150b1c71e0c6a516f2c6fff68aa9cabc2c5403cb243ed7dd637a0317d14f78d87ec11260df3dd714b49cc8a7a3b21f8a187ff494e97d5d0f5ff207031a0861e1922a0d33a9d86085267742c5a4484ca7cc5a43901b9b26b2068cfa2c030df5997bd01d7ac5f19e678a655a682b32a913c08363173191b01293bbe48907021c74126d7a22fa38ea3249af705d7451ba2b085d0a938fdfa89cd5a654d85088d863f50b3cc37229307642e52d3dd156ffa06e78eba10cd6d51c4e49bafec6c0a4059abc40da022df19f2b9918f37fc0a4c3ab26b27889601345c8eee7bca02564b554c7f234685c55a3cdcbf70036dcd1cbbd15430a78dd67ec67a45feeb1d7e5297b99a4015a7d7a2cbcdb651667ebe3e644b5a321b1a98c2c3ae2400e5a0935c215939c22ebbd618778cfbe7372f0db6541bb63661f62be777583027920d24b6b44bced4cde1b9aa968d028c00454019b47cca70010fc615f1a3646c510ccd977503fcef97cb4ce593c53c796ca1771b60398c082f314f1aa231b4cc70afc8cda9a37de8a14ea462f81243e448d9855a77f54a7e05d2875d5aebdbdb36d433016f3b92546f467cacc94c74f7c51c8ea30ce95d7efdd8d03db561524cb84912e280bdec2501ea758eb2b4df7dbac853ad4e38499245b38bb86371aba7369d69a380cab5c472dda066eda44b3a9681900888f277e66a13b3844832b245edaebf501e3598cb05d82fcfd4432679df00f918f081fe8cdcd594613d9c51fb9fd187dd4532f03296fb1ba1d2f9cb1f2213cd64679758235bd041fb31ed1ee65ec2a2891ca4641fd744bcd38f2d1d729e0ac448c458f9776e9882113736c2bab120c4e9ec379b19f59a01eed034fcba02f2e75209a546f43885956bd172d3800ad5d341307f0094b7702ce97d0379a9f9dbfd1d24f8d8dce66688511ed9e740fcef4f82e833da48c208e9f418bb15a046ca598451695db8b9cec127adeb9970b3f8d3448b5163a02ed684c093f19da394b546a10100dbae7897be1dba22ecb5271d0f9be972e04ed50accc67415de24ffe60ef0ccb17fb133c192d0fca4044d09b052678c2554820d99f8c5592b3e3225c7c1c276cb6de40cd123a1ef6467e0c4337d02711e754b0fa847f76dd730bb861ca3d5858312c7454a271311aa3c1b40af67969e285d63a029eccdb3136102ebb6d31e1dae239492d38697ed3fe5bf2dea6cca7d74a6f156adae7660253d74c947cd2ef865a96060fa13f0646a6d088f034145a6a2bda1a96816abc1d7f5845f9ff1291366849d4198c20146d93f48965199edf285cb7fd710d08be32ce4def76b342f618da5f3de7f720ce574ab6a3591af9166a90dbad80330a7d8afff0cf3e5978f9d76010183ccb74433baecd004a3d7558876771686017f08f2c58c18563ec75560508f887af925bc89acb514e6ba43fdb67016305271a71968b3bfe1d43e7cf7fe3d18f70fca143cb30a9d843d8bf435ed4898f31be4cf3c3de170d53cd1fbebf734d8d7ad09dd81f0bd057b21e582dca6c888fcce5b95e925602486970a65f57253aed68d27a3966f8a316309b29d22c2644f9ea8665e6b12dae29c17fb6469490199a67a723a97d9f0f7fa6b1f49e2578a19b02b7ff4f8a68cee67759ef82ce8394b8b387dce007c498806f9e2ac9d3bc18528401614beb59528c10303a7b9128bf2e47434b62b36903795f37d59c6b1fc2dbea41d3b81ce491be1b9080fd3ac78e8eb6ff7dba482daaa33fcc5ba9adc989d4bf85180647ba7fc56fa4ce479cb5faf104838586d964acf3373435a6d91c1ca4bca421691ab2f11837096245f5f84eeb7cf5e7dfc62146afb1e3a5e760c18c6495a9b20f644f544d3cc723e8bb8ca2bcd9f52fa207c48456c18abbe6dc651bdae846adf0b0075489e63ab2b931644ffb2392d9d0b664030bdb59dea3cc24be6ecce37ff2483ca459b3095f1b540c8cb077d248d5629f8f29152182e8d0945a7837cbe8f60e3bfb5608e3d7f24843d50f65df1cb54c5e2dfc09b361500db97b4c26af655f31a4bd036e6c171609", "53ac", 0, -2063601244, 0, "002fc7bfe2e15ff2ae89aa9319448aafb237bfda39ddb723b47a8d42d176a9e8"], + ["1d09870401cea927e1d808060d058de7c87898a322bbfa104883730fbe293843d81437b63c020000000365536aa2c7c66201c1f60a0000000000086351ac6a6a636a53000000000119c2e0040000000000000000000000004340e96ce18f6d4e9ea19be08874bfae6b8157da50455c7c8686de126ec6d5c735741bf14bd8111bd3cbd3f636ebfcdecd125a5bc5a39b30696ba2c49979c987f95294ac91991a8127f670560568b0bd98c48cbd2e96c27d751d4876f84faa0f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008ddaa8950a1306585c89dc49c1219fe8bb945d13fee3ee06f67691d1a08f2516038ac99c12b51db743447f18c98b863bc634b6fad55ee2a31e6a693f197928e8783bcae4deb5617c0ec193e004b30b33bd3ad1060335367fda7bf11214137a482ed1b83bbe83b9d0817731b08292cf1cc86b05bb74ac2f6e7cc8ccff6419c56d0324d727d8e1fcd765eb3e7b890521ac6b15a8e587dbc697bf3538bfbbf85e9905031895d65a1d0603cbfb623ede58528e16592553d3e0c448e7e3acb3a36823bd850a02c817b17e0b02326753c26d77f9bbd0df4f4d342251157b17a654290693d67a6ac77ddddd0e8b79fecd247323205430cd9500a4fd4aa2c3c09c6fdd86e65b4a02258bfdbfdd78a5587635bedba775ed7947aaf514e978b70281a7866559860bfe0326589a630441b4b9a375a1fac3e3830d7e6f7993a9e5a1dd23256f283e0d1d9f032faa912077d4f426f2f670b00afb06674d96560ed84623afbb7d11ee2608a50b0213fd784e42c1f8aa1b9acd2765615976068028132744ff22e9799dc76beaff1f02002f1b498409000e5cdedf0d77b96212bc1e3bbc976ea085a1fa9a3b7c8916d46f3c8d146c6aee0bc0acc5079c1e36ebb8203033fdc6eb1f4eb67d0e4327fda50cebda89812b841dc6198fea948da254ccc2be0ea303c578b0fc42f7d5729f0f280869b4eda8caf67ceb29d698fe4c16905a132695bc3caa5092c2ffb6ba5a959498cae00d60438a8989218d0b7d8668bb0218019c19f11ac99af1ae1fc93688349929079dca123ea1e489019735ecd2f7d6067e9384dbaca2b93345d5093c721fea69b6f4b84d38e1ed34fcf90553d91173cebd8ef3e6592fdfd787fc19dbf32dbac9011367a8270fac00608a8394526e46ddbf232a5b87253dbd365ba43ec8ec3ec77a1dc2e66b5f29d6fd898575995b52dd47063e066311d82f06493795a9a0f64864111197f6d171e7bd016d920e3db896cdf43e05b0221c52e67edd1fb96f18ae77ab61946caa03db0a44d3330d60cd4fe1a63efe098c23fb9fec0573f29a815ff5d4c0f0d7b9153f78215b4b37be19899e085570ebcd145303878e7916b17c09f5e72b29fbc4f9dbdc790020d76efc43d75833c2ed488590e5234c51096a269c4fb3c394ca5966ffad7ad4ac7a16a083c56ad1583294dcde1fa57322a91c3f0f55172d41418ac0ea1407d23006c5241436914077815ad030a9bc4c96273d1318139342538d1c08421380c3af830c4ea0ff66b2af99a12b94333f6f22a7b289087fde35d00764abeaf81c552dabcef40005e6195800223a03f9fcf21de79d79169cddb06dfb3766eba05a1d3821eec9cad899ee464cda664dc0a2e411ab147e85b038cf0fd15595aec945389f7d0ff4c31d05a08eda27f0b55f0b8d463ca208a566b9cbcb51ea79856cc50a78877c58f4de0249098534a7c629467481194f7d481b7040d34d66692c121bd77040181685177c7ba74965c0f1abba456ce4fd9a6f2079d344ce17db1192092fa160c0e8c6be40fafe313fb63fd964b06ba98e51e8f10bbefe4b27fb6cd20560048adc9ab31445757399c4b4af872856ad222467532eeb3eb713ed408c10c6bce51811e13ccd640c83c90b3f20c402e9c585f08309ce0d13c8e177908aae7cecc918f35b785f07b7de88d2dac5d7236edeac07a0bc99033aa60c739f3a75ffbd24c71514eff04a5090c659e1288a8cb11043c67ae5db556407b7c7ab404888817ee0116a00a65622ed8ea1c5ae35618516a7b09049845ea7ee29d6229ffe5fe98b5319e43286fb5ccd810b3d581ee9b8ac60df687adfd4d7854dc78797f595d1340970378f278edc4cae9bb668130f6021ca18754ed0d9056dca3fe0ec39abbbdd9822c9efe0ef2c303d70200f125e95879d1adfe7b36a6a54516377a13e958ee19649923a05c6705c808d276cc0a92bed9daa3e208555ebea34986c21ab8f85b8166e64ada3a18831a133a636cd53ce053908a98bd353d41092d4ef9a4453b7c7b5c8f08cf1de96842d9bf2deab014b37e347057a98f36a68ab944c1aa1d8aa536b0dd7af5ce2ad6d15564f0a96fb3c33d112d5345bee52970a8a48d935fd2d01934ef96d6f9117dea6c4fa6bce0a723b70bef9dba816afb274de2547d7b64daa798bb6cc1513b07423c598a1e600ba81bcfb09b49f07a586993d6fa2d16f1fb84d6e0a2f93cb5f660b8a0e783840bf59d640c8af0e3d4b399151af4a9a458ef005f6a0946f1d19674334b13857f418006a8a2d94546079365b884d58532ad6e3c8eb9ee6ea15bd1e55ee4e9d8d017e655ace0c47f8b2b4a39baa68cbc228955491a8b73a00886e75fdae9af74c228a86d0fab18bd81d97f36bc62223ef1724cdb7c6ab9b109c0c8185991a66015832f590e80e4be532a1de508be29240c2acfd5ea905", "", 0, 1995900764, 0, "9721fb036a89abab2c9c4a1fd3d7df6af8a834cf1d712fcff97d84aae934009b"], + ["7955cb0002ded4d1fe9ff66bf872c7549d1291f8d6a074f7cfd8edf335a9903036394d27d802000000056a53ac5265873fec7058f64e437f0ea9448066b42015202a1d56d1cb424a55461e80c5f948c4e7bc870200000002ac53c8fd6a58046ced4a010000000008516363acac6a006512eb1e0000000000005ef1a302000000000952ac5365000051ac00fd0a750400000000050063acac530000000000", "6a", 1, -1208408977, 0, "00c4e3d7fcb8def9ad41aba6a934e3e100c260136ccae8575ecf5c19442e6af7"] ] diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json index ebd1f0403..b99a72da9 100644 --- a/src/test/data/tx_invalid.json +++ b/src/test/data/tx_invalid.json @@ -86,12 +86,43 @@ [[["b3da01dd4aae683c7aee4d5d8b52a540a508e1115f77cd7fa9a291243f501223", 0, "HASH160 0x14 0xb1ce99298d5f07364b57b1e5c9cc00be0b04a954 EQUAL"]], "01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfe000048304502207aacee820e08b0b174e248abd8d7a34ed63b5da3abedb99934df9fddd65c05c4022100dfe87896ab5ee3df476c2655f9fbe5bd089dccbef3e4ea05b5d121169fe7f5f401483045022100f6649b0eddfdfd4ad55426663385090d51ee86c3481bdc6b0c18ea6c0ece2c0b0220561c315b07cffa6f7dd9df96dbae9200c2dee09bf93cc35ca05e6cdf613340aa014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000", "P2SH"], +["The following is 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], +["It is of particular interest because it contains an invalidly-encoded signature which OpenSSL accepts"], +["See http://r6.ca/blog/20111119T211504Z.html"], +["It is also the first OP_CHECKMULTISIG transaction on the Bitcoin block chain in standard form"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"], + +["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], +["It is an OP_CHECKMULTISIG with an arbitrary extra byte stuffed into the signature at pos length - 2"], +["The dummy byte is fine however, so the NULLDUMMY flag should be happy"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a0048304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2bab01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"], + +["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], +["It is an OP_CHECKMULTISIG with the dummy value set to something other than an empty string"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a01ff47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"], + +["As above, but using a OP_1"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000495147304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"], + +["As above, but using a OP_1NEGATE"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000494f47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"], ["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], ["It is an OP_CHECKMULTISIG with the dummy value missing"], [[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004847304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"], +["The following is f7fdd091fa6d8f5e7a8c2458f5c38faffff2d3f1406b6e4fe2c99dcc0d2d1cbb"], +["It caught a bug in the workaround for 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63 in an overly simple implementation"], +[[["b464e85df2a238416f8bdae11d120add610380ea07f4ef19c5f9dfd472f96c3d", 0, "DUP HASH160 0x14 0xbef80ecf3a44500fda1bc92176e442891662aed2 EQUALVERIFY CHECKSIG"], +["b7978cc96e59a8b13e0865d3f95657561a7f725be952438637475920bac9eb21", 1, "DUP HASH160 0x14 0xbef80ecf3a44500fda1bc92176e442891662aed2 EQUALVERIFY CHECKSIG"]], +"01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a4730440220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f81e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df32949d4646dfa10a92458cfaa88ac00000000", "P2SH"], + ["CHECKMULTISIG SCRIPT_VERIFY_NULLDUMMY tests:"], diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json index e7f0464d8..265ba5de6 100644 --- a/src/test/data/tx_valid.json +++ b/src/test/data/tx_valid.json @@ -5,32 +5,6 @@ ["serializedTransaction, verifyFlags]"], ["Objects that are only a single string (like this one) are ignored"], -["The following is 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], -["It is of particular interest because it contains an invalidly-encoded signature which OpenSSL accepts"], -["See http://r6.ca/blog/20111119T211504Z.html"], -["It is also the first OP_CHECKMULTISIG transaction in standard form"], -[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], -"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"], - -["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], -["It is an OP_CHECKMULTISIG with an arbitrary extra byte stuffed into the signature at pos length - 2"], -["The dummy byte is fine however, so the NULLDUMMY flag should be happy"], -[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], -"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a0048304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2bab01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"], - -["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], -["It is an OP_CHECKMULTISIG with the dummy value set to something other than an empty string"], -[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], -"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a01ff47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"], - -["As above, but using a OP_1"], -[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], -"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000495147304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"], - -["As above, but using a OP_1NEGATE"], -[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], -"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000494f47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"], - ["The following is c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73"], ["It is of interest because it contains a 0-sequence as well as a signature of SIGHASH type 0 (which is not a real type)"], [[["406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602", 0, "DUP HASH160 0x14 0xdc44b1164188067c3a32d4780f5996fa14a4f2d9 EQUALVERIFY CHECKSIG"]], @@ -40,12 +14,6 @@ [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1"]], "01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "P2SH"], -["The following is f7fdd091fa6d8f5e7a8c2458f5c38faffff2d3f1406b6e4fe2c99dcc0d2d1cbb"], -["It caught a bug in the workaround for 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63 in an overly simple implementation"], -[[["b464e85df2a238416f8bdae11d120add610380ea07f4ef19c5f9dfd472f96c3d", 0, "DUP HASH160 0x14 0xbef80ecf3a44500fda1bc92176e442891662aed2 EQUALVERIFY CHECKSIG"], -["b7978cc96e59a8b13e0865d3f95657561a7f725be952438637475920bac9eb21", 1, "DUP HASH160 0x14 0xbef80ecf3a44500fda1bc92176e442891662aed2 EQUALVERIFY CHECKSIG"]], -"01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a4730440220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f81e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df32949d4646dfa10a92458cfaa88ac00000000", "P2SH"], - ["An invalid P2SH Transaction"], [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]], "010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000", "NONE"], diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp index 0996e13c4..325789572 100644 --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "consensus/upgrades.h" #include "main.h" #include "txmempool.h" #include "util.h" @@ -17,6 +18,7 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest) { // Test CTxMemPool::remove functionality + TestMemPoolEntryHelper entry; // Parent transaction with three children, // and three grand-children: CMutableTransaction txParent; @@ -60,17 +62,17 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest) BOOST_CHECK_EQUAL(removed.size(), 0); // Just the parent: - testPool.addUnchecked(txParent.GetHash(), CTxMemPoolEntry(txParent, 0, 0, 0.0, 1)); + testPool.addUnchecked(txParent.GetHash(), entry.FromTx(txParent)); testPool.remove(txParent, removed, true); BOOST_CHECK_EQUAL(removed.size(), 1); removed.clear(); // Parent, children, grandchildren: - testPool.addUnchecked(txParent.GetHash(), CTxMemPoolEntry(txParent, 0, 0, 0.0, 1)); + testPool.addUnchecked(txParent.GetHash(), entry.FromTx(txParent)); for (int i = 0; i < 3; i++) { - testPool.addUnchecked(txChild[i].GetHash(), CTxMemPoolEntry(txChild[i], 0, 0, 0.0, 1)); - testPool.addUnchecked(txGrandChild[i].GetHash(), CTxMemPoolEntry(txGrandChild[i], 0, 0, 0.0, 1)); + testPool.addUnchecked(txChild[i].GetHash(), entry.FromTx(txChild[i])); + testPool.addUnchecked(txGrandChild[i].GetHash(), entry.FromTx(txGrandChild[i])); } // Remove Child[0], GrandChild[0] should be removed: testPool.remove(txChild[0], removed, true); @@ -90,8 +92,8 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest) // Add children and grandchildren, but NOT the parent (simulate the parent being in a block) for (int i = 0; i < 3; i++) { - testPool.addUnchecked(txChild[i].GetHash(), CTxMemPoolEntry(txChild[i], 0, 0, 0.0, 1)); - testPool.addUnchecked(txGrandChild[i].GetHash(), CTxMemPoolEntry(txGrandChild[i], 0, 0, 0.0, 1)); + testPool.addUnchecked(txChild[i].GetHash(), entry.FromTx(txChild[i])); + testPool.addUnchecked(txGrandChild[i].GetHash(), entry.FromTx(txGrandChild[i])); } // Now remove the parent, as might happen if a block-re-org occurs but the parent cannot be // put into the mempool (maybe because it is non-standard): @@ -101,4 +103,113 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest) removed.clear(); } +BOOST_AUTO_TEST_CASE(MempoolIndexingTest) +{ + CTxMemPool pool(CFeeRate(0)); + TestMemPoolEntryHelper entry; + entry.hadNoDependencies = true; + + /* 3rd highest fee */ + CMutableTransaction tx1 = CMutableTransaction(); + 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)); + + /* 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)); + + /* 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)); + + /* 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)); + + /* equal fee rate to tx1, but newer */ + CMutableTransaction tx5 = CMutableTransaction(); + tx5.vout.resize(1); + 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); + + // 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()); +} + +BOOST_AUTO_TEST_CASE(RemoveWithoutBranchId) { + CTxMemPool pool(CFeeRate(0)); + TestMemPoolEntryHelper entry; + entry.nFee = 10000LL; + entry.hadNoDependencies = true; + + // Add some Sprout transactions + for (auto i = 1; i < 11; i++) { + CMutableTransaction tx = CMutableTransaction(); + tx.vout.resize(1); + tx.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; + tx.vout[0].nValue = i * COIN; + pool.addUnchecked(tx.GetHash(), entry.BranchId(NetworkUpgradeInfo[Consensus::BASE_SPROUT].nBranchId).FromTx(tx)); + } + BOOST_CHECK_EQUAL(pool.size(), 10); + + // Check the pool only contains Sprout transactions + for (CTxMemPool::indexed_transaction_set::const_iterator it = pool.mapTx.begin(); it != pool.mapTx.end(); it++) { + BOOST_CHECK_EQUAL(it->GetValidatedBranchId(), NetworkUpgradeInfo[Consensus::BASE_SPROUT].nBranchId); + } + + // Add some dummy transactions + for (auto i = 1; i < 11; i++) { + CMutableTransaction tx = CMutableTransaction(); + tx.vout.resize(1); + tx.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; + tx.vout[0].nValue = i * COIN + 100; + pool.addUnchecked(tx.GetHash(), entry.BranchId(NetworkUpgradeInfo[Consensus::UPGRADE_TESTDUMMY].nBranchId).FromTx(tx)); + } + BOOST_CHECK_EQUAL(pool.size(), 20); + + // Add some Overwinter transactions + for (auto i = 1; i < 11; i++) { + CMutableTransaction tx = CMutableTransaction(); + tx.vout.resize(1); + tx.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; + tx.vout[0].nValue = i * COIN + 200; + pool.addUnchecked(tx.GetHash(), entry.BranchId(NetworkUpgradeInfo[Consensus::UPGRADE_OVERWINTER].nBranchId).FromTx(tx)); + } + BOOST_CHECK_EQUAL(pool.size(), 30); + + // Remove transactions that are not for Overwinter + pool.removeWithoutBranchId(NetworkUpgradeInfo[Consensus::UPGRADE_OVERWINTER].nBranchId); + BOOST_CHECK_EQUAL(pool.size(), 10); + + // Check the pool only contains Overwinter transactions + for (CTxMemPool::indexed_transaction_set::const_iterator it = pool.mapTx.begin(); it != pool.mapTx.end(); it++) { + BOOST_CHECK_EQUAL(it->GetValidatedBranchId(), NetworkUpgradeInfo[Consensus::UPGRADE_OVERWINTER].nBranchId); + } + + // Roll back to Sprout + pool.removeWithoutBranchId(NetworkUpgradeInfo[Consensus::BASE_SPROUT].nBranchId); + BOOST_CHECK_EQUAL(pool.size(), 0); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 4a960df76..9b8674f04 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -23,119 +23,118 @@ struct { const char *nonce_hex; const char *solution_hex; } blockinfo[] = { - {"0000000000000000000000000000000000000000000000000000000000000017", "004e2b5a2020abd95263f39c4d65c1d9112073ccf30a7df5428e5e8a7bbffdb556d6d4fb1978eeae589e10a155f665fbc57bf3b4d1a10ca38b9d4f9ab6d5931524deacd7cf9b774fdf72eb87425950bde392c8cf0c8447c08156f8679702a1a3866b7e4d13601ca5761003db87feea6febda45c5a3b76e5a0992b91182c30eef5a91516c57b3cf8102af61e893561c0e3f6d48582b8beea1de094b33ac87c8725c2386a2eebf434d01d46cfa27ca3ca54a84e0b4853a49ef2862bcb18933a9a41efe8f1081c57fc661db7dbe56c2e7f85af813b0dca2e0e233d1a1cfd65a013de09e12d4bdcf89210246d735486ffab49b468606e091e5e2ba55617602400a7cb91de9e3a217252aec3c6d861f75f8e0611ece2c4efbb174558be083cbe752e2bef323bb6a74179fe2d11eb15627b53321f73ac7684b591f3afb8819767f67e406d78eced8840c19ef57fd38e06bcde60152101804a71d0374a9f34f1fcb9db6f9b9f8ed7418bc8c363348390d246253275fccb27e872e351eab0f7b02899043fd0b4c9ae22f6c95ac52a91fdd185f41852d34be23f14be1de966838724956d436d8c9881199ced14c5fa8511975a65e02c358522cedb334fa2d15e47bebaf62b1be11c3b84265db3a36553a59eb14c24fff633573adb1afe53a2ec158eadfb27b82fe24df22b2229a2188f3d8044c6746a20a2c13b7f53c09ea775af45be369d209a3470644b765644131432427502d6e585269055c48136166bac32e46c2d559a80e91e1348731e7279cd6fa21d1ebc1ff5f451cb8a1134847da0450d1bdf534e3e914acfdfe8f0a1c39fb14b55f027aed21a7d7656305eea2e6a9cdacba0c612b0c0c43c0d1a16eb9fb4a81246a5e3337e5f9c6581d29e3566b20658f843fc43d09e71a220bd5bb75353b2dfcb708340de9a1da1580807c2a5987b8d19870004f355b95832071b52c72747a6a9f58ac41997e5a0ef9509e592229d315de45fb62d7d649eb6ad432f60f5f75a8e1045dfe69517159d6e850c9a3c15d7ac61b77467f04ca7091a99b46313dd5a639b4a77e3fd80a25308062d56ee0eb1eb44f784a316d66297be48b26e8d24e0b4fc88ff99407803d3cecafaed8ffaee81e19dc76f06eae5b790712cf14b3b534b8743d5cf93dd4e2a8111e24f17d2275973544608e88237d2fce06d62ad6f64649dc3e2ac3999a27b7bd0dff90aca9119a00aabb6ac8f1b6d9f43e63e1c0222d0e3b219d244551eb4e5c068b416d859f83d431066423b807ee3e2d5b80cee773a1991854c56ed703016f4febabd2159483ae1962fe5be771fa067ae8b95f4ad89f93141c7609fd8b9cf6b98c606222921eb9ecfdcdb06afa1cc2647e4d22d7411e4079904dd2fbd314f45abcf31e0707a6a85fd631b41fc3432be18c7b5b9b3b0cbb014de9b53643f6bc629ec0dcc82404247712fc62820618a6eb8d86f36893d7e199b678d2b22d8378246008402c962f9cef67db64c32d4231ec9553a7500fc410486ade3d8c506dc517b29972ae9e7528620bf4cc075902dee7ce6439d85a610d1badc39d7e4e118f22166eae2c0f492606f53e019c754df61e0069fcb13316b3bfd82e946497e97713fed435bba57a8f75b66419b32a435b24c907289bc1c6d527821ac4e4bf3eef04e9564b9328b82fd179c29cdfa6801a45a5d540900b894400c88c3790cb3260c59f878611d2881f60e00a3c0ee7ded90f971309c207caf1b8012d7872dd891e153f5cac1643bd54e51436c12607ba22d13e55c9081f2204bc07c996485dcacc41d794df509afdc0c00b4cf79d4e890dd4fdb9c19b2e1373f25b88d9fe5727afc6bc1ce57e2156e8f3c637d2cbb1826670252942df0be221d7495759ef659892bdc75f47c63cace6"}, - {"000000000000000000000000000000000000000000000000000000000000000e", "0067af1274820bfb2e16e1fca76120ea823e5e13b2229815a4cadd5a3f69a062dbaace24d646e73c6742151c1cab9a6e78db9bb8e59d497718be11c5d61c2f160b9d9ee28a907301f977efb9f9efda96e6ff75dd071421260fb945c3d2c5dcbb31f476ab4fd97ab81a1491c78dd2c76954e5a4d426edb110d1ec92bce38611dcc17d8a5435a6aaef673acb3e026215d07be0f0165884185c21819fab0e23d4051eabd209c7f11f040618ba8e4e91f3e9493fe2079d23030dacf9b415552732071db823633f53dec479315eacde8c94b7bb9928645146469b0d6fc0697324633c512e7d8d1c2c392ba9bfdf1fd687fce7a3764a45d79e22787ffb4a3c0a21448ba26ba2a1c3c5c27defd44dca084eba029b140e58ea37a88b2b50ff2a1a1162ab429d891d4c462f5ab9fc615ce14544fd34ebe0fcf8f145ad1200ca3f90d3cd9f2a90f178ee44e52a4ba2b1e6293933f303a3a54f7d586ce3a2263540a7caa5a5d1823035821ebecab9d531c019b29c5387b0e674362c4b75c06913771d4495da11d4db2bb2a001c97c3274f6b6c5f451378aab7462d0891c337a38756f092b127f1cc11503ce8ee245071052c779e17344fba8e72e8f3b1a4b2e6cfd9d104f4147aab9b397de2688f350fcbf568b0ace4c8abf45fc50eb07e87953c44bf6dd5f1775281f353bbac14adf29d3b772568f43b6b9b9b59c35d505bb02ebb455043f0b9070db025ddf02b74e9d23441eb0cc90f4cbdeab6e390a4530e99baabfaa9d84c716803e6239fa0663d47ef5ed5f338b3baf247dc78322d7cab715d3bd18c3fd7539d366ab8d691e1db73e1140ed5ddc0db729186ea24062e8fe31d2eadd2ac01fc737e476c95b6f1f7604daacb993c596c5fda7bc1740d66bd779115beceea175136a5bcdab3b5543884757247725167095ec41f7272dda8dd65e9e787ff300aeb008354a18e293f700985ef36bd19b0cbbc0444115bc05a11be26fefd2448741eb80a9291bd293c817854b84061ae038ec5ef393d37b950d5aa517f2ce2586dc265312de4974235466112ada1572962f3d821fa29e4ffe76c1b5c44084e76ec597e1e518f6ddaa245a4b25bb8bf5d10d206257f64c5844e2d6dfd1703a034424e56b78b987f594bcbd292fbef73c1db13c5d24134fbb306053b2ef07ce5256520292f47514d701a312da0c664097cbd2f1741aa7999583dc585654045d354d615b7a13ccfbb57cf95ba7ba5f219587100a602b30894cbc4ae7dd464496b3f3ea7675d59f072611d3d6af91d655fc7d2635e4771689e06098d5660293e6905316dd9ff91c103157d9879c1ef7e879fd066f011782655de589c4a119ed1197e2313cb5ea640878f0bf2564ef47ee57391d0ff83826848f5b59201ef99f409b9c98b521015682e9fef849b6701164a6031ca11109954dc9f7493266dcd8d635f7b331a7399f3427a84cb4a2bbdc0d09fae852ee167f96ddc2a430214ec29025c14f31b7c350b734959b00f03ae4013e230e5fab29d283f9af487ecc6016a6978f3c49b803fbb795646d79e7d6a4b22c7cacafcdd59d15943f0cc589f193455e5e9bfbd7ab2aec97ea7c19bbab6b0ae0c9b7fc23bc0f2e05b1f9acd991962580d728202eeca5aa104fc80be8fd886c43e1a7476c565b82b1050d75cb5147a7c65d6f12a860bf938daae6b6d13e23e717fc291b34f4e0fb58f42f6fb9030d637d89a507a871f03ea86881d6e04493d9c6c3759bb6f75481153472086f5e93e3867255f5e64c42f3488d1c50f5169767f29893576dfbf7f23767b6abe54fa48cc6cd1de60bb07e12824578a872c2a7538ee0b45f4fa36f397c0bf8bc2df303d76604e0f4ba56d3844a9eff9b4b079e97a052d146c18b67aa0945626e44793d38bf"}, - {"0000000000000000000000000000000000000000000000000000000000000039", "012f3a69fbe4852dbd5eb7ba5e6a284a651174d15e0806c99e6517e048ef61516e0774b4353c1a36693602d5be8037d079e5d425b1c61023cc3963b578da010706fce809dbd9ad849872bf65e0da167bccfea7b50ec5222cfe20b355b05751b1742c06b9e7f7afd519328aa9d494d80c2f5503a4af3d732c11db83b6e4bd47f45d303156e81ad54fd78648c889d6335c1a523b51363faeae6743d97ced65dd1ec4f0ee16e672ff6b02ebfc4336596e4cd03ba72775528919f41ad8d5cb0752e53b5e8c516cae6ef3d588cce01501464e0529569403c0da35e4cdbfe0662948b5963e036a9951d373faacb8872062b97ea8685bdb6786ef17039a0f280581ca5d84817511a54d56680957429aa8adf955aa56a1569bf06704a544c95742847e2835f7cd3a692628ea73366d9a11770ece790781d5db16cea29a988235807fe841a94e8bf8cc6405f5c454ca28b21b17350329c74c772048fb9026e5f7cd45051dd1bbde75ab0c98f11dc116683f00883a0f3d59a7d6fde0186cb623901f1fba687819e980e63f0cfb9801d2e7bd80de30e25d87605812a6cebc8474a17fe9ca41947515b106165bf6e95b322ffebb44cc944abe21f52b949b341b627f6a2d5588a9dd7492e8f3ccdf8142892f23171d4fb36aeb4935bb1eda3726ea753d7bee277fb7d961ddf71a91aa3b71f1e7e694cbc82ba9cb7b3e423a04bf0360dc32dd91f8af30ba61436071410e1df9ab0824fc93648bf223e223025a5cc724d95d166cd14b0538664e4b1950975dfa2132dc8b6e88cc1e094fac05675d166d9eaa8bc81d527bf135d32d6af4dc36410948acd47e14e8318804b29be99e49621779d557601571fc257f2622395ea152bcd361ba60eed8ae4e050bbd6a57371f8591eca6b2f46ba2ac7519d1d7eb1513d68d2f7c3305ada496e1e95b1a346b75fe1ff5180144dc03da8efcfd8b0ba48aa0504257580ebaf09b03c4dfdf29d17674cef1844249d13e05583d31260f05dfa686b484b69e87b851d77cf39761eab21bd9931a4a342da51369f5362a98f24849d4aac8e5ba4f24035ebec9c9a860c156b4d4c37ddfb23e9779d6ab273c6c5dbf15d500f944f669f45a6d7fdaa28cbaee121a8a7a06418e41eb66f2a63e10d16971a7b9f8b9b2218c4c0951758e0dcc46821e3819493a2b31b576a501ccb670d59204a950a551519792f610d5dcbbc22f193d7d9537b300fbafb3d5f2d2618e71e3fbd05d480288741be713054fe621530f722dd1f24c409566ff1ae0894bd05f4260fb29c2e176f6009122151fd4bd093f27b0ff20b6dd594c919f791c69ca5813b5f45c20b1c96dcc4be72723cec5a4d148e155c1045b4ce3177f894ba4072083c46411c41d46950ee9167ce6eb3464bf75bccfa90cdc1b5352925906aea5a29bb3070291b2d89561869db3bb01847737f14dcc21f22285419e4f61e01636cf5513850384488ecf27fbbe099b107617064e18447fdc15554b2c54ea8e96745501892a51b67c6cd95f7ddad5e8788df84eba9cd75e890f05c995cf62c46be46bee730c6ea3aac2e6d0bed9bf2408afe5ceca4e0e752264dd73e69672ad3cff6f030702067cfae0a437ca33e1499ab7131d505dec46741553b0cd3fc9ac365c5f6802db4f8862f5f4797282030d565bc66ca0a790c363ad9d6b47816e780e32d317aa82ee1e530c730e3bc59cc274dc7199476ccff72a8efb574fe5ea6bf9bcf46d17d529eb3988dc72934d721b7de7e05765cfe787242fd0e80746551f6c640ee6dc640d4d14da99f5a6ec655b004e60c69b7744303d04b7d8e3ea5bf4526872964d635304379a0e7d221f7b7721fddd45f92af259cab3a81287a2dc0530287d22b30492b2bb1d354481b2323c4ed5dfbbf0b5"}, - {"0000000000000000000000000000000000000000000000000000000000000005", "00315eb8fc4d578682ddf164933903e8ddba1ca90322f7a5d326700311f9dc82d0acd6b214c8d6ebdc6807f40d5b4943e079dea1739d4aabf5e6cd061727da14c0dfcda695819b360111fb9d6649926fc653d7ee1e1e1c6522deb1ed48e6f46f127e7081ce2a328291292b9d3162afb53180ca2706676972ce4439fcf0ac2634c9a337ad5395e1c9f3e1832edb5535b893ad98334e2fb747ce0e439187a77b674c0f4bb95cfdf1b400390cba29d24092bad472149569b0888547bfb7593e2506eeba9397d8babd07fa834c99fba158ffe4220307dc03d0c6e02bcd2b324cf11981128165f61a9d0b137a2edf698e1f6f84b2a8823baa6ece225b9f2d0188ad9873957daaea3101b883c812c999b7d9fd5c2316155a939fed619c25f32702bf29f5cf103e069635ec139b6ab83ecffb0d93d4f4a6fb6f1eb9df5e62588acff2a3b158fd9a7a477a6cfc63663c8ef70cda017eee67e9434db76ae712ca26a08a69217c09540d6f485c7fafa2da0fc7d317567fdb53e62667fd02561714299d93deef21413ef58e2a376a39ba94b0765b352dc1d46bda765d6bead72426ea0eb3c482ff716615a23186f6ccd37f798961c70f5bd741c715bd6ffb24a0c60a291f2491db12a7085653f836d791ff9c572261a51f68583068de670647be384a01927d5a555236d07eec65537659b17eb4518bca4f6220aff84eca06956084a1499bc94b6d31978772fcca4c663b844d254ed3bae09b900dd61e95d12d5eee366c3b5b237a0d6e40a7c115ed8beccde270bfad2ee5a61079b36937994f5288235b2d2b01062eec454769b92dbffc4f12cda572da6f4f0fd9c182f49ebecfb284503a46a726d67c340a6bc5298e2435b2a7f1b0e6033e7df2c118081b45b5cd517706c3f1e83a7066b54b003b5a1c23a6df4ef1a0aa55d3eb826b1e2dd6a2ea8ef964e800fbfecf1ed41fb585ebf97041ed193a9f5e5f55e81593c77563df944f42ae427466739df93fc4ae1a9d1458978461a4220d39d0b2d11d2d7bed2b359a2adc1514c5cf4c559135265473030c4db84f3322bb066f05b2a04d2a10ac0183a634739d420cc54acaf6cf4c0872dd96258307c4b32592e50fa8184a2626d19b0a0b02447d55d9797fe7f6825ef7dfd05938081022582eb33eb77c270241b97a157f1af8db01b7d358687f0229128be307ab1dc21493f4c27817795f71f18f643cc75e7d5451efb96b9a264906c7210337195a226110b078b8a1c7d4885fd35a0f3a7a44f2eaed3eebd434a2dffb1d11e43d2a9d779b106093a62ad8b5043706d8c08b3c4d5c21c37c32b5c6c7bb7cb075d9452c2ad23feeebd5bf1d3a8db6b8a46a9175f95658dde11b5d7d6d8c14a09f097be7bb54f8102b21a25924ee8982bd9044b7773fd18bb89e39775952b8b239e26001216a4333b1389de3cce0eef196175e1783d1c327125ec521510af88d21ed015ee8509a4dfd7d50e0601de3b1fbe1c797bb4ae162ad1ccc0f8ea1d9fcca9e2c1cd641554c7f417929175036498a1f712afbfb44053768d6dfea7367eb267400c0ebf58d121196ffe4272e0b0ce78d4201488ed6c962e1a8f667c2f76655056511b13a55a6bf63433119e57e6e61f60b9a43bd0d7cdc4ffc4cb8f59e6da62abc423d46c2457fc40f0156efd3cbc7d68ff5da24aafa49b709cbf3198d894483f22c041998015dc26797d0d0b0fa1828f88918066f0c581508e50d8b9c71d8332f061ce6037ea1e52950ae8212a563f9bbb148869a7a3f1287a958f4a209be299d475f9de3b1811784c7cf7b6ed7e33cc2d90c7dff20fa8d9a70da2089666b50667370bddec0d726897e1b99dc54f0f0fe2a84b1faccaeb40c1d6481380235f33f97bf21c3975556546f73c1f17e74476a"}, - {"0000000000000000000000000000000000000000000000000000000000000009", "007636b7d700cf6c94c7e31e417aef2d13444ccfb5012d9514e00a02dadec5c10ef2464f50adadaeffbf02c5b1621befff41b3eff8c38dee732ed868f958bd1e60366da214b0a3ce14b287a6f772c2c644b9b71406e74f2e43435c79f25fd145820f861ced6d93d74b1007e51c85458d8b3a23a170ab1c13512da397fb320bf9ee07fc243d37e30f26b0e4e79722a4509fe2bb18c586e495c8fdb9756fc1faa8c1de16a6dcf99a020a426cf96449f67fa265a1fa2b7bd8f559d5d7f29e307cf58129672001a3caf711123e07c65eb17d63e01410742cd29b807ee3b0458370b9945e82d27d70f019c3c580b0ee1ae79231a4755ad1e0b581ac33142d0d5204876de25717eb8401e545ed00ccace65cdb2a6dae05dc10a1cdf1142b075e36501d3fae3fbfe9b20e43992e0ed01789f998748f0db7336ebe705e23292a42ee01aad64a4f285b9ca21cf2e83f30e179bc1802ab3ad655bd0d2feba902831ddf3c451f4fbec28c1759b79a7ed6dd4ec8d9b638f0d11911e8049cbe841094e20a1595660f2d3a814f48226b0ebd4f7a8bc2406955574fa1a0d78428a9877cd10d82717a7beb0d05e4d3180f975f4bde54813ae42864a56c8f9632520a4452f91c5e80a7674e92352b2f0b4572d67c23841a8a9a35e5a28faf50f242d4d8bc4c85ef0856d2049b5f0d21aef8b31befcf49ddfc7e1836bf14bdfb500d21e9b88090fe0738cd51e8c1e46aec8f5706b24c196693a1e220f4cf27b3b4f648714796b1c0b66faf4ef3764fa913ffeebd4856f1f4716f4e9cac598f2460a86311ca633d197c56573c2a3d9873056cb8afe42a1cfd7772f108e5f46962d9b872a310cf7c9669d32e19ddb88c5a897de73bb8042d5990aa74667726c337b1f9d8a2b0a7f9ae2f983d405535567b68b9524c427b1ecf61148c52ad1a24c9c53484a240321a655a00c4adcffcd19edb6d470616804932c7d46f5ec0b41e6f4473d0626c87c360d284f6e19ed4b73ebbe7030a9a44c4d7dbfbd7fc6c2267a72f2b3997ff5f965f1636df70e550dca9f602625b9e9c5f50aa557e7b4c015acf3c6501b3a3fe33a2d964695e552a1c39781409a52b184bea227bb9c4c1431a784b14c530565339082a96edf349a5133513c514c867400dd46a71937209ca9cdd704d7315432ff55157b808031d3cbc717203a0045d07cd1d91f9d1a2f3914d00f6309cd8394a0b4ac3b1fa8e7052f8207221a1f6c2d0cacadb6f741263676e1a06ef96caa1d2ce7c51c4f5aeb1d4e78835f6c59eaa9ade353e14056a6733db224476956caa0cb44401d7c47a1062682b32ba5f8af75e12db2a091da681b6f9a4b7797e20d29cb1f4e43167ccfe21e61868251adce8cea7db96e2a9ec28a7a4beefc74d6232096f93ef60a39d1e5723f512d0edd6ad233c11a0030766fdd0e14079d65201ae922bb1e4d8d116a2458d12f73288b2068baf792b8ed9f5adcf214a9be69b152308aca00f0c5d2555a4b27e48aab1b09d3118db3be2cc322c5d1a630e3f742b6edff4fd91def6798111f4c322d395d12dbbbf7854b7f2d1ce17e83498321534e503b89bd009c5ccb89b865ec91b5b913c040a3a0afb5788f603e1fe95469b1adea2d9e4529e1eba561d4424be7cc3f5f14cf89ca24e97bf56dbdda8e503e23e217e5869a58b78e9190a7eceafac2f5ddf910fcc8ca9cfce3bdf35f3c5e8785ebece6fddf4401c197f092b8c1688d734b5641451fac9e9d50df441172fcb5ed111d561c3387826aaa6bb84f1f43cdbebb7071ef3c4cad2d9b9c68985b712e45ab99879f199681001b594eb875b5be6d42662aa72def9cc973d40e70999f0e694d39b5b94eec596e1d2169b15177b6c7b0cf091e32de60059b1dad6a613d87689deb45f5704"}, - {"0000000000000000000000000000000000000000000000000000000000000020", "0048c6dacd48a4d9091658d30050325257e636f68003a573707c08ff046193a08a75d2fceda084f4a8830ee75c5bc3dc738d3d4ad175d90e2309a94e7748853ab4fca24ee3b047ba85a4b0782ebb71bc19f3827903c469f670101c8b084a105d04949d9a17a91286dd26841134d0e570a762ce32e5fbc4a636556dd43b102060aa00358c84996579746863d8de8bc7ca1ef1592dbd54e09b60335bc018740e89b36519f3f01dba4c063781f8f08a040d970da2128036ef11645b7343631be849cb6a28ef7b7e15d2343ef814a6b3f65b4bee1f7ce2de1ad82c62fd3f32fc11ac19ef14083e8ebc34958d26a23894c1e91939699e6f6c7eeb94f98ce5130c56b50c8f7119f17ad33e44bebf662c5f742fd72dbc7531214f28e5ae02a413afa4f3c190b97e52fe1f65f29738ea0fef8baa7586c2ea538198993c6cb43fd947f2429a61c3fa3fb7f20a60d4a2c9c7dd503202a972101d22294159b8c20e486d727a9e50bb401e0ae7bcd1a50846b09399817760befd91556fb971780c3a3a2ae7844660fc3c351a7644efde236d9cc6c30f21590cc10f9767a90183489f20d5ae34fafafc1f0541e4645b093ac9893dd061989cdc0e388993e1ff2de60387c324da8d9f2dd53c0062904e28bf92069e0920bad3bc928576c85aeab861e8ba7720cb99a10b456faea4135f677d1e61984a2affac0704e898403203d38ba5c0d10c1d1b8a03dafcd2cb4562da7a7f3c42e47ed5499699b13465c51c16b3ed7759d7dbe52413dd5160c0237b058b2061d1144fb96228d377faa91b2acf70cb5b1ddb4c68244cbf3e47be66677e79920a8a8719df35a3d7fadbb14398905d8c56c7d3b41260406572a8db08596a7b2794843eab71fdb21d485226eb2b213d17b75151ba6456eefa0989f67c99941d2eb18feec1f3c0b3d84fd4b78fcb86c1f536d4f207017e1a87f5f0c795a70293d42aad0dae13dc39947c12f49fd19ce045510358e6fa96d8f0d38c52bf324b01b51bba9c13e93f158f51c375ed57d146de2ec9f82df9fd7c4018931994be34b622eca4093533104f05045d154dc16be633729741a9c2bda0c5b3b372451a07cbe901f21838c6da14f1c88b5d04894959341f622cd253691b13ff457367c5a641bb363680e2d4a26940bd3214205a240b5f1094afe0f7d1ee23b8de70dd01d47b495c5259f18f47a8d1f46677bb33699a9d570c79a52076d20535b78b62cd00d1341337517ecfdc0b31870d0f8f080cc8a6839c4a305e85960ad0605c75763cbba12ca20de78fe814a8c495eafcc73c1c631171b3128b10ce5897dca1992a76cdb767eb7c718c16ff9c738ba9de77f86eb2593861e7255062730162172581795d8606f392fb127975a34c54babff21f774ee18aeb60a503e943301a3595e66b6e951dfb6bf0051d7cf5df66cc2d3c2a20cbedbe99c915e9790def60d6bc232de181bdda7b4766cfe9859ac8cc5728743447935479173b7f311154f4f13391e99d136d4cac53af5df0a0d50faeebfcd59228609a717aed6e5084167c2d5101d1a5414263da16ee6a9f4343f8da599c28775a70f40dace09b3cc523c866d85f83151d25bc2b6b9e135a6ac4a9fb18a34cbbfbe551f93cde4ceb30270ca676710b199f38e565ceb3f39affafdbedf6055cda63f1213f237a0757a9bdfbee861c6f56ea4b0c87546c0363109d7d7254369be4bf992ac02c19fd151107555a74e219b066236a58ca47d9cb13f9f3171b6c452fce518a05d1d2a371fffef2669aa21ce0b110cfad72764a13c4d78891f983d76b04c9c2ebdaa24bd2c2d71fa51bdf98a2f932dd56f8db62147d1d3f27312d9330f1ff25d9aa8632e4c72d2a3867b7232232a33d5ac45be18102756706446302ddee16791717"}, - {"000000000000000000000000000000000000000000000000000000000000001e", "00300031f011ff93ca7dd320f7c4aac5aa695186612c84a6bbe2a3a09557eaf59cde41841672d8fce0f0081d091bc5e9150bbcdd63ceae7c7b2a5828bb34072543866e4313e4ff4a1fb551574d401b030519157103d40974ed67696fa5bfb67e00fc7e15dcbe9eb41428ad2e80a5a1ed3f97ce93b8a3593dd95614d4a3e715de2dca770aee7e64ea9315d72c14e0ec924af9084e3cfad35f59d28f58e4971daf39d19e06943e08db018ea7681a18ef25c13c1284fda08f9e88fa1485820bfb5f0eac973d9ec050f29a57175a1654d6fcaeeb172f67a993887ec1f79653a8baab2bcad739b6f5652c15c4426e141138bca656c0baffeaddd00c9a0329042101432e1690f6e378313a6d922ea15a05afaa0b20ad295c3f33ec91a08483fc2fb10972376ebdaad705c574499b1384a940bc11709bf32993c7661fa36b09544336efd1db17603812415f6c6755b4cc754ad103c97186b533e459d6d8a26ccd7533c3554adce1a71805adf7d8bd09f1efae23057b5d4d3d84065049b3088e6db401cc4731e309b587f4723eb70058f950b51c10a6b69c521a5f162564b20339d2da84945bb08f12b07a62ff4a7653f677a38f0a58cd82811bf61d3d72fc052bc56b6a5f6d6a091feb6f56a65d14f6bfa324afc756c918417ac3eff47dd73c00a97b080e70ae513fadeb44a6572d8c12ca252ae0eb92ef01da8b7404860921980ae093820281a33dceabb91131d066851c56c62057716a0bb8f4f3fd55549a09e56d92d94807003f83676accf556e423252ff3a3c9458db9824f096a3b194c83320c61b860a65948ca1c9a9bc8ba310ab74a8865887c579808145d11584acd95689bd62720c69a1111d413ab6302325b6d1e66ad46e97de6d518c9e6baf8689dd9fc5232cf1cc63bc507a6350a7b28288fc177d7a80b0a63c68cd1df9a631d64da6917028743f628e1abfd3f84d2ad40f3ad912cc2ade127110904d17f19f6f5861bd3f836dbb0a50d9f10349806fa8c6666f464d9ad5aa367442ad0e51be5197af32d9f472b0d674093473e23d84b4858e6adc8973f5f21167f4085d02b84d4ecb3a479e9a43de9ea3c9aa321bdd533cd5fd0bbba2912cef534a0ee3ee6d20f372e9072a6e5a0bc2599313900e34a79ee95669e9d2e66164e1ab79e25ab1968d6cbbdbf5b56137d1675cc03393ac7f7489686b30a834378c2e54d879e59ec251e539ebed55cd1d55eea28f5485418e240aa5728720fdf19af79d01607bfd3668c003893b3172adf214f139b70cf5a094b14bbf0c1a847189dc4ef8d89baaf1178cedf29512ed0a69cc319301b1c56fe0b5daf521d798fc908cbfae19017f4be3eaf2f76cc3dbd653529a64df493cb0c55241502d8cb57489cfd7ae8738248dd0dd613e92233ab4504aacee0e63271fede7a4b0320d245159cdf795a9d736e4153ac01b578559331033dc56252f242e1f791a69f9c55cd7e2a4e718f7212cd57c098a8f929daeb82eaff54634936fa1009f82bfc7f75048fe7b3c792a38f90540555ba12f633c310703b955c5921711e0c54dc5dff0319a5d7f8db931cd867d471257e7bce94344bdb5e779595d4ed42f316974248298c9410bdeb38090d7e67a2931f59a1d92968115aefa041b1c6fb050bb5bf3bd5be555ecbb307c0276f97d8e349ba0cd559d86f02bd92106f592e26c643da125b09b56b79f672117e4a05b942304f970b05db9e17561328cd9f9204dcc76b58bbe26967b7251787b38b1272a993be33f8294bf30586185d95e90bc80a12c94ffb67330ec986a5532d8e680d5d0499265dbace7e1282cf5ab785f1e3b3cdde8df95e6c6f142ec6a0034fda19b67f029ad259375d6187ece3132bf754e603e58f59736c842f6423eca9160c3713ad"}, - {"0000000000000000000000000000000000000000000000000000000000000008", "009a41ce877363d1ee5860fa19102756c11b5f196013bad670a9eb746bf29b344ce7fff419621578a5451dc5c94ba25c01ef1ae46578a0d1796999538e97163e9867213ea0737f8f6c19f6a97803db632c1b842e0166b525d0d7b8a53cf4103c10241afb23d0fd6ebb903a57ed0eb76757f74d19fb9dec8dbabca25b3cbb08ad89dcd7578381af0bf257a91e8dc0fa086c166a2a2b74e8c1fae651e2324567acd5281643699b77da00b65ebff019c4114ba47485c56642b21cf2d49e7e1d7f5641d85e268540fd653f6144c16d881fdd62de0427b751be0e7d70c4f9928381183bde8e729ee331131b87503ae99e99d0c7f1da03e0e3a21e0b741c0704327247cf9292a76062909ca71c92489f59fc48df20273bc6188e691999a2c49c96e3f61dbf866e457408d6d85aea628a2d2efac549dd64925e8f43fd60216d6f067c60fce977fa5c06e7e5ea54bdd13072aec200f493b60dd753ef1fa61401b0fd60337b47fd9a19059bdf531485c7da59ce91a42fddf169224c2fb6310192e5a81ec76e8fff2091f4c0ef42b9a37a921a2e2db1841d32d82e0b06ce458d2f44bb9a47ae34f2be04427895b4c76da145f766d6a458f46706cd3faf4c35a31487184dcf736216538f0476bc29342c6c4ab90ac60b9314231241b630d3c3be3c6bdde93c993aaf2cbcdd117691fa9495dc64a2093f4116e5315d17fe063bc65787de0fb7822f7242a45b35963d865cb7b70e3b74df7d1fb6c7b1da9423d0a96ee3092fd8639b0725d31f11c4960f502c9617b0fe4c61bd093ed4fa232ce26a89e0490fda79040965726b42455772d1df0a3664c1db346dc3d60403eed047325dd70f324390165b4680be05fa23eab23563c43c456196ad109dee0eb7c9e5e550b4f7dcf1b26676c24184f69437efe96188ecb6d1a7e145edf657f916578e0eeffed9a03c01549bcc6c4d66e58b1c1039d78dad56b25e1c529602b5fe514a5a3ab1d4a835313cb6072a2e10b2c3680905c523d8573035255e9223564d0ddcf1dfdb5e071f89d27b2a8d626cbb0674437766eee9924daca532031220a2aecb3ba38d21e83950f77cf659b0dd794c222572eec38c6db5716d14f66ab4b04d7f22bf5cd605566be496830e7eac9331d33eee6c0d24c06d6c7315247673b055699cfb17a37951f23752c59a7e03fc01ad904a5d026fa5fba1d5c8dc505ed5e85ddd1e72403a8e3815e17e6b77d4757fb1b109f20bcdfdc98023bb6d00edecbefbb3b13341c9b464829a8474f1712ba1c5067d9e0643412222f251b994d528d5d2c755150c59087115928927c9324df8d16ddcde404fc7ff269ac7c2c6951986d27e83910ef26bf53597ba1c771f370b7cc65679e79ccdba86a2f701beef2b9b5ef12c490549361e44e1aa808304209eb49b50135b2a380211549cc346d434bd4693304b307749c3c216a130039d01a63d5314a7cee6803d1c90cff04ec71d00410ccc5584608f5488b393334961426969ec64df51b42391ae7c7673d517fb271983f272712a64d35c3e7805b23b1d4d54f4e5a2c4443f7bbec389bf55d77990116516af405cf5672cc2b236185858ad4f2b2eebe81661ae0b8d4dd3b36e3633b36f1da3a15f8cd7e3914df16cea6af9b567cf3e59e4eaf7998b43dc5d9a6203516cecd1d85a83e520f20c81c50aac9d437509fe0742296d0fe37d51ef0ca12c804ec522fdf4b896330d1b4448b303a2cf229b311503b796154aeb3b36f10de522113b0bf8b8935d63c097bc4c514f26eabd9506873508b38aa3fbee462457c3db94671a8b1b132b2752ab64615178049043e27a9d7992a57090ddc8ec0f4f44ab7e1165dd7827680d41583696f5259ab34a154374742a157bc5a74983e9f5a87b7d2dca0fa2fd"}, - {"0000000000000000000000000000000000000000000000000000000000000028", "0028ba8566cb549b6729ca31366c4e57ba4f5fc0c21b57a1b9aea04acf8f06a2b9cb1dcf8a2fbaff1f8113822bb582922595a891ca07e9fd0b47b93c1fd8b54894e5234e74db73a88ab67ed5e815034cb17dcf7c059fc192e4522097420c39f5ccea3f6aa56adda3f408516b43650f6e44a5fc839972b29225cfaa9f26e01ca87585cd9c0b80f601b463d0aeb1a310fc1986553820875eee1dc0dbd668542c5b34b7a62f9cf98727016c949faa1fbe29e739f785cecc811b09191dbbfa0dd4636ca74534bfc7ba46a154f43fe6e4bbda100303fb5614d5337099c2f8f39996e54cb1e9b8b0b5414bb532c075a334ada46dc72d06f961e2e2c297bdd30f822b2b1554b3fcd55bf18f182b01427e179638691cb60fd2ffebc5a3806d74dd8ffadfa180bdf8dfde358b5ba197e1b04b460644202f57df152cd4f29a036e6cf56841a43887624a6ccfc9e9c3374f04fddfd6007ec81ff216eb4f382d61d97c26a8888d9896ed8a45240f27a3e475a39234169ede4c894f0be7ded8a70093045062847e03bb167037c2952a425e113530643cb8d2bd2074ca61bdd6a47a9abd6c917ed90c2bde0e930ec53a15b2099e304101a6c2b64058cbd750be146989819fa7d4d3472536ecc04fed49bd21d9d4b118c75cde96d4ea5d3f3bd326a63620096bdc53305623437c2ca88c933481ebc64c0677f0af7afabdd6ab01711574dd67d369ddb531215dbe7194f0e29123023e5923b5ff597553f213249655654301a2a12ee3fa0b58f7ce668de8d3150340c9503e77f988ff9792f627889ea7d69fe7abb38f82a19f6a6ac1876c32b9380c205f16fb13809ac8a4d1afad5806a57d13792f3515e8996da36896a15937546a074f02829265963cdd1e30269306cda0f886a5c2686f6c4424cd4856a1281ea79d9e60b165e5949c42c31d2218199936f12f73011e46133f40b3b703b76210b8641d593c6cebbea31d3bf1a77130a841c162657386b3622960a25a02780bd9c9b12c389707c88d64cdf8e6cb222181b5403f17db4a78568eea5dd50e3409b12753817dcbcd8cde06aa2712f9cf813711e9418cc09dbc00d19893bbc008b0bbb2bfd01c070d35a58d524ecd5ec4d198a13609de852ab353023b768183e100b74585204230eb4742142b7db65ca69722ef9534be386795615d5ea2c2029d7a5902cccce78d16f097272052683ce85fd6f22108fd4015db5a9b293ad25b06c10200e4c5be233b05b6a11b29f1b55ba9fa633576c061260da81bdd2105d297c72f2d4c91a48945ba206270c3845b5cd76518208a4ecae4744bd5f45283194900351e5a8ba735418f3dfb9fad92dfde64d5c22eef5d897a00185f671d92c1e0cf6d369dadb676495ae5bf5368ed3bc250232137c3884b304a7104f426c9e68565e906f20d9209b67eff681971d9b89151e59e335cd1600810cb3f179c3f2f6a208ad71a9f94d74cd6b4c94d39b465b30d53662ee1d04d8b7cf87291881c0466244979112f1971cedd2e86d25b8441626623b2b9c94ef6ddab720f62111d110edb8b22b9b1905aac911ba3c41f3c3b17e97f0f791e72e5a769e4a12f79ab5187c63e9f2b11d9c3772657165f49a0f24b867998812d085734762fcf0e654ed2e0e0b4bb245dc838fe16e177beb91a0bbc8b92d3ccb79c850251c02277767879f0daa3b72d90723cc590893adb9cb4bce6bc692569682bce622170120d03490ab5991ea375917305023029d8d0f24aa13c83343091f996ac54ceed79f08ee7a5f9fccd128f0f8c6d77976fdd388172fcdc4bdcd30eac49b514230fe28613a4abb139e521bfe9094eabad3e0b8c1d2ab515360caae7c29573c9c0310096846999714f4e523e9acdafb1cda334088dbc6a6262c52af7e471"}, - {"0000000000000000000000000000000000000000000000000000000000000000", "005e10e25ade6dbb9f4a30b487a53178fc6ad3efc50ee83d9b18885575e3ca08a76fd1688251a41a89db13ba975fc245f79b735a47e4f6ee7a667f077ed01356dd935cafb0dc59a47ba6220dd8e16314b21de9f70a70b933d285f510f16572d5973480d8e1e17ba27528bc450faa0babf4d54dc67c87cd6dbf5bc1dbaeb22012174efd65bd83dca2058214d80c89b3b91816f22ea9ea866852a8d6e5da2c597071bef370857d3d2500d26cd1be85c1e1131123c9c4dc89d6bdf95c8f2a32ffdd691a8eeffb56b164b1e14e9e89524f14dbde055bee5da08ee4415b5f04806cee677936c02d61a52a4454218e1add637c2ac329c6c475f90c6a6ffeb703c9553e6d475399f325a21735e6272556c898ddb413bd660f3250540d873b63be28dca85288581513db0b492f213945009fcdb61470e5f1f80595cc72c05924671468fb9b84f5d24cd4c4dfbd456a036a590cf102a90bcf202786bd9c30306d3a16179ddeddf126c42d353c774c2f53d587b8392c0b701fc71c98bfa3ea03387e75fbf02bf9b11e13cce27f33c7c436df092224812db4ce0cad13e2e8631c44b4b202b4607691c00df8f677a55e269b88c765520cfa148954bdb31c88109efb58c3d24b7336a415a7b8398675de8178017113375e3a5ab0f7cbfd5386d09dbf5f7dd3ffdf98481555cb3b939eb80b72a8f25cb37b5ab6220959f15102d86b551d41c8e7ebe6c15cbf2538169fcbdb38a8075e054618a9ae238ca5209bc64464b20fc4f6c8701524832894dc742fdab2838e276164a29b71764fbe445bf7ffe5ab3ef3ec77c7922cd6cdb22cc9f7a4610a782ad2b884d69ef9dd661d6f7a54bd8e800d55ad0d2bac272ae02489039ce29d00d852761586f23dae30d17c44fea793696d8fc4d969a9f485639bdfcb2d3dea7bc0d41481f556b8268f6ef2d785c671f0315300d9b52fc41df40b3412601268e553fd113629672d05b8d2b8c191f40cd4bab0f2f2f0e3d6a8b9d5872c12ca16849d5731a6cd097422163f40c92966ab29dd1358fb2bb8e555bde2dfb16c2eb4a5cee3281cc21509db52720f2f6a95fbb982c136a37ab1fd449356dc1eba6e18f434d529d68cf3549eebea629320b50f242fed338760913e2acf1c37252a7bc8c6a8903d31f134fb8a10ffe3cd77e8a6f3fc9f4793f5e3a439cd86063fc56e999d902d225e33bfd577cf5ef232de9916096379b0c2505ce0e129f09d6b512bc8b5bc7ff9d40c6e0f00f84766072159d59a5d3424ad846f79eb0a14087bf5dc54723ae7a5718f49c20df6baa296f0c312d92648d78805ed5f7e52b7bff3f0d7a767defcba21b65b5eeed6e321a320b98c1bea1ab26d111d9ee024d3d132899950f11f9d54d0a42a84b21db678c19c27ba84f8e259f31ce1a794689873c98dac7c6d716301e9e69ec5cec3293f9912e7017a33f0ca6d2a0d7a0e51fe159fcbeaaef1042110a2ea09812dca9f579d18046c36611390e5316f64a622eb72efc9db3fbb413fda829caa558a91b0f2472ba8bd7e66fdd6788bf10389cda3185bdfdb2bee717c4d9393053519fa476f2f5aac52f034e34fdcec84462c3e51a67af39580c511b29ad2b718bb8b00d2e48b5c3739f5501675b2311f9daaa2ff53f0cfad70b31c81f49e8daafd92f11502aba187d21f641f06d7355286543aae01ef156ffc111f630a88a3924b45c1d595273892663ce79e477c165746b511c744defc4e6319097af15d56d397700b37f85f627e55a196ebbd743474bedf86365457966002d45e9374ca10a67827a381dab09dada93c7743ab1569eb6bd71e27018d44f529d535f49be59fbf520808a835ac471344dd5ef919d96e5804cb6effdd7ef244214eacb89503830a4846b9f455e0b9caf55f1281"}, - {"0000000000000000000000000000000000000000000000000000000000000025", "0013d902acc168f4772161f59bc274ddde6637cc660929fd408d183bc3da498864e072507f1cbfbbf9750e95cffe1c5ac0dce7cf512f964e5a31b90c8f18631a62aefd22dd34a9c82564efd24020f1b02ebea60a1820266102912491304fa22f0a67dcde78bb5583512fe99c6cce6c0779796313dc4c663d3d402f95b3e8413cdab98ad7c4158bfec465783d7f31af2bb143af415c7e13a2d0c332f63955ad11e6616b93385d763f00660c8816af28d5bc0613faacc54bd33b3aba3129080ffb112c463a25d2c590c6c4e8f5e1d2fe3c0e0e08ce6bdedb250faf39a79418e3bbf73bc2081e579a52ee45823263d2cd55aefab4e156202eef22d7bc320843678d0b0eacc2fd5790a7ca25877c4e9fba592b09362bff07e5b6b5b258e3d054ac0031166c6a7ba40c19e24e08cf2f53d1eb469cf6564072e1c35d1fb71165ac2fa404a80f9ebd31a1455512acb403cf8dba0182239a24e79f87f4c8f19bf50dd2f2ee5c3d299a11f610b2f8e84021986ae172cb32fce67d90bb559a06643a5be82b4c25a799e797acedacadf07651c54112f25a5ab62e244ff33f556091dad5866f87540a7213455c5bacc7ca26c73d78f2e4d4427295d216ebd4344c05dd1f510e1fb17df5b5da507dd676a334a8c11cf02533360e01a519fa05a6f33a644e849d17e49a29c52b414b5e9b34f63723461d3e4a02e0811f67c706bf78e0f8cb8d0fb06f61809cdf706534287343cd0c9ed326fe98f1def93e2352b2ff19dda7abf29eb318dc36e5d45a6d9cf3e352c0b9965b28dc424ae32a40cefaa1ccdf1f95a90dd425c2e7ac2670931afb1308d3e4eb861754492dbfd554cb470d5e9ac5b9fabd0d18164ab45980037e64d15d03552224bb330be845139b4f49491becef03b9415388986d6893ae88b0e616a60f5a5da2a2e7c10276586d6199de9a521dddf700ba683addd758b312c2509d4ba13bd46581239d8a30ee11b271a64711c80ef97802ea946a693f16c3060ccb85270de6f043495f98d397df04163b095b830f2632d7cbb8d0d3fff4c39d46dcfe58d358e25f3bb4132b26d88a06c1b3e3d7a32550301d1390663f6c1823cd3d6735ceeb6ad9fff2dda1f35c011306508dc918fb6f7466e81e577610646f1b70eeb649d95f4cf52d99023dc0a73daf50f927b41860bf4e0335b6c776082c7b66bb091c7d37c718a277c60b667c61f6cf1423af2990c2d840bda5ace5448f5a93e97c19ff4fa2169e8f5e19c609384f47492554f10552cce5d78b9f3ea964e1e2591855ee1c252337d2fd4200339833c20c1e03bf1e852a2d1691c7cebc56ae0ed27d59591a1a824365dddf6f27fa76b22d6d15945d79581631a322e69c3ac4d80b80f624c262e9fdcfc4a1675832335cd2673a51a091c12e63691ee675517a574999abd6024e80a5d8024b328fc094e09956d629c6ad3eb9ea052c25b7ed4bd3047734426f954089a1145b38000b0f978e5d2d5ddf5ba28881460d4ef34cf7ccbc2c143941023e6d703263aa9395ee015e4c7dedb6f64d5a100239c6850ae62396b04504a9c82ca7144b796d891231ca1fbd4527fb6b93d48c8ff8bf0f1f869ebf6d115a5a192353fabacac0e3b01fcecf3f34c71fb8435527feb3171d5907775f05c2a3b1d6a2f8ef3893250d372abe33879bbc60c633fb443256d15bf38fc23220e687de154b51cfc161a21e0fe310c6a6d7bed1b20fd778c08b5436a7d538228a8bb1b07e792e94fb4d1ffe1fe8cfd3b90b82311473b4afdecd97e8fbbd230d71e9d8ce10303cddf461677fcde6456e2dbcf26d715c65f372e36d5997818a6661fff2baeb1bd949240de6d3dede0f661accdf84347a5b9541f2f3cfde8c48f56afea71665b6d5f284e4d46f5f6dbdfb77b1d3"}, - {"0000000000000000000000000000000000000000000000000000000000000029", "0068a20b33963dffe13d80a07426f85a5d17d7a4496bec63ea6f9ed6e7c97af76673f3f96b0c945f316e0d04b6e7d10c3c8cf1c406889fd3d7c5ba80d2833a2466bb8123d34ec98aa984f1405e35c5d9a67072fc0c216a4900d71f7fb676219a3dd75c7a95b6dc8c0619b66435790f52a5015a45810db8062d6167592aa70e17ef1cdf94d94b16740b7e627f7a9b10faff45781a9ced61145a6a0be486028ae67ca80ae10d57be61022467c6b294dcc16d5bc6fb94f2b11298ab57a25d236bd146b772d051d2c87488bedabd85a176b23b330b8016e1d5c9755fc6c00464307968c65ef8db40f029b9caffa4684b09bbbbf787e077f8bae7bd17a3f90f713cbd4bc432319484e45ea240986e5ad4dfbe51112decfbbad4f08cf3be954db87debf69a741bcfd912d9a420b5a0717bf92001755fa90c74743bf89ca21c03b7a23a5189efd7f839655bce8ec34f3bdf144702fa91b160d03eb53601b09d8b183d3936b08bf6c526388e8de6dda4011f2bc49fa2ee2c5e00affeffa30d9b9d3af49382593e2a221ded929b956914f30c491b94d18c32198da95716829159f7259a004f9e51890cd1b37cc74c5e9523fff33d4a344ac5c4f4df5f1e20568c82a818d953688df2698ac3c130d97c6be66a12fcf12f44133e436a04d13accebd314c2e3fb7d2c2531079d60eee28ff512360b0e6332c2c9a15acb4603afba102f5a96dff90335080d5702765e76ddfb7e0c936ba1b65e38b8f89a814170d7e3ed2a79ccbf2f0777f3de461a6cebe9c79477b2e1945de1e0fae9dd197a0515c89cbbe2fb6623b0fe2e30cec7427b595a061911fd8c291227b37b734b4e2e7915d451dfcfcd06a2d5f0a6d865ad634480f0415916e0caf1ac96580703f52dd58dbdf92e0144e1f15d4bba0670748cde2cf0b1d96a969f13ed0cd419ab3ba59dc0f2722ace03c66b9fb5ab7a0dd79c049eaa475e8d6d5d5374a352153e01e2a0cd0b65eb86c20abdd48aa4863cb2430a2db95bcbfd4c21f35941732a7803da2022be79f02e934f7ddbdeb153e75105c1eed8abca7cdb366c48126b692fe896f95369efc2a5cb7db161090c3e18a42e10a3f1f8501071d27e374f5ac2e6864dac184cf712f171251b2547175206e40270fa6ffd89509b39571bab0c9d1e61507f2b5422937469c4f6ba2bbf890206a021e8ef56874978b664c45c4b117df9b9ba042e271d89c07bccaf392f8826210a39c39191b191b2b60c295c29510511b0c5ff43446bf6f249618d9db7d23f29a3bdfe9b8809a76e25b6ce71473d7604b5f6490c7d5a5b01cc4e5b717f7143b7ac28060e4a55dadd0e0d2964961e5f1d5435a49b733b332da38657b9172209eed5968c2c3d5bc75794ef67f09752ac3ac10262b7abc2de18df4f5c94da8fbdf4dc86e97339306703f2a320be07c59d5aa5720afdef29748d4cbfaed71215be30228e0cc2c47347faf946626e85f1d548c6286c24e193948d1562fb22f731ffedd4ecadaab0b43de68c000b5c438b476ba61026336d66d68a96d685041729ca1a81f1fff140417374c48e7328b9fc8391126cae4d4f8c9131ce1c024dd420fd1d178790aa150461c7ea8c889f579c4263d5e634bf81ebae373abc10641b05d16c7175a43e4120bff13da579f1ccd1b60440af7d0304323ddea3c55f81bcfa72417955932441cc7c9324e01fa9e5c0e78a975f221361dfff4f9805541a09ace945e5e5ff32567bfbf11d23a2f726a112c35292e00e1412f52b815112484effcb683f6da20799dd30a882a119cddf76a6d971651a2d4571736c2c3bcf76a9d58bed2c91b35ea9a15d84eedcb117df0feb1887188f0c04ff25419ae0e7d688773807e4031985b91aef468c99c7d3488541c574ba9eb1f97669"}, - {"0000000000000000000000000000000000000000000000000000000000000016", "0114026523aa61cb9c0ef1a0283d6164f0dcca4a283176afb68ba6a141b900481fbf776b12fc4a597580199bc7a218260c8d5529e3fca9d226c2d915d7655931a3e2a579db07c9c44f048da0aabc4a0d94ba19a5038c45e13118f49f6b62a2715e322b62ac6016aab22c00abe42ecf22996db812e8ec471bd5c652fca5b50d631a05c64979689701d1a556f930393233ddc7180fd554b5d90f6f0d724262a232e9ca65892f0d5d1407513dd7e3e0248d5b53e5553fd1f38167e8fc8096132737810c0b491b56ea740fedb9cf762343fbfd4610e7013591c47ac033be434449ba1c0e4b9674ff0929856f9682228e212b4552ea9064a5fd26617b55b9131f8f2fb454119ba93fe3abeef5c1014b428d83113a9bb73d76e68735ab5884912bd8b5b25d04b90c9e1b4333cf038d9254db60634f89e599957c6131af553ff17dac66d3bd6977dab6a6b5785d15eabaf4156201fcdb6580d926afc1a740eace9712ddb08295c43804f9b92cea91b91782b62238306a784219e992361d1c19af3eed50622307029433c4dbf1e3cc775ee3502432fc5416f4bb7dffc1296875596c33921efd146f06da1a52c0d539ab6bdd0397e8772393665f5ed19c38a12a5fbfd92ac4ff83c415abc361669827d5b9ea23f2947ca55354910b173a5854e2c00ea6be1fd1eb36cf234a1194095368f2c65f555425776233dee2720226a90200c20086f0eb614251216ac482232d47c91c2cb9080fa80d337c55c1c8755f7e31c90e1c82f70585df997a0517088e2962c176e928163346921940085c89096490c21aa1a150b18846d3cc4cf0b1c51207efadcddd9f231158ec03afb038a40d0764bf87a22d23cbf69e17bc1dffd1e684c53a4635ddd9eef79e14bf596d4447fb0165ecb3da89bd15de9f9cbb6804152c3bc525687aa78e83c23d087a7f7277c6fb9fc202f8cb48748b649edbac4137591b71ec4ff19b66ee1ea3da469c4cc1a0fb15e59417eb2529b3b1d12dcc06987432501ab5e5288740857efbdca0a33f48d5c72591d2c069d2b0391f36c523b2e99ab981a0ce65651c528a56b8efe4d383cec2a9deded0a95378af3f334cf5a5018533e019de8f081994cf7a5eeeb67b07ea1e89dd1a509243c391ef942297d72ff6c8abfe24cd2f630ea9fc25e58d912ba57ff247c529a98c6eec0b05a9f55f308e6bb9b201616d786259e0b2207c422f11c9e529609f22d99ce131a174e842f580476df8750e4d9a4f6d8841fc4e36566f3abf804de2b993333a2cbb7a3731982b7cc27257974ee711bad08ad8d7a317014b8b5bc7b30ee1d5522606e41f14b694897f48455b8f5dfd9399aeb43228ee99e3501a45b2d6cae719cc36589835cab5c402e3cd14faabc113fc7881ef1f899ce27bda0da30084953778cf4005d302942cd403011755214d9ea2d63566c48ae88c529947f8fa1117933221ddde1f55f3bac2ad5a5b91c662dbdf715c50f40f8796e96bab6baad88fdc5e6b9a7fdf3f4a905b48831c969a985347eec5e81b57aca6ad4d5575ef0cb2f4390990b60bdb5b118ac8453575929bae935c10ac5f1448e72fb19bcad2160f6059cb8ad13d0c1d165acf666886e073241772025eb814e909b86fc04847a94ee8a02d171f89b9f5f9354970d1946d78bf01091d33cdf3b07269beedc24ee71e124cd15f95b86f274b4d8b37603527ac4582cf59b113f26d6eb7b8d9107c67e73404add904dcb17b8025c6854de454d2272dbd61e780161f870452f4d02bcd24e2c48e3750590d090c962cc601e912a8b27677d16b4982d1dd3a2510b38c8cf69c4111f2bf91bba16da580bb60d073c71497e2f872c816895cb204e429710322bce69ab4b621661b4e902cdfe3a58cd32eb3edfea5045a9c6da1"}, - {"000000000000000000000000000000000000000000000000000000000000000c", "00337a3fdc5a3149803651d9c42a1698ba6d4f7a04072f15964cc6b212950c0314744437666e4d9395f00bf8254ace4a78b96114818016ed92edb00538342a17985a2f946cc93fd3c0249b0940db31bf9575941f00af32c134cb8c331e81e3802fc5969188a19abc7c073fe4fa46c9f2cc61fe4280ca220f266aff36069c06ec91b8ba5403637b8a1088fd165055b71afe22180c3c94b8ad1ba1af7d2d51957bcc636a1ba6d23c3b01865e9cf99d0667c1390466d8577a218f074dce1038f3fa07d799eaf5cc5c2477c7c6c6997082de22b30f5bb8f3a787f66069ffa3adfb73b02a086c1f2e2e20c1a4d188a863218c47c41b875b8a0700c67e38b109ab4f55a5f77ab9ccdb629eb9ee123156acb0646d1791fa277e1276a324bc021f614b9f38e285b2020f0a5d873b30e95d0fd877a8ae21d57a6e56505b53bf169b4c3bce1a75fd4137b286f73004b256b1f5c98103a4da5a3a954a98f0983a0b5b76b9e2be4bdc658e068a4458abf513fbcd79e270bc9f1c076f421f716505cef53d4acae9652656211c90bcc64c79df5fe3270fa669bc150b6c72cb4b66100c3f079e7990d4b65113625e4d35e8ff2dda45430e0a6b8042915cfef34c324b0ba0e7ef7b13c11eb5d63de1868a2070b2758e1d654a3462b5b3d9bc294af6a8fc895ec3ac77f0252467cb0d1e20ba63b32bab286572a63ef8c8de977207d37acc4885b9df3246d3d1b962b43142d23b599a11901931d65d0a7d1757e6d3364c5626041d98d24311a86663b75b3321c43c98f1d0e56e0a42a69647713182730c32e497d597688ac9385af782da05fa5a930b565fab56876050b541057e7a5f9a868342956c4a49b48d1635681abff86965fb474bafa99fd9be768013f2b667b2d48ab2d713b337115dc9f9ecf5bd207717578d72e806d498b411328b91692faa51d2dc4bc80097a046f3c23035c03421e58f7933015dfb35fed70d3b93ec4b6a5ed1ab8db0fa70f51fbd87f9b601f80766d72f08942deddc91a9986068671fb0173dcc4b0869c36f5e35c9d3b9a4239a805a222d9506f107bc0853db9339a178b30cdb371bd3f1b6ddf2627e3aad3d70b2bd045bd9574eb1e976e055d26f7dafbd68cf1d07e1436d1bbe55d2bb99db226d628f62d25b5bce4031d2bb489392fcd9be0635d5eb9c2eb7a19ac58203959ba221c355661e1154f05d6ee5e284ff3a62f10a0e8d7edc44289ab8fb123bcbff5032d7971e45fe1310caf4060fe9b339e4d160735b6fa1030193829e23554f04bd9715affb87247c91b221330a4fb8842c077a228d8f673ccdbdc2e14578a5d7d0b8ad729e9437ccbbaa8cda168cf395e50e47b462f353dabbbfb108549dc3570ea22cfe4de47823785fc937d47fa07e2086b642e9c851f70fef42682398c5b59ca112dfaa0102f66272441186d863c386332a8554f6f2fa5d9a1174943bd51442355bfd64fde83a49d94c63d82e2a05f164c8a92923bbcc1ec1f7619ac50370043c837d17cc67a9eec9ae5e7aacbdf7ce73777f895f5f9c1407053ea7668329cada2e71cc6c4928851cc14e0ff50aa15e9cd049ebdb557ea1627cb75e008e65a7760114ac5623efcd1985b5e633d1fd2148f92cc0588eb8464b6465aa14b819e491c5f50f5ed3f20da434cb670580c610cb862997a9f8641f29c179723044fd68ef18d3ab8b86e06dc1f8ab31afaa6eca5ef271991ac313cc4d0fcff76e53d4dd91fe3ffe78e5139a3505cd276424e6b0da449d602975edbfb86e05f448fc6be60a2c563eaccb3657add250f09c91a7249c27571027643623b3eea4180573c5a91b09f360625bd37f672a1659e4a95792dfa5656dd28fe9a129a61bd032e8f1340096c0fa51833147efe9b7e5ea85baf8163d54eb"}, - {"000000000000000000000000000000000000000000000000000000000000000d", "003947af0bc096d6e35ae733fdd820b28490ba62340af649a5643c04afe2913532b2f44f05d4c7911f6708e1f2c35bd7f91775f40207bebeb5816db111e53e104423db231554177c13c15fd0e14d3ca6767372671870247e6b54750ea8d25605ae46b7b61791b187323ac0fe693897731f52aae6c0256e49127a15d8c66a31e7b7fa7f976542f38469e82d57af3a98d1579ba7708e1de6be39b153dfb207f6bf45c9c30827784425059cbf1bb1045fab7343032368e66cdd8d739a3262460f6ae1682038ef09c9c5067a6780eabd3fdfe48514e05319891fd28b3bf9e703d4d1c2fe4d733d99392a3b11c1b4160e0b786de3944f551bc16ad9f7cabf075653379e0d79650974b800aa77ac7f5fd2defbe314d5277b6e16114dec52233ea0cecd2a4e13188ee51fd0e72db2e1400f519615f6f0ebab257ebadcf62d5283fd92df3b2895e2233d69ed6c841fc3b87e483b01216ca64a01fddf3a1c321faeb37bddf163df5e731780a4dca6cdeef3a0af2714f1c4ae81ca4854299919b7b536b3e3de2b29f1856875d1a7328eb7d8edf221a92662c61c8bf3a1386303c898838e080e79c12e0368f8cf785277e127d5b3fb9a5c07c503d233e4712647222769d660bde38df2ed1849add989174f0d9d0865ed5c77d36eef2376a1546ad328399d1ad22b8913257cd4ad5d2df1b4c671c86849e921ecebb85e2b04c315d0bd90ed698bc1572fed7f376a4652bdfb910fbcbc463ea26ff1e31f8203cc51d0f11a611d32ed08351d058d8741fd431ff5ff7ae35f5db25239b1a8194fcf659951956d4b9d241cbdba5ad20d67bb429d0bf4535bafa32ce15c7082f503f7f439f63f3ce93c123db0eced1eba9b0f8635745b4f9c95a8eef1da420c215a1542edcba7b5ead5406fc574b15127d4d8921b75bd30b3a234c573d67d2c0c7a24af65355bb91f00a481520c082d20519d52bff49bb60dce421bdde01dcd73e1494f6520fcef32e879d4102957136de86607986f3f05de9a89e0a9e0c5b7e654c234571b03301a230f5e00ca9551bfd9f52412c19d79cc5679b982030cbf2c37aa60415fa35112eb5d83aa9081b6186e149734f86150b303bf94119c3cbe7ef49c92497599134d6f508d491c01448a34330dba5d7db14a7de07525a266d2f4eadb0bf848a32985aa7d39dc959f3029039a8ebe4f95158b136101e55ff31a99b71cd248ab100e8275dc1536e8bfdb0163e56ddb27007bd9ee86098388fa348ebcd79175f684c0dd7cc9b5fdf4da500a81b136e3506f5d9fcf60af1a78917e5871351d37106291b51724644ddb7ef5f97f596a399cceece73950e0eb3d802ac8f3f68996fa91efa1d71bd07910e81881194dbfc6c75363d8c4d0155f697a4074dfa0d91b82877e4b13d37f8dc212f04e4527d971b2d9e1db00dbfe5bec61d057ffd172943cb664ed66b7d0702f0478854b9b70b553f4c0631003711ba63fdff6de4a0c8514cd6bcd600285abb2d95852ae966a1957f66943e974dcba9351e2c4de65a20950da0a03effab0fc02b7b709b1029fb1d668d162fb8e1a9db662cf7b6c17d95a1a14e8e519aa18f31c31420da51398f4fbdf0382517a288428ee48db13c0672bf282316598ca690a5a8a14ff9f6baf4f2fb21931cf0971b2527f92180227dceeb5d06147f304f2c863a65e49290f375691416c74bcb7a8beed7a6185ea29b4cb22def31a3bfe0f30f48daaa309b33ac8638e0a6e338d950059acaa3866960abd61970be4661556c638780773085c0906037f9889cfbaf2a3ec9645531dfa190f1a75fa4a9020c8e793cbd2a5ad40e1ab0e65ffaaf357789d61800431b77d310cd9789f8060bec1bdac1afe3739c4223d5b5b053bea015b60689469d94f9fa6ff9c1b918e"}, - {"0000000000000000000000000000000000000000000000000000000000000010", "0024f30f6a25882567ee711f7639c9bc6a556d177a1a528b6c0e86bd427b80a263729ed7291d7249b76d0edd43152c09c41390c9832c6571078e091cf2dd4b3fb52edce3dd9a0151ab369c2afbb249f00a9ed6150607286179f01a9fcba290bb3f32efb86c5a5f1334192c6ba7bf5781d1ad3465062fee9f4996fd723cf90cba5bcad047c0d371bcf30bbc699f31e0b0bd59111e1c3455b0f8d26de541b3658d48fed905a4ccada90b55f6c14f1143999bded49c3264bbcdd7e2dc3222315cb2f99ea05a17c76d836b882d32f1f59dd132650e1c8a7ec8ce503bb361d3dc5ff56b54f76b0a347452de37a46e58220da9f21756a7c57b4a7ec874acdb0e162d36c12d876fe79355b265462f55a6c81124b5165763c81707784bbebbe29220db258a779e1dade11316518e06c984a57a1804332335f55b4bc9ff2ee52a6015f6cff20893e66775d00278253334513e42dc003a38698c130569423a654adbf2b1bb0222ba56960da25a2985d39928aa39211d2af53cb84db8f538080f325e300f48caa1c881c43a51bcfd1f99d39dc2ff3a0fcb594d64650d66ac042b5a23bfd2363312b3fd06a8e60e102e2947f56d11edf8a24e65dc0a53b81c50f224481bb4a60ddfcb2893b2e5a902f150188b6015512ed0e2295d77811a17393bf083a2383f1efa7717fd537b1e86d6cc5ea9a28aad41b3c5322995563f0469678d7e9dc21b2d61f18d9de70445163e7497d22b67317bf8958dfb2eb58704ced631a25f6dbc6f1e0efcab9b6f968b87448d470e8e735e3b54ac1b515c56a4a3c5b3ddd3556b30b61b0f4c763ab9e19d87b4061d80601db0ec879e541dab3dfb8827a0b4be41e7227c6795ee8ded595ec096aff2f043bf7ba73ec12807e8d4ac10dad3e36d72c3efd979bd5d4a0eda0bfc095f4ea2b6cebdcece3486c4cafa47ae111c36efa300461bc89a2fb163d1bd60a48b48c948449106be8563f7bd9941dbbbc4ff97b85e8b67a96390761d584522e7e93b7e0ed5374bf856744165f059f71cdcc7662380215bc52e196786e52244c92b591ca56076bc1f0255f8ae42662d2336356296946b930149af9a74453f900e2f58e40eb3f6e40602e2ede835bfa2f11c44042829ee68492c5b1dcfb19407d43e399c1091addd66909615b26082d3b59f087b28ff9ea2a4df7b3c13084379f7694f1d149935210ee07cf321221bfac7ec22aa34aac62979d3ba06991533dbbfc25feefdb1b32195cbd35ea3eb636de4f23a12b0b105d67c990c7a49a5a29228a162c1fbb3fa4ae8eaa0f69c0affbe740d6c096561ebf7e9f29bc4cd21a8bf1318df9fe8cd12a626e61f475b193d51934660cba5832632bdeadb3a5a6c53999a2bab7c2564c8dbe2643320873c57cf4c480aa2fba71edb6d0af72d8b5ff26df0e53a22220316d49a40de8db14e3f0546b4d6994ddbcbdea8c9147896e2ca47524c586da79530c326a6a279be92c751680374b92b39d799cc0685d577ea0eefa41db1e95f1fbc28621af2cfcbdb569778ff1f8226907d904f0761c354be4f4c555338c2faff1808614f70fef68d1dbe07d4b38ffb17767d53bbc12e8ddd0d151782ad0b38a87d9a0f109990eeb2d320cf1c78df6aaabb7e2eae1f8f2d143fc2dfb352ec47a18dda4857fd2a76083027968c4829f7a904a2677b9a0fa0f6f436233d2682c5b0c1157eb7ab2473446e9be9891be1ed6bd508c61df845a6e7a163da92845bcb43fd7562ddc96617621e07fd74259fd0aa822c6cfdc17cdf7292bbb50fe609e0100c0f156da2923e76489eb6859e3edbe12072cf03b14d04b66930f417beec37530cda3cd8bb133945c789d0e7dace20b4df856bec76dfa01e195326c862f5724c299f711bd7c83653fdf9fc09567b9d"}, - {"0000000000000000000000000000000000000000000000000000000000000011", "0133ee8bedcf9a133108f3bbcdbc20ea045675d4460c9a61f5f791b15b91b7c0db1a98890582d4af7d0805e55b0b1c875c872acd76e629399a260362b2294447998efe0658d475935e383ab542ef1b14fd5929ea040500296da9df4d9cd774571d5fd981d040dea61a1d8114b75b60d11b0d3661f9e7bfa309da505330c610148a9ee44cc0e3175586361848302599f09e4ce463c21f146df3ededad1e77bbedc8a0ceeab61c82e5023e9b6120c734149756f126022eecde005550f1ba17489287858afed34370e335b0d79e5256475ae6f013064ba86cc8550d81d5730f4b23fecdd5ea7c17533aacab23ce6104b9d38e25b02af860b60fce1d1f1e1457d78d1ccaa04494ec85c8bb5ab61595cd31b3043beb6a3a854f9cfaf0aedbb8c77d32bfe3819fa6772f7db6dab772f323e358d75c2ed4bd434ce4fbab3b4358a380b5d32597c70c6507c06c85fe23a5dbe4fb01f9f23a4318ae07c873440d457553e52791d784830bddf9c0a5de768180287160791ce08b20813b53770caeee1c2d162a31add7e103a1c2d0010b231384612c0b71fafd923967818a43f939acfef2fddafcfbce0539add5792042b99f8d89d676f71862991c550da43c841410e395ee1ec885f3edc7521bb751177e8af31384e6503e222dbf630bb3b6e8b49125d37ed97da2526ae33048ecf5cb775a5a017ef653e2f7c97d50f4083c02ea0f479dfb091993cff5dec1861c1f77088d16af6b863415b000db43d4f790502715ead3d2b34a257c139c3cd9f9e4d2c1c34dec4d8e15d05e1932eb30120f01238eb179daaca436d3672e7eaef13c28fc0d9814b1007a77cfdd61695c6af0f3b6e76c1a092c37b38e806d194e8f05b188e78e7b49be4c62b91a560ec80d3d107164ffb204c67123f7bc3e0685331f3d28e05991a6a1afff58d884da125363018ff237798b046b5a5324181ec2c296f11cc4ed4178ebb16795ae055c83804dcd2714c45014abf4c42ed64c13d3ad3f102b7b22f3d4eec8d4e3b2257caeb49520886bc1e91d446da94211b978af11127370a0f53c9d1b31071107923993c404b9852a8910f7a8ad314e9bbf997bea0e2d463a9648a5a0c166f1732da29e399fb796869d0a74193e165b1cab8880e128386a5314631a37264c2bbb643c707d962dff31e531e4d907daf541f98ae9075aad322ecd5b39b57331602e90a7f95b890c36b721de8c6f091183e8fbd655d6807c3725f479b0d4fd0ceeca3b3b06a3a6e19374e1455c4d1d52758ad27f1d8286240248742539b55743625b66d2ba287fa3740c49272aa80ba4c8a06a2280eb14fac687efd7467e2126ffdc1d90c0efaff22415abc0665554749f302b140f6ed8a5dca60b892197a05ac392bea3ee5cb0af169af45b84987a1745dd080030d8756ee1fe5f85ee0496bd28dc9eec59570e52ed13edb00d30116c0ea00e1196b1bec78ed24103f3cd66ba838202afd0417e0929e16c98f60053da41a4a385eb5929d23a2ef0894b389c569031310bbe6e57092d4c3c26812aff0b5b07889cbf3f1dc7e5aaabd595cdbb5c31c7ba760b822363295e921051152d0695cb2ccae9f70b307c866b08d72c5b025045a5d44a60b007f71f5d30cad4654323f90375a329044bf32e030b0ef9d74558591a2ee00588e0e03ecb374ca50145f4b239b32b0b11bf37a2242517d2caa4a27b79b793f78542604e5a9e3cb08e17d5a73b92c9dac518a081bd7e9d5eee206733c833226caf94d5ae72e9ba9173ba2ba63d790c513b58ad07c0c329da151783484302c54cd17e7e2beb33284f0d15e3986dc9d8897bdf5724084ac9de96cd9cc4f612ed7a1a72093d3d05c6516f5f3b6f64d25c0b36eb1ca6260d4d6022859badb9520ae5abda7a1f567fc3"}, - {"0000000000000000000000000000000000000000000000000000000000000016", "00ad4c1b755dc9c58e34f04a73cd72d1fa52b1b901026c53f7a34bbc8d6a49b0e9f9d8147730727d923304fa4303d831cf23e0c770c23954aa3db6fa781ecc19d0046acf8b2cb3d9b591a423db77e5318dd494e9132255bd9a4cf7a1da661286f4a90b3f4add9be4ee13616e6fd5487344697922c68c3f09eda234baeb4917916be920a204013d311233e9991acca8833021b52b746554128ddca1c381b81865eb5236ea389fc610015c26eda3985ed36a68d049021cfd7c425a73940a3a3734845fe7bd7bd5b5a871cadec99699565653371b8c613629c95832a460c4afa969f7699e73fa70ca2294031b9de3b57f428b02956af9f6aa1dbf7cf09c064462d620ab60199a2923af322bbde70471daaafc28018569bcb794fbe5e295a6c6c2c0ab58813d19930c692a8e7d88d88a878a6557da3a9e8de343d5a49f3a06626ce293ddcf216e46dd4bb9c42a77609745cd0234d86e1520c4c1e6c200e8c864f675bacf95a2284658dbf8de946d9cc48ae56d14e6ca0f490adc3f672cb2df0227b14b9de92d231311cacf0ce3cdfdee7a34d4ede6b2e1e505586e474451f441975eee1e7eae0416bda82ad5a23df8e4f1514a418f58ae980d67702f4a652f78acc7c1afc5f481bc716add96075287e329dfeebdd84be8809831b6c2cfbfbf120d9e139ce13fbbe7672a2c302d996c96c272f3e792657f7dd03704650c001bad0cff9cf4a22e0cc63185b1e9faf617231491ddb98b837ea907b77f95ebb97b37e4fa9da628e214a9a9747981ff4bf5cb79d70c9f0a4878dd6b4b5407417525bea1657fa8f7b5f8f4ae4bff1748a1088adb41650598db865355dfbd57a4de9771ff11bf0973963aac5d8d4f754487e59ee436727ec51acc0718ea30d446255c3f547ba6762fd1fba6dd3a79fdf13962ff87511a8aacdbe743a71ec5955d626b3a7c3e00e9caab2614aa79f8d1e8d424702a3245c7fe701844f83acaded41ee53b23893083e788debafe19f3a40486a5fda582caf61af5a184ed7fb59cf8c7d438521b0ead4dd007f6afab892350afefab6d4bcbda981903879c05031d05bdf5f6257c415a87aa1af9dfae8a1ab4469829df644b7829b3fed35ba622148edd45ad04d88ded050f2a42fe4ad084677addec35c6efe69617e92271008eadfd5cd723ae39a9efaa47d85460af08305c72384789b50bf014ae2ff20f5debfc7dcbf013e8a555a9dd5dd95dac81d95865e1420eb930eea31a202958dea56b67e0ff6500e655c1a966af2b836e269a2a790f953917461e04cc96dc7dfe7fa0be006e0c0fb4c5b15648e7eb43439ed73686cd1996a923c2166318ced4062c9e7541a4482e28db5dbcbd5d363a342722137de0cd353a71140704cdf0d5bd1f16a3c74a3e52999c33d289b767288f82d08b22603afc476a01c242597bc13d0fcb75f2349922d9fe8fae376ce136843322775b5cb928d9b66bcabdd5032b39d9e6ab0727ce7e4dd767131908f12b8bbd8a3daf517097101580647e2725fab5520e846946efee91fee2ba711303fcd640434d8d1c71f06309d2b998326f3f95fa3227c5f61c2ed57f8d4b58b41144ddaf319eeb55a1a104477b0664cb74e3fb69d08e3c96df31d7795928890b5da88cdc85060a5263847291d41ac589863537280a37829bc4f4dc6ff2c7d144f7f86dfcaedc6b5dd2112fb8e1afdee45bc400261b39dcec7e6e581c99e50ec480b499dd6eaf8157816860bee36cb604df26d911b5c2272b0b822c84a6f72754f5e7aef93f5d4db80dc5cd8f6289f2725d28d8d36cd02ae647ba12a50c35f54a853c18797cd79cd376d8b13131562297e5f7113472769978ffc7df12a639ef427f05fabaf921ef21c8fee985237b2f5f8d35a057313dd9c93f74a07c"}, - {"0000000000000000000000000000000000000000000000000000000000000010", "002847a9e24b3a767545e138e4dd96516698cbf35528036ff7b170f245f14647de37f498ca93c87a134822575520850c1a31cbcbf53ddfe5cdfae2f55c865b6558f719629bd4abbe39d6943e45a83e155dfae96508c3b531da5c15d1d097979ab87f20b3a7255e4d630fe9711dbf92dcbfca63326bd898f68555e919101019b84d29a7117074cd476473f1372149f343f12b792dfd6c73f296a859ddde746aa666efd96421976e4a0290183133d93a738261f368002ab9e1b7c85bac1c0f5e8125495b2b5ef68f52a7b5460e2e44bb790cb10850bdc05e10a17edc78d5df9a583792510c37f3621634ba4caa53d68eafb5341879300667a8a35dfa7203440ca72641369c2b6008f3a2d4f91a910dd690fd0346decf48dfed91fb1ec11d3871c0b22df9b4553108fa947f9b1f841505de02efa66cb3726dea9c0768116acbd1bf4cbda712fc23c8dc446d99f4b291111f005e024f4a4f26cee7d15368c74d3e8a3388fa537230ffa6742c91e55771388b3f1aefdeb74a78bbd6160236609de4614687f7ae63f1c3a2c1e2fc4fdaf43c40bdbb5e749948bf609175a9b361169f8b453c7c57057dbd6e9e94c4973de8e2fc8b47f8c62cb1940e0f2ea83574dc6149b3e243c80085ca629bb0327f71f20d142f0b5ecef5cf2167a302d2aa600f4ded7d70121080f99edf8766036d1a235cf05991d201073aaace0326e4796b050ce559fbb123f909848d0e0149b32e27c8ca71e45236ef444b33ae3be7c8f94105fb62f00b5817787f211503b2f83252e63805a9026f93374d1ea31ae8a61302cfe71b67a7c7d7fb769388170272054faddbea5ce42b0ba4d16c3eb6544b6136be40ad164655f4419ac07956c13429f632bf72eed3d8daf0089a715f40647fdfbfef85816569761338d83f24ab1d5b5d8621986e6bf5256a1ba854d72b52ae9f412a006d704df8db7197022a13b0dfe61c0118bcd45b9a285fc3580ef8022fe97ed2fad3793b2d2a57ede2b9033bd6919bdb9c0bfd0de372e6b2c392d1f93e8ebb1435f20ed0673a077b5b4293d0e75d314116af14bd03ee42632bd1e629c68f8065321fc1ec735edb0ba41115ed819536c15fe66af3575b4415f4e076ae2f9a09d594b40fddda23da8193f97b37443d921bb686ed66b2dc6f4dee651d969496b6baecf91fbda73f4f040bdf7e6c8dced033e018515e969cbca90dc29cd7e80ec8bd83a1466f0b46facbb96dee8c375bc71ccdea0e0f3a82ce531802c44284c10b61cc260e641eb1ec16f7a225be28bc13910de4317271310952464baf400c6fdb9a07e470d941a0e1684bca68f4987468ba23157fb70fdcd1acd89aab023aad9d770cb2cc07d31c17ca66750ea77319878321f360f6c8648d4e55a9231858adaa238e8551e71d42d8374708de818ed8168b013ccafd1ee122b1d6b2639e5624dfd270c0de23a2203b7b2a2d2e1ca19e1e4411314f3ca190dad02cb80777f1675e45ea26a76950b4be21a941990c9de2613cd91ec3ae90af059886f50d92ffc1bb5605bc1c8607ed02a64bc6ccf2c57827ae91c8b66a9cc43ef1621e5fceec4d51383ef8f1c5bb51508d658bf915c50f0ccbb140341b01fdf028d6f349cd47231f5c1a44b419048c14fa758ca3d9a822b53db171a201c39c54c0109a1eb1198ee10d8877d4871cb6c4a38ae8be3628ac1b85fc346bd481cbdf3b3a40646e3718d51e8afa198801c3e496b1cb7dc1549f3d7402767c3c76d237264ecba4e45ac9ad953747b78c7802137dbd5c37191c98fe7b4ed2a81be2c8522d86cbfe5e1157b2b8fc58644f1afd623f09d062ed8eaffaaab36cb35f6de728281c0145f3dd35ac1e471b273b2b268f5ddb3bec3192facf438d639304566c4ac6f0e83794197ce276"}, - {"0000000000000000000000000000000000000000000000000000000000000017", "0234ad6332dcf330f5fc461f6fddcb899b0a1ca60e1c51ff039347c08ee76b14033dd30ec121769c0dfc058a4c74703146cd8b38e515b75503732853fa94db689113fb46e2da45a1d5285f8455fc2f858ddf9fc9055653070793bedfaa8f34151e2255e106f24998bf0cd5e5382905d6f2a889b398c0fba87b51a63e4a8a06247ab3c5d0e4cdddc8106625dd11b55fb5b3515819af119dd2637a7ba071a5461f6bbccdcca2fbf7ec02dd083d6f11a6cbe20bd72e77ef89d756777e39093f232f3eacd1a034cadbe4a55abde9934ee9fba4d6111854feaf909598bd0308e3354c923ac111bec1da20bbf404f3a4a62132ec52193a69f1ddb44b5bb47813dba9a534d5c8994be43298583b1699574dcf6e2f42e6cc05efaf490d9941c5059144b9cd82fc505ca71ea6b268fa08bab184e0d7f22172c10360acbd85044585dbb97c6ecdd7cfa844d567c567fe60f7968b7602423ee054823982b3bf7510874603f297031db5452124f6e6646693e5ed87b3aefbd6ac2d986f9cb3462e8966f602ee7d29e579834e51d9e8850007f0715531c1c68cc976e1b1ea26e466c1c6850157c8528f810661025ea444262ce98d74d7d436e35960e53bffd445e28e1bb41e9d5fdd7c145ece400355e51b1adc7a0aeb6efc664aa21d357c52d3c64d741a77b07915775442c58cf5a1b7cb34866a5b74def59b94b15d521c043a308d36518237ae59921401ccb3e91fb51c9bf02d154e9fd41266a5013274b74172304a10d8fb081b134b1edd53d4d9632e3ce86885e05a0ae9649ebd27200cdb7b6026358b930cb2c1e1a49bc61ff51c31120a4bdee14447961ccacfc6f51657d04a3102d30c9d3919562ed62735039be104b4d6b6555d5e740f14370af312a83cd46ec38b7052155795eebd97439c8bb61796aa49894c47ed2bd352cf8be97b8d6e4237d37702b3055fa4cc3db4a45c741b8a4eebdd3bb790e30e2315a477b0ee56f3a70f47969875241bdd113f909b0627a157a820ebd32b9c816869d4e4085a49e6a3dd1a8ce15e3da2c91920ac51ae5934868d8f58bd24810a8dac67bbce1921f8b364f45c2904aa0ee792dfc43bc84c9789da3226e84d278ccf581036411b18af0c2358b571254fe4fadb5e72feff38a1a6295a73299831404efe7b17c821e4f124943c4f0d29841c18b65303f5a4f18c101f57120c44d7bce07b5178489a0d97077c89e486a912e962071705a8723baa3455d5bf420baeddb22f15238b23ae81c461b52ba60d263bbfc731d9057c59ad88c1eebca3337ad122ff9537bde562247ebb6198152fa3c9e5d50c736fcc773812baae51562b45c76017ddbaddf875d97246a2f24726968307262ab69f2a159d653792627d482e8ff21801be8b2a3c41ba83f559fbc73d96d62c39611779cee90ff34102f27ffd6e8fcd04f8c1c983b8cfcb469cd07a49ed16c677666caded05882b11d04b7ea8b2a50abaa48b15923a0110e7b149884ba6431756042ea9fc3c20a01bc622bc2b6bbbaf9c9b424b372be84547c7daef8d12e2d4dda9454aa3801986e72b6eff4a56a75628e82f5164df1e10fef3c833d4a34c60e64dd09b7b6b1f17bdae9ee4e13069782d82d0dfe4aee17f94983ff61a4541266ea02ad3723071f42ae5a1e0f5485d725a09c37d5a072544a5629fb55ab642ca861bbe1f055f3056372ee1dcd3df8ffc091656d81ffb76e7feea4812bbadac26cabb89a587064239441225df503d193518c950c882c8d727a55d4602f95ca2165296b785270a33c220942385a7be67016a78571350c35f56be4c0cd512a0d71826858bef4754d03f70e326361a1df527a252136c730b379c54d2ccc1eaf830e7990eee982b8b5ad34e10b4fcb9c5b33cb22267d74e695c0c27"}, - {"000000000000000000000000000000000000000000000000000000000000000b", "0012b949cd1c913b7c4809115f6c369f7b6a9cea133415652c871a25e3fd5fe3b2f3b146ce2c805e235f224af3fe1b70b345ffebc67c4dc8c96eac9cbf874c32a85624efecdf397e3ab720075eeabddcf95386f701d86a24468c2de342b660bf71dc0ed8b0c02af85c41099f6e1b3078b9fb1a650d675bd27a229fbbbad80f7cb6623618da7ade7151810ad85b30f4cb5b5f783d848e78cc5f4f0f5a45e658cc3a037a656c5e163709e85ee7d64502332c8bd5f61ef4becdc10ef8d1f725be84a3fa23f099c0dce4d6a2f6d55b3b0f1e37b014663abdc68c4753b69b51cc5ae69fa4a9ae72518d28b57ff84f987b41a001e36f2424af6618085d7d950e8c0252f7dded49306f3aeba2f3934adc8b7ccc74342e83014455ba7faf54241515c95e51c05bb4b8db1bb9a327348bbff8be5b851bc5cf74597e951790dc1f84dc60f18bc266d77bc3dedcb7bacef722bf9fcd02bdca7e9b5adaabe9eb347ad0dff10339f83e82c81dcdd90ecc73e221e0e8352b86b5b752e5ce1b539203df03299db2216fd679c414612651f5e40119161507120745f6a4a1c3c21761fd19f4466db39af50e0f184d7e63be6878638d8f32781c6d7f632c4aba4e941f1ff5097d5e5934f7a298ba44f06bdab37f79fa801ef78c8311091e256daf42b8641c752d4f1a8ccc4429dddc971592f01d8967987afa51af32f99fbb468903173dd89d5b7c65e64310db1f6168ac71d8ca418a06e5fb2a65da0ea1e42d53abf86fe9b1eb9756fbbd2ce2bea9692124c391254547132ed0f191ca7baf2454bebbc56065cd9590e947938fd12d4e73439a5e870523889bc7839291ccfa633aaea667a2f3b01b4ebf67aacc6f761aa687418f26c713789bbf59a55c303e1cd62904b5d05819edce9305a3dfcd594670b0ae6b6252d51b88a9f93b8ccf67896478d8ca573f539486013db9a3b1e8cc2f70c8e4c07263a477167519ff9f7d62fc6519df82db6eda5957077338aa70971a0e1102ffafc7338f4f71c080a08089dfcf895a307fcb6407997c81fd46e6f1b2d1f2fb68752458f07f5db7b603897e1f0c61478f5699160305c95ce2fa1af809850ea0258f94b62d3bca81e5fea9e835e27599ff7e8803c005353ccb97afe221013cdb30b50d1d6c79252921eef267fe4b58266277847e44dbd75e87de590a71036fce65f2d8ea75affab3db612362dd5a5ceaecc334e03c3e267a62bbf6914580767ba8c6a1af1bd9bc09cb21e4725dccb74c90f10697a36dc56adcd6d4fa3d58c70230ddcdaf895a86b4befbad9e76837bbfab05557436782c6e91c879594d97d1034b4bf45eb8eb288e87ac390eb08c843e55e758f0b69d9246b31f8b163dc67f92557c132edd2229e7459b8cafd30e0dd8397cde63fa65b4976cdd8545023f0b5d9b9cb00f3702f8acfd61d27473064b434a18221d5674151cd75217a98b3d1d9b598da98ca3eeded8038246fcfc67ca17321b552e1552b7e55e1446e3d987f26574b9fab418b94b419b19e907b4ece45faa6b4732541474e79721bca7f1e3924106f1e393afb85d9268f1566cf0803c34250587e00781cfa3957040b184229804bd4b49249fa1fc108be3087fcdb61254edaf32b9963f0da8283ca96bbdcb1065bdf1b31a4ec91591edb6340af20878e2c80b1f20851bbad545d03ce5b1c4c81c9eea0e5b2b1284eb95a98c6d226e26eb19951ec7ae5a532ae9d6cb3833bf3bb7cab8a2f4c6e67a558ddfa55a2d0f2496f2929d49605633b14b78f3e116abb056eb0cd331a1e6561a8d459cd1e8e7925b3c9f02edf5e421d3270d0acee66703f0e2bae0d1db0e8a8435443e210c3f1307e579539f3ba259ad69811d254b940c8c32368a21145725d14dc3e43aaf419bd5f93c569ac2"}, - {"0000000000000000000000000000000000000000000000000000000000000007", "0122f69589efba97bd9073b0dbd55c0a2355df13142f9eb1f87f508d80d6985461666b17ab34fb39e4b305271f26c736ab25c2a2534abb5fc042bf1f3af8bf26d2c66ce5af1693ceb1c488df4b66a21ec83b6bcd02093f73a09fbb6b81cba47af15071deceb41cc3a92a756dce14d540c5d8e3931a96b1b465adee13b347064de7a36713d1154e00f22a1fb60be6acd3991cf60ba40a8396c778d2816334017ad2de5a5d63d7a8420131974094b04623faa8721292af6f6cb8c08a13ab210b1e1e8ba439034dca6350d86fdb2d39461a3bce095b7227e6a9c8a9ba37925e7e5cc3c52efa6a6cbd1ad86735cc9c6363b680956cbbc83e928bb17cdfa207d240435829a1f1ca66a6be49fc6f9f0952bca443197e034f8e57186d76dfe412aae8eedf3d79dc0b56163ba8f1b2e74059fdd402842529c8c8a4c53ef2aa228bcaf4af515da0f28d3791d8c6aff9f81ebd611e012ef6c93fcd6b26950183c18f4a4275aa74eeaf4f02e72ab5685c58d14b44057258ac7b3b91429cc0c51b829dbe529d63ddeea063061e32584d0401899ae31cc9b65c892f584d9615928e759f1ed53f210e14350217ff962d8e4bb7e122a220235df97cb6cede9f813a12ba109912706b0b0ee85e04f89bb2bbf0fbb7852631d42ef595c4378348244ed1cdef4eed805a75ad2fe62fb7e61c9d0d36dfa31a93f07671deab594a63016642b9dcf4cc1dbfa45045fa48dd62d2569ec272155c4d1f76d52c3b5d1791ff624650920dad3e70d7080b5d9e4e4afe44f268d58bea7e0d4a58ed3c608f092a3b3c690df3ff559fc0f821252941c7a6fe763f05f7d04f65930c45ea41543f40a75e55a6cad89ec4102f242534239c475961717cbffaf8a9f32bd8008d160ce18bbfa4ca636c50b24b4551d1e8a20dac21ae255aaecb1b11f64d856db3301042d35554080d3f8f016f60477fda27b30f8a80d327261a1197df9ab8e81f5373602f93c591d005d5dc36ef990e87c35c7c35098eab5d3194dae38474640578ab2f7dd38c1d48b80e9df41e17aa33cf8e2f76056269e1d6450658dfb903024e495a8fe42321d730d2b745059207649982260d253d3ccb6b8e29ac9af47f263a84821b1a7c6e2009d27dec586036a354d2680c22675816e7c31f16ca129b1c88427921d7f2f734513d3fae398c0e91f9950c261d1379232969ef2aeaf51d5da49efacabd3e24412d64b6e2e3c9e9f52175ec0b6d2d99e0e61d71221368ee8a7fe48bd79c06d79f267370e2ef7cd94b761998b25c5536caafd6bb34b0ade1d96341d15ff6470c9a78f157ac7ca57fd2c51c3bd50325adc312692218bce61c10df0175b39be6091e6d0fe232b97704aa0d59250a43c51394e12ca107efeb5da2390914e7c625ea8cb32030f2dd88d9b41214b0581d63691f111202af3f36a2ca986180bf86687dcefe762c0a58b1382a91be0c8ed1d7cb6a3247e29c6f703e77f53b8ac80c2887207b9d6aeb7cf4789b525228c743a37f9c6d1f9913866b098138f841fb1f865b5a1ee4dafca9410e4ade41fc968d2aebcfc1dc29bd22b173b3ad5ea52731355a438cecdc7020353de1b160de8467d4c3cb15c9c7f8594948ddacfe51f228eac7ad0d52dd1d6129e317759166d4e78a2c26344d70ed0a15899240ce02c784e229130532983f912435d837b05c5b8414af08fcf84c61a0252bf4dc11942c1ac7aa8a805ef86c4049ca21e49a7830fd73a57c5c2db48ad388bfa69f477c33d342d79cc1a744d8bd7b6e9b0a969d99d88c1a9eedd422b1462fd38de530a2711bf1dc5678615522e04aac0353a7ff5dd008ad465a5ed6a7a5d956472f7bfe6011d2ae017fd8e7a6c07f9e4aba929f061f44337733fe6baa23fadd866430b4e33275579ceae3"}, - {"0000000000000000000000000000000000000000000000000000000000000023", "004f01973c4ff102b9dd97838d712a664024fc2b75195bd5e56346be8dbef5e7307377cc96a89bfa8e8f22c9aace5f0abeb5d692837fcab54f1634bf3482f84a6872c838611f3fa004868929b8893acbea596b10044f72f810da984fb24e17d5a9ee20463c4b373fe42638698cd4cc3a848305e5d77e3f7d363eacbdff350fa45a98200b4dcab0dc93fa5628587d6dec9bd636256f823a370b830dd28f95a714cbb8b9f16171fceb084818a9cb59caf5449942f698cfdbb6ff9497fd5121f615c470af9d3bee96c2d1a83680b51a86dc5348279ad252eae9eb75dffff47b4175fd99dd8adcbe3f3d6195a4d859f39ce64443fc8b3dbece79b4f6b601085301da69430b964f5419f373573672ecfb58383d2375fd8d8bc8e10e9cf644000243e1a1b253de730e1eb76d18cf9d610aeb8729b877dc4a26e9925e3fd83a599e35c4e7082bb2705493acd8b5fda491528aa30a471ca15ca2bb874a6f3184e143e1106bbfeeb9aa155692d0df193c94fb6d76f810c2b9dde86fd951a115eccdf3a00c6652ab095272f8d7277d7c097c890d1b8a2646a356c100c47372ef3cd1a77eaa07b751060d9da58f34097257ec5f4186e190fe825ff57f5ac82e9641d52694e6b4cbe295d3d65c40af39943af044298b936f4f5cdf517206b340169a3d350d3479500e2ad674dfb250bc5ed06074cd4847953299137a79e40f96f1a47575350fd428c2c719a354216e4910e5821b5a4d463698a67793c6a39b29cbfac0f3fd9cce7f12b5272737e9ebf9a2d9836916a0775cedc6959c381dff099dc2491e5b569aa3bdfc6391a50192b036e01cfcab87a68990a7a39b4467b7b2ef71247a1a0a24421ca70bdb5111297563666dadfc022e7651382d862c9e7ec6e26933b1c201a31c10f72cc9a9cc1f5e0aa2ec7e507b2a755b59a3db29227c4e974c30ba75c4006e6be6d772b9b7b29523f3b84240e598033179203cb9c67d4edcdaa5c09eb6d7a23ad832eafdba7c3601ed9dea93eaa76bf1e0602b8a833d8a4f7bbcbef90f3164ec9a06f5f8f96f1a1353f014439d2cfe67c604e963dcfc4deff3c0d9a340cd37402239481ff1d409e3aed92b1ba5e58831519158354706b0ba16db43192bd2a5d08dcba1c10be4e418c4838e7fa9759b204386fb2365e1d2c3adfe0621d43f50760bbcda5dfb12178d418fa7fb319ff7262b08fd3d02893837d2f73e4c5b848024cbd5e38256fea5c42e9e97fbd7c7621ebd594bb50e0a3da064c7f62e6ebe0f72a0dc3e9d315acd01a90e4dc9b78715c75a468bc673a6b9a8921473db2e40ea4ebde5fce2a1584e641660951a5ce8324cab4748d45c83c616e3dada6d1fd5d16cfd12f41af7ec2450e5418dd5f927f2ee61a5b6d2999e8bf631be81dd3160d2814ca41559c73e2fc664b63e8f1204c318919a91414db9db50f47eb110857f342c716c548dfcccf8284c2d6c3359df51dd3aee8a775c7424177bed8f95d9d8e1058ce3aefcc5f7dfa45bfdd3ae2d43443a10cbca37e811853863799ac5b210d4ef6c06ea1d3d96964904ff405369554be8a540a24dda84223eb1f6a2d048511ff6c4237ae8ad7124bdbb533d3fabb7eb83e7c1f1cb22a8ac4d4b500ef84bf9f81c5567d63dbce2f6099c4f65e813f09e35f059922be60890f2213012b04df81855e93bd93e01cf7d7aa7a419814d48f5a61d196c64f38c834a1ee1247132e93c21443911a6092428eb2b34c0fad96b6177b0f4e9b2490d0b715da273f96b98f73ef9d9c26b8d30ffd9d311165b422d453c6af16d8412a954720f538e5d33ee3188aff044a537c5619236da90605cae091199e3cf2320ef56a2371f53e947d340c871dbbdb90bd74f893e33e680bb94831bc6e97460e2b51819fd293d94f5"}, - {"000000000000000000000000000000000000000000000000000000000000002f", "006096c727b210b9d1b9004b82b974adfb5df6c14e2a974c33260ab5a39f122791483dafc32bb95c6e2010b0c9177dae668be8462365c9e0546de581fa142f17e15c0811cafffd83bca7b759e5494e4cdd9432f302555b402184c88dfa55e15923e4a10612305471d812646ea3dd77fff5d8f5a387d2f16d3d3bafd634170ff7abef47da24d32ca16417e8b7c00278475ea4e642625f7451d9321f6b63aa09a7e821a28ec3dde6e301e05e57b1e0148784c3d32363993a117a12506b1c0eb01fa0126f59cfb633d353c61d2ce9a1b991221227db6a093351c6d5e6ed25ce8f6b95f9806f6de3d63a7b3ac299d44ef13ed0642eb12797f5b50fb51d4f0541dfc958f3482dbd8fd699f568d7eaa1175cae4754308bdc9bdd2d35b00bc6321633cd2ae081ba254311e261111189366d0744723f071cec38dc33d6136b55f545e71ee8c8b15af087adbdeb40f37b40bd8b920135331eacdcb465e054c0fb798efd1529374c108f15b87eaa38ae61fbc751d47208c7189a245a383d4632807dda88658e73da2535e743cebda9fa4079927637ea5e9704d435cd05103498f557d8416e8f9ae021052d289ea385caebcbf3b05e17bb9105603b7756c90d35a28a428bc57b9d8c221badeb27389be70a04870cf06939795082cfd267d0ecece5cc1e101a7ff63e24eab7cedfabfd8bba2492db05533e8d92cef3650d01d6e516f4cc4155b4c3f27f98c252a70aabb9784e126692242d928f9cb4cad3fffeb4604d43b83048d708981fbd3d8f9d0fe5d4445ddf3a7676dc5b9c6cc51efed4121953570dec81b84be3e1886b494bfb88c004d19d3d1ccab1ebaadbd1b436efa02736e2bac899270ca3653f8c10373177c4bcddad6b07275cfa3445056e3199e846522d5865a6417e6653e2a208bf74f32ec639b6ce57e02cce39b8fe2df7a9d6b2e85cf83302441ded5f4ad005eb7033a2815fc2712019b5f81629b2777ad68b83247f8c449b312926bf12687e380014f80731655a26f56f08a6d22a45a1a61c8cd8bf5f2df2cae8c7900ebfdda686279d69c6d5a35817d0c9097c1e73d8c2f61cfad213f88a55d8de2e1ab96bac0b35ad22e8d91289fccbd0d8cc9d91985ff458bd7918b97dc45814fa6f59fd96e76c775d09cebb52f25d1f9237c0af245205e928e9faf9565546e5fefadf2d037be71cc2935c610e8bd15649d70a30ac47a567d31998f46998d9804133f6549dc5ad847e874abe35b31699f4b096221cdb36a52437e56edb491faedcb49621f232b79f4c8ecbd487991a946b8c32b213dfbf380d738cb50b5da561e9f6a2ca547e3c6d5631b7a69e313ec6a21495dc014d74241b292a56c55b3c7e94b120098f9736a16c15baa3f35916f6c6f38492be565671db9bbc16a8e8f56429c8fb4eea372e66173c23c606e1bc9b1806d11523455360b5aabd2564fcf7a8c94bb71799465bf1c90ba817ea39f9f58dfae81a4b0a23fe31273d392907f36dc4410b3c26cdb191f5332033e7837933e24b0fb728f5d32bf2f2d2b8243f042d07b2fd6b13e6bd696815614603b5e1c20634b1e0360d53f322cba64e39797b314b2516872069d7b4cbc12b773d5e0396250dd32c96181742a2ced4dd78e9ba2fd2f3501f26a0f7ca48c99ad3e3d4a75f609f42db0e1f8cc4f15487a36ffb76ade862ceb201f33f47d1101d05765197c2b702a75497d066d449f3a8d7ba140e2e5f8f1de77fcb4bbe06c115cc382f0e173fabad37d21c75d950c7aaa307949db1beb70a2139955e2c1817a720e7b1d9f1b7e8b20b68a56fa48a7f5db066342564edfd5328c9fecea51358fe428a3031da466f23e5e698150cb41bcbbca4292854f86e70b79ab56c3d7774ec9a20e0af6ddc4818c8ca177a24eed16756"}, - {"0000000000000000000000000000000000000000000000000000000000000001", "011a0a8d08494acd366931eb0eaf77099f5f331eeb12ab66a72263d49952a612562efd7c511652dba74c28778ce5f21cbd435ab922eb84fe96012e6d1a1fe97ce00710cb74d02bb4c417f77c5bf0967554b9415f020bf73137a41603ee3bc4a9535aa6e213b9b9497907ddadba43996d92cf87a418b83cc026e97abcff052eef33a85d634565a3a6b47294cefa831e6cdcd0d153eb7db22919890101f7967c4cc2450efa857ac6e002e316c36c97430583ca31032cfc9c261c739ce7520c61bc2db09196f34ee7731838324709570ff01c070f71bcbcb19a153b4be243856c51ad9581fa1acc171c4a6e9c5a908ae707959275eafe5f2e848cdc541e03f6aa1451183b9b5bd1c446d1dc75119f50b2f6060d7ec943b934af5bdfcd16f506f8ba5eb1be7ab74729ccf584439b7fcf7c016a0d616c50d6bf4cb8c44b335f4472671e2c37e021b4d6b332cf9a34811dff7801591c4e8fc60161656f54ccf658aa02df6edf25a943370e1f2fe41fc7ef5814cbbcfb98cdf7ed5d9dbf09ae97fb6b4b6986f7d341fe67e354dcf017ae76c91a30d2373e1cb0e6f540e2fb805e654559dc3707e310f085d64284b688531444f0a939660282e19fc6ca24f0369b7fb8e9e3fa9fe335c2e240c6eb729f9d7f255dd178d172bca3ebafe51ada2f029b5de4dd0bba526d4d77eade16b30b9a45b2c0c26236b279575cd601a1e7d7b7c8fd29b823630d72722c1683def9dcbf2e3ea2863fa02387c19723d171e9bec9c3733237350288cb46be8e8b4496dcb296535a08331ecb9bd38904c67c26ab19245747b4570d94c9f041e030f50acb03ca34cb0a81976151ecf119ffa5b63616fdbc987649c7fff494a0c367e4a384e165b0c01d8216f357c9304e233ec1d23e32cd00e5ec795a1252d776f825333507c7285c2abba3d654d592cdd0908e2633de62ca0266cdfcd751a004bcde10f2562a5611f376b5ce271dadf6fbad140725d57762aeb86da32cd97f8b882d078aea81de849bbacaded65e1d5bd7f2b21bb6a00a3657ef1463574c33209654fde478945e01f8ba3bb10586a4ce50da5bd16322016cb195a0f5e00d75167f10568d38bd0a83f590bf6110ef75f34deb90f54a4e24a5597209500f8c932cc4b4fce52ab360289bcf4056435b71a7b0b0b1c7e8f737fac3dd66b3e89921f103f171303a5d1305dc8da541423645576dc75b75f0079d5b4ca8cd25f3024c420cd2eb10f086aa7c6dea097057fb3ed7a26396b7a0b50e78f9a82de16af07610ce4a9e69f06515a3f96357f6fe7c0992bc12b8b806961feeea168425b37311076ac1afb8afb3f857f2171807408ed21784cdddb354c67c2bbdcbc0b3df373c2bed09bf55b7199bd306c80270e916461052578e4ef3debe89ecf6f1ba57a4fe0976c2bef8191f9e8f02cbcd59a996915939b9b0fe8c33fa7133307ffe79080bbb4e2e91a067726b658fb0f44b6d717730cfab0f2f4b7e9b896f97f205d529192e7f4e1423df04d410d4930f11d07f7cae367193a970c720f19b48b89910b1c53d8fa8971f6e866740a0d227c394727d0cc217ce15f0ed9300d1295ca478886f62cd42a7ef2a3e31ced2756f9890c11374c5c13c5961fd9a34cd913d39f8874a585f2e0b1a8cb539a135c75db45cb6e50c0ce8575ca24a397fd6e5b1871ac6e2207e88db664f120d1210d8c4fdb8c8f703c5b0328be2cc15faf35f20dbbd86d75dd531673db680bef2026ec43f9ece9b419a24e394719b61c4ff8502a5fc68f9444ceb2fa80dab20e792b5fa4dbb8fc2df10675e991c08f4bcdb348add1d5217c7cd5388e3cf1c248eae242df84396413e8c77db1e3a1305ed87ae50f54ab32d21fbdd44577b9bec0a6adad3eee7866a11bf5b525a80dafae0"}, - {"0000000000000000000000000000000000000000000000000000000000000000", "00e60a09b0daded79b3d858c2657320e58333aa50a46a68d58b7204a95ba7154e20cafce3b0c14bca8e9026948d845ccdf8dec2f9612e338e13e9f2b7bc6b0697c058bbd66ada77cb517127e44ae3f5e84bc3f5a0b3c954d26570e0f7841a90fb3f0e4eed7421ff6e50f914d1cdba4855380c4b9593cdb297704657a76440f3eb55c095cde5520e8d58fba66596181474ca4c6543c3dfb579c9e07c59039211871aec70de01aa75b0139f26ca45ae0abb76fb24128d4f33e9ef93865c81dbdf2b8b0d0ab036e5d62d8d2693f397730d5b7520822ba70e1d3372512bd27a3b46df1be7a7116e49a1061688c1d4453c0f14152401230479d2ac42cab1501bb250a8f0e99ada39a211f8563aac607ad763e853216db65ea16270b53f3e45284e619d1272932f1d10ba1e0c99e9cf55159e651ee456bc23898492898231f04d6b200e08661241078496d637f031f0f1ceedb06d0f59676eaa109882b744348db7a922dbf594ad92b42fe601d65839bf2b11492b4fc8a72f5023b214b0cf3339685b155f992d59513abd51649e50cd4544025885a20c5e9d26db62e630da778d20956208b3cba0ee34989d313a346e981d18bfad9a7a466cc8c28a22c4cffb61431b739985fe634e23a558b20009f68ca2f3ec303aab6585dc5d7963fd074d9d6172e55fefc40205cf3bd936cf8a6c3b47db4c38051b26fee7d33108ed454aee43dfbdde723b432c008ccff257641eb269c7aa2b9240cafc12b46a98346320a4b7a759a9034b5ba1b57cf317ac4b2a7b606d1b60e5042fef8bc59075bbba4dc3183fb5867259b6aa55f1804f9c9d113dd6bfb1cac1f719cd541dbc02bb22cee5a52dee1306861956f955c1df4a1e51a56375a416a1b2c75221470f425079953b3382d023d0d29238ac962f9fe5120f24f95b7ce96173b7f14ab60f6c0d1690f3ba1e0016f947ac2815f224997e362df9f370cdf0e9c69af2e2c46192ccbfd93b2bfa76483d57dede5d6b015810aeef7d9ad6f39a5cc9985c3b3d0ac8dd85e133cad0f1578c0aa2f9cc7ae6244aa7770fee9ceb4721ef901c3184e265f0647f513009d302e898e1f883c0954075e926d7ec82d6a5ba5710f593bb97d048dbe51e80e1445a4dae51b61d9a2d147cabe8127437f3f3fe51a1b7438dfd0f035ea3e06f3dddcf2da6eb33ed9b70189ddebceee9bb37c178547606fd2e5d98ab3792946438f635e6f16e39c9759521266a46308a23857eb0f35ffecaab5b1bfbcc75422af39afdd801199c3443712468c162d955dbeacea4d1b570ecfa53a3e2206035d197b6f42d995147b16c55fcd9b0dfbe7b0144929e5297de8138a3f822835deb7e7ace5dfe333a5be0678f59c70e33f95ff9fb1b946b89fe902d67c77750d551619f69a00ced7be367a8b7d15ba662f1787f401b4f88b940d608f986952aac2c5b5fd6f6f96c43f3d322ed389716b03e403683e05ee06e333513bd9180adde39fd345232c9f9cb4ba34d928762fd43e56974395f2e8eb63e16d6fde15d37db47745d68fd88ab601ba9729b2898def6d83e10c374c83ae1b83b82cf40272b71664293f35d605b0cc44bd0bf1993ef005dd077567346b9660c0d156036ee4ac95f9215413f1b30bcc3c13a683b81cb9a8e28feaceba092148ebf1d90810019289f0c11bed39b95022d01de30091fd5c760d2bd68caf4f3c8c904504e8474aa807368ebc9ab80b7b5aaeaaeeed0fb352c1b2be349d9111a13c6604351327ecd29290258c4857f683c263ca94e4dcbbb7136c4af16149001ed61ad379b7a7aaf4f904717e312c39970da0962e51951d42d598cb30ffc47d5f774e1636d553b249fabf42fe4220f86086ad247efa9f002d7baa2af49d4d0bdd1e76d5fec29c67106bfb54f6"}, - {"0000000000000000000000000000000000000000000000000000000000000000", "00e85247c083dda6f079877a46f5820a8f2eb641362b2989caeb9e63ff802906e7b9f19ae701113feb1f024225c44ad11ba355f7b7615a505dbe42deb4cc7b43521720ff2d212bf24cc587fbe93a8994e59db787017c6e75e9976d31aeb0d4b996d9a4e39aeb5f37f304caa8584c66f5d186d8e273bd18cd356f7817a38f03eba740958ba406d6fa314f22be117a4e9c576d73546d63627ae2baabbf90c91dd7555aeab6d9b9488202dc4f60d6d6fa3f1c5a22e072740ef23021df77221d72a386422054b737e6c62551751f41ff9d71d1281e5c41dc17d5bc36b59ee59119e0a625beefd100a42580a4bcb1576087739ec5f30c4e9a0eb73b3ecf18069559405eca6d087c72e0984a19630887f69da35a0db6bcae483c4ee3fb1176c9f4c224f60738fdbe5f0f72ca9f95109e2551032467b863246573f8f266b9197dbabc072324eb5e89e1d330fa3a0d19ce17d981012765a3e449e9d18d2fd0ba95291ed689cd1dce5204ae6c3ea70cd86f4b71e1b834b07a269ed21aa36b028950c65082c6c82b0f92700dfdd1ecadb4730dad036e1252e408dc4e9edb345e4668abc5f40e9180aa10ddce4a5cc7416f915ea1c8e031b78094d1fde415962d6f95c5b239ddedc75b339dfd787b515e9d6cb318a6377a33180848c801d22dbfb17ab2a678f83f393be3d4df84ac03cbfb7cbc32076313bba23f3d1d5901d4e73925807634927a523a05dbf6a20a339c275f09fd794ba148bf4b877e23ec53d7947aa8205b1b8703436f605943f9c1c1551860dff042666c355bc823088784551b88450a53ac124e7db84845357a0cc86103bcc8f4c65daefda4149121ea257b567acbfddab12d5a773d64aa7239af89a2f1185a244602bfd338aa24ca4208dcaa6139c711f6a588da1496005d31d19b40c06f0ef2a58261569fa68ea7e217021d0fff1e0202a09226a44550d25789d85abc5add375f397dc17727af17e408224c6fcca149d801758b26e2c338e9c517cb04b596c6758cc0a3c28d9fd8c3c1757338a03c327abc9d6ed6fd3de32a14fac06b8005d5cfd0fe9904aa0b1fe12d8c8fe8a5449bbc7cfbcb1247397fb906350cdf55a4b82360eae07b9f55b3892aab1ccf7e39f6cecf2dac2e4bbc66c77f9858cf2689e9fd8de36583f458f0a08d9fdaaf98cba6d9cbd6354ebd964103e157c7e627f189666ab1eb15a0c4868010dd985f183f2959aad4742d291e73e69561a59122842b4848049aaca3ddce13496658e0f1c4b6a9c0adbd58152336ff0d97f7d700aad2dcd9edb0df7086fa08192aa20d67691b374c04cbdeafa7208644c89a4600976208201307db21181e151717f328a52fa580de60e83e95140baad0462a93ef7ca662c06bfd933203b554ce8e3725b371cf11b3d33cc79523b0b5b5779eab9e816d04456fc232557b6f522474f467b5ff126c1b1ba98f0c8e13f2781db937ef4f31c5b63fcc62cb8dd7418c0fc06ed4adda39d131c782f19f602e46d66d7bcbfd447b3714b8343235d4afe537deeec0f961381629dc04aac1fdc2200b1b60705053955dc7643599fa8efd1e15fcdb53dd9ad1b008d5a0a2ae3902dd833efd101b79edb4ee14081364fb46ccf3e5c1ae678efdecf02029e67db95f625b845d2682a5571e332bca1b25cf0ab44e44b5cfe3c90ee175048f2d156270e2385c8aa828f6da8f2c5f7f6aa7bb2343f4b7b312711d0a4d391e077e8e969279022b38826a6255aedc261cb1f546bcb43c8c16e01b05a104ce9bf2e09e17acbd5bd50c6207249544aa3b44d259bb6c663ffeb12dfe8bfa206c8219badc5b8b684fb2cd0fa41096fffababfff1d135dcd96137092feca89f1e1e7d7df2d043e05be23804f796925113b62ada49098413dceaa64b62183"}, - {"0000000000000000000000000000000000000000000000000000000000000004", "00b4d3e45fd4534b6ddcf4b787b87b118eb352c776018b895bbcd29420dd2e556bbecc22e16c385946cf0a624c72df66a1a1b2f5c55ff7570c156955f1e0270a67b42705067ae8a774e425d831146938fa2b3762029250f617621b59a0971781316b926df1d8104c84082822dedf59416ad008e674ae5ee3b5a0e2f8ac56232e538b622a553f962943858ad02bb903a7e83b812fd5d24a7e926b00c55c348d8678bd8a99569d8d7201aa147b021f1729b645f2a0c146648dc98dce4ce92bc587c2fda470e18458131906e50eceabd8772f5c08b9f6702c8c08c4c92ff533116c6c4611e59fe0211e61dec8c4d4692d011487f18b61f99ace0d1a366b10130ea1f9d026cf376914a42c297359ea38fcbddd10886b0af48989ca6846634fe3facae519db2ef6f2144c46e48e1d25a94b251432322dc9c9ab67f91acc39a89ae25612e6dfa145d4f854a8813a97c3b6fa02041d0773bb5919392bcdb4116241b1391b33b2586619f9691f53e5a997f3ba43e57c763cd5265a4d946109d3dcc419054872ad1b45c0bc71527dd0aaaecfa50d0f1f6530919c1e9b339118679f37ed00b275ef1e08c89465960a8c495f0890b4fd446c482f3a79798d226af30c6e4ac390febde51971b52c71673b32fe9b166f2b03e1d7333ddce7042e4cb7b6333c025b625f68b464d30c5af3d75b71b811f96dc632992d95d3ba0719e6ef4feb170d81096bbcad7f5a9b33a39ee5bb0f24e78aebd9e320f82bc1e503d109c5b0734ffaf408835670395a50e7663988cbc6e743723af99f98b931d2a5922fd48afdced489c723d6ee62f7427fdabd109b5c00aae6451970a2737b665cba092b650be6282f3346e7b9f4fb81d4a23b56b1ffc8a784919fff3f237bdcffa389be19e95cf4fb8861759698585e59ce35a7bfe9611abc19d5cae62c834dccaa447cb3adae00fe2780561299a4ddeae15c90231c1e5f1cd7dac519ec931dee65529df7ba52d5732f221d62398c0edc169de40cdac9c90079af34e3964a5a6ef2c15e8a9b1dc3213e41e3ba158306d4a5487aaa5581317b40a007baf9c9fc4bbc0799ee524b02950d48b4d19a514b470ff6d06e20c049da97966bc76e72d67768599ef726927433eed5b22f9c1dc2c92cacdd41ca405713142aa32f6dea9214517959f4e1fc41c99b56b15d00af023120760391fdad4e254273f2ab09525dfd7e4cd40e632c5b1e5dad494711f38bb15c9c4118eb9dfc8210a6e92d5c094b952e64b33373648e4dd49d1ae6451c1b4710754a74ec5885343ceec5ca8561c88d836c074b3e0aa8429909a8c920fa93543f9c5e622444861b84325e7a153d3f3aa37233236bca55351b165e3638d4bddf0d258679f840e64357bbdb9ae8473ee94d42b5e40366d6221957333c72fdfd94072b5cfa686b0264d7b495f90669e9d0026397b211611d6e700be423c77c4603ee115be71052bd0ce846914ac4dd31f40886bfd878955c8f76a931b51d37e27cea0ac910f209b841da8461baeb6df803481ba527f111ef92952e0280788ab3d2946f098f938bfc1f12a5635c7684f256f56f9f7660374119eec6c341595c327a3d798a9505f35e70c72cb88da03a2975ac6250e2db29575206200ebe764072bfd3cb2196f09dda07b2fc35bc4096059b29df8d9971a7a554f3518fc3345ee3f0fee9c6248d3e000cccb6913910e28aa8f03a8d80b8d6940b083b158560c696334af2f1a2aa348ef608c4f0c4542335d6e656cc1803cc66557dbd3acbb1ca81f62ac7089d4e30684cdfcf4960d094288ff1d03174a5c7141d588757bf1fc0a34e6f93f3e6f7febabb5717b8e20b3c9ac27bc9d721903b317c7772b366313dbdab9621c33601da77fa4deb5bd819266afd522bea542bf1"}, - {"0000000000000000000000000000000000000000000000000000000000000014", "00312dad99cd5e1c8c62b13a4e0dd02d358019d54804958d390494860f4c356391717c36a521d0ac82af08811a1ab545d0439b9331128756df25f1b271b7c7370a474c9851a244ed620535b6e7cb05705f1c180a0a1d0486846ab9395689555f09c8c16b0b5fde1f5e1a9f817bdfd120b3b44c66c71a685ccf6d289e390410c755285e444ba475f34330857eda40f9d6912b1c17bc057b966db755e2f171f617a6bab4a0fbde0bc001c4c7c872863804bc7eb0c1adc75a755f59f8d05f71c5f3f0626ee9d1b4700a0879f11c573d0f1c8bd30d209f68f66f686984979490f6dc05a1af9a38e3795434f32374568fd2bd7b56bd40e035d686b119a58b19b848df69c9bfdf16e9d61fb8e71965c9b693a9332852d798ef8ecbc33a3916e3ba4d9ec2fc34df0cef28978a79114ec27be85383c42acec32151d3de419935003eb696d1e5a8dcb9d97637e447937dfb1c437401800e3e750159508b53a28e932645c0bfcaf83f7b10d3e6f44ae4d073e31822d4155bf55a2d569c8da813528245eed072c8b65cc2182253b85e3e13fa2a2b165f9aa681cf7e9db02ce1f722d38ed25f0fd42ff00982864b58c40dc0ade4d325a4c18b2917132b842110d7b8d9d29490cfc31195071e6a19f979aab5769f0bcc5f445ae6e9afbec281c04325296201c875d8d521b781efa2d4f32f5cc80325ac68213d3de14f20ee037dada40b9718fafd2e522a42320fb58d4f5d400c0b5474745e0fc5dbdf01e3936f2d7109c493b53c130d65623529717899fb43f0e45c2fe13107099b79e60fcb86c9e6d970831392b132c7e7a2666546747556078ba1614852313957dfc0840012e6d4d0842ed23a1217135d7fe4639566ae623acfbedff8f7294fb51126dea7e8b79be93cff1177cad3da668ea0545ddbb929de496a1bb2b113db6ca4f0ccfce6171dd7f969a00109ad04d9cc86873b385a86ca66bf3ae3eada64241641d8dfa69894ef20f689c941e29712cc88b79bf60112c7790358ee58cd90b12f6124f02a8ca8baa2a70d9d9a8a21dd2194f665b15d8ad29c55854192f07909085ad78ef19addf964e7d50bcbeaeaf6a6dffbde908d7524746a2495af12d94d84f31ee2dd63d9a07910c7df87668a9d8273f751fbf7b8ea496c809b003211043faa2b2cc3bb6c90d59e9f4dfba1c910717ab901dfa74f75485f647119f1ba74fc1c9678669c4d781bef355de2e22059a93bf1c2604848b77db23cdf430289f9c64c067c53e3f6d0aa79aae378785224c6f7170ec5e39f1e7107dc6425c0c3d1f6ad78b4b110bf0ca427e76cc32cc8b00d93129fabfff18104d2e4440d533f88aab9fe47d68373df72ec520f3ebf9e285513992dcccfcf8294dde182c53b7d35cd7a2a6ef7fe2e7e56ce2351a9b50d6214f499e16e85654c79f2aa040eab284fc4e07fd59416f7d7f97b1df17f1c5de00cdbd3670d11ca01aaa301b3e34f81687a8cd8078309e9b7e02e1d1b09bef63105b68c4bc1eb08b6a91920f7bfa2b55fa8876a9af28c6d5bf249f6ce749be904bd11b673c89e93454f35311aae135e7b72f417d004dbfaad6a928b9b2bd617a343e1e68a0c9a16ffb80c19c6cd810701265e93f442074503aa265cd8e7464c56667eba65053f28e015ff67e688998f4ebc2441068a6074d142b5663cfa3215c2aa8adcb44a08c13a327b91ac301e2503e44d96cc2ed3a199dfab9a9a623a289cbb80ec1e6f71a7c44f76c34c6622269e1ba5704cde0996690aa97b8cc76814d8c3cae4519912d207175c2b1a84b051e7aaa3a224212f2b05ef1d458918df6645bd8fb536f7d4953081d0e4bdb8647e10311d02772a79a16b6b1ac8655d39f8f94235db5aa1427d047c7a4c62ec69b1c79ce16c75210739129bcb42"}, - {"0000000000000000000000000000000000000000000000000000000000000002", "004d8a744cd98656cf97a3c2dc6495915197d72f0b1dd822b385e4297d6e61344ad8cdddc115d70f7c131603a11165c84efce2a971b2782c6cca9a5ab58bec19bf92cb6f8fe051bd1c781264e2da9a9e58fea22108c6359763855e2ba211d17a6b4f5c9545717d1c650ac81b2ea92af9878b2062ffa91c06b777b7fcc2241843e9f705e23e89504802cf9545d329554a2c882e562133cab1999884e686b68a105a9e9bb0027fe6e600a96b5e35c7e929f57900c2fb398e40be0e2a119a3f241f8ef5117e7ef672d73fb1d116f28b321acefe04d6dce35952be03d5d47218a998eac9f29d761c840539743637116577745600d7d57111034d537accb21166058073633249b0d432cb0d4914496cad52279d1e9814b78799af6b515b948969645fb9675739000f260be93760d96e6954ba5271737345476af47de8ac2c7a2b870b5ecf87cd5513a64721db230756bacac5024099519f44f6af93ff72d70fbd6c81a288b8f62405e6c9ef88cbf513a70920d012f26c760e41d350f10df6a20e07059d91657542e980f154a619545c8ce7450ad73e89961177764c375637ceb587103f9f3cc9053ad06c17cb01a1efd660bbcbf345804e119af64a11dff1017e092f6a66f677b33949c0b218973f3d1724b914acfd123c215a1f139451fc80116c44f62eca76ca25a5d85ff3bbd6fb19fc006f2cef40aefd9db50dca532378497555e27f214fb510c322f3b657d4014f965c4360634ec173c3a913854a4d43008c1de83f6ed054b2dc5eafa75e82f6ff835af59a20ea97a7118068d69b4723e885970b68f4705df1475de57ec5ed1639a691ed128616cb8f2516d549dbfe98843720c91dfd474b36d2608bb0cb93b33ade837982544f13b2175ff1a1cd5d8e071fff24f0f9c238fde3b619b81c1bbf31f0766a28bff542830cca9e260248fe9349c60165411ed2b204c9f604818995fb426d84ca73b1393cc323e49dde91a18b30657c976ae622188eb5693816174afad40ecd9b69d154305263bd1980c8ec11a93ef6fc7e8e143015d634b5be01eeef1368897f3e8212b2b6158c3b64abe03613a4c54ffdb4fbc3ca40f738e7bc94f814eb70f76583c663e79e55c9309019bd2d389fb8a1d99ce4fdcbc3058fe6fe1a96e5993f0663bac7cfb099e76924c496a6dfedb0260d4fb739e4020315b8669759930b37c1051e1e86194a9ab6c52345c8cc151fb1c805c62c15202eba4b2d57a794881403b54cb16ddf70973e41b61758ff04d1c67b18ffda05cffe86a689aeb8a5b840a2d2abb4c0c25e54f286040cd5ac22474bb2dba07342756c37e4e12d98d2d9110279453ad11762a34e51f904129d1cebb7569bee0b3f17e3a8e266f3369d197adafe9c8a928fbf1a3c30ee5d1bc5d6a2a3d34df8ca33d7ec2a95aa9c64c60397063dd3de1cef7460962c8e5cf1618bc4d23198384699d8e411dedd194b84abad6b9a0b4cd1bdc66c0721a1b99dd118fee67e2270a04e79e13a94750771483dbe9671121798d823bb70fbed6d6ef5ca9d272f0a8333dd5db7f221f0bac12a36dc81a44a9f9aecc10ad3891687c96b853f4175959dae7ba6db7b96edda1e970bc1deee0cd3fc4d120fee54d262f89618973b387a754809d8cfd75a61f41128c2413345969e19620ed188f3910ba51f9823e23fca7700189835371a8e32e96b9dc2ec336d63f2c50927bad752b2469fb2f1177429a5fc4d8dab04748483912b2c2d252b0953ed32d78bdd40e5a863a55c973260742bf69ab0bb44930f32efd0d592b9dd866012d705a32ecd27dcd34c753eedc2ee2a22fb9d8bf725c4c9527eb20cc4148a6d17d25e35b18dc259c6cc783abf5f5c42d25edfca7d1f42a28814db7cd2f5ded48caea759117add16225b"}, - {"0000000000000000000000000000000000000000000000000000000000000025", "0027319d33403e08c63db54ab050c0062b07b971951e2aeda6764d93fbb980e42c4c462f4d5a495ef80b04c79f74c8369c3fd91478677353188663877d7c49108063ccc515873ad336216a947347f0b20ad2c31f29e8f2104964cc433da6b34b96455d1ea947beea0537a38ee14e5f4eedd3ad37105eef4dc639805345d72c2b637415aa226dc6520323a0c4b5f160631406532dfe3287c19b127b8394a4b5862ca945e8ea56961704c0dae6c1de326d990122565f727cf89bda51dfff0685fb2db9543eeaf10f13f5e4d923e9d85e599a7c218d35b172e2b7ed207cd2b01dab388707f7da3bf3476be3640a9ac94fd2fb257b49c9aac208587dc7fe0edb9534d917d27efbcb55543345b546115ed31d433a0843be9be40ec9c13294654c768ba58a704e7f2e1c283b57c9e42175aecbb6032d5f92bdddc551260921aa2ce62b93119c9a947413deb09303329bddb614062d2ec1f10bcd39d477921b22ec939e7b2c9b59f958618457802f61e3ea7bba0e1c62eec31b741e94ea165ed666cfcae4f27fa9d410d54cf77d46b8cf0618179dc63e6ead1e7d7fa8facde8d6e3cb36aefe586706cc3e204fc666a0ef26a2d19e298bed14c8c8fd1e5650c6b90bdc55b77349896da96eedcadd9d5905d11cc092a7df135870c8d0c30b61bd04396af811ef611dc7349c5d66e323ea2573f808d171de43b6bce22907327f46cf8a9aa5175e33ac29376681cbf35214c63b51ce17b190e787fd74884856f36c1e60fe5f28930a1cb799820a09c11bfd33184bacac8a90ee760fc7134ac5044767c93751ef0406df5de772607c9914e31697ca06fe392e39fbce4c6177ef52c36a325e98856f3b36d230b00031a2a61964f256f21ec8f67987f519bf7398c54b2d92cb2ef43957f81d5a0b1fd913af19d86d47f1178e5310c2e76b7eff849b7772bbbca1012651fb46ac1117b2ffb482a15771df8f67fdb6c52052fc6bfd92cedaecb004da00657795b73c9e132301fbee6920eb25f99927f0ef1a3b42169e771b27643e09b492685d83239ec3b3f92ce0dc611afdbb1212081b7e61a84f2f8510f030cf5f75bb4466da7b537f2a397418f554f35b2a1c04e5f4bff55ec50638d3b10aca9517d41a6f9ddddec658725c2ea6376c9adebc4575623fbda0378d50aca4f4e04d0371e9d7119b95103b7ad01c0f5d7f3998725e7afe36d959e77d9cb3319cf54a18ec4315971eb40a1b3f81b253e837601016235193d0273707428d416ba51f2f1d6f46b6f7875518153325a4038977a2ba931fdb50774856fa6e5a120bd2d79fcc0e571300e6a7043adf89d0327c6a631df5254ef027743fd521f7353cc581bded4cd29ba92bea32050a5749cd503c431f97a3280199ffda14f33f91eef206de879792be081aba684a5e4cc2991208039d298085a593db344dd351f6f07996745bb56d66462e7f354f9c68439b5fb79453d542e1fa1b56e27605e09de43d55f39542065160c3e95cc21fb6dcf4960ede7b45572e3f3d73bb7185140cabd0a6b5300d1206dcfc4c1f84809bcc4a73e00b3965a58a6c4eeb361757d1cff3952ae76d33032c025935125790562d570f4a2d2e52209b6f864691a08bff02e0c7069bedbb4dca57060dd43d8740de07f2dad6090a849eb94c9b05b04cda950377881c38e5154fb7f5b32e1cfc5836186b9c571217b607f41f34d95de62e016dbc6e48fd098445f588d74783475a02fa681e1291ecc3573f47226615aa7752146f0502624af6c8fdbb3c9e5f91410b6a17cf90ea797fcc170762d37c67e2d5e55c897f16f06bedb51064028c9f8235e09e483ab4321cc3a013a4f784c9207f1f7ec3814614439b24638c15169246c8fa938fa84bff5d628705f5fd5fbe29d9fa1547"}, - {"0000000000000000000000000000000000000000000000000000000000000004", "0005cb49890fbbe1ed60872c58f31241d0197d915d36c3fcee36ce5936fe3c495593fe6157294efbb2330ad15a4a30041898f8f8311a7bab44e4adf259854d2b81cd035d97f65127526425c94bb10d6051d8e67c17e578e1f956e16ff1af6333a9d020474fea3e70482e024cf5b350a97b1097b371d3616391d49c75fd3d1db75ddbfad85308ebd1334600ec3cd37937fc4f6d2d52feff5e0e73fe756f2548be3793a1f2acd2477708fc2c5fbe129b14e02024afea5162017961b711ad31aff4b64e525b73921703ec302cec99ae1bfefa74131f6bbf51c99da52d7fa4d282b145c2b7897f8f661af998ecfe222005882f827a4b1a166ab013be084e09fcb705f50ab19922e4674cd070d6825e3ed37a4a1cda93e4b713208cf2cc044aa362e102bd8db9586819e35ac93348e06d6db689742174f53a81961c3003921347ab7be5a4013fe53b3e7efb4d7312e65befb7007b362dc0441d6256eb4012b4ba5f30a755777d431aec1583466277d92ed8e4a5a8bd01274b79fb437910fe81a7e8d07c02976fc8d9436f6d125c78355d201d8e0ef26dd44a13f465e41262f60389f58d76b404124d0eecbe09a59ce24a12d97026e984be9132ca9e3dafdf459f5892854387f6ba23598c62b24dfb43b812d118eb75227cb5fcb7c2334fdef36d23ac2b907e1cf18c92cb87ee0858bf65b342daece21e2d1b6c7901b756f29b83849c6d252186c1fc5824805cb1a4ec0539205e1f1705fd0f1ca3cb72b3f92a39aaf87d0d08a2b494e1cfa6d1226f856399d7060a80ddfce7f43bb3b2f32654e09949e90500bf64893589579e11ac063b5629fe59edfd4ff103f0727f84224e49f514e81c669497f4189af9719d3231f3b8e37a2c51d9f0621fb11cc7ea52e3f0d4b035070f318a6dc768fdb4623cd62476de96439ed38834bf3075ecfb44621f38c200c7c6b4f70c436ca208d2ce816e7dc710551fd681035f205478de7afd46b183859d29cc221583bb7b5413848c8149e3dab13a0c54306c356489ac577149282e9b3b7cf417ca54c1d574e10f5327b6785f5d05cd088902cfd7162c2187b5f4a5d36391ee0aacfa805d27173ac325a4943db540e442d96c14920313dd41d2134fc5131355b303f6de852efa7c1d617a77987edf2130daef934da0994b6b6657053ebb62eb4ede769801b8232cc78952c3c73353820566ed32db1016f37a3f4fff0e64d0a7ccab98449ac1c20fc1574f5f1faf10243be3ed0d4c78893f73d9004dfd9dd9cbfa192e10fdff37d492abd3001e26b08fbed6ce81425cb34d02fec35a1f9131637b15d1dcc6f5dfc28d07168d9c431695dbf934fa45f9dd277f425534070b257cd0260545acc333b5d779fe13a098f649414cb3c0a8f78a5677e4346b2668c3b588f87a947969822497f34f9300d523e4b4c9301fa0c8b7df825788db8b60de94f149c94e536bf7724deb14b74984ef6632e4cd997b420dae69ffed044793dd91eb570076688393ff3d87f86c3c3f70cd9bce09371908355ff84f32e52dbbb8bd0d4ec96000a40cbb5314d59f3cadf842de3d5dc21314898dce76e40b1b55b1f44747f867b137cb71a39c1ba15e7a3f74af47fb6962e65f4c1700f9b1ca5cd82c05d3d511e3dd55a6ed46e6e6fb1d1e065719eed1023c401a8958b40d374820e4c823eeb9af98bee04a24c2bc96aead2d0dfb51534ba45e97aeafe09fdedb0679e5e55cc957f3f3d7f479206de77d4b0eeaf2b23f7c82343f23ba876e863564dee452d1696eaf4a6507218c64d84342f132b791055e913970bd4c6ad22a368c0f8d9b936a17e8fd757e82c09e818dc0d4974c0aba573d8e8ea4a4914e027491d0c6e0f9d26ca6610e5fa24d072badbdc75312399da890090cc1f47446"}, - {"000000000000000000000000000000000000000000000000000000000000002d", "0079283d17c2603c55e540feb3fa41b94d64589c5901b69e428e54993bff01e0712e7b4ddd5a778fd98b00ce35210c0e69b56172506713c5ec507f51d3bf390191750d1522112590a260253b879be5712872e9380ce782bc5512e64d05f25423af49c751aa1916ec68198faa507f9bfecfb06c12c4e9a08e8d8c01f7c51c1df9698d5271309de07aa1f6707c7341fff9fa09652a153ce2e5b1c9199c6813304bc16844f00d90531400ea7ce7613377cbd2dd238560d1c7018926dc278e11fab1423e98e7b92dba524cb5e7ecd2646518f3c314f7ec23fd0e9f4525723580d7415de5afd9f8afa73615a658388e03f6a619845d974d1b353d888bf77e027f885017d30365c078e09fed11f999372c796c1316a99af4bba29f1bceb96cffbee81f2b46a9bdbf2f2ff7c6c98e2293b7748fe383246b658cf67d7060793dd206a4fe91bd4b1c8a48d30e48269a597dbbef150439d9c69f4780be6c5d2cb7466f27af4ba5da798b188cf32f686ecd81a907e8844566698232c9bdfd2304ac5d231eda4a68fdeff3bfad3d39419cfb9a059832bb2a00e7108d6991cb36e5cfdc9369eb7579d72604bb284e7143d5007243f38dccf99fab29ac1e767c1078caa048074e123f95fa7b4ef1de06ba4a5c81c8067a61c613aa83d1f298c454a5bf84f24c40bd0ff406a04baad15b8b8ddee805e0a7b0433b68eb5eca3f0a35a5eff9528523f679182aa5513e9f6658df6411209793cb309a0a2d33a0443ec128d779543c9ababf1728473f2e1227b56364523bcf74d52e65e4be1f7855b5b5646c597d5d540d955fe67b7379a39d7fe6b010d952496990a268c46fe32b72757d50d2c166bb4c176599bb526bf6b9a592a5ac57d4edb57ecf1430ce14ed354ddfce3ddeb6ef91acf5c192c17870dbe2141d243c4b0d4a215b043602211030cbe8a2acdb2c05009d28a21e1797edfa45b2a63fd25eed657036cfef56b7ff01371da8a56ce0d9599969a95adf433dc2443e8bace91c54f8d1ec58d547f4f611f6c6e41f149a467c243067e3567b3e5b758b6ddc8097ef941fe489019336647d54e87fdf45d490dbed6119ee1f1b80412c828e383cb36d51a137838ceea3b888f5cc56fa4212c2c514d65f4c09d244e3ea84c5254aa21cbe64b920a68c0c38e1e0e77e9fd2e7c86e0e08e84688a129030bbb3970c8f6ed4d4860fd9ad4f92542aad12e23071a074cdde459357c03534302dda1ef3fc87e38090c0d61f823e98f2bd99c5570707817d5cba7ffa7ab11397e955e5f13e12a32441ba47a64166daf56ae5912770975444d3a39137ea35d1e36353238443e41ea1ab56f20fc07b71147eca273357b2bc50046dd8d7016418f1ec306c3be97668236d596d18d4fc70e351040fe9c828a6ffae9b608b622f26df589afa576fa480216a86f0682dc451e4712cb994ca6cf074c1a87130bf8caf65f4ccafea49b70dd5bcdf0a4554670f3b014dc5d90b05b8c1ded9955bff2efaca3879e5f9cd74a75c76413aaba61d797c57044af0352c8d8f9d5a103be323afa53d875a8ad03a0094a8d8f85ed1d4a6505bb472ac8cf7252f609f09eec7262e3a7abffae0905ac7afb3f8881b1b00a352edd3272116ae6ee2060266e99545a938f3cdf45b47ce06c3f755d37cc912c03c100225f362ee3f15b817c11128d1aa209bd51830bd0fd69b7a4f7ab7d57b37f9ae4d8fa344196f1cd249856dfa24f7e23bf14fa83ac54322efb2bfdb2ee2ce466b12c52107afb1162ee65ccf22711b5fc793506f8e7df08666f67bd83c3d4703eb682249b7a41630ffdaf0e04dc2053f298a3269ecdd3ad563a95343f102c8260005646f6b4499372c83e6e19e539db870f143d30ce549b59e567d466f1efde59625eb51a5b88"}, - {"0000000000000000000000000000000000000000000000000000000000000076", "000be8f3069e2d41840af4ad792af3ad9b316d18f429af355677e4cae5e224134bbf2eee21c2c331cdc10347b24c4af87f2bd477109e8e57272d5985b43c621feaa6af16ea2c31a7faf36874d32185bb91fe0aaa0e450eb4b1ccba1e7a97d106e743bcf5695b903e7d426c76ae05e6522d7052068973ee5a2dfdcc1507c23881fdf5759043ff8992672dc26ea95e3aebdfbe473ad4cf0e0265bc4d6b7a56bb2958d71ae92b588fe003e0472aa762d977df0c62b39d51819cb6788a316816f54bc6e998c03142b24262d0eef4d946e68b9afc0e12fd564f18c0dcc71ac3fa3120d91e284ad337285fa7cb530eb570aff3fbf932fdfff0d277d39ca5ef0d426aab3649117d9a58d31c35f62944f20afa428a194e748cecd1e4e6fd52f4d90a63f4214f62bc8d53159b294f2948c53c941d87b7926c38129686180a412e5b74678694ffeda791f7e21fda9b637982bc405f02a56c2eb91b5b3b566c31acebbca57dec4259e6232ee296fb3e4f098786d658302ff26eae10c71554b00b2246161450f5d187261376e2b1b19dab5236ed6d0e27249a62315145b093b0ebcc993e044f07ae291004585d90a75e46fb4598f49aa76ee252277999ceea0fadfe35760e4121c62c2785c67e008a3e74f220c80bcf21ea5b08908d709aa2080516187a4723f3dc042502834dd89f8139a45a04e0365226699a37bdf495059f92c769722f77c94242484f14bc96c0aebb9a660991cee144daba431c3b553622cf2056e586f80c4d0ed3fdd1e1b1d991e74d12ed53d773ee18c5fbcfe1340d44b58019e2356112859958346c260ead90955d05c8a2e79f4272a66a2ba2577e3f889d41913baf260b2f65fcb4d70bc8ced173180938243af1f39c4a1914d8b2221abe36cdf7f431c90c51dcc8c78751c5161ffc9d4fa663927fb24205a40fcb471d7325af6dc2002b46c3f74a4ed931d360f1acdf1756daacd7661f1bb809889ba6e6e3c3ec22700f1f65658f7492e50410a7762247a351cdaa4962680aeff6bceb67ab6ec6113a2f1c5a0ecbbb0066a7bca65c63b2a0277faeff069949ef496cc4e3ef5ae13c33af7c95aab3f4e58e4c898533fc1fa64dcb86c97ac75982bf5f3b9d8d8329c37c9ebd4b9835e708690eb36fc10e614a39384738a59f5cba928edba032b3f68e562429b2ca1b646e04df77be0909c2fb3de1a11093c427c169c076da00222184e01e4f7f48ef2dc3c668ab34bad5f27a64a408052a85c15b91536a220208ac5cab01163b6dc2662afc5663caf48ab7be83c2ee3f4d01c51fda36c19c1c35da4c3388e2a728c4d23d7e7531e3e4faffc53a40e8679b5497fc411287d42ee355b75a15db31bcf72396156c68b3d115d701180fc1e3a3fa5a7ddf95b44082dcf8ec26dc1d99a2251e7b7c2f168dff9bf4f40140db799dc152075a7b340d9b4b879126803f6f65084d17b4c32c87a9e60200b52230e91118c371ac4f22ce5f2b53628f5d704536f9a350b29ae88258f3f131565bbf109e43815c2126383838d9d6134f1e1bd602e8b678b5ab508fb741809fe7cac6c88b7ac4eed0117b867613c825a8acddc1f7176765ccb1848c02191c286ba567166fc57086e2d360b92eaa0a893b89812ee9ef58c4abf841c2bfd3c8dce0a3ca41a91c0d9d0536d45e2742d395be42f0d6e890928e42d0fd27dc084fba52388af6d9de6ce45816bc998e44ab589aae063c3b9ca442012186cde20abfca9ae536effeaafd192427d5638d3cfef1fbf2b455acb91d48fb768e9a06c484d208a9fa6756e385a917d814ad8e2e7599d42c53d3beac1031fd957f7373017556a60f24f3ac4806ea5a3ce5d4718b05b7454b524ec97aef1839d39e0e0a07c96b9d0023ed342133abe0686120a35d0a1a"}, - {"0000000000000000000000000000000000000000000000000000000000000002", "0014b7063799854fb8a070b8ab2921ee9d5cfd845610453b2c681a443b7c8222afe24c960f25ff5a834107187da435d1b17f1976f18867d74c629cc81b9c143476adec85aaeea1cf7746c5d4452e0b001c5b495b00a3c007124af6472d1706d6467f7005f3affcd9de4cbfa72bbb56287ccdbb45b1bfdd4b921dca1d25a70f61da071fc5a7bcc48187a7487ad0c645e79887d41524d6f505b021abf601b2452cbd4a38cbbd48e6140083843fb7c9643ba7a5233d667b0aa95029718c042a692b770f6e6ffdd52555f97b704d3f054ebe1cf21306fc016e8fdb4b7a77f7d7e053c5aa858bdca429286aefa65e288955b7fd456091c4a2f204a0ba2c7e021870e50fddeee713f9c1c002fa9584afd02876e907d2d2ca5d635a056b4f108b57416d1502def186c40ea86a9f21210e81c47993b06668a1ff482b9be19b1101b6c3e10ec7ea94f6a4648468f0059832cead50004ce3575005cdc86e9e52e0934ca055e3c87dcb7f2e1e749fba91102d1fae252414f4de76042fb8bb4213441652a3cdd37acb2f92a0ea361ee8da2f9227351f03ad92a5b1b2958f95f5b114597472c0da1cb325043da101088f376deb5d32d407420cf9ad02b385833f1ce384006e3b2df0a9664ce6e485e7143f7cf3f40834b687eec385aff1b243d8d33485567e55bcc24b4b3ede9e7c30262392fb761151b7ca4e6bc19e17c302582bcdfd1573b1ff82d3289945221cedf6cb042e2501563db625000979ebf7511df7616a1e823932590587c6db394a7a5706d752f6db6e0944f738b10b322cad0ac02e50b476d0855741d9d2e12644aff780c504115bccc07a9d55d9c0d1c23b299639fad516f9fd2322ab81599c7f95ee2f691db6fc7e1738203ac56a052ecd19d7a9b647e8b8229595ec4606df711cba8907b54a863669b9c952ac84dbb2f266de83301c647902f08dfa2b0d8e777190d5f015ed98368f8e181ab407d7d16e5652243ded8dd2e23d708b6d43b7501d2409151a368197c798d3abb47d96bf4119fac1b3fdd775475cd333667c8dbd04cb207b6ecadfa69b9d631d06643444fc4e9d089844e44c5125e04d2973d5a5271c7fde453fd02cefe15f14364141c0416e08ce9a0d0a8a09ef410e3e0ebfada580d76270028cf4177ce3170a2f55ec61eae35e19e61f276524cad3bebc363903965ace871b4f1b508e33bc2cb01c75cf2754abcd0da88e17adb0bd9fea4b647ac87eb3826f6b38c8011c98f7aa70d9d94d123bb2b45f29b4b55efb7ce23f2ec47d4473af6ee7ee28e52d1c3400c623ed1c073016aff96e8307ca3753192375b2d262a65cf8bb532c4a164b7cecdef1a542556539ad645aa2a2f7bcc6f519baeb84818d13f4ab4383ee53337d47ceb27f2cb449a4166bf1e4b8c3b740068dadd13161d8326ed9f50409073fd891403af49a3087b004c98998e21ddc9122d991de070bcbb9f33042e503f3c8c8ea5f278f0f153b357a821842bf62ef329fac24922d9bd0922e842b08dbe58899092bf49f138939f04afa8ec199a1d5120bba186f54d221452414f06af903aea37fbf0a2e5b2ee323096e2407e20669ee4fee91f2ee4b9dbc551e3252c770506964afee84c74e6b845de66e3851852d63fcf49bd7c24d819a83fcf365482f8ec41c959a0d73bcbf4252f90c98c112b2eb4c0641f0021101fd120ec72969a0a63582e454b61470e919df059509b92f71c6e3290ddf26f8f7c68fc9bc33e6135e354a4e30ad2190f111c709c04d2707886fc7ef471c1dd9ef10ffcd20ed8c351f614a02dacbcff7f7431eda65f43cae22c1fab2a2e3d458053b0243a909bb56b0126e2064dfaea0a17b194d1c62292b1ac294b71670c84345f963805061687f8700a48004cd0385dcfe51409e"}, - {"000000000000000000000000000000000000000000000000000000000000000d", "001b30abcb837a1ec9e513cf6347dcb1de621eadbc162046dab8d72d25a74d148ddafc4a99a276b346380a74b0c72fd6a43fd56d163d7a6e3c2998a0f7a5152b055e1aad13e4eff8b6c5a8d4637cae54ce792f4e03173f7ccba5e6777f129548b3ff3755557b1ddacb3e09d7b549130021ab7263f13bbf90ef53285da548052023ec4bc3aebefb9e733de71bb0d1adce90fdd50849c8cb6f55eb2b6e81e29a5ead2c1d91dd15ec8e005cbac67c21affb2b91e0ea71e59e5e46ca53217a0e78db591310703f080ea726f7ed466637e0127e781f3a23cc8a67d1ebdb90131dd2ca766e76f215960175be87f113e22577ee3db7dbc748ba6a86ec1e31671f358628e291baa4ef502684d947eb92413914c8662e773b9b8f66c591c477b416b8a5c5a14b3c4b2cac2e221dbfe64f0c67a18ba97fd374393f44e61d99a14b9156c5626a8b456312e7cfd5c5463b65d37f39a5018cb353a5cc6cc5e5cd83311acfeeb5ec0c19a02e0fbeb8faa1a7ff375f42610e0fc989a557293d68061458e318f28c9ae95a6db7365f700aca89c1bf72622b21b444f558acd3244585abddebac21c53c50cf7f02bebe2be5d4c140ea03e6abef6f22bdb485182de4355d1279cb94438902df6853d65e10ae9c49dc7d2d1d15c152def826a3eb91f79d22f7439e4bb43f67424993ebbda26a8fc58144f556b4482b25d8f8bdd811027713cb8cc691f18c4462db66ef39f984f94f86db0b9a4725d0139c616eefc42895e2954d9d5c2de5f202aebf36e9ce636f274ce327b7e67e7f038b5826e1056f58af8edd4c779e6a24b1f9d2f9de15c11e9eb902c33ab3c809f5e470e63579cab87d1d7b9c4da16e301b7acbb3589f5cecc0895814ee80a2d80b1c02fc087f7fae6730ec41b57dc4aa4c71eaa53b2e735e3418183476b09b37ff8729a5c1d7402dca8d96daeedc00649f2fcbb207d1e97d224dbdbacfec970cfab2120a88a79c990579b7607712ad05a8444674a3d775b70248a6fc3262f269fbc547773d4fb9c74d24bfc54e3847f5e2df5bf0bf076389d5b0d7112f307dfe1a8e0e160d99a305ba7a7125a2fb4f78cc72640f186ced0ffd4c494b143b86b82894abe5393c8562d64dacd318a2c1464bdfa797dd4215395331a316db7e5a4e5d1ffa72b60952da1ebde6422448227d5dd744b620a701e6e974ace344d38a489579a3e09281651bd64da532da4eb018d4ee28f560f667b3d0817a728d741fa90cf1d992a08fc1c7c9b8e610194e174a862c547b201d83f70977081e3050a5c558032e6d8b88443c7f390304d3d27a0b2461fd8c11f743d5a26e03903854ff06b0364087906d6e938ce4dcb93437016aefb8f4fb04c44bcb4e977f6156d00385b93d48c9e057eff9a41cc678fb03882b897a6f321d213234e60041b0b43f0092dd7df978e039dd63e1ad6a50fc816d0e3639b6054e3cd0b8155cd4ff0b32f546188f2e9c6256341701a045180f6b0abf5e7db87ba9ff9c8a61f21865ac0f69f6341f5f5c756d5a067a03edecdf4d9f9ed483080e79d84d42a39359c100f892b17d186c5f89995b0b7894f6c161045d2798f6ebf1e744e6fc9e9cadc71471a3a9993761c9d0af8271a93c6e2a715955a3d623d03e6cb0a9e40bf337924e63161df703ffbb1b8b0284ed74a1c7e55a77ef51d584229ceecb747a929d32ca8673e514730dd1cca3572f9e6efe6341bf46fe1e2597c10c521b718d46f2b4811b7a20e9492b8741519e2f748970a1b5f40b06f395f31c5a116357a8781e40863a066ade0dfabd191064f47cbef0d07947d92360fee6c9ed41e9d401b9597e7d3c3ae0cf9dc4c352e6a7a8c92575338962989cc2f727b29f41352f0675c2ded18fe05c134cdfc8707063f10f246cbb70cb"}, - {"000000000000000000000000000000000000000000000000000000000000000b", "006b446880e0536bf7282041cfadc1f9f7c8b593670178892734551c4bc526549a90e45d7e8437f5bc871a58ba1f9ef95953d9ab929b024aa6b5e7e4b2da2061843c81c99ab4171f4d7b0893765dd787851e31530121274cc34eecd59ab56593b4e27cf9ccded9cd5705de9064c756c46949010072a0d40b45f45099b024244e4da3bc6c718b98a373abe258658a83331693ecaf2a66ede9704995c7c17cfba56969c3b89b5f5ed3022ce963a868204f7751117663732a19b5c3db43b740e627433a59d8cd684dea348f60701ffe88bffbd907a9f6626c4ab604663e47cce56eaab672937b77aa270bab062fdcc87b4cf413ae5ba40f430e787897110f6b7fd9c976801fd29252bb0670fb655e16b8f57c1124ed7636c98a0f11d1d3d813a8e835dbb5de9b201f3f870317c7eda77a1c2679a64d746dcbe73dfcc0305ce27cd51876ebc01ca51425336ba1b01c2df25a0160901ff01db14f47dab6890ec16072cba8fac369078c13a1ef4385b14dcfd231f6f817d7a9d6dee1311b755b77be9e1d7da78454518e6f9352a2d07a9713561a1ad95a3a1febdd4786c27a4422f628ce938b9a05a2e9226fa75ef16ea07272eb950ce16434781a3a0684dcf784e8e8d5c476287b6ec7c1cecb07b9e9e106f2360fd553ed03c5544494d06a73aaf4e39fb24d2d14924b2b8d8dc8cd8ea6eec6c69b5f00a27caf840d380df3a4633bcbb98bf1c333c7c71534d8130e2211f11ac0c65dd81f55349279dc42cf6a2d673b302e0e38ae9483cc0441ec6231a35e17a0346ca95f3cea55fafc801b23a28bf2923713f15eaee677811cf56227ff924d4bd490dfbef8a85e7a70e35e44375e07c82dfb3fd9517828ebed9775839bfa64c68a269c85433908920f97966442e0ca97371cd969f62a907f788656f73f39bae574e764b777fc546d809a29a4f3831001366f9daa10fc73f714c105ef3b19fd062e9650071c557764ead388a52e65c53dc0c23bf56f883d70b507dd72039527f73ff2f85541ad445136228b5858ad14df4dde44d481a8d6537240a566ce10ff21f6797b0b211d5876c84a369d07425a81f06d2a524d1930c564d9b4dedbe2903f7993278df7fe0d1a53a8ddc6fd16aef3863519dacb822db6b50b652d15b80a37d83d67b0b669285a30b7ddb6a81be1c8defacd56fc916e0619e155430c47111932a06fdef6bf60217b5a30211b1bd3824fc7eac58e57c1de4b5cfa372a275f2eac0d4dbc81f0c797dfc59b02d43e56ff6a64209a5de423d86ab7d26a84ef6cae73cdaf605bf2a4ed78017c0bab9663f92ac3dbbdbe52c74eda756d2510dc6e6b309321d901a7a303e544c63549cdc99a8c977ae85a268e855ef7392fcbeb00067bc360aa26d46558171f3dd653399ca8c5a164ea6735dce1609e564e1b70b2042b7decf73801a9c3ec4289d47ad1f54cd131864e4001f4957d515b0f0d2bb9dac9dfc02b284d3f2cb904d19bb12f433eec96a661b2c036d5fb90607ef43929008a4e2c69c8bfc6a1451d0bec4c2eab3e7937bc08fe69a585a4a26ffce823b949ee48c5131434141d1923d4fee324e195b77394281f2a82d144736a9aee1265b74e5dca8f2bed0c84d32f4acac9463ded6857785187162fa7a24ff071e7950a67874a0f71f1b117053f8d55794e5f69810530e6b09f1370a97a776fff0a3eb48b215c434d04e432d7a2583656dd15b7fb3015ea72c743da0bc2d35582b0182cba8d0d1b38ff4a1652fbed589719a0fae072cb9dcfb775985e746e7212cbdf6f59d73cd55d8505960ebbed79b82a31f375338e47e59f16386aca4297ed2864e71664385579241867ccdfad8b6d12c03564ff105b2f69e76d75aa73240811be6f0bf0348e3152e63c402ce70b9f9907f0"}, - {"0000000000000000000000000000000000000000000000000000000000000002", "00207482d20c9371ac8ee33f6f642eddeeb13a74e300ea88538e0372273983b4bdf5541d1143e8705bdb0172a69593d236b3d492712ce2cbc76a1a5f3b677e47177589a915684d0b3ce731c9c135d2a67e3ccf680177fedda7abedef9c20a6473556da9f3e02bae08d1034cab60135c83fbc42b38e8b1e840662179519e01d803c6706a80829734212cdd93b05c6ef5d9a44b11f770ad4a547f2e4554ea2049fd615ad9b44f8b87100b0e226c5d466eedd306047e51443ede9c415a9bd0a8b750dfc7c76adfd95f25fdef595fa26b814ed830d56fc51119cbdf5f1a586d024424b4e13bd151d9716e5fe431e6429473ac06428047c1af6eab4178059012913276775752bc70b73fbd0e6015dada77d90bc0827f2a22487a2bd8baac29f599b7fddc7ecf9ae9f2684cb7f52cba2bbb0073604ac58ee9e11111a76c3334adc491e10999b7bf9c7fe79cddcf6959f3e1a73012c79f67ca827b1a6ac40cd143d1bd86d9c24d9851b79f3fd83cc9e8393f4b22fbd56972c8ea8b26a151203a68ec86101437be57427b6b2cce152809bd4e21a1fef9e6a0f8d3499858a3ce6f1850bc1543f65b4022ef6caead06f0bc8d5b2f70c99f4f50ba25a7af20a68d20edfcf42df43bd92a956f9f89928b8576f4130172db37c56c1c7a7a735f895f2a0f22fa69b4fd635db7c370816bb97b94ef5b75ed8d68669179e0b1d021281f834782053fd6898a4ad69813a2a3134de45309545ef9ecf6580f154d38e807b48263583bda821097f2cc03d62f3b9985bf3aad6e1653a2efe1ef4a81cb9d257eb104c251324762f28d72b422cf0d6320a0615bf644201d54e142148be07cbcbf22fea3711ea20557d8a4149a909f484e2821995f24f256e99bb2409514f15ddd6dd56c74082d509988fc550f53b508009e226fe5bceb005d76d8362c9bf6208db486d4bfa0212559dd04261a9b2b6e8e24178f6665e9f13949002a120dc37e6e76f96841180235a281cd454da36140894db242c0a2cdc76b812abb0c1a0f662699d55690ffc0fa7d053beb0d4ceda0d11691cded3aeb91c5e0c353966468c7c67466c82908d3a1872fcc67d4eea23458f421f53c4896652866e016f3f079b405f74921e44e8faa9a22bf736a5359ac5cc7346cc295ff07e20889d2c26efe4cdda1d82afb117b1ae215bd564b3072d34a05286ee6b9cf711c2d82fe35da15a8d9b4d379756ece560328176eb1671926524ee773159544a1339a7db5f5bf619a9e9e6e599f6e61f2ee09c8b38220b2d06e1c9b89f4e3a4b4730f72faba44c3de68c233d265ca6498a8fd754f379f6ed918abefa373ecf27cc4b0e71645227d92218468d64c6a70c59f8b91b36fbfc74d7f43b2ffd9a564551b648124d013f9bfc3f07024323244e396214f48096c203aa368957e70505e1bca107c5765ceed495d96cbb77f39da1de565f1c177fa3de0f3c2483a6d22da66f1da6ce81fdb0d00e6f25e9395f599d82ed91cae2465d3699d41adf0a16f20ce21aae2275a2992871b0f5cc2ea582558a32108d19b24dcc92e55c1e8405a72c71f99bc4f01dcd194a9980b1e3f015aa4ec3fef94b95c27210d49bab1aab8eff1f59f6fd96db59068cdf464e7d8a3857071f4a25ef889a61dd85d5526bb1d2ce56979255084c1188b51eaf1b8d950330413a76298684589610d02646b1d4c840f8ccbbe4c928b152ca67f677689bcbcb22a547294fe24bd9b006a4c2de28ede976128d878a67e64e7df22de64bb880b8917dfb3b9b7ccc1e664330565db410a0154f5ef82b31c473b0ff784d9bfa305f1c8cddc69f4da752266708acd230f2ef7158e19a3bfcc647edecf4d7c85c08b767db5e23543ffc04da43c2f397fa9c973536b2c5ad78479c097c517698d4"}, - {"000000000000000000000000000000000000000000000000000000000000001f", "006aa79a0aa0efc9da0f9172f8430d93917c9dc6e6176b5af2868871915b0d435878e5183de2791ff451072acb90e4052950aa82034b324823d60deb98e5010b741e46fcdbc6651700911dfaa191cd7a08b377710a1d5584aa67b7cf623dc570d6eb10ff483e9a5cdd1bb11c9fa9b4cf77dda3b349a5dc1125cb2230b23523d3caf9f58aa5a57d964549835bcc736b81bc02cd26e4b27c130e3434d2af0432ea23ea597c633145dc035a5b45325521c0d3df51a4cf67b7064bd9ddd0bc071da705ac20c65907cba62f8be286069a52fce29b0e6811e3568dc9852416e2d1a7b1b309c6759971d529eac7a373df38dbcc67c73767ee07bdcf11f62d35096bf6ec0ea741c9ca3bb875bd5f09423a66fc29314146ef2a5095ee0ee74054d9ce2964962878bb115e0faaf5923c06322f85e7a86df743d0827d481632cb5473b461b6ae2803e0e938e9d1efb596eff4186e94052d2eda984630ec5f6c243862b078ef8ba71d57850ad3561735c507b1bf3ff3b567c213f9631375f98c0de462e35158d775c01ff1844dd0c49e31b713aee155f96d1844b90ea5d1bc569cf263c822e08e3903470a5fcc948a8712c7382815cd3b707d7ddc72b103b01ef9c2d06cfbde11df1ef3c875d04d756ea6d275f7210a2cd8212619cba17f88a0836cdae6c5f876e7e82b37bf0fd30d1b00f24f1431fceb35c2601b9fa995075cda962a0f2dfbfed4d5af8bc2d62a97009bb98b111e898af691c9f09e1f425dcfd85d221dde5460ef395c67cfb815c993f2f184a9f3f8f809cb88f64f19489d0bad2e5b74e95b5885209d4123ca667fb3959d0c99bbe41615c8eb141dc3dd2d646131c0dc523fcb16b42b76081a0d5fa3c59567a859e422ef38fe9a102477ac899f0e04ab02c3a41569b9afb50a885ae81d26586b0399a10dd9534043c59a2d4c629b0336e69f009e9d2d5cdc72f3344ae048f32927f35267de420808822c891c26771fe941d72c4cc5406f81d2dccd4f05af7ef6494db39d150de49708bbece206bddc3a562b3b718f071818cbb9d6a5af23fb035a3bc252e0f006a14d243858eba79d6986ed7a51799389fbbc5f3a35c6223c13e83acdee58fb9434f0902b90775d748c08b149c58606fd148995126675dfaf258369f34f90145391076650fbb16c5933456d23f25172b96e0cec0aadf8705030c2df878c832438de51f8cdf64c91480d1316e7b4db5785d2c5281725cebf4e1386b1da111862f4d8959a157eeb0c04b6ed2958fa1b25ddc5e727d4fb6a629cc8cde6fea47abe6733a3268739aee518b77b08375eae810ff7d4c24c3fc59a50fd58a99569aeaba38ea036d31e417ad6c765019726ec9bd042229d55681b4df6d88c4a532e5fa36955fe07bdd21024aa4b8e9faafda19f32527bfb2753adccd8383c750351a581f0d14edb56d6333082c975d2abcc79f4e020aa271850d8aa94e7fc15a9c1f3b0972474fe7d2304d057c43102bd1aa139a252a4259ca53cc40a34763fe9fca1d866bc077a29189a22c6447b0a529994c2065bf9ad7106c2afd50af6815770c6ce016295cb0116361b63c5cb6314768cd1cb77cfaa35e0049617ca0a667b46ab6ffd979cbbb882fac4cf0ebb5d1d21fe1106d89936992d1587ddc55cde366f1da92b6daa751002f9de3a1c07bd2bf302db4d60dca5f09addcd481583f0b3bd9dc4a1d4e94369004f232688357de1d52282f6054fddfa25a415a24ee7d12fc4c1e0c6c7af4221ab7193577436c784458897e5ca22d3057c5f761c67a35cbfd22ef5e20c427cbeddfbc6bfe93d3c923d50bf5544dddfcbc19e33db672f6c0360d71f721f2126fa903e25217b5d0f12dc58ea3f560b279412b4313cdb72e94cea716b1ee5a954d289fdd9d2b658f1"}, - {"000000000000000000000000000000000000000000000000000000000000000f", "008f85222f0a8177517b716351943994c6f586e0fa0fc66377010f7806ea2807bcb4f5c1764c833c12dc0d688b9a0b3beedde858e6317966e91692e0fcf971377d2414749472136ceea64fb93bcfbb689d5e96a500c146659345f881173581e131adb3a8d4e1945b84032b994b9885d71e642883a3bca6a0a5e47b5fac7408509ecf6a7710fdcd4a24788453df8a2e49f40b46369cbab31c22cb9d8223c53c9a731a9a8cafdb3e5200e86ef703345e5fd3cb52476072573286a8fd282d07fe4929024bcfb58952112558c55f5850092f78161aa6aa834954295904f333010fdf28412c099a84a14a3c3ad97ba16a8d43ceb573177af8e77c007c7e42016d35d757430afd68b0c6bf3cfde3d23f2032d37933cca5c22722f0176d6d79a30bd5e66ab4767d77690c3a87ea2f453fef8059e44c46c47bb2ce12168f9014368d35e057d2dad49dc4512a7b66169ad3bfa84c0266ae5d38cbf7b0af2631a22c1a1d148faf093f580a782e8d63aa5069e2f3131b8729ce967d329b8e2123392567201233b2a78702db646d0e46366c762fa72955fd57e1d3206f0119d5d97545cdc289b2962003028bcaca94c5988a795c932f8265d6222ad9d6f82d16b81789679b875b378822bf2d60f8e18c8df2c9b91a92391b8759ac8b204075fd7eb3de2603b9bf13ec212d850a5c91324fbfe8f2a519a0bf3a77eb7c8e3903d5b1d04942a2368752a2a0b8b54eb726de3fe4f82f92a2e564b05dedf25713bf202eb1213ebed681c8150b5a7a084fa1c4f2fec277d4d1fd3163873fa39e33a7cbb7ac1c5411ad068a18dd5fe612b2ab5ff16a0cd7afc8c00715e1686e80e5d314b57612823bf5242a67fba3e4d7bf53b548332f1747e1eac997fecea729e5076e79d1057897c4b4ed1c778ee352917a9f352d1b8e316f7574b7c3d922d281cc35313e44d1f69d0396dbab9c92eb411f880313b1d85fb59cb7ffb0e536252d76a7dc7de1dc8935c7185eb09b0b48b97ac22e4b2e95ec165474c936b5c189fdab4ae45ff9057b35c2f7077163af47697716c5165d4499dfa998ee46054ef0c5fb175a69e50873b451454a371b54ffa6841001ab4c466b8cb38869f20cf1370e9982c49fb7f6221d65b1f6e42581a3cf7295b418eb40aecf77238031c287f2a20d38b9eacbd4e7cc62b5c5a10fb9e6ca0c072964841c5b8357e2b508802f808e1f76bfccd621b55b62fb4a0ffaa9dc46d4a24dd035f0f0b52b550d4c9a09eb0d1a7c869e05a921d4b6638d76bfdb5d2b707ae2ac0c56abbf8a94ac9ebd091b0ccc7f292114e32f3f8ed45d51a79b89558bd9d3fb45035c3b7f5c1ced97f262e5219499b9e3f7f8436af7df39a4ed1da362249e07a93a451e230dea9e89b57666b35f5d243d9e230a0e730d446112c6b4a1dced3d627e3e8e072047edb0671c7da30f325e69f97b19e7a9bb819622c83d4d0be39c5137a8a387b3b29bca9ab17e8721107628b29eae163f930a42eee1ad655a936618aded1633a5fed853d957a2c2545360f0994b1210b8d5a10c99527eff96dc3d521b97f636478fbb61d8bb69c35aa3e670662f1b9bbe7da6f7b6bcfe7e21509bb5ea1d4ffc3ca0c7a21e4adb68d50dc9a7d715825f545a1ddd34e2752ff791fcd0c6971c5d51d23919b684be08276f13f00ffa873790d249897b021651155f07fc37353383e1e5f3a9b15a953ee6be307aa344fe190e12991f0037a16605abfba57dda676ed2399672b51580458cfa6cad8dfddaeed8dcc2ede672d82e9cc7181ac5361508a1e5b9c872e2dd467fca391e39f6f0a01f1721e99d9e556d896b635d8632d760e2f078392f377b62c4be5f7819d45db501b37bb49d89dfba39d83f824c5b2720b9a71422545492675a19cbdffdb89b"}, - {"0000000000000000000000000000000000000000000000000000000000000000", "00c6b1a88a89208c577f80d3306e8d14e40bbfe0b20e334b2a3018ce556c16d3247d99b22164e719dfcf070895990645c6a5ca60d146b5c7f52c80e02b8cec31eac4967898b52f1076c4f5c3ba5dc189a313c53a02f08ad575864c9e3323b0c6c2b4b8a8b1799d965519887d267fc78c43fe6c17e54fcffcafe1fb5f1305036acf8f85697681890b305afa233b4d90139019b529e91e12810ae6675cab65f400c7367f531f5b25f00528edf2b5d508791f84c2838f6580acb90fa6f63e07579f138749749d51b5f3497d52a771d1017a3d470e303b96260619195d4413fa77a3d0f9413fb916e247435307c227a70db6e9d4cdef47a67ed295bd2f1209b50057b1cebcaecdb2c19eaad27888a5526bf2760d86dd696a053d61fbfc913acc48f754fd091a9f663fc1da98e1e82fada08214e1fda8af7f44a0ff82fd692fd5ae7bf0b061fbba5765074a6e369839989c08028a5a229214f533a3a3a0f4b63031f998ced73ad81456f1109f866caa7fcbf152ab6b5aaebb06de492e041f23c973b5ddf1cc6bd64726e1c9b9e76b395b38306504a3da98b569dbe533ea80bd392950179d2e9d10365a83e18624d907190689675cf07e5079d369621a701609ecdb4305acd3c45eef71f18f09a2d9746721a469343f51a701830706ba1855ec6f45f43bd8de43c9eaa6eed9b239fd7ae546e7d08b8b3ba57ca4ee0341c69b884aad2094b803aa74ee635148e02a97c604fccbdecb1a800754aa4401bf4bb5b65b9ffb97db0c986b4f2be88cb1c7c59116b8dfd6264bd23d61ca2129ab14c09c31d538b4f2cdd1e810daf5e0de4eb305ee7dd8fbd23ae595f549469d6bb96ebebcbffa791d4bee182e8db69fc8a3d37fd75f3eb9f07590828e15a55eae1bccb77d063184342a527d528e119d2d9f328242306554c4ddd150254ea6719efe0376d5ee6503b44a8beecacdf195f936d753d7bc761a26d5e1166b8543d0d03ced97f51ee72541da1707521ffb50040cb7c5aacdcfd392a9a5366c41508f624c7319028917c5c7130290f9cdf3d69357b55737990a1fef91be0b688cd5e068588f90bab0d46f1c6e38364daffc982210d7314e08aa1eb275c28f325fec54a41d0a0134123bb5cc4f978fe2e96302c962a10cf11a04527e0c314417e47a9e94a1a8d1854f503199dafa065a21e4042e5d8d4b880f5bbdf1f33cd4a310cda0c7be87bd0b6826e7690dd9b7c9521237eda6b369120a4ab6810911466f4be3fb7fe0f3b36236cbaf3dd0a22eff6509422a141c16276d83ee25ab6c2d9cbb1e059c83af0719c8570edc11cda5a140a82f2834d30f599bb28b4500bc143d5ff7a3bfaf06ba836138a60a20d8221f0cda4c20776ee32be733d3d52c5edc4fb8d3bfe67b1bb1c3ed95a23261311dc42d62e4cf412844fa78820438c53bc6dc107bd770265b9dfeabee9b13d686b30c1f91933a915e571e3232eaa0c40189dd66bc9c4f048868c3df1964f3cc82f04db641f2f1997ed4a1060f07bc05e5d60a378be1f39dff9fc5a2b57df7769d04c3e329ef555b930630e37c96e8e905c170f154f12c6586c8ef6c0c4fb2086402f867975644a155e88334eb15ab12ff0057f90224eac861f6da3f2dd7bc7648a57f835b9d82d57cf4455a39f835da364a5bc47d091cb6089863248d8b8e60f9c1fcffe9ca85d03e88161e5c6eaee19a5d630f04c742f049fa6224d5def3169ac50ced191c3725c9c35c25677a264fa155755227fc5a2e3fa11e05a8b085a88732ad59e85e967df7123ba163072a0f4d5eae3462dcbfce7deb3b5828ae1b5f231db1c9bf0a803f5998b9ebca32c2f87c9438352753f806764387c289548b11b6b795f3165db49a40d8cccc7c17669f7100b57bd4ff1bfdec3cba4267"}, - {"000000000000000000000000000000000000000000000000000000000000000a", "008666ae56c04ce79db3d3085e185bcda4dc9bc65817af22485ca3647942effa0dd6f3f7ab69501d0aff137a9ca30d716925e2d03410bbf417691d63f5d2c718306d53d3da53eb42ae9c08446613df04b6be66a4094ad52a16628d8dafecf5e8793f89aa1a091bb46254ea16f77da42bd36a4e573d33e3113dd623dca20412984bc34a0e8401b85223a46976e7c5fb911c5d95250c9216425befd5eea0628eb55354dced0014b2b20e6ae74092cf4c1cdb8ef82170cc27ea0c8078c62431013c1846a630a9cc28f45f1eeb972a7d9ddb8df2145d653388a1d2b367814201b4df3db1c003b92efe2b6d1bd1dc9da6f5b49e27a3ab57e43e8df47f46d41086ffc812190c7354fc0db20f7c637bef20ffe91c4323d3a9f1d26851a43d16c554fe0adb5a205c7e4212564a87205512caca2485ac38ce937273c0b70c771453c69b3a8d0710747de3e802d6852278b4f5cce9014a123fe2dee09def03a0e54de67dedce177b102806360cf913591644f1e8863629bb3db5e226dfa9a404604a533f05d4ab0788861652b48a4ea4707697941fcd5de7aa3015a7b83aa2fbfa6d14f23d6759d25a077f3d84ab5331d994e734b2dca89fc650eb35f1a45f250db950eac1c97a2766c6edd6296e56649c76992354d3d98bd9bf8aeaae9296e9f23141ad607d80c9426352a5cd13af6f35b194db2432a97a849b3c67130495f932018d16d98cc1c9c5cc57400a80cd143da90f60dbe3abe0d0f9416462e7e3d44f7578cedeb1931a4a4dce6f1ae167f635c950c95ea68baf8f7dad9d3aa995f9e070d5f9958994debe4d274aa2ecb91d8404d643792a46ad4935a6a3cbe8fe2f4eed8cb9d65d075646d5fbac271388981585d17b97cd8bf359b092207917c02ae89551c46d439aa9aab85601da73074c43df0c3886ef20d99ca6c6385172908a3da7b8784b00d2739a1086125fe3ca32f59a6a44f1dcd61d78db2929652174ecd2ff675fa2c4009c4aad49396dadec02bc08850b41f97e344e90461bdfe3fdbd84da7ea81af437e5d589e7a1506555d58063abaa1087ff7d210799a36b4d084172b606e39e001efcad9bea1b0f8f4b66a7fbe7e7d34168b5f82004c436826b3b3f41d724d7c6079b89bb939c4942fff0dbe16e48831f30512eeae201f9548ac30ea2e6c66c75ee46ea5b3f3b791a9ea1d5c2bb2871ee6782f580d79cf548f88e099927c8038d7dd22c609498f37c0cac074956ad94b11923e871809a5197a90c4c638390bb5b3d7855b39e4e3fbf4d96e9a6632f388cd54cb9f53ffb61e4feb7231f5cf578a6d1601feb8135685dfdfd9233d2bdc62220bc0263941cc6d7b19276123445e5b2e69679707f3d26efbbad6e69b1d78779115a493c26b373def3e467fc86922bbaa167d9c436d6aa7ad2268bdcd921ed0403cc99f9d7fb33a89bd0f076debecc7411930937361fe7c4ee9c90697770e974cadfc5f73ba01e42c20cdcf312baa08cebb40ef1046bc174a9421737f5f141e4525302ba94a5fde2d6f83a628786c8e19a20e5046541bbc7ad2bcf9635327878fd440dc62b3d4deb305f3a5175d99e2f737fe89da4fd39e2b41afe4a781d06010f3c1228439e05f41fed6173c5b7113be8d4221f63c17ba2d0c1e6fb36cf73f3639a1f387b3d8c07d87bb2999d57372389174f7d6c59b6e843dcb9721b4553cc6809f9fb4c5324e8b435e31b0d6afae1c81c5e8190902107c582d0f38e243647e5612c19f44e32a9fb187ad6c12958634362e5573519ceab39115007d9c9a19230d36dfcbd4238c6245fe1fee15fc1001b675106679ab361cb7691fbb81cd4e8b663b2f0a011d735163746d36b9e6e4427a0bd36a10a4e5aaebb24d901ca48e80fb17e01fa77a6fd606bc721ffc215"}, - {"0000000000000000000000000000000000000000000000000000000000000001", "00f48ca73716345f177ba8517fcbbfa282b6b88a3a0969e90515185833cb7cb403bd49c301534e50cf5c01da94d1038cadf570b534b3e1bc588391a95f2f701132ffc6629dbc7d608c1133ee4162c1ba825c572507e6f14eea22905d9bd0d5317a3965f362a4de3e6b09bdd25d1783b4146f3e921d0c6127a9a524cf3eb1112a7de0940f4738cfdee2acaf7ffd1f450e7eddbd3c0f2a6e72ab459b5a91a571f0d23e258d9a4fbea004aac2f501c62178a94dd0b0c5687974ed66a84c2f0a0d558036c55c3c4428115627dabd84c60308986f088d38d6d746cb174c92544a447cf3699931791c340cb3c52c60ddb00b3389614ef8f5bb52bf9f594997082bccb64c1314cd971ef47f67e079896149b88d3d0ba715ec1969f9e3f2454161a410f974dafe750c1f14518a74bfe0219f5ad3d17d05c3f9bc797a39506b1cbcf6d5908a216bc10228e6a1e4b8db04f9fe147d0248cebb4349da05ad55c1216e6952c6a688dec32317a368ed212fd4cfe1b2630587987499a301d947920375acb86acbaaa59ff57510d957efad57b56ba6bb19438a50c7a74edd733d32ed9daeaa4b0a5b387e261631e24912e9a551c25516f098f7336f52abbc864421fc02a1eba05a83b1397b4cd0e205f2dd22788b371e23a48b9b331077ea6684b713c209499dc13ba7d445a142604f2a6689ac1a5547dcb9696d99b1930743062a5b74c0683625b6ab731478ab13d976d91a5bbf14b3339c8e533996d3436287fead52922ec33ddd8a0ec09568d916019be61ec17c044cf9e99eabd4ad5f222776a1ab1cbc07f9a118baecf0be236aedddf8820677bd19e9cee206ceb6b6592069ca49abafeef5e910726af92f6dbed3834dc137989dec61890c0ff11e15f674a4e2d2d019a3d81284749a72320d7c5da47b1d1e5d9b2c4f355b91f2b696fdc5bb9de1f23daf290108ee042ba83a5b7ff852de764a5ea1b497d9478d094cd5d54d540962a832f606e8d3258f99b6de241b03a43b6ca21bf68fca3e50b88a2748509fca66369510a099da7372f547fc9be69ed9c6968b7556dd08fd07d55ddd99a1d9333fb39239d7eb47ba316d94911e2c9b6d56c3e025374380f73a6ef63f4379ef3f24880a2c47a875947e12dc77a77c02dd26ce732c56928a18ca09c0f8cb3be759e664831b26917759b87c1ec303f41868453d3db1ef41d4150e6e031e32729c69424518cda366e5f003301d35c8aa7d3e41b76d3216270462dd7f10c39508b851804b27fccbf062e948cb043231f7ccb291be2339d716033658d66e145fd49481070293b70747e5c84128f073f604f83da06a7d0fc52902979cfbf00895a74549c43ffadb06f259d7f0c6156e4c940dd519e90a3856149d5b88e9b87a379a304da0874e335594f35ef5392de5e43b3f0972b8b35303b7c553360f44a28b85987c9df75382a291d783380c5f4ef9af5cd2cdefdeb61e72c2c6ceb4ab979afc03edc43b496047a97af512cbe6f7f0de0475fce6310831e61bea84be090ea651feafc962599a7d2fc60e068cce31bc3090abbd4eb5cf65ca278a660d36f1342683a959d24a03fedfc8b86948f3d1be216654cbbb06edd174f242ea92b3cda34cbfa083990443cb36045d567607df5fc0fd9ec356d45eebf00b6b8f5ea7410677c7dcad4a2189d3f92256999e9cc6551ed446dd3a52e30d925bc68fd435e4f69f3be39f279ffb54c70a641bd85666a48f591c20c296dce721ed581244e715467ac131ee5c977668798703756b16d07cdba9c00aad64bd55a06fed9b28b0fad7924c352ac2d582f311797d80254aabdce991316028e3158641f7d76d69149f3a1cbad0f24af28aba27daed7027d20b9fc64c2ea0e52b2be2054b146c860302be39b58f227c923c"}, - {"0000000000000000000000000000000000000000000000000000000000000010", "00333f1e90c31543d056c279b4fb5a78c7511484ad30d4643ca2cc58b527a0d376074b2703450c7d64da01ff2f247567a9dd5230e3d352e9a0bdbe453b55270b8518f1928ea1dce3f30105e5653aab145e9f619b08361e4373847d9bf8d3315ce6eac5c0da06dd7e4e12bfea98c7a20fd969e3e14707d0be9a6359ff8a060862cfb60263acb5aa6a5505f2413fa679095f0c331623aa2d4bd72342d8b082d0742cfde563230cf10f17264e9eb95faacf584ff5998c641dbd78011cb01319852fb656552a58b3f674c18a414e5560b2b2d4fc1751eb5613cfe89ad37893758deb75b72d771d177a240cff727129b75d4e69361b46e2d261ff751ee13020c98adea7372543d1616721d7c8b3aeea12f89a364831d31d9fed9881a85e452c094c5e6d73845194df4dc5e6909355a2f7ac6737646ac4fd13f2aebfac89823b261156bdd1ebf05b185abfcc817ac1753fb80f005725ae2f8d077cdf15b0cf50d8921b12d1391b5c36c593c9cd15f6cd27a495a472e3bdf5bf9a3dfb1101ea7f28fa16aea0d8bd5645a45f19ba1048dc3c8e112f41b8a8c55abcedd8c1916e6d5c137869fe9e16097c11f8d86346cfdca584743f38deae0c33bc736f137537bff74fed4548d461dd3592e78cabd1069cd52ec93a649a9f7e49f260553496c165be459bb856133dd1bdb4f22bdccfc534255a9975967afa2e998e1b029c48afa8c25308d19bc1b3bb42435a36fcdbe6031c9f94294c75fb4fc05033332b6df895935e1607b62816bfeb991a7875ec9fe471f06d756190b45a713b2896abb67c6d3df9d9154546436ef10989ddec98162cbb04e72cf897a5d96ba4c0b36bf25e26c737096e36d945f55ff1c0ffe1c2f50d2d31718a89b7b8c8683db0dbd17bde93edad78b503ade82112a2eadeb3fa49cad7a7282691199f7d171e3e39bb29f4b0716367027aa9402ba59037cf6e604be62048c87a112401c70d885b7a5096d98d9a38c25dff48f6e2674d9f2af40a155a631d59bd593ebdf83a9efa2c229ac976a3ec25f6227968cc076f3377c606c45ffe42c206fb85ac0370c5791190ed98a68051ca1d1fec994313dfe3b2221fabfbfe5d2a1f44201b59fd7a909b84fb1ddceb0c0361fad596d9a39aaab6eb89f1a809ed725861580f1ed410d4c7233d4137d185207edab17ffab92859050850921383a7ddabedf1b897fd03ce9f6d17b3d5255047f89626db49b0dfe9e4757f0d66fea69c564c0b00d440fb179c29a7f867ed29e80262c9c4bb0fb22ae2a7fb3e5c437fe943935cdc33b9c9011069d9ea0a4553d44cc438e1a1f363c4f2df30623af41ecebf1779f99bfcd79fabfb64d46706bdce5e1c5f96aa30114bec90575ab1dde53252c4346f82ca3d497b88b715504766ba0ec36db9a934bdae69cbce57cad55ece052c9d3301c4a8643b9f3067bccba300bc59c686bd15048426e1d173b0ef7bf5da65670139ce12391aac05fd22d39a1f255b122af0a64bbbd5ebc23afe755d06cabc7fbb1a09cb590342c85af404926dc119ed451089bd44b8dfad2fd992b15206a2bbced7bd7cf5571862371cb8e663b9f10b431419c189f279fafdddae25b855304a8cb3ce7a3625b16ece469a1cc539188338c8925ac48e9658ff864915dbf70d4ee8a578f2a615fca6fb9f07885162720457e65ab97d16f5362ca448ad970e80b7f7edfeacc79b06c7bd6650adfdaf1f1ff49b6c484eea2be071343cebc8ce027e79fa9c2b3581e4f5b657c77cf494792fd1fa8fee6fecbdc99b2f225d8e4f0c32b526482485686531259d3093a42337d0afe9dc9786f18ff703a413a8a70359313cb27e3323147a6e0d6dbd3a561867e0c93ae2468eb48409517257f17218ef8d4439f9731d58f7866a63b7a688"}, - {"0000000000000000000000000000000000000000000000000000000000000015", "010570a8b3c77a7f88fd653f90aec919f098343fd55cd48f0f56fc814be970db12d768d507c0d39ffe6e021e483e248543f6ba9d434e61b9bd2164e24e8aa308b7d88235429dadcb2c518769d92e31be2a58bde201a51dfd34842222661a25630f5e1a0990a0b40c6901d3ba8b6f112c2e984904254adc91c9c7727f87d005cdff01b5cd68c1f76a8292caae8164eccf31fe4a1c6414dcae730e01a416d92b69ec98d26462948e980279a7352990f8bfcdaf1776926cea8acdfa58a2331b2c43258037151dd6a392b912a9416b69667ebc093610856b74175c1581d58928665e908a950114ef7c5cfb6cca92a2ba957a68e5eecfdfe2e33a3cdf240815c70ac4506eafbfe5ac325f89380127afa65ed4dd2b7f4612aba61e5b508767ff43ff9b9349b95aca521d6d57df59755973fd2033ad8fd6f5b5bf7a548e1669139e50f223248de4eb08492fca6eb6b9be9b19360108d5d0624da616b189e530567d99b1704095701e3da7aa036b15ba1350598511323408316bb5784c19031e2d81e572aeabc3d580cf9aee56a8f87607e8e2056e9ef2739ae98be514b4c82bcdd5a65093b3de7402c4f23b1f2245d9d07b05db5d3fa8f9cb6e8f263025b6d179e0d6140fdcc564389836e65e060dd85fc109b9450730d65dcebe5c446dc77c3f11e437bbb382246d629741d7de03cf5865bae16fa801ce7ff796a60d645edd72882c00ac349a7f9ae4bf9b6f6b5c26dd492673690754ca37256ff49bab3b42423f87fe3dfb1021bfc433b7b535f03136f91057a01616c77aad073bedea0b11646dd389c08a933d76072762d1ff641d0e69c55f8a4a68f692c013cecdae063b84269e58ca309de9da6acd538950f7d32647f4b0757247f06c7d1b58b9b0d1ce868194631568fbdb7006449bfa511b2198b6269f32b69bb38a463c3e446f0724b01c7c380186e1ab33d195bbe6b861442bc580f228971d03d124cdb24fc8302275a3b0f2632eeb28c5dc98b6fe7d01d51e3944e94ebdd8c36bbe2bde0313813ffc5a6b318915d169d1a5549f39b327535852690dbcf5cd2c0e4bb9068686ae89d814112620b327e0bd3f8e4b0a0f2c7249951ece69be201613586a726745acdf219e22463d7992984710c7d618924bcf9e5ab894dd00ad28067406c9cb91df33e91374b0ab5121f9a01789790e667ce09c0439f3377c64f6a26ec425e37356a6bd24f64db91a2a76b580448576b571b09b2a26bc0d14165111972315c7d152a083f7737286891e38b9aa7b220fe7a65112501a9f3ee29e4622c48bbcc3bf5064101838c9df5cabc8f8da918547f384e58cf7d2288e1415d2e2949377b9f37fa546f3d2045b1e0e3ff4f9129f66be5989860e6cc1220690e5aa62a94f58fdec370ca4ef60d0ec211a3e25abacba54523ec19fe6d301a971b5171491dd0254d7ec2ec138e644aefbf46d133b79ab0426924d9cfc79b56d5e77f7cc28ff47c71533040805ca643a5ab08424cd2d501141b577139b2a8b2c615c559ea6fa847a9ac8778582bba81e30ef0381a3977ca671d7a28920a6d3b821ac5b290a54420c7c334f9752104b33a5835315754689e2a851ba2b067b1bae1de9588ffdfa034da646ed8636e69678020a71b710ceb4f3c5ee86d189d8388674cd3f0b08560f7fca8a505ccf41e5c6d444dccbd515bb2b5d5c5b16a2eee3c9c6acfd7e66f2ad1f51d650ef60d311cc2013050ed892ff11f543b6596070b2d1a0fcd2d491342807355754d15128e8456f0cc5ccc96da0ef7f130fdc623aa393ca4fe4b086925f463856cfcb1e91d2138d5e945309c85686b4a477a77e52226287faf7311f5f4971e2081ade6eca7337092882e13acfaabab55b383ae335662005ee6ab5e95165fe5265fdd89322"}, - {"000000000000000000000000000000000000000000000000000000000000000e", "007087bcb52115f3833f40ddc74d5eb8692f4e3eb84bb5a7c4cb99b98efcbc8643bbf7035f461ebe42fa0b48dae3c59507ef0ca8f457402b74c65dd554b3e11b2bc0f44f8d7f70cddf490e706b422266c99a099808da81c3154f8216888f52a3a972424277f5bfd36d28c5898c6558fdd0f5e2f652ea7e7135f47b5a8b851889b1139ee31371661f86e5ca471da1e5d950c24a502af4bb2960ba251715489d44cfe9ce5d8176733001c06f3cd44fe544e6a64618e5fff79a3a983d317a1f6aba8b41d40bb50597c2acb4c197a651cd984110240381f09425817b5a3bb250864da83635d7bdee21409d4cc1c123b4314c50a62f98719687a01ebe0c6e020a1575e4ca0baf1f8845f5e2ef71ee08d759112b38c21b357852efd93e4cf3bdf66fbb352bea30940a197c5b7ca3a75ccbc58f4414eb35a33d9441d972432d347b33512564a78534e77c3fd5b8d5f33c9296d602cfd3d88153ccf6e67ca17175f4a6c9eb3e55640f09280df50146830af0b4a5fcfc66ce8dc1969b74b10b434301b495b884bbe11387f16326d23a1872a8cb122565271f0add5e90e0ead151e07da2fe2afa3ec20f0c8236b01d0da7aa7ab1e8c0ef025a5b50fc7ce136086f1b061204a6d543f76eade7c81256a79c821a29ed3c84e2e5aa9b6a3a382aabf31767d1703f8867325ae576445ea8f5c5ac65ec66de2dc279751ae6f702e05abe3d57dae9022af220772ca846e3a43a435939d08dd2d10f9ea7a9a319326fec2b3f3b085d84b3065245d7ddd7695f1edfc43d98b2c2dd390bb6427b10efd6c22dd21abb683f45b93cdf3061da7016980109269a6a294d6f25e7d7a5a183b93a53d64d1f774b23741f7e956b9de1eeb7e31926a50c911ef670c4f60ef71a061d29901b8fa9a10212d429390486cfce1227cb7b4b33ecba91bcafb301a36d92751729beae4b0303bbb4e1837c272729283b0ddca1be914f9aed780405bb44fd88808ade6bd4736664ec69f1f76fd517036562fc820894ee56d7037676571faa0d8f5e36f92280618e709c4bbf4e7814b1b1387bf1afe01e9a6e08a07fb0103631b5db4b8203f5c2967746df3af6bf1f42d236ffd467bd315f771b5fce0575fa00fb90200e29ecc77d51d82e9684a2521f96d818a4276a19e611e2e670d191ac057db7d23e1fbb552d9ed01d63a9053031b88f5171b9304ed0f5810a53367cc8d66a032bccc2ea54a1f9b391b623197ecd01060ccef20afd0adfc2e2c50517aeb393f8c9ce632176adee7731ec13ca43b1b45808e6e19ee7e7d2f613d38db69c8b130823041d8c4d2901f4960300e9c4725e1dc297037230f029f7e9ece985b0f543963c6d819e522814fec42c31b4895363f0dbb8b40a683a5e9c0b03d85b16fe353f2d710e98ee28e2e5e7f1d346455372a89ca775032df7a2f54c713e9ce3221091671110a28be6f64e09c38e3f03878bba758a429a8b5d15b2b119f5deea0d2d698157955a70c936a4dab0b25b89a32d7ea9b9103f1e67f9b1ef71a3644221f021336d675f9627eb147fec996f5c1c13753026e61a6c4d0aa65e9daf6e32fc7e2660cf2307ddbc645c17dffecd2c49b1e4ee160285a4d150e4eab5e2e5dddafb89563ce0562662226abd0d72d071b7f877f4e3c1780c1a67281fb0460bfa93e51dddfda0fd1053c599ce679a07c1728c3b4807b24d16dcc16d6c85889641fe8f2336cabe34142c590220f82a8715caf73600f3f3f452739fbfc57b4766456c801c31f79a6dd75042dee3a730133eb92616ea89f0a64734479d8bb2801d557638b7db7915d92ce18b484f8bca4dcdac14e59a4f79ffbc3cbfc5311c46128a395d1ab1739862618b76d585b8f8cf670623b1bcac63b15b8dc317841476bce7a5270e89f5c1"}, - {"000000000000000000000000000000000000000000000000000000000000000b", "01c5c04fc4cf1132a804d2afabe6d5f9d90a1590061b93fa8dcb4e45c148d0768ba646a181c737fb7fc11a493d3ba2a07be3bddae1f0e94017823ede38fb1025b27a5c3cea448db26093c34ccbaaa18a21b830db073a80ffe05530dcde8a2369c3e236dd37ba7c8598339a5cd2f830c41bcf59440354ee4599a7a4b2fa790b39d662dec5089ad403948e204890fdd38fd4f7f61281b3205616b72b2a86aa6ea174a0271f715a4cac0ca64d80f998c5a52bbbf23c07cee85566b0f4548146ec94e4a623aeb1d67e548e80cb279ea2347b906213083794f6ae914d80208196bbb23156d58dde461231640584d6261f418f1cb5b65c429369e0940f2d920f25d9af9f29dc63b123325df5615d70b06d6b006b16b479742f17c389e102825905612cb66e92d646b3185deb6629988751d56a778e50db3e1a3a731ebf9630b75477298de2e11656a3e3a5fed2c1961857003d0441d51ff056718ad13d559ace383f521c61f1e0e10c500432aa963ceb83c4f4feb3db669f5bdbdfed450e616d52f0a99bbb8fb192dc17e154d1f71ddd7667442a7b5ad6d584e5b2f4773bc2c3ddde2e3ff6f8141600fe08844a99706da396aeb263580afad23c0460393595dda40ea1cda479b3fd354062dde3f47cfb881c405a8d2564673b96ae9774896cbffdf3f6323f6f5786841fbd248961a0bca5ca5560396270ae177a4905b7bf50a59ff8f129890106ba6855ad4d5a6e5db8073ddd3ce50e2665cda4710788c8e3995750f93ffe4449d358db9fc94bb4b336fe937269e256dc3d9dde59b1d4c863e47001c62a45a8b162a87e1f81df2b280fb2fb6ef8cb249df9ae8b2c23696eeaefbddcca521b30db42884b71d8e8e8940ffbddf5425eb45fb7e1123b07718be33515401c02fd0bcf000d1553b901b1219c6adac8d3600dc580526658ad65a4a7f8ddbc740352bbc7fbc423ef2d7bf1621d5df428c7871461b930f019ebc92c3c17751d64a2df6e957d9608d20a0924cc51f495d586ffaa9155d538dcc8c24af0956f8e3fdd47fc97535276fbf239f2756826d31909bdb2820da815381e4780231de3f0ebef217fb90b9b33bd061ec8bf2b5a8c9b5ced7544e5b0cfe376aa0d15f4a12045cd476b55acab88f602e2c552d271a306f33ead265e3eb3fd563c4ac85062f3e79acb5cf681f1a03208d21cf77286de6ab996a24dc1cebf91278cbcade92a65c9b11f1c43ab12d9e3c587299ed34b69bc05630d28eda7a550b938b712e1e587f093d4c716a89c455105c7de06dacf9f6dc8453ca4f4a1f9bad43db358184266bfd54d96a7e82762e8286325821b285ecf9c2e2386bdf15ccdd361a7762bba7285035ebc5f5e712311bfa664162a19170c16fca75e415dc1adee2570241fa448f81aedd72b78c36caef855ea0034b18acb05bd2875064acc68b639937ecb35431f2cbefc3b592f34a4de81637221f46295ab8cd7626d9ef77f528423829c67b7915d21fde6f610526450c63f04163eb5513e2b51b72bf4c18bf728bf7b5ff6daf4e1d86b890f8ffe5948d0b490dcbf4a2e8360ad56f9e5d94f6b82af0ee24d7475ebe989593fd9dbed7b81f0bc6d721493a7a33a564a68f90514ceabee2835c3e770386c1b04be023c06d833b75ab2a10ac77d924b6eb7a8f506e04d79d1f44335ff52f1ce06d2dc033519fc3a5d297d2eb2a1156d70f326a8447bc740de7080199df60b4611cb4e511749b7f530f1b84370b64e72d916f341eb9acefd94a510a9d2e9c7bad7b896b4119805a112aa644a5c0af8e663af9153cc44b5b6f15b9d76e45eae0da95f398223ec82678ffc655172827f94eec038c66752db5f61e56008a43e572fa91de4799e62075ced9fe37d2039bf8c89a84378fac37f363cffa2e8"}, - {"0000000000000000000000000000000000000000000000000000000000000001", "0014f0ece6e362455de0a39d79f74a891df9af877b16188b2d296065b16fded57006d76112807a5609ce0398b2bcc301e3730b0d162abdccbc3a9be8fed6be0d73e97cfd060757ab6cf3a51d6ef97e47c4f8c670016e4a000ec76810b667e23c73c7e151b57cff351b02ae4a0b461b64b18aeab389f53a549710b73dd96b036662330ee4b567bedd3783d8ec823a6b3e9bd824229fed332b1f888565ce028505e73a8a324e93fcb007e6cdcfe78f3ce692771a2799f148e3139dbd42c73deb8c37929d15470c05960d87d9d492b0d2b99ec70c1b4b2180dabed5c150b47527b25e261a3639c2a45c265c355ca5045bf310d6c856368d5e5e963a5c5e0c4d0cbc2a08216bcdc24166c3ae4728cfc4bcc01c59344c1d8aea513b741d55aab8b1be82190b354ee80d1700b3ca919733f82124e16b60f3ae36b03bb1ba1a67bbffc8983ab35df2433f28f36c494dbd53bb670094ae65f9743d4fc4996102835da41a39d0de1f124af0caf0c5303f29bcb939168bc938d6a656fdb64d0a4394c4921ba46f492b11d0d9f4c91e142ef516601808cf7f6c2547256a34424f181d33b6cb10fc3754075e3f1df4d4711f7d66c1cb0e1dd27d795251886b219a0ac683a193c7b58233e8cf2c4fcef5733bda2b29890bbc52580262eff5867c2ed4261dcd8af90e2f3bbf1c49ddd57c09bdc125da89e1db423cc9370d4b02fc0222efa74e7b4f2d23e117ef2425a9041e43543a8eb46b4f907fd19d145667f9c2aefe20bcf96f950558a3ecd760011bf844647640fa13099fc033853610f5c9c709f1b8b1ec62552f37e9bce179e7fe0af3138b92caea1f5957c41952e2f77f44b3dd821f88f817984b7c6dd0af81c747c3009f6ad8ecd19e874b08165dccbec0c90545103d337b35553e01dba90fffe81f4e1123ad4c792767cda348f6e26ebe77729fd2d500768734b59642e1de22c186b598be97bc7f3de9513036437b494db41b3bdce8cb38de749a64d61b1ecc064bc89b54c77e7672c744e2e4b5fb25e0e89dbcdb518f233140f9245dd021a5df09b784f5c8697644f70b92e5066d30180b88afa537aec17429ab192e76c14c58ec93c769ea97b6d868728cdd44668d2c5b2e6c1af4ceddd62ec3d9e329447a9a3919834ffcfd2a30353e2ebc414fc9ccfabe2445bd5647d236055ee0a4014167d17600aa2243c0f060e3ebba6cdb425857f40600f6a74183fd25e8e701ac9255eae4a164196b9909e2ec94b250baa309c7e30be4201464f6925c74070f05f5615b44880ee7daa1ad8a6a3ded93c89aef5a09fb5da27426a327e0e420abbea18969c6f58f01720e01d07b510949ea7974817432455710e9e66d6f2b1777027a7f6082e9276a467383cd755e57dbbdb17d249787210876f447bc10057a6bdd2051b40d4f3cf101efa91dc6dd943993ceb19e3553feb12b9a95ed7615021eca63ca9cc0f1f213e9dac85fd6e1967ffc0b056f7bcfb485cbdb0688354d913eeff98c56925af0250f655e67495742ee24c2778a583ad1a1be18a208026962239ea8e287d72ac1d8486bd6dcf51c7f77a80706734e572923e9c183b3e512f76e41dc3ef7675f06eb077619d9900bd24270e08ae2c1e3d9085ee6131a55c42d3f916bfbd24d368c68d5002b4b4a9d454307957a5745a541dbd09cf5aebe437072ae6177a34a195a06874c0f170f004a33dfd6fb19857046bad37f0ab3a588c95db2934f0fc0e59c7273a10ccccc093523120189d7f76c2df13ec2a52deaa4b26ca6bf877b09e5679eba89bbe5051b178625d42f569d629bb0aa342a3369eb5922b4dc3fc54ab3bdd81b6fd95cb6441416e60c88547b177eaff29ec42a3f5b26b53bcdd125d2f20e4faa602b8c4dea4da072b88f30d77b5bc8"}, - {"0000000000000000000000000000000000000000000000000000000000000005", "0012ff803deee80dfcaa92f0b5345608fd6fac5d0f067cd5a691057d46a5a47170c08ca571fec0b64335045a1c92780f2088cfd2113df6b8466ef48679a0df0ddef88fc0882b71d429216c210ff9e4776f967e1f07038f232c070b8eb541389f8df83a1faa3efeb78a2f3f628cd2a47fe1bfedc38977d741951d1ff3423107d5a8dbe50f96d10968d23e50b6a51246067aac9a20d3dadaaee16e4bd9efe38859df1bd11d3bd357bc072397d6a2dabfc3a3d040e4ac769082b2953a21b918a3c956b50970ca77e7c3ee1df0f049d1f53d74cc10b424f0df1514859fbff3f76b2f8bcdac037fff9a156d3ff0cf659a716fc7f33175fd4bf51fd30bf8c50c9f2fdcd9c78b4f0998f42226b77755f06e1e69d81cad7c7c9b9ca3ab6f0b67ebe173008391b3decf4817815e0926665c33e77ed17996978239601c3bda4f2ccce4a6b4580e458f9794262059c636726ed3fc700317cd6d921892c73ea30847bec7e41e53a4bc5bde53aa65181addb207ca1437a3f6731cb6ee55fd72470bb082db0e0386e30b29734e715b4fe35bdc5e42581a19e1a842cd875befd11262163e2509b615b0641a045808b4c2cc673ca47903c65038ca0d5db4332e8f31d8e654abaa0bf1cbcf1a95e76bd34790d3fdcb88185f81beb6580a87468db2d133daefb57c17f42ae22d2c43eb3a260e055a3e34819c45cd057d6cde27fc09e0da0b06e0ae79c39c23832ed3fde2ef753ceb982920a50c1da48735f9da0598157e2706315af4c0ec0da2387c78dd32e3022ad125c67e27193e511c5773370b974cfc9e00ed2b24c375e1ac4842d022983b2f1489ead8bd7a9ddbe305231f1def0a4a64d916e93741a29c5d4e232adbada96771b1766cd271725affa116486914be5807c7d1b603a6ce68e4d5249d1cd38633e95661c18d70217fe2c3562f52021f37873d8353009c7387559e96636e91b4a07bedae75cbb05c89133a7294b7320ef6275d934472c9ef6cb66a7d954d242bfe9da6df7b3bf5e5f654160f49c1ef1a9c1b2c3042ff522ad6e58ce37455e58920e2efe596c6fc7d140793ba97d53a88c7f8a53672a1d16c43a2c59ff6431c8e88f1a0dd22530af5c20a66779014d69a59d62230e1e2d3079af6d3c7cc59a63ad7ae9366121bcd3e5993d78b22176fb57f0bb8eabe546abfbc119fb54e01a5a708e50464c5d3a9f0d20bc29f815cb57ef8273f83da54d09cdda316654429462f9a2214a11f19db16d4a4052305eb98c779f7bdfc695c56a5e33c09062382158f408b2a46f9fdb7dd84ca024e5ecbf6bb7a08925b569e8f03611911245cf26d9309c9ec1183432f6e0c2f0f9850515bd5e9c4e74fc0e2734799f8a80f66b302a330a273da71e103619064cdd9b07e627d13c93ad77cface39da3a22039e12aeca3886b8c3960122cd9b84cdd3df390c55b2f8cfa95a39f4f8937a2987d9b889a06cc1115642bed17fcf25b79ad92434104f6751d3f69171e183c5bf71e97ae5840451d93510521eedca53457b23457143ab458edb5763de41db1678d106bdfd9987f7e683376058667506550d301c1bc811db3ccb5fc1fcb1e22703e3db5eb9ff7ab943515c43b766ae3bf7c01c984f4cee2a3255705b694954134db59ca537b3a98217d15275dc6203b83269fd02aa3ba9d7984f2d25368422955a1cb5d1793e23f12002cb09a75137eebb4346ea3de0f0e1e1819489f20552e2602418090ce73a80fe77990d81244b50b03e100065bd3d99b603d8e384f21673fb4540796a6e2e062c5f1e148ca1dfeeed75c9f261224a029db20c7e19a1772dccc8a116918f2243e71378717300fc1e160fe2a08ab14c2a5e6f84c3bbc124d7c0f3673dda9c341fa427335179bcff0dd45148a8ff591832906447"}, - {"000000000000000000000000000000000000000000000000000000000000019c", "0044a6c2c7e036d77992e156fac954cf173818c6a7231e3de5cf893397c5f1944b7db3b8d62c6e9ebb4a173dd7e335b0eba3c5bb32708cd0d6d1a7729a1aa41b2624d24c1b543d00e19467abe4ae451fa4fed03701aa6d9aa4920b9b4ff320d82573fbbfd67ddfd10145ac57c86fd1b1131d2829f6f67eece783265d676d037f1ca3224e62c55f18e14df4ea250d1c0a78d8b329a39c3325582755d38f557df253dd6a825776bb35042f15da17e070af615115a58335f76789a65e5d8139afef952195d3ead846a725a66cd2ae2b8afb91eb0917a555f42e739ba74de1d009a4897c78dbdfdb110d8427635f89f122c11c858218cecd91ad951918b4078d492826e1eda33482911c21d67e51923a8ef32312cf4a514af8e54be258f2a4f7bdc90e100c380ffb0ac36c91ff2429a94500f577614189ae7f0436aa8339c9dac09657b60341bbd886de496a96c7205aefd100983a8d1a04c90edcaa38ae2b7d31c29f7a59f0754df62d215155e225fe429548453c1845ee3dd95df7130805ce4b169eeb2c6e268656cc9e4f54a37cd4751b3c29652e9ae00eec50476296cc4659e6431a9b4d078f01b0cc88b705f84e824bccaa54aa15b99425510fa91fbed00d9d64b91421a31ce5666884ab5dbcce17058b46cbaf486bb5cf11e8bde5395a95f436217b24fd5d8e428aac71d70457796a4d5929e765f54aa400a8ae709de75dd1a04da83dd15ddad2e2765ec29243549e4777d317fcbee975a358cd447de07259075b1bb32ac0cb485f6f30e4d428d747a53a3afd1cf76a30c27f85d9ae0e2f8924152caf75e26aa77b588775050d94bce6c545c303c370f83609ea51b72eb502280eb0c5a18bccf3c7553c514fc71dbf1edea29a15b3059dc1434f51a167e21c90b59f500aec870e3e50920664f44bd91ec1c5e9d39234cd28604dae1c2fc829033fc5c53407f8018f46332406f433f5325d69f967075bfe83ff9a6e3d8bfca3fa10aa52fb3d947d7fca0601e8c119fd531bf991227141f0f76e29d7fe36d61946f112a314cce6f6d5f5116b2d953394643fab610cd4faab3a8706a08a01f18008ddeb5db90e5d8adf49f7ef69a81a8cf5e729f53542b1bd2a1657f358910dcc7b73f02c2951b500ab05556e8c7ac8c75c73792327c1f0b8ef82b57fc372e94a3e5e5907ed75a86c039593dcdf119ead2315a20f92268f19f233ba9b26169f7171e6513906af76c206c099dc3232337a2ba21a4e2e8eab4d72f8a0ccc26d632765ce9f22565e7f2dbfabfd08dd8a7efa2de4a4a1b7c35f04145f80b90d8211143f4767bbb5f44764243f365604321790bd44f522f4d2f18599d2c3071f11d3283a73afda82a0189d277a0d4a38fab1be535f26f85480ff388d3b5837b38ad98353c72103c306eca0478951fa9f7363f7035ae7fc0d5e01fb1f8b274bd4dd2f2306165d28c32ec5ed3986db2c2fc9de53ba4a7de5c33b1f9c3db80b0472ffc9d4058cfe3ad23b0da9b28a97a71fe57b103893dd050722d4d0c9f1a4d7440335d690795fe1049765770ca024cfd78bc27dae60c5621a40b891410bf55a04a9cc383ec4cec1096bb829ed458cd2d56d1058d4396848c091723d8175d5e5024cc9a7d4d08012e0776a7e46776722fe84eb37d659822d7b12374503b7c7e11a110959d57462e501a3668d7cba59be26344e73240af87f97d4dff47f1b4dab89be77baa4290c3b114afbc31a4ac79f41d012a7cd2564f72e7b5624f292e5c49b7bd54e6689c2087292de9ea797c84004984f29deed27496e0a431a31dec199b16917a7906497cc35709d6c5bcb329a1724d4f896bca9593c0a0d258e2e5b8e6469d627b23a71c007b12118ecc9bf16a5e404f1618df963d8252e85b564797ed58d42f2"}, - {"0000000000000000000000000000000000000000000000000000000000000006", "0005c144030ab585ab2a30eab225663dc7ced30fcc62184f9a78e7dc0d45d416e5396b251235de32785203189426cb289dd3ef1440623b8bd69b7deddbf8ec0da47fb7e5c4c501c24bc5d47e3c57d97b3db5cac302f3fc93f6c8529fad480a83865f7a0ab7a43893920af3b5e6a7d002a3cdefb21e0f477100a084fc6a0d594c93818d69af5b5cfbf5f9244fc5fe4ec2f8e28d62d2652689255bc12b4f68cf3571152e57e89de0fe08c4c948f855480720e7221e37e35d1add6c9cbb1f264c75b3eed17946ccb57282e6aa6081344e78324612a0c1aea3a1b5ff3bc1337eae35f04b5f719f4170138a1a1fd1d76dabcbe72268da79d8d52efc6a7f920a8b00bd71865e644d5f5717dfe02f9340b59fbece26325c4f45d3deb319d7058d777372417dadaeb3071da6b45e30d68438ed2c04ef7ef50e91bebf5550372615857d2eaf7ef39a60264de575e1063b4452cd46052062b7a3a346fb607822194d5b3f41366d0b1311172b3ace03d684d37534a85b905ebbda697cfb47a0181cbbb519995d2dd0f29209cfd8f4e2e54ebc6cb12f750cf7549bcded308b27eb626d80ee089b7bae352ba3dda65b1ea4dffaeb99900a6b8b167443192b45338a83a1948d61519c34e48b58cbd5e5e00d3278a82c57127c44ce3db531f92497a1d209560aeaddccc1466537e05de898e3af70e4c4c66b6565d14c1c2ff307a3369bd59cc1cf98dc05f85ff0c516cb07fdfa23241022aea13c2c09fbac36dfcdf74739e3b97e165f0c802c5017663e5d6bc411d804ad6025b21f72ded44bd0a58da4943a27dcd007f858c091f3111efd437b140a0c8d131b5b24eb07752f9aaf09f9c2f57f282615e03b5f5096db69420e43a7856ef021f3df725c981512e15e7fc817e7a75c518bbea553ea76bf58c78b18474dc6c9573d82e528832a764d785d4ab717c1a5004a53aeb8d998e0eb2b13e9d0389b8da16758bb4a0327146e48c8a92d757a19ab5fef4973bb775dee3b07d93d4470c3032e4d32f44492d4cb69cddf51bb0c49b92545cc579160bf2316c96cf18645e46b3fcf7305a54a24e3447c573333532e3fba4f499a90f3d7872da9a75af75105895f9cf375ab62d60986b8ec3baa1eca190fa3cdb826944ba6f09dda27fdbf497c0fad26a81c6a75b1e8c5c2a862d3844722f5033e7c5d5001a8a1403e6d5bbd9a31f315f69e4d813c7b8ce004057741d4db90f7bb697d42fc6020516359787df443063ce1dc3e567b85c13c91d07798396d9e11b508ff28640d3e639068d7a7c945e4c5fbc12e700edd9af304f88b2dd967ad45afe550fe5423865961207505af13d536347ae5eee3b08831dbadc0e2bd629af93af90799cade484e2e79fa66c147adb4e48ee1fcfca9c012660d1ef98802fdc3c3f3ae65ce4362be8e36313302f5bc7e360bff86798a303bd7a540ed224c5197a67b597df4bf6722857338c92809cb95b6d02c1706a40ade0d139c0b9d1bf985d27345b1b79678cb54967b32f19e094a8eec4d8719e3fba37af2fd81add298460464c5513d81b6cb46849092a98f5ad0cc57ffeaff04c386e516193554e2f80142841270c8fd784e45d80693a5224d980abaea4905ce4b425905dd50fef12018dfca457109fbe36313925a8d15dde64686d8c12406902f83814f81b10ee4d0b3e97ad0d8dfa5ebf09226d213b177db69f5613cb4ccfcafc8ad4c664ab5b8127f94364851dc031177924ba3d6a3659538d74c151dcd7103680a1e8b460b15656c7d82e601639cc8eb0a512b995e84275aded8121409ddcf362c2d13f7eb0c83a459d662893148d5a81f6572803663e0349d9d1884d2e5c5b67d35ff8cd697e0ea993f4dd49eefcb31f0b482228f04a14f125328ce2de5d5dfccdcee76"}, - {"0000000000000000000000000000000000000000000000000000000000000006", "003ed39a7e92e5c4a21132e5d32301a1d8376fba6b1b011feabb95cf19a91e532449dc35bdde6b54a8a9210d3a3e908dd2bfdf49737bde6880ae349f54f77d4468c339a4d3ea0bf826c8bf51ef9b7e4f529ace2f1342742b3c29a0c7eff632e2aaaab799cd975faf7928b945eed88d64b897e6d4423064c13f2734fceb31223eb679cf25d601784093a7df70223a0f4abf1ecb2fbc0b67c81c933f6a98537d98780de1293d5d342200a0a816f7052a853329f4be39baabdde2ce78bb6e15903a4bc4320107ba665186af678d0a25a118c69d18672bee3c063b5ca84154ac19b5de2e067699d6213afae214b651167de318179639e90912282f11cb7d01269728c7263121514e29a82b5a8c1bc3035efbb71c950118b2ed530393bcab338f6194ff3a219f306a0efcc74305b3029bd08085b34ccd51da11a057c78a5232050700d89fb13acf455c854e448a04a6db4f82011e91551e0de0507b57451d44f5e079990378bf2c1734ce15df9536bedaab35152031d15accf9d6e07d1365e321d153c78f7b51e843e6cc194eaf473d92074498832402e79a0d6e3427ce73dd44168bbdb7a8d80167dbc7e21a2d45c38a80c16638230a612816cf60045d07592355d1e712bba058e2062e24627f14a77003c57398c3462880961a83c6286bd3953f43f265f34874ab1c53e8983d6b8ee9e286f37bbeb224bca435081962a95384bb907a07f459c737e492d0463b8ac62e554b8a04ea4f0f750314d9126795a60f91bbfa671e2b44e467a762195e9bf5cce7e41989b35ef6683449cdc3bc6e1b9827a3dfa68152d32207062bbf7a590978019a45545b5366f0c135629b9e61c76c1e6646136d86f73921d437d3e60981016a3e97a8847e49ab0bd575eff4d090c57b6c112442c2ac12c0463c00211c356185b5700be1c3a2d35231c2b828f994d0e61801cde136bd80c65459213124bc40d6ce33969f9d0a1fb491f2bb53e543f866e2c0d33fcc1a4ca6d5ea050ba5ef29bdc437aaabe964e9f754be99a76bb4f5e00e5b14ca0ed5ed39b8c1b74080fef37eb522b831260a39bdcb6f1c6a39f78404a591eb8b113087ee8cea3851f4afe15e4b6b08a8f9c90757cdda8a0e34b953113657baf55529656dd0e7b4db7dd7d3061c5d31901d1d35adae11c915e7a137b7b957db868475ff916f0b41ad69861698f778dbb0b61297cb26a27dbbceb21bc90e161f8f79e5eb70354c9a68afd15882b4909c1d9e70f231ab66b3a2d3b7178bf2011232fb9325b71f06c79b015c224ef3bd73a623d6f122b7a8df28710dcc9ed902e749cf4a62111b6bad791daba84ea8753c02ec8edddda8faffef24bf624a9de94efd3525f413cfe6fd6d26a91da6fc1b3ed564031b89b75f43c6512aebe2fb9fa79537f7b6a1f46b986b08c49fa907083cf859f2831692bc06d1f8ef6be56108324c59bf1bfb16c6f609185374f6e29d5bf63bc8c9f8b0b14b12b3a8de7f655447440ae51f9b45efad7700fd86a01a9c874b59e51e71518bf1ef1ac15528b0c9bebf1619c29cb3ad4fead6b496c5cf75dd1fba5d3719f650224414f92f8fad496109347b96651cc5cd2e5f827326c2ee6143194e2d750f54947e68466e288a5b205c344854cf035d6c82fa0cc6d04bf36ccfb1ba7f55f40b81e31e585db1e126d1ab232c663cbacdc87e9a892d3bbc4d4e9352b4d65663ee1854cec0fd7c8803931b34cbca8862b30fad1f151aecdae2525b7f7b32942221c42cff1799a2fb8a23a2933c1d0775f97c1857133e50bd860aa25a63a7b504d0acd46ea16b1cb0934182bf497a9ee3e9f1b5647809d8e6c3440e5fc23614d745a10572ce5bddf0d4a05db220a5911850b1a11c745ffd85b7de21c1d043bdf0f954c55e2add53fa"}, - {"0000000000000000000000000000000000000000000000000000000000000010", "01f460cd7acc8a0b205c813580297a189abbbbec6f02882b11ce42c03b32a8e1f53494bf0979a5d75d0f243a1c8148556666b54d931f3ddbae8573b5f7f8a230792b71b42aa519f2b256e5433fa0330cd8dd449f1d51abfa6aedcf97b73f83dc6ba62dfddff4b4a16a201b128fdfa5be599b20245c5fc32e7288f33ea0ad37a1c4ea0a169091459e5877895d844ad1ba396baf4b96fab70ae0215fbfc4e65a07dad30613f23bc44e027ea8a5bd07a71ab7f6f0b110b918f1db889605a323938aa2f4ca7bca60e7d7d629ea3dda8dff7909670d5862c82258872aedd7258b9cd031b1eb1eb4443729375faf14e654e7373dc5c870b8969eb9755f540c048f0a5af6d02aa78b18b5af4e6623328548f5a1891b37b913be6e5a03b504b2003c3083095e413eb23321d6c16c61174a2d3ea752687de16877a5ee1e20b265791b4b361f4bdfe09196ee443c5166a039b7f73e071447c264c93a65c277a4d167cff245af335a10030c719a76506755bb871bb50eea2dba59f8203e727c0fb1bb1d2a11d227bfcb47397ee588332a511e2cfb36b909c80d7a350bea3b338d36cc29a917b0bc9a7f0b0b6db40c43705cc59004203e6db18950b8f1dff425fdd20e50d3b746c7f8e2636f42cb3d4c7f904e212456afaca33839cfc83c236153a6425782b63ec95a3db1f6cc6993b3bfbe1bc62fb237c3768828f960380b971587b4e34cf3a39ee11f8ad155fa149bb7583a10ac6f49140feb257f0567616c770d13d8bcdfb9511481622e0b1b7b17c1ffb4a514f0e32e21b8b3ab201d8492ee8d07af618212f4ed0c674895ef32d208f20f995eb6a03a365dd62b61d3b3283ae2b285dc40316267f5c4c9a305498491a7f12ac832628a703743051c2bdfea3c563c38c4a885891733242f571f7b27b42597c15e47ca00cbaf8666ba6c4a24ddfba135be6b0622c7576b44658ee1919066c88503193fa44cec890fae38a4e1f03a03e5719413983d957d635ddd46100fb9c4eefeb6d12db755b8b4adef17b234477949b022ade6f83129ca9fee144350d3d87bd9d7681d426b0818a0dd7a02ec51f511f20697ba1805c025d379071d7cc57a1863f1d727dae3a7a243463596fcbd4cc12ac3d2e498d56b2b9472e5ef68cbb4edf26d5c5dd04344d68e1b97eb0acae9c523994ca3a5f73698fc1d0edd6f1219cca2707acbc34ceec10f9269f9db54fd111f1f4bef2773e54b81f25745485ded35e0d7beac2faba6879f201f8f50b603ef55bdbfe1a1d3b8ed484fdeaca8ccd9dc954b8bc5c4c5b709a5f18cd50e4e18ecf7cff8d09badbe1cf364b24b0f865e513cb5c120a824e6a79bfb498b3a658deb4af919fa5afd70c6263f07929f904da3c243d7054c72111b5d93fd01c42b1ffbd59791919d544b34c8346dc74733d37a508d071155e5969427f52f32d699256ef01b07f078b4c30f0b0e50cb65d0fbe336915e51327fc46e4e1ae8dd1973e3e93a09c83d2456788416fcef1242031a84164d990c7ce5a039bd8519a584d3bebbe1f74c3c498d0b403a9b7a8e2a748333e57263bbc615c9be7a130a1f12e161db8c67509fd1a658d54e157671c3bc8b011119df87fc9c05dd506445e437414741a96a51238012658177c5eeef5995bca3107556f78a3bd1b7530d07a1fbbb8f108f8b5ead10d6e173cc086755502c6c3703b2f0a94deec5c9ff655450daeb79cd51fe4d16094a9381f40b747ef5fda524f175679dcfd7f5cceb24b84c84e4377a2dcffb23ced2fe2765345dd25dff214b8a3eff167d13b9c852a42fb02ad7163bfa3dcb2435a4a048ec1a37e9a1598edf66463bc70e7f6ebd2cc05b3af6d705295b190989757d9b832e2b9e02be50a62e6472e04709264d8613c7f0cb86152bd1e664"}, - {"0000000000000000000000000000000000000000000000000000000000000004", "000f0222044557a1a3f1e59f77c1d05eb6ca77aa3b011d436e92d453d2fc1270b7458c8c56851fbb55dc031692e27609e19aeeef8101bbf568184f709729b828ccb31de159a270f8e6d4671be090c75b5cdbe16810635a9ad2fbbdbfe479550772b8a8de02dd74497929c872fd55cad6809ef93799e56019d2990196176027a2fd58aaa05a5d32cf82f9d9df9f65338dded4224fb50502d8d477a346460780773f38eeb5ad166900023e1455065ef0176fe0c27f6ef561ab5a1c7d804f30abe3f3f4a1f8ff19e5176c74d39d425f9b3317e20268e3b22385ecf08943d1f3a157f936a784b9dc754216efbd3eb70cbbc967045751f3c9facdedb6ad7502c1eabd788952399cd851f54af0d73938206ba4b00d692288f464739b46e341208a9f788ebe08baa4e2094421c80d0f4884b2d8830a41357585097fed6c38126916fff54720d397c7114dfcec0efd85446d985200117357d78df645f41575c10c5eb452dc9d98b0aa4afe3651a82d184dc7b5460f4ffbe00d86487e7c44049591448151f0cae11982c10d74be720d2f51b6110e0015953444e58d493f830f15329981aede986563087bd9cf4268fd574deb5664cde606eeb910f7a5261b6cccb5314c16746f2c21dbbf2ef63ceecf2b1c0911fd341aca65aea993aee23a95210edcab23cc503c19b309c7c12a3741572bc35afca20261c1a8ffa2f90abab9b6ac85e587a198043cb3acff3535e9fc450435bd9d87edf33135b48f443124ebdd51cddc106f5033961412b6e10cc30e4cc597d1d0afe25ff4ba2d6c35edea841f29c285a36c2524514cfb4afa363ecb330ad468df81aa64c5d599d29c5b3c716b0e4bfc243063427363efee2cfbaa354927bf6512beea4559aa651b96ccbb6ad49f2f0204f1dfe831ccb917c5f091e9405254e9325515c59daee6fa753e9962ca02feb13900a903ddff290cd582a314180ff84bae4ae6f98ad208f71047f488afa506e8a11e1633b6a91f63abcd0e33fabe25baefc7e1dc9c589557693d027699bf96b3498f5b2cd69e57290e5bd79eaad5fe82dbda3753f00bdc9092d251c8611963345bc2f744020c839e027c0df789cef00b02aed51178c555ee77eab0c699fb260cc72b22619681ed38b408527561e122f6767f7f832493fd9293970bf78c41d7647bef7ef2fca8f8f3420857f6dc5b4f2ac70283a6e912c7ac4a0a4a1260192e2475c718f1d587ec09b566614358c9c92374c3e41a9f96605d4f4bd740495273ade22ff25f8ddcb4491de9fecf7f9efc85ce0a383be2684a0a272f9f99491e4b7aa3623a0f6df6e645fc95780fef3a0ade48a121064ec1714a5d06a551a313595dd755f9957841f427d92c33e5e8ff235dc1b7f03662df4a99a457a2562d4663d8f2ea1347851604eb5aebd1b5bd7d9e933c049fdca62e62a14b703ce720c23c32da401b369ca82207871ea5ca82eafeb0f5ea82713fa6125e3b48f80c000ae32a742259d522a0cc2c2015d600c49dc7991f60bd210f9c9dada0ae6386b24cb67d8e5db92733129bb15cbba0ec1f78df486de97cb2f2eb65d772d830f39d2f8b586244fb43e9fdff752bff65c1fde49f164a5bdd63dc13dd2b6967153ce2d1c28254d68e445470cb36433c429df67cc6f9fbd732820b4557daf607a4f17133cfe9556c55936f61bcbf6eebbfdaa1d7162c4d597908760bcc03a3eb086da2ca160cb0b2711a11e3b6a6e8caa5c72521b61f3c961182d36c8bae23b34a83be9610013bec8266add7cbab17db5c05d7098af7b85af4f9cbcdfe3c1fe1725ac31a0f3afefd1ef2ca0de648733dbcf382963537b9222c5598e91f1964f654201bdab93bd9922323ca3bbd9e283930544967ecfb419fed3f6a569b1b615b3e5f4d43fccc6c"}, - {"0000000000000000000000000000000000000000000000000000000000000027", "005b86c33be0c0b9bbb13648ebcc428e9584dc3b1215f609aeeb6cb103902079bef67829e6eba839b18a0454d23f63c8abd5a60ba3a9622775053b65bce948074be9e449eb2fa5adcb00abefbd2aa87c37797898015bf562c6df6768fc2f324e0ae9bfbcdded49fd0d19b4a99422ec804bb698b7672e3d1e0f1aefbc2deb0383270ea4a1efc9eb9f846883ca151224baf2ba1226b9bbaee70dd3e1d1322b6cccf4cf3339e039e0fb0af14da450d491daca2602e21753413e0ff9bd284d57277529c8964155aaf987abe97c80b296df9a0b830ebc25ea6228fbedd5a9c1177b32b92905ae0e02aa1c740e90cadd6a5bba3703e2e9c284bea9a03bd97f123fa497110a5e3a89fa324c20315aad62469cd61813ee51d1cd788feff6f133bbb4f77cae23aa1346321b67aa9206bc3de9f11256c7165b1b12fb95fcd80a1cc3dc637ddb2cc36e3ba464d0395bf983e29d032905a4f431732ea6e7bd05e41e5046ab0ecf463edd4b0bdd34df6dc4dc2a8d1804ab2ff26a6db43cd5dc1507967deb30de73efeede308d8af684987420587e670850feb9851e5e25bb9fb089687aa9a52edf5455e6113a31a6e4b0032d9c644411ccb4930681fbfe914342e67fa643566df7a56e974445f7cb6effa4385f7b2153444e628d76c73c6468eb4bd2801288feb5cba8310266b5856022631a5c64dbb1541aa5cefd7885550b016fe5271a4a69fe0ad602a947e8d229f8d8b1a11454d306960c36e2ebc3e32efa38648eb90d3b66982b65fb40b96e51ddb129b2f40b25749d6e0cabf6a437b73fe5d020c9730a60a55de76e953678385b6bb116a342c343ca8ba2773f724112bd9d2247d632e19e58d262f5ff31329ba37aa5a27f4e932e324f1c18ac261b249700251233554303986fa5426d1a88090fd62d891ae844e4b69ff1bf93f64e7787d114f033db9a0278179b640f0a7fe49303549745339ded54f82d7f86b34ff6b8a521358fa309e1eefc278f31873ad5c20adb8118cf5543b929b884ca85cee8fadae15b63045ca6fc27555f5ba35c6e55cd4b3856fa0fe21d6b0a07dfce73e9837ed34176607f16b997c85b61c9a28323cef1ed40abfd05b6e5a7533fce75d76fa27fc9b9146da498c6097e015fedb2cad2e5d401a0b1566b4a4ce8cbd75d54e0a38378f861725318025931584f860300cb85a792c614ed42829d29a908e524ae4aee29040973ba4bca0db3d912f28d377a5da0f451d3812b067659b0e85d1aa163cd21cbae92098353cb9ee68a2a5b6a47e255885f648df501adde230e46f7b3ba09062a6e5b76e1c1efebd1c40128c39e854b689dc47815d03b9c56a64bfd450f07658ac30a12215efac19e15cad4027d4993b1082b83a535eec39114ebacf6241b5e0d7733da8ef6ea94637b61cf08b747083b1701097c9f6c9ee6b6438ed372ac891a1bb0cec31ce807120bcc734e157a994f9ca13eb73aff8954d3b1cb6c14e1dce7b2971547173b75e388c69439aed67bdbf818200a08c232da51fa96c1af22e705e2c78cdc7e090f480c3e9a29fb5b8321f315c6c10dfcfeab4d25622d48a2689cced2447950d7d02fe63357a3399ddf5c11b796afa0081f9eaec8b1dab096c2374736ff212a348446626e91c09d247a73a7a87821e8edf4ce516f0e833e4bd293b7e3c69d4b2f2c751057c80d1f46da516d93cd0fecc4bdc51095e75b414a259a4fbe44e810293ecfc6696f5f5832324b6e76e87d537a38cb6c21f2ae1841da27d744c8d489f5e1dae57976d552a80eeb68f91a2cacd3ed63214d18b8fa68fe792db4674bfb2647badcec654084eabab0fd39e3582a3e2efa10e372ee6655bfe139466336ad56f91310241f15761113d1501b53094fab3522785326d14cf9564b3290"}, - {"0000000000000000000000000000000000000000000000000000000000000400", "003d8c676b8be132abb260199b5e36a02f1db1666c3050d1928377ea75de7a4a8271fc29c33d619cd41e02aede70c18dfd2077b229b062620ac6e0bd5c5fdc0e808b5133962da7aa9bf4e608643cede0dd1bebeb0cdd73b15ca6d7cbb805e1f7545fc5953e860a87c24daba46415944de74f2ee8dca4e27e6643411f86f643a40333cc7a1cdfe5e424a720b582455cc20e6280640ecbeb63e9654587101670c7cefe51cfd94fc056012235962c417145a4b328bc4047c20a7e22dd2fb3145230b15f105331bc8da1fa522495c2450af9a4120535ddfa26060fab00b8530773de90ed4b48f88c8d05bd2d2f2f544413d3c3253c09cfaf93d2cabfbd38034a292600df56d7b40151096fc83024cfb1510e201b936caa48c9a6b0cd6b664c2efd412df3bddee7850c151e161fe6682d96e8a1fbc163560e2905381ee81fd608ffe39bc5b961185216689357a20c675abe350183f33b90830cdccc7d71291b49802b5df67d2aee649f3333d1708b23c0897658e3d11b4208cf9c510e28a3ac2007537adea74fb491c15e82cae8b19e70f45c339c1fc4578630c682997daa600f92b6d77ff0880dfb72d6dc15716f9b5a26361bb691fa09ba727ae912afe9d2d30b39a18948b40d8bae20897838bacd3f13876e19b0093e63f4d372457e476a50e329701d3a1f0e9fac56e379a530c8a22b192ce9389c24e76d7d05ed38c79c468a75a35de0a96c1ca37b1ef99beb3d1641c44d89d50e85848162877a613f20a913716c930f559b0c634fa8639cfb558ad2becebe1318d4cf4a1bff5f925c6acabd78a484b5b53a926933d79c56750c3181cdef562857c1e1e10950b87130ad67b2b04f1ebf03c56fd660e1d75a979f2fdf9bbeab87bfef081a6576f9d114b5f4ffc6123c2ca185e8a1769935f71b094be549d5da42b4f6a1cf06ad0f809a6fc7f9b1014091005f050da648c3575ad0f6eb5b7de65dbbba773573f277af77058906286ead65aed696f1b60bcf0262095fa2d98eed1ce0034be5f2d89e786c94fcb6105a523aec17bb618169d6bb0357d749dfdc79a8420dd42acce40da69d48b8f5abf4b11ef692e67c602238080344286a988ff4d8656c36597c32b02bb6f5eb4a369c15205fa20bc1a42937496cb8f29811ddf80466522f6960a479a1922dc793c46cad0edc0f792204014a8e7c30aef1ab8830e697533dfba9fa1f1efe3f3bea6471b42b25d39899382a0ac55cc25c5f1682fc01a12336d049e436dbc7045614fa4c9921163539a32bff070402b07541ef61e3953f6d6925463cdc3b570d853461054806953254b31e7ef6bb819bcc17b0d70e4ed268b0970d7105f04569bf42fe755fa0bed9151ee43b200e2f390decd202a1e5a8d48dca14b22fca3c9dedc150e4a33b677179cfbbd8b7769fdafcf87b0838ca64f7703499f9b4617c753ff63890a5f5b5e20c4d41cf36d9d157eecbd255ee39b7a4aed348a40b0d574acf9ce5d093df4724d06777a3c6d4fbbe6dfb173a75acbf6d45ab791dc5f96ae3e3727c21fc31cc09a93eae980a9375dd34e4a617600d66c12b3e5022467514f39b2334b9ad02772a1b628512c7487bd7500cd002cd2ee4cb8595e20234b63ee640bd10d9327d13bd116e3c0e49b38f4d941b2c5e88f6a74bdd4e4c0bb95502d0430e746ade08f495d471c30d653a63aa1a00376442abfc876f7965e91f7f4171c627193d081244b8ddd9995fb9bc33e42522ba2cba2c5cb9fc701fb331628b90d9ddf5ddd6e428f9a4ea37699473190c3c2a991e8fd20dba1214ec976174460362bfe5cf1fc56cb3234e1d8bbb52a25a343571ed7abd3116660ed3519cbe29d29f9b3e66c74dbddeb2cee97980ba1f4a5c8952884bad4eace33884e38e71ac71b8fe2e"}, - {"0000000000000000000000000000000000000000000000000000000000000004", "02c7a8e145dcbc037ba5031cb2d7d5159f344d3c155661bd830f2be755ac66a63f886c8ed9da84707975143bf25ef752693ef6d46171d9c2a7115c4f7174671a4234182152832f6358947c17f4635788035ed5c61cbad5919262ccf96226f3533855083abb141857c1387b1fbc4399a01b813995de8d4455b9896f978acf2113294163df9675fc5233c4fd7e42b1bd925e1f34400a57dbd696742b8a98a668534b0eaab085f6f853034eaaed58649f91e1c0c0c0c4d9d0a036572dd0172b177af8ed96a24ec177046917f914a2a4bcb695e505e0db803fd3a68926a900996b5c3dcc7f608d7d0b3fd7c5f771efa073b7fc645a5952c58ab416d6f08c0580a0f5e844e7e77f40240499ec25263578fbc6ff11102fe6668ccb50871281ce4ca43afc907b6c0c641359518b9c883aed826f92db962a4eda0732357257240661e1dbe62df3c20387d82d605d92fc2f5d44c1048c0ab932d1cfdee2956151983a8e4212969cd0bf2d45ef4c0e4d9b8b9aecc7574e6346c26f881f231818c1297be15509412a0a53ca61bab2dda5091ac21b29f266cd6e523f20d6f084ae4dfa1a59ce947c50c4079bbd175b0deda888b94404ab4ef79d87ebbbc43b26913a5dce4dad91220a66edf7f7538ddc4d79ab991121429e86affe55bedba4737d2c9725d157f1b18e35394bb95f94a79b717423ff75f483160e71da6aeb0dcfcae5b18ee5f8f7fc50e8b34681f15722d243cd1115b663f2564b83e450b370685606c931935392951d6e2b93ed47a02a4e0201d84c268238c6d0d9aa483660f7484e3865d5fcd043c59048897704467b38ab1c882ce2404c881dd043a7a6c3f20f561eba5bbd032aa1a791e814ab66b80283ab3d314695359e6ff4202bfc2a941f6f3715bc0344ea1ac8e76a9d587caad4325f85bcf42317db7d42d58401bd8395894f8e6ffb03a129536c472bd1b673f3b6349e478ac04fdfb49d117919a6e639d243ff4d91423716ee32bbba5b2e3d0be97cd6215c38eb8ea14b52c7ff7c777b059fc8cf13a4324d748881549d99d17297b527367708161eb0046c156c004439350b2070952957e69853d315e7b02707652cdcef3ff7cb9be8d6117e27a2932ab9e1aa2475fe3d87f7a815bf39c35702b2c6c1a4e59c0e602ec09fc6f42985c37d1647a23be51f9244f8d3cee505a61f81a1e74773bce972027d2d8a99e60e341ab115060242c38d23b2be25626464dc3b719ec77e5c6d0bf70adcce43541ee289417d21337202aaea3ec52b1512ffc1c3aedd3fb6e2e425a35807f9fa3450f9c413416cef9508c592e195a3c8b3b7b98dcff57794443afb433be1a91fcbc588d6cd5a40c1e6105939328c1bc6c6e7ad47e553838243b509fd8425a4573bef721c7b5bff844ace9ddba7879f5df0bdf5f4fa11e8a4046bc1223614355bd28de1ef7b9e32e5c2fdb8c64c43a9aeb368f09fd5fe6775d7e735e69a0bebdc8f950fa96295ecb3e163dde88298db15400930d62db165255554cfe994836d9f2726e19de8d8c2a6ac9e9373048c31797636d7a5ecafd81d10e6265a9a4458d6640f6956c741e8b8c3864859dd41d92e269106f9e1f30b290ff9a689954de793d2955b9dcc94da210eed5f2b7099cee30ca705041344f069c345975829bcb4dd04f8f475534ad6b4822c611ad3c79d485f213707080f8378d26f44211d2c29130c7dae6449453c1e349a07db2d5bbe02f619cf08a1778c6488e9b2203a2eed1f53093e789186b5305179cc2669e71a92af9d62540789c8d671ca7480e9f370c65629254440025a07bd3929757b215b887ff2201469f6e10cf1bb4d7fc6f016265d5754660e8373217553a04f3baf18069b97953f15a460dd231fd773220752c852e24deb17789ea3"}, - {"0000000000000000000000000000000000000000000000000000000000000000", "00ff597115286b1d48fcf26e094c5f0179084e36590811fdf3c2c8d6fbca0604154cc45f4b7389bf877f02e1e15da306595c72a8c0bbdcbaafd4841765ea5b0414d9b2d58d222e919dd7e80578a9cdffbb5e5fec03f3f942970a48c51cf1fa3377ed4f4e99d31af5e205f4954d711d9e4518fa03ded777b4abfd307ff1b2078f66bac850a2ad00788539d5676323e53fbf96781c10cd212413a922b9c7620959dc577d93e51a1c2d0ebafaa45068c8e9bde940f4d22565f2b2bf1aba461b3631efcb513a4bda6f844b922735c5296d3a5ff2174cb5d0924a4be38ab963f7312aaef9acb01e2d4d34a326e086e6ca9bfeea83994521296f835c9e258310f142f9268727b4f5d3942ac274631a34c0175aa71a56bd4e36603f3b179f732171beaf99ff5f105ca9169d2e277acce7ab57a4d8bf96effd2a90f5dc0cce1c1396aae890eb489d96c34a8cedb1918b04ed417d01b0b1d556d796fbc99340d3a1fd28aee83218113b02b7d114ed956a1fdccdd11362d5a78067a62489fa3d8b1d82ed558a58c8d084d4eb6180422af7ba9ae046d14faaf7e39ccffe43b51d3a5e6cc22561337c25081df1aab24bd075c8d400f3dec2b224a87f1c75c80ad51f52678edf17605401be2940f034f171ba032d265ae18552c99f8f5cb86292b052716e0dd3fb9588379d6ae4400e55db9bb53539eeed8b75eccd30b5ce03aec61b2513fd75d41856cfe5c64ea213919464f2127b75557a6cb5d7fb104949a07e8e6b8bb93c997c12bcf4d4ce5302ecafed32900dfc2040dc63c6f8a62854a2415ed01e088d4b25cf9ce32bca7cd57d047f102621eb17ca840f945d664ea2725b26cc593ab75d1631c68249528e1d55a436bc6847a1f6ede87baa0a21a426f3ed5bb224f2d2b2307ad37b9550c83c66502cf6fdf739261f97ba3ce385e4eb13a6bee4dce6bb0350257e03e5ecdfa9e221c34e1c2e05bd887f2b2915015d17d6200ba94a6d824708ada25e60c5bf55851e230160b852d4479b6be815f0de92e2a413fd59272ecef4673932eb67bb6fe353ac2a67159f495fca5b064ff60bafa75b35f5b0f0dc3166ad176c665dbc780e25be8884119b63dc2d5343e171eef5bd789596861be80ee96adde4efffd92325c4d2fff92fde79baf14a633d40cbd49ab9682ae9a33b53bd0e8693158ffe0464677ec4e0fadfcedc633508be32c1a3c23e5a53163e3434bdf9e38bd69f53fed876032609eb38d5652a85b4e0b0db85d3e7f5c5a243dac7ba0ea539af362b18aff205a6620ff8013681fc3838b9be8b5782e009b512df6a311c3deb06b0ae03c51cfa66379fd84936d48b57576a010bec1673eea1a08e59aa86541c6d1b7377be7c224169318f550af7f9c2e2a236b7bb4c3335975319141c018165f88ab953b54b6be0ff5e8e041b118c35949809c423d207384e6f597f50cff7d82671751cf3b54e3be0796278923c6d8d1eba4e4b950e3c7106a91359097e13d13466b24b2cac26e82829135ead0f70983c7cf63ae382f0a94971eee996d9d308147debbf57b36f558542e84d4d27badc70fed9810a7095bff446a1a4e400710a13e565be4285389e600f7807104585c190ee5d359568c3ac3efde2b9cea316130d7fd2a34e25a674e40c99f103052af7566843129a10c788d35a6bee5dc2bf37a31e916b3833854c501adb7e32299cd98c1d38e883dfa72b2f6c3c0b391848135358defb635fd4a7d76bd086160cd41b1db22aa773aadbda72774bdc22e5cf613dea734798773a208f51686db70b61be5625d53ad7f1c66b1dbbe7c9240b12536ff2ab91bc3515a4a33caa21e5199bcab32d795c5e9b93985bd99f1daa2c78ccbb6bc41d6a866f569bb541b8d135cd5dbaa2fc6e783b391c9a6e29"}, - {"000000000000000000000000000000000000000000000000000000000000000b", "000f72279dcb3abb7879e4072acde4a253c658f93912d41c7c01a4ad95d9bde241c14b335c9ff8bbccac05fbe515c44e2485eae2823e2eac8695a672d15dc111f6e0c1344df74ae281c3521f768bb5b2577fbcf20acccfa3e60d7cd162a6426136ef375a9f273c408e13b98f1724d3f7e7829da3d30bc6a38968e44ef4330b40b214fa06f629264526667d78c341b5f8f61c331cd35430c8e147a730fb21e2536f60f0cd5a5dbbe501e6c16b9d42bf01d5e800ecb3c09075e5ddb957180da3ce20e8977f3bec4592cc4ff65c9def5433e41f088c1568cc899eec79caf09b6af2d6118496d48cdd2c32a1db0e29b901adc92343224a18b53a5ced6d94102d9c1268e4c327d33ac41db63a5b06194a17044e30673df9965605ad2508b7e8a76ae3aef8cf7fce591255f58e294f69df994282cd86d3e2119b785e121c7a59369c54aaa477adc8697651d6c813ac841dff600c8207926c4981f1d03405fcc1c44da67e66744b4813c569f8b81504b1f40ca223ac313e8da1659f05830cc58d2b0b97617d94a5d3d6b846b3fe64d9ba4e8f3b29959c9b56d5e4e61d494079789762be21b788c8112d9a406dddabe794389194af5004b9698f7278001169933968522e2d568e07c5706101ce7558b74b781b9b5d24fdde87f7a5b458befef5b58f6a197bad9b27317e23189fb5d9fb7d2307ca32ab81f2d85e812f0fa89e711d49e866bd4682882335dc0e6a98f978ab7dd0dbf86bee4bd7bfbe89692c4dc90efe437cedf434855d085b9b0f4dd826d54f456cb0aa349873e7d234d8631cc10faa7abc6cf35a8cbc371756dbfcec0b18cadf98c5923e119cbca4bc2a3b7e6b7d955fb01668eb1b73b0aced87f59249596968e96edcaa3e95a71f944e61bb49779b99a97607b4c21905f4ad76f29a31bda5ea2457be7542557568d9d6f9995a6f0c116400897c496db996bfd015d2bfed9b51a67559373d8d33371a9e5b601fcf4571f3965fd155529aeede7c8a07f436d9c58be0307eb722b3891b845a70c174dcd23dd8ca0a5f34f1b9e0ded52c32daa005752336ba4f290c67dad7370723fe9c454471dcc36a7ba1ddc7f168cbd7aabff09c0bcefc7994f8702d36afae79df06480a8c1f491e9097d8650570f65239657934da7d5687e0d6022ee5a289ec8eaac5fbed13d2c39217fc8f034be25bae7066d5b31a614756a839ddc4cef8ae5b26c3e2842d20416dc2e5e3f9d7fc43e5e7d4f61a2c0bb0f66a07524e513f380811a6ece9de9d7a1f2532126edef15c6ed469e0158389e6cca63f0c601d6889169c3d466e585a99a826e29c84a2e599ab32b174564dc57af4c8335a3bbfc1f7f190e3fdf24441f3f04d1a6a5ee9efa0b657f0baca1a8b79a1caed4efb81cd2ca89e2246f23933a8cbd89dcb7292be303b185a7c059d7a56c0e0d9ad4367e0db240fa7f074959ca80c06f07f4723ceeb3acb69035d3ed780798906974d7d23674e3dcb1bfb7cf77c34398272fc155d863cd7a0378253db7fb5e0fbd2efb3af935d4ab951938e5960060b6ddaf80e46cf38980a3aa0ddabaaf2f1bd8b45129135418aef00fbe90c4244a75852431ae43d77a809dd92ab72c58da7b1abd5c62bc4e1199fecd351652e2ee47c71cd961d9fabf5d901cc484b309d1db8be08577c418ed4dfab334cc6b080e0910a13c7524d811d8f1cbdbe87d79fd6fa6515b1501bde57285cace81095da64c7696ee15b2b21857aeacf456dc154cd044d4dabd3e6b564fbece8c55bbbbcd1d6ba16d8b3381f661ad17e9910818ec48473b2e73b39bfb6719f905218dcb9f7968e8997c5e54bbf59831d652390d195263dff937f0a294a9f96545413d8b75916152ca996380a9301ec97c8c3608843ed12baf555df4bfe4d01"}, - {"000000000000000000000000000000000000000000000000000000000000001c", "008c21b118c4cbb1d0528338165cdfd9d3bab17cdf14bf14b8414d93710508426ec1b6e645c7cf32de5303342ee0e2cfb227ca6e68f37c5fc79a80c11e1d5c15e323053a703995a5e01257d1e948491a9af052c6038a7e7c07877b2704e7b0774e434f8a6d6e3ec012428f97e3e6b393cbbf20664681b687029833362d1804c9ab42fa0b37ffd133707f7a9939cf30a81feff31b3ea35b8a13a261d55857296af59931d812b23639033702142a9e58b9bb5e277dfcc893330a351e59dc199a9e9501189039e51b6a1e5c5d624aa2d7df1cc819832e7cc3a091ab51b744af75e6c202ebdd9ca5b871d985e6716006856fb23737f87e88ab222bbaf346052082bfc754622f101595f172b70d4f2a30dc5e0f057e07875899bcef4c1782c4dfab59d9083e7163b50ec4c9f9a692ab47d18bc314524270616c3d76cf2610ba9ff837dae6933c034153946c521bd275ff01650243df22e552a53bbed742f1585d1c08dc2e6b89df213762e62e194a3533315256c1dfa0ea9cab9b20f7196abe015b9f28ff7deefbf1d9ed40b760d51b0864431bdd889ea828bbac3128758c53af2ece1e1ea12f167e66634edbbfd9b80e41c78e3a94bf39051d859329c6ba3c4551eb8fa26a8afee2fc43cf80b75eaab02169224209093838cc17a26b177f29c59be0540b267e9a7ec662e25193d9cf991a1c6912eae4009e3efe03986d281cc63e9930d384f12cf608c2e5071e84021aef875f0892cf66b0d6b838bdda0ca6225fb712562f7d99af2e6e3145eb98b42f04753e999244919109371c967632a1b281789bba917a6b705efaa0d80cb50b1e39a8440f7cc590f842e9be411b86553d7786641e7829bd3ea1ba89f1223359547b469254e47436de273365e33f93933fb14f92f9f0b0870515ae6cefac57512693169b0b9ce72778caf5da5b17a2abde709f01d2b8654482e62c958a436c80fb5174f783887eab2572e2e96bd922475d391286141e7be5bf1bb33fb81119ed1a922d184bcb89f381c1f4f9c9bba4b2f5822e2f6d7e78ce5141e9b839ea4267695297d0ba6e6010a405e7db0c13b2cf03f3dbaa5b4f4aad143fc5813b2e376d899e1b63192ceaaf75d8b3cb1168bec95f135696c322e62a07c9e2b139be13d24f0ac49cc4b24ee21c3e70ae2039b36396335eea1e6a03125fa0540337410c1b492700d5fd053810fdda71c88ede9d1207af7e47451f2854fefb1092462bc750b99a73e79e1b1c756af3157175e15ef4f15a7b177d60bc9a24248a835d3d7526dced758549cb02d25602e8965ef1a70979a31e8589353b37f873a9a5266259c10f6f7daf1f64e1318f4bab128914b1fa553890f5cfaaf173d00d16adf00dcb0347458952b74636f9c17fe2754d6430dbbb32bd53000d067f257a594c06d2c717967d7e02db4451821e060b29204285b5272a8744b07a891e3aef04d1974f1995f6c2ba4b4fd994f69ca29e67f1062a7def51c70edcbcf53615e974ba3645559b6ca03a21ff158753df395384d598c74a4c9a89309af5b506c8b5a0db8229caf3d624957ba67bade9241765b315edbb6fe457ed33529521e4b1c1e31dc2491e05dd170c7d7d565178e7e39c51d7f14b0dfc79dd940d9e1a2724ceb6d7947f094972fc412cac5615e5d3b35206af049bd115fca9916e15c210af78fa609494e3f80dcd2ffe4a5514e5d18250fdb0d43cc77d671e0a9315f2b3ec5c943a2abb61635978d5f026eed7ddfee44c92acc4d660e589510f989538ea96fec2d2b970800d46c7651a4ad0d6b4e562c4eed566226d971ca70627e047f82b935f831382d3d97440974a66f4df52f51bf47dd53e25f0b53d524265827766d19a5e9a9eb91eeb039fb988e23a7ead242055d7dcb1706eeed6bc"}, - {"0000000000000000000000000000000000000000000000000000000000000005", "0008b49b3508f49d7672d280c575bda9c1f7ba3e9510af18dbf7477d17919b92a06f9a56d97c7d0da97e20571fe3cd59ecfbfbd8a7d96bc306aa60b1fdf34537fa15a2a91d9f511e89849df9694d36860e1aff54056d229fb91be979f6d5561d7e656a9fcc5abe94e4072f457af864f7554371f281ab5d5382a60f372b840a8c960ca1d508e3ff72568d04ddba55a88436e7372220fe996fd8c880d95fd5d5467ccd65d7d6b8fa62026a08e7f706a59fb37fa759476dae5e80427495720cfa241b130baf37c6587357b05b42cea0075554ea0cb41bca4ece396ab90de1337226aaa112fb7d1d420da1762f110aa4a0b22a51ef336304ee49bc5b32470ac00365d1320fb1a1b081da123fc45107700897a90b31c6e141191e1555c9a494cd77e8d202113a8e900de40cb2591f012974ef65eeed6383c6c9227e64e419f79a27200d5b1ec2f958faa3d6e81a5aecfe5b3c01e1786077f4c483ddec93ac272c5eb1a77298f19943fcfc2c462628d7469faa03a9deb992df493a829203495c5755b65c53be69a3c7d92763559d36da8bbb24d11f850397acefa8f852d67a1a0be92dac1d12550974d2fc315bead1c8a832baf8d6ddced13bbcb6ee0fc52b84c8505f45008d447517ab99e9d79c92e2e70af0f178f360646f2a349837cc6aca331959b8d1b51233639fcd185777b05e81c319e1c8a4d7ae6d147715482e4c5e1a04a77e7994195baec8fd95f3d4e38b205f0fb1175440479ab9863947f2d952345ff9a67a1ee1ae769dd364c99b3c94c5e5bccae759213d88c148cb6c1460a24a0530e916755158214aa2ceb606ca20b817582e4cd578e86c2530cceac1ea8bffb564ca78d097fb4fb23b1fe4e4a9cb6859c5a36d93bea0d624ba1e1bcc506fe58ab3797afb63621ad5c7bd02252548141012cec1d89be8e3283bdb5828ee96ff5cf80076a0b658b64bb5fcd9f2aee7e1382e4f14b86df900ed31c71d616823f9320116157c067c9a548dc047211f893f64195853d7a355d6e86f271dc9e5da54b43488a1d2ae1370c09f4bd55263cfca6d82db1032bb0164f27b20c336b05cdf83afccdeb7cd6fbc7d69700c20bb45b9c947c0c10e633428b67bda522e37bf2316f322fce0913fe6ad0d04d073adf18e24ee9c1dd02520e563738c0aebea04f26b732bec509d4dbabc8d0259dfbf2eabca8b5e97b11e1b0b35e6f4c499ee1d2be45542d856ce39ff454436e53fffd169bd516f5a1f20494a19c7dae8fe81b279c42b14826a3d1984432086a6fcb28b4b99f34d47f59bcc4ece5999f7d2e6029f71f8574d33b93f3613b0ded36795c5263817423771ec802d8fdd4b1c7e23ef63781f8ed3d75e781f0971e7a783caedeae0d2b556a0f321a993abfca7571a375751964e37ef4eecb3f4fa6c81ca3a051927a10343e755f10e6c6acb8db078cb9ffe6d7a8273a878146670cf5530ed33f6c623d305d5debb82959eef970559a70bbfd50ba5313600b847f64ee1887512d7d711c5b1ed916ab06d729aa3d82768a8a4fc577869f919c5faddaa5a059dae02f21330d996fa4fb8fa63ea28d755839113207bbaea85c9cceca9657fd59389f1213ea4c1248a7f5f1b5f52e5c558053a2c657d194f270806ee84d47283ef1d72a0436d6b9d503f4d41b107455c62ca9b3cb2f493540e2ef0a149a3a357474512d48a052c452b48de1fe1ccc4219cb51f30940b0f2296d9595060c07139298239bbe878208e8586bd7935e094a2082ab135ac7cdd83a7f28b37a9061f8c4e0c3be63ab3cb6a0b8956c471c6757879320a1f81cb0e41867a33ccb53477d922170f6b1e19d8b3ffdbb1133b8d0de13996d3d1f3819bc9bed78938a593d3e8228b9b9f7a49947d80e9c4f10c59593a6c543984e3"}, - {"0000000000000000000000000000000000000000000000000000000000000436", "0005f1979ef84397e453e2042a228ad0ddcabae0880f51e27d84d79209d6f3d36d77f284d57e2e1741d4019b6edc05c6ddf4ff1fe3d2883ee09ef0ffb8efc656bf3d4016e2c407ab7d05b89b3d2e6a387a78b2ab0e436efae23741d3f661570e2f6f77e28fb43d1d7113c1ba11b9262e3d997a12e0ca609f4dbfa3f8941e179c3c25a41336538a709860aa5aeee38a36fc9aaa4443168d5ed1b0bfc06da4f55e72ee632a7e3af7590617511c220cd648ade47201b53c687a1fa551031735a086fc0375a3cbdf2fd3f5c8eb6bb2cb1ade55ca072773c2e3aa78b96aa8f1bea011f70953ba1a9ce70aa69404863810d3dd86c12157ab83cd384a508ee00953f476971b98abe0ff2643ca4395a9c7f9f2d14c0f76167ee49397654986e754ef7b365e4b861c20290cd7f38f6b55430acfd8712709fd221f12da1a8df1130b9e7d0c55f37f6cc2b13abf7585e7b43ddfe93c0219c7715fdc3ebf5e97464c80cf789e1311daebae15932e24e9d6eaa7926e960ea1d6aed705d35c6c011a45f46a0390db750682b4106aadf811b3ce38b9f15d3172fec11b6ee12bc48ab8166f355b0c53197cd60c49d0742fe48f7be106238f017507fde1bcd944770f956d7cf7d5deab4896551216c12cdb51823f8fc2195fb17b51a33f151d4fe39bb7e358e67f363b1b162391e1a9438a016d2ecb33b2c5bce5a5b6659e31b508444e4b87077ebd4d0f11f8b2fe3d2dde28b02a5a27139c50654a5b67e1528350a0b9ec797d279a4ecb0f890c05c8a253a7f9cf8441f2ef5b39548b12745e3914240c28e677b5d894b8880968bbff9f69bdac700bcc4cde1204d7c6ccef422215f2a8f231099e936d242d1e9b21d05d43e72d0649c6d7b8aa0a7877703221daea4d02a0568b461942ee43da05711505be9de7285a32166ffd7bfbfd5177b0c3e0205a0974be58c7001d4a8015d04697df6356c317593422228ff1c5e40b1e67a2506b6fb5a9fb5109fa3eb9dcc3ba9c0ea30f8969c597ee7b6d8d5dc8b999df286f81175fab9f1476c7d20e0ce2e38205315560b2e39066169b57b30f92046829e0aba3bb05f1ab300e8bb892acb6fbbb24bdccc80fcc111d02759b305a6886fb233cdefb2228c04c2e97550dcd2e3172c7a7eaddf1c0435661f442904f7cb5997133ef6dea7f1f78ddff55debb8857050c8d740a5b28877290f35100cd096143292c9bea39c86bc2f5fba8f9f6d8846892402ea1649bd70b8b0f51fd5a1e07d0cf6f3eb5994f6464de8892159be31bb37254214a393b7693f5a0c65f34c6ad11f63f651117e3d0bf0f711c7e36747be6d64ed1496559082a6c050442d4ab901772ceb7e24cc2a4a6395e923b30163ececa2c959308e695d4260defabb9998f36d4431ccd8d1ea70bdcf1db9b23a0ceefc103c8a01e5d470219513cdb30b7fda48e02611be3f755877def8bfe0b3cb57b1e338395ca6620b3fbdfe3c5049b2f381f0bc8a21c2fc85de44c0bb198c01acbfd8dfa3dd63e1c9a0194984d28535232f693737e4df704045d53490eb4613af7a8d55d5284e36cef492b6a50cd7bb3b61a021cd72fccf6f331dd7319b63a8125b9fe16c68311e65fc2e48d78b08f26f69d8c5a6612a092963c263b990a3d28743b6bdb0e4b6e2d5f8297a5f2bea59e096d93023f4c322af2de73287a4f28dea6def5626c122a99ea8a53183d763d51c926ca8b74b1bb388c921ec31f62b19900878e81baaca5e3f9c3815dbfdb5a2ab18649da1b5a891d277398165d51df5cd6dcdc820ef847d5e1e0df5f8509d249505b67e0bc37f3e3a7345ece854c926fa59eb7c5493ce9146ba25ddf201811480a96ad94c28d1a69b5799577c3b16a531b73e718af3969b06dabd7c93e82f62f280741cddf7df1b5"}, - {"0000000000000000000000000000000000000000000000000000000000000012", "0079da69eca3a8e3a644948e61303a5762b03d127c373ed4630ccef73ce9d338a3effafd963538f5d91100ab56511165c673f558b1aad60e0b60872ef6435b0eb88a7073b702cdbb7e86cc85ec9172aa967b28fd00d59f7f52c86e8672a07135760e4df5284250b1841c1dfbabb61bd95b3d2192e35a57eba12bf5316e3507e362e1a52ac47b67e3b55ad5d120f630b6b59df912bc9e28cf8f6ad2ee6367e77944deea50669d642501d58ce5c487e7297a8970880514dd1028e013643c0631e10eea62ad33f892f5546dc10d8584fa59f8a6093eccc122c5e777138744695162b159fce473ff4c139d4e584f251005721e34d068e900f1a9e40fa92312206ddd34c9db14fd5967eb00d82367c56c1ee1063712c343edd99c89e830e67419bc4499aec64fc18119738d5abcad02a599ce51e6d6bf3d05938cb39be96edd0f937c39606ddbd2aa32c2dbacaf5f46feb51203f1b9b66b444383902040547094d9ed53503bb7e919b9e7521e20fa71d188b46b3047984327b6fdd7fb19598968c18677e5541bb1f3e24fcd0d4638f00ad24311231fe867922dbea084d34a4101b6e750f9127a0bf2352386cc665af9302111c55e147d6bafbc49ee3958b48d0b6dd28bba9407f85f531a5e0bf1bd05bc0dbe80b703a413639cd142a69d321b55b5d1919fbd138cef7a728acc7776915a043b55940e97939b0f840852ecf52944f792ba84d0b276873e8f11e21a4618389326350c3e3183f795b723b7d7e8ce97637e1413147c70b47888874a6649233f7cd5b361c1151867023bca7a7e87570c3bfa234539e3b7a5d650d01a4bae08f49ecdd89ce7a5a243255c6bad1a06181f5811322007357171e8cc15d8b072326ec644c6d1531cd69f0fd36e723e5e45bd240431dd05c8b384a74678e9141f4e4bd957a64c59d7cb352bf974f601f62a7b32bf007ff4bc85d8f59bd2a1610e7cbcce3162d63d36c845b3a6f0f6658cf1f5b816d466bedda1b8d056a015093a4777fb5ea0ab03cad632d1698bc65d96fd397014c48d909511fc7bf5d8a70c857b320b4f9fda86bf03accdb104147b515c1b525086abb2f19e1394669d4de72cf8be5f52ff1873551d82d0007235233c3375084fb88a53e8b607b4e56149f3903828f540b006f01fc95205118c3ca396b58507856bf6a18ec8ae7d6f00a948773751e539d19a5438f5c342025df7dbbc9212c67b182be6009ba211353b7550d15b74659c768b118a15b4d7d1da67f21872d50471b54d558cf9f3142249c79e38288db3df25b9ed42fa5acf85945da4eb0147f00a588d1c6aa717013974d51aa05bc868df3605673188034c075edc9ae426f6c38679cc24bd5e6301fedc52c5d33f238463b74fd7c1054a322c72f11302e0f0de8d7520afe484c65d40f8ab4a84d677fa5200c01ba44e8117bdf30ce4aff2336f4a199e5f6cf3306ea73233aa6a0198c5a75c766e8d76d04439c7300d2b49014f193e32e1ae6753a0e7895a253ed88ce7135f3d403894ec399e4101716be9de54a12a55d29a01dffea96101b38b7de783c0f0654f3e539afb14603fdd0d6f2a8ff9a5f220e4fa2965c4ea8a1d1f49640b239e614cb0f055cac81176bcb3adb55b2bcdbd68244ae50af0aa7f5b79429344a928cf71641c6d62e40825ac33401c8749adf850e5a5b6b17c3b2762281e218f951980d1c883e40e4712c4e5b0ba98727c655d1aed46d693087587f754dcb0c478e79b40d4dfeb1420f9f4242b960c79b707650cc6ec74ba62ea5b0e8f160a5798fbea18d7c7ca04fb8bbe78271dac5997fc320fc6335f65874ff35b9331a2def7f99744af87d343001b1d5b54e8ab538458966cc96bbb1329ff869c51dcce9781de4cb144768523c1ce94bd8203502510"}, - {"0000000000000000000000000000000000000000000000000000000000000016", "000011581d5e0a197314b23ed76975590c56d1bd6a074a30eaead093bbbfb9c552e8c7706ae11b797f1704b88b1c108e18209293b13939d46999c98cbb75a70fb43afab64af32dba76b1f1e4a2f1fd8ebc939d8204c177541aa7effbf023130758fcffcd42ec6cb00410146cfc0bd5f7b177f5874efe55285348ee7f2106381a0e566054c193510cc4219622c1913a94194abb496e2da8b694155b2b78a6719fd70581d75f73534c02d10985c6aad453b66fc2ec322e47e703fc5f1179294ba3c7b3adc1b584db32b02771e2390c9d9ebc320ea376e49c125fea9d90515959db898702c7fc463b1c8a8b44125dc867add343e7465068ad6ef82ba547078b216fbb084a6ed545a1f5cd905394b3d75f396535f1c5673530aba1db8f65fc41b4c225ef9ed77cc215f20d5dffdb429515a9d3ffdf2f2b79a8531229702e1b540d128e1861b210c42c7e58651e8ac77d4a47023fd932ff49efd67b0305318f3e526ab02a1fb5c145fce61914272983910a455b74d1fb65974e1f32860fe4b122e252a4a3674266e3ffc336c75d0e1dd3ec2c0e7e3607508b95a48e05c616d55e69f69d7a81120b20afa4994658a633e8c26da021fbb4efc2d96afb176c354fef0ea6774ff113d0c8676f55471259aa960fc26dcaaae0876b88cd342988bf49651e85bed78b171c1595ce50573fb4939c65867c5b2333a81a915b027c6aae1eaa136bcb0b93139930ea0e77019a3e56134a40e7f50a8b30e1cda8bf2ff79d6e345451fe990f02ca185296f537980064f4cdc4afb7109399466e16c795ca8c4c6743c9c2b4a644e4f025c8b21ffbae1869621af4508e1aea73023adb3abbc0aed99250f034ae3ec6d12ad617c6f678f9e2dd98d7a45dbd646718eefc8969d90dd5228b58bad9d778a2ac9c7a9f4520987e46c5a2fe5375e0f32f3d2810137223df73dd0295811c8748cc67a1a97295ef67b07d88a591754809d66f10db8afcbc79aac0eafcaa98bc5fbcdd0d7c03075f7c90367fb1db88f9d95de0f9431fe67cde1a1a5f78ea02c8a6d769e84550be4a3945c068bc4deb065790edf794fca38d88f2bc66176d2e2415baf7fe2568db4460755e17e801933e137c433726a8de632d0f07aee36f94085ad5aa53c8372cbff1fde279359e3267e5069c5c2eb53234a3b2e82992e15a452e2c7e0757f28d5b1d27535e3cf254b736a2809d4a3099060983d54a890f67969c69734aa0d637b2b44e16645815686e530686e886fea4217291a9643e31cb7c289d5d572e9fa5ae5bf7aaa2ca61a35ef6b6e9015861df09b9f96b20603c6907d145351852bd9de502577b1d24b8d2d16fd41631af65b77e1f75d14e8a823d160e23881543c46b8543868c126c22eee1da6eb9d67acc2c16352ba4ddbda3039699df9dee1b96b69afbd6b50305461ccfd7c0ebc7ffe35e169afd9f50ee1b4832122bc77456a738b3518db269d430c4da2be29d2f660d0b9202805c4c7ee53024580ff79c1d1daf0ac562145813f1a60b76d983a4e2ce6a7fa962a0e4ffa2e40385387365c52d9058028058d4411964a361f4808e0bb8c94ac3613a457a5bc0ebe32f9d20e9fbd9507313be852e989099589ccbd3956cc7909a6bd6dabec928451333d992a795efc6d8e25c59621e94b4d8af460319dd37d42b267598d620a379965076c3d35ac62433773ca08cdef207be59c7c688ce1bcacc1a18558810fa215b04509eb5dac761db8a4c3ad10beba9e39011795309ac0fc3dd417e447279e889e9eb8f5ed4400384b98b09e7c7699489747933a78de5351ffd71531422b35d92db078ff44fa7108353cbcdfa539d62b703faa4d8cc87a323164954067724ef9732bc3fa583175f9b5ab0b50119af9bd7a61847bafb03989f5529"}, - {"0000000000000000000000000000000000000000000000000000000000000004", "0051032be70cfed8bea63361ba582eff3d513f9bea66145bb17f376b15d525d86b757f04331f00bd701801e1687d2f47989b9194e04a5c571f144591e5dbe109bc4b31dbd859aad2cc59aa80ef26b3016f3e41b700ac7b49ccc485bb86d5127934e8d6e0d58b98db6e12b90b1a91e486d1c2488b740fe448ef3a3cfaa1230621a2102b4d8065cb3a128275e6c7f54b67bd600576aa2ccf7bf2f1a7b028c9fa82e7cb570ff538b019008df5759ca0d985c9a425a9315e455760e91eb9c5207ca6f5d0aa87f1dece4377e2d03948e522f9a45901aeacb71fe1f22334cc0455507270921f8cd543aa06dacae06b66210fa767b56942faf637c1083f372b0e78cb2c6ea335838c86736038a5d9d703e87b1ca61979e6d09f8ca948fa15731b879d94c5ebcb99d8df1420f92d075c36419edd530905b486550b65b572ac1a514b2568e1fa59a6b80234452cb2aa8aebdeb44e025a990e88d3dcc2a8e3603831bcd6887997709a0f3e5ced3eb7562b64fa78853c3ff8266e54a83fb4aa10509ab0a430d9a1dbe7f86aacecc39ea45d1fb4e1175026886063446dbf34352e8842343bbc83fdf4230614c1dcba86150ec9c698b0cc6af8c2ce47dc4d371aebad5bf91018b3429073c1d24727de1388b0e3ff14d36e5a741018cf9ebf015078ead152c76bf8439b4ab26607e9a5809f4c16450ae543b695a93f9af85c02efaadc59066421c682a290fd3665526acdbb0f1008a361c3d7a7fba5f94a43cde2bb5565fccaf166b91bd602ea4bda0bc8f09838334aecd38a53f7f4511a479747e30253522f59c917198a62a3feebd61f6b93050be733b20fc7bd26f2c2dab03b14d14b93907aae65184e999f99c9add99e5a4b41fe2072bed95663be2cac3f761f9a891be509aaa27f7ce36b0d81bce14243f7e5b0945ec9313160e55fcde6851f8cd49d86fb03d7de5e49949b51fc81b0719672437c39fbb2720a526fff75d2352b63d715b52bab3d7171a07b1ccd9f08dd60d33f4da5f8da6f119acb21ec8a4335bae3ae30ef9785384dc2f3024308434145475a47e7f7a3f5056e3178ceb51d33c974a439e8eb4a4b2666fcc5bb217ab511c0893d6eee0cc58e1e40ca01f49ffa726c057c71a1d0056b7a3ea380723e9c93184d7f1d1dd61f3b9de786967ce9e544a44a8ad996255b28eb99fe062f286564dd6523de0280efe38d1460f1391c1b844b49adc723263d95660c35f381613b4a051d9349f216653f29738fc1fdd1e5e3e18fa2e26e2292d908bc3caf7db2b8a348171d62e69395393c3a9263ff02de0b0ec8b619ca0bcb29c9f0f160ceb811b0288d87760da70111584b9a2e70ec73385f45a8ea1f6778a8661a6434594929240bc7cda88f1362d4de48745e325c338c1ac3bfb0a4d9dbe6b95e77e7068aca119872f803de8426fa0e5a01ccf8a1d5f6634fe9ab154e0df709f1d34d05d9f4571bb481203a34ef6db2c193fa862a05fca3afb1cfa9beda34be6abc44e32ab5db1ce133f7cf1e507134f3cc40267b7bd7d3f27529be597306a1f5772cc312e55ebd01c38810dc7c738c916e8d0b40fe6501dd8175e2cbd2709242cc80a7b934614e0e7d8c51e589ebccdf4e431b2ba57960d1206c9a311b6a4e29f71ffa133a7ee2ee2aab3da92b7d56a7c30843ae75ed5b3653bda1715b7cf648b227b1b59747320883b9e0a4f68f9b4937da73f1972ee0d7d8ac3b0cbbdcde1c45500fdee380fc9d45a042747c1988a42e8cf7646499c181af3da3a3b6ec1151912cf6cf550fab20c0fb266c09490a5506f2f80491c7c1ef63a43257e9aef0de7555ab4955f3b43046edef39dd756819a99c21df922433b11c8282de4629954f096bf4a120e3cb42556dbbc3d99ce478e95cd64ee92cf8f439"}, - {"0000000000000000000000000000000000000000000000000000000000000002", "007f06c9fd40e6ea745d12f8504d4269f7ce39aff91a88de03be12f0055c4584d3ccc8c74f0bb319f4f201adffdcb113acdf198910f360dba5c9213d37c4c119ee79a22e677737e4dfc1b5b7f3367e4c3d73502614ae659fb8c8b79ba756d839a0e847965a6d14f6362bbf64be651b29e9b71f95bfbed0f8aeb95e7f861a2e4bbbb394d9618521d9c5193b46e7ee7d0776428848253bedec148c5caec43a8924dba3ef4930db5e1f0295c46cf30b08cf999d8045654d7d8eee2818c74d0f1804338d841457fa45d1915a770005c0647b6dd316720d539cdfd771276b835d1fb9bd5329e99bb7bf4ba905ab0d6036418688e7627ace3c8b79ed5ceb611154ea1a1e890c4d3111b5b5efbd2dcd7c554fdb39405fa7c8a5d55115ce377597aade97fb564d3fe36941b18e893850c65120e1b5511cc5389dc804959206499407dcc5a46da54d82b5b268e1b33655227692cb00ef1defb633ed2fb564953a4dfeda2dd4d22eb706512c92ef851ae043eed65610c23734de828b3807f0127a04fd927d6247ef17e6cc6165edcdbb0495f1eb1e0c15b16fe86de38627a4e9dd61a811e628bb5c9b01d3cbfbe15cf90de2543171cc8e6210d2642de184137f6495d94af0796eb7485f8dc9bcb37c547d5ded0c108177d60fb06597535728565fb5ba1f6d390edd0cdaf8d231902b01427363f698d6f85a5c319857ab0c0a6d4c0a990745e52ff40b0073d6d110aaa899a30fcd4c9ecae6a6238bdca1ac11e62a50e824b5b1a32e1092fc78380103d3f4147397a4864b2657dc60b430b57458da0d7376f99cf4f5c17e686a3520f24f460d9f32a8ece4b471be1651f4ba6b2b7cb7132f3833136a16ced924e6f3e7114594c0bf3ce170539f3225175dc8e6f450856edb57f2d1ae48917e3477fc52911777069ad2e2981f9843a2ec32a05e965c797d521e02100f223a12413da5e0320fd81401808f64cb039616e2e3ed8964adf9aef2d353b76d60f1760c96df5f17479534f330cb83b74d42651b9dc54d000bb8aabe1c7b47ddcd8a80afb29737900f7aedf3990e5dfd700385683e1d8193028b8a914c6df325b2b892b74cdb107a22d01ecee813d8a4331328d29746582538618e146d51d7ffd54739cae6f351c1b75b0e748c174b203aa507eda31e720595b0f4fb27e6b697414f3f06ce061804429a99747b3e99268d51d9dc1e27897e482b21228c9fd8e72ee7f97ec73a2b7258daf31e3fefe8140b7cda1f1012df022071526e2f6c30cd06bc700e2e902bbf1e5274a72a1e738c0964acd0fd1f9aeb8507c0410f794d792d551721632bb8d38e4afc137f1d0b8bf24922dc2abba459e3cfb11ee2b5c2e50e51181621438ce2ddb619eed8c312bda500e19fa54d0999189192dea44fe48fe6121927b773f3368e5b7589f4025c4f46f8e7c015bb517505cae93305f4edf6b9ff1f298a6f7464efb94603d31ff7ea6ee6a87cfff9a40e0eaf2bf58e72d77b9f61c937e00d9109737e2b7b218cae3511576ccf8aab027d652c983d080f388795085c577dcae058c9df2060dc274b3985cab75bcd692976f4d0f0300fe7ce12ea3d2a56d1cb0887dd0d602a334f48d35849a78a12246088c02ed55749ba99a240800af023f0edb5d86a8b08f86bd7cec7521878f107ad338c0ee0025ba260111cfbcea6454b497e54f41de5d3e7992338c7ae181523d2367ee700f7fed6411e23afc40ba59f63365f628c0a712dd8dfced01c8f2ec3eb4ae795c94fcff784e8ddf439c235f07f2fc00ac866c2525ab165902957bcc56d6dce1fce950ee0380f6c86ab12e13d4dd105ef423406ea742878dbd61d46466fd8918949015343a4a71d27c179be6c706c2716c46bfe2dd6eb99a3a423c53eedf537e793c542"}, - {"0000000000000000000000000000000000000000000000000000000000000012", "001210e9cfca13c38157c0211b3ef81db65038d98016c8e3a83061c5f7cc9d7537c02eebe675bc79e4c22a0534d4a09018b79d2c63c4304b5dc7300edb7dd343c31f835ca83753ed5506a4674de55aca437aee7b01895a28c1556eb5ee94169a4f504e120cf736eb6b299e3f7f710bb31d8809f3d7b5d15e1b0119f9a6c323417ffc2d4de2fd3e9412a886f0015ab7a3fd9a7a34cc5b4886aec3a1bb6306a76cbaea4dd748729240010df3609556b26f38e75435926ba19f47969d59e72183b51801c98ecda2b803ed012af582544899fd72036503b4304dbc45092c70c5710b74df552c5d0edc40684b3be2a5daa5d1ade641efbb4a322275f1324a0bc5d4982e4ea951677365e511596a1da09fbaa4d735a76caa5ecddba19ceb04962efe9362e702f797b90bcb24fc510a8e1bd3461507e566497e1d18d6a4e9337547527f9183fbcadcd85dcec7873b50bddf14ef05d678f76051b2c37c04f095c5eb4a9036a559d620368b9a2e8415e62f479e84f54dd091e1b1399b165d1812458f5a87f003816782a551515f3cec46c8e1b139ffadf944619b3955d703e6f6b872771d7a18fa4108946b166411485aacab520b7550fc91537f92114d18ea7c18b419f4074d184687cffab87b58b91dd5110bb52540e990abe1ea2294e777d299515d911025981f64c56108abe5c1e00e32940a9c64c0f35a39f9a507e7ce16c684c4fdd344f1e13919da556596d5f9352d9445beefd70cb1b87624cc37ae359d424fef570f29554353bdb83781db17e5872ae02e5aa51ade84c1340ae3c983e311b36406170226488bf2c284ba0e6923abcb1ab9e40bf72446341d2925b5bd5717dc4c9e43645a7a789f301d25c2956c03d6a5baebc73966c53d8dcf891233d3b9efeab4674fcf106eb3bc586ba66bf2e36ed3a935354db6ca478762610ecbb797ad62022f78a172059f6b2defd1abd1b4d0c2a6229e635c023afac4c49b497b822a913ae7be04a48d3d0885e61bc8f7d7a18b071be5af9258b71a37f1a1bcce82d34195ccf15d75de63b09d073e944ec8d21b205ec90617b72197041098af0a36b49cbedfd631a03bf70dc329c9174e0d397b29d6a8268b3c5029469ed77e9d4e2205da3ff856ab2af11ee83a9d738bf6421dbc63fe691f4edeedee3bf1fe79881bc2e5fb3eda3b1cfee8065fa4fb3b6d5565ccb6b0a592ffed920ef93973a606bf2f77b8462d01379020eb8a41c0654fda1b6ab00b46d5ed54a539fb597f92eb916fffbbbf6dfe84a223275fa1fa8a8f4ca4b7d5865156c5ab68bc9fa6b00c2d1c38dc962ce4c7e062d0167836111f1449e20715344526f1d70d86ddc13772cbe4d49f34ce3d32c630154487ac65c4ebe5cb1307fd63dfd548b0971fa34f824b4065fb95e9ebf5a91efaf02a0a827bfb19b104ed76c5e107a66d42c5547ae57623e93b6ec9e47911c36e4361d8de1396a8d4aaae6e602aaf773c85470af8d43d055f0adb9e88d1a7b37e1710fd4974eca078bf9d55109ff579f77b779d453de56332fbba360f06959e4ac2e6e8abcbb81b4f12fd710b08065e1fa70d2d18f97593930ca188c16bc7f63406593a13d1a6368a44d19f9bed8ba0ecf62fa2ce6026a2475fd5413e79a798fed7a40743d59410527b062e0f83718d920aa938c2d6ca3115a6fa52de57a66825c9bd8e5dc4194b48e2f50b1069da4493ca4a3f18af5f271e82931cbb38f730113580963d06c8924552e27862d71ec432f89b17bcedf39f99f58397ae78328a7cb43893140b30c218708a21379881d400dda67181e2599bd3b8197dc7826409a93e9cf116ee5fc3609e43ce98d1b20f0ebbc10b8c7f39c58513f08dbb206b2d145a45771232a1ec86973170d16031bd0cdf9464dab3923142"}, - {"0000000000000000000000000000000000000000000000000000000000000358", "01c238139398c8a6f3e0c793c34f437a675e186ca22d0b03c6b720100fd5a2355d44b2260ec3313704a40df0b1f5f5f90c61d0c043a6027b8f664878fce82b176226af6c980ba714f5d20663492d423e7b520c99096a2def500adf1ea1321177ffff16d1acec0e63761d314d7cbb5bf934f9a011fbd9336b5d64f87269880dfa196930e419c793f4e14d641ce5e47aa3b3363c7474453d5ba292c97e12bd01446bd96bb3f5df16ac04b5a97057d346dbba00364db2bb82a6cc365d962309b53d530f0e40c1a86da26883e710eda6565aea382cfbdf84634ed9e70e38291dfd6acc2b2fb71a31054340fbdedfa2c971b74e947eb94f6e393a863556c90b3dd7024126c41140e0344bed35e2f2c49a1fc259177cebfd7966ae439deec608a037a1b5c94612946b19bcfab6f78b42451d02b63727417105e528faf6861a57434346391863f6c55242df7711f2cac2b7575b03742f93d3df08ab809311675956dbb9cedbb2fe2d069e22afcc492edb9042b14fd897a26c5c28322dc116ca454722d11279f44437c9d9585dca7a677725b32510ce39e353e88bb8f698dca46958e36ccb5c6063067fa69e0c08a04c474bf721bf690a92bd681d3ecf0dbcb1cd2d4803d4fb12d0def66f392b634d7c4498159fa7cea611c7e33871449d53e1fecb001db91e5c2fca345d7423287b928936c40058370e5073ddc2f00ee4066507f9aabdf17ab84941e369e223db91ffe36204aba7095c50f70269ca6b377384f3d3321e9d862d5839b29fa693a5d01cd2f949d7b900e7ed539e253c9d86af82f2b145f86c8550192c61f197badd3d7f0f9104288a59d7a1bdfb42bf9958579723aaf9d5ff1c2fcea21089841eb14bd52cece703c22aee7d74f60fd47dba0e9182772ebff2a39b4bc00ddeae9c0ca745ab7e8accdc32736d71859df15dec1365eb3d120603ae8e94e548b6eb9edf54496bbf5b8962e9f0497942b29fffc15afc10fe242bc5d2fd19a30d911efef606f51c31901d7b3fa3d5945630ae64c292c19676b2111a4b2280462b5d6444d299a99f105a08fb35fd900dce03cf07888b40bfcc12c31e7a9a3d24c7b9590b3408a9df714e6d63ff40235fe79ee77571f09ccd5d16557be7530a23967ae0b1e1815b0bb95c433fa9ba3368a65081e6780b6dffe95f00efa76efc4efad79e04179a11a8685097867f50794e4de82ae0d1b86cbd10e68d92e77739bdc4cb2345eb462d20dbef8d23cf26f92ac64bd1d728e3ec43cf7bdba962e4b2178062298b8fab4cd9c0c7835d774f5e59c6fb19f85f2f040a71466ff8acc403be6680ba1038cd1fe0011fbf2720c07faf4aaf06ffdf84a600003a79b668649af5a34736dd6d03592ba9aafb84bfccb51ea1cdef7a33cd550a3766c19877b140a7a66e484fb9ee72b0ddc59f055298e1adb2bfb9ed5ad2fa021a8a462e82b4bdf036b1f4211e77bb3fce70055ca3cbfb4e082e14844d4beec4b8739b66b5f4da991ae3ce754e9b4057fdcb67ab853736a4fa77b374a6ceb1504efec92236cd490b5234a3b51d813cf37f35316139a92e49961a71b63dab1a1454ddcde1f614357006e9e1cd87333c2f8c16861940c6ac36836c4ae445a3c280c60d4872d70e17bd3a9f9d996227c8bfa47c3edd136ad8475947e005bc9476f45b6ce5fb2a00bf2897c09ea9b05fcb8b12c62ab0a295e1b9b3d4a604cf42e3d58769dd7861120355bb09de7b713ab1d5fa52e0bdb77f223d96df62dc3533f0a1c5c5ae5319451ade29beb46fdce3cc0b0269690cebf00172bcc78fe8eb9856cca516c453348ebcbfce1eab279e61d54dc0ebb6968f8bfaafdd1033ec1483df6dd972a8e4c706af455144939f28072b000a400e56c1bd08fa46573ff5924dcac674d071"}, - {"0000000000000000000000000000000000000000000000000000000000000001", "006e1e15bcb15dbdc47c815d44b3bd471a90de82fc4547977b349d9537622316515c6f3b85cfe5b66ff2018178a7b3264a3959da30a953a576207a2b9392585e406edd179acc035dbd865673b74f0a0ab1b1cf5b0357854e3056ce98d48a2127a89167985b0ab3b35d461d1ae6aa276b7bb8327675f17e13d1e863b7de3c072d2ee2cf9fbb596346056359dc4c1bc74f7eb5b53fe552ec7b1001db40b7e4e27da967ce6e74db51fe06d369e5920fe77f0741c57c84f1a556b2f1da6b1e2e5ad2cacff0f059c9771591247294664c1679e90407302990e03828c7de0cf3fc89eb3cc562f4b8cfe82d8263cafdd2ba27d6f9539dfb24d9be0e543598ed0af92c6acf9a61fdc2770616003fba6b3a645d45c944dbb6c0f0612c352fee653857fd5955e67b9d4dee1055379a8e74a643f965c81278cef68e6cb19cde9b15f3369c621733e7c7f8a62951d2ecb2bd8bfa200601111f20c2cc6d5896345585ad74663d89d87a3bc4099868519a9163b4ff08b405106805337b15fc7d4e06d57f0d32cb356ffbade3d2b6b527ff6b5d3d3006392884a7f8e7b1fd6294e713ae5f3f4ab51178543506fbb74e8ca76f678f693236bd56d98a17d7ba243609e0cd82564410a2710a82d1ce4e26714270cb87c3164c4c3164699fd777a2561bda754026873c9f12144011163401ab130f63aa1465e6580c56915b9b66030191a8d78858cb152aabf091a3cadb8e41a6d43b1205ca93825010d28a9da91223564af03a8a0ed5a0ee1c428ea66bd80302e82b55b5cbb157e98c97178f5c34c8268c85d046536f6f25e68fe0d1ad9fbb78e2ce118645179113e392dc8b12bd1f181009fabf78ba471d47d15f97dd667b3d70f3cf2150f0a118e93d7425133ae222aaa0cf352219336cd460d3a4e23f98e81d1604e6c3178f4297638842acb9dfc2299b878fac3c00c307b488cb117ff24f542459ec7781948efcac4b0f083be43084f80a5855984f976a362a1babf2d7fd383f0fcdd266d247cc5917b5a565d8ba1e5a3e3afa5e236b95af5cb63f949617ce237b45ea1479fc92d2074281e58e997748fc3081166a39bba8cbcb469c57238bb4852e933c60fdd492ce9bcd525ad2a757fe9c0d45427dcb195174db8a22d41a3475c0e6770fed5f66b4541be1dd5c45cecae74f18dc54a21faa9f0a8f121a6f71ffa0a5d942ab84e1c17c89a62378d2f7002c3084ef160f1e238179840bccefee9d367e914fa81df1cfacf64ad03d946cb2b87658778192d5dae75f29a2dcf66465bf1791a9a2f6babb17d98bccbdc002133294bff267e7cf9d4198608359821f111cb9e7a9351eabe789571cf4f37b058e32bbf21db38ffe32b514756160f1d07117f13c41aa57c7af9c9a0dba072f780aade5372f0ec9bde947a68b49dc224ff19fef9b00d2b759bfc8c1dd6af283417844b46cd3043f632108330883d5b15a339aa28443f7e8f33165dcf7a5e315c7128ce530dfa9da66b1682b951e2d9ae2ad2b913de657668a5388e6fd067a80f5f35bfad2cbba27491a380227f52a77f7b78265ef8ffa2d32184251287a1de22e5282dd6967bdd5f29cff64ce15a49b5d31fc29d3dace32da8135fc35e4fe593c54d5f614fa40bc37ee9280961adb9d7032d554e1c431f337fefd1999015db3b0080eebbf6f09c68de3feb91347e4df90c927688c1cf8d411dd9664f6f21a6095160211b0fe7418456e66dff18411b02773013c6de70e9d49ba2b0d50e9b2d4a498ffff530ae64562710e869439d692bf01c55e32bbd1b930cbccc1ef38f2482d1040945a5c02609799808099ba2ae8d0e3deecc8306d1add12bb049342b4b2e720dd80a3a175d6dfbc6a6fde3ccbf6058adcc98aa165933b3d50a478709d787b1aa41a86"}, - {"0000000000000000000000000000000000000000000000000000000000000000", "001fc828b48aaf46eefee2b03a2221f8c36088737e012602db650d10db2bfe6353836642f64a42f9c08a09bfe726abed9845aa3ec160068c5c0c81dc888fe3257fe2a03ab2e949e749640a876b2e491d0930e9100e9b3ee0145e1aef3b70d25358dd20d18cdf3973973430d394336008137ce1654d76e63bc6b916fa746a1cbcd32455f6aadde935a533c2d2586f038bdfc27036275c261ae11cd17036758a3d6ebe0b54201af5da02764b28cc1b68b99fc6c248b9b0d9a1fc6357ac7c19631b8b37db0887c89e92040efb1894dac68a22a203bc97d2e7d963457b70d75ff9592e26bc503b44ce10b3c9936db61c2dc7a7f344d5de32022d6a741bda0f870918254e16b72c3f92f6fadc67599b309aedb635e8d2ad685f87ab2efe58e07d48ad47781d7bda57106166594bd9720cdab5110782eb361e2852f9c51324e4b92c6613dc3776f01412f364871d49582d352f010170275100bce051ebe323ad34feb154826ceea30b8703fe7f12f92938a0b1886598919d03ba3510b7011ea5227b66fd775ed19528cd643d5d53d91cefc0245f775af6160512c8f8843c49c83db65302fb0bb50eb88fe15453a6d4b05e21d6e33630c1ca7134ffd12ed7218b94641385d11fb541f67e04ad6aedd65ec9109515890e4dea69f6cb626d7b6017760dbb1332542355475cf1c9bb07551cbbd7e07ca357792a3fc51a0276847b23567deaf4767822c5533962da3e9889e027ab2ac93bd4e8af2172b368d7685a8aaaf0fefc340cfc09f82159dd53073c6139f5696a20afbd07686a2d9966d61dab6805d920f30492df6df386395e249d15a293c7e808e3d91034b3878e47796d2ee651f2cb1d543bd38593dc2edd4572e2f35bb1e98734f9965a2fd04b0a7896113f5ebfc3e8266abba94993752f5945e2dc2ecda79869d97934e3092a0d966f2e5de2b70049f08975480ce09358744a0a7079d1335d312b1313f0aff0599550bef2a602a37974eb2cffdc3f22b80497245da7dd4155b92cc4889b71bc8af991bf25f4109bdf28c58aec7297fb9324052f60a2049b39410b013b27e3d5c4d96c824b51c2a7c1c97276c0bc2bc622011a266756f986fe5434f75fd8b5bd85b43514800d0d7cf693d46545d29744a8fb70912da24e31928a0ea5b46d34c489ca505aa1d068cc876557acf0182400a95f542b6a0e2fd7cd913640c627f5c0931d3eab47a2bd5f18634e256867267e0adc092f0a45bf2eb506af007adc725a43df3af52039a9388b5ecc5b020a1289f7ec0c225b913d6293f47bdcfba176e476d6cf05486add3dac5a2d681261519c35cfecdd713b11be0ae60a9426e7ea059c93a23ace24da4318951cfe87065755feab3610cbc9f841812ca431aad1a87fbf971171c794bb156bad191a834b4e41476a3845d8b98802258bd0270c3aef1ac0b03fd3cb681dc64db5e14a1d643d94f0e0c00d3b9333797459e60137effa22b609c782917b2a3053865437b79c40b095f932703ce82e75b69ff094f352f0fbd34545a1839f1ca77c58a802fdcc3ffd6963fb68e7b5b524d0d80dbfbff16a070d6ba2abdc10e9095595413f92583141097c0b747d1645a54808c81fa918b9e502eed7e70d6b3dbd5627286d2e7eabcaebf57065b4c9757ec2cdf571bd19ae03255570a6d01f24d772423217a446cde30a8f4f0d04809d746aaf6b05f286e4791c6d780e482894e44e2106b28e51aad553ffc392e8eefe308e116cb700de430abceeb82a12856bea25e6d13fc51a5d81dcaf8308003105fc4ab660cebfd6e0faf749e5f213953b0b2fb9fb6f32d7adbbf97fa398c7bc7922d7843be94c09bec7c96e252147ab0bf7d9174bef339f303d0a22160bf1baf387e36f6e02c64b90d10373370bdf2870"}, - {"0000000000000000000000000000000000000000000000000000000000000003", "006d4fc54a0adf96ce1fc11c64e3de217fdf1ea7391764f4e9113675f7f5921347cdc9bcad7c08f23ac408c51171dd6e8a818c1861e4ddad260132eb1ebf872db71693311b8b2b179b44c9e5fc5abab0657cb8081330b55fc28e08e8e40cc37902d841a0e2d20987b77d9dcd50e2619ef1fd7a48b383d2ba4b5a7fdfc84b147d82b50d452ab2a2101635605561bd91649de60d154b8c9faa6341edf082019eb84a33b5f32c1fd3d5102e18ec78cad22680bb3161cbb64505b4bd355f03232f86292056f15b30c66490e9d65b823eef55ce081100fddc02364eb3fafb8351c96faf6a43903e792f187dd5c43f8db35fc5e6054dfabd8bb70adefd9c2a1305541af1d789eaff7f024d4b790c59908a32669531c8dc865b8c7876ba26349288364282de6adb4de4137e6fe3fe901b47c823c5417a6f8a96b6639b3b4a1b4a1494b6efeb79a7e773364c51832d1b90eb71a000f038439e1146d89b3740937b6a43a2af79fed63b5df0a70982ac7067d425b9c5d2d90f06eebad8e6360b0ccd9cfd1d7401e608d5a63e5fa33b08b2fca4cf3bd8dc9f56691999f43585b7a258309f2b1d7a7aab0fb186b7c0512b2eceeff7137c7c33e70a561f0dc01554de456d0647e5f7e4f5c7c450ace214101241841353fae09bc8a8f6bf6706ffc7cda431ff07b2f5d032a89b7d3f0f757d1ef779546b4e4c47821dbcd16903697889838bf8d9018f41e922286fede2ddd734aa5f7f94468bf0791dfa5656d138ebc75e5fda3bb952048255a288e67d3df49450fab20fd102f8db5a0136068e0f2b74550a72fabe6920b95b6cb30fb21b032f05ff7edf7aa1bed7919f714adce29f64e6cc7c82913d9a8785a7a25389df0ed714233ece561eec76950319ccea3f37e557df5e14854a8936c11738be9cae622e05799af097ef654c5ed72fd2d310daa94157e65a02ef45d1a248d4dbcadff4a9a23c0da166104cde9b462a656101630a714d49f4ebfa64240ad5b9dfc73240656bd34413a6a186e854f55eba59661a5cba6249699df5b4a6a0feb1acca9a3dde70804a9b129fd147098b47d871cd468f7d60d3068ab639a0ca0d54c6344d1363cf9cd3ccdd06aea77dba7e67fdf91bb777c60a940796b5d784a113ce83f74b450572306899353d1dfc037fbf606c438c84b203a2a885e9c8149d1f1705c766aa7757b6ad9d9ec455635ea929d65f507ea816fa89abb1c6809276db631de82c3c59457c5098b821ff4631cb8dc5eed7b1a32173ed1690d9925aaf51655b773db96e972d942efa7830fe8ae2e23d17d764077fedc57d591144fdc22562f8caddbab84a16b4c8126cbce87ccff940dc8c8456035468567a6a1829da14162e02724c578ccf9c66423f73756a8802fac4f13f4e820dc6771863f36324485d60cf5d7902b627a604aab5a8ddea272d97b560e43e8a24c10e339d164d0bb37d9f42903266e0cdbacc1d5d01e71eb65fd41d0eba7b7a065093b2eb7e7462bd6316c550271db1b012e9ef24905f1645f68c519325a8cb1e0e2b99fee909c847209298864aeee2a6d9c23b90e61b6b96732c6ae09b93f45e8d019e7bc749776a3cabd635dfc13d0a0d1d508a9e5725df9a92ce4a2c1f24c5eb685e2b2383c605dbeeef6dd668986fb2601bd6722d5fb9a309ff28ae86a1e969cb0a0457e73ee3b68932b71f730d1979ca6f052244b4100631db70a8ae4f3f75d7220cd3f77d9286fd83147345a9ad64e5ae4b1afe123327dcab55e3e3ef5bdcbcb291ff3c2cb2047af79697135050c2a195d8ed43c2f1c4fc51ec8cc6c390ce8f29b4b7055dd0a203ebae858f01ba71a7aed67f9e8e3b81baec9c5b1e85785968d06272443f6460bf9e5940687b90f05985dd827684fbdad90dc6e54719b678"}, - {"0000000000000000000000000000000000000000000000000000000000000001", "012230ffdf84d68f1b31a025c04f744049728b8ba401516fde3f82dcbce044a856cdfda10a6ae9b52f9801ba5562bd14a2ffda98a1fa32bc46190ec27f2b231d8487910c2a3a5f55ecb2a98a3065aa8d957e31650cca1bcbe7e29c7fe8a2838b113e8031111ced3e12261646b2844f1fef373426420377f1a70c6e1e1b8b12ddf169836134bb417c29dd71db00b6c3e3fd3cb71659f576a3e40b215c63761ae7e01545e5e112ece7031e48e9616304297c2b312884b8eae494adfe0fc41146b5b86612a4d3471104dd0afcc96643823ba6e4277932869a9e3111d95554c4d95822397f7bbef0b92c6329cbb117d0b2d17b13d08944fe6dbfef98fdb3051c286c89472e012d1d45377f7cce7a0aa0bd60c90cc95dadd38e0e2e9111c65e0973450ad8835f987507d2ee209a179f6be63de689274029c9c6d07d72390ec06489cc4d0c8375c84635c4b5062f98b5df34f501a79fad370e58c70427903e202bf1babe589df25f10ba38c7614d6867572791cff3b77a41dc553e6c7b0dc124ed151915c9ece614314f70a0ede2d678476214fad467dfd99005b3cf83fa636608bd2ee3ad80460c07cd4026a53e07b85e11a473edd81224193eb19e0f39d76aa08d21f14248b27a5971732924866b79e50e05b08f6b46a0f879a950f9a8c46681f3e6fff2fd333a3ead1ad84b89faf9845a8a583e834b315d13eb0589cb21604507b7d36f858426b1f015d01b727f9c0da77b76caf11fe3c93f675852e72a8f522a3fb3463c9b72d3179dd4113af7f73edf79d1e5fc07fc102f6c69dfda4a26713771ec66e55f6181e65572f5cae505c8630ad35908d7704ce7352dba0aa3730d1d46572220cf4a84a2ce8164e9d4bee0e2b77e2870bbe632069a17f94d8efa710246c66db7e401a7a7b0fe0dd5208645ad8b8a4431c2d403efaa6aec59dfcdff6c7f023764f5e992ae8d5ea2a19fa8a44779acbefca0de102dadbece101f81228c18c98b5189b748535d104c034b699556d0665abfcac19c8f3cfcd88d1d903e8709b9f14e8453852ae4a9c657fab42e0a690afcca8115486f664f221533326d44afba5c86a640a8d2dcb82190fb09234ffac08ba0a59064eef9517e39b27b652c3341edf4d1a09eed97f2f2ed4162fd56a2d07ee943a3e79188d90efb2928d5f3c23b954982373152e4094f142844ce5cb0b9e084109861c45eb7b8bc703d11d3be71d9ca3dbd643037cf756cdf262ecb76994c209fb9c3f72ad54f6b7463981c43fcd9177e6ff15827b5fc132d8a4885a862f2b8996f7ff0ce7a358b702112020bfd714185cff7a53171e61d4db63fdb2edb222843b07aadff81d5c0c44270eea2591e2c1e8ee02c0cf4f9936dbb3bab15d2cb62d9bd1dbbc1af819249b9d5b333db8a491bfa5662343ab2c21dd371382004bd111ea991a8958719742e49d72c913866d8372b20746c06dc1a8b03f5bea64aa9700d55d1535fe3eb0862631046a37b95b7221185e9428481060e1a35d51c2f95553f1c4ef5e8c64828fd6bb32675151cb8d506c2bda5f85512cb3c17c509f22b3f0d7b7454b1405ac6fed39f19d109c3ed3905b1de4b070fc61cda3011d2bf4ac85564b75836741ba4d453cb368ddfee0413311d47dd0ab204c9ccf1c84fae633cce7571954a0d1742d65d0706758cb5215023bd815535a63416f61044876adccee5a68b8b817348dd85016326be09ca114a739b7f5da90f1da2d121dcae5d205d7ccb268713ec5d456d125c97df0802f3f746278530e7cdd489126b825e8f8b72b13211f37ed2a48145047c8d00d621a2bcebaf61b0397d49c49c742f8976c98cffe5101d7fa3da662eef01f47a25fd4d495946e71857f9a32e3247019450f99d9f9074662472ddcbf10e5fd98a"}, - {"0000000000000000000000000000000000000000000000000000000000000003", "002a1775ce594566fc7c63c3862029fa4406954e2e07976c812720045f0822e22da9ddcb3d9ff6b8bee90962eec23418527f1386417302fd5fd22841fb69804e9203c047269397b828a5a85447b4adcac114019c06094cd24798fdfd2cb0d1a5c4454ac798d97e64542b1cc1956c1286eb6c30e5db54dcae997c2e31eda0089c1432fc0a650aa78b5130ed4c56fe685c38dc105c1634e97be38319841a18b60ee8c36b2487dd2d960e057c14d3d285c1fdff40e592f00078a1b20dfe13537215221f9e94c5fec3fb9be6f99ce325817bff432876ea860e8c72c90779d3d681694e157a4830b83e2bfc84babf9d2d4541a655206c2ad7c5b183de818e10280a63394b0805fc7f92305051e03179ad1d8c085a4686fc335ae455eb526c41ad739233af6f3f05531c65a1c5676cdff3b492c3d5efae19bdd10b5b772d1fc00a9230486235be4233bfa44cfedef6b97b8c8900648c29f48792dbd8c5f2bb66f980d191879eed15186ec275998ec66b8ebda3460352105926e6d5c8fd0efeeeefdc151335b7881835b9d706de21fe599a7a29ca7ec61faf2f81b314a7234ed3d335e9f2d534b40272be855ac34aba6d2d2a9c2861643f8c885e0f912573e4c45153156b948575bf4eff497970554f3ae23005039003bd89ddfda584dacf2e0d824c1c542a42318762c29d0dc982b8c425e952f73fa9837353bf5e081542a0571e082524ccd2f65550b9e5ca063dd5d039daaa2e86b7e035d501247210eb4ce1835e3c06461a95b40c8198ca22c8539c0d736fd76b6ad17d8ac21f1bd29d14d49f0ce07e42d7cd1a7b61239013a3c309e8a7e6aced2341be6c9b38e95a094b509e7ac2902630f299742c06b3d0a21598c0f009168ef6da5fe13ca40f58b75b4db95330b7ae0e7c149705acf925ef3ce1bd7840ad3241f8cd171d2bd1f7065b4d9af52801d8cbc1535f8c6b8f57516d30e4466136d0b9b94126012638668afd22d303a5bdc6d269b6a5771928a602552d27f0c3aff0c68c51e5b4d29f9b40ab7a81db12125f1459d7c1cfffaad46eb0a3fb99dac9fd85dc16f69df8a3ccd04fe0c9340a15ae98b12cb432281e1dfdca29871275acddd9727690a24a1ce81fb29c821c0ab2dd37335949c462053355b6a496f19ffd423439023f3a37129e053b52f5543bae88a25ba19707e504c27ff17c9d512f560b9202dcfc7f3c85475c74e80507589adb7a05abd428f15981c7c1c724a55c0d2121ae0c75aa4c03e0b27818bf4c479052e5609f19743de0675816d6d167cdbb65df7c628f5a1aceff41a2051b3cd2a91674854730a1667e5127d9718dda64354c961e887fd32fe36c6ca7efc0e6c412120b73478c1c832ea00b2db109b22da4b18928a307177a3eaec16201c398a6374583d98d09a0846ff472b333db5bb10274ff77d3b4b275ef5861e9a31c8d74ca4e2ab2b7027568a95d9679233ee6d4fc53536636f449daead206f286a103475d9178b3f34bcdca3b4dccec1726a71c721ad4138f872ab9020356b4ba413df93f9978021189c0d8adcbbc40fd7a75a849532fe1d3457337bc37040e1e6416825b7567c40ab24d7e919f9e78da8f1b89434c299db6fb32ca827f081c8708b5e23ed337217b1a270588bc0f3f71924c29ccb75cbc3fc8c80303161ffc84c4bbf8b58080831030211c6c74375a262b8062b53ad225cfc071d58d94b6bce71021dbe52d13f87d95609a6993b03c028a73a4f41a5348770997165f84e3bca930cd9f132406dd66fb66c1255855fe0c62dbd62e5804fd7ad6b3ee474e5c198cb370b482745acccddba1d9a3bfed4828bbfe2b8f672ebf78db0efcac51e813dcedbdd1e1de854f514df8c25f5dc71d15021a74e6b9537c09b3f9b561ef19a7d37df9e3"}, - {"0000000000000000000000000000000000000000000000000000000000000403", "0037d3d925920a7bb424b0c80b94bda2acb6fa61811ab235f33ad0e2738a9b635a1fc8f51dba091de56009f231c1c7c47fb0a285973f06ef78d282dafb71281f065fb1756c9129a3da73aa224c062936fc96a46105228b6e56e3b785f7b0a8996e5f435a45a17bf24d31c394cededd00fb2254969f39cb80a9ad291148aa136d0969506c4febeb5ed446a161b87949a32b37981df661592366d6a7f99792ba8656dfd1ccd10fa94000aaf4af0a5713b9cafeb6a66175afe271d3f88200146921658717418ef5cfc80f7ffb9003833adedaa80595f99c9008765a55e0831db26538413491d1f0d6081a3bd74ad5cf59421280b883b24acde924d40fec0736692e4c841513b3553b029569100753e5ff55f82a8aae42a38e177ec14d42eab14a0050d99f1ed1ca195da9b8909bdd47d40d12923540144e86d59f423c1fbb33244cdef909720cd5b872753a2b1e4f1c60ff0307a187e9879098e5b2573938c57a97cad27f8378083f8b112df08101c184bcb619f1e5d739899d50b51964efeaf26174e9c9edd5e0e9e9da91f651d0032331bd9e3ceea89d4f5733c5d8754224299ccf35a0781633a2467e876f468bba69083fe95276e210de6fb15f6e73c515b278a5c0d9469ee1c820b5d2711e8ba31a9b27e5241a283d468af873f0564d32d3d59bb4194349728fbaf12b4fd1f67781d74f05eaeda4ffd4d60943f4060b02d6faecf8b4ec41d62c9b2a19bcd80d0daaa21e5c5d586bfd9c35115cc4960698af774d550abea1f5ea4bcbbf0e8cc27a7227d5e18694b7bd95155965551f1ef25fb393d4becdd6313a7dcfb686980bdd1c4d8dda32338f6ad47ea2448322193eb25f6611a09d6f9868f9cd8c85a4f87d3fd98b49753fda4d138a4c81c6d90d731b0fa17ab049a3b70745bcf7b4287a83c592daa2bfe714059ba3c6a4bf0d6378f3b7012a85bbdc8df0f535fcb498a76c9c594a599935d82f665b7a8635de9df314d48f0e68be267ff995b22211bbf107db3614cbd8eed1e6ac99f0e900fcfc1c4b325b12e43ab7edb3e361b397f52298f24b1c361a9e03e9cfc99681d9e03dcb408067bb35f87935d4201f186836d9044d977d10dc7863297b95d273487f286209090569819825f8d099a392ce75cc2261809a838b11a1fde4ef958a89c586113b3bb78fd2c3ea1e581701ee335d045745fba4bab5d0594d3ce72ddcbeb48404a495c0e1497bbfd3dd38f716cc7602cdc79861bd167305f9db8c552c9fcd6571b435add986ef1b034939006c69f2e51ec96d68159019ac90ba84fcd8b5e9060afc88a948cacdd26b8a416ae21d03b6bb1feaaf15975532514c53ac7ee99193b89dd0f1082813fa5c0eaddac6fc49e492cef823b67eccad7dd78911380725ce8690f159cb1f205d781c1579409721959a297a0263a5ab647231d3beef42e5947b4fccda97da82f92d839d27dfda132d42b44342c535fdaa59f13ef8130b17cdf6c1333e17ba5c949ad1f6fba777c27d237a47dabce40f6a4a2b9f1d45d3b3e3ba43a8bf5dbb360bcbf7d013541a6ae713818fee49b665cb0432448917fb6ea2f0500c7a80d6622d76bddbda3129d3d4080ce0def2290b90e480c621f816def77184f8bd016022749d9ed512bc015a6c429f8d3a39ab618bdc1ac2053ad996ea098fd6d24fe2d3ff256a7181cada84cf14fe5ec8dce6f27bfcd0f739bb77700311515d26c40daffacd4338bfffff55e51641397b49831e90ef772c0fdd49c14e3894cd5b566fc6cab2aea3adbaecd3078dac3ba883069edcc401b461e0e7f60816fd34a22a1e8752b1cafce7dba3b37fbde24b7da1bd51633a0d66a903005cea113985f48181d3958e4855d4834b4f5125b33857822b73cfc908c356e05e61151aafd2"}, - {"0000000000000000000000000000000000000000000000000000000000000003", "0066f07022cd4b851d91a1b28726eccb69ea7eee710331ccabe9e134851a95c512d0bc98c6bab45e4ac90ffe5c8f2408f463de3b24398f27755e51a5b8bdb63801dbf777dc24a19be33777c8fba7da22239f991f0448c867bb5bc6e943f2813c832577d6cfdfb7402b097b3b2b21d62ef9cf94f25aa2a0dfad983f722daf09c412ec301f9785e716d335c4edd79a5672d66c2855d574b11b5ae5bd5b4e368a06c79d4636175b096903a509dedc0f28cf6c779a5597ee87eaff5f5a17990a05b310109bdf6fcc0ac220d5654f2cb3344842890908c34c090a56bcad0860936e22423c2a085585a31cf0154dca18af4344733a3b3eedd8cad5ce58de5506b9a99a698cbb8b212b70eefc23945ca01647d5d0277d599a95aa7e4b77354640e238dcfe1a19dd25552ab531f7c10d2ddd317485258f70e54e13c3b91c5a301fbf3b574f5151808f1478016d61c5a2522de0e1023f145e00c6cabe472c7398dbf952d32f7e9e2fd00520ee95852840d1c82255cdc64cd01ab2bf1fc2850ff2cb1d53b1a313a5d554b1d7725ccd2ce91daff438112bebb41f25319aee94c0d8412f4dcb2239776c063f65bb5544d694aec9c0cc39195a3d67e0de786a10f6690602a15c611a008509a3b58e91674ddd23df0d5023033f5a353be7f5635e40c236fe818195c8e63544ca845f1f708d083205365539fccec1431c349802d8255fcdec0c2d7a9b106a4f7b23e228aa5b04c509a6a676dd0b3a549ce65635bcc89d7f66ac9d3fd81137ba333673350dc2ba984fb649b8e3a26b3d18f241e4a7f4e0a8ac8bb6500a28ddfb51e2cc453a283e085d1cd57f4dee72c1a9b2335b747d6135137505f60d0aece7a6debbdb3b4632471528cf591036161a741cd403a7614c2178d140f7e9ec49ef86b09c36ce52511f26b3e898a6caf4e6561c91ee2e86e933bfdb6001433d2d7411358fa466e464a63c4baee734795f535694eadd5de944b59651272d5ac044ab2eb03c18971d452fa10fd318ef2f9e2519b8bbc5fd977a762bb32ad1b5b5f1729f1bf7d96488184b4025a26833d7630484a34886c857d8866e608a4b07d47d29c4f82e251aef12f5638982554c3a87b04974e972be687e0de810abffcd190a1d855a04b11225f3db9c64bcca081b369253bd7ba388eb8abd7736f73faca69f4ad65a57054ca733a845981b30ff4176bd363d92d6e7fda7d617b4f77f72656283745e1b5025690633128bfa6456079d65bb1291e2f1c2d4246a11523b223b2198ec401abe7cb6f00e42d13bd6221b6312d455672015239c0b6c1d91e0e34795ba30a2a69bb3f58a267ed69d7f1222f62f7bd8e02d6eced1e6e7f89faa295eb83ca81028194563999e58cdfe39342e70895b8e47dc9f9312edd0a8f8988d33bc2e11345c9f46d507e73dc6bb04b9e33ad012403752b0149ec4ffdf02ca7f365ca238bc8ccfa0a14ac96d2dd7713c3e17d600665ab6b9165758f86baee113b43b15598aaf4fc1ea0897360233b82d804091bb0fafa6c8ea0a5c0deec9757beb321e29cca7911006a33d2f462420db5b968b14d6aba9281a56052ccd7a606c2c4975fa639deb136f59bde51f0e4dcf4510e5c6f03238bfc4fb082700537ed68a28645cccc828e79d7f1b835d8324bc92dab9fda587059e55fcb6888bf2c7fc34e215d98922a7e3bb301a10a568b4f60c322196a6847a212cf275716afe3fce196f616b7a896374d0f11271014dddad9bd95ac59537dba2f4e93e7c3ff553068ca4d0eb61c394cfaf790d4690dadf8eb6d737e95390e34f83e8e989779adf35d66d9654b70131bf03c7587b573cbe4cab79dce62563467161d406730b7d7308cfe88d298575fdbe4044dd936aca1d11496fb2150db476093146ae4a4a6f"}, - {"0000000000000000000000000000000000000000000000000000000000000016", "0012abbb486c2095622f0530c5419392f1a07d1a3c037ab4440cd0214104d002d69da5fb6b27e49dd3a5084cfc25520423f4bb057791ca63619e24dabf439038c44f4d26f194b3f26816a0d7ff519e18ddd78cab23e574a967dbf07bbd29129a21698af930d11a84f62c7fca5755924e69509b134e9347ecddaf5698179f3033cdc73dd7d819fee3940cb2d2716951225f7a763b198c89b855a8d8b8c3b66515c5e5b2225c5e1e7701298f70550fbc448f6035efc772e1eb34353b0dd308e87968d7b146519c6332dc6cb7b8268d8c7956ed0526ab53e6919637d0c7c2445ffdf1be41ebd52bdb0aab4422131d20139a55a2d857f547ae931fdfd5bd0ff2393a400afab482f0f5d2a4720df9be24793e382194deab364a008db6e7e2be6af02dacd59a0a8b4d1464faeee212ad26f4de44b329d8ee0e3edd764552304b061d5ee6fee1727c265f1b4801bba39a5e571e023f098d344642d90bdeb18172e7ccede4b090d377317857126455ac2909eeb68940b50411bf5292f5031cdd86213ad48815cb1fd9ab78ea7b6f1579fe12a12bfc65de8fad90539b942539d0499a0152e56aba35039fde89b1a266652cbab6e2fccd87ead3f7194acb0946433716d3a2c4aa4cb15893b44898696487caab06eaf09db1a7329368c4f2a2854c7a24efb3ac9e6f173a839846928cece2c0a86ab8c45332adf897a838156ca77c7ad288356489e3a1975311e9508b1cbb9a187cc6db48547d99c84cb450e5ba645e9e85fd77ff3b72fb47416053e1a0d7ca0e45e79c929cc43fc3ee435b04492f69e4cd87c4864deeba76a1d5b0b7a666249b5342d74ac0767a94a8109af3674e39641accaa461cb5443be63d45552e36c5843e909dc7bc138e64387d7f1d80519175534b6547d7d212b2c2daf802b548c57384c9f1f3f7c607c6c632dde02b384fff9fae0112877b451ad43bab1438cdb2f7b3ce77bf3a26dc13a804a4495dc113b88f3155402397a0a03937c02e0e299dca552f6551df76817d0aecf95ce702d06f191dedfeec198990f85b25c646d648b48d95f30fbf80012e3974dac413b17556525ec919af6d1f9430c7c42a820b50f3cd909b242efa2e6e5a944ad76b1e5433171525885a53f164b6a3e380ca2bceb374f01fa1933ada6346dd74ae53fc5c764ff7781a567547d9a6600ed6d7801d94da713226230d47df06c5752a57cfc0221e0c4b3009514f5d2c4252adf3e2399bbbd822b92b467c7a6ca2e8a1986d33bceb7c1fed1203316948502487b4bcd96d090b0ea7711ee14eb64ae37a18cc14177aad4bda9ecbc5bd823b5a145ebd3cde9065f32dbb4414e3b24275d20e4529c8ac061e73d1d3feee18a612e2f8c6772c56af51e125b8935cdf72b6fd87281ecaccc42dce5bbe989905de557e039b333d69ea0180e6a6e3482bec7074511c11291610bd0ff8435a1e8b47a0858b79133776b3b333b255bb34ca5aaad20b5bdcc3878329af57ba581265c70bbe30297879ae36ec965e76504c18b94e665eaec443c209d0319d96099423f284a3c5fdc74ee1169fead76b2ecf5b6f9210900a888859236598eb54aae75165fddf411985610b420ddd9ac51be3455dd460f8cd56bde7ba4fb6c02683c703ea21b451794b747be67b266a0faf5fb7b702efc30d9304e1e5bb9a01582349d54916e24e1efe0b048f98f3cb6b08f676d0b668abba38413ff5db6b272435680a95d567241454dae7bde15ad40f7c92d235605c5a2c5b56b16b25c7b6a2599e3685ac94c8b5036718b4875361f32a7a504ffd87ad32f9189e1eb82975baa8d0d2daf92458182748574f2770cd9d1c9505b3095225824c06d1054817724e50ce400d5457a343285eced8782b21d85716affdbc326224ecff3150"}, - {"0000000000000000000000000000000000000000000000000000000000000003", "00027229c452938ea70bf42bfb67ac95cd6d1d00fd2e206e5d4698d08be38733fa6d5fcec27716f4a77b049b93154f9b2e11ae08a069cccdf6b453437fb7d010f1a1f32b5b7b0daaceb194615622f5692eacc377082eaf3706c3ea6b2f79e2d812c1ffdd28b490afbc149b52f0006185c3624535ca4e6778e2781997993e32e38bf1e87401c3e142560218554d1e658038f09254239b3e012e9f538380b6c05fe868d34a2d1fb0eb0c4b399d823a9c9de897e7c0cf6b1a7ead90bc50dd1c02474ef3cc166303e1924df7d29c7e2089d6b6723a75745b61a63a9f821c87567970c76a3c1edf320b469097e8945b92377d74d5b99b7587bd858b6eea321082e6787255ecedbb9bb18f31e92e4928dabf60291c2758e8fbe57f19b08103c3156672a5701b0f3e5c4b07fb33bd2571a376d4cc32b7fcb0cba09dffce726cc6f42d3922018f38fe47f0e44f41870f879dd4fc013980d1fe97b9e3a3437337fae41b20eafd5cc4a6116499cb104461eff772a70f1f79077a4810d55a06044200e8f2e233a357ab90d4e76892111130910c3b29745ae6081ae10727a634826ff6897f0055bd7a71050a6d26a141a3f27659b389d3b714a5dda61b76e0066a9b30be5deb1f1e3e16b656d4b83ebcc0bb165807e113bfd3ab9e21b0f772e52321109d49ac92a33410d835807a879630ec1d61769ac04a620de8bf42ad043d6bac5004214a90a181ac77c838f6a318d69ecc18fbc63961d40502d2bdb33370e98fa58557ed8e8d086a1771931eacd7e71672078ac4ada9870b9246e05826b30e7057a749398468e1166d5c8f3efa5c2bc608143a790f035d5984d6a25045eb1c4a07cd3daff44760c6c74418777b4038148d143fb1a5c4b011919513a14ea9aca46213e58be28eb3e8ee756babdb3cbc15432fe28aa678c1e68773667643cfd11268dc7eb10078842afadeeb5f72dce239b85d7bdd51fbb805211c6bb66b902763d353aab353989ca19a268c34436e031018da9469d669c87d845c51d40d6965111018382e00ddacd21cf0a56a21233fd0a033a9998d74b2760def9ea4f64a6f11d4831474ba62d5e1b03e75fe3523517a09e6b339cdeaa513a040f6a6f302a4d8c94d12d703d39e8e1201295884d4d7e8a78e0c6b33f08d1dc386f07a0e5351c40a431efea3c37518b038ab1801b5d121ecd1967e9df732dc27d02a78bc7b1fa51d2d759b2261d15868998244b02f3a449252b6d51f5a133b10b33651f46bb405ca1c25f8a1e7a5a45fe13517b8fe759e18505b926e55b7f5626122ae16beed8e0801d8df9b9f0d09853d60cbdee2b549bb221a7b251f66bd80f2ea84c175bda85b9867737acc171f4b3b0c47be34cc636da5a50e63965ca63145ef4b332a95463752beba55f98f1bef570ee1e0fc7e9eb0b8866e00989904bb0c20ed1614006948ade62d302aadc9e40830519cb3176cc1fc0080cce1fdbca9d655323cc41f7c47d3ec0e35cbf77998debfc7c6be776955b28e2c29d4f1352387e13cb5e84347ecb8beebdf982b7a07adbcbe630805174414db766065034f1571dc78ca1f19bfeb7e1d2cff7cf022300e95ab31f200d642b20d2e12d7d7fde863f878f19b5f7182e0e6d73e14740e9f1151e7c4600486602595f9fc4689e76532695b017e707149e4ed0b6a7cc665db42644dac1a7d28ed0a26bc2dee4af88ea5fcc37aedb3872ec5a8bab9dd0f0e851ca185e39eadc977d0444e27b2f0aafae44f5d56f42b17bafe21ead48a80d5628263985fbd679606263ddcab8721c98114172dc4edbf1e80383c72a810ffad98a91dc183f1ece3aeb3b5fc01a38fd0bd3213fad3e2ed075d52eb2ee8fa774e91027d605a055c1f4831659a92f54716c5e292b5e5831672c8b8afc3"}, - {"0000000000000000000000000000000000000000000000000000000000000007", "003b58a789a43e9b6b98f2155f4659953c81555d9e1b93f33dd8487c55fbe9f93f3c6f6732cf0758066715473740121bcbb31d2673de9adb3e25884f2e9f071b1d2556ebd5cdcd3a0b1770fd43ae56aef457dfce041e7a36e8d11121f2c800b359c0de6577908e5aad330399edc6a8642f64f3c554b2ab0a3d83c3daebe10a842feafbc5a18f36f250de47d60f1f866dbf92e21b568d90c6c9efec7009aa84835a4616b4c43fb77d0108b19cb3c3c5e776645334c9c3100d91cc9aa6280c862c09b316f38f2486e70ac9f4f341cf5f31bfed0777a1d4f2205c9d848c68282e5b0d0f3752fd1cbc136d8d7357081d7b093d659291f9ca89986a920cab01f928bcfa86eb7f28f65450aafc77052a225964af0e964f018eaadb93d477b59d3dd685919979b4be860cece9754fd961e9689a92f0dad3ee760bc99aaa6d284792c57e54c88151da3615704002b73298de031e00b9ee4659512cdea39821bf28b71f9e618db3365310e81091835ad19d5e6c95289d4ed9ce4bbc39a03925e1c957ad21b2d9357c05059feea319b365ae9068502f5b9885d7e1476aa41884dc521eda99793e034a0d608459c9a6e665ac7bc53d39e006c5c2a7d0a5ee24bd616c815f3941b8a0256a6ec9d72676fbdf140219480aa6dd866b51397493d1e6a2d60d3d5b789e671ac394f985620c0168b824364278033a1e829b7ad7065eb8f10b033150a437f48b22634ae2900ab4a45d190938e615d95e6fd733b5c1ed44d2afdb2a3f6f0a198cae5ca6a600fb418ee30e92f1139d0cae3e1d5f31338c84ad1bd72b09dd63c3661f023d757cccfb81073b6a129f079d17dad8211f552387fe5f1bfa47160a2d83c310697c795be047254944727a338f96c6a01163fd7b5ce45f53261114abca4547958776d8eeed39401ccfd55171aa93f2d4832b6389a70c939eafa40125367d8052e671857e82656a5716de77d39cb9703bdcc31eca5d43fba1835749e2f95843312a1bd07a02808d25b24ecfedf6d88070f68638da4432178170587975cb122138d3b42be9a154530cd37ed89f65fe109fb8f44f8fb9e32bfe820a8c267378cd593b2517224c16b7a1c9b24c880043e3d75debfedef47e3d9c15161cceb54d7eb0767db29b276a393798b77d970b7275877989a0a1d90f43675e017fb4171e94fc59a408845cedb8156eff8bd7a27b85e3230531eeebf7242a045df482253105d95183d4ee67636142423ab85e15f085ff115a10cd9df82231c123ceecd49977e3c12911cd1cc5a2700380e5556729f0d0f2dc2efb8078120ab48dd91c9204f0da71a9622bf0f9c968ce8a7473725bfee73a4a61dad649740077cb46afedf7cc021244e165a40fd370eaef121b9c1800acdf26fbd11133415ff15e52d50cee1d55bc6dd84cd5888b5cae770359f3c8dd5214b2ae42737eb4d6f925010ccc6b1d12e8dac86b691fb5d3afb516f6cbdaa1efff12aef0189e7a4f1b5fa0bd8e73d281d01aaa9d6abb2ef8ec93468d121b653d9996403a7c04f43b5f7950fbebde0dfee5de50cfbbf5fc6064bf1ffeba8de2fef7a3d734b21277bed7bc91f0f625c740d058d6a59cbaee4019e67b69351f332bd2c0f5a88577b5c589594efbe1211b6e0b5dd3225ac87d42f9c73542a20a66bad5800414f86a4408c73b9d24a209dac245d971483f9f242ae6fbf7ba75db83f95ae363192f91fa0f9dd9ef420dbd7b9d01e3040f86272279a9f1e7c70b1f7cc1592cfe9ca8dcb5a7edea3d6618e4384c99c64f3c66d807354e42a778e777cffd30e26abd99e0e7e1b3ea410bbc5c604bd7aad31ee94275c7cba742d7cbdea8b213177350965602ef06d30153b7d9de2924d93086a72a0eee4c26e7bb4d9a1b72e47af0d694dd000b4e38"}, - {"000000000000000000000000000000000000000000000000000000000000000d", "001d3a5e7d663d53a3234107da4892c638e5d5310d03647e6b65b127b1c818b2ee605aa1369a9dbc57130c7e464079ddde53f199c52ef3edf3b15233f65cef12ac3b49fddaaffdc70a5222175f5e6d814b9d04ed0194747430122a137e0a91048e5a2e69480b91a8c20b181dc853868b3c8240233121cf11a9a4151343f10bb4d3e7890d9a6f5b5cc2c66bad89fb0fa6fc865e22ab04ebc80d96efbd4b124ea97539ddf2b5d5572501833238f2e06f6d960ff0d6c4f3aa3a0dccbf94100e13c48797954883d651453b835701eaada9d848cb0bbfcd7fa8103eb5a174913bc06b36958db9f64dec1d2a77cb89dbbb49724d9578df34e306e069bd2926022bb3386f255b45f0c1d4640a785225445b188af30806023c19cd06ab225811993829b668979747180c07b3611db48e4679a36011592ea328b4c720dedcf12f669f5cd95170f2b01673f8b6599fb51921d05bfa01aba1270fdc426d87e955034ecb77ca9bbc358bcf11fd26d317aa8787cf98067ba9f9430616bedea8df0832a654839827632378116722b6d578fd2f4c661c13727da103519fe5903b4414ddfa61adbfc3d63df107fa2dbbf094a382fee5f428976ca735a275ddc6373496b3b4345772b0d345e40af1ddb22e78f2fab84e0f7a230d5d5de452f60e21fe5ded0c6a61d37ad9861603ae291a177c1f02a184a2cb29cf3734325c0ab606d01ec2e83a7945f9d0d72daf7c1dc31105fc12d17abb347c10a939b3df7539ae6d55853e88723d4c7607b1c8f82fca9336f6dda8ad60f7c43f87ff9c86f53b0ec326459de63feb95ba3e41611fa31b20db56ef0bdda6163f39125bfb11b3baa95090b788639edc0011c75f34364958a0dc24547ddf62ce1b704c3c758a3044c5ebf51d194990c4835598794ca1be5b96cbce32f59e245d22cd59e723c54416c75515fd1e5587450031c008d044b236ed73208022f3cac590bc310e9c0a170431ebeeeaa1b9a012f27f226072a0b4dc47ff29c75deee315d1af76e34a053e78dcced8b0b7df192c6249d76ecbd147aab54656a2e28842d0b65dd00906c33fb7a463d2b1c06fd564853fd4d28821d6cc8b2132a9f69a6c84276f8b62dd141f3672f2ed389409104236e8384eb9d34379e366bec2353a3e07f22f15154692f4d2221e33cf2e6b8ce76a1f6aeb177ee8d50b6d7e3ef9db45d595e054eee2350de7b4fabe54a9178a62216720fb814ea686753bb6ecd6f2739a9d22134c6f17315b11bf2242332d02409e0a7a877f82c24e5d5485dc6c4b9fe5aca85942cfd13b266079a21e0e96e7beab135e7b58e06484e9369d9923c16ad6bc252bbc0c94de7a7ff1b5b42768b440320153114ce2178f00ca7fe20b09f545ac35dcf9d99f36347a0cd520b03e632f16a34d8405251cebcb7846475db7343f00cf75fb9b0f981ae8f4302aab1b07aa00929ed525032fb760724c0f53889413ce633a811d3379bc72df05fc9e1b9fc20c9947c510e28d1c32287553e73e001c42cd3f56a4e43943ef13842d600523c8ff3fbb290292e672471ed8a1cd1c9196c92a2e1307cc79b4372c0897c2c5a057b914d2437dc0b837b13ce598575508525db5b9451ebd486241ed47b1d2eedef2b7893b65b21690cf2791a1502b1a56f0e20e331a9cfc8dee085c21456d4e67ae85b12126f43b5e4081eaee897c0faa449ee36408ff279024fde1d8bb89cb05f67aef35157c5d9396606eb30ed63df677450634c19a970257e1151ad2ef1a17bc8c46cf575e21ee219f5883c50e444e06ade9d5399c95a3efb124c6da002ed608ec3c4d53dc1e611517885c059195cf84c31eb21de5001fc17728e0a8b0bfbe2eb8f514ebf3d29ce256dd4939339c7a14a644e5c0caa4445adc54211fa4ca2789"}, - {"00000000000000000000000000000000000000000000000000000000000003f1", "002d56c2e04ab1885ef230446aa853491c0af04f1b4d54bfcb7b5ec65796b9f7dbf9de5e06d9c5775988105991f35827dc65b8a673cdb56a2f1a0a36f5c82a2bd4726d6294613b4f5ba335162cc3470bab9ae5280082636f3fe2de79e2d1f5bd3ded7cd630b572813e43647aab7d51bd9da4cb4579b6d8d3e3099d5e8248211d46b0dc158028c5da1347d1307ff68a6657e67a313f1341725da4011ee1774919fcc13e1a3a7e995201a09f0ddfc6ce5bdcf9e4fe9c28ac3a0527358e391a6d020e4b12a6fdf3bba524005bc7be7ab638f50e01f5e79903c122b76123f48c9c4339c934dc9ca02a49e50293311f3c4734bd696fc56ad9ead62bfc2b05051261997e535f2759c242d09ac66f1943029f1e0f1d604f39340c6a69bef18998d3f18a978b581f0f460c53dfd732c92f5f204fd34c23aad653c601bf8def15b09b95f4df7971997b495c0bf7d4dae13c19bae0038539267c934679889c77844ed2b6aaefbb7dcb3c2183ca521d22c319d675a4d7f6e821b9bcc9745bdb11f6d154ae1089bdee6173c1ebf69dfcf8894ca4433586342e47aebdafa26af3e0e069450b24855cb3470aa7db853a257fdb6034c15e87b77e84e3f5690a7720f09740d7adfd65750a53635f5f6f70ed64b411380d9d2feb241724d312b0313514acbd394930f5dce11661de2c31995b4b6c5962e2f8a574d6e2c83dcb17057a4b6b0e86f363bc41529ea93445a8b5ae39285a323d9400ec7046ddded9b334f1698b4d368f140e0d07027f0ce09c1e8974bf812968d359987fc0b5ee6b4ce847931b1a1dcda07236624b55505b07435afe960b448988f29f246ba72423f90463e55526466ab0e21dba872ba1c9dbbcc0975570fe42a99abe8ade73be1304de1cabcf7efd7da2c9e2eb5b85f6c260fbdf3f23ac240fcb89175c49faf437b0e34c0575aedfb4b700990ac41e0d43897d9746f48e4aaccaa8ff1aa6dd4da56602dfaaa3e55686466b8eba81db1a5bdaca6e07381afde89aca03747d90bd422440c4579f255ff91444512b3769427ff36ff19abf51ac5b149cbbb6f90d7580f494a33447d9fad0f516bc19c45bb5857a5a40a3f2241d611f6f9b5f36da41ed374a31533d004a239c2aa63399a6270cfd46f7da5ffb6e22b158d52442f2f5348f6dee85fad5471aa967d9d2a1521a087902b629bf4c99ebfee383f2490bd22aae74a0bfba5820fedfdd259c14ccfa840595983b67b31230fccdd20af299f860c905e590ed50c62073d2925919dc14d40b055d604764cc07cf2924967de559915050bf8af004b9023e64da4b85e4df31dee0d280a2d1977c80ec1e04c7690d91f076d47b3319f0ff385df1d8be5e2f1bc45c29f4ed1d67f9f794e62b68d26f51001bdd3c2ef8d6a2d50f363d9769971bb2d426771c947a7bb302a00f15b15f328153d4e4d3c07a4ac19bdc9b188e45998f072b7c3d3ff743657a77461e13a24a1e1e8f24a513de999a9101f2ac837ab0404431b8ed2f6bfb292e41ab43ad7753f56c15425df53f517ffe1c517413cc86561959267b001842b21536ca5a5791b3e47d3a5f06c8b05ac0f1db556772b1cf2ea34f637cbdef156dfdcd76b102438b284a17465e12ff80909c7949355fb7abac6d4243ebab1770f5ed8115fd3bd195a00379859bc2ac3c81710fd355156f4a31148012db5648efbf7bc29d0c1709bd453f9cc34bc73aaa5c6d5c07443d9b1f765c0fe2477113244f9adced56fdd95d2ea82996adcc3f5897a0d89834dcfcca32147466d410a2d0f8de9d13e514285303cbc273f902d45048dc25d4fb102cca0fcfa777369ec4e8962dc1875457612699155d28a77407857c79e97463b4b34ec89bc39e3dbef44437b766adf76ac820befe7586f8d1daebc4"}, - {"000000000000000000000000000000000000000000000000000000000000000f", "00893317f3e2b0ebe43b6437d0ea447d629932d94c289d25cf206b6dbb7d23e330c348b338f135fde6890c50f1ae1c09b220c09ec62ca4da5dd6472d1cee792974e70918115059544767d168ca8f422dd63ada4707f6752040e4c4018ece56262dd38e7ec0541d0225115ba61b7bd1a9e1caad32be1c326de96e8c9e5e202e86d9bff820175f25d1b4600742367919fe35351166bde4342aa6d245fcfdf6727c7e9ed234d85cf13d0596de227f43fc410c6261a2065ba234f8bbd5da36360416d88fdfa2c3cec7262ceeeb1bd22cda97b65b0d821304dcd862cb795225615553f0270ebcfc2b331975a38acb3bb53bf13c24e911e539d59d92bb012409a5ad8affd4fe09087f0127fed9200097fdb2db7d1dd3b692b66705cf732d61df4d9cb2ff24d0fc5f710d211e3f740db38c9b95226494a457da4bc71e51580fd256ae05cfcdb34a17b731323d4d1f75a23e13aa0112a3bcc41c1a35cc186258a3c32e7e24867dfb8702ff2c162587be23ad0932b938a9e3e0d923d1bf9a02ce137e38e06dd9c32b09d845554e6ac35dba1f7d1e78e7f6a59b88a9b3f762a8cee2df5d6f895bcc4c027bed3939823933e5c5a35081a3b10a2954337cab214c897c721ac39feb1df24784277c9eee37df962f0666042a475d1d916343d106131d05f4aa5c51d7fe15705f454e89456fb541db2fef69cc2b0281f855ce06d1fffab651226ba51c3118e673dca2193f99dfe0193057c5778ca24d6cdb81ae4812b8e38ce03f504b09165ca636ccc3a173c0130c4564f36ee3fe9f2c57116f59c7058543b2a0d031f45ba52f790c80ef4dee09174aae5baeeeafa968856e1ecfd0626c2475e3ff28636c34d1d69241bd648a04f8e17802ba4b5909990ee6bc5075478021cc3863b04db2c7eac9f3dcc3fe36bb8f8ab9143371ff94ca2d6be653c2ec67dcddb600aedec913c5bdb259cf912d05473b1225fa3e1d64131ccb557c2640c5db0de72fdccc25721d4c7ca99a0f7e8bc14c633d6337c97587f8656801ed6f515269154f8126722a0ba7ff7431c9fe8e50f4c7e697a52603756f9793cf889f0f9261d4c56d430d3855b37c83077dcc15fedc672ba3dd75f10335975d84e0cdd15d08b7ad507c9627856aaf652e82fb227e7b9bbb778d5b5605461fa285f3974e96b7dcc5aeb6b21b38e7c70957f0d313f6801be776c2783fcd2446cc84b8d852270e9b9c0f8c9fad906e6b148eef8d87b720bea95e133bbba91bce2942d4e6b2bcd27d80a11f5a8d3f9315690cb03d4f5327aa5e52c3d6a32533592f3ae652106681d095517ac7642384d9d54197fd68ee8f2a8a7329cec960e5a87df3de685ed2664a5f998a3d9f7254d34ff0a52fb837ff1225d24370297ece9dd8a005733ae73fb81e6ec1365fdae946fb78e2c5e8de0172043944512ad2aa5caf9f659f364226d2d0bb5c20390e31f60cb05f1449ec84e172285fdb49b5a378bbab060819f9e4c1f67bf41555c003f69fc772b7fbf50b0f39f5a386df504f795b5461a1b452494534783fba11f184b18b19b5cad6a953a7b2d5ffe7b399ff9d973303df99b26683695e51762940e54c5a28311aed2f1c81ad2c23673ac1487f62d1bda673b24abcfed6f0403615273f7455c1b7b585d5a6ee4dd5c15afe32de0661b7fdf1cf9421cef3c86389794f7b9de91f5d36074f9b2ac0a429ddff1cf1d1d57c6f4cc7d5b8733a0cc91912a1cb7fb4a54db7b8bd6282165d361ce03239a79add42923da38992551e1f7e00e655ab56d6721104d275d59f45016d6f01d1baf830856fbe14be4519adc560cde02939f1d5d313627ff67db99173331523f2f9505755f196fad835a2a4783e662047144e1d42376c14cad323cdc89b1438a43be4276fdffc8f3d"}, - {"0000000000000000000000000000000000000000000000000000000000000040", "005b32b36a9169b7b99a110fbec8837486e4bca13110c247c840cd761be100328c07a10f3ac6b05821043cbd966461162c4597be649dfa51939df4831fe1ad74da1f150829fb87afcac8f832fc953a5947d78f3f050d186c0102cd691374560df7737c7632431cc6df23e69770cd166ef8b9595ba989e7708b542affb57b1503320e7ac676909c4231ec40129e8b2a42fb1b8331d1d3e3f47d86e5f7f936cc54738b3de983f01af30b2b76afe2c62a63427311c29bdbc97d0c40d4b94a47a84ef55aaac15bf05586d36e6a315df9983e990716be07eb910897936a5b918af3928d5cb82fd8a10a2a0c73c9b65c12b5c8d9b3d6c9d2dc359b0cee79240c8fae92f5accba5798c811dee7dc8ac72c8557d8f23ea7a1bd6a92325b8b3c2b7163417cd77a19d711e0dabacc2f6ce452e96fc04d67153ea5d8adb1ec2e1171a898b26c9c30d1f83a33c604ff77e5ab8fbc13b0216d7b9e74ea22d18e58522233329a30b777d92462c969b053025224fb733571ea749d292441b9ea0d605a4aef04798470d43542378251d78edbd5fb36fe6064ae9eab551459948c9672b9ad07d8e15bd7c37700230673f40a6521f6d86809ac860a642c334bd74a006999336a34a1e55853de3f13b2352d985ecdfad3f0c242318d1c4f1dec90e814c2ff401ccf5d5ca1eba0e18a1415707ff372f72410e0b58db9e3428f95dce04e2c847d9495dbcc32014bf17a6b3c61ac8dbeacd0d23169cd550426f834904de8a33002e872197d69c0e8b121aa0c6b665331e85353d68ca823ef2bbac3b295a9d52baefac49ab8de55183fe9f3aaac5da289905898414a658a59cc63f309c26c36c313861789bdd1afd87ae0f86d6ed2c359806e4508c7a0fd47558e20ee775e4ae4b85a0b92d8231e035828965e3fb1ada2f86a5f17a238a8b7cfea8237c58a2670a79d9e5ac018dce55d7c42f975bd2708b07da46dd1838ecf7e21a83964c34ac6055d918531f9156c912a390f9596f1553a659371101dbb7ebc1d75be5b97cb3b46b6e522b6afbf5ec65050175a053838cc4683e5426bbb3e31065d7684c9665f15edf1422263898f957f3f09f4712869b2803069648387292b92a96aeb55032fcbb2f17f4a137fd90e2f79cd1d721d1749d3e0b8c35107f2197e29456b1671fcec2966f9833c227382e9c5c5d039af24e0e302dd58d9721b6ecde586942be1471e92513e742f91a47f75d01e2b17bab98fa2eef557ed9231e0ab04ee186fd2b032449a7b5d8851761d80c064204dd87eb5d17a59e6525a7b7597b45b8115da03c0b4a9c0baa1bc953889623f6c6e9696d9de99fc08910b6aa2c8e5faae7aa72b356d7fd2a4fc6a7df831510f3f168ec4d3337a1c48a8db2f25cc2e4e9b9ad3f16dbbc649260802942438287eff74db641b2b229df01e9e6176228aaf9f13a449ef34ed8392f023908e57caf7f70c82393f1b0c569912ce55576b595ffd33406e4095ec5075034d0af181f5273e56e3fecd202261ab4f4025494420f402c8716d1df651a953fba1a4009b7872fd4b1bbebebf8f22001f3d8e7a7dcbf459311265a728fdb77ffee37b1eb2fe9f92d5f5876f7ce0dd35f5277e3fa35436271da1b545b6d05cd0cb4174262daf665275bb9cbd6b7839be19e8f3ee4dc9a9f02d033b39d9092fbb4c1434edd2a0ac511adf84f70194fc6319a0bdaabdd7a2387551f3255d45c763093053e66cdaf4382b44bdab21a2d2f5b35985e95857a074c8ceed798cf03bca2d22f81a2dc8cc390bc0c680463e6354accd9d5b959551ce8bb83a2adc33f2b550f1fdb6dac0a71853d8551ac326a34c1333dff1c9b219f492e9bd6e525dd33734747d9c8863d6c9461d2533b9f301f15e0b8b242b9ff376d71331c4d3f230a"}, - {"0000000000000000000000000000000000000000000000000000000000000006", "00080440fa43a4305708d1a60b2db84114295dc0ea0c5d7c7e508323fc4da1820298da1314dc3770737729c136a45ccd747f7e2164cda6600c5de4f070db144f803cdf809c421786559ba1f5e7bbc765453d03450589bbe40dce6e76b507d62003f8081ea99cfbc1a052aaa4a45fb37267af0cf5db0ae272ae09c2b114670e7c731eae4d334b1d9c11183850faa5a1b1578de225af72a95e2b5c839c4ea46d995cc6e32fea5eca8700e01cf97b344201d5a59090f874138eda581f91ac1411faa6c62ef4f5c6113320e8468e1f58be5d760b020905baa504f19a4920d4d52fc48f22b21c5aa28a296b474d9fa0edad55744407a0f80305a5177a557f12f5afddfc9698639c4ba9b08ce5d2f2e74a38d50e4694c4f2939ba385a49d8685d65351e38dfa3f2e3c19349a5df5b197c3e0a742e660d180a6727b1f264628f08308310ee353818a72b6b9f4169db62113695604827fdc595b4d0b724ce2d4055cef656bc49b417014c5ad7183cb8dfcd49834e57cf411c3ac04bfabe90b4c9c4a7b1ce7f345daa32fa2ee7d72250799e30f0be54fc7fc496958b022fd82aa768f6b6550df25b116df11bc332be9fbd03952ca09c9b0c98507366c8f1b1aa3745e546407e77996cd5338058dbad79bcc8418b3f4cedb6c5b59aba974920cf9d3973e48bc43f443b944232aa42d47a21a1a684c5b519716cd9dac4e0536b9d0f4842f05e25f133b5d21434512ff9ecc3b3a79247b419df9c50dbbb5128db7505e30c43825e30888b9f7671607a3c869b54fcf2aaf0db828dabd422e3d4446c1ece4f79d94f683b0c7d11b0115fa2d100c96bbc8a61bf2e7da9ef1a74145b149afbbefb11d0d40693b6417dd35b66e2b678876d7471eb1bd63090dbc73ae75cbcce7ac3372758d7907ea81cef43d4b49024f9762d6b50ec1c0483f0554b9b6c290168c5500322be3128f66fe8ed166dcb3d6f8be2fcdd26af70d81d2a1c9688fada806389b266bfdbae1e71d2b611338bbf033886203fa57a545b2ecd061ec62df23fd2ea19271ebef42df8b14178d626dfc725e601cbc33052abaca9546a113ca22d31f3e9bf1c27bd7fa435f07d3b9237ca9b9fd9dfae0876db97d86a0fc1902fe185de9dda18e0181569451d6fdfdcf0a0efc9ac1cb51df4cbe1e70390f98d3d5452ab658719ef11747c90b0a8e4dd15186bdd1ee561c44ed235261997fe47023ac7c71af52ab5726a3825614b6f8b8a075dfebc00d9e5dd8eacfaa3bb8f28373e6c75ad5c7bc9224461efc561f7f87df65ab973303031e9a4645eb3ef4f113763d1aea9f82f5d54001cf4aac06ddebee5b835213f4778a8af4a96bb3f051b729720f420e139bbefe242d6b5ff066c5cd5170444d66d7edd7a1673e98996b1f939097345e2dfcec78588c525c57eee4ffce4606749a8b4bca77f5731a31c39ca0b1267b74f9699f09834c8c22f2d901b53051e2e6b78e3934c470c1cd194f4bcb45b41501b78a03c614d8ce8eaca3fac9692a94841241342c2ffb4cc3cfd35ccf392a19fb9e5e11c0341853110650e8d705f5de303c79e2cc97df721a916a25a0f7971bfeca7463b8db92e95871ab907d2b1496254b8c99aa776264146dbd7282eb98db53aa527ba580e16d3e9776941678be53eea64496967fb70a7d2167ffc6c2b51e0b948aed2e553acaabb6a4f919905d0bc84830eff90e8497e3d4acddcace1c560f0c9c2903b7c4c2dfb4ade16a3df6a8f93d92794cfe353a3d64de77a62dd22f748871ce0c9e663eb63d3d12db28b05328cef1d6d7d3230f3a46ed580c0eddfe69702d0971ad8cad852cd815c97d91c61292be9f61167520eaa191c68cbd1b24b31ab82b71446bb3aaa31f429233187227b9ea33f2f09261b4ee975a5db5ca"}, - {"0000000000000000000000000000000000000000000000000000000000000002", "006f901c59a010c72be0e2e881e81a61565d0e0a97261214ee04d3968be764e43e4e3edb9a918b596de90d214f243b243d2372e99464aa5b4ac1650bb8f65b0dc97c089c92c0af49a55436c5b3597529467e57b706dd3c39eb077549462dc11a99f13729c538ba656337178449b7d4fd10e56e5432f374379f1b47ba24fd1b9e9db876b0b8adddcee3f0e5776f2506e4592fbc4a654712805b0551a18cd8e294faae7affccf8b57b0a80f79219472ea69f3fe11d4d93bf59c37596dc1f2e028bef28e87f93c0cef646bf61918a581fd6d6ae28038f2c8ccfd6437649452fb74a8c1695ba357cc82ab20c9229bcde4ff3c1a2fe15e520e264317f32351430d1bab4e00ebf977c63ff3bde679d11f08e1da71bf659fec6b2ced3980adc5aa4e8bba73cf71cfcea14f0f7992988e005c49be188aba80bde34b097f7b1694a2bd2a79ca5dbd95c57822b67d232abec56723001401cc1cccd886f5f239982504fbcfec461b899a257384fb3a359be15613ef72becf64445cdf1dedd13069c25178c483de9b97550dbffc70e75d4d69683be1b0885b7564cfd1f245cdb268efcaa2acd653ce4a609ba48ea102342773c03b10eb470a3acc0d76f71b014c71176b548d632b01ef34acce3eb22a62ebae70f10da2f2ed05c3985572bd1f4d9a8feeecd18bb7d16207f6d6920700d759f28d3396c27f016ce2579d792083a8e4aed5b85af5cc3510c98cc19a6dc947cfc400c11d90b052ad0e7a23f85289b4b4cb1ca28161cbd0d4ec7d00f0523df792a79a738f9b99e88b4da277a1aeb3bf992b93f03d1eab1e8d84b7768ffc152d1f20e8f97d467a17691c922c1b276100b9d4bb2fbb8de2940b2fcb84afa9c899eb656ef629c46a5717eb1ec0ec600a4fc04d45d3502247643280abd9db11c88d35888ae8e635b057b0a670589233caa1a4098f4b4cf00c36fc6279609a1058aa014030e492d3555de66e70b99e89efc51df58b586c2f89f2624be7b7857990a050bfa8f6a0bcdab4ad17180a44ca6be843cbc948030236b97a1aefd47aa7ed4a3e955b98d2c7e7ebd650dd68b698dc43b3858a7e91b3d73573aa2447ec29b433ac6329766502dfcf9c8555bc9fca703701d4ff52fddf4c0c34e23c1f951f3f6d852d94a4f613e57307706b51f3521caf733c3f881434b3fa70474fcd97c0478d752b4cdc599bbb2574e00526bb324ee1cf4351fd71b312d134a998bf0632c57e8e172306279f72308763e6753103c8786e71895655b403ecc0db9c278096b22fa978a74ea8bb481cc8d66d0d8c825df06ea05b44165345bc852e47e0451cf475c159de432beec12def11f4bd5266f9e3ac20b4d1c8c3cb5e6e7ff351a0e3feccce21d4f820ed26a41694cf98c503ba59f2dc7d9b56114b0f5f954b7fca7e987775a12bde18d00d1b2c5b605a38f8e2fe11b64d4bb14c53cbf320839056d9c5f1509d2ccaad48b56d05cba2d9a5fc3ee035f94dc71ca91cc8e43d05d7bffcad92415723b3707403b40810ce625be9b138ce34cc3b61b94741c6c03e55bcf7b5b1a9eda2a420c3ef417412758fa548b07d72911790546c84202e0eb507960b16f1f78635211b077d032e35f81e97094643b7199de9ba17630f52c4dbe3277e0071d2d2f84c33273085187a538062a01523cd23a18476b9ec210f4bead92896b2b56c8a306ccd454dca44eed84d1440082213039838b1770020ad1841d6122929b170db4024036014178d1f676700c75e2a1a6474da78ecfa5a16dbe42e9cd979a12af092f754a434e0457f546c0c22c66c16db76cf4ceec245385a0c15edae5473c92ceebda0d331c325a02d00a27799a22d7f9d9e5c2b54a08c043f1a88c107d1d0b9f709f79080d7d586d92645a72a4712311ccabdd"}, - {"0000000000000000000000000000000000000000000000000000000000000004", "005ac1e7e9ec960df137f0750ebbef144eb4ec488904723a2d7892c80cb4a622f429301ad5bda92fd58a08d3d7312be01f473d4f16a045ef0566f0759de91c2622b235d554e5a962341290fba5d6891a0c75c8bf090f4c168a696cc953d130fb16473678ce81d16a0c17ba6164ace4870b775ea1ace76beab06fce7368180f6fa53c3e0b3acacc69f37b72b718daed04bd2d9e6f9eee43a7a587e7629698d0dc702ad2b75718bc99011a473fdc8d32d9128fb54a56c3d01a5f55d8226b066e3e5a1503945660549325e8ee963e50a05f6dc7088c28a2e9eb7a2bba736219c63f4b8e25f17384f00cae7fdd741fe02b09c0ebc881de48f39e035ee60e1878ad84ca5077bf30e0164e50c8ffda294034e9814f5262f4f1e7856bb316b983c768806b7b77df74da1de495dd2f18f917243bb9aa19d09212a956bdb72a3a6aaca7ffed6e9bdbf6f45cabf04bb3113e9ad48601ba70e17a11a8f7bc5aa07494d8a56540ad3b89171baff6b4f40f43d8eb3f53d748f2770e30dfb44fff07af9f22066fc9cfff4ec2f953611ff138995ed32a4e484b17b895bcd35cb2150c074eeb9a4011d27f160404c93cef7d6eb5f0ceb178af1d1fc204015529a2392752f0482acf5fbdd29719786de15601e1f6a33e066de607fa0cf1a5a4e100e62b2f58bdf86cd33b5b52cd6f05095526f3887a15fbcd3f78f731c25c79b002fe41eb092ef3f3bbb7037238c028e19e94b3354e35188c4c3e9cff19e655891847ebad9e5735d2d3d70eda488f53c45f230171017d2fd1d482766695b856158952a6d3e0cdbf3b38f374863f541d7e86d0dc280430dd4c654a7265aafc31b7a4584a494811eb874a1c591a6a4254c76dd947b22d794ed734ce9918570f0e64dd7412608e2fe82fc0f5aef760e8a8526574f613c41cfa16342589a9d7e666b4b87ca62c2416e16c0136daab3cf6faebc08d81e1d3ca97bd536a9a21da03516f545e483108c5ba057310d3ef4a841b17e45803821dfad8121771657f75fff6d6fe95c9ce524f5516608d6d6adf3187ab74ca3f9853c12b56a99e134c0802df629407aa83a733952341e166e9747b1d12643983a6b37b3d180dee9675501f666d6d69ba198ba90c0fcb9e868746a983be62ddb443d55e01acde3e0e2084e27ff326c05ffb9b5297b09601cd93f7761ea20405562bccad0b076f4310c0ae1c8c20a7da4629b60c4378a4cbc741636556c1a21a29537185030fd4a918d457e289a6e8038533725b24ebdc05ba7850201739cba3e56d5261bd8878e88ad94adec6334bbf221e04f092ed3a482042b17986013f6f2c15bccbde228f0bf81614643181e7bfc3f2b979ed63b0d3e2195641109c964ccace0642e6b4022f12f7781939115025c3654255c75330d333d62d16d305e23a562ef159121003eb5f03c3c829eca4e650d41b5d6894b2d15489c42f2eed7d6d0fd00760ddb646db57d2b5a0d3501e12071bd3842c480400697861523abeec70cf07b92c9e1b917c0cd19522f77d9a84d731e34a95dae27921b310531ccd7acbb0bf75b112a919b7e76afccedb2bf217f5f2ce965b6ee574cc81b7652515599db95e8ba125490a04d2497fe5654394ad60f933b21c339d20a53cafee417b19adfb9a15869f2bd3548e08e9361bfb04a1a75a43299cdd82d9d14621ed1ba07993f5d94209a2cf3816579da4c86444279ae7f10923adccf24311cfdc390514d9bad9d0e33a1248e94e2e6112a80613cde4f76e89664c719c91e4c494e226af2c3d77e50e82eedf635d167d7019d59421c41dad737e78be422119b4e3e6c8d5df923714ff735aa41709c73ce1890fa9b1859806b675c3ca911a281adf944b2fea9f5d30314e5325676e6dd0ab048a4d28e91abcd89a630e"}, - {"00000000000000000000000000000000000000000000000000000000000003aa", "0046e8aa8dd6bccde4d227d290e391f698cb7f87890951bbd93689486d3658b5d7a7d43eae85857530cd0697c0777597e6ccc603345600bceeca3c88b7c5c32b0a9742fae7e9417005a70e75cfb4e5cd631205eb020a4db66dcaebab30d17114f627f9bd54612d75af05f1059238c3374aa54153a59550034e76e3b651280496f7a79793b2b1afe061bf967777728dc25f96f219388c9d27d83e7af7ecd2d709c0589bc8ab7eb0b202db4e5855c3bc6cede5913db23b96d4ca4456797f1442467d0ed665cec38363b6e6dd00e61f6c112b941434b3b20a4a596c6eabe290f460b309edc97f0050158a2e575049f27eaaa59be14ae2510b2d4a3fd83204068dc93aa45abf8c02e8dd0778957e5ae7b8f98f0f2d49ad53debc4f557d53a01c40b70ea681ddc4d332fadb9fd9536836d06f54fe1c7117a26abcdd03353aac743ea3d8f41b2ebc25946f6a70fa441edbf633007a3f47d54c778bf0a8d68c144d75d785177d29130d06ddfe488b4cf0956bd14212fd2b129af2b71b0d10550c9755918807a2f0d6d23753df95c0a16f97101fa7ee4d5d3b110ff67d155abef48db18b6ad825a707fd1b05864e84e9b7b534f417e7887579ba71149811d5e8fff825f2cf3c24536a099d561ea4a75c03e012bc7699098fe0d29e3a8682fc6530b339129db6df2ae5bfd3521e15c723ba548a4bc2e9132ae45ebc9000ff9dcb77513ab59b3330205fc59f20895dadcb9b5f3f2434296aacc9faf218eebd68456a7499df7b6a072ad3277349989994561236c4e6bc617765b95920388494e2ee8eaf034cac54c7dff92a519dcd58d4f20af6cd1e924bdc587f327c7147fbf073a80e1f63e320a164b1450bfbbf8505a2d9c9c996ad154d1733ef2a649616aedfda7dcf71152c7eab551375077cda99344a72c899dc3989f9c58411a457c299db4b94b5aa0118686bc720a599ad8c65b02dff19f3ddbedfc825353c4512130ef31bb451b47082e6e89a9c9c3835b817101c9432db0d1341e5b5ff164605727cb7ba08641c46fed9eddc6cff4ab5574deb46953a6df776f9b4063021cd417a1c49f8a9d0e3b2b7c3359d6732a1e2067a87e9fec1ccb26e2fa7d139cfd1172aff7fe85c0f4f1d550e48d917cc23b343886c5e8abc455e1eb4355a96659d22a62fd65d53912e2d9ce2a9709a65770140a318ff14cad780daf4bf1fec9b9d30a9b315fc1101bd1140f9f267f4bd83a5c9f03e7e54c9dd18423c1289f24de0411364a157752b79b966eb3f7a08405c01ab7a90a990ed9ee6d627bce2b125979b2cf2000249dc49df55d53ac9fbe025a599c7402f3c26d7b2296943d8528e3b19d84687abc4f715f2f0cedde98a2adce728bce0313b0248766339e84f89e72cf8fafb451f0f9e3197fc7bcd9696da4dc2b84a1b12595a1104dd60c943d4b059ce8430e23adfd8ea49bd52997e139026fa5e482cd953f0115d80362c3628ebf4e2cc1b09744b5687fa64419dc4bccfd33c1286f1b684b930e16de24dcefebf8121a674feeb5d6a31f936edb5058d9ada2d316e2b972982fde1f3d7ace7ab5c10aa1835de690af2d241fb9c93a0d95e40b73749fe2baf1aaa9c4c534d96f4a202c26d057ee8e35f7e7cd9a248c2c38c91a5c319fefd262083b40092cd2bdffae60521508f04c5f4b3ad85508b361427f565005b07c91653b14f7e1cb631d2e394ceaa59dc62386c1b590d110f614b7195e7cdeca73764f9ec96f34e327f6c5b338135934268410f958dc421296ff1a6284d5564f10a9eee81c81500f18e91b1d1801e633e99281e94461145646acbd80b5f634e68d60cd6fb8f2b9d9ecb9c0c0a0b803b5065fdd324f604d36a3eb23f34f260d724d106a3628ecbff474b564aced796e26d39b57777"}, - {"000000000000000000000000000000000000000000000000000000000000000b", "004119923cced1f108e0372cbf43ee2e60f3bed7bd13a6d8eac89c27e30763e301df2dedd1d073be53110cd1f9d93dc73898462ef5650cbf5bf1ce6f3e6a4d1f610e7a12244bebc286b4e01f4c1da9d8dd1857c60f6cfd343a0760838fcc423aa1c1bc791e4b0abb4f1c2cace44dd302d8b17ad569da5f7665d2c86fc89c28c21c65ad8b5915cc4ee3d2a26f75da1d48da36c66d1cfd8ddeefb40bed77795b4a5d0512ade89d28b304ede16a2e5b443948181242defcca3db866721d6b47020b66e3191200ea80768394c552c6220c5980080c3d622c4343c5dd9a6bd6110ff5b972c9663fa26915d5d2a0f1e19c8d1161246eebb1631dd18434538e09e316be4dcf9edb96a6e19549ef2c652ec9959b3d2ea46db8f34dd53b1eda4541af7a7efa4a213f726f22ba2eb94011683d8d12065663c8376297639b56e94894ab3713d3323f2692356cc2338e8615f2bd37ab0138f3a8d15a5e9731cd42bd30dc3b7ce8496c25f41098e60e3f0c5fbbf33261e33ecc1b46164473369002d4423f541c2d37e52d63b776d4f7514ba39c99bc433b2397f159b279f32717e025d4b8b68d09b9f64c01daeb48826a0cddb056423abbaa8346d86e5f152419767c31f95cda75047275ac6d4de69e18ced487040536b9ea1f072109936f12e1dbd68439de0336477a1ac125609668e04fd82a658b82de7da28b913e478d0854d115298bb28c6d5f424b497bc21d3eddac8c2f164013e301aa9629e44ad65d1f5a39fa153751a5660f6e43bc0c5170f78100e4b3b9bec6055ccf0cda831496fe149762f50938a43232191ef0d52c318eb52308c5205ea1e68dcf3806b88c7b6f1b063641f4ad170ee96b68921f60c97b2814821ee1dc2eb9d5b782711968c17854258a97aea897e2e9c595e6269378a21e2b0675873310d9bac1722381e49e0927c3fe1fb3240073f2333a4d87009e868294aed97d18cece4f9930042c6792bd1a87cb5ded35a4e3475bfe741f73c3fe191accd72188df7ec4d451e896e307e4a0c1677b821e4dfc7d0de662a5dad882dc066fa16536dd1b17e1010605ce59e61cb9ccfc51f3275336091056dce72c08482c4128a7a8633eda71eddf9d83810372487b1505634ce5f4903847c1d340f70b08472e49e85f43a4216087a5f4c908c55191949336c68b76ea8d1ee438008e9ba8dea54a95bbe0b2c92e59788cd5ceb64aef279d3e38f665cf43968c459444576181c3de5772e31ffcfbccccf4ba87b985ea6be9ecbdab78ba5ffb47292d4760865bc5f17fc934e57b4accf59b91f3ffbc02ba72bce1d93110f0a1e0a19fafccad7194ac43061f835dbdeedc82112bb112d55f227a06058cf702b3030e10465ac32cab38b35283b5f0feea73b4583b57062b3bdb160c966ac2db959569c41ce9b9666f017e050cfe3f00c9ed0f54c7063938419135df9556b72f056ed2fa90c89b5ec41f677f25c855535ca6dcd5a70834d3307b473521236bd166c328518abf5f3a095629d9f6ca05dc1df3d77fa64d31c888d5bbf8d37e3b083e6dc6adc5e11329e042058d991c35ec379f17301efb5664a5cdf78ccbc85771525690620ae61dc9a31d7cff74965594a199aee3788c3072f97c299b173925864e1bc6da49373d80f29f372af33b2f5bdfb0c90a58f9704845462b42ca03c052c07b811ed5711a4f3d2b6b7c7b6c3139f30098020243fa62223ad9fcf51809767f1c9e2699f94477d8a0c344622b26d7f9aa1a79d709cc21e8e73509151e4155b65a2b5857a2d70fc9bd52c8e73515bc99667052775c71a2fb57f9d711f1d7894e05ec9055ae51f5ef9e2080a6686ceb051e4cfe681b7c4dabfd37724b5bb9233caa171ff9683ee989fd8f50842aea35f6a7efdf15ca61ddf5652a"}, - {"0000000000000000000000000000000000000000000000000000000000000005", "0035afde4b88e0f3cd1dd4e703b3487d6380763f0332a16ccccf942641c04db473567969be7d073b631c1c9edcbbcb2225331de373414cf6f93b9b45fd09d22c94e4bea9910f59dadc049970745932c82ddae8240d6df10b2ef89441eaead62030fea4170a7699ddde3fa42a159311d805894805c1a15231b1e598580b3b0e41575b04674925e98364b9316b2205e489de876f1399e290b8cc339fe49467b50c4d23df45ec5a6c82029db50ca4cc6ce0d37c516451b9726d70090eaf6019b2a2bf9bcc873b6aeb76b5333d71be1a669a850b1379b0ea0116d174b796626ed97d39ee98bbf7fc50435755290998ddc1afd9277e1843994e4221fad12515c89f3cad9d2ead2d45345f8eec679181a9ecff73242f956c5c0d4e3d6536674df4fb80de4f7cdeb23c2f03be9cebd13bf36f0d0518f6d8191d74c0700d7d6bd23e0b637387b5f2da075a6ac0e1f2aeb87986100479234733970f2fa06655f24673caf1c9825e84e626a7c5ac418bab251f30d2902d9b1fba6b4f57ec751a1c6ff2f44a985bb23e18de56d990df03ce9f958c335403fa5993df45a9bee3eb99d23e9ac7f83ff5120a235d9f63dc1479fd0f6352ac5a31a309e61e2f6624cae95ab814961bb57b23323b5fd1bec2ba3cc44b0bf4a0e25a24a839f403b6dc2cd074da30301f81da106aafadc86fad47ce23c514f87562f181aa512b4704b3e02d2b0dd6390314626b92f84fe5d0d434859e12492ae7bcdd8c1b8037455a3d361e02982ab709020b9d7ad31d8d56bb2aa832c367d7eb9f19337e310124107a42de2122d71f5d74a0aebdb2b9909e7bac5505d6c0d4e66a54455799053fe77987b5b13bfccc86137067277b07471f6b5d32d993afd9956515cda6b506b32d54e9d56ec16056010ec59474f641c2dd893942e5a42356187f79707a590882f4e4c33aa67f7e0f00469f5b3587597b4df960bca2537872b35addc1851a4af9b3970dc963bdc101b754166f4e993114f91801516a0e885355b1e55f00f7c7db7f14e166945dff18ba34bdc15b9b4fb96da284b95277c5047fd95140013b2f5564a46aafa39df705ed748a31fc4479e28724eca5b6fa0ea3372d81c261eb9ea0b0db5aba2baf030307c9401a2a9eda1908bfed5befae53a21c51030df559e94460859be0e9c7b44e70086e4a3636e1840429286643db06af1f2e12d9fd599eedc73c9c4e655328139cc8d66fa9e9915564b0f3d2b6b1ea9da8c20aa0191a7d82b3e70d5911e0dc9d97b0bd30313fbc365d72402438e229f4f033b108b0bbe1485f14f2a90a189fdfa251c7c29bcc010c0009a30dc163ee8db836813c43b456e9ff4ddef6aadadca5cdc9611b31700acffedc7e83977ef8a7312cbdc36a94d2ab771031201d8539ba217f494abb1a65df7dfe8f15d13d570f07368cc0cf0f97930745d2ada0aff5632a6dda46c908396a1dcfc23937546140abb1bdd30045c30670c133451368932f6081c9a0d4aac16a1b8a024410e5e5410156cc855c2792fa1926002be0ae2e8c14b565a00b3b0d25f9ce0795402292a85c665e4585f20f75ae435a2be3c910d76e963a76af9167159eb321bb941f0d356dc12c912438c4ce259d9af1aaf9cbaa96d99814a38dfca7567b72eded81fbeafc0c6c8aefaaeabd07bb36896b46dc10629190bddd279ed912edd44d644023be19279065ee86fd06b7dcdbe68293481dc5810d583d8f388d800352f352f68121bed788a37cbcd412cbaed54a2334d79917f34158a090f9cdfbb4c9790af073ee42c929076919541b805bce268c71f4bebe1b63622257b2b475b8418521a775eba1a3d81f8ffc10294ef4348c9bdbc4fef2d5e167f9e9ba53f4c6b92e4ba560ba8c5075f97ef5861e52f271a9d7f45469"}, - {"0000000000000000000000000000000000000000000000000000000000000004", "0045cc55438ec569072433ac0b4a3c314bad15e9ea0bbc1daac7914ab6e487c4b90a460e016fdbbb944701c21a300c9d917daf7600c93f55ff0dd8d2b8ce3e21528deda497b17bfe7512751eac6e5920afcc6c1709c32b821c96d679a48db3c17273ad16251c3357b426405cecfaccf9678533f328ad2a6f90fe8a5ea626173a0637108c0517cd6991f5b221eb989740786d991e2793a15497744309a38388b5bfcb69cc9ef915aa024229a2f59f9103367dc777b06a928e8b551abea9118849e101d3bbb1677e7217e4e265a5e306172aa827957e826156c243bd82234d63249c556328dff7502f1f69ff90524913b4a992f5124054c61414d0b52209371555831572b1dac0b2658942b6a17331f16b1b0f1f3a6e93ea218dbf5047a2024261067c1d7c99d109b806bffb05ac79e85e41c2dbd14e3eb68c7739a21122bbb0af0b06385d9a614ec3b5404064b3bcfb0f0626a2fa0d0406d2d45dd587b6ad6f4a1039d5e509412bf7a354e14e8b55f624cbdbcacb797fab7c6a9320c7f467eb937efcc9f6a4b8d83e3faaeb8d590ea424519c02ea0aa6f8f15f94fafba840073b149fe4ee0b53d112940de10d7e81d12c758cc2e986974d429b2856e38d8866ddd997f8842b4af6c3b24946761d3e1d57a1056234c2b7ef3b339915b24b21d49db365ff249d6459e653465b52e2b2a03828c6e20116746dff06418b11dd87629c6c8fb0f69a2334d99c04db69001518c56e8807bbbca8498230e930d1f93186bbb6c0080baf83c76a34d3e59f0462fcb503a9901efad6a01dae857207cfbbe6ec4114f0c7e9e1befe5d1bab510f01c17cc08abbb1e41e5190ffc0f0d770dc3caa411c3b4b0a471ef053fbf6540e34b54b96614dfaf319174b9b81b72aa63bef1255bc2fd792ba3a5f7c82983b0753fffd54fab7248533c43a56387ec32b36e10500af84affe504a5aac050125f8e746f246d19c3510600436ac55acf3198e9e673726e95389daa4b0541c146bac2644190d82f93fd2a9ed1ddf271a2b38fcdb1e5b5c26759b39c1881b334638b753e15d50f8b41d06f4d684840e94957e64a227a9c6417d2976b5b2b92e11251f0ae568916cabc307dbd3e7ea8a67f58b470916770f400c7cf26c5f4a1319647a3ac02a1720e10caf8476dc0e53bf45d77395ea340e6e3e061ad15d08098598614aa2d8c7ffa1b942cb1bf1f435927b4b0f5eb5a5f790e97cd47c164a7fc8035ac63eda3bf60ca54f5686930932d1e0a453ad7c4dd183234e1f0316754dcbf8cf1caddbabe2b5c0a54999a54f1f24821c2004e91218a7eb168af5b28d7cfe65dc3bb4417e3559fea3f6a1d1156ba4b446093c176eac4e57757a2efe45bf904c83a0da2303a17ad0b9e340d37c4d7932b19ee1988f39f30ea50381bb6c26ceb92cddf63100e808c2f666fe87c609f6e7dbda384a2642bedd54085b46ae4cc503a4b6e9fbe1c8720797f94effcbc0051f4cba5a18ff1ba2e2008ad01dbc255f37548c9908e3a3e1bd19511106fac6542e7de6be2140b8c8290a6df3dbdc03dfe557e166070cc19c073fb1baf7010e23cbe9f4c578a3d01a51b378133655616d6cf16d159921a1bec984e2e62c615aed7cdd1b0d27fc3c3f61bde50d20a358f5cab9e98760f9e38f7ed97f90b8074d05736b43358d8f0ae355fecd12f61dd89a29b0272dd7a3f013bc4dd75493844cd7f97ec6d4fbd2b30e0686a9d89e5481fc5e03984af7952d9c6a7840c715fbed41c3768483d69b0a2cea6abe5719c4b9c6d60967ef909a632079dd6b36ecc97c0c85e596bc0fce193b5fc2658e9352e3ee5399ffbf5d0e47013ec77c1746daa6791473e3136a8195f95ae0f4c2c6eb9c101f55d586b15953c57928a60caf4abc438debbce892"}, - {"0000000000000000000000000000000000000000000000000000000000000021", "00b4902728a8a8db67b1344baf4a1541ecf8b81a13497b3d1afdef05e987b8371d61bc7e91ea3d5623da211b3cdb2c7937e1dc8ff280a14bbb2e1ab93e1ef63f42443440707e9fa9e9544cb3aa0211ad11127de20510adad4304c3d22c7896d03642b7520650dab1e440815ac4565306cd56af65fdd8c4e2398c5fcf921d092a3375fc546f490983514747c08e0d05b93cc87809b3ed5caf6f3cb9bc0150ffb77d090646df553a8a05da226fd5e3e28f255bd2c3261eeae68a59d50b890a6d0c15cd4f4210c67500f4ca5f3ceca4eeb54b1f199ef8edab49c3438d29a5882e51555d95911b505a1e7572ef79079f85386bb2aa543cd6bcce024964040e1c11e176f6bf41f06a8279d5278e653298cc5f45174369adc50e0b0923c8522c4197ab18ee88dd344619869faff6e7a3ef6ab48454e34718994dc35517483418b334a1ea80cd78fc2347f6a0ec869485d8e8cd011e4876cd487ab6abba1033b85aa2ccac03cd8bbd1b8674e18c675e69d85952d782b7475164e4b684e00546885da78b59bd3cf1c22b15593ede908e56e86506f46ef8eec72c65d028c0fcda75ff46fa77f8443a04a02602a9e72ca9ea5e169e94538cd2b28279b7a50fe890a963499df14b0f2397f3a630d5562aff50f00674ddac27f82dc3d8c393d36620273a385b5c2b760dff53fadac9b7ff651d555fb1b47f8f01939f7f4301f8a97f6aac5ddf951261176acf434ca0d67e16341bea85245eb5f345cb1ae2904a4f2954eceedd30d914e32f30c4b2e9cbd659645ac4ccef27a8111eb6da21aa258f5812cb3d94ae13fa2c2beadd675b558cef04808a72859cc96584a32964a9cf1c26803df5d53e0667d5edd244e3a3f41530c428a069b534fa337fae113c9734d30ecc8f488b1238e4c2a3b96d101b6ec95ef9a6b173d99401feee86f0dc6fdf8f3bddfbcda1018f8fd12e50dce74be42313456b8334fa0051512b22c10e554fda2429ab2df2713c2d1a624511366ec72495b94943c94f367e3e83dd3be2c5610467ef0ee228008798fa5e8d8f5b2673ec00791f82fac57e51310924ba88fc28ff13c9ef7454dcb9a3265d75f31cac1bf164e9b752d691346c3c4308f62ddb5ce07ee76e101d19500bb571d5e40bd139d25180e328f439960e5ee2330143623c8d9e15263638bc6f69c94035ca4608b0dde2ed0908aa84b290b77213355d42aa35abc01ae7299e7b4ec649b761b5f02db0c24997c75c23d22012c9e4acca4f47d8d274362c63995b2bb81967cb26b4b7bdc058c61cee0f829b7f60bf64e8fa2f4a152bdaa99004a90aa9a2e8d5dfb6ba073a269c3b8a9f46df4edc7caf04ebbca7552dbae525ffc2bd5f68cc352d1bbdf2d07bf79581337650a19a894e11d91e144397ecdd70e4be315068c43d416c323e38eff7f5f20191bd02519c0121eb6fd3027c3403b1b527b4d9622ac3ea2b21e429c3e085f57f4b340136a3bfbc196505e729cc84ad8373a63a926acf4ebf9e5a5e9edf011b6af75e98b7a11de8a19363577f034a2b05f467c4061e6db182b8cb01cd7d95f60378387e8bf37e09030f1629865f10dcf0e4fd24f3d557e129894232db240ac8be642c4bd483530351721934850a1782f74c7b6cd964442c65acedca5ab7458dd11a3ea721f96be80582d796e0440d287425317bf9478cb4a396da9c4a11f4f2f4b10590b64abae528d14267a2fd2c5fecd417c9c4a0ae5f4c91c22cd7d037e494ce972dba0400362fb5c5f662a7a78ba1252186d5b487a2dfde81ca072a52dacf8c03d4ce1f02bbc5eb6e81cf00b51b4420f20cbc2362e8a34dbc64ebd83ffb25920bfead7435a3ed98dddbb9bdafb0b37653eddd6eb65bd6e34a5dda65bba828a4eb8e542807f5661ada22497e01c9"}, - {"0000000000000000000000000000000000000000000000000000000000000003", "000f32f3eff1e9a9cfb380d8ee306ac44bf431106c23699a165dadc6d184dcf3c54bc5bc6da1192d1b3004306658e45998cb67d5b3f82b3a4f39281bdcd6f81bdb790c5e4704189ff85278081f4850a1d6fcc91403b8677ed8084e1e90f16292d9c604d92b16ea840432e2ff8e44d3c26bec3a769e11dd94b5b8a5dbe91905d0ff96830fa8ffc78210725eddb200638e77acab0aa2339544c6fe96b020a657e5fa1671bc64926f1e0e39defb94dfa6c920ad428b4228b155855b8d9d251e6042dde91482c9a69152ac82afb39516b9e9a4261d2b7796c7aafae5cc87f6e778c8026e45b4199bab1fe07fd3e2ce325699b968a2bc4f20fe799113db890ed66ac04954a19ff8b2c18ea31d17550e475a9b2442bc0dc7dc1a920743d795e219bd38aa880317dad93d900354a9e4648b842bc81a25d7446782d55e248e42dda29a05ef12419dbe49d9e5f8ccb78ef7fca428005e79a3aca4018bf75ee0fc5a6d3e461c3f1a140012349dc5ad4f8d2ac200133c2620fd6a5e0695d57601182aa49d27997978af144a45f4333772bb3eba67113ae1f32b0cf6fee17ce133c6a2415cac2af1a22a03d16f75ae74fb53d617f22d431e82bcf84fbef73808158bc8aac8a898a249d77de8ef09ca481c5ef8441663279562ef9555ebc59a3c7a63c7736e99fd4bf617c65490f74f2492b77ec4226d281c272af33c802b0081689404512551582a03e1524ad5215a9f9c1bb4283d0b61fde8f155a2e582ec29a2016e5e7d3bd7960cb2e566298d3790c0f7e38fbf271ffe4f0e58b61223715a68a6997fa7bab2d30d7570cecd1b1db314970253d209e64e610514c54374542723c6cd789b9e3f126e5ac2ab77ecf7de6952b789ebf29a96b0b606913b76959e3c6f9d8f9bc1e5e95bba5d59ed4ed58b77497a7f8b9a1c4591ec03452a09b4fa96da1099813a006398a5a105de7d331fc14ebb2ff7f9727af5319d229fdc67271d057b7360f5f2dccbbbbb2e40de7aa604ae06cdafd07120fb50c08a8ccb51a42fec2478e50c3327a90cddd4c78b37240b0c4a2dc51110798234015b28cae7146d1b43f4095789d3a3265ed7dcc9c3486f7d14f057a9f7e2d24a1943f47452cb40da643709647a2426826c917b4701413a2a3d78dce02c538462aea6afb39bcaef22d548cfdf65d4265d11fe943a098cbcab39277761d2da41371cd9c6b679c5165cb8714a5c1d215f14bd227b08eddbe17ab75b0f7dcfa12d0fa56395d24e97add3a3ceae5b67fa604c7597813b5f39dfc05564d0fb4ef4cb637393c75c95bb77060c98fefd99498812b573d8c1ed5106a673075acb337638ccc40a2d894981aaa85335df82775f46db3d0f21e0cd3ba48db7537810973b0f7dcbd5ebf63d71b4a3c00f86032b32b5769f9debc87d9dff8281ddefb201d34ba6a991a0796ed0208c361e1f1a63d5f861b40d29cae09b9e4a2f6b9fb6fa47da12b264c354d93d06e44e5e744b004b967a047d93ade54567f3369b194790dede375a9b391d83ba4c65ef7ca29b1fb743d2056e53cac90d6d7fcbac352ecdcae66666da362b1d5f0c83165022ec355c4f480032705883237dff7775217fce56b9dd41634a47ebd96af5051bc2ca1ec45030dad36ff1e3e3955324238532b71bc144de979772033d472e25d20c5e964bd2ce2ec177420a6cd6aa313f90c316a738405bcbd6483618e9f20a0e4e78fcad0525944d3ec20a69e85fa08aed1acd9d8dc817d0d1384744d1d0e7dab599b07a264bd5518aded2b71fe404d70c6c5d6bed0fa89501dbc93c1f30be76fc42832b7d77688d0d8db3c3c4247edeea19d242db524ae9129bffd6014a15b8eb4ba1abcf446470cd824c46c81e5cda5bb70a69a31d77b200f95597ed601e0ba72a"}, - {"00000000000000000000000000000000000000000000000000000000000003d6", "01547d9556dbf38f94c2c76036fa35c277563aa87714b0bb9f2a89eb5cffd0b5d7c4dbd1ba6e3d98c0fa0e3c85fa9e8d0e155e7bb472b5a9ca8e43a9b8db304889faec5016840584df3580bbfa2d3e057b3b22a20347f399c5d00a6180b292c8b9b4eaf8f9407541c029df33f6462e218bb8acf4e88fbf71024702be3bb60ca88bebaf48673a939cc17e452c115dad937448bf26a41974cc4b3898f5bd743b6fec796b32ca5f3aeb0dcde1b7ebcfd78710d43a3b265a9aa6d7991fef692ad239a4e6e86d95e52d38e136e952668e8815d7d412d6f7a1bb93e83f4aa9e29554534dbddb0fbf22f115e1e36f06e48751e59a8313d67d94b0cd6c9b24e11497d93988cff91927f13404233d8cb2745cf4de2e511a7c209cd73f174188c83fe0e9c63e70fff6bbfb3654adede531c5239bb143e41fd6c5415297bdd3636e3a54d003b20153bdb939533fcaf16f6d3a5db7a301c2a2965fc2993cac59a19b42dc63c8f4a5ed3af7063bbb0a2ae29c0d23d6e1c45d9cd73d16b83f3b0001e1737faa20c3bb920c74eaf6e3f58a4ca35c1db22136f4cb7b0aa5a49c7734a34d70a0c56af18d526a028656e6d2d42f4d7d39f3f515d0ac4d495d50b096054c3ec74c8adec87ed2b2c20cdd56aa8f5d784e84148342979aabe26bfcb3986b6afd09a71225dc0ce11c2fa326c8161383212bd21b47aafac9a60af05a7701f8ca2b6e27f7ff91210052d7f720a0456844f1830a94bb1d9c93354eafc07242a272dfd75feb5c8d1716ab6d269c77ccfdde2079ee19efb5dea2c81d2e1316b03ee737e36463eed618fa7359e39fc80adeddad12c910b770cdd132b90f91a55ea62e236918feeb4a42ab5b9ca92bd263870cc8adf1de2cab4a19ff14012414b635b1dbe429dff8f2fbe3bf65c4e1f59671917553c7ea0670a023dbc54806ef648a8b2fef7b7cf80160f9ba3b8a584e7dabb8cd2475b9c6c490f8c0551eab1602a8de4250f3dc2555887543c68a06b60c0a2a12b60ccccfa8d5e09c2575e1ea917a5686d4b48b746b8ef0551daa3326f1cb5e4a6a38b70737dbf435097ce68b26452b5d56c931995fdd3021825153ec4c0bfc815bfae5715567a9210facf8d0bdbc267359d22695acb3439157e1c738d43e885de3d15a0c8e05b745d103e4b024190ba875857f113ce9ad72e43146cd065ac17248d86017025e56fdc7d44f4ece6b9cabb92daf25dd0e92e14d83eee4745bdd17996a448c13aa17aa5de5b9d1a0753ee664403fc5ec626de3dce15c38d6df7734d91d87a17d459b733bdabeb22cfaf96009d51ee859d42760ac4d2502a938ab06968cf536582f872ee5b9e536117036d7236cbd3609cb359949e810181d2e52532b0ba650d70f9ee4c1322c09fa427a19ba13194acaa6473cc31248821cf22ebd5f5d7f6704ae7ecc87cd8ba2e11d6224ca9580a19c3f18ea10050671895aca2bb1c88131f703ac64c88d31de9c4007ca6ae713fc5991e42a9276393a448cd3c6dc137f143ed4ed82ded4abf5a215d00e74d72610225ee8e72bc71ccf57207a2f8d01062e076995b6a1f0988e7e5887dfdd1f9806078f7c296c375953925da7dd0a4b30c4fcfc02e253c172eb887adcca02aa2fe355a7b7445d06e9f71a294fde89850ab2fba51303d99a8281095ee4bbe2576ce5c9d7b0a21aeadd25d8779a43b414fc4b5ce4d6bac558d9c1e040c796f9f0551e675832fd375a2b920d6fa67964df61f1e68a954afaec8044abd60e30d35210f8bfa48785f992e58fee3925050e566bbae5e1cca39ea48922cd72afab0c5e3eda043fc74d23691dda518812e462f8dac3a645f7dcdc071b3312174894bfb382fc63bdf7c7f9d5b371769bd01d63b614844e64f58ea50732037a892e7880d9efcb"}, - {"0000000000000000000000000000000000000000000000000000000000000022", "0004030f316defaf9df74ca4d571f86b38835d3a8f0e003d42f55a25c5f2a20ce561748f836a02fb932805c5ed5f87968f32d14f30fa97a8c84cb5dbb3945d07056576db6426f1f78cd174b0b54ea4cc3f79defc100a3c420629d717a55b9164a83e6e9467cba74d5d4c073e64031492c3686f6593dacfb48e1099b82a4f1837036f8d4eaf3bb41622a9799aba00ef6be78d3b18b8f0cce7a0692315e8e18f36e6074096549ce4c9000ead6f1c0f29438e4c818c315681b47df6f2d541088aabd003c9a5fbb20088065ad19ee35364de556630f766eac192a9a390c43478d851b793743ebc4fe831a697081a7627c7f493249e1db07825d221354e0206d8ab4de52cb291a3789124773e16057890bed07012aa8b6efbf77579d3de913961d869ba9c2f5b6b5a0e98712f48a4dd39c07623b5abffe9a9008c4aa8e30eb4fff8fcf3f611c92c9323be76bc42700015a60d0c6a6c6f3d08351269ef76785a447e8e8deb7d319d23711a88560b6760f7fe32a6e272fa21e4f132de61239c9fdf8b0e32baf920e24eab7dcea2d57c5851b52e08fca69553a7ffa0dea34ac53bd6930a76ba5a3b201accc3d9c9cb62d359543049c8ba737a43ff8782382926f6e611a7e55511263161e72a33849a7fbb1c2bd0fa6398d4b924d274c2c1f47f057d35794efd2b2f3c856b94a5f4ef495dd394886cfe1ced5db871e70da73ee9e4092ddf11d923284dfb2cf2c5dc37a8fc10bcd17be84b65b56bf974a63bd02ece81a8bb6b6d19ad07876e2dd7d57451e385ca1c9ee230567a810f1fd98dcfae4ee88eacf7126004ab777d6d19cd3d6213ee528cd1c895babc2c44d5f55270a2b785dbea9b5861778dff9a565769eef739b0ff655ebddf99a07b18935369318da2569d5352cb765b7dacc58a1a801a2e7839e6291fd7a10f93e31dc0fb0d9938592a4839011b0f3afb4b0534e51ca2416fddf3f5c890f0d06705c1d445f2776da7f28dc5cfd7c1a1addf5b3eca8a18fd2dada3da8987337fd278b82db151579eda43ee38b60ca56f0ee1c2a727f7e19dc828da67913f0cd60f03dae010ca7f075c423539d2e25b7a4114deab1b104bfd0dfee955856ff745efd0b510d6270431bed41dd6522857dc20798cdec92de84dfb4a9888ba26ae277696d9ae948ba9e46d749b91d6ccc16dceaec2dd0397ca4c96539528b5f5f612985c444297569674e819e6bbb983ca255c5ef88238aff4f54215a11a15a945d477af3d63852b622dd4aa657f9c2616b11b72b150d50fafc3286e0173d898e089637d5736b4dffa4d08af68a42b873e819161c5cf2b71093575bd175ef6211c3c95708a770b97cd1317379f028e0c3d32a9390dce999ebfcbbff9e019815930db0f9894f82870e52fde36a1e3991559f3cdf5a449bedd76633d36797a0432943863f69fdfefb2f0a3a805ef8ab3b0db81eb27f4634a8dd6187f97d1430a4ce7684cc4c8efbfeb219a738bb1a1ad57524c94419cb1d3b5f143fbb7e84d392d2ddb96a57f0095861655ea485a94241ec7100ef1be41a91a429feefd1247f75f61c697153f9b231a539948562bff157485b55f962ca4ea139aff0a1712a9cdf1ef0c5b37aacea28ecfe69ed62f5391d05313066d0ac7e6034f640c067d324d991a5040fcd34605edac20bd09ca27af68c77f6cc1a0c2c36979d0530fc62eb2c25e962944b1a428afa316a920dfdbc68e1bcbb1a38d4be191a38a12a7d1248ce4f0112cb3c46938ae654ded4a29e9ca86c58b4a423e714b5b9a76099f432143631e738579e29c5ad5ee8cab31cb6b630d2b473623ca5d008a217113ea168b6c694c252f322eca52b1c1665f87bcc394145e52ebfdf5103132ae3025e50f9d6c55b9bae2081d227099c7bb031fd331"}, - {"0000000000000000000000000000000000000000000000000000000000000006", "007c0779e65899914a5f2305887132ce6d59fd1f8d453843ae6ca8b847f3a04859657a79d3055dff8ecf1708b8cfe74ac0cea6e7f7ab8bc02b220a21bf13521fbe964eb52ae2fdede4637c13e417599da8f56e341a3882542bf269d1c662359afb6d48eaf0bdb7b03f31bc73c6c497ec497e45c3c260f68315a5f23f881a2b6596bf81e5af698123864be236244f3c7c7ff0553236edca5ee270e3c8fef886666c6f5f05a6fd193807b6c4b405f0dd6df1c190d8ed09fe961d389608d727dd54098a322e95eea025a345496a06b7a63cf0490ed6153f39076deafa13393240d117fb55fa9d04d8275b0bac9250f0659c9a046f24e4a83a74dff7b36c165b13766d91fdf5713ad7a6f54e4603b966df8bb020e00b3c11519b553c55353613e7609246327d934716e2df91a9166ca11e7e31f745dcb628e58974fa9a212f5f7eff13590ce3dc6553c749583edc4859c27d013b64419ba28325ddcf1761d4722f2e3c733c839d5a3b5bc7ffa24ed58b10c63676b3071201c7117e0732696215dedd240572b4946852684fe55a3b9a30a5336569e36579f1efd05d4557556ae49d7b3c1205c4042dac08f319e6456394817a17d128e0700f76d29511d13b4c7b5cb8dbec21657305bab5ae4720fda51a0f86ef9a74f38f1be70ba3c29d6f77c98e9194412428c8e4c2884afd3d03e71a5379f91e8ae032170ccc03d583fd231518a0d5be37caff7b694e60a79e96eb0ce0953243cf059ec95e12a47456fd6a962f79fba40841489643d3733ee64352b2caa43e5136d8d99c511d093ab292dbbf5d6065e2d80b9e6b557f73384100099488c8abd9e1c9e391d0d886bbb479bbef97393a1011011e13a234c73ef5724420d0fcd26e7e99c0800a7e2d1194ad9da7db46718ff3c056548b820b54b721c18ba54fb9cbf3f7d8f33ae31af82a450454a67d00d37174ae62eb6130da52dcae17f958e4ff2d21931d64174fdd4a66c33ac4925b473a4da907885d3dd30c11c4e32792f5c0c79713a1b4723c8917b077fa814067968919912c71e2f587963cc9529df70db356910a4ead4b34846acf14d296e9cc6bd555cb697851d614133acf2155c891e001654cbdf0d8cde63556736a1391162c5421b23527fc8275ac7369add549ffa47322c6e3cb45d9fd2da85e53942733d85627549590a701b72818f5f2723f9ee0625b3f303a5b00533dde4d1726a386538bee79c838d58ea138b045708a7830250a7de278a4b94949d677b2003ce2b8adbd14f6e1c10b7afdff7246d85453bf254ca14deef310d49c13bd02622932dcf163effe4200ec549e10e0bdfd7b303e2d1c0266fda0b9b71461b2f8ac4298b1e0d417af6c165903ae52f98b35fa038272971e69b6058896c1132278d16cff57e0758326c2c49ebf0445eb32b9542d0134615b3b27a469f93944718efe20972fd17994f5061e158d0a25f09b621e91680a280b8605df12e8e216a739d2dbe6b867790ae242597a61e0c0c8762dde2764c365570c8bc0a3fab2a82b5e4f8227e176b55d0264f666ddab9deb6d3b42caf9993b39286fbf49d409561b473e7659cbd776117ea0fa3325b31e1d629832167c7765f4b6b5ddcec89dcb5537fbdb81df44405f535304e55deb6b838566c20b37dc839ef13ea11803999dfbd4c999cb018006dfb2f256a20f3b5e062d2535d1b58d541c8eee061556ccb7bc3d8846998348184cb4805348760bb88e537240c949c8f3708ae514254547f0fb57afadc6fb9754c1f877b24e737903ac0b31c3bda8d46d6946920953ab745c7f83239d58070b7cea5f7ade5bb960bfa201243aed52cfed97b44b17a4cb2e140910a28c74d1a4a3ce0149e5fafa104730f443cdef95c336efb5754f7abb30c1c0315f97b2"}, - {"0000000000000000000000000000000000000000000000000000000000000003", "003722b77b8537f5c469d0e521551b484a0279cfc718434a3e86d3a77bb738127d77a00e45432731433614bf7ac9b3f2d2fbc36bb193fdf1fcd872eb4c404f2277ac8977d97c28eaecc361307c6b277786fd62cb0844d2069d8f5d1be32eb3235c2117355e543f95850bbee1ccea0bb485c0f33a72385a355ac208bbde970f070f95c3abf0b3785de3f90b40bd7f629b9dcc023cf362029c53c40d1931346ac55f1641810fb076960e86e7f919482b5e8f13a3e43c61606d272a7a952917812c10a51ff2f5c94e0572c6b8744d5f9254bd2f3e2f867ff69b7055e44c8521954967cb1e487914c05f455d7be7eaf1115935276620f5829a69e9f3c4a31239bf92b8ea9411d7802163d6dd8a24b68fb700f04d13e36ed4d5ef2fdd3449ba9cecd24f4d1cbe8fd61322cceb94ab1875d76891ee4c550ca2504373e32e2420cc222da96719df87d32319b68c5149897127e00586abed611ef0ff0ce90171823371d97bc656be8c1df21f729e289bbff49b45ec95c11b2da78b1e042906d8dcaa26d95cc3aa9f755d92644095e902f692a63feabfa5226afd4758fb848e84f1bec14e73ac397608815c7d288a036868bc81fd7e77881d9d8c8fe64c11395e283b1a74dbaaa40940b065b16eef53d7bdae0a663e902e6efd53c2b923213e44ba15d62ad99cbb1705c6c65fa0c09fb8ed720158681fc9530478c600077138e051121519a46cf0ad2df662bda3ef9f0b9d3ae92d19fbea5b9d823ec4f846cb54be195d54d6670bf4e7f8549eed7fdcc6554083d069ae1842350f7a1a17172e5b19b32fa556741f6a7e7aef1ec01b40f80e0eaa934f690247e90f2338c733e091d0c333b9f70fcffa1852c64e8cccb4b24e1c2342f4d5fbb842a0145de19fde995c6dea095218119c0fb212c3575eb624ac8adc1289e6bdbc7c663282c807c9edccbe354401460c0c5a22c31fbe6bb37c22e259c5e10af1cf7e0f45888fa25b76c9559952c020507c4181e995def51469a29a98091ca44a5e64e5bc3d60b7300b5dd32c1e307793e65dfe8588cb17a33a7a8fc26ce33c478906ba35f363a43c0bb394c5c9e3501e432a11fb9d580dfe316ac806a2dd1b2924d207ea761a3758d239ca0fd7ce93100fcad18b79343889ef032ad6dc984e7a2396dabce695e9b5f5b417ad03e890fb3a3f3f36e104693d1665988c577bf0c174e8486a7eb8a519b66d284255119058a733687ca6e90046e1631a1d3b223b05e4d192490c5d14c40a6494e6c8689d5714cbc2bf17a63d5c0206c452789315270671ed6b7af9bc473e0a9fd16055da3265c5c4d2dda07be40ba3c5be09ea11f5e7ca40ac73c9743ba1245e09b4927a54ff017519ce0a632a24e0fb773304b509679882a2bb3777331acc411f5b93708cf4d499259b4a09f3909b3cc7ea0328b050b9461bf12a6215f8dab129e5c4c8b1560910e1ee004d4ad22138e871da837ea47496db377f7013880776574d96bd9322a13e668dd00ccffe8c409c1dfd0d4ab6d1672cbd3e73db24cd41ad800f72061911f5a55802e846775cce085844ec6e1a3fdaf367a43b3dcc57c956024ac64bd66975d15ee345509fede13de83e3377980cfdb6834509bb5e514a005d5de8e0405605a61fad1db5ba44e4ba22f3f49ec7eafcc04a133487243dcb85f97c84617f53bc52fd5cfdec7c3a29606b8081e36005ab4e6829f662d2424c0438cc5e200a27808f908b85e907459c5e51c172893cb942103da90219d0cff6e4d046a42b4451b879605ceeaf8b13f91376601c856dd97f351fdccc84a3536b1b03e62781aa73412a4f456e1873dd357e0d3907e9572fb33de314a10f922257ec0dd5573ebe5cf9be23787f133faaaffc931405057f8f946cf7c2d9114f7f6be357"}, - {"0000000000000000000000000000000000000000000000000000000000000016", "018ec6ba92014dfd68d6d702e5d92f8612b957060628b6c1c8c21dc6d4f9206421c6e67882bbf77b1ec71f7db235daeb8403a501454718ac7d1b4064ba14bf304faa7d6e50073687615304fe783834f8767898450764e545de19a3c331ada2e6119b1a28ce85683af60b7f7ffad94345deb15451f089f652c5f3353684790cdfa9cf30d288cbf8c385fa61e6d47e5376fbe2c434b7cb21dad7f06124cd848abb7842c587cff7f0bf026a6b75558533c196f21419c3db0cb1e88a3fdccb1163a39a0548767fff16415efed8b8b93a970c35ee04b924dd46d87a6b71c3633f04226305e52713101a167c5d52a0fa2ec3ddb7746c86b8857a102850f9c205a8b58cb60deb23c33282a2e77f80411c9fb517b30c247eb508cec00d67c451bf8659d309ba962e974526fbebe7fc582fa73f08c55015fc738de47096387a32e9fb691252ce3333460b9b2c732743480a7e447a01cd41e810cb2e292f18e1897af3b975338ed3b949215bcce013d741af5c8d636c536d725e2cbb1466420d6e08d1f4ecda478b12963e96f6e67a492fd3823213497c24035e07f957fe11f613fc61de198d9651c2067e451d63c8eb39165ff09030d1630046c3d2b1561f95948f78ced4f7019ef5b272634ebdd1b3fe319807702bded3432745453266332e650e3b2245de303039cf6eae27524e8771afe3cc19c471924320de3bb1033608e0bc30d17da136229e14dd44b115e50e85ce31b704854ba97a278d86f57517ccbd42694919df20058c84492fe2a8a93ea0f76b0e5c302b8f51df485320834fc9a09c6c7378f0327c0dd85b0de1435a931e049f78d6044afe6bd4e1716205cf728cf95c924210218b81adc0edf99bdb46d4d3dae202dbc6597f679d1870bdd9740a3e45aa2bc1c0c453499942a5b5b41f1f3155b9ff60432751d1a4dc975018ddef5197e725027df477974633706f01d1e543ecdfa1b3d85e3c7b06a5bf7254d3d9491966f3fbab34f2652698337cfb067e3d3a3459e08980afd759c6eb7365de13bac7a03fd2bb4731aff64be614360d3c63796e0984787828170af98c8ca6bc73b716725ffbab80566df27d99911c768152ab507a6562c5436f267f94f25713dd0e571ce60c875926a739942be251e0c6d6d691cfd52b712462c48e4692d9c0ff650b67eb7de3df3d4a7ff70c04530304e593c331a7a8e113bbad358dce983ca5570542cc5326ee44277262b9d70f59f62f29451d666b315e4380e6ee91d1a86463c12f6a6c1199e353f7fd57a507d7722bcacdcac31a43c1586ad296c15e91040a94786581e57a1555701413a2e7f34acefa7c2e72131804b60fdad673410f138500c4dd828ab39fc1b8178365a484e8e00fb62cf2c4d4b2e87da72a7f63261a87555fe867c09d9837932ec8fe5309192d8b65a1029eadcb2b540abb865ef2aac5e3ebcdb9d259f05f169e8d3a1f54187d87c581f0666e6373bc471f41901b2b5bb2df66ddcde0c6e1e496d9ca552cb78b9ae84788dd26fbe951fbd28296cf6846e72a33de59a64c0c2d8cfe76e04227181613f56e4486e99a78ee84da2b5e735bc40c145d33e324b76f628359e8e5fec02e123410bc258af427bf2184797bcb71ad8a8852bc5512bf3481384aa648f884e67ea9c1198b844b3d80830526244a544e52a690972324f827e1324a84bfaa2c422cee4315612215327a5bfbc17b79f33dbf1b42150ab782627f04a1543acd75b000eb40258cb3373ebf23288e5504efde17892814e2dcdff8f98265160a7422e74fd8a81a0f0daa8d677c63cd53525870db92fc4fd1b5311c1eab2dd21f46970dc8f3a5cf48aea80a2de535a32faf987594080a3b8cedb1df985a3f0e1c6ae7354e789d8b272cc2d76b313fe0c32d921a121d"}, - {"0000000000000000000000000000000000000000000000000000000000000020", "00830f6275c3027f210387bd9a4ed45a46257cf51a3dac95cc53641a75db5aa511bb38374e2ea699581f1723f93ddfbd62cff22d722ef2960ae65bd95c928022906b5fdf6469117aa97283651da1855e57972fed03333fcd0a2446c9821aa50f59e2428f4ae49bf8230691f2fb16ca48c7f9e44294cef8e5860a5b1e82e204da5680932d6f6ddc1c01809d95eb38e145d7fb771bdd7e877fa3281f41b3858911ecf1debc2599533c06f2793cb29bd029ca0a217416531fce78813b679c0eedd685cfca0579d1a8b4d2a1345fb652b67cb2cb0b5cfaa1398b54dc6cefa269a29534f6f1449984991af8635adf489d35a5aed8864762ea0ab057d7747407434e2b9c5a084dc0d3d1167bcc0199620b6e273f0f236516d5fa3bcbd7f466517a3a03fa0ef3def9c1134a21b56baf43a3e4ad83e41dceb08a0956d8030829bbf7964fb48855d45754c869ddb1ad32d23e2a18015c11f4d903cdf131df70e86f0e9b7306e1196ae201b01d76642af6797f31c078ba6ecea9c6545c02d401d2e5193b4b275124982252ce6fc6fd4c4b9ca74e3a01bf5cfdda084baf94a59753dfaf3236237abe4809c630ee6aa6287f9422911a162b82ca5bcabc2cf62ed31fe06f5aa37705664a3f1b63e246b63b7e84171630bc4fde0ba87a76df7608147a42829bfb9a92a21efb64494a1ccd672980aac73a7f727b250c3f7c5c05b50b99ceb3e313d03702f9fc9e0f6a184e10d096133f1a847839bd1fdaa1763fe479ff6374569f907c2b4126cea5e219ab452f13803f40a5ad2ec8d481923c982288a2e03f816a24d51d9430e06a35817240a50c0b0da11851bc53f74bc6eb35d8d739c7ea3ce7f10c43fa0723a142cfb85563db9a5842e959c1b9910712838999915ebe057a89034d02723cb65919d8da4f20477fb16390472b12cca3eecfded97fe6859f87a200bd26471fa1b78fec2e105a684965c9c5a0d071ee39fb5bab75508929c9f7e4b26c3cc931e7f49cb323135e90cdc5d52bf8d0b274c1f37c0af9da14fe6bbcab8075a0296b3dd9fd40bb61cdfe397f1a299aae15030fc20f3ba853779076e2143e592ad3c730fea0c43224cdca1fdff475858fc6d8997daf2dcfd1f6b6f02af4e4d719b01bcbffc284ba4a52e94abbc4f790782db0ef4b4a6972e5aad833d0012bdf4d1e0b8d7b0904a5b57b9f4c833c78026288f2c1760617355ebc993c4cfa7ca03312afbf3b95ccf87f6f2989a0cf9ec824de04738092fe6954cee2896adfefe1ad80ef326a39f595622a4f5c63f21c67f0a7f8502a277d7b96490cd304e794d8fb539308110431178f16ab0578bf7d157b01f93a2b89f1ec6943a2c7af4fa525a45588e312b126bde465cae77315b399a87b35292f433260b1352ecd29ff99329acdd854a20d4c10dac08bffd4ab00c85a016090129566d9c27a3623bb1aafec189cf71519c934da0f98928b66e49cb43fefe665b0d7128d0e689293f0cab85a5c7714ff184737176f911bbdc0184d7ef5650db672fccf435ebb53fb51e3e3546d52011fa43fc3ebc923fcd412deeb32f8a93aa8ef052125ec39f56456e449b5844520463e1715548818f05315b1559d7321ceef55bc52e09f999149c36b0ea4e72763b3f1078fe283f518f4576561849726087ff5980a07870512693dff75ddb2b3dfadaa95a9940e6dac1584cf717bdd8653e53f44a117dcbbc94de8d765630d610c0a7e1c490940163135116aa53d5c6caee5424c8985b6b2963a073e3c9524d3484bb6ba5bdbd7f6112f959b876c6d0def13566d3069a64a23685722781246be504d04c045dbd5c236cff366fcbcad29268f137a7c457754bfe2eb10f30d816f315b05cd3bfbfa3830050ab46481416eb4e738195d45e266e43ee698"}, - {"00000000000000000000000000000000000000000000000000000000000003bf", "004f47f15f8a883b31d12097f23d3d2d0c3717979248266c9ef992ee4ec685f992abe88cc6ecfe97eaf404e2d89968212109dbee22a71099b30a8d0a7c2906384a43ed4bbd41b9f75924d8ab38060da092d3e83205cb07218d6f75ffd7b238e1275fe1e6de757ba59c137c548866898a7f081f614f47970541a675d4f5d7062393f8a1cdf98ca4d44168a690f951652914e044128c99b90288eef5da43b302f9651a8af198babe510641e63c2b30f2099a6911f8e6104f55fecf70d96c440fad23dc2a582fc455f7ad90c467a2c5fc97fb172a00a98f410ac29da7a744a6d375ac2988fb6d550046f5f53b90d3fa3b3aa386b7ebd8e372f1dcbf335907103a7384d775dbaa2963b1a172dce181da5d78ad38584f2860ecefa5e8c993c39f290809ffdc906584152a793303735e65db65e169cb2c8354af8496029854a19d359f72bc6dcbda55ffbeeb5509d220393a78013bcdd194d878397fef48b51468acb34a369ce4b70a46bbee434413151ebac7711ac576b644ff97568301930043360560913799223d18f90109ca2bb80fc5208b291c9389451d0cbc944b785d5595c598fbc2cc119ae6086e8d460b7433837660f15c2552505ca02e12b207bb0fd19fddab1156a37365f8bdb2ba563d061bb1a6facba58e7b673de25bfc2d5b7dbede5eb418524c0bb8051e2323c5c2999920dfa04b41cb7e78f201b9bf88a0d7a9e17384d31ed7e7c141e8e9b62f291525a6c7a849ae9c7844238661bd1436c2bb3f23df0bbc4dd14c4c1ae3846f838c58276dcce995147b7830320feeca95936bfbeaa3bc4eef8f90fc5fd4693a0cd8792ea6940b4993f0955ca5cc38d38a55be54e015b56161f7b99883defc7b10f4f8305f8010bf61091e23671ef427107d567ba6813251cab26053d81e194d5b7344f29e679f0bf5d58c9c50f23e01ac94e745072186f8d5c2be7136d49459f64bc031aec0b3d2e60e2ef48152d117e9c173c7b61a76967a1594d74b621c5107f1b50e8623070002b06258d2c2ae571f83d548371fe02ed806a39783851b244af352d40b7c08a5245dc2f5ee4aba0918d025406f35dc8a5c7717ec55305ff4aa4d1b43d4e4503571294768ad8906af41b625329d86395f947d6546a40245e7ee56259ff9f94a6b2766d6b2f224e5e978895da24b29032dfe1d2901093f1b6faee481a5687cd0c4e21133055514cf876b0c34595fe81ada4f19d6870323e0742b0c0158b2f40f4ad586f2a28a478caac17a4ee97212ea2e3c756b2ba9c3d41ad8d1d5078d489a36c7e57231a63eea650b74a65a6e46ab60a95ca1cc047a3b9077fd7d1e850c06db155b96a68ffd9e817edc6b5fd10053d26af033ddeafe2dabf5d38b4633404c1cfc6a18013576f75a6cdbf9aa9a02718d478766d2c7f531e5a6b6db8008111be60e486de2ac91f47ecce4c4ea36bffefda83113e990e5220b2d2d3a05d409dfed2e90e479669c0927f2274f99d358d0c973df4e3d7531073c39ebb1303cdb28ae578d7d4a8e641fe2b100fef83b3c52cb172ca39abaa875a173544513dac8898aaf7a394b742606f6bf6290ca29d7c6242f14df2cda44d1b4da401d3531ba5f9072b2ea6ae9ba28e87a52e2d95db82d29e214b98c10a8aabf1ce53f1e49d7fa2b23f3a5630a66f963b74812b91a92524ec5f891f8ede4e9ad7a2cfa9d5f44f2abd7beea44047a340d429a29f5ed7a231f5c50dd9a6344fe78629001fb0a8dab7855b783585b1517ae97791d9d20e6376a487eca1e5bf2b27a0e37e967dea3c631866a539959294be9e8d81a5a5d410f943f33a56b3dea8964413f2e4d8e87d31be62c3fedd3acf56c0109863f57b4bedd09fe7d4afabc1645d5fc241a5891131e3a6a245452281fa984be4b17"}, - {"000000000000000000000000000000000000000000000000000000000000001f", "0031f9e6e8645e35b05014daeaf5ae630e121eaf6a01029d772831524df94858acc070f4568fee3c286411a24c5403c933ab239b12c4174fec6f134759ede43cb5f3b91cd54bf3c3052484c145dda5214d3d3c7d06a85b188caad0e79a7462bd8bd6c553701a9e33ce2904c1fc8ba89a1dcd69d6f67a7991b7c9effeaf1a150db95625a793cf6a5e4579e7dc31927d26ff2ce62823e3934b54bd70b11ef3985b2e260d1a67708eb3041e3d667f2159db0e15a2206fdf23154da99848fb096cbcb40e876c6d795e8217c3a01014fba5dddf7a0b2f7e9d2490b912fae6734552524b3dcaf0baa8ef0e4aeab9ee134ef31ea6b1e09962fec983416e1f2e05daf1e915a44791a854e61c58db84ba9e83da727a2f519c72722123974b82081f47e43aeaddda1e26f90e35171052dcdf913301a7909dfdf236b050986fbe24d78c350b904c28f7eee297384a7721a00553a14603e835c97b8192c0d87f613365aa178499928a1f101712570e4f8850ba60e5f781a53cd13e4c82194acc15b4fa52dcdb85a7dd67f3654ac72aca0dff1f42c41af3f9204f5545d551fa763682b8f3d5ee5d0fa85d0be048e04311fddfd18406004dea1c45c0bff886c9141a4eac9b120e43316765e6dfcd97b9bcc79cf1ed116b81de248d4013a06b227daab703b90e091dc1a1173b657e46a4d70bdb7972daea582fe4d0c433a595067769d385e378a3c64df0e7240d675e0221b0939b0fcf50c0ef9e1db7111c025e90ddb70cabe38b4ec71303a324a35edd81bb99b6709fdea69d9c73d5249d3fc744106f1f835d902317eebbc244721f94feca3113948fbe91a6ebd7be0fa360101efec73dd0fecdc73527aba73258e6076e44674c93d9b4f64754b75a3230cb5e4260611f976d13b45bedf1bc3531b5eb43674b62ea6bd6a7d85b59db066fc959a6d60b221bcd1b00ed31fc0c0798369a7fe147bc513f8131e57410dc01741edc91a9638b6035404c5137b7df7f4bde1340301a69f7fb62cb05271b56b350433b8310197d331262b9634ac06498f1cecdf80f344b8a87216a5b30300a0ef8e95a5203b8b895f5e01f366d55d1555287b939f66502ea66ec0b4dc2f8aba1fc8a7afeb03bbea10f750344b60d11ad57cfa30b2c3fded95bf378cdfa20df23f7875fb119c203c39aca435c29cc611bba7b026279a91496c9d2e783c1d417320fd4f8a907fd1948175d643b2ac54ff8bac4bf037a9d773054bf18a00e75cf8d168e425d2a16a1a435d15b2153efacbc2f2809c7ef5b4b53212c1053c79cc390f92a1bdc4f5107c72fb471143167fa980143772fd446d96e7bfd290f36bba4a70e8891d8f4c8b0826a3f5ba4e19d389a1d4776a0f32e753fd3d7a40c3abef9a5f40cd038dd5eac3c7ad872b669a4ef869d4c7e1eb634d5929d680203991f58a20f5dd70dd5d79f35cf35d6762f0eaa0b704d85ed6a40598985550bab7b5325e0437822be094caf8a60c3ff6d3b8451c1ffaecfacca92f7b3ff4d5463f5e32e7b41a38ff8f5cb7ec73253691e619003c5b6177c0c195fce18c37989513fe18400f00aeb1339d9639277575fc6a243906f71aad5adb91c29380480d1532588b999722de0bedbcdcec89651f796364a7ad56cff71d6e79e0857155bc5e0535b7dbcd11b04739981159dab2bec07b2f967de57735e3f9b8ee710c36544af8fa84e7f5c428516dbd3b934d629ffdc0daa17de278e8953ffcc93828aa9725503d7f1d1031edfea2bf20d5990ef4f48e356fe3902a9f3d7f8e21903eab38a4e19f4b151727528bc1e750363f6f3f5241fea13028fd6d910c3235933eeef59129cf3fedd2feccfc3a8ee94b9ae2fa7fa44569de21d50516e5837c4ef18f1e1fe8b5171b7a6a1d431ff110c9dca0d"}, - {"0000000000000000000000000000000000000000000000000000000000000006", "0053769b0f2ab8cf56de834c6e21de00dbac896b3b3b94f4b16d945f61e609953053aa7796acff7ff73100ed25744a11099e9f0d822052124f849650b7a6f90a976d1c59ab4a9bc0e8e119a7c46695fa32b113d910442a6644d3bc1570f7d6205e3484ae5fc5ff0ea91cdd85e584d2daeafe58e51722af3e1d4b2a9296ad22836921a219ba70db5174af187705baab1af944e02512dce59bd0549c860a77fe3f5c335b027b38aec70e9a888c3a8d8127571b9165c53448f502cd2a8e411d5cf1d935e4b9eb7349868b85fd859a2ba47f9beb5a6142e119a8f081756d3a13d251b5cef895fbd426700eabab991f310ba3bff72749471d22fb539a003d27c6817ba8d699fbac2ea3b5c344e2c50c782f662637aa252e1e5662a2e67e15f5dd6d4395b5d4bdca953366f550e820f311b2093d0db1f299675a7c9bcb013a7cffe25bb1031be800065961f354d5972a34e87b024b9d504ef7f841ea945361fba51b510be6f25d743a48cd280e23521d3c52a4030053a093141b9a4a010f8cb49ca658a222de2744f5a0d0791e11d251c21310d4f8d790d14929e6668144b3450be8a0bcac25f8047184ff42951e27be4c963f63d4d1cda83d989d2311fdcab09786b8197e0301b82ec9b7f5c34e531c4d04e70b0658a21e27dd1f828fd741744cd10bc7bb2f0d1c69e14e6160df0f35d27951b912e2f0863e36bc038b176e36e900e3cbb941021a6df9e4a3f48d636926441610a6c9cb490a16e3726ddaa8467e9f3cba5016af8653146a320dec98f49b7d26519aaccd55e5ff19ef9aa7589ccb616f490265eb195cad00d5123199190755a7e5c7ffb108d0e5fbbb3a813e0f1096cbe727e9660b718a19f9876db43ab2b6999531a017593b363bb7495e58b9a5c96de4bf7ab7d92d48d213ca2358d864e23fa19661b9a8984584c6c7264ed2ba57b8005c7acf5c9d2c710889ba554ad75172f9caf9bbc77380a59d1c641e8dcda8c73b2c46ddbab4b057bc551e1022b9088a6dcf58b9b235eced236533ac4d9ffc5af8d489662c22efa681a9a0f769a7b28a387cb55800b23b76ba910e47f431631e4bad14bcfd991590c01bf4327a7ba46e19d6996519bbaad5e587cacc80fe0fe9cb00e4888caf19023619dc62003a38f9bd8a2b1d195d34df8abe0dd317e45aa8da568d97ec951873011cf9c9c449bf4994111589c67d8dea92b5954be21c4d7ea662a0e025cc8b257de573f6617d227f340c023fa510359ec8df312d60e2b4a56e80bddb50617b06dfe491c28a57933ff36316cca0443edccff81d7f056ea897f293714b5d43f39da5d3b2610aca4939b512f3c2ec83c6177ea26a8440a133657e4cef5c3b351439846c526b01c9a7a1f1c4eb60909d28773397e942d692c117258bebd11a95b2a5d63ac7a4c95d565d038e593990514e28c7a8d0a1912cd639f7a4fac19b3ef3c45e18a7e3ed733483f974d56c3e5e5bd69b0c082189d34d142ac3785dc562f03f5aa565f20fb4e91bc5f6143f07c2e47f0f826b5fb8f53344153d29ec0461506efb8f086b6d6230f2349723ca59aa9ab1f8246d24dfb81089153973c753774a541e299fb4e0cc0b13abc6b69bd6fd04b8e890d2f14bc28b33d9f8de37894c36f7dca4caf5bec7acd46e0d8a88855ec3ea0842491d6e0c67a4f20141964872d2a0d462af533a0dd75bf1499c1c955161e259966b8a27531dfb42350b988581110466bf230c71a17362893cf385540221105a6462d74d501b84ec5203b96afe9afc02bcbf090b3451d38108f7c0e5375441e22d114598bad9a71423476ba5634c7f79387db3660d2ce4ad96243ab78e110ae5b95351b3533e4d9705b77dd24b577edec5141d4075b1d8d702d32761663f383709fb137e5f50f1"}, - {"000000000000000000000000000000000000000000000000000000000000001e", "0049495267c57b1546cea029f70e2fc48e49908f3a01843e2f0796e8e0d472157fbec9a90247333e22a108fd27d70b619297cc915527a4c635967502ff0712254c46e44199cb235e76e604693a04aa7b4bd4ebb20e7faabb070ee4d6e7a33559873b1801b73b7312ad18504f22f763e9afb61db1ea05cf3024ec81cb42f223fdbe8cc8da8b611cca43516efb968ce3a132782e36dbc7396f6278ed4f8816923853a151c6f01cd88700bcc256a2868670bfe860ffb6d52261405d9401f44c1535b479177af1c636568611de408a556b5fcaf00aafd3c5e005fb3de8203721f1c4db7f327d1b304d3c7d0cf862b3ec3dc89a19794d6b62927a97d9c83d02a08ee56846d7c437dde05c92aa4e81a59fdd97e324ac0db35476e9b5b79565f5257dd84f4f237e31c40b6b1a234008934f43f8a24ee16e8bd65fde97c6c5337a4e2b421a223de5b3a618c26c621f62033fc4ab010eb966eda13b75ef31823fc4d70148ba911cb6744d34bba0659e26394a387643375e60422a0ad4de3d136769c12f05c69d5675741b1c20f1e95f4eb9533745fcb71047b821a9ed87a4ea3a3cd973c5865e4c4c0d8615f645ea9805e64813c900a771dd120cecc4f01089f8cd3f260ec5a5d4615c44ab47210d7d4c0d4410bddf6c3108e38f9662e4a92df3d9a66ff279f0c426d902c9d124dded451e235ae8b4e9e27371d76d92050b377caaa65209d27a13f68d2f314df8a2599d3e15756178750975acd5eaf4f36eeee8d9ea931305bc274a241189ef8635b4c6fabcdeeed55af641dda4433d4c8b78e2a9e77f5e3aa802ccc1e9c695573a996c098b4be66d242c633b20710ab5a9a38ddf3fd6151f2852b402bc0fecbcff6c930f71ebd073061ed83ef60c3a22c24b4ffd38d985163777dbc9da97627e17eb52ac642cecb13fdfcad2b7decc78ce9e1e9df511ed0279397c20299163524ef703036852d7540d3fec0e06d6499934dbe831d998f3ad9ae97b4210f7ba705618df8fe1fcd0470d239dc1dfd25d64b5d54cd5669c31b8cf77af56c3a99439774a137186a762491e48b104c9e7f692c310c51f03307ab11cea4dfe817967f207757d67c20771bc469862d3d96de570f9cd2b4abb19d6640ab4d71f8fc8ebb3364976ce2a281fb2fcf574d703ea02a26239354ed9e092ebc9170da098ef460416d4bc504fcad4ce8f76c0936d93be273333d5eb0896248c98b36d99df3e71c6c896db227a2b9a2add1657d913f120d285e493e7cc9045bbb21264117a21695baf6b08616f8767e86907af59421eae08dedeb30c3f5f48ba4bf9c8f36370c5057166783838b1c8990cca43dd621c8d073aa8b42430c441ce87fe7838a31e92b2f9f7288c917ce80dfd997d030bb9e79f03e42f0cc3bd9c55bf057356d50cd753c9cd73be9ee6060409b0f76f2d1d85b7e2d1ddb2100dfda9217476140d4c5e14fcdd67cdb85313dc0cc6e173431c3c2fea1f9b4bcad8c9a36b29878387a2ad17125ec33a2a5123478454f98bdd89a273a58d3364a23a2b8a1d0f2504aa042d2bd06eddf42a04e39f69013d42fb5f8d2b1718f2065f202b07cf12c1b69f584f618f5cb93cc20ccf036013d1f77d95dd0257a23e14ea7e80f8b9a71d3e7f2752530cb4eeff22ba4b4132a5673db25cbe045eec6b69418aa1c037a114fc18f4d9db99f093c209d1b10bf8082e44772b37522176e4d60f2d1abb8415da35947c8bcda53a28d35d4741807b28f65f251e59c42faba1b39be1c81e273ccd42e87f6e1fbf935a05dab6b10094b1caa654a30e7fb895bcea386af68b12bb7e7d4bdabf64fd8d163fc4e78ef5f7c2df555808498d78c9e5bb59f9ec6a344ee8dc0fcd73bfdc632227b29f6b63d7515d0e126c1b9c8699ab7494834d"}, - {"0000000000000000000000000000000000000000000000000000000000000029", "00346c46f773deb7cb31a1fcb128fa86577ffe731225dc3ee84ae1e513fbf905d6d3381f42de6cffc355104e1d19fb5d98c934ef3310b57568973aeb9d3e6e2ec80b35b0e04a47f7d3c5b384d0c609ce94f37ac3080ca5b653924d3ca2ff31c5bd377ab4ef8fbdd18d14c3a9aa47f9d367d6c3891cd06676bfb6339ff2dd125293338790bb39314f69cd39d0043eec4a19cf854058bc3978db881d9cf976d0b877bf4a0d28d67ccf00752de3964194ba3daa66287d5435a1d12b90c4cb00fbbe0be902666fe08a051c3759089d70fd56fd01244b475e76a3c67fdf5c74041c65417d336b32442d2732448b4e5f055362e4766744b628d1c89d8e52ed01847433f7827e32c7c4e059c28e6364283aba6e990f97bd9da5116cd3d728017a8f7d7f74fc10948078118d27729d6583bf49f4048f1cf894c1ca90b657a22f85bdb96427ced9a926a40b0e3604167e13d9f2500198f9ba43d7e2890ac4d47b2b27dccf9a737dcaed21e851c88ad765d9f2a3a5218be71b0d623a4bd7930a294afbb6323b01986f332b746535d1ecf6959252107019ab428a82673ede33c7dfbdaf7dbf605909c0022f7e3ac3573c477d27e7a95674d262aaa2d79e8c0be7a5ea6a031a3443dcf1139bb19b80579019e4a5047b85b1a38e862e96fc21502925bac11fae51479c45bfefa7c89c2c617b5294c8cfe6b53582b0d4e9c0019dcacde633be57b154612caa14c7544ff8dfcdd808cd587d26cccb3f994f53c580f497559bddd9ecdd07d88c46af03e10a5ba7d141f78b0dae5a62b4d0bd1bff020a7493e94925f451d6029c2f2515fdda6aec06538c61f7c42c07f8bb90f0e2e69a9ad966170d42077e8cfe7b61ed89232312825dc08ea776c93c6a920bfcd5ef3be76709773140dd1254b2e0968b8f2bae303da4e147176a37e13c03fdbee2c07d6aa2114b4102054346f3d2843fdf1a37380d4b3ed20743d2e7a73a3f8d44d7f0bd099ddfa659c25f27130a903ee7170afd2aad1f1d4c5943d191960ae52a397da58c8d323923ecf7408ffd43bb5d29c0d777035ee9c85874350b3114c30551e1fbf48cf64448fa333ee74b1b2de3198364636d0940b9723312036d3bcfb65482b8eff012612eafbe84ca4d18ef019b096ba31b27cbdc56a049cc1afead9edcdbaaf85567d7f63a2d9336bb5bb1044dd65b270a09931dcc1077229bfccc963a06970f101b58d5c90a07fe78dbf6f833f4ae5a879377c45926af1cfa451885adeb43132bc4715cd975953bce394bdbd55523da50c9a9c4057c29d848269aacdc926f05016b2d51b771bdf75f518cae231614d97e1e443413ee435b7d9b8521039194b28137f2360738dc6f430a3dc3ef13d3fb5f0441a40a5f7d10811043aea7a5231d35bafde571f186b8f6ff60622342ee8d3f9c9304ddd0855b661413bacc03dbcffd45b143cebdb5ff1e2947f7860c75bdc50b033187e9b444d3441c0a6010a08f5ddf17b417edaf115db20e306505f28edddc56276dac60b4f5d1aa0606a2c7b7b866ccb89c1e71057c97c798720b4dd42126e325f4aea717c899b74705f4103127c9c1f5eeb4921d3db230fb4672bf493a295cda244d9df485f04935267648dba1d10a552c8f324c11e1c2e36959a99997b4c9d4d9462a2fd6991f06cec44cb961b1e97601e534e7e8b7bee52e791d831d6a6660f89817df52c4c2b4fe4c9f0d5f93ba573009b86505ccd809cff0e3950df9f7ca5ed37a5d91ce1a9ecbdc1f1c5d8ae91034b70c31535718bd9ea4fd09c7aac4bc55e9ecf0a20220804cba3ad7183bf52b0fc95fd0f90df0b9ff9a6192961e7261320991174d10ab099c635406936570428cb498131dab2a5c41d61e4d8684a454c3d5135ad24560454bd9dd3b1a9b1c"}, - {"000000000000000000000000000000000000000000000000000000000000000e", "0043c00c93738ce1e4352b017bdcae3f75a1bccd1c08902516d4101534b5c350e2a64a98d28d59d6d1110221c59d260516a738a5b62aeb43c38e2d73f948290dd4265e6975c123f7c62982d77c6d7f45f71d77690e71e8dad90a02316a314318c2195b1925ad7708be50d5ee04c39b8bfb7c2305719af1877df90f3282066af655aeafa5e93b941c97f35f484be624b0f8b2197bc464202c72fc5dfe356927afd57bee7684b7488003fe09134e5e33adebc5b3f043ef93e9e0d9bb2e3a14eb8ff47f4dd2b89b80c6148c60853a85321c8b2509fe155edaae1497de84c166d243d6b55d78944aba205543425157f28ff3ba135b6ce4dc9ae08b1badd40f0f23393e21e9475182934d9b4ae1e5b87718f93b376e7d95ef5556234956d4e82e6e9079778cd8404c19af44de7d49eb90b3bc443f28e07e1ec5f49eaaa13bed563262d6c08dd3f3447c51e5b785b56f8fd50c02cb16bd06da18993593338701c4f72d4df7d7132109e0147f218da7f33dbdb77340d02b1a4c2c3d793b087dc4c6c6cf3b6742f43098cfdac2994ea4dd77d456484775ab95f67ebdcd85ed4bfd29099a1cbdf4bb0aa9c602ec4f7037696a34bb08458299d97b93c53a0d1f89baf1da77950083e3c7edb394e54bc1d9e9fc34e78b60759fee4575be27cbdb72cd26222759d8c04c240bf9b76ccf4d9170d707594d6851cff89cff0205f21e76034994a335c3216187493dfe7f953b26e051b1263b4bba34abda7d15ab447cdb1b4d665a71c608c6c49fff84a2a87f4350dfb5450dcc89ddc8b1585757af4c7ae619e792669761343fde4de49a9236e70f66aa2dfae48a779385744d5f30939ab3dcff5a6f12514a24e607210edaf044226665352271e138b56347c8ae317d92d6558934c77466f4645634cdf1e9b549e0d5e6715a96b3fe00d60aa87e5302688cd71df400ae4c7748845a57d3d6c2038e389fe50e6e3893e62785ee5327db8cf37c1fd43bc135bd55d63fd704170148c95b21b46509fc5ff37f834e391de60fbe4c840da7368f20c5e41dd3fb72ee525b13bd42cd3f722502b660f71a399de1d2a2d11b700be708bf4bf20ba911dcbf6e9a4a01d3ff4321ce21f0948a4bfc5e2253068b65f3705289c7835f344be0338df9b3939ff7df31e3b5c2269be23fb77b349f61d2e1a326de7f1a12025a004862939d5383c4d4883753acbe0b2ff535dc1bf7c19f39e3582fb1ad03ad5ee3462df6b8bf9470171355f69535a6e7f33e9532dffc0839c41db9bb25226e2f8a2c22ba175e4d590efee2b406531b3439a80d45879124623991197bf14dbc7ead1ea854194b2c203ce620bb5ba161a627426d2a1a304afb8bf994c513d6aa22e959ef50e58ae30544c5ec260b4f5ab874302786ad7e5da58763058782637d026de7c137a7a603487fc94a6028cf56235344c5daafb9fd801cf9292bc7251f83a7ea05ec5c7964147c39d3b87f7e4f44067bd9bbe274166fbe2803792be9537629ccde0bf01524ca3427900550f876f2bf9b2b48e95573fbcd0803777f18bda516a3cd38e368f8b41b41c18e1d021c04571ffc1a3460f9ad1452a34675c9e56f0977459b183ac4f218861969692763c0b8c48a1931c31610bc3cd58f9120ef1d3bb4af7511c7b55265e38cb62f0e084f992b176fa29be07e6578f165596efbbefb7b7743725cd533ddb54ef5db649f49ad107dca5f9eea510ed65ac6b48745b151fb3427b9ce6edd3ad31291f33eef9279799b02b99ec954711aeb22a6526ff8f2c80b697b8326d92d6b2c3ca35e6af6539972e51e4a9a42a79c57b0d5619bfb398705955951e612aebdc7a50d5bbefffdc85e96608c92700b32e03751e5fb937a38cb7484e41432210c04f41ef4cf44d9a52d962528"}, - {"000000000000000000000000000000000000000000000000000000000000035b", "003d7a484b0c9c1872b2a16fe1476995f749b182520262e83fc45ecbb7642f678e4eef29fb90abfdd14117990f170c084ba0de053323ed733b79d1151f52a524bc9edb53d06f81833562e4d5f93a4a83c0785c541047d2219c17c767db219328409bed873e693af1f4310d4e236216aadf7c8d9b38afda10e6d5939e1d644cd1feda485d8b1529e95544c3d3b4f5f33ab153535bb04cf6281b3b5533ab67382b7cff5a5c69b8b4fa0bd97e35d50b92656d20a50ad03be5776263fed1b1248bf6e2a74b7adc6030a9e4bd63866e8942fc38cb0f91bdbc2c880002e98601752e3aac459e8ded4ba019395b9e034ff0df4dce62592974d1c5dafc51560a1504870964d1655d09df36554af4e3869c9257827725b2acd7ae0b82edc7ac669189418219a725fcc6b12209ca1ee6b6079bc382e894f9eaa06f382cbb20f624e66ccec2b15627a80833f9ec7edce361ce3f617b00637b7da3d468abac4eb4c7e755a4f15a8131bc573cda0a36d52b299dd50f840a17f52c2ec874b6d12101365e514cafad29f261a40a553e3e318254721fc91cc8469005584e2729c0b2e052eacbed4135383952021196e76cc82def307452bbcdc386a6466275144726bbd4d52266b497c0aa67def97224166db716e5b90321f491519d42034cfc32b4df53d25cc61739069d299f05193890910cb7e302ddcfcd10f9cccb1891d7034ce4223e5bb1b108a6e1cc4f61802c961eaa49530d39ea3e4ca9bfc5e1cae3b183a1bf25113b372e9b381eecf71355b84151b1548e1a265115f4de3b03b938a2bb483354eee32073f5565ae6024555f7f999190da3d358fd1c559b8b23c18a8a981d4f94ef1f5941195aa331d9cca467611272f8812abc2f1b505a43800eef6c128fcb63569ca1b3123caba429742d8ede44135b7343059623af2c9911be1234771ca7ce0d451302bd0e7b774f3316fd9d70c2267b986e1598be5d0807100ccdb50313637fd01584fdff70ca4ab21da6090751f5d516975f39e2e5e2e0a6bffb11a358bcbc6008a951741da7f669ce259999d57bc38e8ad2db6bf52bd6a255e4696537e2ed830921eb8096f86edbddb363804d64629cdc0f3022b6b78ec18d51fff57c42b839e13bd99491759cce1c050a4836e3ca17cd120113468952b459f64473fc04a6110a4d5b5a1bdc570a65099b668151564a939323942743f2979612a857a2e50e2ae78ffbf65bade3b5f38a43f74fe90d6f3df4f80a823dc10609cc2ab6b5811f0970b3758610d034f810fd5c58bea3a817859983c3005d135322d9db7d86266344ce4f13f482b89e9292b64327dd30f93d6451353ee4ebc1686857cf03a5a277428445ec455dd3fd47fce49ce056a257d65534e85bb2c10a1bbab8b90267a91d1100fa6913f850385921c935f22cff7fade002d77c5bf0d7be357e1c10d57b0ca9e639381c6b2944b83ae1acaa14c7db1ac5e02efd9a1dcc05b18ada0e0dd16add5850eff56e40fd77ca4d75102d36f54231a5ed01299f161b44e979273950dfc2d8de1e55f0139f40f6b38df696a249b51e18c06cb559272afb521801962c44ccfa0bd0b5e47a794eedd1a5e7cf6dad1443249a4a8fbcc7b7e153db7478767232041555072aa286c2a8d6d66fe07086c635b633ae8432dd93b405094b6622649fade3f985f25cb072ce35baf3f27705a1ee53e75a14859b1b909af48f7ec84e025f5e5818495a6780cecbd1be16d59444ea7d69bbf658db191a364ea87a4a34793e6d54f7a52f23029e3d553dba0970a90b33d446d1c9c4421a5de94ed16b783af097453ca78306519c4111f33b366776838af453ff0e1715984d9eee0d617ea10472f9806878936776fe2a091b471f7aec0db1bfda02c2714de560d21b69db16d2"}, - {"000000000000000000000000000000000000000000000000000000000000000a", "000ee6ddaac3c7a9b5fd21f541ae6d81bc436ea24000876ced434cc9c12fea943c0e3c605a482f7e39d323af314c0fa0922fc0fd92e4e53906113f923f1f6d61170b87a01ddff30003f6ca51fc12c9bc11ae9f3605eb3b26abab9a838f5040d56f47ecd8b5360e8a6a0a5cb575052bb499b2d0764602da8059d3a5785d7b1b9b2dbd9c089483a40f63e3662a88016808d6d5992cafdf32ed68d2eb9f65752f24e0ef972fa4dbd6210672ce2e9569819b8199e4011065f7499d90d31a1c3c420393528ff96d2f22d6eb9dfa7e767299bf5ebd0e3c5eba2e1dccd7b5d9725a57438d46854fbe5c092ceae5bcfd993ddf734db4e1fbd5baa5febb1d9446075e771fec902fcded5f07adf4da714e27c778c42226f12f455d5216ff769383447cdffd5a3d2659cbf60de5d69555131e8ccf39d20461da66b0fa9c4b8c1927e7539058d4bbf914cd8624a3384e66c9527864e10217fcda4582732d2fc0871b423dc4e71a9c1dd152241057fdb6233a49576ba356bde1ecc0eef2ab54d81b6403f0ed7753a5ea595441147a67a98bd7beb916319d79ce72aead53d895b5ff47d87b8660e2d8f04a076bf2bba4e3a6896343920661e089b4da803fdc24457b65887ae473d746ae78d670dd8ade98567cebbb083d6ef133c94beb730b21ad8d144388927a9ef6991915a672d55e0fd1c121860c68f8a94350f25c4ae50d5567cc458c9063863c871942efdf9312a0df14d3111f3ea58ba3aa9df9089219c1a1dc2915b7d99f531881956e48e25d4f260c06a393f5d736a4d8da086e48ac068f9af5cfedf3c42811275e1db6830bd4eda91758fed0ef616aadbe64a45493e0c721db9c9eae85227bcc7aee5a1031823776fca2c64929e3dc772a2531e28f7dfc5979c94cae67de766503d685249b50ed3fa91bc2ed5cc52bb1e754a2825a482e5d557dd2cc0130a1b3f4cf54a307c2c164b620089ee600ff69fe1c875e34b00a2c75042134b6daed32769f0f9fea77049977742f4312742f90b69dcb6c53aeaabb595c15688e7d86ccf0cee3945cca9cede542c786269ee04e06866260ac07032ae7f3d50dba2a34627f15f5e3ee490cefefe4d87cd190323cdbbc7afa6b81935dd2780fdc652b46219249f76af3d83cb22ff1dff1bd03c5108f5c9ad90db2526e2112ee8be6694500a4fdeeeb0730b23093dcb2891fe626df9cee0092e16cfa61520fece3c865a064d5385392791dddd3b0ccfa98b9d7122a02ac05d7b05f67beb3780fa69c2337819b5dea1fd037bfe54cef70e45607065c47bf4e15d4b8464f09599d2ecd46194b9b7dd0af43397b2eb8f8f940b3188a765af2208a4119cff795f3c7838eae8c9b1cc616e388fdb046b9234fec74881d452e3daf53da606219f5f0dc07acf8fdb523929662fbb162bed9f83807020a6cb65d510fe6e6be1669f6dedd92aaabdfecd40c9e09bbc3913d66e1ce1542507be3ce6a609b5eda0f22ededd3dd0c6db963c25a267c97c210a1733c544fc91b854af331cdf7652b16e5eeaa66e1de79ea4103af543dbc18bd8f2a3126116ef08cb5e85dd1a5d528d32216078c7e015e4d961c1168c03219fc9627c72d0465b1509763cebc38f67d7de6f06278227c11125308dfb28ae37e27641ce535cb2c56f6f1eafba3dc053a960fc294a217c2cd307476094039e7bd9b953512bc8ec4e64f71792c0fb3f0b04ba7990605199aa10ab8acd10dcfcead0a7a3261325b83b9f84d1c6a8a262b3ef822f1bb2be3e0e26b1c2bf9d8df3408930709ecf71aad43338be7dd119077663271e26cbcd16231a0ca0a2f9c4457f3a11590e561d78db1567df54815e9b3aaeb5516a904423c24256deb6746c91fc44d1c0a4c7c416b1d07d292f31178f34b9e7eab1e12af"}, - {"000000000000000000000000000000000000000000000000000000000000000f", "00a437c55f115f8de60ad25789f4eb05876819ba2403e5816da12e283bd5cb134d9d2619a67e061c45c102c92d5f0e4279752ac7935a49a6acd51ad7e9d631055a1434dfb056efe8d7e4e0cd788dca2eac1e02a1031bbac286e2fc6feaed70603640ba608825fb28c81da7a512848cb3d0c862f4c5875347f29523ba57d507f3795329030b251e02d5e519ebdf02bd049a341333a9823cc2661e193d19d6cf8071d87e890196fb7400f2dee19708553f002b52992e742f2d727ed38483039e11bb7a837c9170f1b3746ab1fee6cc68fd8fd5070b42ce0421fd555c3050bb6a592e4cbd0c486cf717dc26d83fec4045c23be7cfbfc0085f65f17c700802d1f65887592eab0185d62b7f4ad231e4139319a309e49bf0f928f6e1e28c714c6feeb366ccc27866ad08bd6355c66fcc87c344d6ce513d0ba7a5af3f22470bbc967e054f93c3d71e62cd76606dd4d7591584bb0206cdcb43cfff78e994b5b48bc1a99317d87ad5590f7778829d8af0e05c25165b70dd32ad99400e80f72f7c6f940a6395b72be1032d2c1f25ed1c9614d893365fd66fbe57ad2bd739047f87297b469f2d3660470262ce3be10c6a6da9a7b29633be985181d43dd3b722a6f6d9bc1d5f1fe9e327f37dc49b16f6221b856008a703b4f8ccc2df8bfb60dc617382f5ccf33f85a6132edcb00e5d70392d46213cd2a03f8089b7cd703e02e74fe9ca5015ca954e30e6dfc27ad613ef3e2a2e242625a6645a40f5008812665d3e5e71ffcf916f38048cb1fc0e4f24c7f6da2240d2581784cd7f4c08880740405d479d8121753e24db0e7baad9bac4b09ef0063ae567bf972aed6f52049d982df2c5bc141263251330424cf225b22d6e9f07d5ceed8b12907b56c76e093eeaebed1a94dff82d13db1ae4ebfd5d247d248016b4b719c14c400b8cd27759254efa926f519a2b300158903c16c0d87ace5c85ecc92ff0fa320734cdf555a0268c4c1c817d0ad085f90a64aa9615be9ffadf2ae51d07e84cc639d7e494111c24e4413172ae8e8171e1dddfce75ebebd10fd769f06b9f76566e3fb7c604a9495731deaf3f40e2d079d9e1597842eed1c8b13a6f05113ca16f830d4344324ef2f6c2dd0fdab7881b3196f47f0d6ba0c407f1faebddf96ad4d03a697a2fa87639b498c9694bc3d7e475f28213121e7fda4901d7451cccdc1a5d8362e2cef275e791321b0ad1ed27d1d721ab6c1345cec304977854604e0a7f7bf02f0b4702605143a96ca89d424dd2ae69665543b7f737151fe8b5d294189b6350234622a3f72b8f0bdeb86813b9b1a1ee1bc4e1e54073aead70a089e3516fc06f46da5c220d21391df4b898d1b2d10cfa4a191de52b1a0c3cfd551ff14f69cd53e62bf438f9dbe5764f2f2ddc9a20ec558b9bd1ce965e856ff85694855c9b44070a8f01b34ca7a108f861655d127aa33ef7bb8cb24e6bfcfef85c7907f32fa53b55d5e979c6201bcdb5135155e3e51f274780e5a49776e042bb10053b005b4d9d6ca6bb5d1bd5f80516805172d69f24e69ea1260f799573cae3821bae7cc6c865f91c46c8dbdd7272151e93e0030c8c09804c615ef4693cf945909859c12fa8dc4ea1b209d7f75834a3cae7d9594bc15e4d4e4e361c40641b0384fd20271798727f0e52e5bf8ff00ac50232d6956666b58f07e2d16020523daf16aa2d2d6dc1c7e95d936d9def73786251a46fb1009e90d71c766d585c13e3212c2b74bfee47ac8e952af6069566332422f89b9bdfc4d467f80fcb940a0bc39af47019b04e7c3e9e043131d7234075bbf464d5609760ab2105bc744ccc40f6824b45b7bfc826fa366a38c94b1f8b6c113892986cf2e4b897c67c52a2dbd93f05572046f7456a0dc9e2bbb986b3d74fc01dcdba989c47"}, - {"000000000000000000000000000000000000000000000000000000000000003d", "0046697102de3edbdfbcb7bac3d92cc9fc30d75db801b27d97cdc296c7f07010b21cd4a3727c57fa90d4147be341ef8dd335bd5ef436686b9e8d25e0f989591c45433154d9ee12ef9403ec5c62bf86ab29b9cf9f025b464e042428bd3a169226f5aed925e1b1ef3a7e0e766c86c5c88828636c62579f9719431566dd26a938e2460cdf28065fee86165be74769caf0fe3bd94b4ff6d3f07d5c7307350c260d4a5a8aea691e1f0de6031d898099c2a70e6b45927cb06aa4e595622cad1c27ac053414d82675ff4875dbd954428238f399b4930c00a1678b0b87dff740e30410573d34c906951e1334c462bdc057d8f51c4a2b21cadf17f6ffdad8ac6806ea8418108ac619ddbf75365ce18bb608825c4f350ee77a6a2225687b818393e428f94171fde358f2e217a535ba8c9d1ccf708bc53429569dc2986996e0d05213eefd549db4758fcc79e8087b3a86ec3a3958e30095b33bc50d3427e62b44f375c226eda19ecebd9f14f9c64e5545ae30814ee4b408b2a4cd81a3dd34740f3d81082a73c7bfbc3835b5286b64769ba6fb5af618cf99cbec5c5331349aa9a6356cc7cf15c4ff15c60ae2d6a2765e437fa0fbd467227e29152b3a296f1a221c8189b28f18bb591549e6935c6e56a8e595ca951435bc3eb9492329ccee2443e2b251f287dfb80e241ce2b590f7a956bf8117059a454ecf6d96c631c5e706d59d9df2525c0ea857668d9270252afc647940bc38925230b3d01443fe5cd684ebe478bdefca1921e10c871f2bc3267f95ce8023e9c8accc9100da6e38d6373193e9afe46d8f3cc168f4c3c9ca7b6bf3be7f740b37cb79c926308d3f6ff7c89cf03b2ad9317d98b4277a7b75a112df07596866e1e7ee93e20276d903d82bb399d01e162d4fa0504494e9e727f76ebdbbe3783d15ebe530e68ef5bc2796f6f4bc74bb481afc120d00cfeb7c940709a6fc217785cc42c3fe9f471d858c1dce0e16a392c13f1cdfc6becf39606e8de87d075909771ac61d21772f623bb1998537663ab5b65ef6452d2f35935651ad7a9e48c493f7eb2fb54ad170d07404fbbb63fc90f77d96221a4909f053efd7883f2d050692fcf4e91c4cd73786057edcf9f4c262619db8381cb70be70a07f3dc714732901cb99c58ed23959bb43ecddb2260e10e816299481540f0d9fe6b74de4156053c248396e3f1fb29ef008ae43dc50dcd6c71d07d0904af23af2bccf59073f2b911717690b332543fe4073193fabee8e99d9180f0bb61f1f89851198f6fdb53a6de801e2178618a90e6bcd7f319bdcf7b6f700e09cdbef24acb9c5ad45132c369fde1a4c0594ab58720406bd36dcaa2dd59cca51822bbf5322e6f9238930de7b5c11a89b012a2327761f74a597a7595d69e891a7d0b16e9a8395be75032a54254b87aec137a6cab01106b863930a1ddfbf940190c420da24688969287395e97d0579c3074f006555a49559d9b5d72bfe6ae1284d6e3c2924b4b698888a563cfa0ea98595afd97360a8f6101e18937e003e9c7a36f65d6c052def65e05f1e422f60f4932c73a440fbd5f55ce7b427def2f1e7425e956ee78177e6ae913407bc036f8fc9c67e6112fcff4be7b8747e0ad859be8c11939fad71c0681163e26de84550ddd5952fb13d2f80fc30fc93e0dbf0306df6a618a7f8ee862f4d8eac1aff64c1f1cdafe0df2b4155688c2ded14e035328dd0060e7bd87eb0d108372db39a407f981fc212efcdaec82c7cf1b56b3155d229be449860b7327a9b6206fc6d2e31d5e25500d315e6bc507b53aaa2ef4c76b276086e7d1db92fc6c358f645c7ef49ffed557e02879795f296ede9f5726120563ad4a6d34a1d356ac3f3cf08e79fc16612070047dace7aa3fd3bd20e71048e9d8cdfba1daf049"}, - {"000000000000000000000000000000000000000000000000000000000000002a", "007fdaed3735b74db866f0f936aeaa78f4e730076d1814817a5a4b969dd96f947a277fe80540d3bcf9723381f9b570e3350942d413d4a62c7d2985599ab3be3f100fb2a6960f4adca107ad8df5601252d994dd5904f54138ac9ef2834ffc96b21e60f2e3be40de123b334c8b52956fe923c512874394600c873a903f3e2a18b3e1dbf5cb640f41ab18cc9bfb627eb3d73830942ec27c80191eed6b27f4c6a8e1e29385f1fd97d9f6060d4327bdca27b8a08421da6775d5b98831b8f1e715d0a21e7087fd7083c7e1bf98ad3bde49e6f81ec0071442152be2e227ed7e508d4ec0ba018767716a800d882992d75191e4e83231724dfd9e597e563805a3158fb93d63d7c86d693d62f27ffdf138f639092bb8206515d1280c9d61bc4605878b47b59e0fc0da96e61897ed23914999356c5a2637bcd4757711b7fd45de4dd7cdc786b3e3c7cdacc5480d744c2aa0fe5f45ec01be7900034938948d62a24c947fba5d60b9dc55aa15ebe457d99bcc85cded031cbaf9a62ce2d2d1c495026474489e29ec376be0570497c6c2c5f80419bd8325bb61420c2ab8cda04485e0ece768d603e15aec6303e83451b8554d3b2d4d50e498342cf83ba4f1af4b1b59aa10b4258dffe6867479ede8f1cdffef95b0260f7cdac03146bc68576ee3beef6bdcf51ed8bc2aef1e775387361d0e49de19a30f36ac743dca381371a9022e119b122a32cd5eab19dba7d45387ec221f823b1bc8310f66c8a7d247e3c3c8f2ceec875c05dc11d50794963106b1bfa59c19321ed1f7838309e35e221a0fdf16610d2076316cc4a3fa115184a9f29592b8a002cd97382dc618e072373171777a788098721f0f3628c203c996cd0bee78a8250213352fee728dd6330530ad7d2eb8ab4853cd9133c4964c3a6510b28e4cc656aeab959e977f86c89bf63572d9c946716fbaac18011e87801409c4d5da6fd397e3b2a639ab90b6ac150719b96e60c395cd1bcc54492cc9b8e5900e16a0b30221e88c7cc783990402e57d05dab21da16fcda3d906f06aa33f98f9fd90f930f1c49c8b3c8ac18bebeb110668e56efbdf2ff9286346bea82318f5703e55cd27f7fb8b97513e71f83833118e59465d1312ca5fd016596d39eacfc8708d2613d98f68a04e116c1500f82fb39da8254ed32331bb489cb45e2dd2d948ff772e0184d40fceca7f8ea96b119147b7c06d87b014fb10171fa5c76d2229716af6c5a2b35d691a652f96347f02e7df99f7d5f268b9b7d44f1ec7d00d147999bbb7060e5f4169210ded0f7cf782467721d61618f79c57046b2496b0b7258dcc9d32215d5c883371559c45f96329c690e57997ffd28896cd2175f24de922f173c205921d2bed45f967779cc69d24ce4a89bc0dbbfba634f71a39c1a9cfc78e0dc84a515fe456653a7f9e380122f628a3c7a14132f1610278b4a738988b78028d0e071a121f1b29bd9edc01f97b303fbccc6d2b139d1333fc6e47dc0727b3876478737a4a45a5609a892e26e71d7ef5cdfe3083c0330682d4ed226676bfdf54052fae7c4fa33f4392f123536d2fb5fe0440b092150e9e8978f2e97563aeb80416b4dc80e18c8938c7d22186fe888c5318195ed3e238db6a462125888be5dc224fcde0fad00c7179502a6cc66977eeb19a5d918d01cd4158d2e0f9fde335d2985d9d02ed03cacd0e9f0d011db68ea3197954b211178cc7bee19648d8d89619c49be46ee7e69bb8bb697b5e7f61fa64acddeef14668861c2f975bc1b9f8965c32fcbe81e299b9b94e1bbcc3f8201a7c71ff2352f8bfb8b099f73197ada11e88cf9c3d188cd5174762c460385ff8c6eea786d537777f802ed2efe1a7f1842d1d3b62693640fb8026596eaccd09d95fb2f5a9e7c856c8c636615337c634"}, - {"0000000000000000000000000000000000000000000000000000000000000007", "0124a9483114abcb470b4644f2cf0c79d142f09e3e1c028db098d6ec64e237357ebd60856a0942daa62f3cd91fff2593e1ecfe5b3aa33bfac3975b13fb5c624fd5bc1ef29e67c5766de67bfec9ed96e1d35c52b60b7d350c26ac1e1d947f256db25b470db54fb536023ee5fa6711a71ed3ad058423c2f1f0637db17c3c1e2f94e4e1c6903a98a744593af94bdcc699761576883b1b9c196cf62827bf5c886d88de70bacca35a009e029feb8c978f97918cb1d267f52ebc952bd2d4162833638ce3f3d6b0fbb1d2e4b06b2afdea241e9c815f1837dfb801699b77fe8942ea50ef8274e2604d06662fbcf24bfd5129709df0031483fb5f412119bfef390656821d989ed737128d52bcb0cf72be7ecad52bbf06c48911f5920a1f86cd50b81971ac9c78101e93731887a562b794b7b59a84b55bfddb40e58a663063021e88dcb6789c86573b6c522801eb34c54dd5b63d6e0346a794486bb9e9a8d57adefe6ba766bf28594127386e9bd98aa0d69d710665f37078234d83c8d6da651c49331deee7d5f9ad338a1507619aeaf74c5bfca21e3ed691f972c66bc4cda5fb9ec1dc323323f1c4fd171b5e3579063df5996948b8c2758aa75f653d9ea3355b03d3b18d8360db935676ad37de3b7117be472132c86b3e949fa9798aefa78304caf386c08f7ea9d0363f0b04106adb29934ea473b5724a3543db2acca711ae95165e9cf7c74e8232e4cace2fb8bf35a8e24814c88b8ca831bea3d71394ebac3de3d643eb3cddb7165df9d0229a749f4db6c846bd44c33636b8f96e342020fbd424db48759ac6347cdbc5ec770fa53ccee0211a12c6ada9bd87543e89257760729eb5527655f123040dfc3717671de71e4261c8f2f2fb72debf8bbc25f6eb6283d1497cd1c31298576b0782ff58597025284d077d70e4cf999366b2c3e2dd56c4c3ed726469061722b9b9a38115efa5e71fb0ce736662df7cfb9512d3c5e099ad7cb978cd94a7aa3ad76d5864b0a25a27cf6bf1a7bb69c1fa32c33a2f4e51d9db00d5f2e45f5962fceaa2272739320b4394f2ae2333e3dd4a1d0ffd6cb19d5979d756ebf1d1939d4c80f8d919b85829424a929790855518122525e96f1f4580ebb7180119f39386de894be4e021e5d0577a813291cc5aec652b160f2d0ed8af4b30ce772169f35bba4fea3a8ed006ccc5306ac8139c4510674e59f0bac1eb1acfc9da27d22c4fcd20fdb741ec03262c1fd7b50e2c5e2a170a0424b8e69ee5a33458715940955a4c60fa876d140b82d19a8b0c760f6145417ade1c9966bc837a6df609b03af89a6804af498d815eb66f4e72ca0a7c34aa47b224a2a457ff430b79a62e74cc4773a40e5e4627246dd178a71ef2ed65f6861fc0c53e418af1cd928047bbed2aaadf8503fd29253a91daf3718af2df3eaa0775286d46b8db7dda05f1b602324aed65d05b573f39b45ca4457153adaa381457c42b90bed706d7cfa024fa1b7b80945305a43993aa95461c1617a439d0db38ff8e09cfda99a192e0c60e8d53eb17a130fd76370d93e946de975bd3446501dc532be416953db9c0c81deadce3981499cfc3ea5628817cb64baf07ff2f8831b81c4285cd3ca55b0873962ae6b0c951203eb5ad3eb3ef4a1a52a112dab955f68cb4abd9c71ad206f20c9882d757f09c8b8dbc9162f1a5f6b8a235d242592c740c559e1dd325248915c380ddb9b65052de1b1f0d7e3e9907a738496c48e0e968ef9aad0ff5bcdc9f36b3fc9e1319d945b0e1fa4b6554463f6c595f710a0ff791f59cd0ffbeeea1451e852978d1f83454d0d738862470a5f261e9a07d461bbd43373221f53d368f161577826a099bd981cd5867ae7dffc288307e653e437eb74c1692908caefa967236f250a7221d5cfa73"}, - {"0000000000000000000000000000000000000000000000000000000000000339", "005b0b144ecae618b78d94feb671c1cd62701926100102ce1111ee9b5f876444df913b007a4ed875cb2218516b240fb910a9e67e91cc14102011fb67d78db42183bf7fba6d15df6f75130fff65a02e940f1fa70000fc074bcf57d47f56d210233a40c7af88adff801e07df5941c24a6a6a794221a9a6acf8a8ea28bb471d0954c6ae014381f9ac162173092fccf0e074738e36420ce66bae78f17df294747424c5d2ad6fa6d6620e040c7ec25e8d0ea7c73e55264e7d80a175681182951a46c37b0f9daedcfb4e27b2fad8142e059d59d57b0ba9fc8629cb85e35cb5d4df38748f7bae0dbf4cea39e2f77507d99883c1d9942cb05418c23405f66311058c0423862ce173b23700a092d8571a8a3d15826a36a28462a329f56f600645bbb859b83971cf5e696b12ee6ee6dd6327fb49a151cdb720688da3cbb31f1b2d1c4c4db6e7ec09b132e2e43676848a08e6f812ed0332051f6e05cc651e7b73b36cf8720dd159dad37b58361bb3dc5eb0736024f6531a44627609e11af8be03ec1486694353925c608236eac9b8fa19405a01452e43175a998f478d2719758a7c507b269cc81c060e033c7fbefb8a4951303ec6d4b74bb5864a0abf5cb416e9adad5032e2c1e41a347b8dc48d335c7efb86c1095d95fb0b4de0e3aaf062fbcef3aaae316dde18ea1b2a2ab1144a13ebdb0dd1dcf10f199d4b6e9bf51f0b61d5e765f48f87e0dbe2e210335bdaacc7df52e3190d392ea208d8004dbb642a3a3e5f3d3d787d90130e094f06405afe599863c2660727cfadf80cd35751344d4d50e29c7a33717dc58a0d7bc0fedf1ffcf3b80c266591a9728105cc8ab35a14e0b2d5b184df795f62757da16a6821954c31b75534e615526f81f71d8c2876ca0fe94fa1d5479f154a78ebbdf9ecea75c8ee289837ed6f20533d78476461912a3ab71273bf93a2018f55dc7f4255edec0d4280ff6892c689f17e6f994762ba94d6dd3233b8652547ee620981a07c1bc4a70a1dabab24698ae5b53a42b991e771acc7c9a86263294c9a33e08a65251c25f6973cca1ae9ac12fa3791038c55d8544bbe7c7901f0b6c2dae02d363ab98e8b24d046fbbd1f6941676564456e65b2ea031819692204cd1eb5df28ec07508ba1cc9946d6188fa5bf49ce07d72e2d655f7845e913e1f5de78517176d0f5b96a03399230ba072d986a38d08c3977467214c3510ced176aed91c6773671e591c1886d16fea2fbd5d9464b1de3ee1db813a6276a5543a1b7e702c8fc37af44f124f02fefe0d3b879d156f619c0cfdcda5014ffeed00626247be75b2743947d72af8951d82a746a1c5ec320b6db327508dde108bc3346fa4e4cf3030affb0e72d2b157b660e26dabec9fa968a6b3703a3765f4446314835c0e56caa81805d63ff0e66439572d8ac9b0402c6cc06891862333fccb03ac263eb0a50b595dc692e858274d3e722bfc2fc441077e1566f0f99be988f081fd95dcdc83ca29581d46d56cdce06273358b56515c071d68848f511c0f3c21fecdc543f71c17c5829094d53f884a6596d9b2da2c1e367f3aedaa3dc6e080b1800f1b1d7ce6f5c2552476dc7157dfb849995b00d5f7c2ee003d76f3ec8a53cc4f79765a58fce1aa736da67f9936542e571d03669da6893660a6adb24f20b6ec35fc6eea42b9242c5191140a9be8a561fa71221d2dacab2737c2baede34867233ccb17697983c9c100772333487993d620a92d28b5a12a92f897e611321a6f1a6a75f59f36da725d8dd5e17cabc9b59ddde0cafe79e04e7fb6da0675252e6c687452e0e3c738e326606cba192c5615194c42526acf0a5e575b95da919030697070b1679f61b2257ef7fa2b71d051e91892fc0dfd181341597c553c46449ca7c032f80bc1169"}, + {"00000000000000000000000000000000000000000000000000000000000024b2", "0026ca22cec36c1937fcc1c7379c054dced412c3a11becd6e3cdd3090b1961a4f9db3c30b6ce55f77f32187ea5f868a23955853a54583ae4cfea025f159044407c6bff1a9bc6494a6ed52d697cc395c50cd879f30637d73f100409a6c428f42a0ddea12a71edd8cf3f0c1da21072091ef36e6ca2596c1e86be5b107536e420fee672cfa22417dfaad91d906ac41a7b8a76c0c2213482f207ef1c6ff95a8220e27bf2eef62019256203036f50bc90809ebcc1db0676e52d1b4f033ff903183b27d45f0f67ed53f683fd56b43a699dc9f091e117e6621e9af0b02be0992a733ffeddbac5fb7c49852ad10c2fa3b0b92fc0cda438c8be0e127621f6e4f90df4a9b703d1e74297f7a1c8b433a016006e1f769b155d6e6b01ebaed3adfb83027543da91799d8fa49219d7eba700c94d88b836f3a3a5517ca57f3d1120ce26d189a1074b6f5abf75d3100c3157b925e94a46ba042488e905d47bc18880e0a828ef9e81b832d0b61427686b2940ab3be198d307fc64498f8baf6f9f29fe193dbf9b9048a3424d8f22432ebabee2ef795ff8153e4717dea77004c3ac24a41d272ea9fa5dd1d9b8c605f7b3180a83bae378fb52a1b26deabcf0daa98f120c10d42762a6b72d695331878fba6165dad0734e3f08059f7a8dcf7c7cd3d440f009b4b4f58b6af1258e14c67593f89a2def471fb5b2e9b15e3df016f5132407126a431f4304488d3550a88822234242657a07311c198e10044c9c3d4692167a6df59e6a5ed7f51e970801b91e9c8ae26e82225168421fef16ddc579d1d32dd74d3b661beaf5a9ffb4d7c928db3e5937151abf0f29137e4bdb99f9ae96f34db7487236c0519e024223e1fcff065d50ff938d9581947aa36d7797d761311fe1a9ffe3cbdb7b3f43d6ba76c465f2544cf8f12f25c732c12d49a6a274be62efc64dc8f9ef24fb6a3b007b7d568c544469628b42c3e371db76e542be84311ebcf6c07ad7ec34ce2fe2debf3173971b92df9b150252818a1e0a4339661f46414fff41a201e211173627de34c75acdf3e35fc4b843d2efe7a7a3c8de46b600d1ea22f95e318d537f220d99a70c192cc1bc8c850d8a6f977a498c94673ce2a04532c1c30f36199e650519d318b2cfc205e09b64ec34b98e0d3c90da7f604a096e33e89e5e15f139c501ad60cb6df2e731449f008924270e05119342a3a2e24a37159db7d13092411b1d06728d4a07671089d3fd0c5e642df3da5c2a3c027426696f9aa0c38e43632745ed08de3c47569ec214922d44b8e6c077f602b3a84e4004a2475c1c24a201aa2d9dd3c8f6753d7c37eedeef7a22b3d81f6d531a7ebe59d36191d78c59551c9ebe501594447c947f0c308f1641cd72976f5b69dd2dd76aff033b5e91141f7d45593710cea54f65e41bd5bd4fe9c5240e8ac201b3cf649e01b10c83ef88771cf783fecf30383a7b593f76a758dd518925a9f883897ef44f4d523d46e70dbbc5570c1ac3618c3de53172592a9dc537d4e7d20f4df281c1919a5ae04ec2a2191b8ff8ecc8a78ff302555dedb24551e726ef62121de15ba173c0bd34ab1e350d749fc8f3135e05d205691ad2ccc8257a957b0803c20a3054b130cc2ab5e9ebdb57ce1e3e75815e0bc0e21fa5d2ceff943034a3c5b887bd61b33f647d01e9bd2c2e8dedfea5a8d489ffeb203b518d7fe2a829a29e4cf44d18615a0d865435457481cb69587d3019c334b55789a97bd1dd52bcf270b23d165aedfda01aab0afe6323332328c1b223cb4ab11278487b1fd303a53ad1090b4bcd4a17b152352e2761a0642e4a780f6875ed99cfede0d08e978d6cdf083ec748f68d41115800d56026bb0986e274f52a58049aaba31618fe2ffded45a0632711b138243499455971f3bbf5eacd"}, + {"00000000000000000000000000000000000000000000000000000000000000cc", "00ef1327f8e5fd65553e60ef249d8c786f59d7bae20c9053dfc20643233a3ab273223f6b46308c12cbfe0baa7b3f716c28cff80516b49b79fceb38ec1b7dee31fcfacf8a8feb3daf0d036390e0a0073bccbc2af210838f54f38fd9907f96250c4ed258e5b950f393ff1138146d8eed4e4d7b95e12c2a982e29e9d998afd51895228fba8bbce4682605fd5c45da65f03e12fd2d4294a5afe696e430e88594791baac0e5365caeedc70291d9c6458ed294ecaf32b4915c217958ab6d27b3121818d41e056014c457c1333c97c1f55f3f98c35b09f6873bf70521fd6ed613bef8515a4b58c7fe8f4335f65b220c1ce6695250e50f7cc8eed22bd81e68e507f5677ab75adc81ccd0f55100b6e7a9e10b51dcf744a7ffbea6746e23bcc81466bf5da1070adf79f5ef10598175c6aa47abb1ada659136114faec87996c8927431acc98e890d1daf8c8f9e6e0cb8e50d67ec45f03305c716fc52a2429d061947af64c51d579b3c6430b2fe493ec5acc0fc104f276d1e7a20532e58e24a91076d722914e1751c2de5393584f1f61aa02b22d8524f3515e474d1ad8c98c134582d50c8a8fbe9da4150a2422195656b361155214b5bd79fa2975c87789fb13cf2ce2c0ca5bc0de8d53ae783b5bd9fa1af10be50f6887d48ede2e77b2fd34c5f65e6e0677c819dc8d113c9de81aca9b4323f2366c6de96efa11c294e1fa06efb894f84973b7623cf1ef59b957ca17d8fae0a90c1a0c6c010401f51e4482ff3257ffd628983b3f122a875f50ba1d0ec1aee0b2f931ee9274c4ee70cf333feb6cb08f21a4fb11edf84157c828b7727f3c729c0a0fa1f45f1d0110ff1e24528278956ec4a8da544c482b73ad873467dbb9fca69e3859512abb70772e72170f00d6ca6cfd419e2311e0c476ce5a1715fea60a198d5d1e3cdf9df3eda682fbb728b3b4e6993b4ba90118c4561182e9e0aa28b2e66a6ff0525a2b7fbeff3313669d36e44a014c0d2745477592c654a1f656fc0b75e06254f3cfe9b4f0814ad4315540956d1fc179370e25a73092c3c78872662e58f5f3b26143bc7e9f05dbd98cfa4b12590a0e34c00c5d048f9c95fe20ab089206acea458d06eb5ef1326aa09dbcef945f78920ea21e81f4e52e79e2517a726579c4c3d3af3fffad2f8f3a02e14c49506c1bb761d5ce5f8f6b147cccfd0317839dd354df430a5f7ad1f4fbe8db7e6e9c969f1137d98095527757559372e5fc479eaccc5d6baa5a0a06ef9608d5dccffb2d92719ae4f6730923fa4daa1b5884a4785d9f97a1d5e73e9fc90eb2910998424911274f10ecd1a860d35f7177118d9eca1625f42cfe2091e34e4b5b3e5572e6958bf0d1a9818b0dd0a8d413666fff245c37e3393a21a37da18d0110022b817e20bc4a3819cdd0a4b0fc4827c0e16a2f88209fc2e301e5275341a5cbd766b9b8a44479f8527f6c194f78070a63115a598cf9c14e7479c83f18f62177769c2a05fcf28b260f263cc06583903addcc52e322dfcf61287cb7897d9091ece987a2ddbe588a6a979977d9fb0fe23cb8e8f0c1658df903764a75b8a23d14bc4c743d785fe3b9695443f42444606723808b496afbe97e37770b3918a05be3ea1084b44bf4c2614ee2bd8ee144c8ad3d4bfd152dfcc5a80d8872d136ec46fe57ef07cbd0e1155ec8e560bce2330cd1443e00e5346ee7106a7c28d7d951c5359612985425a2f9be58f0462814a2dc4c1b14419cb3be0291cdd81ff61b3896c5c7230374a127143269e6d2f45e673f5199fff93940530902ab1806981382f7979361bbe0695e4662f39aa811fa176bc0d77259d963f367c9e7eb40f5333132c60e4cdad5903a1545e273667aa8ebd42216405e68d131844f1578ce591eda470aaf0cda3d1f2b141b1df4"}, + {"000000000000000000000000000000000000000000000000000000000000138f", "005ccdc553532b92e89982161cf128fe755f1e25f30811e95b59086c3b30ba6396b8ac8e0d27446f749a325ffcfdc7300e199b5bb824d949883e39f2f4933132ccfe2280d1d24fc3f82357eaa9e132f1827f8f4d01ea432f314793715dfe35578567e33a93c0be85a217a79f4a730f3625ab93731c1fc01c6f86ee1dd1880acac4998461e32950b6373c35d8378e6afc3f10660fd5c6f1ece9c09fd1a632417c4841f1f18d794ed803e7449268428c6c26ed56110969837aa03a37b72a2290e640486e9d6d9b2b42cd7d67172e85ae3794be083e35a9b9482a89e27c19485afb2842c8c0f941792a24d5718451ae90d77d84cd5edc47edaf0b7b66b00aa607e59d48ccf08194f2c04b990110fd2457188f517017cfae2ae47ff83a3559bce297357371d28ff21d046a3a401504c5656a827a5cddf96e6852f4d70558c3571f7adefad5c52e162f57b9a79997757dfa3206574c4a81126796b7f5130610dcd38acfde57df1714b2bb19eba14bdfaa7e91a983bbea51b524ceadfa2f35cde5390c1fd59bb4f77e9fea37f63cd4324b9642176b68069fc849594254b571d8ead30254fbe879096a46bc2393cbb7a76e6670beb85d6dcd906f44860f16f9c350d9aed94c3b07aa42fcf7e26e4d96bcd00ca07b0cdf8f60898fc2e4230f6803c5e99e53a9dd1f54a1d502d3ad97161d82b5121974eb3b47fd6ff809284abe789746df0d4662852fa28191ebd5b80cda2811cece07f29d5fb2c05568526e3ca2b35f7b8ff91061093661a23b712df134da00d7e261aa2190e3016900fd4330a1e539c0cbe94e14cd0c8b87e71d59a00daeb08a900ed3410c5c820817456c7582bbb9365534b0f710020d7a454a7bf7f06742469b28dbdfbcd92c922587f4e4466394fff4f872e4e65e4c189fa8f23a3142ab17548621e07b05c3dff647526e78563c8300ec300cdf9365173928352d69e1c75d7e9c588e480fa294a26650f0d7ac0fd2893abb9332979f5daa9a1eae2e0ede1a51b4ecce926157725b5147f93f034731d356166eb60c77f091c3951baadbf9493e9a3f320d4f5510d16496e92b9ca1505a220d48c86a0c6e8444214f66291c9907e11b478eb3435032364ad829a34340bebd27558a75e65b34f87eb9a7cd7a25ba86a26b829ed3c32136afbbe357656b41e67f70695fb349027406ff0af14a25dfe56278a26a59ed20423f89c1035d6e53175aa0c9a16a9089e78ea05ccb6d68c38a08b56553e56f520be89910901206455397b45d882c44fe2eb7fe28674594cb75af6abb0931c389aed1e410fb3471892460d9c1e9b717547174ca0ff932ea0a4208e25ba763c291826a447cc5e32941cdbab4948c14c7335302cb4f0ab2e8f1ea79213498a790f9f8196555d7a3606e9b71e1e0c83c1ec9ea439dd33d8e0c02a08a72a896428b1ac8929f72fc0085a5f4df70510b24a8e9e3d42b84c76e65e94d2fb94f8b185fc7721bc49540b74fde3d3c38a2b33cba6ca25f18d4778b52a87f52956d1551ca88e59254af1a21a05a796cdb0a59f6fbf8545a13b52040aea237dad97be22e53ab2497da2e370a3c855d2e37a873bf478692d2da94c6159148d1dfdc4b99bbd444ade4f598116d45b8a8ab1df65f75e2c7c522820992f1cc47c676c16bf6bc3803d37767a5f60895b2ddf41d32563b82d0447988cf0c0ffa8a980e8331d595719d4bac8e2d22cdede8a4131b7c64585cba15416b2173d4e153946c92c405d12890c779192c395774411348bea8745ec7871c98d203efa07c3d7465bdbfed09aef6cd89133cbf9cda872c43cc46fadfc17b16c6c819277a36d67f28da34370f184aed92c7c062552bd3bed2c7f861a9dbd9c61c22dc4e2f28a6d7b144bfb9b99d5243e2ab33588d83"}, + {"0000000000000000000000000000000000000000000000000000000000000b6c", "008876c9418cc0fabf51d07b658daa5a465cda0ddd05c3042d8daaf603ba9245a1c2afa7158f485c376e27232e770956972b85ec33db7df69901fffff136fc4c7c9ca7c3a230218694475fd2dbff8ee74d5f217b0509fa79f307b49a8f03d367f841564f4eb6bb18404ddc37538ddd0d17c2da3626cb675b6ea03e796f1f168ccb5f7cf7ab51c22752a8567e96d144084c13c335fbfbd654f86015d4da1401916b7b9988f49c5d3109cbde61aba236a9a914b21ee040ea1f836d3cddfe0d379541091debbbd03ac0ee8f6b9ecc508cde61b41f24c9d2d326626dadb82249a99df311a54f7e05e020f85e5174341ddde5fd56400e44115f56cb1e4afb0e12c66755613f2f5fce152d29b686154fe7b362f126d04ab0828a7b85dd7244e03344e34de59cd7b7bd1029a7bb868ef1233172f97b4cdb05674c733bd9162f28849eb310e276d71dc46148da21b93f894defb30251c6951d5b4e2d9397777f404fdd96debb59a30d30d1db51602f651df858940a576fef3585534cfaaa1b5875b2170d914c91fcc514f87ad4aedd879a9d3e243d8f7c0f0e4ce68acff6f0704f01062a3c971dd50996eaea3891ea1fe0cf216d8b70ccac8ea9e4d2f520b48cc0b79d900ffa96b3bbce9fc8992d248e83f91b5aed1b975d6f3eedfb1274c9fe5fe4a19477249b2995532d98ce7d9dc0d152ee05cb7254dc6e115c2f06304a6c68486d29b3956495fcf884cf4781de5cd10e60c99eb41921418588c7c81056c6ae0d5973ab322141f9bf5e8ed59f133934879859fa6627115c012e41bff31cff915fa15317b59f01edfea9981fb032270d8e749ea565035d47b6c23a959707e213f6bcfb2d13973b43b5068c8ef618aabfd65c0bcf47e97e15b11d641af00bde89b54c5e82137b3e39bde38493e35e31bdb5de184d58676374247d89d7317594feda81f2036d7bfdd387c4baaae400f187dd0f6d3df577a28a0a09fa9e81d9a9ed605396a8e661dd22f2eb9b6ebb09d8bb139c8c20d8e9c2a13b05c4f4954a709d463126bca3e545dc1125fa5483d1e9fc6a69444f8f4bf80b16a1728f9dc14372c326091655b8caea2c19cb4b2e7d83f4191f355b9e00e3a5f42984b7d5559f61e818d3be1d628a725ddb6692c9e3e1167ddab71ced541e8cacd17ca4ac654b0e33c2743c885194a911755b0cbbecf775a6030582fbd0e277c17308392a3526692b59edd6f8a4742330ae741a72d3abaf1b02992a341447871ab10c8108dcf2f67ec5ece479e55ed5036e241714670de5939382e4627d0398daaede6afabc71197db42093f3da9da0a093003a5c61f3a23f1fcff72aef5230630d098b367863cc31b81d96614b3f1e352fa0fc79dcd907934a8ad440dab9ea72e3d63e52fd32641317ddd1718cebbc97968bd5d7c9e663ef3c350623bddf64cb8c2f26d5091de0f2a9ca96ba1e97e01f6c29ad9a6aa5d9fc481605387622ddc1ee914cfc2745214407f0d529c1467458e5cd9c31adebae9f97334a7a37eb9e6a274802354dc56206e611c7dc84e409a61c4cf34ff43de3b8846afb75cefd6ef7ed382942bf62909f51167162d515a85f63f49f38231ea7070f8f95a543c434f24e3b44b78747df95b052fca2162bc50ab1c4a30c4f58ac25846c7e52d1b5a4f6cae209e152152b03f2a2334ae9e333ea41fb8693fcfa770e83ae46d1a56bf35a506641816f3a4a24281ec6a81829ef4f26a10fcdf40fc24bcdb649a73c2d1a772620957e5fc4e89b3d46cb66e3da4b0f7e4eba1c28fd0b2b56c4f65e0be6fbc641b0a15e6bfda334d4b7711f2d95a490cc3c92dc94a344584a4df32a3dbedfbe3f462624d7dfd41994d254f789fab49b89691e735f4e6d177e5a3858cdd72c5759c7dfb8067998ff1dc1"}, + {"0000000000000000000000000000000000000000000000000000000000002b93", "008f0670a8d50c02c3e44514f265459da51df724924796ec653c398f0fdf88b5543862d68dbb489258d1201e423accd7222f99ab0276d6d36e8d31bff3ee744cb36f6a4a23f72badafe8f635706aaa86e1758acb07fb70ff8946e8b1a65376503fb6806ab443b80f37175470f41b160ba1a0ec42e606e8a77b9cfade53f60ee08c182f083a968c1589f3545e824a7d4f158e5820ccc67cf6a3b807d4c8e550086d9c130f17dbf283046347155550ba735afd350bad59793980d3991145148da12f01103e53977bd2469d2ada767d6a3c7d5c38ec7d1405b0dc25f7f473cdaedbb53399e09d01bc431f0e7c5ba1e75365e86483eb37da69bb6f7d40c90675b04aac0773ca6307135ce0dc1ee6a1bb3f068b250bb45a558a77b073f4b71bdfbe8919f5ec592f5212738a276c6f4d61b37522429220cc4d0f97d58e9760c0bc7ca76b3503bad15880005e654a2fee34796601d8e8d182c6e8ec895040c8f1ae7b5d040df90c9d0ebc4241335e1e676dfc846c4bb068a22ae459a7dc17fb294cfdeb26f79790c55b1d5233a99491bc765c368b9462738e9face214e720a771f383059f9ba86a0cfa3f48941126f6ba193271c8627b45babf75a41118c94b759f2ba41ddb51b9360c4a8232f41a387e8f24d76c07e121f8bf2c2bb7b8b7ca3592a0289f60e09b644ee7d3ea8e9b8261dc04817d798f09b57a394601f865c71598e459065f9081e087c7509e59ee66ef46aa7719c593324dab4106d1fb47c9522bb2d2464d28db9bd61aa0771d10e374d1e0e7024aa75ad7064a2ca29fd37f9ac7331e1635ce09bed232c599fa0d1b033a628de59bddb3640d15a6e6eb7f0ad3231fbd2b07b393c5d15c7929ecfb527ebb702d0a1ef6fdfe292d4d7be16a64390376069af8acde8902da74fb098c30e4171d6c518b86ed9a23e11ca50931149ceeef0b00d4bd1cc3c2de1d9ff643dbb52db94f74241e285b223c379b1bd56f71683f7272fb675794da3d5a36d70e2a2a97b9aabacfc535f50153b54701f0e39b1f6f1506246b4f93c8337b14c5e9ed7060661b5916ef150112bad677f0fb6dff40e0fd4c18714986ac7be8d514a8cb3806b83cebe9851273db56b4cc9e9b4c1c6a0a5f649454a6c4cd425eb11fb9c50999d77630eac61d4522e35e0e8c155bc4744c406876a554109338a600f5c7c7198ee143dadfa62be2629195902c5504fa15cc00d4a586226fc09be7338efb62c1d0e9ef4af01bc51c09b0c922ffd69ed751a25f2daeaee25880391cf76f00fedb801fe898c3b0896f9376120f1d52e102ed3c2cdc424b837ef852484ef31bd5648c51af413d9db503f39cdf51a1b24a7f3367a95b0abe79966e0528139e1009d3bcf99bc62279fc1c35e7c0100f4717ee03a4ae1ba031243b122e97474a59a6615214cb0b393b138a4c36b3f915556994e7348e60addbb0a1129dca295ecea19fd6ebd3a7f16e5c76f252b7b0331b1957be11670aefae3531f9ea7141c8d6378e25b51b1a3a71814b10bbf18763de6ea1e26e56fe9bb5991f2ffc39301b27a587fff20b6314393e1ab7124ece4c6d7e817357f6fef290d542d4f96dd5fe6fb1b3dc230ce416c69ec331fecd52710d3ac9bb6b605dcb082543b96b568ae12ba768e8af0c53ab1714dbdd2eb30b7b86e5fbd7b875bedf8357e079a9baa87915cd5f1b6f01240e2502c5e29f8349f9f8e2767cc2b58f2813d6734acb190191e865c2d5f37a0e675761de92ec2ffc7636b0cfe354c562f37b1752646dea7d3d78570b9a89e994c530586220a8ebdc5b78d23de1b79bf516fadeed20cca40b05ee75cfca57790ec66cb936882426acdd92738ec9c6b2f2b0d4dd30eaf1ed5cb6af2d58f98e8aa5018f8512b65dbff3bacf12dbdc9126"}, + {"0000000000000000000000000000000000000000000000000000000000000399", "0105ce49bf09a646bf50f7b731f3bcf2721518c65a30f86eab949e7ebb83e73382adc20231341a9be93009a89d3f238e7faba90ce857bad9763e41b53b5832221459ea8d262d7b98a04733ccd908d1fb5a75195f0ceea9c95da010c5b04207d681684d1aa37ddb9cc754fb07234957be12f5ec968220e5b3c2adab9d2e8e17d3ec6f78a1b7dfdd219271ae958d34d7ef15dfff372a4af62156ecb2c86ee59fb248aa09fbf510cd72011d90c1d444e44de9f6c0eb0f7ab355f81053e4c82c4be498a15004a3fab4d40bcaf2f4a678e45644f5103b9ebad49fbf29c4bd51b5ac9446363270b48f62112a1a27cc62005394f90705a4ba8b35d6145128b40175991798527146fff705982761759a844b38deb323f8f61a25d0d3093c5f22d5a5bab957ac5bfe1cbf184d62ef5207caa8dd6203ec854b108a4191d3b4d22821bd0cb2bbc0e9f6e1d490cf547ee934511841af011f73d20caec2a7cf54935d16d5d9997dcffa58f10aa2520ec963ea8d71698364a2ea0830e9b4cd259f166d25003f99945153be36f1ecfce44a2f6955b12830b2b9ebb6180601491dc83c5d75fc963706f594910a5a2c7eb39602d5f29700ba37bbbf8441cfdd8b052396d5afc549bf2c9ed545fe7ee5495a325253acf60ce8a641cbf07c01fc74fa50cce8fcbb0dad9ba17b0d154a33cd62ab2fbdd006670061dd1ec65af656930768b896cb96c16b2caae40bdf4cf945b35158e34f10b513040c06510ebc0d3385e25aed1d36cd91dbf71872321a456f6c0f9159d571bc5eec7a870a3727353dcf4b5505a3636b4b9814096bc863d6b723789c870f17e74a5fdb2391dd38e3a37cb0b74d283e9d75f50fcf3e8b7917a4d8e1e4b18c7b2702a8aced2b74461be966a40d92847dfb4bc20d0b6b39b11f6b4a61f61c3a147065cabc9b1b5f43295a7e11d13f086e100c01b2d73861c65d978674d1a13363e48df4e9be0ec806770bc53d99b9a9326f81fcb632bfa9e7c5bc463e13c05b0a82871c443a4e717a9ae98861b4c814aa49500eec9fef9f95f3aed4c64f43e4352ec2dd16b082109c098c2a1e23b94b2ce26cee15ea250fd79d89042a9152f7ddcddf7f266a660200b58f323746b2e51313bf8b14b155e808d0c6c1b17f1aa6dac11539e26e3c947a905c1515757f8c463f26ded79a9be3bbed35146a556089f0aafdd2f6e1aa1d77f84d43cd0ca2cb2846e3658fe23d6b8fcf22aff7ad6b7535a3516dd217d56ea468715543926e8582a0fe55122a7d3e4f944c76a676d15727fcca0446a075454966dc763ed1ff1e2483876397c678bf4ef6d4264c27fae59e58c809283eb7d9ea9a4ced959dc6052abd75866db29fd07431a655763e6642f53ee79ca2f6ed603f7b9dbc1d36341a91aa3fba0231e6da8638a2cb8c35ff2eb1bcc7021e4ad37e535b49e64973a8646d514abe38b63c1406703a38620d8f38ac96fbaf8e6023539ced1fd1d01702dfc941a497c78f83d21726b4b1437056fba36c23cfc7546d8e48631e5774a9b235d72da806f3e055097e9b6b921f60013d816343cfc91aa0f446f661f912719aca431e4e278a18f3f4c55b79de776ddc38522bc7bbc361eec6a5ca9f939535a28f32cf8c7f19cc39f15ec03d16aacbca34d3a184a0d97f18ea1e0e210ae32e722a95a51b6341d12fcb1ba6f91e035ba958172b7fa6626ad2f77606e347b6cdc4550c8071cbb51c5aa30a6514d48de7b205c13e318ccedd3e3cd38e1cf2a49528164c92ef1ac3b58b4eb5e9bd43dac1d70be32f650508190fb83518ac40cc2e9270bd5f8733145f1edd520d81b55e8fb17f407b01ed0522f18e920c738b0e57ea1ba5acb3b1f9f6f3bce311e65da576437c12d8f7a853cb521a297f3e71292b6c035e73fb"}, + {"00000000000000000000000000000000000000000000000000000000000010f1", "00148ce2afce7bafa649a72c2fc68d2b22f75c922909661c03f24b1355b7fa626f6a35cfc1584638a893054b3dfc080161906bd013d4fcd79cc101dc50bb2012cdbbbd21caab62fa53d2ecea544852c621bd0a65049c1854ed5b17e0eec0b0ed4074bb5a8946397b850fcb0bb7eb9aaaab5ea6925dd251fc25769631380d0d505764454d68c38f1d86debae409f70a4cfd7ead11fbc19cc8206ef171207474c5d7e28e33145cae8600ffb3e8af5d73c3ec0c72405bf4522364eb3c60cd0d765498eee3ca53b107b3cd96d4556e46f6571f490c2a61e7d0c61c504844a5571a57266e230eff1d9249696741702c4deb72c707502d4d10625ba19eb84c0ad5d5b9d891f1231b77071335e47b9a2e353fde3a3641afdffb734f99b7e6d8988368e56eb08d3b48632ba25f79d74d06755fc987b5cc516b2734095ef0c32cf881ebd517f0a33a62746565f05f75a6d65bb0ec0240e6e75e3087fbf56054126a4befc1f356fe69912cef93940453c387413c556fdaae35ea33f05d2fa1043528508a09db9fd9b4e1ebd2d3548909332eb56c31c56a4043155dafffe714165273963f92efde19da02e458fc2ec1d85248b463b274dca2e2f5f8db473c1a325392a6cf117f3f3fc41b53def655f40c9d34c0201a7749b0cb0d77cfbea2484212d32156883b8fe92f5b94504fad2ad5e3f416e53048f555bc05b1d7f2053218a37ec45d710a63208155a0ccd19fa2b227c544a3037d98f7fdf7e540e49759efe3cd71c40c5379058f3ad762a56087976e162d50b8008ea5cd36f8c063e91ca7ece15433927efa649c7d9aa774673f7fac0c942866b050de2ef53d94001ba774aa67c8b8e23b3dbc534b7b95014b1695d756b349b6e9da1ed4a9f30ec2d3d4fe747585b96ab2533ca298ba1577b848291cd873e122a263ff3acf953685b56f560d4091d3b6003a2d48410594b0ba345396c83e85159101599a7035be2e88b6d0eca4dbf5a914535e85426596d3c36a075c43351a4a54049c3081cd9e4a63e647041902071bd4bdbd0ca2a7594df7d34c514a83c7260b3b98030c86be0a290b13b59abc76973df4eeda518f9f006e384bb31a3ea295cb1d3cd75f5ed07871f460353b51136a6ef84217b0b58ccf24cfe3c18b2a88b6dc65df1af290df4dfa4069f462b56dd6461309e92b71a09302b5d38bb3530777a353f136d045942dffea9f337d25c376162daeac2592a844eeabdfce8a3611be50281a8c1b26f9a78133501e0603b947cfbdaa1836bb053d484eb1c9dcc5f50d46e9382776f0924f9c7635690a12518060695d79988c418185c92602eb953e12331b11c5e379150e53d585478bbe40578afaa4791d0727cfd20a450c1c9c7e2904c3436d50fa8def149c585a56043dc9f48791c74215f3bbcf751d800d1f136801ade205cec6593de3870564f44839931efc7f62da01c64291ca822a0f1064b668fd72dd5ad37b185227040e4a0188610487867f21b6f0e12e9b46407aed4205f68f93ffc68e6510ca525e327dc260db627d6d6504e42065168acbc3793e419f3b54cab9159f5138fa279504151eca62398968663d96f2115a8ebb9d86770cef6543cf13290d0bf235a089db40897a412d57a23c7adb616d172bf79c23848d1bcfb69d24c9cc748a0741a4b289e271b1ae3cc3ffc5e4c8e1346d75c7aa183b86323b0bbc20808e437267d19685399f17f6732911a968fd55a9f7914e72beee9fe18912aabd68122a4af59e4634a729d96433f38fe14e8a1ab018dbe70d8c1b3d7f9ba2910fd2f20010636da0f6be8c366a1c730589018dc0ebdc6b2556f036db81bc82ff31780d9c3990141f2f81d48652043e1a442a2396f628481c70c9084870f551f32be346a5d9b650d5abb8b2f6"}, + {"00000000000000000000000000000000000000000000000000000000000008a6", "000bcfc59ad2b22723cf557da9ef5e465d7f133eff4d447dec3fd68ccb6a11d7fe67de526e665bb8b0fb456a0d16b6da51ccf3af245f00bf5dea05bcd98157630cac81e66817d780f2e70a3a6545d22ed07bf3c90ddf60d1b8039b51a8021230ea9ead269c26d7f12012e782afeeeb5d31aeb9b571eecddbae8c4cf5917a3259fd6201b149359798f3b67fb760511b88319d4532de545c4a189fd53287451cabc9a4d9e9c496f80a007d58131b887582e77a634b7c47820131dfaf3ebc09e758dd352e23d7f06f345242dd8205279ad678110a328661bd64f2eb73d6014117a35b576ffbde88bd191561bf48de6861c58347de95550a7f07e27c09d90ec336f87eac3f01c5ef3191e336d4c91dac36fe4e1d3d430dc70c7945c82dd3b957b25ebb19449b1756240e77f5db1fcbc5b3e6e91cf1fbde734d781d66899892d7af4da7a5bbfcd2ab79216cf94b209e59e79c044286933dc85ad3ec2fd167443e9c8e2e21790ccb3197befae817c941a37573d41e48b8690a2b0ff6e30fd89da1c9ca7e00bc40212aa09e4ac1bcdfb27a4320f946deae4e6a09600c325f7de146ba75b51a096d073b2cefa1865b22969f769370fc7eea88945f06044d34ff2550d6b9671e0f46efe6fc9e1621d51e889918959a92e7706105d1962244bcfeabe8d00a95dcaf212d0dd2e24b0a771be0c647a7fbf456b97675eee4076ed1f91844ea6eee5d5303d45aa701af709f8874961e3db8d1aebc13e07ca99c0d7d8f1f64ef5cded142874293f525a47792f4044239def3b5715679fb3342e2c50f4fa5ce81ef766a979169e272a92e5939a90ab42333b0ea4401ef78d3c92550d4d1837731d18420b12f23470835538793744f41cee9e724f5bf2d901cdc9914d10d7623aa7624cb122cdd6e0dccb51ea75708f5109d6d298fdabf05901673fe1592a37b4cc00691a42ebdc3b294a6946457794cbf496835f5d5f714ea228670a7a4a15a76943b51c8dfe996db74c7262533d43df717c3a3181c94e6b5fe500e193878f9903a17a4ae1b582483f9b2f438d5a57595fecf10c6ec0ed9ee3777ebf693ccddd69f34f72926d89297ee8e31c50d88f2515163075604c95f272357617bfcb263144e4b8aed1450b9a43ee3a4fdcd616a2d1357f2bd5c9fd34dfdea86abd73ac7fad3e0c24a87cb5a1df90baee4eeecd290c5f5eb7510c1fb33368e795e150e24a152a63e0aae59178db73b6d3c6f927327768c8f12269dea1fe2246d15879190f7b9cb40f0394fb0cd2651dbca84e3bbd3ee30e3ac48b679e183df93c9a913476cbbf99acccded3614181cf9f605529d9c18ba193341d0f8120f4898bd97d54dc5e849f7a57095d617c38417d5182628eca0a3d63d44c1e5e1ed10b5dd6b54779cfcac19f3c9cdc805aeecdc9b18e37a2bd40a4dee7351872af39efbb482ee635f49eff8582096177ee2e382379a8df57d121a9abf178a0d4f3d37b80b39f799c94af1a3b127a19d8f1d2c2ea5c69a83af3587a1bd88ed099d7dbcf51fb229c8757a9292db990ae646c9c4032bcc9bb77203f9512e04c43ac8ae6427eba478a24ad1b0d647b61944cf92562bde97278c24afcb7af3efb897920a73420d9f8e1b29609edc4c2a09dcaa38b3de19ca09a4a35e6ce0c1f6b637e4c90f16ef03694f067aaf10427b33738e8da5b12f506a3a8aeb9336d33d0bdab1160c83ba36b28778de4db21d1d5ea796e624b7eab9881283c7017ea07e36900c30d2928c0d94fac999ed46b60a77bbcf43749f900a22204f5a16981a9ff5e10379f922d449c74b7aa41d4614ed2e7ab34e93e57c25f790f1e6e2325ff45a952559be60f4b36389db149379823fb6c5e72675041c2bcf336f61588bb5bfaa14a4c7b8b409ce61d61ad0"}, + {"000000000000000000000000000000000000000000000000000000000000086b", "0003c35591af1b17dbeff0d6e4e5ab65efdab06825099efdbd4414c5cb0429e596f0f4d553913abeb64805e1612e8fc86f53a17c22f2abf69939af6b313b2011947e39672be2e98037974683ca23f24eebf731371ac49eaa9714dcfb11f5539471aad3120f5274396d1c3911fcae63a71f8f4f42acb6d3a1a99bfc34efde2105054a0a2844d5792d72c49fb603260357572adb4ce79fe51b9a9a7fbe3ca7dd3ccf00be8d8cbac91603eb28522e24a0715a9e76368cfe5fa3151e3dca8f19e9576173928aabd25b72aa3771d0a98a5833941d0ed647ee0f6164d72b47d278efafadde22c296fc1547dd872f10db03f116619690b3df198dfd2c7d212b09ba87688a57a28d5d9a32cfd7cf498d60538d5e4b0f0119c800cb935fcac256e134d86535bd1e7a9f5e0d622c2fcd628e7b94609155fec477b1624fde2de21095ffadfcc504d7d3cbc2eeb1bd14bcc59c8af52600a79e405fc66a22ad7e981158f509025d8d33ed4c0d29f641991e6d639c3265defc352e6daece7c7c9c170f5232d8690615f7d26480c9481745abff5bba033de88e6b96d89f5f7a50d524b16d8871dcc3ff22980919c866e703dce4822e25014ec8a2a28ca0f7903b137cbfa2db56f440da02c5dbd1d955eee05f9a80c31ee2f3a691a848a77b03e23ca265c86e8526d51f002fda0ad50fdab1d97cd548a950dcc27321e4df6df50cef98ededddfb55f2dcf189b050cff09dbff1a09c11fdf6b91e9ab687ee16a8d31febe04e5f36d69e1f1df4549524cd73a8bb11f73acbdcb423dba81f92a432b89277cca371c33af8b6ee7344bd6b03345b9feb0e2d8fef4e674099ed15045cf1b4b309aa94df56a91cd7099fa3cbddfbffb7b42752f0a3f7a47e5f7ab1144d72f148736a97c88d3a2e2c6c4192c3dc5c9d4e2e17029a784ce88bf83d82ee3a630190e53e70e411004a9680f18260d70cc7705388c45a792b7bf7aecc116a858b10693bfd7584c219aac86bc1d208ef359113ffe1541c1e50558b6982bced6bc0571ef47e2c6e24d8f435af5ad6a0dd6ee2575f981e7589a78ed8b8013c283bbf6dca21e3a8f863e3e6933643a9db9b680b464b830a91d142dba1e50c203fbc761388f4a2fb0a618b673877940bd7f024b87fc38af60aa679aa7f1a983c5520a2375fd45e05e2e8b9553a9850393ed80951ff1812169ee74257e17285eacc257a63ae7be33bdd4d30b69b9ad3a6aa67793b511a3653415a25b414019e1a1a69ecbfc02bf1bf119487c28fe5376018143e64e835dd447960b691f26bcefd44e9907f16f6228607672dce6af6aa7ef391735e811d228f9f2eea33192c0334523d9b37ea1340447786ce4a28f7f7da387fce077ca8f213c6593612ee46487a64d53ff339533b54cb773488c3aa14a7b3bb76d95a607bb9455500d34f0a3537d93df00d623266a5ca18cf215854d41c094b3a159a82f7258846e0dc6baf52a56f98db2007b7122330617a5b98ac714c4f56d736670c7709d61ffc3509eab71955df7012574a7ddd561918b55cbb01951da1ee8e6317c9aa442270a64a82114fd77b73112bed9d5a270eef920762127b3945466d63582b9d0ab07529c2a3863d47856215d6caf880ad35d928f321098da6ec4ab057ad3fe52774e9e1fa589a3431f105a6a1ec955ae9bfe11283c7a75a197dd4793a8eea1930f47a5f151f435761b1f9737ea6ef1a25ff53ad23fac30bd60d45834bc7e6f521616a25c2a57298042a3d118e300b52dcd1e93b9d83e4508b236b3fe0971c59e5ea6c594519f2a4f3400bb7bf78eb59ea60ea6526e37d719bdcf35b1327ad08ed88def87e1b81cb20da4d17511c157130d7552c117383ae034739ef793481d50dd2d28fb98e0407604164cd51f953fd109a"}, + {"0000000000000000000000000000000000000000000000000000000000000285", "002323f5168d9298971261aa304528ae13fb712a5d1d8ec7b757d4a1f18754935e17b67145015b3f58300babe567800f771b4ab14327562fba2613d2733c3c0f88a773e3a995d5c7c3e1b30a691f5ed97d3b905213d2427638ddc7331079d6978e38670723d7b9cef028da51a0bd385c3bdc0852c0d435c280e788f035042c27dbe964f3133bee9c130385c21b54dcc8aacfab3c9585be6c971127b6c713d3502eeaf1566595c2a003b6829d61b8193feddc511aa0ebe355905a59ca3e23fad13cb2cf2e53bc8933b1553abc77275c5e171e17172ec8668ca3d78a2ac380ddebcd852b8330eb911de095b84890a4d322e862e467c3971109033969401ec9a5dea70f011720e9b74195645a7adc0b597631379bdb53d89c1b7b780539218a66aaf29cfa39ccf627a05ab217535dacf90f341eb7370f2a8b6a5b1212301617aa97f59bfdb0e225f006347fd767819ffb30002e45b5d08240fbc81ff7c25673931f5a2adca8bd02a21a84734fb1f96f325591d8e271fa9e9a17fee5084e8722d548a6c862a8c50ba665ff6d62f7df308a599c76c7d62bf94d607e26c9834b9baadc3b3995e30ceab0907a6baa75aed3318fbe95cb44ec93ad6799123f2fe08d963419867cd1f790470561b57517b67f19cacfe9aa4e3f8152f8f65610d605ab76ec9d343b28675991100cb6bdce42e6b880c0dd5e06d41b8d6e032a04cbd581103842dd51b1b4cdde2541fb75230508995567f0567a9be11ea437f05afc39aeb0b53767204481e88499f3e17d5a46f3acf4fb6711687b8dc14d805656a762752bac8746dcfdc7202a60eb35ee570922315c5c6ccd7f88dbc204ff4d7acf51857f9482254572cb8a4bf311c7fdd63b044b74ee626096295b09821327465d3d9def3bd64c60b4ecaa7e737bf89a1e0c5431ad77e4dbe7c493f300483d7247dc1bcdbe00aeceb17b088bf5b2e3674367e8e56a120e9e9a8710b9e3bfca9eb5ab62c0a7df19f676062c3d7cf52d00db5d3f3f96b4e78aaf205af3ad5190fe5cb063bf010ab2e8c75f93c75ccae5ee10e4c61a68e11bd29a04cf07eff71cb8c98b90821c137cb94ce844ff2fcf14f9b5f1aeb3b765e1ad3682345a4f3a269e7a01181c1cd3f7f1ae9dd9dd84e6471d4ddba690775ada6f4da9ce9defdeeb93fbf376887a523a0a13c8ff1f6301ca93eb6e9bff47896381a1a194665abc29573ce709f6b230acc959eeed7e259234d060aa1f3b3b6b7a07c40fcae7985b8137a583f1accd0481132457d34a082edde78724adbf9087727652e49f714fd6cda4fb0236c4b4dbd1c35ddd25f68f283ab4aa61e77b466422333e07a0ad5c23e83fe85120e9f496fbbdbe96ef0df9171878a40d89298a231662c6fd1610adde76910f9823c5c20cdf7b7baed1446c4b6519c21dd3d3d700d1c8e55403fd298fa10380cf39dce36235bd2d16261c56a87a55f3c14e89b51c99588f32f9325dd98f0238a41a4898e265927e642e37417bc5c4b7f9a93a0e93775cd91ee1396c3d64955ef47d7179c27ab5dc00ddf0f8771c3ea1b44c34139d607cc587d7f6acd20b60bba0df65dff1e7a7b47b4bd56b122145172b690b1196099d8f4a697819a2b07ca2a061b5a95bf74917ac1b50899c97f1800391e6c66d0000ebbcf7d77901b0c0bbe85ec32d8ed3e72a23c679aea53ff9bb0f1b7eb1c3ecdc3719583cf2163354c1dcde51ba7b941b11555f9ddb4475bd880299f7f7bb3e159579cfcf2233fce372db0b9b53af75ae3de3a79dcb861399fb0c438a52ae1cbee13337f4b93e29ca2e27ea9ee53538cc4dbc238f28cea13203a1f5c00cad164af05b7f0d0b86b226c4b1f2761211ffc36c3fb117f5134a043227ce45606050cf1d25242d7be288b1de16ffa7bd"}, + {"00000000000000000000000000000000000000000000000000000000000003ae", "00a6772f6e1018f0fefa448e6342bd05a60572a2692d6a736e4e1903d8d69b568e507dd1ee7340be47850b182fd40846cdf311b094226e39f199a2693ba5db3b31773e3f10a12b3a9cd4080bfc0981a4f8f49f7b00eeaa2b46d7540341a1753ac0f6b676f225daf5cc0a4ac2c05d9d137938dd235948415dd90069c8bb1619df0421de488a9cadfeb830ea59b4320f1e78651f1af70b5fdc74909bdb651410562b170678109dcb3100de0d857121a1d99086b23d399318c1229fd67ff9076713c847954a9b90417091af0f3aa2aaa8f5ce8231acc3ef621a3dc5c6ea39a963530a5294db392b345323d71778f78657f1b0c6e70ad5e355ee5e3567fa192f299be7d3a7fb7d42c9f394ed73b352cd7d31b53a74db519f1454e524db2cef6aea884f53707fc1aa236a378554a33937fae89680f964cebdcebbf6fb4847cd9bfb25e7633961d495a2176dec360c3a542f290115a7bb0dd9fc8ffe7e229e1c78b6975bb93cd9160380cdfc404bd5f4e529218463430c6624a935477b1db83f7ed5ea554d9d4ba57bb5eac66d76e378dd6d527376eff2ef6e4dc7d418f2617898d6eeb99ff1300ea65bc34d99bc2b94b9e7d89d6e162e0382f70f48367da2f0ac768e7fc5de7733a56ae26e28e6f488a22b7eada245efa373ffee153f424cc4de425ebe32644e4a145551f6a461d3810ab59e7332bf0a4b78bc6b01ca4865f0504e92eabdb0311f44b7e474ec3f74d205f0aed08a6ca401bf496119fee1a859f1b49ee2c80fe75ed2fa12b671d38a43c52aa457a9eb12d6d9a12365bef5f84afbdebe2f14b82dd9859af09e9ebab00260b5216493078fbdce2705e7bcc3e9c8547f56bc331782d2bebe991ffbb0446063a93f9b24a95c713c1520fa0457504049f7a40851a96a38a27b8916ee08326e0a22495733caeef8f5814bcd5092e7283e143803b41d446059acf167ce6359e54d84bd4b664caebe1f37aaa046e1fd33b3a95434197e0c62b255dd930116659c03328675ab0c11e1c8c39b633e7f80594efa313edee95be69375b89858c19eed99c6b3855a0eca10307365a2c527a0dbe412a12734e2398547ae99743db88f6869e0450798049455d950d60e2048b8d01a12085c55d59caa05c8019acf6377582369233d06a72725af377ea8ac97fd32a4edbd7167b55354ade67b03c0bb2105e682eb99dd128042dd1a0eeb697a7893402a9ba798e327efb7f1e7fe4542a8c66d063af2060c30336cacc97a97c5985464372ca20b21827d011b3ba094853f9df8e77214a695ea6383b5d3e85897c407180c2c7ac9e973ba8854ace74f34327fe93c580c16fbb612cca08565943882e841d07f1e5bb858d9be15f2b9e3bfd1fcbf17bcb39be0b522a67939b3cd9b43f31286f571643fc3b7949461bfd31304531d0bd003de20c53aee5c2fb859c20c25b50c950368dd47e32298b96c6d65bb8362cc63b4b0fc45a60a05d59a151c3fd5644214c38b3e21b327c5d55c27f5057fcf4642a45e2566b8b6dbd2fb458cc76474edb38c3785dd26d75d91af580d36d1385384c3dd8e4d0c0dd5177f3dc6248aad125a215ddb0465f942e9493cc13ec3f428f55e58acd41d5f24c982a93acca750e79d6cc2a435b06d16ff913972c7a164aae5f61ab218b99f1f7b0677ae9e680e26bf9b14819548e90189cf9c2fcc5907604fa038f9bc25f44b42415b6386c11253907e62118715e0a1300abdb02da5b4c8d239ee956ef5ebf43e80235d3d360031e40eb426a27c3d266444150cf510d0e89d5c45292c409bf3789e743e01cf6d6f7717412e2c8b012d2b5bc8b2548cd148fdf1658ddb2ee4339942f4c9361963b1c958a6565335daa13abe7ccf5e5edffbb9237b812598f76c0a43214ae1d0d83ab8"}, + {"0000000000000000000000000000000000000000000000000000000000000276", "0030d24773a58caf67e24275edca3e562745b9356828f28788279f49b7604053e9b6febf95b500114bcc0153915d091de9d771a934a652a806fda19c703ba0117ec0be4b0f1f53aa96f1f0b463bb6d4621ebd800015e9c933702d7243927c429a8a502caf6b83fff980f1ddb119455981b31d64959806c307b4a42fc7c6208f8be5a63d50a171cedb36908e4c6a4e449f6cf9d880b2cdf7725d0937259695c68679a86be133759bb02a022d71f553f718d72606558fdd621e2823738ab0e2a9349180ab0c4f6c0a1fcb3fa80f8dfa8bf2c5d18e1fa6f2d8c65c377871206fc74407e2fb91c281e1ad07acd4e8cc5f8b6d0a41a0bb700d51d30377bbb0aa7655a094b2caad99e56eb44b7b452cbcf7fc6f2230ae492eeb79b31c0aec2f9c59c5c575999fee7d90d58a15418b04b3794096115ae2989c91380fae12017e1bc4d470c30e99d7ab6722bc6323648b2bcc48a03514c804fb75899f2c7c8b82dcce277643ade6e2f1b50adbff2d81443a7c84c2d62e637ef124e99783011129a6fd1bfa647fe622160c5c466009c5997709b16689c1fb30a26e1cc2052b8d0f05fcdc7ba3e995707897149551b0048ece314d6d93c4fa22489795a753bd02ad0a6908245025d070df8ee1bb2129c5422181165f392ef0e7b4ad0f822b5fb3640a66d093c62d81d1d1a5e8b9ae441bcfeb607a049e82da0cefe7128090cf621224ca482f78bc9f5c0fd672a87eb1e8aa415cddcb46b0dd292e3b322cd0722539554f8b413522cd384f10e923a4ac19c044b64f452fea6a9367059337f1a7dad4da1e3cbf4baf94de34aef5b547c9c1509cbc8b0bc443dc1c91e03a1c6d25830f37caee517419a766cb81be83f8b6288aacd4c60739fdbfd781028d2bebb570b8611497fb5e4ce3aa739d4ea3fb848401127cf9d571187cf7f25531be166aee9335fa6a101f0e2ee8b3ee321fdb07314309bd080f308dd99de13924df2db8aeb0fcb86b40bd44d3aba1ba7521b780a549ff49305c4f53a60c4ce5a72d07b149ebe8256110d735744876a27e10511e397d360e17f2cda548c08028a3bbe8e32ef8bd8ec04b36db4c325377d8db219a7d957a91b1b694926619ecc9373de5e20783c3410fe2ad5575ab60124f4d299c5cb0d46b708f832bf2cc7c9e8a0ec11bb8df292e4a22a802b56d9bcdeac0de5f0c88b6abc3de54f177e72c4b983377f5e61b4175ebfc9dfdba2ed1f704235de7d25d89c9431769b1f44698c8f91a809be55986bf7c5f0b249fdda8f2029e49fcdb6d9a7f7ed1b1a42f25e1272e5259e539022fdd4477c0b6bdd2a8da4381f2aa6913a82d7e3ae8972d62f1fec11f1bcdb591d10cb4d5271d0fa2b3436e784e5a4b07597cc425553cdbc8b61a8891149973bf33c3fec35953bed1204236a2ae1a5fa91f4da9f042d27e547f36ffffa5af4f6307e4aaf1a403a0b2924fd35e45f314bb5badc559d0a4e85b61e3dba877b13d2bc03dbf061d1c6afeb15d5f036fef793fb8cbc158fdba6f647e693b49b75e3e3e61c66f81839d8d90f19134da5d5a38b6796f2053f7eceecf46a5109971b19cf569570f9e7c69754d69839761a6dbeda09a317a474582d8fff46a1b5b36b5aa5f856a12bd65f9244b4d5e341d229a2b1a3192c0de477a76a45ff6ee706186e7a28c9807d9eb83232c5e7f8c49e49f9899107fedff9fb4f1a1d962db0b945439532a77b39de270e0879ccc150ba7ac16d616af29d466d79811ebdeb396133882ba1efeb9e92673268ef9e021e6bfa3fcd09eace20da4729234397e0e39f94174734b8df95ca3c2a7478445b8091bbfdb51f92c7b8b99a4defd0760eb33f1279d59c0cde4462673afe9998b47a52734419035fcc8a4bead91212e2ca612b4c9dc31351316e"}, + {"000000000000000000000000000000000000000000000000000000000000031c", "001109e266da0ec6eaba92c04af5769a68cb783cab1e7baa98f667990f699b041b5b611fa9b89b1d5f8904e99c5cd7d127379ae583d96568a4ff32fb7a4d4f0d23c2fafb036dd75d00035c9636af55a113db452d072a5c74d6b2e73be25bf678925b194f244d1f3e7a1d0587aa8c6b0e9177e9224c25730f1cc32e910709205342b212d6026ddb3ad41a1c5e82154ec1fc5ad02d7fc420a35937afa95c8467c4fd971b1ee89f675c04385d09bf07da0988a31193884c89047eb1152705080a4fade16b917ddcfc6675a0f935cf33099ca51209fd116844340b4bd52ac346bdbe7b5aed4b3c99db437d424bc3bbf039f2d5558ee95ed0172194de2a51056b89c90433a6ebe869917a8534b981364333956f1a9823c64bba44c1de60d408e4680599cbe1fb7bb734a77ab47d12adfb1e9424c01fc7b17758cedc6fcf694526fd53b26267c72b46a5eeb9525dcb4b5eace2031e0849355b1db5e553643017e7a5b5ff8e3784b72e7cf7c76f974f53c39a180b59ddb3c24fe37d1c0d13da52344baf6031a4dbc4dfd0750706e8e0d9a8ff1438aed3bed193c172b156c2595cc4addae1fe3f03042ff42d95c4dd4ba95e63b20abb3d320d143e2d6104662ce64917e7436af500cd38081fde3f7832d3880f215ab570ce1d3e899452be99f8ab96b0b19898b8232a557ba5df13bb00c9f34c00f6f7f6588277023805e61d83eb4be450a366825e7e4ce5f15730b89d571c497e14875643110632365581d58c3b50825b66b2229d6c881024ef97ac9b3234ca47b0e4f71e379b122c894166c010573db9888345e65cfdd12e073db85c0bd3ffb2b267d6fbddfbe1258c2e7561ef40b3922624a07c4391f4472dc7e6a934c9f32686502e7a5d161917e24c35497e47e7dee45f24761525dd3435d6f42cdd674027e8c9fbbb4183622e47671f791b9e0eb0009be12526d26baedb5984a4302a919d5c5671c50a3e769733e86c744fb74af5643c592e92d1ff18695d176ce3a8419aa4cb84f6b896a5c4f0fa5b769a7481293a8a74fc20f6499b1282b0a698f6a514affdfbc2019b64451bb337a5bfaf41d7233f3244eb6f8b0a4c1418cff1d6d9749b5422a361e2ff7d2e94999cff9d128615b11b864c584ae685811747ebfb3c059ead513cd5db45b925d3597fff27ba5050094b1c41de3cb1103a6122f3d2ccaf96f1a196861605fe1be918c51d206a2cec2b70c2ede18cc9d96a6692ea8643da72aa1501d36c2ee795cfb9b5918dc0dc4d7206c63271111e70f7ef520cdc7d99d435fc42d0dfadbf419f89e1162a48daee093883f7be7494e9b1dbdd2d403ff21e1a8c6bb71bd5ea836d2309b0ec4f4be6efdb1fbfdb16954234d3077dee73d761de62e473391ae63925ab1ea0da380850fd8ddd16c6590b4d284aced277869e00d8aec284c87f5972df05bf7ce3c75e982d3ed66121cf471da2dc41cf19e782a0ddb7bc49943afe0ca404c82b8aada3fa03c13fa67f73d12e764c5af7e00e134af5aecd6e991bd590e42b457dabb66d41790a6d02c8cdff1616c1e3b0157785807d4431ebae31c1574010540c2ba89cf5f4532983a45f19e28b7bf9b0640a2f4efaf38878491d0a628a6dd2acf4d9fd3eece3504592f05b229061b206b52aeaae62999fe196d176092af49eb8900d7b6cbdd2ebe8256e9d5ad9b04b851a6ad5b8a20d23f38201a499462d1cf94081ed0b7815a48f97b8e69dcbbd15465363653f8b279ebf3813179464806f98e7f7e1dca2efb25f329df57272771f10c956ac0e26dd958cc464ba38afbe2badf17f559a43cfac0d1257a26109812a6d77675d72c3709e749851189e3ee1956c9f8002262610f4ee764c0dd9e05255d9a745305b94bfe3d8190b8165df6abc7dbe96fa"}, + {"0000000000000000000000000000000000000000000000000000000000000329", "022e7f086a9b2dd6dbbf71736d8ff5c13af31713f42ae7664efb29ca8588c8c43b1d6f5ca1a073f181270a60d5e262973a07a58ea22830b5081fb9557e02d90b2af6027defc90b9f3b91f961a50f4a5d131b75e41045cffff8cdb6bb1ee8d71ce256687247661615e31e755cbac719e169e426c356cfed8b4db1c156d8bf220f1254fa8d00859a60a6ac07413f824a9dba945646cc8f476caa99c3e08064e5726c071e62d7fda8e802b227c58f4962dd1c0044211c2868deadfb38ad6922798a4576dfd6a1507b32e4b9e6a29d5fe3700fb30f1d658f685e55bf2452f87bcf47ee2ec0b1d72aad1da96cd23612eb15a962541e22c73a8d376f76ac4c0e44f9d8d44dfd31347aa51abbd733732ac35a57e10e7cddace8e74f93f4a5b1a701f3faae59cf548fdf2b652eb84a8faad3336a58080b7c9feef01a1fccee39e22214f990d6cae27a08eaeaf4374664acb4e1da0256f53a3193852b0e88763ac5719f722787d5ab0d03b1d2869696e5215ce621b04daf04e8769d7ad48704ee6931d1d595b6c835d070e3eb540937253bdf40235b417092afce9f946155b494d866d3adb6bd93ed05422b8d75e241d18c7ba401e47985fb875dbcdd301dfc91d5c4d756f5498222bd64b077022e9e396fe90dd865cf71aadd715ab5f6f918ffc48348a2fd760a1c351d285cce461d8e77e325c156c67217b8db318a05b640a5abdd1a379e7802379a2d3cc8c7787be2d0130272c5075b2e07759191b65b55b865c715ae88c709d57b04b1dee9cfd8bff10de85f01a24776f3ed8528c0978e85a5c9fd8e6ca5182be77411b4f45c5c30081937879ca0190904e562f6cc293f5dfb2ef03a060ce3e70b7f1917d6f217e423c4e41a0ab44a7c425c1fa9bb3dfab1e60bd06e2268e97e01f28983bfee772fa17612099aba71985d145f05c4ab2a3553bd674f027d6fc2189dac7b838041043a884388f275311877250d352767987d0bb119653e9c402e1d9f1d3f7d190e0642bbc69149cb93ea72a546158f7e22cb78748622cf9bb50a2539c15fa554a8bbec76f64110351a680a60f3d15e0c5948ee311482c1c0b161713a7322eb21648f81334c412f66e1476e613f1cf9f82d1cb75e3e2afd225b6e0537bf36c7c6e7dbbf9dfbc3d28f2842f826b2fea8d3d39815c457f2acd0053a890d3ba505199febdc0de53db63e311a647a5e344a96d8f14305bf4ce56d95fbb0d5b241d5e26c3b020aa2d433e7191ee1e16ac74ab738e995d6913b70a30cd599c2941f78f1f44023fa6f7c9222ff0b21d72a93911bfe4a09e79d1f8d042623d24d91bac6ad21a103179f8c9e316fa5226126643d7ea8535ad94d52e9b6acff707b0b76e13a1148e3234befd1b2d79761add0b79bcc61315555de5b1dea3777e6a3e95a2fdc4a2588542c4a06d0bf87d3177b099565e646ed3315f1a64b988b951352f53a404626c169b2e205ccd084c1b588fecff54350d73b7b2a44af9dc7d6e3ef6f8d36dcbefc9f67481b1351dfa2dbbdf3d11527babef4720dc1379f1709834adc5b876357f70d20f1e3e9e38274da3bf70417f0e4fad6df19513511c37f0fc6a3d1388909fe9b1ac3243f865964bda93325d3f7d8d3f9e5ffb4d6c92406df5212a12c67f1d3743f0be7e4d18b3a33e4d1071ed531286b05c1598f853a6d6ab5c6330cf51ea416c808c0619b26b1b26571e7fc76cfce0db53754550bce8a81bd88b65cc54d937726a6c095a51f2f20503c51c30a05643bcdfa812491ec35d52b6fa23c1b261b449e8a358b73230629b1e3105855808eecf3f10e38ecce180350607f28d34515544ced5269167e31651bbacc9f8bee2e81c4ffc201b6fe11b5aaecf4f4782e17d484e0bad7c1e8cdc9976af7a26f9416fe054f"}, + {"00000000000000000000000000000000000000000000000000000000000006a2", "0013b4e457c83e54503011ee8e27088c8b70ffbb5b1583aa037970c7658833f3b8862f969600bf9e83751119938d731229ed9ff243e5dcf4f5b9b68e90a1b31a4d8187b6954189e204b2b3e45e511e269b32e0c406bababdfcb42657f4fd94e230339dbbb6421ffb430c5ebc6b1dcf6d58d7405383b53f090622751d350723f8cb38ed90ac01e84d227a25aca6311b1b5d755f2ca16202751c73251187350793628a69648911d3910803eaaa5a85b43b45d2972e8f63710b989d1d77550ba1d71f3e8559497e98b824ef47cbef3910bd0f202221ba2345e9cca5826ef6f5ac5e4466eaa41f451c36a9d3597bd522eaedf36660f6df1106a64938be5d0d470e87325b3df3ca674125c2a5c2fcbc7554c8cc2e3713fee71c388fef0c13e48e211da9cff21d830616f56b909ccfc990f64291c55ea321d6d932b8c51020a0b5b3da68d0edb036249dad5ae6c145676d4a4b01131580d7841d93883f58eecfdd9ac70e64be6d490bdf15fbe45b07294c2c84dc8ffac7356e54fce4670515513f3b781617c3bb61675df2e7f19a27cfb2450ae00cab77260743e84299b315e5fb37b35f7fa420086d4e102ad9cb50f2b9579708ee331e08edd7cfa30e063617dc0c1b2cfef974e227c3860f200b7c7d98119a279cbb4cbbcf5830f66002ee26da0e2e97a4dd1ac00561afe7c62d4d1a471bacefdd531b49fac2ab05293ba78be5dc1991441071880b962575ab1967ef28cbf7620be79e19f6d86550006042d27a14fc84871ea0df741f2761e542574418a4f53e2e4045998bd91ee8959bb226d7b9f95c4a37487c2efaa23f9dd4a6166f4fc2b0b3c499a4a273ff5e27aa9906ebffff14538144ea9aa5967f568449668275e95fadd6be1bca194e11d332a3fc173296136f77eade51271d1cdc8b2bd4ec5cdd1a79cb17ac35988f50dc11b190760ee100eb9447542643c76efc6012d386ee3943e1d57055138cd9296345f707497175b57ab4ceeb2c035ef4c402e34bbaeb07bf3d5ba6855ba575e1b21cad17b71d052251c4ee1d25bf53b4f2f26f3629a32e415f6deb111005104a045b678e44e45c3d6e2031d63739ad582a79abbec2d8b727158c34212265f3020fe27e1c331aacc512a3b2cac1f0a8c57b2ae889b1ef0ebf5dfd1f41975d80e15ddf80f1f21199619e259e163621b901a0b16b6aa60ecf324b148e5575d3013bb38c6dbc021d2e88fca175133fe6703a5474bc818a61f7003d10661947f7ba621fe9bd6584d6e7f7e9d29d9892aa12804b1c4ea72585ac50b3b4ba399f9f04e3f8b55a01cd1d471260e917565f85088562da09baeeb973bf05a4bdc83aa1bfbde04e444a052ada92de627d14070a1b018cbae32843b41241da16fcdeb1f4caf7da971bf0b290ba653981f06d02b29b7f97f597a176992f013af8dd53c34ac239e9d765d941c5ab37db9d122c0c6949654448df88de493223012f08f54b591afd2a0a2ba0f8946955d5e30bf4dcb73ab5aba51c3ecd7011dbd1936c0e59e35279e37859fdbaa634fbdf106e0350bdf2f0f29ce9e3948366474ad141a567757457199d6ac11007d84eaadfd2719a986e75336aecf5182e758cc17f610fc597c6f4cc4cc46d9719105d53a445f79a8823d475b95ec4b7d4fe6e8132beccfd70bd0169abf1c3990be1f164c3e26b2553fabf957d709f2121b407f916aae0d9412372d6a72dd21c26da73f807dad14db7e21df3418c824947baea25b28fbd96ee20b135dc96e8dc6fef72044e6d65c4aa130b5921800d221d724d4468d350b476aa5ff92555be13d7302132605ae9ce8e7b5d0d86c79dc8418bbe9ceb37084011bbe76ee6c9cf4ad72b24b42cad9a31c6f7cecb5e26db141da68e020080ef75aa98cd62c2847c3aad1c"}, + {"00000000000000000000000000000000000000000000000000000000000023bc", "004110e7bb9afc4d10362018f22772502bae42d94223146ecc1015ee79918a25ae2bca73b2913556e98449eb8ac8845adf17ad03379fe1759a7a48a5fecadd529807471eab572594ec78cdc765185a6b7a1e0b6602a6bd3f936bf701cf1e427b499ed1e8d369ca0ad03ba17c8aa21732a35a0b643753c2c21b74a4dfaa24137f2435ed09adc8519d46a2cc7ce9c61f8e38ff041ab6646bf80cd380ab66e26c284200957ec99823bd005f4558ff5ef62b723c14103eb1b352cdd39ce38914c1dd79180d902f76cd62195b7d385181f0aefa780b8400da234da771fc5fd1c1634651888ad2c6e4d022eb1a3145387aa1e1bc73fd6c35d7fdfeb19a294209b74ea02ce21a8b462c93d1f65a56fd2ad7dca8540b62b175aa5e7a2ffdedb1a63aac666909f07d6d650a725765050aea56ceeac0d9a93963018bdeb24d951241b3fc0b4f392ababfc68a0f670c09bbf1ee5902007047897e8f5edd3913743592f93a6a0050d33c5834acc4af4b9ef4d7558b03a20fe4bdb15a59d73e21104cbfee0315ad61b5f6b58df546da35b949132f8936daef74ae8fec1b321a3630d3fcf1f1bd84f868c6117e9f045f4f560b0a7bd8c6e4638832f5cc7e0c872c956cef5c154487bb4fe3462c583bb9c113efd19e15f75d912689f182bf66b6ada33d1a864f099f92582f77cb6f81ee51fda17084b1d7f92f3aab423b34ec021c6fa2a6dc4fed599735ea19f2d2ddd6b1be25860bd398c1121a86e2fe1d1223d27b7e84f5ff28d4760470c96a3c064446f9a8696fa96ff18f5e6bdb909d40f26d38cda827f145d5a603037716d69ab9fff97f15c0f374dcc60554bd3ae27d8a1837a93968d00f481e4d21aa450d99c141df035f757299baaafdd9d71d1e89da1b121a0c7bae75d72c50ce819dde48b211b020cac4f1f63a3657d96c430339a9f6160855fc97aa0197c0db7ca81d11e5fd20f070a5151cccf05e34bf0bb6fb758683a10d0d6184a106df48ad9a8faf836907c1acb767890a6f5c75060a45fdedaad4da36d58611ca2d74fdb34173b70d85896ac7b24edc48d93c1505af4c9b8ca08fd99329234ae83027219dff56ba2b19d2045fefdbe269e59e76a4a2c2698682e9b97c7e19378f23c0124c97e641345a214e6ec53efadf72b8506053bfbd74dfc9c25396d1134f0afdb4935cf8b904dfeca598db8d8ce4be142d784a52de6e9d7d041e0f013c1c5129574bb6f641530a5a0856465cb2a8a00fcb036b4a6dcb4bded0a2c0ce671bd9bd5e3645552881863fa3d6a9e2db4502f16a3a0e691f26beb3771335312572162305c7be14e4245279cdf1628fe31f31da4e73026ad589f43493fdf15e1dd562c353e36d13ecde9aede3876b7f51d18e5943d92ae634b84b8b23a3269bf24d754f782ec417dfc86f6a2ba736113504b3fe4dfde7f3d7d9cf24a94144ce667033f92c6b28ad8560944ef92d12af84cedeba7936aed57707e316e05385d8a2cde3614c31ac6ad885962b2ff5154d1dccd5d4a0efd6ebb1e7b5288bc9276ac71ab6dd461b8c1126789090de89eb930ae3320d5992f4cf10e11cb65c1757d6b7cf6727830df6dc64ea709b76fb571e303cb5ea1844d1442114f2c2e49111ab0a307b0a2084dd0dc690e951412dd2c5b33938397ac2ec6c95065483d33cc5095f5276b3b9facf25d0fe6694ced60fb5097c6eafeae3db9981c87434946b11957a54b01cde156789abe5f5d593b22f854ec51d955ab7b9d64403ea464257aa8d607f4597dbe9497ee2e29dc2a20ddf188619f2bc57f6eb1328ebf101137d52bece05614537c879e4b615e1c029cbbad1df5f04a1bde8952637c578a0551a894c349398585dd3c2a638f6638a2ba59d25471598a9cc6e445fb26041534ce1ffb346"}, + {"0000000000000000000000000000000000000000000000000000000000000132", "01119fda2950ed71ca98d36a1c59b252ddd2d75032053a2e35ac4b389df205b7b660bf7fe77dd8dfe9a801c12246c6e62ce9fe6313b3597bb6053f091d3fe1025af2d1858e1b947819d2dcea7783ed289f0b09400481508a804b8468eada531440995fd3bb74bebd03105b354b4a95f424f761a228b1749d149c7b6c1f321b533a5e9fd98f49425cba2ae6f05a9f8ab47e7d9627b0256273ee3b318e4193299e5971ed0d60a97242033e97df2755982ec6f521ff9ca725f1d6f3799f1e2269d78564cbe52abc1b77d6f3d530aac88eff8bad0f6ab2ef3827bc37a66b356f0e57e8c7de2adef1fb2288416209981ccb4a1dd3216ebe3ad17ec05187460fe8a77874d4d0b8e050290c65584826e335f8d6cb290415299253ed17325892e63bdd6c4f053abdda1f1dac5cde54c90c0b6b2264a3e4558df5ca31ff7dc223fbf72d9321bcb7a27d474110dda761d2eef382f2018c3e2bd3c84857d66a11627331ef61eafefe5d4e0df764fdbf04ce07e1ed94d545b8cdb6aaec1d19b50344c44df99a7cf71ae3d220ceae6a98a460d2607c0b46b86e0173720bf99fb728f352c68251bb74932c1a77e5096cc92cc3b5b75757eefed99e7c7979bb7626f4d2e9f3cb716cae3648ad16617c16a4e8d8c4f81ba15900c860f41b21c1a78b0c6c7a4e4041d2eedd3b597526f0131c79d1be843d18fca237c21ddef3f70525202dd514bc0370561613397a0b5f6a45bc24d315b974265f10147fc001bace2e60486abdb876040a0bb02f1787905ac0c3fdf153449ce4490e85f4471d0e62f0a8a663eaa3d456a2df1b4a6f80cc8c76e74a0e3515ba9b0f8a70ac771450ac60e1ad59f8cbaac80eaca50166a4811f6174b57a765b646e409c35c8be100e4e06d8eee4d3cd60f2cc292cad0321e1dcbfdf2a9a97060768a1b5a130e3ef60520bbb72fffbba4601601502f6094d39d907a6b1e76ecdae98435b11870d257704c6a7403bfeea617ef6e2c297221bbe013f06ab9b31f382bf80497726ca0dec94f245edd4ecc509946f381044728719cb90ef087083196df1fa7daf02a977fa5b74ac37f4eb8053a482ae786084043d5612e8e2e1538a407b8d3ee47b962aad496effef27be0f047c7ec9c8019148a8b2384ab11661f5aafcdd982c4c99b9265a20052c45b4476350314b18ee7b5c20017acd44f750391dfc4775da16c4401184dd5175c126607bc8b1e59505ea1e835acc4328da1805195eb1041a5ba7531f4d457633332824b672b26589fa0fbc390787d659d2c3033c771501e9bee0330d96dfa88b05cc67627cc24077d5eda333caa7e8aa9658b588f50e8494fc0b5a3ad76e1fc4b893c7f811ce2dd264f20fd44cf0d90ea085c150e7aceee4ddb26c7a1874bc1da4561ac658626cd0b688f47fdf359eb54698eaa6040a335d294f6123cb70016fe390b1c0747b8552c763d1cf7cfb22a90547571cdee6f0495360f35bae0a1155147cfc610f216e43740a442525819884ce58c0122637ac50a5e46ff0a8dbb8e7ffa793320b9b72ca08dc52cf10877d2fc601509fbbc00c460a157ee93e0ca5c5d93515752f11d922c2e1e2f9497d83eef10010ed2b4a6a3b30b3ead30aecaee3e166c94d9e876d2671299e6e0c1a395563b52f485339160818f3245c06dafd54e8520f03613d966e13f57b5e56d593d17b4c315ddf242190c583b855f986c7fbd6033c3ce4ae1bcb7dc22bce617ac3a474651ab6b2bd1ff0d820242ec563cdd65d312b69c2f31966a3de79727dd870310cfc2a6f2e8d708cfde003c17732fb015b0e2ca78b1c9ead803e8f0a51a29b61d30d40417a811218846a2e32bf0c4a5e1e5fff4a962018e4f411d3599bfdf149bd4aa5da284f3d62e7c500c73bd1f5faf29bae30"}, + {"00000000000000000000000000000000000000000000000000000000000009c6", "0002c06fe29bbf7923cda38f12fa16fd84a0b08dd10132b1ad69a0402f54657106cdb92b6cae796b9f4200fc12fed958e15f95b3c0e05faf2d85d3e31edeae0b104e437f4fe0e8c56ab477f8ba9e2a5d77b4c40406b8f3ac1a4a9108f29fc63ffbf04455ab509937220f7738edc3dc17efe9a6d3ee35b9c8b6c856fc07e20c3ef48a30217309ed5ca38c7fe435098bb3d1af8912dc96e70ed89e9b30b6515db89579247db9e617e30c6e9a2d1908bf82d234b539e8cd6f2ec3927988cd31d74da009b183bfde4dd378583d7bb58721d1aae013a6a474cf5c9f29b49461fa9b9cb5e19ab7f9dbf14786fc16b2e3430d707a764202ed35a9bd8395200f23aec5de3a52bf79ab884307d7dcf79bb0929ef690302eaf7f23f12f1dae033604da35b5f6c6c4b8a1d63c49ff99a3e48fcdc9b4050c095277f5ec14b2ce6e414b64cf66d45368c51b5428f6b1ff12aac8bdfbe300eb33e5f2185ca124a45266b5b6cea96c7c4cfff90d3ddbdaf28c819dc6cda452372c709764a11f85bd04c2d2db4f46100d2097d181b1285b7ca4a45049a34b8e270245318733c34985b107432989e43efa78ed06aa1b230bcb0b5ace8f523df06aaffe89da35c37d08d393f1968747abe3e336246546518634073c7c8723e8fdc27ca7b647ebbf0296ffdfb620b152932f8b318fb3a3ddf076f7b0dd46342d7393e59f557828f10e96b9a9b60d4f5dff2a430cf4b0f323d13c9f12ba24a17fb8a72bba67bb9d73412db177eeaeab3910c90fedeeb90f16b83111a57665beb659ae4c4e5e472c430ccaa3de1a51fbe9c595c2c954c705b1d31422ad1d59248160e5b325947181dd4dc8ec0d25ff1dab2a22267b244809399cacaac345b2cf6a136cf9be2aa823bfa202eb4b633cc898144e1bbbed6b2235dc3575524a5cd978600b4b8857c673df35e86a19291dea6900199a824f8cbe13afffa3466c23d0a63b22bae8a906396332e84ffe952d00f4d567f3fb91b280549a7e0e1f3ba97866097544eb01bcb99f5e896aa4fe37d02f7da6330c8f6509c2472360163918c59689d7eac304e4054cb2f646d5bbe9f1571b45ed017d6a4d1236090a8f62ec18776af001d3f21f5ab7f97b6f2cdd4c23d68e516edd7e8dca2187cd6148163eb021df9f9c42236fdaedfb0c19f4ded9fb6c612c6b69f43d0464019139b46a2466419cb7b571ebf2fb2ec4145816e44297b38d6a9b036fe751558cf3419f258e2b8de0861ca246a03ec84d07eb4712c925a68290edd7f62b97502fb693f1d95587003519c84ff2410e97a176e08403b32351561cf16b403c460a70e3a9a29d26d5259942cc9410eda13e1fbfef85bd37f413ba06ec9b49040661cbc68cdfe2c19900766509c97f41c7741898700a6e68553798c8a980c4c26c097117793d1c3c663800e47f837c172cf95265b0e5d69bed72570d552b985b1e4668ab1d2ddd6414f82a4c526a9f2feb99c2ab02e10d2385565c29139654c4a5aa0e81e8dbf53f6c081a7c732c2c81fd751561ad3d5abcc0c6d0cf1d0d01b3f16fff5d12b748f061f7cee8c213b7deff868b302743cb4acfc2e7ef0617bb105458fab20fb833600d8eea28d0a737d348da6195552a2b19d4a4113fa939f015449bd13049a55095c13b4d865f76e3bd86ce0889df9815124089b96c63ddea452599aea8ee872909b696fc84e0d365b529d5406c6e2475c6929dd61a33fcea0dbfce878de63106f48679850a75c0b4147a55e075e54557f6fd78bd46361dff208a6bc396ec7209923a6e9be49d2fc5e0e20465282cea328dd1c4a50f14b9e5454bfbeb0de595092ef4ef95867459328214c51a566fcc4580c532b33de72c9206b2039bce69216d7c8651d8e64f65dc443b59b3e5c932cb5c0bce"}, + {"00000000000000000000000000000000000000000000000000000000000001dd", "002a7c283f0819ae52acf1326c185b70968be97b910213dae37f0810ed8ff00929e5680b4a7fa1fbd1b40a853d7acf9821b958ef87d8c653c8bb09c29c021d2b4f050e914cee96dc954d400e6ac6776d531f2b1f17d32341b88d029366c623f68cbd6bf1abaf92ac831b0409063655d467769e84042236faf6a393d68d0324f81f3d87cc79e67ccb5332e29f4cde455d156a2025e6f20e04f002cf84db665be340b0335c00ddba5f04a2fb69189ac66926b2709758e407cf41a5bacafa04c9963b6e42c57d12b272628cbee998e2a68edd231fb04cda4c958937716642bbb3d23b6d93216def8d4070acf2d31c20811b7e85fa783f5cfdbcd2db2c8c08f0d52296c97a12562e33f9bc785bb19cd9134e4650601e79de1f98cdab73e524e92a951d52f11fb9a51209dc381ce4a7997262737edbbd36c2c73cf6573920bbec99519d8ae9f053a2f0e6ecbf291f082ede2c02499431adc620750bfee03bd6b68a783a3476232d1330c8de18ea35b9cdc727c5a150348b1feff92d600b90a260ffe27803fb66a19b95729312ae8b3d6ac11fd70c66c5173ea555c46371d92c55d1c60c0f8a2904b45a50a846a1f24041154b0fbfc89ea4f83c265508ea574a16424479dc3df2c51f7f587df69c3ab65f12308b1ad5c8ce19f9d2248e93398e7d2e0c5b248313bf1f9b2cd0e085830644d92c7c241d3c8d0db1a30b5bbaf611dc70dbab3c8327dccc602955ccfdb04f0c47cec8ddab0df9f21a5437b65c3b7524a919b94d0b99630e3d55693eafd001d1149b84a91a67cb70e20c1135bc3fd76330e88c98de05481e22aae6596bc40c1b17b0bc98180deb79916a1cf3d48314fc9f395717e3ed97fa91664f2b6173f3c5470b1138b95b666f1e97fee0f9d820c2f0d72375eec25378f94517e8a729f2a30987f6e7edd69f233f13613396115e1604e3010bd72e8f011e5efa8a83893579cb662bb2ddfa4b061dc16b65b0bd01fb3a628f22dfeb970a629da7fa1988d5fb2f1e2e67aeb6a2a012d0f8892e7d9254aa303637cd4e74d9cbb4f0274d5dbba39aa65b1b65e00563aa16599842d71baa823e86c49a34f62f1151a52562c66d8199de2959a6e2fe219eaa550a0df310c912d30eb25acfe5fcc8e405e9c7b0dfb61f0393e5f92c346b78e6dfb0b3e77752e562b216ed2ebc341ebb05896774501d23f100eb31b8764a3b75df5d36e20871855467de5dfe09831758d81c6b90123831774b290c1b1cb57e88326defc91257cdd8d6bef79c9889941526eb7f12c7fdf30a7c559eb76073a1ee5dfbb21f10e7339256469fa651fea48db7ec8db1a5323f6740136b1ab4aa645d5d55b6e172c1ba994963131bedbb2171c95acf2048b5893d323e3fea4cc93d3333181a57dd32d4846b5c6765ec66dc404d29fe7644d90cc0028120190ed8255b91c09281962887e350f05db61e151a1aaddfd2e583a0b466607943eb45a27ad1cc56057fcc147670c3f7cf976078bbe63e083151cda990463b64855bdb57df4f18178bcde68c8ea17fdc8cb0071591037768d4aba0285147c730cb81d0f3fc2690513f92e9ab2cf9ef772b7a5d337482f2a6e23f38151165cfb84fab4d43ba8411679ad3558d2b155ec9991256b7a7de60f7511bae87f74c7da003262fdc6c2b07b9887d6e4c1dfabfb52cdf736da41b53c69f6a54447b27e05511f84b8f98b45b3ae330114956396cc123355a5e774f9e06d86653dcbdd7c0a5003b1e2c8e391f9415d5692ed58b457413acceb9fab33d77c90c0c44d9ef60619e693b43b72ba73df2ce39d1750ead1a4c92d88687ed5ddfd735a9b061b96f4d321ace5c1698f5b7078fc01f61ae934129c57da2863a7850616138b74e02db343166bfb8505048d97efc2d9ed520"}, + {"0000000000000000000000000000000000000000000000000000000000000634", "0193aef5ece2b7abaddc406076ff97dd4145f66f5901965a3ef8601209855f0239f9132e5132f535810003b3428af716f68fde4c31860264be34d381f726481eaf1e9321ea83539de4c2c9a7507e857c7e582ea622e2ad51edd08a51f582962d14b961ddeb99f7bfd72a38f5aea839e77ffa7f32b89e474dbb2cb219d77d28c5a5688de75addfd927314dec35da6fea6d8fa1b357649c141a471633a606592994371aaa317be964809fcd8b2eadd2a4bff602541d950ca6de236d18a0c1456c0a58311746d14ab61b1b52d69babf0b1d28020ea2245434e96f7fde784223681d7b5ce39bea5c6650e0fa8ca16ccff369e7d7c5b86323a23a78d4589d0deeb68e64a9bc39621fc1bd0239b54eeb4418271c5670b4ad8ee385978d9af623f7c4410b9332fd490e0f98475b4b8d3588ab8c43b4957b38e682ddb947c81ddfd27bda4f23ee8f10133488a5ca2e15b591fb080351e35e1babc4756e5b915229b721d8939448fcd4682e378be39d63abc6bc369477563281f3633cc494157924683fa6e2d759d9258bbb7d96a9aa736eb6462d74e3e8010e268915c3638fc5c4237cf45cbd8c48081fd8e7087139d3bbfb91ab052adc248957546f0b0fea0d6ffbcecdd969c7a44781252992a5e3f8e4fa0ec29db341c3c09b5d8414a4f8bce545c45a5fe28950686cf7616120adb42f29db2e750a2681aed564de0721a256454be190cc70b14f807ea5c49caeb5788808943f52b41c9885c567d31307e66452f35058af5d0aa78abf2f981c4ac44a733815a36a41044f9196160b93affc7604a6edd48a87b3d3d8bddee8dd7ce7990746efa4d153e383c03e5176ed3db530dd8b7c9e8e20282ac7b68d7f20926747c91a566c9b7afb3bdb2c0aa02590ac844fe5b73a65b7d9d33ffe5015dd7f880dcd6dc86caf6151dc0c132bf3b1ed960adad1c774024d624a30473f7ac2d699115e6b6352f1cdd8931b1a43574d2fe11a55d68a344bfadbe6894712bcfe380377b952d8743c7dce0e355986b68a5e32e237d9043fb2d7e6e9951ac8b333d50d26743a258736ee49ac076ae0f662550b43f046d95d3a5c866b79029ffd2a37f4cc33ead464b749d845bedc7ceefe6ecbf4731e26044a92235a597fe492d69b6ded2daebd4d1b570d27c8b13f5eeb759d86f662977c5c79f9336acf6b0f05b820e62ea3af7b24a4a1f89eb3e4592d8d168c4228a5d738bd146205b911a304f2577cae22a1d1512811cbb721e6a48a2bea4045ecb73b252e677ff782034981ac9dfa34f0a7dd1a380d1765e3f219989eb486072ebcc31783d16ea7fbd0b042bbfee5979a0e33f64005fd27f2e358d91b77f41fb44fbbb376fa1f8cf92df5326ed49af75ae5c405c82844cc3df7097e8fc233fda4edf5e9184db90024afa2e197c1a51b9fbcf5066759732a690e0fc1ff4774976886c63e87fc89751708acc4d692e9d0dc93a5af4643b311dbdfd4f70e0859bff5cff583e5ef8ca5a228658c39761b9342c98d604f2d042a233fba18ab6df7f67652f4dcf80b800d2270702b0c7fa79b70b0ed08ac91b936f812f70b2861bd03825e6571635f678333e40f0a0bf8d3068b35e535637df81f75ed9b182e5afc93aa67cd39ea263ea6ce0cb61612e79806c6450ae94aa62b13d977050a9a55f00794deafed5ef32cdbe849ff224ddc09b920d71e26bf5ee6dfecfa33a693a3f6ea13af55f7890b89bc2eb66bbe09a9c503c48e4b97f73216dbd7af126dfbc6ed228a9193fcf48bedbed0f5dcbdfd6899149ae17910cd678165c5764459c2d0ee2ae4584a2a244c56c1e6a9a6bf9fc8b5849ad91525e7d5d21682215bc1e999d59915cb14047c2ed03c3d5f2b1ec59c2f672ebd3cb020afdbf215f6ba3935b245e7f820bf"}, + {"0000000000000000000000000000000000000000000000000000000000001fe4", "0034d0d7ea85cb9ebadb02f966bd4f9979c65c359d1d065768e9dedb4fe25dca595adb5f2f2862fc9b6b2175ca9045140ce0f38ba3459e7b0255a5cefe3440280f23c18c8be782e5eb0635bf50c5b2d2ee1e45a11eaa3f4dea8f3f18a03de87d57e90a2e20b4546e93496a83814ffc2023ebcfb95c0b5853aac46d5f897f2036c98666cf3c04b75b59ef2f5cdaca8066f6d22f35f43334142e0acf86c9e45ab1a9df11c56d128dbb00c7c049a55cacdfd99af3c851cb0fba0295177d6b0a49560fc682968aeb40c0f3eca3ddd537e0fdd44701e46b1e174ce175dce4b6df83c02f134d7e9f6fa004f4ff5731217d3def62822b605413b29821fc95df0de4e8f1151e00156d28b1da703cb4b12ce1f312c83080c3864c6d183dd9e46a8cf9fde8cf03603b37271b9bb30b8c5ecfeb62429266a46b2d871599dde9dd251dfd71a60f6d9727d5759f2b4e8a020eadf6ed580815fa0dcaec087b72b7308ceac3600d2b5379eb420d5aef338da3fb0961e565915b2d90d78e959dee4917a8affe4b690df5b52929a8a3f30a77d894ff67812fb5ece0c0f3ea81dc2ed4385a5cf24a6a6ebbebf10ab4c9d53656df732869c26994213976cbcaf792c52ff4d55df0233a919770531cd7d4a236abd05978ed0d2026f259daba55c57c013e6fa830285159956afa17e824a74ae62e533a0f82d90b4fce29982d374ac20a03ac177e93bdf8e51893e683c662bf3cd33dfa6c0b8c6649efc9e946f2abd25484538c822c5b3952dd1266633b98591ac3c505f34c8b1c77e5bfdd9d6d873953dfd27f6116fbdd203545cc431bbf4fb01cccaf1580bd90330713d0fbbf1231b6dc2561bd207a162b3ca544ea9dd94ae51af433e120f068f908acd1246c2b2075dde71c60e3cfbc246d2fe868b5d1aa36cd994d165e56129d2d88fcdcf59b69672bb1e665580d5500c34291235ecaa792fd8570f6e6b4d17e47ce61b0147c33c0f9c842b0f34331a9b5951eaa59961a93e51104774705a0f595c20b71f55479b1d4e36875d58912c507da512bfb6d854cc91dcdfe2b66b8143614fc0436ecf89fc317e0a852b1305179c621c0357f706720071678f1745b25cec9f53ce55d17a183e4b70db70e0cecc42b148e48e5a371ff97da554558b358096c1a6e978b2cf09a899f4b2810e56ad6f30b4519ce6004a4698a20068d698aed91b63a7e25baf1eef9efe62174451fd74e9ae7a51da31f50e0b1551dce4efd060eac0f6eee55560d9e71733073fe5e2de9c47e8add32337422c85179c8f7cba6841c43eb5a369cbef07d0718d84b2ab839f9e1a3d5493969bcd6f8bf3fe783124c2b6cdc4b915dff90467f49720f0dc0605733dd113992bea19a77d91a9651a1e792af18def9698a0d72eecc9f8e301c5dede86744bacc100218ee9d4f8d0243fd8cd5198b233799e33ed2f34e2970b17b332024d59ecd6cea5e6bf79b530bf95158bb5c84ff91440bdca15963cf8916b0ec0384782e9ae9e8f03031a40cc67c1bbd4ffce51912a1791d20fb5d80b7dc6077042e7d829c5ea0e78e195812aa60b28a92c07ec25634ad5b6d0b3204a1f5fa6453e66cd376cd411fc3280bdbb1d0055c61b9c02fa4861278fb816719deac5618114ba17f542b4fdc90959953e95f6e72665acf1e02f4361117540971ab5fa0e7c10900b0ccda3508b70c376b57fcd41f77d6149367df3b34250e551d8c860bb5eafa6a4e892b256410c46ff67630799d06c45745c96fb9632978ada096774e9ac45c8ea0edd9bd530b641e2aebc74c52fea7e255d061095a8aeb3be901250439bb706ac49bd38e28ec43d5005a3c743dbec431185784cce36dfb86d2869593bf1eeda74f181c313fdbcc9b472fac1ffef50b60086b3af340d33ef54a"}, + {"00000000000000000000000000000000000000000000000000000000000026fe", "0076543e7f0921bb345aa4f261fc16b5dc8a13ac6f73df25b51db48c61d38e3bc7b57ee9eef3945ae7ca229d4d4d0454dfd8c94176269846c862827ef486b57efee6aa3264c1f7c60ef9acd872ee32ca0b9f49c908d53d3c31dd9645f37391dea0b230e26cf27ace8c51263dfade982e35a01bc5c7e3393cef20441a6b9c10620b5b97cefe60c3beb338fc7a006914957384dc1d70fea28a8e2ddef4e14312b73035c51bdddbc2fd00b2d234d99740adf366436de05483e5919cd78b5310990f980514e7e55051e34509f7255b2483ba18ea2a42eba3c5786b63d26ac91344526c5f5549fdfe5f4e9c3cd8a31dbf06f23dc53ef76e5bba37d57ee207016c8ac05ead2991e54b7234682dfc0d24bf98882506a57ff3e1cd45b928ff76623efa7eda7484b97b1512e5816f65d598fff6714539174746da3e19df4e8e4187e4115afa9da1f20c94234168744e425f5ffe9600a26cc76dd0505550e4c050327ff24e649a93f3820e530e5f816a8d51a14f81ddd677fbadb4f25324bc02571de3444b82b0afd97421edb1dbc945a19a81802bef6382f18c91196155969dd2ca4e39fc6df5ccfb0343119f356cbf796acf839de5d20c76182ddc8e0c2e1e326867e82f534fe364f02a6a217d7a3a18aa7812f82772a276825fc39cb2ecd1a91f6151c4daffab1606be448109db4cc88374895db49d8316ff5b4cf60f6a716bbd87bbc83fa9141817539e361e4c3ea19a400044971752b7a98d2247a292efadcfb4f97e16c7155be91d0a5060b8d42a9460ce551f3145b2bc531316ed65258a6cf111adec677005ffd5ae56efdc4b53142baa166587e03962d522575c43f64a13bb332af32b919517a66038f564b77385982ab1d1cc83f946f31bb516826460bf5930f182e438eda5da36c85373be54da04780ab375c3cfcda5ae15cfcf2634ff38012e035f09896fe9e373b20a80e4dac8fda848638ad36805474b4692d7eb8af1fef908fa6ab94f1be2d98bad108f96a5d029c91fb4fbb25b96fa725221405f5b1520bf67559f5846f9f56d4322799a098d95e8cf72751e2d5c9b5f9e7d73f0d01af116713c4ecccabc50771eba1e16ffaf1ca9f975a3e7add075113b119a3f2b33efe791355a3829128d0697d3e4f58aa868bf76ea3c8b74d2bca39e855fdee6fbb0fb37269aea962d6705303b9bfc5813d1ff99a0c2069388a520379f944e0ac598b544d886ade38bc5154f5165fac91e96571c07f849b4118a8898aecd70d903529f62f57b58a5510aa4589325a1e25d7d20921a41d1e9d9e3a1b1aa5b054b714fea049a16a6cf4067b44477fe33481fce8e06a662f7b91032ddeda4309c99b0b68258a4dfe290099e6f740390ecbaa4a354eeb9b35f1dd345fe59742184d4330e32ed41d86d73d2df796691f1069be93b07137a8545029cce34eff3566ee3f70d368f5d999110d8d661c68c55348eebb3bfe2cccd233e703e567110939a6cc48b8dd74f01f292b467225154a033164d3e162f8914db6923fdaf364c2f42a17ad3989cb9910901cad94543902d00ed5381dc3bc5c9d23794b0cd248119be05e83839690f2d3ce77625af60b05fbd220d3169e8369736e1180683eebf49e11d6798fdea8116e9320cef6a42659af786a468f9a32e3f0b1269380811e92c031934554aa9598646d71d8af10f9baa9d0d6284596520e1ffab3908154ac44c6e1737db2eb22b3fbdb58d8d4cb09a0f160aefb783b66c1b3d645d45951a2df4d3f4c3870e65675939a13a6ee89f5f170cb76e623e5fb05b429a76882a557082720a7efa7f2f05fe397b90ab81ff482772c34c01331b7e7dcd7c13403eb9b016aff0d1f4b709506399da40f89440c8152aec2a2ad4a3d7b3ebc91a366aee5b4dd2ddf9fb"}, + {"0000000000000000000000000000000000000000000000000000000000000cfc", "0053689b781f1e8fffb6a0767f5a9fee99d7f5db640eae25fbfd84a9410548732b02771bef4722fce6750408cb61be5101d77575888ca0f6ad36dfc53aee880b5c031a04d84d89470de1f04e25ecca4d43b7c451007a37a7e111a0b6e1df635b74a55718e30af326981b7ef4a227c8925cca36f2874aee43d516d6ec23f22bd3eb3a4e8fc52154d09553edf3bcbf4ce0fd7f5449cb475258b7c3b5ca8d275e17d1870f1a9819eaf300b503c1124ab934788fa407125084b59723ce5ffc0b232e91841423e382e281e09aed7782b9c4fecea00621d64f4582ef28594d25ff7f5007f64c8bd6f5e05fbbae0414d8d81bf53b98974add34ce6f64dc16580602f7d084b360f5c8a944fad5ea17e160141520f51bb18f2ec01625b106d102083b94f4e8db418c66fa075e7fb1dd0882608f91f52a2be78e5ecbfdd6a73f71a7cce13762ab4fc19ff7620dcb95eb562b1e769d08c6c935b009cc0d0e57f100b329e9d1da79f69036169e2cbc6956482513e7937823bceda16217eca0891ef6d2556a1e6707482cfc18ba7071272ef31a0bde2a5d93b29cad09e5be08a4f8c1391666c01e7834690a2a693452b07bc1bbf100d0f08d85a93115b74f4e10bb2b3996e4dfad594de2295b600d352ae7549440171cf8bf2fe3c41d5cf2d271e9ffc0152e5491892d264ec60847907bfb68591498cdcbfc2ae41db8f7c70d84147e779f58b3d764365297eb1d1deb599fe23b397a76658cea9eb9720a3837f9d3984e8c9275e9af1d5eab974ef90607e11d2c9b1e715be33a65fd9de1226a9944b0d9c4392aeed379493b178b5018ff3a2010b820d533148e66f91b25f29fd51509f4697ea604236e12c3e7d72635ece7a2f87e43bb70cf863be68e3303a48243dca5d537a173c39b58f551092ef0a284456a364c8f9bd28f2b3b097d85d9181f265cdff2d3014ee7c641df0e49426e155806e16161711b93332a06e6a9af819cbe2b077e820358b99c7ac954f89d47046404a465d6ecb1e6b9e25482cc4cd580f83e405535ba759cc4a4e9798a1e2588cfd26fc969675e063a0612aa1c5e15279b199381d3249b8bec8cf4eeda122d303a654b957690cfb655b4ede8d3c5df2fd4171611a75d3d05d0d330cfb6d20ec4e47a3d2e795aff0e2640abe0950b0ddd4fc0836ff4eb1a3e73e31f0e99049808296b9826a18369b7f20dcf31e70f38fa16542c2cd334c2d23eae9f0a268691ef71f5fc91385fe60fdb2329c95bef4148a2724687782f5261877e372c1395cb582b335911b6f54456a0c43fad1748fec0da167e444b6c2e9703c75921f4ed51bc149df95919c22054ef98eb1cdd416542624888ac4a58db834d48272b4e8f2d1ceaa1119fe4c8be6871bb43352d7c6251304ebd80f14ed030af37e674e4e43e4a59e43a0aad0268a6fed7c2298b2fd1109032c22c91012b6fa2320f0c27634d639d6df747023b5dab0bb70143faaf75085695f4b5904185926ab480d93c48628cf05bd9fa09edcd3811984a7ad45d29f4e7ffaeb6f72d1f87cf047cfc5c6aa697cfa96102d552ef1ab5a7cbd4e86f3f98c41ec990ecb1973fe9784c7a152a6bedfb1d2c1c15836e96d024f1ff2fa1d13fcddda1cd2778cd271c2df14b4b135823cf73d3db7cb03b3e2bbbb73fea02f47512b84272206c34c43ec5325bcf10ba3df5890f050f746047bc7547495117a749dcf1b50dda85380abd4114b1ca6b8bc8aad3a0866b3bbaf9ff7e4f491a925904e195a36711a047a6eb3e7c6edb083972791d6893886ab4aa6fb8ce721f8a1f28dc8bff52ab6e2c3ca216faa7b775e8df3746ffd0b25a4ccb3317bb22d535d71a1f5655288d235d699b0f4907f29202724181139d5b5429c8dac6a505a5f6aec980373482a8"}, + {"0000000000000000000000000000000000000000000000000000000000000836", "00747b2490a195794b752231ab22941d124b097253087d2f12c4a56d3fbc3d74f5952d605a780bfd44bb14c7beeb58a2a1614a2581b18742e900eaa02de062170b5b274e0bbd57e95a441bcda6185b11bf3fac4a0242cc19996039b34fd492e5983955891d834e8fa910263329c8c9735eec249842eed3c8febed8bf07101126c3dfa807a6c8ddcb912af6c31f5757d83d58b2118a9cbc9ed6179979a0f50537e89a257d6ef026270773b69261488692a523b35926a2c20da76e0f27e316f3e2029b18934b4682f2c211705386f2f59c7795172f7b09f28eeff4fcdaa1bb68d805e99493bc6e78438dbd858f29ea05ad49066b14c5978637c7984cf91807c69f7ec99f06e5b3836a15ab52f9735e51a1db1dddca1551196885f82f02a2d3fee29cd85a8c63772d623fb17333815da127640f63ad41d5a51d9840f53436ce9d43b3ee67b84b841a24576bcdb78430f34a00b42be43afbe283f7d631a0e1b4bbc1bd8f38e6ca239b463bb74a2b237bd233b5117f44dea09935697012ad23cf6352e38fa280945dcfbb73e726c07e3fe52a115c7374b8704bd302230c31ec3918d9e44dc8750b35ae07bb086bb1a7bc817b64702e89fc12dbc6103ba947f9004fd9dfdc97448be6caf485a5e1dacaa6126cc8d42f67296f687311ba62d807d15a74fc02ff22636468329fc9af4d65a31508fbbf32f79699e19102aa6cbe0f54d34d750ee322eca2652158bf4b265904852656dfa2ffffda3910c3f22c0d5d308d0f9bef119ff67ad68d8cd4dd7f81eb6e3b61c6d063d9e2e92637832a77ea16abdb45935b07e6a48d549dec5dce07b75eac59609dab8c92a220261ef3488a877dead3134da0ace8295f298cdaa71836ecf33ebb90dd64fe1a91d7bf11634cd7f6c6d28f854a67f3b4099f7abc7c801ffe5375e17fc44947f218d99282e4b63eee7e013936322c12c420a0e731db5bf1e448ed4d9bc57e13fbd62d249ed93b7b0b15861b6b3a675cc85dd5520989108eda0a93b4a0f8d70742c524726dde96c1001cbbb6516e5d421b1afa8a75cd7d6966ca5dd9e281054965b89f0ce9f1afaf64297b582dba65e47a78231d2ab95d76d2c2850f94e4ab34c2dbf5403950e4dc1559429e3c0805588b4891bbaafafab5558f3de40b2c7a7db305921cff13940620b37d0726eb92de69f317b77cedf1e13c55bed3d2f7b3e3b52d43755c16723bb87bf9e31656f19f14796a0a682adac4aa1c884f1a4bbf322595d811c3ef73a76d1d9d1e66b6564d1b4eb7abd30465108dc2b60705d159d02aaa8999eca824628ccf0d4a81cdeb6742fff9d29993723bbe72843b84a27a1c62b11d30c94571865489c7988adf2b4428fc29ba4b1805674e28b751ecfb043b06ccfc5cbd5910d507751bfa69c01b56573d7a8f76aa46b5e74e03c238f6d2533f14efeb353ef13808f1950d52e38803fbf217e9014411996668e1c24cba16457cf4cb400577b454c9979ab19c4684e83de06b2dafce7034be26cfbaa28c1e2b5987d38293769c59d1de105523b306f20b6eaddeb7e3145cf4b80e3b59a5738652f03f1eb77a71ad8ac526a1cb033ff0f266b52ef050d2941fc6e5f3723a623be07ac226becf8c34cef770ccb025f6ac6741141c913013d3d7ea46a82d5ffb3cf21105bc6874f4461783f494c963515d9aae6ade78634e144795bc1dea1e73b25df4c6d8dbd70b3b54fc950b0efa86d56408c8370aab51f5194f1241d5ecbe663610cd55cf84b6d931dff951f4d9c98500f2787de8720d9a5ceb08ceae3399215666bfb631075e9e5e8b4318bddfa5e40b279f14bc766ce3bda91f25dc5a1aac1ecf5d57d5888b29698fc55caac478d9bac5ee8ea1207a3b4969987e9dfff5e2b20520fe353b7dad6c42"}, + {"000000000000000000000000000000000000000000000000000000000000381a", "00031d3459d877ccdc94c47f5d3bf6d5a5c51204136475cd28eb276957ed86b78818440c422c4c5a569604759ec2d4c4bed2995df3d39a78af39901a0fc551686f8edbcda0a753ca48870edb7cfd9e09725a2b300b610e19624b63610b2c34d04efb970a8b7f39dd0410fe7dd340187b49732e8366e07f1d71bc307e54742ab3a59c0fcef57736143770c7ce80ff89847e808f67573efd70ebf101b8b30cad0e6633afd8715edae0067302ca7da030f1398454e4edd987fd5f18faa2d41813036bd5edf6b1a47761fec53af3d8d75d5b6b873b807408a8e6e33988c994f268d7d77541fb94a32d4533f72e70d17c74abe675d9774308068de15784c60ef60e1477f90eebf9b29309eff7c0d32769dffbf21392e3a18811a28f1df6c29faf1cdba8b1b9f256eb212d27e1a45443a19bde3599ca433b217a70d6fd392e2a3af2bf9148ab333d8422bf63fc656d36d9f1310278169d3111b40db0fa94eb45375005b9b9bc847a154229e47c5b0d5101f67833d97555fa79da1b102d19566b7cdb534e1b98e0941ee46fce0d432536cc6830c5a27586e0ab9d3850e5114cf33a42d351fc523027b03be4fa726309ea3ad6bd42c3477ed1117bf9907f54165171b0e809ba26585f916adbf65d7adf812e357989e65e62d7d563ce448d25fc5ad9370dcd9ce543783b2a4e6ef0f399ff36548ad46cef1b235e2fd7039e3fcf198b323f213db676163f784f53283a9b8424b82660d18c7269e26bc357bfde4bcb68321d442b06f816d09b0ef1dd07f6b3d9b3e24e29d9955e5bc50f071275eca982db742252d4a79f221a0ebc14ae0b0615e250ad570a52f942b5ddd3deb45b2d2fdb6af83dfdc67e0477e9d9c24a36ef02e1ff62b460f73832216ebe874e5079a11346e9be697af9d6f8429eb7ff693625368aaa532354e9b7bb0aeeeac6a91edb7ea80038877c584bce32737be215d8cc8c3e267792ba0a07065d4a0959bc14fb1bb39810233df73447dc6577027057e232125489f0a8707fc84f82b4c5f3f913a31a1c4e73d1ab9eaff751b473885cb5bdd16ad9ac91085180fe21972595ad543090bad2f46893dd79073b90cda659f66cbe29f5e10a89b8d6561700e6398e941fb85b28270afd7fce7564f2f73807aa6d47d774a0514a9f9d0e5afb03f70d55939d38d44da3474e5f9302843e3fc682628dda6c21014fb0475add71dafca80e5616825899d6c75fa8022940f78fb220501e0c19131c0de8ba5b0da6e5fc373f62feeeedf9167f63d932f703c59995be74d9e395c39f3dbce770215e2bfa0285bed0ec3274d1f514514ea81b7a74becb34b5212ae9ce81abcd32550b077870d7640eced31e1f8bd204a6b975f1925639a6d54369b49b5ef573646cc8b83416a3a42a9da2cd9019752a94fe5e715082fa446602cfe57b804651c6439ab31adb3ecbcf1223f94897051496285c144655729fd578144819927568fae6b50fb8eaf34c6b4da5f329871645748f12116f15f9fe46809cd9cb713797d7fdec04e5fbea0b1e82fe4036038b004dc2e236679ffde054ed8e18547e2bd5e7782da3cc8463cd8524d65da2f68c4784112c6773eeb70722ae3ea982a13090db11cfed10715e67b3d99aa5156db5005cda71e5575883b373abd2cdd50b7519bc032d601d25dcfe28f83e529827b9bc2f0ecefdadb1331a7c97f70ce5527ca263a7f1f68bbac4d67e1a0923471dc7cf4b5bc7ae0b28059a4bdc56aaa455b08232cb449cdf134de9114d458603dfaee6a1973e56f910e9bc74641623ed12e992bdf32c4b1985459a0e0520b957af1448d9dade2c23cbce2c0121edd45adf4623c014696149c76387220b1aff5e946f273e7d2d78298625429d90e1e3eefb638d68ef59c5c23eb83302"}, + {"0000000000000000000000000000000000000000000000000000000000000270", "00857b99c44b09aa8286005e75073e386a62b7f59c165cbefe6c301f09f41b917cb11400fe4133d9053c33ef39ac16146d213400e9f03bfdc102bdc09755825d0ec69fe6ed8875e2b06702a47ccbbe88d3d7c799065570f99605a4a7f9bdc079e0a1fc303ff65ee0ca144196f10894c060cc71f6817dd53b9ddbe491c8a424e1a982b5a320ab7e28b33acdaceace1742b1d7962699c1fb8dafd4e1e9082398a2ef55af82adfd687601fd63524a80a3b75d0cc68d37421f8e416e581c9a1e2e5917b551f1cba6cd071037cd06161b92d8ba72128c4cca478be1f1d1df83b46e541e791d1bb96cd41741cccc4461033928c8d18be3a241d2dbf53b99fc1d1caa091ab5b34ffcf582d58f4c15c1e4c21502662faa99fab04d1ef34a9775772ddbe7aa78225fe36d41f6d51bdfd17464cad776f628d38d39fecf7ed05a5f2a8f87adf0dd15d2ca2643db3da1f608753a79c5018dfd22aa04bd5240ea20a9ec7a810a454ad9466c163ce472c60f1842f3b1e19d69972c0c9d0fa6508333a8d3882170c6f5eea5f373062dcd214f9e8eb88536b86f6a23d31428b3be2676eb59a4623ce718006709f282d693a2e543d007f2106af79ef55d9098d6b93bc7b413225d906358a27790cbefaf02eff279a3a11419b78c191a584f1ffe93743eb82031f85478510018bde1555eaab79d8e19a73073ffa0b221835a77f9019e63aaaae043530266a4a445a796b5f904d155b423bc8b84c375d321ef1f95c86b5d5eb28e69d72ab40b831d0f62d1b81bcf4391a3616e7fcd31d30a2665106c65eca15a9eb3d3e854e0f3ec52a53913d23110037a7bac5c0559e27b0465b5b160a4eec290b7479e241e79556c9bc18190533780ca5b7a86b2ef17c3b3189a7ceca20dc70169f5e9cfcffb04f71b5bbdf01586afa5883428807fc808888a82e1e24e466b3f6eea011da3d6c184d2877a81311e6e7429b3accb7eb9f708e1642821c6300fa7be00923750525d4d4eb2d95201fa4bb6c8c8e638d72df7b08d40ab7e2b5c148c2065d894c38ff31a9dcdab26dd62470c49baad11e83f25979f3753db5fa1609a167d285400fecb2d5b91725a267ce6f4d71347c613d785cac3c7429db5de88d642331d3d3014194320f785b86ebe58099f17777d347c716ca383df6ea78c07b8729a507f0e2dfd9dcd7f017c6ba2751dc1c91e5098d6994c4686b8d1f7ef7e28615572217097fdb3f1329fd7aaea2dc42d742b9804f9c8e92e90a5e1c969b5c2717ca6e6a11e16c71e2487d1753889c5b36c4663488ba0d210f5d1d19eba075dc8677c50d334d73f370441f9275fa4b8de7f4413fd575af510041feb78e41353afec8587e19371511164ea9002d97a64e82de283895e7ed155be18a31535af43a1b5159a2368a225555fd3d4ae514fdb279801a8d33a978e556af97de25ed81f15b0a0b1b94e551d4edca48b199fd1afbc02aea7e77e9a6144b7312a05cd9965eb94e375d8cf55cc54cdceca1d379873640f9144be33f1053fe938b1afbadbfa8e5173b6032405dbc10148d277a4b2fca1880af30c1cbb075678861f93b410a4d63ba74375932b7d5385baf1a95930500e19d593e3d2c834d860e277793dde28c621b890052d3a4e7b5f9efb7b1757f2dd207ffa7590841dc62f01f88b65f6228d4715a57056053576749c6a3dd4d11191c3c1e5c6f41f080aa2773e4369faa37f3979c02aa4a603730c9a92db76c41698f529b2da461d8f303a8ab4472b61b963b406b68429d62b561ef83ed8d417865c76681223f6be6259311a5b2e4ad481782bb41e8545b7e1d1f709c9d7b62915ef554db6b350037b3314fbe3cb2f6a03dc6ec3c75dee54b9afaf73a27844bf0345979179812425e5221e57f176f9fc9d93c1"}, + {"00000000000000000000000000000000000000000000000000000000000008a5", "0003703e1f2a9869c342406284c7d828357c1a5cff49a05fa17d1276b7b80084fbd2d831338b055e56e411b4eee5d0dc950f0c33117e85e2d5e46fc9ccda883a5f67beaa91b7b0da00d79a15e21ff2fac3da0d060305a55397c579943cbf69df1a69da32c5f31df15a0c3e4611c5a7fbadf8c6d2d7715d5b7916a65cb8950ed98e1d2ed5545ef46b64aa7dd8d30a415d7389c41801ba308814b7397535579322c7e6be2ff0f5a4a802a68bc49fb54ea5de3d73786ccae32e1e043bd7df0c0452747a0a5e41354d01918bf8b4c0aab27f6f4c12f24ef77908215bde23516afed90175f69759db1319d12415b8c81ff150cde4b15032e021d050aeb3be151e35c939892cd529c8918cd0a39fa90f27ce58812d161cd96ca68c87a667b807e3d9b91ea2b89d025e22abbc345e5fd7fd5060a44436d69856e7c01a2a02278d9e9962cf437197d1c510466a45f777445d99fb027d13bc6b0ec90c79b6620c655ce7e587e4d003601db354f6d3d6222578bc221a17b7f229381c8bbb260cff2e111ba3a62b4780658c927de1aa4af2b6b8481c0af9d032e4eeb5b530c1e4e3fb2cfd34630aec2606fc489c34c8b48725bd5555df2f09edf44d9ac73f0b1bd3607704f1659395114fe451fe5f38b03e9a680fce33a44d952e418edbd4adbbe957761228fd19da2d43ded32c93644bdea5e4b4ad58e465fdc259454f069c7a1bf48c8c1f91f3a45568f5eab66494da0cc62d49860f5a9092d8a4aef3230244271a77aa355358088ed8d9ae08df4ea113c3822a370e01de34aef50815eb7e3a1a58a224f5df743831b6c272f47cfdc00a14e8f756a118c444e829e54c877dfea957015c038f1fe22eb8ba2489c16c801300a4ffeba59f7dddabbb1eef29f075ce3e8f7dc865c49e32630591de74d2412312076a6d1d797dc2c1c6d78df31021d2b772b72102a697b863d107d5abb7627545998ee52681ce488814a291cdb3c859f3ae4f06cc436a5ca23b82bc3a9c0773259a288c9ce9f4e27a5984e706f32029bb88f61ac35707a2ced552b5158516a86bce99ded4dd6e0d2319beb6be1540650a97a410026a96712eaa6b9120232db16402d33fc6acf0a75e4dcd231e11a8542986588f777f0cab1fcde08b0a2f0cd2ce36bbfa97dc5069289dd1051bf506f17f1726ab65df45d414baac8504fe052f2699f330ea42b13b3c9af520f8af558a5d34dea4ff68964f26f4a2f35076dfd759e3a6afaebd0f549932fc0590dec9ea3505d16daffeb940f8e4a64507f25a2ee792a57097846b29524e6d8618f7165f0c276eeaaee776ab69fe217f66cf05fd8afa5fb45d212fbc2ab10a4ba7d49072874ff4dd522229dd31a9184721323019aaf96551129e94171811d1dcfa331f5f3686ab7ba581a3a98606e35bc5555dbfd9db4d7c03414d7d8ee0eac3103df036bdba40a8a03437cce7244e1f490e1e6c275a6c059673fd6f3985c8d8d5640bbee197ee4a2e832baf6231a447ec79598c7cd4723227ff5f2da2a23155c8e607a9b8da6dc22395f28f07bcb399248adb0ea77440e2b24abf5d849971ebba0f232e09bc68e7f1968763e4ddfbc25eb2fefa5d2f165a42fb67d606cfc3ef723ebce570856b56fb5b4e2a73fb4270d8ddcee3dad7ea03f8ccda177f752e1e07b23ba68d686c43dfdbb2504bf13b8e08e75b129d0df00241679f72efeeeac389a9a964bced07dff2af08af67710a939f0da566d1ff1813b4e0ab4e7f381e78a527a688a50a33df79bb7af4f9da8f414f3a88770979bda57b05cd2ccc26d520a5bc708bb4859f15074058ce3bd71beb8f94a618b6ab5088e6fa3abb8e5f0ea9619b455005bfc19bc3ececeb41f103e1e97ab632692763b4a3ecf3e8cfa3e287673c21e6b30fa293"}, + {"00000000000000000000000000000000000000000000000000000000000000a5", "007363032a9d0cd1045df0c6a8f541d440291263e345ab659bba2e83efbca3964ad6f3112f866d7ebe6909ae4ceac14a4ae28b92d6f2c53e77372127fd2e3f3dcbe1f656917a9b4fcb7ad26478a56ee3b5da4b730eab2628d1547d80cda19122c5ab06905981b270d232cebdb17e4ee6d72a50637da7a53846530b9f5e26308323114c7c4e4bfea463a16bc23af637941aee148c8cc6dec67e70cdf389298729e4db5f1eb119e5dc05ca0bc1beeb5651611d90a370caef967cecfb2dd91ca8d4bf570886f5ec21552ba13757c37405de0f360832e61222de1b3da3c4b0efa2ab080e58cbb629b32f9e31f5585134c9d405347aeb4477be0613d8724a07b55c581a50e380cbfba468e8d35c1e05f57789a10ad7748fb9e61711513091a09d1b9e6ce725cb2b7c0b12e9e545db2d9fbac3560c5dc8dd35f3ec9b88c5105c4fff7d5cd5a9f5771134c954a59d30af2d444d008088b3a72c8cc1a90340562be178e1f531fab56e075075010d05ac0b05aed195da19b7311b97fbfc14136369e33e18f696d18a11a07f461fe23ef29a48407cdee5ad2efaf827e0cb88eea0793b266ce79bd1410096ea2fa7727381a45ac1dd59f538f15e8f333a000aa46b42220c21d666dda1b4e6f807177eddfcad7014c7fd885cf0bd5df7a1f48eea4ce435ba0a35a91b3cd8e786c7194235c458f578a2d5646ad2b77ed153033feda056d03961b39aa45ff641858d5092d65b9e097dae4d80cc3964dac4d18328a41594ca2dd0ae6d6159ae53b5b0cd8de443b80ea0e776c789953c8e507a6ffe998d26f227ff7e6c74416a10076d97bdcf980d66ee329e7565d1d639b3fc285795911fbd11a67f20a20c8201d557fd744524a07df8ebb5e92b348eb22cfe3b854917532d364442e6de35a3c1df9fd694204ccf44a170df99e50408052349ec1d0e95b33fc1ff01ace97e3886532a7db043b4467531edde95f532661acff3f5f8973b8b5d3fd39edb756595b02f1c40320cd8d25f38033bea8b83fa350efb4a9bb7b69e06a912b414df85d6d1b77d4cc34ceae6dae8df29f17bc204ddf9f2bcc4e4fe6394b362946b716ecd6f7fdd9c2c5193c94adeae85de2d983d75e04b46d9187fde1c1ba32b3488d9eaefa37411d22762034e8832bd62eb276db7d4641fa9315a1f936f9a2e8c21cde95c9af103dd48418bc2d2e0fa388902f6f62cdae2c07e79810b477cd7a9d528cad45d1d707a77c95762ad9fa0dd1bf3592bcb920201a6d6443b4ec25f21c38b346d5061ad73472d1c3471265df8293e4f21124e1795e7bc0758a9bc840b4df4d5bc849ce6fa89dd605334ef650c78775a8dccb27fabb993246d458365a62f5a60961265fb2087a5a94da47443e8d3ffab5994eab3b42e35cbf9edc7dc2eacea8654a0d2db7af5dcedd5689801c33561980b00010fd5151dc0fb3405d4f318f58009b50e6cfb4e55d0970cb8a3c9fe88f2beda9c798308ad87c6601b492dd35d26c87bf6cd71fafc3b22151b1d1d147df86977defbc386a6424ab66cec37c41f054399c49bdb1711c90a285cf57fedea1c6bdaa679237b4c199038c07dd4bda3aed87cc7864f597ea5bc05e7861d81ed37ab99159467274640a93fab79d8f808023faddf28a1cf9edd0507fec56705f14c520ede02cd2bb7bd0438c4c55902b857ea2d06a9ea971afd2260a588e7d9542ff0d522a98a3f372ea5375fc6a70adc25f5228c468935e7e67f44c11021c16638f795133d0798a032c60ba959f8d864626ad6a7c61b94ff0436b1c9b20111f48207d2fa6618c0d1959a7a5ba10cc2af268792acc4f697d1b20e8e34ea8e399d39151f17b926cc13b8d5053a12ddd5238f336a1f3c21002a759bf4fc5398e584f4e38517f1fc113efa34cadf"}, + {"0000000000000000000000000000000000000000000000000000000000000556", "003d75ea1757f9575803925110772a5a19c5bfc0d6097944e897871a13dac8f28e009f42657b13ae49480346774a6fe17d29d9d790c2f1793445e1253019f925598194932dad71e326e38c13f53909f812b0f663044f099edf42d597c57102f8369a2579fb5bfd54b90513b7457ddbda2b06e17338e4c24b0512885a0b9605f245ebcd072145b5fd96109a6e424336499fb2c325361a2b4c0d3b5eabfe8336fd497de6781dfb16130177dc49a096ce4b67cf31089341e5611e4fcb728e14ed6c193e321abbe068c1db6577c2549379f97ffe0a184e8bdfaca6bfde85b56751acfa9ab81199812430ced3db08a1cccb5d0ee3c031a6dadac9aa96564604261ebc3ecc82b2f7a7c1a882ef3c7a0dda35967426001135e51a2b5d6712836d9d780dcdd3b11cf78b1125834f45ce22c5f30bb17cfdec20fd82325199762984bf6fe15cc459a740e50006e1a76f1558fe87780111777de880ea5e170d708346888a3064f6524ea4064efd3ce54f889b0ab57887a265dafefe183c0d9a0aec8943821e842b5fffc631b0386885c6d21dd64f48c555bd8728b82daef26a49bd7156069729bbe12908dc03c7811fb3c7e94f5299996db0662d26fad60f19fdd4fc075d994b2c2c11e8d4aadc2dac50f31f700fa5bb4cb049cf6bb80ea5c4dd77810653663d40fd21baebba9d139818b0b2a6e249f55075bcae5eec97071f88f814cf85453e5b78b7894b5a0e4f953386b927ea3e5c708b494b736003de3fb7df2560a85547bf0ec2167578f1653bf5e175e7897226f6b2dc1da27240e85da0e8a2f5f9eac4ebb2b871076afd967fd2cf23cf6c35aecd7c2eb2a8f33b0da55424d2cff8dcd84766952249da7be8df5ef47e8cd547e152161e9c48289759c91acbd4d8f8fe4472cce3e6baf71279c4603ddf52f0e8e634fba79ee5704e78fa9572cdba53f20096448c7e44a98ad58545cb21be058677eb97d4262e66b63d7233345dab752ad96fe5ecf30faaf9fedd350e37b2c6d55b61f94666235dd0fe8b29a47f2ff35c1596ce9a9df163a423a5c984e97e7a3b351e026b036ccc3183a7bcef8c7861801df027736b8fdd38973005ede5798d37ffa39e5479444d5f6b7b427dab7e1468376746255fa16dc65238cdd30d8268ce7cdfad32d69d8786df101fcc6ae5d615ddd89ecd6c5e10290504ef975a1b5ee6e55ed19faa7ca2cdeb2a5f157b22b17d25710967dedf6174033f655eb90a4a70feca2d50c42efa90061fd434183012631caa4fd55fb5d730592a50f8e35ce1c42f26d617ca463a0e8bd8e844065f2ddaf16322cb73875526cfd6add22f7bb4d00e68ff4c2520245ae92e065a0033eb183eaa7c565ce210734c1a5f1d3ef94698732802cc2ea305f17e978b1e5d816ba692093090ec727a9b1e5edd265f7970c701de2c086ab0fc21dfc5252944c6697e60aa1d45ba0713c9331a2d49f3f861e3810770247a45c479954011d84196fe616939d4bc87e3dc4058af504b1b71a91236a179be47e774d4cd160816654921f52938f52d03eb8b82098b0ad9dd6b013446a071394b8bb2fd2354f5cc85f61c579d1ecd7895297ce2de46faf40f7c1269efae41ed7201c6f1559eaeafda2360ae1b17d82ee474229d912a0f6094a3bd804d9e2629c755060002ebc7ce290ef75fb56781f81c6c16ed367b2c06c77a845ccd74e1442d725c298cd9f6915747607d34a80ac9059b0e4603cf97e752a1f5ac1adcccacb561de0f707f18de4a7ac7b4dc9631053d031ac0fd381a6203930bb65ecc0b36e1b1d262cfb95f7a585f95795d4334fce09bec601fe7d3b505ef3b5cf54565b185a40d04621d5c4ea85c8c36415498c59e29353f4d994f2656d2f82692dad593f3d49f684cbceecc80ffa29d"}, + {"00000000000000000000000000000000000000000000000000000000000007d1", "00aeb44d7e06121526c0422e41f0069e1a2b9f4a020afd1e6ac00384b84d3d53d97c7e9bdd80cd1d9567021ec9c3704b9f28cc6f725724aeacd59b3a5234c024b5e287a6a86653c0ae156c4cdddffd628c32732409e94772ecc9476b9e8ca34bb3a6c434db5b301af035612f583f0f02d7efad53edafdc1a314a991bcedd1265a58749e5c443b0d891e12c61c1b16cbe50d9e617c5ad1b0ba737f1fa3921b7b968aa75da167e96fc02c6c74864d76fd78c9218d5aa6fa3e27a8f3e87160fc9557b8c0d1bd3ad37219a355e85cd7b2c17cdd60727c673606d595bcadaf1855b34dcd0f9cdbb745c3ce3eb235130dc7bf1230522553cec4e8cca155c3702e4c4dd2524fc47dbd7b16a2ea90b815c2311dfb5178fa138d65277e5e125018266d534b1e997afc21003ef6cdcdda029936ecb40dc63518a7a8a61b5b4900463a49c160291cbb03e725d116d3a4157f4dcbaf50351c8918302df255800611d5ed842d8b9cc660ce704200bdc664656536bb16939a5f3e092aef657cdc30c1ff956e0ebc8bda4c4760c7c5e22cde9935db2cd1182a427a25721477bf7f48a2b790bbd5e982cae960408558035c50793c39283f0b7c649398faa3e55d834114cea6bd72617d809ea2c4276ec6aa274bee2a9144357325d49afd9cb13717eebf89fa50b92aab01e1d2d2f6dc4569b7b0af4762d41bd4f11b4badd2433074284266ea281e13c1c45b70ced9d6e5ff09358c32345d7deafe7f4519557c7f33863c6a299f0b834783e21a7f3f8735bc1f95264327c5f5e098940de415854d6252e0adf7641c35376baf343beb1d7b57320e316b1e34cfd8a219b273e28da17ca39766eae154fc61c98ab96e22665a534f3562056e118ba623c7a87872909ed07ebd481afc6fe47d711d7129a8fdbdd56ce2c0c8afbaabd1e11eb163311ddb0a88594f537d3c80271b853dac925b99fa9114287ebfb11b873bb96030994f9c9758b5a96965c339ae7ef97c2c11d7865fe0fea5a31c5e2a2b1cb4d45fd2af0081eb10cda581f703b3d483e653cbb6363a9a31d7ca7dae0665ce81805021f432395627b50f852aaaf43a8cd432c1a1a463a793bef4bef1015df442474d1719df2a769f73eee22f341e14fe2db5525ed86df3cbff329ca33f228e8253073053ed8488f445b32848ab4751e4cc11963bb041fa557ae8c4db7e27100bac59e8c1c64c9b777a416c5dc88ec6a1bc1f13a4392009fb6ee565eda6aab0b1a831a9035d49bb10497dca74daa3efe9c3ff7a253ce7d2572a894997df188d855ce02e3acefdf5bd80fc217cfc56ee3e5eda231d291c815b130b12c281d101712f18a9afb8b9d2339b2294e262a76367e84da25982c6ae81b5b75892b7264802860952e98d5754e400c5a776ddc15b9c16d57ceb173a49b693e5f52fb0344e626dcc6353d06059b8ee8796c2389607fbf680a2d3f6b53875c38fbe1a672643b754deeeab7fc370525f76889503188f707c331eccd47b8df4e2f884028454ba10a92fd2b9845a329aab8d2f769c89d8abf053d897826cbfe94faa326b7535fb5a5eabcb07fb74033fc84e955271ba73ac8d2cb6999e68cf5df730f08d020b34b2ad6fbb91ee0b5241ca47d294131ca7a1724e35b36cdd007f8009173236af0963554fbdeb806f12f5a03d82339590ab666db5ff546b3469b7e38117d3c5856b276dfe50325f930f886a58ab7d897dc21851173e6cd72e98445e628a44fcc6dec0b18cd434321eded229c66970ce1f75950dbcea1ff27d829d6219b4c8f3425a883ef609520f56f8141ddbffb08f33ebacb078ba27aa3652c145996c00d1d1d973d83053a81960955aa4809fd7f3bbb9afc95d72f1d3bad543ba9baef3214cd26e048d3c31f3af18512183c30b9"}, + {"0000000000000000000000000000000000000000000000000000000000000ad8", "00366b8f109695c55333509096f3c8c1e29418145a32ab8a66bce2b3a7615757976eef5f5fb7e6be4a290fd5d18f73c84557b91053e8b6d5eb69acf37818333e693218e851acf4a5340a7ee063ff9321b8fed39f003c363bd6a8c363e1202359ebf3d9a514999600901003177a4ae17d9bd9a052c0685e9b6aabb4376e5605040b4e8901b78b6cbc165338585b4ad398dd6aee2e679bb04d8da856736194e1bb68d435ef15501ca2017a6256b1824e3905a510a34e592ceca8a37b579c3db3a208662faba7be4c642e7f39f16da2edffd7ea33fd766fcde5b2ddfe13364f1dde862a554776ddb4540e845bb3d9684ffff73a04c0d2a43bb41b7feea110d8f272c5a1ae0d545ea8c0b8ee8c9e440cb69a7d50c3dd78ca6a765b763b66ee9bbd6e6379f69e62cb117b4b5b7025cc5d36b4b2aba33c738e8919b606251821dfd423060f698c2a03b636f20cfd1d8bfc27ae01940ef3b2202271db504932f9d49a6f625fbd5eba0978a91681cd2a070fa9b151da2ffa617c08df98f5263f4f7f1e5535853ab626955ce86f75edc05f2bce2b8e132f3de37b47427ad6c8917bad9efd8d3eab9203ebfcbfe1e81e99481b713e5f9fb8d53670ec12f72b070b4bdbfc25adfef1c4456d61f57d3002b454f6228c0f25804a94a3bdc2d7bc1f7cd762471c76fa922429a7eaddf1c055a518544c66d1bdce7ee3d888b00507b5ef79d5c4e9002023c30c263f5a90d3b565301abbc4af2d2004afc73a998562d8a08b22f11fdc1012d1b1bba10547e4ff0d4452046cbef985439c115c2b2e03be55ec55d9b0f2e6e532e90eb34ccd1ec780060818b6e883ad709dbda122ab7c52d6ab29f64ffc2704d41e55e71f2fa0bfc2f481cbef5556df72e4ca0723f14c880444e49e9d88260151a66f1bc41a8d3c12a0c2fdf972fd75f367f179db1bd00964a47ca1b602b5f1be2ad6290fa322e0cf8c8bf77c3ab36adf4116b766a5140acae7f89c4282e23d1d2996d0f7e63e16eb59e1d5dda73f9d2b5464efb4e9b7a1cb9d95d157f1b4644a9a5d90f17d85f918ba50aeaefb96200502bca8cd738816429e6a34885aadf99ad856b8d38e03cf48943242771f2acae068c7e4cc9c2c919d96c60a5f778509230439852b13a15442dbd217ef9864360efc888e8adc754f3d3752e3fb5f65ecc298cb9cb302c7a36df502cddc846674789bb76d8e2d49bedd99196bce65c41c5beb5a9b64a3aaf3863e26d7b55af10e4221a1ff4437b47f2c1560123dca21d85ed61e261e6809b1c06a3e73ba52d36afbdca3f96c135a427908ea6b3f6049671fbcb3e1412c9ab879bb2797f3fb44033cd8dbdc6bbefb0309de6be914f6c8121ec36b0dfab72feed696dd1425037b933eef48eaae55331233d7caba161e44d8f49336f078c33f21be44b354da042259012a6575b13f712180ebf534f20a80930f0b33beb530b0584647bdfc344f21c64d5eadf7dd0af61889fbc6f2c76cebaac4f5026bd0015f4d1a5fa0d9233bc21535902a8e8cb912f322e73f08d96c1a69110e58cf6b80202183b70a4167a41fd291698b19ce98106f84d167ccb69b733eb5fa2db8941a4b959a14cf12669f1832263fe1b213c29e4839f919a925145e9d229a9d5412f406fdcd68f42b07b0e68e04bf93fb2604b4dbcc20ce4684e7fa11e1d7fc49d16787db629a439156c1aeaa28fbab019b55d9647df30be81f9f28076cc12741356cf5b992a2a3319b3621450d1ff3ab13d632d121547df9454a44b769b29f62fdcbdfbbaf0bd75f18621e43b7c8d6c4d04f30bf933a7e7c5e2c171225d83556e0314e6972607fe65e1ca2b0511b8222bc9c1ceae5d7b79340a3b9071e8fd530158c164a2e5aee5741d3acdd2ac163bc716438a9d10ccedd91"}, + {"00000000000000000000000000000000000000000000000000000000000003b3", "00b8c0bba0c9c897e3f42236a72d3d159d817f61472defb97ea1ec43e5e256b7c551e39dee023677c18812c290a23c1b15830ca511e8ec7f4a3535a8fabdaa30d2173d89a8026dbe43a5000e42e14ec17c3aa3b6015eac61ca18561928f11333a03226f5b33f1433f92f151ae50dda780ffa131636dcd2705e4dce98c8f103a4210c6bc8911124c914d6b131ca7a28b03e39e6182fd4a9a69b083b07af87190c433d03dff75fafc90884f760ba1538b3b36473fb2cee9b4f80a2ddce673bb67dde1f279b7de4fa791d88de2d7f70ab7d74811512f2f400196ce1cafad2e451c6757e9dc5d7dacf32f35cd6dd560874d1124491676487f5312aeb508f08e125dc06525ab0e77064d0a7da2f93d0541eb23c168dee7fc687a8d85dfef641547b0baab2cbbba6a809436479fccf8651f957630a8f4178d4e4ba8b4b51395b92d37c5cbcaf5bca35f1143b3aae6fba1d07be0176550fdc44f5af09a0031bb0d3067fcca35ff9e00d291733931f349b7f39c41bc7f28d7f89477d052802318b9c799bbd5dedea26f2efc3f7ba4a4435d66606e884787d157b0cfa3c69548adf8e92b46eb6738b02190223d551449145f91684aaea3566276d9696e43ab267deb5d873eb985645721fda0966bab09ccc5f06b0c7303301ad85570d833a4d9ab4c12bfdf2f293343e99bfcd212a2b9dbeb439e2e89a09b8b41a8d3504b7d752389a401b06cdf48bde2aaebd80915a85b82c1dbc7a6e4ecd356f2032f053f9fd4e21ac57818d0f7f9e2233c56f223fa98585a75e0a0de4a07f3a551a1eed947c8b077b0d1a787ceac6b46e30c033e58013e055c4660e19e1bc31caaa7f5df1f7542a5e59dc3e5687346b142b81b122e8858d526b6ea67a9f7d942d654d9bb0ae82778e9d878fbbc964fa094cd302e94bc6ff70c37c36bfff3ad52bd6bc79098c52af9c1700cfe6b1d38a0a4e879e73bb7f22cee26263547ecd5a8ec5ded919ec0191d126e5917d3bcada24380f08074e64e98af89413f11490f704ffb3a074d851ee580d77e32ff6d1e29fbd11274c0575ee1614a5b67cf2027e1a04bd6fcd1de14ba40945e610922b6a3a73ad0340383168ccbb4b4069f13264fda7bcd9a4cad0001e570c8b58cae235a8d275bfae438049c50e8f1bf123f8d76ce5703f3bb2d9e5a50a517ef74b6d1f5da600e9b471d30e85177ebc1a5ac57c554720b7dabe64055132c80e391efdffd6110ae17e5d9dab22ddd13906c5007c2ff0d45bcd7e042e495edad9d6555a58162712f56102ec7f3f6ff1c72f74f12336a24837cd320fb62ebdf68b5e43719603a1d868efe55c5f9140dd36124462f093af3da445b6a2194dadafb4b2de9cdb1ddbac4371484707069e0468e7f28c961f7c7142741fb6492b19dceabbfdd013220f7920b28a76355ece01bf461ed5027bced2e2c1a279c1caf57fa5920e0b4bb093a8105bd88bb8bde721a4c5ff8e8e3a95e741029be844db55fbe5a77bc4ce02ba5171eb3f0ff5490344e74842cda6454b35f04fc227d444a80a6e02d105a9247a30c2253c4bc6e0c258776c9846a89d301f57df0ce2ff163419dd1127bcae65621ec8689e6d410f940d85e4c86a60f1c731e21bacfe1e6c9c7d6e3f2a3869b45d8e4c7308f43316dbe8d1c743fe3c00af031799f39d5a92e9dff4b6cc9368358675b916b09b0ad092473e2118f71c6f59fde852cb739ca15e024c1d5524bb9c20754fea0a428d8556a2d311fdb8bfa5274e3cd39ad036515763628adf1a2a8d2a1dea896a06f2498143211ecb4db7829c043891b1abadefa532216133c76198fc590178a36099b02854ff731185db07b0c7ba4f0334f998fc20c081c95b28ee83b36694365fd7e13d1855f75ff3146bd4a8003941ac513c76"}, + {"0000000000000000000000000000000000000000000000000000000000000728", "028a9e3025d1b4412a3914f6ddfa9551a2ec7b53402350cb9a1c5843b326846964844f9cd2761ab8190d0ac01aa816d0424f63925148e7f55b4a10feba0dec1630d2c39fdcc99dac4a71f151a33d4e8711b948450caedea304b76565bdda31af8035a8d10c452a943e1c1fb69cfd89789f1da5a3506e6ffbecf894517b9615aa935ff31e9027745f4184e03eb614a3d076b5c124e9f37c841bb14d369a746fd0320bf541adaed4ac02c566fb0fe10c315acc112b211720aadc59d872552dc6bb749cd9c9c51b860763305c857203c19ba4721c28bc764b9aa81dcaf5024c0d17bc09af2213c9be66011baf495bcd13750e5934f6d4398aa0c81ae5761ac314e5b28fdba379d6c1e7c49460158d4635405a52879b47e09fa363245b0c23ea6485e314339e8e801fbccc7414f67777c6b1c456995ca71b7de71d4e85515ba5b4d5f2faa79df40abfaad9be4f758c3d2cd50295f018256191e369b10262951e2ac0a6054cb96e06f0cd69f144c1304527657fb9e68c55eb9050ecec1c6704b48d9ea9e920cf237c38dfe7454db83db4832fbdfe7496207ec3261d74cfa6b3b7727984dff21c10170bffc2cb6cf1924523e01575ffcd6dfeaf3e0710ae5cc224513fc4c74ca71c17eed741fb4f3b6429235d6e627cf587fbc3b1a30f8a59504500543d323a4836aedf0e52c7a777de150017ea3c622255727a80055ff22351c15b2bc72f673a4f4a913a0e9cbfd503087af234b3f2b027fd3d535f5fdb6efd89fdbe2a0c0e3dd48e17df4877732e3173553ae450f690170f2937579c17018fa21574b2b7efdbca81631a80bd08f105c4677171d82ee32373591df156f902fa25ff6b601abf3b1f4ba37009f34132147a388f5e487199e7720ae3fa61fb5ca69af75241cdbf37da191b7c0f3c1689557648beb38e91e893692f5f69a966617bbe9da50330fda891c6323c6d3cf0dbfde90e8881c308c67c17fe7fc6260c5339d3ef72279fd18de931b85e56e303664d014297b6db0dd331fdbbbeda21b6e494afed08f4f20fd21496fd643df18e91697dad9e6c9a464609969d8acf2e7b4d8f93711a83f89f38f9b0e86e9e17f5f10e5e9bf948fff5943679e6f179480a1c10610e585e5e2fc75f22a8a47380633b7128f4afb073f128b6cda6ac0aa67b366605deceb98e79ba1e969fce06d2ace7166f834989091180332068696a6f91e3644c72aec9c95e6629cd06a6ff8b6d28d6bb4d9bc0111e760793f88dbb30e6c3335d40bcb01d49088eb3132b206e67784b2f5e9ae2055ae9eac63e97af9c8ed2195a44b44b4a62ff9818f4aa43736955b1a371f7e37063dfc0fae29edf4c153b5866690f9b5cbe9c97a82ec6056943dba4ff45ee26e330dc224ee7f15aeb9341d11cdf20d1834695487510715df87209743d8ba903dd732a2fc60c4879b601b74758aa4d17b2f22dc4151cfaf94f8802150ccfc390e62286c5e28e9fd7890967596503f506cfef88f1c16f1577c1314a6dbf4f1386292195e5972d3576d27b82b7fc0b46c45a5b8d043e041ca3d6978f579cd215807f185cafbaaa06e03d49c44900e109cb130cd564e8e357fff364dff51443891dda0f519d10fc70874f13d44693130f59203b45d59b88bc6c20c7744cd49884ffb301959e8ee6c7053bd926260493ed6379859af5ffcf119fc1763bcb0c845f3ab1385625d9f4468251ffca2ebe0afcdb66368b15417898ec3dcfd6d3a2d949d56e3037f5cb6378576deca7e856e1749e4a0d747d2c4e9f5f968f5c0ba4e1fb25ca3753746d07803d5993f60b55bed9c5599525c1f8618c0ba411b9d174f2448f4ded5e205d0ebe747551c680fe775ce1442dff1c2d6c6b9791232c3a2eae57101a29006be97321f162b6ad74df656b"}, + {"00000000000000000000000000000000000000000000000000000000000022cc", "00685ddb1f521327b6abf189a5158a4575b72cde210d4a59245edd28715d3e753dd544b81dda6b9b67f2019c5dc24251eed9c903319b3356b430806d55b16808d35ffeb0e56fa531b0f15b1314ae84cedf39d1c902adb2058a894a8102021203dc44c0f29e1838b5602672269b56de2d16f85562684c4aebccdaea4b5acd0c3e7ff98abc931bf9ed74032042b7aa8ff319b86e39ee863b1e58cf1affa814f65d63444d829fbf07c7019bfba89a184f5aea9e97f88c407b0b010578d9af498c1ef3033b997be91745d60f74bade12c6787098070c32aba8098a7148ba91381fe5602498f62e20e64ca29317fc5cbf89b58dd585da6ecf45b8ed572d1102a2a0978d6779279af2807310be50a514eb1abaf91fb15445d84a75ae791ec2c79ed5c3568ba79d9f2c0cd48c7a3304e188cfd9f41fbbaee95d9e3996702f1303f5faa4d3ce42f28106a9086317ddd225b0b471008400e81f5b912dda2b352869e6399563428c063e144be4a22be1c50ff98523affb5b0f9501b25659f20c2639d1e54b0b9bad96e30a3a7952c4f53a9c56c32a3ffcf2238aa3bcf57b0835ad59200a5cb5fd95d5016493cfc00f0ac1c3a3a086397ce3c4e1598efaac02f9b7d979c1ede65797b260437136bb47907a577a0ebdb14d751534e9bd0637304944ee8fc839df44703d6cafd14eb00c83b0ec94dcdfe60929711ad39509064a09cbbfcdf35d6a57d63025c91a2293ae9d5bb41afa970e11eed59bc93c97a84041af56cda01c4b0108a94d99e14fdaa71fc03161bb4f59f0c4dc18caa215274961d3528e39cde0bc2a167ad29f27fabdb55b077761879f316a41db7ea2039e3ec7b94679edce833b70aec21253fdbf4bedb55b7354be798c18f077b313a600d0bf4f7a13d5a5a266ca35b698bc7d767fa154beffd4ce29ee399cca06ddf5bb40b6be641d044c00e42b7dba4a0f265afc2043ef05e7b07dc747b4fb08655d3ea29e46e7de4202497c67ebbd89d4f7a82715ae722a669038969420120537c032918d8ff49e37843d1f2ba73343b9a1a70e23c6fc9b5f8a6d3d86f003e869cc69506e8f51a7f0add4db277daa08b459301f80ac7d106e93c1ca07225f6e77c2e92fad52caff0556fe7b8483470105dec5a549da7a3ac398f670290bb0b97280edf13f8c5be284ce2c9d5ee682fb4e99034ba38ca9a2aca19415f1dfedc0e5907b5fb770010981c214628d09f9626f149d6b30893145bece1bb90fc462db8151f02da36f21989d621cedbb99ce23b32247596928168a4f72318738d27b329641cc7398050bc2d33ffa31b9bbd00462c4bfef52d1c18ef3b6e6634d6c72cc5ee54bc3d7d7a4895ce22e651394271f1159d6855c0c2de9ffdc870ffddcc722063438de9620df89b1f59966eeff04991ae8538c7278a1b62363016be01ae9d2e9a9fa97812f522ae8417a2a7667f02d7f54f82327f74d7e5f48d148505d9240409644d708cc757719e1d6852e4d718fb199b6f0d02aeab5750a3720fc826c5803da0ef2823dfefad8e24eea4eda06b0519aedd81e47745744a6a8af19e559e5ef6db73a323333128f2a578227a4c8eada4b6217ee9449f315c610bd3d6288bb5668e74e3ebae55208541150001eec95c25cda16abb08262fa9a7e0afadeb35b185301af6b3b93f14243e4e647b383da13760eefd6822d2439199b39cfd7c1020ff4214ff0c02b2c58bfd5b90d178dc60f8b640d6f637208d92f96391b830d989318f7e63b3436b647f80f9416bf6832d117f49bd4310ff4b14d126853775e3db251da9cffca1df09f88cb18bb47646297e461fcafc4f556bd257a68a814e7a832c10f80792cc797cf6114e819561063328d7da7583b2bb761c512edfcbcf1f6b81b47a0da14e79ed433"}, + {"0000000000000000000000000000000000000000000000000000000000000547", "00150450ad5048eaa719525e4920f869308a77de840a748122a990c028f42484a6b3aa51b6757e3eb7ad02a810a7c7dbf48eec9f91962ae40f29dd413efef16e95cc950a38b9c5c7510b8bdf654f9f03617eb0141b0588fa7e4be58744ce3595c0eaf1033d0abe64d31d4376a93d52650d9c0b56a2dd47fb61cb44112c4a1c3b9a73232d7689ddee841857e068ed4052cfde4a23ec0c3f73de7fb5238c37904b44612b2faabe36930074a25a5957d1f9caa6c10adbec46b65cc918321c1783827320061b70dc76a49c556adcd53052ea1026078f2b6bd0176e53ce11a1183fc573d0b20e95132d4fcc4ecb45f1b8abf4a08751baee3de5f33c3b7150092b450ab5c6d9c34a1488a68848ca968f021c06d64edf7f39d31bc08d27d579b117eed596ae9c37006b25d009d2369d71651d80a27a64eb497195cd144e1f27d18a7c16e2c2499c4d33ac91b07481a086328f63005d3905d806d47f039c702969cb3918798fd41b1e5a29c79a37e3a1abeaf91b1390e039cb1c3d9fd79d1549b655b2d0eb75fe6f6176d1fc08e96464b791221bbe5e4d1349e6d7d3f14800967aea1b4d0efd46b405dcf4c2b8dba259a57672380d7b0e8dfa91d9b7ed0d9acce599cf8f710b4431c4aa8f4dee4f46d3610b0fe54aa91425597b6e7fd57055af22a1af1abe940878c57c28582b96a78675db41e1636d6356955cb75c01eaeac08ce8f619be0a183574789b2ef3917a73b023b39d06c62256af43f9059093d39746b2a1de4c31023b3953bf4f399f7eee91b74b94e958d0259c715539c2f6c265cf83b39074663d776f304e62ea1783fd01ff0d1a03e553fdd4da030b262b22850672b460790593f120da87523157d950b3ad85c4ad9c0e189c5203222d72b0f6429db5b6d7d59ac2d3b29a5155171e4b0955004eaa5c1bfa0769dbb8d7403aa28b168959004340986067a57fae43a27ed0c49c0a0dcab32bb202b17cc908f81d1bf191c45fb56717812c8cd68f9c0329caeddac78cf3b45462f26158e17664c334f8bf50b6671f4f6e88b7831d2527e14794ce66e1568a7b0e4e95a0d24f2e98ccfaf8b205f64422d53219fcee21902571549863f36e5c12d40841c66fc1c0ffaa2b101b53f182baa2c3e406c7d092632cda2388152e39118a2aa04dc8bf18e73ef1b4d3e12f45585057b1c30748c5b7478baa54b6d3f1618836f37906fefd6152119bc963aa44d9afd6fc4242c494fedda33c93216e08ba38770c550474bc8a322d92445d38f24ab9930a15780b6fa7ec0f95f4c988000841e3fa72255461a40cc991760ca169fda4cfa3b24fa2ea55e85834970914b3a90e725b7b116b72026699ad011d0d89d4df5511ab11f0f5216da3e13bd571f334c09163e7d76c1a2229a9adce8a549d08e592d2b37b1181433c10721101fd841ec5079cfbe086c0b37f090349262550d8d6104841abf1c434273370429d953bea8112395f626f0252f7380ce0bb37342e7921f4e6a272f2539eb67b2ac1fb75711d00e55370e68cb3c031fa7d9a9696430ab811d84343f51dd12771cf3d72d290c63d7a62f419b4f7d2fb753057beccc1f083372a60b66df279c50ef691c3ec5cd6b93256a1a53eaa3c1cbed0cb4b8237db5af97393c94d9636e5074ec5661ad4803809ca0772cff53fa333495a86653c1377ae4f1dedbe835d0aee8ac8c169abcfa17d554d16dfab2a4153dae1740a8b6bec8442cff357a170fe89c6bb2dd851bd0d39219c2b88b9ed1bf58cd2868e7cceb1579797de22a910209370db957b2f4351f33017bae7dd1c668d968a387b0d1ef89f1b0baeeac616503b715a00ba30a3823120a6faaadbf0457898938ce373239defab589c0049520476d8aa8bebbe4fd72c5ae4589e061b546874"}, + {"0000000000000000000000000000000000000000000000000000000000000483", "0094a2bccd4dd6b9718d843db64f86fdce01f65ba317b7a27f63dabd953c2ca19a6cb9328e1d73b769260978805111d3e135fc34a1f8cbc42b957b161babea254d87450d993dd9c53b042743c98f352af27149fb13ed7dd32c596b8db803541307cb1252a03fb58fa81b83fca9d214b63be31df1e7d0965061f0927de83e1792edf669d05515ca6fe7b88ce001f29720f6a0fc1f45bd2b1e08dd34655dec06f672b2db39ff1c6a8f01b8df7401857891c971306436de3a3a5d773f82901071b556025c95e9a640e3b80ee4efa10930311f060c79e8be35b5c395efb8823e63491710d6b932e9551898063614b803b5f6f832efe71c6acdd1a9749c0e041c9afd599250efec533060f62edb5cbd2a5dbd5816f8ace96e5d5cbfca3eb44b7c4d2d9ac7e5785bf6100be51d61e00a29867f3a3944e98f4788225e41f22a380fc61a19415f88e365a2e66193323b169910d001ebc096215c846ba64194b87445d455b5722f1c050a09bb70925f7a55ce76a3822fb80d1a22e7bc9cea13bdb6019ad9551ae3c21325bc3781d1cc2d33da0720819312687c1ce3fb9e43a395ee77f205acd6911706258ca258d543af2a3a049ad75db632d0f797bee62fd53de03ca8c2256761a65d6dfb505206fa9c690806f452ea4817caf4c34fe1ca4dec9dbc8e073f916e1964e5688d150c7cc0db0374b12db6f9bb3afb5e2e059217e568966b22e2b1819df98e0270f6e0da8bca3b2f9fa282966b8f2d7d27e9f157972ec8e33989de0bdc999e698d3b7fe4e6d11f66be84c8cda0763b6329b9f9d1fae7cd27a864080a21c6a40ab3f0f7ff880f7c443b04f879dbf3c5a2daab9d1d150a5574bba60fa162f61a0524e30c9490ff88c7ccd8dc32d1f36d13b45414cb0e6f60e05662ed54de8552fae2fd9f4038dffe69e3d204baa2d7439489f1ac461e345c5e8f046378922049f423a5c084a7fdc4c0b23b22dd5dd6148cb627a18605f19cbf748122a656a55d439ecdcd295db303c797e1d99bc174441dca8c2355a5fd3a1538cb3371d51685fb8ce2e7567c790fda592756f95514b0b557532423cfe50c8bbe3ffe77ab398d5eb7a057600af630a3c827f6fb55d24f32fe8aeb36bb2b0c1bf8918046133305c7e59521df4150f7bbea1f308f4f3dcc2c10ddc141d295766188e3c349ddbfde44f1061f70856e8713b923d8cbc56e7371b3ef2f7f9d2f4a81f56401a56595ca046ba26b7fc16335d87a6fff2977dbc43cdf53fd074c029a72c7270d9522d1f7da6a931792faadde6dcc12f99d58eb82f66d3959ae150e6c4593488411de336938d099f6bc6b66247cefbb2d544c42fa9f876bde085771a5f5f7f33fbc3aa8061285e31d7716dbc0b9cb2188ca43ac4a6ba4392319427125488d3306419d86172059d2fd4e3dfc92c3e705bc81dc8e46ea27d671d508a17c26f62776d3595b28c397fd9ad1ec72bef54342ab7708623d9a75fca311f65a8e711a0307f5dcc23be44b215e3ba51377fc19f25950fc65b3677917b36bdaf0e4b2624e14980a07c753d7da886e9d1a8c81d26e6055c0d680ac932349945c77756273afcefd680fd5679c52c858198c0c1d3c55540e5ebaa9b1ffa1db0711bfef16495edebb25289ec88a59bff79a788415352bc0a228ab16e89f0e97930a30df0113d5c811b99a3735dccdfc6ec44a240c0f04094d73c9b909232973c57d79d506fffbd2321011ecd35356db7f156759b36fa6da3b9b3eed4c7bcce5dcd3a2b74335e187c7decd494e642c7c694212c3e614869012199a07a68a2cbd465e0c72525c51361b7c363e904293e6ca48ae97cd99ba3a4bb6be6621d15cece7d7629dff2f14addb5d7aefe1427f79be291534c511210687373f057541352cae2357d89ec9"}, + {"0000000000000000000000000000000000000000000000000000000000000523", "004168265f6724295a44579ca27b6c528e3a98af860998cb3367d28ebfee57a3367eccf8d5fa8c53a3c6015674e373d14515fe0df45a0656f9f98b539a64105729aae40ea242c17775257ba1ffb4fa7553796efc07b72edef6587bf3af64618f0b96536e37cbf4eb4c0b9913ba758aa3ba6422354af459f1fda3fb77c8a02ba7fe152b0ea337263eb3b89f37439e088874c2244c80bc754a1daa8d6639376052e52b7de6081b552c023163975b6c2515dd1b918556b17ae06f5adf229c4c40e28071167c8587236693eef6ad11a5d16e84ea0d43ea06d9085693301070d6f97229e51d5d79716b126ee2b3fb178eebf00f3307cd5483853ebedd47a309a973e2a5c9a5d2a06ef52045dc881e9f311a76a02dc62e2e5e98aa58cc7ae31899e95a9b66059d82af0a60db6b59cefcd2dc608155052fb1028cb07e519e0e0ee7fb7a17598143ddc14a284819d063f02c0c73004a26915121512fa7b521e7ce6e5ae0e2bcecf9b014bb612e86865810418f827ba7cd37696de574d81e181e8c7e4b496c1afa00044d41585d36f8577c0c782949ab8337600bd5adb206df334c37abdbd3df6afe033d78523fc54f4cc0d3f6d73e71bb89e0e93eceea3db5038ede9b0f19fb54d5b9c57a13b222c39fc332035216dea869342186a3210366f544fae32cb7d692322abbd0f61f4c05f7dd756633ece61e609096d81302cbed3212d45309433ad383d538e879e3b84fe5181085b1232dcfd131e36c2367475c3b6cdf415aca2623f2d51a359b49ff8626649e0bee716f07b4fb43b838cf1e701a240d93ca51b390c672d7b531f2500c3b0ff19ba1da58103ddfdbc48e93bf9a3573683154602309f4efa99af5b50b5413eb71feb8fdd9b5f7a86115cf3ae28b0d3a1acce2445e98290e73eb803fe7ae25091fd8188fd94eefc7972fc3f2b6efd1943f998b01dc789c2d8bb47ded7b6602f24f0932df443c82bc59c1fb7fc858893505f6275b35bb08ce46cedc80231b76653c6b12133dd05312c626abeebd3933d8a66d31fdaec9bdaa5a91d8a2593189ef0aab5ad77c64440a29ddc91513119f71a3c4dce2d80c91ec735567470d22b3abef0574031e375205d5d20f5985bc9056900d4c7ae608cd227331b8c1f98fafa56d2a2efc267c16e9e2ac43adcd0bdd29467458615dbe18dfb7f1310876ae1ccd920dd0a39af49e0ecd9ad987703e68f76557eb5386e3cd612172f83d29502422b07e7e34aa0a7e5185772908b1991fe34b5b3391ddd6fed1c598147728e2284abb67ce9843d3eaa7973d02cdcf9479171f25976cad1127bafa6300412ffef955cfb4c666196f9dd2d8adc0edcab4a2e8fac618f4ccf79269d920171d4b4927ff9b8ebad2a13d17393261b27dbf462e98fac5bcec4a2fe7f9e9265cd33bce666b5cf904057ae50c6010d868d04afa2de3d2bbf6b7c9578e7c1bf7f533f7c78785ca75e636ed7cfd6df4e43070ad06d306fac607f26be51aa1a7d6ee01a5803fb993560b0e59e662dc3703c572474db1d2f35a0008d58231077e174e8a43331e56ab8392474450debf40fd2f482cda4b8bc02139670e5ad36b68c01b1548f6b2ef761ba5c17e0d6fbc33a78e21c812d7d43aea1c58524229d07d432ff6a175f0e3942d8cd767b14884f8483106f73b0355957b8f17da083600c8b2e2bb601762093df302150191b001ce37a47e87dca8faed9017ecc4101d210e30d338ccefd29102e0699e16148dbd4da828ff65882b67628949945602c3ba365a04345df1620e4ef19cc4a4a9474cf31135f2246f958cd3582bf064bb65b3f75c18719643f877d0cb050692157807e01bb7c69f72989ffb784016bdb4e134d2246bf5aef823e977a7e7586be1348316cae7fa96931e4fdb8917"}, + {"0000000000000000000000000000000000000000000000000000000000000edf", "00488b0b4f619277300cb0166cc02f908c9b55623f27b94bf9a7d3858dc6f108d6e154fc9e7fe35a0c6418d78747511d324962d6c88686e6ff57a9729da7f92bf0ccbee86c7b959ee3a2fd629f1a454ecd6a972b06634ca69406ebefa6e5f2d66ed06de903b43ba6de336d99b7d6f19531ac58e6ce713fe421b4c9f8813f077dca472b696191dc19b3c914b7fb411185d379c73c812caf33b4a215da7cc64186792d064fe7fe3ff300f1e10c05407a2f4f8d31556a0ee5b9ca00bfc1cf0265c8ad4a0eee3bdc11f1871ee266574d42ddbdcb36a1735877681333830e0591cad79e63505b9d44075a4cac4831b9e78bd5e9c941f2d5179706c2defd9804ea694f6dca8bff7aedf3a66636e35f63c29e798920abfe43cc97bff2ec831356007689d0d8ff1c0273139f4c5ccce52d35e2d6e47798da9b0a036fb411fb17133e2ee01e2243acd043a541c45ab8f3e6d5a68d016cb925830e974d845021cdc44fabf65e73d814a8064d0cac336066fbeb10c33527597e8165408c14b401d63a56bf44dae4ef9041b01b5362b657a27fe0c60f3ce7a9edc83be8ba73c64711e38e4e9413f56f110abe62eff5d71c73d43ab24ff7fddb4e6566d6f22011b3f50c43d37e66dc71e2a63af67bf55e74bb9f2719cd4efc724aea43cfd5c478eb506a993bf5f07dc9248ba9e1c195b4f8b31bf4436c3e3ef1a31c7389ba06dd6cbddc8f275c7e8aa890e25e62562517f9259a25390accdd6ee4779852067c64d85ace462ede0c2311ed00fe9bcc0860633e646f7fd4af0b55cfbaca74313f3611665b078f3ac7e60982b989b5d9059ff87f06ee0868b6d729031ddf8818dc478632cbac9fb5782a0184198693255311d81458bc35f6c27006fddfd40cb01939d8841c195c65a10d933746047e40af22100f808a2919d87be72d79e8ea225c78c6eb455a28c1007f9bc76397b8e6dfe0f23294746c3e868ffb6c1f05520b89d3c9e3f2c93919d57671e1f6987a959f6c18db53a1ef8c299795050301be4be6c99e2e7f278b33ce16eea8e11a73d85e359e206f22e56fb47b8d740d7f7af44a4593a3a407115e4dc772e590645cfd5219fb3ed68ca53d8db817864fbbe768dda7491f0a7b162e9a9bc2376171d79a61a0dead62b55540abfd2e2395219dbc4cbc1673a832a1c6e2043134ce3bd3e80342b347d60d834bc29d508e23e3d2ca9e4e362ef110c8d2eb89cfae83175752a64a78f28adaef5ff0aa0968ab5b3a291e0f535e7231739534c5536fb12004355817b95c52555d6e18a974205d6c2f1b93fd3f2507cadc11740877e54adb56251fee367a73c6195074106fa68374d501036abdd34d6031f4a57db72d4e8208453323152eec59f506332e46f8e8d54b5fbc720a0c3ed353e30e4933f3ef685480652b668013d8cb4a05238b2335ec1e11627722050ff2e671815e336e52146413b053e71d19588462bc4e97e6f5f44fd6420607a2823e336ef909da59d22d27f79c36a38df7ce0545d5a73ad1947d390a97b5576f6070561753b3e2df0aa1dcb574c55e2b8ed3e0e0e4ec3e79a77cfc91f82efaaccafb4d146be56984d38d67a209868d30cf120b24cf04c555c7479605b7f6d5eb1cab346b3b3b0c40a472320b97d3392647e59cc4de3612cd63983b22095a57049a629443254c71207e53fc09653f31b8f40e635cef17cfb64b37b0d22c4441ea7593d615a465142592e6344bef7b1186a1ebe6373ec1a0819986eb18cfb49533e1bf3fda4f247f8eb153d7cfbd1ee98610315eaa2d054b4ebea05350e2fe6b7541429a45b432c10e07b86e117b86f4e3f7251fed4204d71073b135725e0f4f917103ddcaf463e9ca0f3d1eec999f3d62f9e4d0ee5e5b0763ed070cdf66b62ab787bfb9b4"}, + {"0000000000000000000000000000000000000000000000000000000000000277", "001009413583c088bcaa3416517245a52315ba878e06f0ac9f23209e3d6c6114de7167a0fd558e2b2fc30d1f9974936cd4a79611549ef47263aab79e79efbd114108f2480e2f49271e314b8318c3e8c531c8ca8d037fc79e8e192fcf4868b188056b556cee8d1a89e81ef76610ddd23670b53822f2662cef67a92e1dc428069273e57f255d1faba2e34aa7fef325932815e5b208998339bbc7eabdd46be6fe9ed3b686837078779b0064079cffcec933ce24a0504f5ebc386c1f4f79430a6b2eca04ca56e6b268a4104e625c57e177bf25020dd82f6099ed6295955a551930ab8283d83ddf15761b3221ee9bff35affbf7c91dffccc1070ee53d8b83055d015549d3f3bd5571218399cdad014b85522c360774ab93b4465012a67391afe23026cebde5d798e7059edbf1fa052111a147a4eb2a2e3519505db73a4735d66730b71a5237e5a647e1d8d65923ae8d1d9db500f9c3025e976a41d09bd063e4a7e6f2d9289ddfb20e53f76ec3636963e62d02130eb4cbe0cdedfad47c057c6628c70a497c6374e3d10ee9da7681475ff77c0b0b70711f0c34fd56bdf151c115599cc83ab7c0b619300542305ae2cb854113370f30883348e71acad34ce31fcddc5c4e87a139594303ce1d5ac4273956d01b300184661c34bb073ab1bb75a94f6cea7c12a2081f920730495a204b98df3356b9cf35cd119675700d0b22d88bf2231fa1fb8102bffe78a7e1a9e173662e2a9d8a3985d9ac7373b4d58795c6dd6e89c69d95361286a716d16362d1e66b247c7f727785a40bd31a6528afb58236e33acfd926e4bb29346aaa0b2dbbc39a0f7732e5fdaea8fd8823558674d1e105994177c9cd17d6cad34388a8e54b7c72aca8681ea91ad5f2bd2d17507522c85205acc62fa6d420eea247d70c1f526148e3b254d8d893c0e8fca7d2def9b6c2b23a595ebd005869313db0bef7d2de64f7907c2bd229889c4bf82058fb8fd70cd22360f78495767d021294b17ed71004b74ce8b2569b2f7dea90ffe32d78c6f2359de773248791d6dc11f0ab74dea8de254e4dca567c586432046cb52c5adbe161bdfca5a54361fe7225485a501b04a96d44060c622689b6245ddec72bc928efb9fd7a07e17ec9fd449c8dc1dcf1ed47bb29ace90ecbd674196b5c604a08f9eadbebb1dce3f0ef2f53f81bee0b04f905b870ddee750fb5b1555aeec5d96f647e075164345b3425dc3fb71bd87848876cd112de03f8695f366dc9def11c397d757de4d4af4bc72dd5bd5b7dd13fe35eb024117dc2f45de4f7a0dc3d56035ada057a08081c0ddc0fddc7a3a0b1fbdbb19010a0863821950dbb45aabd484d0d296db26684ee83bdf8505ab9601fee65f2a8d0a48ab9e4f22617253bc641171a75af71223ebd4b35739bad07678173c5056e216efd230c072eceecf248425b009d4612e0b243be092237aeb8626693212d26df2d9e760d1e4f7d430b5f645cdbe30b93c5bacb1033189ac6417616e7b22c6607722dbd31bd22190621aa0f5182137887ab06654a292bf62807aa49eb90c401b724fa83d37c73080d7170d704731391b7723aca3b1ff6fe85229547b15d58caac6b2b29aa03fb3110891b4235c52c6fc4dc23047d7f73e5392293d41d5b8a7d452c986400fff66afbfd5d691907e5c42176139faae70cc7cee748348b12c39bdb234fe10f6b2eaed9eff6224765b3ddec2a9a6db74a4a0c1b0aab73c34f6358c77906fc5137b37fb47cf232257553c4a9fd2bf3f3fc54159e72e7e14abfd29b800a8ba97f615a8757eb5ab8c155fe765b2e493c8d44624c94cb5befd2bfed4ab80e94d6a3f261ce15fb722fd51308e76f3151981c867a35fc979efd461ab0a93135c5d6d4a4abc78d3ce662dc3c49d29d92f8ce7b"}, + {"000000000000000000000000000000000000000000000000000000000000005f", "0033d78f74a4f55bc463d88cbc522f1a448397fbb2076e970b36e21c179d0803777d6378722197560aeb161b24bb8a5bfb23d220d2d32cea32d8f831dd5afc369ef6121eaaf19187e57551e846ef1753e6bb450e01713ac33a173f039dd6b27d1f2152dd54cf31c4c02393f554cc0cc2c9a40fb28d8972b893e8cd5f9c9b1d5d269e272450a36ebc25b6375c61460bdcb30300622975ea93d88eff6b4187322cca4d2dfb8737fa060277bbe5da53a6938252a031f8c9a688cdb234527e160acc1df38d62e12321a58cf9fdad1366db9c2ec20a81fa7338d2a049397591f15eab8f5ab1673e6c5613b41cb4af235857eeecb7020cc549ce667a99e8060c223497ea987dafcf2518894fe9fb1e70723a32e41c1a8546365adfc7038b724acfa3360ea6fb7be82e12c1cd23d9d23e991094a1569f4b18fca7ab3738242097a3f1cd5609d9ba4805b33f3e142a2bb2735547008d821b7361c7ab1ca36065dddf998222f51cfc8a32f1a9a3e712c814ec42e51501dae7d1cbd332592501f9e74481d90c09cc82f5644faca52f67eede10be04cf570081676281d144610935d900365613d3c0dc037529deb384de07e05f046e163db462be87df18242f3c72791ceff7efb74d355bcdd9ce2ddd9978a31508f2db0a227662b3db55f959044d3c7fd9d37f30122aed87b3f1530505572c0437665c710a4cf17419cd019bc7e486a549c7494950683d473da302621afb110a87885f4fc707d17466218379fce88cab577c826b11d30d7e4e2e168df484e38bd4e160c4f4ecd648b03e3dfda8d9704c9fc46164c8d1c48e369f1079d5480cf9a6f5a9c9967a95692295686bc7d75040fdc9e21d6da5861e8e18098038222fa048aaad8bc038604a105d4fd091688b63d5d14815c74ba45e86ebb8bd21402c02ea84a260672d3b25f17fd245b61a76d27e7d0109e6a31583ebb07c7de4185458a31290c7549d090ad63bf30447b98f06140188867ef7aa6d907e8c691207463d84a0abeb0a4d66595c5712bded921a935723aef5ec97544ccca41364dba24f86d1981239f5620c73506f841fea636046888f28f4c8ee588cf7733c1f972a75c15451efda62237e516790e54210bcb5ec170151f129d474232229c28d29637b1e2ae6b4f9c63548e399f0d160ef05bb63b0646b1c31c1c41592570197111d7685a019438b44aef7e74cc5a71bd497a51568ddf9ebc860518364a20697c20a8cb92ccf65c61a6a0a64ea2b3f03c967821fb338828f90c15cdd0b256a69f65d1791a1b33544ee6ac1812d9c17709e06111794ae81061732d10ed562a4d9f55b5e7cfc79182b59ade138cc273149d809f057f5960b50e11f64121334f97ee1536a65fb2a0828e05be03eba8ed5daa53dc5ee9e8d29c655fcbfc61cae4b233a0f2e12a1f2024380490b645fa59ee390532244e745ce27fde712145c78a34957208d5923d78f464ae04e12d973833a22cdeb9aa14d1cdaf1ec84c75ff89a214bab7d7b4e423fefc16c31165fe7291695e2b5188f08379e9d4f036d638d975c4de3df4ba1b3b68f7a22dae477b7f151688fda6a663525b55428eb07c8b93e507c14bdcc03f7a861d9e06935387430f93d0a5fcdd1b7758d0f0c6db4d0f9106e2dc3fc526f704cc1dd2d5fdf19ab06e9dc980718f423a3879169c2a127fc840314a3db10ebb27567b45cc5c3eff71c555bcf96137d1979851634028b07faa467fdaa037c672e2152ad41fecabf2f0d1e0a9ad74ea724077689aff65c6f1c0a5bd9261021f3514ba21901e1412296149b5d68e6dbf059c01cc712aac5b3fa4daabd62be2a5304ccf03eea9985128f6b92abdbbecf264aa1b5e966de66fe929e850b254ada6f24906bcff882b2e062ef4f960df2dbb972"}, + {"0000000000000000000000000000000000000000000000000000000000000812", "00461f6d26517c89507da33b4d5233f4e64cf24abd0bf549b88ae7d1f1a24e23fd4ffdce425e699d700e10c4d14adc10dd0aa9c8f6f1736e82a62fc79a6f7314405130ca09c99b6dd0715aec486eaceddada5d51035e0515cc98d02b5facec879669d58b39e8bff7043b7917b481aa9313687316225a435bf59d9df137e40b409c811a5ee3af1b6ca1bc768e810eb6ea7e065b0c2dd69c3e9e5af1cde05729a0dc926610c7bffba9058c5080dc41e3a96d332458a7df9692d1373fc39e22e9493c56d802e0cc53435df47f72c1d88e1dea6e0f5e7b47a78a836d6c8030fca4ff37453557d5e6585236ff446de08f55d261f5dd1dca4bd74baddd658210ecbe570f0620de63842800d6df1f024155bb8ca81c0ef651c4e0039562ab22a8184cf05bb5a19e679b13a03fd888338433b305d3424fee2831fa1d925cb124692dab735d60a386e04511425e66ea54a39a650602ce093d660c0fbc6359e145b17f5d09713fba6f5c1c4b01639a5eb84139ae98637e4379e667c4d43817058dbd75774ae2ec5ee411cc4bf89df5727b79f3221df0d28c51944521dd68a38e245b5e6e76799f29df03950e741758cd99185711c31d1c8ea08a59e7c39c214044ac45f80ab9f9483224539d4aeab8697c5baf07b389e70048755a7ff0239da15df5970cbbfb80041c6f0931348d74bb60b4743f4c6eda86841e35c3a903e03e858d8a5dad9854d06deefef218abc058fe6e871ca6474564baf13563e9f22afcfb234e1d1b54a835bc9c61faab63f9c9b52579cb49f27e5bc9f41cc8376ebb1704cf2ac7611aa6edac55b2c9fe2e352d3606af7328154e52958a05b08a847135709df80fdcfe092814269696290983e12413da29ff264bfad2a5b30efafdbadd474511ca17548d032e26baa0a1bb42e53f5a6d06b4eb8a616ed3b5fb12f56605b83ed8ac8000f43a62790668d4b88599c5587878d6c9853b394a06d04d7c4c93764b24b784fe79feb8f1c1c40e8cfd0eec8571f0281e73c7842385a14d0645e64cf299e5112ec27a5d52eaa6dc0f9209d62830688f1afa7b180ab30b2189d3171f52c280fd1dc6322d6c7a936a9021cfc79ab90b8dbdba611410ef3b6d62f6b3dfb0c61a25ac3dadd70ac110df231a4bda28f685bf9940e549c1fb22a9df0047c6c4a841e866bf5add5ebf845e01caa70da561fa0d90925072596f578c83b0bd03e61c5b834020b039a9f7afb4b0195181653a78af718b0d1b59321d0a881270c267a321e16fde9c93fc7c01357b92c0cc5651e53d0f84566c4cc279f8367b2fb504958ee48baa1f497455540a747d5b258025b727791de93f6eece7b7573f9f8555d5478bbdb1436db2af055ce467e9d30037923960c79547acac3a4aba750c0e56830442d9081b1948e2d1b32dc74dc36a91c0b802fdd60b49c7f9af5c8112d92ef8902a9370180fe74d9fde4369e6badf89a095710d3daa622266b49843062c6068799d772f13e9a0f9b11cd9295ad654f09e1eee7a2f8ce9b75fa11af2e002e037e27c2f7e7ce0048242893c4d5f8d571ba67f253d62723d853ab63b0e505f103adde1076e403387f4abb52e2c92f87edb13daa1201988e8bcb0c441cbfcc7b4cdee87911f631d5e4c2c264b00dceeebd7cca1d965f2e6c17ec8e71528929bb30de3d942ec39542de423a283853f5a8b18add1fa4e0d2fe1b697b8aa20663f0f32a3dc45be1d3fa3a2228e1017178285df87616e060a69da20882ee4ad8c309d57951f35844b7ee6169940780f3f5f26fab710dc1b14030c9462ca2fd63791dd18dc1dbb4ef26b746416fb9dd8661845dcd0282e214b53fa7542f7b578f4762db1d16904d8dc7233617d4fabedca793a5fbefa1e52592a6d0c9c2deb7d3b73c7fd4316"}, + {"0000000000000000000000000000000000000000000000000000000000001b3b", "0047b17bb0414d1b5deb37f1d7c162fb0a44bf642e1eebfe8f9447d513a49197c13c4b35b2a1b3ded96f0565b735a308200fbedb20f16bfc3b72327754255a1880e4b70806aecdac9bb23da53d357a3612fc5ed32b609bdbc8552d64fd768468053be91ef5751ae19057873ac0469eaa6d729e459cd2c72a41d5f7dcb285373dddce26de92d1624a3467ccf5ee12c6ab1e94859e211feaf42b1be3c5e929f5edddf8773062df2ec306f9ee51936cfac7a07bb15d006a9fb1b78a7872ba632eed756426bc8bdb423779bbd4e53efb871840bd249a4d2580a463c9c9d9b39f68c236d655157409b4331b74e8ff1f559503131388456995e51221fd90c91f1706888c087b3f0aeee583ddb88d22c5f8bcffc61fe5053ee94f721769a318878c7e9efe256555908129fd53afa325ce9def2172ab1073b0c5a27cb91c6c34451522dd95069ba04fc8237e57109e63bdf715b7019834156af0a303a6dce23e2be6814d49d877344e1ecf34878caf2ce3999354b783e5a506e2d65dbf3a0542b5c33268de85ed71e08fa0ee9e316741da64184a7bc7e9ebb32bb7c26c2b3bd5ef266ad104d79abd0448e450ef8703588eb770a3af1722cc98b651aaa828cf57e52b551beb727453cfdd2b0fdddd4233d22918be4faa53471c79aa21d1ca893eccbf487e9e67bb5b3715d90febfd6b8596892f627dc84f5e4d1d86820348445fa59f61bbb55cc415d13bc8f9f259f005b6037d62b6eeb09e01aac4f25fd5c2380a174b1fff08258acc90660b4fbaea7815d193433521e5207df95e2d23797b5a10c5774ae5834ac9f0a2153dcd6e8af605d715586302c42d4e2d03c08664c73540ac8dd6ce31d7ea8b22d8267b7b96079621f940a63eddfe44f5330977e55b51db4e9acc64fbc6b78cb7072d1db0794b91e2b4abdebff38ea60900bbd7a65795ecbed13d047516b81914d649c7d1b545c5fe7195d9a6f84c4e53c62fc44b3b056be8b7867df9538a21e6ae1576381930db774f763fefe27725e68238960196bf70c98165399e8b692b802bdd75279f12d3533b3dd43cb0b50f530175e32635b7c114e5950163036e1dad1dfe871a52b3a740d2cdd5b09f356eb4defd9ae4c218f94126eb25e4b1519f654581f4bc75fb704991e55d796438c71697d9e6aadd7ded4964ba6bbdcf6f1d7ee46b0b559beaca914b71501cc502617258eef0eb7cb99e49362612dc9a5853d12b549ad0d39a953c9638485115cdb1e42f1f908f16d6751af9518b21aa9814a15b16eb2d77cf998972eefbd46e187bfae67da95646080c7b02c32443e44a8f662610d0ee839265e557ce0017646bf075485bbcc43c61f7260fcd3f39d61cdceb2496d5b3399726faf72063f6a767f5e5cfc43f366826003cf34f73ba939e48d40c56479431c2fb9289f806d9aa83ba42df6a7c54a5b1cdeb2b166d6c5eb66a19064351a3edc0c98d8a5449c5c8162d9d6af470db18ad6923010e03ada185733d0c37fb3d0f72f2f4b440b5df33356948eda247ca88bd7cb372aeb7febe5e115c6dffee1adedeecf4344c98c726bd5dfe6c603135a27c0f1ce1d197cf3f637ef3258ac0ff434f0b25203b4aeb879c3d4bf7a053deaee3d414f7fbf4c5804ee82bf20ba4fbdfe5d5f521c7e17819ef057dd90a09ad6bc93151687dd5bba87cc9f39c4ed323bc9941144b8e7ebdd058435f8c41cdcc772f08e4ab0b76aa1c87f412ab4f90c5f7f142e4cd77a4713b217accbb2936633bba682a9949b464f58cd531e693d5bd4cc90d05494744ee8e9f90ee376c9ad6e865de6dbbd0a315b7862d594c956d6b2302137ea90226d098179644303aa742e52ee233883da52260cfca3ab38519480f3af93f220016ff91fbdaa544d0fa037b0362de3d28"}, + {"0000000000000000000000000000000000000000000000000000000000002324", "01aff257a2dc9edd0a0d746c6a6cef718c99b8369c06a3bec78c4214f38b4180bc582d01c337605b00581b1985dcfc5b9937405c6254504cc7b931c7de64d42139418b928b87c47c1cd6f4ba4eb3f7529f3d065c105694b36a865841ead3214063678b82dd3c9965f02c72757a10d55e32ea0c356ceb3daf660eb7f3af4d162c9df4ec78ac63c64d81b4e576120b7dadffa6c31dce3592f6edb9fbe1b2d340aba9532cd4cf66b6b0030b20bd12417aac6c0bd1442f1192a8f439d7ad2043afefc125e9f5dda794290145fff626f55d5942262417aee79f22d0fbd911a2cfde7f462108ea90cd6844a3f497cfd40f48a85e7493037ed0c9bb962f2f510389f68a139fb52165eb21e780290235c9dbbbb74e15c13639eb0bb488d8e39a637c63c1eec6d55db60503cac2ed44e15945cf85636027ac1b665162b853dd06810f95ba4fc34bab6a613027d9e505496978245502d59463be442ce3602ba4e1a2ebe2f1c06bbe82a51b2789a259077f0042d694cf66dd92c9e78bb60a5c033889716d09a1871be142c3f1b8d6824696740a12107bc55959d97ce171b243d4885e65a77c63dc95e90a6a150a77dd9f3fe04020e07db6b5f1ce78bdb4e3122d71095cd127ff9c18a2a0497990d153316ad26a1e4a7d2075cc8dbb57d1159103d4fa62642e37c65e200c5f2d2763d601fa00023b4153a4874e21fad80302e0523050aea40d9a396102f2f5726aa6bcf9f02a0ac599579f48552dbeaa00e01607bf0e4f7cd7341617cb8d857346c35051abca6703f4010eb4685b47d01949a2256c597e22ebefb2c6db7f0ecd98e31c1ed00cd93a3ede88e102af36a0e18f10d17913085f848d554a6ca5a798bf3b2a1256ae6d5accae8a16b9260e150d9c5b2d2e35efaeb74773aaf1f81a81275cd9c01a13cc7c888af68edf9e54a0e74ff67d4b29d29c5702277fe6605ab4c306f35a76485c1ed2b85c3fd3bb08d31271ed550d49bf2bc21414e11a790cd0b886c909fef20a016b3f53d29b549e852c4f91fb57584e1a0d1051fc1a3694dff82510df42c024acea9650123f033e2d23d380f525116f55d945e22f19e56c3773b953b6fdc0f19cd86bdfcd47990b4ac7ea01bbb4124c2144aa39a95f4b6b19cca33752fad7aa08465ae3732845bf911e2954335503928c539a70f112e779967b02794a8bd19378e6f7a327556e76badedfdeb7e4212016fe5c08287a3fd89ad75117e3eb6df4f9b158c01b8f1f710ec6e8b7e93477a3b17515fa93dc9f450c1fbcea3e71da4908d4b5b5a5bf50b3465a78f971a11a987e5ddf92bce776742ce77b7deb9f60d97eb455566b9efc95ec580bfb7286c80b57c6ee956c78ddaf209f03c82f141a23361fe3ea8467652607fbbf435b46e4eef200a60e8b5224f60a485f4266ed16fe9e3e02daf37beca9eb476615b111237491fc56b17d9b5e0fb7992184ccfea8c04114c47cd66161c9a32ec850031e3a0bd084a81edd9fb22bcd23b8e165235cc43608ddad66978f4d19224ca6576957e596ed4d3f1f900915f9c276cec19eebdcf0cc7d42a0dcca2aa77c5829a9e7519bcfe1fef935a7ae6651ecda22a99e28621494cc4c70e5304d5a43c69c8a726f5e177db89690289196c6c3972fdb7320133b61dc6c3e24d05b4dfc0ba5132cc845ce03e93c42d4b49eace225d93464be0dd4e6536ed960f0d51a22a1bf7411b1d596385ef1580b574ff85c8a03aff086b11c4dfe2b327e3c6c355a4432e984e04229a74259d762fd908e82d197c1ae0fdf6166091bc2daf975d2a06bd9b8b92796db62954dfabde727d6bea1dea6187f3252b776d6f6fc113c265d61e0c359c0f3e1a45674c2fca8be9788b99a2335c6ce28aa956ddd1ff65d8ee6f036e78cc59e08f9"}, + {"0000000000000000000000000000000000000000000000000000000000000c80", "0124356ce1406f4b224e830c4b225b4539d479a139184889247b32ae75cb822872cfe75c93b51b1e3e28025357091b4b5f3f967fd3817cdb21a28185bcc0a60ad084ccc7af8eb39287a3efd248449ed5fe5d18b5040918ccf912fd29686511aeb5312af1bd90b18ff30f0eac4cecf23c77b299e0f64b28bf34dfb51158a609d15576c2d7a334cca305f665f942ce05afbf3f504ac0432c265d8671cd07f66ae251b5370f177977f604217ca683120afbfd6432ef19c40abce094133eb11bee6a3a67da754b7bd5a71635cd63b78ec19e2c0b059c2dc65709dabbaee5363b96fe4975a8e959ddb5111ba130ad37e4a9ce1f1699356a88fe8861ddaf04061a5603d9a16d233a8a551f1fbd368626a51cb09219035d0f5d133c1f9adc872220f8779dfe0376979315b6df1e608daf13a996943f8d730495158ed1d35a33a4425d8825fe9ff416e57cbab4f8e1631bb7a0080171bac7006a3799ca54828124e826dea07dfb40561d29fbb56eef47a9b7ad21d2c8f98331767038f6e5114d61a872db9d8b97ba2245a820ede98e1d16e4eb17e2fe1c9f0b553102f6e28cd24cb4291ec1d463d60917d2afa80f530d589a090ec36e004eca319aaece4dee27917e6ae8d983e145708de41481ca3073f0ca0f85e50b56ce9ed5068df200c27aeba2219593ef3f2915228b97e0a3eb279063479a56796af15cfd40d504f755add18b94cef8bb85040532a06d8dafafb71722685cf728615c0f2c0d46e60fe0bb05e3def7b5402a826fce02a40e9123de063870b4fc1377749d762e58cb1d77d95d617bc62b18980afe00de4353394b5c05b816754e062467a8b988bf8365dda63c87d4f07015787b407d4d3ca16d04e194b399ae019c78b635f826ca4466f6d3333afe6bd43d7233f87e6b4f5adcfa399855c02b91230e908303ca974c7c3fc38bbf645f0142f682af1f1a85c86e532dfd4b2fc4f3281b5512567103e96d9c79a0ff65b8df8b4bc7d29db95a97840819d4c94470d1dbc23f4c16e17a3c7b7404ff1ffa140f5a70dc097100b364d4365974528e7786ff63ff0afb65ec9a4f861284bcaa1adcf28c170c1e1bd924197afd20646b778583cbd2e132e96cd5cf00dd77da11ab33bd9f0736d9533474dd54d4c445605653b4db15e284228d9d2cc7b163653363e5a4057649f0f4f90c24ed10f1577cafc5c461ee4b114e25747976547424f3b9de811b3b77e6a392dc47647eb28218f5d20b0e103a0406dec78beb99f1b31d3d968e6766fe0510664886ea465e9d315f6b56c643cec672269a747db80c6368c28b134571399210f7d6de1069e23030a6e71149fbfd7cad846bb3ebd59979c9c39ed93fba839d1fac33fbdc4cb158c43b2853656e532a27469ef8fe41b27d66ce12adba9790d6b99fd0aa99fd61f3b5f80208f1a416f97cadd0a131074c5302146e57da0b350ab876b23df447fbaa38e4133ab29ca353935e871d083c1d1374237a65ee442165fb7575a16b687933c122a2738eef60d6bb7af8a3517b74288cf6c231b517038dad7c4d1072a6851f1398e0e9f12911493c67cf193359d6339f2ebb1b41b84e9fdd848a6170d8577a0b34db7ae5891e9103e790daa3d38acd0d80567e481245d892dfc51911acea791218e76bea7ad3bd5fd5058e60564c56e5ab44fcf4936ac0b37dc8412fcd3d21ac5a7d5d6374894193731e77a8219e9fae1f73370b5c07deb9c4234a7bf57549437cf3a1b0bd11c93516b1a39901505dbcab1beb9d7bede18bb6489dd9b90713d9bf66eeffbfe858a564555c194a0fdfdea8501dd95368a61e9a5f0efcc5347a58867e572dde64650e306446696b7b5df91a045adfd0d6167ed719576264c5be90f22714394bb997a316d0217b110c5b8077"}, + {"0000000000000000000000000000000000000000000000000000000000000625", "0027c7641442df2e7fa446c786bbf9e9eebcf5bda1342fb6c134955cb79f96035a0b45f7959e318ea0ea0721698467e53db73d4c50f25f643cd8595d37f7e20aca2776b6af0d5fcefca4815ff5e6dd3c4bcf765d00f0b220c2ee0047a43933e37dc8f6e677de5d251533d9feac5fce4eb3f261635b4c4a2425448870c30811766eb6b84684aaa86c845b32e824d1976a1a6cc4117cdec323e74ae9bd7867899273f2ef280a1cf81000fa80bda0e412f3bfefb25628a39c716ff7f12f61017bb7b9151e71839d6ae131c3e0feb9a1425f22fa19c71f06e996ae9f29ee033308c0b4c98a14303a8f2a4ce1872b9bcf3bee3d747390e4b64b1817fd45730e6fd151d74f2da3d992a59b0f34978ed435faff7e0fcc20d9a0681acda9f861e7c9947e58b6245ee2ac1affe932e8d5d4031d7ac8b78bd8ae1261239b382424e889c62dea95ed9bde99f5eff834dea5145e42b20201553a2097343b0559a6d920d3ea422e1c3b4fc11860a23ab5939c0183456261e6589bfaf47b5e312404047db844c96dcc4dd483844dedfa86536ed348880e9f8969f09e1bb1468124c22941d429dcaa95708e1ae54635c05b9fd0f6d3a218c8e28320ee481bbb161bfc1e81da29ca9f5adae67567d7042a548edbb82b22031a2ee809dfbabc8d14b36ade83460475bae7434a1ffd8bef15ac08ea35050245e5b5e64257f961e907d3d3aeb326882bad22314915f8bb7cf63829a06c0e6de8fb5f46287117909241554413aab686ff0f87125ebb91db0678d684f4e4b722e41ae1cdddbd197128940eb940d7a9a2e3af04d373cf0f01416dada9440d0a29ead657d0af21fb9588702f39c19febaeaec30d65abf1f21bc159adb762c8253803534d53bf4a1a21a8867158a061c5f6f206aab6c1a511ef055d43b7266f6b316fdbc105836414ba3a56dc0174a05716d20057f15ee3907c494db1c2304327b6589760aa9e8d735b259d86dd8b81999677ce947c5ede727dd9374a00921f76d87bdcd3fff578a02fde6a9fa02d3e081a054143229904a815d5a546f8c856b665dfb059372908c5c71f11301653a660b4218d35a18de4ca1ea1b612d46eeb819be50fcafc948a19aa52eaaee0dab7940fa240c350d99665d5a4a8402ed30e4a2687f512ff65755f8c1ca56c3b8b443a7cf1e7b1ffad671e1e170da5d455afdb4bfbf7b73375867074af081c7da6802d0cf1f1bce335536ce15641c44fbd5d96a30ff5fe1a0c76f7afed3e2db25c35953a553426ef62b7a0359e611db334f3b6efb8eb6b1e26f5832708ff5f6c901612926202d1af5ac95a3340d3a2fbaf30179b9fd121000787318e9b6df5c055db8f7028c1ac308f20731aee7b4943186cbec5d6d5e0e3710f1da076fbda753a0bf22c336741a170ccf3f7594d05117c603a91330173f143bf0123851aff70aa6cc297f9cb96da4e230c7f4c71d1d21507e671959672ed5bd322b5bfe5a50be58c96f294654d7c78e5ac3c7ae122877d5688cd0d364085faab9c7f608d41c5c1fe5c64f9167aaaf303cfa7f30d1d1897cd1a6147adb5afd13e33b472f21d472346110f601db3cc52229a668f8664229fec2e1bd922407d504baac86121d5311aecc7943b5d11f3281fbaae71686197fb0f968443c994e72556dbf09603c1ea203d7841cde7616798f776ea4e51b599aeee08e721d97106f1566ea879072ccbcc4668c735c6c20a57ef45063a8c6be8d3d16653164939157eda32e220bd3f787027f6a19ed886b252d1e23a752bbaadb60d83474f445bad9d1659f640cb324b4f24cf3fd24f164c62c4fea830ddedce5226952a75e0e9b7ec68ef31e6eaa96da7db817d5edcef11e8cfa756e67e44954dae5aaf837992c1dd6856dcb7c25152454074dbf2"}, + {"0000000000000000000000000000000000000000000000000000000000000217", "00497e2f54186131f9d932ca36aa90e171bc1f26bf3ac1db282010a527cb71c72fc3f5dd5e443bbc5c19136ffbd63b90f468bd55838c70bf98e2fa747ae92e1ab1a6f3cc89827ee1621c6671fa49972e257b05920cd22ebf4bc7579f49a4531ec77b73a16401f19fa819eb5fd8f3d32bf2de86a4266523f92dda26bb608620ca4f050a13602f6c3bd3275cc3530f56b31e14f32d4224b8e350f89310c71546c9b53519f540b3bf60056df6b33922e083a81213ba2c5121b51a5d3169a61edae13d48a1d33368366474b2aa58bd1e695e8f27064a326854d543ded2cc748a86fdbb02aa7e1e2a0c1d4a4382cf0afe129bae62d87ee6118e3a57b3f32a13bb7ab4f5d594c1c57061c13b0f6d8109e42f3d293c9f4f2d24fc512de33de8f1774a4832df29dabd3424b89f8f790ad5b6955912b3df3cfa398fd8eff5532c7cbba27ce3dc97bc62557ccb3887b60789f08da50832472d745651ad1ad768725e568082621f16664a1f48c3aa2ed0f2611787543d72a7f1258874ff35790a82f0d726628ac370f172992fe437aaf6ab78d430131879a8fd23ebb79b249257a86750fd6875b69783107a5335a017ae30c09b728bf4414f08fe6739cfca1e5b6f4373d5cac7ac077210814bed69c5e1b307b3443ec44e38939dab773278a283d066e69ba5fbbb255a28237735aa6e05792da6917d70b595abdf1243e60e863580ac1092b9f2b67341f4d4d0d79cf67d3b142dad3d974dd48471c481a5feebb570f2cf82bbea541006cfb5c34de3d7fbcb6229a7ed57153985de4e961cba255ec252aaeca6b383c2f9ba83022421d70e6017f456197e149910d07aea14bce14426a78cd7baa220b1b1c6ef60e5bb34e7a2ed82dc6968cbc2d824e91803dd5e89e40a87c2b4b94c71d7665385b0becafb21dcf36b060921dbad1fa274ee4b0a0fc2857ff813011e436e3f42f7ea6d1c52cf1eb778fef4837b7a433ffb4e738bb75f2bec3b7902e454251b70729be589167148f4cddd390f91c7c1e0d41de2c2ecc4f9e6052e79bc584b68632b5d46c318e2ebc495c9b9b30364033cdd204026fdbd5173f1fd3f12eec97f235a615f3e21e49b89e31abd1f2725582866aea19ae593fd0e1a98bb195d3b2f5bf0a3a860cde541db0a603f02301cafa7f79f4b7e2342a30244b1bdcf0a14945c44930268568e59ef41eda9d0e3d938d8bc81c164dcc3b00ba3438c9f845737127a661825f7debd9103f45b530ca5f61c701b3dcf5c76d281485fc71163f5ed5da81e2819f2b29297a7f69f5747f74c6efe0d53909294031deeeef18c0ed3c2d9c3fab7570332e90437d53c176983f51293ba994b5a73014c2485bddafd33b0f81307ca9c04c4f372db05720337dc23f5c428bf370a3a4f5dc6e2e8adbf7617c58464d038a9fb0bf7868b01dd1e947d429892c5a7c2ec585ddb04bcce4c11502b5ddc1f4a9c00af8163d4c4fe72288d6522d7e6cf161aa4e0ff4c76f75997638c4ab7687d67955ddbf14d9beb7e896e700beb5ef53d1f6e2ead8c717a60ff0847de075f0c3826a7383b149d6a3692cc365ed0e22e23a4150ad5305cc4aa56e080fe54e5dcdbffd2e51c9ffc56c89e18492b2a98f2f058bb723f1c52c78229a974f84c6534334508d895e2eb95468016bb5d060b56587df3efd927edd0072c8667f95afdadbc29b214888e73db9494a5733ea2672740861d72d1b681f611593ff28c851a13d6ffb1fff1ae4ab98d679d2e5e1ba80c0d5d5b2d0b2d9166ae9cdba0feb7ce5c2dca1079f8a1b79fcd89e31a12e2607763891552bf771d2f789a37cc52401d5cd4345314b8aeba499b5b16ea19865783c98e65a98840e57d3dbef8c5e15a5b17ec205acd22b153a4a3d0cbd3bacca73f767afb55a254"}, + {"0000000000000000000000000000000000000000000000000000000000000642", "0009f24f53dadd7efd36306647234aaef8de5f56700a07e381b985953a9c15d2888de9bd59acc9bd723805a58657dd5318caea8cb389d272ee2137303bd79214a3ba1db8d4e1b3943fd3ea324e3214fbc18bf9f510c8498f341437f36969d3fc60f2d48e615edb7aad181771fca6aabc4162b252c391e2368eed2bdfecd23e591a679920539b6f13842d7ca45c31733c16e3c047cc932458b54f3fe88428ff19cad172dbb7fef64e05e9006e68519fd5b2d6c923cfcdce86f9aa9d4c7910c0ac80a29884550732e45ca6abf2b2c84f9b2c520a64fb281c5903a107ad02d200fcd266c066f9885635355bc1ca3031ad912144a702253e9971a6de405409c241df3084ec5e87e013add16659957783715fd9269eaee8d9cb3e7ea67ed2e1774fc9fad4ed7c61e31cc135253596378b38fb7973db67b7974acd3f3af4455a04a169a9fa657fb9b49913ee539e39cdd96d0a001efebd9e0b53795880912b44d99b1a129192c4aa0bea4f3138e885b3b5bfb0e6806211acb97818260506b4faf4eee691f96e3b54132dda18daad947d17fc17812ef193880d136c4941ae995001f95e3a3d98e80aa2bab339432bbca59831ebf9b42a6ec8b53a9f0710006fa9a41a48093353d4de1fc8675be0299f3eac1eb7c9cdbc59ba538fc34360ac3f61f302149d6dae6127ddfef1df07bfe32636733f45bb95a0da756a9c057afdfaa0eb071db1ebb07a0534b84cb399c5bc101f25ab1fcf4b90a9bc50524a7c52861218af1c4194161b61d8516660f3790ec1f8a05271913077b6b4572e9893fd6c0d1c7d65d294fec5ab2375f61531fc130d64a22147244447fb54b1e424ddeabe8d0977d05f243e5d1376996d33f1b3b5641e733a7722abbd8a721b255e253ae246cf7e3fe9a9a95ba75e9456d51acd1d1a1ebac4e58da3f9e582206639b19a0ca9769257020e21bbb8c92a88e66bc09ed6b5195c2bee63feab0f337acbcc7b297ff23e74ecd6edaeb5c406568dca13d436bd52d0cdf4c0c3f49ee3bb5c77109b9baee34212cd5e835999c765f4857906f63cda7d7dfdad78023554ef672142e1bc4eb6e23d74d789eb3e1dbfe50c664458b58ce06bd55eb4af0bf67d5d2f40f848a202d8a84c2af14ce9bec0a0c62ad902d5be3cd825791c727f0f8a8d0121555832e35d583b44f4944abc11025bb59817a8688bc5fbd26c14acd9d4ed573bee8d32554e15d1505272fd73677971d0f22e1a8a141db6064f9bbf9898fae935d9f23b0eb9fccae499fb1c1a09d50f6be5cdb2aea62ee174bc737f12291491acb0043598581aa78d8bf16410afd5f0332c56531083f32ce9399250598b6f17b179a446fafff2cefd77304904dd841693959c51381d9317a2dc5e862cbcb556de2c2f6b5246a1d27bb8844707f96f0cb6d75b9b8c3a02821cf857f45e6ba9f301e4d52d72b0bed0f4a42e07b9e58a129baa9fdc078952137c16df0f2abe6d6a160e52ea337297b1dd37025c31cb564a021d74bb621a9abb6b614b08cec410c44308d223a519d98a57d108c62eccd1c4f93c9d58f3b8fd22da82234af27ed21a926f1ff8b4323df3d882e88165e1994b2e3669490a2a314920d1e0a31883c478234a39f9f7ae39b74037fe9c5133145997515ba661336ca7bb8984bfff450434c03e9c6854fd8e5fa19ecbd9a8a6849cbb6515254f4df50cd20b9fab9cb39d24fbe44562374dfe1b1e5d1adf10b37b2fbfffb5681130cfa62f033889793b9edc0d0c668bc99384e51b11d149a1795f1c6fc90e81c5b9dda9c069b7277163f7184add1968f825b01dcd1b0eac090bd3796fe38c15ff1895a410fcf8f61ad6fc295aa44021b83d84dcf047d2f64816773e0621ea4bbfdeb04f97c986d56be2ff5bd315a5ff6358"}, + {"0000000000000000000000000000000000000000000000000000000000000cbc", "002f39326974fc51d9ff50c71cd16570baf0ac5338199689bd654d577e96f4f6435c7b7c6b7b6b7d95a810e29437dcd0f66dcfbdd1148392eb95a839983803116b22d1ca8a7de7e0e335e0ace64fad927712ccea05667b86439a6a6f1648bb23a37bac837a9bff23b5290865c0749b2d60ed70b33e865e3bfa0a6570615d11e2d54bf78b90e900a551b329f2d12d8d2e1a927e3efefa451bab9153aac3a6fd2dc384e9ec9f3057c907cdd311d494ebacb005531cea193511a0e41859d60cc4166c95555b0177f157c620cbbfe5ffb7f48aa20e4f7afb7a6f45a1dea9d1b47b692e1ebb69dd4d4582f1a68531741cc1f2b159e3c56f437eb5863cedce10d8427073a6a3f5ef23e5db8c75dfa981e2d745471899ebbe8daa47c5be12459451d590068f6fd4d90f11b5f6b412884e5bc80d06a7ee68bdbb112c5dcdbd1582acbeb94b7f8fc15923ffd45917b61b9135f0d00185ba99e3b0fa89b16045536fb76bc1cdd313434e33f576ccf952a53de022048be9791ac55f5cb4b0c4169c1d0733da9ef1eebac5de197dfa0ba78b3dd7403d9d6d610ae5cd9769d1d5ff8bea94a5bc6d159eaf0a85a7226fe4c3cfcef09424bd4f02ae54789ecfae0feb047c96135ff7f17304a46e4c2b6e7b1a16d19b0fe347c6df1b97cde7c936a1d2cbba2b17a01eb2972389cd1665a49b8b8c0ec23fc1cc326d2d3a307d1006b1d0d20bb29f5da4eed3b5424f711754097d6fe308245b8c1923eb6de14c34203abf118938c9f3b4b50f1b695f7783d9a8fe9ac51b134aae1dc6433ca18c106964aced8792effb23186995546ae313fd7f6f39126cb2f02948a3967838e7756348b4422bc87a371c4b63a2f132d79f754d1de569a5e67c022a3d3a1d2d1400fedfc646a633b00a019d44a08a69ca31df87a316c018d17447d1da8e2e22d7d1d39b962ff89b6cd50252e667cb425de124a7c7e04373dd3a2961923bd30ee279150e57ddb1d162e5991e316c556ce0ef52d00da3ac2c50922ab1b88211827c6e4dfd4af1cf690a277dfedc7e5d32078665955f3ee7cdcdf0501355c40920385c2a5f63a98072e5f52cb3a862c8cddb202f35938e1dcb9dca119893aaaf0375b59fe4ab9f456d124c3626e74ca5f76b3d37b1e8c56b8291225d8252211014fd790d62e5e77cd2320365f772194538a24a05951e0cbf8eb54b93c7119b1a7330749b3adfe2b52323ce97aa14714bfb36f5112dd1c2758f248ed5260fa01f1ca6c9c705ffc73be1c77f5f9b1c233fcde01b63b9bc222285d51a30a613713c4786a9975b4a760ca2c88a8e6db4cdfbebe67c4e3fa7bf54f7fec4685747d48258a72b41b0bd75a05be43b85bd0f3f16b9290b0bada9bd11f9ebdc16ab22c6525ec41a3a4f19308b5a2e3114445b0534460d9043f7c676d2186e330441fb7c81274f39fe2fe36439cead729a1b7a62280f3d6921d4202c951c7b5589defc4b5e613cba90db09001f8ee0a576655670d211eac445a12248925eab395fdf84fd95dd9fc8d1f3c7814c68964ab4d29dcd0dda98cb67da517fb9ee8955007e8d3fab37fe8873431e9a90af3114938ca5f6b3064fe66ea27ed9048820a3da412496e51f2c44784f96e8665e525d1dcbd431cd422d3695ffaecd3514bd47ac543e6e1f3e5e7e0506228b1dd74d39bb5210937f6cf5e35a6c1d9d562b458443f118616aef7fb2c08e3bf5b7112f1f6f7b06278dcc470280882a7081e653b71a9714ae5c138e0d4bc79a9f6bcf0785b0d12d58e9b569b01f581b210522fd7f3147c874a6b7a4781c641936971cb8c2661748995f81108b5969e414401a300519efb8f9891e0b25c104ce8ce9dac543c24871db8c4e2db8b460b13fe69ef6be5f5387323664c4e64e4fcd48ddf4aae3"}, + {"0000000000000000000000000000000000000000000000000000000000002ca1", "00300bc198c86c744e2d0370e3e8d0352e382e24e80726bf4bb9084f6ca6a690b0da6e83c4cb805619d604a99ea32cd4b6ef82ad912c8eeee599ca5e6f371818a2cccea0196ea3a74663ec6e6a12019bc8d641db0263c7d6b91634d3e5edb0645673d4a0f87fbf0c950d0a7245e4868bfb323e545ed94724ca3dc236c3dd0bbb6b54dbd4fb1953fb11555d2e057a4ec7193cc2259ddbaa610e7af526dfc63bb9ca5d8756485bebc3089d4e8dbd16b0bcd9c4638b27eb28a11e812d8d6d31b32de0c69af329f227599ce3701c62c7faf769c108d93d54bbb23f03ec10a485b8bb0e77276e5a259920e4bcac5be6e4c36d5524a9a431fbee7c2e98dc280f9864ef5f888d0ee0de1330e99bf6ca8dddb7563832bbf530649abf10e09c1a208574ac7aa2699e06471e258759e7f74915c442e31d12b36405261b33409e2b18fa6a40a100059fe0b2b92dea646af8747b3f7c02c105fca211f76120c9a622997fc9da5229b94f7d07df869c032b2209f00260e327d3b496eaca59f6f612c9cdc432ec450b798651ec0f954120a4ea142d2f3e873b2954d0556b81774787214c2f930dd69c1747082f5a8cbb9510a9fbc602936feb4a8dc181749d321d1286d111ce3dcd28ebd78a67d37c925c711b35f20f175fd9ccd1e871a0c653d2da7995697b69f76251260a7c934f9fa12178bfb87148d7b8ded717777b030375dbc0a720471707dc5429907466c7096e5f60b5084eae315ed4179f5e3e946aa45fd65dbf9252d44b10d1e0c86e564be1a6dd71884cb0d86da424fa0bbf28cf4969ec0cddb765c773c7def8cdd65ec47ff87b1716c717dacf04df404a68fc5b4a7b1f23db7a881f1f3821b04cceea8de37d64fac6c6e55da86777922e265fc40161931f4aff68d7e0e140f55e65d4faa0cb2aa945f9e39462e17cd07708e8f839df0c3cb8e91b038b467faf30db51fddb24a838745f75ee25af970a06179f30468afe55780b61dba8cc82a53b4d09f23a1821adba10679e655aac72f7b1591a597135f73c451d37549a7edb5937f83671f4b55c8e0e1b6bd7f4bb095765cdfc973a6771cb02c4717e0dae79919782360c26b8ee13142a7decad40fbb539603c7c98d72872228d05b4cc98580be239775c50544365db4bbe6d0d785b7fd4ff33fe75ce622a665b57692b34409c365c0664af995197b7bdb6ae11c3564689916e2ddd86a71a7e8a4e771a95dfa37061d93cb84ffae50e9f3578223e6cf29834a3abd345c444c66982993a94f112fd34523455ee1ed819ecee642593dff6758f0c9d299e09f096b18753f572b27ab5406ef77765a8129973ca3857d6f81ed178c4901544a4f7fff2a600497bde8b21087db8ab8cf109c00c7328a42993cf530d7c3c8245a7f7722599f3bf53cf0ba5e263485b42e4bbb06c0421fab1389435214d866447ec67b2eef826fea6c32b4433a97fdcbe37383b6668abcef5dde9c719f7f22211749d8112df5d5d3993ac46a0b44129fc59bce52d8c9dbcc154807922f965738cd0b216b665fc5968062fbfdf6e5ea815e68ff613a33fb1f60b94dd9b7d255e8c80e81535b91f5f2ad0726d0bcabe803a5e3107166fb7e644eaeb86c942314a6de8e1f5801a012908571e03d7d548db16654125529f81d504e175062807410bd0b220833f778f41485e66b79ccec4bae7d526eabcc431195c97bb7dc296d6b154f65872d5506132df59cb525302d930eb633b5ab9d1560b3d5cd74f338dcc90242f01358f2d158731d89335fc1752b019096c72f88351d786ba8f86d2e551d85a21607adb662a436658e05c6dc7b69d83f444c4623f06dc3ca4910d9aef92380dae68f6f1044440f2ea399b1d78018b4e5352ab1598105582d30580c7ef85458bf273438d"}, + {"0000000000000000000000000000000000000000000000000000000000000601", "00a52925b268311fa6fbb0ff7c58061566097f05f4134f28ce5e8fea109cbed1fdc974da78a14fcbda7308cad94d90d184c3f0a621c8dfc6cf4ddaa793e5c30e1b28840fb8af27e770b2077cf2773e796af6d9df0fe82c8d5f316f37ad71b1294dddc1104f5596a87d1ae13fedcc10b174d9b5c66c653fbe4e4ab63e75841b5f7cfe06e60d8133af15bbda5b0eadbb5d707df71f71fb852c571ddbf6e9f618f6dc956dadb2ff76bd1022e5bd6d87f7d9169ed17fcfbcc30fe7fd9ff69d1be329f6410f72d9efad9b3d0cda839772f69ca3de1e5149b96e8cbac8f62c6334ec6f54ca1d62daae23273bef8b6dcdecd1bb1e25a193f1353eb4157ce7be1944ca3a842ccb45eeafa673203e66ea700f3b3ff821cc3d4c19f14f13e60612c86d6405635619dacc4b4c19bf94bbd494978555995d8b763f06a020fdb7475ad9af06c8f1e291e8148753fec5ea2656385a9c1d00f5309046c5ac15b70021c5311e1b3c9653f5cd9c0f67139ef4e1153b61914232301ed511aa17b340bc0417afea4d0df0d6cac405065937b5960c8a5aec8109c3f417958de737122162d321f95c7a44cdbbe23702cd543f10cd7c29da3506b717ec28779f551ddc94109c2fae409966f51fbdf12607de70b1e5c8afda891237da9bcf0d0bd14e1ed3284cb23d42d68f3f8cc81aeea2cbe690044328f5e24ce1416fb5dcf8f239ef016190574b349ae5fab6517571ff13e1009df52456197322d6411dd0b3770063e4c43b052561c6901f5e12d3f39cbd108e2511f235991b3a37d3062bf8a23744b38cc7271764f304a105de557ab0ae3d58932b9f04f58f9c1084c8637cda2869cde213d71d013bb01f0d8d91cea2a97bffa0b201fc6e14b7bf78829d40d50d3656bb849b9d7f620c058375c2a9aa35a973cfc639bb86b1d52976e96939f74d6ad6ae7e4c03d2ed800195da5b98c28a2cefdb21533bbef954e9b4b4e5560ebc097e8492d3c53dbad20ba56aa24db81c6fcab106dc3618bc873976dee2258d593a97b9a1f85fbbbf15656b780a0f1c2732b762033bf604a956a05ef4701121faaf9b86b70fd1ebb60cccfa580b1e9ebe4042161ab46ecc52b1a2cb6c01e3cd1a25b6ee17b79a7e1a56673affda3694f608028fe19da8bf1d0b59fbc62488f303e88f0f5df66e640364bf73beebafbfc60e05752c38aa43a8eb20a5e7037641ac8e430c7adbaf0b040f27846bfc6b803ef48049d756c139931da18a0b2395f7dec5ae1bd1b091508a2cadfc787231065a69dab68f8429e337922187171ed66dfa9904f9fd8f08e66aa1b321a2a138cde1dc1b3dea60c6931f1446509ce3ee8ad44ca1c159b867f86ffb6f103d5cea173012d56a940fbb658f54c5dd73421eae2a46f91e8037f3bbfcd05449a37d54772a19de807603e3bfe10104264a6cbf50f819c23d113b7057b679a1c0ae55a716e615225946bf484fe592b1006fa379fcdd500709133b095cea4862d94579721b3cc3dd09963bf16abf26e7c2dd366edb27bd55f3bf525c4cc5d697fcaa210633732f750448f5a36b922f65be40cd7bced73ac24f426591ee9468e6afb497b4d7ffca3abdd0ff179e0945ea42044354a73c2a416c2e9671310dab3f6649422f124c9ff052d1a8d8257619b69186d1d038b86c04716c5470cb9910e956f17d0d4a03a61ae77e45461bf4ef94301d5242ed66a3ffc0f920d205fdb070fd04a1da09129e9d93d0cfa18ce922faf13171b9d8dd0f66ba48cb07d3519c6123fd8a45107a3bde7885520d0d4d4da9cb115be9f2f686663d220dc7dc14ab06340314f14e11dd4f4d3f47474dcaa3fe6363bda3181055cbb05d9eb91d6bd86509c8507b7e72085c5939247ccfb0e549a10e69da348d255ed0b256291c1b60"}, + {"0000000000000000000000000000000000000000000000000000000000000385", "01664300c29c6af1b90f80a55969bf308f5b19c14f13b60d7e9e4fba01a614d4ba332bad5561e27fb807058f452c049995bd20e4749e492733cb3dcf9e78e111fa5d47f4ae4149d13f61d43fe989777813de776903f1f7c429ead11dc43dc3593ff246fe684576a4771579d7d9feed8179d50fc431d5c4f1756d9d0e79cb087e657974a4ec3182e227dbfebf48567196de552d17ce89ddb9e8401370525269e4a0120b0addb8ec5802d9a2b47cd1dac93ce3e70cc7be74decd5a7945c9148db917c545e83494a16749e3e0315e620519b0740a0f89a8e319570dc1de2122051c77b59e051a40f842eae3b897a655fb396b94989f32803166588fe6a507f867e759c7f8cde11431dee7acb0f770aa7c0f833f7d251d4aa0bb29dbe3b8d9bfd9a8db61a79b892e266bd7fd61d3c3d96f55b3b075ecec7dfdb3f53a7d405732e8adb1a0bbc704e5e6c632b28736b0bf18d201a62a95d7021f9b266a40d5a0d4796aac769915761183ac79e805ba14f51c12f3cfc668b66dad795fc513f14ad4eb19998f060af72f1147d4af9f371ee05a5f20931e64282a2de4b195f3f73fa00b1d2ebc7ae801ec63b74dc6fc4adebf38e6d36af7a6dcd77c4103157177958a5d04e4fe4f139c8a511b2953aad0c4fc102728a9691b689918dd99f799fcd833b11abe1a132165748f7dcdb06e8c1f832f46331de71a1ff9d8f3027fd81c1f12c24af2a4f39e3030ec71d7c3b7980c09dec7da9612afd4db31b22c6c67d71d455fcad6a31feb8c449e88bbdb5f3b78ab29e691ba961b1c1ca245bb2623a7606b03a47e3784a054122eca0af881020433e292dcce083d8e3992bb1f982481833b905c9e0eab776b8293aa4bff8d443a2ac0e1c5273e0df2a21e88457b054fc3fce190134932f86e310954f17677220bb3b3ea5a1ae71daadd793374c1eb733fdbbec9017b798f180b1d770c86c29c2f2db5795294ecc0370d8a014d8a8660dcd8ab245b4f4edf3531816f33f9298305cc1d9a7b9af1dd9336af5996995c9d3522e0361d763b69d5b1acee7b753cf9fce41360643d6b8601a8a256edc1f661761891d816ee96f124ff55a8c66ac505ce6b2ec6ebd54a79639270c84a71f71549010f99f7428759b815b36c25504cad4449e44b1eaf4739aeada9c9563057c6e4c5731a45ddb9800b73a9a3028e5de01f59440f0be0557d0d76858d7fe5ae1f1563b507b1c39a4d253524b9804ce863226cfe945ec527aee5cbae4bd2cd0454f38c90a2b65b573d1b7e622a84f15c1ff84a1ffa3273f433417e2284667fb9d5117e119be18a428c71e4525078313bd0b50034a6312583c1d98a101ec1d45f2463e460e5a55d3e0f6a201d16fc44a6106133af34c3c6ba4a964af8c6dcd67d21d3af23820f41514bddf30fae1f66ae6e229e6d630204f8a74dcbb35066f7543604a1b51aae09778d2b046ca48df654f616f1c143cb89a4a79577737505f822790206e70ff6e7e63d02f1874716f5a7577859c3259a2ac42fca3f8f5719a318159a17f53f976a411a04f82e7dadd58bf17b0348bb5eedae8ec99fdbea0d0c48739112106fe9752ec419a07ae5aa5b48b9c1b310714e09e1c71b6586c718ab756be95e80e71524bc225d69c0454e419eafb6d8c9a4eb2a8245e51a9a570247743aea18ccf6fff3b7d03dbfc5a6ce4178289839381f271eb804b5cfd2d3cda5bef1669c1c78a9c70710fc72d5ef29098bfd810d7cac4d2a25fd1d3c4454800c8556261947f3a8f9db13f030bf505e5b868108d0a287bd6052ff865310d5346d7ba83f9788a85a335977bee4a564c7a6822767e1cffd0f35197f98f110d3aac13155d03b0a3c43a4abc3d7d9bb22963fe111c16e112e0fd0915c84e6b8cc413142d16a3e1e24"}, + {"00000000000000000000000000000000000000000000000000000000000017be", "0056dbe2d1558381705130ea16a2e8291c0eeab8340a730e985dc5bb3b4c5770afef4da1e434e91de16f175d7122bf2286cda03291a2edb80d04c2945091b117e37a9ed655d0abd856f6a6b65ce7b1b7b63a489100951d6bf580b7a4f8dbe2306d9b58521de27895d302933432751a62efc0e604dd447cea35d6798ecf650fd391646e4a454f68d6415469d8788ec1a6590fc110c1175aafc4e1869408442466b616c1739b0bbd41041beb7e7ec639a3faf913576ad9113799faff70881b49395ed1a5a305b8abd6f645ef31523001f586bc1098fd142fdc83dd1685d60689dd80a6b64655eadc3a3f6316d79aea0b697206060b334e618db9f39fd8081f8feaf2df948167ff6115b77cd50a61e934e9c70b37b4264c47ee62b8de925cddc86a7d1ed6302e1727d053888e66478bc71764485bcdb541dcb45d947128ed6f3e2e5db80f996193c7ce53d17ebd20babbf502f77b665bd796a103c3220d2ceffe3d213135888b123c940f8b732f1ffa78913feb287884fc69fdf18223f9e9f2c7517d6bee5e434c05cb7f694bad0df62d3e7addadf35387398168e653be797a29ae9b9fda9405a9fd62d6d99effa8e140d4b26653284ce1f3f9760a8266c1a005aa9194a6362a40d249623340195ef609f0eab59ec79f6a696f245619709f3aa0d617a2ce0bed160722b2510db4f53132e96d292cd52d754686059ca6b9d60ce75e7d96406e71191c14b79dbaaf3816ff15dff1d83afcf982319cb0a566ea673fd44f241263a7319a5cd30eeb84a648e4e538cb1e8f9dc22450109db91f1a80d4fbe2085080dbc53e3e765439a111da67fac1b8663de5e953dcf0f307c69e563a1eed137f75dc8d974a234d89525cb4a78268e8cc1c2dc319d017d3b953406b6f45ca94c57a43d6a6b35a5e895834a66002292329b84d6bf948fa5e6f6f775d6afc009a6d95d28d99b39f5d11ff0b5bbf3d42300b98db0280a87ac55eb4d7da2a9385b4c17ea6861ff5ce2506853e601687710782d2a1b6b8aeef8327317bd3e41e29c376481d7a50f8b3a2ea56de0589e69b94b78605bc56aeb110f3e4ba27538124674af13c4bebe0f77ae35f16c560b09b662ce8f79f59f3766b9e5754a408784131f2d159adb2f60339471e4a18e11750bd4a24327a5e5bde53bd50d2c353c0cdc35dce39fa62ff03a631db1f4deaa33ddf31bf6d5893f93a48742efa2487ca742bff7b4dff42547e30db8cf5a3d17fb00a0645a270236038af2a21f727efe575025589b4da3c1cd5e47afd5f18bd6176a4860bba34212b88782bf91597021631cd5e29a79af1a72d4bcc612b7a3f957f18cf6388271cd871361f483fda456b322fa497e3031f61f731beaaee1bc18f26a76ee83d1e395e9e916f27355f26241f6ca33c30b3514376650d664b9525f101cbf1204318b6f3767ad3cab36d384e7dc7d4195e2cda2d5b5b15295f62f6a43736de37a13f9fdb382d0ce1af8c612d7915930d921c55b82ea19ca27318c80fd0b0ab0c93da291efde27a5adb1cda275597e2250609aec909ee372d9080b45293b329f303a93c4c54490d669010d696dd036f762868cd3355b1cdfc8cb523354d96cd4af67865f5351f3e5f27cdca605deedb251823e06d9c77f999dd8324b97dba76d9d798bfd302ee0b1aff04d5bd9db231c2746108d95e630b340e04dbbaa1180ca534797050679bdbb83127756a92ec170b9c08fcd0a17c881b35f249d995965735d66e1d2846a9dd18bb88e9eb3d63cd47d3d82d5f6f3ca2a112dc3af41208b1ccd4c2aaaccedcb8431843fbad4136a167f09c5f0b9b35e2d4556da71b4b2f291ed54f168a2c3cb66eda81aa8f27a7f075b9db1a1e1b196b1aeebc348bd13921fea0c6d73e7f5abe6cd5db085a"}, + {"000000000000000000000000000000000000000000000000000000000000088e", "006a43da6c7b9ffff09a81d276d11e2cebcfc993ed03e2e0a08044e792faf1a25cb14ae09619b0b42693029e72182700db8f314a311b868e8289184479bb306da514732963047130e0d9b8caedd856922498121f0ea14c5844e97cf9de6d8460ab25696e0af9bea25e3220caaedb90b23389beb613cec41cbb2ad15b70c5149f8b44cb60799d3f0df6617f54113f6fc7fe040c188aa0e3ec301aa3a2f555323936f2162d4f59510405a96b5a4f1c3bf140dd03a3dccddd956083ebd4441a596b48e1160f6da87d93897fa514d59282be8a80144dbc5c134cd3ed589e747d1f367c2f19ca19d5501968e10c00dea10b9266f8e64c4a5fea5b8b1941ef0c491d7f8924f0b1b285831c302bed5910b59db03e13231921b96023531942c95ec8d932cf5a6a7b819e0eaa595dc26cdc33819ac138d315abcd3bb3350851101ba8c06bd23e2f582ce5701cec58d6b6cddc267e06131b30849908dfbe0f2740c64da4aa938ab6b19d2ac977ec8e4fb813b52bd2ed3fbf5e9aa20c3c3cc51d1c9e9d6d876c0c782b5553f5397b562c5e16a64439c747641450fd94b45fe409d6f37fba182ab5d5150830d68bfee9e57ff0c0e4e2535830521ca05739a5101ae4b6ff4497133490b6a8d8eeb8c283ac38de921c3f0b921f6fcc9dd850894353f049d3407bdc3b5a398f27d66d70a031d4fc357e0665f90306447b12f00991e49a58ef4b3793c722199125a4b151bb7b2faf1f788e010adc87e57b6291fd1845dd552675592a93416917268929b593c94887f817705b167eb7fb3e5f8de57fa0f9699cd3c4e239e8767f640ad5ed7af76810bb7b99fc6101037362b50cebe3877207a7be6eca584b33d533dbb94ee4effa4e57e75a470347bde38b1281393e2f9f62b54c6ce3ed8c513df569be97c2493cf0372a5f61ce919d1f24160a4c850ee7e078c1c2047cd4017cc8afe7334f4323a9506ff92230393de007d7377756f7a88dfbb831fe96f44c39aedc7d4d2e0e05aed31407338dcf5730ffe2687955264ad1cf46543be5964f2b2cd39dba377818ecd1b63f767c6c8611453906a4a51cd15b5c7319c0bb84f21c3f3cc889175ba20487ec7809ba5cd801c5d181ce3bcadcc6213074318b72171d6cd593b3d8493b18e97c20f3e7f674964f1fb29d7697d05a99056cd9eb164ce0bab07609b999cdb39a0198ef74924f3f422cd6f78bfb365e3064656b0b223603eaadd3b9daae10b9c145f2c38c24d8cb3185384ce5ccb5f3952c9d12053385fa4ef22cccf54f2fa0810d31ade8dd1ff2607750903e792210077ecb3b11a88fb766587a6545704e828d3a9f0010739ffa3f95baad7a87a17b38bc993478c3ca7f001a9522fca20de10dad08a28f181f97950e87b396fa2ea8da2fdb6a04efa274d4779a64396788e1bd0a0f05adc1a99f650d8f474a8412cf228ab5bc9278f206123ff17fe60ee90b353731ebe3131054fa5aba5a5d29a1d4219279bc3bd27515e66733e0c6a1d1ba0ae82f031420602789bfdc8eb57e1378d2ed9bae4d8cb212936c0b0fce04af6df5eb5edf5b14bf09fb9e032c2eb229b15f309971b77233ccc1358e5a0cc1b164c8183a08e19fdeab40f6a921e6cddd84f20d2dba2bae3f4c63f9e290acc9b4fd048e7fff42f52ef975ed5210ab5729569149c346059d5aa073501bc36a1f14b91c6bbe64190ec1b2f56942e278ab6a374c6efcc756186e05b8555c49fb81d9d57f67347d167c0a1ab9d0269059a571a0ed39831dc91cf4d1632aacf7bc7df21a9f550806983a58c5faea14e96092e7b096ffb00e445274fdcc52e711cccfa7a979ca531dfee1373bee3b6d11fe4253b483d9bba50a8a46882e819e57ad0d81e2460dddab264b9e06283b37c6a6c6adb59b995e"}, + {"000000000000000000000000000000000000000000000000000000000000014d", "0096e1752a899332cae450367b2d6d7db1c25c36080800cf3bdba0efb50f8652ae7ceef6c4daddffc6e910085c886959aa3d001e7254e99f20352cf2bdc14024764bd9754c5d7f5643e3cd22f8e9b65b9f139ac503ae7d05d090b0e9ee809404b147d865f151ffce510d86373bc824b9612650f65c877382131e64fd59f61a83070673afaead918d730134527f5b2e3a9b2d5d2189c94e9b4cda25be2385f878d11ad628b53f2753009dac0588de2a5f6331b20ba5a8f8a894e85335e610d10ba447d35ba5c2aba36c35dfb9c9b9d2f3cba102198306e9f1c6a5f275419b80740df5d48b521b4c1dca1d93bd506c1b5933c44cdc406aded7b699e313240d83e1390979a11b5e0453dc5c90b95c4a1ec7ae2828f9fb005442cbcb1778a810d87e9f26f8be6b12240ec5f79dd3acffe5d6d8059be06242e5a7d9d2f4271c04ff890af8ed20a734cf3a3ce191376c4a8cb10118b471a688dbef3f6f01a0f6d5f8e973699b9df61a72c51ecd649613c6cfb2e0ecbfbc052d0e2d3ade08745505159bb1e19ec0e17d39ef5ed8f10d4a93a613e6919b1099190f49b6b24683128f0d4fc10dde770380b7a4bd05282b1e7473d235694c4ed0c83d599a0d29eaa2788f4c12ba0b017b1d690a657bf74f769d20b76bbbeaef0889cb8bb7f346e7c0f728765d7d6b23bb2749560e6b1702cf3369efa1cb710fb77b98e002bd053e79957b14f09ab1111c9ae521ab9833518d3357926618cd9a6512e8aa2de0e36b4791295c9fa8039576026e48347db4e6b377de9ff259534fdaa4984837439c2018e46f226c16d838d14756d012dd6d4a0acb577f0ea0e441980515f995d79f83356f9a738a204c9dfcb528457ba452d53ad64afffe6a89f9dde73962b56b029b2925e44fc5642ab029a1b8a49bb64a3b7044c1b7cf16ea8d1e5407f5b73f7bc5fdbfb36700db6ce17e46b8bedfe0124115cf0ebadfd47ea9ea2839d59af4db301d01c1f37b2e74479dfb12d861ae052ec4dfdb9752039fd1a4e129f6277a70af5d60b12772219d135944f94b5a855f0a5cfeddfc1a1bed540ce01650a151cce1ecf250e377e3c4bc78b4cce33a132462469a184de7449952c9397b4811ba139b490b20a6da7d8ca7abef458039759b5a132ec6011e76a92ed94b286b1747f97bd627d62565951e7f16d9e21308dd4f51d2c5ef638aedd54e0daf500588af9d4cdb62f89c9542a639d77119a942386bb256840e7eb71d1d1baf98a0db1f45801b16482b646821dbfdef4548461fadbb4128163d5beb647a1ab9321d363b0af0f30d61e75b2fcd4f61f6a36681647f688a5a0c58aa501cb4763a65a0a8f90612b2fa061bed65a91055856e36c3a66331f375d19e77547165575fdf26829af2974c504cbc19d79d64c11439d48bfde562f5a0fea2fa04d5e346ec08d18771c3814ddf712aecc20d1677315a51cd967bd87fe7a8da88b36f5803167e1a94b1c931d6a792ba8f7562e177970ce3dd37e6c0f4dc886134759ca07d59bbcce629d3fea7d7214115b42f87b30d0c8f70b346d4af728d6744bb401b05e67c7aa0640f704b158a69f3a3bd08131333c3ca05780b9c0d3f133e23433ec653cccfe0642843780bb62b327d61381cea345d700ce57d775ad35d70e0c22d52135e82d90885b67305c3c7692870b11ee4c1ad68c3d3bfca342e8363e55b9ec35f4552c8f4167b76db4635babdce0cef3cce355812f187c3d5f7ddc6e78df354becf871ab8d71a330c52489c41752e092f125daab95ee5fa0ae7fb9c53353dc7c9cdb1e7065e934d1e5b9125ba1560a429ebc7c6d6c1eb7203bdd9a708c7aa57ea5f2a5c7e5a46a56a59d8f7583f1b7ecd02c5069a1d9048387439bee951bd6a78c515dd3cc3196182ffc726"}, + {"0000000000000000000000000000000000000000000000000000000000000429", "0060e3d23bd52aa8bfe2335c5520fb0d70445fb60300790f2dea4511ba30b4b1eb7895b11e0de67b15011b6346bfa3d0d7d71dacc72d33fd6c423d303b3a391e1dd9055da1d1971fcdf4111249dae6b8197b40690eff645e6270221385e7b2ad83f41228ffe09a8f0f1936dd2a940b0dcc98f943b24539387320b53955222823a7b7c75af55522d7b4f5b8357e6e258b581e0828702bdc96cc0458ef5bc8536eff7b0f655e1dc3920494b8fff4c4bd1cdcee25a7b9ff2fdac1abd8b46f169ff2517f550f42c3d851e78ac1c88c953c3587150a539a2553917d734c1ee35af6ebc7dd6ba7ffc96f10fe3b574748d80477093443e250ccf7894a1fcc5409aa763a38cc6a03e799a1843579dbe7616d9cb3cc241fa96c5ace96dd6bcca4ad3a628e72ad37d780c71896cd7d9d903c4d6222d405205438217daccf2327485076a6fbed7349b59ee926b37cc67afa5cde9d0702f369299d5fed8d7086d0dccf3714df41a5bf43ae0a7e05b87195243736ece310d36aa626930075c1890ec64a4b45861021659983ed8c50b47d58fc7b05ba4e54ec290e24f0916b75c687045efddf2088bfd5c10e0021fd0dab63e1610f01a38421ce346d8126cba01b35da12f5cc4d92fc0078acb65844124e36bff2a7244994cb34d14035b73423e1d1b5f78ed4e31cadc533ea8d27c3180dc54c68c3daf32eb60dc1623cc50303e6de169584a34b0c1149a2f977f7576388fb48ed0bad2296aa49b823b26c06b08ce2e186bfa6dcd0a90afe377e2ba4aa7f6adc621b3fa4bdbd5743992b034dcb1283ab274d01930985e2a5bfe60f1f06bb5f3b0640153166de55f3ae1e235af9a2224cdfd30ac5b8133880b1c88b615aaf78d28aabd1374ed67d9efada122034653c4ccaaef88bb50f6dc3310261e57796fe15f253bb170d6c0f9bc9591bbdd3365a4c0bf45808008c027cbdc3832e93dbc591c8c76be99ac435aa1b050d2d0b4b833694b3281a484856f09f43af7b7bd72063fa262a48d9ecce02829696b5ce15d4b09b7d5342bd5f1cec16bc453c1b753cb862606157bf31043c0662c71466c68cf1eb03258c865ba462e2577f18e91b0876d25bf24895d142e5740562b02388f0bcbed1091147071dcaedeac7070610466c8371a6cf7eba2214fc90cf51519ad156f7f4e80d6edbcdbf453baa99077c0e493d88fb18eb5c87a7116e0f6f8846dcb5322158551ea1df5c3dee9852c2eea4e1cde3183d7f43230621ba0c5e38e5346926b62637111214473e9a4433180be3c7d4cb97451165798dfb129610c49c2c730af2fba5ff4d85b6b7c762183af310fc897b15088a38f2c3b1a3358125d53f99e5cf5f405725dafcdc521d3a96f864cda83e9308b97ed667c7bf199f3cbc6c250d56150751df9b860b430ea936fea22d1cfeea61021fe2acdac59a4ccaffe339a7f16a9adfba5ae4d0190a0e49a30fcb1d2d11d56dae49c1724e0eb51c0e0fa30fbbe54730512434f49e864c097e206f9a6816165544d14adbedaf384613160f6dd544ffbbef957d11fc28eda871ddedf48ff714417e61125ed95f9fbc349ffbc7e13a6e2defc8548914b0c6aa83d49531a5220585de42229ca97205c4bf3765993249f35c21973f5fc4e33595d66bf9a15551114f7aad95e738878302226836dfe1dc2566f1d1f079dcff37213ebffe86039adfd74d959bd1a6cd62d68020ace23b92b844061a6db673649464a9fefd224a252e28820a90544ed52dfbf78480cd6dd0aa88e390e3a032fd0f6d2d8c2b0baa11065b12dbe8b58f2380a0d7aa2975e47e8a273445eb6179daa2832a96f40cc0600785be6a7b945315a1b42e5650f896e439e63d9ffdc465de16ff63ca1b343d82fe53c49ad75c3546f7555705bbebf618af"}, + {"0000000000000000000000000000000000000000000000000000000000001274", "0000fea5fed6c72f6bf731cc279d5202341213079200b1c0a6869e1bdda1042210aac25e20e1b69c518a0afdf132be5630b0d30d82db52777462b560bace964d7535a3712fedf99ce787c69750bb0a6a46dca344090551b028e14aabc63a010aeb464e547820d874614234d665019840dcf2bbe8105eff4ca2a7f97b08a0097d4d83c9b60a23f3b2734d5f716ed31343b9a8b51c2c6d0ebe64f687afca442997299fdb59cadf141400fe55d700ddf767cb0098c566e5411ecf9b9d03912d1615dcec14e5c13d31c49ca2c02d6542c7d007bc0b1315bb9e98d2038c0f1807f85621f6c6657be879168ab6490cc5ff8ce607c3e531554ad31af79fdaf307eb04a880a143bdc609d104f567ee86347435e8d00dffb460f5580f154628a449cbb1186669fabecce211724f28ec7c6c75ffc5d5065968ffbe1fef9b6363618eb345a8b05f6d8fc2a760545b3ce2a18f7b343d00f1f22442982094fd1220648f6f1dd19f7a1cf0480bf259ba82d117f5608382395494e3ad44280cb1e50d116d4b078d524a9ee421e88f10767da2f17b4f3d2279c5a48b9ab0dfbd80b4e7dc5018ae966adad2b402b2029565d3dc0103e206d7eddbb089e7a41912ee0b5435e8500b66688c7e239ea7cf347a002e1c5cab1929dc4ce1e849e3bb3681a20a9b2c189490ea3e2b23e442b602e57fb1851463cb87b4aa860c3e5f046d04f34a15b7c2fcfbe57a25697dd3bc02efd83ccdc321b1e7a9fc92ed29fb4782fa83ca59b925c09c7bb40910ba9728c44535242622efb7253405a3e05ee7bc2eb782d959f2508b9d51a3cd854d4de9ba899309bb0b156eea797243d1dc1a71c8d27719bd98bc5436801da4dcec6cc856a7698233ea7e5a748b9def1f94df2d7baeb604d9c9a3c643d7c21370650249891a30275c26f449b89713d31f73d9688c66ceb3e147ffac070084a9662c855f510d90c0e22aa57e266f7f13e89b0093fc810697c4a0ddb8409ba34d153969ab357be204578a72dd1b1f3b733a21366f1c31251ec2dfabe7242f16e978ccb178a7d004a6f852617afc24d9f5de02888e4a181bf41b6698b4d6c252f549c974755baa38f1a2b001a2c0b99db2c6b269fc030283d8999db315dd7c328f5c92bb05dca1c3bafaa4dcfa887766901d0cd15acd0897dbb75ad6dd4d5d2b7e1208d8431704abe3ca1d0798f7a9e5b0c14064522cae85494cd62a8802db6d5e4717711bb453c8395207a6a15df25c236e22b5764bbf76e842d57ac2f8b6f262a133a522325099fd50114c9af0c6fc4397fe38871a6a3e94750f0fc482d40d133993ef532c6b9ba0ca4c53ba6369124f55256d0730377b65661020ba3edd9ad435a63612306c3cfc8de7c2cfa643fddd66d3ad3161ad31da304cf998bfecee8d67ce24698a40c131d964369ddf014660ad302c16dfa2d301041cb6cd3d6ddc7e059f04eb95ebb2d7a323cd8911ad58e07a137df9be150b19ce55fc5f8fadfdfa7d55e077baf39acbc57719f82022fb3816a4a0dfbac878a09affdd06cf25fc2af402fea3fbdc0a809f0765972176c2caa6520938fde81fb0720e805dc1710e7df3323e21f891877653b7601b156922f4e7cc4bc174d60ddc66c3c19e0e1659194e77939e2ce801b7c08046efea613ad9d42d1135b90210a37ac50fab71686bccc7d4e7456f692edda9960c41c170dbed4109f34ad7d16471e64a728fdba29a224f5ac74fde7667ecb3a92c5456422b41d11ea3ff54c937c5225ddb89b8c67d082a7258c7bf49df93de13155ef70f598cfbe9b1c410d0a790767d8f3fb5d51a116e6ec3d626b57e14a7dca9eb0ad6e2a91a1f5c16964dbad29081fc97b8439a3e728b3f11715e4799409a7e1cfe181feccd2099aef5db70eea12abde6e1"}, + {"0000000000000000000000000000000000000000000000000000000000001332", "007185e1818c8290cfe2c282e0964b24e4c0f615070d5ade4dd6ca5cf581cdd24ab6db2772ec4e1a5dca1a0a43d6cd711ad9d2cb674369437032a3b65b921f26788a127c9e6671ca356650cdf63aedad7c71df1811ead21f9f15a99cd7dce34722f9d2b1ceae9648e21e7099b50914601b3154b2c9cafc981e48ff5843eb2596a34547b7148fd3af33547c7eaf99bd770fc3073129ae98b3a8d1f37d0373ee9be1893e9b967e77e901427500d58c23a8a19364ccf96c5de66d57dbaca31d25c1baa3b3b2ffc03bf40098d57525751854c24127db24279c50af98d452c3a9caf4d36a949e5542b13a76e9e5fc9feb75ddd4358bab6111467b0297d5ca0216a07d77594d12f8b685f9455eb7ce13c4721616364c8e0be5621725a93a94ece36ea0d26e999a301c07ea87c92b43053122b09248ebbf72ae5b5bff3d1c3c71721c44afaa07e7cd19fc4af72656f69738f37f07cf67436e9453b6b87a81498ac0959c8d8bfd416b2d5c5e6d46df1d9d063ec934f77dc6aedfa53c0ecc189cf4610c86cbfec6c703381d9fa9132a5f1f816c4b56d2a28d972e99975708477469e48ea685b92332218584c4b7a4177139f07346cb4a66cdb6493de3c531075d7158a32f9b7e5bc504d258748d8d887b5ed92f86466f3b0c2a4f4c7053ec00e5e6b612df52eecc311e55c12c603b1d9ad0f562ffb4f1623ac5d2493f096d8fa3936dae99f756d1f4aeb6eb45d05eb3ae863e6ebe0a2510f85b32f533f254e195cb159cdfcf0129408de9000fb62b5dee364d7234dede1098772be32f473db0629901c1e6e6971440e4ca4b2e1cfae9411454eb1f82ac518b6687e42e1a73bb8ddd7358c2703149ffea1cf311fbc6e823bdcee584296696ed3e651b45768572cbcfe7eef4a29d53bb33463d4cf3f9171f9f054f0ca0e5e9bd59492d25ff2d9fb2d6be4aee036e335e5e52d77bfd4ec834886fa6164283f84cdd098a64fbe048827ab50a51d74812ca79177fd1974e177bfc2de45e203b5b667448e5229bf22f13f23fae389785c8a5b4d005c45326635c44ed7a55de3f79e7076f0b53cd0ee7d99c0c1558c963ccda1de8985f03197d6133a80b72ee868af21ef327a47ff333dfacd109612d4b4026748fd36da5a19853cb1615dd9714830e4b9f024866ecc76b918205ca3fa1f16cd90dc331052bbb516b9cf6173a5034c12c65883d93c9b3b6070b675ed847966d00b5119300e95a9454efdaf0c74b140ef749ffe7748bc3dba29a1dd8bec2c6557d18ff315e19a847a333bd55b1634c25daaa059bf9d147951589f4ef2289176340f4e7375efd8316fad93f8fdf1df699f60a6c654bcaf4c2b9d1eae8f1bab8bdf83015e7dc3bdd9298dab681656f147d3a9724347d79a3505b26346f9f564396239638d73cc4ce3bf955015506225d37d8a278e3dbc57063b14dadecfb92090567099d138d988e86d76b0c147488f266b97eaebd6b9e1be641bd75377b0fc07c2283b71d2d09143e2a3bd848df6ba93621609bf012659e3d74a4bf5abbfc78bf08e7d0b21648e9f4c7acf0bce34c45a1c035d030d811ac01035347c72eb5c1c23da2bf21c15b039763350c0fe787d6f0b8e7d4e84190a3bb5e197097953a651a10523bde57177d5ffb11dd75630269a231900b73089c884c3bcd842f9c09a213eb2784956c2b90ed462086f5f764200929574492a739b7b0b5b5177f176b08a17cb83149ae5ec96632104ede614d4e8f0c37bd13918204dc1e5a5115b144d7a22fe8432cb77f7f0d098feb14fa95b13aeca0891633591fd25248f8e448288b0b35a626740fd65fe6e19c48c04b21aeded8421f3755df945d0769590b5237f4b773e60fb912f34c56364bfb476cd9e184c1678d1a612f8a3dd65b9867"}, + {"0000000000000000000000000000000000000000000000000000000000000044", "00773b47fb113669fc24767d65d5008a5e9b1afc2017283d75319d04d3e51e84d5bdbfece9a03b77217b0b4f31f449e02565b62640bb52df516cf933f0148c1686a0e072661ad7a04aa6beb954af11e6d2f2cebd07cf48a41d70ecfbc868016550c7f2b4de887adaa8143c9685898b15948ce6e21669a19bbcc5b48d2a181ee6eb95ab21239dccd3a4b3e538c1d702fa590320294e23ed656f47cdc9a552bf22da30a5082b19ebf105153946446ed721c6ae619d64d2d51a9201f6604220a98ad3270c58ad5dde373e5ec58191d16211b4d60e9b9f1e16690405ee42e313542ac8ab057c3a8daf187f3dd776cf9b9d1b91b4de19709f49eddbd9bdea05edf73464a76c097d50a161d09524150f66b5b37108df7c531585d102ed57b114048f63ec69692e4416205126981d888be1f18805060ef10abd778e35469041493ea7e259e63f6043c4803768f0531821ba9f540108e6a0c21e347149e19023ef961f359bffd280521d4673c5b20fd0f6b7c902a7b196f0dcae48cf8a9a0db4cd4c7116d99ecfe313e56ff1742f356ebdb9ba3c227b43216894e3f48ab7099555a49e60e47793800614ac5a0b1bb2e1bc23a277985617d986f7ff3acd0e873181543d7885f1e9f131d7be10f9bc3f92d19a30ba13617daa1b65fb4f433847474d45fb4a9c9d75935f67c0d2b21b9393a1fb061f6e4787a1493dadb4024ffbc8834a7dfe7f5812cbdfa7168e9e4e9daa2f0d87c44a2b4f7991e01474a34966aa061235b6db1808cff4c379b812fdf0cff3559ef390e6e625fd845911d0e52a4f560f4d2558087ae57fefca5b46794b1e2363fa374c77c06be73b6259101a9ab2770cbe3f4741a017156979e6c7f3c305e1f6de3bf6d3315774d926df2d16385cfd12ef1d52bf6dbcda4a1ab773c6e64de7f336cceaf52d8639d7e62a7c5eae3727f2af79027f0e9be38512c8767242fc3439b28d3245d8598b1721a795d3d7f9b8cf453c0caaf1887b2ff8fb7da60362734a3bd6e299e5c1c4501b3f143d453ed340c426c3336127a98ec5cf1145b9bf52f90dd2810ffb06156506476e0d239d89bc07884e59fbf26f59b82ecb49984d4423e11ded798255527567d13ae7971f552730ca5b1ab42ed9bfff15e42eccb80c212c084dad7636a912d2f751a361799f44d4f1781e45eb20bda99904ba1832eb030ce3f303a05593ff8c169aedbad9895243d451fdd4b05580877845176f44fe7273f4f0c1193b4747c4d7552d4c47b46cd8b27ce6d257d920fe5fbf4476c1deb9550d03c700d5e942aab8ab1d2939053574f5cc208223dc9ce45816fce2897e7cdf6be7164b90ce13cc4167a5483321db5969dd7fe4b71984355bb1f4a6a146d5da1be62723da6aa6561537f0bd72b6e61249b156e5a973a90830d996430fc6bf2426037750e05db5f949fe8c174d355a286e32377cdf46584825e4165fde157d59a766457d477ac247f6557d14d0bc3218dab1f90fdc71dc40974a54944d51351f2a4f7da360965d879ab06611453db585c4df79137b03cbab6565242e77a311c260cb483a612a85781e104a49ec7326a7d133bd7b057c8c584a7e2b07fa067f32257ae8a1538a8d5a7f74b286fb07664ce1949a8a3370758bf2e25725799e7530c75b73b25ddf152b050396992a2ccc824defe3244065346441d0a8d6f89a0a2e6760ebc6b526ab6f14472cb4f88b55095c324a34d0efdb39ce22b92dc3073cce4485ceb89a5bda3f478b5b7fa695ead0c96498d85f6a0a5a595553d5aa09b2528a5745d947bdff22909955d55d6548d5542e3cf16fe1edad9d7db27fa71b69bfbd92706c7ee47c21dafd6ca7ec494b7f9c328219e73f26654b3f5f4b2509cc7ea0b4751db98b16854f547efe5c53130584"}, + {"00000000000000000000000000000000000000000000000000000000000004f1", "013de39a9540c2646bde41b816df2d89756bdd12fb1519bfa650deee25cbca4277adb58ace40dffc42a608438ea1119ca633e646e631b03936b9ece51732bf2587917048e4aff7846f769794d07b420ee6f52fb905b273ca0989c07bf81780ffdac8aac65e70d93e3846b73662fd535b015b08b67484f6130afb6b3f7d55154167e3de246cab7994c223ba113d09cc723108d918bc7ac36bcc679efa71a2115dcd197b42f7fa7bf401f1a12316127bc19a81442e48b7726d3177924d7f2bd83d32b059d91faa0012de7ffad2918d25d4db461826c786f0cdaf7cc6b0120164bbeaecfd349dc29024a976d76a18bd39e51e84ce8f7d7811f3efb4173103beca35bc76816bf43622bd26ca657133497f88ad4f39fcfbf3aa7edf86c98564414291f5e5e6dfab1d1c7bf5e30ecd784b9548a46bd446b4f534e73088df2d1c951d558cd550a9e4882f89c975ae87635ff501054814a1299fcdd5fca311975dd7dec1583f33c2460e41cd286f5e0b0bc601311b16f51b85370151249c077cea1c064ad9b32c64b53e63dd247b5d7fdc11466e3e1e2ca9f1af7dae9f5a30ef739456b3803aaa410d24591e5e5d0ba3d4cb93023a7e0e0dd2cf52cef6638beef88e5b6887193278889febe82e8850d56d7c2e5a4442ba10ec43bc6e4459775c5342822217ff79380bee68b11043ff0f52979e81770cf2c407b7770707d4d33ff613ed4b02a921150c4eb1f1ba1dbbde0244b6beb6b65561fccace36401a39ed89a344ae28661c57b2737dd1ffeee15234e177f32f970ecffacd6327afec43a84c9a44913296c3a5f55526f37a1aed340ddb79726214ee2784d0f653284fecbea5727865af54f6ad631967f8b78c8d9924d74cecf3378cfeb6ae105e2d80fa464fc3482118fc747f6cf76f74bc8d9f13a87badff596a55a63003deadc214ddf1c0b0b5db013ed70d1e4f7cbd8958234ec5b4016d0d3ab2d3ff1e63dc0e959adcf9903514fbd4726f86c52ebf643610034a52a26c7dc982e9d15ced1163c57f436c1a99195206f353699bf7f26fa3a721e78cbf3c67fe849d0fe0f78a094bd32d2b7041dc547d7f29cc81954e6319969d2c2b0da3919ab4726e53735e2cc468ec92c6163e9697b19ab3e3a33a2455a2cb3ba5a1f35e0c2b174ff6f88a22796dd8c6d236531aa7415b17d0fe40070f1c78f89544f0d849612f87483a11bfd0be8c0028e11418868f7c98fe3f9332adac616d8c743bdb5e10b7d0e0dec7895b661271617e3f2d01e7291c3bdd282283fd88d2e760b5d6d62bbcdb35c5ce5b17261f10bf5707732f8801e6c7814e397ab60f3394be9b933d6ddb6fac591a19394a19365150c0675f5c7ff951122153c0f4a6e44db6c7914e313a7ff5ba7993e22544581e4e4577d847c48e74d4885348795045164b0d04e266031ee2bf41fd7100a3ac724590cafdbe0b5b09d0e331d2363923c02af3d8f3661ad9aeb054f7e30602f4d88173b23fe4024328eb6759bd8813563b8b0b1872ac365333fd4bccf22b29d05255092f9db3b808531050c6249055347a940b65abf39da93afe3e9c0fa53a3fd98bda05f06cb3bdf5a7fff53aab73a3761d716f6059938e4d924fa282e69b3a7614a512c88239bf8f5ffeecd52beda493f570ecce168a99dbcba50af269f326068f774d496684b45688a5ec9c158eed1063a576ef09293128c02aef62db085fb7827f9b5d166d8d8533ce69a13f84c1cd3cefbe610756fc249877482dbddb5e98854221f8929fd11862d7db1d80b60d81f38c150e293ccb9103693da97c3ea2781decd2370653ceb6d11988982f1861ca6b3e96cf0477f0d51e356d9f832f32dba90a06b4935c4691ba74b420f43d2f3bcc20185b17858529f0ccfd110e8494b97cb5"}, + {"00000000000000000000000000000000000000000000000000000000000000d0", "00943c4eeac4ad0d2385d22d71b81ebe437852c94525b1ed45229dfa76fb38d3c5e3736381c00512db880d79a493af91f15b5d0e724b3b18c537401a1df07a399b94f38ca54e1b4e0c57c24dee681ea8f2d7023d00a40a3f340895f5ad8b9097ee0f9f60457f37828f4c5ddef2f4971f630523189312f512da43e732a4dc0a598e11680804927ac5111afc0db6dda838dd0aae11453e94f71714cd2d35931143a8b54ea1c6bab5610441db62264b442ac009b1417810cba534f24be7bb3f77a78f9b2085cf5acdf580e8db4b7b1a651d2eb8154ba6ff6fd6716980a2d15e4634a786f97458202b25a56627139d6ec14af3c6cd28b7fd2a1f8c1856ef055b58c0e8b59411d64592ab1315f20d4f3092b9c60f6fdb00d82a70957be28921ccea88869114de233d06443cdfaba549439af93128c0c1d2bcf5149f4e1f07d2b190bee2073b8edec31711c86c958c68be359b04510438ae27dde3b76e66a6bbbdd4ebbbe7bed65f77d8e5bdcb212db54a69a7af7e55342dee691eeb1a30f795018d93a048d6731368c99c59ba0ace59ad726f5d4da1c39e31fdc590e8b369fe369300ac98a0e90470d67b80d4cd010528742624c02a12c72496d03d05566248b30f7a92b38e817aa8ddb8449bf16507ea0c557ffb0263a39d6184241119e2619217569c89d43c8bb656e0d6cd790148b44781b2dce32fd65a03ad0d1d693d019cebf96dace0ead12ee5ee12395d3d5c1a7a17307c74f2e7ff1e86f461dfa295f2e9d738f71e895c22078e4d1195885821cb5cd89267631a2c8027f1a7f120255d634c8846bef37197e9dfe1feb34c0d717d2e02c7df7194ac73892b5034099dddde1ef130c697c9ffa00893187a33c962c42f81cf909bde472072d3516b8d7e1533db54e363c255897ebc8c44194db3bdc3bfe7cfd3ba84f6b7216146bdf6683d8edb015604c2f584fa43b43c618ca1936456d8f5deb9b712d09f8fefa3d29f655fa255ec17ff2ff47b7fd6cd0670340a82308ae3e7a481a3795376759adcfdbf5e4e46e52e53d9ebdde37a86659f6676e7c56bbf0c7e05822e03850e916ff6e7526035be5150b59b85d795458dc63dbe95a28fb10f086d5ff0b3137f755dae2a0fdeceeffdcc4213498b53035f721129a7714fbab81848696a6927aa81efaa8497f3bd60126e725a08d204657be4138155103456907750507e2e417f751151226674a099e9a391f7117260e737651d54c7d8c14808d43fec8803f8cccd7841b856c8bcc4920d48e5bc15bbfd8b2291fdf6fbd075dd89525c11aa7eef978e0b009d3660c2c77be674f71ec0479e6fbd269f59660cc084f6d14aa6e5065514d18135bf0955a2cfcefb0d7273565309d401d10523016266a5d3576e5ee2f81092f97f17d12c3152ebb1e7526a80615b2c3c06ae038fe7763c196881f523d1be7b901e06d2e7ff2f321b0b32a1771aa7ad36db32a825af1f624febd78cd91b0cc2f2c312fd9b439ef4c2e35e70d972b8743d3c302591fb1b1d79310bdd838260b98106c406585e51075abb9173520757b19f32aa94ef28f16b167ef10f12313a82e160a8eb31dc11371e5d4ee483deee4e861576d1092e688563930c37d8b66202264e361545f91a5c23ee6610b7f5e01813e07ccade756d7e9a0a87069c0b401717380dcb6d28370e6352ab44ef5ee46d495175b71d56660563318a4025715056fec21cc2c41c5955d8bc94b213606b646f994b726995ff953b611e71831e4f53d5f37f8713f6482786998126dd5b9e0b3642322f0bf056e71489e72d63928f56071be79137bb5d817d60285f4376cb1b84ebdb8709ee9e1823137f671b4047a8ed1f0812ff4d4ac6f12a0fb7b42e1b8418e197531640f68796f0bfddf059d4bb167244"}, + {"0000000000000000000000000000000000000000000000000000000000000ad5", "002f9b1e475e6f6321db87753b7399725380ffd1d030922fd167ed9a59f9517484e2fc88a27073f44d0f2145e6dc4d628cc387f3834f4f50cc7ce72b3c6c362f5c14858f1abfb5361026937df29502ad6536a6eb0099007c6b47e28a553665a0e750d00dfe2c7d1f0814288a5d699ec0ab654af1b5d59370775b65bfb0350242ea4a9fd588fac645e087b75cfc11e9e8b91471309c3c2b6450cd61f80c87dfafe51dd2f0c1be4db007c635ff650d21812f3b92df2638e5790cd68b8e8f2cf743684025ff05640993fd08781ff987096ea32d280647dbb8cff692e5eb884df7c6f7cace34f6b9e33daed25d1b68485de0c56492afc009cd3d7f0b0ffa07e94447af869087f903327482b1fa127a323a7ebf0ef75a4190e06a7365ea9238b0b5a5f8ad933243b907fa309638b9388fdce880ef5cdfcbd5de2b11f086135df6e539c8b8e87328230d38f89b957a55ebe88901eb232e221003f72960246fa87b01a148366bb293096e2edbeec527624d2350fc7cd6f7b50d2c4c26961effd55e49abc557ef3945ed494658922bb198b6ef20bd3ffe20748567cd466650d5bcb09a0d81d293e81663c513171613dbf54e228ba6c400d4e28f35b23e52891fbdc136391dc1d1c77fe4e7298e180951a8b31ee4c93428ea1add61a2c454a44c36359529d224ae6ea993aa47a2b5ed57212a75f06fd9b6a1edf9bca50424497104a0b1e1ed98a4e8cd3c2da749ce5bb45f359f9fab8a4db0aedb16281ad5ee3e1edad35d0261089696463e67e68fc8fa52803affb13d734650949733dc369449e7f03d6ad3c402f5760509592a3c0b920b120a05666b4b6f70d60571a8dfd375f559f927f22b35efc6ae5ea5fdd7a7c5852b398c16fcb099f94f0b69cdfce1e1212f862a628b4a25f025fc8438b6c1318f0ff25a6baca9622dc3198f1fec0932f1d8a930006d1db00d89fd452312f22bc250278e2f809a835b11c63c1ed1574880f0fb94508e323776d52c7c7951044d1be9578be013d7e7948846f53a2d42e4dca573065f1582866f89ddedcbb1b3eaaee070e8fd572394009432d8b74ab166a7b1e788dbe51772862f7a7dfc02ea8c87c68b06653e2373518e2763f278ecb5a844114abcfb22959d66bbd262561e18282560016ee1fa16e93be3fe1d26378e93945bfbe1118a13f21be2df04ec472d202f59b7d65bc350cf63f832b4cbddbe070f61fdaffe0ecbcaf708d276c9ca8ae79dcf3fa234154fd8f863a3b9bf5406a8e85cf0e4c66ff51536f637c4d46daa604fc39f8de4e39830b427d2d23eea021ce41ca58454fd6352f873b4037f861d523c2a9d0d4c1dc67120aa71edb956059f1de1a4861de118359b210a66868fe3020d67e9c339dbc1c61a73fb94110f23471e2c47cc99c135cb328ec0be55e12751f41ef700b20277d841532c79596e55b973de639cb9bf8af38196e4da61f73075bce6c8f9466caa96b7d4f8ccf61787915947ec86a77a71c387fc41e60a7b42dcb88f2cfaad144b12a8cba8da6449d83aaec1b5e8b399c2026d7668edab3d19d5112229ed54945a07fbb55f9a13388280dcd42e80dd1884da2bfc60871dc8ff45cb1bb06658d2e707f9ea7c64b15e67315a9966f746d44a47a3415264e57f3cfaa9ddf7d2686b68189b48bc0d409fc727cd66ddca78d3a50c3ec8924ad37f993926470e0c995f0fedc6ad25913d30818d80238d80531090562c15d27a3999dde1d58c95a26610a738ee4d167592f4d9a0ad3f5bb4a3d4999fc15dfc6ef46a14152982ecc90c4dee642cc2ec8f1a98f93342eef6e6561b435df8b08bc3c847256dd2d689a98ab25890783f8abf3e7d60b0a3e585168091e6deea06597724a65d193f01ab2f26a1d009e90469e3fc971eb3de729d"}, + {"0000000000000000000000000000000000000000000000000000000000001506", "005b0ceeeb6de013dab7318c122a19e234c493601e29c2dbcf6f4bd4d4868fe2e951e703a26ec576aa410cb721477fc940bbc704a57ed153630324139d9a621f324dc8114a5325c909a508fbf7541b18a75d657e1043378899e9e481514203baa4ffb806bf30570ee217e9e75e2d6b583f845853b721d74c4e86febb31f51223dfd95bd032817a4b91299c5d3d06dc72d914cf247271a6ae1047056f17b5d1cfbda5739c37de634401e03270b406865555c192d975564291646d9d5fa813e2370f7c72364bd11a96dba4f7c339fdee3d85a80e9a443ee14885598995b848e5597b9b88e83d57dd19627f5b481c2b6af858c41b02eb7a4a3862d3241802493cc3f7d199ab271c83e6342b4a24fad1ad1bc60d621fba25c814bb94e3610887e5046b0805bba111029fa2f47feb6ac7e881d15b4f2979ef9a04dd20b608f67f1bf1427b1545cca7321cff912b04f59f60fa03683b0af025abb161b021436916912e24b8574ffc91a3b6e5af31b577dd7b1c107c7cba4335de7e9d1e2290a5232a1ebb87d26c0afede785b8b1f0f1c148f436592584e9a769b9e91eabe2565965eeb85dec14c045c1b16011b4c379467085a2d7fc12b40d57d0e1004e79bfdec73a5afaa0f51bab6fe51e1946570f22c0620d83bbb0e364f1ee811137c4c0af33f9ffef98c0daca46fa9883cd1f546852fb0744a7a15ef7d342e06b4b5fde7565ff99ae7756671408ab55daef35878419e2e2313d5fed342bb043daa3f41e30b047e18710c6534fba79a12bbed3b820b6c3801549bb31a744a105e1d93fa5d1001219a5a1fb75ddd6acdc93f78de0ab97d6a6e4345a8b58f524157cceb3ced0199e69b23cd9cb797d5ea01da3aa7ce33689376b5ce57f68a10899b132b3bc72de7a3a8f7805b0892f2df599f7926a01b0227534ac1ba5362f1189c49aec2575bbeb2013880d204c82c38eb495174d43a2f15c8d1dc34e638c4eb9564559c476e29a414e2eda161f86f93dada0150ebf60d2252edfbbce0bf22d2fdfe2ac3d912a41007f95e4c9be0df7895720e6372360161b63ffc7508a3c5ff2a92e4756a9af2aff166f151a430df695f361184c7a98f0f15da9324cea3db906e97565ca1530b0c69bb4a9576a367e970b672deea16648156ecde17ad62ea1a470423dbbfa22535ed1ff09fc0384c99031fd2825cb1279198db125629ec3ff5f7a55845356533f5cd19b4e6c3f12927d744d167fe5726bfef7d117c16f05d61d9816572e2e8d4ae90f98243add2ac4ebc3b9a975534571fe5f5a6ede5ea8a1b713df7ab181c324c6b1b2f531c8c92fe192226b5bc95b0e7091d6d7e6fa3a3cf79b7baf2c4bdfbbb498a7ef875f32478fbc4892dadf3f1bf4909494cd7272b227c72fd653e2768cd1af301205b0731505f3ebe13431ac82207374727bbad295fe3986560af4684ce4e0b3c29a80bc02f8f635eca771576f55d5dac779dad0f5bd0521671c497cfeba1b36e80d1ea19dbc321d6f79c4a0a1811ec618ea977f35a9c2424ba4224eac9be1aeea809c0549624d447f90a3f40b96ad67bd09d3b320cd50f3fb219f36968977d87771b9d4fc98a928fb588c1105dde2e31d4af25bb18c3fc0436f5a661cd3a3b2c852c658398a5393bb7f568e33c71cba2f7231a31fa0ba510b7add98acb9dc7a13034f522adf9e1baf5ac3a7ece90314ed7e7493548498c42ab1645717ace7512ddd1b8b83695a5c1c0b17694224a766945ba5c0e41998dc21ab0cc1bb44a37e6477c03eaab0a9fd5d416bb5b921f0d5c8e9994437e827278911f087e0dc46621d7938d62ab4bd3db388fb6646bf2f81c7df38b2b747fe2d5f22c43aa3cc4544df641e934747256562ddd83b30ae40007b08a2365531d012ae8fbbddaec"}, + {"0000000000000000000000000000000000000000000000000000000000000064", "018d219242e7ff85dbe5c36843f9de75ff65516f0c27477d768f234c337ff1abfe1ae30fa30f3ed9ae102b2c237f56535bc1aebf46a20d3b3b05c5007518883a2574fe29942c1edd78972829fe44360db67bd0c702e20707e7063d25555283ab8b30b065454afec5451734e9734311dca8e993c5cb784ff0398bb4ad0d8d1adc72efc93467a5ed4552e2de471775c493534b1e1cbc4ad3d852df779c47c7faafe5553f411e7fdb800422e6539e5d979903322608ac6afe41d9013d4fd813a82b970148fb36ac5532d2572ae2ddacc391e57b0ccd22362a18652d47a1b34024e84aca10db5ac2461203293a87873386accae16a08dadf2080121e84a70a2b015f8d1fca4d7e3b61e090cf7f2cbd207549f2152711c6cbcdd7a8b13fe9c6646e4b12726759eacf17f4933a140bc60ffc69c3a5d451694552831f7ed41997ff8dceca138caa1de347143370ca3778920b0f03b36e2b3c0ad9c78b98a295a5787a557310902bd9274404219a97c60f22b495745cd6594dad4b58ec40156edefdde572b596bff8344953231e544a7bbf92721a9ab28b2e5463de747a284aa3460d8bc541121c804bf6f0bc71b99d93b2c02d68ffa7bcf99fd1f9a501e6cb402ac4fab39fdfbbc86a26bc65f56c89b264c095581bb52a540cbbebda1267de6d2907bc736dd281bb78f0b0ea3fa47ddf498f65b4877ee7010f38cd30650afc449b2900ffaf282656f26246cab5ae87d2610cf6bbe48982ab9771d93bba6bd307e788a5c169c07332ccc92ed0ead9ca1c539a565ead6575c32c4095bf9a491d3aaa509c456f5c953e9fd7e35859c9cc20ce7b6ce3fab2149707631e8e3956fa8a29dc57d533930148f431b9a45ddea87341556fd0a9f221834c925fd93370b2324932da723cb1a3e4cee1f607a8c2e3febdad32667b6634c8e3b2af56935bb2b21ff2302018d332ab59acf657e16220f7ac7fceab9fbfdf91e06b0a7a9ce4847c8fe22f0fd0b3d7d8954b1ab8e131889a6f37d8aaaeb8d9083f554fc41056da7179119564cb73c10d881096cb5a6de633d059dceed95fbfd0ae66250aecf9f6ac06f64e4ce2d152555238ff8100cf6ba6be32d4841faff4922f07dec1e5dc61dd48312ef3184421c8d854458a64f11d3a02277f61e04d7294c356b661ec9cf857a541a2524ffa9ca331794f703acb2b58ad25fcaa28eb1702572ec3635427b993a1a4daabcdad88969efa951e7d9be532f18fe19b79e0e4905674a909323eaa2a1f6232b9f4d86c7d9057b1f04852c782ac3d9eb0b33cc6566f4997f4cfb784a060710bc186860d5a00134e2207997fba2851f23c01de03ff31ece77e09660a35718fe9ac189105898b510a65f422f09f84b9274662b81b4a3d1b8ef7699b2533e7f119f6a43596806279cb14c4756cca577af3c03a72e7ba86915c9fb84f140c2fa2b7dee8ff4f5eb47099f80645b3470fe9d95172b3c7116e9ca5f59e809134f04e3613d5b3ec5e39033a73932bdc879838b40e0436c40f040c5c06cc43ce62386893363fa91160ba6826233cfd332b8598889b9526ba63c575431ab4b020cc2a25339414ed674bfccdebdd5e245fb43810fb1f69c17a8b6c1bbf4b1ce116c915d8cd71c536414a0a1b8840c9239249e54a321d7642593b67b48da0c89728cdb49691b8dbfb1cdca4cfaad76477650a42ab81b0ea197109ae44e33a415c3809e84c9b51e9f1047a5fc0be03c15201185898f55a3072c34bc01c92270698a124924ac632413e07ba2e6fa0aaf15db1019c317c138e89c1bc2ea46c59eed16b22c6fd9a1215560f50f4621593544a057d50de7afaa7165d3d37e51986b2afb22046b4c6656a7f0c0ab93ac9cff877259327fc860997f95fc6d98d38a6fce270d0e1f886e"}, + {"0000000000000000000000000000000000000000000000000000000000000f1d", "01062ede308a524f99e010cca87a186c810ccdb6de3a45a7e84ba5223f544005d93265e8e711ff3c691a0f280dff2d4e28509021c258ecb5ea0cb37baea0cc11ff1278daa52fc57e796226f7fdba715f8a3c6fdb02c5c6d08f4ebaa317c9e8fe8fef9646cf63382934454d5dd36356c19b245e07fbf34d39ae523ab69c6505702dae820e3b2358b4b4346fbb36a202e61d54e936a05714fe8e147f2549d5254a7a912e0d127f254a06fb78421c6291854ab9a0882ed6c86075e4f1006547a5ef87066bcc81d457c6f78df49d8a99ce96f67e2206ec2ae7e581374118d41f11efbb4afc16bdbfeb3aff2e67cf1ef7edb7b0c589883d8beee27539b985071d2e7f6e24b5a9e20412e888d40a00c08c19a37e31289e1b1d5537eddcd6b35b4c9b295b67f43ee0c41395ccff466c02e7781f11c7d64942d542195f630a2c5e02ed6fa4f4d19223f592fbb73402033e1fe7700274e4de8cde7287cabc80a88633ddf059d1f72ebd14e54b19798e668f60f4e428c4b4ccf2649d142a800526c251f11622c93f02b422dcb39f1d7678b4218014b7e6624d552f23896412f992c08e7605fdd7f498059450da4414896d452111bc2b3b6fa08769bb1d9b075f1d3bfbc24a24cd67a5f6f9db41a5e1cbb597e60eca9795ec4a896192e03d1663f17b637589fc3f6c17b5ac925bde5077f9739433875937f22b6452a76303c6cfaa981212614b6862838ad27b9f1d239abb34255301a8a1639c455e0a62cbc9dbfd1948308cae8f08f53a7cf602c4e5698877618c697112f9181bbcd81e81d9b10559eb91d247c35331724b5f29659ec4b312f1951a41d0c6577c1f64a71c779b55bd743056f85d733cdc155b08fb51a2aaef4372cfd2fa8d9e3f6d27237f55c956fce5486d7511fbe9f05edcdf9920894d60d5087a150f5dce5f84f43a2be68a5af59be27f01460a5f234408f3533461f404be17a18d90cc816450bb234c3c27ac3da24667b521c1dd576cdcfc395005a3bbdce78c4b13c5dd60d7c1e0e52d1316ce849259fe4d8cbdd71ed512449726d94d2641fbb3be3c550b41bf00ffe43ca34756622e4d9389c50a9bd075ba15eac9e83d46f354c9e672e0656c10be01685480bd2d8815c3e822a8c58c26ca539ae13d52e9857d00a07e98b5eff170736bcd7c3a4649d611e70feedeb7090179633f1ec1a512cf64a0a78dc973dad26ffa0f7c115325e3b0d172dbecd416fd4868004a23e7f884ca05542751e92d8227d5a6f3d9766798357e0d57138b186f71023da9d48dd8b90456d2e16241ae482dc52907c9eabbf2b2a6c9a4b74217e3beb8819390f8eb0e0ff6b9802964161fc3df235a03cacabab48affb18e1f52ea2c2cd8455959b8527dc3d01150b446da4e6a3b675a5feb6e4c45db2d2561c1e13dc1a92734c59f014cf2d6df88c8ef01a158081b74559efea75ca36b0443d48f8677a111c56b428f95dfc0ef1c377c428c06005d919f8f1ae73e9333f00a5d78c0fd1bd21d551ba9f8edca1c65ecf4db42016a46a76aeae5589169067934f8e6ddcd5743ae511812a587eb787ddd4e990e4878c172d915e14d3e00ea2b6b0f84875325b59c0ce250d2228acbaf98aaf0f28d1e766d1db10d22d7524dd483ef9ecb56ffbdb5ac37d29a1a5f3c9bea7901790819ac081b75af635344de53bfa9ac2ad8b11f086e4dc55ce2acb7e77132a0b37b6368c6345fa4cb05eba750cc979445848bd1ce341e70099f72d547f2299129eb20214cd50c94448a90b5d781a08a1d95c3074903823cd2889b6d02918d8a7eaa4e38157ae5232a0945b4731676dd7071164f22d5785eee03be1f7e17a4fa7db5d7313ba95b76b31cf237d2f9285d53301fb509213c0bac2f689842be6e56467ddde63e6913"}, + {"0000000000000000000000000000000000000000000000000000000000000553", "01df4850a61bb8118ce085d5b460c325bd235ce61a03205a69df444078db903655dad5f426947a9eef4704f53ad89bd182c507fde233904fb87962a50c9c753bdfe4debc1a59db9b8f143b3fa25f9dc905509a6f04267f02aa480acb2707ec28bde70cc3178c19731a27cca7a1a3cc59837b33d86e9175268a76fdf9e6891697b1d6fbc8f857ce381286ae24f447871c1dfc2220c091757468a695518fc5894871de917b0e7333640688c585039afaaadebc26d9ba702239cedd18db3f07b14e94d0a85d4d8995236ecc1eee0ed8ed9b64b20cbaad4399317b9db5b485feda571715881eb865cf358923e0480feb7f38e34ceefeffcd2770f0fea9e9099a63104a8e4b3dfcf3d793aa4a4986412333f8f90c36a3e13984d4fe702b26efcdee68e26544dafe5720334e0afaa2602db3fca47aa3c5c67e70ce36308622434a827915ae5abdaef765c87f6bee220013c87d084b28435bd22e7d8a5094175bc66651beda7c4e743707630a86ff03c7fb0e33839548d89101b3d03fb12dad82002896c2e8e44ac7c01d7ef616afe15aada83bbae27c42a26d51d0f9172011e293d5f3ce3532d41e183a0f7d5a2a4fe4e4235313b73be6d2a637be1120417a2280e8031df8eae6f0304b3693d22abf727928f83d935ecfd926c197b86cb8ff092eb5d576ccb731f08fe4b9ac93cfc671b45d94f57ae143d21bb6bb0dcb82d38717d82ff98728ce6a52cb8ee5d45d7b994ac70a64819fd9c343f2b6e69376d48211d6d2e81b1a9a2e5f935b655f2d653596ecd33c55d1e699222f3c173cc08fdede79b2a875b63e6126aab50b1d91eb11b7173dce4ba5f7212ce968eff9fad74516de15f02f2abaf9d32324d5e86219a2bff571b67b8693f77f1ffb62e626d1f62f7b0a66ccb2d33f21f6b5b0b93c4320d725bf72faaf98c44447b74041858cc599b09f03f7a798f447663d185954a27e5c5499f8589d5b4b16394427718d01091564b50aee559f2a1cb2def32a0d005b9a6903953530367cdc1aea11ef4b055b7f6b0f52f0db0e1e8ba56f8303258f221da2e91efca73e15368ac0e3c640bf28e2127374b3dd915a00ccd1431634b24ccf09be8a905874c1b94c9532f04cd7e9e12b91d1f0f20dcab5b23bd7694bc6b7f1ff363a4c6f4384543b2ea776c3bda2553816d520223de9da76040c8dee8d87e75d4bcbae929545ef08ccc16ef060050cae0b4f85affa6fc3e4a1e5bcb776949f979f3e0f1caaf6d35d91b139b1025991fc66dd32bfe69d60cc4ed8bb7ae815b64cbfc5557643499691ca489c7d28132a4a3470095ee1d026b19571eef439104febd0612005762a0eed1f19e34af2c07627c391f8a53ff8f51726a35be3073353935239beb6665b32952d55097a369244edeeaf266f9b5535da626f315e3d9b92547104ae62c7f18fc2d50606e15cbc724ae63724df884606a525e47e994bd4f0cf322cbbb28d3df17ef2455e0ec70fca599c040af378c44e8453be2203e39877c3165e330575071f299a2d527c762b1a1de5745f97cd08bd0320891f6995b516d1f6237a9d1e59a017c78f1dde51d8e08f3edccee925f2693a86aee7ce7d280d157d32965c226bf7efe8b2c75a4413366e5ab6be012bd17f9a52d3fd936671a5774eb6011170443d3a4e050e76457b464e1d94a0153d606fa75a2d51bb582023b905fca71183ed58165769d66101de24f13d5028191788e9711129a9a0b04bc991f9ba1f25249e252126d4c16df69313eed9c303a9ab7b62222ff6f71e940b4121fcbf61bd5d907ed49afd444095cdd65f6a1912ce39125404cab37c3774814532b986fc319dce710ead0d78df2706e572c6436617fb1e2d9051b676bf11b057e725148b47194b9798555faba733c7bfab71"}, + {"000000000000000000000000000000000000000000000000000000000000249a", "0033d4cfdad02b8deebc69050a4ffe064312de17e5058daf47655dc89b76ab74c028ab9ddd51299627270161875e140129f01f5cb32966f08521686f6fe56f4012135bee9358b12c1b26f0d2e5194ad52f5e488f0207724bb68a5bdc745983b7d7f55c4a0f9fdfd245104a1a315e5f23bb8bf595e0386b76f24db5fbd8f907f107b518c50debdb02f1add2b53f09346b96999509cfb0db8663d18d9b18d1717529ba20df65cd4c5d015a1c18d89d81391c515506c7440219fda458bb4108c580b11f2636a79f5163f87e512cb6eb8f17e47104048c72bf227bcf66f831523873d752042bd4178e19e6abd7bdcee2e9bd3a524640a9ed589221792764038d7be55d86eeaf7412038a51e05120fab79d97341522b72a4eafb83197129a089c5aaa12ba9ddb2def28cca24803bbc3edf45f46548bd03a2dc343ce83ec2cb8bf2008910095c26be6cfaffe3beadc3d5b542d00c4c8f77884a03d3acdc185ae207ced056f3689e903edbe62d20461c518da420cfbd1d07d72c8b907a00506f2ca668e2b6510b4514f1552ef809bcccdb3c23af494ce5a1bfc5596456421f87549aa785c3843db09dd443fdd94c1a566b32235fc684fca8fadf5d94e12f0c15fc09415aae29953ef3b722405bb3d5f96a541c8829623a5eed72f8ef4d0edb21acdbee6519c7145e1c41e455a0dceef693a1c1ee36ee6ec365e6c69059ecd8f9852aa7b757a50b55128ee60b4de7758093338844cad0d41bd36285468a4e0d233005a5c4b980889cb92dc9ff87b72796223da148c88a1f8edd89634f87fcad726f0e7ec6e16927efd9f8b53bd5d3f0a09cff7358307045862d231dc6e8f183668a0139dee1b10d77706a4522fc514f41c0d36b4cdb0316da922189a8192faf856abfb47b22d5c2f015689ba9d34416439cc0c339da37bd9f96d04f7f06993af471f3e0f01e0e8c9770b0658940f028eb7c48d7108b47edced177f6b8692ec422b6e1e0581432e7439f1f7394b26138a5c0e145fbe55f233d343de358e51ec6516de68250721db4acd1108da4a5464fd37f6bd57876bcb6d09003e5f10182b0f63fea20949a95756fd3ff83c6b2105570709c98b8a501e283fe8daa61eb6ff39f727106686fc5a05445e9b1a92976a216744fc8911d57f3cd655764fcf3b7a919c2a04cc7e0b7f72f27cb9e30292766f752039234f3132755f4543aa67c0d7bce429120a0b82def9cd7f71d4316a36877e7e53b4697c0375587238d8976926d7834c0d7ad81dfe5890626e09f0827ef4888eb45c86320e6b5a0c7e708e7e6288101ddb18f51ae51f4d25447806ee981ab1555a55c25d5fbee802adf249bc6f1723414a709a60c517ff411729b2034208ec3877be651534c19de163cc6e1a662326c536cd8a06dee8a984b1b76e3e8643d1fd8cda05f4b0d681eae78b977e55d77fedc2d18b52f5ec06165967e5204d9ab50f29c6a86a5bfe5ef5ae1c5ee30fc1e4dc174b604b59b5a22eee502fa1dd9c3e012763eb3f7fa5f1bde5e35ef8ef2ec80132bb659bf70e12ccc611ab1ac4aeea8485d20e7a09bb52aa3ea7e22e5a097c7e6f366fff5393c8ef2298d5965af45efb35f1d2be61626175a4a8c3f84c6f50b6044adbe3533e31c79baed3e515743844996fad41493f0dfefd94081bb55805e433e7e27be5a03fe8934df457fcf7b71c434f052a4890f395fe12dc10f857bba8cb1e3c5e1d52aec63ad06541f9f1b200b1f862a0b7a6fd144f374ea6b92a1d3d99bd7f06ed5bbd44b7afc69f614709c15b2a57615cbd35bad1ff96a0a8c96db812f28317b64cc4f198e19f8140c2ccc36150c0d2ee18392a29b0b37ea4cb0b26d60bc38b18ce32a8e6cb9e5db635c6ad903b4e4e5328e7c36c174672d14392ff00c2"}, + {"00000000000000000000000000000000000000000000000000000000000007de", "0006952d60d34eede8cf8204bb4d33364d3bda07650e399f130333af2fcf116389c01e1f0a8ded759e2b16a38fac874a04667ede58ba457b19dee41cdffbf4200f33d76b08c43d55dd972e5639f8ca61f35470ef0b6bdcd62e02f63bec3591b92afd00e9721ed3ff2a636aeb697a34762df48cf6c73254a032d4b51d83f126faae7f96e56ee1a00c976eaec5a69ee43c5df38d3774863bd9d6aa79d89373cc2020c9222c4b9218e70299307cc86ba995d2f8923da09ef2309bde2fcb1219ee64534c8ffe594a3432dd857cd890ea6adbdc50168603571f1a0e075be01306349c9bf19ea53ea0872450476bdad53c6be816f5d200e37d06082a715c840954656984a49567c34d4293a0156ec0c9b2f6dc5f1c6d87ba359ffba56f86b33a15a82e29459df4499b2d9719a163940cd7433535d85cbeecd5af6797a46836b322f511673dc19718a6f87bf96fb668c9346dc7016ad61ea54f237b627a0045e48d41a32390bc3ef10d9a2e29a1cddd0cc15e33695c509fbce0fc9116cc23052a373f0b6de384e4570d6b7b5812d188f82cec4394f27a84dbc09d5401575eaa7ad1aaa210becc24059107ef05d84ef3e074826749454ee2654e1ce73d1f96a7dde9b65b8bda1076b0805e74df55b1fe8a6a063e5a3ef99f9ea93e1e85b32cb5bd9aaa4357178546cdcb87fdaab0539820b7deaae845227009dba6e606160ce58fd45b755895d0dfa3f564a9f893bc15064d2c0d53fd9e0b4d9d5cc6ee3f7345e35185ff69d207b404ea2c61f2ed2c94e533c15b3aa1ddb3bcb4840d22e0fd1d26d91f3d2813b6cbc4d261dab6fcd59923a24d93b2768917cd4a34b601e40dbd47423581a8608aefeedc5877a961df86cebc705fa272c63672a92e210aa5fa639151e20a452b685792724b5f349e643d8e7447b35720550479c42a3fe1d1ba174a7c8277012efcacbb95e79ef606559786530bae7fde5ca9cc058d02de074851465c18719b2e4f6224e24d492a1f13db533cf58c7768e3d801422e9250de968e9fa09733930c53026ffe35a987e94a2edbcba6a5241bead20c48db6edbcc93bb7579b61ddbb14a0a6ef8fdbc8f19f068e6d4e0a93f76bbe76643660355e1e1d97d0d128baad86f32685d9f339271b06f4fb8ac8c9637c9148cfd168f60ee89754bb4568b4b80121878db39e8091baa5e62913147eb161327c765cefdb269bbf7b30a6871826c8dc686db94f377c9f5ae355aa777508115a894cad507ce7edb9672ca0f33196142eb6ee87967551667265db8a55a2ee7a1f170b2520831976ff71450852254c72e99ef92d14a78386e119e7b5d891c25132acd8ccc21d31a55b3d248a938a209af9eafa31c8a5d7bd397d3d5b7bc32744f5c1b58d270dc524d257216c34e19b36996681b81b65e2fdfb75a5f581602470dc97b82599574ee40e8116e9959fc1036cb281f2491bff754b8f17cf2d37a0fd0ffe3ee3cdff18902eb047fe91183e9459b83d181cb8479b9f29caecc2f860f2d60e67165e37c2623f4eedbcace8e16752a0c7fd8f5b70a78e466bb65fedd67325e2305be96f434ba8b969f508d40c073b82b64e03ca219e39903c211c834f3fd123c15f9cd5667f3dfdb8a9fd65e131c2875b1e8aadee5dd580bc2b584b5ba35c3461004bf0365f06b626e9e17b0fa022781eb4676c673fc582d1a034f65999589b591f2386993cc3d2e2888d1bff1108576fd6271a59fe54c220eeb77389d116379b8e2157878ef5a5e618f697fc6353d41e96aac4d1e45ad12264f4d10a67ad3c06bf23840761dd498228764261941679f28493d619bd671b6ffe20a0d85c3db39ab40b57a7df6a7ff71ea96d5b4f4d826730c4c3ce2084a5b1ed07ed2b4495d01466706fcb2a6406472523b"}, + {"000000000000000000000000000000000000000000000000000000000000095c", "01369a38b74c4ce5fbfee0c4ec36b800a484f9d1a20f00c52f568aea7a7d0622a58b4c30ea70257de7fc0f924d8cde1a770578bfe54839c2d56e8efddf1fb42023d9fcbf94a743b478133a4459c29248963294610aee346c13a4c0e9e96ce0fd524aedd8ec233c131f1a5a89b4288ef4ef92a986c76164c809bca21c3ffb12e55290210f037d2b58e4b08f5a752ec5c0df0cbd1e3e6984920d4e71175271ed8fb3e76d079b8b8bd5024bcdc6f9d2e33f6f27073ddb4dce8fb1757eeb1644827441e7dd6941b1a7fc10c1ecdce742c77f5ea30c26ccd227469b0f2a7a5567ddbde51f2342fbf12686a6f7b1cde21f433a27fa8f4c7ada9ac2d6393e880a0f079eae83af9732c9c5000f7adf05725313ad4523ca72650967a2b5cf36b31614cda5ca0f487e3fdb0b422fbd3fcad185d683d220d9e2f22f6d331d320e10babc0ba6e389dd34bd546378cf0f59f9759702e6018876855d2c4d79f3f8615064b5dc753ce2d3e91712bd7426d18adc01777676b64bd287ff20f91ba0f90ce3bb5d84d4cc0add0531f250f78214a0f8f29a1018bb1c74c3485f27c5a244bc7130b925a883744f530e65e963bcc9b44aa9ea8249456cf1b6c9dbb8c62d13db12b4a60566177299235483f52dd1da1bb69b0814754639000fa14116fda6801abce09da67cb875ae1bcff504aa1efb65c12b15f646619cc655c99649d503f68426361abd1d645f73b278b11642062d19fcdd1e2a8e6a103b218fe88dc23e7e70e751a88cde78ee27ddce4588583ccb1f66828bc29ace3d8f98d6f5e83a17f57b5ae81e45fc2937f7fff414cbb1aa7dcbfa06389e4cef98d4aee59a32e1e8fd2299d37cbc6a52089e5684b8cf1d118c6d65cf513fa1bbc2153eaa0b0d3842297e12d027968724884d448ecde73b7f74e65ef8efe0ebdd410b2542b692e5ca07bef4cd1f6a170292cd704c145c4123e240bf14f1751b817d3d299c13a19c4ff00d0c99ad4102283afc0468d3e88983c912c746b86650ecedba45aa1ea5e50ac2a18a9f397336e1361dfeec95c5a72d65ed7afbb2aa3b86b5f79b058a8fb20e4919085cf4e3d5ceb10f328c507600c452060b350960a3c33fefe6d627e36de2af7bb77dac24b20955a45413fbe5b654f96b79241ee4683ba5c03a268c429ee3d373fa8453f974b270790dda35bf070301d7db36c24e45e1f5d1198b532e130676f8b5b70c125df2336e207dc97c63ad0c3a76fe63c27828ec0b03ac952eb1e5d390e734fe6ecdf9fa99ce1e06630db1694af1237c472e97c3e3575aef84fa2598857b0afe7a997aad4adba8fc04460f239c5253a398d5951811cbde2254e3f6d98f371469d2f9833a5e1f4a110b6e3d16a0073cbfd86ff1d160ca9ed170e2dd71c629b51460f35e0509c405985715d9eaf71b4719d0ed03e4b5e0334881ea929097836ed1ccd66dc1dc1d264367a281391dd5699ecd47fe1dcaae2248713813db145cbe7e0dd26da3da3c52554fa3033901d6af089a29f03ef5f469271d4f4c562a75748bc9f04fdf04390e3edf8f7862982bcfd31b0b02590786c816b7928a377737511ca16ef361f1fa0c14628c675082dd67ab1ef2011f4f6abbf399df640c237fdf92a097fef2a825e93f7aca5491d6fe7be3547f1e3a6a63c21c1058041422894fb49089b5a634c5eef8075229cb71e9220a4b9c4adc8fc559e1142c43a77ddfd3c6e99ee7d637b762e9a2d7ca09cee578d5b8e74bfe42c53f3ed13c2f9ac1e51c9ab516edc4c236bb8f2adbbafc12a709df1295e98c72bf09fa12452a215f3d31ebf7608a2944cf585f536037f735d805c3c2979676b55f975e135a7c163d8b9c73b79dc23703ef178d87a4b1915815c50599327ce019fd6eb732f7bcbea632725574e3"}, + {"000000000000000000000000000000000000000000000000000000000000029c", "007c7cd7d3abd28fdc79b08d46b42a88b91c3ba9bd01243a458c0e28ef96b4ce0a47fe597fdf155f50910d02d469ab1179d5afb732627ed317f69616fd23710efb551a0386f627f5a8117e3ec617909dc59019bd068e37a4a99a1a692feac7b60abe1c42127f5cf3616d9a6ba2b0df6b0dfabae8ee58efa8cec682feca8415b87dc98f9df0c7a89fe54e30b617097726cf1ead1c62c35112cceb597fb317942e41db46f038dbfcf10fe7d7fe394b0f71cea7173d8f7d025a08ed37566b22270439aaf634f5e974a280cff8bfeecca8bdb2c013d08325e8469b5e4dcde36eb6eae36673dc93c13b1c5b0a4e061b9781b98e8689e739ba2dddd7b17f1e11b8c14c9923f01b342e735ad5b09349c31a7d261b3e9c656fec35dcdbf79e0423315d457e3c8eb9630f17840198244992b3de8d975e9e5f386ef44a99b0ac204f354b60aa30b56c53f2840595ac0ede571735c900f5ebce1303b686b4bf10cb1f7c70bc60b67654f507244c0b544bb263e38fc1ecf23ad3fe8763fcc72519e6f922019145a154c051ef90b0dbb0a106bc3f676590a3c9c1e0182f5a7806ae65bc91be36043391b4056a6a689ae01e91cccd5545b7c6393deb3fff0a0706502a0a7282c57b213e20cdc408765d755aba9ca60d678a01ae26c3c99e62f2ea2ab2e892406379c74b1e38b37316e0fefb85c4c3ce71ab6222761adbf2c20132cd561519200d56beb54292d92407b3b81e0a9c0a49e33cbd6701338497032fd12b878a3810971a45047f2b6ef7d47646c4f0a9d216564caf69ef1dc91c3b7cf778006b8d9fb0b4b79480d21b66d23a5c491a153336008adaaa79d7b7121362c1e4150032bb38ce15eb4274538e4eb4a716535fe0f862cd38219611e417d41a7db40c62aa95552236cc3695510fc248e6821d6a5cf056f3a149b1ed33e43152546d5ebb3810f2008bdd28d49dda4b05de709120ad0b0c37b50b4b764a033fdb745a2b496c017d60e5eeb3736cebbf3d710620e48f51aaad7fbda4509610949c38f6c29a494a2737a7a6af31d0e1bb78aa0213ed1c0ab2d7f762a50a3a34a83196de351ac5f6059d717ecb3eab3f94971c433b6f8ad6a52b5eb8588710d831a3a2bebd355b186435235b9b00897356a211984582ae590a93556e3af88283b59fedd5a2e005e35de087d2e68ebb8f2a038050cc61a11cefe1a194b49d25b52f33bd5acb8525940d1f905b435951336b672c71afb319e4bfd38a0d03ab4a261387fbd54bf431c2b4061d1893b460bd16fe9661ea0fe0d8b0e56285ae413370ac3dd0578c0aa35c845e095f94f97c24390f389b358fbefb01102d5e89a8811e999b0a77d7edb976d3ee8f3a5d0bca265ecba10d903ba38746d466beb0d3228e8d94e9fd2c30b6964f780f53f2837612885aa5a9fbe09b674d01bc610d9c0424ccf0df1617caf23c69bf9c3350a802fbe3d21fc1d5a93178d3b9fc2f7fedac795003d522a8a4a99cd7c81f229f849d6769b8de77cfd988476188ce72f62c1057a952d68d724874ca550b3b9bb10e790da6d3065445e98252dbcc5ab32cf5702e09681354c0b76068c9db8b18131127b58530d13156a4af4d7d2f7c4029aa69f6514767687d5e9e430d5d7a624e833781bb9819752277f529314f26499f38918baf054b34810637ad8dfe7831c0673d70458aa85e759b2d4f855cbd5a5041d3dbe56a88e085559b95bbc49e0656d8a56e9a2211e2d7227875759b8ea93abab5161144221edfd425dd5361d33044479fce4bbcd5f4130617e8ac53c9d9af4f02c27be9e8643207b2508844445b9aea41942111ddad85330c6940b598bbfa1061084ea74e60c27e74aa3451c626be7f87072d1b7be809882c5b684a50ceba58e1c2a03321bf26697f833f"}, + {"0000000000000000000000000000000000000000000000000000000000001357", "000617afffd9dce5be88e024370bab7ece4e5dbb9628ed3a3297d060afbf57737e3d61d52e3f321401a402991d8b79839e882d70403994a933b5960352d8df078ec9d2e111b776ecc374c68bd32871f9b1b0f10000c883e027c2dd28bea3945d894099c559fc37524634349d1c21a8114ba800643f19b50cbf6da0bd8b3218bd845b21fa442dec5dc32cf124d45203ab7d6b751a478536505582e976824450e025d4794684386d810a256ee7828553b93d38a55701cca4ae79f395c18327251ab984169001b70d333600741bb2997abfefbb3c37849daf9fc32bbab769c7a8d672828d75d7289c45a5867537195c6f230f968d4cb4857287e4761a900a346e0ca0a27927c60fa383de67af08efed4eee302025c543df9f98c1da90562addddc4d24d063736bc2f2f86ea30746b1bb808a3c469f229b569f811f1b5401b8de74b704bafbce494711758a68d4d4a9a629d02670a19e03a6c8df96760c6ed2ba94c7a193cd971217f75ccfb4a59468aefc4cd4be071913d214ca72c035ee4b8ba92f4d2b51762b9986364c60f379d03850aa7769e68c43f04fb54628c653f922654ed7cf1f50593c5d7f8022cf779ba317f6afff3155792536de929572d25ea5b7a29b462d414a1a88e7599704faa4915c107434d8813df0203c6f8c45420064a3a7e3e59227c5ee0750a5db155ee528afd9fd46cfa8378d20803791d9e3f8c8c727cbfd03d4703e044126b761d7e156f5a0fd1d4ff83c2db6256774084ab204db9dbb2417e86efda117b2978067440d24a07b1a94f51f35857255499002219f36de248d894e641cb31fc5ecf820a3dbc76845ed8d9c8bda15bcd588b2a235d985e568444cefe662cf93bf99f98c58c5646b6cdd07f27d723b419a1e95327b9695453b06a323b1105e9b5cba949288de4d361c19b40e856d2eabcee75f012bebd99004eb40314575b536ab2e2d6a55f86a4ee267ac88738d88d86fcd7464cf367e4741cb55a42e7775f9b5803f110924ea3e72f222db7cfe8d0719646035739df2fa442a3fad86d41e7c5699c7ff1c352c6ea5caf5504ecf67462e2dd5752a46a2ab4586daeda799d920c0e90747622d005474d4156f2a1456aa6181f1c3bef0677ae3cc31a43c7bbaa30b86d2f853938cd1626b51b994ec5c322fc7dab4774d9bcc29555f1fed4d50a0a613eaa8384bc10b88cc59fef6683b708a4be863a4ee7c5d9d6ac251bb8c4f590f9b32979f35c989e382a6fada31fe97bc38e2be67347daacbeee67ff62b13e6c5e44a6527293f1b5f54c34b42932d924d7a9620dfaffca500eccfb2c7dc0f4b31c7d1cddc012d48c2f23fdd90ed656ef607f14d815385bed65214e79b20f87c26abeddf0abcc0513d3de2bcdf2a6ee7c653f125bdd6f0b5d61b1ebf7e272afdf55c65d599394db01ef25b6adb939f5ee6f547e6825b1251fc390f95b1d3c237d1716f488b83936416c6687a632ecf970832a5f4362e71ec1234e06d5055873bd9a6cefbd171c7325e51ade2d4891f0370a19f256b7baaabefb6fb605903b783acd4361073d92c325340395bf2b54ebf13854a33f3d52de2b1592d72babfc359a55d613c6bf0e54f79d40d9114eda6164bbf4f114553cdc322bb410b0a307fc1306559c33656689cdae4fa13efe988402bba6cd6b83b15a697501fa82367baf10f2d936dd34dec6766b52cc6504131862c1ee7a46c25759f72a055dbaa66ba363fbe848c365eabcb151eb41173316678a2d8a516a08ad702559165764637e7783d51bbe19d04ad9e31caf3349e785753d751a755db299633b4372c7f2e891213cfb52f7a3ecd9ea4a45f6d5a2531c35d4aff1547169310851f81931bd1ac106774bc226c411756bb0cbf5916cc2f24be14cdd74a88baefd"}, + {"0000000000000000000000000000000000000000000000000000000000000ed4", "00b210d686183ba3e140f22b31aeef34b74b5b7aa630351e5c844c7e776de14458867cce9b1cfcdefd0c047ca29390f14d19c6c6e12d358d195486c3455fca0825b1631ddada4d6e8e33ad1b4aa02187e9d8e6eb018e63f18e1300cab8ae512ac85fea652e4e6a2e60545b83aa6a19cb37295de7071b66b44df64c73d3d7030be0fb628856756569168620eb1a35d82036456b188deeaf8ab419a9e8c102a37075cf39ac9c57c01c043c352e263377adba73a3d73d2ccf79e3a7dfb8b917dc3d10af15f915dfd5b233f62e760a7cf1582f4117654f5a2b9439ebd092051136ef9b05b92270e6901923297181c8d209d1cb72ce216ce80d290a8ecbfa0add0bd4f79e91891c4ba1d391b504d0d385711fa915eaad436ea9e8cd54ba13e8804e757b1d81bcaa4925576dffcd6477f1cb8f137a0278c901bd2f0e1b4b5a3322e3ba1fe21b1e1b16da51ea07eafc0ab8962303dd63595f44c8b3b3d4310ab30b6b4554827e3bb0401e64aebc151ed5d591852df5d1d6619a8152c3730cb6ef26dea1e60158f201689ad4de4dc0004eabf911c8fc8874132375c2eca1e99a6c022212c7de88b70d4cb080da666abfaa5f5481decf9059a9d475d47313c25e965316aaa3684771c5e653b4dd67204e40081765a399200fb0f8f03e24ad3e7ea1796f3f1b1cdb1fe93eee08e0f489dd47c41cecd6e49dc978d21b530f0293f08d53957767a637af8b3e4346627af737e1168b8451b05960f53690a322d9fa46a9ef9a5bf475219c326ce15fb8a18f0c92ae792bd96d4e0632f2433c4df66e1df30c45c6cd044566d1ade598e7f98fb9105d69a8b2c9d5b9470ec4922dde7ec27c55bb40174395ab24bb5810c9a8dc345378b0ddd925a5cc9eb816adcbf7f311adc7fa8078b90446275e6afb1ab598335efc08a5956f37083416df393a45ede948d978c100f5e1922e02a9686dd9018b04a4b55213fd1b824d1a5bcc7cda1d79c19ca6031b4faa60d9739f18b1cf0a1a0a76995a2513611709fa9fd1f6571d34be1ef84d4b04d07ce2a7ab638fb651a03c2005c9e9f54fa6110718c4e158f300de0f53f4cc792bcd227bef0d79237d3a38f91eea7387564487b6b5e2e1d4201a2a801651ca2c4da34bdff3db1334e5546209708b54e23f358404739f6d1517784569fc3f53b8bab698b86d610539143c43c6522473fc730bc560435572bcf48f8f37c4b6250fa95d5d9af9f79a53f097c6d073bad86714b31d242f9cafd79afcb3e3f5594775ee7eb9ba80153ad5ec43cd7bd1f474627aeaca44e50978d21e640659313d741048cceedfc0ca0d24e6ccdd05ae248344017406efea9ca9ba350445c36b03a677fb3a38700a0ea30f65748efbfc99fa5300d48bff673adf6c771fac02d796e8ce23d8c9987c56de2502306f351d820888761c275e13196a78937a4eac3ef17ed46db0d0244875ce54cbf5474f11124cd71b84fda8bef3457e20929c883d9ae7acfda464c8604a3726e46398f67626a68feb47ca79dcb6e243e340210bd1994f0dd04f176f997d930af38f9f68e2d042e980c5da0d9969082f1399d01d6a1799c4fe235a013eea8ce1caabca7819d81bd0e564c0dd5481d43b1b694aa524da9fb9cd22b766b9d8252c939c871919fcdc71d2e258fa6c690db15e3a011349bb0ec2f6d180c657cefed5fd37cf1b1acc7200238daf5d07d24f87a3d449602878db420f730b45a3d06be7f68ae13442e5642b185f5e3b7c60ffec96bb5a3cdb231ae714e1e0e68293b0fcb56615b81119a7da075d0fa2e4d28e66ae8d4da24f426b184b969a3423307fbce042666cd0f6555949307d9819011edfcccb71ac83d43402a8af8c7e02ea763bd01a63cf95d89c3ce1e218e4a038df776e9b535abd64"}, + {"0000000000000000000000000000000000000000000000000000000000003718", "00e2267ee3831153d2829094ad437a4cb96bb4dec017efefee40d496f5f67db7627178efaef6cebbf46d012c816701d892313e8da63e60f71ac9e784f217991416ce0117b367d7e60403d3403739b93e706a75510647d8590f4b0c4180e431f205efb901c96dbf44b32a50b6053955e679cc937347c99c9f72263ed75bbd092b372579def19b20926262845808158db32ef5021c95dc371e4e2ad89ad24b26d6f88012d4517c4fcc0253da36318d10a1c005816ffd9ed1693ae4d8285e25c9ac4ba174c971bfadc39aa956e48aaf27f5d39415072b92189d698b677c25e071f5b0b5995b78bc3426780cd31672debfbfeb32718a14296dd399ff68ea0d34be35fadefad3a98679326df84cf2a351fa613213f18688f51e5a2187311188bbf7197cd37353552a0f6e462aaf66cd3d4873b4738874c38b73053c3f61112af99bffde1ccd6ed3920176910cb38a995f596804acee4d6dced2d5fdd9c0ac60cd80daf0a7ff79c00e2a5311d3d195a1f337aa45b8d6eef2fe7f7d5c41104e2115da206be37082d6049038111dfbbc59ac7e13cc2c62dc9b2380fae1a1837bae437343119f44f604f22fab90d240d98cea8281a1a8f7236b519d4c194da4fe803fbd722fede8c66b65387a3ddabed3cc6713bcd44142896445597f58b908e38bf72183fce16819e92754ca0e675ef28ee62c174b25eaf26a79eb0008cb78f2704319971e5be14d52f8e88c6468f7842e36f74793539ec8d16113f720c85dfce6692db7e16a2248bdb74b5229d5479f15c3ffe321622462db17a6274935f2938ddf3f6d03352b1c605a9724e6fb32420a52d206e6f96a19f2fd411eeca0e3b09bc671646a236103f6301301975685226e92bb3118a2a8b981c8115c0f16d4d4626d3aedd3ad782898e11d645ce4652dcdde51f6a17463cd9f552f222ce3ee38c2b6a96e011269f0f71c851fb00050f5551bad6e37b39fafa1272dabf2eb25dc518fd349e32c5f84a7adaefd738c1399441106cf0a8ed01f364fe9d67f1a4aa5df7ea131d4b681a5e6346b711af7125be4d1120ae490ad4b05beeda0692193d7ac68a467b6e4397538293cbfe41d5fafc1178c5ac56dbc25c82c7638461eee9ca794105c9949518bfb98e09051f38cebffb71b26d8f5d220808c08024dae92a2d64423f6f3c9fa5dc83b863c101c88a1ca2c77997ad6e174613f85437710bca74e3bc9fc29e3f89ab1f30dc5a18e482bc5c6d8b3b8901d41cdf5355eec6138c912368648274cd6170a78a57083449734690d93923b37a5bb6a8079f01e77a28314ed1740ee6ac3fdf18bf3835e29e7f97d1a354cf2196b8fe12c22d02bfa31d2d9413dbb1ce9c3b08eb517e11a3552cdca9fff7691a2d29a75c07ce6f2717e1cd8db1627efebcdac256b281a63f02f8bfebfa0530157bfe4e2738887c90280410baa959469ca2f2f9210467a80ecd0f92b181552099f58b7dd5ebebe97760178003643c50aaf0cd21680595c64eaa9a5b75f500f97afc01528194dcf3ef397dcc6928e8d84d4ac641447090a34980f95f3cbd6c58ecee4beb4e41fb2722222f5497d5011e5b5ef443b4c5a4ebd59539bc9d429a6c2b09f24da31d21728e0b17e9c0707ed185f8e2f1196aec867744fe2cde85cad59915e99f0f62f800248d5f8bed239f3b1eb754d014993ae9f6bdbf4c60a516233fdcd1001b5d3521edaf50082add2fffb3f04722c135e9d1fc9a22a760afb7d992e44f07b30172d4729dcc293a3d9279b2411e7a84efb0b70dbae430c410b61fb50bbc5ca1b771079ce1159f8b7fb2d3c1648dffa7d1949a12ee236e4bc7b8a774ee7be26ce15f6164c692a93ebe3ca81c0a04c6b817c40ada83b328e62dedbdd08636a53a974aa6d2f6b119b7c2cd3"}, + {"0000000000000000000000000000000000000000000000000000000000003d9d", "0013f9595ddb1757dbc7715fc9af27689f17061cf81107350bc6d54d1ee82497599851cbeb009e7b5ca2058467ff82d0cffaf605008c60796de20184b26fda0fcc01d3e1cb0ad38e8f8453b8361b6dac55dd23dc1b381fd5fd17bc9da1bbc24c03f23345bbea961c8a231bcb3d979eae5360494515a4d87a2752e8feef931cd01a3f4a9eb3eb5c52b3e65128fac11576d7971727aacada34db7f3f4f0e53909ca320caeb495c4513049a7fe9a5901a5994774296c34ac1556eb094201318b22f6ccc9ca405009e655737c73e016d80b513020dda2f90c8dc5e3b717e42e318eff05127f1526c4a53c7feb951f2562be1698aab8ed7f1cb320ddc2740098a3f17f20e3c71cbb4b5f0613580ca1a89f9c08b639c4d6425f5446baadb99ed2758e4668374dd140d12dcf1da791d95632eece3a048f4ef34fa8be85a0639d8f72dd8a46ba1f88684a071621a813e701f05e200fdb9b363515a12b6ca6693a25b103a423b5b20a102f1cfa62c4f4d5f9135c24a2b365981900458908503626bc229578ea6ec91f42fb4f2718def18de879203669c2f61de1e9976cb53501d3b0799cca216bfaa01cadbf00480dafe819cb65da139ff2dc5a11df73524e10b62274b416b19e1348da64afb99d38df90e831b9c251db7d0ebc8fce81384af3e6b3d360d5913b42d0f560c49ee47c5d83ac2e0917ed6b5c4e7155259027ed3b2d0e8115f778f52cd5475b1c52ee437ed4b208b6b3153a052f7240682db933d99f5032cd8991b23c5869e6171609faa8de38dadeaebf213017f9d34534e8d63552fc03dfeba7739e6ecb28e48dcd496e0099c45ca3d690be7ffcc920f0a402edd7b2498de461acb83af2f20b5a72bf2c2e3732cdea0bd61d704f80a213ffc1183b5df9ff7e47bb0aba08fa6289d61550ac7834b4c4ebdef82a481886514b328c7949788f4014e1fee5d0129eac988d03549c2f3b4df006e7a7e0a01192161148055668cf52f83bc25818fbd4e70fb1afcfaf6fec9c596522c52116c7f4ca2041051add82b0b8c6ef769dc93bd8165550a5bcd59869e333e3103eb46c2a7838b6cc537d8f631d3ca06ffb2583cbb06f0eef51ca711e15dc2f54ca0550b05c41c3ea10c1223c89bd65591b4decd11ee2e3fb11d1e4c9ee2641fdb1acdca932e7746e0c27d162facb5406118b1d801dcb0514454fe7139b021a50e22801133005dc4760e4a8ed20db4e5f1ba5e342bafb4ca35208a7466081162c9a4012c692db60ae17185d9f9bd19e30e16493ed695595de4c16bdae7c426e3ddb90b2a55fdacb5059f806d891acc3d4e76d08e1ceff9e4f6fe52cdc70a452e5cea92198d672a30ae41edb6a945feeb6c76175fd9371fe11985920db41059c5759650f777522718f447546491cf613b3db54795f97d13ab06ff5af201fd965696dc9a4985f0e1421b3d97dd47acb0bfe30dbd76fc6c4466087add510cfafec11a242674d7f611c03717890a8852b154014de75d582d9dbc93def33c95b24b86508c14c87ca40ccf57d0ddf28ad8d41205976d3e9272b04ff6f043db35b8d2de98f79d500f1bbbeb144f33497d9ed292ef2127b69ce793bf863132cae57bb4ce739b4588d4a00c717031933bb3889f3ec4e2de3ede4649d4b6c56f8a5d96f76df4fcfbae09607dbec68b2781fba6d390165d8c59508132d90e1f3282c24b0afbbecd3472752efc1cb9e3d73a8e691aad5402655852858714c87fd85a4d6ee20edc490c2e47ac4c9fe559c7ef8aa5fcf8eadfa2a70e7f24551fff61cb1e9c8459870bc497ec7b3a55fc12f57e9c234a74a5d212e54924ffe46fd14c093d5f44da59bf23a3c17358d41d1f00a4d833ea432ec670b1d9df3b5f1dbbe2d41985cf4eaf28d91ee5713edc82b83495"}, + {"00000000000000000000000000000000000000000000000000000000000000a4", "00a0f3484914f2c3215661ee32b01b622e9e9273cb08e8d315319131b0b7bdc270cf2c98a15085b2f36010634ac03b4d4f45f46ac22bc93187019160d0702f25af860e77ce33a492459923eadc06cadf6fdd9242090abf00a25a560958b7b3dba24519691abd1aebd71d56842ec307c307ad5b43727a7e97714d171efd110a6ca95806325c9992f1726032379506d9541f261211ea26b26fc6aa259332428d821ae5db69cc5bf29c05c49f5004e753d76ea191de2e0f3e2dbc70511b2a2a719c02ffdc7ffd6ecd471073c63de60e4a1a8fa22831a77127536eb6b8012423736da7f5406577fd0440723ec3e23d8983f949c81485d59846b211385bf710cf93eefe968a772b8171e1a71cff757c6afb6b8217befce6d4ca207ecdc207c938fa95eeee0b9eac092153f74f611b0f4f9715fa0b2f597dd718affdb776245083c840d83c7359cd932b35b0bfbd0782344f5300e33d7143856ae12a6003d6b8e0007992698ee7245e3c13969731c07fbd2fd7d99ff3896e73fcff42680636c5bc007429a5d0a652e8bde3ec2304c219d08c1e34270b945b7955653996fba3d1e3fe9de13ac2f702e0e697cba54ef9d18b639fd32dea627ced966f9a1062b9545094746fc886c28f2a9f89d15e2d5c5dc81f0aa3f4e5de72570b2883daef766b555a0bdbcd3c9f712793d5b4c7c5de281a6c87e3f0c724effba7e006a09f8b6e15b387517773ea3fd894e9bc030f18934ceba2c0a7d3b6f5602aa8aba5487882fc3fd9bfc2156b92526f8705bff18c127da69eb9e5314a8f05dc2596aa55a3143486bd0476cdde5bf1a608ef750746105bf489891d0fd36785db3aacf479fb82e33f033550683aa8d0fcbf15f34376d36adfe4cee17d7c756b1745e20f6a9bbf4f1851474d3461aa92b23b38fce62048e6e4822c5e49753314c1e3f9617d66a47a59a900bfcd6b89f7407ff163e889c96599caae081989db1644b3583348b17c6487a3b9ac6fa005c48beed5d71419d1d335093640ce37648d73f4bb4a2119b68ec1263c2ddb6a5eafe5d39d78d035e7b51f07925972990b6618d942e5fba93fffa20cf2d3caf677c1377aac22fadc9505ca6882c076e370789e8ff6309817392412ac32f388a28dc19f99119b3b2f7745ec969297bc1b3f6eb347d843af6d09d4f7d6c8b8d9d04a506901078ce3c9f7e19521db45c14f758e60058ede1a7fed0a0f54d5d70cc37acb02b11795b91139eeb55afa6a0f1a0dc415c5c52dd4100298e1b609255ba6fc399057174f5de6f6b64fea73959901f15836a0e83a44e4117fa9957a4e4188e29d434833f6a0f6b7c59880c815dc7fce56f18a9bcdaaea4eb6711f03799fbf1e3d16175965d851af7bc7e703e832e7cf5586e558fe9328f6d3cf584be41790bfe71c72ce7b85fee7179f7b016f5dd587dafadfb2fa804423527cb0c679145053049f40a27c1299b7800443c0874e94ccf09ad49ade1ef65f69131b042de023128ca3419e2946139dfe7b2cea06d66d9ed0e9e026238d3af4ee51c7522ef18e05e307c2dd236781826f71dbfb106ba58563f260e11d334e689589beb3111263377afec3fd4e763bbc8644b5fc858e1fe63f024136d202635722f4797bd61c487d45a38b95b66fade7350b81ec3ff6a7ca7daea20351051711cf19a2fcfa04f34d3dc72950bbd255ee05a03f2614471c86c209154d72615459b418d7acdc31a33f3141d401eabd1d8793a74684ba575f9c01284d4d4e1b4ee83f414f8c569963528b330d5b39ad740ddd96d7e06ac64186ac41f0d5745aa198b4d248eb61dd15ba961d201f017459bcc67583ff66b05ee858136cddf43188d8318b233158ef5b0b38744969d0312d38a58ca30c074f7fab973857c4b5b7b2269dc051"}, + {"0000000000000000000000000000000000000000000000000000000000002d03", "001b01791c29861fac40658af2c22511a9cf3f5d21061acc6d1b57d59d5ff08257903f5a3973e658a0fb09d1aa56405edb6ba6e5e3a853b7e2fb19eb7c6ae932b4ded5895bc432ebb9a4b6003f639e3787bf6c1100ce7ddd5f428bc7fa7f861b1ffd1cf2ed9d9f4d4146c57375f35748b914e018202c4a7452664c948d540ce80ab222a7c8ab40f182dd8840ce9d783378050741b8938ad89a85852ec284a20fea77e9cc2c921a3e0582585965c9a9efcfcbb587f05a1d5a27d0b51f281ebd6fabf4d5e09b814416e823c9980aa1475abe3211d667ca464718c32d4e613a2aaed98d11c8d18a3850c822a96655f9d589c79599bf36382e41a6d3b19107f39dae678a1a6fb8dd12f71d6be9f1c77c6f82b141d68a75aed8d979708145b8f2ca14e9dde559a18611453716d1eb3c9db45043ea637ff2b14a7baaac851a4abcdf2791f60b32f2339b92c6385f40991e9def003c533e0d076f21be15a2c0bacaa81e28e015d7bb069ac1052849f0f33911b3079c9a1c44eeaaf8bd7b0124de1faacd374b92add4de04c794a9a748113f233c169235615ecb2b97f9c67e9152d41bbe947f8c840211567aff550f97d11f241c1ac8d5e9847ef4725319dd88fc19d11b71ec19e85f82ccddd63c96d5d66406a5cbbb26d3ed05787540c9617e83b56bb6923aa61133df8a6716beb369a351c258a19864b87626beb40b6431694a25b563ce1aa0ce2f7729650f66f4e26e1a85ccd2b7ab247f84bbb372a5eb801924d52b6d11172c643a23ca447b713db7a3e53dccea4a243fd2381a2c0555b9d79c85e667f28f54f9689792147f0c1e1a53adc8e526a6c79daa82f21cc4e6c5eec910c2961ec08415bd53b5b5e890e6402275dc46b92399c5153c57f63c361261f37515a7b77a5e90230bea7db9ed431f4ce7841200cd54b1447afdac752740bf5aa78b003866fd56c248bffa3402a17fc8bab5221e3fee32013bd6ae2b274a2da2a162a40b564625ccf1f45dfb02a4f3e84894608dc5b53286e9e9fc98b799dd015515d95ae0bf6363e1e62694d7fdebe9aad41479ea90055831124d82dfced368d42cb2e523daba1dfb651a3b252578fe275ddb71b9944d27c20f5a27e11ec186138b6bf02e8a25a0672e643c9f58c2f1c5827e6ca11559c69ebf14cbe9e63e119801a9dde8f72fdf234400cd3c9de515df634cfc706de99114fd230a513f760cc51347d1581576c12d25149c416875e7fa1ef65d11b94fdc85049611a2703369b0fbdf393a54127ace12b6c57a35e6bb27e9b2859a1273e0ee0e3c3f7672064a912020c7bce9ee26142746d8018a4dc4d954313048f23380520b0192c3c70a7be26a3322605b81820d5b0278c9d9ccffcd1d66ad6b7831ca1d8695bcdd275e5b3a1a9013b35e8e92c9b5373b59bc3bf2fe32007dad87d603ece90737f7b8bcd0e3a2991db7b0240873860b58923190edfbfab8a5673e0373825ff7ae023fbb90bf825b30870c80e4a9a31539ba5cefa708034a64024815065d932f53593bc5ac9ea6887aeced0f886d904471fbdff00c5155c4fcae399ffbf55dad3231679372e97d73d65fbaaa36da64aef236385ba9118bdec5377046c3a163841b81fa4cc326c2fdb9ae23fc2238d5530d097e58e26ab79fdfed2389f8641d053ebc07352c9febaca0e1ba2c2af14d0e7a1b374822b05c9e929fb38b7cdd93efc3fd54594174f94eef0abd8369a287de09e4e8b1015b730011ba51b178a00c198dffbcccae9ae70d93b24fd6a68dd7e1dc7c8408c01f4d669456f731f1b667c4fe75fa08443057d00ae009f5c95eeba1e7b1e29fe628951a971679bbae1c30d0ffdc074c775679033c797720ba16a8327dd32b76618ecc1591b1e593d9c5556469177355bf6869"}, + {"0000000000000000000000000000000000000000000000000000000000000ba5", "0084253538de5f112ab556c7543e759a170039e61b248352d419a55a4bce3ce68030d28af21e0d9fd9c31a71ef908d499222d34b360d23f0e13203699a60e9358e0a856752d0f19732fb60b26c38cb2ce73e410e08795745ae6f3a89da3c50989689db5f27fb9cb92d36dee44351ec1307d7fb86129b3372cf8495fc2a830c032f0e210f1f9dab9741103832d1d9c7a793756b143222e709140b69b99d83b908d261c331c25a88c601d2ac48d9c8d4dc8717803aa7c336974d4f5ebf1b076fcf47a5eb2779f93cf1b1ed4da09d2d858b16102642295761dc41e3568f34b9b2c0b7562d9c93aa729915d7444f6a660b57dc6a055d5f8eff087a987d3621baf2d5350e57db98010981f84ff976899cde7ec6328801a045d191eef8cac4ab8978f41dfc039c2ffa3e279ddd769c37c951c534b00ce3ec6640087697b76b35a75f2adf32dbd026bc790c73677753a53b0f6e013b28b36e5b6875b3785363fc7a0a054ea17668c41de19362ac9f9dfb3ced4448a842484aba7edef981149073118a646b79e3ae11939b1c910da9f13f63af1650853cd56b96697dabc1b1c8f1f3326faf53ae9508937078b007d116620088169effc0aea3005c723220ceae62706bb1879d31a243162a9ab562381d83b20a7ba37de827922bce04f2cc43a8482df2e9fbdf9226de9bde981f13c1bfbad42174fd8911d959fc13f40163b28ae192ffff999b901e3aa0c7b82d4ce4fcec10d4db119ddcf3d5d49613d0c7c55db7932c3f916d09a550de21a98e7fe729213aafcae03577e08d17e022648f68d4917e9b343a63319ce03b29e4697934db083790cf9e0f49c5f0dfe1cb01c09192d77dddf35e4c960784d9db3b0357b457f8c25c520e36a138627508d545b52d978f81b59db63028c38e0a40d933e80929d8fc01b4693a7dc6a5530648c32e7647ccfd62a40372924e36dfa77fa3acd20e2a42d574a20615cb7819d061137394350dc0b4451db8b9681f6fe27efd7c0868a397dc49d1e566a1522670fdb3598b8835b2fd4457c23e9df120a3edc1d76cbceaba7afd03f83eaf1a7a39c0fd3927bfda4a13beda27958d2cd36e391859f353c91a259ddd61e9bb04fdfbdaa2d6673d9f6c3209ff79481e3e638ea973e0d7d77d6960cd19b31d37862edbabf8793dd4c3e62b6c6717a1a108796c210459e92e700fc8278feef34fb1ef053746023ecd8d0d41107fba1ba921888dd1388ea193e2f156db092d166f8bad940a4f5489f8e60875e949e99f811d9714311fdfa49b13f185438e99c1e6792a07a5d83eed8a130c1db5d7d57c10fdc8f2e62645ed28fc27f64b15195964632152c955656c32a283616e013883e9def526dccaa8fa54e415cdc7368f38dc423e2b1ff297d953190d45eea4aab5adc0f93fb4e2eaee5908fc7f5a0605bbb5485cf0e3ea58536d3474c5f5ee57f86c684a8c16d4d5234d23ac5936f54cf8e44a09145b831425933fe28620998db520e8c027fed3627118d57fa82e4e83b5d30cc85be5b868b133d2e2f29d1c18baea198df7d4e86f2445e237c1bbc98e73b8c60ca87f7743acb3e6b472a269ab8c251ce0e322cb5b10bf75601ef896e951e17cf9256758d06665d04eaa1e9847c12338abae401ae539ac35e2ab9cffd6c986823c2b630884ed28cd8378c62b8835c23cdd99b9a26fd2c4731cda744270e5ff95dc97755a566d25f5a391b6f07d222acd9bffd572ccd8cf0535a27da96d92a3b2235825df6a7b09d96a99e99128f1af72f9436c945d1fdb08c4e638081069b7e1d396a4bdb65a8a9f965ae37c2accb7df552ed5cbdbd9c4a4edbc32ad41f5f2665309d8b97e4623ac9f6c6a40dfa864a0c90b6f5fc41c238e273179676f41c94c750eeb4695624ddfb967a8"}, + {"00000000000000000000000000000000000000000000000000000000000008ab", "0078683e3e9e738dabf610672e7e03f8280ac7c5c9401285ccbde1481bc7dc3c9bf3feacbf65057b9c2a3238f3b50d6e9eed9fb38736a1c58db6b3b83f2a8560c94c497ae517355661070eb34c129a4db03274d6035d44c6c4de8957c919a7b8ce78daeaff6c9f0e7f2b075ba0dd4d7727473cc2efcd51eb9f4a8e1e5ec305fcd31d7ec184dde8afb132d9632ef92ac2164ce90799c109d8058f3d61de961799d7d0dac6207fb782008ed49df8852faadbf9d58f0232b45ea8c41ddfab10b506559bd3becf5299b338307f016d926b394ba2069337c2c9666659f7cf02b4f4411379ae5171049127597a686f923ee1c55965f003de99028b13d9ac2a04711e4e1ee6895b42fe03f4e439d5ab2a111ee08c0ebf7b86261554bdc8f7f4372ecc16b94c8d13f7ed0d3987febbc5c1bb6e510576bac92c4315bcfd9a761be512cdab1b1ddbdc96532ddb43b011a1adda09cc0136dbf6da6235fb335767a85e6ce0930b78bf002908250c824b8cf472874dd1ec9fdefbb9fefe54bd8e0342d2dcfd11b900bb1a827c6545e11cd985d3aefd0dc5c1234c8989754d6f08fe1fe8e4ca993b1b378102493e8462f01609d20530c73d492fc92851db0db559e08f9b59a590c78edcb637c4e81a3e4905f4fabd0c054d7d7118abbbce02e1f09392936d40bfffad0529a1daec115053318f33d51085df49aeadcf15ca26083676137c0568c2520eb346502772fdd8b55ab3d3376ec2f7d6a39f6dfc5e9492bee7930e0ac8b3c91609fd62c20b84fdc2366fe16544f0df0859f73d35b0172cae239bdf42b31cd362865b45177e8e5a9fab9309bb99a19fc898630f04327c4f5899925f9c157c224e87cf5c12ee84edfa7105de185e9a65f2679cac972236e670f3d8df257e431b1329702bbef5927f1b0932422d2b9a28b579ad6583dfe7344ea92bf27530d60313868ced554588b001f15deb9251ecce4e7cf7322043316ee04c3dc13e0ee2eef44f909a84e73c365911c9510d7776021fcb1d25f03e69c462c0c69bfc061d5e56337f0f4463750d03f520be195a346b3a166d0d0713d248962ef9a01673537dc1f318f8c7c930413e6653ad899e459d22d6f71da9ce2c2dd961151f112acb45d97f2175c180bb081ee768243f73481e929b65823b8b8faf34097fb448753961bc766baa1ceacf09750d4ba5e1ab9f4c5a338595352f014ce9fbfe2f0aaf86ac4be52f69cf33315313bfcfc257f434e89f0eb680a776124d6d0b65a186f738fe0482e729742013a4751d6f07150af8c632736a9b174b547a3fefdf15f68b8e345c5c1d429761c66e37f5cdd945544af73c60ef3a7ddb7535af07a4e0725dc336260bde72c71c37f473e18e92d7d53fe58d0e5d968aa14c96034b1ce0e49bfe9e48317728bb340a7b3e5ef4e437dcec04c66bdc5aad383bdb7c1050759b32b8c21cd4374b39b8631712366c0bc83ba623286b2331ed2bbc8ac216b5727c3a550f58efc1035f5ba9759cf112d70bca4a3c5f682ad63277024284d8f4eecfb9cb537ba85404c9b6c61107b84d82213571c23f505d9c894fb2510e3dfc600ed2ead99054a173ff98c888b55ed44a0b085798a50f4e0249c64961a0ba21e4cdc46ef8092779e2cca722721571ae77e842cc62535a7e72f42b3008275931b77835f1c59e1bd144effb3b8f5cfdd4f7088964c7db54e7f1e3b272b6d0ff7d88b6a7e9221d3a20c26dc4d09561ce26344eb6755803408b5aa3b075d34fe6cfb1385de1802a67cffb8aa38e713e18760cbadab3bd1c2f9954a9715d9fa2b76a7393fe65f61f6ae5ac9d20883d268f49fd98d50d3b118f9fbfa84c03aa8c05a1f18de3a3a648696184ad98305ecb0759684bf6be1b6951bc3d98c07b6258eaf9849e27af"}, + {"0000000000000000000000000000000000000000000000000000000000000b98", "0191ccfa9d67dde9e6e144d8e045df95bc5df8f8d913bc09a21b9da2e7996901c781b48dc3273dbbe41b0c7a3c6cf188610173c0a1b50e3b353d302b4ef2b32962af06a5a710d7ab6036d3fe68ed7362c61e6444038e0793479b9def96e7570e3b69c83a99ae1a41dd0e22c55a8659b68daf2dc10b71f7a63998fa9782270ca7254128d23a729693e49f50aed3abb8047f392f1b244d486bce7bf1ab0e02877e6393bce78209ef4f0645e4cdaf5c775d24be0201a5d4acd5df0034baff36e31a7eb74f0914df2463798fc2cb96c99dbb22241f2b9ad90d8d7a553aff234a8f23d9ace9baf040c11f4bc4a91ba10485932aa66f3cd67adab569d994630b5f4b094aacdea57913d2ea656a3e1a98825829a03e793e841b588ea6e954b422ee573d2a6c27d4fea41bba7ee6384c4ee7cc1332eaa4de877b3af49f18671c372116e48b72995b8bd25eb2381ae5ab202dadf20338bbf8c491c6eb024c624d73bf6e6ec106bd898c12dda4da6064466d8408623eb1624be0d32cf44ed31589856fc7c85babbc8eb3288544554eb570db155444d4f2717962a87519f2761dff70d1fa5a089a2829052c7f048a8324a4901f74c1ccc878fa3e42f75fb70b6d78df8bb93227e7240ac446fa299f0e1758b8190d905952b92078d3bd6c3479cd75edc1c6444fc4a81544d559460558f1715bb18dcf254f15892e1bb6c70ce376e7fd930ae0a519023f674238c25ddfd376e71533117c2d4a9a71ddb44406f32f030d5521fa8a2827109231845a8dc70c05e4ee77d253e1fe6a9e191136b16513f91ed5a75d7bf3e19633fa3df54c3d9a91123db977e8d98fe78e94e48bba740e7e2dd0fd45ae171c5663c2cda71a85adea095aea6fd3f6075fda9b144940cefdb3eeabe3d0533133cb9c2b1e6fff0b881de876dd5b258373b3a3e4723feb5349ac04b1bc11028132dee0e2f7014ff502282c11d21a8a82566fc122d87be6a9d0c1913732c2f70b3f55bdaedc16e8f006236fe315d53e11bed1845d14c5e57a0df474679341d7cda1357b33ebe6ae9c5b9d6ec98b8d8a5edf9b03b48069a2b3d7cbbe5080eaafa53892a444b962d905d332f54a81f37270f0d448f739ba36214d1da52f21280520e5dbb497003536f0e4da089b65aefc3e8230319443135e57c766225cd6d668f5db61161ea5b206d9245c89e3de639a17e65ea671aff63b63fe169f115082690a0504c518fc21b3363e4d648f4e47918707d8ab013303e9883e4e6280b0ee2ec0fbc978b2dd198aae8f71d7e223dbf643cb7bb97a72155f5f277e07f2290d48cd759d2fba7a1c7663d37af447fa35a73797eb4843eaf309a1b6159957ba8bff75049bb0230c8f5760df859c6e52a122ebb4508909b15d558b821dae776b65505d291492354de9b6872d84cfd4f9d406701e47d58b116965cf92374eca417902827a5b801b3917c9098e3635785b8261ddf79c50f2e45d23ad11d16a5cc8567e8376a4d1b3c7f5a82c9a8bb80c7a60e7e453392632d9f4737622df74475dd9ad33b36407156880ec14d3ffb60882e7942a7cea9cd89a3cbb3aad351e26d18d22d7e9043d98d14f3ab90bf69b612794a22faacb5534a987138acab9e9e92b955f9c503a975ec71da87717a1ab74d4a3607b7d77fa4ca1be08562f188fe0938f6279213afcb98598817ccea7675e9cfea57e57daffab430753c6432aeeb2347e792e1e5a6a42b7e0d9715e5432e2a74295116220f86cb42b413770cb5f3cf756cc84c9a3d6ffc6ac61f69f4411bec7f0725583f5171e634818ce2099753cb4a7b2230ec40d21df63d3d15ba33aeed945bab5d3d6948c211c2b132d6254e52879565890e33012fb67bd23f4296da6eb660b70011c6775b096422fdeb006b7b8ef"}, + {"0000000000000000000000000000000000000000000000000000000000001f85", "0006523a496062e91bf9c3e6e07878b525bfa9e4582ea8fe2e7c10432bcabd97c3b25d35db64da7cb3280ca4a499eecba8e969e4358547458d9623ef7ee7191504ef48a3a4af3143a851faf39e4361763baec4c700ce15b79d8c93711e8900f1b64e111c7605168fbd0a65e48a19cb30f52a8db377fc73700a2a2379e5bc1a6b9977a91a12412e427563a3b45ed373f79d933c2193465cf11d22f19a1346347d62e2a26cfadf9cc3000ac97e74829767fa9a108a049fbe1d8e69382f120b6d290b5453b301def0f5bbd0f640f1882874f7e519d4915e3f8ea1767618d4e43076294e5c713495055939ffa71218b77d279ac7b1d25a02ce887818eccc03e07625c9d7ef34d8f85460cbedc5ad48b9af804845d9bf7fae9d9a1b4bb61599d4bf58b9fc9f5c124406078424491f413bd171e12d35ec39ff2715ffdf2267495bf584f4fe8dc22a67a01d41d049e98dfd635a0103bf056e61b187b08b2515e14aff6a21fc7e67aa1d176bc6704ff9f1f4b79433e642a54938807db0af0d7ece76e98d6629054e664dba4cdb49f376563f102bed02941c69cb357c22a6f52a6bbe4fe7d61ff1580985ec2cc6c8f0b99a4a819c961d6cdc81ab7aa41a0e4c017bf8bb5db5eb66256e2bd012b18245b7a3a50b611c7d2ed32497d8e142e03060a12d6e51f14afc0baa57da69cfff8b804c01b3bffabf4784ffdd2eed0ab72cbf2bd5eab4c8c5c4e40f30d1916bbf8d9f4927b19af49ecb842d4f288308582bd03226d2dd69751eefff2b772dff1b9390a25eb5acad59c8050f5ca21fe4fa1444e8e2d19de6238a7232394de96b55d1250b6cea8546052d6f96e91281715a4456297b79ddcf16139e87c411832d292914012b3a19d679e8bfe66f1f89f1920967c91b4270e2234e7774ae034974be40355f1efbbc12435afe9073d1c3b693610e90af1902000a1b493eea05455c71315fc4722256d96c5fb8d0085dc8aa58a70e45fa78c5db94b347366fbd3c999907f62b500d058df3dc9d61b6ef45b81e322d3485000dba3f4e92966042f4891176f56061f4619731773008346eb37ddad6170b08f40fe5755df384b0bdb4f614be5b8b0b52fff69eea733621401ec4daf63b68d014e58775b0cea898ad5751f3b47c9cb5cf4c4ed9386cdd4b891f671fadc671785462634beac6fef7cf0f0609022e47a3466bf793b8ccf860f1de6be17917f50ee817715504db079230d49b3cdf7236d79e5b00b50920112add9a391159ee43c1592de2f315e09be36e21c15c1d2bb07d9be64f3a865be0163b820d3e5c1b0970fc212205755c3cff1168051e2d70890a8c5bb85ca5667d859936992eb017786ff491d70c665a52ea1bc4f9f4198f23968011f3cad7b6f8a1455390c0ac25ad3d43a1de749927288ae72af0a48ad0fc9929c2032e49ab81472457aff7f2fbd29ef6d5cd4895932c17d85a741f87b486db2c95394f74c4ca6df4b373b11326bf048aa2d7537c34c58e4b70177e596b99387a1e2421a40f69c975ad9643cd81213a85b3eb3dae22067885f530ab72d9b982027bd7a85eb604f9d684f54c6993b31a6e125fc8bf262ab765bdc362761bdfbd1fe70caa430fbf77793802cc279ced84c85768fea932c0ce3a53cf6beda01624524f5ff6ffadd2de7dcd03b7b2c0c11fe07dad9b0420b250a85efd09bfe39d0c5cf8a2da87bbaded97011c1479117ca964f4196e044f0eea548339c1efbd71e98758b82db0241ca96c4110d5d988254d19f4a4748126efc90a0762bf193214389b77eee560ef58b1622a20cab630af8e6f027b2c8c6fe6cd58bcf1caabd8e8ee708b9a9dacd8100f1b7773db08a6713fc9dc01febf7a8948b83b5ca74c32d57f12a9b5ee77d808f4e455537bf9d8d69731ab"}, + {"0000000000000000000000000000000000000000000000000000000000000a65", "002a908ea8a042d1102da06927d821d44b39cf3a862da4da0e999a805ef49148dce3d077ef0dbffd176608450a6f8c4ce488847e08420a6e6ac7384e7d49dc2294d9c29349bf89a52f45cc7f667805e167dd9f7c0e39c0a1d691000cfeb5325a2dc6a2532a89b9a3c714ddf8dcc1a934b175a9f83a9861335f33b17b31db1ab49269cfb32629a659849c905395f31352de58124ee68e1c1269be71cb0de5fe063a9fbe68efd6e69a0103202d9904f258323ea8b79e4ddb9e75197f43fc0d4b05f2e8a642815ec53630cde9b126d3e9fb77b70beeff8001c7c2070ecd7300e3f8eb99004cf352201766e69cbb2289af90583786aff70d8a4d5d7bb39502c63a9c3a2c64e98eba50752ebbcc1c8c49fb4cb3079b8a2eda648399ee49e86636634a36d9399e70780664ed810809fd8166357277067b9421ca611fdd4b0742565fc4315c4dd0f8d37450bf4fa9caba18d71a005f197e176362b589a32277dd32917525eefd93d5203a252cd44c024dc8c22413c1ca6eae3441f5315b00f52d37eb01197c4bc1617ac064e661a1be7dc29e2162fb001965d1b135bcf7a55a7f23860dde17e4980a1fef24502f7103885d416ee9933c6d92787bb4d0323992f452950106acf074f1fef5c4121ea6bbf5eb20da24b1d19e78198594545286d67eee81863e95522d603dff9b27053ff7cfb7bd116061ab841eff9a3d06dce5f711132559a65ec4b9bab2f611436cd084261f376301ddcc21f5632df20b2d4624bcf1c3a9db221aca541f5ee747179b6565d784c92b3a73eb9b9ab3253d2241220c7fb4aeb6a43101271b49888b53ec0312864cea91a25ba576fda2146720bcaaa5f917944c448cbf6689a07e89828a55db6d39f6075353dd6fb42cfb3f6677cb6ed9841c33f01b582c1f2fb7fddd9d30da36b8b1165068ebc214ad76a82aa308149c1590004b9f6b114a03dae0bff2616c276e2ccb0d5bd884239f5e0da69f448be12fc26f10595aeefc82bb19c901afb7d2864f157ea19b408c803c1ca60ab2f2a8d1107a7a944825bc1f525ee240125664ab4cc3fcab5301204be6114e73368c80221cb154b26b85ecfe4e442b14c556a19b58f50fdcc7b427c926c67ad7950c310f983b8509a9cc1183e7833402fc5e6d603b707716525f5e5efee3271bad28b54c2b648d7f809a9d06f501c099fc3781e5fe4c95d1ad6b2c171c7b027a5cce1fa91eae8a5925f5b90e72ea6b659f3d2f1277c7cd023500ffb520fa5b7a66b4755771379e45df3fd1d64cc7a570b3e821c34fec14e9ef57987e96d5dda1c810db2e37d785894b0678675a00e8bebe6f6fb70dbd1cc1cf5eb1de297912a99560e5f2cbd7c9669fb75b1502aabecd7324d3e4485406ae5c31656bcaf8c0e44366d289c79f1527100be5d7ab379ffa0e0439661904f0764a860d8994fd8479e9d7d97eeeb725de3ceb1ae338e413a1b341ca47a36d595384897e9e78b0e018835b7ae2c6cab83f222389dcef596936cd19ce47573adea1d7eac72bc71ea5b569d0ae99f23d3753b00c6be5fd61acb463c68e5364a02eda916c7c543b75176de7157f1d82fefd62923ec4795f720aa213a4ce10cade890f1333549f65a426c53bf1c5f8fe995c4b3a1c97e58fda648312ffe7cef4edb92a5eecb6ee6309c027411f8ea0fe866693029ca68462fc48bca50217189c947f5f3d93532432beaba80020e1c14f22f820e75b2a78cdec10f4816694d5d41d3e9a7c7c672b3f76c47144a7a279685ac792fdf24322903e9bc8eb162dc1fbe954c90cfa4164ef9273ac6af79c782ae560089781f8f39f7bb48bd8fdf7d0d327c1a0dfa4c2177c83f2ebd042a3aef6e336dec062cdaf36f6cacb1c59dd1487a3e11364d442d3613468b8ce537393be"}, + {"00000000000000000000000000000000000000000000000000000000000001e0", "0019884ee8a0fc475b35817b1e9f98a12961fd1783238a62f800d7b82739f8c71642ded05e0519bf941f246983199d157574ef2d124ef7be1f7960850b13893e079fd3a0eb2ec782c56500c3f84caf987b9e150302e25bd9eb47e9ee5c4362e34ec2b50a45179ff67e059e4130334bb88dd76041188e5e4069865a56068e03fca6c8a0e083fdecc012d4f06422559ec7fa5d5507bc5a190a93222710f41284fa5096960c709e0cb1118b870ea1d4be53e059f3782656a621822f5b8033222c1749bb64e337477694baf6489ed3019c3c08b514888250fa4dcc3f4772588c0773db2a715d7be4c33fe75c4dd49d9cdf345b69494979ab7f1734df9f71123b09c916d08fc7b5dab1289d125e0481cde8c0e827a64c268255ddd50c5164a336e84ff66ef7feb1ff19c86d91fdd408bdb5dd03389a616fa1a278198a491c1bc1877b1b68ff6f7ff2544823b1918f483db6ae043ca8c91b92b9d5f50dd19671f70b89129d1cb94b05d2ead1ad054caba0df0460a1c1ec02e8f71b1116266ba1ab6d8b8007ccdf42b34eea361b4e04ddaec42bb29edbcfe4bd7137851339d79be7f74014fac5e609eced7c008f5b09d5dcc2dc2dd0f9aebe395808c038d53285c41ce11d317a14c425d3a7f2bf1a9a8b2f2d3826f8661d9a12ed87f2e39eadaa24fcf2d7ed5438088bdd9c24960dfe41d8c649cb554b36411a9bd20533971cd46b7f0be1a1016516f50a83a00b9d06e36f7b86d5d97143ddd389cc8c95e58c0f656d3e68930d44e3ad7391ffeca345326bff2f122615549f31ef37bd734b19325463c8cd94fc75dcb1ddbe209934ee101df2fbfeae0a1f9e944211a21f41e8b4ebb266b74cbb4493eb9d8a21cc0fe8b0f0eae82274585936e41362a3dd116f6733f28521aaaee40740bc370e1ffc258739794ea196dbd51245dd5c4111edc0843818d5002fe2df9d178c33c458424f111625b131bdf8cbd300316fa7b19e2a2f6d02a1bc1c1beedee5cf9af596060c6adc8e5e47a32c20c1bd0d18cfe5f0afda6feb097d2ef7a6cad5931ea560d7d7beb82a137478ad8b0670d8f424188973e0e1c5497bfd18d674e71516d62be6c9795a6cd9879ac666a0c2c56686093e7201c9123fd5126d3ddb49fd8082bd1fbdbc52c38eda38a73cc37a01f814eb95619a6415e6b33a4277061adae703bd4483d1dc0de7edf3346e7ee4553768363dc18b1a3d216aea599a992a82c844e4649ed6da8e7d454609dbeec6b8d412231dd8416e756062c171ab7a65a2239f2c31fc5c87dd5a5ee5de6d542acd8ef3178a200437f2cfbf0f4fb76872b11e79b8e1b4c69a93c8d3093caf4c1d71e9eba08963627cb63685176e5faa8c0bd0395e3c97e612fba0a14d5def6d9d74f98e08d029c84d9e9a35f037ff8bc2fd45eb3961c5f05a444100f0cddc0dd62c86f002647dc6baa869ce5efa72b12494a6c71329769f923a232971700b1dc867908bcf11246e496c9e95fb1c7783a7d93189556d5e136d0f2a2e6fbd3ff59dcfc3db22ed69c873d51f7aedf7dc0768ace4391460c11afe1148f8e2db70a3e333231217abe29303de9ab5dc48c17eecd6f5973f315d61b50b06a0fa544471e49ffac22dd56bfcaf1daf9b895a0edb03f6e0654187f05901eccd95589221a6b6c989069174f8460bfacd3617e322ca1aeb1d10bd68a1210c4b7c6bb49452ab84b6443e7e41f2d21b649a67d70ca34ceefbb7c7cff361f26d46956932cb2b1ee8993ddd442e6b12d45eb8d50453532534fd4f0a58e57c17a1bb5371dd86f98c5a048f2b6dbad14a3a745c1e1afdcbc17a10dbf5c91c522cff6d0cfa20deba878318092d1eb4f79d97bd3233888aaaad433c949d9e7b1a0d5f12e8676377cc8cd1a97c32cdfdd6dc9067b1"}, + {"0000000000000000000000000000000000000000000000000000000000000a2a", "002098293c21b2afafe052ad2ac3280d2fd75ea6300800ec336c981c77002cf200b3cddb6706597c5bb51f9379a383c8ea628b3893642153ca19ebe45eea4f4b315dad3ad5cfefc2f1668f9d717345aa681370e612d09c884f2c0f11c3ba249bb7c47dbb7caffcb29725b315921b2ff11f8675c459787c1c7efb33b9b6841345811784a1b21dbd133207daf652b117a18ca1253a2d953c7d19506917b0e77f16f0c61efa5318eeb8012a03db979aafa9aca568db25e3927a6eed3bd16901547fc7822f8aedc57623bd97adbe396e3b99ec7324662595f3cbc941673e26c49e528a069bec9f939e39ae97ce9cf02a01ca20d5dcc05ddf8fa36eddc9400882a424fb9981f3bdba3382efbab49d042a0ec5950e363c3a0c16c911b06c72a706cbd9f115fd7a4d0d2b017c9a04a290652f0d237e386a5b09bb5e58eb4e376a4d3149a106935ab01920c37949ba868b591045027244c960423012c9c630c6d3f13399b0975a1b503b5dd52190e25b7b3332a707133d17567507fea39403d945722e063f6ac21e10956554084a1a90d816404b994c9c6be197af8578157e23b283bdd7037c9946142b6f0d519bbf73f05e018f2b4f36192f70b9477219627c9e7d479c037c01f19bc132f98472b34d02e01c875a21dedd5af1423e11eefb2d67a1a704bb2ecc8ff65dd326ae4fc5ae748bca536f1d67795d9eac8c03fc18d34474ec07abc82185a48c8478df33d1b0bb28e35bf180cd36388d2f95ee7fc6a672589b56cd970dbb4e2f558d87e5a086d4229273030ddf8eb6067127e057cc12746869ee8095959450541efcf79ed1e104b45b4662c168b9685993938527b73e72fa988e7e10720180b525d77fd14281c80c67378cb83ad0cc380b60d983a708704bcc0302d954746c418169b1769b42de5c449ae123fb2023a4cd33f3397f1990da25610170d591b2993e679ded84388f40e9b5cbed178cf31a6abc88a655c8f374ef239ab3757a498b2e1a1ffb089f82c48bae0b659699221407a8e0a4cd30f6fa642c58cc164f0d0a3eaca112daef7ee3ccc675088796113d6e9ce38b655ef57964bff5c2105ba08f7f807733e747b51e97c7799960441d793468726cc2788b211806bda7a4e03c837c7d3594c841b069d8955b5a2f35a71b9cf1ce1770d26616e895f65b626917dfcaf2033da7e7c2d47c78e926e0f0d84a97ad5cecb986672442d9b2d8a94d3578a7d2ee56bceaecbf66bef3fe0c6ab878a0f0ca89d1ebb19eff1079cd48a33a2d680ef0de49181472578ade64fda5c4dfb70b455ac6cb11f3cfc67ed87d45308e31429e5113292d36785210299101ad215190029cd192a1c4e92cf0e0532bdd7e174dcc9a29d1f7c739cd33ca7e6d4b52430dbff50d1bfe41a22421ef9d7ae4747cb934962dee2d112b950678d6cfd28947868b84027681e2eb86baf35b5126070483adb8ce6ae32f8ec301cc7977a25feef877b406dc2dc7bfb9b907fda405fdaacf6bc6b5643b9e4d1d44ffb75a750847d2f2b433b2ee00f6faa63bddbf06a14ed775c581a2ff5a494685697f92d12ad92fd21a699ecec1c9c3a3acbb7555cb75617d5cb77b7d4919d0c24c5a5d1ed93f48e58770ae9d5624fdfcbdfb2146c42ef9777be1f83c08f6997a795b60065babe0069d1cdd3a556dc3fa7f30dbfd67ff206e00d61b7421395ab72669863dac5b6391594aaffdcc849f5fb413e4f8c38b0b77e77050c41105571b520dbad9e3201f3afd20940d0f34d64da3f78f3b010923a372f4160be63f296aee560185d6f2de56c5bcc1baefbcfae0128bee7d182ddd85d05612523076d7551f4c774637134a618c6ce621ab644ac33c6dc7996d373ddaee7a4bb6ad2c91a4f2a1c6640515af335cade400f6e8f1"}, + {"0000000000000000000000000000000000000000000000000000000000001b0b", "003242e09e94e78b29be96cc5b67ed0775373efa15482e2d90706d5d63c0cb853344b3c6a190de5395e200bdc6f56270ccfdb36873a95871941987f50d6dda18153d3b31567efcfaf1657ab2e8b62b2d99b9f5d7065f099334f31133ad06e22d1c17c601144aef204f0bd36f151288de93b0c7c56e8ffc9b9961e26ec6230c7f1eff814bf2abee2074988d267baa7985dc59a010905ff367903782f286e58d922fe199e8c83fb1190304a87687ca4e75d6dc2811604b6c9647ad766a503919afe7ce62ed57485b060f00df1076eb3f5c1b520519039f7210f6d782d1f3a9feb5a9f18e2c1f6013354f0b7a03de2301152583804f76a8a0f37e489dc5046fcacaf5a88fdb7dbd93635d2cb73288bb786c9708bb7a59f2a274fb9c15878a3565423e83a63d26150e259ddbd1f26ed1e178542c05fc6445b094fdb665178a7992dfb06829dffa96a6c270cbaa43f8b9904a012417a7bc1777e55fc2a060552e53adc8c49553aa02b5fe718fc4905944fba8596be4cc66efd0ba6e39125b092b3704ed5e8d7ec3b0d4c4d4667d8d59328f2cd38256da721525d91ec2d67d5374d97b309978e50221f98290728e219e5ce1c42fbf0b896cfd1258573bbf454a91cfe8338e7ec80480ef7462562956a5b202c9f6dd0c45d2269908952db4c6f48685b8fea3c62890594bb6924d19a3c3f7a414dbdddaf8f298a58403e7ed273622fc6b28cbf0eaec39c796781f795755125243d6886f706bd23181919861c81d72ba73e5dc09866166df1f9819056294e88cad9d560bbb75e2b874e9d450b53320d1bee6f8ace55c305a7b6ddb0e2b0fc9132b3b4c99e6c796c1bee31a9ebe95171e28ec36df9d3df3718d6bbd0fd406cfbffca1a1ea36193e2589cda3e3f025c9849403a72fa261e102054eac4642dadca69ca67a5beab9d59de7cb4902d8a79a0500012c19d097522321d0a1e3f210b9e5f55a2a8d38e01bf973d409140706db1161d72f75e33230ce1a5b452831f18892de588b3185c61a0fd4aa5a780b79bf9437b5439c6e530a5ab18a93dd05ccaeb553dbb76a2b02582e9af08a2bcfba8050cc6475981a53aeb7d4010b3b78c6cb8f69a79b5e030ad7d5ac49f3459642fe035bd50e1ac4504a468271c8ecbfc8fd1a86996fff464d62b98f173dc6be05775f9cf365eebc221b7a19014323718cd473a1cec5202163aa6e8c321e1288d12b02e165ef508a5df29968a91179cfc66c14ff278314fb97da0d547974b574b33536d6b379eb0a718c5034e393dfffe245f1d4c0098a12634e82e6657c3c0b06707856339d5c8359fe6152e9441178795cfcae9b2749429b4514e1efdeb7e36dceb8f47a3d55f47d4806767643ef231f7f3aeb2186878f96958f8518401a45800e45db53cdadd4ba0841fb6546f774b4bc571e033e12fb2684aeb7989282d4084460a2da71bb60c76033032302a8eb0bb3969658ce5ad9c213b957bc011d925d3c964ec456a9b8d8e8a1ced1cf530cbc845f28769a98a4ca90a4b91f74e18738b28f0daedfd3e6056740dfe2615e854f4ba05a68ead1b90242318ef7242da4d0af097d61dbd477c133f0fb3ea3cc37f64509235453241ee0e11d28e4657ae9408a26d0dcd5229d64becd9eb029d9d0726c749172073f804f5c1bd9073983da8fa48f63492de1658f1b3b8536317c010908f8b9c388935c1701b8314ff28accea9535fa14f90b0fb8731f8f232586093237c9e1af969f139bc6762132acc285c94954ddc632588eed3d48fc0b3aad801649fd5609c86972f32434400eb03259339b70bfe62f6911a4b36293f5bdb08436b3e6c0ca04f07814a11e1bbacc488a7c0dd367d5230dd22ce2c7251d019a84bd757f6f2ba849d7340a0bed713136a738f56bca"}, + {"0000000000000000000000000000000000000000000000000000000000000f8c", "003d0b5f1617c405285421306b2ae86a2d399c33a81e39c4502b59da9d13ac125ca547006da24d9a618901a158a9d3d6d8575dec7c1deafee59f41b0baeb190600272ddddb80ed090e20cee918c0245dfb0aaf44053edc86f958c2ed01a780d9088ea520a3eec533417da16c72f2efc48993461a0c0bd4c3b2cb55597b6205ec3b227fc219e156f5e08e18ac9d00a0810bec7c0cbee93aa29962bcf807d170d52d62da63f677e452051ef18472cc031ca8a732e403fa950975657008050b55bac826588ab8fb83c20d46e662f24afc364ea50cf9f4be8a55cfc0d46814cd75e70c0a6e3fff7f7013a77195bc15b42ce219020f31679f423e7b53ba420ecda6ef21862bc53a56a51f065dcba6879cf62ce6223f2c3779d752cce9ea447c616f85f16a7f197a05170a4bdc93a6e415a71e04b9e3cc5a015140ef7f382d2af23ceb1f94fbe2a4732ae2fa91e9acb5ae4a03027aa9072fda25250b32d1543422e8a5beddae017607c38bd34217331ff2c0f15ac97b55e73685de4a5f05e8578797e810f79e38d83e64c76fe2eec33fe516085cd23c2489375da7b225e781760146b536d9bec5070347988ece6f589edef376b59cd77e30a07de8b52192a4869594148757cf94eb9749b63eaa29fbeb941ea10acd359a19f1f7eb63be1235bca9d08f7da998250e91750c94b6e9e67af8de6f6ccb4e5d95d4cfde02a7bdbb414748c1080be0977b79c15ca44a6d90630ed9bb29380cae8c8d81e332736c732ec5bcdae4c009aa9e8ab65eaac5151fc1c4eefbf379231a113d2935740a1c5c60d5c7afb0b3b5dcc2eac2db62f7816b0324ba6b7eaaaadbb007a351f1df1c316e9a35f7ab0e355b45406526c3f70ef2862dc592d59ac16d02261d40aa5f8214193d9c4e54ab6f73fbfdc88c2ecd471d525af1b718193cfa0612c283aa2d4ce427996737003f1e8ba274669fb69f12f99f1d6ab589de93720803f20b2f0dd99d0f739720b9e055289847c3c997b92ccbd563078f5916a1aa55f9267fac233e359d0198440f0b2b961cfc15796684fda25ef9218539b9c9d405400a51aec15c704c6768829a4d15129e7c96dceb060889d81272e111a27954946ad564ea70b61fbf030dcfa4331e1e254bdc78d37fbf720d699a131f389f1829eec79c1e9d9b23c853da54c215b295ba17432705fbf1e9a2184688df6ae21c89294d154e3aee1b09071c02fa040fc131b806c0d946a3fa8c844e3a695815775e4383ec5a19b98366e013fc248f1835ff054a2891afdfd69f404171e392e268bc5ea923bbb9f55e0dd4e2b38eb13461d854521e40b6ec90eab71a0fa21f4b44c2d96f63bb99b2f362c3ce47ae0643bd74ff1a54e7f0d24e31472ebcc3b9cc7d3621cc137a2a71212c55b84c7b203df333742076ca81b571aeccee8000a47a791b823fa52a7b403c3e73e07a58641b537609b7ba9e0249a0bd0ad7b1d4d9b4145ca91853b8b42934a3d82522a7239b6f346cd1da18de7ed4ba8a0f2b5a3c1da04b6fb4721dd6ab37e53c1a47acfd7362101cff9cd096ef3fd77b83bb45b2d46a25b7517f1b20475ac7cdedaaf18f063506c86573fa69e217da1820f7fea1e55c5ddcea86442a15490963d20b9f63535695be809a66af2d5f2d46688eec17dae4665cebaa01fd8ba7c35637db73942232a79bdd3d25c18f8d911f4c3f72435fc07be7d5a286eced159f928e3eb55c1a53d74905dbfe21a7a2289d7bee229a59dfda234e1abc691f2150bb0b02ec064f4c5744de27c7d83d0f09a9d294cfc691fa98d291dd1ddde5fe8813f5bbdc18e6d2ce6c30ae6bd512d25e1e7c4769ae3e1b9f4d26c32943ab5079ff35c2163e70cb9225c381bf67e84bf9546b08b9deafd6b2660aeb65558dadd8f4b692"}, + {"0000000000000000000000000000000000000000000000000000000000000ad2", "0012c8aecc0d9278a5af2943d7f8dfd258a25feab011e66922091387cf5f40e1484a18b55ac3e23c5d480ca2f27c9e84e0fcc3a507c99dc008834ba37d4609148d9184108f2e45e8c023d14930c68cf84fb7c2f800c5a153230958c1d3f662037e7ca9ef6a96fff22c3fa8db088dd4618d56b58574774402f6374bf8171410083a8b14840dfbcb4d666a6e4966ff13563c9d2e8c49bd66ed2da24f8a3e89de7cd2b68295e758422f056fa7e8df8a330e9a9694dfb4755a42d21b1cae8d07341f5caa53e7acf53ac1bc9de268f47d356530bc17557543bb1395c779e6852ad8b69ef55c6eeaf16826147a44cae8842f563fe3351f58eda593308d17b10b1a5870c814f2a305b4118a3c7566719c34ee8c1623392b309dcf6947e842e3d0f1eac84d5c9b70b31f10ceefab6c0a4fce65fc52029d7c4fdd27ea0cc63d393e6dbc341f5379410cd8d0f7647ceae0535e50f70405f0e8b50768e599b234c2dac691459dd7550ddd226c4fa68e495e5fdd24b3eba1c9a262840036d4d70f62a4d9718d3bcfcbd9c4e18a3dab16b02afc1a3e1103a4c50a08effd0d2267b623c157e1f253be0b67085bddab048f049b9bf9838a274e4bce8e693b9f3b30de952726d550919f8a458974d3fda1817918b50d0bbfd5fa572246fd6644d0c2be411d11e573d0bba22015cc8433e420d1f06342ba3de72ee174656c664807039a5ed00869c858bf8859dd7186c65fb9dce1d31fef91b8008b5c39e21e546c834a78be7d2a7f5f7619f64ad0a4ac7803969e64ad6bde5772f2029963e61f537ef61914dd95988b52af7ed01c313dc7159c160d6037d3522027a9c45a6212479ac67a8989b5ebfc28150f5097739ad5b751254b9fc5a45dec8adc710b0e117bb100d3613b0f46516e91ed351a17ff3c0167482f1dd5669c08c137b8955d2ac7d5157bb919450a003b887343e633cd806005beb042c1aeb6833686bc3d33fbc0551e0843fb1f966392e551f9b8375441e10495edadb58131b5c85c03a2a7416a4562762e6e7a0e8ceb87b31cb534fcf9b5597f52de3318a43d055c054a224740e334cda4c325f9416d8f21a56ef380ed1a4113895a5c14ab9347b5fbeb62b0e5848f7901b207e97b3fb6c33538bdc650a8afc2a482a85df7f8f452f894d53228168773045668044407d59f78fb0c830185d8f7610ba9e5261aa0499521d161d1c66f074d0950af2baa6474dffd9eb15a9bae5a0c71e74542d4023e558c69dcaabb1b88048794605c4668f5fa14a91e242ec33a4fbccde155c458d44368ed66af8dbe8709541e740cd6750b45af7835b4c6422e2dbddfb5f534eece9b1b6c228dc1bb13590b5066be3165f42fa20b1226c12567ade97b12965e97516a3998f46ef4b23ec78b206ce8a8c9aa72954b2648b5656c1a0efd43011c261dc81fb6c3fa87d102c190f24630d73432e012635c68dd619a2ffbe2b22e13be13fa6524d975d002017837bd0f6994c3ef52e317c963bd5903daf66c349aa79e3519ef710bacea27e0d7e6ab12b9dae1e908f097a877c3971d22a121d91b2af3c9db695cc0ea0d16d0ecc6619027e8b7f173e0f795b62d6856e3fc09847629adde84bdc84fb140b3971ac11004d032f5324641fc4e5805cfd69f944ec1fda9735158ffe4c708a5158110079928a4e561c0325492469109dfaa9011cbddaac04b106f7af371a8c129575269fadad8f90c43a16ffa8c5da2aede71d7ff9a16458aec966d260e4caa59a2238433b0c25798db632757d6185eb210090e91e2d458228dfc6169280ee49f6252bd983315355542cedf652c4d975f67ac08bf3f632e3d3d06d90d792aa1dd46d1cc90d121321cd83bc4aa004f7eb8203527c622cbde50cff5c5dc5ce41a3a6d93bc1c41"}, + {"0000000000000000000000000000000000000000000000000000000000000f5a", "0081dd9be2e0ab034c9a00848a7fc8e51d33b0f13937b7825e5c549d05d3a4d3d6bd6e3ffdc9c59485e50d15ea06f2211fe70ac755e96fb20fb9ece71297d230ef019faccf3423aca8e439b4ff1476401b5dcda20213c05d22d0455537425a9980f116c2ae8f56458b42af122624a8a465878bd6f2ecf2ce51e82bfcb4f32a203d6076afff25b787c626cf5714c27321fe82263824d7814619ba7714dc48caabd54c2e8ed5bb1fae02ebe90a72e6adc171b424245cc84b5b346a7acca85a45fc786c96e73f3cec7886176f090263ba94371221a2eac4a1af596de0ce2ae2e4687a1ece961a31dc28567a085395d0a5b04ad394b31cf305d7e1b4f5ec049914fa1d0b3f493842d6a666df128699dcfd909136c0b32e0f6ef62dfa82348ec9f45edbabbc3e875c08c511c29fdfb6a94f96a1023ac5beca2b6b92344830ddb76cd2947827afde346c82ebf7056f05ad1c4601e68571c00ce1046fd0e1a319ee7215899e302cf20ac5e36dd5a025e796a5f28530c84200d16a4d83e911baf0de205726bd734852da04bb9c3ed73b98743523792d7dad9b7441df791632b5cd2d35cfad6ef03208143c5f3ecf474aab456240eded187f72711ec20e0df2e882e61cb1ab02ae46f0be7f5b4a1bdfbebe1632b69e12e2a4ec5d3387c3878c5915b9c6bdd1129f46ec0e1dfe6edf69c2c3b8b76cce6b66b20ffb44e1086feec161ddc387a85af2bb1be9067eabaf3c42d81a692f8e1b46d1356496041a1633d7993a4a7f894a2512844dff28074dea48b27b19a9454d5d60d83bb77c9b8654ed6d780571dd199c4ced0dc315371aa9e30e094258f0decb4dd921923732a51b0de3839360de2d2635db4264e00ba2d934f5113a5f8b12e59fbcc1130daf306a8cf72f2bdb33e77c51b6ceb650be0d9c26db2cb150abe415641a32bf0827256bade1ddee5f031eda0ca5c28829d130a08be2dbaa7848ef74f15915bfa6a0c865b755fc580358de3525c1e32d1adf730375531f2d653391875592782bd1dfd20dc41899d51a607e1d1fdc6edeeefe441c3dd00f7e16add63dd6048c9dc47b837414eeaba1132509d4df7adb5e44e50896d2a9955781f2d194b1e82efd69f981cd53874805da25d75dff76d3fbcb41d6677c9f78bd0e9d04a75d4fe3c6435f6e3fa64f5a1ec45af2168a6c9d27d4040f894a160eefe5eb452781467ba1c24d48bbf5481af0b28ae0aefe37b7c9e4516a53a8c12156a925b809839bb9978728368f3031b7f72df312558ad3bcb70b4a186752ca1bcbf027d3ccb43ba10519fed169460f2185baa7149d637cbfd2c0a14a871f1bfb9c2a7e1cb2dc48f79f026b4792f7825073f8c75252de3c7d3816cb1e1edd565796a7568356e5e85ebc8d5dcd734e29bba9a0e75f6d63ebf78fc2690067a67f7dc50c052cb9ddf548fb56e612431a4eb0bea8df9771e2d41602c533a7d30c5f47d0a5f3213254adcd24da906747bd37df137116cff45cc61876e4be4b02c33a4f61595636bfd2687bb34cff36b570508a1747779e44770e2bb13568a8c40fea61a2362df2cd08ccb0345e7021bf1f01e05d71def10cc7dadc763b966260f660d3139f1179f9af3d0b9d660791a5cba4165e53db3e3715e1514be20d199ccce436347d4a306e0b7090649b05cb5cd9b65b1ecf781ff782b2f358ea16dff27b2a074e441fe0301a7fb4b95232ea275d5216eadd28a309634f251285f30bc60e1494deb686da4407f321643e1387c662ebfa8d78253480395f3515ba83f5bad412b935b285f29a5da37732db394bf6652fdb362f4a1714412aee0ba9c471551604c8baf6119a231b859f17464421d888756ef8b7c5de8860b476164f1e85395b806510cd34b47fd140095a065a931a6ed2d8f5cd"}, + {"0000000000000000000000000000000000000000000000000000000000001ca7", "018bbb465b2c7d279b5c006c91f192d26bbedb0b5f0fc5ff3c9b5f5e0d854427dec85c3362136b980f5a0ac03a46b645bc87a963b1eab32dcee5ba5337948f13926e35cbe1c579a9d5c1e64160a0a8a1d335583e0388f86c9edb595165b483ab2c7354fea1a0b8ff8f0464b3371ec419e150f471a805d785b0f1341781cb055ca0783408ae4bd11d509dc586fe04d1edcbdd4008481ab564a2c379c9d541598b2a508e24addd9389025404aaca0bdd3fa71c58c32ad8bcd232f85d449d0d1635d048d2a17396c52702eb38c4ca013a76978c262822065c58368cd69ef2cfc66e89b6c1c9d9102a28a9423bfa51fa31b617b464c86f38ed86d67371ab031099c50b723e0dbe46e0db92e3439a257d3f0a7129b189a5ded6ded1b90693a621b584b984d5f3dbbc13720262a6922a119bc112c44fdc3e4d25973587791714293a30d714b1fb402508a27924a1bdee1c2aba06e68e64141d98e9fc62715efdbc9d25bea73133850804dffef7205e97c45a3207e52d7dc991f65c4a684c5536456198c7615f46a84ea9522b925352f31a9e577feb965ba116496d440681545857fa7a9d5e90a5094b91ed7e1079dd8f74e09a6e96d88add89d9ec8f365aa642b59cf685b61893fc634f820d6e8cdd65db14458d7b1714a4dfdd10e66e8b4e5e8eccd0d98f112a15060c85cc3f0b2e8a44daeded2521822090de5b0adf3ccb3e8b6732f0a504994eb1cab6357e7b929d1cb5b1a94eb71237d4ca1204b023eb3498267d233e0e583904ff4744888aad663bff64766a4259b4fe361926d67db397fffccab056426cf567f5f198f785300b081bfbd9d602b2e87ea12f9f269725b8e57839374f537fe6f6de3abba272658e405f18d5bfe3905ce81657e5544799da65686f2586084d54929ff83951982f2485c56d4d6cd6a092032e29b19ce9047a39e6c301cfae9b98cce93114694175c954810e6cae1f883c065bc1c4e45c19f389069424f0360e86cf6afd5e02048365346411a1e4cbfe0410e7f1ff122c1ab4857f169d211520d02433c7abc37705b8ac854611928c310888a456f616cebf3f77f79187ffc5dae6c95991c015bbaa622a4e9c95b9e883c588cfd3cbc8505ef01c1e471d4071e3d093ad6ae4888ca9632989341bbcba3a519652016ff17d91bb168c6de72b29f50554edd0081b549499e77025621ed0b5521358ed1d6993faa923a16d3d7c13f31375cc13ad2b4cc6758b565ecd7c19443a447f93fe573fd7f23c10313616074e1a2d572bdcfe71d1d252faab664544e653638f01793c71dc094476efe24788cc89e9432fda4b60e52381dd2c1e3faf67a9a39a11e52bf6a70b4b74ddea2a87dd321c0ceec5695e040afd0fc4e2b164e73531b4533ddd5b43f3a2bbe0715085a2bbe58e50e6b489c570f8360f0238fff1f2c91c5d670cf1c367cbd130cf87197025250fa3f5bbca3ff5c65f63cc1834aff629a73d5b8f125832fa3221241f6c59493f93ee8e62a4ebdae2911b8edce4ba109fccab6c635182cf91e92c5ddcf43502fe06d0b5890838d431a0cf91d3f3fac2359a4f4013248111d15716c753da540358a75399ea22bda000038514e1e58f9b5d04233122575201d165e1de156f11e2b44d19192abb4c93c7181cf13626c454dc8deb025320e7e113225cbccca02566e13ef4f3d2dc83c703c973e5bb4743faecd0a629a44495a7c8f01e69501062a247fbc98ff33471a107e17ad17578926cbc811b2436d6671d469331e463116f98ffa54caf4ba6180a65d92f4233931fe06fa84edfc9a58280f47a713b1abaac49d28c5834e760e506b3c1aa3bc3aa3ec408130a5daa0f52af39996115648b52c2fb3dd37d11f04d465d7dca21f76b6abbfa8dc76087aef78258f27b"}, + {"0000000000000000000000000000000000000000000000000000000000000bc1", "004450e8399792037fed35c905efa3c21e38d52c6b1a9e1b7f36c9078cd3168225bf4a17ba006a1c33c105ac22792314370dbdda79509dde862661105dd0bc1734ad9f12df287bc4e251893becc8d9a7a0b5a8b202a1b7af30a845d1eec9741ae35dc7ef76a75fca9a0ec65dcb909e1435111711ec74fed6bafaefba5d270d786fb0cb60ca87a597b21c7dbbdcb515bb158d6d525f1fccef9ae98fe891d5392df1b3668547dcafad00ac04cb81655fa9546532a56adc1485330f51eba6398e2ecafaa3af693566e7dfa46a01c7296aba34550cbd71a81810deedaf1d66f2bb61cc86b89d3e602e29509ea7455a9b5efc58e44f7532763a219991ad6904ee19a63a61c4d1afc1943eff55ed4ed59579b4da0c02414b38863837ae0014ea4061373ee69f7bd6fe1f4a2445a3dda331d4fae383a5b7a10d118211cf4a27be2e0764df3bb96234d4f270a868c26d94774415023370acf062d8df19e9d7af3b65fd1ea6e53b9209296b434df2d7e69dd42e46b02964107e79a83a193402ab263fe89b9c1f5940e26678f667d933e4fba3741a05bf62ffbe4543f829592ff5697b330a30d8df2609d23bc50150404a8742316ad3b8b00d1a850d0d451ddd8bf5be989c03731db39d09dbfb0e6096fb9e580e883695be8cc12535dc0155a3ed4206208d154a9b1b7b4190f135a6bbed2e82a070e2fc7d2a976b68aa06d04f38d85b93adcde182bdd0d2d8fd565378a11d50fc27b26d5c2d73e7f668a1ea5702668900fb067712d2a7e0a4c7c60b68fa02affbfa2aaab58e1f13702fa397ac1adb4849d174c3990342619d869e9f8eb40e902975cd075e796a2a3add92f799af086a5fc14a2623ba6ed063f95db88106cf46c6a529cfe38f2f4816075c3000652327de28734c84fcb355b92bf0f5662bbdcdec0cd39d311ee4331545cbf685ea12ba057a00c9b9744f721c01d7ade055ef7566cf443b7b6cfb22524f88ac256bef59d7430efd312c38df0aebf20b29808d72609075c5ef231dbac4f8ddab7818dd915834aa7307c32e829d9f99c4447e591856cf2638447201c3aaa04c80fd7264ef10af5037226b8b7d9de7d84c24d44ea5939d79b72025514d5ebbc6f5d17f07a22ed6fa88b85dac7fcf6b9503fcc08a8a750f798d7c378f2f294bbd927fedb2643ad7e3864b14e89ba467014145e3b224f46d37c862e0d9ccb89aca68f81b7644d04c03c3a427efe9b507aa1b3ed58e43a29c569e0a45e2cbdfc6a3b47bee33bf265dedea7fb5dea2b8139020a22b1c7873a89b556e8be7043d95fbbdc5e60428dad7014baec5c505c521ef479091b60890713d1c71293659d7b8e0f91f03985db97a758edc799a801977768cdba3c5f12a17e1b7226a8862e17e5ed7172fbe0279ae6f994bd85bc499563f5617471e1a49750621a11e464620fd1f2af10a849fb0a066486426b11766899f7ef0cf979f3074da42b7df8d6671d5f7ec1b63737f6cd4418906acc2e3aab3892cf5572a59a425c8b7c6ea0eace8a597b60df034eab5b15714c01c0ca5264020f3638bc743e2124d9fa82497780cb70f2a44f7e8d8cc99c1066002d42c56e72eec8cd837f311df8f6a174fa83ae245e2397cf7f4a8ee84cfda8a21d2071f4e5cb0c132b94c5b46ffa593bd817f62590687d979422367c9c057c895d64dfac2d957375d2c16f186588f928fcfd50a3283e3aa6aae7ca7783a7507606a3fe715dfaf71c028e0455ca1866e365e7dce08600ea80adf7aa5664481befea20a994b705db96e0693d7a2e85bdfadaddfd16322f05621dd8fd740bf1abb792df20f3d24e3e3e309596550cf0716ddb2ec159c219a71d22d53ecca729c0d62fd34b2519a7d081767873ac5e5a5b9e7b3524dbf39a810ab4df4d044"}, + {"0000000000000000000000000000000000000000000000000000000000002463", "011fe9bc62c35e571b70d8e6156662d3977edd9c6908d716a2f024c50f3a17416e5352b6b647827951db087e2c820307836c956e53e44e6c5ebe07a7f97c9b57ed4fefa85ace10d918c58db34a9039979d8fc68402399beca0aacb415cebe67c7cc1e45ac26bf63bc60837dc6c6fb132df9c5214b2d6d8038a13197e5fb90e16b5f2c99e364d026e243a42c45406a8115846e438294fa86f18db2fc2cd3bd3db65e00f1ecbdf23f3056e7094fd53bd63064799d24df66702ba03db5a90400a5ed7c81273b5cd7d8a39196a5167270bbb70e016888a83ea2329714d92a2a0f7192605ba24f3ddfd1aa8dc830967d769da99943e4bb3cab1700bedd9530cb1a88ecb4b99bdc01cb4e53d75a3ae2f8bd40dfe1823cdf4f1466e8dc95c44d35672fd49e4e0fc47cb0ccc007931c4828786264464ffe85877dd9f7f88a011be5754d79a027747c847258071cb024e9a566a79016b58ea58047a2f507c2274f034debd7d19cfc107175661aa6d871655b95574b3c0baf1aada84fb88a301b0a4a69655ac035f682920566486deba5b5f9d221771b0d2ca6fae19e8f1a40a57aabd1a0925d9af410818a516a2a4aec1c40744a78a2d2865efb8fa40673f49ea91f3319f79a47f44f78a6416930fc59c074840bcbdf6b8e0d4cf72c028b3dce4b9aea782ba2a6442842d16791bd0fedeab249c0f6519e3583bfb6e2f063c77bde9c4d4c4bc81c157113f95f6bc917e1faa11538e09391cd92f54703307b9f461c4f63a5c94773bb3376fe02a52affa58470da36d34ade11dde697149b574ea4b94128b1d1725340d3cc4617af47e61bd08162e78fd30fe29cd64639cceb0f521322bb0349b14d53add6b47b161fd3cb2f2d0fece01b71c90247f0dd91da60b879521094a362ce0daccae3fed16277f1c5962b1a5136eaceb7ee2131039c3d70561bb430001b724ea96932162f54920770aa0982109a111cbe13a478694199a3f1ffe746e3eb176b1e7b5bc7edb1507b99a57fd07b1727af34376945187dd6fd85584e41a8f830e2a75ea29ff53a49b46684c6e2d945901a60708a5971b846871c8bef0ce37f41ddc583a6e57cc25e2374c989b42fda99cea23d1e5c83f17b17c401415439540d9857694712a94e47734a4dd8028bc09fa2410dd7ea111eabaee6ce6d6a6f641428e341fb9fc09ab158aef08e2584d9235fd14d855fb39d93b1dc5171128cfffd11b4fbb460386ff4b9071cb926ef733209933a8dc50497eb26c328e947ce4ee2974bfa2ca3e5cfbc6cc263a5f7844d58376e952fb30d49d92b209f1cba737c4516abcb468684d7845ce4aa9b96459137f7dd1df2a8a3581f56b2972e33b3afb01b97d6b1133c4a6031df831e65c15f35f41864e9e66fe3d173c3f36b21fd292d8ef82845943cda1c2d8f2fe489b02a7516d88d4e139843f07e9bcdad45e8b75d5bd280d7511c690d681f5125f9455cc6aa2564a6c7fd62210d391f99be88ca58b6ef772aee490a2d39fd8fcb31ba7a1df74347d79c64241e493bf1fb21bd158ff2f02c71463efd424dd8b7503841bcd23a20fda5c4032037ff93329bc5511e8c5ad0369fb02e34e28bd04e9121dd774a41b91a594c0c3c773f2dc35fd28df5591554c137795e248cfd368e588167b91039fa35fdd9a05ab0f64e249c7479a8935e09d791936afd858271c303a7a00289c2e96fb19d351576aed59370e8cdd2606b820fa88936a87b132e10a232419d1d91c706a32222c06670891d3d15246c300694216aa6da7dcab8210ee67a1ba218d17245e112825899236e6957981e9262a86f1dbd6ffdfbc3dcad992dd1b8314269ddcab1a2555ecef9d73f16c8a6665735f9421c9853ede1c2228f26c5e4905a0a6285230581ae4d8bf0a31d3bb"}, + {"0000000000000000000000000000000000000000000000000000000000001247", "00d9f8926165d90d59f842aa762eec20afe572224d40aae70f81eac8fff933997516d05a167286fcfa571c77cffd5a16286f1ab3574698c89eae18f45d96c242a325860ba39713ac73e549a6bc1e23a8141ffcc9043bc596d385f7f28296b05b656d73c44736b707610fb61fd6ebd52feb755801c141d7736db5695a9dff095b92f2897074fbce157484c2d4f3f1346111c3ce45f9de9fcab1370f8ec0462348de6f89f461103a8009dca2efe3ddc5ebb27921ec6f7786453dbc89ee4d1ff011da43c89d092405558aa070cf02b62ade8b32280554734e1d203fb568c3552c626b59142fdcec592e64a6f09c573855b3230577f451103b5c833bcf2e11540d2f472918579534a33297fc60bb5f70be0f6717707b0bd6b9031bf34022f7397484ce47cddc11ad126c546fdd05b0183fea23d22afdc5f108784db20b1aa424f89657fcfde64ce27007391e10b4ebb31bb70206faa8b709221b8674272ec2ff05bb494d9f0546190ef3d682942591a02972b52f1646d3c77dfe97e817d61358f0176c1995c8c3100b2f0a8df04872883e7c7a2fa0b16393f755d9c956d84fc756ed8a5d091e02f5474c2e72c165d4025315cfb2ca4a72b87a660a11e0bf47ef9f4697f12bb13da664cafc9b07f4b5c035a5aaad6ded6a3bab4ef44e814d69929a033ce14b4a19ab9ee993bb23b9a0c5e0c8747b6f83545dd606031b3a11e61e6227e6e6e65a8ab663d2c9617e2f1823568c58648ebf0be2d652a85428bfe5070caf6b8812a54bb9b51d014b9ee9a3e29254f5c9243f9bf022343b81c401a17cf35ba529e5e86eb7db968a5f043c08850e32bb5bc8b1a5744639d8fac8f9bb95b684610898b13f4b5594d0e857b3d98dc915ad1b06b4595b126f4b2ced1af4df4fa721b2a00f5b3090c9eff0c92f48e5ddc95c09a34b59c53fcfb5e7b19cf1efcf8601e58121fc18cc19260eb880cd6306ae482bb6402d04d4724a5ffe27b9f7b131e6fee5d2c915564a28632996637e3263656bbdeaf823bceb8eb33f6a5cbcb638b60b2d86a3073537e957a77f72e92f2678dcf6aa0db2793e583586fff2b858376174642640c6b49adc17635c6dd44f87cfde15f3f1adca9f2a3ed85fcd813865a3d3a655f5a7ed84a5e0fadc1e76c727bab2f372704e3b96fc2b43f100a8722ccb051a3cb6f2640e0364b0bd46db9e393eeef07c58dc7fe1c07ed669381cadcefc2ad88158fad653951d6de38d2bb6b4be5303650dce6d862973f927b413bf5ebf2d91a132c15e2a7a5f64fbd3a645114773baa7cd26967f77992a68037d9c03700cb668cea553f86aba392b2597baee9832814a89cdf6de65edc10480b5f2e69d3b88f2a2910c6eb8b474565ca8dfb6c27db948240a9982f9593124661d3539e3f5edca34c2e35244cd0762badcf356021d657e8356b82b3cf973483052afcf3fcdbb072e03b46f8c48cfab14dfe1d512d1de438daa2b36acfe029ea5e6c29be7494bcfa23ff728d47204019297860e996d869891ede19390c66932eab4d23cbdfe2c93048458423c4547538c044171ac2a9738f913346dac1337e48d7550c8b55c8377d71962b6664dcfb9b2f20c42fcd4cf03fa30295911e33cdff7ce0666d6bc023d60674ebd8f60891eb5aad46a5d7b8f509bbe7f870309d1be8a168e0755e7c4bf11a64881538b53099e25ebe4d801ac5cefd3c1d2d444a9e73a5018540a440d8df44dbc3171edbff99284eb55c43caa1dd00e1f22cfd1f3b114024edd18d50dc1316fba04fa32f680059358debd94e2f7005d92ecb33319aaced43e2ea50a5c8f820e94095cbe51e19d44d58fc56658ac11000f2af5b1a0d895112f2a619903f91c64d9d1f85f3b110186f3bb06a0b34c824677bcd53a87af555ed997"}, + {"00000000000000000000000000000000000000000000000000000000000005bb", "001ac390930ab7f47acf242ec04f4911472a1e5560077ab1761ccf16e38e7c02e8e41ecad5434f54fa1c16df6d2f4d6b7e536226a4176d3c9b99c00fdd6cbe3c556bff6f980688fb7a985dda5766d2887a5dab3d0103cae41f102a8ca6949476ef713009f184511c7926717f6fcb58f8e1feefe3c4eef94f49ae89b9b7740e7ff0c9479df179a1067144ba11620cc53b482cb44b82248a041e2fa1336398f037cde886703b9b9b940400a5298b5c62e3fb96d522f5d6340e3eddfcc9374272969090d7b315be45664ee8c03f69f20896f18b105b22b414b37aebad77515f13b3dc950784915228117007617c91a377b3bfa55d3bd0738ecfa297f1d009e1061269cbc195c27b30dd7b401d6f26301bd8a42dd83a790294f539141b149e84659ad9594ad4c70010707ff5019a867735e8b14782b0bcbb334a1ff6bb21d422c1a4a5a5a75fd6a2b16be2f538e569a7545300807ca1b61a328fe527879aaffb3f6a6c98fd0a462e70128ffc26600150cee5d2fa5489b6f7f05f7a831c742749770f34df529313209ac4ddd1f2865a114c45b026c780d28cafd7dba619785105cb0166dd7f3f0eb90cdfae5204319e5ca7b664cf128a809e793b6a19c29d4ef5d74440c9e46b6a37ebdec6e320bb48a21650abe506252c3f9fc9544144f398d912374df3544dcf6bce18593862dbf236f546bc2b22e1c51ef6dd04b5b2ad1890cbeabc1a4129f432abecc1c887ae6f08a693ef72465a1dcc913172f8bc04e943abda415e1890392806200a7f67d5550e7871b7ce0fc170f8a619a922a013efdc93cd5ce56f446c9859926dd2edc206304e98060fe1796945f093341b01907ab5fd396d500794cc14e1cc01734ffa9bb2f65503665c9b9ae924d4223fb6d08745670bd86afa5917fe8b03f4f9ff4d4a6306b49a93997cf9a5f574d643170cd8fcc6de00f0523292edfa2dd78e813662465a61ffdefdf337142396c6f5d33da5dcc6154859b808433cf69e9025079c8b96266d2073bffdd12685f48cf9842a11c2e21df9f61b43e65143faa7aa593b62f75326acbe469403120ab2c90843e0471243a1077c84637e8b1c2fbb50aab37af398791d2d8605a33566921f22c87e5cb6157fb290daf2669bed6815b2cc7f0eb9e130ff21ce3062aee1d2e642af55aec81c2b5fb36a638bd9b75114137f2bb3d64c6bc36cea53a17b749748137f256d1f34aaf248ccdbe4731733329fd5ad0254fefa25091db48dad448bace3ba4e75b991e745afaf7ebf3af230182b83bc9f579fb4677393e94a3859ee3cd13e9720ee95ba2874db2fb3a4b22ce81b09ca7ed03fb43a238843eb990bb75b0ab9237ad2cebfd8ea1cb26682273eeb546e0e3503ae10941d8d513c31310acd33982fdbad57b7f3cc55aa35fc758479fb63b63b7f3fb00256badef4ccdbead01871048c2728215465afc370174cbab55a8f6a6d454a33e5cfbdf09211b5dca3850739bfc202b4373fcdfd91d244202bfd3731d2a67a08f3f467381c7f3ee95fd61114db1f41ab3432bdba03f5ba1833ef6007ed8e411808a7c7b512e65c4b9607cfec18ea243bcf7de8f3cf0ffb152d21773948592b141180c662388d4f72643f6f6f541aa71cb9bb8441e0a4fe0b53c6b9bf2a0496d2acac6952c3f451f2054ac5639542948944c312db53a92dccd7b15b80f6338dfbe85d617b5b2add87d7df7fd0b69d787d24d6126d79d533accb3beef8e6787741dedb7fdefe24e82d38ec8b394fb1bdf66c476649f54f05e39f1c0a970acc9fc2ff6330bd8028710b90bb430df6f49bc02a35775d27ade703c76a95e61f1db4045dd6c53f79ef13848eb20ab0fc3fcc9f33588528c05afa2abf59cb69ef1c7c3f5bae9eeba017823edd6959f63fbfef1c"}, + {"0000000000000000000000000000000000000000000000000000000000000ebb", "00acc80df3df6ab75aaca389d1c9804cf85cd3d09b01c202bb8551b1ddd46f6268b3f5475df3a311ec8c12c293e5061f4755c047ca3803f0a87ec82a98cf824122bc15952e0227e32965bd614b7f0191499fbf44046c9e9f558b849b90e7b7033bf9c84b106afbd32412873a5da595f92128b296f5d1eae88dea0b58612b26c05d8915976758c01a061a28bf1c81e3615489352707c26895699a0551d437c0ecfc819e4840fb522c04ce95a0390c1639f24431e4cdf949af9bd61dea182773a2d01414404985beb65f363d38cf22d45c4d5110ac5d0e3a94933dc354b2985ffc4da26ad475f6203031dc234ba62bfd46c9f3dbb9b95292b914d7a9220666d9e53c053358f45792dd884efd6e6a7a9831c547347c7099b314b3fce194bcaa66aec755237e78d72604044c7ee0cdd7f5e17371b1d1e1e8dfcb2e61c581a105eaf069e61d9d7fc8a00ee96f2787d9dc97b603ec0c3482891d9910dd72c26542fba4f6413c7e520855739b42e912eb7d74bbb0c97883ab555c1f7c4d082284b22446185ed92f52c26a722f6efd4558cc39240da45f0562b40bf2b034ff34ca770af5d89ab819053e886adc0acb2a572cc56079e92151cd1efdc8d0155917cf1a7931dff4a0946fa7d9f9331ddc5fefe410e7a6cbc58b3472e703ec79676d632f3fedfd141614a510af84ab79ef9d2a81cd53ad0eb950db908c1e0b7d964e6b5ffb8badaa23dd29ea4aa220bc3ad4b7103eba597576327bf24f3514d9c2b30188086e31bd12726c6f085462ace282e3faf0f47c6b622ebef4b2300f529a77d47401d699748b3fe4a03a3f8133f5f015956c65f1d587d5bf2361d523b512e8cf7333dcc01aa85190906f391db37d71e7451c4be2d0d916f7343909d348d4a15cf7be5b34b1445a5925ae30b01d683fa644849674b2c1d78ce7bed3f699f36a251f3d4f00f9822690169c8f088a4059409f5130784005b5d51a780647ba0ee4ecf1ef057c2dde7f5e260db9386c1d5679ce211fec33dcd2d37118b304c54a98b875ed38a65d5a7970b10bc994156fe555de1de1c7b2573b0699e60a82f1232990a9b1dd3ace2f0de8bfd85738506184892cd9fcc94774281eafd50ddf80a7dc4715075f734e0bdd8aeb513ed29decb9ad0b3c089a161807da806aa14e1c2aac8422fbdc37ce8900db1131eb042e34a6f192b7b53184b152a929c328b946f9925c2f051b2e959451fdf5e699e4665e9102a37e9f4b4c0c5f547bf9d9cabff3059262c1a023f61a9af36e3b1f6121d2bacc199e648e6662533c171a05baf37f690b353b435959763535d9c4efc971935b373edd80af640c9e03c3a00f815368a6d09a5c8dfa2841bea9281d54ba85ac498a5daf524594d57b144a06285960531e562a5da61630497cb704afc6f778dd59590bb9f903fcc38623c23310ddd506f22f71dd69f3b1972df123fdf2da6ed74f251263a5860450bf0de37cd601f90861d3c02eaf4f7fc13021de0628b834ac9b2723ca177e993c5636ada9e5518382c3387bb549bcd8d8460ec1bc835fd80e2acac0ba2392e41f42ef645c45cc31bc63b7886e9a13a83beaafa3dfa746c1fc19bedd2577bbd05ccf246f3e4356b2943ded55c5f25279ac4945aeb350aed435af3ba8547552c68670defb59100af8cd1e2be32dcf8ae3c821924cc09f5b93bcb0304ee7a2c1bad8fae2cccbf5c947d3ed4578bffd80121464e33fdacee2a97c5f58511aff67a2654cf9829d14ea5ace1685e4f6968ed32cdf28eb7a08a39936610c9b8bcef04e8180ede30149fb3e6a8180fb1501f06c29667ec22c68b3d5d279c9d6fe8b0f6a1cdf2b7c110d388c98c56f5ac449e23e0e3013f68044389d2512da8557b907f294f47a3326b1625e524040be6960"}, + {"000000000000000000000000000000000000000000000000000000000000159f", "004c6cb4d71465ccb24f4228ef9c0914b3efd2e2972635f932406cc7f5c558c7c832f30aca7ac5f5b76b1a8f63c3ccd640f1c2c6e3051328b9d5ab4e30b5a033f183bb080d1943136179754c52fe8b42077ee18d052cd3d3b9d537b5cfeed3f3c069e966991016c3b8201f09e81815ac91e557c60d3be1538e3477b56beb1636ad0b5c4643d0cbd438e92571182771063d6d5120e5c4689e0ecaf753aa527bac202ab1880699d14a0068cb730310ffb3c8d2e2bae94f9f0943d416495f1100199b535ffa750a8f6191b0e368bddbc5d1368908c07069b8848f9cead9b72cc0c213b1d0ed5827f773181d6b585cece5a33748925761c4ee7ddf78979110bb1b88d758661331e3e428c621e8198c79d2992a2836df40e6d31068e7c644924aa6e7fd50a0dacadc236ba79171d9f821c9f444edaf483485ec55b2473444526fb00d7c22ebf8dc048478397dfdfaea91c6e102e8d8ba8c64b58d7b2e3046d4ec2a3a45f2fa00d2429944de031d9b9d26f535270ad35131afc631fbbc03d06638b34b7d533e98c9cce355b7b6b51f390d8a1405fbf53547911dbae625146667af8a9bb258ae56041a36b0d50eab75debbc60265bc7953053c7a07ae0b2b2140760ec2497c7b45eedf662d558a755d3b1d05a2691829138564a4f71645c855713663d79613ff2d5e82d6cf53002b54c683e660aa706b92d99edced04e72670b8d449f73399d1b529db3ca1786efb14461f4bdb8b599e90bd5f9f42f43a49762583b79763f71a9c72a45b0ffcd084087207d84e8e24a6d64cf9ef2069ee5e18b3eb57dc4c7926585437373e8e5c2f9e0a9d3c69e548ffe28110e2e319ef91ad65c45361550aefc27f23b80df5f0ee13f3acc31c1ed68b58766327568ee0dbaaccd9639df3280f6ab232ac18be416d391d0f1f039bbc2931eed4db246aefddb27a3eff3d006690e49fc57eb076a9d3768eff3ae5b38c51625119b20199ee59162aceb5a1c758624b94e5a9bc711504cc4ad940440136958ae2ddf73bf03da892db2bd235e065f30fe58599de23e9b41fe7fb72be0eff78f00290ea95008d1ddd2babf4489c588621c9263727bb1eb4ab5055c8ed198bab72dcd4fda463506f1db4862e156da711a0c7a128909308d6a3e79dbe29168eea32c0e9a6700d581d032069f60b6abecb63373d8ac0012a43ec654fbda7e4a4c0251983b644840aabb64f0c9e0604dc5949ad2435f4a4455d4b7d35709cef650e8e79ab9e5450cd3b97e26d55efeca219ef731ec415cb6e1853ef625baa4462682359173175aad247c6041111accc534a3f9aa010a96e2728319719af6e732e2fb350a9115f530a4ae89a4bef504b492b7e13060464f557e790dfbacc99f46c6bac28b9d250906466065c11e37f183b52eaef314e20b633c16fa7b76afa01d2ce9379d054e16e4a21a7666923c5a191f7a23622bc19c7c4f65681ba55d35e547fad4583a614327c070400a2eca6dc038c59219873616574f13e3c188125e53cfded90d9756f3c03c0f6d4bb952e93f34170056bd2f2dfe9cc6ba41cb2a7535994a11326160b152048e9e3dd12cee3dc2e7961aa5aa65f0d093c1bdd0d0c064784d9a8f1332a59215061727ea27aff230647473d6044d5d3e6ed40969c94e7794660119ab0ea01ef9d0d5786e8db6603516a6aaa786a8b439a24a30ec11bc28247cfefccce234384780c0a0efa14b04f1027b97c23eece6bb51ae40499ad25c9552c2dc050107d857ae4647409e8e051c8b661bc1e260551f1580ce0d7187dc6cd968f9411fc55148f160c2351757e0d979597ce6a29475b8186dd16d1c2979227fd1f0013c4f4f5fba24ef33429d9756ae82d8f605a1f299e5efebe8de562aca1487db648ed7371d5e42e9d27b0"}, + {"0000000000000000000000000000000000000000000000000000000000000158", "001c93009f8bea13690e44cd634301914c568c720504cb4dd2940e05df7c715387076700d8e4ccdb88080ae0f27f0a6402532264320c597ce6c377e47cf15b0e79774c53cc9cc52ecae16cce3da43ef444fb6eae04506dc0741700573deab0c367c3e3e935b175fbbb1857e3803a24e005714821a342189b6d97ee7fe2821061cae7fd509681c8e8a6d6d3bb04af3079bfd3973539ac992216caf7809ca5478866649221d012a4fe010841dcacd822154ebb77f948ca7d4e45623b790522fd4ca4b90f9f41e814d2e00920a9368e899c4acd0c5aa6201acc973b444be2abee44d968fff81440642dfc7523bf9ae8a57b48e64110c890d5dec53ad2940d28690eb3f01975829015cf55f7f501de4f3488584d92da6cb0b19ddfab35f611e06481418c92db545b36566780ce9cd3e5e9eaa98d5ed00c668c2e7e51e96d7097c40de598713db776ec4cbecf7e9898fb08da0479a2e96152f29be672426345ca756d64af13477f29aac6d8204ca3590c3883e8fd5d60022063b74c6f06fef7b1b388ecbe7acfa576f43880bd6694b41e9a1545639003170985842318ce6bed782b5dae5e654e06a1e03dbdd7b1e1dca904d8182778b22d9dd9d3dc089c85edfec96683bec572a080e3c6e5904577dfa00e559ce9a0cc85b4e26921745f98fd6ce5f08c792a23416a8829dd14751ae6266eac67ed5f45413b65fb0671719128db3a1f1cbcb0fecd7dc3813dde6b29ce162d9c6f994ea679830c230e54ccf58f6674db43dd0b7b6b30505ca4eb4427767885e6590a316efaf16b2ed172a7085b16c74013d4e8b2d2321200de76fb6d11346f30266b84c190b681325a5f97e4736cc4afd81cfd1b1f999b9355c122c448e033adca410496901e161f2346c9c7704b5efe11dd1c53c16243e8bdcfee2285493160ef085facbd2349516f947226153dd52b00b7a4aa8f0983df184884deb9510639d0b23bc55f5a29d372ee98bad2e574e5edd34f27f6643974b467011029f151d850dd4ce6f1cd714f7b799dcfd4af982eae873c579794275762831698a945e57ae6dfd5370101cab4eee3662b5c64b028d4fbdb46b3f3ba3c1d61c9c769e99f8fbb4bd03675ff43a06a854d1f516314b412fb38f253a7a95c167b10d08f7e19d53a1c473de7a2cd246fc899cd6f345987f25945424affd7ac0c04f761d9915fde9754739edff6e71678f21c433d25498f5195b5218de3dd23e370b7a4fd64221fe85720af59a5e725144ba8ff05735b7f47c57538af672b422eeff0ba719367a2931ac36f62cebb58655d38d01cab24af0a1b257aed0ba60b886cf6ee30d05dfd492f29ccff5020ef79636a76b4ccdad03f1cf4db811b4290122163a0723dbccfd51086f39fae3143798295459bcd2977946d3de0210622cebca52e36943a9c28029490e07199b614d00673a6f9fdc36aa35ef9b73a0970d4fafacea3f48893c677e4d90f6638715d839d2c918dc0f89353fb6be2f7fa39bfe19a66bdbeb3b549fae6b2a29932712265984bfdf882b63438d4aa0f05f947c984eb9a47ee4d25fc214fcfc3211ebdb7e111a4ee4dde1e052b5c9d6621abe0b866dbe0385bed079fa914a0eee831ce2522d026b606851f8aeffef827e0b7aca6141b60c75f666c43d8b28e4f7d94a835039717d5c04af924d5f2d29471a91c9d23476c25e02c13599773298a71b617a2c56db97151334e8a06224979cd8ab3a60ac1479ac5a5a5f37e4d76f65b73165b22cfd6149766134e06aad6236eedfaf43f785b26242e8e8b661bcf8d00ae045e0f3af569e2acb4c4aa3c242bab53b8ac39fa0596df8574fe828892572ef8273e8f90474e1b6108a0e42185d04522120ad8517f31dbcf2485da1f211a232371389cb45f272b7eaba5"}, + {"0000000000000000000000000000000000000000000000000000000000000606", "0038adfe05454b9b32bde2b987b84eaa2b67f404a929cdceaba42338316a7053890c41637962dff6779a092d487520885f9484eec256caaef9e0d9c1fcba58511e8e79831970ef3b86469b6e728516f825988c3d0373fcf4360bfe86dc3345240cdc51138ca67fe6b60810e8c5664d09991d86a0f7962672610a25c93c8b1b16655e138ff9dcd621c52f7cead36279ccde199f2814afd8b98a30d98e8f79b0eacec1eb255afe49fc026a87d136936811ea0e7203135c2b85292a09b7730940b4a1252a0685b6143500352bcaa6f9ae78f77618aeb5f2e6a31055b7fcf3c0acec42d25a011d168f50397ac8a7df1141bf52f6bf3bd52a79f5987593640c3325a9f4d4a0db584e146a4e44da3945305b3eaa373253b53fe19cdd66a64902a0f961171dc75aa34b2e5aa5dfaf26a1b1844fd327a299b2a2f2c858ca1a31eba5613113b6f7f6a24409df3d8efa2bdaff945b00be977a889c3a9f748fd09e7b33ad80ffdbb61ffa107186e5bf09b33882ecc46e9f56a1ce0d5e78610604cad340b0d16af112b4019afafa4219192a7cf418219016e52695799dd49fb68cc1e6e799eebb502a66049d4ff3e20f5261a45cc7411f7b54ba7139b68ca30d85c5e822de11cb02d2a1b4cd37de192ace990fe70d8064123b9f3281fa53374f9265d55a612436b86c1f994f272f7418f5e5151256bac234eea6c1ba1e7f0c073cfafb3400b5a2be12b39362d010d8e9cad26727c3825a4d8a4beae68df31ae242c3d99003f7de923415e7885d1ab0a550f8e6de4dc76ad75696be78ba552e431bf6a1c2ad46e635f6a9cf3515a27ace66400f75fba5235c9b6351b04152048e37fcc6fb6a5a4640d3c5cfdad4ad6ba55014a5b9c62b19eab493e12c0f84c5b086610e47987f16b583422e3e1c803649b91593ba53626a0b3be8e677697553cba5f0d5dd774500c4f323b2939853b05f323269c8e1bd942bda22e60ae08e9e85b12407c2b63508173d59df5c145e79fd1f46752b94d7db60e05da3d0465946a6fc087d19ea328976de1bf562bdeb9af6b58539d451d2b971209305706b28d2092f40afb180e427d248ba31c036bd770729239580c9bf6747e120e126a3e315709e6d54670b99945127c822809e611178a49760ad941f74ca775312bbe91f585baf075238d32b4758aa83423671c201fb7f623e4c2c06e65cc5897dd53a8a786f58dd66060f293743aec6ad8d3f0229a7f20c3a7070593ec20327c7db38a61bc1d84f709dddb6cda82c100cf713415f7a5863e27d355d8c590555d0c8e2548dd3346308ac55091f4a58f75166633973642faa23bedc03ac1427f8b0fec9c7852659d35e3ae9957f9615fcb2f90bffb754c4eed927f642311602a0896c91fda519170d9af22e569b630b19bc039cbcb0ac55203677a4bc02df3e4e37d936e5cc6941ce49e2f83aac733f423d175a4584544ca8bce2ef74c5cde453755da4d4a64f257c9dd1379a5e5faab26724676a5bbe5e6afaec345b66558a6be02527678546c60143e5d64296193231094a096e011b6597993c60d439c8f6eb3d825b8ee8176dc625580a633b0db133e870cb35769625b6d85636806bb60c2231451cd656988167152e745693c30460b97d6d1fdfd5b149eaa6abce72d74e947476d68b04928069930f83e8a3bd418e9c39a4e8d6b607eb280ae89b759f71712bfa7915ad6fb8f899f77139b8f80c02bb301d0cd314a563b0fb11dcd359120ba9aa1142a8433f1853debeb1802a6eca725a27aa4e5ff21707ba93310c06dc02c552634d5d75ee69b37f3a7cc2180a64ec9e53da45a30b65963966ac32e645bdfe970928535ca4c93636bc59b0b6bdd6eb28869fda48282620aed0ea12a6d0be0c77a57ed2f1fa250f5c42a5"}, + {"00000000000000000000000000000000000000000000000000000000000004dd", "000229b5f56c140d9cce93f203ab56da28d85b8416067fcf6142a0acb1f38170c834bb4b763ae336393208b69c3faed40baf9ea951a6fae2ea82449ef8d8700d9c0c59825e2933e884811bb5ea95064355564f5a002bbd40f5ca75733d9a00ed8f594ae4c1ecfa245606ae7d04275e30698030c242e135c7a4cf3a885d0e0ad829b9b6960ad7af42a183ad61987f5d879b5bca2a30060b4ccfe85b7167753442c8618ddc821a9056001632bba8573f0fc5ad3475cf34bf4adef97c521112ac0b351489335d2ffdc2c009742520d75b594e651117240c1f571ca2e220c8c330d2635ad037f9653d2e4cf979dc684c4f98e899d0f2e3450ae2473eff4a006581abf591ef832fa4009e3f513809073752a97d022837d3c8085a26d9bc91b96119b030fcbbbe3e65084394dd498d29b56d6570ee44e5417873a25f8d2709eb169e8de22277b05490a437ca78e5bc6b79eb2101c7a6e13d1ac067ab6436c82f4423ae5cc75e1caf12a2c9da4f1a1aa11e20e3fdaf7d3e455115aaa3a709446e85c9ca86590dec9317a6b93fe0cdf9704fa219e41a0a65e6a9b9aaa9d2c8293e2a4a13b35716e00bd60b354e09f0f7d3aad140181c5c0b20747ae94716a45e11505cc1092f38922aa4e6a5dc9af8e742882e77b232be91a3353c2b65177d3c4d017c6e9a87fe3d7e735debd1d224a19ca61c1a5d5d77184219e63b02454b552dc0a2fd074b82c15ab141dcd5831c896b0fb122feafc787d6d89d3124631639722d611de99c02f48cae7e427b795851169f5159e496e160581d011b7ce5136bc7fbfef9651c58917739ab8ad75fcaa002738576fb27c01b5971e0681db816908c7e70ba003d20bdc541d08bc385ec0577b47d3f1a788ab5ef5510ae48d0aa2eba37b61d414c8af60b346f415c09524802b4eb3968384bce6b65423143776a8d94bac18a0040b94f62c1a1ccbc89b20336bcf08d9bc43826a8264dc6b8bb14b84f6fa633674460b356deacbe8ed1033e8b4a504f74ebfc101297a140b198edc92a5d17059cf3a5d1c62a30a02041b96093b4bce7deea7a130edc6a1e8f15f7e34863b2884351c404b5748bbed21109e515dad10ff99b26f5513058f2423573f5427e1456a1c2f207fb5563d1b36ed4d91fd4df73a88d615bd74ec51bb14c53a6cc8785c2bdeb62e06b1a8e4600d2dc349a968a31ced24053592f1c804e806eb3ba28b2accb725d252965edd9aaaf695256cc9cbf11b00a6470ef130f23b4adb530dd1d55445c55c23c66f70f9830960304c17b8c7ed3b018344c81693df1d46a0cefac4bc0074198f2aaa630863d6515ac471645dd2bff1a4b0633333da8065614cffd48558d710fac000e9f3ecb021f8c1bd944b3720e4244598fa1b5ed341c87c5b8fdd676c5c672a5633fcea06981758ff8f50048f7a18e4226dce5feb09ad6dc1310cdcb0c2564028c490c204b546c6a17d6671260870a4d469b91dd076adb89c19e56351903b2a8ce2d61aa0798f469b9149c6d5f99e5455bef79b680bd5b3f5fb069be3f3d0237a5f77406b6f3e1a1b24eb0e4c1ff6872fff7f51f2ee2c6860cf15b9482fb94ec6e462706beb9d65d147916ae6ddd38bf67f017bf97f98afb67c4dd3063202c3e42d55745278d48140fe9d7dc2ed7a8df5d3a0a4d6166e19e415593c5c538677d2ff5df54d3984010dbad82a2cd1eab5b9521555fc4ec4693ef17dd011b8ed7571f9ad777902ae34cbe68ff2323621eacd43f6a1cb52fe2dd8ffb6827e4127443cf20e05c32de11cf3b52b1856f3c7d66e12dd5665a9ab8193c731415a6f8fbb891bd0b354eb513563a125def22594c4e35c47215f7b70b3ffe34f8951e51607270b19b471038cae7f8ae4ff5ec803da7a08854cd7b4954dbbe88"}, + {"0000000000000000000000000000000000000000000000000000000000001190", "008e0a8d28735fb9cf5f448996d2ba76d7823f434f10d6a2cd14600b1d5deb743bfa24557210157f719505dbab16bb47fadbfbe4b20dc3cfc9ffa298fdccfb096b2ecf484fd30adeb3b6939d4cd4f33dbe7c65ee039aa58e7061e91b9643c16c0ea8eadf1119fedb1f0cc59fd6701f40dd04ad68a934e64d9a3b53f4aa3f2f4edaf01311c2c4ea5084278b472c9dbcb03e217e34dcf28f195c7b6d952595b81dc7a87fb7a13e15bb0406ea3744d5804fb73f280011dde222bd86d94b5e12ded672a5cbd51307c2e307b5d4b9e21ffbda2f4b0893da708f4c64c4997c526d34a6586e29837d843a1699441552d524efee90e8279763f72b027e59a87a15fd01adfae31d87b13ba75def47c8a6c2ae764d2f1a820146de0d779bf0433304bfaa66656165ba62ee2216ce7fae3c698bff1704391af5a42d51f39906254d2b2f26611e72ebac3bd5f2cdf85d53414ddec2cd037bc91dfe8ff0572ac7e8a5b4ffb7fe4311dec88d07e997b01c1feda17d67f08bd57ad8b08b66ae88b203f3e9579189d20ec1bf5124ee7caabdd9ccd6bd56141539b2b5087ebd08c8a3c1dd7a27bd583c6dae900777fd64f4b8e9ade68c05bce7c6221ba0b85ddc9e2b660da1e75869d580a3f336a56eeb77c5c19fc26b0c73dccf9218c7f55831d1e2a94cc7f9922331d22e1978bcba34d06b91d356a5c60734d89de3cc9e48380b4f299f0493e1d52240d801e67b4ad61e9c966d6c46a6b38ef4ab6115ce7db5023a339e457c816d32e12b8e03d2fad1b88b41fad7810245162ee6c4fc6ccf45ee6f880c954af7d357280f29c1c912c0a99d4fa410a66c7be31fc81530fbe5c35ad9f276c6e9170d8137976e73f3a1c885ea7d7466efd47cb19decb9181d1468cbae0432d915a5a7e7327f4ca16b0d335fd7f11a2e256c59d487679040d5b6843a416590e9583d2202c5b61c3588b4f2e68dc49bc6c80df678b41c80926f793e42225d156d81e22902e777596a67eb38aed60308f7791d21d02d4670e4afdce1ac3d38c5f6056e188cb10003dbd4a949aef1adb5e0549d79c3faf719143cef9a99972df9394e73aa91c3ca9993a0f4fc571cf04bcbd2cc3bb6cf5ee4d0c3c8936d496e5aca6c28503eaef65c16d1d8f8e2f0837f9928e2e45ec3663a54e451338f5d3beb9f7506b3b04e6588dd1ad7660458bf3cd7d69fb0ce1cd26bb85bacde9c1137bf510b6bf0bbc2caeff8acf9f3d38f61d74aae619af9450af04aae0873fff9b0b7519855cc9591d0d518f70a19a6594d478e60d5178494e547d034c7904abef270055792242dcaef3a7acbd1a9d76ba2c3a40e9f9a740fb4bf9de6c402d7bcfeb202e37c5809dc8b1a0cdb0959ca22c88b9329bf7dc2e435c0d1cdc3fab01b9211f088d01cd5bb91cb06217ba995ad3d0e03793c1903e64b43df12c4337148c067749636554428cbdd0b203a5f0fcf38aae1d83f562c8bf37e99e1500f18860974aa731d54f952f35f52716248b7b9c8ddf02ef51d8da60aaa78e4fbd64512d752c20489d7e93bbdf10ca5adbe825efd1f1e60356ed3ee16464ccc9df6951eb12720144831c9713f45c530f805557d873ca94612e6e99c161d2ef31221e1524d414f7b06e89acbf5216bee10e70c715f740b85ed8bdf15b6181236af6e0e9a7dcf33974ee922fe5682e0c87ef2b06df73095320c82364e28fa53c67bc6cbc9573fe6347bdb230310a221d648dd717fbe88026a52561f77032e9cc73b271c1ddc75e626155116c2c01ea4e1ed232c5e5a451056d10cfc13c4454efeb6fdd141162e19d7f274782c45fdc70e16e7bb3a784390e3f71052ab291aa5d2112501db51250917891e58e283d78bff3fbbbc7c4112b29e8ec9fe3ee3fbb091f456711d5192bcd9cea5"}, + {"000000000000000000000000000000000000000000000000000000000000130b", "017acafe3934c0f3b2595a9f9e71c84eeda3dbc2851f9fa4f3f91b641940d8d2522723f90642b93f56481fb42edb33d63983b630d21f04a33c1102be9b7b8829c32739f8d6ee22d1afa394fc414321746cb7b58d0b419c7d33a02db75efbf54eca5f7a33307d5a7b570eb455a34ab3a901e11f21ec8f9db329a1b29e7d370e56fbdb418dba8363ccc10dea501a2ed0825a4b8854fe55cf8499d1f8de6aaba0a4fffc335b43dd981e02af1c719e0e01bf236e52704f5db678d24fb8c17628abfa82bc1249b4d4401409a1decb3ac891dc9ba539d9776a5392b7b7eb10387b786921f2534b3ca2fd3db8f2ac07d4f77974d165c6dbf59379b9b039628c0376b0b42e6e4f41f2cdd3084cc0d3f5f85bf2191f0a4337386326cf9bda72d1c800ffc3c649447727621a30ed8796e085af0de481ccc2ca6bfcd7c9f44cbf287655763ecf5ff3b1e835f77beb4ff19a319a1acc020fe9ef526da61de2efb0df7a3e392cf440ed3d080b1e6cdd9a8cc3ab02ff94d684bd725e4c3298af4c1829e76017c84f3bc9ba659226d82b25688472098e2b69eb1c6ab0bfabc53e66490c521d76298c7f8baa0be26e87214db5090b19d43867e3de7eefee7b24d62165c966448ffb937d0093acd43ddb018196f415fc10b31d930e8a1dad2dd2a17e0eff383c95cb298ff32481a5b734f3309fd520b27ba1d3604e5f5f1dcefa04fdc4c2f34f1354873c5059441b809347cd3e233538b43fa96867a7ab8423962662d4ea1a24033b109810f60f33b51fd85dce52f36cb93af296f51a3f51bd32465fe792e320a34729e5c504e64c15d86373552f08ef1a4cfccaacbb3aaf31e92efd971ec3933d56a3093ce8c719a7b619bf53f32a94b04c12f8cb1be5ca0df68faeca370e21f8d302bfb2eaab56239059f7a51734e92d609bc3f926d644a65bb18749e4ddb97e0c0235f514a029a45dc54fe2c54e192ae54fd419574e55401b6b1f1c3e474a66459e3dad6d21847b9489f9044dae6767d94ea3f06b524caf484628c8360b0ca63a3cd205e81164ddfd9c66ee77d055eaa4a616edd2084e16ca86830de419b79415aa7d4745b75e91017409f16d1f5a893c45170b1336f8ad16cb340659a1f310ec4b76b34fce27f284b9d6dd6880eed38459600117071fd95d11992152b4221483e077ad7b70ad2c4602c01fbce1dc1697d70df210bdaa23c4a9103da8bc236c2e1918dcb4dfea88f6b282e5a753395e7fe4f8196bfd30520ad953f932021833985439273f1d0b6a19a4dc7d2f8ac9e9f20cf55c72c5eccdf5d4109cf81420eb9c1acf8d7e9bb79736535bdf4a65acf9c7f924a1057c3f20c14f2b5d8844fc48ca2e9028fe936d283b9adc006e6b5b7a8697afd47ed29b0c111d7d5e438cbd7b5220dccf7a2b36d5325eb2bebf0cbfe70d0516ce827089c3614f5e907a3bd016853dd77a687a34593360215a04ef502e19ccf4e0307e7588bc639d0c8e4bc01be77e8751f002a91de6ce357030130e9622949cf56764fd85bccd8a17cc7ed3ea975b976524153a3d6007a9e01de09801a43b5235518d873857ba24811c29429819ad5b7dd5066f57fa5fe10f7f3b6e1976074c1fa26e6de2c5942352dec3e5d9f29a048240e8ede2f87b8b4dfa1894c1cff217a59f03360894060a0e44e88bbabdece032f511c39425610696d2622921bcce6d3427b9ee35cabb535afc96d2601ddb360e35c660eb4e966576eff240edfebb910f4d1107b820cc1f675cd1b7c8bb2472b0b9beef43797d9c6fe30ab33a2c31979d01302b03db864c66b163ba3a369826f331d65f74a71dc66539aa76fe765a889096264a1b82278d78d76df7a9b21359cae5744d28d3eae7fc5e86acb7e3daaf4bf62a196abdf7f80a9d8319d242"}, + {"0000000000000000000000000000000000000000000000000000000000000154", "00a5d3d4fee34b1fd4fdd07abe65572e81e878262f06de2c3cc845e4074455648fa6b462a658ea18cf15075fa926d4dccce18f9bc12ba64e55dc9521f279b121fcf3a89dd72c3f6b03626f92a8b6ee0cd938b06103f3183339029f12317da6e277b95b27be997e5c1e11a1a8c273a86251c3324292d96beb8a54b69ebebc1546578c32aa0cbd7b70c63023ca035991d04f24df26ee278573dc1b7ef736945cdd7d6366ceb43a7ee512979207180be214c0bc8246bc695934e42ac9aa921aecad62106ec14bdc6af4aeb14a7589a280745f5a31ebae70422c8cbfb16f0336c625d3b2d7765f55045bef7d12a9dbfc15aa1b67037a720a961e621f78cf140d6e9f791a7e4dd95eb598acb370c63ac21dd5796257de205e3b0f11fcdfe6ee72b9d8bdd07f554e5e158a4bba9aa2af078988152d11d8ac7dfa06d957ce7c6badf323b3c275dde88a955f6959a7aa701fcd9000e43e4b27d850f7a2ad1547df42c14f808dbcb2eb256af40a3a1d348af684b491a9296e2a4720f79da316fdae084c124235ccaef3e84a478d8efabe9ae3615b9623976c3b1d47fbd9194f875fa75e6d723b5e9a0ef05fb7340b49cebea8b27da369aa8947fa56a5ed4b58b619ec2b6e7b7b68180cfbe28ff2edbe7e4aca16385a5122520ef544a2760722c3bdca8347dedd043e6646bce22147bfec2015fc19d3aaeadc5c5d012916ceefe7000d1e99e6448247a542fb994fcc2f089844fa957e5a958f736836d76b74e3a16a15b872d66120f56a2ca7661d2d5baf82e2d2b86e40c6bf4978002c7ba417dee26ee9abdca32aa56e46e5c6a26e980e1ca9e9b623cca30fbe8ba57a7b36850e4ff25ba8911d61da4e58cb7a5bf97052663fe93054c3114dee5221a3818d27238e69978e928a62ca51e4a9ab927eb12ec8c544615968b5dee90512ed61f76ec9e5bd0c1601c4f95b0221f55f345a722e68249a8c9ae33cc5290e6add805dd8632fd3f6f11d20df3622fdc33fe12619bbd9b2e6cbfb349503d4721d61f7556e9598850b1f6a22fb98c84da6f1cdc7c474cd4fa29a87bd6a5d0895f9a350c68161ac25b25a4a308b8d74e99b8d28150e77b06f526ff35acf91ded8944fd4b00d8c2fbf2166a2ec5cea7491c7a783ba7e728cede2e29660a424da32631e25faf3f0ed130451ed404ab8dc19030002fdb7cb4f2159b32913cae7265e9f92e3db5f57303f8e564e36d48d3f9032eaf137fb547b4da61e97b31e7455bb1ed22620bd6f355ff93ff312d584fb8cbd6966ee6b1e66fba378d1c6f651df4c1b44771d38840925a185af89961b4576825bc53c927ac2173f765b1e914ddb1e9ec2f1b708a36826bf8351bd26dd7a312978f2da5fdbbfc323bb64fcc0abe7a547795cdda1359205a0c9eb9637696583e1c46b2e2adead79442e03650780c74b8be92ed32767b97a82af38d4dd3cbe03cd9f669aa0c6918dc3833c96739b95e704defccc0392dcdedb687e2b7eaf848600fe891b713dbcc881139e0ace7708fc173bfffade3974006ed85a7e4432074b3c1626c8ee2e6924c1812b49f2e69aaadc139d35c3f225c86ba517a709c3de093d6e5b6c5edf3082153acdf0c828465f73b7c5644c59632db350572bbe6eeeadab9aa64d47c3c9d8588cf1836668ee56e6c305909ff879e6a24d4e7b74967331fe4a6e8e15479507089c835171f32df51218d0cac890727e5a1409d909085542a3d5c4a94c3ef6a4a5e0c629ce86f2f438284fe98899569490bbd36356dddfc08e955a7fe25009d1aac858779ae9d18260d5167a241e3e1f9f9c3012370bc4764cb5c87d1f928b309a469a73dd5fd4281cd37df42ad67df94b2b64ce2abd1fc568e15f3067561af7cd63e3bb39d4a0e6a2826db49236aa1b1925"}, + {"0000000000000000000000000000000000000000000000000000000000000bf6", "00282ba93bcb748b9fe6c7aeb755207f59377f360909990ac08116e389e69be15ecf7c37cce244bf07f10d031b17ccce0054841041acadfcd43262237f4a1a1661bb5a4e953b64b133eba14de1454723d6fd0615059d44488a0a3dff454506b0df4cbfbdb52a5d31700a4815198a461164efcb02de304ac5791c663676250a0e88623eb044af889e8653b963e22edfb05959550cfc12e11a57f880c66b714c2ea0d9d90506f3d7ac0129671ddd06950d1d59d273a53716da86d23c678627b0f647cf916c39813d96c6a5d020ae019616297718390a2e8651535fb415f3f1bfb69bef56025b60f11a49a0fca3314367c971b3741e304df69f16f66ac6051c203f7710369731b940a6fbe39e572b569e0cba112510c6dfd2df13d07c712279a5cb18ccf72acbcf0870276273ca2810d23861751b8ea43d805a6f9f540b36c0ef5a776fc1e3252259f7476c3eab107f78fa016bd2cd6059a333472c12de5619a53532bd0b244c2884fce94058873dbf052565013bdad9f4ed3755101a5c2dfc485cafe7a93a02ddaf66d5bcc7e934271825a6d4610228d65faf9c33bd4abc47ed0c158d3118044ddcd7bbda0c8da8445547dbec49d5ba69b04c633c45ba2daa9c165ae6a19590ea371d1185c1f6b71d1bf9bce2e612fae9803771f5f866e97d29e339f6cd20cbb4e11b5948ab52242346badb1dc29e8cdff7a30cb7f36e98cc955d8547f21e53b2a3412493ae9e231eb39cf1b5d0c37734280707d7ce057610d3f5b19c0cb9fc2cca0b9c7b8223b6ff8abf2ef1f939f7119f0e650ad1ae19a2f59fb5f3a3adc1b405b45f2f098a0ed1e42bd989715f2be63802daf66e06b795588cb41342e9c1208f0133d10f7942b74a23ffa6f15e4330193ef7aecc465e8d5ffa43aed367d1878f6d9db22d4d2544f779a93f977171d656bbc297321fc51ce13d010072361ec47cf7a00da0146f4e12d2f1f317e8fe229fce879a710d0dddbf6302e95ba70a8bbe36c9e60436007615ee5447af6322c3c97336c1bcb53722cb06822cdee647451097c0343acfdf52122622f76330022f52da86c6db1ec59b00848dcc168c687274080d03fe55484049e8009f6102ffeaa30f1e61117dcc0125327635bc25a55f59d2a4eeb7a93c69f5179e027e62d3c7a1e9f5b5c5e62888b6ac6c0c9ac75cbeb80d0e78538a86f5754bbd348465f44ca69e4a0237345d1d1b3acdd80e5cc16aec3a5ed3e7e79ed392799dc31076a6cecb8714bf3db1214722f8c731c15318de02170e5dc8fdf20bc197eb54108f3eb1e909f6108a580ff95c46400fb99b7ea0bdf381fa13a7bb757ef276320536a1e932e9bbd00dbc60ac6da1932050bf8aed1aab952a1b48c69aaf8082cae078dbe0ead6b267062763ac5297619cd59b7ad28a736d13a63082130b7e02bda966daecc28faf7a116331943400e7d4ef98e2153f99303dd2360944bb625dd997e1116f381f4c5e156eb7e9ede55b51f72f533568daab153eae30a72d1f923a3f0b181ccd77f2b313fae04a2e8cb65f1c13078bf500c90f660dcf0e232015bb577d837c33b42f0d8c5588de54aacecfa6e293e8f5059d48b8d8cce6081bf68686ad56c7ea9580ecb6f911c9fc1911dfe11eeb1729cf0bf5c5297378c0145c653b98ff9fe7a6038b1c01442f6937c7eb239f6fb55cc501c77b3b122a5e5e316a4b656f80f418078149378ab8c8386d6c2e07a1a02d4cc5bc6fc455ade53a8e1e017bf5e62635934396d7aa6487afb186800c368166093839f1a203fe37e276016b8c504863fd36b12abd1982374d053e3252710aac3e6596853a5b706b489ec3cadefff817681b3687a601c17916540e2dc47b2278891dac77188bd713f2cfa189a5ac819acd21a32738095d4005"}, + {"0000000000000000000000000000000000000000000000000000000000000904", "00d3689dcc540c42a60c1955cb611252b16afb7b770a0c061f9f943b16df69b808226189168d9eddee4c20db4c67daa9fb4d750c139d823804418e5f305dc0253293ccfd8a8026728235c937d0fb7a71cd1b874a064eb4d9255120f9d1daa37d595d109e8422db2c083039dc737de08c6f8ab0858d577cfb921af57fd6e11d6034cd9172f7e1ee6733fd76e76f1a449335c79d33b4f36208a183e1715903fb73e103bdd1fadc2252018eed1ada9a05bcf79ef79dc5e348c717d91dea9e3dae536e0813785d7c420504706e8f8a245218a69224e5d2b931e838b9cbcd23bc6c494e8e0b06f131414e9c8b2abf25529d8dff37ed8cf5b6be7833d53b3308a1dd148b93ec6bdb81d26b524f5ded8a3a7b0963099ea8f1b2893bf5bc1fd462a26d2f5b64a33c77951c5276eaf2f7737bebdbe4da87b34f028f86f7a388241d679fc65fb01fa8cee70001c405a293d0dadbaf02c65578c82a734b68cd83cf6a61591552d0bbad2a054c9bc71103dda2f491c3bd9ca532eb4797fbc8410e3fbdcb80a650eff265047199ab1a663cd873187932002d569511fdabda89e4ece0c8779d85d3b0d7f504984ea2431ed291ec944103fec85e78c9802be56006795135cce27499d912417df752d07e8141de3934091d6ed71e674ce79092e357424d2bae89a4df4ded1f64d9f3299b390b9869b24cb07d3e3a77cfdca58c03f008fb96cc6c3a78a5c281fde00d615e53558312142fd1207aec9e437792b57f66f965016739dc34e20934d4df4312742ba90b53b73ac71b77a7099f7f76250bf17ae499f863c800f73f0f5ed1cb57fbfd5ad7064288d9aec3c43c60a754e22d72013dde889a7a770a2dc7076e223e7df64a8373dbd75ed15ad012e2e327f7f716a9920a31c37522b602d30c4500e9fa81f63a13872dfd6c4713d21d67218474606e21157726aa00f3ec523f09d997cc5672ed83be986d3a430ad42e07452773904510fa39013bf6cee0f16beaa7df990b027d8b3ffe11cdc4aaa08331ffa14b2a8e33d7c1b018b20b6703d773b7f7769a92c95b3cdf58603f8b7105658e528d10ea212621a316a0d7db0e5e5d5c0c5108588419d664f2818f2837877d7d4592260134d46e20aaf1a276273fdd83a057a93f7739eb61a2fe875081928c146f70c567d07afab8056027137d3cff9bfc08c1e66717963b0d81b3b4a5a56d5c1d6fe8992fba10d179371c0ecd5fff9236b566fcfbf6faf19b999e0b8cecb0b3c86d63251bb23c13fc7ebd1aa132dd534d9ad56b0e7115e39cba898f3e639b628a869fd4a10d2bd19e9a6ad93ba011754537e1c0c20c46b90fe61ed75d45e31802a1d9f5432b5e7fd03e22cb976fcf125839f51aabd9fb729d55547dd2ba52179bb1566c42d6d61b4752878d2cbc76b42dfa56c24ff2bd538a032eef10474f7dc9a9ec8155c17abdbc9c16d942c935de1286caa3ea6f3bd686a3e77d9bef8ad51c9cb9264c656379b8e1c5dc5aa7421ae4c85e87577e5ef42f8eec68621066d9851454e0f9bd232dbe53f1546d099280e0ab853164df3e909d579329a5503d1be23750287e87276b9979b65936fce3414c9e1875b75a9610d58609bac54e9e98bf733ddbcd3c211c88af9ead39cd3d79c5564041ac7557554df2fe56b3c4374d630c1a056d955bf0256a4d7105b0b1dbf1d8bf9ebd8d1ec3914b339f44acfa72a78e5e7b17b2074319e09117ebd7cbb5788b47dade776d5dd6a01a1b9d57c4842d50cc64d9ad86c7ba543460d45b3bca6e9adec09e0c2b5b8679cddf19265ef13f8a7d9e1aa66456204a44c1d551b5dbcecffb0664bafbd5aef18bdfd8245814713a5cfed1d7e508fc4184c7e912e15df0f1f22e20d90515fe0ae7699a04b77d03bc399217f8f13453"}, + {"0000000000000000000000000000000000000000000000000000000000000869", "000a133e004e8576d8a950506bbf02c532919e76d30d102bd973ac33e17a11924dc35d0d00a239af04c80305f426deeac771c1b0a77a71c616da7f8556c18211b60af3949c3cf59f41c1d6ebd5bbc8911793ce0a015ce8d90ec5759c7be7119fcf1b0b08cbd1feb72407aaeedc0256f7b93c38b494bb6ef8f63571b25b2e13270c46e349ba50ac6bc2352a274520f7c3570a9519fec71b4bce532d6b9d943f8ecb6a698566f5953c0d8324db8fc786f30bd923f64172e83e5afe1f120450e4b6b984592a05cf10c88e057638863657bb164c11604a27af048ba55881f162612327bf0717bb0e6b31e1ec87ced10ca4bcc2a5c969f72eeed6439845cc1116a66606c58e88f8629562176d37a9bea10df69f19b0044fc3dec2e9f823f48a5e4c0746c1369e779a16201c405dd78b5f7501796f8f528f02e0653c61bf3801f2fd8b15e0d0b99398a72470e9d672929c21ee01623e0caf809c5d507eca8698fa8ba2ed40d9adb20beb65902c04a3c9684801c58e43c3f4cb235da033094a6735b9a6a85df77260e97339e3f90fbe546e83211c19fd256a7b4993490867537b10ab25585b845f07fea658644a00617ca25ac34fed58d6eb615a402d1bf431987b4d4eb09b76e2a1e2a93b50c891ad377e13f87d68c6a4b4e77dd324bdcd7b890dc0fa3b741d393cfbfeecd55b692f28aacd80da987edb685f33e50b8384b1fc69183348e295d8d3ed6a49c2139807fd399b9ff2319cb86902de5831e27bd32b1b0d1d82601abe4bf7e9af272daa1e82abfc71d111c0d2d7aa493073f58acb193f7b688434fce9cf07da92f53f3f3e0d275cda0e5d39978333213dbe235124931f2e262c1db1e6690e675595b9d7023d1c52d017a4725d3b4d34cca34c1ace4c61081a23c51671d565af4313d0204b245e833f625dd58b8b689a535c5b1730601db9c1000bf025d7dbbce5bdc612ec5aa69526057dfae4ec1b031c67362473f1edc5832d2dda48ace1d8073f0405611a4dfb8ea2f9048701914a6da6486b1a1198e72e1624b15714798ef272ba3c6a7cc02f065fbfeb2407481c06459f67a1040b80b0b527feb98cf47ba0846ac99367a9e235cb4bc86bd99ee087ff3f573de103079daeb9dc4f1d89ecd5632570f99815004a6e9a4b081dc60c2351b26503c753e58ed7f96ddae0f52d570543bdc5c8c1a6b7cad543caafaa0288f6bf35a5591ea349b6b158fb3f67670205a6f2d586ed76dd7d890a258d6c72a21bef6cec0196a2599ca521b2d4cdc133fb3b06deda3609da34473f665968ab73b6fc50cc0a5436ece951ac49fa81e8c58f75c20e629397b0ed17d0f61b499404cb6f15d1fb8c44f95d9f68168da20be3e48d8d56df832abc2104c84a35bc95d091fb4917e962a68a9480318bb3333bf1406214fe8792ab18031fda9e42267669ea92457855fd5cf5973914d947070751755850dd0da33a24843bc06159eca8b1f2044f927c702c67ea375c02b7dd4169b1f291a0dacfe0504fc75f70d88b77179d78bcd169a7dbc7e03ea3c311c5e0e440dfb737b89fa5eaa6c4469af88a996c692ef2aa6d73f1eeb9cc8817794bed7a2622487a9bd926aa49b1a1cf93a3412273b53a68c4e4fbb6b789d380f7f4419921d1ab19d63a4cdcd6537f40237e020304c5d270d75fb6bb1386c7ed34e47e330a7ddc5729077a864746915ce8f995a2f0bbbd53e1a3d813ffbd04ccd6f952919c934731d43d396370b9c7e051b5e7052ea3c794c2a84f43310346c45b05acd42b78173605615f6f2bd9b9e7b596e2311f9604b94730b9a7ba1450878a90d6d80743a1f2011c95035971af9ab344073bf4f7ec65aca969b565336272059db9b3dbbbcb2729f74f74379327f986d45162bfd1c557dc2ebe79"}, + {"00000000000000000000000000000000000000000000000000000000000015b8", "00e32ae2030b9cd4f0f3d7b26b711d260f14f4013b0489ad07dea76647ea6b6203b8f7cddeb68557f31e025d939699a83153a3dcb38e7ba92d19d35db29844250db9d5924b779af274d4d16370ced59f862d10960aad7919aeaef325ccb127ea7d60e84a40b577a1ad160d75fd1607bea35813a3b6cee4c999106c5726c41caa34dde44c9ea8ffa512e4ab35da2ccfb06c76ae601bac4ee8b42eb7e01ab62dcd771ec5cc41f0778c04eab882b3966abfcb22c08a832d5d172be3fb5b7b3680be82569c9ce734edc9cdc0650502f2f5387c2c0d92ffe7138c3f04f1ab916b1e37dd111e3e9947ae149571d9aa455e349f7c47711f7b952abc3c1897970b0b9f55af84dc84712333123453e01d228ade0e7f33c38673670dbf81326898937e4982f705e75984f811e4020bd5dd973193ca052b8c344077524cdf56dd2bbf1397951bd379187a63ee7762173202c33bd97801d8b6dcaa9fb753b730e24e8d522c462f725fd81e3817b529f5535a2d6ba074849afeea75678df9ea5019e9fca0021b29ead99b45b0325970c5ec71f44ba03207b6d48c5db78902e5068cdaf72d89b2fd9086390c415b1e26551f0f55f359731bd392a2ff1cdc113f32bd62fa8271896bc98af65694480145b130fa7c4c23b95281ade2e2893cadc31134f6e380ce8b08960626d7553a57940977cd83941c0c774a128d3bbc27210959147240c88d26dffaa2d7ecae87ad8bac2cf4a659448f117b9d63d5cc7fe76016d8e5da5ffa9390130be7f42a6199ad2f2b8631741427f4e93c7d7a517113fc63505d54ff0de4401693e96d7f226584b8e99525f93774e312c6dfdc147402316cb11561c4fd8a2d4edc1ae535dbcf3f8fabd7dd03fb00470f739fc1973cd3a72a3f167345a67ce6d5e1d0545a4a225d70af453f0d501b5f991b6f00552aef6634d23877d2485f011362966be5411d2e65b44c40b36615ce7c17bf0c43a3fda585d54ecb86939657ea528a56b5ab19f0ba0f8502d3a6f384a9b875d0fc96cd10657d0018d566293956c918136fb0cb9e799aabed7abab66bdcd24b0be6ed045630e13ff20a6c4600f9912fcd7d7e7b5842420c0a71e522394d7f8536604105ef15df9af8793579437920d3ca20ad23176f3053c51bacfc9d70d14c2b447d0fdc8d8becb115440148fadd65b6bd3b0203d34628da5381f0da4077397e417e4e2e5218af8f16674b9cc3a61373f7d821a3af41b28879add5cd4511337c6fa71546deba5d882fc35450ca8095bf7be3154cfb8da0dbdca95c05f34f87ce200e2c42f5ebd2081fe95054967b10fe6213baf35d29c4fe164857ac54dd6e292f97aa2590dd9765784e51c6c3171be4b20b0ef2ef0acb2b11b0b3b2c7eccc0d950750daa3af1ae23e3236c7a18bf902455453f707bdef10dafe5503e276ae6550cd81aaf7f1b67bb544c2192b3870f608f0eac944d50221bd1246d820d4dfba0480197cd8159885fadce642994520d44ae03a9e5a024873e9ff2e6b2a02ea6aa771f55ed3021169a7aea864bf94ca0f21342f38956a2105a37672263460da2049fcf3c43368b73844b380d39e7a142f11335175b274cfe36c11c23dae1c109bd8a61e32fa2cfd221a09bf3a88981937cc1c1a3100818a17d33562ab6960f19bf6757516ce36ab56c87c52f2bf524864a0e4bca8e7534eac1a54eab38493b0cd64db467c91d2e8cec6169e438a29b5b41a58e6a4b15f17f5725c4b3d95925270217e35aba7e3d95adc8adab0d52d05bf2c49922f33cec318dd85bbbcaaf6abec70630e7da1802e042698e5ff21cc34e5df15901dec9db35b1ee41575a01675b8ed26e06ac51dd815bece91e52580b7cbedf7e4f0615147a78c197bd2b6e516fe3510eaeb87819819de7b49"}, + {"00000000000000000000000000000000000000000000000000000000000010cb", "001c706aaf1d2d270809e6a7d2f0c2e6d14fdeb0fb45a06a7826a9bf5dd9d5351ca95cee56311238e38e265ab1e87b96e535a50b9274a52b7f050e1fdb2a613c8c34e4faeba1f1ab2ff9491d6a3ac2bab6de17f4034e289b81e2f115d98352582f39eabf0ee4b8a63609b1a731b294c00cb7d6050d91af99b5d0c59224bd1cde9904ee54d7c7e069b2548f463389d66fd94f513456a794ce7f6c25ffd2642312db55f952b23adf5e022d01bdc0095ec69602c86404fc877647665a0fbe052b212a8ec9af0e63fa91be31d7aa68f047fbe4f10849c857d496eb0ecb5e516fd336e9ea8ef9bce5e30c712538749a2fd33cbe29a7ba67f63ad4b41cfb8503db6a54cf09d339a6b840961a85daf8cde10e0e7f0530ca61d79d6e71c276a05da5d0fc28658271511a2c66ef1b089ad6ced7897863f35eb94a1f8d7e112340bbbc184fa328cd2480d5eb656ccb3a58f53ffce1018a6b65f742de3599b2e4f0e471a0ce0f4635feae6347b451c0aa6223c319c7559c42a6f621695b15e5026c9eee6324785743a9d1d43eb387fee23dbac6a92be0872fe6f14007e668c2dc4936656d8db61caff901b5c55eb763273f27ee701d04f975c1c7f13b27c40b3e7d442e20f995c8e860c81acf5d9944ae761c7f0774cecfac4f61954822734e27f59de61dfb5df1231e782cb80aa7306163f3c2c96d545a4e7d5cfc7941057dcc28d54fdbebd72f7223bff7e1ef3b993e731f44bae280d9d635c5926e45176255a9f16e1def3472091f7c03bb4d3195d89735d98b4c2841bdc696bd601b3d8d56f4d9f28b4c1dd3882750a9c962e419dac40aec8adc7778b479deedb1105a12a4533c0a3bcbee0d382def81ccc43535c3230cbf24d3c92deabf8ad3122d6509b010c152e0cf6331216de222dce3fa51f712b86777d4cab0f46946a42ecc7cbdc5a52799248c00a39cbb306a08c3f0c592aa08c6140a10bb59daaf1ffb1c3d0612ec912b7dd38cdb744a11109651cd301b33adc52337b3adede8b87cb9704107cb415e83835e2ab4116d9ba7ecfc83c6f49eea3a468cc23ce4c611025ed04229d7cdd9cef38f7dad66517b5d1a9310817a679dfa27f741c62ef98d4771523ada699db7152e806454200f205f27d435311a6d993200a892de3f47a34f10dfa283f92e4ff609a439dd8e5c85b9cef60376ccb769db5c8bda96128e9c55f352d93e39a5cf2d12c6c95aa141355fa1b5e670c90339aa335eff310cd0b53a3e5b391ef35ff3aaf1753eb542dcf689860e57616933f11347891342603b611e7965efd2148309c42bec5b58540d2b5353a9f742111d384009f33116cd0fb759612c0dded96217c01af53564426e3b3f0d7687e1ac911ee2cf3312a1dc60c524f380fd426215b2bfcf3145ec92aa2627bb12647b2ef1ca5e748908ebe5c036433f6749f3d377204a0580f690f50a972565a7288e542a6f6c5c762ed0e8fcd1cf8d9d6c3208ef9f7d6cc4f7b0e701e100ec524258961cb4601725880ba53c9f1f834bd495c42ae382da22705154402a68c9bfda353873f1ebb46396e10daab13e1b14da466184a4af77bce7e0b945b5e8e13f6a578b348b2b37cd3710a197733da82697069a4b39126f9af635bc6d609d5175206a3335f808c820df5b864912f59f55093e3602bc07fad2cc6250c345c3a0d8c7e16a98ef196aebd5a759410dbf9c56468664c211a06d1b699518505f83caccc905db5981d3bb38d7ac89e48549742e7b44e30326ec9b49cec38a614b046ec3f39ab607100ca0d19e94200f4dbd926d77f8fda50ae071e83d13473b3b255b4cf11e2c7252872da68c94d0df9e5626748f2b20af7ba3cf2fb2d64c6160cd984a35151d3963476e8f6c7103fd94370f55edca9a6c15b7699b"}, + {"0000000000000000000000000000000000000000000000000000000000001652", "000a779571c2b44d7fda937d5fe3bb9e115998225c27b127878d952eb93618234f049d80fdf74af479bc0e06a4ee7c3bf1f1f790a3fb656b9b6e102fd4bf1d37bc3d0c342841d58d6bb8077cc872f75dc09ed87b04f649f7b94de4c79ec550c893a5ce09fcca9c2ec41e996e951bcba813bae46a3ae564d7e2b74216ec1837769d422f628791a33469caccf222ff1cb09c0dc644a863e76ba8ad03b418277a4fc8d1a2294079054105f317a155c9e50b10bd6135d568d9bca73ebe0bfc09d6e360b6e5f6e734d355b727b1ad5e1a5cf6f8f2139e437b24ce679b154ce7934e47e5b38b08dea856305c12403b68b803c334b3a1dbb428b5d9719b4d8406198dae290c1a60d8df246f90d17736a7d5d9bb5a2b23b1a24e13b7873bd5f4c8ace717a5922d6d568e09dc3e4ebf46f037a15721cad1c89748c8e9fcdbc452dae3f63d260103fb3305b9bbc47db57d227f9eda01855c386233d5c5b206c0e98c0c571a145574ed8b01c41de3ce955a87961300a4e8e49df529279d792e02212d5f3145587d6f9bf0e275e0033d049d2984ee03a63cf8270aaa9968a7022d9cc7c8118fcdbd8fa9095f60901160ac5966268b05a3f78d5f27819c10a5121741864fc51b4abf4e52c3dd6b7e9692463ca933204b8f4174a9d2bf521f42253339225e0cb1d9788f25ab39d05a0cc696e3d33567c3ba73e78a0a3d42330249a879614ebc8d783eb2fe55ef9e812ec12ce9070dc8068eab914542a27551bd022dc6493c2a5eda760b506784f7ae16ad9cf601b6b9d6d5a0a9a8ff55bd49a9faedfc21ffa5c864fbfd4aea7a73032cd9b0aa10f9ce5e1d6e898ff162463d28792045ed503010e729cd33e68c9375739c87d3b316ac09699e79b22943276286057ecea191fa6353f119c0fc55560db0ab163c6f320637e3d99be0b8454e3c369f3ac24c5ece8f004a3d5a88cc21f292c6e0f1b4568a006b9ccf3c6d0fecc50d8cdc3d7eef5393a96bb16f399e860f377b0668190b2a05a01463d25a8f3570dfb2d4ee1ab83e09bbbdd6a68f20d5fd64414591438ddcd65e0fb14f0f38e8fa732c82cfdfaeb3bdc8c9f43f0d15de0ef9270afcc9395769c74a4863de8df68ba303ee38a39527bfaa4be42d797591ed74720c6cbede28589e6f12404eb2acfbdd50453ea0d44fefae3e1ee6819c409c0f21f70cc903f3eea75415ca5a74236978a80cd3771566da06fc513d0d8a9ee29f65d527f9a165175aa725808bf060ce3e93545ec355301f7671953179194e75b405acc35fa7f56e271ae8cfde2f3f3f94bf18c80fb6a0cf2a072b578791519cf875c9d86c7251ef470fc008c1ba440527f7df41bbfab4b7e5f7995d0f67185d0127afc6ceaebf375415aaec9d93615fbe1e7b3cb78b3ebf9022034719e860395f02425a28b3326800c06bff8570bf9fc7a090724ace349aa35d38831110204bca839e86f91c433102d21f36b1ad5097b42007b665d46b5daf0dffbc41ef49cd90397d0b1e083112709c889af0db7d9c4421831b651f71ade89676a30b889ab706a6d839586cd3e05ee4001112db88e2d12c71c540a2cb40fe79d4d3a5a355ac1345ad7b4b500e63cf4ccfd694df53b44300add9bc60e2c8bb3c5c24e50f9f30df7a196ea8d37300e934656cec2c3f6301707d660176f9edd674304c29fcca1d6d7c9c6352181344ce010873005edaac7f16e597c7258c5b6e8f2646d2d2a45acdd334e5a3ac041da8ba532ddb6e2b2e9ac20670ce59bcdb13b38e193e7ca33fd4faa93e24f02a49186692d1f022e27579fcb5c4b99c0b88ae26667f70b5d8b387b755969eb7711216996aff8cdc401f833dd0a6ef57b9c9270253e70a4dcc455de0e15c546644401f4a99e6a1f6cf366a94ab9b9a9da237"}, + {"00000000000000000000000000000000000000000000000000000000000008a3", "00bd559ad249caded7163150acabdd099a9b71d9ef0164c5d5474368d8e29427fad66f52da483cb24dc9099541a094c501522a12d13abc168d9597124cda1b09e8ccd1940fb1079f9bd523095d4a4222091f71d507a6f46ae61a5e4edbd368ac9755ceeaa96bb5559c2e4a76b261af004ffcfc341e305bab61c33a314a2708136892c6a1907dc94b611adbb024919f8fbab1333e0f7550f67b9399f9d806224251fce1fe581eb48d022f32780d1d0f479a0b90e56a445b40daf958ea08459284091c96cb78c3d1981c6359404b5ddbdfb15910c881a317e1bd338d0703eab164458f006b5850d328ac448b1591940bbe06646c1c3dbdd68a7f5d1d9e035de483af172f1dfea3442f487aa3fa6ad9de25180f771303ba48be41ae02b4517b38db6691c59ca7ce0efa114766102c9da3fe9354cad16525cf1c169af65032e2c9a4b5bc8dc5cea89327f3d65a6f491cf7b9016615bf0ddad08b90fcd33ea6673c1127ea92a5ad39533b84ede54811c7cb33ad216e1748fe98bea0010454b56d5e1540a8fdd57bd47c7d8573d9d75f86902be46a7925e3dc43b88e660e39b18c8e2eb439a4611ef1de111193cc7d2ab1358db7f3e05dd4d1dd1ccf2b166968fdcb5c18cca632f4d4998d4cfa37974a202265e71fbfcbd89729b9760dd036b2d594297f62b25b9f87360f6d6ab39372a5d0c5cf78aaedda7f090d055397152aa3943d374585e8bbbd9c06dc99df8d8b0eeb272c96d535997537317fd3587ee2f154fcb4a1061da9cbe782fa16acb2c2ed771849fd9414ceb99e2e94cc8a489e474fe11fd62fd65c5d55a36eb41bc90600b5e4b0dc3529705a7343f1ed08e52f683588f0153897538b32f38de09372603337e8722146db84700811669028a07ffbcee8408956ce92947cbdcd06cd347b1f3c7f1f23911593352178cd56a998e798dc06018981989865229f597f2042a7335f7441e59308362b91e7a2a1a159c9d27195273b31c36188fa9d64ad067578aa0cb55069aefb837c7de2babdbc56aedca62d51874d6e5929c901ed592cf56347c26f7976a0b90785793c7e42792d272956ae8d38d26e1d91d7b5042668be86fd21bac73d6f0422a6c49ffbb4b95fcadc0a0779e0bf13afe5afd9c1f0afd0f829477b79b7951bc1ec704c5ae185bee863e21b23f935842d53a2a70429dfdaa54f20435069461524c474f9e6c33ceaf717334af18008a3ec8f02d24b7174028570ca3d197713d70bdf671f6dcb6cb2552bbdd1c0c73e06febc042530bf7594d49253596db2ece2c6bba5d362787edc086dec83461bfd2b1af5c285235eddbdd0c8fb95011393ecc91d4b82c9a53b4823e756bc3ed9c97762f51758ef4643d760df66d5220ef2a8f16d0d584aa13b47f9ce5e3c95bfc8b527c4af0840547a96e1bab11c026da16b29b571b3c779b405b64c55fb25921d240815a7c92389e4ce0b400d0394244d275a2883fc9cad184a76bc4b67dafb9dd5f296c1fafdd0d52795c90b2359216bd8a3e7b130d2c6883f4b22c61a7edb043b04f1216b98b229bdee9de584f9c3cf51a18452ec346101275e18e9546dd7b1897ff0e56d27052dbc96e1089edee91097a616bd683320a61c3fcd99131be1ce1b1ad10379500751ea1907c550f3c1df249c5c1edb032aa0be98954b435de075e9daff4c3df0f41282e0196d679fa990cb85ea1946e6d0e212ea1f437cce8b1001b5f53545fcc2d862b11bdfdfc23532bd76650d737e7fb49224b7e56b92d79ac74e5b61ea9b3ccc4b0604c6988324320374bd5269f1739f30c4c1e736150b07a9158b152e45588b66ce13d7f33614dc55d18211ad8ba63caad39fee4c15548ef3d40162d6f5b3cd3aa9afeda0a2e20ba02f83c414e9c5e92a863c0eee"}, + {"000000000000000000000000000000000000000000000000000000000000060a", "01d2f32340427431391d16fabe7b216613d15d80681b62e9583e17017fdb641cb65170856f7448fc274902aeec53d897351cdd2f63ef74a588eac7df5d7bdb1eed33294d8cdf9d8bfcb28b5ee0534a07ec5cc10c03509defdcfe91c7f712d062511b03d228005d65f2173b5f718c4d2dad222fab662bf8f8b2f6d2de109f19b03f9ba70fcebfe3e257de5b5ba2ae85951e382c216f326211cbacdbfe8cc4024be6e39715ec3e368a029e2ddca4e2ca21891592041f956b9b415c5fba8a1915379a8e697b09b7f71302d4d426850ad5352d3e0ffa048387841826e7c6857a38db171572b2f7b6bc158033ee7499e27925c642d403bcd01512425287d303c43f35efc70112da7b91fb6c5506b4acdf0d7f2b0650346a801b280df734410f67d424a583d4efa53f0c8054496d59d613e16950ce9c2ed5fb41625aa25d1f10aea44d57a310e3bd165b333fcd05ff4cfa1a6004062daf12158bbbbae81178efd228997e7cfcd1ba04fc199894475cbdbc55330c482acd6a3b3afbad680d3ef334cc9354919c5af0e977e6d6797ad519e5512d62c72cd398d54ce381652cec4e999da727d3002406c17f4810cd21b7846e64d948d12a5f5a945e4c0408cd0fcf78f00d7f8523d25812fe7034cb1253f3c1098aa4d1c75ff18b110522270bac8e99bcdb365115126f4c0251abfca9d82913a83b2e6eda40503bf007053be4754a85a8f5fbb461fc43d1da45458c6de66f14bd8c1c6645d5b9342d079ddd58ee22189993dff62c94c9e5c56559cbddb7053489ae3f1a55c47340092f616db5fe5349eb3ed6da061bf8f016a02d95291d127b16475487d2bd7ed1d4422428a686bff8f7a9c73872cfdd604f3a92e0efab4f217d6a6339131f6c981515124083e57eb1e1213765564da43eb7b59f0c181dad1ad7954e285fe69c845f9aade7c9199e6a894a0201f8b90a1375ebeda6e68c233f8b163274d512751b65b3919309ba08f986c274de519e5d1c4cbc3a4d06535660c79deefb1810a2da09bac6f911ec1cd37145e3f2cf43d4a6eb9e81e5f904537fbe3dc01c90320b69bcfb5dc3dc1a970225107a416cd9ed0b51a1bc2394aecfbdccd5d9a2c6773b9bd8d241eea492157911464e093a0a77e76ac5746195e606bd58c2bb66d4260c133035d6490d905a842fa95f9f8a1c705a80cd0568245aa790bb0fea59b14fe210a37c5ff175b207448f961807231bf3568a668e5ab8242e71945a891707a696f660922bbeaf1b60bb640732dccecdc8eb3d1a5979e6935540213418c9ea545181c33ea91ec7f70a5c49801547285cf80e91f64ca2fea9ef607faf4365e49ff8a3a8552b7913b847ba52feba644af94b6c21e19ee9250a7927efb755aa243c2a92c9afbd649131a2d4f8bf702107f28fd3d56234bfda6d3eb7e52403e43c54411e0aabe1d111fb845a102b1f96fb5611250eac40028bece3911575d2e34076ad8e272db28405b3bf0dc686fb631251758bd2c9e33a4ef275876626c93d9a2deeeea1e1c785211b63fabf56b3de34dc0d4e7f4f7db40b23aed6f1710d408f407269777a071e737cffdf8f56b78f0fd33f17b0576d45301c82d20ff961fd4459071d4d81e12dca37b5ce2eaf7ad6915f3572ff28d946b6cfbc58766b4b6d7a33feb884e707e5cd61efe796ed9ea021e54f45dfeca3b8954bba3116cf9a92ff2a7fffa2767d9773ae3e7cc8ffe2a60d063ee9ae0c9fbb934d55d6cd4d2ab33d387d9a181769e9215d9892ef4109d2a5a524b3f54ae8ff77561983b9f1291c37d8fdc5b53636de572185001d59c11b242e16e4ac29d37a1f13e74a3bd5062ae31e8f4f1b8568ddf528ebb5d375d368b1e9d535ba2e3aeac93563bf40e3995f3f710cf8895369f17a5a5d14bb6a"}, + {"0000000000000000000000000000000000000000000000000000000000000e67", "006104bfc0d7d12fed0162241b28c22d415cb1cfce0298bccfdba64f6dd1ea60337cc2e4d07a2a2608811c6349d7c3fa24f5de5a3741e4673a334a157bc7fa446497645065ba7bf319e6084ab828ca589bf6e27505865af28b79e631d46d08bc72f7dee29844d9c4012ee92dbb696f5d137d70b668bff7a3b6a0021cbcf10a915468d66e6b379f96321d2d7e5d4a3e9457c03e0ed89d84b09ee9e9902e31d885a573c961d61ca35004f87f677c1c0bc5f488d05331602f80d9b57d42e906ab11d9be0fdc48b8f1968a87ebea39d083394e481675f2a85e35a76fdd1de94005578d7f693a1fd931472a4cda98ea6f19c532d61a6ecba7a5b7377b48ac08c9c485b7d187e37809d46f80d0f9d17b21fc57782e7716962acf2b6d67f246bfcfdf0aae04efd86fb019cd9e44c9133548ef9251a62eb596009bfeff55e01fa46402d88b00789d0173d96c73fed1aaf737ddbc00b9d2a4b0c56406c5c7371a98ef1fe39ba07d0f511c3457dfa65c99893954b45c89c76d493dbc106dfe1ee42ce1dd9f15c79636c50bfe76a99f1b909993451f39549b5e885c95e7ed75e570377be338d25bf783010541dc774b2b7baac350cc925bc2885f926dc6293e9dba3d86d3a8a1ff5a76c75d60892aedc1bd07e907fb5c3eec0af4e501bac201fa4364355e1daf0bef135d45bc07518f3b92dbe4abed79bb7de16eff7ffd05cd3cbbb283b05220f970d1cda05ea280dc3b684f0def76a6f31c62f39e6e340d2f7a74ca45621f32f41c791c8d5a13bce4a12dc77d7f40d502613454282b2a7b7c717ce58ad96444e327f1be4fd68e2915f30f069fa966b8629621a0ea11abd773a0589476ddefbc15d35bb95e6929f3af87317065100c4ce68327c09910467371ee469069fbfd773223ebd0c2a8a2b7c2c11893dc9cd8d091f0e476b4bd443021595daa9cd40b0283a1da707171afc89bf90a414d19b7a116fecac676aad68670af80f5b539eacbb3e14656edab7aed7910e572e131dba74ded3f34256be26e79ab133620c543cd346925515c433c4586b0e65d98df20607c8fc1104d709b7e9c8125e925a574aeae31b271245e4f6c7d311d9cdc318609960e595d15fd9eb75e357d03be1440d9d42f4994094dd387b02d458e56cd8c3eadf638360a1e2d53321d268eab48c07814ab4acf7dbfee02d774daf574feb9c0bce41c63cfe532eb4c7ece86657034e580bb9b47ebf6d9c528e5e0268ef3553bfc25283d6c9916cddf7129f341006f412f50759cea1f3d4bc67d8d5ba8a2e69f863f5add092a5cecbcc6d60bd61b8980affb1f99cbc145b0c19c98a79d8c7fc45cc9aef7dd2ce9bfba74d8dd6dccf18f26409e883317291cc6e7eae04fa97341f1cd640dedef071e10671e32746226230cd132f7a5f612634529c75b7b938d0566f3f07305d276f2e859100851c42ae052fefb4725d679933bb2e36baa6aa3cc7d1f6626c88f19cf7d13b49d05f686b8f37b82b44cb5bd315e0cb4fbb4bb18ecb40e0a0a8993ceaf329fd72e6ff9339cd77f870728a418d00340d554d7015a1022dbe49bd567b4473305afd1d0ac781199a7e58f2041bd9f1184fb60480d34924e1a793625d7ca514fea1f015a391e5dda8017155b7c80a94b9ba6863523682d2873bbc09e6cb0056ec4d7370db33ed7366511fead2d3b2a825990e922c1cd3180eba683eead129e264a09d92845edb40a2bb5db26a624b12f84e4d60d3ad954c1daefb622ab2d61ab41644e36bffc14a497a8d99bc14fc77072250b0d81157f95aeb0d52c133bf7a87fca50c138f61e37eecc99dcd121bc98b1d843624f90778e7a9ed64c18ca1a88450e5c76a82751948a291caeb301585e78263ddfed46da579fa16574a37776c8ad31d6dc2a66"}, + {"0000000000000000000000000000000000000000000000000000000000001a1e", "02c15514adacf021e7dc629d773d4eb9b9d4fb7e2f0bd2407da31e4923d48236140e69808da546b6e5ed0cc7515e51102a70df67f67cdc3721130fe67db65e300bcae2168cad7ecd25447c0949243d3eca0aec9b079344297cc4dcef96a1f1dc14e7a4059990f4e583092ad5c6d947c1c09a582435065637e1296eec816d12102b2758094c0665a0b469d176335e4639b368d420611dbf82d13dd5e1b1624b7868c5060459d8cdf80619f75625f4561df9a3e7765dc388a2503bbcf3e01d962c8042f9de6bd39a6538ece99fe6dc399bfa390e8c8d9cce6452ebe7ba1945b0756ee39e4f1e636f5605cea4f4f2ba91f0a2f748827ad725d5177bfb5127b53c7827d385a12d4432a0a8a244bd62d8ee88d028009364b3d94fcd17e6f3f112b15c757a6d9e627937afffbeca1284d3f891a703cd57907289853bd917494d324df71a6e3b2518473233b998e232fab930850783928ff2946959551d8174fbc01ef357c05acdd40a564fe99b4b87fd79a0f247b0bffd1da7a635bb6720f9947f9b26866da04a96fa00b956d6255056d01627cbd41f88d7636b757e72e530c1f4855720d7b6370f2e89749bc85394b74ef46d446aae767aa298e0304c989eea99608f3f8802666778bbbf0f403edf9b8c1ddff3b0abdb2f914b4ff3157ebb9244c83910d2ff26e3f7bd2b4a41278da2e35914f3339eb60eb673d3094e114395921ec7f999b37e1132719b6bec3c4c20364b2635f8d68160f2b9c462482c7d569303df78c80b9c0bd14049a15985ea3aa5a6578a3b5c6b9f73b214f48ad797171f492bad5211b164a5561fdbdf0d8211dc5722a48b4de16de122ffc1f0fa5657489889862c63a3b82b4c7af7f30097eeaffd955659eefa7d2e1447b37fbf659367815b92dd28aa31cd569810dd7f429f2a486ddae7d5c131a667b2c5fca7bd757fa6a802dc0a288dc3a7d92391f496186a32132bc879c0c4042f87432b1cdbe569da105d325097d821c193e3b106bea48a55164bd7d2de0c08bf7d3f2772961cf3b31c5f07a61de7c1d3738cb3eab256992e568cbaa24103d892044f61f23dd296c23936c873c51e383d1b82144dc3e97e8b9f3b916f916590b29c93c9bcde6db01a557312778f562d5dd16877d1f6355a30b29c0ec52cd2ae11171e93db29f57896b6567d6ab74d7ceea804dfd8a0910a5ee2df875427853c2da123307bb0dd59181dfedd9783e4e85a86365b58260a9fb67d644b08dad713428f6306a0ed4973c9f9b9b6658d1fd4693b5da341dd5194d5942ea677b93be4d9c73c548e38092341a411ce321f986e553ff6745dc2464816b8422c935c0f40633a5771d7b3edddc1648e2b453308200d79af05b952d17aa994a1d1d8fbbdf68ded95051324de11b9ff90bd2e9af81625a8d1cd5285bd1e6f0e039a5d2e5b43aa49197ea123b3481c60fa99d43185098683ee4620e871fd1b9118c6998eb4bb9d1dea490b8222218f5792fce4f516a849c15a6ed5c5bc564d122f34c37c22439b4d450223a340729b42c7fe162a08fc2af542dbe8f3492330c8b2f107de7d2ddf172a0deb5df3a94a081968b5b5a321f3a3a5ac94d46dcb12e6f7b889a7dcf96f1e48562be67dca745f5f1d8f2c36fca37ee8cea56414933c655e91a595e3940cee08215963559728f4dbbd32900d1fc23a62fa75dfff13db343b85c73a6aba40d7d3ecc9a44e434d179bfa2636a7cb3b8adfdca3f76a96d3f3baaaf1aaba183c3deec644305f6f7d1fdcc798e36fb2d675aa376a4f12c6aa342214dc1bb979698e71e9a6b67a709e34822f6454c866dcfa7b60f91833346643c2dd12bf0b0f1575133e6905a5e0b28773c9697a47cea5c7d7c1e62bf283b4f6243523e887b566e56a1372f297bb3e33"}, + {"00000000000000000000000000000000000000000000000000000000000022de", "007f388bed6b91756ea3e0866716ef6e9485fae6160195c7cda5c1e43f96ee359e105bcf4e8c293690420939124f04a0196363910421187811575929db40500b0bfdd1e8964aa334b801e3339a336d585a30852f1dc294a2d3d36f9ecc747458f3d41b4572415496df2a9fb1f882156cdabf9f65e681f38019865d6d47482277e24c9b8973eb34a41254faae4c5e2caa9dde5925ec118f3d8fa767ae00f434645957154367afe72000c59c79182c8faddd24424b9ebbb09ccd651b00540c96b9c7eec648a28a1d72c2e575d0f2250078511a011598db8e0788edf0ddc15ae24b62f63d6f93f71a2743a3c43ece55471a9802a76f31561a6f365c3647029bfa736395883afc0632bc25d4a8661b25d5aa0310f3c3fd3a183e75d359d6de3e5910b5dfbb74b7660af906917dc42b12e3e484aae1dbd20eaee037ef301572b7fc24d85b4aff9c82b27dcd421cee1639230d0188fec59f0dd4c0ced69c1ad07abd23692b1bd30735af942df597dcf6f403a36371bc416cf3e29a58570f586b05c357dc49515689788ad9581b8887dd913a41dc35e1ac9c9f9f4ea534eb6b36cc8af0299b6d3905750425da0366bdc59a7824477d7946b6f35c4ec90b8e61790fa74a4fa92396ea856661027828d40abb11dbe36bba516fe8ec8913106677285a4790d8034d1d1bf9fd87990889ddffc369b954a3d1c172be7e1812226c2b100cbe82c42bf4423456b6cb2bac3b4828135cb54f7a933a01f7f4a2057ad92136ba8e19fec313b412d43c089a71f06fd1625329b78d49ac92c59e4080932ddb1645910fd874dfb1f358e214231f62041acc41fd2c4e7b7127b3042459e1457f6b307fce9825aa4d2b942277f52665f2a77dd107b4f16cb3280f20c7551ff6cd855f97a6144131f69bab5648fb4b81261eefbf629094e8bcc4e36077f46d51a647da51fc01dca9a9ac12e2f7e2e2b1c9229dae099e95370177143d3b38ab661f19758494a01b32f0c27155b45a872a867dc50f9d76473695e9e2c4f9357f5ba6bb6c455d985f4e2c21486fde6576c6a8ceda6e010a7dc2b504130f429ac33376781ee4af5bbe8d768005bc4cb5092b15c4f296a8bd8c54a298eecd790a5161755a8605cc46bf890b8ff93d508501842b78c7261e5deeb1096891c528a300e57bf2f0aa9e8af2623cdf16bba20427704120484b6af8be26e4983d2685c783ce85d0174f84598719c6beefcc3603a94d4aa62750725df50671d7f9903ec255f779643ebd2fd8122fae3319e61928dcdaa44880d6a483140de63d2d7d7dc9dd449e0ee00d908e0f2164fc054198641e8fb0d74279c9b4117884b9335028a9f50c7223d3c03675ecf73329e52603f77f20cffba99356e51a365b75825f7db56d77542784f3c2663c493a2e564d73f753e9d6ebb0c2f2027a2330a7117c67a20507474fc47282a02cb572de17bbc7a335959316f74a05e3687cfb5227bc5b1b7f084f50902760e77740d420df9a495521c09b911e5f199a8343918b8386fc74f22552a76524a22c8c70ff06084e7fefe9b3ab98e004fadf35eb5f60483f287851712d90ebdd6b512877170d3b7fb34f16813917ae3b5ed54ede6081bdd7cc646fb336658121fd8fbafc52959b48d13375dfa4ce8616c157533a05ee1dc1120f215c348b54357d68adb4da7f5f48d55c005b3e7a23d05746e44d968f7601d4dbdff702861030d6e3a4140e6e1a29978be541f713f8e2cc9aa1ac32fecc941ee4aa4c41bc7f91ea5328ff87cbf35a8de17d1d3d1ad6d4384b0df52d10b3984d62e1678e86dd150ee425490bc727ee7107fda0f5d2433ab1c5d407be9d123fad5c201355601d926d3923787be86a4aa5be0b8d5750171ad658f8e97798b5dcaed46345a9af70c441"}, }; -/* TODO: Regenerate miner tests after launch // NOTE: These tests rely on CreateNewBlock doing its own self-validation! BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { @@ -144,6 +143,10 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) CMutableTransaction tx,tx2; CScript script; uint256 hash; + TestMemPoolEntryHelper entry; + entry.nFee = 11; + entry.dPriority = 111.0; + entry.nHeight = 11; LOCK(cs_main); fCheckpointsEnabled = false; @@ -166,6 +169,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // will be closer to the tip, and blocks will appear slower. pblock->nTime = chainActive.Tip()->GetMedianTimePast()+6*Params().GetConsensus().nPowTargetSpacing; CMutableTransaction txCoinbase(pblock->vtx[0]); + txCoinbase.nVersion = 1; txCoinbase.vin[0].scriptSig = CScript() << (chainActive.Height()+1) << OP_0; txCoinbase.vout[0].scriptPubKey = CScript(); pblock->vtx[0] = CTransaction(txCoinbase); @@ -175,7 +179,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) pblock->nNonce = uint256S(blockinfo[i].nonce_hex); pblock->nSolution = ParseHex(blockinfo[i].solution_hex); -*//* +/* { arith_uint256 try_nonce(0); unsigned int n = Params().EquihashN(); @@ -254,7 +258,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) std::cout << "\"}," << std::endl; } -*//* +*/ CValidationState state; BOOST_CHECK(ProcessNewBlock(state, NULL, pblock, true, NULL)); @@ -281,7 +285,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { tx.vout[0].nValue -= 10; hash = tx.GetHash(); - mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); + bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase + mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); @@ -301,7 +306,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { tx.vout[0].nValue -= 350; hash = tx.GetHash(); - mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); + bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase + mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); @@ -310,7 +316,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // orphan in mempool hash = tx.GetHash(); - mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); + mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx)); BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -320,7 +326,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, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); + mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vin[0].prevout.hash = hash; tx.vin.resize(2); tx.vin[1].scriptSig = CScript() << OP_1; @@ -328,7 +334,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[1].prevout.n = 0; tx.vout[0].nValue = 49000LL; hash = tx.GetHash(); - mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); + mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -339,7 +345,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].scriptSig = CScript() << OP_0 << OP_1; tx.vout[0].nValue = 0; hash = tx.GetHash(); - mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); + mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -352,12 +358,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) script = CScript() << OP_0; tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script)); hash = tx.GetHash(); - mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); + mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vin[0].prevout.hash = hash; tx.vin[0].scriptSig = CScript() << (std::vector)script; tx.vout[0].nValue -= 10000; hash = tx.GetHash(); - mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); + mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -368,10 +374,10 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = 49000LL; tx.vout[0].scriptPubKey = CScript() << OP_1; hash = tx.GetHash(); - mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); + mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vout[0].scriptPubKey = CScript() << OP_2; hash = tx.GetHash(); - mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); + mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -397,7 +403,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, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); + mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK(!CheckFinalTx(tx, LOCKTIME_MEDIAN_TIME_PAST)); // time locked @@ -411,12 +417,12 @@ 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, CTxMemPoolEntry(tx2, 11, GetTime(), 111.0, 11)); + mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx2)); BOOST_CHECK(!CheckFinalTx(tx2, LOCKTIME_MEDIAN_TIME_PAST)); BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); - // Neither tx should have make it into the template. + // Neither tx should have made it into the template. BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1); delete pblocktemplate; @@ -443,6 +449,5 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) fCheckpointsEnabled = true; fCoinbaseEnforcedProtectionEnabled = true; } -*/ BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index 6b189a6b5..a7decc94e 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "consensus/upgrades.h" #include "key.h" #include "keystore.h" #include "main.h" @@ -26,9 +27,9 @@ typedef vector valtype; BOOST_FIXTURE_TEST_SUITE(multisig_tests, BasicTestingSetup) CScript -sign_multisig(CScript scriptPubKey, vector keys, CTransaction transaction, int whichIn) +sign_multisig(CScript scriptPubKey, vector keys, CTransaction transaction, int whichIn, uint32_t consensusBranchId) { - uint256 hash = SignatureHash(scriptPubKey, transaction, whichIn, SIGHASH_ALL); + uint256 hash = SignatureHash(scriptPubKey, transaction, whichIn, SIGHASH_ALL, 0, consensusBranchId); CScript result; result << OP_0; // CHECKMULTISIG bug workaround @@ -44,10 +45,12 @@ sign_multisig(CScript scriptPubKey, vector keys, CTransaction transaction, BOOST_AUTO_TEST_CASE(multisig_verify) { + uint32_t consensusBranchId = SPROUT_BRANCH_ID; unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC; ScriptError err; CKey key[4]; + CAmount amount = 0; for (int i = 0; i < 4; i++) key[i].MakeNewKey(true); @@ -82,21 +85,21 @@ BOOST_AUTO_TEST_CASE(multisig_verify) // Test a AND b: keys.assign(1,key[0]); keys.push_back(key[1]); - s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK(VerifyScript(s, a_and_b, flags, MutableTransactionSignatureChecker(&txTo[0], 0), &err)); + s = sign_multisig(a_and_b, keys, txTo[0], 0, consensusBranchId); + BOOST_CHECK(VerifyScript(s, a_and_b, flags, MutableTransactionSignatureChecker(&txTo[0], 0, amount), consensusBranchId, &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); for (int i = 0; i < 4; i++) { keys.assign(1,key[i]); - s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, flags, MutableTransactionSignatureChecker(&txTo[0], 0), &err), strprintf("a&b 1: %d", i)); + s = sign_multisig(a_and_b, keys, txTo[0], 0, consensusBranchId); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, flags, MutableTransactionSignatureChecker(&txTo[0], 0, amount), consensusBranchId, &err), strprintf("a&b 1: %d", i)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err)); keys.assign(1,key[1]); keys.push_back(key[i]); - s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, flags, MutableTransactionSignatureChecker(&txTo[0], 0), &err), strprintf("a&b 2: %d", i)); + s = sign_multisig(a_and_b, keys, txTo[0], 0, consensusBranchId); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, flags, MutableTransactionSignatureChecker(&txTo[0], 0, amount), consensusBranchId, &err), strprintf("a&b 2: %d", i)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); } @@ -104,21 +107,21 @@ BOOST_AUTO_TEST_CASE(multisig_verify) for (int i = 0; i < 4; i++) { keys.assign(1,key[i]); - s = sign_multisig(a_or_b, keys, txTo[1], 0); + s = sign_multisig(a_or_b, keys, txTo[1], 0, consensusBranchId); if (i == 0 || i == 1) { - BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, flags, MutableTransactionSignatureChecker(&txTo[1], 0), &err), strprintf("a|b: %d", i)); + BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, flags, MutableTransactionSignatureChecker(&txTo[1], 0, amount), consensusBranchId, &err), strprintf("a|b: %d", i)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } else { - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, flags, MutableTransactionSignatureChecker(&txTo[1], 0), &err), strprintf("a|b: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, flags, MutableTransactionSignatureChecker(&txTo[1], 0, amount), consensusBranchId, &err), strprintf("a|b: %d", i)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); } } s.clear(); s << OP_0 << OP_1; - BOOST_CHECK(!VerifyScript(s, a_or_b, flags, MutableTransactionSignatureChecker(&txTo[1], 0), &err)); + BOOST_CHECK(!VerifyScript(s, a_or_b, flags, MutableTransactionSignatureChecker(&txTo[1], 0, amount), consensusBranchId, &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_SIG_DER, ScriptErrorString(err)); @@ -127,15 +130,15 @@ BOOST_AUTO_TEST_CASE(multisig_verify) { keys.assign(1,key[i]); keys.push_back(key[j]); - s = sign_multisig(escrow, keys, txTo[2], 0); + s = sign_multisig(escrow, keys, txTo[2], 0, consensusBranchId); if (i < j && i < 3 && j < 3) { - BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, flags, MutableTransactionSignatureChecker(&txTo[2], 0), &err), strprintf("escrow 1: %d %d", i, j)); + BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, flags, MutableTransactionSignatureChecker(&txTo[2], 0, amount), consensusBranchId, &err), strprintf("escrow 1: %d %d", i, j)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } else { - BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, flags, MutableTransactionSignatureChecker(&txTo[2], 0), &err), strprintf("escrow 2: %d %d", i, j)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, flags, MutableTransactionSignatureChecker(&txTo[2], 0, amount), consensusBranchId, &err), strprintf("escrow 2: %d %d", i, j)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); } } @@ -276,6 +279,8 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1) BOOST_AUTO_TEST_CASE(multisig_Sign) { + uint32_t consensusBranchId = SPROUT_BRANCH_ID; + // Test SignSignature() (and therefore the version of Solver() that signs transactions) CBasicKeyStore keystore; CKey key[4]; @@ -312,7 +317,7 @@ BOOST_AUTO_TEST_CASE(multisig_Sign) for (int i = 0; i < 3; i++) { - BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i)); + BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0, SIGHASH_ALL, consensusBranchId), strprintf("SignSignature %d", i)); } } diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp index 8837e56c3..a530749e7 100644 --- a/src/test/policyestimator_tests.cpp +++ b/src/test/policyestimator_tests.cpp @@ -16,6 +16,7 @@ BOOST_FIXTURE_TEST_SUITE(policyestimator_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) { CTxMemPool mpool(CFeeRate(1000)); + TestMemPoolEntryHelper entry; CAmount basefee(2000); double basepri = 1e6; CAmount deltaFee(100); @@ -63,7 +64,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) for (int k = 0; k < 5; k++) { // add 4 fee txs for every priority tx tx.vin[0].prevout.n = 10000*blocknum+100*j+k; // make transaction unique uint256 hash = tx.GetHash(); - mpool.addUnchecked(hash, CTxMemPoolEntry(tx, feeV[k/4][j], GetTime(), priV[k/4][j], blocknum, mpool.HasNoInputsOf(tx))); + mpool.addUnchecked(hash, entry.Fee(feeV[k/4][j]).Time(GetTime()).Priority(priV[k/4][j]).Height(blocknum).FromTx(tx, &mpool)); txHashes[j].push_back(hash); } } @@ -132,7 +133,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) for (int k = 0; k < 5; k++) { // add 4 fee txs for every priority tx tx.vin[0].prevout.n = 10000*blocknum+100*j+k; uint256 hash = tx.GetHash(); - mpool.addUnchecked(hash, CTxMemPoolEntry(tx, feeV[k/4][j], GetTime(), priV[k/4][j], blocknum, mpool.HasNoInputsOf(tx))); + mpool.addUnchecked(hash, entry.Fee(feeV[k/4][j]).Time(GetTime()).Priority(priV[k/4][j]).Height(blocknum).FromTx(tx, &mpool)); txHashes[j].push_back(hash); } } @@ -168,7 +169,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) for (int k = 0; k < 5; k++) { // add 4 fee txs for every priority tx tx.vin[0].prevout.n = 10000*blocknum+100*j+k; uint256 hash = tx.GetHash(); - mpool.addUnchecked(hash, CTxMemPoolEntry(tx, feeV[k/4][j], GetTime(), priV[k/4][j], blocknum, mpool.HasNoInputsOf(tx))); + mpool.addUnchecked(hash, entry.Fee(feeV[k/4][j]).Time(GetTime()).Priority(priV[k/4][j]).Height(blocknum).FromTx(tx, &mpool)); CTransaction btx; if (mpool.lookup(hash, btx)) block.push_back(btx); @@ -187,9 +188,8 @@ 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"); - + BOOST_CHECK_EQUAL(txcs.FindBucketIndex(-1.0), 0); BOOST_CHECK_EQUAL(txcs.FindBucketIndex(0.0), 0); BOOST_CHECK_EQUAL(txcs.FindBucketIndex(1.0), 1); diff --git a/src/test/reverselock_tests.cpp b/src/test/reverselock_tests.cpp index e7e627ae0..8bdff9700 100644 --- a/src/test/reverselock_tests.cpp +++ b/src/test/reverselock_tests.cpp @@ -42,22 +42,18 @@ BOOST_AUTO_TEST_CASE(reverselock_errors) BOOST_CHECK(failed); BOOST_CHECK(!lock.owns_lock()); - // Make sure trying to lock a lock after it has been reverse locked fails - failed = false; - bool locked = false; + // Locking the original lock after it has been taken by a reverse lock + // makes no sense. Ensure that the original lock no longer owns the lock + // after giving it to a reverse one. lock.lock(); BOOST_CHECK(lock.owns_lock()); - - try { + { reverse_lock > rlock(lock); - lock.lock(); - locked = true; - } catch(...) { - failed = true; + BOOST_CHECK(!lock.owns_lock()); } - BOOST_CHECK(locked && failed); + BOOST_CHECK(failed); BOOST_CHECK(lock.owns_lock()); } diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index c2b29ab67..16c713300 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -7,6 +7,7 @@ #include "base58.h" #include "netbase.h" +#include "utilstrencodings.h" #include "test/test_bitcoin.h" @@ -251,7 +252,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban) BOOST_CHECK(banned_until.get_int64() > now); BOOST_CHECK(banned_until.get_int64()-now <= 200); - // must throw an exception because 127.0.0.1 is in already banned suubnet range + // 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); BOOST_CHECK_NO_THROW(CallRPC(string("setban 127.0.0.0/24 remove")));; @@ -295,4 +296,44 @@ BOOST_AUTO_TEST_CASE(rpc_ban) BOOST_CHECK_EQUAL(adr.get_str(), "2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); } + +BOOST_AUTO_TEST_CASE(rpc_raw_create_overwinter_v3) +{ + SelectParams(CBaseChainParams::REGTEST); + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); + + // Sample regtest address: + // public: tmHU5HLMu3yS8eoNvbrU1NWeJaGf6jxehru + // private: cW1G4SxEm5rui2RQtBcSUZrERTVYPtyZXKbSi5MCwBqzbn5kqwbN + + UniValue r; + std::string prevout = + "[{\"txid\":\"b4cc287e58f87cdae59417329f710f3ecd75a4ee1d2872b7248f50977c8493f3\"," + "\"vout\":1}]"; + r = CallRPC(string("createrawtransaction ") + prevout + " " + + "{\"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( + ParseHexToUInt32(find_value(r.get_obj(), "versiongroupid").get_str()), + OVERWINTER_VERSION_GROUP_ID); + + // Sanity check we can deserialize the raw hex + // 030000807082c40301f393847c97508f24b772281deea475cd3e0f719f321794e5da7cf8587e28ccb40100000000ffffffff0100ab9041000000001976a914550dc92d3ff8d1f0cb6499fddf2fe43b745330cd88ac000000000000000000 + CDataStream ss(ParseHex(rawhex), SER_DISK, PROTOCOL_VERSION); + CTransaction tx; + ss >> tx; + CDataStream ss2(ParseHex(rawhex), SER_DISK, PROTOCOL_VERSION); + CMutableTransaction mtx; + ss2 >> mtx; + BOOST_CHECK_EQUAL(tx.GetHash().GetHex(), CTransaction(mtx).GetHash().GetHex()); + + // Revert to default + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); +} + + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/rpc_wallet_tests.cpp b/src/test/rpc_wallet_tests.cpp index e1fcd8829..e7cc0570f 100644 --- a/src/test/rpc_wallet_tests.cpp +++ b/src/test/rpc_wallet_tests.cpp @@ -16,7 +16,10 @@ #include "rpcserver.h" #include "asyncrpcqueue.h" #include "asyncrpcoperation.h" +#include "wallet/asyncrpcoperation_mergetoaddress.h" #include "wallet/asyncrpcoperation_sendmany.h" +#include "wallet/asyncrpcoperation_shieldcoinbase.h" + #include "rpcprotocol.h" #include "init.h" @@ -44,6 +47,13 @@ bool find_error(const UniValue& objError, const std::string& expected) { return find_value(objError, "message").get_str().find(expected) != string::npos; } +static UniValue ValueFromString(const std::string &str) +{ + UniValue value; + BOOST_CHECK(value.setNumStr(str)); + return value; +} + BOOST_FIXTURE_TEST_SUITE(rpc_wallet_tests, TestingSetup) BOOST_AUTO_TEST_CASE(rpc_addmultisig) @@ -289,20 +299,20 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_getbalance) LOCK(pwalletMain->cs_wallet); - + BOOST_CHECK_THROW(CallRPC("z_getbalance too many args"), runtime_error); BOOST_CHECK_THROW(CallRPC("z_getbalance invalidaddress"), runtime_error); BOOST_CHECK_NO_THROW(CallRPC("z_getbalance tmC6YZnCUhm19dEXxh3Jb7srdBJxDawaCab")); BOOST_CHECK_THROW(CallRPC("z_getbalance tmC6YZnCUhm19dEXxh3Jb7srdBJxDawaCab -1"), runtime_error); BOOST_CHECK_NO_THROW(CallRPC("z_getbalance tmC6YZnCUhm19dEXxh3Jb7srdBJxDawaCab 0")); BOOST_CHECK_THROW(CallRPC("z_getbalance tnRZ8bPq2pff3xBWhTJhNkVUkm2uhzksDeW5PvEa7aFKGT9Qi3YgTALZfjaY4jU3HLVKBtHdSXxoPoLA3naMPcHBcY88FcF 1"), runtime_error); - - + + BOOST_CHECK_THROW(CallRPC("z_gettotalbalance too manyargs"), runtime_error); BOOST_CHECK_THROW(CallRPC("z_gettotalbalance -1"), runtime_error); BOOST_CHECK_NO_THROW(CallRPC("z_gettotalbalance 0")); - - + + BOOST_CHECK_THROW(CallRPC("z_listreceivedbyaddress too many args"), runtime_error); // negative minconf not allowed BOOST_CHECK_THROW(CallRPC("z_listreceivedbyaddress tmC6YZnCUhm19dEXxh3Jb7srdBJxDawaCab -1"), runtime_error); @@ -374,7 +384,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_exportwallet) CZCPaymentAddress paymentAddress = pwalletMain->GenerateNewZKey(); pwalletMain->GetPaymentAddresses(addrs); 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("%%%%%%%%"); @@ -402,10 +412,10 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_exportwallet) std::string s1 = paymentAddress.ToString(); std::string s2 = CZCSpendingKey(key).ToString(); - + // There's no way to really delete a private key so we will read in the // exported wallet file and search for the spending key and payment address. - + EnsureWalletIsUnlocked(); ifstream file; @@ -434,7 +444,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_exportwallet) BOOST_AUTO_TEST_CASE(rpc_wallet_z_importwallet) { LOCK2(cs_main, pwalletMain->cs_wallet); - + // error if no args BOOST_CHECK_THROW(CallRPC("z_importwallet"), runtime_error); @@ -446,7 +456,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importwallet) auto testPaymentAddress = testSpendingKey.address(); std::string testAddr = CZCPaymentAddress(testPaymentAddress).ToString(); std::string testKey = CZCSpendingKey(testSpendingKey).ToString(); - + // create test data using the random key std::string format_str = "# Wallet dump created by Komodo v0.11.2.0.z8-9155cc6-dirty (2016-08-11 11:37:00 -0700)\n" "# * Created on 2016-08-12T21:55:36Z\n" @@ -458,10 +468,10 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importwallet) "%s 2016-08-12T21:55:36Z # zaddr=%s\n" "\n" "\n# End of dump"; - + boost::format formatobject(format_str); 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(); @@ -474,19 +484,19 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importwallet) std::set addrs; pwalletMain->GetPaymentAddresses(addrs); BOOST_CHECK(addrs.size()==0); - + // import test data from file into wallet BOOST_CHECK_NO_THROW(CallRPC(string("z_importwallet ") + path)); - + // wallet should now have one zkey pwalletMain->GetPaymentAddresses(addrs); BOOST_CHECK(addrs.size()==1); - + // check that we have the spending key for the address CZCPaymentAddress address(testAddr); auto addr = address.Get(); BOOST_CHECK(pwalletMain->HaveSpendingKey(addr)); - + // Verify the spending key is the same as the test data libzcash::SpendingKey k; BOOST_CHECK(pwalletMain->GetSpendingKey(addr, k)); @@ -504,10 +514,10 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) UniValue retValue; int n1 = 1000; // number of times to import/export int n2 = 1000; // number of addresses to create and list - + // error if no args - BOOST_CHECK_THROW(CallRPC("z_importkey"), runtime_error); - BOOST_CHECK_THROW(CallRPC("z_exportkey"), runtime_error); + BOOST_CHECK_THROW(CallRPC("z_importkey"), runtime_error); + BOOST_CHECK_THROW(CallRPC("z_exportkey"), runtime_error); // error if too many args BOOST_CHECK_THROW(CallRPC("z_importkey way too many args"), runtime_error); @@ -548,7 +558,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) for (UniValue element : arr.getValues()) { myaddrs.insert(element.get_str()); } - + // Make new addresses for the set for (int i=0; iGenerateNewZKey()).ToString()); @@ -558,19 +568,19 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) int numAddrs = myaddrs.size(); BOOST_CHECK(numAddrs == n1+n2); pwalletMain->GetPaymentAddresses(addrs); - BOOST_CHECK(addrs.size()==numAddrs); - + BOOST_CHECK(addrs.size()==numAddrs); + // Ask wallet to list addresses BOOST_CHECK_NO_THROW(retValue = CallRPC("z_listaddresses")); arr = retValue.get_array(); BOOST_CHECK(arr.size() == numAddrs); - + // Create a set from them std::unordered_set listaddrs; for (UniValue element : arr.getValues()) { listaddrs.insert(element.get_str()); } - + // Verify the two sets of addresses are the same BOOST_CHECK(listaddrs.size() == numAddrs); BOOST_CHECK(myaddrs == listaddrs); @@ -623,19 +633,19 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_async_operations) BOOST_CHECK(ids.size()==0); std::shared_ptr op1 = std::make_shared(); - q->addOperation(op1); + q->addOperation(op1); BOOST_CHECK(q->getOperationCount() == 1); - + OperationStatus status = op1->getState(); BOOST_CHECK(status == OperationStatus::READY); - + AsyncRPCOperationId id1 = op1->getId(); int64_t creationTime1 = op1->getCreationTime(); - + q->addWorker(); BOOST_CHECK(q->getNumberOfWorkers() == 1); - - // an AsyncRPCOperation doesn't do anything so will finish immediately + + // an AsyncRPCOperation doesn't do anything so will finish immediately std::this_thread::sleep_for(std::chrono::seconds(1)); BOOST_CHECK(q->getOperationCount() == 0); @@ -649,7 +659,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_async_operations) BOOST_CHECK_EQUAL(op1->getResult().isNull(), false); BOOST_CHECK_EQUAL(op1->getStateAsString(), "success"); BOOST_CHECK_NE(op1->getStateAsString(), "executing"); - + // Create a second operation which just sleeps std::shared_ptr op2(new MockSleepOperation(2500)); AsyncRPCOperationId id2 = op2->getId(); @@ -683,8 +693,8 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_async_operations) BOOST_CHECK_EQUAL(op2->isSuccess(), true); BOOST_CHECK_EQUAL(op2->isCancelled(), false); BOOST_CHECK_EQUAL(op3->isCancelled(), true); - - + + v = q->getAllOperationIds(); std::copy( v.begin(), v.end(), std::inserter( opids, opids.end() ) ); BOOST_CHECK(opids.size() == 3); @@ -702,7 +712,7 @@ class CountOperation : public AsyncRPCOperation { public: CountOperation() {} virtual ~CountOperation() {} - virtual void main() { + virtual void main() { set_state(OperationStatus::EXECUTING); gCounter++; std::this_thread::sleep_for(std::chrono::milliseconds(1000)); @@ -714,7 +724,7 @@ public: BOOST_AUTO_TEST_CASE(rpc_wallet_async_operations_parallel_wait) { gCounter = 0; - + std::shared_ptr q = std::make_shared(); q->addWorker(); q->addWorker(); @@ -739,7 +749,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_async_operations_parallel_wait) BOOST_AUTO_TEST_CASE(rpc_wallet_async_operations_parallel_cancel) { gCounter = 0; - + std::shared_ptr q = std::make_shared(); q->addWorker(); q->addWorker(); @@ -755,7 +765,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_async_operations_parallel_cancel) q->closeAndWait(); int numSuccess = 0; - int numCancelled = 0; + int numCancelled = 0; for (auto & id : ids) { std::shared_ptr ptr = q->popOperationForId(id); if (ptr->isCancelled()) { @@ -764,7 +774,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_async_operations_parallel_cancel) numSuccess++; } } - + BOOST_CHECK_EQUAL(numOperations, numSuccess+numCancelled); BOOST_CHECK_EQUAL(gCounter.load(), numSuccess); BOOST_CHECK(q->getOperationCount() == 0); @@ -790,19 +800,19 @@ BOOST_AUTO_TEST_CASE(rpc_z_getoperations) BOOST_CHECK_NO_THROW(CallRPC("z_getoperationresult [\"opid-1234\"]")); BOOST_CHECK_THROW(CallRPC("z_getoperationresult [] toomanyargs"), runtime_error); BOOST_CHECK_THROW(CallRPC("z_getoperationresult not_an_array"), runtime_error); - + std::shared_ptr op1 = std::make_shared(); q->addOperation(op1); std::shared_ptr op2 = std::make_shared(); q->addOperation(op2); - + BOOST_CHECK(q->getOperationCount() == 2); BOOST_CHECK(q->getNumberOfWorkers() == 0); q->addWorker(); BOOST_CHECK(q->getNumberOfWorkers() == 1); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); BOOST_CHECK(q->getOperationCount() == 0); - + // Check if too many args BOOST_CHECK_THROW(CallRPC("z_listoperationids toomany args"), runtime_error); @@ -817,28 +827,28 @@ BOOST_AUTO_TEST_CASE(rpc_z_getoperations) // idempotent BOOST_CHECK_NO_THROW(retValue = CallRPC("z_getoperationstatus")); array = retValue.get_array(); - BOOST_CHECK(array.size() == 2); - + BOOST_CHECK(array.size() == 2); + for (UniValue v : array.getValues()) { UniValue obj = v.get_obj(); UniValue id = find_value(obj, "id"); - + UniValue result; // removes result from internal storage BOOST_CHECK_NO_THROW(result = CallRPC("z_getoperationresult [\"" + id.get_str() + "\"]")); UniValue resultArray = result.get_array(); BOOST_CHECK(resultArray.size() == 1); - + UniValue resultObj = resultArray[0].get_obj(); UniValue resultId = find_value(resultObj, "id"); BOOST_CHECK_EQUAL(id.get_str(), resultId.get_str()); - - // verify the operation has been removed + + // verify the operation has been removed BOOST_CHECK_NO_THROW(result = CallRPC("z_getoperationresult [\"" + id.get_str() + "\"]")); resultArray = result.get_array(); BOOST_CHECK(resultArray.size() == 0); } - + // operations removed BOOST_CHECK_NO_THROW(retValue = CallRPC("z_getoperationstatus")); array = retValue.get_array(); @@ -905,29 +915,37 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_parameters) std::string zaddr1 = pa.ToString(); BOOST_CHECK_THROW(CallRPC(string("z_sendmany tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ ") + "[{\"address\":\"" + zaddr1 + "\", \"amount\":123.456}]"), runtime_error); - - // Test constructor of AsyncRPCOperation_sendmany + + // Mutable tx containing contextual information we need to build tx + UniValue retValue = CallRPC("getblockcount"); + int nHeight = retValue.get_int(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight + 1); + if (mtx.nVersion == 1) { + mtx.nVersion = 2; + } + + // Test constructor of AsyncRPCOperation_sendmany try { - std::shared_ptr operation(new AsyncRPCOperation_sendmany("",{}, {}, -1)); + std::shared_ptr operation(new AsyncRPCOperation_sendmany(mtx, "",{}, {}, -1)); } catch (const UniValue& objError) { BOOST_CHECK( find_error(objError, "Minconf cannot be negative")); } try { - std::shared_ptr operation(new AsyncRPCOperation_sendmany("",{}, {}, 1)); + std::shared_ptr operation(new AsyncRPCOperation_sendmany(mtx, "",{}, {}, 1)); } catch (const UniValue& objError) { BOOST_CHECK( find_error(objError, "From address parameter missing")); } try { - std::shared_ptr operation( new AsyncRPCOperation_sendmany("tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ", {}, {}, 1) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(mtx, "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ", {}, {}, 1) ); } catch (const UniValue& objError) { BOOST_CHECK( find_error(objError, "No recipients")); } try { std::vector recipients = { SendManyRecipient("dummy",1.0, "") }; - std::shared_ptr operation( new AsyncRPCOperation_sendmany("INVALID", recipients, {}, 1) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(mtx, "INVALID", recipients, {}, 1) ); } catch (const UniValue& objError) { BOOST_CHECK( find_error(objError, "payment address is invalid")); } @@ -935,7 +953,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_parameters) // Testnet payment addresses begin with 'zt'. This test detects an incorrect prefix. try { std::vector recipients = { SendManyRecipient("dummy",1.0, "") }; - std::shared_ptr operation( new AsyncRPCOperation_sendmany("zcMuhvq8sEkHALuSU2i4NbNQxshSAYrpCExec45ZjtivYPbuiFPwk6WHy4SvsbeZ4siy1WheuRGjtaJmoD1J8bFqNXhsG6U", recipients, {}, 1) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(mtx, "zcMuhvq8sEkHALuSU2i4NbNQxshSAYrpCExec45ZjtivYPbuiFPwk6WHy4SvsbeZ4siy1WheuRGjtaJmoD1J8bFqNXhsG6U", recipients, {}, 1) ); } catch (const UniValue& objError) { BOOST_CHECK( find_error(objError, "payment address is for wrong network type")); } @@ -944,7 +962,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_parameters) // invokes a method on pwalletMain, which is undefined in the google test environment. try { std::vector recipients = { SendManyRecipient("dummy",1.0, "") }; - std::shared_ptr operation( new AsyncRPCOperation_sendmany("ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP", recipients, {}, 1) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(mtx, "ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP", recipients, {}, 1) ); } catch (const UniValue& objError) { BOOST_CHECK( find_error(objError, "no spending key found for zaddr")); } @@ -959,27 +977,47 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals) LOCK(pwalletMain->cs_wallet); UniValue retValue; - + + // Mutable tx containing contextual information we need to build tx + retValue = CallRPC("getblockcount"); + int nHeight = retValue.get_int(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight + 1); + if (mtx.nVersion == 1) { + mtx.nVersion = 2; + } + // add keys manually BOOST_CHECK_NO_THROW(retValue = CallRPC("getnewaddress")); std::string taddr1 = retValue.get_str(); CZCPaymentAddress pa = pwalletMain->GenerateNewZKey(); std::string zaddr1 = pa.ToString(); - + // there are no utxos to spend { std::vector recipients = { SendManyRecipient(zaddr1,100.0, "DEADBEEF") }; - std::shared_ptr operation( new AsyncRPCOperation_sendmany(taddr1, {}, recipients, 1) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(mtx, taddr1, {}, recipients, 1) ); operation->main(); BOOST_CHECK(operation->isFailed()); std::string msg = operation->getErrorMessage(); BOOST_CHECK( msg.find("Insufficient funds, no UTXOs found") != string::npos); } - + + // minconf cannot be zero when sending from zaddr + { + try { + std::vector recipients = {SendManyRecipient(taddr1, 100.0, "DEADBEEF")}; + std::shared_ptr operation(new AsyncRPCOperation_sendmany(mtx, zaddr1, recipients, {}, 0)); + BOOST_CHECK(false); // Fail test if an exception is not thrown + } catch (const UniValue& objError) { + BOOST_CHECK(find_error(objError, "Minconf cannot be zero when sending from zaddr")); + } + } + + // there are no unspent notes to spend { std::vector recipients = { SendManyRecipient(taddr1,100.0, "DEADBEEF") }; - std::shared_ptr operation( new AsyncRPCOperation_sendmany(zaddr1, recipients, {}, 1) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(mtx, zaddr1, recipients, {}, 1) ); operation->main(); BOOST_CHECK(operation->isFailed()); std::string msg = operation->getErrorMessage(); @@ -989,10 +1027,10 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals) // get_memo_from_hex_string()) { std::vector recipients = { SendManyRecipient(zaddr1,100.0, "DEADBEEF") }; - std::shared_ptr operation( new AsyncRPCOperation_sendmany(zaddr1, recipients, {}, 1) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(mtx, zaddr1, recipients, {}, 1) ); std::shared_ptr ptr = std::dynamic_pointer_cast (operation); TEST_FRIEND_AsyncRPCOperation_sendmany proxy(ptr); - + std::string memo = "DEADBEEF"; boost::array array = proxy.get_memo_from_hex_string(memo); BOOST_CHECK_EQUAL(array[0], 0xDE); @@ -1002,28 +1040,28 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals) for (int i=4; i v (2 * (ZC_MEMO_SIZE+1)); std::fill(v.begin(),v.end(), 'A'); std::string bigmemo(v.begin(), v.end()); - + try { proxy.get_memo_from_hex_string(bigmemo); } catch (const UniValue& objError) { BOOST_CHECK( find_error(objError, "too big")); } - + // invalid hexadecimal string std::fill(v.begin(),v.end(), '@'); // not a hex character std::string badmemo(v.begin(), v.end()); - + try { proxy.get_memo_from_hex_string(badmemo); } catch (const UniValue& objError) { BOOST_CHECK( find_error(objError, "hexadecimal format")); } - + // odd length hexadecimal string std::fill(v.begin(),v.end(), 'A'); v.resize(v.size() - 1); @@ -1035,33 +1073,31 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals) BOOST_CHECK( find_error(objError, "hexadecimal format")); } } - - + + // add_taddr_change_output_to_tx() will append a vout to a raw transaction { std::vector recipients = { SendManyRecipient(zaddr1,100.0, "DEADBEEF") }; - std::shared_ptr operation( new AsyncRPCOperation_sendmany(zaddr1, recipients, {}, 1) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(mtx, zaddr1, recipients, {}, 1) ); std::shared_ptr ptr = std::dynamic_pointer_cast (operation); TEST_FRIEND_AsyncRPCOperation_sendmany proxy(ptr); - + CTransaction tx = proxy.getTx(); BOOST_CHECK(tx.vout.size() == 0); - - CAmount amount = 123.456; - proxy.add_taddr_change_output_to_tx(0,amount); + CAmount amount = AmountFromValue(ValueFromString("123.456")); + proxy.add_taddr_change_output_to_tx(amount); tx = proxy.getTx(); BOOST_CHECK(tx.vout.size() == 1); CTxOut out = tx.vout[0]; BOOST_CHECK_EQUAL(out.nValue, amount); - - amount = 1.111; - proxy.add_taddr_change_output_to_tx(0,amount); + amount = AmountFromValue(ValueFromString("1.111")); + proxy.add_taddr_change_output_to_tx(amount); tx = proxy.getTx(); BOOST_CHECK(tx.vout.size() == 2); out = tx.vout[1]; BOOST_CHECK_EQUAL(out.nValue, amount); } - + // add_taddr_outputs_to_tx() will append many vouts to a raw transaction { std::vector recipients = { @@ -1069,36 +1105,36 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals) SendManyRecipient("tmUSbHz3vxnwLvRyNDXbwkZxjVyDodMJEhh",CAmount(4.56), ""), SendManyRecipient("tmYZAXYPCP56Xa5JQWWPZuK7o7bfUQW6kkd",CAmount(7.89), ""), }; - std::shared_ptr operation( new AsyncRPCOperation_sendmany(zaddr1, recipients, {}, 1) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(mtx, zaddr1, recipients, {}, 1) ); std::shared_ptr ptr = std::dynamic_pointer_cast (operation); TEST_FRIEND_AsyncRPCOperation_sendmany proxy(ptr); - + proxy.add_taddr_outputs_to_tx(); - + CTransaction tx = proxy.getTx(); BOOST_CHECK(tx.vout.size() == 3); BOOST_CHECK_EQUAL(tx.vout[0].nValue, CAmount(1.23)); BOOST_CHECK_EQUAL(tx.vout[1].nValue, CAmount(4.56)); BOOST_CHECK_EQUAL(tx.vout[2].nValue, CAmount(7.89)); } - + // Raw joinsplit is a zaddr->zaddr { std::string raw = "020000000000000000000100000000000000001027000000000000183a0d4c46c369078705e39bcfebee59a978dbd210ce8de3efc9555a03fbabfd3cea16693d730c63850d7e48ccde79854c19adcb7e9dcd7b7d18805ee09083f6b16e1860729d2d4a90e2f2acd009cf78b5eb0f4a6ee4bdb64b1262d7ce9eb910c460b02022991e968d0c50ee44908e4ccccbc591d0053bcca154dd6d6fc400a29fa686af4682339832ccea362a62aeb9df0d5aa74f86a1e75ac0f48a8ccc41e0a940643c6c33e1d09223b0a46eaf47a1bb4407cfc12b1dcf83a29c0cef51e45c7876ca5b9e5bae86d92976eb3ef68f29cd29386a8be8451b50f82bf9da10c04651868655194da8f6ed3d241bb5b5ff93a3e2bbe44644544d88bcde5cc35978032ee92699c7a61fcbb395e7583f47e698c4d53ede54f956629400bf510fb5e22d03158cc10bdcaaf29e418ef18eb6480dd9c8b9e2a377809f9f32a556ef872febd0021d4ad013aa9f0b7255e98e408d302abefd33a71180b720271835b487ab309e160b06dfe51932120fb84a7ede16b20c53599a11071592109e10260f265ee60d48c62bfe24074020e9b586ce9e9356e68f2ad1a9538258234afe4b83a209f178f45202270eaeaeecaf2ce3100b2c5a714f75f35777a9ebff5ebf47059d2bbf6f3726190216468f2b152673b766225b093f3a2f827c86d6b48b42117fec1d0ac38dd7af700308dcfb02eba821612b16a2c164c47715b9b0c93900893b1aba2ea03765c94d87022db5be06ab338d1912e0936dfe87586d0a8ee49144a6cd2e306abdcb652faa3e0222739deb23154d778b50de75069a4a2cce1208cd1ced3cb4744c9888ce1c2fcd2e66dc31e62d3aa9e423d7275882525e9981f92e84ac85975b8660739407efbe1e34c2249420fde7e17db3096d5b22e83d051d01f0e6e7690dca7d168db338aadf0897fedac10de310db2b1bff762d322935dddbb60c2efb8b15d231fa17b84630371cb275c209f0c4c7d0c68b150ea5cd514122215e3f7fcfb351d69514788d67c2f3c8922581946e3a04bdf1f07f15696ca76eb95b10698bf1188fd882945c57657515889d042a6fc45d38cbc943540c4f0f6d1c45a1574c81f3e42d1eb8702328b729909adee8a5cfed7c79d54627d1fd389af941d878376f7927b9830ca659bf9ab18c5ca5192d52d02723008728d03701b8ab3e1c4a3109409ec0b13df334c7deec3523eeef4c97b5603e643de3a647b873f4c1b47fbfc6586ba66724f112e51fc93839648005043620aa3ce458e246d77977b19c53d98e3e812de006afc1a79744df236582943631d04cc02941ac4be500e4ed9fb9e3e7cc187b1c4050fad1d9d09d5fd70d5d01d615b439d8c0015d2eb10398bcdbf8c4b2bd559dbe4c288a186aed3f86f608da4d582e120c4a896e015e2241900d1daeccd05db968852677c71d752bec46de9962174b46f980e8cc603654daf8b98a3ee92dac066033954164a89568b70b1780c2ce2410b2f816dbeddb2cd463e0c8f21a52cf6427d9647a6fd4bafa8fb4cd4d47ac057b0160bee86c6b2fb8adce214c2bcdda277512200adf0eaa5d2114a2c077b009836a68ec254bfe56f51d147b9afe2ddd9cb917c0c2de19d81b7b8fd9f4574f51fa1207630dc13976f4d7587c962f761af267de71f3909a576e6bedaf6311633910d291ac292c467cc8331ef577aef7646a5d949322fa0367a49f20597a13def53136ee31610395e3e48d291fd8f58504374031fe9dcfba5e06086ebcf01a9106f6a4d6e16e19e4c5bb893f7da79419c94eca31a384be6fa1747284dee0fc3bbc8b1b860172c10b29c1594bb8c747d7fe05827358ff2160f49050001625ffe2e880bd7fc26cd0ffd89750745379a8e862816e08a5a2008043921ab6a4976064ac18f7ee37b6628bc0127d8d5ebd3548e41d8881a082d86f20b32e33094f15a0e6ea6074b08c6cd28142de94713451640a55985051f5577eb54572699d838cb34a79c8939e981c0c277d06a6e2ce69ccb74f8a691ff08f81d8b99e6a86223d29a2b7c8e7b041aba44ea678ae654277f7e91cbfa79158b989164a3d549d9f4feb0cc43169699c13e321fe3f4b94258c75d198ff9184269cd6986c55409e07528c93f64942c6c283ce3917b4bf4c3be2fe3173c8c38cccb35f1fbda0ca88b35a599c0678cb22aa8eabea8249dbd2e4f849fffe69803d299e435ebcd7df95854003d8eda17a74d98b4be0e62d45d7fe48c06a6f464a14f8e0570077cc631279092802a89823f031eef5e1028a6d6fdbd502869a731ee7d28b4d6c71b419462a30d31442d3ee444ffbcbd16d558c9000c97e949c2b1f9d6f6d8db7b9131ebd963620d3fc8595278d6f8fdf49084325373196d53e64142fa5a23eccd6ef908c4d80b8b3e6cc334b7f7012103a3682e4678e9b518163d262a39a2c1a69bf88514c52b7ccd7cc8dc80e71f7c2ec0701cff982573eb0c2c4daeb47fa0b586f4451c10d1da2e5d182b03dd067a5e971b3a6138ca6667aaf853d2ac03b80a1d5870905f2cfb6c78ec3c3719c02f973d638a0f973424a2b0f2b0023f136d60092fe15fba4bc180b9176bd0ff576e053f1af6939fe9ca256203ffaeb3e569f09774d2a6cbf91873e56651f4d6ff77e0b5374b0a1a201d7e523604e0247644544cc571d48c458a4f96f45580b"; UniValue obj(UniValue::VOBJ); obj.push_back(Pair("rawtxn", raw)); - + // we have the spending key for the dummy recipient zaddr1 std::vector recipients = { SendManyRecipient(zaddr1, 0.0005, "ABCD") }; - - std::shared_ptr operation( new AsyncRPCOperation_sendmany(zaddr1, {}, recipients, 1) ); + + std::shared_ptr operation( new AsyncRPCOperation_sendmany(mtx, zaddr1, {}, recipients, 1) ); std::shared_ptr ptr = std::dynamic_pointer_cast (operation); TEST_FRIEND_AsyncRPCOperation_sendmany proxy(ptr); - + // Enable test mode so tx is not sent static_cast(operation.get())->testmode = true; - + // Pretend that the operation completed successfully proxy.set_state(OperationStatus::SUCCESS); @@ -1110,21 +1146,21 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals) std::string hex = find_value(resultObj, "hex").get_str(); BOOST_CHECK_EQUAL(hex, raw); } - - + + // Test the perform_joinsplit methods. { // Dummy input so the operation object can be instantiated. std::vector recipients = { SendManyRecipient(zaddr1, 0.0005, "ABCD") }; - - std::shared_ptr operation( new AsyncRPCOperation_sendmany(zaddr1, {}, recipients, 1) ); + + std::shared_ptr operation( new AsyncRPCOperation_sendmany(mtx, zaddr1, {}, recipients, 1) ); std::shared_ptr ptr = std::dynamic_pointer_cast (operation); - TEST_FRIEND_AsyncRPCOperation_sendmany proxy(ptr); + TEST_FRIEND_AsyncRPCOperation_sendmany proxy(ptr); // Enable test mode so tx is not sent and proofs are not generated static_cast(operation.get())->testmode = true; - - AsyncJoinSplitInfo info; + + AsyncJoinSplitInfo info; std::vector> witnesses; uint256 anchor; try { @@ -1146,7 +1182,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals) } catch (const std::runtime_error & e) { BOOST_CHECK( string(e.what()).find("number of notes")!= string::npos); } - + info.notes.clear(); info.vjsin.push_back(JSInput()); info.vjsin.push_back(JSInput()); @@ -1156,15 +1192,15 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals) } catch (const std::runtime_error & e) { BOOST_CHECK( string(e.what()).find("unsupported joinsplit input")!= string::npos); } - + info.vjsin.clear(); try { proxy.perform_joinsplit(info); } catch (const std::runtime_error & e) { - BOOST_CHECK( string(e.what()).find("JoinSplit verifying key not loaded")!= string::npos); + BOOST_CHECK( string(e.what()).find("error verifying joinsplit")!= string::npos); } } - + } @@ -1202,29 +1238,489 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_encrypted_wallet_zkeys) boost::filesystem::current_path(GetArg("-datadir","/tmp/thisshouldnothappen")); BOOST_CHECK(pwalletMain->EncryptWallet(strWalletPass)); - + // Verify we can still list the keys imported BOOST_CHECK_NO_THROW(retValue = CallRPC("z_listaddresses")); arr = retValue.get_array(); BOOST_CHECK(arr.size() == n); - + // Try to add a new key, but we can't as the wallet is locked BOOST_CHECK_THROW(CallRPC("z_getnewaddress"), runtime_error); - + // We can't call RPC walletpassphrase as that invokes RPCRunLater which breaks tests. // So we manually unlock. BOOST_CHECK(pwalletMain->Unlock(strWalletPass)); - + // Now add a key BOOST_CHECK_NO_THROW(CallRPC("z_getnewaddress")); - + // Verify the key has been added BOOST_CHECK_NO_THROW(retValue = CallRPC("z_listaddresses")); arr = retValue.get_array(); - BOOST_CHECK(arr.size() == n+1); + BOOST_CHECK(arr.size() == n+1); // We can't simulate over RPC the wallet closing and being reloaded // but there are tests for this in gtest. } + + +BOOST_AUTO_TEST_CASE(rpc_z_shieldcoinbase_parameters) +{ + SelectParams(CBaseChainParams::TESTNET); + + LOCK(pwalletMain->cs_wallet); + + BOOST_CHECK_THROW(CallRPC("z_shieldcoinbase"), runtime_error); + BOOST_CHECK_THROW(CallRPC("z_shieldcoinbase toofewargs"), runtime_error); + BOOST_CHECK_THROW(CallRPC("z_shieldcoinbase too many args shown here"), runtime_error); + + // bad from address + BOOST_CHECK_THROW(CallRPC("z_shieldcoinbase " + "INVALIDtmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB"), runtime_error); + + // bad from address + BOOST_CHECK_THROW(CallRPC("z_shieldcoinbase " + "** tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB"), runtime_error); + + // bad to address + BOOST_CHECK_THROW(CallRPC("z_shieldcoinbase " + "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ INVALIDtnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB"), runtime_error); + + // invalid fee amount, cannot be negative + BOOST_CHECK_THROW(CallRPC("z_shieldcoinbase " + "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ " + "tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB " + "-0.0001" + ), runtime_error); + + // invalid fee amount, bigger than MAX_MONEY + BOOST_CHECK_THROW(CallRPC("z_shieldcoinbase " + "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ " + "tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB " + "21000001" + ), runtime_error); + + // invalid limit, must be at least 0 + BOOST_CHECK_THROW(CallRPC("z_shieldcoinbase " + "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ " + "tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB " + "100 -1" + ), runtime_error); + + // Mutable tx containing contextual information we need to build tx + UniValue retValue = CallRPC("getblockcount"); + int nHeight = retValue.get_int(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight + 1); + if (mtx.nVersion == 1) { + mtx.nVersion = 2; + } + + // Test constructor of AsyncRPCOperation_sendmany + std::string testnetzaddr = "ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP"; + std::string mainnetzaddr = "zcMuhvq8sEkHALuSU2i4NbNQxshSAYrpCExec45ZjtivYPbuiFPwk6WHy4SvsbeZ4siy1WheuRGjtaJmoD1J8bFqNXhsG6U"; + + try { + std::shared_ptr operation(new AsyncRPCOperation_shieldcoinbase(mtx, {}, testnetzaddr, -1 )); + } catch (const UniValue& objError) { + BOOST_CHECK( find_error(objError, "Fee is out of range")); + } + + try { + std::shared_ptr operation(new AsyncRPCOperation_shieldcoinbase(mtx, {}, testnetzaddr, 1)); + } catch (const UniValue& objError) { + BOOST_CHECK( find_error(objError, "Empty inputs")); + } + + // Testnet payment addresses begin with 'zt'. This test detects an incorrect prefix. + try { + std::vector inputs = { ShieldCoinbaseUTXO{uint256(),0,0} }; + std::shared_ptr operation( new AsyncRPCOperation_shieldcoinbase(mtx, inputs, mainnetzaddr, 1) ); + } catch (const UniValue& objError) { + BOOST_CHECK( find_error(objError, "payment address is for wrong network type")); + } + +} + + + +BOOST_AUTO_TEST_CASE(rpc_z_shieldcoinbase_internals) +{ + SelectParams(CBaseChainParams::TESTNET); + + LOCK(pwalletMain->cs_wallet); + + // Mutable tx containing contextual information we need to build tx + UniValue retValue = CallRPC("getblockcount"); + int nHeight = retValue.get_int(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight + 1); + if (mtx.nVersion == 1) { + mtx.nVersion = 2; + } + + // Test that option -mempooltxinputlimit is respected. + mapArgs["-mempooltxinputlimit"] = "1"; + + // Add keys manually + CZCPaymentAddress pa = pwalletMain->GenerateNewZKey(); + std::string zaddr = pa.ToString(); + + // Supply 2 inputs when mempool limit is 1 + { + std::vector inputs = { ShieldCoinbaseUTXO{uint256(),0,0}, ShieldCoinbaseUTXO{uint256(),0,0} }; + std::shared_ptr operation( new AsyncRPCOperation_shieldcoinbase(mtx, inputs, zaddr) ); + operation->main(); + BOOST_CHECK(operation->isFailed()); + std::string msg = operation->getErrorMessage(); + BOOST_CHECK( msg.find("Number of inputs 2 is greater than mempooltxinputlimit of 1") != string::npos); + } + + // Insufficient funds + { + std::vector inputs = { ShieldCoinbaseUTXO{uint256(),0,0} }; + std::shared_ptr operation( new AsyncRPCOperation_shieldcoinbase(mtx, inputs, zaddr) ); + operation->main(); + BOOST_CHECK(operation->isFailed()); + std::string msg = operation->getErrorMessage(); + BOOST_CHECK( msg.find("Insufficient coinbase funds") != string::npos); + } + + // Test the perform_joinsplit methods. + { + // Dummy input so the operation object can be instantiated. + std::vector inputs = { ShieldCoinbaseUTXO{uint256(),0,100000} }; + std::shared_ptr operation( new AsyncRPCOperation_shieldcoinbase(mtx, inputs, zaddr) ); + std::shared_ptr ptr = std::dynamic_pointer_cast (operation); + TEST_FRIEND_AsyncRPCOperation_shieldcoinbase proxy(ptr); + static_cast(operation.get())->testmode = true; + + ShieldCoinbaseJSInfo info; + info.vjsin.push_back(JSInput()); + info.vjsin.push_back(JSInput()); + info.vjsin.push_back(JSInput()); + try { + proxy.perform_joinsplit(info); + } catch (const std::runtime_error & e) { + BOOST_CHECK( string(e.what()).find("unsupported joinsplit input")!= string::npos); + } + + info.vjsin.clear(); + try { + proxy.perform_joinsplit(info); + } catch (const std::runtime_error & e) { + BOOST_CHECK( string(e.what()).find("error verifying joinsplit")!= string::npos); + } + } + +} + + +BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_parameters) +{ + SelectParams(CBaseChainParams::TESTNET); + + LOCK(pwalletMain->cs_wallet); + + BOOST_CHECK_THROW(CallRPC("z_mergetoaddress"), runtime_error); + BOOST_CHECK_THROW(CallRPC("z_mergetoaddress toofewargs"), runtime_error); + BOOST_CHECK_THROW(CallRPC("z_mergetoaddress just too many args present for this method"), runtime_error); + + // bad from address + BOOST_CHECK_THROW(CallRPC("z_mergetoaddress " + "[\"INVALIDtmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ\"] tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB"), runtime_error); + + // bad from address + BOOST_CHECK_THROW(CallRPC("z_mergetoaddress " + "** tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB"), runtime_error); + + // bad from address + BOOST_CHECK_THROW(CallRPC("z_mergetoaddress " + "[\"**\"] tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB"), runtime_error); + + // bad from address + BOOST_CHECK_THROW(CallRPC("z_mergetoaddress " + "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB"), runtime_error); + + // bad from address + BOOST_CHECK_THROW(CallRPC("z_mergetoaddress " + "[tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ] tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB"), runtime_error); + + // bad to address + BOOST_CHECK_THROW(CallRPC("z_mergetoaddress " + "[\"tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ\"] INVALIDtnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB"), runtime_error); + + // duplicate address + BOOST_CHECK_THROW(CallRPC("z_mergetoaddress " + "[\"tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ\", \"tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ\"] " + "tmQP9L3s31cLsghVYf2Jb5MhKj1jRBPoeQn" + ), runtime_error); + + // invalid fee amount, cannot be negative + BOOST_CHECK_THROW(CallRPC("z_mergetoaddress " + "[\"tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ\"] " + "tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB " + "-0.0001" + ), runtime_error); + + // invalid fee amount, bigger than MAX_MONEY + BOOST_CHECK_THROW(CallRPC("z_mergetoaddress " + "[\"tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ\"] " + "tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB " + "21000001" + ), runtime_error); + + // invalid transparent limit, must be at least 0 + BOOST_CHECK_THROW(CallRPC("z_mergetoaddress " + "[\"tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ\"] " + "tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB " + "0.0001 -1" + ), runtime_error); + + // invalid shielded limit, must be at least 0 + BOOST_CHECK_THROW(CallRPC("z_mergetoaddress " + "[\"tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ\"] " + "tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB " + "0.0001 100 -1" + ), runtime_error); + + // memo bigger than allowed length of ZC_MEMO_SIZE + 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()); + CZCPaymentAddress pa = pwalletMain->GenerateNewZKey(); + std::string zaddr1 = pa.ToString(); + BOOST_CHECK_THROW(CallRPC(string("z_mergetoaddress [\"tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ\"] ") + + zaddr1 + " 0.0001 100 100 " + badmemo), runtime_error); + + // Mutable tx containing contextual information we need to build tx + UniValue retValue = CallRPC("getblockcount"); + int nHeight = retValue.get_int(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight + 1); + + // Test constructor of AsyncRPCOperation_mergetoaddress + MergeToAddressRecipient testnetzaddr( + "ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP", + "testnet memo"); + MergeToAddressRecipient mainnetzaddr( + "zcMuhvq8sEkHALuSU2i4NbNQxshSAYrpCExec45ZjtivYPbuiFPwk6WHy4SvsbeZ4siy1WheuRGjtaJmoD1J8bFqNXhsG6U", + "mainnet memo"); + + try { + std::shared_ptr operation(new AsyncRPCOperation_mergetoaddress(mtx, {}, {}, testnetzaddr, -1 )); + BOOST_FAIL("Should have caused an error"); + } catch (const UniValue& objError) { + BOOST_CHECK( find_error(objError, "Fee is out of range")); + } + + try { + std::shared_ptr operation(new AsyncRPCOperation_mergetoaddress(mtx, {}, {}, testnetzaddr, 1)); + BOOST_FAIL("Should have caused an error"); + } catch (const UniValue& objError) { + BOOST_CHECK( find_error(objError, "No inputs")); + } + + std::vector inputs = { MergeToAddressInputUTXO{ COutPoint{uint256(), 0}, 0} }; + + try { + MergeToAddressRecipient badaddr("", "memo"); + std::shared_ptr operation(new AsyncRPCOperation_mergetoaddress(mtx, inputs, {}, badaddr, 1)); + BOOST_FAIL("Should have caused an error"); + } catch (const UniValue& objError) { + BOOST_CHECK( find_error(objError, "Recipient parameter missing")); + } + + // Testnet payment addresses begin with 'zt'. This test detects an incorrect prefix. + try { + std::vector inputs = { MergeToAddressInputUTXO{ COutPoint{uint256(), 0}, 0} }; + std::shared_ptr operation( new AsyncRPCOperation_mergetoaddress(mtx, inputs, {}, mainnetzaddr, 1) ); + BOOST_FAIL("Should have caused an error"); + } catch (const UniValue& objError) { + BOOST_CHECK( find_error(objError, "payment address is for wrong network type")); + } +} + + +// TODO: test private methods +BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_internals) +{ + SelectParams(CBaseChainParams::TESTNET); + + LOCK(pwalletMain->cs_wallet); + + // Mutable tx containing contextual information we need to build tx + UniValue retValue = CallRPC("getblockcount"); + int nHeight = retValue.get_int(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight + 1); + + // Test that option -mempooltxinputlimit is respected. + mapArgs["-mempooltxinputlimit"] = "1"; + + // Add keys manually + BOOST_CHECK_NO_THROW(retValue = CallRPC("getnewaddress")); + MergeToAddressRecipient taddr1(retValue.get_str(), ""); + CZCPaymentAddress pa = pwalletMain->GenerateNewZKey(); + MergeToAddressRecipient zaddr1(pa.ToString(), "DEADBEEF"); + + // Supply 2 inputs when mempool limit is 1 + { + std::vector inputs = { + MergeToAddressInputUTXO{COutPoint{uint256(),0},0}, + MergeToAddressInputUTXO{COutPoint{uint256(),0},0} + }; + std::shared_ptr operation( new AsyncRPCOperation_mergetoaddress(mtx, inputs, {}, zaddr1) ); + operation->main(); + BOOST_CHECK(operation->isFailed()); + std::string msg = operation->getErrorMessage(); + BOOST_CHECK( msg.find("Number of transparent inputs 2 is greater than mempooltxinputlimit of 1") != string::npos); + } + + // Insufficient funds + { + std::vector inputs = { MergeToAddressInputUTXO{COutPoint{uint256(),0},0} }; + std::shared_ptr operation( new AsyncRPCOperation_mergetoaddress(mtx, inputs, {}, zaddr1) ); + 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); + } + + // get_memo_from_hex_string()) + { + std::vector inputs = { MergeToAddressInputUTXO{COutPoint{uint256(),0},100000} }; + std::shared_ptr operation( new AsyncRPCOperation_mergetoaddress(mtx, inputs, {}, zaddr1) ); + std::shared_ptr ptr = std::dynamic_pointer_cast (operation); + TEST_FRIEND_AsyncRPCOperation_mergetoaddress proxy(ptr); + + std::string memo = "DEADBEEF"; + boost::array array = proxy.get_memo_from_hex_string(memo); + BOOST_CHECK_EQUAL(array[0], 0xDE); + BOOST_CHECK_EQUAL(array[1], 0xAD); + BOOST_CHECK_EQUAL(array[2], 0xBE); + BOOST_CHECK_EQUAL(array[3], 0xEF); + for (int i=4; i v (2 * (ZC_MEMO_SIZE+1)); + std::fill(v.begin(),v.end(), 'A'); + std::string bigmemo(v.begin(), v.end()); + + try { + proxy.get_memo_from_hex_string(bigmemo); + BOOST_FAIL("Should have caused an error"); + } catch (const UniValue& objError) { + BOOST_CHECK( find_error(objError, "too big")); + } + + // invalid hexadecimal string + std::fill(v.begin(),v.end(), '@'); // not a hex character + std::string badmemo(v.begin(), v.end()); + + try { + proxy.get_memo_from_hex_string(badmemo); + BOOST_FAIL("Should have caused an error"); + } catch (const UniValue& objError) { + BOOST_CHECK( find_error(objError, "hexadecimal format")); + } + + // odd length hexadecimal string + std::fill(v.begin(),v.end(), 'A'); + v.resize(v.size() - 1); + assert(v.size() %2 == 1); // odd length + std::string oddmemo(v.begin(), v.end()); + try { + proxy.get_memo_from_hex_string(oddmemo); + BOOST_FAIL("Should have caused an error"); + } catch (const UniValue& objError) { + BOOST_CHECK( find_error(objError, "hexadecimal format")); + } + } + + // Test the perform_joinsplit methods. + { + // Dummy input so the operation object can be instantiated. + std::vector inputs = { MergeToAddressInputUTXO{COutPoint{uint256(),0},100000} }; + std::shared_ptr operation( new AsyncRPCOperation_mergetoaddress(mtx, inputs, {}, zaddr1) ); + std::shared_ptr ptr = std::dynamic_pointer_cast (operation); + TEST_FRIEND_AsyncRPCOperation_mergetoaddress proxy(ptr); + + // Enable test mode so tx is not sent and proofs are not generated + static_cast(operation.get())->testmode = true; + + MergeToAddressJSInfo info; + std::vector> witnesses; + uint256 anchor; + try { + proxy.perform_joinsplit(info, witnesses, anchor); + BOOST_FAIL("Should have caused an error"); + } catch (const std::runtime_error & e) { + BOOST_CHECK( string(e.what()).find("anchor is null")!= string::npos); + } + + try { + std::vector v; + proxy.perform_joinsplit(info, v); + BOOST_FAIL("Should have caused an error"); + } catch (const std::runtime_error & e) { + BOOST_CHECK( string(e.what()).find("anchor is null")!= string::npos); + } + + info.notes.push_back(Note()); + try { + proxy.perform_joinsplit(info); + BOOST_FAIL("Should have caused an error"); + } catch (const std::runtime_error & e) { + BOOST_CHECK( string(e.what()).find("number of notes")!= string::npos); + } + + info.notes.clear(); + info.vjsin.push_back(JSInput()); + info.vjsin.push_back(JSInput()); + info.vjsin.push_back(JSInput()); + try { + proxy.perform_joinsplit(info); + BOOST_FAIL("Should have caused an error"); + } catch (const std::runtime_error & e) { + BOOST_CHECK( string(e.what()).find("unsupported joinsplit input")!= string::npos); + } + + info.vjsin.clear(); + try { + proxy.perform_joinsplit(info); + BOOST_FAIL("Should have caused an error"); + } catch (const std::runtime_error & e) { + BOOST_CHECK( string(e.what()).find("error verifying joinsplit")!= string::npos); + } + } + + // Raw joinsplit is a zaddr->zaddr + { + std::string raw = "020000000000000000000100000000000000001027000000000000183a0d4c46c369078705e39bcfebee59a978dbd210ce8de3efc9555a03fbabfd3cea16693d730c63850d7e48ccde79854c19adcb7e9dcd7b7d18805ee09083f6b16e1860729d2d4a90e2f2acd009cf78b5eb0f4a6ee4bdb64b1262d7ce9eb910c460b02022991e968d0c50ee44908e4ccccbc591d0053bcca154dd6d6fc400a29fa686af4682339832ccea362a62aeb9df0d5aa74f86a1e75ac0f48a8ccc41e0a940643c6c33e1d09223b0a46eaf47a1bb4407cfc12b1dcf83a29c0cef51e45c7876ca5b9e5bae86d92976eb3ef68f29cd29386a8be8451b50f82bf9da10c04651868655194da8f6ed3d241bb5b5ff93a3e2bbe44644544d88bcde5cc35978032ee92699c7a61fcbb395e7583f47e698c4d53ede54f956629400bf510fb5e22d03158cc10bdcaaf29e418ef18eb6480dd9c8b9e2a377809f9f32a556ef872febd0021d4ad013aa9f0b7255e98e408d302abefd33a71180b720271835b487ab309e160b06dfe51932120fb84a7ede16b20c53599a11071592109e10260f265ee60d48c62bfe24074020e9b586ce9e9356e68f2ad1a9538258234afe4b83a209f178f45202270eaeaeecaf2ce3100b2c5a714f75f35777a9ebff5ebf47059d2bbf6f3726190216468f2b152673b766225b093f3a2f827c86d6b48b42117fec1d0ac38dd7af700308dcfb02eba821612b16a2c164c47715b9b0c93900893b1aba2ea03765c94d87022db5be06ab338d1912e0936dfe87586d0a8ee49144a6cd2e306abdcb652faa3e0222739deb23154d778b50de75069a4a2cce1208cd1ced3cb4744c9888ce1c2fcd2e66dc31e62d3aa9e423d7275882525e9981f92e84ac85975b8660739407efbe1e34c2249420fde7e17db3096d5b22e83d051d01f0e6e7690dca7d168db338aadf0897fedac10de310db2b1bff762d322935dddbb60c2efb8b15d231fa17b84630371cb275c209f0c4c7d0c68b150ea5cd514122215e3f7fcfb351d69514788d67c2f3c8922581946e3a04bdf1f07f15696ca76eb95b10698bf1188fd882945c57657515889d042a6fc45d38cbc943540c4f0f6d1c45a1574c81f3e42d1eb8702328b729909adee8a5cfed7c79d54627d1fd389af941d878376f7927b9830ca659bf9ab18c5ca5192d52d02723008728d03701b8ab3e1c4a3109409ec0b13df334c7deec3523eeef4c97b5603e643de3a647b873f4c1b47fbfc6586ba66724f112e51fc93839648005043620aa3ce458e246d77977b19c53d98e3e812de006afc1a79744df236582943631d04cc02941ac4be500e4ed9fb9e3e7cc187b1c4050fad1d9d09d5fd70d5d01d615b439d8c0015d2eb10398bcdbf8c4b2bd559dbe4c288a186aed3f86f608da4d582e120c4a896e015e2241900d1daeccd05db968852677c71d752bec46de9962174b46f980e8cc603654daf8b98a3ee92dac066033954164a89568b70b1780c2ce2410b2f816dbeddb2cd463e0c8f21a52cf6427d9647a6fd4bafa8fb4cd4d47ac057b0160bee86c6b2fb8adce214c2bcdda277512200adf0eaa5d2114a2c077b009836a68ec254bfe56f51d147b9afe2ddd9cb917c0c2de19d81b7b8fd9f4574f51fa1207630dc13976f4d7587c962f761af267de71f3909a576e6bedaf6311633910d291ac292c467cc8331ef577aef7646a5d949322fa0367a49f20597a13def53136ee31610395e3e48d291fd8f58504374031fe9dcfba5e06086ebcf01a9106f6a4d6e16e19e4c5bb893f7da79419c94eca31a384be6fa1747284dee0fc3bbc8b1b860172c10b29c1594bb8c747d7fe05827358ff2160f49050001625ffe2e880bd7fc26cd0ffd89750745379a8e862816e08a5a2008043921ab6a4976064ac18f7ee37b6628bc0127d8d5ebd3548e41d8881a082d86f20b32e33094f15a0e6ea6074b08c6cd28142de94713451640a55985051f5577eb54572699d838cb34a79c8939e981c0c277d06a6e2ce69ccb74f8a691ff08f81d8b99e6a86223d29a2b7c8e7b041aba44ea678ae654277f7e91cbfa79158b989164a3d549d9f4feb0cc43169699c13e321fe3f4b94258c75d198ff9184269cd6986c55409e07528c93f64942c6c283ce3917b4bf4c3be2fe3173c8c38cccb35f1fbda0ca88b35a599c0678cb22aa8eabea8249dbd2e4f849fffe69803d299e435ebcd7df95854003d8eda17a74d98b4be0e62d45d7fe48c06a6f464a14f8e0570077cc631279092802a89823f031eef5e1028a6d6fdbd502869a731ee7d28b4d6c71b419462a30d31442d3ee444ffbcbd16d558c9000c97e949c2b1f9d6f6d8db7b9131ebd963620d3fc8595278d6f8fdf49084325373196d53e64142fa5a23eccd6ef908c4d80b8b3e6cc334b7f7012103a3682e4678e9b518163d262a39a2c1a69bf88514c52b7ccd7cc8dc80e71f7c2ec0701cff982573eb0c2c4daeb47fa0b586f4451c10d1da2e5d182b03dd067a5e971b3a6138ca6667aaf853d2ac03b80a1d5870905f2cfb6c78ec3c3719c02f973d638a0f973424a2b0f2b0023f136d60092fe15fba4bc180b9176bd0ff576e053f1af6939fe9ca256203ffaeb3e569f09774d2a6cbf91873e56651f4d6ff77e0b5374b0a1a201d7e523604e0247644544cc571d48c458a4f96f45580b"; + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("rawtxn", raw)); + + // we have the spending key for the dummy recipient zaddr1 + std::vector inputs = { MergeToAddressInputUTXO{COutPoint{uint256(),0},100000} }; + std::shared_ptr operation( new AsyncRPCOperation_mergetoaddress(mtx, inputs, {}, zaddr1) ); + std::shared_ptr ptr = std::dynamic_pointer_cast (operation); + TEST_FRIEND_AsyncRPCOperation_mergetoaddress proxy(ptr); + + // Enable test mode so tx is not sent + static_cast(operation.get())->testmode = true; + + // Pretend that the operation completed successfully + proxy.set_state(OperationStatus::SUCCESS); + + // Verify test mode is returning output (since no input taddrs, signed and unsigned are the same). + BOOST_CHECK_NO_THROW( proxy.sign_send_raw_transaction(obj) ); + UniValue result = operation->getResult(); + BOOST_CHECK(!result.isNull()); + UniValue resultObj = result.get_obj(); + std::string hex = find_value(resultObj, "hex").get_str(); + BOOST_CHECK_EQUAL(hex, raw); + } +} + + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp index cb1a427db..d6c93ef3b 100644 --- a/src/test/scheduler_tests.cpp +++ b/src/test/scheduler_tests.cpp @@ -30,14 +30,7 @@ static void microTask(CScheduler& s, boost::mutex& mutex, int& counter, int delt static void MicroSleep(uint64_t n) { -#if defined(HAVE_WORKING_BOOST_SLEEP_FOR) boost::this_thread::sleep_for(boost::chrono::microseconds(n)); -#elif defined(HAVE_WORKING_BOOST_SLEEP) - boost::this_thread::sleep(boost::posix_time::microseconds(n)); -#else - //should never get here - #error missing boost sleep implementation -#endif } BOOST_AUTO_TEST_CASE(manythreads) diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index c8cfe2872..209ff06c9 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -2,6 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "consensus/upgrades.h" +#include "core_io.h" #include "key.h" #include "keystore.h" #include "main.h" @@ -31,6 +33,8 @@ Serialize(const CScript& s) static bool Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, ScriptError& err) { + uint32_t consensusBranchId = SPROUT_BRANCH_ID; + // Create dummy to/from transactions: CMutableTransaction txFrom; txFrom.vout.resize(1); @@ -44,7 +48,7 @@ Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, Scri txTo.vin[0].scriptSig = scriptSig; txTo.vout[0].nValue = 1; - return VerifyScript(scriptSig, scriptPubKey, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, MutableTransactionSignatureChecker(&txTo, 0), &err); + return VerifyScript(scriptSig, scriptPubKey, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, MutableTransactionSignatureChecker(&txTo, 0, txFrom.vout[0].nValue), consensusBranchId, &err); } @@ -53,6 +57,7 @@ BOOST_FIXTURE_TEST_SUITE(script_P2SH_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(sign) { LOCK(cs_main); + uint32_t consensusBranchId = SPROUT_BRANCH_ID; // Pay-to-script-hash looks like this: // scriptSig: // scriptPubKey: HASH160 EQUAL @@ -106,22 +111,24 @@ BOOST_AUTO_TEST_CASE(sign) } for (int i = 0; i < 8; i++) { - BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i)); + BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0, SIGHASH_ALL, consensusBranchId), strprintf("SignSignature %d", i)); } // All of the above should be OK, and the txTos have valid signatures // Check to make sure signature verification fails if we use the wrong ScriptSig: - for (int i = 0; i < 8; i++) + for (int i = 0; i < 8; i++) { + PrecomputedTransactionData txdata(txTo[i]); for (int j = 0; j < 8; j++) { CScript sigSave = txTo[i].vin[0].scriptSig; txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig; - bool sigOK = CScriptCheck(CCoins(txFrom, 0), txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false)(); + bool sigOK = CScriptCheck(CCoins(txFrom, 0), txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, consensusBranchId, &txdata)(); if (i == j) BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j)); else BOOST_CHECK_MESSAGE(!sigOK, strprintf("VerifySignature %d %d", i, j)); txTo[i].vin[0].scriptSig = sigSave; } + } } BOOST_AUTO_TEST_CASE(norecurse) @@ -154,6 +161,7 @@ BOOST_AUTO_TEST_CASE(norecurse) BOOST_AUTO_TEST_CASE(set) { LOCK(cs_main); + uint32_t consensusBranchId = SPROUT_BRANCH_ID; // Test the CScript::Set* methods CBasicKeyStore keystore; CKey key[4]; @@ -203,7 +211,7 @@ BOOST_AUTO_TEST_CASE(set) } for (int i = 0; i < 4; i++) { - BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i)); + BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0, SIGHASH_ALL, consensusBranchId), strprintf("SignSignature %d", i)); BOOST_CHECK_MESSAGE(IsStandardTx(txTo[i], reason), strprintf("txTo[%d].IsStandard", i)); } } @@ -262,6 +270,7 @@ BOOST_AUTO_TEST_CASE(switchover) BOOST_AUTO_TEST_CASE(AreInputsStandard) { LOCK(cs_main); + uint32_t consensusBranchId = SPROUT_BRANCH_ID; CCoinsView coinsDummy; CCoinsViewCache coins(&coinsDummy); CBasicKeyStore keystore; @@ -332,16 +341,16 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txTo.vin[i].prevout.n = i; txTo.vin[i].prevout.hash = txFrom.GetHash(); } - BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 0)); - BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 1)); - BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 2)); + BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL, consensusBranchId)); + BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 1, SIGHASH_ALL, consensusBranchId)); + BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 2, SIGHASH_ALL, consensusBranchId)); // SignSignature doesn't know how to sign these. We're // not testing validating signatures, so just create // dummy signatures that DO include the correct P2SH scripts: txTo.vin[3].scriptSig << OP_11 << OP_11 << static_cast >(oneAndTwo); txTo.vin[4].scriptSig << static_cast >(fifteenSigops); - BOOST_CHECK(::AreInputsStandard(txTo, coins)); + BOOST_CHECK(::AreInputsStandard(txTo, coins, consensusBranchId)); // 22 P2SH sigops for all inputs (1 for vin[0], 6 for vin[3], 15 for vin[4] BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txTo, coins), 22U); @@ -350,7 +359,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) { CScript t = txTo.vin[i].scriptSig; txTo.vin[i].scriptSig = (CScript() << 11) + t; - BOOST_CHECK(!::AreInputsStandard(txTo, coins)); + BOOST_CHECK(!::AreInputsStandard(txTo, coins, consensusBranchId)); txTo.vin[i].scriptSig = t; } @@ -363,7 +372,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txToNonStd1.vin[0].prevout.hash = txFrom.GetHash(); txToNonStd1.vin[0].scriptSig << static_cast >(sixteenSigops); - BOOST_CHECK(!::AreInputsStandard(txToNonStd1, coins)); + BOOST_CHECK(!::AreInputsStandard(txToNonStd1, coins, consensusBranchId)); BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txToNonStd1, coins), 16U); CMutableTransaction txToNonStd2; @@ -375,7 +384,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txToNonStd2.vin[0].prevout.hash = txFrom.GetHash(); txToNonStd2.vin[0].scriptSig << static_cast >(twentySigops); - BOOST_CHECK(!::AreInputsStandard(txToNonStd2, coins)); + BOOST_CHECK(!::AreInputsStandard(txToNonStd2, coins, consensusBranchId)); BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txToNonStd2, coins), 20U); } diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index a1e3dbdc2..bb363be41 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -5,6 +5,7 @@ #include "data/script_invalid.json.h" #include "data/script_valid.json.h" +#include "consensus/upgrades.h" #include "core_io.h" #include "key.h" #include "keystore.h" @@ -87,12 +88,13 @@ CMutableTransaction BuildSpendingTransaction(const CScript& scriptSig, const CMu return txSpend; } -void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, int flags, bool expect, const std::string& message) +void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, int flags, uint32_t consensusBranchId, bool expect, const std::string& message) { ScriptError err; - CMutableTransaction tx = BuildSpendingTransaction(scriptSig, BuildCreditingTransaction(scriptPubKey)); + CMutableTransaction txCredit = BuildCreditingTransaction(scriptPubKey); + CMutableTransaction tx = BuildSpendingTransaction(scriptSig, txCredit); CMutableTransaction tx2 = tx; - BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, flags, MutableTransactionSignatureChecker(&tx, 0), &err) == expect, message); + 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); @@ -187,6 +189,7 @@ private: std::vector push; std::string comment; int flags; + uint32_t consensusBranchId; void DoPush() { @@ -204,7 +207,7 @@ private: } public: - TestBuilder(const CScript& redeemScript, const std::string& comment_, int flags_, bool P2SH = false) : scriptPubKey(redeemScript), havePush(false), comment(comment_), flags(flags_) + TestBuilder(const CScript& redeemScript, const std::string& comment_, int flags_, bool P2SH = false) : scriptPubKey(redeemScript), havePush(false), comment(comment_), flags(flags_), consensusBranchId(0) { if (P2SH) { creditTx = BuildCreditingTransaction(CScript() << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL); @@ -236,7 +239,7 @@ public: TestBuilder& PushSig(const CKey& key, int nHashType = SIGHASH_ALL, unsigned int lenR = 32, unsigned int lenS = 32) { - uint256 hash = SignatureHash(scriptPubKey, spendTx, 0, nHashType); + uint256 hash = SignatureHash(scriptPubKey, spendTx, 0, nHashType, 0, consensusBranchId); std::vector vchSig, r, s; uint32_t iter = 0; do { @@ -288,7 +291,7 @@ public: { TestBuilder copy = *this; // Make a copy so we can rollback the push. DoPush(); - DoTest(creditTx.vout[0].scriptPubKey, spendTx.vin[0].scriptSig, flags, expect, comment); + DoTest(creditTx.vout[0].scriptPubKey, spendTx.vin[0].scriptSig, flags, consensusBranchId, expect, comment); *this = copy; return *this; } @@ -372,114 +375,61 @@ BOOST_AUTO_TEST_CASE(script_build) "P2SH(2-of-3), 1 sig", SCRIPT_VERIFY_P2SH, true ).Num(0).PushSig(keys.key1).Num(0).PushRedeem()); - good.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG, - "P2PK with too much R padding but no DERSIG", 0 - ).PushSig(keys.key1, SIGHASH_ALL, 31, 32).EditPush(1, "43021F", "44022000")); bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG, - "P2PK with too much R padding", SCRIPT_VERIFY_DERSIG + "P2PK with too much R padding", 0 ).PushSig(keys.key1, SIGHASH_ALL, 31, 32).EditPush(1, "43021F", "44022000")); - good.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG, - "P2PK with too much S padding but no DERSIG", 0 - ).PushSig(keys.key1, SIGHASH_ALL).EditPush(1, "44", "45").EditPush(37, "20", "2100")); bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG, - "P2PK with too much S padding", SCRIPT_VERIFY_DERSIG + "P2PK with too much S padding", 0 ).PushSig(keys.key1, SIGHASH_ALL).EditPush(1, "44", "45").EditPush(37, "20", "2100")); - good.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG, - "P2PK with too little R padding but no DERSIG", 0 - ).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220")); bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG, - "P2PK with too little R padding", SCRIPT_VERIFY_DERSIG + "P2PK with too little R padding", 0 ).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220")); - good.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey2C) << OP_CHECKSIG << OP_NOT, - "P2PK NOT with bad sig with too much R padding but no DERSIG", 0 - ).PushSig(keys.key2, SIGHASH_ALL, 31, 32).EditPush(1, "43021F", "44022000").DamagePush(10)); bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey2C) << OP_CHECKSIG << OP_NOT, - "P2PK NOT with bad sig with too much R padding", SCRIPT_VERIFY_DERSIG + "P2PK NOT with bad sig with too much R padding", 0 ).PushSig(keys.key2, SIGHASH_ALL, 31, 32).EditPush(1, "43021F", "44022000").DamagePush(10)); bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey2C) << OP_CHECKSIG << OP_NOT, - "P2PK NOT with too much R padding but no DERSIG", 0 - ).PushSig(keys.key2, SIGHASH_ALL, 31, 32).EditPush(1, "43021F", "44022000")); - bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey2C) << OP_CHECKSIG << OP_NOT, - "P2PK NOT with too much R padding", SCRIPT_VERIFY_DERSIG + "P2PK NOT with too much R padding", 0 ).PushSig(keys.key2, SIGHASH_ALL, 31, 32).EditPush(1, "43021F", "44022000")); - good.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG, - "BIP66 example 1, without DERSIG", 0 - ).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220")); bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG, - "BIP66 example 1, with DERSIG", SCRIPT_VERIFY_DERSIG - ).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220")); - bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG << OP_NOT, - "BIP66 example 2, without DERSIG", 0 + "BIP66 example 1", 0 ).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220")); bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG << OP_NOT, - "BIP66 example 2, with DERSIG", SCRIPT_VERIFY_DERSIG + "BIP66 example 2", 0 ).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220")); bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG, - "BIP66 example 3, without DERSIG", 0 + "BIP66 example 3", 0 ).Num(0)); - bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG, - "BIP66 example 3, with DERSIG", SCRIPT_VERIFY_DERSIG - ).Num(0)); - good.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG << OP_NOT, - "BIP66 example 4, without DERSIG", 0 - ).Num(0)); good.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG << OP_NOT, - "BIP66 example 4, with DERSIG", SCRIPT_VERIFY_DERSIG + "BIP66 example 4", 0 ).Num(0)); bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG, - "BIP66 example 5, without DERSIG", 0 - ).Num(1)); - bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG, - "BIP66 example 5, with DERSIG", SCRIPT_VERIFY_DERSIG + "BIP66 example 5", 0 ).Num(1)); - good.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG << OP_NOT, - "BIP66 example 6, without DERSIG", 0 - ).Num(1)); bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey1C) << OP_CHECKSIG << OP_NOT, - "BIP66 example 6, with DERSIG", SCRIPT_VERIFY_DERSIG + "BIP66 example 6", 0 ).Num(1)); - good.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG, - "BIP66 example 7, without DERSIG", 0 - ).Num(0).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220").PushSig(keys.key2)); bad.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG, - "BIP66 example 7, with DERSIG", SCRIPT_VERIFY_DERSIG + "BIP66 example 7", 0 ).Num(0).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220").PushSig(keys.key2)); bad.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG << OP_NOT, - "BIP66 example 8, without DERSIG", 0 + "BIP66 example 8", 0 ).Num(0).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220").PushSig(keys.key2)); - bad.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG << OP_NOT, - "BIP66 example 8, with DERSIG", SCRIPT_VERIFY_DERSIG - ).Num(0).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220").PushSig(keys.key2)); - bad.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG, - "BIP66 example 9, without DERSIG", 0 - ).Num(0).Num(0).PushSig(keys.key2, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220")); bad.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG, - "BIP66 example 9, with DERSIG", SCRIPT_VERIFY_DERSIG + "BIP66 example 9", 0 ).Num(0).Num(0).PushSig(keys.key2, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220")); - good.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG << OP_NOT, - "BIP66 example 10, without DERSIG", 0 - ).Num(0).Num(0).PushSig(keys.key2, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220")); bad.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG << OP_NOT, - "BIP66 example 10, with DERSIG", SCRIPT_VERIFY_DERSIG + "BIP66 example 10", 0 ).Num(0).Num(0).PushSig(keys.key2, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220")); bad.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG, - "BIP66 example 11, without DERSIG", 0 - ).Num(0).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220").Num(0)); - bad.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG, - "BIP66 example 11, with DERSIG", SCRIPT_VERIFY_DERSIG + "BIP66 example 11", 0 ).Num(0).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220").Num(0)); good.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG << OP_NOT, - "BIP66 example 12, without DERSIG", 0 + "BIP66 example 12", 0 ).Num(0).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220").Num(0)); - good.push_back(TestBuilder(CScript() << OP_2 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey2C) << OP_2 << OP_CHECKMULTISIG << OP_NOT, - "BIP66 example 12, with DERSIG", SCRIPT_VERIFY_DERSIG - ).Num(0).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220").Num(0)); - good.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey2C) << OP_CHECKSIG, - "P2PK with multi-byte hashtype, without DERSIG", 0 - ).PushSig(keys.key2, SIGHASH_ALL).EditPush(70, "01", "0101")); + bad.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey2C) << OP_CHECKSIG, - "P2PK with multi-byte hashtype, with DERSIG", SCRIPT_VERIFY_DERSIG + "P2PK with multi-byte hashtype", 0 ).PushSig(keys.key2, SIGHASH_ALL).EditPush(70, "01", "0101")); good.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey2C) << OP_CHECKSIG, @@ -629,6 +579,7 @@ BOOST_AUTO_TEST_CASE(script_build) BOOST_AUTO_TEST_CASE(script_valid) { + uint32_t consensusBranchId = SPROUT_BRANCH_ID; // Read tests from test/data/script_valid.json // Format is an array of arrays // Inner arrays are [ "scriptSig", "scriptPubKey", "flags" ] @@ -652,12 +603,13 @@ BOOST_AUTO_TEST_CASE(script_valid) CScript scriptPubKey = ParseScript(scriptPubKeyString); unsigned int scriptflags = ParseScriptFlags(test[2].get_str()); - DoTest(scriptPubKey, scriptSig, scriptflags, true, strTest); + DoTest(scriptPubKey, scriptSig, scriptflags, consensusBranchId, true, strTest); } } BOOST_AUTO_TEST_CASE(script_invalid) { + uint32_t consensusBranchId = SPROUT_BRANCH_ID; // Scripts that should evaluate as invalid UniValue tests = read_json(std::string(json_tests::script_invalid, json_tests::script_invalid + sizeof(json_tests::script_invalid))); @@ -677,12 +629,14 @@ BOOST_AUTO_TEST_CASE(script_invalid) CScript scriptPubKey = ParseScript(scriptPubKeyString); unsigned int scriptflags = ParseScriptFlags(test[2].get_str()); - DoTest(scriptPubKey, scriptSig, scriptflags, false, strTest); + DoTest(scriptPubKey, scriptSig, scriptflags, consensusBranchId, false, strTest); } } BOOST_AUTO_TEST_CASE(script_PushData) { + uint32_t consensusBranchId = SPROUT_BRANCH_ID; + // Check that PUSHDATA1, PUSHDATA2, and PUSHDATA4 create the same value on // the stack as the 1-75 opcodes do. static const unsigned char direct[] = { 1, 0x5a }; @@ -692,29 +646,29 @@ BOOST_AUTO_TEST_CASE(script_PushData) ScriptError err; vector > directStack; - BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), &err)); + BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), consensusBranchId, &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); vector > pushdata1Stack; - BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), &err)); + BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), consensusBranchId, &err)); BOOST_CHECK(pushdata1Stack == directStack); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); vector > pushdata2Stack; - BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), &err)); + BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), consensusBranchId, &err)); BOOST_CHECK(pushdata2Stack == directStack); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); vector > pushdata4Stack; - BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), &err)); + BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), consensusBranchId, &err)); BOOST_CHECK(pushdata4Stack == directStack); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } CScript -sign_multisig(CScript scriptPubKey, std::vector keys, CTransaction transaction) +sign_multisig(CScript scriptPubKey, std::vector keys, CTransaction transaction, uint32_t consensusBranchId) { - uint256 hash = SignatureHash(scriptPubKey, transaction, 0, SIGHASH_ALL); + uint256 hash = SignatureHash(scriptPubKey, transaction, 0, SIGHASH_ALL, 0, consensusBranchId); CScript result; // @@ -736,15 +690,17 @@ sign_multisig(CScript scriptPubKey, std::vector keys, CTransaction transac return result; } CScript -sign_multisig(CScript scriptPubKey, const CKey &key, CTransaction transaction) +sign_multisig(CScript scriptPubKey, const CKey &key, CTransaction transaction, uint32_t consensusBranchId) { std::vector keys; keys.push_back(key); - return sign_multisig(scriptPubKey, keys, transaction); + return sign_multisig(scriptPubKey, keys, transaction, consensusBranchId); } BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12) { + uint32_t consensusBranchId = SPROUT_BRANCH_ID; + ScriptError err; CKey key1, key2, key3; key1.MakeNewKey(true); @@ -757,24 +713,26 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12) CMutableTransaction txFrom12 = BuildCreditingTransaction(scriptPubKey12); CMutableTransaction txTo12 = BuildSpendingTransaction(CScript(), txFrom12); - CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, flags, MutableTransactionSignatureChecker(&txTo12, 0), &err)); + CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12, consensusBranchId); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, flags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), consensusBranchId, &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); txTo12.vout[0].nValue = 2; - BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, flags, MutableTransactionSignatureChecker(&txTo12, 0), &err)); + BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, flags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), consensusBranchId, &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); - CScript goodsig2 = sign_multisig(scriptPubKey12, key2, txTo12); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, flags, MutableTransactionSignatureChecker(&txTo12, 0), &err)); + CScript goodsig2 = sign_multisig(scriptPubKey12, key2, txTo12, consensusBranchId); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, flags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), consensusBranchId, &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); - CScript badsig1 = sign_multisig(scriptPubKey12, key3, txTo12); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, flags, MutableTransactionSignatureChecker(&txTo12, 0), &err)); + CScript badsig1 = sign_multisig(scriptPubKey12, key3, txTo12, consensusBranchId); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, flags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), consensusBranchId, &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); } BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) { + uint32_t consensusBranchId = SPROUT_BRANCH_ID; + ScriptError err; CKey key1, key2, key3, key4; key1.MakeNewKey(true); @@ -790,61 +748,64 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) std::vector keys; keys.push_back(key1); keys.push_back(key2); - CScript goodsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + CScript goodsig1 = sign_multisig(scriptPubKey23, keys, txTo23, consensusBranchId); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), consensusBranchId, &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); keys.clear(); keys.push_back(key1); keys.push_back(key3); - CScript goodsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + CScript goodsig2 = sign_multisig(scriptPubKey23, keys, txTo23, consensusBranchId); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), consensusBranchId, &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); keys.clear(); keys.push_back(key2); keys.push_back(key3); - CScript goodsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + CScript goodsig3 = sign_multisig(scriptPubKey23, keys, txTo23, consensusBranchId); + BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), consensusBranchId, &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); keys.clear(); keys.push_back(key2); keys.push_back(key2); // Can't re-use sig - CScript badsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + CScript badsig1 = sign_multisig(scriptPubKey23, keys, txTo23, consensusBranchId); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), consensusBranchId, &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key2); keys.push_back(key1); // sigs must be in correct order - CScript badsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + CScript badsig2 = sign_multisig(scriptPubKey23, keys, txTo23, consensusBranchId); + BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), consensusBranchId, &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key3); keys.push_back(key2); // sigs must be in correct order - CScript badsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + CScript badsig3 = sign_multisig(scriptPubKey23, keys, txTo23, consensusBranchId); + BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), consensusBranchId, &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key4); keys.push_back(key2); // sigs must match pubkeys - CScript badsig4 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + CScript badsig4 = sign_multisig(scriptPubKey23, keys, txTo23, consensusBranchId); + BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), consensusBranchId, &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key1); keys.push_back(key4); // sigs must match pubkeys - CScript badsig5 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + CScript badsig5 = sign_multisig(scriptPubKey23, keys, txTo23, consensusBranchId); + BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), consensusBranchId, &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); // Must have signatures - CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23, consensusBranchId); + BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), consensusBranchId, &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err)); } BOOST_AUTO_TEST_CASE(script_combineSigs) { + uint32_t consensusBranchId = SPROUT_BRANCH_ID; + // Test the CombineSignatures function + CAmount amount = 0; CBasicKeyStore keystore; vector keys; vector pubkeys; @@ -862,62 +823,62 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) CScript& scriptPubKey = txFrom.vout[0].scriptPubKey; CScript& scriptSig = txTo.vin[0].scriptSig; - CScript empty; - CScript combined = CombineSignatures(scriptPubKey, txTo, 0, empty, empty); - BOOST_CHECK(combined.empty()); + SignatureData empty; + SignatureData combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), empty, empty, consensusBranchId); + BOOST_CHECK(combined.scriptSig.empty()); // Single signature case: - SignSignature(keystore, txFrom, txTo, 0); // changes scriptSig - combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSig, empty); - BOOST_CHECK(combined == scriptSig); - combined = CombineSignatures(scriptPubKey, txTo, 0, empty, scriptSig); - BOOST_CHECK(combined == scriptSig); + SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL, consensusBranchId); // changes scriptSig + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSig), empty, consensusBranchId); + BOOST_CHECK(combined.scriptSig == scriptSig); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), empty, SignatureData(scriptSig), consensusBranchId); + BOOST_CHECK(combined.scriptSig == scriptSig); CScript scriptSigCopy = scriptSig; // Signing again will give a different, valid signature: - SignSignature(keystore, txFrom, txTo, 0); - combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSigCopy, scriptSig); - BOOST_CHECK(combined == scriptSigCopy || combined == scriptSig); + SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL, consensusBranchId); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSigCopy), SignatureData(scriptSig), consensusBranchId); + BOOST_CHECK(combined.scriptSig == scriptSigCopy || combined.scriptSig == scriptSig); // P2SH, single-signature case: CScript pkSingle; pkSingle << ToByteVector(keys[0].GetPubKey()) << OP_CHECKSIG; keystore.AddCScript(pkSingle); scriptPubKey = GetScriptForDestination(CScriptID(pkSingle)); - SignSignature(keystore, txFrom, txTo, 0); - combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSig, empty); - BOOST_CHECK(combined == scriptSig); - combined = CombineSignatures(scriptPubKey, txTo, 0, empty, scriptSig); - BOOST_CHECK(combined == scriptSig); + SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL, consensusBranchId); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSig), empty, consensusBranchId); + BOOST_CHECK(combined.scriptSig == scriptSig); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), empty, SignatureData(scriptSig), consensusBranchId); + BOOST_CHECK(combined.scriptSig == scriptSig); scriptSigCopy = scriptSig; - SignSignature(keystore, txFrom, txTo, 0); - combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSigCopy, scriptSig); - BOOST_CHECK(combined == scriptSigCopy || combined == scriptSig); + SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL, consensusBranchId); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSigCopy), SignatureData(scriptSig), consensusBranchId); + BOOST_CHECK(combined.scriptSig == scriptSigCopy || combined.scriptSig == scriptSig); // dummy scriptSigCopy with placeholder, should always choose non-placeholder: scriptSigCopy = CScript() << OP_0 << static_cast >(pkSingle); - combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSigCopy, scriptSig); - BOOST_CHECK(combined == scriptSig); - combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSig, scriptSigCopy); - BOOST_CHECK(combined == scriptSig); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSigCopy), SignatureData(scriptSig), consensusBranchId); + BOOST_CHECK(combined.scriptSig == scriptSig); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSig), SignatureData(scriptSigCopy), consensusBranchId); + BOOST_CHECK(combined.scriptSig == scriptSig); // Hardest case: Multisig 2-of-3 scriptPubKey = GetScriptForMultisig(2, pubkeys); keystore.AddCScript(scriptPubKey); - SignSignature(keystore, txFrom, txTo, 0); - combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSig, empty); - BOOST_CHECK(combined == scriptSig); - combined = CombineSignatures(scriptPubKey, txTo, 0, empty, scriptSig); - BOOST_CHECK(combined == scriptSig); + SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL, consensusBranchId); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSig), empty, consensusBranchId); + BOOST_CHECK(combined.scriptSig == scriptSig); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), empty, SignatureData(scriptSig), consensusBranchId); + BOOST_CHECK(combined.scriptSig == scriptSig); // A couple of partially-signed versions: vector sig1; - uint256 hash1 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_ALL); + uint256 hash1 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_ALL, 0, consensusBranchId); BOOST_CHECK(keys[0].Sign(hash1, sig1)); sig1.push_back(SIGHASH_ALL); vector sig2; - uint256 hash2 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_NONE); + uint256 hash2 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_NONE, 0, consensusBranchId); BOOST_CHECK(keys[1].Sign(hash2, sig2)); sig2.push_back(SIGHASH_NONE); vector sig3; - uint256 hash3 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_SINGLE); + uint256 hash3 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_SINGLE, 0, consensusBranchId); BOOST_CHECK(keys[2].Sign(hash3, sig3)); sig3.push_back(SIGHASH_SINGLE); @@ -933,32 +894,34 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) CScript complete13 = CScript() << OP_0 << sig1 << sig3; CScript complete23 = CScript() << OP_0 << sig2 << sig3; - combined = CombineSignatures(scriptPubKey, txTo, 0, partial1a, partial1b); - BOOST_CHECK(combined == partial1a); - combined = CombineSignatures(scriptPubKey, txTo, 0, partial1a, partial2a); - BOOST_CHECK(combined == complete12); - combined = CombineSignatures(scriptPubKey, txTo, 0, partial2a, partial1a); - BOOST_CHECK(combined == complete12); - combined = CombineSignatures(scriptPubKey, txTo, 0, partial1b, partial2b); - BOOST_CHECK(combined == complete12); - combined = CombineSignatures(scriptPubKey, txTo, 0, partial3b, partial1b); - BOOST_CHECK(combined == complete13); - combined = CombineSignatures(scriptPubKey, txTo, 0, partial2a, partial3a); - BOOST_CHECK(combined == complete23); - combined = CombineSignatures(scriptPubKey, txTo, 0, partial3b, partial2b); - BOOST_CHECK(combined == complete23); - combined = CombineSignatures(scriptPubKey, txTo, 0, partial3b, partial3a); - BOOST_CHECK(combined == partial3c); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial1a), SignatureData(partial1b), consensusBranchId); + BOOST_CHECK(combined.scriptSig == partial1a); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial1a), SignatureData(partial2a), consensusBranchId); + BOOST_CHECK(combined.scriptSig == complete12); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial2a), SignatureData(partial1a), consensusBranchId); + BOOST_CHECK(combined.scriptSig == complete12); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial1b), SignatureData(partial2b), consensusBranchId); + BOOST_CHECK(combined.scriptSig == complete12); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial3b), SignatureData(partial1b), consensusBranchId); + BOOST_CHECK(combined.scriptSig == complete13); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial2a), SignatureData(partial3a), consensusBranchId); + BOOST_CHECK(combined.scriptSig == complete23); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial3b), SignatureData(partial2b), consensusBranchId); + BOOST_CHECK(combined.scriptSig == complete23); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial3b), SignatureData(partial3a), consensusBranchId); + BOOST_CHECK(combined.scriptSig == partial3c); } BOOST_AUTO_TEST_CASE(script_standard_push) { + uint32_t consensusBranchId = SPROUT_BRANCH_ID; + ScriptError err; for (int i=0; i<67000; i++) { CScript script; script << i; BOOST_CHECK_MESSAGE(script.IsPushOnly(), "Number " << i << " is not pure push."); - BOOST_CHECK_MESSAGE(VerifyScript(script, CScript() << OP_1, SCRIPT_VERIFY_MINIMALDATA, BaseSignatureChecker(), &err), "Number " << i << " push is not minimal data."); + BOOST_CHECK_MESSAGE(VerifyScript(script, CScript() << OP_1, SCRIPT_VERIFY_MINIMALDATA, BaseSignatureChecker(), consensusBranchId, &err), "Number " << i << " push is not minimal data."); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } @@ -967,7 +930,7 @@ BOOST_AUTO_TEST_CASE(script_standard_push) CScript script; script << data; BOOST_CHECK_MESSAGE(script.IsPushOnly(), "Length " << i << " is not pure push."); - BOOST_CHECK_MESSAGE(VerifyScript(script, CScript() << OP_1, SCRIPT_VERIFY_MINIMALDATA, BaseSignatureChecker(), &err), "Length " << i << " push is not minimal data."); + BOOST_CHECK_MESSAGE(VerifyScript(script, CScript() << OP_1, SCRIPT_VERIFY_MINIMALDATA, BaseSignatureChecker(), consensusBranchId, &err), "Length " << i << " push is not minimal data."); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } } diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index 989ed7428..c5a6ba1d4 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "consensus/upgrades.h" #include "consensus/validation.h" #include "data/sighash.json.h" #include "main.h" @@ -15,6 +16,7 @@ #include "sodium.h" #include +#include #include @@ -92,8 +94,23 @@ void static RandomScript(CScript &script) { script << oplist[insecure_rand() % (sizeof(oplist)/sizeof(oplist[0]))]; } -void static RandomTransaction(CMutableTransaction &tx, bool fSingle) { - tx.nVersion = insecure_rand(); +// Overwinter tx version numbers are selected randomly from current version range. +// http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution +// https://stackoverflow.com/a/19728404 +std::random_device rd; +std::mt19937 rng(rd()); +std::uniform_int_distribution version_dist(CTransaction::OVERWINTER_MIN_CURRENT_VERSION, + CTransaction::OVERWINTER_MAX_CURRENT_VERSION); + +void static RandomTransaction(CMutableTransaction &tx, bool fSingle, uint32_t consensusBranchId) { + tx.fOverwintered = insecure_rand() % 2; + if (tx.fOverwintered) { + tx.nVersion = version_dist(rng); + tx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID; + tx.nExpiryHeight = (insecure_rand() % 2) ? insecure_rand() : 0; + } else { + tx.nVersion = insecure_rand() & 0x7FFFFFFF; + } tx.vin.clear(); tx.vout.clear(); tx.nLockTime = (insecure_rand() % 2) ? insecure_rand() : 0; @@ -143,7 +160,7 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle) { // Empty output script. CScript scriptCode; CTransaction signTx(tx); - uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL); + uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId); assert(crypto_sign_detached(&tx.joinSplitSig[0], NULL, dataToBeSigned.begin(), 32, @@ -152,15 +169,16 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle) { } } -BOOST_FIXTURE_TEST_SUITE(sighash_tests, BasicTestingSetup) +BOOST_FIXTURE_TEST_SUITE(sighash_tests, JoinSplitTestingSetup) BOOST_AUTO_TEST_CASE(sighash_test) { + uint32_t overwinterBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_OVERWINTER].nBranchId; seed_insecure_rand(false); #if defined(PRINT_SIGHASH_JSON) std::cout << "[\n"; - std::cout << "\t[\"raw_transaction, script, input_index, hashType, signature_hash (result)\"],\n"; + std::cout << "\t[\"raw_transaction, script, input_index, hashType, branchId, signature_hash (result)\"],\n"; #endif int nRandomTests = 50000; @@ -169,15 +187,16 @@ BOOST_AUTO_TEST_CASE(sighash_test) #endif for (int i=0; i> tx; CValidationState state; - if (tx.nVersion < MIN_TX_VERSION) { + if (tx.fOverwintered) { + // Note that OVERWINTER_MIN_CURRENT_VERSION and OVERWINTER_MAX_CURRENT_VERSION + // are checked in IsStandardTx(), not in CheckTransactionWithoutProofVerification() + if (tx.nVersion < OVERWINTER_MIN_TX_VERSION || + tx.nExpiryHeight >= TX_EXPIRY_HEIGHT_THRESHOLD) + { + // Transaction must be invalid + BOOST_CHECK_MESSAGE(!CheckTransactionWithoutProofVerification(tx, state), strTest); + BOOST_CHECK(!state.IsValid()); + } else { + BOOST_CHECK_MESSAGE(CheckTransactionWithoutProofVerification(tx, state), strTest); + BOOST_CHECK(state.IsValid()); + } + } else if (tx.nVersion < SPROUT_MIN_TX_VERSION) { // Transaction must be invalid BOOST_CHECK_MESSAGE(!CheckTransactionWithoutProofVerification(tx, state), strTest); BOOST_CHECK(!state.IsValid()); @@ -250,7 +287,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data) continue; } - sh = SignatureHash(scriptCode, tx, nIn, nHashType); + sh = SignatureHash(scriptCode, tx, nIn, nHashType, 0, consensusBranchId); BOOST_CHECK_MESSAGE(sh.GetHex() == sigHashHex, strTest); } } diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 5c701c5cc..02a3a50d2 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -12,6 +12,7 @@ #include "main.h" #include "random.h" #include "txdb.h" +#include "txmempool.h" #include "ui_interface.h" #include "util.h" #ifdef ENABLE_WALLET @@ -30,20 +31,30 @@ ZCJoinSplit *pzcashParams; extern bool fPrintToConsole; extern void noui_connect(); +JoinSplitTestingSetup::JoinSplitTestingSetup() +{ + boost::filesystem::path pk_path = ZC_GetParamsDir() / "sprout-proving.key"; + boost::filesystem::path vk_path = ZC_GetParamsDir() / "sprout-verifying.key"; + pzcashParams = ZCJoinSplit::Prepared(vk_path.string(), pk_path.string()); +} + +JoinSplitTestingSetup::~JoinSplitTestingSetup() +{ + delete pzcashParams; +} + BasicTestingSetup::BasicTestingSetup() { - assert(init_and_check_sodium() != -1); - ECC_Start(); - pzcashParams = ZCJoinSplit::Unopened(); - SetupEnvironment(); - fPrintToDebugLog = false; // don't want to write to debug.log file - fCheckBlockIndex = true; - SelectParams(CBaseChainParams::MAIN); + assert(init_and_check_sodium() != -1); + ECC_Start(); + SetupEnvironment(); + fPrintToDebugLog = false; // don't want to write to debug.log file + fCheckBlockIndex = true; + SelectParams(CBaseChainParams::MAIN); } BasicTestingSetup::~BasicTestingSetup() { - ECC_Stop(); - delete pzcashParams; + ECC_Stop(); } TestingSetup::TestingSetup() @@ -92,6 +103,13 @@ TestingSetup::~TestingSetup() boost::filesystem::remove_all(pathTemp); } + +CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CMutableTransaction &tx, CTxMemPool *pool) { + return CTxMemPoolEntry(tx, nFee, nTime, dPriority, nHeight, + pool ? pool->HasNoInputsOf(tx) : hadNoDependencies, + spendsCoinbase, nBranchId); +} + void Shutdown(void* parg) { exit(0); diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h index 2f75332d4..ae528d682 100644 --- a/src/test/test_bitcoin.h +++ b/src/test/test_bitcoin.h @@ -1,6 +1,8 @@ #ifndef BITCOIN_TEST_TEST_BITCOIN_H #define BITCOIN_TEST_TEST_BITCOIN_H +#include "consensus/upgrades.h" +#include "pubkey.h" #include "txdb.h" #include @@ -10,15 +12,23 @@ * This just configures logging and chain parameters. */ struct BasicTestingSetup { + ECCVerifyHandle globalVerifyHandle; + BasicTestingSetup(); ~BasicTestingSetup(); }; +// Setup w.r.t. zk-SNARK API +struct JoinSplitTestingSetup: public BasicTestingSetup { + JoinSplitTestingSetup(); + ~JoinSplitTestingSetup(); +}; + /** Testing setup that configures a complete environment. * Included are data directory, coins database, script check threads * and wallet (if enabled) setup. */ -struct TestingSetup: public BasicTestingSetup { +struct TestingSetup: public JoinSplitTestingSetup { CCoinsViewDB *pcoinsdbview; boost::filesystem::path pathTemp; boost::thread_group threadGroup; @@ -27,4 +37,34 @@ struct TestingSetup: public BasicTestingSetup { ~TestingSetup(); }; +class CTxMemPoolEntry; +class CTxMemPool; + +struct TestMemPoolEntryHelper +{ + // Default values + CAmount nFee; + int64_t nTime; + double dPriority; + unsigned int nHeight; + bool hadNoDependencies; + bool spendsCoinbase; + uint32_t nBranchId; + + TestMemPoolEntryHelper() : + nFee(0), nTime(0), dPriority(0.0), nHeight(1), + hadNoDependencies(false), spendsCoinbase(false), + nBranchId(SPROUT_BRANCH_ID) { } + + CTxMemPoolEntry FromTx(CMutableTransaction &tx, CTxMemPool *pool = NULL); + + // 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; } + TestMemPoolEntryHelper &BranchId(uint32_t _branchId) { nBranchId = _branchId; return *this; } +}; #endif diff --git a/src/test/torcontrol_tests.cpp b/src/test/torcontrol_tests.cpp index 68516599d..b7affaacd 100644 --- a/src/test/torcontrol_tests.cpp +++ b/src/test/torcontrol_tests.cpp @@ -119,29 +119,60 @@ BOOST_AUTO_TEST_CASE(util_ParseTorReplyMapping) {"Foo", "Bar Baz"}, }); - // Escapes (which are left escaped by the parser) + // Escapes CheckParseTorReplyMapping( "Foo=\"Bar\\ Baz\"", { - {"Foo", "Bar\\ Baz"}, + {"Foo", "Bar Baz"}, }); CheckParseTorReplyMapping( "Foo=\"Bar\\Baz\"", { - {"Foo", "Bar\\Baz"}, + {"Foo", "BarBaz"}, }); CheckParseTorReplyMapping( "Foo=\"Bar\\@Baz\"", { - {"Foo", "Bar\\@Baz"}, + {"Foo", "Bar@Baz"}, }); CheckParseTorReplyMapping( "Foo=\"Bar\\\"Baz\" Spam=\"\\\"Eggs\\\"\"", { - {"Foo", "Bar\\\"Baz"}, - {"Spam", "\\\"Eggs\\\""}, + {"Foo", "Bar\"Baz"}, + {"Spam", "\"Eggs\""}, }); CheckParseTorReplyMapping( "Foo=\"Bar\\\\Baz\"", { - {"Foo", "Bar\\\\Baz"}, + {"Foo", "Bar\\Baz"}, }); + // C escapes + CheckParseTorReplyMapping( + "Foo=\"Bar\\nBaz\\t\" Spam=\"\\rEggs\" Octals=\"\\1a\\11\\17\\18\\81\\377\\378\\400\\2222\" Final=Check", { + {"Foo", "Bar\nBaz\t"}, + {"Spam", "\rEggs"}, + {"Octals", "\1a\11\17\1" "881\377\37" "8\40" "0\222" "2"}, + {"Final", "Check"}, + }); + CheckParseTorReplyMapping( + "Valid=Mapping Escaped=\"Escape\\\\\"", { + {"Valid", "Mapping"}, + {"Escaped", "Escape\\"}, + }); + CheckParseTorReplyMapping( + "Valid=Mapping Bare=\"Escape\\\"", {}); + CheckParseTorReplyMapping( + "OneOctal=\"OneEnd\\1\" TwoOctal=\"TwoEnd\\11\"", { + {"OneOctal", "OneEnd\1"}, + {"TwoOctal", "TwoEnd\11"}, + }); + + // Special handling for null case + // (needed because string comparison reads the null as end-of-string) + BOOST_TEST_MESSAGE(std::string("CheckParseTorReplyMapping(Null=\"\\0\")")); + auto ret = ParseTorReplyMapping("Null=\"\\0\""); + BOOST_CHECK_EQUAL(ret.size(), 1); + auto r_it = ret.begin(); + BOOST_CHECK_EQUAL(r_it->first, "Null"); + BOOST_CHECK_EQUAL(r_it->second.size(), 1); + BOOST_CHECK_EQUAL(r_it->second[0], '\0'); + // A more complex valid grammar. PROTOCOLINFO accepts a VersionLine that // takes a key=value pair followed by an OptArguments, making this valid. // Because an OptArguments contains no semantic data, there is no point in diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 7723cce1d..9fd35c2cc 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -6,7 +6,10 @@ #include "data/tx_valid.json.h" #include "test/test_bitcoin.h" +#include "init.h" #include "clientversion.h" +#include "checkqueue.h" +#include "consensus/upgrades.h" #include "consensus/validation.h" #include "core_io.h" #include "key.h" @@ -14,6 +17,7 @@ #include "main.h" #include "script/script.h" #include "script/script_error.h" +#include "script/sign.h" #include "primitives/transaction.h" #include "sodium.h" @@ -42,7 +46,6 @@ static std::map mapFlagNames = boost::assign::map_list_of (string("NONE"), (unsigned int)SCRIPT_VERIFY_NONE) (string("P2SH"), (unsigned int)SCRIPT_VERIFY_P2SH) (string("STRICTENC"), (unsigned int)SCRIPT_VERIFY_STRICTENC) - (string("DERSIG"), (unsigned int)SCRIPT_VERIFY_DERSIG) (string("LOW_S"), (unsigned int)SCRIPT_VERIFY_LOW_S) (string("SIGPUSHONLY"), (unsigned int)SCRIPT_VERIFY_SIGPUSHONLY) (string("MINIMALDATA"), (unsigned int)SCRIPT_VERIFY_MINIMALDATA) @@ -86,10 +89,12 @@ string FormatScriptFlags(unsigned int flags) return ret.substr(0, ret.size() - 1); } -BOOST_FIXTURE_TEST_SUITE(transaction_tests, BasicTestingSetup) +BOOST_FIXTURE_TEST_SUITE(transaction_tests, JoinSplitTestingSetup) BOOST_AUTO_TEST_CASE(tx_valid) { + uint32_t consensusBranchId = SPROUT_BRANCH_ID; + // Read tests from test/data/tx_valid.json // Format is an array of arrays // Inner arrays are either [ "comment" ] @@ -98,6 +103,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) // // verifyFlags is a comma separated list of script verification flags to apply, or "NONE" 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(); ScriptError err; @@ -108,7 +114,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) { if (test.size() != 3 || !test[1].isStr() || !test[2].isStr()) { - BOOST_ERROR("Bad test: " << strTest); + BOOST_ERROR("Bad test: " << strTest << comment); continue; } @@ -133,7 +139,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) } if (!fValid) { - BOOST_ERROR("Bad test: " << strTest); + BOOST_ERROR("Bad test: " << strTest << comment); continue; } @@ -143,29 +149,40 @@ BOOST_AUTO_TEST_CASE(tx_valid) stream >> tx; CValidationState state; - BOOST_CHECK_MESSAGE(CheckTransaction(tx, state, verifier), strTest); - BOOST_CHECK(state.IsValid()); + BOOST_CHECK_MESSAGE(CheckTransaction(tx, state, verifier), strTest + comment); + BOOST_CHECK_MESSAGE(state.IsValid(), comment); + PrecomputedTransactionData txdata(tx); for (unsigned int i = 0; i < tx.vin.size(); i++) { if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout)) { - BOOST_ERROR("Bad test: " << strTest); + BOOST_ERROR("Bad test: " << strTest << comment); break; } + CAmount amount = 0; unsigned int verify_flags = ParseScriptFlags(test[2].get_str()); BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], - verify_flags, TransactionSignatureChecker(&tx, i), &err), - strTest); - BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); + verify_flags, TransactionSignatureChecker(&tx, i, amount, txdata), consensusBranchId, &err), + strTest + comment); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err) + comment); } + + comment = ""; + } + else if (test.size() == 1) + { + comment += "\n# "; + comment += test[0].write(); } } } BOOST_AUTO_TEST_CASE(tx_invalid) { + uint32_t consensusBranchId = SPROUT_BRANCH_ID; + // Read tests from test/data/tx_invalid.json // Format is an array of arrays // Inner arrays are either [ "comment" ] @@ -174,6 +191,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) // // verifyFlags is a comma separated list of script verification flags to apply, or "NONE" 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(); ScriptError err; @@ -184,7 +202,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) { if (test.size() != 3 || !test[1].isStr() || !test[2].isStr()) { - BOOST_ERROR("Bad test: " << strTest); + BOOST_ERROR("Bad test: " << strTest << comment); continue; } @@ -209,7 +227,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) } if (!fValid) { - BOOST_ERROR("Bad test: " << strTest); + BOOST_ERROR("Bad test: " << strTest << comment); continue; } @@ -221,20 +239,29 @@ BOOST_AUTO_TEST_CASE(tx_invalid) CValidationState state; fValid = CheckTransaction(tx, state, verifier) && state.IsValid(); + PrecomputedTransactionData txdata(tx); for (unsigned int i = 0; i < tx.vin.size() && fValid; i++) { if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout)) { - BOOST_ERROR("Bad test: " << strTest); + BOOST_ERROR("Bad test: " << strTest << comment); break; } unsigned int verify_flags = ParseScriptFlags(test[2].get_str()); + CAmount amount = 0; fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], - verify_flags, TransactionSignatureChecker(&tx, i), &err); + verify_flags, TransactionSignatureChecker(&tx, i, amount, txdata), consensusBranchId, &err); } - BOOST_CHECK_MESSAGE(!fValid, strTest); - BOOST_CHECK_MESSAGE(err != SCRIPT_ERR_OK, ScriptErrorString(err)); + BOOST_CHECK_MESSAGE(!fValid, strTest + comment); + BOOST_CHECK_MESSAGE(err != SCRIPT_ERR_OK, ScriptErrorString(err) + comment); + + comment = ""; + } + else if (test.size() == 1) + { + comment += "\n# "; + comment += test[0].write(); } } } @@ -311,9 +338,6 @@ BOOST_AUTO_TEST_CASE(test_basic_joinsplit_verification) // Also, it's generally libzcash's job to ensure the // integrity of the scheme through its own tests. - // construct the r1cs keypair - auto p = ZCJoinSplit::Generate(); - // construct a merkle tree ZCIncrementalMerkleTree merkleTree; @@ -347,8 +371,8 @@ BOOST_AUTO_TEST_CASE(test_basic_joinsplit_verification) auto verifier = libzcash::ProofVerifier::Strict(); { - JSDescription jsdesc(*p, pubKeyHash, rt, inputs, outputs, 0, 0); - BOOST_CHECK(jsdesc.Verify(*p, verifier, pubKeyHash)); + JSDescription jsdesc(*pzcashParams, pubKeyHash, rt, inputs, outputs, 0, 0); + BOOST_CHECK(jsdesc.Verify(*pzcashParams, verifier, pubKeyHash)); CDataStream ss(SER_DISK, CLIENT_VERSION); ss << jsdesc; @@ -357,25 +381,26 @@ BOOST_AUTO_TEST_CASE(test_basic_joinsplit_verification) ss >> jsdesc_deserialized; BOOST_CHECK(jsdesc_deserialized == jsdesc); - BOOST_CHECK(jsdesc_deserialized.Verify(*p, verifier, pubKeyHash)); + BOOST_CHECK(jsdesc_deserialized.Verify(*pzcashParams, verifier, pubKeyHash)); } { // Ensure that the balance equation is working. - BOOST_CHECK_THROW(JSDescription(*p, pubKeyHash, rt, inputs, outputs, 10, 0), std::invalid_argument); - BOOST_CHECK_THROW(JSDescription(*p, pubKeyHash, rt, inputs, outputs, 0, 10), std::invalid_argument); + BOOST_CHECK_THROW(JSDescription(*pzcashParams, pubKeyHash, rt, inputs, outputs, 10, 0), std::invalid_argument); + BOOST_CHECK_THROW(JSDescription(*pzcashParams, pubKeyHash, rt, inputs, outputs, 0, 10), std::invalid_argument); } { // Ensure that it won't verify if the root is changed. - auto test = JSDescription(*p, pubKeyHash, rt, inputs, outputs, 0, 0); + auto test = JSDescription(*pzcashParams, pubKeyHash, rt, inputs, outputs, 0, 0); test.anchor = GetRandHash(); - BOOST_CHECK(!test.Verify(*p, verifier, pubKeyHash)); + BOOST_CHECK(!test.Verify(*pzcashParams, verifier, pubKeyHash)); } } BOOST_AUTO_TEST_CASE(test_simple_joinsplit_invalidity) { + uint32_t consensusBranchId = SPROUT_BRANCH_ID; auto verifier = libzcash::ProofVerifier::Strict(); CMutableTransaction tx; tx.nVersion = 2; @@ -403,13 +428,14 @@ BOOST_AUTO_TEST_CASE(test_simple_joinsplit_invalidity) jsdesc->nullifiers[0] = GetRandHash(); jsdesc->nullifiers[1] = GetRandHash(); - BOOST_CHECK(!CheckTransactionWithoutProofVerification(newTx, state)); + BOOST_CHECK(CheckTransactionWithoutProofVerification(newTx, state)); + BOOST_CHECK(!ContextualCheckTransaction(newTx, state, 0, 100)); BOOST_CHECK(state.GetRejectReason() == "bad-txns-invalid-joinsplit-signature"); // Empty output script. CScript scriptCode; CTransaction signTx(newTx); - uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL); + uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId); assert(crypto_sign_detached(&newTx.joinSplitSig[0], NULL, dataToBeSigned.begin(), 32, @@ -417,6 +443,7 @@ BOOST_AUTO_TEST_CASE(test_simple_joinsplit_invalidity) ) == 0); BOOST_CHECK(CheckTransactionWithoutProofVerification(newTx, state)); + BOOST_CHECK(ContextualCheckTransaction(newTx, state, 0, 100)); } { // Ensure that values within the joinsplit are well-formed. @@ -506,6 +533,8 @@ BOOST_AUTO_TEST_CASE(test_simple_joinsplit_invalidity) BOOST_AUTO_TEST_CASE(test_Get) { + uint32_t consensusBranchId = SPROUT_BRANCH_ID; + CBasicKeyStore keystore; CCoinsView coinsDummy; CCoinsViewCache coins(&coinsDummy); @@ -525,17 +554,99 @@ BOOST_AUTO_TEST_CASE(test_Get) t1.vout.resize(2); t1.vout[0].nValue = 90*CENT; t1.vout[0].scriptPubKey << OP_1; - int64_t interest; - BOOST_CHECK(AreInputsStandard(t1, coins)); - BOOST_CHECK_EQUAL(coins.GetValueIn(0,&interest,t1,0), (50+21+22)*CENT); + BOOST_CHECK(AreInputsStandard(t1, coins, consensusBranchId)); + BOOST_CHECK_EQUAL(coins.GetValueIn(t1), (50+21+22)*CENT); // Adding extra junk to the scriptSig should make it non-standard: t1.vin[0].scriptSig << OP_11; - BOOST_CHECK(!AreInputsStandard(t1, coins)); + BOOST_CHECK(!AreInputsStandard(t1, coins, consensusBranchId)); // ... as should not having enough: t1.vin[0].scriptSig = CScript(); - BOOST_CHECK(!AreInputsStandard(t1, coins)); + BOOST_CHECK(!AreInputsStandard(t1, coins, consensusBranchId)); +} + +BOOST_AUTO_TEST_CASE(test_big_overwinter_transaction) { + uint32_t consensusBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_OVERWINTER].nBranchId; + CMutableTransaction mtx; + mtx.fOverwintered = true; + mtx.nVersion = 3; + mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID; + + CKey key; + key.MakeNewKey(false); + CBasicKeyStore keystore; + keystore.AddKeyPubKey(key, key.GetPubKey()); + CKeyID hash = key.GetPubKey().GetID(); + CScript scriptPubKey = GetScriptForDestination(hash); + + vector sigHashes; + sigHashes.push_back(SIGHASH_NONE | SIGHASH_ANYONECANPAY); + sigHashes.push_back(SIGHASH_SINGLE | SIGHASH_ANYONECANPAY); + sigHashes.push_back(SIGHASH_ALL | SIGHASH_ANYONECANPAY); + sigHashes.push_back(SIGHASH_NONE); + sigHashes.push_back(SIGHASH_SINGLE); + sigHashes.push_back(SIGHASH_ALL); + + // create a big transaction of 4500 inputs signed by the same key + for(uint32_t ij = 0; ij < 4500; ij++) { + uint32_t i = mtx.vin.size(); + uint256 prevId; + prevId.SetHex("0000000000000000000000000000000000000000000000000000000000000100"); + COutPoint outpoint(prevId, i); + + mtx.vin.resize(mtx.vin.size() + 1); + mtx.vin[i].prevout = outpoint; + mtx.vin[i].scriptSig = CScript(); + + mtx.vout.resize(mtx.vout.size() + 1); + mtx.vout[i].nValue = 1000; + mtx.vout[i].scriptPubKey = CScript() << OP_1; + } + + // sign all inputs + for(uint32_t i = 0; i < mtx.vin.size(); i++) { + bool hashSigned = SignSignature(keystore, scriptPubKey, mtx, i, 1000, sigHashes.at(i % sigHashes.size()), consensusBranchId); + assert(hashSigned); + } + + CTransaction tx; + CDataStream ssout(SER_NETWORK, PROTOCOL_VERSION); + ssout << mtx; + ssout >> tx; + + // check all inputs concurrently, with the cache + PrecomputedTransactionData txdata(tx); + boost::thread_group threadGroup; + CCheckQueue scriptcheckqueue(128); + CCheckQueueControl control(&scriptcheckqueue); + + for (int i=0; i<20; i++) + threadGroup.create_thread(boost::bind(&CCheckQueue::Thread, boost::ref(scriptcheckqueue))); + + CCoins coins; + coins.nVersion = 1; + coins.fCoinBase = false; + for(uint32_t i = 0; i < mtx.vin.size(); i++) { + CTxOut txout; + txout.nValue = 1000; + txout.scriptPubKey = scriptPubKey; + coins.vout.push_back(txout); + } + + for(uint32_t i = 0; i < mtx.vin.size(); i++) { + std::vector vChecks; + CScriptCheck check(coins, tx, i, SCRIPT_VERIFY_P2SH, false, consensusBranchId, &txdata); + vChecks.push_back(CScriptCheck()); + check.swap(vChecks.back()); + control.Add(vChecks); + } + + bool controlCheck = control.Wait(); + assert(controlCheck); + + threadGroup.interrupt_all(); + threadGroup.join_all(); } BOOST_AUTO_TEST_CASE(test_IsStandard) diff --git a/src/timedata.cpp b/src/timedata.cpp index a14d69c11..1dcef2de3 100644 --- a/src/timedata.cpp +++ b/src/timedata.cpp @@ -99,7 +99,7 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample) if (!fMatch) { fDone = true; - string strMessage = _("Warning: Please check that your computer's date and time are correct! If your clock is wrong Bitcoin Core will not work properly."); + string strMessage = _("Warning: Please check that your computer's date and time are correct! If your clock is wrong Zcash will not work properly."); strMiscWarning = strMessage; LogPrintf("*** %s\n", strMessage); uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING); diff --git a/src/tinyformat.h b/src/tinyformat.h index 5971b7af8..4bb96953d 100644 --- a/src/tinyformat.h +++ b/src/tinyformat.h @@ -155,7 +155,7 @@ namespace tfm = tinyformat; #endif #ifdef __APPLE__ -// Workaround OSX linker warning: xcode uses different default symbol +// Workaround macOS linker warning: Xcode uses different default symbol // visibilities for static libs vs executables (see issue #25) # define TINYFORMAT_HIDDEN __attribute__((visibility("hidden"))) #else @@ -584,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 60181b440..99c76995b 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -1,3 +1,8 @@ +// Copyright (c) 2015-2017 The Bitcoin Core developers +// Copyright (c) 2017 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include "torcontrol.h" #include "utilstrencodings.h" #include "net.h" @@ -285,17 +290,61 @@ static std::map ParseTorReplyMapping(const std::string ++ptr; // skip opening '"' bool escape_next = false; while (ptr < s.size() && (escape_next || s[ptr] != '"')) { - escape_next = (s[ptr] == '\\'); + // Repeated backslashes must be interpreted as pairs + escape_next = (s[ptr] == '\\' && !escape_next); value.push_back(s[ptr]); ++ptr; } if (ptr == s.size()) // unexpected end of line return std::map(); ++ptr; // skip closing '"' - /* TODO: unescape value - according to the spec this depends on the - * context, some strings use C-LogPrintf style escape codes, some - * don't. So may be better handled at the call site. + /** + * Unescape value. Per https://spec.torproject.org/control-spec section 2.1.1: + * + * For future-proofing, controller implementors MAY use the following + * rules to be compatible with buggy Tor implementations and with + * future ones that implement the spec as intended: + * + * Read \n \t \r and \0 ... \377 as C escapes. + * Treat a backslash followed by any other character as that character. */ + std::string escaped_value; + for (size_t i = 0; i < value.size(); ++i) { + if (value[i] == '\\') { + // This will always be valid, because if the QuotedString + // ended in an odd number of backslashes, then the parser + // would already have returned above, due to a missing + // terminating double-quote. + ++i; + if (value[i] == 'n') { + escaped_value.push_back('\n'); + } else if (value[i] == 't') { + escaped_value.push_back('\t'); + } else if (value[i] == 'r') { + escaped_value.push_back('\r'); + } else if ('0' <= value[i] && value[i] <= '7') { + size_t j; + // Octal escape sequences have a limit of three octal digits, + // but terminate at the first character that is not a valid + // octal digit if encountered sooner. + for (j = 1; j < 3 && (i+j) < value.size() && '0' <= value[i+j] && value[i+j] <= '7'; ++j) {} + // Tor restricts first digit to 0-3 for three-digit octals. + // A leading digit of 4-7 would therefore be interpreted as + // a two-digit octal. + if (j == 3 && value[i] > '3') { + j--; + } + escaped_value.push_back(strtol(value.substr(i, j).c_str(), NULL, 8)); + // Account for automatic incrementing at loop end + i += j - 1; + } else { + escaped_value.push_back(value[i]); + } + } else { + escaped_value.push_back(value[i]); + } + } + value = escaped_value; } else { // Unquoted value. Note that values can contain '=' at will, just no spaces while (ptr < s.size() && s[ptr] != ' ') { value.push_back(s[ptr]); @@ -327,8 +376,10 @@ static std::pair ReadBinaryFile(const std::string &filename, s while ((n=fread(buffer, 1, sizeof(buffer), f)) > 0) { // Check for reading errors so we don't return any data if we couldn't // read the entire file (or up to maxsize) - if (ferror(f)) + if (ferror(f)) { + fclose(f); return std::make_pair(false,""); + } retval.append(buffer, buffer+n); if (retval.size() > maxsize) break; @@ -364,7 +415,7 @@ public: TorController(struct event_base* base, const std::string& target); ~TorController(); - /** Get name fo file to store private key in */ + /** Get name for file to store private key in */ std::string GetPrivateKeyFile(); /** Reconnect, after getting disconnected */ diff --git a/src/txdb.cpp b/src/txdb.cpp index 9a23596d9..73109881f 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -65,6 +65,9 @@ void static BatchWriteHashBestAnchor(CLevelDBBatch &batch, const uint256 &hash) batch.Write(DB_BEST_ANCHOR, hash); } +CCoinsViewDB::CCoinsViewDB(std::string dbName, size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / dbName, nCacheSize, fMemory, fWipe) { +} + CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe) { } @@ -304,15 +307,24 @@ bool CBlockTreeDB::LoadBlockIndexGuts() pindexNew->nVersion = diskindex.nVersion; pindexNew->hashReserved = diskindex.hashReserved; pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; + pindexNew->hashReserved = diskindex.hashReserved; pindexNew->nTime = diskindex.nTime; pindexNew->nBits = diskindex.nBits; pindexNew->nNonce = diskindex.nNonce; pindexNew->nSolution = diskindex.nSolution; pindexNew->nStatus = diskindex.nStatus; + pindexNew->nCachedBranchId = diskindex.nCachedBranchId; pindexNew->nTx = diskindex.nTx; + pindexNew->nSproutValue = diskindex.nSproutValue; + + // Consistency checks + auto header = pindexNew->GetBlockHeader(); + if (header.GetHash() != pindexNew->GetBlockHash()) + return error("LoadBlockIndex(): block header inconsistency detected: on-disk = %s, in-memory = %s", + diskindex.ToString(), pindexNew->ToString()); uint8_t pubkey33[33]; komodo_index2pubkey33(pubkey33,pindexNew,pindexNew->nHeight); - if (!CheckProofOfWork(pindexNew->nHeight,pubkey33,pindexNew->GetBlockHash(), pindexNew->nBits, Params().GetConsensus())) + if (!CheckProofOfWork(pindexNew->nHeight,pubkey33,pindexNew->GetBlockHash(), pindexNew->nBits, Params().GetConsensus(),pindexNew->nTime)) return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew->ToString()); pcursor->Next(); } else { diff --git a/src/txdb.h b/src/txdb.h index 220cfb171..04ac8627b 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -20,8 +20,8 @@ struct CDiskTxPos; class uint256; //! -dbcache default (MiB) -static const int64_t nDefaultDbCache = 100; -//! max. -dbcache in (MiB) +static const int64_t nDefaultDbCache = 450; +//! max. -dbcache (MiB) static const int64_t nMaxDbCache = sizeof(void*) > 4 ? 16384 : 1024; //! min. -dbcache in (MiB) static const int64_t nMinDbCache = 4; @@ -31,6 +31,7 @@ class CCoinsViewDB : public CCoinsView { protected: CLevelDBWrapper db; + CCoinsViewDB(std::string dbName, size_t nCacheSize, bool fMemory = false, bool fWipe = false); public: CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 6931afb3d..2dc9a4642 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -11,6 +11,7 @@ #include "main.h" #include "policy/fees.h" #include "streams.h" +#include "timedata.h" #include "util.h" #include "utilmoneystr.h" #include "version.h" @@ -19,20 +20,24 @@ using namespace std; CTxMemPoolEntry::CTxMemPoolEntry(): - nFee(0), nTxSize(0), nModSize(0), nUsageSize(0), nTime(0), dPriority(0.0), hadNoDependencies(false) + 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): + unsigned int _nHeight, bool poolHasNoInputsOf, + bool _spendsCoinbase, uint32_t _nBranchId): tx(_tx), nFee(_nFee), nTime(_nTime), dPriority(_dPriority), nHeight(_nHeight), - hadNoDependencies(poolHasNoInputsOf) + hadNoDependencies(poolHasNoInputsOf), + spendsCoinbase(_spendsCoinbase), nBranchId(_nBranchId) { nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); nModSize = tx.CalculateModifiedSize(nTxSize); nUsageSize = RecursiveDynamicUsage(tx); + feeRate = CFeeRate(nFee, nTxSize); } CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other) @@ -55,7 +60,7 @@ CTxMemPool::CTxMemPool(const CFeeRate& _minRelayFee) : // 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 - fSanityCheck = false; + nCheckFrequency = 0; minerPolicyEstimator = new CBlockPolicyEstimator(_minRelayFee); } @@ -97,8 +102,8 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, // Used by main.cpp AcceptToMemoryPool(), which DOES do // all the appropriate checks. LOCK(cs); - mapTx[hash] = entry; - const CTransaction& tx = mapTx[hash].GetTx(); + mapTx.insert(entry); + const CTransaction& tx = mapTx.find(hash)->GetTx(); for (unsigned int i = 0; i < tx.vin.size(); i++) mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i); BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { @@ -140,7 +145,7 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list& rem txToRemove.pop_front(); if (!mapTx.count(hash)) continue; - const CTransaction& tx = mapTx[hash].GetTx(); + 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)); @@ -158,8 +163,8 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list& rem } removed.push_back(tx); - totalTxSize -= mapTx[hash].GetTxSize(); - cachedInnerUsage -= mapTx[hash].DynamicMemoryUsage(); + totalTxSize -= mapTx.find(hash)->GetTxSize(); + cachedInnerUsage -= mapTx.find(hash)->DynamicMemoryUsage(); mapTx.erase(hash); nTransactionsUpdated++; minerPolicyEstimator->removeTx(hash); @@ -167,25 +172,30 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list& rem } } -void CTxMemPool::removeCoinbaseSpends(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight) +void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags) { // Remove transactions spending a coinbase which are now immature extern char ASSETCHAINS_SYMBOL[]; if ( ASSETCHAINS_SYMBOL[0] == 0 ) COINBASE_MATURITY = _COINBASE_MATURITY; + // Remove transactions spending a coinbase which are now immature and no-longer-final transactions LOCK(cs); list transactionsToRemove; - for (std::map::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { - const CTransaction& tx = it->second.GetTx(); - BOOST_FOREACH(const CTxIn& txin, tx.vin) { - std::map::const_iterator it2 = mapTx.find(txin.prevout.hash); - if (it2 != mapTx.end()) - continue; - const CCoins *coins = pcoins->AccessCoins(txin.prevout.hash); - if (fSanityCheck) assert(coins); - if (!coins || (coins->IsCoinBase() && ((signed long)nMemPoolHeight) - coins->nHeight < COINBASE_MATURITY)) { - transactionsToRemove.push_back(tx); - break; + for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { + const CTransaction& tx = it->GetTx(); + if (!CheckFinalTx(tx, flags)) { + transactionsToRemove.push_back(tx); + } else if (it->GetSpendsCoinbase()) { + BOOST_FOREACH(const CTxIn& txin, tx.vin) { + indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash); + if (it2 != mapTx.end()) + continue; + const CCoins *coins = pcoins->AccessCoins(txin.prevout.hash); + if (nCheckFrequency != 0) assert(coins); + if (!coins || (coins->IsCoinBase() && ((signed long)nMemPoolHeight) - coins->nHeight < COINBASE_MATURITY)) { + transactionsToRemove.push_back(tx); + break; + } } } } @@ -205,8 +215,8 @@ void CTxMemPool::removeWithAnchor(const uint256 &invalidRoot) LOCK(cs); list transactionsToRemove; - for (std::map::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { - const CTransaction& tx = it->second.GetTx(); + for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { + const CTransaction& tx = it->GetTx(); BOOST_FOREACH(const JSDescription& joinsplit, tx.vjoinsplit) { if (joinsplit.anchor == invalidRoot) { transactionsToRemove.push_back(tx); @@ -251,6 +261,25 @@ void CTxMemPool::removeConflicts(const CTransaction &tx, std::list } } +void CTxMemPool::removeExpired(unsigned int nBlockHeight) +{ + // Remove expired txs from the mempool + LOCK(cs); + list transactionsToRemove; + for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) + { + const CTransaction& tx = it->GetTx(); + if (IsExpiredTx(tx, nBlockHeight)) { + transactionsToRemove.push_back(tx); + } + } + for (const CTransaction& tx : transactionsToRemove) { + list removed; + remove(tx, removed, true); + LogPrint("mempool", "Removing expired txid: %s\n", tx.GetHash().ToString()); + } +} + /** * Called when a block is connected. Removes from mempool and updates the miner fee estimator. */ @@ -262,8 +291,10 @@ void CTxMemPool::removeForBlock(const std::vector& vtx, unsigned i BOOST_FOREACH(const CTransaction& tx, vtx) { uint256 hash = tx.GetHash(); - if (mapTx.count(hash)) - entries.push_back(mapTx[hash]); + + indexed_transaction_set::iterator i = mapTx.find(hash); + if (i != mapTx.end()) + entries.push_back(*i); } BOOST_FOREACH(const CTransaction& tx, vtx) { @@ -276,6 +307,28 @@ void CTxMemPool::removeForBlock(const std::vector& vtx, unsigned i minerPolicyEstimator->processBlock(nBlockHeight, entries, fCurrentEstimate); } +/** + * Called whenever the tip changes. Removes transactions which don't commit to + * the given branch ID from the mempool. + */ +void CTxMemPool::removeWithoutBranchId(uint32_t nMemPoolBranchId) +{ + LOCK(cs); + std::list transactionsToRemove; + + for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { + const CTransaction& tx = it->GetTx(); + if (it->GetValidatedBranchId() != nMemPoolBranchId) { + transactionsToRemove.push_back(tx); + } + } + + for (const CTransaction& tx : transactionsToRemove) { + std::list removed; + remove(tx, removed, true); + } +} + void CTxMemPool::clear() { LOCK(cs); @@ -288,7 +341,10 @@ void CTxMemPool::clear() void CTxMemPool::check(const CCoinsViewCache *pcoins) const { - if (!fSanityCheck) + if (nCheckFrequency == 0) + return; + + if (insecure_rand() >= nCheckFrequency) return; LogPrint("mempool", "Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); @@ -297,20 +353,21 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const uint64_t innerUsage = 0; CCoinsViewCache mempoolDuplicate(const_cast(pcoins)); + const int64_t nSpendHeight = GetSpendHeight(mempoolDuplicate); LOCK(cs); list waitingOnDependants; - for (std::map::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { + for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { unsigned int i = 0; - checkTotal += it->second.GetTxSize(); - innerUsage += it->second.DynamicMemoryUsage(); - const CTransaction& tx = it->second.GetTx(); + checkTotal += it->GetTxSize(); + innerUsage += it->DynamicMemoryUsage(); + const CTransaction& tx = it->GetTx(); bool fDependsWait = false; BOOST_FOREACH(const CTxIn &txin, tx.vin) { // Check that every mempool transaction's inputs refer to available coins, or other mempool tx's. - std::map::const_iterator it2 = mapTx.find(txin.prevout.hash); + indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash); if (it2 != mapTx.end()) { - const CTransaction& tx2 = it2->second.GetTx(); + const CTransaction& tx2 = it2->GetTx(); assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull()); fDependsWait = true; } else { @@ -348,11 +405,13 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const intermediates.insert(std::make_pair(tree.root(), tree)); } if (fDependsWait) - waitingOnDependants.push_back(&it->second); + waitingOnDependants.push_back(&(*it)); else { CValidationState state; - assert(ContextualCheckInputs(tx, state, mempoolDuplicate, false, 0, false, Params().GetConsensus(), NULL)); - UpdateCoins(tx, state, mempoolDuplicate, 1000000); + bool fCheckResult = tx.IsCoinBase() || + Consensus::CheckTxInputs(tx, state, mempoolDuplicate, nSpendHeight, Params().GetConsensus()); + assert(fCheckResult); + UpdateCoins(tx, mempoolDuplicate, 1000000); } } unsigned int stepsSinceLastRemove = 0; @@ -365,15 +424,17 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const stepsSinceLastRemove++; assert(stepsSinceLastRemove < waitingOnDependants.size()); } else { - assert(ContextualCheckInputs(entry->GetTx(), state, mempoolDuplicate, false, 0, false, Params().GetConsensus(), NULL)); - UpdateCoins(entry->GetTx(), state, mempoolDuplicate, 1000000); + bool fCheckResult = entry->GetTx().IsCoinBase() || + Consensus::CheckTxInputs(entry->GetTx(), state, mempoolDuplicate, nSpendHeight, Params().GetConsensus()); + assert(fCheckResult); + UpdateCoins(entry->GetTx(), mempoolDuplicate, 1000000); stepsSinceLastRemove = 0; } } for (std::map::const_iterator it = mapNextTx.begin(); it != mapNextTx.end(); it++) { uint256 hash = it->second.ptx->GetHash(); - map::const_iterator it2 = mapTx.find(hash); - const CTransaction& tx = it2->second.GetTx(); + indexed_transaction_set::const_iterator it2 = mapTx.find(hash); + const CTransaction& tx = it2->GetTx(); assert(it2 != mapTx.end()); assert(&tx == it->second.ptx); assert(tx.vin.size() > it->second.n); @@ -382,8 +443,8 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const for (std::map::const_iterator it = mapNullifiers.begin(); it != mapNullifiers.end(); it++) { uint256 hash = it->second->GetHash(); - map::const_iterator it2 = mapTx.find(hash); - const CTransaction& tx = it2->second.GetTx(); + indexed_transaction_set::const_iterator it2 = mapTx.find(hash); + const CTransaction& tx = it2->GetTx(); assert(it2 != mapTx.end()); assert(&tx == it->second); } @@ -398,16 +459,16 @@ void CTxMemPool::queryHashes(vector& vtxid) LOCK(cs); vtxid.reserve(mapTx.size()); - for (map::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) - vtxid.push_back((*mi).first); + for (indexed_transaction_set::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) + vtxid.push_back(mi->GetTx().GetHash()); } bool CTxMemPool::lookup(uint256 hash, CTransaction& result) const { LOCK(cs); - map::const_iterator i = mapTx.find(hash); + indexed_transaction_set::const_iterator i = mapTx.find(hash); if (i == mapTx.end()) return false; - result = i->second.GetTx(); + result = i->GetTx(); return true; } @@ -520,5 +581,6 @@ bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) const { size_t CTxMemPool::DynamicMemoryUsage() const { LOCK(cs); - return memusage::DynamicUsage(mapTx) + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + cachedInnerUsage; + // Estimate the overhead of mapTx to be 6 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented. + return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 6 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + cachedInnerUsage; } diff --git a/src/txmempool.h b/src/txmempool.h index cd0f9a54b..6b6434f1e 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -13,6 +13,10 @@ #include "primitives/transaction.h" #include "sync.h" +#undef foreach +#include "boost/multi_index_container.hpp" +#include "boost/multi_index/ordered_index.hpp" + class CAutoFile; inline double AllowFreeThreshold() @@ -41,25 +45,54 @@ private: 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 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 public: CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, - int64_t _nTime, double _dPriority, unsigned int _nHeight, bool poolHasNoInputsOf = false); + 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; } double GetPriority(unsigned int currentHeight) 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; } bool WasClearAtEntry() const { return hadNoDependencies; } size_t DynamicMemoryUsage() const { return nUsageSize; } + + bool GetSpendsCoinbase() const { return spendsCoinbase; } + uint32_t GetValidatedBranchId() const { return nBranchId; } +}; + +// extracts a TxMemPoolEntry's transaction hash +struct mempoolentry_txid +{ + typedef uint256 result_type; + result_type operator() (const CTxMemPoolEntry &entry) const + { + return entry.GetTx().GetHash(); + } +}; + +class CompareTxMemPoolEntryByFee +{ +public: + bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) + { + if (a.GetFeeRate() == b.GetFeeRate()) + return a.GetTime() < b.GetTime(); + return a.GetFeeRate() > b.GetFeeRate(); + } }; class CBlockPolicyEstimator; @@ -91,7 +124,7 @@ public: class CTxMemPool { private: - bool fSanityCheck; //! Normally false, true if -checkmempool or -regtest + uint32_t nCheckFrequency; //! Value n means that n times in 2^32 we check. unsigned int nTransactionsUpdated; CBlockPolicyEstimator* minerPolicyEstimator; @@ -99,8 +132,21 @@ private: uint64_t cachedInnerUsage; //! sum of dynamic memory usage of all the map elements (NOT the maps themselves) public: + typedef boost::multi_index_container< + CTxMemPoolEntry, + boost::multi_index::indexed_by< + // sorted by txid + boost::multi_index::ordered_unique, + // sorted by fee rate + boost::multi_index::ordered_non_unique< + boost::multi_index::identity, + CompareTxMemPoolEntryByFee + > + > + > indexed_transaction_set; + mutable CCriticalSection cs; - std::map mapTx; + indexed_transaction_set mapTx; std::map mapNextTx; std::map mapNullifiers; std::map > mapDeltas; @@ -115,15 +161,17 @@ public: * check does nothing. */ void check(const CCoinsViewCache *pcoins) const; - void setSanityCheck(bool _fSanityCheck) { fSanityCheck = _fSanityCheck; } + void setSanityCheck(double dFrequency = 1.0) { nCheckFrequency = dFrequency * 4294967296.0; } bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate = true); void remove(const CTransaction &tx, std::list& removed, bool fRecursive = false); void removeWithAnchor(const uint256 &invalidRoot); - void removeCoinbaseSpends(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight); + void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags); void removeConflicts(const CTransaction &tx, std::list& removed); + void removeExpired(unsigned int nBlockHeight); void removeForBlock(const std::vector& vtx, unsigned int nBlockHeight, std::list& conflicts, bool fCurrentEstimate = true); + void removeWithoutBranchId(uint32_t nMemPoolBranchId); void clear(); void queryHashes(std::vector& vtxid); void pruneSpent(const uint256& hash, CCoins &coins); diff --git a/src/univalue/include/univalue.h b/src/univalue/include/univalue.h index 07314bc5c..dfc84f921 100644 --- a/src/univalue/include/univalue.h +++ b/src/univalue/include/univalue.h @@ -3,8 +3,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef __UNIVALUE_H__ -#define __UNIVALUE_H__ +#ifndef UNIVALUE_H__ +#define UNIVALUE_H__ #include @@ -293,4 +293,4 @@ extern const UniValue NullUniValue; const UniValue& find_value( const UniValue& obj, const std::string& name); -#endif // __UNIVALUE_H__ +#endif // UNIVALUE_H__ diff --git a/src/univalue/lib/univalue.cpp b/src/univalue/lib/univalue.cpp index 47ca7aca7..14f4c3c4f 100644 --- a/src/univalue/lib/univalue.cpp +++ b/src/univalue/lib/univalue.cpp @@ -35,7 +35,7 @@ bool ParseInt32(const std::string& str, int32_t *out) 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 a over/underflow + // 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 && @@ -51,7 +51,7 @@ bool ParseInt64(const std::string& str, int64_t *out) 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 a over/underflow + // 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() && diff --git a/src/util.cpp b/src/util.cpp index be7517573..dd9b709a2 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -926,7 +926,7 @@ std::string PrivacyInfo() { return "\n" + FormatParagraph(strprintf(_("In order to ensure you are adequately protecting your privacy when using Zcash, please see <%s>."), - "https://z.cash/support/security/index.html")) + "\n"; + "https://z.cash/support/security/")) + "\n"; } std::string LicenseInfo() @@ -940,6 +940,12 @@ std::string LicenseInfo() "\n" + FormatParagraph(_("Distributed under the MIT software license, see the accompanying file COPYING or .")) + "\n" + "\n" + - FormatParagraph(_("This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard.")) + + FormatParagraph(_("This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit and cryptographic software written by Eric Young.")) + "\n"; } + +int GetNumCores() +{ + return boost::thread::physical_concurrency(); +} + diff --git a/src/util.h b/src/util.h index 424c6693a..faf741409 100644 --- a/src/util.h +++ b/src/util.h @@ -219,6 +219,13 @@ std::string HelpMessageGroup(const std::string& message); */ std::string HelpMessageOpt(const std::string& option, const std::string& message); +/** + * Return the number of physical cores available on the current system. + * @note This does not count virtual cores, such as those provided by HyperThreading + * when boost is newer than 1.56. + */ +int GetNumCores(); + void SetThreadPriority(int nPriority); void RenameThread(const char* name); diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index 054992cfb..0a5fbb3d2 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include using namespace std; @@ -46,6 +47,20 @@ string SanitizeFilename(const string& str) return strResult; } +std::string HexInt(uint32_t val) +{ + std::stringstream ss; + ss << std::setfill('0') << std::setw(sizeof(uint32_t) * 2) << std::hex << val; + return ss.str(); +} + +uint32_t ParseHexToUInt32(const std::string& str) { + std::istringstream converter(str); + uint32_t value; + converter >> std::hex >> value; + return value; +} + const signed char p_util_hexdigit[256] = { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h index d6973a130..ccdc6a76b 100644 --- a/src/utilstrencodings.h +++ b/src/utilstrencodings.h @@ -24,6 +24,8 @@ std::string SanitizeFilename(const std::string& str); std::string SanitizeString(const std::string& str); +std::string HexInt(uint32_t val); +uint32_t ParseHexToUInt32(const std::string& str); std::vector ParseHex(const char* psz); std::vector ParseHex(const std::string& str); signed char HexDigit(char c); diff --git a/src/utiltest.cpp b/src/utiltest.cpp index 5cebc1a5d..4599cec3c 100644 --- a/src/utiltest.cpp +++ b/src/utiltest.cpp @@ -4,6 +4,8 @@ #include "utiltest.h" +#include "consensus/upgrades.h" + CWalletTx GetValidReceive(ZCJoinSplit& params, const libzcash::SpendingKey& sk, CAmount value, bool randomInputs) { @@ -45,9 +47,10 @@ CWalletTx GetValidReceive(ZCJoinSplit& params, mtx.vjoinsplit.push_back(jsdesc); // Empty output script. + uint32_t consensusBranchId = SPROUT_BRANCH_ID; CScript scriptCode; CTransaction signTx(mtx); - uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL); + uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId); // Add the signature assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, @@ -63,7 +66,7 @@ CWalletTx GetValidReceive(ZCJoinSplit& params, libzcash::Note GetNote(ZCJoinSplit& params, const libzcash::SpendingKey& sk, const CTransaction& tx, size_t js, size_t n) { - ZCNoteDecryption decryptor {sk.viewing_key()}; + ZCNoteDecryption decryptor {sk.receiving_key()}; auto hSig = tx.vjoinsplit[js].h_sig(params, tx.joinSplitPubKey); auto note_pt = libzcash::NotePlaintext::decrypt( decryptor, @@ -129,9 +132,10 @@ CWalletTx GetValidSpend(ZCJoinSplit& params, mtx.vjoinsplit.push_back(jsdesc); // Empty output script. + uint32_t consensusBranchId = SPROUT_BRANCH_ID; CScript scriptCode; CTransaction signTx(mtx); - uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL); + uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId); // Add the signature assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, diff --git a/src/utiltime.cpp b/src/utiltime.cpp index a7cdeb114..f1a408a31 100644 --- a/src/utiltime.cpp +++ b/src/utiltime.cpp @@ -43,20 +43,7 @@ int64_t GetTimeMicros() void MilliSleep(int64_t n) { - -/** - * Boost's sleep_for was uninterruptable when backed by nanosleep from 1.50 - * until fixed in 1.52. Use the deprecated sleep method for the broken case. - * See: https://svn.boost.org/trac/boost/ticket/7238 - */ -#if defined(HAVE_WORKING_BOOST_SLEEP_FOR) boost::this_thread::sleep_for(boost::chrono::milliseconds(n)); -#elif defined(HAVE_WORKING_BOOST_SLEEP) - boost::this_thread::sleep(boost::posix_time::milliseconds(n)); -#else -//should never get here -#error missing boost sleep implementation -#endif } std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime) diff --git a/src/version.h b/src/version.h index 91a464030..25527895d 100644 --- a/src/version.h +++ b/src/version.h @@ -9,7 +9,7 @@ * network protocol versioning */ -static const int PROTOCOL_VERSION = 170002; +static const int PROTOCOL_VERSION = 170003; //! initial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; @@ -24,10 +24,6 @@ static const int MIN_PEER_PROTO_VERSION = 170002; //! if possible, avoid requesting addresses nodes older than this static const int CADDR_TIME_VERSION = 31402; -//! only request blocks from nodes outside this range of versions -static const int NOBLKS_VERSION_START = 32000; -static const int NOBLKS_VERSION_END = 32400; - //! BIP 0031, pong message, is enabled for all versions AFTER this one static const int BIP0031_VERSION = 60000; diff --git a/src/wallet/asyncrpcoperation_mergetoaddress.cpp b/src/wallet/asyncrpcoperation_mergetoaddress.cpp new file mode 100644 index 000000000..fa823f50a --- /dev/null +++ b/src/wallet/asyncrpcoperation_mergetoaddress.cpp @@ -0,0 +1,947 @@ +// Copyright (c) 2017 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "asyncrpcoperation_mergetoaddress.h" + +#include "amount.h" +#include "asyncrpcqueue.h" +#include "core_io.h" +#include "init.h" +#include "main.h" +#include "miner.h" +#include "net.h" +#include "netbase.h" +#include "rpcprotocol.h" +#include "rpcserver.h" +#include "script/interpreter.h" +#include "sodium.h" +#include "timedata.h" +#include "util.h" +#include "utilmoneystr.h" +#include "utiltime.h" +#include "wallet.h" +#include "walletdb.h" +#include "zcash/IncrementalMerkleTree.hpp" + +#include +#include +#include +#include + +#include "paymentdisclosuredb.h" + +using namespace libzcash; + +int mta_find_output(UniValue obj, int n) +{ + UniValue outputMapValue = find_value(obj, "outputmap"); + if (!outputMapValue.isArray()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Missing outputmap for JoinSplit operation"); + } + + 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) { + return i; + } + } + + throw std::logic_error("n is not present in outputmap"); +} + +AsyncRPCOperation_mergetoaddress::AsyncRPCOperation_mergetoaddress( + CMutableTransaction contextualTx, + std::vector utxoInputs, + std::vector noteInputs, + MergeToAddressRecipient recipient, + CAmount fee, + UniValue contextInfo) : + tx_(contextualTx), utxoInputs_(utxoInputs), noteInputs_(noteInputs), + recipient_(recipient), fee_(fee), contextinfo_(contextInfo) +{ + if (fee < 0 || fee > MAX_MONEY) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee is out of range"); + } + + if (utxoInputs.empty() && noteInputs.empty()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "No inputs"); + } + + if (std::get<0>(recipient).size() == 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Recipient parameter missing"); + } + + toTaddr_ = CBitcoinAddress(std::get<0>(recipient)); + isToTaddr_ = toTaddr_.IsValid(); + isToZaddr_ = false; + + if (!isToTaddr_) { + CZCPaymentAddress address(std::get<0>(recipient)); + try { + PaymentAddress addr = address.Get(); + + isToZaddr_ = true; + toPaymentAddress_ = addr; + } catch (const std::runtime_error& e) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("runtime error: ") + e.what()); + } + } + + // Log the context info i.e. the call parameters to z_mergetoaddress + if (LogAcceptCategory("zrpcunsafe")) { + LogPrint("zrpcunsafe", "%s: z_mergetoaddress initialized (params=%s)\n", getId(), contextInfo.write()); + } else { + LogPrint("zrpc", "%s: z_mergetoaddress initialized\n", getId()); + } + + // Lock UTXOs + lock_utxos(); + lock_notes(); + + // Enable payment disclosure if requested + paymentDisclosureMode = fExperimentalMode && GetBoolArg("-paymentdisclosure", false); +} + +AsyncRPCOperation_mergetoaddress::~AsyncRPCOperation_mergetoaddress() +{ +} + +void AsyncRPCOperation_mergetoaddress::main() +{ + if (isCancelled()) { + unlock_utxos(); // clean up + unlock_notes(); + return; + } + + set_state(OperationStatus::EXECUTING); + start_execution_clock(); + + bool success = false; + +#ifdef ENABLE_MINING +#ifdef ENABLE_WALLET + GenerateBitcoins(false, NULL, 0); +#else + GenerateBitcoins(false, 0); +#endif +#endif + + 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(); + set_error_code(code); + set_error_message(message); + } catch (const runtime_error& e) { + set_error_code(-1); + set_error_message("runtime error: " + string(e.what())); + } catch (const logic_error& e) { + set_error_code(-1); + set_error_message("logic error: " + string(e.what())); + } catch (const exception& e) { + set_error_code(-1); + set_error_message("general exception: " + string(e.what())); + } catch (...) { + set_error_code(-2); + set_error_message("unknown error"); + } + +#ifdef ENABLE_MINING +#ifdef ENABLE_WALLET + GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", 1)); +#else + GenerateBitcoins(GetBoolArg("-gen", false), GetArg("-genproclimit", 1)); +#endif +#endif + + stop_execution_clock(); + + if (success) { + set_state(OperationStatus::SUCCESS); + } else { + set_state(OperationStatus::FAILED); + } + + std::string s = strprintf("%s: z_mergetoaddress finished (status=%s", getId(), getStateAsString()); + if (success) { + s += strprintf(", txid=%s)\n", tx_.GetHash().ToString()); + } else { + s += strprintf(", error=%s)\n", getErrorMessage()); + } + LogPrintf("%s", s); + + unlock_utxos(); // clean up + unlock_notes(); // clean up + + // !!! Payment disclosure START + if (success && paymentDisclosureMode && paymentDisclosureData_.size() > 0) { + uint256 txidhash = tx_.GetHash(); + std::shared_ptr db = PaymentDisclosureDB::sharedInstance(); + 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()); + } else { + LogPrint("paymentdisclosure", "%s: Payment Disclosure: Successfully added entry to database for key %s\n", getId(), p.first.ToString()); + } + } + } + // !!! Payment disclosure END +} + +// Notes: +// 1. #1359 Currently there is no limit set on the number of joinsplits, so size of tx could be invalid. +// 2. #1277 Spendable notes are not locked, so an operation running in parallel could also try to use them. +bool AsyncRPCOperation_mergetoaddress::main_impl() +{ + assert(isToTaddr_ != isToZaddr_); + + bool isPureTaddrOnlyTx = (noteInputs_.empty() && isToTaddr_); + CAmount minersFee = fee_; + + size_t numInputs = utxoInputs_.size(); + + // Check mempooltxinputlimit to avoid creating a transaction which the local mempool rejects + size_t limit = (size_t)GetArg("-mempooltxinputlimit", 0); + if (limit > 0 && numInputs > limit) { + throw JSONRPCError(RPC_WALLET_ERROR, + strprintf("Number of transparent inputs %d is greater than mempooltxinputlimit of %d", + numInputs, limit)); + } + + CAmount t_inputs_total = 0; + for (MergeToAddressInputUTXO& t : utxoInputs_) { + t_inputs_total += std::get<1>(t); + } + + CAmount z_inputs_total = 0; + for (MergeToAddressInputNote& t : noteInputs_) { + z_inputs_total += std::get<2>(t); + } + + CAmount targetAmount = z_inputs_total + t_inputs_total; + + if (targetAmount <= minersFee) { + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, + strprintf("Insufficient funds, have %s and miners fee is %s", + FormatMoney(targetAmount), FormatMoney(minersFee))); + } + + CAmount sendAmount = targetAmount - minersFee; + + // update the transaction with the UTXO inputs and output (if any) + CMutableTransaction rawTx(tx_); + for (MergeToAddressInputUTXO& t : utxoInputs_) { + CTxIn in(std::get<0>(t)); + rawTx.vin.push_back(in); + } + if (isToTaddr_) { + CScript scriptPubKey = GetScriptForDestination(toTaddr_.Get()); + CTxOut out(sendAmount, scriptPubKey); + rawTx.vout.push_back(out); + } + tx_ = CTransaction(rawTx); + + LogPrint(isPureTaddrOnlyTx ? "zrpc" : "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)); + if (isToTaddr_) { + LogPrint("zrpc", "%s: transparent output: %s\n", getId(), FormatMoney(sendAmount)); + } else { + LogPrint("zrpcunsafe", "%s: private output: %s\n", getId(), FormatMoney(sendAmount)); + } + LogPrint("zrpc", "%s: fee: %s\n", getId(), FormatMoney(minersFee)); + + // Grab the current consensus branch ID + { + LOCK(cs_main); + consensusBranchId_ = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); + } + + /** + * SCENARIO #1 + * + * taddrs -> taddr + * + * There are no zaddrs or joinsplits involved. + */ + if (isPureTaddrOnlyTx) { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("rawtxn", EncodeHexTx(tx_))); + sign_send_raw_transaction(obj); + return true; + } + /** + * END SCENARIO #1 + */ + + + // Prepare raw transaction to handle JoinSplits + CMutableTransaction mtx(tx_); + crypto_sign_keypair(joinSplitPubKey_.begin(), joinSplitPrivKey_); + mtx.joinSplitPubKey = joinSplitPubKey_; + tx_ = CTransaction(mtx); + std::string hexMemo = std::get<1>(recipient_); + + + /** + * SCENARIO #2 + * + * taddrs -> zaddr + * + * We only need a single JoinSplit. + */ + if (noteInputs_.empty() && isToZaddr_) { + // Create JoinSplit to target z-addr. + MergeToAddressJSInfo info; + info.vpub_old = sendAmount; + info.vpub_new = 0; + + JSOutput jso = JSOutput(toPaymentAddress_, sendAmount); + if (hexMemo.size() > 0) { + jso.memo = get_memo_from_hex_string(hexMemo); + } + info.vjsout.push_back(jso); + + UniValue obj(UniValue::VOBJ); + obj = perform_joinsplit(info); + sign_send_raw_transaction(obj); + return true; + } + /** + * END SCENARIO #2 + */ + + + // Copy zinputs to more flexible containers + std::deque zInputsDeque; + for (auto o : noteInputs_) { + zInputsDeque.push_back(o); + } + + // When spending notes, take a snapshot of note witnesses and anchors as the treestate will + // change upon arrival of new blocks which contain joinsplit transactions. This is likely + // to happen as creating a chained joinsplit transaction can take longer than the block interval. + { + LOCK2(cs_main, pwalletMain->cs_wallet); + for (auto t : noteInputs_) { + JSOutPoint jso = std::get<0>(t); + std::vector vOutPoints = {jso}; + uint256 inputAnchor; + std::vector> vInputWitnesses; + pwalletMain->GetNoteWitnesses(vOutPoints, vInputWitnesses, inputAnchor); + jsopWitnessAnchorMap[jso.ToString()] = MergeToAddressWitnessAnchorData{vInputWitnesses[0], inputAnchor}; + } + } + + /** + * SCENARIO #3 + * + * zaddrs -> zaddr + * taddrs -> + * + * zaddrs -> + * taddrs -> taddr + * + * Send to zaddr by chaining JoinSplits together and immediately consuming any change + * Send to taddr by creating dummy z outputs and accumulating value in a change note + * which is used to set vpub_new in the last chained joinsplit. + */ + UniValue obj(UniValue::VOBJ); + CAmount jsChange = 0; // this is updated after each joinsplit + int changeOutputIndex = -1; // this is updated after each joinsplit if jsChange > 0 + bool vpubOldProcessed = false; // updated when vpub_old for taddr inputs is set in first joinsplit + bool vpubNewProcessed = false; // updated when vpub_new for miner fee and taddr outputs is set in last joinsplit + + // At this point, we are guaranteed to have at least one input note. + // Use address of first input note as the temporary change address. + SpendingKey changeKey = std::get<3>(zInputsDeque.front()); + PaymentAddress changeAddress = changeKey.address(); + + CAmount vpubOldTarget = 0; + CAmount vpubNewTarget = 0; + if (isToTaddr_) { + vpubNewTarget = z_inputs_total; + } else { + if (utxoInputs_.empty()) { + vpubNewTarget = minersFee; + } else { + vpubOldTarget = t_inputs_total - minersFee; + } + } + + // Keep track of treestate within this transaction + boost::unordered_map intermediates; + std::vector previousCommitments; + + while (!vpubNewProcessed) { + MergeToAddressJSInfo info; + info.vpub_old = 0; + info.vpub_new = 0; + + // Set vpub_old in the first joinsplit + if (!vpubOldProcessed) { + if (t_inputs_total < vpubOldTarget) { + throw JSONRPCError(RPC_WALLET_ERROR, + strprintf("Insufficient transparent funds for vpub_old %s (miners fee %s, taddr inputs %s)", + FormatMoney(vpubOldTarget), FormatMoney(minersFee), FormatMoney(t_inputs_total))); + } + info.vpub_old += vpubOldTarget; // funds flowing from public pool + vpubOldProcessed = true; + } + + CAmount jsInputValue = 0; + uint256 jsAnchor; + std::vector> witnesses; + + JSDescription prevJoinSplit; + + // Keep track of previous JoinSplit and its commitments + if (tx_.vjoinsplit.size() > 0) { + prevJoinSplit = tx_.vjoinsplit.back(); + } + + // If there is no change, the chain has terminated so we can reset the tracked treestate. + if (jsChange == 0 && tx_.vjoinsplit.size() > 0) { + intermediates.clear(); + previousCommitments.clear(); + } + + // + // Consume change as the first input of the JoinSplit. + // + if (jsChange > 0) { + LOCK2(cs_main, pwalletMain->cs_wallet); + + // Update tree state with previous joinsplit + ZCIncrementalMerkleTree tree; + auto it = intermediates.find(prevJoinSplit.anchor); + if (it != intermediates.end()) { + tree = it->second; + } else if (!pcoinsTip->GetAnchorAt(prevJoinSplit.anchor, tree)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Could not find previous JoinSplit anchor"); + } + + assert(changeOutputIndex != -1); + boost::optional changeWitness; + int n = 0; + for (const uint256& commitment : prevJoinSplit.commitments) { + tree.append(commitment); + previousCommitments.push_back(commitment); + if (!changeWitness && changeOutputIndex == n++) { + changeWitness = tree.witness(); + } else if (changeWitness) { + changeWitness.get().append(commitment); + } + } + if (changeWitness) { + witnesses.push_back(changeWitness); + } + jsAnchor = tree.root(); + intermediates.insert(std::make_pair(tree.root(), tree)); // chained js are interstitial (found in between block boundaries) + + // 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); + try { + NotePlaintext plaintext = NotePlaintext::decrypt( + decryptor, + prevJoinSplit.ciphertexts[changeOutputIndex], + prevJoinSplit.ephemeralKey, + hSig, + (unsigned char)changeOutputIndex); + + Note note = plaintext.note(changeAddress); + info.notes.push_back(note); + info.zkeys.push_back(changeKey); + + jsInputValue += plaintext.value; + + LogPrint("zrpcunsafe", "%s: spending change (amount=%s)\n", + getId(), + FormatMoney(plaintext.value)); + + } catch (const std::exception& e) { + throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Error decrypting output note of previous JoinSplit: %s", e.what())); + } + } + + + // + // Consume spendable non-change notes + // + std::vector vInputNotes; + std::vector vInputZKeys; + std::vector vOutPoints; + std::vector> vInputWitnesses; + uint256 inputAnchor; + int numInputsNeeded = (jsChange > 0) ? 1 : 0; + while (numInputsNeeded++ < ZC_NUM_JS_INPUTS && zInputsDeque.size() > 0) { + MergeToAddressInputNote t = zInputsDeque.front(); + JSOutPoint jso = std::get<0>(t); + Note note = std::get<1>(t); + CAmount noteFunds = std::get<2>(t); + SpendingKey zkey = std::get<3>(t); + zInputsDeque.pop_front(); + + MergeToAddressWitnessAnchorData wad = jsopWitnessAnchorMap[jso.ToString()]; + vInputWitnesses.push_back(wad.witness); + if (inputAnchor.IsNull()) { + inputAnchor = wad.anchor; + } else if (inputAnchor != wad.anchor) { + throw JSONRPCError(RPC_WALLET_ERROR, "Selected input notes do not share the same anchor"); + } + + vOutPoints.push_back(jso); + vInputNotes.push_back(note); + vInputZKeys.push_back(zkey); + + jsInputValue += noteFunds; + + int wtxHeight = -1; + int wtxDepth = -1; + { + LOCK2(cs_main, pwalletMain->cs_wallet); + const CWalletTx& wtx = pwalletMain->mapWallet[jso.hash]; + // Zero confirmation notes belong to transactions which have not yet been mined + if (mapBlockIndex.find(wtx.hashBlock) == mapBlockIndex.end()) { + throw JSONRPCError(RPC_WALLET_ERROR, strprintf("mapBlockIndex does not contain block hash %s", wtx.hashBlock.ToString())); + } + wtxHeight = mapBlockIndex[wtx.hashBlock]->nHeight; + wtxDepth = wtx.GetDepthInMainChain(); + } + LogPrint("zrpcunsafe", "%s: spending note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, height=%d, confirmations=%d)\n", + getId(), + jso.hash.ToString().substr(0, 10), + jso.js, + int(jso.n), // uint8_t + FormatMoney(noteFunds), + wtxHeight, + wtxDepth); + } + + // Add history of previous commitments to witness + if (vInputNotes.size() > 0) { + if (vInputWitnesses.size() == 0) { + throw JSONRPCError(RPC_WALLET_ERROR, "Could not find witness for note commitment"); + } + + for (auto& optionalWitness : vInputWitnesses) { + if (!optionalWitness) { + throw JSONRPCError(RPC_WALLET_ERROR, "Witness for note commitment is null"); + } + ZCIncrementalWitness w = *optionalWitness; // could use .get(); + if (jsChange > 0) { + for (const uint256& commitment : previousCommitments) { + w.append(commitment); + } + if (jsAnchor != w.root()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Witness for spendable note does not have same anchor as change input"); + } + } + witnesses.push_back(w); + } + + // The jsAnchor is null if this JoinSplit is at the start of a new chain + if (jsAnchor.IsNull()) { + jsAnchor = inputAnchor; + } + + // Add spendable notes as inputs + std::copy(vInputNotes.begin(), vInputNotes.end(), std::back_inserter(info.notes)); + std::copy(vInputZKeys.begin(), vInputZKeys.end(), std::back_inserter(info.zkeys)); + } + + // Accumulate change + jsChange = jsInputValue + info.vpub_old; + + // Set vpub_new in the last joinsplit (when there are no more notes to spend) + if (zInputsDeque.empty()) { + assert(!vpubNewProcessed); + if (jsInputValue < vpubNewTarget) { + throw JSONRPCError(RPC_WALLET_ERROR, + strprintf("Insufficient funds for vpub_new %s (miners fee %s, taddr inputs %s)", + FormatMoney(vpubNewTarget), FormatMoney(minersFee), FormatMoney(t_inputs_total))); + } + info.vpub_new += vpubNewTarget; // funds flowing back to public pool + vpubNewProcessed = true; + jsChange -= vpubNewTarget; + // If we are merging to a t-addr, there should be no change + if (isToTaddr_) assert(jsChange == 0); + } + + // create dummy output + info.vjsout.push_back(JSOutput()); // dummy output while we accumulate funds into a change note for vpub_new + + // create output for any change + if (jsChange > 0) { + std::string outputType = "change"; + auto jso = JSOutput(changeAddress, jsChange); + // If this is the final output, set the target and memo + if (isToZaddr_ && vpubNewProcessed) { + outputType = "target"; + jso.addr = toPaymentAddress_; + if (!hexMemo.empty()) { + jso.memo = get_memo_from_hex_string(hexMemo); + } + } + info.vjsout.push_back(jso); + + LogPrint("zrpcunsafe", "%s: generating note for %s (amount=%s)\n", + getId(), + outputType, + FormatMoney(jsChange)); + } + + obj = perform_joinsplit(info, witnesses, jsAnchor); + + if (jsChange > 0) { + changeOutputIndex = mta_find_output(obj, 1); + } + } + + // Sanity check in case changes to code block above exits loop by invoking 'break' + assert(zInputsDeque.size() == 0); + assert(vpubNewProcessed); + + sign_send_raw_transaction(obj); + return true; +} + + +/** + * Sign and send a raw transaction. + * Raw transaction as hex string should be in object field "rawtxn" + */ +void AsyncRPCOperation_mergetoaddress::sign_send_raw_transaction(UniValue obj) +{ + // Sign the raw transaction + UniValue rawtxnValue = find_value(obj, "rawtxn"); + if (rawtxnValue.isNull()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Missing hex data for raw transaction"); + } + std::string rawtxn = rawtxnValue.get_str(); + + UniValue params = UniValue(UniValue::VARR); + params.push_back(rawtxn); + UniValue signResultValue = signrawtransaction(params, false); + UniValue signResultObject = signResultValue.get_obj(); + UniValue completeValue = find_value(signResultObject, "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"); + if (hexValue.isNull()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Missing hex data for signed transaction"); + } + std::string signedtxn = hexValue.get_str(); + + // Send the signed transaction + if (!testmode) { + params.clear(); + params.setArray(); + params.push_back(signedtxn); + UniValue sendResultValue = sendrawtransaction(params, false); + if (sendResultValue.isNull()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Send raw transaction did not return an error or a txid."); + } + + std::string txid = sendResultValue.get_str(); + + UniValue o(UniValue::VOBJ); + o.push_back(Pair("txid", txid)); + set_result(o); + } else { + // Test mode does not send the transaction to the network. + + CDataStream stream(ParseHex(signedtxn), SER_NETWORK, PROTOCOL_VERSION); + CTransaction tx; + stream >> tx; + + UniValue o(UniValue::VOBJ); + o.push_back(Pair("test", 1)); + o.push_back(Pair("txid", tx.GetHash().ToString())); + o.push_back(Pair("hex", signedtxn)); + set_result(o); + } + + // Keep the signed transaction so we can hash to the same txid + CDataStream stream(ParseHex(signedtxn), SER_NETWORK, PROTOCOL_VERSION); + CTransaction tx; + stream >> tx; + tx_ = tx; +} + + +UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(MergeToAddressJSInfo& info) +{ + std::vector> witnesses; + uint256 anchor; + { + LOCK(cs_main); + anchor = pcoinsTip->GetBestAnchor(); // As there are no inputs, ask the wallet for the best anchor + } + return perform_joinsplit(info, witnesses, anchor); +} + + +UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(MergeToAddressJSInfo& info, std::vector& outPoints) +{ + std::vector> witnesses; + uint256 anchor; + { + LOCK(cs_main); + pwalletMain->GetNoteWitnesses(outPoints, witnesses, anchor); + } + return perform_joinsplit(info, witnesses, anchor); +} + +UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit( + MergeToAddressJSInfo& info, + std::vector> witnesses, + uint256 anchor) +{ + if (anchor.IsNull()) { + throw std::runtime_error("anchor is null"); + } + + if (witnesses.size() != info.notes.size()) { + throw runtime_error("number of notes and witnesses do not match"); + } + + if (info.notes.size() != info.zkeys.size()) { + throw runtime_error("number of notes and spending keys do not match"); + } + + for (size_t i = 0; i < witnesses.size(); i++) { + if (!witnesses[i]) { + throw runtime_error("joinsplit input could not be found in tree"); + } + info.vjsin.push_back(JSInput(*witnesses[i], info.notes[i], info.zkeys[i])); + } + + // Make sure there are two inputs and two outputs + while (info.vjsin.size() < ZC_NUM_JS_INPUTS) { + info.vjsin.push_back(JSInput()); + } + + while (info.vjsout.size() < ZC_NUM_JS_OUTPUTS) { + info.vjsout.push_back(JSOutput()); + } + + if (info.vjsout.size() != ZC_NUM_JS_INPUTS || info.vjsin.size() != ZC_NUM_JS_OUTPUTS) { + throw runtime_error("unsupported joinsplit input/output counts"); + } + + 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", + getId(), + tx_.vjoinsplit.size(), + FormatMoney(info.vpub_old), FormatMoney(info.vpub_new), + FormatMoney(info.vjsin[0].note.value), FormatMoney(info.vjsin[1].note.value), + FormatMoney(info.vjsout[0].value), FormatMoney(info.vjsout[1].value)); + + // Generate the proof, this can take over a minute. + boost::array inputs{info.vjsin[0], info.vjsin[1]}; + boost::array outputs{info.vjsout[0], info.vjsout[1]}; + boost::array inputMap; + boost::array outputMap; + + uint256 esk; // payment disclosure - secret + + JSDescription jsdesc = JSDescription::Randomized( + *pzcashParams, + joinSplitPubKey_, + anchor, + inputs, + outputs, + inputMap, + outputMap, + info.vpub_old, + info.vpub_new, + !this->testmode, + &esk); // parameter expects pointer to esk, so pass in address + { + auto verifier = libzcash::ProofVerifier::Strict(); + if (!(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey_))) { + throw std::runtime_error("error verifying joinsplit"); + } + } + + mtx.vjoinsplit.push_back(jsdesc); + + // Empty output script. + CScript scriptCode; + CTransaction signTx(mtx); + uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId_); + + // Add the signature + if (!(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, + dataToBeSigned.begin(), 32, + joinSplitPrivKey_) == 0)) { + throw std::runtime_error("crypto_sign_detached failed"); + } + + // Sanity check + if (!(crypto_sign_verify_detached(&mtx.joinSplitSig[0], + dataToBeSigned.begin(), 32, + mtx.joinSplitPubKey.begin()) == 0)) { + throw std::runtime_error("crypto_sign_verify_detached failed"); + } + + CTransaction rawTx(mtx); + tx_ = rawTx; + + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << rawTx; + + std::string encryptedNote1; + std::string encryptedNote2; + { + CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); + ss2 << ((unsigned char)0x00); + ss2 << jsdesc.ephemeralKey; + ss2 << jsdesc.ciphertexts[0]; + ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_); + + encryptedNote1 = HexStr(ss2.begin(), ss2.end()); + } + { + CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); + ss2 << ((unsigned char)0x01); + ss2 << jsdesc.ephemeralKey; + ss2 << jsdesc.ciphertexts[1]; + ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_); + + encryptedNote2 = HexStr(ss2.begin(), ss2.end()); + } + + UniValue arrInputMap(UniValue::VARR); + UniValue arrOutputMap(UniValue::VARR); + for (size_t i = 0; i < ZC_NUM_JS_INPUTS; i++) { + arrInputMap.push_back(inputMap[i]); + } + for (size_t i = 0; i < ZC_NUM_JS_OUTPUTS; i++) { + arrOutputMap.push_back(outputMap[i]); + } + + + // !!! Payment disclosure START + unsigned char buffer[32] = {0}; + memcpy(&buffer[0], &joinSplitPrivKey_[0], 32); // private key in first half of 64 byte buffer + std::vector vch(&buffer[0], &buffer[0] + 32); + uint256 joinSplitPrivKey = uint256(vch); + size_t js_index = tx_.vjoinsplit.size() - 1; + uint256 placeholder; + for (int i = 0; i < ZC_NUM_JS_OUTPUTS; i++) { + uint8_t mapped_index = outputMap[i]; + // placeholder for txid will be filled in later when tx has been finalized and signed. + PaymentDisclosureKey pdKey = {placeholder, js_index, mapped_index}; + JSOutput output = outputs[mapped_index]; + libzcash::PaymentAddress zaddr = output.addr; // randomized output + PaymentDisclosureInfo pdInfo = {PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL, esk, joinSplitPrivKey, zaddr}; + paymentDisclosureData_.push_back(PaymentDisclosureKeyInfo(pdKey, pdInfo)); + + CZCPaymentAddress address(zaddr); + LogPrint("paymentdisclosure", "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), address.ToString()); + } + // !!! 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)); + return obj; +} + +boost::array AsyncRPCOperation_mergetoaddress::get_memo_from_hex_string(std::string s) +{ + boost::array memo = {{0x00}}; + + std::vector rawMemo = ParseHex(s.c_str()); + + // If ParseHex comes across a non-hex char, it will stop but still return results so far. + size_t slen = s.length(); + if (slen % 2 != 0 || (slen > 0 && rawMemo.size() != slen / 2)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo must be in hexadecimal format"); + } + + if (rawMemo.size() > ZC_MEMO_SIZE) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Memo size of %d is too big, maximum allowed is %d", rawMemo.size(), ZC_MEMO_SIZE)); + } + + // copy vector into boost array + int lenMemo = rawMemo.size(); + for (int i = 0; i < ZC_MEMO_SIZE && i < lenMemo; i++) { + memo[i] = rawMemo[i]; + } + return memo; +} + +/** + * Override getStatus() to append the operation's input parameters to the default status object. + */ +UniValue AsyncRPCOperation_mergetoaddress::getStatus() const +{ + UniValue v = AsyncRPCOperation::getStatus(); + if (contextinfo_.isNull()) { + return v; + } + + UniValue obj = v.get_obj(); + obj.push_back(Pair("method", "z_mergetoaddress")); + obj.push_back(Pair("params", contextinfo_)); + return obj; +} + +/** + * Lock input utxos + */ + void AsyncRPCOperation_mergetoaddress::lock_utxos() { + LOCK2(cs_main, pwalletMain->cs_wallet); + for (auto utxo : utxoInputs_) { + pwalletMain->LockCoin(std::get<0>(utxo)); + } +} + +/** + * Unlock input utxos + */ +void AsyncRPCOperation_mergetoaddress::unlock_utxos() { + LOCK2(cs_main, pwalletMain->cs_wallet); + for (auto utxo : utxoInputs_) { + pwalletMain->UnlockCoin(std::get<0>(utxo)); + } +} + + +/** + * Lock input notes + */ + void AsyncRPCOperation_mergetoaddress::lock_notes() { + LOCK2(cs_main, pwalletMain->cs_wallet); + for (auto note : noteInputs_) { + pwalletMain->LockNote(std::get<0>(note)); + } +} + +/** + * Unlock input notes + */ +void AsyncRPCOperation_mergetoaddress::unlock_notes() { + LOCK2(cs_main, pwalletMain->cs_wallet); + for (auto note : noteInputs_) { + pwalletMain->UnlockNote(std::get<0>(note)); + } +} diff --git a/src/wallet/asyncrpcoperation_mergetoaddress.h b/src/wallet/asyncrpcoperation_mergetoaddress.h new file mode 100644 index 000000000..34548a5ba --- /dev/null +++ b/src/wallet/asyncrpcoperation_mergetoaddress.h @@ -0,0 +1,193 @@ +// Copyright (c) 2017 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef ASYNCRPCOPERATION_MERGETOADDRESS_H +#define ASYNCRPCOPERATION_MERGETOADDRESS_H + +#include "amount.h" +#include "asyncrpcoperation.h" +#include "base58.h" +#include "paymentdisclosure.h" +#include "primitives/transaction.h" +#include "wallet.h" +#include "zcash/Address.hpp" +#include "zcash/JoinSplit.hpp" + +#include +#include + +#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 +typedef std::tuple MergeToAddressInputUTXO; + +// Input JSOP is a tuple of JSOutpoint, note, amount, spending key +typedef std::tuple MergeToAddressInputNote; + +// A recipient is a tuple of address, memo (optional if zaddr) +typedef std::tuple MergeToAddressRecipient; + +// Package of info which is passed to perform_joinsplit methods. +struct MergeToAddressJSInfo { + std::vector vjsin; + std::vector vjsout; + std::vector notes; + std::vector zkeys; + CAmount vpub_old = 0; + CAmount vpub_new = 0; +}; + +// A struct to help us track the witness and anchor for a given JSOutPoint +struct MergeToAddressWitnessAnchorData { + boost::optional witness; + uint256 anchor; +}; + +class AsyncRPCOperation_mergetoaddress : public AsyncRPCOperation +{ +public: + AsyncRPCOperation_mergetoaddress( + CMutableTransaction contextualTx, + std::vector utxoInputs, + std::vector noteInputs, + MergeToAddressRecipient recipient, + CAmount fee = MERGE_TO_ADDRESS_OPERATION_DEFAULT_MINERS_FEE, + UniValue contextInfo = NullUniValue); + virtual ~AsyncRPCOperation_mergetoaddress(); + + // We don't want to be copied or moved around + AsyncRPCOperation_mergetoaddress(AsyncRPCOperation_mergetoaddress const&) = delete; // Copy construct + AsyncRPCOperation_mergetoaddress(AsyncRPCOperation_mergetoaddress&&) = delete; // Move construct + AsyncRPCOperation_mergetoaddress& operator=(AsyncRPCOperation_mergetoaddress const&) = delete; // Copy assign + AsyncRPCOperation_mergetoaddress& operator=(AsyncRPCOperation_mergetoaddress&&) = delete; // Move assign + + virtual void main(); + + virtual UniValue getStatus() const; + + bool testmode = false; // Set to true to disable sending txs and generating proofs + + bool paymentDisclosureMode = false; // Set to true to save esk for encrypted notes in payment disclosure database. + +private: + friend class TEST_FRIEND_AsyncRPCOperation_mergetoaddress; // class for unit testing + + UniValue contextinfo_; // optional data to include in return value from getStatus() + + uint32_t consensusBranchId_; + CAmount fee_; + int mindepth_; + MergeToAddressRecipient recipient_; + bool isToTaddr_; + bool isToZaddr_; + CBitcoinAddress toTaddr_; + PaymentAddress toPaymentAddress_; + + uint256 joinSplitPubKey_; + unsigned char joinSplitPrivKey_[crypto_sign_SECRETKEYBYTES]; + + // The key is the result string from calling JSOutPoint::ToString() + std::unordered_map jsopWitnessAnchorMap; + + std::vector utxoInputs_; + std::vector noteInputs_; + + CTransaction tx_; + + boost::array get_memo_from_hex_string(std::string s); + bool main_impl(); + + // JoinSplit without any input notes to spend + UniValue perform_joinsplit(MergeToAddressJSInfo&); + + // JoinSplit with input notes to spend (JSOutPoints)) + UniValue perform_joinsplit(MergeToAddressJSInfo&, std::vector&); + + // JoinSplit where you have the witnesses and anchor + UniValue perform_joinsplit( + MergeToAddressJSInfo& info, + std::vector> witnesses, + uint256 anchor); + + void sign_send_raw_transaction(UniValue obj); // throws exception if there was an error + + void lock_utxos(); + + void unlock_utxos(); + + void lock_notes(); + + void unlock_notes(); + + // payment disclosure! + std::vector paymentDisclosureData_; +}; + + +// To test private methods, a friend class can act as a proxy +class TEST_FRIEND_AsyncRPCOperation_mergetoaddress +{ +public: + std::shared_ptr delegate; + + TEST_FRIEND_AsyncRPCOperation_mergetoaddress(std::shared_ptr ptr) : delegate(ptr) {} + + CTransaction getTx() + { + return delegate->tx_; + } + + void setTx(CTransaction tx) + { + delegate->tx_ = tx; + } + + // Delegated methods + + boost::array get_memo_from_hex_string(std::string s) + { + return delegate->get_memo_from_hex_string(s); + } + + bool main_impl() + { + return delegate->main_impl(); + } + + UniValue perform_joinsplit(MergeToAddressJSInfo& info) + { + return delegate->perform_joinsplit(info); + } + + UniValue perform_joinsplit(MergeToAddressJSInfo& info, std::vector& v) + { + return delegate->perform_joinsplit(info, v); + } + + UniValue perform_joinsplit( + MergeToAddressJSInfo& info, + std::vector> witnesses, + uint256 anchor) + { + return delegate->perform_joinsplit(info, witnesses, anchor); + } + + void sign_send_raw_transaction(UniValue obj) + { + delegate->sign_send_raw_transaction(obj); + } + + void set_state(OperationStatus state) + { + delegate->state_.store(state); + } +}; + + +#endif /* ASYNCRPCOPERATION_MERGETOADDRESS_H */ diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index 4e8a20be6..6f33b514e 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -5,6 +5,7 @@ #include "asyncrpcoperation_sendmany.h" #include "asyncrpcqueue.h" #include "amount.h" +#include "consensus/upgrades.h" #include "core_io.h" #include "init.h" #include "main.h" @@ -30,6 +31,8 @@ #include #include +#include "paymentdisclosuredb.h" + using namespace libzcash; int find_output(UniValue obj, int n) { @@ -50,13 +53,14 @@ int find_output(UniValue obj, int n) { } AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany( + CMutableTransaction contextualTx, std::string fromAddress, std::vector tOutputs, std::vector zOutputs, int minDepth, CAmount fee, UniValue contextInfo) : - fromaddress_(fromAddress), t_outputs_(tOutputs), z_outputs_(zOutputs), mindepth_(minDepth), fee_(fee), contextinfo_(contextInfo) + tx_(contextualTx), fromaddress_(fromAddress), t_outputs_(tOutputs), z_outputs_(zOutputs), mindepth_(minDepth), fee_(fee), contextinfo_(contextInfo) { assert(fee_ >= 0); @@ -95,12 +99,20 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany( } } + if (isfromzaddr_ && minDepth==0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Minconf cannot be zero when sending from zaddr"); + } + // 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()); } else { LogPrint("zrpc", "%s: z_sendmany initialized\n", getId()); } + + + // Enable payment disclosure if requested + paymentDisclosureMode = fExperimentalMode && GetBoolArg("-paymentdisclosure", false); } AsyncRPCOperation_sendmany::~AsyncRPCOperation_sendmany() { @@ -167,6 +179,21 @@ void AsyncRPCOperation_sendmany::main() { s += strprintf(", error=%s)\n", getErrorMessage()); } LogPrintf("%s",s); + + // !!! Payment disclosure START + if (success && paymentDisclosureMode && paymentDisclosureData_.size()>0) { + uint256 txidhash = tx_.GetHash(); + std::shared_ptr db = PaymentDisclosureDB::sharedInstance(); + 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()); + } else { + LogPrint("paymentdisclosure", "%s: Payment Disclosure: Successfully added entry to database for key %s\n", getId(), p.first.ToString()); + } + } + } + // !!! Payment disclosure END } // Notes: @@ -283,6 +310,15 @@ bool AsyncRPCOperation_sendmany::main_impl() { t_inputs_ = selectedTInputs; t_inputs_total = selectedUTXOAmount; + // Check mempooltxinputlimit to avoid creating a transaction which the local mempool rejects + size_t limit = (size_t)GetArg("-mempooltxinputlimit", 0); + if (limit > 0) { + size_t n = t_inputs_.size(); + if (n > limit) { + throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Too many transparent inputs %zu > limit %zu", n, limit)); + } + } + // update the transaction with these inputs CMutableTransaction rawTx(tx_); for (SendManyInputUTXO & t : t_inputs_) { @@ -304,6 +340,12 @@ bool AsyncRPCOperation_sendmany::main_impl() { LogPrint("zrpcunsafe", "%s: private output: %s\n", getId(), FormatMoney(z_outputs_total)); LogPrint("zrpc", "%s: fee: %s\n", getId(), FormatMoney(minersFee)); + // Grab the current consensus branch ID + { + LOCK(cs_main); + consensusBranchId_ = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); + } + /** * SCENARIO #1 * @@ -339,16 +381,20 @@ bool AsyncRPCOperation_sendmany::main_impl() { // Prepare raw transaction to handle JoinSplits CMutableTransaction mtx(tx_); - mtx.nVersion = 2; crypto_sign_keypair(joinSplitPubKey_.begin(), joinSplitPrivKey_); mtx.joinSplitPubKey = joinSplitPubKey_; mtx.nLockTime = (uint32_t)time(NULL) - 60; // jl777 tx_ = CTransaction(mtx); // Copy zinputs and zoutputs to more flexible containers - std::deque zInputsDeque; + std::deque zInputsDeque; // zInputsDeque stores minimum numbers of notes for target amount + CAmount tmp = 0; for (auto o : z_inputs_) { zInputsDeque.push_back(o); + tmp += std::get<2>(o); + if (tmp >= targetAmount) { + break; + } } std::deque zOutputsDeque; for (auto o : z_outputs_) { @@ -446,283 +492,220 @@ bool AsyncRPCOperation_sendmany::main_impl() { * zaddr -> taddrs * -> zaddrs * - * Processing order: - * Part 1: taddrs and miners fee - * Part 2: zaddrs - */ - - /** - * SCENARIO #3 - * Part 1: Add to the transparent value pool. + * Send to zaddrs by chaining JoinSplits together and immediately consuming any change + * Send to taddrs by creating dummy z outputs and accumulating value in a change note + * which is used to set vpub_new in the last chained joinsplit. */ UniValue obj(UniValue::VOBJ); CAmount jsChange = 0; // this is updated after each joinsplit int changeOutputIndex = -1; // this is updated after each joinsplit if jsChange > 0 - bool minersFeeProcessed = false; - + bool vpubNewProcessed = false; // updated when vpub_new for miner fee and taddr outputs is set in last joinsplit + CAmount vpubNewTarget = minersFee; if (t_outputs_total > 0) { add_taddr_outputs_to_tx(); - CAmount taddrTargetAmount = t_outputs_total + minersFee; - minersFeeProcessed = true; - while (zInputsDeque.size() > 0 && taddrTargetAmount > 0) { - AsyncJoinSplitInfo info; - info.vpub_old = 0; - info.vpub_new = 0; - std::vector outPoints; - int n = 0; - while (n++ < ZC_NUM_JS_INPUTS && taddrTargetAmount > 0) { - SendManyInputJSOP o = zInputsDeque.front(); - JSOutPoint outPoint = std::get<0>(o); - Note note = std::get<1>(o); - CAmount noteFunds = std::get<2>(o); - zInputsDeque.pop_front(); - - info.notes.push_back(note); - outPoints.push_back(outPoint); - - int wtxHeight = -1; - int wtxDepth = -1; - { - LOCK2(cs_main, pwalletMain->cs_wallet); - const CWalletTx& wtx = pwalletMain->mapWallet[outPoint.hash]; - wtxHeight = mapBlockIndex[wtx.hashBlock]->nHeight; - wtxDepth = wtx.GetDepthInMainChain(); - } - LogPrint("zrpcunsafe", "%s: spending note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, height=%d, confirmations=%d)\n", - getId(), - outPoint.hash.ToString().substr(0, 10), - outPoint.js, - int(outPoint.n), // uint8_t - FormatMoney(noteFunds), - wtxHeight, - wtxDepth - ); - - - // Put value back into the value pool - if (noteFunds >= taddrTargetAmount) { - jsChange = noteFunds - taddrTargetAmount; - info.vpub_new += taddrTargetAmount; - } else { - info.vpub_new += noteFunds; - } - - taddrTargetAmount -= noteFunds; - if (taddrTargetAmount <= 0) { - break; - } - } - - if (jsChange > 0) { - info.vjsout.push_back(JSOutput()); - info.vjsout.push_back(JSOutput(frompaymentaddress_, jsChange)); - - LogPrint("zrpcunsafe", "%s: generating note for change (amount=%s)\n", - getId(), - FormatMoney(jsChange) - ); - } - - obj = perform_joinsplit(info, outPoints); - - if (jsChange > 0) { - changeOutputIndex = find_output(obj, 1); - } - } + vpubNewTarget += t_outputs_total; } + // Keep track of treestate within this transaction + boost::unordered_map intermediates; + std::vector previousCommitments; - /** - * SCENARIO #3 - * Part 2: Send to zaddrs by chaining JoinSplits together and immediately consuming any change - */ - if (z_outputs_total>0) { + while (!vpubNewProcessed) { + AsyncJoinSplitInfo info; + info.vpub_old = 0; + info.vpub_new = 0; - // Keep track of treestate within this transaction - boost::unordered_map intermediates; - std::vector previousCommitments; + CAmount jsInputValue = 0; + uint256 jsAnchor; + std::vector> witnesses; - while (zOutputsDeque.size() > 0) { - AsyncJoinSplitInfo info; - info.vpub_old = 0; - info.vpub_new = 0; + JSDescription prevJoinSplit; - CAmount jsInputValue = 0; - uint256 jsAnchor; - std::vector> witnesses; + // Keep track of previous JoinSplit and its commitments + if (tx_.vjoinsplit.size() > 0) { + prevJoinSplit = tx_.vjoinsplit.back(); + } - JSDescription prevJoinSplit; + // If there is no change, the chain has terminated so we can reset the tracked treestate. + if (jsChange==0 && tx_.vjoinsplit.size() > 0) { + intermediates.clear(); + previousCommitments.clear(); + } - // Keep track of previous JoinSplit and its commitments - if (tx_.vjoinsplit.size() > 0) { - prevJoinSplit = tx_.vjoinsplit.back(); + // + // Consume change as the first input of the JoinSplit. + // + if (jsChange > 0) { + LOCK2(cs_main, pwalletMain->cs_wallet); + + // Update tree state with previous joinsplit + ZCIncrementalMerkleTree tree; + auto it = intermediates.find(prevJoinSplit.anchor); + if (it != intermediates.end()) { + tree = it->second; + } else if (!pcoinsTip->GetAnchorAt(prevJoinSplit.anchor, tree)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Could not find previous JoinSplit anchor"); } - // If there is no change, the chain has terminated so we can reset the tracked treestate. - if (jsChange==0 && tx_.vjoinsplit.size() > 0) { - intermediates.clear(); - previousCommitments.clear(); + assert(changeOutputIndex != -1); + boost::optional changeWitness; + int n = 0; + for (const uint256& commitment : prevJoinSplit.commitments) { + tree.append(commitment); + previousCommitments.push_back(commitment); + if (!changeWitness && changeOutputIndex == n++) { + changeWitness = tree.witness(); + } else if (changeWitness) { + changeWitness.get().append(commitment); + } } + if (changeWitness) { + witnesses.push_back(changeWitness); + } + jsAnchor = tree.root(); + intermediates.insert(std::make_pair(tree.root(), tree)); // chained js are interstitial (found in between block boundaries) + + // Decrypt the change note's ciphertext to retrieve some data we need + ZCNoteDecryption decryptor(spendingkey_.receiving_key()); + auto hSig = prevJoinSplit.h_sig(*pzcashParams, tx_.joinSplitPubKey); + try { + NotePlaintext plaintext = NotePlaintext::decrypt( + decryptor, + prevJoinSplit.ciphertexts[changeOutputIndex], + prevJoinSplit.ephemeralKey, + hSig, + (unsigned char) changeOutputIndex); + + Note note = plaintext.note(frompaymentaddress_); + info.notes.push_back(note); - // - // Consume change as the first input of the JoinSplit. - // - if (jsChange > 0) { - LOCK2(cs_main, pwalletMain->cs_wallet); - - // Update tree state with previous joinsplit - ZCIncrementalMerkleTree tree; - auto it = intermediates.find(prevJoinSplit.anchor); - if (it != intermediates.end()) { - tree = it->second; - } else if (!pcoinsTip->GetAnchorAt(prevJoinSplit.anchor, tree)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Could not find previous JoinSplit anchor"); - } + jsInputValue += plaintext.value; - assert(changeOutputIndex != -1); - boost::optional changeWitness; - int n = 0; - for (const uint256& commitment : prevJoinSplit.commitments) { - tree.append(commitment); - previousCommitments.push_back(commitment); - if (!changeWitness && changeOutputIndex == n++) { - changeWitness = tree.witness(); - } else if (changeWitness) { - changeWitness.get().append(commitment); - } - } - if (changeWitness) { - witnesses.push_back(changeWitness); - } - jsAnchor = tree.root(); - intermediates.insert(std::make_pair(tree.root(), tree)); // chained js are interstitial (found in between block boundaries) - - // Decrypt the change note's ciphertext to retrieve some data we need - ZCNoteDecryption decryptor(spendingkey_.viewing_key()); - auto hSig = prevJoinSplit.h_sig(*pzcashParams, tx_.joinSplitPubKey); - try { - NotePlaintext plaintext = NotePlaintext::decrypt( - decryptor, - prevJoinSplit.ciphertexts[changeOutputIndex], - prevJoinSplit.ephemeralKey, - hSig, - (unsigned char) changeOutputIndex); - - Note note = plaintext.note(frompaymentaddress_); - info.notes.push_back(note); - - jsInputValue += plaintext.value; - - LogPrint("zrpcunsafe", "%s: spending change (amount=%s)\n", - getId(), - FormatMoney(plaintext.value) - ); + LogPrint("zrpcunsafe", "%s: spending change (amount=%s)\n", + getId(), + FormatMoney(plaintext.value) + ); - } catch (const std::exception& e) { - throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Error decrypting output note of previous JoinSplit: %s", e.what())); - } + } catch (const std::exception& e) { + throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Error decrypting output note of previous JoinSplit: %s", e.what())); } + } - // - // Consume spendable non-change notes - // - std::vector vInputNotes; - std::vector vOutPoints; - std::vector> vInputWitnesses; - uint256 inputAnchor; - int numInputsNeeded = (jsChange>0) ? 1 : 0; - while (numInputsNeeded++ < ZC_NUM_JS_INPUTS && zInputsDeque.size() > 0) { - SendManyInputJSOP t = zInputsDeque.front(); - JSOutPoint jso = std::get<0>(t); - Note note = std::get<1>(t); - CAmount noteFunds = std::get<2>(t); - zInputsDeque.pop_front(); - - WitnessAnchorData wad = jsopWitnessAnchorMap[ jso.ToString() ]; - vInputWitnesses.push_back(wad.witness); - if (inputAnchor.IsNull()) { - inputAnchor = wad.anchor; - } else if (inputAnchor != wad.anchor) { - throw JSONRPCError(RPC_WALLET_ERROR, "Selected input notes do not share the same anchor"); - } - - vOutPoints.push_back(jso); - vInputNotes.push_back(note); - - jsInputValue += noteFunds; + // + // Consume spendable non-change notes + // + std::vector vInputNotes; + std::vector vOutPoints; + std::vector> vInputWitnesses; + uint256 inputAnchor; + int numInputsNeeded = (jsChange>0) ? 1 : 0; + while (numInputsNeeded++ < ZC_NUM_JS_INPUTS && zInputsDeque.size() > 0) { + SendManyInputJSOP t = zInputsDeque.front(); + JSOutPoint jso = std::get<0>(t); + Note note = std::get<1>(t); + CAmount noteFunds = std::get<2>(t); + zInputsDeque.pop_front(); + + WitnessAnchorData wad = jsopWitnessAnchorMap[ jso.ToString() ]; + vInputWitnesses.push_back(wad.witness); + if (inputAnchor.IsNull()) { + inputAnchor = wad.anchor; + } else if (inputAnchor != wad.anchor) { + throw JSONRPCError(RPC_WALLET_ERROR, "Selected input notes do not share the same anchor"); + } - int wtxHeight = -1; - int wtxDepth = -1; - { - LOCK2(cs_main, pwalletMain->cs_wallet); - const CWalletTx& wtx = pwalletMain->mapWallet[jso.hash]; - wtxHeight = mapBlockIndex[wtx.hashBlock]->nHeight; - wtxDepth = wtx.GetDepthInMainChain(); + vOutPoints.push_back(jso); + vInputNotes.push_back(note); + + jsInputValue += noteFunds; + + int wtxHeight = -1; + int wtxDepth = -1; + { + LOCK2(cs_main, pwalletMain->cs_wallet); + const CWalletTx& wtx = pwalletMain->mapWallet[jso.hash]; + // Zero-confirmation notes belong to transactions which have not yet been mined + if (mapBlockIndex.find(wtx.hashBlock) == mapBlockIndex.end()) { + throw JSONRPCError(RPC_WALLET_ERROR, strprintf("mapBlockIndex does not contain block hash %s", wtx.hashBlock.ToString())); } - LogPrint("zrpcunsafe", "%s: spending note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, height=%d, confirmations=%d)\n", - getId(), - jso.hash.ToString().substr(0, 10), - jso.js, - int(jso.n), // uint8_t - FormatMoney(noteFunds), - wtxHeight, - wtxDepth - ); + wtxHeight = mapBlockIndex[wtx.hashBlock]->nHeight; + wtxDepth = wtx.GetDepthInMainChain(); } + LogPrint("zrpcunsafe", "%s: spending note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, height=%d, confirmations=%d)\n", + getId(), + jso.hash.ToString().substr(0, 10), + jso.js, + int(jso.n), // uint8_t + FormatMoney(noteFunds), + wtxHeight, + wtxDepth + ); + } + + // Add history of previous commitments to witness + if (vInputNotes.size() > 0) { - // Add history of previous commitments to witness - if (vInputNotes.size() > 0) { - - if (vInputWitnesses.size()==0) { - throw JSONRPCError(RPC_WALLET_ERROR, "Could not find witness for note commitment"); + if (vInputWitnesses.size()==0) { + throw JSONRPCError(RPC_WALLET_ERROR, "Could not find witness for note commitment"); + } + + for (auto & optionalWitness : vInputWitnesses) { + if (!optionalWitness) { + throw JSONRPCError(RPC_WALLET_ERROR, "Witness for note commitment is null"); } - - for (auto & optionalWitness : vInputWitnesses) { - if (!optionalWitness) { - throw JSONRPCError(RPC_WALLET_ERROR, "Witness for note commitment is null"); + ZCIncrementalWitness w = *optionalWitness; // could use .get(); + if (jsChange > 0) { + for (const uint256& commitment : previousCommitments) { + w.append(commitment); } - ZCIncrementalWitness w = *optionalWitness; // could use .get(); - if (jsChange > 0) { - for (const uint256& commitment : previousCommitments) { - w.append(commitment); - } - if (jsAnchor != w.root()) { - throw JSONRPCError(RPC_WALLET_ERROR, "Witness for spendable note does not have same anchor as change input"); - } + if (jsAnchor != w.root()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Witness for spendable note does not have same anchor as change input"); } - witnesses.push_back(w); - } - - // The jsAnchor is null if this JoinSplit is at the start of a new chain - if (jsAnchor.IsNull()) { - jsAnchor = inputAnchor; } + witnesses.push_back(w); + } - // Add spendable notes as inputs - std::copy(vInputNotes.begin(), vInputNotes.end(), std::back_inserter(info.notes)); + // The jsAnchor is null if this JoinSplit is at the start of a new chain + if (jsAnchor.IsNull()) { + jsAnchor = inputAnchor; } + // Add spendable notes as inputs + std::copy(vInputNotes.begin(), vInputNotes.end(), std::back_inserter(info.notes)); + } - // - // Find recipient to transfer funds to - // + // Find recipient to transfer funds to + std::string address, hexMemo; + CAmount value = 0; + if (zOutputsDeque.size() > 0) { SendManyRecipient smr = zOutputsDeque.front(); - std::string address = std::get<0>(smr); - CAmount value = std::get<1>(smr); - std::string hexMemo = std::get<2>(smr); + address = std::get<0>(smr); + value = std::get<1>(smr); + hexMemo = std::get<2>(smr); zOutputsDeque.pop_front(); + } - // Will we have any change? Has the miners fee been processed yet? - jsChange = 0; - CAmount outAmount = value; - if (!minersFeeProcessed) { - if (jsInputValue < minersFee) { - throw JSONRPCError(RPC_WALLET_ERROR, "Not enough funds to pay miners fee"); - } - outAmount += minersFee; + // Reset change + jsChange = 0; + CAmount outAmount = value; + + // Set vpub_new in the last joinsplit (when there are no more notes to spend or zaddr outputs to satisfy) + if (zOutputsDeque.size() == 0 && zInputsDeque.size() == 0) { + assert(!vpubNewProcessed); + if (jsInputValue < vpubNewTarget) { + throw JSONRPCError(RPC_WALLET_ERROR, + strprintf("Insufficient funds for vpub_new %s (miners fee %s, taddr outputs %s)", + FormatMoney(vpubNewTarget), FormatMoney(minersFee), FormatMoney(t_outputs_total))); } - + outAmount += vpubNewTarget; + info.vpub_new += vpubNewTarget; // funds flowing back to public pool + vpubNewProcessed = true; + jsChange = jsInputValue - outAmount; + assert(jsChange >= 0); + } + else { + // This is not the last joinsplit, so compute change and any amount still due to the recipient if (jsInputValue > outAmount) { jsChange = jsInputValue - outAmount; } else if (outAmount > jsInputValue) { @@ -733,42 +716,44 @@ bool AsyncRPCOperation_sendmany::main_impl() { // reduce the amount being sent right now to the value of all inputs value = jsInputValue; - if (!minersFeeProcessed) { - value -= minersFee; - } - } - - if (!minersFeeProcessed) { - minersFeeProcessed = true; - info.vpub_new += minersFee; // funds flowing back to public pool } + } - // create output for recipient + // create output for recipient + if (address.empty()) { + assert(value==0); + info.vjsout.push_back(JSOutput()); // dummy output while we accumulate funds into a change note for vpub_new + } else { PaymentAddress pa = CZCPaymentAddress(address).Get(); JSOutput jso = JSOutput(pa, value); if (hexMemo.size() > 0) { jso.memo = get_memo_from_hex_string(hexMemo); } info.vjsout.push_back(jso); + } - // create output for any change - if (jsChange>0) { - info.vjsout.push_back(JSOutput(frompaymentaddress_, jsChange)); + // create output for any change + if (jsChange>0) { + info.vjsout.push_back(JSOutput(frompaymentaddress_, jsChange)); - LogPrint("zrpcunsafe", "%s: generating note for change (amount=%s)\n", - getId(), - FormatMoney(jsChange) - ); - } + LogPrint("zrpcunsafe", "%s: generating note for change (amount=%s)\n", + getId(), + FormatMoney(jsChange) + ); + } - obj = perform_joinsplit(info, witnesses, jsAnchor); + obj = perform_joinsplit(info, witnesses, jsAnchor); - if (jsChange > 0) { - changeOutputIndex = find_output(obj, 1); - } + if (jsChange > 0) { + changeOutputIndex = find_output(obj, 1); } } + // Sanity check in case changes to code block above exits loop by invoking 'break' + assert(zInputsDeque.size() == 0); + assert(zOutputsDeque.size() == 0); + assert(vpubNewProcessed); + sign_send_raw_transaction(obj); return true; } @@ -850,6 +835,10 @@ bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptCoinbase=false) { pwalletMain->AvailableCoins(vecOutputs, false, NULL, true, fAcceptCoinbase); BOOST_FOREACH(const COutput& out, vecOutputs) { + if (!out.fSpendable) { + continue; + } + if (out.nDepth < mindepth_) { continue; } @@ -921,7 +910,7 @@ UniValue AsyncRPCOperation_sendmany::perform_joinsplit(AsyncJoinSplitInfo & info std::vector> witnesses; uint256 anchor; { - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK(cs_main); anchor = pcoinsTip->GetBestAnchor(); // As there are no inputs, ask the wallet for the best anchor } return perform_joinsplit(info, witnesses, anchor); @@ -986,13 +975,15 @@ UniValue AsyncRPCOperation_sendmany::perform_joinsplit( {info.vjsin[0], info.vjsin[1]}; boost::array outputs {info.vjsout[0], info.vjsout[1]}; - #ifdef __LP64__ +#ifdef __LP64__ boost::array inputMap; boost::array outputMap; - #else +#else boost::array inputMap; boost::array outputMap; - #endif +#endif + uint256 esk; // payment disclosure - secret + JSDescription jsdesc = JSDescription::Randomized( *pzcashParams, joinSplitPubKey_, @@ -1003,8 +994,8 @@ UniValue AsyncRPCOperation_sendmany::perform_joinsplit( outputMap, info.vpub_old, info.vpub_new, - !this->testmode); - + !this->testmode, + &esk); // parameter expects pointer to esk, so pass in address { auto verifier = libzcash::ProofVerifier::Strict(); if (!(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey_))) { @@ -1017,7 +1008,7 @@ UniValue AsyncRPCOperation_sendmany::perform_joinsplit( // Empty output script. CScript scriptCode; CTransaction signTx(mtx); - uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL); + uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId_); // Add the signature if (!(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, @@ -1073,6 +1064,28 @@ UniValue AsyncRPCOperation_sendmany::perform_joinsplit( arrOutputMap.push_back(outputMap[i]); } + + // !!! Payment disclosure START + unsigned char buffer[32] = {0}; + memcpy(&buffer[0], &joinSplitPrivKey_[0], 32); // private key in first half of 64 byte buffer + std::vector vch(&buffer[0], &buffer[0] + 32); + uint256 joinSplitPrivKey = uint256(vch); + size_t js_index = tx_.vjoinsplit.size() - 1; + uint256 placeholder; + for (int i = 0; i < ZC_NUM_JS_OUTPUTS; i++) { + uint8_t mapped_index = outputMap[i]; + // placeholder for txid will be filled in later when tx has been finalized and signed. + PaymentDisclosureKey pdKey = {placeholder, js_index, mapped_index}; + JSOutput output = outputs[mapped_index]; + libzcash::PaymentAddress zaddr = output.addr; // randomized output + PaymentDisclosureInfo pdInfo = {PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL, esk, joinSplitPrivKey, zaddr}; + paymentDisclosureData_.push_back(PaymentDisclosureKeyInfo(pdKey, pdInfo)); + + CZCPaymentAddress address(zaddr); + LogPrint("paymentdisclosure", "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), address.ToString()); + } + // !!! Payment disclosure END + UniValue obj(UniValue::VOBJ); obj.push_back(Pair("encryptednote1", encryptedNote1)); obj.push_back(Pair("encryptednote2", encryptedNote2)); diff --git a/src/wallet/asyncrpcoperation_sendmany.h b/src/wallet/asyncrpcoperation_sendmany.h index c69fce4c9..9d812fae1 100644 --- a/src/wallet/asyncrpcoperation_sendmany.h +++ b/src/wallet/asyncrpcoperation_sendmany.h @@ -12,6 +12,7 @@ #include "zcash/JoinSplit.hpp" #include "zcash/Address.hpp" #include "wallet.h" +#include "paymentdisclosure.h" #include #include @@ -50,7 +51,7 @@ struct WitnessAnchorData { class AsyncRPCOperation_sendmany : public AsyncRPCOperation { public: - AsyncRPCOperation_sendmany(std::string fromAddress, std::vector tOutputs, std::vector zOutputs, int minDepth, CAmount fee = ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE, UniValue contextInfo = NullUniValue); + AsyncRPCOperation_sendmany(CMutableTransaction contextualTx, std::string fromAddress, std::vector tOutputs, std::vector zOutputs, int minDepth, CAmount fee = ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE, UniValue contextInfo = NullUniValue); virtual ~AsyncRPCOperation_sendmany(); // We don't want to be copied or moved around @@ -65,11 +66,14 @@ public: bool testmode = false; // Set to true to disable sending txs and generating proofs + bool paymentDisclosureMode = false; // Set to true to save esk for encrypted notes in payment disclosure database. + private: friend class TEST_FRIEND_AsyncRPCOperation_sendmany; // class for unit testing UniValue contextinfo_; // optional data to include in return value from getStatus() + uint32_t consensusBranchId_; CAmount fee_; int mindepth_; std::string fromaddress_; @@ -113,6 +117,8 @@ private: void sign_send_raw_transaction(UniValue obj); // throws exception if there was an error + // payment disclosure! + std::vector paymentDisclosureData_; }; diff --git a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp new file mode 100644 index 000000000..527f810bc --- /dev/null +++ b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp @@ -0,0 +1,497 @@ +// Copyright (c) 2017 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "asyncrpcqueue.h" +#include "amount.h" +#include "consensus/upgrades.h" +#include "core_io.h" +#include "init.h" +#include "main.h" +#include "net.h" +#include "netbase.h" +#include "rpcserver.h" +#include "timedata.h" +#include "util.h" +#include "utilmoneystr.h" +#include "wallet.h" +#include "walletdb.h" +#include "script/interpreter.h" +#include "utiltime.h" +#include "rpcprotocol.h" +#include "zcash/IncrementalMerkleTree.hpp" +#include "sodium.h" +#include "miner.h" + +#include +#include +#include +#include + +#include "asyncrpcoperation_shieldcoinbase.h" + +#include "paymentdisclosure.h" +#include "paymentdisclosuredb.h" + +using namespace libzcash; + +static int find_output(UniValue obj, int n) { + UniValue outputMapValue = find_value(obj, "outputmap"); + if (!outputMapValue.isArray()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Missing outputmap for JoinSplit operation"); + } + + 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) { + return i; + } + } + + throw std::logic_error("n is not present in outputmap"); +} + +AsyncRPCOperation_shieldcoinbase::AsyncRPCOperation_shieldcoinbase( + CMutableTransaction contextualTx, + std::vector inputs, + std::string toAddress, + CAmount fee, + UniValue contextInfo) : + tx_(contextualTx), inputs_(inputs), fee_(fee), contextinfo_(contextInfo) +{ + assert(contextualTx.nVersion >= 2); // transaction format version must support vjoinsplit + + if (fee < 0 || fee > MAX_MONEY) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee is out of range"); + } + + if (inputs.size() == 0) { + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Empty inputs"); + } + + // Check the destination address is valid for this network i.e. not testnet being used on mainnet + CZCPaymentAddress address(toAddress); + try { + tozaddr_ = address.Get(); + } catch (const std::runtime_error& e) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("runtime error: ") + e.what()); + } + + // Log the context info + if (LogAcceptCategory("zrpcunsafe")) { + LogPrint("zrpcunsafe", "%s: z_shieldcoinbase initialized (context=%s)\n", getId(), contextInfo.write()); + } else { + LogPrint("zrpc", "%s: z_shieldcoinbase initialized\n", getId()); + } + + // Lock UTXOs + lock_utxos(); + + // Enable payment disclosure if requested + paymentDisclosureMode = fExperimentalMode && GetBoolArg("-paymentdisclosure", false); +} + +AsyncRPCOperation_shieldcoinbase::~AsyncRPCOperation_shieldcoinbase() { +} + +void AsyncRPCOperation_shieldcoinbase::main() { + if (isCancelled()) { + unlock_utxos(); // clean up + return; + } + + set_state(OperationStatus::EXECUTING); + start_execution_clock(); + + bool success = false; + +#ifdef ENABLE_MINING + #ifdef ENABLE_WALLET + GenerateBitcoins(false, NULL, 0); + #else + GenerateBitcoins(false, 0); + #endif +#endif + + 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(); + set_error_code(code); + set_error_message(message); + } catch (const runtime_error& e) { + set_error_code(-1); + set_error_message("runtime error: " + string(e.what())); + } catch (const logic_error& e) { + set_error_code(-1); + set_error_message("logic error: " + string(e.what())); + } catch (const exception& e) { + set_error_code(-1); + set_error_message("general exception: " + string(e.what())); + } catch (...) { + set_error_code(-2); + set_error_message("unknown error"); + } + +#ifdef ENABLE_MINING + #ifdef ENABLE_WALLET + GenerateBitcoins(GetBoolArg("-gen",false), pwalletMain, GetArg("-genproclimit", 1)); + #else + GenerateBitcoins(GetBoolArg("-gen",false), GetArg("-genproclimit", 1)); + #endif +#endif + + stop_execution_clock(); + + if (success) { + set_state(OperationStatus::SUCCESS); + } else { + set_state(OperationStatus::FAILED); + } + + std::string s = strprintf("%s: z_shieldcoinbase finished (status=%s", getId(), getStateAsString()); + if (success) { + s += strprintf(", txid=%s)\n", tx_.GetHash().ToString()); + } else { + s += strprintf(", error=%s)\n", getErrorMessage()); + } + LogPrintf("%s",s); + + unlock_utxos(); // clean up + + // !!! Payment disclosure START + if (success && paymentDisclosureMode && paymentDisclosureData_.size()>0) { + uint256 txidhash = tx_.GetHash(); + std::shared_ptr db = PaymentDisclosureDB::sharedInstance(); + 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()); + } else { + LogPrint("paymentdisclosure", "%s: Payment Disclosure: Successfully added entry to database for key %s\n", getId(), p.first.ToString()); + } + } + } + // !!! Payment disclosure END +} + + +bool AsyncRPCOperation_shieldcoinbase::main_impl() { + + CAmount minersFee = fee_; + + size_t numInputs = inputs_.size(); + + // Check mempooltxinputlimit to avoid creating a transaction which the local mempool rejects + size_t limit = (size_t)GetArg("-mempooltxinputlimit", 0); + if (limit>0 && numInputs > limit) { + throw JSONRPCError(RPC_WALLET_ERROR, + strprintf("Number of inputs %d is greater than mempooltxinputlimit of %d", + numInputs, limit)); + } + + CAmount targetAmount = 0; + for (ShieldCoinbaseUTXO & utxo : inputs_) { + targetAmount += utxo.amount; + } + + if (targetAmount <= minersFee) { + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, + strprintf("Insufficient coinbase funds, have %s and miners fee is %s", + FormatMoney(targetAmount), FormatMoney(minersFee))); + } + + CAmount sendAmount = targetAmount - minersFee; + LogPrint("zrpc", "%s: spending %s to shield %s with fee %s\n", + getId(), FormatMoney(targetAmount), FormatMoney(sendAmount), FormatMoney(minersFee)); + + // update the transaction with these inputs + CMutableTransaction rawTx(tx_); + for (ShieldCoinbaseUTXO & t : inputs_) { + CTxIn in(COutPoint(t.txid, t.vout)); + rawTx.vin.push_back(in); + } + tx_ = CTransaction(rawTx); + + // Prepare raw transaction to handle JoinSplits + CMutableTransaction mtx(tx_); + crypto_sign_keypair(joinSplitPubKey_.begin(), joinSplitPrivKey_); + mtx.joinSplitPubKey = joinSplitPubKey_; + tx_ = CTransaction(mtx); + + // Create joinsplit + UniValue obj(UniValue::VOBJ); + ShieldCoinbaseJSInfo info; + info.vpub_old = sendAmount; + info.vpub_new = 0; + JSOutput jso = JSOutput(tozaddr_, sendAmount); + info.vjsout.push_back(jso); + obj = perform_joinsplit(info); + + sign_send_raw_transaction(obj); + return true; +} + + +/** + * Sign and send a raw transaction. + * Raw transaction as hex string should be in object field "rawtxn" + */ +void AsyncRPCOperation_shieldcoinbase::sign_send_raw_transaction(UniValue obj) +{ + // Sign the raw transaction + UniValue rawtxnValue = find_value(obj, "rawtxn"); + if (rawtxnValue.isNull()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Missing hex data for raw transaction"); + } + std::string rawtxn = rawtxnValue.get_str(); + + UniValue params = UniValue(UniValue::VARR); + params.push_back(rawtxn); + UniValue signResultValue = signrawtransaction(params, false); + UniValue signResultObject = signResultValue.get_obj(); + UniValue completeValue = find_value(signResultObject, "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"); + if (hexValue.isNull()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Missing hex data for signed transaction"); + } + std::string signedtxn = hexValue.get_str(); + + // Send the signed transaction + if (!testmode) { + params.clear(); + params.setArray(); + params.push_back(signedtxn); + UniValue sendResultValue = sendrawtransaction(params, false); + if (sendResultValue.isNull()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Send raw transaction did not return an error or a txid."); + } + + std::string txid = sendResultValue.get_str(); + + UniValue o(UniValue::VOBJ); + o.push_back(Pair("txid", txid)); + set_result(o); + } else { + // Test mode does not send the transaction to the network. + + CDataStream stream(ParseHex(signedtxn), SER_NETWORK, PROTOCOL_VERSION); + CTransaction tx; + stream >> tx; + + UniValue o(UniValue::VOBJ); + o.push_back(Pair("test", 1)); + o.push_back(Pair("txid", tx.GetHash().ToString())); + o.push_back(Pair("hex", signedtxn)); + set_result(o); + } + + // Keep the signed transaction so we can hash to the same txid + CDataStream stream(ParseHex(signedtxn), SER_NETWORK, PROTOCOL_VERSION); + CTransaction tx; + stream >> tx; + tx_ = tx; +} + + +UniValue AsyncRPCOperation_shieldcoinbase::perform_joinsplit(ShieldCoinbaseJSInfo & info) { + uint32_t consensusBranchId; + uint256 anchor; + { + LOCK(cs_main); + consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); + anchor = pcoinsTip->GetBestAnchor(); + } + + + if (anchor.IsNull()) { + throw std::runtime_error("anchor is null"); + } + + // Make sure there are two inputs and two outputs + while (info.vjsin.size() < ZC_NUM_JS_INPUTS) { + info.vjsin.push_back(JSInput()); + } + + while (info.vjsout.size() < ZC_NUM_JS_OUTPUTS) { + info.vjsout.push_back(JSOutput()); + } + + if (info.vjsout.size() != ZC_NUM_JS_INPUTS || info.vjsin.size() != ZC_NUM_JS_OUTPUTS) { + throw runtime_error("unsupported joinsplit input/output counts"); + } + + 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", + getId(), + tx_.vjoinsplit.size(), + FormatMoney(info.vpub_old), FormatMoney(info.vpub_new), + FormatMoney(info.vjsin[0].note.value), FormatMoney(info.vjsin[1].note.value), + FormatMoney(info.vjsout[0].value), FormatMoney(info.vjsout[1].value) + ); + + // Generate the proof, this can take over a minute. + boost::array inputs + {info.vjsin[0], info.vjsin[1]}; + boost::array outputs + {info.vjsout[0], info.vjsout[1]}; + boost::array inputMap; + boost::array outputMap; + + uint256 esk; // payment disclosure - secret + + JSDescription jsdesc = JSDescription::Randomized( + *pzcashParams, + joinSplitPubKey_, + anchor, + inputs, + outputs, + inputMap, + outputMap, + info.vpub_old, + info.vpub_new, + !this->testmode, + &esk); // parameter expects pointer to esk, so pass in address + { + auto verifier = libzcash::ProofVerifier::Strict(); + if (!(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey_))) { + throw std::runtime_error("error verifying joinsplit"); + } + } + + mtx.vjoinsplit.push_back(jsdesc); + + // Empty output script. + CScript scriptCode; + CTransaction signTx(mtx); + uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId); + + // Add the signature + if (!(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, + dataToBeSigned.begin(), 32, + joinSplitPrivKey_ + ) == 0)) + { + throw std::runtime_error("crypto_sign_detached failed"); + } + + // Sanity check + if (!(crypto_sign_verify_detached(&mtx.joinSplitSig[0], + dataToBeSigned.begin(), 32, + mtx.joinSplitPubKey.begin() + ) == 0)) + { + throw std::runtime_error("crypto_sign_verify_detached failed"); + } + + CTransaction rawTx(mtx); + tx_ = rawTx; + + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << rawTx; + + std::string encryptedNote1; + std::string encryptedNote2; + { + CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); + ss2 << ((unsigned char) 0x00); + ss2 << jsdesc.ephemeralKey; + ss2 << jsdesc.ciphertexts[0]; + ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_); + + encryptedNote1 = HexStr(ss2.begin(), ss2.end()); + } + { + CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); + ss2 << ((unsigned char) 0x01); + ss2 << jsdesc.ephemeralKey; + ss2 << jsdesc.ciphertexts[1]; + ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_); + + encryptedNote2 = HexStr(ss2.begin(), ss2.end()); + } + + UniValue arrInputMap(UniValue::VARR); + UniValue arrOutputMap(UniValue::VARR); + for (size_t i = 0; i < ZC_NUM_JS_INPUTS; i++) { + arrInputMap.push_back(inputMap[i]); + } + for (size_t i = 0; i < ZC_NUM_JS_OUTPUTS; i++) { + arrOutputMap.push_back(outputMap[i]); + } + + // !!! Payment disclosure START + unsigned char buffer[32] = {0}; + memcpy(&buffer[0], &joinSplitPrivKey_[0], 32); // private key in first half of 64 byte buffer + std::vector vch(&buffer[0], &buffer[0] + 32); + uint256 joinSplitPrivKey = uint256(vch); + size_t js_index = tx_.vjoinsplit.size() - 1; + uint256 placeholder; + for (int i = 0; i < ZC_NUM_JS_OUTPUTS; i++) { + uint8_t mapped_index = outputMap[i]; + // placeholder for txid will be filled in later when tx has been finalized and signed. + PaymentDisclosureKey pdKey = {placeholder, js_index, mapped_index}; + JSOutput output = outputs[mapped_index]; + libzcash::PaymentAddress zaddr = output.addr; // randomized output + PaymentDisclosureInfo pdInfo = {PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL, esk, joinSplitPrivKey, zaddr}; + paymentDisclosureData_.push_back(PaymentDisclosureKeyInfo(pdKey, pdInfo)); + + CZCPaymentAddress address(zaddr); + LogPrint("paymentdisclosure", "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), address.ToString()); + } + // !!! 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)); + return obj; +} + +/** + * Override getStatus() to append the operation's context object to the default status object. + */ +UniValue AsyncRPCOperation_shieldcoinbase::getStatus() const { + UniValue v = AsyncRPCOperation::getStatus(); + if (contextinfo_.isNull()) { + return v; + } + + UniValue obj = v.get_obj(); + obj.push_back(Pair("method", "z_shieldcoinbase")); + obj.push_back(Pair("params", contextinfo_ )); + return obj; +} + +/** + * Lock input utxos + */ + void AsyncRPCOperation_shieldcoinbase::lock_utxos() { + LOCK2(cs_main, pwalletMain->cs_wallet); + for (auto utxo : inputs_) { + COutPoint outpt(utxo.txid, utxo.vout); + pwalletMain->LockCoin(outpt); + } +} + +/** + * Unlock input utxos + */ +void AsyncRPCOperation_shieldcoinbase::unlock_utxos() { + LOCK2(cs_main, pwalletMain->cs_wallet); + for (auto utxo : inputs_) { + COutPoint outpt(utxo.txid, utxo.vout); + pwalletMain->UnlockCoin(outpt); + } +} diff --git a/src/wallet/asyncrpcoperation_shieldcoinbase.h b/src/wallet/asyncrpcoperation_shieldcoinbase.h new file mode 100644 index 000000000..c7faf28e8 --- /dev/null +++ b/src/wallet/asyncrpcoperation_shieldcoinbase.h @@ -0,0 +1,129 @@ +// Copyright (c) 2017 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef ASYNCRPCOPERATION_SHIELDCOINBASE_H +#define ASYNCRPCOPERATION_SHIELDCOINBASE_H + +#include "asyncrpcoperation.h" +#include "amount.h" +#include "base58.h" +#include "primitives/transaction.h" +#include "zcash/JoinSplit.hpp" +#include "zcash/Address.hpp" +#include "wallet.h" + +#include +#include + +#include + +#include "paymentdisclosure.h" + +// Default transaction fee if caller does not specify one. +#define SHIELD_COINBASE_DEFAULT_MINERS_FEE 10000 + +using namespace libzcash; + +struct ShieldCoinbaseUTXO { + uint256 txid; + int vout; + CAmount amount; +}; + +// Package of info which is passed to perform_joinsplit methods. +struct ShieldCoinbaseJSInfo +{ + std::vector vjsin; + std::vector vjsout; + CAmount vpub_old = 0; + CAmount vpub_new = 0; +}; + +class AsyncRPCOperation_shieldcoinbase : public AsyncRPCOperation { +public: + AsyncRPCOperation_shieldcoinbase(CMutableTransaction contextualTx, std::vector inputs, std::string toAddress, CAmount fee = SHIELD_COINBASE_DEFAULT_MINERS_FEE, UniValue contextInfo = NullUniValue); + virtual ~AsyncRPCOperation_shieldcoinbase(); + + // We don't want to be copied or moved around + AsyncRPCOperation_shieldcoinbase(AsyncRPCOperation_shieldcoinbase const&) = delete; // Copy construct + AsyncRPCOperation_shieldcoinbase(AsyncRPCOperation_shieldcoinbase&&) = delete; // Move construct + AsyncRPCOperation_shieldcoinbase& operator=(AsyncRPCOperation_shieldcoinbase const&) = delete; // Copy assign + AsyncRPCOperation_shieldcoinbase& operator=(AsyncRPCOperation_shieldcoinbase &&) = delete; // Move assign + + virtual void main(); + + virtual UniValue getStatus() const; + + bool testmode = false; // Set to true to disable sending txs and generating proofs + + bool paymentDisclosureMode = false; // Set to true to save esk for encrypted notes in payment disclosure database. + +private: + friend class TEST_FRIEND_AsyncRPCOperation_shieldcoinbase; // class for unit testing + + UniValue contextinfo_; // optional data to include in return value from getStatus() + + CAmount fee_; + PaymentAddress tozaddr_; + + uint256 joinSplitPubKey_; + unsigned char joinSplitPrivKey_[crypto_sign_SECRETKEYBYTES]; + + std::vector inputs_; + + CTransaction tx_; + + bool main_impl(); + + // JoinSplit without any input notes to spend + UniValue perform_joinsplit(ShieldCoinbaseJSInfo &); + + void sign_send_raw_transaction(UniValue obj); // throws exception if there was an error + + void lock_utxos(); + + void unlock_utxos(); + + // payment disclosure! + std::vector paymentDisclosureData_; +}; + + +// To test private methods, a friend class can act as a proxy +class TEST_FRIEND_AsyncRPCOperation_shieldcoinbase { +public: + std::shared_ptr delegate; + + TEST_FRIEND_AsyncRPCOperation_shieldcoinbase(std::shared_ptr ptr) : delegate(ptr) {} + + CTransaction getTx() { + return delegate->tx_; + } + + void setTx(CTransaction tx) { + delegate->tx_ = tx; + } + + // Delegated methods + + bool main_impl() { + return delegate->main_impl(); + } + + UniValue perform_joinsplit(ShieldCoinbaseJSInfo &info) { + return delegate->perform_joinsplit(info); + } + + void sign_send_raw_transaction(UniValue obj) { + delegate->sign_send_raw_transaction(obj); + } + + void set_state(OperationStatus state) { + delegate->state_.store(state); + } +}; + + +#endif /* ASYNCRPCOPERATION_SHIELDCOINBASE_H */ + diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index a5ef786d8..69a2649b1 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -153,7 +153,7 @@ static bool DecryptSpendingKey(const CKeyingMaterial& vMasterKey, bool CCryptoKeyStore::SetCrypted() { - LOCK(cs_KeyStore); + LOCK2(cs_KeyStore, cs_SpendingKeyStore); if (fUseCrypto) return true; if (!(mapKeys.empty() && mapSpendingKeys.empty())) @@ -179,7 +179,7 @@ bool CCryptoKeyStore::Lock() bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) { { - LOCK(cs_KeyStore); + LOCK2(cs_KeyStore, cs_SpendingKeyStore); if (!SetCrypted()) return false; @@ -316,14 +316,14 @@ bool CCryptoKeyStore::AddSpendingKey(const libzcash::SpendingKey &sk) if (!EncryptSecret(vMasterKey, vchSecret, address.GetHash(), vchCryptedSecret)) return false; - if (!AddCryptedSpendingKey(address, sk.viewing_key(), vchCryptedSecret)) + if (!AddCryptedSpendingKey(address, sk.receiving_key(), vchCryptedSecret)) return false; } return true; } bool CCryptoKeyStore::AddCryptedSpendingKey(const libzcash::PaymentAddress &address, - const libzcash::ViewingKey &vk, + const libzcash::ReceivingKey &rk, const std::vector &vchCryptedSecret) { { @@ -332,7 +332,7 @@ bool CCryptoKeyStore::AddCryptedSpendingKey(const libzcash::PaymentAddress &addr return false; mapCryptedSpendingKeys[address] = vchCryptedSecret; - mapNoteDecryptors.insert(std::make_pair(address, ZCNoteDecryption(vk))); + mapNoteDecryptors.insert(std::make_pair(address, ZCNoteDecryption(rk))); } return true; } @@ -384,7 +384,7 @@ bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn) std::vector vchCryptedSecret; if (!EncryptSecret(vMasterKeyIn, vchSecret, address.GetHash(), vchCryptedSecret)) return false; - if (!AddCryptedSpendingKey(address, sk.viewing_key(), vchCryptedSecret)) + if (!AddCryptedSpendingKey(address, sk.receiving_key(), vchCryptedSecret)) return false; } mapSpendingKeys.clear(); diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index b310b77b0..bcee188cf 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -201,13 +201,13 @@ public: } } virtual bool AddCryptedSpendingKey(const libzcash::PaymentAddress &address, - const libzcash::ViewingKey &vk, + const libzcash::ReceivingKey &rk, const std::vector &vchCryptedSecret); bool AddSpendingKey(const libzcash::SpendingKey &sk); bool HaveSpendingKey(const libzcash::PaymentAddress &address) const { { - LOCK(cs_KeyStore); + LOCK(cs_SpendingKeyStore); if (!IsCrypted()) return CBasicKeyStore::HaveSpendingKey(address); return mapCryptedSpendingKeys.count(address) > 0; diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index 9bcc5f533..e976e4ae4 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -281,7 +281,7 @@ TEST(wallet_tests, find_unspent_notes) { wallet.GetFilteredNotes(entries, "", 2, false); EXPECT_EQ(1, entries.size()); entries.clear(); - // If we also ignore spent notes at thie depth, we won't find any notes. + // If we also ignore spent notes at this depth, we won't find any notes. wallet.GetFilteredNotes(entries, "", 2, true); EXPECT_EQ(0, entries.size()); entries.clear(); @@ -328,7 +328,7 @@ TEST(wallet_tests, GetNoteNullifier) { auto sk = libzcash::SpendingKey::random(); auto address = sk.address(); - auto dec = ZCNoteDecryption(sk.viewing_key()); + auto dec = ZCNoteDecryption(sk.receiving_key()); auto wtx = GetValidReceive(sk, 10, true); auto note = GetNote(sk, wtx, 0, 1); @@ -1046,3 +1046,36 @@ TEST(wallet_tests, MarkAffectedTransactionsDirty) { wallet.MarkAffectedTransactionsDirty(wtx2); EXPECT_FALSE(wallet.mapWallet[hash].fDebitCached); } + +TEST(wallet_tests, NoteLocking) { + TestWallet wallet; + + auto sk = libzcash::SpendingKey::random(); + wallet.AddSpendingKey(sk); + + auto wtx = GetValidReceive(sk, 10, true); + auto wtx2 = GetValidReceive(sk, 10, true); + + JSOutPoint jsoutpt {wtx.GetHash(), 0, 0}; + JSOutPoint jsoutpt2 {wtx2.GetHash(),0, 0}; + + // Test selective locking + wallet.LockNote(jsoutpt); + EXPECT_TRUE(wallet.IsLockedNote(jsoutpt.hash, jsoutpt.js, jsoutpt.n)); + EXPECT_FALSE(wallet.IsLockedNote(jsoutpt2.hash, jsoutpt2.js, jsoutpt2.n)); + + // Test selective unlocking + wallet.UnlockNote(jsoutpt); + EXPECT_FALSE(wallet.IsLockedNote(jsoutpt.hash, jsoutpt.js, jsoutpt.n)); + + // Test multiple locking + wallet.LockNote(jsoutpt); + wallet.LockNote(jsoutpt2); + EXPECT_TRUE(wallet.IsLockedNote(jsoutpt.hash, jsoutpt.js, jsoutpt.n)); + EXPECT_TRUE(wallet.IsLockedNote(jsoutpt2.hash, jsoutpt2.js, jsoutpt2.n)); + + // Test unlock all + wallet.UnlockAllNotes(); + EXPECT_FALSE(wallet.IsLockedNote(jsoutpt.hash, jsoutpt.js, jsoutpt.n)); + EXPECT_FALSE(wallet.IsLockedNote(jsoutpt2.hash, jsoutpt2.js, jsoutpt2.n)); +} diff --git a/src/wallet/gtest/test_wallet_zkeys.cpp b/src/wallet/gtest/test_wallet_zkeys.cpp index c7912ae7a..b40479e87 100644 --- a/src/wallet/gtest/test_wallet_zkeys.cpp +++ b/src/wallet/gtest/test_wallet_zkeys.cpp @@ -66,6 +66,53 @@ TEST(wallet_zkeys_tests, store_and_load_zkeys) { ASSERT_EQ(m.nCreateTime, now); } +/** + * This test covers methods on CWallet + * AddViewingKey() + * RemoveViewingKey() + * LoadViewingKey() + */ +TEST(wallet_zkeys_tests, StoreAndLoadViewingKeys) { + SelectParams(CBaseChainParams::MAIN); + + CWallet wallet; + + // wallet should be empty + std::set addrs; + wallet.GetPaymentAddresses(addrs); + ASSERT_EQ(0, addrs.size()); + + // manually add new viewing key to wallet + auto sk = libzcash::SpendingKey::random(); + auto vk = sk.viewing_key(); + ASSERT_TRUE(wallet.AddViewingKey(vk)); + + // verify wallet did add it + auto addr = sk.address(); + ASSERT_TRUE(wallet.HaveViewingKey(addr)); + // and that we don't have the corresponding spending key + ASSERT_FALSE(wallet.HaveSpendingKey(addr)); + + // verify viewing key stored correctly + libzcash::ViewingKey vkOut; + wallet.GetViewingKey(addr, vkOut); + ASSERT_EQ(vk, vkOut); + + // Load a second viewing key into the wallet + auto sk2 = libzcash::SpendingKey::random(); + ASSERT_TRUE(wallet.LoadViewingKey(sk2.viewing_key())); + + // verify wallet did add it + auto addr2 = sk2.address(); + ASSERT_TRUE(wallet.HaveViewingKey(addr2)); + ASSERT_FALSE(wallet.HaveSpendingKey(addr2)); + + // Remove the first viewing key + ASSERT_TRUE(wallet.RemoveViewingKey(vk)); + ASSERT_FALSE(wallet.HaveViewingKey(addr)); + ASSERT_TRUE(wallet.HaveViewingKey(addr2)); +} + /** * This test covers methods on CWalletDB * WriteZKey() @@ -138,6 +185,50 @@ TEST(wallet_zkeys_tests, write_zkey_direct_to_db) { ASSERT_EQ(m.nCreateTime, now); } +/** + * This test covers methods on CWalletDB + * WriteViewingKey() + */ +TEST(wallet_zkeys_tests, WriteViewingKeyDirectToDB) { + SelectParams(CBaseChainParams::TESTNET); + + // 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); + mapArgs["-datadir"] = pathTemp.string(); + + bool fFirstRun; + CWallet wallet("wallet-vkey.dat"); + ASSERT_EQ(DB_LOAD_OK, wallet.LoadWallet(fFirstRun)); + + // No default CPubKey set + ASSERT_TRUE(fFirstRun); + + // create random viewing key and add it to database directly, bypassing wallet + auto sk = libzcash::SpendingKey::random(); + auto vk = sk.viewing_key(); + auto addr = sk.address(); + int64_t now = GetTime(); + CKeyMetadata meta(now); + CWalletDB db("wallet-vkey.dat"); + db.WriteViewingKey(vk); + + // wallet should not be aware of viewing key + ASSERT_FALSE(wallet.HaveViewingKey(addr)); + + // load the wallet again + ASSERT_EQ(DB_LOAD_OK, wallet.LoadWallet(fFirstRun)); + + // wallet can now see the viewing key + ASSERT_TRUE(wallet.HaveViewingKey(addr)); + + // check key is the same + libzcash::ViewingKey vkOut; + wallet.GetViewingKey(addr, vkOut); + ASSERT_EQ(vk, vkOut); +} + /** @@ -214,5 +305,7 @@ TEST(wallet_zkeys_tests, write_cryptedzkey_direct_to_db) { wallet2.GetSpendingKey(paymentAddress2.Get(), keyOut); ASSERT_EQ(paymentAddress2.Get(), keyOut.address()); + + ECC_Stop(); } diff --git a/src/wallet/rpcdisclosure.cpp b/src/wallet/rpcdisclosure.cpp new file mode 100644 index 000000000..539cf4b2a --- /dev/null +++ b/src/wallet/rpcdisclosure.cpp @@ -0,0 +1,305 @@ +// Copyright (c) 2017 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "base58.h" +#include "rpcserver.h" +#include "init.h" +#include "main.h" +#include "script/script.h" +#include "script/standard.h" +#include "sync.h" +#include "util.h" +#include "utiltime.h" +#include "wallet.h" + +#include +#include + +#include +#include + +#include + +#include "paymentdisclosure.h" +#include "paymentdisclosuredb.h" + +#include "zcash/Note.hpp" +#include "zcash/NoteEncryption.hpp" + +using namespace std; +using namespace libzcash; + +// Function declaration for function implemented in wallet/rpcwallet.cpp +bool EnsureWalletIsAvailable(bool avoidException); + +/** + * RPC call to generate a payment disclosure + */ +UniValue z_getpaymentdisclosure(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + auto fEnablePaymentDisclosure = fExperimentalMode && GetBoolArg("-paymentdisclosure", false); + string strPaymentDisclosureDisabledMsg = ""; + if (!fEnablePaymentDisclosure) { + strPaymentDisclosureDisabledMsg = "\nWARNING: Payment disclosure is currently DISABLED. This call always fails.\n"; + } + + if (fHelp || params.size() < 3 || params.size() > 4 ) + throw runtime_error( + "z_getpaymentdisclosure \"txid\" \"js_index\" \"output_index\" (\"message\") \n" + "\nGenerate a payment disclosure for a given joinsplit output.\n" + "\nEXPERIMENTAL FEATURE\n" + + strPaymentDisclosureDisabledMsg + + "\nArguments:\n" + "1. \"txid\" (string, required) \n" + "2. \"js_index\" (string, required) \n" + "3. \"output_index\" (string, required) \n" + "4. \"message\" (string, optional) \n" + "\nResult:\n" + "\"paymentdisclosure\" (string) Hex data string, with \"zpd:\" prefix.\n" + "\nExamples:\n" + + HelpExampleCli("z_getpaymentdisclosure", "96f12882450429324d5f3b48630e3168220e49ab7b0f066e5c2935a6b88bb0f2 0 0 \"refund\"") + + HelpExampleRpc("z_getpaymentdisclosure", "\"96f12882450429324d5f3b48630e3168220e49ab7b0f066e5c2935a6b88bb0f2\", 0, 0, \"refund\"") + ); + + if (!fEnablePaymentDisclosure) { + throw JSONRPCError(RPC_WALLET_ERROR, "Error: payment disclosure is disabled."); + } + + LOCK2(cs_main, pwalletMain->cs_wallet); + + EnsureWalletIsUnlocked(); + + // Check wallet knows about txid + string txid = params[0].get_str(); + uint256 hash; + hash.SetHex(txid); + + CTransaction tx; + uint256 hashBlock; + + // Check txid has been seen + if (!GetTransaction(hash, tx, hashBlock, true)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); + } + + // Check tx has been confirmed + if (hashBlock.IsNull()) { + throw JSONRPCError(RPC_MISC_ERROR, "Transaction has not been confirmed yet"); + } + + // Check is mine + if (!pwalletMain->mapWallet.count(hash)) { + throw JSONRPCError(RPC_MISC_ERROR, "Transaction does not belong to the wallet"); + } + const CWalletTx& wtx = pwalletMain->mapWallet[hash]; + + // Check if shielded tx + if (wtx.vjoinsplit.empty()) { + throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not a shielded transaction"); + } + + // Check js_index + int js_index = params[1].get_int(); + 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(); + if (output_index < 0 || output_index >= ZC_NUM_JS_OUTPUTS) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid output_index"); + } + + // Get message if it exists + string msg; + if (params.size() == 4) { + msg = params[3].get_str(); + } + + // Create PaymentDisclosureKey + PaymentDisclosureKey key = {hash, (size_t)js_index, (uint8_t)output_index }; + + // TODO: In future, perhaps init the DB in init.cpp + shared_ptr db = PaymentDisclosureDB::sharedInstance(); + PaymentDisclosureInfo info; + if (!db->Get(key, info)) { + throw JSONRPCError(RPC_DATABASE_ERROR, "Could not find payment disclosure info for the given joinsplit output"); + } + + PaymentDisclosure pd( wtx.joinSplitPubKey, key, info, msg ); + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << pd; + string strHex = HexStr(ss.begin(), ss.end()); + return PAYMENT_DISCLOSURE_BLOB_STRING_PREFIX + strHex; +} + + + +/** + * RPC call to validate a payment disclosure data blob. + */ +UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + auto fEnablePaymentDisclosure = fExperimentalMode && GetBoolArg("-paymentdisclosure", false); + string strPaymentDisclosureDisabledMsg = ""; + if (!fEnablePaymentDisclosure) { + strPaymentDisclosureDisabledMsg = "\nWARNING: Payment disclosure is curretly DISABLED. This call always fails.\n"; + } + + if (fHelp || params.size() != 1) + throw runtime_error( + "z_validatepaymentdisclosure \"paymentdisclosure\"\n" + "\nValidates a payment disclosure.\n" + "\nEXPERIMENTAL FEATURE\n" + + strPaymentDisclosureDisabledMsg + + "\nArguments:\n" + "1. \"paymentdisclosure\" (string, required) Hex data string, with \"zpd:\" prefix.\n" + "\nExamples:\n" + + HelpExampleCli("z_validatepaymentdisclosure", "\"zpd:706462ff004c561a0447ba2ec51184e6c204...\"") + + HelpExampleRpc("z_validatepaymentdisclosure", "\"zpd:706462ff004c561a0447ba2ec51184e6c204...\"") + ); + + if (!fEnablePaymentDisclosure) { + throw JSONRPCError(RPC_WALLET_ERROR, "Error: payment disclosure is disabled."); + } + + LOCK2(cs_main, pwalletMain->cs_wallet); + + EnsureWalletIsUnlocked(); + + // Verify the payment disclosure input begins with "zpd:" prefix. + string strInput = params[0].get_str(); + size_t pos = strInput.find(PAYMENT_DISCLOSURE_BLOB_STRING_PREFIX); + if (pos != 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, payment disclosure prefix not found."); + } + string hexInput = strInput.substr(strlen(PAYMENT_DISCLOSURE_BLOB_STRING_PREFIX)); + if (!IsHex(hexInput)) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected payment disclosure data in hexadecimal format."); + } + + // Unserialize the payment disclosure data into an object + PaymentDisclosure pd; + CDataStream ss(ParseHex(hexInput), SER_NETWORK, PROTOCOL_VERSION); + try { + ss >> pd; + // too much data is ignored, but if not enough data, exception of type ios_base::failure is thrown, + // CBaseDataStream::read(): end of data: iostream error + } catch (const std::exception &e) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, payment disclosure data is malformed."); + } + + if (pd.payload.marker != PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, payment disclosure marker not found."); + } + + if (pd.payload.version != PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Payment disclosure version is unsupported."); + } + + uint256 hash = pd.payload.txid; + CTransaction tx; + uint256 hashBlock; + // Check if we have seen the transaction + if (!GetTransaction(hash, tx, hashBlock, true)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); + } + + // Check if the transaction has been confirmed + if (hashBlock.IsNull()) { + throw JSONRPCError(RPC_MISC_ERROR, "Transaction has not been confirmed yet"); + } + + // Check if shielded tx + if (tx.vjoinsplit.empty()) { + throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not a shielded transaction"); + } + + UniValue errs(UniValue::VARR); + UniValue o(UniValue::VOBJ); + o.push_back(Pair("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)); + + 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())); + + // 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)); + if (!sigVerified) { + errs.push_back("Payment disclosure signature does not match transaction signature"); + } + + // Check the payment address is valid + PaymentAddress zaddr = pd.payload.zaddr; + CZCPaymentAddress address; + if (!address.Set(zaddr)) { + errs.push_back("Payment disclosure refers to an invalid payment address"); + } else { + o.push_back(Pair("paymentAddress", address.ToString())); + + 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); + + ZCPaymentDisclosureNoteDecryption decrypter; + + ZCNoteEncryption::Ciphertext ciphertext = jsdesc.ciphertexts[pd.payload.n]; + + uint256 pk_enc = zaddr.pk_enc; + auto plaintext = decrypter.decryptWithEsk(ciphertext, pk_enc, pd.payload.esk, h_sig, pd.payload.n); + + CDataStream ssPlain(SER_NETWORK, PROTOCOL_VERSION); + ssPlain << plaintext; + NotePlaintext npt; + 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))); + + // Check the blockchain commitment matches decrypted note commitment + uint256 cm_blockchain = jsdesc.commitments[pd.payload.n]; + Note note = npt.note(zaddr); + uint256 cm_decrypted = note.cm(); + bool cm_match = (cm_decrypted == cm_blockchain); + o.push_back(Pair("commitmentMatch", cm_match)); + if (!cm_match) { + errs.push_back("Commitment derived from payment disclosure does not match blockchain commitment"); + } + } catch (const std::exception &e) { + errs.push_back(string("Error while decrypting payment disclosure note: ") + string(e.what()) ); + } + } + + bool isValid = errs.empty(); + o.push_back(Pair("valid", isValid)); + if (!isValid) { + o.push_back(Pair("errors", errs)); + } + + return o; +} diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 83f0e5cac..33dc90bb3 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -128,8 +128,9 @@ UniValue importprivkey(const UniValue& params, bool fHelp) pwalletMain->SetAddressBook(vchAddress, strLabel, "receive"); // Don't throw error in case a key is already there - if (pwalletMain->HaveKey(vchAddress)) - return NullUniValue; + if (pwalletMain->HaveKey(vchAddress)) { + return CBitcoinAddress(vchAddress).ToString(); + } pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = 1; @@ -144,7 +145,7 @@ UniValue importprivkey(const UniValue& params, bool fHelp) } } - return NullUniValue; + return CBitcoinAddress(vchAddress).ToString(); } UniValue importaddress(const UniValue& params, bool fHelp) @@ -233,11 +234,11 @@ UniValue z_importwallet(const UniValue& params, bool fHelp) "1. \"filename\" (string, required) The wallet file\n" "\nExamples:\n" "\nDump the wallet\n" - + HelpExampleCli("z_exportwallet", "\"test\"") + + + HelpExampleCli("z_exportwallet", "\"nameofbackup\"") + "\nImport the wallet\n" - + HelpExampleCli("z_importwallet", "\"test\"") + + + HelpExampleCli("z_importwallet", "\"path/to/exportdir/nameofbackup\"") + "\nImport using the json rpc call\n" - + HelpExampleRpc("z_importwallet", "\"test\"") + + HelpExampleRpc("z_importwallet", "\"path/to/exportdir/nameofbackup\"") ); return importwallet_impl(params, fHelp, true); @@ -256,11 +257,11 @@ UniValue importwallet(const UniValue& params, bool fHelp) "1. \"filename\" (string, required) The wallet file\n" "\nExamples:\n" "\nDump the wallet\n" - + HelpExampleCli("dumpwallet", "\"test\"") + + + HelpExampleCli("dumpwallet", "\"nameofbackup\"") + "\nImport the wallet\n" - + HelpExampleCli("importwallet", "\"test\"") + + + HelpExampleCli("importwallet", "\"path/to/exportdir/nameofbackup\"") + "\nImport using the json rpc call\n" - + HelpExampleRpc("importwallet", "\"test\"") + + HelpExampleRpc("importwallet", "\"path/to/exportdir/nameofbackup\"") ); return importwallet_impl(params, fHelp, false); @@ -427,7 +428,7 @@ UniValue z_exportwallet(const UniValue& params, bool fHelp) if (fHelp || params.size() != 1) throw runtime_error( "z_exportwallet \"filename\"\n" - "\nExports all wallet keys, for taddr and zaddr, in a human-readable format.\n" + "\nExports all wallet keys, for taddr and zaddr, in a human-readable format. Overwriting an existing file is not permitted.\n" "\nArguments:\n" "1. \"filename\" (string, required) The filename, saved in folder set by zcashd -exportdir option\n" "\nResult:\n" @@ -448,7 +449,7 @@ UniValue dumpwallet(const UniValue& params, bool fHelp) if (fHelp || params.size() != 1) throw runtime_error( "dumpwallet \"filename\"\n" - "\nDumps taddr wallet keys in a human-readable format.\n" + "\nDumps taddr wallet keys in a human-readable format. Overwriting an existing file is not permitted.\n" "\nArguments:\n" "1. \"filename\" (string, required) The filename, saved in folder set by zcashd -exportdir option\n" "\nResult:\n" @@ -483,6 +484,10 @@ UniValue dumpwallet_impl(const UniValue& params, bool fHelp, bool fDumpZKeys) } boost::filesystem::path exportfilepath = exportdir / clean; + if (boost::filesystem::exists(exportfilepath)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot overwrite existing file " + exportfilepath.string()); + } + ofstream file; file.open(exportfilepath.string().c_str()); if (!file.is_open()) @@ -643,6 +648,94 @@ UniValue z_importkey(const UniValue& params, bool fHelp) return NullUniValue; } +UniValue z_importviewingkey(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() < 1 || params.size() > 3) + throw runtime_error( + "z_importviewingkey \"vkey\" ( rescan startHeight )\n" + "\nAdds a viewing key (as returned by z_exportviewingkey) to your wallet.\n" + "\nArguments:\n" + "1. \"vkey\" (string, required) The viewing key (see z_exportviewingkey)\n" + "2. rescan (string, optional, default=\"whenkeyisnew\") Rescan the wallet for transactions - can be \"yes\", \"no\" or \"whenkeyisnew\"\n" + "3. startHeight (numeric, optional, default=0) Block height to start rescan from\n" + "\nNote: This call can take minutes to complete if rescan is true.\n" + "\nExamples:\n" + "\nImport a viewing key\n" + + HelpExampleCli("z_importviewingkey", "\"vkey\"") + + "\nImport the viewing key without rescan\n" + + HelpExampleCli("z_importviewingkey", "\"vkey\", no") + + "\nImport the viewing key with partial rescan\n" + + HelpExampleCli("z_importviewingkey", "\"vkey\" whenkeyisnew 30000") + + "\nRe-import the viewing key with longer partial rescan\n" + + HelpExampleCli("z_importviewingkey", "\"vkey\" yes 20000") + + "\nAs a JSON-RPC call\n" + + HelpExampleRpc("z_importviewingkey", "\"vkey\", \"no\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + EnsureWalletIsUnlocked(); + + // Whether to perform rescan after import + bool fRescan = true; + bool fIgnoreExistingKey = true; + if (params.size() > 1) { + auto rescan = params[1].get_str(); + if (rescan.compare("whenkeyisnew") != 0) { + fIgnoreExistingKey = false; + if (rescan.compare("no") == 0) { + fRescan = false; + } else if (rescan.compare("yes") != 0) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, + "rescan must be \"yes\", \"no\" or \"whenkeyisnew\""); + } + } + } + + // Height to rescan from + int nRescanHeight = 0; + if (params.size() > 2) { + nRescanHeight = params[2].get_int(); + } + if (nRescanHeight < 0 || nRescanHeight > chainActive.Height()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); + } + + string strVKey = params[0].get_str(); + CZCViewingKey viewingkey(strVKey); + auto vkey = viewingkey.Get(); + auto addr = vkey.address(); + + { + if (pwalletMain->HaveSpendingKey(addr)) { + throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this viewing key"); + } + + // Don't throw error in case a viewing key is already there + if (pwalletMain->HaveViewingKey(addr)) { + if (fIgnoreExistingKey) { + return NullUniValue; + } + } else { + pwalletMain->MarkDirty(); + + if (!pwalletMain->AddViewingKey(vkey)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Error adding viewing key to wallet"); + } + } + + // We want to scan for transactions and notes + if (fRescan) { + pwalletMain->ScanForWalletTransactions(chainActive[nRescanHeight], true); + } + } + + return NullUniValue; +} UniValue z_exportkey(const UniValue& params, bool fHelp) { @@ -681,3 +774,43 @@ UniValue z_exportkey(const UniValue& params, bool fHelp) return spendingkey.ToString(); } +UniValue z_exportviewingkey(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() != 1) + throw runtime_error( + "z_exportviewingkey \"zaddr\"\n" + "\nReveals the viewing key corresponding to 'zaddr'.\n" + "Then the z_importviewingkey can be used with this output\n" + "\nArguments:\n" + "1. \"zaddr\" (string, required) The zaddr for the viewing key\n" + "\nResult:\n" + "\"vkey\" (string) The viewing key\n" + "\nExamples:\n" + + HelpExampleCli("z_exportviewingkey", "\"myaddress\"") + + HelpExampleRpc("z_exportviewingkey", "\"myaddress\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + EnsureWalletIsUnlocked(); + + string strAddress = params[0].get_str(); + + CZCPaymentAddress address(strAddress); + auto addr = address.Get(); + + libzcash::ViewingKey vk; + if (!pwalletMain->GetViewingKey(addr, vk)) { + libzcash::SpendingKey k; + if (!pwalletMain->GetSpendingKey(addr, k)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold private key or viewing key for this zaddr"); + } + vk = k.viewing_key(); + } + + CZCViewingKey viewingkey(vk); + return viewingkey.ToString(); +} diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index c407b0174..347c15f2d 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5,6 +5,7 @@ #include "amount.h" #include "base58.h" +#include "consensus/upgrades.h" #include "core_io.h" #include "init.h" #include "main.h" @@ -23,7 +24,9 @@ #include "utiltime.h" #include "asyncrpcoperation.h" #include "asyncrpcqueue.h" +#include "wallet/asyncrpcoperation_mergetoaddress.h" #include "wallet/asyncrpcoperation_sendmany.h" +#include "wallet/asyncrpcoperation_shieldcoinbase.h" #include "sodium.h" @@ -87,6 +90,7 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) 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)); } uint256 hash = wtx.GetHash(); entry.push_back(Pair("txid", hash.GetHex())); @@ -122,7 +126,7 @@ UniValue getnewaddress(const UniValue& params, bool fHelp) "\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" "\nResult:\n" - "\"zcashaddress\" (string) The new zcash address\n" + "\"zcashaddress\" (string) The new Zcash address\n" "\nExamples:\n" + HelpExampleCli("getnewaddress", "") + HelpExampleRpc("getnewaddress", "") @@ -199,7 +203,7 @@ UniValue getaccountaddress(const UniValue& params, bool fHelp) "\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" - "\"zcashaddress\" (string) The account zcash address\n" + "\"zcashaddress\" (string) The account Zcash address\n" "\nExamples:\n" + HelpExampleCli("getaccountaddress", "") + HelpExampleCli("getaccountaddress", "\"\"") @@ -264,11 +268,11 @@ UniValue setaccount(const UniValue& params, bool fHelp) "setaccount \"zcashaddress\" \"account\"\n" "\nDEPRECATED. Sets the account associated with the given address.\n" "\nArguments:\n" - "1. \"zcashaddress\" (string, required) The zcash address to be associated with an account.\n" + "1. \"zcashaddress\" (string, required) The Zcash 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", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"tabby\"") - + HelpExampleRpc("setaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", \"tabby\"") + + HelpExampleCli("setaccount", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\" \"tabby\"") + + HelpExampleRpc("setaccount", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", \"tabby\"") ); LOCK2(cs_main, pwalletMain->cs_wallet); @@ -310,12 +314,12 @@ UniValue getaccount(const UniValue& params, bool fHelp) "getaccount \"zcashaddress\"\n" "\nDEPRECATED. Returns the account associated with the given address.\n" "\nArguments:\n" - "1. \"zcashaddress\" (string, required) The zcash address for account lookup.\n" + "1. \"zcashaddress\" (string, required) The Zcash address for account lookup.\n" "\nResult:\n" "\"accountname\" (string) the account address\n" "\nExamples:\n" - + HelpExampleCli("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\"") - + HelpExampleRpc("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\"") + + HelpExampleCli("getaccount", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\"") + + HelpExampleRpc("getaccount", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\"") ); LOCK2(cs_main, pwalletMain->cs_wallet); @@ -345,7 +349,7 @@ UniValue getaddressesbyaccount(const UniValue& params, bool fHelp) "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" - " \"zcashaddress\" (string) a zcash address associated with the given account\n" + " \"zcashaddress\" (string) a Zcash address associated with the given account\n" " ,...\n" "]\n" "\nExamples:\n" @@ -425,22 +429,22 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp) "\nSend an amount to a given address. The amount is a real and is rounded to the nearest 0.00000001\n" + HelpRequiringPassphrase() + "\nArguments:\n" - "1. \"zcashaddress\" (string, required) The zcash address to send to.\n" - "2. \"amount\" (numeric, required) The amount in btc to send. eg 0.1\n" + "1. \"zcashaddress\" (string, required) The Zcash address to send to.\n" + "2. \"amount\" (numeric, required) The amount in " + CURRENCY_UNIT + " to send. eg 0.1\n" "3. \"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" "4. \"comment-to\" (string, optional) A comment to store the name of the person or organization \n" " to which you're sending the transaction. This is not part of the \n" " transaction, just kept in your wallet.\n" "5. subtractfeefromamount (boolean, optional, default=false) The fee will be deducted from the amount being sent.\n" - " The recipient will receive less zcash than you enter in the amount field.\n" + " The recipient will receive less Zcash than you enter in the amount field.\n" "\nResult:\n" "\"transactionid\" (string) The transaction id.\n" "\nExamples:\n" - + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") - + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"donation\" \"seans outpost\"") - + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true") - + HelpExampleRpc("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.1, \"donation\", \"seans outpost\"") + + HelpExampleCli("sendtoaddress", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") + + HelpExampleCli("sendtoaddress", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"donation\" \"seans outpost\"") + + HelpExampleCli("sendtoaddress", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true") + + HelpExampleRpc("sendtoaddress", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.1, \"donation\", \"seans outpost\"") ); LOCK2(cs_main, pwalletMain->cs_wallet); @@ -709,7 +713,7 @@ UniValue listaddressgroupings(const UniValue& params, bool fHelp) " [\n" " [\n" " \"zcashaddress\", (string) The zcash address\n" - " amount, (numeric) The amount in btc\n" + " amount, (numeric) The amount in " + CURRENCY_UNIT + "\n" " \"account\" (string, optional) The account (DEPRECATED)\n" " ]\n" " ,...\n" @@ -755,7 +759,7 @@ UniValue signmessage(const UniValue& params, bool fHelp) "\nSign a message with the private key of an address" + HelpRequiringPassphrase() + "\n" "\nArguments:\n" - "1. \"zcashaddress\" (string, required) The zcash address to use for the private key.\n" + "1. \"zcashaddress\" (string, required) The Zcash address to use for the private key.\n" "2. \"message\" (string, required) The message to create a signature of.\n" "\nResult:\n" "\"signature\" (string) The signature of the message encoded in base 64\n" @@ -763,11 +767,11 @@ UniValue signmessage(const UniValue& params, bool fHelp) "\nUnlock the wallet for 30 seconds\n" + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + "\nCreate the signature\n" - + HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"my message\"") + + + HelpExampleCli("signmessage", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\" \"my message\"") + "\nVerify the signature\n" - + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"signature\" \"my message\"") + + + HelpExampleCli("verifymessage", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\" \"signature\" \"my message\"") + "\nAs json rpc\n" - + HelpExampleRpc("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", \"my message\"") + + HelpExampleRpc("signmessage", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", \"my message\"") ); LOCK2(cs_main, pwalletMain->cs_wallet); @@ -808,21 +812,21 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp) if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( "getreceivedbyaddress \"zcashaddress\" ( minconf )\n" - "\nReturns the total amount received by the given zcashaddress in transactions with at least minconf confirmations.\n" + "\nReturns the total amount received by the given Zcash address in transactions with at least minconf confirmations.\n" "\nArguments:\n" - "1. \"zcashaddress\" (string, required) The zcash address for transactions.\n" + "1. \"zcashaddress\" (string, required) The Zcash address for transactions.\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 btc received at this address.\n" + "amount (numeric) The total amount in " + CURRENCY_UNIT + " received at this address.\n" "\nExamples:\n" "\nThe amount from transactions with at least 1 confirmation\n" - + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\"") + + + HelpExampleCli("getreceivedbyaddress", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\"") + "\nThe amount including unconfirmed transactions, zero confirmations\n" - + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" 0") + - "\nThe amount with at least 6 confirmation, very safe\n" - + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" 6") + + + HelpExampleCli("getreceivedbyaddress", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\" 0") + + "\nThe amount with at least 6 confirmations, very safe\n" + + HelpExampleCli("getreceivedbyaddress", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\" 6") + "\nAs a json rpc call\n" - + HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", 6") + + HelpExampleRpc("getreceivedbyaddress", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", 6") ); LOCK2(cs_main, pwalletMain->cs_wallet); @@ -871,7 +875,7 @@ UniValue getreceivedbyaccount(const UniValue& params, bool fHelp) "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 btc received for this account.\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", "\"\"") + @@ -961,7 +965,7 @@ UniValue getbalance(const UniValue& params, bool fHelp) "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 btc received for this account.\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", "") + @@ -1047,14 +1051,15 @@ UniValue movecmd(const UniValue& params, bool fHelp) "\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. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n" - "4. \"comment\" (string, optional) An optional comment, stored in the wallet only.\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 btc from the default account to the account named tabby\n" + "\nMove 0.01 " + CURRENCY_UNIT + " from the default account to the account named tabby\n" + HelpExampleCli("move", "\"\" \"tabby\" 0.01") + - "\nMove 0.01 btc timotei to akiko with a comment and funds have 6 confirmations\n" + "\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!\"") @@ -1115,13 +1120,13 @@ UniValue sendfrom(const UniValue& params, bool fHelp) if (fHelp || params.size() < 3 || params.size() > 6) throw runtime_error( "sendfrom \"fromaccount\" \"tozcashaddress\" amount ( minconf \"comment\" \"comment-to\" )\n" - "\nDEPRECATED (use sendtoaddress). Sent an amount from an account to a zcash address.\n" + "\nDEPRECATED (use sendtoaddress). Sent an amount from an account to a Zcash 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. \"tozcashaddress\" (string, required) The zcash address to send funds to.\n" - "3. amount (numeric, required) The amount in btc. (transaction fee is added on top).\n" + "2. \"tozcashaddress\" (string, required) The Zcash 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" @@ -1131,12 +1136,12 @@ UniValue sendfrom(const UniValue& params, bool fHelp) "\nResult:\n" "\"transactionid\" (string) The transaction id.\n" "\nExamples:\n" - "\nSend 0.01 btc from the default account to the address, must have at least 1 confirmation\n" - + HelpExampleCli("sendfrom", "\"\" \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.01") + + "\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\" \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.01 6 \"donation\" \"seans outpost\"") + + + HelpExampleCli("sendfrom", "\"tabby\" \"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.01 6 \"donation\" \"seans outpost\"") + "\nAs a json rpc call\n" - + HelpExampleRpc("sendfrom", "\"tabby\", \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.01, 6, \"donation\", \"seans outpost\"") + + HelpExampleRpc("sendfrom", "\"tabby\", \"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.01, 6, \"donation\", \"seans outpost\"") ); LOCK2(cs_main, pwalletMain->cs_wallet); @@ -1186,14 +1191,14 @@ UniValue sendmany(const UniValue& params, bool fHelp) "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. \"amounts\" (string, required) A json object with addresses and amounts\n" " {\n" - " \"address\":amount (numeric) The zcash address is the key, the numeric amount in btc is the value\n" + " \"address\":amount (numeric) The Zcash address is the key, the numeric amount in " + CURRENCY_UNIT + " is the value\n" " ,...\n" " }\n" "3. minconf (numeric, optional, default=1) Only use the balance confirmed at least this many times.\n" "4. \"comment\" (string, optional) A comment\n" "5. subtractfeefromamount (string, optional) A json array with addresses.\n" " The fee will be equally deducted from the amount of each selected address.\n" - " Those recipients will receive less zcashs than you enter in their corresponding amount field.\n" + " Those recipients will receive less Zcash than you enter in their corresponding amount field.\n" " If no addresses are specified here, the sender pays the fee.\n" " [\n" " \"address\" (string) Subtract fee from this address\n" @@ -1204,13 +1209,13 @@ UniValue sendmany(const UniValue& params, bool fHelp) " the number of addresses.\n" "\nExamples:\n" "\nSend two amounts to two different addresses:\n" - + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"") + + + HelpExampleCli("sendmany", "\"\" \"{\\\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\\\":0.01,\\\"t1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"") + "\nSend two amounts to two different addresses setting the confirmation and comment:\n" - + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 6 \"testing\"") + + + HelpExampleCli("sendmany", "\"\" \"{\\\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\\\":0.01,\\\"t1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 6 \"testing\"") + "\nSend two amounts to two different addresses, subtract fee from amount:\n" - + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" \"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\",\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") + + + HelpExampleCli("sendmany", "\"\" \"{\\\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\\\":0.01,\\\"t1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" \"[\\\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\\\",\\\"t1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") + "\nAs a json rpc call\n" - + HelpExampleRpc("sendmany", "\"\", \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\", 6, \"testing\"") + + HelpExampleRpc("sendmany", "\"\", \"{\\\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\\\":0.01,\\\"t1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\", 6, \"testing\"") ); LOCK2(cs_main, pwalletMain->cs_wallet); @@ -1301,21 +1306,21 @@ UniValue addmultisigaddress(const UniValue& params, bool fHelp) "\nArguments:\n" "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n" - "2. \"keysobject\" (string, required) A json array of zcash addresses or hex-encoded public keys\n" + "2. \"keysobject\" (string, required) A json array of Zcash addresses or hex-encoded public keys\n" " [\n" - " \"address\" (string) zcash address or hex-encoded public key\n" + " \"address\" (string) Zcash 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" "\nResult:\n" - "\"zcashaddress\" (string) A zcash address associated with the keys.\n" + "\"zcashaddress\" (string) A Zcash address associated with the keys.\n" "\nExamples:\n" "\nAdd a multisig address from 2 addresses\n" - + HelpExampleCli("addmultisigaddress", "2 \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + + + HelpExampleCli("addmultisigaddress", "2 \"[\\\"t16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"t171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + "\nAs json rpc call\n" - + HelpExampleRpc("addmultisigaddress", "2, \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + + HelpExampleRpc("addmultisigaddress", "2, \"[\\\"t16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"t171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") ; throw runtime_error(msg); } @@ -1488,7 +1493,7 @@ UniValue listreceivedbyaddress(const UniValue& params, bool fHelp) " \"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 btc received by the address\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" " ,...\n" @@ -1664,17 +1669,17 @@ UniValue listtransactions(const UniValue& params, bool fHelp) " {\n" " \"account\":\"accountname\", (string) DEPRECATED. The account name associated with the transaction. \n" " It will be \"\" for the default account.\n" - " \"address\":\"zcashaddress\", (string) The zcash address of the transaction. Not present for \n" + " \"address\":\"zcashaddress\", (string) The Zcash 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" - " \"amount\": x.xxx, (numeric) The amount in btc. This is negative for the 'send' category, and for the\n" + " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the\n" " 'move' category for moves outbound. It is positive for the 'receive' category,\n" " and for the 'move' category for inbound funds.\n" " \"vout\" : n, (numeric) the vout value\n" - " \"fee\": x.xxx, (numeric) The amount of the fee in btc. This is negative and only available for the \n" + " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" " 'send' category of transactions.\n" " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and \n" " 'receive' category of transactions.\n" @@ -1864,12 +1869,12 @@ UniValue listsinceblock(const UniValue& params, bool fHelp) "{\n" " \"transactions\": [\n" " \"account\":\"accountname\", (string) DEPRECATED. The account name associated with the transaction. Will be \"\" for the default account.\n" - " \"address\":\"zcashaddress\", (string) The zcash address of the transaction. Not present for move transactions (category = move).\n" + " \"address\":\"zcashaddress\", (string) The Zcash 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" - " \"amount\": x.xxx, (numeric) The amount in btc. This is negative for the 'send' category, and for the 'move' category for moves \n" + " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the 'move' category for moves \n" " outbound. It is positive for the 'receive' category, and for the 'move' category for inbound funds.\n" " \"vout\" : n, (numeric) the vout value\n" - " \"fee\": x.xxx, (numeric) The amount of the fee in btc. This is negative and only available for the 'send' category of transactions.\n" + " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the 'send' category of transactions.\n" " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and 'receive' category of transactions.\n" " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive' category of transactions.\n" " \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive' category of transactions.\n" @@ -1952,7 +1957,7 @@ UniValue gettransaction(const UniValue& params, bool fHelp) "2. \"includeWatchonly\" (bool, optional, default=false) Whether to include watchonly addresses in balance calculation and details[]\n" "\nResult:\n" "{\n" - " \"amount\" : x.xxx, (numeric) The transaction amount in btc\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" " \"blockindex\" : xx, (numeric) The block index\n" @@ -1963,9 +1968,9 @@ UniValue gettransaction(const UniValue& params, bool fHelp) " \"details\" : [\n" " {\n" " \"account\" : \"accountname\", (string) DEPRECATED. The account name involved in the transaction, can be \"\" for the default account.\n" - " \"address\" : \"zcashaddress\", (string) The zcash address involved in the transaction\n" + " \"address\" : \"zcashaddress\", (string) The Zcash address involved in the transaction\n" " \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n" - " \"amount\" : x.xxx (numeric) The amount in btc\n" + " \"amount\" : x.xxx (numeric) The amount in " + CURRENCY_UNIT + "\n" " \"vout\" : n, (numeric) the vout value\n" " }\n" " ,...\n" @@ -2123,7 +2128,7 @@ UniValue walletpassphrase(const UniValue& params, bool fHelp) throw runtime_error( "walletpassphrase \"passphrase\" timeout\n" "\nStores the wallet decryption key in memory for 'timeout' seconds.\n" - "This is needed prior to performing transactions related to private keys such as sending zcash\n" + "This is needed prior to performing transactions related to private keys such as sending Zcash\n" "\nArguments:\n" "1. \"passphrase\" (string, required) The wallet passphrase\n" "2. timeout (numeric, required) The time to keep the decryption key in seconds.\n" @@ -2237,7 +2242,7 @@ UniValue walletlock(const UniValue& params, bool fHelp) "\nSet the passphrase for 2 minutes to perform a transaction\n" + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") + "\nPerform a send (requires passphrase set)\n" - + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 1.0") + + + HelpExampleCli("sendtoaddress", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 1.0") + "\nClear the passphrase since we are done before 2 minutes is up\n" + HelpExampleCli("walletlock", "") + "\nAs json rpc call\n" @@ -2288,7 +2293,7 @@ UniValue encryptwallet(const UniValue& params, bool fHelp) "\nExamples:\n" "\nEncrypt you wallet\n" + HelpExampleCli("encryptwallet", "\"my pass phrase\"") + - "\nNow set the passphrase to use the wallet, such as for signing or sending zcash\n" + "\nNow set the passphrase to use the wallet, such as for signing or sending Zcash\n" + HelpExampleCli("walletpassphrase", "\"my pass phrase\"") + "\nNow we can so something like sign\n" + HelpExampleCli("signmessage", "\"zcashaddress\" \"test message\"") + @@ -2339,7 +2344,7 @@ UniValue lockunspent(const UniValue& params, bool fHelp) "lockunspent unlock [{\"txid\":\"txid\",\"vout\":n},...]\n" "\nUpdates list of temporarily unspendable outputs.\n" "Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n" - "A locked transaction output will not be chosen by automatic coin selection, when spending zcash.\n" + "A locked transaction output will not be chosen by automatic coin selection, when spending Zcash.\n" "Locks are stored in memory only. Nodes start with zero locked outputs, and the locked output list\n" "is always cleared (by virtue of process exit) when a node stops or fails.\n" "Also see the listunspent call\n" @@ -2472,7 +2477,7 @@ UniValue settxfee(const UniValue& params, bool fHelp) "settxfee amount\n" "\nSet the transaction fee per kB.\n" "\nArguments:\n" - "1. amount (numeric, required) The transaction fee in BTC/kB rounded to the nearest 0.00000001\n" + "1. amount (numeric, required) The transaction fee in " + CURRENCY_UNIT + "/kB rounded to the nearest 0.00000001\n" "\nResult\n" "true|false (boolean) Returns true if successful\n" "\nExamples:\n" @@ -2501,14 +2506,14 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp) "\nResult:\n" "{\n" " \"walletversion\": xxxxx, (numeric) the wallet version\n" - " \"balance\": xxxxxxx, (numeric) the total confirmed zcash balance of the wallet\n" - " \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed zcash balance of the wallet\n" - " \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet\n" + " \"balance\": xxxxxxx, (numeric) the total confirmed balance of the wallet in " + CURRENCY_UNIT + "\n" + " \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + CURRENCY_UNIT + "\n" + " \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in " + CURRENCY_UNIT + "\n" " \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n" " \"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 ZEC/KB\n" + " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in KMD/KB\n" "}\n" "\nExamples:\n" + HelpExampleCli("getwalletinfo", "") @@ -2574,9 +2579,9 @@ UniValue listunspent(const UniValue& params, bool fHelp) "\nArguments:\n" "1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n" "2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n" - "3. \"addresses\" (string) A json array of zcash addresses to filter\n" + "3. \"addresses\" (string) A json array of Zcash addresses to filter\n" " [\n" - " \"address\" (string) zcash address\n" + " \"address\" (string) Zcash address\n" " ,...\n" " ]\n" "\nResult\n" @@ -2584,10 +2589,11 @@ UniValue listunspent(const UniValue& params, bool fHelp) " {\n" " \"txid\" : \"txid\", (string) the transaction id \n" " \"vout\" : n, (numeric) the vout value\n" - " \"address\" : \"address\", (string) the zcash address\n" + " \"generated\" : true|false (boolean) true if txout is a coinbase transaction output\n" + " \"address\" : \"address\", (string) the Zcash 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 btc\n" + " \"amount\" : x.xxx, (numeric) the transaction amount in " + CURRENCY_UNIT + "\n" " \"confirmations\" : n (numeric) The number of confirmations\n" " }\n" " ,...\n" @@ -2595,8 +2601,8 @@ UniValue listunspent(const UniValue& params, bool fHelp) "\nExamples\n" + HelpExampleCli("listunspent", "") - + HelpExampleCli("listunspent", "6 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") - + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + + HelpExampleCli("listunspent", "6 9999999 \"[\\\"t1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"t1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"t1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"t1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") ); RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)(UniValue::VNUM)(UniValue::VARR)); @@ -2646,6 +2652,7 @@ UniValue listunspent(const UniValue& params, bool fHelp) 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())); CTxDestination address; if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); @@ -2826,12 +2833,6 @@ UniValue zc_benchmark(const UniValue& params, bool fHelp) std::vector sample_times; - if (benchmarktype == "createjoinsplit") { - /* Load the proving now key so that it doesn't happen as part of the - * first joinsplit. */ - pzcashParams->loadProvingKey(); - } - JSDescription samplejoinsplit; if (benchmarktype == "verifyjoinsplit") { @@ -2869,13 +2870,36 @@ UniValue zc_benchmark(const UniValue& params, bool fHelp) } else if (benchmarktype == "verifyequihash") { sample_times.push_back(benchmark_verify_equihash()); } else if (benchmarktype == "validatelargetx") { - sample_times.push_back(benchmark_large_tx()); + // Number of inputs in the spending transaction that we will simulate + int nInputs = 555; + if (params.size() >= 3) { + nInputs = params[2].get_int(); + } + sample_times.push_back(benchmark_large_tx(nInputs)); } else if (benchmarktype == "trydecryptnotes") { int nAddrs = params[2].get_int(); sample_times.push_back(benchmark_try_decrypt_notes(nAddrs)); } else if (benchmarktype == "incnotewitnesses") { int nTxs = params[2].get_int(); sample_times.push_back(benchmark_increment_note_witnesses(nTxs)); + } else if (benchmarktype == "connectblockslow") { + if (Params().NetworkIDString() != "regtest") { + throw JSONRPCError(RPC_TYPE_ERROR, "Benchmark must be run in regtest mode"); + } + sample_times.push_back(benchmark_connectblock_slow()); + } else if (benchmarktype == "sendtoaddress") { + if (Params().NetworkIDString() != "regtest") { + throw JSONRPCError(RPC_TYPE_ERROR, "Benchmark must be run in regtest mode"); + } + auto amount = AmountFromValue(params[2]); + sample_times.push_back(benchmark_sendtoaddress(amount)); + } else if (benchmarktype == "loadwallet") { + if (Params().NetworkIDString() != "regtest") { + throw JSONRPCError(RPC_TYPE_ERROR, "Benchmark must be run in regtest mode"); + } + sample_times.push_back(benchmark_loadwallet()); + } else if (benchmarktype == "listunspent") { + sample_times.push_back(benchmark_listunspent()); } else { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid benchmarktype"); } @@ -2938,7 +2962,7 @@ UniValue zc_raw_receive(const UniValue& params, bool fHelp) } } - ZCNoteDecryption decryptor(k.viewing_key()); + ZCNoteDecryption decryptor(k.receiving_key()); NotePlaintext npt = NotePlaintext::decrypt( decryptor, @@ -3111,7 +3135,8 @@ UniValue zc_raw_joinsplit(const UniValue& params, bool fHelp) // Empty output script. CScript scriptCode; CTransaction signTx(mtx); - uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL); + auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); + uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId); // Add the signature assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, @@ -3173,6 +3198,7 @@ UniValue zc_raw_keygen(const UniValue& params, bool fHelp) "Output: {\n" " \"zcaddress\": zcaddr,\n" " \"zcsecretkey\": zcsecretkey,\n" + " \"zcviewingkey\": zcviewingkey,\n" "}\n" ); } @@ -3181,18 +3207,14 @@ UniValue zc_raw_keygen(const UniValue& params, bool fHelp) auto addr = k.address(); auto viewing_key = k.viewing_key(); - CDataStream viewing(SER_NETWORK, PROTOCOL_VERSION); - - viewing << viewing_key; - CZCPaymentAddress pubaddr(addr); CZCSpendingKey spendingkey(k); - std::string viewing_hex = HexStr(viewing.begin(), viewing.end()); + CZCViewingKey viewingkey(viewing_key); UniValue result(UniValue::VOBJ); result.push_back(Pair("zcaddress", pubaddr.ToString())); result.push_back(Pair("zcsecretkey", spendingkey.ToString())); - result.push_back(Pair("zcviewingkey", viewing_hex)); + result.push_back(Pair("zcviewingkey", viewingkey.ToString())); return result; } @@ -3231,9 +3253,10 @@ UniValue z_listaddresses(const UniValue& params, bool fHelp) if (fHelp || params.size() > 1) throw runtime_error( - "z_listaddresses\n" + "z_listaddresses ( includeWatchonly )\n" "\nReturns the list of zaddr belonging to the wallet.\n" "\nArguments:\n" + "1. includeWatchonly (bool, optional, default=false) Also include watchonly addresses (see 'z_importviewingkey')\n" "\nResult:\n" "[ (json array of string)\n" " \"zaddr\" (string) a zaddr belonging to the wallet\n" @@ -3246,16 +3269,23 @@ UniValue z_listaddresses(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); + bool fIncludeWatchonly = false; + if (params.size() > 0) { + fIncludeWatchonly = params[0].get_bool(); + } + UniValue ret(UniValue::VARR); std::set addresses; pwalletMain->GetPaymentAddresses(addresses); for (auto addr : addresses ) { - ret.push_back(CZCPaymentAddress(addr).ToString()); + if (fIncludeWatchonly || pwalletMain->HaveSpendingKey(addr)) { + ret.push_back(CZCPaymentAddress(addr).ToString()); + } } return ret; } -CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1) { +CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1, bool ignoreUnspendable=true) { set setAddress; vector vecOutputs; CAmount balance = 0; @@ -3277,6 +3307,10 @@ CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1) { continue; } + if (ignoreUnspendable && !out.fSpendable) { + continue; + } + if (setAddress.size()) { CTxDestination address; if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { @@ -3294,11 +3328,11 @@ CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1) { return balance; } -CAmount getBalanceZaddr(std::string address, int minDepth = 1) { +CAmount getBalanceZaddr(std::string address, int minDepth = 1, bool ignoreUnspendable=true) { CAmount balance = 0; std::vector entries; LOCK2(cs_main, pwalletMain->cs_wallet); - pwalletMain->GetFilteredNotes(entries, address, minDepth); + pwalletMain->GetFilteredNotes(entries, address, minDepth, true, ignoreUnspendable); for (auto & entry : entries) { balance += CAmount(entry.plaintext.value); } @@ -3324,6 +3358,9 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) " \"amount\": xxxxx, (numeric) the amount of value in the note\n" " \"memo\": xxxxx, (string) hexademical string representation of memo field\n" "}\n" + "\nExamples:\n" + + HelpExampleCli("z_listreceivedbyaddress", "\"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") + + HelpExampleRpc("z_listreceivedbyaddress", "\"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") ); LOCK2(cs_main, pwalletMain->cs_wallet); @@ -3347,14 +3384,14 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr."); } - if (!pwalletMain->HaveSpendingKey(zaddr)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key not found."); + if (!(pwalletMain->HaveSpendingKey(zaddr) || pwalletMain->HaveViewingKey(zaddr))) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key or viewing key not found."); } UniValue result(UniValue::VARR); std::vector entries; - pwalletMain->GetFilteredNotes(entries, fromaddress, nMinDepth, false); + pwalletMain->GetFilteredNotes(entries, fromaddress, nMinDepth, false, false); for (CNotePlaintextEntry & entry : entries) { UniValue obj(UniValue::VOBJ); obj.push_back(Pair("txid",entry.jsop.hash.ToString())); @@ -3376,11 +3413,13 @@ UniValue z_getbalance(const UniValue& params, bool fHelp) throw runtime_error( "z_getbalance \"address\" ( minconf )\n" "\nReturns the balance of a taddr or zaddr belonging to the node’s wallet.\n" + "\nCAUTION: If address is a watch-only zaddr, the returned balance may be larger than the actual balance," + "\nbecause spends cannot be detected with incoming viewing keys.\n" "\nArguments:\n" "1. \"address\" (string) The selected address. It may be a transparent or private address.\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 ZEC received for this address.\n" + "amount (numeric) The total amount in KMD received for this address.\n" "\nExamples:\n" "\nThe total amount received by address \"myaddress\"\n" + HelpExampleCli("z_getbalance", "\"myaddress\"") + @@ -3413,16 +3452,16 @@ UniValue z_getbalance(const UniValue& params, bool fHelp) } catch (const std::runtime_error&) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr."); } - if (!pwalletMain->HaveSpendingKey(zaddr)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key not found."); + if (!(pwalletMain->HaveSpendingKey(zaddr) || pwalletMain->HaveViewingKey(zaddr))) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key or viewing key not found."); } } CAmount nBalance = 0; if (fromTaddr) { - nBalance = getBalanceTaddr(fromaddress, nMinDepth); + nBalance = getBalanceTaddr(fromaddress, nMinDepth, false); } else { - nBalance = getBalanceZaddr(fromaddress, nMinDepth); + nBalance = getBalanceZaddr(fromaddress, nMinDepth, false); } return ValueFromAmount(nBalance); @@ -3434,12 +3473,15 @@ UniValue z_gettotalbalance(const UniValue& params, bool fHelp) if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - if (fHelp || params.size() > 1) + if (fHelp || params.size() > 2) throw runtime_error( - "z_gettotalbalance ( minconf )\n" + "z_gettotalbalance ( minconf includeWatchonly )\n" "\nReturn the total value of funds stored in the node’s wallet.\n" + "\nCAUTION: If the wallet contains watch-only zaddrs, the returned private balance may be larger than the actual balance," + "\nbecause spends cannot be detected with incoming viewing keys.\n" "\nArguments:\n" "1. minconf (numeric, optional, default=1) Only include private and transparent transactions confirmed at least this many times.\n" + "2. includeWatchonly (bool, optional, default=false) Also include balance in watchonly addresses (see 'importaddress' and 'z_importviewingkey')\n" "\nResult:\n" "{\n" " \"transparent\": xxxxx, (numeric) the total balance of transparent funds\n" @@ -3458,21 +3500,26 @@ UniValue z_gettotalbalance(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); int nMinDepth = 1; - if (params.size() == 1) { + if (params.size() > 0) { nMinDepth = params[0].get_int(); } if (nMinDepth < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0"); } + bool fIncludeWatchonly = false; + if (params.size() > 1) { + fIncludeWatchonly = params[1].get_bool(); + } + // getbalance and "getbalance * 1 true" should return the same number // but they don't because wtx.GetAmounts() does not handle tx where there are no outputs // pwalletMain->GetBalance() does not accept min depth parameter // so we use our own method to get balance of utxos. - CAmount nBalance = getBalanceTaddr("", nMinDepth); - CAmount nPrivateBalance = getBalanceZaddr("", nMinDepth); + CAmount nBalance = getBalanceTaddr("", nMinDepth, !fIncludeWatchonly); + CAmount nPrivateBalance = getBalanceZaddr("", nMinDepth, !fIncludeWatchonly); uint64_t interest = komodo_interestsum(); - CAmount nTotalBalance = nBalance + nPrivateBalance + interest; + CAmount nTotalBalance = nBalance + nPrivateBalance; UniValue result(UniValue::VOBJ); result.push_back(Pair("transparent", FormatMoney(nBalance))); result.push_back(Pair("interest", FormatMoney(interest))); @@ -3495,6 +3542,9 @@ UniValue z_getoperationresult(const UniValue& params, bool fHelp) "1. \"operationid\" (array, optional) A list of operation ids we are interested in. If not provided, examine all operations known to the node.\n" "\nResult:\n" "\" [object, ...]\" (array) A list of JSON objects\n" + "\nExamples:\n" + + HelpExampleCli("z_getoperationresult", "'[\"operationid\", ... ]'") + + HelpExampleRpc("z_getoperationresult", "'[\"operationid\", ... ]'") ); // This call will remove finished operations @@ -3515,6 +3565,9 @@ UniValue z_getoperationstatus(const UniValue& params, bool fHelp) "1. \"operationid\" (array, optional) A list of operation ids we are interested in. If not provided, examine all operations known to the node.\n" "\nResult:\n" "\" [object, ...]\" (array) A list of JSON objects\n" + "\nExamples:\n" + + HelpExampleCli("z_getoperationstatus", "'[\"operationid\", ... ]'") + + HelpExampleRpc("z_getoperationstatus", "'[\"operationid\", ... ]'") ); // This call is idempotent so we don't want to remove finished operations @@ -3607,7 +3660,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) "2. \"amounts\" (array, required) An array of json objects representing the amounts to send.\n" " [{\n" " \"address\":address (string, required) The address is a taddr or zaddr\n" - " \"amount\":amount (numeric, required) The numeric amount in ZEC is the value\n" + " \"amount\":amount (numeric, required) The numeric amount in KMD is the value\n" " \"memo\":memo (string, optional) If the address is a zaddr, raw data represented in hexadecimal string format\n" " }, ... ]\n" "3. minconf (numeric, optional, default=1) Only use funds confirmed at least this many times.\n" @@ -3615,6 +3668,9 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) + strprintf("%s", FormatMoney(ASYNC_RPC_OPERATION_DEFAULT_MINERS_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" + + HelpExampleCli("z_sendmany", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" '[{\"address\": \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\" ,\"amount\": 5.0}]'") + + HelpExampleRpc("z_sendmany", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", [{\"address\": \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\" ,\"amount\": 5.0}]") ); LOCK2(cs_main, pwalletMain->cs_wallet); @@ -3688,7 +3744,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) if (!memoValue.isNull()) { memo = memoValue.get_str(); if (!isZaddr) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo can not be used with a taddr. It can only be used with a zaddr."); + throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo cannot be used with a taddr. It can only be used with a zaddr."); } else if (!IsHex(memo)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected memo data in hexadecimal format."); } @@ -3767,15 +3823,571 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) o.push_back(Pair("fee", std::stod(FormatMoney(nFee)))); UniValue contextInfo = o; + // Contextual transaction we will build on + int nextBlockHeight = chainActive.Height() + 1; + CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextBlockHeight); + bool isShielded = !fromTaddr || zaddrRecipients.size() > 0; + if (contextualTx.nVersion == 1 && isShielded) { + contextualTx.nVersion = 2; // Tx format should support vjoinsplits + } + if (NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER)) { + contextualTx.nExpiryHeight = nextBlockHeight + expiryDelta; + } + // Create operation and add to global queue std::shared_ptr q = getAsyncRPCQueue(); - std::shared_ptr operation( new AsyncRPCOperation_sendmany(fromaddress, taddrRecipients, zaddrRecipients, nMinDepth, nFee, contextInfo) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(contextualTx, fromaddress, taddrRecipients, zaddrRecipients, nMinDepth, nFee, contextInfo) ); q->addOperation(operation); AsyncRPCOperationId operationId = operation->getId(); return operationId; } +/** +When estimating the number of coinbase utxos we can shield in a single transaction: +1. Joinsplit description is 1802 bytes. +2. Transaction overhead ~ 100 bytes +3. Spending a typical P2PKH is >=148 bytes, as defined in CTXIN_SPEND_DUST_SIZE. +4. Spending a multi-sig P2SH address can vary greatly: + https://github.com/bitcoin/bitcoin/blob/c3ad56f4e0b587d8d763af03d743fdfc2d180c9b/src/main.cpp#L517 + In real-world coinbase utxos, we consider a 3-of-3 multisig, where the size is roughly: + (3*(33+1))+3 = 105 byte redeem script + 105 + 1 + 3*(73+1) = 328 bytes of scriptSig, rounded up to 400 based on testnet experiments. +*/ +#define CTXIN_SPEND_P2SH_SIZE 400 + +#define SHIELD_COINBASE_DEFAULT_LIMIT 50 + +UniValue z_shieldcoinbase(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() < 2 || params.size() > 4) + throw runtime_error( + "z_shieldcoinbase \"fromaddress\" \"tozaddress\" ( fee ) ( limit )\n" + "\nShield transparent coinbase funds by sending to a shielded zaddr. This is an asynchronous operation and utxos" + "\nselected for shielding will be locked. If there is an error, they are unlocked. The RPC call `listlockunspent`" + "\ncan be used to return a list of locked utxos. The number of coinbase utxos selected for shielding can be limited" + "\nby the caller. If the limit parameter is set to zero, the -mempooltxinputlimit option will determine the number" + "\nof uxtos. Any limit is constrained by the consensus rule defining a maximum transaction size of " + + strprintf("%d bytes.", MAX_TX_SIZE) + + HelpRequiringPassphrase() + "\n" + "\nArguments:\n" + "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" + "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 node option -mempooltxinputlimit.\n" + "\nResult:\n" + "{\n" + " \"remainingUTXOs\": xxx (numeric) Number of coinbase utxos still available for shielding.\n" + " \"remainingValue\": xxx (numeric) Value of coinbase utxos still available for shielding.\n" + " \"shieldingUTXOs\": xxx (numeric) Number of coinbase utxos being shielded.\n" + " \"shieldingValue\": xxx (numeric) Value of coinbase utxos being shielded.\n" + " \"opid\": xxx (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("z_shieldcoinbase", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") + + HelpExampleRpc("z_shieldcoinbase", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + // Validate the from address + auto fromaddress = params[0].get_str(); + bool isFromWildcard = fromaddress == "*"; + CBitcoinAddress taddr; + if (!isFromWildcard) { + taddr = CBitcoinAddress(fromaddress); + if (!taddr.IsValid()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or \"*\"."); + } + } + + // Validate the destination address + auto destaddress = params[1].get_str(); + try { + CZCPaymentAddress pa(destaddress); + libzcash::PaymentAddress zaddr = pa.Get(); + } catch (const std::runtime_error&) { + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destaddress ); + } + + // Convert fee from currency format to zatoshis + CAmount nFee = SHIELD_COINBASE_DEFAULT_MINERS_FEE; + if (params.size() > 2) { + if (params[2].get_real() == 0.0) { + nFee = 0; + } else { + nFee = AmountFromValue( params[2] ); + } + } + + int nLimit = SHIELD_COINBASE_DEFAULT_LIMIT; + if (params.size() > 3) { + nLimit = params[3].get_int(); + if (nLimit < 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of utxos cannot be negative"); + } + } + + // Prepare to get coinbase utxos + std::vector inputs; + CAmount shieldedValue = 0; + CAmount remainingValue = 0; + size_t estimatedTxSize = 2000; // 1802 joinsplit description + tx overhead + wiggle room + size_t utxoCounter = 0; + bool maxedOutFlag = false; + size_t mempoolLimit = (nLimit != 0) ? nLimit : (size_t)GetArg("-mempooltxinputlimit", 0); + + // Set of addresses to filter utxos by + set setAddress = {}; + if (!isFromWildcard) { + setAddress.insert(taddr); + } + + // Get available utxos + vector vecOutputs; + pwalletMain->AvailableCoins(vecOutputs, true, NULL, false, true); + + // Find unspent coinbase utxos and update estimated size + BOOST_FOREACH(const COutput& out, vecOutputs) { + if (!out.fSpendable) { + continue; + } + + CTxDestination address; + if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { + continue; + } + // If taddr is not wildcard "*", filter utxos + if (setAddress.size()>0 && !setAddress.count(address)) { + continue; + } + + if (!out.tx->IsCoinBase()) { + continue; + } + + utxoCounter++; + CAmount nValue = out.tx->vout[out.i].nValue; + + if (!maxedOutFlag) { + CBitcoinAddress ba(address); + size_t increase = (ba.IsScript()) ? CTXIN_SPEND_P2SH_SIZE : CTXIN_SPEND_DUST_SIZE; + if (estimatedTxSize + increase >= MAX_TX_SIZE || + (mempoolLimit > 0 && utxoCounter > mempoolLimit)) + { + maxedOutFlag = true; + } else { + estimatedTxSize += increase; + ShieldCoinbaseUTXO utxo = {out.tx->GetHash(), out.i, nValue}; + inputs.push_back(utxo); + shieldedValue += nValue; + } + } + + if (maxedOutFlag) { + remainingValue += nValue; + } + } + + size_t numUtxos = inputs.size(); + + if (numUtxos == 0) { + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Could not find any coinbase funds to shield."); + } + + if (shieldedValue < nFee) { + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, + strprintf("Insufficient coinbase funds, have %s, which is less than miners fee %s", + FormatMoney(shieldedValue), FormatMoney(nFee))); + } + + // Check that the user specified fee is sane (if too high, it can result in error -25 absurd fee) + CAmount netAmount = shieldedValue - nFee; + if (nFee > netAmount) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Fee %s is greater than the net amount to be shielded %s", FormatMoney(nFee), FormatMoney(netAmount))); + } + + // 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))); + + // Contextual transaction we will build on + int nextBlockHeight = chainActive.Height() + 1; + CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction( + Params().GetConsensus(), nextBlockHeight); + if (contextualTx.nVersion == 1) { + contextualTx.nVersion = 2; // Tx format should support vjoinsplits + } + if (NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER)) { + contextualTx.nExpiryHeight = nextBlockHeight + expiryDelta; + } + + // Create operation and add to global queue + std::shared_ptr q = getAsyncRPCQueue(); + std::shared_ptr operation( new AsyncRPCOperation_shieldcoinbase(contextualTx, inputs, destaddress, nFee, contextInfo) ); + q->addOperation(operation); + AsyncRPCOperationId operationId = operation->getId(); + + // Return continuation information + UniValue o(UniValue::VOBJ); + o.push_back(Pair("remainingUTXOs", utxoCounter - numUtxos)); + o.push_back(Pair("remainingValue", ValueFromAmount(remainingValue))); + o.push_back(Pair("shieldingUTXOs", numUtxos)); + o.push_back(Pair("shieldingValue", ValueFromAmount(shieldedValue))); + o.push_back(Pair("opid", operationId)); + return o; +} + + +#define MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT 50 +#define MERGE_TO_ADDRESS_DEFAULT_SHIELDED_LIMIT 10 + +#define JOINSPLIT_SIZE JSDescription().GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION) + +UniValue z_mergetoaddress(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + auto fEnableMergeToAddress = fExperimentalMode && GetBoolArg("-zmergetoaddress", false); + std::string strDisabledMsg = ""; + if (!fEnableMergeToAddress) { + strDisabledMsg = "\nWARNING: z_mergetoaddress is DISABLED but can be enabled as an experimental feature.\n"; + } + + if (fHelp || params.size() < 2 || params.size() > 6) + throw runtime_error( + "z_mergetoaddress [\"fromaddress\", ... ] \"toaddress\" ( fee ) ( transparent_limit ) ( shielded_limit ) ( memo )\n" + + strDisabledMsg + + "\nMerge multiple UTXOs and notes into a single UTXO or note. Coinbase UTXOs are ignored; use `z_shieldcoinbase`" + "\nto combine those into a single note." + "\n\nThis is an asynchronous operation, and UTXOs selected for merging will be locked. If there is an error, they" + "\nare unlocked. The RPC call `listlockunspent` can be used to return a list of locked UTXOs." + "\n\nThe number of UTXOs and notes selected for merging can be limited by the caller. If the transparent limit" + "\nparameter is set to zero, the -mempooltxinputlimit option will determine the number of UTXOs. Any limit is" + "\nconstrained by the consensus rule defining a maximum transaction size of " + + strprintf("%d bytes.", MAX_TX_SIZE) + + HelpRequiringPassphrase() + "\n" + "\nArguments:\n" + "1. fromaddresses (string, required) A JSON array with addresses.\n" + " The following special strings are accepted inside the array:\n" + " - \"*\": Merge both UTXOs and notes from all addresses belonging to the wallet.\n" + " - \"ANY_TADDR\": Merge UTXOs from all t-addrs belonging to the wallet.\n" + " - \"ANY_ZADDR\": Merge notes from all z-addrs belonging to the wallet.\n" + " If a special string is given, any given addresses of that type will be ignored.\n" + " [\n" + " \"address\" (string) Can be a t-addr or a z-addr\n" + " ,...\n" + " ]\n" + "2. \"toaddress\" (string, required) The t-addr or z-addr 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" + "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 node option -mempooltxinputlimit.\n" + "4. shielded_limit (numeric, optional, default=" + + strprintf("%d", MERGE_TO_ADDRESS_DEFAULT_SHIELDED_LIMIT) + ") Limit on the maximum number of notes to merge. Set to 0 to merge as many as will fit in the transaction.\n" + "5. \"memo\" (string, optional) Encoded as hex. When toaddress is a z-addr, this will be stored in the memo field of the new note.\n" + "\nResult:\n" + "{\n" + " \"remainingUTXOs\": xxx (numeric) Number of UTXOs still available for merging.\n" + " \"remainingTransparentValue\": xxx (numeric) Value of UTXOs still available for merging.\n" + " \"remainingNotes\": xxx (numeric) Number of notes still available for merging.\n" + " \"remainingShieldedValue\": xxx (numeric) Value of notes still available for merging.\n" + " \"mergingUTXOs\": xxx (numeric) Number of UTXOs being merged.\n" + " \"mergingTransparentValue\": xxx (numeric) Value of UTXOs being merged.\n" + " \"mergingNotes\": xxx (numeric) Number of notes being merged.\n" + " \"mergingShieldedValue\": xxx (numeric) Value of notes being merged.\n" + " \"opid\": xxx (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("z_mergetoaddress", "'[\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\"]' ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf") + + HelpExampleRpc("z_mergetoaddress", "[\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\"], \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") + ); + + if (!fEnableMergeToAddress) { + throw JSONRPCError(RPC_WALLET_ERROR, "Error: z_mergetoaddress is disabled."); + } + + LOCK2(cs_main, pwalletMain->cs_wallet); + + bool useAny = false; + bool useAnyUTXO = false; + bool useAnyNote = false; + std::set taddrs = {}; + std::set zaddrs = {}; + + UniValue addresses = params[0].get_array(); + if (addresses.size()==0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, fromaddresses array is empty."); + + // Keep track of addresses to spot duplicates + std::set setAddress; + + // Sources + for (const UniValue& o : addresses.getValues()) { + if (!o.isStr()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected string"); + + std::string address = o.get_str(); + if (address == "*") { + useAny = true; + } else if (address == "ANY_TADDR") { + useAnyUTXO = true; + } else if (address == "ANY_ZADDR") { + useAnyNote = true; + } else { + CBitcoinAddress taddr(address); + if (taddr.IsValid()) { + // Ignore any listed t-addrs if we are using all of them + if (!(useAny || useAnyUTXO)) { + taddrs.insert(taddr); + } + } else { + try { + CZCPaymentAddress zaddr(address); + // Ignore listed z-addrs if we are using all of them + if (!(useAny || useAnyNote)) { + zaddrs.insert(zaddr.Get()); + } + } catch (const std::runtime_error&) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, + string("Invalid parameter, unknown address format: ") + address); + } + } + } + + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ") + address); + setAddress.insert(address); + } + + // Validate the destination address + auto destaddress = params[1].get_str(); + bool isToZaddr = false; + CBitcoinAddress taddr(destaddress); + if (!taddr.IsValid()) { + try { + CZCPaymentAddress zaddr(destaddress); + zaddr.Get(); + isToZaddr = true; + } catch (const std::runtime_error&) { + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destaddress ); + } + } + + // Convert fee from currency format to zatoshis + CAmount nFee = SHIELD_COINBASE_DEFAULT_MINERS_FEE; + if (params.size() > 2) { + if (params[2].get_real() == 0.0) { + nFee = 0; + } else { + nFee = AmountFromValue( params[2] ); + } + } + + int nUTXOLimit = MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT; + if (params.size() > 3) { + nUTXOLimit = params[3].get_int(); + if (nUTXOLimit < 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of UTXOs cannot be negative"); + } + } + + int nNoteLimit = MERGE_TO_ADDRESS_DEFAULT_SHIELDED_LIMIT; + if (params.size() > 4) { + nNoteLimit = params[4].get_int(); + if (nNoteLimit < 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of notes cannot be negative"); + } + } + + std::string memo; + if (params.size() > 5) { + memo = params[5].get_str(); + if (!isToZaddr) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo can not be used with a taddr. It can only be used with a zaddr."); + } else if (!IsHex(memo)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected memo data in hexadecimal format."); + } + if (memo.length() > ZC_MEMO_SIZE*2) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, size of memo is larger than maximum allowed %d", ZC_MEMO_SIZE )); + } + } + + MergeToAddressRecipient recipient(destaddress, memo); + + // Prepare to get UTXOs and notes + std::vector utxoInputs; + std::vector noteInputs; + CAmount mergedUTXOValue = 0; + CAmount mergedNoteValue = 0; + CAmount remainingUTXOValue = 0; + CAmount remainingNoteValue = 0; + size_t utxoCounter = 0; + size_t noteCounter = 0; + bool maxedOutUTXOsFlag = false; + bool maxedOutNotesFlag = false; + size_t mempoolLimit = (nUTXOLimit != 0) ? nUTXOLimit : (size_t)GetArg("-mempooltxinputlimit", 0); + + size_t estimatedTxSize = 200; // tx overhead + wiggle room + if (isToZaddr) { + estimatedTxSize += JOINSPLIT_SIZE; + } + + if (useAny || useAnyUTXO || taddrs.size() > 0) { + // Get available utxos + vector vecOutputs; + pwalletMain->AvailableCoins(vecOutputs, true, NULL, false, false); + + // Find unspent utxos and update estimated size + for (const COutput& out : vecOutputs) { + if (!out.fSpendable) { + continue; + } + + CTxDestination address; + if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { + continue; + } + // If taddr is not wildcard "*", filter utxos + if (taddrs.size() > 0 && !taddrs.count(address)) { + continue; + } + + utxoCounter++; + CAmount nValue = out.tx->vout[out.i].nValue; + + if (!maxedOutUTXOsFlag) { + CBitcoinAddress ba(address); + size_t increase = (ba.IsScript()) ? CTXIN_SPEND_P2SH_SIZE : CTXIN_SPEND_DUST_SIZE; + if (estimatedTxSize + increase >= MAX_TX_SIZE || + (mempoolLimit > 0 && utxoCounter > mempoolLimit)) + { + maxedOutUTXOsFlag = true; + } else { + estimatedTxSize += increase; + COutPoint utxo(out.tx->GetHash(), out.i); + utxoInputs.emplace_back(utxo, nValue); + mergedUTXOValue += nValue; + } + } + + if (maxedOutUTXOsFlag) { + remainingUTXOValue += nValue; + } + } + } + + if (useAny || useAnyNote || zaddrs.size() > 0) { + // Get available notes + std::vector entries; + pwalletMain->GetFilteredNotes(entries, zaddrs); + + // Find unspent notes and update estimated size + for (CNotePlaintextEntry& entry : entries) { + noteCounter++; + CAmount nValue = entry.plaintext.value; + + if (!maxedOutNotesFlag) { + // If we haven't added any notes yet and the merge is to a + // z-address, we have already accounted for the first JoinSplit. + size_t increase = (noteInputs.empty() && !isToZaddr) || (noteInputs.size() % 2 == 0) ? JOINSPLIT_SIZE : 0; + if (estimatedTxSize + increase >= MAX_TX_SIZE || + (nNoteLimit > 0 && noteCounter > nNoteLimit)) + { + maxedOutNotesFlag = true; + } else { + estimatedTxSize += increase; + SpendingKey zkey; + pwalletMain->GetSpendingKey(entry.address, zkey); + noteInputs.emplace_back(entry.jsop, entry.plaintext.note(entry.address), nValue, zkey); + mergedNoteValue += nValue; + } + } + + if (maxedOutNotesFlag) { + remainingNoteValue += nValue; + } + } + } + + size_t numUtxos = utxoInputs.size(); + size_t numNotes = noteInputs.size(); + + if (numUtxos == 0 && numNotes == 0) { + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Could not find any funds to merge."); + } + + // Sanity check: Don't do anything if: + // - We only have one from address + // - It's equal to toaddress + // - The address only contains a single UTXO or note + if (setAddress.size() == 1 && setAddress.count(destaddress) && (numUtxos + numNotes) == 1) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Destination address is also the only source address, and all its funds are already merged."); + } + + CAmount mergedValue = mergedUTXOValue + mergedNoteValue; + if (mergedValue < nFee) { + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, + strprintf("Insufficient funds, have %s, which is less than miners fee %s", + FormatMoney(mergedValue), FormatMoney(nFee))); + } + + // Check that the user specified fee is sane (if too high, it can result in error -25 absurd fee) + CAmount netAmount = mergedValue - nFee; + if (nFee > netAmount) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Fee %s is greater than the net amount to be shielded %s", FormatMoney(nFee), FormatMoney(netAmount))); + } + + // 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))); + + // Contextual transaction we will build on + int nextBlockHeight = chainActive.Height() + 1; + CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction( + Params().GetConsensus(), + nextBlockHeight); + bool isShielded = numNotes > 0 || isToZaddr; + if (contextualTx.nVersion == 1 && isShielded) { + contextualTx.nVersion = 2; // Tx format should support vjoinsplit + } + if (NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER)) { + contextualTx.nExpiryHeight = nextBlockHeight + expiryDelta; + } + + // Create operation and add to global queue + std::shared_ptr q = getAsyncRPCQueue(); + std::shared_ptr operation( + new AsyncRPCOperation_mergetoaddress(contextualTx, utxoInputs, noteInputs, recipient, nFee, contextInfo) ); + q->addOperation(operation); + AsyncRPCOperationId operationId = operation->getId(); + + // Return continuation information + UniValue o(UniValue::VOBJ); + o.push_back(Pair("remainingUTXOs", utxoCounter - numUtxos)); + o.push_back(Pair("remainingTransparentValue", ValueFromAmount(remainingUTXOValue))); + o.push_back(Pair("remainingNotes", noteCounter - numNotes)); + o.push_back(Pair("remainingShieldedValue", ValueFromAmount(remainingNoteValue))); + o.push_back(Pair("mergingUTXOs", numUtxos)); + o.push_back(Pair("mergingTransparentValue", ValueFromAmount(mergedUTXOValue))); + o.push_back(Pair("mergingNotes", numNotes)); + o.push_back(Pair("mergingShieldedValue", ValueFromAmount(mergedNoteValue))); + o.push_back(Pair("opid", operationId)); + return o; +} + + UniValue z_listoperationids(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) @@ -3786,7 +4398,7 @@ UniValue z_listoperationids(const UniValue& params, bool fHelp) "z_listoperationids\n" "\nReturns the list of operation ids currently known to the wallet.\n" "\nArguments:\n" - "1. \"status\" (string, optional) Filter result by the operation's state state e.g. \"success\".\n" + "1. \"status\" (string, optional) Filter result by the operation's state e.g. \"success\".\n" "\nResult:\n" "[ (json array of string)\n" " \"operationid\" (string) an operation id belonging to the wallet\n" @@ -3822,3 +4434,111 @@ UniValue z_listoperationids(const UniValue& params, bool fHelp) return ret; } + + +#include "script/sign.h" +int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex); +extern std::string NOTARY_PUBKEY; +uint32_t komodo_stake(int32_t validateflag,arith_uint256 bnTarget,int32_t nHeight,uint256 hash,int32_t n,uint32_t blocktime,uint32_t prevtime,char *destaddr); + +int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blocktimep,uint32_t *txtimep,uint256 *utxotxidp,int32_t *utxovoutp,uint64_t *utxovaluep,uint8_t *utxosig) +{ + set setAddress; int32_t i,siglen=0,nMinDepth = 1,nMaxDepth = 9999999; vector vecOutputs; uint32_t eligible,earliest = 0; CScript best_scriptPubKey; arith_uint256 bnTarget; bool fNegative,fOverflow; + bnTarget.SetCompact(nBits, &fNegative, &fOverflow); + assert(pwalletMain != NULL); + LOCK2(cs_main, pwalletMain->cs_wallet); + *utxovaluep = 0; + memset(utxotxidp,0,sizeof(*utxotxidp)); + memset(utxovoutp,0,sizeof(*utxovoutp)); + memset(utxosig,0,72); + pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); + BOOST_FOREACH(const COutput& out, vecOutputs) + { + if ( out.nDepth < nMinDepth || out.nDepth > nMaxDepth ) + continue; + if ( setAddress.size() ) + { + CTxDestination address; + if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) + continue; + if (!setAddress.count(address)) + continue; + } + CAmount nValue = out.tx->vout[out.i].nValue; + const CScript& pk = out.tx->vout[out.i].scriptPubKey; + //entry.push_back(Pair("generated", out.tx->IsCoinBase())); + CTxDestination address; + if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) + { + //entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); + //if (pwalletMain->mapAddressBook.count(address)) + // entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name)); + } + /*entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end()))); + if (pk.IsPayToScriptHash()) + { + CTxDestination address; + if (ExtractDestination(pk, address)) { + const CScriptID& hash = boost::get(address); + CScript redeemScript; + if (pwalletMain->GetCScript(hash, redeemScript)) + entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); + } + } + entry.push_back(Pair("amount",ValueFromAmount(nValue)));*/ + //BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); + CBlockIndex *tipindex; + if ( (tipindex= chainActive.Tip()) != 0 ) + { + eligible = komodo_stake(0,bnTarget,(uint32_t)tipindex->nHeight+1,out.tx->GetHash(),out.i,*blocktimep,(uint32_t)tipindex->nTime,(char *)CBitcoinAddress(address).ToString().c_str()); + if ( eligible > 0 ) + { + if ( eligible != komodo_stake(1,bnTarget,(uint32_t)tipindex->nHeight+1,out.tx->GetHash(),out.i,eligible,(uint32_t)tipindex->nTime,(char *)CBitcoinAddress(address).ToString().c_str()) ) + fprintf(stderr,"validation of winning blocktime failed %u -> eligible.%u\n",*blocktimep,eligible); + else if ( earliest == 0 || eligible < earliest || (eligible == earliest && (*utxovaluep == 0 || nValue < *utxovaluep)) ) + { + earliest = eligible; + best_scriptPubKey = out.tx->vout[out.i].scriptPubKey; + *utxovaluep = (uint64_t)nValue; + decode_hex((uint8_t *)utxotxidp,32,(char *)out.tx->GetHash().GetHex().c_str()); + *utxovoutp = out.i; + *txtimep = (uint32_t)out.tx->nLockTime; + fprintf(stderr,"earliest.%u [%d] (%s) nValue %.8f locktime.%u\n",earliest,(int32_t)(earliest- *blocktimep),CBitcoinAddress(address).ToString().c_str(),(double)nValue/COIN,*txtimep); + } + } + } + } + if ( earliest != 0 ) + { + bool signSuccess; SignatureData sigdata; uint64_t txfee; uint8_t *ptr; uint256 revtxid,utxotxid; + auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); + const CKeyStore& keystore = *pwalletMain; + txNew.vin.resize(1); + txNew.vout.resize(1); + txfee = 0; + for (i=0; i<32; i++) + ((uint8_t *)&revtxid)[i] = ((uint8_t *)utxotxidp)[31 - i]; + txNew.vin[0].prevout.hash = revtxid; + txNew.vin[0].prevout.n = *utxovoutp; + txNew.vout[0].scriptPubKey = CScript() << ParseHex(NOTARY_PUBKEY) << OP_CHECKSIG; + txNew.vout[0].nValue = *utxovaluep - txfee; + txNew.nLockTime = earliest; + CTransaction txNewConst(txNew); + signSuccess = ProduceSignature(TransactionSignatureCreator(&keystore, &txNewConst, 0, *utxovaluep, SIGHASH_ALL), best_scriptPubKey, sigdata, consensusBranchId); + if (!signSuccess) + fprintf(stderr,"failed to create signature\n"); + else + { + UpdateTransaction(txNew,0,sigdata); + ptr = (uint8_t *)sigdata.scriptSig.data(); + siglen = sigdata.scriptSig.size(); + for (i=0; i &vchCryptedSecret) { - if (!CCryptoKeyStore::AddCryptedSpendingKey(address, vk, vchCryptedSecret)) + if (!CCryptoKeyStore::AddCryptedSpendingKey(address, rk, vchCryptedSecret)) return false; if (!fFileBacked) return true; @@ -205,12 +211,12 @@ bool CWallet::AddCryptedSpendingKey(const libzcash::PaymentAddress &address, LOCK(cs_wallet); if (pwalletdbEncryption) { return pwalletdbEncryption->WriteCryptedZKey(address, - vk, + rk, vchCryptedSecret, mapZKeyMetadata[address]); } else { return CWalletDB(strWalletFile).WriteCryptedZKey(address, - vk, + rk, vchCryptedSecret, mapZKeyMetadata[address]); } @@ -240,9 +246,9 @@ bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret) +bool CWallet::LoadCryptedZKey(const libzcash::PaymentAddress &addr, const libzcash::ReceivingKey &rk, const std::vector &vchCryptedSecret) { - return CCryptoKeyStore::AddCryptedSpendingKey(addr, vk, vchCryptedSecret); + return CCryptoKeyStore::AddCryptedSpendingKey(addr, rk, vchCryptedSecret); } bool CWallet::LoadZKey(const libzcash::SpendingKey &key) @@ -250,6 +256,38 @@ bool CWallet::LoadZKey(const libzcash::SpendingKey &key) return CCryptoKeyStore::AddSpendingKey(key); } +bool CWallet::AddViewingKey(const libzcash::ViewingKey &vk) +{ + if (!CCryptoKeyStore::AddViewingKey(vk)) { + return false; + } + nTimeFirstKey = 1; // No birthday information for viewing keys. + if (!fFileBacked) { + return true; + } + return CWalletDB(strWalletFile).WriteViewingKey(vk); +} + +bool CWallet::RemoveViewingKey(const libzcash::ViewingKey &vk) +{ + AssertLockHeld(cs_wallet); + if (!CCryptoKeyStore::RemoveViewingKey(vk)) { + return false; + } + if (fFileBacked) { + if (!CWalletDB(strWalletFile).EraseViewingKey(vk)) { + return false; + } + } + + return true; +} + +bool CWallet::LoadViewingKey(const libzcash::ViewingKey &vk) +{ + return CCryptoKeyStore::AddViewingKey(vk); +} + bool CWallet::AddCScript(const CScript& redeemScript) { if (!CCryptoKeyStore::AddCScript(redeemScript)) @@ -963,7 +1001,8 @@ void CWallet::MarkDirty() } /** - * Ensure that every note in the wallet has a cached nullifier. + * Ensure that every note in the wallet (for which we possess a spending key) + * has a cached nullifier. */ bool CWallet::UpdateNullifierNoteMap() { @@ -977,16 +1016,17 @@ bool CWallet::UpdateNullifierNoteMap() for (std::pair& wtxItem : mapWallet) { for (mapNoteData_t::value_type& item : wtxItem.second.mapNoteData) { if (!item.second.nullifier) { - auto i = item.first.js; - GetNoteDecryptor(item.second.address, dec); - auto hSig = wtxItem.second.vjoinsplit[i].h_sig( - *pzcashParams, wtxItem.second.joinSplitPubKey); - item.second.nullifier = GetNoteNullifier( - wtxItem.second.vjoinsplit[i], - item.second.address, - dec, - hSig, - item.first.n); + if (GetNoteDecryptor(item.second.address, dec)) { + auto i = item.first.js; + auto hSig = wtxItem.second.vjoinsplit[i].h_sig( + *pzcashParams, wtxItem.second.joinSplitPubKey); + item.second.nullifier = GetNoteNullifier( + wtxItem.second.vjoinsplit[i], + item.second.address, + dec, + hSig, + item.first.n); + } } } UpdateNullifierNoteMapWithTx(wtxItem.second); @@ -1248,7 +1288,9 @@ boost::optional CWallet::GetNoteNullifier(const JSDescription& jsdesc, hSig, (unsigned char) n); auto note = note_pt.note(address); - // SpendingKeys are only available if the wallet is unlocked + // SpendingKeys are only available if: + // - We have them (this isn't a viewing key) + // - The wallet is unlocked libzcash::SpendingKey key; if (GetSpendingKey(address, key)) { ret = note.nullifier(key); @@ -2580,6 +2622,7 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount &nFeeRet, int& nC CReserveKey reservekey(this); CWalletTx wtx; + if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosRet, strFailReason, &coinControl, false)) return false; @@ -2629,36 +2672,23 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt wtxNew.fTimeReceivedIsTxTime = true; wtxNew.BindWallet(this); - CMutableTransaction txNew; - - if ( 0 ) - { - // Discourage fee sniping. - // - // However because of a off-by-one-error in previous versions we need to - // neuter it by setting nLockTime to at least one less than nBestHeight. - // Secondly currently propagation of transactions created for block heights - // corresponding to blocks that were just mined may be iffy - transactions - // aren't re-accepted into the mempool - we additionally neuter the code by - // going ten blocks back. Doesn't yet do anything for sniping, but does act - // to shake out wallet bugs like not showing nLockTime'd transactions at - // all. - txNew.nLockTime = std::max(0, chainActive.Height() - 10); - - // Secondly occasionally randomly pick a nLockTime even further back, so - // that transactions that are delayed after signing for whatever reason, - // e.g. high-latency mix networks and some CoinJoin implementations, have - // better privacy. - if (GetRandInt(10) == 0) - txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); - - assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); - assert(txNew.nLockTime < LOCKTIME_THRESHOLD); - } - else - { + int nextBlockHeight = chainActive.Height() + 1; + CMutableTransaction txNew = CreateNewContextualCMutableTransaction( + Params().GetConsensus(), nextBlockHeight); txNew.nLockTime = (uint32_t)chainActive.Tip()->nTime + 1; // set to a time close to now + + // Activates after Overwinter network upgrade + // Set nExpiryHeight to expiryDelta (default 20) blocks past current block height + if (NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER)) + { + if (nextBlockHeight + expiryDelta >= TX_EXPIRY_HEIGHT_THRESHOLD){ + strFailReason = _("nExpiryHeight must be less than TX_EXPIRY_HEIGHT_THRESHOLD."); + return false; + } else { + txNew.nExpiryHeight = nextBlockHeight + expiryDelta; + } } + { LOCK2(cs_main, cs_wallet); { @@ -2779,8 +2809,6 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt extern int32_t USE_EXTERNAL_PUBKEY; extern std::string NOTARY_PUBKEY; if ( USE_EXTERNAL_PUBKEY == 0 ) { - //fprintf(stderr,"use notary pubkey\n"); - //scriptPubKey = CScript() << ParseHex(NOTARY_PUBKEY) << OP_CHECKSIG; bool ret; ret = reservekey.GetReservedKey(vchPubKey); assert(ret); // should never fail, as we just unlocked @@ -2788,6 +2816,7 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt } else { + //fprintf(stderr,"use notary pubkey\n"); scriptChange = CScript() << ParseHex(NOTARY_PUBKEY) << OP_CHECKSIG; } } @@ -2839,6 +2868,19 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second,CScript(), std::numeric_limits::max()-1)); + // Check mempooltxinputlimit to avoid creating a transaction which the local mempool rejects + size_t limit = (size_t)GetArg("-mempooltxinputlimit", 0); + if (limit > 0) { + size_t n = txNew.vin.size(); + if (n > limit) { + strFailReason = _(strprintf("Too many transparent inputs %zu > limit %zu", n, limit).c_str()); + return false; + } + } + + // Grab the current consensus branch ID + auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); + // Sign int nIn = 0; CTransaction txNewConst(txNew); @@ -2846,17 +2888,20 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt { bool signSuccess; const CScript& scriptPubKey = coin.first->vout[coin.second].scriptPubKey; - CScript& scriptSigRes = txNew.vin[nIn].scriptSig; + SignatureData sigdata; if (sign) - signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, SIGHASH_ALL), scriptPubKey, scriptSigRes); + signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata, consensusBranchId); else - signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, scriptSigRes); + signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata, consensusBranchId); if (!signSuccess) { strFailReason = _("Signing transaction failed"); return false; + } else { + UpdateTransaction(txNew, nIn, sigdata); } + nIn++; } @@ -3490,6 +3535,42 @@ void CWallet::ListLockedCoins(std::vector& vOutpts) } } + +// Note Locking Operations + +void CWallet::LockNote(JSOutPoint& output) +{ + AssertLockHeld(cs_wallet); // setLockedNotes + setLockedNotes.insert(output); +} + +void CWallet::UnlockNote(JSOutPoint& output) +{ + AssertLockHeld(cs_wallet); // setLockedNotes + setLockedNotes.erase(output); +} + +void CWallet::UnlockAllNotes() +{ + AssertLockHeld(cs_wallet); // setLockedNotes + setLockedNotes.clear(); +} + +bool CWallet::IsLockedNote(uint256 hash, size_t js, uint8_t n) const +{ + AssertLockHeld(cs_wallet); // setLockedNotes + JSOutPoint outpt(hash, js, n); + + return (setLockedNotes.count(outpt) > 0); +} + +std::vector CWallet::ListLockedNotes() +{ + AssertLockHeld(cs_wallet); // setLockedNotes + std::vector vOutpts(setLockedNotes.begin(), setLockedNotes.end()); + return vOutpts; +} + /** @} */ // end of Actions class CAffectedKeysVisitor : public boost::static_visitor { @@ -3725,15 +3806,28 @@ bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectAbsurdFee) * Find notes in the wallet filtered by payment address, min depth and ability to spend. * These notes are decrypted and added to the output parameter vector, outEntries. */ -void CWallet::GetFilteredNotes(std::vector & outEntries, std::string address, int minDepth, bool ignoreSpent) +void CWallet::GetFilteredNotes(std::vector & outEntries, std::string address, int minDepth, bool ignoreSpent, bool ignoreUnspendable) { - bool fFilterAddress = false; - libzcash::PaymentAddress filterPaymentAddress; + std::set filterAddresses; + if (address.length() > 0) { - filterPaymentAddress = CZCPaymentAddress(address).Get(); - fFilterAddress = true; + filterAddresses.insert(CZCPaymentAddress(address).Get()); } + GetFilteredNotes(outEntries, filterAddresses, minDepth, ignoreSpent, ignoreUnspendable); +} + +/** + * Find notes in the wallet filtered by payment addresses, min depth and ability to spend. + * These notes are decrypted and added to the output parameter vector, outEntries. + */ +void CWallet::GetFilteredNotes( + std::vector& outEntries, + std::set& filterAddresses, + int minDepth, + bool ignoreSpent, + bool ignoreUnspendable) +{ LOCK2(cs_main, cs_wallet); for (auto & p : mapWallet) { @@ -3754,7 +3848,7 @@ void CWallet::GetFilteredNotes(std::vector & outEntries, st PaymentAddress pa = nd.address; // skip notes which belong to a different payment address in the wallet - if (fFilterAddress && !(pa == filterPaymentAddress)) { + if (!(filterAddresses.empty() || filterAddresses.count(pa))) { continue; } @@ -3763,6 +3857,16 @@ void CWallet::GetFilteredNotes(std::vector & outEntries, st continue; } + // skip notes which cannot be spent + if (ignoreUnspendable && !HaveSpendingKey(pa)) { + continue; + } + + // skip locked notes + if (IsLockedNote(jsop.hash, jsop.js, jsop.n)) { + continue; + } + int i = jsop.js; // Index into CTransaction.vjoinsplit int j = jsop.n; // Index into JSDescription.ciphertexts @@ -3783,7 +3887,7 @@ void CWallet::GetFilteredNotes(std::vector & outEntries, st hSig, (unsigned char) j); - outEntries.push_back(CNotePlaintextEntry{jsop, plaintext}); + outEntries.push_back(CNotePlaintextEntry{jsop, pa, plaintext}); } catch (const note_decryption_failed &err) { // Couldn't decrypt with this spending key diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 01285ca2b..f77a55a04 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -8,9 +8,9 @@ #include "amount.h" #include "coins.h" -#include "consensus/consensus.h" #include "key.h" #include "keystore.h" +#include "main.h" #include "primitives/block.h" #include "primitives/transaction.h" #include "tinyformat.h" @@ -61,7 +61,6 @@ static const unsigned int MAX_FREE_TRANSACTION_CREATE_SIZE = 1000; #define _COINBASE_MATURITY 100 static const unsigned int WITNESS_CACHE_SIZE = _COINBASE_MATURITY+10; -class CAccountingEntry; class CBlockIndex; class CCoinControl; class COutput; @@ -154,7 +153,7 @@ struct COutputEntry int vout; }; -/** An note outpoint */ +/** A note outpoint */ class JSOutPoint { public: @@ -273,6 +272,7 @@ typedef std::map mapNoteData_t; struct CNotePlaintextEntry { JSOutPoint jsop; + libzcash::PaymentAddress address; libzcash::NotePlaintext plaintext; }; @@ -575,6 +575,86 @@ public: } }; +/** + * 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 nType, int nVersion) { + if (!(nType & 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(nType, nVersion); + 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()), nType, nVersion); + 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; +}; /** @@ -802,6 +882,7 @@ public: CPubKey vchDefaultKey; std::set setLockedCoins; + std::set setLockedNotes; int64_t nTimeFirstKey; @@ -822,6 +903,14 @@ public: void UnlockAllCoins(); void ListLockedCoins(std::vector& vOutpts); + + bool IsLockedNote(uint256 hash, size_t js, uint8_t n) const; + void LockNote(JSOutPoint& output); + void UnlockNote(JSOutPoint& output); + void UnlockAllNotes(); + std::vector ListLockedNotes(); + + /** * keystore implementation * Generate a new key @@ -876,9 +965,15 @@ public: //! Load spending key metadata (used by LoadWallet) bool LoadZKeyMetadata(const libzcash::PaymentAddress &addr, const CKeyMetadata &meta); //! Adds an encrypted spending key to the store, without saving it to disk (used by LoadWallet) - bool LoadCryptedZKey(const libzcash::PaymentAddress &addr, const libzcash::ViewingKey &vk, const std::vector &vchCryptedSecret); + bool LoadCryptedZKey(const libzcash::PaymentAddress &addr, const libzcash::ReceivingKey &rk, const std::vector &vchCryptedSecret); //! Adds an encrypted spending key to the store, and saves it to disk (virtual method, declared in crypter.h) - bool AddCryptedSpendingKey(const libzcash::PaymentAddress &address, const libzcash::ViewingKey &vk, const std::vector &vchCryptedSecret); + bool AddCryptedSpendingKey(const libzcash::PaymentAddress &address, const libzcash::ReceivingKey &rk, const std::vector &vchCryptedSecret); + + //! Adds a viewing key to the store, and saves it to disk. + bool AddViewingKey(const libzcash::ViewingKey &vk); + bool RemoveViewingKey(const libzcash::ViewingKey &vk); + //! Adds a viewing key to the store, without saving it to disk (used by LoadWallet) + bool LoadViewingKey(const libzcash::ViewingKey &dest); /** * Increment the next transaction order id @@ -1041,8 +1136,19 @@ public: void SetBroadcastTransactions(bool broadcast) { fBroadcastTransactions = broadcast; } /* Find notes filtered by payment address, min depth, ability to spend */ - void GetFilteredNotes(std::vector & outEntries, std::string address, int minDepth=1, bool ignoreSpent=true); - + void GetFilteredNotes(std::vector & outEntries, + std::string address, + int minDepth=1, + bool ignoreSpent=true, + bool ignoreUnspendable=true); + + /* Find notes filtered by payment addresses, min depth, ability to spend */ + void GetFilteredNotes(std::vector& outEntries, + std::set& filterAddresses, + int minDepth=1, + bool ignoreSpent=true, + bool ignoreUnspendable=true); + }; /** A key allocated from the key pool. */ @@ -1098,88 +1204,4 @@ public: READWRITE(vchPubKey); } }; - - - -/** - * 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 nType, int nVersion) { - if (!(nType & 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(nType, nVersion); - 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()), nType, nVersion); - 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; -}; - #endif // BITCOIN_WALLET_WALLET_H diff --git a/src/wallet/wallet_ismine.cpp b/src/wallet/wallet_ismine.cpp index 5482348e3..5e144eabe 100644 --- a/src/wallet/wallet_ismine.cpp +++ b/src/wallet/wallet_ismine.cpp @@ -71,6 +71,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) } break; } + case TX_MULTISIG: { // Only consider transactions "mine" if we own ALL the diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index ec6c55ee3..4bf191380 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -106,7 +106,7 @@ bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey, } bool CWalletDB::WriteCryptedZKey(const libzcash::PaymentAddress & addr, - const libzcash::ViewingKey &vk, + const libzcash::ReceivingKey &rk, const std::vector& vchCryptedSecret, const CKeyMetadata &keyMeta) { @@ -116,7 +116,7 @@ bool CWalletDB::WriteCryptedZKey(const libzcash::PaymentAddress & addr, if (!Write(std::make_pair(std::string("zkeymeta"), addr), keyMeta)) return false; - if (!Write(std::make_pair(std::string("czkey"), addr), std::make_pair(vk, vchCryptedSecret), false)) + if (!Write(std::make_pair(std::string("czkey"), addr), std::make_pair(rk, vchCryptedSecret), false)) return false; if (fEraseUnencryptedKey) { @@ -142,6 +142,18 @@ bool CWalletDB::WriteZKey(const libzcash::PaymentAddress& addr, const libzcash:: return Write(std::make_pair(std::string("zkey"), addr), key, false); } +bool CWalletDB::WriteViewingKey(const libzcash::ViewingKey &vk) +{ + nWalletDBUpdated++; + return Write(std::make_pair(std::string("vkey"), vk), '1'); +} + +bool CWalletDB::EraseViewingKey(const libzcash::ViewingKey &vk) +{ + nWalletDBUpdated++; + return Erase(std::make_pair(std::string("vkey"), vk)); +} + bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript) { nWalletDBUpdated++; @@ -471,6 +483,19 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, // so set the wallet birthday to the beginning of time. pwallet->nTimeFirstKey = 1; } + else if (strType == "vkey") + { + libzcash::ViewingKey vk; + ssKey >> vk; + char fYes; + ssValue >> fYes; + if (fYes == '1') + pwallet->LoadViewingKey(vk); + + // Viewing keys have no birthday information for now, + // so set the wallet birthday to the beginning of time. + pwallet->nTimeFirstKey = 1; + } else if (strType == "zkey") { libzcash::PaymentAddress addr; @@ -585,14 +610,14 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, libzcash::PaymentAddress addr; ssKey >> addr; // Deserialization of a pair is just one item after another - uint256 vkValue; - ssValue >> vkValue; - libzcash::ViewingKey vk(vkValue); + uint256 rkValue; + ssValue >> rkValue; + libzcash::ReceivingKey rk(rkValue); vector vchCryptedSecret; ssValue >> vchCryptedSecret; wss.nCKeys++; - if (!pwallet->LoadCryptedZKey(addr, vk, vchCryptedSecret)) + if (!pwallet->LoadCryptedZKey(addr, rk, vchCryptedSecret)) { strErr = "Error reading wallet database: LoadCryptedZKey failed"; return false; @@ -694,6 +719,7 @@ static bool IsKeyType(string strType) { return (strType== "key" || strType == "wkey" || strType == "zkey" || strType == "czkey" || + strType == "vkey" || strType == "mkey" || strType == "ckey"); } @@ -968,11 +994,7 @@ bool BackupWallet(const CWallet& wallet, const string& strDest) pathDest /= wallet.strWalletFile; try { -#if BOOST_VERSION >= 104000 boost::filesystem::copy_file(pathSrc, pathDest, boost::filesystem::copy_option::overwrite_if_exists); -#else - boost::filesystem::copy_file(pathSrc, pathDest); -#endif LogPrintf("copied wallet.dat to %s\n", pathDest.string()); return true; } catch (const boost::filesystem::filesystem_error& e) { diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index f9f71e00c..e455ad953 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -136,10 +136,13 @@ public: /// Write spending key to wallet database, where key is payment address and value is spending key. bool WriteZKey(const libzcash::PaymentAddress& addr, const libzcash::SpendingKey& key, const CKeyMetadata &keyMeta); bool WriteCryptedZKey(const libzcash::PaymentAddress & addr, - const libzcash::ViewingKey & vk, + const libzcash::ReceivingKey & rk, const std::vector& vchCryptedSecret, const CKeyMetadata &keyMeta); + bool WriteViewingKey(const libzcash::ViewingKey &vk); + bool EraseViewingKey(const libzcash::ViewingKey &vk); + private: CWalletDB(const CWalletDB&); void operator=(const CWalletDB&); diff --git a/src/zcash/Address.cpp b/src/zcash/Address.cpp index 3849b2ffc..baefeae4e 100644 --- a/src/zcash/Address.cpp +++ b/src/zcash/Address.cpp @@ -12,12 +12,20 @@ uint256 PaymentAddress::GetHash() const { return Hash(ss.begin(), ss.end()); } -uint256 ViewingKey::pk_enc() { +uint256 ReceivingKey::pk_enc() const { return ZCNoteEncryption::generate_pubkey(*this); } +PaymentAddress ViewingKey::address() const { + return PaymentAddress(a_pk, sk_enc.pk_enc()); +} + +ReceivingKey SpendingKey::receiving_key() const { + return ReceivingKey(ZCNoteEncryption::generate_privkey(*this)); +} + ViewingKey SpendingKey::viewing_key() const { - return ViewingKey(ZCNoteEncryption::generate_privkey(*this)); + return ViewingKey(PRF_addr_a_pk(*this), receiving_key()); } SpendingKey SpendingKey::random() { @@ -25,7 +33,7 @@ SpendingKey SpendingKey::random() { } PaymentAddress SpendingKey::address() const { - return PaymentAddress(PRF_addr_a_pk(*this), viewing_key().pk_enc()); + return viewing_key().address(); } } diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index efae2af22..2dbe10a60 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -1,5 +1,5 @@ -#ifndef _ZCADDRESS_H_ -#define _ZCADDRESS_H_ +#ifndef ZC_ADDRESS_H_ +#define ZC_ADDRESS_H_ #include "uint256.h" #include "uint252.h" @@ -8,6 +8,7 @@ namespace libzcash { const size_t SerializedPaymentAddressSize = 64; +const size_t SerializedViewingKeySize = 64; const size_t SerializedSpendingKeySize = 32; class PaymentAddress { @@ -38,11 +39,39 @@ public: } }; -class ViewingKey : public uint256 { +class ReceivingKey : public uint256 { public: - ViewingKey(uint256 sk_enc) : uint256(sk_enc) { } + ReceivingKey() { } + ReceivingKey(uint256 sk_enc) : uint256(sk_enc) { } - uint256 pk_enc(); + uint256 pk_enc() const; +}; + +class ViewingKey { +public: + uint256 a_pk; + ReceivingKey sk_enc; + + ViewingKey() : a_pk(), sk_enc() { } + ViewingKey(uint256 a_pk, ReceivingKey sk_enc) : a_pk(a_pk), sk_enc(sk_enc) { } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(a_pk); + READWRITE(sk_enc); + } + + PaymentAddress address() const; + + friend inline bool operator==(const ViewingKey& a, const ViewingKey& b) { + return a.a_pk == b.a_pk && a.sk_enc == b.sk_enc; + } + friend inline bool operator<(const ViewingKey& a, const ViewingKey& b) { + return (a.a_pk < b.a_pk || + (a.a_pk == b.a_pk && a.sk_enc < b.sk_enc)); + } }; class SpendingKey : public uint252 { @@ -52,10 +81,11 @@ public: static SpendingKey random(); + ReceivingKey receiving_key() const; ViewingKey viewing_key() const; PaymentAddress address() const; }; } -#endif // _ZCADDRESS_H_ +#endif // ZC_ADDRESS_H_ diff --git a/src/zcash/CreateJoinSplit.cpp b/src/zcash/CreateJoinSplit.cpp index 9c7760e4f..166b4fac7 100644 --- a/src/zcash/CreateJoinSplit.cpp +++ b/src/zcash/CreateJoinSplit.cpp @@ -5,11 +5,13 @@ #include "../util.h" #include "primitives/transaction.h" #include "zcash/JoinSplit.hpp" + #include "libsnark/common/profiling.hpp" #include "komodo_defs.h" char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; int64_t MAX_MONEY = 200000000 * 100000000LL; uint16_t BITCOIND_PORT = 7771; +uint32_t ASSETCHAINS_CC = 0; using namespace libzcash; @@ -17,10 +19,8 @@ int main(int argc, char **argv) { libsnark::start_profiling(); - auto p = ZCJoinSplit::Unopened(); - p->loadVerifyingKey((ZC_GetParamsDir() / "sprout-verifying.key").string()); - p->setProvingKeyPath((ZC_GetParamsDir() / "sprout-proving.key").string()); - p->loadProvingKey(); + auto p = ZCJoinSplit::Prepared((ZC_GetParamsDir() / "sprout-verifying.key").string(), + (ZC_GetParamsDir() / "sprout-proving.key").string()); // construct a proof. @@ -36,4 +36,6 @@ int main(int argc, char **argv) 0, 0); } + + delete p; // not that it matters } diff --git a/src/zcash/GenerateParams.cpp b/src/zcash/GenerateParams.cpp index e1557bea6..94f1c73c1 100644 --- a/src/zcash/GenerateParams.cpp +++ b/src/zcash/GenerateParams.cpp @@ -20,13 +20,7 @@ int main(int argc, char **argv) std::string vkFile = argv[2]; std::string r1csFile = argv[3]; - auto p = ZCJoinSplit::Generate(); - - p->saveProvingKey(pkFile); - p->saveVerifyingKey(vkFile); - p->saveR1CS(r1csFile); - - delete p; + ZCJoinSplit::Generate(r1csFile, vkFile, pkFile); return 0; } diff --git a/src/zcash/IncrementalMerkleTree.hpp b/src/zcash/IncrementalMerkleTree.hpp index 67b356318..151a395c1 100644 --- a/src/zcash/IncrementalMerkleTree.hpp +++ b/src/zcash/IncrementalMerkleTree.hpp @@ -1,5 +1,5 @@ -#ifndef ZCINCREMENTALMERKLETREE_H_ -#define ZCINCREMENTALMERKLETREE_H_ +#ifndef ZC_INCREMENTALMERKLETREE_H_ +#define ZC_INCREMENTALMERKLETREE_H_ #include #include @@ -202,5 +202,4 @@ typedef libzcash::IncrementalMerkleTree ZCIncrementalWitness; typedef libzcash::IncrementalWitness ZCTestingIncrementalWitness; -#endif /* ZCINCREMENTALMERKLETREE_H_ */ - +#endif /* ZC_INCREMENTALMERKLETREE_H_ */ diff --git a/src/zcash/JoinSplit.cpp b/src/zcash/JoinSplit.cpp index 590700cd9..2685569d3 100644 --- a/src/zcash/JoinSplit.cpp +++ b/src/zcash/JoinSplit.cpp @@ -10,11 +10,11 @@ #include #include #include -#include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp" -#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp" -#include "libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp" -#include "libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.hpp" - +#include +#include +#include +#include +#include "tinyformat.h" #include "sync.h" #include "amount.h" @@ -28,7 +28,7 @@ CCriticalSection cs_ParamsIO; CCriticalSection cs_LoadKeys; template -void saveToFile(std::string path, T& obj) { +void saveToFile(const std::string path, T& obj) { LOCK(cs_ParamsIO); std::stringstream ss; @@ -42,14 +42,14 @@ void saveToFile(std::string path, T& obj) { } template -void loadFromFile(std::string path, boost::optional& objIn) { +void loadFromFile(const std::string path, T& objIn) { LOCK(cs_ParamsIO); std::stringstream ss; std::ifstream fh(path, std::ios::binary); if(!fh.is_open()) { - throw std::runtime_error((boost::format("could not load param file at %s") % path).str()); + throw std::runtime_error(strprintf("could not load param file at %s", path)); } ss << fh.rdbuf(); @@ -69,77 +69,33 @@ public: typedef default_r1cs_ppzksnark_pp ppzksnark_ppT; typedef Fr FieldT; - boost::optional> pk; - boost::optional> vk; - boost::optional> vk_precomp; - boost::optional pkPath; - - JoinSplitCircuit() {} - ~JoinSplitCircuit() {} - - void setProvingKeyPath(std::string path) { - pkPath = path; - } - - void loadProvingKey() { - LOCK(cs_LoadKeys); - - if (!pk) { - if (!pkPath) { - throw std::runtime_error("proving key path unknown"); - } - loadFromFile(*pkPath, pk); - } - } - - void saveProvingKey(std::string path) { - if (pk) { - saveToFile(path, *pk); - } else { - throw std::runtime_error("cannot save proving key; key doesn't exist"); - } - } - void loadVerifyingKey(std::string path) { - LOCK(cs_LoadKeys); - - loadFromFile(path, vk); - - processVerifyingKey(); - } - void processVerifyingKey() { - vk_precomp = r1cs_ppzksnark_verifier_process_vk(*vk); - } - void saveVerifyingKey(std::string path) { - if (vk) { - saveToFile(path, *vk); - } else { - throw std::runtime_error("cannot save verifying key; key doesn't exist"); - } - } - void saveR1CS(std::string path) { - auto r1cs = generate_r1cs(); + r1cs_ppzksnark_verification_key vk; + r1cs_ppzksnark_processed_verification_key vk_precomp; + std::string pkPath; - saveToFile(path, r1cs); + JoinSplitCircuit(const std::string vkPath, const std::string pkPath) : pkPath(pkPath) { + loadFromFile(vkPath, vk); + vk_precomp = r1cs_ppzksnark_verifier_process_vk(vk); } + ~JoinSplitCircuit() {} - r1cs_constraint_system generate_r1cs() { + static void generate(const std::string r1csPath, + const std::string vkPath, + const std::string pkPath) + { protoboard pb; joinsplit_gadget g(pb); g.generate_r1cs_constraints(); - return pb.get_constraint_system(); - } + auto r1cs = pb.get_constraint_system(); - void generate() { - LOCK(cs_LoadKeys); + saveToFile(r1csPath, r1cs); - const r1cs_constraint_system constraint_system = generate_r1cs(); - r1cs_ppzksnark_keypair keypair = r1cs_ppzksnark_generator(constraint_system); + r1cs_ppzksnark_keypair keypair = r1cs_ppzksnark_generator(r1cs); - pk = keypair.pk; - vk = keypair.vk; - processVerifyingKey(); + saveToFile(vkPath, keypair.vk); + saveToFile(pkPath, keypair.pk); } bool verify( @@ -154,10 +110,6 @@ public: uint64_t vpub_new, const uint256& rt ) { - if (!vk || !vk_precomp) { - throw std::runtime_error("JoinSplit verifying key not loaded"); - } - try { auto r1cs_proof = proof.to_libsnark_proof>(); @@ -174,8 +126,8 @@ public: ); return verifier.check( - *vk, - *vk_precomp, + vk, + vk_precomp, witness, r1cs_proof ); @@ -198,12 +150,9 @@ public: uint64_t vpub_old, uint64_t vpub_new, const uint256& rt, - bool computeProof + bool computeProof, + uint256 *out_esk // Payment disclosure ) { - if (computeProof && !pk) { - throw std::runtime_error("JoinSplit proving key not loaded"); - } - if (vpub_old > MAX_MONEY) { throw std::invalid_argument("nonsensical vpub_old value"); } @@ -303,6 +252,12 @@ public: } out_ephemeralKey = encryptor.get_epk(); + + // !!! Payment disclosure START + if (out_esk != nullptr) { + *out_esk = encryptor.get_esk(); + } + // !!! Payment disclosure END } // Authenticate h_sig with each of the input @@ -345,8 +300,14 @@ public: // estimate that it doesn't matter if we check every time. pb.constraint_system.swap_AB_if_beneficial(); - return ZCProof(r1cs_ppzksnark_prover( - *pk, + std::ifstream fh(pkPath, std::ios::binary); + + if(!fh.is_open()) { + throw std::runtime_error(strprintf("could not load param file at %s", pkPath)); + } + + return ZCProof(r1cs_ppzksnark_prover_streaming( + fh, primary_input, aux_input, pb.constraint_system @@ -355,20 +316,20 @@ public: }; template -JoinSplit* JoinSplit::Generate() +void JoinSplit::Generate(const std::string r1csPath, + const std::string vkPath, + const std::string pkPath) { initialize_curve_params(); - auto js = new JoinSplitCircuit(); - js->generate(); - - return js; + JoinSplitCircuit::generate(r1csPath, vkPath, pkPath); } template -JoinSplit* JoinSplit::Unopened() +JoinSplit* JoinSplit::Prepared(const std::string vkPath, + const std::string pkPath) { initialize_curve_params(); - return new JoinSplitCircuit(); + return new JoinSplitCircuit(vkPath, pkPath); } template diff --git a/src/zcash/JoinSplit.hpp b/src/zcash/JoinSplit.hpp index a8c08d21b..6a2d4e1f2 100644 --- a/src/zcash/JoinSplit.hpp +++ b/src/zcash/JoinSplit.hpp @@ -1,5 +1,5 @@ -#ifndef _ZCJOINSPLIT_H_ -#define _ZCJOINSPLIT_H_ +#ifndef ZC_JOINSPLIT_H_ +#define ZC_JOINSPLIT_H_ #include "Zcash.h" #include "Proof.hpp" @@ -48,22 +48,17 @@ class JoinSplit { public: virtual ~JoinSplit() {} - static JoinSplit* Generate(); - static JoinSplit* Unopened(); + static void Generate(const std::string r1csPath, + const std::string vkPath, + const std::string pkPath); + static JoinSplit* Prepared(const std::string vkPath, + const std::string pkPath); + static uint256 h_sig(const uint256& randomSeed, const boost::array& nullifiers, const uint256& pubKeyHash ); - // TODO: #789 - virtual void setProvingKeyPath(std::string) = 0; - virtual void loadProvingKey() = 0; - - virtual void saveProvingKey(std::string path) = 0; - virtual void loadVerifyingKey(std::string path) = 0; - virtual void saveVerifyingKey(std::string path) = 0; - virtual void saveR1CS(std::string path) = 0; - virtual ZCProof prove( const boost::array& inputs, const boost::array& outputs, @@ -78,7 +73,11 @@ public: uint64_t vpub_old, uint64_t vpub_new, const uint256& rt, - bool computeProof = true + bool computeProof = true, + // For paymentdisclosure, we need to retrieve the esk. + // Reference as non-const parameter with default value leads to compile error. + // So use pointer for simplicity. + uint256 *out_esk = nullptr ) = 0; virtual bool verify( @@ -103,4 +102,4 @@ protected: typedef libzcash::JoinSplit ZCJoinSplit; -#endif // _ZCJOINSPLIT_H_ +#endif // ZC_JOINSPLIT_H_ diff --git a/src/zcash/Note.hpp b/src/zcash/Note.hpp index 460e68f9d..faacd2720 100644 --- a/src/zcash/Note.hpp +++ b/src/zcash/Note.hpp @@ -1,5 +1,5 @@ -#ifndef _ZCNOTE_H_ -#define _ZCNOTE_H_ +#ifndef ZC_NOTE_H_ +#define ZC_NOTE_H_ #include "uint256.h" #include "Zcash.h" @@ -68,4 +68,4 @@ public: } -#endif // _ZCNOTE_H_ \ No newline at end of file +#endif // ZC_NOTE_H_ diff --git a/src/zcash/NoteEncryption.cpp b/src/zcash/NoteEncryption.cpp index a5ea2da15..9ae0ba5c3 100644 --- a/src/zcash/NoteEncryption.cpp +++ b/src/zcash/NoteEncryption.cpp @@ -135,6 +135,52 @@ typename NoteDecryption::Plaintext NoteDecryption::decrypt return plaintext; } +// +// Payment disclosure - decrypt with esk +// +template +typename PaymentDisclosureNoteDecryption::Plaintext PaymentDisclosureNoteDecryption::decryptWithEsk + (const PaymentDisclosureNoteDecryption::Ciphertext &ciphertext, + const uint256 &pk_enc, + const uint256 &esk, + const uint256 &hSig, + unsigned char nonce + ) const +{ + uint256 dhsecret; + + if (crypto_scalarmult(dhsecret.begin(), esk.begin(), pk_enc.begin()) != 0) { + throw std::logic_error("Could not create DH secret"); + } + + // Regenerate keypair + uint256 epk = NoteEncryption::generate_pubkey(esk); + + unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE]; + KDF(K, dhsecret, epk, pk_enc, hSig, nonce); + + // The nonce is zero because we never reuse keys + unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {}; + + PaymentDisclosureNoteDecryption::Plaintext plaintext; + + // Message length is always NOTEENCRYPTION_AUTH_BYTES less than + // the ciphertext length. + if (crypto_aead_chacha20poly1305_ietf_decrypt(plaintext.begin(), NULL, + NULL, + ciphertext.begin(), PaymentDisclosureNoteDecryption::CLEN, + NULL, + 0, + cipher_nonce, K) != 0) { + throw note_decryption_failed(); + } + + return plaintext; +} + + + + template uint256 NoteEncryption::generate_privkey(const uint252 &a_sk) { @@ -176,4 +222,6 @@ uint252 random_uint252() template class NoteEncryption; template class NoteDecryption; +template class PaymentDisclosureNoteDecryption; + } diff --git a/src/zcash/NoteEncryption.hpp b/src/zcash/NoteEncryption.hpp index 11346ebc1..321d7dead 100644 --- a/src/zcash/NoteEncryption.hpp +++ b/src/zcash/NoteEncryption.hpp @@ -31,6 +31,11 @@ public: NoteEncryption(uint256 hSig); + // Gets the ephemeral secret key + uint256 get_esk() { + return esk; + } + // Gets the ephemeral public key uint256 get_epk() { return epk; @@ -87,9 +92,34 @@ public: note_decryption_failed() : std::runtime_error("Could not decrypt message") { } }; + + +// Subclass PaymentDisclosureNoteDecryption provides a method to decrypt a note with esk. +template +class PaymentDisclosureNoteDecryption : public NoteDecryption { +protected: +public: + enum { CLEN=MLEN+NOTEENCRYPTION_AUTH_BYTES }; + typedef boost::array Ciphertext; + typedef boost::array Plaintext; + + PaymentDisclosureNoteDecryption() : NoteDecryption() {} + PaymentDisclosureNoteDecryption(uint256 sk_enc) : NoteDecryption(sk_enc) {} + + Plaintext decryptWithEsk( + const Ciphertext &ciphertext, + const uint256 &pk_enc, + const uint256 &esk, + const uint256 &hSig, + unsigned char nonce + ) const; +}; + } typedef libzcash::NoteEncryption ZCNoteEncryption; typedef libzcash::NoteDecryption ZCNoteDecryption; +typedef libzcash::PaymentDisclosureNoteDecryption ZCPaymentDisclosureNoteDecryption; + #endif /* ZC_NOTE_ENCRYPTION_H_ */ diff --git a/src/zcash/Proof.cpp b/src/zcash/Proof.cpp index 1b2199407..e7264e684 100644 --- a/src/zcash/Proof.cpp +++ b/src/zcash/Proof.cpp @@ -1,12 +1,12 @@ #include "Proof.hpp" +#include "crypto/common.h" + #include +#include +#include #include -#include "crypto/common.h" -#include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp" -#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp" - using namespace libsnark; typedef alt_bn128_pp curve_pp; diff --git a/src/zcash/Proof.hpp b/src/zcash/Proof.hpp index 3b6b5e568..5f05aa2c5 100644 --- a/src/zcash/Proof.hpp +++ b/src/zcash/Proof.hpp @@ -1,5 +1,5 @@ -#ifndef _ZCPROOF_H_ -#define _ZCPROOF_H_ +#ifndef ZC_PROOF_H_ +#define ZC_PROOF_H_ #include "serialize.h" #include "uint256.h" @@ -274,4 +274,4 @@ public: } -#endif // _ZCPROOF_H_ +#endif // ZC_PROOF_H_ diff --git a/src/zcash/Zcash.h b/src/zcash/Zcash.h index c2dfe548b..9e6684475 100644 --- a/src/zcash/Zcash.h +++ b/src/zcash/Zcash.h @@ -1,5 +1,5 @@ -#ifndef _ZCCONSTANTS_H_ -#define _ZCCONSTANTS_H_ +#ifndef ZC_ZCASH_H_ +#define ZC_ZCASH_H_ #define ZC_NUM_JS_INPUTS 2 #define ZC_NUM_JS_OUTPUTS 2 @@ -14,4 +14,4 @@ #define ZC_NOTEPLAINTEXT_SIZE (ZC_NOTEPLAINTEXT_LEADING + ZC_V_SIZE + ZC_RHO_SIZE + ZC_R_SIZE + ZC_MEMO_SIZE) -#endif // _ZCCONSTANTS_H_ +#endif // ZC_ZCASH_H_ diff --git a/src/zcash/prf.h b/src/zcash/prf.h index c6cb45384..dec934f4a 100644 --- a/src/zcash/prf.h +++ b/src/zcash/prf.h @@ -3,8 +3,8 @@ Zcash uses SHA256Compress as a PRF for various components within the zkSNARK circuit. */ -#ifndef _PRF_H_ -#define _PRF_H_ +#ifndef ZC_PRF_H_ +#define ZC_PRF_H_ #include "uint256.h" #include "uint252.h" @@ -15,4 +15,4 @@ uint256 PRF_nf(const uint252& a_sk, const uint256& rho); uint256 PRF_pk(const uint252& a_sk, size_t i0, const uint256& h_sig); uint256 PRF_rho(const uint252& phi, size_t i0, const uint256& h_sig); -#endif // _PRF_H_ +#endif // ZC_PRF_H_ diff --git a/src/zcash/util.h b/src/zcash/util.h index bbfeac1c3..10886e3ca 100644 --- a/src/zcash/util.h +++ b/src/zcash/util.h @@ -1,5 +1,5 @@ -#ifndef __ZCASH_UTIL_H -#define __ZCASH_UTIL_H +#ifndef ZC_UTIL_H_ +#define ZC_UTIL_H_ #include #include @@ -8,4 +8,4 @@ std::vector convertIntToVectorLE(const uint64_t val_int); std::vector convertBytesVectorToVector(const std::vector& bytes); uint64_t convertVectorToInt(const std::vector& v); -#endif // __ZCASH_UTIL_H +#endif // ZC_UTIL_H_ diff --git a/src/zcbenchmarks.cpp b/src/zcbenchmarks.cpp index 14eabebff..2c7e99a67 100644 --- a/src/zcbenchmarks.cpp +++ b/src/zcbenchmarks.cpp @@ -1,4 +1,6 @@ +#include #include +#include #include #include #include @@ -9,14 +11,18 @@ #include "primitives/transaction.h" #include "base58.h" #include "crypto/equihash.h" +#include "chain.h" #include "chainparams.h" +#include "consensus/upgrades.h" #include "consensus/validation.h" #include "main.h" #include "miner.h" #include "pow.h" +#include "rpcserver.h" #include "script/sign.h" #include "sodium.h" #include "streams.h" +#include "txdb.h" #include "utiltest.h" #include "wallet/wallet.h" @@ -26,6 +32,39 @@ #include "zcash/IncrementalMerkleTree.hpp" using namespace libzcash; +// This method is based on Shutdown from init.cpp +void pre_wallet_load() +{ + LogPrintf("%s: In progress...\n", __func__); + if (ShutdownRequested()) + throw new std::runtime_error("The node is shutting down"); + + if (pwalletMain) + pwalletMain->Flush(false); +#ifdef ENABLE_MINING + GenerateBitcoins(false, NULL, 0); +#endif + UnregisterNodeSignals(GetNodeSignals()); + if (pwalletMain) + pwalletMain->Flush(true); + + UnregisterValidationInterface(pwalletMain); + delete pwalletMain; + pwalletMain = NULL; + bitdb.Reset(); + RegisterNodeSignals(GetNodeSignals()); + LogPrintf("%s: done\n", __func__); +} + +void post_wallet_load(){ + RegisterValidationInterface(pwalletMain); +#ifdef ENABLE_MINING + // Generate coins in the background + if (pwalletMain || !GetArg("-mineraddress", "").empty()) + GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", 1)); +#endif +} + void timer_start(timeval &tv_start) { @@ -59,11 +98,7 @@ double benchmark_parameter_loading() struct timeval tv_start; timer_start(tv_start); - auto newParams = ZCJoinSplit::Unopened(); - - newParams->loadVerifyingKey(vk_path.string()); - newParams->setProvingKeyPath(pk_path.string()); - newParams->loadProvingKey(); + auto newParams = ZCJoinSplit::Prepared(vk_path.string(), pk_path.string()); double ret = timer_stop(tv_start); @@ -187,11 +222,8 @@ double benchmark_verify_equihash() return timer_stop(tv_start); } -double benchmark_large_tx() +double benchmark_large_tx(size_t nInputs) { - // Number of inputs in the spending transaction that we will simulate - const size_t NUM_INPUTS = 555; - // Create priv/pub key CKey priv; priv.MakeNewKey(false); @@ -210,26 +242,20 @@ double benchmark_large_tx() auto orig_tx = CTransaction(m_orig_tx); CMutableTransaction spending_tx; + spending_tx.fOverwintered = true; + spending_tx.nVersion = 3; + spending_tx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID; + auto input_hash = orig_tx.GetHash(); - // Add NUM_INPUTS inputs - for (size_t i = 0; i < NUM_INPUTS; i++) { + // Add nInputs inputs + for (size_t i = 0; i < nInputs; i++) { spending_tx.vin.emplace_back(input_hash, 0); } // Sign for all the inputs - for (size_t i = 0; i < NUM_INPUTS; i++) { - SignSignature(tempKeystore, prevPubKey, spending_tx, i, SIGHASH_ALL); - } - - // Serialize: - { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << spending_tx; - //std::cout << "SIZE OF SPENDING TX: " << ss.size() << std::endl; - - auto error = MAX_TX_SIZE / 20; // 5% error - assert(ss.size() < MAX_TX_SIZE + error); - assert(ss.size() > MAX_TX_SIZE - error); + auto consensusBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_OVERWINTER].nBranchId; + for (size_t i = 0; i < nInputs; i++) { + SignSignature(tempKeystore, prevPubKey, spending_tx, i, 1000000, SIGHASH_ALL, consensusBranchId); } // Spending tx has all its inputs signed and does not need to be mutated anymore @@ -238,12 +264,14 @@ double benchmark_large_tx() // Benchmark signature verification costs: struct timeval tv_start; timer_start(tv_start); - for (size_t i = 0; i < NUM_INPUTS; i++) { + PrecomputedTransactionData txdata(final_spending_tx); + for (size_t i = 0; i < nInputs; i++) { ScriptError serror = SCRIPT_ERR_OK; assert(VerifyScript(final_spending_tx.vin[i].scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, - TransactionSignatureChecker(&final_spending_tx, i), + TransactionSignatureChecker(&final_spending_tx, i, 1000000, txdata), + consensusBranchId, &serror)); } return timer_stop(tv_start); @@ -322,3 +350,117 @@ double benchmark_increment_note_witnesses(size_t nTxs) return timer_stop(tv_start); } +// Fake the input of a given block +class FakeCoinsViewDB : public CCoinsViewDB { + uint256 hash; + ZCIncrementalMerkleTree t; + +public: + FakeCoinsViewDB(std::string dbName, uint256& hash) : CCoinsViewDB(dbName, 100, false, false), hash(hash) {} + + bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { + if (rt == t.root()) { + tree = t; + return true; + } + return false; + } + + bool GetNullifier(const uint256 &nf) const { + return false; + } + + uint256 GetBestBlock() const { + return hash; + } + + uint256 GetBestAnchor() const { + return t.root(); + } + + bool BatchWrite(CCoinsMap &mapCoins, + const uint256 &hashBlock, + const uint256 &hashAnchor, + CAnchorsMap &mapAnchors, + CNullifiersMap &mapNullifiers) { + return false; + } + + bool GetStats(CCoinsStats &stats) const { + return false; + } +}; + +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"); + if (!fp) throw new std::runtime_error("Failed to open block data file"); + CAutoFile blkFile(fp, SER_DISK, CLIENT_VERSION); + blkFile >> block; + blkFile.fclose(); + + // Fake its inputs + auto hashPrev = uint256S("00000000159a41f468e22135942a567781c3f3dc7ad62257993eb3c69c3f95ef"); + FakeCoinsViewDB fakeDB("benchmark/block-107134-inputs", hashPrev); + CCoinsViewCache view(&fakeDB); + + // Fake the chain + CBlockIndex index(block); + index.nHeight = 107134; + CBlockIndex indexPrev; + indexPrev.phashBlock = &hashPrev; + indexPrev.nHeight = index.nHeight - 1; + index.pprev = &indexPrev; + mapBlockIndex.insert(std::make_pair(hashPrev, &indexPrev)); + + CValidationState state; + struct timeval tv_start; + timer_start(tv_start); + assert(ConnectBlock(block, state, &index, view, true)); + auto duration = timer_stop(tv_start); + + // Undo alterations to global state + mapBlockIndex.erase(hashPrev); + SelectParamsFromCommandLine(); + + return duration; +} + +double benchmark_sendtoaddress(CAmount amount) +{ + UniValue params(UniValue::VARR); + auto addr = getnewaddress(params, false); + + params.push_back(addr); + params.push_back(ValueFromAmount(amount)); + + struct timeval tv_start; + timer_start(tv_start); + auto txid = sendtoaddress(params, false); + return timer_stop(tv_start); +} + +double benchmark_loadwallet() +{ + pre_wallet_load(); + struct timeval tv_start; + bool fFirstRunRet=true; + timer_start(tv_start); + pwalletMain = new CWallet("wallet.dat"); + DBErrors nLoadWalletRet = pwalletMain->LoadWallet(fFirstRunRet); + auto res = timer_stop(tv_start); + post_wallet_load(); + return res; +} + +double benchmark_listunspent() +{ + UniValue params(UniValue::VARR); + struct timeval tv_start; + timer_start(tv_start); + auto unspent = listunspent(params, false); + return timer_stop(tv_start); +} diff --git a/src/zcbenchmarks.h b/src/zcbenchmarks.h index b2bc2e373..60a0be848 100644 --- a/src/zcbenchmarks.h +++ b/src/zcbenchmarks.h @@ -12,8 +12,12 @@ extern double benchmark_solve_equihash(); extern std::vector benchmark_solve_equihash_threaded(int nThreads); extern double benchmark_verify_joinsplit(const JSDescription &joinsplit); extern double benchmark_verify_equihash(); -extern double benchmark_large_tx(); +extern double benchmark_large_tx(size_t nInputs); extern double benchmark_try_decrypt_notes(size_t nAddrs); extern double benchmark_increment_note_witnesses(size_t nTxs); +extern double benchmark_connectblock_slow(); +extern double benchmark_sendtoaddress(CAmount amount); +extern double benchmark_loadwallet(); +extern double benchmark_listunspent(); #endif diff --git a/zcutil/build-debian-package.sh b/zcutil/build-debian-package.sh index 8c2d05c3f..870530083 100755 --- a/zcutil/build-debian-package.sh +++ b/zcutil/build-debian-package.sh @@ -18,6 +18,7 @@ if [ ! -d $BUILD_PATH ]; then fi PACKAGE_VERSION=$($SRC_PATH/src/zcashd --version | grep version | cut -d' ' -f4 | tr -d v) +DEBVERSION=$(echo $PACKAGE_VERSION | sed 's/-beta/~beta/' | sed 's/-rc/~rc/' | sed 's/-/+/') BUILD_DIR="$BUILD_PATH/$PACKAGE_NAME-$PACKAGE_VERSION-amd64" if [ -d $BUILD_DIR ]; then @@ -50,8 +51,8 @@ cp $SRC_DOC/man/zcashd.1 $DEB_MAN cp $SRC_DOC/man/zcash-cli.1 $DEB_MAN cp $SRC_DOC/man/zcash-fetch-params.1 $DEB_MAN # Copy bash completion files -cp $SRC_PATH/contrib/bitcoind.bash-completion $DEB_CMP/zcashd -cp $SRC_PATH/contrib/bitcoin-cli.bash-completion $DEB_CMP/zcash-cli +cp $SRC_PATH/contrib/zcashd.bash-completion $DEB_CMP/zcashd +cp $SRC_PATH/contrib/zcash-cli.bash-completion $DEB_CMP/zcash-cli # Gzip files gzip --best -n $DEB_DOC/changelog gzip --best -n $DEB_DOC/changelog.Debian @@ -63,7 +64,7 @@ cd $SRC_PATH/contrib # Create the control file dpkg-shlibdeps $DEB_BIN/zcashd $DEB_BIN/zcash-cli -dpkg-gencontrol -P$BUILD_DIR +dpkg-gencontrol -P$BUILD_DIR -v$DEBVERSION # Create the Debian package fakeroot dpkg-deb --build $BUILD_DIR diff --git a/zcutil/build.sh b/zcutil/build.sh index f789c7a8d..6b142990c 100755 --- a/zcutil/build.sh +++ b/zcutil/build.sh @@ -2,6 +2,22 @@ set -eu -o pipefail +function cmd_pref() { + if type -p "$2" > /dev/null; then + eval "$1=$2" + else + eval "$1=$3" + fi +} + +# If a g-prefixed version of the command exists, use it preferentially. +function gprefix() { + cmd_pref "$1" "g$2" "$2" +} + +gprefix READLINK readlink +cd "$(dirname "$("$READLINK" -f "$0")")/.." + # Allow user overrides to $MAKE. Typical usage for users who need it: # MAKE=gmake ./zcutil/build.sh -j$(nproc) if [[ -z "${MAKE-}" ]]; then @@ -11,10 +27,10 @@ fi # Allow overrides to $BUILD and $HOST for porters. Most users will not need it. # BUILD=i686-pc-linux-gnu ./zcutil/build.sh if [[ -z "${BUILD-}" ]]; then - BUILD=x86_64-unknown-linux-gnu + BUILD="$(./depends/config.guess)" fi if [[ -z "${HOST-}" ]]; then - HOST=x86_64-unknown-linux-gnu + HOST="$BUILD" fi # Allow override to $CC and $CXX for porters. Most users will not need it. @@ -25,6 +41,11 @@ if [[ -z "${CXX-}" ]]; then CXX=g++ fi +# Allow users to set arbitary compile flags. Most users will not need this. +if [[ -z "${CONFIGURE_FLAGS-}" ]]; then + CONFIGURE_FLAGS="" +fi + if [ "x$*" = 'x--help' ] then cat <&2 <&2 + exit 1 fi fi } @@ -100,8 +156,8 @@ EOF cd "$PARAMS_DIR" - fetch_params "$SPROUT_PKEY_URL" "$PARAMS_DIR/$SPROUT_PKEY_NAME" "8bc20a7f013b2b58970cddd2e7ea028975c88ae7ceb9259a5344a16bc2c0eef7" - fetch_params "$SPROUT_VKEY_URL" "$PARAMS_DIR/$SPROUT_VKEY_NAME" "4bd498dae0aacfd8e98dc306338d017d9c08dd0918ead18172bd0aec2fc5df82" + fetch_params "$SPROUT_PKEY_NAME" "$PARAMS_DIR/$SPROUT_PKEY_NAME" "8bc20a7f013b2b58970cddd2e7ea028975c88ae7ceb9259a5344a16bc2c0eef7" + fetch_params "$SPROUT_VKEY_NAME" "$PARAMS_DIR/$SPROUT_VKEY_NAME" "4bd498dae0aacfd8e98dc306338d017d9c08dd0918ead18172bd0aec2fc5df82" } main diff --git a/zcutil/make-release.py b/zcutil/make-release.py new file mode 100755 index 000000000..c19544312 --- /dev/null +++ b/zcutil/make-release.py @@ -0,0 +1,668 @@ +#! /usr/bin/env python2 + +import os +import re +import sys +import logging +import argparse +import subprocess +import traceback +import unittest +import random +from cStringIO import StringIO +from functools import wraps + + +def main(args=sys.argv[1:]): + """ + Perform the final Zcash release process up to the git tag. + """ + opts = parse_args(args) + chdir_to_repo(opts.REPO) + initialize_logging() + logging.debug('argv %r', sys.argv) + + try: + main_logged( + opts.RELEASE_VERSION, + opts.RELEASE_PREV, + opts.RELEASE_FROM, + opts.RELEASE_HEIGHT, + opts.HOTFIX, + ) + except SystemExit as e: + logging.error(str(e)) + raise SystemExit(1) + except: + logging.error(traceback.format_exc()) + raise SystemExit(2) + + +def parse_args(args): + p = argparse.ArgumentParser(description=main.__doc__) + p.add_argument( + '--repo', + dest='REPO', + type=str, + help='Path to repository root.', + ) + p.add_argument( + '--hotfix', + action='store_true', + dest='HOTFIX', + help='Use if this is a hotfix release from a non-master branch.', + ) + p.add_argument( + 'RELEASE_VERSION', + type=Version.parse_arg, + help='The release version: vX.Y.Z-N', + ) + p.add_argument( + 'RELEASE_PREV', + type=Version.parse_arg, + help='The previously released version.', + ) + p.add_argument( + 'RELEASE_FROM', + type=Version.parse_arg, + help='The previously released non-beta non-RC version. May be the same as RELEASE_PREV.', + ) + p.add_argument( + 'RELEASE_HEIGHT', + type=int, + help='A block height approximately occuring on release day.', + ) + return p.parse_args(args) + + +# Top-level flow: +def main_logged(release, releaseprev, releasefrom, releaseheight, hotfix): + verify_tags(releaseprev, releasefrom) + verify_version(release, releaseprev, hotfix) + initialize_git(release, hotfix) + patch_version_in_files(release, releaseprev) + patch_release_height(releaseheight) + commit('Versioning changes for {}.'.format(release.novtext)) + + build() + gen_manpages() + commit('Updated manpages for {}.'.format(release.novtext)) + + gen_release_notes(release, releasefrom) + update_debian_changelog(release) + commit( + 'Updated release notes and changelog for {}.'.format( + release.novtext, + ), + ) + + +def phase(message): + def deco(f): + @wraps(f) + def g(*a, **kw): + logging.info('%s', message) + return f(*a, **kw) + return g + return deco + + +@phase('Checking tags.') +def verify_tags(releaseprev, releasefrom): + candidates = [] + + # Any tag beginning with a 'v' followed by [1-9] must be a version + # matching our Version parser. Tags beginning with v0 may exist from + # upstream and those do not follow our schema and are silently + # ignored. Any other tag is silently ignored. + candidatergx = re.compile('^v[1-9].*$') + + for tag in sh_out('git', 'tag', '--list').splitlines(): + if candidatergx.match(tag): + candidates.append(Version.parse_arg(tag)) + + candidates.sort() + try: + latest = candidates[-1] + except IndexError: + raise SystemExit('No previous releases found by `git tag --list`.') + + if releaseprev != latest: + raise SystemExit( + 'The latest candidate in `git tag --list` is {} not {}' + .format( + latest.vtext, + releaseprev.vtext, + ), + ) + + candidates.reverse() + prev_tags = [] + for candidate in candidates: + if releasefrom == candidate: + break + else: + prev_tags.append(candidate) + else: + raise SystemExit( + '{} does not appear in `git tag --list`' + .format( + releasefrom.vtext, + ), + ) + + for tag in prev_tags: + if not tag.betarc: + raise SystemExit( + '{} appears to be a more recent non-beta non-RC release than {}' + .format( + tag.vtext, + releasefrom.vtext, + ), + ) + + +@phase('Checking version.') +def verify_version(release, releaseprev, hotfix): + if not hotfix: + return + + expected = Version( + releaseprev.major, + releaseprev.minor, + releaseprev.patch, + releaseprev.betarc, + releaseprev.hotfix + 1 if releaseprev.hotfix else 1, + ) + if release != expected: + raise SystemExit( + "Expected {!r}, given {!r}".format( + expected, release, + ), + ) + + +@phase('Initializing git.') +def initialize_git(release, hotfix): + junk = sh_out('git', 'status', '--porcelain') + if junk.strip(): + raise SystemExit('There are uncommitted changes:\n' + junk) + + branch = sh_out('git', 'rev-parse', '--abbrev-ref', 'HEAD').strip() + if hotfix: + expected = 'hotfix-' + release.vtext + else: + expected = 'master' + if branch != expected: + raise SystemExit( + "Expected branch {!r}, found branch {!r}".format( + expected, branch, + ), + ) + + logging.info('Pulling to latest master.') + sh_log('git', 'pull', '--ff-only') + + branch = 'release-' + release.vtext + logging.info('Creating release branch: %r', branch) + sh_log('git', 'checkout', '-b', branch) + return branch + + +@phase('Patching versioning in files.') +def patch_version_in_files(release, releaseprev): + patch_README(release, releaseprev) + patch_clientversion_h(release) + patch_configure_ac(release) + patch_gitian_linux_yml(release, releaseprev) + + +@phase('Patching release height for auto-senescence.') +def patch_release_height(releaseheight): + rgx = re.compile( + r'^(static const int APPROX_RELEASE_HEIGHT = )\d+(;)$', + ) + with PathPatcher('src/deprecation.h') as (inf, outf): + for line in inf: + m = rgx.match(line) + if m is None: + outf.write(line) + else: + [prefix, suffix] = m.groups() + outf.write( + '{}{}{}\n'.format( + prefix, + releaseheight, + suffix, + ), + ) + + +@phase('Building...') +def build(): + base_dir = os.getcwd() + depends_dir = os.path.join(base_dir, 'depends') + src_dir = os.path.join(base_dir, 'src') + nproc = sh_out('nproc').strip() + sh_progress([ + 'Staging boost...', + 'Staging libevent...', + 'Staging zeromq...', + 'Staging libgmp...', + 'Staging libsodium...', + "Leaving directory '%s'" % depends_dir, + 'config.status: creating libzcashconsensus.pc', + "Entering directory '%s'" % src_dir, + 'httpserver.cpp', + 'torcontrol.cpp', + 'gtest/test_tautology.cpp', + 'gtest/test_metrics.cpp', + 'test/equihash_tests.cpp', + 'test/util_tests.cpp', + "Leaving directory '%s'" % src_dir, + ], './zcutil/build.sh', '-j', nproc) + + +@phase('Generating manpages.') +def gen_manpages(): + sh_log('./contrib/devtools/gen-manpages.sh') + + +@phase('Generating release notes.') +def gen_release_notes(release, releasefrom): + release_notes = [ + 'python', + './zcutil/release-notes.py', + '--version', + release.novtext, + '--prev', + releasefrom.vtext, + ] + if not release.betarc: + release_notes.append('--clear') + sh_log(*release_notes) + sh_log( + 'git', + 'add', + './doc/authors.md', + './doc/release-notes/release-notes-{}.md'.format(release.novtext), + ) + + +@phase('Updating debian changelog.') +def update_debian_changelog(release): + os.environ['DEBEMAIL'] = 'team@z.cash' + os.environ['DEBFULLNAME'] = 'Zcash Company' + sh_log( + 'debchange', + '--newversion', release.debversion, + '--distribution', 'stable', + '--changelog', './contrib/debian/changelog', + '{} release.'.format(release.novtext), + ) + + +# Helper code: +def commit(message): + logging.info('Committing: %r', message) + fullmsg = 'make-release.py: {}'.format(message) + sh_log('git', 'commit', '--all', '-m', fullmsg) + + +def chdir_to_repo(repo): + if repo is None: + dn = os.path.dirname + repo = dn(dn(os.path.abspath(sys.argv[0]))) + os.chdir(repo) + + +def patch_README(release, releaseprev): + with PathPatcher('README.md') as (inf, outf): + firstline = inf.readline() + assert firstline == 'Zcash {}\n'.format(releaseprev.novtext), \ + repr(firstline) + + outf.write('Zcash {}\n'.format(release.novtext)) + outf.write(inf.read()) + + +def patch_clientversion_h(release): + _patch_build_defs( + release, + 'src/clientversion.h', + (r'^(#define CLIENT_VERSION_(MAJOR|MINOR|REVISION|BUILD|IS_RELEASE))' + r' \d+()$'), + ) + + +def patch_configure_ac(release): + _patch_build_defs( + release, + 'configure.ac', + (r'^(define\(_CLIENT_VERSION_(MAJOR|MINOR|REVISION|BUILD|IS_RELEASE),)' + r' \d+(\))$'), + ) + + +def patch_gitian_linux_yml(release, releaseprev): + path = 'contrib/gitian-descriptors/gitian-linux.yml' + with PathPatcher(path) as (inf, outf): + outf.write(inf.readline()) + + secondline = inf.readline() + assert secondline == 'name: "zcash-{}"\n'.format( + releaseprev.novtext + ), repr(secondline) + + outf.write('name: "zcash-{}"\n'.format(release.novtext)) + outf.write(inf.read()) + + +def _patch_build_defs(release, path, pattern): + rgx = re.compile(pattern) + with PathPatcher(path) as (inf, outf): + for line in inf: + m = rgx.match(line) + if m: + prefix, label, suffix = m.groups() + repl = { + 'MAJOR': release.major, + 'MINOR': release.minor, + 'REVISION': release.patch, + 'BUILD': release.build, + 'IS_RELEASE': ( + 'false' if release.build < 50 else 'true' + ), + }[label] + outf.write('{} {}{}\n'.format(prefix, repl, suffix)) + else: + outf.write(line) + + +def initialize_logging(): + logname = './zcash-make-release.log' + fmtr = logging.Formatter( + '%(asctime)s L%(lineno)-4d %(levelname)-5s | %(message)s', + '%Y-%m-%d %H:%M:%S' + ) + + hout = logging.StreamHandler(sys.stdout) + hout.setLevel(logging.INFO) + hout.setFormatter(fmtr) + + hpath = logging.FileHandler(logname, mode='a') + hpath.setLevel(logging.DEBUG) + hpath.setFormatter(fmtr) + + root = logging.getLogger() + root.setLevel(logging.DEBUG) + root.addHandler(hout) + root.addHandler(hpath) + logging.info('zcash make-release.py debug log: %r', logname) + + +def sh_out(*args): + logging.debug('Run (out): %r', args) + return subprocess.check_output(args) + + +def sh_log(*args): + PIPE = subprocess.PIPE + STDOUT = subprocess.STDOUT + try: + p = subprocess.Popen(args, stdout=PIPE, stderr=STDOUT, stdin=None) + except OSError: + logging.error('Error launching %r...', args) + raise + + logging.debug('Run (log PID %r): %r', p.pid, args) + for line in p.stdout: + logging.debug('> %s', line.rstrip()) + status = p.wait() + if status != 0: + raise SystemExit('Nonzero exit status: {!r}'.format(status)) + + +def sh_progress(markers, *args): + try: + import progressbar + except: + sh_log(*args) + return + + PIPE = subprocess.PIPE + STDOUT = subprocess.STDOUT + try: + p = subprocess.Popen(args, stdout=PIPE, stderr=STDOUT, stdin=None) + except OSError: + logging.error('Error launching %r...', args) + raise + + pbar = progressbar.ProgressBar(max_value=len(markers)) + marker = 0 + pbar.update(marker) + logging.debug('Run (log PID %r): %r', p.pid, args) + for line in p.stdout: + logging.debug('> %s', line.rstrip()) + for idx, val in enumerate(markers[marker:]): + if val in line: + marker += idx + 1 + pbar.update(marker) + break + pbar.finish() + status = p.wait() + if status != 0: + raise SystemExit('Nonzero exit status: {!r}'.format(status)) + + +class Version (object): + '''A release version.''' + + RGX = re.compile( + r'^v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-(beta|rc)?([1-9]\d*))?$', + ) + + @staticmethod + def parse_arg(text): + m = Version.RGX.match(text) + if m is None: + raise argparse.ArgumentTypeError( + 'Could not parse version {!r} against regex {}'.format( + text, + Version.RGX.pattern, + ), + ) + else: + [major, minor, patch, _, betarc, hotfix] = m.groups() + return Version( + int(major), + int(minor), + int(patch), + betarc, + int(hotfix) if hotfix is not None else None, + ) + + def __init__(self, major, minor, patch, betarc, hotfix): + for i in [major, minor, patch]: + assert type(i) is int, i + assert betarc in {None, 'rc', 'beta'}, betarc + assert hotfix is None or type(hotfix) is int, hotfix + if betarc is not None: + assert hotfix is not None, (betarc, hotfix) + + self.major = major + self.minor = minor + self.patch = patch + self.betarc = betarc + self.hotfix = hotfix + + if hotfix is None: + self.build = 50 + else: + assert hotfix > 0, hotfix + if betarc is None: + assert hotfix < 50, hotfix + self.build = 50 + hotfix + else: + assert hotfix < 26, hotfix + self.build = {'beta': 0, 'rc': 25}[betarc] + hotfix - 1 + + @property + def novtext(self): + return self._novtext(debian=False) + + @property + def vtext(self): + return 'v' + self.novtext + + @property + def debversion(self): + return self._novtext(debian=True) + + def _novtext(self, debian): + novtext = '{}.{}.{}'.format(self.major, self.minor, self.patch) + + if self.hotfix is None: + return novtext + else: + assert self.hotfix > 0, self.hotfix + if self.betarc is None: + assert self.hotfix < 50, self.hotfix + sep = '+' if debian else '-' + return '{}{}{}'.format(novtext, sep, self.hotfix) + else: + assert self.hotfix < 26, self.hotfix + sep = '~' if debian else '-' + return '{}{}{}{}'.format( + novtext, + sep, + self.betarc, + self.hotfix, + ) + + def __repr__(self): + return ''.format(self.vtext) + + def _sort_tup(self): + if self.hotfix is None: + prio = 2 + else: + prio = {'beta': 0, 'rc': 1, None: 3}[self.betarc] + + return ( + self.major, + self.minor, + self.patch, + prio, + self.hotfix, + ) + + def __cmp__(self, other): + return cmp(self._sort_tup(), other._sort_tup()) + + +class PathPatcher (object): + def __init__(self, path): + self._path = path + + def __enter__(self): + logging.debug('Patching %r', self._path) + self._inf = file(self._path, 'r') + self._outf = StringIO() + return (self._inf, self._outf) + + def __exit__(self, et, ev, tb): + if (et, ev, tb) == (None, None, None): + self._inf.close() + with file(self._path, 'w') as f: + f.write(self._outf.getvalue()) + + +# Unit Tests +class TestVersion (unittest.TestCase): + ValidVersionsAndBuilds = [ + # These are taken from: git tag --list | grep '^v1' + ('v1.0.0-beta1', 0), + ('v1.0.0-beta2', 1), + ('v1.0.0-rc1', 25), + ('v1.0.0-rc2', 26), + ('v1.0.0-rc3', 27), + ('v1.0.0-rc4', 28), + ('v1.0.0', 50), + ('v1.0.1', 50), + ('v1.0.2', 50), + ('v1.0.3', 50), + ('v1.0.4', 50), + ('v1.0.5', 50), + ('v1.0.6', 50), + ('v1.0.7-1', 51), + ('v1.0.8', 50), + ('v1.0.8-1', 51), + ('v1.0.9', 50), + ('v1.0.10', 50), + ('v7.42.1000', 50), + ] + + ValidVersions = [ + v + for (v, _) + in ValidVersionsAndBuilds + ] + + def test_arg_parse_and_vtext_identity(self): + for case in self.ValidVersions: + v = Version.parse_arg(case) + self.assertEqual(v.vtext, case) + + def test_arg_parse_negatives(self): + cases = [ + 'v07.0.0', + 'v1.0.03', + 'v1.2.3-0', # Hotfix numbers must begin w/ 1 + 'v1.2.3~0', + 'v1.2.3+0', + '1.2.3', + ] + + for case in cases: + self.assertRaises( + argparse.ArgumentTypeError, + Version.parse_arg, + case, + ) + + def test_version_sort(self): + expected = [Version.parse_arg(v) for v in self.ValidVersions] + + rng = random.Random() + rng.seed(0) + + for _ in range(1024): + vec = list(expected) + rng.shuffle(vec) + vec.sort() + self.assertEqual(vec, expected) + + def test_build_nums(self): + for (text, expected) in self.ValidVersionsAndBuilds: + version = Version.parse_arg(text) + self.assertEqual(version.build, expected) + + +if __name__ == '__main__': + if len(sys.argv) == 2 and sys.argv[1] == '--help': + main() + else: + actualargs = sys.argv + sys.argv = [sys.argv[0], '--verbose'] + + print '=== Self Test ===' + try: + unittest.main() + except SystemExit as e: + if e.args[0] != 0: + raise + + sys.argv = actualargs + print '=== Running ===' + main() diff --git a/zcutil/release-notes.py b/zcutil/release-notes.py old mode 100644 new mode 100755 index 47f234906..01f658f07 --- a/zcutil/release-notes.py +++ b/zcutil/release-notes.py @@ -4,6 +4,21 @@ import argparse from itertools import islice from operator import itemgetter +TEMP_RELEASE_NOTES_HEADER = [ + '(note: this is a temporary file, to be added-to by anybody, and moved to\n', + 'release-notes at release time)\n', + '\n', + 'Notable changes\n', + '===============\n', + '\n', +] + +RELEASE_NOTES_CHANGELOG_HEADING = [ + 'Changelog\n', + '=========\n', + '\n', +] + author_aliases = { 'Simon': 'Simon Liu', 'bitcartel': 'Simon Liu', @@ -55,6 +70,10 @@ def document_authors(): f.write('Zcash Contributors\n==================\n\n') total_contrib = {} for notes in os.listdir(os.path.join(doc_dir, 'release-notes')): + # Commits are duplicated across beta, RC and final release notes, + # except for the pre-launch release notes. + if ('-beta' in notes or '-rc' in notes) and '1.0.0-' not in notes: + continue authors = authors_in_release_notes(notes) for author in authors: commits = int(authors[author]) @@ -68,34 +87,56 @@ def document_authors(): f.write("{0} ({1})\n".format(n, c)) ## Writes release note to ./doc/release-notes based on git shortlog when current version number is specified -def generate_release_note(version, filename): +def generate_release_note(version, prev, clear): + filename = 'release-notes-{0}.md'.format(version) print "Automatically generating release notes for {0} from git shortlog. Should review {1} for accuracy.".format(version, filename) - # fetches latest tags, so that latest_tag will be correct - subprocess.Popen(['git fetch -t'], shell=True, stdout=subprocess.PIPE).communicate()[0] - latest_tag = subprocess.Popen(['git describe --abbrev=0'], shell=True, stdout=subprocess.PIPE).communicate()[0].strip() + if prev: + latest_tag = prev + else: + # fetches latest tags, so that latest_tag will be correct + subprocess.Popen(['git fetch -t'], shell=True, stdout=subprocess.PIPE).communicate()[0] + latest_tag = subprocess.Popen(['git describe --abbrev=0'], shell=True, stdout=subprocess.PIPE).communicate()[0].strip() print "Previous release tag: ", latest_tag notes = subprocess.Popen(['git shortlog --no-merges {0}..HEAD'.format(latest_tag)], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate()[0] lines = notes.split('\n') lines = [alias_authors_in_release_notes(line) for line in lines] - release_note = os.path.join(doc_dir, 'release-notes', 'release-notes-{0}.md'.format(version)) + temp_release_note = os.path.join(doc_dir, 'release-notes.md') + with open(temp_release_note, 'r') as f: + notable_changes = f.readlines() + # Assumes that all notable changes are appended to the default header + if len(notable_changes) > 6: + notable_changes = notable_changes[3:] + ['\n'] + else: + notable_changes = [] + release_note = os.path.join(doc_dir, 'release-notes', filename) with open(release_note, 'w') as f: + f.writelines(notable_changes) + f.writelines(RELEASE_NOTES_CHANGELOG_HEADING) f.writelines('\n'.join(lines)) + if clear: + # Clear temporary release notes file + with open(temp_release_note, 'w') as f: + f.writelines(TEMP_RELEASE_NOTES_HEADER) -def main(version, filename): +def main(version, prev, clear): if version != None: - generate_release_note(version, filename) + generate_release_note(version, prev, clear) document_authors() if __name__ == "__main__": parser = argparse.ArgumentParser() - parser.add_argument('--version') + parser.add_argument('--version', help='Upcoming version, without leading v') + parser.add_argument('--prev', help='Previous version, with leading v') + parser.add_argument('--clear', help='Wipe doc/release-notes.md', action='store_true') args = parser.parse_args() root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) doc_dir = os.path.join(root_dir, 'doc') version = None - filename = None + prev = None + clear = False if args.version: version = args.version - filename = 'release-notes-{0}.md'.format(version) - main(version, filename) + prev = args.prev + clear = args.clear + main(version, prev, clear)